mac80211: update brcmfmac including missing boardrev workaround
[openwrt/svn-archive/archive.git] / package / kernel / mac80211 / patches / 349-0006-brcmfmac-cleanup-ampdu-rx-host-reorder-code.patch
diff --git a/package/kernel/mac80211/patches/349-0006-brcmfmac-cleanup-ampdu-rx-host-reorder-code.patch b/package/kernel/mac80211/patches/349-0006-brcmfmac-cleanup-ampdu-rx-host-reorder-code.patch
new file mode 100644 (file)
index 0000000..33b263d
--- /dev/null
@@ -0,0 +1,585 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:26 +0200
+Subject: [PATCH] brcmfmac: cleanup ampdu-rx host reorder code
+
+The code for ampdu-rx host reorder is related to the firmware signalling
+supported in BCDC protocol. This change moves the code to fwsignal module.
+
+Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Reviewed-by: Franky Lin <franky.lin@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
+@@ -351,6 +351,12 @@ brcmf_proto_bcdc_add_tdls_peer(struct br
+ {
+ }
++static void brcmf_proto_bcdc_rxreorder(struct brcmf_if *ifp,
++                                     struct sk_buff *skb)
++{
++      brcmf_fws_rxreorder(ifp, skb);
++}
++
+ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
+ {
+       struct brcmf_bcdc *bcdc;
+@@ -372,6 +378,7 @@ int brcmf_proto_bcdc_attach(struct brcmf
+       drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
+       drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
+       drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
++      drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder;
+       drvr->proto->pd = bcdc;
+       drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -40,19 +40,6 @@
+ #define MAX_WAIT_FOR_8021X_TX                 msecs_to_jiffies(950)
+-/* AMPDU rx reordering definitions */
+-#define BRCMF_RXREORDER_FLOWID_OFFSET         0
+-#define BRCMF_RXREORDER_MAXIDX_OFFSET         2
+-#define BRCMF_RXREORDER_FLAGS_OFFSET          4
+-#define BRCMF_RXREORDER_CURIDX_OFFSET         6
+-#define BRCMF_RXREORDER_EXPIDX_OFFSET         8
+-
+-#define BRCMF_RXREORDER_DEL_FLOW              0x01
+-#define BRCMF_RXREORDER_FLUSH_ALL             0x02
+-#define BRCMF_RXREORDER_CURIDX_VALID          0x04
+-#define BRCMF_RXREORDER_EXPIDX_VALID          0x08
+-#define BRCMF_RXREORDER_NEW_HOLE              0x10
+-
+ #define BRCMF_BSSIDX_INVALID                  -1
+ char *brcmf_ifname(struct brcmf_if *ifp)
+@@ -342,207 +329,11 @@ void brcmf_netif_rx(struct brcmf_if *ifp
+               netif_rx_ni(skb);
+ }
+-static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
+-                                       u8 start, u8 end,
+-                                       struct sk_buff_head *skb_list)
+-{
+-      /* initialize return list */
+-      __skb_queue_head_init(skb_list);
+-
+-      if (rfi->pend_pkts == 0) {
+-              brcmf_dbg(INFO, "no packets in reorder queue\n");
+-              return;
+-      }
+-
+-      do {
+-              if (rfi->pktslots[start]) {
+-                      __skb_queue_tail(skb_list, rfi->pktslots[start]);
+-                      rfi->pktslots[start] = NULL;
+-              }
+-              start++;
+-              if (start > rfi->max_idx)
+-                      start = 0;
+-      } while (start != end);
+-      rfi->pend_pkts -= skb_queue_len(skb_list);
+-}
+-
+-static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
+-                                       struct sk_buff *pkt)
+-{
+-      u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
+-      struct brcmf_ampdu_rx_reorder *rfi;
+-      struct sk_buff_head reorder_list;
+-      struct sk_buff *pnext;
+-      u8 flags;
+-      u32 buf_size;
+-
+-      flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
+-      flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
+-
+-      /* validate flags and flow id */
+-      if (flags == 0xFF) {
+-              brcmf_err("invalid flags...so ignore this packet\n");
+-              brcmf_netif_rx(ifp, pkt, false);
+-              return;
+-      }
+-
+-      rfi = ifp->drvr->reorder_flows[flow_id];
+-      if (flags & BRCMF_RXREORDER_DEL_FLOW) {
+-              brcmf_dbg(INFO, "flow-%d: delete\n",
+-                        flow_id);
+-
+-              if (rfi == NULL) {
+-                      brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+-                                flow_id);
+-                      brcmf_netif_rx(ifp, pkt, false);
+-                      return;
+-              }
+-
+-              brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
+-                                           &reorder_list);
+-              /* add the last packet */
+-              __skb_queue_tail(&reorder_list, pkt);
+-              kfree(rfi);
+-              ifp->drvr->reorder_flows[flow_id] = NULL;
+-              goto netif_rx;
+-      }
+-      /* from here on we need a flow reorder instance */
+-      if (rfi == NULL) {
+-              buf_size = sizeof(*rfi);
+-              max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+-
+-              buf_size += (max_idx + 1) * sizeof(pkt);
+-
+-              /* allocate space for flow reorder info */
+-              brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
+-                        flow_id, max_idx);
+-              rfi = kzalloc(buf_size, GFP_ATOMIC);
+-              if (rfi == NULL) {
+-                      brcmf_err("failed to alloc buffer\n");
+-                      brcmf_netif_rx(ifp, pkt, false);
+-                      return;
+-              }
+-
+-              ifp->drvr->reorder_flows[flow_id] = rfi;
+-              rfi->pktslots = (struct sk_buff **)(rfi+1);
+-              rfi->max_idx = max_idx;
+-      }
+-      if (flags & BRCMF_RXREORDER_NEW_HOLE)  {
+-              if (rfi->pend_pkts) {
+-                      brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
+-                                                   rfi->exp_idx,
+-                                                   &reorder_list);
+-                      WARN_ON(rfi->pend_pkts);
+-              } else {
+-                      __skb_queue_head_init(&reorder_list);
+-              }
+-              rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+-              rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+-              rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+-              rfi->pktslots[rfi->cur_idx] = pkt;
+-              rfi->pend_pkts++;
+-              brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
+-                        flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
+-      } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
+-              cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+-              exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+-
+-              if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
+-                      /* still in the current hole */
+-                      /* enqueue the current on the buffer chain */
+-                      if (rfi->pktslots[cur_idx] != NULL) {
+-                              brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
+-                              brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+-                              rfi->pktslots[cur_idx] = NULL;
+-                      }
+-                      rfi->pktslots[cur_idx] = pkt;
+-                      rfi->pend_pkts++;
+-                      rfi->cur_idx = cur_idx;
+-                      brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
+-                                flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+-
+-                      /* can return now as there is no reorder
+-                       * list to process.
+-                       */
+-                      return;
+-              }
+-              if (rfi->exp_idx == cur_idx) {
+-                      if (rfi->pktslots[cur_idx] != NULL) {
+-                              brcmf_dbg(INFO, "error buffer pending..free it\n");
+-                              brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+-                              rfi->pktslots[cur_idx] = NULL;
+-                      }
+-                      rfi->pktslots[cur_idx] = pkt;
+-                      rfi->pend_pkts++;
+-
+-                      /* got the expected one. flush from current to expected
+-                       * and update expected
+-                       */
+-                      brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
+-                                flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+-
+-                      rfi->cur_idx = cur_idx;
+-                      rfi->exp_idx = exp_idx;
+-
+-                      brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
+-                                                   &reorder_list);
+-                      brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
+-                                flow_id, skb_queue_len(&reorder_list),
+-                                rfi->pend_pkts);
+-              } else {
+-                      u8 end_idx;
+-
+-                      brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
+-                                flow_id, flags, rfi->cur_idx, rfi->exp_idx,
+-                                cur_idx, exp_idx);
+-                      if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+-                              end_idx = rfi->exp_idx;
+-                      else
+-                              end_idx = exp_idx;
+-
+-                      /* flush pkts first */
+-                      brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+-                                                   &reorder_list);
+-
+-                      if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
+-                              __skb_queue_tail(&reorder_list, pkt);
+-                      } else {
+-                              rfi->pktslots[cur_idx] = pkt;
+-                              rfi->pend_pkts++;
+-                      }
+-                      rfi->exp_idx = exp_idx;
+-                      rfi->cur_idx = cur_idx;
+-              }
+-      } else {
+-              /* explicity window move updating the expected index */
+-              exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+-
+-              brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
+-                        flow_id, flags, rfi->exp_idx, exp_idx);
+-              if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+-                      end_idx =  rfi->exp_idx;
+-              else
+-                      end_idx =  exp_idx;
+-
+-              brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+-                                           &reorder_list);
+-              __skb_queue_tail(&reorder_list, pkt);
+-              /* set the new expected idx */
+-              rfi->exp_idx = exp_idx;
+-      }
+-netif_rx:
+-      skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+-              __skb_unlink(pkt, &reorder_list);
+-              brcmf_netif_rx(ifp, pkt, false);
+-      }
+-}
+-
+ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_evnt)
+ {
+       struct brcmf_if *ifp;
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_pub *drvr = bus_if->drvr;
+-      struct brcmf_skb_reorder_data *rd;
+       int ret;
+       brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
+@@ -557,9 +348,8 @@ void brcmf_rx_frame(struct device *dev,
+               return;
+       }
+-      rd = (struct brcmf_skb_reorder_data *)skb->cb;
+-      if (rd->reorder)
+-              brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
++      if (brcmf_proto_is_reorder_skb(skb))
++              brcmf_proto_rxreorder(ifp, skb);
+       else
+               brcmf_netif_rx(ifp, skb, handle_evnt);
+ }
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+@@ -208,10 +208,6 @@ struct brcmf_if {
+       u8 ipv6addr_idx;
+ };
+-struct brcmf_skb_reorder_data {
+-      u8 *reorder;
+-};
+-
+ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
+ /* Return pointer to interface name */
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
+@@ -92,6 +92,19 @@ enum brcmf_fws_tlv_len {
+ };
+ #undef BRCMF_FWS_TLV_DEF
++/* AMPDU rx reordering definitions */
++#define BRCMF_RXREORDER_FLOWID_OFFSET         0
++#define BRCMF_RXREORDER_MAXIDX_OFFSET         2
++#define BRCMF_RXREORDER_FLAGS_OFFSET          4
++#define BRCMF_RXREORDER_CURIDX_OFFSET         6
++#define BRCMF_RXREORDER_EXPIDX_OFFSET         8
++
++#define BRCMF_RXREORDER_DEL_FLOW              0x01
++#define BRCMF_RXREORDER_FLUSH_ALL             0x02
++#define BRCMF_RXREORDER_CURIDX_VALID          0x04
++#define BRCMF_RXREORDER_EXPIDX_VALID          0x08
++#define BRCMF_RXREORDER_NEW_HOLE              0x10
++
+ #ifdef DEBUG
+ /*
+  * brcmf_fws_tlv_names - array of tlv names.
+@@ -1614,6 +1627,202 @@ static int brcmf_fws_notify_bcmc_credit_
+       return 0;
+ }
++static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
++                                       u8 start, u8 end,
++                                       struct sk_buff_head *skb_list)
++{
++      /* initialize return list */
++      __skb_queue_head_init(skb_list);
++
++      if (rfi->pend_pkts == 0) {
++              brcmf_dbg(INFO, "no packets in reorder queue\n");
++              return;
++      }
++
++      do {
++              if (rfi->pktslots[start]) {
++                      __skb_queue_tail(skb_list, rfi->pktslots[start]);
++                      rfi->pktslots[start] = NULL;
++              }
++              start++;
++              if (start > rfi->max_idx)
++                      start = 0;
++      } while (start != end);
++      rfi->pend_pkts -= skb_queue_len(skb_list);
++}
++
++void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *pkt)
++{
++      u8 *reorder_data;
++      u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
++      struct brcmf_ampdu_rx_reorder *rfi;
++      struct sk_buff_head reorder_list;
++      struct sk_buff *pnext;
++      u8 flags;
++      u32 buf_size;
++
++      reorder_data = ((struct brcmf_skb_reorder_data *)pkt->cb)->reorder;
++      flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
++      flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
++
++      /* validate flags and flow id */
++      if (flags == 0xFF) {
++              brcmf_err("invalid flags...so ignore this packet\n");
++              brcmf_netif_rx(ifp, pkt, false);
++              return;
++      }
++
++      rfi = ifp->drvr->reorder_flows[flow_id];
++      if (flags & BRCMF_RXREORDER_DEL_FLOW) {
++              brcmf_dbg(INFO, "flow-%d: delete\n",
++                        flow_id);
++
++              if (rfi == NULL) {
++                      brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
++                                flow_id);
++                      brcmf_netif_rx(ifp, pkt, false);
++                      return;
++              }
++
++              brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
++                                           &reorder_list);
++              /* add the last packet */
++              __skb_queue_tail(&reorder_list, pkt);
++              kfree(rfi);
++              ifp->drvr->reorder_flows[flow_id] = NULL;
++              goto netif_rx;
++      }
++      /* from here on we need a flow reorder instance */
++      if (rfi == NULL) {
++              buf_size = sizeof(*rfi);
++              max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
++
++              buf_size += (max_idx + 1) * sizeof(pkt);
++
++              /* allocate space for flow reorder info */
++              brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
++                        flow_id, max_idx);
++              rfi = kzalloc(buf_size, GFP_ATOMIC);
++              if (rfi == NULL) {
++                      brcmf_err("failed to alloc buffer\n");
++                      brcmf_netif_rx(ifp, pkt, false);
++                      return;
++              }
++
++              ifp->drvr->reorder_flows[flow_id] = rfi;
++              rfi->pktslots = (struct sk_buff **)(rfi + 1);
++              rfi->max_idx = max_idx;
++      }
++      if (flags & BRCMF_RXREORDER_NEW_HOLE)  {
++              if (rfi->pend_pkts) {
++                      brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
++                                                   rfi->exp_idx,
++                                                   &reorder_list);
++                      WARN_ON(rfi->pend_pkts);
++              } else {
++                      __skb_queue_head_init(&reorder_list);
++              }
++              rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
++              rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
++              rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
++              rfi->pktslots[rfi->cur_idx] = pkt;
++              rfi->pend_pkts++;
++              brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
++                        flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
++      } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
++              cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
++              exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
++
++              if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
++                      /* still in the current hole */
++                      /* enqueue the current on the buffer chain */
++                      if (rfi->pktslots[cur_idx] != NULL) {
++                              brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
++                              brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
++                              rfi->pktslots[cur_idx] = NULL;
++                      }
++                      rfi->pktslots[cur_idx] = pkt;
++                      rfi->pend_pkts++;
++                      rfi->cur_idx = cur_idx;
++                      brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
++                                flow_id, cur_idx, exp_idx, rfi->pend_pkts);
++
++                      /* can return now as there is no reorder
++                       * list to process.
++                       */
++                      return;
++              }
++              if (rfi->exp_idx == cur_idx) {
++                      if (rfi->pktslots[cur_idx] != NULL) {
++                              brcmf_dbg(INFO, "error buffer pending..free it\n");
++                              brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
++                              rfi->pktslots[cur_idx] = NULL;
++                      }
++                      rfi->pktslots[cur_idx] = pkt;
++                      rfi->pend_pkts++;
++
++                      /* got the expected one. flush from current to expected
++                       * and update expected
++                       */
++                      brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
++                                flow_id, cur_idx, exp_idx, rfi->pend_pkts);
++
++                      rfi->cur_idx = cur_idx;
++                      rfi->exp_idx = exp_idx;
++
++                      brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
++                                                   &reorder_list);
++                      brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
++                                flow_id, skb_queue_len(&reorder_list),
++                                rfi->pend_pkts);
++              } else {
++                      u8 end_idx;
++
++                      brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
++                                flow_id, flags, rfi->cur_idx, rfi->exp_idx,
++                                cur_idx, exp_idx);
++                      if (flags & BRCMF_RXREORDER_FLUSH_ALL)
++                              end_idx = rfi->exp_idx;
++                      else
++                              end_idx = exp_idx;
++
++                      /* flush pkts first */
++                      brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
++                                                   &reorder_list);
++
++                      if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
++                              __skb_queue_tail(&reorder_list, pkt);
++                      } else {
++                              rfi->pktslots[cur_idx] = pkt;
++                              rfi->pend_pkts++;
++                      }
++                      rfi->exp_idx = exp_idx;
++                      rfi->cur_idx = cur_idx;
++              }
++      } else {
++              /* explicity window move updating the expected index */
++              exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
++
++              brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
++                        flow_id, flags, rfi->exp_idx, exp_idx);
++              if (flags & BRCMF_RXREORDER_FLUSH_ALL)
++                      end_idx =  rfi->exp_idx;
++              else
++                      end_idx =  exp_idx;
++
++              brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
++                                           &reorder_list);
++              __skb_queue_tail(&reorder_list, pkt);
++              /* set the new expected idx */
++              rfi->exp_idx = exp_idx;
++      }
++netif_rx:
++      skb_queue_walk_safe(&reorder_list, pkt, pnext) {
++              __skb_unlink(pkt, &reorder_list);
++              brcmf_netif_rx(ifp, pkt, false);
++      }
++}
++
+ void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
+ {
+       struct brcmf_skb_reorder_data *rd;
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
+@@ -29,5 +29,6 @@ void brcmf_fws_add_interface(struct brcm
+ void brcmf_fws_del_interface(struct brcmf_if *ifp);
+ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb);
+ void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked);
++void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb);
+ #endif /* FWSIGNAL_H_ */
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+@@ -527,6 +527,9 @@ static int brcmf_msgbuf_hdrpull(struct b
+       return -ENODEV;
+ }
++static void brcmf_msgbuf_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb)
++{
++}
+ static void
+ brcmf_msgbuf_remove_flowring(struct brcmf_msgbuf *msgbuf, u16 flowid)
+@@ -1466,6 +1469,7 @@ int brcmf_proto_msgbuf_attach(struct brc
+       drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode;
+       drvr->proto->delete_peer = brcmf_msgbuf_delete_peer;
+       drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer;
++      drvr->proto->rxreorder = brcmf_msgbuf_rxreorder;
+       drvr->proto->pd = msgbuf;
+       init_waitqueue_head(&msgbuf->ioctl_resp_wait);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
+@@ -22,6 +22,9 @@ enum proto_addr_mode {
+       ADDR_DIRECT
+ };
++struct brcmf_skb_reorder_data {
++      u8 *reorder;
++};
+ struct brcmf_proto {
+       int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws,
+@@ -38,6 +41,7 @@ struct brcmf_proto {
+                           u8 peer[ETH_ALEN]);
+       void (*add_tdls_peer)(struct brcmf_pub *drvr, int ifidx,
+                             u8 peer[ETH_ALEN]);
++      void (*rxreorder)(struct brcmf_if *ifp, struct sk_buff *skb);
+       void *pd;
+ };
+@@ -91,6 +95,18 @@ brcmf_proto_add_tdls_peer(struct brcmf_p
+ {
+       drvr->proto->add_tdls_peer(drvr, ifidx, peer);
+ }
++static inline bool brcmf_proto_is_reorder_skb(struct sk_buff *skb)
++{
++      struct brcmf_skb_reorder_data *rd;
++
++      rd = (struct brcmf_skb_reorder_data *)skb->cb;
++      return !!rd->reorder;
++}
++static inline void
++brcmf_proto_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb)
++{
++      ifp->drvr->proto->rxreorder(ifp, skb);
++}
+ #endif /* BRCMFMAC_PROTO_H */