Commit 22c7fdf4 authored by David S. Miller's avatar David S. Miller
parents 5423b2ed 86b89eed
......@@ -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
......
......@@ -551,7 +551,7 @@ static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx)
/* write address into NextDescriptor field of last desc in chain */
to_ioat_desc(ioat_chan->used_desc.prev)->hw->next =
first->async_tx.phys;
__list_splice(&new_chain, ioat_chan->used_desc.prev);
list_splice_tail(&new_chain, &ioat_chan->used_desc);
ioat_chan->dmacount += desc_count;
ioat_chan->pending += desc_count;
......
......@@ -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"
......
......@@ -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
......@@ -95,8 +95,6 @@ static struct pci_device_id ath5k_pci_id_table[] __devinitdata = {
{ PCI_VDEVICE(ATHEROS, 0x001a), .driver_data = AR5K_AR5212 }, /* 2413 Griffin-lite */
{ PCI_VDEVICE(ATHEROS, 0x001b), .driver_data = AR5K_AR5212 }, /* 5413 Eagle */
{ PCI_VDEVICE(ATHEROS, 0x001c), .driver_data = AR5K_AR5212 }, /* 5424 Condor (PCI-E)*/
{ PCI_VDEVICE(ATHEROS, 0x0023), .driver_data = AR5K_AR5212 }, /* 5416 */
{ PCI_VDEVICE(ATHEROS, 0x0024), .driver_data = AR5K_AR5212 }, /* 5418 */
{ 0 }
};
MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table);
......
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.
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
/*
* 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 wireless_mode {
ATH9K_MODE_11A = 0,
ATH9K_MODE_11B = 2,
ATH9K_MODE_11G = 3,
ATH9K_MODE_11NA_HT20 = 6,
ATH9K_MODE_11NG_HT20 = 7,
ATH9K_MODE_11NA_HT40PLUS = 8,
ATH9K_MODE_11NA_HT40MINUS = 9,
ATH9K_MODE_11NG_HT40PLUS = 10,
ATH9K_MODE_11NG_HT40MINUS = 11,
ATH9K_MODE_MAX
};
enum ath9k_hw_caps {
ATH9K_HW_CAP_CHAN_SPREAD = BIT(0),
ATH9K_HW_CAP_MIC_AESCCM = BIT(1),
ATH9K_HW_CAP_MIC_CKIP = BIT(2),
ATH9K_HW_CAP_MIC_TKIP = BIT(3),
ATH9K_HW_CAP_CIPHER_AESCCM = BIT(4),
ATH9K_HW_CAP_CIPHER_CKIP = BIT(5),
ATH9K_HW_CAP_CIPHER_TKIP = BIT(6),
ATH9K_HW_CAP_VEOL = BIT(7),
ATH9K_HW_CAP_BSSIDMASK = BIT(8),
ATH9K_HW_CAP_MCAST_KEYSEARCH = BIT(9),
ATH9K_HW_CAP_CHAN_HALFRATE = BIT(10),
ATH9K_HW_CAP_CHAN_QUARTERRATE = BIT(11),
ATH9K_HW_CAP_HT = BIT(12),
ATH9K_HW_CAP_GTT = BIT(13),
ATH9K_HW_CAP_FASTCC = BIT(14),
ATH9K_HW_CAP_RFSILENT = BIT(15),
ATH9K_HW_CAP_WOW = BIT(16),
ATH9K_HW_CAP_CST = BIT(17),
ATH9K_HW_CAP_ENHANCEDPM = BIT(18),
ATH9K_HW_CAP_AUTOSLEEP = BIT(19),
ATH9K_HW_CAP_4KB_SPLITTRANS = BIT(20),
ATH9K_HW_CAP_WOW_MATCHPATTERN_EXACT = BIT(21),
};
enum ath9k_capability_type {
ATH9K_CAP_CIPHER = 0,
ATH9K_CAP_TKIP_MIC,
ATH9K_CAP_TKIP_SPLIT,
ATH9K_CAP_PHYCOUNTERS,
ATH9K_CAP_DIVERSITY,
ATH9K_CAP_TXPOW,
ATH9K_CAP_PHYDIAG,
ATH9K_CAP_MCAST_KEYSRCH,
ATH9K_CAP_TSF_ADJUST,
ATH9K_CAP_WME_TKIPMIC,
ATH9K_CAP_RFSILENT,
ATH9K_CAP_ANT_CFG_2GHZ,
ATH9K_CAP_ANT_CFG_5GHZ
};
struct ath9k_hw_capabilities {
u32 hw_caps; /* ATH9K_HW_CAP_* from ath9k_hw_caps */
DECLARE_BITMAP(wireless_modes, ATH9K_MODE_MAX); /* ATH9K_MODE_* */
u16 total_queues;
u16 keycache_size;
u16 low_5ghz_chan, high_5ghz_chan;
u16 low_2ghz_chan, high_2ghz_chan;
u16 num_mr_retries;
u16 rts_aggr_limit;
u8 tx_chainmask;
u8 rx_chainmask;
u16 tx_triglevel_max;
u16 reg_cap;
u8 num_gpio_pins;
u8 num_antcfg_2ghz;
u8 num_antcfg_5ghz;
};
struct ath9k_ops_config {
int dma_beacon_response_time;
int sw_beacon_response_time;
int additional_swba_backoff;
int ack_6mb;
int cwm_ignore_extcca;
u8 pcie_powersave_enable;
u8 pcie_l1skp_enable;
u8 pcie_clock_req;
u32 pcie_waen;
int pcie_power_reset;
u8 pcie_restore;
u8 analog_shiftreg;
u8 ht_enable;
u32 ofdm_trig_low;
u32 ofdm_trig_high;
u32 cck_trig_high;
u32 cck_trig_low;
u32 enable_ani;
u8 noise_immunity_level;
u32 ofdm_weaksignal_det;
u32 cck_weaksignal_thr;
u8 spur_immunity_level;
u8 firstep_level;
int8_t rssi_thr_high;
int8_t rssi_thr_low;
u16 diversity_control;
u16 antenna_switch_swap;
int serialize_regmode;
int intr_mitigation;
#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 spurmode;
u16 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,
};
#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
};
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 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,
};
#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 ath9k_ops_config ah_config;
struct ath9k_hw_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
};
struct chan_centers {
u16 synth_center;
u16 ctl_center;
u16 ext_center;
};
int ath_hal_getcapability(struct ath_hal *ah,
enum ath9k_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,
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 ath9k_capability_type type,
u32 capability,
u32 *result);
bool ath9k_hw_setcapability(struct ath_hal *ah,
enum ath9k_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_get_txq_props(struct ath_hal *ah, int q,
struct ath9k_tx_queue_info *qinfo);
bool ath9k_hw_set_txq_props(struct ath_hal *ah, int q,
const struct ath9k_tx_queue_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_tx_queue_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
/*
* 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_tx_queue_info qi;
ath9k_hw_get_txq_props(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_set_txq_props(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.hw_caps & ATH9K_HW_CAP_VEOL)) {
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 = 0;
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_tx_queue_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.hw_caps & ATH9K_HW_CAP_VEOL)) {
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.hw_caps & ATH9K_HW_CAP_VEOL))
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.hw_caps & ATH9K_HW_CAP_VEOL))
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;
}
/*
* 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 = ath9k_hw_getratetable(sc->sc_ah, 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 == ATH9K_MODE_11G ? 1 : 0);
}
/*
* Set up rate table (legacy rates)
*/
static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band)
{
struct ath_hal *ah = sc->sc_ah;
const struct ath9k_rate_table *rt = NULL;
struct ieee80211_supported_band *sband;
struct ieee80211_rate *rate;
int i, maxrates;
switch (band) {
case IEEE80211_BAND_2GHZ:
rt = ath9k_hw_getratetable(ah, ATH9K_MODE_11G);
break;
case IEEE80211_BAND_5GHZ:
rt = ath9k_hw_getratetable(ah, ATH9K_MODE_11A);
break;
default:
break;
}
if (rt == NULL)
return;
sband = &sc->sbands[band];
rate = sc->rates[band];
if (rt->rateCount > ATH_RATE_MAX)
maxrates = ATH_RATE_MAX;
else
maxrates = rt->rateCount;
for (i = 0; i < maxrates; i++) {
rate[i].bitrate = rt->info[i].rateKbps / 100;
rate[i].hw_value = rt->info[i].rateCode;
sband->n_bitrates++;
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Rate: %2dMbps, ratecode: %2d\n",
__func__,
rate[i].bitrate / 10,
rate[i].hw_value);
}
}
/*
* 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,
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 ATH9K_MODE_11A;
else if (chan->chanmode == CHANNEL_G)
return ATH9K_MODE_11G;
else if (chan->chanmode == CHANNEL_B)
return ATH9K_MODE_11B;
else if (chan->chanmode == CHANNEL_A_HT20)
return ATH9K_MODE_11NA_HT20;
else if (chan->chanmode == CHANNEL_G_HT20)
return ATH9K_MODE_11NG_HT20;
else if (chan->chanmode == CHANNEL_A_HT40PLUS)
return ATH9K_MODE_11NA_HT40PLUS;
else if (chan->chanmode == CHANNEL_A_HT40MINUS)
return ATH9K_MODE_11NA_HT40MINUS;
else if (chan->chanmode == CHANNEL_G_HT40PLUS)
return ATH9K_MODE_11NG_HT40PLUS;
else if (chan->chanmode == CHANNEL_G_HT40MINUS)
return ATH9K_MODE_11NG_HT40MINUS;
/* NB: should not get here */
return ATH9K_MODE_11B;
}
/*
* 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_setcurmode(sc, ath_chan2mode(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.tx_chainmask != 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.tx_chainmask;
sc->sc_rx_chainmask = sc->sc_ah->ah_caps.rx_chainmask;
} 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.tx_chainmask;
sc->sc_rx_chainmask = ah->ah_caps.rx_chainmask;
/* 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.hw_caps & ATH9K_HW_CAP_GTT)
sc->sc_imask |= ATH9K_INT_GTT;
if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
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.hw_caps & ATH9K_HW_CAP_ENHANCEDPM) &&
(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_setcurmode(sc, ath_chan2mode(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_setcurmode(sc, ath_chan2mode(&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.hw_caps &
ATH9K_HW_CAP_AUTOSLEEP)) {
/* 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.rts_aggr_limit;
/* Get the hardware key cache size. */
sc->sc_keymax = ah->ah_caps.keycache_size;
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 */
ath_setup_rates(sc, IEEE80211_BAND_2GHZ);
ath_setup_rates(sc, IEEE80211_BAND_5GHZ);
/* NB: setup here so ath_rate_update is happy */
ath_setcurmode(sc, ATH9K_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, ATH9K_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, ATH9K_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, ATH9K_CAP_CIPHER,
ATH9K_CIPHER_TKIP, NULL)
&& ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
ATH9K_CIPHER_MIC, NULL)
&& ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT,
0, NULL))
sc->sc_splitmic = 1;
/* turn on mcast key search if possible */
if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
(void)ath9k_hw_setcapability(ah, ATH9K_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.hw_caps & ATH9K_HW_CAP_HT) {
sc->sc_txaggr = 1;
sc->sc_rxaggr = 1;
}
sc->sc_tx_chainmask = ah->ah_caps.tx_chainmask;
sc->sc_rx_chainmask = ah->ah_caps.rx_chainmask;
/* 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, ATH9K_CAP_DIVERSITY, 1, true, NULL);
sc->sc_defant = ath9k_hw_getdefantenna(ah);
ath9k_hw_getmac(ah, sc->sc_myaddr);
if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) {
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, ATH9K_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.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) {
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.hw_caps &
ATH9K_HW_CAP_4KB_SPLITTRANS)) {
/*
* 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;
}
/*
* 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;
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;
struct ath_rxbuf *rxbuf; /* re-ordering buffer */
struct timer_list timer;
spinlock_t tidlock;
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;
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 */
/* count to determine if descriptor should generate int on this txq. */
u32 axq_intrcnt;
bool stopped; /* Is mac80211 queue stopped ? */
struct ath_buf *axq_linkbuf; /* virtual addr of last buffer*/
/* first desc of the last descriptor that contains CTS */
struct ath_desc *axq_lastdsWithCTS;
/* final desc of the gating desc that determines whether
lastdsWithCTS has been DMA'ed or not */
struct ath_desc *axq_gatingds;
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;
struct ath_atx_ac *ac;
struct ath_buf *tx_buf[ATH_TID_MAX_BUFS]; /* active tx frames */
u16 seq_start;
u16 seq_next;
u16 baw_size;
int tidno;
int baw_head; /* first un-acked tx buffer */
int baw_tail; /* next unused tx buffer slot */
int sched;
int paused;
int cleanup_inprogress;
u32 addba_exchangecomplete:1;
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;
int if_id;
int qnum;
u32 ht:1;
u32 ps:1;
u32 use_minrate:1;
enum ath9k_pkt_type atype;
enum ath9k_key_type keytype;
u32 flags;
u16 seqno;
u16 tidno;
u16 txpower;
u16 frmlen;
u32 keyix;
int min_rate;
int mcast_rate;
u16 nextfraglen;
struct ath_softc *dev;
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_tx_queue_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;
atomic_t an_refcnt;
struct ath_chainmask_sel an_chainmask_sel;
struct ath_node_aggr an_aggr;
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 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;
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; /* txctl 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*/
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;
struct pci_dev *pdev;
void __iomem *mem;
struct tasklet_struct intr_tq;
struct tasklet_struct bcon_tasklet;
struct ath_config sc_config; /* load-time parameters */
int sc_debug;
struct ath_hal *sc_ah;
struct ath_rate_softc *sc_rc; /* tx rate control support */
u32 sc_intrstatus;
enum ath9k_opmode sc_opmode; /* current operating mode */
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;
u16 sc_curaid;
u8 sc_curbssid[ETH_ALEN];
u8 sc_myaddr[ETH_ALEN];
enum PROT_MODE sc_protmode;
u8 sc_mcastantenna;
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];
enum ath9k_int sc_imask;
u8 sc_bssidmask[ETH_ALEN];
u8 sc_defant; /* current default antenna */
u8 sc_rxotherant; /* rx's on non-default antenna */
u16 sc_cachelsz;
int sc_slotupdate; /* slot to next advance fsm */
int sc_slottime;
u8 sc_noreset;
int sc_bslot[ATH_BCBUF];
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
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;
/* RX */
struct list_head sc_rxbuf;
struct ath_descdma sc_rxdma;
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;
struct ath_txq sc_txq[ATH9K_NUM_TX_QUEUES];
struct ath_descdma sc_txdma;
u32 sc_txqsetup;
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_tx_queue_info sc_beacon_qi;
struct ath_descdma sc_bdma;
struct ath_txq *sc_cabq;
struct list_head sc_bbuf;
u32 sc_bhalq;
u32 sc_bmisscount;
u32 ast_be_xmit; /* beacons transmitted */
/* Rate */
struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX];
const struct ath9k_rate_table *sc_currates;
u8 sc_rixmap[256]; /* IEEE to h/w rate table ix */
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;
/* Locks */
spinlock_t sc_rxflushlock;
spinlock_t sc_rxbuflock;
spinlock_t sc_txbuflock;
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 */
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* 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 HW_H
#define HW_H
#include <linux/if_ether.h>
#include <linux/delay.h>
struct ar5416_desc {
u32 ds_link;
u32 ds_data;
u32 ds_ctl0;
u32 ds_ctl1;
union {
struct {
u32 ctl2;
u32 ctl3;
u32 ctl4;
u32 ctl5;
u32 ctl6;
u32 ctl7;
u32 ctl8;
u32 ctl9;
u32 ctl10;
u32 ctl11;
u32 status0;
u32 status1;
u32 status2;
u32 status3;
u32 status4;
u32 status5;
u32 status6;
u32 status7;
u32 status8;
u32 status9;
} tx;
struct {
u32 status0;
u32 status1;
u32 status2;
u32 status3;
u32 status4;
u32 status5;
u32 status6;
u32 status7;
u32 status8;
} rx;
} u;
} __packed;
#define AR5416DESC(_ds) ((struct ar5416_desc *)(_ds))
#define AR5416DESC_CONST(_ds) ((const struct ar5416_desc *)(_ds))
#define ds_ctl2 u.tx.ctl2
#define ds_ctl3 u.tx.ctl3
#define ds_ctl4 u.tx.ctl4
#define ds_ctl5 u.tx.ctl5
#define ds_ctl6 u.tx.ctl6
#define ds_ctl7 u.tx.ctl7
#define ds_ctl8 u.tx.ctl8
#define ds_ctl9 u.tx.ctl9
#define ds_ctl10 u.tx.ctl10
#define ds_ctl11 u.tx.ctl11
#define ds_txstatus0 u.tx.status0
#define ds_txstatus1 u.tx.status1
#define ds_txstatus2 u.tx.status2
#define ds_txstatus3 u.tx.status3
#define ds_txstatus4 u.tx.status4
#define ds_txstatus5 u.tx.status5
#define ds_txstatus6 u.tx.status6
#define ds_txstatus7 u.tx.status7
#define ds_txstatus8 u.tx.status8
#define ds_txstatus9 u.tx.status9
#define ds_rxstatus0 u.rx.status0
#define ds_rxstatus1 u.rx.status1
#define ds_rxstatus2 u.rx.status2
#define ds_rxstatus3 u.rx.status3
#define ds_rxstatus4 u.rx.status4
#define ds_rxstatus5 u.rx.status5
#define ds_rxstatus6 u.rx.status6
#define ds_rxstatus7 u.rx.status7
#define ds_rxstatus8 u.rx.status8
#define AR_FrameLen 0x00000fff
#define AR_VirtMoreFrag 0x00001000
#define AR_TxCtlRsvd00 0x0000e000
#define AR_XmitPower 0x003f0000
#define AR_XmitPower_S 16
#define AR_RTSEnable 0x00400000
#define AR_VEOL 0x00800000
#define AR_ClrDestMask 0x01000000
#define AR_TxCtlRsvd01 0x1e000000
#define AR_TxIntrReq 0x20000000
#define AR_DestIdxValid 0x40000000
#define AR_CTSEnable 0x80000000
#define AR_BufLen 0x00000fff
#define AR_TxMore 0x00001000
#define AR_DestIdx 0x000fe000
#define AR_DestIdx_S 13
#define AR_FrameType 0x00f00000
#define AR_FrameType_S 20
#define AR_NoAck 0x01000000
#define AR_InsertTS 0x02000000
#define AR_CorruptFCS 0x04000000
#define AR_ExtOnly 0x08000000
#define AR_ExtAndCtl 0x10000000
#define AR_MoreAggr 0x20000000
#define AR_IsAggr 0x40000000
#define AR_BurstDur 0x00007fff
#define AR_BurstDur_S 0
#define AR_DurUpdateEna 0x00008000
#define AR_XmitDataTries0 0x000f0000
#define AR_XmitDataTries0_S 16
#define AR_XmitDataTries1 0x00f00000
#define AR_XmitDataTries1_S 20
#define AR_XmitDataTries2 0x0f000000
#define AR_XmitDataTries2_S 24
#define AR_XmitDataTries3 0xf0000000
#define AR_XmitDataTries3_S 28
#define AR_XmitRate0 0x000000ff
#define AR_XmitRate0_S 0
#define AR_XmitRate1 0x0000ff00
#define AR_XmitRate1_S 8
#define AR_XmitRate2 0x00ff0000
#define AR_XmitRate2_S 16
#define AR_XmitRate3 0xff000000
#define AR_XmitRate3_S 24
#define AR_PacketDur0 0x00007fff
#define AR_PacketDur0_S 0
#define AR_RTSCTSQual0 0x00008000
#define AR_PacketDur1 0x7fff0000
#define AR_PacketDur1_S 16
#define AR_RTSCTSQual1 0x80000000
#define AR_PacketDur2 0x00007fff
#define AR_PacketDur2_S 0
#define AR_RTSCTSQual2 0x00008000
#define AR_PacketDur3 0x7fff0000
#define AR_PacketDur3_S 16
#define AR_RTSCTSQual3 0x80000000
#define AR_AggrLen 0x0000ffff
#define AR_AggrLen_S 0
#define AR_TxCtlRsvd60 0x00030000
#define AR_PadDelim 0x03fc0000
#define AR_PadDelim_S 18
#define AR_EncrType 0x0c000000
#define AR_EncrType_S 26
#define AR_TxCtlRsvd61 0xf0000000
#define AR_2040_0 0x00000001
#define AR_GI0 0x00000002
#define AR_ChainSel0 0x0000001c
#define AR_ChainSel0_S 2
#define AR_2040_1 0x00000020
#define AR_GI1 0x00000040
#define AR_ChainSel1 0x00000380
#define AR_ChainSel1_S 7
#define AR_2040_2 0x00000400
#define AR_GI2 0x00000800
#define AR_ChainSel2 0x00007000
#define AR_ChainSel2_S 12
#define AR_2040_3 0x00008000
#define AR_GI3 0x00010000
#define AR_ChainSel3 0x000e0000
#define AR_ChainSel3_S 17
#define AR_RTSCTSRate 0x0ff00000
#define AR_RTSCTSRate_S 20
#define AR_TxCtlRsvd70 0xf0000000
#define AR_TxRSSIAnt00 0x000000ff
#define AR_TxRSSIAnt00_S 0
#define AR_TxRSSIAnt01 0x0000ff00
#define AR_TxRSSIAnt01_S 8
#define AR_TxRSSIAnt02 0x00ff0000
#define AR_TxRSSIAnt02_S 16
#define AR_TxStatusRsvd00 0x3f000000
#define AR_TxBaStatus 0x40000000
#define AR_TxStatusRsvd01 0x80000000
#define AR_FrmXmitOK 0x00000001
#define AR_ExcessiveRetries 0x00000002
#define AR_FIFOUnderrun 0x00000004
#define AR_Filtered 0x00000008
#define AR_RTSFailCnt 0x000000f0
#define AR_RTSFailCnt_S 4
#define AR_DataFailCnt 0x00000f00
#define AR_DataFailCnt_S 8
#define AR_VirtRetryCnt 0x0000f000
#define AR_VirtRetryCnt_S 12
#define AR_TxDelimUnderrun 0x00010000
#define AR_TxDataUnderrun 0x00020000
#define AR_DescCfgErr 0x00040000
#define AR_TxTimerExpired 0x00080000
#define AR_TxStatusRsvd10 0xfff00000
#define AR_SendTimestamp ds_txstatus2
#define AR_BaBitmapLow ds_txstatus3
#define AR_BaBitmapHigh ds_txstatus4
#define AR_TxRSSIAnt10 0x000000ff
#define AR_TxRSSIAnt10_S 0
#define AR_TxRSSIAnt11 0x0000ff00
#define AR_TxRSSIAnt11_S 8
#define AR_TxRSSIAnt12 0x00ff0000
#define AR_TxRSSIAnt12_S 16
#define AR_TxRSSICombined 0xff000000
#define AR_TxRSSICombined_S 24
#define AR_TxEVM0 ds_txstatus5
#define AR_TxEVM1 ds_txstatus6
#define AR_TxEVM2 ds_txstatus7
#define AR_TxDone 0x00000001
#define AR_SeqNum 0x00001ffe
#define AR_SeqNum_S 1
#define AR_TxStatusRsvd80 0x0001e000
#define AR_TxOpExceeded 0x00020000
#define AR_TxStatusRsvd81 0x001c0000
#define AR_FinalTxIdx 0x00600000
#define AR_FinalTxIdx_S 21
#define AR_TxStatusRsvd82 0x01800000
#define AR_PowerMgmt 0x02000000
#define AR_TxStatusRsvd83 0xfc000000
#define AR_RxCTLRsvd00 0xffffffff
#define AR_BufLen 0x00000fff
#define AR_RxCtlRsvd00 0x00001000
#define AR_RxIntrReq 0x00002000
#define AR_RxCtlRsvd01 0xffffc000
#define AR_RxRSSIAnt00 0x000000ff
#define AR_RxRSSIAnt00_S 0
#define AR_RxRSSIAnt01 0x0000ff00
#define AR_RxRSSIAnt01_S 8
#define AR_RxRSSIAnt02 0x00ff0000
#define AR_RxRSSIAnt02_S 16
#define AR_RxRate 0xff000000
#define AR_RxRate_S 24
#define AR_RxStatusRsvd00 0xff000000
#define AR_DataLen 0x00000fff
#define AR_RxMore 0x00001000
#define AR_NumDelim 0x003fc000
#define AR_NumDelim_S 14
#define AR_RxStatusRsvd10 0xff800000
#define AR_RcvTimestamp ds_rxstatus2
#define AR_GI 0x00000001
#define AR_2040 0x00000002
#define AR_Parallel40 0x00000004
#define AR_Parallel40_S 2
#define AR_RxStatusRsvd30 0x000000f8
#define AR_RxAntenna 0xffffff00
#define AR_RxAntenna_S 8
#define AR_RxRSSIAnt10 0x000000ff
#define AR_RxRSSIAnt10_S 0
#define AR_RxRSSIAnt11 0x0000ff00
#define AR_RxRSSIAnt11_S 8
#define AR_RxRSSIAnt12 0x00ff0000
#define AR_RxRSSIAnt12_S 16
#define AR_RxRSSICombined 0xff000000
#define AR_RxRSSICombined_S 24
#define AR_RxEVM0 ds_rxstatus4
#define AR_RxEVM1 ds_rxstatus5
#define AR_RxEVM2 ds_rxstatus6
#define AR_RxDone 0x00000001
#define AR_RxFrameOK 0x00000002
#define AR_CRCErr 0x00000004
#define AR_DecryptCRCErr 0x00000008
#define AR_PHYErr 0x00000010
#define AR_MichaelErr 0x00000020
#define AR_PreDelimCRCErr 0x00000040
#define AR_RxStatusRsvd70 0x00000080
#define AR_RxKeyIdxValid 0x00000100
#define AR_KeyIdx 0x0000fe00
#define AR_KeyIdx_S 9
#define AR_PHYErrCode 0x0000ff00
#define AR_PHYErrCode_S 8
#define AR_RxMoreAggr 0x00010000
#define AR_RxAggr 0x00020000
#define AR_PostDelimCRCErr 0x00040000
#define AR_RxStatusRsvd71 0x3ff80000
#define AR_DecryptBusyErr 0x40000000
#define AR_KeyMiss 0x80000000
#define AR5416_MAGIC 0x19641014
#define RXSTATUS_RATE(ah, ads) (AR_SREV_5416_V20_OR_LATER(ah) ? \
MS(ads->ds_rxstatus0, AR_RxRate) : \
(ads->ds_rxstatus3 >> 2) & 0xFF)
#define RXSTATUS_DUPLICATE(ah, ads) (AR_SREV_5416_V20_OR_LATER(ah) ? \
MS(ads->ds_rxstatus3, AR_Parallel40) : \
(ads->ds_rxstatus3 >> 10) & 0x1)
#define set11nTries(_series, _index) \
(SM((_series)[_index].Tries, AR_XmitDataTries##_index))
#define set11nRate(_series, _index) \
(SM((_series)[_index].Rate, AR_XmitRate##_index))
#define set11nPktDurRTSCTS(_series, _index) \
(SM((_series)[_index].PktDuration, AR_PacketDur##_index) | \
((_series)[_index].RateFlags & ATH9K_RATESERIES_RTS_CTS ? \
AR_RTSCTSQual##_index : 0))
#define set11nRateFlags(_series, _index) \
(((_series)[_index].RateFlags & ATH9K_RATESERIES_2040 ? \
AR_2040_##_index : 0) \
|((_series)[_index].RateFlags & ATH9K_RATESERIES_HALFGI ? \
AR_GI##_index : 0) \
|SM((_series)[_index].ChSel, AR_ChainSel##_index))
#define AR_SREV_9100(ah) ((ah->ah_macVersion) == AR_SREV_VERSION_9100)
#define INIT_CONFIG_STATUS 0x00000000
#define INIT_RSSI_THR 0x00000700
#define INIT_BCON_CNTRL_REG 0x00000000
#define MIN_TX_FIFO_THRESHOLD 0x1
#define MAX_TX_FIFO_THRESHOLD ((4096 / 64) - 1)
#define INIT_TX_FIFO_THRESHOLD MIN_TX_FIFO_THRESHOLD
#define NUM_CORNER_FIX_BITS_2133 7
#define CCK_OFDM_GAIN_DELTA 15
struct ar5416AniState {
struct ath9k_channel c;
u8 noiseImmunityLevel;
u8 spurImmunityLevel;
u8 firstepLevel;
u8 ofdmWeakSigDetectOff;
u8 cckWeakSigThreshold;
u32 listenTime;
u32 ofdmTrigHigh;
u32 ofdmTrigLow;
int32_t cckTrigHigh;
int32_t cckTrigLow;
int32_t rssiThrLow;
int32_t rssiThrHigh;
u32 noiseFloor;
u32 txFrameCount;
u32 rxFrameCount;
u32 cycleCount;
u32 ofdmPhyErrCount;
u32 cckPhyErrCount;
u32 ofdmPhyErrBase;
u32 cckPhyErrBase;
int16_t pktRssi[2];
int16_t ofdmErrRssi[2];
int16_t cckErrRssi[2];
};
#define HAL_PROCESS_ANI 0x00000001
#define HAL_RADAR_EN 0x80000000
#define HAL_AR_EN 0x40000000
#define DO_ANI(ah) \
((AH5416(ah)->ah_procPhyErr & HAL_PROCESS_ANI))
struct ar5416Stats {
u32 ast_ani_niup;
u32 ast_ani_nidown;
u32 ast_ani_spurup;
u32 ast_ani_spurdown;
u32 ast_ani_ofdmon;
u32 ast_ani_ofdmoff;
u32 ast_ani_cckhigh;
u32 ast_ani_ccklow;
u32 ast_ani_stepup;
u32 ast_ani_stepdown;
u32 ast_ani_ofdmerrs;
u32 ast_ani_cckerrs;
u32 ast_ani_reset;
u32 ast_ani_lzero;
u32 ast_ani_lneg;
struct ath9k_mib_stats ast_mibstats;
struct ath9k_node_stats ast_nodestats;
};
#define AR5416_OPFLAGS_11A 0x01
#define AR5416_OPFLAGS_11G 0x02
#define AR5416_OPFLAGS_N_5G_HT40 0x04
#define AR5416_OPFLAGS_N_2G_HT40 0x08
#define AR5416_OPFLAGS_N_5G_HT20 0x10
#define AR5416_OPFLAGS_N_2G_HT20 0x20
#define EEP_RFSILENT_ENABLED 0x0001
#define EEP_RFSILENT_ENABLED_S 0
#define EEP_RFSILENT_POLARITY 0x0002
#define EEP_RFSILENT_POLARITY_S 1
#define EEP_RFSILENT_GPIO_SEL 0x001c
#define EEP_RFSILENT_GPIO_SEL_S 2
#define AR5416_EEP_NO_BACK_VER 0x1
#define AR5416_EEP_VER 0xE
#define AR5416_EEP_VER_MINOR_MASK 0x0FFF
#define AR5416_EEP_MINOR_VER_2 0x2
#define AR5416_EEP_MINOR_VER_3 0x3
#define AR5416_EEP_MINOR_VER_7 0x7
#define AR5416_EEP_MINOR_VER_9 0x9
#define AR5416_EEP_START_LOC 256
#define AR5416_NUM_5G_CAL_PIERS 8
#define AR5416_NUM_2G_CAL_PIERS 4
#define AR5416_NUM_5G_20_TARGET_POWERS 8
#define AR5416_NUM_5G_40_TARGET_POWERS 8
#define AR5416_NUM_2G_CCK_TARGET_POWERS 3
#define AR5416_NUM_2G_20_TARGET_POWERS 4
#define AR5416_NUM_2G_40_TARGET_POWERS 4
#define AR5416_NUM_CTLS 24
#define AR5416_NUM_BAND_EDGES 8
#define AR5416_NUM_PD_GAINS 4
#define AR5416_PD_GAINS_IN_MASK 4
#define AR5416_PD_GAIN_ICEPTS 5
#define AR5416_EEPROM_MODAL_SPURS 5
#define AR5416_MAX_RATE_POWER 63
#define AR5416_NUM_PDADC_VALUES 128
#define AR5416_NUM_RATES 16
#define AR5416_BCHAN_UNUSED 0xFF
#define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64
#define AR5416_EEPMISC_BIG_ENDIAN 0x01
#define AR5416_MAX_CHAINS 3
#define AR5416_ANT_16S 25
#define AR5416_NUM_ANT_CHAIN_FIELDS 7
#define AR5416_NUM_ANT_COMMON_FIELDS 4
#define AR5416_SIZE_ANT_CHAIN_FIELD 3
#define AR5416_SIZE_ANT_COMMON_FIELD 4
#define AR5416_ANT_CHAIN_MASK 0x7
#define AR5416_ANT_COMMON_MASK 0xf
#define AR5416_CHAIN_0_IDX 0
#define AR5416_CHAIN_1_IDX 1
#define AR5416_CHAIN_2_IDX 2
#define AR5416_PWR_TABLE_OFFSET -5
#define AR5416_LEGACY_CHAINMASK 1
enum eeprom_param {
EEP_NFTHRESH_5,
EEP_NFTHRESH_2,
EEP_MAC_MSW,
EEP_MAC_MID,
EEP_MAC_LSW,
EEP_REG_0,
EEP_REG_1,
EEP_OP_CAP,
EEP_OP_MODE,
EEP_RF_SILENT,
EEP_OB_5,
EEP_DB_5,
EEP_OB_2,
EEP_DB_2,
EEP_MINOR_REV,
EEP_TX_MASK,
EEP_RX_MASK,
};
enum ar5416_rates {
rate6mb, rate9mb, rate12mb, rate18mb,
rate24mb, rate36mb, rate48mb, rate54mb,
rate1l, rate2l, rate2s, rate5_5l,
rate5_5s, rate11l, rate11s, rateXr,
rateHt20_0, rateHt20_1, rateHt20_2, rateHt20_3,
rateHt20_4, rateHt20_5, rateHt20_6, rateHt20_7,
rateHt40_0, rateHt40_1, rateHt40_2, rateHt40_3,
rateHt40_4, rateHt40_5, rateHt40_6, rateHt40_7,
rateDupCck, rateDupOfdm, rateExtCck, rateExtOfdm,
Ar5416RateSize
};
struct base_eep_header {
u16 length;
u16 checksum;
u16 version;
u8 opCapFlags;
u8 eepMisc;
u16 regDmn[2];
u8 macAddr[6];
u8 rxMask;
u8 txMask;
u16 rfSilent;
u16 blueToothOptions;
u16 deviceCap;
u32 binBuildNumber;
u8 deviceType;
u8 pwdclkind;
u8 futureBase[32];
} __packed;
struct spur_chan {
u16 spurChan;
u8 spurRangeLow;
u8 spurRangeHigh;
} __packed;
struct modal_eep_header {
u32 antCtrlChain[AR5416_MAX_CHAINS];
u32 antCtrlCommon;
u8 antennaGainCh[AR5416_MAX_CHAINS];
u8 switchSettling;
u8 txRxAttenCh[AR5416_MAX_CHAINS];
u8 rxTxMarginCh[AR5416_MAX_CHAINS];
u8 adcDesiredSize;
u8 pgaDesiredSize;
u8 xlnaGainCh[AR5416_MAX_CHAINS];
u8 txEndToXpaOff;
u8 txEndToRxOn;
u8 txFrameToXpaOn;
u8 thresh62;
u8 noiseFloorThreshCh[AR5416_MAX_CHAINS];
u8 xpdGain;
u8 xpd;
u8 iqCalICh[AR5416_MAX_CHAINS];
u8 iqCalQCh[AR5416_MAX_CHAINS];
u8 pdGainOverlap;
u8 ob;
u8 db;
u8 xpaBiasLvl;
u8 pwrDecreaseFor2Chain;
u8 pwrDecreaseFor3Chain;
u8 txFrameToDataStart;
u8 txFrameToPaOn;
u8 ht40PowerIncForPdadc;
u8 bswAtten[AR5416_MAX_CHAINS];
u8 bswMargin[AR5416_MAX_CHAINS];
u8 swSettleHt40;
u8 xatten2Db[AR5416_MAX_CHAINS];
u8 xatten2Margin[AR5416_MAX_CHAINS];
u8 ob_ch1;
u8 db_ch1;
u8 useAnt1:1,
force_xpaon:1,
local_bias:1,
femBandSelectUsed:1, xlnabufin:1, xlnaisel:2, xlnabufmode:1;
u8 futureModalar9280;
u16 xpaBiasLvlFreq[3];
u8 futureModal[6];
struct spur_chan spurChans[AR5416_EEPROM_MODAL_SPURS];
} __packed;
struct cal_data_per_freq {
u8 pwrPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
u8 vpdPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
} __packed;
struct cal_target_power_leg {
u8 bChannel;
u8 tPow2x[4];
} __packed;
struct cal_target_power_ht {
u8 bChannel;
u8 tPow2x[8];
} __packed;
#ifdef __BIG_ENDIAN_BITFIELD
struct cal_ctl_edges {
u8 bChannel;
u8 flag:2, tPower:6;
} __packed;
#else
struct cal_ctl_edges {
u8 bChannel;
u8 tPower:6, flag:2;
} __packed;
#endif
struct cal_ctl_data {
struct cal_ctl_edges
ctlEdges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES];
} __packed;
struct ar5416_eeprom {
struct base_eep_header baseEepHeader;
u8 custData[64];
struct modal_eep_header modalHeader[2];
u8 calFreqPier5G[AR5416_NUM_5G_CAL_PIERS];
u8 calFreqPier2G[AR5416_NUM_2G_CAL_PIERS];
struct cal_data_per_freq
calPierData5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS];
struct cal_data_per_freq
calPierData2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS];
struct cal_target_power_leg
calTargetPower5G[AR5416_NUM_5G_20_TARGET_POWERS];
struct cal_target_power_ht
calTargetPower5GHT20[AR5416_NUM_5G_20_TARGET_POWERS];
struct cal_target_power_ht
calTargetPower5GHT40[AR5416_NUM_5G_40_TARGET_POWERS];
struct cal_target_power_leg
calTargetPowerCck[AR5416_NUM_2G_CCK_TARGET_POWERS];
struct cal_target_power_leg
calTargetPower2G[AR5416_NUM_2G_20_TARGET_POWERS];
struct cal_target_power_ht
calTargetPower2GHT20[AR5416_NUM_2G_20_TARGET_POWERS];
struct cal_target_power_ht
calTargetPower2GHT40[AR5416_NUM_2G_40_TARGET_POWERS];
u8 ctlIndex[AR5416_NUM_CTLS];
struct cal_ctl_data ctlData[AR5416_NUM_CTLS];
u8 padding;
} __packed;
struct ar5416IniArray {
u32 *ia_array;
u32 ia_rows;
u32 ia_columns;
};
#define INIT_INI_ARRAY(iniarray, array, rows, columns) do { \
(iniarray)->ia_array = (u32 *)(array); \
(iniarray)->ia_rows = (rows); \
(iniarray)->ia_columns = (columns); \
} while (0)
#define INI_RA(iniarray, row, column) \
(((iniarray)->ia_array)[(row) * ((iniarray)->ia_columns) + (column)])
#define INIT_CAL(_perCal) do { \
(_perCal)->calState = CAL_WAITING; \
(_perCal)->calNext = NULL; \
} while (0)
#define INSERT_CAL(_ahp, _perCal) \
do { \
if ((_ahp)->ah_cal_list_last == NULL) { \
(_ahp)->ah_cal_list = \
(_ahp)->ah_cal_list_last = (_perCal); \
((_ahp)->ah_cal_list_last)->calNext = (_perCal); \
} else { \
((_ahp)->ah_cal_list_last)->calNext = (_perCal); \
(_ahp)->ah_cal_list_last = (_perCal); \
(_perCal)->calNext = (_ahp)->ah_cal_list; \
} \
} while (0)
enum hal_cal_types {
ADC_DC_INIT_CAL = 0x1,
ADC_GAIN_CAL = 0x2,
ADC_DC_CAL = 0x4,
IQ_MISMATCH_CAL = 0x8
};
enum hal_cal_state {
CAL_INACTIVE,
CAL_WAITING,
CAL_RUNNING,
CAL_DONE
};
#define MIN_CAL_SAMPLES 1
#define MAX_CAL_SAMPLES 64
#define INIT_LOG_COUNT 5
#define PER_MIN_LOG_COUNT 2
#define PER_MAX_LOG_COUNT 10
struct hal_percal_data {
enum hal_cal_types calType;
u32 calNumSamples;
u32 calCountMax;
void (*calCollect) (struct ath_hal *);
void (*calPostProc) (struct ath_hal *, u8);
};
struct hal_cal_list {
const struct hal_percal_data *calData;
enum hal_cal_state calState;
struct hal_cal_list *calNext;
};
struct ath_hal_5416 {
struct ath_hal ah;
struct ar5416_eeprom ah_eeprom;
u8 ah_macaddr[ETH_ALEN];
u8 ah_bssid[ETH_ALEN];
u8 ah_bssidmask[ETH_ALEN];
u16 ah_assocId;
int16_t ah_curchanRadIndex;
u32 ah_maskReg;
struct ar5416Stats ah_stats;
u32 ah_txDescMask;
u32 ah_txOkInterruptMask;
u32 ah_txErrInterruptMask;
u32 ah_txDescInterruptMask;
u32 ah_txEolInterruptMask;
u32 ah_txUrnInterruptMask;
struct ath9k_tx_queue_info ah_txq[ATH9K_NUM_TX_QUEUES];
enum ath9k_power_mode ah_powerMode;
bool ah_chipFullSleep;
u32 ah_atimWindow;
enum ath9k_ant_setting ah_diversityControl;
u16 ah_antennaSwitchSwap;
enum hal_cal_types ah_suppCals;
struct hal_cal_list ah_iqCalData;
struct hal_cal_list ah_adcGainCalData;
struct hal_cal_list ah_adcDcCalInitData;
struct hal_cal_list ah_adcDcCalData;
struct hal_cal_list *ah_cal_list;
struct hal_cal_list *ah_cal_list_last;
struct hal_cal_list *ah_cal_list_curr;
#define ah_totalPowerMeasI ah_Meas0.unsign
#define ah_totalPowerMeasQ ah_Meas1.unsign
#define ah_totalIqCorrMeas ah_Meas2.sign
#define ah_totalAdcIOddPhase ah_Meas0.unsign
#define ah_totalAdcIEvenPhase ah_Meas1.unsign
#define ah_totalAdcQOddPhase ah_Meas2.unsign
#define ah_totalAdcQEvenPhase ah_Meas3.unsign
#define ah_totalAdcDcOffsetIOddPhase ah_Meas0.sign
#define ah_totalAdcDcOffsetIEvenPhase ah_Meas1.sign
#define ah_totalAdcDcOffsetQOddPhase ah_Meas2.sign
#define ah_totalAdcDcOffsetQEvenPhase ah_Meas3.sign
union {
u32 unsign[AR5416_MAX_CHAINS];
int32_t sign[AR5416_MAX_CHAINS];
} ah_Meas0;
union {
u32 unsign[AR5416_MAX_CHAINS];
int32_t sign[AR5416_MAX_CHAINS];
} ah_Meas1;
union {
u32 unsign[AR5416_MAX_CHAINS];
int32_t sign[AR5416_MAX_CHAINS];
} ah_Meas2;
union {
u32 unsign[AR5416_MAX_CHAINS];
int32_t sign[AR5416_MAX_CHAINS];
} ah_Meas3;
u16 ah_CalSamples;
u32 ah_tx6PowerInHalfDbm;
u32 ah_staId1Defaults;
u32 ah_miscMode;
bool ah_tpcEnabled;
u32 ah_beaconInterval;
enum {
AUTO_32KHZ,
USE_32KHZ,
DONT_USE_32KHZ,
} ah_enable32kHzClock;
u32 *ah_analogBank0Data;
u32 *ah_analogBank1Data;
u32 *ah_analogBank2Data;
u32 *ah_analogBank3Data;
u32 *ah_analogBank6Data;
u32 *ah_analogBank6TPCData;
u32 *ah_analogBank7Data;
u32 *ah_addac5416_21;
u32 *ah_bank6Temp;
u32 ah_ofdmTxPower;
int16_t ah_txPowerIndexOffset;
u32 ah_slottime;
u32 ah_acktimeout;
u32 ah_ctstimeout;
u32 ah_globaltxtimeout;
u8 ah_gBeaconRate;
u32 ah_gpioSelect;
u32 ah_polarity;
u32 ah_gpioBit;
bool ah_eepEnabled;
u32 ah_procPhyErr;
bool ah_hasHwPhyCounters;
u32 ah_aniPeriod;
struct ar5416AniState *ah_curani;
struct ar5416AniState ah_ani[255];
int ah_totalSizeDesired[5];
int ah_coarseHigh[5];
int ah_coarseLow[5];
int ah_firpwr[5];
u16 ah_ratesArray[16];
u32 ah_intrTxqs;
bool ah_intrMitigation;
u32 ah_cycleCount;
u32 ah_ctlBusy;
u32 ah_extBusy;
enum ath9k_ht_extprotspacing ah_extprotspacing;
u8 ah_txchainmask;
u8 ah_rxchainmask;
int ah_hwp;
void __iomem *ah_cal_mem;
enum ath9k_ani_cmd ah_ani_function;
struct ar5416IniArray ah_iniModes;
struct ar5416IniArray ah_iniCommon;
struct ar5416IniArray ah_iniBank0;
struct ar5416IniArray ah_iniBB_RfGain;
struct ar5416IniArray ah_iniBank1;
struct ar5416IniArray ah_iniBank2;
struct ar5416IniArray ah_iniBank3;
struct ar5416IniArray ah_iniBank6;
struct ar5416IniArray ah_iniBank6TPC;
struct ar5416IniArray ah_iniBank7;
struct ar5416IniArray ah_iniAddac;
struct ar5416IniArray ah_iniPcieSerdes;
struct ar5416IniArray ah_iniModesAdditional;
};
#define AH5416(_ah) ((struct ath_hal_5416 *)(_ah))
#define FREQ2FBIN(x, y) ((y) ? ((x) - 2300) : (((x) - 4800) / 5))
#define IS_5416_EMU(ah) \
((ah->ah_devid == AR5416_DEVID_EMU) || \
(ah->ah_devid == AR5416_DEVID_EMU_PCIE))
#define ar5416RfDetach(ah) do { \
if (AH5416(ah)->ah_rfHal.rfDetach != NULL) \
AH5416(ah)->ah_rfHal.rfDetach(ah); \
} while (0)
#define ath9k_hw_use_flash(_ah) \
(!(_ah->ah_flags & AH_USE_EEPROM))
#define DO_DELAY(x) do { \
if ((++(x) % 64) == 0) \
udelay(1); \
} while (0)
#define REG_WRITE_ARRAY(iniarray, column, regWr) do { \
int r; \
for (r = 0; r < ((iniarray)->ia_rows); r++) { \
REG_WRITE(ah, INI_RA((iniarray), (r), 0), \
INI_RA((iniarray), r, (column))); \
DO_DELAY(regWr); \
} \
} while (0)
#define BASE_ACTIVATE_DELAY 100
#define RTC_PLL_SETTLE_DELAY 1000
#define COEF_SCALE_S 24
#define HT40_CHANNEL_CENTER_SHIFT 10
#define ar5416CheckOpMode(_opmode) \
((_opmode == ATH9K_M_STA) || (_opmode == ATH9K_M_IBSS) || \
(_opmode == ATH9K_M_HOSTAP) || (_opmode == ATH9K_M_MONITOR))
#define AR5416_EEPROM_MAGIC_OFFSET 0x0
#define AR5416_EEPROM_S 2
#define AR5416_EEPROM_OFFSET 0x2000
#define AR5416_EEPROM_START_ADDR \
(AR_SREV_9100(ah)) ? 0x1fff1000 : 0x503f1200
#define AR5416_EEPROM_MAX 0xae0
#define ar5416_get_eep_ver(_ahp) \
(((_ahp)->ah_eeprom.baseEepHeader.version >> 12) & 0xF)
#define ar5416_get_eep_rev(_ahp) \
(((_ahp)->ah_eeprom.baseEepHeader.version) & 0xFFF)
#define ar5416_get_ntxchains(_txchainmask) \
(((_txchainmask >> 2) & 1) + \
((_txchainmask >> 1) & 1) + (_txchainmask & 1))
#define IS_EEP_MINOR_V3(_ahp) \
(ath9k_hw_get_eeprom((_ahp), EEP_MINOR_REV) >= AR5416_EEP_MINOR_VER_3)
#define FIXED_CCA_THRESHOLD 15
#ifdef __BIG_ENDIAN
#define AR5416_EEPROM_MAGIC 0x5aa5
#else
#define AR5416_EEPROM_MAGIC 0xa55a
#endif
#define ATH9K_POW_SM(_r, _s) (((_r) & 0x3f) << (_s))
#define ATH9K_ANTENNA0_CHAINMASK 0x1
#define ATH9K_ANTENNA1_CHAINMASK 0x2
#define ATH9K_NUM_DMA_DEBUG_REGS 8
#define ATH9K_NUM_QUEUES 10
#define HAL_NOISE_IMMUNE_MAX 4
#define HAL_SPUR_IMMUNE_MAX 7
#define HAL_FIRST_STEP_MAX 2
#define ATH9K_ANI_OFDM_TRIG_HIGH 500
#define ATH9K_ANI_OFDM_TRIG_LOW 200
#define ATH9K_ANI_CCK_TRIG_HIGH 200
#define ATH9K_ANI_CCK_TRIG_LOW 100
#define ATH9K_ANI_NOISE_IMMUNE_LVL 4
#define ATH9K_ANI_USE_OFDM_WEAK_SIG true
#define ATH9K_ANI_CCK_WEAK_SIG_THR false
#define ATH9K_ANI_SPUR_IMMUNE_LVL 7
#define ATH9K_ANI_FIRSTEP_LVL 0
#define ATH9K_ANI_RSSI_THR_HIGH 40
#define ATH9K_ANI_RSSI_THR_LOW 7
#define ATH9K_ANI_PERIOD 100
#define AR_GPIOD_MASK 0x00001FFF
#define AR_GPIO_BIT(_gpio) (1 << (_gpio))
#define MAX_ANALOG_START 319
#define HAL_EP_RND(x, mul) \
((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
#define BEACON_RSSI(ahp) \
HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \
ATH9K_RSSI_EP_MULTIPLIER)
#define ah_mibStats ah_stats.ast_mibstats
#define AH_TIMEOUT 100000
#define AH_TIME_QUANTUM 10
#define IS(_c, _f) (((_c)->channelFlags & _f) || 0)
#define AR_KEYTABLE_SIZE 128
#define POWER_UP_TIME 200000
#define EXT_ADDITIVE (0x8000)
#define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE)
#define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE)
#define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE)
#define SUB_NUM_CTL_MODES_AT_5G_40 2
#define SUB_NUM_CTL_MODES_AT_2G_40 3
#define SPUR_RSSI_THRESH 40
#define TU_TO_USEC(_tu) ((_tu) << 10)
#define CAB_TIMEOUT_VAL 10
#define BEACON_TIMEOUT_VAL 10
#define MIN_BEACON_TIMEOUT_VAL 1
#define SLEEP_SLOP 3
#define CCK_SIFS_TIME 10
#define CCK_PREAMBLE_BITS 144
#define CCK_PLCP_BITS 48
#define OFDM_SIFS_TIME 16
#define OFDM_PREAMBLE_TIME 20
#define OFDM_PLCP_BITS 22
#define OFDM_SYMBOL_TIME 4
#define OFDM_SIFS_TIME_HALF 32
#define OFDM_PREAMBLE_TIME_HALF 40
#define OFDM_PLCP_BITS_HALF 22
#define OFDM_SYMBOL_TIME_HALF 8
#define OFDM_SIFS_TIME_QUARTER 64
#define OFDM_PREAMBLE_TIME_QUARTER 80
#define OFDM_PLCP_BITS_QUARTER 22
#define OFDM_SYMBOL_TIME_QUARTER 16
u32 ath9k_hw_get_eeprom(struct ath_hal_5416 *ahp,
enum eeprom_param param);
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* 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.
*/
/* mac80211 and PCI callbacks */
#include <linux/nl80211.h>
#include "core.h"
#define ATH_PCI_VERSION "0.1"
#define IEEE80211_HTCAP_MAXRXAMPDU_FACTOR 13
#define IEEE80211_ACTION_CAT_HT 7
#define IEEE80211_ACTION_HT_TXCHWIDTH 0
static char *dev_info = "ath9k";
MODULE_AUTHOR("Atheros Communications");
MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
MODULE_LICENSE("Dual BSD/GPL");
static struct pci_device_id ath_pci_id_table[] __devinitdata = {
{ PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
{ PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
{ 0 }
};
static int ath_get_channel(struct ath_softc *sc,
struct ieee80211_channel *chan)
{
int i;
for (i = 0; i < sc->sc_ah->ah_nchan; i++) {
if (sc->sc_ah->ah_channels[i].channel == chan->center_freq)
return i;
}
return -1;
}
static u32 ath_get_extchanmode(struct ath_softc *sc,
struct ieee80211_channel *chan)
{
u32 chanmode = 0;
u8 ext_chan_offset = sc->sc_ht_info.ext_chan_offset;
enum ath9k_ht_macmode tx_chan_width = sc->sc_ht_info.tx_chan_width;
switch (chan->band) {
case IEEE80211_BAND_2GHZ:
if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_NONE) &&
(tx_chan_width == ATH9K_HT_MACMODE_20))
chanmode = CHANNEL_G_HT20;
if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_ABOVE) &&
(tx_chan_width == ATH9K_HT_MACMODE_2040))
chanmode = CHANNEL_G_HT40PLUS;
if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_BELOW) &&
(tx_chan_width == ATH9K_HT_MACMODE_2040))
chanmode = CHANNEL_G_HT40MINUS;
break;
case IEEE80211_BAND_5GHZ:
if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_NONE) &&
(tx_chan_width == ATH9K_HT_MACMODE_20))
chanmode = CHANNEL_A_HT20;
if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_ABOVE) &&
(tx_chan_width == ATH9K_HT_MACMODE_2040))
chanmode = CHANNEL_A_HT40PLUS;
if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_BELOW) &&
(tx_chan_width == ATH9K_HT_MACMODE_2040))
chanmode = CHANNEL_A_HT40MINUS;
break;
default:
break;
}
return chanmode;
}
static int ath_setkey_tkip(struct ath_softc *sc,
struct ieee80211_key_conf *key,
struct ath9k_keyval *hk,
const u8 *addr)
{
u8 *key_rxmic = NULL;
u8 *key_txmic = NULL;
key_txmic = key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
key_rxmic = key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
if (addr == NULL) {
/* Group key installation */
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
return ath_keyset(sc, key->keyidx, hk, addr);
}
if (!sc->sc_splitmic) {
/*
* data key goes at first index,
* the hal handles the MIC keys at index+64.
*/
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
return ath_keyset(sc, key->keyidx, hk, addr);
}
/*
* TX key goes at first index, RX key at +32.
* The hal handles the MIC keys at index+64.
*/
memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
if (!ath_keyset(sc, key->keyidx, hk, NULL)) {
/* Txmic entry failed. No need to proceed further */
DPRINTF(sc, ATH_DBG_KEYCACHE,
"%s Setting TX MIC Key Failed\n", __func__);
return 0;
}
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
/* XXX delete tx key on failure? */
return ath_keyset(sc, key->keyidx+32, hk, addr);
}
static int ath_key_config(struct ath_softc *sc,
const u8 *addr,
struct ieee80211_key_conf *key)
{
struct ieee80211_vif *vif;
struct ath9k_keyval hk;
const u8 *mac = NULL;
int ret = 0;
enum ieee80211_if_types opmode;
memset(&hk, 0, sizeof(hk));
switch (key->alg) {
case ALG_WEP:
hk.kv_type = ATH9K_CIPHER_WEP;
break;
case ALG_TKIP:
hk.kv_type = ATH9K_CIPHER_TKIP;
break;
case ALG_CCMP:
hk.kv_type = ATH9K_CIPHER_AES_CCM;
break;
default:
return -EINVAL;
}
hk.kv_len = key->keylen;
memcpy(hk.kv_val, key->key, key->keylen);
if (!sc->sc_vaps[0])
return -EIO;
vif = sc->sc_vaps[0]->av_if_data;
opmode = vif->type;
/*
* Strategy:
* For _M_STA mc tx, we will not setup a key at all since we never
* tx mc.
* _M_STA mc rx, we will use the keyID.
* for _M_IBSS mc tx, we will use the keyID, and no macaddr.
* for _M_IBSS mc rx, we will alloc a slot and plumb the mac of the
* peer node. BUT we will plumb a cleartext key so that we can do
* perSta default key table lookup in software.
*/
if (is_broadcast_ether_addr(addr)) {
switch (opmode) {
case IEEE80211_IF_TYPE_STA:
/* default key: could be group WPA key
* or could be static WEP key */
mac = NULL;
break;
case IEEE80211_IF_TYPE_IBSS:
break;
case IEEE80211_IF_TYPE_AP:
break;
default:
ASSERT(0);
break;
}
} else {
mac = addr;
}
if (key->alg == ALG_TKIP)
ret = ath_setkey_tkip(sc, key, &hk, mac);
else
ret = ath_keyset(sc, key->keyidx, &hk, mac);
if (!ret)
return -EIO;
sc->sc_keytype = hk.kv_type;
return 0;
}
static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key)
{
#define ATH_MAX_NUM_KEYS 4
int freeslot;
freeslot = (key->keyidx >= ATH_MAX_NUM_KEYS) ? 1 : 0;
ath_key_reset(sc, key->keyidx, freeslot);
#undef ATH_MAX_NUM_KEYS
}
static void setup_ht_cap(struct ieee80211_ht_info *ht_info)
{
/* Until mac80211 includes these fields */
#define IEEE80211_HT_CAP_DSSSCCK40 0x1000
#define IEEE80211_HT_CAP_MAXRXAMPDU_65536 0x3 /* 2 ^ 16 */
#define IEEE80211_HT_CAP_MPDUDENSITY_8 0x6 /* 8 usec */
ht_info->ht_supported = 1;
ht_info->cap = (u16)IEEE80211_HT_CAP_SUP_WIDTH
|(u16)IEEE80211_HT_CAP_MIMO_PS
|(u16)IEEE80211_HT_CAP_SGI_40
|(u16)IEEE80211_HT_CAP_DSSSCCK40;
ht_info->ampdu_factor = IEEE80211_HT_CAP_MAXRXAMPDU_65536;
ht_info->ampdu_density = IEEE80211_HT_CAP_MPDUDENSITY_8;
/* setup supported mcs set */
memset(ht_info->supp_mcs_set, 0, 16);
ht_info->supp_mcs_set[0] = 0xff;
ht_info->supp_mcs_set[1] = 0xff;
ht_info->supp_mcs_set[12] = IEEE80211_HT_CAP_MCS_TX_DEFINED;
}
static int ath_rate2idx(struct ath_softc *sc, int rate)
{
int i = 0, cur_band, n_rates;
struct ieee80211_hw *hw = sc->hw;
cur_band = hw->conf.channel->band;
n_rates = sc->sbands[cur_band].n_bitrates;
for (i = 0; i < n_rates; i++) {
if (sc->sbands[cur_band].bitrates[i].bitrate == rate)
break;
}
/*
* NB:mac80211 validates rx rate index against the supported legacy rate
* index only (should be done against ht rates also), return the highest
* legacy rate index for rx rate which does not match any one of the
* supported basic and extended rates to make mac80211 happy.
* The following hack will be cleaned up once the issue with
* the rx rate index validation in mac80211 is fixed.
*/
if (i == n_rates)
return n_rates - 1;
return i;
}
static void ath9k_rx_prepare(struct ath_softc *sc,
struct sk_buff *skb,
struct ath_recv_status *status,
struct ieee80211_rx_status *rx_status)
{
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_channel *curchan = hw->conf.channel;
memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
rx_status->mactime = status->tsf;
rx_status->band = curchan->band;
rx_status->freq = curchan->center_freq;
rx_status->noise = ATH_DEFAULT_NOISE_FLOOR;
rx_status->signal = rx_status->noise + status->rssi;
rx_status->rate_idx = ath_rate2idx(sc, (status->rateKbps / 100));
rx_status->antenna = status->antenna;
rx_status->qual = status->rssi * 100 / 64;
if (status->flags & ATH_RX_MIC_ERROR)
rx_status->flag |= RX_FLAG_MMIC_ERROR;
if (status->flags & ATH_RX_FCS_ERROR)
rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
rx_status->flag |= RX_FLAG_TSFT;
}
static u8 parse_mpdudensity(u8 mpdudensity)
{
/*
* 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
* 0 for no restriction
* 1 for 1/4 us
* 2 for 1/2 us
* 3 for 1 us
* 4 for 2 us
* 5 for 4 us
* 6 for 8 us
* 7 for 16 us
*/
switch (mpdudensity) {
case 0:
return 0;
case 1:
case 2:
case 3:
/* Our lower layer calculations limit our precision to
1 microsecond */
return 1;
case 4:
return 2;
case 5:
return 4;
case 6:
return 8;
case 7:
return 16;
default:
return 0;
}
}
static int ath9k_start(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
struct ieee80211_channel *curchan = hw->conf.channel;
int error = 0, pos;
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Starting driver with "
"initial channel: %d MHz\n", __func__, curchan->center_freq);
/* setup initial channel */
pos = ath_get_channel(sc, curchan);
if (pos == -1) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
return -EINVAL;
}
sc->sc_ah->ah_channels[pos].chanmode =
(curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A;
/* open ath_dev */
error = ath_open(sc, &sc->sc_ah->ah_channels[pos]);
if (error) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to complete ath_open\n", __func__);
return error;
}
ieee80211_wake_queues(hw);
return 0;
}
static int ath9k_tx(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
struct ath_softc *sc = hw->priv;
int hdrlen, padsize;
/* Add the padding after the header if this is not already done */
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
if (hdrlen & 3) {
padsize = hdrlen % 4;
if (skb_headroom(skb) < padsize)
return -1;
skb_push(skb, padsize);
memmove(skb->data, skb->data + padsize, hdrlen);
}
DPRINTF(sc, ATH_DBG_XMIT, "%s: transmitting packet, skb: %p\n",
__func__,
skb);
if (ath_tx_start(sc, skb) != 0) {
DPRINTF(sc, ATH_DBG_XMIT, "%s: TX failed\n", __func__);
dev_kfree_skb_any(skb);
/* FIXME: Check for proper return value from ATH_DEV */
return 0;
}
return 0;
}
static void ath9k_stop(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
int error;
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Driver halt\n", __func__);
error = ath_suspend(sc);
if (error)
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Device is no longer present\n", __func__);
ieee80211_stop_queues(hw);
}
static int ath9k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
struct ath_softc *sc = hw->priv;
int error, ic_opmode = 0;
/* Support only vap for now */
if (sc->sc_nvaps)
return -ENOBUFS;
switch (conf->type) {
case IEEE80211_IF_TYPE_STA:
ic_opmode = ATH9K_M_STA;
break;
case IEEE80211_IF_TYPE_IBSS:
ic_opmode = ATH9K_M_IBSS;
break;
default:
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Only STA and IBSS are supported currently\n",
__func__);
return -EOPNOTSUPP;
}
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a VAP of type: %d\n",
__func__,
ic_opmode);
error = ath_vap_attach(sc, 0, conf->vif, ic_opmode);
if (error) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to attach vap, error: %d\n",
__func__, error);
return error;
}
return 0;
}
static void ath9k_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
struct ath_softc *sc = hw->priv;
struct ath_vap *avp;
int error;
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach VAP\n", __func__);
avp = sc->sc_vaps[0];
if (avp == NULL) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
__func__);
return;
}
#ifdef CONFIG_SLOW_ANT_DIV
ath_slow_ant_div_stop(&sc->sc_antdiv);
#endif
/* Update ratectrl */
ath_rate_newstate(sc, avp);
/* Reclaim beacon resources */
if (sc->sc_opmode == ATH9K_M_HOSTAP || sc->sc_opmode == ATH9K_M_IBSS) {
ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
ath_beacon_return(sc, avp);
}
/* Set interrupt mask */
sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask & ~ATH9K_INT_GLOBAL);
sc->sc_beacons = 0;
error = ath_vap_detach(sc, 0);
if (error)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to detach vap, error: %d\n",
__func__, error);
}
static int ath9k_config(struct ieee80211_hw *hw,
struct ieee80211_conf *conf)
{
struct ath_softc *sc = hw->priv;
struct ieee80211_channel *curchan = hw->conf.channel;
int pos;
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n",
__func__,
curchan->center_freq);
pos = ath_get_channel(sc, curchan);
if (pos == -1) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
return -EINVAL;
}
sc->sc_ah->ah_channels[pos].chanmode =
(curchan->band == IEEE80211_BAND_2GHZ) ?
CHANNEL_G : CHANNEL_A;
if (sc->sc_curaid && hw->conf.ht_conf.ht_supported)
sc->sc_ah->ah_channels[pos].chanmode =
ath_get_extchanmode(sc, curchan);
sc->sc_config.txpowlimit = 2 * conf->power_level;
/* set h/w channel */
if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
DPRINTF(sc, ATH_DBG_FATAL, "%s: Unable to set channel\n",
__func__);
return 0;
}
static int ath9k_config_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_if_conf *conf)
{
struct ath_softc *sc = hw->priv;
struct ath_vap *avp;
u32 rfilt = 0;
int error, i;
DECLARE_MAC_BUF(mac);
avp = sc->sc_vaps[0];
if (avp == NULL) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
__func__);
return -EINVAL;
}
if ((conf->changed & IEEE80211_IFCC_BSSID) &&
!is_zero_ether_addr(conf->bssid)) {
switch (vif->type) {
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
/* Update ratectrl about the new state */
ath_rate_newstate(sc, avp);
/* Set rx filter */
rfilt = ath_calcrxfilter(sc);
ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
/* Set BSSID */
memcpy(sc->sc_curbssid, conf->bssid, ETH_ALEN);
sc->sc_curaid = 0;
ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
sc->sc_curaid);
/* Set aggregation protection mode parameters */
sc->sc_config.ath_aggr_prot = 0;
/*
* Reset our TSF so that its value is lower than the
* beacon that we are trying to catch.
* Only then hw will update its TSF register with the
* new beacon. Reset the TSF before setting the BSSID
* to avoid allowing in any frames that would update
* our TSF only to have us clear it
* immediately thereafter.
*/
ath9k_hw_reset_tsf(sc->sc_ah);
/* Disable BMISS interrupt when we're not associated */
ath9k_hw_set_interrupts(sc->sc_ah,
sc->sc_imask &
~(ATH9K_INT_SWBA | ATH9K_INT_BMISS));
sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
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);
/* need to reconfigure the beacon */
sc->sc_beacons = 0;
break;
default:
break;
}
}
if ((conf->changed & IEEE80211_IFCC_BEACON) &&
(vif->type == IEEE80211_IF_TYPE_IBSS)) {
/*
* Allocate and setup the beacon frame.
*
* Stop any previous beacon DMA. This may be
* necessary, for example, when an ibss merge
* causes reconfiguration; we may be called
* with beacon transmission active.
*/
ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
error = ath_beacon_alloc(sc, 0);
if (error != 0)
return error;
ath_beacon_sync(sc, 0);
}
/* Check for WLAN_CAPABILITY_PRIVACY ? */
if ((avp->av_opmode != IEEE80211_IF_TYPE_STA)) {
for (i = 0; i < IEEE80211_WEP_NKID; i++)
if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i))
ath9k_hw_keysetmac(sc->sc_ah,
(u16)i,
sc->sc_curbssid);
}
/* Only legacy IBSS for now */
if (vif->type == IEEE80211_IF_TYPE_IBSS)
ath_update_chainmask(sc, 0);
return 0;
}
#define SUPPORTED_FILTERS \
(FIF_PROMISC_IN_BSS | \
FIF_ALLMULTI | \
FIF_CONTROL | \
FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC | \
FIF_FCSFAIL)
/* Accept unicast, bcast and mcast frames */
static void ath9k_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags,
int mc_count,
struct dev_mc_list *mclist)
{
struct ath_softc *sc = hw->priv;
changed_flags &= SUPPORTED_FILTERS;
*total_flags &= SUPPORTED_FILTERS;
if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
ath_scan_start(sc);
else
ath_scan_end(sc);
}
}
static void ath9k_sta_notify(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum sta_notify_cmd cmd,
const u8 *addr)
{
struct ath_softc *sc = hw->priv;
struct ath_node *an;
unsigned long flags;
DECLARE_MAC_BUF(mac);
spin_lock_irqsave(&sc->node_lock, flags);
an = ath_node_find(sc, (u8 *) addr);
spin_unlock_irqrestore(&sc->node_lock, flags);
switch (cmd) {
case STA_NOTIFY_ADD:
spin_lock_irqsave(&sc->node_lock, flags);
if (!an) {
ath_node_attach(sc, (u8 *)addr, 0);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a node: %s\n",
__func__,
print_mac(mac, addr));
} else {
ath_node_get(sc, (u8 *)addr);
}
spin_unlock_irqrestore(&sc->node_lock, flags);
break;
case STA_NOTIFY_REMOVE:
if (!an)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Removal of a non-existent node\n",
__func__);
else {
ath_node_put(sc, an, ATH9K_BH_STATUS_INTACT);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Put a node: %s\n",
__func__,
print_mac(mac, addr));
}
break;
default:
break;
}
}
static int ath9k_conf_tx(struct ieee80211_hw *hw,
u16 queue,
const struct ieee80211_tx_queue_params *params)
{
struct ath_softc *sc = hw->priv;
struct ath9k_tx_queue_info qi;
int ret = 0, qnum;
if (queue >= WME_NUM_AC)
return 0;
qi.tqi_aifs = params->aifs;
qi.tqi_cwmin = params->cw_min;
qi.tqi_cwmax = params->cw_max;
qi.tqi_burstTime = params->txop;
qnum = ath_get_hal_qnum(queue, sc);
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Configure tx [queue/halq] [%d/%d], "
"aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
__func__,
queue,
qnum,
params->aifs,
params->cw_min,
params->cw_max,
params->txop);
ret = ath_txq_update(sc, qnum, &qi);
if (ret)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: TXQ Update failed\n", __func__);
return ret;
}
static int ath9k_set_key(struct ieee80211_hw *hw,
enum set_key_cmd cmd,
const u8 *local_addr,
const u8 *addr,
struct ieee80211_key_conf *key)
{
struct ath_softc *sc = hw->priv;
int ret = 0;
DPRINTF(sc, ATH_DBG_KEYCACHE, " %s: Set HW Key\n", __func__);
switch (cmd) {
case SET_KEY:
ret = ath_key_config(sc, addr, key);
if (!ret) {
set_bit(key->keyidx, sc->sc_keymap);
key->hw_key_idx = key->keyidx;
/* push IV and Michael MIC generation to stack */
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
}
break;
case DISABLE_KEY:
ath_key_delete(sc, key);
clear_bit(key->keyidx, sc->sc_keymap);
sc->sc_keytype = ATH9K_CIPHER_CLR;
break;
default:
ret = -EINVAL;
}
return ret;
}
static void ath9k_ht_conf(struct ath_softc *sc,
struct ieee80211_bss_conf *bss_conf)
{
#define IEEE80211_HT_CAP_40MHZ_INTOLERANT BIT(14)
struct ath_ht_info *ht_info = &sc->sc_ht_info;
if (bss_conf->assoc_ht) {
ht_info->ext_chan_offset =
bss_conf->ht_bss_conf->bss_cap &
IEEE80211_HT_IE_CHA_SEC_OFFSET;
if (!(bss_conf->ht_conf->cap &
IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
(bss_conf->ht_bss_conf->bss_cap &
IEEE80211_HT_IE_CHA_WIDTH))
ht_info->tx_chan_width = ATH9K_HT_MACMODE_2040;
else
ht_info->tx_chan_width = ATH9K_HT_MACMODE_20;
ath9k_hw_set11nmac2040(sc->sc_ah, ht_info->tx_chan_width);
ht_info->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
bss_conf->ht_conf->ampdu_factor);
ht_info->mpdudensity =
parse_mpdudensity(bss_conf->ht_conf->ampdu_density);
}
#undef IEEE80211_HT_CAP_40MHZ_INTOLERANT
}
static void ath9k_bss_assoc_info(struct ath_softc *sc,
struct ieee80211_bss_conf *bss_conf)
{
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_channel *curchan = hw->conf.channel;
struct ath_vap *avp;
int pos;
DECLARE_MAC_BUF(mac);
if (bss_conf->assoc) {
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Bss Info ASSOC %d\n",
__func__,
bss_conf->aid);
avp = sc->sc_vaps[0];
if (avp == NULL) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
__func__);
return;
}
/* New association, store aid */
if (avp->av_opmode == ATH9K_M_STA) {
sc->sc_curaid = bss_conf->aid;
ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
sc->sc_curaid);
}
/* Configure the beacon */
ath_beacon_config(sc, 0);
sc->sc_beacons = 1;
/* Reset rssi stats */
sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
/* Update chainmask */
ath_update_chainmask(sc, bss_conf->assoc_ht);
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: bssid %s aid 0x%x\n",
__func__,
print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n",
__func__,
curchan->center_freq);
pos = ath_get_channel(sc, curchan);
if (pos == -1) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Invalid channel\n", __func__);
return;
}
if (hw->conf.ht_conf.ht_supported)
sc->sc_ah->ah_channels[pos].chanmode =
ath_get_extchanmode(sc, curchan);
else
sc->sc_ah->ah_channels[pos].chanmode =
(curchan->band == IEEE80211_BAND_2GHZ) ?
CHANNEL_G : CHANNEL_A;
/* set h/w channel */
if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to set channel\n",
__func__);
ath_rate_newstate(sc, avp);
/* Update ratectrl about the new state */
ath_rc_node_update(hw, avp->rc_node);
} else {
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Bss Info DISSOC\n", __func__);
sc->sc_curaid = 0;
}
}
static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
u32 changed)
{
struct ath_softc *sc = hw->priv;
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed PREAMBLE %d\n",
__func__,
bss_conf->use_short_preamble);
if (bss_conf->use_short_preamble)
sc->sc_flags |= ATH_PREAMBLE_SHORT;
else
sc->sc_flags &= ~ATH_PREAMBLE_SHORT;
}
if (changed & BSS_CHANGED_ERP_CTS_PROT) {
DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed CTS PROT %d\n",
__func__,
bss_conf->use_cts_prot);
if (bss_conf->use_cts_prot &&
hw->conf.channel->band != IEEE80211_BAND_5GHZ)
sc->sc_flags |= ATH_PROTECT_ENABLE;
else
sc->sc_flags &= ~ATH_PROTECT_ENABLE;
}
if (changed & BSS_CHANGED_HT) {
DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed HT %d\n",
__func__,
bss_conf->assoc_ht);
ath9k_ht_conf(sc, bss_conf);
}
if (changed & BSS_CHANGED_ASSOC) {
DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed ASSOC %d\n",
__func__,
bss_conf->assoc);
ath9k_bss_assoc_info(sc, bss_conf);
}
}
static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
{
u64 tsf;
struct ath_softc *sc = hw->priv;
struct ath_hal *ah = sc->sc_ah;
tsf = ath9k_hw_gettsf64(ah);
return tsf;
}
static void ath9k_reset_tsf(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
struct ath_hal *ah = sc->sc_ah;
ath9k_hw_reset_tsf(ah);
}
static int ath9k_ampdu_action(struct ieee80211_hw *hw,
enum ieee80211_ampdu_mlme_action action,
const u8 *addr,
u16 tid,
u16 *ssn)
{
struct ath_softc *sc = hw->priv;
int ret = 0;
switch (action) {
case IEEE80211_AMPDU_RX_START:
ret = ath_rx_aggr_start(sc, addr, tid, ssn);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to start RX aggregation\n",
__func__);
break;
case IEEE80211_AMPDU_RX_STOP:
ret = ath_rx_aggr_stop(sc, addr, tid);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to stop RX aggregation\n",
__func__);
break;
case IEEE80211_AMPDU_TX_START:
ret = ath_tx_aggr_start(sc, addr, tid, ssn);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to start TX aggregation\n",
__func__);
else
ieee80211_start_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid);
break;
case IEEE80211_AMPDU_TX_STOP:
ret = ath_tx_aggr_stop(sc, addr, tid);
if (ret < 0)
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to stop TX aggregation\n",
__func__);
ieee80211_stop_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid);
break;
default:
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unknown AMPDU action\n", __func__);
}
return ret;
}
static struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
.stop = ath9k_stop,
.add_interface = ath9k_add_interface,
.remove_interface = ath9k_remove_interface,
.config = ath9k_config,
.config_interface = ath9k_config_interface,
.configure_filter = ath9k_configure_filter,
.get_stats = NULL,
.sta_notify = ath9k_sta_notify,
.conf_tx = ath9k_conf_tx,
.get_tx_stats = NULL,
.bss_info_changed = ath9k_bss_info_changed,
.set_tim = NULL,
.set_key = ath9k_set_key,
.hw_scan = NULL,
.get_tkip_seq = NULL,
.set_rts_threshold = NULL,
.set_frag_threshold = NULL,
.set_retry_limit = NULL,
.get_tsf = ath9k_get_tsf,
.reset_tsf = ath9k_reset_tsf,
.tx_last_beacon = NULL,
.ampdu_action = ath9k_ampdu_action
};
void ath_get_beaconconfig(struct ath_softc *sc,
int if_id,
struct ath_beacon_config *conf)
{
struct ieee80211_hw *hw = sc->hw;
/* fill in beacon config data */
conf->beacon_interval = hw->conf.beacon_int;
conf->listen_interval = 100;
conf->dtim_count = 1;
conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval;
}
int ath_update_beacon(struct ath_softc *sc,
int if_id,
struct ath_beacon_offset *bo,
struct sk_buff *skb,
int mcast)
{
return 0;
}
void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
struct ath_xmit_status *tx_status, struct ath_node *an)
{
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
DPRINTF(sc, ATH_DBG_XMIT,
"%s: TX complete: skb: %p\n", __func__, skb);
if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
/* free driver's private data area of tx_info */
if (tx_info->driver_data[0] != NULL)
kfree(tx_info->driver_data[0]);
tx_info->driver_data[0] = NULL;
}
if (tx_status->flags & ATH_TX_BAR) {
tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
tx_status->flags &= ~ATH_TX_BAR;
}
if (tx_status->flags)
tx_info->status.excessive_retries = 1;
tx_info->status.retry_count = tx_status->retries;
ieee80211_tx_status(hw, skb);
if (an)
ath_node_put(sc, an, ATH9K_BH_STATUS_CHANGE);
}
int ath__rx_indicate(struct ath_softc *sc,
struct sk_buff *skb,
struct ath_recv_status *status,
u16 keyix)
{
struct ieee80211_hw *hw = sc->hw;
struct ath_node *an = NULL;
struct ieee80211_rx_status rx_status;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
int padsize;
enum ATH_RX_TYPE st;
/* see if any padding is done by the hw and remove it */
if (hdrlen & 3) {
padsize = hdrlen % 4;
memmove(skb->data + padsize, skb->data, hdrlen);
skb_pull(skb, padsize);
}
/* remove FCS before passing up to protocol stack */
skb_trim(skb, (skb->len - FCS_LEN));
/* Prepare rx status */
ath9k_rx_prepare(sc, skb, status, &rx_status);
if (!(keyix == ATH9K_RXKEYIX_INVALID) &&
!(status->flags & ATH_RX_DECRYPT_ERROR)) {
rx_status.flag |= RX_FLAG_DECRYPTED;
} else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED)
&& !(status->flags & ATH_RX_DECRYPT_ERROR)
&& skb->len >= hdrlen + 4) {
keyix = skb->data[hdrlen + 3] >> 6;
if (test_bit(keyix, sc->sc_keymap))
rx_status.flag |= RX_FLAG_DECRYPTED;
}
spin_lock_bh(&sc->node_lock);
an = ath_node_find(sc, hdr->addr2);
spin_unlock_bh(&sc->node_lock);
if (an) {
ath_rx_input(sc, an,
hw->conf.ht_conf.ht_supported,
skb, status, &st);
}
if (!an || (st != ATH_RX_CONSUMED))
__ieee80211_rx(hw, skb, &rx_status);
return 0;
}
int ath_rx_subframe(struct ath_node *an,
struct sk_buff *skb,
struct ath_recv_status *status)
{
struct ath_softc *sc = an->an_sc;
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_rx_status rx_status;
/* Prepare rx status */
ath9k_rx_prepare(sc, skb, status, &rx_status);
if (!(status->flags & ATH_RX_DECRYPT_ERROR))
rx_status.flag |= RX_FLAG_DECRYPTED;
__ieee80211_rx(hw, skb, &rx_status);
return 0;
}
enum ath9k_ht_macmode ath_cwm_macmode(struct ath_softc *sc)
{
return sc->sc_ht_info.tx_chan_width;
}
static int ath_detach(struct ath_softc *sc)
{
struct ieee80211_hw *hw = sc->hw;
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__);
/* Unregister hw */
ieee80211_unregister_hw(hw);
/* unregister Rate control */
ath_rate_control_unregister();
/* tx/rx cleanup */
ath_rx_cleanup(sc);
ath_tx_cleanup(sc);
/* Deinit */
ath_deinit(sc);
return 0;
}
static int ath_attach(u16 devid,
struct ath_softc *sc)
{
struct ieee80211_hw *hw = sc->hw;
int error = 0;
DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach ATH hw\n", __func__);
error = ath_init(devid, sc);
if (error != 0)
return error;
/* Init nodes */
INIT_LIST_HEAD(&sc->node_list);
spin_lock_init(&sc->node_lock);
/* get mac address from hardware and set in mac80211 */
SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr);
/* setup channels and rates */
sc->sbands[IEEE80211_BAND_2GHZ].channels =
sc->channels[IEEE80211_BAND_2GHZ];
sc->sbands[IEEE80211_BAND_2GHZ].bitrates =
sc->rates[IEEE80211_BAND_2GHZ];
sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
/* Setup HT capabilities for 2.4Ghz*/
setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_info);
hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
&sc->sbands[IEEE80211_BAND_2GHZ];
if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) {
sc->sbands[IEEE80211_BAND_5GHZ].channels =
sc->channels[IEEE80211_BAND_5GHZ];
sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
sc->rates[IEEE80211_BAND_5GHZ];
sc->sbands[IEEE80211_BAND_5GHZ].band =
IEEE80211_BAND_5GHZ;
if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
/* Setup HT capabilities for 5Ghz*/
setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_info);
hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
&sc->sbands[IEEE80211_BAND_5GHZ];
}
/* FIXME: Have to figure out proper hw init values later */
hw->queues = 4;
hw->ampdu_queues = 1;
/* Register rate control */
hw->rate_control_algorithm = "ath9k_rate_control";
error = ath_rate_control_register();
if (error != 0) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: Unable to register rate control "
"algorithm:%d\n", __func__, error);
ath_rate_control_unregister();
goto bad;
}
error = ieee80211_register_hw(hw);
if (error != 0) {
ath_rate_control_unregister();
goto bad;
}
/* initialize tx/rx engine */
error = ath_tx_init(sc, ATH_TXBUF);
if (error != 0)
goto bad1;
error = ath_rx_init(sc, ATH_RXBUF);
if (error != 0)
goto bad1;
return 0;
bad1:
ath_detach(sc);
bad:
return error;
}
static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
void __iomem *mem;
struct ath_softc *sc;
struct ieee80211_hw *hw;
const char *athname;
u8 csz;
u32 val;
int ret = 0;
if (pci_enable_device(pdev))
return -EIO;
/* XXX 32-bit addressing only */
if (pci_set_dma_mask(pdev, 0xffffffff)) {
printk(KERN_ERR "ath_pci: 32-bit DMA not available\n");
ret = -ENODEV;
goto bad;
}
/*
* Cache line size is used to size and align various
* structures used to communicate with the hardware.
*/
pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz);
if (csz == 0) {
/*
* Linux 2.4.18 (at least) writes the cache line size
* register as a 16-bit wide register which is wrong.
* We must have this setup properly for rx buffer
* DMA to work so force a reasonable value here if it
* comes up zero.
*/
csz = L1_CACHE_BYTES / sizeof(u32);
pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz);
}
/*
* The default setting of latency timer yields poor results,
* set it to the value used by other systems. It may be worth
* tweaking this setting more.
*/
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8);
pci_set_master(pdev);
/*
* Disable the RETRY_TIMEOUT register (0x41) to keep
* PCI Tx retries from interfering with C3 CPU state.
*/
pci_read_config_dword(pdev, 0x40, &val);
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
ret = pci_request_region(pdev, 0, "ath9k");
if (ret) {
dev_err(&pdev->dev, "PCI memory region reserve error\n");
ret = -ENODEV;
goto bad;
}
mem = pci_iomap(pdev, 0, 0);
if (!mem) {
printk(KERN_ERR "PCI memory map error\n") ;
ret = -EIO;
goto bad1;
}
hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
if (hw == NULL) {
printk(KERN_ERR "ath_pci: no memory for ieee80211_hw\n");
goto bad2;
}
hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_DBM;
SET_IEEE80211_DEV(hw, &pdev->dev);
pci_set_drvdata(pdev, hw);
sc = hw->priv;
sc->hw = hw;
sc->pdev = pdev;
sc->mem = mem;
if (ath_attach(id->device, sc) != 0) {
ret = -ENODEV;
goto bad3;
}
/* setup interrupt service routine */
if (request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath", sc)) {
printk(KERN_ERR "%s: request_irq failed\n",
wiphy_name(hw->wiphy));
ret = -EIO;
goto bad4;
}
athname = ath9k_hw_probe(id->vendor, id->device);
printk(KERN_INFO "%s: %s: mem=0x%lx, irq=%d\n",
wiphy_name(hw->wiphy),
athname ? athname : "Atheros ???",
(unsigned long)mem, pdev->irq);
return 0;
bad4:
ath_detach(sc);
bad3:
ieee80211_free_hw(hw);
bad2:
pci_iounmap(pdev, mem);
bad1:
pci_release_region(pdev, 0);
bad:
pci_disable_device(pdev);
return ret;
}
static void ath_pci_remove(struct pci_dev *pdev)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_softc *sc = hw->priv;
if (pdev->irq)
free_irq(pdev->irq, sc);
ath_detach(sc);
pci_iounmap(pdev, sc->mem);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
ieee80211_free_hw(hw);
}
#ifdef CONFIG_PM
static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, 3);
return 0;
}
static int ath_pci_resume(struct pci_dev *pdev)
{
u32 val;
int err;
err = pci_enable_device(pdev);
if (err)
return err;
pci_restore_state(pdev);
/*
* Suspend/Resume resets the PCI configuration space, so we have to
* re-disable the RETRY_TIMEOUT register (0x41) to keep
* PCI Tx retries from interfering with C3 CPU state
*/
pci_read_config_dword(pdev, 0x40, &val);
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
return 0;
}
#endif /* CONFIG_PM */
MODULE_DEVICE_TABLE(pci, ath_pci_id_table);
static struct pci_driver ath_pci_driver = {
.name = "ath9k",
.id_table = ath_pci_id_table,
.probe = ath_pci_probe,
.remove = ath_pci_remove,
#ifdef CONFIG_PM
.suspend = ath_pci_suspend,
.resume = ath_pci_resume,
#endif /* CONFIG_PM */
};
static int __init init_ath_pci(void)
{
printk(KERN_INFO "%s: %s\n", dev_info, ATH_PCI_VERSION);
if (pci_register_driver(&ath_pci_driver) < 0) {
printk(KERN_ERR
"ath_pci: No devices found, driver not installed.\n");
pci_unregister_driver(&ath_pci_driver);
return -ENODEV;
}
return 0;
}
module_init(init_ath_pci);
static void __exit exit_ath_pci(void)
{
pci_unregister_driver(&ath_pci_driver);
printk(KERN_INFO "%s: driver unloaded\n", dev_info);
}
module_exit(exit_ath_pci);
/*
* 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 "core.h"
#include "hw.h"
#include "reg.h"
#include "phy.h"
void
ath9k_hw_write_regs(struct ath_hal *ah, u32 modesIndex, u32 freqIndex,
int regWrites)
{
struct ath_hal_5416 *ahp = AH5416(ah);
REG_WRITE_ARRAY(&ahp->ah_iniBB_RfGain, freqIndex, regWrites);
}
bool
ath9k_hw_set_channel(struct ath_hal *ah, struct ath9k_channel *chan)
{
u32 channelSel = 0;
u32 bModeSynth = 0;
u32 aModeRefSel = 0;
u32 reg32 = 0;
u16 freq;
struct chan_centers centers;
ath9k_hw_get_channel_centers(ah, chan, &centers);
freq = centers.synth_center;
if (freq < 4800) {
u32 txctl;
if (((freq - 2192) % 5) == 0) {
channelSel = ((freq - 672) * 2 - 3040) / 10;
bModeSynth = 0;
} else if (((freq - 2224) % 5) == 0) {
channelSel = ((freq - 704) * 2 - 3040) / 10;
bModeSynth = 1;
} else {
DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
"%s: invalid channel %u MHz\n", __func__,
freq);
return false;
}
channelSel = (channelSel << 2) & 0xff;
channelSel = ath9k_hw_reverse_bits(channelSel, 8);
txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL);
if (freq == 2484) {
REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
txctl | AR_PHY_CCK_TX_CTRL_JAPAN);
} else {
REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN);
}
} else if ((freq % 20) == 0 && freq >= 5120) {
channelSel =
ath9k_hw_reverse_bits(((freq - 4800) / 20 << 2), 8);
aModeRefSel = ath9k_hw_reverse_bits(1, 2);
} else if ((freq % 10) == 0) {
channelSel =
ath9k_hw_reverse_bits(((freq - 4800) / 10 << 1), 8);
if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah))
aModeRefSel = ath9k_hw_reverse_bits(2, 2);
else
aModeRefSel = ath9k_hw_reverse_bits(1, 2);
} else if ((freq % 5) == 0) {
channelSel = ath9k_hw_reverse_bits((freq - 4800) / 5, 8);
aModeRefSel = ath9k_hw_reverse_bits(1, 2);
} else {
DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL,
"%s: invalid channel %u MHz\n", __func__, freq);
return false;
}
reg32 =
(channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) |
(1 << 5) | 0x1;
REG_WRITE(ah, AR_PHY(0x37), reg32);
ah->ah_curchan = chan;
AH5416(ah)->ah_curchanRadIndex = -1;
return true;
}
bool
ath9k_hw_ar9280_set_channel(struct ath_hal *ah,
struct ath9k_channel *chan)
{
u16 bMode, fracMode, aModeRefSel = 0;
u32 freq, ndiv, channelSel = 0, channelFrac = 0, reg32 = 0;
struct chan_centers centers;
u32 refDivA = 24;
ath9k_hw_get_channel_centers(ah, chan, &centers);
freq = centers.synth_center;
reg32 = REG_READ(ah, AR_PHY_SYNTH_CONTROL);
reg32 &= 0xc0000000;
if (freq < 4800) {
u32 txctl;
bMode = 1;
fracMode = 1;
aModeRefSel = 0;
channelSel = (freq * 0x10000) / 15;
txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL);
if (freq == 2484) {
REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
txctl | AR_PHY_CCK_TX_CTRL_JAPAN);
} else {
REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,
txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN);
}
} else {
bMode = 0;
fracMode = 0;
if ((freq % 20) == 0) {
aModeRefSel = 3;
} else if ((freq % 10) == 0) {
aModeRefSel = 2;
} else {
aModeRefSel = 0;
fracMode = 1;
refDivA = 1;
channelSel = (freq * 0x8000) / 15;
REG_RMW_FIELD(ah, AR_AN_SYNTH9,
AR_AN_SYNTH9_REFDIVA, refDivA);
}
if (!fracMode) {
ndiv = (freq * (refDivA >> aModeRefSel)) / 60;
channelSel = ndiv & 0x1ff;
channelFrac = (ndiv & 0xfffffe00) * 2;
channelSel = (channelSel << 17) | channelFrac;
}
}
reg32 = reg32 |
(bMode << 29) |
(fracMode << 28) | (aModeRefSel << 26) | (channelSel);
REG_WRITE(ah, AR_PHY_SYNTH_CONTROL, reg32);
ah->ah_curchan = chan;
AH5416(ah)->ah_curchanRadIndex = -1;
return true;
}
static void
ath9k_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32,
u32 numBits, u32 firstBit,
u32 column)
{
u32 tmp32, mask, arrayEntry, lastBit;
int32_t bitPosition, bitsLeft;
tmp32 = ath9k_hw_reverse_bits(reg32, numBits);
arrayEntry = (firstBit - 1) / 8;
bitPosition = (firstBit - 1) % 8;
bitsLeft = numBits;
while (bitsLeft > 0) {
lastBit = (bitPosition + bitsLeft > 8) ?
8 : bitPosition + bitsLeft;
mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) <<
(column * 8);
rfBuf[arrayEntry] &= ~mask;
rfBuf[arrayEntry] |= ((tmp32 << bitPosition) <<
(column * 8)) & mask;
bitsLeft -= 8 - bitPosition;
tmp32 = tmp32 >> (8 - bitPosition);
bitPosition = 0;
arrayEntry++;
}
}
bool
ath9k_hw_set_rf_regs(struct ath_hal *ah, struct ath9k_channel *chan,
u16 modesIndex)
{
struct ath_hal_5416 *ahp = AH5416(ah);
u32 eepMinorRev;
u32 ob5GHz = 0, db5GHz = 0;
u32 ob2GHz = 0, db2GHz = 0;
int regWrites = 0;
if (AR_SREV_9280_10_OR_LATER(ah))
return true;
eepMinorRev = ath9k_hw_get_eeprom(ahp, EEP_MINOR_REV);
RF_BANK_SETUP(ahp->ah_analogBank0Data, &ahp->ah_iniBank0, 1);
RF_BANK_SETUP(ahp->ah_analogBank1Data, &ahp->ah_iniBank1, 1);
RF_BANK_SETUP(ahp->ah_analogBank2Data, &ahp->ah_iniBank2, 1);
RF_BANK_SETUP(ahp->ah_analogBank3Data, &ahp->ah_iniBank3,
modesIndex);
{
int i;
for (i = 0; i < ahp->ah_iniBank6TPC.ia_rows; i++) {
ahp->ah_analogBank6Data[i] =
INI_RA(&ahp->ah_iniBank6TPC, i, modesIndex);
}
}
if (eepMinorRev >= 2) {
if (IS_CHAN_2GHZ(chan)) {
ob2GHz = ath9k_hw_get_eeprom(ahp, EEP_OB_2);
db2GHz = ath9k_hw_get_eeprom(ahp, EEP_DB_2);
ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data,
ob2GHz, 3, 197, 0);
ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data,
db2GHz, 3, 194, 0);
} else {
ob5GHz = ath9k_hw_get_eeprom(ahp, EEP_OB_5);
db5GHz = ath9k_hw_get_eeprom(ahp, EEP_DB_5);
ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data,
ob5GHz, 3, 203, 0);
ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data,
db5GHz, 3, 200, 0);
}
}
RF_BANK_SETUP(ahp->ah_analogBank7Data, &ahp->ah_iniBank7, 1);
REG_WRITE_RF_ARRAY(&ahp->ah_iniBank0, ahp->ah_analogBank0Data,
regWrites);
REG_WRITE_RF_ARRAY(&ahp->ah_iniBank1, ahp->ah_analogBank1Data,
regWrites);
REG_WRITE_RF_ARRAY(&ahp->ah_iniBank2, ahp->ah_analogBank2Data,
regWrites);
REG_WRITE_RF_ARRAY(&ahp->ah_iniBank3, ahp->ah_analogBank3Data,
regWrites);
REG_WRITE_RF_ARRAY(&ahp->ah_iniBank6TPC, ahp->ah_analogBank6Data,
regWrites);
REG_WRITE_RF_ARRAY(&ahp->ah_iniBank7, ahp->ah_analogBank7Data,
regWrites);
return true;
}
void
ath9k_hw_rfdetach(struct ath_hal *ah)
{
struct ath_hal_5416 *ahp = AH5416(ah);
if (ahp->ah_analogBank0Data != NULL) {
kfree(ahp->ah_analogBank0Data);
ahp->ah_analogBank0Data = NULL;
}
if (ahp->ah_analogBank1Data != NULL) {
kfree(ahp->ah_analogBank1Data);
ahp->ah_analogBank1Data = NULL;
}
if (ahp->ah_analogBank2Data != NULL) {
kfree(ahp->ah_analogBank2Data);
ahp->ah_analogBank2Data = NULL;
}
if (ahp->ah_analogBank3Data != NULL) {
kfree(ahp->ah_analogBank3Data);
ahp->ah_analogBank3Data = NULL;
}
if (ahp->ah_analogBank6Data != NULL) {
kfree(ahp->ah_analogBank6Data);
ahp->ah_analogBank6Data = NULL;
}
if (ahp->ah_analogBank6TPCData != NULL) {
kfree(ahp->ah_analogBank6TPCData);
ahp->ah_analogBank6TPCData = NULL;
}
if (ahp->ah_analogBank7Data != NULL) {
kfree(ahp->ah_analogBank7Data);
ahp->ah_analogBank7Data = NULL;
}
if (ahp->ah_addac5416_21 != NULL) {
kfree(ahp->ah_addac5416_21);
ahp->ah_addac5416_21 = NULL;
}
if (ahp->ah_bank6Temp != NULL) {
kfree(ahp->ah_bank6Temp);
ahp->ah_bank6Temp = NULL;
}
}
bool ath9k_hw_init_rf(struct ath_hal *ah, int *status)
{
struct ath_hal_5416 *ahp = AH5416(ah);
if (!AR_SREV_9280_10_OR_LATER(ah)) {
ahp->ah_analogBank0Data =
kzalloc((sizeof(u32) *
ahp->ah_iniBank0.ia_rows), GFP_KERNEL);
ahp->ah_analogBank1Data =
kzalloc((sizeof(u32) *
ahp->ah_iniBank1.ia_rows), GFP_KERNEL);
ahp->ah_analogBank2Data =
kzalloc((sizeof(u32) *
ahp->ah_iniBank2.ia_rows), GFP_KERNEL);
ahp->ah_analogBank3Data =
kzalloc((sizeof(u32) *
ahp->ah_iniBank3.ia_rows), GFP_KERNEL);
ahp->ah_analogBank6Data =
kzalloc((sizeof(u32) *
ahp->ah_iniBank6.ia_rows), GFP_KERNEL);
ahp->ah_analogBank6TPCData =
kzalloc((sizeof(u32) *
ahp->ah_iniBank6TPC.ia_rows), GFP_KERNEL);
ahp->ah_analogBank7Data =
kzalloc((sizeof(u32) *
ahp->ah_iniBank7.ia_rows), GFP_KERNEL);
if (ahp->ah_analogBank0Data == NULL
|| ahp->ah_analogBank1Data == NULL
|| ahp->ah_analogBank2Data == NULL
|| ahp->ah_analogBank3Data == NULL
|| ahp->ah_analogBank6Data == NULL
|| ahp->ah_analogBank6TPCData == NULL
|| ahp->ah_analogBank7Data == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"%s: cannot allocate RF banks\n",
__func__);
*status = -ENOMEM;
return false;
}
ahp->ah_addac5416_21 =
kzalloc((sizeof(u32) *
ahp->ah_iniAddac.ia_rows *
ahp->ah_iniAddac.ia_columns), GFP_KERNEL);
if (ahp->ah_addac5416_21 == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"%s: cannot allocate ah_addac5416_21\n",
__func__);
*status = -ENOMEM;
return false;
}
ahp->ah_bank6Temp =
kzalloc((sizeof(u32) *
ahp->ah_iniBank6.ia_rows), GFP_KERNEL);
if (ahp->ah_bank6Temp == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"%s: cannot allocate ah_bank6Temp\n",
__func__);
*status = -ENOMEM;
return false;
}
}
return true;
}
void
ath9k_hw_decrease_chain_power(struct ath_hal *ah, struct ath9k_channel *chan)
{
int i, regWrites = 0;
struct ath_hal_5416 *ahp = AH5416(ah);
u32 bank6SelMask;
u32 *bank6Temp = ahp->ah_bank6Temp;
switch (ahp->ah_diversityControl) {
case ATH9K_ANT_FIXED_A:
bank6SelMask =
(ahp->
ah_antennaSwitchSwap & ANTSWAP_AB) ? REDUCE_CHAIN_0 :
REDUCE_CHAIN_1;
break;
case ATH9K_ANT_FIXED_B:
bank6SelMask =
(ahp->
ah_antennaSwitchSwap & ANTSWAP_AB) ? REDUCE_CHAIN_1 :
REDUCE_CHAIN_0;
break;
case ATH9K_ANT_VARIABLE:
return;
break;
default:
return;
break;
}
for (i = 0; i < ahp->ah_iniBank6.ia_rows; i++)
bank6Temp[i] = ahp->ah_analogBank6Data[i];
REG_WRITE(ah, AR_PHY_BASE + 0xD8, bank6SelMask);
ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 189, 0);
ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 190, 0);
ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 191, 0);
ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 192, 0);
ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 193, 0);
ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 222, 0);
ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 245, 0);
ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 246, 0);
ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 247, 0);
REG_WRITE_RF_ARRAY(&ahp->ah_iniBank6, bank6Temp, regWrites);
REG_WRITE(ah, AR_PHY_BASE + 0xD8, 0x00000053);
#ifdef ALTER_SWITCH
REG_WRITE(ah, PHY_SWITCH_CHAIN_0,
(REG_READ(ah, PHY_SWITCH_CHAIN_0) & ~0x38)
| ((REG_READ(ah, PHY_SWITCH_CHAIN_0) >> 3) & 0x38));
#endif
}
/*
* 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 PHY_H
#define PHY_H
bool ath9k_hw_ar9280_set_channel(struct ath_hal *ah,
struct ath9k_channel
*chan);
bool ath9k_hw_set_channel(struct ath_hal *ah,
struct ath9k_channel *chan);
void ath9k_hw_write_regs(struct ath_hal *ah, u32 modesIndex,
u32 freqIndex, int regWrites);
bool ath9k_hw_set_rf_regs(struct ath_hal *ah,
struct ath9k_channel *chan,
u16 modesIndex);
void ath9k_hw_decrease_chain_power(struct ath_hal *ah,
struct ath9k_channel *chan);
bool ath9k_hw_init_rf(struct ath_hal *ah,
int *status);
#define AR_PHY_BASE 0x9800
#define AR_PHY(_n) (AR_PHY_BASE + ((_n)<<2))
#define AR_PHY_TEST 0x9800
#define PHY_AGC_CLR 0x10000000
#define RFSILENT_BB 0x00002000
#define AR_PHY_TURBO 0x9804
#define AR_PHY_FC_TURBO_MODE 0x00000001
#define AR_PHY_FC_TURBO_SHORT 0x00000002
#define AR_PHY_FC_DYN2040_EN 0x00000004
#define AR_PHY_FC_DYN2040_PRI_ONLY 0x00000008
#define AR_PHY_FC_DYN2040_PRI_CH 0x00000010
#define AR_PHY_FC_DYN2040_EXT_CH 0x00000020
#define AR_PHY_FC_HT_EN 0x00000040
#define AR_PHY_FC_SHORT_GI_40 0x00000080
#define AR_PHY_FC_WALSH 0x00000100
#define AR_PHY_FC_SINGLE_HT_LTF1 0x00000200
#define AR_PHY_TIMING2 0x9810
#define AR_PHY_TIMING3 0x9814
#define AR_PHY_TIMING3_DSC_MAN 0xFFFE0000
#define AR_PHY_TIMING3_DSC_MAN_S 17
#define AR_PHY_TIMING3_DSC_EXP 0x0001E000
#define AR_PHY_TIMING3_DSC_EXP_S 13
#define AR_PHY_CHIP_ID 0x9818
#define AR_PHY_CHIP_ID_REV_0 0x80
#define AR_PHY_CHIP_ID_REV_1 0x81
#define AR_PHY_CHIP_ID_9160_REV_0 0xb0
#define AR_PHY_ACTIVE 0x981C
#define AR_PHY_ACTIVE_EN 0x00000001
#define AR_PHY_ACTIVE_DIS 0x00000000
#define AR_PHY_RF_CTL2 0x9824
#define AR_PHY_TX_END_DATA_START 0x000000FF
#define AR_PHY_TX_END_DATA_START_S 0
#define AR_PHY_TX_END_PA_ON 0x0000FF00
#define AR_PHY_TX_END_PA_ON_S 8
#define AR_PHY_RF_CTL3 0x9828
#define AR_PHY_TX_END_TO_A2_RX_ON 0x00FF0000
#define AR_PHY_TX_END_TO_A2_RX_ON_S 16
#define AR_PHY_ADC_CTL 0x982C
#define AR_PHY_ADC_CTL_OFF_INBUFGAIN 0x00000003
#define AR_PHY_ADC_CTL_OFF_INBUFGAIN_S 0
#define AR_PHY_ADC_CTL_OFF_PWDDAC 0x00002000
#define AR_PHY_ADC_CTL_OFF_PWDBANDGAP 0x00004000
#define AR_PHY_ADC_CTL_OFF_PWDADC 0x00008000
#define AR_PHY_ADC_CTL_ON_INBUFGAIN 0x00030000
#define AR_PHY_ADC_CTL_ON_INBUFGAIN_S 16
#define AR_PHY_ADC_SERIAL_CTL 0x9830
#define AR_PHY_SEL_INTERNAL_ADDAC 0x00000000
#define AR_PHY_SEL_EXTERNAL_RADIO 0x00000001
#define AR_PHY_RF_CTL4 0x9834
#define AR_PHY_RF_CTL4_TX_END_XPAB_OFF 0xFF000000
#define AR_PHY_RF_CTL4_TX_END_XPAB_OFF_S 24
#define AR_PHY_RF_CTL4_TX_END_XPAA_OFF 0x00FF0000
#define AR_PHY_RF_CTL4_TX_END_XPAA_OFF_S 16
#define AR_PHY_RF_CTL4_FRAME_XPAB_ON 0x0000FF00
#define AR_PHY_RF_CTL4_FRAME_XPAB_ON_S 8
#define AR_PHY_RF_CTL4_FRAME_XPAA_ON 0x000000FF
#define AR_PHY_RF_CTL4_FRAME_XPAA_ON_S 0
#define AR_PHY_SETTLING 0x9844
#define AR_PHY_SETTLING_SWITCH 0x00003F80
#define AR_PHY_SETTLING_SWITCH_S 7
#define AR_PHY_RXGAIN 0x9848
#define AR_PHY_RXGAIN_TXRX_ATTEN 0x0003F000
#define AR_PHY_RXGAIN_TXRX_ATTEN_S 12
#define AR_PHY_RXGAIN_TXRX_RF_MAX 0x007C0000
#define AR_PHY_RXGAIN_TXRX_RF_MAX_S 18
#define AR9280_PHY_RXGAIN_TXRX_ATTEN 0x00003F80
#define AR9280_PHY_RXGAIN_TXRX_ATTEN_S 7
#define AR9280_PHY_RXGAIN_TXRX_MARGIN 0x001FC000
#define AR9280_PHY_RXGAIN_TXRX_MARGIN_S 14
#define AR_PHY_DESIRED_SZ 0x9850
#define AR_PHY_DESIRED_SZ_ADC 0x000000FF
#define AR_PHY_DESIRED_SZ_ADC_S 0
#define AR_PHY_DESIRED_SZ_PGA 0x0000FF00
#define AR_PHY_DESIRED_SZ_PGA_S 8
#define AR_PHY_DESIRED_SZ_TOT_DES 0x0FF00000
#define AR_PHY_DESIRED_SZ_TOT_DES_S 20
#define AR_PHY_FIND_SIG 0x9858
#define AR_PHY_FIND_SIG_FIRSTEP 0x0003F000
#define AR_PHY_FIND_SIG_FIRSTEP_S 12
#define AR_PHY_FIND_SIG_FIRPWR 0x03FC0000
#define AR_PHY_FIND_SIG_FIRPWR_S 18
#define AR_PHY_AGC_CTL1 0x985C
#define AR_PHY_AGC_CTL1_COARSE_LOW 0x00007F80
#define AR_PHY_AGC_CTL1_COARSE_LOW_S 7
#define AR_PHY_AGC_CTL1_COARSE_HIGH 0x003F8000
#define AR_PHY_AGC_CTL1_COARSE_HIGH_S 15
#define AR_PHY_AGC_CONTROL 0x9860
#define AR_PHY_AGC_CONTROL_CAL 0x00000001
#define AR_PHY_AGC_CONTROL_NF 0x00000002
#define AR_PHY_AGC_CONTROL_ENABLE_NF 0x00008000
#define AR_PHY_AGC_CONTROL_FLTR_CAL 0x00010000
#define AR_PHY_AGC_CONTROL_NO_UPDATE_NF 0x00020000
#define AR_PHY_CCA 0x9864
#define AR_PHY_MINCCA_PWR 0x0FF80000
#define AR_PHY_MINCCA_PWR_S 19
#define AR_PHY_CCA_THRESH62 0x0007F000
#define AR_PHY_CCA_THRESH62_S 12
#define AR9280_PHY_MINCCA_PWR 0x1FF00000
#define AR9280_PHY_MINCCA_PWR_S 20
#define AR9280_PHY_CCA_THRESH62 0x000FF000
#define AR9280_PHY_CCA_THRESH62_S 12
#define AR_PHY_SFCORR_LOW 0x986C
#define AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW 0x00000001
#define AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW 0x00003F00
#define AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW_S 8
#define AR_PHY_SFCORR_LOW_M1_THRESH_LOW 0x001FC000
#define AR_PHY_SFCORR_LOW_M1_THRESH_LOW_S 14
#define AR_PHY_SFCORR_LOW_M2_THRESH_LOW 0x0FE00000
#define AR_PHY_SFCORR_LOW_M2_THRESH_LOW_S 21
#define AR_PHY_SFCORR 0x9868
#define AR_PHY_SFCORR_M2COUNT_THR 0x0000001F
#define AR_PHY_SFCORR_M2COUNT_THR_S 0
#define AR_PHY_SFCORR_M1_THRESH 0x00FE0000
#define AR_PHY_SFCORR_M1_THRESH_S 17
#define AR_PHY_SFCORR_M2_THRESH 0x7F000000
#define AR_PHY_SFCORR_M2_THRESH_S 24
#define AR_PHY_SLEEP_CTR_CONTROL 0x9870
#define AR_PHY_SLEEP_CTR_LIMIT 0x9874
#define AR_PHY_SYNTH_CONTROL 0x9874
#define AR_PHY_SLEEP_SCAL 0x9878
#define AR_PHY_PLL_CTL 0x987c
#define AR_PHY_PLL_CTL_40 0xaa
#define AR_PHY_PLL_CTL_40_5413 0x04
#define AR_PHY_PLL_CTL_44 0xab
#define AR_PHY_PLL_CTL_44_2133 0xeb
#define AR_PHY_PLL_CTL_40_2133 0xea
#define AR_PHY_RX_DELAY 0x9914
#define AR_PHY_SEARCH_START_DELAY 0x9918
#define AR_PHY_RX_DELAY_DELAY 0x00003FFF
#define AR_PHY_TIMING_CTRL4(_i) (0x9920 + ((_i) << 12))
#define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF 0x01F
#define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF_S 0
#define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF 0x7E0
#define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S 5
#define AR_PHY_TIMING_CTRL4_IQCORR_ENABLE 0x800
#define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX 0xF000
#define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S 12
#define AR_PHY_TIMING_CTRL4_DO_CAL 0x10000
#define AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI 0x80000000
#define AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER 0x40000000
#define AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK 0x20000000
#define AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK 0x10000000
#define AR_PHY_TIMING5 0x9924
#define AR_PHY_TIMING5_CYCPWR_THR1 0x000000FE
#define AR_PHY_TIMING5_CYCPWR_THR1_S 1
#define AR_PHY_POWER_TX_RATE1 0x9934
#define AR_PHY_POWER_TX_RATE2 0x9938
#define AR_PHY_POWER_TX_RATE_MAX 0x993c
#define AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE 0x00000040
#define AR_PHY_FRAME_CTL 0x9944
#define AR_PHY_FRAME_CTL_TX_CLIP 0x00000038
#define AR_PHY_FRAME_CTL_TX_CLIP_S 3
#define AR_PHY_TXPWRADJ 0x994C
#define AR_PHY_TXPWRADJ_CCK_GAIN_DELTA 0x00000FC0
#define AR_PHY_TXPWRADJ_CCK_GAIN_DELTA_S 6
#define AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX 0x00FC0000
#define AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX_S 18
#define AR_PHY_RADAR_EXT 0x9940
#define AR_PHY_RADAR_EXT_ENA 0x00004000
#define AR_PHY_RADAR_0 0x9954
#define AR_PHY_RADAR_0_ENA 0x00000001
#define AR_PHY_RADAR_0_FFT_ENA 0x80000000
#define AR_PHY_RADAR_0_INBAND 0x0000003e
#define AR_PHY_RADAR_0_INBAND_S 1
#define AR_PHY_RADAR_0_PRSSI 0x00000FC0
#define AR_PHY_RADAR_0_PRSSI_S 6
#define AR_PHY_RADAR_0_HEIGHT 0x0003F000
#define AR_PHY_RADAR_0_HEIGHT_S 12
#define AR_PHY_RADAR_0_RRSSI 0x00FC0000
#define AR_PHY_RADAR_0_RRSSI_S 18
#define AR_PHY_RADAR_0_FIRPWR 0x7F000000
#define AR_PHY_RADAR_0_FIRPWR_S 24
#define AR_PHY_RADAR_1 0x9958
#define AR_PHY_RADAR_1_RELPWR_ENA 0x00800000
#define AR_PHY_RADAR_1_USE_FIR128 0x00400000
#define AR_PHY_RADAR_1_RELPWR_THRESH 0x003F0000
#define AR_PHY_RADAR_1_RELPWR_THRESH_S 16
#define AR_PHY_RADAR_1_BLOCK_CHECK 0x00008000
#define AR_PHY_RADAR_1_MAX_RRSSI 0x00004000
#define AR_PHY_RADAR_1_RELSTEP_CHECK 0x00002000
#define AR_PHY_RADAR_1_RELSTEP_THRESH 0x00001F00
#define AR_PHY_RADAR_1_RELSTEP_THRESH_S 8
#define AR_PHY_RADAR_1_MAXLEN 0x000000FF
#define AR_PHY_RADAR_1_MAXLEN_S 0
#define AR_PHY_SWITCH_CHAIN_0 0x9960
#define AR_PHY_SWITCH_COM 0x9964
#define AR_PHY_SIGMA_DELTA 0x996C
#define AR_PHY_SIGMA_DELTA_ADC_SEL 0x00000003
#define AR_PHY_SIGMA_DELTA_ADC_SEL_S 0
#define AR_PHY_SIGMA_DELTA_FILT2 0x000000F8
#define AR_PHY_SIGMA_DELTA_FILT2_S 3
#define AR_PHY_SIGMA_DELTA_FILT1 0x00001F00
#define AR_PHY_SIGMA_DELTA_FILT1_S 8
#define AR_PHY_SIGMA_DELTA_ADC_CLIP 0x01FFE000
#define AR_PHY_SIGMA_DELTA_ADC_CLIP_S 13
#define AR_PHY_RESTART 0x9970
#define AR_PHY_RESTART_DIV_GC 0x001C0000
#define AR_PHY_RESTART_DIV_GC_S 18
#define AR_PHY_RFBUS_REQ 0x997C
#define AR_PHY_RFBUS_REQ_EN 0x00000001
#define AR_PHY_TIMING7 0x9980
#define AR_PHY_TIMING8 0x9984
#define AR_PHY_TIMING8_PILOT_MASK_2 0x000FFFFF
#define AR_PHY_TIMING8_PILOT_MASK_2_S 0
#define AR_PHY_BIN_MASK2_1 0x9988
#define AR_PHY_BIN_MASK2_2 0x998c
#define AR_PHY_BIN_MASK2_3 0x9990
#define AR_PHY_BIN_MASK2_4 0x9994
#define AR_PHY_BIN_MASK_1 0x9900
#define AR_PHY_BIN_MASK_2 0x9904
#define AR_PHY_BIN_MASK_3 0x9908
#define AR_PHY_MASK_CTL 0x990c
#define AR_PHY_BIN_MASK2_4_MASK_4 0x00003FFF
#define AR_PHY_BIN_MASK2_4_MASK_4_S 0
#define AR_PHY_TIMING9 0x9998
#define AR_PHY_TIMING10 0x999c
#define AR_PHY_TIMING10_PILOT_MASK_2 0x000FFFFF
#define AR_PHY_TIMING10_PILOT_MASK_2_S 0
#define AR_PHY_TIMING11 0x99a0
#define AR_PHY_TIMING11_SPUR_DELTA_PHASE 0x000FFFFF
#define AR_PHY_TIMING11_SPUR_DELTA_PHASE_S 0
#define AR_PHY_TIMING11_SPUR_FREQ_SD 0x3FF00000
#define AR_PHY_TIMING11_SPUR_FREQ_SD_S 20
#define AR_PHY_TIMING11_USE_SPUR_IN_AGC 0x40000000
#define AR_PHY_TIMING11_USE_SPUR_IN_SELFCOR 0x80000000
#define AR_PHY_RX_CHAINMASK 0x99a4
#define AR_PHY_NEW_ADC_DC_GAIN_CORR(_i) (0x99b4 + ((_i) << 12))
#define AR_PHY_NEW_ADC_GAIN_CORR_ENABLE 0x40000000
#define AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE 0x80000000
#define AR_PHY_MULTICHAIN_GAIN_CTL 0x99ac
#define AR_PHY_EXT_CCA0 0x99b8
#define AR_PHY_EXT_CCA0_THRESH62 0x000000FF
#define AR_PHY_EXT_CCA0_THRESH62_S 0
#define AR_PHY_EXT_CCA 0x99bc
#define AR_PHY_EXT_CCA_CYCPWR_THR1 0x0000FE00
#define AR_PHY_EXT_CCA_CYCPWR_THR1_S 9
#define AR_PHY_EXT_CCA_THRESH62 0x007F0000
#define AR_PHY_EXT_CCA_THRESH62_S 16
#define AR_PHY_EXT_MINCCA_PWR 0xFF800000
#define AR_PHY_EXT_MINCCA_PWR_S 23
#define AR9280_PHY_EXT_MINCCA_PWR 0x01FF0000
#define AR9280_PHY_EXT_MINCCA_PWR_S 16
#define AR_PHY_SFCORR_EXT 0x99c0
#define AR_PHY_SFCORR_EXT_M1_THRESH 0x0000007F
#define AR_PHY_SFCORR_EXT_M1_THRESH_S 0
#define AR_PHY_SFCORR_EXT_M2_THRESH 0x00003F80
#define AR_PHY_SFCORR_EXT_M2_THRESH_S 7
#define AR_PHY_SFCORR_EXT_M1_THRESH_LOW 0x001FC000
#define AR_PHY_SFCORR_EXT_M1_THRESH_LOW_S 14
#define AR_PHY_SFCORR_EXT_M2_THRESH_LOW 0x0FE00000
#define AR_PHY_SFCORR_EXT_M2_THRESH_LOW_S 21
#define AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S 28
#define AR_PHY_HALFGI 0x99D0
#define AR_PHY_HALFGI_DSC_MAN 0x0007FFF0
#define AR_PHY_HALFGI_DSC_MAN_S 4
#define AR_PHY_HALFGI_DSC_EXP 0x0000000F
#define AR_PHY_HALFGI_DSC_EXP_S 0
#define AR_PHY_CHAN_INFO_MEMORY 0x99DC
#define AR_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK 0x0001
#define AR_PHY_HEAVY_CLIP_ENABLE 0x99E0
#define AR_PHY_M_SLEEP 0x99f0
#define AR_PHY_REFCLKDLY 0x99f4
#define AR_PHY_REFCLKPD 0x99f8
#define AR_PHY_CALMODE 0x99f0
#define AR_PHY_CALMODE_IQ 0x00000000
#define AR_PHY_CALMODE_ADC_GAIN 0x00000001
#define AR_PHY_CALMODE_ADC_DC_PER 0x00000002
#define AR_PHY_CALMODE_ADC_DC_INIT 0x00000003
#define AR_PHY_CAL_MEAS_0(_i) (0x9c10 + ((_i) << 12))
#define AR_PHY_CAL_MEAS_1(_i) (0x9c14 + ((_i) << 12))
#define AR_PHY_CAL_MEAS_2(_i) (0x9c18 + ((_i) << 12))
#define AR_PHY_CAL_MEAS_3(_i) (0x9c1c + ((_i) << 12))
#define AR_PHY_CURRENT_RSSI 0x9c1c
#define AR9280_PHY_CURRENT_RSSI 0x9c3c
#define AR_PHY_RFBUS_GRANT 0x9C20
#define AR_PHY_RFBUS_GRANT_EN 0x00000001
#define AR_PHY_CHAN_INFO_GAIN_DIFF 0x9CF4
#define AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320
#define AR_PHY_CHAN_INFO_GAIN 0x9CFC
#define AR_PHY_MODE 0xA200
#define AR_PHY_MODE_AR2133 0x08
#define AR_PHY_MODE_AR5111 0x00
#define AR_PHY_MODE_AR5112 0x08
#define AR_PHY_MODE_DYNAMIC 0x04
#define AR_PHY_MODE_RF2GHZ 0x02
#define AR_PHY_MODE_RF5GHZ 0x00
#define AR_PHY_MODE_CCK 0x01
#define AR_PHY_MODE_OFDM 0x00
#define AR_PHY_MODE_DYN_CCK_DISABLE 0x100
#define AR_PHY_CCK_TX_CTRL 0xA204
#define AR_PHY_CCK_TX_CTRL_JAPAN 0x00000010
#define AR_PHY_CCK_DETECT 0xA208
#define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK 0x0000003F
#define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S 0
/* [12:6] settling time for antenna switch */
#define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME 0x00001FC0
#define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME_S 6
#define AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV 0x2000
#define AR_PHY_GAIN_2GHZ 0xA20C
#define AR_PHY_GAIN_2GHZ_RXTX_MARGIN 0x00FC0000
#define AR_PHY_GAIN_2GHZ_RXTX_MARGIN_S 18
#define AR_PHY_GAIN_2GHZ_BSW_MARGIN 0x00003C00
#define AR_PHY_GAIN_2GHZ_BSW_MARGIN_S 10
#define AR_PHY_GAIN_2GHZ_BSW_ATTEN 0x0000001F
#define AR_PHY_GAIN_2GHZ_BSW_ATTEN_S 0
#define AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN 0x003E0000
#define AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN_S 17
#define AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN 0x0001F000
#define AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN_S 12
#define AR_PHY_GAIN_2GHZ_XATTEN2_DB 0x00000FC0
#define AR_PHY_GAIN_2GHZ_XATTEN2_DB_S 6
#define AR_PHY_GAIN_2GHZ_XATTEN1_DB 0x0000003F
#define AR_PHY_GAIN_2GHZ_XATTEN1_DB_S 0
#define AR_PHY_CCK_RXCTRL4 0xA21C
#define AR_PHY_CCK_RXCTRL4_FREQ_EST_SHORT 0x01F80000
#define AR_PHY_CCK_RXCTRL4_FREQ_EST_SHORT_S 19
#define AR_PHY_DAG_CTRLCCK 0xA228
#define AR_PHY_DAG_CTRLCCK_EN_RSSI_THR 0x00000200
#define AR_PHY_DAG_CTRLCCK_RSSI_THR 0x0001FC00
#define AR_PHY_DAG_CTRLCCK_RSSI_THR_S 10
#define AR_PHY_FORCE_CLKEN_CCK 0xA22C
#define AR_PHY_FORCE_CLKEN_CCK_MRC_MUX 0x00000040
#define AR_PHY_POWER_TX_RATE3 0xA234
#define AR_PHY_POWER_TX_RATE4 0xA238
#define AR_PHY_SCRM_SEQ_XR 0xA23C
#define AR_PHY_HEADER_DETECT_XR 0xA240
#define AR_PHY_CHIRP_DETECTED_XR 0xA244
#define AR_PHY_BLUETOOTH 0xA254
#define AR_PHY_TPCRG1 0xA258
#define AR_PHY_TPCRG1_NUM_PD_GAIN 0x0000c000
#define AR_PHY_TPCRG1_NUM_PD_GAIN_S 14
#define AR_PHY_TPCRG1_PD_GAIN_1 0x00030000
#define AR_PHY_TPCRG1_PD_GAIN_1_S 16
#define AR_PHY_TPCRG1_PD_GAIN_2 0x000C0000
#define AR_PHY_TPCRG1_PD_GAIN_2_S 18
#define AR_PHY_TPCRG1_PD_GAIN_3 0x00300000
#define AR_PHY_TPCRG1_PD_GAIN_3_S 20
#define AR_PHY_VIT_MASK2_M_46_61 0xa3a0
#define AR_PHY_MASK2_M_31_45 0xa3a4
#define AR_PHY_MASK2_M_16_30 0xa3a8
#define AR_PHY_MASK2_M_00_15 0xa3ac
#define AR_PHY_MASK2_P_15_01 0xa3b8
#define AR_PHY_MASK2_P_30_16 0xa3bc
#define AR_PHY_MASK2_P_45_31 0xa3c0
#define AR_PHY_MASK2_P_61_45 0xa3c4
#define AR_PHY_SPUR_REG 0x994c
#define AR_PHY_SPUR_REG_MASK_RATE_CNTL (0xFF << 18)
#define AR_PHY_SPUR_REG_MASK_RATE_CNTL_S 18
#define AR_PHY_SPUR_REG_ENABLE_MASK_PPM 0x20000
#define AR_PHY_SPUR_REG_MASK_RATE_SELECT (0xFF << 9)
#define AR_PHY_SPUR_REG_MASK_RATE_SELECT_S 9
#define AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI 0x100
#define AR_PHY_SPUR_REG_SPUR_RSSI_THRESH 0x7F
#define AR_PHY_SPUR_REG_SPUR_RSSI_THRESH_S 0
#define AR_PHY_PILOT_MASK_01_30 0xa3b0
#define AR_PHY_PILOT_MASK_31_60 0xa3b4
#define AR_PHY_CHANNEL_MASK_01_30 0x99d4
#define AR_PHY_CHANNEL_MASK_31_60 0x99d8
#define AR_PHY_ANALOG_SWAP 0xa268
#define AR_PHY_SWAP_ALT_CHAIN 0x00000040
#define AR_PHY_TPCRG5 0xA26C
#define AR_PHY_TPCRG5_PD_GAIN_OVERLAP 0x0000000F
#define AR_PHY_TPCRG5_PD_GAIN_OVERLAP_S 0
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1 0x000003F0
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1_S 4
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2 0x0000FC00
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2_S 10
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3 0x003F0000
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3_S 16
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4 0x0FC00000
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4_S 22
#define AR_PHY_POWER_TX_RATE5 0xA38C
#define AR_PHY_POWER_TX_RATE6 0xA390
#define AR_PHY_CAL_CHAINMASK 0xA39C
#define AR_PHY_POWER_TX_SUB 0xA3C8
#define AR_PHY_POWER_TX_RATE7 0xA3CC
#define AR_PHY_POWER_TX_RATE8 0xA3D0
#define AR_PHY_POWER_TX_RATE9 0xA3D4
#define AR_PHY_XPA_CFG 0xA3D8
#define AR_PHY_FORCE_XPA_CFG 0x000000001
#define AR_PHY_FORCE_XPA_CFG_S 0
#define AR_PHY_CH1_CCA 0xa864
#define AR_PHY_CH1_MINCCA_PWR 0x0FF80000
#define AR_PHY_CH1_MINCCA_PWR_S 19
#define AR9280_PHY_CH1_MINCCA_PWR 0x1FF00000
#define AR9280_PHY_CH1_MINCCA_PWR_S 20
#define AR_PHY_CH2_CCA 0xb864
#define AR_PHY_CH2_MINCCA_PWR 0x0FF80000
#define AR_PHY_CH2_MINCCA_PWR_S 19
#define AR_PHY_CH1_EXT_CCA 0xa9bc
#define AR_PHY_CH1_EXT_MINCCA_PWR 0xFF800000
#define AR_PHY_CH1_EXT_MINCCA_PWR_S 23
#define AR9280_PHY_CH1_EXT_MINCCA_PWR 0x01FF0000
#define AR9280_PHY_CH1_EXT_MINCCA_PWR_S 16
#define AR_PHY_CH2_EXT_CCA 0xb9bc
#define AR_PHY_CH2_EXT_MINCCA_PWR 0xFF800000
#define AR_PHY_CH2_EXT_MINCCA_PWR_S 23
#define REG_WRITE_RF_ARRAY(iniarray, regData, regWr) do { \
int r; \
for (r = 0; r < ((iniarray)->ia_rows); r++) { \
REG_WRITE(ah, INI_RA((iniarray), r, 0), (regData)[r]); \
DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, \
"RF 0x%x V 0x%x\n", \
INI_RA((iniarray), r, 0), (regData)[r]); \
DO_DELAY(regWr); \
} \
} while (0)
#define ATH9K_KEY_XOR 0xaa
#define ATH9K_IS_MIC_ENABLED(ah) \
(AH5416(ah)->ah_staId1Defaults & AR_STA_ID1_CRPT_MIC_ENABLE)
#define ANTSWAP_AB 0x0001
#define REDUCE_CHAIN_0 0x00000050
#define REDUCE_CHAIN_1 0x00000051
#define RF_BANK_SETUP(_bank, _iniarray, _col) do { \
int i; \
for (i = 0; i < (_iniarray)->ia_rows; i++) \
(_bank)[i] = INI_RA((_iniarray), i, _col);; \
} while (0)
#endif
/*
* Copyright (c) 2004 Video54 Technologies, Inc.
* Copyright (c) 2004-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.
*/
/*
* Atheros rate control algorithm
*/
#include "core.h"
#include "../net/mac80211/rate.h"
static u32 tx_triglevel_max;
static struct ath_rate_table ar5416_11na_ratetable = {
42,
{
{ TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 6 Mb */
5400, 0x0b, 0x00, 12,
0, 2, 1, 0, 0, 0, 0, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 9 Mb */
7800, 0x0f, 0x00, 18,
0, 3, 1, 1, 1, 1, 1, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */
10000, 0x0a, 0x00, 24,
2, 4, 2, 2, 2, 2, 2, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */
13900, 0x0e, 0x00, 36,
2, 6, 2, 3, 3, 3, 3, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */
17300, 0x09, 0x00, 48,
4, 10, 3, 4, 4, 4, 4, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */
23000, 0x0d, 0x00, 72,
4, 14, 3, 5, 5, 5, 5, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */
27400, 0x08, 0x00, 96,
4, 20, 3, 6, 6, 6, 6, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */
29300, 0x0c, 0x00, 108,
4, 23, 3, 7, 7, 7, 7, 0 },
{ TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 6500, /* 6.5 Mb */
6400, 0x80, 0x00, 0,
0, 2, 3, 8, 24, 8, 24, 3216 },
{ TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 13000, /* 13 Mb */
12700, 0x81, 0x00, 1,
2, 4, 3, 9, 25, 9, 25, 6434 },
{ TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 19500, /* 19.5 Mb */
18800, 0x82, 0x00, 2,
2, 6, 3, 10, 26, 10, 26, 9650 },
{ TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 26000, /* 26 Mb */
25000, 0x83, 0x00, 3,
4, 10, 3, 11, 27, 11, 27, 12868 },
{ TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 39000, /* 39 Mb */
36700, 0x84, 0x00, 4,
4, 14, 3, 12, 28, 12, 28, 19304 },
{ FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 52000, /* 52 Mb */
48100, 0x85, 0x00, 5,
4, 20, 3, 13, 29, 13, 29, 25740 },
{ FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 58500, /* 58.5 Mb */
53500, 0x86, 0x00, 6,
4, 23, 3, 14, 30, 14, 30, 28956 },
{ FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 65000, /* 65 Mb */
59000, 0x87, 0x00, 7,
4, 25, 3, 15, 31, 15, 32, 32180 },
{ FALSE, FALSE, WLAN_PHY_HT_20_DS, 13000, /* 13 Mb */
12700, 0x88, 0x00,
8, 0, 2, 3, 16, 33, 16, 33, 6430 },
{ FALSE, FALSE, WLAN_PHY_HT_20_DS, 26000, /* 26 Mb */
24800, 0x89, 0x00, 9,
2, 4, 3, 17, 34, 17, 34, 12860 },
{ FALSE, FALSE, WLAN_PHY_HT_20_DS, 39000, /* 39 Mb */
36600, 0x8a, 0x00, 10,
2, 6, 3, 18, 35, 18, 35, 19300 },
{ TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 52000, /* 52 Mb */
48100, 0x8b, 0x00, 11,
4, 10, 3, 19, 36, 19, 36, 25736 },
{ TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 78000, /* 78 Mb */
69500, 0x8c, 0x00, 12,
4, 14, 3, 20, 37, 20, 37, 38600 },
{ TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 104000, /* 104 Mb */
89500, 0x8d, 0x00, 13,
4, 20, 3, 21, 38, 21, 38, 51472 },
{ TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 117000, /* 117 Mb */
98900, 0x8e, 0x00, 14,
4, 23, 3, 22, 39, 22, 39, 57890 },
{ TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 130000, /* 130 Mb */
108300, 0x8f, 0x00, 15,
4, 25, 3, 23, 40, 23, 41, 64320 },
{ TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 13500, /* 13.5 Mb */
13200, 0x80, 0x00, 0,
0, 2, 3, 8, 24, 24, 24, 6684 },
{ TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 27500, /* 27.0 Mb */
25900, 0x81, 0x00, 1,
2, 4, 3, 9, 25, 25, 25, 13368 },
{ TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 40500, /* 40.5 Mb */
38600, 0x82, 0x00, 2,
2, 6, 3, 10, 26, 26, 26, 20052 },
{ TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 54000, /* 54 Mb */
49800, 0x83, 0x00, 3,
4, 10, 3, 11, 27, 27, 27, 26738 },
{ TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 81500, /* 81 Mb */
72200, 0x84, 0x00, 4,
4, 14, 3, 12, 28, 28, 28, 40104 },
{ FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 108000, /* 108 Mb */
92900, 0x85, 0x00, 5,
4, 20, 3, 13, 29, 29, 29, 53476 },
{ FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 121500, /* 121.5 Mb */
102700, 0x86, 0x00, 6,
4, 23, 3, 14, 30, 30, 30, 60156 },
{ FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 135000, /* 135 Mb */
112000, 0x87, 0x00, 7,
4, 25, 3, 15, 31, 32, 32, 66840 },
{ FALSE, TRUE_40, WLAN_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */
122000, 0x87, 0x00, 7,
4, 25, 3, 15, 31, 32, 32, 74200 },
{ FALSE, FALSE, WLAN_PHY_HT_40_DS, 27000, /* 27 Mb */
25800, 0x88, 0x00, 8,
0, 2, 3, 16, 33, 33, 33, 13360 },
{ FALSE, FALSE, WLAN_PHY_HT_40_DS, 54000, /* 54 Mb */
49800, 0x89, 0x00, 9,
2, 4, 3, 17, 34, 34, 34, 26720 },
{ FALSE, FALSE, WLAN_PHY_HT_40_DS, 81000, /* 81 Mb */
71900, 0x8a, 0x00, 10,
2, 6, 3, 18, 35, 35, 35, 40080 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 108000, /* 108 Mb */
92500, 0x8b, 0x00, 11,
4, 10, 3, 19, 36, 36, 36, 53440 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 162000, /* 162 Mb */
130300, 0x8c, 0x00, 12,
4, 14, 3, 20, 37, 37, 37, 80160 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 216000, /* 216 Mb */
162800, 0x8d, 0x00, 13,
4, 20, 3, 21, 38, 38, 38, 106880 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 243000, /* 243 Mb */
178200, 0x8e, 0x00, 14,
4, 23, 3, 22, 39, 39, 39, 120240 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 270000, /* 270 Mb */
192100, 0x8f, 0x00, 15,
4, 25, 3, 23, 40, 41, 41, 133600 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */
207000, 0x8f, 0x00, 15,
4, 25, 3, 23, 40, 41, 41, 148400 },
},
50, /* probe interval */
50, /* rssi reduce interval */
WLAN_RC_HT_FLAG, /* Phy rates allowed initially */
};
/* TRUE_ALL - valid for 20/40/Legacy,
* TRUE - Legacy only,
* TRUE_20 - HT 20 only,
* TRUE_40 - HT 40 only */
/* 4ms frame limit not used for NG mode. The values filled
* for HT are the 64K max aggregate limit */
static struct ath_rate_table ar5416_11ng_ratetable = {
46,
{
{ TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 1000, /* 1 Mb */
900, 0x1b, 0x00, 2,
0, 0, 1, 0, 0, 0, 0, 0 },
{ TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 2000, /* 2 Mb */
1900, 0x1a, 0x04, 4,
1, 1, 1, 1, 1, 1, 1, 0 },
{ TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 5500, /* 5.5 Mb */
4900, 0x19, 0x04, 11,
2, 2, 2, 2, 2, 2, 2, 0 },
{ TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 11000, /* 11 Mb */
8100, 0x18, 0x04, 22,
3, 3, 2, 3, 3, 3, 3, 0 },
{ FALSE, FALSE, WLAN_PHY_OFDM, 6000, /* 6 Mb */
5400, 0x0b, 0x00, 12,
4, 2, 1, 4, 4, 4, 4, 0 },
{ FALSE, FALSE, WLAN_PHY_OFDM, 9000, /* 9 Mb */
7800, 0x0f, 0x00, 18,
4, 3, 1, 5, 5, 5, 5, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */
10100, 0x0a, 0x00, 24,
6, 4, 1, 6, 6, 6, 6, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */
14100, 0x0e, 0x00, 36,
6, 6, 2, 7, 7, 7, 7, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */
17700, 0x09, 0x00, 48,
8, 10, 3, 8, 8, 8, 8, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */
23700, 0x0d, 0x00, 72,
8, 14, 3, 9, 9, 9, 9, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */
27400, 0x08, 0x00, 96,
8, 20, 3, 10, 10, 10, 10, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */
30900, 0x0c, 0x00, 108,
8, 23, 3, 11, 11, 11, 11, 0 },
{ FALSE, FALSE, WLAN_PHY_HT_20_SS, 6500, /* 6.5 Mb */
6400, 0x80, 0x00, 0,
4, 2, 3, 12, 28, 12, 28, 3216 },
{ TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 13000, /* 13 Mb */
12700, 0x81, 0x00, 1,
6, 4, 3, 13, 29, 13, 29, 6434 },
{ TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 19500, /* 19.5 Mb */
18800, 0x82, 0x00, 2,
6, 6, 3, 14, 30, 14, 30, 9650 },
{ TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 26000, /* 26 Mb */
25000, 0x83, 0x00, 3,
8, 10, 3, 15, 31, 15, 31, 12868 },
{ TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 39000, /* 39 Mb */
36700, 0x84, 0x00, 4,
8, 14, 3, 16, 32, 16, 32, 19304 },
{ FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 52000, /* 52 Mb */
48100, 0x85, 0x00, 5,
8, 20, 3, 17, 33, 17, 33, 25740 },
{ FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 58500, /* 58.5 Mb */
53500, 0x86, 0x00, 6,
8, 23, 3, 18, 34, 18, 34, 28956 },
{ FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 65000, /* 65 Mb */
59000, 0x87, 0x00, 7,
8, 25, 3, 19, 35, 19, 36, 32180 },
{ FALSE, FALSE, WLAN_PHY_HT_20_DS, 13000, /* 13 Mb */
12700, 0x88, 0x00, 8,
4, 2, 3, 20, 37, 20, 37, 6430 },
{ FALSE, FALSE, WLAN_PHY_HT_20_DS, 26000, /* 26 Mb */
24800, 0x89, 0x00, 9,
6, 4, 3, 21, 38, 21, 38, 12860 },
{ FALSE, FALSE, WLAN_PHY_HT_20_DS, 39000, /* 39 Mb */
36600, 0x8a, 0x00, 10,
6, 6, 3, 22, 39, 22, 39, 19300 },
{ TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 52000, /* 52 Mb */
48100, 0x8b, 0x00, 11,
8, 10, 3, 23, 40, 23, 40, 25736 },
{ TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 78000, /* 78 Mb */
69500, 0x8c, 0x00, 12,
8, 14, 3, 24, 41, 24, 41, 38600 },
{ TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 104000, /* 104 Mb */
89500, 0x8d, 0x00, 13,
8, 20, 3, 25, 42, 25, 42, 51472 },
{ TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 117000, /* 117 Mb */
98900, 0x8e, 0x00, 14,
8, 23, 3, 26, 43, 26, 44, 57890 },
{ TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 130000, /* 130 Mb */
108300, 0x8f, 0x00, 15,
8, 25, 3, 27, 44, 27, 45, 64320 },
{ TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 13500, /* 13.5 Mb */
13200, 0x80, 0x00, 0,
8, 2, 3, 12, 28, 28, 28, 6684 },
{ TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 27500, /* 27.0 Mb */
25900, 0x81, 0x00, 1,
8, 4, 3, 13, 29, 29, 29, 13368 },
{ TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 40500, /* 40.5 Mb */
38600, 0x82, 0x00, 2,
8, 6, 3, 14, 30, 30, 30, 20052 },
{ TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 54000, /* 54 Mb */
49800, 0x83, 0x00, 3,
8, 10, 3, 15, 31, 31, 31, 26738 },
{ TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 81500, /* 81 Mb */
72200, 0x84, 0x00, 4,
8, 14, 3, 16, 32, 32, 32, 40104 },
{ FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 108000, /* 108 Mb */
92900, 0x85, 0x00, 5,
8, 20, 3, 17, 33, 33, 33, 53476 },
{ FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 121500, /* 121.5 Mb */
102700, 0x86, 0x00, 6,
8, 23, 3, 18, 34, 34, 34, 60156 },
{ FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 135000, /* 135 Mb */
112000, 0x87, 0x00, 7,
8, 23, 3, 19, 35, 36, 36, 66840 },
{ FALSE, TRUE_40, WLAN_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */
122000, 0x87, 0x00, 7,
8, 25, 3, 19, 35, 36, 36, 74200 },
{ FALSE, FALSE, WLAN_PHY_HT_40_DS, 27000, /* 27 Mb */
25800, 0x88, 0x00, 8,
8, 2, 3, 20, 37, 37, 37, 13360 },
{ FALSE, FALSE, WLAN_PHY_HT_40_DS, 54000, /* 54 Mb */
49800, 0x89, 0x00, 9,
8, 4, 3, 21, 38, 38, 38, 26720 },
{ FALSE, FALSE, WLAN_PHY_HT_40_DS, 81000, /* 81 Mb */
71900, 0x8a, 0x00, 10,
8, 6, 3, 22, 39, 39, 39, 40080 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 108000, /* 108 Mb */
92500, 0x8b, 0x00, 11,
8, 10, 3, 23, 40, 40, 40, 53440 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 162000, /* 162 Mb */
130300, 0x8c, 0x00, 12,
8, 14, 3, 24, 41, 41, 41, 80160 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 216000, /* 216 Mb */
162800, 0x8d, 0x00, 13,
8, 20, 3, 25, 42, 42, 42, 106880 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 243000, /* 243 Mb */
178200, 0x8e, 0x00, 14,
8, 23, 3, 26, 43, 43, 43, 120240 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 270000, /* 270 Mb */
192100, 0x8f, 0x00, 15,
8, 23, 3, 27, 44, 45, 45, 133600 },
{ TRUE_40, FALSE, WLAN_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */
207000, 0x8f, 0x00, 15,
8, 25, 3, 27, 44, 45, 45, 148400 },
},
50, /* probe interval */
50, /* rssi reduce interval */
WLAN_RC_HT_FLAG, /* Phy rates allowed initially */
};
static struct ath_rate_table ar5416_11a_ratetable = {
8,
{
{ TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 6 Mb */
5400, 0x0b, 0x00, (0x80|12),
0, 2, 1, 0, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 9 Mb */
7800, 0x0f, 0x00, 18,
0, 3, 1, 1, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */
10000, 0x0a, 0x00, (0x80|24),
2, 4, 2, 2, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */
13900, 0x0e, 0x00, 36,
2, 6, 2, 3, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */
17300, 0x09, 0x00, (0x80|48),
4, 10, 3, 4, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */
23000, 0x0d, 0x00, 72,
4, 14, 3, 5, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */
27400, 0x08, 0x00, 96,
4, 19, 3, 6, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */
29300, 0x0c, 0x00, 108,
4, 23, 3, 7, 0 },
},
50, /* probe interval */
50, /* rssi reduce interval */
0, /* Phy rates allowed initially */
};
static struct ath_rate_table ar5416_11a_ratetable_Half = {
8,
{
{ TRUE, TRUE, WLAN_PHY_OFDM, 3000, /* 6 Mb */
2700, 0x0b, 0x00, (0x80|6),
0, 2, 1, 0, 0},
{ TRUE, TRUE, WLAN_PHY_OFDM, 4500, /* 9 Mb */
3900, 0x0f, 0x00, 9,
0, 3, 1, 1, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 12 Mb */
5000, 0x0a, 0x00, (0x80|12),
2, 4, 2, 2, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 18 Mb */
6950, 0x0e, 0x00, 18,
2, 6, 2, 3, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 24 Mb */
8650, 0x09, 0x00, (0x80|24),
4, 10, 3, 4, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 36 Mb */
11500, 0x0d, 0x00, 36,
4, 14, 3, 5, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 48 Mb */
13700, 0x08, 0x00, 48,
4, 19, 3, 6, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 27000, /* 54 Mb */
14650, 0x0c, 0x00, 54,
4, 23, 3, 7, 0 },
},
50, /* probe interval */
50, /* rssi reduce interval */
0, /* Phy rates allowed initially */
};
static struct ath_rate_table ar5416_11a_ratetable_Quarter = {
8,
{
{ TRUE, TRUE, WLAN_PHY_OFDM, 1500, /* 6 Mb */
1350, 0x0b, 0x00, (0x80|3),
0, 2, 1, 0, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 2250, /* 9 Mb */
1950, 0x0f, 0x00, 4,
0, 3, 1, 1, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 3000, /* 12 Mb */
2500, 0x0a, 0x00, (0x80|6),
2, 4, 2, 2, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 4500, /* 18 Mb */
3475, 0x0e, 0x00, 9,
2, 6, 2, 3, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 25 Mb */
4325, 0x09, 0x00, (0x80|12),
4, 10, 3, 4, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 36 Mb */
5750, 0x0d, 0x00, 18,
4, 14, 3, 5, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 48 Mb */
6850, 0x08, 0x00, 24,
4, 19, 3, 6, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 13500, /* 54 Mb */
7325, 0x0c, 0x00, 27,
4, 23, 3, 7, 0 },
},
50, /* probe interval */
50, /* rssi reduce interval */
0, /* Phy rates allowed initially */
};
static struct ath_rate_table ar5416_11g_ratetable = {
12,
{
{ TRUE, TRUE, WLAN_PHY_CCK, 1000, /* 1 Mb */
900, 0x1b, 0x00, 2,
0, 0, 1, 0, 0 },
{ TRUE, TRUE, WLAN_PHY_CCK, 2000, /* 2 Mb */
1900, 0x1a, 0x04, 4,
1, 1, 1, 1, 0 },
{ TRUE, TRUE, WLAN_PHY_CCK, 5500, /* 5.5 Mb */
4900, 0x19, 0x04, 11,
2, 2, 2, 2, 0 },
{ TRUE, TRUE, WLAN_PHY_CCK, 11000, /* 11 Mb */
8100, 0x18, 0x04, 22,
3, 3, 2, 3, 0 },
{ FALSE, FALSE, WLAN_PHY_OFDM, 6000, /* 6 Mb */
5400, 0x0b, 0x00, 12,
4, 2, 1, 4, 0 },
{ FALSE, FALSE, WLAN_PHY_OFDM, 9000, /* 9 Mb */
7800, 0x0f, 0x00, 18,
4, 3, 1, 5, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */
10000, 0x0a, 0x00, 24,
6, 4, 1, 6, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */
13900, 0x0e, 0x00, 36,
6, 6, 2, 7, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */
17300, 0x09, 0x00, 48,
8, 10, 3, 8, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */
23000, 0x0d, 0x00, 72,
8, 14, 3, 9, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */
27400, 0x08, 0x00, 96,
8, 19, 3, 10, 0 },
{ TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */
29300, 0x0c, 0x00, 108,
8, 23, 3, 11, 0 },
},
50, /* probe interval */
50, /* rssi reduce interval */
0, /* Phy rates allowed initially */
};
static struct ath_rate_table ar5416_11b_ratetable = {
4,
{
{ TRUE, TRUE, WLAN_PHY_CCK, 1000, /* 1 Mb */
900, 0x1b, 0x00, (0x80|2),
0, 0, 1, 0, 0 },
{ TRUE, TRUE, WLAN_PHY_CCK, 2000, /* 2 Mb */
1800, 0x1a, 0x04, (0x80|4),
1, 1, 1, 1, 0 },
{ TRUE, TRUE, WLAN_PHY_CCK, 5500, /* 5.5 Mb */
4300, 0x19, 0x04, (0x80|11),
1, 2, 2, 2, 0 },
{ TRUE, TRUE, WLAN_PHY_CCK, 11000, /* 11 Mb */
7100, 0x18, 0x04, (0x80|22),
1, 4, 100, 3, 0 },
},
100, /* probe interval */
100, /* rssi reduce interval */
0, /* Phy rates allowed initially */
};
static void ar5416_attach_ratetables(struct ath_rate_softc *sc)
{
/*
* Attach rate tables.
*/
sc->hw_rate_table[ATH9K_MODE_11B] = &ar5416_11b_ratetable;
sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable;
sc->hw_rate_table[ATH9K_MODE_11G] = &ar5416_11g_ratetable;
sc->hw_rate_table[ATH9K_MODE_11NA_HT20] = &ar5416_11na_ratetable;
sc->hw_rate_table[ATH9K_MODE_11NG_HT20] = &ar5416_11ng_ratetable;
sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS] =
&ar5416_11na_ratetable;
sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS] =
&ar5416_11na_ratetable;
sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS] =
&ar5416_11ng_ratetable;
sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS] =
&ar5416_11ng_ratetable;
}
static void ar5416_setquarter_ratetable(struct ath_rate_softc *sc)
{
sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable_Quarter;
return;
}
static void ar5416_sethalf_ratetable(struct ath_rate_softc *sc)
{
sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable_Half;
return;
}
static void ar5416_setfull_ratetable(struct ath_rate_softc *sc)
{
sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable;
return;
}
/*
* Return the median of three numbers
*/
static inline int8_t median(int8_t a, int8_t b, int8_t c)
{
if (a >= b) {
if (b >= c)
return b;
else if (a > c)
return c;
else
return a;
} else {
if (a >= c)
return a;
else if (b >= c)
return c;
else
return b;
}
}
static void ath_rc_sort_validrates(const struct ath_rate_table *rate_table,
struct ath_tx_ratectrl *rate_ctrl)
{
u8 i, j, idx, idx_next;
for (i = rate_ctrl->max_valid_rate - 1; i > 0; i--) {
for (j = 0; j <= i-1; j++) {
idx = rate_ctrl->valid_rate_index[j];
idx_next = rate_ctrl->valid_rate_index[j+1];
if (rate_table->info[idx].ratekbps >
rate_table->info[idx_next].ratekbps) {
rate_ctrl->valid_rate_index[j] = idx_next;
rate_ctrl->valid_rate_index[j+1] = idx;
}
}
}
}
/* Access functions for valid_txrate_mask */
static void ath_rc_init_valid_txmask(struct ath_tx_ratectrl *rate_ctrl)
{
u8 i;
for (i = 0; i < rate_ctrl->rate_table_size; i++)
rate_ctrl->valid_rate_index[i] = FALSE;
}
static inline void ath_rc_set_valid_txmask(struct ath_tx_ratectrl *rate_ctrl,
u8 index, int valid_tx_rate)
{
ASSERT(index <= rate_ctrl->rate_table_size);
rate_ctrl->valid_rate_index[index] = valid_tx_rate ? TRUE : FALSE;
}
static inline int ath_rc_isvalid_txmask(struct ath_tx_ratectrl *rate_ctrl,
u8 index)
{
ASSERT(index <= rate_ctrl->rate_table_size);
return rate_ctrl->valid_rate_index[index];
}
/* Iterators for valid_txrate_mask */
static inline int
ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table,
struct ath_tx_ratectrl *rate_ctrl,
u8 cur_valid_txrate,
u8 *next_idx)
{
u8 i;
for (i = 0; i < rate_ctrl->max_valid_rate - 1; i++) {
if (rate_ctrl->valid_rate_index[i] == cur_valid_txrate) {
*next_idx = rate_ctrl->valid_rate_index[i+1];
return TRUE;
}
}
/* No more valid rates */
*next_idx = 0;
return FALSE;
}
/* Return true only for single stream */
static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw)
{
if (WLAN_RC_PHY_HT(phy) & !(capflag & WLAN_RC_HT_FLAG))
return FALSE;
if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG))
return FALSE;
if (WLAN_RC_PHY_SGI(phy) && !(capflag & WLAN_RC_SGI_FLAG))
return FALSE;
if (!ignore_cw && WLAN_RC_PHY_HT(phy))
if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG))
return FALSE;
if (!WLAN_RC_PHY_40(phy) && (capflag & WLAN_RC_40_FLAG))
return FALSE;
return TRUE;
}
static inline int
ath_rc_get_nextlowervalid_txrate(const struct ath_rate_table *rate_table,
struct ath_tx_ratectrl *rate_ctrl,
u8 cur_valid_txrate, u8 *next_idx)
{
int8_t i;
for (i = 1; i < rate_ctrl->max_valid_rate ; i++) {
if (rate_ctrl->valid_rate_index[i] == cur_valid_txrate) {
*next_idx = rate_ctrl->valid_rate_index[i-1];
return TRUE;
}
}
return FALSE;
}
/*
* Initialize the Valid Rate Index from valid entries in Rate Table
*/
static u8
ath_rc_sib_init_validrates(struct ath_rate_node *ath_rc_priv,
const struct ath_rate_table *rate_table,
u32 capflag)
{
struct ath_tx_ratectrl *rate_ctrl;
u8 i, hi = 0;
u32 valid;
rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv);
for (i = 0; i < rate_table->rate_cnt; i++) {
valid = (ath_rc_priv->single_stream ?
rate_table->info[i].valid_single_stream :
rate_table->info[i].valid);
if (valid == TRUE) {
u32 phy = rate_table->info[i].phy;
u8 valid_rate_count = 0;
if (!ath_rc_valid_phyrate(phy, capflag, FALSE))
continue;
valid_rate_count = rate_ctrl->valid_phy_ratecnt[phy];
rate_ctrl->valid_phy_rateidx[phy][valid_rate_count] = i;
rate_ctrl->valid_phy_ratecnt[phy] += 1;
ath_rc_set_valid_txmask(rate_ctrl, i, TRUE);
hi = A_MAX(hi, i);
}
}
return hi;
}
/*
* Initialize the Valid Rate Index from Rate Set
*/
static u8
ath_rc_sib_setvalid_rates(struct ath_rate_node *ath_rc_priv,
const struct ath_rate_table *rate_table,
struct ath_rateset *rateset,
u32 capflag)
{
/* XXX: Clean me up and make identation friendly */
u8 i, j, hi = 0;
struct ath_tx_ratectrl *rate_ctrl =
(struct ath_tx_ratectrl *)(ath_rc_priv);
/* Use intersection of working rates and valid rates */
for (i = 0; i < rateset->rs_nrates; i++) {
for (j = 0; j < rate_table->rate_cnt; j++) {
u32 phy = rate_table->info[j].phy;
u32 valid = (ath_rc_priv->single_stream ?
rate_table->info[j].valid_single_stream :
rate_table->info[j].valid);
/* We allow a rate only if its valid and the
* capflag matches one of the validity
* (TRUE/TRUE_20/TRUE_40) flags */
/* XXX: catch the negative of this branch
* first and then continue */
if (((rateset->rs_rates[i] & 0x7F) ==
(rate_table->info[j].dot11rate & 0x7F)) &&
((valid & WLAN_RC_CAP_MODE(capflag)) ==
WLAN_RC_CAP_MODE(capflag)) &&
!WLAN_RC_PHY_HT(phy)) {
u8 valid_rate_count = 0;
if (!ath_rc_valid_phyrate(phy, capflag, FALSE))
continue;
valid_rate_count =
rate_ctrl->valid_phy_ratecnt[phy];
rate_ctrl->valid_phy_rateidx[phy]
[valid_rate_count] = j;
rate_ctrl->valid_phy_ratecnt[phy] += 1;
ath_rc_set_valid_txmask(rate_ctrl, j, TRUE);
hi = A_MAX(hi, j);
}
}
}
return hi;
}
static u8
ath_rc_sib_setvalid_htrates(struct ath_rate_node *ath_rc_priv,
const struct ath_rate_table *rate_table,
u8 *mcs_set, u32 capflag)
{
u8 i, j, hi = 0;
struct ath_tx_ratectrl *rate_ctrl =
(struct ath_tx_ratectrl *)(ath_rc_priv);
/* Use intersection of working rates and valid rates */
for (i = 0; i < ((struct ath_rateset *)mcs_set)->rs_nrates; i++) {
for (j = 0; j < rate_table->rate_cnt; j++) {
u32 phy = rate_table->info[j].phy;
u32 valid = (ath_rc_priv->single_stream ?
rate_table->info[j].valid_single_stream :
rate_table->info[j].valid);
if (((((struct ath_rateset *)
mcs_set)->rs_rates[i] & 0x7F) !=
(rate_table->info[j].dot11rate & 0x7F)) ||
!WLAN_RC_PHY_HT(phy) ||
!WLAN_RC_PHY_HT_VALID(valid, capflag))
continue;
if (!ath_rc_valid_phyrate(phy, capflag, FALSE))
continue;
rate_ctrl->valid_phy_rateidx[phy]
[rate_ctrl->valid_phy_ratecnt[phy]] = j;
rate_ctrl->valid_phy_ratecnt[phy] += 1;
ath_rc_set_valid_txmask(rate_ctrl, j, TRUE);
hi = A_MAX(hi, j);
}
}
return hi;
}
/*
* Attach to a device instance. Setup the public definition
* of how much per-node space we need and setup the private
* phy tables that have rate control parameters.
*/
struct ath_rate_softc *ath_rate_attach(struct ath_hal *ah)
{
struct ath_rate_softc *asc;
/* we are only in user context so we can sleep for memory */
asc = kzalloc(sizeof(struct ath_rate_softc), GFP_KERNEL);
if (asc == NULL)
return NULL;
ar5416_attach_ratetables(asc);
/* Save Maximum TX Trigger Level (used for 11n) */
tx_triglevel_max = ah->ah_caps.tx_triglevel_max;
/* return alias for ath_rate_softc * */
return asc;
}
static struct ath_rate_node *ath_rate_node_alloc(struct ath_vap *avp,
struct ath_rate_softc *rsc,
gfp_t gfp)
{
struct ath_rate_node *anode;
anode = kzalloc(sizeof(struct ath_rate_node), gfp);
if (anode == NULL)
return NULL;
anode->avp = avp;
anode->asc = rsc;
avp->rc_node = anode;
return anode;
}
static void ath_rate_node_free(struct ath_rate_node *anode)
{
if (anode != NULL)
kfree(anode);
}
void ath_rate_detach(struct ath_rate_softc *asc)
{
if (asc != NULL)
kfree(asc);
}
u8 ath_rate_findrateix(struct ath_softc *sc,
u8 dot11rate)
{
const struct ath_rate_table *ratetable;
struct ath_rate_softc *rsc = sc->sc_rc;
int i;
ratetable = rsc->hw_rate_table[sc->sc_curmode];
if (WARN_ON(!ratetable))
return 0;
for (i = 0; i < ratetable->rate_cnt; i++) {
if ((ratetable->info[i].dot11rate & 0x7f) == (dot11rate & 0x7f))
return i;
}
return 0;
}
/*
* Update rate-control state on a device state change. When
* operating as a station this includes associate/reassociate
* with an AP. Otherwise this gets called, for example, when
* the we transition to run state when operating as an AP.
*/
void ath_rate_newstate(struct ath_softc *sc, struct ath_vap *avp)
{
struct ath_rate_softc *asc = sc->sc_rc;
/* For half and quarter rate channles use different
* rate tables
*/
if (sc->sc_curchan.channelFlags & CHANNEL_HALF)
ar5416_sethalf_ratetable(asc);
else if (sc->sc_curchan.channelFlags & CHANNEL_QUARTER)
ar5416_setquarter_ratetable(asc);
else /* full rate */
ar5416_setfull_ratetable(asc);
if (avp->av_config.av_fixed_rateset != IEEE80211_FIXED_RATE_NONE) {
asc->fixedrix =
sc->sc_rixmap[avp->av_config.av_fixed_rateset & 0xff];
/* NB: check the fixed rate exists */
if (asc->fixedrix == 0xff)
asc->fixedrix = IEEE80211_FIXED_RATE_NONE;
} else {
asc->fixedrix = IEEE80211_FIXED_RATE_NONE;
}
}
static u8 ath_rc_ratefind_ht(struct ath_softc *sc,
struct ath_rate_node *ath_rc_priv,
const struct ath_rate_table *rate_table,
int probe_allowed, int *is_probing,
int is_retry)
{
u32 dt, best_thruput, this_thruput, now_msec;
u8 rate, next_rate, best_rate, maxindex, minindex;
int8_t rssi_last, rssi_reduce = 0, index = 0;
struct ath_tx_ratectrl *rate_ctrl = NULL;
rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv ?
(ath_rc_priv) : NULL);
*is_probing = FALSE;
rssi_last = median(rate_ctrl->rssi_last,
rate_ctrl->rssi_last_prev,
rate_ctrl->rssi_last_prev2);
/*
* Age (reduce) last ack rssi based on how old it is.
* The bizarre numbers are so the delta is 160msec,
* meaning we divide by 16.
* 0msec <= dt <= 25msec: don't derate
* 25msec <= dt <= 185msec: derate linearly from 0 to 10dB
* 185msec <= dt: derate by 10dB
*/
now_msec = jiffies_to_msecs(jiffies);
dt = now_msec - rate_ctrl->rssi_time;
if (dt >= 185)
rssi_reduce = 10;
else if (dt >= 25)
rssi_reduce = (u8)((dt - 25) >> 4);
/* Now reduce rssi_last by rssi_reduce */
if (rssi_last < rssi_reduce)
rssi_last = 0;
else
rssi_last -= rssi_reduce;
/*
* Now look up the rate in the rssi table and return it.
* If no rates match then we return 0 (lowest rate)
*/
best_thruput = 0;
maxindex = rate_ctrl->max_valid_rate-1;
minindex = 0;
best_rate = minindex;
/*
* Try the higher rate first. It will reduce memory moving time
* if we have very good channel characteristics.
*/
for (index = maxindex; index >= minindex ; index--) {
u8 per_thres;
rate = rate_ctrl->valid_rate_index[index];
if (rate > rate_ctrl->rate_max_phy)
continue;
/*
* For TCP the average collision rate is around 11%,
* so we ignore PERs less than this. This is to
* prevent the rate we are currently using (whose
* PER might be in the 10-15 range because of TCP
* collisions) looking worse than the next lower
* rate whose PER has decayed close to 0. If we
* used to next lower rate, its PER would grow to
* 10-15 and we would be worse off then staying
* at the current rate.
*/
per_thres = rate_ctrl->state[rate].per;
if (per_thres < 12)
per_thres = 12;
this_thruput = rate_table->info[rate].user_ratekbps *
(100 - per_thres);
if (best_thruput <= this_thruput) {
best_thruput = this_thruput;
best_rate = rate;
}
}
rate = best_rate;
/* if we are retrying for more than half the number
* of max retries, use the min rate for the next retry
*/
if (is_retry)
rate = rate_ctrl->valid_rate_index[minindex];
rate_ctrl->rssi_last_lookup = rssi_last;
/*
* Must check the actual rate (ratekbps) to account for
* non-monoticity of 11g's rate table
*/
if (rate >= rate_ctrl->rate_max_phy && probe_allowed) {
rate = rate_ctrl->rate_max_phy;
/* Probe the next allowed phy state */
/* FIXME:XXXX Check to make sure ratMax is checked properly */
if (ath_rc_get_nextvalid_txrate(rate_table,
rate_ctrl, rate, &next_rate) &&
(now_msec - rate_ctrl->probe_time >
rate_table->probe_interval) &&
(rate_ctrl->hw_maxretry_pktcnt >= 1)) {
rate = next_rate;
rate_ctrl->probe_rate = rate;
rate_ctrl->probe_time = now_msec;
rate_ctrl->hw_maxretry_pktcnt = 0;
*is_probing = TRUE;
}
}
/*
* Make sure rate is not higher than the allowed maximum.
* We should also enforce the min, but I suspect the min is
* normally 1 rather than 0 because of the rate 9 vs 6 issue
* in the old code.
*/
if (rate > (rate_ctrl->rate_table_size - 1))
rate = rate_ctrl->rate_table_size - 1;
ASSERT((rate_table->info[rate].valid && !ath_rc_priv->single_stream) ||
(rate_table->info[rate].valid_single_stream &&
ath_rc_priv->single_stream));
return rate;
}
static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table ,
struct ath_rc_series *series,
u8 tries,
u8 rix,
int rtsctsenable)
{
series->tries = tries;
series->flags = (rtsctsenable ? ATH_RC_RTSCTS_FLAG : 0) |
(WLAN_RC_PHY_DS(rate_table->info[rix].phy) ?
ATH_RC_DS_FLAG : 0) |
(WLAN_RC_PHY_40(rate_table->info[rix].phy) ?
ATH_RC_CW40_FLAG : 0) |
(WLAN_RC_PHY_SGI(rate_table->info[rix].phy) ?
ATH_RC_SGI_FLAG : 0);
series->rix = rate_table->info[rix].base_index;
series->max_4ms_framelen = rate_table->info[rix].max_4ms_framelen;
}
static u8 ath_rc_rate_getidx(struct ath_softc *sc,
struct ath_rate_node *ath_rc_priv,
const struct ath_rate_table *rate_table,
u8 rix, u16 stepdown,
u16 min_rate)
{
u32 j;
u8 nextindex;
struct ath_tx_ratectrl *rate_ctrl =
(struct ath_tx_ratectrl *)(ath_rc_priv);
if (min_rate) {
for (j = RATE_TABLE_SIZE; j > 0; j--) {
if (ath_rc_get_nextlowervalid_txrate(rate_table,
rate_ctrl, rix, &nextindex))
rix = nextindex;
else
break;
}
} else {
for (j = stepdown; j > 0; j--) {
if (ath_rc_get_nextlowervalid_txrate(rate_table,
rate_ctrl, rix, &nextindex))
rix = nextindex;
else
break;
}
}
return rix;
}
static void ath_rc_ratefind(struct ath_softc *sc,
struct ath_rate_node *ath_rc_priv,
int num_tries, int num_rates, unsigned int rcflag,
struct ath_rc_series series[], int *is_probe,
int is_retry)
{
u8 try_per_rate = 0, i = 0, rix, nrix;
struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc;
struct ath_rate_table *rate_table;
rate_table =
(struct ath_rate_table *)asc->hw_rate_table[sc->sc_curmode];
rix = ath_rc_ratefind_ht(sc, ath_rc_priv, rate_table,
(rcflag & ATH_RC_PROBE_ALLOWED) ? 1 : 0,
is_probe, is_retry);
nrix = rix;
if ((rcflag & ATH_RC_PROBE_ALLOWED) && (*is_probe)) {
/* set one try for probe rates. For the
* probes don't enable rts */
ath_rc_rate_set_series(rate_table,
&series[i++], 1, nrix, FALSE);
try_per_rate = (num_tries/num_rates);
/* Get the next tried/allowed rate. No RTS for the next series
* after the probe rate
*/
nrix = ath_rc_rate_getidx(sc,
ath_rc_priv, rate_table, nrix, 1, FALSE);
ath_rc_rate_set_series(rate_table,
&series[i++], try_per_rate, nrix, 0);
} else {
try_per_rate = (num_tries/num_rates);
/* Set the choosen rate. No RTS for first series entry. */
ath_rc_rate_set_series(rate_table,
&series[i++], try_per_rate, nrix, FALSE);
}
/* Fill in the other rates for multirate retry */
for ( ; i < num_rates; i++) {
u8 try_num;
u8 min_rate;
try_num = ((i + 1) == num_rates) ?
num_tries - (try_per_rate * i) : try_per_rate ;
min_rate = (((i + 1) == num_rates) &&
(rcflag & ATH_RC_MINRATE_LASTRATE)) ? 1 : 0;
nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
rate_table, nrix, 1, min_rate);
/* All other rates in the series have RTS enabled */
ath_rc_rate_set_series(rate_table,
&series[i], try_num, nrix, TRUE);
}
/*
* NB:Change rate series to enable aggregation when operating
* at lower MCS rates. When first rate in series is MCS2
* in HT40 @ 2.4GHz, series should look like:
*
* {MCS2, MCS1, MCS0, MCS0}.
*
* When first rate in series is MCS3 in HT20 @ 2.4GHz, series should
* look like:
*
* {MCS3, MCS2, MCS1, MCS1}
*
* So, set fourth rate in series to be same as third one for
* above conditions.
*/
if ((sc->sc_curmode == ATH9K_MODE_11NG_HT20) ||
(sc->sc_curmode == ATH9K_MODE_11NG_HT40PLUS) ||
(sc->sc_curmode == ATH9K_MODE_11NG_HT40MINUS)) {
u8 dot11rate = rate_table->info[rix].dot11rate;
u8 phy = rate_table->info[rix].phy;
if (i == 4 &&
((dot11rate == 2 && phy == WLAN_RC_PHY_HT_40_SS) ||
(dot11rate == 3 && phy == WLAN_RC_PHY_HT_20_SS))) {
series[3].rix = series[2].rix;
series[3].flags = series[2].flags;
series[3].max_4ms_framelen = series[2].max_4ms_framelen;
}
}
}
/*
* Return the Tx rate series.
*/
void ath_rate_findrate(struct ath_softc *sc,
struct ath_rate_node *ath_rc_priv,
int num_tries,
int num_rates,
unsigned int rcflag,
struct ath_rc_series series[],
int *is_probe,
int is_retry)
{
struct ath_vap *avp = ath_rc_priv->avp;
DPRINTF(sc, ATH_DBG_RATE, "%s", __func__);
if (!num_rates || !num_tries)
return;
if (avp->av_config.av_fixed_rateset == IEEE80211_FIXED_RATE_NONE) {
ath_rc_ratefind(sc, ath_rc_priv, num_tries, num_rates,
rcflag, series, is_probe, is_retry);
} else {
/* Fixed rate */
int idx;
u8 flags;
u32 rix;
struct ath_rate_softc *asc = ath_rc_priv->asc;
struct ath_rate_table *rate_table;
rate_table = (struct ath_rate_table *)
asc->hw_rate_table[sc->sc_curmode];
for (idx = 0; idx < 4; idx++) {
unsigned int mcs;
u8 series_rix = 0;
series[idx].tries =
IEEE80211_RATE_IDX_ENTRY(
avp->av_config.av_fixed_retryset, idx);
mcs = IEEE80211_RATE_IDX_ENTRY(
avp->av_config.av_fixed_rateset, idx);
if (idx == 3 && (mcs & 0xf0) == 0x70)
mcs = (mcs & ~0xf0)|0x80;
if (!(mcs & 0x80))
flags = 0;
else
flags = ((ath_rc_priv->ht_cap &
WLAN_RC_DS_FLAG) ?
ATH_RC_DS_FLAG : 0) |
((ath_rc_priv->ht_cap &
WLAN_RC_40_FLAG) ?
ATH_RC_CW40_FLAG : 0) |
((ath_rc_priv->ht_cap &
WLAN_RC_SGI_FLAG) ?
((ath_rc_priv->ht_cap &
WLAN_RC_40_FLAG) ?
ATH_RC_SGI_FLAG : 0) : 0);
series[idx].rix = sc->sc_rixmap[mcs];
series_rix = series[idx].rix;
/* XXX: Give me some cleanup love */
if ((flags & ATH_RC_CW40_FLAG) &&
(flags & ATH_RC_SGI_FLAG))
rix = rate_table->info[series_rix].ht_index;
else if (flags & ATH_RC_SGI_FLAG)
rix = rate_table->info[series_rix].sgi_index;
else if (flags & ATH_RC_CW40_FLAG)
rix = rate_table->info[series_rix].cw40index;
else
rix = rate_table->info[series_rix].base_index;
series[idx].max_4ms_framelen =
rate_table->info[rix].max_4ms_framelen;
series[idx].flags = flags;
}
}
}
static void ath_rc_update_ht(struct ath_softc *sc,
struct ath_rate_node *ath_rc_priv,
struct ath_tx_info_priv *info_priv,
int tx_rate, int xretries, int retries)
{
struct ath_tx_ratectrl *rate_ctrl;
u32 now_msec = jiffies_to_msecs(jiffies);
int state_change = FALSE, rate, count;
u8 last_per;
struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc;
struct ath_rate_table *rate_table =
(struct ath_rate_table *)asc->hw_rate_table[sc->sc_curmode];
static u32 nretry_to_per_lookup[10] = {
100 * 0 / 1,
100 * 1 / 4,
100 * 1 / 2,
100 * 3 / 4,
100 * 4 / 5,
100 * 5 / 6,
100 * 6 / 7,
100 * 7 / 8,
100 * 8 / 9,
100 * 9 / 10
};
if (!ath_rc_priv)
return;
rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv);
ASSERT(tx_rate >= 0);
if (tx_rate < 0)
return;
/* To compensate for some imbalance between ctrl and ext. channel */
if (WLAN_RC_PHY_40(rate_table->info[tx_rate].phy))
info_priv->tx.ts_rssi =
info_priv->tx.ts_rssi < 3 ? 0 :
info_priv->tx.ts_rssi - 3;
last_per = rate_ctrl->state[tx_rate].per;
if (xretries) {
/* Update the PER. */
if (xretries == 1) {
rate_ctrl->state[tx_rate].per += 30;
if (rate_ctrl->state[tx_rate].per > 100)
rate_ctrl->state[tx_rate].per = 100;
} else {
/* xretries == 2 */
count = sizeof(nretry_to_per_lookup) /
sizeof(nretry_to_per_lookup[0]);
if (retries >= count)
retries = count - 1;
/* new_PER = 7/8*old_PER + 1/8*(currentPER) */
rate_ctrl->state[tx_rate].per =
(u8)(rate_ctrl->state[tx_rate].per -
(rate_ctrl->state[tx_rate].per >> 3) +
((100) >> 3));
}
/* xretries == 1 or 2 */
if (rate_ctrl->probe_rate == tx_rate)
rate_ctrl->probe_rate = 0;
} else { /* xretries == 0 */
/* Update the PER. */
/* Make sure it doesn't index out of array's bounds. */
count = sizeof(nretry_to_per_lookup) /
sizeof(nretry_to_per_lookup[0]);
if (retries >= count)
retries = count - 1;
if (info_priv->n_bad_frames) {
/* new_PER = 7/8*old_PER + 1/8*(currentPER) */
/*
* Assuming that n_frames is not 0. The current PER
* from the retries is 100 * retries / (retries+1),
* since the first retries attempts failed, and the
* next one worked. For the one that worked,
* n_bad_frames subframes out of n_frames wored,
* so the PER for that part is
* 100 * n_bad_frames / n_frames, and it contributes
* 100 * n_bad_frames / (n_frames * (retries+1)) to
* the above PER. The expression below is a
* simplified version of the sum of these two terms.
*/
if (info_priv->n_frames > 0)
rate_ctrl->state[tx_rate].per
= (u8)
(rate_ctrl->state[tx_rate].per -
(rate_ctrl->state[tx_rate].per >> 3) +
((100*(retries*info_priv->n_frames +
info_priv->n_bad_frames) /
(info_priv->n_frames *
(retries+1))) >> 3));
} else {
/* new_PER = 7/8*old_PER + 1/8*(currentPER) */
rate_ctrl->state[tx_rate].per = (u8)
(rate_ctrl->state[tx_rate].per -
(rate_ctrl->state[tx_rate].per >> 3) +
(nretry_to_per_lookup[retries] >> 3));
}
rate_ctrl->rssi_last_prev2 = rate_ctrl->rssi_last_prev;
rate_ctrl->rssi_last_prev = rate_ctrl->rssi_last;
rate_ctrl->rssi_last = info_priv->tx.ts_rssi;
rate_ctrl->rssi_time = now_msec;
/*
* If we got at most one retry then increase the max rate if
* this was a probe. Otherwise, ignore the probe.
*/
if (rate_ctrl->probe_rate && rate_ctrl->probe_rate == tx_rate) {
if (retries > 0 || 2 * info_priv->n_bad_frames >
info_priv->n_frames) {
/*
* Since we probed with just a single attempt,
* any retries means the probe failed. Also,
* if the attempt worked, but more than half
* the subframes were bad then also consider
* the probe a failure.
*/
rate_ctrl->probe_rate = 0;
} else {
u8 probe_rate = 0;
rate_ctrl->rate_max_phy = rate_ctrl->probe_rate;
probe_rate = rate_ctrl->probe_rate;
if (rate_ctrl->state[probe_rate].per > 30)
rate_ctrl->state[probe_rate].per = 20;
rate_ctrl->probe_rate = 0;
/*
* Since this probe succeeded, we allow the next
* probe twice as soon. This allows the maxRate
* to move up faster if the probes are
* succesful.
*/
rate_ctrl->probe_time = now_msec -
rate_table->probe_interval / 2;
}
}
if (retries > 0) {
/*
* Don't update anything. We don't know if
* this was because of collisions or poor signal.
*
* Later: if rssi_ack is close to
* rate_ctrl->state[txRate].rssi_thres and we see lots
* of retries, then we could increase
* rate_ctrl->state[txRate].rssi_thres.
*/
rate_ctrl->hw_maxretry_pktcnt = 0;
} else {
/*
* It worked with no retries. First ignore bogus (small)
* rssi_ack values.
*/
if (tx_rate == rate_ctrl->rate_max_phy &&
rate_ctrl->hw_maxretry_pktcnt < 255) {
rate_ctrl->hw_maxretry_pktcnt++;
}
if (info_priv->tx.ts_rssi >=
rate_table->info[tx_rate].rssi_ack_validmin) {
/* Average the rssi */
if (tx_rate != rate_ctrl->rssi_sum_rate) {
rate_ctrl->rssi_sum_rate = tx_rate;
rate_ctrl->rssi_sum =
rate_ctrl->rssi_sum_cnt = 0;
}
rate_ctrl->rssi_sum += info_priv->tx.ts_rssi;
rate_ctrl->rssi_sum_cnt++;
if (rate_ctrl->rssi_sum_cnt > 4) {
int32_t rssi_ackAvg =
(rate_ctrl->rssi_sum + 2) / 4;
int8_t rssi_thres =
rate_ctrl->state[tx_rate].
rssi_thres;
int8_t rssi_ack_vmin =
rate_table->info[tx_rate].
rssi_ack_validmin;
rate_ctrl->rssi_sum =
rate_ctrl->rssi_sum_cnt = 0;
/* Now reduce the current
* rssi threshold. */
if ((rssi_ackAvg < rssi_thres + 2) &&
(rssi_thres > rssi_ack_vmin)) {
rate_ctrl->state[tx_rate].
rssi_thres--;
}
state_change = TRUE;
}
}
}
}
/* For all cases */
/*
* If this rate looks bad (high PER) then stop using it for
* a while (except if we are probing).
*/
if (rate_ctrl->state[tx_rate].per >= 55 && tx_rate > 0 &&
rate_table->info[tx_rate].ratekbps <=
rate_table->info[rate_ctrl->rate_max_phy].ratekbps) {
ath_rc_get_nextlowervalid_txrate(rate_table, rate_ctrl,
(u8) tx_rate, &rate_ctrl->rate_max_phy);
/* Don't probe for a little while. */
rate_ctrl->probe_time = now_msec;
}
if (state_change) {
/*
* Make sure the rates above this have higher rssi thresholds.
* (Note: Monotonicity is kept within the OFDM rates and
* within the CCK rates. However, no adjustment is
* made to keep the rssi thresholds monotonically
* increasing between the CCK and OFDM rates.)
*/
for (rate = tx_rate; rate <
rate_ctrl->rate_table_size - 1; rate++) {
if (rate_table->info[rate+1].phy !=
rate_table->info[tx_rate].phy)
break;
if (rate_ctrl->state[rate].rssi_thres +
rate_table->info[rate].rssi_ack_deltamin >
rate_ctrl->state[rate+1].rssi_thres) {
rate_ctrl->state[rate+1].rssi_thres =
rate_ctrl->state[rate].
rssi_thres +
rate_table->info[rate].
rssi_ack_deltamin;
}
}
/* Make sure the rates below this have lower rssi thresholds. */
for (rate = tx_rate - 1; rate >= 0; rate--) {
if (rate_table->info[rate].phy !=
rate_table->info[tx_rate].phy)
break;
if (rate_ctrl->state[rate].rssi_thres +
rate_table->info[rate].rssi_ack_deltamin >
rate_ctrl->state[rate+1].rssi_thres) {
if (rate_ctrl->state[rate+1].rssi_thres <
rate_table->info[rate].
rssi_ack_deltamin)
rate_ctrl->state[rate].rssi_thres = 0;
else {
rate_ctrl->state[rate].rssi_thres =
rate_ctrl->state[rate+1].
rssi_thres -
rate_table->info[rate].
rssi_ack_deltamin;
}
if (rate_ctrl->state[rate].rssi_thres <
rate_table->info[rate].
rssi_ack_validmin) {
rate_ctrl->state[rate].rssi_thres =
rate_table->info[rate].
rssi_ack_validmin;
}
}
}
}
/* Make sure the rates below this have lower PER */
/* Monotonicity is kept only for rates below the current rate. */
if (rate_ctrl->state[tx_rate].per < last_per) {
for (rate = tx_rate - 1; rate >= 0; rate--) {
if (rate_table->info[rate].phy !=
rate_table->info[tx_rate].phy)
break;
if (rate_ctrl->state[rate].per >
rate_ctrl->state[rate+1].per) {
rate_ctrl->state[rate].per =
rate_ctrl->state[rate+1].per;
}
}
}
/* Maintain monotonicity for rates above the current rate */
for (rate = tx_rate; rate < rate_ctrl->rate_table_size - 1; rate++) {
if (rate_ctrl->state[rate+1].per < rate_ctrl->state[rate].per)
rate_ctrl->state[rate+1].per =
rate_ctrl->state[rate].per;
}
/* Every so often, we reduce the thresholds and
* PER (different for CCK and OFDM). */
if (now_msec - rate_ctrl->rssi_down_time >=
rate_table->rssi_reduce_interval) {
for (rate = 0; rate < rate_ctrl->rate_table_size; rate++) {
if (rate_ctrl->state[rate].rssi_thres >
rate_table->info[rate].rssi_ack_validmin)
rate_ctrl->state[rate].rssi_thres -= 1;
}
rate_ctrl->rssi_down_time = now_msec;
}
/* Every so often, we reduce the thresholds
* and PER (different for CCK and OFDM). */
if (now_msec - rate_ctrl->per_down_time >=
rate_table->rssi_reduce_interval) {
for (rate = 0; rate < rate_ctrl->rate_table_size; rate++) {
rate_ctrl->state[rate].per =
7 * rate_ctrl->state[rate].per / 8;
}
rate_ctrl->per_down_time = now_msec;
}
}
/*
* This routine is called in rate control callback tx_status() to give
* the status of previous frames.
*/
static void ath_rc_update(struct ath_softc *sc,
struct ath_rate_node *ath_rc_priv,
struct ath_tx_info_priv *info_priv, int final_ts_idx,
int xretries, int long_retry)
{
struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc;
struct ath_rate_table *rate_table;
struct ath_tx_ratectrl *rate_ctrl;
struct ath_rc_series rcs[4];
u8 flags;
u32 series = 0, rix;
memcpy(rcs, info_priv->rcs, 4 * sizeof(rcs[0]));
rate_table = (struct ath_rate_table *)
asc->hw_rate_table[sc->sc_curmode];
rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv);
ASSERT(rcs[0].tries != 0);
/*
* If the first rate is not the final index, there
* are intermediate rate failures to be processed.
*/
if (final_ts_idx != 0) {
/* Process intermediate rates that failed.*/
for (series = 0; series < final_ts_idx ; series++) {
if (rcs[series].tries != 0) {
flags = rcs[series].flags;
/* If HT40 and we have switched mode from
* 40 to 20 => don't update */
if ((flags & ATH_RC_CW40_FLAG) &&
(rate_ctrl->rc_phy_mode !=
(flags & ATH_RC_CW40_FLAG)))
return;
if ((flags & ATH_RC_CW40_FLAG) &&
(flags & ATH_RC_SGI_FLAG))
rix = rate_table->info[
rcs[series].rix].ht_index;
else if (flags & ATH_RC_SGI_FLAG)
rix = rate_table->info[
rcs[series].rix].sgi_index;
else if (flags & ATH_RC_CW40_FLAG)
rix = rate_table->info[
rcs[series].rix].cw40index;
else
rix = rate_table->info[
rcs[series].rix].base_index;
ath_rc_update_ht(sc, ath_rc_priv,
info_priv, rix,
xretries ? 1 : 2,
rcs[series].tries);
}
}
} else {
/*
* Handle the special case of MIMO PS burst, where the second
* aggregate is sent out with only one rate and one try.
* Treating it as an excessive retry penalizes the rate
* inordinately.
*/
if (rcs[0].tries == 1 && xretries == 1)
xretries = 2;
}
flags = rcs[series].flags;
/* If HT40 and we have switched mode from 40 to 20 => don't update */
if ((flags & ATH_RC_CW40_FLAG) &&
(rate_ctrl->rc_phy_mode != (flags & ATH_RC_CW40_FLAG)))
return;
if ((flags & ATH_RC_CW40_FLAG) && (flags & ATH_RC_SGI_FLAG))
rix = rate_table->info[rcs[series].rix].ht_index;
else if (flags & ATH_RC_SGI_FLAG)
rix = rate_table->info[rcs[series].rix].sgi_index;
else if (flags & ATH_RC_CW40_FLAG)
rix = rate_table->info[rcs[series].rix].cw40index;
else
rix = rate_table->info[rcs[series].rix].base_index;
ath_rc_update_ht(sc, ath_rc_priv, info_priv, rix,
xretries, long_retry);
}
/*
* Process a tx descriptor for a completed transmit (success or failure).
*/
static void ath_rate_tx_complete(struct ath_softc *sc,
struct ath_node *an,
struct ath_rate_node *rc_priv,
struct ath_tx_info_priv *info_priv)
{
int final_ts_idx = info_priv->tx.ts_rateindex;
int tx_status = 0, is_underrun = 0;
struct ath_vap *avp;
avp = rc_priv->avp;
if ((avp->av_config.av_fixed_rateset != IEEE80211_FIXED_RATE_NONE)
|| info_priv->tx.ts_status & ATH9K_TXERR_FILT)
return;
if (info_priv->tx.ts_rssi > 0) {
ATH_RSSI_LPF(an->an_chainmask_sel.tx_avgrssi,
info_priv->tx.ts_rssi);
}
/*
* If underrun error is seen assume it as an excessive retry only
* if prefetch trigger level have reached the max (0x3f for 5416)
* Adjust the long retry as if the frame was tried ATH_11N_TXMAXTRY
* times. This affects how ratectrl updates PER for the failed rate.
*/
if (info_priv->tx.ts_flags &
(ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN) &&
((sc->sc_ah->ah_txTrigLevel) >= tx_triglevel_max)) {
tx_status = 1;
is_underrun = 1;
}
if ((info_priv->tx.ts_status & ATH9K_TXERR_XRETRY) ||
(info_priv->tx.ts_status & ATH9K_TXERR_FIFO))
tx_status = 1;
ath_rc_update(sc, rc_priv, info_priv, final_ts_idx, tx_status,
(is_underrun) ? ATH_11N_TXMAXTRY :
info_priv->tx.ts_longretry);
}
/*
* Update the SIB's rate control information
*
* This should be called when the supported rates change
* (e.g. SME operation, wireless mode change)
*
* It will determine which rates are valid for use.
*/
static void ath_rc_sib_update(struct ath_softc *sc,
struct ath_rate_node *ath_rc_priv,
u32 capflag, int keep_state,
struct ath_rateset *negotiated_rates,
struct ath_rateset *negotiated_htrates)
{
struct ath_rate_table *rate_table = NULL;
struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc;
struct ath_rateset *rateset = negotiated_rates;
u8 *ht_mcs = (u8 *)negotiated_htrates;
struct ath_tx_ratectrl *rate_ctrl = (struct ath_tx_ratectrl *)
(ath_rc_priv);
u8 i, j, k, hi = 0, hthi = 0;
rate_table = (struct ath_rate_table *)
asc->hw_rate_table[sc->sc_curmode];
/* Initial rate table size. Will change depending
* on the working rate set */
rate_ctrl->rate_table_size = MAX_TX_RATE_TBL;
/* Initialize thresholds according to the global rate table */
for (i = 0 ; (i < rate_ctrl->rate_table_size) && (!keep_state); i++) {
rate_ctrl->state[i].rssi_thres =
rate_table->info[i].rssi_ack_validmin;
rate_ctrl->state[i].per = 0;
}
/* Determine the valid rates */
ath_rc_init_valid_txmask(rate_ctrl);
for (i = 0; i < WLAN_RC_PHY_MAX; i++) {
for (j = 0; j < MAX_TX_RATE_PHY; j++)
rate_ctrl->valid_phy_rateidx[i][j] = 0;
rate_ctrl->valid_phy_ratecnt[i] = 0;
}
rate_ctrl->rc_phy_mode = (capflag & WLAN_RC_40_FLAG);
/* Set stream capability */
ath_rc_priv->single_stream = (capflag & WLAN_RC_DS_FLAG) ? 0 : 1;
if (!rateset->rs_nrates) {
/* No working rate, just initialize valid rates */
hi = ath_rc_sib_init_validrates(ath_rc_priv, rate_table,
capflag);
} else {
/* Use intersection of working rates and valid rates */
hi = ath_rc_sib_setvalid_rates(ath_rc_priv, rate_table,
rateset, capflag);
if (capflag & WLAN_RC_HT_FLAG) {
hthi = ath_rc_sib_setvalid_htrates(ath_rc_priv,
rate_table,
ht_mcs,
capflag);
}
hi = A_MAX(hi, hthi);
}
rate_ctrl->rate_table_size = hi + 1;
rate_ctrl->rate_max_phy = 0;
ASSERT(rate_ctrl->rate_table_size <= MAX_TX_RATE_TBL);
for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) {
for (j = 0; j < rate_ctrl->valid_phy_ratecnt[i]; j++) {
rate_ctrl->valid_rate_index[k++] =
rate_ctrl->valid_phy_rateidx[i][j];
}
if (!ath_rc_valid_phyrate(i, rate_table->initial_ratemax, TRUE)
|| !rate_ctrl->valid_phy_ratecnt[i])
continue;
rate_ctrl->rate_max_phy = rate_ctrl->valid_phy_rateidx[i][j-1];
}
ASSERT(rate_ctrl->rate_table_size <= MAX_TX_RATE_TBL);
ASSERT(k <= MAX_TX_RATE_TBL);
rate_ctrl->max_valid_rate = k;
/*
* Some third party vendors don't send the supported rate series in
* order. So sorting to make sure its in order, otherwise our RateFind
* Algo will select wrong rates
*/
ath_rc_sort_validrates(rate_table, rate_ctrl);
rate_ctrl->rate_max_phy = rate_ctrl->valid_rate_index[k-4];
}
/*
* Update rate-control state on station associate/reassociate.
*/
static int ath_rate_newassoc(struct ath_softc *sc,
struct ath_rate_node *ath_rc_priv,
unsigned int capflag,
struct ath_rateset *negotiated_rates,
struct ath_rateset *negotiated_htrates)
{
ath_rc_priv->ht_cap =
((capflag & ATH_RC_DS_FLAG) ? WLAN_RC_DS_FLAG : 0) |
((capflag & ATH_RC_SGI_FLAG) ? WLAN_RC_SGI_FLAG : 0) |
((capflag & ATH_RC_HT_FLAG) ? WLAN_RC_HT_FLAG : 0) |
((capflag & ATH_RC_CW40_FLAG) ? WLAN_RC_40_FLAG : 0);
ath_rc_sib_update(sc, ath_rc_priv, ath_rc_priv->ht_cap, 0,
negotiated_rates, negotiated_htrates);
return 0;
}
/*
* This routine is called to initialize the rate control parameters
* in the SIB. It is called initially during system initialization
* or when a station is associated with the AP.
*/
static void ath_rc_sib_init(struct ath_rate_node *ath_rc_priv)
{
struct ath_tx_ratectrl *rate_ctrl;
rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv);
rate_ctrl->rssi_down_time = jiffies_to_msecs(jiffies);
}
static void ath_setup_rates(struct ieee80211_local *local, struct sta_info *sta)
{
struct ieee80211_supported_band *sband;
struct ieee80211_hw *hw = local_to_hw(local);
struct ath_softc *sc = hw->priv;
struct ath_rate_node *rc_priv = sta->rate_ctrl_priv;
int i, j = 0;
DPRINTF(sc, ATH_DBG_RATE, "%s", __func__);
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
for (i = 0; i < sband->n_bitrates; i++) {
if (sta->supp_rates[local->hw.conf.channel->band] & BIT(i)) {
rc_priv->neg_rates.rs_rates[j]
= (sband->bitrates[i].bitrate * 2) / 10;
j++;
}
}
rc_priv->neg_rates.rs_nrates = j;
}
void ath_rc_node_update(struct ieee80211_hw *hw, struct ath_rate_node *rc_priv)
{
struct ath_softc *sc = hw->priv;
u32 capflag = 0;
if (hw->conf.ht_conf.ht_supported) {
capflag |= ATH_RC_HT_FLAG | ATH_RC_DS_FLAG;
if (sc->sc_ht_info.tx_chan_width == ATH9K_HT_MACMODE_2040)
capflag |= ATH_RC_CW40_FLAG;
}
ath_rate_newassoc(sc, rc_priv, capflag,
&rc_priv->neg_rates,
&rc_priv->neg_ht_rates);
}
/* Rate Control callbacks */
static void ath_tx_status(void *priv, struct net_device *dev,
struct sk_buff *skb)
{
struct ath_softc *sc = priv;
struct ath_tx_info_priv *tx_info_priv;
struct ath_node *an;
struct sta_info *sta;
struct ieee80211_local *local;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr;
__le16 fc;
local = hw_to_local(sc->hw);
hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
spin_lock_bh(&sc->node_lock);
an = ath_node_find(sc, hdr->addr1);
spin_unlock_bh(&sc->node_lock);
sta = sta_info_get(local, hdr->addr1);
if (!an || !sta || !ieee80211_is_data(fc)) {
if (tx_info->driver_data[0] != NULL) {
kfree(tx_info->driver_data[0]);
tx_info->driver_data[0] = NULL;
}
return;
}
if (tx_info->driver_data[0] != NULL) {
ath_rate_tx_complete(sc, an, sta->rate_ctrl_priv, tx_info_priv);
kfree(tx_info->driver_data[0]);
tx_info->driver_data[0] = NULL;
}
}
static void ath_tx_aggr_resp(struct ath_softc *sc,
struct sta_info *sta,
struct ath_node *an,
u8 tidno)
{
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_local *local;
struct ath_atx_tid *txtid;
struct ieee80211_supported_band *sband;
u16 buffersize = 0;
int state;
DECLARE_MAC_BUF(mac);
if (!sc->sc_txaggr)
return;
txtid = ATH_AN_2_TID(an, tidno);
if (!txtid->paused)
return;
local = hw_to_local(sc->hw);
sband = hw->wiphy->bands[hw->conf.channel->band];
buffersize = IEEE80211_MIN_AMPDU_BUF <<
sband->ht_info.ampdu_factor; /* FIXME */
state = sta->ampdu_mlme.tid_state_tx[tidno];
if (state & HT_ADDBA_RECEIVED_MSK) {
txtid->addba_exchangecomplete = 1;
txtid->addba_exchangeinprogress = 0;
txtid->baw_size = buffersize;
DPRINTF(sc, ATH_DBG_AGGR,
"%s: Resuming tid, buffersize: %d\n",
__func__,
buffersize);
ath_tx_resume_tid(sc, txtid);
}
}
static void ath_get_rate(void *priv, struct net_device *dev,
struct ieee80211_supported_band *sband,
struct sk_buff *skb,
struct rate_selection *sel)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
struct ath_softc *sc = (struct ath_softc *)priv;
struct ieee80211_hw *hw = sc->hw;
struct ath_tx_info_priv *tx_info_priv;
struct ath_rate_node *ath_rc_priv;
struct ath_node *an;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
int is_probe, chk, ret;
s8 lowest_idx;
__le16 fc = hdr->frame_control;
u8 *qc, tid;
DECLARE_MAC_BUF(mac);
DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__);
/* allocate driver private area of tx_info */
tx_info->driver_data[0] = kzalloc(sizeof(*tx_info_priv), GFP_ATOMIC);
ASSERT(tx_info->driver_data[0] != NULL);
tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
sta = sta_info_get(local, hdr->addr1);
lowest_idx = rate_lowest_index(local, sband, sta);
tx_info_priv->min_rate = (sband->bitrates[lowest_idx].bitrate * 2) / 10;
/* lowest rate for management and multicast/broadcast frames */
if (!ieee80211_is_data(fc) ||
is_multicast_ether_addr(hdr->addr1) || !sta) {
sel->rate_idx = lowest_idx;
return;
}
ath_rc_priv = sta->rate_ctrl_priv;
/* Find tx rate for unicast frames */
ath_rate_findrate(sc, ath_rc_priv,
ATH_11N_TXMAXTRY, 4,
ATH_RC_PROBE_ALLOWED,
tx_info_priv->rcs,
&is_probe,
false);
if (is_probe)
sel->probe_idx = ((struct ath_tx_ratectrl *)
sta->rate_ctrl_priv)->probe_rate;
/* Ratecontrol sometimes returns invalid rate index */
if (tx_info_priv->rcs[0].rix != 0xff)
ath_rc_priv->prev_data_rix = tx_info_priv->rcs[0].rix;
else
tx_info_priv->rcs[0].rix = ath_rc_priv->prev_data_rix;
sel->rate_idx = tx_info_priv->rcs[0].rix;
/* Check if aggregation has to be enabled for this tid */
if (hw->conf.ht_conf.ht_supported) {
if (ieee80211_is_data_qos(fc)) {
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & 0xf;
spin_lock_bh(&sc->node_lock);
an = ath_node_find(sc, hdr->addr1);
spin_unlock_bh(&sc->node_lock);
if (!an) {
DPRINTF(sc, ATH_DBG_AGGR,
"%s: Node not found to "
"init/chk TX aggr\n", __func__);
return;
}
chk = ath_tx_aggr_check(sc, an, tid);
if (chk == AGGR_REQUIRED) {
ret = ieee80211_start_tx_ba_session(hw,
hdr->addr1, tid);
if (ret)
DPRINTF(sc, ATH_DBG_AGGR,
"%s: Unable to start tx "
"aggr for: %s\n",
__func__,
print_mac(mac, hdr->addr1));
else
DPRINTF(sc, ATH_DBG_AGGR,
"%s: Started tx aggr for: %s\n",
__func__,
print_mac(mac, hdr->addr1));
} else if (chk == AGGR_EXCHANGE_PROGRESS)
ath_tx_aggr_resp(sc, sta, an, tid);
}
}
}
static void ath_rate_init(void *priv, void *priv_sta,
struct ieee80211_local *local,
struct sta_info *sta)
{
struct ieee80211_supported_band *sband;
struct ieee80211_hw *hw = local_to_hw(local);
struct ieee80211_conf *conf = &local->hw.conf;
struct ath_softc *sc = hw->priv;
int i, j = 0;
DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__);
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
sta->txrate_idx = rate_lowest_index(local, sband, sta);
ath_setup_rates(local, sta);
if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
for (i = 0; i < MCS_SET_SIZE; i++) {
if (conf->ht_conf.supp_mcs_set[i/8] & (1<<(i%8)))
((struct ath_rate_node *)
priv_sta)->neg_ht_rates.rs_rates[j++] = i;
if (j == ATH_RATE_MAX)
break;
}
((struct ath_rate_node *)priv_sta)->neg_ht_rates.rs_nrates = j;
}
ath_rc_node_update(hw, priv_sta);
}
static void ath_rate_clear(void *priv)
{
return;
}
static void *ath_rate_alloc(struct ieee80211_local *local)
{
struct ieee80211_hw *hw = local_to_hw(local);
struct ath_softc *sc = hw->priv;
DPRINTF(sc, ATH_DBG_RATE, "%s", __func__);
return local->hw.priv;
}
static void ath_rate_free(void *priv)
{
return;
}
static void *ath_rate_alloc_sta(void *priv, gfp_t gfp)
{
struct ath_softc *sc = priv;
struct ath_vap *avp = sc->sc_vaps[0];
struct ath_rate_node *rate_priv;
DPRINTF(sc, ATH_DBG_RATE, "%s", __func__);
rate_priv = ath_rate_node_alloc(avp, sc->sc_rc, gfp);
if (!rate_priv) {
DPRINTF(sc, ATH_DBG_FATAL, "%s:Unable to allocate"
"private rate control structure", __func__);
return NULL;
}
ath_rc_sib_init(rate_priv);
return rate_priv;
}
static void ath_rate_free_sta(void *priv, void *priv_sta)
{
struct ath_rate_node *rate_priv = priv_sta;
struct ath_softc *sc = priv;
DPRINTF(sc, ATH_DBG_RATE, "%s", __func__);
ath_rate_node_free(rate_priv);
}
static struct rate_control_ops ath_rate_ops = {
.module = NULL,
.name = "ath9k_rate_control",
.tx_status = ath_tx_status,
.get_rate = ath_get_rate,
.rate_init = ath_rate_init,
.clear = ath_rate_clear,
.alloc = ath_rate_alloc,
.free = ath_rate_free,
.alloc_sta = ath_rate_alloc_sta,
.free_sta = ath_rate_free_sta
};
int ath_rate_control_register(void)
{
return ieee80211_rate_control_register(&ath_rate_ops);
}
void ath_rate_control_unregister(void)
{
ieee80211_rate_control_unregister(&ath_rate_ops);
}
/*
* Copyright (c) 2004 Sam Leffler, Errno Consulting
* Copyright (c) 2004 Video54 Technologies, Inc.
* 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 RC_H
#define RC_H
#include "ath9k.h"
/*
* Interface definitions for transmit rate control modules for the
* Atheros driver.
*
* A rate control module is responsible for choosing the transmit rate
* for each data frame. Management+control frames are always sent at
* a fixed rate.
*
* Only one module may be present at a time; the driver references
* rate control interfaces by symbol name. If multiple modules are
* to be supported we'll need to switch to a registration-based scheme
* as is currently done, for example, for authentication modules.
*
* An instance of the rate control module is attached to each device
* at attach time and detached when the device is destroyed. The module
* may associate data with each device and each node (station). Both
* sets of storage are opaque except for the size of the per-node storage
* which must be provided when the module is attached.
*
* The rate control module is notified for each state transition and
* station association/reassociation. Otherwise it is queried for a
* rate for each outgoing frame and provided status from each transmitted
* frame. Any ancillary processing is the responsibility of the module
* (e.g. if periodic processing is required then the module should setup
* it's own timer).
*
* In addition to the transmit rate for each frame the module must also
* indicate the number of attempts to make at the specified rate. If this
* number is != ATH_TXMAXTRY then an additional callback is made to setup
* additional transmit state. The rate control code is assumed to write
* this additional data directly to the transmit descriptor.
*/
struct ath_softc;
#define TRUE 1
#define FALSE 0
#define ATH_RATE_MAX 30
#define MCS_SET_SIZE 128
enum ieee80211_fixed_rate_mode {
IEEE80211_FIXED_RATE_NONE = 0,
IEEE80211_FIXED_RATE_MCS = 1 /* HT rates */
};
/*
* Use the hal os glue code to get ms time
*/
#define IEEE80211_RATE_IDX_ENTRY(val, idx) (((val&(0xff<<(idx*8)))>>(idx*8)))
#define SHORT_PRE 1
#define LONG_PRE 0
#define WLAN_PHY_HT_20_SS WLAN_RC_PHY_HT_20_SS
#define WLAN_PHY_HT_20_DS WLAN_RC_PHY_HT_20_DS
#define WLAN_PHY_HT_20_DS_HGI WLAN_RC_PHY_HT_20_DS_HGI
#define WLAN_PHY_HT_40_SS WLAN_RC_PHY_HT_40_SS
#define WLAN_PHY_HT_40_SS_HGI WLAN_RC_PHY_HT_40_SS_HGI
#define WLAN_PHY_HT_40_DS WLAN_RC_PHY_HT_40_DS
#define WLAN_PHY_HT_40_DS_HGI WLAN_RC_PHY_HT_40_DS_HGI
#define WLAN_PHY_OFDM PHY_OFDM
#define WLAN_PHY_CCK PHY_CCK
#define TRUE_20 0x2
#define TRUE_40 0x4
#define TRUE_2040 (TRUE_20|TRUE_40)
#define TRUE_ALL (TRUE_2040|TRUE)
enum {
WLAN_RC_PHY_HT_20_SS = 4,
WLAN_RC_PHY_HT_20_DS,
WLAN_RC_PHY_HT_40_SS,
WLAN_RC_PHY_HT_40_DS,
WLAN_RC_PHY_HT_20_SS_HGI,
WLAN_RC_PHY_HT_20_DS_HGI,
WLAN_RC_PHY_HT_40_SS_HGI,
WLAN_RC_PHY_HT_40_DS_HGI,
WLAN_RC_PHY_MAX
};
#define WLAN_RC_PHY_DS(_phy) ((_phy == WLAN_RC_PHY_HT_20_DS) \
|| (_phy == WLAN_RC_PHY_HT_40_DS) \
|| (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \
|| (_phy == WLAN_RC_PHY_HT_40_DS_HGI))
#define WLAN_RC_PHY_40(_phy) ((_phy == WLAN_RC_PHY_HT_40_SS) \
|| (_phy == WLAN_RC_PHY_HT_40_DS) \
|| (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \
|| (_phy == WLAN_RC_PHY_HT_40_DS_HGI))
#define WLAN_RC_PHY_SGI(_phy) ((_phy == WLAN_RC_PHY_HT_20_SS_HGI) \
|| (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \
|| (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \
|| (_phy == WLAN_RC_PHY_HT_40_DS_HGI))
#define WLAN_RC_PHY_HT(_phy) (_phy >= WLAN_RC_PHY_HT_20_SS)
/* Returns the capflag mode */
#define WLAN_RC_CAP_MODE(capflag) (((capflag & WLAN_RC_HT_FLAG) ? \
(capflag & WLAN_RC_40_FLAG) ? TRUE_40 : TRUE_20 : TRUE))
/* Return TRUE if flag supports HT20 && client supports HT20 or
* return TRUE if flag supports HT40 && client supports HT40.
* This is used becos some rates overlap between HT20/HT40.
*/
#define WLAN_RC_PHY_HT_VALID(flag, capflag) (((flag & TRUE_20) && !(capflag \
& WLAN_RC_40_FLAG)) || ((flag & TRUE_40) && \
(capflag & WLAN_RC_40_FLAG)))
#define WLAN_RC_DS_FLAG (0x01)
#define WLAN_RC_40_FLAG (0x02)
#define WLAN_RC_SGI_FLAG (0x04)
#define WLAN_RC_HT_FLAG (0x08)
/* Index into the rate table */
#define INIT_RATE_MAX_20 23
#define INIT_RATE_MAX_40 40
#define RATE_TABLE_SIZE 64
/* XXX: Convert to kdoc */
struct ath_rate_table {
int rate_cnt;
struct {
int valid; /* Valid for use in rate control */
int valid_single_stream;/* Valid for use in rate control
for single stream operation */
u8 phy; /* CCK/OFDM/TURBO/XR */
u32 ratekbps; /* Rate in Kbits per second */
u32 user_ratekbps; /* User rate in KBits per second */
u8 ratecode; /* rate that goes into
hw descriptors */
u8 short_preamble; /* Mask for enabling short preamble
in rate code for CCK */
u8 dot11rate; /* Value that goes into supported
rates info element of MLME */
u8 ctrl_rate; /* Index of next lower basic rate,
used for duration computation */
int8_t rssi_ack_validmin; /* Rate control related */
int8_t rssi_ack_deltamin; /* Rate control related */
u8 base_index; /* base rate index */
u8 cw40index; /* 40cap rate index */
u8 sgi_index; /* shortgi rate index */
u8 ht_index; /* shortgi rate index */
u32 max_4ms_framelen; /* Maximum frame length(bytes)
for 4ms tx duration */
} info[RATE_TABLE_SIZE];
u32 probe_interval; /* interval for ratectrl to
probe for other rates */
u32 rssi_reduce_interval; /* interval for ratectrl
to reduce RSSI */
u8 initial_ratemax; /* the initial ratemax value used
in ath_rc_sib_update() */
};
#define ATH_RC_PROBE_ALLOWED 0x00000001
#define ATH_RC_MINRATE_LASTRATE 0x00000002
#define ATH_RC_SHORT_PREAMBLE 0x00000004
struct ath_rc_series {
u8 rix;
u8 tries;
u8 flags;
u32 max_4ms_framelen;
};
/* rcs_flags definition */
#define ATH_RC_DS_FLAG 0x01
#define ATH_RC_CW40_FLAG 0x02 /* CW 40 */
#define ATH_RC_SGI_FLAG 0x04 /* Short Guard Interval */
#define ATH_RC_HT_FLAG 0x08 /* HT */
#define ATH_RC_RTSCTS_FLAG 0x10 /* RTS-CTS */
/*
* State structures for new rate adaptation code
*/
#define MAX_TX_RATE_TBL 64
#define MAX_TX_RATE_PHY 48
struct ath_tx_ratectrl_state {
int8_t rssi_thres; /* required rssi for this rate (dB) */
u8 per; /* recent estimate of packet error rate (%) */
};
struct ath_tx_ratectrl {
struct ath_tx_ratectrl_state state[MAX_TX_RATE_TBL]; /* state */
int8_t rssi_last; /* last ack rssi */
int8_t rssi_last_lookup; /* last ack rssi used for lookup */
int8_t rssi_last_prev; /* previous last ack rssi */
int8_t rssi_last_prev2; /* 2nd previous last ack rssi */
int32_t rssi_sum_cnt; /* count of rssi_sum for averaging */
int32_t rssi_sum_rate; /* rate that we are averaging */
int32_t rssi_sum; /* running sum of rssi for averaging */
u32 valid_txrate_mask; /* mask of valid rates */
u8 rate_table_size; /* rate table size */
u8 rate_max; /* max rate that has recently worked */
u8 probe_rate; /* rate we are probing at */
u32 rssi_time; /* msec timestamp for last ack rssi */
u32 rssi_down_time; /* msec timestamp for last down step */
u32 probe_time; /* msec timestamp for last probe */
u8 hw_maxretry_pktcnt; /* num packets since we got
HW max retry error */
u8 max_valid_rate; /* maximum number of valid rate */
u8 valid_rate_index[MAX_TX_RATE_TBL]; /* valid rate index */
u32 per_down_time; /* msec timstamp for last
PER down step */
/* 11n state */
u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX]; /* valid rate count */
u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][MAX_TX_RATE_TBL];
u8 rc_phy_mode;
u8 rate_max_phy; /* Phy index for the max rate */
u32 rate_max_lastused; /* msec timstamp of when we
last used rateMaxPhy */
u32 probe_interval; /* interval for ratectrl to probe
for other rates */
};
struct ath_rateset {
u8 rs_nrates;
u8 rs_rates[ATH_RATE_MAX];
};
/* per-device state */
struct ath_rate_softc {
/* phy tables that contain rate control data */
const void *hw_rate_table[ATH9K_MODE_MAX];
int fixedrix; /* -1 or index of fixed rate */
};
/* per-node state */
struct ath_rate_node {
struct ath_tx_ratectrl tx_ratectrl; /* rate control state proper */
u32 prev_data_rix; /* rate idx of last data frame */
/* map of rate ix -> negotiated rate set ix */
u8 rixmap[MAX_TX_RATE_TBL];
/* map of ht rate ix -> negotiated rate set ix */
u8 ht_rixmap[MAX_TX_RATE_TBL];
u8 ht_cap; /* ht capabilities */
u8 ant_tx; /* current transmit antenna */
u8 single_stream; /* When TRUE, only single
stream Tx possible */
struct ath_rateset neg_rates; /* Negotiated rates */
struct ath_rateset neg_ht_rates; /* Negotiated HT rates */
struct ath_rate_softc *asc; /* back pointer to atheros softc */
struct ath_vap *avp; /* back pointer to vap */
};
/* Driver data of ieee80211_tx_info */
struct ath_tx_info_priv {
struct ath_rc_series rcs[4];
struct ath_tx_status tx;
int n_frames;
int n_bad_frames;
u8 min_rate;
};
/*
* Attach/detach a rate control module.
*/
struct ath_rate_softc *ath_rate_attach(struct ath_hal *ah);
void ath_rate_detach(struct ath_rate_softc *asc);
/*
* Update/reset rate control state for 802.11 state transitions.
* Important mostly as the analog to ath_rate_newassoc when operating
* in station mode.
*/
void ath_rc_node_update(struct ieee80211_hw *hw, struct ath_rate_node *rc_priv);
void ath_rate_newstate(struct ath_softc *sc, struct ath_vap *avp);
/*
* Return the tx rate series.
*/
void ath_rate_findrate(struct ath_softc *sc, struct ath_rate_node *ath_rc_priv,
int num_tries, int num_rates,
unsigned int rcflag, struct ath_rc_series[],
int *is_probe, int isretry);
/*
* Return rate index for given Dot11 Rate.
*/
u8 ath_rate_findrateix(struct ath_softc *sc,
u8 dot11_rate);
/* Routines to register/unregister rate control algorithm */
int ath_rate_control_register(void);
void ath_rate_control_unregister(void);
#endif /* RC_H */
/*
* 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 receive path.
*/
#include "core.h"
/*
* Setup and link descriptors.
*
* 11N: we can no longer afford to self link the last descriptor.
* MAC acknowledges BA status as long as it copies frames to host
* buffer (or rx fifo). This can incorrectly acknowledge packets
* to a sender if last desc is self-linked.
*
* NOTE: Caller should hold the rxbuf lock.
*/
static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_desc *ds;
struct sk_buff *skb;
ATH_RXBUF_RESET(bf);
ds = bf->bf_desc;
ds->ds_link = 0; /* link to null */
ds->ds_data = bf->bf_buf_addr;
/* XXX For RADAR?
* virtual addr of the beginning of the buffer. */
skb = bf->bf_mpdu;
ASSERT(skb != NULL);
ds->ds_vdata = skb->data;
/* setup rx descriptors */
ath9k_hw_setuprxdesc(ah,
ds,
skb_tailroom(skb), /* buffer size */
0);
if (sc->sc_rxlink == NULL)
ath9k_hw_putrxbuf(ah, bf->bf_daddr);
else
*sc->sc_rxlink = bf->bf_daddr;
sc->sc_rxlink = &ds->ds_link;
ath9k_hw_rxena(ah);
}
/* Process received BAR frame */
static int ath_bar_rx(struct ath_softc *sc,
struct ath_node *an,
struct sk_buff *skb)
{
struct ieee80211_bar *bar;
struct ath_arx_tid *rxtid;
struct sk_buff *tskb;
struct ath_recv_status *rx_status;
int tidno, index, cindex;
u16 seqno;
/* look at BAR contents */
bar = (struct ieee80211_bar *)skb->data;
tidno = (le16_to_cpu(bar->control) & IEEE80211_BAR_CTL_TID_M)
>> IEEE80211_BAR_CTL_TID_S;
seqno = le16_to_cpu(bar->start_seq_num) >> IEEE80211_SEQ_SEQ_SHIFT;
/* process BAR - indicate all pending RX frames till the BAR seqno */
rxtid = &an->an_aggr.rx.tid[tidno];
spin_lock_bh(&rxtid->tidlock);
/* get relative index */
index = ATH_BA_INDEX(rxtid->seq_next, seqno);
/* drop BAR if old sequence (index is too large) */
if ((index > rxtid->baw_size) &&
(index > (IEEE80211_SEQ_MAX - (rxtid->baw_size << 2))))
/* discard frame, ieee layer may not treat frame as a dup */
goto unlock_and_free;
/* complete receive processing for all pending frames upto BAR seqno */
cindex = (rxtid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
while ((rxtid->baw_head != rxtid->baw_tail) &&
(rxtid->baw_head != cindex)) {
tskb = rxtid->rxbuf[rxtid->baw_head].rx_wbuf;
rx_status = &rxtid->rxbuf[rxtid->baw_head].rx_status;
rxtid->rxbuf[rxtid->baw_head].rx_wbuf = NULL;
if (tskb != NULL)
ath_rx_subframe(an, tskb, rx_status);
INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
}
/* ... and indicate rest of the frames in-order */
while (rxtid->baw_head != rxtid->baw_tail &&
rxtid->rxbuf[rxtid->baw_head].rx_wbuf != NULL) {
tskb = rxtid->rxbuf[rxtid->baw_head].rx_wbuf;
rx_status = &rxtid->rxbuf[rxtid->baw_head].rx_status;
rxtid->rxbuf[rxtid->baw_head].rx_wbuf = NULL;
ath_rx_subframe(an, tskb, rx_status);
INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
}
unlock_and_free:
spin_unlock_bh(&rxtid->tidlock);
/* free bar itself */
dev_kfree_skb(skb);
return IEEE80211_FTYPE_CTL;
}
/* Function to handle a subframe of aggregation when HT is enabled */
static int ath_ampdu_input(struct ath_softc *sc,
struct ath_node *an,
struct sk_buff *skb,
struct ath_recv_status *rx_status)
{
struct ieee80211_hdr *hdr;
struct ath_arx_tid *rxtid;
struct ath_rxbuf *rxbuf;
u8 type, subtype;
u16 rxseq;
int tid = 0, index, cindex, rxdiff;
__le16 fc;
u8 *qc;
hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
/* collect stats of frames with non-zero version */
if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_VERS) != 0) {
dev_kfree_skb(skb);
return -1;
}
type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE;
subtype = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_STYPE;
if (ieee80211_is_back_req(fc))
return ath_bar_rx(sc, an, skb);
/* special aggregate processing only for qos unicast data frames */
if (!ieee80211_is_data(fc) ||
!ieee80211_is_data_qos(fc) ||
is_multicast_ether_addr(hdr->addr1))
return ath_rx_subframe(an, skb, rx_status);
/* lookup rx tid state */
if (ieee80211_is_data_qos(fc)) {
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & 0xf;
}
if (sc->sc_opmode == ATH9K_M_STA) {
/* Drop the frame not belonging to me. */
if (memcmp(hdr->addr1, sc->sc_myaddr, ETH_ALEN)) {
dev_kfree_skb(skb);
return -1;
}
}
rxtid = &an->an_aggr.rx.tid[tid];
spin_lock(&rxtid->tidlock);
rxdiff = (rxtid->baw_tail - rxtid->baw_head) &
(ATH_TID_MAX_BUFS - 1);
/*
* If the ADDBA exchange has not been completed by the source,
* process via legacy path (i.e. no reordering buffer is needed)
*/
if (!rxtid->addba_exchangecomplete) {
spin_unlock(&rxtid->tidlock);
return ath_rx_subframe(an, skb, rx_status);
}
/* extract sequence number from recvd frame */
rxseq = le16_to_cpu(hdr->seq_ctrl) >> IEEE80211_SEQ_SEQ_SHIFT;
if (rxtid->seq_reset) {
rxtid->seq_reset = 0;
rxtid->seq_next = rxseq;
}
index = ATH_BA_INDEX(rxtid->seq_next, rxseq);
/* drop frame if old sequence (index is too large) */
if (index > (IEEE80211_SEQ_MAX - (rxtid->baw_size << 2))) {
/* discard frame, ieee layer may not treat frame as a dup */
spin_unlock(&rxtid->tidlock);
dev_kfree_skb(skb);
return IEEE80211_FTYPE_DATA;
}
/* sequence number is beyond block-ack window */
if (index >= rxtid->baw_size) {
/* complete receive processing for all pending frames */
while (index >= rxtid->baw_size) {
rxbuf = rxtid->rxbuf + rxtid->baw_head;
if (rxbuf->rx_wbuf != NULL) {
ath_rx_subframe(an, rxbuf->rx_wbuf,
&rxbuf->rx_status);
rxbuf->rx_wbuf = NULL;
}
INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
index--;
}
}
/* add buffer to the recv ba window */
cindex = (rxtid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
rxbuf = rxtid->rxbuf + cindex;
if (rxbuf->rx_wbuf != NULL) {
spin_unlock(&rxtid->tidlock);
/* duplicate frame */
dev_kfree_skb(skb);
return IEEE80211_FTYPE_DATA;
}
rxbuf->rx_wbuf = skb;
rxbuf->rx_time = get_timestamp();
rxbuf->rx_status = *rx_status;
/* advance tail if sequence received is newer
* than any received so far */
if (index >= rxdiff) {
rxtid->baw_tail = cindex;
INCR(rxtid->baw_tail, ATH_TID_MAX_BUFS);
}
/* indicate all in-order received frames */
while (rxtid->baw_head != rxtid->baw_tail) {
rxbuf = rxtid->rxbuf + rxtid->baw_head;
if (!rxbuf->rx_wbuf)
break;
ath_rx_subframe(an, rxbuf->rx_wbuf, &rxbuf->rx_status);
rxbuf->rx_wbuf = NULL;
INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
}
/*
* start a timer to flush all received frames if there are pending
* receive frames
*/
if (rxtid->baw_head != rxtid->baw_tail)
mod_timer(&rxtid->timer, ATH_RX_TIMEOUT);
else
del_timer_sync(&rxtid->timer);
spin_unlock(&rxtid->tidlock);
return IEEE80211_FTYPE_DATA;
}
/* Timer to flush all received sub-frames */
static void ath_rx_timer(unsigned long data)
{
struct ath_arx_tid *rxtid = (struct ath_arx_tid *)data;
struct ath_node *an = rxtid->an;
struct ath_rxbuf *rxbuf;
int nosched;
spin_lock_bh(&rxtid->tidlock);
while (rxtid->baw_head != rxtid->baw_tail) {
rxbuf = rxtid->rxbuf + rxtid->baw_head;
if (!rxbuf->rx_wbuf) {
INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
continue;
}
/*
* Stop if the next one is a very recent frame.
*
* Call get_timestamp in every iteration to protect against the
* case in which a new frame is received while we are executing
* this function. Using a timestamp obtained before entering
* the loop could lead to a very large time interval
* (a negative value typecast to unsigned), breaking the
* function's logic.
*/
if ((get_timestamp() - rxbuf->rx_time) <
(ATH_RX_TIMEOUT * HZ / 1000))
break;
ath_rx_subframe(an, rxbuf->rx_wbuf,
&rxbuf->rx_status);
rxbuf->rx_wbuf = NULL;
INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
}
/*
* start a timer to flush all received frames if there are pending
* receive frames
*/
if (rxtid->baw_head != rxtid->baw_tail)
nosched = 0;
else
nosched = 1; /* no need to re-arm the timer again */
spin_unlock_bh(&rxtid->tidlock);
}
/* Free all pending sub-frames in the re-ordering buffer */
static void ath_rx_flush_tid(struct ath_softc *sc,
struct ath_arx_tid *rxtid, int drop)
{
struct ath_rxbuf *rxbuf;
spin_lock_bh(&rxtid->tidlock);
while (rxtid->baw_head != rxtid->baw_tail) {
rxbuf = rxtid->rxbuf + rxtid->baw_head;
if (!rxbuf->rx_wbuf) {
INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
continue;
}
if (drop)
dev_kfree_skb(rxbuf->rx_wbuf);
else
ath_rx_subframe(rxtid->an,
rxbuf->rx_wbuf,
&rxbuf->rx_status);
rxbuf->rx_wbuf = NULL;
INCR(rxtid->baw_head, ATH_TID_MAX_BUFS);
INCR(rxtid->seq_next, IEEE80211_SEQ_MAX);
}
spin_unlock_bh(&rxtid->tidlock);
}
static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc,
u32 len)
{
struct sk_buff *skb;
u32 off;
/*
* Cache-line-align. This is important (for the
* 5210 at least) as not doing so causes bogus data
* in rx'd frames.
*/
skb = dev_alloc_skb(len + sc->sc_cachelsz - 1);
if (skb != NULL) {
off = ((unsigned long) skb->data) % sc->sc_cachelsz;
if (off != 0)
skb_reserve(skb, sc->sc_cachelsz - off);
} else {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: skbuff alloc of size %u failed\n",
__func__, len);
return NULL;
}
return skb;
}
static void ath_rx_requeue(struct ath_softc *sc, struct sk_buff *skb)
{
struct ath_buf *bf = ATH_RX_CONTEXT(skb)->ctx_rxbuf;
ASSERT(bf != NULL);
spin_lock_bh(&sc->sc_rxbuflock);
if (bf->bf_status & ATH_BUFSTATUS_STALE) {
/*
* This buffer is still held for hw acess.
* Mark it as free to be re-queued it later.
*/
bf->bf_status |= ATH_BUFSTATUS_FREE;
} else {
/* XXX: we probably never enter here, remove after
* verification */
list_add_tail(&bf->list, &sc->sc_rxbuf);
ath_rx_buf_link(sc, bf);
}
spin_unlock_bh(&sc->sc_rxbuflock);
}
/*
* The skb indicated to upper stack won't be returned to us.
* So we have to allocate a new one and queue it by ourselves.
*/
static int ath_rx_indicate(struct ath_softc *sc,
struct sk_buff *skb,
struct ath_recv_status *status,
u16 keyix)
{
struct ath_buf *bf = ATH_RX_CONTEXT(skb)->ctx_rxbuf;
struct sk_buff *nskb;
int type;
/* indicate frame to the stack, which will free the old skb. */
type = ath__rx_indicate(sc, skb, status, keyix);
/* allocate a new skb and queue it to for H/W processing */
nskb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize);
if (nskb != NULL) {
bf->bf_mpdu = nskb;
bf->bf_buf_addr = ath_skb_map_single(sc,
nskb,
PCI_DMA_FROMDEVICE,
/* XXX: Remove get_dma_mem_context() */
get_dma_mem_context(bf, bf_dmacontext));
ATH_RX_CONTEXT(nskb)->ctx_rxbuf = bf;
/* queue the new wbuf to H/W */
ath_rx_requeue(sc, nskb);
}
return type;
}
static void ath_opmode_init(struct ath_softc *sc)
{
struct ath_hal *ah = sc->sc_ah;
u32 rfilt, mfilt[2];
/* configure rx filter */
rfilt = ath_calcrxfilter(sc);
ath9k_hw_setrxfilter(ah, rfilt);
/* configure bssid mask */
if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
ath9k_hw_setbssidmask(ah, sc->sc_bssidmask);
/* configure operational mode */
ath9k_hw_setopmode(ah);
/* Handle any link-level address change. */
ath9k_hw_setmac(ah, sc->sc_myaddr);
/* calculate and install multicast filter */
mfilt[0] = mfilt[1] = ~0;
ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]);
DPRINTF(sc, ATH_DBG_CONFIG ,
"%s: RX filter 0x%x, MC filter %08x:%08x\n",
__func__, rfilt, mfilt[0], mfilt[1]);
}
int ath_rx_init(struct ath_softc *sc, int nbufs)
{
struct sk_buff *skb;
struct ath_buf *bf;
int error = 0;
do {
spin_lock_init(&sc->sc_rxflushlock);
sc->sc_rxflush = 0;
spin_lock_init(&sc->sc_rxbuflock);
/*
* Cisco's VPN software requires that drivers be able to
* receive encapsulated frames that are larger than the MTU.
* Since we can't be sure how large a frame we'll get, setup
* to handle the larges on possible.
*/
sc->sc_rxbufsize = roundup(IEEE80211_MAX_MPDU_LEN,
min(sc->sc_cachelsz,
(u16)64));
DPRINTF(sc, ATH_DBG_CONFIG, "%s: cachelsz %u rxbufsize %u\n",
__func__, sc->sc_cachelsz, sc->sc_rxbufsize);
/* Initialize rx descriptors */
error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
"rx", nbufs, 1);
if (error != 0) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: failed to allocate rx descriptors: %d\n",
__func__, error);
break;
}
/* Pre-allocate a wbuf for each rx buffer */
list_for_each_entry(bf, &sc->sc_rxbuf, list) {
skb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize);
if (skb == NULL) {
error = -ENOMEM;
break;
}
bf->bf_mpdu = skb;
bf->bf_buf_addr =
ath_skb_map_single(sc, skb, PCI_DMA_FROMDEVICE,
get_dma_mem_context(bf, bf_dmacontext));
ATH_RX_CONTEXT(skb)->ctx_rxbuf = bf;
}
sc->sc_rxlink = NULL;
} while (0);
if (error)
ath_rx_cleanup(sc);
return error;
}
/* Reclaim all rx queue resources */
void ath_rx_cleanup(struct ath_softc *sc)
{
struct sk_buff *skb;
struct ath_buf *bf;
list_for_each_entry(bf, &sc->sc_rxbuf, list) {
skb = bf->bf_mpdu;
if (skb)
dev_kfree_skb(skb);
}
/* cleanup rx descriptors */
if (sc->sc_rxdma.dd_desc_len != 0)
ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
}
/*
* Calculate the receive filter according to the
* operating mode and state:
*
* o always accept unicast, broadcast, and multicast traffic
* o maintain current state of phy error reception (the hal
* may enable phy error frames for noise immunity work)
* o probe request frames are accepted only when operating in
* hostap, adhoc, or monitor modes
* o enable promiscuous mode according to the interface state
* o accept beacons:
* - when operating in adhoc mode so the 802.11 layer creates
* node table entries for peers,
* - when operating in station mode for collecting rssi data when
* the station is otherwise quiet, or
* - when operating as a repeater so we see repeater-sta beacons
* - when scanning
*/
u32 ath_calcrxfilter(struct ath_softc *sc)
{
#define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR)
u32 rfilt;
rfilt = (ath9k_hw_getrxfilter(sc->sc_ah) & RX_FILTER_PRESERVE)
| ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
| ATH9K_RX_FILTER_MCAST;
/* If not a STA, enable processing of Probe Requests */
if (sc->sc_opmode != ATH9K_M_STA)
rfilt |= ATH9K_RX_FILTER_PROBEREQ;
/* Can't set HOSTAP into promiscous mode */
if (sc->sc_opmode == ATH9K_M_MONITOR) {
rfilt |= ATH9K_RX_FILTER_PROM;
/* ??? To prevent from sending ACK */
rfilt &= ~ATH9K_RX_FILTER_UCAST;
}
if (sc->sc_opmode == ATH9K_M_STA || sc->sc_opmode == ATH9K_M_IBSS ||
sc->sc_scanning)
rfilt |= ATH9K_RX_FILTER_BEACON;
/* If in HOSTAP mode, want to enable reception of PSPOLL frames
& beacon frames */
if (sc->sc_opmode == ATH9K_M_HOSTAP)
rfilt |= (ATH9K_RX_FILTER_BEACON | ATH9K_RX_FILTER_PSPOLL);
return rfilt;
#undef RX_FILTER_PRESERVE
}
/* Enable the receive h/w following a reset. */
int ath_startrecv(struct ath_softc *sc)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf, *tbf;
spin_lock_bh(&sc->sc_rxbuflock);
if (list_empty(&sc->sc_rxbuf))
goto start_recv;
sc->sc_rxlink = NULL;
list_for_each_entry_safe(bf, tbf, &sc->sc_rxbuf, list) {
if (bf->bf_status & ATH_BUFSTATUS_STALE) {
/* restarting h/w, no need for holding descriptors */
bf->bf_status &= ~ATH_BUFSTATUS_STALE;
/*
* Upper layer may not be done with the frame yet so
* we can't just re-queue it to hardware. Remove it
* from h/w queue. It'll be re-queued when upper layer
* returns the frame and ath_rx_requeue_mpdu is called.
*/
if (!(bf->bf_status & ATH_BUFSTATUS_FREE)) {
list_del(&bf->list);
continue;
}
}
/* chain descriptors */
ath_rx_buf_link(sc, bf);
}
/* We could have deleted elements so the list may be empty now */
if (list_empty(&sc->sc_rxbuf))
goto start_recv;
bf = list_first_entry(&sc->sc_rxbuf, struct ath_buf, list);
ath9k_hw_putrxbuf(ah, bf->bf_daddr);
ath9k_hw_rxena(ah); /* enable recv descriptors */
start_recv:
spin_unlock_bh(&sc->sc_rxbuflock);
ath_opmode_init(sc); /* set filters, etc. */
ath9k_hw_startpcureceive(ah); /* re-enable PCU/DMA engine */
return 0;
}
/* Disable the receive h/w in preparation for a reset. */
bool ath_stoprecv(struct ath_softc *sc)
{
struct ath_hal *ah = sc->sc_ah;
u64 tsf;
bool stopped;
ath9k_hw_stoppcurecv(ah); /* disable PCU */
ath9k_hw_setrxfilter(ah, 0); /* clear recv filter */
stopped = ath9k_hw_stopdmarecv(ah); /* disable DMA engine */
mdelay(3); /* 3ms is long enough for 1 frame */
tsf = ath9k_hw_gettsf64(ah);
sc->sc_rxlink = NULL; /* just in case */
return stopped;
}
/* Flush receive queue */
void ath_flushrecv(struct ath_softc *sc)
{
/*
* ath_rx_tasklet may be used to handle rx interrupt and flush receive
* queue at the same time. Use a lock to serialize the access of rx
* queue.
* ath_rx_tasklet cannot hold the spinlock while indicating packets.
* Instead, do not claim the spinlock but check for a flush in
* progress (see references to sc_rxflush)
*/
spin_lock_bh(&sc->sc_rxflushlock);
sc->sc_rxflush = 1;
ath_rx_tasklet(sc, 1);
sc->sc_rxflush = 0;
spin_unlock_bh(&sc->sc_rxflushlock);
}
/* Process an individual frame */
int ath_rx_input(struct ath_softc *sc,
struct ath_node *an,
int is_ampdu,
struct sk_buff *skb,
struct ath_recv_status *rx_status,
enum ATH_RX_TYPE *status)
{
if (is_ampdu && sc->sc_rxaggr) {
*status = ATH_RX_CONSUMED;
return ath_ampdu_input(sc, an, skb, rx_status);
} else {
*status = ATH_RX_NON_CONSUMED;
return -1;
}
}
/* Process receive queue, as well as LED, etc. */
int ath_rx_tasklet(struct ath_softc *sc, int flush)
{
#define PA2DESC(_sc, _pa) \
((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
struct ath_buf *bf, *bf_held = NULL;
struct ath_desc *ds;
struct ieee80211_hdr *hdr;
struct sk_buff *skb = NULL;
struct ath_recv_status rx_status;
struct ath_hal *ah = sc->sc_ah;
int type, rx_processed = 0;
u32 phyerr;
u8 chainreset = 0;
int retval;
__le16 fc;
do {
/* If handling rx interrupt and flush is in progress => exit */
if (sc->sc_rxflush && (flush == 0))
break;
spin_lock_bh(&sc->sc_rxbuflock);
if (list_empty(&sc->sc_rxbuf)) {
sc->sc_rxlink = NULL;
spin_unlock_bh(&sc->sc_rxbuflock);
break;
}
bf = list_first_entry(&sc->sc_rxbuf, struct ath_buf, list);
/*
* There is a race condition that BH gets scheduled after sw
* writes RxE and before hw re-load the last descriptor to get
* the newly chained one. Software must keep the last DONE
* descriptor as a holding descriptor - software does so by
* marking it with the STALE flag.
*/
if (bf->bf_status & ATH_BUFSTATUS_STALE) {
bf_held = bf;
if (list_is_last(&bf_held->list, &sc->sc_rxbuf)) {
/*
* The holding descriptor is the last
* descriptor in queue. It's safe to
* remove the last holding descriptor
* in BH context.
*/
list_del(&bf_held->list);
bf_held->bf_status &= ~ATH_BUFSTATUS_STALE;
sc->sc_rxlink = NULL;
if (bf_held->bf_status & ATH_BUFSTATUS_FREE) {
list_add_tail(&bf_held->list,
&sc->sc_rxbuf);
ath_rx_buf_link(sc, bf_held);
}
spin_unlock_bh(&sc->sc_rxbuflock);
break;
}
bf = list_entry(bf->list.next, struct ath_buf, list);
}
ds = bf->bf_desc;
++rx_processed;
/*
* Must provide the virtual address of the current
* descriptor, the physical address, and the virtual
* address of the next descriptor in the h/w chain.
* This allows the HAL to look ahead to see if the
* hardware is done with a descriptor by checking the
* done bit in the following descriptor and the address
* of the current descriptor the DMA engine is working
* on. All this is necessary because of our use of
* a self-linked list to avoid rx overruns.
*/
retval = ath9k_hw_rxprocdesc(ah,
ds,
bf->bf_daddr,
PA2DESC(sc, ds->ds_link),
0);
if (retval == -EINPROGRESS) {
struct ath_buf *tbf;
struct ath_desc *tds;
if (list_is_last(&bf->list, &sc->sc_rxbuf)) {
spin_unlock_bh(&sc->sc_rxbuflock);
break;
}
tbf = list_entry(bf->list.next, struct ath_buf, list);
/*
* On some hardware the descriptor status words could
* get corrupted, including the done bit. Because of
* this, check if the next descriptor's done bit is
* set or not.
*
* If the next descriptor's done bit is set, the current
* descriptor has been corrupted. Force s/w to discard
* this descriptor and continue...
*/
tds = tbf->bf_desc;
retval = ath9k_hw_rxprocdesc(ah,
tds, tbf->bf_daddr,
PA2DESC(sc, tds->ds_link), 0);
if (retval == -EINPROGRESS) {
spin_unlock_bh(&sc->sc_rxbuflock);
break;
}
}
/* XXX: we do not support frames spanning
* multiple descriptors */
bf->bf_status |= ATH_BUFSTATUS_DONE;
skb = bf->bf_mpdu;
if (skb == NULL) { /* XXX ??? can this happen */
spin_unlock_bh(&sc->sc_rxbuflock);
continue;
}
/*
* Now we know it's a completed frame, we can indicate the
* frame. Remove the previous holding descriptor and leave
* this one in the queue as the new holding descriptor.
*/
if (bf_held) {
list_del(&bf_held->list);
bf_held->bf_status &= ~ATH_BUFSTATUS_STALE;
if (bf_held->bf_status & ATH_BUFSTATUS_FREE) {
list_add_tail(&bf_held->list, &sc->sc_rxbuf);
/* try to requeue this descriptor */
ath_rx_buf_link(sc, bf_held);
}
}
bf->bf_status |= ATH_BUFSTATUS_STALE;
bf_held = bf;
/*
* Release the lock here in case ieee80211_input() return
* the frame immediately by calling ath_rx_mpdu_requeue().
*/
spin_unlock_bh(&sc->sc_rxbuflock);
if (flush) {
/*
* If we're asked to flush receive queue, directly
* chain it back at the queue without processing it.
*/
goto rx_next;
}
hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
memzero(&rx_status, sizeof(struct ath_recv_status));
if (ds->ds_rxstat.rs_more) {
/*
* Frame spans multiple descriptors; this
* cannot happen yet as we don't support
* jumbograms. If not in monitor mode,
* discard the frame.
*/
#ifndef ERROR_FRAMES
/*
* Enable this if you want to see
* error frames in Monitor mode.
*/
if (sc->sc_opmode != ATH9K_M_MONITOR)
goto rx_next;
#endif
/* fall thru for monitor mode handling... */
} else if (ds->ds_rxstat.rs_status != 0) {
if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC)
rx_status.flags |= ATH_RX_FCS_ERROR;
if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY) {
phyerr = ds->ds_rxstat.rs_phyerr & 0x1f;
goto rx_next;
}
if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) {
/*
* Decrypt error. We only mark packet status
* here and always push up the frame up to let
* mac80211 handle the actual error case, be
* it no decryption key or real decryption
* error. This let us keep statistics there.
*/
rx_status.flags |= ATH_RX_DECRYPT_ERROR;
} else if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) {
/*
* Demic error. We only mark frame status here
* and always push up the frame up to let
* mac80211 handle the actual error case. This
* let us keep statistics there. Hardware may
* post a false-positive MIC error.
*/
if (ieee80211_is_ctl(fc))
/*
* Sometimes, we get invalid
* MIC failures on valid control frames.
* Remove these mic errors.
*/
ds->ds_rxstat.rs_status &=
~ATH9K_RXERR_MIC;
else
rx_status.flags |= ATH_RX_MIC_ERROR;
}
/*
* Reject error frames with the exception of
* decryption and MIC failures. For monitor mode,
* we also ignore the CRC error.
*/
if (sc->sc_opmode == ATH9K_M_MONITOR) {
if (ds->ds_rxstat.rs_status &
~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
ATH9K_RXERR_CRC))
goto rx_next;
} else {
if (ds->ds_rxstat.rs_status &
~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) {
goto rx_next;
}
}
}
/*
* The status portion of the descriptor could get corrupted.
*/
if (sc->sc_rxbufsize < ds->ds_rxstat.rs_datalen)
goto rx_next;
/*
* Sync and unmap the frame. At this point we're
* committed to passing the sk_buff somewhere so
* clear buf_skb; this means a new sk_buff must be
* allocated when the rx descriptor is setup again
* to receive another frame.
*/
skb_put(skb, ds->ds_rxstat.rs_datalen);
skb->protocol = cpu_to_be16(ETH_P_CONTROL);
rx_status.tsf = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp);
rx_status.rateieee =
sc->sc_hwmap[ds->ds_rxstat.rs_rate].ieeerate;
rx_status.rateKbps =
sc->sc_hwmap[ds->ds_rxstat.rs_rate].rateKbps;
rx_status.ratecode = ds->ds_rxstat.rs_rate;
/* HT rate */
if (rx_status.ratecode & 0x80) {
/* TODO - add table to avoid division */
if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040) {
rx_status.flags |= ATH_RX_40MHZ;
rx_status.rateKbps =
(rx_status.rateKbps * 27) / 13;
}
if (ds->ds_rxstat.rs_flags & ATH9K_RX_GI)
rx_status.rateKbps =
(rx_status.rateKbps * 10) / 9;
else
rx_status.flags |= ATH_RX_SHORT_GI;
}
/* sc->sc_noise_floor is only available when the station
attaches to an AP, so we use a default value
if we are not yet attached. */
/* XXX we should use either sc->sc_noise_floor or
* ath_hal_getChanNoise(ah, &sc->sc_curchan)
* to calculate the noise floor.
* However, the value returned by ath_hal_getChanNoise
* seems to be incorrect (-31dBm on the last test),
* so we will use a hard-coded value until we
* figure out what is going on.
*/
rx_status.abs_rssi =
ds->ds_rxstat.rs_rssi + ATH_DEFAULT_NOISE_FLOOR;
pci_dma_sync_single_for_cpu(sc->pdev,
bf->bf_buf_addr,
skb_tailroom(skb),
PCI_DMA_FROMDEVICE);
pci_unmap_single(sc->pdev,
bf->bf_buf_addr,
sc->sc_rxbufsize,
PCI_DMA_FROMDEVICE);
/* XXX: Ah! make me more readable, use a helper */
if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) {
if (ds->ds_rxstat.rs_moreaggr == 0) {
rx_status.rssictl[0] =
ds->ds_rxstat.rs_rssi_ctl0;
rx_status.rssictl[1] =
ds->ds_rxstat.rs_rssi_ctl1;
rx_status.rssictl[2] =
ds->ds_rxstat.rs_rssi_ctl2;
rx_status.rssi = ds->ds_rxstat.rs_rssi;
if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040) {
rx_status.rssiextn[0] =
ds->ds_rxstat.rs_rssi_ext0;
rx_status.rssiextn[1] =
ds->ds_rxstat.rs_rssi_ext1;
rx_status.rssiextn[2] =
ds->ds_rxstat.rs_rssi_ext2;
rx_status.flags |=
ATH_RX_RSSI_EXTN_VALID;
}
rx_status.flags |= ATH_RX_RSSI_VALID |
ATH_RX_CHAIN_RSSI_VALID;
}
} else {
/*
* Need to insert the "combined" rssi into the
* status structure for upper layer processing
*/
rx_status.rssi = ds->ds_rxstat.rs_rssi;
rx_status.flags |= ATH_RX_RSSI_VALID;
}
/* Pass frames up to the stack. */
type = ath_rx_indicate(sc, skb,
&rx_status, ds->ds_rxstat.rs_keyix);
/*
* change the default rx antenna if rx diversity chooses the
* other antenna 3 times in a row.
*/
if (sc->sc_defant != ds->ds_rxstat.rs_antenna) {
if (++sc->sc_rxotherant >= 3)
ath_setdefantenna(sc,
ds->ds_rxstat.rs_antenna);
} else {
sc->sc_rxotherant = 0;
}
#ifdef CONFIG_SLOW_ANT_DIV
if ((rx_status.flags & ATH_RX_RSSI_VALID) &&
ieee80211_is_beacon(fc)) {
ath_slow_ant_div(&sc->sc_antdiv, hdr, &ds->ds_rxstat);
}
#endif
/*
* For frames successfully indicated, the buffer will be
* returned to us by upper layers by calling
* ath_rx_mpdu_requeue, either synchronusly or asynchronously.
* So we don't want to do it here in this loop.
*/
continue;
rx_next:
bf->bf_status |= ATH_BUFSTATUS_FREE;
} while (TRUE);
if (chainreset) {
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: Reset rx chain mask. "
"Do internal reset\n", __func__);
ASSERT(flush == 0);
ath_internal_reset(sc);
}
return 0;
#undef PA2DESC
}
/* Process ADDBA request in per-TID data structure */
int ath_rx_aggr_start(struct ath_softc *sc,
const u8 *addr,
u16 tid,
u16 *ssn)
{
struct ath_arx_tid *rxtid;
struct ath_node *an;
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_supported_band *sband;
u16 buffersize = 0;
spin_lock_bh(&sc->node_lock);
an = ath_node_find(sc, (u8 *) addr);
spin_unlock_bh(&sc->node_lock);
if (!an) {
DPRINTF(sc, ATH_DBG_AGGR,
"%s: Node not found to initialize RX aggregation\n",
__func__);
return -1;
}
sband = hw->wiphy->bands[hw->conf.channel->band];
buffersize = IEEE80211_MIN_AMPDU_BUF <<
sband->ht_info.ampdu_factor; /* FIXME */
rxtid = &an->an_aggr.rx.tid[tid];
spin_lock_bh(&rxtid->tidlock);
if (sc->sc_rxaggr) {
/* Allow aggregation reception
* Adjust rx BA window size. Peer might indicate a
* zero buffer size for a _dont_care_ condition.
*/
if (buffersize)
rxtid->baw_size = min(buffersize, rxtid->baw_size);
/* set rx sequence number */
rxtid->seq_next = *ssn;
/* Allocate the receive buffers for this TID */
DPRINTF(sc, ATH_DBG_AGGR,
"%s: Allcating rxbuffer for TID %d\n", __func__, tid);
if (rxtid->rxbuf == NULL) {
/*
* If the rxbuff is not NULL at this point, we *probably*
* already allocated the buffer on a previous ADDBA,
* and this is a subsequent ADDBA that got through.
* Don't allocate, but use the value in the pointer,
* we zero it out when we de-allocate.
*/
rxtid->rxbuf = kmalloc(ATH_TID_MAX_BUFS *
sizeof(struct ath_rxbuf), GFP_ATOMIC);
}
if (rxtid->rxbuf == NULL) {
DPRINTF(sc, ATH_DBG_AGGR,
"%s: Unable to allocate RX buffer, "
"refusing ADDBA\n", __func__);
} else {
/* Ensure the memory is zeroed out (all internal
* pointers are null) */
memzero(rxtid->rxbuf, ATH_TID_MAX_BUFS *
sizeof(struct ath_rxbuf));
DPRINTF(sc, ATH_DBG_AGGR,
"%s: Allocated @%p\n", __func__, rxtid->rxbuf);
/* Allow aggregation reception */
rxtid->addba_exchangecomplete = 1;
}
}
spin_unlock_bh(&rxtid->tidlock);
return 0;
}
/* Process DELBA */
int ath_rx_aggr_stop(struct ath_softc *sc,
const u8 *addr,
u16 tid)
{
struct ath_node *an;
spin_lock_bh(&sc->node_lock);
an = ath_node_find(sc, (u8 *) addr);
spin_unlock_bh(&sc->node_lock);
if (!an) {
DPRINTF(sc, ATH_DBG_AGGR,
"%s: RX aggr stop for non-existent node\n", __func__);
return -1;
}
ath_rx_aggr_teardown(sc, an, tid);
return 0;
}
/* Rx aggregation tear down */
void ath_rx_aggr_teardown(struct ath_softc *sc,
struct ath_node *an, u8 tid)
{
struct ath_arx_tid *rxtid = &an->an_aggr.rx.tid[tid];
if (!rxtid->addba_exchangecomplete)
return;
del_timer_sync(&rxtid->timer);
ath_rx_flush_tid(sc, rxtid, 0);
rxtid->addba_exchangecomplete = 0;
/* De-allocate the receive buffer array allocated when addba started */
if (rxtid->rxbuf) {
DPRINTF(sc, ATH_DBG_AGGR,
"%s: Deallocating TID %d rxbuff @%p\n",
__func__, tid, rxtid->rxbuf);
kfree(rxtid->rxbuf);
/* Set pointer to null to avoid reuse*/
rxtid->rxbuf = NULL;
}
}
/* Initialize per-node receive state */
void ath_rx_node_init(struct ath_softc *sc, struct ath_node *an)
{
if (sc->sc_rxaggr) {
struct ath_arx_tid *rxtid;
int tidno;
/* Init per tid rx state */
for (tidno = 0, rxtid = &an->an_aggr.rx.tid[tidno];
tidno < WME_NUM_TID;
tidno++, rxtid++) {
rxtid->an = an;
rxtid->seq_reset = 1;
rxtid->seq_next = 0;
rxtid->baw_size = WME_MAX_BA;
rxtid->baw_head = rxtid->baw_tail = 0;
/*
* Ensure the buffer pointer is null at this point
* (needs to be allocated when addba is received)
*/
rxtid->rxbuf = NULL;
setup_timer(&rxtid->timer, ath_rx_timer,
(unsigned long)rxtid);
spin_lock_init(&rxtid->tidlock);
/* ADDBA state */
rxtid->addba_exchangecomplete = 0;
}
}
}
void ath_rx_node_cleanup(struct ath_softc *sc, struct ath_node *an)
{
if (sc->sc_rxaggr) {
struct ath_arx_tid *rxtid;
int tidno, i;
/* Init per tid rx state */
for (tidno = 0, rxtid = &an->an_aggr.rx.tid[tidno];
tidno < WME_NUM_TID;
tidno++, rxtid++) {
if (!rxtid->addba_exchangecomplete)
continue;
/* must cancel timer first */
del_timer_sync(&rxtid->timer);
/* drop any pending sub-frames */
ath_rx_flush_tid(sc, rxtid, 1);
for (i = 0; i < ATH_TID_MAX_BUFS; i++)
ASSERT(rxtid->rxbuf[i].rx_wbuf == NULL);
rxtid->addba_exchangecomplete = 0;
}
}
}
/* Cleanup per-node receive state */
void ath_rx_node_free(struct ath_softc *sc, struct ath_node *an)
{
ath_rx_node_cleanup(sc, an);
}
dma_addr_t ath_skb_map_single(struct ath_softc *sc,
struct sk_buff *skb,
int direction,
dma_addr_t *pa)
{
/*
* NB: do NOT use skb->len, which is 0 on initialization.
* Use skb's entire data area instead.
*/
*pa = pci_map_single(sc->pdev, skb->data,
skb_end_pointer(skb) - skb->head, direction);
return *pa;
}
void ath_skb_unmap_single(struct ath_softc *sc,
struct sk_buff *skb,
int direction,
dma_addr_t *pa)
{
/* Unmap skb's entire data area */
pci_unmap_single(sc->pdev, *pa,
skb_end_pointer(skb) - skb->head, direction);
}
/*
* 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 REG_H
#define REG_H
#define AR_CR 0x0008
#define AR_CR_RXE 0x00000004
#define AR_CR_RXD 0x00000020
#define AR_CR_SWI 0x00000040
#define AR_RXDP 0x000C
#define AR_CFG 0x0014
#define AR_CFG_SWTD 0x00000001
#define AR_CFG_SWTB 0x00000002
#define AR_CFG_SWRD 0x00000004
#define AR_CFG_SWRB 0x00000008
#define AR_CFG_SWRG 0x00000010
#define AR_CFG_AP_ADHOC_INDICATION 0x00000020
#define AR_CFG_PHOK 0x00000100
#define AR_CFG_CLK_GATE_DIS 0x00000400
#define AR_CFG_EEBS 0x00000200
#define AR_CFG_PCI_MASTER_REQ_Q_THRESH 0x00060000
#define AR_CFG_PCI_MASTER_REQ_Q_THRESH_S 17
#define AR_MIRT 0x0020
#define AR_MIRT_VAL 0x0000ffff
#define AR_MIRT_VAL_S 16
#define AR_IER 0x0024
#define AR_IER_ENABLE 0x00000001
#define AR_IER_DISABLE 0x00000000
#define AR_TIMT 0x0028
#define AR_TIMT_LAST 0x0000ffff
#define AR_TIMT_LAST_S 0
#define AR_TIMT_FIRST 0xffff0000
#define AR_TIMT_FIRST_S 16
#define AR_RIMT 0x002C
#define AR_RIMT_LAST 0x0000ffff
#define AR_RIMT_LAST_S 0
#define AR_RIMT_FIRST 0xffff0000
#define AR_RIMT_FIRST_S 16
#define AR_DMASIZE_4B 0x00000000
#define AR_DMASIZE_8B 0x00000001
#define AR_DMASIZE_16B 0x00000002
#define AR_DMASIZE_32B 0x00000003
#define AR_DMASIZE_64B 0x00000004
#define AR_DMASIZE_128B 0x00000005
#define AR_DMASIZE_256B 0x00000006
#define AR_DMASIZE_512B 0x00000007
#define AR_TXCFG 0x0030
#define AR_TXCFG_DMASZ_MASK 0x00000003
#define AR_TXCFG_DMASZ_4B 0
#define AR_TXCFG_DMASZ_8B 1
#define AR_TXCFG_DMASZ_16B 2
#define AR_TXCFG_DMASZ_32B 3
#define AR_TXCFG_DMASZ_64B 4
#define AR_TXCFG_DMASZ_128B 5
#define AR_TXCFG_DMASZ_256B 6
#define AR_TXCFG_DMASZ_512B 7
#define AR_FTRIG 0x000003F0
#define AR_FTRIG_S 4
#define AR_FTRIG_IMMED 0x00000000
#define AR_FTRIG_64B 0x00000010
#define AR_FTRIG_128B 0x00000020
#define AR_FTRIG_192B 0x00000030
#define AR_FTRIG_256B 0x00000040
#define AR_FTRIG_512B 0x00000080
#define AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY 0x00000800
#define AR_RXCFG 0x0034
#define AR_RXCFG_CHIRP 0x00000008
#define AR_RXCFG_ZLFDMA 0x00000010
#define AR_RXCFG_DMASZ_MASK 0x00000007
#define AR_RXCFG_DMASZ_4B 0
#define AR_RXCFG_DMASZ_8B 1
#define AR_RXCFG_DMASZ_16B 2
#define AR_RXCFG_DMASZ_32B 3
#define AR_RXCFG_DMASZ_64B 4
#define AR_RXCFG_DMASZ_128B 5
#define AR_RXCFG_DMASZ_256B 6
#define AR_RXCFG_DMASZ_512B 7
#define AR_MIBC 0x0040
#define AR_MIBC_COW 0x00000001
#define AR_MIBC_FMC 0x00000002
#define AR_MIBC_CMC 0x00000004
#define AR_MIBC_MCS 0x00000008
#define AR_TOPS 0x0044
#define AR_TOPS_MASK 0x0000FFFF
#define AR_RXNPTO 0x0048
#define AR_RXNPTO_MASK 0x000003FF
#define AR_TXNPTO 0x004C
#define AR_TXNPTO_MASK 0x000003FF
#define AR_TXNPTO_QCU_MASK 0x000FFC00
#define AR_RPGTO 0x0050
#define AR_RPGTO_MASK 0x000003FF
#define AR_RPCNT 0x0054
#define AR_RPCNT_MASK 0x0000001F
#define AR_MACMISC 0x0058
#define AR_MACMISC_PCI_EXT_FORCE 0x00000010
#define AR_MACMISC_DMA_OBS 0x000001E0
#define AR_MACMISC_DMA_OBS_S 5
#define AR_MACMISC_DMA_OBS_LINE_0 0
#define AR_MACMISC_DMA_OBS_LINE_1 1
#define AR_MACMISC_DMA_OBS_LINE_2 2
#define AR_MACMISC_DMA_OBS_LINE_3 3
#define AR_MACMISC_DMA_OBS_LINE_4 4
#define AR_MACMISC_DMA_OBS_LINE_5 5
#define AR_MACMISC_DMA_OBS_LINE_6 6
#define AR_MACMISC_DMA_OBS_LINE_7 7
#define AR_MACMISC_DMA_OBS_LINE_8 8
#define AR_MACMISC_MISC_OBS 0x00000E00
#define AR_MACMISC_MISC_OBS_S 9
#define AR_MACMISC_MISC_OBS_BUS_LSB 0x00007000
#define AR_MACMISC_MISC_OBS_BUS_LSB_S 12
#define AR_MACMISC_MISC_OBS_BUS_MSB 0x00038000
#define AR_MACMISC_MISC_OBS_BUS_MSB_S 15
#define AR_MACMISC_MISC_OBS_BUS_1 1
#define AR_GTXTO 0x0064
#define AR_GTXTO_TIMEOUT_COUNTER 0x0000FFFF
#define AR_GTXTO_TIMEOUT_LIMIT 0xFFFF0000
#define AR_GTXTO_TIMEOUT_LIMIT_S 16
#define AR_GTTM 0x0068
#define AR_GTTM_USEC 0x00000001
#define AR_GTTM_IGNORE_IDLE 0x00000002
#define AR_GTTM_RESET_IDLE 0x00000004
#define AR_GTTM_CST_USEC 0x00000008
#define AR_CST 0x006C
#define AR_CST_TIMEOUT_COUNTER 0x0000FFFF
#define AR_CST_TIMEOUT_LIMIT 0xFFFF0000
#define AR_CST_TIMEOUT_LIMIT_S 16
#define AR_SREV_VERSION_9100 0x014
#define AR_SREV_5416_V20_OR_LATER(_ah) \
(AR_SREV_9100((_ah)) || AR_SREV_5416_20_OR_LATER(_ah))
#define AR_SREV_5416_V22_OR_LATER(_ah) \
(AR_SREV_9100((_ah)) || AR_SREV_5416_22_OR_LATER(_ah))
#define AR_ISR 0x0080
#define AR_ISR_RXOK 0x00000001
#define AR_ISR_RXDESC 0x00000002
#define AR_ISR_RXERR 0x00000004
#define AR_ISR_RXNOPKT 0x00000008
#define AR_ISR_RXEOL 0x00000010
#define AR_ISR_RXORN 0x00000020
#define AR_ISR_TXOK 0x00000040
#define AR_ISR_TXDESC 0x00000080
#define AR_ISR_TXERR 0x00000100
#define AR_ISR_TXNOPKT 0x00000200
#define AR_ISR_TXEOL 0x00000400
#define AR_ISR_TXURN 0x00000800
#define AR_ISR_MIB 0x00001000
#define AR_ISR_SWI 0x00002000
#define AR_ISR_RXPHY 0x00004000
#define AR_ISR_RXKCM 0x00008000
#define AR_ISR_SWBA 0x00010000
#define AR_ISR_BRSSI 0x00020000
#define AR_ISR_BMISS 0x00040000
#define AR_ISR_BNR 0x00100000
#define AR_ISR_RXCHIRP 0x00200000
#define AR_ISR_BCNMISC 0x00800000
#define AR_ISR_TIM 0x00800000
#define AR_ISR_QCBROVF 0x02000000
#define AR_ISR_QCBRURN 0x04000000
#define AR_ISR_QTRIG 0x08000000
#define AR_ISR_GENTMR 0x10000000
#define AR_ISR_TXMINTR 0x00080000
#define AR_ISR_RXMINTR 0x01000000
#define AR_ISR_TXINTM 0x40000000
#define AR_ISR_RXINTM 0x80000000
#define AR_ISR_S0 0x0084
#define AR_ISR_S0_QCU_TXOK 0x000003FF
#define AR_ISR_S0_QCU_TXOK_S 0
#define AR_ISR_S0_QCU_TXDESC 0x03FF0000
#define AR_ISR_S0_QCU_TXDESC_S 16
#define AR_ISR_S1 0x0088
#define AR_ISR_S1_QCU_TXERR 0x000003FF
#define AR_ISR_S1_QCU_TXERR_S 0
#define AR_ISR_S1_QCU_TXEOL 0x03FF0000
#define AR_ISR_S1_QCU_TXEOL_S 16
#define AR_ISR_S2 0x008c
#define AR_ISR_S2_QCU_TXURN 0x000003FF
#define AR_ISR_S2_CST 0x00400000
#define AR_ISR_S2_GTT 0x00800000
#define AR_ISR_S2_TIM 0x01000000
#define AR_ISR_S2_CABEND 0x02000000
#define AR_ISR_S2_DTIMSYNC 0x04000000
#define AR_ISR_S2_BCNTO 0x08000000
#define AR_ISR_S2_CABTO 0x10000000
#define AR_ISR_S2_DTIM 0x20000000
#define AR_ISR_S2_TSFOOR 0x40000000
#define AR_ISR_S2_TBTT_TIME 0x80000000
#define AR_ISR_S3 0x0090
#define AR_ISR_S3_QCU_QCBROVF 0x000003FF
#define AR_ISR_S3_QCU_QCBRURN 0x03FF0000
#define AR_ISR_S4 0x0094
#define AR_ISR_S4_QCU_QTRIG 0x000003FF
#define AR_ISR_S4_RESV0 0xFFFFFC00
#define AR_ISR_S5 0x0098
#define AR_ISR_S5_TIMER_TRIG 0x000000FF
#define AR_ISR_S5_TIMER_THRESH 0x0007FE00
#define AR_ISR_S5_TIM_TIMER 0x00000010
#define AR_ISR_S5_DTIM_TIMER 0x00000020
#define AR_ISR_S5_S 0x00d8
#define AR_IMR_S5 0x00b8
#define AR_IMR_S5_TIM_TIMER 0x00000010
#define AR_IMR_S5_DTIM_TIMER 0x00000020
#define AR_IMR 0x00a0
#define AR_IMR_RXOK 0x00000001
#define AR_IMR_RXDESC 0x00000002
#define AR_IMR_RXERR 0x00000004
#define AR_IMR_RXNOPKT 0x00000008
#define AR_IMR_RXEOL 0x00000010
#define AR_IMR_RXORN 0x00000020
#define AR_IMR_TXOK 0x00000040
#define AR_IMR_TXDESC 0x00000080
#define AR_IMR_TXERR 0x00000100
#define AR_IMR_TXNOPKT 0x00000200
#define AR_IMR_TXEOL 0x00000400
#define AR_IMR_TXURN 0x00000800
#define AR_IMR_MIB 0x00001000
#define AR_IMR_SWI 0x00002000
#define AR_IMR_RXPHY 0x00004000
#define AR_IMR_RXKCM 0x00008000
#define AR_IMR_SWBA 0x00010000
#define AR_IMR_BRSSI 0x00020000
#define AR_IMR_BMISS 0x00040000
#define AR_IMR_BNR 0x00100000
#define AR_IMR_RXCHIRP 0x00200000
#define AR_IMR_BCNMISC 0x00800000
#define AR_IMR_TIM 0x00800000
#define AR_IMR_QCBROVF 0x02000000
#define AR_IMR_QCBRURN 0x04000000
#define AR_IMR_QTRIG 0x08000000
#define AR_IMR_GENTMR 0x10000000
#define AR_IMR_TXMINTR 0x00080000
#define AR_IMR_RXMINTR 0x01000000
#define AR_IMR_TXINTM 0x40000000
#define AR_IMR_RXINTM 0x80000000
#define AR_IMR_S0 0x00a4
#define AR_IMR_S0_QCU_TXOK 0x000003FF
#define AR_IMR_S0_QCU_TXOK_S 0
#define AR_IMR_S0_QCU_TXDESC 0x03FF0000
#define AR_IMR_S0_QCU_TXDESC_S 16
#define AR_IMR_S1 0x00a8
#define AR_IMR_S1_QCU_TXERR 0x000003FF
#define AR_IMR_S1_QCU_TXERR_S 0
#define AR_IMR_S1_QCU_TXEOL 0x03FF0000
#define AR_IMR_S1_QCU_TXEOL_S 16
#define AR_IMR_S2 0x00ac
#define AR_IMR_S2_QCU_TXURN 0x000003FF
#define AR_IMR_S2_QCU_TXURN_S 0
#define AR_IMR_S2_CST 0x00400000
#define AR_IMR_S2_GTT 0x00800000
#define AR_IMR_S2_TIM 0x01000000
#define AR_IMR_S2_CABEND 0x02000000
#define AR_IMR_S2_DTIMSYNC 0x04000000
#define AR_IMR_S2_BCNTO 0x08000000
#define AR_IMR_S2_CABTO 0x10000000
#define AR_IMR_S2_DTIM 0x20000000
#define AR_IMR_S2_TSFOOR 0x40000000
#define AR_IMR_S3 0x00b0
#define AR_IMR_S3_QCU_QCBROVF 0x000003FF
#define AR_IMR_S3_QCU_QCBRURN 0x03FF0000
#define AR_IMR_S3_QCU_QCBRURN_S 16
#define AR_IMR_S4 0x00b4
#define AR_IMR_S4_QCU_QTRIG 0x000003FF
#define AR_IMR_S4_RESV0 0xFFFFFC00
#define AR_IMR_S5 0x00b8
#define AR_IMR_S5_TIMER_TRIG 0x000000FF
#define AR_IMR_S5_TIMER_THRESH 0x0000FF00
#define AR_ISR_RAC 0x00c0
#define AR_ISR_S0_S 0x00c4
#define AR_ISR_S0_QCU_TXOK 0x000003FF
#define AR_ISR_S0_QCU_TXOK_S 0
#define AR_ISR_S0_QCU_TXDESC 0x03FF0000
#define AR_ISR_S0_QCU_TXDESC_S 16
#define AR_ISR_S1_S 0x00c8
#define AR_ISR_S1_QCU_TXERR 0x000003FF
#define AR_ISR_S1_QCU_TXERR_S 0
#define AR_ISR_S1_QCU_TXEOL 0x03FF0000
#define AR_ISR_S1_QCU_TXEOL_S 16
#define AR_ISR_S2_S 0x00cc
#define AR_ISR_S3_S 0x00d0
#define AR_ISR_S4_S 0x00d4
#define AR_ISR_S5_S 0x00d8
#define AR_DMADBG_0 0x00e0
#define AR_DMADBG_1 0x00e4
#define AR_DMADBG_2 0x00e8
#define AR_DMADBG_3 0x00ec
#define AR_DMADBG_4 0x00f0
#define AR_DMADBG_5 0x00f4
#define AR_DMADBG_6 0x00f8
#define AR_DMADBG_7 0x00fc
#define AR_NUM_QCU 10
#define AR_QCU_0 0x0001
#define AR_QCU_1 0x0002
#define AR_QCU_2 0x0004
#define AR_QCU_3 0x0008
#define AR_QCU_4 0x0010
#define AR_QCU_5 0x0020
#define AR_QCU_6 0x0040
#define AR_QCU_7 0x0080
#define AR_QCU_8 0x0100
#define AR_QCU_9 0x0200
#define AR_Q0_TXDP 0x0800
#define AR_Q1_TXDP 0x0804
#define AR_Q2_TXDP 0x0808
#define AR_Q3_TXDP 0x080c
#define AR_Q4_TXDP 0x0810
#define AR_Q5_TXDP 0x0814
#define AR_Q6_TXDP 0x0818
#define AR_Q7_TXDP 0x081c
#define AR_Q8_TXDP 0x0820
#define AR_Q9_TXDP 0x0824
#define AR_QTXDP(_i) (AR_Q0_TXDP + ((_i)<<2))
#define AR_Q_TXE 0x0840
#define AR_Q_TXE_M 0x000003FF
#define AR_Q_TXD 0x0880
#define AR_Q_TXD_M 0x000003FF
#define AR_Q0_CBRCFG 0x08c0
#define AR_Q1_CBRCFG 0x08c4
#define AR_Q2_CBRCFG 0x08c8
#define AR_Q3_CBRCFG 0x08cc
#define AR_Q4_CBRCFG 0x08d0
#define AR_Q5_CBRCFG 0x08d4
#define AR_Q6_CBRCFG 0x08d8
#define AR_Q7_CBRCFG 0x08dc
#define AR_Q8_CBRCFG 0x08e0
#define AR_Q9_CBRCFG 0x08e4
#define AR_QCBRCFG(_i) (AR_Q0_CBRCFG + ((_i)<<2))
#define AR_Q_CBRCFG_INTERVAL 0x00FFFFFF
#define AR_Q_CBRCFG_INTERVAL_S 0
#define AR_Q_CBRCFG_OVF_THRESH 0xFF000000
#define AR_Q_CBRCFG_OVF_THRESH_S 24
#define AR_Q0_RDYTIMECFG 0x0900
#define AR_Q1_RDYTIMECFG 0x0904
#define AR_Q2_RDYTIMECFG 0x0908
#define AR_Q3_RDYTIMECFG 0x090c
#define AR_Q4_RDYTIMECFG 0x0910
#define AR_Q5_RDYTIMECFG 0x0914
#define AR_Q6_RDYTIMECFG 0x0918
#define AR_Q7_RDYTIMECFG 0x091c
#define AR_Q8_RDYTIMECFG 0x0920
#define AR_Q9_RDYTIMECFG 0x0924
#define AR_QRDYTIMECFG(_i) (AR_Q0_RDYTIMECFG + ((_i)<<2))
#define AR_Q_RDYTIMECFG_DURATION 0x00FFFFFF
#define AR_Q_RDYTIMECFG_DURATION_S 0
#define AR_Q_RDYTIMECFG_EN 0x01000000
#define AR_Q_ONESHOTARM_SC 0x0940
#define AR_Q_ONESHOTARM_SC_M 0x000003FF
#define AR_Q_ONESHOTARM_SC_RESV0 0xFFFFFC00
#define AR_Q_ONESHOTARM_CC 0x0980
#define AR_Q_ONESHOTARM_CC_M 0x000003FF
#define AR_Q_ONESHOTARM_CC_RESV0 0xFFFFFC00
#define AR_Q0_MISC 0x09c0
#define AR_Q1_MISC 0x09c4
#define AR_Q2_MISC 0x09c8
#define AR_Q3_MISC 0x09cc
#define AR_Q4_MISC 0x09d0
#define AR_Q5_MISC 0x09d4
#define AR_Q6_MISC 0x09d8
#define AR_Q7_MISC 0x09dc
#define AR_Q8_MISC 0x09e0
#define AR_Q9_MISC 0x09e4
#define AR_QMISC(_i) (AR_Q0_MISC + ((_i)<<2))
#define AR_Q_MISC_FSP 0x0000000F
#define AR_Q_MISC_FSP_ASAP 0
#define AR_Q_MISC_FSP_CBR 1
#define AR_Q_MISC_FSP_DBA_GATED 2
#define AR_Q_MISC_FSP_TIM_GATED 3
#define AR_Q_MISC_FSP_BEACON_SENT_GATED 4
#define AR_Q_MISC_FSP_BEACON_RCVD_GATED 5
#define AR_Q_MISC_ONE_SHOT_EN 0x00000010
#define AR_Q_MISC_CBR_INCR_DIS1 0x00000020
#define AR_Q_MISC_CBR_INCR_DIS0 0x00000040
#define AR_Q_MISC_BEACON_USE 0x00000080
#define AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN 0x00000100
#define AR_Q_MISC_RDYTIME_EXP_POLICY 0x00000200
#define AR_Q_MISC_RESET_CBR_EXP_CTR 0x00000400
#define AR_Q_MISC_DCU_EARLY_TERM_REQ 0x00000800
#define AR_Q_MISC_RESV0 0xFFFFF000
#define AR_Q0_STS 0x0a00
#define AR_Q1_STS 0x0a04
#define AR_Q2_STS 0x0a08
#define AR_Q3_STS 0x0a0c
#define AR_Q4_STS 0x0a10
#define AR_Q5_STS 0x0a14
#define AR_Q6_STS 0x0a18
#define AR_Q7_STS 0x0a1c
#define AR_Q8_STS 0x0a20
#define AR_Q9_STS 0x0a24
#define AR_QSTS(_i) (AR_Q0_STS + ((_i)<<2))
#define AR_Q_STS_PEND_FR_CNT 0x00000003
#define AR_Q_STS_RESV0 0x000000FC
#define AR_Q_STS_CBR_EXP_CNT 0x0000FF00
#define AR_Q_STS_RESV1 0xFFFF0000
#define AR_Q_RDYTIMESHDN 0x0a40
#define AR_Q_RDYTIMESHDN_M 0x000003FF
#define AR_NUM_DCU 10
#define AR_DCU_0 0x0001
#define AR_DCU_1 0x0002
#define AR_DCU_2 0x0004
#define AR_DCU_3 0x0008
#define AR_DCU_4 0x0010
#define AR_DCU_5 0x0020
#define AR_DCU_6 0x0040
#define AR_DCU_7 0x0080
#define AR_DCU_8 0x0100
#define AR_DCU_9 0x0200
#define AR_D0_QCUMASK 0x1000
#define AR_D1_QCUMASK 0x1004
#define AR_D2_QCUMASK 0x1008
#define AR_D3_QCUMASK 0x100c
#define AR_D4_QCUMASK 0x1010
#define AR_D5_QCUMASK 0x1014
#define AR_D6_QCUMASK 0x1018
#define AR_D7_QCUMASK 0x101c
#define AR_D8_QCUMASK 0x1020
#define AR_D9_QCUMASK 0x1024
#define AR_DQCUMASK(_i) (AR_D0_QCUMASK + ((_i)<<2))
#define AR_D_QCUMASK 0x000003FF
#define AR_D_QCUMASK_RESV0 0xFFFFFC00
#define AR_D_TXBLK_CMD 0x1038
#define AR_D_TXBLK_DATA(i) (AR_D_TXBLK_CMD+(i))
#define AR_D0_LCL_IFS 0x1040
#define AR_D1_LCL_IFS 0x1044
#define AR_D2_LCL_IFS 0x1048
#define AR_D3_LCL_IFS 0x104c
#define AR_D4_LCL_IFS 0x1050
#define AR_D5_LCL_IFS 0x1054
#define AR_D6_LCL_IFS 0x1058
#define AR_D7_LCL_IFS 0x105c
#define AR_D8_LCL_IFS 0x1060
#define AR_D9_LCL_IFS 0x1064
#define AR_DLCL_IFS(_i) (AR_D0_LCL_IFS + ((_i)<<2))
#define AR_D_LCL_IFS_CWMIN 0x000003FF
#define AR_D_LCL_IFS_CWMIN_S 0
#define AR_D_LCL_IFS_CWMAX 0x000FFC00
#define AR_D_LCL_IFS_CWMAX_S 10
#define AR_D_LCL_IFS_AIFS 0x0FF00000
#define AR_D_LCL_IFS_AIFS_S 20
#define AR_D_LCL_IFS_RESV0 0xF0000000
#define AR_D0_RETRY_LIMIT 0x1080
#define AR_D1_RETRY_LIMIT 0x1084
#define AR_D2_RETRY_LIMIT 0x1088
#define AR_D3_RETRY_LIMIT 0x108c
#define AR_D4_RETRY_LIMIT 0x1090
#define AR_D5_RETRY_LIMIT 0x1094
#define AR_D6_RETRY_LIMIT 0x1098
#define AR_D7_RETRY_LIMIT 0x109c
#define AR_D8_RETRY_LIMIT 0x10a0
#define AR_D9_RETRY_LIMIT 0x10a4
#define AR_DRETRY_LIMIT(_i) (AR_D0_RETRY_LIMIT + ((_i)<<2))
#define AR_D_RETRY_LIMIT_FR_SH 0x0000000F
#define AR_D_RETRY_LIMIT_FR_SH_S 0
#define AR_D_RETRY_LIMIT_STA_SH 0x00003F00
#define AR_D_RETRY_LIMIT_STA_SH_S 8
#define AR_D_RETRY_LIMIT_STA_LG 0x000FC000
#define AR_D_RETRY_LIMIT_STA_LG_S 14
#define AR_D_RETRY_LIMIT_RESV0 0xFFF00000
#define AR_D0_CHNTIME 0x10c0
#define AR_D1_CHNTIME 0x10c4
#define AR_D2_CHNTIME 0x10c8
#define AR_D3_CHNTIME 0x10cc
#define AR_D4_CHNTIME 0x10d0
#define AR_D5_CHNTIME 0x10d4
#define AR_D6_CHNTIME 0x10d8
#define AR_D7_CHNTIME 0x10dc
#define AR_D8_CHNTIME 0x10e0
#define AR_D9_CHNTIME 0x10e4
#define AR_DCHNTIME(_i) (AR_D0_CHNTIME + ((_i)<<2))
#define AR_D_CHNTIME_DUR 0x000FFFFF
#define AR_D_CHNTIME_DUR_S 0
#define AR_D_CHNTIME_EN 0x00100000
#define AR_D_CHNTIME_RESV0 0xFFE00000
#define AR_D0_MISC 0x1100
#define AR_D1_MISC 0x1104
#define AR_D2_MISC 0x1108
#define AR_D3_MISC 0x110c
#define AR_D4_MISC 0x1110
#define AR_D5_MISC 0x1114
#define AR_D6_MISC 0x1118
#define AR_D7_MISC 0x111c
#define AR_D8_MISC 0x1120
#define AR_D9_MISC 0x1124
#define AR_DMISC(_i) (AR_D0_MISC + ((_i)<<2))
#define AR_D_MISC_BKOFF_THRESH 0x0000003F
#define AR_D_MISC_RETRY_CNT_RESET_EN 0x00000040
#define AR_D_MISC_CW_RESET_EN 0x00000080
#define AR_D_MISC_FRAG_WAIT_EN 0x00000100
#define AR_D_MISC_FRAG_BKOFF_EN 0x00000200
#define AR_D_MISC_CW_BKOFF_EN 0x00001000
#define AR_D_MISC_VIR_COL_HANDLING 0x0000C000
#define AR_D_MISC_VIR_COL_HANDLING_S 14
#define AR_D_MISC_VIR_COL_HANDLING_DEFAULT 0
#define AR_D_MISC_VIR_COL_HANDLING_IGNORE 1
#define AR_D_MISC_BEACON_USE 0x00010000
#define AR_D_MISC_ARB_LOCKOUT_CNTRL 0x00060000
#define AR_D_MISC_ARB_LOCKOUT_CNTRL_S 17
#define AR_D_MISC_ARB_LOCKOUT_CNTRL_NONE 0
#define AR_D_MISC_ARB_LOCKOUT_CNTRL_INTRA_FR 1
#define AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL 2
#define AR_D_MISC_ARB_LOCKOUT_IGNORE 0x00080000
#define AR_D_MISC_SEQ_NUM_INCR_DIS 0x00100000
#define AR_D_MISC_POST_FR_BKOFF_DIS 0x00200000
#define AR_D_MISC_VIT_COL_CW_BKOFF_EN 0x00400000
#define AR_D_MISC_BLOWN_IFS_RETRY_EN 0x00800000
#define AR_D_MISC_RESV0 0xFF000000
#define AR_D_SEQNUM 0x1140
#define AR_D_GBL_IFS_SIFS 0x1030
#define AR_D_GBL_IFS_SIFS_M 0x0000FFFF
#define AR_D_GBL_IFS_SIFS_RESV0 0xFFFFFFFF
#define AR_D_TXBLK_BASE 0x1038
#define AR_D_TXBLK_WRITE_BITMASK 0x0000FFFF
#define AR_D_TXBLK_WRITE_BITMASK_S 0
#define AR_D_TXBLK_WRITE_SLICE 0x000F0000
#define AR_D_TXBLK_WRITE_SLICE_S 16
#define AR_D_TXBLK_WRITE_DCU 0x00F00000
#define AR_D_TXBLK_WRITE_DCU_S 20
#define AR_D_TXBLK_WRITE_COMMAND 0x0F000000
#define AR_D_TXBLK_WRITE_COMMAND_S 24
#define AR_D_GBL_IFS_SLOT 0x1070
#define AR_D_GBL_IFS_SLOT_M 0x0000FFFF
#define AR_D_GBL_IFS_SLOT_RESV0 0xFFFF0000
#define AR_D_GBL_IFS_EIFS 0x10b0
#define AR_D_GBL_IFS_EIFS_M 0x0000FFFF
#define AR_D_GBL_IFS_EIFS_RESV0 0xFFFF0000
#define AR_D_GBL_IFS_MISC 0x10f0
#define AR_D_GBL_IFS_MISC_LFSR_SLICE_SEL 0x00000007
#define AR_D_GBL_IFS_MISC_TURBO_MODE 0x00000008
#define AR_D_GBL_IFS_MISC_USEC_DURATION 0x000FFC00
#define AR_D_GBL_IFS_MISC_DCU_ARBITER_DLY 0x00300000
#define AR_D_GBL_IFS_MISC_RANDOM_LFSR_SLICE_DIS 0x01000000
#define AR_D_GBL_IFS_MISC_SLOT_XMIT_WIND_LEN 0x06000000
#define AR_D_GBL_IFS_MISC_FORCE_XMIT_SLOT_BOUND 0x08000000
#define AR_D_GBL_IFS_MISC_IGNORE_BACKOFF 0x10000000
#define AR_D_FPCTL 0x1230
#define AR_D_FPCTL_DCU 0x0000000F
#define AR_D_FPCTL_DCU_S 0
#define AR_D_FPCTL_PREFETCH_EN 0x00000010
#define AR_D_FPCTL_BURST_PREFETCH 0x00007FE0
#define AR_D_FPCTL_BURST_PREFETCH_S 5
#define AR_D_TXPSE 0x1270
#define AR_D_TXPSE_CTRL 0x000003FF
#define AR_D_TXPSE_RESV0 0x0000FC00
#define AR_D_TXPSE_STATUS 0x00010000
#define AR_D_TXPSE_RESV1 0xFFFE0000
#define AR_D_TXSLOTMASK 0x12f0
#define AR_D_TXSLOTMASK_NUM 0x0000000F
#define AR_CFG_LED 0x1f04
#define AR_CFG_SCLK_RATE_IND 0x00000003
#define AR_CFG_SCLK_RATE_IND_S 0
#define AR_CFG_SCLK_32MHZ 0x00000000
#define AR_CFG_SCLK_4MHZ 0x00000001
#define AR_CFG_SCLK_1MHZ 0x00000002
#define AR_CFG_SCLK_32KHZ 0x00000003
#define AR_CFG_LED_BLINK_SLOW 0x00000008
#define AR_CFG_LED_BLINK_THRESH_SEL 0x00000070
#define AR_CFG_LED_MODE_SEL 0x00000380
#define AR_CFG_LED_MODE_SEL_S 7
#define AR_CFG_LED_POWER 0x00000280
#define AR_CFG_LED_POWER_S 7
#define AR_CFG_LED_NETWORK 0x00000300
#define AR_CFG_LED_NETWORK_S 7
#define AR_CFG_LED_MODE_PROP 0x0
#define AR_CFG_LED_MODE_RPROP 0x1
#define AR_CFG_LED_MODE_SPLIT 0x2
#define AR_CFG_LED_MODE_RAND 0x3
#define AR_CFG_LED_MODE_POWER_OFF 0x4
#define AR_CFG_LED_MODE_POWER_ON 0x5
#define AR_CFG_LED_MODE_NETWORK_OFF 0x4
#define AR_CFG_LED_MODE_NETWORK_ON 0x6
#define AR_CFG_LED_ASSOC_CTL 0x00000c00
#define AR_CFG_LED_ASSOC_CTL_S 10
#define AR_CFG_LED_ASSOC_NONE 0x0
#define AR_CFG_LED_ASSOC_ACTIVE 0x1
#define AR_CFG_LED_ASSOC_PENDING 0x2
#define AR_CFG_LED_BLINK_SLOW 0x00000008
#define AR_CFG_LED_BLINK_SLOW_S 3
#define AR_CFG_LED_BLINK_THRESH_SEL 0x00000070
#define AR_CFG_LED_BLINK_THRESH_SEL_S 4
#define AR_MAC_SLEEP 0x1f00
#define AR_MAC_SLEEP_MAC_AWAKE 0x00000000
#define AR_MAC_SLEEP_MAC_ASLEEP 0x00000001
#define AR_RC 0x4000
#define AR_RC_AHB 0x00000001
#define AR_RC_APB 0x00000002
#define AR_RC_HOSTIF 0x00000100
#define AR_WA 0x4004
#define AR_PM_STATE 0x4008
#define AR_PM_STATE_PME_D3COLD_VAUX 0x00100000
#define AR_HOST_TIMEOUT 0x4018
#define AR_HOST_TIMEOUT_APB_CNTR 0x0000FFFF
#define AR_HOST_TIMEOUT_APB_CNTR_S 0
#define AR_HOST_TIMEOUT_LCL_CNTR 0xFFFF0000
#define AR_HOST_TIMEOUT_LCL_CNTR_S 16
#define AR_EEPROM 0x401c
#define AR_EEPROM_ABSENT 0x00000100
#define AR_EEPROM_CORRUPT 0x00000200
#define AR_EEPROM_PROT_MASK 0x03FFFC00
#define AR_EEPROM_PROT_MASK_S 10
#define EEPROM_PROTECT_RP_0_31 0x0001
#define EEPROM_PROTECT_WP_0_31 0x0002
#define EEPROM_PROTECT_RP_32_63 0x0004
#define EEPROM_PROTECT_WP_32_63 0x0008
#define EEPROM_PROTECT_RP_64_127 0x0010
#define EEPROM_PROTECT_WP_64_127 0x0020
#define EEPROM_PROTECT_RP_128_191 0x0040
#define EEPROM_PROTECT_WP_128_191 0x0080
#define EEPROM_PROTECT_RP_192_255 0x0100
#define EEPROM_PROTECT_WP_192_255 0x0200
#define EEPROM_PROTECT_RP_256_511 0x0400
#define EEPROM_PROTECT_WP_256_511 0x0800
#define EEPROM_PROTECT_RP_512_1023 0x1000
#define EEPROM_PROTECT_WP_512_1023 0x2000
#define EEPROM_PROTECT_RP_1024_2047 0x4000
#define EEPROM_PROTECT_WP_1024_2047 0x8000
#define AR_SREV \
((AR_SREV_9100(ah)) ? 0x0600 : 0x4020)
#define AR_SREV_ID \
((AR_SREV_9100(ah)) ? 0x00000FFF : 0x000000FF)
#define AR_SREV_VERSION 0x000000F0
#define AR_SREV_VERSION_S 4
#define AR_SREV_REVISION 0x00000007
#define AR_SREV_ID2 0xFFFFFFFF
#define AR_SREV_VERSION2 0xFFFC0000
#define AR_SREV_VERSION2_S 18
#define AR_SREV_TYPE2 0x0003F000
#define AR_SREV_TYPE2_S 12
#define AR_SREV_TYPE2_CHAIN 0x00001000
#define AR_SREV_TYPE2_HOST_MODE 0x00002000
#define AR_SREV_REVISION2 0x00000F00
#define AR_SREV_REVISION2_S 8
#define AR_SREV_VERSION_5416_PCI 0xD
#define AR_SREV_VERSION_5416_PCIE 0xC
#define AR_SREV_REVISION_5416_10 0
#define AR_SREV_REVISION_5416_20 1
#define AR_SREV_REVISION_5416_22 2
#define AR_SREV_VERSION_9160 0x40
#define AR_SREV_REVISION_9160_10 0
#define AR_SREV_REVISION_9160_11 1
#define AR_SREV_VERSION_9280 0x80
#define AR_SREV_REVISION_9280_10 0
#define AR_SREV_REVISION_9280_20 1
#define AR_SREV_REVISION_9280_21 2
#define AR_SREV_VERSION_9285 0xC0
#define AR_SREV_REVISION_9285_10 0
#define AR_SREV_9100_OR_LATER(_ah) \
(((_ah)->ah_macVersion >= AR_SREV_VERSION_5416_PCIE))
#define AR_SREV_5416_20_OR_LATER(_ah) \
(((_ah)->ah_macVersion >= AR_SREV_VERSION_9160) || \
((_ah)->ah_macRev >= AR_SREV_REVISION_5416_20))
#define AR_SREV_5416_22_OR_LATER(_ah) \
(((_ah)->ah_macVersion >= AR_SREV_VERSION_9160) || \
((_ah)->ah_macRev >= AR_SREV_REVISION_5416_22))
#define AR_SREV_9160(_ah) \
(((_ah)->ah_macVersion == AR_SREV_VERSION_9160))
#define AR_SREV_9160_10_OR_LATER(_ah) \
(((_ah)->ah_macVersion >= AR_SREV_VERSION_9160))
#define AR_SREV_9160_11(_ah) \
(AR_SREV_9160(_ah) && ((_ah)->ah_macRev == AR_SREV_REVISION_9160_11))
#define AR_SREV_9280(_ah) \
(((_ah)->ah_macVersion == AR_SREV_VERSION_9280))
#define AR_SREV_9280_10_OR_LATER(_ah) \
(((_ah)->ah_macVersion >= AR_SREV_VERSION_9280))
#define AR_SREV_9280_20(_ah) \
(((_ah)->ah_macVersion == AR_SREV_VERSION_9280) && \
((_ah)->ah_macRev >= AR_SREV_REVISION_9280_20))
#define AR_SREV_9280_20_OR_LATER(_ah) \
(((_ah)->ah_macVersion > AR_SREV_VERSION_9280) || \
(((_ah)->ah_macVersion == AR_SREV_VERSION_9280) && \
((_ah)->ah_macRev >= AR_SREV_REVISION_9280_20)))
#define AR_SREV_9285(_ah) (((_ah)->ah_macVersion == AR_SREV_VERSION_9285))
#define AR_SREV_9285_10_OR_LATER(_ah) \
(((_ah)->ah_macVersion >= AR_SREV_VERSION_9285))
#define AR_RADIO_SREV_MAJOR 0xf0
#define AR_RAD5133_SREV_MAJOR 0xc0
#define AR_RAD2133_SREV_MAJOR 0xd0
#define AR_RAD5122_SREV_MAJOR 0xe0
#define AR_RAD2122_SREV_MAJOR 0xf0
#define AR_AHB_MODE 0x4024
#define AR_AHB_EXACT_WR_EN 0x00000000
#define AR_AHB_BUF_WR_EN 0x00000001
#define AR_AHB_EXACT_RD_EN 0x00000000
#define AR_AHB_CACHELINE_RD_EN 0x00000002
#define AR_AHB_PREFETCH_RD_EN 0x00000004
#define AR_AHB_PAGE_SIZE_1K 0x00000000
#define AR_AHB_PAGE_SIZE_2K 0x00000008
#define AR_AHB_PAGE_SIZE_4K 0x00000010
#define AR_INTR_RTC_IRQ 0x00000001
#define AR_INTR_MAC_IRQ 0x00000002
#define AR_INTR_EEP_PROT_ACCESS 0x00000004
#define AR_INTR_MAC_AWAKE 0x00020000
#define AR_INTR_MAC_ASLEEP 0x00040000
#define AR_INTR_SPURIOUS 0xFFFFFFFF
#define AR_INTR_SYNC_CAUSE_CLR 0x4028
#define AR_INTR_SYNC_CAUSE 0x4028
#define AR_INTR_SYNC_ENABLE 0x402c
#define AR_INTR_SYNC_ENABLE_GPIO 0xFFFC0000
#define AR_INTR_SYNC_ENABLE_GPIO_S 18
enum {
AR_INTR_SYNC_RTC_IRQ = 0x00000001,
AR_INTR_SYNC_MAC_IRQ = 0x00000002,
AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS = 0x00000004,
AR_INTR_SYNC_APB_TIMEOUT = 0x00000008,
AR_INTR_SYNC_PCI_MODE_CONFLICT = 0x00000010,
AR_INTR_SYNC_HOST1_FATAL = 0x00000020,
AR_INTR_SYNC_HOST1_PERR = 0x00000040,
AR_INTR_SYNC_TRCV_FIFO_PERR = 0x00000080,
AR_INTR_SYNC_RADM_CPL_EP = 0x00000100,
AR_INTR_SYNC_RADM_CPL_DLLP_ABORT = 0x00000200,
AR_INTR_SYNC_RADM_CPL_TLP_ABORT = 0x00000400,
AR_INTR_SYNC_RADM_CPL_ECRC_ERR = 0x00000800,
AR_INTR_SYNC_RADM_CPL_TIMEOUT = 0x00001000,
AR_INTR_SYNC_LOCAL_TIMEOUT = 0x00002000,
AR_INTR_SYNC_PM_ACCESS = 0x00004000,
AR_INTR_SYNC_MAC_AWAKE = 0x00008000,
AR_INTR_SYNC_MAC_ASLEEP = 0x00010000,
AR_INTR_SYNC_MAC_SLEEP_ACCESS = 0x00020000,
AR_INTR_SYNC_ALL = 0x0003FFFF,
AR_INTR_SYNC_DEFAULT = (AR_INTR_SYNC_HOST1_FATAL |
AR_INTR_SYNC_HOST1_PERR |
AR_INTR_SYNC_RADM_CPL_EP |
AR_INTR_SYNC_RADM_CPL_DLLP_ABORT |
AR_INTR_SYNC_RADM_CPL_TLP_ABORT |
AR_INTR_SYNC_RADM_CPL_ECRC_ERR |
AR_INTR_SYNC_RADM_CPL_TIMEOUT |
AR_INTR_SYNC_LOCAL_TIMEOUT |
AR_INTR_SYNC_MAC_SLEEP_ACCESS),
AR_INTR_SYNC_SPURIOUS = 0xFFFFFFFF,
};
#define AR_INTR_ASYNC_MASK 0x4030
#define AR_INTR_ASYNC_MASK_GPIO 0xFFFC0000
#define AR_INTR_ASYNC_MASK_GPIO_S 18
#define AR_INTR_SYNC_MASK 0x4034
#define AR_INTR_SYNC_MASK_GPIO 0xFFFC0000
#define AR_INTR_SYNC_MASK_GPIO_S 18
#define AR_INTR_ASYNC_CAUSE_CLR 0x4038
#define AR_INTR_ASYNC_CAUSE 0x4038
#define AR_INTR_ASYNC_ENABLE 0x403c
#define AR_INTR_ASYNC_ENABLE_GPIO 0xFFFC0000
#define AR_INTR_ASYNC_ENABLE_GPIO_S 18
#define AR_PCIE_SERDES 0x4040
#define AR_PCIE_SERDES2 0x4044
#define AR_PCIE_PM_CTRL 0x4014
#define AR_PCIE_PM_CTRL_ENA 0x00080000
#define AR_NUM_GPIO 14
#define AR928X_NUM_GPIO 10
#define AR_GPIO_IN_OUT 0x4048
#define AR_GPIO_IN_VAL 0x0FFFC000
#define AR_GPIO_IN_VAL_S 14
#define AR928X_GPIO_IN_VAL 0x000FFC00
#define AR928X_GPIO_IN_VAL_S 10
#define AR_GPIO_OE_OUT 0x404c
#define AR_GPIO_OE_OUT_DRV 0x3
#define AR_GPIO_OE_OUT_DRV_NO 0x0
#define AR_GPIO_OE_OUT_DRV_LOW 0x1
#define AR_GPIO_OE_OUT_DRV_HI 0x2
#define AR_GPIO_OE_OUT_DRV_ALL 0x3
#define AR_GPIO_INTR_POL 0x4050
#define AR_GPIO_INTR_POL_VAL 0x00001FFF
#define AR_GPIO_INTR_POL_VAL_S 0
#define AR_GPIO_INPUT_EN_VAL 0x4054
#define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF 0x00000080
#define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S 7
#define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB 0x00008000
#define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB_S 15
#define AR_GPIO_RTC_RESET_OVERRIDE_ENABLE 0x00010000
#define AR_GPIO_JTAG_DISABLE 0x00020000
#define AR_GPIO_INPUT_MUX1 0x4058
#define AR_GPIO_INPUT_MUX2 0x405c
#define AR_GPIO_INPUT_MUX2_CLK25 0x0000000f
#define AR_GPIO_INPUT_MUX2_CLK25_S 0
#define AR_GPIO_INPUT_MUX2_RFSILENT 0x000000f0
#define AR_GPIO_INPUT_MUX2_RFSILENT_S 4
#define AR_GPIO_INPUT_MUX2_RTC_RESET 0x00000f00
#define AR_GPIO_INPUT_MUX2_RTC_RESET_S 8
#define AR_GPIO_OUTPUT_MUX1 0x4060
#define AR_GPIO_OUTPUT_MUX2 0x4064
#define AR_GPIO_OUTPUT_MUX3 0x4068
#define AR_GPIO_OUTPUT_MUX_AS_OUTPUT 0
#define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1
#define AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED 2
#define AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED 5
#define AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED 6
#define AR_INPUT_STATE 0x406c
#define AR_EEPROM_STATUS_DATA 0x407c
#define AR_EEPROM_STATUS_DATA_VAL 0x0000ffff
#define AR_EEPROM_STATUS_DATA_VAL_S 0
#define AR_EEPROM_STATUS_DATA_BUSY 0x00010000
#define AR_EEPROM_STATUS_DATA_BUSY_ACCESS 0x00020000
#define AR_EEPROM_STATUS_DATA_PROT_ACCESS 0x00040000
#define AR_EEPROM_STATUS_DATA_ABSENT_ACCESS 0x00080000
#define AR_OBS 0x4080
#define AR_PCIE_MSI 0x4094
#define AR_PCIE_MSI_ENABLE 0x00000001
#define AR_RTC_9160_PLL_DIV 0x000003ff
#define AR_RTC_9160_PLL_DIV_S 0
#define AR_RTC_9160_PLL_REFDIV 0x00003C00
#define AR_RTC_9160_PLL_REFDIV_S 10
#define AR_RTC_9160_PLL_CLKSEL 0x0000C000
#define AR_RTC_9160_PLL_CLKSEL_S 14
#define AR_RTC_BASE 0x00020000
#define AR_RTC_RC \
(AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0000) : 0x7000
#define AR_RTC_RC_M 0x00000003
#define AR_RTC_RC_MAC_WARM 0x00000001
#define AR_RTC_RC_MAC_COLD 0x00000002
#define AR_RTC_RC_COLD_RESET 0x00000004
#define AR_RTC_RC_WARM_RESET 0x00000008
#define AR_RTC_PLL_CONTROL \
(AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0014) : 0x7014
#define AR_RTC_PLL_DIV 0x0000001f
#define AR_RTC_PLL_DIV_S 0
#define AR_RTC_PLL_DIV2 0x00000020
#define AR_RTC_PLL_REFDIV_5 0x000000c0
#define AR_RTC_PLL_CLKSEL 0x00000300
#define AR_RTC_PLL_CLKSEL_S 8
#define AR_RTC_RESET \
((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0040) : 0x7040)
#define AR_RTC_RESET_EN (0x00000001)
#define AR_RTC_STATUS \
((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0044) : 0x7044)
#define AR_RTC_STATUS_M \
((AR_SREV_9100(ah)) ? 0x0000003f : 0x0000000f)
#define AR_RTC_PM_STATUS_M 0x0000000f
#define AR_RTC_STATUS_SHUTDOWN 0x00000001
#define AR_RTC_STATUS_ON 0x00000002
#define AR_RTC_STATUS_SLEEP 0x00000004
#define AR_RTC_STATUS_WAKEUP 0x00000008
#define AR_RTC_SLEEP_CLK \
((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0048) : 0x7048)
#define AR_RTC_FORCE_DERIVED_CLK 0x2
#define AR_RTC_FORCE_WAKE \
((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x004c) : 0x704c)
#define AR_RTC_FORCE_WAKE_EN 0x00000001
#define AR_RTC_FORCE_WAKE_ON_INT 0x00000002
#define AR_RTC_INTR_CAUSE \
((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0050) : 0x7050)
#define AR_RTC_INTR_ENABLE \
((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0054) : 0x7054)
#define AR_RTC_INTR_MASK \
((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0058) : 0x7058)
#define AR_SEQ_MASK 0x8060
#define AR_AN_RF2G1_CH0 0x7810
#define AR_AN_RF2G1_CH0_OB 0x03800000
#define AR_AN_RF2G1_CH0_OB_S 23
#define AR_AN_RF2G1_CH0_DB 0x1C000000
#define AR_AN_RF2G1_CH0_DB_S 26
#define AR_AN_RF5G1_CH0 0x7818
#define AR_AN_RF5G1_CH0_OB5 0x00070000
#define AR_AN_RF5G1_CH0_OB5_S 16
#define AR_AN_RF5G1_CH0_DB5 0x00380000
#define AR_AN_RF5G1_CH0_DB5_S 19
#define AR_AN_RF2G1_CH1 0x7834
#define AR_AN_RF2G1_CH1_OB 0x03800000
#define AR_AN_RF2G1_CH1_OB_S 23
#define AR_AN_RF2G1_CH1_DB 0x1C000000
#define AR_AN_RF2G1_CH1_DB_S 26
#define AR_AN_RF5G1_CH1 0x783C
#define AR_AN_RF5G1_CH1_OB5 0x00070000
#define AR_AN_RF5G1_CH1_OB5_S 16
#define AR_AN_RF5G1_CH1_DB5 0x00380000
#define AR_AN_RF5G1_CH1_DB5_S 19
#define AR_AN_TOP2 0x7894
#define AR_AN_TOP2_XPABIAS_LVL 0xC0000000
#define AR_AN_TOP2_XPABIAS_LVL_S 30
#define AR_AN_TOP2_LOCALBIAS 0x00200000
#define AR_AN_TOP2_LOCALBIAS_S 21
#define AR_AN_TOP2_PWDCLKIND 0x00400000
#define AR_AN_TOP2_PWDCLKIND_S 22
#define AR_AN_SYNTH9 0x7868
#define AR_AN_SYNTH9_REFDIVA 0xf8000000
#define AR_AN_SYNTH9_REFDIVA_S 27
#define AR_STA_ID0 0x8000
#define AR_STA_ID1 0x8004
#define AR_STA_ID1_SADH_MASK 0x0000FFFF
#define AR_STA_ID1_STA_AP 0x00010000
#define AR_STA_ID1_ADHOC 0x00020000
#define AR_STA_ID1_PWR_SAV 0x00040000
#define AR_STA_ID1_KSRCHDIS 0x00080000
#define AR_STA_ID1_PCF 0x00100000
#define AR_STA_ID1_USE_DEFANT 0x00200000
#define AR_STA_ID1_DEFANT_UPDATE 0x00400000
#define AR_STA_ID1_RTS_USE_DEF 0x00800000
#define AR_STA_ID1_ACKCTS_6MB 0x01000000
#define AR_STA_ID1_BASE_RATE_11B 0x02000000
#define AR_STA_ID1_SECTOR_SELF_GEN 0x04000000
#define AR_STA_ID1_CRPT_MIC_ENABLE 0x08000000
#define AR_STA_ID1_KSRCH_MODE 0x10000000
#define AR_STA_ID1_PRESERVE_SEQNUM 0x20000000
#define AR_STA_ID1_CBCIV_ENDIAN 0x40000000
#define AR_STA_ID1_MCAST_KSRCH 0x80000000
#define AR_BSS_ID0 0x8008
#define AR_BSS_ID1 0x800C
#define AR_BSS_ID1_U16 0x0000FFFF
#define AR_BSS_ID1_AID 0x07FF0000
#define AR_BSS_ID1_AID_S 16
#define AR_BCN_RSSI_AVE 0x8010
#define AR_BCN_RSSI_AVE_MASK 0x00000FFF
#define AR_TIME_OUT 0x8014
#define AR_TIME_OUT_ACK 0x00003FFF
#define AR_TIME_OUT_ACK_S 0
#define AR_TIME_OUT_CTS 0x3FFF0000
#define AR_TIME_OUT_CTS_S 16
#define AR_RSSI_THR 0x8018
#define AR_RSSI_THR_MASK 0x000000FF
#define AR_RSSI_THR_BM_THR 0x0000FF00
#define AR_RSSI_THR_BM_THR_S 8
#define AR_RSSI_BCN_WEIGHT 0x1F000000
#define AR_RSSI_BCN_WEIGHT_S 24
#define AR_RSSI_BCN_RSSI_RST 0x20000000
#define AR_USEC 0x801c
#define AR_USEC_USEC 0x0000007F
#define AR_USEC_TX_LAT 0x007FC000
#define AR_USEC_TX_LAT_S 14
#define AR_USEC_RX_LAT 0x1F800000
#define AR_USEC_RX_LAT_S 23
#define AR_RESET_TSF 0x8020
#define AR_RESET_TSF_ONCE 0x01000000
#define AR_MAX_CFP_DUR 0x8038
#define AR_CFP_VAL 0x0000FFFF
#define AR_RX_FILTER 0x803C
#define AR_RX_FILTER_ALL 0x00000000
#define AR_RX_UCAST 0x00000001
#define AR_RX_MCAST 0x00000002
#define AR_RX_BCAST 0x00000004
#define AR_RX_CONTROL 0x00000008
#define AR_RX_BEACON 0x00000010
#define AR_RX_PROM 0x00000020
#define AR_RX_PROBE_REQ 0x00000080
#define AR_RX_MY_BEACON 0x00000200
#define AR_RX_COMPR_BAR 0x00000400
#define AR_RX_COMPR_BA 0x00000800
#define AR_RX_UNCOM_BA_BAR 0x00001000
#define AR_MCAST_FIL0 0x8040
#define AR_MCAST_FIL1 0x8044
#define AR_DIAG_SW 0x8048
#define AR_DIAG_CACHE_ACK 0x00000001
#define AR_DIAG_ACK_DIS 0x00000002
#define AR_DIAG_CTS_DIS 0x00000004
#define AR_DIAG_ENCRYPT_DIS 0x00000008
#define AR_DIAG_DECRYPT_DIS 0x00000010
#define AR_DIAG_RX_DIS 0x00000020
#define AR_DIAG_LOOP_BACK 0x00000040
#define AR_DIAG_CORR_FCS 0x00000080
#define AR_DIAG_CHAN_INFO 0x00000100
#define AR_DIAG_SCRAM_SEED 0x0001FE00
#define AR_DIAG_SCRAM_SEED_S 8
#define AR_DIAG_FRAME_NV0 0x00020000
#define AR_DIAG_OBS_PT_SEL1 0x000C0000
#define AR_DIAG_OBS_PT_SEL1_S 18
#define AR_DIAG_FORCE_RX_CLEAR 0x00100000
#define AR_DIAG_IGNORE_VIRT_CS 0x00200000
#define AR_DIAG_FORCE_CH_IDLE_HIGH 0x00400000
#define AR_DIAG_EIFS_CTRL_ENA 0x00800000
#define AR_DIAG_DUAL_CHAIN_INFO 0x01000000
#define AR_DIAG_RX_ABORT 0x02000000
#define AR_DIAG_SATURATE_CYCLE_CNT 0x04000000
#define AR_DIAG_OBS_PT_SEL2 0x08000000
#define AR_DIAG_RX_CLEAR_CTL_LOW 0x10000000
#define AR_DIAG_RX_CLEAR_EXT_LOW 0x20000000
#define AR_TSF_L32 0x804c
#define AR_TSF_U32 0x8050
#define AR_TST_ADDAC 0x8054
#define AR_DEF_ANTENNA 0x8058
#define AR_AES_MUTE_MASK0 0x805c
#define AR_AES_MUTE_MASK0_FC 0x0000FFFF
#define AR_AES_MUTE_MASK0_QOS 0xFFFF0000
#define AR_AES_MUTE_MASK0_QOS_S 16
#define AR_AES_MUTE_MASK1 0x8060
#define AR_AES_MUTE_MASK1_SEQ 0x0000FFFF
#define AR_GATED_CLKS 0x8064
#define AR_GATED_CLKS_TX 0x00000002
#define AR_GATED_CLKS_RX 0x00000004
#define AR_GATED_CLKS_REG 0x00000008
#define AR_OBS_BUS_CTRL 0x8068
#define AR_OBS_BUS_SEL_1 0x00040000
#define AR_OBS_BUS_SEL_2 0x00080000
#define AR_OBS_BUS_SEL_3 0x000C0000
#define AR_OBS_BUS_SEL_4 0x08040000
#define AR_OBS_BUS_SEL_5 0x08080000
#define AR_OBS_BUS_1 0x806c
#define AR_OBS_BUS_1_PCU 0x00000001
#define AR_OBS_BUS_1_RX_END 0x00000002
#define AR_OBS_BUS_1_RX_WEP 0x00000004
#define AR_OBS_BUS_1_RX_BEACON 0x00000008
#define AR_OBS_BUS_1_RX_FILTER 0x00000010
#define AR_OBS_BUS_1_TX_HCF 0x00000020
#define AR_OBS_BUS_1_QUIET_TIME 0x00000040
#define AR_OBS_BUS_1_CHAN_IDLE 0x00000080
#define AR_OBS_BUS_1_TX_HOLD 0x00000100
#define AR_OBS_BUS_1_TX_FRAME 0x00000200
#define AR_OBS_BUS_1_RX_FRAME 0x00000400
#define AR_OBS_BUS_1_RX_CLEAR 0x00000800
#define AR_OBS_BUS_1_WEP_STATE 0x0003F000
#define AR_OBS_BUS_1_WEP_STATE_S 12
#define AR_OBS_BUS_1_RX_STATE 0x01F00000
#define AR_OBS_BUS_1_RX_STATE_S 20
#define AR_OBS_BUS_1_TX_STATE 0x7E000000
#define AR_OBS_BUS_1_TX_STATE_S 25
#define AR_LAST_TSTP 0x8080
#define AR_NAV 0x8084
#define AR_RTS_OK 0x8088
#define AR_RTS_FAIL 0x808c
#define AR_ACK_FAIL 0x8090
#define AR_FCS_FAIL 0x8094
#define AR_BEACON_CNT 0x8098
#define AR_SLEEP1 0x80d4
#define AR_SLEEP1_ASSUME_DTIM 0x00080000
#define AR_SLEEP1_CAB_TIMEOUT 0xFFE00000
#define AR_SLEEP1_CAB_TIMEOUT_S 21
#define AR_SLEEP2 0x80d8
#define AR_SLEEP2_BEACON_TIMEOUT 0xFFE00000
#define AR_SLEEP2_BEACON_TIMEOUT_S 21
#define AR_BSSMSKL 0x80e0
#define AR_BSSMSKU 0x80e4
#define AR_TPC 0x80e8
#define AR_TPC_ACK 0x0000003f
#define AR_TPC_ACK_S 0x00
#define AR_TPC_CTS 0x00003f00
#define AR_TPC_CTS_S 0x08
#define AR_TPC_CHIRP 0x003f0000
#define AR_TPC_CHIRP_S 0x16
#define AR_TFCNT 0x80ec
#define AR_RFCNT 0x80f0
#define AR_RCCNT 0x80f4
#define AR_CCCNT 0x80f8
#define AR_QUIET1 0x80fc
#define AR_QUIET1_NEXT_QUIET_S 0
#define AR_QUIET1_NEXT_QUIET_M 0x0000ffff
#define AR_QUIET1_QUIET_ENABLE 0x00010000
#define AR_QUIET1_QUIET_ACK_CTS_ENABLE 0x00020000
#define AR_QUIET2 0x8100
#define AR_QUIET2_QUIET_PERIOD_S 0
#define AR_QUIET2_QUIET_PERIOD_M 0x0000ffff
#define AR_QUIET2_QUIET_DUR_S 16
#define AR_QUIET2_QUIET_DUR 0xffff0000
#define AR_TSF_PARM 0x8104
#define AR_TSF_INCREMENT_M 0x000000ff
#define AR_TSF_INCREMENT_S 0x00
#define AR_QOS_NO_ACK 0x8108
#define AR_QOS_NO_ACK_TWO_BIT 0x0000000f
#define AR_QOS_NO_ACK_TWO_BIT_S 0
#define AR_QOS_NO_ACK_BIT_OFF 0x00000070
#define AR_QOS_NO_ACK_BIT_OFF_S 4
#define AR_QOS_NO_ACK_BYTE_OFF 0x00000180
#define AR_QOS_NO_ACK_BYTE_OFF_S 7
#define AR_PHY_ERR 0x810c
#define AR_PHY_ERR_DCHIRP 0x00000008
#define AR_PHY_ERR_RADAR 0x00000020
#define AR_PHY_ERR_OFDM_TIMING 0x00020000
#define AR_PHY_ERR_CCK_TIMING 0x02000000
#define AR_RXFIFO_CFG 0x8114
#define AR_MIC_QOS_CONTROL 0x8118
#define AR_MIC_QOS_SELECT 0x811c
#define AR_PCU_MISC 0x8120
#define AR_PCU_FORCE_BSSID_MATCH 0x00000001
#define AR_PCU_MIC_NEW_LOC_ENA 0x00000004
#define AR_PCU_TX_ADD_TSF 0x00000008
#define AR_PCU_CCK_SIFS_MODE 0x00000010
#define AR_PCU_RX_ANT_UPDT 0x00000800
#define AR_PCU_TXOP_TBTT_LIMIT_ENA 0x00001000
#define AR_PCU_MISS_BCN_IN_SLEEP 0x00004000
#define AR_PCU_BUG_12306_FIX_ENA 0x00020000
#define AR_PCU_FORCE_QUIET_COLL 0x00040000
#define AR_PCU_TBTT_PROTECT 0x00200000
#define AR_PCU_CLEAR_VMF 0x01000000
#define AR_PCU_CLEAR_BA_VALID 0x04000000
#define AR_FILT_OFDM 0x8124
#define AR_FILT_OFDM_COUNT 0x00FFFFFF
#define AR_FILT_CCK 0x8128
#define AR_FILT_CCK_COUNT 0x00FFFFFF
#define AR_PHY_ERR_1 0x812c
#define AR_PHY_ERR_1_COUNT 0x00FFFFFF
#define AR_PHY_ERR_MASK_1 0x8130
#define AR_PHY_ERR_2 0x8134
#define AR_PHY_ERR_2_COUNT 0x00FFFFFF
#define AR_PHY_ERR_MASK_2 0x8138
#define AR_PHY_COUNTMAX (3 << 22)
#define AR_MIBCNT_INTRMASK (3 << 22)
#define AR_TSF_THRESHOLD 0x813c
#define AR_TSF_THRESHOLD_VAL 0x0000FFFF
#define AR_PHY_ERR_EIFS_MASK 8144
#define AR_PHY_ERR_3 0x8168
#define AR_PHY_ERR_3_COUNT 0x00FFFFFF
#define AR_PHY_ERR_MASK_3 0x816c
#define AR_TXSIFS 0x81d0
#define AR_TXSIFS_TIME 0x000000FF
#define AR_TXSIFS_TX_LATENCY 0x00000F00
#define AR_TXSIFS_TX_LATENCY_S 8
#define AR_TXSIFS_ACK_SHIFT 0x00007000
#define AR_TXSIFS_ACK_SHIFT_S 12
#define AR_TXOP_X 0x81ec
#define AR_TXOP_X_VAL 0x000000FF
#define AR_TXOP_0_3 0x81f0
#define AR_TXOP_4_7 0x81f4
#define AR_TXOP_8_11 0x81f8
#define AR_TXOP_12_15 0x81fc
#define AR_NEXT_TBTT_TIMER 0x8200
#define AR_NEXT_DMA_BEACON_ALERT 0x8204
#define AR_NEXT_SWBA 0x8208
#define AR_NEXT_CFP 0x8208
#define AR_NEXT_HCF 0x820C
#define AR_NEXT_TIM 0x8210
#define AR_NEXT_DTIM 0x8214
#define AR_NEXT_QUIET_TIMER 0x8218
#define AR_NEXT_NDP_TIMER 0x821C
#define AR_BEACON_PERIOD 0x8220
#define AR_DMA_BEACON_PERIOD 0x8224
#define AR_SWBA_PERIOD 0x8228
#define AR_HCF_PERIOD 0x822C
#define AR_TIM_PERIOD 0x8230
#define AR_DTIM_PERIOD 0x8234
#define AR_QUIET_PERIOD 0x8238
#define AR_NDP_PERIOD 0x823C
#define AR_TIMER_MODE 0x8240
#define AR_TBTT_TIMER_EN 0x00000001
#define AR_DBA_TIMER_EN 0x00000002
#define AR_SWBA_TIMER_EN 0x00000004
#define AR_HCF_TIMER_EN 0x00000008
#define AR_TIM_TIMER_EN 0x00000010
#define AR_DTIM_TIMER_EN 0x00000020
#define AR_QUIET_TIMER_EN 0x00000040
#define AR_NDP_TIMER_EN 0x00000080
#define AR_TIMER_OVERFLOW_INDEX 0x00000700
#define AR_TIMER_OVERFLOW_INDEX_S 8
#define AR_TIMER_THRESH 0xFFFFF000
#define AR_TIMER_THRESH_S 12
#define AR_SLP32_MODE 0x8244
#define AR_SLP32_HALF_CLK_LATENCY 0x000FFFFF
#define AR_SLP32_ENA 0x00100000
#define AR_SLP32_TSF_WRITE_STATUS 0x00200000
#define AR_SLP32_WAKE 0x8248
#define AR_SLP32_WAKE_XTL_TIME 0x0000FFFF
#define AR_SLP32_INC 0x824c
#define AR_SLP32_TST_INC 0x000FFFFF
#define AR_SLP_CNT 0x8250
#define AR_SLP_CYCLE_CNT 0x8254
#define AR_SLP_MIB_CTRL 0x8258
#define AR_SLP_MIB_CLEAR 0x00000001
#define AR_SLP_MIB_PENDING 0x00000002
#define AR_2040_MODE 0x8318
#define AR_2040_JOINED_RX_CLEAR 0x00000001
#define AR_EXTRCCNT 0x8328
#define AR_SELFGEN_MASK 0x832c
#define AR_PCU_TXBUF_CTRL 0x8340
#define AR_PCU_TXBUF_CTRL_SIZE_MASK 0x7FF
#define AR_PCU_TXBUF_CTRL_USABLE_SIZE 0x700
#define AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE 0x380
#define AR_KEYTABLE_0 0x8800
#define AR_KEYTABLE(_n) (AR_KEYTABLE_0 + ((_n)*32))
#define AR_KEY_CACHE_SIZE 128
#define AR_RSVD_KEYTABLE_ENTRIES 4
#define AR_KEY_TYPE 0x00000007
#define AR_KEYTABLE_TYPE_40 0x00000000
#define AR_KEYTABLE_TYPE_104 0x00000001
#define AR_KEYTABLE_TYPE_128 0x00000003
#define AR_KEYTABLE_TYPE_TKIP 0x00000004
#define AR_KEYTABLE_TYPE_AES 0x00000005
#define AR_KEYTABLE_TYPE_CCM 0x00000006
#define AR_KEYTABLE_TYPE_CLR 0x00000007
#define AR_KEYTABLE_ANT 0x00000008
#define AR_KEYTABLE_VALID 0x00008000
#define AR_KEYTABLE_KEY0(_n) (AR_KEYTABLE(_n) + 0)
#define AR_KEYTABLE_KEY1(_n) (AR_KEYTABLE(_n) + 4)
#define AR_KEYTABLE_KEY2(_n) (AR_KEYTABLE(_n) + 8)
#define AR_KEYTABLE_KEY3(_n) (AR_KEYTABLE(_n) + 12)
#define AR_KEYTABLE_KEY4(_n) (AR_KEYTABLE(_n) + 16)
#define AR_KEYTABLE_TYPE(_n) (AR_KEYTABLE(_n) + 20)
#define AR_KEYTABLE_MAC0(_n) (AR_KEYTABLE(_n) + 24)
#define AR_KEYTABLE_MAC1(_n) (AR_KEYTABLE(_n) + 28)
#endif
/*
* 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/kernel.h>
#include <linux/slab.h>
#include "core.h"
#include "hw.h"
#include "regd.h"
#include "regd_common.h"
static int ath9k_regd_chansort(const void *a, const void *b)
{
const struct ath9k_channel *ca = a;
const struct ath9k_channel *cb = b;
return (ca->channel == cb->channel) ?
(ca->channelFlags & CHAN_FLAGS) -
(cb->channelFlags & CHAN_FLAGS) : ca->channel - cb->channel;
}
static void
ath9k_regd_sort(void *a, u32 n, u32 size, ath_hal_cmp_t *cmp)
{
u8 *aa = a;
u8 *ai, *t;
for (ai = aa + size; --n >= 1; ai += size)
for (t = ai; t > aa; t -= size) {
u8 *u = t - size;
if (cmp(u, t) <= 0)
break;
swap(u, t, size);
}
}
static u16 ath9k_regd_get_eepromRD(struct ath_hal *ah)
{
return ah->ah_currentRD & ~WORLDWIDE_ROAMING_FLAG;
}
static bool ath9k_regd_is_chan_bm_zero(u64 *bitmask)
{
int i;
for (i = 0; i < BMLEN; i++) {
if (bitmask[i] != 0)
return false;
}
return true;
}
static bool ath9k_regd_is_eeprom_valid(struct ath_hal *ah)
{
u16 rd = ath9k_regd_get_eepromRD(ah);
int i;
if (rd & COUNTRY_ERD_FLAG) {
u16 cc = rd & ~COUNTRY_ERD_FLAG;
for (i = 0; i < ARRAY_SIZE(allCountries); i++)
if (allCountries[i].countryCode == cc)
return true;
} else {
for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
if (regDomainPairs[i].regDmnEnum == rd)
return true;
}
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: invalid regulatory domain/country code 0x%x\n",
__func__, rd);
return false;
}
static bool ath9k_regd_is_fcc_midband_supported(struct ath_hal *ah)
{
u32 regcap;
regcap = ah->ah_caps.reg_cap;
if (regcap & AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND)
return true;
else
return false;
}
static bool ath9k_regd_is_ccode_valid(struct ath_hal *ah,
u16 cc)
{
u16 rd;
int i;
if (cc == CTRY_DEFAULT)
return true;
if (cc == CTRY_DEBUG)
return true;
rd = ath9k_regd_get_eepromRD(ah);
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: EEPROM regdomain 0x%x\n",
__func__, rd);
if (rd & COUNTRY_ERD_FLAG) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: EEPROM setting is country code %u\n",
__func__, rd & ~COUNTRY_ERD_FLAG);
return cc == (rd & ~COUNTRY_ERD_FLAG);
}
for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
if (cc == allCountries[i].countryCode) {
#ifdef AH_SUPPORT_11D
if ((rd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)
return true;
#endif
if (allCountries[i].regDmnEnum == rd ||
rd == DEBUG_REG_DMN || rd == NO_ENUMRD)
return true;
}
}
return false;
}
static void
ath9k_regd_get_wmodes_nreg(struct ath_hal *ah,
struct country_code_to_enum_rd *country,
struct regDomain *rd5GHz,
unsigned long *modes_allowed)
{
bitmap_copy(modes_allowed, ah->ah_caps.wireless_modes, ATH9K_MODE_MAX);
if (test_bit(ATH9K_MODE_11G, ah->ah_caps.wireless_modes) &&
(!country->allow11g))
clear_bit(ATH9K_MODE_11G, modes_allowed);
if (test_bit(ATH9K_MODE_11A, ah->ah_caps.wireless_modes) &&
(ath9k_regd_is_chan_bm_zero(rd5GHz->chan11a)))
clear_bit(ATH9K_MODE_11A, modes_allowed);
if (test_bit(ATH9K_MODE_11NG_HT20, ah->ah_caps.wireless_modes)
&& (!country->allow11ng20))
clear_bit(ATH9K_MODE_11NG_HT20, modes_allowed);
if (test_bit(ATH9K_MODE_11NA_HT20, ah->ah_caps.wireless_modes)
&& (!country->allow11na20))
clear_bit(ATH9K_MODE_11NA_HT20, modes_allowed);
if (test_bit(ATH9K_MODE_11NG_HT40PLUS, ah->ah_caps.wireless_modes) &&
(!country->allow11ng40))
clear_bit(ATH9K_MODE_11NG_HT40PLUS, modes_allowed);
if (test_bit(ATH9K_MODE_11NG_HT40MINUS, ah->ah_caps.wireless_modes) &&
(!country->allow11ng40))
clear_bit(ATH9K_MODE_11NG_HT40MINUS, modes_allowed);
if (test_bit(ATH9K_MODE_11NA_HT40PLUS, ah->ah_caps.wireless_modes) &&
(!country->allow11na40))
clear_bit(ATH9K_MODE_11NA_HT40PLUS, modes_allowed);
if (test_bit(ATH9K_MODE_11NA_HT40MINUS, ah->ah_caps.wireless_modes) &&
(!country->allow11na40))
clear_bit(ATH9K_MODE_11NA_HT40MINUS, modes_allowed);
}
bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah)
{
u16 rd;
rd = ath9k_regd_get_eepromRD(ah);
switch (rd) {
case FCC4_FCCA:
case (CTRY_UNITED_STATES_FCC49 | COUNTRY_ERD_FLAG):
return true;
case DEBUG_REG_DMN:
case NO_ENUMRD:
if (ah->ah_countryCode == CTRY_UNITED_STATES_FCC49)
return true;
break;
}
return false;
}
static struct country_code_to_enum_rd*
ath9k_regd_find_country(u16 countryCode)
{
int i;
for (i = 0; i < ARRAY_SIZE(allCountries); i++) {
if (allCountries[i].countryCode == countryCode)
return &allCountries[i];
}
return NULL;
}
static u16 ath9k_regd_get_default_country(struct ath_hal *ah)
{
u16 rd;
int i;
rd = ath9k_regd_get_eepromRD(ah);
if (rd & COUNTRY_ERD_FLAG) {
struct country_code_to_enum_rd *country = NULL;
u16 cc = rd & ~COUNTRY_ERD_FLAG;
country = ath9k_regd_find_country(cc);
if (country != NULL)
return cc;
}
for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
if (regDomainPairs[i].regDmnEnum == rd) {
if (regDomainPairs[i].singleCC != 0)
return regDomainPairs[i].singleCC;
else
i = ARRAY_SIZE(regDomainPairs);
}
return CTRY_DEFAULT;
}
static bool ath9k_regd_is_valid_reg_domain(int regDmn,
struct regDomain *rd)
{
int i;
for (i = 0; i < ARRAY_SIZE(regDomains); i++) {
if (regDomains[i].regDmnEnum == regDmn) {
if (rd != NULL) {
memcpy(rd, &regDomains[i],
sizeof(struct regDomain));
}
return true;
}
}
return false;
}
static bool ath9k_regd_is_valid_reg_domainPair(int regDmnPair)
{
int i;
if (regDmnPair == NO_ENUMRD)
return false;
for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
if (regDomainPairs[i].regDmnEnum == regDmnPair)
return true;
}
return false;
}
static bool
ath9k_regd_get_wmode_regdomain(struct ath_hal *ah, int regDmn,
u16 channelFlag, struct regDomain *rd)
{
int i, found;
u64 flags = NO_REQ;
struct reg_dmn_pair_mapping *regPair = NULL;
int regOrg;
regOrg = regDmn;
if (regDmn == CTRY_DEFAULT) {
u16 rdnum;
rdnum = ath9k_regd_get_eepromRD(ah);
if (!(rdnum & COUNTRY_ERD_FLAG)) {
if (ath9k_regd_is_valid_reg_domain(rdnum, NULL) ||
ath9k_regd_is_valid_reg_domainPair(rdnum)) {
regDmn = rdnum;
}
}
}
if ((regDmn & MULTI_DOMAIN_MASK) == 0) {
for (i = 0, found = 0;
(i < ARRAY_SIZE(regDomainPairs)) && (!found); i++) {
if (regDomainPairs[i].regDmnEnum == regDmn) {
regPair = &regDomainPairs[i];
found = 1;
}
}
if (!found) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: Failed to find reg domain pair %u\n",
__func__, regDmn);
return false;
}
if (!(channelFlag & CHANNEL_2GHZ)) {
regDmn = regPair->regDmn5GHz;
flags = regPair->flags5GHz;
}
if (channelFlag & CHANNEL_2GHZ) {
regDmn = regPair->regDmn2GHz;
flags = regPair->flags2GHz;
}
}
found = ath9k_regd_is_valid_reg_domain(regDmn, rd);
if (!found) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: Failed to find unitary reg domain %u\n",
__func__, regDmn);
return false;
} else {
rd->pscan &= regPair->pscanMask;
if (((regOrg & MULTI_DOMAIN_MASK) == 0) &&
(flags != NO_REQ)) {
rd->flags = flags;
}
rd->flags &= (channelFlag & CHANNEL_2GHZ) ?
REG_DOMAIN_2GHZ_MASK : REG_DOMAIN_5GHZ_MASK;
return true;
}
}
static bool ath9k_regd_is_bit_set(int bit, u64 *bitmask)
{
int byteOffset, bitnum;
u64 val;
byteOffset = bit / 64;
bitnum = bit - byteOffset * 64;
val = ((u64) 1) << bitnum;
if (bitmask[byteOffset] & val)
return true;
else
return false;
}
static void
ath9k_regd_add_reg_classid(u8 *regclassids, u32 maxregids,
u32 *nregids, u8 regclassid)
{
int i;
if (regclassid == 0)
return;
for (i = 0; i < maxregids; i++) {
if (regclassids[i] == regclassid)
return;
if (regclassids[i] == 0)
break;
}
if (i == maxregids)
return;
else {
regclassids[i] = regclassid;
*nregids += 1;
}
return;
}
static bool
ath9k_regd_get_eeprom_reg_ext_bits(struct ath_hal *ah,
enum reg_ext_bitmap bit)
{
return (ah->ah_currentRDExt & (1 << bit)) ? true : false;
}
#ifdef ATH_NF_PER_CHAN
static void ath9k_regd_init_rf_buffer(struct ath9k_channel *ichans,
int nchans)
{
int i, j, next;
for (next = 0; next < nchans; next++) {
for (i = 0; i < NUM_NF_READINGS; i++) {
ichans[next].nfCalHist[i].currIndex = 0;
ichans[next].nfCalHist[i].privNF =
AR_PHY_CCA_MAX_GOOD_VALUE;
ichans[next].nfCalHist[i].invalidNFcount =
AR_PHY_CCA_FILTERWINDOW_LENGTH;
for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
ichans[next].nfCalHist[i].nfCalBuffer[j] =
AR_PHY_CCA_MAX_GOOD_VALUE;
}
}
}
}
#endif
static int ath9k_regd_is_chan_present(struct ath_hal *ah,
u16 c)
{
int i;
for (i = 0; i < 150; i++) {
if (!ah->ah_channels[i].channel)
return -1;
else if (ah->ah_channels[i].channel == c)
return i;
}
return -1;
}
static bool
ath9k_regd_add_channel(struct ath_hal *ah,
u16 c,
u16 c_lo,
u16 c_hi,
u16 maxChan,
u8 ctl,
int pos,
struct regDomain rd5GHz,
struct RegDmnFreqBand *fband,
struct regDomain *rd,
const struct cmode *cm,
struct ath9k_channel *ichans,
bool enableExtendedChannels)
{
struct ath9k_channel *chan;
int ret;
u32 channelFlags = 0;
u8 privFlags = 0;
if (!(c_lo <= c && c <= c_hi)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: c %u out of range [%u..%u]\n",
__func__, c, c_lo, c_hi);
return false;
}
if ((fband->channelBW == CHANNEL_HALF_BW) &&
!(ah->ah_caps.hw_caps & ATH9K_HW_CAP_CHAN_HALFRATE)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: Skipping %u half rate channel\n",
__func__, c);
return false;
}
if ((fband->channelBW == CHANNEL_QUARTER_BW) &&
!(ah->ah_caps.hw_caps & ATH9K_HW_CAP_CHAN_QUARTERRATE)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: Skipping %u quarter rate channel\n",
__func__, c);
return false;
}
if (((c + fband->channelSep) / 2) > (maxChan + HALF_MAXCHANBW)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: c %u > maxChan %u\n",
__func__, c, maxChan);
return false;
}
if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"Skipping ecm channel\n");
return false;
}
if ((rd->flags & NO_HOSTAP) && (ah->ah_opmode == ATH9K_M_HOSTAP)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"Skipping HOSTAP channel\n");
return false;
}
if (IS_HT40_MODE(cm->mode) &&
!(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_FCC_DFS_HT40)) &&
(fband->useDfs) &&
(rd->conformanceTestLimit != MKK)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"Skipping HT40 channel (en_fcc_dfs_ht40 = 0)\n");
return false;
}
if (IS_HT40_MODE(cm->mode) &&
!(ath9k_regd_get_eeprom_reg_ext_bits(ah,
REG_EXT_JAPAN_NONDFS_HT40)) &&
!(fband->useDfs) && (rd->conformanceTestLimit == MKK)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"Skipping HT40 channel (en_jap_ht40 = 0)\n");
return false;
}
if (IS_HT40_MODE(cm->mode) &&
!(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_JAPAN_DFS_HT40)) &&
(fband->useDfs) &&
(rd->conformanceTestLimit == MKK)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"Skipping HT40 channel (en_jap_dfs_ht40 = 0)\n");
return false;
}
/* Calculate channel flags */
channelFlags = cm->flags;
switch (fband->channelBW) {
case CHANNEL_HALF_BW:
channelFlags |= CHANNEL_HALF;
break;
case CHANNEL_QUARTER_BW:
channelFlags |= CHANNEL_QUARTER;
break;
}
if (fband->usePassScan & rd->pscan)
channelFlags |= CHANNEL_PASSIVE;
else
channelFlags &= ~CHANNEL_PASSIVE;
if (fband->useDfs & rd->dfsMask)
privFlags = CHANNEL_DFS;
else
privFlags = 0;
if (rd->flags & LIMIT_FRAME_4MS)
privFlags |= CHANNEL_4MS_LIMIT;
if (privFlags & CHANNEL_DFS)
privFlags |= CHANNEL_DISALLOW_ADHOC;
if (rd->flags & ADHOC_PER_11D)
privFlags |= CHANNEL_PER_11D_ADHOC;
if (channelFlags & CHANNEL_PASSIVE) {
if ((c < 2412) || (c > 2462)) {
if (rd5GHz.regDmnEnum == MKK1 ||
rd5GHz.regDmnEnum == MKK2) {
u32 regcap = ah->ah_caps.reg_cap;
if (!(regcap &
(AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN |
AR_EEPROM_EEREGCAP_EN_KK_U2 |
AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) &&
isUNII1OddChan(c)) {
channelFlags &= ~CHANNEL_PASSIVE;
} else {
privFlags |= CHANNEL_DISALLOW_ADHOC;
}
} else {
privFlags |= CHANNEL_DISALLOW_ADHOC;
}
}
}
if ((cm->mode == ATH9K_MODE_11A) ||
(cm->mode == ATH9K_MODE_11NA_HT20) ||
(cm->mode == ATH9K_MODE_11NA_HT40PLUS) ||
(cm->mode == ATH9K_MODE_11NA_HT40MINUS)) {
if (rd->flags & (ADHOC_NO_11A | DISALLOW_ADHOC_11A))
privFlags |= CHANNEL_DISALLOW_ADHOC;
}
/* Fill in channel details */
ret = ath9k_regd_is_chan_present(ah, c);
if (ret == -1) {
chan = &ah->ah_channels[pos];
chan->channel = c;
chan->maxRegTxPower = fband->powerDfs;
chan->antennaMax = fband->antennaMax;
chan->regDmnFlags = rd->flags;
chan->maxTxPower = AR5416_MAX_RATE_POWER;
chan->minTxPower = AR5416_MAX_RATE_POWER;
chan->channelFlags = channelFlags;
chan->privFlags = privFlags;
} else {
chan = &ah->ah_channels[ret];
chan->channelFlags |= channelFlags;
chan->privFlags |= privFlags;
}
/* Set CTLs */
if ((cm->flags & CHANNEL_ALL) == CHANNEL_A)
chan->conformanceTestLimit[0] = ctl;
else if ((cm->flags & CHANNEL_ALL) == CHANNEL_B)
chan->conformanceTestLimit[1] = ctl;
else if ((cm->flags & CHANNEL_ALL) == CHANNEL_G)
chan->conformanceTestLimit[2] = ctl;
return (ret == -1) ? true : false;
}
static bool ath9k_regd_japan_check(struct ath_hal *ah,
int b,
struct regDomain *rd5GHz)
{
bool skipband = false;
int i;
u32 regcap;
for (i = 0; i < ARRAY_SIZE(j_bandcheck); i++) {
if (j_bandcheck[i].freqbandbit == b) {
regcap = ah->ah_caps.reg_cap;
if ((j_bandcheck[i].eepromflagtocheck & regcap) == 0) {
skipband = true;
} else if ((regcap & AR_EEPROM_EEREGCAP_EN_KK_U2) ||
(regcap & AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) {
rd5GHz->dfsMask |= DFS_MKK4;
rd5GHz->pscan |= PSCAN_MKK3;
}
break;
}
}
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: Skipping %d freq band\n",
__func__, j_bandcheck[i].freqbandbit);
return skipband;
}
bool
ath9k_regd_init_channels(struct ath_hal *ah,
u32 maxchans,
u32 *nchans, u8 *regclassids,
u32 maxregids, u32 *nregids, u16 cc,
bool enableOutdoor,
bool enableExtendedChannels)
{
u16 maxChan = 7000;
struct country_code_to_enum_rd *country = NULL;
struct regDomain rd5GHz, rd2GHz;
const struct cmode *cm;
struct ath9k_channel *ichans = &ah->ah_channels[0];
int next = 0, b;
u8 ctl;
int regdmn;
u16 chanSep;
unsigned long *modes_avail;
DECLARE_BITMAP(modes_allowed, ATH9K_MODE_MAX);
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: cc %u %s %s\n",
__func__, cc,
enableOutdoor ? "Enable outdoor" : "",
enableExtendedChannels ? "Enable ecm" : "");
if (!ath9k_regd_is_ccode_valid(ah, cc)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: invalid country code %d\n", __func__, cc);
return false;
}
if (!ath9k_regd_is_eeprom_valid(ah)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: invalid EEPROM contents\n", __func__);
return false;
}
ah->ah_countryCode = ath9k_regd_get_default_country(ah);
if (ah->ah_countryCode == CTRY_DEFAULT) {
ah->ah_countryCode = cc & COUNTRY_CODE_MASK;
if ((ah->ah_countryCode == CTRY_DEFAULT) &&
(ath9k_regd_get_eepromRD(ah) == CTRY_DEFAULT)) {
ah->ah_countryCode = CTRY_UNITED_STATES;
}
}
#ifdef AH_SUPPORT_11D
if (ah->ah_countryCode == CTRY_DEFAULT) {
regdmn = ath9k_regd_get_eepromRD(ah);
country = NULL;
} else {
#endif
country = ath9k_regd_find_country(ah->ah_countryCode);
if (country == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"Country is NULL!!!!, cc= %d\n",
ah->ah_countryCode);
return false;
} else {
regdmn = country->regDmnEnum;
#ifdef AH_SUPPORT_11D
if (((ath9k_regd_get_eepromRD(ah) &
WORLD_SKU_MASK) == WORLD_SKU_PREFIX) &&
(cc == CTRY_UNITED_STATES)) {
if (!isWwrSKU_NoMidband(ah)
&& ath9k_regd_is_fcc_midband_supported(ah))
regdmn = FCC3_FCCA;
else
regdmn = FCC1_FCCA;
}
#endif
}
#ifdef AH_SUPPORT_11D
}
#endif
if (!ath9k_regd_get_wmode_regdomain(ah,
regdmn,
~CHANNEL_2GHZ,
&rd5GHz)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: couldn't find unitary "
"5GHz reg domain for country %u\n",
__func__, ah->ah_countryCode);
return false;
}
if (!ath9k_regd_get_wmode_regdomain(ah,
regdmn,
CHANNEL_2GHZ,
&rd2GHz)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: couldn't find unitary 2GHz "
"reg domain for country %u\n",
__func__, ah->ah_countryCode);
return false;
}
if (!isWwrSKU(ah) && ((rd5GHz.regDmnEnum == FCC1) ||
(rd5GHz.regDmnEnum == FCC2))) {
if (ath9k_regd_is_fcc_midband_supported(ah)) {
if (!ath9k_regd_get_wmode_regdomain(ah,
FCC3_FCCA,
~CHANNEL_2GHZ,
&rd5GHz)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: couldn't find unitary 5GHz "
"reg domain for country %u\n",
__func__, ah->ah_countryCode);
return false;
}
}
}
if (country == NULL) {
modes_avail = ah->ah_caps.wireless_modes;
} else {
ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz, modes_allowed);
modes_avail = modes_allowed;
if (!enableOutdoor)
maxChan = country->outdoorChanStart;
}
next = 0;
if (maxchans > ARRAY_SIZE(ah->ah_channels))
maxchans = ARRAY_SIZE(ah->ah_channels);
for (cm = modes; cm < &modes[ARRAY_SIZE(modes)]; cm++) {
u16 c, c_hi, c_lo;
u64 *channelBM = NULL;
struct regDomain *rd = NULL;
struct RegDmnFreqBand *fband = NULL, *freqs;
int8_t low_adj = 0, hi_adj = 0;
if (!test_bit(cm->mode, modes_avail)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: !avail mode %d flags 0x%x\n",
__func__, cm->mode, cm->flags);
continue;
}
if (!ath9k_get_channel_edges(ah, cm->flags, &c_lo, &c_hi)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: channels 0x%x not supported "
"by hardware\n",
__func__, cm->flags);
continue;
}
switch (cm->mode) {
case ATH9K_MODE_11A:
case ATH9K_MODE_11NA_HT20:
case ATH9K_MODE_11NA_HT40PLUS:
case ATH9K_MODE_11NA_HT40MINUS:
rd = &rd5GHz;
channelBM = rd->chan11a;
freqs = &regDmn5GhzFreq[0];
ctl = rd->conformanceTestLimit;
break;
case ATH9K_MODE_11B:
rd = &rd2GHz;
channelBM = rd->chan11b;
freqs = &regDmn2GhzFreq[0];
ctl = rd->conformanceTestLimit | CTL_11B;
break;
case ATH9K_MODE_11G:
case ATH9K_MODE_11NG_HT20:
case ATH9K_MODE_11NG_HT40PLUS:
case ATH9K_MODE_11NG_HT40MINUS:
rd = &rd2GHz;
channelBM = rd->chan11g;
freqs = &regDmn2Ghz11gFreq[0];
ctl = rd->conformanceTestLimit | CTL_11G;
break;
default:
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: Unknown HAL mode 0x%x\n", __func__,
cm->mode);
continue;
}
if (ath9k_regd_is_chan_bm_zero(channelBM))
continue;
if ((cm->mode == ATH9K_MODE_11NA_HT40PLUS) ||
(cm->mode == ATH9K_MODE_11NG_HT40PLUS)) {
hi_adj = -20;
}
if ((cm->mode == ATH9K_MODE_11NA_HT40MINUS) ||
(cm->mode == ATH9K_MODE_11NG_HT40MINUS)) {
low_adj = 20;
}
/* XXX: Add a helper here instead */
for (b = 0; b < 64 * BMLEN; b++) {
if (ath9k_regd_is_bit_set(b, channelBM)) {
fband = &freqs[b];
if (rd5GHz.regDmnEnum == MKK1
|| rd5GHz.regDmnEnum == MKK2) {
if (ath9k_regd_japan_check(ah,
b,
&rd5GHz))
continue;
}
ath9k_regd_add_reg_classid(regclassids,
maxregids,
nregids,
fband->
regClassId);
if (IS_HT40_MODE(cm->mode) && (rd == &rd5GHz)) {
chanSep = 40;
if (fband->lowChannel == 5280)
low_adj += 20;
if (fband->lowChannel == 5170)
continue;
} else
chanSep = fband->channelSep;
for (c = fband->lowChannel + low_adj;
((c <= (fband->highChannel + hi_adj)) &&
(c >= (fband->lowChannel + low_adj)));
c += chanSep) {
if (next >= maxchans) {
DPRINTF(ah->ah_sc,
ATH_DBG_REGULATORY,
"%s: too many channels "
"for channel table\n",
__func__);
goto done;
}
if (ath9k_regd_add_channel(ah,
c, c_lo, c_hi,
maxChan, ctl,
next,
rd5GHz,
fband, rd, cm,
ichans,
enableExtendedChannels))
next++;
}
if (IS_HT40_MODE(cm->mode) &&
(fband->lowChannel == 5280)) {
low_adj -= 20;
}
}
}
}
done:
if (next != 0) {
int i;
if (next > ARRAY_SIZE(ah->ah_channels)) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: too many channels %u; truncating to %u\n",
__func__, next,
(int) ARRAY_SIZE(ah->ah_channels));
next = ARRAY_SIZE(ah->ah_channels);
}
#ifdef ATH_NF_PER_CHAN
ath9k_regd_init_rf_buffer(ichans, next);
#endif
ath9k_regd_sort(ichans, next,
sizeof(struct ath9k_channel),
ath9k_regd_chansort);
ah->ah_nchan = next;
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "Channel list:\n");
for (i = 0; i < next; i++) {
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"chan: %d flags: 0x%x\n",
ah->ah_channels[i].channel,
ah->ah_channels[i].channelFlags);
}
}
*nchans = next;
ah->ah_countryCode = ah->ah_countryCode;
ah->ah_currentRDInUse = regdmn;
ah->ah_currentRD5G = rd5GHz.regDmnEnum;
ah->ah_currentRD2G = rd2GHz.regDmnEnum;
if (country == NULL) {
ah->ah_iso[0] = 0;
ah->ah_iso[1] = 0;
} else {
ah->ah_iso[0] = country->isoName[0];
ah->ah_iso[1] = country->isoName[1];
}
return next != 0;
}
struct ath9k_channel*
ath9k_regd_check_channel(struct ath_hal *ah,
const struct ath9k_channel *c)
{
struct ath9k_channel *base, *cc;
int flags = c->channelFlags & CHAN_FLAGS;
int n, lim;
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: channel %u/0x%x (0x%x) requested\n", __func__,
c->channel, c->channelFlags, flags);
cc = ah->ah_curchan;
if (cc != NULL && cc->channel == c->channel &&
(cc->channelFlags & CHAN_FLAGS) == flags) {
if ((cc->privFlags & CHANNEL_INTERFERENCE) &&
(cc->privFlags & CHANNEL_DFS))
return NULL;
else
return cc;
}
base = ah->ah_channels;
n = ah->ah_nchan;
for (lim = n; lim != 0; lim >>= 1) {
int d;
cc = &base[lim >> 1];
d = c->channel - cc->channel;
if (d == 0) {
if ((cc->channelFlags & CHAN_FLAGS) == flags) {
if ((cc->privFlags & CHANNEL_INTERFERENCE) &&
(cc->privFlags & CHANNEL_DFS))
return NULL;
else
return cc;
}
d = flags - (cc->channelFlags & CHAN_FLAGS);
}
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY,
"%s: channel %u/0x%x d %d\n", __func__,
cc->channel, cc->channelFlags, d);
if (d > 0) {
base = cc + 1;
lim--;
}
}
DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: no match for %u/0x%x\n",
__func__, c->channel, c->channelFlags);
return NULL;
}
u32
ath9k_regd_get_antenna_allowed(struct ath_hal *ah,
struct ath9k_channel *chan)
{
struct ath9k_channel *ichan = NULL;
ichan = ath9k_regd_check_channel(ah, chan);
if (!ichan)
return 0;
return ichan->antennaMax;
}
u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan)
{
u32 ctl = NO_CTL;
struct ath9k_channel *ichan;
if (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)) {
if (IS_CHAN_B(chan))
ctl = SD_NO_CTL | CTL_11B;
else if (IS_CHAN_G(chan))
ctl = SD_NO_CTL | CTL_11G;
else
ctl = SD_NO_CTL | CTL_11A;
} else {
ichan = ath9k_regd_check_channel(ah, chan);
if (ichan != NULL) {
/* FIXME */
if (IS_CHAN_A(ichan))
ctl = ichan->conformanceTestLimit[0];
else if (IS_CHAN_B(ichan))
ctl = ichan->conformanceTestLimit[1];
else if (IS_CHAN_G(ichan))
ctl = ichan->conformanceTestLimit[2];
if (IS_CHAN_G(chan) && (ctl & 0xf) == CTL_11B)
ctl = (ctl & ~0xf) | CTL_11G;
}
}
return ctl;
}
void ath9k_regd_get_current_country(struct ath_hal *ah,
struct ath9k_country_entry *ctry)
{
u16 rd = ath9k_regd_get_eepromRD(ah);
ctry->isMultidomain = false;
if (rd == CTRY_DEFAULT)
ctry->isMultidomain = true;
else if (!(rd & COUNTRY_ERD_FLAG))
ctry->isMultidomain = isWwrSKU(ah);
ctry->countryCode = ah->ah_countryCode;
ctry->regDmnEnum = ah->ah_currentRD;
ctry->regDmn5G = ah->ah_currentRD5G;
ctry->regDmn2G = ah->ah_currentRD2G;
ctry->iso[0] = ah->ah_iso[0];
ctry->iso[1] = ah->ah_iso[1];
ctry->iso[2] = ah->ah_iso[2];
}
/*
* 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 REGD_H
#define REGD_H
#include "ath9k.h"
#define BMLEN 2
#define BMZERO {(u64) 0, (u64) 0}
#define BM(_fa, _fb, _fc, _fd, _fe, _ff, _fg, _fh, _fi, _fj, _fk, _fl) \
{((((_fa >= 0) && (_fa < 64)) ? \
(((u64) 1) << _fa) : (u64) 0) | \
(((_fb >= 0) && (_fb < 64)) ? \
(((u64) 1) << _fb) : (u64) 0) | \
(((_fc >= 0) && (_fc < 64)) ? \
(((u64) 1) << _fc) : (u64) 0) | \
(((_fd >= 0) && (_fd < 64)) ? \
(((u64) 1) << _fd) : (u64) 0) | \
(((_fe >= 0) && (_fe < 64)) ? \
(((u64) 1) << _fe) : (u64) 0) | \
(((_ff >= 0) && (_ff < 64)) ? \
(((u64) 1) << _ff) : (u64) 0) | \
(((_fg >= 0) && (_fg < 64)) ? \
(((u64) 1) << _fg) : (u64) 0) | \
(((_fh >= 0) && (_fh < 64)) ? \
(((u64) 1) << _fh) : (u64) 0) | \
(((_fi >= 0) && (_fi < 64)) ? \
(((u64) 1) << _fi) : (u64) 0) | \
(((_fj >= 0) && (_fj < 64)) ? \
(((u64) 1) << _fj) : (u64) 0) | \
(((_fk >= 0) && (_fk < 64)) ? \
(((u64) 1) << _fk) : (u64) 0) | \
(((_fl >= 0) && (_fl < 64)) ? \
(((u64) 1) << _fl) : (u64) 0) | \
((((_fa > 63) && (_fa < 128)) ? \
(((u64) 1) << (_fa - 64)) : (u64) 0) | \
(((_fb > 63) && (_fb < 128)) ? \
(((u64) 1) << (_fb - 64)) : (u64) 0) | \
(((_fc > 63) && (_fc < 128)) ? \
(((u64) 1) << (_fc - 64)) : (u64) 0) | \
(((_fd > 63) && (_fd < 128)) ? \
(((u64) 1) << (_fd - 64)) : (u64) 0) | \
(((_fe > 63) && (_fe < 128)) ? \
(((u64) 1) << (_fe - 64)) : (u64) 0) | \
(((_ff > 63) && (_ff < 128)) ? \
(((u64) 1) << (_ff - 64)) : (u64) 0) | \
(((_fg > 63) && (_fg < 128)) ? \
(((u64) 1) << (_fg - 64)) : (u64) 0) | \
(((_fh > 63) && (_fh < 128)) ? \
(((u64) 1) << (_fh - 64)) : (u64) 0) | \
(((_fi > 63) && (_fi < 128)) ? \
(((u64) 1) << (_fi - 64)) : (u64) 0) | \
(((_fj > 63) && (_fj < 128)) ? \
(((u64) 1) << (_fj - 64)) : (u64) 0) | \
(((_fk > 63) && (_fk < 128)) ? \
(((u64) 1) << (_fk - 64)) : (u64) 0) | \
(((_fl > 63) && (_fl < 128)) ? \
(((u64) 1) << (_fl - 64)) : (u64) 0)))}
#define DEF_REGDMN FCC1_FCCA
#define DEF_DMN_5 FCC1
#define DEF_DMN_2 FCCA
#define COUNTRY_ERD_FLAG 0x8000
#define WORLDWIDE_ROAMING_FLAG 0x4000
#define SUPER_DOMAIN_MASK 0x0fff
#define COUNTRY_CODE_MASK 0x3fff
#define CF_INTERFERENCE (CHANNEL_CW_INT | CHANNEL_RADAR_INT)
#define CHANNEL_14 (2484)
#define IS_11G_CH14(_ch,_cf) \
(((_ch) == CHANNEL_14) && ((_cf) == CHANNEL_G))
#define NO_PSCAN 0x0ULL
#define PSCAN_FCC 0x0000000000000001ULL
#define PSCAN_FCC_T 0x0000000000000002ULL
#define PSCAN_ETSI 0x0000000000000004ULL
#define PSCAN_MKK1 0x0000000000000008ULL
#define PSCAN_MKK2 0x0000000000000010ULL
#define PSCAN_MKKA 0x0000000000000020ULL
#define PSCAN_MKKA_G 0x0000000000000040ULL
#define PSCAN_ETSIA 0x0000000000000080ULL
#define PSCAN_ETSIB 0x0000000000000100ULL
#define PSCAN_ETSIC 0x0000000000000200ULL
#define PSCAN_WWR 0x0000000000000400ULL
#define PSCAN_MKKA1 0x0000000000000800ULL
#define PSCAN_MKKA1_G 0x0000000000001000ULL
#define PSCAN_MKKA2 0x0000000000002000ULL
#define PSCAN_MKKA2_G 0x0000000000004000ULL
#define PSCAN_MKK3 0x0000000000008000ULL
#define PSCAN_DEFER 0x7FFFFFFFFFFFFFFFULL
#define IS_ECM_CHAN 0x8000000000000000ULL
#define isWwrSKU(_ah) \
(((ath9k_regd_get_eepromRD((_ah)) & WORLD_SKU_MASK) == \
WORLD_SKU_PREFIX) || \
(ath9k_regd_get_eepromRD(_ah) == WORLD))
#define isWwrSKU_NoMidband(_ah) \
((ath9k_regd_get_eepromRD((_ah)) == WOR3_WORLD) || \
(ath9k_regd_get_eepromRD(_ah) == WOR4_WORLD) || \
(ath9k_regd_get_eepromRD(_ah) == WOR5_ETSIC))
#define isUNII1OddChan(ch) \
((ch == 5170) || (ch == 5190) || (ch == 5210) || (ch == 5230))
#define IS_HT40_MODE(_mode) \
(((_mode == ATH9K_MODE_11NA_HT40PLUS || \
_mode == ATH9K_MODE_11NG_HT40PLUS || \
_mode == ATH9K_MODE_11NA_HT40MINUS || \
_mode == ATH9K_MODE_11NG_HT40MINUS) ? true : false))
#define CHAN_FLAGS (CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER)
#define swap(_a, _b, _size) { \
u8 *s = _b; \
int i = _size; \
do { \
u8 tmp = *_a; \
*_a++ = *s; \
*s++ = tmp; \
} while (--i); \
_a -= _size; \
}
#define HALF_MAXCHANBW 10
#define MULTI_DOMAIN_MASK 0xFF00
#define WORLD_SKU_MASK 0x00F0
#define WORLD_SKU_PREFIX 0x0060
#define CHANNEL_HALF_BW 10
#define CHANNEL_QUARTER_BW 5
typedef int ath_hal_cmp_t(const void *, const void *);
struct reg_dmn_pair_mapping {
u16 regDmnEnum;
u16 regDmn5GHz;
u16 regDmn2GHz;
u32 flags5GHz;
u32 flags2GHz;
u64 pscanMask;
u16 singleCC;
};
struct ccmap {
char isoName[3];
u16 countryCode;
};
struct country_code_to_enum_rd {
u16 countryCode;
u16 regDmnEnum;
const char *isoName;
const char *name;
bool allow11g;
bool allow11aTurbo;
bool allow11gTurbo;
bool allow11ng20;
bool allow11ng40;
bool allow11na20;
bool allow11na40;
u16 outdoorChanStart;
};
struct RegDmnFreqBand {
u16 lowChannel;
u16 highChannel;
u8 powerDfs;
u8 antennaMax;
u8 channelBW;
u8 channelSep;
u64 useDfs;
u64 usePassScan;
u8 regClassId;
};
struct regDomain {
u16 regDmnEnum;
u8 conformanceTestLimit;
u64 dfsMask;
u64 pscan;
u32 flags;
u64 chan11a[BMLEN];
u64 chan11a_turbo[BMLEN];
u64 chan11a_dyn_turbo[BMLEN];
u64 chan11b[BMLEN];
u64 chan11g[BMLEN];
u64 chan11g_turbo[BMLEN];
};
struct cmode {
u32 mode;
u32 flags;
};
#define YES true
#define NO false
struct japan_bandcheck {
u16 freqbandbit;
u32 eepromflagtocheck;
};
struct common_mode_power {
u16 lchan;
u16 hchan;
u8 pwrlvl;
};
enum CountryCode {
CTRY_ALBANIA = 8,
CTRY_ALGERIA = 12,
CTRY_ARGENTINA = 32,
CTRY_ARMENIA = 51,
CTRY_AUSTRALIA = 36,
CTRY_AUSTRIA = 40,
CTRY_AZERBAIJAN = 31,
CTRY_BAHRAIN = 48,
CTRY_BELARUS = 112,
CTRY_BELGIUM = 56,
CTRY_BELIZE = 84,
CTRY_BOLIVIA = 68,
CTRY_BOSNIA_HERZ = 70,
CTRY_BRAZIL = 76,
CTRY_BRUNEI_DARUSSALAM = 96,
CTRY_BULGARIA = 100,
CTRY_CANADA = 124,
CTRY_CHILE = 152,
CTRY_CHINA = 156,
CTRY_COLOMBIA = 170,
CTRY_COSTA_RICA = 188,
CTRY_CROATIA = 191,
CTRY_CYPRUS = 196,
CTRY_CZECH = 203,
CTRY_DENMARK = 208,
CTRY_DOMINICAN_REPUBLIC = 214,
CTRY_ECUADOR = 218,
CTRY_EGYPT = 818,
CTRY_EL_SALVADOR = 222,
CTRY_ESTONIA = 233,
CTRY_FAEROE_ISLANDS = 234,
CTRY_FINLAND = 246,
CTRY_FRANCE = 250,
CTRY_GEORGIA = 268,
CTRY_GERMANY = 276,
CTRY_GREECE = 300,
CTRY_GUATEMALA = 320,
CTRY_HONDURAS = 340,
CTRY_HONG_KONG = 344,
CTRY_HUNGARY = 348,
CTRY_ICELAND = 352,
CTRY_INDIA = 356,
CTRY_INDONESIA = 360,
CTRY_IRAN = 364,
CTRY_IRAQ = 368,
CTRY_IRELAND = 372,
CTRY_ISRAEL = 376,
CTRY_ITALY = 380,
CTRY_JAMAICA = 388,
CTRY_JAPAN = 392,
CTRY_JORDAN = 400,
CTRY_KAZAKHSTAN = 398,
CTRY_KENYA = 404,
CTRY_KOREA_NORTH = 408,
CTRY_KOREA_ROC = 410,
CTRY_KOREA_ROC2 = 411,
CTRY_KOREA_ROC3 = 412,
CTRY_KUWAIT = 414,
CTRY_LATVIA = 428,
CTRY_LEBANON = 422,
CTRY_LIBYA = 434,
CTRY_LIECHTENSTEIN = 438,
CTRY_LITHUANIA = 440,
CTRY_LUXEMBOURG = 442,
CTRY_MACAU = 446,
CTRY_MACEDONIA = 807,
CTRY_MALAYSIA = 458,
CTRY_MALTA = 470,
CTRY_MEXICO = 484,
CTRY_MONACO = 492,
CTRY_MOROCCO = 504,
CTRY_NEPAL = 524,
CTRY_NETHERLANDS = 528,
CTRY_NETHERLANDS_ANTILLES = 530,
CTRY_NEW_ZEALAND = 554,
CTRY_NICARAGUA = 558,
CTRY_NORWAY = 578,
CTRY_OMAN = 512,
CTRY_PAKISTAN = 586,
CTRY_PANAMA = 591,
CTRY_PAPUA_NEW_GUINEA = 598,
CTRY_PARAGUAY = 600,
CTRY_PERU = 604,
CTRY_PHILIPPINES = 608,
CTRY_POLAND = 616,
CTRY_PORTUGAL = 620,
CTRY_PUERTO_RICO = 630,
CTRY_QATAR = 634,
CTRY_ROMANIA = 642,
CTRY_RUSSIA = 643,
CTRY_SAUDI_ARABIA = 682,
CTRY_SERBIA_MONTENEGRO = 891,
CTRY_SINGAPORE = 702,
CTRY_SLOVAKIA = 703,
CTRY_SLOVENIA = 705,
CTRY_SOUTH_AFRICA = 710,
CTRY_SPAIN = 724,
CTRY_SRI_LANKA = 144,
CTRY_SWEDEN = 752,
CTRY_SWITZERLAND = 756,
CTRY_SYRIA = 760,
CTRY_TAIWAN = 158,
CTRY_THAILAND = 764,
CTRY_TRINIDAD_Y_TOBAGO = 780,
CTRY_TUNISIA = 788,
CTRY_TURKEY = 792,
CTRY_UAE = 784,
CTRY_UKRAINE = 804,
CTRY_UNITED_KINGDOM = 826,
CTRY_UNITED_STATES = 840,
CTRY_UNITED_STATES_FCC49 = 842,
CTRY_URUGUAY = 858,
CTRY_UZBEKISTAN = 860,
CTRY_VENEZUELA = 862,
CTRY_VIET_NAM = 704,
CTRY_YEMEN = 887,
CTRY_ZIMBABWE = 716,
CTRY_JAPAN1 = 393,
CTRY_JAPAN2 = 394,
CTRY_JAPAN3 = 395,
CTRY_JAPAN4 = 396,
CTRY_JAPAN5 = 397,
CTRY_JAPAN6 = 4006,
CTRY_JAPAN7 = 4007,
CTRY_JAPAN8 = 4008,
CTRY_JAPAN9 = 4009,
CTRY_JAPAN10 = 4010,
CTRY_JAPAN11 = 4011,
CTRY_JAPAN12 = 4012,
CTRY_JAPAN13 = 4013,
CTRY_JAPAN14 = 4014,
CTRY_JAPAN15 = 4015,
CTRY_JAPAN16 = 4016,
CTRY_JAPAN17 = 4017,
CTRY_JAPAN18 = 4018,
CTRY_JAPAN19 = 4019,
CTRY_JAPAN20 = 4020,
CTRY_JAPAN21 = 4021,
CTRY_JAPAN22 = 4022,
CTRY_JAPAN23 = 4023,
CTRY_JAPAN24 = 4024,
CTRY_JAPAN25 = 4025,
CTRY_JAPAN26 = 4026,
CTRY_JAPAN27 = 4027,
CTRY_JAPAN28 = 4028,
CTRY_JAPAN29 = 4029,
CTRY_JAPAN30 = 4030,
CTRY_JAPAN31 = 4031,
CTRY_JAPAN32 = 4032,
CTRY_JAPAN33 = 4033,
CTRY_JAPAN34 = 4034,
CTRY_JAPAN35 = 4035,
CTRY_JAPAN36 = 4036,
CTRY_JAPAN37 = 4037,
CTRY_JAPAN38 = 4038,
CTRY_JAPAN39 = 4039,
CTRY_JAPAN40 = 4040,
CTRY_JAPAN41 = 4041,
CTRY_JAPAN42 = 4042,
CTRY_JAPAN43 = 4043,
CTRY_JAPAN44 = 4044,
CTRY_JAPAN45 = 4045,
CTRY_JAPAN46 = 4046,
CTRY_JAPAN47 = 4047,
CTRY_JAPAN48 = 4048,
CTRY_JAPAN49 = 4049,
CTRY_JAPAN50 = 4050,
CTRY_JAPAN51 = 4051,
CTRY_JAPAN52 = 4052,
CTRY_JAPAN53 = 4053,
CTRY_JAPAN54 = 4054,
CTRY_JAPAN55 = 4055,
CTRY_JAPAN56 = 4056,
CTRY_JAPAN57 = 4057,
CTRY_JAPAN58 = 4058,
CTRY_JAPAN59 = 4059,
CTRY_AUSTRALIA2 = 5000,
CTRY_CANADA2 = 5001,
CTRY_BELGIUM2 = 5002
};
void ath9k_regd_get_current_country(struct ath_hal *ah,
struct ath9k_country_entry *ctry);
#endif
/*
* 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 REGD_COMMON_H
#define REGD_COMMON_H
enum EnumRd {
NO_ENUMRD = 0x00,
NULL1_WORLD = 0x03,
NULL1_ETSIB = 0x07,
NULL1_ETSIC = 0x08,
FCC1_FCCA = 0x10,
FCC1_WORLD = 0x11,
FCC4_FCCA = 0x12,
FCC5_FCCA = 0x13,
FCC6_FCCA = 0x14,
FCC2_FCCA = 0x20,
FCC2_WORLD = 0x21,
FCC2_ETSIC = 0x22,
FCC6_WORLD = 0x23,
FRANCE_RES = 0x31,
FCC3_FCCA = 0x3A,
FCC3_WORLD = 0x3B,
ETSI1_WORLD = 0x37,
ETSI3_ETSIA = 0x32,
ETSI2_WORLD = 0x35,
ETSI3_WORLD = 0x36,
ETSI4_WORLD = 0x30,
ETSI4_ETSIC = 0x38,
ETSI5_WORLD = 0x39,
ETSI6_WORLD = 0x34,
ETSI_RESERVED = 0x33,
MKK1_MKKA = 0x40,
MKK1_MKKB = 0x41,
APL4_WORLD = 0x42,
MKK2_MKKA = 0x43,
APL_RESERVED = 0x44,
APL2_WORLD = 0x45,
APL2_APLC = 0x46,
APL3_WORLD = 0x47,
MKK1_FCCA = 0x48,
APL2_APLD = 0x49,
MKK1_MKKA1 = 0x4A,
MKK1_MKKA2 = 0x4B,
MKK1_MKKC = 0x4C,
APL3_FCCA = 0x50,
APL1_WORLD = 0x52,
APL1_FCCA = 0x53,
APL1_APLA = 0x54,
APL1_ETSIC = 0x55,
APL2_ETSIC = 0x56,
APL5_WORLD = 0x58,
APL6_WORLD = 0x5B,
APL7_FCCA = 0x5C,
APL8_WORLD = 0x5D,
APL9_WORLD = 0x5E,
WOR0_WORLD = 0x60,
WOR1_WORLD = 0x61,
WOR2_WORLD = 0x62,
WOR3_WORLD = 0x63,
WOR4_WORLD = 0x64,
WOR5_ETSIC = 0x65,
WOR01_WORLD = 0x66,
WOR02_WORLD = 0x67,
EU1_WORLD = 0x68,
WOR9_WORLD = 0x69,
WORA_WORLD = 0x6A,
WORB_WORLD = 0x6B,
MKK3_MKKB = 0x80,
MKK3_MKKA2 = 0x81,
MKK3_MKKC = 0x82,
MKK4_MKKB = 0x83,
MKK4_MKKA2 = 0x84,
MKK4_MKKC = 0x85,
MKK5_MKKB = 0x86,
MKK5_MKKA2 = 0x87,
MKK5_MKKC = 0x88,
MKK6_MKKB = 0x89,
MKK6_MKKA2 = 0x8A,
MKK6_MKKC = 0x8B,
MKK7_MKKB = 0x8C,
MKK7_MKKA2 = 0x8D,
MKK7_MKKC = 0x8E,
MKK8_MKKB = 0x8F,
MKK8_MKKA2 = 0x90,
MKK8_MKKC = 0x91,
MKK14_MKKA1 = 0x92,
MKK15_MKKA1 = 0x93,
MKK10_FCCA = 0xD0,
MKK10_MKKA1 = 0xD1,
MKK10_MKKC = 0xD2,
MKK10_MKKA2 = 0xD3,
MKK11_MKKA = 0xD4,
MKK11_FCCA = 0xD5,
MKK11_MKKA1 = 0xD6,
MKK11_MKKC = 0xD7,
MKK11_MKKA2 = 0xD8,
MKK12_MKKA = 0xD9,
MKK12_FCCA = 0xDA,
MKK12_MKKA1 = 0xDB,
MKK12_MKKC = 0xDC,
MKK12_MKKA2 = 0xDD,
MKK13_MKKB = 0xDE,
MKK3_MKKA = 0xF0,
MKK3_MKKA1 = 0xF1,
MKK3_FCCA = 0xF2,
MKK4_MKKA = 0xF3,
MKK4_MKKA1 = 0xF4,
MKK4_FCCA = 0xF5,
MKK9_MKKA = 0xF6,
MKK10_MKKA = 0xF7,
MKK6_MKKA1 = 0xF8,
MKK6_FCCA = 0xF9,
MKK7_MKKA1 = 0xFA,
MKK7_FCCA = 0xFB,
MKK9_FCCA = 0xFC,
MKK9_MKKA1 = 0xFD,
MKK9_MKKC = 0xFE,
MKK9_MKKA2 = 0xFF,
APL1 = 0x0150,
APL2 = 0x0250,
APL3 = 0x0350,
APL4 = 0x0450,
APL5 = 0x0550,
APL6 = 0x0650,
APL7 = 0x0750,
APL8 = 0x0850,
APL9 = 0x0950,
APL10 = 0x1050,
ETSI1 = 0x0130,
ETSI2 = 0x0230,
ETSI3 = 0x0330,
ETSI4 = 0x0430,
ETSI5 = 0x0530,
ETSI6 = 0x0630,
ETSIA = 0x0A30,
ETSIB = 0x0B30,
ETSIC = 0x0C30,
FCC1 = 0x0110,
FCC2 = 0x0120,
FCC3 = 0x0160,
FCC4 = 0x0165,
FCC5 = 0x0510,
FCC6 = 0x0610,
FCCA = 0x0A10,
APLD = 0x0D50,
MKK1 = 0x0140,
MKK2 = 0x0240,
MKK3 = 0x0340,
MKK4 = 0x0440,
MKK5 = 0x0540,
MKK6 = 0x0640,
MKK7 = 0x0740,
MKK8 = 0x0840,
MKK9 = 0x0940,
MKK10 = 0x0B40,
MKK11 = 0x1140,
MKK12 = 0x1240,
MKK13 = 0x0C40,
MKK14 = 0x1440,
MKK15 = 0x1540,
MKKA = 0x0A40,
MKKC = 0x0A50,
NULL1 = 0x0198,
WORLD = 0x0199,
DEBUG_REG_DMN = 0x01ff,
};
enum {
FCC = 0x10,
MKK = 0x40,
ETSI = 0x30,
};
enum {
NO_REQ = 0x00000000,
DISALLOW_ADHOC_11A = 0x00000001,
DISALLOW_ADHOC_11A_TURB = 0x00000002,
NEED_NFC = 0x00000004,
ADHOC_PER_11D = 0x00000008,
ADHOC_NO_11A = 0x00000010,
PUBLIC_SAFETY_DOMAIN = 0x00000020,
LIMIT_FRAME_4MS = 0x00000040,
NO_HOSTAP = 0x00000080,
REQ_MASK = 0x000000FF,
};
#define REG_DOMAIN_2GHZ_MASK (REQ_MASK & \
(!(ADHOC_NO_11A | DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB)))
#define REG_DOMAIN_5GHZ_MASK REQ_MASK
static struct reg_dmn_pair_mapping regDomainPairs[] = {
{NO_ENUMRD, DEBUG_REG_DMN, DEBUG_REG_DMN, NO_REQ, NO_REQ,
PSCAN_DEFER, 0},
{NULL1_WORLD, NULL1, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{NULL1_ETSIB, NULL1, ETSIB, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{NULL1_ETSIC, NULL1, ETSIC, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{FCC2_FCCA, FCC2, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{FCC2_WORLD, FCC2, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{FCC2_ETSIC, FCC2, ETSIC, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{FCC3_FCCA, FCC3, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{FCC3_WORLD, FCC3, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{FCC4_FCCA, FCC4, FCCA,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{FCC5_FCCA, FCC5, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{FCC6_FCCA, FCC6, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{FCC6_WORLD, FCC6, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{ETSI1_WORLD, ETSI1, WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{ETSI2_WORLD, ETSI2, WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{ETSI3_WORLD, ETSI3, WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{ETSI4_WORLD, ETSI4, WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{ETSI5_WORLD, ETSI5, WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{ETSI6_WORLD, ETSI6, WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{ETSI3_ETSIA, ETSI3, WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{FRANCE_RES, ETSI3, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{FCC1_WORLD, FCC1, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{FCC1_FCCA, FCC1, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL1_WORLD, APL1, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL2_WORLD, APL2, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL3_WORLD, APL3, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL4_WORLD, APL4, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL5_WORLD, APL5, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL6_WORLD, APL6, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL8_WORLD, APL8, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL9_WORLD, APL9, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL3_FCCA, APL3, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL1_ETSIC, APL1, ETSIC, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL2_ETSIC, APL2, ETSIC, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{APL2_APLD, APL2, APLD, NO_REQ, NO_REQ, PSCAN_DEFER,},
{MKK1_MKKA, MKK1, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKKA, CTRY_JAPAN},
{MKK1_MKKB, MKK1, MKKA,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC |
LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKK1 | PSCAN_MKKA | PSCAN_MKKA_G,
CTRY_JAPAN1},
{MKK1_FCCA, MKK1, FCCA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1, CTRY_JAPAN2},
{MKK1_MKKA1, MKK1, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN4},
{MKK1_MKKA2, MKK1, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN5},
{MKK1_MKKC, MKK1, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1, CTRY_JAPAN6},
{MKK2_MKKA, MKK2, MKKA,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC |
LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKK2 | PSCAN_MKKA | PSCAN_MKKA_G,
CTRY_JAPAN3},
{MKK3_MKKA, MKK3, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKKA, CTRY_JAPAN25},
{MKK3_MKKB, MKK3, MKKA,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC |
LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKKA | PSCAN_MKKA_G,
CTRY_JAPAN7},
{MKK3_MKKA1, MKK3, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN26},
{MKK3_MKKA2, MKK3, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN8},
{MKK3_MKKC, MKK3, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
NO_PSCAN, CTRY_JAPAN9},
{MKK3_FCCA, MKK3, FCCA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
NO_PSCAN, CTRY_JAPAN27},
{MKK4_MKKA, MKK4, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3, CTRY_JAPAN36},
{MKK4_MKKB, MKK4, MKKA,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC |
LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G,
CTRY_JAPAN10},
{MKK4_MKKA1, MKK4, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN28},
{MKK4_MKKA2, MKK4, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN11},
{MKK4_MKKC, MKK4, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3, CTRY_JAPAN12},
{MKK4_FCCA, MKK4, FCCA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3, CTRY_JAPAN29},
{MKK5_MKKB, MKK5, MKKA,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC |
LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G,
CTRY_JAPAN13},
{MKK5_MKKA2, MKK5, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN14},
{MKK5_MKKC, MKK5, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3, CTRY_JAPAN15},
{MKK6_MKKB, MKK6, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKKA | PSCAN_MKKA_G, CTRY_JAPAN16},
{MKK6_MKKA1, MKK6, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN30},
{MKK6_MKKA2, MKK6, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN17},
{MKK6_MKKC, MKK6, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1, CTRY_JAPAN18},
{MKK6_FCCA, MKK6, FCCA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
NO_PSCAN, CTRY_JAPAN31},
{MKK7_MKKB, MKK7, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G,
CTRY_JAPAN19},
{MKK7_MKKA1, MKK7, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN32},
{MKK7_MKKA2, MKK7, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G,
CTRY_JAPAN20},
{MKK7_MKKC, MKK7, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN21},
{MKK7_FCCA, MKK7, FCCA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN33},
{MKK8_MKKB, MKK8, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G,
CTRY_JAPAN22},
{MKK8_MKKA2, MKK8, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G,
CTRY_JAPAN23},
{MKK8_MKKC, MKK8, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN24},
{MKK9_MKKA, MKK9, MKKA,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC |
LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK2 | PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G,
CTRY_JAPAN34},
{MKK9_FCCA, MKK9, FCCA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
NO_PSCAN, CTRY_JAPAN37},
{MKK9_MKKA1, MKK9, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN38},
{MKK9_MKKA2, MKK9, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN40},
{MKK9_MKKC, MKK9, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
NO_PSCAN, CTRY_JAPAN39},
{MKK10_MKKA, MKK10, MKKA,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC |
LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKK2 | PSCAN_MKK3, CTRY_JAPAN35},
{MKK10_FCCA, MKK10, FCCA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
NO_PSCAN, CTRY_JAPAN41},
{MKK10_MKKA1, MKK10, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN42},
{MKK10_MKKA2, MKK10, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN44},
{MKK10_MKKC, MKK10, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
NO_PSCAN, CTRY_JAPAN43},
{MKK11_MKKA, MKK11, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3, CTRY_JAPAN45},
{MKK11_FCCA, MKK11, FCCA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3, CTRY_JAPAN46},
{MKK11_MKKA1, MKK11, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN47},
{MKK11_MKKA2, MKK11, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN49},
{MKK11_MKKC, MKK11, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK3, CTRY_JAPAN48},
{MKK12_MKKA, MKK12, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN50},
{MKK12_FCCA, MKK12, FCCA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN51},
{MKK12_MKKA1, MKK12, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA1 | PSCAN_MKKA1_G,
CTRY_JAPAN52},
{MKK12_MKKA2, MKK12, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G,
CTRY_JAPAN54},
{MKK12_MKKC, MKK12, MKKC,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN53},
{MKK13_MKKB, MKK13, MKKA,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC |
LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G,
CTRY_JAPAN57},
{MKK14_MKKA1, MKK14, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN58},
{MKK15_MKKA1, MKK15, MKKA,
DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC,
PSCAN_MKK1 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN59},
{WOR0_WORLD, WOR0_WORLD, WOR0_WORLD, NO_REQ, NO_REQ, PSCAN_DEFER,
0},
{WOR1_WORLD, WOR1_WORLD, WOR1_WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{WOR2_WORLD, WOR2_WORLD, WOR2_WORLD, DISALLOW_ADHOC_11A_TURB,
NO_REQ, PSCAN_DEFER, 0},
{WOR3_WORLD, WOR3_WORLD, WOR3_WORLD, NO_REQ, NO_REQ, PSCAN_DEFER,
0},
{WOR4_WORLD, WOR4_WORLD, WOR4_WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{WOR5_ETSIC, WOR5_ETSIC, WOR5_ETSIC,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{WOR01_WORLD, WOR01_WORLD, WOR01_WORLD, NO_REQ, NO_REQ,
PSCAN_DEFER, 0},
{WOR02_WORLD, WOR02_WORLD, WOR02_WORLD, NO_REQ, NO_REQ,
PSCAN_DEFER, 0},
{EU1_WORLD, EU1_WORLD, EU1_WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0},
{WOR9_WORLD, WOR9_WORLD, WOR9_WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{WORA_WORLD, WORA_WORLD, WORA_WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
{WORB_WORLD, WORB_WORLD, WORB_WORLD,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER,
0},
};
#define NO_INTERSECT_REQ 0xFFFFFFFF
#define NO_UNION_REQ 0
static struct country_code_to_enum_rd allCountries[] = {
{CTRY_DEBUG, NO_ENUMRD, "DB", "DEBUG", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_DEFAULT, DEF_REGDMN, "NA", "NO_COUNTRY_SET", YES, YES, YES,
YES, YES, YES, YES, 7000},
{CTRY_ALBANIA, NULL1_WORLD, "AL", "ALBANIA", YES, NO, YES, YES, NO,
NO, NO, 7000},
{CTRY_ALGERIA, NULL1_WORLD, "DZ", "ALGERIA", YES, NO, YES, YES, NO,
NO, NO, 7000},
{CTRY_ARGENTINA, APL3_WORLD, "AR", "ARGENTINA", YES, NO, NO, YES,
NO, YES, NO, 7000},
{CTRY_ARMENIA, ETSI4_WORLD, "AM", "ARMENIA", YES, NO, YES, YES,
YES, NO, NO, 7000},
{CTRY_AUSTRALIA, FCC2_WORLD, "AU", "AUSTRALIA", YES, YES, YES, YES,
YES, YES, YES, 7000},
{CTRY_AUSTRALIA2, FCC6_WORLD, "AU", "AUSTRALIA2", YES, YES, YES,
YES, YES, YES, YES, 7000},
{CTRY_AUSTRIA, ETSI1_WORLD, "AT", "AUSTRIA", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ", "AZERBAIJAN", YES, YES, YES,
YES, YES, YES, YES, 7000},
{CTRY_BAHRAIN, APL6_WORLD, "BH", "BAHRAIN", YES, NO, YES, YES, YES,
YES, NO, 7000},
{CTRY_BELARUS, ETSI1_WORLD, "BY", "BELARUS", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_BELGIUM, ETSI1_WORLD, "BE", "BELGIUM", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_BELGIUM2, ETSI4_WORLD, "BL", "BELGIUM", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_BELIZE, APL1_ETSIC, "BZ", "BELIZE", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_BOLIVIA, APL1_ETSIC, "BO", "BOLVIA", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_BOSNIA_HERZ, ETSI1_WORLD, "BA", "BOSNIA_HERZGOWINA", YES, NO,
YES, YES, YES, YES, NO, 7000},
{CTRY_BRAZIL, FCC3_WORLD, "BR", "BRAZIL", YES, NO, NO, YES, NO,
YES, NO, 7000},
{CTRY_BRUNEI_DARUSSALAM, APL1_WORLD, "BN", "BRUNEI DARUSSALAM",
YES, YES, YES, YES, YES, YES, YES, 7000},
{CTRY_BULGARIA, ETSI6_WORLD, "BG", "BULGARIA", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_CANADA, FCC2_FCCA, "CA", "CANADA", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_CANADA2, FCC6_FCCA, "CA", "CANADA2", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_CHILE, APL6_WORLD, "CL", "CHILE", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_CHINA, APL1_WORLD, "CN", "CHINA", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_COLOMBIA, FCC1_FCCA, "CO", "COLOMBIA", YES, NO, YES, YES,
YES, YES, NO, 7000},
{CTRY_COSTA_RICA, FCC1_WORLD, "CR", "COSTA RICA", YES, NO, YES,
YES, YES, YES, NO, 7000},
{CTRY_CROATIA, ETSI3_WORLD, "HR", "CROATIA", YES, NO, YES, YES,
YES, YES, NO, 7000},
{CTRY_CYPRUS, ETSI1_WORLD, "CY", "CYPRUS", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_CZECH, ETSI3_WORLD, "CZ", "CZECH REPUBLIC", YES, NO, YES,
YES, YES, YES, YES, 7000},
{CTRY_DENMARK, ETSI1_WORLD, "DK", "DENMARK", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_DOMINICAN_REPUBLIC, FCC1_FCCA, "DO", "DOMINICAN REPUBLIC",
YES, YES, YES, YES, YES, YES, YES, 7000},
{CTRY_ECUADOR, FCC1_WORLD, "EC", "ECUADOR", YES, NO, NO, YES, YES,
YES, NO, 7000},
{CTRY_EGYPT, ETSI3_WORLD, "EG", "EGYPT", YES, NO, YES, YES, YES,
YES, NO, 7000},
{CTRY_EL_SALVADOR, FCC1_WORLD, "SV", "EL SALVADOR", YES, NO, YES,
YES, YES, YES, NO, 7000},
{CTRY_ESTONIA, ETSI1_WORLD, "EE", "ESTONIA", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_FINLAND, ETSI1_WORLD, "FI", "FINLAND", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_FRANCE, ETSI1_WORLD, "FR", "FRANCE", YES, NO, YES, YES, YES,
YES, YES, 7000},
{CTRY_GEORGIA, ETSI4_WORLD, "GE", "GEORGIA", YES, YES, YES, YES,
YES, YES, YES, 7000},
{CTRY_GERMANY, ETSI1_WORLD, "DE", "GERMANY", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_GREECE, ETSI1_WORLD, "GR", "GREECE", YES, NO, YES, YES, YES,
YES, YES, 7000},
{CTRY_GUATEMALA, FCC1_FCCA, "GT", "GUATEMALA", YES, YES, YES, YES,
YES, YES, YES, 7000},
{CTRY_HONDURAS, NULL1_WORLD, "HN", "HONDURAS", YES, NO, YES, YES,
YES, NO, NO, 7000},
{CTRY_HONG_KONG, FCC2_WORLD, "HK", "HONG KONG", YES, YES, YES, YES,
YES, YES, YES, 7000},
{CTRY_HUNGARY, ETSI1_WORLD, "HU", "HUNGARY", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_ICELAND, ETSI1_WORLD, "IS", "ICELAND", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_INDIA, APL6_WORLD, "IN", "INDIA", YES, NO, YES, YES, YES,
YES, NO, 7000},
{CTRY_INDONESIA, APL1_WORLD, "ID", "INDONESIA", YES, NO, YES, YES,
YES, YES, NO, 7000},
{CTRY_IRAN, APL1_WORLD, "IR", "IRAN", YES, YES, YES, YES, YES, YES,
YES, 7000},
{CTRY_IRELAND, ETSI1_WORLD, "IE", "IRELAND", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_ISRAEL, NULL1_WORLD, "IL", "ISRAEL", YES, NO, YES, YES, YES,
NO, NO, 7000},
{CTRY_ITALY, ETSI1_WORLD, "IT", "ITALY", YES, NO, YES, YES, YES,
YES, YES, 7000},
{CTRY_JAMAICA, ETSI1_WORLD, "JM", "JAMAICA", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_JAPAN, MKK1_MKKA, "JP", "JAPAN", YES, NO, NO, YES, YES, YES,
YES, 7000},
{CTRY_JAPAN1, MKK1_MKKB, "JP", "JAPAN1", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN2, MKK1_FCCA, "JP", "JAPAN2", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN3, MKK2_MKKA, "JP", "JAPAN3", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN4, MKK1_MKKA1, "JP", "JAPAN4", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN5, MKK1_MKKA2, "JP", "JAPAN5", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN6, MKK1_MKKC, "JP", "JAPAN6", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN7, MKK3_MKKB, "JP", "JAPAN7", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN8, MKK3_MKKA2, "JP", "JAPAN8", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN9, MKK3_MKKC, "JP", "JAPAN9", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN10, MKK4_MKKB, "JP", "JAPAN10", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN11, MKK4_MKKA2, "JP", "JAPAN11", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN12, MKK4_MKKC, "JP", "JAPAN12", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN13, MKK5_MKKB, "JP", "JAPAN13", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN14, MKK5_MKKA2, "JP", "JAPAN14", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN15, MKK5_MKKC, "JP", "JAPAN15", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN16, MKK6_MKKB, "JP", "JAPAN16", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN17, MKK6_MKKA2, "JP", "JAPAN17", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN18, MKK6_MKKC, "JP", "JAPAN18", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN19, MKK7_MKKB, "JP", "JAPAN19", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN20, MKK7_MKKA2, "JP", "JAPAN20", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN21, MKK7_MKKC, "JP", "JAPAN21", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN22, MKK8_MKKB, "JP", "JAPAN22", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN23, MKK8_MKKA2, "JP", "JAPAN23", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN24, MKK8_MKKC, "JP", "JAPAN24", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN25, MKK3_MKKA, "JP", "JAPAN25", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN26, MKK3_MKKA1, "JP", "JAPAN26", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN27, MKK3_FCCA, "JP", "JAPAN27", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN28, MKK4_MKKA1, "JP", "JAPAN28", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN29, MKK4_FCCA, "JP", "JAPAN29", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN30, MKK6_MKKA1, "JP", "JAPAN30", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN31, MKK6_FCCA, "JP", "JAPAN31", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN32, MKK7_MKKA1, "JP", "JAPAN32", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN33, MKK7_FCCA, "JP", "JAPAN33", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN34, MKK9_MKKA, "JP", "JAPAN34", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN35, MKK10_MKKA, "JP", "JAPAN35", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN36, MKK4_MKKA, "JP", "JAPAN36", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN37, MKK9_FCCA, "JP", "JAPAN37", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN38, MKK9_MKKA1, "JP", "JAPAN38", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN39, MKK9_MKKC, "JP", "JAPAN39", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN40, MKK9_MKKA2, "JP", "JAPAN40", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN41, MKK10_FCCA, "JP", "JAPAN41", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN42, MKK10_MKKA1, "JP", "JAPAN42", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN43, MKK10_MKKC, "JP", "JAPAN43", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN44, MKK10_MKKA2, "JP", "JAPAN44", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN45, MKK11_MKKA, "JP", "JAPAN45", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN46, MKK11_FCCA, "JP", "JAPAN46", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN47, MKK11_MKKA1, "JP", "JAPAN47", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN48, MKK11_MKKC, "JP", "JAPAN48", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN49, MKK11_MKKA2, "JP", "JAPAN49", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN50, MKK12_MKKA, "JP", "JAPAN50", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN51, MKK12_FCCA, "JP", "JAPAN51", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN52, MKK12_MKKA1, "JP", "JAPAN52", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN53, MKK12_MKKC, "JP", "JAPAN53", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN54, MKK12_MKKA2, "JP", "JAPAN54", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN57, MKK13_MKKB, "JP", "JAPAN57", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN58, MKK14_MKKA1, "JP", "JAPAN58", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JAPAN59, MKK15_MKKA1, "JP", "JAPAN59", YES, NO, NO, YES, YES,
YES, YES, 7000},
{CTRY_JORDAN, ETSI2_WORLD, "JO", "JORDAN", YES, NO, YES, YES, YES,
YES, NO, 7000},
{CTRY_KAZAKHSTAN, NULL1_WORLD, "KZ", "KAZAKHSTAN", YES, NO, YES,
YES, YES, NO, NO, 7000},
{CTRY_KOREA_NORTH, APL9_WORLD, "KP", "NORTH KOREA", YES, NO, NO,
YES, YES, YES, YES, 7000},
{CTRY_KOREA_ROC, APL9_WORLD, "KR", "KOREA REPUBLIC", YES, NO, NO,
YES, NO, YES, NO, 7000},
{CTRY_KOREA_ROC2, APL2_WORLD, "K2", "KOREA REPUBLIC2", YES, NO, NO,
YES, NO, YES, NO, 7000},
{CTRY_KOREA_ROC3, APL9_WORLD, "K3", "KOREA REPUBLIC3", YES, NO, NO,
YES, NO, YES, NO, 7000},
{CTRY_KUWAIT, NULL1_WORLD, "KW", "KUWAIT", YES, NO, YES, YES, YES,
NO, NO, 7000},
{CTRY_LATVIA, ETSI1_WORLD, "LV", "LATVIA", YES, NO, YES, YES, YES,
YES, YES, 7000},
{CTRY_LEBANON, NULL1_WORLD, "LB", "LEBANON", YES, NO, YES, YES,
YES, NO, NO, 7000},
{CTRY_LIECHTENSTEIN, ETSI1_WORLD, "LI", "LIECHTENSTEIN", YES, NO,
YES, YES, YES, YES, YES, 7000},
{CTRY_LITHUANIA, ETSI1_WORLD, "LT", "LITHUANIA", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_LUXEMBOURG, ETSI1_WORLD, "LU", "LUXEMBOURG", YES, NO, YES,
YES, YES, YES, YES, 7000},
{CTRY_MACAU, FCC2_WORLD, "MO", "MACAU", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_MACEDONIA, NULL1_WORLD, "MK", "MACEDONIA", YES, NO, YES, YES,
YES, NO, NO, 7000},
{CTRY_MALAYSIA, APL8_WORLD, "MY", "MALAYSIA", YES, NO, NO, YES, NO,
YES, NO, 7000},
{CTRY_MALTA, ETSI1_WORLD, "MT", "MALTA", YES, NO, YES, YES, YES,
YES, YES, 7000},
{CTRY_MEXICO, FCC1_FCCA, "MX", "MEXICO", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_MONACO, ETSI4_WORLD, "MC", "MONACO", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_MOROCCO, NULL1_WORLD, "MA", "MOROCCO", YES, NO, YES, YES,
YES, NO, NO, 7000},
{CTRY_NEPAL, APL1_WORLD, "NP", "NEPAL", YES, NO, YES, YES, YES,
YES, YES, 7000},
{CTRY_NETHERLANDS, ETSI1_WORLD, "NL", "NETHERLANDS", YES, NO, YES,
YES, YES, YES, YES, 7000},
{CTRY_NETHERLANDS_ANTILLES, ETSI1_WORLD, "AN",
"NETHERLANDS-ANTILLES", YES, NO, YES, YES, YES, YES, YES, 7000},
{CTRY_NEW_ZEALAND, FCC2_ETSIC, "NZ", "NEW ZEALAND", YES, NO, YES,
YES, YES, YES, NO, 7000},
{CTRY_NORWAY, ETSI1_WORLD, "NO", "NORWAY", YES, NO, YES, YES, YES,
YES, YES, 7000},
{CTRY_OMAN, APL6_WORLD, "OM", "OMAN", YES, NO, YES, YES, YES, YES,
NO, 7000},
{CTRY_PAKISTAN, NULL1_WORLD, "PK", "PAKISTAN", YES, NO, YES, YES,
YES, NO, NO, 7000},
{CTRY_PANAMA, FCC1_FCCA, "PA", "PANAMA", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_PAPUA_NEW_GUINEA, FCC1_WORLD, "PG", "PAPUA NEW GUINEA", YES,
YES, YES, YES, YES, YES, YES, 7000},
{CTRY_PERU, APL1_WORLD, "PE", "PERU", YES, NO, YES, YES, YES, YES,
NO, 7000},
{CTRY_PHILIPPINES, APL1_WORLD, "PH", "PHILIPPINES", YES, YES, YES,
YES, YES, YES, YES, 7000},
{CTRY_POLAND, ETSI1_WORLD, "PL", "POLAND", YES, NO, YES, YES, YES,
YES, YES, 7000},
{CTRY_PORTUGAL, ETSI1_WORLD, "PT", "PORTUGAL", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_PUERTO_RICO, FCC1_FCCA, "PR", "PUERTO RICO", YES, YES, YES,
YES, YES, YES, YES, 7000},
{CTRY_QATAR, NULL1_WORLD, "QA", "QATAR", YES, NO, YES, YES, YES,
NO, NO, 7000},
{CTRY_ROMANIA, NULL1_WORLD, "RO", "ROMANIA", YES, NO, YES, YES,
YES, NO, NO, 7000},
{CTRY_RUSSIA, NULL1_WORLD, "RU", "RUSSIA", YES, NO, YES, YES, YES,
NO, NO, 7000},
{CTRY_SAUDI_ARABIA, NULL1_WORLD, "SA", "SAUDI ARABIA", YES, NO,
YES, YES, YES, NO, NO, 7000},
{CTRY_SERBIA_MONTENEGRO, ETSI1_WORLD, "CS", "SERBIA & MONTENEGRO",
YES, NO, YES, YES, YES, YES, YES, 7000},
{CTRY_SINGAPORE, APL6_WORLD, "SG", "SINGAPORE", YES, YES, YES, YES,
YES, YES, YES, 7000},
{CTRY_SLOVAKIA, ETSI1_WORLD, "SK", "SLOVAK REPUBLIC", YES, NO, YES,
YES, YES, YES, YES, 7000},
{CTRY_SLOVENIA, ETSI1_WORLD, "SI", "SLOVENIA", YES, NO, YES, YES,
YES, YES, YES, 7000},
{CTRY_SOUTH_AFRICA, FCC3_WORLD, "ZA", "SOUTH AFRICA", YES, NO, YES,
YES, YES, YES, NO, 7000},
{CTRY_SPAIN, ETSI1_WORLD, "ES", "SPAIN", YES, NO, YES, YES, YES,
YES, YES, 7000},
{CTRY_SRI_LANKA, FCC3_WORLD, "LK", "SRI LANKA", YES, NO, YES, YES,
YES, YES, NO, 7000},
{CTRY_SWEDEN, ETSI1_WORLD, "SE", "SWEDEN", YES, NO, YES, YES, YES,
YES, YES, 7000},
{CTRY_SWITZERLAND, ETSI1_WORLD, "CH", "SWITZERLAND", YES, NO, YES,
YES, YES, YES, YES, 7000},
{CTRY_SYRIA, NULL1_WORLD, "SY", "SYRIA", YES, NO, YES, YES, YES,
NO, NO, 7000},
{CTRY_TAIWAN, APL3_FCCA, "TW", "TAIWAN", YES, YES, YES, YES, YES,
YES, YES, 7000},
{CTRY_THAILAND, NULL1_WORLD, "TH", "THAILAND", YES, NO, YES, YES,
YES, NO, NO, 7000},
{CTRY_TRINIDAD_Y_TOBAGO, ETSI4_WORLD, "TT", "TRINIDAD & TOBAGO",
YES, NO, YES, YES, YES, YES, NO, 7000},
{CTRY_TUNISIA, ETSI3_WORLD, "TN", "TUNISIA", YES, NO, YES, YES,
YES, YES, NO, 7000},
{CTRY_TURKEY, ETSI3_WORLD, "TR", "TURKEY", YES, NO, YES, YES, YES,
YES, NO, 7000},
{CTRY_UKRAINE, NULL1_WORLD, "UA", "UKRAINE", YES, NO, YES, YES,
YES, NO, NO, 7000},
{CTRY_UAE, NULL1_WORLD, "AE", "UNITED ARAB EMIRATES", YES, NO, YES,
YES, YES, NO, NO, 7000},
{CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB", "UNITED KINGDOM", YES, NO,
YES, YES, YES, YES, YES, 7000},
{CTRY_UNITED_STATES, FCC3_FCCA, "US", "UNITED STATES", YES, YES,
YES, YES, YES, YES, YES, 5825},
{CTRY_UNITED_STATES_FCC49, FCC4_FCCA, "PS",
"UNITED STATES (PUBLIC SAFETY)", YES, YES, YES, YES, YES, YES,
YES, 7000},
{CTRY_URUGUAY, APL2_WORLD, "UY", "URUGUAY", YES, NO, YES, YES, YES,
YES, NO, 7000},
{CTRY_UZBEKISTAN, FCC3_FCCA, "UZ", "UZBEKISTAN", YES, YES, YES,
YES, YES, YES, YES, 7000},
{CTRY_VENEZUELA, APL2_ETSIC, "VE", "VENEZUELA", YES, NO, YES, YES,
YES, YES, NO, 7000},
{CTRY_VIET_NAM, NULL1_WORLD, "VN", "VIET NAM", YES, NO, YES, YES,
YES, NO, NO, 7000},
{CTRY_YEMEN, NULL1_WORLD, "YE", "YEMEN", YES, NO, YES, YES, YES,
NO, NO, 7000},
{CTRY_ZIMBABWE, NULL1_WORLD, "ZW", "ZIMBABWE", YES, NO, YES, YES,
YES, NO, NO, 7000}
};
enum {
NO_DFS = 0x0000000000000000ULL,
DFS_FCC3 = 0x0000000000000001ULL,
DFS_ETSI = 0x0000000000000002ULL,
DFS_MKK4 = 0x0000000000000004ULL,
};
enum {
F1_4915_4925,
F1_4935_4945,
F1_4920_4980,
F1_4942_4987,
F1_4945_4985,
F1_4950_4980,
F1_5035_5040,
F1_5040_5080,
F1_5055_5055,
F1_5120_5240,
F1_5170_5230,
F2_5170_5230,
F1_5180_5240,
F2_5180_5240,
F3_5180_5240,
F4_5180_5240,
F5_5180_5240,
F6_5180_5240,
F7_5180_5240,
F8_5180_5240,
F1_5180_5320,
F1_5240_5280,
F1_5260_5280,
F1_5260_5320,
F2_5260_5320,
F3_5260_5320,
F4_5260_5320,
F5_5260_5320,
F6_5260_5320,
F1_5260_5700,
F1_5280_5320,
F1_5500_5580,
F1_5500_5620,
F1_5500_5700,
F2_5500_5700,
F3_5500_5700,
F4_5500_5700,
F5_5500_5700,
F1_5660_5700,
F1_5745_5805,
F2_5745_5805,
F3_5745_5805,
F1_5745_5825,
F2_5745_5825,
F3_5745_5825,
F4_5745_5825,
F5_5745_5825,
F6_5745_5825,
W1_4920_4980,
W1_5040_5080,
W1_5170_5230,
W1_5180_5240,
W1_5260_5320,
W1_5745_5825,
W1_5500_5700,
A_DEMO_ALL_CHANNELS
};
static struct RegDmnFreqBand regDmn5GhzFreq[] = {
{4915, 4925, 23, 0, 10, 5, NO_DFS, PSCAN_MKK2, 16},
{4935, 4945, 23, 0, 10, 5, NO_DFS, PSCAN_MKK2, 16},
{4920, 4980, 23, 0, 20, 20, NO_DFS, PSCAN_MKK2, 7},
{4942, 4987, 27, 6, 5, 5, NO_DFS, PSCAN_FCC, 0},
{4945, 4985, 30, 6, 10, 5, NO_DFS, PSCAN_FCC, 0},
{4950, 4980, 33, 6, 20, 5, NO_DFS, PSCAN_FCC, 0},
{5035, 5040, 23, 0, 10, 5, NO_DFS, PSCAN_MKK2, 12},
{5040, 5080, 23, 0, 20, 20, NO_DFS, PSCAN_MKK2, 2},
{5055, 5055, 23, 0, 10, 5, NO_DFS, PSCAN_MKK2, 12},
{5120, 5240, 5, 6, 20, 20, NO_DFS, NO_PSCAN, 0},
{5170, 5230, 23, 0, 20, 20, NO_DFS, PSCAN_MKK1 | PSCAN_MKK2, 1},
{5170, 5230, 20, 0, 20, 20, NO_DFS, PSCAN_MKK1 | PSCAN_MKK2, 1},
{5180, 5240, 15, 0, 20, 20, NO_DFS, PSCAN_FCC | PSCAN_ETSI, 0},
{5180, 5240, 17, 6, 20, 20, NO_DFS, NO_PSCAN, 1},
{5180, 5240, 18, 0, 20, 20, NO_DFS, PSCAN_FCC | PSCAN_ETSI, 0},
{5180, 5240, 20, 0, 20, 20, NO_DFS, PSCAN_FCC | PSCAN_ETSI, 0},
{5180, 5240, 23, 0, 20, 20, NO_DFS, PSCAN_FCC | PSCAN_ETSI, 0},
{5180, 5240, 23, 6, 20, 20, NO_DFS, PSCAN_FCC, 0},
{5180, 5240, 20, 0, 20, 20, NO_DFS, PSCAN_MKK1 | PSCAN_MKK3, 0},
{5180, 5240, 23, 6, 20, 20, NO_DFS, NO_PSCAN, 0},
{5180, 5320, 20, 6, 20, 20, NO_DFS, PSCAN_ETSI, 0},
{5240, 5280, 23, 0, 20, 20, DFS_FCC3, PSCAN_FCC | PSCAN_ETSI, 0},
{5260, 5280, 23, 0, 20, 20, DFS_FCC3 | DFS_ETSI,
PSCAN_FCC | PSCAN_ETSI, 0},
{5260, 5320, 18, 0, 20, 20, DFS_FCC3 | DFS_ETSI,
PSCAN_FCC | PSCAN_ETSI, 0},
{5260, 5320, 20, 0, 20, 20, DFS_FCC3 | DFS_ETSI | DFS_MKK4,
PSCAN_FCC | PSCAN_ETSI | PSCAN_MKK3, 0},
{5260, 5320, 20, 6, 20, 20, DFS_FCC3 | DFS_ETSI,
PSCAN_FCC | PSCAN_ETSI, 2},
{5260, 5320, 23, 6, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_FCC, 2},
{5260, 5320, 23, 6, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_FCC, 0},
{5260, 5320, 30, 0, 20, 20, NO_DFS, NO_PSCAN, 0},
{5260, 5700, 5, 6, 20, 20, DFS_FCC3 | DFS_ETSI, NO_PSCAN, 0},
{5280, 5320, 17, 6, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_FCC, 0},
{5500, 5580, 23, 6, 20, 20, DFS_FCC3, PSCAN_FCC, 0},
{5500, 5620, 30, 6, 20, 20, DFS_ETSI, PSCAN_ETSI, 0},
{5500, 5700, 20, 6, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_FCC, 4},
{5500, 5700, 27, 0, 20, 20, DFS_FCC3 | DFS_ETSI,
PSCAN_FCC | PSCAN_ETSI, 0},
{5500, 5700, 30, 0, 20, 20, DFS_FCC3 | DFS_ETSI,
PSCAN_FCC | PSCAN_ETSI, 0},
{5500, 5700, 23, 0, 20, 20, DFS_FCC3 | DFS_ETSI | DFS_MKK4,
PSCAN_MKK3 | PSCAN_FCC, 0},
{5500, 5700, 30, 6, 20, 20, DFS_ETSI, PSCAN_ETSI, 0},
{5660, 5700, 23, 6, 20, 20, DFS_FCC3, PSCAN_FCC, 0},
{5745, 5805, 23, 0, 20, 20, NO_DFS, NO_PSCAN, 0},
{5745, 5805, 30, 6, 20, 20, NO_DFS, NO_PSCAN, 0},
{5745, 5805, 30, 6, 20, 20, NO_DFS, PSCAN_ETSI, 0},
{5745, 5825, 5, 6, 20, 20, NO_DFS, NO_PSCAN, 0},
{5745, 5825, 17, 0, 20, 20, NO_DFS, NO_PSCAN, 0},
{5745, 5825, 20, 0, 20, 20, NO_DFS, NO_PSCAN, 0},
{5745, 5825, 30, 0, 20, 20, NO_DFS, NO_PSCAN, 0},
{5745, 5825, 30, 6, 20, 20, NO_DFS, NO_PSCAN, 3},
{5745, 5825, 30, 6, 20, 20, NO_DFS, NO_PSCAN, 0},
{4920, 4980, 30, 0, 20, 20, NO_DFS, PSCAN_WWR, 0},
{5040, 5080, 30, 0, 20, 20, NO_DFS, PSCAN_WWR, 0},
{5170, 5230, 30, 0, 20, 20, NO_DFS, PSCAN_WWR, 0},
{5180, 5240, 30, 0, 20, 20, NO_DFS, PSCAN_WWR, 0},
{5260, 5320, 30, 0, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, 0},
{5745, 5825, 30, 0, 20, 20, NO_DFS, PSCAN_WWR, 0},
{5500, 5700, 30, 0, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, 0},
{4920, 6100, 30, 6, 20, 20, NO_DFS, NO_PSCAN, 0},
};
enum {
T1_5130_5650,
T1_5150_5670,
T1_5200_5200,
T2_5200_5200,
T3_5200_5200,
T4_5200_5200,
T5_5200_5200,
T6_5200_5200,
T7_5200_5200,
T8_5200_5200,
T1_5200_5280,
T2_5200_5280,
T3_5200_5280,
T4_5200_5280,
T5_5200_5280,
T6_5200_5280,
T1_5200_5240,
T1_5210_5210,
T2_5210_5210,
T3_5210_5210,
T4_5210_5210,
T5_5210_5210,
T6_5210_5210,
T7_5210_5210,
T8_5210_5210,
T9_5210_5210,
T10_5210_5210,
T1_5240_5240,
T1_5210_5250,
T1_5210_5290,
T2_5210_5290,
T3_5210_5290,
T1_5280_5280,
T2_5280_5280,
T1_5290_5290,
T2_5290_5290,
T3_5290_5290,
T1_5250_5290,
T2_5250_5290,
T3_5250_5290,
T4_5250_5290,
T1_5540_5660,
T2_5540_5660,
T3_5540_5660,
T1_5760_5800,
T2_5760_5800,
T3_5760_5800,
T4_5760_5800,
T5_5760_5800,
T6_5760_5800,
T7_5760_5800,
T1_5765_5805,
T2_5765_5805,
T3_5765_5805,
T4_5765_5805,
T5_5765_5805,
T6_5765_5805,
T7_5765_5805,
T8_5765_5805,
T9_5765_5805,
WT1_5210_5250,
WT1_5290_5290,
WT1_5540_5660,
WT1_5760_5800,
};
enum {
F1_2312_2372,
F2_2312_2372,
F1_2412_2472,
F2_2412_2472,
F3_2412_2472,
F1_2412_2462,
F2_2412_2462,
F1_2432_2442,
F1_2457_2472,
F1_2467_2472,
F1_2484_2484,
F2_2484_2484,
F1_2512_2732,
W1_2312_2372,
W1_2412_2412,
W1_2417_2432,
W1_2437_2442,
W1_2447_2457,
W1_2462_2462,
W1_2467_2467,
W2_2467_2467,
W1_2472_2472,
W2_2472_2472,
W1_2484_2484,
W2_2484_2484,
};
static struct RegDmnFreqBand regDmn2GhzFreq[] = {
{2312, 2372, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0},
{2312, 2372, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2412, 2472, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0},
{2412, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA, 0},
{2412, 2472, 30, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2412, 2462, 27, 6, 20, 5, NO_DFS, NO_PSCAN, 0},
{2412, 2462, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA, 0},
{2432, 2442, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2457, 2472, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2467, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA2 | PSCAN_MKKA, 0},
{2484, 2484, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0},
{2484, 2484, 20, 0, 20, 5, NO_DFS,
PSCAN_MKKA | PSCAN_MKKA1 | PSCAN_MKKA2, 0},
{2512, 2732, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0},
{2312, 2372, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2412, 2412, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2417, 2432, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2437, 2442, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2447, 2457, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2462, 2462, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2467, 2467, 20, 0, 20, 5, NO_DFS, PSCAN_WWR | IS_ECM_CHAN, 0},
{2467, 2467, 20, 0, 20, 5, NO_DFS, NO_PSCAN | IS_ECM_CHAN, 0},
{2472, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_WWR | IS_ECM_CHAN, 0},
{2472, 2472, 20, 0, 20, 5, NO_DFS, NO_PSCAN | IS_ECM_CHAN, 0},
{2484, 2484, 20, 0, 20, 5, NO_DFS, PSCAN_WWR | IS_ECM_CHAN, 0},
{2484, 2484, 20, 0, 20, 5, NO_DFS, NO_PSCAN | IS_ECM_CHAN, 0},
};
enum {
G1_2312_2372,
G2_2312_2372,
G1_2412_2472,
G2_2412_2472,
G3_2412_2472,
G1_2412_2462,
G2_2412_2462,
G1_2432_2442,
G1_2457_2472,
G1_2512_2732,
G1_2467_2472,
WG1_2312_2372,
WG1_2412_2462,
WG1_2467_2472,
WG2_2467_2472,
G_DEMO_ALL_CHANNELS
};
static struct RegDmnFreqBand regDmn2Ghz11gFreq[] = {
{2312, 2372, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0},
{2312, 2372, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2412, 2472, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0},
{2412, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA_G, 0},
{2412, 2472, 30, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2412, 2462, 27, 6, 20, 5, NO_DFS, NO_PSCAN, 0},
{2412, 2462, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA_G, 0},
{2432, 2442, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2457, 2472, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2512, 2732, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0},
{2467, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA2 | PSCAN_MKKA, 0},
{2312, 2372, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2412, 2462, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0},
{2467, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_WWR | IS_ECM_CHAN, 0},
{2467, 2472, 20, 0, 20, 5, NO_DFS, NO_PSCAN | IS_ECM_CHAN, 0},
{2312, 2732, 27, 6, 20, 5, NO_DFS, NO_PSCAN, 0},
};
enum {
T1_2312_2372,
T1_2437_2437,
T2_2437_2437,
T3_2437_2437,
T1_2512_2732
};
static struct regDomain regDomains[] = {
{DEBUG_REG_DMN, FCC, DFS_FCC3, NO_PSCAN, NO_REQ,
BM(A_DEMO_ALL_CHANNELS, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5130_5650, T1_5150_5670, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5200_5240, T1_5280_5280, T1_5540_5660, T1_5765_5805, -1, -1,
-1, -1, -1, -1, -1, -1),
BM(F1_2312_2372, F1_2412_2472, F1_2484_2484, F1_2512_2732, -1, -1,
-1, -1, -1, -1, -1, -1),
BM(G_DEMO_ALL_CHANNELS, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_2312_2372, T1_2437_2437, T1_2512_2732, -1, -1, -1, -1, -1,
-1, -1, -1, -1)},
{APL1, FCC, NO_DFS, NO_PSCAN, NO_REQ,
BM(F4_5745_5825, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T1_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{APL2, FCC, NO_DFS, NO_PSCAN, NO_REQ,
BM(F1_5745_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T1_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{APL3, FCC, NO_DFS, NO_PSCAN, NO_REQ,
BM(F1_5280_5320, F2_5745_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5290_5290, T1_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{APL4, FCC, NO_DFS, NO_PSCAN, NO_REQ,
BM(F4_5180_5240, F3_5745_5825, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5210_5210, T3_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5200_5200, T3_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{APL5, FCC, NO_DFS, NO_PSCAN, NO_REQ,
BM(F2_5745_5825, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T4_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T4_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{APL6, ETSI, DFS_ETSI, PSCAN_FCC_T | PSCAN_FCC, NO_REQ,
BM(F4_5180_5240, F2_5260_5320, F3_5745_5825, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T2_5210_5210, T1_5250_5290, T1_5760_5800, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T1_5200_5280, T5_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{APL7, ETSI, DFS_ETSI, PSCAN_ETSI, NO_REQ,
BM(F1_5280_5320, F5_5500_5700, F3_5745_5805, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T3_5290_5290, T5_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5540_5660, T6_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{APL8, ETSI, NO_DFS, NO_PSCAN,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BM(F6_5260_5320, F4_5745_5825, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T2_5290_5290, T2_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5280_5280, T1_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{APL9, ETSI, DFS_ETSI, PSCAN_ETSI,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BM(F1_5180_5320, F1_5500_5620, F3_5745_5805, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T3_5290_5290, T5_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5540_5660, T6_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{APL10, ETSI, DFS_ETSI, PSCAN_ETSI,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BM(F1_5180_5320, F5_5500_5700, F3_5745_5805, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T3_5290_5290, T5_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5540_5660, T6_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{ETSI1, ETSI, DFS_ETSI, PSCAN_ETSI,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BM(F4_5180_5240, F2_5260_5320, F2_5500_5700, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T1_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_5200_5280, T2_5540_5660, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{ETSI2, ETSI, DFS_ETSI, PSCAN_ETSI,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BM(F3_5180_5240, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T3_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{ETSI3, ETSI, DFS_ETSI, PSCAN_ETSI,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BM(F4_5180_5240, F2_5260_5320, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T1_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{ETSI4, ETSI, DFS_ETSI, PSCAN_ETSI,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BM(F3_5180_5240, F1_5260_5320, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T2_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T3_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{ETSI5, ETSI, DFS_ETSI, PSCAN_ETSI,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BM(F1_5180_5240, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T4_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T3_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{ETSI6, ETSI, DFS_ETSI, PSCAN_ETSI,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BM(F5_5180_5240, F1_5260_5280, F3_5500_5700, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T1_5210_5250, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T4_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{FCC1, FCC, NO_DFS, NO_PSCAN, NO_REQ,
BM(F2_5180_5240, F4_5260_5320, F5_5745_5825, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T6_5210_5210, T2_5250_5290, T6_5760_5800, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T1_5200_5240, T2_5280_5280, T7_5765_5805, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{FCC2, FCC, NO_DFS, NO_PSCAN, NO_REQ,
BM(F6_5180_5240, F5_5260_5320, F6_5745_5825, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T7_5210_5210, T3_5250_5290, T2_5760_5800, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T7_5200_5200, T1_5240_5240, T2_5280_5280, T1_5765_5805, -1, -1,
-1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{FCC3, FCC, DFS_FCC3, PSCAN_FCC | PSCAN_FCC_T, NO_REQ,
BM(F2_5180_5240, F3_5260_5320, F1_5500_5700, F5_5745_5825, -1, -1,
-1, -1, -1, -1, -1, -1),
BM(T6_5210_5210, T2_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T4_5200_5200, T8_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{FCC4, FCC, DFS_FCC3, PSCAN_FCC | PSCAN_FCC_T, NO_REQ,
BM(F1_4942_4987, F1_4945_4985, F1_4950_4980, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T8_5210_5210, T4_5250_5290, T7_5760_5800, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T1_5200_5240, T1_5280_5280, T9_5765_5805, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{FCC5, FCC, NO_DFS, NO_PSCAN, NO_REQ,
BM(F2_5180_5240, F6_5745_5825, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T6_5210_5210, T2_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T8_5200_5200, T7_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{FCC6, FCC, DFS_FCC3, PSCAN_FCC, NO_REQ,
BM(F8_5180_5240, F5_5260_5320, F1_5500_5580, F1_5660_5700,
F6_5745_5825, -1, -1, -1, -1, -1, -1, -1),
BM(T7_5210_5210, T3_5250_5290, T2_5760_5800, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T7_5200_5200, T1_5240_5240, T2_5280_5280, T1_5765_5805, -1, -1,
-1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK1, MKK, NO_DFS, PSCAN_MKK1, DISALLOW_ADHOC_11A_TURB,
BM(F1_5170_5230, F4_5180_5240, F2_5260_5320, F4_5500_5700, -1, -1,
-1, -1, -1, -1, -1, -1),
BM(T7_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T5_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK2, MKK, NO_DFS, PSCAN_MKK2, DISALLOW_ADHOC_11A_TURB,
BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040,
F1_5055_5055, F1_5040_5080, F1_5170_5230, F4_5180_5240,
F2_5260_5320, F4_5500_5700, -1, -1),
BM(T7_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T5_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK3, MKK, NO_DFS, PSCAN_MKK3, DISALLOW_ADHOC_11A_TURB,
BM(F4_5180_5240, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T9_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T1_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK4, MKK, DFS_MKK4, PSCAN_MKK3, DISALLOW_ADHOC_11A_TURB,
BM(F4_5180_5240, F2_5260_5320, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T10_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T6_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK5, MKK, DFS_MKK4, PSCAN_MKK3, DISALLOW_ADHOC_11A_TURB,
BM(F4_5180_5240, F2_5260_5320, F4_5500_5700, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T5_5200_5280, T3_5540_5660, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{MKK6, MKK, NO_DFS, PSCAN_MKK1, DISALLOW_ADHOC_11A_TURB,
BM(F2_5170_5230, F4_5180_5240, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T3_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T6_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK7, MKK, DFS_MKK4, PSCAN_MKK1 | PSCAN_MKK3,
DISALLOW_ADHOC_11A_TURB,
BM(F1_5170_5230, F4_5180_5240, F2_5260_5320, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T5_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK8, MKK, DFS_MKK4, PSCAN_MKK1 | PSCAN_MKK3,
DISALLOW_ADHOC_11A_TURB,
BM(F1_5170_5230, F4_5180_5240, F2_5260_5320, F4_5500_5700, -1, -1,
-1, -1, -1, -1, -1, -1),
BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T5_5200_5280, T3_5540_5660, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO,
BMZERO,
BMZERO},
{MKK9, MKK, NO_DFS, PSCAN_MKK2 | PSCAN_MKK3,
DISALLOW_ADHOC_11A_TURB,
BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040,
F1_5055_5055, F1_5040_5080, F4_5180_5240, -1, -1, -1, -1, -1),
BM(T9_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T1_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK10, MKK, DFS_MKK4, PSCAN_MKK2 | PSCAN_MKK3,
DISALLOW_ADHOC_11A_TURB,
BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040,
F1_5055_5055, F1_5040_5080, F4_5180_5240, F2_5260_5320, -1, -1,
-1, -1),
BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T1_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK11, MKK, DFS_MKK4, PSCAN_MKK3, DISALLOW_ADHOC_11A_TURB,
BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040,
F1_5055_5055, F1_5040_5080, F4_5180_5240, F2_5260_5320,
F4_5500_5700, -1, -1, -1),
BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T1_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK12, MKK, DFS_MKK4, PSCAN_MKK1 | PSCAN_MKK3,
DISALLOW_ADHOC_11A_TURB,
BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040,
F1_5055_5055, F1_5040_5080, F1_5170_5230, F4_5180_5240,
F2_5260_5320, F4_5500_5700, -1, -1),
BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T1_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO},
{MKK13, MKK, DFS_MKK4, PSCAN_MKK1 | PSCAN_MKK3,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BM(F1_5170_5230, F7_5180_5240, F2_5260_5320, F4_5500_5700, -1, -1,
-1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO,
BMZERO,
BMZERO},
{MKK14, MKK, DFS_MKK4, PSCAN_MKK1, DISALLOW_ADHOC_11A_TURB,
BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040,
F1_5040_5080, F1_5055_5055, F1_5170_5230, F4_5180_5240, -1, -1,
-1, -1),
BMZERO,
BMZERO,
BMZERO,
BMZERO,
BMZERO},
{MKK15, MKK, DFS_MKK4, PSCAN_MKK1, DISALLOW_ADHOC_11A_TURB,
BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040,
F1_5040_5080, F1_5055_5055, F1_5170_5230, F4_5180_5240,
F2_5260_5320, -1, -1, -1),
BMZERO,
BMZERO,
BMZERO,
BMZERO,
BMZERO},
{APLD, NO_CTL, NO_DFS, NO_PSCAN, NO_REQ,
BMZERO,
BMZERO,
BMZERO,
BM(F2_2312_2372, F2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(G2_2312_2372, G2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BMZERO},
{ETSIA, NO_CTL, NO_DFS, PSCAN_ETSIA,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BMZERO,
BMZERO,
BMZERO,
BM(F1_2457_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(G1_2457_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{ETSIB, ETSI, NO_DFS, PSCAN_ETSIB,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BMZERO,
BMZERO,
BMZERO,
BM(F1_2432_2442, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(G1_2432_2442, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{ETSIC, ETSI, NO_DFS, PSCAN_ETSIC,
DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB,
BMZERO,
BMZERO,
BMZERO,
BM(F3_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(G3_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{FCCA, FCC, NO_DFS, NO_PSCAN, NO_REQ,
BMZERO,
BMZERO,
BMZERO,
BM(F1_2412_2462, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(G1_2412_2462, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{MKKA, MKK, NO_DFS,
PSCAN_MKKA | PSCAN_MKKA_G | PSCAN_MKKA1 | PSCAN_MKKA1_G |
PSCAN_MKKA2 | PSCAN_MKKA2_G, DISALLOW_ADHOC_11A_TURB,
BMZERO,
BMZERO,
BMZERO,
BM(F2_2412_2462, F1_2467_2472, F2_2484_2484, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(G2_2412_2462, G1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1),
BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{MKKC, MKK, NO_DFS, NO_PSCAN, NO_REQ,
BMZERO,
BMZERO,
BMZERO,
BM(F2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(G2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WORLD, ETSI, NO_DFS, NO_PSCAN, NO_REQ,
BMZERO,
BMZERO,
BMZERO,
BM(F2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(G2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WOR0_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_PER_11D,
BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825,
W1_5500_5700, -1, -1, -1, -1, -1, -1, -1),
BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1,
-1, -1, -1, -1, -1),
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472,
W1_2417_2432, W1_2447_2457, W1_2467_2467, W1_2484_2484, -1, -1,
-1, -1),
BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WOR01_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR,
ADHOC_PER_11D,
BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825,
W1_5500_5700, -1, -1, -1, -1, -1, -1, -1),
BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1,
-1, -1, -1, -1, -1),
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2417_2432,
W1_2447_2457, -1, -1, -1, -1, -1, -1, -1),
BM(WG1_2412_2462, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WOR02_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR,
ADHOC_PER_11D,
BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825,
W1_5500_5700, -1, -1, -1, -1, -1, -1, -1),
BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1,
-1, -1, -1, -1, -1),
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472,
W1_2417_2432, W1_2447_2457, W1_2467_2467, -1, -1, -1, -1, -1),
BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{EU1_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_PER_11D,
BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825,
W1_5500_5700, -1, -1, -1, -1, -1, -1, -1),
BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1,
-1, -1, -1, -1, -1),
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W2_2472_2472,
W1_2417_2432, W1_2447_2457, W2_2467_2467, -1, -1, -1, -1, -1),
BM(WG1_2412_2462, WG2_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WOR1_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A,
BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825,
W1_5500_5700, -1, -1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472,
W1_2417_2432, W1_2447_2457, W1_2467_2467, W1_2484_2484, -1, -1,
-1, -1),
BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WOR2_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A,
BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825,
W1_5500_5700, -1, -1, -1, -1, -1, -1, -1),
BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1,
-1, -1, -1, -1, -1),
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472,
W1_2417_2432, W1_2447_2457, W1_2467_2467, W1_2484_2484, -1, -1,
-1, -1),
BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WOR3_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_PER_11D,
BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825, -1, -1,
-1, -1, -1, -1, -1, -1),
BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1,
-1, -1, -1, -1, -1),
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472,
W1_2417_2432, W1_2447_2457, W1_2467_2467, -1, -1, -1, -1, -1),
BM(WG1_2412_2462, WG2_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WOR4_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A,
BM(W1_5260_5320, W1_5180_5240, W1_5745_5825, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1,
-1, -1, -1, -1, -1),
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2417_2432,
W1_2447_2457, -1, -1, -1, -1, -1, -1, -1),
BM(WG1_2412_2462, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WOR5_ETSIC, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A,
BM(W1_5260_5320, W1_5180_5240, W1_5745_5825, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BMZERO,
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472,
W1_2417_2432, W1_2447_2457, W1_2467_2467, -1, -1, -1, -1, -1),
BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WOR9_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A,
BM(W1_5260_5320, W1_5180_5240, W1_5745_5825, W1_5500_5700, -1, -1,
-1, -1, -1, -1, -1, -1),
BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1,
-1, -1, -1, -1, -1),
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2417_2432,
W1_2447_2457, -1, -1, -1, -1, -1, -1, -1),
BM(WG1_2412_2462, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WORA_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A,
BM(W1_5260_5320, W1_5180_5240, W1_5745_5825, W1_5500_5700, -1, -1,
-1, -1, -1, -1, -1, -1),
BMZERO,
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472,
W1_2417_2432, W1_2447_2457, W1_2467_2467, -1, -1, -1, -1, -1),
BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{WORB_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A,
BM(W1_5260_5320, W1_5180_5240, W1_5500_5700, -1, -1, -1, -1, -1,
-1, -1, -1, -1),
BMZERO,
BMZERO,
BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472,
W1_2417_2432, W1_2447_2457, W1_2467_2467, -1, -1, -1, -1, -1),
BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1),
BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)},
{NULL1, NO_CTL, NO_DFS, NO_PSCAN, NO_REQ,
BMZERO,
BMZERO,
BMZERO,
BMZERO,
BMZERO,
BMZERO}
};
static const struct cmode modes[] = {
{ATH9K_MODE_11A, CHANNEL_A},
{ATH9K_MODE_11B, CHANNEL_B},
{ATH9K_MODE_11G, CHANNEL_G},
{ATH9K_MODE_11NG_HT20, CHANNEL_G_HT20},
{ATH9K_MODE_11NG_HT40PLUS, CHANNEL_G_HT40PLUS},
{ATH9K_MODE_11NG_HT40MINUS, CHANNEL_G_HT40MINUS},
{ATH9K_MODE_11NA_HT20, CHANNEL_A_HT20},
{ATH9K_MODE_11NA_HT40PLUS, CHANNEL_A_HT40PLUS},
{ATH9K_MODE_11NA_HT40MINUS, CHANNEL_A_HT40MINUS},
};
static struct japan_bandcheck j_bandcheck[] = {
{F1_5170_5230, AR_EEPROM_EEREGCAP_EN_KK_U1_ODD},
{F4_5180_5240, AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN},
{F2_5260_5320, AR_EEPROM_EEREGCAP_EN_KK_U2},
{F4_5500_5700, AR_EEPROM_EEREGCAP_EN_KK_MIDBAND}
};
#endif
/*
* 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 transmit path.
*/
#include "core.h"
#define BITS_PER_BYTE 8
#define OFDM_PLCP_BITS 22
#define HT_RC_2_MCS(_rc) ((_rc) & 0x0f)
#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1)
#define L_STF 8
#define L_LTF 8
#define L_SIG 4
#define HT_SIG 8
#define HT_STF 4
#define HT_LTF(_ns) (4 * (_ns))
#define SYMBOL_TIME(_ns) ((_ns) << 2) /* ns * 4 us */
#define SYMBOL_TIME_HALFGI(_ns) (((_ns) * 18 + 4) / 5) /* ns * 3.6 us */
#define NUM_SYMBOLS_PER_USEC(_usec) (_usec >> 2)
#define NUM_SYMBOLS_PER_USEC_HALFGI(_usec) (((_usec*5)-4)/18)
#define OFDM_SIFS_TIME 16
static u32 bits_per_symbol[][2] = {
/* 20MHz 40MHz */
{ 26, 54 }, /* 0: BPSK */
{ 52, 108 }, /* 1: QPSK 1/2 */
{ 78, 162 }, /* 2: QPSK 3/4 */
{ 104, 216 }, /* 3: 16-QAM 1/2 */
{ 156, 324 }, /* 4: 16-QAM 3/4 */
{ 208, 432 }, /* 5: 64-QAM 2/3 */
{ 234, 486 }, /* 6: 64-QAM 3/4 */
{ 260, 540 }, /* 7: 64-QAM 5/6 */
{ 52, 108 }, /* 8: BPSK */
{ 104, 216 }, /* 9: QPSK 1/2 */
{ 156, 324 }, /* 10: QPSK 3/4 */
{ 208, 432 }, /* 11: 16-QAM 1/2 */
{ 312, 648 }, /* 12: 16-QAM 3/4 */
{ 416, 864 }, /* 13: 64-QAM 2/3 */
{ 468, 972 }, /* 14: 64-QAM 3/4 */
{ 520, 1080 }, /* 15: 64-QAM 5/6 */
};
#define IS_HT_RATE(_rate) ((_rate) & 0x80)
/*
* Insert a chain of ath_buf (descriptors) on a multicast txq
* but do NOT start tx DMA on this queue.
* NB: must be called with txq lock held
*/
static void ath_tx_mcastqaddbuf(struct ath_softc *sc,
struct ath_txq *txq,
struct list_head *head)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf;
if (list_empty(head))
return;
/*
* Insert the frame on the outbound list and
* pass it on to the hardware.
*/
bf = list_first_entry(head, struct ath_buf, list);
/*
* The CAB queue is started from the SWBA handler since
* frames only go out on DTIM and to avoid possible races.
*/
ath9k_hw_set_interrupts(ah, 0);
/*
* If there is anything in the mcastq, we want to set
* the "more data" bit in the last item in the queue to
* indicate that there is "more data". It makes sense to add
* it here since you are *always* going to have
* more data when adding to this queue, no matter where
* you call from.
*/
if (txq->axq_depth) {
struct ath_buf *lbf;
struct ieee80211_hdr *hdr;
/*
* Add the "more data flag" to the last frame
*/
lbf = list_entry(txq->axq_q.prev, struct ath_buf, list);
hdr = (struct ieee80211_hdr *)
((struct sk_buff *)(lbf->bf_mpdu))->data;
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
}
/*
* Now, concat the frame onto the queue
*/
list_splice_tail_init(head, &txq->axq_q);
txq->axq_depth++;
txq->axq_totalqueued++;
txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list);
DPRINTF(sc, ATH_DBG_QUEUE,
"%s: txq depth = %d\n", __func__, txq->axq_depth);
if (txq->axq_link != NULL) {
*txq->axq_link = bf->bf_daddr;
DPRINTF(sc, ATH_DBG_XMIT,
"%s: link[%u](%p)=%llx (%p)\n",
__func__,
txq->axq_qnum, txq->axq_link,
ito64(bf->bf_daddr), bf->bf_desc);
}
txq->axq_link = &(bf->bf_lastbf->bf_desc->ds_link);
ath9k_hw_set_interrupts(ah, sc->sc_imask);
}
/*
* Insert a chain of ath_buf (descriptors) on a txq and
* assume the descriptors are already chained together by caller.
* NB: must be called with txq lock held
*/
static void ath_tx_txqaddbuf(struct ath_softc *sc,
struct ath_txq *txq, struct list_head *head)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf;
/*
* Insert the frame on the outbound list and
* pass it on to the hardware.
*/
if (list_empty(head))
return;
bf = list_first_entry(head, struct ath_buf, list);
list_splice_tail_init(head, &txq->axq_q);
txq->axq_depth++;
txq->axq_totalqueued++;
txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list);
DPRINTF(sc, ATH_DBG_QUEUE,
"%s: txq depth = %d\n", __func__, txq->axq_depth);
if (txq->axq_link == NULL) {
ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
DPRINTF(sc, ATH_DBG_XMIT,
"%s: TXDP[%u] = %llx (%p)\n",
__func__, txq->axq_qnum,
ito64(bf->bf_daddr), bf->bf_desc);
} else {
*txq->axq_link = bf->bf_daddr;
DPRINTF(sc, ATH_DBG_XMIT, "%s: link[%u] (%p)=%llx (%p)\n",
__func__,
txq->axq_qnum, txq->axq_link,
ito64(bf->bf_daddr), bf->bf_desc);
}
txq->axq_link = &(bf->bf_lastbf->bf_desc->ds_link);
ath9k_hw_txstart(ah, txq->axq_qnum);
}
/* Get transmit rate index using rate in Kbps */
static int ath_tx_findindex(const struct ath9k_rate_table *rt, int rate)
{
int i;
int ndx = 0;
for (i = 0; i < rt->rateCount; i++) {
if (rt->info[i].rateKbps == rate) {
ndx = i;
break;
}
}
return ndx;
}
/* Check if it's okay to send out aggregates */
static int ath_aggr_query(struct ath_softc *sc,
struct ath_node *an, u8 tidno)
{
struct ath_atx_tid *tid;
tid = ATH_AN_2_TID(an, tidno);
if (tid->addba_exchangecomplete || tid->addba_exchangeinprogress)
return 1;
else
return 0;
}
static enum ath9k_pkt_type get_hal_packet_type(struct ieee80211_hdr *hdr)
{
enum ath9k_pkt_type htype;
__le16 fc;
fc = hdr->frame_control;
/* Calculate Atheros packet type from IEEE80211 packet header */
if (ieee80211_is_beacon(fc))
htype = ATH9K_PKT_TYPE_BEACON;
else if (ieee80211_is_probe_resp(fc))
htype = ATH9K_PKT_TYPE_PROBE_RESP;
else if (ieee80211_is_atim(fc))
htype = ATH9K_PKT_TYPE_ATIM;
else if (ieee80211_is_pspoll(fc))
htype = ATH9K_PKT_TYPE_PSPOLL;
else
htype = ATH9K_PKT_TYPE_NORMAL;
return htype;
}
static void fill_min_rates(struct sk_buff *skb, struct ath_tx_control *txctl)
{
struct ieee80211_hdr *hdr;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ath_tx_info_priv *tx_info_priv;
__le16 fc;
hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)) {
txctl->use_minrate = 1;
txctl->min_rate = tx_info_priv->min_rate;
} else if (ieee80211_is_data(fc)) {
if (ieee80211_is_nullfunc(fc) ||
/* Port Access Entity (IEEE 802.1X) */
(skb->protocol == cpu_to_be16(0x888E))) {
txctl->use_minrate = 1;
txctl->min_rate = tx_info_priv->min_rate;
}
if (is_multicast_ether_addr(hdr->addr1))
txctl->mcast_rate = tx_info_priv->min_rate;
}
}
/* This function will setup additional txctl information, mostly rate stuff */
/* FIXME: seqno, ps */
static int ath_tx_prepare(struct ath_softc *sc,
struct sk_buff *skb,
struct ath_tx_control *txctl)
{
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_hdr *hdr;
struct ath_rc_series *rcs;
struct ath_txq *txq = NULL;
const struct ath9k_rate_table *rt;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ath_tx_info_priv *tx_info_priv;
int hdrlen;
u8 rix, antenna;
__le16 fc;
u8 *qc;
memset(txctl, 0, sizeof(struct ath_tx_control));
txctl->dev = sc;
hdr = (struct ieee80211_hdr *)skb->data;
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
fc = hdr->frame_control;
rt = sc->sc_currates;
BUG_ON(!rt);
/* Fill misc fields */
spin_lock_bh(&sc->node_lock);
txctl->an = ath_node_get(sc, hdr->addr1);
/* create a temp node, if the node is not there already */
if (!txctl->an)
txctl->an = ath_node_attach(sc, hdr->addr1, 0);
spin_unlock_bh(&sc->node_lock);
if (ieee80211_is_data_qos(fc)) {
qc = ieee80211_get_qos_ctl(hdr);
txctl->tidno = qc[0] & 0xf;
}
txctl->if_id = 0;
txctl->nextfraglen = 0;
txctl->frmlen = skb->len + FCS_LEN - (hdrlen & 3);
txctl->txpower = MAX_RATE_POWER; /* FIXME */
/* Fill Key related fields */
txctl->keytype = ATH9K_KEY_TYPE_CLEAR;
txctl->keyix = ATH9K_TXKEYIX_INVALID;
if (tx_info->control.hw_key) {
txctl->keyix = tx_info->control.hw_key->hw_key_idx;
txctl->frmlen += tx_info->control.icv_len;
if (sc->sc_keytype == ATH9K_CIPHER_WEP)
txctl->keytype = ATH9K_KEY_TYPE_WEP;
else if (sc->sc_keytype == ATH9K_CIPHER_TKIP)
txctl->keytype = ATH9K_KEY_TYPE_TKIP;
else if (sc->sc_keytype == ATH9K_CIPHER_AES_CCM)
txctl->keytype = ATH9K_KEY_TYPE_AES;
}
/* Fill packet type */
txctl->atype = get_hal_packet_type(hdr);
/* Fill qnum */
txctl->qnum = ath_get_hal_qnum(skb_get_queue_mapping(skb), sc);
txq = &sc->sc_txq[txctl->qnum];
spin_lock_bh(&txq->axq_lock);
/* Try to avoid running out of descriptors */
if (txq->axq_depth >= (ATH_TXBUF - 20)) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: TX queue: %d is full, depth: %d\n",
__func__,
txctl->qnum,
txq->axq_depth);
ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
txq->stopped = 1;
spin_unlock_bh(&txq->axq_lock);
return -1;
}
spin_unlock_bh(&txq->axq_lock);
/* Fill rate */
fill_min_rates(skb, txctl);
/* Fill flags */
txctl->flags = ATH9K_TXDESC_CLRDMASK; /* needed for crypto errors */
if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
tx_info->flags |= ATH9K_TXDESC_NOACK;
if (tx_info->flags & IEEE80211_TX_CTL_USE_RTS_CTS)
tx_info->flags |= ATH9K_TXDESC_RTSENA;
/*
* Setup for rate calculations.
*/
tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
rcs = tx_info_priv->rcs;
if (ieee80211_is_data(fc) && !txctl->use_minrate) {
/* Enable HT only for DATA frames and not for EAPOL */
txctl->ht = (hw->conf.ht_conf.ht_supported &&
(tx_info->flags & IEEE80211_TX_CTL_AMPDU));
if (is_multicast_ether_addr(hdr->addr1)) {
rcs[0].rix = (u8)
ath_tx_findindex(rt, txctl->mcast_rate);
/*
* mcast packets are not re-tried.
*/
rcs[0].tries = 1;
}
/* For HT capable stations, we save tidno for later use.
* We also override seqno set by upper layer with the one
* in tx aggregation state.
*
* First, the fragmentation stat is determined.
* If fragmentation is on, the sequence number is
* not overridden, since it has been
* incremented by the fragmentation routine.
*/
if (likely(!(txctl->flags & ATH9K_TXDESC_FRAG_IS_ON)) &&
txctl->ht && sc->sc_txaggr) {
struct ath_atx_tid *tid;
tid = ATH_AN_2_TID(txctl->an, txctl->tidno);
hdr->seq_ctrl = cpu_to_le16(tid->seq_next <<
IEEE80211_SEQ_SEQ_SHIFT);
txctl->seqno = tid->seq_next;
INCR(tid->seq_next, IEEE80211_SEQ_MAX);
}
} else {
/* for management and control frames,
* or for NULL and EAPOL frames */
if (txctl->min_rate)
rcs[0].rix = ath_rate_findrateix(sc, txctl->min_rate);
else
rcs[0].rix = 0;
rcs[0].tries = ATH_MGT_TXMAXTRY;
}
rix = rcs[0].rix;
/*
* Calculate duration. This logically belongs in the 802.11
* layer but it lacks sufficient information to calculate it.
*/
if ((txctl->flags & ATH9K_TXDESC_NOACK) == 0 && !ieee80211_is_ctl(fc)) {
u16 dur;
/*
* XXX not right with fragmentation.
*/
if (sc->sc_flags & ATH_PREAMBLE_SHORT)
dur = rt->info[rix].spAckDuration;
else
dur = rt->info[rix].lpAckDuration;
if (le16_to_cpu(hdr->frame_control) &
IEEE80211_FCTL_MOREFRAGS) {
dur += dur; /* Add additional 'SIFS + ACK' */
/*
** Compute size of next fragment in order to compute
** durations needed to update NAV.
** The last fragment uses the ACK duration only.
** Add time for next fragment.
*/
dur += ath9k_hw_computetxtime(sc->sc_ah, rt,
txctl->nextfraglen,
rix, sc->sc_flags & ATH_PREAMBLE_SHORT);
}
if (ieee80211_has_morefrags(fc) ||
(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) {
/*
** Force hardware to use computed duration for next
** fragment by disabling multi-rate retry, which
** updates duration based on the multi-rate
** duration table.
*/
rcs[1].tries = rcs[2].tries = rcs[3].tries = 0;
rcs[1].rix = rcs[2].rix = rcs[3].rix = 0;
/* reset tries but keep rate index */
rcs[0].tries = ATH_TXMAXTRY;
}
hdr->duration_id = cpu_to_le16(dur);
}
/*
* Determine if a tx interrupt should be generated for
* this descriptor. We take a tx interrupt to reap
* descriptors when the h/w hits an EOL condition or
* when the descriptor is specifically marked to generate
* an interrupt. We periodically mark descriptors in this
* way to insure timely replenishing of the supply needed
* for sending frames. Defering interrupts reduces system
* load and potentially allows more concurrent work to be
* done but if done to aggressively can cause senders to
* backup.
*
* NB: use >= to deal with sc_txintrperiod changing
* dynamically through sysctl.
*/
spin_lock_bh(&txq->axq_lock);
if ((++txq->axq_intrcnt >= sc->sc_txintrperiod)) {
txctl->flags |= ATH9K_TXDESC_INTREQ;
txq->axq_intrcnt = 0;
}
spin_unlock_bh(&txq->axq_lock);
if (is_multicast_ether_addr(hdr->addr1)) {
antenna = sc->sc_mcastantenna + 1;
sc->sc_mcastantenna = (sc->sc_mcastantenna + 1) & 0x1;
} else
antenna = sc->sc_txantenna;
#ifdef USE_LEGACY_HAL
txctl->antenna = antenna;
#endif
return 0;
}
/* To complete a chain of buffers associated a frame */
static void ath_tx_complete_buf(struct ath_softc *sc,
struct ath_buf *bf,
struct list_head *bf_q,
int txok, int sendbar)
{
struct sk_buff *skb = bf->bf_mpdu;
struct ath_xmit_status tx_status;
dma_addr_t *pa;
/*
* Set retry information.
* NB: Don't use the information in the descriptor, because the frame
* could be software retried.
*/
tx_status.retries = bf->bf_retries;
tx_status.flags = 0;
if (sendbar)
tx_status.flags = ATH_TX_BAR;
if (!txok) {
tx_status.flags |= ATH_TX_ERROR;
if (bf->bf_isxretried)
tx_status.flags |= ATH_TX_XRETRY;
}
/* Unmap this frame */
pa = get_dma_mem_context(bf, bf_dmacontext);
pci_unmap_single(sc->pdev,
*pa,
skb->len,
PCI_DMA_TODEVICE);
/* complete this frame */
ath_tx_complete(sc, skb, &tx_status, bf->bf_node);
/*
* Return the list of ath_buf of this mpdu to free queue
*/
spin_lock_bh(&sc->sc_txbuflock);
list_splice_tail_init(bf_q, &sc->sc_txbuf);
spin_unlock_bh(&sc->sc_txbuflock);
}
/*
* queue up a dest/ac pair for tx scheduling
* NB: must be called with txq lock held
*/
static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
{
struct ath_atx_ac *ac = tid->ac;
/*
* if tid is paused, hold off
*/
if (tid->paused)
return;
/*
* add tid to ac atmost once
*/
if (tid->sched)
return;
tid->sched = true;
list_add_tail(&tid->list, &ac->tid_q);
/*
* add node ac to txq atmost once
*/
if (ac->sched)
return;
ac->sched = true;
list_add_tail(&ac->list, &txq->axq_acq);
}
/* pause a tid */
static void ath_tx_pause_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
{
struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum];
spin_lock_bh(&txq->axq_lock);
tid->paused++;
spin_unlock_bh(&txq->axq_lock);
}
/* resume a tid and schedule aggregate */
void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
{
struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum];
ASSERT(tid->paused > 0);
spin_lock_bh(&txq->axq_lock);
tid->paused--;
if (tid->paused > 0)
goto unlock;
if (list_empty(&tid->buf_q))
goto unlock;
/*
* Add this TID to scheduler and try to send out aggregates
*/
ath_tx_queue_tid(txq, tid);
ath_txq_schedule(sc, txq);
unlock:
spin_unlock_bh(&txq->axq_lock);
}
/* Compute the number of bad frames */
static int ath_tx_num_badfrms(struct ath_softc *sc,
struct ath_buf *bf, int txok)
{
struct ath_node *an = bf->bf_node;
int isnodegone = (an->an_flags & ATH_NODE_CLEAN);
struct ath_buf *bf_last = bf->bf_lastbf;
struct ath_desc *ds = bf_last->bf_desc;
u16 seq_st = 0;
u32 ba[WME_BA_BMP_SIZE >> 5];
int ba_index;
int nbad = 0;
int isaggr = 0;
if (isnodegone || ds->ds_txstat.ts_flags == ATH9K_TX_SW_ABORTED)
return 0;
isaggr = bf->bf_isaggr;
if (isaggr) {
seq_st = ATH_DS_BA_SEQ(ds);
memcpy(ba, ATH_DS_BA_BITMAP(ds), WME_BA_BMP_SIZE >> 3);
}
while (bf) {
ba_index = ATH_BA_INDEX(seq_st, bf->bf_seqno);
if (!txok || (isaggr && !ATH_BA_ISSET(ba, ba_index)))
nbad++;
bf = bf->bf_next;
}
return nbad;
}
static void ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf)
{
struct sk_buff *skb;
struct ieee80211_hdr *hdr;
bf->bf_isretried = 1;
bf->bf_retries++;
skb = bf->bf_mpdu;
hdr = (struct ieee80211_hdr *)skb->data;
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_RETRY);
}
/* Update block ack window */
static void ath_tx_update_baw(struct ath_softc *sc,
struct ath_atx_tid *tid, int seqno)
{
int index, cindex;
index = ATH_BA_INDEX(tid->seq_start, seqno);
cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
tid->tx_buf[cindex] = NULL;
while (tid->baw_head != tid->baw_tail && !tid->tx_buf[tid->baw_head]) {
INCR(tid->seq_start, IEEE80211_SEQ_MAX);
INCR(tid->baw_head, ATH_TID_MAX_BUFS);
}
}
/*
* ath_pkt_dur - compute packet duration (NB: not NAV)
*
* rix - rate index
* pktlen - total bytes (delims + data + fcs + pads + pad delims)
* width - 0 for 20 MHz, 1 for 40 MHz
* half_gi - to use 4us v/s 3.6 us for symbol time
*/
static u32 ath_pkt_duration(struct ath_softc *sc,
u8 rix,
struct ath_buf *bf,
int width,
int half_gi,
bool shortPreamble)
{
const struct ath9k_rate_table *rt = sc->sc_currates;
u32 nbits, nsymbits, duration, nsymbols;
u8 rc;
int streams, pktlen;
pktlen = bf->bf_isaggr ? bf->bf_al : bf->bf_frmlen;
rc = rt->info[rix].rateCode;
/*
* for legacy rates, use old function to compute packet duration
*/
if (!IS_HT_RATE(rc))
return ath9k_hw_computetxtime(sc->sc_ah,
rt,
pktlen,
rix,
shortPreamble);
/*
* find number of symbols: PLCP + data
*/
nbits = (pktlen << 3) + OFDM_PLCP_BITS;
nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
nsymbols = (nbits + nsymbits - 1) / nsymbits;
if (!half_gi)
duration = SYMBOL_TIME(nsymbols);
else
duration = SYMBOL_TIME_HALFGI(nsymbols);
/*
* addup duration for legacy/ht training and signal fields
*/
streams = HT_RC_2_STREAMS(rc);
duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
return duration;
}
/* Rate module function to set rate related fields in tx descriptor */
static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
{
struct ath_hal *ah = sc->sc_ah;
const struct ath9k_rate_table *rt;
struct ath_desc *ds = bf->bf_desc;
struct ath_desc *lastds = bf->bf_lastbf->bf_desc;
struct ath9k_11n_rate_series series[4];
int i, flags, rtsctsena = 0, dynamic_mimops = 0;
u32 ctsduration = 0;
u8 rix = 0, cix, ctsrate = 0;
u32 aggr_limit_with_rts = sc->sc_rtsaggrlimit;
struct ath_node *an = (struct ath_node *) bf->bf_node;
/*
* get the cix for the lowest valid rix.
*/
rt = sc->sc_currates;
for (i = 4; i--;) {
if (bf->bf_rcs[i].tries) {
rix = bf->bf_rcs[i].rix;
break;
}
}
flags = (bf->bf_flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA));
cix = rt->info[rix].controlRate;
/*
* If 802.11g protection is enabled, determine whether
* to use RTS/CTS or just CTS. Note that this is only
* done for OFDM/HT unicast frames.
*/
if (sc->sc_protmode != PROT_M_NONE &&
(rt->info[rix].phy == PHY_OFDM ||
rt->info[rix].phy == PHY_HT) &&
(bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) {
if (sc->sc_protmode == PROT_M_RTSCTS)
flags = ATH9K_TXDESC_RTSENA;
else if (sc->sc_protmode == PROT_M_CTSONLY)
flags = ATH9K_TXDESC_CTSENA;
cix = rt->info[sc->sc_protrix].controlRate;
rtsctsena = 1;
}
/* For 11n, the default behavior is to enable RTS for
* hw retried frames. We enable the global flag here and
* let rate series flags determine which rates will actually
* use RTS.
*/
if ((ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) && bf->bf_isdata) {
BUG_ON(!an);
/*
* 802.11g protection not needed, use our default behavior
*/
if (!rtsctsena)
flags = ATH9K_TXDESC_RTSENA;
/*
* For dynamic MIMO PS, RTS needs to precede the first aggregate
* and the second aggregate should have any protection at all.
*/
if (an->an_smmode == ATH_SM_PWRSAV_DYNAMIC) {
if (!bf->bf_aggrburst) {
flags = ATH9K_TXDESC_RTSENA;
dynamic_mimops = 1;
} else {
flags = 0;
}
}
}
/*
* Set protection if aggregate protection on
*/
if (sc->sc_config.ath_aggr_prot &&
(!bf->bf_isaggr || (bf->bf_isaggr && bf->bf_al < 8192))) {
flags = ATH9K_TXDESC_RTSENA;
cix = rt->info[sc->sc_protrix].controlRate;
rtsctsena = 1;
}
/*
* For AR5416 - RTS cannot be followed by a frame larger than 8K.
*/
if (bf->bf_isaggr && (bf->bf_al > aggr_limit_with_rts)) {
/*
* Ensure that in the case of SM Dynamic power save
* while we are bursting the second aggregate the
* RTS is cleared.
*/
flags &= ~(ATH9K_TXDESC_RTSENA);
}
/*
* CTS transmit rate is derived from the transmit rate
* by looking in the h/w rate table. We must also factor
* in whether or not a short preamble is to be used.
*/
/* NB: cix is set above where RTS/CTS is enabled */
BUG_ON(cix == 0xff);
ctsrate = rt->info[cix].rateCode |
(bf->bf_shpreamble ? rt->info[cix].shortPreamble : 0);
/*
* Setup HAL rate series
*/
memzero(series, sizeof(struct ath9k_11n_rate_series) * 4);
for (i = 0; i < 4; i++) {
if (!bf->bf_rcs[i].tries)
continue;
rix = bf->bf_rcs[i].rix;
series[i].Rate = rt->info[rix].rateCode |
(bf->bf_shpreamble ? rt->info[rix].shortPreamble : 0);
series[i].Tries = bf->bf_rcs[i].tries;
series[i].RateFlags = (
(bf->bf_rcs[i].flags & ATH_RC_RTSCTS_FLAG) ?
ATH9K_RATESERIES_RTS_CTS : 0) |
((bf->bf_rcs[i].flags & ATH_RC_CW40_FLAG) ?
ATH9K_RATESERIES_2040 : 0) |
((bf->bf_rcs[i].flags & ATH_RC_SGI_FLAG) ?
ATH9K_RATESERIES_HALFGI : 0);
series[i].PktDuration = ath_pkt_duration(
sc, rix, bf,
(bf->bf_rcs[i].flags & ATH_RC_CW40_FLAG) != 0,
(bf->bf_rcs[i].flags & ATH_RC_SGI_FLAG),
bf->bf_shpreamble);
if ((an->an_smmode == ATH_SM_PWRSAV_STATIC) &&
(bf->bf_rcs[i].flags & ATH_RC_DS_FLAG) == 0) {
/*
* When sending to an HT node that has enabled static
* SM/MIMO power save, send at single stream rates but
* use maximum allowed transmit chains per user,
* hardware, regulatory, or country limits for
* better range.
*/
series[i].ChSel = sc->sc_tx_chainmask;
} else {
if (bf->bf_ht)
series[i].ChSel =
ath_chainmask_sel_logic(sc, an);
else
series[i].ChSel = sc->sc_tx_chainmask;
}
if (rtsctsena)
series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
/*
* Set RTS for all rates if node is in dynamic powersave
* mode and we are using dual stream rates.
*/
if (dynamic_mimops && (bf->bf_rcs[i].flags & ATH_RC_DS_FLAG))
series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
}
/*
* For non-HT devices, calculate RTS/CTS duration in software
* and disable multi-rate retry.
*/
if (flags && !(ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)) {
/*
* Compute the transmit duration based on the frame
* size and the size of an ACK frame. We call into the
* HAL to do the computation since it depends on the
* characteristics of the actual PHY being used.
*
* NB: CTS is assumed the same size as an ACK so we can
* use the precalculated ACK durations.
*/
if (flags & ATH9K_TXDESC_RTSENA) { /* SIFS + CTS */
ctsduration += bf->bf_shpreamble ?
rt->info[cix].spAckDuration :
rt->info[cix].lpAckDuration;
}
ctsduration += series[0].PktDuration;
if ((bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) { /* SIFS + ACK */
ctsduration += bf->bf_shpreamble ?
rt->info[rix].spAckDuration :
rt->info[rix].lpAckDuration;
}
/*
* Disable multi-rate retry when using RTS/CTS by clearing
* series 1, 2 and 3.
*/
memzero(&series[1], sizeof(struct ath9k_11n_rate_series) * 3);
}
/*
* set dur_update_en for l-sig computation except for PS-Poll frames
*/
ath9k_hw_set11n_ratescenario(ah, ds, lastds,
!bf->bf_ispspoll,
ctsrate,
ctsduration,
series, 4, flags);
if (sc->sc_config.ath_aggr_prot && flags)
ath9k_hw_set11n_burstduration(ah, ds, 8192);
}
/*
* Function to send a normal HT (non-AMPDU) frame
* NB: must be called with txq lock held
*/
static int ath_tx_send_normal(struct ath_softc *sc,
struct ath_txq *txq,
struct ath_atx_tid *tid,
struct list_head *bf_head)
{
struct ath_buf *bf;
struct sk_buff *skb;
struct ieee80211_tx_info *tx_info;
struct ath_tx_info_priv *tx_info_priv;
BUG_ON(list_empty(bf_head));
bf = list_first_entry(bf_head, struct ath_buf, list);
bf->bf_isampdu = 0; /* regular HT frame */
skb = (struct sk_buff *)bf->bf_mpdu;
tx_info = IEEE80211_SKB_CB(skb);
tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
memcpy(bf->bf_rcs, tx_info_priv->rcs, 4 * sizeof(tx_info_priv->rcs[0]));
/* update starting sequence number for subsequent ADDBA request */
INCR(tid->seq_start, IEEE80211_SEQ_MAX);
/* Queue to h/w without aggregation */
bf->bf_nframes = 1;
bf->bf_lastbf = bf->bf_lastfrm; /* one single frame */
ath_buf_set_rate(sc, bf);
ath_tx_txqaddbuf(sc, txq, bf_head);
return 0;
}
/* flush tid's software queue and send frames as non-ampdu's */
static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
{
struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum];
struct ath_buf *bf;
struct list_head bf_head;
INIT_LIST_HEAD(&bf_head);
ASSERT(tid->paused > 0);
spin_lock_bh(&txq->axq_lock);
tid->paused--;
if (tid->paused > 0) {
spin_unlock_bh(&txq->axq_lock);
return;
}
while (!list_empty(&tid->buf_q)) {
bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
ASSERT(!bf->bf_isretried);
list_cut_position(&bf_head, &tid->buf_q, &bf->bf_lastfrm->list);
ath_tx_send_normal(sc, txq, tid, &bf_head);
}
spin_unlock_bh(&txq->axq_lock);
}
/* Completion routine of an aggregate */
static void ath_tx_complete_aggr_rifs(struct ath_softc *sc,
struct ath_txq *txq,
struct ath_buf *bf,
struct list_head *bf_q,
int txok)
{
struct ath_node *an = bf->bf_node;
struct ath_atx_tid *tid = ATH_AN_2_TID(an, bf->bf_tidno);
struct ath_buf *bf_last = bf->bf_lastbf;
struct ath_desc *ds = bf_last->bf_desc;
struct ath_buf *bf_next, *bf_lastq = NULL;
struct list_head bf_head, bf_pending;
u16 seq_st = 0;
u32 ba[WME_BA_BMP_SIZE >> 5];
int isaggr, txfail, txpending, sendbar = 0, needreset = 0;
int isnodegone = (an->an_flags & ATH_NODE_CLEAN);
isaggr = bf->bf_isaggr;
if (isaggr) {
if (txok) {
if (ATH_DS_TX_BA(ds)) {
/*
* extract starting sequence and
* block-ack bitmap
*/
seq_st = ATH_DS_BA_SEQ(ds);
memcpy(ba,
ATH_DS_BA_BITMAP(ds),
WME_BA_BMP_SIZE >> 3);
} else {
memzero(ba, WME_BA_BMP_SIZE >> 3);
/*
* AR5416 can become deaf/mute when BA
* issue happens. Chip needs to be reset.
* But AP code may have sychronization issues
* when perform internal reset in this routine.
* Only enable reset in STA mode for now.
*/
if (sc->sc_opmode == ATH9K_M_STA)
needreset = 1;
}
} else {
memzero(ba, WME_BA_BMP_SIZE >> 3);
}
}
INIT_LIST_HEAD(&bf_pending);
INIT_LIST_HEAD(&bf_head);
while (bf) {
txfail = txpending = 0;
bf_next = bf->bf_next;
if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, bf->bf_seqno))) {
/* transmit completion, subframe is
* acked by block ack */
} else if (!isaggr && txok) {
/* transmit completion */
} else {
if (!tid->cleanup_inprogress && !isnodegone &&
ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) {
if (bf->bf_retries < ATH_MAX_SW_RETRIES) {
ath_tx_set_retry(sc, bf);
txpending = 1;
} else {
bf->bf_isxretried = 1;
txfail = 1;
sendbar = 1;
}
} else {
/*
* cleanup in progress, just fail
* the un-acked sub-frames
*/
txfail = 1;
}
}
/*
* Remove ath_buf's of this sub-frame from aggregate queue.
*/
if (bf_next == NULL) { /* last subframe in the aggregate */
ASSERT(bf->bf_lastfrm == bf_last);
/*
* The last descriptor of the last sub frame could be
* a holding descriptor for h/w. If that's the case,
* bf->bf_lastfrm won't be in the bf_q.
* Make sure we handle bf_q properly here.
*/
if (!list_empty(bf_q)) {
bf_lastq = list_entry(bf_q->prev,
struct ath_buf, list);
list_cut_position(&bf_head,
bf_q, &bf_lastq->list);
} else {
/*
* XXX: if the last subframe only has one
* descriptor which is also being used as
* a holding descriptor. Then the ath_buf
* is not in the bf_q at all.
*/
INIT_LIST_HEAD(&bf_head);
}
} else {
ASSERT(!list_empty(bf_q));
list_cut_position(&bf_head,
bf_q, &bf->bf_lastfrm->list);
}
if (!txpending) {
/*
* complete the acked-ones/xretried ones; update
* block-ack window
*/
spin_lock_bh(&txq->axq_lock);
ath_tx_update_baw(sc, tid, bf->bf_seqno);
spin_unlock_bh(&txq->axq_lock);
/* complete this sub-frame */
ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar);
} else {
/*
* retry the un-acked ones
*/
/*
* XXX: if the last descriptor is holding descriptor,
* in order to requeue the frame to software queue, we
* need to allocate a new descriptor and
* copy the content of holding descriptor to it.
*/
if (bf->bf_next == NULL &&
bf_last->bf_status & ATH_BUFSTATUS_STALE) {
struct ath_buf *tbf;
/* allocate new descriptor */
spin_lock_bh(&sc->sc_txbuflock);
ASSERT(!list_empty((&sc->sc_txbuf)));
tbf = list_first_entry(&sc->sc_txbuf,
struct ath_buf, list);
list_del(&tbf->list);
spin_unlock_bh(&sc->sc_txbuflock);
ATH_TXBUF_RESET(tbf);
/* copy descriptor content */
tbf->bf_mpdu = bf_last->bf_mpdu;
tbf->bf_node = bf_last->bf_node;
tbf->bf_buf_addr = bf_last->bf_buf_addr;
*(tbf->bf_desc) = *(bf_last->bf_desc);
/* link it to the frame */
if (bf_lastq) {
bf_lastq->bf_desc->ds_link =
tbf->bf_daddr;
bf->bf_lastfrm = tbf;
ath9k_hw_cleartxdesc(sc->sc_ah,
bf->bf_lastfrm->bf_desc);
} else {
tbf->bf_state = bf_last->bf_state;
tbf->bf_lastfrm = tbf;
ath9k_hw_cleartxdesc(sc->sc_ah,
tbf->bf_lastfrm->bf_desc);
/* copy the DMA context */
copy_dma_mem_context(
get_dma_mem_context(tbf,
bf_dmacontext),
get_dma_mem_context(bf_last,
bf_dmacontext));
}
list_add_tail(&tbf->list, &bf_head);
} else {
/*
* Clear descriptor status words for
* software retry
*/
ath9k_hw_cleartxdesc(sc->sc_ah,
bf->bf_lastfrm->bf_desc);
}
/*
* Put this buffer to the temporary pending
* queue to retain ordering
*/
list_splice_tail_init(&bf_head, &bf_pending);
}
bf = bf_next;
}
/*
* node is already gone. no more assocication
* with the node. the node might have been freed
* any node acces can result in panic.note tid
* is part of the node.
*/
if (isnodegone)
return;
if (tid->cleanup_inprogress) {
/* check to see if we're done with cleaning the h/w queue */
spin_lock_bh(&txq->axq_lock);
if (tid->baw_head == tid->baw_tail) {
tid->addba_exchangecomplete = 0;
tid->addba_exchangeattempts = 0;
spin_unlock_bh(&txq->axq_lock);
tid->cleanup_inprogress = false;
/* send buffered frames as singles */
ath_tx_flush_tid(sc, tid);
} else
spin_unlock_bh(&txq->axq_lock);
return;
}
/*
* prepend un-acked frames to the beginning of the pending frame queue
*/
if (!list_empty(&bf_pending)) {
spin_lock_bh(&txq->axq_lock);
/* Note: we _prepend_, we _do_not_ at to
* the end of the queue ! */
list_splice(&bf_pending, &tid->buf_q);
ath_tx_queue_tid(txq, tid);
spin_unlock_bh(&txq->axq_lock);
}
if (needreset)
ath_internal_reset(sc);
return;
}
/* Process completed xmit descriptors from the specified queue */
static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf, *lastbf, *bf_held = NULL;
struct list_head bf_head;
struct ath_desc *ds, *tmp_ds;
struct sk_buff *skb;
struct ieee80211_tx_info *tx_info;
struct ath_tx_info_priv *tx_info_priv;
int nacked, txok, nbad = 0, isrifs = 0;
int status;
DPRINTF(sc, ATH_DBG_QUEUE,
"%s: tx queue %d (%x), link %p\n", __func__,
txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum),
txq->axq_link);
nacked = 0;
for (;;) {
spin_lock_bh(&txq->axq_lock);
txq->axq_intrcnt = 0; /* reset periodic desc intr count */
if (list_empty(&txq->axq_q)) {
txq->axq_link = NULL;
txq->axq_linkbuf = NULL;
spin_unlock_bh(&txq->axq_lock);
break;
}
bf = list_first_entry(&txq->axq_q, struct ath_buf, list);
/*
* There is a race condition that a BH gets scheduled
* after sw writes TxE and before hw re-load the last
* descriptor to get the newly chained one.
* Software must keep the last DONE descriptor as a
* holding descriptor - software does so by marking
* it with the STALE flag.
*/
bf_held = NULL;
if (bf->bf_status & ATH_BUFSTATUS_STALE) {
bf_held = bf;
if (list_is_last(&bf_held->list, &txq->axq_q)) {
/* FIXME:
* The holding descriptor is the last
* descriptor in queue. It's safe to remove
* the last holding descriptor in BH context.
*/
spin_unlock_bh(&txq->axq_lock);
break;
} else {
/* Lets work with the next buffer now */
bf = list_entry(bf_held->list.next,
struct ath_buf, list);
}
}
lastbf = bf->bf_lastbf;
ds = lastbf->bf_desc; /* NB: last decriptor */
status = ath9k_hw_txprocdesc(ah, ds);
if (status == -EINPROGRESS) {
spin_unlock_bh(&txq->axq_lock);
break;
}
if (bf->bf_desc == txq->axq_lastdsWithCTS)
txq->axq_lastdsWithCTS = NULL;
if (ds == txq->axq_gatingds)
txq->axq_gatingds = NULL;
/*
* Remove ath_buf's of the same transmit unit from txq,
* however leave the last descriptor back as the holding
* descriptor for hw.
*/
lastbf->bf_status |= ATH_BUFSTATUS_STALE;
INIT_LIST_HEAD(&bf_head);
if (!list_is_singular(&lastbf->list))
list_cut_position(&bf_head,
&txq->axq_q, lastbf->list.prev);
txq->axq_depth--;
if (bf->bf_isaggr)
txq->axq_aggr_depth--;
txok = (ds->ds_txstat.ts_status == 0);
spin_unlock_bh(&txq->axq_lock);
if (bf_held) {
list_del(&bf_held->list);
spin_lock_bh(&sc->sc_txbuflock);
list_add_tail(&bf_held->list, &sc->sc_txbuf);
spin_unlock_bh(&sc->sc_txbuflock);
}
if (!bf->bf_isampdu) {
/*
* This frame is sent out as a single frame.
* Use hardware retry status for this frame.
*/
bf->bf_retries = ds->ds_txstat.ts_longretry;
if (ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY)
bf->bf_isxretried = 1;
nbad = 0;
} else {
nbad = ath_tx_num_badfrms(sc, bf, txok);
}
skb = bf->bf_mpdu;
tx_info = IEEE80211_SKB_CB(skb);
tx_info_priv = (struct ath_tx_info_priv *)
tx_info->driver_data[0];
if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT)
tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 &&
(bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) {
if (ds->ds_txstat.ts_status == 0)
nacked++;
if (bf->bf_isdata) {
if (isrifs)
tmp_ds = bf->bf_rifslast->bf_desc;
else
tmp_ds = ds;
memcpy(&tx_info_priv->tx,
&tmp_ds->ds_txstat,
sizeof(tx_info_priv->tx));
tx_info_priv->n_frames = bf->bf_nframes;
tx_info_priv->n_bad_frames = nbad;
}
}
/*
* Complete this transmit unit
*/
if (bf->bf_isampdu)
ath_tx_complete_aggr_rifs(sc, txq, bf, &bf_head, txok);
else
ath_tx_complete_buf(sc, bf, &bf_head, txok, 0);
/* Wake up mac80211 queue */
spin_lock_bh(&txq->axq_lock);
if (txq->stopped && ath_txq_depth(sc, txq->axq_qnum) <=
(ATH_TXBUF - 20)) {
int qnum;
qnum = ath_get_mac80211_qnum(txq->axq_qnum, sc);
if (qnum != -1) {
ieee80211_wake_queue(sc->hw, qnum);
txq->stopped = 0;
}
}
/*
* schedule any pending packets if aggregation is enabled
*/
if (sc->sc_txaggr)
ath_txq_schedule(sc, txq);
spin_unlock_bh(&txq->axq_lock);
}
return nacked;
}
static void ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_hal *ah = sc->sc_ah;
(void) ath9k_hw_stoptxdma(ah, txq->axq_qnum);
DPRINTF(sc, ATH_DBG_XMIT, "%s: tx queue [%u] %x, link %p\n",
__func__, txq->axq_qnum,
ath9k_hw_gettxbuf(ah, txq->axq_qnum), txq->axq_link);
}
/* Drain only the data queues */
static void ath_drain_txdataq(struct ath_softc *sc, bool retry_tx)
{
struct ath_hal *ah = sc->sc_ah;
int i;
int npend = 0;
enum ath9k_ht_macmode ht_macmode = ath_cwm_macmode(sc);
/* XXX return value */
if (!sc->sc_invalid) {
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(sc, i)) {
ath_tx_stopdma(sc, &sc->sc_txq[i]);
/* The TxDMA may not really be stopped.
* Double check the hal tx pending count */
npend += ath9k_hw_numtxpending(ah,
sc->sc_txq[i].axq_qnum);
}
}
}
if (npend) {
int status;
/* TxDMA not stopped, reset the hal */
DPRINTF(sc, ATH_DBG_XMIT,
"%s: Unable to stop TxDMA. Reset HAL!\n", __func__);
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, true, &status)) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: unable to reset hardware; hal status %u\n",
__func__,
status);
}
spin_unlock_bh(&sc->sc_resetlock);
}
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(sc, i))
ath_tx_draintxq(sc, &sc->sc_txq[i], retry_tx);
}
}
/* Add a sub-frame to block ack window */
static void ath_tx_addto_baw(struct ath_softc *sc,
struct ath_atx_tid *tid,
struct ath_buf *bf)
{
int index, cindex;
if (bf->bf_isretried)
return;
index = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno);
cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
ASSERT(tid->tx_buf[cindex] == NULL);
tid->tx_buf[cindex] = bf;
if (index >= ((tid->baw_tail - tid->baw_head) &
(ATH_TID_MAX_BUFS - 1))) {
tid->baw_tail = cindex;
INCR(tid->baw_tail, ATH_TID_MAX_BUFS);
}
}
/*
* Function to send an A-MPDU
* NB: must be called with txq lock held
*/
static int ath_tx_send_ampdu(struct ath_softc *sc,
struct ath_txq *txq,
struct ath_atx_tid *tid,
struct list_head *bf_head,
struct ath_tx_control *txctl)
{
struct ath_buf *bf;
struct sk_buff *skb;
struct ieee80211_tx_info *tx_info;
struct ath_tx_info_priv *tx_info_priv;
BUG_ON(list_empty(bf_head));
bf = list_first_entry(bf_head, struct ath_buf, list);
bf->bf_isampdu = 1;
bf->bf_seqno = txctl->seqno; /* save seqno and tidno in buffer */
bf->bf_tidno = txctl->tidno;
/*
* Do not queue to h/w when any of the following conditions is true:
* - there are pending frames in software queue
* - the TID is currently paused for ADDBA/BAR request
* - seqno is not within block-ack window
* - h/w queue depth exceeds low water mark
*/
if (!list_empty(&tid->buf_q) || tid->paused ||
!BAW_WITHIN(tid->seq_start, tid->baw_size, bf->bf_seqno) ||
txq->axq_depth >= ATH_AGGR_MIN_QDEPTH) {
/*
* Add this frame to software queue for scheduling later
* for aggregation.
*/
list_splice_tail_init(bf_head, &tid->buf_q);
ath_tx_queue_tid(txq, tid);
return 0;
}
skb = (struct sk_buff *)bf->bf_mpdu;
tx_info = IEEE80211_SKB_CB(skb);
tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
memcpy(bf->bf_rcs, tx_info_priv->rcs, 4 * sizeof(tx_info_priv->rcs[0]));
/* Add sub-frame to BAW */
ath_tx_addto_baw(sc, tid, bf);
/* Queue to h/w without aggregation */
bf->bf_nframes = 1;
bf->bf_lastbf = bf->bf_lastfrm; /* one single frame */
ath_buf_set_rate(sc, bf);
ath_tx_txqaddbuf(sc, txq, bf_head);
return 0;
}
/*
* looks up the rate
* returns aggr limit based on lowest of the rates
*/
static u32 ath_lookup_rate(struct ath_softc *sc,
struct ath_buf *bf)
{
const struct ath9k_rate_table *rt = sc->sc_currates;
struct sk_buff *skb;
struct ieee80211_tx_info *tx_info;
struct ath_tx_info_priv *tx_info_priv;
u32 max_4ms_framelen, frame_length;
u16 aggr_limit, legacy = 0, maxampdu;
int i;
skb = (struct sk_buff *)bf->bf_mpdu;
tx_info = IEEE80211_SKB_CB(skb);
tx_info_priv = (struct ath_tx_info_priv *)
tx_info->driver_data[0];
memcpy(bf->bf_rcs,
tx_info_priv->rcs, 4 * sizeof(tx_info_priv->rcs[0]));
/*
* Find the lowest frame length among the rate series that will have a
* 4ms transmit duration.
* TODO - TXOP limit needs to be considered.
*/
max_4ms_framelen = ATH_AMPDU_LIMIT_MAX;
for (i = 0; i < 4; i++) {
if (bf->bf_rcs[i].tries) {
frame_length = bf->bf_rcs[i].max_4ms_framelen;
if (rt->info[bf->bf_rcs[i].rix].phy != PHY_HT) {
legacy = 1;
break;
}
max_4ms_framelen = min(max_4ms_framelen, frame_length);
}
}
/*
* limit aggregate size by the minimum rate if rate selected is
* not a probe rate, if rate selected is a probe rate then
* avoid aggregation of this packet.
*/
if (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE || legacy)
return 0;
aggr_limit = min(max_4ms_framelen,
(u32)ATH_AMPDU_LIMIT_DEFAULT);
/*
* h/w can accept aggregates upto 16 bit lengths (65535).
* The IE, however can hold upto 65536, which shows up here
* as zero. Ignore 65536 since we are constrained by hw.
*/
maxampdu = sc->sc_ht_info.maxampdu;
if (maxampdu)
aggr_limit = min(aggr_limit, maxampdu);
return aggr_limit;
}
/*
* returns the number of delimiters to be added to
* meet the minimum required mpdudensity.
* caller should make sure that the rate is HT rate .
*/
static int ath_compute_num_delims(struct ath_softc *sc,
struct ath_buf *bf,
u16 frmlen)
{
const struct ath9k_rate_table *rt = sc->sc_currates;
u32 nsymbits, nsymbols, mpdudensity;
u16 minlen;
u8 rc, flags, rix;
int width, half_gi, ndelim, mindelim;
/* Select standard number of delimiters based on frame length alone */
ndelim = ATH_AGGR_GET_NDELIM(frmlen);
/*
* If encryption enabled, hardware requires some more padding between
* subframes.
* TODO - this could be improved to be dependent on the rate.
* The hardware can keep up at lower rates, but not higher rates
*/
if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR)
ndelim += ATH_AGGR_ENCRYPTDELIM;
/*
* Convert desired mpdu density from microeconds to bytes based
* on highest rate in rate series (i.e. first rate) to determine
* required minimum length for subframe. Take into account
* whether high rate is 20 or 40Mhz and half or full GI.
*/
mpdudensity = sc->sc_ht_info.mpdudensity;
/*
* If there is no mpdu density restriction, no further calculation
* is needed.
*/
if (mpdudensity == 0)
return ndelim;
rix = bf->bf_rcs[0].rix;
flags = bf->bf_rcs[0].flags;
rc = rt->info[rix].rateCode;
width = (flags & ATH_RC_CW40_FLAG) ? 1 : 0;
half_gi = (flags & ATH_RC_SGI_FLAG) ? 1 : 0;
if (half_gi)
nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(mpdudensity);
else
nsymbols = NUM_SYMBOLS_PER_USEC(mpdudensity);
if (nsymbols == 0)
nsymbols = 1;
nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width];
minlen = (nsymbols * nsymbits) / BITS_PER_BYTE;
/* Is frame shorter than required minimum length? */
if (frmlen < minlen) {
/* Get the minimum number of delimiters required. */
mindelim = (minlen - frmlen) / ATH_AGGR_DELIM_SZ;
ndelim = max(mindelim, ndelim);
}
return ndelim;
}
/*
* For aggregation from software buffer queue.
* NB: must be called with txq lock held
*/
static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
struct ath_atx_tid *tid,
struct list_head *bf_q,
struct ath_buf **bf_last,
struct aggr_rifs_param *param,
int *prev_frames)
{
#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
struct ath_buf *bf, *tbf, *bf_first, *bf_prev = NULL;
struct list_head bf_head;
int rl = 0, nframes = 0, ndelim;
u16 aggr_limit = 0, al = 0, bpad = 0,
al_delta, h_baw = tid->baw_size / 2;
enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
int prev_al = 0, is_ds_rate = 0;
INIT_LIST_HEAD(&bf_head);
BUG_ON(list_empty(&tid->buf_q));
bf_first = list_first_entry(&tid->buf_q, struct ath_buf, list);
do {
bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
/*
* do not step over block-ack window
*/
if (!BAW_WITHIN(tid->seq_start, tid->baw_size, bf->bf_seqno)) {
status = ATH_AGGR_BAW_CLOSED;
break;
}
if (!rl) {
aggr_limit = ath_lookup_rate(sc, bf);
rl = 1;
/*
* Is rate dual stream
*/
is_ds_rate =
(bf->bf_rcs[0].flags & ATH_RC_DS_FLAG) ? 1 : 0;
}
/*
* do not exceed aggregation limit
*/
al_delta = ATH_AGGR_DELIM_SZ + bf->bf_frmlen;
if (nframes && (aggr_limit <
(al + bpad + al_delta + prev_al))) {
status = ATH_AGGR_LIMITED;
break;
}
/*
* do not exceed subframe limit
*/
if ((nframes + *prev_frames) >=
min((int)h_baw, ATH_AMPDU_SUBFRAME_DEFAULT)) {
status = ATH_AGGR_LIMITED;
break;
}
/*
* add padding for previous frame to aggregation length
*/
al += bpad + al_delta;
/*
* Get the delimiters needed to meet the MPDU
* density for this node.
*/
ndelim = ath_compute_num_delims(sc, bf_first, bf->bf_frmlen);
bpad = PADBYTES(al_delta) + (ndelim << 2);
bf->bf_next = NULL;
bf->bf_lastfrm->bf_desc->ds_link = 0;
/*
* this packet is part of an aggregate
* - remove all descriptors belonging to this frame from
* software queue
* - add it to block ack window
* - set up descriptors for aggregation
*/
list_cut_position(&bf_head, &tid->buf_q, &bf->bf_lastfrm->list);
ath_tx_addto_baw(sc, tid, bf);
list_for_each_entry(tbf, &bf_head, list) {
ath9k_hw_set11n_aggr_middle(sc->sc_ah,
tbf->bf_desc, ndelim);
}
/*
* link buffers of this frame to the aggregate
*/
list_splice_tail_init(&bf_head, bf_q);
nframes++;
if (bf_prev) {
bf_prev->bf_next = bf;
bf_prev->bf_lastfrm->bf_desc->ds_link = bf->bf_daddr;
}
bf_prev = bf;
#ifdef AGGR_NOSHORT
/*
* terminate aggregation on a small packet boundary
*/
if (bf->bf_frmlen < ATH_AGGR_MINPLEN) {
status = ATH_AGGR_SHORTPKT;
break;
}
#endif
} while (!list_empty(&tid->buf_q));
bf_first->bf_al = al;
bf_first->bf_nframes = nframes;
*bf_last = bf_prev;
return status;
#undef PADBYTES
}
/*
* process pending frames possibly doing a-mpdu aggregation
* NB: must be called with txq lock held
*/
static void ath_tx_sched_aggr(struct ath_softc *sc,
struct ath_txq *txq, struct ath_atx_tid *tid)
{
struct ath_buf *bf, *tbf, *bf_last, *bf_lastaggr = NULL;
enum ATH_AGGR_STATUS status;
struct list_head bf_q;
struct aggr_rifs_param param = {0, 0, 0, 0, NULL};
int prev_frames = 0;
do {
if (list_empty(&tid->buf_q))
return;
INIT_LIST_HEAD(&bf_q);
status = ath_tx_form_aggr(sc, tid, &bf_q, &bf_lastaggr, &param,
&prev_frames);
/*
* no frames picked up to be aggregated; block-ack
* window is not open
*/
if (list_empty(&bf_q))
break;
bf = list_first_entry(&bf_q, struct ath_buf, list);
bf_last = list_entry(bf_q.prev, struct ath_buf, list);
bf->bf_lastbf = bf_last;
/*
* if only one frame, send as non-aggregate
*/
if (bf->bf_nframes == 1) {
ASSERT(bf->bf_lastfrm == bf_last);
bf->bf_isaggr = 0;
/*
* clear aggr bits for every descriptor
* XXX TODO: is there a way to optimize it?
*/
list_for_each_entry(tbf, &bf_q, list) {
ath9k_hw_clr11n_aggr(sc->sc_ah, tbf->bf_desc);
}
ath_buf_set_rate(sc, bf);
ath_tx_txqaddbuf(sc, txq, &bf_q);
continue;
}
/*
* setup first desc with rate and aggr info
*/
bf->bf_isaggr = 1;
ath_buf_set_rate(sc, bf);
ath9k_hw_set11n_aggr_first(sc->sc_ah, bf->bf_desc, bf->bf_al);
/*
* anchor last frame of aggregate correctly
*/
ASSERT(bf_lastaggr);
ASSERT(bf_lastaggr->bf_lastfrm == bf_last);
tbf = bf_lastaggr;
ath9k_hw_set11n_aggr_last(sc->sc_ah, tbf->bf_desc);
/* XXX: We don't enter into this loop, consider removing this */
while (!list_empty(&bf_q) && !list_is_last(&tbf->list, &bf_q)) {
tbf = list_entry(tbf->list.next, struct ath_buf, list);
ath9k_hw_set11n_aggr_last(sc->sc_ah, tbf->bf_desc);
}
txq->axq_aggr_depth++;
/*
* Normal aggregate, queue to hardware
*/
ath_tx_txqaddbuf(sc, txq, &bf_q);
} while (txq->axq_depth < ATH_AGGR_MIN_QDEPTH &&
status != ATH_AGGR_BAW_CLOSED);
}
/* Called with txq lock held */
static void ath_tid_drain(struct ath_softc *sc,
struct ath_txq *txq,
struct ath_atx_tid *tid,
bool bh_flag)
{
struct ath_buf *bf;
struct list_head bf_head;
INIT_LIST_HEAD(&bf_head);
for (;;) {
if (list_empty(&tid->buf_q))
break;
bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
list_cut_position(&bf_head, &tid->buf_q, &bf->bf_lastfrm->list);
/* update baw for software retried frame */
if (bf->bf_isretried)
ath_tx_update_baw(sc, tid, bf->bf_seqno);
/*
* do not indicate packets while holding txq spinlock.
* unlock is intentional here
*/
if (likely(bh_flag))
spin_unlock_bh(&txq->axq_lock);
else
spin_unlock(&txq->axq_lock);
/* complete this sub-frame */
ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
if (likely(bh_flag))
spin_lock_bh(&txq->axq_lock);
else
spin_lock(&txq->axq_lock);
}
/*
* TODO: For frame(s) that are in the retry state, we will reuse the
* sequence number(s) without setting the retry bit. The
* alternative is to give up on these and BAR the receiver's window
* forward.
*/
tid->seq_next = tid->seq_start;
tid->baw_tail = tid->baw_head;
}
/*
* Drain all pending buffers
* NB: must be called with txq lock held
*/
static void ath_txq_drain_pending_buffers(struct ath_softc *sc,
struct ath_txq *txq,
bool bh_flag)
{
struct ath_atx_ac *ac, *ac_tmp;
struct ath_atx_tid *tid, *tid_tmp;
list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) {
list_del(&ac->list);
ac->sched = false;
list_for_each_entry_safe(tid, tid_tmp, &ac->tid_q, list) {
list_del(&tid->list);
tid->sched = false;
ath_tid_drain(sc, txq, tid, bh_flag);
}
}
}
static int ath_tx_start_dma(struct ath_softc *sc,
struct sk_buff *skb,
struct scatterlist *sg,
u32 n_sg,
struct ath_tx_control *txctl)
{
struct ath_node *an = txctl->an;
struct ath_buf *bf = NULL;
struct list_head bf_head;
struct ath_desc *ds;
struct ath_hal *ah = sc->sc_ah;
struct ath_txq *txq = &sc->sc_txq[txctl->qnum];
struct ath_tx_info_priv *tx_info_priv;
struct ath_rc_series *rcs;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
__le16 fc = hdr->frame_control;
/* For each sglist entry, allocate an ath_buf for DMA */
INIT_LIST_HEAD(&bf_head);
spin_lock_bh(&sc->sc_txbuflock);
if (unlikely(list_empty(&sc->sc_txbuf))) {
spin_unlock_bh(&sc->sc_txbuflock);
return -ENOMEM;
}
bf = list_first_entry(&sc->sc_txbuf, struct ath_buf, list);
list_del(&bf->list);
spin_unlock_bh(&sc->sc_txbuflock);
list_add_tail(&bf->list, &bf_head);
/* set up this buffer */
ATH_TXBUF_RESET(bf);
bf->bf_frmlen = txctl->frmlen;
bf->bf_isdata = ieee80211_is_data(fc);
bf->bf_isbar = ieee80211_is_back_req(fc);
bf->bf_ispspoll = ieee80211_is_pspoll(fc);
bf->bf_flags = txctl->flags;
bf->bf_shpreamble = sc->sc_flags & ATH_PREAMBLE_SHORT;
bf->bf_keytype = txctl->keytype;
tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0];
rcs = tx_info_priv->rcs;
bf->bf_rcs[0] = rcs[0];
bf->bf_rcs[1] = rcs[1];
bf->bf_rcs[2] = rcs[2];
bf->bf_rcs[3] = rcs[3];
bf->bf_node = an;
bf->bf_mpdu = skb;
bf->bf_buf_addr = sg_dma_address(sg);
/* setup descriptor */
ds = bf->bf_desc;
ds->ds_link = 0;
ds->ds_data = bf->bf_buf_addr;
/*
* Save the DMA context in the first ath_buf
*/
copy_dma_mem_context(get_dma_mem_context(bf, bf_dmacontext),
get_dma_mem_context(txctl, dmacontext));
/*
* Formulate first tx descriptor with tx controls.
*/
ath9k_hw_set11n_txdesc(ah,
ds,
bf->bf_frmlen, /* frame length */
txctl->atype, /* Atheros packet type */
min(txctl->txpower, (u16)60), /* txpower */
txctl->keyix, /* key cache index */
txctl->keytype, /* key type */
txctl->flags); /* flags */
ath9k_hw_filltxdesc(ah,
ds,
sg_dma_len(sg), /* segment length */
true, /* first segment */
(n_sg == 1) ? true : false, /* last segment */
ds); /* first descriptor */
bf->bf_lastfrm = bf;
bf->bf_ht = txctl->ht;
spin_lock_bh(&txq->axq_lock);
if (txctl->ht && sc->sc_txaggr) {
struct ath_atx_tid *tid = ATH_AN_2_TID(an, txctl->tidno);
if (ath_aggr_query(sc, an, txctl->tidno)) {
/*
* Try aggregation if it's a unicast data frame
* and the destination is HT capable.
*/
ath_tx_send_ampdu(sc, txq, tid, &bf_head, txctl);
} else {
/*
* Send this frame as regular when ADDBA exchange
* is neither complete nor pending.
*/
ath_tx_send_normal(sc, txq, tid, &bf_head);
}
} else {
bf->bf_lastbf = bf;
bf->bf_nframes = 1;
ath_buf_set_rate(sc, bf);
if (ieee80211_is_back_req(fc)) {
/* This is required for resuming tid
* during BAR completion */
bf->bf_tidno = txctl->tidno;
}
if (is_multicast_ether_addr(hdr->addr1)) {
struct ath_vap *avp = sc->sc_vaps[txctl->if_id];
/*
* When servicing one or more stations in power-save
* mode (or) if there is some mcast data waiting on
* mcast queue (to prevent out of order delivery of
* mcast,bcast packets) multicast frames must be
* buffered until after the beacon. We use the private
* mcast queue for that.
*/
/* XXX? more bit in 802.11 frame header */
spin_lock_bh(&avp->av_mcastq.axq_lock);
if (txctl->ps || avp->av_mcastq.axq_depth)
ath_tx_mcastqaddbuf(sc,
&avp->av_mcastq, &bf_head);
else
ath_tx_txqaddbuf(sc, txq, &bf_head);
spin_unlock_bh(&avp->av_mcastq.axq_lock);
} else
ath_tx_txqaddbuf(sc, txq, &bf_head);
}
spin_unlock_bh(&txq->axq_lock);
return 0;
}
static void xmit_map_sg(struct ath_softc *sc,
struct sk_buff *skb,
dma_addr_t *pa,
struct ath_tx_control *txctl)
{
struct ath_xmit_status tx_status;
struct ath_atx_tid *tid;
struct scatterlist sg;
*pa = pci_map_single(sc->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
/* setup S/G list */
memset(&sg, 0, sizeof(struct scatterlist));
sg_dma_address(&sg) = *pa;
sg_dma_len(&sg) = skb->len;
if (ath_tx_start_dma(sc, skb, &sg, 1, txctl) != 0) {
/*
* We have to do drop frame here.
*/
pci_unmap_single(sc->pdev, *pa, skb->len, PCI_DMA_TODEVICE);
tx_status.retries = 0;
tx_status.flags = ATH_TX_ERROR;
if (txctl->ht && sc->sc_txaggr) {
/* Reclaim the seqno. */
tid = ATH_AN_2_TID((struct ath_node *)
txctl->an, txctl->tidno);
DECR(tid->seq_next, IEEE80211_SEQ_MAX);
}
ath_tx_complete(sc, skb, &tx_status, txctl->an);
}
}
/* Initialize TX queue and h/w */
int ath_tx_init(struct ath_softc *sc, int nbufs)
{
int error = 0;
do {
spin_lock_init(&sc->sc_txbuflock);
/* Setup tx descriptors */
error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf,
"tx", nbufs * ATH_FRAG_PER_MSDU, ATH_TXDESC);
if (error != 0) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: failed to allocate tx descriptors: %d\n",
__func__, error);
break;
}
/* XXX allocate beacon state together with vap */
error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
"beacon", ATH_BCBUF, 1);
if (error != 0) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: failed to allocate "
"beacon descripotrs: %d\n",
__func__, error);
break;
}
} while (0);
if (error != 0)
ath_tx_cleanup(sc);
return error;
}
/* Reclaim all tx queue resources */
int ath_tx_cleanup(struct ath_softc *sc)
{
/* cleanup beacon descriptors */
if (sc->sc_bdma.dd_desc_len != 0)
ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf);
/* cleanup tx descriptors */
if (sc->sc_txdma.dd_desc_len != 0)
ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
return 0;
}
/* Setup a h/w transmit queue */
struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
{
struct ath_hal *ah = sc->sc_ah;
struct ath9k_tx_queue_info qi;
int qnum;
memzero(&qi, sizeof(qi));
qi.tqi_subtype = subtype;
qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT;
qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT;
qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT;
qi.tqi_physCompBuf = 0;
/*
* Enable interrupts only for EOL and DESC conditions.
* We mark tx descriptors to receive a DESC interrupt
* when a tx queue gets deep; otherwise waiting for the
* EOL to reap descriptors. Note that this is done to
* reduce interrupt load and this only defers reaping
* descriptors, never transmitting frames. Aside from
* reducing interrupts this also permits more concurrency.
* The only potential downside is if the tx queue backs
* up in which case the top half of the kernel may backup
* due to a lack of tx descriptors.
*
* The UAPSD queue is an exception, since we take a desc-
* based intr on the EOSP frames.
*/
if (qtype == ATH9K_TX_QUEUE_UAPSD)
qi.tqi_qflags = TXQ_FLAG_TXDESCINT_ENABLE;
else
qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE |
TXQ_FLAG_TXDESCINT_ENABLE;
qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi);
if (qnum == -1) {
/*
* NB: don't print a message, this happens
* normally on parts with too few tx queues
*/
return NULL;
}
if (qnum >= ARRAY_SIZE(sc->sc_txq)) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: hal qnum %u out of range, max %u!\n",
__func__, qnum, (unsigned int)ARRAY_SIZE(sc->sc_txq));
ath9k_hw_releasetxqueue(ah, qnum);
return NULL;
}
if (!ATH_TXQ_SETUP(sc, qnum)) {
struct ath_txq *txq = &sc->sc_txq[qnum];
txq->axq_qnum = qnum;
txq->axq_link = NULL;
INIT_LIST_HEAD(&txq->axq_q);
INIT_LIST_HEAD(&txq->axq_acq);
spin_lock_init(&txq->axq_lock);
txq->axq_depth = 0;
txq->axq_aggr_depth = 0;
txq->axq_totalqueued = 0;
txq->axq_intrcnt = 0;
txq->axq_linkbuf = NULL;
sc->sc_txqsetup |= 1<<qnum;
}
return &sc->sc_txq[qnum];
}
/* Reclaim resources for a setup queue */
void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
{
ath9k_hw_releasetxqueue(sc->sc_ah, txq->axq_qnum);
sc->sc_txqsetup &= ~(1<<txq->axq_qnum);
}
/*
* Setup a hardware data transmit queue for the specified
* access control. The hal may not support all requested
* queues in which case it will return a reference to a
* previously setup queue. We record the mapping from ac's
* to h/w queues for use by ath_tx_start and also track
* the set of h/w queues being used to optimize work in the
* transmit interrupt handler and related routines.
*/
int ath_tx_setup(struct ath_softc *sc, int haltype)
{
struct ath_txq *txq;
if (haltype >= ARRAY_SIZE(sc->sc_haltype2q)) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: HAL AC %u out of range, max %zu!\n",
__func__, haltype, ARRAY_SIZE(sc->sc_haltype2q));
return 0;
}
txq = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, haltype);
if (txq != NULL) {
sc->sc_haltype2q[haltype] = txq->axq_qnum;
return 1;
} else
return 0;
}
int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype)
{
int qnum;
switch (qtype) {
case ATH9K_TX_QUEUE_DATA:
if (haltype >= ARRAY_SIZE(sc->sc_haltype2q)) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: HAL AC %u out of range, max %zu!\n",
__func__,
haltype, ARRAY_SIZE(sc->sc_haltype2q));
return -1;
}
qnum = sc->sc_haltype2q[haltype];
break;
case ATH9K_TX_QUEUE_BEACON:
qnum = sc->sc_bhalq;
break;
case ATH9K_TX_QUEUE_CAB:
qnum = sc->sc_cabq->axq_qnum;
break;
default:
qnum = -1;
}
return qnum;
}
/* Update parameters for a transmit queue */
int ath_txq_update(struct ath_softc *sc, int qnum,
struct ath9k_tx_queue_info *qinfo)
{
struct ath_hal *ah = sc->sc_ah;
int error = 0;
struct ath9k_tx_queue_info qi;
if (qnum == sc->sc_bhalq) {
/*
* XXX: for beacon queue, we just save the parameter.
* It will be picked up by ath_beaconq_config when
* it's necessary.
*/
sc->sc_beacon_qi = *qinfo;
return 0;
}
ASSERT(sc->sc_txq[qnum].axq_qnum == qnum);
ath9k_hw_get_txq_props(ah, qnum, &qi);
qi.tqi_aifs = qinfo->tqi_aifs;
qi.tqi_cwmin = qinfo->tqi_cwmin;
qi.tqi_cwmax = qinfo->tqi_cwmax;
qi.tqi_burstTime = qinfo->tqi_burstTime;
qi.tqi_readyTime = qinfo->tqi_readyTime;
if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) {
DPRINTF(sc, ATH_DBG_FATAL,
"%s: unable to update hardware queue %u!\n",
__func__, qnum);
error = -EIO;
} else {
ath9k_hw_resettxqueue(ah, qnum); /* push to h/w */
}
return error;
}
int ath_cabq_update(struct ath_softc *sc)
{
struct ath9k_tx_queue_info qi;
int qnum = sc->sc_cabq->axq_qnum;
struct ath_beacon_config conf;
ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
/*
* Ensure the readytime % is within the bounds.
*/
if (sc->sc_config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND)
sc->sc_config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND;
else if (sc->sc_config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND)
sc->sc_config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND;
ath_get_beaconconfig(sc, ATH_IF_ID_ANY, &conf);
qi.tqi_readyTime =
(conf.beacon_interval * sc->sc_config.cabqReadytime) / 100;
ath_txq_update(sc, qnum, &qi);
return 0;
}
int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb)
{
struct ath_tx_control txctl;
int error = 0;
error = ath_tx_prepare(sc, skb, &txctl);
if (error == 0)
/*
* Start DMA mapping.
* ath_tx_start_dma() will be called either synchronously
* or asynchrounsly once DMA is complete.
*/
xmit_map_sg(sc, skb,
get_dma_mem_context(&txctl, dmacontext),
&txctl);
else
ath_node_put(sc, txctl.an, ATH9K_BH_STATUS_CHANGE);
/* failed packets will be dropped by the caller */
return error;
}
/* Deferred processing of transmit interrupt */
void ath_tx_tasklet(struct ath_softc *sc)
{
u64 tsf = ath9k_hw_gettsf64(sc->sc_ah);
int i, nacked = 0;
u32 qcumask = ((1 << ATH9K_NUM_TX_QUEUES) - 1);
ath9k_hw_gettxintrtxqs(sc->sc_ah, &qcumask);
/*
* Process each active queue.
*/
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(sc, i) && (qcumask & (1 << i)))
nacked += ath_tx_processq(sc, &sc->sc_txq[i]);
}
if (nacked)
sc->sc_lastrx = tsf;
}
void ath_tx_draintxq(struct ath_softc *sc,
struct ath_txq *txq, bool retry_tx)
{
struct ath_buf *bf, *lastbf;
struct list_head bf_head;
INIT_LIST_HEAD(&bf_head);
/*
* NB: this assumes output has been stopped and
* we do not need to block ath_tx_tasklet
*/
for (;;) {
spin_lock_bh(&txq->axq_lock);
if (list_empty(&txq->axq_q)) {
txq->axq_link = NULL;
txq->axq_linkbuf = NULL;
spin_unlock_bh(&txq->axq_lock);
break;
}
bf = list_first_entry(&txq->axq_q, struct ath_buf, list);
if (bf->bf_status & ATH_BUFSTATUS_STALE) {
list_del(&bf->list);
spin_unlock_bh(&txq->axq_lock);
spin_lock_bh(&sc->sc_txbuflock);
list_add_tail(&bf->list, &sc->sc_txbuf);
spin_unlock_bh(&sc->sc_txbuflock);
continue;
}
lastbf = bf->bf_lastbf;
if (!retry_tx)
lastbf->bf_desc->ds_txstat.ts_flags =
ATH9K_TX_SW_ABORTED;
/* remove ath_buf's of the same mpdu from txq */
list_cut_position(&bf_head, &txq->axq_q, &lastbf->list);
txq->axq_depth--;
spin_unlock_bh(&txq->axq_lock);
if (bf->bf_isampdu)
ath_tx_complete_aggr_rifs(sc, txq, bf, &bf_head, 0);
else
ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
}
/* flush any pending frames if aggregation is enabled */
if (sc->sc_txaggr) {
if (!retry_tx) {
spin_lock_bh(&txq->axq_lock);
ath_txq_drain_pending_buffers(sc, txq,
ATH9K_BH_STATUS_CHANGE);
spin_unlock_bh(&txq->axq_lock);
}
}
}
/* Drain the transmit queues and reclaim resources */
void ath_draintxq(struct ath_softc *sc, bool retry_tx)
{
/* stop beacon queue. The beacon will be freed when
* we go to INIT state */
if (!sc->sc_invalid) {
(void) ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
DPRINTF(sc, ATH_DBG_XMIT, "%s: beacon queue %x\n", __func__,
ath9k_hw_gettxbuf(sc->sc_ah, sc->sc_bhalq));
}
ath_drain_txdataq(sc, retry_tx);
}
u32 ath_txq_depth(struct ath_softc *sc, int qnum)
{
return sc->sc_txq[qnum].axq_depth;
}
u32 ath_txq_aggr_depth(struct ath_softc *sc, int qnum)
{
return sc->sc_txq[qnum].axq_aggr_depth;
}
/* Check if an ADDBA is required. A valid node must be passed. */
enum ATH_AGGR_CHECK ath_tx_aggr_check(struct ath_softc *sc,
struct ath_node *an,
u8 tidno)
{
struct ath_atx_tid *txtid;
DECLARE_MAC_BUF(mac);
if (!sc->sc_txaggr)
return AGGR_NOT_REQUIRED;
/* ADDBA exchange must be completed before sending aggregates */
txtid = ATH_AN_2_TID(an, tidno);
if (txtid->addba_exchangecomplete)
return AGGR_EXCHANGE_DONE;
if (txtid->cleanup_inprogress)
return AGGR_CLEANUP_PROGRESS;
if (txtid->addba_exchangeinprogress)
return AGGR_EXCHANGE_PROGRESS;
if (!txtid->addba_exchangecomplete) {
if (!txtid->addba_exchangeinprogress &&
(txtid->addba_exchangeattempts < ADDBA_EXCHANGE_ATTEMPTS)) {
txtid->addba_exchangeattempts++;
return AGGR_REQUIRED;
}
}
return AGGR_NOT_REQUIRED;
}
/* Start TX aggregation */
int ath_tx_aggr_start(struct ath_softc *sc,
const u8 *addr,
u16 tid,
u16 *ssn)
{
struct ath_atx_tid *txtid;
struct ath_node *an;
spin_lock_bh(&sc->node_lock);
an = ath_node_find(sc, (u8 *) addr);
spin_unlock_bh(&sc->node_lock);
if (!an) {
DPRINTF(sc, ATH_DBG_AGGR,
"%s: Node not found to initialize "
"TX aggregation\n", __func__);
return -1;
}
if (sc->sc_txaggr) {
txtid = ATH_AN_2_TID(an, tid);
txtid->addba_exchangeinprogress = 1;
ath_tx_pause_tid(sc, txtid);
}
return 0;
}
/* Stop tx aggregation */
int ath_tx_aggr_stop(struct ath_softc *sc,
const u8 *addr,
u16 tid)
{
struct ath_node *an;
spin_lock_bh(&sc->node_lock);
an = ath_node_find(sc, (u8 *) addr);
spin_unlock_bh(&sc->node_lock);
if (!an) {
DPRINTF(sc, ATH_DBG_AGGR,
"%s: TX aggr stop for non-existent node\n", __func__);
return -1;
}
ath_tx_aggr_teardown(sc, an, tid);
return 0;
}
/*
* Performs transmit side cleanup when TID changes from aggregated to
* unaggregated.
* - Pause the TID and mark cleanup in progress
* - Discard all retry frames from the s/w queue.
*/
void ath_tx_aggr_teardown(struct ath_softc *sc,
struct ath_node *an, u8 tid)
{
struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid);
struct ath_txq *txq = &sc->sc_txq[txtid->ac->qnum];
struct ath_buf *bf;
struct list_head bf_head;
INIT_LIST_HEAD(&bf_head);
DPRINTF(sc, ATH_DBG_AGGR, "%s: teardown TX aggregation\n", __func__);
if (txtid->cleanup_inprogress) /* cleanup is in progress */
return;
if (!txtid->addba_exchangecomplete) {
txtid->addba_exchangeattempts = 0;
return;
}
/* TID must be paused first */
ath_tx_pause_tid(sc, txtid);
/* drop all software retried frames and mark this TID */
spin_lock_bh(&txq->axq_lock);
while (!list_empty(&txtid->buf_q)) {
bf = list_first_entry(&txtid->buf_q, struct ath_buf, list);
if (!bf->bf_isretried) {
/*
* NB: it's based on the assumption that
* software retried frame will always stay
* at the head of software queue.
*/
break;
}
list_cut_position(&bf_head,
&txtid->buf_q, &bf->bf_lastfrm->list);
ath_tx_update_baw(sc, txtid, bf->bf_seqno);
/* complete this sub-frame */
ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
}
if (txtid->baw_head != txtid->baw_tail) {
spin_unlock_bh(&txq->axq_lock);
txtid->cleanup_inprogress = true;
} else {
txtid->addba_exchangecomplete = 0;
txtid->addba_exchangeattempts = 0;
spin_unlock_bh(&txq->axq_lock);
ath_tx_flush_tid(sc, txtid);
}
}
/*
* Tx scheduling logic
* NB: must be called with txq lock held
*/
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
{
struct ath_atx_ac *ac;
struct ath_atx_tid *tid;
/* nothing to schedule */
if (list_empty(&txq->axq_acq))
return;
/*
* get the first node/ac pair on the queue
*/
ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
list_del(&ac->list);
ac->sched = false;
/*
* process a single tid per destination
*/
do {
/* nothing to schedule */
if (list_empty(&ac->tid_q))
return;
tid = list_first_entry(&ac->tid_q, struct ath_atx_tid, list);
list_del(&tid->list);
tid->sched = false;
if (tid->paused) /* check next tid to keep h/w busy */
continue;
if (!(tid->an->an_smmode == ATH_SM_PWRSAV_DYNAMIC) ||
((txq->axq_depth % 2) == 0)) {
ath_tx_sched_aggr(sc, txq, tid);
}
/*
* add tid to round-robin queue if more frames
* are pending for the tid
*/
if (!list_empty(&tid->buf_q))
ath_tx_queue_tid(txq, tid);
/* only schedule one TID at a time */
break;
} while (!list_empty(&ac->tid_q));
/*
* schedule AC if more TIDs need processing
*/
if (!list_empty(&ac->tid_q)) {
/*
* add dest ac to txq if not already added
*/
if (!ac->sched) {
ac->sched = true;
list_add_tail(&ac->list, &txq->axq_acq);
}
}
}
/* Initialize per-node transmit state */
void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an)
{
if (sc->sc_txaggr) {
struct ath_atx_tid *tid;
struct ath_atx_ac *ac;
int tidno, acno;
sc->sc_ht_info.maxampdu = ATH_AMPDU_LIMIT_DEFAULT;
/*
* Init per tid tx state
*/
for (tidno = 0, tid = &an->an_aggr.tx.tid[tidno];
tidno < WME_NUM_TID;
tidno++, tid++) {
tid->an = an;
tid->tidno = tidno;
tid->seq_start = tid->seq_next = 0;
tid->baw_size = WME_MAX_BA;
tid->baw_head = tid->baw_tail = 0;
tid->sched = false;
tid->paused = false;
tid->cleanup_inprogress = false;
INIT_LIST_HEAD(&tid->buf_q);
acno = TID_TO_WME_AC(tidno);
tid->ac = &an->an_aggr.tx.ac[acno];
/* ADDBA state */
tid->addba_exchangecomplete = 0;
tid->addba_exchangeinprogress = 0;
tid->addba_exchangeattempts = 0;
}
/*
* Init per ac tx state
*/
for (acno = 0, ac = &an->an_aggr.tx.ac[acno];
acno < WME_NUM_AC; acno++, ac++) {
ac->sched = false;
INIT_LIST_HEAD(&ac->tid_q);
switch (acno) {
case WME_AC_BE:
ac->qnum = ath_tx_get_qnum(sc,
ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
break;
case WME_AC_BK:
ac->qnum = ath_tx_get_qnum(sc,
ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BK);
break;
case WME_AC_VI:
ac->qnum = ath_tx_get_qnum(sc,
ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VI);
break;
case WME_AC_VO:
ac->qnum = ath_tx_get_qnum(sc,
ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VO);
break;
}
}
}
}
/* Cleanupthe pending buffers for the node. */
void ath_tx_node_cleanup(struct ath_softc *sc,
struct ath_node *an, bool bh_flag)
{
int i;
struct ath_atx_ac *ac, *ac_tmp;
struct ath_atx_tid *tid, *tid_tmp;
struct ath_txq *txq;
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(sc, i)) {
txq = &sc->sc_txq[i];
if (likely(bh_flag))
spin_lock_bh(&txq->axq_lock);
else
spin_lock(&txq->axq_lock);
list_for_each_entry_safe(ac,
ac_tmp, &txq->axq_acq, list) {
tid = list_first_entry(&ac->tid_q,
struct ath_atx_tid, list);
if (tid && tid->an != an)
continue;
list_del(&ac->list);
ac->sched = false;
list_for_each_entry_safe(tid,
tid_tmp, &ac->tid_q, list) {
list_del(&tid->list);
tid->sched = false;
ath_tid_drain(sc, txq, tid, bh_flag);
tid->addba_exchangecomplete = 0;
tid->addba_exchangeattempts = 0;
tid->cleanup_inprogress = false;
}
}
if (likely(bh_flag))
spin_unlock_bh(&txq->axq_lock);
else
spin_unlock(&txq->axq_lock);
}
}
}
/* Cleanup per node transmit state */
void ath_tx_node_free(struct ath_softc *sc, struct ath_node *an)
{
if (sc->sc_txaggr) {
struct ath_atx_tid *tid;
int tidno, i;
/* Init per tid rx state */
for (tidno = 0, tid = &an->an_aggr.tx.tid[tidno];
tidno < WME_NUM_TID;
tidno++, tid++) {
for (i = 0; i < ATH_TID_MAX_BUFS; i++)
ASSERT(tid->tx_buf[i] == NULL);
}
}
}
......@@ -932,7 +932,7 @@ static struct ehci_qh *qh_append_tds (
list_del (&qtd->qtd_list);
list_add (&dummy->qtd_list, qtd_list);
__list_splice (qtd_list, qh->qtd_list.prev);
list_splice_tail(qtd_list, &qh->qtd_list);
ehci_qtd_init(ehci, qtd, qtd->qtd_dma);
qh->dummy = qtd;
......
......@@ -214,22 +214,62 @@ static inline int list_is_singular(const struct list_head *head)
return !list_empty(head) && (head->next == head->prev);
}
static inline void __list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
struct list_head *new_first = entry->next;
list->next = head->next;
list->next->prev = list;
list->prev = entry;
entry->next = list;
head->next = new_first;
new_first->prev = head;
}
/**
* list_cut_position - cut a list into two
* @list: a new list to add all removed entries
* @head: a list with entries
* @entry: an entry within head, could be the head itself
* and if so we won't cut the list
*
* This helper moves the initial part of @head, up to and
* including @entry, from @head to @list. You should
* pass on @entry an element you know is on @head. @list
* should be an empty list or a list you do not care about
* losing its data.
*
*/
static inline void list_cut_position(struct list_head *list,
struct list_head *head, struct list_head *entry)
{
if (list_empty(head))
return;
if (list_is_singular(head) &&
(head->next != entry && head != entry))
return;
if (entry == head)
INIT_LIST_HEAD(list);
else
__list_cut_position(list, head, entry);
}
static inline void __list_splice(const struct list_head *list,
struct list_head *head)
struct list_head *prev,
struct list_head *next)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
first->prev = prev;
prev->next = first;
last->next = at;
at->prev = last;
last->next = next;
next->prev = last;
}
/**
* list_splice - join two lists
* list_splice - join two lists, this is designed for stacks
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
......@@ -237,7 +277,19 @@ static inline void list_splice(const struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head);
__list_splice(list, head, head->next);
}
/**
* list_splice_tail - join two lists, each list being a queue
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice_tail(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head->prev, head);
}
/**
......@@ -251,7 +303,24 @@ static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head);
__list_splice(list, head, head->next);
INIT_LIST_HEAD(list);
}
}
/**
* list_splice_tail_init - join two lists, each list being a queue, and
* reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_tail_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head->prev, head);
INIT_LIST_HEAD(list);
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment