Merge branch 'master' of git://git.infradead.org/users/linville/wireless-next into...
[pandora-kernel.git] / net / mac80211 / sta_info.c
index 5732e4d..ce962d2 100644 (file)
@@ -73,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;
        }
@@ -83,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;
        }
 
@@ -232,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)
@@ -244,22 +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_flags(sta, WLAN_STA_UAPSD)) {
-               clear_sta_flags(sta, WLAN_STA_PS_DRIVER);
+       } 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,
@@ -292,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);
@@ -871,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);
@@ -892,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);
        }
@@ -904,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)
@@ -1116,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 */
@@ -1127,7 +1130,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
        struct sk_buff_head pending;
        int filtered = 0, buffered = 0, ac;
 
-       clear_sta_flags(sta, WLAN_STA_SP);
+       clear_sta_flag(sta, WLAN_STA_SP);
 
        BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1);
        sta->driver_buffered_tids = 0;
@@ -1166,14 +1169,14 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
 static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
                                         struct sta_info *sta, int tid,
-                                        bool uapsd)
+                                        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_flags(sta, WLAN_STA_WME);
+       bool qos = test_sta_flag(sta, WLAN_STA_WME);
        struct ieee80211_tx_info *info;
 
        if (qos) {
@@ -1200,14 +1203,12 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
        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) {
-               skb->priority = tid;
-
-               skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]);
-
                nullfunc->qos_ctrl = cpu_to_le16(tid);
 
-               if (uapsd)
+               if (reason == IEEE80211_FRAME_RELEASE_UAPSD)
                        nullfunc->qos_ctrl |=
                                cpu_to_le16(IEEE80211_QOS_CTL_EOSP);
        }
@@ -1224,6 +1225,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
                       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);
 }
 
@@ -1241,7 +1244,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
        struct sk_buff_head frames;
 
        /* Service or PS-Poll period starts */
-       set_sta_flags(sta, WLAN_STA_SP);
+       set_sta_flag(sta, WLAN_STA_SP);
 
        __skb_queue_head_init(&frames);
 
@@ -1321,20 +1324,24 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                /* This will evaluate to 1, 3, 5 or 7. */
                tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
 
-               ieee80211_send_null_response(sdata, sta, tid,
-                               reason == IEEE80211_FRAME_RELEASE_UAPSD);
+               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
@@ -1354,19 +1361,29 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                                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 &&
-                           skb_queue_empty(&frames)) {
-                               /* set EOSP for the frame */
-                               u8 *p = ieee80211_get_qos_ctl(hdr);
-                               *p |= IEEE80211_QOS_CTL_EOSP;
-                       }
+                           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);
@@ -1453,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)
 {