Index: sys/dev/ath/if_ath.c =================================================================== --- sys/dev/ath/if_ath.c (revision 264291) +++ sys/dev/ath/if_ath.c (working copy) @@ -279,6 +279,88 @@ } } +/* + * 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) +{ +// ATH_LOCK_ASSERT(sc); + + sc->sc_target_powerstate = power_state; + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: state=%d, refcnt=%d\n", + __func__, + 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) +{ +// ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: state=%d, refcnt=%d\n", + __func__, + 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) +{ + +// ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: refcnt=%d\n", + __func__, + sc->sc_powersave_refcnt); + + 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 | \ @@ -605,6 +687,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 +1078,12 @@ if (bootverbose) ieee80211_announce(ic); ath_announce(sc); + + /* + * Put it to sleep for now. + */ + ath_power_setpower(sc, HAL_PM_FULL_SLEEP); + return 0; bad2: ath_tx_cleanup(sc); @@ -1039,7 +1129,14 @@ * 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_stop(ifp); + ieee80211_ifdetach(ifp->if_l2com); taskqueue_free(sc->sc_tq); #ifdef ATH_TX99_DIAG @@ -1547,8 +1644,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 +1701,10 @@ 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_power_setpower(sc, HAL_PM_AWAKE); + ath_hal_reset(ah, sc->sc_opmode, sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, AH_FALSE, &status); @@ -1689,6 +1794,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 +1807,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 +1850,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; } @@ -1907,6 +2025,10 @@ 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 @@ -1968,6 +2090,7 @@ 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 +2113,10 @@ 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); + /* * Do a reset upon any becaon miss event. * @@ -2003,6 +2130,10 @@ ath_reset(ifp, ATH_RESET_NOLOSS); ieee80211_beacon_miss(ifp->if_l2com); } + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } /* @@ -2042,6 +2173,11 @@ ATH_LOCK(sc); /* + * Force the sleep state 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 +2194,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); @@ -2170,6 +2307,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,6 +2353,9 @@ 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 @@ -2361,6 +2507,14 @@ /* Try to (stop any further TX/RX from occuring */ taskqueue_block(sc->sc_tq); + + /* + * Wake the hardware up. + */ + ATH_LOCK(sc); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_PCU_LOCK(sc); /* @@ -2739,6 +2893,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 +3131,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 +3163,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 +3172,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 +3182,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 +3223,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 +3289,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 +3312,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 +4435,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 +4459,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 +4484,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 +4520,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 +4544,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 +4573,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 @@ -5057,6 +5258,11 @@ HAL_BOOL aniCal, shortCal = AH_FALSE; int nextcal; + /* + * Force the hardware awake for ANI work. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ goto restart; longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz); @@ -5086,6 +5292,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 +5364,10 @@ __func__); /* NB: don't rearm timer */ } + /* + * Restore power state now that we're done. + */ + ath_power_restore_power_state(sc); } static void @@ -5309,6 +5520,11 @@ */ IEEE80211_LOCK_ASSERT(ic); + /* Before we touch the hardware - wake it up */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if (vap->iv_state == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) csa_run_transition = 1; @@ -5322,6 +5538,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); @@ -5480,7 +5702,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 +5721,7 @@ DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); } + taskqueue_unblock(sc->sc_tq); } else if (nstate == IEEE80211_S_INIT) { /* @@ -5510,9 +5741,22 @@ #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 */ + ATH_LOCK(sc); + ath_power_setpower(sc, 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; } @@ -5811,6 +6055,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", @@ -5820,6 +6068,10 @@ do_reset = 1; ifp->if_oerrors++; sc->sc_stats.ast_watchdog++; + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } /* @@ -5972,11 +6224,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_debug.h =================================================================== --- sys/dev/ath/if_ath_debug.h (revision 264291) +++ 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 264291) +++ 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_misc.h =================================================================== --- sys/dev/ath/if_ath_misc.h (revision 264291) +++ sys/dev/ath/if_ath_misc.h (working copy) @@ -128,6 +128,13 @@ 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); +extern void ath_power_set_power_state(struct ath_softc *sc, int power_state); +extern void ath_power_restore_power_state(struct ath_softc *sc); + +/* * Kick the frame TX task. */ static inline void Index: sys/dev/ath/if_ath_rx.c =================================================================== --- sys/dev/ath/if_ath_rx.c (revision 264291) +++ sys/dev/ath/if_ath_rx.c (working copy) @@ -911,6 +911,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 +1070,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_tx.c =================================================================== --- sys/dev/ath/if_ath_tx.c (revision 264291) +++ sys/dev/ath/if_ath_tx.c (working copy) @@ -2106,6 +2106,7 @@ int do_override; uint8_t type, subtype; int queue_to_head; + struct ath_node *an = ATH_NODE(ni); ATH_TX_LOCK_ASSERT(sc); @@ -2183,12 +2184,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 +2274,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; @@ -2359,6 +2371,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_TX_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) { @@ -2437,7 +2454,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 +2471,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); + ATH_KTR(sc, ATH_KTR_TX, 2, "ath_raw_xmit: bad0: m=%p, params=%p", m, params); ifp->if_oerrors++; @@ -3262,7 +3292,7 @@ * until it's actually resolved. */ if (tid->paused == 0) { - DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + device_printf(sc->sc_dev, "%s: %6D: paused=0?\n", __func__, tid->an->an_node.ni_macaddr, ":"); } else { Index: sys/dev/ath/if_athvar.h =================================================================== --- sys/dev/ath/if_athvar.h (revision 264291) +++ sys/dev/ath/if_athvar.h (working copy) @@ -864,6 +864,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) \ Index: sys/dev/bwi/bwirf.c =================================================================== --- sys/dev/bwi/bwirf.c (revision 264291) +++ sys/dev/bwi/bwirf.c (working copy) @@ -1094,6 +1094,7 @@ * Find out max TX power */ val = bwi_read_sprom(sc, BWI_SPROM_MAX_TXPWR); + device_printf(sc->sc_dev, "%s: TXPWR=%04x\n", __func__, val); if (phy->phy_mode == IEEE80211_MODE_11A) { rf->rf_txpower_max = __SHIFTOUT(val, BWI_SPROM_MAX_TXPWR_MASK_11A); @@ -1123,7 +1124,10 @@ else ant_gain = __SHIFTOUT(val, BWI_SPROM_ANT_GAIN_MASK_11BG); if (ant_gain == 0xff) { - device_printf(sc->sc_dev, "invalid antenna gain in sprom\n"); + device_printf(sc->sc_dev, + "invalid antenna gain in sprom (val=0x%08x) (mode=%d)\n", + val, + phy->phy_mode); ant_gain = 2; } ant_gain *= 4; Index: sys/dev/iwi/if_iwi.c =================================================================== --- sys/dev/iwi/if_iwi.c (revision 264291) +++ sys/dev/iwi/if_iwi.c (working copy) @@ -789,6 +789,7 @@ error = ENOMEM; goto fail; } + memset(mtod(data->m, char *), '\0', MCLBYTES); error = bus_dmamap_load(ring->data_dmat, data->map, mtod(data->m, void *), MCLBYTES, iwi_dma_map_addr, @@ -799,6 +800,9 @@ goto fail; } + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_PREREAD); + data->reg = IWI_CSR_RX_BASE + i * 4; } @@ -1238,6 +1242,7 @@ ifp->if_ierrors++; return; } + memset(mtod(mnew, char *), '\0', MCLBYTES); bus_dmamap_unload(sc->rxq.data_dmat, data->map); @@ -1397,6 +1402,7 @@ } lq = (struct iwi_notif_link_quality *)(notif + 1); + DPRINTFN(5, (" notif=%p, lq=%p\n", notif, lq)); memcpy(&sc->sc_linkqual, lq, sizeof(sc->sc_linkqual)); sc->sc_linkqual_valid = 1; } @@ -1619,6 +1625,16 @@ DPRINTFN(15, ("rx done idx=%u\n", sc->rxq.cur)); + memset(mtod(data->m, char *), '\0', MCLBYTES); + + /* + * XXX ideally we'd be freeing and recycling the + * XXX buffer here. + */ + + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_PREREAD); + sc->rxq.cur = (sc->rxq.cur + 1) % IWI_RX_RING_COUNT; } Index: sys/net80211/ieee80211.c =================================================================== --- sys/net80211/ieee80211.c (revision 264291) +++ sys/net80211/ieee80211.c (working copy) @@ -1408,7 +1408,7 @@ * rate only when running; otherwise we may have a mismatch * in which case the rate will not be convertible. */ - if (vap->iv_state == IEEE80211_S_RUN) { + if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) { imr->ifm_status |= IFM_ACTIVE; mode = ieee80211_chan2mode(ic->ic_curchan); } else Index: sys/net80211/ieee80211_mesh.c =================================================================== --- sys/net80211/ieee80211_mesh.c (revision 264291) +++ sys/net80211/ieee80211_mesh.c (working copy) @@ -1142,6 +1142,13 @@ /* * Forward the specified frame. * Decrement the TTL and set TA to our MAC address. + * + * XXX This needs to be changed to stuff the mesh header into an + * mbuf tag and then reinject it into the VAP TX method. + * + * XXX There's no VAP 802.11 encap TX method! So we should + * calculate the mesh header information, inject into the 802.3 + * queue, and then have all the right seqno stuff assigned there. */ static void mesh_forward(struct ieee80211vap *vap, struct mbuf *m, Index: sys/net80211/ieee80211_output.c =================================================================== --- sys/net80211/ieee80211_output.c (revision 264291) +++ sys/net80211/ieee80211_output.c (working copy) @@ -390,6 +390,19 @@ /* * We've resolved the sender, so attempt to transmit it. */ + + if (vap->iv_state == IEEE80211_S_SLEEP) { + /* + * In power save; queue frame and then wakeup device + * for transmit. + */ + ic->ic_lastdata = ticks; + (void) ieee80211_pwrsave(ni, m); + ieee80211_free_node(ni); + ieee80211_new_state(vap, IEEE80211_S_RUN, 0); + return (0); + } + if (ieee80211_vap_pkt_send_dest(vap, m, ni) != 0) return (ENOBUFS); return (0); @@ -420,14 +433,27 @@ m_freem(m); return (EINVAL); } + +#if 0 + /* + * XXX TODO: we may want to optionally transmit data frames without + * waking the device up and instead use PS-POLL to grab frames from + * the peer. + */ if (vap->iv_state == IEEE80211_S_SLEEP) { /* * In power save, wakeup device for transmit. + * + * XXX should just queue the frame for when it finishes + * XXX transitioning it to RUN. */ + ic->ic_lastdata = ticks; ieee80211_new_state(vap, IEEE80211_S_RUN, 0); m_freem(m); return (0); } +#endif + /* * No data frames go out unless we're running. * Note in particular this covers CAC and CSA @@ -434,7 +460,7 @@ * states (though maybe we should check muting * for CSA). */ - if (vap->iv_state != IEEE80211_S_RUN) { + if (vap->iv_state != IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_SLEEP) { IEEE80211_LOCK(ic); /* re-check under the com lock to avoid races */ if (vap->iv_state != IEEE80211_S_RUN) { @@ -477,6 +503,13 @@ /* * 802.11 raw output routine. + * + * XXX TODO: this (and other send routines) should correctly + * XXX keep the pwr mgmt bit set if it decides to call into the + * XXX driver to send a frame whilst the state is SLEEP. + * + * Otherwise the peer may decide that we're awake and flood us + * with traffic we are still too asleep to receive! */ int ieee80211_raw_output(struct ieee80211vap *vap, struct ieee80211_node *ni, Index: sys/net80211/ieee80211_power.c =================================================================== --- sys/net80211/ieee80211_power.c (revision 264291) +++ sys/net80211/ieee80211_power.c (working copy) @@ -48,6 +48,7 @@ static void ieee80211_update_ps(struct ieee80211vap *, int); static int ieee80211_set_tim(struct ieee80211_node *, int); +static void ieee80211_sta_ps_timer_cb(void *arg); static MALLOC_DEFINE(M_80211_POWER, "80211power", "802.11 power save state"); @@ -91,6 +92,23 @@ vap->iv_tim_len = 0; } } + + /* For STA, Adhoc and Mesh power save */ + callout_init_mtx(&vap->iv_stapstimer, IEEE80211_LOCK_OBJ(vap->iv_ic), + 0); + + /* + * Start the sleep timer if we're in STA mode. + * + * XXX yes, this is pretty terrible. It should just be something + * based on consecutive beacons w/ no active transmit. + */ + if (vap->iv_caps & IEEE80211_C_SWSLEEP && + vap->iv_opmode == IEEE80211_M_STA) { + printf("%s: add STA timer\n", __func__); + callout_reset(&vap->iv_stapstimer, msecs_to_ticks(500), + ieee80211_sta_ps_timer_cb, vap); + } } void @@ -100,6 +118,10 @@ free(vap->iv_tim_bitmap, M_80211_POWER); vap->iv_tim_bitmap = NULL; } + + IEEE80211_LOCK(vap->iv_ic); + callout_drain(&vap->iv_stapstimer); + IEEE80211_UNLOCK(vap->iv_ic); } void @@ -555,3 +577,90 @@ ieee80211_send_nulldata(ieee80211_ref_node(ni)); } } + +/* + * Handle being notified that we have data available for us in a TIM/ATIM. + * + * This may schedule a transition from _SLEEP -> _RUN if it's appropriate. + */ +void +ieee80211_sta_tim_notify(struct ieee80211vap *vap, int set) +{ + /* + * Schedule the driver state change. It'll happen at some point soon. + * Since the hardware shouldn't know that we're running just yet + * (and thus tell the peer that we're awake before we actually wake + * up said hardware), we leave the actual node state transition + * up to the transition to RUN. + * + * XXX TODO: verify that the transition to RUN will wake up the + * BSS node! + */ + IEEE80211_LOCK(vap->iv_ic); + if (set == 1 && vap->iv_state == IEEE80211_S_SLEEP) { + printf("%s: TIM=%d\n", __func__, set); + ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); + } + IEEE80211_UNLOCK(vap->iv_ic); +} + +static void +ieee80211_sta_ps_timer_cb(void *arg) +{ + struct ieee80211vap *vap = arg; + + /* XXX make an overridable vap method! */ + ieee80211_sta_ps_timer_check(vap); + + callout_reset(&vap->iv_stapstimer, msecs_to_ticks(500), + ieee80211_sta_ps_timer_cb, vap); +} + +/* + * Timer check on whether the VAP has had any transmit activity. + * + * This may schedule a transition from _RUN -> _SLEEP if it's appropriate. + */ +void +ieee80211_sta_ps_timer_check(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + /* XXX lock assert */ + + /* If we're not at run state, bail */ + if (vap->iv_state != IEEE80211_S_RUN) + goto out; + +// printf("%s: lastdata=%llu, ticks=%llu\n", __func__, (unsigned long long) ic->ic_lastdata, (unsigned long long) ticks); + + /* If powersave is disabled on the VAP, don't bother */ + if (! (vap->iv_flags & IEEE80211_F_PMGTON)) + goto out; + + /* If we've done any data within our idle interval, bail */ + /* XXX hard-coded to one second for now, ew! */ + if (time_after(ic->ic_lastdata + 1000, ticks)) + goto out; + + /* + * Signify we're going into power save and transition the + * node to powersave. + */ + if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) + vap->iv_sta_ps(vap, 1); + + /* + * XXX The driver has to handle the fact that we're going + * to sleep but frames may still be transmitted; + * hopefully it and/or us will do the right thing and mark any + * transmitted frames with PWRMGT set to 1. + */ + ieee80211_new_state_locked(vap, IEEE80211_S_SLEEP, 0); + + printf("%s: time delta=%d msec\n", __func__, + (int) ticks_to_msecs(ticks - ic->ic_lastdata)); + +out: + return; +} Index: sys/net80211/ieee80211_power.h =================================================================== --- sys/net80211/ieee80211_power.h (revision 264291) +++ sys/net80211/ieee80211_power.h (working copy) @@ -79,6 +79,9 @@ int ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *); void ieee80211_node_pwrsave(struct ieee80211_node *, int enable); void ieee80211_sta_pwrsave(struct ieee80211vap *, int enable); +void ieee80211_sta_tim_notify(struct ieee80211vap *vap, int set); +void ieee80211_sta_ps_timer_check(struct ieee80211vap *vap); +/* XXX what's this? */ void ieee80211_power_poll(struct ieee80211com *); #endif /* _NET80211_IEEE80211_POWER_H_ */ Index: sys/net80211/ieee80211_sta.c =================================================================== --- sys/net80211/ieee80211_sta.c (revision 264291) +++ sys/net80211/ieee80211_sta.c (working copy) @@ -234,6 +234,7 @@ switch (ostate) { case IEEE80211_S_SLEEP: /* XXX wakeup */ + /* XXX driver hook to wakeup the hardware? */ case IEEE80211_S_RUN: IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC, @@ -403,6 +404,7 @@ arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); break; case IEEE80211_S_SLEEP: + /* Wake up from sleep */ vap->iv_sta_ps(vap, 0); break; default: @@ -1312,7 +1314,16 @@ vap->iv_stats.is_beacon_bad++; return; } + /* + * XXX We should check if the beacon frame tells us + * that there's traffic for us. If so, we should + * schedule a wakeup from IEEE80211_S_SLEEP or + * (whenever the framework is implemented) a PS-POLL + * to fetch frames. + */ + + /* * Count frame now that we know it's to be processed. */ if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { @@ -1384,12 +1395,17 @@ if (scan.tim != NULL) { struct ieee80211_tim_ie *tim = (struct ieee80211_tim_ie *) scan.tim; -#if 0 + /* + * XXX Check/debug this code; see if it's about + * the right time to force the VAP awake if we + * receive a frame destined for us? + */ +#if 1 int aid = IEEE80211_AID(ni->ni_associd); int ix = aid / NBBY; int min = tim->tim_bitctl &~ 1; int max = tim->tim_len + min - 4; - if ((tim->tim_bitctl&1) || + if ((tim->tim_bitctl & 1) || (min <= ix && ix <= max && isset(tim->tim_bitmap - min, aid))) { /* @@ -1396,8 +1412,10 @@ * XXX Do not let bg scan kick off * we are expecting data. */ + ieee80211_sta_tim_notify(vap, 1); ic->ic_lastdata = ticks; - vap->iv_sta_ps(vap, 0); + // XXX not yet? +// vap->iv_sta_ps(vap, 0); } #endif ni->ni_dtim_count = tim->tim_count; Index: sys/net80211/ieee80211_var.h =================================================================== --- sys/net80211/ieee80211_var.h (revision 264291) +++ sys/net80211/ieee80211_var.h (working copy) @@ -504,6 +504,9 @@ struct sockaddr *, struct route *); #endif uint64_t iv_spare[6]; + + /* station power save state stuff */ + struct callout iv_stapstimer; }; MALLOC_DECLARE(M_80211_VAP); @@ -629,6 +632,7 @@ #define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */ #define IEEE80211_C_DFS 0x00020000 /* CAPABILITY: DFS/radar avail*/ #define IEEE80211_C_MBSS 0x00040000 /* CAPABILITY: MBSS available */ +#define IEEE80211_C_SWSLEEP 0x00080000 /* CAPABILITY: do sleep here */ /* 0x7c0000 available */ #define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ #define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */