]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/rx.c
Merge branches 'release' and 'wmi-2.6.25' into release
[~andy/linux] / net / mac80211 / rx.c
index a58a94b2140101bd522cd35d54b3b01b8d25ba69..535407d07fa40890a4731648f85157a00fb39dc8 100644 (file)
 #include "tkip.h"
 #include "wme.h"
 
+u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+                               struct tid_ampdu_rx *tid_agg_rx,
+                               struct sk_buff *skb, u16 mpdu_seq_num,
+                               int bar_req);
 /*
  * monitor mode reception
  *
@@ -64,7 +68,9 @@ static inline int should_drop_frame(struct ieee80211_rx_status *status,
        if (((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FTYPE)) ==
                        cpu_to_le16(IEEE80211_FTYPE_CTL)) &&
            ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE)) !=
-                       cpu_to_le16(IEEE80211_STYPE_PSPOLL)))
+                       cpu_to_le16(IEEE80211_STYPE_PSPOLL)) &&
+           ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_STYPE)) !=
+                       cpu_to_le16(IEEE80211_STYPE_BACK_REQ)))
                return 1;
        return 0;
 }
@@ -217,7 +223,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
                if (!netif_running(sdata->dev))
                        continue;
 
-               if (sdata->type != IEEE80211_IF_TYPE_MNTR)
+               if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR)
                        continue;
 
                if (prev_dev) {
@@ -289,7 +295,7 @@ ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx)
 }
 
 
-u32 ieee80211_rx_load_stats(struct ieee80211_local *local,
+static u32 ieee80211_rx_load_stats(struct ieee80211_local *local,
                              struct sk_buff *skb,
                              struct ieee80211_rx_status *status)
 {
@@ -330,14 +336,53 @@ u32 ieee80211_rx_load_stats(struct ieee80211_local *local,
 
        /* Divide channel_use by 8 to avoid wrapping around the counter */
        load >>= CHAN_UTIL_SHIFT;
-       local->channel_use_raw += load;
 
        return load;
 }
 
+#ifdef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT
+static ieee80211_txrx_result
+ieee80211_rx_h_verify_ip_alignment(struct ieee80211_txrx_data *rx)
+{
+       int hdrlen;
+
+       if (!WLAN_FC_DATA_PRESENT(rx->fc))
+               return TXRX_CONTINUE;
+
+       /*
+        * Drivers are required to align the payload data in a way that
+        * guarantees that the contained IP header is aligned to a four-
+        * byte boundary. In the case of regular frames, this simply means
+        * aligning the payload to a four-byte boundary (because either
+        * the IP header is directly contained, or IV/RFC1042 headers that
+        * have a length divisible by four are in front of it.
+        *
+        * With A-MSDU frames, however, the payload data address must
+        * yield two modulo four because there are 14-byte 802.3 headers
+        * within the A-MSDU frames that push the IP header further back
+        * to a multiple of four again. Thankfully, the specs were sane
+        * enough this time around to require padding each A-MSDU subframe
+        * to a length that is a multiple of four.
+        *
+        * Padding like atheros hardware adds which is inbetween the 802.11
+        * header and the payload is not supported, the driver is required
+        * to move the 802.11 header further back in that case.
+        */
+       hdrlen = ieee80211_get_hdrlen(rx->fc);
+       if (rx->flags & IEEE80211_TXRXD_RX_AMSDU)
+               hdrlen += ETH_HLEN;
+       WARN_ON_ONCE(((unsigned long)(rx->skb->data + hdrlen)) & 3);
+
+       return TXRX_CONTINUE;
+}
+#endif
+
 ieee80211_rx_handler ieee80211_rx_pre_handlers[] =
 {
        ieee80211_rx_h_parse_qos,
+#ifdef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT
+       ieee80211_rx_h_verify_ip_alignment,
+#endif
        NULL
 };
 
@@ -414,7 +459,7 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
        if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
                      ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
                       (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
-                    rx->sdata->type != IEEE80211_IF_TYPE_IBSS &&
+                    rx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
                     (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) {
                if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
                     !(rx->fc & IEEE80211_FCTL_TODS) &&
@@ -634,13 +679,14 @@ ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
        /* Update last_rx only for IBSS packets which are for the current
         * BSSID to avoid keeping the current IBSS network alive in cases where
         * other STAs are using different BSSID. */
-       if (rx->sdata->type == IEEE80211_IF_TYPE_IBSS) {
-               u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len);
+       if (rx->sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
+               u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
+                                               IEEE80211_IF_TYPE_IBSS);
                if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0)
                        sta->last_rx = jiffies;
        } else
        if (!is_multicast_ether_addr(hdr->addr1) ||
-           rx->sdata->type == IEEE80211_IF_TYPE_STA) {
+           rx->sdata->vif.type == IEEE80211_IF_TYPE_STA) {
                /* Update last_rx only for unicast frames in order to prevent
                 * the Probe Request frames (the only broadcast frames from a
                 * STA in infrastructure mode) from keeping a connection alive.
@@ -895,8 +941,8 @@ ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
                   !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)))
                return TXRX_CONTINUE;
 
-       if ((sdata->type != IEEE80211_IF_TYPE_AP) &&
-           (sdata->type != IEEE80211_IF_TYPE_VLAN))
+       if ((sdata->vif.type != IEEE80211_IF_TYPE_AP) &&
+           (sdata->vif.type != IEEE80211_IF_TYPE_VLAN))
                return TXRX_DROP;
 
        skb = skb_dequeue(&rx->sta->tx_filtered);
@@ -1052,8 +1098,8 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
                memcpy(dst, hdr->addr3, ETH_ALEN);
                memcpy(src, hdr->addr2, ETH_ALEN);
 
-               if (unlikely(sdata->type != IEEE80211_IF_TYPE_AP &&
-                            sdata->type != IEEE80211_IF_TYPE_VLAN)) {
+               if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_AP &&
+                            sdata->vif.type != IEEE80211_IF_TYPE_VLAN)) {
                        if (net_ratelimit())
                                printk(KERN_DEBUG "%s: dropped ToDS frame "
                                       "(BSSID=%s SA=%s DA=%s)\n",
@@ -1069,7 +1115,7 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
                memcpy(dst, hdr->addr3, ETH_ALEN);
                memcpy(src, hdr->addr4, ETH_ALEN);
 
-               if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS)) {
+               if (unlikely(sdata->vif.type != IEEE80211_IF_TYPE_WDS)) {
                        if (net_ratelimit())
                                printk(KERN_DEBUG "%s: dropped FromDS&ToDS "
                                       "frame (RA=%s TA=%s DA=%s SA=%s)\n",
@@ -1086,7 +1132,7 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
                memcpy(dst, hdr->addr1, ETH_ALEN);
                memcpy(src, hdr->addr3, ETH_ALEN);
 
-               if (sdata->type != IEEE80211_IF_TYPE_STA ||
+               if (sdata->vif.type != IEEE80211_IF_TYPE_STA ||
                    (is_multicast_ether_addr(dst) &&
                     !compare_ether_addr(src, dev->dev_addr)))
                        return -1;
@@ -1096,7 +1142,7 @@ ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
                memcpy(dst, hdr->addr1, ETH_ALEN);
                memcpy(src, hdr->addr2, ETH_ALEN);
 
-               if (sdata->type != IEEE80211_IF_TYPE_IBSS) {
+               if (sdata->vif.type != IEEE80211_IF_TYPE_IBSS) {
                        if (net_ratelimit()) {
                                printk(KERN_DEBUG "%s: dropped IBSS frame "
                                       "(DA=%s SA=%s BSSID=%s)\n",
@@ -1184,8 +1230,8 @@ ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
        skb = rx->skb;
        xmit_skb = NULL;
 
-       if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP ||
-                                     sdata->type == IEEE80211_IF_TYPE_VLAN) &&
+       if (local->bridge_packets && (sdata->vif.type == IEEE80211_IF_TYPE_AP ||
+                                     sdata->vif.type == IEEE80211_IF_TYPE_VLAN) &&
            (rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
                if (is_multicast_ether_addr(ehdr->h_dest)) {
                        /*
@@ -1377,6 +1423,49 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
        return TXRX_QUEUED;
 }
 
+static ieee80211_txrx_result
+ieee80211_rx_h_ctrl(struct ieee80211_txrx_data *rx)
+{
+       struct ieee80211_local *local = rx->local;
+       struct ieee80211_hw *hw = &local->hw;
+       struct sk_buff *skb = rx->skb;
+       struct ieee80211_bar *bar = (struct ieee80211_bar *) skb->data;
+       struct tid_ampdu_rx *tid_agg_rx;
+       u16 start_seq_num;
+       u16 tid;
+
+       if (likely((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL))
+               return TXRX_CONTINUE;
+
+       if ((rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BACK_REQ) {
+               if (!rx->sta)
+                       return TXRX_CONTINUE;
+               tid = le16_to_cpu(bar->control) >> 12;
+               tid_agg_rx = &(rx->sta->ampdu_mlme.tid_rx[tid]);
+               if (tid_agg_rx->state != HT_AGG_STATE_OPERATIONAL)
+                       return TXRX_CONTINUE;
+
+               start_seq_num = le16_to_cpu(bar->start_seq_num) >> 4;
+
+               /* reset session timer */
+               if (tid_agg_rx->timeout) {
+                       unsigned long expires =
+                               jiffies + (tid_agg_rx->timeout / 1000) * HZ;
+                       mod_timer(&tid_agg_rx->session_timer, expires);
+               }
+
+               /* manage reordering buffer according to requested */
+               /* sequence number */
+               rcu_read_lock();
+               ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL,
+                                                start_seq_num, 1);
+               rcu_read_unlock();
+               return TXRX_DROP;
+       }
+
+       return TXRX_CONTINUE;
+}
+
 static ieee80211_txrx_result
 ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
 {
@@ -1386,8 +1475,8 @@ ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
                return TXRX_DROP;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
-       if ((sdata->type == IEEE80211_IF_TYPE_STA ||
-            sdata->type == IEEE80211_IF_TYPE_IBSS) &&
+       if ((sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+            sdata->vif.type == IEEE80211_IF_TYPE_IBSS) &&
            !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
                ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
        else
@@ -1479,7 +1568,7 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
                goto ignore;
        }
 
-       if (rx->sdata->type == IEEE80211_IF_TYPE_AP && keyidx) {
+       if (rx->sdata->vif.type == IEEE80211_IF_TYPE_AP && keyidx) {
                /*
                 * APs with pairwise keys should never receive Michael MIC
                 * errors for non-zero keyidx because these are reserved for
@@ -1528,6 +1617,7 @@ ieee80211_rx_handler ieee80211_rx_handlers[] =
        ieee80211_rx_h_remove_qos_control,
        ieee80211_rx_h_amsdu,
        ieee80211_rx_h_data,
+       ieee80211_rx_h_ctrl,
        ieee80211_rx_h_mgmt,
        NULL
 };
@@ -1540,7 +1630,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
 {
        int multicast = is_multicast_ether_addr(hdr->addr1);
 
-       switch (sdata->type) {
+       switch (sdata->vif.type) {
        case IEEE80211_IF_TYPE_STA:
                if (!bssid)
                        return 0;
@@ -1614,8 +1704,10 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
  * This is the actual Rx frames handler. as it blongs to Rx path it must
  * be called with rcu_read_lock protection.
  */
-void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb,
-                           struct ieee80211_rx_status *status, u32 load)
+static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
+                                        struct sk_buff *skb,
+                                        struct ieee80211_rx_status *status,
+                                        u32 load)
 {
        struct ieee80211_local *local = hw_to_local(hw);
        struct ieee80211_sub_if_data *sdata;
@@ -1627,7 +1719,6 @@ void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb,
        struct ieee80211_sub_if_data *prev = NULL;
        struct sk_buff *skb_new;
        u8 *bssid;
-       int hdrlen;
 
        hdr = (struct ieee80211_hdr *) skb->data;
        memset(&rx, 0, sizeof(rx));
@@ -1639,18 +1730,6 @@ void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb,
        rx.fc = le16_to_cpu(hdr->frame_control);
        type = rx.fc & IEEE80211_FCTL_FTYPE;
 
-       /*
-        * Drivers are required to align the payload data to a four-byte
-        * boundary, so the last two bits of the address where it starts
-        * may not be set. The header is required to be directly before
-        * the payload data, padding like atheros hardware adds which is
-        * inbetween the 802.11 header and the payload is not supported,
-        * the driver is required to move the 802.11 header further back
-        * in that case.
-        */
-       hdrlen = ieee80211_get_hdrlen(rx.fc);
-       WARN_ON_ONCE(((unsigned long)(skb->data + hdrlen)) & 3);
-
        if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT)
                local->dot11ReceivedFragmentCount++;
 
@@ -1680,19 +1759,17 @@ void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb,
                ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx,
                                             rx.sta);
                sta_info_put(sta);
-               rcu_read_unlock();
                return;
        }
 
-       bssid = ieee80211_get_bssid(hdr, skb->len);
-
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
                if (!netif_running(sdata->dev))
                        continue;
 
-               if (sdata->type == IEEE80211_IF_TYPE_MNTR)
+               if (sdata->vif.type == IEEE80211_IF_TYPE_MNTR)
                        continue;
 
+               bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
                rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
                prepares = prepare_for_handlers(sdata, bssid, &rx, hdr);
                /* prepare_for_handlers can change sta */
@@ -1749,6 +1826,190 @@ void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, struct sk_buff *skb,
                sta_info_put(sta);
 }
 
+#define SEQ_MODULO 0x1000
+#define SEQ_MASK   0xfff
+
+static inline int seq_less(u16 sq1, u16 sq2)
+{
+       return (((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1));
+}
+
+static inline u16 seq_inc(u16 sq)
+{
+       return ((sq + 1) & SEQ_MASK);
+}
+
+static inline u16 seq_sub(u16 sq1, u16 sq2)
+{
+       return ((sq1 - sq2) & SEQ_MASK);
+}
+
+
+/*
+ * As it function blongs to Rx path it must be called with
+ * the proper rcu_read_lock protection for its flow.
+ */
+u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+                               struct tid_ampdu_rx *tid_agg_rx,
+                               struct sk_buff *skb, u16 mpdu_seq_num,
+                               int bar_req)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_rx_status status;
+       u16 head_seq_num, buf_size;
+       int index;
+       u32 pkt_load;
+
+       buf_size = tid_agg_rx->buf_size;
+       head_seq_num = tid_agg_rx->head_seq_num;
+
+       /* frame with out of date sequence number */
+       if (seq_less(mpdu_seq_num, head_seq_num)) {
+               dev_kfree_skb(skb);
+               return 1;
+       }
+
+       /* if frame sequence number exceeds our buffering window size or
+        * block Ack Request arrived - release stored frames */
+       if ((!seq_less(mpdu_seq_num, head_seq_num + buf_size)) || (bar_req)) {
+               /* new head to the ordering buffer */
+               if (bar_req)
+                       head_seq_num = mpdu_seq_num;
+               else
+                       head_seq_num =
+                               seq_inc(seq_sub(mpdu_seq_num, buf_size));
+               /* release stored frames up to new head to stack */
+               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;
+
+                       if (tid_agg_rx->reorder_buf[index]) {
+                               /* release the reordered frames to stack */
+                               memcpy(&status,
+                                       tid_agg_rx->reorder_buf[index]->cb,
+                                       sizeof(status));
+                               pkt_load = ieee80211_rx_load_stats(local,
+                                               tid_agg_rx->reorder_buf[index],
+                                               &status);
+                               __ieee80211_rx_handle_packet(hw,
+                                       tid_agg_rx->reorder_buf[index],
+                                       &status, pkt_load);
+                               tid_agg_rx->stored_mpdu_num--;
+                               tid_agg_rx->reorder_buf[index] = NULL;
+                       }
+                       tid_agg_rx->head_seq_num =
+                               seq_inc(tid_agg_rx->head_seq_num);
+               }
+               if (bar_req)
+                       return 1;
+       }
+
+       /* now the new frame is always in the range of the reordering */
+       /* buffer window */
+       index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn)
+                               % tid_agg_rx->buf_size;
+       /* check if we already stored this frame */
+       if (tid_agg_rx->reorder_buf[index]) {
+               dev_kfree_skb(skb);
+               return 1;
+       }
+
+       /* if arrived mpdu is in the right order and nothing else stored */
+       /* release it immediately */
+       if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
+                       tid_agg_rx->stored_mpdu_num == 0) {
+               tid_agg_rx->head_seq_num =
+                       seq_inc(tid_agg_rx->head_seq_num);
+               return 0;
+       }
+
+       /* put the frame in the reordering buffer */
+       tid_agg_rx->reorder_buf[index] = skb;
+       tid_agg_rx->stored_mpdu_num++;
+       /* release the buffer until next missing frame */
+       index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn)
+                                               % tid_agg_rx->buf_size;
+       while (tid_agg_rx->reorder_buf[index]) {
+               /* release the reordered frame back to stack */
+               memcpy(&status, tid_agg_rx->reorder_buf[index]->cb,
+                       sizeof(status));
+               pkt_load = ieee80211_rx_load_stats(local,
+                                       tid_agg_rx->reorder_buf[index],
+                                       &status);
+               __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index],
+                                               &status, pkt_load);
+               tid_agg_rx->stored_mpdu_num--;
+               tid_agg_rx->reorder_buf[index] = NULL;
+               tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
+               index = seq_sub(tid_agg_rx->head_seq_num,
+                       tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+       }
+       return 1;
+}
+
+static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
+                                    struct sk_buff *skb)
+{
+       struct ieee80211_hw *hw = &local->hw;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct sta_info *sta;
+       struct tid_ampdu_rx *tid_agg_rx;
+       u16 fc, sc;
+       u16 mpdu_seq_num;
+       u8 ret = 0, *qc;
+       int tid;
+
+       sta = sta_info_get(local, hdr->addr2);
+       if (!sta)
+               return ret;
+
+       fc = le16_to_cpu(hdr->frame_control);
+
+       /* filter the QoS data rx stream according to
+        * STA/TID and check if this STA/TID is on aggregation */
+       if (!WLAN_FC_IS_QOS_DATA(fc))
+               goto end_reorder;
+
+       qc = skb->data + ieee80211_get_hdrlen(fc) - QOS_CONTROL_LEN;
+       tid = qc[0] & QOS_CONTROL_TID_MASK;
+       tid_agg_rx = &(sta->ampdu_mlme.tid_rx[tid]);
+
+       if (tid_agg_rx->state != HT_AGG_STATE_OPERATIONAL)
+               goto end_reorder;
+
+       /* null data frames are excluded */
+       if (unlikely(fc & IEEE80211_STYPE_NULLFUNC))
+               goto end_reorder;
+
+       /* new un-ordered ampdu frame - process it */
+
+       /* reset session timer */
+       if (tid_agg_rx->timeout) {
+               unsigned long expires =
+                       jiffies + (tid_agg_rx->timeout / 1000) * HZ;
+               mod_timer(&tid_agg_rx->session_timer, expires);
+       }
+
+       /* if this mpdu is fragmented - terminate rx aggregation session */
+       sc = le16_to_cpu(hdr->seq_ctrl);
+       if (sc & IEEE80211_SCTL_FRAG) {
+               ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr,
+                       tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
+               ret = 1;
+               goto end_reorder;
+       }
+
+       /* according to mpdu sequence number deal with reordering buffer */
+       mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
+       ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb,
+                                               mpdu_seq_num, 0);
+end_reorder:
+       if (sta)
+               sta_info_put(sta);
+       return ret;
+}
+
 /*
  * This is the receive path handler. It is called by a low level driver when an
  * 802.11 MPDU is received from the hardware.
@@ -1779,8 +2040,10 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
        }
 
        pkt_load = ieee80211_rx_load_stats(local, skb, status);
+       local->channel_use_raw += pkt_load;
 
-       __ieee80211_rx_handle_packet(hw, skb, status, pkt_load);
+       if (!ieee80211_rx_reorder_ampdu(local, skb))
+               __ieee80211_rx_handle_packet(hw, skb, status, pkt_load);
 
        rcu_read_unlock();
 }