ath9k: Add new Atheros IEEE 802.11n driver
Luis R. Rodriguez [Mon, 4 Aug 2008 07:16:41 +0000 (00:16 -0700)]
This adds the new mac80211 11n ath9k Atheros driver. Only STA support
is currently enabled and tested.

Signed-off-by: Senthil Balasubramanian <senthilkumar@atheros.com>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Jack Howarth <howarth@bromo.msbb.uc.edu>
Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: Sujith Manoharan <Sujith.Manoharan@atheros.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: Pavel Roskin <proski@gnu.org>
Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

23 files changed:
MAINTAINERS
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/ath9k/Kconfig [new file with mode: 0644]
drivers/net/wireless/ath9k/Makefile [new file with mode: 0644]
drivers/net/wireless/ath9k/ath9k.h [new file with mode: 0644]
drivers/net/wireless/ath9k/beacon.c [new file with mode: 0644]
drivers/net/wireless/ath9k/core.c [new file with mode: 0644]
drivers/net/wireless/ath9k/core.h [new file with mode: 0644]
drivers/net/wireless/ath9k/hw.c [new file with mode: 0644]
drivers/net/wireless/ath9k/hw.h [new file with mode: 0644]
drivers/net/wireless/ath9k/initvals.h [new file with mode: 0644]
drivers/net/wireless/ath9k/main.c [new file with mode: 0644]
drivers/net/wireless/ath9k/phy.c [new file with mode: 0644]
drivers/net/wireless/ath9k/phy.h [new file with mode: 0644]
drivers/net/wireless/ath9k/rc.c [new file with mode: 0644]
drivers/net/wireless/ath9k/rc.h [new file with mode: 0644]
drivers/net/wireless/ath9k/recv.c [new file with mode: 0644]
drivers/net/wireless/ath9k/reg.h [new file with mode: 0644]
drivers/net/wireless/ath9k/regd.c [new file with mode: 0644]
drivers/net/wireless/ath9k/regd.h [new file with mode: 0644]
drivers/net/wireless/ath9k/regd_common.h [new file with mode: 0644]
drivers/net/wireless/ath9k/xmit.c [new file with mode: 0644]

index 8223a52..c67a402 100644 (file)
@@ -720,6 +720,15 @@ L: linux-wireless@vger.kernel.org
 L:     ath5k-devel@lists.ath5k.org
 S:     Maintained
 
+ATHEROS ATH9K WIRELESS DRIVER
+P:     Luis R. Rodriguez
+M:     lrodriguez@atheros.com
+P:     Jouni Malinen
+M:     jmalinen@atheros.com
+L:     linux-wireless@vger.kernel.org
+L:     ath9k-devel@lists.ath9k.org
+S:     Supported
+
 ATI_REMOTE2 DRIVER
 P:     Ville Syrjala
 M:     syrjala@sci.fi
index 4c7ff61..9931b5a 100644 (file)
@@ -695,6 +695,7 @@ config MAC80211_HWSIM
 
 source "drivers/net/wireless/p54/Kconfig"
 source "drivers/net/wireless/ath5k/Kconfig"
+source "drivers/net/wireless/ath9k/Kconfig"
 source "drivers/net/wireless/iwlwifi/Kconfig"
 source "drivers/net/wireless/hostap/Kconfig"
 source "drivers/net/wireless/b43/Kconfig"
index 54a4f6f..59aa89e 100644 (file)
@@ -62,5 +62,6 @@ obj-$(CONFIG_RT2X00)  += rt2x00/
 obj-$(CONFIG_P54_COMMON)       += p54/
 
 obj-$(CONFIG_ATH5K)    += ath5k/
+obj-$(CONFIG_ATH9K)    += ath9k/
 
 obj-$(CONFIG_MAC80211_HWSIM)   += mac80211_hwsim.o
diff --git a/drivers/net/wireless/ath9k/Kconfig b/drivers/net/wireless/ath9k/Kconfig
new file mode 100644 (file)
index 0000000..9e19dcc
--- /dev/null
@@ -0,0 +1,8 @@
+config ATH9K
+       tristate "Atheros 802.11n wireless cards support"
+       depends on PCI && MAC80211 && WLAN_80211
+       ---help---
+         This module adds support for wireless adapters based on
+         Atheros IEEE 802.11n AR5008 and AR9001 family of chipsets.
+
+         If you choose to build a module, it'll be called ath9k.
diff --git a/drivers/net/wireless/ath9k/Makefile b/drivers/net/wireless/ath9k/Makefile
new file mode 100644 (file)
index 0000000..a641151
--- /dev/null
@@ -0,0 +1,11 @@
+ath9k-y +=     hw.o \
+               phy.o \
+               regd.o \
+               beacon.o \
+               main.o \
+               recv.o \
+               xmit.o \
+               rc.o \
+               core.o
+
+obj-$(CONFIG_ATH9K) += ath9k.o
diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h
new file mode 100644 (file)
index 0000000..dc1da64
--- /dev/null
@@ -0,0 +1,1066 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef ATH9K_H
+#define ATH9K_H
+
+#include <linux/io.h>
+
+#define ATHEROS_VENDOR_ID      0x168c
+
+#define AR5416_DEVID_PCI       0x0023
+#define AR5416_DEVID_PCIE      0x0024
+#define AR9160_DEVID_PCI       0x0027
+#define AR9280_DEVID_PCI       0x0029
+#define AR9280_DEVID_PCIE      0x002a
+
+#define AR5416_AR9100_DEVID    0x000b
+
+#define        AR_SUBVENDOR_ID_NOG     0x0e11
+#define AR_SUBVENDOR_ID_NEW_A  0x7065
+
+#define ATH9K_TXERR_XRETRY         0x01
+#define ATH9K_TXERR_FILT           0x02
+#define ATH9K_TXERR_FIFO           0x04
+#define ATH9K_TXERR_XTXOP          0x08
+#define ATH9K_TXERR_TIMER_EXPIRED  0x10
+
+#define ATH9K_TX_BA                0x01
+#define ATH9K_TX_PWRMGMT           0x02
+#define ATH9K_TX_DESC_CFG_ERR      0x04
+#define ATH9K_TX_DATA_UNDERRUN     0x08
+#define ATH9K_TX_DELIM_UNDERRUN    0x10
+#define ATH9K_TX_SW_ABORTED        0x40
+#define ATH9K_TX_SW_FILTERED       0x80
+
+#define NBBY    8
+
+struct ath_tx_status {
+       u32 ts_tstamp;
+       u16 ts_seqnum;
+       u8 ts_status;
+       u8 ts_ratecode;
+       u8 ts_rateindex;
+       int8_t ts_rssi;
+       u8 ts_shortretry;
+       u8 ts_longretry;
+       u8 ts_virtcol;
+       u8 ts_antenna;
+       u8 ts_flags;
+       int8_t ts_rssi_ctl0;
+       int8_t ts_rssi_ctl1;
+       int8_t ts_rssi_ctl2;
+       int8_t ts_rssi_ext0;
+       int8_t ts_rssi_ext1;
+       int8_t ts_rssi_ext2;
+       u8 pad[3];
+       u32 ba_low;
+       u32 ba_high;
+       u32 evm0;
+       u32 evm1;
+       u32 evm2;
+};
+
+struct ath_rx_status {
+       u32 rs_tstamp;
+       u16 rs_datalen;
+       u8 rs_status;
+       u8 rs_phyerr;
+       int8_t rs_rssi;
+       u8 rs_keyix;
+       u8 rs_rate;
+       u8 rs_antenna;
+       u8 rs_more;
+       int8_t rs_rssi_ctl0;
+       int8_t rs_rssi_ctl1;
+       int8_t rs_rssi_ctl2;
+       int8_t rs_rssi_ext0;
+       int8_t rs_rssi_ext1;
+       int8_t rs_rssi_ext2;
+       u8 rs_isaggr;
+       u8 rs_moreaggr;
+       u8 rs_num_delims;
+       u8 rs_flags;
+       u32 evm0;
+       u32 evm1;
+       u32 evm2;
+};
+
+#define ATH9K_RXERR_CRC           0x01
+#define ATH9K_RXERR_PHY           0x02
+#define ATH9K_RXERR_FIFO          0x04
+#define ATH9K_RXERR_DECRYPT       0x08
+#define ATH9K_RXERR_MIC           0x10
+
+#define ATH9K_RX_MORE             0x01
+#define ATH9K_RX_MORE_AGGR        0x02
+#define ATH9K_RX_GI               0x04
+#define ATH9K_RX_2040             0x08
+#define ATH9K_RX_DELIM_CRC_PRE    0x10
+#define ATH9K_RX_DELIM_CRC_POST   0x20
+#define ATH9K_RX_DECRYPT_BUSY     0x40
+
+#define ATH9K_RXKEYIX_INVALID  ((u8)-1)
+#define ATH9K_TXKEYIX_INVALID  ((u32)-1)
+
+struct ath_desc {
+       u32 ds_link;
+       u32 ds_data;
+       u32 ds_ctl0;
+       u32 ds_ctl1;
+       u32 ds_hw[20];
+       union {
+               struct ath_tx_status tx;
+               struct ath_rx_status rx;
+               void *stats;
+       } ds_us;
+       void *ds_vdata;
+} __packed;
+
+#define        ds_txstat       ds_us.tx
+#define        ds_rxstat       ds_us.rx
+#define ds_stat                ds_us.stats
+
+#define ATH9K_TXDESC_CLRDMASK          0x0001
+#define ATH9K_TXDESC_NOACK             0x0002
+#define ATH9K_TXDESC_RTSENA            0x0004
+#define ATH9K_TXDESC_CTSENA            0x0008
+#define ATH9K_TXDESC_INTREQ            0x0010
+#define ATH9K_TXDESC_VEOL              0x0020
+#define ATH9K_TXDESC_EXT_ONLY          0x0040
+#define ATH9K_TXDESC_EXT_AND_CTL       0x0080
+#define ATH9K_TXDESC_VMF               0x0100
+#define ATH9K_TXDESC_FRAG_IS_ON        0x0200
+
+#define ATH9K_RXDESC_INTREQ            0x0020
+
+enum hal_capability_type {
+       HAL_CAP_CIPHER = 0,
+       HAL_CAP_TKIP_MIC,
+       HAL_CAP_TKIP_SPLIT,
+       HAL_CAP_PHYCOUNTERS,
+       HAL_CAP_DIVERSITY,
+       HAL_CAP_PSPOLL,
+       HAL_CAP_TXPOW,
+       HAL_CAP_PHYDIAG,
+       HAL_CAP_MCAST_KEYSRCH,
+       HAL_CAP_TSF_ADJUST,
+       HAL_CAP_WME_TKIPMIC,
+       HAL_CAP_RFSILENT,
+       HAL_CAP_ANT_CFG_2GHZ,
+       HAL_CAP_ANT_CFG_5GHZ
+};
+
+struct hal_capabilities {
+       u32 halChanSpreadSupport:1,
+               halChapTuningSupport:1,
+               halMicAesCcmSupport:1,
+               halMicCkipSupport:1,
+               halMicTkipSupport:1,
+               halCipherAesCcmSupport:1,
+               halCipherCkipSupport:1,
+               halCipherTkipSupport:1,
+               halVEOLSupport:1,
+               halBssIdMaskSupport:1,
+               halMcastKeySrchSupport:1,
+               halTsfAddSupport:1,
+               halChanHalfRate:1,
+               halChanQuarterRate:1,
+               halHTSupport:1,
+               halGTTSupport:1,
+               halFastCCSupport:1,
+               halRfSilentSupport:1,
+               halWowSupport:1,
+               halCSTSupport:1,
+               halEnhancedPmSupport:1,
+               halAutoSleepSupport:1,
+               hal4kbSplitTransSupport:1,
+               halWowMatchPatternExact:1;
+       u32 halWirelessModes;
+       u16 halTotalQueues;
+       u16 halKeyCacheSize;
+       u16 halLow5GhzChan, halHigh5GhzChan;
+       u16 halLow2GhzChan, halHigh2GhzChan;
+       u16 halNumMRRetries;
+       u16 halRtsAggrLimit;
+       u8 halTxChainMask;
+       u8 halRxChainMask;
+       u16 halTxTrigLevelMax;
+       u16 halRegCap;
+       u8 halNumGpioPins;
+       u8 halNumAntCfg2GHz;
+       u8 halNumAntCfg5GHz;
+};
+
+struct hal_ops_config {
+       int ath_hal_dma_beacon_response_time;
+       int ath_hal_sw_beacon_response_time;
+       int ath_hal_additional_swba_backoff;
+       int ath_hal_6mb_ack;
+       int ath_hal_cwmIgnoreExtCCA;
+       u8 ath_hal_pciePowerSaveEnable;
+       u8 ath_hal_pcieL1SKPEnable;
+       u8 ath_hal_pcieClockReq;
+       u32 ath_hal_pcieWaen;
+       int ath_hal_pciePowerReset;
+       u8 ath_hal_pcieRestore;
+       u8 ath_hal_analogShiftReg;
+       u8 ath_hal_htEnable;
+       u32 ath_hal_ofdmTrigLow;
+       u32 ath_hal_ofdmTrigHigh;
+       u32 ath_hal_cckTrigHigh;
+       u32 ath_hal_cckTrigLow;
+       u32 ath_hal_enableANI;
+       u8 ath_hal_noiseImmunityLvl;
+       u32 ath_hal_ofdmWeakSigDet;
+       u32 ath_hal_cckWeakSigThr;
+       u8 ath_hal_spurImmunityLvl;
+       u8 ath_hal_firStepLvl;
+       int8_t ath_hal_rssiThrHigh;
+       int8_t ath_hal_rssiThrLow;
+       u16 ath_hal_diversityControl;
+       u16 ath_hal_antennaSwitchSwap;
+       int ath_hal_serializeRegMode;
+       int ath_hal_intrMitigation;
+#define SPUR_DISABLE           0
+#define SPUR_ENABLE_IOCTL      1
+#define SPUR_ENABLE_EEPROM     2
+#define AR_EEPROM_MODAL_SPURS   5
+#define AR_SPUR_5413_1         1640
+#define AR_SPUR_5413_2         1200
+#define AR_NO_SPUR             0x8000
+#define AR_BASE_FREQ_2GHZ      2300
+#define AR_BASE_FREQ_5GHZ      4900
+#define AR_SPUR_FEEQ_BOUND_HT40 19
+#define AR_SPUR_FEEQ_BOUND_HT20 10
+       int ath_hal_spurMode;
+       u16 ath_hal_spurChans[AR_EEPROM_MODAL_SPURS][2];
+};
+
+enum ath9k_tx_queue {
+       ATH9K_TX_QUEUE_INACTIVE = 0,
+       ATH9K_TX_QUEUE_DATA,
+       ATH9K_TX_QUEUE_BEACON,
+       ATH9K_TX_QUEUE_CAB,
+       ATH9K_TX_QUEUE_UAPSD,
+       ATH9K_TX_QUEUE_PSPOLL
+};
+
+#define        ATH9K_NUM_TX_QUEUES 10
+
+enum ath9k_tx_queue_subtype {
+       ATH9K_WME_AC_BK = 0,
+       ATH9K_WME_AC_BE,
+       ATH9K_WME_AC_VI,
+       ATH9K_WME_AC_VO,
+       ATH9K_WME_UPSD
+};
+
+enum ath9k_tx_queue_flags {
+       TXQ_FLAG_TXOKINT_ENABLE = 0x0001,
+       TXQ_FLAG_TXERRINT_ENABLE = 0x0001,
+       TXQ_FLAG_TXDESCINT_ENABLE = 0x0002,
+       TXQ_FLAG_TXEOLINT_ENABLE = 0x0004,
+       TXQ_FLAG_TXURNINT_ENABLE = 0x0008,
+       TXQ_FLAG_BACKOFF_DISABLE = 0x0010,
+       TXQ_FLAG_COMPRESSION_ENABLE = 0x0020,
+       TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE = 0x0040,
+       TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080,
+};
+
+struct ath9k_txq_info {
+       u32 tqi_ver;
+       enum ath9k_tx_queue_subtype tqi_subtype;
+       enum ath9k_tx_queue_flags tqi_qflags;
+       u32 tqi_priority;
+       u32 tqi_aifs;
+       u32 tqi_cwmin;
+       u32 tqi_cwmax;
+       u16 tqi_shretry;
+       u16 tqi_lgretry;
+       u32 tqi_cbrPeriod;
+       u32 tqi_cbrOverflowLimit;
+       u32 tqi_burstTime;
+       u32 tqi_readyTime;
+       u32 tqi_compBuf;
+};
+
+#define ATH9K_TXQ_USEDEFAULT ((u32) -1)
+
+#define ATH9K_DECOMP_MASK_SIZE     128
+#define ATH9K_READY_TIME_LO_BOUND  50
+#define ATH9K_READY_TIME_HI_BOUND  96
+
+enum ath9k_pkt_type {
+       ATH9K_PKT_TYPE_NORMAL = 0,
+       ATH9K_PKT_TYPE_ATIM,
+       ATH9K_PKT_TYPE_PSPOLL,
+       ATH9K_PKT_TYPE_BEACON,
+       ATH9K_PKT_TYPE_PROBE_RESP,
+       ATH9K_PKT_TYPE_CHIRP,
+       ATH9K_PKT_TYPE_GRP_POLL,
+};
+
+struct ath9k_tx_queue_info {
+       u32 tqi_ver;
+       enum ath9k_tx_queue tqi_type;
+       enum ath9k_tx_queue_subtype tqi_subtype;
+       enum ath9k_tx_queue_flags tqi_qflags;
+       u32 tqi_priority;
+       u32 tqi_aifs;
+       u32 tqi_cwmin;
+       u32 tqi_cwmax;
+       u16 tqi_shretry;
+       u16 tqi_lgretry;
+       u32 tqi_cbrPeriod;
+       u32 tqi_cbrOverflowLimit;
+       u32 tqi_burstTime;
+       u32 tqi_readyTime;
+       u32 tqi_physCompBuf;
+       u32 tqi_intFlags;
+};
+
+enum ath9k_rx_filter {
+       ATH9K_RX_FILTER_UCAST = 0x00000001,
+       ATH9K_RX_FILTER_MCAST = 0x00000002,
+       ATH9K_RX_FILTER_BCAST = 0x00000004,
+       ATH9K_RX_FILTER_CONTROL = 0x00000008,
+       ATH9K_RX_FILTER_BEACON = 0x00000010,
+       ATH9K_RX_FILTER_PROM = 0x00000020,
+       ATH9K_RX_FILTER_PROBEREQ = 0x00000080,
+       ATH9K_RX_FILTER_PSPOLL = 0x00004000,
+       ATH9K_RX_FILTER_PHYERR = 0x00000100,
+       ATH9K_RX_FILTER_PHYRADAR = 0x00002000,
+};
+
+enum ath9k_int {
+       ATH9K_INT_RX = 0x00000001,
+       ATH9K_INT_RXDESC = 0x00000002,
+       ATH9K_INT_RXNOFRM = 0x00000008,
+       ATH9K_INT_RXEOL = 0x00000010,
+       ATH9K_INT_RXORN = 0x00000020,
+       ATH9K_INT_TX = 0x00000040,
+       ATH9K_INT_TXDESC = 0x00000080,
+       ATH9K_INT_TIM_TIMER = 0x00000100,
+       ATH9K_INT_TXURN = 0x00000800,
+       ATH9K_INT_MIB = 0x00001000,
+       ATH9K_INT_RXPHY = 0x00004000,
+       ATH9K_INT_RXKCM = 0x00008000,
+       ATH9K_INT_SWBA = 0x00010000,
+       ATH9K_INT_BMISS = 0x00040000,
+       ATH9K_INT_BNR = 0x00100000,
+       ATH9K_INT_TIM = 0x00200000,
+       ATH9K_INT_DTIM = 0x00400000,
+       ATH9K_INT_DTIMSYNC = 0x00800000,
+       ATH9K_INT_GPIO = 0x01000000,
+       ATH9K_INT_CABEND = 0x02000000,
+       ATH9K_INT_CST = 0x10000000,
+       ATH9K_INT_GTT = 0x20000000,
+       ATH9K_INT_FATAL = 0x40000000,
+       ATH9K_INT_GLOBAL = 0x80000000,
+       ATH9K_INT_BMISC = ATH9K_INT_TIM |
+               ATH9K_INT_DTIM |
+               ATH9K_INT_DTIMSYNC |
+               ATH9K_INT_CABEND,
+       ATH9K_INT_COMMON = ATH9K_INT_RXNOFRM |
+               ATH9K_INT_RXDESC |
+               ATH9K_INT_RXEOL |
+               ATH9K_INT_RXORN |
+               ATH9K_INT_TXURN |
+               ATH9K_INT_TXDESC |
+               ATH9K_INT_MIB |
+               ATH9K_INT_RXPHY |
+               ATH9K_INT_RXKCM |
+               ATH9K_INT_SWBA |
+               ATH9K_INT_BMISS |
+               ATH9K_INT_GPIO,
+       ATH9K_INT_NOCARD = 0xffffffff
+};
+
+struct ath9k_rate_table {
+       int rateCount;
+       u8 rateCodeToIndex[256];
+       struct {
+               u8 valid;
+               u8 phy;
+               u32 rateKbps;
+               u8 rateCode;
+               u8 shortPreamble;
+               u8 dot11Rate;
+               u8 controlRate;
+               u16 lpAckDuration;
+               u16 spAckDuration;
+       } info[32];
+};
+
+#define ATH9K_RATESERIES_RTS_CTS  0x0001
+#define ATH9K_RATESERIES_2040     0x0002
+#define ATH9K_RATESERIES_HALFGI   0x0004
+
+struct ath9k_11n_rate_series {
+       u32 Tries;
+       u32 Rate;
+       u32 PktDuration;
+       u32 ChSel;
+       u32 RateFlags;
+};
+
+#define CHANNEL_CW_INT    0x00002
+#define CHANNEL_CCK       0x00020
+#define CHANNEL_OFDM      0x00040
+#define CHANNEL_2GHZ      0x00080
+#define CHANNEL_5GHZ      0x00100
+#define CHANNEL_PASSIVE   0x00200
+#define CHANNEL_DYN       0x00400
+#define CHANNEL_HALF      0x04000
+#define CHANNEL_QUARTER   0x08000
+#define CHANNEL_HT20      0x10000
+#define CHANNEL_HT40PLUS  0x20000
+#define CHANNEL_HT40MINUS 0x40000
+
+#define CHANNEL_INTERFERENCE    0x01
+#define CHANNEL_DFS             0x02
+#define CHANNEL_4MS_LIMIT       0x04
+#define CHANNEL_DFS_CLEAR       0x08
+#define CHANNEL_DISALLOW_ADHOC  0x10
+#define CHANNEL_PER_11D_ADHOC   0x20
+
+#define CHANNEL_A           (CHANNEL_5GHZ|CHANNEL_OFDM)
+#define CHANNEL_B           (CHANNEL_2GHZ|CHANNEL_CCK)
+#define CHANNEL_G           (CHANNEL_2GHZ|CHANNEL_OFDM)
+#define CHANNEL_G_HT20      (CHANNEL_2GHZ|CHANNEL_HT20)
+#define CHANNEL_A_HT20      (CHANNEL_5GHZ|CHANNEL_HT20)
+#define CHANNEL_G_HT40PLUS  (CHANNEL_2GHZ|CHANNEL_HT40PLUS)
+#define CHANNEL_G_HT40MINUS (CHANNEL_2GHZ|CHANNEL_HT40MINUS)
+#define CHANNEL_A_HT40PLUS  (CHANNEL_5GHZ|CHANNEL_HT40PLUS)
+#define CHANNEL_A_HT40MINUS (CHANNEL_5GHZ|CHANNEL_HT40MINUS)
+#define CHANNEL_ALL                            \
+       (CHANNEL_OFDM|                          \
+        CHANNEL_CCK|                           \
+        CHANNEL_2GHZ |                         \
+        CHANNEL_5GHZ |                         \
+        CHANNEL_HT20 |                         \
+        CHANNEL_HT40PLUS |                     \
+        CHANNEL_HT40MINUS)
+
+struct ath9k_channel {
+       u16 channel;
+       u32 channelFlags;
+       u8 privFlags;
+       int8_t maxRegTxPower;
+       int8_t maxTxPower;
+       int8_t minTxPower;
+       u32 chanmode;
+       int32_t CalValid;
+       bool oneTimeCalsDone;
+       int8_t iCoff;
+       int8_t qCoff;
+       int16_t rawNoiseFloor;
+       int8_t antennaMax;
+       u32 regDmnFlags;
+       u32 conformanceTestLimit[3]; /* 0:11a, 1: 11b, 2:11g */
+#ifdef ATH_NF_PER_CHAN
+       struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
+#endif
+};
+
+#define IS_CHAN_A(_c) ((((_c)->channelFlags & CHANNEL_A) == CHANNEL_A) || \
+       (((_c)->channelFlags & CHANNEL_A_HT20) == CHANNEL_A_HT20) || \
+       (((_c)->channelFlags & CHANNEL_A_HT40PLUS) == CHANNEL_A_HT40PLUS) || \
+       (((_c)->channelFlags & CHANNEL_A_HT40MINUS) == CHANNEL_A_HT40MINUS))
+#define IS_CHAN_B(_c) (((_c)->channelFlags & CHANNEL_B) == CHANNEL_B)
+#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \
+       (((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \
+       (((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \
+       (((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS))
+#define IS_CHAN_CCK(_c) (((_c)->channelFlags & CHANNEL_CCK) != 0)
+#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0)
+#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0)
+#define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0)
+#define IS_CHAN_PASSIVE(_c) (((_c)->channelFlags & CHANNEL_PASSIVE) != 0)
+#define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0)
+#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0)
+
+/* These macros check chanmode and not channelFlags */
+#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) ||        \
+                         ((_c)->chanmode == CHANNEL_G_HT20))
+#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) ||    \
+                         ((_c)->chanmode == CHANNEL_A_HT40MINUS) ||    \
+                         ((_c)->chanmode == CHANNEL_G_HT40PLUS) ||     \
+                         ((_c)->chanmode == CHANNEL_G_HT40MINUS))
+#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c)))
+
+#define IS_CHAN_IN_PUBLIC_SAFETY_BAND(_c) ((_c) > 4940 && (_c) < 4990)
+#define IS_CHAN_A_5MHZ_SPACED(_c)                      \
+       ((((_c)->channelFlags & CHANNEL_5GHZ) != 0) &&  \
+        (((_c)->channel % 20) != 0) &&                 \
+        (((_c)->channel % 10) != 0))
+
+struct ath9k_keyval {
+       u8 kv_type;
+       u8 kv_pad;
+       u16 kv_len;
+       u8 kv_val[16];
+       u8 kv_mic[8];
+       u8 kv_txmic[8];
+};
+
+enum ath9k_key_type {
+       ATH9K_KEY_TYPE_CLEAR,
+       ATH9K_KEY_TYPE_WEP,
+       ATH9K_KEY_TYPE_AES,
+       ATH9K_KEY_TYPE_TKIP,
+};
+
+enum ath9k_cipher {
+       ATH9K_CIPHER_WEP = 0,
+       ATH9K_CIPHER_AES_OCB = 1,
+       ATH9K_CIPHER_AES_CCM = 2,
+       ATH9K_CIPHER_CKIP = 3,
+       ATH9K_CIPHER_TKIP = 4,
+       ATH9K_CIPHER_CLR = 5,
+       ATH9K_CIPHER_MIC = 127
+};
+
+#define AR_EEPROM_EEPCAP_COMPRESS_DIS   0x0001
+#define AR_EEPROM_EEPCAP_AES_DIS        0x0002
+#define AR_EEPROM_EEPCAP_FASTFRAME_DIS  0x0004
+#define AR_EEPROM_EEPCAP_BURST_DIS      0x0008
+#define AR_EEPROM_EEPCAP_MAXQCU         0x01F0
+#define AR_EEPROM_EEPCAP_MAXQCU_S       4
+#define AR_EEPROM_EEPCAP_HEAVY_CLIP_EN  0x0200
+#define AR_EEPROM_EEPCAP_KC_ENTRIES     0xF000
+#define AR_EEPROM_EEPCAP_KC_ENTRIES_S   12
+
+#define AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND   0x0040
+#define AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN    0x0080
+#define AR_EEPROM_EEREGCAP_EN_KK_U2         0x0100
+#define AR_EEPROM_EEREGCAP_EN_KK_MIDBAND    0x0200
+#define AR_EEPROM_EEREGCAP_EN_KK_U1_ODD     0x0400
+#define AR_EEPROM_EEREGCAP_EN_KK_NEW_11A    0x0800
+
+#define AR_EEPROM_EEREGCAP_EN_KK_U1_ODD_PRE4_0  0x4000
+#define AR_EEPROM_EEREGCAP_EN_KK_NEW_11A_PRE4_0 0x8000
+
+#define SD_NO_CTL               0xE0
+#define NO_CTL                  0xff
+#define CTL_MODE_M              7
+#define CTL_11A                 0
+#define CTL_11B                 1
+#define CTL_11G                 2
+#define CTL_2GHT20              5
+#define CTL_5GHT20              6
+#define CTL_2GHT40              7
+#define CTL_5GHT40              8
+
+#define AR_EEPROM_MAC(i)        (0x1d+(i))
+#define EEP_SCALE       100
+#define EEP_DELTA       10
+
+#define AR_EEPROM_RFSILENT_GPIO_SEL     0x001c
+#define AR_EEPROM_RFSILENT_GPIO_SEL_S   2
+#define AR_EEPROM_RFSILENT_POLARITY     0x0002
+#define AR_EEPROM_RFSILENT_POLARITY_S   1
+
+#define CTRY_DEBUG 0x1ff
+#define        CTRY_DEFAULT 0
+
+enum reg_ext_bitmap {
+       REG_EXT_JAPAN_MIDBAND = 1,
+       REG_EXT_FCC_DFS_HT40 = 2,
+       REG_EXT_JAPAN_NONDFS_HT40 = 3,
+       REG_EXT_JAPAN_DFS_HT40 = 4
+};
+
+struct ath9k_country_entry {
+       u16 countryCode;
+       u16 regDmnEnum;
+       u16 regDmn5G;
+       u16 regDmn2G;
+       u8 isMultidomain;
+       u8 iso[3];
+};
+
+#define REG_WRITE(_ah, _reg, _val) iowrite32(_val, _ah->ah_sh + _reg)
+#define REG_READ(_ah, _reg) ioread32(_ah->ah_sh + _reg)
+
+#define SM(_v, _f)  (((_v) << _f##_S) & _f)
+#define MS(_v, _f)  (((_v) & _f) >> _f##_S)
+#define REG_RMW(_a, _r, _set, _clr)    \
+       REG_WRITE(_a, _r, (REG_READ(_a, _r) & ~(_clr)) | (_set))
+#define REG_RMW_FIELD(_a, _r, _f, _v) \
+       REG_WRITE(_a, _r, \
+       (REG_READ(_a, _r) & ~_f) | (((_v) << _f##_S) & _f))
+#define REG_SET_BIT(_a, _r, _f) \
+       REG_WRITE(_a, _r, REG_READ(_a, _r) | _f)
+#define REG_CLR_BIT(_a, _r, _f) \
+       REG_WRITE(_a, _r, REG_READ(_a, _r) & ~_f)
+
+#define ATH9K_COMP_BUF_MAX_SIZE   9216
+#define ATH9K_COMP_BUF_ALIGN_SIZE 512
+
+#define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS   0x00000001
+
+#define INIT_AIFS       2
+#define INIT_CWMIN      15
+#define INIT_CWMIN_11B  31
+#define INIT_CWMAX      1023
+#define INIT_SH_RETRY   10
+#define INIT_LG_RETRY   10
+#define INIT_SSH_RETRY  32
+#define INIT_SLG_RETRY  32
+
+#define WLAN_CTRL_FRAME_SIZE (2+2+6+4)
+
+#define ATH_AMPDU_LIMIT_MAX      (64 * 1024 - 1)
+#define ATH_AMPDU_LIMIT_DEFAULT  ATH_AMPDU_LIMIT_MAX
+
+#define IEEE80211_WEP_IVLEN      3
+#define IEEE80211_WEP_KIDLEN     1
+#define IEEE80211_WEP_CRCLEN     4
+#define IEEE80211_MAX_MPDU_LEN  (3840 + FCS_LEN +              \
+                                (IEEE80211_WEP_IVLEN +         \
+                                 IEEE80211_WEP_KIDLEN +        \
+                                 IEEE80211_WEP_CRCLEN))
+#define IEEE80211_MAX_LEN       (2300 + FCS_LEN +              \
+                                (IEEE80211_WEP_IVLEN +         \
+                                 IEEE80211_WEP_KIDLEN +        \
+                                 IEEE80211_WEP_CRCLEN))
+
+#define MAX_REG_ADD_COUNT   129
+#define MAX_RATE_POWER 63
+
+enum ath9k_power_mode {
+       ATH9K_PM_AWAKE = 0,
+       ATH9K_PM_FULL_SLEEP,
+       ATH9K_PM_NETWORK_SLEEP,
+       ATH9K_PM_UNDEFINED
+};
+
+#define HAL_ANTENNA_MIN_MODE  0
+#define HAL_ANTENNA_FIXED_A   1
+#define HAL_ANTENNA_FIXED_B   2
+#define HAL_ANTENNA_MAX_MODE  3
+
+struct ath9k_mib_stats {
+       u32 ackrcv_bad;
+       u32 rts_bad;
+       u32 rts_good;
+       u32 fcs_bad;
+       u32 beacons;
+};
+
+enum ath9k_ant_setting {
+       ATH9K_ANT_VARIABLE = 0,
+       ATH9K_ANT_FIXED_A,
+       ATH9K_ANT_FIXED_B
+};
+
+enum ath9k_opmode {
+       ATH9K_M_STA = 1,
+       ATH9K_M_IBSS = 0,
+       ATH9K_M_HOSTAP = 6,
+       ATH9K_M_MONITOR = 8
+};
+
+#define ATH9K_SLOT_TIME_6 6
+#define ATH9K_SLOT_TIME_9 9
+#define ATH9K_SLOT_TIME_20 20
+
+enum ath9k_ht_macmode {
+       ATH9K_HT_MACMODE_20 = 0,
+       ATH9K_HT_MACMODE_2040 = 1,
+};
+
+enum ath9k_ht_extprotspacing {
+       ATH9K_HT_EXTPROTSPACING_20 = 0,
+       ATH9K_HT_EXTPROTSPACING_25 = 1,
+};
+
+struct ath9k_ht_cwm {
+       enum ath9k_ht_macmode ht_macmode;
+       enum ath9k_ht_extprotspacing ht_extprotspacing;
+};
+
+enum hal_freq_band {
+       HAL_FREQ_BAND_5GHZ = 0,
+       HAL_FREQ_BAND_2GHZ = 1,
+};
+
+enum ath9k_ani_cmd {
+       ATH9K_ANI_PRESENT = 0x1,
+       ATH9K_ANI_NOISE_IMMUNITY_LEVEL = 0x2,
+       ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION = 0x4,
+       ATH9K_ANI_CCK_WEAK_SIGNAL_THR = 0x8,
+       ATH9K_ANI_FIRSTEP_LEVEL = 0x10,
+       ATH9K_ANI_SPUR_IMMUNITY_LEVEL = 0x20,
+       ATH9K_ANI_MODE = 0x40,
+       ATH9K_ANI_PHYERR_RESET = 0x80,
+       ATH9K_ANI_ALL = 0xff
+};
+
+enum phytype {
+       PHY_DS,
+       PHY_FH,
+       PHY_OFDM,
+       PHY_HT,
+       PHY_MAX
+};
+#define PHY_CCK PHY_DS
+
+enum start_adhoc_option {
+       START_ADHOC_NO_11A,
+       START_ADHOC_PER_11D,
+       START_ADHOC_IN_11A,
+       START_ADHOC_IN_11B,
+};
+
+enum ath9k_tp_scale {
+       ATH9K_TP_SCALE_MAX = 0,
+       ATH9K_TP_SCALE_50,
+       ATH9K_TP_SCALE_25,
+       ATH9K_TP_SCALE_12,
+       ATH9K_TP_SCALE_MIN
+};
+
+enum ser_reg_mode {
+       SER_REG_MODE_OFF = 0,
+       SER_REG_MODE_ON = 1,
+       SER_REG_MODE_AUTO = 2,
+};
+
+#define AR_PHY_CCA_MAX_GOOD_VALUE                      -85
+#define AR_PHY_CCA_MAX_HIGH_VALUE                      -62
+#define AR_PHY_CCA_MIN_BAD_VALUE                       -121
+#define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT     3
+#define AR_PHY_CCA_FILTERWINDOW_LENGTH          5
+
+#define ATH9K_NF_CAL_HIST_MAX           5
+#define NUM_NF_READINGS                 6
+
+struct ath9k_nfcal_hist {
+       int16_t nfCalBuffer[ATH9K_NF_CAL_HIST_MAX];
+       u8 currIndex;
+       int16_t privNF;
+       u8 invalidNFcount;
+};
+
+struct ath9k_beacon_state {
+       u32 bs_nexttbtt;
+       u32 bs_nextdtim;
+       u32 bs_intval;
+#define ATH9K_BEACON_PERIOD       0x0000ffff
+#define ATH9K_BEACON_ENA          0x00800000
+#define ATH9K_BEACON_RESET_TSF    0x01000000
+       u32 bs_dtimperiod;
+       u16 bs_cfpperiod;
+       u16 bs_cfpmaxduration;
+       u32 bs_cfpnext;
+       u16 bs_timoffset;
+       u16 bs_bmissthreshold;
+       u32 bs_sleepduration;
+};
+
+struct ath9k_node_stats {
+       u32 ns_avgbrssi;
+       u32 ns_avgrssi;
+       u32 ns_avgtxrssi;
+       u32 ns_avgtxrate;
+};
+
+#define ATH9K_RSSI_EP_MULTIPLIER  (1<<7)
+
+enum ath9k_gpio_output_mux_type {
+       ATH9K_GPIO_OUTPUT_MUX_AS_OUTPUT,
+       ATH9K_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED,
+       ATH9K_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED,
+       ATH9K_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED,
+       ATH9K_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED,
+       ATH9K_GPIO_OUTPUT_MUX_NUM_ENTRIES
+};
+
+enum {
+       ATH9K_RESET_POWER_ON,
+       ATH9K_RESET_WARM,
+       ATH9K_RESET_COLD,
+};
+
+#define AH_USE_EEPROM   0x1
+
+struct ath_hal {
+       u32 ah_magic;
+       u16 ah_devid;
+       u16 ah_subvendorid;
+       struct ath_softc *ah_sc;
+       void __iomem *ah_sh;
+       u16 ah_countryCode;
+       u32 ah_macVersion;
+       u16 ah_macRev;
+       u16 ah_phyRev;
+       u16 ah_analog5GhzRev;
+       u16 ah_analog2GhzRev;
+       u8 ah_decompMask[ATH9K_DECOMP_MASK_SIZE];
+       u32 ah_flags;
+       enum ath9k_opmode ah_opmode;
+       struct hal_ops_config ah_config;
+       struct hal_capabilities ah_caps;
+       int16_t ah_powerLimit;
+       u16 ah_maxPowerLevel;
+       u32 ah_tpScale;
+       u16 ah_currentRD;
+       u16 ah_currentRDExt;
+       u16 ah_currentRDInUse;
+       u16 ah_currentRD5G;
+       u16 ah_currentRD2G;
+       char ah_iso[4];
+       enum start_adhoc_option ah_adHocMode;
+       bool ah_commonMode;
+       struct ath9k_channel ah_channels[150];
+       u32 ah_nchan;
+       struct ath9k_channel *ah_curchan;
+       u16 ah_rfsilent;
+       bool ah_rfkillEnabled;
+       bool ah_isPciExpress;
+       u16 ah_txTrigLevel;
+#ifndef ATH_NF_PER_CHAN
+       struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
+#endif
+};
+
+enum wireless_mode {
+       WIRELESS_MODE_11a = 0,
+       WIRELESS_MODE_11b = 2,
+       WIRELESS_MODE_11g = 3,
+       WIRELESS_MODE_11NA_HT20 = 6,
+       WIRELESS_MODE_11NG_HT20 = 7,
+       WIRELESS_MODE_11NA_HT40PLUS = 8,
+       WIRELESS_MODE_11NA_HT40MINUS = 9,
+       WIRELESS_MODE_11NG_HT40PLUS = 10,
+       WIRELESS_MODE_11NG_HT40MINUS = 11,
+       WIRELESS_MODE_MAX
+};
+
+enum {
+       ATH9K_MODE_SEL_11A = 0x00001,
+       ATH9K_MODE_SEL_11B = 0x00002,
+       ATH9K_MODE_SEL_11G = 0x00004,
+       ATH9K_MODE_SEL_11NG_HT20 = 0x00008,
+       ATH9K_MODE_SEL_11NA_HT20 = 0x00010,
+       ATH9K_MODE_SEL_11NG_HT40PLUS = 0x00020,
+       ATH9K_MODE_SEL_11NG_HT40MINUS = 0x00040,
+       ATH9K_MODE_SEL_11NA_HT40PLUS = 0x00080,
+       ATH9K_MODE_SEL_11NA_HT40MINUS = 0x00100,
+       ATH9K_MODE_SEL_2GHZ = (ATH9K_MODE_SEL_11B |
+                              ATH9K_MODE_SEL_11G |
+                              ATH9K_MODE_SEL_11NG_HT20),
+       ATH9K_MODE_SEL_5GHZ = (ATH9K_MODE_SEL_11A |
+                              ATH9K_MODE_SEL_11NA_HT20),
+       ATH9K_MODE_SEL_ALL = 0xffffffff
+};
+
+struct chan_centers {
+       u16 synth_center;
+       u16 ctl_center;
+       u16 ext_center;
+};
+
+int ath_hal_getcapability(struct ath_hal *ah,
+                         enum hal_capability_type type,
+                         u32 capability,
+                         u32 *result);
+const struct ath9k_rate_table *ath9k_hw_getratetable(struct ath_hal *ah,
+                                                    u32 mode);
+void ath9k_hw_detach(struct ath_hal *ah);
+struct ath_hal *ath9k_hw_attach(u16 devid,
+                               struct ath_softc *sc,
+                               void __iomem *mem,
+                               int *error);
+bool ath9k_regd_init_channels(struct ath_hal *ah,
+                             u32 maxchans, u32 *nchans,
+                             u8 *regclassids,
+                             u32 maxregids, u32 *nregids,
+                             u16 cc, u32 modeSelect,
+                             bool enableOutdoor,
+                             bool enableExtendedChannels);
+u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags);
+enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah,
+                                    enum ath9k_int ints);
+bool ath9k_hw_reset(struct ath_hal *ah, enum ath9k_opmode opmode,
+                   struct ath9k_channel *chan,
+                   enum ath9k_ht_macmode macmode,
+                   u8 txchainmask, u8 rxchainmask,
+                   enum ath9k_ht_extprotspacing extprotspacing,
+                   bool bChannelChange,
+                   int *status);
+bool ath9k_hw_phy_disable(struct ath_hal *ah);
+void ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan,
+                            bool *isCalDone);
+void ath9k_hw_ani_monitor(struct ath_hal *ah,
+                         const struct ath9k_node_stats *stats,
+                         struct ath9k_channel *chan);
+bool ath9k_hw_calibrate(struct ath_hal *ah,
+                       struct ath9k_channel *chan,
+                       u8 rxchainmask,
+                       bool longcal,
+                       bool *isCalDone);
+int16_t ath9k_hw_getchan_noise(struct ath_hal *ah,
+                              struct ath9k_channel *chan);
+void ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid,
+                           u16 assocId);
+void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits);
+void ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid,
+                           u16 assocId);
+bool ath9k_hw_stoptxdma(struct ath_hal *ah, u32 q);
+void ath9k_hw_reset_tsf(struct ath_hal *ah);
+bool ath9k_hw_keyisvalid(struct ath_hal *ah, u16 entry);
+bool ath9k_hw_keysetmac(struct ath_hal *ah, u16 entry,
+                       const u8 *mac);
+bool ath9k_hw_set_keycache_entry(struct ath_hal *ah,
+                                u16 entry,
+                                const struct ath9k_keyval *k,
+                                const u8 *mac,
+                                int xorKey);
+bool ath9k_hw_set_tsfadjust(struct ath_hal *ah,
+                           u32 setting);
+void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore);
+bool ath9k_hw_intrpend(struct ath_hal *ah);
+bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked);
+bool ath9k_hw_updatetxtriglevel(struct ath_hal *ah,
+                               bool bIncTrigLevel);
+void ath9k_hw_procmibevent(struct ath_hal *ah,
+                          const struct ath9k_node_stats *stats);
+bool ath9k_hw_setrxabort(struct ath_hal *ah, bool set);
+void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode);
+bool ath9k_hw_phycounters(struct ath_hal *ah);
+bool ath9k_hw_keyreset(struct ath_hal *ah, u16 entry);
+bool ath9k_hw_getcapability(struct ath_hal *ah,
+                           enum hal_capability_type type,
+                           u32 capability,
+                           u32 *result);
+bool ath9k_hw_setcapability(struct ath_hal *ah,
+                           enum hal_capability_type type,
+                           u32 capability,
+                           u32 setting,
+                           int *status);
+u32 ath9k_hw_getdefantenna(struct ath_hal *ah);
+void ath9k_hw_getmac(struct ath_hal *ah, u8 *mac);
+void ath9k_hw_getbssidmask(struct ath_hal *ah, u8 *mask);
+bool ath9k_hw_setbssidmask(struct ath_hal *ah,
+                          const u8 *mask);
+bool ath9k_hw_setpower(struct ath_hal *ah,
+                      enum ath9k_power_mode mode);
+enum ath9k_int ath9k_hw_intrget(struct ath_hal *ah);
+u64 ath9k_hw_gettsf64(struct ath_hal *ah);
+u32 ath9k_hw_getdefantenna(struct ath_hal *ah);
+bool ath9k_hw_setslottime(struct ath_hal *ah, u32 us);
+bool ath9k_hw_setantennaswitch(struct ath_hal *ah,
+                              enum ath9k_ant_setting settings,
+                              struct ath9k_channel *chan,
+                              u8 *tx_chainmask,
+                              u8 *rx_chainmask,
+                              u8 *antenna_cfgd);
+void ath9k_hw_setantenna(struct ath_hal *ah, u32 antenna);
+int ath9k_hw_select_antconfig(struct ath_hal *ah,
+                             u32 cfg);
+bool ath9k_hw_puttxbuf(struct ath_hal *ah, u32 q,
+                      u32 txdp);
+bool ath9k_hw_txstart(struct ath_hal *ah, u32 q);
+u16 ath9k_hw_computetxtime(struct ath_hal *ah,
+                                const struct ath9k_rate_table *rates,
+                                u32 frameLen, u16 rateix,
+                                bool shortPreamble);
+void ath9k_hw_set11n_ratescenario(struct ath_hal *ah, struct ath_desc *ds,
+                                 struct ath_desc *lastds,
+                                 u32 durUpdateEn, u32 rtsctsRate,
+                                 u32 rtsctsDuration,
+                                 struct ath9k_11n_rate_series series[],
+                                 u32 nseries, u32 flags);
+void ath9k_hw_set11n_burstduration(struct ath_hal *ah,
+                                  struct ath_desc *ds,
+                                  u32 burstDuration);
+void ath9k_hw_cleartxdesc(struct ath_hal *ah, struct ath_desc *ds);
+u32 ath9k_hw_reverse_bits(u32 val, u32 n);
+bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q);
+u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan);
+u32 ath9k_regd_get_antenna_allowed(struct ath_hal *ah,
+                                    struct ath9k_channel *chan);
+u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags);
+bool ath9k_hw_gettxqueueprops(struct ath_hal *ah, int q,
+                             struct ath9k_txq_info *qInfo);
+bool ath9k_hw_settxqueueprops(struct ath_hal *ah, int q,
+                             const struct ath9k_txq_info *qInfo);
+struct ath9k_channel *ath9k_regd_check_channel(struct ath_hal *ah,
+                                             const struct ath9k_channel *c);
+void ath9k_hw_set11n_txdesc(struct ath_hal *ah, struct ath_desc *ds,
+                           u32 pktLen, enum ath9k_pkt_type type,
+                           u32 txPower, u32 keyIx,
+                           enum ath9k_key_type keyType, u32 flags);
+bool ath9k_hw_filltxdesc(struct ath_hal *ah, struct ath_desc *ds,
+                        u32 segLen, bool firstSeg,
+                        bool lastSeg,
+                        const struct ath_desc *ds0);
+u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah,
+                                       u32 *rxc_pcnt,
+                                       u32 *rxf_pcnt,
+                                       u32 *txf_pcnt);
+void ath9k_hw_dmaRegDump(struct ath_hal *ah);
+void ath9k_hw_beaconinit(struct ath_hal *ah,
+                        u32 next_beacon, u32 beacon_period);
+void ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah,
+                                   const struct ath9k_beacon_state *bs);
+bool ath9k_hw_setuprxdesc(struct ath_hal *ah, struct ath_desc *ds,
+                         u32 size, u32 flags);
+void ath9k_hw_putrxbuf(struct ath_hal *ah, u32 rxdp);
+void ath9k_hw_rxena(struct ath_hal *ah);
+void ath9k_hw_setopmode(struct ath_hal *ah);
+bool ath9k_hw_setmac(struct ath_hal *ah, const u8 *mac);
+void ath9k_hw_setmcastfilter(struct ath_hal *ah, u32 filter0,
+                            u32 filter1);
+u32 ath9k_hw_getrxfilter(struct ath_hal *ah);
+void ath9k_hw_startpcureceive(struct ath_hal *ah);
+void ath9k_hw_stoppcurecv(struct ath_hal *ah);
+bool ath9k_hw_stopdmarecv(struct ath_hal *ah);
+int ath9k_hw_rxprocdesc(struct ath_hal *ah,
+                       struct ath_desc *ds, u32 pa,
+                       struct ath_desc *nds, u64 tsf);
+u32 ath9k_hw_gettxbuf(struct ath_hal *ah, u32 q);
+int ath9k_hw_txprocdesc(struct ath_hal *ah,
+                       struct ath_desc *ds);
+void ath9k_hw_set11n_aggr_middle(struct ath_hal *ah, struct ath_desc *ds,
+                                u32 numDelims);
+void ath9k_hw_set11n_aggr_first(struct ath_hal *ah, struct ath_desc *ds,
+                               u32 aggrLen);
+void ath9k_hw_set11n_aggr_last(struct ath_hal *ah, struct ath_desc *ds);
+bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q);
+void ath9k_hw_gettxintrtxqs(struct ath_hal *ah, u32 *txqs);
+void ath9k_hw_clr11n_aggr(struct ath_hal *ah, struct ath_desc *ds);
+void ath9k_hw_set11n_virtualmorefrag(struct ath_hal *ah,
+                                    struct ath_desc *ds, u32 vmf);
+bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit);
+bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah);
+int ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type,
+                         const struct ath9k_txq_info *qInfo);
+u32 ath9k_hw_numtxpending(struct ath_hal *ah, u32 q);
+const char *ath9k_hw_probe(u16 vendorid, u16 devid);
+bool ath9k_hw_disable(struct ath_hal *ah);
+void ath9k_hw_rfdetach(struct ath_hal *ah);
+void ath9k_hw_get_channel_centers(struct ath_hal *ah,
+                                 struct ath9k_channel *chan,
+                                 struct chan_centers *centers);
+bool ath9k_get_channel_edges(struct ath_hal *ah,
+                            u16 flags, u16 *low,
+                            u16 *high);
+#endif
diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c
new file mode 100644 (file)
index 0000000..00993f8
--- /dev/null
@@ -0,0 +1,977 @@
+/*
+ * Copyright (c) 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.
+ */
+
+ /* Implementation of beacon processing. */
+
+#include <asm/unaligned.h>
+#include "core.h"
+
+/*
+ *  Configure parameters for the beacon queue
+ *
+ *  This function will modify certain transmit queue properties depending on
+ *  the operating mode of the station (AP or AdHoc).  Parameters are AIFS
+ *  settings and channel width min/max
+*/
+
+static int ath_beaconq_config(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath9k_txq_info qi;
+
+       ath9k_hw_gettxqueueprops(ah, sc->sc_bhalq, &qi);
+       if (sc->sc_opmode == ATH9K_M_HOSTAP) {
+               /* Always burst out beacon and CAB traffic. */
+               qi.tqi_aifs = 1;
+               qi.tqi_cwmin = 0;
+               qi.tqi_cwmax = 0;
+       } else {
+               /* Adhoc mode; important thing is to use 2x cwmin. */
+               qi.tqi_aifs = sc->sc_beacon_qi.tqi_aifs;
+               qi.tqi_cwmin = 2*sc->sc_beacon_qi.tqi_cwmin;
+               qi.tqi_cwmax = sc->sc_beacon_qi.tqi_cwmax;
+       }
+
+       if (!ath9k_hw_settxqueueprops(ah, sc->sc_bhalq, &qi)) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to update h/w beacon queue parameters\n",
+                       __func__);
+               return 0;
+       } else {
+               ath9k_hw_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */
+               return 1;
+       }
+}
+
+/*
+ *  Setup the beacon frame for transmit.
+ *
+ *  Associates the beacon frame buffer with a transmit descriptor.  Will set
+ *  up all required antenna switch parameters, rate codes, and channel flags.
+ *  Beacons are always sent out at the lowest rate, and are not retried.
+*/
+
+static void ath_beacon_setup(struct ath_softc *sc,
+       struct ath_vap *avp, struct ath_buf *bf)
+{
+       struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_desc *ds;
+       int flags, antenna;
+       const struct ath9k_rate_table *rt;
+       u8 rix, rate;
+       int ctsrate = 0;
+       int ctsduration = 0;
+       struct ath9k_11n_rate_series  series[4];
+
+       DPRINTF(sc, ATH_DBG_BEACON, "%s: m %p len %u\n",
+               __func__, skb, skb->len);
+
+       /* setup descriptors */
+       ds = bf->bf_desc;
+
+       flags = ATH9K_TXDESC_NOACK;
+
+       if (sc->sc_opmode == ATH9K_M_IBSS && ah->ah_caps.halVEOLSupport) {
+               ds->ds_link = bf->bf_daddr; /* self-linked */
+               flags |= ATH9K_TXDESC_VEOL;
+               /* Let hardware handle antenna switching. */
+               antenna = 0;
+       } else {
+               ds->ds_link = 0;
+               /*
+                * Switch antenna every beacon.
+                * Should only switch every beacon period, not for every
+                * SWBA's
+                * XXX assumes two antenna
+                */
+               antenna = ((sc->ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1);
+       }
+
+       ds->ds_data = bf->bf_buf_addr;
+
+       /*
+        * Calculate rate code.
+        * XXX everything at min xmit rate
+        */
+       rix = sc->sc_minrateix;
+       rt = sc->sc_currates;
+       rate = rt->info[rix].rateCode;
+       if (sc->sc_flags & ATH_PREAMBLE_SHORT)
+               rate |= rt->info[rix].shortPreamble;
+
+       ath9k_hw_set11n_txdesc(ah, ds
+                             , skb->len + FCS_LEN /* frame length */
+                             , ATH9K_PKT_TYPE_BEACON /* Atheros packet type */
+                             , avp->av_btxctl.txpower /* txpower XXX */
+                             , ATH9K_TXKEYIX_INVALID /* no encryption */
+                             , ATH9K_KEY_TYPE_CLEAR /* no encryption */
+                             , flags /* no ack, veol for beacons */
+               );
+
+       /* NB: beacon's BufLen must be a multiple of 4 bytes */
+       ath9k_hw_filltxdesc(ah, ds
+                          , roundup(skb->len, 4) /* buffer length */
+                          , true /* first segment */
+                          , true /* last segment */
+                          , ds /* first descriptor */
+               );
+
+       memzero(series, sizeof(struct ath9k_11n_rate_series) * 4);
+       series[0].Tries = 1;
+       series[0].Rate = rate;
+       series[0].ChSel = sc->sc_tx_chainmask;
+       series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0;
+       ath9k_hw_set11n_ratescenario(ah, ds, ds, 0,
+               ctsrate, ctsduration, series, 4, 0);
+}
+
+/* Move everything from the vap's mcast queue to the hardware cab queue.
+ * Caller must hold mcasq lock and cabq lock
+ * XXX MORE_DATA bit?
+ */
+static void empty_mcastq_into_cabq(struct ath_hal *ah,
+       struct ath_txq *mcastq, struct ath_txq *cabq)
+{
+       struct ath_buf *bfmcast;
+
+       BUG_ON(list_empty(&mcastq->axq_q));
+
+       bfmcast = list_first_entry(&mcastq->axq_q, struct ath_buf, list);
+
+       /* link the descriptors */
+       if (!cabq->axq_link)
+               ath9k_hw_puttxbuf(ah, cabq->axq_qnum, bfmcast->bf_daddr);
+       else
+               *cabq->axq_link = bfmcast->bf_daddr;
+
+       /* append the private vap mcast list to  the cabq */
+
+       cabq->axq_depth += mcastq->axq_depth;
+       cabq->axq_totalqueued += mcastq->axq_totalqueued;
+       cabq->axq_linkbuf = mcastq->axq_linkbuf;
+       cabq->axq_link = mcastq->axq_link;
+       list_splice_tail_init(&mcastq->axq_q, &cabq->axq_q);
+       mcastq->axq_depth = 0;
+       mcastq->axq_totalqueued = 0;
+       mcastq->axq_linkbuf = NULL;
+       mcastq->axq_link = NULL;
+}
+
+/* This is only run at DTIM. We move everything from the vap's mcast queue
+ * to the hardware cab queue. Caller must hold the mcastq lock. */
+static void trigger_mcastq(struct ath_hal *ah,
+       struct ath_txq *mcastq, struct ath_txq *cabq)
+{
+       spin_lock_bh(&cabq->axq_lock);
+
+       if (!list_empty(&mcastq->axq_q))
+               empty_mcastq_into_cabq(ah, mcastq, cabq);
+
+       /* cabq is gated by beacon so it is safe to start here */
+       if (!list_empty(&cabq->axq_q))
+               ath9k_hw_txstart(ah, cabq->axq_qnum);
+
+       spin_unlock_bh(&cabq->axq_lock);
+}
+
+/*
+ *  Generate beacon frame and queue cab data for a vap.
+ *
+ *  Updates the contents of the beacon frame.  It is assumed that the buffer for
+ *  the beacon frame has been allocated in the ATH object, and simply needs to
+ *  be filled for this cycle.  Also, any CAB (crap after beacon?) traffic will
+ *  be added to the beacon frame at this point.
+*/
+static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_buf *bf;
+       struct ath_vap *avp;
+       struct sk_buff *skb;
+       int cabq_depth;
+       int mcastq_depth;
+       int is_beacon_dtim = 0;
+       unsigned int curlen;
+       struct ath_txq *cabq;
+       struct ath_txq *mcastq;
+       avp = sc->sc_vaps[if_id];
+
+       mcastq = &avp->av_mcastq;
+       cabq = sc->sc_cabq;
+
+       ASSERT(avp);
+
+       if (avp->av_bcbuf == NULL) {
+               DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n",
+                       __func__, avp, avp->av_bcbuf);
+               return NULL;
+       }
+       bf = avp->av_bcbuf;
+       skb = (struct sk_buff *) bf->bf_mpdu;
+
+       /*
+        * Update dynamic beacon contents.  If this returns
+        * non-zero then we need to remap the memory because
+        * the beacon frame changed size (probably because
+        * of the TIM bitmap).
+        */
+       curlen = skb->len;
+
+       /* XXX: spin_lock_bh should not be used here, but sparse bitches
+        * otherwise. We should fix sparse :) */
+       spin_lock_bh(&mcastq->axq_lock);
+       mcastq_depth = avp->av_mcastq.axq_depth;
+
+       if (ath_update_beacon(sc, if_id, &avp->av_boff, skb, mcastq_depth) ==
+           1) {
+               ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE,
+                                    get_dma_mem_context(bf, bf_dmacontext));
+               bf->bf_buf_addr = ath_skb_map_single(sc, skb, PCI_DMA_TODEVICE,
+                       get_dma_mem_context(bf, bf_dmacontext));
+       } else {
+               pci_dma_sync_single_for_cpu(sc->pdev,
+                                           bf->bf_buf_addr,
+                                           skb_tailroom(skb),
+                                           PCI_DMA_TODEVICE);
+       }
+
+       /*
+        * if the CABQ traffic from previous DTIM is pending and the current
+        *  beacon is also a DTIM.
+        *  1) if there is only one vap let the cab traffic continue.
+        *  2) if there are more than one vap and we are using staggered
+        *     beacons, then drain the cabq by dropping all the frames in
+        *     the cabq so that the current vaps cab traffic can be scheduled.
+        */
+       spin_lock_bh(&cabq->axq_lock);
+       cabq_depth = cabq->axq_depth;
+       spin_unlock_bh(&cabq->axq_lock);
+
+       is_beacon_dtim = avp->av_boff.bo_tim[4] & 1;
+
+       if (mcastq_depth && is_beacon_dtim && cabq_depth) {
+               /*
+                * Unlock the cabq lock as ath_tx_draintxq acquires
+                * the lock again which is a common function and that
+                * acquires txq lock inside.
+                */
+               if (sc->sc_nvaps > 1) {
+                       ath_tx_draintxq(sc, cabq, false);
+                       DPRINTF(sc, ATH_DBG_BEACON,
+                               "%s: flush previous cabq traffic\n", __func__);
+               }
+       }
+
+       /* Construct tx descriptor. */
+       ath_beacon_setup(sc, avp, bf);
+
+       /*
+        * Enable the CAB queue before the beacon queue to
+        * insure cab frames are triggered by this beacon.
+        */
+       if (is_beacon_dtim)
+               trigger_mcastq(ah, mcastq, cabq);
+
+       spin_unlock_bh(&mcastq->axq_lock);
+       return bf;
+}
+
+/*
+ * Startup beacon transmission for adhoc mode when they are sent entirely
+ * by the hardware using the self-linked descriptor + veol trick.
+*/
+
+static void ath_beacon_start_adhoc(struct ath_softc *sc, int if_id)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_buf *bf;
+       struct ath_vap *avp;
+       struct sk_buff *skb;
+
+       avp = sc->sc_vaps[if_id];
+       ASSERT(avp);
+
+       if (avp->av_bcbuf == NULL) {
+               DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n",
+                       __func__, avp, avp != NULL ? avp->av_bcbuf : NULL);
+               return;
+       }
+       bf = avp->av_bcbuf;
+       skb = (struct sk_buff *) bf->bf_mpdu;
+
+       /* Construct tx descriptor. */
+       ath_beacon_setup(sc, avp, bf);
+
+       /* NB: caller is known to have already stopped tx dma */
+       ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
+       ath9k_hw_txstart(ah, sc->sc_bhalq);
+       DPRINTF(sc, ATH_DBG_BEACON, "%s: TXDP%u = %llx (%p)\n", __func__,
+               sc->sc_bhalq, ito64(bf->bf_daddr), bf->bf_desc);
+}
+
+/*
+ *  Setup a h/w transmit queue for beacons.
+ *
+ *  This function allocates an information structure (struct ath9k_txq_info)
+ *  on the stack, sets some specific parameters (zero out channel width
+ *  min/max, and enable aifs). The info structure does not need to be
+ *  persistant.
+*/
+
+int ath_beaconq_setup(struct ath_hal *ah)
+{
+       struct ath9k_txq_info qi;
+
+       memzero(&qi, sizeof(qi));
+       qi.tqi_aifs = 1;
+       qi.tqi_cwmin = 0;
+       qi.tqi_cwmax = 0;
+       /* NB: don't enable any interrupts */
+       return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi);
+}
+
+
+/*
+ *  Allocate and setup an initial beacon frame.
+ *
+ *  Allocate a beacon state variable for a specific VAP instance created on
+ *  the ATH interface.  This routine also calculates the beacon "slot" for
+ *  staggared beacons in the mBSSID case.
+*/
+
+int ath_beacon_alloc(struct ath_softc *sc, int if_id)
+{
+       struct ath_vap *avp;
+       struct ieee80211_hdr *wh;
+       struct ath_buf *bf;
+       struct sk_buff *skb;
+
+       avp = sc->sc_vaps[if_id];
+       ASSERT(avp);
+
+       /* Allocate a beacon descriptor if we haven't done so. */
+       if (!avp->av_bcbuf) {
+               /*
+                * Allocate beacon state for hostap/ibss.  We know
+                * a buffer is available.
+                */
+
+               avp->av_bcbuf = list_first_entry(&sc->sc_bbuf,
+                               struct ath_buf, list);
+               list_del(&avp->av_bcbuf->list);
+
+               if (sc->sc_opmode == ATH9K_M_HOSTAP ||
+                       !sc->sc_ah->ah_caps.halVEOLSupport) {
+                       int slot;
+                       /*
+                        * Assign the vap to a beacon xmit slot. As
+                        * above, this cannot fail to find one.
+                        */
+                       avp->av_bslot = 0;
+                       for (slot = 0; slot < ATH_BCBUF; slot++)
+                               if (sc->sc_bslot[slot] == ATH_IF_ID_ANY) {
+                                       /*
+                                        * XXX hack, space out slots to better
+                                        * deal with misses
+                                        */
+                                       if (slot+1 < ATH_BCBUF &&
+                                           sc->sc_bslot[slot+1] ==
+                                               ATH_IF_ID_ANY) {
+                                               avp->av_bslot = slot+1;
+                                               break;
+                                       }
+                                       avp->av_bslot = slot;
+                                       /* NB: keep looking for a double slot */
+                               }
+                       BUG_ON(sc->sc_bslot[avp->av_bslot] != ATH_IF_ID_ANY);
+                       sc->sc_bslot[avp->av_bslot] = if_id;
+                       sc->sc_nbcnvaps++;
+               }
+       }
+
+       /* release the previous beacon frame , if it already exists. */
+       bf = avp->av_bcbuf;
+       if (bf->bf_mpdu != NULL) {
+               skb = (struct sk_buff *)bf->bf_mpdu;
+               ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE,
+                                    get_dma_mem_context(bf, bf_dmacontext));
+               dev_kfree_skb_any(skb);
+               bf->bf_mpdu = NULL;
+       }
+
+       /*
+        * NB: the beacon data buffer must be 32-bit aligned;
+        * we assume the wbuf routines will return us something
+        * with this alignment (perhaps should assert).
+        * FIXME: Fill avp->av_boff.bo_tim,avp->av_btxctl.txpower and
+        * avp->av_btxctl.shortPreamble
+        */
+       skb = ieee80211_beacon_get(sc->hw, avp->av_if_data);
+       if (skb == NULL) {
+               DPRINTF(sc, ATH_DBG_BEACON, "%s: cannot get skb\n",
+                       __func__);
+               return -ENOMEM;
+       }
+
+       /*
+        * Calculate a TSF adjustment factor required for
+        * staggered beacons.  Note that we assume the format
+        * of the beacon frame leaves the tstamp field immediately
+        * following the header.
+        */
+       if (avp->av_bslot > 0) {
+               u64 tsfadjust;
+               __le64 val;
+               int intval;
+
+               /* FIXME: Use default value for now: Sujith */
+
+               intval = ATH_DEFAULT_BINTVAL;
+
+               /*
+                * The beacon interval is in TU's; the TSF in usecs.
+                * We figure out how many TU's to add to align the
+                * timestamp then convert to TSF units and handle
+                * byte swapping before writing it in the frame.
+                * The hardware will then add this each time a beacon
+                * frame is sent.  Note that we align vap's 1..N
+                * and leave vap 0 untouched.  This means vap 0
+                * has a timestamp in one beacon interval while the
+                * others get a timestamp aligned to the next interval.
+                */
+               tsfadjust = (intval * (ATH_BCBUF - avp->av_bslot)) / ATH_BCBUF;
+               val = cpu_to_le64(tsfadjust << 10);     /* TU->TSF */
+
+               DPRINTF(sc, ATH_DBG_BEACON,
+                       "%s: %s beacons, bslot %d intval %u tsfadjust %llu\n",
+                       __func__, "stagger",
+                       avp->av_bslot, intval, (unsigned long long)tsfadjust);
+
+               wh = (struct ieee80211_hdr *)skb->data;
+               memcpy(&wh[1], &val, sizeof(val));
+       }
+
+       bf->bf_buf_addr = ath_skb_map_single(sc, skb, PCI_DMA_TODEVICE,
+               get_dma_mem_context(bf, bf_dmacontext));
+       bf->bf_mpdu = skb;
+
+       return 0;
+}
+
+/*
+ *  Reclaim beacon resources and return buffer to the pool.
+ *
+ *  Checks the VAP to put the beacon frame buffer back to the ATH object
+ *  queue, and de-allocates any wbuf frames that were sent as CAB traffic.
+*/
+
+void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp)
+{
+       if (avp->av_bcbuf != NULL) {
+               struct ath_buf *bf;
+
+               if (avp->av_bslot != -1) {
+                       sc->sc_bslot[avp->av_bslot] = ATH_IF_ID_ANY;
+                       sc->sc_nbcnvaps--;
+               }
+
+               bf = avp->av_bcbuf;
+               if (bf->bf_mpdu != NULL) {
+                       struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu;
+                       ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE,
+                               get_dma_mem_context(bf, bf_dmacontext));
+                       dev_kfree_skb_any(skb);
+                       bf->bf_mpdu = NULL;
+               }
+               list_add_tail(&bf->list, &sc->sc_bbuf);
+
+               avp->av_bcbuf = NULL;
+       }
+}
+
+/*
+ *  Reclaim beacon resources and return buffer to the pool.
+ *
+ *  This function will free any wbuf frames that are still attached to the
+ *  beacon buffers in the ATH object.  Note that this does not de-allocate
+ *  any wbuf objects that are in the transmit queue and have not yet returned
+ *  to the ATH object.
+*/
+
+void ath_beacon_free(struct ath_softc *sc)
+{
+       struct ath_buf *bf;
+
+       list_for_each_entry(bf, &sc->sc_bbuf, list) {
+               if (bf->bf_mpdu != NULL) {
+                       struct sk_buff *skb = (struct sk_buff *) bf->bf_mpdu;
+                       ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE,
+                               get_dma_mem_context(bf, bf_dmacontext));
+                       dev_kfree_skb_any(skb);
+                       bf->bf_mpdu = NULL;
+               }
+       }
+}
+
+/*
+ * Tasklet for Sending Beacons
+ *
+ * Transmit one or more beacon frames at SWBA.  Dynamic updates to the frame
+ * contents are done as needed and the slot time is also adjusted based on
+ * current state.
+ *
+ * This tasklet is not scheduled, it's called in ISR context.
+*/
+
+void ath9k_beacon_tasklet(unsigned long data)
+{
+#define TSF_TO_TU(_h,_l)                                       \
+       ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
+
+       struct ath_softc *sc = (struct ath_softc *)data;
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_buf *bf = NULL;
+       int slot, if_id;
+       u32 bfaddr;
+       u32 rx_clear = 0, rx_frame = 0, tx_frame = 0;
+       u32 show_cycles = 0;
+       u32 bc = 0; /* beacon count */
+       u64 tsf;
+       u32 tsftu;
+       u16 intval;
+
+       if (sc->sc_noreset) {
+               show_cycles = ath9k_hw_GetMibCycleCountsPct(ah,
+                                                           &rx_clear,
+                                                           &rx_frame,
+                                                           &tx_frame);
+       }
+
+       /*
+        * Check if the previous beacon has gone out.  If
+        * not don't try to post another, skip this period
+        * and wait for the next.  Missed beacons indicate
+        * a problem and should not occur.  If we miss too
+        * many consecutive beacons reset the device.
+        */
+       if (ath9k_hw_numtxpending(ah, sc->sc_bhalq) != 0) {
+               sc->sc_bmisscount++;
+               /* XXX: doth needs the chanchange IE countdown decremented.
+                *      We should consider adding a mac80211 call to indicate
+                *      a beacon miss so appropriate action could be taken
+                *      (in that layer).
+                */
+               if (sc->sc_bmisscount < BSTUCK_THRESH) {
+                       if (sc->sc_noreset) {
+                               DPRINTF(sc, ATH_DBG_BEACON,
+                                       "%s: missed %u consecutive beacons\n",
+                                       __func__, sc->sc_bmisscount);
+                               if (show_cycles) {
+                                       /*
+                                        * Display cycle counter stats
+                                        * from HW to aide in debug of
+                                        * stickiness.
+                                        */
+                                       DPRINTF(sc,
+                                               ATH_DBG_BEACON,
+                                               "%s: busy times: rx_clear=%d, "
+                                               "rx_frame=%d, tx_frame=%d\n",
+                                               __func__, rx_clear, rx_frame,
+                                               tx_frame);
+                               } else {
+                                       DPRINTF(sc,
+                                               ATH_DBG_BEACON,
+                                               "%s: unable to obtain "
+                                               "busy times\n", __func__);
+                               }
+                       } else {
+                               DPRINTF(sc, ATH_DBG_BEACON,
+                                       "%s: missed %u consecutive beacons\n",
+                                       __func__, sc->sc_bmisscount);
+                       }
+               } else if (sc->sc_bmisscount >= BSTUCK_THRESH) {
+                       if (sc->sc_noreset) {
+                               if (sc->sc_bmisscount == BSTUCK_THRESH) {
+                                       DPRINTF(sc,
+                                               ATH_DBG_BEACON,
+                                               "%s: beacon is officially "
+                                               "stuck\n", __func__);
+                                       ath9k_hw_dmaRegDump(ah);
+                               }
+                       } else {
+                               DPRINTF(sc, ATH_DBG_BEACON,
+                                       "%s: beacon is officially stuck\n",
+                                       __func__);
+                               ath_bstuck_process(sc);
+                       }
+               }
+
+               return;
+       }
+       if (sc->sc_bmisscount != 0) {
+               if (sc->sc_noreset) {
+                       DPRINTF(sc,
+                               ATH_DBG_BEACON,
+                               "%s: resume beacon xmit after %u misses\n",
+                               __func__, sc->sc_bmisscount);
+               } else {
+                       DPRINTF(sc, ATH_DBG_BEACON,
+                               "%s: resume beacon xmit after %u misses\n",
+                               __func__, sc->sc_bmisscount);
+               }
+               sc->sc_bmisscount = 0;
+       }
+
+       /*
+        * Generate beacon frames. we are sending frames
+        * staggered so calculate the slot for this frame based
+        * on the tsf to safeguard against missing an swba.
+        */
+
+       /* FIXME: Use default value for now - Sujith */
+       intval = ATH_DEFAULT_BINTVAL;
+
+       tsf = ath9k_hw_gettsf64(ah);
+       tsftu = TSF_TO_TU(tsf>>32, tsf);
+       slot = ((tsftu % intval) * ATH_BCBUF) / intval;
+       if_id = sc->sc_bslot[(slot + 1) % ATH_BCBUF];
+       DPRINTF(sc, ATH_DBG_BEACON,
+                       "%s: slot %d [tsf %llu tsftu %u intval %u] if_id %d\n",
+                       __func__, slot, (unsigned long long) tsf, tsftu,
+                       intval, if_id);
+       bfaddr = 0;
+       if (if_id != ATH_IF_ID_ANY) {
+               bf = ath_beacon_generate(sc, if_id);
+               if (bf != NULL) {
+                       bfaddr = bf->bf_daddr;
+                       bc = 1;
+               }
+       }
+       /*
+        * Handle slot time change when a non-ERP station joins/leaves
+        * an 11g network.  The 802.11 layer notifies us via callback,
+        * we mark updateslot, then wait one beacon before effecting
+        * the change.  This gives associated stations at least one
+        * beacon interval to note the state change.
+        *
+        * NB: The slot time change state machine is clocked according
+        *     to whether we are bursting or staggering beacons.  We
+        *     recognize the request to update and record the current
+        *     slot then don't transition until that slot is reached
+        *     again.  If we miss a beacon for that slot then we'll be
+        *     slow to transition but we'll be sure at least one beacon
+        *     interval has passed.  When bursting slot is always left
+        *     set to ATH_BCBUF so this check is a noop.
+        */
+       /* XXX locking */
+       if (sc->sc_updateslot == UPDATE) {
+               sc->sc_updateslot = COMMIT; /* commit next beacon */
+               sc->sc_slotupdate = slot;
+       } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot)
+               ath_setslottime(sc);        /* commit change to hardware */
+
+       if (bfaddr != 0) {
+               /*
+                * Stop any current dma and put the new frame(s) on the queue.
+                * This should never fail since we check above that no frames
+                * are still pending on the queue.
+                */
+               if (!ath9k_hw_stoptxdma(ah, sc->sc_bhalq)) {
+                       DPRINTF(sc, ATH_DBG_FATAL,
+                               "%s: beacon queue %u did not stop?\n",
+                               __func__, sc->sc_bhalq);
+                       /* NB: the HAL still stops DMA, so proceed */
+               }
+
+               /* NB: cabq traffic should already be queued and primed */
+               ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bfaddr);
+               ath9k_hw_txstart(ah, sc->sc_bhalq);
+
+               sc->ast_be_xmit += bc;     /* XXX per-vap? */
+       }
+#undef TSF_TO_TU
+}
+
+/*
+ *  Tasklet for Beacon Stuck processing
+ *
+ *  Processing for Beacon Stuck.
+ *  Basically calls the ath_internal_reset function to reset the chip.
+*/
+
+void ath_bstuck_process(struct ath_softc *sc)
+{
+       DPRINTF(sc, ATH_DBG_BEACON,
+               "%s: stuck beacon; resetting (bmiss count %u)\n",
+               __func__, sc->sc_bmisscount);
+       ath_internal_reset(sc);
+}
+
+/*
+ * Configure the beacon and sleep timers.
+ *
+ * When operating as an AP this resets the TSF and sets
+ * up the hardware to notify us when we need to issue beacons.
+ *
+ * When operating in station mode this sets up the beacon
+ * timers according to the timestamp of the last received
+ * beacon and the current TSF, configures PCF and DTIM
+ * handling, programs the sleep registers so the hardware
+ * will wakeup in time to receive beacons, and configures
+ * the beacon miss handling so we'll receive a BMISS
+ * interrupt when we stop seeing beacons from the AP
+ * we've associated with.
+ */
+
+void ath_beacon_config(struct ath_softc *sc, int if_id)
+{
+#define TSF_TO_TU(_h,_l)                                       \
+       ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
+       struct ath_hal *ah = sc->sc_ah;
+       u32 nexttbtt, intval;
+       struct ath_beacon_config conf;
+       enum ath9k_opmode av_opmode;
+
+       if (if_id != ATH_IF_ID_ANY)
+               av_opmode = sc->sc_vaps[if_id]->av_opmode;
+       else
+               av_opmode = sc->sc_opmode;
+
+       memzero(&conf, sizeof(struct ath_beacon_config));
+
+       /* FIXME: Use default values for now - Sujith */
+       /* Query beacon configuration first */
+       /*
+        * Protocol stack doesn't support dynamic beacon configuration,
+        * use default configurations.
+        */
+       conf.beacon_interval = ATH_DEFAULT_BINTVAL;
+       conf.listen_interval = 1;
+       conf.dtim_period = conf.beacon_interval;
+       conf.dtim_count = 1;
+       conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval;
+
+       /* extract tstamp from last beacon and convert to TU */
+       nexttbtt = TSF_TO_TU(get_unaligned_le32(conf.u.last_tstamp + 4),
+                            get_unaligned_le32(conf.u.last_tstamp));
+       /* XXX conditionalize multi-bss support? */
+       if (sc->sc_opmode == ATH9K_M_HOSTAP) {
+               /*
+                * For multi-bss ap support beacons are either staggered
+                * evenly over N slots or burst together.  For the former
+                * arrange for the SWBA to be delivered for each slot.
+                * Slots that are not occupied will generate nothing.
+                */
+               /* NB: the beacon interval is kept internally in TU's */
+               intval = conf.beacon_interval & ATH9K_BEACON_PERIOD;
+               intval /= ATH_BCBUF;    /* for staggered beacons */
+       } else {
+               intval = conf.beacon_interval & ATH9K_BEACON_PERIOD;
+       }
+
+       if (nexttbtt == 0)      /* e.g. for ap mode */
+               nexttbtt = intval;
+       else if (intval)        /* NB: can be 0 for monitor mode */
+               nexttbtt = roundup(nexttbtt, intval);
+       DPRINTF(sc, ATH_DBG_BEACON, "%s: nexttbtt %u intval %u (%u)\n",
+               __func__, nexttbtt, intval, conf.beacon_interval);
+       /* Check for ATH9K_M_HOSTAP and sc_nostabeacons for WDS client */
+       if (sc->sc_opmode == ATH9K_M_STA) {
+               struct ath9k_beacon_state bs;
+               u64 tsf;
+               u32 tsftu;
+               int dtimperiod, dtimcount, sleepduration;
+               int cfpperiod, cfpcount;
+
+               /*
+                * Setup dtim and cfp parameters according to
+                * last beacon we received (which may be none).
+                */
+               dtimperiod = conf.dtim_period;
+               if (dtimperiod <= 0)        /* NB: 0 if not known */
+                       dtimperiod = 1;
+               dtimcount = conf.dtim_count;
+               if (dtimcount >= dtimperiod)    /* NB: sanity check */
+                       dtimcount = 0;      /* XXX? */
+               cfpperiod = 1;          /* NB: no PCF support yet */
+               cfpcount = 0;
+
+               sleepduration = conf.listen_interval * intval;
+               if (sleepduration <= 0)
+                       sleepduration = intval;
+
+#define FUDGE   2
+               /*
+                * Pull nexttbtt forward to reflect the current
+                * TSF and calculate dtim+cfp state for the result.
+                */
+               tsf = ath9k_hw_gettsf64(ah);
+               tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
+               do {
+                       nexttbtt += intval;
+                       if (--dtimcount < 0) {
+                               dtimcount = dtimperiod - 1;
+                               if (--cfpcount < 0)
+                                       cfpcount = cfpperiod - 1;
+                       }
+               } while (nexttbtt < tsftu);
+#undef FUDGE
+               memzero(&bs, sizeof(bs));
+               bs.bs_intval = intval;
+               bs.bs_nexttbtt = nexttbtt;
+               bs.bs_dtimperiod = dtimperiod*intval;
+               bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
+               bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
+               bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
+               bs.bs_cfpmaxduration = 0;
+               /*
+                * Calculate the number of consecutive beacons to miss
+                * before taking a BMISS interrupt.  The configuration
+                * is specified in TU so we only need calculate based
+                * on the beacon interval.  Note that we clamp the
+                * result to at most 15 beacons.
+                */
+               if (sleepduration > intval) {
+                       bs.bs_bmissthreshold =
+                               conf.listen_interval *
+                                       ATH_DEFAULT_BMISS_LIMIT / 2;
+               } else {
+                       bs.bs_bmissthreshold =
+                               DIV_ROUND_UP(conf.bmiss_timeout, intval);
+                       if (bs.bs_bmissthreshold > 15)
+                               bs.bs_bmissthreshold = 15;
+                       else if (bs.bs_bmissthreshold <= 0)
+                               bs.bs_bmissthreshold = 1;
+               }
+
+               /*
+                * Calculate sleep duration.  The configuration is
+                * given in ms.  We insure a multiple of the beacon
+                * period is used.  Also, if the sleep duration is
+                * greater than the DTIM period then it makes senses
+                * to make it a multiple of that.
+                *
+                * XXX fixed at 100ms
+                */
+
+               bs.bs_sleepduration =
+                       roundup(IEEE80211_MS_TO_TU(100), sleepduration);
+               if (bs.bs_sleepduration > bs.bs_dtimperiod)
+                       bs.bs_sleepduration = bs.bs_dtimperiod;
+
+               DPRINTF(sc, ATH_DBG_BEACON,
+                       "%s: tsf %llu "
+                       "tsf:tu %u "
+                       "intval %u "
+                       "nexttbtt %u "
+                       "dtim %u "
+                       "nextdtim %u "
+                       "bmiss %u "
+                       "sleep %u "
+                       "cfp:period %u "
+                       "maxdur %u "
+                       "next %u "
+                       "timoffset %u\n"
+                       , __func__
+                       , (unsigned long long)tsf, tsftu
+                       , bs.bs_intval
+                       , bs.bs_nexttbtt
+                       , bs.bs_dtimperiod
+                       , bs.bs_nextdtim
+                       , bs.bs_bmissthreshold
+                       , bs.bs_sleepduration
+                       , bs.bs_cfpperiod
+                       , bs.bs_cfpmaxduration
+                       , bs.bs_cfpnext
+                       , bs.bs_timoffset
+                       );
+
+               ath9k_hw_set_interrupts(ah, 0);
+               ath9k_hw_set_sta_beacon_timers(ah, &bs);
+               sc->sc_imask |= ATH9K_INT_BMISS;
+               ath9k_hw_set_interrupts(ah, sc->sc_imask);
+       } else {
+               u64 tsf;
+               u32 tsftu;
+               ath9k_hw_set_interrupts(ah, 0);
+               if (nexttbtt == intval)
+                       intval |= ATH9K_BEACON_RESET_TSF;
+               if (sc->sc_opmode == ATH9K_M_IBSS) {
+                       /*
+                        * Pull nexttbtt forward to reflect the current
+                        * TSF .
+                        */
+#define FUDGE   2
+                       if (!(intval & ATH9K_BEACON_RESET_TSF)) {
+                               tsf = ath9k_hw_gettsf64(ah);
+                               tsftu = TSF_TO_TU((u32)(tsf>>32),
+                                       (u32)tsf) + FUDGE;
+                               do {
+                                       nexttbtt += intval;
+                               } while (nexttbtt < tsftu);
+                       }
+#undef FUDGE
+                       DPRINTF(sc, ATH_DBG_BEACON,
+                               "%s: IBSS nexttbtt %u intval %u (%u)\n",
+                               __func__, nexttbtt,
+                               intval & ~ATH9K_BEACON_RESET_TSF,
+                               conf.beacon_interval);
+
+                       /*
+                        * In IBSS mode enable the beacon timers but only
+                        * enable SWBA interrupts if we need to manually
+                        * prepare beacon frames.  Otherwise we use a
+                        * self-linked tx descriptor and let the hardware
+                        * deal with things.
+                        */
+                       intval |= ATH9K_BEACON_ENA;
+                       if (!ah->ah_caps.halVEOLSupport)
+                               sc->sc_imask |= ATH9K_INT_SWBA;
+                       ath_beaconq_config(sc);
+               } else if (sc->sc_opmode == ATH9K_M_HOSTAP) {
+                       /*
+                        * In AP mode we enable the beacon timers and
+                        * SWBA interrupts to prepare beacon frames.
+                        */
+                       intval |= ATH9K_BEACON_ENA;
+                       sc->sc_imask |= ATH9K_INT_SWBA;   /* beacon prepare */
+                       ath_beaconq_config(sc);
+               }
+               ath9k_hw_beaconinit(ah, nexttbtt, intval);
+               sc->sc_bmisscount = 0;
+               ath9k_hw_set_interrupts(ah, sc->sc_imask);
+               /*
+                * When using a self-linked beacon descriptor in
+                * ibss mode load it once here.
+                */
+               if (sc->sc_opmode == ATH9K_M_IBSS && ah->ah_caps.halVEOLSupport)
+                       ath_beacon_start_adhoc(sc, 0);
+       }
+#undef TSF_TO_TU
+}
+
+/* Function to collect beacon rssi data and resync beacon if necessary */
+
+void ath_beacon_sync(struct ath_softc *sc, int if_id)
+{
+       /*
+        * Resync beacon timers using the tsf of the
+        * beacon frame we just received.
+        */
+       ath_beacon_config(sc, if_id);
+       sc->sc_beacons = 1;
+}
diff --git a/drivers/net/wireless/ath9k/core.c b/drivers/net/wireless/ath9k/core.c
new file mode 100644 (file)
index 0000000..e8b3f1f
--- /dev/null
@@ -0,0 +1,1961 @@
+/*
+ * Copyright (c) 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.
+ */
+
+ /* Implementation of the main "ATH" layer. */
+
+#include "core.h"
+#include "regd.h"
+
+static int ath_outdoor;                /* enable outdoor use */
+
+static const u8 ath_bcast_mac[ETH_ALEN] =
+    { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static u32 ath_chainmask_sel_up_rssi_thres =
+       ATH_CHAINMASK_SEL_UP_RSSI_THRES;
+static u32 ath_chainmask_sel_down_rssi_thres =
+       ATH_CHAINMASK_SEL_DOWN_RSSI_THRES;
+static u32 ath_chainmask_sel_period =
+       ATH_CHAINMASK_SEL_TIMEOUT;
+
+/* return bus cachesize in 4B word units */
+
+static void bus_read_cachesize(struct ath_softc *sc, int *csz)
+{
+       u8 u8tmp;
+
+       pci_read_config_byte(sc->pdev, PCI_CACHE_LINE_SIZE, (u8 *)&u8tmp);
+       *csz = (int)u8tmp;
+
+       /*
+        * This check was put in to avoid "unplesant" consequences if
+        * the bootrom has not fully initialized all PCI devices.
+        * Sometimes the cache line size register is not set
+        */
+
+       if (*csz == 0)
+               *csz = DEFAULT_CACHELINE >> 2;   /* Use the default size */
+}
+
+/*
+ *  Set current operating mode
+ *
+ *  This function initializes and fills the rate table in the ATH object based
+ *  on the operating mode.  The blink rates are also set up here, although
+ *  they have been superceeded by the ath_led module.
+*/
+
+static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode)
+{
+       const struct ath9k_rate_table *rt;
+       int i;
+
+       memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap));
+       rt = sc->sc_rates[mode];
+       BUG_ON(!rt);
+
+       for (i = 0; i < rt->rateCount; i++)
+               sc->sc_rixmap[rt->info[i].rateCode] = (u8) i;
+
+       memzero(sc->sc_hwmap, sizeof(sc->sc_hwmap));
+       for (i = 0; i < 256; i++) {
+               u8 ix = rt->rateCodeToIndex[i];
+
+               if (ix == 0xff)
+                       continue;
+
+               sc->sc_hwmap[i].ieeerate =
+                   rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
+               sc->sc_hwmap[i].rateKbps = rt->info[ix].rateKbps;
+
+               if (rt->info[ix].shortPreamble ||
+                   rt->info[ix].phy == PHY_OFDM) {
+                       /* XXX: Handle this */
+               }
+
+               /* NB: this uses the last entry if the rate isn't found */
+               /* XXX beware of overlow */
+       }
+       sc->sc_currates = rt;
+       sc->sc_curmode = mode;
+       /*
+        * All protection frames are transmited at 2Mb/s for
+        * 11g, otherwise at 1Mb/s.
+        * XXX select protection rate index from rate table.
+        */
+       sc->sc_protrix = (mode == WIRELESS_MODE_11g ? 1 : 0);
+       /* rate index used to send mgt frames */
+       sc->sc_minrateix = 0;
+}
+
+/*
+ *  Select Rate Table
+ *
+ *  Based on the wireless mode passed in, the rate table in the ATH object
+ *  is set to the mode specific rate table.  This also calls the callback
+ *  function to set the rate in the protocol layer object.
+*/
+
+static int ath_rate_setup(struct ath_softc *sc, enum wireless_mode mode)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       const struct ath9k_rate_table *rt;
+
+       switch (mode) {
+       case WIRELESS_MODE_11a:
+               sc->sc_rates[mode] =
+                       ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11A);
+               break;
+       case WIRELESS_MODE_11b:
+               sc->sc_rates[mode] =
+                       ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11B);
+               break;
+       case WIRELESS_MODE_11g:
+               sc->sc_rates[mode] =
+                       ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11G);
+               break;
+       case WIRELESS_MODE_11NA_HT20:
+               sc->sc_rates[mode] =
+                       ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NA_HT20);
+               break;
+       case WIRELESS_MODE_11NG_HT20:
+               sc->sc_rates[mode] =
+                       ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NG_HT20);
+               break;
+       case WIRELESS_MODE_11NA_HT40PLUS:
+               sc->sc_rates[mode] =
+                       ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NA_HT40PLUS);
+               break;
+       case WIRELESS_MODE_11NA_HT40MINUS:
+               sc->sc_rates[mode] =
+                       ath9k_hw_getratetable(ah,
+                               ATH9K_MODE_SEL_11NA_HT40MINUS);
+               break;
+       case WIRELESS_MODE_11NG_HT40PLUS:
+               sc->sc_rates[mode] =
+                       ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NG_HT40PLUS);
+               break;
+       case WIRELESS_MODE_11NG_HT40MINUS:
+               sc->sc_rates[mode] =
+                       ath9k_hw_getratetable(ah,
+                               ATH9K_MODE_SEL_11NG_HT40MINUS);
+               break;
+       default:
+               DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid mode %u\n",
+                       __func__, mode);
+               return 0;
+       }
+       rt = sc->sc_rates[mode];
+       if (rt == NULL)
+               return 0;
+
+       /* setup rate set in 802.11 protocol layer */
+       ath_setup_rate(sc, mode, NORMAL_RATE, rt);
+
+       return 1;
+}
+
+/*
+ *  Set up channel list
+ */
+static int ath_setup_channels(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       int nchan, i, a = 0, b = 0;
+       u8 regclassids[ATH_REGCLASSIDS_MAX];
+       u32 nregclass = 0;
+       struct ieee80211_supported_band *band_2ghz;
+       struct ieee80211_supported_band *band_5ghz;
+       struct ieee80211_channel *chan_2ghz;
+       struct ieee80211_channel *chan_5ghz;
+       struct ath9k_channel *c;
+
+       /* Fill in ah->ah_channels */
+       if (!ath9k_regd_init_channels(ah,
+                                     ATH_CHAN_MAX,
+                                     (u32 *)&nchan,
+                                     regclassids,
+                                     ATH_REGCLASSIDS_MAX,
+                                     &nregclass,
+                                     CTRY_DEFAULT,
+                                     ATH9K_MODE_SEL_ALL,
+                                     false,
+                                     1)) {
+               u32 rd = ah->ah_currentRD;
+
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to collect channel list; "
+                       "regdomain likely %u country code %u\n",
+                       __func__, rd, CTRY_DEFAULT);
+               return -EINVAL;
+       }
+
+       band_2ghz = &sc->sbands[IEEE80211_BAND_2GHZ];
+       band_5ghz = &sc->sbands[IEEE80211_BAND_5GHZ];
+       chan_2ghz = sc->channels[IEEE80211_BAND_2GHZ];
+       chan_5ghz = sc->channels[IEEE80211_BAND_5GHZ];
+
+       for (i = 0; i < nchan; i++) {
+               c = &ah->ah_channels[i];
+               if (IS_CHAN_2GHZ(c)) {
+                       chan_2ghz[a].band = IEEE80211_BAND_2GHZ;
+                       chan_2ghz[a].center_freq = c->channel;
+                       chan_2ghz[a].max_power = c->maxTxPower;
+
+                       if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
+                               chan_2ghz[a].flags |=
+                                       IEEE80211_CHAN_NO_IBSS;
+                       if (c->channelFlags & CHANNEL_PASSIVE)
+                               chan_2ghz[a].flags |=
+                                       IEEE80211_CHAN_PASSIVE_SCAN;
+
+                       band_2ghz->n_channels = ++a;
+
+                       DPRINTF(sc, ATH_DBG_CONFIG,
+                               "%s: 2MHz channel: %d, "
+                               "channelFlags: 0x%x\n",
+                               __func__,
+                               c->channel,
+                               c->channelFlags);
+               } else if (IS_CHAN_5GHZ(c)) {
+                       chan_5ghz[b].band = IEEE80211_BAND_5GHZ;
+                       chan_5ghz[b].center_freq = c->channel;
+                       chan_5ghz[b].max_power = c->maxTxPower;
+
+                       if (c->privFlags & CHANNEL_DISALLOW_ADHOC)
+                               chan_5ghz[b].flags |=
+                                       IEEE80211_CHAN_NO_IBSS;
+                       if (c->channelFlags & CHANNEL_PASSIVE)
+                               chan_5ghz[b].flags |=
+                                       IEEE80211_CHAN_PASSIVE_SCAN;
+
+                       band_5ghz->n_channels = ++b;
+
+                       DPRINTF(sc, ATH_DBG_CONFIG,
+                               "%s: 5MHz channel: %d, "
+                               "channelFlags: 0x%x\n",
+                               __func__,
+                               c->channel,
+                               c->channelFlags);
+               }
+       }
+
+       return 0;
+}
+
+/*
+ *  Determine mode from channel flags
+ *
+ *  This routine will provide the enumerated WIRELESSS_MODE value based
+ *  on the settings of the channel flags.  If ho valid set of flags
+ *  exist, the lowest mode (11b) is selected.
+*/
+
+static enum wireless_mode ath_chan2mode(struct ath9k_channel *chan)
+{
+       if (chan->chanmode == CHANNEL_A)
+               return WIRELESS_MODE_11a;
+       else if (chan->chanmode == CHANNEL_G)
+               return WIRELESS_MODE_11g;
+       else if (chan->chanmode == CHANNEL_B)
+               return WIRELESS_MODE_11b;
+       else if (chan->chanmode == CHANNEL_A_HT20)
+               return WIRELESS_MODE_11NA_HT20;
+       else if (chan->chanmode == CHANNEL_G_HT20)
+               return WIRELESS_MODE_11NG_HT20;
+       else if (chan->chanmode == CHANNEL_A_HT40PLUS)
+               return WIRELESS_MODE_11NA_HT40PLUS;
+       else if (chan->chanmode == CHANNEL_A_HT40MINUS)
+               return WIRELESS_MODE_11NA_HT40MINUS;
+       else if (chan->chanmode == CHANNEL_G_HT40PLUS)
+               return WIRELESS_MODE_11NG_HT40PLUS;
+       else if (chan->chanmode == CHANNEL_G_HT40MINUS)
+               return WIRELESS_MODE_11NG_HT40MINUS;
+
+       /* NB: should not get here */
+       return WIRELESS_MODE_11b;
+}
+
+/*
+ *  Change Channels
+ *
+ *  Performs the actions to change the channel in the hardware, and set up
+ *  the current operating mode for the new channel.
+*/
+
+static void ath_chan_change(struct ath_softc *sc, struct ath9k_channel *chan)
+{
+       enum wireless_mode mode;
+
+       mode = ath_chan2mode(chan);
+
+       ath_rate_setup(sc, mode);
+       ath_setcurmode(sc, mode);
+}
+
+/*
+ * Stop the device, grabbing the top-level lock to protect
+ * against concurrent entry through ath_init (which can happen
+ * if another thread does a system call and the thread doing the
+ * stop is preempted).
+ */
+
+static int ath_stop(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+
+       DPRINTF(sc, ATH_DBG_CONFIG, "%s: invalid %u\n",
+               __func__, sc->sc_invalid);
+
+       /*
+        * Shutdown the hardware and driver:
+        *    stop output from above
+        *    reset 802.11 state machine
+        *      (sends station deassoc/deauth frames)
+        *    turn off timers
+        *    disable interrupts
+        *    clear transmit machinery
+        *    clear receive machinery
+        *    turn off the radio
+        *    reclaim beacon resources
+        *
+        * Note that some of this work is not possible if the
+        * hardware is gone (invalid).
+        */
+
+       if (!sc->sc_invalid)
+               ath9k_hw_set_interrupts(ah, 0);
+       ath_draintxq(sc, false);
+       if (!sc->sc_invalid) {
+               ath_stoprecv(sc);
+               ath9k_hw_phy_disable(ah);
+       } else
+               sc->sc_rxlink = NULL;
+
+       return 0;
+}
+
+/*
+ *  Start Scan
+ *
+ *  This function is called when starting a channel scan.  It will perform
+ *  power save wakeup processing, set the filter for the scan, and get the
+ *  chip ready to send broadcast packets out during the scan.
+*/
+
+void ath_scan_start(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       u32 rfilt;
+       u32 now = (u32) jiffies_to_msecs(get_timestamp());
+
+       sc->sc_scanning = 1;
+       rfilt = ath_calcrxfilter(sc);
+       ath9k_hw_setrxfilter(ah, rfilt);
+       ath9k_hw_write_associd(ah, ath_bcast_mac, 0);
+
+       /* Restore previous power management state. */
+
+       DPRINTF(sc, ATH_DBG_CONFIG, "%d.%03d | %s: RX filter 0x%x aid 0\n",
+               now / 1000, now % 1000, __func__, rfilt);
+}
+
+/*
+ *  Scan End
+ *
+ *  This routine is called by the upper layer when the scan is completed.  This
+ *  will set the filters back to normal operating mode, set the BSSID to the
+ *  correct value, and restore the power save state.
+*/
+
+void ath_scan_end(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       u32 rfilt;
+       u32 now = (u32) jiffies_to_msecs(get_timestamp());
+
+       sc->sc_scanning = 0;
+       /* Request for a full reset due to rx packet filter changes */
+       sc->sc_full_reset = 1;
+       rfilt = ath_calcrxfilter(sc);
+       ath9k_hw_setrxfilter(ah, rfilt);
+       ath9k_hw_write_associd(ah, sc->sc_curbssid, sc->sc_curaid);
+
+       DPRINTF(sc, ATH_DBG_CONFIG, "%d.%03d | %s: RX filter 0x%x aid 0x%x\n",
+               now / 1000, now % 1000, __func__, rfilt, sc->sc_curaid);
+}
+
+/*
+ * Set the current channel
+ *
+ * Set/change channels.  If the channel is really being changed, it's done
+ * by reseting the chip.  To accomplish this we must first cleanup any pending
+ * DMA, then restart stuff after a la ath_init.
+*/
+int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       bool fastcc = true, stopped;
+       enum ath9k_ht_macmode ht_macmode;
+
+       if (sc->sc_invalid)     /* if the device is invalid or removed */
+               return -EIO;
+
+       DPRINTF(sc, ATH_DBG_CONFIG,
+               "%s: %u (%u MHz) -> %u (%u MHz), cflags:%x\n",
+               __func__,
+               ath9k_hw_mhz2ieee(ah, sc->sc_curchan.channel,
+                                 sc->sc_curchan.channelFlags),
+               sc->sc_curchan.channel,
+               ath9k_hw_mhz2ieee(ah, hchan->channel, hchan->channelFlags),
+               hchan->channel, hchan->channelFlags);
+
+       ht_macmode = ath_cwm_macmode(sc);
+
+       if (hchan->channel != sc->sc_curchan.channel ||
+           hchan->channelFlags != sc->sc_curchan.channelFlags ||
+           sc->sc_update_chainmask || sc->sc_full_reset) {
+               int status;
+               /*
+                * This is only performed if the channel settings have
+                * actually changed.
+                *
+                * To switch channels clear any pending DMA operations;
+                * wait long enough for the RX fifo to drain, reset the
+                * hardware at the new frequency, and then re-enable
+                * the relevant bits of the h/w.
+                */
+               ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */
+               ath_draintxq(sc, false);        /* clear pending tx frames */
+               stopped = ath_stoprecv(sc);     /* turn off frame recv */
+
+               /* XXX: do not flush receive queue here. We don't want
+                * to flush data frames already in queue because of
+                * changing channel. */
+
+               if (!stopped || sc->sc_full_reset)
+                       fastcc = false;
+
+               spin_lock_bh(&sc->sc_resetlock);
+               if (!ath9k_hw_reset(ah, sc->sc_opmode, hchan,
+                                       ht_macmode, sc->sc_tx_chainmask,
+                                       sc->sc_rx_chainmask,
+                                       sc->sc_ht_extprotspacing,
+                                       fastcc, &status)) {
+                       DPRINTF(sc, ATH_DBG_FATAL,
+                               "%s: unable to reset channel %u (%uMhz) "
+                               "flags 0x%x hal status %u\n", __func__,
+                               ath9k_hw_mhz2ieee(ah, hchan->channel,
+                                                 hchan->channelFlags),
+                               hchan->channel, hchan->channelFlags, status);
+                       spin_unlock_bh(&sc->sc_resetlock);
+                       return -EIO;
+               }
+               spin_unlock_bh(&sc->sc_resetlock);
+
+               sc->sc_curchan = *hchan;
+               sc->sc_update_chainmask = 0;
+               sc->sc_full_reset = 0;
+
+               /* Re-enable rx framework */
+               if (ath_startrecv(sc) != 0) {
+                       DPRINTF(sc, ATH_DBG_FATAL,
+                               "%s: unable to restart recv logic\n", __func__);
+                       return -EIO;
+               }
+               /*
+                * Change channels and update the h/w rate map
+                * if we're switching; e.g. 11a to 11b/g.
+                */
+               ath_chan_change(sc, hchan);
+               ath_update_txpow(sc);   /* update tx power state */
+               /*
+                * Re-enable interrupts.
+                */
+               ath9k_hw_set_interrupts(ah, sc->sc_imask);
+       }
+       return 0;
+}
+
+/**********************/
+/* Chainmask Handling */
+/**********************/
+
+static void ath_chainmask_sel_timertimeout(unsigned long data)
+{
+       struct ath_chainmask_sel *cm = (struct ath_chainmask_sel *)data;
+       cm->switch_allowed = 1;
+}
+
+/* Start chainmask select timer */
+static void ath_chainmask_sel_timerstart(struct ath_chainmask_sel *cm)
+{
+       cm->switch_allowed = 0;
+       mod_timer(&cm->timer, ath_chainmask_sel_period);
+}
+
+/* Stop chainmask select timer */
+static void ath_chainmask_sel_timerstop(struct ath_chainmask_sel *cm)
+{
+       cm->switch_allowed = 0;
+       del_timer_sync(&cm->timer);
+}
+
+static void ath_chainmask_sel_init(struct ath_softc *sc, struct ath_node *an)
+{
+       struct ath_chainmask_sel *cm = &an->an_chainmask_sel;
+
+       memzero(cm, sizeof(struct ath_chainmask_sel));
+
+       cm->cur_tx_mask = sc->sc_tx_chainmask;
+       cm->cur_rx_mask = sc->sc_rx_chainmask;
+       cm->tx_avgrssi = ATH_RSSI_DUMMY_MARKER;
+       setup_timer(&cm->timer,
+               ath_chainmask_sel_timertimeout, (unsigned long) cm);
+}
+
+int ath_chainmask_sel_logic(struct ath_softc *sc, struct ath_node *an)
+{
+       struct ath_chainmask_sel *cm = &an->an_chainmask_sel;
+
+       /*
+        * Disable auto-swtiching in one of the following if conditions.
+        * sc_chainmask_auto_sel is used for internal global auto-switching
+        * enabled/disabled setting
+        */
+       if (sc->sc_ah->ah_caps.halTxChainMask != ATH_CHAINMASK_SEL_3X3) {
+               cm->cur_tx_mask = sc->sc_tx_chainmask;
+               return cm->cur_tx_mask;
+       }
+
+       if (cm->tx_avgrssi == ATH_RSSI_DUMMY_MARKER)
+               return cm->cur_tx_mask;
+
+       if (cm->switch_allowed) {
+               /* Switch down from tx 3 to tx 2. */
+               if (cm->cur_tx_mask == ATH_CHAINMASK_SEL_3X3 &&
+                   ATH_RSSI_OUT(cm->tx_avgrssi) >=
+                   ath_chainmask_sel_down_rssi_thres) {
+                       cm->cur_tx_mask = sc->sc_tx_chainmask;
+
+                       /* Don't let another switch happen until
+                        * this timer expires */
+                       ath_chainmask_sel_timerstart(cm);
+               }
+               /* Switch up from tx 2 to 3. */
+               else if (cm->cur_tx_mask == sc->sc_tx_chainmask &&
+                        ATH_RSSI_OUT(cm->tx_avgrssi) <=
+                        ath_chainmask_sel_up_rssi_thres) {
+                       cm->cur_tx_mask = ATH_CHAINMASK_SEL_3X3;
+
+                       /* Don't let another switch happen
+                        * until this timer expires */
+                       ath_chainmask_sel_timerstart(cm);
+               }
+       }
+
+       return cm->cur_tx_mask;
+}
+
+/*
+ * Update tx/rx chainmask. For legacy association,
+ * hard code chainmask to 1x1, for 11n association, use
+ * the chainmask configuration.
+ */
+
+void ath_update_chainmask(struct ath_softc *sc, int is_ht)
+{
+       sc->sc_update_chainmask = 1;
+       if (is_ht) {
+               sc->sc_tx_chainmask = sc->sc_ah->ah_caps.halTxChainMask;
+               sc->sc_rx_chainmask = sc->sc_ah->ah_caps.halRxChainMask;
+       } else {
+               sc->sc_tx_chainmask = 1;
+               sc->sc_rx_chainmask = 1;
+       }
+
+       DPRINTF(sc, ATH_DBG_CONFIG, "%s: tx chmask: %d, rx chmask: %d\n",
+               __func__, sc->sc_tx_chainmask, sc->sc_rx_chainmask);
+}
+
+/******************/
+/* VAP management */
+/******************/
+
+/*
+ *  VAP in Listen mode
+ *
+ *  This routine brings the VAP out of the down state into a "listen" state
+ *  where it waits for association requests.  This is used in AP and AdHoc
+ *  modes.
+*/
+
+int ath_vap_listen(struct ath_softc *sc, int if_id)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_vap *avp;
+       u32 rfilt = 0;
+       DECLARE_MAC_BUF(mac);
+
+       avp = sc->sc_vaps[if_id];
+       if (avp == NULL) {
+               DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid interface id %u\n",
+                       __func__, if_id);
+               return -EINVAL;
+       }
+
+#ifdef CONFIG_SLOW_ANT_DIV
+       ath_slow_ant_div_stop(&sc->sc_antdiv);
+#endif
+
+       /* update ratectrl about the new state */
+       ath_rate_newstate(sc, avp);
+
+       rfilt = ath_calcrxfilter(sc);
+       ath9k_hw_setrxfilter(ah, rfilt);
+
+       if (sc->sc_opmode == ATH9K_M_STA || sc->sc_opmode == ATH9K_M_IBSS) {
+               memcpy(sc->sc_curbssid, ath_bcast_mac, ETH_ALEN);
+               ath9k_hw_write_associd(ah, sc->sc_curbssid, sc->sc_curaid);
+       } else
+               sc->sc_curaid = 0;
+
+       DPRINTF(sc, ATH_DBG_CONFIG,
+               "%s: RX filter 0x%x bssid %s aid 0x%x\n",
+               __func__, rfilt, print_mac(mac,
+                       sc->sc_curbssid), sc->sc_curaid);
+
+       /*
+        * XXXX
+        * Disable BMISS interrupt when we're not associated
+        */
+       ath9k_hw_set_interrupts(ah,
+               sc->sc_imask & ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS));
+       sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
+       /* need to reconfigure the beacons when it moves to RUN */
+       sc->sc_beacons = 0;
+
+       return 0;
+}
+
+int ath_vap_attach(struct ath_softc *sc,
+                  int if_id,
+                  struct ieee80211_vif *if_data,
+                  enum ath9k_opmode opmode)
+{
+       struct ath_vap *avp;
+
+       if (if_id >= ATH_BCBUF || sc->sc_vaps[if_id] != NULL) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: Invalid interface id = %u\n", __func__, if_id);
+               return -EINVAL;
+       }
+
+       switch (opmode) {
+       case ATH9K_M_STA:
+       case ATH9K_M_IBSS:
+       case ATH9K_M_MONITOR:
+               break;
+       case ATH9K_M_HOSTAP:
+               /* XXX not right, beacon buffer is allocated on RUN trans */
+               if (list_empty(&sc->sc_bbuf))
+                       return -ENOMEM;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* create ath_vap */
+       avp = kmalloc(sizeof(struct ath_vap), GFP_KERNEL);
+       if (avp == NULL)
+               return -ENOMEM;
+
+       memzero(avp, sizeof(struct ath_vap));
+       avp->av_if_data = if_data;
+       /* Set the VAP opmode */
+       avp->av_opmode = opmode;
+       avp->av_bslot = -1;
+       INIT_LIST_HEAD(&avp->av_mcastq.axq_q);
+       INIT_LIST_HEAD(&avp->av_mcastq.axq_acq);
+       spin_lock_init(&avp->av_mcastq.axq_lock);
+
+       ath9k_hw_set_tsfadjust(sc->sc_ah, 1);
+
+       sc->sc_vaps[if_id] = avp;
+       sc->sc_nvaps++;
+       /* Set the device opmode */
+       sc->sc_opmode = opmode;
+
+       /* default VAP configuration */
+       avp->av_config.av_fixed_rateset = IEEE80211_FIXED_RATE_NONE;
+       avp->av_config.av_fixed_retryset = 0x03030303;
+
+       return 0;
+}
+
+int ath_vap_detach(struct ath_softc *sc, int if_id)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       struct ath_vap *avp;
+
+       avp = sc->sc_vaps[if_id];
+       if (avp == NULL) {
+               DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid interface id %u\n",
+                       __func__, if_id);
+               return -EINVAL;
+       }
+
+       /*
+        * Quiesce the hardware while we remove the vap.  In
+        * particular we need to reclaim all references to the
+        * vap state by any frames pending on the tx queues.
+        *
+        * XXX can we do this w/o affecting other vap's?
+        */
+       ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */
+       ath_draintxq(sc, false);        /* stop xmit side */
+       ath_stoprecv(sc);       /* stop recv side */
+       ath_flushrecv(sc);      /* flush recv queue */
+
+       /* Reclaim any pending mcast bufs on the vap. */
+       ath_tx_draintxq(sc, &avp->av_mcastq, false);
+
+       kfree(avp);
+       sc->sc_vaps[if_id] = NULL;
+       sc->sc_nvaps--;
+
+       return 0;
+}
+
+int ath_vap_config(struct ath_softc *sc,
+       int if_id, struct ath_vap_config *if_config)
+{
+       struct ath_vap *avp;
+
+       if (if_id >= ATH_BCBUF) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: Invalid interface id = %u\n", __func__, if_id);
+               return -EINVAL;
+       }
+
+       avp = sc->sc_vaps[if_id];
+       ASSERT(avp != NULL);
+
+       if (avp)
+               memcpy(&avp->av_config, if_config, sizeof(avp->av_config));
+
+       return 0;
+}
+
+/********/
+/* Core */
+/********/
+
+int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       int status;
+       int error = 0;
+       enum ath9k_ht_macmode ht_macmode = ath_cwm_macmode(sc);
+
+       DPRINTF(sc, ATH_DBG_CONFIG, "%s: mode %d\n", __func__, sc->sc_opmode);
+
+       /*
+        * Stop anything previously setup.  This is safe
+        * whether this is the first time through or not.
+        */
+       ath_stop(sc);
+
+       /* Initialize chanmask selection */
+       sc->sc_tx_chainmask = ah->ah_caps.halTxChainMask;
+       sc->sc_rx_chainmask = ah->ah_caps.halRxChainMask;
+
+       /* Reset SERDES registers */
+       ath9k_hw_configpcipowersave(ah, 0);
+
+       /*
+        * The basic interface to setting the hardware in a good
+        * state is ``reset''.  On return the hardware is known to
+        * be powered up and with interrupts disabled.  This must
+        * be followed by initialization of the appropriate bits
+        * and then setup of the interrupt mask.
+        */
+       sc->sc_curchan = *initial_chan;
+
+       spin_lock_bh(&sc->sc_resetlock);
+       if (!ath9k_hw_reset(ah, sc->sc_opmode, &sc->sc_curchan, ht_macmode,
+                          sc->sc_tx_chainmask, sc->sc_rx_chainmask,
+                          sc->sc_ht_extprotspacing, false, &status)) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to reset hardware; hal status %u "
+                       "(freq %u flags 0x%x)\n", __func__, status,
+                       sc->sc_curchan.channel, sc->sc_curchan.channelFlags);
+               error = -EIO;
+               spin_unlock_bh(&sc->sc_resetlock);
+               goto done;
+       }
+       spin_unlock_bh(&sc->sc_resetlock);
+       /*
+        * This is needed only to setup initial state
+        * but it's best done after a reset.
+        */
+       ath_update_txpow(sc);
+
+       /*
+        * Setup the hardware after reset:
+        * The receive engine is set going.
+        * Frame transmit is handled entirely
+        * in the frame output path; there's nothing to do
+        * here except setup the interrupt mask.
+        */
+       if (ath_startrecv(sc) != 0) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to start recv logic\n", __func__);
+               error = -EIO;
+               goto done;
+       }
+       /* Setup our intr mask. */
+       sc->sc_imask = ATH9K_INT_RX | ATH9K_INT_TX
+               | ATH9K_INT_RXEOL | ATH9K_INT_RXORN
+               | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL;
+
+       if (ah->ah_caps.halGTTSupport)
+               sc->sc_imask |= ATH9K_INT_GTT;
+
+       if (ah->ah_caps.halHTSupport)
+               sc->sc_imask |= ATH9K_INT_CST;
+
+       /*
+        * Enable MIB interrupts when there are hardware phy counters.
+        * Note we only do this (at the moment) for station mode.
+        */
+       if (ath9k_hw_phycounters(ah) &&
+           ((sc->sc_opmode == ATH9K_M_STA) || (sc->sc_opmode == ATH9K_M_IBSS)))
+               sc->sc_imask |= ATH9K_INT_MIB;
+       /*
+        * Some hardware processes the TIM IE and fires an
+        * interrupt when the TIM bit is set.  For hardware
+        * that does, if not overridden by configuration,
+        * enable the TIM interrupt when operating as station.
+        */
+       if (ah->ah_caps.halEnhancedPmSupport && sc->sc_opmode == ATH9K_M_STA &&
+               !sc->sc_config.swBeaconProcess)
+               sc->sc_imask |= ATH9K_INT_TIM;
+       /*
+        *  Don't enable interrupts here as we've not yet built our
+        *  vap and node data structures, which will be needed as soon
+        *  as we start receiving.
+        */
+       ath_chan_change(sc, initial_chan);
+
+       /* XXX: we must make sure h/w is ready and clear invalid flag
+        * before turning on interrupt. */
+       sc->sc_invalid = 0;
+done:
+       return error;
+}
+
+/*
+ * Reset the hardware w/o losing operational state.  This is
+ * basically a more efficient way of doing ath_stop, ath_init,
+ * followed by state transitions to the current 802.11
+ * operational state.  Used to recover from errors rx overrun
+ * and to reset the hardware when rf gain settings must be reset.
+ */
+
+static int ath_reset_start(struct ath_softc *sc, u32 flag)
+{
+       struct ath_hal *ah = sc->sc_ah;
+
+       ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */
+       ath_draintxq(sc, flag & RESET_RETRY_TXQ);       /* stop xmit side */
+       ath_stoprecv(sc);       /* stop recv side */
+       ath_flushrecv(sc);      /* flush recv queue */
+
+       return 0;
+}
+
+static int ath_reset_end(struct ath_softc *sc, u32 flag)
+{
+       struct ath_hal *ah = sc->sc_ah;
+
+       if (ath_startrecv(sc) != 0)     /* restart recv */
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to start recv logic\n", __func__);
+
+       /*
+        * We may be doing a reset in response to a request
+        * that changes the channel so update any state that
+        * might change as a result.
+        */
+       ath_chan_change(sc, &sc->sc_curchan);
+
+       ath_update_txpow(sc);   /* update tx power state */
+
+       if (sc->sc_beacons)
+               ath_beacon_config(sc, ATH_IF_ID_ANY);   /* restart beacons */
+       ath9k_hw_set_interrupts(ah, sc->sc_imask);
+
+       /* Restart the txq */
+       if (flag & RESET_RETRY_TXQ) {
+               int i;
+               for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
+                       if (ATH_TXQ_SETUP(sc, i)) {
+                               spin_lock_bh(&sc->sc_txq[i].axq_lock);
+                               ath_txq_schedule(sc, &sc->sc_txq[i]);
+                               spin_unlock_bh(&sc->sc_txq[i].axq_lock);
+                       }
+               }
+       }
+       return 0;
+}
+
+int ath_reset(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       int status;
+       int error = 0;
+       enum ath9k_ht_macmode ht_macmode = ath_cwm_macmode(sc);
+
+       /* NB: indicate channel change so we do a full reset */
+       spin_lock_bh(&sc->sc_resetlock);
+       if (!ath9k_hw_reset(ah, sc->sc_opmode, &sc->sc_curchan,
+                          ht_macmode,
+                          sc->sc_tx_chainmask, sc->sc_rx_chainmask,
+                          sc->sc_ht_extprotspacing, false, &status)) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to reset hardware; hal status %u\n",
+                       __func__, status);
+               error = -EIO;
+       }
+       spin_unlock_bh(&sc->sc_resetlock);
+
+       return error;
+}
+
+int ath_suspend(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+
+       /* No I/O if device has been surprise removed */
+       if (sc->sc_invalid)
+               return -EIO;
+
+       /* Shut off the interrupt before setting sc->sc_invalid to '1' */
+       ath9k_hw_set_interrupts(ah, 0);
+
+       /* XXX: we must make sure h/w will not generate any interrupt
+        * before setting the invalid flag. */
+       sc->sc_invalid = 1;
+
+       /* disable HAL and put h/w to sleep */
+       ath9k_hw_disable(sc->sc_ah);
+
+       ath9k_hw_configpcipowersave(sc->sc_ah, 1);
+
+       return 0;
+}
+
+/* Interrupt handler.  Most of the actual processing is deferred.
+ * It's the caller's responsibility to ensure the chip is awake. */
+
+irqreturn_t ath_isr(int irq, void *dev)
+{
+       struct ath_softc *sc = dev;
+       struct ath_hal *ah = sc->sc_ah;
+       enum ath9k_int status;
+       bool sched = false;
+
+       do {
+               if (sc->sc_invalid) {
+                       /*
+                        * The hardware is not ready/present, don't
+                        * touch anything. Note this can happen early
+                        * on if the IRQ is shared.
+                        */
+                       return IRQ_NONE;
+               }
+               if (!ath9k_hw_intrpend(ah)) {   /* shared irq, not for us */
+                       return IRQ_NONE;
+               }
+
+               /*
+                * Figure out the reason(s) for the interrupt.  Note
+                * that the hal returns a pseudo-ISR that may include
+                * bits we haven't explicitly enabled so we mask the
+                * value to insure we only process bits we requested.
+                */
+               ath9k_hw_getisr(ah, &status);   /* NB: clears ISR too */
+
+               status &= sc->sc_imask; /* discard unasked-for bits */
+
+               /*
+                * If there are no status bits set, then this interrupt was not
+                * for me (should have been caught above).
+                */
+
+               if (!status)
+                       return IRQ_NONE;
+
+               sc->sc_intrstatus = status;
+
+               if (status & ATH9K_INT_FATAL) {
+                       /* need a chip reset */
+                       sched = true;
+               } else if (status & ATH9K_INT_RXORN) {
+                       /* need a chip reset */
+                       sched = true;
+               } else {
+                       if (status & ATH9K_INT_SWBA) {
+                               /* schedule a tasklet for beacon handling */
+                               tasklet_schedule(&sc->bcon_tasklet);
+                       }
+                       if (status & ATH9K_INT_RXEOL) {
+                               /*
+                                * NB: the hardware should re-read the link when
+                                *     RXE bit is written, but it doesn't work
+                                *     at least on older hardware revs.
+                                */
+                               sched = true;
+                       }
+
+                       if (status & ATH9K_INT_TXURN)
+                               /* bump tx trigger level */
+                               ath9k_hw_updatetxtriglevel(ah, true);
+                       /* XXX: optimize this */
+                       if (status & ATH9K_INT_RX)
+                               sched = true;
+                       if (status & ATH9K_INT_TX)
+                               sched = true;
+                       if (status & ATH9K_INT_BMISS)
+                               sched = true;
+                       /* carrier sense timeout */
+                       if (status & ATH9K_INT_CST)
+                               sched = true;
+                       if (status & ATH9K_INT_MIB) {
+                               /*
+                                * Disable interrupts until we service the MIB
+                                * interrupt; otherwise it will continue to
+                                * fire.
+                                */
+                               ath9k_hw_set_interrupts(ah, 0);
+                               /*
+                                * Let the hal handle the event. We assume
+                                * it will clear whatever condition caused
+                                * the interrupt.
+                                */
+                               ath9k_hw_procmibevent(ah, &sc->sc_halstats);
+                               ath9k_hw_set_interrupts(ah, sc->sc_imask);
+                       }
+                       if (status & ATH9K_INT_TIM_TIMER) {
+                               if (!ah->ah_caps.halAutoSleepSupport) {
+                                       /* Clear RxAbort bit so that we can
+                                        * receive frames */
+                                       ath9k_hw_setrxabort(ah, 0);
+                                       sched = true;
+                               }
+                       }
+               }
+       } while (0);
+
+       if (sched) {
+               /* turn off every interrupt except SWBA */
+               ath9k_hw_set_interrupts(ah, (sc->sc_imask & ATH9K_INT_SWBA));
+               tasklet_schedule(&sc->intr_tq);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/* Deferred interrupt processing  */
+
+static void ath9k_tasklet(unsigned long data)
+{
+       struct ath_softc *sc = (struct ath_softc *)data;
+       u32 status = sc->sc_intrstatus;
+
+       if (status & ATH9K_INT_FATAL) {
+               /* need a chip reset */
+               ath_internal_reset(sc);
+               return;
+       } else {
+
+               if (status &
+                   (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN)) {
+                       /* XXX: fill me in */
+                       /*
+                       if (status & ATH9K_INT_RXORN) {
+                       }
+                       if (status & ATH9K_INT_RXEOL) {
+                       }
+                       */
+                       spin_lock_bh(&sc->sc_rxflushlock);
+                       ath_rx_tasklet(sc, 0);
+                       spin_unlock_bh(&sc->sc_rxflushlock);
+               }
+               /* XXX: optimize this */
+               if (status & ATH9K_INT_TX)
+                       ath_tx_tasklet(sc);
+               /* XXX: fill me in */
+               /*
+               if (status & ATH9K_INT_BMISS) {
+               }
+               if (status & (ATH9K_INT_TIM | ATH9K_INT_DTIMSYNC)) {
+                       if (status & ATH9K_INT_TIM) {
+                       }
+                       if (status & ATH9K_INT_DTIMSYNC) {
+                       }
+               }
+               */
+       }
+
+       /* re-enable hardware interrupt */
+       ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
+}
+
+int ath_init(u16 devid, struct ath_softc *sc)
+{
+       struct ath_hal *ah = NULL;
+       int status;
+       int error = 0, i;
+       int csz = 0;
+       u32 rd;
+
+       /* XXX: hardware will not be ready until ath_open() being called */
+       sc->sc_invalid = 1;
+
+       sc->sc_debug = DBG_DEFAULT;
+       DPRINTF(sc, ATH_DBG_CONFIG, "%s: devid 0x%x\n", __func__, devid);
+
+       /* Initialize tasklet */
+       tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
+       tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
+                    (unsigned long)sc);
+
+       /*
+        * Cache line size is used to size and align various
+        * structures used to communicate with the hardware.
+        */
+       bus_read_cachesize(sc, &csz);
+       /* XXX assert csz is non-zero */
+       sc->sc_cachelsz = csz << 2;     /* convert to bytes */
+
+       spin_lock_init(&sc->sc_resetlock);
+
+       ah = ath9k_hw_attach(devid, sc, sc->mem, &status);
+       if (ah == NULL) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to attach hardware; HAL status %u\n",
+                       __func__, status);
+               error = -ENXIO;
+               goto bad;
+       }
+       sc->sc_ah = ah;
+
+       /* Get the chipset-specific aggr limit. */
+       sc->sc_rtsaggrlimit = ah->ah_caps.halRtsAggrLimit;
+
+       /* Get the hardware key cache size. */
+       sc->sc_keymax = ah->ah_caps.halKeyCacheSize;
+       if (sc->sc_keymax > ATH_KEYMAX) {
+               DPRINTF(sc, ATH_DBG_KEYCACHE,
+                       "%s: Warning, using only %u entries in %u key cache\n",
+                       __func__, ATH_KEYMAX, sc->sc_keymax);
+               sc->sc_keymax = ATH_KEYMAX;
+       }
+
+       /*
+        * Reset the key cache since some parts do not
+        * reset the contents on initial power up.
+        */
+       for (i = 0; i < sc->sc_keymax; i++)
+               ath9k_hw_keyreset(ah, (u16) i);
+       /*
+        * Mark key cache slots associated with global keys
+        * as in use.  If we knew TKIP was not to be used we
+        * could leave the +32, +64, and +32+64 slots free.
+        * XXX only for splitmic.
+        */
+       for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+               set_bit(i, sc->sc_keymap);
+               set_bit(i + 32, sc->sc_keymap);
+               set_bit(i + 64, sc->sc_keymap);
+               set_bit(i + 32 + 64, sc->sc_keymap);
+       }
+       /*
+        * Collect the channel list using the default country
+        * code and including outdoor channels.  The 802.11 layer
+        * is resposible for filtering this list based on settings
+        * like the phy mode.
+        */
+       rd = ah->ah_currentRD;
+
+       error = ath_setup_channels(sc);
+       if (error)
+               goto bad;
+
+       /* default to STA mode */
+       sc->sc_opmode = ATH9K_M_MONITOR;
+
+       /* Setup rate tables for all potential media types. */
+       /* 11g encompasses b,g */
+
+       ath_rate_setup(sc, WIRELESS_MODE_11a);
+       ath_rate_setup(sc, WIRELESS_MODE_11g);
+
+       /* NB: setup here so ath_rate_update is happy */
+       ath_setcurmode(sc, WIRELESS_MODE_11a);
+
+       /*
+        * Allocate hardware transmit queues: one queue for
+        * beacon frames and one data queue for each QoS
+        * priority.  Note that the hal handles reseting
+        * these queues at the needed time.
+        */
+       sc->sc_bhalq = ath_beaconq_setup(ah);
+       if (sc->sc_bhalq == -1) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to setup a beacon xmit queue\n", __func__);
+               error = -EIO;
+               goto bad2;
+       }
+       sc->sc_cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
+       if (sc->sc_cabq == NULL) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to setup CAB xmit queue\n", __func__);
+               error = -EIO;
+               goto bad2;
+       }
+
+       sc->sc_config.cabqReadytime = ATH_CABQ_READY_TIME;
+       ath_cabq_update(sc);
+
+       for (i = 0; i < ARRAY_SIZE(sc->sc_haltype2q); i++)
+               sc->sc_haltype2q[i] = -1;
+
+       /* Setup data queues */
+       /* NB: ensure BK queue is the lowest priority h/w queue */
+       if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to setup xmit queue for BK traffic\n",
+                       __func__);
+               error = -EIO;
+               goto bad2;
+       }
+
+       if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to setup xmit queue for BE traffic\n",
+                       __func__);
+               error = -EIO;
+               goto bad2;
+       }
+       if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to setup xmit queue for VI traffic\n",
+                       __func__);
+               error = -EIO;
+               goto bad2;
+       }
+       if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                       "%s: unable to setup xmit queue for VO traffic\n",
+                       __func__);
+               error = -EIO;
+               goto bad2;
+       }
+
+       sc->sc_rc = ath_rate_attach(ah);
+       if (sc->sc_rc == NULL) {
+               error = EIO;
+               goto bad2;
+       }
+
+       if (ath9k_hw_getcapability(ah, HAL_CAP_CIPHER,
+                                  ATH9K_CIPHER_TKIP, NULL)) {
+               /*
+                * Whether we should enable h/w TKIP MIC.
+                * XXX: if we don't support WME TKIP MIC, then we wouldn't
+                * report WMM capable, so it's always safe to turn on
+                * TKIP MIC in this case.
+                */
+               ath9k_hw_setcapability(sc->sc_ah, HAL_CAP_TKIP_MIC, 0, 1, NULL);
+       }
+
+       /*
+        * Check whether the separate key cache entries
+        * are required to handle both tx+rx MIC keys.
+        * With split mic keys the number of stations is limited
+        * to 27 otherwise 59.
+        */
+       if (ath9k_hw_getcapability(ah, HAL_CAP_CIPHER,
+                                  ATH9K_CIPHER_TKIP, NULL)
+           && ath9k_hw_getcapability(ah, HAL_CAP_CIPHER,
+                                     ATH9K_CIPHER_MIC, NULL)
+           && ath9k_hw_getcapability(ah, HAL_CAP_TKIP_SPLIT,
+                                     0, NULL))
+               sc->sc_splitmic = 1;
+
+       /* turn on mcast key search if possible */
+       if (!ath9k_hw_getcapability(ah, HAL_CAP_MCAST_KEYSRCH, 0, NULL))
+               (void)ath9k_hw_setcapability(ah, HAL_CAP_MCAST_KEYSRCH, 1,
+                                            1, NULL);
+
+       sc->sc_config.txpowlimit = ATH_TXPOWER_MAX;
+       sc->sc_config.txpowlimit_override = 0;
+
+       /* 11n Capabilities */
+       if (ah->ah_caps.halHTSupport) {
+               sc->sc_txaggr = 1;
+               sc->sc_rxaggr = 1;
+       }
+
+       sc->sc_tx_chainmask = ah->ah_caps.halTxChainMask;
+       sc->sc_rx_chainmask = ah->ah_caps.halRxChainMask;
+
+       /* Configuration for rx chain detection */
+       sc->sc_rxchaindetect_ref = 0;
+       sc->sc_rxchaindetect_thresh5GHz = 35;
+       sc->sc_rxchaindetect_thresh2GHz = 35;
+       sc->sc_rxchaindetect_delta5GHz = 30;
+       sc->sc_rxchaindetect_delta2GHz = 30;
+
+       ath9k_hw_setcapability(ah, HAL_CAP_DIVERSITY, 1, true, NULL);
+       sc->sc_defant = ath9k_hw_getdefantenna(ah);
+
+       ath9k_hw_getmac(ah, sc->sc_myaddr);
+       if (ah->ah_caps.halBssIdMaskSupport) {
+               ath9k_hw_getbssidmask(ah, sc->sc_bssidmask);
+               ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask);
+               ath9k_hw_setbssidmask(ah, sc->sc_bssidmask);
+       }
+       sc->sc_slottime = ATH9K_SLOT_TIME_9;    /* default to short slot time */
+
+       /* initialize beacon slots */
+       for (i = 0; i < ARRAY_SIZE(sc->sc_bslot); i++)
+               sc->sc_bslot[i] = ATH_IF_ID_ANY;
+
+       /* save MISC configurations */
+       sc->sc_config.swBeaconProcess = 1;
+
+#ifdef CONFIG_SLOW_ANT_DIV
+       /* range is 40 - 255, we use something in the middle */
+       ath_slow_ant_div_init(&sc->sc_antdiv, sc, 0x127);
+#endif
+
+       return 0;
+bad2:
+       /* cleanup tx queues */
+       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
+               if (ATH_TXQ_SETUP(sc, i))
+                       ath_tx_cleanupq(sc, &sc->sc_txq[i]);
+bad:
+       if (ah)
+               ath9k_hw_detach(ah);
+       return error;
+}
+
+void ath_deinit(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       int i;
+
+       DPRINTF(sc, ATH_DBG_CONFIG, "%s\n", __func__);
+
+       ath_stop(sc);
+       if (!sc->sc_invalid)
+               ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
+       ath_rate_detach(sc->sc_rc);
+       /* cleanup tx queues */
+       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
+               if (ATH_TXQ_SETUP(sc, i))
+                       ath_tx_cleanupq(sc, &sc->sc_txq[i]);
+       ath9k_hw_detach(ah);
+}
+
+/*******************/
+/* Node Management */
+/*******************/
+
+struct ath_node *ath_node_attach(struct ath_softc *sc, u8 *addr, int if_id)
+{
+       struct ath_vap *avp;
+       struct ath_node *an;
+       DECLARE_MAC_BUF(mac);
+
+       avp = sc->sc_vaps[if_id];
+       ASSERT(avp != NULL);
+
+       /* mac80211 sta_notify callback is from an IRQ context, so no sleep */
+       an = kmalloc(sizeof(struct ath_node), GFP_ATOMIC);
+       if (an == NULL)
+               return NULL;
+       memzero(an, sizeof(*an));
+
+       an->an_sc = sc;
+       memcpy(an->an_addr, addr, ETH_ALEN);
+       atomic_set(&an->an_refcnt, 1);
+
+       /* set up per-node tx/rx state */
+       ath_tx_node_init(sc, an);
+       ath_rx_node_init(sc, an);
+
+       ath_chainmask_sel_init(sc, an);
+       ath_chainmask_sel_timerstart(&an->an_chainmask_sel);
+       list_add(&an->list, &sc->node_list);
+
+       return an;
+}
+
+void ath_node_detach(struct ath_softc *sc, struct ath_node *an, bool bh_flag)
+{
+       unsigned long flags;
+
+       DECLARE_MAC_BUF(mac);
+
+       ath_chainmask_sel_timerstop(&an->an_chainmask_sel);
+       an->an_flags |= ATH_NODE_CLEAN;
+       ath_tx_node_cleanup(sc, an, bh_flag);
+       ath_rx_node_cleanup(sc, an);
+
+       ath_tx_node_free(sc, an);
+       ath_rx_node_free(sc, an);
+
+       spin_lock_irqsave(&sc->node_lock, flags);
+
+       list_del(&an->list);
+
+       spin_unlock_irqrestore(&sc->node_lock, flags);
+
+       kfree(an);
+}
+
+/* Finds a node and increases the refcnt if found */
+
+struct ath_node *ath_node_get(struct ath_softc *sc, u8 *addr)
+{
+       struct ath_node *an = NULL, *an_found = NULL;
+
+       if (list_empty(&sc->node_list)) /* FIXME */
+               goto out;
+       list_for_each_entry(an, &sc->node_list, list) {
+               if (!compare_ether_addr(an->an_addr, addr)) {
+                       atomic_inc(&an->an_refcnt);
+                       an_found = an;
+                       break;
+               }
+       }
+out:
+       return an_found;
+}
+
+/* Decrements the refcnt and if it drops to zero, detach the node */
+
+void ath_node_put(struct ath_softc *sc, struct ath_node *an, bool bh_flag)
+{
+       if (atomic_dec_and_test(&an->an_refcnt))
+               ath_node_detach(sc, an, bh_flag);
+}
+
+/* Finds a node, doesn't increment refcnt. Caller must hold sc->node_lock */
+struct ath_node *ath_node_find(struct ath_softc *sc, u8 *addr)
+{
+       struct ath_node *an = NULL, *an_found = NULL;
+
+       if (list_empty(&sc->node_list))
+               return NULL;
+
+       list_for_each_entry(an, &sc->node_list, list)
+               if (!compare_ether_addr(an->an_addr, addr)) {
+                       an_found = an;
+                       break;
+               }
+
+       return an_found;
+}
+
+/*
+ * Set up New Node
+ *
+ * Setup driver-specific state for a newly associated node.  This routine
+ * really only applies if compression or XR are enabled, there is no code
+ * covering any other cases.
+*/
+
+void ath_newassoc(struct ath_softc *sc,
+       struct ath_node *an, int isnew, int isuapsd)
+{
+       int tidno;
+
+       /* if station reassociates, tear down the aggregation state. */
+       if (!isnew) {
+               for (tidno = 0; tidno < WME_NUM_TID; tidno++) {
+                       if (sc->sc_txaggr)
+                               ath_tx_aggr_teardown(sc, an, tidno);
+                       if (sc->sc_rxaggr)
+                               ath_rx_aggr_teardown(sc, an, tidno);
+               }
+       }
+       an->an_flags = 0;
+}
+
+/**************/
+/* Encryption */
+/**************/
+
+void ath_key_reset(struct ath_softc *sc, u16 keyix, int freeslot)
+{
+       ath9k_hw_keyreset(sc->sc_ah, keyix);
+       if (freeslot)
+               clear_bit(keyix, sc->sc_keymap);
+}
+
+int ath_keyset(struct ath_softc *sc,
+              u16 keyix,
+              struct ath9k_keyval *hk,
+              const u8 mac[ETH_ALEN])
+{
+       bool status;
+
+       status = ath9k_hw_set_keycache_entry(sc->sc_ah,
+               keyix, hk, mac, false);
+
+       return status != false;
+}
+
+/***********************/
+/* TX Power/Regulatory */
+/***********************/
+
+/*
+ *  Set Transmit power in HAL
+ *
+ *  This routine makes the actual HAL calls to set the new transmit power
+ *  limit.
+*/
+
+void ath_update_txpow(struct ath_softc *sc)
+{
+       struct ath_hal *ah = sc->sc_ah;
+       u32 txpow;
+
+       if (sc->sc_curtxpow != sc->sc_config.txpowlimit) {
+               ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit);
+               /* read back in case value is clamped */
+               ath9k_hw_getcapability(ah, HAL_CAP_TXPOW, 1, &txpow);
+               sc->sc_curtxpow = txpow;
+       }
+}
+
+/* Return the current country and domain information */
+void ath_get_currentCountry(struct ath_softc *sc,
+       struct ath9k_country_entry *ctry)
+{
+       ath9k_regd_get_current_country(sc->sc_ah, ctry);
+
+       /* If HAL not specific yet, since it is band dependent,
+        * use the one we passed in. */
+       if (ctry->countryCode == CTRY_DEFAULT) {
+               ctry->iso[0] = 0;
+               ctry->iso[1] = 0;
+       } else if (ctry->iso[0] && ctry->iso[1]) {
+               if (!ctry->iso[2]) {
+                       if (ath_outdoor)
+                               ctry->iso[2] = 'O';
+                       else
+                               ctry->iso[2] = 'I';
+               }
+       }
+}
+
+/**************************/
+/* Slow Antenna Diversity */
+/**************************/
+
+void ath_slow_ant_div_init(struct ath_antdiv *antdiv,
+                          struct ath_softc *sc,
+                          int32_t rssitrig)
+{
+       int trig;
+
+       /* antdivf_rssitrig can range from 40 - 0xff */
+       trig = (rssitrig > 0xff) ? 0xff : rssitrig;
+       trig = (rssitrig < 40) ? 40 : rssitrig;
+
+       antdiv->antdiv_sc = sc;
+       antdiv->antdivf_rssitrig = trig;
+}
+
+void ath_slow_ant_div_start(struct ath_antdiv *antdiv,
+                           u8 num_antcfg,
+                           const u8 *bssid)
+{
+       antdiv->antdiv_num_antcfg =
+               num_antcfg < ATH_ANT_DIV_MAX_CFG ?
+               num_antcfg : ATH_ANT_DIV_MAX_CFG;
+       antdiv->antdiv_state = ATH_ANT_DIV_IDLE;
+       antdiv->antdiv_curcfg = 0;
+       antdiv->antdiv_bestcfg = 0;
+       antdiv->antdiv_laststatetsf = 0;
+
+       memcpy(antdiv->antdiv_bssid, bssid, sizeof(antdiv->antdiv_bssid));
+
+       antdiv->antdiv_start = 1;
+}
+
+void ath_slow_ant_div_stop(struct ath_antdiv *antdiv)
+{
+       antdiv->antdiv_start = 0;
+}
+
+static int32_t ath_find_max_val(int32_t *val,
+       u8 num_val, u8 *max_index)
+{
+       u32 MaxVal = *val++;
+       u32 cur_index = 0;
+
+       *max_index = 0;
+       while (++cur_index < num_val) {
+               if (*val > MaxVal) {
+                       MaxVal = *val;
+                       *max_index = cur_index;
+               }
+
+               val++;
+       }
+
+       return MaxVal;
+}
+
+void ath_slow_ant_div(struct ath_antdiv *antdiv,
+                     struct ieee80211_hdr *hdr,
+                     struct ath_rx_status *rx_stats)
+{
+       struct ath_softc *sc = antdiv->antdiv_sc;
+       struct ath_hal *ah = sc->sc_ah;
+       u64 curtsf = 0;
+       u8 bestcfg, curcfg = antdiv->antdiv_curcfg;
+       __le16 fc = hdr->frame_control;
+
+       if (antdiv->antdiv_start && ieee80211_is_beacon(fc)
+           && !compare_ether_addr(hdr->addr3, antdiv->antdiv_bssid)) {
+               antdiv->antdiv_lastbrssi[curcfg] = rx_stats->rs_rssi;
+               antdiv->antdiv_lastbtsf[curcfg] = ath9k_hw_gettsf64(sc->sc_ah);
+               curtsf = antdiv->antdiv_lastbtsf[curcfg];
+       } else {
+               return;
+       }
+
+       switch (antdiv->antdiv_state) {
+       case ATH_ANT_DIV_IDLE:
+               if ((antdiv->antdiv_lastbrssi[curcfg] <
+                    antdiv->antdivf_rssitrig)
+                   && ((curtsf - antdiv->antdiv_laststatetsf) >
+                       ATH_ANT_DIV_MIN_IDLE_US)) {
+
+                       curcfg++;
+                       if (curcfg == antdiv->antdiv_num_antcfg)
+                               curcfg = 0;
+
+                       if (!ath9k_hw_select_antconfig(ah, curcfg)) {
+                               antdiv->antdiv_bestcfg = antdiv->antdiv_curcfg;
+                               antdiv->antdiv_curcfg = curcfg;
+                               antdiv->antdiv_laststatetsf = curtsf;
+                               antdiv->antdiv_state = ATH_ANT_DIV_SCAN;
+                       }
+               }
+               break;
+
+       case ATH_ANT_DIV_SCAN:
+               if ((curtsf - antdiv->antdiv_laststatetsf) <
+                   ATH_ANT_DIV_MIN_SCAN_US)
+                       break;
+
+               curcfg++;
+               if (curcfg == antdiv->antdiv_num_antcfg)
+                       curcfg = 0;
+
+               if (curcfg == antdiv->antdiv_bestcfg) {
+                       ath_find_max_val(antdiv->antdiv_lastbrssi,
+                                  antdiv->antdiv_num_antcfg, &bestcfg);
+                       if (!ath9k_hw_select_antconfig(ah, bestcfg)) {
+                               antdiv->antdiv_bestcfg = bestcfg;
+                               antdiv->antdiv_curcfg = bestcfg;
+                               antdiv->antdiv_laststatetsf = curtsf;
+                               antdiv->antdiv_state = ATH_ANT_DIV_IDLE;
+                       }
+               } else {
+                       if (!ath9k_hw_select_antconfig(ah, curcfg)) {
+                               antdiv->antdiv_curcfg = curcfg;
+                               antdiv->antdiv_laststatetsf = curtsf;
+                               antdiv->antdiv_state = ATH_ANT_DIV_SCAN;
+                       }
+               }
+
+               break;
+       }
+}
+
+/***********************/
+/* Descriptor Handling */
+/***********************/
+
+/*
+ *  Set up DMA descriptors
+ *
+ *  This function will allocate both the DMA descriptor structure, and the
+ *  buffers it contains.  These are used to contain the descriptors used
+ *  by the system.
+*/
+
+int ath_descdma_setup(struct ath_softc *sc,
+                     struct ath_descdma *dd,
+                     struct list_head *head,
+                     const char *name,
+                     int nbuf,
+                     int ndesc)
+{
+#define        DS2PHYS(_dd, _ds)                                               \
+       ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
+#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
+#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096)
+
+       struct ath_desc *ds;
+       struct ath_buf *bf;
+       int i, bsize, error;
+
+       DPRINTF(sc, ATH_DBG_CONFIG, "%s: %s DMA: %u buffers %u desc/buf\n",
+               __func__, name, nbuf, ndesc);
+
+       /* ath_desc must be a multiple of DWORDs */
+       if ((sizeof(struct ath_desc) % 4) != 0) {
+               DPRINTF(sc, ATH_DBG_FATAL, "%s: ath_desc not DWORD aligned\n",
+                       __func__);
+               ASSERT((sizeof(struct ath_desc) % 4) == 0);
+               error = -ENOMEM;
+               goto fail;
+       }
+
+       dd->dd_name = name;
+       dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc;
+
+       /*
+        * Need additional DMA memory because we can't use
+        * descriptors that cross the 4K page boundary. Assume
+        * one skipped descriptor per 4K page.
+        */
+       if (!(sc->sc_ah->ah_caps.hal4kbSplitTransSupport)) {
+               u32 ndesc_skipped =
+                       ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len);
+               u32 dma_len;
+
+               while (ndesc_skipped) {
+                       dma_len = ndesc_skipped * sizeof(struct ath_desc);
+                       dd->dd_desc_len += dma_len;
+
+                       ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len);
+               };
+       }
+
+       /* allocate descriptors */
+       dd->dd_desc = pci_alloc_consistent(sc->pdev,
+                             dd->dd_desc_len,
+                             &dd->dd_desc_paddr);
+       if (dd->dd_desc == NULL) {
+               error = -ENOMEM;
+               goto fail;
+       }
+       ds = dd->dd_desc;
+       DPRINTF(sc, ATH_DBG_CONFIG, "%s: %s DMA map: %p (%u) -> %llx (%u)\n",
+               __func__, dd->dd_name, ds, (u32) dd->dd_desc_len,
+               ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
+
+       /* allocate buffers */
+       bsize = sizeof(struct ath_buf) * nbuf;
+       bf = kmalloc(bsize, GFP_KERNEL);
+       if (bf == NULL) {
+               error = -ENOMEM;
+               goto fail2;
+       }
+       memzero(bf, bsize);
+       dd->dd_bufptr = bf;
+
+       INIT_LIST_HEAD(head);
+       for (i = 0; i < nbuf; i++, bf++, ds += ndesc) {
+               bf->bf_desc = ds;
+               bf->bf_daddr = DS2PHYS(dd, ds);
+
+               if (!(sc->sc_ah->ah_caps.hal4kbSplitTransSupport)) {
+                       /*
+                        * Skip descriptor addresses which can cause 4KB
+                        * boundary crossing (addr + length) with a 32 dword
+                        * descriptor fetch.
+                        */
+                       while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
+                               ASSERT((caddr_t) bf->bf_desc <
+                                      ((caddr_t) dd->dd_desc +
+                                       dd->dd_desc_len));
+
+                               ds += ndesc;
+                               bf->bf_desc = ds;
+                               bf->bf_daddr = DS2PHYS(dd, ds);
+                       }
+               }
+               list_add_tail(&bf->list, head);
+       }
+       return 0;
+fail2:
+       pci_free_consistent(sc->pdev,
+               dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr);
+fail:
+       memzero(dd, sizeof(*dd));
+       return error;
+#undef ATH_DESC_4KB_BOUND_CHECK
+#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED
+#undef DS2PHYS
+}
+
+/*
+ *  Cleanup DMA descriptors
+ *
+ *  This function will free the DMA block that was allocated for the descriptor
+ *  pool.  Since this was allocated as one "chunk", it is freed in the same
+ *  manner.
+*/
+
+void ath_descdma_cleanup(struct ath_softc *sc,
+                        struct ath_descdma *dd,
+                        struct list_head *head)
+{
+       /* Free memory associated with descriptors */
+       pci_free_consistent(sc->pdev,
+               dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr);
+
+       INIT_LIST_HEAD(head);
+       kfree(dd->dd_bufptr);
+       memzero(dd, sizeof(*dd));
+}
+
+/*************/
+/* Utilities */
+/*************/
+
+void ath_internal_reset(struct ath_softc *sc)
+{
+       ath_reset_start(sc, 0);
+       ath_reset(sc);
+       ath_reset_end(sc, 0);
+}
+
+int ath_get_hal_qnum(u16 queue, struct ath_softc *sc)
+{
+       int qnum;
+
+       switch (queue) {
+       case 0:
+               qnum = sc->sc_haltype2q[ATH9K_WME_AC_VO];
+               break;
+       case 1:
+               qnum = sc->sc_haltype2q[ATH9K_WME_AC_VI];
+               break;
+       case 2:
+               qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE];
+               break;
+       case 3:
+               qnum = sc->sc_haltype2q[ATH9K_WME_AC_BK];
+               break;
+       default:
+               qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE];
+               break;
+       }
+
+       return qnum;
+}
+
+int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc)
+{
+       int qnum;
+
+       switch (queue) {
+       case ATH9K_WME_AC_VO:
+               qnum = 0;
+               break;
+       case ATH9K_WME_AC_VI:
+               qnum = 1;
+               break;
+       case ATH9K_WME_AC_BE:
+               qnum = 2;
+               break;
+       case ATH9K_WME_AC_BK:
+               qnum = 3;
+               break;
+       default:
+               qnum = -1;
+               break;
+       }
+
+       return qnum;
+}
+
+
+/*
+ *  Expand time stamp to TSF
+ *
+ *  Extend 15-bit time stamp from rx descriptor to
+ *  a full 64-bit TSF using the current h/w TSF.
+*/
+
+u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp)
+{
+       u64 tsf;
+
+       tsf = ath9k_hw_gettsf64(sc->sc_ah);
+       if ((tsf & 0x7fff) < rstamp)
+               tsf -= 0x8000;
+       return (tsf & ~0x7fff) | rstamp;
+}
+
+/*
+ *  Set Default Antenna
+ *
+ *  Call into the HAL to set the default antenna to use.  Not really valid for
+ *  MIMO technology.
+*/
+
+void ath_setdefantenna(void *context, u32 antenna)
+{
+       struct ath_softc *sc = (struct ath_softc *)context;
+       struct ath_hal *ah = sc->sc_ah;
+
+       /* XXX block beacon interrupts */
+       ath9k_hw_setantenna(ah, antenna);
+       sc->sc_defant = antenna;
+       sc->sc_rxotherant = 0;
+}
+
+/*
+ * Set Slot Time
+ *
+ * This will wake up the chip if required, and set the slot time for the
+ * frame (maximum transmit time).  Slot time is assumed to be already set
+ * in the ATH object member sc_slottime
+*/
+
+void ath_setslottime(struct ath_softc *sc)
+{
+       ath9k_hw_setslottime(sc->sc_ah, sc->sc_slottime);
+       sc->sc_updateslot = OK;
+}
diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h
new file mode 100644 (file)
index 0000000..cf76b36
--- /dev/null
@@ -0,0 +1,1097 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef CORE_H
+#define CORE_H
+
+#include <linux/version.h>
+#include <linux/autoconf.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <asm/byteorder.h>
+#include <linux/scatterlist.h>
+#include <asm/page.h>
+#include <net/mac80211.h>
+
+#include "ath9k.h"
+#include "rc.h"
+
+struct ath_node;
+
+/******************/
+/* Utility macros */
+/******************/
+
+/* Macro to expand scalars to 64-bit objects */
+
+#define        ito64(x) (sizeof(x) == 8) ? \
+       (((unsigned long long int)(x)) & (0xff)) : \
+       (sizeof(x) == 16) ? \
+       (((unsigned long long int)(x)) & 0xffff) : \
+       ((sizeof(x) == 32) ?                    \
+        (((unsigned long long int)(x)) & 0xffffffff) : \
+       (unsigned long long int)(x))
+
+/* increment with wrap-around */
+#define INCR(_l, _sz)   do { \
+       (_l)++; \
+       (_l) &= ((_sz) - 1); \
+       } while (0)
+
+/* decrement with wrap-around */
+#define DECR(_l,  _sz)  do { \
+       (_l)--; \
+       (_l) &= ((_sz) - 1); \
+       } while (0)
+
+#define A_MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define ASSERT(exp) do {                       \
+               if (unlikely(!(exp))) {         \
+                       BUG();                  \
+               }                               \
+       } while (0)
+
+/* XXX: remove */
+#define memzero(_buf, _len) memset(_buf, 0, _len)
+
+#define get_dma_mem_context(var, field) (&((var)->field))
+#define copy_dma_mem_context(dst, src)  (*dst = *src)
+
+#define ATH9K_BH_STATUS_INTACT         0
+#define ATH9K_BH_STATUS_CHANGE         1
+
+#define        ATH_TXQ_SETUP(sc, i)        ((sc)->sc_txqsetup & (1<<i))
+
+static inline unsigned long get_timestamp(void)
+{
+       return ((jiffies / HZ) * 1000) + (jiffies % HZ) * (1000 / HZ);
+}
+
+/*************/
+/* Debugging */
+/*************/
+
+enum ATH_DEBUG {
+       ATH_DBG_RESET           = 0x00000001,
+       ATH_DBG_PHY_IO          = 0x00000002,
+       ATH_DBG_REG_IO          = 0x00000004,
+       ATH_DBG_QUEUE           = 0x00000008,
+       ATH_DBG_EEPROM          = 0x00000010,
+       ATH_DBG_NF_CAL          = 0x00000020,
+       ATH_DBG_CALIBRATE       = 0x00000040,
+       ATH_DBG_CHANNEL         = 0x00000080,
+       ATH_DBG_INTERRUPT       = 0x00000100,
+       ATH_DBG_REGULATORY      = 0x00000200,
+       ATH_DBG_ANI             = 0x00000400,
+       ATH_DBG_POWER_MGMT      = 0x00000800,
+       ATH_DBG_XMIT            = 0x00001000,
+       ATH_DBG_BEACON          = 0x00002000,
+       ATH_DBG_RATE            = 0x00004000,
+       ATH_DBG_CONFIG          = 0x00008000,
+       ATH_DBG_KEYCACHE        = 0x00010000,
+       ATH_DBG_AGGR            = 0x00020000,
+       ATH_DBG_FATAL           = 0x00040000,
+       ATH_DBG_ANY             = 0xffffffff
+};
+
+#define DBG_DEFAULT (ATH_DBG_FATAL)
+
+#define        DPRINTF(sc, _m, _fmt, ...) do {                 \
+               if (sc->sc_debug & (_m))                \
+                       printk(_fmt , ##__VA_ARGS__);   \
+       } while (0)
+
+/***************************/
+/* Load-time Configuration */
+/***************************/
+
+/* Per-instance load-time (note: NOT run-time) configurations
+ * for Atheros Device */
+struct ath_config {
+       u32   ath_aggr_prot;
+       u16   txpowlimit;
+       u16   txpowlimit_override;
+       u8    cabqReadytime; /* Cabq Readytime % */
+       u8    swBeaconProcess; /* Process received beacons
+                                       in SW (vs HW) */
+};
+
+/***********************/
+/* Chainmask Selection */
+/***********************/
+
+#define ATH_CHAINMASK_SEL_TIMEOUT         6000
+/* Default - Number of last RSSI values that is used for
+ * chainmask selection */
+#define ATH_CHAINMASK_SEL_RSSI_CNT        10
+/* Means use 3x3 chainmask instead of configured chainmask */
+#define ATH_CHAINMASK_SEL_3X3             7
+/* Default - Rssi threshold below which we have to switch to 3x3 */
+#define ATH_CHAINMASK_SEL_UP_RSSI_THRES           20
+/* Default - Rssi threshold above which we have to switch to
+ * user configured values */
+#define ATH_CHAINMASK_SEL_DOWN_RSSI_THRES  35
+/* Struct to store the chainmask select related info */
+struct ath_chainmask_sel {
+       struct timer_list   timer;
+       int                 cur_tx_mask;        /* user configured or 3x3 */
+       int                 cur_rx_mask;        /* user configured or 3x3 */
+       int                 tx_avgrssi;
+       u8                  switch_allowed:1,   /* timer will set this */
+                           cm_sel_enabled:1;
+};
+
+int ath_chainmask_sel_logic(struct ath_softc *sc, struct ath_node *an);
+void ath_update_chainmask(struct ath_softc *sc, int is_ht);
+
+/*************************/
+/* Descriptor Management */
+/*************************/
+
+/* Number of descriptors per buffer. The only case where we see skbuff
+chains is due to FF aggregation in the driver. */
+#define        ATH_TXDESC          1
+/* if there's more fragment for this MSDU */
+#define ATH_BF_MORE_MPDU    1
+#define ATH_TXBUF_RESET(_bf) do {                              \
+               (_bf)->bf_status = 0;                           \
+               (_bf)->bf_lastbf = NULL;                        \
+               (_bf)->bf_lastfrm = NULL;                       \
+               (_bf)->bf_next = NULL;                          \
+               memzero(&((_bf)->bf_state),                     \
+                           sizeof(struct ath_buf_state));      \
+       } while (0)
+
+struct ath_buf_state {
+       int bfs_nframes;        /* # frames in aggregate */
+       u16 bfs_al;     /* length of aggregate */
+       u16 bfs_frmlen; /* length of frame */
+       int bfs_seqno;          /* sequence number */
+       int bfs_tidno;          /* tid of this frame */
+       int bfs_retries;        /* current retries */
+       struct ath_rc_series bfs_rcs[4];        /* rate series */
+       u8 bfs_isdata:1;        /* is a data frame/aggregate */
+       u8 bfs_isaggr:1;        /* is an aggregate */
+       u8 bfs_isampdu:1;       /* is an a-mpdu, aggregate or not */
+       u8 bfs_ht:1;            /* is an HT frame */
+       u8 bfs_isretried:1;     /* is retried */
+       u8 bfs_isxretried:1;    /* is excessive retried */
+       u8 bfs_shpreamble:1;    /* is short preamble */
+       u8 bfs_isbar:1; /* is a BAR */
+       u8 bfs_ispspoll:1;      /* is a PS-Poll */
+       u8 bfs_aggrburst:1;     /* is a aggr burst */
+       u8 bfs_calcairtime:1;   /* requests airtime be calculated
+                               when set for tx frame */
+       int bfs_rifsburst_elem; /* RIFS burst/bar */
+       int bfs_nrifsubframes;  /* # of elements in burst */
+       /* key type use to encrypt this frame */
+       enum ath9k_key_type bfs_keytype;
+};
+
+#define bf_nframes             bf_state.bfs_nframes
+#define bf_al                  bf_state.bfs_al
+#define bf_frmlen              bf_state.bfs_frmlen
+#define bf_retries             bf_state.bfs_retries
+#define bf_seqno               bf_state.bfs_seqno
+#define bf_tidno               bf_state.bfs_tidno
+#define bf_rcs                 bf_state.bfs_rcs
+#define bf_isdata              bf_state.bfs_isdata
+#define bf_isaggr              bf_state.bfs_isaggr
+#define bf_isampdu             bf_state.bfs_isampdu
+#define bf_ht                  bf_state.bfs_ht
+#define bf_isretried           bf_state.bfs_isretried
+#define bf_isxretried          bf_state.bfs_isxretried
+#define bf_shpreamble          bf_state.bfs_shpreamble
+#define bf_rifsburst_elem      bf_state.bfs_rifsburst_elem
+#define bf_nrifsubframes       bf_state.bfs_nrifsubframes
+#define bf_keytype             bf_state.bfs_keytype
+#define bf_isbar               bf_state.bfs_isbar
+#define bf_ispspoll            bf_state.bfs_ispspoll
+#define bf_aggrburst           bf_state.bfs_aggrburst
+#define bf_calcairtime         bf_state.bfs_calcairtime
+
+/*
+ * Abstraction of a contiguous buffer to transmit/receive.  There is only
+ * a single hw descriptor encapsulated here.
+ */
+
+struct ath_buf {
+       struct list_head list;
+       struct list_head *last;
+       struct ath_buf *bf_lastbf;      /* last buf of this unit (a frame or
+                                       an aggregate) */
+       struct ath_buf *bf_lastfrm;     /* last buf of this frame */
+       struct ath_buf *bf_next;        /* next subframe in the aggregate */
+       struct ath_buf *bf_rifslast;    /* last buf for RIFS burst */
+       void *bf_mpdu;                  /* enclosing frame structure */
+       void *bf_node;                  /* pointer to the node */
+       struct ath_desc *bf_desc;       /* virtual addr of desc */
+       dma_addr_t bf_daddr;            /* physical addr of desc */
+       dma_addr_t bf_buf_addr;         /* physical addr of data buffer */
+       u32 bf_status;
+       u16 bf_flags;           /* tx descriptor flags */
+       struct ath_buf_state bf_state;  /* buffer state */
+       dma_addr_t bf_dmacontext;
+};
+
+/*
+ * reset the rx buffer.
+ * any new fields added to the athbuf and require
+ * reset need to be added to this macro.
+ * currently bf_status is the only one requires that
+ * requires reset.
+ */
+#define ATH_RXBUF_RESET(_bf)    ((_bf)->bf_status = 0)
+
+/* hw processing complete, desc processed by hal */
+#define ATH_BUFSTATUS_DONE      0x00000001
+/* hw processing complete, desc hold for hw */
+#define ATH_BUFSTATUS_STALE     0x00000002
+/* Rx-only: OS is done with this packet and it's ok to queued it to hw */
+#define ATH_BUFSTATUS_FREE      0x00000004
+
+/* DMA state for tx/rx descriptors */
+
+struct ath_descdma {
+       const char *dd_name;
+       struct ath_desc *dd_desc;       /* descriptors  */
+       dma_addr_t dd_desc_paddr;       /* physical addr of dd_desc  */
+       u32 dd_desc_len;                /* size of dd_desc  */
+       struct ath_buf *dd_bufptr;      /* associated buffers */
+       dma_addr_t dd_dmacontext;
+};
+
+/* Abstraction of a received RX MPDU/MMPDU, or a RX fragment */
+
+struct ath_rx_context {
+       struct ath_buf *ctx_rxbuf;      /* associated ath_buf for rx */
+};
+#define ATH_RX_CONTEXT(skb) ((struct ath_rx_context *)skb->cb)
+
+int ath_descdma_setup(struct ath_softc *sc,
+                     struct ath_descdma *dd,
+                     struct list_head *head,
+                     const char *name,
+                     int nbuf,
+                     int ndesc);
+int ath_desc_alloc(struct ath_softc *sc);
+void ath_desc_free(struct ath_softc *sc);
+void ath_descdma_cleanup(struct ath_softc *sc,
+                        struct ath_descdma *dd,
+                        struct list_head *head);
+
+/******/
+/* RX */
+/******/
+
+#define ATH_MAX_ANTENNA          3
+#define ATH_RXBUF                512
+#define ATH_RX_TIMEOUT           40      /* 40 milliseconds */
+#define WME_NUM_TID              16
+#define IEEE80211_BAR_CTL_TID_M  0xF000  /* tid mask */
+#define IEEE80211_BAR_CTL_TID_S  2       /* tid shift */
+
+enum ATH_RX_TYPE {
+       ATH_RX_NON_CONSUMED = 0,
+       ATH_RX_CONSUMED
+};
+
+/* per frame rx status block */
+struct ath_recv_status {
+       u64 tsf;                /* mac tsf */
+       int8_t rssi;            /* RSSI (noise floor ajusted) */
+       int8_t rssictl[ATH_MAX_ANTENNA];        /* RSSI (noise floor ajusted) */
+       int8_t rssiextn[ATH_MAX_ANTENNA];       /* RSSI (noise floor ajusted) */
+       int8_t abs_rssi;        /* absolute RSSI */
+       u8 rateieee;    /* data rate received (IEEE rate code) */
+       u8 ratecode;    /* phy rate code */
+       int rateKbps;           /* data rate received (Kbps) */
+       int antenna;            /* rx antenna */
+       int flags;              /* status of associated skb */
+#define ATH_RX_FCS_ERROR        0x01
+#define ATH_RX_MIC_ERROR        0x02
+#define ATH_RX_DECRYPT_ERROR    0x04
+#define ATH_RX_RSSI_VALID       0x08
+/* if any of ctl,extn chainrssis are valid */
+#define ATH_RX_CHAIN_RSSI_VALID 0x10
+/* if extn chain rssis are valid */
+#define ATH_RX_RSSI_EXTN_VALID  0x20
+/* set if 40Mhz, clear if 20Mhz */
+#define ATH_RX_40MHZ            0x40
+/* set if short GI, clear if full GI */
+#define ATH_RX_SHORT_GI         0x80
+};
+
+struct ath_rxbuf {
+       struct sk_buff                  *rx_wbuf; /* buffer */
+       unsigned long                   rx_time; /* system time when received */
+       struct ath_recv_status          rx_status; /* cached rx status */
+};
+
+/* Per-TID aggregate receiver state for a node */
+struct ath_arx_tid {
+       struct ath_node     *an;        /* parent ath node */
+       struct ath_rxbuf    *rxbuf;     /* re-ordering buffer */
+       struct timer_list   timer;
+       spinlock_t          tidlock;    /* lock to protect this TID structure */
+       int                 baw_head;   /* seq_next at head */
+       int                 baw_tail;   /* tail of block-ack window */
+       int                 seq_reset;  /* need to reset start sequence */
+       int                 addba_exchangecomplete;
+       u16           seq_next;   /* next expected sequence */
+       u16           baw_size;   /* block-ack window size */
+};
+
+/* Per-node receiver aggregate state */
+struct ath_arx {
+       struct ath_arx_tid  tid[WME_NUM_TID];
+};
+
+int ath_startrecv(struct ath_softc *sc);
+bool ath_stoprecv(struct ath_softc *sc);
+void ath_flushrecv(struct ath_softc *sc);
+u32 ath_calcrxfilter(struct ath_softc *sc);
+void ath_rx_node_init(struct ath_softc *sc, struct ath_node *an);
+void ath_rx_node_free(struct ath_softc *sc, struct ath_node *an);
+void ath_rx_node_cleanup(struct ath_softc *sc, struct ath_node *an);
+void ath_handle_rx_intr(struct ath_softc *sc);
+int ath_rx_init(struct ath_softc *sc, int nbufs);
+void ath_rx_cleanup(struct ath_softc *sc);
+int ath_rx_tasklet(struct ath_softc *sc, int flush);
+int ath_rx_input(struct ath_softc *sc,
+                struct ath_node *node,
+                int is_ampdu,
+                struct sk_buff *skb,
+                struct ath_recv_status *rx_status,
+                enum ATH_RX_TYPE *status);
+int ath__rx_indicate(struct ath_softc *sc,
+                   struct sk_buff *skb,
+                   struct ath_recv_status *status,
+                   u16 keyix);
+int ath_rx_subframe(struct ath_node *an, struct sk_buff *skb,
+                   struct ath_recv_status *status);
+
+/******/
+/* TX */
+/******/
+
+#define ATH_FRAG_PER_MSDU       1
+#define ATH_TXBUF               (512/ATH_FRAG_PER_MSDU)
+/* max number of transmit attempts (tries) */
+#define ATH_TXMAXTRY            13
+/* max number of 11n transmit attempts (tries) */
+#define ATH_11N_TXMAXTRY        10
+/* max number of tries for management and control frames */
+#define ATH_MGT_TXMAXTRY        4
+#define WME_BA_BMP_SIZE         64
+#define WME_MAX_BA              WME_BA_BMP_SIZE
+#define ATH_TID_MAX_BUFS        (2 * WME_MAX_BA)
+#define TID_TO_WME_AC(_tid)                            \
+       ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \
+        (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \
+        (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \
+        WME_AC_VO)
+
+
+/* Wireless Multimedia Extension Defines */
+#define WME_AC_BE               0 /* best effort */
+#define WME_AC_BK               1 /* background */
+#define WME_AC_VI               2 /* video */
+#define WME_AC_VO               3 /* voice */
+#define WME_NUM_AC              4
+
+enum ATH_SM_PWRSAV{
+       ATH_SM_ENABLE,
+       ATH_SM_PWRSAV_STATIC,
+       ATH_SM_PWRSAV_DYNAMIC,
+};
+
+/*
+ * Data transmit queue state.  One of these exists for each
+ * hardware transmit queue.  Packets sent to us from above
+ * are assigned to queues based on their priority.  Not all
+ * devices support a complete set of hardware transmit queues.
+ * For those devices the array sc_ac2q will map multiple
+ * priorities to fewer hardware queues (typically all to one
+ * hardware queue).
+ */
+struct ath_txq {
+       u32                     axq_qnum;       /* hardware q number */
+       u32             *axq_link;      /* link ptr in last TX desc */
+       struct list_head        axq_q;          /* transmit queue */
+       spinlock_t              axq_lock;       /* lock on q and link */
+       unsigned long           axq_lockflags;  /* intr state when must cli */
+       u32                     axq_depth;      /* queue depth */
+       u8                axq_aggr_depth; /* aggregates queued */
+       u32             axq_totalqueued;/* total ever queued */
+       u32                     axq_intrcnt;    /* count to determine
+                                               if descriptor should generate
+                                               int on this txq. */
+       bool                    stopped;        /* Is mac80211 queue
+                                               stopped ? */
+       /* State for patching up CTS when bursting */
+       struct  ath_buf         *axq_linkbuf;   /* virtual addr of last buffer*/
+       struct  ath_desc        *axq_lastdsWithCTS;     /* first desc of the
+                                       last descriptor that contains CTS  */
+       struct  ath_desc        *axq_gatingds; /* final desc of the gating desc
+                               * that determines whether lastdsWithCTS has
+                               * been DMA'ed or not */
+       struct list_head        axq_acq;
+};
+
+/* per TID aggregate tx state for a destination */
+struct ath_atx_tid {
+       struct list_head        list;   /* round-robin tid entry */
+       struct list_head        buf_q;    /* pending buffers */
+       struct ath_node         *an;        /* parent node structure */
+       struct ath_atx_ac       *ac;        /* parent access category */
+       struct ath_buf          *tx_buf[ATH_TID_MAX_BUFS];/* active tx frames */
+       u16               seq_start;  /* starting seq of BA window */
+       u16               seq_next;   /* next seq to be used */
+       u16               baw_size;   /* BA window size */
+       int                     tidno;      /* TID number */
+       int                     baw_head;   /* first un-acked tx buffer */
+       int                     baw_tail;   /* next unused tx buffer slot */
+       int                     sched;      /* TID is scheduled */
+       int                     paused;     /* TID is paused */
+       int                     cleanup_inprogress; /* aggr of this TID is
+                                               being teared down */
+       u32               addba_exchangecomplete:1; /* ADDBA state */
+       int32_t                 addba_exchangeinprogress;
+       int                     addba_exchangeattempts;
+};
+
+/* per access-category aggregate tx state for a destination */
+struct ath_atx_ac {
+       int                     sched;      /* dest-ac is scheduled */
+       int                     qnum; /* H/W queue number associated
+                                       with this AC */
+       struct list_head        list;   /* round-robin txq entry */
+       struct list_head        tid_q;      /* queue of TIDs with buffers */
+};
+
+/* per dest tx state */
+struct ath_atx {
+       struct ath_atx_tid  tid[WME_NUM_TID];
+       struct ath_atx_ac   ac[WME_NUM_AC];
+};
+
+/* per-frame tx control block */
+struct ath_tx_control {
+       struct ath_node *an;    /* destination to sent to */
+       int if_id;              /* only valid for cab traffic */
+       int qnum;               /* h/w queue number */
+       u32 ht:1;             /* if it can be transmitted using HT */
+       u32 ps:1;             /* if one or more stations are in PS mode */
+       u32 use_minrate:1;      /* if this frame should transmitted using
+                               minimum rate */
+       enum ath9k_pkt_type atype;      /* Atheros packet type */
+       enum ath9k_key_type keytype;    /* key type */
+       u32 flags;              /* HAL flags */
+       u16 seqno;      /* sequence number */
+       u16 tidno;      /* tid number */
+       u16 txpower;    /* transmit power */
+       u16 frmlen;       /* frame length */
+       u32 keyix;        /* key index */
+       int min_rate;           /* minimum rate */
+       int mcast_rate;         /* multicast rate */
+       u16 nextfraglen;        /* next fragment length */
+       /* below is set only by ath_dev */
+       struct ath_softc *dev;  /* device handle */
+       dma_addr_t dmacontext;
+};
+
+/* per frame tx status block */
+struct ath_xmit_status {
+       int retries; /* number of retries to successufully
+                       transmit this frame */
+       int flags; /* status of transmit */
+#define ATH_TX_ERROR        0x01
+#define ATH_TX_XRETRY       0x02
+#define ATH_TX_BAR          0x04
+};
+
+struct ath_tx_stat {
+       int rssi;               /* RSSI (noise floor ajusted) */
+       int rssictl[ATH_MAX_ANTENNA];   /* RSSI (noise floor ajusted) */
+       int rssiextn[ATH_MAX_ANTENNA];  /* RSSI (noise floor ajusted) */
+       int rateieee;           /* data rate xmitted (IEEE rate code) */
+       int rateKbps;           /* data rate xmitted (Kbps) */
+       int ratecode;           /* phy rate code */
+       int flags;              /* validity flags */
+/* if any of ctl,extn chain rssis are valid */
+#define ATH_TX_CHAIN_RSSI_VALID 0x01
+/* if extn chain rssis are valid */
+#define ATH_TX_RSSI_EXTN_VALID  0x02
+       u32 airtime;    /* time on air per final tx rate */
+};
+
+struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype);
+void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq);
+int ath_tx_setup(struct ath_softc *sc, int haltype);
+void ath_draintxq(struct ath_softc *sc, bool retry_tx);
+void ath_tx_draintxq(struct ath_softc *sc,
+       struct ath_txq *txq, bool retry_tx);
+void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an);
+void ath_tx_node_cleanup(struct ath_softc *sc,
+       struct ath_node *an, bool bh_flag);
+void ath_tx_node_free(struct ath_softc *sc, struct ath_node *an);
+void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq);
+int ath_tx_init(struct ath_softc *sc, int nbufs);
+int ath_tx_cleanup(struct ath_softc *sc);
+int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype);
+int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_txq_info *q);
+int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb);
+void ath_tx_tasklet(struct ath_softc *sc);
+u32 ath_txq_depth(struct ath_softc *sc, int qnum);
+u32 ath_txq_aggr_depth(struct ath_softc *sc, int qnum);
+void ath_notify_txq_status(struct ath_softc *sc, u16 queue_depth);
+void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
+                    struct ath_xmit_status *tx_status, struct ath_node *an);
+
+/**********************/
+/* Node / Aggregation */
+/**********************/
+
+/* indicates the node is clened up */
+#define ATH_NODE_CLEAN          0x1
+/* indicates the node is 80211 power save */
+#define ATH_NODE_PWRSAVE        0x2
+
+#define ADDBA_TIMEOUT              200 /* 200 milliseconds */
+#define ADDBA_EXCHANGE_ATTEMPTS    10
+#define ATH_AGGR_DELIM_SZ          4   /* delimiter size   */
+#define ATH_AGGR_MINPLEN           256 /* in bytes, minimum packet length */
+/* number of delimiters for encryption padding */
+#define ATH_AGGR_ENCRYPTDELIM      10
+/* minimum h/w qdepth to be sustained to maximize aggregation */
+#define ATH_AGGR_MIN_QDEPTH        2
+#define ATH_AMPDU_SUBFRAME_DEFAULT 32
+#define IEEE80211_SEQ_SEQ_SHIFT    4
+#define IEEE80211_SEQ_MAX          4096
+#define IEEE80211_MIN_AMPDU_BUF    0x8
+
+/* return whether a bit at index _n in bitmap _bm is set
+ * _sz is the size of the bitmap  */
+#define ATH_BA_ISSET(_bm, _n)  (((_n) < (WME_BA_BMP_SIZE)) &&          \
+                               ((_bm)[(_n) >> 5] & (1 << ((_n) & 31))))
+
+/* return block-ack bitmap index given sequence and starting sequence */
+#define ATH_BA_INDEX(_st, _seq) (((_seq) - (_st)) & (IEEE80211_SEQ_MAX - 1))
+
+/* returns delimiter padding required given the packet length */
+#define ATH_AGGR_GET_NDELIM(_len)                                      \
+       (((((_len) + ATH_AGGR_DELIM_SZ) < ATH_AGGR_MINPLEN) ?           \
+         (ATH_AGGR_MINPLEN - (_len) - ATH_AGGR_DELIM_SZ) : 0) >> 2)
+
+#define BAW_WITHIN(_start, _bawsz, _seqno) \
+       ((((_seqno) - (_start)) & 4095) < (_bawsz))
+
+#define ATH_DS_BA_SEQ(_ds)               ((_ds)->ds_us.tx.ts_seqnum)
+#define ATH_DS_BA_BITMAP(_ds)            (&(_ds)->ds_us.tx.ba_low)
+#define ATH_DS_TX_BA(_ds)      ((_ds)->ds_us.tx.ts_flags & ATH9K_TX_BA)
+#define ATH_AN_2_TID(_an, _tidno)        (&(_an)->an_aggr.tx.tid[(_tidno)])
+
+enum ATH_AGGR_STATUS {
+       ATH_AGGR_DONE,
+       ATH_AGGR_BAW_CLOSED,
+       ATH_AGGR_LIMITED,
+       ATH_AGGR_SHORTPKT,
+       ATH_AGGR_8K_LIMITED,
+};
+
+enum ATH_AGGR_CHECK {
+       AGGR_NOT_REQUIRED,
+       AGGR_REQUIRED,
+       AGGR_CLEANUP_PROGRESS,
+       AGGR_EXCHANGE_PROGRESS,
+       AGGR_EXCHANGE_DONE
+};
+
+struct aggr_rifs_param {
+       int param_max_frames;
+       int param_max_len;
+       int param_rl;
+       int param_al;
+       struct ath_rc_series *param_rcs;
+};
+
+/* Per-node aggregation state */
+struct ath_node_aggr {
+       struct ath_atx      tx;         /* node transmit state */
+       struct ath_arx      rx;         /* node receive state */
+};
+
+/* driver-specific node state */
+struct ath_node {
+       struct list_head        list;
+       struct ath_softc        *an_sc;                 /* back pointer */
+       atomic_t                an_refcnt;
+       struct ath_chainmask_sel an_chainmask_sel;
+       struct ath_node_aggr    an_aggr; /* A-MPDU aggregation state */
+       u8              an_smmode; /* SM Power save mode */
+       u8              an_flags;
+       u8                      an_addr[ETH_ALEN];
+};
+
+void ath_tx_resume_tid(struct ath_softc *sc,
+       struct ath_atx_tid *tid);
+enum ATH_AGGR_CHECK ath_tx_aggr_check(struct ath_softc *sc,
+       struct ath_node *an, u8 tidno);
+void ath_tx_aggr_teardown(struct ath_softc *sc,
+       struct ath_node *an, u8 tidno);
+void ath_rx_aggr_teardown(struct ath_softc *sc,
+       struct ath_node *an, u8 tidno);
+int ath_rx_aggr_start(struct ath_softc *sc,
+                     const u8 *addr,
+                     u16 tid,
+                     u16 *ssn);
+int ath_rx_aggr_stop(struct ath_softc *sc,
+                    const u8 *addr,
+                    u16 tid);
+int ath_tx_aggr_start(struct ath_softc *sc,
+                     const u8 *addr,
+                     u16 tid,
+                     u16 *ssn);
+int ath_tx_aggr_stop(struct ath_softc *sc,
+                    const u8 *addr,
+                    u16 tid);
+void ath_newassoc(struct ath_softc *sc,
+       struct ath_node *node, int isnew, int isuapsd);
+struct ath_node *ath_node_attach(struct ath_softc *sc,
+       u8 addr[ETH_ALEN], int if_id);
+void ath_node_detach(struct ath_softc *sc, struct ath_node *an, bool bh_flag);
+struct ath_node *ath_node_get(struct ath_softc *sc, u8 addr[ETH_ALEN]);
+void ath_node_put(struct ath_softc *sc, struct ath_node *an, bool bh_flag);
+struct ath_node *ath_node_find(struct ath_softc *sc, u8 *addr);
+
+/*******************/
+/* Beacon Handling */
+/*******************/
+
+/*
+ * Regardless of the number of beacons we stagger, (i.e. regardless of the
+ * number of BSSIDs) if a given beacon does not go out even after waiting this
+ * number of beacon intervals, the game's up.
+ */
+#define BSTUCK_THRESH                  (9 * ATH_BCBUF)
+#define        ATH_BCBUF                       4   /* number of beacon buffers */
+#define ATH_DEFAULT_BINTVAL            100 /* default beacon interval in TU */
+#define ATH_DEFAULT_BMISS_LIMIT        10
+#define        ATH_BEACON_AIFS_DEFAULT         0  /* Default aifs for ap beacon q */
+#define        ATH_BEACON_CWMIN_DEFAULT        0  /* Default cwmin for ap beacon q */
+#define        ATH_BEACON_CWMAX_DEFAULT        0  /* Default cwmax for ap beacon q */
+#define IEEE80211_MS_TO_TU(x)           (((x) * 1000) / 1024)
+
+/* beacon configuration */
+struct ath_beacon_config {
+       u16 beacon_interval;
+       u16 listen_interval;
+       u16 dtim_period;
+       u16 bmiss_timeout;
+       u8 dtim_count;
+       u8 tim_offset;
+       union {
+               u64 last_tsf;
+               u8 last_tstamp[8];
+       } u; /* last received beacon/probe response timestamp of this BSS. */
+};
+
+/* offsets in a beacon frame for
+ * quick acess of beacon content by low-level driver */
+struct ath_beacon_offset {
+       u8 *bo_tim;     /* start of atim/dtim */
+};
+
+void ath9k_beacon_tasklet(unsigned long data);
+void ath_beacon_config(struct ath_softc *sc, int if_id);
+int ath_beaconq_setup(struct ath_hal *ah);
+int ath_beacon_alloc(struct ath_softc *sc, int if_id);
+void ath_bstuck_process(struct ath_softc *sc);
+void ath_beacon_tasklet(struct ath_softc *sc, int *needmark);
+void ath_beacon_free(struct ath_softc *sc);
+void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp);
+void ath_beacon_sync(struct ath_softc *sc, int if_id);
+void ath_update_beacon_info(struct ath_softc *sc, int avgbrssi);
+void ath_get_beaconconfig(struct ath_softc *sc,
+                         int if_id,
+                         struct ath_beacon_config *conf);
+int ath_update_beacon(struct ath_softc *sc,
+                     int if_id,
+                     struct ath_beacon_offset *bo,
+                     struct sk_buff *skb,
+                     int mcast);
+/********/
+/* VAPs */
+/********/
+
+#define ATH_IF_HW_OFF           0x0001 /* hardware state needs to turn off */
+#define ATH_IF_HW_ON            0x0002 /* hardware state needs to turn on */
+/* STA only: the associated AP is HT capable */
+#define ATH_IF_HT               0x0004
+/* AP/IBSS only: current BSS has privacy on */
+#define ATH_IF_PRIVACY          0x0008
+#define ATH_IF_BEACON_ENABLE    0x0010 /* AP/IBSS only: enable beacon */
+#define ATH_IF_BEACON_SYNC      0x0020 /* IBSS only: need to sync beacon */
+
+/*
+ * Define the scheme that we select MAC address for multiple
+ * BSS on the same radio. The very first VAP will just use the MAC
+ * address from the EEPROM. For the next 3 VAPs, we set the
+ * U/L bit (bit 1) in MAC address, and use the next two bits as the
+ * index of the VAP.
+ */
+
+#define ATH_SET_VAP_BSSID_MASK(bssid_mask) \
+       ((bssid_mask)[0] &= ~(((ATH_BCBUF-1)<<2)|0x02))
+
+/* VAP configuration (from protocol layer) */
+struct ath_vap_config {
+       u32 av_fixed_rateset;
+       u32 av_fixed_retryset;
+};
+
+/* driver-specific vap state */
+struct ath_vap {
+       struct ieee80211_vif            *av_if_data; /* interface(vap)
+                               instance from 802.11 protocal layer */
+       enum ath9k_opmode                 av_opmode;  /* VAP operational mode */
+       struct ath_buf                  *av_bcbuf;  /* beacon buffer */
+       struct ath_beacon_offset        av_boff;    /* dynamic update state */
+       struct ath_tx_control           av_btxctl;  /* tx control information
+                                                       for beacon */
+       int                             av_bslot;   /* beacon slot index */
+       struct ath_txq                  av_mcastq;  /* multicast
+                                               transmit queue */
+       struct ath_vap_config           av_config;  /* vap configuration
+                                       parameters from 802.11 protocol layer*/
+       struct ath_rate_node            *rc_node;
+};
+
+int ath_vap_attach(struct ath_softc *sc,
+                  int if_id,
+                  struct ieee80211_vif *if_data,
+                  enum ath9k_opmode opmode);
+int ath_vap_detach(struct ath_softc *sc, int if_id);
+int ath_vap_config(struct ath_softc *sc,
+       int if_id, struct ath_vap_config *if_config);
+int ath_vap_listen(struct ath_softc *sc, int if_id);
+
+/*********************/
+/* Antenna diversity */
+/*********************/
+
+#define ATH_ANT_DIV_MAX_CFG      2
+#define ATH_ANT_DIV_MIN_IDLE_US  1000000  /* us */
+#define ATH_ANT_DIV_MIN_SCAN_US  50000   /* us */
+
+enum ATH_ANT_DIV_STATE{
+       ATH_ANT_DIV_IDLE,
+       ATH_ANT_DIV_SCAN,       /* evaluating antenna */
+};
+
+struct ath_antdiv {
+       struct ath_softc *antdiv_sc;
+       u8 antdiv_start;
+       enum ATH_ANT_DIV_STATE antdiv_state;
+       u8 antdiv_num_antcfg;
+       u8 antdiv_curcfg;
+       u8 antdiv_bestcfg;
+       int32_t antdivf_rssitrig;
+       int32_t antdiv_lastbrssi[ATH_ANT_DIV_MAX_CFG];
+       u64 antdiv_lastbtsf[ATH_ANT_DIV_MAX_CFG];
+       u64 antdiv_laststatetsf;
+       u8 antdiv_bssid[ETH_ALEN];
+};
+
+void ath_slow_ant_div_init(struct ath_antdiv *antdiv,
+       struct ath_softc *sc, int32_t rssitrig);
+void ath_slow_ant_div_start(struct ath_antdiv *antdiv,
+                           u8 num_antcfg,
+                           const u8 *bssid);
+void ath_slow_ant_div_stop(struct ath_antdiv *antdiv);
+void ath_slow_ant_div(struct ath_antdiv *antdiv,
+                     struct ieee80211_hdr *wh,
+                     struct ath_rx_status *rx_stats);
+void ath_setdefantenna(void *sc, u32 antenna);
+
+/********************/
+/* Main driver core */
+/********************/
+
+/*
+ * Default cache line size, in bytes.
+ * Used when PCI device not fully initialized by bootrom/BIOS
+*/
+#define DEFAULT_CACHELINE       32
+#define        ATH_DEFAULT_NOISE_FLOOR -95
+#define ATH_REGCLASSIDS_MAX     10
+#define ATH_CABQ_READY_TIME     80  /* % of beacon interval */
+#define ATH_PREAMBLE_SHORT     (1<<0)
+#define ATH_PROTECT_ENABLE     (1<<1)
+#define ATH_MAX_SW_RETRIES      10
+/* Num farmes difference in tx to flip default recv */
+#define        ATH_ANTENNA_DIFF        2
+#define ATH_CHAN_MAX            255
+#define IEEE80211_WEP_NKID      4       /* number of key ids */
+#define IEEE80211_RATE_VAL      0x7f
+/*
+ * The key cache is used for h/w cipher state and also for
+ * tracking station state such as the current tx antenna.
+ * We also setup a mapping table between key cache slot indices
+ * and station state to short-circuit node lookups on rx.
+ * Different parts have different size key caches.  We handle
+ * up to ATH_KEYMAX entries (could dynamically allocate state).
+ */
+#define        ATH_KEYMAX              128        /* max key cache size we handle */
+
+#define RESET_RETRY_TXQ         0x00000001
+#define ATH_IF_ID_ANY          0xff
+
+#define ATH_TXPOWER_MAX         100     /* .5 dBm units */
+
+#define RSSI_LPF_THRESHOLD         -20
+#define ATH_RSSI_EP_MULTIPLIER     (1<<7)  /* pow2 to optimize out * and / */
+#define ATH_RATE_DUMMY_MARKER      0
+#define ATH_RSSI_LPF_LEN           10
+#define ATH_RSSI_DUMMY_MARKER      0x127
+
+#define ATH_EP_MUL(x, mul)         ((x) * (mul))
+#define ATH_EP_RND(x, mul)                                             \
+       ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
+#define ATH_RSSI_OUT(x)                                                        \
+       (((x) != ATH_RSSI_DUMMY_MARKER) ?                               \
+        (ATH_EP_RND((x), ATH_RSSI_EP_MULTIPLIER)) : ATH_RSSI_DUMMY_MARKER)
+#define ATH_RSSI_IN(x)                                 \
+       (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER))
+#define ATH_LPF_RSSI(x, y, len)                                                \
+       ((x != ATH_RSSI_DUMMY_MARKER) ? \
+               (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define ATH_RSSI_LPF(x, y) do {                                                \
+               if ((y) >= RSSI_LPF_THRESHOLD)                          \
+                       x = ATH_LPF_RSSI((x), \
+                               ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \
+       } while (0)
+
+
+enum PROT_MODE {
+       PROT_M_NONE = 0,
+       PROT_M_RTSCTS,
+       PROT_M_CTSONLY
+};
+
+enum RATE_TYPE {
+       NORMAL_RATE = 0,
+       HALF_RATE,
+       QUARTER_RATE
+};
+
+struct ath_ht_info {
+       enum ath9k_ht_macmode tx_chan_width;
+       u16 maxampdu;
+       u8 mpdudensity;
+       u8 ext_chan_offset;
+};
+
+struct ath_softc {
+       struct ieee80211_hw *hw; /* mac80211 instance */
+       struct pci_dev          *pdev;      /* Bus handle */
+       void __iomem            *mem;       /* address of the device */
+       struct tasklet_struct   intr_tq;    /* General tasklet */
+       struct tasklet_struct   bcon_tasklet; /* Beacon tasklet */
+       struct ath_config       sc_config;  /* per-instance load-time
+                                               parameters */
+       int                     sc_debug;   /* Debug masks */
+       struct ath_hal          *sc_ah;     /* HAL Instance */
+       struct ath_rate_softc    *sc_rc;     /* tx rate control support */
+       u32               sc_intrstatus; /* HAL_STATUS */
+       enum ath9k_opmode         sc_opmode;  /* current operating mode */
+
+       /* Properties, Config */
+       u8                sc_invalid;   /* being detached */
+       u8                sc_beacons;   /* beacons running */
+       u8                sc_scanning;  /* scanning active */
+       u8                sc_txaggr;    /* enable 11n tx aggregation */
+       u8                sc_rxaggr;    /* enable 11n rx aggregation */
+       u8                sc_update_chainmask;  /* change chain mask */
+       u8                sc_full_reset;                /* force full reset */
+       enum wireless_mode      sc_curmode;     /* current phy mode */
+       u16               sc_curtxpow;    /* current tx power limit */
+       u16               sc_curaid;      /* current association id */
+       u8                sc_curbssid[ETH_ALEN];
+       u8                sc_myaddr[ETH_ALEN];
+       enum PROT_MODE          sc_protmode;    /* protection mode */
+       u8                sc_mcastantenna;/* Multicast antenna number */
+       u8                sc_txantenna;   /* data tx antenna
+                                               (fixed or auto) */
+       u8                sc_nbcnvaps;    /* # of vaps sending beacons */
+       u16               sc_nvaps;       /* # of active virtual ap's */
+       struct ath_vap          *sc_vaps[ATH_BCBUF]; /* interface id
+                                               to avp map */
+       enum ath9k_int            sc_imask;       /* interrupt mask copy */
+       u8                sc_bssidmask[ETH_ALEN];
+       u8                sc_defant;      /* current default antenna */
+       u8                sc_rxotherant;  /* rx's on non-default antenna*/
+       u16               sc_cachelsz;    /* cache line size */
+       int                     sc_slotupdate;  /* slot to next advance fsm */
+       int                     sc_slottime;    /* slot time */
+       u8                sc_noreset;
+       int                     sc_bslot[ATH_BCBUF];/* beacon xmit slots */
+       struct ath9k_node_stats   sc_halstats;    /* station-mode rssi stats */
+       struct list_head        node_list;
+       struct ath_ht_info      sc_ht_info;
+       int16_t                 sc_noise_floor; /* signal noise floor in dBm */
+       enum ath9k_ht_extprotspacing   sc_ht_extprotspacing;
+       u8                sc_tx_chainmask;
+       u8                sc_rx_chainmask;
+       u8                sc_rxchaindetect_ref;
+       u8                sc_rxchaindetect_thresh5GHz;
+       u8                sc_rxchaindetect_thresh2GHz;
+       u8                sc_rxchaindetect_delta5GHz;
+       u8                sc_rxchaindetect_delta2GHz;
+       u32               sc_rtsaggrlimit; /* Chipset specific
+                                               aggr limit */
+       u32                     sc_flags;
+#ifdef CONFIG_SLOW_ANT_DIV
+       /* Slow antenna diversity */
+       struct ath_antdiv       sc_antdiv;
+#endif
+       enum {
+               OK,                 /* no change needed */
+               UPDATE,             /* update pending */
+               COMMIT              /* beacon sent, commit change */
+       } sc_updateslot;            /* slot time update fsm */
+
+       /* Crypto */
+       u32                   sc_keymax;      /* size of key cache */
+       DECLARE_BITMAP(sc_keymap, ATH_KEYMAX); /* key use bit map */
+       u8              sc_splitmic;    /* split TKIP MIC keys */
+       int                     sc_keytype;     /* type of the key being used */
+
+       /* RX */
+       struct list_head        sc_rxbuf;       /* receive buffer */
+       struct ath_descdma      sc_rxdma;       /* RX descriptors */
+       int                     sc_rxbufsize;   /* rx size based on mtu */
+       u32               *sc_rxlink;     /* link ptr in last RX desc */
+       u32               sc_rxflush;     /* rx flush in progress */
+       u64               sc_lastrx;      /* tsf of last rx'd frame */
+
+       /* TX */
+       struct list_head        sc_txbuf;       /* transmit buffer */
+       struct ath_txq          sc_txq[ATH9K_NUM_TX_QUEUES];
+       struct ath_descdma      sc_txdma;       /* TX descriptors */
+       u32                   sc_txqsetup;    /* h/w queues setup */
+       u32                   sc_txintrperiod;/* tx interrupt batching */
+       int                     sc_haltype2q[ATH9K_WME_AC_VO+1]; /* HAL WME
+                                                       AC -> h/w qnum */
+       u32               sc_ant_tx[8];   /* recent tx frames/antenna */
+
+       /* Beacon */
+       struct ath9k_txq_info     sc_beacon_qi;   /* adhoc only: beacon
+                                               queue parameters */
+       struct ath_descdma      sc_bdma;        /* beacon descriptors */
+       struct ath_txq          *sc_cabq;       /* tx q for cab frames */
+       struct list_head        sc_bbuf;        /* beacon buffers */
+       u32                   sc_bhalq;       /* HAL q for outgoing beacons */
+       u32                   sc_bmisscount;  /* missed beacon transmits */
+       u32               ast_be_xmit; /* beacons transmitted */
+
+       /* Rate */
+       struct ieee80211_rate          rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX];
+       const struct ath9k_rate_table    *sc_rates[WIRELESS_MODE_MAX];
+       const struct ath9k_rate_table    *sc_currates; /* current rate table */
+       u8                       sc_rixmap[256]; /* IEEE to h/w
+                                               rate table ix */
+       u8                       sc_minrateix;   /* min h/w rate index */
+       u8                       sc_protrix; /* protection rate index */
+       struct {
+               u32 rateKbps;      /* transfer rate in kbs */
+               u8 ieeerate;       /* IEEE rate */
+       } sc_hwmap[256];         /* h/w rate ix mappings */
+
+       /* Channel, Band */
+       struct ieee80211_channel channels[IEEE80211_NUM_BANDS][ATH_CHAN_MAX];
+       struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+       struct ath9k_channel            sc_curchan; /* current h/w channel */
+
+       /* Locks */
+       spinlock_t              sc_rxflushlock; /* lock of RX flush */
+       spinlock_t              sc_rxbuflock;   /* rxbuf lock */
+       spinlock_t              sc_txbuflock;   /* txbuf lock */
+       spinlock_t              sc_resetlock;
+       spinlock_t              node_lock;
+};
+
+int ath_init(u16 devid, struct ath_softc *sc);
+void ath_deinit(struct ath_softc *sc);
+int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan);
+int ath_suspend(struct ath_softc *sc);
+irqreturn_t ath_isr(int irq, void *dev);
+int ath_reset(struct ath_softc *sc);
+void ath_scan_start(struct ath_softc *sc);
+void ath_scan_end(struct ath_softc *sc);
+int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan);
+void ath_setup_rate(struct ath_softc *sc,
+                   enum wireless_mode wMode,
+                   enum RATE_TYPE type,
+                   const struct ath9k_rate_table *rt);
+
+/*********************/
+/* Utility Functions */
+/*********************/
+
+void ath_key_reset(struct ath_softc *sc, u16 keyix, int freeslot);
+int ath_keyset(struct ath_softc *sc,
+              u16 keyix,
+              struct ath9k_keyval *hk,
+              const u8 mac[ETH_ALEN]);
+int ath_get_hal_qnum(u16 queue, struct ath_softc *sc);
+int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc);
+void ath_setslottime(struct ath_softc *sc);
+void ath_update_txpow(struct ath_softc *sc);
+int ath_cabq_update(struct ath_softc *);
+void ath_get_currentCountry(struct ath_softc *sc,
+       struct ath9k_country_entry *ctry);
+u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp);
+void ath_internal_reset(struct ath_softc *sc);
+u32 ath_chan2flags(struct ieee80211_channel *chan, struct ath_softc *sc);
+dma_addr_t ath_skb_map_single(struct ath_softc *sc,
+                             struct sk_buff *skb,
+                             int direction,
+                             dma_addr_t *pa);
+void ath_skb_unmap_single(struct ath_softc *sc,
+                         struct sk_buff *skb,
+                         int direction,
+                         dma_addr_t *pa);
+void ath_mcast_merge(struct ath_softc *sc, u32 mfilt[2]);
+enum ath9k_ht_macmode ath_cwm_macmode(struct ath_softc *sc);
+
+#endif /* CORE_H */
diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c
new file mode 100644 (file)
index 0000000..1f6f393
--- /dev/null
@@ -0,0 +1,8563 @@
+/*
+ * Copyright (c) 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 <linux/io.h>
+#include <asm/unaligned.h>
+
+#include "core.h"
+#include "hw.h"
+#include "reg.h"
+#include "phy.h"
+#include "initvals.h"
+
+static void ath9k_hw_iqcal_collect(struct ath_hal *ah);
+static void ath9k_hw_iqcalibrate(struct ath_hal *ah, u8 numChains);
+static void ath9k_hw_adc_gaincal_collect(struct ath_hal *ah);
+static void ath9k_hw_adc_gaincal_calibrate(struct ath_hal *ah,
+                                          u8 numChains);
+static void ath9k_hw_adc_dccal_collect(struct ath_hal *ah);
+static void ath9k_hw_adc_dccal_calibrate(struct ath_hal *ah,
+                                        u8 numChains);
+
+static const u8 CLOCK_RATE[] = { 40, 80, 22, 44, 88, 40 };
+static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93, -96 };
+
+static const struct hal_percal_data iq_cal_multi_sample = {
+       IQ_MISMATCH_CAL,
+       MAX_CAL_SAMPLES,
+       PER_MIN_LOG_COUNT,
+       ath9k_hw_iqcal_collect,
+       ath9k_hw_iqcalibrate
+};
+static const struct hal_percal_data iq_cal_single_sample = {
+       IQ_MISMATCH_CAL,
+       MIN_CAL_SAMPLES,
+       PER_MAX_LOG_COUNT,
+       ath9k_hw_iqcal_collect,
+       ath9k_hw_iqcalibrate
+};
+static const struct hal_percal_data adc_gain_cal_multi_sample = {
+       ADC_GAIN_CAL,
+       MAX_CAL_SAMPLES,
+       PER_MIN_LOG_COUNT,
+       ath9k_hw_adc_gaincal_collect,
+       ath9k_hw_adc_gaincal_calibrate
+};
+static const struct hal_percal_data adc_gain_cal_single_sample = {
+       ADC_GAIN_CAL,
+       MIN_CAL_SAMPLES,
+       PER_MAX_LOG_COUNT,
+       ath9k_hw_adc_gaincal_collect,
+       ath9k_hw_adc_gaincal_calibrate
+};
+static const struct hal_percal_data adc_dc_cal_multi_sample = {
+       ADC_DC_CAL,
+       MAX_CAL_SAMPLES,
+       PER_MIN_LOG_COUNT,
+       ath9k_hw_adc_dccal_collect,
+       ath9k_hw_adc_dccal_calibrate
+};
+static const struct hal_percal_data adc_dc_cal_single_sample = {
+       ADC_DC_CAL,
+       MIN_CAL_SAMPLES,
+       PER_MAX_LOG_COUNT,
+       ath9k_hw_adc_dccal_collect,
+       ath9k_hw_adc_dccal_calibrate
+};
+static const struct hal_percal_data adc_init_dc_cal = {
+       ADC_DC_INIT_CAL,
+       MIN_CAL_SAMPLES,
+       INIT_LOG_COUNT,
+       ath9k_hw_adc_dccal_collect,
+       ath9k_hw_adc_dccal_calibrate
+};
+
+static const struct ath_hal ar5416hal = {
+       AR5416_MAGIC,
+       0,
+       0,
+       NULL,
+       NULL,
+       CTRY_DEFAULT,
+       0,
+       0,
+       0,
+       0,
+       0,
+       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       },
+};
+
+static struct ath9k_rate_table ar5416_11a_table = {
+       8,
+       {0},
+       {
+               {true, PHY_OFDM, 6000, 0x0b, 0x00, (0x80 | 12), 0},
+               {true, PHY_OFDM, 9000, 0x0f, 0x00, 18, 0},
+               {true, PHY_OFDM, 12000, 0x0a, 0x00, (0x80 | 24), 2},
+               {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 2},
+               {true, PHY_OFDM, 24000, 0x09, 0x00, (0x80 | 48), 4},
+               {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 4},
+               {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 4},
+               {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 4}
+       },
+};
+
+static struct ath9k_rate_table ar5416_11b_table = {
+       4,
+       {0},
+       {
+               {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0},
+               {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1},
+               {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 1},
+               {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 1}
+       },
+};
+
+static struct ath9k_rate_table ar5416_11g_table = {
+       12,
+       {0},
+       {
+               {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0},
+               {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1},
+               {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 2},
+               {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 3},
+
+               {false, PHY_OFDM, 6000, 0x0b, 0x00, 12, 4},
+               {false, PHY_OFDM, 9000, 0x0f, 0x00, 18, 4},
+               {true, PHY_OFDM, 12000, 0x0a, 0x00, 24, 6},
+               {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 6},
+               {true, PHY_OFDM, 24000, 0x09, 0x00, 48, 8},
+               {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 8},
+               {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 8},
+               {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 8}
+       },
+};
+
+static struct ath9k_rate_table ar5416_11ng_table = {
+       28,
+       {0},
+       {
+               {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0},
+               {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1},
+               {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 2},
+               {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 3},
+
+               {false, PHY_OFDM, 6000, 0x0b, 0x00, 12, 4},
+               {false, PHY_OFDM, 9000, 0x0f, 0x00, 18, 4},
+               {true, PHY_OFDM, 12000, 0x0a, 0x00, 24, 6},
+               {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 6},
+               {true, PHY_OFDM, 24000, 0x09, 0x00, 48, 8},
+               {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 8},
+               {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 8},
+               {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 8},
+               {true, PHY_HT, 6500, 0x80, 0x00, 0, 4},
+               {true, PHY_HT, 13000, 0x81, 0x00, 1, 6},
+               {true, PHY_HT, 19500, 0x82, 0x00, 2, 6},
+               {true, PHY_HT, 26000, 0x83, 0x00, 3, 8},
+               {true, PHY_HT, 39000, 0x84, 0x00, 4, 8},
+               {true, PHY_HT, 52000, 0x85, 0x00, 5, 8},
+               {true, PHY_HT, 58500, 0x86, 0x00, 6, 8},
+               {true, PHY_HT, 65000, 0x87, 0x00, 7, 8},
+               {true, PHY_HT, 13000, 0x88, 0x00, 8, 4},
+               {true, PHY_HT, 26000, 0x89, 0x00, 9, 6},
+               {true, PHY_HT, 39000, 0x8a, 0x00, 10, 6},
+               {true, PHY_HT, 52000, 0x8b, 0x00, 11, 8},
+               {true, PHY_HT, 78000, 0x8c, 0x00, 12, 8},
+               {true, PHY_HT, 104000, 0x8d, 0x00, 13, 8},
+               {true, PHY_HT, 117000, 0x8e, 0x00, 14, 8},
+               {true, PHY_HT, 130000, 0x8f, 0x00, 15, 8},
+       },
+};
+
+static struct ath9k_rate_table ar5416_11na_table = {
+       24,
+       {0},
+       {
+               {true, PHY_OFDM, 6000, 0x0b, 0x00, (0x80 | 12), 0},
+               {true, PHY_OFDM, 9000, 0x0f, 0x00, 18, 0},
+               {true, PHY_OFDM, 12000, 0x0a, 0x00, (0x80 | 24), 2},
+               {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 2},
+               {true, PHY_OFDM, 24000, 0x09, 0x00, (0x80 | 48), 4},
+               {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 4},
+               {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 4},
+               {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 4},
+               {true, PHY_HT, 6500, 0x80, 0x00, 0, 0},
+               {true, PHY_HT, 13000, 0x81, 0x00, 1, 2},
+               {true, PHY_HT, 19500, 0x82, 0x00, 2, 2},
+               {true, PHY_HT, 26000, 0x83, 0x00, 3, 4},
+               {true, PHY_HT, 39000, 0x84, 0x00, 4, 4},
+               {true, PHY_HT, 52000, 0x85, 0x00, 5, 4},
+               {true, PHY_HT, 58500, 0x86, 0x00, 6, 4},
+               {true, PHY_HT, 65000, 0x87, 0x00, 7, 4},
+               {true, PHY_HT, 13000, 0x88, 0x00, 8, 0},
+               {true, PHY_HT, 26000, 0x89, 0x00, 9, 2},
+               {true, PHY_HT, 39000, 0x8a, 0x00, 10, 2},
+               {true, PHY_HT, 52000, 0x8b, 0x00, 11, 4},
+               {true, PHY_HT, 78000, 0x8c, 0x00, 12, 4},
+               {true, PHY_HT, 104000, 0x8d, 0x00, 13, 4},
+               {true, PHY_HT, 117000, 0x8e, 0x00, 14, 4},
+               {true, PHY_HT, 130000, 0x8f, 0x00, 15, 4},
+       },
+};
+
+static enum wireless_mode ath9k_hw_chan2wmode(struct ath_hal *ah,
+                                      const struct ath9k_channel *chan)
+{
+       if (IS_CHAN_CCK(chan))
+               return WIRELESS_MODE_11b;
+       if (IS_CHAN_G(chan))
+               return WIRELESS_MODE_11g;
+       return WIRELESS_MODE_11a;
+}
+
+static bool ath9k_hw_wait(struct ath_hal *ah,
+                         u32 reg,
+                         u32 mask,
+                         u32 val)
+{
+       int i;
+
+       for (i = 0; i < (AH_TIMEOUT / AH_TIME_QUANTUM); i++) {
+               if ((REG_READ(ah, reg) & mask) == val)
+                       return true;
+
+               udelay(AH_TIME_QUANTUM);
+       }
+       DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO,
+                "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n",
+                __func__, reg, REG_READ(ah, reg), mask, val);
+       return false;
+}
+
+static bool ath9k_hw_eeprom_read(struct ath_hal *ah, u32 off,
+                                u16 *data)
+{
+       (void) REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S));
+
+       if (!ath9k_hw_wait(ah,
+                          AR_EEPROM_STATUS_DATA,
+                          AR_EEPROM_STATUS_DATA_BUSY |
+                          AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0)) {
+               return false;
+       }
+
+       *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA),
+                  AR_EEPROM_STATUS_DATA_VAL);
+
+       return true;
+}
+
+static int ath9k_hw_flash_map(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+
+       ahp->ah_cal_mem = ioremap(AR5416_EEPROM_START_ADDR, AR5416_EEPROM_MAX);
+
+       if (!ahp->ah_cal_mem) {
+               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                        "%s: cannot remap eeprom region \n", __func__);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static bool ath9k_hw_flash_read(struct ath_hal *ah, u32 off,
+                               u16 *data)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+
+       *data = ioread16(ahp->ah_cal_mem + off);
+       return true;
+}
+
+static void ath9k_hw_read_revisions(struct ath_hal *ah)
+{
+       u32 val;
+
+       val = REG_READ(ah, AR_SREV) & AR_SREV_ID;
+
+       if (val == 0xFF) {
+               val = REG_READ(ah, AR_SREV);
+
+               ah->ah_macVersion =
+                       (val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S;
+
+               ah->ah_macRev = MS(val, AR_SREV_REVISION2);
+               ah->ah_isPciExpress =
+                       (val & AR_SREV_TYPE2_HOST_MODE) ? 0 : 1;
+
+       } else {
+               if (!AR_SREV_9100(ah))
+                       ah->ah_macVersion = MS(val, AR_SREV_VERSION);
+
+               ah->ah_macRev = val & AR_SREV_REVISION;
+
+               if (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE)
+                       ah->ah_isPciExpress = true;
+       }
+}
+
+u32 ath9k_hw_reverse_bits(u32 val, u32 n)
+{
+       u32 retval;
+       int i;
+
+       for (i = 0, retval = 0; i < n; i++) {
+               retval = (retval << 1) | (val & 1);
+               val >>= 1;
+       }
+       return retval;
+}
+
+static void ath9k_hw_set_defaults(struct ath_hal *ah)
+{
+       int i;
+
+       ah->ah_config.ath_hal_dma_beacon_response_time = 2;
+       ah->ah_config.ath_hal_sw_beacon_response_time = 10;
+       ah->ah_config.ath_hal_additional_swba_backoff = 0;
+       ah->ah_config.ath_hal_6mb_ack = 0x0;
+       ah->ah_config.ath_hal_cwmIgnoreExtCCA = 0;
+       ah->ah_config.ath_hal_pciePowerSaveEnable = 0;
+       ah->ah_config.ath_hal_pcieL1SKPEnable = 0;
+       ah->ah_config.ath_hal_pcieClockReq = 0;
+       ah->ah_config.ath_hal_pciePowerReset = 0x100;
+       ah->ah_config.ath_hal_pcieRestore = 0;
+       ah->ah_config.ath_hal_pcieWaen = 0;
+       ah->ah_config.ath_hal_analogShiftReg = 1;
+       ah->ah_config.ath_hal_htEnable = 1;
+       ah->ah_config.ath_hal_ofdmTrigLow = 200;
+       ah->ah_config.ath_hal_ofdmTrigHigh = 500;
+       ah->ah_config.ath_hal_cckTrigHigh = 200;
+       ah->ah_config.ath_hal_cckTrigLow = 100;
+       ah->ah_config.ath_hal_enableANI = 0;
+       ah->ah_config.ath_hal_noiseImmunityLvl = 4;
+       ah->ah_config.ath_hal_ofdmWeakSigDet = 1;
+       ah->ah_config.ath_hal_cckWeakSigThr = 0;
+       ah->ah_config.ath_hal_spurImmunityLvl = 2;
+       ah->ah_config.ath_hal_firStepLvl = 0;
+       ah->ah_config.ath_hal_rssiThrHigh = 40;
+       ah->ah_config.ath_hal_rssiThrLow = 7;
+       ah->ah_config.ath_hal_diversityControl = 0;
+       ah->ah_config.ath_hal_antennaSwitchSwap = 0;
+
+       for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
+               ah->ah_config.ath_hal_spurChans[i][0] = AR_NO_SPUR;
+               ah->ah_config.ath_hal_spurChans[i][1] = AR_NO_SPUR;
+       }
+
+       ah->ah_config.ath_hal_intrMitigation = 0;
+}
+
+static inline void ath9k_hw_override_ini(struct ath_hal *ah,
+                                        struct ath9k_channel *chan)
+{
+       if (!AR_SREV_5416_V20_OR_LATER(ah)
+           || AR_SREV_9280_10_OR_LATER(ah))
+               return;
+
+       REG_WRITE(ah, 0x9800 + (651 << 2), 0x11);
+}
+
+static inline void ath9k_hw_init_bb(struct ath_hal *ah,
+                                   struct ath9k_channel *chan)
+{
+       u32 synthDelay;
+
+       synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY;
+       if (IS_CHAN_CCK(chan))
+               synthDelay = (4 * synthDelay) / 22;
+       else
+               synthDelay /= 10;
+
+       REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
+
+       udelay(synthDelay + BASE_ACTIVATE_DELAY);
+}
+
+static inline void ath9k_hw_init_interrupt_masks(struct ath_hal *ah,
+                                                enum ath9k_opmode opmode)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+
+       ahp->ah_maskReg = AR_IMR_TXERR |
+               AR_IMR_TXURN |
+               AR_IMR_RXERR |
+               AR_IMR_RXORN |
+               AR_IMR_BCNMISC;
+
+       if (ahp->ah_intrMitigation)
+               ahp->ah_maskReg |= AR_IMR_RXINTM | AR_IMR_RXMINTR;
+       else
+               ahp->ah_maskReg |= AR_IMR_RXOK;
+
+       ahp->ah_maskReg |= AR_IMR_TXOK;
+
+       if (opmode == ATH9K_M_HOSTAP)
+               ahp->ah_maskReg |= AR_IMR_MIB;
+
+       REG_WRITE(ah, AR_IMR, ahp->ah_maskReg);
+       REG_WRITE(ah, AR_IMR_S2, REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT);
+
+       if (!AR_SREV_9100(ah)) {
+               REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF);
+               REG_WRITE(ah, AR_INTR_SYNC_ENABLE, AR_INTR_SYNC_DEFAULT);
+               REG_WRITE(ah, AR_INTR_SYNC_MASK, 0);
+       }
+}
+
+static inline void ath9k_hw_init_qos(struct ath_hal *ah)
+{
+       REG_WRITE(ah, AR_MIC_QOS_CONTROL, 0x100aa);
+       REG_WRITE(ah, AR_MIC_QOS_SELECT, 0x3210);
+
+       REG_WRITE(ah, AR_QOS_NO_ACK,
+                 SM(2, AR_QOS_NO_ACK_TWO_BIT) |
+                 SM(5, AR_QOS_NO_ACK_BIT_OFF) |
+                 SM(0, AR_QOS_NO_ACK_BYTE_OFF));
+
+       REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL);
+       REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF);
+       REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF);
+       REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF);
+       REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF);
+}
+
+static void ath9k_hw_analog_shift_rmw(struct ath_hal *ah,
+                                     u32 reg,
+                                     u32 mask,
+                                     u32 shift,
+                                     u32 val)
+{
+       u32 regVal;
+
+       regVal = REG_READ(ah, reg) & ~mask;
+       regVal |= (val << shift) & mask;
+
+       REG_WRITE(ah, reg, regVal);
+
+       if (ah->ah_config.ath_hal_analogShiftReg)
+               udelay(100);
+
+       return;
+}
+
+static u8 ath9k_hw_get_num_ant_config(struct ath_hal_5416 *ahp,
+                                           enum hal_freq_band freq_band)
+{
+       struct ar5416_eeprom *eep = &ahp->ah_eeprom;
+       struct modal_eep_header *pModal =
+               &(eep->modalHeader[HAL_FREQ_BAND_2GHZ == freq_band]);
+       struct base_eep_header *pBase = &eep->baseEepHeader;
+       u8 num_ant_config;
+
+       num_ant_config = 1;
+
+       if (pBase->version >= 0x0E0D)
+               if (pModal->useAnt1)
+                       num_ant_config += 1;
+
+       return num_ant_config;
+}
+
+static int
+ath9k_hw_get_eeprom_antenna_cfg(struct ath_hal_5416 *ahp,
+                               struct ath9k_channel *chan,
+                               u8 index,
+                               u16 *config)
+{
+       struct ar5416_eeprom *eep = &ahp->ah_eeprom;
+       struct modal_eep_header *pModal =
+               &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
+       struct base_eep_header *pBase = &eep->baseEepHeader;
+
+       switch (index) {
+       case 0:
+               *config = pModal->antCtrlCommon & 0xFFFF;
+               return 0;
+       case 1:
+               if (pBase->version >= 0x0E0D) {
+                       if (pModal->useAnt1) {
+                               *config =
+                               ((pModal->antCtrlCommon & 0xFFFF0000) >> 16);
+                               return 0;
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+
+       return -EINVAL;
+}
+
+static inline bool ath9k_hw_nvram_read(struct ath_hal *ah,
+                                      u32 off,
+                                      u16 *data)
+{
+       if (ath9k_hw_use_flash(ah))
+               return ath9k_hw_flash_read(ah, off, data);
+       else
+               return ath9k_hw_eeprom_read(ah, off, data);
+}
+
+static inline bool ath9k_hw_fill_eeprom(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       struct ar5416_eeprom *eep = &ahp->ah_eeprom;
+       u16 *eep_data;
+       int addr, ar5416_eep_start_loc = 0;
+
+       if (!ath9k_hw_use_flash(ah)) {
+               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                        "%s: Reading from EEPROM, not flash\n", __func__);
+               ar5416_eep_start_loc = 256;
+       }
+       if (AR_SREV_9100(ah))
+               ar5416_eep_start_loc = 256;
+
+       eep_data = (u16 *) eep;
+       for (addr = 0;
+            addr < sizeof(struct ar5416_eeprom) / sizeof(u16);
+            addr++) {
+               if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc,
+                                        eep_data)) {
+                       DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                                "%s: Unable to read eeprom region \n",
+                                __func__);
+                       return false;
+               }
+               eep_data++;
+       }
+       return true;
+}
+
+/* XXX: Clean me up, make me more legible */
+static bool
+ath9k_hw_eeprom_set_board_values(struct ath_hal *ah,
+                                struct ath9k_channel *chan)
+{
+       struct modal_eep_header *pModal;
+       int i, regChainOffset;
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       struct ar5416_eeprom *eep = &ahp->ah_eeprom;
+       u8 txRxAttenLocal;
+       u16 ant_config;
+
+       pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
+
+       txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44;
+
+       ath9k_hw_get_eeprom_antenna_cfg(ahp, chan, 1, &ant_config);
+       REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config);
+
+       for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+               if (AR_SREV_9280(ah)) {
+                       if (i >= 2)
+                               break;
+               }
+
+               if (AR_SREV_5416_V20_OR_LATER(ah) &&
+                   (ahp->ah_rxchainmask == 5 || ahp->ah_txchainmask == 5)
+                   && (i != 0))
+                       regChainOffset = (i == 1) ? 0x2000 : 0x1000;
+               else
+                       regChainOffset = i * 0x1000;
+
+               REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset,
+                         pModal->antCtrlChain[i]);
+
+               REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset,
+                         (REG_READ(ah,
+                                   AR_PHY_TIMING_CTRL4(0) +
+                                   regChainOffset) &
+                          ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF |
+                            AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) |
+                         SM(pModal->iqCalICh[i],
+                            AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) |
+                         SM(pModal->iqCalQCh[i],
+                            AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF));
+
+               if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
+                       if ((eep->baseEepHeader.version &
+                            AR5416_EEP_VER_MINOR_MASK) >=
+                           AR5416_EEP_MINOR_VER_3) {
+                               txRxAttenLocal = pModal->txRxAttenCh[i];
+                               if (AR_SREV_9280_10_OR_LATER(ah)) {
+                                       REG_RMW_FIELD(ah,
+                                               AR_PHY_GAIN_2GHZ +
+                                               regChainOffset,
+                                               AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN,
+                                               pModal->
+                                               bswMargin[i]);
+                                       REG_RMW_FIELD(ah,
+                                               AR_PHY_GAIN_2GHZ +
+                                               regChainOffset,
+                                               AR_PHY_GAIN_2GHZ_XATTEN1_DB,
+                                               pModal->
+                                               bswAtten[i]);
+                                       REG_RMW_FIELD(ah,
+                                               AR_PHY_GAIN_2GHZ +
+                                               regChainOffset,
+                                               AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
+                                               pModal->
+                                               xatten2Margin[i]);
+                                       REG_RMW_FIELD(ah,
+                                               AR_PHY_GAIN_2GHZ +
+                                               regChainOffset,
+                                               AR_PHY_GAIN_2GHZ_XATTEN2_DB,
+                                               pModal->
+                                               xatten2Db[i]);
+                               } else {
+                                       REG_WRITE(ah,
+                                                 AR_PHY_GAIN_2GHZ +
+                                                 regChainOffset,
+                                                 (REG_READ(ah,
+                                                           AR_PHY_GAIN_2GHZ +
+                                                           regChainOffset) &
+                                                  ~AR_PHY_GAIN_2GHZ_BSW_MARGIN)
+                                                 | SM(pModal->
+                                                 bswMargin[i],
+                                                 AR_PHY_GAIN_2GHZ_BSW_MARGIN));
+                                       REG_WRITE(ah,
+                                                 AR_PHY_GAIN_2GHZ +
+                                                 regChainOffset,
+                                                 (REG_READ(ah,
+                                                           AR_PHY_GAIN_2GHZ +
+                                                           regChainOffset) &
+                                                  ~AR_PHY_GAIN_2GHZ_BSW_ATTEN)
+                                                 | SM(pModal->bswAtten[i],
+                                                 AR_PHY_GAIN_2GHZ_BSW_ATTEN));
+                               }
+                       }
+                       if (AR_SREV_9280_10_OR_LATER(ah)) {
+                               REG_RMW_FIELD(ah,
+                                             AR_PHY_RXGAIN +
+                                             regChainOffset,
+                                             AR9280_PHY_RXGAIN_TXRX_ATTEN,
+                                             txRxAttenLocal);
+                               REG_RMW_FIELD(ah,
+                                             AR_PHY_RXGAIN +
+                                             regChainOffset,
+                                             AR9280_PHY_RXGAIN_TXRX_MARGIN,
+                                             pModal->rxTxMarginCh[i]);
+                       } else {
+                               REG_WRITE(ah,
+                                         AR_PHY_RXGAIN + regChainOffset,
+                                         (REG_READ(ah,
+                                                   AR_PHY_RXGAIN +
+                                                   regChainOffset) &
+                                          ~AR_PHY_RXGAIN_TXRX_ATTEN) |
+                                         SM(txRxAttenLocal,
+                                            AR_PHY_RXGAIN_TXRX_ATTEN));
+                               REG_WRITE(ah,
+                                         AR_PHY_GAIN_2GHZ +
+                                         regChainOffset,
+                                         (REG_READ(ah,
+                                                   AR_PHY_GAIN_2GHZ +
+                                                   regChainOffset) &
+                                          ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) |
+                                         SM(pModal->rxTxMarginCh[i],
+                                            AR_PHY_GAIN_2GHZ_RXTX_MARGIN));
+                       }
+               }
+       }
+
+       if (AR_SREV_9280_10_OR_LATER(ah)) {
+               if (IS_CHAN_2GHZ(chan)) {
+                       ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0,
+                                                 AR_AN_RF2G1_CH0_OB,
+                                                 AR_AN_RF2G1_CH0_OB_S,
+                                                 pModal->ob);
+                       ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0,
+                                                 AR_AN_RF2G1_CH0_DB,
+                                                 AR_AN_RF2G1_CH0_DB_S,
+                                                 pModal->db);
+                       ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1,
+                                                 AR_AN_RF2G1_CH1_OB,
+                                                 AR_AN_RF2G1_CH1_OB_S,
+                                                 pModal->ob_ch1);
+                       ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1,
+                                                 AR_AN_RF2G1_CH1_DB,
+                                                 AR_AN_RF2G1_CH1_DB_S,
+                                                 pModal->db_ch1);
+               } else {
+                       ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0,
+                                                 AR_AN_RF5G1_CH0_OB5,
+                                                 AR_AN_RF5G1_CH0_OB5_S,
+                                                 pModal->ob);
+                       ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0,
+                                                 AR_AN_RF5G1_CH0_DB5,
+                                                 AR_AN_RF5G1_CH0_DB5_S,
+                                                 pModal->db);
+                       ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1,
+                                                 AR_AN_RF5G1_CH1_OB5,
+                                                 AR_AN_RF5G1_CH1_OB5_S,
+                                                 pModal->ob_ch1);
+                       ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1,
+                                                 AR_AN_RF5G1_CH1_DB5,
+                                                 AR_AN_RF5G1_CH1_DB5_S,
+                                                 pModal->db_ch1);
+               }
+               ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2,
+                                         AR_AN_TOP2_XPABIAS_LVL,
+                                         AR_AN_TOP2_XPABIAS_LVL_S,
+                                         pModal->xpaBiasLvl);
+               ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2,
+                                         AR_AN_TOP2_LOCALBIAS,
+                                         AR_AN_TOP2_LOCALBIAS_S,
+                                         pModal->local_bias);
+               DPRINTF(ah->ah_sc, ATH_DBG_ANY, "ForceXPAon: %d\n",
+                       pModal->force_xpaon);
+               REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG,
+                             pModal->force_xpaon);
+       }
+
+       REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH,
+                     pModal->switchSettling);
+       REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC,
+                     pModal->adcDesiredSize);
+
+       if (!AR_SREV_9280_10_OR_LATER(ah))
+               REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
+                             AR_PHY_DESIRED_SZ_PGA,
+                             pModal->pgaDesiredSize);
+
+       REG_WRITE(ah, AR_PHY_RF_CTL4,
+                 SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF)
+                 | SM(pModal->txEndToXpaOff,
+                      AR_PHY_RF_CTL4_TX_END_XPAB_OFF)
+                 | SM(pModal->txFrameToXpaOn,
+                      AR_PHY_RF_CTL4_FRAME_XPAA_ON)
+                 | SM(pModal->txFrameToXpaOn,
+                      AR_PHY_RF_CTL4_FRAME_XPAB_ON));
+
+       REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON,
+                     pModal->txEndToRxOn);
+       if (AR_SREV_9280_10_OR_LATER(ah)) {
+               REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62,
+                             pModal->thresh62);
+               REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0,
+                             AR_PHY_EXT_CCA0_THRESH62,
+                             pModal->thresh62);
+       } else {
+               REG_RMW_FIELD(ah, AR_PHY_CCA, AR_PHY_CCA_THRESH62,
+                             pModal->thresh62);
+               REG_RMW_FIELD(ah, AR_PHY_EXT_CCA,
+                             AR_PHY_EXT_CCA_THRESH62,
+                             pModal->thresh62);
+       }
+
+       if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+           AR5416_EEP_MINOR_VER_2) {
+               REG_RMW_FIELD(ah, AR_PHY_RF_CTL2,
+                             AR_PHY_TX_END_DATA_START,
+                             pModal->txFrameToDataStart);
+               REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_PA_ON,
+                             pModal->txFrameToPaOn);
+       }
+
+       if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
+           AR5416_EEP_MINOR_VER_3) {
+               if (IS_CHAN_HT40(chan))
+                       REG_RMW_FIELD(ah, AR_PHY_SETTLING,
+                                     AR_PHY_SETTLING_SWITCH,
+                                     pModal->swSettleHt40);
+       }
+
+       return true;
+}
+
+static inline int ath9k_hw_check_eeprom(struct ath_hal *ah)
+{
+       u32 sum = 0, el;
+       u16 *eepdata;
+       int i;
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       bool need_swap = false;
+       struct ar5416_eeprom *eep =
+               (struct ar5416_eeprom *) &ahp->ah_eeprom;
+
+       if (!ath9k_hw_use_flash(ah)) {
+               u16 magic, magic2;
+               int addr;
+
+               if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET,
+                                       &magic)) {
+                       DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                                "%s: Reading Magic # failed\n", __func__);
+                       return false;
+               }
+               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "%s: Read Magic = 0x%04X\n",
+                        __func__, magic);
+
+               if (magic != AR5416_EEPROM_MAGIC) {
+                       magic2 = swab16(magic);
+
+                       if (magic2 == AR5416_EEPROM_MAGIC) {
+                               need_swap = true;
+                               eepdata = (u16 *) (&ahp->ah_eeprom);
+
+                               for (addr = 0;
+                                    addr <
+                                            sizeof(struct ar5416_eeprom) /
+                                            sizeof(u16); addr++) {
+                                       u16 temp;
+
+                                       temp = swab16(*eepdata);
+                                       *eepdata = temp;
+                                       eepdata++;
+
+                                       DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                                                "0x%04X  ", *eepdata);
+                                       if (((addr + 1) % 6) == 0)
+                                               DPRINTF(ah->ah_sc,
+                                                        ATH_DBG_EEPROM,
+                                                        "\n");
+                               }
+                       } else {
+                               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                                        "Invalid EEPROM Magic. "
+                                       "endianness missmatch.\n");
+                               return -EINVAL;
+                       }
+               }
+       }
+       DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n",
+                need_swap ? "True" : "False");
+
+       if (need_swap)
+               el = swab16(ahp->ah_eeprom.baseEepHeader.length);
+       else
+               el = ahp->ah_eeprom.baseEepHeader.length;
+
+       if (el > sizeof(struct ar5416_eeprom))
+               el = sizeof(struct ar5416_eeprom) / sizeof(u16);
+       else
+               el = el / sizeof(u16);
+
+       eepdata = (u16 *) (&ahp->ah_eeprom);
+
+       for (i = 0; i < el; i++)
+               sum ^= *eepdata++;
+
+       if (need_swap) {
+               u32 integer, j;
+               u16 word;
+
+               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                        "EEPROM Endianness is not native.. Changing \n");
+
+               word = swab16(eep->baseEepHeader.length);
+               eep->baseEepHeader.length = word;
+
+               word = swab16(eep->baseEepHeader.checksum);
+               eep->baseEepHeader.checksum = word;
+
+               word = swab16(eep->baseEepHeader.version);
+               eep->baseEepHeader.version = word;
+
+               word = swab16(eep->baseEepHeader.regDmn[0]);
+               eep->baseEepHeader.regDmn[0] = word;
+
+               word = swab16(eep->baseEepHeader.regDmn[1]);
+               eep->baseEepHeader.regDmn[1] = word;
+
+               word = swab16(eep->baseEepHeader.rfSilent);
+               eep->baseEepHeader.rfSilent = word;
+
+               word = swab16(eep->baseEepHeader.blueToothOptions);
+               eep->baseEepHeader.blueToothOptions = word;
+
+               word = swab16(eep->baseEepHeader.deviceCap);
+               eep->baseEepHeader.deviceCap = word;
+
+               for (j = 0; j < ARRAY_SIZE(eep->modalHeader); j++) {
+                       struct modal_eep_header *pModal =
+                               &eep->modalHeader[j];
+                       integer = swab32(pModal->antCtrlCommon);
+                       pModal->antCtrlCommon = integer;
+
+                       for (i = 0; i < AR5416_MAX_CHAINS; i++) {
+                               integer = swab32(pModal->antCtrlChain[i]);
+                               pModal->antCtrlChain[i] = integer;
+                       }
+
+                       for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) {
+                               word = swab16(pModal->spurChans[i].spurChan);
+                               pModal->spurChans[i].spurChan = word;
+                       }
+               }
+       }
+
+       if (sum != 0xffff || ar5416_get_eep_ver(ahp) != AR5416_EEP_VER ||
+           ar5416_get_eep_rev(ahp) < AR5416_EEP_NO_BACK_VER) {
+               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                        "Bad EEPROM checksum 0x%x or revision 0x%04x\n",
+                        sum, ar5416_get_eep_ver(ahp));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static bool ath9k_hw_chip_test(struct ath_hal *ah)
+{
+       u32 regAddr[2] = { AR_STA_ID0, AR_PHY_BASE + (8 << 2) };
+       u32 regHold[2];
+       u32 patternData[4] = { 0x55555555,
+                                    0xaaaaaaaa,
+                                    0x66666666,
+                                    0x99999999 };
+       int i, j;
+
+       for (i = 0; i < 2; i++) {
+               u32 addr = regAddr[i];
+               u32 wrData, rdData;
+
+               regHold[i] = REG_READ(ah, addr);
+               for (j = 0; j < 0x100; j++) {
+                       wrData = (j << 16) | j;
+                       REG_WRITE(ah, addr, wrData);
+                       rdData = REG_READ(ah, addr);
+                       if (rdData != wrData) {
+                               DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+                                "%s: address test failed "
+                               "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n",
+                                __func__, addr, wrData, rdData);
+                               return false;
+                       }
+               }
+               for (j = 0; j < 4; j++) {
+                       wrData = patternData[j];
+                       REG_WRITE(ah, addr, wrData);
+                       rdData = REG_READ(ah, addr);
+                       if (wrData != rdData) {
+                               DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+                                "%s: address test failed "
+                               "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n",
+                                __func__, addr, wrData, rdData);
+                               return false;
+                       }
+               }
+               REG_WRITE(ah, regAddr[i], regHold[i]);
+       }
+       udelay(100);
+       return true;
+}
+
+u32 ath9k_hw_getrxfilter(struct ath_hal *ah)
+{
+       u32 bits = REG_READ(ah, AR_RX_FILTER);
+       u32 phybits = REG_READ(ah, AR_PHY_ERR);
+
+       if (phybits & AR_PHY_ERR_RADAR)
+               bits |= ATH9K_RX_FILTER_PHYRADAR;
+       if (phybits & (AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING))
+               bits |= ATH9K_RX_FILTER_PHYERR;
+       return bits;
+}
+
+void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits)
+{
+       u32 phybits;
+
+       REG_WRITE(ah, AR_RX_FILTER, (bits & 0xffff) | AR_RX_COMPR_BAR);
+       phybits = 0;
+       if (bits & ATH9K_RX_FILTER_PHYRADAR)
+               phybits |= AR_PHY_ERR_RADAR;
+       if (bits & ATH9K_RX_FILTER_PHYERR)
+               phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING;
+       REG_WRITE(ah, AR_PHY_ERR, phybits);
+
+       if (phybits)
+               REG_WRITE(ah, AR_RXCFG,
+                         REG_READ(ah, AR_RXCFG) | AR_RXCFG_ZLFDMA);
+       else
+               REG_WRITE(ah, AR_RXCFG,
+                         REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_ZLFDMA);
+}
+
+bool ath9k_hw_setcapability(struct ath_hal *ah,
+                           enum hal_capability_type type,
+                           u32 capability,
+                           u32 setting,
+                           int *status)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       u32 v;
+
+       switch (type) {
+       case HAL_CAP_TKIP_MIC:
+               if (setting)
+                       ahp->ah_staId1Defaults |=
+                               AR_STA_ID1_CRPT_MIC_ENABLE;
+               else
+                       ahp->ah_staId1Defaults &=
+                               ~AR_STA_ID1_CRPT_MIC_ENABLE;
+               return true;
+       case HAL_CAP_DIVERSITY:
+               v = REG_READ(ah, AR_PHY_CCK_DETECT);
+               if (setting)
+                       v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
+               else
+                       v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
+               REG_WRITE(ah, AR_PHY_CCK_DETECT, v);
+               return true;
+       case HAL_CAP_MCAST_KEYSRCH:
+               if (setting)
+                       ahp->ah_staId1Defaults |= AR_STA_ID1_MCAST_KSRCH;
+               else
+                       ahp->ah_staId1Defaults &= ~AR_STA_ID1_MCAST_KSRCH;
+               return true;
+       case HAL_CAP_TSF_ADJUST:
+               if (setting)
+                       ahp->ah_miscMode |= AR_PCU_TX_ADD_TSF;
+               else
+                       ahp->ah_miscMode &= ~AR_PCU_TX_ADD_TSF;
+               return true;
+       default:
+               return false;
+       }
+}
+
+void ath9k_hw_dmaRegDump(struct ath_hal *ah)
+{
+       u32 val[ATH9K_NUM_DMA_DEBUG_REGS];
+       int qcuOffset = 0, dcuOffset = 0;
+       u32 *qcuBase = &val[0], *dcuBase = &val[4];
+       int i;
+
+       REG_WRITE(ah, AR_MACMISC,
+                 ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
+                  (AR_MACMISC_MISC_OBS_BUS_1 <<
+                   AR_MACMISC_MISC_OBS_BUS_MSB_S)));
+
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "Raw DMA Debug values:\n");
+       for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) {
+               if (i % 4 == 0)
+                       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n");
+
+               val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32)));
+               DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "%d: %08x ", i, val[i]);
+       }
+
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n\n");
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+                "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
+
+       for (i = 0; i < ATH9K_NUM_QUEUES;
+            i++, qcuOffset += 4, dcuOffset += 5) {
+               if (i == 8) {
+                       qcuOffset = 0;
+                       qcuBase++;
+               }
+
+               if (i == 6) {
+                       dcuOffset = 0;
+                       dcuBase++;
+               }
+
+               DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+                        "%2d          %2x      %1x     %2x           %2x\n",
+                        i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
+                        (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset +
+                                                            3),
+                        val[2] & (0x7 << (i * 3)) >> (i * 3),
+                        (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
+       }
+
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n");
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+                "qcu_stitch state:   %2x    qcu_fetch state:        %2x\n",
+                (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22);
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+                "qcu_complete state: %2x    dcu_complete state:     %2x\n",
+                (val[3] & 0x1c000000) >> 26, (val[6] & 0x3));
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+                "dcu_arb state:      %2x    dcu_fp state:           %2x\n",
+                (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27);
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+                "chan_idle_dur:     %3d    chan_idle_dur_valid:     %1d\n",
+                (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10);
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+                "txfifo_valid_0:      %1d    txfifo_valid_1:          %1d\n",
+                (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12);
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+                "txfifo_dcu_num_0:   %2d    txfifo_dcu_num_1:       %2d\n",
+                (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17);
+
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "pcu observe 0x%x \n",
+               REG_READ(ah, AR_OBS_BUS_1));
+       DPRINTF(ah->ah_sc, ATH_DBG_REG_IO,
+               "AR_CR 0x%x \n", REG_READ(ah, AR_CR));
+}
+
+u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah,
+                                       u32 *rxc_pcnt,
+                                       u32 *rxf_pcnt,
+                                       u32 *txf_pcnt)
+{
+       static u32 cycles, rx_clear, rx_frame, tx_frame;
+       u32 good = 1;
+
+       u32 rc = REG_READ(ah, AR_RCCNT);
+       u32 rf = REG_READ(ah, AR_RFCNT);
+       u32 tf = REG_READ(ah, AR_TFCNT);
+       u32 cc = REG_READ(ah, AR_CCCNT);
+
+       if (cycles == 0 || cycles > cc) {
+               DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+                        "%s: cycle counter wrap. ExtBusy = 0\n",
+                        __func__);
+               good = 0;
+       } else {
+               u32 cc_d = cc - cycles;
+               u32 rc_d = rc - rx_clear;
+               u32 rf_d = rf - rx_frame;
+               u32 tf_d = tf - tx_frame;
+
+               if (cc_d != 0) {
+                       *rxc_pcnt = rc_d * 100 / cc_d;
+                       *rxf_pcnt = rf_d * 100 / cc_d;
+                       *txf_pcnt = tf_d * 100 / cc_d;
+               } else {
+                       good = 0;
+               }
+       }
+
+       cycles = cc;
+       rx_frame = rf;
+       rx_clear = rc;
+       tx_frame = tf;
+
+       return good;
+}
+
+void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode)
+{
+       u32 macmode;
+
+       if (mode == ATH9K_HT_MACMODE_2040 &&
+           !ah->ah_config.ath_hal_cwmIgnoreExtCCA)
+               macmode = AR_2040_JOINED_RX_CLEAR;
+       else
+               macmode = 0;
+
+       REG_WRITE(ah, AR_2040_MODE, macmode);
+}
+
+static void ath9k_hw_mark_phy_inactive(struct ath_hal *ah)
+{
+       REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
+}
+
+
+static struct ath_hal_5416 *ath9k_hw_newstate(u16 devid,
+                                             struct ath_softc *sc,
+                                             void __iomem *mem,
+                                             int *status)
+{
+       static const u8 defbssidmask[ETH_ALEN] =
+               { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+       struct ath_hal_5416 *ahp;
+       struct ath_hal *ah;
+
+       ahp = kzalloc(sizeof(struct ath_hal_5416), GFP_KERNEL);
+       if (ahp == NULL) {
+               DPRINTF(sc, ATH_DBG_FATAL,
+                        "%s: cannot allocate memory for state block\n",
+                        __func__);
+               *status = -ENOMEM;
+               return NULL;
+       }
+
+       ah = &ahp->ah;
+
+       memcpy(&ahp->ah, &ar5416hal, sizeof(struct ath_hal));
+
+       ah->ah_sc = sc;
+       ah->ah_sh = mem;
+
+       ah->ah_devid = devid;
+       ah->ah_subvendorid = 0;
+
+       ah->ah_flags = 0;
+       if ((devid == AR5416_AR9100_DEVID))
+               ah->ah_macVersion = AR_SREV_VERSION_9100;
+       if (!AR_SREV_9100(ah))
+               ah->ah_flags = AH_USE_EEPROM;
+
+       ah->ah_powerLimit = MAX_RATE_POWER;
+       ah->ah_tpScale = ATH9K_TP_SCALE_MAX;
+
+       ahp->ah_atimWindow = 0;
+       ahp->ah_diversityControl = ah->ah_config.ath_hal_diversityControl;
+       ahp->ah_antennaSwitchSwap =
+               ah->ah_config.ath_hal_antennaSwitchSwap;
+
+       ahp->ah_staId1Defaults = AR_STA_ID1_CRPT_MIC_ENABLE;
+       ahp->ah_beaconInterval = 100;
+       ahp->ah_enable32kHzClock = DONT_USE_32KHZ;
+       ahp->ah_slottime = (u32) -1;
+       ahp->ah_acktimeout = (u32) -1;
+       ahp->ah_ctstimeout = (u32) -1;
+       ahp->ah_globaltxtimeout = (u32) -1;
+       memcpy(&ahp->ah_bssidmask, defbssidmask, ETH_ALEN);
+
+       ahp->ah_gBeaconRate = 0;
+
+       return ahp;
+}
+
+static int ath9k_hw_eeprom_attach(struct ath_hal *ah)
+{
+       int status;
+
+       if (ath9k_hw_use_flash(ah))
+               ath9k_hw_flash_map(ah);
+
+       if (!ath9k_hw_fill_eeprom(ah))
+               return -EIO;
+
+       status = ath9k_hw_check_eeprom(ah);
+
+       return status;
+}
+
+u32 ath9k_hw_get_eeprom(struct ath_hal_5416 *ahp,
+                             enum eeprom_param param)
+{
+       struct ar5416_eeprom *eep = &ahp->ah_eeprom;
+       struct modal_eep_header *pModal = eep->modalHeader;
+       struct base_eep_header *pBase = &eep->baseEepHeader;
+
+       switch (param) {
+       case EEP_NFTHRESH_5:
+               return -pModal[0].noiseFloorThreshCh[0];
+       case EEP_NFTHRESH_2:
+               return -pModal[1].noiseFloorThreshCh[0];
+       case AR_EEPROM_MAC(0):
+               return pBase->macAddr[0] << 8 | pBase->macAddr[1];
+       case AR_EEPROM_MAC(1):
+               return pBase->macAddr[2] << 8 | pBase->macAddr[3];
+       case AR_EEPROM_MAC(2):
+               return pBase->macAddr[4] << 8 | pBase->macAddr[5];
+       case EEP_REG_0:
+               return pBase->regDmn[0];
+       case EEP_REG_1:
+               return pBase->regDmn[1];
+       case EEP_OP_CAP:
+               return pBase->deviceCap;
+       case EEP_OP_MODE:
+               return pBase->opCapFlags;
+       case EEP_RF_SILENT:
+               return pBase->rfSilent;
+       case EEP_OB_5:
+               return pModal[0].ob;
+       case EEP_DB_5:
+               return pModal[0].db;
+       case EEP_OB_2:
+               return pModal[1].ob;
+       case EEP_DB_2:
+               return pModal[1].db;
+       case EEP_MINOR_REV:
+               return pBase->version & AR5416_EEP_VER_MINOR_MASK;
+       case EEP_TX_MASK:
+               return pBase->txMask;
+       case EEP_RX_MASK:
+               return pBase->rxMask;
+       default:
+               return 0;
+       }
+}
+
+static inline int ath9k_hw_get_radiorev(struct ath_hal *ah)
+{
+       u32 val;
+       int i;
+
+       REG_WRITE(ah, AR_PHY(0x36), 0x00007058);
+       for (i = 0; i < 8; i++)
+               REG_WRITE(ah, AR_PHY(0x20), 0x00010000);
+       val = (REG_READ(ah, AR_PHY(256)) >> 24) & 0xff;
+       val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4);
+       return ath9k_hw_reverse_bits(val, 8);
+}
+
+static inline int ath9k_hw_init_macaddr(struct ath_hal *ah)
+{
+       u32 sum;
+       int i;
+       u16 eeval;
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       DECLARE_MAC_BUF(mac);
+
+       sum = 0;
+       for (i = 0; i < 3; i++) {
+               eeval = ath9k_hw_get_eeprom(ahp, AR_EEPROM_MAC(i));
+               sum += eeval;
+               ahp->ah_macaddr[2 * i] = eeval >> 8;
+               ahp->ah_macaddr[2 * i + 1] = eeval & 0xff;
+       }
+       if (sum == 0 || sum == 0xffff * 3) {
+               DPRINTF(ah->ah_sc, ATH_DBG_EEPROM,
+                        "%s: mac address read failed: %s\n", __func__,
+                        print_mac(mac, ahp->ah_macaddr));
+               return -EADDRNOTAVAIL;
+       }
+
+       return 0;
+}
+
+static inline int16_t ath9k_hw_interpolate(u16 target,
+                                          u16 srcLeft,
+                                          u16 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;
+}
+
+static inline u16 ath9k_hw_fbin2freq(u8 fbin,
+                                          bool is2GHz)
+{
+
+       if (fbin == AR5416_BCHAN_UNUSED)
+               return fbin;
+
+       return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin));
+}
+
+static u16 ath9k_hw_eeprom_get_spur_chan(struct ath_hal *ah,
+                                              u16 i,
+                                              bool is2GHz)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       struct ar5416_eeprom *eep =
+               (struct ar5416_eeprom *) &ahp->ah_eeprom;
+       u16 spur_val = AR_NO_SPUR;
+
+       DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                "Getting spur idx %d is2Ghz. %d val %x\n",
+                i, is2GHz, ah->ah_config.ath_hal_spurChans[i][is2GHz]);
+
+       switch (ah->ah_config.ath_hal_spurMode) {
+       case SPUR_DISABLE:
+               break;
+       case SPUR_ENABLE_IOCTL:
+               spur_val = ah->ah_config.ath_hal_spurChans[i][is2GHz];
+               DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                        "Getting spur val from new loc. %d\n", spur_val);
+               break;
+       case SPUR_ENABLE_EEPROM:
+               spur_val = eep->modalHeader[is2GHz].spurChans[i].spurChan;
+               break;
+
+       }
+       return spur_val;
+}
+
+static inline int ath9k_hw_rfattach(struct ath_hal *ah)
+{
+       bool rfStatus = false;
+       int ecode = 0;
+
+       rfStatus = ath9k_hw_init_rf(ah, &ecode);
+       if (!rfStatus) {
+               DPRINTF(ah->ah_sc, ATH_DBG_RESET,
+                        "%s: RF setup failed, status %u\n", __func__,
+                        ecode);
+               return ecode;
+       }
+
+       return 0;
+}
+
+static int ath9k_hw_rf_claim(struct ath_hal *ah)
+{
+       u32 val;
+
+       REG_WRITE(ah, AR_PHY(0), 0x00000007);
+
+       val = ath9k_hw_get_radiorev(ah);
+       switch (val & AR_RADIO_SREV_MAJOR) {
+       case 0:
+               val = AR_RAD5133_SREV_MAJOR;
+               break;
+       case AR_RAD5133_SREV_MAJOR:
+       case AR_RAD5122_SREV_MAJOR:
+       case AR_RAD2133_SREV_MAJOR:
+       case AR_RAD2122_SREV_MAJOR:
+               break;
+       default:
+               DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+                        "%s: 5G Radio Chip Rev 0x%02X is not "
+                       "supported by this driver\n",
+                        __func__, ah->ah_analog5GhzRev);
+               return -EOPNOTSUPP;
+       }
+
+       ah->ah_analog5GhzRev = val;
+
+       return 0;
+}
+
+static inline void ath9k_hw_init_pll(struct ath_hal *ah,
+                                    struct ath9k_channel *chan)
+{
+       u32 pll;
+
+       if (AR_SREV_9100(ah)) {
+               if (chan && IS_CHAN_5GHZ(chan))
+                       pll = 0x1450;
+               else
+                       pll = 0x1458;
+       } else {
+               if (AR_SREV_9280_10_OR_LATER(ah)) {
+                       pll = SM(0x5, AR_RTC_9160_PLL_REFDIV);
+
+                       if (chan && IS_CHAN_HALF_RATE(chan))
+                               pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL);
+                       else if (chan && IS_CHAN_QUARTER_RATE(chan))
+                               pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL);
+
+                       if (chan && IS_CHAN_5GHZ(chan)) {
+                               pll |= SM(0x28, AR_RTC_9160_PLL_DIV);
+
+
+                               if (AR_SREV_9280_20(ah)) {
+                                       if (((chan->channel % 20) == 0)
+                                           || ((chan->channel % 10) == 0))
+                                               pll = 0x2850;
+                                       else
+                                               pll = 0x142c;
+                               }
+                       } else {
+                               pll |= SM(0x2c, AR_RTC_9160_PLL_DIV);
+                       }
+
+               } else if (AR_SREV_9160_10_OR_LATER(ah)) {
+
+                       pll = SM(0x5, AR_RTC_9160_PLL_REFDIV);
+
+                       if (chan && IS_CHAN_HALF_RATE(chan))
+                               pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL);
+                       else if (chan && IS_CHAN_QUARTER_RATE(chan))
+                               pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL);
+
+                       if (chan && IS_CHAN_5GHZ(chan))
+                               pll |= SM(0x50, AR_RTC_9160_PLL_DIV);
+                       else
+                               pll |= SM(0x58, AR_RTC_9160_PLL_DIV);
+               } else {
+                       pll = AR_RTC_PLL_REFDIV_5 | AR_RTC_PLL_DIV2;
+
+                       if (chan && IS_CHAN_HALF_RATE(chan))
+                               pll |= SM(0x1, AR_RTC_PLL_CLKSEL);
+                       else if (chan && IS_CHAN_QUARTER_RATE(chan))
+                               pll |= SM(0x2, AR_RTC_PLL_CLKSEL);
+
+                       if (chan && IS_CHAN_5GHZ(chan))
+                               pll |= SM(0xa, AR_RTC_PLL_DIV);
+                       else
+                               pll |= SM(0xb, AR_RTC_PLL_DIV);
+               }
+       }
+       REG_WRITE(ah, (u16) (AR_RTC_PLL_CONTROL), pll);
+
+       udelay(RTC_PLL_SETTLE_DELAY);
+
+       REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
+}
+
+static void ath9k_hw_set_regs(struct ath_hal *ah, struct ath9k_channel *chan,
+                             enum ath9k_ht_macmode macmode)
+{
+       u32 phymode;
+       struct ath_hal_5416 *ahp = AH5416(ah);
+
+       phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40
+               | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH;
+
+       if (IS_CHAN_HT40(chan)) {
+               phymode |= AR_PHY_FC_DYN2040_EN;
+
+               if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
+                   (chan->chanmode == CHANNEL_G_HT40PLUS))
+                       phymode |= AR_PHY_FC_DYN2040_PRI_CH;
+
+               if (ahp->ah_extprotspacing == ATH9K_HT_EXTPROTSPACING_25)
+                       phymode |= AR_PHY_FC_DYN2040_EXT_CH;
+       }
+       REG_WRITE(ah, AR_PHY_TURBO, phymode);
+
+       ath9k_hw_set11nmac2040(ah, macmode);
+
+       REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
+       REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S);
+}
+
+static void ath9k_hw_set_operating_mode(struct ath_hal *ah, int opmode)
+{
+       u32 val;
+
+       val = REG_READ(ah, AR_STA_ID1);
+       val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC);
+       switch (opmode) {
+       case ATH9K_M_HOSTAP:
+               REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP
+                         | AR_STA_ID1_KSRCH_MODE);
+               REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
+               break;
+       case ATH9K_M_IBSS:
+               REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC
+                         | AR_STA_ID1_KSRCH_MODE);
+               REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
+               break;
+       case ATH9K_M_STA:
+       case ATH9K_M_MONITOR:
+               REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE);
+               break;
+       }
+}
+
+static inline void
+ath9k_hw_set_rfmode(struct ath_hal *ah, struct ath9k_channel *chan)
+{
+       u32 rfMode = 0;
+
+       if (chan == NULL)
+               return;
+
+       rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
+               ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
+
+       if (!AR_SREV_9280_10_OR_LATER(ah))
+               rfMode |= (IS_CHAN_5GHZ(chan)) ? AR_PHY_MODE_RF5GHZ :
+                       AR_PHY_MODE_RF2GHZ;
+
+       if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan))
+               rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE);
+
+       REG_WRITE(ah, AR_PHY_MODE, rfMode);
+}
+
+static bool ath9k_hw_set_reset(struct ath_hal *ah, int type)
+{
+       u32 rst_flags;
+       u32 tmpReg;
+
+       REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN |
+                 AR_RTC_FORCE_WAKE_ON_INT);
+
+       if (AR_SREV_9100(ah)) {
+               rst_flags = AR_RTC_RC_MAC_WARM | AR_RTC_RC_MAC_COLD |
+                       AR_RTC_RC_COLD_RESET | AR_RTC_RC_WARM_RESET;
+       } else {
+               tmpReg = REG_READ(ah, AR_INTR_SYNC_CAUSE);
+               if (tmpReg &
+                   (AR_INTR_SYNC_LOCAL_TIMEOUT |
+                    AR_INTR_SYNC_RADM_CPL_TIMEOUT)) {
+                       REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
+                       REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
+               } else {
+                       REG_WRITE(ah, AR_RC, AR_RC_AHB);
+               }
+
+               rst_flags = AR_RTC_RC_MAC_WARM;
+               if (type == ATH9K_RESET_COLD)
+                       rst_flags |= AR_RTC_RC_MAC_COLD;
+       }
+
+       REG_WRITE(ah, (u16) (AR_RTC_RC), rst_flags);
+       udelay(50);
+
+       REG_WRITE(ah, (u16) (AR_RTC_RC), 0);
+       if (!ath9k_hw_wait(ah, (u16) (AR_RTC_RC), AR_RTC_RC_M, 0)) {
+               DPRINTF(ah->ah_sc, ATH_DBG_RESET,
+                       "%s: RTC stuck in MAC reset\n",
+                       __func__);
+               return false;
+       }
+
+       if (!AR_SREV_9100(ah))
+               REG_WRITE(ah, AR_RC, 0);
+
+       ath9k_hw_init_pll(ah, NULL);
+
+       if (AR_SREV_9100(ah))
+               udelay(50);
+
+       return true;
+}
+
+static inline bool ath9k_hw_set_reset_power_on(struct ath_hal *ah)
+{
+       REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN |
+                 AR_RTC_FORCE_WAKE_ON_INT);
+
+       REG_WRITE(ah, (u16) (AR_RTC_RESET), 0);
+       REG_WRITE(ah, (u16) (AR_RTC_RESET), 1);
+
+       if (!ath9k_hw_wait(ah,
+                          AR_RTC_STATUS,
+                          AR_RTC_STATUS_M,
+                          AR_RTC_STATUS_ON)) {
+               DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: RTC not waking up\n",
+                        __func__);
+               return false;
+       }
+
+       ath9k_hw_read_revisions(ah);
+
+       return ath9k_hw_set_reset(ah, ATH9K_RESET_WARM);
+}
+
+static bool ath9k_hw_set_reset_reg(struct ath_hal *ah,
+                                  u32 type)
+{
+       REG_WRITE(ah, AR_RTC_FORCE_WAKE,
+                 AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT);
+
+       switch (type) {
+       case ATH9K_RESET_POWER_ON:
+               return ath9k_hw_set_reset_power_on(ah);
+               break;
+       case ATH9K_RESET_WARM:
+       case ATH9K_RESET_COLD:
+               return ath9k_hw_set_reset(ah, type);
+               break;
+       default:
+               return false;
+       }
+}
+
+static inline
+struct ath9k_channel *ath9k_hw_check_chan(struct ath_hal *ah,
+                                         struct ath9k_channel *chan)
+{
+       if (!(IS_CHAN_2GHZ(chan) ^ IS_CHAN_5GHZ(chan))) {
+               DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+                        "%s: invalid channel %u/0x%x; not marked as "
+                        "2GHz or 5GHz\n", __func__, chan->channel,
+                        chan->channelFlags);
+               return NULL;
+       }
+
+       if (!IS_CHAN_OFDM(chan) &&
+             !IS_CHAN_CCK(chan) &&
+             !IS_CHAN_HT20(chan) &&
+             !IS_CHAN_HT40(chan)) {
+               DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+                       "%s: invalid channel %u/0x%x; not marked as "
+                       "OFDM or CCK or HT20 or HT40PLUS or HT40MINUS\n",
+                       __func__, chan->channel, chan->channelFlags);
+               return NULL;
+       }
+
+       return ath9k_regd_check_channel(ah, chan);
+}
+
+static inline bool
+ath9k_hw_get_lower_upper_index(u8 target,
+                              u8 *pList,
+                              u16 listSize,
+                              u16 *indexL,
+                              u16 *indexR)
+{
+       u16 i;
+
+       if (target <= pList[0]) {
+               *indexL = *indexR = 0;
+               return true;
+       }
+       if (target >= pList[listSize - 1]) {
+               *indexL = *indexR = (u16) (listSize - 1);
+               return true;
+       }
+
+       for (i = 0; i < listSize - 1; i++) {
+               if (pList[i] == target) {
+                       *indexL = *indexR = i;
+                       return true;
+               }
+               if (target < pList[i + 1]) {
+                       *indexL = i;
+                       *indexR = (u16) (i + 1);
+                       return false;
+               }
+       }
+       return false;
+}
+
+static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer)
+{
+       int16_t nfval;
+       int16_t sort[ATH9K_NF_CAL_HIST_MAX];
+       int i, j;
+
+       for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++)
+               sort[i] = nfCalBuffer[i];
+
+       for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) {
+               for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) {
+                       if (sort[j] > sort[j - 1]) {
+                               nfval = sort[j];
+                               sort[j] = sort[j - 1];
+                               sort[j - 1] = nfval;
+                       }
+               }
+       }
+       nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1];
+
+       return nfval;
+}
+
+static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
+                                             int16_t *nfarray)
+{
+       int i;
+
+       for (i = 0; i < NUM_NF_READINGS; i++) {
+               h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
+
+               if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX)
+                       h[i].currIndex = 0;
+
+               if (h[i].invalidNFcount > 0) {
+                       if (nfarray[i] < AR_PHY_CCA_MIN_BAD_VALUE
+                           || nfarray[i] > AR_PHY_CCA_MAX_HIGH_VALUE) {
+                               h[i].invalidNFcount = ATH9K_NF_CAL_HIST_MAX;
+                       } else {
+                               h[i].invalidNFcount--;
+                               h[i].privNF = nfarray[i];
+                       }
+               } else {
+                       h[i].privNF =
+                               ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
+               }
+       }
+       return;
+}
+
+static void ar5416GetNoiseFloor(struct ath_hal *ah,
+                               int16_t nfarray[NUM_NF_READINGS])
+{
+       int16_t nf;
+
+       if (AR_SREV_9280_10_OR_LATER(ah))
+               nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR);
+       else
+               nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR);
+
+       if (nf & 0x100)
+               nf = 0 - ((nf ^ 0x1ff) + 1);
+       DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+                "NF calibrated [ctl] [chain 0] is %d\n", nf);
+       nfarray[0] = nf;
+
+       if (AR_SREV_9280_10_OR_LATER(ah))
+               nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
+                       AR9280_PHY_CH1_MINCCA_PWR);
+       else
+               nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
+                       AR_PHY_CH1_MINCCA_PWR);
+
+       if (nf & 0x100)
+               nf = 0 - ((nf ^ 0x1ff) + 1);
+       DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL,
+                "NF calibrated [ctl] [chain 1] is %d\n", nf);
+       nfarray[1] = nf;
+
+       if (!AR_SREV_9280(ah)) {
+               nf = MS(REG_READ(ah, AR_PHY_CH2_CCA),
+                       AR_PHY_CH2_MINCCA_PWR);
+               if (nf & 0x100)
+                       nf = 0 - ((nf ^ 0x1ff) + 1);
+               DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL,
+                        "NF calibrated [ctl] [chain 2] is %d\n", nf);
+               nfarray[2] = nf;
+       }
+
+       if (AR_SREV_9280_10_OR_LATER(ah))
+               nf = MS(REG_READ(ah, AR_PHY_EXT_CCA),
+                       AR9280_PHY_EXT_MINCCA_PWR);
+       else
+               nf = MS(REG_READ(ah, AR_PHY_EXT_CCA),
+                       AR_PHY_EXT_MINCCA_PWR);
+
+       if (nf & 0x100)
+               nf = 0 - ((nf ^ 0x1ff) + 1);
+       DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL,
+                "NF calibrated [ext] [chain 0] is %d\n", nf);
+       nfarray[3] = nf;
+
+       if (AR_SREV_9280_10_OR_LATER(ah))
+               nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
+                       AR9280_PHY_CH1_EXT_MINCCA_PWR);
+       else
+               nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
+                       AR_PHY_CH1_EXT_MINCCA_PWR);
+
+       if (nf & 0x100)
+               nf = 0 - ((nf ^ 0x1ff) + 1);
+       DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+                "NF calibrated [ext] [chain 1] is %d\n", nf);
+       nfarray[4] = nf;
+
+       if (!AR_SREV_9280(ah)) {
+               nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA),
+                       AR_PHY_CH2_EXT_MINCCA_PWR);
+               if (nf & 0x100)
+                       nf = 0 - ((nf ^ 0x1ff) + 1);
+               DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL,
+                        "NF calibrated [ext] [chain 2] is %d\n", nf);
+               nfarray[5] = nf;
+       }
+}
+
+static bool
+getNoiseFloorThresh(struct ath_hal *ah,
+                   const struct ath9k_channel *chan,
+                   int16_t *nft)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+
+       switch (chan->chanmode) {
+       case CHANNEL_A:
+       case CHANNEL_A_HT20:
+       case CHANNEL_A_HT40PLUS:
+       case CHANNEL_A_HT40MINUS:
+               *nft = (int16_t) ath9k_hw_get_eeprom(ahp, EEP_NFTHRESH_5);
+               break;
+       case CHANNEL_B:
+       case CHANNEL_G:
+       case CHANNEL_G_HT20:
+       case CHANNEL_G_HT40PLUS:
+       case CHANNEL_G_HT40MINUS:
+               *nft = (int16_t) ath9k_hw_get_eeprom(ahp, EEP_NFTHRESH_2);
+               break;
+       default:
+               DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
+                        "%s: invalid channel flags 0x%x\n", __func__,
+                        chan->channelFlags);
+               return false;
+       }
+       return true;
+}
+
+static void ath9k_hw_start_nfcal(struct ath_hal *ah)
+{
+       REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+                   AR_PHY_AGC_CONTROL_ENABLE_NF);
+       REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+                   AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+       REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+}
+
+static void
+ath9k_hw_loadnf(struct ath_hal *ah, struct ath9k_channel *chan)
+{
+       struct ath9k_nfcal_hist *h;
+       int i, j;
+       int32_t val;
+       const u32 ar5416_cca_regs[6] = {
+               AR_PHY_CCA,
+               AR_PHY_CH1_CCA,
+               AR_PHY_CH2_CCA,
+               AR_PHY_EXT_CCA,
+               AR_PHY_CH1_EXT_CCA,
+               AR_PHY_CH2_EXT_CCA
+       };
+       u8 chainmask;
+
+       if (AR_SREV_9280(ah))
+               chainmask = 0x1B;
+       else
+               chainmask = 0x3F;
+
+#ifdef ATH_NF_PER_CHAN
+       h = chan->nfCalHist;
+#else
+       h = ah->nfCalHist;
+#endif
+
+       for (i = 0; i < NUM_NF_READINGS; i++) {
+               if (chainmask & (1 << i)) {
+                       val = REG_READ(ah, ar5416_cca_regs[i]);
+                       val &= 0xFFFFFE00;
+                       val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
+                       REG_WRITE(ah, ar5416_cca_regs[i], val);
+               }
+       }
+
+       REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+                   AR_PHY_AGC_CONTROL_ENABLE_NF);
+       REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+                   AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+       REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+
+       for (j = 0; j < 1000; j++) {
+               if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
+                    AR_PHY_AGC_CONTROL_NF) == 0)
+                       break;
+               udelay(10);
+       }
+
+       for (i = 0; i < NUM_NF_READINGS; i++) {
+               if (chainmask & (1 << i)) {
+                       val = REG_READ(ah, ar5416_cca_regs[i]);
+                       val &= 0xFFFFFE00;
+                       val |= (((u32) (-50) << 1) & 0x1ff);
+                       REG_WRITE(ah, ar5416_cca_regs[i], val);
+               }
+       }
+}
+
+static int16_t ath9k_hw_getnf(struct ath_hal *ah,
+                             struct ath9k_channel *chan)
+{
+       int16_t nf, nfThresh;
+       int16_t nfarray[NUM_NF_READINGS] = { 0 };
+       struct ath9k_nfcal_hist *h;
+       u8 chainmask;
+
+       if (AR_SREV_9280(ah))
+               chainmask = 0x1B;
+       else
+               chainmask = 0x3F;
+
+       chan->channelFlags &= (~CHANNEL_CW_INT);
+       if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
+               DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+                        "%s: NF did not complete in calibration window\n",
+                        __func__);
+               nf = 0;
+               chan->rawNoiseFloor = nf;
+               return chan->rawNoiseFloor;
+       } else {
+               ar5416GetNoiseFloor(ah, nfarray);
+               nf = nfarray[0];
+               if (getNoiseFloorThresh(ah, chan, &nfThresh)
+                   && nf > nfThresh) {
+                       DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE,
+                                "%s: noise floor failed detected; "
+                                "detected %d, threshold %d\n", __func__,
+                                nf, nfThresh);
+                       chan->channelFlags |= CHANNEL_CW_INT;
+               }
+       }
+
+#ifdef ATH_NF_PER_CHAN
+       h = chan->nfCalHist;
+#else
+       h = ah->nfCalHist;
+#endif
+
+       ath9k_hw_update_nfcal_hist_buffer(h, nfarray);
+       chan->rawNoiseFloor = h[0].privNF;
+
+       return chan->rawNoiseFloor;
+}
+
+static void ath9k_hw_update_mibstats(struct ath_hal *ah,
+                             struct ath9k_mib_stats *stats)
+{
+       stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
+       stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
+       stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
+       stats->rts_good += REG_READ(ah, AR_RTS_OK);
+       stats->beacons += REG_READ(ah, AR_BEACON_CNT);
+}
+
+static void ath9k_enable_mib_counters(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+
+       DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable mib counters\n");
+
+       ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
+
+       REG_WRITE(ah, AR_FILT_OFDM, 0);
+       REG_WRITE(ah, AR_FILT_CCK, 0);
+       REG_WRITE(ah, AR_MIBC,
+                 ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
+                 & 0x0f);
+       REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
+       REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
+}
+
+static void ath9k_hw_disable_mib_counters(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+
+       DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disabling MIB counters\n");
+
+       REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC | AR_MIBC_CMC);
+
+       ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
+
+       REG_WRITE(ah, AR_FILT_OFDM, 0);
+       REG_WRITE(ah, AR_FILT_CCK, 0);
+}
+
+static int ath9k_hw_get_ani_channel_idx(struct ath_hal *ah,
+                                       struct ath9k_channel *chan)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) {
+               if (ahp->ah_ani[i].c.channel == chan->channel)
+                       return i;
+               if (ahp->ah_ani[i].c.channel == 0) {
+                       ahp->ah_ani[i].c.channel = chan->channel;
+                       ahp->ah_ani[i].c.channelFlags = chan->channelFlags;
+                       return i;
+               }
+       }
+
+       DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                "No more channel states left. Using channel 0\n");
+       return 0;
+}
+
+static void ath9k_hw_ani_attach(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       int i;
+
+       ahp->ah_hasHwPhyCounters = 1;
+
+       memset(ahp->ah_ani, 0, sizeof(ahp->ah_ani));
+       for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) {
+               ahp->ah_ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH;
+               ahp->ah_ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW;
+               ahp->ah_ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH;
+               ahp->ah_ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW;
+               ahp->ah_ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
+               ahp->ah_ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
+               ahp->ah_ani[i].ofdmWeakSigDetectOff =
+                       !ATH9K_ANI_USE_OFDM_WEAK_SIG;
+               ahp->ah_ani[i].cckWeakSigThreshold =
+                       ATH9K_ANI_CCK_WEAK_SIG_THR;
+               ahp->ah_ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
+               ahp->ah_ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
+               if (ahp->ah_hasHwPhyCounters) {
+                       ahp->ah_ani[i].ofdmPhyErrBase =
+                               AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH;
+                       ahp->ah_ani[i].cckPhyErrBase =
+                               AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH;
+               }
+       }
+       if (ahp->ah_hasHwPhyCounters) {
+               DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                       "Setting OfdmErrBase = 0x%08x\n",
+                       ahp->ah_ani[0].ofdmPhyErrBase);
+               DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n",
+                       ahp->ah_ani[0].cckPhyErrBase);
+
+               REG_WRITE(ah, AR_PHY_ERR_1, ahp->ah_ani[0].ofdmPhyErrBase);
+               REG_WRITE(ah, AR_PHY_ERR_2, ahp->ah_ani[0].cckPhyErrBase);
+               ath9k_enable_mib_counters(ah);
+       }
+       ahp->ah_aniPeriod = ATH9K_ANI_PERIOD;
+       if (ah->ah_config.ath_hal_enableANI)
+               ahp->ah_procPhyErr |= HAL_PROCESS_ANI;
+}
+
+static inline void ath9k_hw_ani_setup(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       int i;
+
+       const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
+       const int coarseHigh[] = { -14, -14, -14, -14, -12 };
+       const int coarseLow[] = { -64, -64, -64, -64, -70 };
+       const int firpwr[] = { -78, -78, -78, -78, -80 };
+
+       for (i = 0; i < 5; i++) {
+               ahp->ah_totalSizeDesired[i] = totalSizeDesired[i];
+               ahp->ah_coarseHigh[i] = coarseHigh[i];
+               ahp->ah_coarseLow[i] = coarseLow[i];
+               ahp->ah_firpwr[i] = firpwr[i];
+       }
+}
+
+static void ath9k_hw_ani_detach(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+
+       DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Detaching Ani\n");
+       if (ahp->ah_hasHwPhyCounters) {
+               ath9k_hw_disable_mib_counters(ah);
+               REG_WRITE(ah, AR_PHY_ERR_1, 0);
+               REG_WRITE(ah, AR_PHY_ERR_2, 0);
+       }
+}
+
+
+static bool ath9k_hw_ani_control(struct ath_hal *ah,
+                                enum ath9k_ani_cmd cmd, int param)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       struct ar5416AniState *aniState = ahp->ah_curani;
+
+       switch (cmd & ahp->ah_ani_function) {
+       case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{
+               u32 level = param;
+
+               if (level >= ARRAY_SIZE(ahp->ah_totalSizeDesired)) {
+                       DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                                "%s: level out of range (%u > %u)\n",
+                                __func__, level,
+                                (unsigned) ARRAY_SIZE(ahp->
+                                                      ah_totalSizeDesired));
+                       return false;
+               }
+
+               REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
+                             AR_PHY_DESIRED_SZ_TOT_DES,
+                             ahp->ah_totalSizeDesired[level]);
+               REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
+                             AR_PHY_AGC_CTL1_COARSE_LOW,
+                             ahp->ah_coarseLow[level]);
+               REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
+                             AR_PHY_AGC_CTL1_COARSE_HIGH,
+                             ahp->ah_coarseHigh[level]);
+               REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
+                             AR_PHY_FIND_SIG_FIRPWR,
+                             ahp->ah_firpwr[level]);
+
+               if (level > aniState->noiseImmunityLevel)
+                       ahp->ah_stats.ast_ani_niup++;
+               else if (level < aniState->noiseImmunityLevel)
+                       ahp->ah_stats.ast_ani_nidown++;
+               aniState->noiseImmunityLevel = level;
+               break;
+       }
+       case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{
+               const int m1ThreshLow[] = { 127, 50 };
+               const int m2ThreshLow[] = { 127, 40 };
+               const int m1Thresh[] = { 127, 0x4d };
+               const int m2Thresh[] = { 127, 0x40 };
+               const int m2CountThr[] = { 31, 16 };
+               const int m2CountThrLow[] = { 63, 48 };
+               u32 on = param ? 1 : 0;
+
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
+                             m1ThreshLow[on]);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
+                             m2ThreshLow[on]);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M1_THRESH,
+                             m1Thresh[on]);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M2_THRESH,
+                             m2Thresh[on]);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR,
+                             AR_PHY_SFCORR_M2COUNT_THR,
+                             m2CountThr[on]);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
+                             AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
+                             m2CountThrLow[on]);
+
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
+                             m1ThreshLow[on]);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
+                             m2ThreshLow[on]);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M1_THRESH,
+                             m1Thresh[on]);
+               REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
+                             AR_PHY_SFCORR_EXT_M2_THRESH,
+                             m2Thresh[on]);
+
+               if (on)
+                       REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
+                                   AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
+               else
+                       REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
+                                   AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
+
+               if (!on != aniState->ofdmWeakSigDetectOff) {
+                       if (on)
+                               ahp->ah_stats.ast_ani_ofdmon++;
+                       else
+                               ahp->ah_stats.ast_ani_ofdmoff++;
+                       aniState->ofdmWeakSigDetectOff = !on;
+               }
+               break;
+       }
+       case ATH9K_ANI_CCK_WEAK_SIGNAL_THR:{
+               const int weakSigThrCck[] = { 8, 6 };
+               u32 high = param ? 1 : 0;
+
+               REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,
+                             AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK,
+                             weakSigThrCck[high]);
+               if (high != aniState->cckWeakSigThreshold) {
+                       if (high)
+                               ahp->ah_stats.ast_ani_cckhigh++;
+                       else
+                               ahp->ah_stats.ast_ani_ccklow++;
+                       aniState->cckWeakSigThreshold = high;
+               }
+               break;
+       }
+       case ATH9K_ANI_FIRSTEP_LEVEL:{
+               const int firstep[] = { 0, 4, 8 };
+               u32 level = param;
+
+               if (level >= ARRAY_SIZE(firstep)) {
+                       DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                                "%s: level out of range (%u > %u)\n",
+                                __func__, level,
+                               (unsigned) ARRAY_SIZE(firstep));
+                       return false;
+               }
+               REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
+                             AR_PHY_FIND_SIG_FIRSTEP,
+                             firstep[level]);
+               if (level > aniState->firstepLevel)
+                       ahp->ah_stats.ast_ani_stepup++;
+               else if (level < aniState->firstepLevel)
+                       ahp->ah_stats.ast_ani_stepdown++;
+               aniState->firstepLevel = level;
+               break;
+       }
+       case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{
+               const int cycpwrThr1[] =
+                       { 2, 4, 6, 8, 10, 12, 14, 16 };
+               u32 level = param;
+
+               if (level >= ARRAY_SIZE(cycpwrThr1)) {
+                       DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                                "%s: level out of range (%u > %u)\n",
+                                __func__, level,
+                                (unsigned)
+                               ARRAY_SIZE(cycpwrThr1));
+                       return false;
+               }
+               REG_RMW_FIELD(ah, AR_PHY_TIMING5,
+                             AR_PHY_TIMING5_CYCPWR_THR1,
+                             cycpwrThr1[level]);
+               if (level > aniState->spurImmunityLevel)
+                       ahp->ah_stats.ast_ani_spurup++;
+               else if (level < aniState->spurImmunityLevel)
+                       ahp->ah_stats.ast_ani_spurdown++;
+               aniState->spurImmunityLevel = level;
+               break;
+       }
+       case ATH9K_ANI_PRESENT:
+               break;
+       default:
+               DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                       "%s: invalid cmd %u\n", __func__, cmd);
+               return false;
+       }
+
+       DPRINTF(ah->ah_sc, ATH_DBG_ANI, "%s: ANI parameters:\n", __func__);
+       DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+               "noiseImmunityLevel=%d, spurImmunityLevel=%d, "
+               "ofdmWeakSigDetectOff=%d\n",
+                aniState->noiseImmunityLevel, aniState->spurImmunityLevel,
+                !aniState->ofdmWeakSigDetectOff);
+       DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+               "cckWeakSigThreshold=%d, "
+               "firstepLevel=%d, listenTime=%d\n",
+                aniState->cckWeakSigThreshold, aniState->firstepLevel,
+                aniState->listenTime);
+       DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                "cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n",
+                aniState->cycleCount, aniState->ofdmPhyErrCount,
+                aniState->cckPhyErrCount);
+       return true;
+}
+
+static void ath9k_ani_restart(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       struct ar5416AniState *aniState;
+
+       if (!DO_ANI(ah))
+               return;
+
+       aniState = ahp->ah_curani;
+
+       aniState->listenTime = 0;
+       if (ahp->ah_hasHwPhyCounters) {
+               if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) {
+                       aniState->ofdmPhyErrBase = 0;
+                       DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                                "OFDM Trigger is too high for hw counters\n");
+               } else {
+                       aniState->ofdmPhyErrBase =
+                               AR_PHY_COUNTMAX - aniState->ofdmTrigHigh;
+               }
+               if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) {
+                       aniState->cckPhyErrBase = 0;
+                       DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                                "CCK Trigger is too high for hw counters\n");
+               } else {
+                       aniState->cckPhyErrBase =
+                               AR_PHY_COUNTMAX - aniState->cckTrigHigh;
+               }
+               DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                        "%s: Writing ofdmbase=%u   cckbase=%u\n",
+                        __func__, aniState->ofdmPhyErrBase,
+                        aniState->cckPhyErrBase);
+               REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase);
+               REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase);
+               REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
+               REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
+
+               ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
+       }
+       aniState->ofdmPhyErrCount = 0;
+       aniState->cckPhyErrCount = 0;
+}
+
+static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       struct ath9k_channel *chan = ah->ah_curchan;
+       struct ar5416AniState *aniState;
+       enum wireless_mode mode;
+       int32_t rssi;
+
+       if (!DO_ANI(ah))
+               return;
+
+       aniState = ahp->ah_curani;
+
+       if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
+               if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
+                                        aniState->noiseImmunityLevel + 1)) {
+                       return;
+               }
+       }
+
+       if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) {
+               if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
+                                        aniState->spurImmunityLevel + 1)) {
+                       return;
+               }
+       }
+
+       if (ah->ah_opmode == ATH9K_M_HOSTAP) {
+               if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
+                       ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+                                            aniState->firstepLevel + 1);
+               }
+               return;
+       }
+       rssi = BEACON_RSSI(ahp);
+       if (rssi > aniState->rssiThrHigh) {
+               if (!aniState->ofdmWeakSigDetectOff) {
+                       if (ath9k_hw_ani_control(ah,
+                                        ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+                                        false)) {
+                               ath9k_hw_ani_control(ah,
+                                       ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
+                                       0);
+                               return;
+                       }
+               }
+               if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
+                       ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+                                            aniState->firstepLevel + 1);
+                       return;
+               }
+       } else if (rssi > aniState->rssiThrLow) {
+               if (aniState->ofdmWeakSigDetectOff)
+                       ath9k_hw_ani_control(ah,
+                                    ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+                                    true);
+               if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
+                       ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+                                            aniState->firstepLevel + 1);
+               return;
+       } else {
+               mode = ath9k_hw_chan2wmode(ah, chan);
+               if (mode == WIRELESS_MODE_11g || mode == WIRELESS_MODE_11b) {
+                       if (!aniState->ofdmWeakSigDetectOff)
+                               ath9k_hw_ani_control(ah,
+                                    ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+                                    false);
+                       if (aniState->firstepLevel > 0)
+                               ath9k_hw_ani_control(ah,
+                                                    ATH9K_ANI_FIRSTEP_LEVEL,
+                                                    0);
+                       return;
+               }
+       }
+}
+
+static void ath9k_hw_ani_cck_err_trigger(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       struct ath9k_channel *chan = ah->ah_curchan;
+       struct ar5416AniState *aniState;
+       enum wireless_mode mode;
+       int32_t rssi;
+
+       if (!DO_ANI(ah))
+               return;
+
+       aniState = ahp->ah_curani;
+       if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
+               if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
+                                        aniState->noiseImmunityLevel + 1)) {
+                       return;
+               }
+       }
+       if (ah->ah_opmode == ATH9K_M_HOSTAP) {
+               if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
+                       ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+                                            aniState->firstepLevel + 1);
+               }
+               return;
+       }
+       rssi = BEACON_RSSI(ahp);
+       if (rssi > aniState->rssiThrLow) {
+               if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
+                       ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+                                            aniState->firstepLevel + 1);
+       } else {
+               mode = ath9k_hw_chan2wmode(ah, chan);
+               if (mode == WIRELESS_MODE_11g || mode == WIRELESS_MODE_11b) {
+                       if (aniState->firstepLevel > 0)
+                               ath9k_hw_ani_control(ah,
+                                                    ATH9K_ANI_FIRSTEP_LEVEL,
+                                                    0);
+               }
+       }
+}
+
+static void ath9k_ani_reset(struct ath_hal *ah)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       struct ar5416AniState *aniState;
+       struct ath9k_channel *chan = ah->ah_curchan;
+       int index;
+
+       if (!DO_ANI(ah))
+               return;
+
+       index = ath9k_hw_get_ani_channel_idx(ah, chan);
+       aniState = &ahp->ah_ani[index];
+       ahp->ah_curani = aniState;
+
+       if (DO_ANI(ah) && ah->ah_opmode != ATH9K_M_STA
+           && ah->ah_opmode != ATH9K_M_IBSS) {
+               DPRINTF(ah->ah_sc, ATH_DBG_ANI,
+                        "%s: Reset ANI state opmode %u\n", __func__,
+                        ah->ah_opmode);
+               ahp->ah_stats.ast_ani_reset++;
+               ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
+               ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
+               ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
+               ath9k_hw_ani_control(ah,
+                                    ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+                                    !ATH9K_ANI_USE_OFDM_WEAK_SIG);
+               ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
+                                    ATH9K_ANI_CCK_WEAK_SIG_THR);
+               ath9k_hw_setrxfilter(ah,
+                                    ath9k_hw_getrxfilter(ah) |
+                                    ATH9K_RX_FILTER_PHYERR);
+               if (ah->ah_opmode == ATH9K_M_HOSTAP) {
+                       ahp->ah_curani->ofdmTrigHigh =
+                               ah->ah_config.ath_hal_ofdmTrigHigh;
+                       ahp->ah_curani->ofdmTrigLow =
+                               ah->ah_config.ath_hal_ofdmTrigLow;
+                       ahp->ah_curani->cckTrigHigh =
+                               ah->ah_config.ath_hal_cckTrigHigh;
+                       ahp->ah_curani->cckTrigLow =
+                               ah->ah_config.ath_hal_cckTrigLow;
+               }
+               ath9k_ani_restart(ah);
+               return;
+       }
+
+       if (aniState->noiseImmunityLevel != 0)
+               ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
+                                    aniState->noiseImmunityLevel);
+       if (aniState->spurImmunityLevel != 0)
+               ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
+                                    aniState->spurImmunityLevel);
+       if (aniState->ofdmWeakSigDetectOff)
+               ath9k_hw_ani_control(ah,
+                                    ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
+                                    !aniState->ofdmWeakSigDetectOff);
+       if (aniState->cckWeakSigThreshold)
+               ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
+                                    aniState->cckWeakSigThreshold);
+       if (aniState->firstepLevel != 0)
+               ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
+                                    aniState->firstepLevel);
+       if (ahp->ah_hasHwPhyCounters) {
+               ath9k_hw_setrxfilter(ah,
+                                    ath9k_hw_getrxfilter(ah) &
+                                    ~ATH9K_RX_FILTER_PHYERR);
+               ath9k_ani_restart(ah);
+               REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
+               REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
+
+       } else {
+               ath9k_ani_restart(ah);
+               ath9k_hw_setrxfilter(ah,
+                                    ath9k_hw_getrxfilter(ah) |
+                                    ATH9K_RX_FILTER_PHYERR);
+       }
+}
+
+void ath9k_hw_procmibevent(struct ath_hal *ah,
+                          const struct ath9k_node_stats *stats)
+{
+       struct ath_hal_5416 *ahp = AH5416(ah);
+       u32 phyCnt1, phyCnt2;
+
+       DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Processing Mib Intr\n");
+
+       REG_WRITE(ah, AR_FILT_OFDM, 0);
+       REG_WRITE(ah, AR_FILT_CCK, 0);
+       if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING))
+               REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
+
+       ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats);
+       ahp->ah_stats.ast_nodestats = *stats;
+
+       if (!DO_ANI(ah))
+               return;
+
+       phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
+       phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
+       if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
+           ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
+               struct ar5416AniState *aniState = ahp->ah_curani;
+               u32 ofdmPhyErrCnt, cckPhyErrCnt;
+
+               ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
+        &n