From d3ac945bb431ead3a88f07cdefada7a753dfdf45 Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Thu, 29 May 2008 00:10:48 +0000 Subject: [PATCH] Cleanup power handling and fix suspend/resume: o do not put the chip into full sleep in ath_stop as it gains nothing and causes many parts to hang in ath_detach because we may touch the chip during vap teardown; this may also fix issues with unloading the module o add a note in ath_detach to explain ath_hal_detach puts the chip in low power mode; this is useful to know as it means unloading the module will place a pci device in the lowest possible power state o leave an #ifdef notyet marker for powering down the chip when a device is marked down; we can't do that until we handle all the ways the driver may be entered and touch the chip o fix resume by reloading the h/w key cache as it's been clobbered (for pci) by the socket being powered off; for station mode we directly stop+init the chip and then simulate a beacon miss to get the upper layers sync'd up; for other configs we must brute force stop+start the vaps so they go through the state machine --- sys/dev/ath/if_ath.c | 85 +++++++++++++++++++++++++++++------------ sys/dev/ath/if_athvar.h | 3 +- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 8fdbb573067..4a53e219cb5 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -138,6 +138,8 @@ static void ath_fatal_proc(void *, int); static void ath_rxorn_proc(void *, int); static void ath_bmiss_vap(struct ieee80211vap *); static void ath_bmiss_proc(void *, int); +static int ath_keyset(struct ath_softc *, const struct ieee80211_key *, + struct ieee80211_node *); static int ath_key_alloc(struct ieee80211vap *, const struct ieee80211_key *, ieee80211_keyix *, ieee80211_keyix *); @@ -1056,29 +1058,69 @@ void ath_suspend(struct ath_softc *sc) { struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", __func__, ifp->if_flags); - ath_stop(ifp); + sc->sc_resume_up = (ifp->if_flags & IFF_UP) != 0; + if (ic->ic_opmode == IEEE80211_M_STA) + ath_stop(ifp); + else + ieee80211_suspend_all(ic); + /* + * NB: don't worry about putting the chip in low power + * mode; pci will power off our socket on suspend and + * cardbus detaches the device. + */ +} + +/* + * Reset the key cache since some parts do not reset the + * contents on resume. First we clear all entries, then + * re-load keys that the 802.11 layer assumes are setup + * in h/w. + */ +static void +ath_reset_keycache(struct ath_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ath_hal *ah = sc->sc_ah; + int i; + + for (i = 0; i < sc->sc_keymax; i++) + ath_hal_keyreset(ah, i); + ieee80211_crypto_reload_keys(ic); } void ath_resume(struct ath_softc *sc) { struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ath_hal *ah = sc->sc_ah; + HAL_STATUS status; DPRINTF(sc, ATH_DEBUG_ANY, "%s: if_flags %x\n", __func__, ifp->if_flags); - if (ifp->if_flags & IFF_UP) { - ath_init(sc); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ath_start(ifp); + /* + * Must reset the chip before we reload the + * keycache as we were powered down on suspend. + */ + ath_hal_reset(ah, sc->sc_opmode, &sc->sc_curchan, AH_FALSE, &status); + ath_reset_keycache(sc); + if (sc->sc_resume_up) { + if (ic->ic_opmode == IEEE80211_M_STA) { + ath_init(sc); + ieee80211_beacon_miss(ic); + } else + ieee80211_resume_all(ic); } if (sc->sc_softled) { - ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin); - ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon); + ath_hal_gpioCfgOutput(ah, sc->sc_ledpin); + ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon); } } @@ -1091,6 +1133,7 @@ ath_shutdown(struct ath_softc *sc) __func__, ifp->if_flags); ath_stop(ifp); + /* NB: no point powering down chip as we're about to reboot */ } /* @@ -1468,18 +1511,6 @@ ath_stop(struct ifnet *ifp) ATH_LOCK(sc); ath_stop_locked(ifp); - if (!sc->sc_invalid) { - /* - * Set the chip in full sleep mode. Note that we are - * careful to do this only when bringing the interface - * completely to a stop. When the chip is in this state - * it must be carefully woken up or references to - * registers in the PCI clock domain may freeze the bus - * (and system). This varies by chip and is mostly an - * issue with newer parts that go to sleep more quickly. - */ - ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP); - } ATH_UNLOCK(sc); } @@ -2155,7 +2186,6 @@ ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k, */ static int ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, - const u_int8_t mac0[IEEE80211_ADDR_LEN], struct ieee80211_node *bss) { #define N(a) (sizeof(a)/sizeof(a[0])) @@ -2199,7 +2229,7 @@ ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, gmac[0] |= 0x80; mac = gmac; } else - mac = mac0; + mac = k->wk_macaddr; if (hk.kv_type == HAL_CIPHER_TKIP && (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { @@ -2458,7 +2488,7 @@ ath_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, { struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; - return ath_keyset(sc, k, mac, vap->iv_bss); + return ath_keyset(sc, k, vap->iv_bss); } /* @@ -5780,8 +5810,9 @@ ath_setup_stationkey(struct ieee80211_node *ni) /* XXX locking? */ ni->ni_ucastkey.wk_keyix = keyix; ni->ni_ucastkey.wk_rxkeyix = rxkeyix; + IEEE80211_ADDR_COPY(ni->ni_ucastkey.wk_macaddr, ni->ni_macaddr); /* NB: this will create a pass-thru key entry */ - ath_keyset(sc, &ni->ni_ucastkey, ni->ni_macaddr, vap->iv_bss); + ath_keyset(sc, &ni->ni_ucastkey, vap->iv_bss); } } @@ -6315,8 +6346,14 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) */ if (!sc->sc_invalid) ath_init(sc); /* XXX lose error */ - } else + } 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_UNLOCK(sc); break; case SIOCGIFMEDIA: diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h index 4af480ca299..242ac415bf8 100644 --- a/sys/dev/ath/if_athvar.h +++ b/sys/dev/ath/if_athvar.h @@ -245,7 +245,8 @@ struct ath_softc { sc_beacons : 1,/* beacons running */ sc_swbmiss : 1,/* sta mode using sw bmiss */ sc_stagbeacons:1,/* use staggered beacons */ - sc_wmetkipmic:1;/* can do WME+TKIP MIC */ + sc_wmetkipmic:1,/* can do WME+TKIP MIC */ + sc_resume_up: 1;/* on resume, start all vaps */ uint32_t sc_eerd; /* regdomain from EEPROM */ uint32_t sc_eecc; /* country code from EEPROM */ /* rate tables */