diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath5k/base.c | 109 |
1 files changed, 92 insertions, 17 deletions
diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index de590163a07..c49061c5168 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -290,6 +290,7 @@ static int ath5k_beacon_setup(struct ath5k_softc *sc, struct ieee80211_tx_control *ctl); static void ath5k_beacon_send(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 inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) { @@ -1984,34 +1985,102 @@ ath5k_beacon_send(struct ath5k_softc *sc) } +/** + * ath5k_beacon_update_timers - update beacon timers + * + * @sc: struct ath5k_softc pointer we are operating on + * @bc_tsf: the timestamp of the beacon. 0 to reset the TSF. -1 to perform a + * beacon timer update based on the current HW TSF. + * + * Calculate the next target beacon transmit time (TBTT) based on the timestamp + * of a received beacon or the current local hardware TSF and write it to the + * beacon timer registers. + * + * This is called in a variety of situations, e.g. when a beacon is received, + * when a HW merge has been detected, but also when an new IBSS is created or + * when we otherwise know we have to update the timers, but we keep it in this + * function to have it all together in one place. + */ static void -ath5k_beacon_update_timers(struct ath5k_softc *sc) +ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf) { struct ath5k_hw *ah = sc->ah; - u32 uninitialized_var(nexttbtt), intval, tsftu; - u64 tsf; + u32 nexttbtt, intval, hw_tu, bc_tu; + u64 hw_tsf; intval = sc->bintval & AR5K_BEACON_PERIOD; if (WARN_ON(!intval)) return; - /* current TSF converted to TU */ - tsf = ath5k_hw_get_tsf64(ah); - tsftu = TSF_TO_TU(tsf); + /* beacon TSF converted to TU */ + bc_tu = TSF_TO_TU(bc_tsf); - /* - * Pull nexttbtt forward to reflect the current - * TSF. Add one intval otherwise the timespan - * can be too short for ibss merges. - */ - nexttbtt = tsftu + 2 * intval; + /* current TSF converted to TU */ + hw_tsf = ath5k_hw_get_tsf64(ah); + hw_tu = TSF_TO_TU(hw_tsf); - ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, - "hw tsftu %u nexttbtt %u intval %u\n", tsftu, nexttbtt, intval); +#define FUDGE 3 + /* we use FUDGE to make sure the next TBTT is ahead of the current TU */ + if (bc_tsf == -1) { + /* + * no beacons received, called internally. + * just need to refresh timers based on HW TSF. + */ + nexttbtt = roundup(hw_tu + FUDGE, intval); + } else if (bc_tsf == 0) { + /* + * no beacon received, probably called by ath5k_reset_tsf(). + * reset TSF to start with 0. + */ + nexttbtt = intval; + intval |= AR5K_BEACON_RESET_TSF; + } else if (bc_tsf > hw_tsf) { + /* + * beacon received, SW merge happend but HW TSF not yet updated. + * not possible to reconfigure timers yet, but next time we + * receive a beacon with the same BSSID, the hardware will + * automatically update the TSF and then we need to reconfigure + * the timers. + */ + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "need to wait for HW TSF sync\n"); + return; + } else { + /* + * most important case for beacon synchronization between STA. + * + * beacon received and HW TSF has been already updated by HW. + * update next TBTT based on the TSF of the beacon, but make + * sure it is ahead of our local TSF timer. + */ + nexttbtt = bc_tu + roundup(hw_tu + FUDGE - bc_tu, intval); + } +#undef FUDGE intval |= AR5K_BEACON_ENA; - ath5k_hw_init_beacon(ah, nexttbtt, intval); + + /* + * debugging output last in order to preserve the time critical aspect + * of this function + */ + if (bc_tsf == -1) + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "reconfigured timers based on HW TSF\n"); + else if (bc_tsf == 0) + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "reset HW TSF and timers\n"); + else + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "updated timers based on beacon TSF\n"); + + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, + "bc_tsf %llx hw_tsf %llx bc_tu %u hw_tu %u nexttbtt %u\n", + bc_tsf, hw_tsf, bc_tu, hw_tu, nexttbtt); + ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "intval %u %s %s\n", + intval & AR5K_BEACON_PERIOD, + intval & AR5K_BEACON_ENA ? "AR5K_BEACON_ENA" : "", + intval & AR5K_BEACON_RESET_TSF ? "AR5K_BEACON_RESET_TSF" : ""); } @@ -2045,7 +2114,6 @@ ath5k_beacon_config(struct ath5k_softc *sc) * only once here. */ ath5k_beaconq_config(sc); - ath5k_beacon_update_timers(sc); if (!ath5k_hw_hasveol(ah)) sc->imask |= AR5K_INT_SWBA; @@ -2795,7 +2863,14 @@ ath5k_reset_tsf(struct ieee80211_hw *hw) { struct ath5k_softc *sc = hw->priv; - ath5k_hw_reset_tsf(sc->ah); + /* + * in IBSS mode we need to update the beacon timers too. + * this will also reset the TSF if we call it with 0 + */ + if (sc->opmode == IEEE80211_IF_TYPE_IBSS) + ath5k_beacon_update_timers(sc, 0); + else + ath5k_hw_reset_tsf(sc->ah); } static int |