summaryrefslogtreecommitdiff
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorRajkumar Manoharan <rmanohar@qti.qualcomm.com>2014-05-19 10:14:37 +0400
committerJohn W. Linville <linville@tuxdriver.com>2014-05-20 00:42:15 +0400
commit08232bf94949eabaabfcc2987025aed378aa13ae (patch)
tree02620e03af5ce05e23dc4d319c26d62f93a42baa /drivers/net/wireless
parent1db10595c6c7274930de3d2ed525e0f6bd817b9d (diff)
downloadlinux-08232bf94949eabaabfcc2987025aed378aa13ae.tar.xz
ath9k_hw: Abort transmission for sleeping station
The data transmission to the power save station should be aborted immediately, whenever the station informs sleep state. Right now the frames queued into into hardware are being transmitted until the hardware detects the power save station based excessive retries of the data frames due to unacknowlegdement. Then remaining frames are returned with filetered status and might be retried later by driver or mac80211. Per WFA certification testing, AP should not send out more than two frames after processing nullfunc with PM bit set from associated station. To speed up tx filtering, the pending frames in hardware queues for given station will be aborted immediately via tx filter registers. This transmit filters can be ignored if the descriptor is having invalid destination index or clear destination mask set. Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c22
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/reg.h3
4 files changed, 31 insertions, 3 deletions
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 51ce36f108f9..275205ab5f15 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -958,3 +958,25 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah)
return;
}
EXPORT_SYMBOL(ath9k_hw_set_interrupts);
+
+#define ATH9K_HW_MAX_DCU 10
+#define ATH9K_HW_SLICE_PER_DCU 16
+#define ATH9K_HW_BIT_IN_SLICE 16
+void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set)
+{
+ int dcu_idx;
+ u32 filter;
+
+ for (dcu_idx = 0; dcu_idx < 10; dcu_idx++) {
+ filter = SM(set, AR_D_TXBLK_WRITE_COMMAND);
+ filter |= SM(dcu_idx, AR_D_TXBLK_WRITE_DCU);
+ filter |= SM((destidx / ATH9K_HW_SLICE_PER_DCU),
+ AR_D_TXBLK_WRITE_SLICE);
+ filter |= BIT(destidx % ATH9K_HW_BIT_IN_SLICE);
+ ath_dbg(ath9k_hw_common(ah), PS,
+ "DCU%d staid %d set %d txfilter %08x\n",
+ dcu_idx, destidx, set, filter);
+ REG_WRITE(ah, AR_D_TXBLK_BASE, filter);
+ }
+}
+EXPORT_SYMBOL(ath9k_hw_set_tx_filter);
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 89df634e81f9..da7686757535 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -729,6 +729,7 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning);
void ath9k_hw_abortpcurecv(struct ath_hw *ah);
bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset);
int ath9k_hw_beaconq_setup(struct ath_hw *ah);
+void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set);
/* Interrupt Handling */
bool ath9k_hw_intrpend(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 8d7b9b66fefa..47d442a288cf 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1506,8 +1506,12 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw,
case STA_NOTIFY_SLEEP:
an->sleeping = true;
ath_tx_aggr_sleep(sta, sc, an);
+ if (an->ps_key > 0)
+ ath9k_hw_set_tx_filter(sc->sc_ah, an->ps_key, true);
break;
case STA_NOTIFY_AWAKE:
+ if (an->ps_key > 0)
+ ath9k_hw_set_tx_filter(sc->sc_ah, an->ps_key, false);
an->sleeping = false;
ath_tx_aggr_wakeup(sc, an);
break;
@@ -1593,6 +1597,8 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
ath9k_del_ps_key(sc, vif, sta);
ret = ath_key_config(common, vif, sta, key);
+ if (sta && (ret > 0))
+ ((struct ath_node *)sta->drv_priv)->ps_key = ret;
if (ret >= 0) {
key->hw_key_idx = ret;
/* push IV and Michael MIC generation to stack */
@@ -1607,6 +1613,8 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
break;
case DISABLE_KEY:
ath_key_delete(common, key);
+ if (sta)
+ ((struct ath_node *)sta->drv_priv)->ps_key = 0;
break;
default:
ret = -EINVAL;
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index b1fd3fa84983..f1bbce3f7774 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -505,9 +505,6 @@
#define AR_D_QCUMASK 0x000003FF
#define AR_D_QCUMASK_RESV0 0xFFFFFC00
-#define AR_D_TXBLK_CMD 0x1038
-#define AR_D_TXBLK_DATA(i) (AR_D_TXBLK_CMD+(i))
-
#define AR_D0_LCL_IFS 0x1040
#define AR_D1_LCL_IFS 0x1044
#define AR_D2_LCL_IFS 0x1048