diff options
author | Bruno Randolf <br1@einfach.org> | 2010-09-26 23:22:21 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-09-28 15:47:57 -0400 |
commit | 7f896126017830b29cf501d246ee32b81e359acd (patch) | |
tree | c7c1444a291fffb91ef96c6cae281f821c8ff8d0 /drivers | |
parent | 4a79f2c517cce31c3b25aab0ec5078368b22c363 (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.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath5k/base.c | 9 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath5k/pcu.c | 91 |
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); | |||
1195 | void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64); | 1195 | void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64); |
1196 | void ath5k_hw_reset_tsf(struct ath5k_hw *ah); | 1196 | void ath5k_hw_reset_tsf(struct ath5k_hw *ah); |
1197 | void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval); | 1197 | void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval); |
1198 | bool ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval); | ||
1198 | /* ACK bit rate */ | 1199 | /* ACK bit rate */ |
1199 | void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high); | 1200 | void 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 | */ | ||
657 | static inline bool | ||
658 | ath5k_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 | */ | ||
714 | bool | ||
715 | ath5k_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 |