aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBruno Randolf <br1@einfach.org>2010-09-26 23:22:21 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-09-28 15:47:57 -0400
commit7f896126017830b29cf501d246ee32b81e359acd (patch)
treec7c1444a291fffb91ef96c6cae281f821c8ff8d0 /drivers
parent4a79f2c517cce31c3b25aab0ec5078368b22c363 (diff)
ath5k: Check and fix ATIM window
This patch adds sanity-checks for the beacon timers and especially the ATIM window to ath5k. It is basically the same what i did for madwifi two years ago and fixes a problem in IBSS mode which has been described as "ramping" pings. See the code comments for a more detailed description and these links: http://madwifi-project.org/ticket/1154 http://madwifi-project.org/changeset/3867 http://thread.gmane.org/gmane.linux.drivers.madwifi.devel/6066 Signed-off-by: Bruno Randolf <br1@einfach.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/ath/ath5k/ath5k.h1
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c9
-rw-r--r--drivers/net/wireless/ath/ath5k/pcu.c91
3 files changed, 101 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index 42e6e821259e..0cba2e315d9a 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1195,6 +1195,7 @@ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah);
1195void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64); 1195void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64);
1196void ath5k_hw_reset_tsf(struct ath5k_hw *ah); 1196void ath5k_hw_reset_tsf(struct ath5k_hw *ah);
1197void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval); 1197void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval);
1198bool ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval);
1198/* ACK bit rate */ 1199/* ACK bit rate */
1199void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high); 1200void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high);
1200/* Clock rate related functions */ 1201/* Clock rate related functions */
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 95072db0ec21..4103765000ea 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,
1191 */ 1191 */
1192 if (hw_tu >= sc->nexttbtt) 1192 if (hw_tu >= sc->nexttbtt)
1193 ath5k_beacon_update_timers(sc, bc_tstamp); 1193 ath5k_beacon_update_timers(sc, bc_tstamp);
1194
1195 /* Check if the beacon timers are still correct, because a TSF
1196 * update might have created a window between them - for a
1197 * longer description see the comment of this function: */
1198 if (!ath5k_hw_check_beacon_timers(sc->ah, sc->bintval)) {
1199 ath5k_beacon_update_timers(sc, bc_tstamp);
1200 ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON,
1201 "fixed beacon timers after beacon receive\n");
1202 }
1194 } 1203 }
1195} 1204}
1196 1205
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c
index 71dca1023e72..604114fb34b1 100644
--- a/drivers/net/wireless/ath/ath5k/pcu.c
+++ b/drivers/net/wireless/ath/ath5k/pcu.c
@@ -641,6 +641,97 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval)
641} 641}
642 642
643/** 643/**
644 * ath5k_check_timer_win - Check if timer B is timer A + window
645 *
646 * @a: timer a (before b)
647 * @b: timer b (after a)
648 * @window: difference between a and b
649 * @intval: timers are increased by this interval
650 *
651 * This helper function checks if timer B is timer A + window and covers
652 * cases where timer A or B might have already been updated or wrapped
653 * around (Timers are 16 bit).
654 *
655 * Returns true if O.K.
656 */
657static inline bool
658ath5k_check_timer_win(int a, int b, int window, int intval)
659{
660 /*
661 * 1.) usually B should be A + window
662 * 2.) A already updated, B not updated yet
663 * 3.) A already updated and has wrapped around
664 * 4.) B has wrapped around
665 */
666 if ((b - a == window) || /* 1.) */
667 (a - b == intval - window) || /* 2.) */
668 ((a | 0x10000) - b == intval - window) || /* 3.) */
669 ((b | 0x10000) - a == window)) /* 4.) */
670 return true; /* O.K. */
671 return false;
672}
673
674/**
675 * ath5k_hw_check_beacon_timers - Check if the beacon timers are correct
676 *
677 * @ah: The &struct ath5k_hw
678 * @intval: beacon interval
679 *
680 * This is a workaround for IBSS mode:
681 *
682 * The need for this function arises from the fact that we have 4 separate
683 * HW timer registers (TIMER0 - TIMER3), which are closely related to the
684 * next beacon target time (NBTT), and that the HW updates these timers
685 * seperately based on the current TSF value. The hardware increments each
686 * timer by the beacon interval, when the local TSF coverted to TU is equal
687 * to the value stored in the timer.
688 *
689 * The reception of a beacon with the same BSSID can update the local HW TSF
690 * at any time - this is something we can't avoid. If the TSF jumps to a
691 * time which is later than the time stored in a timer, this timer will not
692 * be updated until the TSF in TU wraps around at 16 bit (the size of the
693 * timers) and reaches the time which is stored in the timer.
694 *
695 * The problem is that these timers are closely related to TIMER0 (NBTT) and
696 * that they define a time "window". When the TSF jumps between two timers
697 * (e.g. ATIM and NBTT), the one in the past will be left behind (not
698 * updated), while the one in the future will be updated every beacon
699 * interval. This causes the window to get larger, until the TSF wraps
700 * around as described above and the timer which was left behind gets
701 * updated again. But - because the beacon interval is usually not an exact
702 * divisor of the size of the timers (16 bit), an unwanted "window" between
703 * these timers has developed!
704 *
705 * This is especially important with the ATIM window, because during
706 * the ATIM window only ATIM frames and no data frames are allowed to be
707 * sent, which creates transmission pauses after each beacon. This symptom
708 * has been described as "ramping ping" because ping times increase linearly
709 * for some time and then drop down again. A wrong window on the DMA beacon
710 * timer has the same effect, so we check for these two conditions.
711 *
712 * Returns true if O.K.
713 */
714bool
715ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval)
716{
717 unsigned int nbtt, atim, dma;
718
719 nbtt = ath5k_hw_reg_read(ah, AR5K_TIMER0);
720 atim = ath5k_hw_reg_read(ah, AR5K_TIMER3);
721 dma = ath5k_hw_reg_read(ah, AR5K_TIMER1) >> 3;
722
723 /* NOTE: SWBA is different. Having a wrong window there does not
724 * stop us from sending data and this condition is catched thru
725 * other means (SWBA interrupt) */
726
727 if (ath5k_check_timer_win(nbtt, atim, 1, intval) &&
728 ath5k_check_timer_win(dma, nbtt, AR5K_TUNE_DMA_BEACON_RESP,
729 intval))
730 return true; /* O.K. */
731 return false;
732}
733
734/**
644 * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class 735 * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class
645 * 736 *
646 * @ah: The &struct ath5k_hw 737 * @ah: The &struct ath5k_hw