Commit 2111ac0d authored by Bruno Randolf's avatar Bruno Randolf Committed by John W. Linville

ath5k: Adaptive Noise Immunity (ANI) Implementation

This is an Adaptive Noise Imunity (ANI) implementation for ath5k. I have looked
at both ath9k and HAL sources (they are nearly the same), and even though i
have implemented some things differently, the basic algorithm is practically
the same, for now. I hope that this can serve as a clean start to improve the
algorithm later.

This also adds a possibility to manually control ANI settings, right now only
thru a debugfs file:
  * set lowest sensitivity (=highest noise immunity):
	echo sens-low > /sys/kernel/debug/ath5k/phy0/ani
  * set highest sensitivity (=lowest noise immunity):
	echo sens-high > /sys/kernel/debug/ath5k/phy0/ani
  * automatically control immunity (default):
	echo ani-on > /sys/kernel/debug/ath5k/phy0/ani
  * to see the parameters in use and watch them change:
	cat /sys/kernel/debug/ath5k/phy0/ani

Manually setting sensitivity will turn the automatic control off. You can also
control each of the five immunity parameters (noise immunity, spur immunity,
firstep, ofdm weak signal detection, cck weak signal detection) manually thru
the debugfs file.

This is tested on AR5414 and nearly doubles the thruput in a noisy 2GHz band.
Signed-off-by: default avatarBruno Randolf <br1@einfach.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent bc53e512
...@@ -12,5 +12,6 @@ ath5k-y += attach.o ...@@ -12,5 +12,6 @@ ath5k-y += attach.o
ath5k-y += base.o ath5k-y += base.o
ath5k-y += led.o ath5k-y += led.o
ath5k-y += rfkill.o ath5k-y += rfkill.o
ath5k-y += ani.o
ath5k-$(CONFIG_ATH5K_DEBUG) += debug.o ath5k-$(CONFIG_ATH5K_DEBUG) += debug.o
obj-$(CONFIG_ATH5K) += ath5k.o obj-$(CONFIG_ATH5K) += ath5k.o
This diff is collapsed.
/*
* Copyright (C) 2010 Bruno Randolf <br1@einfach.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.
*/
#ifndef ANI_H
#define ANI_H
/* these thresholds are relative to the ATH5K_ANI_LISTEN_PERIOD */
#define ATH5K_ANI_LISTEN_PERIOD 100
#define ATH5K_ANI_OFDM_TRIG_HIGH 500
#define ATH5K_ANI_OFDM_TRIG_LOW 200
#define ATH5K_ANI_CCK_TRIG_HIGH 200
#define ATH5K_ANI_CCK_TRIG_LOW 100
/* average beacon RSSI thresholds */
#define ATH5K_ANI_RSSI_THR_HIGH 40
#define ATH5K_ANI_RSSI_THR_LOW 7
/* maximum availabe levels */
#define ATH5K_ANI_MAX_FIRSTEP_LVL 2
#define ATH5K_ANI_MAX_NOISE_IMM_LVL 1
/**
* enum ath5k_ani_mode - mode for ANI / noise sensitivity
*
* @ATH5K_ANI_MODE_OFF: Turn ANI off. This can be useful to just stop the ANI
* algorithm after it has been on auto mode.
* ATH5K_ANI_MODE_MANUAL_LOW: Manually set all immunity parameters to low,
* maximizing sensitivity. ANI will not run.
* ATH5K_ANI_MODE_MANUAL_HIGH: Manually set all immunity parameters to high,
* minimizing sensitivity. ANI will not run.
* ATH5K_ANI_MODE_AUTO: Automatically control immunity parameters based on the
* amount of OFDM and CCK frame errors (default).
*/
enum ath5k_ani_mode {
ATH5K_ANI_MODE_OFF = 0,
ATH5K_ANI_MODE_MANUAL_LOW = 1,
ATH5K_ANI_MODE_MANUAL_HIGH = 2,
ATH5K_ANI_MODE_AUTO = 3
};
/**
* struct ath5k_ani_state - ANI state and associated counters
*
* @max_spur_level: the maximum spur level is chip dependent
*/
struct ath5k_ani_state {
enum ath5k_ani_mode ani_mode;
/* state */
int noise_imm_level;
int spur_level;
int firstep_level;
bool ofdm_weak_sig;
bool cck_weak_sig;
int max_spur_level;
/* used by the algorithm */
unsigned int listen_time;
unsigned int ofdm_errors;
unsigned int cck_errors;
/* debug/statistics only: numbers from last ANI calibration */
unsigned int pfc_tx;
unsigned int pfc_rx;
unsigned int pfc_busy;
unsigned int pfc_cycles;
unsigned int last_listen;
unsigned int last_ofdm_errors;
unsigned int last_cck_errors;
unsigned int sum_ofdm_errors;
unsigned int sum_cck_errors;
};
void ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode);
void ath5k_ani_mib_intr(struct ath5k_hw *ah);
void ath5k_ani_calibration(struct ath5k_hw *ah);
void ath5k_ani_phy_error_report(struct ath5k_hw *ah,
enum ath5k_phy_error_code phyerr);
/* for manual control */
void ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level);
void ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level);
void ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level);
void ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on);
void ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on);
void ath5k_ani_print_counters(struct ath5k_hw *ah);
#endif /* ANI_H */
...@@ -203,6 +203,7 @@ ...@@ -203,6 +203,7 @@
#define AR5K_TUNE_DEFAULT_TXPOWER 25 #define AR5K_TUNE_DEFAULT_TXPOWER 25
#define AR5K_TUNE_TPC_TXPOWER false #define AR5K_TUNE_TPC_TXPOWER false
#define ATH5K_TUNE_CALIBRATION_INTERVAL_FULL 10000 /* 10 sec */ #define ATH5K_TUNE_CALIBRATION_INTERVAL_FULL 10000 /* 10 sec */
#define ATH5K_TUNE_CALIBRATION_INTERVAL_ANI 1000 /* 1 sec */
#define AR5K_INIT_CARR_SENSE_EN 1 #define AR5K_INIT_CARR_SENSE_EN 1
...@@ -800,9 +801,9 @@ struct ath5k_athchan_2ghz { ...@@ -800,9 +801,9 @@ struct ath5k_athchan_2ghz {
* @AR5K_INT_TXURN: received when we should increase the TX trigger threshold * @AR5K_INT_TXURN: received when we should increase the TX trigger threshold
* We currently do increments on interrupt by * We currently do increments on interrupt by
* (AR5K_TUNE_MAX_TX_FIFO_THRES - current_trigger_level) / 2 * (AR5K_TUNE_MAX_TX_FIFO_THRES - current_trigger_level) / 2
* @AR5K_INT_MIB: Indicates the Management Information Base counters should be * @AR5K_INT_MIB: Indicates the either Management Information Base counters or
* checked. We should do this with ath5k_hw_update_mib_counters() but * one of the PHY error counters reached the maximum value and should be
* it seems we should also then do some noise immunity work. * read and cleared.
* @AR5K_INT_RXPHY: RX PHY Error * @AR5K_INT_RXPHY: RX PHY Error
* @AR5K_INT_RXKCM: RX Key cache miss * @AR5K_INT_RXKCM: RX Key cache miss
* @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a * @AR5K_INT_SWBA: SoftWare Beacon Alert - indicates its time to send a
...@@ -894,6 +895,7 @@ enum ath5k_int { ...@@ -894,6 +895,7 @@ enum ath5k_int {
enum ath5k_calibration_mask { enum ath5k_calibration_mask {
AR5K_CALIBRATION_FULL = 0x01, AR5K_CALIBRATION_FULL = 0x01,
AR5K_CALIBRATION_SHORT = 0x02, AR5K_CALIBRATION_SHORT = 0x02,
AR5K_CALIBRATION_ANI = 0x04,
}; };
/* /*
...@@ -1115,6 +1117,7 @@ struct ath5k_hw { ...@@ -1115,6 +1117,7 @@ struct ath5k_hw {
/* Calibration timestamp */ /* Calibration timestamp */
unsigned long ah_cal_next_full; unsigned long ah_cal_next_full;
unsigned long ah_cal_next_ani;
/* Calibration mask */ /* Calibration mask */
u8 ah_cal_mask; u8 ah_cal_mask;
......
...@@ -124,6 +124,7 @@ int ath5k_hw_attach(struct ath5k_softc *sc) ...@@ -124,6 +124,7 @@ int ath5k_hw_attach(struct ath5k_softc *sc)
ah->ah_software_retry = false; ah->ah_software_retry = false;
ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT; ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT;
ah->ah_noise_floor = -95; /* until first NF calibration is run */ ah->ah_noise_floor = -95; /* until first NF calibration is run */
sc->ani_state.ani_mode = ATH5K_ANI_MODE_AUTO;
/* /*
* Find the mac version * Find the mac version
......
...@@ -58,6 +58,7 @@ ...@@ -58,6 +58,7 @@
#include "base.h" #include "base.h"
#include "reg.h" #include "reg.h"
#include "debug.h" #include "debug.h"
#include "ani.h"
static int modparam_nohwcrypt; static int modparam_nohwcrypt;
module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
...@@ -363,6 +364,7 @@ static void ath5k_beacon_send(struct ath5k_softc *sc); ...@@ -363,6 +364,7 @@ static void ath5k_beacon_send(struct ath5k_softc *sc);
static void ath5k_beacon_config(struct ath5k_softc *sc); static void ath5k_beacon_config(struct ath5k_softc *sc);
static void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf); static void ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf);
static void ath5k_tasklet_beacon(unsigned long data); static void ath5k_tasklet_beacon(unsigned long data);
static void ath5k_tasklet_ani(unsigned long data);
static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
{ {
...@@ -828,6 +830,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) ...@@ -828,6 +830,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc); tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc);
tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc); tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc);
tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc); tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc);
tasklet_init(&sc->ani_tasklet, ath5k_tasklet_ani, (unsigned long)sc);
ret = ath5k_eeprom_read_mac(ah, mac); ret = ath5k_eeprom_read_mac(ah, mac);
if (ret) { if (ret) {
...@@ -2530,7 +2533,8 @@ ath5k_init(struct ath5k_softc *sc) ...@@ -2530,7 +2533,8 @@ ath5k_init(struct ath5k_softc *sc)
sc->curband = &sc->sbands[sc->curchan->band]; sc->curband = &sc->sbands[sc->curchan->band];
sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
AR5K_INT_FATAL | AR5K_INT_GLOBAL; AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
ret = ath5k_reset(sc, NULL); ret = ath5k_reset(sc, NULL);
if (ret) if (ret)
goto done; goto done;
...@@ -2642,6 +2646,7 @@ ath5k_stop_hw(struct ath5k_softc *sc) ...@@ -2642,6 +2646,7 @@ ath5k_stop_hw(struct ath5k_softc *sc)
tasklet_kill(&sc->restq); tasklet_kill(&sc->restq);
tasklet_kill(&sc->calib); tasklet_kill(&sc->calib);
tasklet_kill(&sc->beacontq); tasklet_kill(&sc->beacontq);
tasklet_kill(&sc->ani_tasklet);
ath5k_rfkill_hw_stop(sc->ah); ath5k_rfkill_hw_stop(sc->ah);
...@@ -2651,7 +2656,14 @@ ath5k_stop_hw(struct ath5k_softc *sc) ...@@ -2651,7 +2656,14 @@ ath5k_stop_hw(struct ath5k_softc *sc)
static void static void
ath5k_intr_calibration_poll(struct ath5k_hw *ah) ath5k_intr_calibration_poll(struct ath5k_hw *ah)
{ {
if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) { if (time_is_before_eq_jiffies(ah->ah_cal_next_ani) &&
!(ah->ah_cal_mask & AR5K_CALIBRATION_FULL)) {
/* run ANI only when full calibration is not active */
ah->ah_cal_next_ani = jiffies +
msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_ANI);
tasklet_schedule(&ah->ah_sc->ani_tasklet);
} else if (time_is_before_eq_jiffies(ah->ah_cal_next_full)) {
ah->ah_cal_next_full = jiffies + ah->ah_cal_next_full = jiffies +
msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL); msecs_to_jiffies(ATH5K_TUNE_CALIBRATION_INTERVAL_FULL);
tasklet_schedule(&ah->ah_sc->calib); tasklet_schedule(&ah->ah_sc->calib);
...@@ -2710,7 +2722,9 @@ ath5k_intr(int irq, void *dev_id) ...@@ -2710,7 +2722,9 @@ ath5k_intr(int irq, void *dev_id)
/* TODO */ /* TODO */
} }
if (status & AR5K_INT_MIB) { if (status & AR5K_INT_MIB) {
sc->stats.mib_intr++;
ath5k_hw_update_mib_counters(ah); ath5k_hw_update_mib_counters(ah);
ath5k_ani_mib_intr(ah);
} }
if (status & AR5K_INT_GPIO) if (status & AR5K_INT_GPIO)
tasklet_schedule(&sc->rf_kill.toggleq); tasklet_schedule(&sc->rf_kill.toggleq);
...@@ -2775,6 +2789,18 @@ ath5k_tasklet_calibrate(unsigned long data) ...@@ -2775,6 +2789,18 @@ ath5k_tasklet_calibrate(unsigned long data)
} }
static void
ath5k_tasklet_ani(unsigned long data)
{
struct ath5k_softc *sc = (void *)data;
struct ath5k_hw *ah = sc->ah;
ah->ah_cal_mask |= AR5K_CALIBRATION_ANI;
ath5k_ani_calibration(ah);
ah->ah_cal_mask &= ~AR5K_CALIBRATION_ANI;
}
/********************\ /********************\
* Mac80211 functions * * Mac80211 functions *
\********************/ \********************/
...@@ -2874,6 +2900,8 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan) ...@@ -2874,6 +2900,8 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan)
goto err; goto err;
} }
ath5k_ani_init(ah, ah->ah_sc->ani_state.ani_mode);
/* /*
* Change channels and update the h/w rate map if we're switching; * Change channels and update the h/w rate map if we're switching;
* e.g. 11a to 11b/g. * e.g. 11a to 11b/g.
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include "ath5k.h" #include "ath5k.h"
#include "debug.h" #include "debug.h"
#include "ani.h"
#include "../regd.h" #include "../regd.h"
#include "../ath.h" #include "../ath.h"
...@@ -132,6 +133,8 @@ struct ath5k_statistics { ...@@ -132,6 +133,8 @@ struct ath5k_statistics {
unsigned int rts_ok; unsigned int rts_ok;
unsigned int fcs_error; unsigned int fcs_error;
unsigned int beacons; unsigned int beacons;
unsigned int mib_intr;
}; };
#if CHAN_DEBUG #if CHAN_DEBUG
...@@ -221,6 +224,9 @@ struct ath5k_softc { ...@@ -221,6 +224,9 @@ struct ath5k_softc {
bool enable_beacon; /* true if beacons are on */ bool enable_beacon; /* true if beacons are on */
struct ath5k_statistics stats; struct ath5k_statistics stats;
struct ath5k_ani_state ani_state;
struct tasklet_struct ani_tasklet; /* ANI calibration */
}; };
#define ath5k_hw_hasbssidmask(_ah) \ #define ath5k_hw_hasbssidmask(_ah) \
......
...@@ -69,6 +69,7 @@ module_param_named(debug, ath5k_debug, uint, 0); ...@@ -69,6 +69,7 @@ module_param_named(debug, ath5k_debug, uint, 0);
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include "reg.h" #include "reg.h"
#include "ani.h"
static struct dentry *ath5k_global_debugfs; static struct dentry *ath5k_global_debugfs;
...@@ -307,6 +308,7 @@ static const struct { ...@@ -307,6 +308,7 @@ static const struct {
{ ATH5K_DEBUG_DUMP_TX, "dumptx", "print transmit skb content" }, { ATH5K_DEBUG_DUMP_TX, "dumptx", "print transmit skb content" },
{ ATH5K_DEBUG_DUMPBANDS, "dumpbands", "dump bands" }, { ATH5K_DEBUG_DUMPBANDS, "dumpbands", "dump bands" },
{ ATH5K_DEBUG_TRACE, "trace", "trace function calls" }, { ATH5K_DEBUG_TRACE, "trace", "trace function calls" },
{ ATH5K_DEBUG_ANI, "ani", "adaptive noise immunity" },
{ ATH5K_DEBUG_ANY, "all", "show all debug levels" }, { ATH5K_DEBUG_ANY, "all", "show all debug levels" },
}; };
...@@ -573,6 +575,160 @@ static const struct file_operations fops_frameerrors = { ...@@ -573,6 +575,160 @@ static const struct file_operations fops_frameerrors = {
}; };
/* debugfs: ani */
static ssize_t read_file_ani(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
struct ath5k_statistics *st = &sc->stats;
struct ath5k_ani_state *as = &sc->ani_state;
char buf[700];
unsigned int len = 0;
len += snprintf(buf+len, sizeof(buf)-len,
"HW has PHY error counters:\t%s\n",
sc->ah->ah_capabilities.cap_has_phyerr_counters ?
"yes" : "no");
len += snprintf(buf+len, sizeof(buf)-len,
"HW max spur immunity level:\t%d\n",
as->max_spur_level);
len += snprintf(buf+len, sizeof(buf)-len,
"\nANI state\n--------------------------------------------\n");
len += snprintf(buf+len, sizeof(buf)-len, "operating mode:\t\t\t");
switch (as->ani_mode) {
case ATH5K_ANI_MODE_OFF:
len += snprintf(buf+len, sizeof(buf)-len, "OFF\n");
break;
case ATH5K_ANI_MODE_MANUAL_LOW:
len += snprintf(buf+len, sizeof(buf)-len,
"MANUAL LOW\n");
break;
case ATH5K_ANI_MODE_MANUAL_HIGH:
len += snprintf(buf+len, sizeof(buf)-len,
"MANUAL HIGH\n");
break;
case ATH5K_ANI_MODE_AUTO:
len += snprintf(buf+len, sizeof(buf)-len, "AUTO\n");
break;
default:
len += snprintf(buf+len, sizeof(buf)-len,
"??? (not good)\n");
break;
}
len += snprintf(buf+len, sizeof(buf)-len,
"noise immunity level:\t\t%d\n",
as->noise_imm_level);
len += snprintf(buf+len, sizeof(buf)-len,
"spur immunity level:\t\t%d\n",
as->spur_level);
len += snprintf(buf+len, sizeof(buf)-len, "firstep level:\t\t\t%d\n",
as->firstep_level);
len += snprintf(buf+len, sizeof(buf)-len,
"OFDM weak signal detection:\t%s\n",
as->ofdm_weak_sig ? "on" : "off");
len += snprintf(buf+len, sizeof(buf)-len,
"CCK weak signal detection:\t%s\n",
as->cck_weak_sig ? "on" : "off");
len += snprintf(buf+len, sizeof(buf)-len,
"\nMIB INTERRUPTS:\t\t%u\n",
st->mib_intr);
len += snprintf(buf+len, sizeof(buf)-len,
"beacon RSSI average:\t%d\n",
sc->ah->ah_beacon_rssi_avg.avg);
len += snprintf(buf+len, sizeof(buf)-len, "profcnt tx\t\t%u\t(%d%%)\n",
as->pfc_tx,
as->pfc_cycles > 0 ?
as->pfc_tx*100/as->pfc_cycles : 0);
len += snprintf(buf+len, sizeof(buf)-len, "profcnt rx\t\t%u\t(%d%%)\n",
as->pfc_rx,
as->pfc_cycles > 0 ?
as->pfc_rx*100/as->pfc_cycles : 0);
len += snprintf(buf+len, sizeof(buf)-len, "profcnt busy\t\t%u\t(%d%%)\n",
as->pfc_busy,
as->pfc_cycles > 0 ?
as->pfc_busy*100/as->pfc_cycles : 0);
len += snprintf(buf+len, sizeof(buf)-len, "profcnt cycles\t\t%u\n",
as->pfc_cycles);
len += snprintf(buf+len, sizeof(buf)-len,
"listen time\t\t%d\tlast: %d\n",
as->listen_time, as->last_listen);
len += snprintf(buf+len, sizeof(buf)-len,
"OFDM errors\t\t%u\tlast: %u\tsum: %u\n",
as->ofdm_errors, as->last_ofdm_errors,
as->sum_ofdm_errors);
len += snprintf(buf+len, sizeof(buf)-len,
"CCK errors\t\t%u\tlast: %u\tsum: %u\n",
as->cck_errors, as->last_cck_errors,
as->sum_cck_errors);
len += snprintf(buf+len, sizeof(buf)-len,
"AR5K_PHYERR_CNT1\t%x\t(=%d)\n",
ath5k_hw_reg_read(sc->ah, AR5K_PHYERR_CNT1),
ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX -
ath5k_hw_reg_read(sc->ah, AR5K_PHYERR_CNT1)));
len += snprintf(buf+len, sizeof(buf)-len,
"AR5K_PHYERR_CNT2\t%x\t(=%d)\n",
ath5k_hw_reg_read(sc->ah, AR5K_PHYERR_CNT2),
ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX -
ath5k_hw_reg_read(sc->ah, AR5K_PHYERR_CNT2)));
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t write_file_ani(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ath5k_softc *sc = file->private_data;
char buf[20];
if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
return -EFAULT;
if (strncmp(buf, "sens-low", 8) == 0) {
ath5k_ani_init(sc->ah, ATH5K_ANI_MODE_MANUAL_HIGH);
} else if (strncmp(buf, "sens-high", 9) == 0) {
ath5k_ani_init(sc->ah, ATH5K_ANI_MODE_MANUAL_LOW);
} else if (strncmp(buf, "ani-off", 7) == 0) {
ath5k_ani_init(sc->ah, ATH5K_ANI_MODE_OFF);
} else if (strncmp(buf, "ani-on", 6) == 0) {
ath5k_ani_init(sc->ah, ATH5K_ANI_MODE_AUTO);
} else if (strncmp(buf, "noise-low", 9) == 0) {
ath5k_ani_set_noise_immunity_level(sc->ah, 0);
} else if (strncmp(buf, "noise-high", 10) == 0) {
ath5k_ani_set_noise_immunity_level(sc->ah,
ATH5K_ANI_MAX_NOISE_IMM_LVL);
} else if (strncmp(buf, "spur-low", 8) == 0) {
ath5k_ani_set_spur_immunity_level(sc->ah, 0);
} else if (strncmp(buf, "spur-high", 9) == 0) {
ath5k_ani_set_spur_immunity_level(sc->ah,
sc->ani_state.max_spur_level);
} else if (strncmp(buf, "fir-low", 7) == 0) {
ath5k_ani_set_firstep_level(sc->ah, 0);
} else if (strncmp(buf, "fir-high", 8) == 0) {
ath5k_ani_set_firstep_level(sc->ah, ATH5K_ANI_MAX_FIRSTEP_LVL);
} else if (strncmp(buf, "ofdm-off", 8) == 0) {
ath5k_ani_set_ofdm_weak_signal_detection(sc->ah, false);
} else if (strncmp(buf, "ofdm-on", 7) == 0) {
ath5k_ani_set_ofdm_weak_signal_detection(sc->ah, true);
} else if (strncmp(buf, "cck-off", 7) == 0) {
ath5k_ani_set_cck_weak_signal_detection(sc->ah, false);
} else if (strncmp(buf, "cck-on", 6) == 0) {
ath5k_ani_set_cck_weak_signal_detection(sc->ah, true);
}
return count;
}
static const struct file_operations fops_ani = {
.read = read_file_ani,
.write = write_file_ani,
.open = ath5k_debugfs_open,
.owner = THIS_MODULE,
};
/* init */ /* init */
void void
...@@ -611,6 +767,11 @@ ath5k_debug_init_device(struct ath5k_softc *sc) ...@@ -611,6 +767,11 @@ ath5k_debug_init_device(struct ath5k_softc *sc)
S_IWUSR | S_IRUSR, S_IWUSR | S_IRUSR,
sc->debug.debugfs_phydir, sc, sc->debug.debugfs_phydir, sc,
&fops_frameerrors); &fops_frameerrors);
sc->debug.debugfs_ani = debugfs_create_file("ani",
S_IWUSR | S_IRUSR,
sc->debug.debugfs_phydir, sc,
&fops_ani);
} }
void void
...@@ -628,6 +789,7 @@ ath5k_debug_finish_device(struct ath5k_softc *sc) ...@@ -628,6 +789,7 @@ ath5k_debug_finish_device(struct ath5k_softc *sc)
debugfs_remove(sc->debug.debugfs_reset); debugfs_remove(sc->debug.debugfs_reset);
debugfs_remove(sc->debug.debugfs_antenna); debugfs_remove(sc->debug.debugfs_antenna);
debugfs_remove(sc->debug.debugfs_frameerrors); debugfs_remove(sc->debug.debugfs_frameerrors);
debugfs_remove(sc->debug.debugfs_ani);
debugfs_remove(sc->debug.debugfs_phydir); debugfs_remove(sc->debug.debugfs_phydir);
} }
......
...@@ -76,6 +76,7 @@ struct ath5k_dbg_info { ...@@ -76,6 +76,7 @@ struct ath5k_dbg_info {
struct dentry *debugfs_reset; struct dentry *debugfs_reset;
struct dentry *debugfs_antenna; struct dentry *debugfs_antenna;
struct dentry *debugfs_frameerrors; struct dentry *debugfs_frameerrors;
struct dentry *debugfs_ani;
}; };
/** /**
...@@ -115,6 +116,7 @@ enum ath5k_debug_level { ...@@ -115,6 +116,7 @@ enum ath5k_debug_level {
ATH5K_DEBUG_DUMP_TX = 0x00000200, ATH5K_DEBUG_DUMP_TX = 0x00000200,
ATH5K_DEBUG_DUMPBANDS = 0x00000400, ATH5K_DEBUG_DUMPBANDS = 0x00000400,
ATH5K_DEBUG_TRACE = 0x00001000, ATH5K_DEBUG_TRACE = 0x00001000,
ATH5K_DEBUG_ANI = 0x00002000,
ATH5K_DEBUG_ANY = 0xffffffff ATH5K_DEBUG_ANY = 0xffffffff
}; };
......
...@@ -645,6 +645,7 @@ static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah, ...@@ -645,6 +645,7 @@ static int ath5k_hw_proc_5212_rx_status(struct ath5k_hw *ah,
rs->rs_status |= AR5K_RXERR_PHY; rs->rs_status |= AR5K_RXERR_PHY;
rs->rs_phyerr |= AR5K_REG_MS(rx_err->rx_error_1, rs->rs_phyerr |= AR5K_REG_MS(rx_err->rx_error_1,
AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE); AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE);
ath5k_ani_phy_error_report(ah, rs->rs_phyerr);
} }
if (rx_status->rx_status_1 & if (rx_status->rx_status_1 &
......
...@@ -379,7 +379,6 @@ void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) ...@@ -379,7 +379,6 @@ void ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask)
* (ACK etc). * (ACK etc).
* *
* NOTE: RX DMA should be already enabled using ath5k_hw_start_rx_dma * NOTE: RX DMA should be already enabled using ath5k_hw_start_rx_dma
* TODO: Init ANI here
*/ */
void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah) void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah)
{ {
......
...@@ -212,10 +212,10 @@ ...@@ -212,10 +212,10 @@
* MIB control register * MIB control register
*/ */
#define AR5K_MIBC 0x0040 /* Register Address */ #define AR5K_MIBC 0x0040 /* Register Address */
#define AR5K_MIBC_COW 0x00000001 /* Warn test indicator */ #define AR5K_MIBC_COW 0x00000001 /* Counter Overflow Warning */
#define AR5K_MIBC_FMC 0x00000002 /* Freeze MIB Counters */ #define AR5K_MIBC_FMC 0x00000002 /* Freeze MIB Counters */
#define AR5K_MIBC_CMC 0x00000004 /* Clean MIB Counters */ #define AR5K_MIBC_CMC 0x00000004 /* Clear MIB Counters */
#define AR5K_MIBC_MCS 0x00000008 /* MIB counter strobe */ #define AR5K_MIBC_MCS 0x00000008 /* MIB counter strobe, increment all */
/* /*
* Timeout prescale register * Timeout prescale register
...@@ -1516,7 +1516,14 @@ ...@@ -1516,7 +1516,14 @@
AR5K_NAV_5210 : AR5K_NAV_5211) AR5K_NAV_5210 : AR5K_NAV_5211)
/* /*
* RTS success register * MIB counters:
*
* max value is 0xc000, if this is reached we get a MIB interrupt.
* they can be controlled via AR5K_MIBC and are cleared on read.
*/
/*
* RTS success (MIB counter)
*/ */
#define AR5K_RTS_OK_5210 0x8090 #define AR5K_RTS_OK_5210 0x8090
#define AR5K_RTS_OK_5211 0x8088 #define AR5K_RTS_OK_5211 0x8088
...@@ -1524,7 +1531,7 @@ ...@@ -1524,7 +1531,7 @@
AR5K_RTS_OK_5210 : AR5K_RTS_OK_5211) AR5K_RTS_OK_5210 : AR5K_RTS_OK_5211)
/* /*
* RTS failure register * RTS failure (MIB counter)
*/ */
#define AR5K_RTS_FAIL_5210 0x8094 #define AR5K_RTS_FAIL_5210 0x8094
#define AR5K_RTS_FAIL_5211 0x808c #define AR5K_RTS_FAIL_5211 0x808c
...@@ -1532,7 +1539,7 @@ ...@@ -1532,7 +1539,7 @@
AR5K_RTS_FAIL_5210 : AR5K_RTS_FAIL_5211) AR5K_RTS_FAIL_5210 : AR5K_RTS_FAIL_5211)
/* /*
* ACK failure register * ACK failure (MIB counter)
*/ */
#define AR5K_ACK_FAIL_5210 0x8098 #define AR5K_ACK_FAIL_5210 0x8098
#define AR5K_ACK_FAIL_5211 0x8090 #define AR5K_ACK_FAIL_5211 0x8090
...@@ -1540,7 +1547,7 @@ ...@@ -1540,7 +1547,7 @@
AR5K_ACK_FAIL_5210 : AR5K_ACK_FAIL_5211) AR5K_ACK_FAIL_5210 : AR5K_ACK_FAIL_5211)
/* /*
* FCS failure register * FCS failure (MIB counter)
*/ */
#define AR5K_FCS_FAIL_5210 0x809c #define AR5K_FCS_FAIL_5210 0x809c
#define AR5K_FCS_FAIL_5211 0x8094 #define AR5K_FCS_FAIL_5211 0x8094
...@@ -1667,11 +1674,17 @@ ...@@ -1667,11 +1674,17 @@
/* /*
* Profile count registers * Profile count registers
*
* These registers can be cleared and freezed with ATH5K_MIBC, but they do not
* generate a MIB interrupt.
* Instead of overflowing, they shift by one bit to the right. All registers
* shift together, i.e. when one reaches the max, all shift at the same time by
* one bit to the right. This way we should always get consistent values.
*/ */
#define AR5K_PROFCNT_TX 0x80ec /* Tx count */ #define AR5K_PROFCNT_TX 0x80ec /* Tx count */
#define AR5K_PROFCNT_RX 0x80f0 /* Rx count */ #define AR5K_PROFCNT_RX 0x80f0 /* Rx count */
#define AR5K_PROFCNT_RXCLR 0x80f4 /* Clear Rx count */ #define AR5K_PROFCNT_RXCLR 0x80f4 /* Busy count */
#define AR5K_PROFCNT_CYCLE 0x80f8 /* Cycle count (?) */ #define AR5K_PROFCNT_CYCLE 0x80f8 /* Cycle counter */
/* /*
* Quiet period control registers * Quiet period control registers
...@@ -1758,7 +1771,7 @@ ...@@ -1758,7 +1771,7 @@
#define AR5K_CCK_FIL_CNT 0x8128 #define AR5K_CCK_FIL_CNT 0x8128
/* /*
* PHY Error Counters (?) * PHY Error Counters (same masks as AR5K_PHY_ERR_FIL)
*/ */
#define AR5K_PHYERR_CNT1 0x812c #define AR5K_PHYERR_CNT1 0x812c
#define AR5K_PHYERR_CNT1_MASK 0x8130 #define AR5K_PHYERR_CNT1_MASK 0x8130
...@@ -1766,6 +1779,9 @@ ...@@ -1766,6 +1779,9 @@
#define AR5K_PHYERR_CNT2 0x8134 #define AR5K_PHYERR_CNT2 0x8134
#define AR5K_PHYERR_CNT2_MASK 0x8138 #define AR5K_PHYERR_CNT2_MASK 0x8138
/* if the PHY Error Counters reach this maximum, we get MIB interrupts */
#define ATH5K_PHYERR_CNT_MAX 0x00c00000
/* /*
* TSF Threshold register (?) * TSF Threshold register (?)
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment