Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH] ath9k: disable radio when all devices are marked idle
From: Luis R. Rodriguez @ 2009-07-15  0:22 UTC (permalink / raw)
  To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez

This uses the new configuration changes indicated up by
mac80211 when all interfaces are marked idle. We need to do
a little more work as we have our own set of virtual
wiphys within ath9k.

Only when all virtual wiphys are inactive do we allow an idle
state change for a wiphy to trigger disabling the radio.

Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/ath/ath9k/ath9k.h   |    1 +
 drivers/net/wireless/ath/ath9k/main.c    |   24 ++++++++++++++++++++++++
 drivers/net/wireless/ath/ath9k/virtual.c |   17 +++++++++++++++++
 3 files changed, 42 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 2723385..efa185a 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -709,6 +709,7 @@ void ath9k_wiphy_pause_all_forced(struct ath_softc *sc,
 				  struct ath_wiphy *selected);
 bool ath9k_wiphy_scanning(struct ath_softc *sc);
 void ath9k_wiphy_work(struct work_struct *work);
+bool ath9k_all_wiphys_idle(struct ath_softc *sc);
 
 void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val);
 unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 84c0fed..2528571 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2281,9 +2281,28 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
 	struct ath_softc *sc = aphy->sc;
 	struct ieee80211_conf *conf = &hw->conf;
 	struct ath_hw *ah = sc->sc_ah;
+	bool all_wiphys_idle = false, disable_radio = false;
 
 	mutex_lock(&sc->mutex);
 
+	/* Leave this as the first check */
+	if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+
+		spin_lock_bh(&sc->wiphy_lock);
+		all_wiphys_idle =  ath9k_all_wiphys_idle(sc);
+		spin_unlock_bh(&sc->wiphy_lock);
+
+		if (conf->flags & IEEE80211_CONF_IDLE){
+			if (all_wiphys_idle)
+				disable_radio = true;
+		}
+		else if (all_wiphys_idle) {
+			ath_radio_enable(sc);
+			DPRINTF(sc, ATH_DBG_CONFIG,
+				"not-idle: enabling radio\n");
+		}
+	}
+
 	if (changed & IEEE80211_CONF_CHANGE_PS) {
 		if (conf->flags & IEEE80211_CONF_PS) {
 			if (!(ah->caps.hw_caps &
@@ -2351,6 +2370,11 @@ skip_chan_change:
 	if (changed & IEEE80211_CONF_CHANGE_POWER)
 		sc->config.txpowlimit = 2 * conf->power_level;
 
+	if (disable_radio) {
+		DPRINTF(sc, ATH_DBG_CONFIG, "idle: disabling radio\n");
+		ath_radio_disable(sc);
+	}
+
 	mutex_unlock(&sc->mutex);
 
 	return 0;
diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c
index 1ff429b..e1d419e 100644
--- a/drivers/net/wireless/ath/ath9k/virtual.c
+++ b/drivers/net/wireless/ath/ath9k/virtual.c
@@ -660,3 +660,20 @@ void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int)
 		queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work,
 				   sc->wiphy_scheduler_int);
 }
+
+/* caller must hold wiphy_lock */
+bool ath9k_all_wiphys_idle(struct ath_softc *sc)
+{
+	unsigned int i;
+	if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) {
+		return false;
+	}
+	for (i = 0; i < sc->num_sec_wiphy; i++) {
+		struct ath_wiphy *aphy = sc->sec_wiphy[i];
+		if (!aphy)
+			continue;
+		if (aphy->state != ATH_WIPHY_INACTIVE)
+			return false;
+	}
+	return true;
+}
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 4/5] mac80211: add WoW support
From: Luis R. Rodriguez @ 2009-07-15  0:21 UTC (permalink / raw)
  To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
In-Reply-To: <1247617320-20163-1-git-send-email-lrodriguez@atheros.com>

If we're associated and have not already gone into PS mode
we ensure send a nullfunc frame before going to suspend. Upon
resume we ensure we restore PS mode if it was enabled and inform
the driver accordingly, also notify the AP we're awake.

WoW requires the radio to be left on during suspend, to do this
we inform drivers of the requested wow triggers on the mac80211
stop callback.

Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/adm8211.c              |    2 +-
 drivers/net/wireless/at76c50x-usb.c         |    3 +-
 drivers/net/wireless/ath/ar9170/main.c      |    2 +-
 drivers/net/wireless/ath/ath5k/base.c       |    4 +-
 drivers/net/wireless/ath/ath9k/main.c       |    2 +-
 drivers/net/wireless/b43/main.c             |    2 +-
 drivers/net/wireless/b43legacy/main.c       |    2 +-
 drivers/net/wireless/iwlwifi/iwl-agn.c      |    2 +-
 drivers/net/wireless/iwlwifi/iwl3945-base.c |    2 +-
 drivers/net/wireless/libertas_tf/main.c     |    2 +-
 drivers/net/wireless/mac80211_hwsim.c       |    2 +-
 drivers/net/wireless/mwl8k.c                |    2 +-
 drivers/net/wireless/p54/main.c             |    2 +-
 drivers/net/wireless/rt2x00/rt2x00.h        |    2 +-
 drivers/net/wireless/rt2x00/rt2x00mac.c     |    2 +-
 drivers/net/wireless/rtl818x/rtl8180_dev.c  |    2 +-
 drivers/net/wireless/rtl818x/rtl8187_dev.c  |    2 +-
 drivers/net/wireless/wl12xx/wl1251_main.c   |    2 +-
 drivers/net/wireless/zd1211rw/zd_mac.c      |    2 +-
 include/net/mac80211.h                      |   17 +++++++++-----
 net/mac80211/cfg.c                          |    5 ++-
 net/mac80211/debugfs.c                      |    2 +-
 net/mac80211/driver-ops.h                   |    6 ++--
 net/mac80211/driver-trace.h                 |    6 +++-
 net/mac80211/ieee80211_i.h                  |   12 ++++++++-
 net/mac80211/iface.c                        |    6 ++--
 net/mac80211/pm.c                           |   31 +++++++++++++++++++++++++-
 net/mac80211/util.c                         |   14 ++++++++++++
 28 files changed, 99 insertions(+), 41 deletions(-)

diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index 5695911..776a715 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -1547,7 +1547,7 @@ fail:
 	return retval;
 }
 
-static void adm8211_stop(struct ieee80211_hw *dev)
+static void adm8211_stop(struct ieee80211_hw *dev, struct cfg80211_wow *wow)
 {
 	struct adm8211_priv *priv = dev->priv;
 
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index 13303fa..8357fef 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -1767,7 +1767,8 @@ error:
 	return 0;
 }
 
-static void at76_mac80211_stop(struct ieee80211_hw *hw)
+static void at76_mac80211_stop(struct ieee80211_hw *hw,
+			       struct cfg80211_wow *wow)
 {
 	struct at76_priv *priv = hw->priv;
 
diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
index 51753ed..2c2f002 100644
--- a/drivers/net/wireless/ath/ar9170/main.c
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -1132,7 +1132,7 @@ out:
 	return err;
 }
 
-static void ar9170_op_stop(struct ieee80211_hw *hw)
+static void ar9170_op_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct ar9170 *ar = hw->priv;
 	unsigned int i;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 20ba6fa..0bd53e8 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -223,7 +223,7 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
 static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
 static int ath5k_reset_wake(struct ath5k_softc *sc);
 static int ath5k_start(struct ieee80211_hw *hw);
-static void ath5k_stop(struct ieee80211_hw *hw);
+static void ath5k_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow);
 static int ath5k_add_interface(struct ieee80211_hw *hw,
 		struct ieee80211_if_init_conf *conf);
 static void ath5k_remove_interface(struct ieee80211_hw *hw,
@@ -2735,7 +2735,7 @@ static int ath5k_start(struct ieee80211_hw *hw)
 	return ath5k_init(hw->priv);
 }
 
-static void ath5k_stop(struct ieee80211_hw *hw)
+static void ath5k_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	ath5k_stop_hw(hw->priv);
 }
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 907434a..f40f2cf 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2087,7 +2087,7 @@ exit:
 	return 0;
 }
 
-static void ath9k_stop(struct ieee80211_hw *hw)
+static void ath9k_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct ath_wiphy *aphy = hw->priv;
 	struct ath_softc *sc = aphy->sc;
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index e71c8d9..f0ca9d0 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -4368,7 +4368,7 @@ static int b43_op_start(struct ieee80211_hw *hw)
 	return err;
 }
 
-static void b43_op_stop(struct ieee80211_hw *hw)
+static void b43_op_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct b43_wl *wl = hw_to_b43_wl(hw);
 	struct b43_wldev *dev = wl->current_dev;
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index c4973c1..9235578 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -3469,7 +3469,7 @@ out_mutex_unlock:
 	return err;
 }
 
-static void b43legacy_op_stop(struct ieee80211_hw *hw)
+static void b43legacy_op_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
 	struct b43legacy_wldev *dev = wl->current_dev;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 13279b1..fd2c146 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -2137,7 +2137,7 @@ out:
 	return 0;
 }
 
-static void iwl_mac_stop(struct ieee80211_hw *hw)
+static void iwl_mac_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct iwl_priv *priv = hw->priv;
 
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index a05a039..83cc25c 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -3126,7 +3126,7 @@ out_release_irq:
 	return ret;
 }
 
-static void iwl3945_mac_stop(struct ieee80211_hw *hw)
+static void iwl3945_mac_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct iwl_priv *priv = hw->priv;
 
diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c
index 4872345..8557ca4 100644
--- a/drivers/net/wireless/libertas_tf/main.c
+++ b/drivers/net/wireless/libertas_tf/main.c
@@ -291,7 +291,7 @@ err_prog_firmware:
 	return ret;
 }
 
-static void lbtf_op_stop(struct ieee80211_hw *hw)
+static void lbtf_op_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct lbtf_private *priv = hw->priv;
 	unsigned long flags;
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 2e2af65..516a31c 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -488,7 +488,7 @@ static int mac80211_hwsim_start(struct ieee80211_hw *hw)
 }
 
 
-static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
+static void mac80211_hwsim_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct mac80211_hwsim_data *data = hw->priv;
 	data->started = 0;
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index b9eded8..1261f8d 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -2917,7 +2917,7 @@ static int mwl8k_stop_wt(struct work_struct *wt)
 	return rc;
 }
 
-static void mwl8k_stop(struct ieee80211_hw *hw)
+static void mwl8k_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	int rc;
 	struct mwl8k_stop_worker *worker;
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
index c9a0545..43b5940 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/p54/main.c
@@ -190,7 +190,7 @@ out:
 	return err;
 }
 
-static void p54_stop(struct ieee80211_hw *dev)
+static void p54_stop(struct ieee80211_hw *dev, struct cfg80211_wow *wow)
 {
 	struct p54_common *priv = dev->priv;
 	int i;
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 71f37cb..e2701fa 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -953,7 +953,7 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
  */
 int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
 int rt2x00mac_start(struct ieee80211_hw *hw);
-void rt2x00mac_stop(struct ieee80211_hw *hw);
+void rt2x00mac_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow);
 int rt2x00mac_add_interface(struct ieee80211_hw *hw,
 			    struct ieee80211_if_init_conf *conf);
 void rt2x00mac_remove_interface(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index b7e0ddd..4522c96 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -179,7 +179,7 @@ int rt2x00mac_start(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_start);
 
-void rt2x00mac_stop(struct ieee80211_hw *hw)
+void rt2x00mac_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct rt2x00_dev *rt2x00dev = hw->priv;
 
diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c
index 09f46ab..807605c 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -628,7 +628,7 @@ static int rtl8180_start(struct ieee80211_hw *dev)
 	return ret;
 }
 
-static void rtl8180_stop(struct ieee80211_hw *dev)
+static void rtl8180_stop(struct ieee80211_hw *dev, struct cfg80211_wow *wow)
 {
 	struct rtl8180_priv *priv = dev->priv;
 	u8 reg;
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index c9b9dbe..fd1e8f8 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -989,7 +989,7 @@ static int rtl8187_start(struct ieee80211_hw *dev)
 	return 0;
 }
 
-static void rtl8187_stop(struct ieee80211_hw *dev)
+static void rtl8187_stop(struct ieee80211_hw *dev, struct cfg80211_wow *wow)
 {
 	struct rtl8187_priv *priv = dev->priv;
 	struct sk_buff *skb;
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c
index cf5e054..a87a25a 100644
--- a/drivers/net/wireless/wl12xx/wl1251_main.c
+++ b/drivers/net/wireless/wl12xx/wl1251_main.c
@@ -376,7 +376,7 @@ out:
 	return ret;
 }
 
-static void wl1251_op_stop(struct ieee80211_hw *hw)
+static void wl1251_op_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct wl1251 *wl = hw->priv;
 
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 9600b72..70dcf01 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -256,7 +256,7 @@ out:
 	return r;
 }
 
-static void zd_op_stop(struct ieee80211_hw *hw)
+static void zd_op_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct zd_mac *mac = zd_hw_mac(hw);
 	struct zd_chip *chip = &mac->chip;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7dd67a1..004e6b3 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -545,6 +545,7 @@ enum ieee80211_conf_flags {
  * @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed
  * @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
  * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
+ * @IEEE80211_CONF_CHANGE_WOW: Wake-on-Wireless LAN triggers have changed
  */
 enum ieee80211_conf_changed {
 	_IEEE80211_CONF_CHANGE_RADIO_ENABLED	= BIT(0),
@@ -555,6 +556,7 @@ enum ieee80211_conf_changed {
 	IEEE80211_CONF_CHANGE_CHANNEL		= BIT(6),
 	IEEE80211_CONF_CHANGE_RETRY_LIMITS	= BIT(7),
 	IEEE80211_CONF_CHANGE_IDLE		= BIT(8),
+	IEEE80211_CONF_CHANGE_WOW		= BIT(9),
 };
 
 static inline __deprecated enum ieee80211_conf_changed
@@ -1294,11 +1296,14 @@ enum ieee80211_ampdu_mlme_action {
  *	Must be implemented.
  *
  * @stop: Called after last netdevice attached to the hardware
- *	is disabled. This should turn off the hardware (at least
- *	it must turn off frame reception.)
- *	May be called right after add_interface if that rejects
- *	an interface.
- *	Must be implemented.
+ *	is disabled or during suspend. This should turn off the
+ *	hardware (at least it must turn off frame reception) unless
+ *	the device wants to enable Wake-on-Wireless-LAN. In order to
+ *	process WoW triggers the radio must be left on and the driver
+ *	must enable the triggers in hardware. This callback may be
+ *	called right after add_interface if that rejects an interface.
+ *	Must be implemented. WoW triggers which should be enabled prior
+ *	suspend are passed in the callback.
  *
  * @add_interface: Called when a netdevice attached to the hardware is
  *	enabled. Because it is not called for monitor mode devices, @start
@@ -1425,7 +1430,7 @@ enum ieee80211_ampdu_mlme_action {
 struct ieee80211_ops {
 	int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
 	int (*start)(struct ieee80211_hw *hw);
-	void (*stop)(struct ieee80211_hw *hw);
+	void (*stop)(struct ieee80211_hw *hw, struct cfg80211_wow *wow);
 	int (*add_interface)(struct ieee80211_hw *hw,
 			     struct ieee80211_if_init_conf *conf);
 	void (*remove_interface)(struct ieee80211_hw *hw,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 52928ad..afa6476 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1138,15 +1138,16 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
 }
 
 #ifdef CONFIG_PM
-static int ieee80211_suspend(struct wiphy *wiphy)
+static int ieee80211_suspend(struct wiphy *wiphy, struct cfg80211_wow *wow)
 {
-	return __ieee80211_suspend(wiphy_priv(wiphy));
+	return __ieee80211_suspend(wiphy_priv(wiphy), wow);
 }
 
 static int ieee80211_resume(struct wiphy *wiphy)
 {
 	return __ieee80211_resume(wiphy_priv(wiphy));
 }
+
 #else
 #define ieee80211_suspend NULL
 #define ieee80211_resume NULL
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 96991b6..8985c40 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -115,7 +115,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf,
 	struct ieee80211_local *local = file->private_data;
 
 	rtnl_lock();
-	__ieee80211_suspend(&local->hw);
+	__ieee80211_suspend(&local->hw, NULL);
 	__ieee80211_resume(&local->hw);
 	rtnl_unlock();
 
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 4100c36..f12488f 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -17,10 +17,10 @@ static inline int drv_start(struct ieee80211_local *local)
 	return ret;
 }
 
-static inline void drv_stop(struct ieee80211_local *local)
+static inline void drv_stop(struct ieee80211_local *local, struct cfg80211_wow *wow)
 {
-	local->ops->stop(&local->hw);
-	trace_drv_stop(local);
+	local->ops->stop(&local->hw, wow);
+	trace_drv_stop(local, wow);
 }
 
 static inline int drv_add_interface(struct ieee80211_local *local,
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 48c93d1..f83f913 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -51,16 +51,18 @@ TRACE_EVENT(drv_start,
 );
 
 TRACE_EVENT(drv_stop,
-	TP_PROTO(struct ieee80211_local *local),
+	TP_PROTO(struct ieee80211_local *local, struct cfg80211_wow *wow),
 
-	TP_ARGS(local),
+	TP_ARGS(local, wow),
 
 	TP_STRUCT__entry(
 		LOCAL_ENTRY
+		__field(u32, triggers_enabled)
 	),
 
 	TP_fast_assign(
 		LOCAL_ASSIGN;
+		__entry->triggers_enabled = wow->triggers_enabled;
 	),
 
 	TP_printk(
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 6a01771..2ebf56d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -612,6 +612,14 @@ struct ieee80211_local {
 	 */
 	bool quiescing;
 
+	/*
+	 * This will be true if we had enabled PS before going to suspend.
+	 * We use this to help resume restore the PS state to what it was.
+	 * Note that this only makes sense if we're associated and are a
+	 * station.
+	 */
+	bool ps_before_suspend;
+
 	int tx_headroom; /* required headroom for hardware/radiotap */
 
 	/* Tasklet and skb queue to process calls from IRQ mode. All frames
@@ -1035,14 +1043,14 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
 int ieee80211_reconfig(struct ieee80211_local *local);
 
 #ifdef CONFIG_PM
-int __ieee80211_suspend(struct ieee80211_hw *hw);
+int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wow *wow);
 
 static inline int __ieee80211_resume(struct ieee80211_hw *hw)
 {
 	return ieee80211_reconfig(hw_to_local(hw));
 }
 #else
-static inline int __ieee80211_suspend(struct ieee80211_hw *hw)
+static inline int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	return 0;
 }
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index cadb0f6..b5e1c76 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -195,7 +195,7 @@ static int ieee80211_open(struct net_device *dev)
 	 */
 	if (!is_valid_ether_addr(dev->dev_addr)) {
 		if (!local->open_count)
-			drv_stop(local);
+			drv_stop(local, NULL);
 		return -EADDRNOTAVAIL;
 	}
 
@@ -321,7 +321,7 @@ static int ieee80211_open(struct net_device *dev)
 	drv_remove_interface(local, &conf);
  err_stop:
 	if (!local->open_count)
-		drv_stop(local);
+		drv_stop(local, NULL);
  err_del_bss:
 	sdata->bss = NULL;
 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -532,7 +532,7 @@ static int ieee80211_stop(struct net_device *dev)
 	ieee80211_recalc_ps(local, -1);
 
 	if (local->open_count == 0) {
-		drv_stop(local);
+		drv_stop(local, NULL);
 
 		ieee80211_led_radio(local, false);
 
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 7a549f9..d1918ad 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -6,7 +6,7 @@
 #include "driver-ops.h"
 #include "led.h"
 
-int __ieee80211_suspend(struct ieee80211_hw *hw)
+int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_sub_if_data *sdata;
@@ -16,6 +16,33 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
 
 	ieee80211_scan_cancel(local);
 
+	local->ps_before_suspend = !!(local->hw.conf.flags & IEEE80211_CONF_PS);
+
+	if (wow->triggers_enabled) {
+		if (!local->ps_before_suspend) {
+			local->hw.conf.flags |= IEEE80211_CONF_PS;
+			ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+		}
+
+		del_timer_sync(&local->dynamic_ps_timer);
+		cancel_work_sync(&local->dynamic_ps_enable_work);
+
+		list_for_each_entry(sdata, &local->interfaces, list) {
+			struct ieee80211_if_managed *ifmgd;
+
+			if (sdata->vif.type != NL80211_IFTYPE_STATION)
+				continue;
+
+			ifmgd = &sdata->u.mgd;
+			if (!ifmgd->associated)
+				continue;
+
+			if (!local->ps_before_suspend)
+				ieee80211_send_nullfunc(local, sdata, 1);
+		}
+
+	}
+
 	ieee80211_stop_queues_by_reason(hw,
 			IEEE80211_QUEUE_STOP_REASON_SUSPEND);
 
@@ -61,7 +88,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
 	/* stop hardware - this must stop RX */
 	if (local->open_count) {
 		ieee80211_led_radio(local, false);
-		drv_stop(local);
+		drv_stop(local, wow);
 	}
 
 	/* remove STAs */
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 7fc5584..c6985ce 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -970,6 +970,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 	 */
 	local->suspended = false;
 
+	if (from_suspend && local->ps_before_suspend)
+		local->hw.conf.flags |= IEEE80211_CONF_PS;
+
 	/* restart hardware */
 	if (local->open_count) {
 		res = drv_start(local);
@@ -1072,6 +1075,17 @@ int ieee80211_reconfig(struct ieee80211_local *local)
 		return 0;
 
 #ifdef CONFIG_PM
+	if (!local->ps_before_suspend) {
+		list_for_each_entry(sdata, &local->interfaces, list) {
+			struct ieee80211_if_managed *ifmgd;
+			if (sdata->vif.type != NL80211_IFTYPE_STATION)
+				continue;
+			ifmgd = &sdata->u.mgd;
+			if (ifmgd->associated)
+				ieee80211_send_nullfunc(local, sdata, 0);
+		}
+	}
+
 	local->suspended = false;
 
 	list_for_each_entry(sdata, &local->interfaces, list) {
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 5/5] ath9k: Add Wake-on-Wireless-LAN support
From: Luis R. Rodriguez @ 2009-07-15  0:22 UTC (permalink / raw)
  To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
In-Reply-To: <1247617320-20163-1-git-send-email-lrodriguez@atheros.com>

This adds WoW suppport for ath9k. Note that not all cards
have WoW support available; you also need BIOS support to
trigger a wake from the device's PCI PME. We enable only
Magic Packet triggers for now, note that if you disconnect
you won't get a wake up. Beacon miss and link change triggers
need more testing before being enabled.

Tested on AR9280 with Magic Packet.

Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/ath/ath9k/Makefile   |    1 +
 drivers/net/wireless/ath/ath9k/ath9k.h    |   18 +
 drivers/net/wireless/ath/ath9k/debug.c    |    1 -
 drivers/net/wireless/ath/ath9k/hw.c       |   22 ++
 drivers/net/wireless/ath/ath9k/hw.h       |   21 ++
 drivers/net/wireless/ath/ath9k/initvals.h |   31 ++
 drivers/net/wireless/ath/ath9k/main.c     |   23 ++
 drivers/net/wireless/ath/ath9k/pci.c      |  102 ++++++
 drivers/net/wireless/ath/ath9k/reg.h      |  154 +++++++++
 drivers/net/wireless/ath/ath9k/wow.c      |  511 +++++++++++++++++++++++++++++
 10 files changed, 883 insertions(+), 1 deletions(-)
 create mode 100644 drivers/net/wireless/ath/ath9k/wow.c

diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 783bc39..d4a598d 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -14,5 +14,6 @@ ath9k-y +=	hw.o \
 ath9k-$(CONFIG_PCI) += pci.o
 ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o
 ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o
+ath9k-$(CONFIG_PM) += wow.o
 
 obj-$(CONFIG_ATH9K) += ath9k.o
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 1576812..2723385 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -604,6 +604,12 @@ struct ath_softc {
 
 	struct ath_ani ani;
 	struct ath9k_node_stats nodestats;
+#ifdef CONFIG_PM
+	bool wow_got_bmiss_intr;
+	bool wow_sleep_proc_intr;
+	u32 wow_intr_before_sleep;
+	u32 wow_triggers_enabled;
+#endif
 #ifdef CONFIG_ATH9K_DEBUG
 	struct ath9k_debug debug;
 #endif
@@ -612,6 +618,18 @@ struct ath_softc {
 	struct delayed_work tx_complete_work;
 };
 
+#ifdef CONFIG_PM
+/*
+ * WoW trigger types
+ */
+#define AH_WOW_USER_PATTERN_EN		0x1
+#define AH_WOW_MAGIC_PATTERN_EN		0x2
+#define AH_WOW_LINK_CHANGE		0x4
+#define AH_WOW_BEACON_MISS		0x8
+#define AH_WOW_MAX_EVENTS		4
+
+#endif
+
 struct ath_wiphy {
 	struct ath_softc *sc; /* shared for all virtual wiphys */
 	struct ieee80211_hw *hw;
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 9f99f00..8705c35 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -486,7 +486,6 @@ static const struct file_operations fops_wiphy = {
 	.owner = THIS_MODULE
 };
 
-
 int ath9k_init_debug(struct ath_softc *sc)
 {
 	sc->debug.debug_mask = ath9k_debug;
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index b9d1a13..3d98f8d 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -718,6 +718,12 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc,
 			ARRAY_SIZE(ar9285PciePhy_clkreq_always_on_L1_9285_1_2),
 				  2);
 		}
+#ifdef CONFIG_PM
+		/* SerDes values during WOW sleep */
+		INIT_INI_ARRAY(&ah->iniPcieSerdesWow,
+			       ar9285PciePhy_AWOW_9285_1_2,
+			       ARRAY_SIZE(ar9285PciePhy_AWOW_9285_1_2), 2);
+#endif
 	} else if (AR_SREV_9285_10_OR_LATER(ah)) {
 		INIT_INI_ARRAY(&ah->iniModes, ar9285Modes_9285,
 			       ARRAY_SIZE(ar9285Modes_9285), 6);
@@ -748,6 +754,12 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc,
 			       ar9280PciePhy_clkreq_always_on_L1_9280,
 			       ARRAY_SIZE(ar9280PciePhy_clkreq_always_on_L1_9280), 2);
 		}
+#ifdef CONFIG_PM
+		/* SerDes values during WOW sleep */
+		INIT_INI_ARRAY(&ah->iniPcieSerdesWow, ar9280PciePhy_AWOW_9280,
+			       ARRAY_SIZE(ar9280PciePhy_AWOW_9280), 2);
+#endif
+
 		INIT_INI_ARRAY(&ah->iniModesAdditional,
 			       ar9280Modes_fast_clock_9280_2,
 			       ARRAY_SIZE(ar9280Modes_fast_clock_9280_2), 3);
@@ -893,6 +905,16 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc,
 		goto bad;
 	}
 
+#ifdef CONFIG_PM
+	/* WOW */
+	ath9k_wow_set_gpio_reset_low(ah);
+	/* Clear the Wow Status */
+	REG_WRITE(ah, AR_PCIE_PM_CTRL, REG_READ(ah, AR_PCIE_PM_CTRL) |
+		  AR_PMCTRL_WOW_PME_CLR);
+	REG_WRITE(ah, AR_WOW_PATTERN_REG,
+		  AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN_REG)));
+#endif
+
 	if (AR_SREV_9285(ah))
 		ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S);
 	else
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 9a4570d..6126b99 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -520,6 +520,11 @@ struct ath_hw {
 	int initPDADC;
 	int PDADCdelta;
 
+#ifdef CONFIG_PM
+	/* WoW mask -- used to indicate which WoW we have enabled */
+	u32 ah_wowEventMask;
+#endif
+
 	struct ar5416IniArray iniModes;
 	struct ar5416IniArray iniCommon;
 	struct ar5416IniArray iniBank0;
@@ -535,6 +540,10 @@ struct ath_hw {
 	struct ar5416IniArray iniModesAdditional;
 	struct ar5416IniArray iniModesRxGain;
 	struct ar5416IniArray iniModesTxGain;
+#ifdef CONFIG_PM
+	/* SerDes values during WOW sleep */
+	struct ar5416IniArray iniPcieSerdesWow;
+#endif
 };
 
 /* Attach, Detach, Reset */
@@ -613,4 +622,16 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints);
 
 void ath9k_hw_btcoex_enable(struct ath_hw *ah);
 
+#ifdef CONFIG_PM
+
+/* WOW - Wake on Wireless */
+void ath9k_wow_set_gpio_reset_low(struct ath_hw *ah);
+/* Called when going to suspend/hibernate */
+void ath9k_hw_wow_enable(struct ath_hw *ah, u32 patternEnable);
+/* Called when coming back up from suspend/hibernation */
+u32 ath9k_hw_wow_wake_up(struct ath_hw *ah);
+const char *ath9k_hw_wow_event_to_string(u32 wow_event);
+
+#endif /* CONFIG_PM */
+
 #endif
diff --git a/drivers/net/wireless/ath/ath9k/initvals.h b/drivers/net/wireless/ath/ath9k/initvals.h
index f67a2a9..52e1743 100644
--- a/drivers/net/wireless/ath/ath9k/initvals.h
+++ b/drivers/net/wireless/ath/ath9k/initvals.h
@@ -3439,6 +3439,22 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = {
     {0x00004044,  0x00000000 },
 };
 
+#ifdef CONFIG_PM
+/* Auto generated PCI-E PHY config for AR9280 with WOW */
+static const u_int32_t ar9280PciePhy_AWOW_9280[][2] = {
+    {0x00004040,  0x9248fd00 },
+    {0x00004040,  0x24924924 },
+    {0x00004040,  0xa8000019 },
+    {0x00004040,  0x13160820 },
+    {0x00004040,  0xe5980560 },
+    {0x00004040,  0xc01ddffd },
+    {0x00004040,  0x1aaabe41 },
+    {0x00004040,  0xbe105554 },
+    {0x00004040,  0x00043007 },
+    {0x00004044,  0x00000000 },
+};
+#endif
+
 /* AR9285 Revsion 10*/
 static const u_int32_t ar9285Modes_9285[][6] = {
     { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
@@ -4849,3 +4865,18 @@ static const u_int32_t ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = {
     {0x00004040,  0x00043007 },
     {0x00004044,  0x00000000 },
 };
+
+#ifdef CONFIG_PM
+static const u_int32_t ar9285PciePhy_AWOW_9285_1_2[][2] = {
+    {0x00004040,  0x9248fd00 },
+    {0x00004040,  0x24924924 },
+    {0x00004040,  0xa8000019 },
+    {0x00004040,  0x13160820 },
+    {0x00004040,  0xe5980560 },
+    {0x00004040,  0xc01ddffd },
+    {0x00004040,  0x1aaabe41 },
+    {0x00004040,  0xbe105554 },
+    {0x00004040,  0x00043007 },
+    {0x00004044,  0x00000000 },
+};
+#endif /* CONFIG_PM */
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index f40f2cf..84c0fed 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -596,6 +596,20 @@ irqreturn_t ath_isr(int irq, void *dev)
 		ath9k_hw_set_interrupts(ah, sc->imask);
 	}
 
+	if (status & ATH9K_INT_BMISS) {
+#ifdef CONFIG_PM
+		if (sc->wow_sleep_proc_intr) {
+			DPRINTF(sc, (ATH_DBG_ANY | ATH_DBG_INTERRUPT),
+				"during WoW we got a BMISS\n");
+			sc->wow_got_bmiss_intr = true;
+			sc->wow_sleep_proc_intr = false;
+		}
+#else
+		/* BMISS not enabled on ath9k unless WoW is enabled */
+		DPRINTF(sc, ATH_DBG_INTERRUPT,
+			"spurious unattended beacon miss interrupt\n");
+#endif
+	}
 	if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
 		if (status & ATH9K_INT_TIM_TIMER) {
 			/* Clear RxAbort bit so that we can
@@ -2094,6 +2108,15 @@ static void ath9k_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
 
 	aphy->state = ATH_WIPHY_INACTIVE;
 
+#ifdef CONFIG_PM
+	if (wow && wow->triggers_enabled) {
+		sc->wow_triggers_enabled = wow->triggers_enabled;
+		DPRINTF(sc, ATH_DBG_ANY, "Leaving radio on during "
+			"suspend for WoW\n");
+		return;
+	}
+#endif
+
 	if (sc->sc_flags & SC_OP_INVALID) {
 		DPRINTF(sc, ATH_DBG_ANY, "Device not present\n");
 		return;
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 170c5b3..4d5865c 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -142,6 +142,9 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	if ((val & 0x0000ff00) != 0)
 		pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
 
+	device_init_wakeup(&pdev->dev, 1);
+	device_set_wakeup_enable(&pdev->dev, 0);
+
 	ret = pci_request_region(pdev, 0, "ath9k");
 	if (ret) {
 		dev_err(&pdev->dev, "PCI memory region reserve error\n");
@@ -192,6 +195,14 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 	sc->irq = pdev->irq;
 
+	/* WoW is for PCI for ath9k */
+
+	/*
+	 * Bmiss and link change triggers not yet fully tested, add them
+	 * after properly testing and confirming behaviour.
+	 */
+	hw->wiphy->wow_triggers_supported = NL80211_WOW_TRIGGER_MAGIC_PACKET;
+
 	ah = sc->sc_ah;
 	printk(KERN_INFO
 	       "%s: Atheros AR%s MAC/BB Rev:%x "
@@ -228,21 +239,109 @@ static void ath_pci_remove(struct pci_dev *pdev)
 
 #ifdef CONFIG_PM
 
+static u32 ath9k_wow_event_map(u32 cfg_wow_events)
+{
+	u32 wow_events = 0;
+
+	if (cfg_wow_events & NL80211_WOW_TRIGGER_MAGIC_PACKET)
+		wow_events |= AH_WOW_MAGIC_PATTERN_EN;
+	if (cfg_wow_events & NL80211_WOW_TRIGGER_BMISS)
+		wow_events |= AH_WOW_BEACON_MISS;
+	if (cfg_wow_events & NL80211_WOW_TRIGGER_LINK_CHANGE)
+		wow_events |= AH_WOW_LINK_CHANGE;
+
+	return wow_events;
+}
+
+static void ath9k_pci_wow_enable(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->sc_ah;
+	u32 wake_up_events;
+
+	wake_up_events = ath9k_wow_event_map(sc->wow_triggers_enabled);
+
+	/* eventually we'll add this...
+	 * if (wake_up_events & AH_WOW_USER_PATTERN_EN)
+	 *	ath9k_wow_create_pattern(sc);
+	 */
+
+	/*
+	 * To avoid false wake, we enable beacon miss interrupt only when
+	 * we go to sleep. We save the current interrupt mask so that
+	 * we can restore it after the system wakes up.
+	 */
+	sc->wow_intr_before_sleep = ah->mask_reg;
+	ath9k_hw_set_interrupts(ah, ATH9K_INT_BMISS | ATH9K_INT_GLOBAL);
+	sc->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
+
+	ath9k_hw_wow_enable(ah, wake_up_events);
+	device_set_wakeup_enable(sc->dev, 1);
+
+	DPRINTF(sc, ATH_DBG_ANY,
+		"WoW enabled\n");
+
+	sc->wow_sleep_proc_intr = true;
+}
+
 static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 {
 	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
 	struct ath_wiphy *aphy = hw->priv;
 	struct ath_softc *sc = aphy->sc;
 
+	if (!!sc->wow_triggers_enabled && device_can_wakeup(&pdev->dev))
+		ath9k_pci_wow_enable(sc);
+
 	ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
 
 	pci_save_state(pdev);
 	pci_disable_device(pdev);
+
+	if (!!sc->wow_triggers_enabled && device_can_wakeup(&pdev->dev)) {
+		pci_prepare_to_sleep(pdev);
+		return 0;
+	}
+
+	pci_wake_from_d3(pdev, !!sc->wow_triggers_enabled);
 	pci_set_power_state(pdev, PCI_D3hot);
 
 	return 0;
 }
 
+static void ath9k_pci_wow_wake(struct ath_softc *sc)
+{
+	struct ath_hw *ah = sc->sc_ah;
+	u32 wow_status;
+
+	ath9k_hw_set_interrupts(ah, sc->wow_intr_before_sleep);
+	sc->imask = sc->wow_intr_before_sleep;
+
+	wow_status = ath9k_hw_wow_wake_up(sc->sc_ah);
+
+	if (sc->wow_got_bmiss_intr) {
+		/*
+		 * Some devices may not pick beacon miss
+		 * as the reason they woke up so we add that
+		 * here for that shortcoming
+		 */
+		wow_status |= AH_WOW_BEACON_MISS;
+		sc->wow_got_bmiss_intr = false;
+		DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+			"Beacon miss interrupt picked up during sleep, "
+			"adding as possible wake up reason\n");
+	}
+
+	if (wow_status) {
+		DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+			"Waking up due to WoW trigger%s\n",
+			ath9k_hw_wow_event_to_string(wow_status));
+		DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+			"WoW status: %d\n WoW reason: %s\n",
+			wow_status,
+			ath9k_hw_wow_event_to_string((wow_status & 0x0FFF)));
+	}
+}
+
 static int ath_pci_resume(struct pci_dev *pdev)
 {
 	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
@@ -264,6 +363,9 @@ static int ath_pci_resume(struct pci_dev *pdev)
 	if ((val & 0x0000ff00) != 0)
 		pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
 
+	if (sc->wow_triggers_enabled)
+		ath9k_pci_wow_wake(sc);
+
 	/* Enable LED */
 	ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
 			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index 5260524..387bbe7 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -665,6 +665,11 @@
 #define AR_RC_HOSTIF         0x00000100
 
 #define AR_WA                		0x4004
+#define AR_WA_UNTIE_RESET_EN	(1 << 15)   /* Enable PCI Reset to POR (power-on-reset) */
+#define AR_WA_RESET_EN		(1 << 18)   /* Sw Control to enable PCI-Reset to POR (bit 15) */
+#define AR_WA_ANALOG_SHIFT	(1 << 20)
+#define AR_WA_POR_SHORT		(1 << 21)   /* PCI-E Phy reset control */
+
 #define AR9285_WA_DEFAULT 		0x004a05cb
 #define AR9280_WA_DEFAULT           	0x0040073f
 #define AR_WA_DEFAULT               	0x0000073f
@@ -1508,4 +1513,153 @@ enum {
 #define AR_KEYTABLE_MAC0(_n)    (AR_KEYTABLE(_n) + 24)
 #define AR_KEYTABLE_MAC1(_n)    (AR_KEYTABLE(_n) + 28)
 
+/* WoW - Wake On Wireless */
+
+#define AR_PMCTRL_AUX_PWR_DET		0x10000000  /* Puts Chip in L2 state */
+#define AR_PMCTRL_D3COLD_VAUX		0x00800000
+#define AR_PMCTRL_HOST_PME_EN		0x00400000    /* Send OOB WAKE_L on WoW event */
+#define AR_PMCTRL_WOW_PME_CLR		0x00200000    /* Clear WoW event */
+#define AR_PMCTRL_PWR_STATE_MASK	0x0F000000    /* Power State Mask */
+#define AR_PMCTRL_PWR_STATE_D1D3	0x0F000000    /* Activate D1 and D3 */
+#define AR_PMCTRL_PWR_STATE_D0		0x08000000    /* Activate D0 */
+#define AR_PMCTRL_PWR_PM_CTRL_ENA	0x00008000    /* Enable power management */
+
+#define AR_WOW_BEACON_TIMO_MAX		0xFFFFFFFF  /* Max. value for Beacon Timeout */
+
+/*
+ * MAC WoW Registers.
+ */
+#define AR_WOW_PATTERN_REG		0x825C
+#define AR_WOW_COUNT_REG		0x8260
+#define AR_WOW_BCN_EN_REG		0x8270
+#define AR_WOW_BCN_TIMO_REG		0x8274
+#define AR_WOW_KEEP_ALIVE_TIMO_REG	0x8278
+#define AR_WOW_KEEP_ALIVE_REG		0x827C
+#define AR_WOW_US_SCALAR_REG		0x8284
+#define AR_WOW_KEEP_ALIVE_DELAY_REG	0x8288
+#define AR_WOW_PATTERN_MATCH_REG	0x828C
+#define AR_WOW_PATTERN_OFF1_REG		0x8290 /* Pattern bytes 0 -> 3 */
+#define AR_WOW_PATTERN_OFF2_REG		0x8294 /* Pattern bytes 4 -> 7 */
+/* For AR9285 or Later version of chips */
+#define AR_WOW_EXACT_REG		0x829C
+#define AR_WOW_LENGTH1_REG		0x8360
+#define AR_WOW_LENGTH2_REG		0x8364
+/* Register to enable pattern match for less than 256 bytes packets */
+#define AR_WOW_PATTERN_MATCH_LT_256B_REG	0x8368
+
+/* AR_WOW_PATTERN_REG Values */
+#define AR_WOW_BACK_OFF_SHIFT(x)	((x & 0xf) << 27)    /* in usecs */
+#define AR_WOW_MAC_INTR_EN		0x00040000
+#define AR_WOW_MAGIC_EN			0x00010000
+#define AR_WOW_PATTERN_EN(x)		((x & 0xff) << 0)
+#define AR_WOW_PATTERN_FOUND_SHIFT	8
+#define AR_WOW_PATTERN_FOUND(x)		(x & (0xff << AR_WOW_PATTERN_FOUND_SHIFT))
+#define AR_WOW_PATTERN_FOUND_MASK	((0xff) << AR_WOW_PATTERN_FOUND_SHIFT)
+#define AR_WOW_MAGIC_PAT_FOUND		0x00020000
+#define AR_WOW_MAC_INTR			0x00080000
+#define AR_WOW_KEEP_ALIVE_FAIL		0x00100000
+#define AR_WOW_BEACON_FAIL		0x00200000
+
+#define AR_WOW_STATUS(x)	(x & (AR_WOW_PATTERN_FOUND_MASK | \
+				      AR_WOW_MAGIC_PAT_FOUND | \
+				      AR_WOW_KEEP_ALIVE_FAIL | \
+				      AR_WOW_BEACON_FAIL))
+#define AR_WOW_CLEAR_EVENTS(x)	(x & ~(AR_WOW_PATTERN_EN(0xff) | \
+				       AR_WOW_MAGIC_EN | \
+				       AR_WOW_MAC_INTR_EN | \
+				       AR_WOW_BEACON_FAIL | \
+				       AR_WOW_KEEP_ALIVE_FAIL))
+
+/* AR_WOW_COUNT_REG Values */
+#define AR_WOW_AIFS_CNT(x)		((x & 0xff) << 0)
+#define AR_WOW_SLOT_CNT(x)		((x & 0xff) << 8)
+#define AR_WOW_KEEP_ALIVE_CNT(x)	((x & 0xff) << 16)
+
+/* AR_WOW_BCN_EN_REG */
+#define AR_WOW_BEACON_FAIL_EN		0x00000001
+
+/* AR_WOW_BCN_TIMO_REG */
+#define AR_WOW_BEACON_TIMO		0x40000000  /* Valid if BCN_EN is set */
+
+/* AR_WOW_KEEP_ALIVE_TIMO_REG */
+#define AR_WOW_KEEP_ALIVE_TIMO		0x00007A12
+#define AR_WOW_KEEP_ALIVE_NEVER		0xFFFFFFFF
+
+/* AR_WOW_KEEP_ALIVE_REG */
+#define AR_WOW_KEEP_ALIVE_AUTO_DIS	0x00000001
+#define AR_WOW_KEEP_ALIVE_FAIL_DIS	0x00000002
+
+/* AR_WOW_KEEP_ALIVE_DELAY_REG */
+#define AR_WOW_KEEP_ALIVE_DELAY		0x000003E8 /* 1 msec */
+
+/*
+ * Keep it long for Beacon workaround - ensures no false alarm
+ */
+#define AR_WOW_BMISSTHRESHOLD 0x20
+
+/* AR_WOW_PATTERN_MATCH_REG */
+#define AR_WOW_PAT_END_OF_PKT(x)	((x & 0xf) << 0)
+#define AR_WOW_PAT_OFF_MATCH(x)		((x & 0xf) << 8)
+
+/*
+ * Default values for Wow Configuration for backoff, aifs, slot, keep-alive, etc
+ * to be programmed into various registers.
+ */
+#define AR_WOW_PAT_BACKOFF	0x00000004 /* AR_WOW_PATTERN_REG */
+#define AR_WOW_CNT_AIFS_CNT	0x00000022 /* AR_WOW_COUNT_REG */
+#define AR_WOW_CNT_SLOT_CNT	0x00000009 /* AR_WOW_COUNT_REG */
+/*
+ * Keepalive count applicable for AR9280 2.0 and above.
+ */
+#define AR_WOW_CNT_KA_CNT 0x00000008    /* AR_WOW_COUNT_REG */
+
+/* WoW - Transmit buffer for keep alive frames */
+#define AR_WOW_TRANSMIT_BUFFER	0xE000 /* E000 - EFFC */
+
+#define AR_WOW_KA_DESC_WORD2	0xE000
+#define AR_WOW_KA_DESC_WORD3	0xE004
+#define AR_WOW_KA_DESC_WORD4	0xE008
+#define AR_WOW_KA_DESC_WORD5	0xE00C
+#define AR_WOW_KA_DESC_WORD6	0xE010
+#define AR_WOW_KA_DESC_WORD7	0xE014
+#define AR_WOW_KA_DESC_WORD8	0xE018
+#define AR_WOW_KA_DESC_WORD9	0xE01C
+#define AR_WOW_KA_DESC_WORD10	0xE020
+#define AR_WOW_KA_DESC_WORD11	0xE024
+#define AR_WOW_KA_DESC_WORD12	0xE028
+#define AR_WOW_KA_DESC_WORD13	0xE02C
+
+#define AR_WOW_KA_DATA_WORD0	0xE030
+#define AR_WOW_KA_DATA_WORD1	0xE034
+#define AR_WOW_KA_DATA_WORD2	0xE038
+#define AR_WOW_KA_DATA_WORD3	0xE03C
+#define AR_WOW_KA_DATA_WORD4	0xE040
+#define AR_WOW_KA_DATA_WORD5	0xE044
+
+/* WoW Transmit Buffer for patterns */
+#define AR_WOW_TB_PATTERN0	0xE100
+#define AR_WOW_TB_PATTERN1	0xE200
+#define AR_WOW_TB_PATTERN2	0xE300
+#define AR_WOW_TB_PATTERN3	0xE400
+#define AR_WOW_TB_PATTERN4	0xE500
+#define AR_WOW_TB_PATTERN5	0xE600
+#define AR_WOW_TB_PATTERN6	0xE700
+#define AR_WOW_TB_PATTERN7	0xE800
+#define AR_WOW_TB_MASK0		0xEC00
+#define AR_WOW_TB_MASK1		0xEC20
+#define AR_WOW_TB_MASK2		0xEC40
+#define AR_WOW_TB_MASK3		0xEC60
+#define AR_WOW_TB_MASK4		0xEC80
+#define AR_WOW_TB_MASK5		0xECa0
+#define AR_WOW_TB_MASK6		0xECC0
+#define AR_WOW_TB_MASK7		0xECE0
+
+/* Currently Pattern 0-7 are supported - so bit 0-7 are set */
+#define AR_WOW_PATTERN_SUPPORTED	0xFF
+#define AR_WOW_LENGTH_MAX		0xFF
+#define AR_WOW_LENGTH1_SHIFT(_i)	((0x3 - ((_i) & 0x3)) << 0x3)
+#define AR_WOW_LENGTH1_MASK(_i)		(AR_WOW_LENGTH_MAX << AR_WOW_LENGTH1_SHIFT(_i))
+#define AR_WOW_LENGTH2_SHIFT(_i)	((0x7 - ((_i) & 0x7)) << 0x3)
+#define AR_WOW_LENGTH2_MASK(_i)		(AR_WOW_LENGTH_MAX << AR_WOW_LENGTH2_SHIFT(_i))
+
 #endif
diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c
new file mode 100644
index 0000000..ca9073c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ath9k.h"
+#include "reg.h"
+
+#ifdef CONFIG_PM
+
+/*
+ * This routine is called to configure the SerDes register for the
+ * AR9280 2.0 and above chip during WOW sleep.
+ */
+static void
+ath9k_ar928xConfigSerDes_WowSleep(struct ath_hw *ah)
+{
+	unsigned int i;
+
+	/*
+	 * For WOW sleep, we reprogram the SerDes so that the PLL and CHK REQ
+	 * are both enabled. This uses more power but in certain cases this
+	 * is required as otherwise WOW sleep is unstable and chip may
+	 * disappears.
+	 */
+	for (i = 0; i < ah->iniPcieSerdesWow.ia_rows; i++)
+		REG_WRITE(ah,
+			  INI_RA(&ah->iniPcieSerdesWow, i, 0),
+			  INI_RA(&ah->iniPcieSerdesWow, i, 1));
+	udelay(1000);
+}
+
+static bool ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
+{
+	u32 frame_len = 28;
+	u32 tpc = 0x3f;
+	u32 antenna_mode = 1;
+	u32 transmit_rate;
+	u32 frame_type = 0x2; /* Frame Type -> Data */
+	u32 sub_type = 0x4; /* Subtype -> Null Data */
+	u32 to_ds = 1;
+	u32 duration_id = 0x3d;
+	u8 *StaMacAddr, *ApMacAddr;
+	u8 *addr1, *addr2, *addr3;
+	u32 ctl[12] = { 0 };
+	u32 data_word0 = 0, data_word1 = 0, data_word2 = 0,
+	data_word3 = 0, data_word4 = 0, data_word5 = 0;
+	u32 i;
+
+	StaMacAddr = (u8 *)ah->macaddr;
+	ApMacAddr = (u8 *)ah->ah_sc->curbssid;
+	addr2 = StaMacAddr;
+	addr1 = addr3 = ApMacAddr;
+
+	/*
+	 * XXX: we need a way to determine if the AP we're on
+	 * is using CCK only and if so use this:
+	 * transmit_rate = 0x1B; // CCK_1M
+	 * For now we just assume your AP supports OFDM
+	 */
+	transmit_rate = 0xB; /* OFDM_6M */
+
+	/* Set the Transmit Buffer. */
+	ctl[0] = (frame_len | (tpc << 16)) + (antenna_mode << 25);
+	ctl[1] = 0;
+	ctl[2] = 0x7 << 16; /* tx_tries0 */
+	ctl[3] = transmit_rate;
+	ctl[4] = 0;
+	ctl[7] = ah->txchainmask << 2;
+
+	for (i = 0; i < 12; i++)
+		REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
+
+	data_word0 = (frame_type << 2) | (sub_type << 4) |
+		     (to_ds << 8) | (duration_id << 16);
+	data_word1 = (((u32)addr1[3] << 24) | ((u32)addr1[2] << 16) |
+		     ((u32)addr1[1]) << 8 | ((u32)addr1[0]));
+	data_word2 = (((u32)addr2[1] << 24) | ((u32)addr2[0] << 16) |
+		     ((u32)addr1[5]) << 8 | ((u32)addr1[4]));
+	data_word3 = (((u32)addr2[5] << 24) | ((u32)addr2[4] << 16) |
+		     ((u32)addr2[3]) << 8 | ((u32)addr2[2]));
+	data_word4 = (((u32)addr3[3] << 24) | ((u32)addr3[2] << 16) |
+		     ((u32)addr3[1]) << 8 | (u32)addr3[0]);
+	data_word5 = (((u32)addr3[5]) << 8 | ((u32)addr3[4]));
+
+	REG_WRITE(ah, AR_WOW_KA_DATA_WORD0, data_word0);
+	REG_WRITE(ah, AR_WOW_KA_DATA_WORD1, data_word1);
+	REG_WRITE(ah, AR_WOW_KA_DATA_WORD2, data_word2);
+	REG_WRITE(ah, AR_WOW_KA_DATA_WORD3, data_word3);
+	REG_WRITE(ah, AR_WOW_KA_DATA_WORD4, data_word4);
+	REG_WRITE(ah, AR_WOW_KA_DATA_WORD5, data_word5);
+
+	return true;
+}
+
+/* TBD: Should querying hw for hardware capability */
+#define MAX_PATTERN_SIZE	256
+#define MAX_PATTERN_MASK_SIZE	32
+
+/* Deducting the disassociate/deauthenticate packets */
+#define MAX_NUM_USER_PATTERN	6
+
+# if 0
+static void ath9k_wow_apply_pattern(struct ath_hw *ah,
+				    u8 *pAthPattern, u8 *pAthMask,
+				    int pattern_count, u32 athPatternLen)
+{
+	unsigned int i;
+	u32 reg_pat[] = {
+		AR_WOW_TB_PATTERN0,
+		AR_WOW_TB_PATTERN1,
+		AR_WOW_TB_PATTERN2,
+		AR_WOW_TB_PATTERN3,
+		AR_WOW_TB_PATTERN4,
+		AR_WOW_TB_PATTERN5,
+		AR_WOW_TB_PATTERN6,
+		AR_WOW_TB_PATTERN7
+	};
+	u32 reg_mask[] = {
+		AR_WOW_TB_MASK0,
+		AR_WOW_TB_MASK1,
+		AR_WOW_TB_MASK2,
+		AR_WOW_TB_MASK3,
+		AR_WOW_TB_MASK4,
+		AR_WOW_TB_MASK5,
+		AR_WOW_TB_MASK6,
+		AR_WOW_TB_MASK7
+	};
+	u32 pattern_val;
+	u32 mask_val;
+	u8 mask_bit = 0x1;
+	u8 pattern;
+
+	/* TBD: should check count by querying the hardware capability */
+	if (pattern_count >= MAX_NUM_USER_PATTERN)
+		return;
+
+	pattern = (u8)REG_READ(ah, AR_WOW_PATTERN_REG);
+	pattern = pattern | (mask_bit << pattern_count);
+	REG_WRITE(ah, AR_WOW_PATTERN_REG, pattern);
+
+	/* Set the registers for pattern */
+	for (i = 0; i < MAX_PATTERN_SIZE; i+=4) {
+		pattern_val = (((u32)pAthPattern[i]) |
+			      ((u32)pAthPattern[i+1] << 8) |
+			      ((u32)pAthPattern[i+2] << 16) |
+			      ((u32)pAthPattern[i+3] << 24));
+		REG_WRITE(ah, (reg_pat[pattern_count] + i), pattern_val);
+	}
+
+	/* Set the registers for mask */
+	for (i = 0; i < MAX_PATTERN_MASK_SIZE; i+=4) {
+		mask_val = (((u32)pAthMask[i]) |
+			   ((u32)pAthMask[i+1] << 8) |
+			   ((u32)pAthMask[i+2] << 16) |
+			   ((u32)pAthMask[i+3] << 24));
+		REG_WRITE(ah, (reg_mask[pattern_count] + i), mask_val);
+	}
+
+	if (AR_SREV_9285_10_OR_LATER(ah)) {
+		/* Set the pattern length to be matched */
+		u32 val;
+		if (pattern_count < 4) {
+			/* Pattern 0-3 uses AR_WOW_LENGTH1_REG register */
+			val = REG_READ(ah, AR_WOW_LENGTH1_REG);
+			val = ((val & (~AR_WOW_LENGTH1_MASK(pattern_count))) |
+			      ((athPatternLen & AR_WOW_LENGTH_MAX)
+				<< AR_WOW_LENGTH1_SHIFT(pattern_count)));
+			REG_WRITE(ah, AR_WOW_LENGTH1_REG, val);
+		} else {
+			/* Pattern 4-7 uses AR_WOW_LENGTH2_REG register */
+			val = REG_READ(ah, AR_WOW_LENGTH2_REG);
+			val = ((val & (~AR_WOW_LENGTH2_MASK(pattern_count))) |
+			      ((athPatternLen & AR_WOW_LENGTH_MAX)
+				<< AR_WOW_LENGTH2_SHIFT(pattern_count)));
+			REG_WRITE(ah, AR_WOW_LENGTH2_REG, val);
+		}
+	}
+
+	ah->ah_wowEventMask |=
+		(1 << (pattern_count + AR_WOW_PATTERN_FOUND_SHIFT));
+}
+#endif
+
+static bool ath9k_set_power_mode_wow_sleep(struct ath_hw *ah)
+{
+	REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
+
+	REG_WRITE(ah, AR_CR, AR_CR_RXD); /* Set receive disable bit */
+	if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) {
+		DPRINTF(ah->ah_sc, ATH_DBG_CONFIG,
+			"dma failed to stop in 10ms\n"
+			"AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n",
+			REG_READ(ah, AR_CR),
+			REG_READ(ah, AR_DIAG_SW));
+		return false;
+	} else {
+		REG_WRITE(ah, AR_RXDP, 0x0);
+
+		/* AR9280 2.0/2.1 WOW has sleep issue, do not set it to sleep */
+		if (AR_SREV_9280_20(ah))
+			return true;
+		else
+			REG_WRITE(ah, AR_RTC_FORCE_WAKE,
+				  AR_RTC_FORCE_WAKE_ON_INT);
+		return true;
+	}
+}
+
+void ath9k_hw_wow_enable(struct ath_hw *ah, u32 patternEnable)
+{
+	u32 init_val, val, rval = 0;
+	/* Send a Keep-Alive frame every 900 millisec */
+	const int ka_timo = 900;
+	/* Delay of 4 millisec between two KeepAlive's */
+	const int ka_delay = 4;
+	u32 wow_event_mask;
+
+	/*
+	 * ah_wowEventMask is a mask to the AR_WOW_PATTERN_REG register to
+	 * indicate which WOW events that we have enabled. The WOW Events
+	 * are from the patternEnable in this function and pattern_count of
+	 * ath9k_wow_apply_pattern()
+	 */
+	wow_event_mask = ah->ah_wowEventMask;
+
+	/*
+	 * Untie Power-On-Reset from the PCI-E Reset. When we are in WOW sleep,
+	 * we do not want the Reset from the PCI-E to disturb our hw state.
+	 */
+	if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress) {
+		/*
+		 * We need to untie the internal POR (power-on-reset) to the
+		 * external PCI-E reset. We also need to tie the PCI-E Phy
+		 * reset to the PCI-E reset.
+		 */
+		u32 wa_reg_val;
+		if (AR_SREV_9285(ah))
+			wa_reg_val = AR9285_WA_DEFAULT;
+		else
+			wa_reg_val = AR9280_WA_DEFAULT;
+		wa_reg_val = wa_reg_val & ~(AR_WA_UNTIE_RESET_EN);
+		wa_reg_val = wa_reg_val | AR_WA_RESET_EN | AR_WA_POR_SHORT;
+		REG_WRITE(ah, AR_WA, wa_reg_val);
+
+		if (!AR_SREV_9285(ah) || AR_SREV_9285_12_OR_LATER(ah)) {
+			/*
+			 * For WOW sleep, we reprogram the SerDes so that the
+			 * PLL and CHK REQ are both enabled. This uses more
+			 * power but otherwise in certain cases, WOW sleep is
+			 * unusable and chip may disappears.
+			 */
+			ath9k_ar928xConfigSerDes_WowSleep(ah);
+		}
+	}
+
+	/*
+	 * Set the power states appropriately and enable pme.
+	 */
+	val = REG_READ(ah, AR_PCIE_PM_CTRL);
+	val |= AR_PMCTRL_HOST_PME_EN |
+	       AR_PMCTRL_PWR_PM_CTRL_ENA |
+	       AR_PMCTRL_AUX_PWR_DET;
+	val &= ~AR_PMCTRL_WOW_PME_CLR;
+	REG_WRITE(ah, AR_PCIE_PM_CTRL, val);
+
+	/*
+	 * Setup for:
+	 *     - beacon misses
+	 *     - magic pattern
+	 *     - keep alive timeout
+	 *     - pattern matching
+	 */
+
+	/*
+	 * Program some default values for keep-alives, beacon misses, etc.
+	 */
+	init_val = REG_READ(ah, AR_WOW_PATTERN_REG);
+	val = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF) | init_val;
+	REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
+	rval = REG_READ(ah, AR_WOW_PATTERN_REG);
+
+	init_val = REG_READ(ah, AR_WOW_COUNT_REG);
+	val = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) | \
+	      AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) | \
+	AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT);
+	REG_WRITE(ah, AR_WOW_COUNT_REG, val);
+	rval = REG_READ(ah, AR_WOW_COUNT_REG);
+
+
+	init_val = REG_READ(ah, AR_WOW_BCN_TIMO_REG);
+	if (patternEnable & AH_WOW_BEACON_MISS)
+		val = AR_WOW_BEACON_TIMO;
+	else
+		/* We are not using the beacon miss. Program a large value. */
+		val = AR_WOW_BEACON_TIMO_MAX;
+	REG_WRITE(ah, AR_WOW_BCN_TIMO_REG, val);
+
+	init_val = REG_READ(ah, AR_WOW_KEEP_ALIVE_TIMO_REG);
+
+	/*
+	 * Keep Alive Timo in ms.
+	 */
+	if (patternEnable == 0)
+		val =  AR_WOW_KEEP_ALIVE_NEVER;
+	else
+		val =  ka_timo * 32;
+	REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO_REG, val);
+	rval = REG_READ(ah, AR_WOW_KEEP_ALIVE_TIMO_REG);
+
+	init_val = REG_READ(ah, AR_WOW_KEEP_ALIVE_DELAY_REG);
+	/*
+	 * Keep Alive delay in us.
+	 */
+	val = ka_delay * 1000;
+	REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY_REG, val);
+	rval = REG_READ(ah, AR_WOW_KEEP_ALIVE_DELAY_REG);
+
+	/*
+	 * Create KeepAlive Pattern to respond to beacons.
+	 */
+	ath9k_wow_create_keep_alive_pattern(ah);
+
+	/*
+	 * Configure Mac Wow Registers.
+	 */
+
+	val = REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
+	/*
+	 * Send keep alive timeouts anyway.
+	 */
+	val &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS;
+
+	if (patternEnable & AH_WOW_LINK_CHANGE) {
+		val &= ~ AR_WOW_KEEP_ALIVE_FAIL_DIS;
+		wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
+	} else
+		val |=  AR_WOW_KEEP_ALIVE_FAIL_DIS;
+
+	REG_WRITE(ah, AR_WOW_KEEP_ALIVE_REG, val);
+	val = REG_READ(ah, AR_WOW_BCN_EN_REG);
+
+	/*
+	 * We are relying on a bmiss failure. Ensure we have enough
+	 * threshold to prevent false positives.
+	 */
+	REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
+		      AR_WOW_BMISSTHRESHOLD);
+
+	/*
+	 * Beacon miss & user pattern events do not work on AR5416.
+	 * We enable beacon miss wow pattern only for AR9280...
+	 */
+	if (!AR_SREV_9280_10_OR_LATER(ah))
+		patternEnable &= ~AH_WOW_BEACON_MISS;
+
+	if (patternEnable & AH_WOW_BEACON_MISS) {
+		val |= AR_WOW_BEACON_FAIL_EN;
+		wow_event_mask |= AR_WOW_BEACON_FAIL;
+	} else
+		val &= ~AR_WOW_BEACON_FAIL_EN;
+
+	REG_WRITE(ah, AR_WOW_BCN_EN_REG, val);
+
+	/*
+	 * Enable the magic packet registers.
+	 */
+	val = REG_READ(ah, AR_WOW_PATTERN_REG);
+	if (patternEnable & AH_WOW_MAGIC_PATTERN_EN) {
+		val |= AR_WOW_MAGIC_EN;
+		wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
+	} else
+		val &= ~AR_WOW_MAGIC_EN;
+
+	val |= AR_WOW_MAC_INTR_EN;
+	REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
+
+	/*
+	 * For AR9285 and later version of the chips
+	 * enable wow pattern match for packets less than
+	 * 256 bytes for all patterns.
+	 */
+	if (AR_SREV_9285_10_OR_LATER(ah))
+		REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B_REG,
+			  AR_WOW_PATTERN_SUPPORTED);
+
+	/*
+	 * Set the power states appropriately and enable pme.
+	 */
+	val = REG_READ(ah, AR_PCIE_PM_CTRL);
+	val |=  AR_PMCTRL_PWR_STATE_D1D3 |
+		AR_PMCTRL_HOST_PME_EN |
+		AR_PMCTRL_PWR_PM_CTRL_ENA;
+	REG_WRITE(ah, AR_PCIE_PM_CTRL, val);
+
+	ath9k_set_power_mode_wow_sleep(ah);
+
+	ah->ah_wowEventMask = wow_event_mask;
+}
+
+u32 ath9k_hw_wow_wake_up(struct ath_hw *ah)
+{
+	u32 wowStatus = 0;
+	u32 val = 0, rval;
+
+	/*
+	 * Read the WOW Status register to know the wakeup reason.
+	 */
+	rval = REG_READ(ah, AR_WOW_PATTERN_REG);
+	val = AR_WOW_STATUS(rval);
+
+	/*
+	 * Mask only the WOW events that we have enabled. Sometimes, we have
+	 * spurious WOW events from the AR_WOW_PATTERN_REG register. This mask
+	 * will clean it up.
+	 */
+	val &= ah->ah_wowEventMask;
+
+	if (val) {
+		if (val & AR_WOW_MAGIC_PAT_FOUND)
+			wowStatus |= AH_WOW_MAGIC_PATTERN_EN;
+		if (AR_WOW_PATTERN_FOUND(val))
+			wowStatus |= AH_WOW_USER_PATTERN_EN;
+		if (val & AR_WOW_KEEP_ALIVE_FAIL)
+			wowStatus |= AH_WOW_LINK_CHANGE;
+		if (val & AR_WOW_BEACON_FAIL)
+			wowStatus |= AH_WOW_BEACON_MISS;
+	}
+
+	/*
+	 * Set and clear WOW_PME_CLEAR registers for the chip to generate next
+	 * wow signal. Disable D3 before accessing other registers ?
+	 */
+	val = REG_READ(ah, AR_PCIE_PM_CTRL);
+	/* Do we have to check the bit value 0x01000000 (7-10) ?? */
+	val &= ~AR_PMCTRL_PWR_STATE_D1D3;
+	val |= AR_PMCTRL_WOW_PME_CLR;
+	REG_WRITE(ah, AR_PCIE_PM_CTRL, val);
+
+	/*
+	 * Clear all events.
+	 */
+	REG_WRITE(ah, AR_WOW_PATTERN_REG,
+		  AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN_REG)));
+
+	/*
+	 * Tie reset register.
+	 * NB: Not tieing it back might have some repurcussions.
+	 */
+	if (AR_SREV_9280_10_OR_LATER(ah)) {
+		REG_WRITE(ah, AR_WA, REG_READ(ah, AR_WA) |
+				     AR_WA_UNTIE_RESET_EN |
+				     AR_WA_POR_SHORT |
+				     AR_WA_RESET_EN);
+	}
+
+	/* Restore the Beacon Threshold to init value */
+	REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);
+
+	/*
+	 * Restore the way the PCI-E Reset, Power-On-Reset, external
+	 * PCIE_POR_SHORT pins are tied to its original value. Previously
+	 * just before WOW sleep, we untie the PCI-E Reset to our Chip's
+	 * Power On Reset so that any PCI-E reset from the bus will not
+	 * reset our chip.
+	 */
+	if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress)
+		ath9k_hw_configpcipowersave(ah, 0);
+
+	ah->ah_wowEventMask = 0;
+
+	return wowStatus;
+}
+
+void ath9k_wow_set_gpio_reset_low(struct ath_hw *ah)
+{
+    u32 val;
+
+    val = REG_READ(ah, AR_GPIO_OE_OUT);
+    val |= (1 << (2 * 2));
+    REG_WRITE(ah, AR_GPIO_OE_OUT, val);
+    val = REG_READ(ah, AR_GPIO_OE_OUT);
+    val = REG_READ(ah,AR_GPIO_IN_OUT );
+}
+
+const char *
+ath9k_hw_wow_event_to_string(u32 wow_event)
+{
+	if (wow_event & AH_WOW_MAGIC_PATTERN_EN)
+		return "Magic pattern";
+	if (wow_event & AH_WOW_USER_PATTERN_EN)
+		return "User pattern";
+	if (wow_event & AH_WOW_LINK_CHANGE)
+		return "Link change";
+	if (wow_event & AH_WOW_BEACON_MISS)
+		return "Beacon miss";
+	return "Uknown reason";
+}
+
+#endif /* CONFIG_PM */
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 3/5] cfg80211: add WoW support
From: Luis R. Rodriguez @ 2009-07-15  0:21 UTC (permalink / raw)
  To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
In-Reply-To: <1247617320-20163-1-git-send-email-lrodriguez@atheros.com>

We support a set and get for WoW parameters. We support
setting of 3 type of triggers:

 * Magic Packet
 * Beacon miss
 * Link change (no longer associated)

Not yet supported:

 * User pattern match

The user pattern match will need a parser as patterns
can be customized. It should be noted WoW will require
suspend to S3 Hot (power cable plugged), and proper
BIOS support to receive PCI PMEs and kick the box on.

For USB this may be easier and no BIOS intervention should
be required.

Drivers which support WoW must set their supported hardware/driver
triggers on the wiphy's cfg80211 wow_triggers_supported. cfg80211
drivers will be informed of the enabled wow triggers through the
suspend() cfg80211 callback. By default we disable all triggers.

Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 include/linux/nl80211.h |   46 +++++++++++++++++++
 include/net/cfg80211.h  |   26 ++++++++++-
 net/wireless/core.h     |    3 +
 net/wireless/nl80211.c  |  116 +++++++++++++++++++++++++++++++++++++++++++++++
 net/wireless/sysfs.c    |    2 +-
 5 files changed, 190 insertions(+), 3 deletions(-)

diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 962e223..0c869ca 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -262,6 +262,18 @@
  *	reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
  *	%NL80211_ATTR_REASON_CODE attributes are used.
  *
+ * @NL80211_CMD_GET_WOW: get Wake-on-Wireless-LAN (WoW) settings.
+ * @NL80211_CMD_SET_WOW: set Wake-on-Wireless-LAN (Wow) settings. Wake on
+ *	wireless makes use of standard Wake-on-LAN (WoL) frames, you receive
+ *	a WoW frame when your AP sends you a regular WOL frame. The difference
+ *	difference WoL is you need to be associated to an AP in order to
+ *	receive WoW frames, so additional triggers are available for a wakeup.
+ *	A driver capable of WoW should initialize the wiphy with its supported
+ *	WoW triggers. Upon suspend cfg80211 will inform the driver of the user
+ *	enabled triggers. By default no WoW triggers are enabled.
+ *	For more information see:
+ *	http://wireless.kernel.org/en/users/Documentation/WoW
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -336,6 +348,9 @@ enum nl80211_commands {
 	NL80211_CMD_ROAM,
 	NL80211_CMD_DISCONNECT,
 
+	NL80211_CMD_GET_WOW,
+	NL80211_CMD_SET_WOW,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -355,6 +370,8 @@ enum nl80211_commands {
 #define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE
 #define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
 #define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT
+#define NL80211_CMD_GET_WOW NL80211_CMD_GET_WOW
+#define NL80211_CMD_SET_WOW NL80211_CMD_SET_WOW
 
 /**
  * enum nl80211_attrs - nl80211 netlink attributes
@@ -573,6 +590,12 @@ enum nl80211_commands {
  *	and join_ibss(), key information is in a nested attribute each
  *	with %NL80211_KEY_* sub-attributes
  *
+ * @NL80211_ATTR_WOW_TRIGGERS_SUPPORTED: the supported WoW triggers
+ * @NL80211_ATTR_WOW_TRIGGERS_ENABLED: used by %NL80211_CMD_SET_WOW to
+ *	indicate which WoW triggers should be enabled. This is also
+ *	used by %NL80211_CMD_GET_WOW to get the currently enabled WoW
+ *	triggers.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -701,6 +724,9 @@ enum nl80211_attrs {
 	NL80211_ATTR_KEY,
 	NL80211_ATTR_KEYS,
 
+	NL80211_ATTR_WOW_TRIGGERS_SUPPORTED,
+	NL80211_ATTR_WOW_TRIGGERS_ENABLED,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -1372,4 +1398,24 @@ enum nl80211_key_attributes {
 	NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1
 };
 
+/**
+ * enum nl80211_wow_triggers - Wake-on-Wireless-LAN triggers
+ *
+ * NL80211_WOW_TRIGGER_MAGIC_PACKET: a wake signal will be sent to the
+ *     devices if a magic packet is received.
+ * NL80211_WOW_TRIGGER_BMISS: WoW signal will be sent to the device when
+ *     a beacon has been missed by the associated AP.
+ * NL80211_WOW_TRIGGER_LINK_CHANGE: a wake signal will be sent to
+ *     the device if a link change is detected on the device.
+ * NL80211_WOW_TRIGGER_USER_PATTERN: a wake signal will be sent to the
+ *     device if a user configurable pattern is received by
+ *     the device.
+ */
+enum nl80211_wow_triggers {
+	NL80211_WOW_TRIGGER_MAGIC_PACKET	= 1 << 0,
+	NL80211_WOW_TRIGGER_BMISS		= 1 << 1,
+	NL80211_WOW_TRIGGER_LINK_CHANGE		= 1 << 2,
+	NL80211_WOW_TRIGGER_PATTERN		= 1 << 3,
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index a981ca8..d45fcec 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -828,6 +828,21 @@ struct cfg80211_bitrate_mask {
 	u32 maxrate; /* in kbps, 0 == no limit */
 };
 
+/*
+ * struct cfg80211_wow - Wake on Wireless-LAN support info
+ *
+ * This structure defines the WoW triggers enabled and set for the device.
+ * For now we only carry the supported tiggers but this is expected to grow
+ * once we add support for user patterns.
+ *
+ * @triggers_enabled: enabled triggers by the user. Default
+ *	is to disable all triggers. The flags for this bitmask
+ *	are %NL80211_WOW_TRIGGER_*.
+ */
+struct cfg80211_wow {
+	u32 triggers_enabled;
+};
+
 /**
  * struct cfg80211_ops - backend description for wireless configuration
  *
@@ -841,7 +856,8 @@ struct cfg80211_bitrate_mask {
  * wireless extensions but this is subject to reevaluation as soon as this
  * code is used more widely and we have a first user without wext.
  *
- * @suspend: wiphy device needs to be suspended
+ * @suspend: wiphy device needs to be suspended. We pass the struct cfg80211_wow
+ *	so the device enables the appropriate triggers during suspend.
  * @resume: wiphy device needs to be resumed
  *
  * @add_virtual_intf: create a new virtual interface with the given name,
@@ -931,7 +947,7 @@ struct cfg80211_bitrate_mask {
  * @testmode_cmd: run a test mode command
  */
 struct cfg80211_ops {
-	int	(*suspend)(struct wiphy *wiphy);
+	int	(*suspend)(struct wiphy *wiphy, struct cfg80211_wow *wow);
 	int	(*resume)(struct wiphy *wiphy);
 
 	int	(*add_virtual_intf)(struct wiphy *wiphy, char *name,
@@ -1088,6 +1104,10 @@ struct cfg80211_ops {
  * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold);
  *	-1 = fragmentation disabled, only odd values >= 256 used
  * @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled
+ * @wow_triggers_supported: supported bitmask of Wake-on-Wireless triggers.
+ *	The flags for this bitmask are %NL80211_WOW_TRIGGER_*. The driver
+ *	should set the capabilities before registering the wiphy. WoW triggers
+ *	which should be used are passed to the driver upon suspend.
  */
 struct wiphy {
 	/* assign these fields before you register the wiphy */
@@ -1115,6 +1135,8 @@ struct wiphy {
 	u32 frag_threshold;
 	u32 rts_threshold;
 
+	u32 wow_triggers_supported;
+
 	/* If multiple wiphys are registered and you're handed e.g.
 	 * a regular netdev with assigned ieee80211_ptr, you won't
 	 * know whether it points to a wiphy your driver has registered
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 2ec8ddb..1d250cd 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -66,6 +66,9 @@ struct cfg80211_registered_device {
 	struct work_struct conn_work;
 	struct work_struct event_work;
 
+	/* Used to keep track of user configurable triggers */
+	struct cfg80211_wow wow;
+
 #ifdef CONFIG_CFG80211_DEBUGFS
 	/* Debugfs entries */
 	struct wiphy_debugfsdentries {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 6dad3e7..50de866 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -133,6 +133,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
 	[NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
 	[NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
 	[NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
+
+	[NL80211_ATTR_WOW_TRIGGERS_SUPPORTED] = { .type = NLA_U32 },
+	[NL80211_ATTR_WOW_TRIGGERS_ENABLED] = { .type = NLA_U32 },
 };
 
 /* policy for the attributes */
@@ -4023,6 +4026,109 @@ unlock_rtnl:
 	return err;
 }
 
+static int nl80211_get_wow(struct sk_buff *skb,
+			   struct genl_info *info)
+{
+	struct cfg80211_registered_device *rdev;
+	struct net_device *dev;
+	struct wiphy *wiphy;
+	int err;
+	void *hdr;
+	struct sk_buff *msg;
+
+	rtnl_lock();
+
+	/* Look up our device */
+	err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+	if (err)
+		goto out_rtnl;
+
+	wiphy = &rdev->wiphy;
+
+	/* Draw up a netlink message to send back */
+	msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+	if (!msg) {
+		err = -ENOBUFS;
+		goto out;
+	}
+	hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_GET_WOW);
+	if (!hdr)
+		goto nla_put_failure;
+	NLA_PUT_U32(msg, NL80211_ATTR_WOW_TRIGGERS_SUPPORTED,
+		    wiphy->wow_triggers_supported);
+	NLA_PUT_U32(msg, NL80211_ATTR_WOW_TRIGGERS_ENABLED,
+		    rdev->wow.triggers_enabled);
+
+	genlmsg_end(msg, hdr);
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto out;
+
+ nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	err = -EMSGSIZE;
+ out:
+	/* Cleanup */
+	cfg80211_unlock_rdev(rdev);
+	dev_put(dev);
+ out_rtnl:
+	rtnl_unlock();
+
+	return err;
+}
+
+static int nl80211_set_wow(struct sk_buff *skb, struct genl_info *info)
+{
+	int err;
+	struct cfg80211_registered_device *rdev;
+	struct net_device *dev;
+	struct wiphy *wiphy;
+	u32 triggers_requested;
+
+	if (!info->attrs[NL80211_ATTR_WOW_TRIGGERS_ENABLED])
+		return -EINVAL;
+
+	rtnl_lock();
+
+	err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+	if (err)
+		goto out_rtnl;
+
+	wiphy = &rdev->wiphy;
+
+	if (!wiphy->wow_triggers_supported) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	triggers_requested =
+		nla_get_u32(info->attrs[NL80211_ATTR_WOW_TRIGGERS_ENABLED]);
+
+	/* Needs a pattern attribute implemented and passed to the driver */
+	if (triggers_requested & NL80211_WOW_TRIGGER_PATTERN)
+		return -EOPNOTSUPP;
+
+	if (!(wiphy->wow_triggers_supported & triggers_requested)) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	/*
+	 * Apply changes. This information gets passed to the
+	 * drivers during suspend.
+	 */
+	rdev->wow.triggers_enabled = triggers_requested;
+
+ out:
+	/* cleanup */
+	cfg80211_unlock_rdev(rdev);
+	dev_put(dev);
+ out_rtnl:
+	rtnl_unlock();
+
+	return err;
+}
+
 static struct genl_ops nl80211_ops[] = {
 	{
 		.cmd = NL80211_CMD_GET_WIPHY,
@@ -4253,6 +4359,16 @@ static struct genl_ops nl80211_ops[] = {
 	{
 		.cmd = NL80211_CMD_DISCONNECT,
 		.doit = nl80211_disconnect,
+	},
+	{
+		.cmd = NL80211_CMD_GET_WOW,
+		.doit = nl80211_get_wow,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_SET_WOW,
+		.doit = nl80211_set_wow,
 		.policy = nl80211_policy,
 		.flags = GENL_ADMIN_PERM,
 	},
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index efe3c5c..8b60a5b 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -64,7 +64,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
 
 	if (rdev->ops->suspend) {
 		rtnl_lock();
-		ret = rdev->ops->suspend(&rdev->wiphy);
+		ret = rdev->ops->suspend(&rdev->wiphy, &rdev->wow);
 		rtnl_unlock();
 	}
 
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 2/5] adm8211: remove uneeded code during suspend/resume
From: Luis R. Rodriguez @ 2009-07-15  0:21 UTC (permalink / raw)
  To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
In-Reply-To: <1247617320-20163-1-git-send-email-lrodriguez@atheros.com>

mac80211 drivers do not need to stop the software queues
or call their own stop() callback upon suspend as we do it
for drivers. Equally drivers don't have to call their own
start() or start the queues as mac80211 will do it for us.

Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/adm8211.c |   17 -----------------
 1 files changed, 0 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index ecc9383..5695911 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -1964,14 +1964,6 @@ static void __devexit adm8211_remove(struct pci_dev *pdev)
 #ifdef CONFIG_PM
 static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state)
 {
-	struct ieee80211_hw *dev = pci_get_drvdata(pdev);
-	struct adm8211_priv *priv = dev->priv;
-
-	if (priv->mode != NL80211_IFTYPE_UNSPECIFIED) {
-		ieee80211_stop_queues(dev);
-		adm8211_stop(dev);
-	}
-
 	pci_save_state(pdev);
 	pci_set_power_state(pdev, pci_choose_state(pdev, state));
 	return 0;
@@ -1979,17 +1971,8 @@ static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state)
 
 static int adm8211_resume(struct pci_dev *pdev)
 {
-	struct ieee80211_hw *dev = pci_get_drvdata(pdev);
-	struct adm8211_priv *priv = dev->priv;
-
 	pci_set_power_state(pdev, PCI_D0);
 	pci_restore_state(pdev);
-
-	if (priv->mode != NL80211_IFTYPE_UNSPECIFIED) {
-		adm8211_start(dev);
-		ieee80211_wake_queues(dev);
-	}
-
 	return 0;
 }
 #endif /* CONFIG_PM */
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 0/5] wireless: add wow support
From: Luis R. Rodriguez @ 2009-07-15  0:21 UTC (permalink / raw)
  To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez

This series adds Wake-on-Wireless support for wireless with
initial support for ath9k. No event reporting is done yet,
we'll leave that for later once we figure out a unified
method for all devices (including non-wireless).

If you are not familiar with WoW you are encouraged to read:

http://wireless.kernel.org/en/users/Documentation/WoW

Please note not all laptops/desktops will have support for
allowing WoW. You will need proper BIOS support, also not
all cards have it enabled.

Luis R. Rodriguez (5):
  ath9k: do not stop the queues in driver stop
  adm8211: remove uneeded code during suspend/resume
  cfg80211: add WoW support
  mac80211: add WoW support
  ath9k: Add Wake-on-Wireless-LAN support

 drivers/net/wireless/adm8211.c              |   19 +-
 drivers/net/wireless/at76c50x-usb.c         |    3 +-
 drivers/net/wireless/ath/ar9170/main.c      |    2 +-
 drivers/net/wireless/ath/ath5k/base.c       |    4 +-
 drivers/net/wireless/ath/ath9k/Makefile     |    1 +
 drivers/net/wireless/ath/ath9k/ath9k.h      |   18 +
 drivers/net/wireless/ath/ath9k/debug.c      |    1 -
 drivers/net/wireless/ath/ath9k/hw.c         |   22 ++
 drivers/net/wireless/ath/ath9k/hw.h         |   21 ++
 drivers/net/wireless/ath/ath9k/initvals.h   |   31 ++
 drivers/net/wireless/ath/ath9k/main.c       |   27 ++-
 drivers/net/wireless/ath/ath9k/pci.c        |  102 ++++++
 drivers/net/wireless/ath/ath9k/reg.h        |  154 ++++++++
 drivers/net/wireless/ath/ath9k/wow.c        |  511 +++++++++++++++++++++++++++
 drivers/net/wireless/b43/main.c             |    2 +-
 drivers/net/wireless/b43legacy/main.c       |    2 +-
 drivers/net/wireless/iwlwifi/iwl-agn.c      |    2 +-
 drivers/net/wireless/iwlwifi/iwl3945-base.c |    2 +-
 drivers/net/wireless/libertas_tf/main.c     |    2 +-
 drivers/net/wireless/mac80211_hwsim.c       |    2 +-
 drivers/net/wireless/mwl8k.c                |    2 +-
 drivers/net/wireless/p54/main.c             |    2 +-
 drivers/net/wireless/rt2x00/rt2x00.h        |    2 +-
 drivers/net/wireless/rt2x00/rt2x00mac.c     |    2 +-
 drivers/net/wireless/rtl818x/rtl8180_dev.c  |    2 +-
 drivers/net/wireless/rtl818x/rtl8187_dev.c  |    2 +-
 drivers/net/wireless/wl12xx/wl1251_main.c   |    2 +-
 drivers/net/wireless/zd1211rw/zd_mac.c      |    2 +-
 include/linux/nl80211.h                     |   46 +++
 include/net/cfg80211.h                      |   26 ++-
 include/net/mac80211.h                      |   17 +-
 net/mac80211/cfg.c                          |    5 +-
 net/mac80211/debugfs.c                      |    2 +-
 net/mac80211/driver-ops.h                   |    6 +-
 net/mac80211/driver-trace.h                 |    6 +-
 net/mac80211/ieee80211_i.h                  |   12 +-
 net/mac80211/iface.c                        |    6 +-
 net/mac80211/pm.c                           |   31 ++-
 net/mac80211/util.c                         |   14 +
 net/wireless/core.h                         |    3 +
 net/wireless/nl80211.c                      |  116 ++++++
 net/wireless/sysfs.c                        |    2 +-
 42 files changed, 1172 insertions(+), 64 deletions(-)
 create mode 100644 drivers/net/wireless/ath/ath9k/wow.c


^ permalink raw reply

* [PATCH 1/5] ath9k: do not stop the queues in driver stop
From: Luis R. Rodriguez @ 2009-07-15  0:21 UTC (permalink / raw)
  To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
In-Reply-To: <1247617320-20163-1-git-send-email-lrodriguez@atheros.com>

mac80211 will have disabled the queues for us when
needed.

Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/ath/ath9k/main.c |    2 --
 1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index d14f8c9..907434a 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2101,8 +2101,6 @@ static void ath9k_stop(struct ieee80211_hw *hw)
 
 	mutex_lock(&sc->mutex);
 
-	ieee80211_stop_queues(hw);
-
 	if (ath9k_wiphy_started(sc)) {
 		mutex_unlock(&sc->mutex);
 		return; /* another wiphy still in use */
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 8/9] ath9k: uninline ath9k_ps_{wakeup,restore} functions
From: Luis R. Rodriguez @ 2009-07-15  0:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, ath9k-devel, Gabor Juhos
In-Reply-To: <1247617035-15402-1-git-send-email-lrodriguez@atheros.com>

From: Gabor Juhos <juhosg@openwrt.org>

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(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index b37eb59..8b38c0a 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -674,27 +674,8 @@ static inline int ath_ahb_init(void) { return 0; };
 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);
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 6740a6b..df278fd 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2775,6 +2775,27 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
 	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.
  *
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 0/9] ath9k: rebasing pending patches
From: Luis R. Rodriguez @ 2009-07-15  0:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez

I have some patches I want to send but our queue has
been growing. To help speed up the processing of our
patches I have taken the time to cherry pick what I
saw was pending and rebased it against wireless-testing
and the rate control cleanup series I just sent.

If I missed any pendign ath9k patch you should rebase
against all pending patches and send it afterwards.

Gabor Juhos (3):
  ath9k: serialize ath9k_hw_setpower calls
  ath9k: uninline ath9k_ps_{wakeup,restore} functions
  ath9k: serialize ath9k_ps_{wakeup,restore} calls

Senthil Balasubramanian (3):
  ath9k: Manipulate and report the correct RSSI
  ath9k: RX stucks during heavy traffic in HT40 mode.
  ath9k: Fix TX hang issue with Atheros chipsets

Vasanthakumar Thiagarajan (3):
  ath9k: Remove bogus assert in ath_clone_txbuf()
  ath9k: Handle tx desc shortage more appropriately
  ath9k: Remove pointless ath9k_ps_restore() in ath_detach()

 drivers/net/wireless/ath/ath9k/ath9k.h    |   46 +++++++++--------
 drivers/net/wireless/ath/ath9k/calib.c    |   13 ++++-
 drivers/net/wireless/ath/ath9k/calib.h    |    4 +-
 drivers/net/wireless/ath/ath9k/eeprom.c   |   20 +++++++-
 drivers/net/wireless/ath/ath9k/hw.c       |   52 +++++++++++++++++++-
 drivers/net/wireless/ath/ath9k/initvals.h |   47 +++++++++--------
 drivers/net/wireless/ath/ath9k/mac.c      |   30 +++++++++---
 drivers/net/wireless/ath/ath9k/main.c     |    6 ++-
 drivers/net/wireless/ath/ath9k/recv.c     |   25 +++++++++-
 drivers/net/wireless/ath/ath9k/xmit.c     |   76 +++++++++++++++++++++++------
 10 files changed, 244 insertions(+), 75 deletions(-)


^ permalink raw reply

* [PATCH 2/9] ath9k: RX stucks during heavy traffic in HT40 mode.
From: Luis R. Rodriguez @ 2009-07-15  0:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, ath9k-devel, Senthil Balasubramanian
In-Reply-To: <1247617035-15402-1-git-send-email-lrodriguez@atheros.com>

From: Senthil Balasubramanian <senthilkumar@atheros.com>

Running iperf along with p2p traffic on both TX and RX side then
stop one side, then stop the other side, then start it up again,
eventually the STA gets into a mode that it can not pass data at
all.

A hardware workaround for invalid RSSI can make FIFO write pointer
to jump over read pointer, causing RX data corruption and repeated
DMA. Both TX and RX works fine when the workaround is disabled.

To replace the original hardware work around, software looks for
frames with post delimiter CRC error and mark the RSSI invalid so
that the upperlayer will not use the RSSI associated with this
frame. So disable the hardware workaround by updating the appropriate
registers.

Signed-off-by: Senthil Balasubramanian <senthilkumar@atheros.com>
---
 drivers/net/wireless/ath/ath9k/eeprom.c   |   20 +++++++++++-
 drivers/net/wireless/ath/ath9k/initvals.h |   47 +++++++++++++++-------------
 drivers/net/wireless/ath/ath9k/mac.c      |   30 ++++++++++++++----
 3 files changed, 67 insertions(+), 30 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index d82a0f9..df41ed5 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -1208,6 +1208,19 @@ static void ath9k_hw_4k_set_gain(struct ath_hw *ah,
 			      pModal->xatten2Margin[0]);
 		REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset,
 			      AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]);
+
+		/* Set the block 1 value to block 0 value */
+		REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000,
+			      AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN,
+			      pModal->bswMargin[0]);
+		REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000,
+			      AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]);
+		REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000,
+			      AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN,
+			      pModal->xatten2Margin[0]);
+		REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000,
+			      AR_PHY_GAIN_2GHZ_XATTEN2_DB,
+			      pModal->xatten2Db[0]);
 	}
 
 	REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset,
@@ -1215,6 +1228,11 @@ static void ath9k_hw_4k_set_gain(struct ath_hw *ah,
 	REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset,
 		      AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
 
+	REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000,
+		      AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal);
+	REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000,
+		      AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]);
+
 	if (AR_SREV_9285_11(ah))
 		REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14));
 }
@@ -1239,7 +1257,7 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
 	ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal, 0);
 
 	/* Initialize Ant Diversity settings from EEPROM */
-	if (pModal->version == 3) {
+	if (pModal->version >= 3) {
 		ant_div_control1 = ((pModal->ob_234 >> 12) & 0xf);
 		ant_div_control2 = ((pModal->db1_234 >> 12) & 0xf);
 		regVal = REG_READ(ah, 0x99ac);
diff --git a/drivers/net/wireless/ath/ath9k/initvals.h b/drivers/net/wireless/ath/ath9k/initvals.h
index e2f0a34..f67a2a9 100644
--- a/drivers/net/wireless/ath/ath9k/initvals.h
+++ b/drivers/net/wireless/ath/ath9k/initvals.h
@@ -2782,7 +2782,7 @@ static const u32 ar9280Common_9280_2[][2] = {
     { 0x00008338, 0x00ff0000 },
     { 0x0000833c, 0x00000000 },
     { 0x00008340, 0x000107ff },
-    { 0x00008344, 0x00581043 },
+    { 0x00008344, 0x00481043 },
     { 0x00009808, 0x00000000 },
     { 0x0000980c, 0xafa68e30 },
     { 0x00009810, 0xfd14e000 },
@@ -3439,7 +3439,7 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = {
     {0x00004044,  0x00000000 },
 };
 
-/* AR9285 */
+/* AR9285 Revsion 10*/
 static const u_int32_t ar9285Modes_9285[][6] = {
     { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
     { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 },
@@ -3955,7 +3955,7 @@ static const u_int32_t ar9285Common_9285[][2] = {
     { 0x00008338, 0x00000000 },
     { 0x0000833c, 0x00000000 },
     { 0x00008340, 0x00010380 },
-    { 0x00008344, 0x00581043 },
+    { 0x00008344, 0x00481043 },
     { 0x00009808, 0x00000000 },
     { 0x0000980c, 0xafe68e30 },
     { 0x00009810, 0xfd14e000 },
@@ -4121,8 +4121,9 @@ static const u_int32_t ar9285PciePhy_clkreq_off_L1_9285[][2] = {
     {0x00004044,  0x00000000 },
 };
 
-/* AR9285 v1_2 PCI Register Writes.  Created: 03/04/09 */
+/* AR9285 v1_2 PCI Register Writes.  Created: 04/13/09 */
 static const u_int32_t ar9285Modes_9285_1_2[][6] = {
+    /* Address      5G-HT20     5G-HT40     2G-HT40     2G-HT20     Turbo   */
     { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
     { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 },
     { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 },
@@ -4139,6 +4140,7 @@ static const u_int32_t ar9285Modes_9285_1_2[][6] = {
     { 0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e },
     { 0x00009844, 0x0372161e, 0x0372161e, 0x03721620, 0x03721620, 0x037216a0 },
     { 0x00009848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059 },
+    { 0x0000a848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059 },
     { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 },
     { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e },
     { 0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e },
@@ -4419,6 +4421,7 @@ static const u_int32_t ar9285Modes_9285_1_2[][6] = {
     { 0x0000abfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 },
     { 0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004 },
     { 0x0000a20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000 },
+    { 0x0000b20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000 },
     { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a },
     { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 },
     { 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 },
@@ -4618,7 +4621,7 @@ static const u_int32_t ar9285Common_9285_1_2[][2] = {
     { 0x00008338, 0x00ff0000 },
     { 0x0000833c, 0x00000000 },
     { 0x00008340, 0x00010380 },
-    { 0x00008344, 0x00581043 },
+    { 0x00008344, 0x00481043 },
     { 0x00009808, 0x00000000 },
     { 0x0000980c, 0xafe68e30 },
     { 0x00009810, 0xfd14e000 },
@@ -4752,18 +4755,18 @@ static const u_int32_t ar9285Common_9285_1_2[][2] = {
 static const u_int32_t ar9285Modes_high_power_tx_gain_9285_1_2[][6] = {
     /* Address      5G-HT20     5G-HT40     2G-HT40     2G-HT20     Turbo   */
     { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
-    { 0x0000a304, 0x00000000, 0x00000000, 0x00005200, 0x00005200, 0x00000000 },
-    { 0x0000a308, 0x00000000, 0x00000000, 0x00007201, 0x00007201, 0x00000000 },
+    { 0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200, 0x00000000 },
+    { 0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201, 0x00000000 },
     { 0x0000a30c, 0x00000000, 0x00000000, 0x0000b240, 0x0000b240, 0x00000000 },
     { 0x0000a310, 0x00000000, 0x00000000, 0x0000d241, 0x0000d241, 0x00000000 },
-    { 0x0000a314, 0x00000000, 0x00000000, 0x0000f440, 0x0000f440, 0x00000000 },
-    { 0x0000a318, 0x00000000, 0x00000000, 0x00014640, 0x00014640, 0x00000000 },
-    { 0x0000a31c, 0x00000000, 0x00000000, 0x00018680, 0x00018680, 0x00000000 },
-    { 0x0000a320, 0x00000000, 0x00000000, 0x00019841, 0x00019841, 0x00000000 },
-    { 0x0000a324, 0x00000000, 0x00000000, 0x0001ca40, 0x0001ca40, 0x00000000 },
-    { 0x0000a328, 0x00000000, 0x00000000, 0x0001fa80, 0x0001fa80, 0x00000000 },
-    { 0x0000a32c, 0x00000000, 0x00000000, 0x00023ac0, 0x00023ac0, 0x00000000 },
-    { 0x0000a330, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40, 0x00000000 },
+    { 0x0000a314, 0x00000000, 0x00000000, 0x0000f600, 0x0000f600, 0x00000000 },
+    { 0x0000a318, 0x00000000, 0x00000000, 0x00012800, 0x00012800, 0x00000000 },
+    { 0x0000a31c, 0x00000000, 0x00000000, 0x00016802, 0x00016802, 0x00000000 },
+    { 0x0000a320, 0x00000000, 0x00000000, 0x0001b805, 0x0001b805, 0x00000000 },
+    { 0x0000a324, 0x00000000, 0x00000000, 0x00021a80, 0x00021a80, 0x00000000 },
+    { 0x0000a328, 0x00000000, 0x00000000, 0x00028b00, 0x00028b00, 0x00000000 },
+    { 0x0000a32c, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40, 0x00000000 },
+    { 0x0000a330, 0x00000000, 0x00000000, 0x0002cd80, 0x0002cd80, 0x00000000 },
     { 0x0000a334, 0x00000000, 0x00000000, 0x00033d82, 0x00033d82, 0x00000000 },
     { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 },
     { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 },
@@ -4776,13 +4779,13 @@ static const u_int32_t ar9285Modes_high_power_tx_gain_9285_1_2[][6] = {
     { 0x00007838, 0xfac68803, 0xfac68803, 0xfac68803, 0xfac68803, 0xfac68803 },
     { 0x0000786c, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe },
     { 0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00 },
-    { 0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a21a652, 0x0a21a652, 0x0a22a652 },
-    { 0x0000a278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce },
-    { 0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce },
-    { 0x0000a394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce },
-    { 0x0000a398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce },
-    { 0x0000a3dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce },
-    { 0x0000a3e0, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce },
+    { 0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a216652, 0x0a216652, 0x0a22a652 },
+    { 0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 },
+    { 0x0000a27c, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7 },
+    { 0x0000a394, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 },
+    { 0x0000a398, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7 },
+    { 0x0000a3dc, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 },
+    { 0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7 },
 };
 
 static const u_int32_t ar9285Modes_original_tx_gain_9285_1_2[][6] = {
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 8ae4ec2..6f923e3 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -825,13 +825,29 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
 	ds->ds_rxstat.rs_datalen = ads.ds_rxstatus1 & AR_DataLen;
 	ds->ds_rxstat.rs_tstamp = ads.AR_RcvTimestamp;
 
-	ds->ds_rxstat.rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined);
-	ds->ds_rxstat.rs_rssi_ctl0 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt00);
-	ds->ds_rxstat.rs_rssi_ctl1 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt01);
-	ds->ds_rxstat.rs_rssi_ctl2 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt02);
-	ds->ds_rxstat.rs_rssi_ext0 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt10);
-	ds->ds_rxstat.rs_rssi_ext1 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt11);
-	ds->ds_rxstat.rs_rssi_ext2 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt12);
+	if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) {
+		ds->ds_rxstat.rs_rssi = ATH9K_RSSI_BAD;
+		ds->ds_rxstat.rs_rssi_ctl0 = ATH9K_RSSI_BAD;
+		ds->ds_rxstat.rs_rssi_ctl1 = ATH9K_RSSI_BAD;
+		ds->ds_rxstat.rs_rssi_ctl2 = ATH9K_RSSI_BAD;
+		ds->ds_rxstat.rs_rssi_ext0 = ATH9K_RSSI_BAD;
+		ds->ds_rxstat.rs_rssi_ext1 = ATH9K_RSSI_BAD;
+		ds->ds_rxstat.rs_rssi_ext2 = ATH9K_RSSI_BAD;
+	} else {
+		ds->ds_rxstat.rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined);
+		ds->ds_rxstat.rs_rssi_ctl0 = MS(ads.ds_rxstatus0,
+						AR_RxRSSIAnt00);
+		ds->ds_rxstat.rs_rssi_ctl1 = MS(ads.ds_rxstatus0,
+						AR_RxRSSIAnt01);
+		ds->ds_rxstat.rs_rssi_ctl2 = MS(ads.ds_rxstatus0,
+						AR_RxRSSIAnt02);
+		ds->ds_rxstat.rs_rssi_ext0 = MS(ads.ds_rxstatus4,
+						AR_RxRSSIAnt10);
+		ds->ds_rxstat.rs_rssi_ext1 = MS(ads.ds_rxstatus4,
+						AR_RxRSSIAnt11);
+		ds->ds_rxstat.rs_rssi_ext2 = MS(ads.ds_rxstatus4,
+						AR_RxRSSIAnt12);
+	}
 	if (ads.ds_rxstatus8 & AR_RxKeyIdxValid)
 		ds->ds_rxstat.rs_keyix = MS(ads.ds_rxstatus8, AR_KeyIdx);
 	else
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 3/9] ath9k: Fix TX hang issue with Atheros chipsets
From: Luis R. Rodriguez @ 2009-07-15  0:17 UTC (permalink / raw)
  To: linville
  Cc: linux-wireless, ath9k-devel, Senthil Balasubramanian,
	Vasanthakumar Thiagarajan
In-Reply-To: <1247617035-15402-1-git-send-email-lrodriguez@atheros.com>

From: Senthil Balasubramanian <senthilkumar@atheros.com>

The hardware doesn't generate interrupts in some cases and so work
around this by monitoring the TX status periodically and reset the
chip if required.

This behavior of the hardware not generating the TX interrupts can
be noticed through ath9k debugfs interrupt statistics when heavy
traffic is being sent from STA to AP. One can easily see this behavior
when the STA is transmitting at a higher rates. The interrupt statistics
in the debugfs interface clearly shows that only RX interrupts alone
being generated and TX being stuck.

TX should be monitored through a timer and reset the chip only when
frames are queued to the hardware but TX interrupts are not generated
for the same even after one second. Also, we shouldn't remove holding
descriptor from AC queue if it happens to be the only descriptor and
schedule TX aggregation regarless of queue depth as it improves
scheduling of AMPDUs from software to hardware queue.

Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: Senthil Balasubramanian <senthilkumar@atheros.com>
---
 drivers/net/wireless/ath/ath9k/ath9k.h |    4 ++
 drivers/net/wireless/ath/ath9k/main.c  |    3 ++
 drivers/net/wireless/ath/ath9k/xmit.c  |   57 ++++++++++++++++++++++++--------
 3 files changed, 50 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 544599b..20bf4a7 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -225,6 +225,8 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd,
 #define ATH_DS_TX_BA(_ds)          ((_ds)->ds_us.tx.ts_flags & ATH9K_TX_BA)
 #define ATH_AN_2_TID(_an, _tidno)  (&(_an)->tid[(_tidno)])
 
+#define ATH_TX_COMPLETE_POLL_INT	1000
+
 enum ATH_AGGR_STATUS {
 	ATH_AGGR_DONE,
 	ATH_AGGR_BAW_CLOSED,
@@ -240,6 +242,7 @@ struct ath_txq {
 	u8 axq_aggr_depth;
 	u32 axq_totalqueued;
 	bool stopped;
+	bool axq_tx_inprogress;
 	struct ath_buf *axq_linkbuf;
 
 	/* first desc of the last descriptor that contains CTS */
@@ -605,6 +608,7 @@ struct ath_softc {
 #endif
 	struct ath_bus_ops *bus_ops;
 	struct ath_beacon_config cur_beacon_conf;
+	struct delayed_work tx_complete_work;
 };
 
 struct ath_wiphy {
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 2ad7184..46f4a69 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1259,6 +1259,7 @@ void ath_detach(struct ath_softc *sc)
 	ath_deinit_leds(sc);
 	cancel_work_sync(&sc->chan_work);
 	cancel_delayed_work_sync(&sc->wiphy_work);
+	cancel_delayed_work_sync(&sc->tx_complete_work);
 
 	for (i = 0; i < sc->num_sec_wiphy; i++) {
 		struct ath_wiphy *aphy = sc->sec_wiphy[i];
@@ -1979,6 +1980,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
 
 	ieee80211_wake_queues(hw);
 
+	queue_delayed_work(sc->hw->workqueue, &sc->tx_complete_work, 0);
+
 mutex_unlock:
 	mutex_unlock(&sc->mutex);
 
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 5de9878..a3bc431 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -857,6 +857,7 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
 		txq->axq_aggr_depth = 0;
 		txq->axq_totalqueued = 0;
 		txq->axq_linkbuf = NULL;
+		txq->axq_tx_inprogress = false;
 		sc->tx.txqsetup |= 1<<qnum;
 	}
 	return &sc->tx.txq[qnum];
@@ -1023,6 +1024,10 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx)
 			ath_tx_complete_buf(sc, bf, &bf_head, 0, 0);
 	}
 
+	spin_lock_bh(&txq->axq_lock);
+	txq->axq_tx_inprogress = false;
+	spin_unlock_bh(&txq->axq_lock);
+
 	/* flush any pending frames if aggregation is enabled */
 	if (sc->sc_flags & SC_OP_TXAGGR) {
 		if (!retry_tx) {
@@ -1103,8 +1108,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
 		if (tid->paused)
 			continue;
 
-		if ((txq->axq_depth % 2) == 0)
-			ath_tx_sched_aggr(sc, txq, tid);
+		ath_tx_sched_aggr(sc, txq, tid);
 
 		/*
 		 * add tid to round-robin queue if more frames
@@ -1947,19 +1951,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 		if (bf->bf_stale) {
 			bf_held = bf;
 			if (list_is_last(&bf_held->list, &txq->axq_q)) {
-				txq->axq_link = NULL;
-				txq->axq_linkbuf = NULL;
 				spin_unlock_bh(&txq->axq_lock);
-
-				/*
-				 * The holding descriptor is the last
-				 * descriptor in queue. It's safe to remove
-				 * the last holding descriptor in BH context.
-				 */
-				spin_lock_bh(&sc->tx.txbuflock);
-				list_move_tail(&bf_held->list, &sc->tx.txbuf);
-				spin_unlock_bh(&sc->tx.txbuflock);
-
 				break;
 			} else {
 				bf = list_entry(bf_held->list.next,
@@ -1996,6 +1988,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 			txq->axq_aggr_depth--;
 
 		txok = (ds->ds_txstat.ts_status == 0);
+		txq->axq_tx_inprogress = false;
 		spin_unlock_bh(&txq->axq_lock);
 
 		if (bf_held) {
@@ -2029,6 +2022,40 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
 	}
 }
 
+void ath_tx_complete_poll_work(struct work_struct *work)
+{
+	struct ath_softc *sc = container_of(work, struct ath_softc,
+			tx_complete_work.work);
+	struct ath_txq *txq;
+	int i;
+	bool needreset = false;
+
+	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
+		if (ATH_TXQ_SETUP(sc, i)) {
+			txq = &sc->tx.txq[i];
+			spin_lock_bh(&txq->axq_lock);
+			if (txq->axq_depth) {
+				if (txq->axq_tx_inprogress) {
+					needreset = true;
+					spin_unlock_bh(&txq->axq_lock);
+					break;
+				} else {
+					txq->axq_tx_inprogress = true;
+				}
+			}
+			spin_unlock_bh(&txq->axq_lock);
+		}
+
+	if (needreset) {
+		DPRINTF(sc, ATH_DBG_RESET, "tx hung, resetting the chip\n");
+		ath_reset(sc, false);
+	}
+
+	queue_delayed_work(sc->hw->workqueue, &sc->tx_complete_work,
+			msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT));
+}
+
+
 
 void ath_tx_tasklet(struct ath_softc *sc)
 {
@@ -2069,6 +2096,8 @@ int ath_tx_init(struct ath_softc *sc, int nbufs)
 		goto err;
 	}
 
+	INIT_DELAYED_WORK(&sc->tx_complete_work, ath_tx_complete_poll_work);
+
 err:
 	if (error != 0)
 		ath_tx_cleanup(sc);
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 6/9] ath9k: Remove pointless ath9k_ps_restore() in ath_detach()
From: Luis R. Rodriguez @ 2009-07-15  0:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, ath9k-devel, Vasanthakumar Thiagarajan
In-Reply-To: <1247617035-15402-1-git-send-email-lrodriguez@atheros.com>

From: Vasanthakumar Thiagarajan <vasanth@atheros.com>

Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
---
 drivers/net/wireless/ath/ath9k/main.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 46f4a69..90cecce 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1286,7 +1286,6 @@ void ath_detach(struct ath_softc *sc)
 
 	ath9k_hw_detach(sc->sc_ah);
 	ath9k_exit_debug(sc);
-	ath9k_ps_restore(sc);
 }
 
 static int ath9k_reg_notifier(struct wiphy *wiphy,
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 7/9] ath9k: serialize ath9k_hw_setpower calls
From: Luis R. Rodriguez @ 2009-07-15  0:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, ath9k-devel, Gabor Juhos
In-Reply-To: <1247617035-15402-1-git-send-email-lrodriguez@atheros.com>

From: Gabor Juhos <juhosg@openwrt.org>

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(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 20bf4a7..b37eb59 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -559,6 +559,7 @@ struct ath_softc {
 	spinlock_t sc_resetlock;
 	spinlock_t sc_serial_rw;
 	spinlock_t ani_lock;
+	spinlock_t sc_pm_lock;
 	struct mutex mutex;
 
 	u8 curbssid[ETH_ALEN];
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index a115c8c..6740a6b 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2728,7 +2728,8 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
 	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[] = {
@@ -2762,6 +2763,18 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
 	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.
  *
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 90cecce..d14f8c9 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1316,6 +1316,7 @@ static int ath_init(u16 devid, struct ath_softc *sc)
 	spin_lock_init(&sc->sc_resetlock);
 	spin_lock_init(&sc->sc_serial_rw);
 	spin_lock_init(&sc->ani_lock);
+	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,
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 9/9] ath9k: serialize ath9k_ps_{wakeup,restore} calls
From: Luis R. Rodriguez @ 2009-07-15  0:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, ath9k-devel, Gabor Juhos
In-Reply-To: <1247617035-15402-1-git-send-email-lrodriguez@atheros.com>

From: Gabor Juhos <juhosg@openwrt.org>

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(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 8b38c0a..1576812 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -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;
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 @@ 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);
 }
 
 /*
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 1/9] ath9k: Manipulate and report the correct RSSI
From: Luis R. Rodriguez @ 2009-07-15  0:17 UTC (permalink / raw)
  To: linville
  Cc: linux-wireless, ath9k-devel, Senthil Balasubramanian,
	Vasanthakumar Thiagarajan
In-Reply-To: <1247617035-15402-1-git-send-email-lrodriguez@atheros.com>

From: Senthil Balasubramanian <senthilkumar@atheros.com>

RSSI reported by the RX descriptor requires little manipulation.
Manipulate and report the correct RSSI to the stack. This will
fix the improper signal levels reported by iwconfig iw dev wlanX
station dump. Also the Link Quality reported seems to be varying
(falls to zero also sometimes) when iperf is run from STA to AP.

Also use the default noise floor for now as the one reported
during the caliberation seems to be wrong.

The Signal and Link Quality before this patch (taken while TX is
in progress from STA to AP)

09:59:13.285428037 Link Quality=29/70  Signal level=-81 dBm
09:59:13.410660084 Link Quality=20/70  Signal level=-90 dBm
09:59:13.586864392 Link Quality=21/70  Signal level=-89 dBm
09:59:13.710296281 Link Quality=21/70  Signal level=-89 dBm
09:59:13.821683064 Link Quality=25/70  Signal level=-85 dBm
09:59:13.933402989 Link Quality=24/70  Signal level=-86 dBm
09:59:14.045839276 Link Quality=26/70  Signal level=-84 dBm
09:59:14.193926673 Link Quality=23/70  Signal level=-87 dBm
09:59:14.306230262 Link Quality=31/70  Signal level=-79 dBm
09:59:14.419459667 Link Quality=26/70  Signal level=-84 dBm
09:59:14.530711167 Link Quality=37/70  Signal level=-73 dBm
09:59:14.642593962 Link Quality=29/70  Signal level=-81 dBm
09:59:14.754361169 Link Quality=21/70  Signal level=-89 dBm
09:59:14.866217355 Link Quality=21/70  Signal level=-89 dBm
09:59:14.976963623 Link Quality=28/70  Signal level=-82 dBm
09:59:15.089149809 Link Quality=26/70  Signal level=-84 dBm
09:59:15.205039887 Link Quality=27/70  Signal level=-83 dBm
09:59:15.316368003 Link Quality=23/70  Signal level=-87 dBm
09:59:15.427684036 Link Quality=36/70  Signal level=-74 dBm
09:59:15.539756380 Link Quality=21/70  Signal level=-89 dBm
09:59:15.650549093 Link Quality=22/70  Signal level=-88 dBm
09:59:15.761171672 Link Quality=32/70  Signal level=-78 dBm
09:59:15.872793750 Link Quality=23/70  Signal level=-87 dBm
09:59:15.984421694 Link Quality=22/70  Signal level=-88 dBm
09:59:16.097315093 Link Quality=21/70  Signal level=-89 dBm

The link quality and signal level after this patch (take while
TX is in progress from STA to AP)

17:21:25.627848091 Link Quality=65/70  Signal level=-45 dBm
17:21:25.762805607 Link Quality=65/70  Signal level=-45 dBm
17:21:25.875521888 Link Quality=66/70  Signal level=-44 dBm
17:21:25.987468448 Link Quality=66/70  Signal level=-44 dBm
17:21:26.100628151 Link Quality=66/70  Signal level=-44 dBm
17:21:26.213129671 Link Quality=66/70  Signal level=-44 dBm
17:21:26.324923070 Link Quality=65/70  Signal level=-45 dBm
17:21:26.436831357 Link Quality=65/70  Signal level=-45 dBm
17:21:26.610356973 Link Quality=65/70  Signal level=-45 dBm
17:21:26.723340047 Link Quality=65/70  Signal level=-45 dBm
17:21:26.835715293 Link Quality=64/70  Signal level=-46 dBm
17:21:26.949542748 Link Quality=64/70  Signal level=-46 dBm
17:21:27.062261613 Link Quality=65/70  Signal level=-45 dBm
17:21:27.174511563 Link Quality=64/70  Signal level=-46 dBm
17:21:27.287616232 Link Quality=64/70  Signal level=-46 dBm
17:21:27.400598119 Link Quality=64/70  Signal level=-46 dBm
17:21:27.511381404 Link Quality=64/70  Signal level=-46 dBm
17:21:27.624530421 Link Quality=65/70  Signal level=-45 dBm
17:21:27.737807109 Link Quality=64/70  Signal level=-46 dBm
17:21:27.850861352 Link Quality=65/70  Signal level=-45 dBm
17:21:27.963369436 Link Quality=64/70  Signal level=-46 dBm
17:21:28.076582289 Link Quality=64/70  Signal level=-46 dBm

Signed-off-by: Senthil Balasubramanian <senthilkumar@atheros.com>
Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
---
 drivers/net/wireless/ath/ath9k/ath9k.h |   16 ++++++++++++++++
 drivers/net/wireless/ath/ath9k/calib.c |   13 ++++++++++---
 drivers/net/wireless/ath/ath9k/calib.h |    4 +++-
 drivers/net/wireless/ath/ath9k/main.c  |    1 +
 drivers/net/wireless/ath/ath9k/recv.c  |   25 ++++++++++++++++++++++++-
 5 files changed, 54 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 3afd7ee..544599b 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -290,12 +290,28 @@ struct ath_tx_control {
 #define ATH_TX_XRETRY       0x02
 #define ATH_TX_BAR          0x04
 
+#define ATH_RSSI_LPF_LEN 		10
+#define RSSI_LPF_THRESHOLD		-20
+#define ATH9K_RSSI_BAD			0x80
+#define ATH_RSSI_EP_MULTIPLIER     (1<<7)
+#define ATH_EP_MUL(x, mul)         ((x) * (mul))
+#define ATH_RSSI_IN(x)             (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER))
+#define ATH_LPF_RSSI(x, y, len) \
+    ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define ATH_RSSI_LPF(x, y) do {                     			\
+    if ((y) >= RSSI_LPF_THRESHOLD)                         		\
+	x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN);  	\
+} while (0)
+#define ATH_EP_RND(x, mul) 						\
+	((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
+
 struct ath_node {
 	struct ath_softc *an_sc;
 	struct ath_atx_tid tid[WME_NUM_TID];
 	struct ath_atx_ac ac[WME_NUM_AC];
 	u16 maxampdu;
 	u8 mpdudensity;
+	int last_rssi;
 };
 
 struct ath_tx {
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index a32d7e7..1f0c5fe 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -691,15 +691,22 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
 void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah)
 {
 	int i, j;
+	s16 noise_floor;
+
+	if (AR_SREV_9280(ah))
+		noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE;
+	else if (AR_SREV_9285(ah))
+		noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE;
+	else
+		noise_floor = AR_PHY_CCA_MAX_AR5416_GOOD_VALUE;
 
 	for (i = 0; i < NUM_NF_READINGS; i++) {
 		ah->nfCalHist[i].currIndex = 0;
-		ah->nfCalHist[i].privNF = AR_PHY_CCA_MAX_GOOD_VALUE;
+		ah->nfCalHist[i].privNF = noise_floor;
 		ah->nfCalHist[i].invalidNFcount =
 			AR_PHY_CCA_FILTERWINDOW_LENGTH;
 		for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
-			ah->nfCalHist[i].nfCalBuffer[j] =
-				AR_PHY_CCA_MAX_GOOD_VALUE;
+			ah->nfCalHist[i].nfCalBuffer[j] = noise_floor;
 		}
 	}
 }
diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h
index fe5367f..547e697 100644
--- a/drivers/net/wireless/ath/ath9k/calib.h
+++ b/drivers/net/wireless/ath/ath9k/calib.h
@@ -25,7 +25,9 @@ extern const struct ath9k_percal_data adc_dc_cal_multi_sample;
 extern const struct ath9k_percal_data adc_dc_cal_single_sample;
 extern const struct ath9k_percal_data adc_init_dc_cal;
 
-#define AR_PHY_CCA_MAX_GOOD_VALUE      		-85
+#define AR_PHY_CCA_MAX_AR5416_GOOD_VALUE	-85
+#define AR_PHY_CCA_MAX_AR9280_GOOD_VALUE	-112
+#define AR_PHY_CCA_MAX_AR9285_GOOD_VALUE	-118
 #define AR_PHY_CCA_MAX_HIGH_VALUE      		-62
 #define AR_PHY_CCA_MIN_BAD_VALUE       		-140
 #define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT     3
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 19df788..2ad7184 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -465,6 +465,7 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
 		an->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
 				     sta->ht_cap.ampdu_factor);
 		an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density);
+		an->last_rssi = ATH_RSSI_DUMMY_MARKER;
 	}
 }
 
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index b3da81d..61edfab 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -145,6 +145,10 @@ static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds,
 	u8 ratecode;
 	__le16 fc;
 	struct ieee80211_hw *hw;
+	struct ieee80211_sta *sta;
+	struct ath_node *an;
+	int last_rssi = ATH_RSSI_DUMMY_MARKER;
+
 
 	hdr = (struct ieee80211_hdr *)skb->data;
 	fc = hdr->frame_control;
@@ -229,11 +233,30 @@ static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds,
 		}
 	}
 
+	rcu_read_lock();
+	sta = ieee80211_find_sta(sc->hw, hdr->addr2);
+	if (sta) {
+		an = (struct ath_node *) sta->drv_priv;
+		if (ds->ds_rxstat.rs_rssi != ATH9K_RSSI_BAD &&
+		   !ds->ds_rxstat.rs_moreaggr)
+			ATH_RSSI_LPF(an->last_rssi, ds->ds_rxstat.rs_rssi);
+		last_rssi = an->last_rssi;
+	}
+	rcu_read_unlock();
+
+	if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
+		ds->ds_rxstat.rs_rssi = ATH_EP_RND(last_rssi,
+					ATH_RSSI_EP_MULTIPLIER);
+	if (ds->ds_rxstat.rs_rssi < 0)
+		ds->ds_rxstat.rs_rssi = 0;
+	else if (ds->ds_rxstat.rs_rssi > 127)
+		ds->ds_rxstat.rs_rssi = 127;
+
 	rx_status->mactime = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp);
 	rx_status->band = hw->conf.channel->band;
 	rx_status->freq = hw->conf.channel->center_freq;
 	rx_status->noise = sc->ani.noise_floor;
-	rx_status->signal = rx_status->noise + ds->ds_rxstat.rs_rssi;
+	rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + ds->ds_rxstat.rs_rssi;
 	rx_status->antenna = ds->ds_rxstat.rs_antenna;
 
 	/*
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 5/9] ath9k: Handle tx desc shortage more appropriately
From: Luis R. Rodriguez @ 2009-07-15  0:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, ath9k-devel, Vasanthakumar Thiagarajan
In-Reply-To: <1247617035-15402-1-git-send-email-lrodriguez@atheros.com>

From: Vasanthakumar Thiagarajan <vasanth@atheros.com>

Update tx BA window and complete the frame as failed
one if we can't clone the holding descriptor due to
unavailability of descriptors.

Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
---
 drivers/net/wireless/ath/ath9k/xmit.c |   18 +++++++++++++++++-
 1 files changed, 17 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 24663ce..4ff155e 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -382,8 +382,24 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
 				struct ath_buf *tbf;
 
 				tbf = ath_clone_txbuf(sc, bf_last);
-				if (!tbf)
+				/*
+				 * Update tx baw and complete the frame with
+				 * failed status if we run out of tx buf
+				 */
+				if (!tbf) {
+					spin_lock_bh(&txq->axq_lock);
+					ath_tx_update_baw(sc, tid,
+							  bf->bf_seqno);
+					spin_unlock_bh(&txq->axq_lock);
+
+					bf->bf_state.bf_type |= BUF_XRETRY;
+					ath_tx_rc_status(bf, ds, nbad,
+							 0, false);
+					ath_tx_complete_buf(sc, bf, &bf_head,
+							    0, 0);
 					break;
+				}
+
 				ath9k_hw_cleartxdesc(sc->sc_ah, tbf->bf_desc);
 				list_add_tail(&tbf->list, &bf_head);
 			} else {
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 4/9] ath9k: Remove bogus assert in ath_clone_txbuf()
From: Luis R. Rodriguez @ 2009-07-15  0:17 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, ath9k-devel, Vasanthakumar Thiagarajan
In-Reply-To: <1247617035-15402-1-git-send-email-lrodriguez@atheros.com>

From: Vasanthakumar Thiagarajan <vasanth@atheros.com>

oops, this one should be part of the original patch
"ath9k: downgrade assert in ath_clone_txbuf()"

Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
---
 drivers/net/wireless/ath/ath9k/xmit.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index a3bc431..24663ce 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -242,7 +242,6 @@ static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf)
 		spin_unlock_bh(&sc->tx.txbuflock);
 		return NULL;
 	}
-	ASSERT(!list_empty((&sc->tx.txbuf)));
 	tbf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list);
 	list_del(&tbf->list);
 	spin_unlock_bh(&sc->tx.txbuflock);
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 07/21] ath9k: remove pointless wrapper ath_rc_rate_getidx()
From: Luis R. Rodriguez @ 2009-07-15  0:13 UTC (permalink / raw)
  To: linville, johannes
  Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez, Derek Smithies,
	Chittajit Mitra
In-Reply-To: <1247616853-11221-1-git-send-email-lrodriguez@atheros.com>

This is just calling another helper, so just use the other
helper directly. This should make it clear that when do not
find the next rate we stick to the current one.

Cc: Derek Smithies <derek@indranet.co.nz>
Cc: Chittajit Mitra <Chittajit.Mitra@Atheros.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/ath/ath9k/rc.c |   21 ++++-----------------
 1 files changed, 4 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index 64cb697..03e7df4 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -814,19 +814,6 @@ static void ath_rc_rate_set_rtscts(struct ath_softc *sc,
 	tx_info->control.rts_cts_rate_idx = cix;
 }
 
-static u8 ath_rc_rate_getidx(struct ath_softc *sc,
-			     struct ath_rate_priv *ath_rc_priv,
-			     const struct ath_rate_table *rate_table,
-			     u8 rix)
-{
-	u8 nextindex = 0;
-	if (ath_rc_get_nextlowervalid_txrate(rate_table,
-					     ath_rc_priv, rix, &nextindex))
-		return nextindex;
-	else
-		return rix;
-}
-
 static void ath_rc_ratefind(struct ath_softc *sc,
 			    struct ath_rate_priv *ath_rc_priv,
 			    struct ieee80211_tx_rate_control *txrc)
@@ -865,8 +852,8 @@ static void ath_rc_ratefind(struct ath_softc *sc,
 		/* Get the next tried/allowed rate. No RTS for the next series
 		 * after the probe rate
 		 */
-		nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
-					  rate_table, nrix);
+		ath_rc_get_nextlowervalid_txrate(rate_table, ath_rc_priv,
+						 rix, &nrix);
 		ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
 				       try_per_rate, nrix, 0);
 
@@ -883,8 +870,8 @@ static void ath_rc_ratefind(struct ath_softc *sc,
 		if (i + 1 == 4)
 			try_per_rate = 4;
 
-		nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
-					  rate_table, nrix);
+		ath_rc_get_nextlowervalid_txrate(rate_table, ath_rc_priv,
+						 rix, &nrix);
 		/* All other rates in the series have RTS enabled */
 		ath_rc_rate_set_series(rate_table, &rates[i], txrc,
 				       try_per_rate, nrix, 1);
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 06/21] ath9k: remove unused stepdown when looking for the next rate
From: Luis R. Rodriguez @ 2009-07-15  0:13 UTC (permalink / raw)
  To: linville, johannes
  Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez, Derek Smithies,
	Chittajit Mitra
In-Reply-To: <1247616853-11221-1-git-send-email-lrodriguez@atheros.com>

This is not used, remove this.

Cc: Derek Smithies <derek@indranet.co.nz>
Cc: Chittajit Mitra <Chittajit.Mitra@Atheros.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/ath/ath9k/rc.c |   21 ++++++++-------------
 1 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index 7b37b27..64cb697 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -817,19 +817,14 @@ static void ath_rc_rate_set_rtscts(struct ath_softc *sc,
 static u8 ath_rc_rate_getidx(struct ath_softc *sc,
 			     struct ath_rate_priv *ath_rc_priv,
 			     const struct ath_rate_table *rate_table,
-			     u8 rix, u16 stepdown)
+			     u8 rix)
 {
-	u32 j;
 	u8 nextindex = 0;
-
-	for (j = stepdown; j > 0; j--) {
-		if (ath_rc_get_nextlowervalid_txrate(rate_table,
-					ath_rc_priv, rix, &nextindex))
-			rix = nextindex;
-		else
-			break;
-	}
-	return rix;
+	if (ath_rc_get_nextlowervalid_txrate(rate_table,
+					     ath_rc_priv, rix, &nextindex))
+		return nextindex;
+	else
+		return rix;
 }
 
 static void ath_rc_ratefind(struct ath_softc *sc,
@@ -871,7 +866,7 @@ static void ath_rc_ratefind(struct ath_softc *sc,
 		 * after the probe rate
 		 */
 		nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
-					  rate_table, nrix, 1);
+					  rate_table, nrix);
 		ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
 				       try_per_rate, nrix, 0);
 
@@ -889,7 +884,7 @@ static void ath_rc_ratefind(struct ath_softc *sc,
 			try_per_rate = 4;
 
 		nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
-					  rate_table, nrix, 1);
+					  rate_table, nrix);
 		/* All other rates in the series have RTS enabled */
 		ath_rc_rate_set_series(rate_table, &rates[i], txrc,
 				       try_per_rate, nrix, 1);
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 05/21] ath9k: remove unused min rate calculation code
From: Luis R. Rodriguez @ 2009-07-15  0:13 UTC (permalink / raw)
  To: linville, johannes
  Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez, Derek Smithies,
	Chittajit Mitra
In-Reply-To: <1247616853-11221-1-git-send-email-lrodriguez@atheros.com>

This is not used, and when we need to get the lowest rate
we should simply use mac80211's own rate_lowest_index(sband, sta).

Cc: Derek Smithies <derek@indranet.co.nz>
Cc: Chittajit Mitra <Chittajit.Mitra@Atheros.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/ath/ath9k/rc.c |   33 +++++++++------------------------
 1 files changed, 9 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index a23b66b..7b37b27 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -817,28 +817,17 @@ static void ath_rc_rate_set_rtscts(struct ath_softc *sc,
 static u8 ath_rc_rate_getidx(struct ath_softc *sc,
 			     struct ath_rate_priv *ath_rc_priv,
 			     const struct ath_rate_table *rate_table,
-			     u8 rix, u16 stepdown,
-			     u16 min_rate)
+			     u8 rix, u16 stepdown)
 {
 	u32 j;
 	u8 nextindex = 0;
 
-	if (min_rate) {
-		for (j = RATE_TABLE_SIZE; j > 0; j--) {
-			if (ath_rc_get_nextlowervalid_txrate(rate_table,
-						ath_rc_priv, rix, &nextindex))
-				rix = nextindex;
-			else
-				break;
-		}
-	} else {
-		for (j = stepdown; j > 0; j--) {
-			if (ath_rc_get_nextlowervalid_txrate(rate_table,
-						ath_rc_priv, rix, &nextindex))
-				rix = nextindex;
-			else
-				break;
-		}
+	for (j = stepdown; j > 0; j--) {
+		if (ath_rc_get_nextlowervalid_txrate(rate_table,
+					ath_rc_priv, rix, &nextindex))
+			rix = nextindex;
+		else
+			break;
 	}
 	return rix;
 }
@@ -882,7 +871,7 @@ static void ath_rc_ratefind(struct ath_softc *sc,
 		 * after the probe rate
 		 */
 		nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
-					  rate_table, nrix, 1, 0);
+					  rate_table, nrix, 1);
 		ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
 				       try_per_rate, nrix, 0);
 
@@ -895,16 +884,12 @@ static void ath_rc_ratefind(struct ath_softc *sc,
 
 	/* Fill in the other rates for multirate retry */
 	for ( ; i < 4; i++) {
-		u8 min_rate;
-
 		/* Use twice the number of tries for the last MRR segment. */
 		if (i + 1 == 4)
 			try_per_rate = 4;
 
-		min_rate = (((i + 1) == 4) && 0);
-
 		nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
-					  rate_table, nrix, 1, min_rate);
+					  rate_table, nrix, 1);
 		/* All other rates in the series have RTS enabled */
 		ath_rc_rate_set_series(rate_table, &rates[i], txrc,
 				       try_per_rate, nrix, 1);
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 04/21] ath9k: cleanup try count for MRR in rate control
From: Luis R. Rodriguez @ 2009-07-15  0:13 UTC (permalink / raw)
  To: linville, johannes
  Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez, Derek Smithies,
	Chittajit Mitra
In-Reply-To: <1247616853-11221-1-git-send-email-lrodriguez@atheros.com>

This has no functional change and just cleans up the code
to be more legible and removes a useless variable for
Multi Rate Retry.

For regular frames we use 2 retries for MRR segments [0-2].
For the last MRR segment [3] we use 4.

MRR[0] = 2
MRR[1] = 2
MRR[2] = 2
MRR[3] = 4

Cc: Derek Smithies <derek@indranet.co.nz>
Cc: Chittajit Mitra <Chittajit.Mitra@Atheros.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/ath/ath9k/ath9k.h |    1 -
 drivers/net/wireless/ath/ath9k/main.c  |    3 ++-
 drivers/net/wireless/ath/ath9k/rc.c    |   29 ++++++++++++++++++++---------
 3 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index eb9d522..3afd7ee 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -164,7 +164,6 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd,
 #define WME_NUM_TID             16
 #define ATH_TXBUF               512
 #define ATH_TXMAXTRY            13
-#define ATH_11N_TXMAXTRY        10
 #define ATH_MGT_TXMAXTRY        4
 #define WME_BA_BMP_SIZE         64
 #define WME_MAX_BA              WME_BA_BMP_SIZE
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 961b0ce..19df788 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1540,7 +1540,8 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 	hw->max_rates = 4;
 	hw->channel_change_time = 5000;
 	hw->max_listen_interval = 10;
-	hw->max_rate_tries = ATH_11N_TXMAXTRY;
+	/* Hardware supports 10 but we use 4 */
+	hw->max_rate_tries = 4;
 	hw->sta_data_size = sizeof(struct ath_node);
 	hw->vif_data_size = sizeof(struct ath_vif);
 
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index d7f4030..a23b66b 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -853,9 +853,21 @@ static void ath_rc_ratefind(struct ath_softc *sc,
 	struct ieee80211_tx_rate *rates = tx_info->control.rates;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	__le16 fc = hdr->frame_control;
-	u8 try_per_rate = 0, i = 0, rix, nrix;
+	u8 try_per_rate, i = 0, rix, nrix;
 	int is_probe = 0;
 
+	/*
+	 * For Multi Rate Retry we use a different number of
+	 * retry attempt counts. This ends up looking like this:
+	 *
+	 * MRR[0] = 2
+	 * MRR[1] = 2
+	 * MRR[2] = 2
+	 * MRR[3] = 4
+	 *
+	 */
+	try_per_rate = sc->hw->max_rate_tries;
+
 	rate_table = sc->cur_rate_table;
 	rix = ath_rc_ratefind_ht(sc, ath_rc_priv, rate_table, &is_probe);
 	nrix = rix;
@@ -866,7 +878,6 @@ static void ath_rc_ratefind(struct ath_softc *sc,
 		ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
 				       1, nrix, 0);
 
-		try_per_rate = (ATH_11N_TXMAXTRY/4);
 		/* Get the next tried/allowed rate. No RTS for the next series
 		 * after the probe rate
 		 */
@@ -877,7 +888,6 @@ static void ath_rc_ratefind(struct ath_softc *sc,
 
 		tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
 	} else {
-		try_per_rate = (ATH_11N_TXMAXTRY/4);
 		/* Set the choosen rate. No RTS for first series entry. */
 		ath_rc_rate_set_series(rate_table, &rates[i++], txrc,
 				       try_per_rate, nrix, 0);
@@ -885,18 +895,19 @@ static void ath_rc_ratefind(struct ath_softc *sc,
 
 	/* Fill in the other rates for multirate retry */
 	for ( ; i < 4; i++) {
-		u8 try_num;
 		u8 min_rate;
 
-		try_num = ((i + 1) == 4) ?
-			ATH_11N_TXMAXTRY - (try_per_rate * i) : try_per_rate ;
+		/* Use twice the number of tries for the last MRR segment. */
+		if (i + 1 == 4)
+			try_per_rate = 4;
+
 		min_rate = (((i + 1) == 4) && 0);
 
 		nrix = ath_rc_rate_getidx(sc, ath_rc_priv,
 					  rate_table, nrix, 1, min_rate);
 		/* All other rates in the series have RTS enabled */
 		ath_rc_rate_set_series(rate_table, &rates[i], txrc,
-				       try_num, nrix, 1);
+				       try_per_rate, nrix, 1);
 	}
 
 	/*
@@ -1529,7 +1540,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
 	/*
 	 * If underrun error is seen assume it as an excessive retry only
 	 * if prefetch trigger level have reached the max (0x3f for 5416)
-	 * Adjust the long retry as if the frame was tried ATH_11N_TXMAXTRY
+	 * Adjust the long retry as if the frame was tried hw->max_rate_tries
 	 * times. This affects how ratectrl updates PER for the failed rate.
 	 */
 	if (tx_info_priv->tx.ts_flags &
@@ -1544,7 +1555,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband,
 		tx_status = 1;
 
 	ath_rc_tx_status(sc, ath_rc_priv, tx_info, final_ts_idx, tx_status,
-			 (is_underrun) ? ATH_11N_TXMAXTRY :
+			 (is_underrun) ? sc->hw->max_rate_tries :
 			 tx_info_priv->tx.ts_longretry);
 
 	/* Check if aggregation has to be enabled for this tid */
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 19/21] ath9k: Remove unused members from rate control structure
From: Luis R. Rodriguez @ 2009-07-15  0:14 UTC (permalink / raw)
  To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Vasanthakumar Thiagarajan
In-Reply-To: <1247616853-11221-1-git-send-email-lrodriguez@atheros.com>

From: Vasanthakumar Thiagarajan <vasanth@atheros.com>

Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
---
 drivers/net/wireless/ath/ath9k/rc.c |  216 +++++++++++++++++-----------------
 drivers/net/wireless/ath/ath9k/rc.h |   21 ----
 2 files changed, 108 insertions(+), 129 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index a39b1a7..731967c 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -22,130 +22,130 @@ static const struct ath_rate_table ar5416_11na_ratetable = {
 	{
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
 			5400, 0x0b, 0x00, 12,
-			0, 2, 1, 0, 0, 0, 0, 0 },
+			0, 0, 0, 0, 0, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
 			7800,  0x0f, 0x00, 18,
-			0, 3, 1, 1, 1, 1, 1, 0 },
+			0, 1, 1, 1, 1, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
 			10000, 0x0a, 0x00, 24,
-			2, 4, 2, 2, 2, 2, 2, 0 },
+			2, 2, 2, 2, 2, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
 			13900, 0x0e, 0x00, 36,
-			2, 6,  2, 3, 3, 3, 3, 0 },
+			2,  3, 3, 3, 3, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
 			17300, 0x09, 0x00, 48,
-			4, 10, 3, 4, 4, 4, 4, 0 },
+			4,  4, 4, 4, 4, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
 			23000, 0x0d, 0x00, 72,
-			4, 14, 3, 5, 5, 5, 5, 0 },
+			4,  5, 5, 5, 5, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
 			27400, 0x08, 0x00, 96,
-			4, 20, 3, 6, 6, 6, 6, 0 },
+			4,  6, 6, 6, 6, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
 			29300, 0x0c, 0x00, 108,
-			4, 23, 3, 7, 7, 7, 7, 0 },
+			4,  7, 7, 7, 7, 0 },
 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */
 			6400, 0x80, 0x00, 0,
-			0, 2, 3, 8, 24, 8, 24, 3216 },
+			0, 8, 24, 8, 24, 3216 },
 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */
 			12700, 0x81, 0x00, 1,
-			2, 4, 3, 9, 25, 9, 25, 6434 },
+			2, 9, 25, 9, 25, 6434 },
 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */
 			18800, 0x82, 0x00, 2,
-			2, 6, 3, 10, 26, 10, 26, 9650 },
+			2, 10, 26, 10, 26, 9650 },
 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */
 			25000, 0x83, 0x00, 3,
-			4, 10, 3, 11, 27, 11, 27, 12868 },
+			4,  11, 27, 11, 27, 12868 },
 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */
 			36700, 0x84, 0x00, 4,
-			4, 14, 3, 12, 28, 12, 28, 19304 },
+			4,  12, 28, 12, 28, 19304 },
 		{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */
 			48100, 0x85, 0x00, 5,
-			4, 20, 3, 13, 29, 13, 29, 25740 },
+			4,  13, 29, 13, 29, 25740 },
 		{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */
 			53500, 0x86, 0x00, 6,
-			4, 23, 3, 14, 30, 14, 30,  28956 },
+			4,  14, 30, 14, 30,  28956 },
 		{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */
 			59000, 0x87, 0x00, 7,
-			4, 25, 3, 15, 31, 15, 32, 32180 },
+			4,  15, 31, 15, 32, 32180 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */
 			12700, 0x88, 0x00,
-			8, 0, 2, 3, 16, 33, 16, 33, 6430 },
+			8, 3, 16, 33, 16, 33, 6430 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */
 			24800, 0x89, 0x00, 9,
-			2, 4, 3, 17, 34, 17, 34, 12860 },
+			2, 17, 34, 17, 34, 12860 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */
 			36600, 0x8a, 0x00, 10,
-			2, 6, 3, 18, 35, 18, 35, 19300 },
+			2, 18, 35, 18, 35, 19300 },
 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */
 			48100, 0x8b, 0x00, 11,
-			4, 10, 3, 19, 36, 19, 36, 25736 },
+			4,  19, 36, 19, 36, 25736 },
 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */
 			69500, 0x8c, 0x00, 12,
-			4, 14, 3, 20, 37, 20, 37, 38600 },
+			4,  20, 37, 20, 37, 38600 },
 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */
 			89500, 0x8d, 0x00, 13,
-			4, 20, 3, 21, 38, 21, 38, 51472 },
+			4,  21, 38, 21, 38, 51472 },
 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */
 			98900, 0x8e, 0x00, 14,
-			4, 23, 3, 22, 39, 22, 39, 57890 },
+			4,  22, 39, 22, 39, 57890 },
 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */
 			108300, 0x8f, 0x00, 15,
-			4, 25, 3, 23, 40, 23, 41, 64320 },
+			4,  23, 40, 23, 41, 64320 },
 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */
 			13200, 0x80, 0x00, 0,
-			0, 2, 3, 8, 24, 24, 24, 6684 },
+			0, 8, 24, 24, 24, 6684 },
 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */
 			25900, 0x81, 0x00, 1,
-			2, 4, 3, 9, 25, 25, 25, 13368 },
+			2, 9, 25, 25, 25, 13368 },
 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */
 			38600, 0x82, 0x00, 2,
-			2, 6, 3, 10, 26, 26, 26, 20052 },
+			2, 10, 26, 26, 26, 20052 },
 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */
 			49800, 0x83, 0x00, 3,
-			4, 10, 3, 11, 27, 27, 27, 26738 },
+			4,  11, 27, 27, 27, 26738 },
 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */
 			72200, 0x84, 0x00, 4,
-			4, 14, 3, 12, 28, 28, 28, 40104 },
+			4,  12, 28, 28, 28, 40104 },
 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */
 			92900, 0x85, 0x00, 5,
-			4, 20, 3, 13, 29, 29, 29, 53476 },
+			4,  13, 29, 29, 29, 53476 },
 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */
 			102700, 0x86, 0x00, 6,
-			4, 23, 3, 14, 30, 30, 30, 60156 },
+			4,  14, 30, 30, 30, 60156 },
 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */
 			112000, 0x87, 0x00, 7,
-			4, 25, 3, 15, 31, 32, 32, 66840 },
+			4,  15, 31, 32, 32, 66840 },
 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */
 			122000, 0x87, 0x00, 7,
-			4, 25, 3, 15, 31, 32, 32, 74200 },
+			4,  15, 31, 32, 32, 74200 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */
 			25800, 0x88, 0x00, 8,
-			0, 2, 3, 16, 33, 33, 33, 13360 },
+			0, 16, 33, 33, 33, 13360 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */
 			49800, 0x89, 0x00, 9,
-			2, 4, 3, 17, 34, 34, 34, 26720 },
+			2, 17, 34, 34, 34, 26720 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */
 			71900, 0x8a, 0x00, 10,
-			2, 6, 3, 18, 35, 35, 35, 40080 },
+			2, 18, 35, 35, 35, 40080 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */
 			92500, 0x8b, 0x00, 11,
-			4, 10, 3, 19, 36, 36, 36, 53440 },
+			4,  19, 36, 36, 36, 53440 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */
 			130300, 0x8c, 0x00, 12,
-			4, 14, 3, 20, 37, 37, 37, 80160 },
+			4,  20, 37, 37, 37, 80160 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */
 			162800, 0x8d, 0x00, 13,
-			4, 20, 3, 21, 38, 38, 38, 106880 },
+			4,  21, 38, 38, 38, 106880 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */
 			178200, 0x8e, 0x00, 14,
-			4, 23, 3, 22, 39, 39, 39, 120240 },
+			4,  22, 39, 39, 39, 120240 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */
 			192100, 0x8f, 0x00, 15,
-			4, 25, 3, 23, 40, 41, 41, 133600 },
+			4,  23, 40, 41, 41, 133600 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */
 			207000, 0x8f, 0x00, 15,
-			4, 25, 3, 23, 40, 41, 41, 148400 },
+			4,  23, 40, 41, 41, 148400 },
 	},
 	50,  /* probe interval */
 	50,  /* rssi reduce interval */
@@ -160,142 +160,142 @@ static const struct ath_rate_table ar5416_11ng_ratetable = {
 	{
 		{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
 			900, 0x1b, 0x00, 2,
-			0, 0, 1, 0, 0, 0, 0, 0 },
+			0, 0, 0, 0, 0, 0 },
 		{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
 			1900, 0x1a, 0x04, 4,
-			1, 1, 1, 1, 1, 1, 1, 0 },
+			1, 1, 1, 1, 1, 0 },
 		{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
 			4900, 0x19, 0x04, 11,
-			2, 2, 2, 2, 2, 2, 2, 0 },
+			2, 2, 2, 2, 2, 0 },
 		{ VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
 			8100, 0x18, 0x04, 22,
-			3, 3, 2, 3, 3, 3, 3, 0 },
+			3, 3, 3, 3, 3, 0 },
 		{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
 			5400, 0x0b, 0x00, 12,
-			4, 2, 1, 4, 4, 4, 4, 0 },
+			4, 4, 4, 4, 4, 0 },
 		{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
 			7800, 0x0f, 0x00, 18,
-			4, 3, 1, 5, 5, 5, 5, 0 },
+			4, 5, 5, 5, 5, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
 			10100, 0x0a, 0x00, 24,
-			6, 4, 1, 6, 6, 6, 6, 0 },
+			6, 6, 6, 6, 6, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
 			14100,  0x0e, 0x00, 36,
-			6, 6, 2, 7, 7, 7, 7, 0 },
+			6, 7, 7, 7, 7, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
 			17700, 0x09, 0x00, 48,
-			8, 10, 3, 8, 8, 8, 8, 0 },
+			8,  8, 8, 8, 8, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
 			23700, 0x0d, 0x00, 72,
-			8, 14, 3, 9, 9, 9, 9, 0 },
+			8,  9, 9, 9, 9, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
 			27400, 0x08, 0x00, 96,
-			8, 20, 3, 10, 10, 10, 10, 0 },
+			8,  10, 10, 10, 10, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
 			30900, 0x0c, 0x00, 108,
-			8, 23, 3, 11, 11, 11, 11, 0 },
+			8,  11, 11, 11, 11, 0 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */
 			6400, 0x80, 0x00, 0,
-			4, 2, 3, 12, 28, 12, 28, 3216 },
+			4, 12, 28, 12, 28, 3216 },
 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */
 			12700, 0x81, 0x00, 1,
-			6, 4, 3, 13, 29, 13, 29, 6434 },
+			6, 13, 29, 13, 29, 6434 },
 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */
 			18800, 0x82, 0x00, 2,
-			6, 6, 3, 14, 30, 14, 30, 9650 },
+			6, 14, 30, 14, 30, 9650 },
 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */
 			25000, 0x83, 0x00, 3,
-			8, 10, 3, 15, 31, 15, 31, 12868 },
+			8,  15, 31, 15, 31, 12868 },
 		{ VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */
 			36700, 0x84, 0x00, 4,
-			8, 14, 3, 16, 32, 16, 32, 19304 },
+			8,  16, 32, 16, 32, 19304 },
 		{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */
 			48100, 0x85, 0x00, 5,
-			8, 20, 3, 17, 33, 17, 33, 25740 },
+			8,  17, 33, 17, 33, 25740 },
 		{ INVALID,  VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */
 			53500, 0x86, 0x00, 6,
-			8, 23, 3, 18, 34, 18, 34, 28956 },
+			8,  18, 34, 18, 34, 28956 },
 		{ INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */
 			59000, 0x87, 0x00, 7,
-			8, 25, 3, 19, 35, 19, 36, 32180 },
+			8,  19, 35, 19, 36, 32180 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */
 			12700, 0x88, 0x00, 8,
-			4, 2, 3, 20, 37, 20, 37, 6430 },
+			4, 20, 37, 20, 37, 6430 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */
 			24800, 0x89, 0x00, 9,
-			6, 4, 3, 21, 38, 21, 38, 12860 },
+			6, 21, 38, 21, 38, 12860 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */
 			36600, 0x8a, 0x00, 10,
-			6, 6, 3, 22, 39, 22, 39, 19300 },
+			6, 22, 39, 22, 39, 19300 },
 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */
 			48100, 0x8b, 0x00, 11,
-			8, 10, 3, 23, 40, 23, 40, 25736 },
+			8,  23, 40, 23, 40, 25736 },
 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */
 			69500, 0x8c, 0x00, 12,
-			8, 14, 3, 24, 41, 24, 41, 38600 },
+			8,  24, 41, 24, 41, 38600 },
 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */
 			89500, 0x8d, 0x00, 13,
-			8, 20, 3, 25, 42, 25, 42, 51472 },
+			8,  25, 42, 25, 42, 51472 },
 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */
 			98900, 0x8e, 0x00, 14,
-			8, 23, 3, 26, 43, 26, 44, 57890 },
+			8,  26, 43, 26, 44, 57890 },
 		{ VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */
 			108300, 0x8f, 0x00, 15,
-			8, 25, 3, 27, 44, 27, 45, 64320 },
+			8,  27, 44, 27, 45, 64320 },
 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */
 			13200, 0x80, 0x00, 0,
-			8, 2, 3, 12, 28, 28, 28, 6684 },
+			8, 12, 28, 28, 28, 6684 },
 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */
 			25900, 0x81, 0x00, 1,
-			8, 4, 3, 13, 29, 29, 29, 13368 },
+			8, 13, 29, 29, 29, 13368 },
 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */
 			38600, 0x82, 0x00, 2,
-			8, 6, 3, 14, 30, 30, 30, 20052 },
+			8, 14, 30, 30, 30, 20052 },
 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */
 			49800, 0x83, 0x00, 3,
-			8, 10, 3, 15, 31, 31, 31, 26738 },
+			8,  15, 31, 31, 31, 26738 },
 		{ VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */
 			72200, 0x84, 0x00, 4,
-			8, 14, 3, 16, 32, 32, 32, 40104 },
+			8,  16, 32, 32, 32, 40104 },
 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */
 			92900, 0x85, 0x00, 5,
-			8, 20, 3, 17, 33, 33, 33, 53476 },
+			8,  17, 33, 33, 33, 53476 },
 		{ INVALID,  VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */
 			102700, 0x86, 0x00, 6,
-			8, 23, 3, 18, 34, 34, 34, 60156 },
+			8,  18, 34, 34, 34, 60156 },
 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */
 			112000, 0x87, 0x00, 7,
-			8, 23, 3, 19, 35, 36, 36, 66840 },
+			8,  19, 35, 36, 36, 66840 },
 		{ INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */
 			122000, 0x87, 0x00, 7,
-			8, 25, 3, 19, 35, 36, 36, 74200 },
+			8,  19, 35, 36, 36, 74200 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */
 			25800, 0x88, 0x00, 8,
-			8, 2, 3, 20, 37, 37, 37, 13360 },
+			8, 20, 37, 37, 37, 13360 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */
 			49800, 0x89, 0x00, 9,
-			8, 4, 3, 21, 38, 38, 38, 26720 },
+			8, 21, 38, 38, 38, 26720 },
 		{ INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */
 			71900, 0x8a, 0x00, 10,
-			8, 6, 3, 22, 39, 39, 39, 40080 },
+			8, 22, 39, 39, 39, 40080 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */
 			92500, 0x8b, 0x00, 11,
-			8, 10, 3, 23, 40, 40, 40, 53440 },
+			8,  23, 40, 40, 40, 53440 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */
 			130300, 0x8c, 0x00, 12,
-			8, 14, 3, 24, 41, 41, 41, 80160 },
+			8,  24, 41, 41, 41, 80160 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */
 			162800, 0x8d, 0x00, 13,
-			8, 20, 3, 25, 42, 42, 42, 106880 },
+			8,  25, 42, 42, 42, 106880 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */
 			178200, 0x8e, 0x00, 14,
-			8, 23, 3, 26, 43, 43, 43, 120240 },
+			8,  26, 43, 43, 43, 120240 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */
 			192100, 0x8f, 0x00, 15,
-			8, 23, 3, 27, 44, 45, 45, 133600 },
+			8,  27, 44, 45, 45, 133600 },
 		{ VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */
 			207000, 0x8f, 0x00, 15,
-			8, 25, 3, 27, 44, 45, 45, 148400 },
+			8,  27, 44, 45, 45, 148400 },
 		},
 	50,  /* probe interval */
 	50,  /* rssi reduce interval */
@@ -307,28 +307,28 @@ static const struct ath_rate_table ar5416_11a_ratetable = {
 	{
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
 			5400, 0x0b, 0x00, (0x80|12),
-			0, 2, 1, 0, 0 },
+			0, 0, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
 			7800, 0x0f, 0x00, 18,
-			0, 3, 1, 1, 0 },
+			0, 1, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
 			10000, 0x0a, 0x00, (0x80|24),
-			2, 4, 2, 2, 0 },
+			2, 2, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
 			13900, 0x0e, 0x00, 36,
-			2, 6, 2, 3, 0 },
+			2, 3, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
 			17300, 0x09, 0x00, (0x80|48),
-			4, 10, 3, 4, 0 },
+			4,  4, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
 			23000, 0x0d, 0x00, 72,
-			4, 14, 3, 5, 0 },
+			4,  5, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
 			27400, 0x08, 0x00, 96,
-			4, 19, 3, 6, 0 },
+			4,  6, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
 			29300, 0x0c, 0x00, 108,
-			4, 23, 3, 7, 0 },
+			4,  7, 0 },
 	},
 	50,  /* probe interval */
 	50,  /* rssi reduce interval */
@@ -340,40 +340,40 @@ static const struct ath_rate_table ar5416_11g_ratetable = {
 	{
 		{ VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
 			900, 0x1b, 0x00, 2,
-			0, 0, 1, 0, 0 },
+			0, 0, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
 			1900, 0x1a, 0x04, 4,
-			1, 1, 1, 1, 0 },
+			1, 1, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
 			4900, 0x19, 0x04, 11,
-			2, 2, 2, 2, 0 },
+			2, 2, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
 			8100, 0x18, 0x04, 22,
-			3, 3, 2, 3, 0 },
+			3, 3, 0 },
 		{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
 			5400, 0x0b, 0x00, 12,
-			4, 2, 1, 4, 0 },
+			4, 4, 0 },
 		{ INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
 			7800, 0x0f, 0x00, 18,
-			4, 3, 1, 5, 0 },
+			4, 5, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
 			10000, 0x0a, 0x00, 24,
-			6, 4, 1, 6, 0 },
+			6, 6, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
 			13900, 0x0e, 0x00, 36,
-			6, 6, 2, 7, 0 },
+			6, 7, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
 			17300, 0x09, 0x00, 48,
-			8, 10, 3, 8, 0 },
+			8,  8, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
 			23000, 0x0d, 0x00, 72,
-			8, 14, 3, 9, 0 },
+			8,  9, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
 			27400, 0x08, 0x00, 96,
-			8, 19, 3, 10, 0 },
+			8,  10, 0 },
 		{ VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
 			29300, 0x0c, 0x00, 108,
-			8, 23, 3, 11, 0 },
+			8,  11, 0 },
 	},
 	50,  /* probe interval */
 	50,  /* rssi reduce interval */
diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h
index e3abd76..6797df0 100644
--- a/drivers/net/wireless/ath/ath9k/rc.h
+++ b/drivers/net/wireless/ath/ath9k/rc.h
@@ -112,8 +112,6 @@ struct ath_rate_table {
 		u8 short_preamble;
 		u8 dot11rate;
 		u8 ctrl_rate;
-		int8_t rssi_ack_validmin;
-		int8_t rssi_ack_deltamin;
 		u8 base_index;
 		u8 cw40index;
 		u8 sgi_index;
@@ -126,7 +124,6 @@ struct ath_rate_table {
 };
 
 struct ath_tx_ratectrl_state {
-	int8_t rssi_thres;	/* required rssi for this rate (dB) */
 	u8 per;			/* recent estimate of packet error rate (%) */
 };
 
@@ -138,16 +135,7 @@ struct ath_rateset {
 /**
  * struct ath_rate_priv - Rate Control priv data
  * @state: RC state
- * @rssi_last: last ACK rssi
- * @rssi_last_lookup: last ACK rssi used for lookup
- * @rssi_last_prev: previous last ACK rssi
- * @rssi_last_prev2: 2nd previous last ACK rssi
- * @rssi_sum_cnt: count of rssi_sum for averaging
- * @rssi_sum_rate: rate that we are averaging
- * @rssi_sum: running sum of rssi for averaging
  * @probe_rate: rate we are probing at
- * @rssi_time: msec timestamp for last ack rssi
- * @rssi_down_time: msec timestamp for last down step
  * @probe_time: msec timestamp for last probe
  * @hw_maxretry_pktcnt: num of packets since we got HW max retry error
  * @max_valid_rate: maximum number of valid rate
@@ -161,13 +149,6 @@ struct ath_rateset {
  * @neg_ht_rates: Negotiated HT rates
  */
 struct ath_rate_priv {
-	int8_t rssi_last;
-	int8_t rssi_last_lookup;
-	int8_t rssi_last_prev;
-	int8_t rssi_last_prev2;
-	int32_t rssi_sum_cnt;
-	int32_t rssi_sum_rate;
-	int32_t rssi_sum;
 	u8 rate_table_size;
 	u8 probe_rate;
 	u8 hw_maxretry_pktcnt;
@@ -177,8 +158,6 @@ struct ath_rate_priv {
 	u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX];
 	u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][RATE_TABLE_SIZE];
 	u8 rate_max_phy;
-	u32 rssi_time;
-	u32 rssi_down_time;
 	u32 probe_time;
 	u32 per_down_time;
 	u32 probe_interval;
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 16/21] mac80211: add helper for management / no-ack frame rate decision
From: Luis R. Rodriguez @ 2009-07-15  0:14 UTC (permalink / raw)
  To: linville, johannes
  Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez, Zhu Yi,
	Reinette Chatre, ipw3945-devel, Gabor Juhos, Felix Fietkau,
	Derek Smithies, Chittajit Mitra
In-Reply-To: <1247616853-11221-1-git-send-email-lrodriguez@atheros.com>

All current rate control algorithms agree to send management and no-ack
frames at the lowest rate. They also agree to do this when sta
and the private rate control data is NULL. We add a hlper to mac80211
for this and simplify the rate control algorithm code.

Developers wishing to make enhancements to rate control algorithms
are for broadcast/multicast can opt to not use this in their
gate_rate() mac80211 callback.

Cc: Zhu Yi <yi.zhu@intel.com>
Cc: Reinette Chatre <reinette.chatre@intel.com>
Cc: ipw3945-devel@lists.sourceforge.net
Cc: Gabor Juhos <juhosg@openwrt.org>
Cc: Felix Fietkau <nbd@openwrt.org>
Cc: Derek Smithies <derek@indranet.co.nz>
Cc: Chittajit Mitra <Chittajit.Mitra@Atheros.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/ath/ath9k/rc.c        |   14 +------------
 drivers/net/wireless/iwlwifi/iwl-3945-rs.c |   13 ++---------
 drivers/net/wireless/iwlwifi/iwl-agn-rs.c  |    7 +-----
 include/net/mac80211.h                     |   23 ++++++++++++++++++++++
 net/mac80211/rate.c                        |   29 ++++++++++++++++++++++++++++
 net/mac80211/rc80211_minstrel.c            |   22 +--------------------
 net/mac80211/rc80211_pid_algo.c            |   11 +---------
 7 files changed, 59 insertions(+), 60 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index 2c72901..630fcf4 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -1518,23 +1518,11 @@ exit:
 static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
 			 struct ieee80211_tx_rate_control *txrc)
 {
-	struct ieee80211_supported_band *sband = txrc->sband;
-	struct sk_buff *skb = txrc->skb;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
 	struct ath_softc *sc = priv;
 	struct ath_rate_priv *ath_rc_priv = priv_sta;
-	__le16 fc = hdr->frame_control;
 
-	/* lowest rate for management and NO_ACK frames */
-	if (!ieee80211_is_data(fc) ||
-	    tx_info->flags & IEEE80211_TX_CTL_NO_ACK || !sta) {
-		tx_info->control.rates[0].idx = rate_lowest_index(sband, sta);
-		tx_info->control.rates[0].count =
-			(tx_info->flags & IEEE80211_TX_CTL_NO_ACK) ?
-				1 : ATH_MGT_TXMAXTRY;
+	if (rate_control_send_low(sta, priv_sta, txrc))
 		return;
-	}
 
 	/* Find tx rate for unicast frames */
 	ath_rc_ratefind(sc, ath_rc_priv, txrc);
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
index 2b77692..a16bd41 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
@@ -673,7 +673,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
 	s8 scale_action = 0;
 	unsigned long flags;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-	__le16 fc;
 	u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0;
 	s8 max_rate_idx = -1;
 	struct iwl_priv *priv = (struct iwl_priv *)priv_r;
@@ -681,16 +680,10 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
 
 	IWL_DEBUG_RATE(priv, "enter\n");
 
-	/* Send management frames and NO_ACK data using lowest rate. */
-	fc = hdr->frame_control;
-	if (!ieee80211_is_data(fc) || info->flags & IEEE80211_TX_CTL_NO_ACK ||
-	    !sta || !priv_sta) {
-		IWL_DEBUG_RATE(priv, "leave: No STA priv data to update!\n");
-		info->control.rates[0].idx = rate_lowest_index(sband, sta);
-		if (info->flags & IEEE80211_TX_CTL_NO_ACK)
-			info->control.rates[0].count = 1;
+	if (rate_control_send_low(sta, priv_sta, txrc))
 		return;
-	}
+
+	rate_mask = sta->supp_rates[sband->band];
 
 	/* get user max rate if set */
 	max_rate_idx = txrc->max_rate_idx;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
index 3fea027..695a841 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c
@@ -2481,13 +2481,8 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
 	}
 
 	/* Send management frames and NO_ACK data using lowest rate. */
-	if (!ieee80211_is_data(hdr->frame_control) ||
-	    info->flags & IEEE80211_TX_CTL_NO_ACK || !sta || !lq_sta) {
-		info->control.rates[0].idx = rate_lowest_index(sband, sta);
-		if (info->flags & IEEE80211_TX_CTL_NO_ACK)
-			info->control.rates[0].count = 1;
+	if (rate_control_send_low(sta, priv_r, txrc))
 		return;
-	}
 
 	rate_idx  = lq_sta->last_txrate_idx;
 
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 9428258..7dd67a1 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2097,6 +2097,29 @@ static inline int rate_supported(struct ieee80211_sta *sta,
 	return (sta == NULL || sta->supp_rates[band] & BIT(index));
 }
 
+/**
+ * rate_control_send_low - helper for drivers for management/no-ack frames
+ *
+ * Rate control algorithms that agree to use the lowest rate to
+ * send management frames and NO_ACK data with the respective hw
+ * retries should use this in the beginning of their mac80211 get_rate
+ * callback. If true is returned the rate control can simply return.
+ * If false is returned we guarantee that sta and sta and priv_sta is
+ * not null.
+ *
+ * Rate control algorithms wishing to do more intelligent selection of
+ * rate for multicast/broadcast frames may choose to not use this.
+ *
+ * @sta: &struct ieee80211_sta pointer to the target destination. Note
+ * 	that this may be null.
+ * @priv_sta: private rate control structure. This may be null.
+ * @txrc: rate control information we sholud populate for mac80211.
+ */
+bool rate_control_send_low(struct ieee80211_sta *sta,
+			   void *priv_sta,
+			   struct ieee80211_tx_rate_control *txrc);
+
+
 static inline s8
 rate_lowest_index(struct ieee80211_supported_band *sband,
 		  struct ieee80211_sta *sta)
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index ac5c8e9..b33efc4 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -198,6 +198,35 @@ static void rate_control_release(struct kref *kref)
 	kfree(ctrl_ref);
 }
 
+static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc)
+{
+	struct sk_buff *skb = txrc->skb;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	__le16 fc;
+
+	fc = hdr->frame_control;
+
+	return ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc));
+}
+
+bool rate_control_send_low(struct ieee80211_sta *sta,
+			   void *priv_sta,
+			   struct ieee80211_tx_rate_control *txrc)
+{
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
+
+	if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) {
+		info->control.rates[0].idx = rate_lowest_index(txrc->sband, sta);
+		info->control.rates[0].count =
+			(info->flags & IEEE80211_TX_CTL_NO_ACK) ?
+			1 : txrc->hw->max_rate_tries;
+		return true;
+	}
+	return false;
+}
+EXPORT_SYMBOL(rate_control_send_low);
+
 void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
 			   struct sta_info *sta,
 			   struct ieee80211_tx_rate_control *txrc)
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 5bdce0c..7c51429 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -70,19 +70,6 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix)
 	return i;
 }
 
-static inline bool
-use_low_rate(struct sk_buff *skb)
-{
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-	__le16 fc;
-
-	fc = hdr->frame_control;
-
-	return ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc));
-}
-
-
 static void
 minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
 {
@@ -231,7 +218,6 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
 		  void *priv_sta, struct ieee80211_tx_rate_control *txrc)
 {
 	struct sk_buff *skb = txrc->skb;
-	struct ieee80211_supported_band *sband = txrc->sband;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct minstrel_sta_info *mi = priv_sta;
 	struct minstrel_priv *mp = priv;
@@ -244,14 +230,8 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
 	int mrr_ndx[3];
 	int sample_rate;
 
-	if (!sta || !mi || use_low_rate(skb)) {
-		ar[0].idx = rate_lowest_index(sband, sta);
-		if (info->flags & IEEE80211_TX_CTL_NO_ACK)
-			ar[0].count = 1;
-		else
-			ar[0].count = mp->max_retry;
+	if (rate_control_send_low(sta, priv_sta, txrc))
 		return;
-	}
 
 	mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot;
 
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
index 5496077..8c053be 100644
--- a/net/mac80211/rc80211_pid_algo.c
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -276,11 +276,9 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
 {
 	struct sk_buff *skb = txrc->skb;
 	struct ieee80211_supported_band *sband = txrc->sband;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct rc_pid_sta_info *spinfo = priv_sta;
 	int rateidx;
-	__le16 fc;
 
 	if (txrc->rts)
 		info->control.rates[0].count =
@@ -290,15 +288,8 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
 			txrc->hw->conf.short_frame_max_tx_count;
 
 	/* Send management frames and NO_ACK data using lowest rate. */
-	fc = hdr->frame_control;
-	if (!sta || !spinfo || !ieee80211_is_data(fc) ||
-	    info->flags & IEEE80211_TX_CTL_NO_ACK) {
-		info->control.rates[0].idx = rate_lowest_index(sband, sta);
-		if (info->flags & IEEE80211_TX_CTL_NO_ACK)
-			info->control.rates[0].count = 1;
-
+	if (rate_control_send_low(sta, priv_sta, txrc))
 		return;
-	}
 
 	rateidx = spinfo->txrate_idx;
 
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 14/21] mac80211: make minstrel/pid RC use ieee80211_is_data(fc)
From: Luis R. Rodriguez @ 2009-07-15  0:14 UTC (permalink / raw)
  To: linville, johannes
  Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez, Felix Fietkau,
	Derek Smithies, Chittajit Mitra
In-Reply-To: <1247616853-11221-1-git-send-email-lrodriguez@atheros.com>

Cc: Felix Fietkau <nbd@openwrt.org>
Cc: Derek Smithies <derek@indranet.co.nz>
Cc: Chittajit Mitra <Chittajit.Mitra@Atheros.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 net/mac80211/rc80211_minstrel.c |    7 +++----
 net/mac80211/rc80211_pid_algo.c |    7 +++----
 2 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 37771ab..5bdce0c 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -75,12 +75,11 @@ use_low_rate(struct sk_buff *skb)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-	u16 fc;
+	__le16 fc;
 
-	fc = le16_to_cpu(hdr->frame_control);
+	fc = hdr->frame_control;
 
-	return ((info->flags & IEEE80211_TX_CTL_NO_ACK) ||
-		(fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA);
+	return ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc));
 }
 
 
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
index a0bef76..5496077 100644
--- a/net/mac80211/rc80211_pid_algo.c
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -280,7 +280,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct rc_pid_sta_info *spinfo = priv_sta;
 	int rateidx;
-	u16 fc;
+	__le16 fc;
 
 	if (txrc->rts)
 		info->control.rates[0].count =
@@ -290,9 +290,8 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
 			txrc->hw->conf.short_frame_max_tx_count;
 
 	/* Send management frames and NO_ACK data using lowest rate. */
-	fc = le16_to_cpu(hdr->frame_control);
-	if (!sta || !spinfo ||
-	    (fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
+	fc = hdr->frame_control;
+	if (!sta || !spinfo || !ieee80211_is_data(fc) ||
 	    info->flags & IEEE80211_TX_CTL_NO_ACK) {
 		info->control.rates[0].idx = rate_lowest_index(sband, sta);
 		if (info->flags & IEEE80211_TX_CTL_NO_ACK)
-- 
1.6.0.6


^ permalink raw reply related

* [PATCH 10/21] ath9k: remove ATH9K_MODE_11B
From: Luis R. Rodriguez @ 2009-07-15  0:14 UTC (permalink / raw)
  To: linville, johannes
  Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez, Derek Smithies,
	Chittajit Mitra
In-Reply-To: <1247616853-11221-1-git-send-email-lrodriguez@atheros.com>

This saves us 2733 bytes.

   text    data     bss     dec     hex filename
 252265    3628    1584  257477   3edc5 ath9k-has-b-rate.ko
 249905    3628    1584  255117   3e48d ath9k.ko

Cc: Derek Smithies <derek@indranet.co.nz>
Cc: Chittajit Mitra <Chittajit.Mitra@Atheros.com>
Siged-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
 drivers/net/wireless/ath/ath9k/hw.c |    1 -
 drivers/net/wireless/ath/ath9k/hw.h |    1 -
 drivers/net/wireless/ath/ath9k/rc.c |   23 -----------------------
 3 files changed, 0 insertions(+), 25 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index cffb078..a115c8c 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -3305,7 +3305,6 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah)
 	}
 
 	if (eeval & AR5416_OPFLAGS_11G) {
-		set_bit(ATH9K_MODE_11B, pCap->wireless_modes);
 		set_bit(ATH9K_MODE_11G, pCap->wireless_modes);
 		if (ah->config.ht_enable) {
 			if (!(eeval & AR5416_OPFLAGS_N_2G_HT20))
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 9d0b31a..5e40223 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -113,7 +113,6 @@
 
 enum wireless_mode {
 	ATH9K_MODE_11A = 0,
-	ATH9K_MODE_11B = 2,
 	ATH9K_MODE_11G = 3,
 	ATH9K_MODE_11NA_HT20 = 6,
 	ATH9K_MODE_11NG_HT20 = 7,
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index b0e3702..112a0ec 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -380,27 +380,6 @@ static const struct ath_rate_table ar5416_11g_ratetable = {
 	0,   /* Phy rates allowed initially */
 };
 
-static const struct ath_rate_table ar5416_11b_ratetable = {
-	4,
-	{
-		{ VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
-			900, 0x1b,  0x00, (0x80|2),
-			0, 0, 1, 0, 0 },
-		{ VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
-			1800, 0x1a, 0x04, (0x80|4),
-			1, 1, 1, 1, 0 },
-		{ VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
-			4300, 0x19, 0x04, (0x80|11),
-			1, 2, 2, 2, 0 },
-		{ VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
-			7100, 0x18, 0x04, (0x80|22),
-			1, 4, 100, 3, 0 },
-	},
-	100, /* probe interval */
-	100, /* rssi reduce interval */
-	0,   /* Phy rates allowed initially */
-};
-
 static inline int8_t median(int8_t a, int8_t b, int8_t c)
 {
 	if (a >= b) {
@@ -1702,8 +1681,6 @@ static struct rate_control_ops ath_rate_ops = {
 
 void ath_rate_attach(struct ath_softc *sc)
 {
-	sc->hw_rate_table[ATH9K_MODE_11B] =
-		&ar5416_11b_ratetable;
 	sc->hw_rate_table[ATH9K_MODE_11A] =
 		&ar5416_11a_ratetable;
 	sc->hw_rate_table[ATH9K_MODE_11G] =
-- 
1.6.0.6


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox