Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 2 May 2014 00:48:10 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r265205 - head/sys/dev/ath
Message-ID:  <201405020048.s420mA1S040257@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Fri May  2 00:48:09 2014
New Revision: 265205
URL: http://svnweb.freebsd.org/changeset/base/265205

Log:
  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

Modified:
  head/sys/dev/ath/if_ath.c
  head/sys/dev/ath/if_ath_misc.h
  head/sys/dev/ath/if_athvar.h

Modified: head/sys/dev/ath/if_ath.c
==============================================================================
--- head/sys/dev/ath/if_ath.c	Fri May  2 00:45:30 2014	(r265204)
+++ head/sys/dev/ath/if_ath.c	Fri May  2 00:48:09 2014	(r265205)
@@ -305,6 +305,55 @@ _ath_power_setpower(struct ath_softc *sc
 	    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_so
 	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 at
 		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, e
 
 	/* 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, e
 
 		/* 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, e
 		 * 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, e
 		    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);
 		}
 	}

Modified: head/sys/dev/ath/if_ath_misc.h
==============================================================================
--- head/sys/dev/ath/if_ath_misc.h	Fri May  2 00:45:30 2014	(r265204)
+++ head/sys/dev/ath/if_ath_misc.h	Fri May  2 00:48:09 2014	(r265205)
@@ -131,10 +131,12 @@ extern void ath_tx_dump(struct ath_softc
  * 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__)
 

Modified: head/sys/dev/ath/if_athvar.h
==============================================================================
--- head/sys/dev/ath/if_athvar.h	Fri May  2 00:45:30 2014	(r265204)
+++ head/sys/dev/ath/if_athvar.h	Fri May  2 00:48:09 2014	(r265205)
@@ -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;
 };
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201405020048.s420mA1S040257>