From owner-svn-src-head@FreeBSD.ORG Fri May 2 00:48:10 2014 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id D76492E0; Fri, 2 May 2014 00:48:10 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id B94EE1122; Fri, 2 May 2014 00:48:10 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s420mA9m040260; Fri, 2 May 2014 00:48:10 GMT (envelope-from adrian@svn.freebsd.org) Received: (from adrian@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s420mA1S040257; Fri, 2 May 2014 00:48:10 GMT (envelope-from adrian@svn.freebsd.org) Message-Id: <201405020048.s420mA1S040257@svn.freebsd.org> From: Adrian Chadd Date: Fri, 2 May 2014 00:48:10 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r265205 - head/sys/dev/ath X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 02 May 2014 00:48:10 -0000 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; };