|
@@ -407,12 +407,17 @@ void ath9k_beacon_tasklet(unsigned long data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt, u32 intval)
|
|
|
+/*
|
|
|
+ * Both nexttbtt and intval have to be in usecs.
|
|
|
+ */
|
|
|
+static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt,
|
|
|
+ u32 intval, bool reset_tsf)
|
|
|
{
|
|
|
struct ath_hw *ah = sc->sc_ah;
|
|
|
|
|
|
ath9k_hw_disable_interrupts(ah);
|
|
|
- ath9k_hw_reset_tsf(ah);
|
|
|
+ if (reset_tsf)
|
|
|
+ ath9k_hw_reset_tsf(ah);
|
|
|
ath9k_beaconq_config(sc);
|
|
|
ath9k_hw_beaconinit(ah, nexttbtt, intval);
|
|
|
sc->beacon.bmisscnt = 0;
|
|
@@ -442,10 +447,12 @@ static void ath9k_beacon_config_ap(struct ath_softc *sc,
|
|
|
else
|
|
|
ah->imask &= ~ATH9K_INT_SWBA;
|
|
|
|
|
|
- ath_dbg(common, BEACON, "AP nexttbtt: %u intval: %u conf_intval: %u\n",
|
|
|
+ ath_dbg(common, BEACON,
|
|
|
+ "AP (%s) nexttbtt: %u intval: %u conf_intval: %u\n",
|
|
|
+ (conf->enable_beacon) ? "Enable" : "Disable",
|
|
|
nexttbtt, intval, conf->beacon_interval);
|
|
|
|
|
|
- ath9k_beacon_init(sc, nexttbtt, intval);
|
|
|
+ ath9k_beacon_init(sc, nexttbtt, intval, true);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -586,17 +593,45 @@ static void ath9k_beacon_config_adhoc(struct ath_softc *sc,
|
|
|
ath9k_reset_beacon_status(sc);
|
|
|
|
|
|
intval = TU_TO_USEC(conf->beacon_interval);
|
|
|
- nexttbtt = intval;
|
|
|
+
|
|
|
+ if (conf->ibss_creator) {
|
|
|
+ nexttbtt = intval;
|
|
|
+ } else {
|
|
|
+ u32 tbtt, offset, tsftu;
|
|
|
+ u64 tsf;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Pull nexttbtt forward to reflect the current
|
|
|
+ * sync'd TSF.
|
|
|
+ */
|
|
|
+ tsf = ath9k_hw_gettsf64(ah);
|
|
|
+ tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE;
|
|
|
+ offset = tsftu % conf->beacon_interval;
|
|
|
+ tbtt = tsftu - offset;
|
|
|
+ if (offset)
|
|
|
+ tbtt += conf->beacon_interval;
|
|
|
+
|
|
|
+ nexttbtt = TU_TO_USEC(tbtt);
|
|
|
+ }
|
|
|
|
|
|
if (conf->enable_beacon)
|
|
|
ah->imask |= ATH9K_INT_SWBA;
|
|
|
else
|
|
|
ah->imask &= ~ATH9K_INT_SWBA;
|
|
|
|
|
|
- ath_dbg(common, BEACON, "IBSS nexttbtt: %u intval: %u conf_intval: %u\n",
|
|
|
+ ath_dbg(common, BEACON,
|
|
|
+ "IBSS (%s) nexttbtt: %u intval: %u conf_intval: %u\n",
|
|
|
+ (conf->enable_beacon) ? "Enable" : "Disable",
|
|
|
nexttbtt, intval, conf->beacon_interval);
|
|
|
|
|
|
- ath9k_beacon_init(sc, nexttbtt, intval);
|
|
|
+ ath9k_beacon_init(sc, nexttbtt, intval, conf->ibss_creator);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Set the global 'beacon has been configured' flag for the
|
|
|
+ * joiner case in IBSS mode.
|
|
|
+ */
|
|
|
+ if (!conf->ibss_creator && conf->enable_beacon)
|
|
|
+ set_bit(SC_OP_BEACONS, &sc->sc_flags);
|
|
|
}
|
|
|
|
|
|
bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
|
|
@@ -639,6 +674,7 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc,
|
|
|
cur_conf->dtim_period = bss_conf->dtim_period;
|
|
|
cur_conf->listen_interval = 1;
|
|
|
cur_conf->dtim_count = 1;
|
|
|
+ cur_conf->ibss_creator = bss_conf->ibss_creator;
|
|
|
cur_conf->bmiss_timeout =
|
|
|
ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
|
|
|
|
|
@@ -666,34 +702,59 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
|
|
|
{
|
|
|
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
|
|
|
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
|
|
|
+ unsigned long flags;
|
|
|
+ bool skip_beacon = false;
|
|
|
|
|
|
if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
|
|
|
ath9k_cache_beacon_config(sc, bss_conf);
|
|
|
ath9k_set_beacon(sc);
|
|
|
set_bit(SC_OP_BEACONS, &sc->sc_flags);
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * Take care of multiple interfaces when
|
|
|
- * enabling/disabling SWBA.
|
|
|
- */
|
|
|
- if (changed & BSS_CHANGED_BEACON_ENABLED) {
|
|
|
- if (!bss_conf->enable_beacon &&
|
|
|
- (sc->nbcnvifs <= 1)) {
|
|
|
- cur_conf->enable_beacon = false;
|
|
|
- } else if (bss_conf->enable_beacon) {
|
|
|
- cur_conf->enable_beacon = true;
|
|
|
- ath9k_cache_beacon_config(sc, bss_conf);
|
|
|
- }
|
|
|
+ return;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Take care of multiple interfaces when
|
|
|
+ * enabling/disabling SWBA.
|
|
|
+ */
|
|
|
+ if (changed & BSS_CHANGED_BEACON_ENABLED) {
|
|
|
+ if (!bss_conf->enable_beacon &&
|
|
|
+ (sc->nbcnvifs <= 1)) {
|
|
|
+ cur_conf->enable_beacon = false;
|
|
|
+ } else if (bss_conf->enable_beacon) {
|
|
|
+ cur_conf->enable_beacon = true;
|
|
|
+ ath9k_cache_beacon_config(sc, bss_conf);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (cur_conf->beacon_interval) {
|
|
|
+ /*
|
|
|
+ * Configure the HW beacon registers only when we have a valid
|
|
|
+ * beacon interval.
|
|
|
+ */
|
|
|
+ if (cur_conf->beacon_interval) {
|
|
|
+ /*
|
|
|
+ * If we are joining an existing IBSS network, start beaconing
|
|
|
+ * only after a TSF-sync has taken place. Ensure that this
|
|
|
+ * happens by setting the appropriate flags.
|
|
|
+ */
|
|
|
+ if ((changed & BSS_CHANGED_IBSS) && !bss_conf->ibss_creator &&
|
|
|
+ bss_conf->enable_beacon) {
|
|
|
+ spin_lock_irqsave(&sc->sc_pm_lock, flags);
|
|
|
+ sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
|
|
|
+ spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
|
|
|
+ skip_beacon = true;
|
|
|
+ } else {
|
|
|
ath9k_set_beacon(sc);
|
|
|
-
|
|
|
- if (cur_conf->enable_beacon)
|
|
|
- set_bit(SC_OP_BEACONS, &sc->sc_flags);
|
|
|
- else
|
|
|
- clear_bit(SC_OP_BEACONS, &sc->sc_flags);
|
|
|
}
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Do not set the SC_OP_BEACONS flag for IBSS joiner mode
|
|
|
+ * here, it is done in ath9k_beacon_config_adhoc().
|
|
|
+ */
|
|
|
+ if (cur_conf->enable_beacon && !skip_beacon)
|
|
|
+ set_bit(SC_OP_BEACONS, &sc->sc_flags);
|
|
|
+ else
|
|
|
+ clear_bit(SC_OP_BEACONS, &sc->sc_flags);
|
|
|
}
|
|
|
}
|
|
|
|