Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[pandora-kernel.git] / drivers / net / wireless / ath / carl9170 / tx.c
index 7e6506a..0ef70b6 100644 (file)
@@ -242,9 +242,11 @@ static void carl9170_tx_release(struct kref *ref)
                        ar->tx_ampdu_schedule = true;
 
                if (txinfo->flags & IEEE80211_TX_STAT_AMPDU) {
-                       txinfo->status.ampdu_len = txinfo->pad[0];
-                       txinfo->status.ampdu_ack_len = txinfo->pad[1];
-                       txinfo->pad[0] = txinfo->pad[1] = 0;
+                       struct _carl9170_tx_superframe *super;
+
+                       super = (void *)skb->data;
+                       txinfo->status.ampdu_len = super->s.rix;
+                       txinfo->status.ampdu_ack_len = super->s.cnt;
                } else if (txinfo->flags & IEEE80211_TX_STAT_ACK) {
                        /*
                         * drop redundant tx_status reports:
@@ -337,7 +339,8 @@ static void carl9170_tx_status_process_ampdu(struct ar9170 *ar,
        u8 tid;
 
        if (!(txinfo->flags & IEEE80211_TX_CTL_AMPDU) ||
-           txinfo->flags & IEEE80211_TX_CTL_INJECTED)
+           txinfo->flags & IEEE80211_TX_CTL_INJECTED ||
+          (!(super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_AGGR))))
                return;
 
        tx_info = IEEE80211_SKB_CB(skb);
@@ -389,8 +392,8 @@ static void carl9170_tx_status_process_ampdu(struct ar9170 *ar,
                sta_info->stats[tid].ampdu_ack_len++;
 
        if (super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_IMM_BA)) {
-               txinfo->pad[0] = sta_info->stats[tid].ampdu_len;
-               txinfo->pad[1] = sta_info->stats[tid].ampdu_ack_len;
+               super->s.rix = sta_info->stats[tid].ampdu_len;
+               super->s.cnt = sta_info->stats[tid].ampdu_ack_len;
                txinfo->flags |= IEEE80211_TX_STAT_AMPDU;
                sta_info->stats[tid].clear = true;
        }
@@ -524,6 +527,59 @@ next:
        }
 }
 
+static void carl9170_tx_ampdu_timeout(struct ar9170 *ar)
+{
+       struct carl9170_sta_tid *iter;
+       struct sk_buff *skb;
+       struct ieee80211_tx_info *txinfo;
+       struct carl9170_tx_info *arinfo;
+       struct _carl9170_tx_superframe *super;
+       struct ieee80211_sta *sta;
+       struct ieee80211_vif *vif;
+       struct ieee80211_hdr *hdr;
+       unsigned int vif_id;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(iter, &ar->tx_ampdu_list, list) {
+               if (iter->state < CARL9170_TID_STATE_IDLE)
+                       continue;
+
+               spin_lock_bh(&iter->lock);
+               skb = skb_peek(&iter->queue);
+               if (!skb)
+                       goto unlock;
+
+               txinfo = IEEE80211_SKB_CB(skb);
+               arinfo = (void *)txinfo->rate_driver_data;
+               if (time_is_after_jiffies(arinfo->timeout +
+                   msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT)))
+                       goto unlock;
+
+               super = (void *) skb->data;
+               hdr = (void *) super->frame_data;
+
+               vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >>
+                        CARL9170_TX_SUPER_MISC_VIF_ID_S;
+
+               if (WARN_ON(vif_id >= AR9170_MAX_VIRTUAL_MAC))
+                       goto unlock;
+
+               vif = rcu_dereference(ar->vif_priv[vif_id].vif);
+               if (WARN_ON(!vif))
+                       goto unlock;
+
+               sta = ieee80211_find_sta(vif, hdr->addr1);
+               if (WARN_ON(!sta))
+                       goto unlock;
+
+               ieee80211_stop_tx_ba_session(sta, iter->tid);
+unlock:
+               spin_unlock_bh(&iter->lock);
+
+       }
+       rcu_read_unlock();
+}
+
 void carl9170_tx_janitor(struct work_struct *work)
 {
        struct ar9170 *ar = container_of(work, struct ar9170,
@@ -534,6 +590,7 @@ void carl9170_tx_janitor(struct work_struct *work)
        ar->tx_janitor_last_run = jiffies;
 
        carl9170_check_queue_stop_timeout(ar);
+       carl9170_tx_ampdu_timeout(ar);
 
        if (!atomic_read(&ar->tx_total_queued))
                return;
@@ -805,6 +862,9 @@ static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
        if (unlikely(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM))
                txc->s.misc |= CARL9170_TX_SUPER_MISC_CAB;
 
+       if (unlikely(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
+               txc->s.misc |= CARL9170_TX_SUPER_MISC_ASSIGN_SEQ;
+
        if (unlikely(ieee80211_is_probe_resp(hdr->frame_control)))
                txc->s.misc |= CARL9170_TX_SUPER_MISC_FILL_IN_TSF;
 
@@ -842,10 +902,8 @@ static int carl9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
                if (unlikely(!sta || !cvif))
                        goto err_out;
 
-               factor = min_t(unsigned int, 1u,
-                        info->control.sta->ht_cap.ampdu_factor);
-
-               density = info->control.sta->ht_cap.ampdu_density;
+               factor = min_t(unsigned int, 1u, sta->ht_cap.ampdu_factor);
+               density = sta->ht_cap.ampdu_density;
 
                if (density) {
                        /*
@@ -1206,6 +1264,7 @@ static void carl9170_tx(struct ar9170 *ar)
 static bool carl9170_tx_ampdu_queue(struct ar9170 *ar,
        struct ieee80211_sta *sta, struct sk_buff *skb)
 {
+       struct _carl9170_tx_superframe *super = (void *) skb->data;
        struct carl9170_sta_info *sta_info;
        struct carl9170_sta_tid *agg;
        struct sk_buff *iter;
@@ -1274,12 +1333,13 @@ err_unlock:
 
 err_unlock_rcu:
        rcu_read_unlock();
+       super->f.mac_control &= ~cpu_to_le16(AR9170_TX_MAC_AGGR);
        carl9170_tx_status(ar, skb, false);
        ar->tx_dropped++;
        return false;
 }
 
-int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct ar9170 *ar = hw->priv;
        struct ieee80211_tx_info *info;
@@ -1302,9 +1362,6 @@ int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
         */
 
        if (info->flags & IEEE80211_TX_CTL_AMPDU) {
-               if (WARN_ON_ONCE(!sta))
-                       goto err_free;
-
                run = carl9170_tx_ampdu_queue(ar, sta, skb);
                if (run)
                        carl9170_tx_ampdu(ar);
@@ -1316,12 +1373,11 @@ int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
        }
 
        carl9170_tx(ar);
-       return NETDEV_TX_OK;
+       return;
 
 err_free:
        ar->tx_dropped++;
        dev_kfree_skb_any(skb);
-       return NETDEV_TX_OK;
 }
 
 void carl9170_tx_scheduler(struct ar9170 *ar)