Merge branch 'master' of git://git.infradead.org/users/linville/wireless-next into...
[pandora-kernel.git] / net / mac80211 / sta_info.c
index b3f8419..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;
        }
 
@@ -231,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)
@@ -243,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,
@@ -282,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);
@@ -861,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);
@@ -882,10 +891,13 @@ 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_recalc_tim(sta);
        }
@@ -894,7 +906,7 @@ static int __must_check __sta_info_destroy(struct sta_info *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)
@@ -1106,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 */
@@ -1117,6 +1130,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
        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;
 
@@ -1152,32 +1167,94 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 #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_local *local = sdata->local;
+       struct ieee80211_qos_hdr *nullfunc;
+       struct sk_buff *skb;
+       int size = sizeof(*nullfunc);
+       __le16 fc;
+       bool qos = test_sta_flag(sta, WLAN_STA_WME);
+       struct ieee80211_tx_info *info;
+
+       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);
+       }
+
+       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;
-       struct sk_buff *skb = NULL;
        bool found = false;
        bool more_data = false;
        int ac;
        unsigned long driver_release_tids = 0;
-       u8 ignore_for_response = sta->sta.uapsd_queues;
+       struct sk_buff_head frames;
 
-       /*
-        * 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;
+       /* Service or PS-Poll period starts */
+       set_sta_flag(sta, WLAN_STA_SP);
+
+       __skb_queue_head_init(&frames);
 
        /*
-        * Get response frame and more data bit for it.
+        * Get response frame(s) and more data bit for it.
         */
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                unsigned long tids;
 
-               if (ignore_for_response & BIT(ac))
+               if (ignored_acs & BIT(ac))
                        continue;
 
                tids = ieee80211_tids_for_ac(ac);
@@ -1187,14 +1264,22 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
                        if (driver_release_tids) {
                                found = true;
                        } else {
-                               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)
+                               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);
+                               }
                        }
 
                        /*
@@ -1202,7 +1287,8 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
                         * certainly there's more data if we release just a
                         * single frame now (from a single TID).
                         */
-                       if (hweight16(driver_release_tids) > 1) {
+                       if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
+                           hweight16(driver_release_tids) > 1) {
                                more_data = true;
                                driver_release_tids =
                                        BIT(ffs(driver_release_tids) - 1);
@@ -1218,45 +1304,87 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
        }
 
        if (!found) {
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+               int tid;
+
                /*
-                * 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?
+                * 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.
                 */
-               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 */
 
+               /* This will evaluate to 1, 3, 5 or 7. */
+               tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
+
+               ieee80211_send_null_response(sdata, sta, tid, reason);
                return;
        }
 
-       if (skb) {
-               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-               struct ieee80211_hdr *hdr =
-                       (struct ieee80211_hdr *) skb->data;
+       if (!driver_release_tids) {
+               struct sk_buff_head pending;
+               struct sk_buff *skb;
+               int num = 0;
+               u16 tids = 0;
 
-               /*
-                * 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_PSPOLL_RESPONSE;
+               skb_queue_head_init(&pending);
 
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-               printk(KERN_DEBUG "STA %pM aid %d: PS Poll\n",
-                      sta->sta.addr, sta->sta.aid);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+               while ((skb = __skb_dequeue(&frames))) {
+                       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+                       struct ieee80211_hdr *hdr = (void *) skb->data;
+                       u8 *qoshdr = NULL;
 
-               /* 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);
+                       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;
 
-               ieee80211_add_pending_skb(local, skb);
+                       /*
+                        * 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);
+               }
+
+               drv_allow_buffered_frames(local, sta, tids, num,
+                                         reason, more_data);
+
+               ieee80211_add_pending_skbs(local, &pending);
 
                sta_info_recalc_tim(sta);
        } else {
@@ -1271,8 +1399,7 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
                 * needs to be set anyway.
                 */
                drv_release_buffered_frames(local, sta, driver_release_tids,
-                                           1, IEEE80211_FRAME_RELEASE_PSPOLL,
-                                           more_data);
+                                           n_frames, reason, more_data);
 
                /*
                 * Note that we don't recalculate the TIM bit here as it would
@@ -1285,6 +1412,56 @@ void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
        }
 }
 
+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)
 {
@@ -1293,12 +1470,37 @@ 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_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)
 {