atheros: put atheros wireless drivers into ath/

Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Luis R. Rodriguez
2009-03-30 22:30:33 -04:00
committed by John W. Linville
parent 1878f77e13
commit 203c4805e9
69 changed files with 12 additions and 10 deletions

View File

@@ -1,4 +1,8 @@
config ATH_COMMON
tristate "Atheros Wireless Cards Shared Support"
tristate "Atheros Wireless Cards"
depends on ATH5K || ATH9K || AR9170_USB
source "drivers/net/wireless/ath/ath5k/Kconfig"
source "drivers/net/wireless/ath/ath9k/Kconfig"
source "drivers/net/wireless/ath/ar9170/Kconfig"

View File

@@ -1,3 +1,7 @@
obj-$(CONFIG_ATH5K) += ath5k/
obj-$(CONFIG_ATH9K) += ath9k/
obj-$(CONFIG_AR9170_USB) += ar9170/
obj-$(CONFIG_ATH_COMMON) += ath.o
ath-objs := regd.o

View File

@@ -0,0 +1,18 @@
config AR9170_USB
tristate "Atheros AR9170 802.11n USB support"
depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL
select FW_LOADER
select ATH_COMMON
help
This is a driver for the Atheros "otus" 802.11n USB devices.
These devices require additional firmware (2 files).
For now, these files can be downloaded from here:
http://wireless.kernel.org/en/users/Drivers/ar9170
If you choose to build a module, it'll be called ar9170usb.
config AR9170_LEDS
bool
depends on AR9170_USB && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = AR9170_USB)
default y

View File

@@ -0,0 +1,3 @@
ar9170usb-objs := usb.o main.o cmd.o mac.o phy.o led.o
obj-$(CONFIG_AR9170_USB) += ar9170usb.o

View File

@@ -0,0 +1,212 @@
/*
* Atheros AR9170 driver
*
* Driver specific definitions
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-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 __AR9170_H
#define __AR9170_H
#include <linux/completion.h>
#include <linux/spinlock.h>
#include <net/wireless.h>
#include <net/mac80211.h>
#ifdef CONFIG_AR9170_LEDS
#include <linux/leds.h>
#endif /* CONFIG_AR9170_LEDS */
#include "eeprom.h"
#include "hw.h"
#include "../regd.h"
#define PAYLOAD_MAX (AR9170_MAX_CMD_LEN/4 - 1)
enum ar9170_bw {
AR9170_BW_20,
AR9170_BW_40_BELOW,
AR9170_BW_40_ABOVE,
__AR9170_NUM_BW,
};
enum ar9170_rf_init_mode {
AR9170_RFI_NONE,
AR9170_RFI_WARM,
AR9170_RFI_COLD,
};
#define AR9170_MAX_RX_BUFFER_SIZE 8192
#ifdef CONFIG_AR9170_LEDS
struct ar9170;
struct ar9170_led {
struct ar9170 *ar;
struct led_classdev l;
char name[32];
unsigned int toggled;
bool registered;
};
#endif /* CONFIG_AR9170_LEDS */
enum ar9170_device_state {
AR9170_UNKNOWN_STATE,
AR9170_STOPPED,
AR9170_IDLE,
AR9170_STARTED,
AR9170_ASSOCIATED,
};
struct ar9170 {
struct ieee80211_hw *hw;
struct mutex mutex;
enum ar9170_device_state state;
int (*open)(struct ar9170 *);
void (*stop)(struct ar9170 *);
int (*tx)(struct ar9170 *, struct sk_buff *, bool, unsigned int);
int (*exec_cmd)(struct ar9170 *, enum ar9170_cmd, u32 ,
void *, u32 , void *);
void (*callback_cmd)(struct ar9170 *, u32 , void *);
/* interface mode settings */
struct ieee80211_vif *vif;
u8 mac_addr[ETH_ALEN];
u8 bssid[ETH_ALEN];
/* beaconing */
struct sk_buff *beacon;
struct work_struct beacon_work;
/* cryptographic engine */
u64 usedkeys;
bool rx_software_decryption;
bool disable_offload;
/* filter settings */
struct work_struct filter_config_work;
u64 cur_mc_hash, want_mc_hash;
u32 cur_filter, want_filter;
unsigned int filter_changed;
bool sniffer_enabled;
/* PHY */
struct ieee80211_channel *channel;
int noise[4];
/* power calibration data */
u8 power_5G_leg[4];
u8 power_2G_cck[4];
u8 power_2G_ofdm[4];
u8 power_5G_ht20[8];
u8 power_5G_ht40[8];
u8 power_2G_ht20[8];
u8 power_2G_ht40[8];
#ifdef CONFIG_AR9170_LEDS
struct delayed_work led_work;
struct ar9170_led leds[AR9170_NUM_LEDS];
#endif /* CONFIG_AR9170_LEDS */
/* qos queue settings */
spinlock_t tx_stats_lock;
struct ieee80211_tx_queue_stats tx_stats[5];
struct ieee80211_tx_queue_params edcf[5];
spinlock_t cmdlock;
__le32 cmdbuf[PAYLOAD_MAX + 1];
/* MAC statistics */
struct ieee80211_low_level_stats stats;
/* EEPROM */
struct ar9170_eeprom eeprom;
struct ath_regulatory regulatory;
/* global tx status for unregistered Stations. */
struct sk_buff_head global_tx_status;
struct sk_buff_head global_tx_status_waste;
struct delayed_work tx_status_janitor;
};
struct ar9170_sta_info {
struct sk_buff_head tx_status[__AR9170_NUM_TXQ];
};
#define IS_STARTED(a) (a->state >= AR9170_STARTED)
#define IS_ACCEPTING_CMD(a) (a->state >= AR9170_IDLE)
#define AR9170_FILTER_CHANGED_PROMISC BIT(0)
#define AR9170_FILTER_CHANGED_MULTICAST BIT(1)
#define AR9170_FILTER_CHANGED_FRAMEFILTER BIT(2)
/* exported interface */
void *ar9170_alloc(size_t priv_size);
int ar9170_register(struct ar9170 *ar, struct device *pdev);
void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb);
void ar9170_unregister(struct ar9170 *ar);
void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb,
bool update_statistics, u16 tx_status);
/* MAC */
int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
int ar9170_init_mac(struct ar9170 *ar);
int ar9170_set_qos(struct ar9170 *ar);
int ar9170_update_multicast(struct ar9170 *ar);
int ar9170_update_frame_filter(struct ar9170 *ar);
int ar9170_set_operating_mode(struct ar9170 *ar);
int ar9170_set_beacon_timers(struct ar9170 *ar);
int ar9170_set_hwretry_limit(struct ar9170 *ar, u32 max_retry);
int ar9170_update_beacon(struct ar9170 *ar);
void ar9170_new_beacon(struct work_struct *work);
int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype,
u8 keyidx, u8 *keydata, int keylen);
int ar9170_disable_key(struct ar9170 *ar, u8 id);
/* LEDs */
#ifdef CONFIG_AR9170_LEDS
int ar9170_register_leds(struct ar9170 *ar);
void ar9170_unregister_leds(struct ar9170 *ar);
#endif /* CONFIG_AR9170_LEDS */
int ar9170_init_leds(struct ar9170 *ar);
int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state);
/* PHY / RF */
int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band);
int ar9170_init_rf(struct ar9170 *ar);
int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
enum ar9170_rf_init_mode rfi, enum ar9170_bw bw);
#endif /* __AR9170_H */

View File

@@ -0,0 +1,129 @@
/*
* Atheros AR9170 driver
*
* Basic HW register/memory/command access functions
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-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 "ar9170.h"
#include "cmd.h"
int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len)
{
int err;
if (unlikely(!IS_ACCEPTING_CMD(ar)))
return 0;
err = ar->exec_cmd(ar, AR9170_CMD_WMEM, len, (u8 *) data, 0, NULL);
if (err)
printk(KERN_DEBUG "%s: writing memory failed\n",
wiphy_name(ar->hw->wiphy));
return err;
}
int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val)
{
__le32 buf[2] = {
cpu_to_le32(reg),
cpu_to_le32(val),
};
int err;
if (unlikely(!IS_ACCEPTING_CMD(ar)))
return 0;
err = ar->exec_cmd(ar, AR9170_CMD_WREG, sizeof(buf),
(u8 *) buf, 0, NULL);
if (err)
printk(KERN_DEBUG "%s: writing reg %#x (val %#x) failed\n",
wiphy_name(ar->hw->wiphy), reg, val);
return err;
}
static int ar9170_read_mreg(struct ar9170 *ar, int nregs,
const u32 *regs, u32 *out)
{
int i, err;
__le32 *offs, *res;
if (unlikely(!IS_ACCEPTING_CMD(ar)))
return 0;
/* abuse "out" for the register offsets, must be same length */
offs = (__le32 *)out;
for (i = 0; i < nregs; i++)
offs[i] = cpu_to_le32(regs[i]);
/* also use the same buffer for the input */
res = (__le32 *)out;
err = ar->exec_cmd(ar, AR9170_CMD_RREG,
4 * nregs, (u8 *)offs,
4 * nregs, (u8 *)res);
if (err)
return err;
/* convert result to cpu endian */
for (i = 0; i < nregs; i++)
out[i] = le32_to_cpu(res[i]);
return 0;
}
int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val)
{
return ar9170_read_mreg(ar, 1, &reg, val);
}
int ar9170_echo_test(struct ar9170 *ar, u32 v)
{
__le32 echobuf = cpu_to_le32(v);
__le32 echores;
int err;
if (unlikely(!IS_ACCEPTING_CMD(ar)))
return -ENODEV;
err = ar->exec_cmd(ar, AR9170_CMD_ECHO,
4, (u8 *)&echobuf,
4, (u8 *)&echores);
if (err)
return err;
if (echobuf != echores)
return -EINVAL;
return 0;
}

View File

@@ -0,0 +1,91 @@
/*
* Atheros AR9170 driver
*
* Basic HW register/memory/command access functions
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-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 __CMD_H
#define __CMD_H
#include "ar9170.h"
/* basic HW access */
int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len);
int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val);
int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val);
int ar9170_echo_test(struct ar9170 *ar, u32 v);
/*
* Macros to facilitate writing multiple registers in a single
* write-combining USB command. Note that when the first group
* fails the whole thing will fail without any others attempted,
* but you won't know which write in the group failed.
*/
#define ar9170_regwrite_begin(ar) \
do { \
int __nreg = 0, __err = 0; \
struct ar9170 *__ar = ar;
#define ar9170_regwrite(r, v) do { \
__ar->cmdbuf[2 * __nreg + 1] = cpu_to_le32(r); \
__ar->cmdbuf[2 * __nreg + 2] = cpu_to_le32(v); \
__nreg++; \
if ((__nreg >= PAYLOAD_MAX/2)) { \
if (IS_ACCEPTING_CMD(__ar)) \
__err = ar->exec_cmd(__ar, AR9170_CMD_WREG, \
8 * __nreg, \
(u8 *) &__ar->cmdbuf[1], \
0, NULL); \
__nreg = 0; \
if (__err) \
goto __regwrite_out; \
} \
} while (0)
#define ar9170_regwrite_finish() \
__regwrite_out : \
if (__nreg) { \
if (IS_ACCEPTING_CMD(__ar)) \
__err = ar->exec_cmd(__ar, AR9170_CMD_WREG, \
8 * __nreg, \
(u8 *) &__ar->cmdbuf[1], \
0, NULL); \
__nreg = 0; \
}
#define ar9170_regwrite_result() \
__err; \
} while (0);
#endif /* __CMD_H */

View File

@@ -0,0 +1,179 @@
/*
* Atheros AR9170 driver
*
* EEPROM layout
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-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 __AR9170_EEPROM_H
#define __AR9170_EEPROM_H
#define AR5416_MAX_CHAINS 2
#define AR5416_MODAL_SPURS 5
struct ar9170_eeprom_modal {
__le32 antCtrlChain[AR5416_MAX_CHAINS];
__le32 antCtrlCommon;
s8 antennaGainCh[AR5416_MAX_CHAINS];
u8 switchSettling;
u8 txRxAttenCh[AR5416_MAX_CHAINS];
u8 rxTxMarginCh[AR5416_MAX_CHAINS];
s8 adcDesiredSize;
s8 pgaDesiredSize;
u8 xlnaGainCh[AR5416_MAX_CHAINS];
u8 txEndToXpaOff;
u8 txEndToRxOn;
u8 txFrameToXpaOn;
u8 thresh62;
s8 noiseFloorThreshCh[AR5416_MAX_CHAINS];
u8 xpdGain;
u8 xpd;
s8 iqCalICh[AR5416_MAX_CHAINS];
s8 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 reserved[22];
struct spur_channel {
__le16 spurChan;
u8 spurRangeLow;
u8 spurRangeHigh;
} __packed spur_channels[AR5416_MODAL_SPURS];
} __packed;
#define AR5416_NUM_PD_GAINS 4
#define AR5416_PD_GAIN_ICEPTS 5
struct ar9170_calibration_data_per_freq {
u8 pwr_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
u8 vpd_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
} __packed;
#define AR5416_NUM_5G_CAL_PIERS 8
#define AR5416_NUM_2G_CAL_PIERS 4
#define AR5416_NUM_5G_TARGET_PWRS 8
#define AR5416_NUM_2G_CCK_TARGET_PWRS 3
#define AR5416_NUM_2G_OFDM_TARGET_PWRS 4
#define AR5416_MAX_NUM_TGT_PWRS 8
struct ar9170_calibration_target_power_legacy {
u8 freq;
u8 power[4];
} __packed;
struct ar9170_calibration_target_power_ht {
u8 freq;
u8 power[8];
} __packed;
#define AR5416_NUM_CTLS 24
struct ar9170_calctl_edges {
u8 channel;
#define AR9170_CALCTL_EDGE_FLAGS 0xC0
u8 power_flags;
} __packed;
#define AR5416_NUM_BAND_EDGES 8
struct ar9170_calctl_data {
struct ar9170_calctl_edges
control_edges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES];
} __packed;
struct ar9170_eeprom {
__le16 length;
__le16 checksum;
__le16 version;
u8 operating_flags;
#define AR9170_OPFLAG_5GHZ 1
#define AR9170_OPFLAG_2GHZ 2
u8 misc;
__le16 reg_domain[2];
u8 mac_address[6];
u8 rx_mask;
u8 tx_mask;
__le16 rf_silent;
__le16 bluetooth_options;
__le16 device_capabilities;
__le32 build_number;
u8 deviceType;
u8 reserved[33];
u8 customer_data[64];
struct ar9170_eeprom_modal
modal_header[2];
u8 cal_freq_pier_5G[AR5416_NUM_5G_CAL_PIERS];
u8 cal_freq_pier_2G[AR5416_NUM_2G_CAL_PIERS];
struct ar9170_calibration_data_per_freq
cal_pier_data_5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS],
cal_pier_data_2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS];
/* power calibration data */
struct ar9170_calibration_target_power_legacy
cal_tgt_pwr_5G[AR5416_NUM_5G_TARGET_PWRS];
struct ar9170_calibration_target_power_ht
cal_tgt_pwr_5G_ht20[AR5416_NUM_5G_TARGET_PWRS],
cal_tgt_pwr_5G_ht40[AR5416_NUM_5G_TARGET_PWRS];
struct ar9170_calibration_target_power_legacy
cal_tgt_pwr_2G_cck[AR5416_NUM_2G_CCK_TARGET_PWRS],
cal_tgt_pwr_2G_ofdm[AR5416_NUM_2G_OFDM_TARGET_PWRS];
struct ar9170_calibration_target_power_ht
cal_tgt_pwr_2G_ht20[AR5416_NUM_2G_OFDM_TARGET_PWRS],
cal_tgt_pwr_2G_ht40[AR5416_NUM_2G_OFDM_TARGET_PWRS];
/* conformance testing limits */
u8 ctl_index[AR5416_NUM_CTLS];
struct ar9170_calctl_data
ctl_data[AR5416_NUM_CTLS];
u8 pad;
__le16 subsystem_id;
} __packed;
#endif /* __AR9170_EEPROM_H */

View File

@@ -0,0 +1,417 @@
/*
* Atheros AR9170 driver
*
* Hardware-specific definitions
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-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 __AR9170_HW_H
#define __AR9170_HW_H
#define AR9170_MAX_CMD_LEN 64
enum ar9170_cmd {
AR9170_CMD_RREG = 0x00,
AR9170_CMD_WREG = 0x01,
AR9170_CMD_RMEM = 0x02,
AR9170_CMD_WMEM = 0x03,
AR9170_CMD_BITAND = 0x04,
AR9170_CMD_BITOR = 0x05,
AR9170_CMD_EKEY = 0x28,
AR9170_CMD_DKEY = 0x29,
AR9170_CMD_FREQUENCY = 0x30,
AR9170_CMD_RF_INIT = 0x31,
AR9170_CMD_SYNTH = 0x32,
AR9170_CMD_FREQ_START = 0x33,
AR9170_CMD_ECHO = 0x80,
AR9170_CMD_TALLY = 0x81,
AR9170_CMD_TALLY_APD = 0x82,
AR9170_CMD_CONFIG = 0x83,
AR9170_CMD_RESET = 0x90,
AR9170_CMD_DKRESET = 0x91,
AR9170_CMD_DKTX_STATUS = 0x92,
AR9170_CMD_FDC = 0xA0,
AR9170_CMD_WREEPROM = 0xB0,
AR9170_CMD_WFLASH = 0xB0,
AR9170_CMD_FLASH_ERASE = 0xB1,
AR9170_CMD_FLASH_PROG = 0xB2,
AR9170_CMD_FLASH_CHKSUM = 0xB3,
AR9170_CMD_FLASH_READ = 0xB4,
AR9170_CMD_FW_DL_INIT = 0xB5,
AR9170_CMD_MEM_WREEPROM = 0xBB,
};
/* endpoints */
#define AR9170_EP_TX 1
#define AR9170_EP_RX 2
#define AR9170_EP_IRQ 3
#define AR9170_EP_CMD 4
#define AR9170_EEPROM_START 0x1600
#define AR9170_GPIO_REG_BASE 0x1d0100
#define AR9170_GPIO_REG_PORT_TYPE AR9170_GPIO_REG_BASE
#define AR9170_GPIO_REG_DATA (AR9170_GPIO_REG_BASE + 4)
#define AR9170_NUM_LEDS 2
#define AR9170_USB_REG_BASE 0x1e1000
#define AR9170_USB_REG_DMA_CTL (AR9170_USB_REG_BASE + 0x108)
#define AR9170_DMA_CTL_ENABLE_TO_DEVICE 0x1
#define AR9170_DMA_CTL_ENABLE_FROM_DEVICE 0x2
#define AR9170_DMA_CTL_HIGH_SPEED 0x4
#define AR9170_DMA_CTL_PACKET_MODE 0x8
#define AR9170_USB_REG_MAX_AGG_UPLOAD (AR9170_USB_REG_BASE + 0x110)
#define AR9170_USB_REG_UPLOAD_TIME_CTL (AR9170_USB_REG_BASE + 0x114)
#define AR9170_MAC_REG_BASE 0x1c3000
#define AR9170_MAC_REG_TSF_L (AR9170_MAC_REG_BASE + 0x514)
#define AR9170_MAC_REG_TSF_H (AR9170_MAC_REG_BASE + 0x518)
#define AR9170_MAC_REG_ATIM_WINDOW (AR9170_MAC_REG_BASE + 0x51C)
#define AR9170_MAC_REG_BCN_PERIOD (AR9170_MAC_REG_BASE + 0x520)
#define AR9170_MAC_REG_PRETBTT (AR9170_MAC_REG_BASE + 0x524)
#define AR9170_MAC_REG_MAC_ADDR_L (AR9170_MAC_REG_BASE + 0x610)
#define AR9170_MAC_REG_MAC_ADDR_H (AR9170_MAC_REG_BASE + 0x614)
#define AR9170_MAC_REG_BSSID_L (AR9170_MAC_REG_BASE + 0x618)
#define AR9170_MAC_REG_BSSID_H (AR9170_MAC_REG_BASE + 0x61c)
#define AR9170_MAC_REG_GROUP_HASH_TBL_L (AR9170_MAC_REG_BASE + 0x624)
#define AR9170_MAC_REG_GROUP_HASH_TBL_H (AR9170_MAC_REG_BASE + 0x628)
#define AR9170_MAC_REG_RX_TIMEOUT (AR9170_MAC_REG_BASE + 0x62C)
#define AR9170_MAC_REG_BASIC_RATE (AR9170_MAC_REG_BASE + 0x630)
#define AR9170_MAC_REG_MANDATORY_RATE (AR9170_MAC_REG_BASE + 0x634)
#define AR9170_MAC_REG_RTS_CTS_RATE (AR9170_MAC_REG_BASE + 0x638)
#define AR9170_MAC_REG_BACKOFF_PROTECT (AR9170_MAC_REG_BASE + 0x63c)
#define AR9170_MAC_REG_RX_THRESHOLD (AR9170_MAC_REG_BASE + 0x640)
#define AR9170_MAC_REG_RX_PE_DELAY (AR9170_MAC_REG_BASE + 0x64C)
#define AR9170_MAC_REG_DYNAMIC_SIFS_ACK (AR9170_MAC_REG_BASE + 0x658)
#define AR9170_MAC_REG_SNIFFER (AR9170_MAC_REG_BASE + 0x674)
#define AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC BIT(0)
#define AR9170_MAC_REG_SNIFFER_DEFAULTS 0x02000000
#define AR9170_MAC_REG_ENCRYPTION (AR9170_MAC_REG_BASE + 0x678)
#define AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE BIT(3)
#define AR9170_MAC_REG_ENCRYPTION_DEFAULTS 0x70
#define AR9170_MAC_REG_MISC_680 (AR9170_MAC_REG_BASE + 0x680)
#define AR9170_MAC_REG_TX_UNDERRUN (AR9170_MAC_REG_BASE + 0x688)
#define AR9170_MAC_REG_FRAMETYPE_FILTER (AR9170_MAC_REG_BASE + 0x68c)
#define AR9170_MAC_REG_FTF_ASSOC_REQ BIT(0)
#define AR9170_MAC_REG_FTF_ASSOC_RESP BIT(1)
#define AR9170_MAC_REG_FTF_REASSOC_REQ BIT(2)
#define AR9170_MAC_REG_FTF_REASSOC_RESP BIT(3)
#define AR9170_MAC_REG_FTF_PRB_REQ BIT(4)
#define AR9170_MAC_REG_FTF_PRB_RESP BIT(5)
#define AR9170_MAC_REG_FTF_BIT6 BIT(6)
#define AR9170_MAC_REG_FTF_BIT7 BIT(7)
#define AR9170_MAC_REG_FTF_BEACON BIT(8)
#define AR9170_MAC_REG_FTF_ATIM BIT(9)
#define AR9170_MAC_REG_FTF_DEASSOC BIT(10)
#define AR9170_MAC_REG_FTF_AUTH BIT(11)
#define AR9170_MAC_REG_FTF_DEAUTH BIT(12)
#define AR9170_MAC_REG_FTF_BIT13 BIT(13)
#define AR9170_MAC_REG_FTF_BIT14 BIT(14)
#define AR9170_MAC_REG_FTF_BIT15 BIT(15)
#define AR9170_MAC_REG_FTF_BAR BIT(24)
#define AR9170_MAC_REG_FTF_BIT25 BIT(25)
#define AR9170_MAC_REG_FTF_PSPOLL BIT(26)
#define AR9170_MAC_REG_FTF_RTS BIT(27)
#define AR9170_MAC_REG_FTF_CTS BIT(28)
#define AR9170_MAC_REG_FTF_ACK BIT(29)
#define AR9170_MAC_REG_FTF_CFE BIT(30)
#define AR9170_MAC_REG_FTF_CFE_ACK BIT(31)
#define AR9170_MAC_REG_FTF_DEFAULTS 0x0500ffff
#define AR9170_MAC_REG_FTF_MONITOR 0xfd00ffff
#define AR9170_MAC_REG_RX_TOTAL (AR9170_MAC_REG_BASE + 0x6A0)
#define AR9170_MAC_REG_RX_CRC32 (AR9170_MAC_REG_BASE + 0x6A4)
#define AR9170_MAC_REG_RX_CRC16 (AR9170_MAC_REG_BASE + 0x6A8)
#define AR9170_MAC_REG_RX_ERR_DECRYPTION_UNI (AR9170_MAC_REG_BASE + 0x6AC)
#define AR9170_MAC_REG_RX_OVERRUN (AR9170_MAC_REG_BASE + 0x6B0)
#define AR9170_MAC_REG_RX_ERR_DECRYPTION_MUL (AR9170_MAC_REG_BASE + 0x6BC)
#define AR9170_MAC_REG_TX_RETRY (AR9170_MAC_REG_BASE + 0x6CC)
#define AR9170_MAC_REG_TX_TOTAL (AR9170_MAC_REG_BASE + 0x6F4)
#define AR9170_MAC_REG_ACK_EXTENSION (AR9170_MAC_REG_BASE + 0x690)
#define AR9170_MAC_REG_EIFS_AND_SIFS (AR9170_MAC_REG_BASE + 0x698)
#define AR9170_MAC_REG_SLOT_TIME (AR9170_MAC_REG_BASE + 0x6F0)
#define AR9170_MAC_REG_POWERMANAGEMENT (AR9170_MAC_REG_BASE + 0x700)
#define AR9170_MAC_REG_POWERMGT_IBSS 0xe0
#define AR9170_MAC_REG_POWERMGT_AP 0xa1
#define AR9170_MAC_REG_POWERMGT_STA 0x2
#define AR9170_MAC_REG_POWERMGT_AP_WDS 0x3
#define AR9170_MAC_REG_POWERMGT_DEFAULTS (0xf << 24)
#define AR9170_MAC_REG_ROLL_CALL_TBL_L (AR9170_MAC_REG_BASE + 0x704)
#define AR9170_MAC_REG_ROLL_CALL_TBL_H (AR9170_MAC_REG_BASE + 0x708)
#define AR9170_MAC_REG_AC0_CW (AR9170_MAC_REG_BASE + 0xB00)
#define AR9170_MAC_REG_AC1_CW (AR9170_MAC_REG_BASE + 0xB04)
#define AR9170_MAC_REG_AC2_CW (AR9170_MAC_REG_BASE + 0xB08)
#define AR9170_MAC_REG_AC3_CW (AR9170_MAC_REG_BASE + 0xB0C)
#define AR9170_MAC_REG_AC4_CW (AR9170_MAC_REG_BASE + 0xB10)
#define AR9170_MAC_REG_AC1_AC0_AIFS (AR9170_MAC_REG_BASE + 0xB14)
#define AR9170_MAC_REG_AC3_AC2_AIFS (AR9170_MAC_REG_BASE + 0xB18)
#define AR9170_MAC_REG_RETRY_MAX (AR9170_MAC_REG_BASE + 0xB28)
#define AR9170_MAC_REG_FCS_SELECT (AR9170_MAC_REG_BASE + 0xBB0)
#define AR9170_MAC_FCS_SWFCS 0x1
#define AR9170_MAC_FCS_FIFO_PROT 0x4
#define AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND (AR9170_MAC_REG_BASE + 0xB30)
#define AR9170_MAC_REG_AC1_AC0_TXOP (AR9170_MAC_REG_BASE + 0xB44)
#define AR9170_MAC_REG_AC3_AC2_TXOP (AR9170_MAC_REG_BASE + 0xB48)
#define AR9170_MAC_REG_ACK_TABLE (AR9170_MAC_REG_BASE + 0xC00)
#define AR9170_MAC_REG_AMPDU_RX_THRESH (AR9170_MAC_REG_BASE + 0xC50)
#define AR9170_MAC_REG_TXRX_MPI (AR9170_MAC_REG_BASE + 0xD7C)
#define AR9170_MAC_TXRX_MPI_TX_MPI_MASK 0x0000000f
#define AR9170_MAC_TXRX_MPI_TX_TO_MASK 0x0000fff0
#define AR9170_MAC_TXRX_MPI_RX_MPI_MASK 0x000f0000
#define AR9170_MAC_TXRX_MPI_RX_TO_MASK 0xfff00000
#define AR9170_MAC_REG_BCN_ADDR (AR9170_MAC_REG_BASE + 0xD84)
#define AR9170_MAC_REG_BCN_LENGTH (AR9170_MAC_REG_BASE + 0xD88)
#define AR9170_MAC_REG_BCN_PLCP (AR9170_MAC_REG_BASE + 0xD90)
#define AR9170_MAC_REG_BCN_CTRL (AR9170_MAC_REG_BASE + 0xD94)
#define AR9170_MAC_REG_BCN_HT1 (AR9170_MAC_REG_BASE + 0xDA0)
#define AR9170_MAC_REG_BCN_HT2 (AR9170_MAC_REG_BASE + 0xDA4)
#define AR9170_PWR_REG_BASE 0x1D4000
#define AR9170_PWR_REG_CLOCK_SEL (AR9170_PWR_REG_BASE + 0x008)
#define AR9170_PWR_CLK_AHB_40MHZ 0
#define AR9170_PWR_CLK_AHB_20_22MHZ 1
#define AR9170_PWR_CLK_AHB_40_44MHZ 2
#define AR9170_PWR_CLK_AHB_80_88MHZ 3
#define AR9170_PWR_CLK_DAC_160_INV_DLY 0x70
/* put beacon here in memory */
#define AR9170_BEACON_BUFFER_ADDRESS 0x117900
struct ar9170_tx_control {
__le16 length;
__le16 mac_control;
__le32 phy_control;
u8 frame_data[0];
} __packed;
/* these are either-or */
#define AR9170_TX_MAC_PROT_RTS 0x0001
#define AR9170_TX_MAC_PROT_CTS 0x0002
#define AR9170_TX_MAC_NO_ACK 0x0004
/* if unset, MAC will only do SIFS space before frame */
#define AR9170_TX_MAC_BACKOFF 0x0008
#define AR9170_TX_MAC_BURST 0x0010
#define AR9170_TX_MAC_AGGR 0x0020
/* encryption is a two-bit field */
#define AR9170_TX_MAC_ENCR_NONE 0x0000
#define AR9170_TX_MAC_ENCR_RC4 0x0040
#define AR9170_TX_MAC_ENCR_CENC 0x0080
#define AR9170_TX_MAC_ENCR_AES 0x00c0
#define AR9170_TX_MAC_MMIC 0x0100
#define AR9170_TX_MAC_HW_DURATION 0x0200
#define AR9170_TX_MAC_QOS_SHIFT 10
#define AR9170_TX_MAC_QOS_MASK (3 << AR9170_TX_MAC_QOS_SHIFT)
#define AR9170_TX_MAC_AGGR_QOS_BIT1 0x0400
#define AR9170_TX_MAC_AGGR_QOS_BIT2 0x0800
#define AR9170_TX_MAC_DISABLE_TXOP 0x1000
#define AR9170_TX_MAC_TXOP_RIFS 0x2000
#define AR9170_TX_MAC_IMM_AMPDU 0x4000
#define AR9170_TX_MAC_RATE_PROBE 0x8000
/* either-or */
#define AR9170_TX_PHY_MOD_CCK 0x00000000
#define AR9170_TX_PHY_MOD_OFDM 0x00000001
#define AR9170_TX_PHY_MOD_HT 0x00000002
/* depends on modulation */
#define AR9170_TX_PHY_SHORT_PREAMBLE 0x00000004
#define AR9170_TX_PHY_GREENFIELD 0x00000004
#define AR9170_TX_PHY_BW_SHIFT 3
#define AR9170_TX_PHY_BW_MASK (3 << AR9170_TX_PHY_BW_SHIFT)
#define AR9170_TX_PHY_BW_20MHZ 0
#define AR9170_TX_PHY_BW_40MHZ 2
#define AR9170_TX_PHY_BW_40MHZ_DUP 3
#define AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT 6
#define AR9170_TX_PHY_TX_HEAVY_CLIP_MASK (7 << AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT)
#define AR9170_TX_PHY_TX_PWR_SHIFT 9
#define AR9170_TX_PHY_TX_PWR_MASK (0x3f << AR9170_TX_PHY_TX_PWR_SHIFT)
/* not part of the hw-spec */
#define AR9170_TX_PHY_QOS_SHIFT 25
#define AR9170_TX_PHY_QOS_MASK (3 << AR9170_TX_PHY_QOS_SHIFT)
#define AR9170_TX_PHY_TXCHAIN_SHIFT 15
#define AR9170_TX_PHY_TXCHAIN_MASK (7 << AR9170_TX_PHY_TXCHAIN_SHIFT)
#define AR9170_TX_PHY_TXCHAIN_1 1
/* use for cck, ofdm 6/9/12/18/24 and HT if capable */
#define AR9170_TX_PHY_TXCHAIN_2 5
#define AR9170_TX_PHY_MCS_SHIFT 18
#define AR9170_TX_PHY_MCS_MASK (0x7f << AR9170_TX_PHY_MCS_SHIFT)
#define AR9170_TX_PHY_SHORT_GI 0x80000000
struct ar9170_rx_head {
u8 plcp[12];
} __packed;
struct ar9170_rx_tail {
union {
struct {
u8 rssi_ant0, rssi_ant1, rssi_ant2,
rssi_ant0x, rssi_ant1x, rssi_ant2x,
rssi_combined;
} __packed;
u8 rssi[7];
} __packed;
u8 evm_stream0[6], evm_stream1[6];
u8 phy_err;
u8 SAidx, DAidx;
u8 error;
u8 status;
} __packed;
#define AR9170_ENC_ALG_NONE 0x0
#define AR9170_ENC_ALG_WEP64 0x1
#define AR9170_ENC_ALG_TKIP 0x2
#define AR9170_ENC_ALG_AESCCMP 0x4
#define AR9170_ENC_ALG_WEP128 0x5
#define AR9170_ENC_ALG_WEP256 0x6
#define AR9170_ENC_ALG_CENC 0x7
#define AR9170_RX_ENC_SOFTWARE 0x8
static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_tail *t)
{
return (t->SAidx & 0xc0) >> 4 |
(t->DAidx & 0xc0) >> 6;
}
#define AR9170_RX_STATUS_MODULATION_MASK 0x03
#define AR9170_RX_STATUS_MODULATION_CCK 0x00
#define AR9170_RX_STATUS_MODULATION_OFDM 0x01
#define AR9170_RX_STATUS_MODULATION_HT 0x02
#define AR9170_RX_STATUS_MODULATION_DUPOFDM 0x03
/* depends on modulation */
#define AR9170_RX_STATUS_SHORT_PREAMBLE 0x08
#define AR9170_RX_STATUS_GREENFIELD 0x08
#define AR9170_RX_STATUS_MPDU_MASK 0x30
#define AR9170_RX_STATUS_MPDU_SINGLE 0x00
#define AR9170_RX_STATUS_MPDU_FIRST 0x10
#define AR9170_RX_STATUS_MPDU_MIDDLE 0x20
#define AR9170_RX_STATUS_MPDU_LAST 0x30
#define AR9170_RX_ERROR_RXTO 0x01
#define AR9170_RX_ERROR_OVERRUN 0x02
#define AR9170_RX_ERROR_DECRYPT 0x04
#define AR9170_RX_ERROR_FCS 0x08
#define AR9170_RX_ERROR_WRONG_RA 0x10
#define AR9170_RX_ERROR_PLCP 0x20
#define AR9170_RX_ERROR_MMIC 0x40
struct ar9170_cmd_tx_status {
__le16 unkn;
u8 dst[ETH_ALEN];
__le32 rate;
__le16 status;
} __packed;
#define AR9170_TX_STATUS_COMPLETE 0x00
#define AR9170_TX_STATUS_RETRY 0x01
#define AR9170_TX_STATUS_FAILED 0x02
struct ar9170_cmd_ba_failed_count {
__le16 failed;
__le16 rate;
} __packed;
struct ar9170_cmd_response {
u8 flag;
u8 type;
union {
struct ar9170_cmd_tx_status tx_status;
struct ar9170_cmd_ba_failed_count ba_fail_cnt;
u8 data[0];
};
} __packed;
/* QoS */
/* mac80211 queue to HW/FW map */
static const u8 ar9170_qos_hwmap[4] = { 3, 2, 0, 1 };
/* HW/FW queue to mac80211 map */
static const u8 ar9170_qos_mac80211map[4] = { 2, 3, 1, 0 };
enum ar9170_txq {
AR9170_TXQ_BE,
AR9170_TXQ_BK,
AR9170_TXQ_VI,
AR9170_TXQ_VO,
__AR9170_NUM_TXQ,
};
#endif /* __AR9170_HW_H */

View File

@@ -0,0 +1,171 @@
/*
* Atheros AR9170 driver
*
* LED handling
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-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 "ar9170.h"
#include "cmd.h"
int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state)
{
return ar9170_write_reg(ar, AR9170_GPIO_REG_DATA, led_state);
}
int ar9170_init_leds(struct ar9170 *ar)
{
int err;
/* disable LEDs */
/* GPIO [0/1 mode: output, 2/3: input] */
err = ar9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3);
if (err)
goto out;
/* GPIO 0/1 value: off */
err = ar9170_set_leds_state(ar, 0);
out:
return err;
}
#ifdef CONFIG_AR9170_LEDS
static void ar9170_update_leds(struct work_struct *work)
{
struct ar9170 *ar = container_of(work, struct ar9170, led_work.work);
int i, tmp, blink_delay = 1000;
u32 led_val = 0;
bool rerun = false;
if (unlikely(!IS_ACCEPTING_CMD(ar)))
return ;
mutex_lock(&ar->mutex);
for (i = 0; i < AR9170_NUM_LEDS; i++)
if (ar->leds[i].toggled) {
led_val |= 1 << i;
tmp = 70 + 200 / (ar->leds[i].toggled);
if (tmp < blink_delay)
blink_delay = tmp;
if (ar->leds[i].toggled > 1)
ar->leds[i].toggled = 0;
rerun = true;
}
ar9170_set_leds_state(ar, led_val);
mutex_unlock(&ar->mutex);
if (rerun)
queue_delayed_work(ar->hw->workqueue, &ar->led_work,
msecs_to_jiffies(blink_delay));
}
static void ar9170_led_brightness_set(struct led_classdev *led,
enum led_brightness brightness)
{
struct ar9170_led *arl = container_of(led, struct ar9170_led, l);
struct ar9170 *ar = arl->ar;
arl->toggled++;
if (likely(IS_ACCEPTING_CMD(ar) && brightness))
queue_delayed_work(ar->hw->workqueue, &ar->led_work, HZ/10);
}
static int ar9170_register_led(struct ar9170 *ar, int i, char *name,
char *trigger)
{
int err;
snprintf(ar->leds[i].name, sizeof(ar->leds[i].name),
"ar9170-%s::%s", wiphy_name(ar->hw->wiphy), name);
ar->leds[i].ar = ar;
ar->leds[i].l.name = ar->leds[i].name;
ar->leds[i].l.brightness_set = ar9170_led_brightness_set;
ar->leds[i].l.brightness = 0;
ar->leds[i].l.default_trigger = trigger;
err = led_classdev_register(wiphy_dev(ar->hw->wiphy),
&ar->leds[i].l);
if (err)
printk(KERN_ERR "%s: failed to register %s LED (%d).\n",
wiphy_name(ar->hw->wiphy), ar->leds[i].name, err);
else
ar->leds[i].registered = true;
return err;
}
void ar9170_unregister_leds(struct ar9170 *ar)
{
int i;
cancel_delayed_work_sync(&ar->led_work);
for (i = 0; i < AR9170_NUM_LEDS; i++)
if (ar->leds[i].registered) {
led_classdev_unregister(&ar->leds[i].l);
ar->leds[i].registered = false;
}
}
int ar9170_register_leds(struct ar9170 *ar)
{
int err;
INIT_DELAYED_WORK(&ar->led_work, ar9170_update_leds);
err = ar9170_register_led(ar, 0, "tx",
ieee80211_get_tx_led_name(ar->hw));
if (err)
goto fail;
err = ar9170_register_led(ar, 1, "assoc",
ieee80211_get_assoc_led_name(ar->hw));
if (err)
goto fail;
return 0;
fail:
ar9170_unregister_leds(ar);
return err;
}
#endif /* CONFIG_AR9170_LEDS */

View File

@@ -0,0 +1,452 @@
/*
* Atheros AR9170 driver
*
* MAC programming
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-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 "ar9170.h"
#include "cmd.h"
int ar9170_set_qos(struct ar9170 *ar)
{
ar9170_regwrite_begin(ar);
ar9170_regwrite(AR9170_MAC_REG_AC0_CW, ar->edcf[0].cw_min |
(ar->edcf[0].cw_max << 16));
ar9170_regwrite(AR9170_MAC_REG_AC1_CW, ar->edcf[1].cw_min |
(ar->edcf[1].cw_max << 16));
ar9170_regwrite(AR9170_MAC_REG_AC2_CW, ar->edcf[2].cw_min |
(ar->edcf[2].cw_max << 16));
ar9170_regwrite(AR9170_MAC_REG_AC3_CW, ar->edcf[3].cw_min |
(ar->edcf[3].cw_max << 16));
ar9170_regwrite(AR9170_MAC_REG_AC4_CW, ar->edcf[4].cw_min |
(ar->edcf[4].cw_max << 16));
ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_AIFS,
((ar->edcf[0].aifs * 9 + 10)) |
((ar->edcf[1].aifs * 9 + 10) << 12) |
((ar->edcf[2].aifs * 9 + 10) << 24));
ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_AIFS,
((ar->edcf[2].aifs * 9 + 10) >> 8) |
((ar->edcf[3].aifs * 9 + 10) << 4) |
((ar->edcf[4].aifs * 9 + 10) << 16));
ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP,
ar->edcf[0].txop | ar->edcf[1].txop << 16);
ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP,
ar->edcf[1].txop | ar->edcf[3].txop << 16);
ar9170_regwrite_finish();
return ar9170_regwrite_result();
}
int ar9170_init_mac(struct ar9170 *ar)
{
ar9170_regwrite_begin(ar);
ar9170_regwrite(AR9170_MAC_REG_ACK_EXTENSION, 0x40);
ar9170_regwrite(AR9170_MAC_REG_RETRY_MAX, 0);
/* enable MMIC */
ar9170_regwrite(AR9170_MAC_REG_SNIFFER,
AR9170_MAC_REG_SNIFFER_DEFAULTS);
ar9170_regwrite(AR9170_MAC_REG_RX_THRESHOLD, 0xc1f80);
ar9170_regwrite(AR9170_MAC_REG_RX_PE_DELAY, 0x70);
ar9170_regwrite(AR9170_MAC_REG_EIFS_AND_SIFS, 0xa144000);
ar9170_regwrite(AR9170_MAC_REG_SLOT_TIME, 9 << 10);
/* CF-END mode */
ar9170_regwrite(0x1c3b2c, 0x19000000);
/* NAV protects ACK only (in TXOP) */
ar9170_regwrite(0x1c3b38, 0x201);
/* Set Beacon PHY CTRL's TPC to 0x7, TA1=1 */
/* OTUS set AM to 0x1 */
ar9170_regwrite(AR9170_MAC_REG_BCN_HT1, 0x8000170);
ar9170_regwrite(AR9170_MAC_REG_BACKOFF_PROTECT, 0x105);
/* AGG test code*/
/* Aggregation MAX number and timeout */
ar9170_regwrite(0x1c3b9c, 0x10000a);
ar9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER,
AR9170_MAC_REG_FTF_DEFAULTS);
/* Enable deaggregator, response in sniffer mode */
ar9170_regwrite(0x1c3c40, 0x1 | 1<<30);
/* rate sets */
ar9170_regwrite(AR9170_MAC_REG_BASIC_RATE, 0x150f);
ar9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, 0x150f);
ar9170_regwrite(AR9170_MAC_REG_RTS_CTS_RATE, 0x10b01bb);
/* MIMO response control */
ar9170_regwrite(0x1c3694, 0x4003C1E);/* bit 26~28 otus-AM */
/* switch MAC to OTUS interface */
ar9170_regwrite(0x1c3600, 0x3);
ar9170_regwrite(AR9170_MAC_REG_AMPDU_RX_THRESH, 0xffff);
/* set PHY register read timeout (??) */
ar9170_regwrite(AR9170_MAC_REG_MISC_680, 0xf00008);
/* Disable Rx TimeOut, workaround for BB. */
ar9170_regwrite(AR9170_MAC_REG_RX_TIMEOUT, 0x0);
/* Set CPU clock frequency to 88/80MHz */
ar9170_regwrite(AR9170_PWR_REG_CLOCK_SEL,
AR9170_PWR_CLK_AHB_80_88MHZ |
AR9170_PWR_CLK_DAC_160_INV_DLY);
/* Set WLAN DMA interrupt mode: generate int per packet */
ar9170_regwrite(AR9170_MAC_REG_TXRX_MPI, 0x110011);
ar9170_regwrite(AR9170_MAC_REG_FCS_SELECT,
AR9170_MAC_FCS_FIFO_PROT);
/* Disables the CF_END frame, undocumented register */
ar9170_regwrite(AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND,
0x141E0F48);
ar9170_regwrite_finish();
return ar9170_regwrite_result();
}
static int ar9170_set_mac_reg(struct ar9170 *ar, const u32 reg, const u8 *mac)
{
static const u8 zero[ETH_ALEN] = { 0 };
if (!mac)
mac = zero;
ar9170_regwrite_begin(ar);
ar9170_regwrite(reg,
(mac[3] << 24) | (mac[2] << 16) |
(mac[1] << 8) | mac[0]);
ar9170_regwrite(reg + 4, (mac[5] << 8) | mac[4]);
ar9170_regwrite_finish();
return ar9170_regwrite_result();
}
int ar9170_update_multicast(struct ar9170 *ar)
{
int err;
ar9170_regwrite_begin(ar);
ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H,
ar->want_mc_hash >> 32);
ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L,
ar->want_mc_hash);
ar9170_regwrite_finish();
err = ar9170_regwrite_result();
if (err)
return err;
ar->cur_mc_hash = ar->want_mc_hash;
return 0;
}
int ar9170_update_frame_filter(struct ar9170 *ar)
{
int err;
err = ar9170_write_reg(ar, AR9170_MAC_REG_FRAMETYPE_FILTER,
ar->want_filter);
if (err)
return err;
ar->cur_filter = ar->want_filter;
return 0;
}
static int ar9170_set_promiscouous(struct ar9170 *ar)
{
u32 encr_mode, sniffer;
int err;
err = ar9170_read_reg(ar, AR9170_MAC_REG_SNIFFER, &sniffer);
if (err)
return err;
err = ar9170_read_reg(ar, AR9170_MAC_REG_ENCRYPTION, &encr_mode);
if (err)
return err;
if (ar->sniffer_enabled) {
sniffer |= AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC;
/*
* Rx decryption works in place.
*
* If we don't disable it, the hardware will render all
* encrypted frames which are encrypted with an unknown
* key useless.
*/
encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
ar->sniffer_enabled = true;
} else {
sniffer &= ~AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC;
if (ar->rx_software_decryption)
encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
else
encr_mode &= ~AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
}
ar9170_regwrite_begin(ar);
ar9170_regwrite(AR9170_MAC_REG_ENCRYPTION, encr_mode);
ar9170_regwrite(AR9170_MAC_REG_SNIFFER, sniffer);
ar9170_regwrite_finish();
return ar9170_regwrite_result();
}
int ar9170_set_operating_mode(struct ar9170 *ar)
{
u32 pm_mode = AR9170_MAC_REG_POWERMGT_DEFAULTS;
u8 *mac_addr, *bssid;
int err;
if (ar->vif) {
mac_addr = ar->mac_addr;
bssid = ar->bssid;
switch (ar->vif->type) {
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_ADHOC:
pm_mode |= AR9170_MAC_REG_POWERMGT_IBSS;
break;
/* case NL80211_IFTYPE_AP:
pm_mode |= AR9170_MAC_REG_POWERMGT_AP;
break;*/
case NL80211_IFTYPE_WDS:
pm_mode |= AR9170_MAC_REG_POWERMGT_AP_WDS;
break;
case NL80211_IFTYPE_MONITOR:
ar->sniffer_enabled = true;
ar->rx_software_decryption = true;
break;
default:
pm_mode |= AR9170_MAC_REG_POWERMGT_STA;
break;
}
} else {
mac_addr = NULL;
bssid = NULL;
}
err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_MAC_ADDR_L, mac_addr);
if (err)
return err;
err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_BSSID_L, bssid);
if (err)
return err;
err = ar9170_set_promiscouous(ar);
if (err)
return err;
ar9170_regwrite_begin(ar);
ar9170_regwrite(AR9170_MAC_REG_POWERMANAGEMENT, pm_mode);
ar9170_regwrite_finish();
return ar9170_regwrite_result();
}
int ar9170_set_hwretry_limit(struct ar9170 *ar, unsigned int max_retry)
{
u32 tmp = min_t(u32, 0x33333, max_retry * 0x11111);
return ar9170_write_reg(ar, AR9170_MAC_REG_RETRY_MAX, tmp);
}
int ar9170_set_beacon_timers(struct ar9170 *ar)
{
u32 v = 0;
u32 pretbtt = 0;
v |= ar->hw->conf.beacon_int;
if (ar->vif) {
switch (ar->vif->type) {
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_ADHOC:
v |= BIT(25);
break;
case NL80211_IFTYPE_AP:
v |= BIT(24);
pretbtt = (ar->hw->conf.beacon_int - 6) << 16;
break;
default:
break;
}
v |= ar->vif->bss_conf.dtim_period << 16;
}
ar9170_regwrite_begin(ar);
ar9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt);
ar9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v);
ar9170_regwrite_finish();
return ar9170_regwrite_result();
}
int ar9170_update_beacon(struct ar9170 *ar)
{
struct sk_buff *skb;
__le32 *data, *old = NULL;
u32 word;
int i;
skb = ieee80211_beacon_get(ar->hw, ar->vif);
if (!skb)
return -ENOMEM;
data = (__le32 *)skb->data;
if (ar->beacon)
old = (__le32 *)ar->beacon->data;
ar9170_regwrite_begin(ar);
for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
/*
* XXX: This accesses beyond skb data for up
* to the last 3 bytes!!
*/
if (old && (data[i] == old[i]))
continue;
word = le32_to_cpu(data[i]);
ar9170_regwrite(AR9170_BEACON_BUFFER_ADDRESS + 4 * i, word);
}
/* XXX: use skb->cb info */
if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ)
ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP,
((skb->len + 4) << (3+16)) + 0x0400);
else
ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP,
((skb->len + 4) << (3+16)) + 0x0400);
ar9170_regwrite(AR9170_MAC_REG_BCN_LENGTH, skb->len + 4);
ar9170_regwrite(AR9170_MAC_REG_BCN_ADDR, AR9170_BEACON_BUFFER_ADDRESS);
ar9170_regwrite(AR9170_MAC_REG_BCN_CTRL, 1);
ar9170_regwrite_finish();
dev_kfree_skb(ar->beacon);
ar->beacon = skb;
return ar9170_regwrite_result();
}
void ar9170_new_beacon(struct work_struct *work)
{
struct ar9170 *ar = container_of(work, struct ar9170,
beacon_work);
struct sk_buff *skb;
if (unlikely(!IS_STARTED(ar)))
return ;
mutex_lock(&ar->mutex);
if (!ar->vif)
goto out;
ar9170_update_beacon(ar);
rcu_read_lock();
while ((skb = ieee80211_get_buffered_bc(ar->hw, ar->vif)))
ar9170_op_tx(ar->hw, skb);
rcu_read_unlock();
out:
mutex_unlock(&ar->mutex);
}
int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype,
u8 keyidx, u8 *keydata, int keylen)
{
__le32 vals[7];
static const u8 bcast[ETH_ALEN] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
u8 dummy;
mac = mac ? : bcast;
vals[0] = cpu_to_le32((keyidx << 16) + id);
vals[1] = cpu_to_le32(mac[1] << 24 | mac[0] << 16 | ktype);
vals[2] = cpu_to_le32(mac[5] << 24 | mac[4] << 16 |
mac[3] << 8 | mac[2]);
memset(&vals[3], 0, 16);
if (keydata)
memcpy(&vals[3], keydata, keylen);
return ar->exec_cmd(ar, AR9170_CMD_EKEY,
sizeof(vals), (u8 *)vals,
1, &dummy);
}
int ar9170_disable_key(struct ar9170 *ar, u8 id)
{
__le32 val = cpu_to_le32(id);
u8 dummy;
return ar->exec_cmd(ar, AR9170_CMD_EKEY,
sizeof(val), (u8 *)&val,
1, &dummy);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,822 @@
/*
* Atheros AR9170 driver
*
* USB - frontend
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2009, Christian Lamparter <chunkeey@web.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-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/module.h>
#include <linux/usb.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <net/mac80211.h>
#include "ar9170.h"
#include "cmd.h"
#include "hw.h"
#include "usb.h"
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless");
MODULE_FIRMWARE("ar9170-1.fw");
MODULE_FIRMWARE("ar9170-2.fw");
static struct usb_device_id ar9170_usb_ids[] = {
/* Atheros 9170 */
{ USB_DEVICE(0x0cf3, 0x9170) },
/* Atheros TG121N */
{ USB_DEVICE(0x0cf3, 0x1001) },
/* Cace Airpcap NX */
{ USB_DEVICE(0xcace, 0x0300) },
/* D-Link DWA 160A */
{ USB_DEVICE(0x07d1, 0x3c10) },
/* Netgear WNDA3100 */
{ USB_DEVICE(0x0846, 0x9010) },
/* Netgear WN111 v2 */
{ USB_DEVICE(0x0846, 0x9001) },
/* Zydas ZD1221 */
{ USB_DEVICE(0x0ace, 0x1221) },
/* ZyXEL NWD271N */
{ USB_DEVICE(0x0586, 0x3417) },
/* Z-Com UB81 BG */
{ USB_DEVICE(0x0cde, 0x0023) },
/* Z-Com UB82 ABG */
{ USB_DEVICE(0x0cde, 0x0026) },
/* Arcadyan WN7512 */
{ USB_DEVICE(0x083a, 0xf522) },
/* Planex GWUS300 */
{ USB_DEVICE(0x2019, 0x5304) },
/* IO-Data WNGDNUS2 */
{ USB_DEVICE(0x04bb, 0x093f) },
/* terminate */
{}
};
MODULE_DEVICE_TABLE(usb, ar9170_usb_ids);
static void ar9170_usb_tx_urb_complete_free(struct urb *urb)
{
struct sk_buff *skb = urb->context;
struct ar9170_usb *aru = (struct ar9170_usb *)
usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
if (!aru) {
dev_kfree_skb_irq(skb);
return ;
}
ar9170_handle_tx_status(&aru->common, skb, false,
AR9170_TX_STATUS_COMPLETE);
}
static void ar9170_usb_tx_urb_complete(struct urb *urb)
{
}
static void ar9170_usb_irq_completed(struct urb *urb)
{
struct ar9170_usb *aru = urb->context;
switch (urb->status) {
/* everything is fine */
case 0:
break;
/* disconnect */
case -ENOENT:
case -ECONNRESET:
case -ENODEV:
case -ESHUTDOWN:
goto free;
default:
goto resubmit;
}
print_hex_dump_bytes("ar9170 irq: ", DUMP_PREFIX_OFFSET,
urb->transfer_buffer, urb->actual_length);
resubmit:
usb_anchor_urb(urb, &aru->rx_submitted);
if (usb_submit_urb(urb, GFP_ATOMIC)) {
usb_unanchor_urb(urb);
goto free;
}
return;
free:
usb_buffer_free(aru->udev, 64, urb->transfer_buffer, urb->transfer_dma);
}
static void ar9170_usb_rx_completed(struct urb *urb)
{
struct sk_buff *skb = urb->context;
struct ar9170_usb *aru = (struct ar9170_usb *)
usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
int err;
if (!aru)
goto free;
switch (urb->status) {
/* everything is fine */
case 0:
break;
/* disconnect */
case -ENOENT:
case -ECONNRESET:
case -ENODEV:
case -ESHUTDOWN:
goto free;
default:
goto resubmit;
}
skb_put(skb, urb->actual_length);
ar9170_rx(&aru->common, skb);
resubmit:
skb_reset_tail_pointer(skb);
skb_trim(skb, 0);
usb_anchor_urb(urb, &aru->rx_submitted);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
usb_unanchor_urb(urb);
dev_kfree_skb_irq(skb);
}
return ;
free:
dev_kfree_skb_irq(skb);
return;
}
static int ar9170_usb_prep_rx_urb(struct ar9170_usb *aru,
struct urb *urb, gfp_t gfp)
{
struct sk_buff *skb;
skb = __dev_alloc_skb(AR9170_MAX_RX_BUFFER_SIZE + 32, gfp);
if (!skb)
return -ENOMEM;
/* reserve some space for mac80211's radiotap */
skb_reserve(skb, 32);
usb_fill_bulk_urb(urb, aru->udev,
usb_rcvbulkpipe(aru->udev, AR9170_EP_RX),
skb->data, min(skb_tailroom(skb),
AR9170_MAX_RX_BUFFER_SIZE),
ar9170_usb_rx_completed, skb);
return 0;
}
static int ar9170_usb_alloc_rx_irq_urb(struct ar9170_usb *aru)
{
struct urb *urb = NULL;
void *ibuf;
int err = -ENOMEM;
/* initialize interrupt endpoint */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
goto out;
ibuf = usb_buffer_alloc(aru->udev, 64, GFP_KERNEL, &urb->transfer_dma);
if (!ibuf)
goto out;
usb_fill_int_urb(urb, aru->udev,
usb_rcvintpipe(aru->udev, AR9170_EP_IRQ), ibuf,
64, ar9170_usb_irq_completed, aru, 1);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb_anchor_urb(urb, &aru->rx_submitted);
err = usb_submit_urb(urb, GFP_KERNEL);
if (err) {
usb_unanchor_urb(urb);
usb_buffer_free(aru->udev, 64, urb->transfer_buffer,
urb->transfer_dma);
}
out:
usb_free_urb(urb);
return err;
}
static int ar9170_usb_alloc_rx_bulk_urbs(struct ar9170_usb *aru)
{
struct urb *urb;
int i;
int err = -EINVAL;
for (i = 0; i < AR9170_NUM_RX_URBS; i++) {
err = -ENOMEM;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
goto err_out;
err = ar9170_usb_prep_rx_urb(aru, urb, GFP_KERNEL);
if (err) {
usb_free_urb(urb);
goto err_out;
}
usb_anchor_urb(urb, &aru->rx_submitted);
err = usb_submit_urb(urb, GFP_KERNEL);
if (err) {
usb_unanchor_urb(urb);
dev_kfree_skb_any((void *) urb->transfer_buffer);
usb_free_urb(urb);
goto err_out;
}
usb_free_urb(urb);
}
/* the device now waiting for a firmware. */
aru->common.state = AR9170_IDLE;
return 0;
err_out:
usb_kill_anchored_urbs(&aru->rx_submitted);
return err;
}
static void ar9170_usb_cancel_urbs(struct ar9170_usb *aru)
{
int ret;
aru->common.state = AR9170_UNKNOWN_STATE;
usb_unlink_anchored_urbs(&aru->tx_submitted);
/* give the LED OFF command and the deauth frame a chance to air. */
ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted,
msecs_to_jiffies(100));
if (ret == 0)
dev_err(&aru->udev->dev, "kill pending tx urbs.\n");
usb_poison_anchored_urbs(&aru->tx_submitted);
usb_poison_anchored_urbs(&aru->rx_submitted);
}
static int ar9170_usb_exec_cmd(struct ar9170 *ar, enum ar9170_cmd cmd,
unsigned int plen, void *payload,
unsigned int outlen, void *out)
{
struct ar9170_usb *aru = (void *) ar;
struct urb *urb = NULL;
unsigned long flags;
int err = -ENOMEM;
if (unlikely(!IS_ACCEPTING_CMD(ar)))
return -EPERM;
if (WARN_ON(plen > AR9170_MAX_CMD_LEN - 4))
return -EINVAL;
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (unlikely(!urb))
goto err_free;
ar->cmdbuf[0] = cpu_to_le32(plen);
ar->cmdbuf[0] |= cpu_to_le32(cmd << 8);
/* writing multiple regs fills this buffer already */
if (plen && payload != (u8 *)(&ar->cmdbuf[1]))
memcpy(&ar->cmdbuf[1], payload, plen);
spin_lock_irqsave(&aru->common.cmdlock, flags);
aru->readbuf = (u8 *)out;
aru->readlen = outlen;
spin_unlock_irqrestore(&aru->common.cmdlock, flags);
usb_fill_int_urb(urb, aru->udev,
usb_sndbulkpipe(aru->udev, AR9170_EP_CMD),
aru->common.cmdbuf, plen + 4,
ar9170_usb_tx_urb_complete, NULL, 1);
usb_anchor_urb(urb, &aru->tx_submitted);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
usb_unanchor_urb(urb);
usb_free_urb(urb);
goto err_unbuf;
}
usb_free_urb(urb);
err = wait_for_completion_timeout(&aru->cmd_wait, HZ);
if (err == 0) {
err = -ETIMEDOUT;
goto err_unbuf;
}
if (outlen >= 0 && aru->readlen != outlen) {
err = -EMSGSIZE;
goto err_unbuf;
}
return 0;
err_unbuf:
/* Maybe the device was removed in the second we were waiting? */
if (IS_STARTED(ar)) {
dev_err(&aru->udev->dev, "no command feedback "
"received (%d).\n", err);
/* provide some maybe useful debug information */
print_hex_dump_bytes("ar9170 cmd: ", DUMP_PREFIX_NONE,
aru->common.cmdbuf, plen + 4);
dump_stack();
}
/* invalidate to avoid completing the next prematurely */
spin_lock_irqsave(&aru->common.cmdlock, flags);
aru->readbuf = NULL;
aru->readlen = 0;
spin_unlock_irqrestore(&aru->common.cmdlock, flags);
err_free:
return err;
}
static int ar9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb,
bool txstatus_needed, unsigned int extra_len)
{
struct ar9170_usb *aru = (struct ar9170_usb *) ar;
struct urb *urb;
int err;
if (unlikely(!IS_STARTED(ar))) {
/* Seriously, what were you drink... err... thinking!? */
return -EPERM;
}
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (unlikely(!urb))
return -ENOMEM;
usb_fill_bulk_urb(urb, aru->udev,
usb_sndbulkpipe(aru->udev, AR9170_EP_TX),
skb->data, skb->len + extra_len, (txstatus_needed ?
ar9170_usb_tx_urb_complete :
ar9170_usb_tx_urb_complete_free), skb);
urb->transfer_flags |= URB_ZERO_PACKET;
usb_anchor_urb(urb, &aru->tx_submitted);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (unlikely(err))
usb_unanchor_urb(urb);
usb_free_urb(urb);
return err;
}
static void ar9170_usb_callback_cmd(struct ar9170 *ar, u32 len , void *buffer)
{
struct ar9170_usb *aru = (void *) ar;
unsigned long flags;
u32 in, out;
if (!buffer)
return ;
in = le32_to_cpup((__le32 *)buffer);
out = le32_to_cpu(ar->cmdbuf[0]);
/* mask off length byte */
out &= ~0xFF;
if (aru->readlen >= 0) {
/* add expected length */
out |= aru->readlen;
} else {
/* add obtained length */
out |= in & 0xFF;
}
/*
* Some commands (e.g: AR9170_CMD_FREQUENCY) have a variable response
* length and we cannot predict the correct length in advance.
* So we only check if we provided enough space for the data.
*/
if (unlikely(out < in)) {
dev_warn(&aru->udev->dev, "received invalid command response "
"got %d bytes, instead of %d bytes "
"and the resp length is %d bytes\n",
in, out, len);
print_hex_dump_bytes("ar9170 invalid resp: ",
DUMP_PREFIX_OFFSET, buffer, len);
/*
* Do not complete, then the command times out,
* and we get a stack trace from there.
*/
return ;
}
spin_lock_irqsave(&aru->common.cmdlock, flags);
if (aru->readbuf && len > 0) {
memcpy(aru->readbuf, buffer + 4, len - 4);
aru->readbuf = NULL;
}
complete(&aru->cmd_wait);
spin_unlock_irqrestore(&aru->common.cmdlock, flags);
}
static int ar9170_usb_upload(struct ar9170_usb *aru, const void *data,
size_t len, u32 addr, bool complete)
{
int transfer, err;
u8 *buf = kmalloc(4096, GFP_KERNEL);
if (!buf)
return -ENOMEM;
while (len) {
transfer = min_t(int, len, 4096);
memcpy(buf, data, transfer);
err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0),
0x30 /* FW DL */, 0x40 | USB_DIR_OUT,
addr >> 8, 0, buf, transfer, 1000);
if (err < 0) {
kfree(buf);
return err;
}
len -= transfer;
data += transfer;
addr += transfer;
}
kfree(buf);
if (complete) {
err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0),
0x31 /* FW DL COMPLETE */,
0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 5000);
}
return 0;
}
static int ar9170_usb_request_firmware(struct ar9170_usb *aru)
{
int err = 0;
err = request_firmware(&aru->init_values, "ar9170-1.fw",
&aru->udev->dev);
if (err) {
dev_err(&aru->udev->dev, "file with init values not found.\n");
return err;
}
err = request_firmware(&aru->firmware, "ar9170-2.fw", &aru->udev->dev);
if (err) {
release_firmware(aru->init_values);
dev_err(&aru->udev->dev, "firmware file not found.\n");
return err;
}
return err;
}
static int ar9170_usb_reset(struct ar9170_usb *aru)
{
int ret, lock = (aru->intf->condition != USB_INTERFACE_BINDING);
if (lock) {
ret = usb_lock_device_for_reset(aru->udev, aru->intf);
if (ret < 0) {
dev_err(&aru->udev->dev, "unable to lock device "
"for reset (%d).\n", ret);
return ret;
}
}
ret = usb_reset_device(aru->udev);
if (lock)
usb_unlock_device(aru->udev);
/* let it rest - for a second - */
msleep(1000);
return ret;
}
static int ar9170_usb_upload_firmware(struct ar9170_usb *aru)
{
int err;
/* First, upload initial values to device RAM */
err = ar9170_usb_upload(aru, aru->init_values->data,
aru->init_values->size, 0x102800, false);
if (err) {
dev_err(&aru->udev->dev, "firmware part 1 "
"upload failed (%d).\n", err);
return err;
}
/* Then, upload the firmware itself and start it */
return ar9170_usb_upload(aru, aru->firmware->data, aru->firmware->size,
0x200000, true);
}
static int ar9170_usb_init_transport(struct ar9170_usb *aru)
{
struct ar9170 *ar = (void *) &aru->common;
int err;
ar9170_regwrite_begin(ar);
/* Set USB Rx stream mode MAX packet number to 2 */
ar9170_regwrite(AR9170_USB_REG_MAX_AGG_UPLOAD, 0x4);
/* Set USB Rx stream mode timeout to 10us */
ar9170_regwrite(AR9170_USB_REG_UPLOAD_TIME_CTL, 0x80);
ar9170_regwrite_finish();
err = ar9170_regwrite_result();
if (err)
dev_err(&aru->udev->dev, "USB setup failed (%d).\n", err);
return err;
}
static void ar9170_usb_stop(struct ar9170 *ar)
{
struct ar9170_usb *aru = (void *) ar;
int ret;
if (IS_ACCEPTING_CMD(ar))
aru->common.state = AR9170_STOPPED;
/* lets wait a while until the tx - queues are dried out */
ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted,
msecs_to_jiffies(1000));
if (ret == 0)
dev_err(&aru->udev->dev, "kill pending tx urbs.\n");
usb_poison_anchored_urbs(&aru->tx_submitted);
/*
* Note:
* So far we freed all tx urbs, but we won't dare to touch any rx urbs.
* Else we would end up with a unresponsive device...
*/
}
static int ar9170_usb_open(struct ar9170 *ar)
{
struct ar9170_usb *aru = (void *) ar;
int err;
usb_unpoison_anchored_urbs(&aru->tx_submitted);
err = ar9170_usb_init_transport(aru);
if (err) {
usb_poison_anchored_urbs(&aru->tx_submitted);
return err;
}
aru->common.state = AR9170_IDLE;
return 0;
}
static int ar9170_usb_init_device(struct ar9170_usb *aru)
{
int err;
err = ar9170_usb_alloc_rx_irq_urb(aru);
if (err)
goto err_out;
err = ar9170_usb_alloc_rx_bulk_urbs(aru);
if (err)
goto err_unrx;
err = ar9170_usb_upload_firmware(aru);
if (err) {
err = ar9170_echo_test(&aru->common, 0x60d43110);
if (err) {
/* force user invention, by disabling the device */
err = usb_driver_set_configuration(aru->udev, -1);
dev_err(&aru->udev->dev, "device is in a bad state. "
"please reconnect it!\n");
goto err_unrx;
}
}
return 0;
err_unrx:
ar9170_usb_cancel_urbs(aru);
err_out:
return err;
}
static int ar9170_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct ar9170_usb *aru;
struct ar9170 *ar;
struct usb_device *udev;
int err;
aru = ar9170_alloc(sizeof(*aru));
if (IS_ERR(aru)) {
err = PTR_ERR(aru);
goto out;
}
udev = interface_to_usbdev(intf);
usb_get_dev(udev);
aru->udev = udev;
aru->intf = intf;
ar = &aru->common;
usb_set_intfdata(intf, aru);
SET_IEEE80211_DEV(ar->hw, &udev->dev);
init_usb_anchor(&aru->rx_submitted);
init_usb_anchor(&aru->tx_submitted);
init_completion(&aru->cmd_wait);
aru->common.stop = ar9170_usb_stop;
aru->common.open = ar9170_usb_open;
aru->common.tx = ar9170_usb_tx;
aru->common.exec_cmd = ar9170_usb_exec_cmd;
aru->common.callback_cmd = ar9170_usb_callback_cmd;
err = ar9170_usb_reset(aru);
if (err)
goto err_freehw;
err = ar9170_usb_request_firmware(aru);
if (err)
goto err_freehw;
err = ar9170_usb_init_device(aru);
if (err)
goto err_freefw;
err = ar9170_usb_open(ar);
if (err)
goto err_unrx;
err = ar9170_register(ar, &udev->dev);
ar9170_usb_stop(ar);
if (err)
goto err_unrx;
return 0;
err_unrx:
ar9170_usb_cancel_urbs(aru);
err_freefw:
release_firmware(aru->init_values);
release_firmware(aru->firmware);
err_freehw:
usb_set_intfdata(intf, NULL);
usb_put_dev(udev);
ieee80211_free_hw(ar->hw);
out:
return err;
}
static void ar9170_usb_disconnect(struct usb_interface *intf)
{
struct ar9170_usb *aru = usb_get_intfdata(intf);
if (!aru)
return;
aru->common.state = AR9170_IDLE;
ar9170_unregister(&aru->common);
ar9170_usb_cancel_urbs(aru);
release_firmware(aru->init_values);
release_firmware(aru->firmware);
usb_put_dev(aru->udev);
usb_set_intfdata(intf, NULL);
ieee80211_free_hw(aru->common.hw);
}
#ifdef CONFIG_PM
static int ar9170_suspend(struct usb_interface *intf,
pm_message_t message)
{
struct ar9170_usb *aru = usb_get_intfdata(intf);
if (!aru)
return -ENODEV;
aru->common.state = AR9170_IDLE;
ar9170_usb_cancel_urbs(aru);
return 0;
}
static int ar9170_resume(struct usb_interface *intf)
{
struct ar9170_usb *aru = usb_get_intfdata(intf);
int err;
if (!aru)
return -ENODEV;
usb_unpoison_anchored_urbs(&aru->rx_submitted);
usb_unpoison_anchored_urbs(&aru->tx_submitted);
/*
* FIXME: firmware upload will fail on resume.
* but this is better than a hang!
*/
err = ar9170_usb_init_device(aru);
if (err)
goto err_unrx;
err = ar9170_usb_open(&aru->common);
if (err)
goto err_unrx;
return 0;
err_unrx:
aru->common.state = AR9170_IDLE;
ar9170_usb_cancel_urbs(aru);
return err;
}
#endif /* CONFIG_PM */
static struct usb_driver ar9170_driver = {
.name = "ar9170usb",
.probe = ar9170_usb_probe,
.disconnect = ar9170_usb_disconnect,
.id_table = ar9170_usb_ids,
.soft_unbind = 1,
#ifdef CONFIG_PM
.suspend = ar9170_suspend,
.resume = ar9170_resume,
#endif /* CONFIG_PM */
};
static int __init ar9170_init(void)
{
return usb_register(&ar9170_driver);
}
static void __exit ar9170_exit(void)
{
usb_deregister(&ar9170_driver);
}
module_init(ar9170_init);
module_exit(ar9170_exit);

View File

@@ -0,0 +1,74 @@
/*
* Atheros AR9170 USB driver
*
* Driver specific definitions
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2009, Christian Lamparter <chunkeey@web.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-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 __USB_H
#define __USB_H
#include <linux/usb.h>
#include <linux/completion.h>
#include <linux/spinlock.h>
#include <linux/leds.h>
#include <net/wireless.h>
#include <net/mac80211.h>
#include <linux/firmware.h>
#include "eeprom.h"
#include "hw.h"
#include "ar9170.h"
#define AR9170_NUM_RX_URBS 16
struct firmware;
struct ar9170_usb {
struct ar9170 common;
struct usb_device *udev;
struct usb_interface *intf;
struct usb_anchor rx_submitted;
struct usb_anchor tx_submitted;
spinlock_t cmdlock;
struct completion cmd_wait;
int readlen;
u8 *readbuf;
const struct firmware *init_values;
const struct firmware *firmware;
};
#endif /* __USB_H */

View File

@@ -0,0 +1,41 @@
config ATH5K
tristate "Atheros 5xxx wireless cards support"
depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL
select ATH_COMMON
select MAC80211_LEDS
select LEDS_CLASS
select NEW_LEDS
---help---
This module adds support for wireless adapters based on
Atheros 5xxx chipset.
Currently the following chip versions are supported:
MAC: AR5211 AR5212
PHY: RF5111/2111 RF5112/2112 RF5413/2413
This driver uses the kernel's mac80211 subsystem.
If you choose to build a module, it'll be called ath5k. Say M if
unsure.
config ATH5K_DEBUG
bool "Atheros 5xxx debugging"
depends on ATH5K
---help---
Atheros 5xxx debugging messages.
Say Y, if and you will get debug options for ath5k.
To use this, you need to mount debugfs:
mkdir /debug/
mount -t debugfs debug /debug/
You will get access to files under:
/debug/ath5k/phy0/
To enable debug, pass the debug level to the debug module
parameter. For example:
modprobe ath5k debug=0x00000400

View File

@@ -0,0 +1,15 @@
ath5k-y += caps.o
ath5k-y += initvals.o
ath5k-y += eeprom.o
ath5k-y += gpio.o
ath5k-y += desc.o
ath5k-y += dma.o
ath5k-y += qcu.o
ath5k-y += pcu.o
ath5k-y += phy.o
ath5k-y += reset.o
ath5k-y += attach.o
ath5k-y += base.o
ath5k-y += led.o
ath5k-$(CONFIG_ATH5K_DEBUG) += debug.o
obj-$(CONFIG_ATH5K) += ath5k.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,348 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
*
* Permission to use, copy, modify, and 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.
*
*/
/*************************************\
* Attach/Detach Functions and helpers *
\*************************************/
#include <linux/pci.h>
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "base.h"
/**
* ath5k_hw_post - Power On Self Test helper function
*
* @ah: The &struct ath5k_hw
*/
static int ath5k_hw_post(struct ath5k_hw *ah)
{
static const u32 static_pattern[4] = {
0x55555555, 0xaaaaaaaa,
0x66666666, 0x99999999
};
static const u16 regs[2] = { AR5K_STA_ID0, AR5K_PHY(8) };
int i, c;
u16 cur_reg;
u32 var_pattern;
u32 init_val;
u32 cur_val;
for (c = 0; c < 2; c++) {
cur_reg = regs[c];
/* Save previous value */
init_val = ath5k_hw_reg_read(ah, cur_reg);
for (i = 0; i < 256; i++) {
var_pattern = i << 16 | i;
ath5k_hw_reg_write(ah, var_pattern, cur_reg);
cur_val = ath5k_hw_reg_read(ah, cur_reg);
if (cur_val != var_pattern) {
ATH5K_ERR(ah->ah_sc, "POST Failed !!!\n");
return -EAGAIN;
}
/* Found on ndiswrapper dumps */
var_pattern = 0x0039080f;
ath5k_hw_reg_write(ah, var_pattern, cur_reg);
}
for (i = 0; i < 4; i++) {
var_pattern = static_pattern[i];
ath5k_hw_reg_write(ah, var_pattern, cur_reg);
cur_val = ath5k_hw_reg_read(ah, cur_reg);
if (cur_val != var_pattern) {
ATH5K_ERR(ah->ah_sc, "POST Failed !!!\n");
return -EAGAIN;
}
/* Found on ndiswrapper dumps */
var_pattern = 0x003b080f;
ath5k_hw_reg_write(ah, var_pattern, cur_reg);
}
/* Restore previous value */
ath5k_hw_reg_write(ah, init_val, cur_reg);
}
return 0;
}
/**
* ath5k_hw_attach - Check if hw is supported and init the needed structs
*
* @sc: The &struct ath5k_softc we got from the driver's attach function
* @mac_version: The mac version id (check out ath5k.h) based on pci id
*
* Check if the device is supported, perform a POST and initialize the needed
* structs. Returns -ENOMEM if we don't have memory for the needed structs,
* -ENODEV if the device is not supported or prints an error msg if something
* else went wrong.
*/
struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version)
{
struct ath5k_hw *ah;
struct pci_dev *pdev = sc->pdev;
int ret;
u32 srev;
/*If we passed the test malloc a ath5k_hw struct*/
ah = kzalloc(sizeof(struct ath5k_hw), GFP_KERNEL);
if (ah == NULL) {
ret = -ENOMEM;
ATH5K_ERR(sc, "out of memory\n");
goto err;
}
ah->ah_sc = sc;
ah->ah_iobase = sc->iobase;
/*
* HW information
*/
ah->ah_op_mode = NL80211_IFTYPE_STATION;
ah->ah_radar.r_enabled = AR5K_TUNE_RADAR_ALERT;
ah->ah_turbo = false;
ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
ah->ah_imr = 0;
ah->ah_atim_window = 0;
ah->ah_aifs = AR5K_TUNE_AIFS;
ah->ah_cw_min = AR5K_TUNE_CWMIN;
ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY;
ah->ah_software_retry = false;
ah->ah_ant_diversity = AR5K_TUNE_ANT_DIVERSITY;
/*
* Set the mac version based on the pci id
*/
ah->ah_version = mac_version;
/*Fill the ath5k_hw struct with the needed functions*/
ret = ath5k_hw_init_desc_functions(ah);
if (ret)
goto err_free;
/* Bring device out of sleep and reset it's units */
ret = ath5k_hw_nic_wakeup(ah, CHANNEL_B, true);
if (ret)
goto err_free;
/* Get MAC, PHY and RADIO revisions */
srev = ath5k_hw_reg_read(ah, AR5K_SREV);
ah->ah_mac_srev = srev;
ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER);
ah->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV);
ah->ah_phy_revision = ath5k_hw_reg_read(ah, AR5K_PHY_CHIP_ID) &
0xffffffff;
ah->ah_radio_5ghz_revision = ath5k_hw_radio_revision(ah,
CHANNEL_5GHZ);
ah->ah_phy = AR5K_PHY(0);
/* Try to identify radio chip based on it's srev */
switch (ah->ah_radio_5ghz_revision & 0xf0) {
case AR5K_SREV_RAD_5111:
ah->ah_radio = AR5K_RF5111;
ah->ah_single_chip = false;
ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
CHANNEL_2GHZ);
break;
case AR5K_SREV_RAD_5112:
case AR5K_SREV_RAD_2112:
ah->ah_radio = AR5K_RF5112;
ah->ah_single_chip = false;
ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
CHANNEL_2GHZ);
break;
case AR5K_SREV_RAD_2413:
ah->ah_radio = AR5K_RF2413;
ah->ah_single_chip = true;
break;
case AR5K_SREV_RAD_5413:
ah->ah_radio = AR5K_RF5413;
ah->ah_single_chip = true;
break;
case AR5K_SREV_RAD_2316:
ah->ah_radio = AR5K_RF2316;
ah->ah_single_chip = true;
break;
case AR5K_SREV_RAD_2317:
ah->ah_radio = AR5K_RF2317;
ah->ah_single_chip = true;
break;
case AR5K_SREV_RAD_5424:
if (ah->ah_mac_version == AR5K_SREV_AR2425 ||
ah->ah_mac_version == AR5K_SREV_AR2417){
ah->ah_radio = AR5K_RF2425;
ah->ah_single_chip = true;
} else {
ah->ah_radio = AR5K_RF5413;
ah->ah_single_chip = true;
}
break;
default:
/* Identify radio based on mac/phy srev */
if (ah->ah_version == AR5K_AR5210) {
ah->ah_radio = AR5K_RF5110;
ah->ah_single_chip = false;
} else if (ah->ah_version == AR5K_AR5211) {
ah->ah_radio = AR5K_RF5111;
ah->ah_single_chip = false;
ah->ah_radio_2ghz_revision = ath5k_hw_radio_revision(ah,
CHANNEL_2GHZ);
} else if (ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4) ||
ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4) ||
ah->ah_phy_revision == AR5K_SREV_PHY_2425) {
ah->ah_radio = AR5K_RF2425;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2425;
} else if (srev == AR5K_SREV_AR5213A &&
ah->ah_phy_revision == AR5K_SREV_PHY_5212B) {
ah->ah_radio = AR5K_RF5112;
ah->ah_single_chip = false;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5112B;
} else if (ah->ah_mac_version == (AR5K_SREV_AR2415 >> 4)) {
ah->ah_radio = AR5K_RF2316;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2316;
} else if (ah->ah_mac_version == (AR5K_SREV_AR5414 >> 4) ||
ah->ah_phy_revision == AR5K_SREV_PHY_5413) {
ah->ah_radio = AR5K_RF5413;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_5413;
} else if (ah->ah_mac_version == (AR5K_SREV_AR2414 >> 4) ||
ah->ah_phy_revision == AR5K_SREV_PHY_2413) {
ah->ah_radio = AR5K_RF2413;
ah->ah_single_chip = true;
ah->ah_radio_5ghz_revision = AR5K_SREV_RAD_2413;
} else {
ATH5K_ERR(sc, "Couldn't identify radio revision.\n");
ret = -ENODEV;
goto err_free;
}
}
/* Return on unsuported chips (unsupported eeprom etc) */
if ((srev >= AR5K_SREV_AR5416) &&
(srev < AR5K_SREV_AR2425)) {
ATH5K_ERR(sc, "Device not yet supported.\n");
ret = -ENODEV;
goto err_free;
}
/*
* Write PCI-E power save settings
*/
if ((ah->ah_version == AR5K_AR5212) && (pdev->is_pcie)) {
ath5k_hw_reg_write(ah, 0x9248fc00, AR5K_PCIE_SERDES);
ath5k_hw_reg_write(ah, 0x24924924, AR5K_PCIE_SERDES);
/* Shut off RX when elecidle is asserted */
ath5k_hw_reg_write(ah, 0x28000039, AR5K_PCIE_SERDES);
ath5k_hw_reg_write(ah, 0x53160824, AR5K_PCIE_SERDES);
/* TODO: EEPROM work */
ath5k_hw_reg_write(ah, 0xe5980579, AR5K_PCIE_SERDES);
/* Shut off PLL and CLKREQ active in L1 */
ath5k_hw_reg_write(ah, 0x001defff, AR5K_PCIE_SERDES);
/* Preserce other settings */
ath5k_hw_reg_write(ah, 0x1aaabe40, AR5K_PCIE_SERDES);
ath5k_hw_reg_write(ah, 0xbe105554, AR5K_PCIE_SERDES);
ath5k_hw_reg_write(ah, 0x000e3007, AR5K_PCIE_SERDES);
/* Reset SERDES to load new settings */
ath5k_hw_reg_write(ah, 0x00000000, AR5K_PCIE_SERDES_RESET);
mdelay(1);
}
/*
* POST
*/
ret = ath5k_hw_post(ah);
if (ret)
goto err_free;
/* Enable pci core retry fix on Hainan (5213A) and later chips */
if (srev >= AR5K_SREV_AR5213A)
ath5k_hw_reg_write(ah, AR5K_PCICFG_RETRY_FIX, AR5K_PCICFG);
/*
* Get card capabilities, calibration values etc
* TODO: EEPROM work
*/
ret = ath5k_eeprom_init(ah);
if (ret) {
ATH5K_ERR(sc, "unable to init EEPROM\n");
goto err_free;
}
/* Get misc capabilities */
ret = ath5k_hw_set_capabilities(ah);
if (ret) {
ATH5K_ERR(sc, "unable to get device capabilities: 0x%04x\n",
sc->pdev->device);
goto err_free;
}
if (srev >= AR5K_SREV_AR2414) {
ah->ah_combined_mic = true;
AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE,
AR5K_MISC_MODE_COMBINED_MIC);
}
/* MAC address is cleared until add_interface */
ath5k_hw_set_lladdr(ah, (u8[ETH_ALEN]){});
/* Set BSSID to bcast address: ff:ff:ff:ff:ff:ff for now */
memset(ah->ah_bssid, 0xff, ETH_ALEN);
ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
ath5k_hw_set_opmode(ah);
ath5k_hw_rfgain_opt_init(ah);
return ah;
err_free:
kfree(ah);
err:
return ERR_PTR(ret);
}
/**
* ath5k_hw_detach - Free the ath5k_hw struct
*
* @ah: The &struct ath5k_hw
*/
void ath5k_hw_detach(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
__set_bit(ATH_STAT_INVALID, ah->ah_sc->status);
if (ah->ah_rf_banks != NULL)
kfree(ah->ah_rf_banks);
ath5k_eeprom_detach(ah);
/* assume interrupts are down */
kfree(ah);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,190 @@
/*-
* Copyright (c) 2002-2007 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
*/
/*
* Defintions for the Atheros Wireless LAN controller driver.
*/
#ifndef _DEV_ATH_ATHVAR_H
#define _DEV_ATH_ATHVAR_H
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/wireless.h>
#include <linux/if_ether.h>
#include <linux/leds.h>
#include "ath5k.h"
#include "debug.h"
#define ATH_RXBUF 40 /* number of RX buffers */
#define ATH_TXBUF 200 /* number of TX buffers */
#define ATH_BCBUF 1 /* number of beacon buffers */
struct ath5k_buf {
struct list_head list;
unsigned int flags; /* rx descriptor flags */
struct ath5k_desc *desc; /* virtual addr of desc */
dma_addr_t daddr; /* physical addr of desc */
struct sk_buff *skb; /* skbuff for buf */
dma_addr_t skbaddr;/* physical addr of skb data */
};
/*
* 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 ath5k_txq {
unsigned int qnum; /* hardware q number */
u32 *link; /* link ptr in last TX desc */
struct list_head q; /* transmit queue */
spinlock_t lock; /* lock on q and link */
bool setup;
};
#define ATH5K_LED_MAX_NAME_LEN 31
/*
* State for LED triggers
*/
struct ath5k_led
{
char name[ATH5K_LED_MAX_NAME_LEN + 1]; /* name of the LED in sysfs */
struct ath5k_softc *sc; /* driver state */
struct led_classdev led_dev; /* led classdev */
};
#if CHAN_DEBUG
#define ATH_CHAN_MAX (26+26+26+200+200)
#else
#define ATH_CHAN_MAX (14+14+14+252+20)
#endif
/* Software Carrier, keeps track of the driver state
* associated with an instance of a device */
struct ath5k_softc {
struct pci_dev *pdev; /* for dma mapping */
void __iomem *iobase; /* address of the device */
struct mutex lock; /* dev-level lock */
/* FIXME: how many does it really need? */
struct ieee80211_tx_queue_stats tx_stats[16];
struct ieee80211_low_level_stats ll_stats;
struct ieee80211_hw *hw; /* IEEE 802.11 common */
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
struct ieee80211_channel channels[ATH_CHAN_MAX];
struct ieee80211_rate rates[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
s8 rate_idx[IEEE80211_NUM_BANDS][AR5K_MAX_RATES];
enum nl80211_iftype opmode;
struct ath5k_hw *ah; /* Atheros HW */
struct ieee80211_supported_band *curband;
#ifdef CONFIG_ATH5K_DEBUG
struct ath5k_dbg_info debug; /* debug info */
#endif /* CONFIG_ATH5K_DEBUG */
struct ath5k_buf *bufptr; /* allocated buffer ptr */
struct ath5k_desc *desc; /* TX/RX descriptors */
dma_addr_t desc_daddr; /* DMA (physical) address */
size_t desc_len; /* size of TX/RX descriptors */
u16 cachelsz; /* cache line size */
DECLARE_BITMAP(status, 5);
#define ATH_STAT_INVALID 0 /* disable hardware accesses */
#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */
#define ATH_STAT_PROMISC 2
#define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */
#define ATH_STAT_STARTED 4 /* opened & irqs enabled */
unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
unsigned int curmode; /* current phy mode */
struct ieee80211_channel *curchan; /* current h/w channel */
struct ieee80211_vif *vif;
enum ath5k_int imask; /* interrupt mask copy */
DECLARE_BITMAP(keymap, AR5K_KEYCACHE_SIZE); /* key use bit map */
u8 bssidmask[ETH_ALEN];
unsigned int led_pin, /* GPIO pin for driving LED */
led_on; /* pin setting for LED on */
struct tasklet_struct restq; /* reset tasklet */
unsigned int rxbufsize; /* rx size based on mtu */
struct list_head rxbuf; /* receive buffer */
spinlock_t rxbuflock;
u32 *rxlink; /* link ptr in last RX desc */
struct tasklet_struct rxtq; /* rx intr tasklet */
struct ath5k_led rx_led; /* rx led */
struct list_head txbuf; /* transmit buffer */
spinlock_t txbuflock;
unsigned int txbuf_len; /* buf count in txbuf list */
struct ath5k_txq txqs[2]; /* beacon and tx */
struct ath5k_txq *txq; /* beacon and tx*/
struct tasklet_struct txtq; /* tx intr tasklet */
struct ath5k_led tx_led; /* tx led */
spinlock_t block; /* protects beacon */
struct tasklet_struct beacontq; /* beacon intr tasklet */
struct ath5k_buf *bbuf; /* beacon buffer */
unsigned int bhalq, /* SW q for outgoing beacons */
bmisscount, /* missed beacon transmits */
bintval, /* beacon interval in TU */
bsent;
unsigned int nexttbtt; /* next beacon time in TU */
struct timer_list calib_tim; /* calibration timer */
int power_level; /* Requested tx power in dbm */
bool assoc; /* assocate state */
};
#define ath5k_hw_hasbssidmask(_ah) \
(ath5k_hw_get_capability(_ah, AR5K_CAP_BSSIDMASK, 0, NULL) == 0)
#define ath5k_hw_hasveol(_ah) \
(ath5k_hw_get_capability(_ah, AR5K_CAP_VEOL, 0, NULL) == 0)
#endif

View File

@@ -0,0 +1,195 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
* Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
*
* Permission to use, copy, modify, and 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.
*
*/
/**************\
* Capabilities *
\**************/
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "base.h"
/*
* Fill the capabilities struct
* TODO: Merge this with EEPROM code when we are done with it
*/
int ath5k_hw_set_capabilities(struct ath5k_hw *ah)
{
u16 ee_header;
ATH5K_TRACE(ah->ah_sc);
/* Capabilities stored in the EEPROM */
ee_header = ah->ah_capabilities.cap_eeprom.ee_header;
if (ah->ah_version == AR5K_AR5210) {
/*
* Set radio capabilities
* (The AR5110 only supports the middle 5GHz band)
*/
ah->ah_capabilities.cap_range.range_5ghz_min = 5120;
ah->ah_capabilities.cap_range.range_5ghz_max = 5430;
ah->ah_capabilities.cap_range.range_2ghz_min = 0;
ah->ah_capabilities.cap_range.range_2ghz_max = 0;
/* Set supported modes */
__set_bit(AR5K_MODE_11A, ah->ah_capabilities.cap_mode);
__set_bit(AR5K_MODE_11A_TURBO, ah->ah_capabilities.cap_mode);
} else {
/*
* XXX The tranceiver supports frequencies from 4920 to 6100GHz
* XXX and from 2312 to 2732GHz. There are problems with the
* XXX current ieee80211 implementation because the IEEE
* XXX channel mapping does not support negative channel
* XXX numbers (2312MHz is channel -19). Of course, this
* XXX doesn't matter because these channels are out of range
* XXX but some regulation domains like MKK (Japan) will
* XXX support frequencies somewhere around 4.8GHz.
*/
/*
* Set radio capabilities
*/
if (AR5K_EEPROM_HDR_11A(ee_header)) {
/* 4920 */
ah->ah_capabilities.cap_range.range_5ghz_min = 5005;
ah->ah_capabilities.cap_range.range_5ghz_max = 6100;
/* Set supported modes */
__set_bit(AR5K_MODE_11A,
ah->ah_capabilities.cap_mode);
__set_bit(AR5K_MODE_11A_TURBO,
ah->ah_capabilities.cap_mode);
if (ah->ah_version == AR5K_AR5212)
__set_bit(AR5K_MODE_11G_TURBO,
ah->ah_capabilities.cap_mode);
}
/* Enable 802.11b if a 2GHz capable radio (2111/5112) is
* connected */
if (AR5K_EEPROM_HDR_11B(ee_header) ||
(AR5K_EEPROM_HDR_11G(ee_header) &&
ah->ah_version != AR5K_AR5211)) {
/* 2312 */
ah->ah_capabilities.cap_range.range_2ghz_min = 2412;
ah->ah_capabilities.cap_range.range_2ghz_max = 2732;
if (AR5K_EEPROM_HDR_11B(ee_header))
__set_bit(AR5K_MODE_11B,
ah->ah_capabilities.cap_mode);
if (AR5K_EEPROM_HDR_11G(ee_header) &&
ah->ah_version != AR5K_AR5211)
__set_bit(AR5K_MODE_11G,
ah->ah_capabilities.cap_mode);
}
}
/* GPIO */
ah->ah_gpio_npins = AR5K_NUM_GPIO;
/* Set number of supported TX queues */
if (ah->ah_version == AR5K_AR5210)
ah->ah_capabilities.cap_queues.q_tx_num =
AR5K_NUM_TX_QUEUES_NOQCU;
else
ah->ah_capabilities.cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES;
return 0;
}
/* Main function used by the driver part to check caps */
int ath5k_hw_get_capability(struct ath5k_hw *ah,
enum ath5k_capability_type cap_type,
u32 capability, u32 *result)
{
ATH5K_TRACE(ah->ah_sc);
switch (cap_type) {
case AR5K_CAP_NUM_TXQUEUES:
if (result) {
if (ah->ah_version == AR5K_AR5210)
*result = AR5K_NUM_TX_QUEUES_NOQCU;
else
*result = AR5K_NUM_TX_QUEUES;
goto yes;
}
case AR5K_CAP_VEOL:
goto yes;
case AR5K_CAP_COMPRESSION:
if (ah->ah_version == AR5K_AR5212)
goto yes;
else
goto no;
case AR5K_CAP_BURST:
goto yes;
case AR5K_CAP_TPC:
goto yes;
case AR5K_CAP_BSSIDMASK:
if (ah->ah_version == AR5K_AR5212)
goto yes;
else
goto no;
case AR5K_CAP_XR:
if (ah->ah_version == AR5K_AR5212)
goto yes;
else
goto no;
default:
goto no;
}
no:
return -EINVAL;
yes:
return 0;
}
/*
* TODO: Following functions should be part of a new function
* set_capability
*/
int ath5k_hw_enable_pspoll(struct ath5k_hw *ah, u8 *bssid,
u16 assoc_id)
{
ATH5K_TRACE(ah->ah_sc);
if (ah->ah_version == AR5K_AR5210) {
AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1,
AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA);
return 0;
}
return -EIO;
}
int ath5k_hw_disable_pspoll(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
if (ah->ah_version == AR5K_AR5210) {
AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1,
AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA);
return 0;
}
return -EIO;
}

View File

@@ -0,0 +1,538 @@
/*
* Copyright (c) 2007-2008 Bruno Randolf <bruno@thinktube.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
* Copyright (c) 2004-2005 Atheros Communications, Inc.
* Copyright (c) 2006 Devicescape Software, Inc.
* Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
* Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
#include "base.h"
#include "debug.h"
static unsigned int ath5k_debug;
module_param_named(debug, ath5k_debug, uint, 0);
#ifdef CONFIG_ATH5K_DEBUG
#include <linux/seq_file.h>
#include "reg.h"
static struct dentry *ath5k_global_debugfs;
static int ath5k_debugfs_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
/* debugfs: registers */
struct reg {
const char *name;
int addr;
};
#define REG_STRUCT_INIT(r) { #r, r }
/* just a few random registers, might want to add more */
static const struct reg regs[] = {
REG_STRUCT_INIT(AR5K_CR),
REG_STRUCT_INIT(AR5K_RXDP),
REG_STRUCT_INIT(AR5K_CFG),
REG_STRUCT_INIT(AR5K_IER),
REG_STRUCT_INIT(AR5K_BCR),
REG_STRUCT_INIT(AR5K_RTSD0),
REG_STRUCT_INIT(AR5K_RTSD1),
REG_STRUCT_INIT(AR5K_TXCFG),
REG_STRUCT_INIT(AR5K_RXCFG),
REG_STRUCT_INIT(AR5K_RXJLA),
REG_STRUCT_INIT(AR5K_MIBC),
REG_STRUCT_INIT(AR5K_TOPS),
REG_STRUCT_INIT(AR5K_RXNOFRM),
REG_STRUCT_INIT(AR5K_TXNOFRM),
REG_STRUCT_INIT(AR5K_RPGTO),
REG_STRUCT_INIT(AR5K_RFCNT),
REG_STRUCT_INIT(AR5K_MISC),
REG_STRUCT_INIT(AR5K_QCUDCU_CLKGT),
REG_STRUCT_INIT(AR5K_ISR),
REG_STRUCT_INIT(AR5K_PISR),
REG_STRUCT_INIT(AR5K_SISR0),
REG_STRUCT_INIT(AR5K_SISR1),
REG_STRUCT_INIT(AR5K_SISR2),
REG_STRUCT_INIT(AR5K_SISR3),
REG_STRUCT_INIT(AR5K_SISR4),
REG_STRUCT_INIT(AR5K_IMR),
REG_STRUCT_INIT(AR5K_PIMR),
REG_STRUCT_INIT(AR5K_SIMR0),
REG_STRUCT_INIT(AR5K_SIMR1),
REG_STRUCT_INIT(AR5K_SIMR2),
REG_STRUCT_INIT(AR5K_SIMR3),
REG_STRUCT_INIT(AR5K_SIMR4),
REG_STRUCT_INIT(AR5K_DCM_ADDR),
REG_STRUCT_INIT(AR5K_DCCFG),
REG_STRUCT_INIT(AR5K_CCFG),
REG_STRUCT_INIT(AR5K_CPC0),
REG_STRUCT_INIT(AR5K_CPC1),
REG_STRUCT_INIT(AR5K_CPC2),
REG_STRUCT_INIT(AR5K_CPC3),
REG_STRUCT_INIT(AR5K_CPCOVF),
REG_STRUCT_INIT(AR5K_RESET_CTL),
REG_STRUCT_INIT(AR5K_SLEEP_CTL),
REG_STRUCT_INIT(AR5K_INTPEND),
REG_STRUCT_INIT(AR5K_SFR),
REG_STRUCT_INIT(AR5K_PCICFG),
REG_STRUCT_INIT(AR5K_GPIOCR),
REG_STRUCT_INIT(AR5K_GPIODO),
REG_STRUCT_INIT(AR5K_SREV),
};
static void *reg_start(struct seq_file *seq, loff_t *pos)
{
return *pos < ARRAY_SIZE(regs) ? (void *)&regs[*pos] : NULL;
}
static void reg_stop(struct seq_file *seq, void *p)
{
/* nothing to do */
}
static void *reg_next(struct seq_file *seq, void *p, loff_t *pos)
{
++*pos;
return *pos < ARRAY_SIZE(regs) ? (void *)&regs[*pos] : NULL;
}
static int reg_show(struct seq_file *seq, void *p)
{
struct ath5k_softc *sc = seq->private;
struct reg *r = p;
seq_printf(seq, "%-25s0x%08x\n", r->name,
ath5k_hw_reg_read(sc->ah, r->addr));
return 0;
}
static const struct seq_operations register_seq_ops = {
.start = reg_start,
.next = reg_next,
.stop = reg_stop,
.show = reg_show
};
static int open_file_registers(struct inode *inode, struct file *file)
{
struct seq_file *s;
int res;
res = seq_open(file, &register_seq_ops);
if (res == 0) {
s = file->private_data;
s->private = inode->i_private;
}
return res;
}
static const struct file_operations fops_registers = {
.open = open_file_registers,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.owner = THIS_MODULE,
};
/* debugfs: beacons */
static ssize_t read_file_beacon(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
struct ath5k_hw *ah = sc->ah;
char buf[500];
unsigned int len = 0;
unsigned int v;
u64 tsf;
v = ath5k_hw_reg_read(sc->ah, AR5K_BEACON);
len += snprintf(buf+len, sizeof(buf)-len,
"%-24s0x%08x\tintval: %d\tTIM: 0x%x\n",
"AR5K_BEACON", v, v & AR5K_BEACON_PERIOD,
(v & AR5K_BEACON_TIM) >> AR5K_BEACON_TIM_S);
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\n",
"AR5K_LAST_TSTP", ath5k_hw_reg_read(sc->ah, AR5K_LAST_TSTP));
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\n\n",
"AR5K_BEACON_CNT", ath5k_hw_reg_read(sc->ah, AR5K_BEACON_CNT));
v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER0);
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER0 (TBTT)", v, v);
v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER1);
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER1 (DMA)", v, v >> 3);
v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER2);
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER2 (SWBA)", v, v >> 3);
v = ath5k_hw_reg_read(sc->ah, AR5K_TIMER3);
len += snprintf(buf+len, sizeof(buf)-len, "%-24s0x%08x\tTU: %08x\n",
"AR5K_TIMER3 (ATIM)", v, v);
tsf = ath5k_hw_get_tsf64(sc->ah);
len += snprintf(buf+len, sizeof(buf)-len,
"TSF\t\t0x%016llx\tTU: %08x\n",
(unsigned long long)tsf, TSF_TO_TU(tsf));
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t write_file_beacon(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
struct ath5k_hw *ah = sc->ah;
char buf[20];
if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
return -EFAULT;
if (strncmp(buf, "disable", 7) == 0) {
AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
printk(KERN_INFO "debugfs disable beacons\n");
} else if (strncmp(buf, "enable", 6) == 0) {
AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
printk(KERN_INFO "debugfs enable beacons\n");
}
return count;
}
static const struct file_operations fops_beacon = {
.read = read_file_beacon,
.write = write_file_beacon,
.open = ath5k_debugfs_open,
.owner = THIS_MODULE,
};
/* debugfs: reset */
static ssize_t write_file_reset(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
tasklet_schedule(&sc->restq);
return count;
}
static const struct file_operations fops_reset = {
.write = write_file_reset,
.open = ath5k_debugfs_open,
.owner = THIS_MODULE,
};
/* debugfs: debug level */
static const struct {
enum ath5k_debug_level level;
const char *name;
const char *desc;
} dbg_info[] = {
{ ATH5K_DEBUG_RESET, "reset", "reset and initialization" },
{ ATH5K_DEBUG_INTR, "intr", "interrupt handling" },
{ ATH5K_DEBUG_MODE, "mode", "mode init/setup" },
{ ATH5K_DEBUG_XMIT, "xmit", "basic xmit operation" },
{ ATH5K_DEBUG_BEACON, "beacon", "beacon handling" },
{ ATH5K_DEBUG_CALIBRATE, "calib", "periodic calibration" },
{ ATH5K_DEBUG_TXPOWER, "txpower", "transmit power setting" },
{ ATH5K_DEBUG_LED, "led", "LED management" },
{ ATH5K_DEBUG_DUMP_RX, "dumprx", "print received skb content" },
{ ATH5K_DEBUG_DUMP_TX, "dumptx", "print transmit skb content" },
{ ATH5K_DEBUG_DUMPBANDS, "dumpbands", "dump bands" },
{ ATH5K_DEBUG_TRACE, "trace", "trace function calls" },
{ ATH5K_DEBUG_ANY, "all", "show all debug levels" },
};
static ssize_t read_file_debug(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
char buf[700];
unsigned int len = 0;
unsigned int i;
len += snprintf(buf+len, sizeof(buf)-len,
"DEBUG LEVEL: 0x%08x\n\n", sc->debug.level);
for (i = 0; i < ARRAY_SIZE(dbg_info) - 1; i++) {
len += snprintf(buf+len, sizeof(buf)-len,
"%10s %c 0x%08x - %s\n", dbg_info[i].name,
sc->debug.level & dbg_info[i].level ? '+' : ' ',
dbg_info[i].level, dbg_info[i].desc);
}
len += snprintf(buf+len, sizeof(buf)-len,
"%10s %c 0x%08x - %s\n", dbg_info[i].name,
sc->debug.level == dbg_info[i].level ? '+' : ' ',
dbg_info[i].level, dbg_info[i].desc);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t write_file_debug(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
unsigned int i;
char buf[20];
if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
return -EFAULT;
for (i = 0; i < ARRAY_SIZE(dbg_info); i++) {
if (strncmp(buf, dbg_info[i].name,
strlen(dbg_info[i].name)) == 0) {
sc->debug.level ^= dbg_info[i].level; /* toggle bit */
break;
}
}
return count;
}
static const struct file_operations fops_debug = {
.read = read_file_debug,
.write = write_file_debug,
.open = ath5k_debugfs_open,
.owner = THIS_MODULE,
};
/* init */
void
ath5k_debug_init(void)
{
ath5k_global_debugfs = debugfs_create_dir("ath5k", NULL);
}
void
ath5k_debug_init_device(struct ath5k_softc *sc)
{
sc->debug.level = ath5k_debug;
sc->debug.debugfs_phydir = debugfs_create_dir(wiphy_name(sc->hw->wiphy),
ath5k_global_debugfs);
sc->debug.debugfs_debug = debugfs_create_file("debug", S_IWUSR | S_IRUGO,
sc->debug.debugfs_phydir, sc, &fops_debug);
sc->debug.debugfs_registers = debugfs_create_file("registers", S_IRUGO,
sc->debug.debugfs_phydir, sc, &fops_registers);
sc->debug.debugfs_beacon = debugfs_create_file("beacon", S_IWUSR | S_IRUGO,
sc->debug.debugfs_phydir, sc, &fops_beacon);
sc->debug.debugfs_reset = debugfs_create_file("reset", S_IWUSR,
sc->debug.debugfs_phydir, sc, &fops_reset);
}
void
ath5k_debug_finish(void)
{
debugfs_remove(ath5k_global_debugfs);
}
void
ath5k_debug_finish_device(struct ath5k_softc *sc)
{
debugfs_remove(sc->debug.debugfs_debug);
debugfs_remove(sc->debug.debugfs_registers);
debugfs_remove(sc->debug.debugfs_beacon);
debugfs_remove(sc->debug.debugfs_reset);
debugfs_remove(sc->debug.debugfs_phydir);
}
/* functions used in other places */
void
ath5k_debug_dump_bands(struct ath5k_softc *sc)
{
unsigned int b, i;
if (likely(!(sc->debug.level & ATH5K_DEBUG_DUMPBANDS)))
return;
BUG_ON(!sc->sbands);
for (b = 0; b < IEEE80211_NUM_BANDS; b++) {
struct ieee80211_supported_band *band = &sc->sbands[b];
char bname[5];
switch (band->band) {
case IEEE80211_BAND_2GHZ:
strcpy(bname, "2 GHz");
break;
case IEEE80211_BAND_5GHZ:
strcpy(bname, "5 GHz");
break;
default:
printk(KERN_DEBUG "Band not supported: %d\n",
band->band);
return;
}
printk(KERN_DEBUG "Band %s: channels %d, rates %d\n", bname,
band->n_channels, band->n_bitrates);
printk(KERN_DEBUG " channels:\n");
for (i = 0; i < band->n_channels; i++)
printk(KERN_DEBUG " %3d %d %.4x %.4x\n",
ieee80211_frequency_to_channel(
band->channels[i].center_freq),
band->channels[i].center_freq,
band->channels[i].hw_value,
band->channels[i].flags);
printk(KERN_DEBUG " rates:\n");
for (i = 0; i < band->n_bitrates; i++)
printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n",
band->bitrates[i].bitrate,
band->bitrates[i].hw_value,
band->bitrates[i].flags,
band->bitrates[i].hw_value_short);
}
}
static inline void
ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done,
struct ath5k_rx_status *rs)
{
struct ath5k_desc *ds = bf->desc;
struct ath5k_hw_all_rx_desc *rd = &ds->ud.ds_rx;
printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n",
ds, (unsigned long long)bf->daddr,
ds->ds_link, ds->ds_data,
rd->rx_ctl.rx_control_0, rd->rx_ctl.rx_control_1,
rd->u.rx_stat.rx_status_0, rd->u.rx_stat.rx_status_0,
!done ? ' ' : (rs->rs_status == 0) ? '*' : '!');
}
void
ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah)
{
struct ath5k_desc *ds;
struct ath5k_buf *bf;
struct ath5k_rx_status rs = {};
int status;
if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
return;
printk(KERN_DEBUG "rx queue %x, link %p\n",
ath5k_hw_get_rxdp(ah), sc->rxlink);
spin_lock_bh(&sc->rxbuflock);
list_for_each_entry(bf, &sc->rxbuf, list) {
ds = bf->desc;
status = ah->ah_proc_rx_desc(ah, ds, &rs);
if (!status)
ath5k_debug_printrxbuf(bf, status == 0, &rs);
}
spin_unlock_bh(&sc->rxbuflock);
}
void
ath5k_debug_dump_skb(struct ath5k_softc *sc,
struct sk_buff *skb, const char *prefix, int tx)
{
char buf[16];
if (likely(!((tx && (sc->debug.level & ATH5K_DEBUG_DUMP_TX)) ||
(!tx && (sc->debug.level & ATH5K_DEBUG_DUMP_RX)))))
return;
snprintf(buf, sizeof(buf), "%s %s", wiphy_name(sc->hw->wiphy), prefix);
print_hex_dump_bytes(buf, DUMP_PREFIX_NONE, skb->data,
min(200U, skb->len));
printk(KERN_DEBUG "\n");
}
void
ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf)
{
struct ath5k_desc *ds = bf->desc;
struct ath5k_hw_5212_tx_desc *td = &ds->ud.ds_tx5212;
struct ath5k_tx_status ts = {};
int done;
if (likely(!(sc->debug.level & ATH5K_DEBUG_RESET)))
return;
done = sc->ah->ah_proc_tx_desc(sc->ah, bf->desc, &ts);
printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x "
"%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link,
ds->ds_data, td->tx_ctl.tx_control_0, td->tx_ctl.tx_control_1,
td->tx_ctl.tx_control_2, td->tx_ctl.tx_control_3,
td->tx_stat.tx_status_0, td->tx_stat.tx_status_1,
done ? ' ' : (ts.ts_status == 0) ? '*' : '!');
}
#endif /* ifdef CONFIG_ATH5K_DEBUG */

View File

@@ -0,0 +1,203 @@
/*
* Copyright (c) 2007 Bruno Randolf <bruno@thinktube.com>
*
* This file is free software: you may copy, redistribute and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
* Copyright (c) 2004-2005 Atheros Communications, Inc.
* Copyright (c) 2006 Devicescape Software, Inc.
* Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
* Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
#ifndef _ATH5K_DEBUG_H
#define _ATH5K_DEBUG_H
struct ath5k_softc;
struct ath5k_hw;
struct sk_buff;
struct ath5k_buf;
struct ath5k_dbg_info {
unsigned int level; /* debug level */
/* debugfs entries */
struct dentry *debugfs_phydir;
struct dentry *debugfs_debug;
struct dentry *debugfs_registers;
struct dentry *debugfs_beacon;
struct dentry *debugfs_reset;
};
/**
* enum ath5k_debug_level - ath5k debug level
*
* @ATH5K_DEBUG_RESET: reset processing
* @ATH5K_DEBUG_INTR: interrupt handling
* @ATH5K_DEBUG_MODE: mode init/setup
* @ATH5K_DEBUG_XMIT: basic xmit operation
* @ATH5K_DEBUG_BEACON: beacon handling
* @ATH5K_DEBUG_CALIBRATE: periodic calibration
* @ATH5K_DEBUG_TXPOWER: transmit power setting
* @ATH5K_DEBUG_LED: led management
* @ATH5K_DEBUG_DUMP_RX: print received skb content
* @ATH5K_DEBUG_DUMP_TX: print transmit skb content
* @ATH5K_DEBUG_DUMPBANDS: dump bands
* @ATH5K_DEBUG_TRACE: trace function calls
* @ATH5K_DEBUG_ANY: show at any debug level
*
* The debug level is used to control the amount and type of debugging output
* we want to see. The debug level is given in calls to ATH5K_DBG to specify
* where the message should appear, and the user can control the debugging
* messages he wants to see, either by the module parameter 'debug' on module
* load, or dynamically by using debugfs 'ath5k/phyX/debug'. these levels can
* be combined together by bitwise OR.
*/
enum ath5k_debug_level {
ATH5K_DEBUG_RESET = 0x00000001,
ATH5K_DEBUG_INTR = 0x00000002,
ATH5K_DEBUG_MODE = 0x00000004,
ATH5K_DEBUG_XMIT = 0x00000008,
ATH5K_DEBUG_BEACON = 0x00000010,
ATH5K_DEBUG_CALIBRATE = 0x00000020,
ATH5K_DEBUG_TXPOWER = 0x00000040,
ATH5K_DEBUG_LED = 0x00000080,
ATH5K_DEBUG_DUMP_RX = 0x00000100,
ATH5K_DEBUG_DUMP_TX = 0x00000200,
ATH5K_DEBUG_DUMPBANDS = 0x00000400,
ATH5K_DEBUG_TRACE = 0x00001000,
ATH5K_DEBUG_ANY = 0xffffffff
};
#ifdef CONFIG_ATH5K_DEBUG
#define ATH5K_TRACE(_sc) do { \
if (unlikely((_sc)->debug.level & ATH5K_DEBUG_TRACE)) \
printk(KERN_DEBUG "ath5k trace %s:%d\n", __func__, __LINE__); \
} while (0)
#define ATH5K_DBG(_sc, _m, _fmt, ...) do { \
if (unlikely((_sc)->debug.level & (_m) && net_ratelimit())) \
ATH5K_PRINTK(_sc, KERN_DEBUG, "(%s:%d): " _fmt, \
__func__, __LINE__, ##__VA_ARGS__); \
} while (0)
#define ATH5K_DBG_UNLIMIT(_sc, _m, _fmt, ...) do { \
if (unlikely((_sc)->debug.level & (_m))) \
ATH5K_PRINTK(_sc, KERN_DEBUG, "(%s:%d): " _fmt, \
__func__, __LINE__, ##__VA_ARGS__); \
} while (0)
void
ath5k_debug_init(void);
void
ath5k_debug_init_device(struct ath5k_softc *sc);
void
ath5k_debug_finish(void);
void
ath5k_debug_finish_device(struct ath5k_softc *sc);
void
ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah);
void
ath5k_debug_dump_bands(struct ath5k_softc *sc);
void
ath5k_debug_dump_skb(struct ath5k_softc *sc,
struct sk_buff *skb, const char *prefix, int tx);
void
ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf);
#else /* no debugging */
#include <linux/compiler.h>
#define ATH5K_TRACE(_sc) typecheck(struct ath5k_softc *, (_sc))
static inline void __attribute__ ((format (printf, 3, 4)))
ATH5K_DBG(struct ath5k_softc *sc, unsigned int m, const char *fmt, ...) {}
static inline void __attribute__ ((format (printf, 3, 4)))
ATH5K_DBG_UNLIMIT(struct ath5k_softc *sc, unsigned int m, const char *fmt, ...)
{}
static inline void
ath5k_debug_init(void) {}
static inline void
ath5k_debug_init_device(struct ath5k_softc *sc) {}
static inline void
ath5k_debug_finish(void) {}
static inline void
ath5k_debug_finish_device(struct ath5k_softc *sc) {}
static inline void
ath5k_debug_printrxbuffs(struct ath5k_softc *sc, struct ath5k_hw *ah) {}
static inline void
ath5k_debug_dump_bands(struct ath5k_softc *sc) {}
static inline void
ath5k_debug_dump_skb(struct ath5k_softc *sc,
struct sk_buff *skb, const char *prefix, int tx) {}
static inline void
ath5k_debug_printtxbuf(struct ath5k_softc *sc, struct ath5k_buf *bf) {}
#endif /* ifdef CONFIG_ATH5K_DEBUG */
#endif /* ifndef _ATH5K_DEBUG_H */

View File

@@ -0,0 +1,696 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
* Copyright (c) 2007-2008 Pavel Roskin <proski@gnu.org>
*
* Permission to use, copy, modify, and 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.
*
*/
/******************************\
Hardware Descriptor Functions
\******************************/
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "base.h"
/*
* TX Descriptors
*/
/*
* Initialize the 2-word tx control descriptor on 5210/5211
*/
static int
ath5k_hw_setup_2word_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
unsigned int pkt_len, unsigned int hdr_len, enum ath5k_pkt_type type,
unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0,
unsigned int key_index, unsigned int antenna_mode, unsigned int flags,
unsigned int rtscts_rate, unsigned int rtscts_duration)
{
u32 frame_type;
struct ath5k_hw_2w_tx_ctl *tx_ctl;
unsigned int frame_len;
tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
/*
* Validate input
* - Zero retries don't make sense.
* - A zero rate will put the HW into a mode where it continously sends
* noise on the channel, so it is important to avoid this.
*/
if (unlikely(tx_tries0 == 0)) {
ATH5K_ERR(ah->ah_sc, "zero retries\n");
WARN_ON(1);
return -EINVAL;
}
if (unlikely(tx_rate0 == 0)) {
ATH5K_ERR(ah->ah_sc, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
/* Clear descriptor */
memset(&desc->ud.ds_tx5210, 0, sizeof(struct ath5k_hw_5210_tx_desc));
/* Setup control descriptor */
/* Verify and set frame length */
/* remove padding we might have added before */
frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN;
if (frame_len & ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
tx_ctl->tx_control_0 = frame_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN;
/* Verify and set buffer length */
/* NB: beacon's BufLen must be a multiple of 4 bytes */
if (type == AR5K_PKT_TYPE_BEACON)
pkt_len = roundup(pkt_len, 4);
if (pkt_len & ~AR5K_2W_TX_DESC_CTL1_BUF_LEN)
return -EINVAL;
tx_ctl->tx_control_1 = pkt_len & AR5K_2W_TX_DESC_CTL1_BUF_LEN;
/*
* Verify and set header length
* XXX: I only found that on 5210 code, does it work on 5211 ?
*/
if (ah->ah_version == AR5K_AR5210) {
if (hdr_len & ~AR5K_2W_TX_DESC_CTL0_HEADER_LEN)
return -EINVAL;
tx_ctl->tx_control_0 |=
AR5K_REG_SM(hdr_len, AR5K_2W_TX_DESC_CTL0_HEADER_LEN);
}
/*Diferences between 5210-5211*/
if (ah->ah_version == AR5K_AR5210) {
switch (type) {
case AR5K_PKT_TYPE_BEACON:
case AR5K_PKT_TYPE_PROBE_RESP:
frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY;
case AR5K_PKT_TYPE_PIFS:
frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS;
default:
frame_type = type /*<< 2 ?*/;
}
tx_ctl->tx_control_0 |=
AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) |
AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
} else {
tx_ctl->tx_control_0 |=
AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) |
AR5K_REG_SM(antenna_mode,
AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT);
tx_ctl->tx_control_1 |=
AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE);
}
#define _TX_FLAGS(_c, _flag) \
if (flags & AR5K_TXDESC_##_flag) { \
tx_ctl->tx_control_##_c |= \
AR5K_2W_TX_DESC_CTL##_c##_##_flag; \
}
_TX_FLAGS(0, CLRDMASK);
_TX_FLAGS(0, VEOL);
_TX_FLAGS(0, INTREQ);
_TX_FLAGS(0, RTSENA);
_TX_FLAGS(1, NOACK);
#undef _TX_FLAGS
/*
* WEP crap
*/
if (key_index != AR5K_TXKEYIX_INVALID) {
tx_ctl->tx_control_0 |=
AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
tx_ctl->tx_control_1 |=
AR5K_REG_SM(key_index,
AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
}
/*
* RTS/CTS Duration [5210 ?]
*/
if ((ah->ah_version == AR5K_AR5210) &&
(flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)))
tx_ctl->tx_control_1 |= rtscts_duration &
AR5K_2W_TX_DESC_CTL1_RTS_DURATION;
return 0;
}
/*
* Initialize the 4-word tx control descriptor on 5212
*/
static int ath5k_hw_setup_4word_tx_desc(struct ath5k_hw *ah,
struct ath5k_desc *desc, unsigned int pkt_len, unsigned int hdr_len,
enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0,
unsigned int tx_tries0, unsigned int key_index,
unsigned int antenna_mode, unsigned int flags,
unsigned int rtscts_rate,
unsigned int rtscts_duration)
{
struct ath5k_hw_4w_tx_ctl *tx_ctl;
unsigned int frame_len;
ATH5K_TRACE(ah->ah_sc);
tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
/*
* Validate input
* - Zero retries don't make sense.
* - A zero rate will put the HW into a mode where it continously sends
* noise on the channel, so it is important to avoid this.
*/
if (unlikely(tx_tries0 == 0)) {
ATH5K_ERR(ah->ah_sc, "zero retries\n");
WARN_ON(1);
return -EINVAL;
}
if (unlikely(tx_rate0 == 0)) {
ATH5K_ERR(ah->ah_sc, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
tx_power += ah->ah_txpower.txp_offset;
if (tx_power > AR5K_TUNE_MAX_TXPOWER)
tx_power = AR5K_TUNE_MAX_TXPOWER;
/* Clear descriptor */
memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc));
/* Setup control descriptor */
/* Verify and set frame length */
/* remove padding we might have added before */
frame_len = pkt_len - ath5k_pad_size(hdr_len) + FCS_LEN;
if (frame_len & ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN)
return -EINVAL;
tx_ctl->tx_control_0 = frame_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN;
/* Verify and set buffer length */
/* NB: beacon's BufLen must be a multiple of 4 bytes */
if (type == AR5K_PKT_TYPE_BEACON)
pkt_len = roundup(pkt_len, 4);
if (pkt_len & ~AR5K_4W_TX_DESC_CTL1_BUF_LEN)
return -EINVAL;
tx_ctl->tx_control_1 = pkt_len & AR5K_4W_TX_DESC_CTL1_BUF_LEN;
tx_ctl->tx_control_0 |=
AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) |
AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT);
tx_ctl->tx_control_1 |= AR5K_REG_SM(type,
AR5K_4W_TX_DESC_CTL1_FRAME_TYPE);
tx_ctl->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0);
tx_ctl->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
#define _TX_FLAGS(_c, _flag) \
if (flags & AR5K_TXDESC_##_flag) { \
tx_ctl->tx_control_##_c |= \
AR5K_4W_TX_DESC_CTL##_c##_##_flag; \
}
_TX_FLAGS(0, CLRDMASK);
_TX_FLAGS(0, VEOL);
_TX_FLAGS(0, INTREQ);
_TX_FLAGS(0, RTSENA);
_TX_FLAGS(0, CTSENA);
_TX_FLAGS(1, NOACK);
#undef _TX_FLAGS
/*
* WEP crap
*/
if (key_index != AR5K_TXKEYIX_INVALID) {
tx_ctl->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID;
tx_ctl->tx_control_1 |= AR5K_REG_SM(key_index,
AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX);
}
/*
* RTS/CTS
*/
if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) {
if ((flags & AR5K_TXDESC_RTSENA) &&
(flags & AR5K_TXDESC_CTSENA))
return -EINVAL;
tx_ctl->tx_control_2 |= rtscts_duration &
AR5K_4W_TX_DESC_CTL2_RTS_DURATION;
tx_ctl->tx_control_3 |= AR5K_REG_SM(rtscts_rate,
AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE);
}
return 0;
}
/*
* Initialize a 4-word multi rate retry tx control descriptor on 5212
*/
static int
ath5k_hw_setup_mrr_tx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2,
u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3)
{
struct ath5k_hw_4w_tx_ctl *tx_ctl;
/*
* Rates can be 0 as long as the retry count is 0 too.
* A zero rate and nonzero retry count will put the HW into a mode where
* it continously sends noise on the channel, so it is important to
* avoid this.
*/
if (unlikely((tx_rate1 == 0 && tx_tries1 != 0) ||
(tx_rate2 == 0 && tx_tries2 != 0) ||
(tx_rate3 == 0 && tx_tries3 != 0))) {
ATH5K_ERR(ah->ah_sc, "zero rate\n");
WARN_ON(1);
return -EINVAL;
}
if (ah->ah_version == AR5K_AR5212) {
tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
#define _XTX_TRIES(_n) \
if (tx_tries##_n) { \
tx_ctl->tx_control_2 |= \
AR5K_REG_SM(tx_tries##_n, \
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n); \
tx_ctl->tx_control_3 |= \
AR5K_REG_SM(tx_rate##_n, \
AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n); \
}
_XTX_TRIES(1);
_XTX_TRIES(2);
_XTX_TRIES(3);
#undef _XTX_TRIES
return 1;
}
return 0;
}
/* no mrr support for cards older than 5212 */
static int
ath5k_hw_setup_no_mrr(struct ath5k_hw *ah, struct ath5k_desc *desc,
unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2,
u_int tx_tries2, unsigned int tx_rate3, u_int tx_tries3)
{
return 0;
}
/*
* Proccess the tx status descriptor on 5210/5211
*/
static int ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc, struct ath5k_tx_status *ts)
{
struct ath5k_hw_2w_tx_ctl *tx_ctl;
struct ath5k_hw_tx_status *tx_status;
ATH5K_TRACE(ah->ah_sc);
tx_ctl = &desc->ud.ds_tx5210.tx_ctl;
tx_status = &desc->ud.ds_tx5210.tx_stat;
/* No frame has been send or error */
if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0))
return -EINPROGRESS;
/*
* Get descriptor status
*/
ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
ts->ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
/*TODO: ts->ts_virtcol + test*/
ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_SEQ_NUM);
ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
ts->ts_antenna = 1;
ts->ts_status = 0;
ts->ts_rate[0] = AR5K_REG_MS(tx_ctl->tx_control_0,
AR5K_2W_TX_DESC_CTL0_XMIT_RATE);
ts->ts_retry[0] = ts->ts_longretry;
ts->ts_final_idx = 0;
if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) {
if (tx_status->tx_status_0 &
AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
ts->ts_status |= AR5K_TXERR_XRETRY;
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
ts->ts_status |= AR5K_TXERR_FIFO;
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
ts->ts_status |= AR5K_TXERR_FILT;
}
return 0;
}
/*
* Proccess a tx status descriptor on 5212
*/
static int ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc, struct ath5k_tx_status *ts)
{
struct ath5k_hw_4w_tx_ctl *tx_ctl;
struct ath5k_hw_tx_status *tx_status;
ATH5K_TRACE(ah->ah_sc);
tx_ctl = &desc->ud.ds_tx5212.tx_ctl;
tx_status = &desc->ud.ds_tx5212.tx_stat;
/* No frame has been send or error */
if (unlikely(!(tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE)))
return -EINPROGRESS;
/*
* Get descriptor status
*/
ts->ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP);
ts->ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT);
ts->ts_longretry = AR5K_REG_MS(tx_status->tx_status_0,
AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT);
ts->ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_SEQ_NUM);
ts->ts_rssi = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH);
ts->ts_antenna = (tx_status->tx_status_1 &
AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1;
ts->ts_status = 0;
ts->ts_final_idx = AR5K_REG_MS(tx_status->tx_status_1,
AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX);
/* The longretry counter has the number of un-acked retries
* for the final rate. To get the total number of retries
* we have to add the retry counters for the other rates
* as well
*/
ts->ts_retry[ts->ts_final_idx] = ts->ts_longretry;
switch (ts->ts_final_idx) {
case 3:
ts->ts_rate[3] = AR5K_REG_MS(tx_ctl->tx_control_3,
AR5K_4W_TX_DESC_CTL3_XMIT_RATE3);
ts->ts_retry[2] = AR5K_REG_MS(tx_ctl->tx_control_2,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2);
ts->ts_longretry += ts->ts_retry[2];
/* fall through */
case 2:
ts->ts_rate[2] = AR5K_REG_MS(tx_ctl->tx_control_3,
AR5K_4W_TX_DESC_CTL3_XMIT_RATE2);
ts->ts_retry[1] = AR5K_REG_MS(tx_ctl->tx_control_2,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
ts->ts_longretry += ts->ts_retry[1];
/* fall through */
case 1:
ts->ts_rate[1] = AR5K_REG_MS(tx_ctl->tx_control_3,
AR5K_4W_TX_DESC_CTL3_XMIT_RATE1);
ts->ts_retry[0] = AR5K_REG_MS(tx_ctl->tx_control_2,
AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1);
ts->ts_longretry += ts->ts_retry[0];
/* fall through */
case 0:
ts->ts_rate[0] = tx_ctl->tx_control_3 &
AR5K_4W_TX_DESC_CTL3_XMIT_RATE0;
break;
}
/* TX error */
if (!(tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK)) {
if (tx_status->tx_status_0 &
AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES)
ts->ts_status |= AR5K_TXERR_XRETRY;
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN)
ts->ts_status |= AR5K_TXERR_FIFO;
if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED)
ts->ts_status |= AR5K_TXERR_FILT;
}
return 0;
}
/*
* RX Descriptors
*/
/*
* Initialize an rx control descriptor
*/
static int ath5k_hw_setup_rx_desc(struct ath5k_hw *ah, struct ath5k_desc *desc,
u32 size, unsigned int flags)
{
struct ath5k_hw_rx_ctl *rx_ctl;
ATH5K_TRACE(ah->ah_sc);
rx_ctl = &desc->ud.ds_rx.rx_ctl;
/*
* Clear the descriptor
* If we don't clean the status descriptor,
* while scanning we get too many results,
* most of them virtual, after some secs
* of scanning system hangs. M.F.
*/
memset(&desc->ud.ds_rx, 0, sizeof(struct ath5k_hw_all_rx_desc));
/* Setup descriptor */
rx_ctl->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN;
if (unlikely(rx_ctl->rx_control_1 != size))
return -EINVAL;
if (flags & AR5K_RXDESC_INTREQ)
rx_ctl->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ;
return 0;
}
/*
* Proccess the rx status descriptor on 5210/5211
*/
static int ath5k_hw_proc_5210_rx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc, struct ath5k_rx_status *rs)
{
struct ath5k_hw_rx_status *rx_status;
rx_status = &desc->ud.ds_rx.u.rx_stat;
/* No frame received / not ready */
if (unlikely(!(rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_DONE)))
return -EINPROGRESS;
/*
* Frame receive status
*/
rs->rs_datalen = rx_status->rx_status_0 &
AR5K_5210_RX_DESC_STATUS0_DATA_LEN;
rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL);
rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE);
rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA);
rs->rs_more = !!(rx_status->rx_status_0 &
AR5K_5210_RX_DESC_STATUS0_MORE);
/* TODO: this timestamp is 13 bit, later on we assume 15 bit */
rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
rs->rs_status = 0;
rs->rs_phyerr = 0;
/*
* Key table status
*/
if (rx_status->rx_status_1 & AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID)
rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5210_RX_DESC_STATUS1_KEY_INDEX);
else
rs->rs_keyix = AR5K_RXKEYIX_INVALID;
/*
* Receive/descriptor errors
*/
if (!(rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) {
if (rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_CRC;
if (rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN)
rs->rs_status |= AR5K_RXERR_FIFO;
if (rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_PHY_ERROR) {
rs->rs_status |= AR5K_RXERR_PHY;
rs->rs_phyerr |= AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5210_RX_DESC_STATUS1_PHY_ERROR);
}
if (rx_status->rx_status_1 &
AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_DECRYPT;
}
return 0;
}
/*
* Proccess the rx status descriptor on 5212
*/
static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah,
struct ath5k_desc *desc, struct ath5k_rx_status *rs)
{
struct ath5k_hw_rx_status *rx_status;
struct ath5k_hw_rx_error *rx_err;
ATH5K_TRACE(ah->ah_sc);
rx_status = &desc->ud.ds_rx.u.rx_stat;
/* Overlay on error */
rx_err = &desc->ud.ds_rx.u.rx_err;
/* No frame received / not ready */
if (unlikely(!(rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_DONE)))
return -EINPROGRESS;
/*
* Frame receive status
*/
rs->rs_datalen = rx_status->rx_status_0 &
AR5K_5212_RX_DESC_STATUS0_DATA_LEN;
rs->rs_rssi = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL);
rs->rs_rate = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE);
rs->rs_antenna = AR5K_REG_MS(rx_status->rx_status_0,
AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA);
rs->rs_more = !!(rx_status->rx_status_0 &
AR5K_5212_RX_DESC_STATUS0_MORE);
rs->rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP);
rs->rs_status = 0;
rs->rs_phyerr = 0;
/*
* Key table status
*/
if (rx_status->rx_status_1 & AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID)
rs->rs_keyix = AR5K_REG_MS(rx_status->rx_status_1,
AR5K_5212_RX_DESC_STATUS1_KEY_INDEX);
else
rs->rs_keyix = AR5K_RXKEYIX_INVALID;
/*
* Receive/descriptor errors
*/
if (!(rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK)) {
if (rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_CRC;
if (rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_PHY_ERROR) {
rs->rs_status |= AR5K_RXERR_PHY;
rs->rs_phyerr |= AR5K_REG_MS(rx_err->rx_error_1,
AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE);
}
if (rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR)
rs->rs_status |= AR5K_RXERR_DECRYPT;
if (rx_status->rx_status_1 &
AR5K_5212_RX_DESC_STATUS1_MIC_ERROR)
rs->rs_status |= AR5K_RXERR_MIC;
}
return 0;
}
/*
* Init function pointers inside ath5k_hw struct
*/
int ath5k_hw_init_desc_functions(struct ath5k_hw *ah)
{
if (ah->ah_version != AR5K_AR5210 &&
ah->ah_version != AR5K_AR5211 &&
ah->ah_version != AR5K_AR5212)
return -ENOTSUPP;
/* XXX: What is this magic value and where is it used ? */
if (ah->ah_version == AR5K_AR5212)
ah->ah_magic = AR5K_EEPROM_MAGIC_5212;
else if (ah->ah_version == AR5K_AR5211)
ah->ah_magic = AR5K_EEPROM_MAGIC_5211;
if (ah->ah_version == AR5K_AR5212) {
ah->ah_setup_rx_desc = ath5k_hw_setup_rx_desc;
ah->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc;
ah->ah_setup_mrr_tx_desc = ath5k_hw_setup_mrr_tx_desc;
ah->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status;
} else {
ah->ah_setup_rx_desc = ath5k_hw_setup_rx_desc;
ah->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc;
ah->ah_setup_mrr_tx_desc = ath5k_hw_setup_no_mrr;
ah->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status;
}
if (ah->ah_version == AR5K_AR5212)
ah->ah_proc_rx_desc = ath5k_hw_proc_5212_rx_status;
else if (ah->ah_version <= AR5K_AR5211)
ah->ah_proc_rx_desc = ath5k_hw_proc_5210_rx_status;
return 0;
}

View File

@@ -0,0 +1,332 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
*
* Permission to use, copy, modify, and 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.
*
*/
/*
* Internal RX/TX descriptor structures
* (rX: reserved fields possibily used by future versions of the ar5k chipset)
*/
/*
* common hardware RX control descriptor
*/
struct ath5k_hw_rx_ctl {
u32 rx_control_0; /* RX control word 0 */
u32 rx_control_1; /* RX control word 1 */
} __packed;
/* RX control word 0 field/sflags */
#define AR5K_DESC_RX_CTL0 0x00000000
/* RX control word 1 fields/flags */
#define AR5K_DESC_RX_CTL1_BUF_LEN 0x00000fff
#define AR5K_DESC_RX_CTL1_INTREQ 0x00002000
/*
* common hardware RX status descriptor
* 5210/11 and 5212 differ only in the flags defined below
*/
struct ath5k_hw_rx_status {
u32 rx_status_0; /* RX status word 0 */
u32 rx_status_1; /* RX status word 1 */
} __packed;
/* 5210/5211 */
/* RX status word 0 fields/flags */
#define AR5K_5210_RX_DESC_STATUS0_DATA_LEN 0x00000fff
#define AR5K_5210_RX_DESC_STATUS0_MORE 0x00001000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE 0x00078000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_RATE_S 15
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x07f80000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 19
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA 0x38000000
#define AR5K_5210_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 27
/* RX status word 1 fields/flags */
#define AR5K_5210_RX_DESC_STATUS1_DONE 0x00000001
#define AR5K_5210_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002
#define AR5K_5210_RX_DESC_STATUS1_CRC_ERROR 0x00000004
#define AR5K_5210_RX_DESC_STATUS1_FIFO_OVERRUN 0x00000008
#define AR5K_5210_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010
#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR 0x000000e0
#define AR5K_5210_RX_DESC_STATUS1_PHY_ERROR_S 5
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX 0x00007e00
#define AR5K_5210_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x0fff8000
#define AR5K_5210_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 15
#define AR5K_5210_RX_DESC_STATUS1_KEY_CACHE_MISS 0x10000000
/* 5212 */
/* RX status word 0 fields/flags */
#define AR5K_5212_RX_DESC_STATUS0_DATA_LEN 0x00000fff
#define AR5K_5212_RX_DESC_STATUS0_MORE 0x00001000
#define AR5K_5212_RX_DESC_STATUS0_DECOMP_CRC_ERROR 0x00002000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE 0x000f8000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_RATE_S 15
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x0ff00000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 20
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA 0xf0000000
#define AR5K_5212_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 28
/* RX status word 1 fields/flags */
#define AR5K_5212_RX_DESC_STATUS1_DONE 0x00000001
#define AR5K_5212_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002
#define AR5K_5212_RX_DESC_STATUS1_CRC_ERROR 0x00000004
#define AR5K_5212_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000008
#define AR5K_5212_RX_DESC_STATUS1_PHY_ERROR 0x00000010
#define AR5K_5212_RX_DESC_STATUS1_MIC_ERROR 0x00000020
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX 0x0000fe00
#define AR5K_5212_RX_DESC_STATUS1_KEY_INDEX_S 9
#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x7fff0000
#define AR5K_5212_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 16
#define AR5K_5212_RX_DESC_STATUS1_KEY_CACHE_MISS 0x80000000
/*
* common hardware RX error descriptor
*/
struct ath5k_hw_rx_error {
u32 rx_error_0; /* RX status word 0 */
u32 rx_error_1; /* RX status word 1 */
} __packed;
/* RX error word 0 fields/flags */
#define AR5K_RX_DESC_ERROR0 0x00000000
/* RX error word 1 fields/flags */
#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE 0x0000ff00
#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE_S 8
/* PHY Error codes */
#define AR5K_DESC_RX_PHY_ERROR_NONE 0x00
#define AR5K_DESC_RX_PHY_ERROR_TIMING 0x20
#define AR5K_DESC_RX_PHY_ERROR_PARITY 0x40
#define AR5K_DESC_RX_PHY_ERROR_RATE 0x60
#define AR5K_DESC_RX_PHY_ERROR_LENGTH 0x80
#define AR5K_DESC_RX_PHY_ERROR_64QAM 0xa0
#define AR5K_DESC_RX_PHY_ERROR_SERVICE 0xc0
#define AR5K_DESC_RX_PHY_ERROR_TRANSMITOVR 0xe0
/*
* 5210/5211 hardware 2-word TX control descriptor
*/
struct ath5k_hw_2w_tx_ctl {
u32 tx_control_0; /* TX control word 0 */
u32 tx_control_1; /* TX control word 1 */
} __packed;
/* TX control word 0 fields/flags */
#define AR5K_2W_TX_DESC_CTL0_FRAME_LEN 0x00000fff
#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN 0x0003f000 /*[5210 ?]*/
#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_S 12
#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE 0x003c0000
#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE_S 18
#define AR5K_2W_TX_DESC_CTL0_RTSENA 0x00400000
#define AR5K_2W_TX_DESC_CTL0_CLRDMASK 0x01000000
#define AR5K_2W_TX_DESC_CTL0_LONG_PACKET 0x00800000 /*[5210]*/
#define AR5K_2W_TX_DESC_CTL0_VEOL 0x00800000 /*[5211]*/
#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE 0x1c000000 /*[5210]*/
#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_S 26
#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 0x02000000
#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211 0x1e000000
#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT \
(ah->ah_version == AR5K_AR5210 ? \
AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 : \
AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211)
#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25
#define AR5K_2W_TX_DESC_CTL0_INTREQ 0x20000000
#define AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000
/* TX control word 1 fields/flags */
#define AR5K_2W_TX_DESC_CTL1_BUF_LEN 0x00000fff
#define AR5K_2W_TX_DESC_CTL1_MORE 0x00001000
#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210 0x0007e000
#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211 0x000fe000
#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX \
(ah->ah_version == AR5K_AR5210 ? \
AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210 : \
AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211)
#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S 13
#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE 0x00700000 /*[5211]*/
#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_S 20
#define AR5K_2W_TX_DESC_CTL1_NOACK 0x00800000 /*[5211]*/
#define AR5K_2W_TX_DESC_CTL1_RTS_DURATION 0xfff80000 /*[5210 ?]*/
/* Frame types */
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NORMAL 0x00
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_ATIM 0x04
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PSPOLL 0x08
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY 0x0c
#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS 0x10
/*
* 5212 hardware 4-word TX control descriptor
*/
struct ath5k_hw_4w_tx_ctl {
u32 tx_control_0; /* TX control word 0 */
#define AR5K_4W_TX_DESC_CTL0_FRAME_LEN 0x00000fff
#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER 0x003f0000
#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER_S 16
#define AR5K_4W_TX_DESC_CTL0_RTSENA 0x00400000
#define AR5K_4W_TX_DESC_CTL0_VEOL 0x00800000
#define AR5K_4W_TX_DESC_CTL0_CLRDMASK 0x01000000
#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT 0x1e000000
#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25
#define AR5K_4W_TX_DESC_CTL0_INTREQ 0x20000000
#define AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000
#define AR5K_4W_TX_DESC_CTL0_CTSENA 0x80000000
u32 tx_control_1; /* TX control word 1 */
#define AR5K_4W_TX_DESC_CTL1_BUF_LEN 0x00000fff
#define AR5K_4W_TX_DESC_CTL1_MORE 0x00001000
#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX 0x000fe000
#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S 13
#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE 0x00f00000
#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE_S 20
#define AR5K_4W_TX_DESC_CTL1_NOACK 0x01000000
#define AR5K_4W_TX_DESC_CTL1_COMP_PROC 0x06000000
#define AR5K_4W_TX_DESC_CTL1_COMP_PROC_S 25
#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN 0x18000000
#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN_S 27
#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN 0x60000000
#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN_S 29
u32 tx_control_2; /* TX control word 2 */
#define AR5K_4W_TX_DESC_CTL2_RTS_DURATION 0x00007fff
#define AR5K_4W_TX_DESC_CTL2_DURATION_UPDATE_ENABLE 0x00008000
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0 0x000f0000
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0_S 16
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1 0x00f00000
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1_S 20
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2 0x0f000000
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2_S 24
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3 0xf0000000
#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3_S 28
u32 tx_control_3; /* TX control word 3 */
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE0 0x0000001f
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1 0x000003e0
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1_S 5
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2 0x00007c00
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2_S 10
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3 0x000f8000
#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3_S 15
#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE 0x01f00000
#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE_S 20
} __packed;
/*
* Common TX status descriptor
*/
struct ath5k_hw_tx_status {
u32 tx_status_0; /* TX status word 0 */
u32 tx_status_1; /* TX status word 1 */
} __packed;
/* TX status word 0 fields/flags */
#define AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK 0x00000001
#define AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES 0x00000002
#define AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN 0x00000004
#define AR5K_DESC_TX_STATUS0_FILTERED 0x00000008
/*???
#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT 0x000000f0
#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT_S 4
*/
#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT 0x000000f0
#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT_S 4
/*???
#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT 0x00000f00
#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT_S 8
*/
#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT 0x00000f00
#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT_S 8
#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT 0x0000f000
#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT_S 12
#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP 0xffff0000
#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP_S 16
/* TX status word 1 fields/flags */
#define AR5K_DESC_TX_STATUS1_DONE 0x00000001
#define AR5K_DESC_TX_STATUS1_SEQ_NUM 0x00001ffe
#define AR5K_DESC_TX_STATUS1_SEQ_NUM_S 1
#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH 0x001fe000
#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH_S 13
#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX 0x00600000
#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX_S 21
#define AR5K_DESC_TX_STATUS1_COMP_SUCCESS 0x00800000
#define AR5K_DESC_TX_STATUS1_XMIT_ANTENNA 0x01000000
/*
* 5210/5211 hardware TX descriptor
*/
struct ath5k_hw_5210_tx_desc {
struct ath5k_hw_2w_tx_ctl tx_ctl;
struct ath5k_hw_tx_status tx_stat;
} __packed;
/*
* 5212 hardware TX descriptor
*/
struct ath5k_hw_5212_tx_desc {
struct ath5k_hw_4w_tx_ctl tx_ctl;
struct ath5k_hw_tx_status tx_stat;
} __packed;
/*
* common hardware RX descriptor
*/
struct ath5k_hw_all_rx_desc {
struct ath5k_hw_rx_ctl rx_ctl;
union {
struct ath5k_hw_rx_status rx_stat;
struct ath5k_hw_rx_error rx_err;
} u;
} __packed;
/*
* Atheros hardware descriptor
* This is read and written to by the hardware
*/
struct ath5k_desc {
u32 ds_link; /* physical address of the next descriptor */
u32 ds_data; /* physical address of data buffer (skb) */
union {
struct ath5k_hw_5210_tx_desc ds_tx5210;
struct ath5k_hw_5212_tx_desc ds_tx5212;
struct ath5k_hw_all_rx_desc ds_rx;
} ud;
} __packed;
#define AR5K_RXDESC_INTREQ 0x0020
#define AR5K_TXDESC_CLRDMASK 0x0001
#define AR5K_TXDESC_NOACK 0x0002 /*[5211+]*/
#define AR5K_TXDESC_RTSENA 0x0004
#define AR5K_TXDESC_CTSENA 0x0008
#define AR5K_TXDESC_INTREQ 0x0010
#define AR5K_TXDESC_VEOL 0x0020 /*[5211+]*/

View File

@@ -0,0 +1,705 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
*
* Permission to use, copy, modify, and 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.
*
*/
/*************************************\
* DMA and interrupt masking functions *
\*************************************/
/*
* dma.c - DMA and interrupt masking functions
*
* Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and
* handle queue setup for 5210 chipset (rest are handled on qcu.c).
* Also we setup interrupt mask register (IMR) and read the various iterrupt
* status registers (ISR).
*
* TODO: Handle SISR on 5211+ and introduce a function to return the queue
* number that resulted the interrupt.
*/
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "base.h"
/*********\
* Receive *
\*********/
/**
* ath5k_hw_start_rx_dma - Start DMA receive
*
* @ah: The &struct ath5k_hw
*/
void ath5k_hw_start_rx_dma(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR);
ath5k_hw_reg_read(ah, AR5K_CR);
}
/**
* ath5k_hw_stop_rx_dma - Stop DMA receive
*
* @ah: The &struct ath5k_hw
*/
int ath5k_hw_stop_rx_dma(struct ath5k_hw *ah)
{
unsigned int i;
ATH5K_TRACE(ah->ah_sc);
ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR);
/*
* It may take some time to disable the DMA receive unit
*/
for (i = 1000; i > 0 &&
(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0;
i--)
udelay(10);
return i ? 0 : -EBUSY;
}
/**
* ath5k_hw_get_rxdp - Get RX Descriptor's address
*
* @ah: The &struct ath5k_hw
*
* XXX: Is RXDP read and clear ?
*/
u32 ath5k_hw_get_rxdp(struct ath5k_hw *ah)
{
return ath5k_hw_reg_read(ah, AR5K_RXDP);
}
/**
* ath5k_hw_set_rxdp - Set RX Descriptor's address
*
* @ah: The &struct ath5k_hw
* @phys_addr: RX descriptor address
*
* XXX: Should we check if rx is enabled before setting rxdp ?
*/
void ath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr)
{
ATH5K_TRACE(ah->ah_sc);
ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP);
}
/**********\
* Transmit *
\**********/
/**
* ath5k_hw_start_tx_dma - Start DMA transmit for a specific queue
*
* @ah: The &struct ath5k_hw
* @queue: The hw queue number
*
* Start DMA transmit for a specific queue and since 5210 doesn't have
* QCU/DCU, set up queue parameters for 5210 here based on queue type (one
* queue for normal data and one queue for beacons). For queue setup
* on newer chips check out qcu.c. Returns -EINVAL if queue number is out
* of range or if queue is already disabled.
*
* NOTE: Must be called after setting up tx control descriptor for that
* queue (see below).
*/
int ath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue)
{
u32 tx_queue;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/* Return if queue is declared inactive */
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
return -EIO;
if (ah->ah_version == AR5K_AR5210) {
tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
/*
* Set the queue by type on 5210
*/
switch (ah->ah_txq[queue].tqi_type) {
case AR5K_TX_QUEUE_DATA:
tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0;
break;
case AR5K_TX_QUEUE_BEACON:
tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE,
AR5K_BSR);
break;
case AR5K_TX_QUEUE_CAB:
tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1;
ath5k_hw_reg_write(ah, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V |
AR5K_BCR_BDMAE, AR5K_BSR);
break;
default:
return -EINVAL;
}
/* Start queue */
ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
ath5k_hw_reg_read(ah, AR5K_CR);
} else {
/* Return if queue is disabled */
if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue))
return -EIO;
/* Start queue */
AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXE, queue);
}
return 0;
}
/**
* ath5k_hw_stop_tx_dma - Stop DMA transmit on a specific queue
*
* @ah: The &struct ath5k_hw
* @queue: The hw queue number
*
* Stop DMA transmit on a specific hw queue and drain queue so we don't
* have any pending frames. Returns -EBUSY if we still have pending frames,
* -EINVAL if queue number is out of range.
*
*/
int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
{
unsigned int i = 40;
u32 tx_queue, pending;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/* Return if queue is declared inactive */
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
return -EIO;
if (ah->ah_version == AR5K_AR5210) {
tx_queue = ath5k_hw_reg_read(ah, AR5K_CR);
/*
* Set by queue type
*/
switch (ah->ah_txq[queue].tqi_type) {
case AR5K_TX_QUEUE_DATA:
tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0;
break;
case AR5K_TX_QUEUE_BEACON:
case AR5K_TX_QUEUE_CAB:
/* XXX Fix me... */
tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1;
ath5k_hw_reg_write(ah, 0, AR5K_BSR);
break;
default:
return -EINVAL;
}
/* Stop queue */
ath5k_hw_reg_write(ah, tx_queue, AR5K_CR);
ath5k_hw_reg_read(ah, AR5K_CR);
} else {
/*
* Schedule TX disable and wait until queue is empty
*/
AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue);
/*Check for pending frames*/
do {
pending = ath5k_hw_reg_read(ah,
AR5K_QUEUE_STATUS(queue)) &
AR5K_QCU_STS_FRMPENDCNT;
udelay(100);
} while (--i && pending);
/* For 2413+ order PCU to drop packets using
* QUIET mechanism */
if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) &&
pending){
/* Set periodicity and duration */
ath5k_hw_reg_write(ah,
AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)|
AR5K_REG_SM(10, AR5K_QUIET_CTL2_QT_DUR),
AR5K_QUIET_CTL2);
/* Enable quiet period for current TSF */
ath5k_hw_reg_write(ah,
AR5K_QUIET_CTL1_QT_EN |
AR5K_REG_SM(ath5k_hw_reg_read(ah,
AR5K_TSF_L32_5211) >> 10,
AR5K_QUIET_CTL1_NEXT_QT_TSF),
AR5K_QUIET_CTL1);
/* Force channel idle high */
AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
AR5K_DIAG_SW_CHANEL_IDLE_HIGH);
/* Wait a while and disable mechanism */
udelay(200);
AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1,
AR5K_QUIET_CTL1_QT_EN);
/* Re-check for pending frames */
i = 40;
do {
pending = ath5k_hw_reg_read(ah,
AR5K_QUEUE_STATUS(queue)) &
AR5K_QCU_STS_FRMPENDCNT;
udelay(100);
} while (--i && pending);
AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211,
AR5K_DIAG_SW_CHANEL_IDLE_HIGH);
}
/* Clear register */
ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD);
if (pending)
return -EBUSY;
}
/* TODO: Check for success on 5210 else return error */
return 0;
}
/**
* ath5k_hw_get_txdp - Get TX Descriptor's address for a specific queue
*
* @ah: The &struct ath5k_hw
* @queue: The hw queue number
*
* Get TX descriptor's address for a specific queue. For 5210 we ignore
* the queue number and use tx queue type since we only have 2 queues.
* We use TXDP0 for normal data queue and TXDP1 for beacon queue.
* For newer chips with QCU/DCU we just read the corresponding TXDP register.
*
* XXX: Is TXDP read and clear ?
*/
u32 ath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue)
{
u16 tx_reg;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/*
* Get the transmit queue descriptor pointer from the selected queue
*/
/*5210 doesn't have QCU*/
if (ah->ah_version == AR5K_AR5210) {
switch (ah->ah_txq[queue].tqi_type) {
case AR5K_TX_QUEUE_DATA:
tx_reg = AR5K_NOQCU_TXDP0;
break;
case AR5K_TX_QUEUE_BEACON:
case AR5K_TX_QUEUE_CAB:
tx_reg = AR5K_NOQCU_TXDP1;
break;
default:
return 0xffffffff;
}
} else {
tx_reg = AR5K_QUEUE_TXDP(queue);
}
return ath5k_hw_reg_read(ah, tx_reg);
}
/**
* ath5k_hw_set_txdp - Set TX Descriptor's address for a specific queue
*
* @ah: The &struct ath5k_hw
* @queue: The hw queue number
*
* Set TX descriptor's address for a specific queue. For 5210 we ignore
* the queue number and we use tx queue type since we only have 2 queues
* so as above we use TXDP0 for normal data queue and TXDP1 for beacon queue.
* For newer chips with QCU/DCU we just set the corresponding TXDP register.
* Returns -EINVAL if queue type is invalid for 5210 and -EIO if queue is still
* active.
*/
int ath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr)
{
u16 tx_reg;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/*
* Set the transmit queue descriptor pointer register by type
* on 5210
*/
if (ah->ah_version == AR5K_AR5210) {
switch (ah->ah_txq[queue].tqi_type) {
case AR5K_TX_QUEUE_DATA:
tx_reg = AR5K_NOQCU_TXDP0;
break;
case AR5K_TX_QUEUE_BEACON:
case AR5K_TX_QUEUE_CAB:
tx_reg = AR5K_NOQCU_TXDP1;
break;
default:
return -EINVAL;
}
} else {
/*
* Set the transmit queue descriptor pointer for
* the selected queue on QCU for 5211+
* (this won't work if the queue is still active)
*/
if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
return -EIO;
tx_reg = AR5K_QUEUE_TXDP(queue);
}
/* Set descriptor pointer */
ath5k_hw_reg_write(ah, phys_addr, tx_reg);
return 0;
}
/**
* ath5k_hw_update_tx_triglevel - Update tx trigger level
*
* @ah: The &struct ath5k_hw
* @increase: Flag to force increase of trigger level
*
* This function increases/decreases the tx trigger level for the tx fifo
* buffer (aka FIFO threshold) that is used to indicate when PCU flushes
* the buffer and transmits it's data. Lowering this results sending small
* frames more quickly but can lead to tx underruns, raising it a lot can
* result other problems (i think bmiss is related). Right now we start with
* the lowest possible (64Bytes) and if we get tx underrun we increase it using
* the increase flag. Returns -EIO if we have have reached maximum/minimum.
*
* XXX: Link this with tx DMA size ?
* XXX: Use it to save interrupts ?
* TODO: Needs testing, i think it's related to bmiss...
*/
int ath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase)
{
u32 trigger_level, imr;
int ret = -EIO;
ATH5K_TRACE(ah->ah_sc);
/*
* Disable interrupts by setting the mask
*/
imr = ath5k_hw_set_imr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL);
trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG),
AR5K_TXCFG_TXFULL);
if (!increase) {
if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES)
goto done;
} else
trigger_level +=
((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2);
/*
* Update trigger level on success
*/
if (ah->ah_version == AR5K_AR5210)
ath5k_hw_reg_write(ah, trigger_level, AR5K_TRIG_LVL);
else
AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG,
AR5K_TXCFG_TXFULL, trigger_level);
ret = 0;
done:
/*
* Restore interrupt mask
*/
ath5k_hw_set_imr(ah, imr);
return ret;
}
/*******************\
* Interrupt masking *
\*******************/
/**
* ath5k_hw_is_intr_pending - Check if we have pending interrupts
*
* @ah: The &struct ath5k_hw
*
* Check if we have pending interrupts to process. Returns 1 if we
* have pending interrupts and 0 if we haven't.
*/
bool ath5k_hw_is_intr_pending(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0;
}
/**
* ath5k_hw_get_isr - Get interrupt status
*
* @ah: The @struct ath5k_hw
* @interrupt_mask: Driver's interrupt mask used to filter out
* interrupts in sw.
*
* This function is used inside our interrupt handler to determine the reason
* for the interrupt by reading Primary Interrupt Status Register. Returns an
* abstract interrupt status mask which is mostly ISR with some uncommon bits
* being mapped on some standard non hw-specific positions
* (check out &ath5k_int).
*
* NOTE: We use read-and-clear register, so after this function is called ISR
* is zeroed.
*/
int ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask)
{
u32 data;
ATH5K_TRACE(ah->ah_sc);
/*
* Read interrupt status from the Interrupt Status register
* on 5210
*/
if (ah->ah_version == AR5K_AR5210) {
data = ath5k_hw_reg_read(ah, AR5K_ISR);
if (unlikely(data == AR5K_INT_NOCARD)) {
*interrupt_mask = data;
return -ENODEV;
}
} else {
/*
* Read interrupt status from Interrupt
* Status Register shadow copy (Read And Clear)
*
* Note: PISR/SISR Not available on 5210
*/
data = ath5k_hw_reg_read(ah, AR5K_RAC_PISR);
if (unlikely(data == AR5K_INT_NOCARD)) {
*interrupt_mask = data;
return -ENODEV;
}
}
/*
* Get abstract interrupt mask (driver-compatible)
*/
*interrupt_mask = (data & AR5K_INT_COMMON) & ah->ah_imr;
if (ah->ah_version != AR5K_AR5210) {
u32 sisr2 = ath5k_hw_reg_read(ah, AR5K_RAC_SISR2);
/*HIU = Host Interface Unit (PCI etc)*/
if (unlikely(data & (AR5K_ISR_HIUERR)))
*interrupt_mask |= AR5K_INT_FATAL;
/*Beacon Not Ready*/
if (unlikely(data & (AR5K_ISR_BNR)))
*interrupt_mask |= AR5K_INT_BNR;
if (unlikely(sisr2 & (AR5K_SISR2_SSERR |
AR5K_SISR2_DPERR |
AR5K_SISR2_MCABT)))
*interrupt_mask |= AR5K_INT_FATAL;
if (data & AR5K_ISR_TIM)
*interrupt_mask |= AR5K_INT_TIM;
if (data & AR5K_ISR_BCNMISC) {
if (sisr2 & AR5K_SISR2_TIM)
*interrupt_mask |= AR5K_INT_TIM;
if (sisr2 & AR5K_SISR2_DTIM)
*interrupt_mask |= AR5K_INT_DTIM;
if (sisr2 & AR5K_SISR2_DTIM_SYNC)
*interrupt_mask |= AR5K_INT_DTIM_SYNC;
if (sisr2 & AR5K_SISR2_BCN_TIMEOUT)
*interrupt_mask |= AR5K_INT_BCN_TIMEOUT;
if (sisr2 & AR5K_SISR2_CAB_TIMEOUT)
*interrupt_mask |= AR5K_INT_CAB_TIMEOUT;
}
if (data & AR5K_ISR_RXDOPPLER)
*interrupt_mask |= AR5K_INT_RX_DOPPLER;
if (data & AR5K_ISR_QCBRORN) {
*interrupt_mask |= AR5K_INT_QCBRORN;
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR3),
AR5K_SISR3_QCBRORN);
}
if (data & AR5K_ISR_QCBRURN) {
*interrupt_mask |= AR5K_INT_QCBRURN;
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR3),
AR5K_SISR3_QCBRURN);
}
if (data & AR5K_ISR_QTRIG) {
*interrupt_mask |= AR5K_INT_QTRIG;
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR4),
AR5K_SISR4_QTRIG);
}
if (data & AR5K_ISR_TXOK)
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR0),
AR5K_SISR0_QCU_TXOK);
if (data & AR5K_ISR_TXDESC)
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR0),
AR5K_SISR0_QCU_TXDESC);
if (data & AR5K_ISR_TXERR)
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR1),
AR5K_SISR1_QCU_TXERR);
if (data & AR5K_ISR_TXEOL)
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR1),
AR5K_SISR1_QCU_TXEOL);
if (data & AR5K_ISR_TXURN)
ah->ah_txq_isr |= AR5K_REG_MS(
ath5k_hw_reg_read(ah, AR5K_RAC_SISR2),
AR5K_SISR2_QCU_TXURN);
} else {
if (unlikely(data & (AR5K_ISR_SSERR | AR5K_ISR_MCABT
| AR5K_ISR_HIUERR | AR5K_ISR_DPERR)))
*interrupt_mask |= AR5K_INT_FATAL;
/*
* XXX: BMISS interrupts may occur after association.
* I found this on 5210 code but it needs testing. If this is
* true we should disable them before assoc and re-enable them
* after a successful assoc + some jiffies.
interrupt_mask &= ~AR5K_INT_BMISS;
*/
}
/*
* In case we didn't handle anything,
* print the register value.
*/
if (unlikely(*interrupt_mask == 0 && net_ratelimit()))
ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr);
return 0;
}
/**
* ath5k_hw_set_imr - Set interrupt mask
*
* @ah: The &struct ath5k_hw
* @new_mask: The new interrupt mask to be set
*
* Set the interrupt mask in hw to save interrupts. We do that by mapping
* ath5k_int bits to hw-specific bits to remove abstraction and writing
* Interrupt Mask Register.
*/
enum ath5k_int ath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask)
{
enum ath5k_int old_mask, int_mask;
old_mask = ah->ah_imr;
/*
* Disable card interrupts to prevent any race conditions
* (they will be re-enabled afterwards if AR5K_INT GLOBAL
* is set again on the new mask).
*/
if (old_mask & AR5K_INT_GLOBAL) {
ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER);
ath5k_hw_reg_read(ah, AR5K_IER);
}
/*
* Add additional, chipset-dependent interrupt mask flags
* and write them to the IMR (interrupt mask register).
*/
int_mask = new_mask & AR5K_INT_COMMON;
if (ah->ah_version != AR5K_AR5210) {
/* Preserve per queue TXURN interrupt mask */
u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2)
& AR5K_SIMR2_QCU_TXURN;
if (new_mask & AR5K_INT_FATAL) {
int_mask |= AR5K_IMR_HIUERR;
simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR
| AR5K_SIMR2_DPERR);
}
/*Beacon Not Ready*/
if (new_mask & AR5K_INT_BNR)
int_mask |= AR5K_INT_BNR;
if (new_mask & AR5K_INT_TIM)
int_mask |= AR5K_IMR_TIM;
if (new_mask & AR5K_INT_TIM)
simr2 |= AR5K_SISR2_TIM;
if (new_mask & AR5K_INT_DTIM)
simr2 |= AR5K_SISR2_DTIM;
if (new_mask & AR5K_INT_DTIM_SYNC)
simr2 |= AR5K_SISR2_DTIM_SYNC;
if (new_mask & AR5K_INT_BCN_TIMEOUT)
simr2 |= AR5K_SISR2_BCN_TIMEOUT;
if (new_mask & AR5K_INT_CAB_TIMEOUT)
simr2 |= AR5K_SISR2_CAB_TIMEOUT;
if (new_mask & AR5K_INT_RX_DOPPLER)
int_mask |= AR5K_IMR_RXDOPPLER;
/* Note: Per queue interrupt masks
* are set via reset_tx_queue (qcu.c) */
ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR);
ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2);
} else {
if (new_mask & AR5K_INT_FATAL)
int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT
| AR5K_IMR_HIUERR | AR5K_IMR_DPERR);
ath5k_hw_reg_write(ah, int_mask, AR5K_IMR);
}
/* If RXNOFRM interrupt is masked disable it
* by setting AR5K_RXNOFRM to zero */
if (!(new_mask & AR5K_INT_RXNOFRM))
ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM);
/* Store new interrupt mask */
ah->ah_imr = new_mask;
/* ..re-enable interrupts if AR5K_INT_GLOBAL is set */
if (new_mask & AR5K_INT_GLOBAL) {
ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER);
ath5k_hw_reg_read(ah, AR5K_IER);
}
return old_mask;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,441 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
*
* Permission to use, copy, modify, and 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.
*
*/
/*
* Common ar5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE)
*/
#define AR5K_EEPROM_MAGIC 0x003d /* EEPROM Magic number */
#define AR5K_EEPROM_MAGIC_VALUE 0x5aa5 /* Default - found on EEPROM */
#define AR5K_EEPROM_MAGIC_5212 0x0000145c /* 5212 */
#define AR5K_EEPROM_MAGIC_5211 0x0000145b /* 5211 */
#define AR5K_EEPROM_MAGIC_5210 0x0000145a /* 5210 */
#define AR5K_EEPROM_IS_HB63 0x000b /* Talon detect */
#define AR5K_EEPROM_REG_DOMAIN 0x00bf /* EEPROM regdom */
#define AR5K_EEPROM_CHECKSUM 0x00c0 /* EEPROM checksum */
#define AR5K_EEPROM_INFO_BASE 0x00c0 /* EEPROM header */
#define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE)
#define AR5K_EEPROM_INFO_CKSUM 0xffff
#define AR5K_EEPROM_INFO(_n) (AR5K_EEPROM_INFO_BASE + (_n))
#define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1) /* EEPROM Version */
#define AR5K_EEPROM_VERSION_3_0 0x3000 /* No idea what's going on before this version */
#define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2Ghz (ar5211_rfregs) */
#define AR5K_EEPROM_VERSION_3_2 0x3002 /* different frequency representation (eeprom_bin2freq) */
#define AR5K_EEPROM_VERSION_3_3 0x3003 /* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */
#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain, ee_cck_ofdm_power_delta (eeprom_read_modes) */
#define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */
#define AR5K_EEPROM_VERSION_4_1 0x4001 /* has ee_margin_tx_rx (eeprom_init) */
#define AR5K_EEPROM_VERSION_4_2 0x4002 /* has ee_cck_ofdm_gain_delta (eeprom_init) */
#define AR5K_EEPROM_VERSION_4_3 0x4003 /* power calibration changes */
#define AR5K_EEPROM_VERSION_4_4 0x4004
#define AR5K_EEPROM_VERSION_4_5 0x4005
#define AR5K_EEPROM_VERSION_4_6 0x4006 /* has ee_scaled_cck_delta */
#define AR5K_EEPROM_VERSION_4_7 0x3007 /* 4007 ? */
#define AR5K_EEPROM_VERSION_4_9 0x4009 /* EAR futureproofing */
#define AR5K_EEPROM_VERSION_5_0 0x5000 /* Has 2413 PDADC calibration etc */
#define AR5K_EEPROM_VERSION_5_1 0x5001 /* Has capability values */
#define AR5K_EEPROM_VERSION_5_3 0x5003 /* Has spur mitigation tables */
#define AR5K_EEPROM_MODE_11A 0
#define AR5K_EEPROM_MODE_11B 1
#define AR5K_EEPROM_MODE_11G 2
#define AR5K_EEPROM_HDR AR5K_EEPROM_INFO(2) /* Header that contains the device caps */
#define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1)
#define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1)
#define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1)
#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2Ghz (?) */
#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for a/XR mode (eeprom_init) */
#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7)
#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */
#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz */
#define AR5K_EEPROM_RFKILL_GPIO_SEL 0x0000001c
#define AR5K_EEPROM_RFKILL_GPIO_SEL_S 2
#define AR5K_EEPROM_RFKILL_POLARITY 0x00000002
#define AR5K_EEPROM_RFKILL_POLARITY_S 1
/* Newer EEPROMs are using a different offset */
#define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \
(((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0)
#define AR5K_EEPROM_ANT_GAIN(_v) AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3)
#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((s8)(((_v) >> 8) & 0xff))
#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((s8)((_v) & 0xff))
/* Misc values available since EEPROM 4.0 */
#define AR5K_EEPROM_MISC0 AR5K_EEPROM_INFO(4)
#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff)
#define AR5K_EEPROM_HDR_XR2_DIS(_v) (((_v) >> 12) & 0x1)
#define AR5K_EEPROM_HDR_XR5_DIS(_v) (((_v) >> 13) & 0x1)
#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3)
#define AR5K_EEPROM_MISC1 AR5K_EEPROM_INFO(5)
#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff)
#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1)
#define AR5K_EEPROM_HAS32KHZCRYSTAL_OLD(_v) (((_v) >> 15) & 0x1)
#define AR5K_EEPROM_MISC2 AR5K_EEPROM_INFO(6)
#define AR5K_EEPROM_EEP_FILE_VERSION(_v) (((_v) >> 8) & 0xff)
#define AR5K_EEPROM_EAR_FILE_VERSION(_v) ((_v) & 0xff)
#define AR5K_EEPROM_MISC3 AR5K_EEPROM_INFO(7)
#define AR5K_EEPROM_ART_BUILD_NUM(_v) (((_v) >> 10) & 0x3f)
#define AR5K_EEPROM_EAR_FILE_ID(_v) ((_v) & 0xff)
#define AR5K_EEPROM_MISC4 AR5K_EEPROM_INFO(8)
#define AR5K_EEPROM_CAL_DATA_START(_v) (((_v) >> 4) & 0xfff)
#define AR5K_EEPROM_MASK_R0(_v) (((_v) >> 2) & 0x3)
#define AR5K_EEPROM_MASK_R1(_v) ((_v) & 0x3)
#define AR5K_EEPROM_MISC5 AR5K_EEPROM_INFO(9)
#define AR5K_EEPROM_COMP_DIS(_v) ((_v) & 0x1)
#define AR5K_EEPROM_AES_DIS(_v) (((_v) >> 1) & 0x1)
#define AR5K_EEPROM_FF_DIS(_v) (((_v) >> 2) & 0x1)
#define AR5K_EEPROM_BURST_DIS(_v) (((_v) >> 3) & 0x1)
#define AR5K_EEPROM_MAX_QCU(_v) (((_v) >> 4) & 0xf)
#define AR5K_EEPROM_HEAVY_CLIP_EN(_v) (((_v) >> 8) & 0x1)
#define AR5K_EEPROM_KEY_CACHE_SIZE(_v) (((_v) >> 12) & 0xf)
#define AR5K_EEPROM_MISC6 AR5K_EEPROM_INFO(10)
#define AR5K_EEPROM_TX_CHAIN_DIS ((_v) & 0x8)
#define AR5K_EEPROM_RX_CHAIN_DIS (((_v) >> 3) & 0x8)
#define AR5K_EEPROM_FCC_MID_EN (((_v) >> 6) & 0x1)
#define AR5K_EEPROM_JAP_U1EVEN_EN (((_v) >> 7) & 0x1)
#define AR5K_EEPROM_JAP_U2_EN (((_v) >> 8) & 0x1)
#define AR5K_EEPROM_JAP_U1ODD_EN (((_v) >> 9) & 0x1)
#define AR5K_EEPROM_JAP_11A_NEW_EN (((_v) >> 10) & 0x1)
/* calibration settings */
#define AR5K_EEPROM_MODES_11A(_v) AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4)
#define AR5K_EEPROM_MODES_11B(_v) AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2)
#define AR5K_EEPROM_MODES_11G(_v) AR5K_EEPROM_OFF(_v, 0x00da, 0x010d)
#define AR5K_EEPROM_CTL(_v) AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128) /* Conformance test limits */
#define AR5K_EEPROM_GROUPS_START(_v) AR5K_EEPROM_OFF(_v, 0x0100, 0x0150) /* Start of Groups */
#define AR5K_EEPROM_GROUP1_OFFSET 0x0
#define AR5K_EEPROM_GROUP2_OFFSET 0x5
#define AR5K_EEPROM_GROUP3_OFFSET 0x37
#define AR5K_EEPROM_GROUP4_OFFSET 0x46
#define AR5K_EEPROM_GROUP5_OFFSET 0x55
#define AR5K_EEPROM_GROUP6_OFFSET 0x65
#define AR5K_EEPROM_GROUP7_OFFSET 0x69
#define AR5K_EEPROM_GROUP8_OFFSET 0x6f
#define AR5K_EEPROM_TARGET_PWR_OFF_11A(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
AR5K_EEPROM_GROUP5_OFFSET, 0x0000)
#define AR5K_EEPROM_TARGET_PWR_OFF_11B(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
AR5K_EEPROM_GROUP6_OFFSET, 0x0010)
#define AR5K_EEPROM_TARGET_PWR_OFF_11G(_v) AR5K_EEPROM_OFF(_v, AR5K_EEPROM_GROUPS_START(_v) + \
AR5K_EEPROM_GROUP7_OFFSET, 0x0014)
/* [3.1 - 3.3] */
#define AR5K_EEPROM_OBDB0_2GHZ 0x00ec
#define AR5K_EEPROM_OBDB1_2GHZ 0x00ed
#define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */
#define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */
#define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */
#define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */
#define AR5K_EEPROM_PROTECT_WR_32_63 0x0008
#define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */
#define AR5K_EEPROM_PROTECT_WR_64_127 0x0020
#define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */
#define AR5K_EEPROM_PROTECT_WR_128_191 0x0080
#define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */
#define AR5K_EEPROM_PROTECT_WR_192_207 0x0200
#define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */
#define AR5K_EEPROM_PROTECT_WR_208_223 0x0800
#define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */
#define AR5K_EEPROM_PROTECT_WR_224_239 0x2000
#define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */
#define AR5K_EEPROM_PROTECT_WR_240_255 0x8000
/* Some EEPROM defines */
#define AR5K_EEPROM_EEP_SCALE 100
#define AR5K_EEPROM_EEP_DELTA 10
#define AR5K_EEPROM_N_MODES 3
#define AR5K_EEPROM_N_5GHZ_CHAN 10
#define AR5K_EEPROM_N_2GHZ_CHAN 3
#define AR5K_EEPROM_N_2GHZ_CHAN_2413 4
#define AR5K_EEPROM_N_2GHZ_CHAN_MAX 4
#define AR5K_EEPROM_MAX_CHAN 10
#define AR5K_EEPROM_N_PWR_POINTS_5111 11
#define AR5K_EEPROM_N_PCDAC 11
#define AR5K_EEPROM_N_PHASE_CAL 5
#define AR5K_EEPROM_N_TEST_FREQ 8
#define AR5K_EEPROM_N_EDGES 8
#define AR5K_EEPROM_N_INTERCEPTS 11
#define AR5K_EEPROM_FREQ_M(_v) AR5K_EEPROM_OFF(_v, 0x7f, 0xff)
#define AR5K_EEPROM_PCDAC_M 0x3f
#define AR5K_EEPROM_PCDAC_START 1
#define AR5K_EEPROM_PCDAC_STOP 63
#define AR5K_EEPROM_PCDAC_STEP 1
#define AR5K_EEPROM_NON_EDGE_M 0x40
#define AR5K_EEPROM_CHANNEL_POWER 8
#define AR5K_EEPROM_N_OBDB 4
#define AR5K_EEPROM_OBDB_DIS 0xffff
#define AR5K_EEPROM_CHANNEL_DIS 0xff
#define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10)
#define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32)
#define AR5K_EEPROM_MAX_CTLS 32
#define AR5K_EEPROM_N_PD_CURVES 4
#define AR5K_EEPROM_N_XPD0_POINTS 4
#define AR5K_EEPROM_N_XPD3_POINTS 3
#define AR5K_EEPROM_N_PD_GAINS 4
#define AR5K_EEPROM_N_PD_POINTS 5
#define AR5K_EEPROM_N_INTERCEPT_10_2GHZ 35
#define AR5K_EEPROM_N_INTERCEPT_10_5GHZ 55
#define AR5K_EEPROM_POWER_M 0x3f
#define AR5K_EEPROM_POWER_MIN 0
#define AR5K_EEPROM_POWER_MAX 3150
#define AR5K_EEPROM_POWER_STEP 50
#define AR5K_EEPROM_POWER_TABLE_SIZE 64
#define AR5K_EEPROM_N_POWER_LOC_11B 4
#define AR5K_EEPROM_N_POWER_LOC_11G 6
#define AR5K_EEPROM_I_GAIN 10
#define AR5K_EEPROM_CCK_OFDM_DELTA 15
#define AR5K_EEPROM_N_IQ_CAL 2
#define AR5K_EEPROM_READ(_o, _v) do { \
ret = ath5k_hw_eeprom_read(ah, (_o), &(_v)); \
if (ret) \
return ret; \
} while (0)
#define AR5K_EEPROM_READ_HDR(_o, _v) \
AR5K_EEPROM_READ(_o, ah->ah_capabilities.cap_eeprom._v); \
enum ath5k_ant_setting {
AR5K_ANT_VARIABLE = 0, /* variable by programming */
AR5K_ANT_FIXED_A = 1, /* fixed to 11a frequencies */
AR5K_ANT_FIXED_B = 2, /* fixed to 11b frequencies */
AR5K_ANT_MAX = 3,
};
enum ath5k_ctl_mode {
AR5K_CTL_11A = 0,
AR5K_CTL_11B = 1,
AR5K_CTL_11G = 2,
AR5K_CTL_TURBO = 3,
AR5K_CTL_TURBOG = 4,
AR5K_CTL_2GHT20 = 5,
AR5K_CTL_5GHT20 = 6,
AR5K_CTL_2GHT40 = 7,
AR5K_CTL_5GHT40 = 8,
AR5K_CTL_MODE_M = 15,
};
/* Default CTL ids for the 3 main reg domains.
* Atheros only uses these by default but vendors
* can have up to 32 different CTLs for different
* scenarios. Note that theese values are ORed with
* the mode id (above) so we can have up to 24 CTL
* datasets out of these 3 main regdomains. That leaves
* 8 ids that can be used by vendors and since 0x20 is
* missing from HAL sources i guess this is the set of
* custom CTLs vendors can use. */
#define AR5K_CTL_FCC 0x10
#define AR5K_CTL_CUSTOM 0x20
#define AR5K_CTL_ETSI 0x30
#define AR5K_CTL_MKK 0x40
/* Indicates a CTL with only mode set and
* no reg domain mapping, such CTLs are used
* for world roaming domains or simply when
* a reg domain is not set */
#define AR5K_CTL_NO_REGDOMAIN 0xf0
/* Indicates an empty (invalid) CTL */
#define AR5K_CTL_NO_CTL 0xff
/* Per channel calibration data, used for power table setup */
struct ath5k_chan_pcal_info_rf5111 {
/* Power levels in half dbm units
* for one power curve. */
u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
/* PCDAC table steps
* for the above values */
u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
/* Starting PCDAC step */
u8 pcdac_min;
/* Final PCDAC step */
u8 pcdac_max;
};
struct ath5k_chan_pcal_info_rf5112 {
/* Power levels in quarter dBm units
* for lower (0) and higher (3)
* level curves in 0.25dB units */
s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
/* PCDAC table steps
* for the above values */
u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
};
struct ath5k_chan_pcal_info_rf2413 {
/* Starting pwr/pddac values */
s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
/* (pwr,pddac) points
* power levels in 0.5dB units */
s8 pwr[AR5K_EEPROM_N_PD_GAINS]
[AR5K_EEPROM_N_PD_POINTS];
u8 pddac[AR5K_EEPROM_N_PD_GAINS]
[AR5K_EEPROM_N_PD_POINTS];
};
enum ath5k_powertable_type {
AR5K_PWRTABLE_PWR_TO_PCDAC = 0,
AR5K_PWRTABLE_LINEAR_PCDAC = 1,
AR5K_PWRTABLE_PWR_TO_PDADC = 2,
};
struct ath5k_pdgain_info {
u8 pd_points;
u8 *pd_step;
/* Power values are in
* 0.25dB units */
s16 *pd_pwr;
};
struct ath5k_chan_pcal_info {
/* Frequency */
u16 freq;
/* Tx power boundaries */
s16 max_pwr;
s16 min_pwr;
union {
struct ath5k_chan_pcal_info_rf5111 rf5111_info;
struct ath5k_chan_pcal_info_rf5112 rf5112_info;
struct ath5k_chan_pcal_info_rf2413 rf2413_info;
};
/* Raw values used by phy code
* Curves are stored in order from lower
* gain to higher gain (max txpower -> min txpower) */
struct ath5k_pdgain_info *pd_curves;
};
/* Per rate calibration data for each mode,
* used for rate power table setup.
* Note: Values in 0.5dB units */
struct ath5k_rate_pcal_info {
u16 freq; /* Frequency */
/* Power level for 6-24Mbit/s rates or
* 1Mb rate */
u16 target_power_6to24;
/* Power level for 36Mbit rate or
* 2Mb rate */
u16 target_power_36;
/* Power level for 48Mbit rate or
* 5.5Mbit rate */
u16 target_power_48;
/* Power level for 54Mbit rate or
* 11Mbit rate */
u16 target_power_54;
};
/* Power edges for conformance test limits */
struct ath5k_edge_power {
u16 freq;
u16 edge; /* in half dBm */
bool flag;
};
/* EEPROM calibration data */
struct ath5k_eeprom_info {
/* Header information */
u16 ee_magic;
u16 ee_protect;
u16 ee_regdomain;
u16 ee_version;
u16 ee_header;
u16 ee_ant_gain;
u16 ee_misc0;
u16 ee_misc1;
u16 ee_misc2;
u16 ee_misc3;
u16 ee_misc4;
u16 ee_misc5;
u16 ee_misc6;
u16 ee_cck_ofdm_gain_delta;
u16 ee_cck_ofdm_power_delta;
u16 ee_scaled_cck_delta;
/* RF Calibration settings (reset, rfregs) */
u16 ee_i_cal[AR5K_EEPROM_N_MODES];
u16 ee_q_cal[AR5K_EEPROM_N_MODES];
u16 ee_fixed_bias[AR5K_EEPROM_N_MODES];
u16 ee_turbo_max_power[AR5K_EEPROM_N_MODES];
u16 ee_xr_power[AR5K_EEPROM_N_MODES];
u16 ee_switch_settling[AR5K_EEPROM_N_MODES];
u16 ee_atn_tx_rx[AR5K_EEPROM_N_MODES];
u16 ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC];
u16 ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
u16 ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB];
u16 ee_tx_end2xlna_enable[AR5K_EEPROM_N_MODES];
u16 ee_tx_end2xpa_disable[AR5K_EEPROM_N_MODES];
u16 ee_tx_frm2xpa_enable[AR5K_EEPROM_N_MODES];
u16 ee_thr_62[AR5K_EEPROM_N_MODES];
u16 ee_xlna_gain[AR5K_EEPROM_N_MODES];
u16 ee_xpd[AR5K_EEPROM_N_MODES];
u16 ee_x_gain[AR5K_EEPROM_N_MODES];
u16 ee_i_gain[AR5K_EEPROM_N_MODES];
u16 ee_margin_tx_rx[AR5K_EEPROM_N_MODES];
u16 ee_switch_settling_turbo[AR5K_EEPROM_N_MODES];
u16 ee_margin_tx_rx_turbo[AR5K_EEPROM_N_MODES];
u16 ee_atn_tx_rx_turbo[AR5K_EEPROM_N_MODES];
/* Power calibration data */
u16 ee_false_detect[AR5K_EEPROM_N_MODES];
/* Number of pd gain curves per mode */
u8 ee_pd_gains[AR5K_EEPROM_N_MODES];
/* Back mapping pdcurve number -> pdcurve index in pd->pd_curves */
u8 ee_pdc_to_idx[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PD_GAINS];
u8 ee_n_piers[AR5K_EEPROM_N_MODES];
struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN];
struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
/* Per rate target power levels */
u8 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN];
struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
/* Conformance test limits (Unused) */
u8 ee_ctls;
u8 ee_ctl[AR5K_EEPROM_MAX_CTLS];
struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS];
/* Noise Floor Calibration settings */
s16 ee_noise_floor_thr[AR5K_EEPROM_N_MODES];
s8 ee_adc_desired_size[AR5K_EEPROM_N_MODES];
s8 ee_pga_desired_size[AR5K_EEPROM_N_MODES];
s8 ee_adc_desired_size_turbo[AR5K_EEPROM_N_MODES];
s8 ee_pga_desired_size_turbo[AR5K_EEPROM_N_MODES];
s8 ee_pd_gain_overlap;
u32 ee_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX];
};

View File

@@ -0,0 +1,176 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
*
* Permission to use, copy, modify, and 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.
*
*/
/****************\
GPIO Functions
\****************/
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "base.h"
/*
* Set led state
*/
void ath5k_hw_set_ledstate(struct ath5k_hw *ah, unsigned int state)
{
u32 led;
/*5210 has different led mode handling*/
u32 led_5210;
ATH5K_TRACE(ah->ah_sc);
/*Reset led status*/
if (ah->ah_version != AR5K_AR5210)
AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG,
AR5K_PCICFG_LEDMODE | AR5K_PCICFG_LED);
else
AR5K_REG_DISABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_LED);
/*
* Some blinking values, define at your wish
*/
switch (state) {
case AR5K_LED_SCAN:
case AR5K_LED_AUTH:
led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_PEND;
led_5210 = AR5K_PCICFG_LED_PEND | AR5K_PCICFG_LED_BCTL;
break;
case AR5K_LED_INIT:
led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_NONE;
led_5210 = AR5K_PCICFG_LED_PEND;
break;
case AR5K_LED_ASSOC:
case AR5K_LED_RUN:
led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_ASSOC;
led_5210 = AR5K_PCICFG_LED_ASSOC;
break;
default:
led = AR5K_PCICFG_LEDMODE_PROM | AR5K_PCICFG_LED_NONE;
led_5210 = AR5K_PCICFG_LED_PEND;
break;
}
/*Write new status to the register*/
if (ah->ah_version != AR5K_AR5210)
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led);
else
AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, led_5210);
}
/*
* Set GPIO inputs
*/
int ath5k_hw_set_gpio_input(struct ath5k_hw *ah, u32 gpio)
{
ATH5K_TRACE(ah->ah_sc);
if (gpio >= AR5K_NUM_GPIO)
return -EINVAL;
ath5k_hw_reg_write(ah,
(ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio))
| AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR);
return 0;
}
/*
* Set GPIO outputs
*/
int ath5k_hw_set_gpio_output(struct ath5k_hw *ah, u32 gpio)
{
ATH5K_TRACE(ah->ah_sc);
if (gpio >= AR5K_NUM_GPIO)
return -EINVAL;
ath5k_hw_reg_write(ah,
(ath5k_hw_reg_read(ah, AR5K_GPIOCR) & ~AR5K_GPIOCR_OUT(gpio))
| AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR);
return 0;
}
/*
* Get GPIO state
*/
u32 ath5k_hw_get_gpio(struct ath5k_hw *ah, u32 gpio)
{
ATH5K_TRACE(ah->ah_sc);
if (gpio >= AR5K_NUM_GPIO)
return 0xffffffff;
/* GPIO input magic */
return ((ath5k_hw_reg_read(ah, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) &
0x1;
}
/*
* Set GPIO state
*/
int ath5k_hw_set_gpio(struct ath5k_hw *ah, u32 gpio, u32 val)
{
u32 data;
ATH5K_TRACE(ah->ah_sc);
if (gpio >= AR5K_NUM_GPIO)
return -EINVAL;
/* GPIO output magic */
data = ath5k_hw_reg_read(ah, AR5K_GPIODO);
data &= ~(1 << gpio);
data |= (val & 1) << gpio;
ath5k_hw_reg_write(ah, data, AR5K_GPIODO);
return 0;
}
/*
* Initialize the GPIO interrupt (RFKill switch)
*/
void ath5k_hw_set_gpio_intr(struct ath5k_hw *ah, unsigned int gpio,
u32 interrupt_level)
{
u32 data;
ATH5K_TRACE(ah->ah_sc);
if (gpio >= AR5K_NUM_GPIO)
return;
/*
* Set the GPIO interrupt
*/
data = (ath5k_hw_reg_read(ah, AR5K_GPIOCR) &
~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH |
AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) |
(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA);
ath5k_hw_reg_write(ah, interrupt_level ? data :
(data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR);
ah->ah_imr |= AR5K_IMR_GPIO;
/* Enable GPIO interrupts */
AR5K_REG_ENABLE_BITS(ah, AR5K_PIMR, AR5K_IMR_GPIO);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,176 @@
/*
* Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
* Copyright (c) 2004-2005 Atheros Communications, Inc.
* Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com>
* Copyright (c) 2009 Bob Copeland <me@bobcopeland.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
*/
#include <linux/pci.h>
#include "ath5k.h"
#include "base.h"
#define ATH_SDEVICE(subv,subd) \
.vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
.subvendor = (subv), .subdevice = (subd)
#define ATH_LED(pin,polarity) .driver_data = (((pin) << 8) | (polarity))
#define ATH_PIN(data) ((data) >> 8)
#define ATH_POLARITY(data) ((data) & 0xff)
/* Devices we match on for LED config info (typically laptops) */
static const struct pci_device_id ath5k_led_devices[] = {
/* IBM-specific AR5212 */
{ PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) },
/* AR5211 */
{ PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5211), ATH_LED(0, 0) },
/* HP Compaq nc6xx, nc4000, nx6000 */
{ ATH_SDEVICE(PCI_VENDOR_ID_COMPAQ, PCI_ANY_ID), ATH_LED(1, 1) },
/* Acer Aspire One A150 (maximlevitsky@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_FOXCONN, 0xe008), ATH_LED(3, 0) },
/* Acer Ferrari 5000 (russ.dill@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0422), ATH_LED(1, 1) },
/* E-machines E510 (tuliom@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_AMBIT, 0x0428), ATH_LED(3, 0) },
/* Acer Extensa 5620z (nekoreeve@gmail.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_QMI, 0x0105), ATH_LED(3, 0) },
{ }
};
void ath5k_led_enable(struct ath5k_softc *sc)
{
if (test_bit(ATH_STAT_LEDSOFT, sc->status)) {
ath5k_hw_set_gpio_output(sc->ah, sc->led_pin);
ath5k_led_off(sc);
}
}
void ath5k_led_on(struct ath5k_softc *sc)
{
if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
return;
ath5k_hw_set_gpio(sc->ah, sc->led_pin, sc->led_on);
}
void ath5k_led_off(struct ath5k_softc *sc)
{
if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
return;
ath5k_hw_set_gpio(sc->ah, sc->led_pin, !sc->led_on);
}
static void
ath5k_led_brightness_set(struct led_classdev *led_dev,
enum led_brightness brightness)
{
struct ath5k_led *led = container_of(led_dev, struct ath5k_led,
led_dev);
if (brightness == LED_OFF)
ath5k_led_off(led->sc);
else
ath5k_led_on(led->sc);
}
static int
ath5k_register_led(struct ath5k_softc *sc, struct ath5k_led *led,
const char *name, char *trigger)
{
int err;
led->sc = sc;
strncpy(led->name, name, sizeof(led->name));
led->led_dev.name = led->name;
led->led_dev.default_trigger = trigger;
led->led_dev.brightness_set = ath5k_led_brightness_set;
err = led_classdev_register(&sc->pdev->dev, &led->led_dev);
if (err) {
ATH5K_WARN(sc, "could not register LED %s\n", name);
led->sc = NULL;
}
return err;
}
static void
ath5k_unregister_led(struct ath5k_led *led)
{
if (!led->sc)
return;
led_classdev_unregister(&led->led_dev);
ath5k_led_off(led->sc);
led->sc = NULL;
}
void ath5k_unregister_leds(struct ath5k_softc *sc)
{
ath5k_unregister_led(&sc->rx_led);
ath5k_unregister_led(&sc->tx_led);
}
int ath5k_init_leds(struct ath5k_softc *sc)
{
int ret = 0;
struct ieee80211_hw *hw = sc->hw;
struct pci_dev *pdev = sc->pdev;
char name[ATH5K_LED_MAX_NAME_LEN + 1];
const struct pci_device_id *match;
match = pci_match_id(&ath5k_led_devices[0], pdev);
if (match) {
__set_bit(ATH_STAT_LEDSOFT, sc->status);
sc->led_pin = ATH_PIN(match->driver_data);
sc->led_on = ATH_POLARITY(match->driver_data);
}
if (!test_bit(ATH_STAT_LEDSOFT, sc->status))
goto out;
ath5k_led_enable(sc);
snprintf(name, sizeof(name), "ath5k-%s::rx", wiphy_name(hw->wiphy));
ret = ath5k_register_led(sc, &sc->rx_led, name,
ieee80211_get_rx_led_name(hw));
if (ret)
goto out;
snprintf(name, sizeof(name), "ath5k-%s::tx", wiphy_name(hw->wiphy));
ret = ath5k_register_led(sc, &sc->tx_led, name,
ieee80211_get_tx_led_name(hw));
out:
return ret;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,546 @@
/*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
*
* Permission to use, copy, modify, and 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.
*
*/
/********************************************\
Queue Control Unit, DFS Control Unit Functions
\********************************************/
#include "ath5k.h"
#include "reg.h"
#include "debug.h"
#include "base.h"
/*
* Get properties for a transmit queue
*/
int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
struct ath5k_txq_info *queue_info)
{
ATH5K_TRACE(ah->ah_sc);
memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info));
return 0;
}
/*
* Set properties for a transmit queue
*/
int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue,
const struct ath5k_txq_info *queue_info)
{
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
return -EIO;
memcpy(&ah->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info));
/*XXX: Is this supported on 5210 ?*/
if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA &&
((queue_info->tqi_subtype == AR5K_WME_AC_VI) ||
(queue_info->tqi_subtype == AR5K_WME_AC_VO))) ||
queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD)
ah->ah_txq[queue].tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
return 0;
}
/*
* Initialize a transmit queue
*/
int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
struct ath5k_txq_info *queue_info)
{
unsigned int queue;
int ret;
ATH5K_TRACE(ah->ah_sc);
/*
* Get queue by type
*/
/*5210 only has 2 queues*/
if (ah->ah_version == AR5K_AR5210) {
switch (queue_type) {
case AR5K_TX_QUEUE_DATA:
queue = AR5K_TX_QUEUE_ID_NOQCU_DATA;
break;
case AR5K_TX_QUEUE_BEACON:
case AR5K_TX_QUEUE_CAB:
queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON;
break;
default:
return -EINVAL;
}
} else {
switch (queue_type) {
case AR5K_TX_QUEUE_DATA:
for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
ah->ah_txq[queue].tqi_type !=
AR5K_TX_QUEUE_INACTIVE; queue++) {
if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
return -EINVAL;
}
break;
case AR5K_TX_QUEUE_UAPSD:
queue = AR5K_TX_QUEUE_ID_UAPSD;
break;
case AR5K_TX_QUEUE_BEACON:
queue = AR5K_TX_QUEUE_ID_BEACON;
break;
case AR5K_TX_QUEUE_CAB:
queue = AR5K_TX_QUEUE_ID_CAB;
break;
case AR5K_TX_QUEUE_XR_DATA:
if (ah->ah_version != AR5K_AR5212)
ATH5K_ERR(ah->ah_sc,
"XR data queues only supported in"
" 5212!\n");
queue = AR5K_TX_QUEUE_ID_XR_DATA;
break;
default:
return -EINVAL;
}
}
/*
* Setup internal queue structure
*/
memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info));
ah->ah_txq[queue].tqi_type = queue_type;
if (queue_info != NULL) {
queue_info->tqi_type = queue_type;
ret = ath5k_hw_set_tx_queueprops(ah, queue, queue_info);
if (ret)
return ret;
}
/*
* We use ah_txq_status to hold a temp value for
* the Secondary interrupt mask registers on 5211+
* check out ath5k_hw_reset_tx_queue
*/
AR5K_Q_ENABLE_BITS(ah->ah_txq_status, queue);
return queue;
}
/*
* Get number of pending frames
* for a specific queue [5211+]
*/
u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue)
{
u32 pending;
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
/* Return if queue is declared inactive */
if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
return false;
/* XXX: How about AR5K_CFG_TXCNT ? */
if (ah->ah_version == AR5K_AR5210)
return false;
pending = (AR5K_QUEUE_STATUS(queue) & AR5K_QCU_STS_FRMPENDCNT);
/* It's possible to have no frames pending even if TXE
* is set. To indicate that q has not stopped return
* true */
if (!pending && AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
return true;
return pending;
}
/*
* Set a transmit queue inactive
*/
void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue)
{
ATH5K_TRACE(ah->ah_sc);
if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num))
return;
/* This queue will be skipped in further operations */
ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE;
/*For SIMR setup*/
AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue);
}
/*
* Set DFS properties for a transmit queue on DCU
*/
int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
{
u32 cw_min, cw_max, retry_lg, retry_sh;
struct ath5k_txq_info *tq = &ah->ah_txq[queue];
ATH5K_TRACE(ah->ah_sc);
AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
tq = &ah->ah_txq[queue];
if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE)
return 0;
if (ah->ah_version == AR5K_AR5210) {
/* Only handle data queues, others will be ignored */
if (tq->tqi_type != AR5K_TX_QUEUE_DATA)
return 0;
/* Set Slot time */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME,
AR5K_SLOT_TIME);
/* Set ACK_CTS timeout */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
AR5K_INIT_ACK_CTS_TIMEOUT_TURBO :
AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME);
/* Set Transmit Latency */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
AR5K_INIT_TRANSMIT_LATENCY_TURBO :
AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210);
/* Set IFS0 */
if (ah->ah_turbo) {
ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO +
(ah->ah_aifs + tq->tqi_aifs) *
AR5K_INIT_SLOT_TIME_TURBO) <<
AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO,
AR5K_IFS0);
} else {
ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS +
(ah->ah_aifs + tq->tqi_aifs) *
AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) |
AR5K_INIT_SIFS, AR5K_IFS0);
}
/* Set IFS1 */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
AR5K_INIT_PROTO_TIME_CNTRL_TURBO :
AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1);
/* Set AR5K_PHY_SETTLING */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
(ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F)
| 0x38 :
(ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F)
| 0x1C,
AR5K_PHY_SETTLING);
/* Set Frame Control Register */
ath5k_hw_reg_write(ah, ah->ah_turbo ?
(AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE |
AR5K_PHY_TURBO_SHORT | 0x2020) :
(AR5K_PHY_FRAME_CTL_INI | 0x1020),
AR5K_PHY_FRAME_CTL_5210);
}
/*
* Calculate cwmin/max by channel mode
*/
cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN;
cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX;
ah->ah_aifs = AR5K_TUNE_AIFS;
/*XR is only supported on 5212*/
if (IS_CHAN_XR(ah->ah_current_channel) &&
ah->ah_version == AR5K_AR5212) {
cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_XR;
cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_XR;
ah->ah_aifs = AR5K_TUNE_AIFS_XR;
/*B mode is not supported on 5210*/
} else if (IS_CHAN_B(ah->ah_current_channel) &&
ah->ah_version != AR5K_AR5210) {
cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_11B;
cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_11B;
ah->ah_aifs = AR5K_TUNE_AIFS_11B;
}
cw_min = 1;
while (cw_min < ah->ah_cw_min)
cw_min = (cw_min << 1) | 1;
cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) :
((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1);
cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) :
((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1);
/*
* Calculate and set retry limits
*/
if (ah->ah_software_retry) {
/* XXX Need to test this */
retry_lg = ah->ah_limit_tx_retries;
retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ?
AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg;
} else {
retry_lg = AR5K_INIT_LG_RETRY;
retry_sh = AR5K_INIT_SH_RETRY;
}
/*No QCU/DCU [5210]*/
if (ah->ah_version == AR5K_AR5210) {
ath5k_hw_reg_write(ah,
(cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
| AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
AR5K_NODCU_RETRY_LMT_SLG_RETRY)
| AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
AR5K_NODCU_RETRY_LMT_SSH_RETRY)
| AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY)
| AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY),
AR5K_NODCU_RETRY_LMT);
} else {
/*QCU/DCU [5211+]*/
ath5k_hw_reg_write(ah,
AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
AR5K_DCU_RETRY_LMT_SLG_RETRY) |
AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
AR5K_DCU_RETRY_LMT_SSH_RETRY) |
AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) |
AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY),
AR5K_QUEUE_DFS_RETRY_LIMIT(queue));
/*===Rest is also for QCU/DCU only [5211+]===*/
/*
* Set initial content window (cw_min/cw_max)
* and arbitrated interframe space (aifs)...
*/
ath5k_hw_reg_write(ah,
AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
AR5K_REG_SM(ah->ah_aifs + tq->tqi_aifs,
AR5K_DCU_LCL_IFS_AIFS),
AR5K_QUEUE_DFS_LOCAL_IFS(queue));
/*
* Set misc registers
*/
/* Enable DCU early termination for this queue */
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_DCU_EARLY);
/* Enable DCU to wait for next fragment from QCU */
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
AR5K_DCU_MISC_FRAG_WAIT);
/* On Maui and Spirit use the global seqnum on DCU */
if (ah->ah_mac_version < AR5K_SREV_AR5211)
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
AR5K_DCU_MISC_SEQNUM_CTL);
if (tq->tqi_cbr_period) {
ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period,
AR5K_QCU_CBRCFG_INTVAL) |
AR5K_REG_SM(tq->tqi_cbr_overflow_limit,
AR5K_QCU_CBRCFG_ORN_THRES),
AR5K_QUEUE_CBRCFG(queue));
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_FRSHED_CBR);
if (tq->tqi_cbr_overflow_limit)
AR5K_REG_ENABLE_BITS(ah,
AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_CBR_THRES_ENABLE);
}
if (tq->tqi_ready_time &&
(tq->tqi_type != AR5K_TX_QUEUE_ID_CAB))
ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time,
AR5K_QCU_RDYTIMECFG_INTVAL) |
AR5K_QCU_RDYTIMECFG_ENABLE,
AR5K_QUEUE_RDYTIMECFG(queue));
if (tq->tqi_burst_time) {
ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time,
AR5K_DCU_CHAN_TIME_DUR) |
AR5K_DCU_CHAN_TIME_ENABLE,
AR5K_QUEUE_DFS_CHANNEL_TIME(queue));
if (tq->tqi_flags
& AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)
AR5K_REG_ENABLE_BITS(ah,
AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_RDY_VEOL_POLICY);
}
if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE)
ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS,
AR5K_QUEUE_DFS_MISC(queue));
if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE)
ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG,
AR5K_QUEUE_DFS_MISC(queue));
/*
* Set registers by queue type
*/
switch (tq->tqi_type) {
case AR5K_TX_QUEUE_BEACON:
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_FRSHED_DBA_GT |
AR5K_QCU_MISC_CBREXP_BCN_DIS |
AR5K_QCU_MISC_BCN_ENABLE);
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
(AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
AR5K_DCU_MISC_ARBLOCK_CTL_S) |
AR5K_DCU_MISC_POST_FR_BKOFF_DIS |
AR5K_DCU_MISC_BCN_ENABLE);
break;
case AR5K_TX_QUEUE_CAB:
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_FRSHED_DBA_GT |
AR5K_QCU_MISC_CBREXP_DIS |
AR5K_QCU_MISC_CBREXP_BCN_DIS);
ath5k_hw_reg_write(ah, ((AR5K_TUNE_BEACON_INTERVAL -
(AR5K_TUNE_SW_BEACON_RESP -
AR5K_TUNE_DMA_BEACON_RESP) -
AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) |
AR5K_QCU_RDYTIMECFG_ENABLE,
AR5K_QUEUE_RDYTIMECFG(queue));
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
(AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
AR5K_DCU_MISC_ARBLOCK_CTL_S));
break;
case AR5K_TX_QUEUE_UAPSD:
AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
AR5K_QCU_MISC_CBREXP_DIS);
break;
case AR5K_TX_QUEUE_DATA:
default:
break;
}
/* TODO: Handle frame compression */
/*
* Enable interrupts for this tx queue
* in the secondary interrupt mask registers
*/
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue);
if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE)
AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue);
/* Update secondary interrupt mask registers */
/* Filter out inactive queues */
ah->ah_txq_imr_txok &= ah->ah_txq_status;
ah->ah_txq_imr_txerr &= ah->ah_txq_status;
ah->ah_txq_imr_txurn &= ah->ah_txq_status;
ah->ah_txq_imr_txdesc &= ah->ah_txq_status;
ah->ah_txq_imr_txeol &= ah->ah_txq_status;
ah->ah_txq_imr_cbrorn &= ah->ah_txq_status;
ah->ah_txq_imr_cbrurn &= ah->ah_txq_status;
ah->ah_txq_imr_qtrig &= ah->ah_txq_status;
ah->ah_txq_imr_nofrm &= ah->ah_txq_status;
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok,
AR5K_SIMR0_QCU_TXOK) |
AR5K_REG_SM(ah->ah_txq_imr_txdesc,
AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0);
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr,
AR5K_SIMR1_QCU_TXERR) |
AR5K_REG_SM(ah->ah_txq_imr_txeol,
AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1);
/* Update simr2 but don't overwrite rest simr2 settings */
AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN);
AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2,
AR5K_REG_SM(ah->ah_txq_imr_txurn,
AR5K_SIMR2_QCU_TXURN));
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn,
AR5K_SIMR3_QCBRORN) |
AR5K_REG_SM(ah->ah_txq_imr_cbrurn,
AR5K_SIMR3_QCBRURN), AR5K_SIMR3);
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig,
AR5K_SIMR4_QTRIG), AR5K_SIMR4);
/* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */
ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm,
AR5K_TXNOFRM_QCU), AR5K_TXNOFRM);
/* No queue has TXNOFRM enabled, disable the interrupt
* by setting AR5K_TXNOFRM to zero */
if (ah->ah_txq_imr_nofrm == 0)
ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM);
/* Set QCU mask for this DCU to save power */
AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(queue), queue);
}
return 0;
}
/*
* Get slot time from DCU
*/
unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
if (ah->ah_version == AR5K_AR5210)
return ath5k_hw_clocktoh(ath5k_hw_reg_read(ah,
AR5K_SLOT_TIME) & 0xffff, ah->ah_turbo);
else
return ath5k_hw_reg_read(ah, AR5K_DCU_GBL_IFS_SLOT) & 0xffff;
}
/*
* Set slot time on DCU
*/
int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time)
{
ATH5K_TRACE(ah->ah_sc);
if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX)
return -EINVAL;
if (ah->ah_version == AR5K_AR5210)
ath5k_hw_reg_write(ah, ath5k_hw_htoclock(slot_time,
ah->ah_turbo), AR5K_SLOT_TIME);
else
ath5k_hw_reg_write(ah, slot_time, AR5K_DCU_GBL_IFS_SLOT);
return 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,516 @@
/*
* RF Gain optimization
*
* Copyright (c) 2004-2009 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
*
* Permission to use, copy, modify, and 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.
*
*/
/*
* Mode-specific RF Gain table (64bytes) for RF5111/5112
* (RF5110 only comes with AR5210 and only supports a/turbo a mode so initial
* RF Gain values are included in AR5K_AR5210_INI)
*/
struct ath5k_ini_rfgain {
u16 rfg_register; /* RF Gain register address */
u32 rfg_value[2]; /* [freq (see below)] */
};
/* Initial RF Gain settings for RF5111 */
static const struct ath5k_ini_rfgain rfgain_5111[] = {
/* 5Ghz 2Ghz */
{ AR5K_RF_GAIN(0), { 0x000001a9, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x000001e9, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000029, 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x00000069, 0x00000150 } },
{ AR5K_RF_GAIN(4), { 0x00000199, 0x00000190 } },
{ AR5K_RF_GAIN(5), { 0x000001d9, 0x000001d0 } },
{ AR5K_RF_GAIN(6), { 0x00000019, 0x00000010 } },
{ AR5K_RF_GAIN(7), { 0x00000059, 0x00000044 } },
{ AR5K_RF_GAIN(8), { 0x00000099, 0x00000084 } },
{ AR5K_RF_GAIN(9), { 0x000001a5, 0x00000148 } },
{ AR5K_RF_GAIN(10), { 0x000001e5, 0x00000188 } },
{ AR5K_RF_GAIN(11), { 0x00000025, 0x000001c8 } },
{ AR5K_RF_GAIN(12), { 0x000001c8, 0x00000014 } },
{ AR5K_RF_GAIN(13), { 0x00000008, 0x00000042 } },
{ AR5K_RF_GAIN(14), { 0x00000048, 0x00000082 } },
{ AR5K_RF_GAIN(15), { 0x00000088, 0x00000178 } },
{ AR5K_RF_GAIN(16), { 0x00000198, 0x000001b8 } },
{ AR5K_RF_GAIN(17), { 0x000001d8, 0x000001f8 } },
{ AR5K_RF_GAIN(18), { 0x00000018, 0x00000012 } },
{ AR5K_RF_GAIN(19), { 0x00000058, 0x00000052 } },
{ AR5K_RF_GAIN(20), { 0x00000098, 0x00000092 } },
{ AR5K_RF_GAIN(21), { 0x000001a4, 0x0000017c } },
{ AR5K_RF_GAIN(22), { 0x000001e4, 0x000001bc } },
{ AR5K_RF_GAIN(23), { 0x00000024, 0x000001fc } },
{ AR5K_RF_GAIN(24), { 0x00000064, 0x0000000a } },
{ AR5K_RF_GAIN(25), { 0x000000a4, 0x0000004a } },
{ AR5K_RF_GAIN(26), { 0x000000e4, 0x0000008a } },
{ AR5K_RF_GAIN(27), { 0x0000010a, 0x0000015a } },
{ AR5K_RF_GAIN(28), { 0x0000014a, 0x0000019a } },
{ AR5K_RF_GAIN(29), { 0x0000018a, 0x000001da } },
{ AR5K_RF_GAIN(30), { 0x000001ca, 0x0000000e } },
{ AR5K_RF_GAIN(31), { 0x0000000a, 0x0000004e } },
{ AR5K_RF_GAIN(32), { 0x0000004a, 0x0000008e } },
{ AR5K_RF_GAIN(33), { 0x0000008a, 0x0000015e } },
{ AR5K_RF_GAIN(34), { 0x000001ba, 0x0000019e } },
{ AR5K_RF_GAIN(35), { 0x000001fa, 0x000001de } },
{ AR5K_RF_GAIN(36), { 0x0000003a, 0x00000009 } },
{ AR5K_RF_GAIN(37), { 0x0000007a, 0x00000049 } },
{ AR5K_RF_GAIN(38), { 0x00000186, 0x00000089 } },
{ AR5K_RF_GAIN(39), { 0x000001c6, 0x00000179 } },
{ AR5K_RF_GAIN(40), { 0x00000006, 0x000001b9 } },
{ AR5K_RF_GAIN(41), { 0x00000046, 0x000001f9 } },
{ AR5K_RF_GAIN(42), { 0x00000086, 0x00000039 } },
{ AR5K_RF_GAIN(43), { 0x000000c6, 0x00000079 } },
{ AR5K_RF_GAIN(44), { 0x000000c6, 0x000000b9 } },
{ AR5K_RF_GAIN(45), { 0x000000c6, 0x000001bd } },
{ AR5K_RF_GAIN(46), { 0x000000c6, 0x000001fd } },
{ AR5K_RF_GAIN(47), { 0x000000c6, 0x0000003d } },
{ AR5K_RF_GAIN(48), { 0x000000c6, 0x0000007d } },
{ AR5K_RF_GAIN(49), { 0x000000c6, 0x000000bd } },
{ AR5K_RF_GAIN(50), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(51), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(52), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(53), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(54), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(55), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(56), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(57), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(58), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(59), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(60), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(61), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(62), { 0x000000c6, 0x000000fd } },
{ AR5K_RF_GAIN(63), { 0x000000c6, 0x000000fd } },
};
/* Initial RF Gain settings for RF5112 */
static const struct ath5k_ini_rfgain rfgain_5112[] = {
/* 5Ghz 2Ghz */
{ AR5K_RF_GAIN(0), { 0x00000007, 0x00000007 } },
{ AR5K_RF_GAIN(1), { 0x00000047, 0x00000047 } },
{ AR5K_RF_GAIN(2), { 0x00000087, 0x00000087 } },
{ AR5K_RF_GAIN(3), { 0x000001a0, 0x000001a0 } },
{ AR5K_RF_GAIN(4), { 0x000001e0, 0x000001e0 } },
{ AR5K_RF_GAIN(5), { 0x00000020, 0x00000020 } },
{ AR5K_RF_GAIN(6), { 0x00000060, 0x00000060 } },
{ AR5K_RF_GAIN(7), { 0x000001a1, 0x000001a1 } },
{ AR5K_RF_GAIN(8), { 0x000001e1, 0x000001e1 } },
{ AR5K_RF_GAIN(9), { 0x00000021, 0x00000021 } },
{ AR5K_RF_GAIN(10), { 0x00000061, 0x00000061 } },
{ AR5K_RF_GAIN(11), { 0x00000162, 0x00000162 } },
{ AR5K_RF_GAIN(12), { 0x000001a2, 0x000001a2 } },
{ AR5K_RF_GAIN(13), { 0x000001e2, 0x000001e2 } },
{ AR5K_RF_GAIN(14), { 0x00000022, 0x00000022 } },
{ AR5K_RF_GAIN(15), { 0x00000062, 0x00000062 } },
{ AR5K_RF_GAIN(16), { 0x00000163, 0x00000163 } },
{ AR5K_RF_GAIN(17), { 0x000001a3, 0x000001a3 } },
{ AR5K_RF_GAIN(18), { 0x000001e3, 0x000001e3 } },
{ AR5K_RF_GAIN(19), { 0x00000023, 0x00000023 } },
{ AR5K_RF_GAIN(20), { 0x00000063, 0x00000063 } },
{ AR5K_RF_GAIN(21), { 0x00000184, 0x00000184 } },
{ AR5K_RF_GAIN(22), { 0x000001c4, 0x000001c4 } },
{ AR5K_RF_GAIN(23), { 0x00000004, 0x00000004 } },
{ AR5K_RF_GAIN(24), { 0x000001ea, 0x0000000b } },
{ AR5K_RF_GAIN(25), { 0x0000002a, 0x0000004b } },
{ AR5K_RF_GAIN(26), { 0x0000006a, 0x0000008b } },
{ AR5K_RF_GAIN(27), { 0x000000aa, 0x000001ac } },
{ AR5K_RF_GAIN(28), { 0x000001ab, 0x000001ec } },
{ AR5K_RF_GAIN(29), { 0x000001eb, 0x0000002c } },
{ AR5K_RF_GAIN(30), { 0x0000002b, 0x00000012 } },
{ AR5K_RF_GAIN(31), { 0x0000006b, 0x00000052 } },
{ AR5K_RF_GAIN(32), { 0x000000ab, 0x00000092 } },
{ AR5K_RF_GAIN(33), { 0x000001ac, 0x00000193 } },
{ AR5K_RF_GAIN(34), { 0x000001ec, 0x000001d3 } },
{ AR5K_RF_GAIN(35), { 0x0000002c, 0x00000013 } },
{ AR5K_RF_GAIN(36), { 0x0000003a, 0x00000053 } },
{ AR5K_RF_GAIN(37), { 0x0000007a, 0x00000093 } },
{ AR5K_RF_GAIN(38), { 0x000000ba, 0x00000194 } },
{ AR5K_RF_GAIN(39), { 0x000001bb, 0x000001d4 } },
{ AR5K_RF_GAIN(40), { 0x000001fb, 0x00000014 } },
{ AR5K_RF_GAIN(41), { 0x0000003b, 0x0000003a } },
{ AR5K_RF_GAIN(42), { 0x0000007b, 0x0000007a } },
{ AR5K_RF_GAIN(43), { 0x000000bb, 0x000000ba } },
{ AR5K_RF_GAIN(44), { 0x000001bc, 0x000001bb } },
{ AR5K_RF_GAIN(45), { 0x000001fc, 0x000001fb } },
{ AR5K_RF_GAIN(46), { 0x0000003c, 0x0000003b } },
{ AR5K_RF_GAIN(47), { 0x0000007c, 0x0000007b } },
{ AR5K_RF_GAIN(48), { 0x000000bc, 0x000000bb } },
{ AR5K_RF_GAIN(49), { 0x000000fc, 0x000001bc } },
{ AR5K_RF_GAIN(50), { 0x000000fc, 0x000001fc } },
{ AR5K_RF_GAIN(51), { 0x000000fc, 0x0000003c } },
{ AR5K_RF_GAIN(52), { 0x000000fc, 0x0000007c } },
{ AR5K_RF_GAIN(53), { 0x000000fc, 0x000000bc } },
{ AR5K_RF_GAIN(54), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(55), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(56), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(57), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(58), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(59), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(60), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(61), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(62), { 0x000000fc, 0x000000fc } },
{ AR5K_RF_GAIN(63), { 0x000000fc, 0x000000fc } },
};
/* Initial RF Gain settings for RF2413 */
static const struct ath5k_ini_rfgain rfgain_2413[] = {
{ AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x00000000, 0x00000181 } },
{ AR5K_RF_GAIN(4), { 0x00000000, 0x000001c1 } },
{ AR5K_RF_GAIN(5), { 0x00000000, 0x00000001 } },
{ AR5K_RF_GAIN(6), { 0x00000000, 0x00000041 } },
{ AR5K_RF_GAIN(7), { 0x00000000, 0x00000081 } },
{ AR5K_RF_GAIN(8), { 0x00000000, 0x00000168 } },
{ AR5K_RF_GAIN(9), { 0x00000000, 0x000001a8 } },
{ AR5K_RF_GAIN(10), { 0x00000000, 0x000001e8 } },
{ AR5K_RF_GAIN(11), { 0x00000000, 0x00000028 } },
{ AR5K_RF_GAIN(12), { 0x00000000, 0x00000068 } },
{ AR5K_RF_GAIN(13), { 0x00000000, 0x00000189 } },
{ AR5K_RF_GAIN(14), { 0x00000000, 0x000001c9 } },
{ AR5K_RF_GAIN(15), { 0x00000000, 0x00000009 } },
{ AR5K_RF_GAIN(16), { 0x00000000, 0x00000049 } },
{ AR5K_RF_GAIN(17), { 0x00000000, 0x00000089 } },
{ AR5K_RF_GAIN(18), { 0x00000000, 0x00000190 } },
{ AR5K_RF_GAIN(19), { 0x00000000, 0x000001d0 } },
{ AR5K_RF_GAIN(20), { 0x00000000, 0x00000010 } },
{ AR5K_RF_GAIN(21), { 0x00000000, 0x00000050 } },
{ AR5K_RF_GAIN(22), { 0x00000000, 0x00000090 } },
{ AR5K_RF_GAIN(23), { 0x00000000, 0x00000191 } },
{ AR5K_RF_GAIN(24), { 0x00000000, 0x000001d1 } },
{ AR5K_RF_GAIN(25), { 0x00000000, 0x00000011 } },
{ AR5K_RF_GAIN(26), { 0x00000000, 0x00000051 } },
{ AR5K_RF_GAIN(27), { 0x00000000, 0x00000091 } },
{ AR5K_RF_GAIN(28), { 0x00000000, 0x00000178 } },
{ AR5K_RF_GAIN(29), { 0x00000000, 0x000001b8 } },
{ AR5K_RF_GAIN(30), { 0x00000000, 0x000001f8 } },
{ AR5K_RF_GAIN(31), { 0x00000000, 0x00000038 } },
{ AR5K_RF_GAIN(32), { 0x00000000, 0x00000078 } },
{ AR5K_RF_GAIN(33), { 0x00000000, 0x00000199 } },
{ AR5K_RF_GAIN(34), { 0x00000000, 0x000001d9 } },
{ AR5K_RF_GAIN(35), { 0x00000000, 0x00000019 } },
{ AR5K_RF_GAIN(36), { 0x00000000, 0x00000059 } },
{ AR5K_RF_GAIN(37), { 0x00000000, 0x00000099 } },
{ AR5K_RF_GAIN(38), { 0x00000000, 0x000000d9 } },
{ AR5K_RF_GAIN(39), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(40), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(41), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(42), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(43), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(44), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(45), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(46), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(47), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(48), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(49), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(50), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(51), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(52), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(53), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(54), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(55), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(56), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(57), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(58), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(59), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(60), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(61), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(62), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(63), { 0x00000000, 0x000000f9 } },
};
/* Initial RF Gain settings for AR2316 */
static const struct ath5k_ini_rfgain rfgain_2316[] = {
{ AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x00000000, 0x000000c0 } },
{ AR5K_RF_GAIN(4), { 0x00000000, 0x000000e0 } },
{ AR5K_RF_GAIN(5), { 0x00000000, 0x000000e0 } },
{ AR5K_RF_GAIN(6), { 0x00000000, 0x00000128 } },
{ AR5K_RF_GAIN(7), { 0x00000000, 0x00000128 } },
{ AR5K_RF_GAIN(8), { 0x00000000, 0x00000128 } },
{ AR5K_RF_GAIN(9), { 0x00000000, 0x00000168 } },
{ AR5K_RF_GAIN(10), { 0x00000000, 0x000001a8 } },
{ AR5K_RF_GAIN(11), { 0x00000000, 0x000001e8 } },
{ AR5K_RF_GAIN(12), { 0x00000000, 0x00000028 } },
{ AR5K_RF_GAIN(13), { 0x00000000, 0x00000068 } },
{ AR5K_RF_GAIN(14), { 0x00000000, 0x000000a8 } },
{ AR5K_RF_GAIN(15), { 0x00000000, 0x000000e8 } },
{ AR5K_RF_GAIN(16), { 0x00000000, 0x000000e8 } },
{ AR5K_RF_GAIN(17), { 0x00000000, 0x00000130 } },
{ AR5K_RF_GAIN(18), { 0x00000000, 0x00000130 } },
{ AR5K_RF_GAIN(19), { 0x00000000, 0x00000170 } },
{ AR5K_RF_GAIN(20), { 0x00000000, 0x000001b0 } },
{ AR5K_RF_GAIN(21), { 0x00000000, 0x000001f0 } },
{ AR5K_RF_GAIN(22), { 0x00000000, 0x00000030 } },
{ AR5K_RF_GAIN(23), { 0x00000000, 0x00000070 } },
{ AR5K_RF_GAIN(24), { 0x00000000, 0x000000b0 } },
{ AR5K_RF_GAIN(25), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(26), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(27), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(28), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(29), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(30), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(31), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(32), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(33), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(34), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(35), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(36), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(37), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(38), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(39), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(40), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(41), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(42), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(43), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(44), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(45), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(46), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(47), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(48), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(49), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(50), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(51), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(52), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(53), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(54), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(55), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(56), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(57), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(58), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(59), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(60), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(61), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(62), { 0x00000000, 0x000000f0 } },
{ AR5K_RF_GAIN(63), { 0x00000000, 0x000000f0 } },
};
/* Initial RF Gain settings for RF5413 */
static const struct ath5k_ini_rfgain rfgain_5413[] = {
/* 5Ghz 2Ghz */
{ AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x00000040, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000080, 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x000001a1, 0x00000161 } },
{ AR5K_RF_GAIN(4), { 0x000001e1, 0x000001a1 } },
{ AR5K_RF_GAIN(5), { 0x00000021, 0x000001e1 } },
{ AR5K_RF_GAIN(6), { 0x00000061, 0x00000021 } },
{ AR5K_RF_GAIN(7), { 0x00000188, 0x00000061 } },
{ AR5K_RF_GAIN(8), { 0x000001c8, 0x00000188 } },
{ AR5K_RF_GAIN(9), { 0x00000008, 0x000001c8 } },
{ AR5K_RF_GAIN(10), { 0x00000048, 0x00000008 } },
{ AR5K_RF_GAIN(11), { 0x00000088, 0x00000048 } },
{ AR5K_RF_GAIN(12), { 0x000001a9, 0x00000088 } },
{ AR5K_RF_GAIN(13), { 0x000001e9, 0x00000169 } },
{ AR5K_RF_GAIN(14), { 0x00000029, 0x000001a9 } },
{ AR5K_RF_GAIN(15), { 0x00000069, 0x000001e9 } },
{ AR5K_RF_GAIN(16), { 0x000001d0, 0x00000029 } },
{ AR5K_RF_GAIN(17), { 0x00000010, 0x00000069 } },
{ AR5K_RF_GAIN(18), { 0x00000050, 0x00000190 } },
{ AR5K_RF_GAIN(19), { 0x00000090, 0x000001d0 } },
{ AR5K_RF_GAIN(20), { 0x000001b1, 0x00000010 } },
{ AR5K_RF_GAIN(21), { 0x000001f1, 0x00000050 } },
{ AR5K_RF_GAIN(22), { 0x00000031, 0x00000090 } },
{ AR5K_RF_GAIN(23), { 0x00000071, 0x00000171 } },
{ AR5K_RF_GAIN(24), { 0x000001b8, 0x000001b1 } },
{ AR5K_RF_GAIN(25), { 0x000001f8, 0x000001f1 } },
{ AR5K_RF_GAIN(26), { 0x00000038, 0x00000031 } },
{ AR5K_RF_GAIN(27), { 0x00000078, 0x00000071 } },
{ AR5K_RF_GAIN(28), { 0x00000199, 0x00000198 } },
{ AR5K_RF_GAIN(29), { 0x000001d9, 0x000001d8 } },
{ AR5K_RF_GAIN(30), { 0x00000019, 0x00000018 } },
{ AR5K_RF_GAIN(31), { 0x00000059, 0x00000058 } },
{ AR5K_RF_GAIN(32), { 0x00000099, 0x00000098 } },
{ AR5K_RF_GAIN(33), { 0x000000d9, 0x00000179 } },
{ AR5K_RF_GAIN(34), { 0x000000f9, 0x000001b9 } },
{ AR5K_RF_GAIN(35), { 0x000000f9, 0x000001f9 } },
{ AR5K_RF_GAIN(36), { 0x000000f9, 0x00000039 } },
{ AR5K_RF_GAIN(37), { 0x000000f9, 0x00000079 } },
{ AR5K_RF_GAIN(38), { 0x000000f9, 0x000000b9 } },
{ AR5K_RF_GAIN(39), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(40), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(41), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(42), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(43), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(44), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(45), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(46), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(47), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(48), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(49), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(50), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(51), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(52), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(53), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(54), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(55), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(56), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(57), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(58), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(59), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(60), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(61), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(62), { 0x000000f9, 0x000000f9 } },
{ AR5K_RF_GAIN(63), { 0x000000f9, 0x000000f9 } },
};
/* Initial RF Gain settings for RF2425 */
static const struct ath5k_ini_rfgain rfgain_2425[] = {
{ AR5K_RF_GAIN(0), { 0x00000000, 0x00000000 } },
{ AR5K_RF_GAIN(1), { 0x00000000, 0x00000040 } },
{ AR5K_RF_GAIN(2), { 0x00000000, 0x00000080 } },
{ AR5K_RF_GAIN(3), { 0x00000000, 0x00000181 } },
{ AR5K_RF_GAIN(4), { 0x00000000, 0x000001c1 } },
{ AR5K_RF_GAIN(5), { 0x00000000, 0x00000001 } },
{ AR5K_RF_GAIN(6), { 0x00000000, 0x00000041 } },
{ AR5K_RF_GAIN(7), { 0x00000000, 0x00000081 } },
{ AR5K_RF_GAIN(8), { 0x00000000, 0x00000188 } },
{ AR5K_RF_GAIN(9), { 0x00000000, 0x000001c8 } },
{ AR5K_RF_GAIN(10), { 0x00000000, 0x00000008 } },
{ AR5K_RF_GAIN(11), { 0x00000000, 0x00000048 } },
{ AR5K_RF_GAIN(12), { 0x00000000, 0x00000088 } },
{ AR5K_RF_GAIN(13), { 0x00000000, 0x00000189 } },
{ AR5K_RF_GAIN(14), { 0x00000000, 0x000001c9 } },
{ AR5K_RF_GAIN(15), { 0x00000000, 0x00000009 } },
{ AR5K_RF_GAIN(16), { 0x00000000, 0x00000049 } },
{ AR5K_RF_GAIN(17), { 0x00000000, 0x00000089 } },
{ AR5K_RF_GAIN(18), { 0x00000000, 0x000001b0 } },
{ AR5K_RF_GAIN(19), { 0x00000000, 0x000001f0 } },
{ AR5K_RF_GAIN(20), { 0x00000000, 0x00000030 } },
{ AR5K_RF_GAIN(21), { 0x00000000, 0x00000070 } },
{ AR5K_RF_GAIN(22), { 0x00000000, 0x00000171 } },
{ AR5K_RF_GAIN(23), { 0x00000000, 0x000001b1 } },
{ AR5K_RF_GAIN(24), { 0x00000000, 0x000001f1 } },
{ AR5K_RF_GAIN(25), { 0x00000000, 0x00000031 } },
{ AR5K_RF_GAIN(26), { 0x00000000, 0x00000071 } },
{ AR5K_RF_GAIN(27), { 0x00000000, 0x000001b8 } },
{ AR5K_RF_GAIN(28), { 0x00000000, 0x000001f8 } },
{ AR5K_RF_GAIN(29), { 0x00000000, 0x00000038 } },
{ AR5K_RF_GAIN(30), { 0x00000000, 0x00000078 } },
{ AR5K_RF_GAIN(31), { 0x00000000, 0x000000b8 } },
{ AR5K_RF_GAIN(32), { 0x00000000, 0x000001b9 } },
{ AR5K_RF_GAIN(33), { 0x00000000, 0x000001f9 } },
{ AR5K_RF_GAIN(34), { 0x00000000, 0x00000039 } },
{ AR5K_RF_GAIN(35), { 0x00000000, 0x00000079 } },
{ AR5K_RF_GAIN(36), { 0x00000000, 0x000000b9 } },
{ AR5K_RF_GAIN(37), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(38), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(39), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(40), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(41), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(42), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(43), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(44), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(45), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(46), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(47), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(48), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(49), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(50), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(51), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(52), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(53), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(54), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(55), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(56), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(57), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(58), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(59), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(60), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(61), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(62), { 0x00000000, 0x000000f9 } },
{ AR5K_RF_GAIN(63), { 0x00000000, 0x000000f9 } },
};
#define AR5K_GAIN_CRN_FIX_BITS_5111 4
#define AR5K_GAIN_CRN_FIX_BITS_5112 7
#define AR5K_GAIN_CRN_MAX_FIX_BITS AR5K_GAIN_CRN_FIX_BITS_5112
#define AR5K_GAIN_DYN_ADJUST_HI_MARGIN 15
#define AR5K_GAIN_DYN_ADJUST_LO_MARGIN 20
#define AR5K_GAIN_CCK_PROBE_CORR 5
#define AR5K_GAIN_CCK_OFDM_GAIN_DELTA 15
#define AR5K_GAIN_STEP_COUNT 10
/* Check if our current measurement is inside our
* current variable attenuation window */
#define AR5K_GAIN_CHECK_ADJUST(_g) \
((_g)->g_current <= (_g)->g_low || (_g)->g_current >= (_g)->g_high)
struct ath5k_gain_opt_step {
s8 gos_param[AR5K_GAIN_CRN_MAX_FIX_BITS];
s8 gos_gain;
};
struct ath5k_gain_opt {
u8 go_default;
u8 go_steps_count;
const struct ath5k_gain_opt_step go_step[AR5K_GAIN_STEP_COUNT];
};
/*
* Parameters on gos_param:
* 1) Tx clip PHY register
* 2) PWD 90 RF register
* 3) PWD 84 RF register
* 4) RFGainSel RF register
*/
static const struct ath5k_gain_opt rfgain_opt_5111 = {
4,
9,
{
{ { 4, 1, 1, 1 }, 6 },
{ { 4, 0, 1, 1 }, 4 },
{ { 3, 1, 1, 1 }, 3 },
{ { 4, 0, 0, 1 }, 1 },
{ { 4, 1, 1, 0 }, 0 },
{ { 4, 0, 1, 0 }, -2 },
{ { 3, 1, 1, 0 }, -3 },
{ { 4, 0, 0, 0 }, -4 },
{ { 2, 1, 1, 0 }, -6 }
}
};
/*
* Parameters on gos_param:
* 1) Mixgain ovr RF register
* 2) PWD 138 RF register
* 3) PWD 137 RF register
* 4) PWD 136 RF register
* 5) PWD 132 RF register
* 6) PWD 131 RF register
* 7) PWD 130 RF register
*/
static const struct ath5k_gain_opt rfgain_opt_5112 = {
1,
8,
{
{ { 3, 0, 0, 0, 0, 0, 0 }, 6 },
{ { 2, 0, 0, 0, 0, 0, 0 }, 0 },
{ { 1, 0, 0, 0, 0, 0, 0 }, -3 },
{ { 0, 0, 0, 0, 0, 0, 0 }, -6 },
{ { 0, 1, 1, 0, 0, 0, 0 }, -8 },
{ { 0, 1, 1, 0, 1, 1, 0 }, -10 },
{ { 0, 1, 0, 1, 1, 1, 0 }, -13 },
{ { 0, 1, 0, 1, 1, 0, 1 }, -16 },
}
};

View File

@@ -0,0 +1,24 @@
config ATH9K
tristate "Atheros 802.11n wireless cards support"
depends on PCI && MAC80211 && WLAN_80211
depends on RFKILL || RFKILL=n
select ATH_COMMON
select MAC80211_LEDS
select LEDS_CLASS
select NEW_LEDS
---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.
config ATH9K_DEBUG
bool "Atheros ath9k debugging"
depends on ATH9K
---help---
Say Y, if you need ath9k to display debug messages.
Pass the debug mask as a module parameter:
modprobe ath9k debug=0x00002000
Look in ath9k/core.h for possible debug masks

View File

@@ -0,0 +1,18 @@
ath9k-y += hw.o \
eeprom.o \
mac.o \
calib.o \
ani.o \
phy.o \
beacon.o \
main.o \
recv.o \
xmit.o \
virtual.o \
rc.o
ath9k-$(CONFIG_PCI) += pci.o
ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o
ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o
obj-$(CONFIG_ATH9K) += ath9k.o

View File

@@ -0,0 +1,192 @@
/*
* Copyright (c) 2008-2009 Atheros Communications Inc.
* Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
* Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
*
* 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/nl80211.h>
#include <linux/platform_device.h>
#include <linux/ath9k_platform.h>
#include "ath9k.h"
/* return bus cachesize in 4B word units */
static void ath_ahb_read_cachesize(struct ath_softc *sc, int *csz)
{
*csz = L1_CACHE_BYTES >> 2;
}
static void ath_ahb_cleanup(struct ath_softc *sc)
{
iounmap(sc->mem);
}
static bool ath_ahb_eeprom_read(struct ath_hw *ah, u32 off, u16 *data)
{
struct ath_softc *sc = ah->ah_sc;
struct platform_device *pdev = to_platform_device(sc->dev);
struct ath9k_platform_data *pdata;
pdata = (struct ath9k_platform_data *) pdev->dev.platform_data;
if (off >= (ARRAY_SIZE(pdata->eeprom_data))) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"%s: flash read failed, offset %08x is out of range\n",
__func__, off);
return false;
}
*data = pdata->eeprom_data[off];
return true;
}
static struct ath_bus_ops ath_ahb_bus_ops = {
.read_cachesize = ath_ahb_read_cachesize,
.cleanup = ath_ahb_cleanup,
.eeprom_read = ath_ahb_eeprom_read,
};
static int ath_ahb_probe(struct platform_device *pdev)
{
void __iomem *mem;
struct ath_wiphy *aphy;
struct ath_softc *sc;
struct ieee80211_hw *hw;
struct resource *res;
int irq;
int ret = 0;
struct ath_hw *ah;
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "no platform data specified\n");
ret = -EINVAL;
goto err_out;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "no memory resource found\n");
ret = -ENXIO;
goto err_out;
}
mem = ioremap_nocache(res->start, res->end - res->start + 1);
if (mem == NULL) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
goto err_out;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(&pdev->dev, "no IRQ resource found\n");
ret = -ENXIO;
goto err_iounmap;
}
irq = res->start;
hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy) +
sizeof(struct ath_softc), &ath9k_ops);
if (hw == NULL) {
dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
ret = -ENOMEM;
goto err_iounmap;
}
SET_IEEE80211_DEV(hw, &pdev->dev);
platform_set_drvdata(pdev, hw);
aphy = hw->priv;
sc = (struct ath_softc *) (aphy + 1);
aphy->sc = sc;
aphy->hw = hw;
sc->pri_wiphy = aphy;
sc->hw = hw;
sc->dev = &pdev->dev;
sc->mem = mem;
sc->bus_ops = &ath_ahb_bus_ops;
sc->irq = irq;
ret = ath_attach(AR5416_AR9100_DEVID, sc);
if (ret != 0) {
dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret);
ret = -ENODEV;
goto err_free_hw;
}
ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc);
if (ret) {
dev_err(&pdev->dev, "request_irq failed, err=%d\n", ret);
ret = -EIO;
goto err_detach;
}
ah = sc->sc_ah;
printk(KERN_INFO
"%s: Atheros AR%s MAC/BB Rev:%x, "
"AR%s RF Rev:%x, mem=0x%lx, irq=%d\n",
wiphy_name(hw->wiphy),
ath_mac_bb_name(ah->hw_version.macVersion),
ah->hw_version.macRev,
ath_rf_name((ah->hw_version.analog5GhzRev & AR_RADIO_SREV_MAJOR)),
ah->hw_version.phyRev,
(unsigned long)mem, irq);
return 0;
err_detach:
ath_detach(sc);
err_free_hw:
ieee80211_free_hw(hw);
platform_set_drvdata(pdev, NULL);
err_iounmap:
iounmap(mem);
err_out:
return ret;
}
static int ath_ahb_remove(struct platform_device *pdev)
{
struct ieee80211_hw *hw = platform_get_drvdata(pdev);
if (hw) {
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
ath_cleanup(sc);
platform_set_drvdata(pdev, NULL);
}
return 0;
}
static struct platform_driver ath_ahb_driver = {
.probe = ath_ahb_probe,
.remove = ath_ahb_remove,
.driver = {
.name = "ath9k",
.owner = THIS_MODULE,
},
};
int ath_ahb_init(void)
{
return platform_driver_register(&ath_ahb_driver);
}
void ath_ahb_exit(void)
{
platform_driver_unregister(&ath_ahb_driver);
}

View File

@@ -0,0 +1,822 @@
/*
* Copyright (c) 2008-2009 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 "ath9k.h"
static int ath9k_hw_get_ani_channel_idx(struct ath_hw *ah,
struct ath9k_channel *chan)
{
int i;
for (i = 0; i < ARRAY_SIZE(ah->ani); i++) {
if (ah->ani[i].c &&
ah->ani[i].c->channel == chan->channel)
return i;
if (ah->ani[i].c == NULL) {
ah->ani[i].c = chan;
return i;
}
}
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"No more channel states left. Using channel 0\n");
return 0;
}
static bool ath9k_hw_ani_control(struct ath_hw *ah,
enum ath9k_ani_cmd cmd, int param)
{
struct ar5416AniState *aniState = ah->curani;
switch (cmd & ah->ani_function) {
case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{
u32 level = param;
if (level >= ARRAY_SIZE(ah->totalSizeDesired)) {
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"level out of range (%u > %u)\n",
level,
(unsigned)ARRAY_SIZE(ah->totalSizeDesired));
return false;
}
REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
AR_PHY_DESIRED_SZ_TOT_DES,
ah->totalSizeDesired[level]);
REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
AR_PHY_AGC_CTL1_COARSE_LOW,
ah->coarse_low[level]);
REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
AR_PHY_AGC_CTL1_COARSE_HIGH,
ah->coarse_high[level]);
REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
AR_PHY_FIND_SIG_FIRPWR,
ah->firpwr[level]);
if (level > aniState->noiseImmunityLevel)
ah->stats.ast_ani_niup++;
else if (level < aniState->noiseImmunityLevel)
ah->stats.ast_ani_nidown++;
aniState->noiseImmunityLevel = level;
break;
}
case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{
const int m1ThreshLow[] = { 127, 50 };
const int m2ThreshLow[] = { 127, 40 };
const int m1Thresh[] = { 127, 0x4d };
const int m2Thresh[] = { 127, 0x40 };
const int m2CountThr[] = { 31, 16 };
const int m2CountThrLow[] = { 63, 48 };
u32 on = param ? 1 : 0;
REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
m1ThreshLow[on]);
REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
m2ThreshLow[on]);
REG_RMW_FIELD(ah, AR_PHY_SFCORR,
AR_PHY_SFCORR_M1_THRESH,
m1Thresh[on]);
REG_RMW_FIELD(ah, AR_PHY_SFCORR,
AR_PHY_SFCORR_M2_THRESH,
m2Thresh[on]);
REG_RMW_FIELD(ah, AR_PHY_SFCORR,
AR_PHY_SFCORR_M2COUNT_THR,
m2CountThr[on]);
REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
m2CountThrLow[on]);
REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
m1ThreshLow[on]);
REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
m2ThreshLow[on]);
REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
AR_PHY_SFCORR_EXT_M1_THRESH,
m1Thresh[on]);
REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
AR_PHY_SFCORR_EXT_M2_THRESH,
m2Thresh[on]);
if (on)
REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
else
REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
if (!on != aniState->ofdmWeakSigDetectOff) {
if (on)
ah->stats.ast_ani_ofdmon++;
else
ah->stats.ast_ani_ofdmoff++;
aniState->ofdmWeakSigDetectOff = !on;
}
break;
}
case ATH9K_ANI_CCK_WEAK_SIGNAL_THR:{
const int weakSigThrCck[] = { 8, 6 };
u32 high = param ? 1 : 0;
REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,
AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK,
weakSigThrCck[high]);
if (high != aniState->cckWeakSigThreshold) {
if (high)
ah->stats.ast_ani_cckhigh++;
else
ah->stats.ast_ani_ccklow++;
aniState->cckWeakSigThreshold = high;
}
break;
}
case ATH9K_ANI_FIRSTEP_LEVEL:{
const int firstep[] = { 0, 4, 8 };
u32 level = param;
if (level >= ARRAY_SIZE(firstep)) {
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"level out of range (%u > %u)\n",
level,
(unsigned) ARRAY_SIZE(firstep));
return false;
}
REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
AR_PHY_FIND_SIG_FIRSTEP,
firstep[level]);
if (level > aniState->firstepLevel)
ah->stats.ast_ani_stepup++;
else if (level < aniState->firstepLevel)
ah->stats.ast_ani_stepdown++;
aniState->firstepLevel = level;
break;
}
case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{
const int cycpwrThr1[] =
{ 2, 4, 6, 8, 10, 12, 14, 16 };
u32 level = param;
if (level >= ARRAY_SIZE(cycpwrThr1)) {
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"level out of range (%u > %u)\n",
level,
(unsigned)
ARRAY_SIZE(cycpwrThr1));
return false;
}
REG_RMW_FIELD(ah, AR_PHY_TIMING5,
AR_PHY_TIMING5_CYCPWR_THR1,
cycpwrThr1[level]);
if (level > aniState->spurImmunityLevel)
ah->stats.ast_ani_spurup++;
else if (level < aniState->spurImmunityLevel)
ah->stats.ast_ani_spurdown++;
aniState->spurImmunityLevel = level;
break;
}
case ATH9K_ANI_PRESENT:
break;
default:
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"invalid cmd %u\n", cmd);
return false;
}
DPRINTF(ah->ah_sc, ATH_DBG_ANI, "ANI parameters:\n");
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"noiseImmunityLevel=%d, spurImmunityLevel=%d, "
"ofdmWeakSigDetectOff=%d\n",
aniState->noiseImmunityLevel, aniState->spurImmunityLevel,
!aniState->ofdmWeakSigDetectOff);
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"cckWeakSigThreshold=%d, "
"firstepLevel=%d, listenTime=%d\n",
aniState->cckWeakSigThreshold, aniState->firstepLevel,
aniState->listenTime);
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n",
aniState->cycleCount, aniState->ofdmPhyErrCount,
aniState->cckPhyErrCount);
return true;
}
static void ath9k_hw_update_mibstats(struct ath_hw *ah,
struct ath9k_mib_stats *stats)
{
stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL);
stats->rts_bad += REG_READ(ah, AR_RTS_FAIL);
stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL);
stats->rts_good += REG_READ(ah, AR_RTS_OK);
stats->beacons += REG_READ(ah, AR_BEACON_CNT);
}
static void ath9k_ani_restart(struct ath_hw *ah)
{
struct ar5416AniState *aniState;
if (!DO_ANI(ah))
return;
aniState = ah->curani;
aniState->listenTime = 0;
if (ah->has_hw_phycounters) {
if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) {
aniState->ofdmPhyErrBase = 0;
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"OFDM Trigger is too high for hw counters\n");
} else {
aniState->ofdmPhyErrBase =
AR_PHY_COUNTMAX - aniState->ofdmTrigHigh;
}
if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) {
aniState->cckPhyErrBase = 0;
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"CCK Trigger is too high for hw counters\n");
} else {
aniState->cckPhyErrBase =
AR_PHY_COUNTMAX - aniState->cckTrigHigh;
}
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"Writing ofdmbase=%u cckbase=%u\n",
aniState->ofdmPhyErrBase,
aniState->cckPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
}
aniState->ofdmPhyErrCount = 0;
aniState->cckPhyErrCount = 0;
}
static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
{
struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
struct ar5416AniState *aniState;
int32_t rssi;
if (!DO_ANI(ah))
return;
aniState = ah->curani;
if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
aniState->noiseImmunityLevel + 1)) {
return;
}
}
if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) {
if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
aniState->spurImmunityLevel + 1)) {
return;
}
}
if (ah->opmode == NL80211_IFTYPE_AP) {
if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
aniState->firstepLevel + 1);
}
return;
}
rssi = BEACON_RSSI(ah);
if (rssi > aniState->rssiThrHigh) {
if (!aniState->ofdmWeakSigDetectOff) {
if (ath9k_hw_ani_control(ah,
ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
false)) {
ath9k_hw_ani_control(ah,
ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
return;
}
}
if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
aniState->firstepLevel + 1);
return;
}
} else if (rssi > aniState->rssiThrLow) {
if (aniState->ofdmWeakSigDetectOff)
ath9k_hw_ani_control(ah,
ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
true);
if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
aniState->firstepLevel + 1);
return;
} else {
if (conf->channel->band == IEEE80211_BAND_2GHZ) {
if (!aniState->ofdmWeakSigDetectOff)
ath9k_hw_ani_control(ah,
ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
false);
if (aniState->firstepLevel > 0)
ath9k_hw_ani_control(ah,
ATH9K_ANI_FIRSTEP_LEVEL, 0);
return;
}
}
}
static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
{
struct ieee80211_conf *conf = &ah->ah_sc->hw->conf;
struct ar5416AniState *aniState;
int32_t rssi;
if (!DO_ANI(ah))
return;
aniState = ah->curani;
if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) {
if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
aniState->noiseImmunityLevel + 1)) {
return;
}
}
if (ah->opmode == NL80211_IFTYPE_AP) {
if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) {
ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
aniState->firstepLevel + 1);
}
return;
}
rssi = BEACON_RSSI(ah);
if (rssi > aniState->rssiThrLow) {
if (aniState->firstepLevel < HAL_FIRST_STEP_MAX)
ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
aniState->firstepLevel + 1);
} else {
if (conf->channel->band == IEEE80211_BAND_2GHZ) {
if (aniState->firstepLevel > 0)
ath9k_hw_ani_control(ah,
ATH9K_ANI_FIRSTEP_LEVEL, 0);
}
}
}
static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
{
struct ar5416AniState *aniState;
int32_t rssi;
aniState = ah->curani;
if (ah->opmode == NL80211_IFTYPE_AP) {
if (aniState->firstepLevel > 0) {
if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
aniState->firstepLevel - 1))
return;
}
} else {
rssi = BEACON_RSSI(ah);
if (rssi > aniState->rssiThrHigh) {
/* XXX: Handle me */
} else if (rssi > aniState->rssiThrLow) {
if (aniState->ofdmWeakSigDetectOff) {
if (ath9k_hw_ani_control(ah,
ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
true) == true)
return;
}
if (aniState->firstepLevel > 0) {
if (ath9k_hw_ani_control(ah,
ATH9K_ANI_FIRSTEP_LEVEL,
aniState->firstepLevel - 1) == true)
return;
}
} else {
if (aniState->firstepLevel > 0) {
if (ath9k_hw_ani_control(ah,
ATH9K_ANI_FIRSTEP_LEVEL,
aniState->firstepLevel - 1) == true)
return;
}
}
}
if (aniState->spurImmunityLevel > 0) {
if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
aniState->spurImmunityLevel - 1))
return;
}
if (aniState->noiseImmunityLevel > 0) {
ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
aniState->noiseImmunityLevel - 1);
return;
}
}
static int32_t ath9k_hw_ani_get_listen_time(struct ath_hw *ah)
{
struct ar5416AniState *aniState;
u32 txFrameCount, rxFrameCount, cycleCount;
int32_t listenTime;
txFrameCount = REG_READ(ah, AR_TFCNT);
rxFrameCount = REG_READ(ah, AR_RFCNT);
cycleCount = REG_READ(ah, AR_CCCNT);
aniState = ah->curani;
if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) {
listenTime = 0;
ah->stats.ast_ani_lzero++;
} else {
int32_t ccdelta = cycleCount - aniState->cycleCount;
int32_t rfdelta = rxFrameCount - aniState->rxFrameCount;
int32_t tfdelta = txFrameCount - aniState->txFrameCount;
listenTime = (ccdelta - rfdelta - tfdelta) / 44000;
}
aniState->cycleCount = cycleCount;
aniState->txFrameCount = txFrameCount;
aniState->rxFrameCount = rxFrameCount;
return listenTime;
}
void ath9k_ani_reset(struct ath_hw *ah)
{
struct ar5416AniState *aniState;
struct ath9k_channel *chan = ah->curchan;
int index;
if (!DO_ANI(ah))
return;
index = ath9k_hw_get_ani_channel_idx(ah, chan);
aniState = &ah->ani[index];
ah->curani = aniState;
if (DO_ANI(ah) && ah->opmode != NL80211_IFTYPE_STATION
&& ah->opmode != NL80211_IFTYPE_ADHOC) {
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"Reset ANI state opmode %u\n", ah->opmode);
ah->stats.ast_ani_reset++;
ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0);
ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0);
ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0);
ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
!ATH9K_ANI_USE_OFDM_WEAK_SIG);
ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
ATH9K_ANI_CCK_WEAK_SIG_THR);
ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
ATH9K_RX_FILTER_PHYERR);
if (ah->opmode == NL80211_IFTYPE_AP) {
ah->curani->ofdmTrigHigh =
ah->config.ofdm_trig_high;
ah->curani->ofdmTrigLow =
ah->config.ofdm_trig_low;
ah->curani->cckTrigHigh =
ah->config.cck_trig_high;
ah->curani->cckTrigLow =
ah->config.cck_trig_low;
}
ath9k_ani_restart(ah);
return;
}
if (aniState->noiseImmunityLevel != 0)
ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL,
aniState->noiseImmunityLevel);
if (aniState->spurImmunityLevel != 0)
ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
aniState->spurImmunityLevel);
if (aniState->ofdmWeakSigDetectOff)
ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
!aniState->ofdmWeakSigDetectOff);
if (aniState->cckWeakSigThreshold)
ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR,
aniState->cckWeakSigThreshold);
if (aniState->firstepLevel != 0)
ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL,
aniState->firstepLevel);
if (ah->has_hw_phycounters) {
ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) &
~ATH9K_RX_FILTER_PHYERR);
ath9k_ani_restart(ah);
REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
} else {
ath9k_ani_restart(ah);
ath9k_hw_setrxfilter(ah, ath9k_hw_getrxfilter(ah) |
ATH9K_RX_FILTER_PHYERR);
}
}
void ath9k_hw_ani_monitor(struct ath_hw *ah,
const struct ath9k_node_stats *stats,
struct ath9k_channel *chan)
{
struct ar5416AniState *aniState;
int32_t listenTime;
if (!DO_ANI(ah))
return;
aniState = ah->curani;
ah->stats.ast_nodestats = *stats;
listenTime = ath9k_hw_ani_get_listen_time(ah);
if (listenTime < 0) {
ah->stats.ast_ani_lneg++;
ath9k_ani_restart(ah);
return;
}
aniState->listenTime += listenTime;
if (ah->has_hw_phycounters) {
u32 phyCnt1, phyCnt2;
u32 ofdmPhyErrCnt, cckPhyErrCnt;
ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
if (phyCnt1 < aniState->ofdmPhyErrBase ||
phyCnt2 < aniState->cckPhyErrBase) {
if (phyCnt1 < aniState->ofdmPhyErrBase) {
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"phyCnt1 0x%x, resetting "
"counter value to 0x%x\n",
phyCnt1, aniState->ofdmPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_1,
aniState->ofdmPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_MASK_1,
AR_PHY_ERR_OFDM_TIMING);
}
if (phyCnt2 < aniState->cckPhyErrBase) {
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"phyCnt2 0x%x, resetting "
"counter value to 0x%x\n",
phyCnt2, aniState->cckPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_2,
aniState->cckPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_MASK_2,
AR_PHY_ERR_CCK_TIMING);
}
return;
}
ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
ah->stats.ast_ani_ofdmerrs +=
ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
ah->stats.ast_ani_cckerrs +=
cckPhyErrCnt - aniState->cckPhyErrCount;
aniState->cckPhyErrCount = cckPhyErrCnt;
}
if (aniState->listenTime > 5 * ah->aniperiod) {
if (aniState->ofdmPhyErrCount <= aniState->listenTime *
aniState->ofdmTrigLow / 1000 &&
aniState->cckPhyErrCount <= aniState->listenTime *
aniState->cckTrigLow / 1000)
ath9k_hw_ani_lower_immunity(ah);
ath9k_ani_restart(ah);
} else if (aniState->listenTime > ah->aniperiod) {
if (aniState->ofdmPhyErrCount > aniState->listenTime *
aniState->ofdmTrigHigh / 1000) {
ath9k_hw_ani_ofdm_err_trigger(ah);
ath9k_ani_restart(ah);
} else if (aniState->cckPhyErrCount >
aniState->listenTime * aniState->cckTrigHigh /
1000) {
ath9k_hw_ani_cck_err_trigger(ah);
ath9k_ani_restart(ah);
}
}
}
bool ath9k_hw_phycounters(struct ath_hw *ah)
{
return ah->has_hw_phycounters ? true : false;
}
void ath9k_enable_mib_counters(struct ath_hw *ah)
{
DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable MIB counters\n");
ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
REG_WRITE(ah, AR_FILT_OFDM, 0);
REG_WRITE(ah, AR_FILT_CCK, 0);
REG_WRITE(ah, AR_MIBC,
~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
& 0x0f);
REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
}
/* Freeze the MIB counters, get the stats and then clear them */
void ath9k_hw_disable_mib_counters(struct ath_hw *ah)
{
DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disable MIB counters\n");
REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC);
REG_WRITE(ah, AR_FILT_OFDM, 0);
REG_WRITE(ah, AR_FILT_CCK, 0);
}
u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah,
u32 *rxc_pcnt,
u32 *rxf_pcnt,
u32 *txf_pcnt)
{
static u32 cycles, rx_clear, rx_frame, tx_frame;
u32 good = 1;
u32 rc = REG_READ(ah, AR_RCCNT);
u32 rf = REG_READ(ah, AR_RFCNT);
u32 tf = REG_READ(ah, AR_TFCNT);
u32 cc = REG_READ(ah, AR_CCCNT);
if (cycles == 0 || cycles > cc) {
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"cycle counter wrap. ExtBusy = 0\n");
good = 0;
} else {
u32 cc_d = cc - cycles;
u32 rc_d = rc - rx_clear;
u32 rf_d = rf - rx_frame;
u32 tf_d = tf - tx_frame;
if (cc_d != 0) {
*rxc_pcnt = rc_d * 100 / cc_d;
*rxf_pcnt = rf_d * 100 / cc_d;
*txf_pcnt = tf_d * 100 / cc_d;
} else {
good = 0;
}
}
cycles = cc;
rx_frame = rf;
rx_clear = rc;
tx_frame = tf;
return good;
}
/*
* Process a MIB interrupt. We may potentially be invoked because
* any of the MIB counters overflow/trigger so don't assume we're
* here because a PHY error counter triggered.
*/
void ath9k_hw_procmibevent(struct ath_hw *ah,
const struct ath9k_node_stats *stats)
{
u32 phyCnt1, phyCnt2;
/* Reset these counters regardless */
REG_WRITE(ah, AR_FILT_OFDM, 0);
REG_WRITE(ah, AR_FILT_CCK, 0);
if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING))
REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
/* Clear the mib counters and save them in the stats */
ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
ah->stats.ast_nodestats = *stats;
if (!DO_ANI(ah))
return;
/* NB: these are not reset-on-read */
phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
struct ar5416AniState *aniState = ah->curani;
u32 ofdmPhyErrCnt, cckPhyErrCnt;
/* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */
ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase;
ah->stats.ast_ani_ofdmerrs +=
ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase;
ah->stats.ast_ani_cckerrs +=
cckPhyErrCnt - aniState->cckPhyErrCount;
aniState->cckPhyErrCount = cckPhyErrCnt;
/*
* NB: figure out which counter triggered. If both
* trigger we'll only deal with one as the processing
* clobbers the error counter so the trigger threshold
* check will never be true.
*/
if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh)
ath9k_hw_ani_ofdm_err_trigger(ah);
if (aniState->cckPhyErrCount > aniState->cckTrigHigh)
ath9k_hw_ani_cck_err_trigger(ah);
/* NB: always restart to insure the h/w counters are reset */
ath9k_ani_restart(ah);
}
}
void ath9k_hw_ani_setup(struct ath_hw *ah)
{
int i;
const int totalSizeDesired[] = { -55, -55, -55, -55, -62 };
const int coarseHigh[] = { -14, -14, -14, -14, -12 };
const int coarseLow[] = { -64, -64, -64, -64, -70 };
const int firpwr[] = { -78, -78, -78, -78, -80 };
for (i = 0; i < 5; i++) {
ah->totalSizeDesired[i] = totalSizeDesired[i];
ah->coarse_high[i] = coarseHigh[i];
ah->coarse_low[i] = coarseLow[i];
ah->firpwr[i] = firpwr[i];
}
}
void ath9k_hw_ani_attach(struct ath_hw *ah)
{
int i;
DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Attach ANI\n");
ah->has_hw_phycounters = 1;
memset(ah->ani, 0, sizeof(ah->ani));
for (i = 0; i < ARRAY_SIZE(ah->ani); i++) {
ah->ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH;
ah->ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW;
ah->ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH;
ah->ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW;
ah->ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH;
ah->ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW;
ah->ani[i].ofdmWeakSigDetectOff =
!ATH9K_ANI_USE_OFDM_WEAK_SIG;
ah->ani[i].cckWeakSigThreshold =
ATH9K_ANI_CCK_WEAK_SIG_THR;
ah->ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
ah->ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
if (ah->has_hw_phycounters) {
ah->ani[i].ofdmPhyErrBase =
AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH;
ah->ani[i].cckPhyErrBase =
AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH;
}
}
if (ah->has_hw_phycounters) {
DPRINTF(ah->ah_sc, ATH_DBG_ANI,
"Setting OfdmErrBase = 0x%08x\n",
ah->ani[0].ofdmPhyErrBase);
DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n",
ah->ani[0].cckPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase);
REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase);
ath9k_enable_mib_counters(ah);
}
ah->aniperiod = ATH9K_ANI_PERIOD;
if (ah->config.enable_ani)
ah->proc_phyerr |= HAL_PROCESS_ANI;
}
void ath9k_hw_ani_detach(struct ath_hw *ah)
{
DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Detach ANI\n");
if (ah->has_hw_phycounters) {
ath9k_hw_disable_mib_counters(ah);
REG_WRITE(ah, AR_PHY_ERR_1, 0);
REG_WRITE(ah, AR_PHY_ERR_2, 0);
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (c) 2008-2009 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 ANI_H
#define ANI_H
#define HAL_PROCESS_ANI 0x00000001
#define ATH9K_RSSI_EP_MULTIPLIER (1<<7)
#define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI))
#define HAL_EP_RND(x, mul) \
((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
#define BEACON_RSSI(ahp) \
HAL_EP_RND(ahp->stats.ast_nodestats.ns_avgbrssi, \
ATH9K_RSSI_EP_MULTIPLIER)
#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 HAL_NOISE_IMMUNE_MAX 4
#define HAL_SPUR_IMMUNE_MAX 7
#define HAL_FIRST_STEP_MAX 2
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
};
struct ath9k_mib_stats {
u32 ackrcv_bad;
u32 rts_bad;
u32 rts_good;
u32 fcs_bad;
u32 beacons;
};
struct ath9k_node_stats {
u32 ns_avgbrssi;
u32 ns_avgrssi;
u32 ns_avgtxrssi;
u32 ns_avgtxrate;
};
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];
};
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 ah_mibStats stats.ast_mibstats
void ath9k_ani_reset(struct ath_hw *ah);
void ath9k_hw_ani_monitor(struct ath_hw *ah,
const struct ath9k_node_stats *stats,
struct ath9k_channel *chan);
bool ath9k_hw_phycounters(struct ath_hw *ah);
void ath9k_enable_mib_counters(struct ath_hw *ah);
void ath9k_hw_disable_mib_counters(struct ath_hw *ah);
u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, u32 *rxc_pcnt,
u32 *rxf_pcnt, u32 *txf_pcnt);
void ath9k_hw_procmibevent(struct ath_hw *ah,
const struct ath9k_node_stats *stats);
void ath9k_hw_ani_setup(struct ath_hw *ah);
void ath9k_hw_ani_attach(struct ath_hw *ah);
void ath9k_hw_ani_detach(struct ath_hw *ah);
#endif /* ANI_H */

View File

@@ -0,0 +1,730 @@
/*
* Copyright (c) 2008-2009 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/etherdevice.h>
#include <linux/device.h>
#include <net/mac80211.h>
#include <linux/leds.h>
#include <linux/rfkill.h>
#include "hw.h"
#include "rc.h"
#include "debug.h"
struct ath_node;
/* 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) BUG_ON(!(exp))
#define TSF_TO_TU(_h,_l) \
((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
#define ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<<i))
static const u8 ath_bcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
struct ath_config {
u32 ath_aggr_prot;
u16 txpowlimit;
u8 cabqReadytime;
};
/*************************/
/* Descriptor Management */
/*************************/
#define ATH_TXBUF_RESET(_bf) do { \
(_bf)->bf_stale = false; \
(_bf)->bf_lastbf = NULL; \
(_bf)->bf_next = NULL; \
memset(&((_bf)->bf_state), 0, \
sizeof(struct ath_buf_state)); \
} while (0)
#define ATH_RXBUF_RESET(_bf) do { \
(_bf)->bf_stale = false; \
} while (0)
/**
* enum buffer_type - Buffer type flags
*
* @BUF_HT: Send this buffer using HT capabilities
* @BUF_AMPDU: This buffer is an ampdu, as part of an aggregate (during TX)
* @BUF_AGGR: Indicates whether the buffer can be aggregated
* (used in aggregation scheduling)
* @BUF_RETRY: Indicates whether the buffer is retried
* @BUF_XRETRY: To denote excessive retries of the buffer
*/
enum buffer_type {
BUF_HT = BIT(1),
BUF_AMPDU = BIT(2),
BUF_AGGR = BIT(3),
BUF_RETRY = BIT(4),
BUF_XRETRY = BIT(5),
};
struct ath_buf_state {
int bfs_nframes;
u16 bfs_al;
u16 bfs_frmlen;
int bfs_seqno;
int bfs_tidno;
int bfs_retries;
u8 bf_type;
u32 bfs_keyix;
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_keyix bf_state.bfs_keyix
#define bf_keytype bf_state.bfs_keytype
#define bf_isht(bf) (bf->bf_state.bf_type & BUF_HT)
#define bf_isampdu(bf) (bf->bf_state.bf_type & BUF_AMPDU)
#define bf_isaggr(bf) (bf->bf_state.bf_type & BUF_AGGR)
#define bf_isretried(bf) (bf->bf_state.bf_type & BUF_RETRY)
#define bf_isxretried(bf) (bf->bf_state.bf_type & BUF_XRETRY)
struct ath_buf {
struct list_head list;
struct ath_buf *bf_lastbf; /* last buf of this unit (a frame or
an aggregate) */
struct ath_buf *bf_next; /* next subframe in the aggregate */
struct sk_buff *bf_mpdu; /* enclosing frame structure */
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 */
bool bf_stale;
u16 bf_flags;
struct ath_buf_state bf_state;
dma_addr_t bf_dmacontext;
};
struct ath_descdma {
struct ath_desc *dd_desc;
dma_addr_t dd_desc_paddr;
u32 dd_desc_len;
struct ath_buf *dd_bufptr;
};
int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
struct list_head *head, const char *name,
int nbuf, int ndesc);
void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd,
struct list_head *head);
/***********/
/* RX / TX */
/***********/
#define ATH_MAX_ANTENNA 3
#define ATH_RXBUF 512
#define WME_NUM_TID 16
#define ATH_TXBUF 512
#define ATH_TXMAXTRY 13
#define ATH_11N_TXMAXTRY 10
#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)
#define WME_AC_BE 0
#define WME_AC_BK 1
#define WME_AC_VI 2
#define WME_AC_VO 3
#define WME_NUM_AC 4
#define ADDBA_EXCHANGE_ATTEMPTS 10
#define ATH_AGGR_DELIM_SZ 4
#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 ATH_AMPDU_LIMIT_MAX (64 * 1024 - 1)
#define ATH_AMPDU_LIMIT_DEFAULT ATH_AMPDU_LIMIT_MAX
#define IEEE80211_SEQ_SEQ_SHIFT 4
#define IEEE80211_SEQ_MAX 4096
#define IEEE80211_MIN_AMPDU_BUF 0x8
#define IEEE80211_HTCAP_MAXRXAMPDU_FACTOR 13
#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))
/* 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)->tid[(_tidno)])
enum ATH_AGGR_STATUS {
ATH_AGGR_DONE,
ATH_AGGR_BAW_CLOSED,
ATH_AGGR_LIMITED,
};
struct ath_txq {
u32 axq_qnum;
u32 *axq_link;
struct list_head axq_q;
spinlock_t axq_lock;
u32 axq_depth;
u8 axq_aggr_depth;
u32 axq_totalqueued;
bool stopped;
struct ath_buf *axq_linkbuf;
/* 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;
};
#define AGGR_CLEANUP BIT(1)
#define AGGR_ADDBA_COMPLETE BIT(2)
#define AGGR_ADDBA_PROGRESS BIT(3)
struct ath_atx_tid {
struct list_head list;
struct list_head buf_q;
struct ath_node *an;
struct ath_atx_ac *ac;
struct ath_buf *tx_buf[ATH_TID_MAX_BUFS];
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;
u8 state;
int addba_exchangeattempts;
};
struct ath_atx_ac {
int sched;
int qnum;
struct list_head list;
struct list_head tid_q;
};
struct ath_tx_control {
struct ath_txq *txq;
int if_id;
enum ath9k_internal_frame_type frame_type;
};
#define ATH_TX_ERROR 0x01
#define ATH_TX_XRETRY 0x02
#define ATH_TX_BAR 0x04
struct ath_node {
struct ath_softc *an_sc;
struct ath_atx_tid tid[WME_NUM_TID];
struct ath_atx_ac ac[WME_NUM_AC];
u16 maxampdu;
u8 mpdudensity;
};
struct ath_tx {
u16 seq_no;
u32 txqsetup;
int hwq_map[ATH9K_WME_AC_VO+1];
spinlock_t txbuflock;
struct list_head txbuf;
struct ath_txq txq[ATH9K_NUM_TX_QUEUES];
struct ath_descdma txdma;
};
struct ath_rx {
u8 defant;
u8 rxotherant;
u32 *rxlink;
int bufsize;
unsigned int rxfilter;
spinlock_t rxflushlock;
spinlock_t rxbuflock;
struct list_head rxbuf;
struct ath_descdma rxdma;
};
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);
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);
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_drain_all_txq(struct ath_softc *sc, bool retry_tx);
void ath_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);
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq);
int ath_tx_init(struct ath_softc *sc, int nbufs);
void ath_tx_cleanup(struct ath_softc *sc);
struct ath_txq *ath_test_get_txq(struct ath_softc *sc, struct sk_buff *skb);
int ath_txq_update(struct ath_softc *sc, int qnum,
struct ath9k_tx_queue_info *q);
int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ath_tx_control *txctl);
void ath_tx_tasklet(struct ath_softc *sc);
void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb);
bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno);
int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
u16 tid, u16 *ssn);
int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
/********/
/* VIFs */
/********/
struct ath_vif {
int av_bslot;
__le64 tsf_adjust; /* TSF adjustment for staggered beacons */
enum nl80211_iftype av_opmode;
struct ath_buf *av_bcbuf;
struct ath_tx_control av_btxctl;
u8 bssid[ETH_ALEN]; /* current BSSID from config_interface */
};
/*******************/
/* 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
#define ATH_DEFAULT_BINTVAL 100 /* TU */
#define ATH_DEFAULT_BMISS_LIMIT 10
#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024)
struct ath_beacon_config {
u16 beacon_interval;
u16 listen_interval;
u16 dtim_period;
u16 bmiss_timeout;
u8 dtim_count;
};
struct ath_beacon {
enum {
OK, /* no change needed */
UPDATE, /* update pending */
COMMIT /* beacon sent, commit change */
} updateslot; /* slot time update fsm */
u32 beaconq;
u32 bmisscnt;
u32 ast_be_xmit;
u64 bc_tstamp;
struct ieee80211_vif *bslot[ATH_BCBUF];
struct ath_wiphy *bslot_aphy[ATH_BCBUF];
int slottime;
int slotupdate;
struct ath9k_tx_queue_info beacon_qi;
struct ath_descdma bdma;
struct ath_txq *cabq;
struct list_head bbuf;
};
void ath_beacon_tasklet(unsigned long data);
void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif);
int ath_beaconq_setup(struct ath_hw *ah);
int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif);
void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp);
/*******/
/* ANI */
/*******/
#define ATH_STA_SHORT_CALINTERVAL 1000 /* 1 second */
#define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */
#define ATH_ANI_POLLINTERVAL 100 /* 100 ms */
#define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */
#define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */
struct ath_ani {
bool caldone;
int16_t noise_floor;
unsigned int longcal_timer;
unsigned int shortcal_timer;
unsigned int resetcal_timer;
unsigned int checkani_timer;
struct timer_list timer;
};
/********************/
/* LED Control */
/********************/
#define ATH_LED_PIN 1
#define ATH_LED_ON_DURATION_IDLE 350 /* in msecs */
#define ATH_LED_OFF_DURATION_IDLE 250 /* in msecs */
enum ath_led_type {
ATH_LED_RADIO,
ATH_LED_ASSOC,
ATH_LED_TX,
ATH_LED_RX
};
struct ath_led {
struct ath_softc *sc;
struct led_classdev led_cdev;
enum ath_led_type led_type;
char name[32];
bool registered;
};
/* Rfkill */
#define ATH_RFKILL_POLL_INTERVAL 2000 /* msecs */
struct ath_rfkill {
struct rfkill *rfkill;
struct delayed_work rfkill_poll;
char rfkill_name[32];
};
/********************/
/* 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_MAX_SW_RETRIES 10
#define ATH_CHAN_MAX 255
#define IEEE80211_WEP_NKID 4 /* number of key ids */
/*
* 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 ATH_TXPOWER_MAX 100 /* .5 dBm units */
#define ATH_RSSI_DUMMY_MARKER 0x127
#define ATH_RATE_DUMMY_MARKER 0
#define SC_OP_INVALID BIT(0)
#define SC_OP_BEACONS BIT(1)
#define SC_OP_RXAGGR BIT(2)
#define SC_OP_TXAGGR BIT(3)
#define SC_OP_FULL_RESET BIT(4)
#define SC_OP_PREAMBLE_SHORT BIT(5)
#define SC_OP_PROTECT_ENABLE BIT(6)
#define SC_OP_RXFLUSH BIT(7)
#define SC_OP_LED_ASSOCIATED BIT(8)
#define SC_OP_RFKILL_REGISTERED BIT(9)
#define SC_OP_RFKILL_SW_BLOCKED BIT(10)
#define SC_OP_RFKILL_HW_BLOCKED BIT(11)
#define SC_OP_WAIT_FOR_BEACON BIT(12)
#define SC_OP_LED_ON BIT(13)
#define SC_OP_SCANNING BIT(14)
#define SC_OP_TSF_RESET BIT(15)
struct ath_bus_ops {
void (*read_cachesize)(struct ath_softc *sc, int *csz);
void (*cleanup)(struct ath_softc *sc);
bool (*eeprom_read)(struct ath_hw *ah, u32 off, u16 *data);
};
struct ath_wiphy;
struct ath_softc {
struct ieee80211_hw *hw;
struct device *dev;
spinlock_t wiphy_lock; /* spinlock to protect ath_wiphy data */
struct ath_wiphy *pri_wiphy;
struct ath_wiphy **sec_wiphy; /* secondary wiphys (virtual radios); may
* have NULL entries */
int num_sec_wiphy; /* number of sec_wiphy pointers in the array */
int chan_idx;
int chan_is_ht;
struct ath_wiphy *next_wiphy;
struct work_struct chan_work;
int wiphy_select_failures;
unsigned long wiphy_select_first_fail;
struct delayed_work wiphy_work;
unsigned long wiphy_scheduler_int;
int wiphy_scheduler_index;
struct tasklet_struct intr_tq;
struct tasklet_struct bcon_tasklet;
struct ath_hw *sc_ah;
void __iomem *mem;
int irq;
spinlock_t sc_resetlock;
spinlock_t sc_serial_rw;
struct mutex mutex;
u8 curbssid[ETH_ALEN];
u8 bssidmask[ETH_ALEN];
u32 intrstatus;
u32 sc_flags; /* SC_OP_* */
u16 curtxpow;
u16 curaid;
u16 cachelsz;
u8 nbcnvifs;
u16 nvifs;
u8 tx_chainmask;
u8 rx_chainmask;
u32 keymax;
DECLARE_BITMAP(keymap, ATH_KEYMAX);
u8 splitmic;
atomic_t ps_usecount;
enum ath9k_int imask;
enum ath9k_ht_extprotspacing ht_extprotspacing;
enum ath9k_ht_macmode tx_chan_width;
struct ath_config config;
struct ath_rx rx;
struct ath_tx tx;
struct ath_beacon beacon;
struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX];
struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX];
struct ath_rate_table *cur_rate_table;
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
struct ath_led radio_led;
struct ath_led assoc_led;
struct ath_led tx_led;
struct ath_led rx_led;
struct delayed_work ath_led_blink_work;
int led_on_duration;
int led_off_duration;
int led_on_cnt;
int led_off_cnt;
struct ath_rfkill rf_kill;
struct ath_ani ani;
struct ath9k_node_stats nodestats;
#ifdef CONFIG_ATH9K_DEBUG
struct ath9k_debug debug;
#endif
struct ath_bus_ops *bus_ops;
};
struct ath_wiphy {
struct ath_softc *sc; /* shared for all virtual wiphys */
struct ieee80211_hw *hw;
enum ath_wiphy_state {
ATH_WIPHY_INACTIVE,
ATH_WIPHY_ACTIVE,
ATH_WIPHY_PAUSING,
ATH_WIPHY_PAUSED,
ATH_WIPHY_SCAN,
} state;
int chan_idx;
int chan_is_ht;
};
int ath_reset(struct ath_softc *sc, bool retry_tx);
int ath_get_hal_qnum(u16 queue, struct ath_softc *sc);
int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc);
int ath_cabq_update(struct ath_softc *);
static inline void ath_read_cachesize(struct ath_softc *sc, int *csz)
{
sc->bus_ops->read_cachesize(sc, csz);
}
static inline void ath_bus_cleanup(struct ath_softc *sc)
{
sc->bus_ops->cleanup(sc);
}
extern struct ieee80211_ops ath9k_ops;
irqreturn_t ath_isr(int irq, void *dev);
void ath_cleanup(struct ath_softc *sc);
int ath_attach(u16 devid, struct ath_softc *sc);
void ath_detach(struct ath_softc *sc);
const char *ath_mac_bb_name(u32 mac_bb_version);
const char *ath_rf_name(u16 rf_version);
void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
struct ath9k_channel *ichan);
void ath_update_chainmask(struct ath_softc *sc, int is_ht);
int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
struct ath9k_channel *hchan);
void ath_radio_enable(struct ath_softc *sc);
void ath_radio_disable(struct ath_softc *sc);
#ifdef CONFIG_PCI
int ath_pci_init(void);
void ath_pci_exit(void);
#else
static inline int ath_pci_init(void) { return 0; };
static inline void ath_pci_exit(void) {};
#endif
#ifdef CONFIG_ATHEROS_AR71XX
int ath_ahb_init(void);
void ath_ahb_exit(void);
#else
static inline int ath_ahb_init(void) { return 0; };
static inline void ath_ahb_exit(void) {};
#endif
static inline void ath9k_ps_wakeup(struct ath_softc *sc)
{
if (atomic_inc_return(&sc->ps_usecount) == 1)
if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) {
sc->sc_ah->restore_mode = sc->sc_ah->power_mode;
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
}
}
static inline void ath9k_ps_restore(struct ath_softc *sc)
{
if (atomic_dec_and_test(&sc->ps_usecount))
if ((sc->hw->conf.flags & IEEE80211_CONF_PS) &&
!(sc->sc_flags & SC_OP_WAIT_FOR_BEACON))
ath9k_hw_setpower(sc->sc_ah,
sc->sc_ah->restore_mode);
}
void ath9k_set_bssid_mask(struct ieee80211_hw *hw);
int ath9k_wiphy_add(struct ath_softc *sc);
int ath9k_wiphy_del(struct ath_wiphy *aphy);
void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb);
int ath9k_wiphy_pause(struct ath_wiphy *aphy);
int ath9k_wiphy_unpause(struct ath_wiphy *aphy);
int ath9k_wiphy_select(struct ath_wiphy *aphy);
void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int);
void ath9k_wiphy_chan_work(struct work_struct *work);
bool ath9k_wiphy_started(struct ath_softc *sc);
void ath9k_wiphy_pause_all_forced(struct ath_softc *sc,
struct ath_wiphy *selected);
bool ath9k_wiphy_scanning(struct ath_softc *sc);
void ath9k_wiphy_work(struct work_struct *work);
/*
* Read and write, they both share the same lock. We do this to serialize
* reads and writes on Atheros 802.11n PCI devices only. This is required
* as the FIFO on these devices can only accept sanely 2 requests. After
* that the device goes bananas. Serializing the reads/writes prevents this
* from happening.
*/
static inline void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val)
{
if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
unsigned long flags;
spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags);
iowrite32(val, ah->ah_sc->mem + reg_offset);
spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags);
} else
iowrite32(val, ah->ah_sc->mem + reg_offset);
}
static inline unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset)
{
u32 val;
if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
unsigned long flags;
spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags);
val = ioread32(ah->ah_sc->mem + reg_offset);
spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags);
} else
val = ioread32(ah->ah_sc->mem + reg_offset);
return val;
}
#endif /* ATH9K_H */

View File

@@ -0,0 +1,743 @@
/*
* Copyright (c) 2008-2009 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 "ath9k.h"
#define FUDGE 2
/*
* 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_hw *ah = sc->sc_ah;
struct ath9k_tx_queue_info qi;
ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
/* 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->beacon.beacon_qi.tqi_aifs;
qi.tqi_cwmin = 2*sc->beacon.beacon_qi.tqi_cwmin;
qi.tqi_cwmax = sc->beacon.beacon_qi.tqi_cwmax;
}
if (!ath9k_hw_set_txq_props(ah, sc->beacon.beaconq, &qi)) {
DPRINTF(sc, ATH_DBG_FATAL,
"Unable to update h/w beacon queue parameters\n");
return 0;
} else {
ath9k_hw_resettxqueue(ah, sc->beacon.beaconq);
return 1;
}
}
/*
* 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_vif *avp,
struct ath_buf *bf)
{
struct sk_buff *skb = bf->bf_mpdu;
struct ath_hw *ah = sc->sc_ah;
struct ath_desc *ds;
struct ath9k_11n_rate_series series[4];
struct ath_rate_table *rt;
int flags, antenna, ctsrate = 0, ctsduration = 0;
u8 rate;
ds = bf->bf_desc;
flags = ATH9K_TXDESC_NOACK;
if (((sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
(sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) &&
(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
* XXX assumes two antennae
*/
antenna = ((sc->beacon.ast_be_xmit / sc->nbcnvifs) & 1 ? 2 : 1);
}
ds->ds_data = bf->bf_buf_addr;
rt = sc->cur_rate_table;
rate = rt->info[0].ratecode;
if (sc->sc_flags & SC_OP_PREAMBLE_SHORT)
rate |= rt->info[0].short_preamble;
ath9k_hw_set11n_txdesc(ah, ds, skb->len + FCS_LEN,
ATH9K_PKT_TYPE_BEACON,
MAX_RATE_POWER,
ATH9K_TXKEYIX_INVALID,
ATH9K_KEY_TYPE_CLEAR,
flags);
/* NB: beacon's BufLen must be a multiple of 4 bytes */
ath9k_hw_filltxdesc(ah, ds, roundup(skb->len, 4),
true, true, ds);
memset(series, 0, sizeof(struct ath9k_11n_rate_series) * 4);
series[0].Tries = 1;
series[0].Rate = rate;
series[0].ChSel = 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);
}
static struct ath_buf *ath_beacon_generate(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
struct ath_buf *bf;
struct ath_vif *avp;
struct sk_buff *skb;
struct ath_txq *cabq;
struct ieee80211_tx_info *info;
int cabq_depth;
if (aphy->state != ATH_WIPHY_ACTIVE)
return NULL;
avp = (void *)vif->drv_priv;
cabq = sc->beacon.cabq;
if (avp->av_bcbuf == NULL)
return NULL;
/* Release the old beacon first */
bf = avp->av_bcbuf;
skb = bf->bf_mpdu;
if (skb) {
dma_unmap_single(sc->dev, bf->bf_dmacontext,
skb->len, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
}
/* Get a new beacon from mac80211 */
skb = ieee80211_beacon_get(hw, vif);
bf->bf_mpdu = skb;
if (skb == NULL)
return NULL;
((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
avp->tsf_adjust;
info = IEEE80211_SKB_CB(skb);
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
/*
* TODO: make sure the seq# gets assigned properly (vs. other
* TX frames)
*/
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
sc->tx.seq_no += 0x10;
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
}
bf->bf_buf_addr = bf->bf_dmacontext =
dma_map_single(sc->dev, skb->data,
skb->len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
DPRINTF(sc, ATH_DBG_FATAL, "dma_mapping_error on beaconing\n");
return NULL;
}
skb = ieee80211_get_buffered_bc(hw, vif);
/*
* if the CABQ traffic from previous DTIM is pending and the current
* beacon is also a DTIM.
* 1) if there is only one vif let the cab traffic continue.
* 2) if there are more than one vif and we are using staggered
* beacons, then drain the cabq by dropping all the frames in
* the cabq so that the current vifs cab traffic can be scheduled.
*/
spin_lock_bh(&cabq->axq_lock);
cabq_depth = cabq->axq_depth;
spin_unlock_bh(&cabq->axq_lock);
if (skb && cabq_depth) {
if (sc->nvifs > 1) {
DPRINTF(sc, ATH_DBG_BEACON,
"Flushing previous cabq traffic\n");
ath_draintxq(sc, cabq, false);
}
}
ath_beacon_setup(sc, avp, bf);
while (skb) {
ath_tx_cabq(hw, skb);
skb = ieee80211_get_buffered_bc(hw, vif);
}
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,
struct ieee80211_vif *vif)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_buf *bf;
struct ath_vif *avp;
struct sk_buff *skb;
avp = (void *)vif->drv_priv;
if (avp->av_bcbuf == NULL)
return;
bf = avp->av_bcbuf;
skb = bf->bf_mpdu;
ath_beacon_setup(sc, avp, bf);
/* NB: caller is known to have already stopped tx dma */
ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bf->bf_daddr);
ath9k_hw_txstart(ah, sc->beacon.beaconq);
DPRINTF(sc, ATH_DBG_BEACON, "TXDP%u = %llx (%p)\n",
sc->beacon.beaconq, ito64(bf->bf_daddr), bf->bf_desc);
}
int ath_beaconq_setup(struct ath_hw *ah)
{
struct ath9k_tx_queue_info qi;
memset(&qi, 0, 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);
}
int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif)
{
struct ath_softc *sc = aphy->sc;
struct ath_vif *avp;
struct ath_buf *bf;
struct sk_buff *skb;
__le64 tstamp;
avp = (void *)vif->drv_priv;
/* 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->beacon.bbuf,
struct ath_buf, list);
list_del(&avp->av_bcbuf->list);
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)) {
int slot;
/*
* Assign the vif 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->beacon.bslot[slot] == NULL) {
/*
* XXX hack, space out slots to better
* deal with misses
*/
if (slot+1 < ATH_BCBUF &&
sc->beacon.bslot[slot+1] == NULL) {
avp->av_bslot = slot+1;
break;
}
avp->av_bslot = slot;
/* NB: keep looking for a double slot */
}
BUG_ON(sc->beacon.bslot[avp->av_bslot] != NULL);
sc->beacon.bslot[avp->av_bslot] = vif;
sc->beacon.bslot_aphy[avp->av_bslot] = aphy;
sc->nbcnvifs++;
}
}
/* release the previous beacon frame, if it already exists. */
bf = avp->av_bcbuf;
if (bf->bf_mpdu != NULL) {
skb = bf->bf_mpdu;
dma_unmap_single(sc->dev, bf->bf_dmacontext,
skb->len, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
}
/* NB: the beacon data buffer must be 32-bit aligned. */
skb = ieee80211_beacon_get(sc->hw, vif);
if (skb == NULL) {
DPRINTF(sc, ATH_DBG_BEACON, "cannot get skb\n");
return -ENOMEM;
}
tstamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp;
sc->beacon.bc_tstamp = le64_to_cpu(tstamp);
/* Calculate a TSF adjustment factor required for staggered beacons. */
if (avp->av_bslot > 0) {
u64 tsfadjust;
int intval;
intval = sc->hw->conf.beacon_int ?
sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL;
/*
* Calculate the TSF offset for this beacon slot, i.e., the
* number of usecs that need to be added to the timestamp field
* in Beacon and Probe Response frames. Beacon slot 0 is
* processed at the correct offset, so it does not require TSF
* adjustment. Other slots are adjusted to get the timestamp
* close to the TBTT for the BSS.
*/
tsfadjust = intval * avp->av_bslot / ATH_BCBUF;
avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
DPRINTF(sc, ATH_DBG_BEACON,
"stagger beacons, bslot %d intval %u tsfadjust %llu\n",
avp->av_bslot, intval, (unsigned long long)tsfadjust);
((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp =
avp->tsf_adjust;
} else
avp->tsf_adjust = cpu_to_le64(0);
bf->bf_mpdu = skb;
bf->bf_buf_addr = bf->bf_dmacontext =
dma_map_single(sc->dev, skb->data,
skb->len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
DPRINTF(sc, ATH_DBG_FATAL,
"dma_mapping_error on beacon alloc\n");
return -ENOMEM;
}
return 0;
}
void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp)
{
if (avp->av_bcbuf != NULL) {
struct ath_buf *bf;
if (avp->av_bslot != -1) {
sc->beacon.bslot[avp->av_bslot] = NULL;
sc->beacon.bslot_aphy[avp->av_bslot] = NULL;
sc->nbcnvifs--;
}
bf = avp->av_bcbuf;
if (bf->bf_mpdu != NULL) {
struct sk_buff *skb = bf->bf_mpdu;
dma_unmap_single(sc->dev, bf->bf_dmacontext,
skb->len, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
}
list_add_tail(&bf->list, &sc->beacon.bbuf);
avp->av_bcbuf = NULL;
}
}
void ath_beacon_tasklet(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)data;
struct ath_hw *ah = sc->sc_ah;
struct ath_buf *bf = NULL;
struct ieee80211_vif *vif;
struct ath_wiphy *aphy;
int slot;
u32 bfaddr, bc = 0, tsftu;
u64 tsf;
u16 intval;
/*
* 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->beacon.beaconq) != 0) {
sc->beacon.bmisscnt++;
if (sc->beacon.bmisscnt < BSTUCK_THRESH) {
DPRINTF(sc, ATH_DBG_BEACON,
"missed %u consecutive beacons\n",
sc->beacon.bmisscnt);
} else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
DPRINTF(sc, ATH_DBG_BEACON,
"beacon is officially stuck\n");
ath_reset(sc, false);
}
return;
}
if (sc->beacon.bmisscnt != 0) {
DPRINTF(sc, ATH_DBG_BEACON,
"resume beacon xmit after %u misses\n",
sc->beacon.bmisscnt);
sc->beacon.bmisscnt = 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.
*/
intval = sc->hw->conf.beacon_int ?
sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL;
tsf = ath9k_hw_gettsf64(ah);
tsftu = TSF_TO_TU(tsf>>32, tsf);
slot = ((tsftu % intval) * ATH_BCBUF) / intval;
/*
* Reverse the slot order to get slot 0 on the TBTT offset that does
* not require TSF adjustment and other slots adding
* slot/ATH_BCBUF * beacon_int to timestamp. For example, with
* ATH_BCBUF = 4, we process beacon slots as follows: 3 2 1 0 3 2 1 ..
* and slot 0 is at correct offset to TBTT.
*/
slot = ATH_BCBUF - slot - 1;
vif = sc->beacon.bslot[slot];
aphy = sc->beacon.bslot_aphy[slot];
DPRINTF(sc, ATH_DBG_BEACON,
"slot %d [tsf %llu tsftu %u intval %u] vif %p\n",
slot, tsf, tsftu, intval, vif);
bfaddr = 0;
if (vif) {
bf = ath_beacon_generate(aphy->hw, vif);
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.
*/
if (sc->beacon.updateslot == UPDATE) {
sc->beacon.updateslot = COMMIT; /* commit next beacon */
sc->beacon.slotupdate = slot;
} else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) {
ath9k_hw_setslottime(sc->sc_ah, sc->beacon.slottime);
sc->beacon.updateslot = OK;
}
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->beacon.beaconq)) {
DPRINTF(sc, ATH_DBG_FATAL,
"beacon queue %u did not stop?\n", sc->beacon.beaconq);
}
/* NB: cabq traffic should already be queued and primed */
ath9k_hw_puttxbuf(ah, sc->beacon.beaconq, bfaddr);
ath9k_hw_txstart(ah, sc->beacon.beaconq);
sc->beacon.ast_be_xmit += bc; /* XXX per-vif? */
}
}
/*
* 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.
*/
static void ath_beacon_config_ap(struct ath_softc *sc,
struct ath_beacon_config *conf,
struct ath_vif *avp)
{
u32 nexttbtt, intval;
/* Configure the timers only when the TSF has to be reset */
if (!(sc->sc_flags & SC_OP_TSF_RESET))
return;
/* NB: the beacon interval is kept internally in TU's */
intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
intval /= ATH_BCBUF; /* for staggered beacons */
nexttbtt = intval;
intval |= ATH9K_BEACON_RESET_TSF;
/*
* In AP mode we enable the beacon timers and SWBA interrupts to
* prepare beacon frames.
*/
intval |= ATH9K_BEACON_ENA;
sc->imask |= ATH9K_INT_SWBA;
ath_beaconq_config(sc);
/* Set the computed AP beacon timers */
ath9k_hw_set_interrupts(sc->sc_ah, 0);
ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
/* Clear the reset TSF flag, so that subsequent beacon updation
will not reset the HW TSF. */
sc->sc_flags &= ~SC_OP_TSF_RESET;
}
/*
* 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.
*/
static void ath_beacon_config_sta(struct ath_softc *sc,
struct ath_beacon_config *conf,
struct ath_vif *avp)
{
struct ath9k_beacon_state bs;
int dtimperiod, dtimcount, sleepduration;
int cfpperiod, cfpcount;
u32 nexttbtt = 0, intval, tsftu;
u64 tsf;
memset(&bs, 0, sizeof(bs));
intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
/*
* 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;
cfpperiod = 1; /* NB: no PCF support yet */
cfpcount = 0;
sleepduration = conf->listen_interval * intval;
if (sleepduration <= 0)
sleepduration = intval;
/*
* Pull nexttbtt forward to reflect the current
* TSF and calculate dtim+cfp state for the result.
*/
tsf = ath9k_hw_gettsf64(sc->sc_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);
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 ensure 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;
/* TSF out of range threshold fixed at 1 second */
bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
DPRINTF(sc, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu);
DPRINTF(sc, ATH_DBG_BEACON,
"bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
bs.bs_bmissthreshold, bs.bs_sleepduration,
bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext);
/* Set the computed STA beacon timers */
ath9k_hw_set_interrupts(sc->sc_ah, 0);
ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs);
sc->imask |= ATH9K_INT_BMISS;
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
}
static void ath_beacon_config_adhoc(struct ath_softc *sc,
struct ath_beacon_config *conf,
struct ath_vif *avp,
struct ieee80211_vif *vif)
{
u64 tsf;
u32 tsftu, intval, nexttbtt;
intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
/* Pull nexttbtt forward to reflect the current TSF */
nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp);
if (nexttbtt == 0)
nexttbtt = intval;
else if (intval)
nexttbtt = roundup(nexttbtt, intval);
tsf = ath9k_hw_gettsf64(sc->sc_ah);
tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE;
do {
nexttbtt += intval;
} while (nexttbtt < tsftu);
DPRINTF(sc, ATH_DBG_BEACON,
"IBSS nexttbtt %u intval %u (%u)\n",
nexttbtt, intval, 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 (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL))
sc->imask |= ATH9K_INT_SWBA;
ath_beaconq_config(sc);
/* Set the computed ADHOC beacon timers */
ath9k_hw_set_interrupts(sc->sc_ah, 0);
ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)
ath_beacon_start_adhoc(sc, vif);
}
void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
{
struct ath_beacon_config conf;
/* Setup the beacon configuration parameters */
memset(&conf, 0, sizeof(struct ath_beacon_config));
conf.beacon_interval = sc->hw->conf.beacon_int ?
sc->hw->conf.beacon_int : 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;
if (vif) {
struct ath_vif *avp = (struct ath_vif *)vif->drv_priv;
switch(avp->av_opmode) {
case NL80211_IFTYPE_AP:
ath_beacon_config_ap(sc, &conf, avp);
break;
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
ath_beacon_config_adhoc(sc, &conf, avp, vif);
break;
case NL80211_IFTYPE_STATION:
ath_beacon_config_sta(sc, &conf, avp);
break;
default:
DPRINTF(sc, ATH_DBG_CONFIG,
"Unsupported beaconing mode\n");
return;
}
sc->sc_flags |= SC_OP_BEACONS;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2008-2009 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 CALIB_H
#define CALIB_H
extern const struct hal_percal_data iq_cal_multi_sample;
extern const struct hal_percal_data iq_cal_single_sample;
extern const struct hal_percal_data adc_gain_cal_multi_sample;
extern const struct hal_percal_data adc_gain_cal_single_sample;
extern const struct hal_percal_data adc_dc_cal_multi_sample;
extern const struct hal_percal_data adc_dc_cal_single_sample;
extern const struct hal_percal_data adc_init_dc_cal;
#define AR_PHY_CCA_MAX_GOOD_VALUE -85
#define AR_PHY_CCA_MAX_HIGH_VALUE -62
#define AR_PHY_CCA_MIN_BAD_VALUE -140
#define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3
#define AR_PHY_CCA_FILTERWINDOW_LENGTH 5
#define NUM_NF_READINGS 6
#define ATH9K_NF_CAL_HIST_MAX 5
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)->cal_list_last == NULL) { \
(_ahp)->cal_list = \
(_ahp)->cal_list_last = (_perCal); \
((_ahp)->cal_list_last)->calNext = (_perCal); \
} else { \
((_ahp)->cal_list_last)->calNext = (_perCal); \
(_ahp)->cal_list_last = (_perCal); \
(_perCal)->calNext = (_ahp)->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_hw *);
void (*calPostProc) (struct ath_hw *, u8);
};
struct hal_cal_list {
const struct hal_percal_data *calData;
enum hal_cal_state calState;
struct hal_cal_list *calNext;
};
struct ath9k_nfcal_hist {
int16_t nfCalBuffer[ATH9K_NF_CAL_HIST_MAX];
u8 currIndex;
int16_t privNF;
u8 invalidNFcount;
};
bool ath9k_hw_reset_calvalid(struct ath_hw *ah);
void ath9k_hw_start_nfcal(struct ath_hw *ah);
void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
int16_t ath9k_hw_getnf(struct ath_hw *ah,
struct ath9k_channel *chan);
void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah);
s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan);
bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
u8 rxchainmask, bool longcal,
bool *isCalDone);
bool ath9k_hw_init_cal(struct ath_hw *ah,
struct ath9k_channel *chan);
#endif /* CALIB_H */

View File

@@ -0,0 +1,562 @@
/*
* Copyright (c) 2008-2009 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 <asm/unaligned.h>
#include "ath9k.h"
static unsigned int ath9k_debug = DBG_DEFAULT;
module_param_named(debug, ath9k_debug, uint, 0);
static struct dentry *ath9k_debugfs_root;
void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...)
{
if (!sc)
return;
if (sc->debug.debug_mask & dbg_mask) {
va_list args;
va_start(args, fmt);
printk(KERN_DEBUG "ath9k: ");
vprintk(fmt, args);
va_end(args);
}
}
static int ath9k_debugfs_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
static ssize_t read_file_dma(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
struct ath_hw *ah = sc->sc_ah;
char buf[1024];
unsigned int len = 0;
u32 val[ATH9K_NUM_DMA_DEBUG_REGS];
int i, qcuOffset = 0, dcuOffset = 0;
u32 *qcuBase = &val[0], *dcuBase = &val[4];
REG_WRITE(ah, AR_MACMISC,
((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
(AR_MACMISC_MISC_OBS_BUS_1 <<
AR_MACMISC_MISC_OBS_BUS_MSB_S)));
len += snprintf(buf + len, sizeof(buf) - len,
"Raw DMA Debug values:\n");
for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) {
if (i % 4 == 0)
len += snprintf(buf + len, sizeof(buf) - len, "\n");
val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32)));
len += snprintf(buf + len, sizeof(buf) - len, "%d: %08x ",
i, val[i]);
}
len += snprintf(buf + len, sizeof(buf) - len, "\n\n");
len += snprintf(buf + len, sizeof(buf) - len,
"Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) {
if (i == 8) {
qcuOffset = 0;
qcuBase++;
}
if (i == 6) {
dcuOffset = 0;
dcuBase++;
}
len += snprintf(buf + len, sizeof(buf) - len,
"%2d %2x %1x %2x %2x\n",
i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
(*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3),
val[2] & (0x7 << (i * 3)) >> (i * 3),
(*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
}
len += snprintf(buf + len, sizeof(buf) - len, "\n");
len += snprintf(buf + len, sizeof(buf) - len,
"qcu_stitch state: %2x qcu_fetch state: %2x\n",
(val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22);
len += snprintf(buf + len, sizeof(buf) - len,
"qcu_complete state: %2x dcu_complete state: %2x\n",
(val[3] & 0x1c000000) >> 26, (val[6] & 0x3));
len += snprintf(buf + len, sizeof(buf) - len,
"dcu_arb state: %2x dcu_fp state: %2x\n",
(val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27);
len += snprintf(buf + len, sizeof(buf) - len,
"chan_idle_dur: %3d chan_idle_dur_valid: %1d\n",
(val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10);
len += snprintf(buf + len, sizeof(buf) - len,
"txfifo_valid_0: %1d txfifo_valid_1: %1d\n",
(val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12);
len += snprintf(buf + len, sizeof(buf) - len,
"txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n",
(val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17);
len += snprintf(buf + len, sizeof(buf) - len, "pcu observe: 0x%x \n",
REG_READ(ah, AR_OBS_BUS_1));
len += snprintf(buf + len, sizeof(buf) - len,
"AR_CR: 0x%x \n", REG_READ(ah, AR_CR));
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static const struct file_operations fops_dma = {
.read = read_file_dma,
.open = ath9k_debugfs_open,
.owner = THIS_MODULE
};
void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status)
{
if (status)
sc->debug.stats.istats.total++;
if (status & ATH9K_INT_RX)
sc->debug.stats.istats.rxok++;
if (status & ATH9K_INT_RXEOL)
sc->debug.stats.istats.rxeol++;
if (status & ATH9K_INT_RXORN)
sc->debug.stats.istats.rxorn++;
if (status & ATH9K_INT_TX)
sc->debug.stats.istats.txok++;
if (status & ATH9K_INT_TXURN)
sc->debug.stats.istats.txurn++;
if (status & ATH9K_INT_MIB)
sc->debug.stats.istats.mib++;
if (status & ATH9K_INT_RXPHY)
sc->debug.stats.istats.rxphyerr++;
if (status & ATH9K_INT_RXKCM)
sc->debug.stats.istats.rx_keycache_miss++;
if (status & ATH9K_INT_SWBA)
sc->debug.stats.istats.swba++;
if (status & ATH9K_INT_BMISS)
sc->debug.stats.istats.bmiss++;
if (status & ATH9K_INT_BNR)
sc->debug.stats.istats.bnr++;
if (status & ATH9K_INT_CST)
sc->debug.stats.istats.cst++;
if (status & ATH9K_INT_GTT)
sc->debug.stats.istats.gtt++;
if (status & ATH9K_INT_TIM)
sc->debug.stats.istats.tim++;
if (status & ATH9K_INT_CABEND)
sc->debug.stats.istats.cabend++;
if (status & ATH9K_INT_DTIMSYNC)
sc->debug.stats.istats.dtimsync++;
if (status & ATH9K_INT_DTIM)
sc->debug.stats.istats.dtim++;
}
static ssize_t read_file_interrupt(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
char buf[512];
unsigned int len = 0;
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "RX", sc->debug.stats.istats.rxok);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "RXEOL", sc->debug.stats.istats.rxeol);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "RXORN", sc->debug.stats.istats.rxorn);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "TX", sc->debug.stats.istats.txok);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "TXURN", sc->debug.stats.istats.txurn);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "MIB", sc->debug.stats.istats.mib);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "RXPHY", sc->debug.stats.istats.rxphyerr);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "RXKCM", sc->debug.stats.istats.rx_keycache_miss);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "SWBA", sc->debug.stats.istats.swba);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "BMISS", sc->debug.stats.istats.bmiss);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "BNR", sc->debug.stats.istats.bnr);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "CST", sc->debug.stats.istats.cst);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "GTT", sc->debug.stats.istats.gtt);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "TIM", sc->debug.stats.istats.tim);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "CABEND", sc->debug.stats.istats.cabend);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "DTIMSYNC", sc->debug.stats.istats.dtimsync);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "DTIM", sc->debug.stats.istats.dtim);
len += snprintf(buf + len, sizeof(buf) - len,
"%8s: %10u\n", "TOTAL", sc->debug.stats.istats.total);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static const struct file_operations fops_interrupt = {
.read = read_file_interrupt,
.open = ath9k_debugfs_open,
.owner = THIS_MODULE
};
static void ath_debug_stat_11n_rc(struct ath_softc *sc, struct sk_buff *skb)
{
struct ath_tx_info_priv *tx_info_priv = NULL;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *rates = tx_info->status.rates;
int final_ts_idx, idx;
tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
final_ts_idx = tx_info_priv->tx.ts_rateindex;
idx = sc->cur_rate_table->info[rates[final_ts_idx].idx].dot11rate;
sc->debug.stats.n_rcstats[idx].success++;
}
static void ath_debug_stat_legacy_rc(struct ath_softc *sc, struct sk_buff *skb)
{
struct ath_tx_info_priv *tx_info_priv = NULL;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *rates = tx_info->status.rates;
int final_ts_idx, idx;
tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
final_ts_idx = tx_info_priv->tx.ts_rateindex;
idx = rates[final_ts_idx].idx;
sc->debug.stats.legacy_rcstats[idx].success++;
}
void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb)
{
if (conf_is_ht(&sc->hw->conf))
ath_debug_stat_11n_rc(sc, skb);
else
ath_debug_stat_legacy_rc(sc, skb);
}
/* FIXME: legacy rates, later on .. */
void ath_debug_stat_retries(struct ath_softc *sc, int rix,
int xretries, int retries, u8 per)
{
if (conf_is_ht(&sc->hw->conf)) {
int idx = sc->cur_rate_table->info[rix].dot11rate;
sc->debug.stats.n_rcstats[idx].xretries += xretries;
sc->debug.stats.n_rcstats[idx].retries += retries;
sc->debug.stats.n_rcstats[idx].per = per;
}
}
static ssize_t ath_read_file_stat_11n_rc(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
char buf[1024];
unsigned int len = 0;
int i = 0;
len += sprintf(buf, "%7s %13s %8s %8s %6s\n\n", "Rate", "Success",
"Retries", "XRetries", "PER");
for (i = 0; i <= 15; i++) {
len += snprintf(buf + len, sizeof(buf) - len,
"%5s%3d: %8u %8u %8u %8u\n", "MCS", i,
sc->debug.stats.n_rcstats[i].success,
sc->debug.stats.n_rcstats[i].retries,
sc->debug.stats.n_rcstats[i].xretries,
sc->debug.stats.n_rcstats[i].per);
}
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t ath_read_file_stat_legacy_rc(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
char buf[512];
unsigned int len = 0;
int i = 0;
len += sprintf(buf, "%7s %13s\n\n", "Rate", "Success");
for (i = 0; i < sc->cur_rate_table->rate_cnt; i++) {
len += snprintf(buf + len, sizeof(buf) - len, "%5u: %12u\n",
sc->cur_rate_table->info[i].ratekbps / 1000,
sc->debug.stats.legacy_rcstats[i].success);
}
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t read_file_rcstat(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
if (sc->cur_rate_table == NULL)
return 0;
if (conf_is_ht(&sc->hw->conf))
return ath_read_file_stat_11n_rc(file, user_buf, count, ppos);
else
return ath_read_file_stat_legacy_rc(file, user_buf, count ,ppos);
}
static const struct file_operations fops_rcstat = {
.read = read_file_rcstat,
.open = ath9k_debugfs_open,
.owner = THIS_MODULE
};
static const char * ath_wiphy_state_str(enum ath_wiphy_state state)
{
switch (state) {
case ATH_WIPHY_INACTIVE:
return "INACTIVE";
case ATH_WIPHY_ACTIVE:
return "ACTIVE";
case ATH_WIPHY_PAUSING:
return "PAUSING";
case ATH_WIPHY_PAUSED:
return "PAUSED";
case ATH_WIPHY_SCAN:
return "SCAN";
}
return "?";
}
static ssize_t read_file_wiphy(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
char buf[512];
unsigned int len = 0;
int i;
u8 addr[ETH_ALEN];
len += snprintf(buf + len, sizeof(buf) - len,
"primary: %s (%s chan=%d ht=%d)\n",
wiphy_name(sc->pri_wiphy->hw->wiphy),
ath_wiphy_state_str(sc->pri_wiphy->state),
sc->pri_wiphy->chan_idx, sc->pri_wiphy->chan_is_ht);
for (i = 0; i < sc->num_sec_wiphy; i++) {
struct ath_wiphy *aphy = sc->sec_wiphy[i];
if (aphy == NULL)
continue;
len += snprintf(buf + len, sizeof(buf) - len,
"secondary: %s (%s chan=%d ht=%d)\n",
wiphy_name(aphy->hw->wiphy),
ath_wiphy_state_str(aphy->state),
aphy->chan_idx, aphy->chan_is_ht);
}
put_unaligned_le32(REG_READ(sc->sc_ah, AR_STA_ID0), addr);
put_unaligned_le16(REG_READ(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4);
len += snprintf(buf + len, sizeof(buf) - len,
"addr: %pM\n", addr);
put_unaligned_le32(REG_READ(sc->sc_ah, AR_BSSMSKL), addr);
put_unaligned_le16(REG_READ(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4);
len += snprintf(buf + len, sizeof(buf) - len,
"addrmask: %pM\n", addr);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static struct ath_wiphy * get_wiphy(struct ath_softc *sc, const char *name)
{
int i;
if (strcmp(name, wiphy_name(sc->pri_wiphy->hw->wiphy)) == 0)
return sc->pri_wiphy;
for (i = 0; i < sc->num_sec_wiphy; i++) {
struct ath_wiphy *aphy = sc->sec_wiphy[i];
if (aphy && strcmp(name, wiphy_name(aphy->hw->wiphy)) == 0)
return aphy;
}
return NULL;
}
static int del_wiphy(struct ath_softc *sc, const char *name)
{
struct ath_wiphy *aphy = get_wiphy(sc, name);
if (!aphy)
return -ENOENT;
return ath9k_wiphy_del(aphy);
}
static int pause_wiphy(struct ath_softc *sc, const char *name)
{
struct ath_wiphy *aphy = get_wiphy(sc, name);
if (!aphy)
return -ENOENT;
return ath9k_wiphy_pause(aphy);
}
static int unpause_wiphy(struct ath_softc *sc, const char *name)
{
struct ath_wiphy *aphy = get_wiphy(sc, name);
if (!aphy)
return -ENOENT;
return ath9k_wiphy_unpause(aphy);
}
static int select_wiphy(struct ath_softc *sc, const char *name)
{
struct ath_wiphy *aphy = get_wiphy(sc, name);
if (!aphy)
return -ENOENT;
return ath9k_wiphy_select(aphy);
}
static int schedule_wiphy(struct ath_softc *sc, const char *msec)
{
ath9k_wiphy_set_scheduler(sc, simple_strtoul(msec, NULL, 0));
return 0;
}
static ssize_t write_file_wiphy(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
char buf[50];
size_t len;
len = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
buf[len] = '\0';
if (len > 0 && buf[len - 1] == '\n')
buf[len - 1] = '\0';
if (strncmp(buf, "add", 3) == 0) {
int res = ath9k_wiphy_add(sc);
if (res < 0)
return res;
} else if (strncmp(buf, "del=", 4) == 0) {
int res = del_wiphy(sc, buf + 4);
if (res < 0)
return res;
} else if (strncmp(buf, "pause=", 6) == 0) {
int res = pause_wiphy(sc, buf + 6);
if (res < 0)
return res;
} else if (strncmp(buf, "unpause=", 8) == 0) {
int res = unpause_wiphy(sc, buf + 8);
if (res < 0)
return res;
} else if (strncmp(buf, "select=", 7) == 0) {
int res = select_wiphy(sc, buf + 7);
if (res < 0)
return res;
} else if (strncmp(buf, "schedule=", 9) == 0) {
int res = schedule_wiphy(sc, buf + 9);
if (res < 0)
return res;
} else
return -EOPNOTSUPP;
return count;
}
static const struct file_operations fops_wiphy = {
.read = read_file_wiphy,
.write = write_file_wiphy,
.open = ath9k_debugfs_open,
.owner = THIS_MODULE
};
int ath9k_init_debug(struct ath_softc *sc)
{
sc->debug.debug_mask = ath9k_debug;
if (!ath9k_debugfs_root)
return -ENOENT;
sc->debug.debugfs_phy = debugfs_create_dir(wiphy_name(sc->hw->wiphy),
ath9k_debugfs_root);
if (!sc->debug.debugfs_phy)
goto err;
sc->debug.debugfs_dma = debugfs_create_file("dma", S_IRUGO,
sc->debug.debugfs_phy, sc, &fops_dma);
if (!sc->debug.debugfs_dma)
goto err;
sc->debug.debugfs_interrupt = debugfs_create_file("interrupt",
S_IRUGO,
sc->debug.debugfs_phy,
sc, &fops_interrupt);
if (!sc->debug.debugfs_interrupt)
goto err;
sc->debug.debugfs_rcstat = debugfs_create_file("rcstat",
S_IRUGO,
sc->debug.debugfs_phy,
sc, &fops_rcstat);
if (!sc->debug.debugfs_rcstat)
goto err;
sc->debug.debugfs_wiphy = debugfs_create_file(
"wiphy", S_IRUGO | S_IWUSR, sc->debug.debugfs_phy, sc,
&fops_wiphy);
if (!sc->debug.debugfs_wiphy)
goto err;
return 0;
err:
ath9k_exit_debug(sc);
return -ENOMEM;
}
void ath9k_exit_debug(struct ath_softc *sc)
{
debugfs_remove(sc->debug.debugfs_wiphy);
debugfs_remove(sc->debug.debugfs_rcstat);
debugfs_remove(sc->debug.debugfs_interrupt);
debugfs_remove(sc->debug.debugfs_dma);
debugfs_remove(sc->debug.debugfs_phy);
}
int ath9k_debug_create_root(void)
{
ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
if (!ath9k_debugfs_root)
return -ENOENT;
return 0;
}
void ath9k_debug_remove_root(void)
{
debugfs_remove(ath9k_debugfs_root);
ath9k_debugfs_root = NULL;
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) 2008-2009 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 DEBUG_H
#define DEBUG_H
enum ATH_DEBUG {
ATH_DBG_RESET = 0x00000001,
ATH_DBG_QUEUE = 0x00000002,
ATH_DBG_EEPROM = 0x00000004,
ATH_DBG_CALIBRATE = 0x00000008,
ATH_DBG_INTERRUPT = 0x00000010,
ATH_DBG_REGULATORY = 0x00000020,
ATH_DBG_ANI = 0x00000040,
ATH_DBG_XMIT = 0x00000080,
ATH_DBG_BEACON = 0x00000100,
ATH_DBG_CONFIG = 0x00000200,
ATH_DBG_FATAL = 0x00000400,
ATH_DBG_ANY = 0xffffffff
};
#define DBG_DEFAULT (ATH_DBG_FATAL)
#ifdef CONFIG_ATH9K_DEBUG
/**
* struct ath_interrupt_stats - Contains statistics about interrupts
* @total: Total no. of interrupts generated so far
* @rxok: RX with no errors
* @rxeol: RX with no more RXDESC available
* @rxorn: RX FIFO overrun
* @txok: TX completed at the requested rate
* @txurn: TX FIFO underrun
* @mib: MIB regs reaching its threshold
* @rxphyerr: RX with phy errors
* @rx_keycache_miss: RX with key cache misses
* @swba: Software Beacon Alert
* @bmiss: Beacon Miss
* @bnr: Beacon Not Ready
* @cst: Carrier Sense TImeout
* @gtt: Global TX Timeout
* @tim: RX beacon TIM occurrence
* @cabend: RX End of CAB traffic
* @dtimsync: DTIM sync lossage
* @dtim: RX Beacon with DTIM
*/
struct ath_interrupt_stats {
u32 total;
u32 rxok;
u32 rxeol;
u32 rxorn;
u32 txok;
u32 txeol;
u32 txurn;
u32 mib;
u32 rxphyerr;
u32 rx_keycache_miss;
u32 swba;
u32 bmiss;
u32 bnr;
u32 cst;
u32 gtt;
u32 tim;
u32 cabend;
u32 dtimsync;
u32 dtim;
};
struct ath_legacy_rc_stats {
u32 success;
};
struct ath_11n_rc_stats {
u32 success;
u32 retries;
u32 xretries;
u8 per;
};
struct ath_stats {
struct ath_interrupt_stats istats;
struct ath_legacy_rc_stats legacy_rcstats[12]; /* max(11a,11b,11g) */
struct ath_11n_rc_stats n_rcstats[16]; /* 0..15 MCS rates */
};
struct ath9k_debug {
int debug_mask;
struct dentry *debugfs_phy;
struct dentry *debugfs_dma;
struct dentry *debugfs_interrupt;
struct dentry *debugfs_rcstat;
struct dentry *debugfs_wiphy;
struct ath_stats stats;
};
void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...);
int ath9k_init_debug(struct ath_softc *sc);
void ath9k_exit_debug(struct ath_softc *sc);
int ath9k_debug_create_root(void);
void ath9k_debug_remove_root(void);
void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status);
void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb);
void ath_debug_stat_retries(struct ath_softc *sc, int rix,
int xretries, int retries, u8 per);
#else
static inline void DPRINTF(struct ath_softc *sc, int dbg_mask,
const char *fmt, ...)
{
}
static inline int ath9k_init_debug(struct ath_softc *sc)
{
return 0;
}
static inline void ath9k_exit_debug(struct ath_softc *sc)
{
}
static inline int ath9k_debug_create_root(void)
{
return 0;
}
static inline void ath9k_debug_remove_root(void)
{
}
static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
enum ath9k_int status)
{
}
static inline void ath_debug_stat_rc(struct ath_softc *sc,
struct sk_buff *skb)
{
}
static inline void ath_debug_stat_retries(struct ath_softc *sc, int rix,
int xretries, int retries, u8 per)
{
}
#endif /* CONFIG_ATH9K_DEBUG */
#endif /* DEBUG_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,509 @@
/*
* Copyright (c) 2008-2009 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 EEPROM_H
#define EEPROM_H
#include <net/wireless.h>
#define AH_USE_EEPROM 0x1
#ifdef __BIG_ENDIAN
#define AR5416_EEPROM_MAGIC 0x5aa5
#else
#define AR5416_EEPROM_MAGIC 0xa55a
#endif
#define CTRY_DEBUG 0x1ff
#define CTRY_DEFAULT 0
#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 AR5416_EEPROM_MAGIC_OFFSET 0x0
#define AR5416_EEPROM_S 2
#define AR5416_EEPROM_OFFSET 0x2000
#define AR5416_EEPROM_MAX 0xae0
#define AR5416_EEPROM_START_ADDR \
(AR_SREV_9100(ah)) ? 0x1fff1000 : 0x503f1200
#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 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 INCREASE_MAXPOW_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */
#define INCREASE_MAXPOW_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */
/*
* For AR9285 and later chipsets, the following bits are not being programmed
* in EEPROM and so need to be enabled always.
*
* Bit 0: en_fcc_mid
* Bit 1: en_jap_mid
* Bit 2: en_fcc_dfs_ht40
* Bit 3: en_jap_ht40
* Bit 4: en_jap_dfs_ht40
*/
#define AR9285_RDEXT_DEFAULT 0x1F
#define AR_EEPROM_MAC(i) (0x1d+(i))
#define ATH9K_POW_SM(_r, _s) (((_r) & 0x3f) << (_s))
#define FREQ2FBIN(x, y) ((y) ? ((x) - 2300) : (((x) - 4800) / 5))
#define ath9k_hw_use_flash(_ah) (!(_ah->ah_flags & AH_USE_EEPROM))
#define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK)
#define OLC_FOR_AR9280_20_LATER (AR_SREV_9280_20_OR_LATER(ah) && \
ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL))
#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 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_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 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_MINOR_VER_16 0x10
#define AR5416_EEP_MINOR_VER_17 0x11
#define AR5416_EEP_MINOR_VER_19 0x13
#define AR5416_EEP_MINOR_VER_20 0x14
#define AR5416_EEP_MINOR_VER_22 0x16
#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_BCHAN_UNUSED 0xFF
#define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64
#define AR5416_MAX_CHAINS 3
#define AR5416_PWR_TABLE_OFFSET -5
/* Rx gain type values */
#define AR5416_EEP_RXGAIN_23DB_BACKOFF 0
#define AR5416_EEP_RXGAIN_13DB_BACKOFF 1
#define AR5416_EEP_RXGAIN_ORIG 2
/* Tx gain type values */
#define AR5416_EEP_TXGAIN_ORIGINAL 0
#define AR5416_EEP_TXGAIN_HIGH_POWER 1
#define AR5416_EEP4K_START_LOC 64
#define AR5416_EEP4K_NUM_2G_CAL_PIERS 3
#define AR5416_EEP4K_NUM_2G_CCK_TARGET_POWERS 3
#define AR5416_EEP4K_NUM_2G_20_TARGET_POWERS 3
#define AR5416_EEP4K_NUM_2G_40_TARGET_POWERS 3
#define AR5416_EEP4K_NUM_CTLS 12
#define AR5416_EEP4K_NUM_BAND_EDGES 4
#define AR5416_EEP4K_NUM_PD_GAINS 2
#define AR5416_EEP4K_PD_GAINS_IN_MASK 4
#define AR5416_EEP4K_PD_GAIN_ICEPTS 5
#define AR5416_EEP4K_MAX_CHAINS 1
#define AR9280_TX_GAIN_TABLE_SIZE 22
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,
EEP_RXGAIN_TYPE,
EEP_TXGAIN_TYPE,
EEP_OL_PWRCTRL,
EEP_RC_CHAIN_MASK,
EEP_DAC_HPWR_5G,
EEP_FRAC_N_5G
};
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
};
enum ath9k_hal_freq_band {
ATH9K_HAL_FREQ_BAND_5GHZ = 0,
ATH9K_HAL_FREQ_BAND_2GHZ = 1
};
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_1[2];
u8 rxGainType;
u8 dacHiPwrMode_5G;
u8 openLoopPwrCntl;
u8 dacLpMode;
u8 txGainType;
u8 rcChainMask;
u8 desiredScaleCCK;
u8 power_table_offset;
u8 frac_n_5g;
u8 futureBase_3[21];
} __packed;
struct base_eep_header_4k {
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 txGainType;
} __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 miscBits;
u16 xpaBiasLvlFreq[3];
u8 futureModal[6];
struct spur_chan spurChans[AR5416_EEPROM_MODAL_SPURS];
} __packed;
struct calDataPerFreqOpLoop {
u8 pwrPdg[2][5];
u8 vpdPdg[2][5];
u8 pcdac[2][5];
u8 empty[2][5];
} __packed;
struct modal_eep_4k_header {
u32 antCtrlChain[AR5416_EEP4K_MAX_CHAINS];
u32 antCtrlCommon;
u8 antennaGainCh[AR5416_EEP4K_MAX_CHAINS];
u8 switchSettling;
u8 txRxAttenCh[AR5416_EEP4K_MAX_CHAINS];
u8 rxTxMarginCh[AR5416_EEP4K_MAX_CHAINS];
u8 adcDesiredSize;
u8 pgaDesiredSize;
u8 xlnaGainCh[AR5416_EEP4K_MAX_CHAINS];
u8 txEndToXpaOff;
u8 txEndToRxOn;
u8 txFrameToXpaOn;
u8 thresh62;
u8 noiseFloorThreshCh[AR5416_EEP4K_MAX_CHAINS];
u8 xpdGain;
u8 xpd;
u8 iqCalICh[AR5416_EEP4K_MAX_CHAINS];
u8 iqCalQCh[AR5416_EEP4K_MAX_CHAINS];
u8 pdGainOverlap;
u8 ob_01;
u8 db1_01;
u8 xpaBiasLvl;
u8 txFrameToDataStart;
u8 txFrameToPaOn;
u8 ht40PowerIncForPdadc;
u8 bswAtten[AR5416_EEP4K_MAX_CHAINS];
u8 bswMargin[AR5416_EEP4K_MAX_CHAINS];
u8 swSettleHt40;
u8 xatten2Db[AR5416_EEP4K_MAX_CHAINS];
u8 xatten2Margin[AR5416_EEP4K_MAX_CHAINS];
u8 db2_01;
u8 version;
u16 ob_234;
u16 db1_234;
u16 db2_234;
u8 futureModal[4];
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_data_per_freq_4k {
u8 pwrPdg[AR5416_EEP4K_NUM_PD_GAINS][AR5416_EEP4K_PD_GAIN_ICEPTS];
u8 vpdPdg[AR5416_EEP4K_NUM_PD_GAINS][AR5416_EEP4K_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 cal_ctl_data_4k {
struct cal_ctl_edges
ctlEdges[AR5416_EEP4K_MAX_CHAINS][AR5416_EEP4K_NUM_BAND_EDGES];
} __packed;
struct ar5416_eeprom_def {
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 ar5416_eeprom_4k {
struct base_eep_header_4k baseEepHeader;
u8 custData[20];
struct modal_eep_4k_header modalHeader;
u8 calFreqPier2G[AR5416_EEP4K_NUM_2G_CAL_PIERS];
struct cal_data_per_freq_4k
calPierData2G[AR5416_EEP4K_MAX_CHAINS][AR5416_EEP4K_NUM_2G_CAL_PIERS];
struct cal_target_power_leg
calTargetPowerCck[AR5416_EEP4K_NUM_2G_CCK_TARGET_POWERS];
struct cal_target_power_leg
calTargetPower2G[AR5416_EEP4K_NUM_2G_20_TARGET_POWERS];
struct cal_target_power_ht
calTargetPower2GHT20[AR5416_EEP4K_NUM_2G_20_TARGET_POWERS];
struct cal_target_power_ht
calTargetPower2GHT40[AR5416_EEP4K_NUM_2G_40_TARGET_POWERS];
u8 ctlIndex[AR5416_EEP4K_NUM_CTLS];
struct cal_ctl_data_4k ctlData[AR5416_EEP4K_NUM_CTLS];
u8 padding;
} __packed;
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];
};
enum ath9k_eep_map {
EEP_MAP_DEFAULT = 0x0,
EEP_MAP_4KBITS,
EEP_MAP_MAX
};
struct eeprom_ops {
int (*check_eeprom)(struct ath_hw *hw);
u32 (*get_eeprom)(struct ath_hw *hw, enum eeprom_param param);
bool (*fill_eeprom)(struct ath_hw *hw);
int (*get_eeprom_ver)(struct ath_hw *hw);
int (*get_eeprom_rev)(struct ath_hw *hw);
u8 (*get_num_ant_config)(struct ath_hw *hw, enum ieee80211_band band);
u16 (*get_eeprom_antenna_cfg)(struct ath_hw *hw,
struct ath9k_channel *chan);
void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan);
void (*set_addac)(struct ath_hw *hw, struct ath9k_channel *chan);
int (*set_txpower)(struct ath_hw *hw, struct ath9k_channel *chan,
u16 cfgCtl, u8 twiceAntennaReduction,
u8 twiceMaxRegulatoryPower, u8 powerLimit);
u16 (*get_spur_channel)(struct ath_hw *ah, u16 i, bool is2GHz);
};
#define ar5416_get_ntxchains(_txchainmask) \
(((_txchainmask >> 2) & 1) + \
((_txchainmask >> 1) & 1) + (_txchainmask & 1))
int ath9k_hw_eeprom_attach(struct ath_hw *ah);
#endif /* EEPROM_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,631 @@
/*
* Copyright (c) 2008-2009 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>
#include <linux/io.h>
#include "mac.h"
#include "ani.h"
#include "eeprom.h"
#include "calib.h"
#include "reg.h"
#include "phy.h"
#include "../regd.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 AR9285_DEVID_PCIE 0x002b
#define AR5416_AR9100_DEVID 0x000b
#define AR_SUBVENDOR_ID_NOG 0x0e11
#define AR_SUBVENDOR_ID_NEW_A 0x7065
#define AR5416_MAGIC 0x19641014
/* Register read/write primitives */
#define REG_WRITE(_ah, _reg, _val) ath9k_iowrite32((_ah), (_reg), (_val))
#define REG_READ(_ah, _reg) ath9k_ioread32((_ah), (_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 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 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_TX_FRAME 3
#define AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED 5
#define AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED 6
#define AR_GPIOD_MASK 0x00001FFF
#define AR_GPIO_BIT(_gpio) (1 << (_gpio))
#define BASE_ACTIVATE_DELAY 100
#define RTC_PLL_SETTLE_DELAY 1000
#define COEF_SCALE_S 24
#define HT40_CHANNEL_CENTER_SHIFT 10
#define ATH9K_ANTENNA0_CHAINMASK 0x1
#define ATH9K_ANTENNA1_CHAINMASK 0x2
#define ATH9K_NUM_DMA_DEBUG_REGS 8
#define ATH9K_NUM_QUEUES 10
#define MAX_RATE_POWER 63
#define AH_WAIT_TIMEOUT 100000 /* (us) */
#define AH_TIME_QUANTUM 10
#define AR_KEYTABLE_SIZE 128
#define POWER_UP_TIME 200000
#define SPUR_RSSI_THRESH 40
#define CAB_TIMEOUT_VAL 10
#define BEACON_TIMEOUT_VAL 10
#define MIN_BEACON_TIMEOUT_VAL 1
#define SLEEP_SLOP 3
#define INIT_CONFIG_STATUS 0x00000000
#define INIT_RSSI_THR 0x00000700
#define INIT_BCON_CNTRL_REG 0x00000000
#define TU_TO_USEC(_tu) ((_tu) << 10)
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_MIC_AESCCM = BIT(0),
ATH9K_HW_CAP_MIC_CKIP = BIT(1),
ATH9K_HW_CAP_MIC_TKIP = BIT(2),
ATH9K_HW_CAP_CIPHER_AESCCM = BIT(3),
ATH9K_HW_CAP_CIPHER_CKIP = BIT(4),
ATH9K_HW_CAP_CIPHER_TKIP = BIT(5),
ATH9K_HW_CAP_VEOL = BIT(6),
ATH9K_HW_CAP_BSSIDMASK = BIT(7),
ATH9K_HW_CAP_MCAST_KEYSEARCH = BIT(8),
ATH9K_HW_CAP_HT = BIT(9),
ATH9K_HW_CAP_GTT = BIT(10),
ATH9K_HW_CAP_FASTCC = BIT(11),
ATH9K_HW_CAP_RFSILENT = BIT(12),
ATH9K_HW_CAP_CST = BIT(13),
ATH9K_HW_CAP_ENHANCEDPM = BIT(14),
ATH9K_HW_CAP_AUTOSLEEP = BIT(15),
ATH9K_HW_CAP_4KB_SPLITTRANS = BIT(16),
ATH9K_HW_CAP_BT_COEX = BIT(17)
};
enum ath9k_capability_type {
ATH9K_CAP_CIPHER = 0,
ATH9K_CAP_TKIP_MIC,
ATH9K_CAP_TKIP_SPLIT,
ATH9K_CAP_DIVERSITY,
ATH9K_CAP_TXPOW,
ATH9K_CAP_MCAST_KEYSRCH,
ATH9K_CAP_DS
};
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 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_clock_req;
u32 pcie_waen;
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;
u16 diversity_control;
u16 antenna_switch_swap;
int serialize_regmode;
bool 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_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_TSFOOR = 0x04000000,
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_TSFOOR |
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
};
#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 {
struct ieee80211_channel *chan;
u16 channel;
u32 channelFlags;
u32 chanmode;
int32_t CalValid;
bool oneTimeCalsDone;
int8_t iCoff;
int8_t qCoff;
int16_t rawNoiseFloor;
};
#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_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_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)
#define IS_CHAN_A_5MHZ_SPACED(_c) \
((((_c)->channelFlags & CHANNEL_5GHZ) != 0) && \
(((_c)->channel % 20) != 0) && \
(((_c)->channel % 10) != 0))
/* These macros check chanmode and not channelFlags */
#define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B)
#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)))
enum ath9k_power_mode {
ATH9K_PM_AWAKE = 0,
ATH9K_PM_FULL_SLEEP,
ATH9K_PM_NETWORK_SLEEP,
ATH9K_PM_UNDEFINED
};
enum ath9k_ant_setting {
ATH9K_ANT_VARIABLE = 0,
ATH9K_ANT_FIXED_A,
ATH9K_ANT_FIXED_B
};
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,
};
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
#define ATH9K_TSFOOR_THRESHOLD 0x00004240 /* 16k us */
u32 bs_dtimperiod;
u16 bs_cfpperiod;
u16 bs_cfpmaxduration;
u32 bs_cfpnext;
u16 bs_timoffset;
u16 bs_bmissthreshold;
u32 bs_sleepduration;
u32 bs_tsfoor_threshold;
};
struct chan_centers {
u16 synth_center;
u16 ctl_center;
u16 ext_center;
};
enum {
ATH9K_RESET_POWER_ON,
ATH9K_RESET_WARM,
ATH9K_RESET_COLD,
};
struct ath9k_hw_version {
u32 magic;
u16 devid;
u16 subvendorid;
u32 macVersion;
u16 macRev;
u16 phyRev;
u16 analog5GhzRev;
u16 analog2GhzRev;
};
struct ath_hw {
struct ath_softc *ah_sc;
struct ath9k_hw_version hw_version;
struct ath9k_ops_config config;
struct ath9k_hw_capabilities caps;
struct ath_regulatory regulatory;
struct ath9k_channel channels[38];
struct ath9k_channel *curchan;
union {
struct ar5416_eeprom_def def;
struct ar5416_eeprom_4k map4k;
} eeprom;
const struct eeprom_ops *eep_ops;
enum ath9k_eep_map eep_map;
bool sw_mgmt_crypto;
bool is_pciexpress;
u8 macaddr[ETH_ALEN];
u16 tx_trig_level;
u16 rfsilent;
u32 rfkill_gpio;
u32 rfkill_polarity;
u32 btactive_gpio;
u32 wlanactive_gpio;
u32 ah_flags;
enum nl80211_iftype opmode;
enum ath9k_power_mode power_mode;
enum ath9k_power_mode restore_mode;
struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
struct ar5416Stats stats;
struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES];
int16_t curchan_rad_index;
u32 mask_reg;
u32 txok_interrupt_mask;
u32 txerr_interrupt_mask;
u32 txdesc_interrupt_mask;
u32 txeol_interrupt_mask;
u32 txurn_interrupt_mask;
bool chip_fullsleep;
u32 atim_window;
u16 antenna_switch_swap;
enum ath9k_ant_setting diversity_control;
/* Calibration */
enum hal_cal_types supp_cals;
struct hal_cal_list iq_caldata;
struct hal_cal_list adcgain_caldata;
struct hal_cal_list adcdc_calinitdata;
struct hal_cal_list adcdc_caldata;
struct hal_cal_list *cal_list;
struct hal_cal_list *cal_list_last;
struct hal_cal_list *cal_list_curr;
#define totalPowerMeasI meas0.unsign
#define totalPowerMeasQ meas1.unsign
#define totalIqCorrMeas meas2.sign
#define totalAdcIOddPhase meas0.unsign
#define totalAdcIEvenPhase meas1.unsign
#define totalAdcQOddPhase meas2.unsign
#define totalAdcQEvenPhase meas3.unsign
#define totalAdcDcOffsetIOddPhase meas0.sign
#define totalAdcDcOffsetIEvenPhase meas1.sign
#define totalAdcDcOffsetQOddPhase meas2.sign
#define totalAdcDcOffsetQEvenPhase meas3.sign
union {
u32 unsign[AR5416_MAX_CHAINS];
int32_t sign[AR5416_MAX_CHAINS];
} meas0;
union {
u32 unsign[AR5416_MAX_CHAINS];
int32_t sign[AR5416_MAX_CHAINS];
} meas1;
union {
u32 unsign[AR5416_MAX_CHAINS];
int32_t sign[AR5416_MAX_CHAINS];
} meas2;
union {
u32 unsign[AR5416_MAX_CHAINS];
int32_t sign[AR5416_MAX_CHAINS];
} meas3;
u16 cal_samples;
u32 sta_id1_defaults;
u32 misc_mode;
enum {
AUTO_32KHZ,
USE_32KHZ,
DONT_USE_32KHZ,
} enable_32kHz_clock;
/* RF */
u32 *analogBank0Data;
u32 *analogBank1Data;
u32 *analogBank2Data;
u32 *analogBank3Data;
u32 *analogBank6Data;
u32 *analogBank6TPCData;
u32 *analogBank7Data;
u32 *addac5416_21;
u32 *bank6Temp;
int16_t txpower_indexoffset;
u32 beacon_interval;
u32 slottime;
u32 acktimeout;
u32 ctstimeout;
u32 globaltxtimeout;
u8 gbeacon_rate;
/* ANI */
u32 proc_phyerr;
bool has_hw_phycounters;
u32 aniperiod;
struct ar5416AniState *curani;
struct ar5416AniState ani[255];
int totalSizeDesired[5];
int coarse_high[5];
int coarse_low[5];
int firpwr[5];
enum ath9k_ani_cmd ani_function;
u32 intr_txqs;
enum ath9k_ht_extprotspacing extprotspacing;
u8 txchainmask;
u8 rxchainmask;
u32 originalGain[22];
int initPDADC;
int PDADCdelta;
struct ar5416IniArray iniModes;
struct ar5416IniArray iniCommon;
struct ar5416IniArray iniBank0;
struct ar5416IniArray iniBB_RfGain;
struct ar5416IniArray iniBank1;
struct ar5416IniArray iniBank2;
struct ar5416IniArray iniBank3;
struct ar5416IniArray iniBank6;
struct ar5416IniArray iniBank6TPC;
struct ar5416IniArray iniBank7;
struct ar5416IniArray iniAddac;
struct ar5416IniArray iniPcieSerdes;
struct ar5416IniArray iniModesAdditional;
struct ar5416IniArray iniModesRxGain;
struct ar5416IniArray iniModesTxGain;
};
/* Attach, Detach, Reset */
const char *ath9k_hw_probe(u16 vendorid, u16 devid);
void ath9k_hw_detach(struct ath_hw *ah);
struct ath_hw *ath9k_hw_attach(u16 devid, struct ath_softc *sc, int *error);
void ath9k_hw_rfdetach(struct ath_hw *ah);
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
bool bChannelChange);
void ath9k_hw_fill_cap_info(struct ath_hw *ah);
bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type,
u32 capability, u32 *result);
bool ath9k_hw_setcapability(struct ath_hw *ah, enum ath9k_capability_type type,
u32 capability, u32 setting, int *status);
/* Key Cache Management */
bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry);
bool ath9k_hw_keysetmac(struct ath_hw *ah, u16 entry, const u8 *mac);
bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
const struct ath9k_keyval *k,
const u8 *mac);
bool ath9k_hw_keyisvalid(struct ath_hw *ah, u16 entry);
/* GPIO / RFKILL / Antennae */
void ath9k_hw_cfg_gpio_input(struct ath_hw *ah, u32 gpio);
u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio);
void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio,
u32 ah_signal_type);
void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val);
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
void ath9k_enable_rfkill(struct ath_hw *ah);
#endif
u32 ath9k_hw_getdefantenna(struct ath_hw *ah);
void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna);
bool ath9k_hw_setantennaswitch(struct ath_hw *ah,
enum ath9k_ant_setting settings,
struct ath9k_channel *chan,
u8 *tx_chainmask, u8 *rx_chainmask,
u8 *antenna_cfgd);
/* General Operation */
bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout);
u32 ath9k_hw_reverse_bits(u32 val, u32 n);
bool ath9k_get_channel_edges(struct ath_hw *ah, u16 flags, u16 *low, u16 *high);
u16 ath9k_hw_computetxtime(struct ath_hw *ah, struct ath_rate_table *rates,
u32 frameLen, u16 rateix, bool shortPreamble);
void ath9k_hw_get_channel_centers(struct ath_hw *ah,
struct ath9k_channel *chan,
struct chan_centers *centers);
u32 ath9k_hw_getrxfilter(struct ath_hw *ah);
void ath9k_hw_setrxfilter(struct ath_hw *ah, u32 bits);
bool ath9k_hw_phy_disable(struct ath_hw *ah);
bool ath9k_hw_disable(struct ath_hw *ah);
bool ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit);
void ath9k_hw_setmac(struct ath_hw *ah, const u8 *mac);
void ath9k_hw_setopmode(struct ath_hw *ah);
void ath9k_hw_setmcastfilter(struct ath_hw *ah, u32 filter0, u32 filter1);
void ath9k_hw_setbssidmask(struct ath_softc *sc);
void ath9k_hw_write_associd(struct ath_softc *sc);
u64 ath9k_hw_gettsf64(struct ath_hw *ah);
void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64);
void ath9k_hw_reset_tsf(struct ath_hw *ah);
bool ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting);
bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us);
void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode);
void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period);
void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
const struct ath9k_beacon_state *bs);
bool ath9k_hw_setpower(struct ath_hw *ah,
enum ath9k_power_mode mode);
void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore);
/* Interrupt Handling */
bool ath9k_hw_intrpend(struct ath_hw *ah);
bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked);
enum ath9k_int ath9k_hw_intrget(struct ath_hw *ah);
enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints);
void ath9k_hw_btcoex_enable(struct ath_hw *ah);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,976 @@
/*
* Copyright (c) 2008-2009 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 "ath9k.h"
static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah,
struct ath9k_tx_queue_info *qi)
{
DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT,
"tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n",
ah->txok_interrupt_mask, ah->txerr_interrupt_mask,
ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask,
ah->txurn_interrupt_mask);
REG_WRITE(ah, AR_IMR_S0,
SM(ah->txok_interrupt_mask, AR_IMR_S0_QCU_TXOK)
| SM(ah->txdesc_interrupt_mask, AR_IMR_S0_QCU_TXDESC));
REG_WRITE(ah, AR_IMR_S1,
SM(ah->txerr_interrupt_mask, AR_IMR_S1_QCU_TXERR)
| SM(ah->txeol_interrupt_mask, AR_IMR_S1_QCU_TXEOL));
REG_RMW_FIELD(ah, AR_IMR_S2,
AR_IMR_S2_QCU_TXURN, ah->txurn_interrupt_mask);
}
u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q)
{
return REG_READ(ah, AR_QTXDP(q));
}
bool ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp)
{
REG_WRITE(ah, AR_QTXDP(q), txdp);
return true;
}
bool ath9k_hw_txstart(struct ath_hw *ah, u32 q)
{
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Enable TXE on queue: %u\n", q);
REG_WRITE(ah, AR_Q_TXE, 1 << q);
return true;
}
u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q)
{
u32 npend;
npend = REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT;
if (npend == 0) {
if (REG_READ(ah, AR_Q_TXE) & (1 << q))
npend = 1;
}
return npend;
}
bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
{
u32 txcfg, curLevel, newLevel;
enum ath9k_int omask;
if (ah->tx_trig_level >= MAX_TX_FIFO_THRESHOLD)
return false;
omask = ath9k_hw_set_interrupts(ah, ah->mask_reg & ~ATH9K_INT_GLOBAL);
txcfg = REG_READ(ah, AR_TXCFG);
curLevel = MS(txcfg, AR_FTRIG);
newLevel = curLevel;
if (bIncTrigLevel) {
if (curLevel < MAX_TX_FIFO_THRESHOLD)
newLevel++;
} else if (curLevel > MIN_TX_FIFO_THRESHOLD)
newLevel--;
if (newLevel != curLevel)
REG_WRITE(ah, AR_TXCFG,
(txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG));
ath9k_hw_set_interrupts(ah, omask);
ah->tx_trig_level = newLevel;
return newLevel != curLevel;
}
bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q)
{
#define ATH9K_TX_STOP_DMA_TIMEOUT 4000 /* usec */
#define ATH9K_TIME_QUANTUM 100 /* usec */
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath9k_tx_queue_info *qi;
u32 tsfLow, j, wait;
u32 wait_time = ATH9K_TX_STOP_DMA_TIMEOUT / ATH9K_TIME_QUANTUM;
if (q >= pCap->total_queues) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Stopping TX DMA, "
"invalid queue: %u\n", q);
return false;
}
qi = &ah->txq[q];
if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Stopping TX DMA, "
"inactive queue: %u\n", q);
return false;
}
REG_WRITE(ah, AR_Q_TXD, 1 << q);
for (wait = wait_time; wait != 0; wait--) {
if (ath9k_hw_numtxpending(ah, q) == 0)
break;
udelay(ATH9K_TIME_QUANTUM);
}
if (ath9k_hw_numtxpending(ah, q)) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
"%s: Num of pending TX Frames %d on Q %d\n",
__func__, ath9k_hw_numtxpending(ah, q), q);
for (j = 0; j < 2; j++) {
tsfLow = REG_READ(ah, AR_TSF_L32);
REG_WRITE(ah, AR_QUIET2,
SM(10, AR_QUIET2_QUIET_DUR));
REG_WRITE(ah, AR_QUIET_PERIOD, 100);
REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsfLow >> 10);
REG_SET_BIT(ah, AR_TIMER_MODE,
AR_QUIET_TIMER_EN);
if ((REG_READ(ah, AR_TSF_L32) >> 10) == (tsfLow >> 10))
break;
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
"TSF has moved while trying to set "
"quiet time TSF: 0x%08x\n", tsfLow);
}
REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
udelay(200);
REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN);
wait = wait_time;
while (ath9k_hw_numtxpending(ah, q)) {
if ((--wait) == 0) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE,
"Failed to stop TX DMA in 100 "
"msec after killing last frame\n");
break;
}
udelay(ATH9K_TIME_QUANTUM);
}
REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH);
}
REG_WRITE(ah, AR_Q_TXD, 0);
return wait != 0;
#undef ATH9K_TX_STOP_DMA_TIMEOUT
#undef ATH9K_TIME_QUANTUM
}
bool ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 segLen, bool firstSeg,
bool lastSeg, const struct ath_desc *ds0)
{
struct ar5416_desc *ads = AR5416DESC(ds);
if (firstSeg) {
ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore);
} else if (lastSeg) {
ads->ds_ctl0 = 0;
ads->ds_ctl1 = segLen;
ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2;
ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3;
} else {
ads->ds_ctl0 = 0;
ads->ds_ctl1 = segLen | AR_TxMore;
ads->ds_ctl2 = 0;
ads->ds_ctl3 = 0;
}
ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
ads->ds_txstatus2 = ads->ds_txstatus3 = 0;
ads->ds_txstatus4 = ads->ds_txstatus5 = 0;
ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
return true;
}
void ath9k_hw_cleartxdesc(struct ath_hw *ah, struct ath_desc *ds)
{
struct ar5416_desc *ads = AR5416DESC(ds);
ads->ds_txstatus0 = ads->ds_txstatus1 = 0;
ads->ds_txstatus2 = ads->ds_txstatus3 = 0;
ads->ds_txstatus4 = ads->ds_txstatus5 = 0;
ads->ds_txstatus6 = ads->ds_txstatus7 = 0;
ads->ds_txstatus8 = ads->ds_txstatus9 = 0;
}
int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds)
{
struct ar5416_desc *ads = AR5416DESC(ds);
if ((ads->ds_txstatus9 & AR_TxDone) == 0)
return -EINPROGRESS;
ds->ds_txstat.ts_seqnum = MS(ads->ds_txstatus9, AR_SeqNum);
ds->ds_txstat.ts_tstamp = ads->AR_SendTimestamp;
ds->ds_txstat.ts_status = 0;
ds->ds_txstat.ts_flags = 0;
if (ads->ds_txstatus1 & AR_ExcessiveRetries)
ds->ds_txstat.ts_status |= ATH9K_TXERR_XRETRY;
if (ads->ds_txstatus1 & AR_Filtered)
ds->ds_txstat.ts_status |= ATH9K_TXERR_FILT;
if (ads->ds_txstatus1 & AR_FIFOUnderrun) {
ds->ds_txstat.ts_status |= ATH9K_TXERR_FIFO;
ath9k_hw_updatetxtriglevel(ah, true);
}
if (ads->ds_txstatus9 & AR_TxOpExceeded)
ds->ds_txstat.ts_status |= ATH9K_TXERR_XTXOP;
if (ads->ds_txstatus1 & AR_TxTimerExpired)
ds->ds_txstat.ts_status |= ATH9K_TXERR_TIMER_EXPIRED;
if (ads->ds_txstatus1 & AR_DescCfgErr)
ds->ds_txstat.ts_flags |= ATH9K_TX_DESC_CFG_ERR;
if (ads->ds_txstatus1 & AR_TxDataUnderrun) {
ds->ds_txstat.ts_flags |= ATH9K_TX_DATA_UNDERRUN;
ath9k_hw_updatetxtriglevel(ah, true);
}
if (ads->ds_txstatus1 & AR_TxDelimUnderrun) {
ds->ds_txstat.ts_flags |= ATH9K_TX_DELIM_UNDERRUN;
ath9k_hw_updatetxtriglevel(ah, true);
}
if (ads->ds_txstatus0 & AR_TxBaStatus) {
ds->ds_txstat.ts_flags |= ATH9K_TX_BA;
ds->ds_txstat.ba_low = ads->AR_BaBitmapLow;
ds->ds_txstat.ba_high = ads->AR_BaBitmapHigh;
}
ds->ds_txstat.ts_rateindex = MS(ads->ds_txstatus9, AR_FinalTxIdx);
switch (ds->ds_txstat.ts_rateindex) {
case 0:
ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate0);
break;
case 1:
ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate1);
break;
case 2:
ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate2);
break;
case 3:
ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate3);
break;
}
ds->ds_txstat.ts_rssi = MS(ads->ds_txstatus5, AR_TxRSSICombined);
ds->ds_txstat.ts_rssi_ctl0 = MS(ads->ds_txstatus0, AR_TxRSSIAnt00);
ds->ds_txstat.ts_rssi_ctl1 = MS(ads->ds_txstatus0, AR_TxRSSIAnt01);
ds->ds_txstat.ts_rssi_ctl2 = MS(ads->ds_txstatus0, AR_TxRSSIAnt02);
ds->ds_txstat.ts_rssi_ext0 = MS(ads->ds_txstatus5, AR_TxRSSIAnt10);
ds->ds_txstat.ts_rssi_ext1 = MS(ads->ds_txstatus5, AR_TxRSSIAnt11);
ds->ds_txstat.ts_rssi_ext2 = MS(ads->ds_txstatus5, AR_TxRSSIAnt12);
ds->ds_txstat.evm0 = ads->AR_TxEVM0;
ds->ds_txstat.evm1 = ads->AR_TxEVM1;
ds->ds_txstat.evm2 = ads->AR_TxEVM2;
ds->ds_txstat.ts_shortretry = MS(ads->ds_txstatus1, AR_RTSFailCnt);
ds->ds_txstat.ts_longretry = MS(ads->ds_txstatus1, AR_DataFailCnt);
ds->ds_txstat.ts_virtcol = MS(ads->ds_txstatus1, AR_VirtRetryCnt);
ds->ds_txstat.ts_antenna = 0;
return 0;
}
void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 pktLen, enum ath9k_pkt_type type, u32 txPower,
u32 keyIx, enum ath9k_key_type keyType, u32 flags)
{
struct ar5416_desc *ads = AR5416DESC(ds);
txPower += ah->txpower_indexoffset;
if (txPower > 63)
txPower = 63;
ads->ds_ctl0 = (pktLen & AR_FrameLen)
| (flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
| SM(txPower, AR_XmitPower)
| (flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
| (flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0)
| (flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
| (keyIx != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0);
ads->ds_ctl1 =
(keyIx != ATH9K_TXKEYIX_INVALID ? SM(keyIx, AR_DestIdx) : 0)
| SM(type, AR_FrameType)
| (flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0)
| (flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0)
| (flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0);
ads->ds_ctl6 = SM(keyType, AR_EncrType);
if (AR_SREV_9285(ah)) {
ads->ds_ctl8 = 0;
ads->ds_ctl9 = 0;
ads->ds_ctl10 = 0;
ads->ds_ctl11 = 0;
}
}
void ath9k_hw_set11n_ratescenario(struct ath_hw *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)
{
struct ar5416_desc *ads = AR5416DESC(ds);
struct ar5416_desc *last_ads = AR5416DESC(lastds);
u32 ds_ctl0;
if (flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA)) {
ds_ctl0 = ads->ds_ctl0;
if (flags & ATH9K_TXDESC_RTSENA) {
ds_ctl0 &= ~AR_CTSEnable;
ds_ctl0 |= AR_RTSEnable;
} else {
ds_ctl0 &= ~AR_RTSEnable;
ds_ctl0 |= AR_CTSEnable;
}
ads->ds_ctl0 = ds_ctl0;
} else {
ads->ds_ctl0 =
(ads->ds_ctl0 & ~(AR_RTSEnable | AR_CTSEnable));
}
ads->ds_ctl2 = set11nTries(series, 0)
| set11nTries(series, 1)
| set11nTries(series, 2)
| set11nTries(series, 3)
| (durUpdateEn ? AR_DurUpdateEna : 0)
| SM(0, AR_BurstDur);
ads->ds_ctl3 = set11nRate(series, 0)
| set11nRate(series, 1)
| set11nRate(series, 2)
| set11nRate(series, 3);
ads->ds_ctl4 = set11nPktDurRTSCTS(series, 0)
| set11nPktDurRTSCTS(series, 1);
ads->ds_ctl5 = set11nPktDurRTSCTS(series, 2)
| set11nPktDurRTSCTS(series, 3);
ads->ds_ctl7 = set11nRateFlags(series, 0)
| set11nRateFlags(series, 1)
| set11nRateFlags(series, 2)
| set11nRateFlags(series, 3)
| SM(rtsctsRate, AR_RTSCTSRate);
last_ads->ds_ctl2 = ads->ds_ctl2;
last_ads->ds_ctl3 = ads->ds_ctl3;
}
void ath9k_hw_set11n_aggr_first(struct ath_hw *ah, struct ath_desc *ds,
u32 aggrLen)
{
struct ar5416_desc *ads = AR5416DESC(ds);
ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr);
ads->ds_ctl6 &= ~AR_AggrLen;
ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen);
}
void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, struct ath_desc *ds,
u32 numDelims)
{
struct ar5416_desc *ads = AR5416DESC(ds);
unsigned int ctl6;
ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr);
ctl6 = ads->ds_ctl6;
ctl6 &= ~AR_PadDelim;
ctl6 |= SM(numDelims, AR_PadDelim);
ads->ds_ctl6 = ctl6;
}
void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, struct ath_desc *ds)
{
struct ar5416_desc *ads = AR5416DESC(ds);
ads->ds_ctl1 |= AR_IsAggr;
ads->ds_ctl1 &= ~AR_MoreAggr;
ads->ds_ctl6 &= ~AR_PadDelim;
}
void ath9k_hw_clr11n_aggr(struct ath_hw *ah, struct ath_desc *ds)
{
struct ar5416_desc *ads = AR5416DESC(ds);
ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr);
}
void ath9k_hw_set11n_burstduration(struct ath_hw *ah, struct ath_desc *ds,
u32 burstDuration)
{
struct ar5416_desc *ads = AR5416DESC(ds);
ads->ds_ctl2 &= ~AR_BurstDur;
ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur);
}
void ath9k_hw_set11n_virtualmorefrag(struct ath_hw *ah, struct ath_desc *ds,
u32 vmf)
{
struct ar5416_desc *ads = AR5416DESC(ds);
if (vmf)
ads->ds_ctl0 |= AR_VirtMoreFrag;
else
ads->ds_ctl0 &= ~AR_VirtMoreFrag;
}
void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs)
{
*txqs &= ah->intr_txqs;
ah->intr_txqs &= ~(*txqs);
}
bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q,
const struct ath9k_tx_queue_info *qinfo)
{
u32 cw;
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath9k_tx_queue_info *qi;
if (q >= pCap->total_queues) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set TXQ properties, "
"invalid queue: %u\n", q);
return false;
}
qi = &ah->txq[q];
if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set TXQ properties, "
"inactive queue: %u\n", q);
return false;
}
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Set queue properties for: %u\n", q);
qi->tqi_ver = qinfo->tqi_ver;
qi->tqi_subtype = qinfo->tqi_subtype;
qi->tqi_qflags = qinfo->tqi_qflags;
qi->tqi_priority = qinfo->tqi_priority;
if (qinfo->tqi_aifs != ATH9K_TXQ_USEDEFAULT)
qi->tqi_aifs = min(qinfo->tqi_aifs, 255U);
else
qi->tqi_aifs = INIT_AIFS;
if (qinfo->tqi_cwmin != ATH9K_TXQ_USEDEFAULT) {
cw = min(qinfo->tqi_cwmin, 1024U);
qi->tqi_cwmin = 1;
while (qi->tqi_cwmin < cw)
qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1;
} else
qi->tqi_cwmin = qinfo->tqi_cwmin;
if (qinfo->tqi_cwmax != ATH9K_TXQ_USEDEFAULT) {
cw = min(qinfo->tqi_cwmax, 1024U);
qi->tqi_cwmax = 1;
while (qi->tqi_cwmax < cw)
qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1;
} else
qi->tqi_cwmax = INIT_CWMAX;
if (qinfo->tqi_shretry != 0)
qi->tqi_shretry = min((u32) qinfo->tqi_shretry, 15U);
else
qi->tqi_shretry = INIT_SH_RETRY;
if (qinfo->tqi_lgretry != 0)
qi->tqi_lgretry = min((u32) qinfo->tqi_lgretry, 15U);
else
qi->tqi_lgretry = INIT_LG_RETRY;
qi->tqi_cbrPeriod = qinfo->tqi_cbrPeriod;
qi->tqi_cbrOverflowLimit = qinfo->tqi_cbrOverflowLimit;
qi->tqi_burstTime = qinfo->tqi_burstTime;
qi->tqi_readyTime = qinfo->tqi_readyTime;
switch (qinfo->tqi_subtype) {
case ATH9K_WME_UPSD:
if (qi->tqi_type == ATH9K_TX_QUEUE_DATA)
qi->tqi_intFlags = ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS;
break;
default:
break;
}
return true;
}
bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q,
struct ath9k_tx_queue_info *qinfo)
{
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath9k_tx_queue_info *qi;
if (q >= pCap->total_queues) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Get TXQ properties, "
"invalid queue: %u\n", q);
return false;
}
qi = &ah->txq[q];
if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Get TXQ properties, "
"inactive queue: %u\n", q);
return false;
}
qinfo->tqi_qflags = qi->tqi_qflags;
qinfo->tqi_ver = qi->tqi_ver;
qinfo->tqi_subtype = qi->tqi_subtype;
qinfo->tqi_qflags = qi->tqi_qflags;
qinfo->tqi_priority = qi->tqi_priority;
qinfo->tqi_aifs = qi->tqi_aifs;
qinfo->tqi_cwmin = qi->tqi_cwmin;
qinfo->tqi_cwmax = qi->tqi_cwmax;
qinfo->tqi_shretry = qi->tqi_shretry;
qinfo->tqi_lgretry = qi->tqi_lgretry;
qinfo->tqi_cbrPeriod = qi->tqi_cbrPeriod;
qinfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit;
qinfo->tqi_burstTime = qi->tqi_burstTime;
qinfo->tqi_readyTime = qi->tqi_readyTime;
return true;
}
int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type,
const struct ath9k_tx_queue_info *qinfo)
{
struct ath9k_tx_queue_info *qi;
struct ath9k_hw_capabilities *pCap = &ah->caps;
int q;
switch (type) {
case ATH9K_TX_QUEUE_BEACON:
q = pCap->total_queues - 1;
break;
case ATH9K_TX_QUEUE_CAB:
q = pCap->total_queues - 2;
break;
case ATH9K_TX_QUEUE_PSPOLL:
q = 1;
break;
case ATH9K_TX_QUEUE_UAPSD:
q = pCap->total_queues - 3;
break;
case ATH9K_TX_QUEUE_DATA:
for (q = 0; q < pCap->total_queues; q++)
if (ah->txq[q].tqi_type ==
ATH9K_TX_QUEUE_INACTIVE)
break;
if (q == pCap->total_queues) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"No available TX queue\n");
return -1;
}
break;
default:
DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Invalid TX queue type: %u\n",
type);
return -1;
}
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Setup TX queue: %u\n", q);
qi = &ah->txq[q];
if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"TX queue: %u already active\n", q);
return -1;
}
memset(qi, 0, sizeof(struct ath9k_tx_queue_info));
qi->tqi_type = type;
if (qinfo == NULL) {
qi->tqi_qflags =
TXQ_FLAG_TXOKINT_ENABLE
| TXQ_FLAG_TXERRINT_ENABLE
| TXQ_FLAG_TXDESCINT_ENABLE | TXQ_FLAG_TXURNINT_ENABLE;
qi->tqi_aifs = INIT_AIFS;
qi->tqi_cwmin = ATH9K_TXQ_USEDEFAULT;
qi->tqi_cwmax = INIT_CWMAX;
qi->tqi_shretry = INIT_SH_RETRY;
qi->tqi_lgretry = INIT_LG_RETRY;
qi->tqi_physCompBuf = 0;
} else {
qi->tqi_physCompBuf = qinfo->tqi_physCompBuf;
(void) ath9k_hw_set_txq_props(ah, q, qinfo);
}
return q;
}
bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q)
{
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath9k_tx_queue_info *qi;
if (q >= pCap->total_queues) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TXQ, "
"invalid queue: %u\n", q);
return false;
}
qi = &ah->txq[q];
if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TXQ, "
"inactive queue: %u\n", q);
return false;
}
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Release TX queue: %u\n", q);
qi->tqi_type = ATH9K_TX_QUEUE_INACTIVE;
ah->txok_interrupt_mask &= ~(1 << q);
ah->txerr_interrupt_mask &= ~(1 << q);
ah->txdesc_interrupt_mask &= ~(1 << q);
ah->txeol_interrupt_mask &= ~(1 << q);
ah->txurn_interrupt_mask &= ~(1 << q);
ath9k_hw_set_txq_interrupts(ah, qi);
return true;
}
bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
{
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath9k_channel *chan = ah->curchan;
struct ath9k_tx_queue_info *qi;
u32 cwMin, chanCwMin, value;
if (q >= pCap->total_queues) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TXQ, "
"invalid queue: %u\n", q);
return false;
}
qi = &ah->txq[q];
if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) {
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TXQ, "
"inactive queue: %u\n", q);
return true;
}
DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Reset TX queue: %u\n", q);
if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) {
if (chan && IS_CHAN_B(chan))
chanCwMin = INIT_CWMIN_11B;
else
chanCwMin = INIT_CWMIN;
for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1);
} else
cwMin = qi->tqi_cwmin;
REG_WRITE(ah, AR_DLCL_IFS(q),
SM(cwMin, AR_D_LCL_IFS_CWMIN) |
SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX) |
SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS));
REG_WRITE(ah, AR_DRETRY_LIMIT(q),
SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH) |
SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG) |
SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH));
REG_WRITE(ah, AR_QMISC(q), AR_Q_MISC_DCU_EARLY_TERM_REQ);
REG_WRITE(ah, AR_DMISC(q),
AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x2);
if (qi->tqi_cbrPeriod) {
REG_WRITE(ah, AR_QCBRCFG(q),
SM(qi->tqi_cbrPeriod, AR_Q_CBRCFG_INTERVAL) |
SM(qi->tqi_cbrOverflowLimit, AR_Q_CBRCFG_OVF_THRESH));
REG_WRITE(ah, AR_QMISC(q),
REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_FSP_CBR |
(qi->tqi_cbrOverflowLimit ?
AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN : 0));
}
if (qi->tqi_readyTime && (qi->tqi_type != ATH9K_TX_QUEUE_CAB)) {
REG_WRITE(ah, AR_QRDYTIMECFG(q),
SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_DURATION) |
AR_Q_RDYTIMECFG_EN);
}
REG_WRITE(ah, AR_DCHNTIME(q),
SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) |
(qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0));
if (qi->tqi_burstTime
&& (qi->tqi_qflags & TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)) {
REG_WRITE(ah, AR_QMISC(q),
REG_READ(ah, AR_QMISC(q)) |
AR_Q_MISC_RDYTIME_EXP_POLICY);
}
if (qi->tqi_qflags & TXQ_FLAG_BACKOFF_DISABLE) {
REG_WRITE(ah, AR_DMISC(q),
REG_READ(ah, AR_DMISC(q)) |
AR_D_MISC_POST_FR_BKOFF_DIS);
}
if (qi->tqi_qflags & TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) {
REG_WRITE(ah, AR_DMISC(q),
REG_READ(ah, AR_DMISC(q)) |
AR_D_MISC_FRAG_BKOFF_EN);
}
switch (qi->tqi_type) {
case ATH9K_TX_QUEUE_BEACON:
REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q))
| AR_Q_MISC_FSP_DBA_GATED
| AR_Q_MISC_BEACON_USE
| AR_Q_MISC_CBR_INCR_DIS1);
REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q))
| (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL <<
AR_D_MISC_ARB_LOCKOUT_CNTRL_S)
| AR_D_MISC_BEACON_USE
| AR_D_MISC_POST_FR_BKOFF_DIS);
break;
case ATH9K_TX_QUEUE_CAB:
REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q))
| AR_Q_MISC_FSP_DBA_GATED
| AR_Q_MISC_CBR_INCR_DIS1
| AR_Q_MISC_CBR_INCR_DIS0);
value = (qi->tqi_readyTime -
(ah->config.sw_beacon_response_time -
ah->config.dma_beacon_response_time) -
ah->config.additional_swba_backoff) * 1024;
REG_WRITE(ah, AR_QRDYTIMECFG(q),
value | AR_Q_RDYTIMECFG_EN);
REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q))
| (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL <<
AR_D_MISC_ARB_LOCKOUT_CNTRL_S));
break;
case ATH9K_TX_QUEUE_PSPOLL:
REG_WRITE(ah, AR_QMISC(q),
REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_CBR_INCR_DIS1);
break;
case ATH9K_TX_QUEUE_UAPSD:
REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) |
AR_D_MISC_POST_FR_BKOFF_DIS);
break;
default:
break;
}
if (qi->tqi_intFlags & ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS) {
REG_WRITE(ah, AR_DMISC(q),
REG_READ(ah, AR_DMISC(q)) |
SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL,
AR_D_MISC_ARB_LOCKOUT_CNTRL) |
AR_D_MISC_POST_FR_BKOFF_DIS);
}
if (qi->tqi_qflags & TXQ_FLAG_TXOKINT_ENABLE)
ah->txok_interrupt_mask |= 1 << q;
else
ah->txok_interrupt_mask &= ~(1 << q);
if (qi->tqi_qflags & TXQ_FLAG_TXERRINT_ENABLE)
ah->txerr_interrupt_mask |= 1 << q;
else
ah->txerr_interrupt_mask &= ~(1 << q);
if (qi->tqi_qflags & TXQ_FLAG_TXDESCINT_ENABLE)
ah->txdesc_interrupt_mask |= 1 << q;
else
ah->txdesc_interrupt_mask &= ~(1 << q);
if (qi->tqi_qflags & TXQ_FLAG_TXEOLINT_ENABLE)
ah->txeol_interrupt_mask |= 1 << q;
else
ah->txeol_interrupt_mask &= ~(1 << q);
if (qi->tqi_qflags & TXQ_FLAG_TXURNINT_ENABLE)
ah->txurn_interrupt_mask |= 1 << q;
else
ah->txurn_interrupt_mask &= ~(1 << q);
ath9k_hw_set_txq_interrupts(ah, qi);
return true;
}
int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 pa, struct ath_desc *nds, u64 tsf)
{
struct ar5416_desc ads;
struct ar5416_desc *adsp = AR5416DESC(ds);
u32 phyerr;
if ((adsp->ds_rxstatus8 & AR_RxDone) == 0)
return -EINPROGRESS;
ads.u.rx = adsp->u.rx;
ds->ds_rxstat.rs_status = 0;
ds->ds_rxstat.rs_flags = 0;
ds->ds_rxstat.rs_datalen = ads.ds_rxstatus1 & AR_DataLen;
ds->ds_rxstat.rs_tstamp = ads.AR_RcvTimestamp;
ds->ds_rxstat.rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined);
ds->ds_rxstat.rs_rssi_ctl0 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt00);
ds->ds_rxstat.rs_rssi_ctl1 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt01);
ds->ds_rxstat.rs_rssi_ctl2 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt02);
ds->ds_rxstat.rs_rssi_ext0 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt10);
ds->ds_rxstat.rs_rssi_ext1 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt11);
ds->ds_rxstat.rs_rssi_ext2 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt12);
if (ads.ds_rxstatus8 & AR_RxKeyIdxValid)
ds->ds_rxstat.rs_keyix = MS(ads.ds_rxstatus8, AR_KeyIdx);
else
ds->ds_rxstat.rs_keyix = ATH9K_RXKEYIX_INVALID;
ds->ds_rxstat.rs_rate = RXSTATUS_RATE(ah, (&ads));
ds->ds_rxstat.rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0;
ds->ds_rxstat.rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0;
ds->ds_rxstat.rs_moreaggr =
(ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
ds->ds_rxstat.rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna);
ds->ds_rxstat.rs_flags =
(ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0;
ds->ds_rxstat.rs_flags |=
(ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0;
if (ads.ds_rxstatus8 & AR_PreDelimCRCErr)
ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_PRE;
if (ads.ds_rxstatus8 & AR_PostDelimCRCErr)
ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_POST;
if (ads.ds_rxstatus8 & AR_DecryptBusyErr)
ds->ds_rxstat.rs_flags |= ATH9K_RX_DECRYPT_BUSY;
if ((ads.ds_rxstatus8 & AR_RxFrameOK) == 0) {
if (ads.ds_rxstatus8 & AR_CRCErr)
ds->ds_rxstat.rs_status |= ATH9K_RXERR_CRC;
else if (ads.ds_rxstatus8 & AR_PHYErr) {
ds->ds_rxstat.rs_status |= ATH9K_RXERR_PHY;
phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode);
ds->ds_rxstat.rs_phyerr = phyerr;
} else if (ads.ds_rxstatus8 & AR_DecryptCRCErr)
ds->ds_rxstat.rs_status |= ATH9K_RXERR_DECRYPT;
else if (ads.ds_rxstatus8 & AR_MichaelErr)
ds->ds_rxstat.rs_status |= ATH9K_RXERR_MIC;
}
return 0;
}
bool ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 size, u32 flags)
{
struct ar5416_desc *ads = AR5416DESC(ds);
struct ath9k_hw_capabilities *pCap = &ah->caps;
ads->ds_ctl1 = size & AR_BufLen;
if (flags & ATH9K_RXDESC_INTREQ)
ads->ds_ctl1 |= AR_RxIntrReq;
ads->ds_rxstatus8 &= ~AR_RxDone;
if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
memset(&(ads->u), 0, sizeof(ads->u));
return true;
}
bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set)
{
u32 reg;
if (set) {
REG_SET_BIT(ah, AR_DIAG_SW,
(AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
if (!ath9k_hw_wait(ah, AR_OBS_BUS_1, AR_OBS_BUS_1_RX_STATE,
0, AH_WAIT_TIMEOUT)) {
REG_CLR_BIT(ah, AR_DIAG_SW,
(AR_DIAG_RX_DIS |
AR_DIAG_RX_ABORT));
reg = REG_READ(ah, AR_OBS_BUS_1);
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"RX failed to go idle in 10 ms RXSM=0x%x\n", reg);
return false;
}
} else {
REG_CLR_BIT(ah, AR_DIAG_SW,
(AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
}
return true;
}
void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp)
{
REG_WRITE(ah, AR_RXDP, rxdp);
}
void ath9k_hw_rxena(struct ath_hw *ah)
{
REG_WRITE(ah, AR_CR, AR_CR_RXE);
}
void ath9k_hw_startpcureceive(struct ath_hw *ah)
{
ath9k_enable_mib_counters(ah);
ath9k_ani_reset(ah);
REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
}
void ath9k_hw_stoppcurecv(struct ath_hw *ah)
{
REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS);
ath9k_hw_disable_mib_counters(ah);
}
bool ath9k_hw_stopdmarecv(struct ath_hw *ah)
{
#define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */
#define AH_RX_TIME_QUANTUM 100 /* usec */
int i;
REG_WRITE(ah, AR_CR, AR_CR_RXD);
/* Wait for rx enable bit to go low */
for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) {
if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0)
break;
udelay(AH_TIME_QUANTUM);
}
if (i == 0) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"DMA failed to stop in %d ms "
"AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
AH_RX_STOP_DMA_TIMEOUT / 1000,
REG_READ(ah, AR_CR),
REG_READ(ah, AR_DIAG_SW));
return false;
} else {
return true;
}
#undef AH_RX_TIME_QUANTUM
#undef AH_RX_STOP_DMA_TIMEOUT
}

View File

@@ -0,0 +1,680 @@
/*
* Copyright (c) 2008-2009 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 MAC_H
#define MAC_H
#define RXSTATUS_RATE(ah, ads) (AR_SREV_5416_20_OR_LATER(ah) ? \
MS(ads->ds_rxstatus0, AR_RxRate) : \
(ads->ds_rxstatus3 >> 2) & 0xFF)
#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 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
#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 ATH9K_SLOT_TIME_6 6
#define ATH9K_SLOT_TIME_9 9
#define ATH9K_SLOT_TIME_20 20
#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 MIN_TX_FIFO_THRESHOLD 0x1
#define MAX_TX_FIFO_THRESHOLD ((4096 / 64) - 1)
#define INIT_TX_FIFO_THRESHOLD MIN_TX_FIFO_THRESHOLD
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
/* ATH9K_TXDESC_INTREQ forces a tx interrupt to be generated for
* the descriptor its marked on. 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 with this flag. Descriptors should be
* marked periodically 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. When the hardware queue is left too
* large rate control information may also be too out of
* date. An Alternative for this is TX interrupt mitigation
* but this needs more testing. */
#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_TXDESC_CAB 0x0400
#define ATH9K_RXDESC_INTREQ 0x0020
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
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_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001
#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_PHYERR = 0x00000100,
ATH9K_RX_FILTER_MYBEACON = 0x00000200,
ATH9K_RX_FILTER_PSPOLL = 0x00004000,
ATH9K_RX_FILTER_PHYRADAR = 0x00002000,
ATH9K_RX_FILTER_MCAST_BCAST_ALL = 0x00008000,
};
#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;
};
struct ath9k_keyval {
u8 kv_type;
u8 kv_pad;
u16 kv_len;
u8 kv_val[16]; /* TK */
u8 kv_mic[8]; /* Michael MIC key */
u8 kv_txmic[8]; /* Michael MIC TX key (used only if the hardware
* supports both MIC keys in the same key cache entry;
* in that case, kv_mic is the RX key) */
};
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
};
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 ath_hw;
struct ath9k_channel;
struct ath_rate_table;
u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q);
bool ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp);
bool ath9k_hw_txstart(struct ath_hw *ah, u32 q);
u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q);
bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel);
bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q);
bool ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 segLen, bool firstSeg,
bool lastSeg, const struct ath_desc *ds0);
void ath9k_hw_cleartxdesc(struct ath_hw *ah, struct ath_desc *ds);
int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds);
void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 pktLen, enum ath9k_pkt_type type, u32 txPower,
u32 keyIx, enum ath9k_key_type keyType, u32 flags);
void ath9k_hw_set11n_ratescenario(struct ath_hw *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_aggr_first(struct ath_hw *ah, struct ath_desc *ds,
u32 aggrLen);
void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, struct ath_desc *ds,
u32 numDelims);
void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, struct ath_desc *ds);
void ath9k_hw_clr11n_aggr(struct ath_hw *ah, struct ath_desc *ds);
void ath9k_hw_set11n_burstduration(struct ath_hw *ah, struct ath_desc *ds,
u32 burstDuration);
void ath9k_hw_set11n_virtualmorefrag(struct ath_hw *ah, struct ath_desc *ds,
u32 vmf);
void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs);
bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q,
const struct ath9k_tx_queue_info *qinfo);
bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q,
struct ath9k_tx_queue_info *qinfo);
int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type,
const struct ath9k_tx_queue_info *qinfo);
bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q);
bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q);
int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 pa, struct ath_desc *nds, u64 tsf);
bool ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds,
u32 size, u32 flags);
bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set);
void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp);
void ath9k_hw_rxena(struct ath_hw *ah);
void ath9k_hw_startpcureceive(struct ath_hw *ah);
void ath9k_hw_stoppcurecv(struct ath_hw *ah);
bool ath9k_hw_stopdmarecv(struct ath_hw *ah);
#endif /* MAC_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,295 @@
/*
* Copyright (c) 2008-2009 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/nl80211.h>
#include <linux/pci.h>
#include "ath9k.h"
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 */
{ PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
{ 0 }
};
/* return bus cachesize in 4B word units */
static void ath_pci_read_cachesize(struct ath_softc *sc, int *csz)
{
u8 u8tmp;
pci_read_config_byte(to_pci_dev(sc->dev), 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 */
}
static void ath_pci_cleanup(struct ath_softc *sc)
{
struct pci_dev *pdev = to_pci_dev(sc->dev);
pci_iounmap(pdev, sc->mem);
pci_disable_device(pdev);
pci_release_region(pdev, 0);
}
static bool ath_pci_eeprom_read(struct ath_hw *ah, u32 off, u16 *data)
{
(void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S));
if (!ath9k_hw_wait(ah,
AR_EEPROM_STATUS_DATA,
AR_EEPROM_STATUS_DATA_BUSY |
AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0,
AH_WAIT_TIMEOUT)) {
return false;
}
*data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA),
AR_EEPROM_STATUS_DATA_VAL);
return true;
}
static struct ath_bus_ops ath_pci_bus_ops = {
.read_cachesize = ath_pci_read_cachesize,
.cleanup = ath_pci_cleanup,
.eeprom_read = ath_pci_eeprom_read,
};
static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
void __iomem *mem;
struct ath_wiphy *aphy;
struct ath_softc *sc;
struct ieee80211_hw *hw;
u8 csz;
int ret = 0;
struct ath_hw *ah;
if (pci_enable_device(pdev))
return -EIO;
ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
if (ret) {
printk(KERN_ERR "ath9k: 32-bit DMA not available\n");
goto bad;
}
ret = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
if (ret) {
printk(KERN_ERR "ath9k: 32-bit DMA consistent "
"DMA enable failed\n");
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);
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_wiphy) +
sizeof(struct ath_softc), &ath9k_ops);
if (hw == NULL) {
printk(KERN_ERR "ath_pci: no memory for ieee80211_hw\n");
goto bad2;
}
SET_IEEE80211_DEV(hw, &pdev->dev);
pci_set_drvdata(pdev, hw);
aphy = hw->priv;
sc = (struct ath_softc *) (aphy + 1);
aphy->sc = sc;
aphy->hw = hw;
sc->pri_wiphy = aphy;
sc->hw = hw;
sc->dev = &pdev->dev;
sc->mem = mem;
sc->bus_ops = &ath_pci_bus_ops;
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;
}
sc->irq = pdev->irq;
ah = sc->sc_ah;
printk(KERN_INFO
"%s: Atheros AR%s MAC/BB Rev:%x "
"AR%s RF Rev:%x: mem=0x%lx, irq=%d\n",
wiphy_name(hw->wiphy),
ath_mac_bb_name(ah->hw_version.macVersion),
ah->hw_version.macRev,
ath_rf_name((ah->hw_version.analog5GhzRev & AR_RADIO_SREV_MAJOR)),
ah->hw_version.phyRev,
(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_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
ath_cleanup(sc);
}
#ifdef CONFIG_PM
static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
#endif
pci_save_state(pdev);
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
static int ath_pci_resume(struct pci_dev *pdev)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
int err;
err = pci_enable_device(pdev);
if (err)
return err;
pci_restore_state(pdev);
/* Enable LED */
ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
/*
* check the h/w rfkill state on resume
* and start the rfkill poll timer
*/
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
queue_delayed_work(sc->hw->workqueue,
&sc->rf_kill.rfkill_poll, 0);
#endif
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 */
};
int ath_pci_init(void)
{
return pci_register_driver(&ath_pci_driver);
}
void ath_pci_exit(void)
{
pci_unregister_driver(&ath_pci_driver);
}

View File

@@ -0,0 +1,424 @@
/*
* Copyright (c) 2008-2009 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 "ath9k.h"
void
ath9k_hw_write_regs(struct ath_hw *ah, u32 modesIndex, u32 freqIndex,
int regWrites)
{
REG_WRITE_ARRAY(&ah->iniBB_RfGain, freqIndex, regWrites);
}
bool
ath9k_hw_set_channel(struct ath_hw *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_FATAL,
"Invalid channel %u MHz\n", 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_FATAL,
"Invalid channel %u MHz\n", freq);
return false;
}
reg32 =
(channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) |
(1 << 5) | 0x1;
REG_WRITE(ah, AR_PHY(0x37), reg32);
ah->curchan = chan;
ah->curchan_rad_index = -1;
return true;
}
bool
ath9k_hw_ar9280_set_channel(struct ath_hw *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;
switch(ah->eep_ops->get_eeprom(ah, EEP_FRAC_N_5G)) {
case 0:
if ((freq % 20) == 0) {
aModeRefSel = 3;
} else if ((freq % 10) == 0) {
aModeRefSel = 2;
}
if (aModeRefSel)
break;
case 1:
default:
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->curchan = chan;
ah->curchan_rad_index = -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_hw *ah, struct ath9k_channel *chan,
u16 modesIndex)
{
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 = ah->eep_ops->get_eeprom(ah, EEP_MINOR_REV);
RF_BANK_SETUP(ah->analogBank0Data, &ah->iniBank0, 1);
RF_BANK_SETUP(ah->analogBank1Data, &ah->iniBank1, 1);
RF_BANK_SETUP(ah->analogBank2Data, &ah->iniBank2, 1);
RF_BANK_SETUP(ah->analogBank3Data, &ah->iniBank3,
modesIndex);
{
int i;
for (i = 0; i < ah->iniBank6TPC.ia_rows; i++) {
ah->analogBank6Data[i] =
INI_RA(&ah->iniBank6TPC, i, modesIndex);
}
}
if (eepMinorRev >= 2) {
if (IS_CHAN_2GHZ(chan)) {
ob2GHz = ah->eep_ops->get_eeprom(ah, EEP_OB_2);
db2GHz = ah->eep_ops->get_eeprom(ah, EEP_DB_2);
ath9k_phy_modify_rx_buffer(ah->analogBank6Data,
ob2GHz, 3, 197, 0);
ath9k_phy_modify_rx_buffer(ah->analogBank6Data,
db2GHz, 3, 194, 0);
} else {
ob5GHz = ah->eep_ops->get_eeprom(ah, EEP_OB_5);
db5GHz = ah->eep_ops->get_eeprom(ah, EEP_DB_5);
ath9k_phy_modify_rx_buffer(ah->analogBank6Data,
ob5GHz, 3, 203, 0);
ath9k_phy_modify_rx_buffer(ah->analogBank6Data,
db5GHz, 3, 200, 0);
}
}
RF_BANK_SETUP(ah->analogBank7Data, &ah->iniBank7, 1);
REG_WRITE_RF_ARRAY(&ah->iniBank0, ah->analogBank0Data,
regWrites);
REG_WRITE_RF_ARRAY(&ah->iniBank1, ah->analogBank1Data,
regWrites);
REG_WRITE_RF_ARRAY(&ah->iniBank2, ah->analogBank2Data,
regWrites);
REG_WRITE_RF_ARRAY(&ah->iniBank3, ah->analogBank3Data,
regWrites);
REG_WRITE_RF_ARRAY(&ah->iniBank6TPC, ah->analogBank6Data,
regWrites);
REG_WRITE_RF_ARRAY(&ah->iniBank7, ah->analogBank7Data,
regWrites);
return true;
}
void
ath9k_hw_rfdetach(struct ath_hw *ah)
{
if (ah->analogBank0Data != NULL) {
kfree(ah->analogBank0Data);
ah->analogBank0Data = NULL;
}
if (ah->analogBank1Data != NULL) {
kfree(ah->analogBank1Data);
ah->analogBank1Data = NULL;
}
if (ah->analogBank2Data != NULL) {
kfree(ah->analogBank2Data);
ah->analogBank2Data = NULL;
}
if (ah->analogBank3Data != NULL) {
kfree(ah->analogBank3Data);
ah->analogBank3Data = NULL;
}
if (ah->analogBank6Data != NULL) {
kfree(ah->analogBank6Data);
ah->analogBank6Data = NULL;
}
if (ah->analogBank6TPCData != NULL) {
kfree(ah->analogBank6TPCData);
ah->analogBank6TPCData = NULL;
}
if (ah->analogBank7Data != NULL) {
kfree(ah->analogBank7Data);
ah->analogBank7Data = NULL;
}
if (ah->addac5416_21 != NULL) {
kfree(ah->addac5416_21);
ah->addac5416_21 = NULL;
}
if (ah->bank6Temp != NULL) {
kfree(ah->bank6Temp);
ah->bank6Temp = NULL;
}
}
bool ath9k_hw_init_rf(struct ath_hw *ah, int *status)
{
if (!AR_SREV_9280_10_OR_LATER(ah)) {
ah->analogBank0Data =
kzalloc((sizeof(u32) *
ah->iniBank0.ia_rows), GFP_KERNEL);
ah->analogBank1Data =
kzalloc((sizeof(u32) *
ah->iniBank1.ia_rows), GFP_KERNEL);
ah->analogBank2Data =
kzalloc((sizeof(u32) *
ah->iniBank2.ia_rows), GFP_KERNEL);
ah->analogBank3Data =
kzalloc((sizeof(u32) *
ah->iniBank3.ia_rows), GFP_KERNEL);
ah->analogBank6Data =
kzalloc((sizeof(u32) *
ah->iniBank6.ia_rows), GFP_KERNEL);
ah->analogBank6TPCData =
kzalloc((sizeof(u32) *
ah->iniBank6TPC.ia_rows), GFP_KERNEL);
ah->analogBank7Data =
kzalloc((sizeof(u32) *
ah->iniBank7.ia_rows), GFP_KERNEL);
if (ah->analogBank0Data == NULL
|| ah->analogBank1Data == NULL
|| ah->analogBank2Data == NULL
|| ah->analogBank3Data == NULL
|| ah->analogBank6Data == NULL
|| ah->analogBank6TPCData == NULL
|| ah->analogBank7Data == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"Cannot allocate RF banks\n");
*status = -ENOMEM;
return false;
}
ah->addac5416_21 =
kzalloc((sizeof(u32) *
ah->iniAddac.ia_rows *
ah->iniAddac.ia_columns), GFP_KERNEL);
if (ah->addac5416_21 == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"Cannot allocate addac5416_21\n");
*status = -ENOMEM;
return false;
}
ah->bank6Temp =
kzalloc((sizeof(u32) *
ah->iniBank6.ia_rows), GFP_KERNEL);
if (ah->bank6Temp == NULL) {
DPRINTF(ah->ah_sc, ATH_DBG_FATAL,
"Cannot allocate bank6Temp\n");
*status = -ENOMEM;
return false;
}
}
return true;
}
void
ath9k_hw_decrease_chain_power(struct ath_hw *ah, struct ath9k_channel *chan)
{
int i, regWrites = 0;
u32 bank6SelMask;
u32 *bank6Temp = ah->bank6Temp;
switch (ah->diversity_control) {
case ATH9K_ANT_FIXED_A:
bank6SelMask =
(ah->
antenna_switch_swap & ANTSWAP_AB) ? REDUCE_CHAIN_0 :
REDUCE_CHAIN_1;
break;
case ATH9K_ANT_FIXED_B:
bank6SelMask =
(ah->
antenna_switch_swap & ANTSWAP_AB) ? REDUCE_CHAIN_1 :
REDUCE_CHAIN_0;
break;
case ATH9K_ANT_VARIABLE:
return;
break;
default:
return;
break;
}
for (i = 0; i < ah->iniBank6.ia_rows; i++)
bank6Temp[i] = 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(&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
}

View File

@@ -0,0 +1,576 @@
/*
* Copyright (c) 2008-2009 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_hw *ah,
struct ath9k_channel
*chan);
bool ath9k_hw_set_channel(struct ath_hw *ah,
struct ath9k_channel *chan);
void ath9k_hw_write_regs(struct ath_hw *ah, u32 modesIndex,
u32 freqIndex, int regWrites);
bool ath9k_hw_set_rf_regs(struct ath_hw *ah,
struct ath9k_channel *chan,
u16 modesIndex);
void ath9k_hw_decrease_chain_power(struct ath_hw *ah,
struct ath9k_channel *chan);
bool ath9k_hw_init_rf(struct ath_hw *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_FC_ENABLE_DAC_FIFO 0x00000800
#define AR_PHY_TEST2 0x9808
#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_TSTDAC_CONST 0x983c
#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_TX_CTRL_TX_DAC_SCALE_CCK 0x0000000C
#define AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK_S 2
#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_TPCRG1_PD_CAL_ENABLE 0x00400000
#define AR_PHY_TPCRG1_PD_CAL_ENABLE_S 22
#define AR_PHY_TX_PWRCTRL4 0xa264
#define AR_PHY_TX_PWRCTRL_PD_AVG_VALID 0x00000001
#define AR_PHY_TX_PWRCTRL_PD_AVG_VALID_S 0
#define AR_PHY_TX_PWRCTRL_PD_AVG_OUT 0x000001FE
#define AR_PHY_TX_PWRCTRL_PD_AVG_OUT_S 1
#define AR_PHY_TX_PWRCTRL6_0 0xa270
#define AR_PHY_TX_PWRCTRL6_1 0xb270
#define AR_PHY_TX_PWRCTRL_ERR_EST_MODE 0x03000000
#define AR_PHY_TX_PWRCTRL_ERR_EST_MODE_S 24
#define AR_PHY_TX_PWRCTRL7 0xa274
#define AR_PHY_TX_PWRCTRL_INIT_TX_GAIN 0x01F80000
#define AR_PHY_TX_PWRCTRL_INIT_TX_GAIN_S 19
#define AR_PHY_TX_PWRCTRL9 0xa27C
#define AR_PHY_TX_DESIRED_SCALE_CCK 0x00007C00
#define AR_PHY_TX_DESIRED_SCALE_CCK_S 10
#define AR_PHY_TX_GAIN_TBL1 0xa300
#define AR_PHY_TX_GAIN 0x0007F000
#define AR_PHY_TX_GAIN_S 12
#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
/* Carrier leak calibration control, do it after AGC calibration */
#define AR_PHY_CL_CAL_CTL 0xA358
#define AR_PHY_CL_CAL_ENABLE 0x00000002
#define AR_PHY_PARALLEL_CAL_ENABLE 0x00000001
#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]); \
DO_DELAY(regWr); \
} \
} while (0)
#define ATH9K_IS_MIC_ENABLED(ah) \
((ah)->sta_id1_defaults & 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
/*
* Copyright (c) 2004 Sam Leffler, Errno Consulting
* Copyright (c) 2004 Video54 Technologies, Inc.
* Copyright (c) 2008-2009 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
struct ath_softc;
#define ATH_RATE_MAX 30
#define RATE_TABLE_SIZE 64
#define MAX_TX_RATE_PHY 48
/* VALID_ALL - valid for 20/40/Legacy,
* VALID - Legacy only,
* VALID_20 - HT 20 only,
* VALID_40 - HT 40 only */
#define INVALID 0x0
#define VALID 0x1
#define VALID_20 0x2
#define VALID_40 0x4
#define VALID_2040 (VALID_20|VALID_40)
#define VALID_ALL (VALID_2040|VALID)
enum {
WLAN_RC_PHY_OFDM,
WLAN_RC_PHY_CCK,
WLAN_RC_PHY_HT_20_SS,
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)
#define WLAN_RC_CAP_MODE(capflag) (((capflag & WLAN_RC_HT_FLAG) ? \
(capflag & WLAN_RC_40_FLAG) ? VALID_40 : VALID_20 : VALID))
/* 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 & VALID_20) && !(capflag & WLAN_RC_40_FLAG)) || \
((flag & VALID_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)
/**
* struct ath_rate_table - Rate Control table
* @valid: valid for use in rate control
* @valid_single_stream: valid for use in rate control for
* single stream operation
* @phy: CCK/OFDM
* @ratekbps: rate in Kbits per second
* @user_ratekbps: user rate in Kbits per second
* @ratecode: rate that goes into HW descriptors
* @short_preamble: Mask for enabling short preamble in ratecode for CCK
* @dot11rate: value that goes into supported
* rates info element of MLME
* @ctrl_rate: Index of next lower basic rate, used for duration computation
* @max_4ms_framelen: maximum frame length(bytes) for tx duration
* @probe_interval: interval for rate control to probe for other rates
* @rssi_reduce_interval: interval for rate control to reduce rssi
* @initial_ratemax: initial ratemax value
*/
struct ath_rate_table {
int rate_cnt;
struct {
int valid;
int valid_single_stream;
u8 phy;
u32 ratekbps;
u32 user_ratekbps;
u8 ratecode;
u8 short_preamble;
u8 dot11rate;
u8 ctrl_rate;
int8_t rssi_ack_validmin;
int8_t rssi_ack_deltamin;
u8 base_index;
u8 cw40index;
u8 sgi_index;
u8 ht_index;
u32 max_4ms_framelen;
} info[RATE_TABLE_SIZE];
u32 probe_interval;
u32 rssi_reduce_interval;
u8 initial_ratemax;
};
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_rateset {
u8 rs_nrates;
u8 rs_rates[ATH_RATE_MAX];
};
/**
* struct ath_rate_priv - Rate Control priv data
* @state: RC state
* @rssi_last: last ACK rssi
* @rssi_last_lookup: last ACK rssi used for lookup
* @rssi_last_prev: previous last ACK rssi
* @rssi_last_prev2: 2nd previous last ACK rssi
* @rssi_sum_cnt: count of rssi_sum for averaging
* @rssi_sum_rate: rate that we are averaging
* @rssi_sum: running sum of rssi for averaging
* @probe_rate: rate we are probing at
* @rssi_time: msec timestamp for last ack rssi
* @rssi_down_time: msec timestamp for last down step
* @probe_time: msec timestamp for last probe
* @hw_maxretry_pktcnt: num of packets since we got HW max retry error
* @max_valid_rate: maximum number of valid rate
* @per_down_time: msec timestamp for last PER down step
* @valid_phy_ratecnt: valid rate count
* @rate_max_phy: phy index for the max rate
* @probe_interval: interval for ratectrl to probe for other rates
* @prev_data_rix: rate idx of last data frame
* @ht_cap: HT capabilities
* @neg_rates: Negotatied rates
* @neg_ht_rates: Negotiated HT rates
*/
struct ath_rate_priv {
int8_t rssi_last;
int8_t rssi_last_lookup;
int8_t rssi_last_prev;
int8_t rssi_last_prev2;
int32_t rssi_sum_cnt;
int32_t rssi_sum_rate;
int32_t rssi_sum;
u8 rate_table_size;
u8 probe_rate;
u8 hw_maxretry_pktcnt;
u8 max_valid_rate;
u8 valid_rate_index[RATE_TABLE_SIZE];
u8 ht_cap;
u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX];
u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][RATE_TABLE_SIZE];
u8 rate_max_phy;
u32 rssi_time;
u32 rssi_down_time;
u32 probe_time;
u32 per_down_time;
u32 probe_interval;
u32 prev_data_rix;
u32 tx_triglevel_max;
struct ath_tx_ratectrl_state state[RATE_TABLE_SIZE];
struct ath_rateset neg_rates;
struct ath_rateset neg_ht_rates;
struct ath_rate_softc *asc;
};
enum ath9k_internal_frame_type {
ATH9K_NOT_INTERNAL,
ATH9K_INT_PAUSE,
ATH9K_INT_UNPAUSE
};
struct ath_tx_info_priv {
struct ath_wiphy *aphy;
struct ath_tx_status tx;
int n_frames;
int n_bad_frames;
bool update_rc;
enum ath9k_internal_frame_type frame_type;
};
#define ATH_TX_INFO_PRIV(tx_info) \
((struct ath_tx_info_priv *)((tx_info)->rate_driver_data[0]))
void ath_rate_attach(struct ath_softc *sc);
u8 ath_rate_findrateix(struct ath_softc *sc, u8 dot11_rate);
int ath_rate_control_register(void);
void ath_rate_control_unregister(void);
#endif /* RC_H */

View File

@@ -0,0 +1,704 @@
/*
* Copyright (c) 2008-2009 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 "ath9k.h"
static struct ieee80211_hw * ath_get_virt_hw(struct ath_softc *sc,
struct ieee80211_hdr *hdr)
{
struct ieee80211_hw *hw = sc->pri_wiphy->hw;
int i;
spin_lock_bh(&sc->wiphy_lock);
for (i = 0; i < sc->num_sec_wiphy; i++) {
struct ath_wiphy *aphy = sc->sec_wiphy[i];
if (aphy == NULL)
continue;
if (compare_ether_addr(hdr->addr1, aphy->hw->wiphy->perm_addr)
== 0) {
hw = aphy->hw;
break;
}
}
spin_unlock_bh(&sc->wiphy_lock);
return hw;
}
/*
* 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.
*/
static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
{
struct ath_hw *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;
/* virtual addr of the beginning of the buffer. */
skb = bf->bf_mpdu;
ASSERT(skb != NULL);
ds->ds_vdata = skb->data;
/* setup rx descriptors. The rx.bufsize here tells the harware
* how much data it can DMA to us and that we are prepared
* to process */
ath9k_hw_setuprxdesc(ah, ds,
sc->rx.bufsize,
0);
if (sc->rx.rxlink == NULL)
ath9k_hw_putrxbuf(ah, bf->bf_daddr);
else
*sc->rx.rxlink = bf->bf_daddr;
sc->rx.rxlink = &ds->ds_link;
ath9k_hw_rxena(ah);
}
static void ath_setdefantenna(struct ath_softc *sc, u32 antenna)
{
/* XXX block beacon interrupts */
ath9k_hw_setantenna(sc->sc_ah, antenna);
sc->rx.defant = antenna;
sc->rx.rxotherant = 0;
}
/*
* Extend 15-bit time stamp from rx descriptor to
* a full 64-bit TSF using the current h/w TSF.
*/
static 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;
}
static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc, u32 len, gfp_t gfp_mask)
{
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.
*/
/* Note: the kernel can allocate a value greater than
* what we ask it to give us. We really only need 4 KB as that
* is this hardware supports and in fact we need at least 3849
* as that is the MAX AMSDU size this hardware supports.
* Unfortunately this means we may get 8 KB here from the
* kernel... and that is actually what is observed on some
* systems :( */
skb = __dev_alloc_skb(len + sc->cachelsz - 1, gfp_mask);
if (skb != NULL) {
off = ((unsigned long) skb->data) % sc->cachelsz;
if (off != 0)
skb_reserve(skb, sc->cachelsz - off);
} else {
DPRINTF(sc, ATH_DBG_FATAL,
"skbuff alloc of size %u failed\n", len);
return NULL;
}
return skb;
}
/*
* For Decrypt or Demic errors, 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.
*/
static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds,
struct ieee80211_rx_status *rx_status, bool *decrypt_error,
struct ath_softc *sc)
{
struct ieee80211_hdr *hdr;
u8 ratecode;
__le16 fc;
struct ieee80211_hw *hw;
hdr = (struct ieee80211_hdr *)skb->data;
fc = hdr->frame_control;
memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
hw = ath_get_virt_hw(sc, hdr);
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. Enable this if you want to see
* error frames in Monitor mode.
*/
if (sc->sc_ah->opmode != NL80211_IFTYPE_MONITOR)
goto rx_next;
} else if (ds->ds_rxstat.rs_status != 0) {
if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC)
rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY)
goto rx_next;
if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) {
*decrypt_error = true;
} else if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) {
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->flag |= RX_FLAG_MMIC_ERROR;
}
/*
* Reject error frames with the exception of
* decryption and MIC failures. For monitor mode,
* we also ignore the CRC error.
*/
if (sc->sc_ah->opmode == NL80211_IFTYPE_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;
}
}
}
ratecode = ds->ds_rxstat.rs_rate;
if (ratecode & 0x80) {
/* HT rate */
rx_status->flag |= RX_FLAG_HT;
if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040)
rx_status->flag |= RX_FLAG_40MHZ;
if (ds->ds_rxstat.rs_flags & ATH9K_RX_GI)
rx_status->flag |= RX_FLAG_SHORT_GI;
rx_status->rate_idx = ratecode & 0x7f;
} else {
int i = 0, cur_band, n_rates;
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].hw_value ==
ratecode) {
rx_status->rate_idx = i;
break;
}
if (sc->sbands[cur_band].bitrates[i].hw_value_short ==
ratecode) {
rx_status->rate_idx = i;
rx_status->flag |= RX_FLAG_SHORTPRE;
break;
}
}
}
rx_status->mactime = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp);
rx_status->band = hw->conf.channel->band;
rx_status->freq = hw->conf.channel->center_freq;
rx_status->noise = sc->ani.noise_floor;
rx_status->signal = rx_status->noise + ds->ds_rxstat.rs_rssi;
rx_status->antenna = ds->ds_rxstat.rs_antenna;
/* at 45 you will be able to use MCS 15 reliably. A more elaborate
* scheme can be used here but it requires tables of SNR/throughput for
* each possible mode used. */
rx_status->qual = ds->ds_rxstat.rs_rssi * 100 / 45;
/* rssi can be more than 45 though, anything above that
* should be considered at 100% */
if (rx_status->qual > 100)
rx_status->qual = 100;
rx_status->flag |= RX_FLAG_TSFT;
return 1;
rx_next:
return 0;
}
static void ath_opmode_init(struct ath_softc *sc)
{
struct ath_hw *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->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
ath9k_hw_setbssidmask(sc);
/* configure operational mode */
ath9k_hw_setopmode(ah);
/* Handle any link-level address change. */
ath9k_hw_setmac(ah, sc->sc_ah->macaddr);
/* calculate and install multicast filter */
mfilt[0] = mfilt[1] = ~0;
ath9k_hw_setmcastfilter(ah, 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;
spin_lock_init(&sc->rx.rxflushlock);
sc->sc_flags &= ~SC_OP_RXFLUSH;
spin_lock_init(&sc->rx.rxbuflock);
sc->rx.bufsize = roundup(IEEE80211_MAX_MPDU_LEN,
min(sc->cachelsz, (u16)64));
DPRINTF(sc, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n",
sc->cachelsz, sc->rx.bufsize);
/* Initialize rx descriptors */
error = ath_descdma_setup(sc, &sc->rx.rxdma, &sc->rx.rxbuf,
"rx", nbufs, 1);
if (error != 0) {
DPRINTF(sc, ATH_DBG_FATAL,
"failed to allocate rx descriptors: %d\n", error);
goto err;
}
list_for_each_entry(bf, &sc->rx.rxbuf, list) {
skb = ath_rxbuf_alloc(sc, sc->rx.bufsize, GFP_KERNEL);
if (skb == NULL) {
error = -ENOMEM;
goto err;
}
bf->bf_mpdu = skb;
bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
sc->rx.bufsize,
DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(sc->dev,
bf->bf_buf_addr))) {
dev_kfree_skb_any(skb);
bf->bf_mpdu = NULL;
DPRINTF(sc, ATH_DBG_FATAL,
"dma_mapping_error() on RX init\n");
error = -ENOMEM;
goto err;
}
bf->bf_dmacontext = bf->bf_buf_addr;
}
sc->rx.rxlink = NULL;
err:
if (error)
ath_rx_cleanup(sc);
return error;
}
void ath_rx_cleanup(struct ath_softc *sc)
{
struct sk_buff *skb;
struct ath_buf *bf;
list_for_each_entry(bf, &sc->rx.rxbuf, list) {
skb = bf->bf_mpdu;
if (skb) {
dma_unmap_single(sc->dev, bf->bf_buf_addr,
sc->rx.bufsize, DMA_FROM_DEVICE);
dev_kfree_skb(skb);
}
}
if (sc->rx.rxdma.dd_desc_len != 0)
ath_descdma_cleanup(sc, &sc->rx.rxdma, &sc->rx.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_ah->opmode != NL80211_IFTYPE_STATION)
rfilt |= ATH9K_RX_FILTER_PROBEREQ;
/*
* Set promiscuous mode when FIF_PROMISC_IN_BSS is enabled for station
* mode interface or when in monitor mode. AP mode does not need this
* since it receives all in-BSS frames anyway.
*/
if (((sc->sc_ah->opmode != NL80211_IFTYPE_AP) &&
(sc->rx.rxfilter & FIF_PROMISC_IN_BSS)) ||
(sc->sc_ah->opmode == NL80211_IFTYPE_MONITOR))
rfilt |= ATH9K_RX_FILTER_PROM;
if (sc->rx.rxfilter & FIF_CONTROL)
rfilt |= ATH9K_RX_FILTER_CONTROL;
if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) &&
!(sc->rx.rxfilter & FIF_BCN_PRBRESP_PROMISC))
rfilt |= ATH9K_RX_FILTER_MYBEACON;
else
rfilt |= ATH9K_RX_FILTER_BEACON;
/* If in HOSTAP mode, want to enable reception of PSPOLL frames */
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP)
rfilt |= ATH9K_RX_FILTER_PSPOLL;
if (sc->sec_wiphy) {
/* TODO: only needed if more than one BSSID is in use in
* station/adhoc mode */
/* TODO: for older chips, may need to add ATH9K_RX_FILTER_PROM
*/
rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
}
return rfilt;
#undef RX_FILTER_PRESERVE
}
int ath_startrecv(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_buf *bf, *tbf;
spin_lock_bh(&sc->rx.rxbuflock);
if (list_empty(&sc->rx.rxbuf))
goto start_recv;
sc->rx.rxlink = NULL;
list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
ath_rx_buf_link(sc, bf);
}
/* We could have deleted elements so the list may be empty now */
if (list_empty(&sc->rx.rxbuf))
goto start_recv;
bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
ath9k_hw_putrxbuf(ah, bf->bf_daddr);
ath9k_hw_rxena(ah);
start_recv:
spin_unlock_bh(&sc->rx.rxbuflock);
ath_opmode_init(sc);
ath9k_hw_startpcureceive(ah);
return 0;
}
bool ath_stoprecv(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
bool stopped;
ath9k_hw_stoppcurecv(ah);
ath9k_hw_setrxfilter(ah, 0);
stopped = ath9k_hw_stopdmarecv(ah);
sc->rx.rxlink = NULL;
return stopped;
}
void ath_flushrecv(struct ath_softc *sc)
{
spin_lock_bh(&sc->rx.rxflushlock);
sc->sc_flags |= SC_OP_RXFLUSH;
ath_rx_tasklet(sc, 1);
sc->sc_flags &= ~SC_OP_RXFLUSH;
spin_unlock_bh(&sc->rx.rxflushlock);
}
int ath_rx_tasklet(struct ath_softc *sc, int flush)
{
#define PA2DESC(_sc, _pa) \
((struct ath_desc *)((caddr_t)(_sc)->rx.rxdma.dd_desc + \
((_pa) - (_sc)->rx.rxdma.dd_desc_paddr)))
struct ath_buf *bf;
struct ath_desc *ds;
struct sk_buff *skb = NULL, *requeue_skb;
struct ieee80211_rx_status rx_status;
struct ath_hw *ah = sc->sc_ah;
struct ieee80211_hdr *hdr;
int hdrlen, padsize, retval;
bool decrypt_error = false;
u8 keyix;
__le16 fc;
spin_lock_bh(&sc->rx.rxbuflock);
do {
/* If handling rx interrupt and flush is in progress => exit */
if ((sc->sc_flags & SC_OP_RXFLUSH) && (flush == 0))
break;
if (list_empty(&sc->rx.rxbuf)) {
sc->rx.rxlink = NULL;
break;
}
bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
ds = bf->bf_desc;
/*
* 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->rx.rxbuf)) {
sc->rx.rxlink = NULL;
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) {
break;
}
}
skb = bf->bf_mpdu;
if (!skb)
continue;
/*
* Synchronize the DMA transfer with CPU before
* 1. accessing the frame
* 2. requeueing the same buffer to h/w
*/
dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr,
sc->rx.bufsize,
DMA_FROM_DEVICE);
/*
* If we're asked to flush receive queue, directly
* chain it back at the queue without processing it.
*/
if (flush)
goto requeue;
if (!ds->ds_rxstat.rs_datalen)
goto requeue;
/* The status portion of the descriptor could get corrupted. */
if (sc->rx.bufsize < ds->ds_rxstat.rs_datalen)
goto requeue;
if (!ath_rx_prepare(skb, ds, &rx_status, &decrypt_error, sc))
goto requeue;
/* Ensure we always have an skb to requeue once we are done
* processing the current buffer's skb */
requeue_skb = ath_rxbuf_alloc(sc, sc->rx.bufsize, GFP_ATOMIC);
/* If there is no memory we ignore the current RX'd frame,
* tell hardware it can give us a new frame using the old
* skb and put it at the tail of the sc->rx.rxbuf list for
* processing. */
if (!requeue_skb)
goto requeue;
/* Unmap the frame */
dma_unmap_single(sc->dev, bf->bf_buf_addr,
sc->rx.bufsize,
DMA_FROM_DEVICE);
skb_put(skb, ds->ds_rxstat.rs_datalen);
skb->protocol = cpu_to_be16(ETH_P_CONTROL);
/* see if any padding is done by the hw and remove it */
hdr = (struct ieee80211_hdr *)skb->data;
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
fc = hdr->frame_control;
/* The MAC header is padded to have 32-bit boundary if the
* packet payload is non-zero. The general calculation for
* padsize would take into account odd header lengths:
* padsize = (4 - hdrlen % 4) % 4; However, since only
* even-length headers are used, padding can only be 0 or 2
* bytes and we can optimize this a bit. In addition, we must
* not try to remove padding from short control frames that do
* not have payload. */
padsize = hdrlen & 3;
if (padsize && hdrlen >= 24) {
memmove(skb->data + padsize, skb->data, hdrlen);
skb_pull(skb, padsize);
}
keyix = ds->ds_rxstat.rs_keyix;
if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error) {
rx_status.flag |= RX_FLAG_DECRYPTED;
} else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED)
&& !decrypt_error && skb->len >= hdrlen + 4) {
keyix = skb->data[hdrlen + 3] >> 6;
if (test_bit(keyix, sc->keymap))
rx_status.flag |= RX_FLAG_DECRYPTED;
}
if (ah->sw_mgmt_crypto &&
(rx_status.flag & RX_FLAG_DECRYPTED) &&
ieee80211_is_mgmt(hdr->frame_control)) {
/* Use software decrypt for management frames. */
rx_status.flag &= ~RX_FLAG_DECRYPTED;
}
/* Send the frame to mac80211 */
if (hdr->addr1[5] & 0x01) {
int i;
/*
* Deliver broadcast/multicast frames to all suitable
* virtual wiphys.
*/
/* TODO: filter based on channel configuration */
for (i = 0; i < sc->num_sec_wiphy; i++) {
struct ath_wiphy *aphy = sc->sec_wiphy[i];
struct sk_buff *nskb;
if (aphy == NULL)
continue;
nskb = skb_copy(skb, GFP_ATOMIC);
if (nskb)
__ieee80211_rx(aphy->hw, nskb,
&rx_status);
}
__ieee80211_rx(sc->hw, skb, &rx_status);
} else {
/* Deliver unicast frames based on receiver address */
__ieee80211_rx(ath_get_virt_hw(sc, hdr), skb,
&rx_status);
}
/* We will now give hardware our shiny new allocated skb */
bf->bf_mpdu = requeue_skb;
bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data,
sc->rx.bufsize,
DMA_FROM_DEVICE);
if (unlikely(dma_mapping_error(sc->dev,
bf->bf_buf_addr))) {
dev_kfree_skb_any(requeue_skb);
bf->bf_mpdu = NULL;
DPRINTF(sc, ATH_DBG_FATAL,
"dma_mapping_error() on RX\n");
break;
}
bf->bf_dmacontext = bf->bf_buf_addr;
/*
* change the default rx antenna if rx diversity chooses the
* other antenna 3 times in a row.
*/
if (sc->rx.defant != ds->ds_rxstat.rs_antenna) {
if (++sc->rx.rxotherant >= 3)
ath_setdefantenna(sc, ds->ds_rxstat.rs_antenna);
} else {
sc->rx.rxotherant = 0;
}
if (ieee80211_is_beacon(fc) &&
(sc->sc_flags & SC_OP_WAIT_FOR_BEACON)) {
sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON;
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
}
requeue:
list_move_tail(&bf->list, &sc->rx.rxbuf);
ath_rx_buf_link(sc, bf);
} while (1);
spin_unlock_bh(&sc->rx.rxbuflock);
return 0;
#undef PA2DESC
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,662 @@
/*
* Copyright (c) 2008-2009 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 "ath9k.h"
struct ath9k_vif_iter_data {
int count;
u8 *addr;
};
static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
{
struct ath9k_vif_iter_data *iter_data = data;
u8 *nbuf;
nbuf = krealloc(iter_data->addr, (iter_data->count + 1) * ETH_ALEN,
GFP_ATOMIC);
if (nbuf == NULL)
return;
memcpy(nbuf + iter_data->count * ETH_ALEN, mac, ETH_ALEN);
iter_data->addr = nbuf;
iter_data->count++;
}
void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
struct ath9k_vif_iter_data iter_data;
int i, j;
u8 mask[ETH_ALEN];
/*
* Add primary MAC address even if it is not in active use since it
* will be configured to the hardware as the starting point and the
* BSSID mask will need to be changed if another address is active.
*/
iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC);
if (iter_data.addr) {
memcpy(iter_data.addr, sc->sc_ah->macaddr, ETH_ALEN);
iter_data.count = 1;
} else
iter_data.count = 0;
/* Get list of all active MAC addresses */
spin_lock_bh(&sc->wiphy_lock);
ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter,
&iter_data);
for (i = 0; i < sc->num_sec_wiphy; i++) {
if (sc->sec_wiphy[i] == NULL)
continue;
ieee80211_iterate_active_interfaces_atomic(
sc->sec_wiphy[i]->hw, ath9k_vif_iter, &iter_data);
}
spin_unlock_bh(&sc->wiphy_lock);
/* Generate an address mask to cover all active addresses */
memset(mask, 0, ETH_ALEN);
for (i = 0; i < iter_data.count; i++) {
u8 *a1 = iter_data.addr + i * ETH_ALEN;
for (j = i + 1; j < iter_data.count; j++) {
u8 *a2 = iter_data.addr + j * ETH_ALEN;
mask[0] |= a1[0] ^ a2[0];
mask[1] |= a1[1] ^ a2[1];
mask[2] |= a1[2] ^ a2[2];
mask[3] |= a1[3] ^ a2[3];
mask[4] |= a1[4] ^ a2[4];
mask[5] |= a1[5] ^ a2[5];
}
}
kfree(iter_data.addr);
/* Invert the mask and configure hardware */
sc->bssidmask[0] = ~mask[0];
sc->bssidmask[1] = ~mask[1];
sc->bssidmask[2] = ~mask[2];
sc->bssidmask[3] = ~mask[3];
sc->bssidmask[4] = ~mask[4];
sc->bssidmask[5] = ~mask[5];
ath9k_hw_setbssidmask(sc);
}
int ath9k_wiphy_add(struct ath_softc *sc)
{
int i, error;
struct ath_wiphy *aphy;
struct ieee80211_hw *hw;
u8 addr[ETH_ALEN];
hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy), &ath9k_ops);
if (hw == NULL)
return -ENOMEM;
spin_lock_bh(&sc->wiphy_lock);
for (i = 0; i < sc->num_sec_wiphy; i++) {
if (sc->sec_wiphy[i] == NULL)
break;
}
if (i == sc->num_sec_wiphy) {
/* No empty slot available; increase array length */
struct ath_wiphy **n;
n = krealloc(sc->sec_wiphy,
(sc->num_sec_wiphy + 1) *
sizeof(struct ath_wiphy *),
GFP_ATOMIC);
if (n == NULL) {
spin_unlock_bh(&sc->wiphy_lock);
ieee80211_free_hw(hw);
return -ENOMEM;
}
n[i] = NULL;
sc->sec_wiphy = n;
sc->num_sec_wiphy++;
}
SET_IEEE80211_DEV(hw, sc->dev);
aphy = hw->priv;
aphy->sc = sc;
aphy->hw = hw;
sc->sec_wiphy[i] = aphy;
spin_unlock_bh(&sc->wiphy_lock);
memcpy(addr, sc->sc_ah->macaddr, ETH_ALEN);
addr[0] |= 0x02; /* Locally managed address */
/*
* XOR virtual wiphy index into the least significant bits to generate
* a different MAC address for each virtual wiphy.
*/
addr[5] ^= i & 0xff;
addr[4] ^= (i & 0xff00) >> 8;
addr[3] ^= (i & 0xff0000) >> 16;
SET_IEEE80211_PERM_ADDR(hw, addr);
ath_set_hw_capab(sc, hw);
error = ieee80211_register_hw(hw);
if (error == 0) {
/* Make sure wiphy scheduler is started (if enabled) */
ath9k_wiphy_set_scheduler(sc, sc->wiphy_scheduler_int);
}
return error;
}
int ath9k_wiphy_del(struct ath_wiphy *aphy)
{
struct ath_softc *sc = aphy->sc;
int i;
spin_lock_bh(&sc->wiphy_lock);
for (i = 0; i < sc->num_sec_wiphy; i++) {
if (aphy == sc->sec_wiphy[i]) {
sc->sec_wiphy[i] = NULL;
spin_unlock_bh(&sc->wiphy_lock);
ieee80211_unregister_hw(aphy->hw);
ieee80211_free_hw(aphy->hw);
return 0;
}
}
spin_unlock_bh(&sc->wiphy_lock);
return -ENOENT;
}
static int ath9k_send_nullfunc(struct ath_wiphy *aphy,
struct ieee80211_vif *vif, const u8 *bssid,
int ps)
{
struct ath_softc *sc = aphy->sc;
struct ath_tx_control txctl;
struct sk_buff *skb;
struct ieee80211_hdr *hdr;
__le16 fc;
struct ieee80211_tx_info *info;
skb = dev_alloc_skb(24);
if (skb == NULL)
return -ENOMEM;
hdr = (struct ieee80211_hdr *) skb_put(skb, 24);
memset(hdr, 0, 24);
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
IEEE80211_FCTL_TODS);
if (ps)
fc |= cpu_to_le16(IEEE80211_FCTL_PM);
hdr->frame_control = fc;
memcpy(hdr->addr1, bssid, ETH_ALEN);
memcpy(hdr->addr2, aphy->hw->wiphy->perm_addr, ETH_ALEN);
memcpy(hdr->addr3, bssid, ETH_ALEN);
info = IEEE80211_SKB_CB(skb);
memset(info, 0, sizeof(*info));
info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS;
info->control.vif = vif;
info->control.rates[0].idx = 0;
info->control.rates[0].count = 4;
info->control.rates[1].idx = -1;
memset(&txctl, 0, sizeof(struct ath_tx_control));
txctl.txq = &sc->tx.txq[sc->tx.hwq_map[ATH9K_WME_AC_VO]];
txctl.frame_type = ps ? ATH9K_INT_PAUSE : ATH9K_INT_UNPAUSE;
if (ath_tx_start(aphy->hw, skb, &txctl) != 0)
goto exit;
return 0;
exit:
dev_kfree_skb_any(skb);
return -1;
}
static bool __ath9k_wiphy_pausing(struct ath_softc *sc)
{
int i;
if (sc->pri_wiphy->state == ATH_WIPHY_PAUSING)
return true;
for (i = 0; i < sc->num_sec_wiphy; i++) {
if (sc->sec_wiphy[i] &&
sc->sec_wiphy[i]->state == ATH_WIPHY_PAUSING)
return true;
}
return false;
}
static bool ath9k_wiphy_pausing(struct ath_softc *sc)
{
bool ret;
spin_lock_bh(&sc->wiphy_lock);
ret = __ath9k_wiphy_pausing(sc);
spin_unlock_bh(&sc->wiphy_lock);
return ret;
}
static bool __ath9k_wiphy_scanning(struct ath_softc *sc)
{
int i;
if (sc->pri_wiphy->state == ATH_WIPHY_SCAN)
return true;
for (i = 0; i < sc->num_sec_wiphy; i++) {
if (sc->sec_wiphy[i] &&
sc->sec_wiphy[i]->state == ATH_WIPHY_SCAN)
return true;
}
return false;
}
bool ath9k_wiphy_scanning(struct ath_softc *sc)
{
bool ret;
spin_lock_bh(&sc->wiphy_lock);
ret = __ath9k_wiphy_scanning(sc);
spin_unlock_bh(&sc->wiphy_lock);
return ret;
}
static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy);
/* caller must hold wiphy_lock */
static void __ath9k_wiphy_unpause_ch(struct ath_wiphy *aphy)
{
if (aphy == NULL)
return;
if (aphy->chan_idx != aphy->sc->chan_idx)
return; /* wiphy not on the selected channel */
__ath9k_wiphy_unpause(aphy);
}
static void ath9k_wiphy_unpause_channel(struct ath_softc *sc)
{
int i;
spin_lock_bh(&sc->wiphy_lock);
__ath9k_wiphy_unpause_ch(sc->pri_wiphy);
for (i = 0; i < sc->num_sec_wiphy; i++)
__ath9k_wiphy_unpause_ch(sc->sec_wiphy[i]);
spin_unlock_bh(&sc->wiphy_lock);
}
void ath9k_wiphy_chan_work(struct work_struct *work)
{
struct ath_softc *sc = container_of(work, struct ath_softc, chan_work);
struct ath_wiphy *aphy = sc->next_wiphy;
if (aphy == NULL)
return;
/*
* All pending interfaces paused; ready to change
* channels.
*/
/* Change channels */
mutex_lock(&sc->mutex);
/* XXX: remove me eventually */
ath9k_update_ichannel(sc, aphy->hw,
&sc->sc_ah->channels[sc->chan_idx]);
ath_update_chainmask(sc, sc->chan_is_ht);
if (ath_set_channel(sc, aphy->hw,
&sc->sc_ah->channels[sc->chan_idx]) < 0) {
printk(KERN_DEBUG "ath9k: Failed to set channel for new "
"virtual wiphy\n");
mutex_unlock(&sc->mutex);
return;
}
mutex_unlock(&sc->mutex);
ath9k_wiphy_unpause_channel(sc);
}
/*
* ath9k version of ieee80211_tx_status() for TX frames that are generated
* internally in the driver.
*/
void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct ath_wiphy *aphy = hw->priv;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info);
if (tx_info_priv && tx_info_priv->frame_type == ATH9K_INT_PAUSE &&
aphy->state == ATH_WIPHY_PAUSING) {
if (!(info->flags & IEEE80211_TX_STAT_ACK)) {
printk(KERN_DEBUG "ath9k: %s: no ACK for pause "
"frame\n", wiphy_name(hw->wiphy));
/*
* The AP did not reply; ignore this to allow us to
* continue.
*/
}
aphy->state = ATH_WIPHY_PAUSED;
if (!ath9k_wiphy_pausing(aphy->sc)) {
/*
* Drop from tasklet to work to allow mutex for channel
* change.
*/
queue_work(aphy->sc->hw->workqueue,
&aphy->sc->chan_work);
}
}
kfree(tx_info_priv);
tx_info->rate_driver_data[0] = NULL;
dev_kfree_skb(skb);
}
static void ath9k_mark_paused(struct ath_wiphy *aphy)
{
struct ath_softc *sc = aphy->sc;
aphy->state = ATH_WIPHY_PAUSED;
if (!__ath9k_wiphy_pausing(sc))
queue_work(sc->hw->workqueue, &sc->chan_work);
}
static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
{
struct ath_wiphy *aphy = data;
struct ath_vif *avp = (void *) vif->drv_priv;
switch (vif->type) {
case NL80211_IFTYPE_STATION:
if (!vif->bss_conf.assoc) {
ath9k_mark_paused(aphy);
break;
}
/* TODO: could avoid this if already in PS mode */
if (ath9k_send_nullfunc(aphy, vif, avp->bssid, 1)) {
printk(KERN_DEBUG "%s: failed to send PS nullfunc\n",
__func__);
ath9k_mark_paused(aphy);
}
break;
case NL80211_IFTYPE_AP:
/* Beacon transmission is paused by aphy->state change */
ath9k_mark_paused(aphy);
break;
default:
break;
}
}
/* caller must hold wiphy_lock */
static int __ath9k_wiphy_pause(struct ath_wiphy *aphy)
{
ieee80211_stop_queues(aphy->hw);
aphy->state = ATH_WIPHY_PAUSING;
/*
* TODO: handle PAUSING->PAUSED for the case where there are multiple
* active vifs (now we do it on the first vif getting ready; should be
* on the last)
*/
ieee80211_iterate_active_interfaces_atomic(aphy->hw, ath9k_pause_iter,
aphy);
return 0;
}
int ath9k_wiphy_pause(struct ath_wiphy *aphy)
{
int ret;
spin_lock_bh(&aphy->sc->wiphy_lock);
ret = __ath9k_wiphy_pause(aphy);
spin_unlock_bh(&aphy->sc->wiphy_lock);
return ret;
}
static void ath9k_unpause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
{
struct ath_wiphy *aphy = data;
struct ath_vif *avp = (void *) vif->drv_priv;
switch (vif->type) {
case NL80211_IFTYPE_STATION:
if (!vif->bss_conf.assoc)
break;
ath9k_send_nullfunc(aphy, vif, avp->bssid, 0);
break;
case NL80211_IFTYPE_AP:
/* Beacon transmission is re-enabled by aphy->state change */
break;
default:
break;
}
}
/* caller must hold wiphy_lock */
static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy)
{
ieee80211_iterate_active_interfaces_atomic(aphy->hw,
ath9k_unpause_iter, aphy);
aphy->state = ATH_WIPHY_ACTIVE;
ieee80211_wake_queues(aphy->hw);
return 0;
}
int ath9k_wiphy_unpause(struct ath_wiphy *aphy)
{
int ret;
spin_lock_bh(&aphy->sc->wiphy_lock);
ret = __ath9k_wiphy_unpause(aphy);
spin_unlock_bh(&aphy->sc->wiphy_lock);
return ret;
}
static void __ath9k_wiphy_mark_all_paused(struct ath_softc *sc)
{
int i;
if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE)
sc->pri_wiphy->state = ATH_WIPHY_PAUSED;
for (i = 0; i < sc->num_sec_wiphy; i++) {
if (sc->sec_wiphy[i] &&
sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE)
sc->sec_wiphy[i]->state = ATH_WIPHY_PAUSED;
}
}
/* caller must hold wiphy_lock */
static void __ath9k_wiphy_pause_all(struct ath_softc *sc)
{
int i;
if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE)
__ath9k_wiphy_pause(sc->pri_wiphy);
for (i = 0; i < sc->num_sec_wiphy; i++) {
if (sc->sec_wiphy[i] &&
sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE)
__ath9k_wiphy_pause(sc->sec_wiphy[i]);
}
}
int ath9k_wiphy_select(struct ath_wiphy *aphy)
{
struct ath_softc *sc = aphy->sc;
bool now;
spin_lock_bh(&sc->wiphy_lock);
if (__ath9k_wiphy_scanning(sc)) {
/*
* For now, we are using mac80211 sw scan and it expects to
* have full control over channel changes, so avoid wiphy
* scheduling during a scan. This could be optimized if the
* scanning control were moved into the driver.
*/
spin_unlock_bh(&sc->wiphy_lock);
return -EBUSY;
}
if (__ath9k_wiphy_pausing(sc)) {
if (sc->wiphy_select_failures == 0)
sc->wiphy_select_first_fail = jiffies;
sc->wiphy_select_failures++;
if (time_after(jiffies, sc->wiphy_select_first_fail + HZ / 2))
{
printk(KERN_DEBUG "ath9k: Previous wiphy select timed "
"out; disable/enable hw to recover\n");
__ath9k_wiphy_mark_all_paused(sc);
/*
* TODO: this workaround to fix hardware is unlikely to
* be specific to virtual wiphy changes. It can happen
* on normal channel change, too, and as such, this
* should really be made more generic. For example,
* tricker radio disable/enable on GTT interrupt burst
* (say, 10 GTT interrupts received without any TX
* frame being completed)
*/
spin_unlock_bh(&sc->wiphy_lock);
ath_radio_disable(sc);
ath_radio_enable(sc);
queue_work(aphy->sc->hw->workqueue,
&aphy->sc->chan_work);
return -EBUSY; /* previous select still in progress */
}
spin_unlock_bh(&sc->wiphy_lock);
return -EBUSY; /* previous select still in progress */
}
sc->wiphy_select_failures = 0;
/* Store the new channel */
sc->chan_idx = aphy->chan_idx;
sc->chan_is_ht = aphy->chan_is_ht;
sc->next_wiphy = aphy;
__ath9k_wiphy_pause_all(sc);
now = !__ath9k_wiphy_pausing(aphy->sc);
spin_unlock_bh(&sc->wiphy_lock);
if (now) {
/* Ready to request channel change immediately */
queue_work(aphy->sc->hw->workqueue, &aphy->sc->chan_work);
}
/*
* wiphys will be unpaused in ath9k_tx_status() once channel has been
* changed if any wiphy needs time to become paused.
*/
return 0;
}
bool ath9k_wiphy_started(struct ath_softc *sc)
{
int i;
spin_lock_bh(&sc->wiphy_lock);
if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) {
spin_unlock_bh(&sc->wiphy_lock);
return true;
}
for (i = 0; i < sc->num_sec_wiphy; i++) {
if (sc->sec_wiphy[i] &&
sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE) {
spin_unlock_bh(&sc->wiphy_lock);
return true;
}
}
spin_unlock_bh(&sc->wiphy_lock);
return false;
}
static void ath9k_wiphy_pause_chan(struct ath_wiphy *aphy,
struct ath_wiphy *selected)
{
if (selected->state == ATH_WIPHY_SCAN) {
if (aphy == selected)
return;
/*
* Pause all other wiphys for the duration of the scan even if
* they are on the current channel now.
*/
} else if (aphy->chan_idx == selected->chan_idx)
return;
aphy->state = ATH_WIPHY_PAUSED;
ieee80211_stop_queues(aphy->hw);
}
void ath9k_wiphy_pause_all_forced(struct ath_softc *sc,
struct ath_wiphy *selected)
{
int i;
spin_lock_bh(&sc->wiphy_lock);
if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE)
ath9k_wiphy_pause_chan(sc->pri_wiphy, selected);
for (i = 0; i < sc->num_sec_wiphy; i++) {
if (sc->sec_wiphy[i] &&
sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE)
ath9k_wiphy_pause_chan(sc->sec_wiphy[i], selected);
}
spin_unlock_bh(&sc->wiphy_lock);
}
void ath9k_wiphy_work(struct work_struct *work)
{
struct ath_softc *sc = container_of(work, struct ath_softc,
wiphy_work.work);
struct ath_wiphy *aphy = NULL;
bool first = true;
spin_lock_bh(&sc->wiphy_lock);
if (sc->wiphy_scheduler_int == 0) {
/* wiphy scheduler is disabled */
spin_unlock_bh(&sc->wiphy_lock);
return;
}
try_again:
sc->wiphy_scheduler_index++;
while (sc->wiphy_scheduler_index <= sc->num_sec_wiphy) {
aphy = sc->sec_wiphy[sc->wiphy_scheduler_index - 1];
if (aphy && aphy->state != ATH_WIPHY_INACTIVE)
break;
sc->wiphy_scheduler_index++;
aphy = NULL;
}
if (aphy == NULL) {
sc->wiphy_scheduler_index = 0;
if (sc->pri_wiphy->state == ATH_WIPHY_INACTIVE) {
if (first) {
first = false;
goto try_again;
}
/* No wiphy is ready to be scheduled */
} else
aphy = sc->pri_wiphy;
}
spin_unlock_bh(&sc->wiphy_lock);
if (aphy &&
aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN &&
ath9k_wiphy_select(aphy)) {
printk(KERN_DEBUG "ath9k: Failed to schedule virtual wiphy "
"change\n");
}
queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work,
sc->wiphy_scheduler_int);
}
void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int)
{
cancel_delayed_work_sync(&sc->wiphy_work);
sc->wiphy_scheduler_int = msecs_to_jiffies(msec_int);
if (sc->wiphy_scheduler_int)
queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work,
sc->wiphy_scheduler_int);
}

File diff suppressed because it is too large Load Diff