ath9k: serialize ath9k_ps_{wakeup,restore} calls
These functions are changing the power mode of the chip, but this may
have unpredictable effects, if another code are trying to set the power
mode via 'ath9k_hw_setpower' in the same time from another context.
Changes-licensed-under: ISC
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index df278fd..b9d1a13 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2777,23 +2777,39 @@
void ath9k_ps_wakeup(struct ath_softc *sc)
{
- if (atomic_inc_return(&sc->ps_usecount) == 1)
- if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) {
- sc->sc_ah->restore_mode = sc->sc_ah->power_mode;
- ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
- }
+ unsigned long flags;
+
+ spin_lock_irqsave(&sc->sc_pm_lock, flags);
+ if (++sc->ps_usecount != 1)
+ goto unlock;
+
+ if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) {
+ sc->sc_ah->restore_mode = sc->sc_ah->power_mode;
+ ath9k_hw_setpower_nolock(sc->sc_ah, ATH9K_PM_AWAKE);
+ }
+
+ unlock:
+ spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
}
void ath9k_ps_restore(struct ath_softc *sc)
{
- if (atomic_dec_and_test(&sc->ps_usecount))
- if ((sc->hw->conf.flags & IEEE80211_CONF_PS) &&
- !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
- SC_OP_WAIT_FOR_CAB |
- SC_OP_WAIT_FOR_PSPOLL_DATA |
- SC_OP_WAIT_FOR_TX_ACK)))
- ath9k_hw_setpower(sc->sc_ah,
- sc->sc_ah->restore_mode);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sc->sc_pm_lock, flags);
+ if (--sc->ps_usecount != 0)
+ goto unlock;
+
+ if ((sc->hw->conf.flags & IEEE80211_CONF_PS) &&
+ !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
+ SC_OP_WAIT_FOR_CAB |
+ SC_OP_WAIT_FOR_PSPOLL_DATA |
+ SC_OP_WAIT_FOR_TX_ACK)))
+ ath9k_hw_setpower_nolock(sc->sc_ah,
+ sc->sc_ah->restore_mode);
+
+ unlock:
+ spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
}
/*