IBSS ATIM window fixes

due to unlucky timing of beacon timer configuration (which we try to avoid) and
due to unlucky timing of local TSF updates (triggered by the reception of a
beacon with the same BSSID - something we can't avoid) the beacon timers (we
have 4) can be updated seperately, leaving one of them in the past, not beeing
updated until the timers wrap around. due to the fact that the beacon interval
does not fit into the timer period (16 bit) a whole number of times the size of
the ATIM window can get bigger than desired (we don't use ATIM right now so the
window size should always be 1).

this results in a phaenome described as "ramping" (ticket #1154) which is
actually a transmission delay, since the hardware is not allowed to transmit
data in the ATIM period.

the problem is described and discussed in length at
http://thread.gmane.org/gmane.linux.drivers.madwifi.devel/6066

since we don't know of a way to disable ATIM alltogether, the following adds
some workarounds to this problem:

 1.) disable interrupts in timing critical function ath_beacon_config

 2.) stop beacons before reconfiguring them

 3.) check ATIM window after critical code which might have changed the timers

   3a.) beacon configuration

   3b.) reception of a beacon with the same BSSID. the hardware will have updated
     the local TSF and this might have left one of the beacon timers in the past.


git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@3867 0192ed92-7a03-0410-a25b-9323aeb14dbd
This commit is contained in:
br1 2008-09-23 18:21:56 +00:00
parent ea9d467903
commit b327291039
3 changed files with 125 additions and 0 deletions

View File

@ -5388,12 +5388,16 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
u_int32_t tsftu, hw_tsftu;
u_int32_t intval, nexttbtt = 0;
int reset_tsf = 0;
unsigned long irqstate;
if (vap == NULL)
vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */
ni = vap->iv_bss;
/* TSF calculation is timing critical - we don't want to be interrupted here */
local_irq_save(irqstate);
hw_tsf = ath_hal_gettsf64(ah);
tsf = le64_to_cpu(ni->ni_tstamp.tsf);
hw_tsftu = IEEE80211_TSF_TO_TU(hw_tsf);
@ -5569,15 +5573,28 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap)
~(HAL_BEACON_RESET_TSF | HAL_BEACON_ENA));
#endif
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 (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(irqstate);
/* We print all debug messages here, in order to preserve the
* time critical aspect of this function. */
DPRINTF(sc, ATH_DEBUG_BEACON,
@ -6327,6 +6344,14 @@ ath_recv_mgmt(struct ieee80211vap * vap, struct ieee80211_node *ni_or_null,
DPRINTF(sc, ATH_DEBUG_BEACON,
"Updated beacon timers\n");
}
if (IEEE80211_ADDR_EQ(ni->ni_bssid, vap->iv_bss->ni_bssid)) {
if (ath_hw_check_atim(sc, 1, vap->iv_bss->ni_intval)) {
DPRINTF(sc, ATH_DEBUG_BEACON,
"fixed atim window after beacon recv\n");
}
}
/* NB: Fall Through */
case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
if (vap->iv_opmode == IEEE80211_M_IBSS &&

View File

@ -161,3 +161,72 @@ ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val)
return name;
}
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);
}
/*
* IBSS mode: check the ATIM window size and fix it if necessary.
*
* the need for this function arises from the problem that due to unlucky timing
* of beacon timer configuration (which we try to avoid) and due to unlucky
* timing of local TSF updates (triggered by the reception of a beacon with the
* same BSSID - something we can't avoid) the beacon timers can be up updated
* seperately, leaving one of them in the past, not beeing updated until the
* timers wrap around. due to the fact that the beacon interval does not fit
* into the timer period (16 bit) a whole number of times the size of the ATIM
* window can get bigger than desired.
*
* usually we have an ATIM window size of 1 but this function is written to
* handle other window sizes as well.
*/
int
ath_hw_check_atim(struct ath_softc *sc, int window, int intval)
{
struct ath_hal *ah = sc->sc_ah;
unsigned int nbtt, atim, is5210 = 0;
if (ATH_SREV_FROM_AH(ah) >= AR5K_SREV_VER_AR5416)
return 0; /* AR5416+ doesn't do ATIM in HW */
if (ATH_SREV_FROM_AH(ah) == AR5K_SREV_VER_AR5210) {
nbtt = OS_REG_READ(ah, AR5K_TIMER0_5210);
atim = OS_REG_READ(ah, AR5K_TIMER3_5210);
is5210 = 1;
}
else {
nbtt = OS_REG_READ(ah, AR5K_TIMER0_5211);
atim = OS_REG_READ(ah, AR5K_TIMER3_5211);
}
/*
* 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
*/
if ((atim - nbtt != window) && /* 1.) */
(nbtt - atim != intval - window) && /* 2.) */
((nbtt | 0x10000) - atim != intval - window) && /* 3.) */
((atim | 0x10000) - nbtt != window)) { /* 4.) */
if (is5210)
OS_REG_WRITE(ah, AR5K_TIMER3_5210, nbtt + window );
else
OS_REG_WRITE(ah, AR5K_TIMER3_5211, nbtt + window );
return atim - nbtt;
}
return 0;
}

View File

@ -176,6 +176,35 @@
#define AR5K_STA_ID1_ACKCTS_6MB 0x01000000 /* Use 6Mbit/s for ACK/CTS (?) */
#define AR5K_STA_ID1_BASE_RATE_11B 0x02000000 /* Use 11b base rate (for ACK/CTS ?) [5211+] */
/*
* PCU beacon control register
*/
#define AR5K_BEACON_5210 0x8024
#define AR5K_BEACON_5211 0x8020
/*
* Next beacon time register
*/
#define AR5K_TIMER0_5210 0x802c
#define AR5K_TIMER0_5211 0x8028
/*
* Next DMA beacon alert register
*/
#define AR5K_TIMER1_5210 0x8030
#define AR5K_TIMER1_5211 0x802c
/*
* Next software beacon alert register
*/
#define AR5K_TIMER2_5210 0x8034
#define AR5K_TIMER2_5211 0x8030
/*
* Next ATIM window time register
*/
#define AR5K_TIMER3_5210 0x8038
#define AR5K_TIMER3_5211 0x8034
enum ath5k_srev_type {
AR5K_VERSION_VER,
@ -241,6 +270,8 @@ enum ath5k_dmasize {
int ath_set_ack_bitrate(struct ath_softc *sc, int);
int ar_device(int devid);
const char * ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val);
void ath_hw_beacon_stop(struct ath_softc *sc);
int ath_hw_check_atim(struct ath_softc *sc, int window, int intval);
static inline unsigned long field_width(unsigned long mask, unsigned long shift)
{