*     Hardware supports Unscheduled Automatic Power Save Delivery
  *     (U-APSD) in managed mode. The mode is configured with
  *     conf_tx() operation.
+ *
+ * @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,
        IEEE80211_HW_SUPPORTS_STATIC_SMPS               = 1<<15,
        IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS              = 1<<16,
        IEEE80211_HW_SUPPORTS_UAPSD                     = 1<<17,
+       IEEE80211_HW_REPORTS_TX_ACK_STATUS              = 1<<18,
 };
 
 /**
 
        IEEE80211_STA_CSA_RECEIVED      = BIT(5),
        IEEE80211_STA_MFP_ENABLED       = BIT(6),
        IEEE80211_STA_UAPSD_ENABLED     = BIT(7),
+       IEEE80211_STA_NULLFUNC_ACKED    = BIT(8),
 };
 
 struct ieee80211_if_managed {
 
        } 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);
+               }
        }
 }
 
                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)
        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)
                return -ENOMEM;
 
        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 ||
 
        rcu_read_lock();
 
        sband = local->hw.wiphy->bands[info->band];
+       fc = hdr->frame_control;
 
        for_each_sta_info(local, hdr->addr1, sta, tmp) {
                /* skip wrong virtual interface */
                        return;
                }
 
-               fc = hdr->frame_control;
-
                if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
                    (ieee80211_is_data_qos(fc))) {
                        u16 tid, ssn;
                        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);