| 1 | --- a/ath/if_ath.c |
| 2 | +++ b/ath/if_ath.c |
| 3 | @@ -161,6 +161,7 @@ static void ath_beacon_send(struct ath_s |
| 4 | static void ath_beacon_return(struct ath_softc *, struct ath_buf *); |
| 5 | static void ath_beacon_free(struct ath_softc *); |
| 6 | static void ath_beacon_config(struct ath_softc *, struct ieee80211vap *); |
| 7 | +static void ath_hw_beacon_stop(struct ath_softc *sc); |
| 8 | static int ath_desc_alloc(struct ath_softc *); |
| 9 | static void ath_desc_free(struct ath_softc *); |
| 10 | static void ath_desc_swap(struct ath_desc *); |
| 11 | @@ -2793,6 +2794,72 @@ ath_set_ack_bitrate(struct ath_softc *sc |
| 12 | return 1; |
| 13 | } |
| 14 | |
| 15 | +static void |
| 16 | +ath_hw_beacon_stop(struct ath_softc *sc) |
| 17 | +{ |
| 18 | + HAL_BEACON_TIMERS btimers; |
| 19 | + |
| 20 | + btimers.bt_intval = 0; |
| 21 | + btimers.bt_nexttbtt = 0; |
| 22 | + btimers.bt_nextdba = 0xffffffff; |
| 23 | + btimers.bt_nextswba = 0xffffffff; |
| 24 | + btimers.bt_nextatim = 0; |
| 25 | + |
| 26 | + ath_hal_setbeacontimers(sc->sc_ah, &btimers); |
| 27 | +} |
| 28 | + |
| 29 | +/* Fix up the ATIM window after TSF resync */ |
| 30 | +static int |
| 31 | +ath_hw_check_atim(struct ath_softc *sc, int window, int intval) |
| 32 | +{ |
| 33 | +#define AR5K_TIMER0_5210 0x802c /* Next beacon time register */ |
| 34 | +#define AR5K_TIMER0_5211 0x8028 |
| 35 | +#define AR5K_TIMER3_5210 0x8038 /* End of ATIM window time register */ |
| 36 | +#define AR5K_TIMER3_5211 0x8034 |
| 37 | + struct ath_hal *ah = sc->sc_ah; |
| 38 | + int dev = sc->sc_ah->ah_macType; |
| 39 | + unsigned int nbtt, atim; |
| 40 | + int is_5210 = 0; |
| 41 | + |
| 42 | + /* |
| 43 | + * check if the ATIM window is still correct: |
| 44 | + * 1.) usually ATIM should be NBTT + window |
| 45 | + * 2.) nbtt already updated |
| 46 | + * 3.) nbtt already updated and has wrapped around |
| 47 | + * 4.) atim has wrapped around |
| 48 | + */ |
| 49 | + switch(dev) { |
| 50 | + case 5210: |
| 51 | + nbtt = OS_REG_READ(ah, AR5K_TIMER0_5210); |
| 52 | + atim = OS_REG_READ(ah, AR5K_TIMER3_5210); |
| 53 | + is_5210 = 1; |
| 54 | + break; |
| 55 | + case 5211: |
| 56 | + case 5212: |
| 57 | + nbtt = OS_REG_READ(ah, AR5K_TIMER0_5211); |
| 58 | + atim = OS_REG_READ(ah, AR5K_TIMER3_5211); |
| 59 | + break; |
| 60 | + /* NB: 5416+ doesn't do ATIM in hw */ |
| 61 | + case 5416: |
| 62 | + default: |
| 63 | + return 0; |
| 64 | + } |
| 65 | + |
| 66 | + if ((atim - nbtt != window) && /* 1.) */ |
| 67 | + (nbtt - atim != intval - window) && /* 2.) */ |
| 68 | + ((nbtt | 0x10000) - atim != intval - window) && /* 3.) */ |
| 69 | + ((atim | 0x10000) - nbtt != window)) { /* 4.) */ |
| 70 | + if (is_5210) |
| 71 | + OS_REG_WRITE(ah, AR5K_TIMER3_5210, nbtt + window ); |
| 72 | + else |
| 73 | + OS_REG_WRITE(ah, AR5K_TIMER3_5211, nbtt + window ); |
| 74 | + return atim - nbtt; |
| 75 | + } |
| 76 | + |
| 77 | + return 0; |
| 78 | +} |
| 79 | + |
| 80 | + |
| 81 | /* |
| 82 | * Reset the hardware w/o losing operational state. This is |
| 83 | * basically a more efficient way of doing ath_stop, ath_init, |
| 84 | @@ -5294,6 +5361,7 @@ ath_beacon_config(struct ath_softc *sc, |
| 85 | u_int64_t tsf, hw_tsf; |
| 86 | u_int32_t tsftu, hw_tsftu; |
| 87 | u_int32_t intval, nexttbtt = 0; |
| 88 | + unsigned long flags; |
| 89 | int reset_tsf = 0; |
| 90 | |
| 91 | if (vap == NULL) |
| 92 | @@ -5301,6 +5369,9 @@ ath_beacon_config(struct ath_softc *sc, |
| 93 | |
| 94 | ni = vap->iv_bss; |
| 95 | |
| 96 | + /* TSF calculation is timing critical - we don't want to be interrupted here */ |
| 97 | + local_irq_save(flags); |
| 98 | + |
| 99 | hw_tsf = ath_hal_gettsf64(ah); |
| 100 | tsf = le64_to_cpu(ni->ni_tstamp.tsf); |
| 101 | hw_tsftu = hw_tsf >> 10; |
| 102 | @@ -5490,15 +5561,27 @@ ath_beacon_config(struct ath_softc *sc, |
| 103 | <= ath_hal_sw_beacon_response_time) |
| 104 | nexttbtt += intval; |
| 105 | sc->sc_nexttbtt = nexttbtt; |
| 106 | + |
| 107 | + /* stop beacons before reconfiguring the timers to avoid race |
| 108 | + * conditions. ath_hal_beaconinit will start them again */ |
| 109 | + ath_hw_beacon_stop(sc); |
| 110 | + |
| 111 | ath_hal_beaconinit(ah, nexttbtt, intval); |
| 112 | if (intval & HAL_BEACON_RESET_TSF) { |
| 113 | sc->sc_last_tsf = 0; |
| 114 | } |
| 115 | sc->sc_bmisscount = 0; |
| 116 | ath_hal_intrset(ah, sc->sc_imask); |
| 117 | + |
| 118 | + if ((sc->sc_opmode == HAL_M_IBSS) && ath_hw_check_atim(sc, 1, intval & HAL_BEACON_PERIOD)) { |
| 119 | + DPRINTF(sc, ATH_DEBUG_BEACON, |
| 120 | + "fixed atim window after beacon init\n"); |
| 121 | + } |
| 122 | } |
| 123 | |
| 124 | ath_beacon_config_debug: |
| 125 | + local_irq_restore(flags); |
| 126 | + |
| 127 | /* We print all debug messages here, in order to preserve the |
| 128 | * time critical aspect of this function */ |
| 129 | DPRINTF(sc, ATH_DEBUG_BEACON, |
| 130 | @@ -6401,6 +6484,11 @@ ath_recv_mgmt(struct ieee80211vap * vap, |
| 131 | DPRINTF(sc, ATH_DEBUG_BEACON, |
| 132 | "Updated beacon timers\n"); |
| 133 | } |
| 134 | + if ((sc->sc_opmode == HAL_M_IBSS) && |
| 135 | + IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid) && |
| 136 | + ath_hw_check_atim(sc, 1, vap->iv_bss->ni_intval)) { |
| 137 | + DPRINTF(sc, ATH_DEBUG_ANY, "Fixed ATIM window after beacon recv\n"); |
| 138 | + } |
| 139 | /* NB: Fall Through */ |
| 140 | case IEEE80211_FC0_SUBTYPE_PROBE_RESP: |
| 141 | if (vap->iv_opmode == IEEE80211_M_IBSS && |
| 142 | |