Skip to content

Commit

Permalink
ath9k: serialize ath9k_ps_{wakeup,restore} calls
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Gabor Juhos authored and John W. Linville committed Jul 24, 2009
1 parent 0bc0798 commit 709ade9
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 14 deletions.
2 changes: 1 addition & 1 deletion drivers/net/wireless/ath/ath9k/ath9k.h
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ struct ath_softc {
u32 keymax;
DECLARE_BITMAP(keymap, ATH_KEYMAX);
u8 splitmic;
atomic_t ps_usecount;
unsigned long ps_usecount;
enum ath9k_int imask;
enum ath9k_ht_extprotspacing ht_extprotspacing;
enum ath9k_ht_macmode tx_chan_width;
Expand Down
42 changes: 29 additions & 13 deletions drivers/net/wireless/ath/ath9k/hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -2777,23 +2777,39 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)

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);
}

/*
Expand Down

0 comments on commit 709ade9

Please sign in to comment.