wlan/atheros - Synchronize sleep state code from FreeBSD
authorMatthew Dillon <dillon@apollo.backplane.com>
Sat, 3 May 2014 21:25:53 +0000 (14:25 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sat, 3 May 2014 21:32:19 +0000 (14:32 -0700)
* 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.

45 files changed:
sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c
sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c
sys/contrib/dev/ath/ath_hal/ar9300/ar9300_power.c
sys/dev/netif/ath/ath/if_ath.c
sys/dev/netif/ath/ath/if_ath_beacon.c
sys/dev/netif/ath/ath/if_ath_beacon.h
sys/dev/netif/ath/ath/if_ath_debug.h
sys/dev/netif/ath/ath/if_ath_keycache.c
sys/dev/netif/ath/ath/if_ath_led.c
sys/dev/netif/ath/ath/if_ath_misc.h
sys/dev/netif/ath/ath/if_ath_rx.c
sys/dev/netif/ath/ath/if_ath_rx_edma.c
sys/dev/netif/ath/ath/if_ath_sysctl.c
sys/dev/netif/ath/ath/if_ath_tx.c
sys/dev/netif/ath/ath/if_athvar.h
sys/dev/netif/ath/ath_hal/ah.c
sys/dev/netif/ath/ath_hal/ah.h
sys/dev/netif/ath/ath_hal/ah_internal.h
sys/dev/netif/ath/ath_hal/ah_osdep.c
sys/dev/netif/ath/ath_hal/ah_regdomain.c
sys/dev/netif/ath/ath_hal/ah_regdomain/ah_rd_regenum.h
sys/dev/netif/ath/ath_hal/ar5210/ar5210.h
sys/dev/netif/ath/ath_hal/ar5210/ar5210_attach.c
sys/dev/netif/ath/ath_hal/ar5210/ar5210_misc.c
sys/dev/netif/ath/ath_hal/ar5210/ar5210_power.c
sys/dev/netif/ath/ath_hal/ar5210/ar5210_reset.c
sys/dev/netif/ath/ath_hal/ar5211/ar5211.h
sys/dev/netif/ath/ath_hal/ar5211/ar5211_power.c
sys/dev/netif/ath/ath_hal/ar5212/ar5212.h
sys/dev/netif/ath/ath_hal/ar5212/ar5212_power.c
sys/dev/netif/ath/ath_hal/ar5312/ar5312_power.c
sys/dev/netif/ath/ath_hal/ar5416/ar5416_attach.c
sys/dev/netif/ath/ath_hal/ar5416/ar5416_beacon.c
sys/dev/netif/ath/ath_hal/ar5416/ar5416_interrupts.c
sys/dev/netif/ath/ath_hal/ar5416/ar5416_power.c
sys/dev/netif/ath/ath_hal/ar5416/ar5416reg.h
sys/netproto/802_11/ieee80211_power.h
sys/netproto/802_11/ieee80211_proto.h
sys/netproto/802_11/ieee80211_var.h
sys/netproto/802_11/wlan/ieee80211.c
sys/netproto/802_11/wlan/ieee80211_ioctl.c
sys/netproto/802_11/wlan/ieee80211_output.c
sys/netproto/802_11/wlan/ieee80211_power.c
sys/netproto/802_11/wlan/ieee80211_proto.c
sys/netproto/802_11/wlan/ieee80211_sta.c

index 335414e..46833ef 100644 (file)
@@ -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)) {
index 251957e..a2cc25d 100644 (file)
@@ -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;
index 44bebe4..0fe4d63 100644 (file)
@@ -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,
index 2e0d0e5..279f7ce 100644 (file)
@@ -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;
index 14b6e49..bd54409 100644 (file)
@@ -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
 }
index f3f73d7..9a17149 100644 (file)
@@ -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
index edd53f2..38d0d9c 100644 (file)
@@ -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
 };
index 0a654b5..f505258 100644 (file)
@@ -75,6 +75,7 @@
 
 #include <dev/netif/ath/ath/if_ath_debug.h>
 #include <dev/netif/ath/ath/if_ath_keycache.h>
+#include <dev/netif/ath/ath/if_ath_misc.h>
 
 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;
 }
 
index 376e201..14fe1d3 100644 (file)
 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
index cc3df2f..4632976 100644 (file)
@@ -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.
  */
index 84af07a..0812781 100644 (file)
@@ -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
index cdb9fdf..53a1514 100644 (file)
@@ -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
index 833caae..ddd46c8 100644 (file)
@@ -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;
 }
index e61cae7..bc225f1 100644 (file)
@@ -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 */
        /*
index 3d609ec..8125877 100644 (file)
@@ -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)
index 7187d57..bbfc09b 100644 (file)
@@ -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;
        }
index 2480803..4f2d3e9 100644 (file)
@@ -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);
index 331093b..460c138 100644 (file)
@@ -280,7 +280,8 @@ typedef struct {
                        halAntDivCombSupportOrg         : 1,
                        halRadioRetentionSupport        : 1,
                        halSpectralScanSupport          : 1,
-                       halRxUsingLnaMixing             : 1;
+                       halRxUsingLnaMixing             : 1,
+                       halRxDoMyBeacon                 : 1;
 
        uint32_t        halWirelessModes;
        uint16_t        halTotalQueues;
index 75e8967..e5b27dd 100644 (file)
@@ -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);
index e871cd7..c699448 100644 (file)
@@ -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;
index bc569cb..d8d211e 100644 (file)
@@ -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 */
index 48ccc20..3e372f7 100644 (file)
@@ -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))
 
index 3c0ed71..ee11ce9 100644 (file)
@@ -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;
index 8170880..5eaa18e 100644 (file)
@@ -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)<<AR_BSS_ID1_AID_S));
index 1c6909b..ec5e75d 100644 (file)
@@ -93,7 +93,6 @@ ar5210SetPowerModeSleep(struct ath_hal *ah, int setChip)
 HAL_BOOL
 ar5210SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip)
 {
-       struct ath_hal_5210 *ahp = AH5210(ah);
 #ifdef AH_DEBUG
        static const char* modes[] = {
                "AWAKE",
@@ -105,25 +104,30 @@ ar5210SetPowerMode(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 = 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
index 1d50f99..1dba729 100644 (file)
@@ -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);
index c50531e..6f04624 100644 (file)
@@ -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;
index 776cfb3..e646d90 100644 (file)
@@ -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
index 6d38d65..8086e71 100644 (file)
@@ -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 {
index 3f755bd..3068510 100644 (file)
@@ -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;
 }
 
index 94a0f1c..7db6d80 100644 (file)
@@ -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; 
 }
 
index 99bab06..bdc6111 100644 (file)
@@ -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;
 }
 
index e2bf6c7..6691c11 100644 (file)
@@ -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",
index 631ca2f..32ce2ed 100644 (file)
@@ -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 |
index 43a9241..dff9a85 100644 (file)
@@ -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;
 }
 
index 2602353..29b84b1 100644 (file)
 /* 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 */
index 11d1737..c23cf24 100644 (file)
@@ -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_ */
index 198dfd0..b61f6d4 100644 (file)
@@ -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);
index 05a871f..f16c97a 100644 (file)
@@ -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 */
index 5fea668..f7bed22 100644 (file)
@@ -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
index 8a5b22d..2cadd9b 100644 (file)
@@ -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 */
        }
index 5b02b20..82c268e 100644 (file)
@@ -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) {
                        /*
index 2fdbf4d..41024e4 100644 (file)
@@ -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;
+}
index 12443e7..a9b782e 100644 (file)
@@ -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)
 {
index 8c25c96..d62d56a 100644 (file)
@@ -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.
                 */