--- a/ath/if_ath.c +++ b/ath/if_ath.c @@ -161,6 +161,7 @@ static void ath_beacon_send(struct ath_s static void ath_beacon_return(struct ath_softc *, struct ath_buf *); static void ath_beacon_free(struct ath_softc *); static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *); +static void ath_hw_beacon_stop(struct ath_softc *sc); static int ath_desc_alloc(struct ath_softc *); static void ath_desc_free(struct ath_softc *); static void ath_desc_swap(struct ath_desc *); @@ -2793,6 +2794,72 @@ ath_set_ack_bitrate(struct ath_softc *sc return 1; } +static void +ath_hw_beacon_stop(struct ath_softc *sc) +{ + HAL_BEACON_TIMERS btimers; + + btimers.bt_intval = 0; + btimers.bt_nexttbtt = 0; + btimers.bt_nextdba = 0xffffffff; + btimers.bt_nextswba = 0xffffffff; + btimers.bt_nextatim = 0; + + ath_hal_setbeacontimers(sc->sc_ah, &btimers); +} + +/* Fix up the ATIM window after TSF resync */ +static int +ath_hw_check_atim(struct ath_softc *sc, int window, int intval) +{ +#define AR5K_TIMER0_5210 0x802c /* Next beacon time register */ +#define AR5K_TIMER0_5211 0x8028 +#define AR5K_TIMER3_5210 0x8038 /* End of ATIM window time register */ +#define AR5K_TIMER3_5211 0x8034 + struct ath_hal *ah = sc->sc_ah; + int dev = sc->sc_ah->ah_macType; + unsigned int nbtt, atim; + int is_5210 = 0; + + /* + * check if the ATIM window is still correct: + * 1.) usually ATIM should be NBTT + window + * 2.) nbtt already updated + * 3.) nbtt already updated and has wrapped around + * 4.) atim has wrapped around + */ + switch(dev) { + case 5210: + nbtt = OS_REG_READ(ah, AR5K_TIMER0_5210); + atim = OS_REG_READ(ah, AR5K_TIMER3_5210); + is_5210 = 1; + break; + case 5211: + case 5212: + nbtt = OS_REG_READ(ah, AR5K_TIMER0_5211); + atim = OS_REG_READ(ah, AR5K_TIMER3_5211); + break; + /* NB: 5416+ doesn't do ATIM in hw */ + case 5416: + default: + return 0; + } + + if ((atim - nbtt != window) && /* 1.) */ + (nbtt - atim != intval - window) && /* 2.) */ + ((nbtt | 0x10000) - atim != intval - window) && /* 3.) */ + ((atim | 0x10000) - nbtt != window)) { /* 4.) */ + if (is_5210) + OS_REG_WRITE(ah, AR5K_TIMER3_5210, nbtt + window ); + else + OS_REG_WRITE(ah, AR5K_TIMER3_5211, nbtt + window ); + return atim - nbtt; + } + + return 0; +} + + /* * Reset the hardware w/o losing operational state. This is * basically a more efficient way of doing ath_stop, ath_init, @@ -5294,6 +5361,7 @@ ath_beacon_config(struct ath_softc *sc, u_int64_t tsf, hw_tsf; u_int32_t tsftu, hw_tsftu; u_int32_t intval, nexttbtt = 0; + unsigned long flags; int reset_tsf = 0; if (vap == NULL) @@ -5301,6 +5369,9 @@ ath_beacon_config(struct ath_softc *sc, ni = vap->iv_bss; + /* TSF calculation is timing critical - we don't want to be interrupted here */ + local_irq_save(flags); + hw_tsf = ath_hal_gettsf64(ah); tsf = le64_to_cpu(ni->ni_tstamp.tsf); hw_tsftu = hw_tsf >> 10; @@ -5490,15 +5561,27 @@ ath_beacon_config(struct ath_softc *sc, <= ath_hal_sw_beacon_response_time) nexttbtt += intval; sc->sc_nexttbtt = nexttbtt; + + /* stop beacons before reconfiguring the timers to avoid race + * conditions. ath_hal_beaconinit will start them again */ + ath_hw_beacon_stop(sc); + ath_hal_beaconinit(ah, nexttbtt, intval); if (intval & HAL_BEACON_RESET_TSF) { sc->sc_last_tsf = 0; } sc->sc_bmisscount = 0; ath_hal_intrset(ah, sc->sc_imask); + + if ((sc->sc_opmode == HAL_M_IBSS) && ath_hw_check_atim(sc, 1, intval & HAL_BEACON_PERIOD)) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "fixed atim window after beacon init\n"); + } } ath_beacon_config_debug: + local_irq_restore(flags); + /* We print all debug messages here, in order to preserve the * time critical aspect of this function */ DPRINTF(sc, ATH_DEBUG_BEACON, @@ -6401,6 +6484,11 @@ ath_recv_mgmt(struct ieee80211vap * vap, DPRINTF(sc, ATH_DEBUG_BEACON, "Updated beacon timers\n"); } + if ((sc->sc_opmode == HAL_M_IBSS) && + IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid) && + ath_hw_check_atim(sc, 1, vap->iv_bss->ni_intval)) { + DPRINTF(sc, ATH_DEBUG_ANY, "Fixed ATIM window after beacon recv\n"); + } /* NB: Fall Through */ case IEEE80211_FC0_SUBTYPE_PROBE_RESP: if (vap->iv_opmode == IEEE80211_M_IBSS &&