mac80211: Retry null data frame for power save
authorVivek Natarajan <vnatarajan@atheros.com>
Thu, 11 Mar 2010 20:59:53 +0000 (12:59 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 1 Apr 2010 23:01:57 +0000 (16:01 -0700)
commit 375177bf35efc08e1bd37bbda4cc0c8cc4db8500 upstream.

Even if the null data frame is not acked by the AP, mac80211
goes into power save. This might lead to loss of frames
from the AP.
Prevent this by restarting dynamic_ps_timer when ack is not
received for null data frames.

Cc: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
include/net/mac80211.h
net/mac80211/ieee80211_i.h
net/mac80211/mlme.c
net/mac80211/status.c

index 15bcb56106ec1fc81c680fad4586a7bdfb3195be..f39b303944dd3eb1c94843eadc770596a54ca7b6 100644 (file)
@@ -926,6 +926,9 @@ enum ieee80211_tkip_key_type {
  * @IEEE80211_HW_BEACON_FILTER:
  *     Hardware supports dropping of irrelevant beacon frames to
  *     avoid waking up cpu.
+ * @IEEE80211_HW_REPORTS_TX_ACK_STATUS:
+ *     Hardware can provide ack status reports of Tx frames to
+ *     the stack.
  */
 enum ieee80211_hw_flags {
        IEEE80211_HW_HAS_RATE_CONTROL                   = 1<<0,
@@ -943,6 +946,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_SUPPORTS_DYNAMIC_PS                = 1<<12,
        IEEE80211_HW_MFP_CAPABLE                        = 1<<13,
        IEEE80211_HW_BEACON_FILTER                      = 1<<14,
+       IEEE80211_HW_REPORTS_TX_ACK_STATUS              = 1<<15,
 };
 
 /**
index 91dc8636d64481bfefe15c063428f0df018b83a1..3521c17b2606814977885c05497c61cfe26bf885 100644 (file)
@@ -264,6 +264,7 @@ enum ieee80211_sta_flags {
        IEEE80211_STA_DISABLE_11N       = BIT(4),
        IEEE80211_STA_CSA_RECEIVED      = BIT(5),
        IEEE80211_STA_MFP_ENABLED       = BIT(6),
+       IEEE80211_STA_NULLFUNC_ACKED    = BIT(7),
 };
 
 /* flags for MLME request */
index 9072a412089e026816325fa6f179c3e7cf46235d..1a209ac67ffa1f4444342007c58b0ce415016422 100644 (file)
@@ -662,8 +662,11 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
        } else {
                if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
                        ieee80211_send_nullfunc(local, sdata, 1);
-               conf->flags |= IEEE80211_CONF_PS;
-               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+
+               if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+                       conf->flags |= IEEE80211_CONF_PS;
+                       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+               }
        }
 }
 
@@ -754,6 +757,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                container_of(work, struct ieee80211_local,
                             dynamic_ps_enable_work);
        struct ieee80211_sub_if_data *sdata = local->ps_sdata;
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
        /* can only happen when PS was just disabled anyway */
        if (!sdata)
@@ -762,11 +766,16 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
        if (local->hw.conf.flags & IEEE80211_CONF_PS)
                return;
 
-       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+       if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
+           (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)))
                ieee80211_send_nullfunc(local, sdata, 1);
 
-       local->hw.conf.flags |= IEEE80211_CONF_PS;
-       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+       if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) ||
+           (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
+               ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
+               local->hw.conf.flags |= IEEE80211_CONF_PS;
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+       }
 }
 
 void ieee80211_dynamic_ps_timer(unsigned long data)
@@ -2468,6 +2477,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        list_add(&wk->list, &ifmgd->work_list);
 
        ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
+       ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
 
        for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
                if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
index d78f36c64c7bf72830303af60c3e2d2a71e7ac55..f5abeec32a001eda701d63fc1c6f0a603cece417 100644 (file)
@@ -165,6 +165,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        rcu_read_lock();
 
        sband = local->hw.wiphy->bands[info->band];
+       fc = hdr->frame_control;
 
        sta = sta_info_get(local, hdr->addr1);
 
@@ -180,8 +181,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                        return;
                }
 
-               fc = hdr->frame_control;
-
                if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
                    (ieee80211_is_data_qos(fc))) {
                        u16 tid, ssn;
@@ -246,6 +245,20 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                        local->dot11FailedCount++;
        }
 
+       if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc) &&
+           (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
+           !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
+           local->ps_sdata && !(local->scanning)) {
+               if (info->flags & IEEE80211_TX_STAT_ACK) {
+                       local->ps_sdata->u.mgd.flags |=
+                                       IEEE80211_STA_NULLFUNC_ACKED;
+                       ieee80211_queue_work(&local->hw,
+                                       &local->dynamic_ps_enable_work);
+               } else
+                       mod_timer(&local->dynamic_ps_timer, jiffies +
+                                       msecs_to_jiffies(10));
+       }
+
        /* this was a transmitted frame, but now we want to reuse it */
        skb_orphan(skb);