Merge branch 'master' of git://git.infradead.org/users/linville/wireless-next into...
[pandora-kernel.git] / net / mac80211 / sta_info.c
index 3db78b6..ce962d2 100644 (file)
@@ -24,6 +24,7 @@
 #include "sta_info.h"
 #include "debugfs_sta.h"
 #include "mesh.h"
+#include "wme.h"
 
 /**
  * DOC: STA information lifetime rules
@@ -72,7 +73,7 @@ static int sta_info_hash_del(struct ieee80211_local *local,
        if (!s)
                return -ENOENT;
        if (s == sta) {
-               rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)],
+               RCU_INIT_POINTER(local->sta_hash[STA_HASH(sta->sta.addr)],
                                   s->hnext);
                return 0;
        }
@@ -82,7 +83,7 @@ static int sta_info_hash_del(struct ieee80211_local *local,
                s = rcu_dereference_protected(s->hnext,
                                        lockdep_is_held(&local->sta_lock));
        if (rcu_access_pointer(s->hnext)) {
-               rcu_assign_pointer(s->hnext, sta->hnext);
+               RCU_INIT_POINTER(s->hnext, sta->hnext);
                return 0;
        }
 
@@ -96,6 +97,27 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
 
+       sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
+                                   lockdep_is_held(&local->sta_lock) ||
+                                   lockdep_is_held(&local->sta_mtx));
+       while (sta) {
+               if (sta->sdata == sdata && !sta->dummy &&
+                   memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
+                       break;
+               sta = rcu_dereference_check(sta->hnext,
+                                           lockdep_is_held(&local->sta_lock) ||
+                                           lockdep_is_held(&local->sta_mtx));
+       }
+       return sta;
+}
+
+/* get a station info entry even if it is a dummy station*/
+struct sta_info *sta_info_get_rx(struct ieee80211_sub_if_data *sdata,
+                             const u8 *addr)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct sta_info *sta;
+
        sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
                                    lockdep_is_held(&local->sta_lock) ||
                                    lockdep_is_held(&local->sta_mtx));
@@ -120,6 +142,32 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct sta_info *sta;
 
+       sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
+                                   lockdep_is_held(&local->sta_lock) ||
+                                   lockdep_is_held(&local->sta_mtx));
+       while (sta) {
+               if ((sta->sdata == sdata ||
+                    (sta->sdata->bss && sta->sdata->bss == sdata->bss)) &&
+                   !sta->dummy &&
+                   memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
+                       break;
+               sta = rcu_dereference_check(sta->hnext,
+                                           lockdep_is_held(&local->sta_lock) ||
+                                           lockdep_is_held(&local->sta_mtx));
+       }
+       return sta;
+}
+
+/*
+ * Get sta info either from the specified interface
+ * or from one of its vlans (including dummy stations)
+ */
+struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata,
+                                 const u8 *addr)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct sta_info *sta;
+
        sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
                                    lockdep_is_held(&local->sta_lock) ||
                                    lockdep_is_held(&local->sta_mtx));
@@ -184,7 +232,7 @@ static void sta_info_hash_add(struct ieee80211_local *local,
                              struct sta_info *sta)
 {
        sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)];
-       rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
+       RCU_INIT_POINTER(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
 }
 
 static void sta_unblock(struct work_struct *wk)
@@ -196,13 +244,22 @@ static void sta_unblock(struct work_struct *wk)
        if (sta->dead)
                return;
 
-       if (!test_sta_flags(sta, WLAN_STA_PS_STA))
+       if (!test_sta_flag(sta, WLAN_STA_PS_STA))
                ieee80211_sta_ps_deliver_wakeup(sta);
-       else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) {
-               clear_sta_flags(sta, WLAN_STA_PS_DRIVER);
+       else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) {
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+
+               local_bh_disable();
                ieee80211_sta_ps_deliver_poll_response(sta);
+               local_bh_enable();
+       } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) {
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+
+               local_bh_disable();
+               ieee80211_sta_ps_deliver_uapsd(sta);
+               local_bh_enable();
        } else
-               clear_sta_flags(sta, WLAN_STA_PS_DRIVER);
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
 }
 
 static int sta_prepare_rate_control(struct ieee80211_local *local,
@@ -235,7 +292,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                return NULL;
 
        spin_lock_init(&sta->lock);
-       spin_lock_init(&sta->flaglock);
        INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
        INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
        mutex_init(&sta->ampdu_mlme.mtx);
@@ -262,8 +318,10 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                 */
                sta->timer_to_tid[i] = i;
        }
-       skb_queue_head_init(&sta->ps_tx_buf);
-       skb_queue_head_init(&sta->tx_filtered);
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               skb_queue_head_init(&sta->ps_tx_buf[i]);
+               skb_queue_head_init(&sta->tx_filtered[i]);
+       }
 
        for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
                sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
@@ -280,7 +338,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        return sta;
 }
 
-static int sta_info_finish_insert(struct sta_info *sta, bool async)
+static int sta_info_finish_insert(struct sta_info *sta,
+                               bool async, bool dummy_reinsert)
 {
        struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -290,50 +349,58 @@ static int sta_info_finish_insert(struct sta_info *sta, bool async)
 
        lockdep_assert_held(&local->sta_mtx);
 
-       /* notify driver */
-       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-               sdata = container_of(sdata->bss,
-                                    struct ieee80211_sub_if_data,
-                                    u.ap);
-       err = drv_sta_add(local, sdata, &sta->sta);
-       if (err) {
-               if (!async)
-                       return err;
-               printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to driver (%d)"
-                                 " - keeping it anyway.\n",
-                      sdata->name, sta->sta.addr, err);
-       } else {
-               sta->uploaded = true;
+       if (!sta->dummy || dummy_reinsert) {
+               /* notify driver */
+               if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+                       sdata = container_of(sdata->bss,
+                                            struct ieee80211_sub_if_data,
+                                            u.ap);
+               err = drv_sta_add(local, sdata, &sta->sta);
+               if (err) {
+                       if (!async)
+                               return err;
+                       printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to "
+                                         "driver (%d) - keeping it anyway.\n",
+                              sdata->name, sta->sta.addr, err);
+               } else {
+                       sta->uploaded = true;
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-               if (async)
-                       wiphy_debug(local->hw.wiphy,
-                                   "Finished adding IBSS STA %pM\n",
-                                   sta->sta.addr);
+                       if (async)
+                               wiphy_debug(local->hw.wiphy,
+                                           "Finished adding IBSS STA %pM\n",
+                                           sta->sta.addr);
 #endif
+               }
+
+               sdata = sta->sdata;
        }
 
-       sdata = sta->sdata;
+       if (!dummy_reinsert) {
+               if (!async) {
+                       local->num_sta++;
+                       local->sta_generation++;
+                       smp_mb();
 
-       if (!async) {
-               local->num_sta++;
-               local->sta_generation++;
-               smp_mb();
+                       /* make the station visible */
+                       spin_lock_irqsave(&local->sta_lock, flags);
+                       sta_info_hash_add(local, sta);
+                       spin_unlock_irqrestore(&local->sta_lock, flags);
+               }
 
-               /* make the station visible */
-               spin_lock_irqsave(&local->sta_lock, flags);
-               sta_info_hash_add(local, sta);
-               spin_unlock_irqrestore(&local->sta_lock, flags);
+               list_add(&sta->list, &local->sta_list);
+       } else {
+               sta->dummy = false;
        }
 
-       list_add(&sta->list, &local->sta_list);
-
-       ieee80211_sta_debugfs_add(sta);
-       rate_control_add_sta_debugfs(sta);
-
-       sinfo.filled = 0;
-       sinfo.generation = local->sta_generation;
-       cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+       if (!sta->dummy) {
+               ieee80211_sta_debugfs_add(sta);
+               rate_control_add_sta_debugfs(sta);
 
+               memset(&sinfo, 0, sizeof(sinfo));
+               sinfo.filled = 0;
+               sinfo.generation = local->sta_generation;
+               cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
+       }
 
        return 0;
 }
@@ -350,7 +417,7 @@ static void sta_info_finish_pending(struct ieee80211_local *local)
                list_del(&sta->list);
                spin_unlock_irqrestore(&local->sta_lock, flags);
 
-               sta_info_finish_insert(sta, true);
+               sta_info_finish_insert(sta, true, false);
 
                spin_lock_irqsave(&local->sta_lock, flags);
        }
@@ -367,106 +434,117 @@ static void sta_info_finish_work(struct work_struct *work)
        mutex_unlock(&local->sta_mtx);
 }
 
-int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
+static int sta_info_insert_check(struct sta_info *sta)
 {
-       struct ieee80211_local *local = sta->local;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
-       unsigned long flags;
-       int err = 0;
 
        /*
         * Can't be a WARN_ON because it can be triggered through a race:
         * something inserts a STA (on one CPU) without holding the RTNL
         * and another CPU turns off the net device.
         */
-       if (unlikely(!ieee80211_sdata_running(sdata))) {
-               err = -ENETDOWN;
-               rcu_read_lock();
-               goto out_free;
-       }
+       if (unlikely(!ieee80211_sdata_running(sdata)))
+               return -ENETDOWN;
 
        if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 ||
-                   is_multicast_ether_addr(sta->sta.addr))) {
-               err = -EINVAL;
+                   is_multicast_ether_addr(sta->sta.addr)))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int sta_info_insert_ibss(struct sta_info *sta) __acquires(RCU)
+{
+       struct ieee80211_local *local = sta->local;
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       unsigned long flags;
+
+       spin_lock_irqsave(&local->sta_lock, flags);
+       /* check if STA exists already */
+       if (sta_info_get_bss_rx(sdata, sta->sta.addr)) {
+               spin_unlock_irqrestore(&local->sta_lock, flags);
                rcu_read_lock();
-               goto out_free;
+               return -EEXIST;
        }
 
-       /*
-        * In ad-hoc mode, we sometimes need to insert stations
-        * from tasklet context from the RX path. To avoid races,
-        * always do so in that case -- see the comment below.
-        */
-       if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
-               spin_lock_irqsave(&local->sta_lock, flags);
-               /* check if STA exists already */
-               if (sta_info_get_bss(sdata, sta->sta.addr)) {
-                       spin_unlock_irqrestore(&local->sta_lock, flags);
-                       rcu_read_lock();
-                       err = -EEXIST;
-                       goto out_free;
-               }
-
-               local->num_sta++;
-               local->sta_generation++;
-               smp_mb();
-               sta_info_hash_add(local, sta);
+       local->num_sta++;
+       local->sta_generation++;
+       smp_mb();
+       sta_info_hash_add(local, sta);
 
-               list_add_tail(&sta->list, &local->sta_pending_list);
+       list_add_tail(&sta->list, &local->sta_pending_list);
 
-               rcu_read_lock();
-               spin_unlock_irqrestore(&local->sta_lock, flags);
+       rcu_read_lock();
+       spin_unlock_irqrestore(&local->sta_lock, flags);
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-               wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n",
-                           sta->sta.addr);
+       wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n",
+                       sta->sta.addr);
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
-               ieee80211_queue_work(&local->hw, &local->sta_finish_work);
+       ieee80211_queue_work(&local->hw, &local->sta_finish_work);
 
-               return 0;
-       }
+       return 0;
+}
+
+/*
+ * should be called with sta_mtx locked
+ * this function replaces the mutex lock
+ * with a RCU lock
+ */
+static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU)
+{
+       struct ieee80211_local *local = sta->local;
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       unsigned long flags;
+       struct sta_info *exist_sta;
+       bool dummy_reinsert = false;
+       int err = 0;
+
+       lockdep_assert_held(&local->sta_mtx);
 
        /*
         * On first glance, this will look racy, because the code
-        * below this point, which inserts a station with sleeping,
+        * in this function, which inserts a station with sleeping,
         * unlocks the sta_lock between checking existence in the
         * hash table and inserting into it.
         *
         * However, it is not racy against itself because it keeps
-        * the mutex locked. It still seems to race against the
-        * above code that atomically inserts the station... That,
-        * however, is not true because the above code can only
-        * be invoked for IBSS interfaces, and the below code will
-        * not be -- and the two do not race against each other as
-        * the hash table also keys off the interface.
+        * the mutex locked.
         */
 
-       might_sleep();
-
-       mutex_lock(&local->sta_mtx);
-
        spin_lock_irqsave(&local->sta_lock, flags);
-       /* check if STA exists already */
-       if (sta_info_get_bss(sdata, sta->sta.addr)) {
-               spin_unlock_irqrestore(&local->sta_lock, flags);
-               mutex_unlock(&local->sta_mtx);
-               rcu_read_lock();
-               err = -EEXIST;
-               goto out_free;
+       /*
+        * check if STA exists already.
+        * only accept a scenario of a second call to sta_info_insert_non_ibss
+        * with a dummy station entry that was inserted earlier
+        * in that case - assume that the dummy station flag should
+        * be removed.
+        */
+       exist_sta = sta_info_get_bss_rx(sdata, sta->sta.addr);
+       if (exist_sta) {
+               if (exist_sta == sta && sta->dummy) {
+                       dummy_reinsert = true;
+               } else {
+                       spin_unlock_irqrestore(&local->sta_lock, flags);
+                       mutex_unlock(&local->sta_mtx);
+                       rcu_read_lock();
+                       return -EEXIST;
+               }
        }
 
        spin_unlock_irqrestore(&local->sta_lock, flags);
 
-       err = sta_info_finish_insert(sta, false);
+       err = sta_info_finish_insert(sta, false, dummy_reinsert);
        if (err) {
                mutex_unlock(&local->sta_mtx);
                rcu_read_lock();
-               goto out_free;
+               return err;
        }
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-       wiphy_debug(local->hw.wiphy, "Inserted STA %pM\n", sta->sta.addr);
+       wiphy_debug(local->hw.wiphy, "Inserted %sSTA %pM\n",
+                       sta->dummy ? "dummy " : "", sta->sta.addr);
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
        /* move reference to rcu-protected */
@@ -476,6 +554,51 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
        if (ieee80211_vif_is_mesh(&sdata->vif))
                mesh_accept_plinks_update(sdata);
 
+       return 0;
+}
+
+int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
+{
+       struct ieee80211_local *local = sta->local;
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       int err = 0;
+
+       err = sta_info_insert_check(sta);
+       if (err) {
+               rcu_read_lock();
+               goto out_free;
+       }
+
+       /*
+        * In ad-hoc mode, we sometimes need to insert stations
+        * from tasklet context from the RX path. To avoid races,
+        * always do so in that case -- see the comment below.
+        */
+       if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+               err = sta_info_insert_ibss(sta);
+               if (err)
+                       goto out_free;
+
+               return 0;
+       }
+
+       /*
+        * It might seem that the function called below is in race against
+        * the function call above that atomically inserts the station... That,
+        * however, is not true because the above code can only
+        * be invoked for IBSS interfaces, and the below code will
+        * not be -- and the two do not race against each other as
+        * the hash table also keys off the interface.
+        */
+
+       might_sleep();
+
+       mutex_lock(&local->sta_mtx);
+
+       err = sta_info_insert_non_ibss(sta);
+       if (err)
+               goto out_free;
+
        return 0;
  out_free:
        BUG_ON(!err);
@@ -492,6 +615,25 @@ int sta_info_insert(struct sta_info *sta)
        return err;
 }
 
+/* Caller must hold sta->local->sta_mtx */
+int sta_info_reinsert(struct sta_info *sta)
+{
+       struct ieee80211_local *local = sta->local;
+       int err = 0;
+
+       err = sta_info_insert_check(sta);
+       if (err) {
+               mutex_unlock(&local->sta_mtx);
+               return err;
+       }
+
+       might_sleep();
+
+       err = sta_info_insert_non_ibss(sta);
+       rcu_read_unlock();
+       return err;
+}
+
 static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
 {
        /*
@@ -510,64 +652,93 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid)
        bss->tim[aid / 8] &= ~(1 << (aid % 8));
 }
 
-static void __sta_info_set_tim_bit(struct ieee80211_if_ap *bss,
-                                  struct sta_info *sta)
+static unsigned long ieee80211_tids_for_ac(int ac)
 {
-       BUG_ON(!bss);
-
-       __bss_tim_set(bss, sta->sta.aid);
-
-       if (sta->local->ops->set_tim) {
-               sta->local->tim_in_locked_section = true;
-               drv_set_tim(sta->local, &sta->sta, true);
-               sta->local->tim_in_locked_section = false;
+       /* If we ever support TIDs > 7, this obviously needs to be adjusted */
+       switch (ac) {
+       case IEEE80211_AC_VO:
+               return BIT(6) | BIT(7);
+       case IEEE80211_AC_VI:
+               return BIT(4) | BIT(5);
+       case IEEE80211_AC_BE:
+               return BIT(0) | BIT(3);
+       case IEEE80211_AC_BK:
+               return BIT(1) | BIT(2);
+       default:
+               WARN_ON(1);
+               return 0;
        }
 }
 
-void sta_info_set_tim_bit(struct sta_info *sta)
+void sta_info_recalc_tim(struct sta_info *sta)
 {
+       struct ieee80211_local *local = sta->local;
+       struct ieee80211_if_ap *bss = sta->sdata->bss;
        unsigned long flags;
+       bool indicate_tim = false;
+       u8 ignore_for_tim = sta->sta.uapsd_queues;
+       int ac;
 
-       BUG_ON(!sta->sdata->bss);
+       if (WARN_ON_ONCE(!sta->sdata->bss))
+               return;
 
-       spin_lock_irqsave(&sta->local->sta_lock, flags);
-       __sta_info_set_tim_bit(sta->sdata->bss, sta);
-       spin_unlock_irqrestore(&sta->local->sta_lock, flags);
-}
+       /* No need to do anything if the driver does all */
+       if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
+               return;
 
-static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss,
-                                    struct sta_info *sta)
-{
-       BUG_ON(!bss);
+       if (sta->dead)
+               goto done;
+
+       /*
+        * If all ACs are delivery-enabled then we should build
+        * the TIM bit for all ACs anyway; if only some are then
+        * we ignore those and build the TIM bit using only the
+        * non-enabled ones.
+        */
+       if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1)
+               ignore_for_tim = 0;
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               unsigned long tids;
 
-       __bss_tim_clear(bss, sta->sta.aid);
+               if (ignore_for_tim & BIT(ac))
+                       continue;
+
+               indicate_tim |= !skb_queue_empty(&sta->tx_filtered[ac]) ||
+                               !skb_queue_empty(&sta->ps_tx_buf[ac]);
+               if (indicate_tim)
+                       break;
 
-       if (sta->local->ops->set_tim) {
-               sta->local->tim_in_locked_section = true;
-               drv_set_tim(sta->local, &sta->sta, false);
-               sta->local->tim_in_locked_section = false;
+               tids = ieee80211_tids_for_ac(ac);
+
+               indicate_tim |=
+                       sta->driver_buffered_tids & tids;
        }
-}
 
-void sta_info_clear_tim_bit(struct sta_info *sta)
-{
-       unsigned long flags;
+ done:
+       spin_lock_irqsave(&local->sta_lock, flags);
 
-       BUG_ON(!sta->sdata->bss);
+       if (indicate_tim)
+               __bss_tim_set(bss, sta->sta.aid);
+       else
+               __bss_tim_clear(bss, sta->sta.aid);
 
-       spin_lock_irqsave(&sta->local->sta_lock, flags);
-       __sta_info_clear_tim_bit(sta->sdata->bss, sta);
-       spin_unlock_irqrestore(&sta->local->sta_lock, flags);
+       if (local->ops->set_tim) {
+               local->tim_in_locked_section = true;
+               drv_set_tim(local, &sta->sta, indicate_tim);
+               local->tim_in_locked_section = false;
+       }
+
+       spin_unlock_irqrestore(&local->sta_lock, flags);
 }
 
-static int sta_info_buffer_expired(struct sta_info *sta,
-                                  struct sk_buff *skb)
+static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb)
 {
        struct ieee80211_tx_info *info;
        int timeout;
 
        if (!skb)
-               return 0;
+               return false;
 
        info = IEEE80211_SKB_CB(skb);
 
@@ -581,24 +752,59 @@ static int sta_info_buffer_expired(struct sta_info *sta,
 }
 
 
-static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
-                                            struct sta_info *sta)
+static bool sta_info_cleanup_expire_buffered_ac(struct ieee80211_local *local,
+                                               struct sta_info *sta, int ac)
 {
        unsigned long flags;
        struct sk_buff *skb;
 
-       if (skb_queue_empty(&sta->ps_tx_buf))
-               return false;
+       /*
+        * First check for frames that should expire on the filtered
+        * queue. Frames here were rejected by the driver and are on
+        * a separate queue to avoid reordering with normal PS-buffered
+        * frames. They also aren't accounted for right now in the
+        * total_ps_buffered counter.
+        */
+       for (;;) {
+               spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags);
+               skb = skb_peek(&sta->tx_filtered[ac]);
+               if (sta_info_buffer_expired(sta, skb))
+                       skb = __skb_dequeue(&sta->tx_filtered[ac]);
+               else
+                       skb = NULL;
+               spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags);
 
+               /*
+                * Frames are queued in order, so if this one
+                * hasn't expired yet we can stop testing. If
+                * we actually reached the end of the queue we
+                * also need to stop, of course.
+                */
+               if (!skb)
+                       break;
+               dev_kfree_skb(skb);
+       }
+
+       /*
+        * Now also check the normal PS-buffered queue, this will
+        * only find something if the filtered queue was emptied
+        * since the filtered frames are all before the normal PS
+        * buffered frames.
+        */
        for (;;) {
-               spin_lock_irqsave(&sta->ps_tx_buf.lock, flags);
-               skb = skb_peek(&sta->ps_tx_buf);
+               spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags);
+               skb = skb_peek(&sta->ps_tx_buf[ac]);
                if (sta_info_buffer_expired(sta, skb))
-                       skb = __skb_dequeue(&sta->ps_tx_buf);
+                       skb = __skb_dequeue(&sta->ps_tx_buf[ac]);
                else
                        skb = NULL;
-               spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags);
+               spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags);
 
+               /*
+                * frames are queued in order, so if this one
+                * hasn't expired yet (or we reached the end of
+                * the queue) we can stop testing
+                */
                if (!skb)
                        break;
 
@@ -608,22 +814,47 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
                       sta->sta.addr);
 #endif
                dev_kfree_skb(skb);
-
-               if (skb_queue_empty(&sta->ps_tx_buf) &&
-                   !test_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF))
-                       sta_info_clear_tim_bit(sta);
        }
 
-       return true;
+       /*
+        * Finally, recalculate the TIM bit for this station -- it might
+        * now be clear because the station was too slow to retrieve its
+        * frames.
+        */
+       sta_info_recalc_tim(sta);
+
+       /*
+        * Return whether there are any frames still buffered, this is
+        * used to check whether the cleanup timer still needs to run,
+        * if there are no frames we don't need to rearm the timer.
+        */
+       return !(skb_queue_empty(&sta->ps_tx_buf[ac]) &&
+                skb_queue_empty(&sta->tx_filtered[ac]));
+}
+
+static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
+                                            struct sta_info *sta)
+{
+       bool have_buffered = false;
+       int ac;
+
+       /* This is only necessary for stations on BSS interfaces */
+       if (!sta->sdata->bss)
+               return false;
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               have_buffered |=
+                       sta_info_cleanup_expire_buffered_ac(local, sta, ac);
+
+       return have_buffered;
 }
 
 static int __must_check __sta_info_destroy(struct sta_info *sta)
 {
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
-       struct sk_buff *skb;
        unsigned long flags;
-       int ret, i;
+       int ret, i, ac;
 
        might_sleep();
 
@@ -639,7 +870,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
         * sessions -- block that to make sure the tear-down
         * will be sufficient.
         */
-       set_sta_flags(sta, WLAN_STA_BLOCK_BA);
+       set_sta_flag(sta, WLAN_STA_BLOCK_BA);
        ieee80211_sta_tear_down_BA_sessions(sta, true);
 
        spin_lock_irqsave(&local->sta_lock, flags);
@@ -660,19 +891,22 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
 
        sta->dead = true;
 
-       if (test_and_clear_sta_flags(sta,
-                               WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
+       if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
+           test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
                BUG_ON(!sdata->bss);
 
+               clear_sta_flag(sta, WLAN_STA_PS_STA);
+               clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+
                atomic_dec(&sdata->bss->num_sta_ps);
-               __sta_info_clear_tim_bit(sdata->bss, sta);
+               sta_info_recalc_tim(sta);
        }
 
        local->num_sta--;
        local->sta_generation++;
 
        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-               rcu_assign_pointer(sdata->u.vlan.sta, NULL);
+               RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
 
        if (sta->uploaded) {
                if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -691,6 +925,12 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
         */
        synchronize_rcu();
 
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
+               __skb_queue_purge(&sta->ps_tx_buf[ac]);
+               __skb_queue_purge(&sta->tx_filtered[ac]);
+       }
+
 #ifdef CONFIG_MAC80211_MESH
        if (ieee80211_vif_is_mesh(&sdata->vif))
                mesh_accept_plinks_update(sdata);
@@ -713,14 +953,6 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
        }
 #endif
 
-       while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
-               local->total_ps_buffered--;
-               dev_kfree_skb_any(skb);
-       }
-
-       while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
-               dev_kfree_skb_any(skb);
-
        __sta_info_free(local, sta);
 
        return 0;
@@ -732,7 +964,7 @@ int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr)
        int ret;
 
        mutex_lock(&sdata->local->sta_mtx);
-       sta = sta_info_get(sdata, addr);
+       sta = sta_info_get_rx(sdata, addr);
        ret = __sta_info_destroy(sta);
        mutex_unlock(&sdata->local->sta_mtx);
 
@@ -746,7 +978,7 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
        int ret;
 
        mutex_lock(&sdata->local->sta_mtx);
-       sta = sta_info_get_bss(sdata, addr);
+       sta = sta_info_get_bss_rx(sdata, addr);
        ret = __sta_info_destroy(sta);
        mutex_unlock(&sdata->local->sta_mtx);
 
@@ -886,7 +1118,8 @@ static void clear_sta_ps_flags(void *_sta)
 {
        struct sta_info *sta = _sta;
 
-       clear_sta_flags(sta, WLAN_STA_PS_DRIVER | WLAN_STA_PS_STA);
+       clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
+       clear_sta_flag(sta, WLAN_STA_PS_STA);
 }
 
 /* powersave support code */
@@ -894,88 +1127,341 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
-       int sent, buffered;
+       struct sk_buff_head pending;
+       int filtered = 0, buffered = 0, ac;
+
+       clear_sta_flag(sta, WLAN_STA_SP);
+
+       BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1);
+       sta->driver_buffered_tids = 0;
 
-       clear_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF);
        if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
                drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta);
 
-       if (!skb_queue_empty(&sta->ps_tx_buf))
-               sta_info_clear_tim_bit(sta);
+       skb_queue_head_init(&pending);
 
        /* Send all buffered frames to the station */
-       sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered);
-       buffered = ieee80211_add_pending_skbs_fn(local, &sta->ps_tx_buf,
-                                                clear_sta_ps_flags, sta);
-       sent += buffered;
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               int count = skb_queue_len(&pending), tmp;
+
+               skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending);
+               tmp = skb_queue_len(&pending);
+               filtered += tmp - count;
+               count = tmp;
+
+               skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending);
+               tmp = skb_queue_len(&pending);
+               buffered += tmp - count;
+       }
+
+       ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
+
        local->total_ps_buffered -= buffered;
 
+       sta_info_recalc_tim(sta);
+
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
        printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames "
               "since STA not sleeping anymore\n", sdata->name,
-              sta->sta.addr, sta->sta.aid, sent - buffered, buffered);
+              sta->sta.addr, sta->sta.aid, filtered, buffered);
 #endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
 }
 
-void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
+static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
+                                        struct sta_info *sta, int tid,
+                                        enum ieee80211_frame_release_type reason)
 {
-       struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_qos_hdr *nullfunc;
        struct sk_buff *skb;
-       int no_pending_pkts;
+       int size = sizeof(*nullfunc);
+       __le16 fc;
+       bool qos = test_sta_flag(sta, WLAN_STA_WME);
+       struct ieee80211_tx_info *info;
 
-       skb = skb_dequeue(&sta->tx_filtered);
-       if (!skb) {
-               skb = skb_dequeue(&sta->ps_tx_buf);
-               if (skb)
-                       local->total_ps_buffered--;
+       if (qos) {
+               fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
+                                IEEE80211_STYPE_QOS_NULLFUNC |
+                                IEEE80211_FCTL_FROMDS);
+       } else {
+               size -= 2;
+               fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
+                                IEEE80211_STYPE_NULLFUNC |
+                                IEEE80211_FCTL_FROMDS);
+       }
+
+       skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
+       if (!skb)
+               return;
+
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+
+       nullfunc = (void *) skb_put(skb, size);
+       nullfunc->frame_control = fc;
+       nullfunc->duration_id = 0;
+       memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
+       memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
+       memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN);
+
+       skb->priority = tid;
+       skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]);
+       if (qos) {
+               nullfunc->qos_ctrl = cpu_to_le16(tid);
+
+               if (reason == IEEE80211_FRAME_RELEASE_UAPSD)
+                       nullfunc->qos_ctrl |=
+                               cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
        }
-       no_pending_pkts = skb_queue_empty(&sta->tx_filtered) &&
-               skb_queue_empty(&sta->ps_tx_buf);
 
-       if (skb) {
-               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-               struct ieee80211_hdr *hdr =
-                       (struct ieee80211_hdr *) skb->data;
+       info = IEEE80211_SKB_CB(skb);
+
+       /*
+        * Tell TX path to send this frame even though the
+        * STA may still remain is PS mode after this frame
+        * exchange. Also set EOSP to indicate this packet
+        * ends the poll/service period.
+        */
+       info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE |
+                      IEEE80211_TX_STATUS_EOSP |
+                      IEEE80211_TX_CTL_REQ_TX_STATUS;
+
+       drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
+
+       ieee80211_xmit(sdata, skb);
+}
+
+static void
+ieee80211_sta_ps_deliver_response(struct sta_info *sta,
+                                 int n_frames, u8 ignored_acs,
+                                 enum ieee80211_frame_release_type reason)
+{
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ieee80211_local *local = sdata->local;
+       bool found = false;
+       bool more_data = false;
+       int ac;
+       unsigned long driver_release_tids = 0;
+       struct sk_buff_head frames;
+
+       /* Service or PS-Poll period starts */
+       set_sta_flag(sta, WLAN_STA_SP);
+
+       __skb_queue_head_init(&frames);
+
+       /*
+        * Get response frame(s) and more data bit for it.
+        */
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               unsigned long tids;
+
+               if (ignored_acs & BIT(ac))
+                       continue;
+
+               tids = ieee80211_tids_for_ac(ac);
+
+               if (!found) {
+                       driver_release_tids = sta->driver_buffered_tids & tids;
+                       if (driver_release_tids) {
+                               found = true;
+                       } else {
+                               struct sk_buff *skb;
+
+                               while (n_frames > 0) {
+                                       skb = skb_dequeue(&sta->tx_filtered[ac]);
+                                       if (!skb) {
+                                               skb = skb_dequeue(
+                                                       &sta->ps_tx_buf[ac]);
+                                               if (skb)
+                                                       local->total_ps_buffered--;
+                                       }
+                                       if (!skb)
+                                               break;
+                                       n_frames--;
+                                       found = true;
+                                       __skb_queue_tail(&frames, skb);
+                               }
+                       }
+
+                       /*
+                        * If the driver has data on more than one TID then
+                        * certainly there's more data if we release just a
+                        * single frame now (from a single TID).
+                        */
+                       if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
+                           hweight16(driver_release_tids) > 1) {
+                               more_data = true;
+                               driver_release_tids =
+                                       BIT(ffs(driver_release_tids) - 1);
+                               break;
+                       }
+               }
+
+               if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
+                   !skb_queue_empty(&sta->ps_tx_buf[ac])) {
+                       more_data = true;
+                       break;
+               }
+       }
+
+       if (!found) {
+               int tid;
 
                /*
-                * Tell TX path to send this frame even though the STA may
-                * still remain is PS mode after this frame exchange.
+                * For PS-Poll, this can only happen due to a race condition
+                * when we set the TIM bit and the station notices it, but
+                * before it can poll for the frame we expire it.
+                *
+                * For uAPSD, this is said in the standard (11.2.1.5 h):
+                *      At each unscheduled SP for a non-AP STA, the AP shall
+                *      attempt to transmit at least one MSDU or MMPDU, but no
+                *      more than the value specified in the Max SP Length field
+                *      in the QoS Capability element from delivery-enabled ACs,
+                *      that are destined for the non-AP STA.
+                *
+                * Since we have no other MSDU/MMPDU, transmit a QoS null frame.
                 */
-               info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE;
 
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-               printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
-                      sta->sta.addr, sta->sta.aid,
-                      skb_queue_len(&sta->ps_tx_buf));
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+               /* This will evaluate to 1, 3, 5 or 7. */
+               tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
 
-               /* Use MoreData flag to indicate whether there are more
-                * buffered frames for this STA */
-               if (no_pending_pkts)
-                       hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
-               else
-                       hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+               ieee80211_send_null_response(sdata, sta, tid, reason);
+               return;
+       }
+
+       if (!driver_release_tids) {
+               struct sk_buff_head pending;
+               struct sk_buff *skb;
+               int num = 0;
+               u16 tids = 0;
+
+               skb_queue_head_init(&pending);
+
+               while ((skb = __skb_dequeue(&frames))) {
+                       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+                       struct ieee80211_hdr *hdr = (void *) skb->data;
+                       u8 *qoshdr = NULL;
+
+                       num++;
+
+                       /*
+                        * Tell TX path to send this frame even though the
+                        * STA may still remain is PS mode after this frame
+                        * exchange.
+                        */
+                       info->flags |= IEEE80211_TX_CTL_POLL_RESPONSE;
+
+                       /*
+                        * Use MoreData flag to indicate whether there are
+                        * more buffered frames for this STA
+                        */
+                       if (!more_data)
+                               hdr->frame_control &=
+                                       cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+                       else
+                               hdr->frame_control |=
+                                       cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+
+                       if (ieee80211_is_data_qos(hdr->frame_control) ||
+                           ieee80211_is_qos_nullfunc(hdr->frame_control))
+                               qoshdr = ieee80211_get_qos_ctl(hdr);
+
+                       /* set EOSP for the frame */
+                       if (reason == IEEE80211_FRAME_RELEASE_UAPSD &&
+                           qoshdr && skb_queue_empty(&frames))
+                               *qoshdr |= IEEE80211_QOS_CTL_EOSP;
+
+                       info->flags |= IEEE80211_TX_STATUS_EOSP |
+                                      IEEE80211_TX_CTL_REQ_TX_STATUS;
+
+                       if (qoshdr)
+                               tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK);
+                       else
+                               tids |= BIT(0);
+
+                       __skb_queue_tail(&pending, skb);
+               }
 
-               ieee80211_add_pending_skb(local, skb);
+               drv_allow_buffered_frames(local, sta, tids, num,
+                                         reason, more_data);
 
-               if (no_pending_pkts)
-                       sta_info_clear_tim_bit(sta);
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+               ieee80211_add_pending_skbs(local, &pending);
+
+               sta_info_recalc_tim(sta);
        } else {
                /*
-                * FIXME: This can be the result of a race condition between
-                *        us expiring a frame and the station polling for it.
-                *        Should we send it a null-func frame indicating we
-                *        have nothing buffered for it?
+                * We need to release a frame that is buffered somewhere in the
+                * driver ... it'll have to handle that.
+                * Note that, as per the comment above, it'll also have to see
+                * if there is more than just one frame on the specific TID that
+                * we're releasing from, and it needs to set the more-data bit
+                * accordingly if we tell it that there's no more data. If we do
+                * tell it there's more data, then of course the more-data bit
+                * needs to be set anyway.
+                */
+               drv_release_buffered_frames(local, sta, driver_release_tids,
+                                           n_frames, reason, more_data);
+
+               /*
+                * Note that we don't recalculate the TIM bit here as it would
+                * most likely have no effect at all unless the driver told us
+                * that the TID became empty before returning here from the
+                * release function.
+                * Either way, however, when the driver tells us that the TID
+                * became empty we'll do the TIM recalculation.
                 */
-               printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
-                      "though there are no buffered frames for it\n",
-                      sdata->name, sta->sta.addr);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
        }
 }
 
+void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
+{
+       u8 ignore_for_response = sta->sta.uapsd_queues;
+
+       /*
+        * If all ACs are delivery-enabled then we should reply
+        * from any of them, if only some are enabled we reply
+        * only from the non-enabled ones.
+        */
+       if (ignore_for_response == BIT(IEEE80211_NUM_ACS) - 1)
+               ignore_for_response = 0;
+
+       ieee80211_sta_ps_deliver_response(sta, 1, ignore_for_response,
+                                         IEEE80211_FRAME_RELEASE_PSPOLL);
+}
+
+void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta)
+{
+       int n_frames = sta->sta.max_sp;
+       u8 delivery_enabled = sta->sta.uapsd_queues;
+
+       /*
+        * If we ever grow support for TSPEC this might happen if
+        * the TSPEC update from hostapd comes in between a trigger
+        * frame setting WLAN_STA_UAPSD in the RX path and this
+        * actually getting called.
+        */
+       if (!delivery_enabled)
+               return;
+
+       switch (sta->sta.max_sp) {
+       case 1:
+               n_frames = 2;
+               break;
+       case 2:
+               n_frames = 4;
+               break;
+       case 3:
+               n_frames = 6;
+               break;
+       case 0:
+               /* XXX: what is a good value? */
+               n_frames = 8;
+               break;
+       }
+
+       ieee80211_sta_ps_deliver_response(sta, n_frames, ~delivery_enabled,
+                                         IEEE80211_FRAME_RELEASE_UAPSD);
+}
+
 void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
                               struct ieee80211_sta *pubsta, bool block)
 {
@@ -984,17 +1470,50 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
        trace_api_sta_block_awake(sta->local, pubsta, block);
 
        if (block)
-               set_sta_flags(sta, WLAN_STA_PS_DRIVER);
-       else if (test_sta_flags(sta, WLAN_STA_PS_DRIVER))
+               set_sta_flag(sta, WLAN_STA_PS_DRIVER);
+       else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER))
                ieee80211_queue_work(hw, &sta->drv_unblock_wk);
 }
 EXPORT_SYMBOL(ieee80211_sta_block_awake);
 
-void ieee80211_sta_set_tim(struct ieee80211_sta *pubsta)
+void ieee80211_sta_eosp_irqsafe(struct ieee80211_sta *pubsta)
 {
        struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+       struct ieee80211_local *local = sta->local;
+       struct sk_buff *skb;
+       struct skb_eosp_msg_data *data;
+
+       trace_api_eosp(local, pubsta);
+
+       skb = alloc_skb(0, GFP_ATOMIC);
+       if (!skb) {
+               /* too bad ... but race is better than loss */
+               clear_sta_flag(sta, WLAN_STA_SP);
+               return;
+       }
+
+       data = (void *)skb->cb;
+       memcpy(data->sta, pubsta->addr, ETH_ALEN);
+       memcpy(data->iface, sta->sdata->vif.addr, ETH_ALEN);
+       skb->pkt_type = IEEE80211_EOSP_MSG;
+       skb_queue_tail(&local->skb_queue, skb);
+       tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_sta_eosp_irqsafe);
+
+void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
+                               u8 tid, bool buffered)
+{
+       struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+
+       if (WARN_ON(tid >= STA_TID_NUM))
+               return;
+
+       if (buffered)
+               set_bit(tid, &sta->driver_buffered_tids);
+       else
+               clear_bit(tid, &sta->driver_buffered_tids);
 
-       set_sta_flags(sta, WLAN_STA_PS_DRIVER_BUF);
-       sta_info_set_tim_bit(sta);
+       sta_info_recalc_tim(sta);
 }
-EXPORT_SYMBOL(ieee80211_sta_set_tim);
+EXPORT_SYMBOL(ieee80211_sta_set_buffered);