PATCH_DIR:=./patches-old
else
PKG_VERSION:=2009-06-25
- PKG_RELEASE:=1
+ PKG_RELEASE:=2
PKG_SOURCE_URL:= \
http://www.orbit-lab.org/kernel/compat-wireless-2.6/2009/06 \
http://wireless.kernel.org/download/compat-wireless-2.6
--- /dev/null
+From: Johannes Berg <johannes@sipsolutions.net>
+Subject: mac80211: fix todo lock
+
+The key todo lock can be taken from different locks
+that require it to be _bh to avoid lock inversion
+due to (soft)irqs.
+
+This should fix the two problems reported by Bob and
+Gabor:
+http://mid.gmane.org/20090619113049.GB18956@hash.localnet
+http://mid.gmane.org/4A3FA376.8020307@openwrt.org
+
+Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
+Cc: Bob Copeland <me@bobcopeland.com>
+Cc: Gabor Juhos <juhosg@openwrt.org>
+---
+ net/mac80211/key.c | 28 +++++++++++++++-------------
+ 1 file changed, 15 insertions(+), 13 deletions(-)
+
+--- a/net/mac80211/key.c
++++ b/net/mac80211/key.c
+@@ -70,6 +70,8 @@ static DECLARE_WORK(todo_work, key_todo)
+ *
+ * @key: key to add to do item for
+ * @flag: todo flag(s)
++ *
++ * Must be called with IRQs or softirqs disabled.
+ */
+ static void add_todo(struct ieee80211_key *key, u32 flag)
+ {
+@@ -143,9 +145,9 @@ static void ieee80211_key_enable_hw_acce
+ ret = drv_set_key(key->local, SET_KEY, &sdata->vif, sta, &key->conf);
+
+ if (!ret) {
+- spin_lock(&todo_lock);
++ spin_lock_bh(&todo_lock);
+ key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
+- spin_unlock(&todo_lock);
++ spin_unlock_bh(&todo_lock);
+ }
+
+ if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
+@@ -167,12 +169,12 @@ static void ieee80211_key_disable_hw_acc
+ if (!key || !key->local->ops->set_key)
+ return;
+
+- spin_lock(&todo_lock);
++ spin_lock_bh(&todo_lock);
+ if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
+- spin_unlock(&todo_lock);
++ spin_unlock_bh(&todo_lock);
+ return;
+ }
+- spin_unlock(&todo_lock);
++ spin_unlock_bh(&todo_lock);
+
+ sta = get_sta_for_key(key);
+ sdata = key->sdata;
+@@ -191,9 +193,9 @@ static void ieee80211_key_disable_hw_acc
+ wiphy_name(key->local->hw.wiphy),
+ key->conf.keyidx, sta ? sta->addr : bcast_addr, ret);
+
+- spin_lock(&todo_lock);
++ spin_lock_bh(&todo_lock);
+ key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+- spin_unlock(&todo_lock);
++ spin_unlock_bh(&todo_lock);
+ }
+
+ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
+@@ -440,14 +442,14 @@ void ieee80211_key_link(struct ieee80211
+
+ __ieee80211_key_replace(sdata, sta, old_key, key);
+
+- spin_unlock_irqrestore(&sdata->local->key_lock, flags);
+-
+ /* free old key later */
+ add_todo(old_key, KEY_FLAG_TODO_DELETE);
+
+ add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS);
+ if (netif_running(sdata->dev))
+ add_todo(key, KEY_FLAG_TODO_HWACCEL_ADD);
++
++ spin_unlock_irqrestore(&sdata->local->key_lock, flags);
+ }
+
+ static void __ieee80211_key_free(struct ieee80211_key *key)
+@@ -550,7 +552,7 @@ static void __ieee80211_key_todo(void)
+ */
+ synchronize_rcu();
+
+- spin_lock(&todo_lock);
++ spin_lock_bh(&todo_lock);
+ while (!list_empty(&todo_list)) {
+ key = list_first_entry(&todo_list, struct ieee80211_key, todo);
+ list_del_init(&key->todo);
+@@ -561,7 +563,7 @@ static void __ieee80211_key_todo(void)
+ KEY_FLAG_TODO_HWACCEL_REMOVE |
+ KEY_FLAG_TODO_DELETE);
+ key->flags &= ~todoflags;
+- spin_unlock(&todo_lock);
++ spin_unlock_bh(&todo_lock);
+
+ work_done = false;
+
+@@ -594,9 +596,9 @@ static void __ieee80211_key_todo(void)
+
+ WARN_ON(!work_done);
+
+- spin_lock(&todo_lock);
++ spin_lock_bh(&todo_lock);
+ }
+- spin_unlock(&todo_lock);
++ spin_unlock_bh(&todo_lock);
+ }
+
+ void ieee80211_key_todo(void)
--- /dev/null
+From d2fa21debb4ea8c022b0fbed165eea821d19da9e Mon Sep 17 00:00:00 2001
+From: Gabor Juhos <juhosg@openwrt.org>
+Date: Sat, 20 Jun 2009 23:57:22 +0200
+Subject: [PATCH] ath9k: wake up the chip for TSF reset
+
+If we are in NETWORK SLEEP state, AR_SLP32_TSF_WRITE_STATUS limit
+always exceeds in 'ath9k_hw_reset_tsf', because reading of the
+AR_SLP3 register always return with the magic 0xdeadbeef value.
+
+Changes-licensed-under: ISC
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/net/wireless/ath/ath9k/hw.c | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -3803,6 +3803,7 @@ void ath9k_hw_reset_tsf(struct ath_hw *a
+ {
+ int count;
+
++ ath9k_ps_wakeup(ah->ah_sc);
+ count = 0;
+ while (REG_READ(ah, AR_SLP32_MODE) & AR_SLP32_TSF_WRITE_STATUS) {
+ count++;
+@@ -3814,6 +3815,7 @@ void ath9k_hw_reset_tsf(struct ath_hw *a
+ udelay(10);
+ }
+ REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE);
++ ath9k_ps_restore(ah->ah_sc);
+ }
+
+ bool ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting)
--- /dev/null
+From 9a0a0221024ddb4ddf0e33bb6fdbb3b02eaaf292 Mon Sep 17 00:00:00 2001
+From: Gabor Juhos <juhosg@openwrt.org>
+Date: Sat, 20 Jun 2009 23:57:23 +0200
+Subject: [PATCH] ath9k: make use ath9k_hw_wait int ath9k_hw_reset_tsf
+
+We have a dedicated function for this kind of checks, use that
+instead of duplicating the code.
+
+Changes-licensed-under: ISC
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/net/wireless/ath/ath9k/hw.c | 17 +++++------------
+ drivers/net/wireless/ath/ath9k/hw.h | 1 +
+ 2 files changed, 6 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -3801,19 +3801,12 @@ void ath9k_hw_settsf64(struct ath_hw *ah
+
+ void ath9k_hw_reset_tsf(struct ath_hw *ah)
+ {
+- int count;
+-
+ ath9k_ps_wakeup(ah->ah_sc);
+- count = 0;
+- while (REG_READ(ah, AR_SLP32_MODE) & AR_SLP32_TSF_WRITE_STATUS) {
+- count++;
+- if (count > 10) {
+- DPRINTF(ah->ah_sc, ATH_DBG_RESET,
+- "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n");
+- break;
+- }
+- udelay(10);
+- }
++ if (!ath9k_hw_wait(ah, AR_SLP32_MODE, AR_SLP32_TSF_WRITE_STATUS, 0,
++ AH_TSF_WRITE_TIMEOUT))
++ DPRINTF(ah->ah_sc, ATH_DBG_RESET,
++ "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n");
++
+ REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE);
+ ath9k_ps_restore(ah->ah_sc);
+ }
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -95,6 +95,7 @@
+
+ #define MAX_RATE_POWER 63
+ #define AH_WAIT_TIMEOUT 100000 /* (us) */
++#define AH_TSF_WRITE_TIMEOUT 100 /* (us) */
+ #define AH_TIME_QUANTUM 10
+ #define AR_KEYTABLE_SIZE 128
+ #define POWER_UP_TIME 200000
--- /dev/null
+From 841c7e339c4775f4cc614c92aaea82f70fcafbdb Mon Sep 17 00:00:00 2001
+From: Gabor Juhos <juhosg@openwrt.org>
+Date: Sun, 21 Jun 2009 16:59:53 +0200
+Subject: [PATCH 1/3] ath9k: serialize ath9k_hw_setpower calls
+
+Because ath9k_setpower is called from various contexts, we have to
+protect it against concurrent calls.
+
+Changes-licensed-under: ISC
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/net/wireless/ath/ath9k/ath9k.h | 1 +
+ drivers/net/wireless/ath/ath9k/hw.c | 15 ++++++++++++++-
+ drivers/net/wireless/ath/ath9k/main.c | 1 +
+ 3 files changed, 16 insertions(+), 1 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -544,6 +544,7 @@ struct ath_softc {
+ int irq;
+ spinlock_t sc_resetlock;
+ spinlock_t sc_serial_rw;
++ spinlock_t sc_pm_lock;
+ struct mutex mutex;
+
+ u8 curbssid[ETH_ALEN];
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -2738,7 +2738,8 @@ static bool ath9k_hw_set_power_awake(str
+ return true;
+ }
+
+-bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
++static bool ath9k_hw_setpower_nolock(struct ath_hw *ah,
++ enum ath9k_power_mode mode)
+ {
+ int status = true, setChip = true;
+ static const char *modes[] = {
+@@ -2772,6 +2773,18 @@ bool ath9k_hw_setpower(struct ath_hw *ah
+ return status;
+ }
+
++bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
++{
++ unsigned long flags;
++ bool ret;
++
++ spin_lock_irqsave(&ah->ah_sc->sc_pm_lock, flags);
++ ret = ath9k_hw_setpower_nolock(ah, mode);
++ spin_unlock_irqrestore(&ah->ah_sc->sc_pm_lock, flags);
++
++ return ret;
++}
++
+ /*
+ * Helper for ASPM support.
+ *
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -1317,6 +1317,7 @@ static int ath_init(u16 devid, struct at
+ spin_lock_init(&sc->wiphy_lock);
+ spin_lock_init(&sc->sc_resetlock);
+ spin_lock_init(&sc->sc_serial_rw);
++ spin_lock_init(&sc->sc_pm_lock);
+ mutex_init(&sc->mutex);
+ tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
+ tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet,
--- /dev/null
+From 900d70802f15e835b3dbbe8750313824aa30a118 Mon Sep 17 00:00:00 2001
+From: Gabor Juhos <juhosg@openwrt.org>
+Date: Sun, 21 Jun 2009 16:59:53 +0200
+Subject: [PATCH 2/3] ath9k: uninline ath9k_ps_{wakeup,restore} functions
+
+Uninline these functions before we add functional changes to them.
+
+Changes-licensed-under: ISC
+Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
+---
+ drivers/net/wireless/ath/ath9k/ath9k.h | 23 ++---------------------
+ drivers/net/wireless/ath/ath9k/hw.c | 21 +++++++++++++++++++++
+ 2 files changed, 23 insertions(+), 21 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -658,27 +658,8 @@ static inline int ath_ahb_init(void) { r
+ static inline void ath_ahb_exit(void) {};
+ #endif
+
+-static inline 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);
+- }
+-}
+-
+-static inline 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);
+-}
+-
++void ath9k_ps_wakeup(struct ath_softc *sc);
++void ath9k_ps_restore(struct ath_softc *sc);
+
+ void ath9k_set_bssid_mask(struct ieee80211_hw *hw);
+ int ath9k_wiphy_add(struct ath_softc *sc);
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -2785,6 +2785,27 @@ bool ath9k_hw_setpower(struct ath_hw *ah
+ return ret;
+ }
+
++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);
++ }
++}
++
++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);
++}
++
+ /*
+ * Helper for ASPM support.
+ *
--- /dev/null
+From 7446da6910f1368273a55ca99acba18828306a6e Mon Sep 17 00:00:00 2001
+From: Gabor Juhos <juhosg@openwrt.org>
+Date: Sun, 21 Jun 2009 16:59:53 +0200
+Subject: [PATCH 3/3] 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>
+---
+ drivers/net/wireless/ath/ath9k/ath9k.h | 2 +-
+ drivers/net/wireless/ath/ath9k/hw.c | 42 ++++++++++++++++++++++----------
+ 2 files changed, 30 insertions(+), 14 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -561,7 +561,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;
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -2787,23 +2787,39 @@ bool ath9k_hw_setpower(struct ath_hw *ah
+
+ 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);
+ }
+
+ /*