summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoradrian <adrian@FreeBSD.org>2014-05-02 00:48:09 +0000
committeradrian <adrian@FreeBSD.org>2014-05-02 00:48:09 +0000
commite3232dbc02b6293bdbc6761eba9be7efb73fcee2 (patch)
treeee4ab4c55f829b829ccf57d951715d2734605390
parentb735ae5b9adff0d10136045496841e43c66321d8 (diff)
downloadFreeBSD-src-e3232dbc02b6293bdbc6761eba9be7efb73fcee2.zip
FreeBSD-src-e3232dbc02b6293bdbc6761eba9be7efb73fcee2.tar.gz
Add tracking for self-generated frames when the VAP is in sleep state.
The hardware can generate its own frames (eg RTS/CTS exchanges, other kinds of 802.11 management stuff, especially when it comes to 802.11n) and these also have PWRMGT flags. So if the VAP is asleep but the NIC is in force-awake for some reason, ensure that the self-generated frames have PWRMGT set to 1. Now, this (like basically everything to do with powersave) is still racy - the only way to guarantee that it's all actually consistent is to pause transmit and let it finish before transitioning the VAP to sleep, but this at least gets the basic method of tracking and updating the state debugged. Tested: * AR5416, STA mode * AR9380, STA mode
-rw-r--r--sys/dev/ath/if_ath.c110
-rw-r--r--sys/dev/ath/if_ath_misc.h2
-rw-r--r--sys/dev/ath/if_athvar.h11
3 files changed, 112 insertions, 11 deletions
diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index e2e2f91..ad8b0d9 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -305,6 +305,55 @@ _ath_power_setpower(struct ath_softc *sc, int power_state, const char *file, int
power_state != sc->sc_cur_powerstate) {
sc->sc_cur_powerstate = power_state;
ath_hal_setpower(sc->sc_ah, power_state);
+
+ /*
+ * If the NIC is force-awake, then set the
+ * self-gen frame state appropriately.
+ *
+ * If the nic is in network sleep or full-sleep,
+ * we let the above call leave the self-gen
+ * state as "sleep".
+ */
+ if (sc->sc_cur_powerstate == HAL_PM_AWAKE &&
+ sc->sc_target_selfgen_state != HAL_PM_AWAKE) {
+ ath_hal_setselfgenpower(sc->sc_ah,
+ sc->sc_target_selfgen_state);
+ }
+ }
+}
+
+/*
+ * Set the current self-generated frames state.
+ *
+ * This is separate from the target power mode. The chip may be
+ * awake but the desired state is "sleep", so frames sent to the
+ * destination has PWRMGT=1 in the 802.11 header. The NIC also
+ * needs to know to set PWRMGT=1 in self-generated frames.
+ */
+void
+_ath_power_set_selfgen(struct ath_softc *sc, int power_state, const char *file, int line)
+{
+
+ ATH_LOCK_ASSERT(sc);
+
+ DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n",
+ __func__,
+ file,
+ line,
+ power_state,
+ sc->sc_target_selfgen_state);
+
+ sc->sc_target_selfgen_state = power_state;
+
+ /*
+ * If the NIC is force-awake, then set the power state.
+ * Network-state and full-sleep will already transition it to
+ * mark self-gen frames as sleeping - and we can't
+ * guarantee the NIC is awake to program the self-gen frame
+ * setting anyway.
+ */
+ if (sc->sc_cur_powerstate == HAL_PM_AWAKE) {
+ ath_hal_setselfgenpower(sc->sc_ah, power_state);
}
}
@@ -334,6 +383,16 @@ _ath_power_set_power_state(struct ath_softc *sc, int power_state, const char *fi
if (power_state != sc->sc_cur_powerstate) {
ath_hal_setpower(sc->sc_ah, power_state);
sc->sc_cur_powerstate = power_state;
+
+ /*
+ * Adjust the self-gen powerstate if appropriate.
+ */
+ if (sc->sc_cur_powerstate == HAL_PM_AWAKE &&
+ sc->sc_target_selfgen_state != HAL_PM_AWAKE) {
+ ath_hal_setselfgenpower(sc->sc_ah,
+ sc->sc_target_selfgen_state);
+ }
+
}
}
@@ -366,6 +425,16 @@ _ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line)
sc->sc_cur_powerstate = sc->sc_target_powerstate;
ath_hal_setpower(sc->sc_ah, sc->sc_target_powerstate);
}
+
+ /*
+ * Adjust the self-gen powerstate if appropriate.
+ */
+ if (sc->sc_cur_powerstate == HAL_PM_AWAKE &&
+ sc->sc_target_selfgen_state != HAL_PM_AWAKE) {
+ ath_hal_setselfgenpower(sc->sc_ah,
+ sc->sc_target_selfgen_state);
+ }
+
}
#define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20)
@@ -1734,6 +1803,7 @@ ath_resume(struct ath_softc *sc)
/* Ensure we set the current power state to on */
ATH_LOCK(sc);
+ ath_power_setselfgen(sc, HAL_PM_AWAKE);
ath_power_set_power_state(sc, HAL_PM_AWAKE);
ath_power_setpower(sc, HAL_PM_AWAKE);
ATH_UNLOCK(sc);
@@ -2252,6 +2322,7 @@ ath_init(void *arg)
/*
* Force the sleep state awake.
*/
+ ath_power_setselfgen(sc, HAL_PM_AWAKE);
ath_power_set_power_state(sc, HAL_PM_AWAKE);
ath_power_setpower(sc, HAL_PM_AWAKE);
@@ -5656,6 +5727,20 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
/* Before we touch the hardware - wake it up */
ATH_LOCK(sc);
+ /*
+ * If the NIC is in anything other than SLEEP state,
+ * we need to ensure that self-generated frames are
+ * set for PWRMGT=0. Otherwise we may end up with
+ * strange situations.
+ *
+ * XXX TODO: is this actually the case? :-)
+ */
+ if (nstate != IEEE80211_S_SLEEP)
+ ath_power_setselfgen(sc, HAL_PM_AWAKE);
+
+ /*
+ * Now, wake the thing up.
+ */
ath_power_set_power_state(sc, HAL_PM_AWAKE);
ATH_UNLOCK(sc);
@@ -5675,6 +5760,7 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
/* Ensure we stay awake during scan */
ATH_LOCK(sc);
+ ath_power_setselfgen(sc, HAL_PM_AWAKE);
ath_power_setpower(sc, HAL_PM_AWAKE);
ATH_UNLOCK(sc);
@@ -5850,6 +5936,7 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
* Force awake for RUN mode.
*/
ATH_LOCK(sc);
+ ath_power_setselfgen(sc, HAL_PM_AWAKE);
ath_power_setpower(sc, HAL_PM_AWAKE);
ATH_UNLOCK(sc);
@@ -5891,20 +5978,23 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
vap->iv_opmode == IEEE80211_M_STA) {
DPRINTF(sc, ATH_DEBUG_BEACON, "%s: syncbeacon=%d\n", __func__, sc->sc_syncbeacon);
ATH_LOCK(sc);
- if (sc->sc_syncbeacon == 0) {
- ath_power_setpower(sc, HAL_PM_NETWORK_SLEEP);
- }
/*
* Always at least set the self-generated
- * power bits appropriately.
+ * frame config to set PWRMGT=1.
+ */
+ ath_power_setselfgen(sc, HAL_PM_NETWORK_SLEEP);
+
+ /*
+ * If we're not syncing beacons, transition
+ * to NETWORK_SLEEP.
*
- * XXX TODO: this should be an ath_power_*() call
- * which also tracks whether we're doing self-gen
- * frames or not, and allows the hardware to be
- * awake _but_ self-gen frames to have PWRMGT=1.
+ * We stay awake if syncbeacon > 0 in case
+ * we need to listen for some beacons otherwise
+ * our beacon timer config may be wrong.
*/
- ath_hal_setselfgenpower(sc->sc_ah,
- HAL_PM_NETWORK_SLEEP);
+ if (sc->sc_syncbeacon == 0) {
+ ath_power_setpower(sc, HAL_PM_NETWORK_SLEEP);
+ }
ATH_UNLOCK(sc);
}
}
diff --git a/sys/dev/ath/if_ath_misc.h b/sys/dev/ath/if_ath_misc.h
index 24dfa95..711e69e8 100644
--- a/sys/dev/ath/if_ath_misc.h
+++ b/sys/dev/ath/if_ath_misc.h
@@ -131,10 +131,12 @@ extern void ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq);
* Power state tracking.
*/
extern void _ath_power_setpower(struct ath_softc *sc, int power_state, const char *file, int line);
+extern void _ath_power_set_selfgen(struct ath_softc *sc, int power_state, const char *file, int line);
extern void _ath_power_set_power_state(struct ath_softc *sc, int power_state, const char *file, int line);
extern void _ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line);
#define ath_power_setpower(sc, ps) _ath_power_setpower(sc, ps, __FILE__, __LINE__)
+#define ath_power_setselfgen(sc, ps) _ath_power_set_selfgen(sc, ps, __FILE__, __LINE__)
#define ath_power_set_power_state(sc, ps) _ath_power_set_power_state(sc, ps, __FILE__, __LINE__)
#define ath_power_restore_power_state(sc) _ath_power_restore_power_state(sc, __FILE__, __LINE__)
diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h
index d0950ae..f127543 100644
--- a/sys/dev/ath/if_athvar.h
+++ b/sys/dev/ath/if_athvar.h
@@ -867,10 +867,19 @@ struct ath_softc {
int status);
/*
- * powersave state tracking.
+ * Powersave state tracking.
+ *
+ * target/cur powerstate is the chip power state.
+ * target selfgen state is the self-generated frames
+ * state. The chip can be awake but transmitted frames
+ * can have the PWRMGT bit set to 1 so the destination
+ * thinks the node is asleep.
*/
HAL_POWER_MODE sc_target_powerstate;
+ HAL_POWER_MODE sc_target_selfgen_state;
+
HAL_POWER_MODE sc_cur_powerstate;
+
int sc_powersave_refcnt;
};
OpenPOWER on IntegriCloud