ath9k: Split beacon configuration into mode specific routines

This makes the code easier to understand.

Signed-off-by: Sujith <Sujith.Manoharan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Sujith 2009-03-03 10:16:54 +05:30 committed by John W. Linville
parent 9546aae086
commit 5379c8a266
3 changed files with 211 additions and 215 deletions

View File

@ -458,7 +458,6 @@ void ath_beacon_config(struct ath_softc *sc, int if_id);
int ath_beaconq_setup(struct ath_hw *ah);
int ath_beacon_alloc(struct ath_softc *sc, int if_id);
void ath_beacon_return(struct ath_softc *sc, struct ath_vif *avp);
void ath_beacon_sync(struct ath_softc *sc, int if_id);
/*******/
/* ANI */

View File

@ -16,6 +16,8 @@
#include "ath9k.h"
#define FUDGE 2
/*
* This function will modify certain transmit queue properties depending on
* the operating mode of the station (AP or AdHoc). Parameters are AIFS
@ -498,39 +500,204 @@ void ath_beacon_tasklet(unsigned long data)
}
/*
* Configure the beacon and sleep timers.
*
* When operating as an AP this resets the TSF and sets
* up the hardware to notify us when we need to issue beacons.
*
* When operating in station mode this sets up the beacon
* timers according to the timestamp of the last received
* beacon and the current TSF, configures PCF and DTIM
* handling, programs the sleep registers so the hardware
* will wakeup in time to receive beacons, and configures
* the beacon miss handling so we'll receive a BMISS
* interrupt when we stop seeing beacons from the AP
* we've associated with.
* For multi-bss ap support beacons are either staggered evenly over N slots or
* burst together. For the former arrange for the SWBA to be delivered for each
* slot. Slots that are not occupied will generate nothing.
*/
void ath_beacon_config(struct ath_softc *sc, int if_id)
static void ath_beacon_config_ap(struct ath_softc *sc,
struct ath_beacon_config *conf,
struct ath_vif *avp)
{
struct ieee80211_vif *vif;
struct ath_hw *ah = sc->sc_ah;
struct ath_beacon_config conf;
struct ath_vif *avp;
enum nl80211_iftype opmode;
u32 nexttbtt, intval;
if (if_id != ATH_IF_ID_ANY) {
vif = sc->vifs[if_id];
avp = (void *)vif->drv_priv;
opmode = avp->av_opmode;
/* NB: the beacon interval is kept internally in TU's */
intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
intval /= ATH_BCBUF; /* for staggered beacons */
nexttbtt = intval;
intval |= ATH9K_BEACON_RESET_TSF;
/*
* In AP mode we enable the beacon timers and SWBA interrupts to
* prepare beacon frames.
*/
intval |= ATH9K_BEACON_ENA;
sc->imask |= ATH9K_INT_SWBA;
ath_beaconq_config(sc);
/* Set the computed AP beacon timers */
ath9k_hw_set_interrupts(sc->sc_ah, 0);
ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
}
/*
* This sets up the beacon timers according to the timestamp of the last
* received beacon and the current TSF, configures PCF and DTIM
* handling, programs the sleep registers so the hardware will wakeup in
* time to receive beacons, and configures the beacon miss handling so
* we'll receive a BMISS interrupt when we stop seeing beacons from the AP
* we've associated with.
*/
static void ath_beacon_config_sta(struct ath_softc *sc,
struct ath_beacon_config *conf,
struct ath_vif *avp)
{
struct ath9k_beacon_state bs;
int dtimperiod, dtimcount, sleepduration;
int cfpperiod, cfpcount;
u32 nexttbtt = 0, intval, tsftu;
u64 tsf;
memset(&bs, 0, sizeof(bs));
intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
/*
* Setup dtim and cfp parameters according to
* last beacon we received (which may be none).
*/
dtimperiod = conf->dtim_period;
if (dtimperiod <= 0) /* NB: 0 if not known */
dtimperiod = 1;
dtimcount = conf->dtim_count;
if (dtimcount >= dtimperiod) /* NB: sanity check */
dtimcount = 0;
cfpperiod = 1; /* NB: no PCF support yet */
cfpcount = 0;
sleepduration = conf->listen_interval * intval;
if (sleepduration <= 0)
sleepduration = intval;
/*
* Pull nexttbtt forward to reflect the current
* TSF and calculate dtim+cfp state for the result.
*/
tsf = ath9k_hw_gettsf64(sc->sc_ah);
tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
do {
nexttbtt += intval;
if (--dtimcount < 0) {
dtimcount = dtimperiod - 1;
if (--cfpcount < 0)
cfpcount = cfpperiod - 1;
}
} while (nexttbtt < tsftu);
bs.bs_intval = intval;
bs.bs_nexttbtt = nexttbtt;
bs.bs_dtimperiod = dtimperiod*intval;
bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
bs.bs_cfpmaxduration = 0;
/*
* Calculate the number of consecutive beacons to miss* before taking
* a BMISS interrupt. The configuration is specified in TU so we only
* need calculate based on the beacon interval. Note that we clamp the
* result to at most 15 beacons.
*/
if (sleepduration > intval) {
bs.bs_bmissthreshold = conf->listen_interval *
ATH_DEFAULT_BMISS_LIMIT / 2;
} else {
opmode = sc->sc_ah->opmode;
bs.bs_bmissthreshold = DIV_ROUND_UP(conf->bmiss_timeout, intval);
if (bs.bs_bmissthreshold > 15)
bs.bs_bmissthreshold = 15;
else if (bs.bs_bmissthreshold <= 0)
bs.bs_bmissthreshold = 1;
}
memset(&conf, 0, sizeof(struct ath_beacon_config));
/*
* Calculate sleep duration. The configuration is given in ms.
* We ensure a multiple of the beacon period is used. Also, if the sleep
* duration is greater than the DTIM period then it makes senses
* to make it a multiple of that.
*
* XXX fixed at 100ms
*/
bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration);
if (bs.bs_sleepduration > bs.bs_dtimperiod)
bs.bs_sleepduration = bs.bs_dtimperiod;
/* TSF out of range threshold fixed at 1 second */
bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
DPRINTF(sc, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu);
DPRINTF(sc, ATH_DBG_BEACON,
"bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
bs.bs_bmissthreshold, bs.bs_sleepduration,
bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext);
/* Set the computed STA beacon timers */
ath9k_hw_set_interrupts(sc->sc_ah, 0);
ath9k_hw_set_sta_beacon_timers(sc->sc_ah, &bs);
sc->imask |= ATH9K_INT_BMISS;
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
}
static void ath_beacon_config_adhoc(struct ath_softc *sc,
struct ath_beacon_config *conf,
struct ath_vif *avp)
{
u64 tsf;
u32 tsftu, intval, nexttbtt;
intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
/* Pull nexttbtt forward to reflect the current TSF */
nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp);
if (nexttbtt == 0)
nexttbtt = intval;
else if (intval)
nexttbtt = roundup(nexttbtt, intval);
tsf = ath9k_hw_gettsf64(sc->sc_ah);
tsftu = TSF_TO_TU((u32)(tsf>>32), (u32)tsf) + FUDGE;
do {
nexttbtt += intval;
} while (nexttbtt < tsftu);
DPRINTF(sc, ATH_DBG_BEACON,
"IBSS nexttbtt %u intval %u (%u)\n",
nexttbtt, intval, conf->beacon_interval);
/*
* In IBSS mode enable the beacon timers but only enable SWBA interrupts
* if we need to manually prepare beacon frames. Otherwise we use a
* self-linked tx descriptor and let the hardware deal with things.
*/
intval |= ATH9K_BEACON_ENA;
if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL))
sc->imask |= ATH9K_INT_SWBA;
ath_beaconq_config(sc);
/* Set the computed ADHOC beacon timers */
ath9k_hw_set_interrupts(sc->sc_ah, 0);
ath9k_hw_beaconinit(sc->sc_ah, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)
ath_beacon_start_adhoc(sc, 0);
}
void ath_beacon_config(struct ath_softc *sc, int if_id)
{
struct ath_beacon_config conf;
struct ath_vif *avp;
struct ieee80211_vif *vif;
/* Setup the beacon configuration parameters */
memset(&conf, 0, sizeof(struct ath_beacon_config));
conf.beacon_interval = sc->hw->conf.beacon_int ?
sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL;
conf.listen_interval = 1;
@ -538,195 +705,26 @@ void ath_beacon_config(struct ath_softc *sc, int if_id)
conf.dtim_count = 1;
conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval;
/* extract tstamp from last beacon and convert to TU */
nexttbtt = TSF_TO_TU(sc->beacon.bc_tstamp >> 32, sc->beacon.bc_tstamp);
if (if_id != ATH_IF_ID_ANY) {
vif = sc->vifs[if_id];
avp = (struct ath_vif *)vif->drv_priv;
/* XXX conditionalize multi-bss support? */
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
/*
* For multi-bss ap support beacons are either staggered
* evenly over N slots or burst together. For the former
* arrange for the SWBA to be delivered for each slot.
* Slots that are not occupied will generate nothing.
*/
/* NB: the beacon interval is kept internally in TU's */
intval = conf.beacon_interval & ATH9K_BEACON_PERIOD;
intval /= ATH_BCBUF; /* for staggered beacons */
} else {
intval = conf.beacon_interval & ATH9K_BEACON_PERIOD;
}
if (nexttbtt == 0) /* e.g. for ap mode */
nexttbtt = intval;
else if (intval) /* NB: can be 0 for monitor mode */
nexttbtt = roundup(nexttbtt, intval);
DPRINTF(sc, ATH_DBG_BEACON, "nexttbtt %u intval %u (%u)\n",
nexttbtt, intval, conf.beacon_interval);
/* Check for NL80211_IFTYPE_AP and sc_nostabeacons for WDS client */
if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
struct ath9k_beacon_state bs;
u64 tsf;
u32 tsftu;
int dtimperiod, dtimcount, sleepduration;
int cfpperiod, cfpcount;
/*
* Setup dtim and cfp parameters according to
* last beacon we received (which may be none).
*/
dtimperiod = conf.dtim_period;
if (dtimperiod <= 0) /* NB: 0 if not known */
dtimperiod = 1;
dtimcount = conf.dtim_count;
if (dtimcount >= dtimperiod) /* NB: sanity check */
dtimcount = 0;
cfpperiod = 1; /* NB: no PCF support yet */
cfpcount = 0;
sleepduration = conf.listen_interval * intval;
if (sleepduration <= 0)
sleepduration = intval;
#define FUDGE 2
/*
* Pull nexttbtt forward to reflect the current
* TSF and calculate dtim+cfp state for the result.
*/
tsf = ath9k_hw_gettsf64(ah);
tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
do {
nexttbtt += intval;
if (--dtimcount < 0) {
dtimcount = dtimperiod - 1;
if (--cfpcount < 0)
cfpcount = cfpperiod - 1;
}
} while (nexttbtt < tsftu);
#undef FUDGE
memset(&bs, 0, sizeof(bs));
bs.bs_intval = intval;
bs.bs_nexttbtt = nexttbtt;
bs.bs_dtimperiod = dtimperiod*intval;
bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
bs.bs_cfpmaxduration = 0;
/*
* Calculate the number of consecutive beacons to miss
* before taking a BMISS interrupt. The configuration
* is specified in TU so we only need calculate based
* on the beacon interval. Note that we clamp the
* result to at most 15 beacons.
*/
if (sleepduration > intval) {
bs.bs_bmissthreshold = conf.listen_interval *
ATH_DEFAULT_BMISS_LIMIT / 2;
} else {
bs.bs_bmissthreshold =
DIV_ROUND_UP(conf.bmiss_timeout, intval);
if (bs.bs_bmissthreshold > 15)
bs.bs_bmissthreshold = 15;
else if (bs.bs_bmissthreshold <= 0)
bs.bs_bmissthreshold = 1;
switch(avp->av_opmode) {
case NL80211_IFTYPE_AP:
ath_beacon_config_ap(sc, &conf, avp);
break;
case NL80211_IFTYPE_ADHOC:
ath_beacon_config_adhoc(sc, &conf, avp);
break;
case NL80211_IFTYPE_STATION:
ath_beacon_config_sta(sc, &conf, avp);
break;
default:
DPRINTF(sc, ATH_DBG_CONFIG,
"Unsupported beaconing mode\n");
return;
}
/*
* Calculate sleep duration. The configuration is
* given in ms. We insure a multiple of the beacon
* period is used. Also, if the sleep duration is
* greater than the DTIM period then it makes senses
* to make it a multiple of that.
*
* XXX fixed at 100ms
*/
bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100),
sleepduration);
if (bs.bs_sleepduration > bs.bs_dtimperiod)
bs.bs_sleepduration = bs.bs_dtimperiod;
/* TSF out of range threshold fixed at 1 second */
bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
DPRINTF(sc, ATH_DBG_BEACON,
"tsf: %llu tsftu: %u\n", tsf, tsftu);
DPRINTF(sc, ATH_DBG_BEACON,
"bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
bs.bs_bmissthreshold, bs.bs_sleepduration,
bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext);
ath9k_hw_set_interrupts(ah, 0);
ath9k_hw_set_sta_beacon_timers(ah, &bs);
sc->imask |= ATH9K_INT_BMISS;
ath9k_hw_set_interrupts(ah, sc->imask);
} else {
u64 tsf;
u32 tsftu;
ath9k_hw_set_interrupts(ah, 0);
if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) {
/* Pull nexttbtt forward to reflect the current TSF */
#define FUDGE 2
if (!(intval & ATH9K_BEACON_RESET_TSF)) {
tsf = ath9k_hw_gettsf64(ah);
tsftu = TSF_TO_TU((u32)(tsf>>32),
(u32)tsf) + FUDGE;
do {
nexttbtt += intval;
} while (nexttbtt < tsftu);
}
#undef FUDGE
DPRINTF(sc, ATH_DBG_BEACON,
"IBSS nexttbtt %u intval %u (%u)\n",
nexttbtt, intval & ~ATH9K_BEACON_RESET_TSF,
conf.beacon_interval);
/*
* In IBSS mode enable the beacon timers but only
* enable SWBA interrupts if we need to manually
* prepare beacon frames. Otherwise we use a
* self-linked tx descriptor and let the hardware
* deal with things.
*/
intval |= ATH9K_BEACON_ENA;
if (!(ah->caps.hw_caps & ATH9K_HW_CAP_VEOL))
sc->imask |= ATH9K_INT_SWBA;
ath_beaconq_config(sc);
} else if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
if (nexttbtt == intval)
intval |= ATH9K_BEACON_RESET_TSF;
/*
* In AP mode we enable the beacon timers and
* SWBA interrupts to prepare beacon frames.
*/
intval |= ATH9K_BEACON_ENA;
sc->imask |= ATH9K_INT_SWBA;
ath_beaconq_config(sc);
}
ath9k_hw_beaconinit(ah, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(ah, sc->imask);
/*
* When using a self-linked beacon descriptor in
* ibss mode load it once here.
*/
if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC &&
(ah->caps.hw_caps & ATH9K_HW_CAP_VEOL))
ath_beacon_start_adhoc(sc, 0);
sc->sc_flags |= SC_OP_BEACONS;
}
}
void ath_beacon_sync(struct ath_softc *sc, int if_id)
{
/*
* Resync beacon timers using the tsf of the
* beacon frame we just received.
*/
ath_beacon_config(sc, if_id);
sc->sc_flags |= SC_OP_BEACONS;
}

View File

@ -926,7 +926,6 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
/* Configure the beacon */
ath_beacon_config(sc, 0);
sc->sc_flags |= SC_OP_BEACONS;
/* Reset rssi stats */
sc->nodestats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
@ -2365,7 +2364,7 @@ static int ath9k_config_interface(struct ieee80211_hw *hw,
if (error != 0)
return error;
ath_beacon_sync(sc, 0);
ath_beacon_config(sc, 0);
}
}