diff options
author | Elias Oltmanns <eo@nebensachen.de> | 2008-11-12 11:28:39 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-11-20 14:54:42 -0800 |
commit | cfce2256fc569c37640eb2ea3204c9b1f02aef4f (patch) | |
tree | 98d5e5c373d3c598b7a594a622aebb2e15c0f9e3 /drivers | |
parent | c72a32e1819c184c16008ab772d414fd2107575b (diff) |
ath5k: fix suspend-related oops on rmmod
Cumulative patch backporting the following two commits from upstream:
commit 8bdd5b9c6bd53add260756b6673a0545fbdbba21 upstream
Author: Bob Copeland <me@bobcopeland.com>
Based on a patch by Elias Oltmanns, we call ath5k_init in resume even
if we didn't previously open the device. Besides starting up the
device unnecessarily, this also causes an oops on rmmod because
mac80211 will not invoke ath5k_stop and softirqs are left running after
the module has been unloaded. Add a new state bit, ATH_STAT_STARTED,
to indicate that we have been started up.
commit bc1b32d6bdd2d6f3fbee9a7c01c9b099f11c579c upstream
Author: Elias Oltmanns <eo@nebensachen.de>
After a s2ram / resume cycle, resetting the key cache does not work
unless it is deferred until after the hardware has been reinitialised by
a call to ath5k_hw_reset(). This fixes a regression introduced by
"ath5k: fix suspend-related oops on rmmod".
Reported-by: Toralf Förster <toralf.foerster@gmx.de>
Signed-off-by: Elias Oltmanns <eo@nebensachen.de>
Signed-off-by: Bob Copeland <me@bobcopeland.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath5k/base.c | 67 | ||||
-rw-r--r-- | drivers/net/wireless/ath5k/base.h | 3 |
2 files changed, 36 insertions, 34 deletions
diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 0676c6d8438..a2258273fd2 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -294,9 +294,9 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) } /* Interrupt handling */ -static int ath5k_init(struct ath5k_softc *sc); +static int ath5k_init(struct ath5k_softc *sc, bool is_resume); static int ath5k_stop_locked(struct ath5k_softc *sc); -static int ath5k_stop_hw(struct ath5k_softc *sc); +static int ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend); static irqreturn_t ath5k_intr(int irq, void *dev_id); static void ath5k_tasklet_reset(unsigned long data); @@ -584,7 +584,7 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state) ath5k_led_off(sc); - ath5k_stop_hw(sc); + ath5k_stop_hw(sc, true); free_irq(pdev->irq, sc); pci_save_state(pdev); @@ -599,8 +599,7 @@ ath5k_pci_resume(struct pci_dev *pdev) { struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath5k_softc *sc = hw->priv; - struct ath5k_hw *ah = sc->ah; - int i, err; + int err; pci_restore_state(pdev); @@ -621,21 +620,11 @@ ath5k_pci_resume(struct pci_dev *pdev) goto err_no_irq; } - err = ath5k_init(sc); + err = ath5k_init(sc, true); if (err) goto err_irq; ath5k_led_enable(sc); - /* - * Reset the key cache since some parts do not - * reset the contents on initial power up or resume. - * - * FIXME: This may need to be revisited when mac80211 becomes - * aware of suspend/resume. - */ - for (i = 0; i < AR5K_KEYTABLE_SIZE; i++) - ath5k_hw_reset_key(ah, i); - return 0; err_irq: free_irq(pdev->irq, sc); @@ -657,7 +646,6 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; u8 mac[ETH_ALEN]; - unsigned int i; int ret; ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device); @@ -676,13 +664,6 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) __set_bit(ATH_STAT_MRRETRY, sc->status); /* - * Reset the key cache since some parts do not - * reset the contents on initial power up. - */ - for (i = 0; i < AR5K_KEYTABLE_SIZE; i++) - ath5k_hw_reset_key(ah, i); - - /* * Collect the channel list. The 802.11 layer * is resposible for filtering this list based * on settings like the phy mode and regulatory @@ -2197,12 +2178,18 @@ ath5k_beacon_config(struct ath5k_softc *sc) \********************/ static int -ath5k_init(struct ath5k_softc *sc) +ath5k_init(struct ath5k_softc *sc, bool is_resume) { - int ret; + struct ath5k_hw *ah = sc->ah; + int ret, i; mutex_lock(&sc->lock); + if (is_resume && !test_bit(ATH_STAT_STARTED, sc->status)) + goto out_ok; + + __clear_bit(ATH_STAT_STARTED, sc->status); + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode); /* @@ -2220,7 +2207,7 @@ ath5k_init(struct ath5k_softc *sc) */ sc->curchan = sc->hw->conf.channel; sc->curband = &sc->sbands[sc->curchan->band]; - ret = ath5k_hw_reset(sc->ah, sc->opmode, sc->curchan, false); + ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, false); if (ret) { ATH5K_ERR(sc, "unable to reset hardware: %d\n", ret); goto done; @@ -2229,7 +2216,14 @@ ath5k_init(struct ath5k_softc *sc) * This is needed only to setup initial state * but it's best done after a reset. */ - ath5k_hw_set_txpower_limit(sc->ah, 0); + ath5k_hw_set_txpower_limit(ah, 0); + + /* + * Reset the key cache since some parts do not reset the + * contents on initial power up or resume from suspend. + */ + for (i = 0; i < AR5K_KEYTABLE_SIZE; i++) + ath5k_hw_reset_key(ah, i); /* * Setup the hardware after reset: the key cache @@ -2249,13 +2243,17 @@ ath5k_init(struct ath5k_softc *sc) AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB; - ath5k_hw_set_intr(sc->ah, sc->imask); + ath5k_hw_set_intr(ah, sc->imask); + + __set_bit(ATH_STAT_STARTED, sc->status); + /* Set ack to be sent at low bit-rates */ - ath5k_hw_set_ack_bitrate_high(sc->ah, false); + ath5k_hw_set_ack_bitrate_high(ah, false); mod_timer(&sc->calib_tim, round_jiffies(jiffies + msecs_to_jiffies(ath5k_calinterval * 1000))); +out_ok: ret = 0; done: mmiowb(); @@ -2310,7 +2308,7 @@ ath5k_stop_locked(struct ath5k_softc *sc) * stop is preempted). */ static int -ath5k_stop_hw(struct ath5k_softc *sc) +ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend) { int ret; @@ -2341,6 +2339,9 @@ ath5k_stop_hw(struct ath5k_softc *sc) } } ath5k_txbuf_free(sc, sc->bbuf); + if (!is_suspend) + __clear_bit(ATH_STAT_STARTED, sc->status); + mmiowb(); mutex_unlock(&sc->lock); @@ -2719,12 +2720,12 @@ err: static int ath5k_start(struct ieee80211_hw *hw) { - return ath5k_init(hw->priv); + return ath5k_init(hw->priv, false); } static void ath5k_stop(struct ieee80211_hw *hw) { - ath5k_stop_hw(hw->priv); + ath5k_stop_hw(hw->priv, false); } static int ath5k_add_interface(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ath5k/base.h b/drivers/net/wireless/ath5k/base.h index 7ec2f377d5c..214a565dd58 100644 --- a/drivers/net/wireless/ath5k/base.h +++ b/drivers/net/wireless/ath5k/base.h @@ -132,11 +132,12 @@ struct ath5k_softc { size_t desc_len; /* size of TX/RX descriptors */ u16 cachelsz; /* cache line size */ - DECLARE_BITMAP(status, 4); + DECLARE_BITMAP(status, 5); #define ATH_STAT_INVALID 0 /* disable hardware accesses */ #define ATH_STAT_MRRETRY 1 /* multi-rate retry support */ #define ATH_STAT_PROMISC 2 #define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */ +#define ATH_STAT_STARTED 4 /* opened & irqs enabled */ unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ unsigned int curmode; /* current phy mode */ |