From d98a0bcf56cb1796ee67614a3ca0c005424745ef Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Sat, 3 May 2014 14:25:53 -0700 Subject: [PATCH] wlan/atheros - Synchronize sleep state code from FreeBSD * Synchronize Adrian Chadd's sleep state code and wlan updates. * With this commit if the wlan or ath interface is in a 'down' state, it will use full sleep mode and save power. * We get all of ATH through today but the 802_11 changes are a bit too substantial so this commit only brings in the sleep state code through today (3-May-2014). * There is a bunch of other 802_11 work that needs to be brought in but it's like 20,000 lines of patches so... not today. * Verified operational on Acer C720 chromebook. full-sleep mode saves 0.3-0.4W. Network sleep mode code is also verified to be operational but does not appear to save any power at the moment. Also verified: beacons are working properly for wakeups on packet reception. --- .../dev/ath/ath_hal/ar9300/ar9300_attach.c | 4 + .../dev/ath/ath_hal/ar9300/ar9300_freebsd.c | 6 + .../dev/ath/ath_hal/ar9300/ar9300_power.c | 18 +- sys/dev/netif/ath/ath/if_ath.c | 482 ++++++++++++++++-- sys/dev/netif/ath/ath/if_ath_beacon.c | 83 ++- sys/dev/netif/ath/ath/if_ath_beacon.h | 1 + sys/dev/netif/ath/ath/if_ath_debug.h | 1 + sys/dev/netif/ath/ath/if_ath_keycache.c | 12 +- sys/dev/netif/ath/ath/if_ath_led.c | 4 + sys/dev/netif/ath/ath/if_ath_misc.h | 13 + sys/dev/netif/ath/ath/if_ath_rx.c | 109 +++- sys/dev/netif/ath/ath/if_ath_rx_edma.c | 26 +- sys/dev/netif/ath/ath/if_ath_sysctl.c | 2 + sys/dev/netif/ath/ath/if_ath_tx.c | 414 ++++++++++----- sys/dev/netif/ath/ath/if_athvar.h | 23 +- sys/dev/netif/ath/ath_hal/ah.c | 2 + sys/dev/netif/ath/ath_hal/ah.h | 5 + sys/dev/netif/ath/ath_hal/ah_internal.h | 3 +- sys/dev/netif/ath/ath_hal/ah_osdep.c | 46 ++ sys/dev/netif/ath/ath_hal/ah_regdomain.c | 5 + .../ath/ath_hal/ah_regdomain/ah_rd_regenum.h | 1 + sys/dev/netif/ath/ath_hal/ar5210/ar5210.h | 3 +- .../netif/ath/ath_hal/ar5210/ar5210_attach.c | 2 +- .../netif/ath/ath_hal/ar5210/ar5210_misc.c | 1 + .../netif/ath/ath_hal/ar5210/ar5210_power.c | 12 +- .../netif/ath/ath_hal/ar5210/ar5210_reset.c | 4 + sys/dev/netif/ath/ath_hal/ar5211/ar5211.h | 1 - .../netif/ath/ath_hal/ar5211/ar5211_power.c | 12 +- sys/dev/netif/ath/ath_hal/ar5212/ar5212.h | 1 - .../netif/ath/ath_hal/ar5212/ar5212_power.c | 10 +- .../netif/ath/ath_hal/ar5312/ar5312_power.c | 5 +- .../netif/ath/ath_hal/ar5416/ar5416_attach.c | 5 + .../netif/ath/ath_hal/ar5416/ar5416_beacon.c | 26 + .../ath/ath_hal/ar5416/ar5416_interrupts.c | 3 + .../netif/ath/ath_hal/ar5416/ar5416_power.c | 13 +- sys/dev/netif/ath/ath_hal/ar5416/ar5416reg.h | 4 +- sys/netproto/802_11/ieee80211_power.h | 3 + sys/netproto/802_11/ieee80211_proto.h | 2 + sys/netproto/802_11/ieee80211_var.h | 3 + sys/netproto/802_11/wlan/ieee80211.c | 3 +- sys/netproto/802_11/wlan/ieee80211_ioctl.c | 14 +- sys/netproto/802_11/wlan/ieee80211_output.c | 29 +- sys/netproto/802_11/wlan/ieee80211_power.c | 87 ++++ sys/netproto/802_11/wlan/ieee80211_proto.c | 4 +- sys/netproto/802_11/wlan/ieee80211_sta.c | 59 ++- 45 files changed, 1328 insertions(+), 238 deletions(-) diff --git a/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c index 335414e2b0..46833ef749 100644 --- a/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c +++ b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c @@ -2934,6 +2934,10 @@ ar9300_fill_capability_info(struct ath_hal *ah) p_cap->halRxUsingLnaMixing = AH_TRUE; } + /* + * AR5416 and later NICs support MYBEACON filtering. + */ + p_cap->halRxDoMyBeacon = AH_TRUE; #if ATH_WOW_OFFLOAD if (AR_SREV_JUPITER_20_OR_LATER(ah) || AR_SREV_APHRODITE(ah)) { diff --git a/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c index 251957eaae..a2cc25d741 100644 --- a/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c +++ b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c @@ -60,6 +60,11 @@ ar9300_freebsd_set_tx_power_limit(struct ath_hal *ah, uint32_t limit) return (ar9300_set_tx_power_limit(ah, limit, 0, 0)); } +static uint64_t +ar9300_get_next_tbtt(struct ath_hal *ah) +{ + return (OS_REG_READ(ah, AR_NEXT_TBTT_TIMER)); +} void ar9300_attach_freebsd_ops(struct ath_hal *ah) @@ -195,6 +200,7 @@ ar9300_attach_freebsd_ops(struct ath_hal *ah) ah->ah_setStationBeaconTimers = ar9300_set_sta_beacon_timers; /* ah_resetStationBeaconTimers */ /* ah_getNextTBTT */ + ah->ah_getNextTBTT = ar9300_get_next_tbtt; /* Interrupt functions */ ah->ah_isInterruptPending = ar9300_is_interrupt_pending; diff --git a/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_power.c b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_power.c index 44bebe4a25..0fe4d6323e 100644 --- a/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_power.c +++ b/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_power.c @@ -666,9 +666,17 @@ ar9300_set_power_mode(struct ath_hal *ah, HAL_POWER_MODE mode, int set_chip) HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: %s -> %s (%s)\n", __func__, modes[ar9300_get_power_mode(ah)], modes[mode], set_chip ? "set chip " : ""); - + +#if 0 + kprintf("%s: %s -> %s (%s)\n", __func__, + modes[ar9300_get_power_mode(ah)], modes[mode], + set_chip ? "set chip " : ""); +#endif + switch (mode) { case HAL_PM_AWAKE: + if (set_chip) + ah->ah_powerMode = mode; status = ar9300_set_power_mode_awake(ah, set_chip); #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { @@ -698,7 +706,10 @@ ar9300_set_power_mode(struct ath_hal *ah, HAL_POWER_MODE mode, int set_chip) } #endif ar9300_set_power_mode_sleep(ah, set_chip); - ahp->ah_chip_full_sleep = AH_TRUE; + if (set_chip) { + ahp->ah_chip_full_sleep = AH_TRUE; + ah->ah_powerMode = mode; + } break; case HAL_PM_NETWORK_SLEEP: #if ATH_SUPPORT_MCI @@ -707,6 +718,9 @@ ar9300_set_power_mode(struct ath_hal *ah, HAL_POWER_MODE mode, int set_chip) } #endif ar9300_set_power_mode_network_sleep(ah, set_chip); + if (set_chip) { + ah->ah_powerMode = mode; + } break; default: HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, diff --git a/sys/dev/netif/ath/ath/if_ath.c b/sys/dev/netif/ath/ath/if_ath.c index 2e0d0e59de..279f7ce735 100644 --- a/sys/dev/netif/ath/ath/if_ath.c +++ b/sys/dev/netif/ath/ath/if_ath.c @@ -288,6 +288,164 @@ ath_legacy_attach_comp_func(struct ath_softc *sc) } } +/* + * 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); + + /* + * 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); + } +} + +/* + * 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; + + /* + * 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); + } + + } +} + +/* + * 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); + } + + /* + * 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) #define HAL_MODE_HT40 \ (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ @@ -359,6 +517,10 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) 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 @@ -631,6 +793,8 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) #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. @@ -1019,6 +1183,12 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) 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); @@ -1067,6 +1237,18 @@ ath_detach(struct ath_softc *sc) * 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_power_set_power_state(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + + /* + * Stop things cleanly. + */ ath_stop(ifp); wlan_serialize_enter(); ieee80211_ifdetach(ifp->if_l2com); @@ -1441,6 +1623,8 @@ ath_vap_delete(struct ieee80211vap *vap) struct ath_hal *ah = sc->sc_ah; struct ath_vap *avp = ATH_VAP(vap); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); if (ifp->if_flags & IFF_RUNNING) { /* @@ -1454,6 +1638,8 @@ ath_vap_delete(struct ieee80211vap *vap) ath_stoprecv(sc, 1); /* stop recv side */ } + /* .. leave the hardware awake for now. */ + ieee80211_vap_detach(vap); /* @@ -1541,6 +1727,9 @@ ath_vap_delete(struct ieee80211vap *vap) } ath_hal_intrset(ah, sc->sc_imask); } + + /* Ok, let the hardware asleep. */ + ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } @@ -1586,8 +1775,10 @@ ath_reset_keycache(struct ath_softc *sc) struct ath_hal *ah = sc->sc_ah; int i; + 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); ieee80211_crypto_reload_keys(ic); } @@ -1639,6 +1830,12 @@ ath_resume(struct ath_softc *sc) 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_setselfgen(sc, HAL_PM_AWAKE); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + 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); @@ -1671,6 +1868,8 @@ ath_resume(struct ath_softc *sc) if (sc->sc_resume_up) ieee80211_resume_all(ic); + ath_power_restore_power_state(sc); + /* XXX beacons ? */ } @@ -1728,6 +1927,8 @@ ath_intr(void *arg) return; } + ath_power_set_power_state(sc, HAL_PM_AWAKE); + if ((ifp->if_flags & IFF_UP) == 0 || (ifp->if_flags & IFF_RUNNING) == 0) { HAL_INT status; @@ -1737,6 +1938,7 @@ ath_intr(void *arg) ath_hal_getisr(ah, &status); /* clear ISR */ ath_hal_intrset(ah, 0); /* disable further intr's */ ATH_PCU_UNLOCK(sc); + ath_power_restore_power_state(sc); return; } @@ -1776,6 +1978,7 @@ ath_intr(void *arg) /* Short-circuit un-handled interrupts */ if (status == 0x0) { ATH_PCU_UNLOCK(sc); + ath_power_restore_power_state(sc); return; } @@ -1942,10 +2145,16 @@ ath_intr(void *arg) 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_power_restore_power_state(sc); } static void @@ -1978,6 +2187,8 @@ ath_fatal_proc(void *arg, int pending) 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 @@ -1986,9 +2197,15 @@ ath_bmiss_vap(struct ieee80211vap *vap) * 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_power_set_power_state(sc, HAL_PM_AWAKE); + if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) { - struct ifnet *ifp = vap->iv_ic->ic_ifp; - struct ath_softc *sc = ifp->if_softc; u_int64_t lastrx = sc->sc_lastrx; u_int64_t tsf = ath_hal_gettsf64(sc->sc_ah); /* XXX should take a locked ref to iv_bss */ @@ -2003,12 +2220,26 @@ ath_bmiss_vap(struct ieee80211vap *vap) if (tsf - lastrx <= bmisstimeout) { sc->sc_stats.ast_bmiss_phantom++; + ath_power_restore_power_state(sc); return; } } + + /* + * There's no need to keep the hardware awake during the call + * to av_bmiss(). + */ + ath_power_restore_power_state(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) { @@ -2038,6 +2269,8 @@ ath_bmiss_proc(void *arg, int pending) * to clear. */ wlan_serialize_enter(); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_beacon_miss(sc); if (ath_hal_gethangstate(sc->sc_ah, 0xff, &hangs) && hangs != 0) { ath_reset(ifp, ATH_RESET_NOLOSS); if_printf(ifp, "bb hang detected (0x%x), resetting\n", hangs); @@ -2045,6 +2278,11 @@ ath_bmiss_proc(void *arg, int pending) 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_power_restore_power_state(sc); + wlan_serialize_exit(); } @@ -2156,6 +2394,7 @@ ath_init(void *arg) */ if (ath_startrecv(sc) != 0) { if_printf(ifp, "unable to start recv logic\n"); + ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return; } @@ -2182,6 +2421,15 @@ ath_init(void *arg) 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; @@ -2193,6 +2441,7 @@ ath_init(void *arg) 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 @@ -2213,6 +2462,12 @@ ath_stop_locked(struct ifnet *ifp) __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_flags & IFF_RUNNING) { /* * Shutdown the hardware and driver: @@ -2253,9 +2508,20 @@ ath_stop_locked(struct ifnet *ifp) 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) { @@ -2274,7 +2540,7 @@ ath_txrx_stop_locked(struct ath_softc *sc) sc->sc_txstart_cnt || sc->sc_intr_cnt) { if (i <= 0) break; - wlan_serialize_sleep(sc, 0, "ath_txrx_stop", 1); + wlan_serialize_sleep(sc, 0, "ath_txrx_stop", (hz + 99) / 100); i--; } @@ -2321,7 +2587,7 @@ ath_txrx_start(struct ath_softc *sc) * 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) { @@ -2339,7 +2605,8 @@ ath_reset_grablock(struct ath_softc *sc, int dowait) break; } ATH_PCU_UNLOCK(sc); - wlan_serialize_sleep(sc, 0, "ath_reset_grablock", 1); + wlan_serialize_sleep(sc, 0, "ath_reset_grablock", + (hz + 9) / 10); i--; ATH_PCU_LOCK(sc); } while (i > 0); @@ -2404,6 +2671,11 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) /* Try to (stop any further TX/RX from occuring */ taskqueue_block(sc->sc_tq); + /* + * Wake the hardware up. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_PCU_LOCK(sc); /* @@ -2498,9 +2770,13 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) * 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); @@ -2514,6 +2790,8 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) * run. */ + /* XXX TODO: we need to hold the tx refcount here! */ + /* Restart TX/RX as needed */ ath_txrx_start(sc); @@ -2544,6 +2822,12 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) IF_UNLOCK(&ifp->if_snd); #endif + ath_power_restore_power_state(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 @@ -2684,6 +2968,7 @@ ath_buf_clone(struct ath_softc *sc, struct ath_buf *bf) 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 */ @@ -2799,6 +3084,9 @@ ath_transmit(struct ifnet *ifp, struct mbuf *m) sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); + /* Wake the hardware up already */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: start"); /* * Grab the TX lock - it's ok to do this here; we haven't @@ -3035,6 +3323,9 @@ finish: sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); + /* Sleep the hardware if required */ + ath_power_restore_power_state(sc); + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: finished"); return (retval); @@ -3062,7 +3353,6 @@ ath_key_update_begin(struct ieee80211vap *vap) 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 @@ -3072,7 +3362,6 @@ ath_key_update_end(struct ieee80211vap *vap) 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); } @@ -3083,8 +3372,10 @@ ath_update_promisc(struct ifnet *ifp) u_int32_t rfilt; /* configure rx filter */ + 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); DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); } @@ -3124,7 +3415,9 @@ ath_update_mcast(struct ifnet *ifp) #endif } else mfilt[0] = mfilt[1] = ~0; + 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); DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", __func__, mfilt[0], mfilt[1]); } @@ -3188,7 +3481,10 @@ ath_setslottime(struct ath_softc *sc) __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_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_setslottime(ah, usec); + ath_power_restore_power_state(sc); sc->sc_updateslot = OK; } @@ -3206,6 +3502,8 @@ ath_updateslot(struct ifnet *ifp) * 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) @@ -4337,6 +4635,8 @@ ath_tx_proc_q0(void *arg, int npending) sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0: txqs=0x%08x", txqs); @@ -4360,6 +4660,8 @@ ath_tx_proc_q0(void *arg, int npending) sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ath_power_restore_power_state(sc); + ath_tx_kick(sc); wlan_serialize_exit(); } @@ -4385,6 +4687,8 @@ ath_tx_proc_q0123(void *arg, int npending) sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0123: txqs=0x%08x", txqs); @@ -4420,6 +4724,8 @@ ath_tx_proc_q0123(void *arg, int npending) sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ath_power_restore_power_state(sc); + ath_tx_kick(sc); wlan_serialize_exit(); } @@ -4444,6 +4750,8 @@ ath_tx_proc(void *arg, int npending) sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc: txqs=0x%08x", txqs); /* @@ -4472,6 +4780,8 @@ ath_tx_proc(void *arg, int npending) sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ath_power_restore_power_state(sc); + ath_tx_kick(sc); wlan_serialize_exit(); } @@ -4502,6 +4812,8 @@ ath_txq_sched_tasklet(void *arg, int npending) sc->sc_txproc_cnt++; ATH_PCU_UNLOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_TX_LOCK(sc); for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { @@ -4510,6 +4822,8 @@ ath_txq_sched_tasklet(void *arg, int npending) } ATH_TX_UNLOCK(sc); + ath_power_restore_power_state(sc); + ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); @@ -5166,6 +5480,15 @@ ath_calibrate(void *arg) 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; + wlan_serialize_enter(); if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ goto restart; @@ -5268,6 +5591,10 @@ restart: /* NB: don't rearm timer */ } done: + /* + * Restore power state now that we're done. + */ + ath_power_restore_power_state(sc); wlan_serialize_exit(); } @@ -5362,8 +5689,10 @@ ath_set_channel(struct ieee80211com *ic) * sta/ibss mode this has no effect in other operating modes. */ ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan) sc->sc_syncbeacon = 1; + ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } @@ -5396,6 +5725,7 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 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 */ @@ -5409,7 +5739,7 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) }; DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[vap->iv_state], + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); /* @@ -5421,7 +5751,24 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) */ IEEE80211_LOCK_ASSERT(ic); - if (vap->iv_state == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) + /* Before we touch the hardware - wake it up */ + /* + * 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); + + if (ostate == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) csa_run_transition = 1; wlan_serialize_exit(); @@ -5436,6 +5783,11 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) * [re]setup beacons. Unblock the task q thread so * deferred interrupt processing is done. */ + + /* Ensure we stay awake during scan */ + ath_power_setselfgen(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + ath_hal_intrset(ah, sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); @@ -5448,6 +5800,11 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 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); @@ -5552,25 +5909,30 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) * 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; - /* - * PR: kern/175227 - * - * Reconfigure beacons during reset; as otherwise - * we won't get the beacon timers reprogrammed - * after a reset and thus we won't pick up a - * beacon miss interrupt. - * - * Hopefully we'll see a beacon before the BMISS - * timer fires (too often), leading to a STA - * disassociation. - */ - sc->sc_beacons = 1; + if (csa_run_transition) + ath_beacon_config(sc, vap); + + /* + * PR: kern/175227 + * + * Reconfigure beacons during reset; as + * otherwise + * we won't get the beacon timers reprogrammed + * after a reset and thus we won't pick up a + * beacon miss interrupt. + * + * Hopefully we'll see a beacon before the BMISS + * timer fires (too often), leading to a STA + * disassociation. + */ + sc->sc_beacons = 1; + } break; case IEEE80211_M_MONITOR: /* @@ -5596,6 +5958,13 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 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_power_setselfgen(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + /* * Finally, start any timers and the task q thread * (in case we didn't go through SCAN state). @@ -5607,6 +5976,7 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); } + taskqueue_unblock(sc->sc_tq); } else if (nstate == IEEE80211_S_INIT) { /* @@ -5626,9 +5996,42 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) #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); + /* + * Always at least set the self-generated + * frame config to set PWRMGT=1. + */ + ath_power_setselfgen(sc, HAL_PM_NETWORK_SLEEP); + + /* + * If we're not syncing beacons, transition + * to NETWORK_SLEEP. + * + * We stay awake if syncbeacon > 0 in case + * we need to listen for some beacons otherwise + * our beacon timer config may be wrong. + */ + if (sc->sc_syncbeacon == 0) { + ath_power_setpower(sc, HAL_PM_NETWORK_SLEEP); + } + } } bad: ieee80211_free_node(ni); + + /* + * Restore the power state - either to what it was, or + * to network_sleep if it's alright. + */ + ath_power_restore_power_state(sc); + return error; } @@ -5683,7 +6086,16 @@ ath_newassoc(struct ieee80211_node *ni, int isnew) an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); + DPRINTF(sc, ATH_DEBUG_NODE, + "%s: %s: reassoc; isnew=%d, is_powersave=%d\n", + __func__, + ath_hal_ether_sprintf(ni->ni_macaddr), + isnew, + an->an_is_powersave); + + ATH_NODE_LOCK(an); ath_rate_newassoc(sc, an, isnew); + ATH_NODE_UNLOCK(an); if (isnew && (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey && @@ -5927,6 +6339,8 @@ ath_watchdog(void *arg) struct ifnet *ifp = sc->sc_ifp; uint32_t hangs; + ath_power_set_power_state(sc, HAL_PM_AWAKE); + if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) { if_printf(ifp, "%s hang detected (0x%x)\n", @@ -5936,6 +6350,7 @@ ath_watchdog(void *arg) do_reset = 1; ifp->if_oerrors++; sc->sc_stats.ast_watchdog++; + ath_power_restore_power_state(sc); } /* @@ -6060,6 +6475,10 @@ ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) goto bad; } } + + if (id != HAL_DIAG_REGS) + ath_power_set_power_state(sc, HAL_PM_AWAKE); + if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { if (outsize < ad->ad_out_size) ad->ad_out_size = outsize; @@ -6069,6 +6488,8 @@ ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) } else { error = EINVAL; } + if (id != HAL_DIAG_REGS) + ath_power_restore_power_state(sc); bad: if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) kfree(indata, M_TEMP); @@ -6114,11 +6535,8 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, ath_init(sc); /* XXX lose error */ } else { 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; diff --git a/sys/dev/netif/ath/ath/if_ath_beacon.c b/sys/dev/netif/ath/ath/if_ath_beacon.c index 14b6e49ab8..bd544096c9 100644 --- a/sys/dev/netif/ath/ath/if_ath_beacon.c +++ b/sys/dev/netif/ath/ath/if_ath_beacon.c @@ -378,7 +378,7 @@ ath_beacon_update(struct ieee80211vap *vap, int item) /* * Handle a beacon miss. */ -static void +void ath_beacon_miss(struct ath_softc *sc) { HAL_SURVEY_SAMPLE hs; @@ -914,7 +914,7 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) 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 */ @@ -930,9 +930,15 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) ni = ieee80211_ref_node(vap->iv_bss); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + /* 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) { /* @@ -978,14 +984,63 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) */ tsf = ath_hal_gettsf64(ah); tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; - do { - nexttbtt += intval; - if (--dtimcount < 0) { - dtimcount = dtimperiod - 1; - if (--cfpcount < 0) - cfpcount = cfpperiod - 1; + + 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); + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: beacon tsf=%llu, hw tsf=%llu, tsf delta=%lld\n", + __func__, + (unsigned long long) tsf_beacon, + (unsigned long long) tsf, + (long long) tsf - + (long long) tsf_beacon); + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: nexttbtt=%llu, beacon tsf delta=%lld\n", + __func__, + (unsigned long long) nexttbtt, + (long long) ((long long) nexttbtt * 1024LL) - (long long) tsf_beacon); + + /* XXX cfpcount? */ + + if (nexttbtt > tsftu) { + uint32_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 + uint32_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; } - } while (nexttbtt < tsftu); + } + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: adj nexttbtt=%llu, rx tsf delta=%lld\n", + __func__, + (unsigned long long) nexttbtt, + (long long) ((long long)nexttbtt * 1024LL) - (long long)tsf); + memset(&bs, 0, sizeof(bs)); bs.bs_intval = intval; bs.bs_nexttbtt = nexttbtt; @@ -1032,9 +1087,12 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); DPRINTF(sc, ATH_DEBUG_BEACON, - "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n" + "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u " + "nextdtim %u bmiss %u sleep %u cfp:period %u " + "maxdur %u next %u timoffset %u\n" , __func__ - , tsf, tsftu + , tsf + , tsftu , bs.bs_intval , bs.bs_nexttbtt , bs.bs_dtimperiod @@ -1111,8 +1169,9 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) 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_power_restore_power_state(sc); #undef FUDGE #undef TSF_TO_TU } diff --git a/sys/dev/netif/ath/ath/if_ath_beacon.h b/sys/dev/netif/ath/ath/if_ath_beacon.h index f3f73d7166..9a17149f07 100644 --- a/sys/dev/netif/ath/ath/if_ath_beacon.h +++ b/sys/dev/netif/ath/ath/if_ath_beacon.h @@ -48,5 +48,6 @@ extern int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni); 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 diff --git a/sys/dev/netif/ath/ath/if_ath_debug.h b/sys/dev/netif/ath/ath/if_ath_debug.h index edd53f2157..38d0d9c420 100644 --- a/sys/dev/netif/ath/ath/if_ath_debug.h +++ b/sys/dev/netif/ath/ath/if_ath_debug.h @@ -68,6 +68,7 @@ enum { 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 }; diff --git a/sys/dev/netif/ath/ath/if_ath_keycache.c b/sys/dev/netif/ath/ath/if_ath_keycache.c index 0a654b528a..f50525822c 100644 --- a/sys/dev/netif/ath/ath/if_ath_keycache.c +++ b/sys/dev/netif/ath/ath/if_ath_keycache.c @@ -75,6 +75,7 @@ #include #include +#include extern const char* ath_hal_ether_sprintf(const uint8_t *mac); @@ -197,6 +198,7 @@ ath_keyset(struct ath_softc *sc, struct ieee80211vap *vap, u_int8_t gmac[IEEE80211_ADDR_LEN]; const u_int8_t *mac; HAL_KEYVAL hk; + int ret; memset(&hk, 0, sizeof(hk)); /* @@ -250,13 +252,17 @@ ath_keyset(struct ath_softc *sc, struct ieee80211vap *vap, } else mac = k->wk_macaddr; + 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); + + return (ret); #undef N } @@ -491,6 +497,7 @@ ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix); + 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. @@ -514,6 +521,7 @@ ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) } } } + ath_power_restore_power_state(sc); return 1; } diff --git a/sys/dev/netif/ath/ath/if_ath_led.c b/sys/dev/netif/ath/ath/if_ath_led.c index 376e201c77..14fe1d3211 100644 --- a/sys/dev/netif/ath/ath/if_ath_led.c +++ b/sys/dev/netif/ath/ath/if_ath_led.c @@ -118,6 +118,8 @@ void ath_led_config(struct ath_softc *sc) { + ath_power_set_power_state(sc, HAL_PM_AWAKE); + /* Software LED blinking - GPIO controlled LED */ if (sc->sc_softled) { ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin, @@ -140,6 +142,8 @@ ath_led_config(struct ath_softc *sc) ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_led_net_pin, HAL_GPIO_OUTPUT_MUX_MAC_NETWORK_LED); } + + ath_power_restore_power_state(sc); } static void diff --git a/sys/dev/netif/ath/ath/if_ath_misc.h b/sys/dev/netif/ath/ath/if_ath_misc.h index cc3df2f551..4632976428 100644 --- a/sys/dev/netif/ath/ath/if_ath_misc.h +++ b/sys/dev/netif/ath/ath/if_ath_misc.h @@ -126,6 +126,19 @@ extern void ath_start_task(void *arg, int npending); extern void ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq); +/* + * Power state tracking. + */ +extern void _ath_power_setpower(struct ath_softc *sc, int power_state, const char *file, int line); +extern void _ath_power_set_selfgen(struct ath_softc *sc, int power_state, const char *file, int line); +extern void _ath_power_set_power_state(struct ath_softc *sc, int power_state, const char *file, int line); +extern void _ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line); + +#define ath_power_setpower(sc, ps) _ath_power_setpower(sc, ps, __FILE__, __LINE__) +#define ath_power_setselfgen(sc, ps) _ath_power_set_selfgen(sc, ps, __FILE__, __LINE__) +#define ath_power_set_power_state(sc, ps) _ath_power_set_power_state(sc, ps, __FILE__, __LINE__) +#define ath_power_restore_power_state(sc) _ath_power_restore_power_state(sc, __FILE__, __LINE__) + /* * Kick the frame TX task. */ diff --git a/sys/dev/netif/ath/ath/if_ath_rx.c b/sys/dev/netif/ath/ath/if_ath_rx.c index 84af07a03d..0812781cb7 100644 --- a/sys/dev/netif/ath/ath/if_ath_rx.c +++ b/sys/dev/netif/ath/ath/if_ath_rx.c @@ -163,10 +163,22 @@ ath_calcrxfilter(struct ath_softc *sc) /* 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 @@ -314,6 +326,23 @@ ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, { struct ieee80211vap *vap = ni->ni_vap; struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + uint64_t tsf_beacon_old, tsf_beacon; + uint64_t nexttbtt; + int64_t tsf_delta; + int32_t tsf_delta_bmiss; + int32_t tsf_remainder; + uint64_t tsf_beacon_target; + int tsf_intval; + + tsf_beacon_old = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon_old |= LE_READ_4(ni->ni_tstamp.data); + +#define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10) + tsf_intval = 1; + if (ni != NULL && ni->ni_intval > 0) { + tsf_intval = TU_TO_TSF(ni->ni_intval); + } +#undef TU_TO_TSF /* * Call up first so subsequent work can use information @@ -325,14 +354,79 @@ ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, /* update rssi statistics for use by the hal */ /* XXX unlocked check against vap->iv_bss? */ ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); + + tsf_beacon = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon |= LE_READ_4(ni->ni_tstamp.data); + + nexttbtt = ath_hal_getnexttbtt(sc->sc_ah); + + /* + * Let's calculate the delta and remainder, so we can see + * if the beacon timer from the AP is varying by more than + * a few TU. (Which would be a huge, huge problem.) + */ + tsf_delta = (long long) tsf_beacon - (long long) tsf_beacon_old; + + tsf_delta_bmiss = tsf_delta / tsf_intval; + + /* + * If our delta is greater than half the beacon interval, + * let's round the bmiss value up to the next beacon + * interval. Ie, we're running really, really early + * on the next beacon. + */ + if (tsf_delta % tsf_intval > (tsf_intval / 2)) + tsf_delta_bmiss ++; + + tsf_beacon_target = tsf_beacon_old + + (((unsigned long long) tsf_delta_bmiss) * (long long) tsf_intval); + + /* + * The remainder using '%' is between 0 .. intval-1. + * If we're actually running too fast, then the remainder + * will be some large number just under intval-1. + * So we need to look at whether we're running + * before or after the target beacon interval + * and if we are, modify how we do the remainder + * calculation. + */ + if (tsf_beacon < tsf_beacon_target) { + tsf_remainder = + -(tsf_intval - ((tsf_beacon - tsf_beacon_old) % tsf_intval)); + } else { + tsf_remainder = (tsf_beacon - tsf_beacon_old) % tsf_intval; + } + + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: old_tsf=%llu, new_tsf=%llu, target_tsf=%llu, delta=%lld, bmiss=%d, remainder=%d\n", + __func__, + (unsigned long long) tsf_beacon_old, + (unsigned long long) tsf_beacon, + (unsigned long long) tsf_beacon_target, + (long long) tsf_delta, + tsf_delta_bmiss, + tsf_remainder); + + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: tsf=%llu, nexttbtt=%llu, delta=%d\n", + __func__, + (unsigned long long) tsf_beacon, + (unsigned long long) nexttbtt, + (int32_t) tsf_beacon - (int32_t) nexttbtt + tsf_intval); + 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; } + + /* fall thru... */ case IEEE80211_FC0_SUBTYPE_PROBE_RESP: if (vap->iv_opmode == IEEE80211_M_IBSS && @@ -908,6 +1002,8 @@ ath_rx_proc(struct ath_softc *sc, int resched) kickpcu = sc->sc_kickpcu; ATH_PCU_UNLOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__); ngood = 0; nf = ath_hal_getchannoise(ah, sc->sc_curchan); @@ -1062,6 +1158,11 @@ rx_proc_next: } #undef PA2DESC + /* + * Put the hardware to sleep again if we're done with it. + */ + ath_power_restore_power_state(sc); + /* * If we hit the maximum number of frames in this round, * reschedule for another immediate pass. This gives diff --git a/sys/dev/netif/ath/ath/if_ath_rx_edma.c b/sys/dev/netif/ath/ath/if_ath_rx_edma.c index cdb9fdf373..53a15140db 100644 --- a/sys/dev/netif/ath/ath/if_ath_rx_edma.c +++ b/sys/dev/netif/ath/ath/if_ath_rx_edma.c @@ -282,17 +282,21 @@ static void ath_edma_recv_sched_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, int dosched) { - + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_edma_recv_proc_queue(sc, qtype, dosched); + ath_power_restore_power_state(sc); + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); } static void ath_edma_recv_sched(struct ath_softc *sc, int dosched) { - + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, dosched); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, dosched); + ath_power_restore_power_state(sc); + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); } @@ -306,6 +310,8 @@ ath_edma_recv_flush(struct ath_softc *sc) sc->sc_rxproc_cnt++; ATH_PCU_UNLOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + /* * Flush any active frames from FIFO -> deferred list */ @@ -315,9 +321,16 @@ ath_edma_recv_flush(struct ath_softc *sc) /* * 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_power_restore_power_state(sc); + ATH_PCU_LOCK(sc); sc->sc_rxproc_cnt--; ATH_PCU_UNLOCK(sc); @@ -552,12 +565,21 @@ ath_edma_recv_tasklet(void *arg, int npending) sc->sc_rxproc_cnt++; ATH_PCU_UNLOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 1); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 1); 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_power_restore_power_state(sc); + /* XXX inside IF_LOCK ? */ if (!ifq_is_oactive(&ifp->if_snd)) { #ifdef IEEE80211_SUPPORT_SUPERG diff --git a/sys/dev/netif/ath/ath/if_ath_sysctl.c b/sys/dev/netif/ath/ath/if_ath_sysctl.c index 833caae370..ddd46c8c14 100644 --- a/sys/dev/netif/ath/ath/if_ath_sysctl.c +++ b/sys/dev/netif/ath/ath/if_ath_sysctl.c @@ -109,10 +109,12 @@ ath_sysctl_slottime(SYSCTL_HANDLER_ARGS) int error; wlan_serialize_enter(); + ath_power_set_power_state(sc, HAL_PM_AWAKE); slottime = ath_hal_getslottime(sc->sc_ah); error = sysctl_handle_int(oidp, &slottime, 0, req); if (error == 0 && req->newptr) error = !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; + ath_power_restore_power_state(sc); wlan_serialize_exit(); return error; } diff --git a/sys/dev/netif/ath/ath/if_ath_tx.c b/sys/dev/netif/ath/ath/if_ath_tx.c index e61cae7de8..bc225f1050 100644 --- a/sys/dev/netif/ath/ath/if_ath_tx.c +++ b/sys/dev/netif/ath/ath/if_ath_tx.c @@ -739,37 +739,21 @@ ath_tx_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, ("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); @@ -1594,6 +1578,7 @@ ath_tx_normal_setup(struct ath_softc *sc, struct ieee80211_node *ni, 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 *); @@ -2085,6 +2070,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, int do_override; uint8_t type, subtype; int queue_to_head; + struct ath_node *an = ATH_NODE(ni); ATH_TX_LOCK_ASSERT(sc); @@ -2144,6 +2130,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, 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.. */ @@ -2162,12 +2149,24 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, 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? */ @@ -2240,8 +2239,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, /* 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; @@ -2333,11 +2331,14 @@ ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, "%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_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_TX_LOCK(sc); if ((ifp->if_flags & IFF_RUNNING) == 0 || sc->sc_invalid) { @@ -2416,7 +2417,12 @@ ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); + + /* Put the hardware back to sleep if required */ + ath_power_restore_power_state(sc); + return 0; + bad2: ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: bad2: m=%p, params=%p, " "bf=%p", @@ -2426,14 +2432,18 @@ bad2: ATH_TXBUF_LOCK(sc); ath_returnbuf_head(sc, bf); ATH_TXBUF_UNLOCK(sc); -bad: +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_power_restore_power_state(sc); + +badbad: ATH_KTR(sc, ATH_KTR_TX, 2, "ath_raw_xmit: bad0: m=%p, params=%p", m, params); ifp->if_oerrors++; @@ -2729,8 +2739,8 @@ ath_tx_update_baw(struct ath_softc *sc, struct ath_node *an, INCR(tid->baw_head, ATH_TID_MAX_BUFS); } DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, - "%s: baw is now %d:%d, baw head=%d\n", - __func__, tap->txa_start, tap->txa_wnd, tid->baw_head); + "%s: tid=%d: baw is now %d:%d, baw head=%d\n", + __func__, tid->tid, tap->txa_start, tap->txa_wnd, tid->baw_head); } static void @@ -3222,8 +3232,11 @@ ath_tx_tid_pause(struct ath_softc *sc, struct ath_tid *tid) ATH_TX_LOCK_ASSERT(sc); tid->paused++; - DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: paused = %d\n", - __func__, tid->paused); + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: [%s]: tid=%d, paused = %d\n", + __func__, + ath_hal_ether_sprintf(tid->an->an_node.ni_macaddr), + tid->tid, + tid->paused); } /* @@ -3240,15 +3253,22 @@ ath_tx_tid_resume(struct ath_softc *sc, struct ath_tid *tid) * until it's actually resolved. */ if (tid->paused == 0) { - DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, - "%s: %s: paused=0?\n", __func__, - ath_hal_ether_sprintf(tid->an->an_node.ni_macaddr)); + device_printf(sc->sc_dev, + "%s: [%s]: tid=%d, paused=0?\n", + __func__, + ath_hal_ether_sprintf( + tid->an->an_node.ni_macaddr), + tid->tid); } else { tid->paused--; } - DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: unpaused = %d\n", - __func__, tid->paused); + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: [%s]: tid=%d, unpaused = %d\n", + __func__, + ath_hal_ether_sprintf(tid->an->an_node.ni_macaddr), + tid->tid, + tid->paused); if (tid->paused) return; @@ -3314,8 +3334,8 @@ ath_tx_tid_filt_comp_buf(struct ath_softc *sc, struct ath_tid *tid, ATH_TX_LOCK_ASSERT(sc); if (! tid->isfiltered) { - DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: filter transition\n", - __func__); + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d; filter transition\n", + __func__, tid->tid); tid->isfiltered = 1; ath_tx_tid_pause(sc, tid); } @@ -3335,15 +3355,20 @@ static void ath_tx_tid_filt_comp_complete(struct ath_softc *sc, struct ath_tid *tid) { struct ath_buf *bf; + int do_resume = 0; ATH_TX_LOCK_ASSERT(sc); if (tid->hwq_depth != 0) return; - DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: hwq=0, transition back\n", - __func__); - tid->isfiltered = 0; + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d, hwq=0, transition back\n", + __func__, tid->tid); + if (tid->isfiltered == 1) { + tid->isfiltered = 0; + do_resume = 1; + } + /* XXX ath_tx_tid_resume() also calls ath_tx_set_clrdmask()! */ ath_tx_set_clrdmask(sc, tid->an); @@ -3353,16 +3378,21 @@ ath_tx_tid_filt_comp_complete(struct ath_softc *sc, struct ath_tid *tid) ATH_TID_INSERT_HEAD(tid, bf, bf_list); } - ath_tx_tid_resume(sc, tid); + /* And only resume if we had paused before */ + if (do_resume) + ath_tx_tid_resume(sc, tid); } /* * Called when a single (aggregate or otherwise) frame is completed. * - * Returns 1 if the buffer could be added to the filtered list - * (cloned or otherwise), 0 if the buffer couldn't be added to the + * Returns 0 if the buffer could be added to the filtered list + * (cloned or otherwise), 1 if the buffer couldn't be added to the * filtered list (failed clone; expired retry) and the caller should * free it and handle it like a failure (eg by sending a BAR.) + * + * since the buffer may be cloned, bf must be not touched after this + * if the return value is 0. */ static int ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid, @@ -3382,8 +3412,9 @@ ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid, "%s: bf=%p, seqno=%d, exceeded retries\n", __func__, bf, - bf->bf_state.bfs_seqno); - return (0); + SEQNO(bf->bf_state.bfs_seqno)); + retval = 1; /* error */ + goto finish; } /* @@ -3403,11 +3434,12 @@ ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid, DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: busy buffer couldn't be cloned (%p)!\n", __func__, bf); - retval = 1; + retval = 1; /* error */ } else { ath_tx_tid_filt_comp_buf(sc, tid, nbf); - retval = 0; + retval = 0; /* ok */ } +finish: ath_tx_tid_filt_comp_complete(sc, tid); return (retval); @@ -3432,10 +3464,11 @@ ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid, if (bf->bf_state.bfs_retries > SWMAX_RETRIES) { sc->sc_stats.ast_tx_swretrymax++; DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, - "%s: bf=%p, seqno=%d, exceeded retries\n", + "%s: tid=%d, bf=%p, seqno=%d, exceeded retries\n", __func__, + tid->tid, bf, - bf->bf_state.bfs_seqno); + SEQNO(bf->bf_state.bfs_seqno)); TAILQ_INSERT_TAIL(bf_q, bf, bf_list); goto next; } @@ -3443,8 +3476,8 @@ ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid, if (bf->bf_flags & ATH_BUF_BUSY) { nbf = ath_tx_retry_clone(sc, tid->an, tid, bf); DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, - "%s: busy buffer cloned: %p -> %p", - __func__, bf, nbf); + "%s: tid=%d, busy buffer cloned: %p -> %p, seqno=%d\n", + __func__, tid->tid, bf, nbf, SEQNO(bf->bf_state.bfs_seqno)); } else { nbf = bf; } @@ -3455,8 +3488,8 @@ ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid, */ if (nbf == NULL) { DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, - "%s: buffer couldn't be cloned! (%p)\n", - __func__, bf); + "%s: tid=%d, buffer couldn't be cloned! (%p) seqno=%d\n", + __func__, tid->tid, bf, SEQNO(bf->bf_state.bfs_seqno)); TAILQ_INSERT_TAIL(bf_q, bf, bf_list); } else { ath_tx_tid_filt_comp_buf(sc, tid, nbf); @@ -3694,7 +3727,7 @@ ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, txq = sc->sc_ac2q[tid->ac]; tap = ath_tx_get_tx_tid(an, tid->tid); - DPRINTF(sc, ATH_DEBUG_SW_TX, + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %s: bf=%p: addbaw=%d, dobaw=%d, " "seqno=%d, retry=%d\n", __func__, @@ -3705,7 +3738,7 @@ ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, bf->bf_state.bfs_dobaw, SEQNO(bf->bf_state.bfs_seqno), bf->bf_state.bfs_retries); - DPRINTF(sc, ATH_DEBUG_SW_TX, + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %s: bf=%p: txq[%d] axq_depth=%d, axq_aggr_depth=%d\n", __func__, pfx, @@ -3714,7 +3747,7 @@ ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, txq->axq_qnum, txq->axq_depth, txq->axq_aggr_depth); - DPRINTF(sc, ATH_DEBUG_SW_TX, + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %s: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d, " "isfiltered=%d\n", __func__, @@ -3725,7 +3758,7 @@ ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, tid->hwq_depth, tid->bar_wait, tid->isfiltered); - DPRINTF(sc, ATH_DEBUG_SW_TX, + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %s: tid %d: " "sched=%d, paused=%d, " "incomp=%d, baw_head=%d, " @@ -3740,9 +3773,10 @@ ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, ni->ni_txseqs[tid->tid]); /* XXX Dump the frame, see what it is? */ - ieee80211_dump_pkt(ni->ni_ic, - mtod(bf->bf_m, const uint8_t *), - bf->bf_m->m_len, 0, -1); + if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) + ieee80211_dump_pkt(ni->ni_ic, + mtod(bf->bf_m, const uint8_t *), + bf->bf_m->m_len, 0, -1); } /* @@ -3783,7 +3817,7 @@ ath_tx_tid_drain(struct ath_softc *sc, struct ath_node *an, if (t == 0) { ath_tx_tid_drain_print(sc, an, "norm", tid, bf); - t = 1; +// t = 1; } ATH_TID_REMOVE(tid, bf, bf_list); @@ -3799,7 +3833,7 @@ ath_tx_tid_drain(struct ath_softc *sc, struct ath_node *an, if (t == 0) { ath_tx_tid_drain_print(sc, an, "filt", tid, bf); - t = 1; +// t = 1; } ATH_TID_FILT_REMOVE(tid, bf, bf_list); @@ -4054,6 +4088,19 @@ ath_tx_normal_comp(struct ath_softc *sc, struct ath_buf *bf, int fail) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: hwq_depth < 0: %d\n", __func__, atid->hwq_depth); + /* If the TID is being cleaned up, track things */ + /* XXX refactor! */ + if (atid->cleanup_inprogress) { + atid->incomp--; + if (atid->incomp == 0) { + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: TID %d: cleaned up! resume!\n", + __func__, tid); + atid->cleanup_inprogress = 0; + ath_tx_tid_resume(sc, atid); + } + } + /* * If the queue is filtered, potentially mark it as complete * and reschedule it as needed. @@ -4101,6 +4148,16 @@ ath_tx_comp_cleanup_unaggr(struct ath_softc *sc, struct ath_buf *bf) ATH_TX_LOCK(sc); atid->incomp--; + + /* XXX refactor! */ + if (bf->bf_state.bfs_dobaw) { + ath_tx_update_baw(sc, an, atid, bf); + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + } + if (atid->incomp == 0) { DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: cleaned up! resume!\n", @@ -4113,14 +4170,72 @@ ath_tx_comp_cleanup_unaggr(struct ath_softc *sc, struct ath_buf *bf) ath_tx_default_comp(sc, bf, 0); } + +/* + * This as it currently stands is a bit dumb. Ideally we'd just + * fail the frame the normal way and have it permanently fail + * via the normal aggregate completion path. + */ +static void +ath_tx_tid_cleanup_frame(struct ath_softc *sc, struct ath_node *an, + int tid, struct ath_buf *bf_head, ath_bufhead *bf_cq) +{ + struct ath_tid *atid = &an->an_tid[tid]; + struct ath_buf *bf, *bf_next; + + ATH_TX_LOCK_ASSERT(sc); + + /* + * Remove this frame from the queue. + */ + ATH_TID_REMOVE(atid, bf_head, bf_list); + + /* + * Loop over all the frames in the aggregate. + */ + bf = bf_head; + while (bf != NULL) { + bf_next = bf->bf_next; /* next aggregate frame, or NULL */ + + /* + * If it's been added to the BAW we need to kick + * it out of the BAW before we continue. + * + * XXX if it's an aggregate, assert that it's in the + * BAW - we shouldn't have it be in an aggregate + * otherwise! + */ + if (bf->bf_state.bfs_addedbaw) { + ath_tx_update_baw(sc, an, atid, bf); + bf->bf_state.bfs_dobaw = 0; + } + + /* + * Give it the default completion handler. + */ + bf->bf_comp = ath_tx_normal_comp; + bf->bf_next = NULL; + + /* + * Add it to the list to free. + */ + TAILQ_INSERT_TAIL(bf_cq, bf, bf_list); + + /* + * Now advance to the next frame in the aggregate. + */ + bf = bf_next; + } +} + /* * Performs transmit side cleanup when TID changes from aggregated to - * unaggregated. + * unaggregated and during reassociation. * - * - Discard all retry frames from the s/w queue. - * - Fix the tx completion function for all buffers in s/w queue. - * - Count the number of unacked frames, and let transmit completion - * handle it later. + * For now, this just tosses everything from the TID software queue + * whether or not it has been retried and marks the TID as + * pending completion if there's anything for this TID queued to + * the hardware. * * The caller is responsible for pausing the TID and unpausing the * TID if no cleanup was required. Otherwise the cleanup path will @@ -4131,18 +4246,19 @@ ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid, ath_bufhead *bf_cq) { struct ath_tid *atid = &an->an_tid[tid]; - struct ieee80211_tx_ampdu *tap; struct ath_buf *bf, *bf_next; ATH_TX_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, - "%s: TID %d: called\n", __func__, tid); + "%s: TID %d: called; inprogress=%d\n", __func__, tid, + atid->cleanup_inprogress); /* * Move the filtered frames to the TX queue, before * we run off and discard/process things. */ + /* XXX this is really quite inefficient */ while ((bf = ATH_TID_FILT_LAST(atid, ath_bufhead_s)) != NULL) { ATH_TID_FILT_REMOVE(atid, bf, bf_list); @@ -4157,47 +4273,35 @@ ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid, */ bf = ATH_TID_FIRST(atid); while (bf) { - if (bf->bf_state.bfs_isretried) { - bf_next = TAILQ_NEXT(bf, bf_list); - ATH_TID_REMOVE(atid, bf, bf_list); - if (bf->bf_state.bfs_dobaw) { - ath_tx_update_baw(sc, an, atid, bf); - if (!bf->bf_state.bfs_addedbaw) - DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, - "%s: wasn't added: seqno %d\n", - __func__, - SEQNO(bf->bf_state.bfs_seqno)); - } - bf->bf_state.bfs_dobaw = 0; - /* - * Call the default completion handler with "fail" just - * so upper levels are suitably notified about this. - */ - TAILQ_INSERT_TAIL(bf_cq, bf, bf_list); - bf = bf_next; - continue; - } - /* Give these the default completion handler */ - bf->bf_comp = ath_tx_normal_comp; - bf = TAILQ_NEXT(bf, bf_list); + /* + * Grab the next frame in the list, we may + * be fiddling with the list. + */ + bf_next = TAILQ_NEXT(bf, bf_list); + + /* + * Free the frame and all subframes. + */ + ath_tx_tid_cleanup_frame(sc, an, tid, bf, bf_cq); + + /* + * Next frame! + */ + bf = bf_next; } /* - * Calculate what hardware-queued frames exist based - * on the current BAW size. Ie, what frames have been - * added to the TX hardware queue for this TID but - * not yet ACKed. + * If there's anything in the hardware queue we wait + * for the TID HWQ to empty. */ - tap = ath_tx_get_tx_tid(an, tid); - /* Need the lock - fiddling with BAW */ - while (atid->baw_head != atid->baw_tail) { - if (atid->tx_buf[atid->baw_head]) { - atid->incomp++; - atid->cleanup_inprogress = 1; - atid->tx_buf[atid->baw_head] = NULL; - } - INCR(atid->baw_head, ATH_TID_MAX_BUFS); - INCR(tap->txa_start, IEEE80211_SEQ_RANGE); + if (atid->hwq_depth > 0) { + /* + * XXX how about we kill atid->incomp, and instead + * replace it with a macro that checks that atid->hwq_depth + * is 0? + */ + atid->incomp = atid->hwq_depth; + atid->cleanup_inprogress = 1; } if (atid->cleanup_inprogress) @@ -4530,9 +4634,19 @@ ath_tx_comp_cleanup_aggr(struct ath_softc *sc, struct ath_buf *bf_first) ATH_TX_LOCK(sc); /* update incomp */ + atid->incomp--; + + /* Update the BAW */ bf = bf_first; while (bf) { - atid->incomp--; + /* XXX refactor! */ + if (bf->bf_state.bfs_dobaw) { + ath_tx_update_baw(sc, an, atid, bf); + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + } bf = bf->bf_next; } @@ -4555,10 +4669,11 @@ ath_tx_comp_cleanup_aggr(struct ath_softc *sc, struct ath_buf *bf_first) ATH_TX_UNLOCK(sc); - /* Handle frame completion */ + /* Handle frame completion as individual frames */ bf = bf_first; while (bf) { bf_next = bf->bf_next; + bf->bf_next = NULL; ath_tx_default_comp(sc, bf, 1); bf = bf_next; } @@ -5000,6 +5115,10 @@ ath_tx_aggr_comp_unaggr(struct ath_softc *sc, struct ath_buf *bf, int fail) "%s: isfiltered=1, fail=%d\n", __func__, fail); freeframe = ath_tx_tid_filt_comp_single(sc, atid, bf); + /* + * If freeframe=0 then bf is no longer ours; don't + * touch it. + */ if (freeframe) { /* Remove from BAW */ if (bf->bf_state.bfs_addedbaw) @@ -5035,7 +5154,6 @@ ath_tx_aggr_comp_unaggr(struct ath_softc *sc, struct ath_buf *bf, int fail) if (freeframe) ath_tx_default_comp(sc, bf, fail); - return; } /* @@ -5740,12 +5858,26 @@ ath_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) */ TAILQ_INIT(&bf_cq); ATH_TX_LOCK(sc); - ath_tx_tid_cleanup(sc, an, tid, &bf_cq); + /* - * Unpause the TID if no cleanup is required. + * In case there's a followup call to this, only call it + * if we don't have a cleanup in progress. + * + * Since we've paused the queue above, we need to make + * sure we unpause if there's already a cleanup in + * progress - it means something else is also doing + * this stuff, so we don't need to also keep it paused. */ - if (! atid->cleanup_inprogress) + if (atid->cleanup_inprogress) { ath_tx_tid_resume(sc, atid); + } else { + ath_tx_tid_cleanup(sc, an, tid, &bf_cq); + /* + * Unpause the TID if no cleanup is required. + */ + if (! atid->cleanup_inprogress) + ath_tx_tid_resume(sc, atid); + } ATH_TX_UNLOCK(sc); /* Handle completing frames and fail them */ @@ -5779,18 +5911,24 @@ ath_tx_node_reassoc(struct ath_softc *sc, struct ath_node *an) tid = &an->an_tid[i]; if (tid->hwq_depth == 0) continue; - ath_tx_tid_pause(sc, tid); DPRINTF(sc, ATH_DEBUG_NODE, "%s: %s: TID %d: cleaning up TID\n", __func__, ath_hal_ether_sprintf(an->an_node.ni_macaddr), i); - ath_tx_tid_cleanup(sc, an, i, &bf_cq); /* - * Unpause the TID if no cleanup is required. + * In case there's a followup call to this, only call it + * if we don't have a cleanup in progress. */ - if (! tid->cleanup_inprogress) - ath_tx_tid_resume(sc, tid); + if (! tid->cleanup_inprogress) { + ath_tx_tid_pause(sc, tid); + ath_tx_tid_cleanup(sc, an, i, &bf_cq); + /* + * Unpause the TID if no cleanup is required. + */ + if (! tid->cleanup_inprogress) + ath_tx_tid_resume(sc, tid); + } } ATH_TX_UNLOCK(sc); @@ -5820,18 +5958,42 @@ ath_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, struct ath_node *an = ATH_NODE(ni); struct ath_tid *atid = &an->an_tid[tid]; int attempts = tap->txa_attempts; + int old_txa_start; DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, - "%s: %s: called; txa_tid=%d, atid->tid=%d, status=%d, attempts=%d\n", + "%s: %s: called; txa_tid=%d, atid->tid=%d, status=%d, attempts=%d" + ", txa_start=%d, txa_seqpending=%d\n", __func__, ath_hal_ether_sprintf(ni->ni_macaddr), tap->txa_ac, atid->tid, status, - attempts); + attempts, + tap->txa_start, + tap->txa_seqpending); /* Note: This may update the BAW details */ + /* + * XXX What if this does slide the BAW along? We need to somehow + * XXX either fix things when it does happen, or prevent the + * XXX seqpending value to be anything other than exactly what + * XXX the hell we want! + * + * XXX So for now, how I do this inside the TX lock for now + * XXX and just correct it afterwards? The below condition should + * XXX never happen and if it does I need to fix all kinds of things. + */ + old_txa_start = tap->txa_start; sc->sc_bar_response(ni, tap, status); + if (tap->txa_start != old_txa_start) { + device_printf(sc->sc_dev, + "%s: tid=%d; txa_start=%d, old=%d, adjusting\n", + __func__, + tid, + tap->txa_start, + old_txa_start); + } + tap->txa_start = old_txa_start; /* Unpause the TID */ /* diff --git a/sys/dev/netif/ath/ath/if_athvar.h b/sys/dev/netif/ath/ath/if_athvar.h index 3d609ecdf4..812587799a 100644 --- a/sys/dev/netif/ath/ath/if_athvar.h +++ b/sys/dev/netif/ath/ath/if_athvar.h @@ -633,7 +633,8 @@ struct ath_softc { 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. @@ -880,6 +881,22 @@ struct ath_softc { void (*sc_bar_response)(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status); + + /* + * 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; }; #define ATH_LOCK_INIT(_sc) @@ -1002,6 +1019,8 @@ void ath_intr(void *); ((*(_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) \ @@ -1230,6 +1249,8 @@ void ath_intr(void *); #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) diff --git a/sys/dev/netif/ath/ath_hal/ah.c b/sys/dev/netif/ath/ath_hal/ah.c index 7187d576e2..bbfc09b84f 100644 --- a/sys/dev/netif/ath/ath_hal/ah.c +++ b/sys/dev/netif/ath/ath_hal/ah.c @@ -786,6 +786,8 @@ ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, return HAL_OK; case HAL_CAP_RX_LNA_MIXING: /* Hardware uses an RX LNA mixer to map 2 antennas to a 1 stream receiver */ return pCap->halRxUsingLnaMixing ? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_DO_MYBEACON: /* Hardware supports filtering my-beacons */ + return pCap->halRxDoMyBeacon ? HAL_OK : HAL_ENOTSUPP; default: return HAL_EINVAL; } diff --git a/sys/dev/netif/ath/ath_hal/ah.h b/sys/dev/netif/ath/ath_hal/ah.h index 2480803300..4f2d3e9a0b 100644 --- a/sys/dev/netif/ath/ath_hal/ah.h +++ b/sys/dev/netif/ath/ath_hal/ah.h @@ -199,6 +199,7 @@ typedef enum { HAL_CAP_SERIALISE_WAR = 245, /* serialise register access on PCI */ HAL_CAP_ENFORCE_TXOP = 246, /* Enforce TXOP if supported */ HAL_CAP_RX_LNA_MIXING = 247, /* RX hardware uses LNA mixing */ + HAL_CAP_DO_MYBEACON = 248, /* Supports HAL_RX_FILTER_MYBEACON */ } HAL_CAPABILITY_TYPE; /* @@ -404,6 +405,7 @@ typedef enum { HAL_RX_FILTER_PROM = 0x00000020, /* Promiscuous mode */ HAL_RX_FILTER_PROBEREQ = 0x00000080, /* Allow probe request frames */ HAL_RX_FILTER_PHYERR = 0x00000100, /* Allow phy errors */ + HAL_RX_FILTER_MYBEACON = 0x00000200, /* Filter beacons other than mine */ HAL_RX_FILTER_COMPBAR = 0x00000400, /* Allow compressed BAR */ HAL_RX_FILTER_COMP_BA = 0x00000800, /* Allow compressed blockack */ HAL_RX_FILTER_PHYRADAR = 0x00002000, /* Allow phy radar errors */ @@ -1297,6 +1299,9 @@ struct ath_hal { uint32_t ah_intrstate[8]; /* last int state */ uint32_t ah_syncstate; /* last sync intr state */ + /* Current powerstate from HAL calls */ + HAL_POWER_MODE ah_powerMode; + HAL_OPS_CONFIG ah_config; const HAL_RATE_TABLE *__ahdecl(*ah_getRateTable)(struct ath_hal *, u_int mode); diff --git a/sys/dev/netif/ath/ath_hal/ah_internal.h b/sys/dev/netif/ath/ath_hal/ah_internal.h index 331093bddf..460c13824b 100644 --- a/sys/dev/netif/ath/ath_hal/ah_internal.h +++ b/sys/dev/netif/ath/ath_hal/ah_internal.h @@ -280,7 +280,8 @@ typedef struct { halAntDivCombSupportOrg : 1, halRadioRetentionSupport : 1, halSpectralScanSupport : 1, - halRxUsingLnaMixing : 1; + halRxUsingLnaMixing : 1, + halRxDoMyBeacon : 1; uint32_t halWirelessModes; uint16_t halTotalQueues; diff --git a/sys/dev/netif/ath/ath_hal/ah_osdep.c b/sys/dev/netif/ath/ath_hal/ah_osdep.c index 75e89677ef..e5b27dda70 100644 --- a/sys/dev/netif/ath/ath_hal/ah_osdep.c +++ b/sys/dev/netif/ath/ath_hal/ah_osdep.c @@ -149,6 +149,24 @@ ath_hal_ether_sprintf(const u_int8_t *mac) #ifdef AH_DEBUG +/* + * XXX This is highly relevant only for the AR5416 and later + * PCI/PCIe NICs. It'll need adjustment for other hardware + * variations. + */ +static int +ath_hal_reg_whilst_asleep(struct ath_hal *ah, uint32_t reg) +{ + + if (reg >= 0x4000 && reg < 0x5000) + return (1); + if (reg >= 0x6000 && reg < 0x7000) + return (1); + if (reg >= 0x7000 && reg < 0x8000) + return (1); + return (0); +} + void DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) { @@ -263,6 +281,13 @@ ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, val=0x%08x, pm=%d\n", + __func__, reg, val, ah->ah_powerMode); + } + if (ath_hal_alq) { struct ale *ale = ath_hal_alq_get(ah); if (ale) { @@ -288,6 +313,13 @@ ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) bus_space_handle_t h = ah->ah_sh; u_int32_t val; + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, pm=%d\n", + __func__, reg, ah->ah_powerMode); + } + if (ah->ah_config.ah_serialise_reg_war) spin_lock(&ah_regser_spin); val = bus_space_read_4(tag, h, reg); @@ -340,6 +372,20 @@ ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, val=0x%08x, pm=%d\n", + __func__, reg, val, ah->ah_powerMode); + } + + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, pm=%d\n", + __func__, reg, ah->ah_powerMode); + } + if (ah->ah_config.ah_serialise_reg_war) spin_lock(&ah_regser_spin); bus_space_write_4(tag, h, reg, val); diff --git a/sys/dev/netif/ath/ath_hal/ah_regdomain.c b/sys/dev/netif/ath/ath_hal/ah_regdomain.c index e871cd7bc8..c699448e45 100644 --- a/sys/dev/netif/ath/ath_hal/ah_regdomain.c +++ b/sys/dev/netif/ath/ath_hal/ah_regdomain.c @@ -169,6 +169,11 @@ isEepromValid(struct ath_hal *ah) if (regDomainPairs[i].regDmnEnum == rd) return AH_TRUE; } + + if (rd == FCC_UBNT) { + return AH_TRUE; + } + HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd); return AH_FALSE; diff --git a/sys/dev/netif/ath/ath_hal/ah_regdomain/ah_rd_regenum.h b/sys/dev/netif/ath/ath_hal/ah_regdomain/ah_rd_regenum.h index bc569cb00a..d8d211e593 100644 --- a/sys/dev/netif/ath/ath_hal/ah_regdomain/ah_rd_regenum.h +++ b/sys/dev/netif/ath/ath_hal/ah_regdomain/ah_rd_regenum.h @@ -51,6 +51,7 @@ enum { FCC2_FCCA = 0x20, /* Canada */ FCC2_WORLD = 0x21, /* Australia & HK */ FCC2_ETSIC = 0x22, + FCC_UBNT = 0x2A, /* Ubiquity PicoStation M2HP */ FRANCE_RES = 0x31, /* Legacy France for OEM */ FCC3_FCCA = 0x3A, /* USA & Canada w/5470 band, 11h, DFS enabled */ FCC3_WORLD = 0x3B, /* USA & Canada w/5470 band, 11h, DFS enabled */ diff --git a/sys/dev/netif/ath/ath_hal/ar5210/ar5210.h b/sys/dev/netif/ath/ath_hal/ar5210/ar5210.h index 48ccc20851..3e372f7616 100644 --- a/sys/dev/netif/ath/ath_hal/ar5210/ar5210.h +++ b/sys/dev/netif/ath/ath_hal/ar5210/ar5210.h @@ -108,7 +108,6 @@ struct ath_hal_5210 { uint32_t ah_txDescInterruptMask; uint32_t ah_txEolInterruptMask; uint32_t ah_txUrnInterruptMask; - HAL_POWER_MODE ah_powerMode; uint8_t ah_bssid[IEEE80211_ADDR_LEN]; HAL_TX_QUEUE_INFO ah_txq[HAL_NUM_TX_QUEUES]; /* beacon+cab+data */ /* @@ -121,6 +120,8 @@ struct ath_hal_5210 { u_int ah_slottime; /* user-specified slot time */ u_int ah_acktimeout; /* user-specified ack timeout */ u_int ah_ctstimeout; /* user-specified cts timeout */ + + uint16_t ah_associd; /* association id */ }; #define AH5210(ah) ((struct ath_hal_5210 *)(ah)) diff --git a/sys/dev/netif/ath/ath_hal/ar5210/ar5210_attach.c b/sys/dev/netif/ath/ath_hal/ar5210/ar5210_attach.c index 3c0ed71ba8..ee11ce9e68 100644 --- a/sys/dev/netif/ath/ath_hal/ar5210/ar5210_attach.c +++ b/sys/dev/netif/ath/ath_hal/ar5210/ar5210_attach.c @@ -219,7 +219,7 @@ ar5210Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, AH_PRIVATE(ah)->ah_powerLimit = AR5210_MAX_RATE_POWER; AH_PRIVATE(ah)->ah_tpScale = HAL_TP_SCALE_MAX; /* no scaling */ - ahp->ah_powerMode = HAL_PM_UNDEFINED; + ah->ah_powerMode = HAL_PM_UNDEFINED; ahp->ah_staId1Defaults = 0; ahp->ah_rssiThr = INIT_RSSI_THR; ahp->ah_sifstime = (u_int) -1; diff --git a/sys/dev/netif/ath/ath_hal/ar5210/ar5210_misc.c b/sys/dev/netif/ath/ath_hal/ar5210/ar5210_misc.c index 81708800fe..5eaa18e1d7 100644 --- a/sys/dev/netif/ath/ath_hal/ar5210/ar5210_misc.c +++ b/sys/dev/netif/ath/ath_hal/ar5210/ar5210_misc.c @@ -315,6 +315,7 @@ ar5210WriteAssocid(struct ath_hal *ah, const uint8_t *bssid, uint16_t assocId) /* XXX save bssid for possible re-use on reset */ OS_MEMCPY(ahp->ah_bssid, bssid, IEEE80211_ADDR_LEN); + ahp->ah_associd = assocId; OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid+4) | ((assocId & 0x3fff)< %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5210SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5210SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5210SetPowerModeAuto(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; - return status; + return status; } HAL_POWER_MODE diff --git a/sys/dev/netif/ath/ath_hal/ar5210/ar5210_reset.c b/sys/dev/netif/ath/ath_hal/ar5210/ar5210_reset.c index 1d50f99f54..1dba729463 100644 --- a/sys/dev/netif/ath/ath_hal/ar5210/ar5210_reset.c +++ b/sys/dev/netif/ath/ath_hal/ar5210/ar5210_reset.c @@ -152,8 +152,12 @@ ar5210Reset(struct ath_hal *ah, HAL_OPMODE opmode, /* Restore previous led state */ OS_REG_WRITE(ah, AR_PCICFG, OS_REG_READ(ah, AR_PCICFG) | ledstate); +#if 0 OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4)); +#endif + /* BSSID, association id, ps-poll */ + ar5210WriteAssocid(ah, ahp->ah_bssid, ahp->ah_associd); OS_REG_WRITE(ah, AR_TXDP0, 0); OS_REG_WRITE(ah, AR_TXDP1, 0); diff --git a/sys/dev/netif/ath/ath_hal/ar5211/ar5211.h b/sys/dev/netif/ath/ath_hal/ar5211/ar5211.h index c50531e33e..6f04624161 100644 --- a/sys/dev/netif/ath/ath_hal/ar5211/ar5211.h +++ b/sys/dev/netif/ath/ath_hal/ar5211/ar5211.h @@ -119,7 +119,6 @@ struct ath_hal_5211 { uint32_t ah_txEolInterruptMask; uint32_t ah_txUrnInterruptMask; HAL_TX_QUEUE_INFO ah_txq[HAL_NUM_TX_QUEUES]; - HAL_POWER_MODE ah_powerMode; HAL_ANT_SETTING ah_diversityControl; /* antenna setting */ uint32_t ah_calibrationTime; HAL_BOOL ah_bIQCalibration; diff --git a/sys/dev/netif/ath/ath_hal/ar5211/ar5211_power.c b/sys/dev/netif/ath/ath_hal/ar5211/ar5211_power.c index 776cfb32b5..e646d90b66 100644 --- a/sys/dev/netif/ath/ath_hal/ar5211/ar5211_power.c +++ b/sys/dev/netif/ath/ath_hal/ar5211/ar5211_power.c @@ -95,7 +95,6 @@ ar5211SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5211SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5211 *ahp = AH5211(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -107,25 +106,30 @@ ar5211SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5211SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5211SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5211SetPowerModeNetworkSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; - return status; + return status; } HAL_POWER_MODE diff --git a/sys/dev/netif/ath/ath_hal/ar5212/ar5212.h b/sys/dev/netif/ath/ath_hal/ar5212/ar5212.h index 6d38d65f72..8086e710d2 100644 --- a/sys/dev/netif/ath/ath_hal/ar5212/ar5212.h +++ b/sys/dev/netif/ath/ath_hal/ar5212/ar5212.h @@ -270,7 +270,6 @@ struct ath_hal_5212 { uint32_t ah_intrTxqs; /* tx q interrupt state */ /* decomp mask array */ uint8_t ah_decompMask[HAL_DECOMP_MASK_SIZE]; - HAL_POWER_MODE ah_powerMode; HAL_ANT_SETTING ah_antControl; /* antenna setting */ HAL_BOOL ah_diversity; /* fast diversity setting */ enum { diff --git a/sys/dev/netif/ath/ath_hal/ar5212/ar5212_power.c b/sys/dev/netif/ath/ath_hal/ar5212/ar5212_power.c index 3f755bdcfe..3068510dd8 100644 --- a/sys/dev/netif/ath/ath_hal/ar5212/ar5212_power.c +++ b/sys/dev/netif/ath/ath_hal/ar5212/ar5212_power.c @@ -119,7 +119,6 @@ ar5212SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5212 *ahp = AH5212(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -131,24 +130,29 @@ ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5212SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5212SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5212SetPowerModeNetworkSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; return status; } diff --git a/sys/dev/netif/ath/ath_hal/ar5312/ar5312_power.c b/sys/dev/netif/ath/ath_hal/ar5312/ar5312_power.c index 94a0f1c64b..7db6d80cb4 100644 --- a/sys/dev/netif/ath/ath_hal/ar5312/ar5312_power.c +++ b/sys/dev/netif/ath/ath_hal/ar5312/ar5312_power.c @@ -71,7 +71,6 @@ ar5312SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5312SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5212 *ahp = AH5212(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -83,7 +82,7 @@ ar5312SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: @@ -100,7 +99,7 @@ ar5312SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; + ah->ah_powerMode = mode; return status; } diff --git a/sys/dev/netif/ath/ath_hal/ar5416/ar5416_attach.c b/sys/dev/netif/ath/ath_hal/ar5416/ar5416_attach.c index 99bab06ee9..bdc61118b9 100644 --- a/sys/dev/netif/ath/ath_hal/ar5416/ar5416_attach.c +++ b/sys/dev/netif/ath/ath_hal/ar5416/ar5416_attach.c @@ -1059,6 +1059,11 @@ ar5416FillCapabilityInfo(struct ath_hal *ah) if (! AH_PRIVATE(ah)->ah_ispcie) pCap->halSerialiseRegWar = 1; + /* + * AR5416 and later NICs support MYBEACON filtering. + */ + pCap->halRxDoMyBeacon = AH_TRUE; + return AH_TRUE; } diff --git a/sys/dev/netif/ath/ath_hal/ar5416/ar5416_beacon.c b/sys/dev/netif/ath/ath_hal/ar5416/ar5416_beacon.c index e2bf6c7327..6691c11bcd 100644 --- a/sys/dev/netif/ath/ath_hal/ar5416/ar5416_beacon.c +++ b/sys/dev/netif/ath/ath_hal/ar5416/ar5416_beacon.c @@ -197,6 +197,25 @@ ar5416SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) * beacon jitter; cab timeout is max time to wait for cab * after seeing the last DTIM or MORE CAB bit */ + +/* + * I've bumped these to 30TU for now. + * + * Some APs (AR933x/AR934x?) in 2GHz especially seem to not always + * transmit beacon frames at exactly the right times and with it set + * to 10TU, the NIC starts not waking up at the right times to hear + * these slightly-larger-jitering beacons. It also never recovers + * from that (it doesn't resync? I'm not sure.) + * + * So for now bump this to 30TU. Ideally we'd cap this based on + * the beacon interval so the sum of CAB+BEACON timeouts never + * exceeded the beacon interval. + * + * Now, since we're doing all the math in the ath(4) driver in TU + * rather than TSF, we may be seeing the result of dumb rounding + * errors causing the jitter to actually be a much bigger problem. + * I'll have to investigate that with a fine tooth comb. + */ #define CAB_TIMEOUT_VAL 10 /* in TU */ #define BEACON_TIMEOUT_VAL 10 /* in TU */ #define SLEEP_SLOP 3 /* in TU */ @@ -248,6 +267,13 @@ ar5416SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_TBTT | AR_TIMER_MODE_TIM | AR_TIMER_MODE_DTIM); + +#define HAL_TSFOOR_THRESHOLD 0x00004240 /* TSF OOR threshold (16k us) */ + + /* TSF out of range threshold */ +// OS_REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold); + OS_REG_WRITE(ah, AR_TSFOOR_THRESHOLD, HAL_TSFOOR_THRESHOLD); + HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next DTIM %d\n", __func__, bs->bs_nextdtim); HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next beacon %d\n", diff --git a/sys/dev/netif/ath/ath_hal/ar5416/ar5416_interrupts.c b/sys/dev/netif/ath/ath_hal/ar5416/ar5416_interrupts.c index 631ca2fca2..32ce2ed5b0 100644 --- a/sys/dev/netif/ath/ath_hal/ar5416/ar5416_interrupts.c +++ b/sys/dev/netif/ath/ath_hal/ar5416/ar5416_interrupts.c @@ -337,6 +337,9 @@ ar5416SetInterrupts(struct ath_hal *ah, HAL_INT ints) /* Write the new IMR and store off our SW copy. */ HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: new IMR 0x%x\n", __func__, mask); OS_REG_WRITE(ah, AR_IMR, mask); + /* Flush write */ + (void) OS_REG_READ(ah, AR_IMR); + mask = OS_REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM | AR_IMR_S2_DTIM | AR_IMR_S2_DTIMSYNC | diff --git a/sys/dev/netif/ath/ath_hal/ar5416/ar5416_power.c b/sys/dev/netif/ath/ath_hal/ar5416/ar5416_power.c index 43a9241fc6..dff9a85591 100644 --- a/sys/dev/netif/ath/ath_hal/ar5416/ar5416_power.c +++ b/sys/dev/netif/ath/ath_hal/ar5416/ar5416_power.c @@ -124,7 +124,6 @@ ar5416SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5416SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5212 *ahp = AH5212(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -134,27 +133,35 @@ ar5416SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) }; #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[ahp->ah_powerMode], modes[mode], setChip ? "set chip " : ""); + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5416SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5416SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5416SetPowerModeNetworkSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode 0x%x\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; return status; } diff --git a/sys/dev/netif/ath/ath_hal/ar5416/ar5416reg.h b/sys/dev/netif/ath/ath_hal/ar5416/ar5416reg.h index 2602353140..29b84b1e90 100644 --- a/sys/dev/netif/ath/ath_hal/ar5416/ar5416reg.h +++ b/sys/dev/netif/ath/ath_hal/ar5416/ar5416reg.h @@ -476,10 +476,10 @@ /* Sleep control */ #define AR5416_SLEEP1_ASSUME_DTIM 0x00080000 #define AR5416_SLEEP1_CAB_TIMEOUT 0xFFE00000 /* Cab timeout (TU) */ -#define AR5416_SLEEP1_CAB_TIMEOUT_S 22 +#define AR5416_SLEEP1_CAB_TIMEOUT_S 21 #define AR5416_SLEEP2_BEACON_TIMEOUT 0xFFE00000 /* Beacon timeout (TU)*/ -#define AR5416_SLEEP2_BEACON_TIMEOUT_S 22 +#define AR5416_SLEEP2_BEACON_TIMEOUT_S 21 /* Sleep Registers */ #define AR_SLP32_HALFCLK_LATENCY 0x000FFFFF /* rising <-> falling edge */ diff --git a/sys/netproto/802_11/ieee80211_power.h b/sys/netproto/802_11/ieee80211_power.h index 11d17370e4..c23cf24dca 100644 --- a/sys/netproto/802_11/ieee80211_power.h +++ b/sys/netproto/802_11/ieee80211_power.h @@ -73,6 +73,9 @@ int ieee80211_node_psq_age(struct ieee80211_node *); 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_ */ diff --git a/sys/netproto/802_11/ieee80211_proto.h b/sys/netproto/802_11/ieee80211_proto.h index 198dfd00e2..b61f6d4c35 100644 --- a/sys/netproto/802_11/ieee80211_proto.h +++ b/sys/netproto/802_11/ieee80211_proto.h @@ -290,6 +290,8 @@ void ieee80211_dturbo_switch(struct ieee80211vap *, int newflags); void ieee80211_swbmiss_callout(void *arg); void ieee80211_beacon_miss(struct ieee80211com *); int ieee80211_new_state(struct ieee80211vap *, enum ieee80211_state, int); +int ieee80211_new_state_locked(struct ieee80211vap *, enum ieee80211_state, + int); void ieee80211_print_essid(const uint8_t *, int); void ieee80211_dump_pkt(struct ieee80211com *, const uint8_t *, int, int, int); diff --git a/sys/netproto/802_11/ieee80211_var.h b/sys/netproto/802_11/ieee80211_var.h index 05a871f682..f16c97a8c6 100644 --- a/sys/netproto/802_11/ieee80211_var.h +++ b/sys/netproto/802_11/ieee80211_var.h @@ -469,6 +469,8 @@ struct ieee80211vap { /* power save handling */ void (*iv_update_ps)(struct ieee80211vap *, int); int (*iv_set_tim)(struct ieee80211_node *, int); + void (*iv_node_ps)(struct ieee80211_node *, int); + void (*iv_sta_ps)(struct ieee80211vap *, int); /* state machine processing */ int (*iv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); @@ -601,6 +603,7 @@ MALLOC_DECLARE(M_80211_VAP); #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 */ diff --git a/sys/netproto/802_11/wlan/ieee80211.c b/sys/netproto/802_11/wlan/ieee80211.c index 5fea6685b5..f7bed2204b 100644 --- a/sys/netproto/802_11/wlan/ieee80211.c +++ b/sys/netproto/802_11/wlan/ieee80211.c @@ -1359,7 +1359,8 @@ ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) * 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 diff --git a/sys/netproto/802_11/wlan/ieee80211_ioctl.c b/sys/netproto/802_11/wlan/ieee80211_ioctl.c index 8a5b22d37e..2cadd9b6b4 100644 --- a/sys/netproto/802_11/wlan/ieee80211_ioctl.c +++ b/sys/netproto/802_11/wlan/ieee80211_ioctl.c @@ -600,7 +600,7 @@ ieee80211_ioctl_getcurchan(struct ieee80211vap *vap, struct ieee80211req *ireq) * in use. When in RUN state report the vap-specific channel. * Otherwise return curchan. */ - if (vap->iv_state == IEEE80211_S_RUN) + if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) c = vap->iv_bss->ni_chan; else c = ic->ic_curchan; @@ -919,7 +919,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, case IEEE80211_IOC_BSSID: if (ireq->i_len != IEEE80211_ADDR_LEN) return EINVAL; - if (vap->iv_state == IEEE80211_S_RUN) { + if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) { error = copyout(vap->iv_opmode == IEEE80211_M_WDS ? vap->iv_bss->ni_macaddr : vap->iv_bss->ni_bssid, ireq->i_data, ireq->i_len); @@ -1010,7 +1010,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, case IEEE80211_IOC_AMPDU_LIMIT: if (vap->iv_opmode == IEEE80211_M_HOSTAP) ireq->i_val = vap->iv_ampdu_rxmax; - else if (vap->iv_state == IEEE80211_S_RUN) + else if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) ireq->i_val = MS(vap->iv_bss->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); else @@ -1018,7 +1018,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, break; case IEEE80211_IOC_AMPDU_DENSITY: if (vap->iv_opmode == IEEE80211_M_STA && - vap->iv_state == IEEE80211_S_RUN) + (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) ireq->i_val = MS(vap->iv_bss->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); else @@ -1092,7 +1092,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, break; case IEEE80211_IOC_SMPS: if (vap->iv_opmode == IEEE80211_M_STA && - vap->iv_state == IEEE80211_S_RUN) { + (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) { if (vap->iv_bss->ni_flags & IEEE80211_NODE_MIMO_RTS) ireq->i_val = IEEE80211_HTCAP_SMPS_DYNAMIC; else if (vap->iv_bss->ni_flags & IEEE80211_NODE_MIMO_PS) @@ -1104,7 +1104,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, break; case IEEE80211_IOC_RIFS: if (vap->iv_opmode == IEEE80211_M_STA && - vap->iv_state == IEEE80211_S_RUN) + (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) ireq->i_val = (vap->iv_bss->ni_flags & IEEE80211_NODE_RIFS) != 0; else @@ -1858,7 +1858,7 @@ setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c) if (IEEE80211_IS_CHAN_NOADHOC(c)) return EINVAL; } - if (vap->iv_state == IEEE80211_S_RUN && + if ((vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) && vap->iv_bss->ni_chan == c) return 0; /* NB: nothing to do */ } diff --git a/sys/netproto/802_11/wlan/ieee80211_output.c b/sys/netproto/802_11/wlan/ieee80211_output.c index 5b02b205d9..82c268e4c6 100644 --- a/sys/netproto/802_11/wlan/ieee80211_output.c +++ b/sys/netproto/802_11/wlan/ieee80211_output.c @@ -135,23 +135,17 @@ ieee80211_start(struct ifnet *ifp, struct ifaltq_subque *ifsq) ifsq_purge(ifsq); return; } - if (vap->iv_state == IEEE80211_S_SLEEP) { - /* - * In power save, wakeup device for transmit. - */ - ieee80211_new_state(vap, IEEE80211_S_RUN, 0); - ifsq_purge(ifsq); - return; - } /* * No data frames go out unless we're running. * Note in particular this covers CAC and CSA * 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) { /* re-check under the com lock to avoid races */ - if (vap->iv_state != IEEE80211_S_RUN) { + if (vap->iv_state != IEEE80211_S_RUN && + vap->iv_state != IEEE80211_S_SLEEP) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "%s: ignore queue, in %s state\n", __func__, ieee80211_state_name[vap->iv_state]); @@ -270,6 +264,21 @@ ieee80211_start(struct ifnet *ifp, struct ifaltq_subque *ifsq) } } #endif + /* + * 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); + continue; + } + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && (m->m_flags & M_PWR_SAV) == 0) { /* diff --git a/sys/netproto/802_11/wlan/ieee80211_power.c b/sys/netproto/802_11/wlan/ieee80211_power.c index 2fdbf4d580..41024e40ad 100644 --- a/sys/netproto/802_11/wlan/ieee80211_power.c +++ b/sys/netproto/802_11/wlan/ieee80211_power.c @@ -70,6 +70,8 @@ ieee80211_power_vattach(struct ieee80211vap *vap) vap->iv_update_ps = ieee80211_update_ps; vap->iv_set_tim = ieee80211_set_tim; } + vap->iv_node_ps = ieee80211_node_pwrsave; + vap->iv_sta_ps = ieee80211_sta_pwrsave; } void @@ -539,3 +541,88 @@ ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable) 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! + */ + kprintf("STA_TIM_NOTIFY\n"); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, "%s: TIM=%d\n", __func__, set); + if (set == 1 && vap->iv_state == IEEE80211_S_SLEEP) { + ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); + } +} + +/* + * 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 */ + + /* For no, only do this in STA mode */ + if (! (vap->iv_caps & IEEE80211_C_SWSLEEP)) + goto out; + + if (vap->iv_opmode != IEEE80211_M_STA) + goto out; + + /* If we're not at run state, bail */ + if (vap->iv_state != IEEE80211_S_RUN) + goto out; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, + "%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 + 500, 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); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, + "%s: time delta=%d msec\n", __func__, + (int) ticks_to_msecs(ticks - ic->ic_lastdata)); + +out: + return; +} diff --git a/sys/netproto/802_11/wlan/ieee80211_proto.c b/sys/netproto/802_11/wlan/ieee80211_proto.c index 12443e7b16..a9b782e1a6 100644 --- a/sys/netproto/802_11/wlan/ieee80211_proto.c +++ b/sys/netproto/802_11/wlan/ieee80211_proto.c @@ -106,8 +106,6 @@ static void update_mcast_task(void *, int); static void update_promisc_task(void *, int); static void update_channel_task(void *, int); static void ieee80211_newstate_task(void *, int); -static int ieee80211_new_state_locked(struct ieee80211vap *, - enum ieee80211_state, int); static int null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, @@ -1708,7 +1706,7 @@ done: * is usually a mistake and indicates lack of proper integration * with the net80211 layer. */ -static int +int ieee80211_new_state_locked(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { diff --git a/sys/netproto/802_11/wlan/ieee80211_sta.c b/sys/netproto/802_11/wlan/ieee80211_sta.c index 8c25c968fa..d62d56a7ce 100644 --- a/sys/netproto/802_11/wlan/ieee80211_sta.c +++ b/sys/netproto/802_11/wlan/ieee80211_sta.c @@ -225,6 +225,7 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 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, @@ -290,12 +291,18 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) ieee80211_check_scan_current(vap); break; + case IEEE80211_S_SLEEP: /* beacon miss */ + /* + * XXX if in sleep we need to wakeup the hardware. + */ + /* FALLTHROUGH */ case IEEE80211_S_RUN: /* beacon miss */ /* * Beacon miss. Notify user space and if not * under control of a user application (roaming * manual) kick off a scan to re-connect. */ + ieee80211_sta_leave(ni); if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) ieee80211_check_scan_current(vap); @@ -394,7 +401,8 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); break; case IEEE80211_S_SLEEP: - ieee80211_sta_pwrsave(vap, 0); + /* Wake up from sleep */ + vap->iv_sta_ps(vap, 0); break; default: goto invalid; @@ -421,9 +429,11 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) ieee80211_node_authorize(ni); /* * Fake association when joining an existing bss. + * + * Don't do this if we're doing SLEEP->RUN. */ - if (ic->ic_newassoc != NULL) - ic->ic_newassoc(vap->iv_bss, ostate != IEEE80211_S_RUN); + if (ic->ic_newassoc != NULL && ostate != IEEE80211_S_SLEEP) + ic->ic_newassoc(vap->iv_bss, (ostate != IEEE80211_S_RUN)); break; case IEEE80211_S_CSA: if (ostate != IEEE80211_S_RUN) @@ -1341,23 +1351,40 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, scan.htcap, scan.htinfo); /* XXX state changes? */ } + 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? + */ 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) || - (min <= ix && ix <= max && - isset(tim->tim_bitmap - min, aid))) { - /* - * XXX Do not let bg scan kick off - * we are expecting data. - */ + + /* + * Only do this for unicast traffic in the TIM + * The multicast traffic notification for + * the scan notification stuff should occur + * differently. + */ + if (min <= ix && ix <= max && + isset(tim->tim_bitmap - min, aid)) { + ieee80211_sta_tim_notify(vap, 1); + ic->ic_lastdata = ticks; + } + + /* + * XXX TODO: do a separate notification + * for the multicast bit being set. + */ +#if 0 + if (tim->tim_bitctl & 1) { + ieee80211_sta_tim_notify(vap, 1); ic->ic_lastdata = ticks; - ieee80211_sta_pwrsave(vap, 0); } #endif ni->ni_dtim_count = tim->tim_count; @@ -1402,8 +1429,16 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, #endif ieee80211_bg_scan(vap, 0); } + + /* + * Put the station to sleep if we haven't seen + * traffic in a while. + */ + ieee80211_sta_ps_timer_check(vap); + return; } + /* * If scanning, just pass information to the scan module. */ -- 2.41.0