Index: sys/dev/ath/ath_hal/ar5210/ar5210_power.c =================================================================== --- sys/dev/ath/ath_hal/ar5210/ar5210_power.c (revision 264898) +++ sys/dev/ath/ath_hal/ar5210/ar5210_power.c (working copy) @@ -108,16 +108,19 @@ setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; status = ar5210SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5210SetPowerModeSleep(ah, setChip); - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5210SetPowerModeAuto(ah, setChip); - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", Index: sys/dev/ath/ath_hal/ar5211/ar5211_power.c =================================================================== --- sys/dev/ath/ath_hal/ar5211/ar5211_power.c (revision 264900) +++ sys/dev/ath/ath_hal/ar5211/ar5211_power.c (working copy) @@ -110,16 +110,19 @@ setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; status = ar5211SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5211SetPowerModeSleep(ah, setChip); - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5211SetPowerModeNetworkSleep(ah, setChip); - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", Index: sys/dev/ath/ath_hal/ar5212/ar5212_power.c =================================================================== --- sys/dev/ath/ath_hal/ar5212/ar5212_power.c (revision 264838) +++ sys/dev/ath/ath_hal/ar5212/ar5212_power.c (working copy) @@ -134,16 +134,19 @@ setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; status = ar5212SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5212SetPowerModeSleep(ah, setChip); - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5212SetPowerModeNetworkSleep(ah, setChip); - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", Index: sys/dev/ath/ath_hal/ar5416/ar5416_power.c =================================================================== --- sys/dev/ath/ath_hal/ar5416/ar5416_power.c (revision 264838) +++ sys/dev/ath/ath_hal/ar5416/ar5416_power.c (working copy) @@ -133,23 +133,29 @@ }; #endif int status = AH_TRUE; + +#if 0 if (!setChip) return AH_TRUE; +#endif HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; status = ar5416SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5416SetPowerModeSleep(ah, setChip); - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5416SetPowerModeNetworkSleep(ah, setChip); - ah->ah_powerMode = mode; + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode 0x%x\n", Index: sys/dev/ath/if_ath.c =================================================================== --- sys/dev/ath/if_ath.c (revision 264838) +++ sys/dev/ath/if_ath.c (working copy) @@ -279,6 +279,95 @@ } } +/* + * Set the target power mode. + * + * If this is called during a point in time where + * the hardware is being programmed elsewhere, it will + * simply store it away and update it when all current + * uses of the hardware are completed. + */ +void +_ath_power_setpower(struct ath_softc *sc, int power_state, const char *file, int line) +{ + ATH_LOCK_ASSERT(sc); + + sc->sc_target_powerstate = power_state; + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", + __func__, + file, + line, + power_state, + sc->sc_powersave_refcnt); + + if (sc->sc_powersave_refcnt == 0 && + power_state != sc->sc_cur_powerstate) { + sc->sc_cur_powerstate = power_state; + ath_hal_setpower(sc->sc_ah, power_state); + } +} + +/* + * Set the hardware power mode and take a reference. + * + * This doesn't update the target power mode in the driver; + * it just updates the hardware power state. + * + * XXX it should only ever force the hardware awake; it should + * never be called to set it asleep. + */ +void +_ath_power_set_power_state(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_powersave_refcnt); + + sc->sc_powersave_refcnt++; + + if (power_state != sc->sc_cur_powerstate) { + ath_hal_setpower(sc->sc_ah, power_state); + sc->sc_cur_powerstate = power_state; + } +} + +/* + * Restore the power save mode to what it once was. + * + * This will decrement the reference counter and once it hits + * zero, it'll restore the powersave state. + */ +void +_ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line) +{ + + ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) refcnt=%d, target state=%d\n", + __func__, + file, + line, + sc->sc_powersave_refcnt, + sc->sc_target_powerstate); + + if (sc->sc_powersave_refcnt == 0) + device_printf(sc->sc_dev, "%s: refcnt=0?\n", __func__); + else + sc->sc_powersave_refcnt--; + + if (sc->sc_powersave_refcnt == 0 && + sc->sc_target_powerstate != sc->sc_cur_powerstate) { + sc->sc_cur_powerstate = sc->sc_target_powerstate; + ath_hal_setpower(sc->sc_ah, sc->sc_target_powerstate); + } +} + #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) #define HAL_MODE_HT40 \ (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ @@ -341,6 +430,10 @@ ath_xmit_setup_legacy(sc); } + if (ath_hal_hasmybeacon(sc->sc_ah)) { + sc->sc_do_mybeacon = 1; + } + /* * Check if the MAC has multi-rate retry support. * We do this by trying to setup a fake extended @@ -605,6 +698,8 @@ #ifdef ATH_ENABLE_DFS | IEEE80211_C_DFS /* Enable radar detection */ #endif + | IEEE80211_C_PMGT /* Station side power mgmt */ + | IEEE80211_C_SWSLEEP ; /* * Query the hal to figure out h/w crypto support. @@ -994,6 +1089,14 @@ if (bootverbose) ieee80211_announce(ic); ath_announce(sc); + + /* + * Put it to sleep for now. + */ + ATH_LOCK(sc); + ath_power_setpower(sc, HAL_PM_FULL_SLEEP); + ATH_UNLOCK(sc); + return 0; bad2: ath_tx_cleanup(sc); @@ -1039,7 +1142,22 @@ * it last * Other than that, it's straightforward... */ + + /* + * XXX Wake the hardware up first. ath_stop() will still + * wake it up first, but I'd rather do it here just to + * ensure it's awake. + */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + /* + * Stop things cleanly. + */ ath_stop(ifp); + ieee80211_ifdetach(ifp->if_l2com); taskqueue_free(sc->sc_tq); #ifdef ATH_TX99_DIAG @@ -1402,6 +1520,10 @@ struct ath_hal *ah = sc->sc_ah; struct ath_vap *avp = ATH_VAP(vap); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { /* @@ -1415,6 +1537,8 @@ ath_stoprecv(sc, 1); /* stop recv side */ } + /* .. leave the hardware awake for now. */ + ieee80211_vap_detach(vap); /* @@ -1502,6 +1626,9 @@ } ath_hal_intrset(ah, sc->sc_imask); } + + /* Ok, let the hardware asleep. */ + ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } @@ -1547,8 +1674,12 @@ struct ath_hal *ah = sc->sc_ah; int i; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); for (i = 0; i < sc->sc_keymax; i++) ath_hal_keyreset(ah, i); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); ieee80211_crypto_reload_keys(ic); } @@ -1600,6 +1731,13 @@ sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); + + /* Ensure we set the current power state to on */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_hal_reset(ah, sc->sc_opmode, sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, AH_FALSE, &status); @@ -1632,6 +1770,10 @@ if (sc->sc_resume_up) ieee80211_resume_all(ic); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + /* XXX beacons ? */ } @@ -1689,6 +1831,10 @@ return; } + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if ((ifp->if_flags & IFF_UP) == 0 || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { HAL_INT status; @@ -1698,6 +1844,10 @@ ath_hal_getisr(ah, &status); /* clear ISR */ ath_hal_intrset(ah, 0); /* disable further intr's */ ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return; } @@ -1737,6 +1887,11 @@ /* Short-circuit un-handled interrupts */ if (status == 0x0) { ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + return; } @@ -1903,10 +2058,18 @@ ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXORN"); sc->sc_stats.ast_rxorn++; } + if (status & HAL_INT_TSFOOR) { + device_printf(sc->sc_dev, "%s: TSFOOR\n", __func__); + sc->sc_syncbeacon = 1; + } } ATH_PCU_LOCK(sc); sc->sc_intr_cnt--; ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } static void @@ -1937,6 +2100,8 @@ static void ath_bmiss_vap(struct ieee80211vap *vap) { + struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + /* * Workaround phantom bmiss interrupts by sanity-checking * the time of our last rx'd frame. If it is within the @@ -1945,6 +2110,16 @@ * be dispatched up for processing. Note this applies only * for h/w beacon miss events. */ + + /* + * XXX TODO: Just read the TSF during the interrupt path; + * that way we don't have to wake up again just to read it + * again. + */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) { struct ifnet *ifp = vap->iv_ic->ic_ifp; struct ath_softc *sc = ifp->if_softc; @@ -1962,12 +2137,32 @@ if (tsf - lastrx <= bmisstimeout) { sc->sc_stats.ast_bmiss_phantom++; + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + return; } } + + /* + * There's no need to keep the hardware awake during the call + * to av_bmiss(). + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + /* + * Attempt to force a beacon resync. + */ + sc->sc_syncbeacon = 1; + ATH_VAP(vap)->av_bmiss(vap); } +/* XXX this needs a force wakeup! */ int ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs) { @@ -1990,6 +2185,12 @@ DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ath_beacon_miss(sc); + /* * Do a reset upon any becaon miss event. * @@ -2003,6 +2204,13 @@ ath_reset(ifp, ATH_RESET_NOLOSS); ieee80211_beacon_miss(ifp->if_l2com); } + + /* Force a beacon resync, in case they've drifted */ + sc->sc_syncbeacon = 1; + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } /* @@ -2042,6 +2250,12 @@ ATH_LOCK(sc); /* + * Force the sleep state awake. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + + /* * Stop anything previously setup. This is safe * whether this is the first time through or not. */ @@ -2058,6 +2272,7 @@ ath_update_chainmasks(sc, ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); + if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, &status)) { if_printf(ifp, "unable to reset hardware; hal status %u\n", status); @@ -2113,6 +2328,7 @@ */ if (ath_startrecv(sc) != 0) { if_printf(ifp, "unable to start recv logic\n"); + ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return; } @@ -2139,6 +2355,15 @@ if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) sc->sc_imask |= HAL_INT_MIB; + /* + * XXX add capability for this. + * + * If we're in STA mode (and maybe IBSS?) then register for + * TSFOOR interrupts. + */ + if (ic->ic_opmode == IEEE80211_M_STA) + sc->sc_imask |= HAL_INT_TSFOOR; + /* Enable global TX timeout and carrier sense timeout if available */ if (ath_hal_gtxto_supported(ah)) sc->sc_imask |= HAL_INT_GTT; @@ -2150,6 +2375,7 @@ callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); ath_hal_intrset(ah, sc->sc_imask); + ath_power_restore_power_state(sc); ATH_UNLOCK(sc); #ifdef ATH_TX99_DIAG @@ -2170,6 +2396,12 @@ __func__, sc->sc_invalid, ifp->if_flags); ATH_LOCK_ASSERT(sc); + + /* + * Wake the hardware up before fiddling with it. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { /* * Shutdown the hardware and driver: @@ -2210,9 +2442,20 @@ sc->sc_rxlink = NULL; ath_beacon_free(sc); /* XXX not needed */ } + + /* And now, restore the current power state */ + ath_power_restore_power_state(sc); } -#define MAX_TXRX_ITERATIONS 1000 +/* + * Wait until all pending TX/RX has completed. + * + * This waits until all existing transmit, receive and interrupts + * have completed. It's assumed that the caller has first + * grabbed the reset lock so it doesn't try to do overlapping + * chip resets. + */ +#define MAX_TXRX_ITERATIONS 100 static void ath_txrx_stop_locked(struct ath_softc *sc) { @@ -2231,7 +2474,8 @@ sc->sc_txstart_cnt || sc->sc_intr_cnt) { if (i <= 0) break; - msleep(sc, &sc->sc_pcu_mtx, 0, "ath_txrx_stop", 1); + msleep(sc, &sc->sc_pcu_mtx, 0, "ath_txrx_stop", + msecs_to_ticks(10)); i--; } @@ -2278,7 +2522,7 @@ * Another, cleaner way should be found to serialise all of * these operations. */ -#define MAX_RESET_ITERATIONS 10 +#define MAX_RESET_ITERATIONS 25 static int ath_reset_grablock(struct ath_softc *sc, int dowait) { @@ -2296,7 +2540,11 @@ break; } ATH_PCU_UNLOCK(sc); - pause("ath_reset_grablock", 1); + /* + * 1 tick is likely not enough time for long calibrations + * to complete. So we should wait quite a while. + */ + pause("ath_reset_grablock", msecs_to_ticks(100)); i--; ATH_PCU_LOCK(sc); } while (i > 0); @@ -2361,6 +2609,13 @@ /* Try to (stop any further TX/RX from occuring */ taskqueue_block(sc->sc_tq); + /* + * Wake the hardware up. + */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_PCU_LOCK(sc); /* @@ -2455,9 +2710,13 @@ * reset counter - this way ath_intr() doesn't end up * disabling interrupts without a corresponding enable * in the rest or channel change path. + * + * Grab the TX reference in case we need to transmit. + * That way a parallel transmit doesn't. */ ATH_PCU_LOCK(sc); sc->sc_inreset_cnt--; + sc->sc_txstart_cnt++; /* XXX only do this if sc_inreset_cnt == 0? */ ath_hal_intrset(ah, sc->sc_imask); ATH_PCU_UNLOCK(sc); @@ -2474,6 +2733,8 @@ /* Restart TX/RX as needed */ ath_txrx_start(sc); + /* XXX TODO: we need to hold the tx refcount here! */ + /* Restart TX completion and pending TX */ if (reset_type == ATH_RESET_NOLOSS) { for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { @@ -2498,6 +2759,14 @@ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; IF_UNLOCK(&ifp->if_snd); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + ATH_PCU_LOCK(sc); + sc->sc_txstart_cnt--; + ATH_PCU_UNLOCK(sc); + /* Handle any frames in the TX queue */ /* * XXX should this be done by the caller, rather than @@ -2638,6 +2907,7 @@ tbf->bf_status = bf->bf_status; tbf->bf_m = bf->bf_m; tbf->bf_node = bf->bf_node; + KASSERT((bf->bf_node != NULL), ("%s: bf_node=NULL!", __func__)); /* will be setup by the chain/setup function */ tbf->bf_lastds = NULL; /* for now, last == self */ @@ -2739,6 +3009,11 @@ sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); + /* Wake the hardware up already */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: start"); /* * Grab the TX lock - it's ok to do this here; we haven't @@ -2972,6 +3247,11 @@ sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); + /* Sleep the hardware if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: finished"); return (retval); @@ -2999,7 +3279,6 @@ DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); taskqueue_block(sc->sc_tq); - IF_LOCK(&ifp->if_snd); /* NB: doesn't block mgmt frames */ } static void @@ -3009,7 +3288,6 @@ struct ath_softc *sc = ifp->if_softc; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); - IF_UNLOCK(&ifp->if_snd); taskqueue_unblock(sc->sc_tq); } @@ -3020,8 +3298,12 @@ u_int32_t rfilt; /* configure rx filter */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); rfilt = ath_calcrxfilter(sc); ath_hal_setrxfilter(sc->sc_ah, rfilt); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); } @@ -3057,7 +3339,11 @@ if_maddr_runlock(ifp); } else mfilt[0] = mfilt[1] = ~0; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", __func__, mfilt[0], mfilt[1]); } @@ -3119,8 +3405,13 @@ __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec); + /* Wake up the hardware first before updating the slot time */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_setslottime(ah, usec); + ath_power_restore_power_state(sc); sc->sc_updateslot = OK; + ATH_UNLOCK(sc); } /* @@ -3137,6 +3428,8 @@ * When not coordinating the BSS, change the hardware * immediately. For other operation we defer the change * until beacon updates have propagated to the stations. + * + * XXX sc_updateslot isn't changed behind a lock? */ if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) @@ -4258,6 +4551,10 @@ sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0: txqs=0x%08x", txqs); @@ -4278,6 +4575,10 @@ sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ath_tx_kick(sc); } @@ -4299,6 +4600,10 @@ sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0123: txqs=0x%08x", txqs); @@ -4331,6 +4636,10 @@ sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ath_tx_kick(sc); } @@ -4351,6 +4660,10 @@ sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc: txqs=0x%08x", txqs); /* @@ -4376,6 +4689,10 @@ sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ath_tx_kick(sc); } #undef TXQACTIVE @@ -4402,6 +4719,10 @@ sc->sc_txproc_cnt++; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_TX_LOCK(sc); for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { @@ -4410,6 +4731,10 @@ } ATH_TX_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); @@ -5057,6 +5382,15 @@ HAL_BOOL aniCal, shortCal = AH_FALSE; int nextcal; + /* + * Force the hardware awake for ANI work. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + + /* Skip trying to do this if we're in reset */ + if (sc->sc_inreset_cnt) + goto restart; + if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ goto restart; longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz); @@ -5086,6 +5420,7 @@ sc->sc_doresetcal = AH_TRUE; taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask); callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); + ath_power_restore_power_state(sc); return; } /* @@ -5157,6 +5492,10 @@ __func__); /* NB: don't rearm timer */ } + /* + * Restore power state now that we're done. + */ + ath_power_restore_power_state(sc); } static void @@ -5284,6 +5623,7 @@ int i, error, stamode; u_int32_t rfilt; int csa_run_transition = 0; + enum ieee80211_state ostate = vap->iv_state; static const HAL_LED_STATE leds[] = { HAL_LED_INIT, /* IEEE80211_S_INIT */ @@ -5297,7 +5637,7 @@ }; DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[vap->iv_state], + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); /* @@ -5309,7 +5649,12 @@ */ IEEE80211_LOCK_ASSERT(ic); - if (vap->iv_state == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) + /* Before we touch the hardware - wake it up */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + if (ostate == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) csa_run_transition = 1; callout_drain(&sc->sc_cal_ch); @@ -5322,6 +5667,12 @@ * [re]setup beacons. Unblock the task q thread so * deferred interrupt processing is done. */ + + /* Ensure we stay awake during scan */ + ATH_LOCK(sc); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_hal_intrset(ah, sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); @@ -5334,6 +5685,11 @@ stamode = (vap->iv_opmode == IEEE80211_M_STA || vap->iv_opmode == IEEE80211_M_AHDEMO || vap->iv_opmode == IEEE80211_M_IBSS); + + /* + * XXX Dont need to do this (and others) if we've transitioned + * from SLEEP->RUN. + */ if (stamode && nstate == IEEE80211_S_RUN) { sc->sc_curaid = ni->ni_associd; IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid); @@ -5436,12 +5792,15 @@ * beacon to update the beacon timer and thus we * won't get notified of the missing beacons. */ - sc->sc_syncbeacon = 1; -#if 0 - if (csa_run_transition) -#endif - ath_beacon_config(sc, vap); + if (ostate != IEEE80211_S_RUN && + ostate != IEEE80211_S_SLEEP) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: STA; syncbeacon=1\n", __func__); + sc->sc_syncbeacon = 1; + if (csa_run_transition) + ath_beacon_config(sc, vap); + /* * PR: kern/175227 * @@ -5454,7 +5813,8 @@ * timer fires (too often), leading to a STA * disassociation. */ - sc->sc_beacons = 1; + sc->sc_beacons = 1; + } break; case IEEE80211_M_MONITOR: /* @@ -5480,7 +5840,15 @@ sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; + /* + * Force awake for RUN mode. + */ + ATH_LOCK(sc); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + /* * Finally, start any timers and the task q thread * (in case we didn't go through SCAN state). */ @@ -5491,6 +5859,7 @@ DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); } + taskqueue_unblock(sc->sc_tq); } else if (nstate == IEEE80211_S_INIT) { /* @@ -5510,9 +5879,40 @@ #ifdef IEEE80211_SUPPORT_TDMA ath_hal_setcca(ah, AH_TRUE); #endif + } else if (nstate == IEEE80211_S_SLEEP) { + /* We're going to sleep, so transition appropriately */ + /* For now, only do this if we're a single STA vap */ + if (sc->sc_nvaps == 1 && + 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. + * + * 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. + */ + ath_hal_setselfgenpower(sc->sc_ah, + HAL_PM_NETWORK_SLEEP); + ATH_UNLOCK(sc); + } } bad: ieee80211_free_node(ni); + + /* + * Restore the power state - either to what it was, or + * to network_sleep if it's alright. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return error; } @@ -5567,6 +5967,13 @@ an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); + DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: reassoc; isnew=%d, is_powersave=%d\n", + __func__, + ni->ni_macaddr, + ":", + isnew, + an->an_is_powersave); + ATH_NODE_LOCK(an); ath_rate_newassoc(sc, an, isnew); ATH_NODE_UNLOCK(an); @@ -5813,6 +6220,10 @@ struct ifnet *ifp = sc->sc_ifp; uint32_t hangs; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) { if_printf(ifp, "%s hang detected (0x%x)\n", @@ -5822,6 +6233,10 @@ do_reset = 1; ifp->if_oerrors++; sc->sc_stats.ast_watchdog++; + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } /* @@ -5919,6 +6334,13 @@ goto bad; } } + + + ATH_LOCK(sc); + if (id != HAL_DIAG_REGS) + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { if (outsize < ad->ad_out_size) ad->ad_out_size = outsize; @@ -5928,6 +6350,12 @@ } else { error = EINVAL; } + + ATH_LOCK(sc); + if (id != HAL_DIAG_REGS) + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + bad: if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) free(indata, M_TEMP); @@ -5974,11 +6402,8 @@ } else { ATH_LOCK(sc); ath_stop_locked(ifp); -#ifdef notyet - /* XXX must wakeup in places like ath_vap_delete */ if (!sc->sc_invalid) - ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP); -#endif + ath_power_setpower(sc, HAL_PM_FULL_SLEEP); ATH_UNLOCK(sc); } break; Index: sys/dev/ath/if_ath_beacon.c =================================================================== --- sys/dev/ath/if_ath_beacon.c (revision 264838) +++ sys/dev/ath/if_ath_beacon.c (working copy) @@ -382,7 +382,7 @@ /* * Handle a beacon miss. */ -static void +void ath_beacon_miss(struct ath_softc *sc) { HAL_SURVEY_SAMPLE hs; @@ -916,7 +916,7 @@ struct ieee80211_node *ni; u_int32_t nexttbtt, intval, tsftu; u_int32_t nexttbtt_u8, intval_u8; - u_int64_t tsf; + u_int64_t tsf, tsf_beacon; if (vap == NULL) vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ @@ -932,9 +932,17 @@ ni = ieee80211_ref_node(vap->iv_bss); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + /* extract tstamp from last beacon and convert to TU */ nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4), LE_READ_4(ni->ni_tstamp.data)); + + tsf_beacon = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon |= LE_READ_4(ni->ni_tstamp.data); + if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) { /* @@ -980,6 +988,22 @@ */ tsf = ath_hal_gettsf64(ah); tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: beacon tsf=%llu, hw tsf=%llu, nexttbtt=%u, tsftu=%u\n", + __func__, + (unsigned long long) tsf_beacon, + (unsigned long long) tsf, + nexttbtt, + tsftu); + + /* + * XXX if it's way, way different, then we may have + * read some invalid sounding TSF value from the hardware. + * (Kite in certain circumstances?) + */ + +#if 0 do { nexttbtt += intval; if (--dtimcount < 0) { @@ -988,6 +1012,34 @@ cfpcount = cfpperiod - 1; } } while (nexttbtt < tsftu); + +#else + if (nexttbtt > tsftu) { + u_int32_t countdiff, oldtbtt, remainder; + oldtbtt = nexttbtt; + remainder = (nexttbtt - tsftu) % intval; + nexttbtt = tsftu + remainder; + + countdiff = (oldtbtt - nexttbtt) / intval % dtimperiod; + if (dtimcount > countdiff) { + dtimcount -= countdiff; + } else { + dtimcount += dtimperiod - countdiff; + } + } else { //nexttbtt <= tsftu + u_int32_t countdiff, oldtbtt, remainder; + oldtbtt = nexttbtt; + remainder = (tsftu - nexttbtt) % intval; + nexttbtt = tsftu - remainder + intval; + countdiff = (nexttbtt - oldtbtt) / intval % dtimperiod; + if (dtimcount > countdiff) { + dtimcount -= countdiff; + } else { + dtimcount += dtimperiod - countdiff; + } + } +#endif + memset(&bs, 0, sizeof(bs)); bs.bs_intval = intval; bs.bs_nexttbtt = nexttbtt; @@ -1113,8 +1165,11 @@ if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) ath_beacon_start_adhoc(sc, vap); } - sc->sc_syncbeacon = 0; ieee80211_free_node(ni); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); #undef FUDGE #undef TSF_TO_TU } Index: sys/dev/ath/if_ath_beacon.h =================================================================== --- sys/dev/ath/if_ath_beacon.h (revision 264838) +++ sys/dev/ath/if_ath_beacon.h (working copy) @@ -48,5 +48,7 @@ extern void ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf); extern void ath_beacon_free(struct ath_softc *sc); extern void ath_beacon_proc(void *arg, int pending); +extern void ath_beacon_miss(struct ath_softc *sc); #endif + Index: sys/dev/ath/if_ath_debug.h =================================================================== --- sys/dev/ath/if_ath_debug.h (revision 264838) +++ sys/dev/ath/if_ath_debug.h (working copy) @@ -68,6 +68,7 @@ ATH_DEBUG_SW_TX_FILT = 0x400000000ULL, /* SW TX FF */ ATH_DEBUG_NODE_PWRSAVE = 0x800000000ULL, /* node powersave */ ATH_DEBUG_DIVERSITY = 0x1000000000ULL, /* Diversity logic */ + ATH_DEBUG_PWRSAVE = 0x2000000000ULL, ATH_DEBUG_ANY = 0xffffffffffffffffULL }; Index: sys/dev/ath/if_ath_keycache.c =================================================================== --- sys/dev/ath/if_ath_keycache.c (revision 264838) +++ sys/dev/ath/if_ath_keycache.c (working copy) @@ -78,6 +78,7 @@ #include #include +#include #ifdef ATH_DEBUG static void @@ -198,6 +199,7 @@ u_int8_t gmac[IEEE80211_ADDR_LEN]; const u_int8_t *mac; HAL_KEYVAL hk; + int ret; memset(&hk, 0, sizeof(hk)); /* @@ -251,13 +253,19 @@ } else mac = k->wk_macaddr; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); if (hk.kv_type == HAL_CIPHER_TKIP && (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { - return ath_keyset_tkip(sc, k, &hk, mac); + ret = ath_keyset_tkip(sc, k, &hk, mac); } else { KEYPRINTF(sc, k->wk_keyix, &hk, mac); - return ath_hal_keyset(ah, k->wk_keyix, &hk, mac); + ret = ath_hal_keyset(ah, k->wk_keyix, &hk, mac); } + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (ret); #undef N } @@ -492,6 +500,8 @@ DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_keyreset(ah, keyix); /* * Handle split tx/rx keying required for TKIP with h/w MIC. @@ -515,6 +525,8 @@ } } } + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return 1; } Index: sys/dev/ath/if_ath_led.c =================================================================== --- sys/dev/ath/if_ath_led.c (revision 264838) +++ sys/dev/ath/if_ath_led.c (working copy) @@ -122,6 +122,11 @@ void ath_led_config(struct ath_softc *sc) { + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + /* Software LED blinking - GPIO controlled LED */ if (sc->sc_softled) { ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin, @@ -144,6 +149,10 @@ ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_led_net_pin, HAL_GPIO_OUTPUT_MUX_MAC_NETWORK_LED); } + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } static void Index: sys/dev/ath/if_ath_misc.h =================================================================== --- sys/dev/ath/if_ath_misc.h (revision 264838) +++ sys/dev/ath/if_ath_misc.h (working copy) @@ -128,6 +128,17 @@ 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_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_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__) + +/* * Kick the frame TX task. */ static inline void Index: sys/dev/ath/if_ath_rx.c =================================================================== --- sys/dev/ath/if_ath_rx.c (revision 264838) +++ sys/dev/ath/if_ath_rx.c (working copy) @@ -166,10 +166,22 @@ /* XXX ic->ic_monvaps != 0? */ if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC)) rfilt |= HAL_RX_FILTER_PROM; + + /* + * Only listen to all beacons if we're scanning. + * + * Otherwise we only really need to hear beacons from + * our own BSSID. + */ if (ic->ic_opmode == IEEE80211_M_STA || - ic->ic_opmode == IEEE80211_M_IBSS || - sc->sc_swbmiss || sc->sc_scanning) - rfilt |= HAL_RX_FILTER_BEACON; + ic->ic_opmode == IEEE80211_M_IBSS || sc->sc_swbmiss) { + if (sc->sc_do_mybeacon && ! sc->sc_scanning) { + rfilt |= HAL_RX_FILTER_MYBEACON; + } else { /* scanning, non-mybeacon chips */ + rfilt |= HAL_RX_FILTER_BEACON; + } + } + /* * NB: We don't recalculate the rx filter when * ic_protmode changes; otherwise we could do @@ -317,7 +329,11 @@ { struct ieee80211vap *vap = ni->ni_vap; struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + uint64_t tsf_beacon_old, tsf_beacon; + tsf_beacon_old = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon_old |= LE_READ_4(ni->ni_tstamp.data); + /* * Call up first so subsequent work can use information * potentially stored in the node (e.g. for ibss merge). @@ -329,13 +345,28 @@ /* XXX unlocked check against vap->iv_bss? */ ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); if (sc->sc_syncbeacon && - ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) { + ni == vap->iv_bss && + (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: syncbeacon=1; syncing\n", + __func__); /* * Resync beacon timers using the tsf of the beacon * frame we just received. */ ath_beacon_config(sc, vap); + sc->sc_syncbeacon = 0; } + + tsf_beacon = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon |= LE_READ_4(ni->ni_tstamp.data); + + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: old_tsf=%llu, new_tsf=%llu, delta=%lld\n", + __func__, + (unsigned long long) tsf_beacon_old, + (unsigned long long) tsf_beacon, + (long long) tsf_beacon - (long long) tsf_beacon_old); + /* fall thru... */ case IEEE80211_FC0_SUBTYPE_PROBE_RESP: if (vap->iv_opmode == IEEE80211_M_IBSS && @@ -911,6 +942,10 @@ kickpcu = sc->sc_kickpcu; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__); ngood = 0; nf = ath_hal_getchannoise(ah, sc->sc_curchan); @@ -1066,6 +1101,13 @@ #undef PA2DESC /* + * Put the hardware to sleep again if we're done with it. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + /* * If we hit the maximum number of frames in this round, * reschedule for another immediate pass. This gives * the TX and TX completion routines time to run, which Index: sys/dev/ath/if_ath_rx_edma.c =================================================================== --- sys/dev/ath/if_ath_rx_edma.c (revision 264838) +++ sys/dev/ath/if_ath_rx_edma.c (working copy) @@ -286,7 +286,16 @@ int dosched) { + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_edma_recv_proc_queue(sc, qtype, dosched); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); } @@ -294,8 +303,17 @@ ath_edma_recv_sched(struct ath_softc *sc, int dosched) { + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, dosched); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, dosched); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); } @@ -309,6 +327,10 @@ sc->sc_rxproc_cnt++; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + /* * Flush any active frames from FIFO -> deferred list */ @@ -318,9 +340,18 @@ /* * Process what's in the deferred queue */ + /* + * XXX: If we read the tsf/channoise here and then pass it in, + * we could restore the power state before processing + * the deferred queue. + */ ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_HP, 0); ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_LP, 0); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ATH_PCU_LOCK(sc); sc->sc_rxproc_cnt--; ATH_PCU_UNLOCK(sc); @@ -560,6 +591,10 @@ sc->sc_rxproc_cnt++; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 1); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 1); @@ -566,6 +601,15 @@ ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_HP, 1); ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_LP, 1); + /* + * XXX: If we read the tsf/channoise here and then pass it in, + * we could restore the power state before processing + * the deferred queue. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + /* XXX inside IF_LOCK ? */ if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { #ifdef IEEE80211_SUPPORT_SUPERG Index: sys/dev/ath/if_ath_sysctl.c =================================================================== --- sys/dev/ath/if_ath_sysctl.c (revision 264838) +++ sys/dev/ath/if_ath_sysctl.c (working copy) @@ -108,13 +108,26 @@ ath_sysctl_slottime(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; - u_int slottime = ath_hal_getslottime(sc->sc_ah); + u_int slottime; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + slottime = ath_hal_getslottime(sc->sc_ah); + ATH_UNLOCK(sc); + error = sysctl_handle_int(oidp, &slottime, 0, req); if (error || !req->newptr) - return error; - return !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; + goto finish; + + error = !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return error; } static int Index: sys/dev/ath/if_ath_tx.c =================================================================== --- sys/dev/ath/if_ath_tx.c (revision 264838) +++ sys/dev/ath/if_ath_tx.c (working copy) @@ -761,37 +761,21 @@ ("ath_tx_handoff_hw called for mcast queue")); /* - * XXX racy, should hold the PCU lock when checking this, - * and also should ensure that the TX counter is >0! + * XXX We should instead just verify that sc_txstart_cnt + * or ath_txproc_cnt > 0. That would mean that + * the reset is going to be waiting for us to complete. */ - KASSERT((sc->sc_inreset_cnt == 0), - ("%s: TX during reset?\n", __func__)); + if (sc->sc_txproc_cnt == 0 && sc->sc_txstart_cnt == 0) { + device_printf(sc->sc_dev, + "%s: TX dispatch without holding txcount/txstart refcnt!\n", + __func__); + } -#if 0 /* - * This causes a LOR. Find out where the PCU lock is being - * held whilst the TXQ lock is grabbed - that shouldn't - * be occuring. + * XXX .. this is going to cause the hardware to get upset; + * so we really should find some way to drop or queue + * things. */ - ATH_PCU_LOCK(sc); - if (sc->sc_inreset_cnt) { - ATH_PCU_UNLOCK(sc); - DPRINTF(sc, ATH_DEBUG_RESET, - "%s: called with sc_in_reset != 0\n", - __func__); - DPRINTF(sc, ATH_DEBUG_XMIT, - "%s: queued: TXDP[%u] = %p (%p) depth %d\n", - __func__, txq->axq_qnum, - (caddr_t)bf->bf_daddr, bf->bf_desc, - txq->axq_depth); - /* XXX axq_link needs to be set and updated! */ - ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); - if (bf->bf_state.bfs_aggr) - txq->axq_aggr_depth++; - return; - } - ATH_PCU_UNLOCK(sc); -#endif ATH_TXQ_LOCK(txq); @@ -1615,6 +1599,7 @@ error = ath_tx_dmasetup(sc, bf, m0); if (error != 0) return error; + KASSERT((ni != NULL), ("%s: ni=NULL!", __func__)); bf->bf_node = ni; /* NB: held reference */ m0 = bf->bf_m; /* NB: may have changed */ wh = mtod(m0, struct ieee80211_frame *); @@ -2106,6 +2091,7 @@ int do_override; uint8_t type, subtype; int queue_to_head; + struct ath_node *an = ATH_NODE(ni); ATH_TX_LOCK_ASSERT(sc); @@ -2165,6 +2151,7 @@ return error; m0 = bf->bf_m; /* NB: may have changed */ wh = mtod(m0, struct ieee80211_frame *); + KASSERT((ni != NULL), ("%s: ni=NULL!", __func__)); bf->bf_node = ni; /* NB: held reference */ /* Always enable CLRDMASK for raw frames for now.. */ @@ -2183,12 +2170,24 @@ rt = sc->sc_currates; KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); + + /* Fetch first rate information */ rix = ath_tx_findrix(sc, params->ibp_rate0); + try0 = params->ibp_try0; + + /* + * Override EAPOL rate as appropriate. + */ + if (m0->m_flags & M_EAPOL) { + /* XXX? maybe always use long preamble? */ + rix = an->an_mgmtrix; + try0 = ATH_TXMAXTRY; /* XXX?too many? */ + } + txrate = rt->info[rix].rateCode; if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) txrate |= rt->info[rix].shortPreamble; sc->sc_txrix = rix; - try0 = params->ibp_try0; ismrr = (params->ibp_try1 != 0); txantenna = params->ibp_pri >> 2; if (txantenna == 0) /* XXX? */ @@ -2261,8 +2260,7 @@ /* Blank the legacy rate array */ bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc)); - bf->bf_state.bfs_rc[0].rix = - ath_tx_findrix(sc, params->ibp_rate0); + bf->bf_state.bfs_rc[0].rix = rix; bf->bf_state.bfs_rc[0].tries = try0; bf->bf_state.bfs_rc[0].ratecode = txrate; @@ -2354,11 +2352,16 @@ "%s: sc_inreset_cnt > 0; bailing\n", __func__); error = EIO; ATH_PCU_UNLOCK(sc); - goto bad0; + goto badbad; } sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); + /* Wake the hardware up already */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_TX_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) { @@ -2437,7 +2440,14 @@ sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); + + /* Put the hardware back to sleep if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + return 0; + bad2: ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: bad2: m=%p, params=%p, " "bf=%p", @@ -2447,14 +2457,20 @@ ATH_TXBUF_LOCK(sc); ath_returnbuf_head(sc, bf); ATH_TXBUF_UNLOCK(sc); + bad: - ATH_TX_UNLOCK(sc); ATH_PCU_LOCK(sc); sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); -bad0: + + /* Put the hardware back to sleep if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + +badbad: ATH_KTR(sc, ATH_KTR_TX, 2, "ath_raw_xmit: bad0: m=%p, params=%p", m, params); ifp->if_oerrors++; Index: sys/dev/ath/if_athvar.h =================================================================== --- sys/dev/ath/if_athvar.h (revision 264838) +++ sys/dev/ath/if_athvar.h (working copy) @@ -621,7 +621,8 @@ sc_resetcal : 1,/* reset cal state next trip */ sc_rxslink : 1,/* do self-linked final descriptor */ sc_rxtsf32 : 1,/* RX dec TSF is 32 bits */ - sc_isedma : 1;/* supports EDMA */ + sc_isedma : 1,/* supports EDMA */ + sc_do_mybeacon : 1; /* supports mybeacon */ /* * Second set of flags. @@ -864,6 +865,13 @@ void (*sc_bar_response)(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status); + + /* + * powersave state tracking. + */ + HAL_POWER_MODE sc_target_powerstate; + HAL_POWER_MODE sc_cur_powerstate; + int sc_powersave_refcnt; }; #define ATH_LOCK_INIT(_sc) \ @@ -1038,6 +1046,8 @@ ((*(_ah)->ah_updateTxTrigLevel)((_ah), (_inc))) #define ath_hal_setpower(_ah, _mode) \ ((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_TRUE)) +#define ath_hal_setselfgenpower(_ah, _mode) \ + ((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_FALSE)) #define ath_hal_keycachesize(_ah) \ ((*(_ah)->ah_getKeyCacheSize)((_ah))) #define ath_hal_keyreset(_ah, _ix) \ @@ -1266,6 +1276,8 @@ #define ath_hal_setintmit(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_INTMIT, \ HAL_CAP_INTMIT_ENABLE, _v, NULL) +#define ath_hal_hasmybeacon(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_DO_MYBEACON, 1, NULL) == HAL_OK) #define ath_hal_hasenforcetxop(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_ENFORCE_TXOP, 0, NULL) == HAL_OK)