#include "tkip.h"
 #include "wme.h"
 
-static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
-                                        struct sk_buff *skb,
-                                        struct ieee80211_rate *rate);
-
 /*
  * monitor mode reception
  *
 
 static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw,
                                            struct tid_ampdu_rx *tid_agg_rx,
-                                           int index)
+                                           int index,
+                                           struct sk_buff_head *frames)
 {
        struct ieee80211_supported_band *sband;
        struct ieee80211_rate *rate = NULL;
        sband = hw->wiphy->bands[status->band];
        if (!(status->flag & RX_FLAG_HT))
                rate = &sband->bitrates[status->rate_idx];
-       __ieee80211_rx_handle_packet(hw, skb, rate);
        tid_agg_rx->stored_mpdu_num--;
        tid_agg_rx->reorder_buf[index] = NULL;
+       skb_queue_tail(frames, skb);
 
 no_frame:
        tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
 
 static void ieee80211_release_reorder_frames(struct ieee80211_hw *hw,
                                             struct tid_ampdu_rx *tid_agg_rx,
-                                            u16 head_seq_num)
+                                            u16 head_seq_num,
+                                            struct sk_buff_head *frames)
 {
        int index;
 
        while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) {
                index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
                                                        tid_agg_rx->buf_size;
-               ieee80211_release_reorder_frame(hw, tid_agg_rx, index);
+               ieee80211_release_reorder_frame(hw, tid_agg_rx, index, frames);
        }
 }
 
  */
 static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
                                             struct tid_ampdu_rx *tid_agg_rx,
-                                            struct sk_buff *skb)
+                                            struct sk_buff *skb,
+                                            struct sk_buff_head *frames)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        u16 sc = le16_to_cpu(hdr->seq_ctrl);
        if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) {
                head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size));
                /* release stored frames up to new head to stack */
-               ieee80211_release_reorder_frames(hw, tid_agg_rx, head_seq_num);
+               ieee80211_release_reorder_frames(hw, tid_agg_rx, head_seq_num,
+                                                frames);
        }
 
        /* Now the new frame is always in the range of the reordering buffer */
                                       "frames\n",
                                       wiphy_name(hw->wiphy));
 #endif
-                       ieee80211_release_reorder_frame(hw, tid_agg_rx, j);
+                       ieee80211_release_reorder_frame(hw, tid_agg_rx,
+                                                       j, frames);
 
                        /*
                         * Increment the head seq# also for the skipped slots.
                        skipped = 0;
                }
        } else while (tid_agg_rx->reorder_buf[index]) {
-               ieee80211_release_reorder_frame(hw, tid_agg_rx, index);
+               ieee80211_release_reorder_frame(hw, tid_agg_rx, index, frames);
                index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
                                                        tid_agg_rx->buf_size;
        }
  * Reorder MPDUs from A-MPDUs, keeping them on a buffer. Returns
  * true if the MPDU was buffered, false if it should be processed.
  */
-static bool ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
-                                      struct sk_buff *skb)
+static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
+                                      struct sk_buff_head *frames)
 {
+       struct sk_buff *skb = rx->skb;
+       struct ieee80211_local *local = rx->local;
        struct ieee80211_hw *hw = &local->hw;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-       struct sta_info *sta;
+       struct sta_info *sta = rx->sta;
        struct tid_ampdu_rx *tid_agg_rx;
        u16 sc;
        int tid;
 
        if (!ieee80211_is_data_qos(hdr->frame_control))
-               return false;
+               goto dont_reorder;
 
        /*
         * filter the QoS data rx stream according to
         * STA/TID and check if this STA/TID is on aggregation
         */
 
-       sta = sta_info_get(local, hdr->addr2);
        if (!sta)
-               return false;
+               goto dont_reorder;
 
        tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
 
        if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL)
-               return false;
+               goto dont_reorder;
 
        tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
 
        /* qos null data frames are excluded */
        if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
-               return false;
+               goto dont_reorder;
 
        /* new, potentially un-ordered, ampdu frame - process it */
 
                ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->sta.addr,
                        tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
                dev_kfree_skb(skb);
-               return true;
+               return;
        }
 
-       return ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb);
+       if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames))
+               return;
+
+ dont_reorder:
+       __skb_queue_tail(frames, skb);
 }
 
 static ieee80211_rx_result debug_noinline
        if (!(rx->flags & IEEE80211_RX_RA_MATCH))
                return RX_CONTINUE;
 
+       /* start without a key */
+       rx->key = NULL;
+
        if (rx->sta)
                stakey = rcu_dereference(rx->sta->key);
 
 }
 
 static ieee80211_rx_result debug_noinline
-ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
+ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
 {
        struct ieee80211_local *local = rx->local;
        struct ieee80211_hw *hw = &local->hw;
                                  TU_TO_EXP_TIME(tid_agg_rx->timeout));
 
                /* release stored frames up to start of BAR */
-               ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num);
+               ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num,
+                                                frames);
                kfree_skb(skb);
                return RX_QUEUED;
        }
                                         struct sk_buff *skb,
                                         struct ieee80211_rate *rate)
 {
+       struct sk_buff_head reorder_release;
        ieee80211_rx_result res = RX_DROP_MONITOR;
 
+       __skb_queue_head_init(&reorder_release);
+
        rx->skb = skb;
        rx->sdata = sdata;
 
        do {                            \
                res = rxh(rx);          \
                if (res != RX_CONTINUE) \
-                       goto rxh_done;  \
+                       goto rxh_next;  \
        } while (0);
 
+       /*
+        * NB: the rxh_next label works even if we jump
+        *     to it from here because then the list will
+        *     be empty, which is a trivial check
+        */
        CALL_RXH(ieee80211_rx_h_passive_scan)
        CALL_RXH(ieee80211_rx_h_check)
-       CALL_RXH(ieee80211_rx_h_decrypt)
-       CALL_RXH(ieee80211_rx_h_check_more_data)
-       CALL_RXH(ieee80211_rx_h_sta_process)
-       CALL_RXH(ieee80211_rx_h_defragment)
-       CALL_RXH(ieee80211_rx_h_ps_poll)
-       CALL_RXH(ieee80211_rx_h_michael_mic_verify)
-       /* must be after MMIC verify so header is counted in MPDU mic */
-       CALL_RXH(ieee80211_rx_h_remove_qos_control)
-       CALL_RXH(ieee80211_rx_h_amsdu)
+
+       ieee80211_rx_reorder_ampdu(rx, &reorder_release);
+
+       while ((skb = __skb_dequeue(&reorder_release))) {
+               /*
+                * all the other fields are valid across frames
+                * that belong to an aMPDU since they are on the
+                * same TID from the same station
+                */
+               rx->skb = skb;
+
+               CALL_RXH(ieee80211_rx_h_decrypt)
+               CALL_RXH(ieee80211_rx_h_check_more_data)
+               CALL_RXH(ieee80211_rx_h_sta_process)
+               CALL_RXH(ieee80211_rx_h_defragment)
+               CALL_RXH(ieee80211_rx_h_ps_poll)
+               CALL_RXH(ieee80211_rx_h_michael_mic_verify)
+               /* must be after MMIC verify so header is counted in MPDU mic */
+               CALL_RXH(ieee80211_rx_h_remove_qos_control)
+               CALL_RXH(ieee80211_rx_h_amsdu)
 #ifdef CONFIG_MAC80211_MESH
-       if (ieee80211_vif_is_mesh(&sdata->vif))
-               CALL_RXH(ieee80211_rx_h_mesh_fwding);
+               if (ieee80211_vif_is_mesh(&sdata->vif))
+                       CALL_RXH(ieee80211_rx_h_mesh_fwding);
 #endif
-       CALL_RXH(ieee80211_rx_h_data)
-       CALL_RXH(ieee80211_rx_h_ctrl)
-       CALL_RXH(ieee80211_rx_h_action)
-       CALL_RXH(ieee80211_rx_h_mgmt)
+               CALL_RXH(ieee80211_rx_h_data)
+
+               /* special treatment -- needs the queue */
+               res = ieee80211_rx_h_ctrl(rx, &reorder_release);
+               if (res != RX_CONTINUE)
+                       goto rxh_next;
+
+               CALL_RXH(ieee80211_rx_h_action)
+               CALL_RXH(ieee80211_rx_h_mgmt)
 
 #undef CALL_RXH
 
- rxh_done:
-       switch (res) {
-       case RX_DROP_MONITOR:
-               I802_DEBUG_INC(sdata->local->rx_handlers_drop);
-               if (rx->sta)
-                       rx->sta->rx_dropped++;
-               /* fall through */
-       case RX_CONTINUE:
-               ieee80211_rx_cooked_monitor(rx, rate);
-               break;
-       case RX_DROP_UNUSABLE:
-               I802_DEBUG_INC(sdata->local->rx_handlers_drop);
-               if (rx->sta)
-                       rx->sta->rx_dropped++;
-               dev_kfree_skb(rx->skb);
-               break;
-       case RX_QUEUED:
-               I802_DEBUG_INC(sdata->local->rx_handlers_queued);
-               break;
+ rxh_next:
+               switch (res) {
+               case RX_DROP_MONITOR:
+                       I802_DEBUG_INC(sdata->local->rx_handlers_drop);
+                       if (rx->sta)
+                               rx->sta->rx_dropped++;
+                       /* fall through */
+               case RX_CONTINUE:
+                       ieee80211_rx_cooked_monitor(rx, rate);
+                       break;
+               case RX_DROP_UNUSABLE:
+                       I802_DEBUG_INC(sdata->local->rx_handlers_drop);
+                       if (rx->sta)
+                               rx->sta->rx_dropped++;
+                       dev_kfree_skb(rx->skb);
+                       break;
+               case RX_QUEUED:
+                       I802_DEBUG_INC(sdata->local->rx_handlers_queued);
+                       break;
+               }
        }
 }
 
                return;
        }
 
-       /*
-        * In theory, the block ack reordering should happen after duplicate
-        * removal (ieee80211_rx_h_check(), which is an RX handler). As such,
-        * the call to ieee80211_rx_reorder_ampdu() should really be moved to
-        * happen as a new RX handler between ieee80211_rx_h_check and
-        * ieee80211_rx_h_decrypt. This cleanup may eventually happen, but for
-        * the time being, the call can be here since RX reorder buf processing
-        * will implicitly skip duplicates. We could, in theory at least,
-        * process frames that ieee80211_rx_h_passive_scan would drop (e.g.,
-        * frames from other than operational channel), but that should not
-        * happen in normal networks.
-        */
-       if (!ieee80211_rx_reorder_ampdu(local, skb))
-               __ieee80211_rx_handle_packet(hw, skb, rate);
+       __ieee80211_rx_handle_packet(hw, skb, rate);
 
        rcu_read_unlock();