summaryrefslogtreecommitdiff
path: root/package/kernel/mac80211/patches/349-0006-brcmfmac-cleanup-ampdu-rx-host-reorder-code.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/349-0006-brcmfmac-cleanup-ampdu-rx-host-reorder-code.patch')
-rw-r--r--package/kernel/mac80211/patches/349-0006-brcmfmac-cleanup-ampdu-rx-host-reorder-code.patch585
1 files changed, 585 insertions, 0 deletions
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
index 0000000..33b263d
--- /dev/null
+++ b/package/kernel/mac80211/patches/349-0006-brcmfmac-cleanup-ampdu-rx-host-reorder-code.patch
@@ -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 */