From e1c6b03e8274fc508cc5706ae8644073d8d39dd2 Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@openwrt.org>
Date: Tue, 8 Feb 2011 19:31:50 +0000
Subject: ath9k: add fixes for AP side powersave frame buffering

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@25426 3c298f89-4303-0410-b956-a3cf2f4a3e73
---
 .../patches/530-mac80211_drv_tim_override.patch    |  37 +++
 .../patches/531-ath9k_fix_ap_ps_buffering.patch    | 307 +++++++++++++++++++++
 2 files changed, 344 insertions(+)
 create mode 100644 package/mac80211/patches/530-mac80211_drv_tim_override.patch
 create mode 100644 package/mac80211/patches/531-ath9k_fix_ap_ps_buffering.patch

(limited to 'package/mac80211')

diff --git a/package/mac80211/patches/530-mac80211_drv_tim_override.patch b/package/mac80211/patches/530-mac80211_drv_tim_override.patch
new file mode 100644
index 0000000000..e64d3aa830
--- /dev/null
+++ b/package/mac80211/patches/530-mac80211_drv_tim_override.patch
@@ -0,0 +1,37 @@
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -2198,6 +2198,20 @@ static inline int ieee80211_sta_ps_trans
+ #define IEEE80211_TX_STATUS_HEADROOM	13
+ 
+ /**
++ * ieee80211_sta_set_tim - set the TIM bit for a sleeping station
++ *
++ * If a driver buffers frames for a powersave station instead of passing
++ * them back to mac80211 for retransmission, the station needs to be told
++ * to wake up using the TIM bitmap in the beacon.
++ *
++ * This function sets the station's TIM bit - it will be cleared automatically
++ * either when the station wakes up (and mac80211 has flushed out its
++ * buffered frames), or if all remaining buffered frames in mac80211 have
++ * timed out.
++ */
++void ieee80211_sta_set_tim(struct ieee80211_sta *sta);
++
++/**
+  * ieee80211_tx_status - transmit status callback
+  *
+  * Call this function for all transmitted frames after they have been
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -991,3 +991,11 @@ void ieee80211_sta_block_awake(struct ie
+ 		ieee80211_queue_work(hw, &sta->drv_unblock_wk);
+ }
+ EXPORT_SYMBOL(ieee80211_sta_block_awake);
++
++void ieee80211_sta_set_tim(struct ieee80211_sta *pubsta)
++{
++	struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
++
++	sta_info_set_tim_bit(sta);
++}
++EXPORT_SYMBOL(ieee80211_sta_set_tim);
diff --git a/package/mac80211/patches/531-ath9k_fix_ap_ps_buffering.patch b/package/mac80211/patches/531-ath9k_fix_ap_ps_buffering.patch
new file mode 100644
index 0000000000..7a9d9eaa0c
--- /dev/null
+++ b/package/mac80211/patches/531-ath9k_fix_ap_ps_buffering.patch
@@ -0,0 +1,307 @@
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -205,6 +205,7 @@ struct ath_atx_ac {
+ 	int sched;
+ 	struct list_head list;
+ 	struct list_head tid_q;
++	bool clear_ps_filter;
+ };
+ 
+ struct ath_frame_info {
+@@ -262,6 +263,8 @@ struct ath_node {
+ 	struct ath_atx_ac ac[WME_NUM_AC];
+ 	u16 maxampdu;
+ 	u8 mpdudensity;
++
++	bool sleeping;
+ };
+ 
+ #define AGGR_CLEANUP         BIT(1)
+@@ -343,6 +346,9 @@ int ath_tx_aggr_start(struct ath_softc *
+ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
+ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
+ 
++void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an);
++bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an);
++
+ /********/
+ /* VIFs */
+ /********/
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -1791,6 +1791,27 @@ static int ath9k_sta_remove(struct ieee8
+ 	return 0;
+ }
+ 
++static void ath9k_sta_notify(struct ieee80211_hw *hw,
++			 struct ieee80211_vif *vif,
++			 enum sta_notify_cmd cmd,
++			 struct ieee80211_sta *sta)
++{
++	struct ath_softc *sc = hw->priv;
++	struct ath_node *an = (struct ath_node *) sta->drv_priv;
++
++	switch (cmd) {
++	case STA_NOTIFY_SLEEP:
++		an->sleeping = true;
++		if (ath_tx_aggr_sleep(sc, an))
++			ieee80211_sta_set_tim(sta);
++		break;
++	case STA_NOTIFY_AWAKE:
++		an->sleeping = false;
++		ath_tx_aggr_wakeup(sc, an);
++		break;
++	}
++}
++
+ static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue,
+ 			 const struct ieee80211_tx_queue_params *params)
+ {
+@@ -2130,6 +2151,7 @@ struct ieee80211_ops ath9k_ops = {
+ 	.configure_filter   = ath9k_configure_filter,
+ 	.sta_add	    = ath9k_sta_add,
+ 	.sta_remove	    = ath9k_sta_remove,
++	.sta_notify         = ath9k_sta_notify,
+ 	.conf_tx 	    = ath9k_conf_tx,
+ 	.bss_info_changed   = ath9k_bss_info_changed,
+ 	.set_key            = ath9k_set_key,
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -357,6 +357,7 @@ static void ath_tx_complete_aggr(struct 
+ 	struct ath_frame_info *fi;
+ 	int nframes;
+ 	u8 tidno;
++	bool clear_filter;
+ 
+ 	skb = bf->bf_mpdu;
+ 	hdr = (struct ieee80211_hdr *)skb->data;
+@@ -442,7 +443,11 @@ static void ath_tx_complete_aggr(struct 
+ 			acked_cnt++;
+ 		} else {
+ 			if (!(tid->state & AGGR_CLEANUP) && retry) {
+-				if (fi->retries < ATH_MAX_SW_RETRIES) {
++				if (ts->ts_status & ATH9K_TXERR_FILT) {
++					if (!an->sleeping)
++						clear_filter = true;
++					txpending = 1;
++				} else if (fi->retries < ATH_MAX_SW_RETRIES) {
+ 					ath_tx_set_retry(sc, txq, bf->bf_mpdu);
+ 					txpending = 1;
+ 				} else {
+@@ -496,6 +501,7 @@ static void ath_tx_complete_aggr(struct 
+ 				!txfail, sendbar);
+ 		} else {
+ 			/* retry the un-acked ones */
++			ath9k_hw_set_clrdmask(sc->sc_ah, bf->bf_desc, false);
+ 			if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)) {
+ 				if (bf->bf_next == NULL && bf_last->bf_stale) {
+ 					struct ath_buf *tbf;
+@@ -547,6 +553,8 @@ static void ath_tx_complete_aggr(struct 
+ 	/* prepend un-acked frames to the beginning of the pending frame queue */
+ 	if (!list_empty(&bf_pending)) {
+ 		spin_lock_bh(&txq->axq_lock);
++		if (clear_filter)
++			tid->ac->clear_ps_filter = true;
+ 		list_splice(&bf_pending, &tid->buf_q);
+ 		ath_tx_queue_tid(txq, tid);
+ 		spin_unlock_bh(&txq->axq_lock);
+@@ -816,6 +824,11 @@ static void ath_tx_sched_aggr(struct ath
+ 		bf = list_first_entry(&bf_q, struct ath_buf, list);
+ 		bf->bf_lastbf = list_entry(bf_q.prev, struct ath_buf, list);
+ 
++		if (tid->ac->clear_ps_filter) {
++			tid->ac->clear_ps_filter = false;
++			ath9k_hw_set_clrdmask(sc->sc_ah, bf->bf_desc, true);
++		}
++
+ 		/* if only one frame, send as non-aggregate */
+ 		if (bf == bf->bf_lastbf) {
+ 			fi = get_frame_info(bf->bf_mpdu);
+@@ -896,6 +909,67 @@ void ath_tx_aggr_stop(struct ath_softc *
+ 	ath_tx_flush_tid(sc, txtid);
+ }
+ 
++bool ath_tx_aggr_sleep(struct ath_softc *sc, struct ath_node *an)
++{
++	struct ath_atx_tid *tid;
++	struct ath_atx_ac *ac;
++	struct ath_txq *txq;
++	bool buffered = false;
++	int tidno;
++
++	for (tidno = 0, tid = &an->tid[tidno];
++	     tidno < WME_NUM_TID; tidno++, tid++) {
++
++		if (!tid->sched)
++			continue;
++
++		ac = tid->ac;
++		txq = ac->txq;
++
++		spin_lock_bh(&txq->axq_lock);
++
++		if (!list_empty(&tid->buf_q))
++			buffered = true;
++
++		tid->sched = false;
++		list_del(&tid->list);
++
++		if (ac->sched) {
++			ac->sched = false;
++			list_del(&ac->list);
++		}
++
++		spin_unlock_bh(&txq->axq_lock);
++	}
++
++	return buffered;
++}
++
++void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
++{
++	struct ath_atx_tid *tid;
++	struct ath_atx_ac *ac;
++	struct ath_txq *txq;
++	int tidno;
++
++	for (tidno = 0, tid = &an->tid[tidno];
++	     tidno < WME_NUM_TID; tidno++, tid++) {
++
++		ac = tid->ac;
++		txq = ac->txq;
++
++		spin_lock_bh(&txq->axq_lock);
++		ac->clear_ps_filter = true;
++
++		if (!list_empty(&tid->buf_q) && !tid->paused) {
++			ath_tx_queue_tid(txq, tid);
++			ath_txq_schedule(sc, txq);
++		}
++
++		spin_unlock_bh(&txq->axq_lock);
++	}
++}
++
+ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
+ {
+ 	struct ath_atx_tid *txtid;
+@@ -1492,7 +1566,6 @@ static int setup_tx_flags(struct sk_buff
+ 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ 	int flags = 0;
+ 
+-	flags |= ATH9K_TXDESC_CLRDMASK; /* needed for crypto errors */
+ 	flags |= ATH9K_TXDESC_INTREQ;
+ 
+ 	if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK)
+@@ -1755,6 +1828,9 @@ static void ath_tx_start_dma(struct ath_
+ 		if (txctl->paprd)
+ 			bf->bf_state.bfs_paprd_timestamp = jiffies;
+ 
++		if (tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
++			ath9k_hw_set_clrdmask(sc->sc_ah, bf->bf_desc, true);
++
+ 		ath_tx_send_normal(sc, txctl->txq, tid, &bf_head);
+ 	}
+ 
+--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
++++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
+@@ -128,6 +128,11 @@ static inline void ath9k_hw_set11n_virtu
+ 	ath9k_hw_ops(ah)->set11n_virtualmorefrag(ah, ds, vmf);
+ }
+ 
++static inline void ath9k_hw_set_clrdmask(struct ath_hw *ah, void *ds, bool val)
++{
++	ath9k_hw_ops(ah)->set_clrdmask(ah, ds, val);
++}
++
+ /* Private hardware call ops */
+ 
+ /* PHY ops */
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -642,6 +642,7 @@ struct ath_hw_ops {
+ 				     u32 burstDuration);
+ 	void (*set11n_virtualmorefrag)(struct ath_hw *ah, void *ds,
+ 				       u32 vmf);
++	void (*set_clrdmask)(struct ath_hw *ah, void *ds, bool val);
+ };
+ 
+ struct ath_nf_limits {
+--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+@@ -290,7 +290,6 @@ static void ar9002_hw_set11n_txdesc(stru
+ 		| (flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
+ 		| SM(txPower, AR_XmitPower)
+ 		| (flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
+-		| (flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0)
+ 		| (flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
+ 		| (keyIx != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0);
+ 
+@@ -311,6 +310,16 @@ static void ar9002_hw_set11n_txdesc(stru
+ 	}
+ }
+ 
++static void ar9002_hw_set_clrdmask(struct ath_hw *ah, void *ds, bool val)
++{
++	struct ar5416_desc *ads = AR5416DESC(ds);
++
++	if (val)
++		ads->ds_ctl0 |= AR_ClrDestMask;
++	else
++		ads->ds_ctl0 &= ~AR_ClrDestMask;
++}
++
+ static void ar9002_hw_set11n_ratescenario(struct ath_hw *ah, void *ds,
+ 					  void *lastds,
+ 					  u32 durUpdateEn, u32 rtsctsRate,
+@@ -460,4 +469,5 @@ void ar9002_hw_attach_mac_ops(struct ath
+ 	ops->clr11n_aggr = ar9002_hw_clr11n_aggr;
+ 	ops->set11n_burstduration = ar9002_hw_set11n_burstduration;
+ 	ops->set11n_virtualmorefrag = ar9002_hw_set11n_virtualmorefrag;
++	ops->set_clrdmask = ar9002_hw_set_clrdmask;
+ }
+--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+@@ -329,7 +329,6 @@ static void ar9003_hw_set11n_txdesc(stru
+ 		| (flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
+ 		| SM(txpower, AR_XmitPower)
+ 		| (flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
+-		| (flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0)
+ 		| (keyIx != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
+ 		| (flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0);
+ 
+@@ -350,6 +349,16 @@ static void ar9003_hw_set11n_txdesc(stru
+ 	ads->ctl22 = 0;
+ }
+ 
++static void ar9003_hw_set_clrdmask(struct ath_hw *ah, void *ds, bool val)
++{
++	struct ar9003_txc *ads = (struct ar9003_txc *) ds;
++
++	if (val)
++		ads->ctl11 |= AR_ClrDestMask;
++	else
++		ads->ctl11 &= ~AR_ClrDestMask;
++}
++
+ static void ar9003_hw_set11n_ratescenario(struct ath_hw *ah, void *ds,
+ 					  void *lastds,
+ 					  u32 durUpdateEn, u32 rtsctsRate,
+@@ -522,6 +531,7 @@ void ar9003_hw_attach_mac_ops(struct ath
+ 	ops->clr11n_aggr = ar9003_hw_clr11n_aggr;
+ 	ops->set11n_burstduration = ar9003_hw_set11n_burstduration;
+ 	ops->set11n_virtualmorefrag = ar9003_hw_set11n_virtualmorefrag;
++	ops->set_clrdmask = ar9003_hw_set_clrdmask;
+ }
+ 
+ void ath9k_hw_set_rx_bufsize(struct ath_hw *ah, u16 buf_size)
+--- a/drivers/net/wireless/ath/ath9k/mac.h
++++ b/drivers/net/wireless/ath/ath9k/mac.h
+@@ -239,7 +239,6 @@ struct ath_desc {
+ 	void *ds_vdata;
+ } __packed __aligned(4);
+ 
+-#define ATH9K_TXDESC_CLRDMASK		0x0001
+ #define ATH9K_TXDESC_NOACK		0x0002
+ #define ATH9K_TXDESC_RTSENA		0x0004
+ #define ATH9K_TXDESC_CTSENA		0x0008
-- 
cgit v1.2.3