aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath5k
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath5k')
-rw-r--r--drivers/net/wireless/ath/ath5k/ath5k.h2
-rw-r--r--drivers/net/wireless/ath/ath5k/attach.c1
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c16
-rw-r--r--drivers/net/wireless/ath/ath5k/debug.c58
-rw-r--r--drivers/net/wireless/ath/ath5k/debug.h1
-rw-r--r--drivers/net/wireless/ath/ath5k/dma.c4
-rw-r--r--drivers/net/wireless/ath/ath5k/pcu.c99
-rw-r--r--drivers/net/wireless/ath/ath5k/phy.c4
-rw-r--r--drivers/net/wireless/ath/ath5k/reg.h29
9 files changed, 190 insertions, 24 deletions
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index b96bb98..0cba2e3 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1041,7 +1041,6 @@ struct ath5k_hw {
#define ah_modes ah_capabilities.cap_mode
#define ah_ee_version ah_capabilities.cap_eeprom.ee_version
- u32 ah_atim_window;
u32 ah_limit_tx_retries;
u8 ah_coverage_class;
@@ -1196,6 +1195,7 @@ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah);
void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64);
void ath5k_hw_reset_tsf(struct ath5k_hw *ah);
void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval);
+bool ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval);
/* ACK bit rate */
void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high);
/* Clock rate related functions */
diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c
index 6e02de3..cd0b14a 100644
--- a/drivers/net/wireless/ath/ath5k/attach.c
+++ b/drivers/net/wireless/ath/ath5k/attach.c
@@ -118,7 +118,6 @@ int ath5k_hw_attach(struct ath5k_softc *sc)
ah->ah_turbo = false;
ah->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER;
ah->ah_imr = 0;
- ah->ah_atim_window = 0;
ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY;
ah->ah_software_retry = false;
ah->ah_ant_mode = AR5K_ANTMODE_DEFAULT;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 95072db..94cc335 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -1191,6 +1191,15 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb,
*/
if (hw_tu >= sc->nexttbtt)
ath5k_beacon_update_timers(sc, bc_tstamp);
+
+ /* Check if the beacon timers are still correct, because a TSF
+ * update might have created a window between them - for a
+ * longer description see the comment of this function: */
+ if (!ath5k_hw_check_beacon_timers(sc->ah, sc->bintval)) {
+ ath5k_beacon_update_timers(sc, bc_tstamp);
+ ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
+ "fixed beacon timers after beacon receive\n");
+ }
}
}
@@ -1877,8 +1886,11 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
hw_tsf = ath5k_hw_get_tsf64(ah);
hw_tu = TSF_TO_TU(hw_tsf);
-#define FUDGE 3
- /* we use FUDGE to make sure the next TBTT is ahead of the current TU */
+#define FUDGE AR5K_TUNE_SW_BEACON_RESP + 3
+ /* We use FUDGE to make sure the next TBTT is ahead of the current TU.
+ * Since we later substract AR5K_TUNE_SW_BEACON_RESP (10) in the timer
+ * configuration we need to make sure it is bigger than that. */
+
if (bc_tsf == -1) {
/*
* no beacons received, called internally.
diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c
index 6583a82..0f06e84 100644
--- a/drivers/net/wireless/ath/ath5k/debug.c
+++ b/drivers/net/wireless/ath/ath5k/debug.c
@@ -483,6 +483,59 @@ static const struct file_operations fops_antenna = {
.owner = THIS_MODULE,
};
+/* debugfs: misc */
+
+static ssize_t read_file_misc(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;
+ u32 filt = ath5k_hw_get_rx_filter(sc->ah);
+
+ len += snprintf(buf+len, sizeof(buf)-len, "bssid-mask: %pM\n",
+ sc->bssidmask);
+ len += snprintf(buf+len, sizeof(buf)-len, "filter-flags: 0x%x ",
+ filt);
+ if (filt & AR5K_RX_FILTER_UCAST)
+ len += snprintf(buf+len, sizeof(buf)-len, " UCAST");
+ if (filt & AR5K_RX_FILTER_MCAST)
+ len += snprintf(buf+len, sizeof(buf)-len, " MCAST");
+ if (filt & AR5K_RX_FILTER_BCAST)
+ len += snprintf(buf+len, sizeof(buf)-len, " BCAST");
+ if (filt & AR5K_RX_FILTER_CONTROL)
+ len += snprintf(buf+len, sizeof(buf)-len, " CONTROL");
+ if (filt & AR5K_RX_FILTER_BEACON)
+ len += snprintf(buf+len, sizeof(buf)-len, " BEACON");
+ if (filt & AR5K_RX_FILTER_PROM)
+ len += snprintf(buf+len, sizeof(buf)-len, " PROM");
+ if (filt & AR5K_RX_FILTER_XRPOLL)
+ len += snprintf(buf+len, sizeof(buf)-len, " XRPOLL");
+ if (filt & AR5K_RX_FILTER_PROBEREQ)
+ len += snprintf(buf+len, sizeof(buf)-len, " PROBEREQ");
+ if (filt & AR5K_RX_FILTER_PHYERR_5212)
+ len += snprintf(buf+len, sizeof(buf)-len, " PHYERR-5212");
+ if (filt & AR5K_RX_FILTER_RADARERR_5212)
+ len += snprintf(buf+len, sizeof(buf)-len, " RADARERR-5212");
+ if (filt & AR5K_RX_FILTER_PHYERR_5211)
+ snprintf(buf+len, sizeof(buf)-len, " PHYERR-5211");
+ if (filt & AR5K_RX_FILTER_RADARERR_5211)
+ len += snprintf(buf+len, sizeof(buf)-len, " RADARERR-5211\n");
+ else
+ len += snprintf(buf+len, sizeof(buf)-len, "\n");
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_misc = {
+ .read = read_file_misc,
+ .open = ath5k_debugfs_open,
+ .owner = THIS_MODULE,
+};
+
/* debugfs: frameerrors */
@@ -856,6 +909,10 @@ ath5k_debug_init_device(struct ath5k_softc *sc)
S_IWUSR | S_IRUSR,
sc->debug.debugfs_phydir, sc, &fops_antenna);
+ sc->debug.debugfs_misc = debugfs_create_file("misc",
+ S_IRUSR,
+ sc->debug.debugfs_phydir, sc, &fops_misc);
+
sc->debug.debugfs_frameerrors = debugfs_create_file("frameerrors",
S_IWUSR | S_IRUSR,
sc->debug.debugfs_phydir, sc,
@@ -886,6 +943,7 @@ ath5k_debug_finish_device(struct ath5k_softc *sc)
debugfs_remove(sc->debug.debugfs_beacon);
debugfs_remove(sc->debug.debugfs_reset);
debugfs_remove(sc->debug.debugfs_antenna);
+ debugfs_remove(sc->debug.debugfs_misc);
debugfs_remove(sc->debug.debugfs_frameerrors);
debugfs_remove(sc->debug.debugfs_ani);
debugfs_remove(sc->debug.debugfs_queue);
diff --git a/drivers/net/wireless/ath/ath5k/debug.h b/drivers/net/wireless/ath/ath5k/debug.h
index 9b22722..4f078b1 100644
--- a/drivers/net/wireless/ath/ath5k/debug.h
+++ b/drivers/net/wireless/ath/ath5k/debug.h
@@ -75,6 +75,7 @@ struct ath5k_dbg_info {
struct dentry *debugfs_beacon;
struct dentry *debugfs_reset;
struct dentry *debugfs_antenna;
+ struct dentry *debugfs_misc;
struct dentry *debugfs_frameerrors;
struct dentry *debugfs_ani;
struct dentry *debugfs_queue;
diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c
index 58bb6c5..923c9ca 100644
--- a/drivers/net/wireless/ath/ath5k/dma.c
+++ b/drivers/net/wireless/ath/ath5k/dma.c
@@ -244,7 +244,7 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
/* Force channel idle high */
AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211,
- AR5K_DIAG_SW_CHANEL_IDLE_HIGH);
+ AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
/* Wait a while and disable mechanism */
udelay(200);
@@ -261,7 +261,7 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue)
} while (--i && pending);
AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211,
- AR5K_DIAG_SW_CHANEL_IDLE_HIGH);
+ AR5K_DIAG_SW_CHANNEL_IDLE_HIGH);
}
/* Clear register */
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
index 6a891c4..095d30b 100644
--- a/drivers/net/wireless/ath/ath5k/pcu.c
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -495,6 +495,10 @@ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah)
{
u32 tsf_lower, tsf_upper1, tsf_upper2;
int i;
+ unsigned long flags;
+
+ /* This code is time critical - we don't want to be interrupted here */
+ local_irq_save(flags);
/*
* While reading TSF upper and then lower part, the clock is still
@@ -517,6 +521,8 @@ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah)
tsf_upper1 = tsf_upper2;
}
+ local_irq_restore(flags);
+
WARN_ON( i == ATH5K_MAX_TSF_READ );
return (((u64)tsf_upper1 << 32) | tsf_lower);
@@ -600,7 +606,7 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
/* Timer3 marks the end of our ATIM window
* a zero length window is not allowed because
* we 'll get no beacons */
- timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1);
+ timer3 = next_beacon + 1;
/*
* Set the beacon register and enable all timers.
@@ -641,6 +647,97 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
}
/**
+ * ath5k_check_timer_win - Check if timer B is timer A + window
+ *
+ * @a: timer a (before b)
+ * @b: timer b (after a)
+ * @window: difference between a and b
+ * @intval: timers are increased by this interval
+ *
+ * This helper function checks if timer B is timer A + window and covers
+ * cases where timer A or B might have already been updated or wrapped
+ * around (Timers are 16 bit).
+ *
+ * Returns true if O.K.
+ */
+static inline bool
+ath5k_check_timer_win(int a, int b, int window, int intval)
+{
+ /*
+ * 1.) usually B should be A + window
+ * 2.) A already updated, B not updated yet
+ * 3.) A already updated and has wrapped around
+ * 4.) B has wrapped around
+ */
+ if ((b - a == window) || /* 1.) */
+ (a - b == intval - window) || /* 2.) */
+ ((a | 0x10000) - b == intval - window) || /* 3.) */
+ ((b | 0x10000) - a == window)) /* 4.) */
+ return true; /* O.K. */
+ return false;
+}
+
+/**
+ * ath5k_hw_check_beacon_timers - Check if the beacon timers are correct
+ *
+ * @ah: The &struct ath5k_hw
+ * @intval: beacon interval
+ *
+ * This is a workaround for IBSS mode:
+ *
+ * The need for this function arises from the fact that we have 4 separate
+ * HW timer registers (TIMER0 - TIMER3), which are closely related to the
+ * next beacon target time (NBTT), and that the HW updates these timers
+ * seperately based on the current TSF value. The hardware increments each
+ * timer by the beacon interval, when the local TSF coverted to TU is equal
+ * to the value stored in the timer.
+ *
+ * The reception of a beacon with the same BSSID can update the local HW TSF
+ * at any time - this is something we can't avoid. If the TSF jumps to a
+ * time which is later than the time stored in a timer, this timer will not
+ * be updated until the TSF in TU wraps around at 16 bit (the size of the
+ * timers) and reaches the time which is stored in the timer.
+ *
+ * The problem is that these timers are closely related to TIMER0 (NBTT) and
+ * that they define a time "window". When the TSF jumps between two timers
+ * (e.g. ATIM and NBTT), the one in the past will be left behind (not
+ * updated), while the one in the future will be updated every beacon
+ * interval. This causes the window to get larger, until the TSF wraps
+ * around as described above and the timer which was left behind gets
+ * updated again. But - because the beacon interval is usually not an exact
+ * divisor of the size of the timers (16 bit), an unwanted "window" between
+ * these timers has developed!
+ *
+ * This is especially important with the ATIM window, because during
+ * the ATIM window only ATIM frames and no data frames are allowed to be
+ * sent, which creates transmission pauses after each beacon. This symptom
+ * has been described as "ramping ping" because ping times increase linearly
+ * for some time and then drop down again. A wrong window on the DMA beacon
+ * timer has the same effect, so we check for these two conditions.
+ *
+ * Returns true if O.K.
+ */
+bool
+ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval)
+{
+ unsigned int nbtt, atim, dma;
+
+ nbtt = ath5k_hw_reg_read(ah, AR5K_TIMER0);
+ atim = ath5k_hw_reg_read(ah, AR5K_TIMER3);
+ dma = ath5k_hw_reg_read(ah, AR5K_TIMER1) >> 3;
+
+ /* NOTE: SWBA is different. Having a wrong window there does not
+ * stop us from sending data and this condition is catched thru
+ * other means (SWBA interrupt) */
+
+ if (ath5k_check_timer_win(nbtt, atim, 1, intval) &&
+ ath5k_check_timer_win(dma, nbtt, AR5K_TUNE_DMA_BEACON_RESP,
+ intval))
+ return true; /* O.K. */
+ return false;
+}
+
+/**
* ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class
*
* @ah: The &struct ath5k_hw
diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c
index 4932bf2..61da913 100644
--- a/drivers/net/wireless/ath/ath5k/phy.c
+++ b/drivers/net/wireless/ath/ath5k/phy.c
@@ -1257,7 +1257,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
* Disable beacons and RX/TX queues, wait
*/
AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5210,
- AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210);
+ AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210);
beacon = ath5k_hw_reg_read(ah, AR5K_BEACON_5210);
ath5k_hw_reg_write(ah, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210);
@@ -1336,7 +1336,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
* Re-enable RX/TX and beacons
*/
AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5210,
- AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210);
+ AR5K_DIAG_SW_DIS_TX_5210 | AR5K_DIAG_SW_DIS_RX_5210);
ath5k_hw_reg_write(ah, beacon, AR5K_BEACON_5210);
return 0;
diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h
index 67d6308..a34929f 100644
--- a/drivers/net/wireless/ath/ath5k/reg.h
+++ b/drivers/net/wireless/ath/ath5k/reg.h
@@ -1387,10 +1387,9 @@
/*
- * PCU control register
+ * PCU Diagnostic register
*
- * Only DIS_RX is used in the code, the rest i guess are
- * for tweaking/diagnostics.
+ * Used for tweaking/diagnostics.
*/
#define AR5K_DIAG_SW_5210 0x8068 /* Register Address [5210] */
#define AR5K_DIAG_SW_5211 0x8048 /* Register Address [5211+] */
@@ -1399,22 +1398,22 @@
#define AR5K_DIAG_SW_DIS_WEP_ACK 0x00000001 /* Disable ACKs if WEP key is invalid */
#define AR5K_DIAG_SW_DIS_ACK 0x00000002 /* Disable ACKs */
#define AR5K_DIAG_SW_DIS_CTS 0x00000004 /* Disable CTSs */
-#define AR5K_DIAG_SW_DIS_ENC 0x00000008 /* Disable encryption */
-#define AR5K_DIAG_SW_DIS_DEC 0x00000010 /* Disable decryption */
-#define AR5K_DIAG_SW_DIS_TX 0x00000020 /* Disable transmit [5210] */
-#define AR5K_DIAG_SW_DIS_RX_5210 0x00000040 /* Disable recieve */
+#define AR5K_DIAG_SW_DIS_ENC 0x00000008 /* Disable HW encryption */
+#define AR5K_DIAG_SW_DIS_DEC 0x00000010 /* Disable HW decryption */
+#define AR5K_DIAG_SW_DIS_TX_5210 0x00000020 /* Disable transmit [5210] */
+#define AR5K_DIAG_SW_DIS_RX_5210 0x00000040 /* Disable receive */
#define AR5K_DIAG_SW_DIS_RX_5211 0x00000020
#define AR5K_DIAG_SW_DIS_RX (ah->ah_version == AR5K_AR5210 ? \
AR5K_DIAG_SW_DIS_RX_5210 : AR5K_DIAG_SW_DIS_RX_5211)
-#define AR5K_DIAG_SW_LOOP_BACK_5210 0x00000080 /* Loopback (i guess it goes with DIS_TX) [5210] */
+#define AR5K_DIAG_SW_LOOP_BACK_5210 0x00000080 /* TX Data Loopback (i guess it goes with DIS_TX) [5210] */
#define AR5K_DIAG_SW_LOOP_BACK_5211 0x00000040
#define AR5K_DIAG_SW_LOOP_BACK (ah->ah_version == AR5K_AR5210 ? \
AR5K_DIAG_SW_LOOP_BACK_5210 : AR5K_DIAG_SW_LOOP_BACK_5211)
-#define AR5K_DIAG_SW_CORR_FCS_5210 0x00000100 /* Corrupted FCS */
+#define AR5K_DIAG_SW_CORR_FCS_5210 0x00000100 /* Generate invalid TX FCS */
#define AR5K_DIAG_SW_CORR_FCS_5211 0x00000080
#define AR5K_DIAG_SW_CORR_FCS (ah->ah_version == AR5K_AR5210 ? \
AR5K_DIAG_SW_CORR_FCS_5210 : AR5K_DIAG_SW_CORR_FCS_5211)
-#define AR5K_DIAG_SW_CHAN_INFO_5210 0x00000200 /* Dump channel info */
+#define AR5K_DIAG_SW_CHAN_INFO_5210 0x00000200 /* Add 56 bytes of channel info before the frame data in the RX buffer */
#define AR5K_DIAG_SW_CHAN_INFO_5211 0x00000100
#define AR5K_DIAG_SW_CHAN_INFO (ah->ah_version == AR5K_AR5210 ? \
AR5K_DIAG_SW_CHAN_INFO_5210 : AR5K_DIAG_SW_CHAN_INFO_5211)
@@ -1426,17 +1425,17 @@
#define AR5K_DIAG_SW_SCVRAM_SEED 0x0003f800 /* [5210] */
#define AR5K_DIAG_SW_SCRAM_SEED_M 0x0001fc00 /* Scrambler seed mask */
#define AR5K_DIAG_SW_SCRAM_SEED_S 10
-#define AR5K_DIAG_SW_DIS_SEQ_INC 0x00040000 /* Disable seqnum increment (?)[5210] */
+#define AR5K_DIAG_SW_DIS_SEQ_INC_5210 0x00040000 /* Disable seqnum increment (?)[5210] */
#define AR5K_DIAG_SW_FRAME_NV0_5210 0x00080000
#define AR5K_DIAG_SW_FRAME_NV0_5211 0x00020000 /* Accept frames of non-zero protocol number */
#define AR5K_DIAG_SW_FRAME_NV0 (ah->ah_version == AR5K_AR5210 ? \
AR5K_DIAG_SW_FRAME_NV0_5210 : AR5K_DIAG_SW_FRAME_NV0_5211)
#define AR5K_DIAG_SW_OBSPT_M 0x000c0000 /* Observation point select (?) */
#define AR5K_DIAG_SW_OBSPT_S 18
-#define AR5K_DIAG_SW_RX_CLEAR_HIGH 0x0010000 /* Force RX Clear high */
-#define AR5K_DIAG_SW_IGNORE_CARR_SENSE 0x0020000 /* Ignore virtual carrier sense */
-#define AR5K_DIAG_SW_CHANEL_IDLE_HIGH 0x0040000 /* Force channel idle high */
-#define AR5K_DIAG_SW_PHEAR_ME 0x0080000 /* ??? */
+#define AR5K_DIAG_SW_RX_CLEAR_HIGH 0x00100000 /* Ignore carrier sense */
+#define AR5K_DIAG_SW_IGNORE_CARR_SENSE 0x00200000 /* Ignore virtual carrier sense */
+#define AR5K_DIAG_SW_CHANNEL_IDLE_HIGH 0x00400000 /* Force channel idle high */
+#define AR5K_DIAG_SW_PHEAR_ME 0x00800000 /* ??? */
/*
* TSF (clock) register (lower 32 bits)