diff --git a/package/kernel/mac80211/patches/345-brcmfmac-Remove-waitqueue_active-check.patch b/package/kernel/mac80211/patches/345-brcmfmac-Remove-waitqueue_active-check.patch
new file mode 100644
index 0000000000000000000000000000000000000000..39f438321e833984ef54d3f653f95c01ed041c47
--- /dev/null
+++ b/package/kernel/mac80211/patches/345-brcmfmac-Remove-waitqueue_active-check.patch
@@ -0,0 +1,54 @@
+From: Hui Wang <hui.wang@canonical.com>
+Date: Wed, 9 Mar 2016 15:25:26 +0800
+Subject: [PATCH] brcmfmac: Remove waitqueue_active check
+
+We met a problem of pm_suspend  when repeated closing/opening the lid
+on a Lenovo laptop (1/20 reproduce rate), below is the log:
+
+[ 199.735876] PM: Entering mem sleep
+[ 199.750516] e1000e: EEE TX LPI TIMER: 00000011
+[ 199.856638] Trying to free nonexistent resource <000000000000d000-000000000000d0ff>
+[ 201.753566] brcmfmac: brcmf_pcie_suspend: Timeout on response for entering D3 substate
+[ 201.753581] pci_legacy_suspend(): brcmf_pcie_suspend+0x0/0x1f0 [brcmfmac] returns -5
+[ 201.753585] dpm_run_callback(): pci_pm_suspend+0x0/0x160 returns -5
+[ 201.753589] PM: Device 0000:04:00.0 failed to suspend async: error -5
+
+Through debugging, we found when problem happens, it is not the device
+fails to enter D3, but the signal D3_ACK comes too early to pass the
+waitqueue_active() check.
+
+Just like this:
+brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM);
+// signal is triggered here
+wait_event_timeout(devinfo->mbdata_resp_wait, devinfo->mbdata_completed,
+		   BRCMF_PCIE_MBDATA_TIMEOUT);
+
+So far I think it is safe to remove waitqueue_active check since there
+is only one place to trigger this signal (sending
+BRCMF_H2D_HOST_D3_INFORM). And it is not a problem calling wake_up
+event earlier than calling wait_event.
+
+Cc: Brett Rudley <brudley@broadcom.com>
+Cc: Hante Meuleman <meuleman@broadcom.com>
+Cc: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+Cc: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+Cc: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Hui Wang <hui.wang@canonical.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+@@ -677,10 +677,8 @@ static void brcmf_pcie_handle_mb_data(st
+ 		brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n");
+ 	if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) {
+ 		brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n");
+-		if (waitqueue_active(&devinfo->mbdata_resp_wait)) {
+-			devinfo->mbdata_completed = true;
+-			wake_up(&devinfo->mbdata_resp_wait);
+-		}
++		devinfo->mbdata_completed = true;
++		wake_up(&devinfo->mbdata_resp_wait);
+ 	}
+ }
+ 
diff --git a/package/kernel/mac80211/patches/346-brcmfmac-uninitialized-ret-variable.patch b/package/kernel/mac80211/patches/346-brcmfmac-uninitialized-ret-variable.patch
new file mode 100644
index 0000000000000000000000000000000000000000..3c9ed425da0cb5140149f2c220d029e792ff41b7
--- /dev/null
+++ b/package/kernel/mac80211/patches/346-brcmfmac-uninitialized-ret-variable.patch
@@ -0,0 +1,21 @@
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Tue, 15 Mar 2016 10:06:10 +0300
+Subject: [PATCH] brcmfmac: uninitialized "ret" variable
+
+There is an error path where "ret" isn't initialized.
+
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+@@ -250,7 +250,7 @@ static int brcmf_sdiod_request_data(stru
+ 				    u32 addr, u8 regsz, void *data, bool write)
+ {
+ 	struct sdio_func *func;
+-	int ret;
++	int ret = -EINVAL;
+ 
+ 	brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
+ 		  write, fn, addr, regsz);
diff --git a/package/kernel/mac80211/patches/347-brcmfmac-sdio-remove-unused-variable-retry_limit.patch b/package/kernel/mac80211/patches/347-brcmfmac-sdio-remove-unused-variable-retry_limit.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d1deb6ee5ce73cb1a6ecc9775360c422113cd0b4
--- /dev/null
+++ b/package/kernel/mac80211/patches/347-brcmfmac-sdio-remove-unused-variable-retry_limit.patch
@@ -0,0 +1,24 @@
+From: Colin Ian King <colin.king@canonical.com>
+Date: Sun, 20 Mar 2016 17:34:52 +0000
+Subject: [PATCH] brcmfmac: sdio: remove unused variable retry_limit
+
+retry_limit has never been used during the life of this driver, so
+we may as well remove it as it is redundant.
+
+Signed-off-by: Colin Ian King <colin.king@canonical.com>
+Reviewed-by: Julian Calaby <julian.calaby@gmail.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+@@ -535,9 +535,6 @@ static int qcount[NUMPRIO];
+ 
+ #define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL)
+ 
+-/* Retry count for register access failures */
+-static const uint retry_limit = 2;
+-
+ /* Limit on rounding up frames */
+ static const uint max_roundup = 512;
+ 
diff --git a/package/kernel/mac80211/patches/348-brcmfmac-Delete-unnecessary-variable-initialisation.patch b/package/kernel/mac80211/patches/348-brcmfmac-Delete-unnecessary-variable-initialisation.patch
new file mode 100644
index 0000000000000000000000000000000000000000..d399b264ea9d2ac0d1ba82d725707720b4c0693e
--- /dev/null
+++ b/package/kernel/mac80211/patches/348-brcmfmac-Delete-unnecessary-variable-initialisation.patch
@@ -0,0 +1,26 @@
+From: Markus Elfring <elfring@users.sourceforge.net>
+Date: Fri, 18 Mar 2016 13:23:24 +1100
+Subject: [PATCH] brcmfmac: Delete unnecessary variable initialisation
+
+In brcmf_sdio_download_firmware(), bcmerror is set by the call to
+brcmf_sdio_download_code_file(), before it's checked in the following
+line.
+
+Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
+Acked-by: Arend van Spriel <arend@broadcom.com>
+[Rewrote commit message]
+Signed-off-by: Julian Calaby <julian.calaby@gmail.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+@@ -3258,7 +3258,7 @@ static int brcmf_sdio_download_firmware(
+ 					const struct firmware *fw,
+ 					void *nvram, u32 nvlen)
+ {
+-	int bcmerror = -EFAULT;
++	int bcmerror;
+ 	u32 rstvec;
+ 
+ 	sdio_claim_host(bus->sdiodev->func[1]);
diff --git a/package/kernel/mac80211/patches/349-0001-brcmfmac-clear-eventmask-array-before-using-it.patch b/package/kernel/mac80211/patches/349-0001-brcmfmac-clear-eventmask-array-before-using-it.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0acb4faaf1acb27dd39d11bef81be070c556dcf0
--- /dev/null
+++ b/package/kernel/mac80211/patches/349-0001-brcmfmac-clear-eventmask-array-before-using-it.patch
@@ -0,0 +1,27 @@
+From: Hante Meuleman <hante.meuleman@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:21 +0200
+Subject: [PATCH] brcmfmac: clear eventmask array before using it
+
+When the event_msgs iovar is set an array is used to configure the
+enabled events. This arrays needs to nulled before configuring
+otherwise unhandled events will be enabled. This solves a problem
+where in case of wowl the host got woken by an incorrectly enabled
+event.
+
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Signed-off-by: Hante Meuleman <hante.meuleman@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/fweh.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
+@@ -371,6 +371,7 @@ int brcmf_fweh_activate_events(struct br
+ 	int i, err;
+ 	s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+ 
++	memset(eventmask, 0, sizeof(eventmask));
+ 	for (i = 0; i < BRCMF_E_LAST; i++) {
+ 		if (ifp->drvr->fweh.evt_handler[i]) {
+ 			brcmf_dbg(EVENT, "enable event %s\n",
diff --git a/package/kernel/mac80211/patches/349-0002-brcmfmac-fix-clearing-wowl-wake-indicators.patch b/package/kernel/mac80211/patches/349-0002-brcmfmac-fix-clearing-wowl-wake-indicators.patch
new file mode 100644
index 0000000000000000000000000000000000000000..8d3067890c2fe87f88547d151d3da6d7facd8ea0
--- /dev/null
+++ b/package/kernel/mac80211/patches/349-0002-brcmfmac-fix-clearing-wowl-wake-indicators.patch
@@ -0,0 +1,27 @@
+From: Hante Meuleman <hante.meuleman@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:22 +0200
+Subject: [PATCH] brcmfmac: fix clearing wowl wake indicators
+
+Newer firmwares require the usage of the wowl wakeind struct as size
+for the iovar to clear the wake indicators. Older firmwares do not
+care, so change the used size.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Signed-off-by: Hante Meuleman <hante.meuleman@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/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -3608,7 +3608,8 @@ static void brcmf_configure_wowl(struct
+ 	if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
+ 		wowl_config |= BRCMF_WOWL_UNASSOC;
+ 
+-	brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear", strlen("clear"));
++	brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear",
++				 sizeof(struct brcmf_wowl_wakeind_le));
+ 	brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
+ 	brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
+ 	brcmf_bus_wowl_config(cfg->pub->bus_if, true);
diff --git a/package/kernel/mac80211/patches/349-0003-brcmfmac-insert-default-boardrev-in-nvram-data-if-mi.patch b/package/kernel/mac80211/patches/349-0003-brcmfmac-insert-default-boardrev-in-nvram-data-if-mi.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f293401ca82d37e367600acf2676b18f6fad92ba
--- /dev/null
+++ b/package/kernel/mac80211/patches/349-0003-brcmfmac-insert-default-boardrev-in-nvram-data-if-mi.patch
@@ -0,0 +1,114 @@
+From: Hante Meuleman <hante.meuleman@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:23 +0200
+Subject: [PATCH] brcmfmac: insert default boardrev in nvram data if
+ missing
+
+Some nvram files/stores come without the boardrev information,
+but firmware requires this to be set. When not found in nvram then
+add a default boardrev string to the nvram data.
+
+Reported-by: Rafal Milecki <zajec5@gmail.com>
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Franky (Zhenhui) Lin <franky.lin@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Signed-off-by: Hante Meuleman <hante.meuleman@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/firmware.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
+@@ -29,6 +29,7 @@
+ #define BRCMF_FW_MAX_NVRAM_SIZE			64000
+ #define BRCMF_FW_NVRAM_DEVPATH_LEN		19	/* devpath0=pcie/1/4/ */
+ #define BRCMF_FW_NVRAM_PCIEDEV_LEN		10	/* pcie/1/4/ + \0 */
++#define BRCMF_FW_DEFAULT_BOARDREV		"boardrev=0xff"
+ 
+ enum nvram_parser_state {
+ 	IDLE,
+@@ -51,6 +52,7 @@ enum nvram_parser_state {
+  * @entry: start position of key,value entry.
+  * @multi_dev_v1: detect pcie multi device v1 (compressed).
+  * @multi_dev_v2: detect pcie multi device v2.
++ * @boardrev_found: nvram contains boardrev information.
+  */
+ struct nvram_parser {
+ 	enum nvram_parser_state state;
+@@ -63,6 +65,7 @@ struct nvram_parser {
+ 	u32 entry;
+ 	bool multi_dev_v1;
+ 	bool multi_dev_v2;
++	bool boardrev_found;
+ };
+ 
+ /**
+@@ -125,6 +128,8 @@ static enum nvram_parser_state brcmf_nvr
+ 			nvp->multi_dev_v1 = true;
+ 		if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
+ 			nvp->multi_dev_v2 = true;
++		if (strncmp(&nvp->data[nvp->entry], "boardrev", 8) == 0)
++			nvp->boardrev_found = true;
+ 	} else if (!is_nvram_char(c) || c == ' ') {
+ 		brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
+ 			  nvp->line, nvp->column);
+@@ -284,6 +289,8 @@ static void brcmf_fw_strip_multi_v1(stru
+ 	while (i < nvp->nvram_len) {
+ 		if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) {
+ 			i += 2;
++			if (strncmp(&nvp->nvram[i], "boardrev", 8) == 0)
++				nvp->boardrev_found = true;
+ 			while (nvp->nvram[i] != 0) {
+ 				nvram[j] = nvp->nvram[i];
+ 				i++;
+@@ -335,6 +342,8 @@ static void brcmf_fw_strip_multi_v2(stru
+ 	while (i < nvp->nvram_len - len) {
+ 		if (strncmp(&nvp->nvram[i], prefix, len) == 0) {
+ 			i += len;
++			if (strncmp(&nvp->nvram[i], "boardrev", 8) == 0)
++				nvp->boardrev_found = true;
+ 			while (nvp->nvram[i] != 0) {
+ 				nvram[j] = nvp->nvram[i];
+ 				i++;
+@@ -356,6 +365,18 @@ fail:
+ 	nvp->nvram_len = 0;
+ }
+ 
++static void brcmf_fw_add_defaults(struct nvram_parser *nvp)
++{
++	if (nvp->boardrev_found)
++		return;
++
++	memcpy(&nvp->nvram[nvp->nvram_len], &BRCMF_FW_DEFAULT_BOARDREV,
++	       strlen(BRCMF_FW_DEFAULT_BOARDREV));
++	nvp->nvram_len += strlen(BRCMF_FW_DEFAULT_BOARDREV);
++	nvp->nvram[nvp->nvram_len] = '\0';
++	nvp->nvram_len++;
++}
++
+ /* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
+  * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
+  * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
+@@ -377,16 +398,21 @@ static void *brcmf_fw_nvram_strip(const
+ 		if (nvp.state == END)
+ 			break;
+ 	}
+-	if (nvp.multi_dev_v1)
++	if (nvp.multi_dev_v1) {
++		nvp.boardrev_found = false;
+ 		brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr);
+-	else if (nvp.multi_dev_v2)
++	} else if (nvp.multi_dev_v2) {
++		nvp.boardrev_found = false;
+ 		brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr);
++	}
+ 
+ 	if (nvp.nvram_len == 0) {
+ 		kfree(nvp.nvram);
+ 		return NULL;
+ 	}
+ 
++	brcmf_fw_add_defaults(&nvp);
++
+ 	pad = nvp.nvram_len;
+ 	*new_length = roundup(nvp.nvram_len + 1, 4);
+ 	while (pad != *new_length) {
diff --git a/package/kernel/mac80211/patches/349-0004-brcmfmac-fix-p2p-scan-abort-null-pointer-exception.patch b/package/kernel/mac80211/patches/349-0004-brcmfmac-fix-p2p-scan-abort-null-pointer-exception.patch
new file mode 100644
index 0000000000000000000000000000000000000000..ed0c83f9bbc9a2ce4741cfbe5a5cd7fde6938c7b
--- /dev/null
+++ b/package/kernel/mac80211/patches/349-0004-brcmfmac-fix-p2p-scan-abort-null-pointer-exception.patch
@@ -0,0 +1,29 @@
+From: Hante Meuleman <hante.meuleman@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:24 +0200
+Subject: [PATCH] brcmfmac: fix p2p scan abort null pointer exception
+
+When p2p connection setup is performed without having ever done an
+escan a null pointer exception can occur. This is because the ifp
+to abort scanning is taken from escan struct while it was never
+initialized. Fix this by using the primary ifp for scan abort. The
+abort should still be performed and all scan related commands are
+performed on primary ifp.
+
+Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Signed-off-by: Hante Meuleman <hante.meuleman@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/p2p.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+@@ -1266,7 +1266,7 @@ static void
+ brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg)
+ {
+ 	struct brcmf_p2p_info *p2p = &cfg->p2p;
+-	struct brcmf_if *ifp = cfg->escan_info.ifp;
++	struct brcmf_if *ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
+ 
+ 	if (test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status) &&
+ 	    (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status) ||
diff --git a/package/kernel/mac80211/patches/349-0005-brcmfmac-screening-firmware-event-packet.patch b/package/kernel/mac80211/patches/349-0005-brcmfmac-screening-firmware-event-packet.patch
new file mode 100644
index 0000000000000000000000000000000000000000..4d26404f51951086da093df1073a2bfd09d14349
--- /dev/null
+++ b/package/kernel/mac80211/patches/349-0005-brcmfmac-screening-firmware-event-packet.patch
@@ -0,0 +1,297 @@
+From: Franky Lin <franky.lin@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:25 +0200
+Subject: [PATCH] brcmfmac: screening firmware event packet
+
+Firmware uses asynchronized events as a communication method to the
+host. The event packets are marked as ETH_P_LINK_CTL protocol type. For
+SDIO and PCIe bus, this kind of packets are delivered through virtual
+event channel not data channel. This patch adds a screening logic to
+make sure the event handler only processes the events coming from the
+correct channel.
+
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Signed-off-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/bus.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+@@ -216,7 +216,9 @@ bool brcmf_c_prec_enq(struct device *dev
+ 		      int prec);
+ 
+ /* Receive frame for delivery to OS.  Callee disposes of rxp. */
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp);
++void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp, bool handle_evnt);
++/* Receive async event packet from firmware. Callee disposes of rxp. */
++void brcmf_rx_event(struct device *dev, struct sk_buff *rxp);
+ 
+ /* Indication from bus module regarding presence/insertion of dongle. */
+ int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -311,16 +311,17 @@ void brcmf_txflowblock(struct device *de
+ 	brcmf_fws_bus_blocked(drvr, state);
+ }
+ 
+-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
++void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb,
++		    bool handle_event)
+ {
+-	skb->dev = ifp->ndev;
+-	skb->protocol = eth_type_trans(skb, skb->dev);
++	skb->protocol = eth_type_trans(skb, ifp->ndev);
+ 
+ 	if (skb->pkt_type == PACKET_MULTICAST)
+ 		ifp->stats.multicast++;
+ 
+ 	/* Process special event packets */
+-	brcmf_fweh_process_skb(ifp->drvr, skb);
++	if (handle_event)
++		brcmf_fweh_process_skb(ifp->drvr, skb);
+ 
+ 	if (!(ifp->ndev->flags & IFF_UP)) {
+ 		brcmu_pkt_buf_free_skb(skb);
+@@ -381,7 +382,7 @@ static void brcmf_rxreorder_process_info
+ 	/* validate flags and flow id */
+ 	if (flags == 0xFF) {
+ 		brcmf_err("invalid flags...so ignore this packet\n");
+-		brcmf_netif_rx(ifp, pkt);
++		brcmf_netif_rx(ifp, pkt, false);
+ 		return;
+ 	}
+ 
+@@ -393,7 +394,7 @@ static void brcmf_rxreorder_process_info
+ 		if (rfi == NULL) {
+ 			brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+ 				  flow_id);
+-			brcmf_netif_rx(ifp, pkt);
++			brcmf_netif_rx(ifp, pkt, false);
+ 			return;
+ 		}
+ 
+@@ -418,7 +419,7 @@ static void brcmf_rxreorder_process_info
+ 		rfi = kzalloc(buf_size, GFP_ATOMIC);
+ 		if (rfi == NULL) {
+ 			brcmf_err("failed to alloc buffer\n");
+-			brcmf_netif_rx(ifp, pkt);
++			brcmf_netif_rx(ifp, pkt, false);
+ 			return;
+ 		}
+ 
+@@ -532,11 +533,11 @@ static void brcmf_rxreorder_process_info
+ netif_rx:
+ 	skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+ 		__skb_unlink(pkt, &reorder_list);
+-		brcmf_netif_rx(ifp, pkt);
++		brcmf_netif_rx(ifp, pkt, false);
+ 	}
+ }
+ 
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
++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);
+@@ -560,7 +561,32 @@ void brcmf_rx_frame(struct device *dev,
+ 	if (rd->reorder)
+ 		brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
+ 	else
+-		brcmf_netif_rx(ifp, skb);
++		brcmf_netif_rx(ifp, skb, handle_evnt);
++}
++
++void brcmf_rx_event(struct device *dev, struct sk_buff *skb)
++{
++	struct brcmf_if *ifp;
++	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
++	struct brcmf_pub *drvr = bus_if->drvr;
++	int ret;
++
++	brcmf_dbg(EVENT, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
++
++	/* process and remove protocol-specific header */
++	ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
++
++	if (ret || !ifp || !ifp->ndev) {
++		if (ret != -ENODATA && ifp)
++			ifp->stats.rx_errors++;
++		brcmu_pkt_buf_free_skb(skb);
++		return;
++	}
++
++	skb->protocol = eth_type_trans(skb, ifp->ndev);
++
++	brcmf_fweh_process_skb(ifp->drvr, skb);
++	brcmu_pkt_buf_free_skb(skb);
+ }
+ 
+ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+@@ -225,7 +225,8 @@ int brcmf_get_next_free_bsscfgidx(struct
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+ 			  enum brcmf_netif_stop_reason reason, bool state);
+ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
+-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
++void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb,
++		    bool handle_event);
+ void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
+ int __init brcmf_core_init(void);
+ void __exit brcmf_core_exit(void);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+@@ -20,6 +20,7 @@
+ 
+ #include <linux/types.h>
+ #include <linux/netdevice.h>
++#include <linux/etherdevice.h>
+ 
+ #include <brcmu_utils.h>
+ #include <brcmu_wifi.h>
+@@ -1075,28 +1076,13 @@ static void brcmf_msgbuf_rxbuf_event_pos
+ }
+ 
+ 
+-static void
+-brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb,
+-		    u8 ifidx)
+-{
+-	struct brcmf_if *ifp;
+-
+-	ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
+-	if (!ifp || !ifp->ndev) {
+-		brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
+-		brcmu_pkt_buf_free_skb(skb);
+-		return;
+-	}
+-	brcmf_netif_rx(ifp, skb);
+-}
+-
+-
+ static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
+ {
+ 	struct msgbuf_rx_event *event;
+ 	u32 idx;
+ 	u16 buflen;
+ 	struct sk_buff *skb;
++	struct brcmf_if *ifp;
+ 
+ 	event = (struct msgbuf_rx_event *)buf;
+ 	idx = le32_to_cpu(event->msg.request_id);
+@@ -1116,7 +1102,19 @@ static void brcmf_msgbuf_process_event(s
+ 
+ 	skb_trim(skb, buflen);
+ 
+-	brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx);
++	ifp = brcmf_get_ifp(msgbuf->drvr, event->msg.ifidx);
++	if (!ifp || !ifp->ndev) {
++		brcmf_err("Received pkt for invalid ifidx %d\n",
++			  event->msg.ifidx);
++		goto exit;
++	}
++
++	skb->protocol = eth_type_trans(skb, ifp->ndev);
++
++	brcmf_fweh_process_skb(ifp->drvr, skb);
++
++exit:
++	brcmu_pkt_buf_free_skb(skb);
+ }
+ 
+ 
+@@ -1128,6 +1126,7 @@ brcmf_msgbuf_process_rx_complete(struct
+ 	u16 data_offset;
+ 	u16 buflen;
+ 	u32 idx;
++	struct brcmf_if *ifp;
+ 
+ 	brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1);
+ 
+@@ -1148,7 +1147,14 @@ brcmf_msgbuf_process_rx_complete(struct
+ 
+ 	skb_trim(skb, buflen);
+ 
+-	brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx);
++	ifp = brcmf_get_ifp(msgbuf->drvr, rx_complete->msg.ifidx);
++	if (!ifp || !ifp->ndev) {
++		brcmf_err("Received pkt for invalid ifidx %d\n",
++			  rx_complete->msg.ifidx);
++		brcmu_pkt_buf_free_skb(skb);
++		return;
++	}
++	brcmf_netif_rx(ifp, skb, false);
+ }
+ 
+ 
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+@@ -1294,6 +1294,17 @@ static inline u8 brcmf_sdio_getdatoffset
+ 	return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
+ }
+ 
++static inline bool brcmf_sdio_fromevntchan(u8 *swheader)
++{
++	u32 hdrvalue;
++	u8 ret;
++
++	hdrvalue = *(u32 *)swheader;
++	ret = (u8)((hdrvalue & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT);
++
++	return (ret == SDPCM_EVENT_CHANNEL);
++}
++
+ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
+ 			      struct brcmf_sdio_hdrinfo *rd,
+ 			      enum brcmf_sdio_frmtype type)
+@@ -1641,7 +1652,11 @@ static u8 brcmf_sdio_rxglom(struct brcmf
+ 					   pfirst->len, pfirst->next,
+ 					   pfirst->prev);
+ 			skb_unlink(pfirst, &bus->glom);
+-			brcmf_rx_frame(bus->sdiodev->dev, pfirst);
++			if (brcmf_sdio_fromevntchan(pfirst->data))
++				brcmf_rx_event(bus->sdiodev->dev, pfirst);
++			else
++				brcmf_rx_frame(bus->sdiodev->dev, pfirst,
++					       false);
+ 			bus->sdcnt.rxglompkts++;
+ 		}
+ 
+@@ -1967,18 +1982,19 @@ static uint brcmf_sdio_readframes(struct
+ 		__skb_trim(pkt, rd->len);
+ 		skb_pull(pkt, rd->dat_offset);
+ 
++		if (pkt->len == 0)
++			brcmu_pkt_buf_free_skb(pkt);
++		else if (rd->channel == SDPCM_EVENT_CHANNEL)
++			brcmf_rx_event(bus->sdiodev->dev, pkt);
++		else
++			brcmf_rx_frame(bus->sdiodev->dev, pkt,
++				       false);
++
+ 		/* prepare the descriptor for the next read */
+ 		rd->len = rd->len_nxtfrm << 4;
+ 		rd->len_nxtfrm = 0;
+ 		/* treat all packet as event if we don't know */
+ 		rd->channel = SDPCM_EVENT_CHANNEL;
+-
+-		if (pkt->len == 0) {
+-			brcmu_pkt_buf_free_skb(pkt);
+-			continue;
+-		}
+-
+-		brcmf_rx_frame(bus->sdiodev->dev, pkt);
+ 	}
+ 
+ 	rxcount = maxframes - rxleft;
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+@@ -514,7 +514,7 @@ static void brcmf_usb_rx_complete(struct
+ 
+ 	if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
+ 		skb_put(skb, urb->actual_length);
+-		brcmf_rx_frame(devinfo->dev, skb);
++		brcmf_rx_frame(devinfo->dev, skb, true);
+ 		brcmf_usb_rx_refill(devinfo, req);
+ 	} else {
+ 		brcmu_pkt_buf_free_skb(skb);
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 0000000000000000000000000000000000000000..33b263df3aefc1b00e99860b4b9450a6ff23ad83
--- /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 */
diff --git a/package/kernel/mac80211/patches/349-0007-brcmfmac-revise-handling-events-in-receive-path.patch b/package/kernel/mac80211/patches/349-0007-brcmfmac-revise-handling-events-in-receive-path.patch
new file mode 100644
index 0000000000000000000000000000000000000000..a43feffe1792c44e207908c21e314bfcf0378cf1
--- /dev/null
+++ b/package/kernel/mac80211/patches/349-0007-brcmfmac-revise-handling-events-in-receive-path.patch
@@ -0,0 +1,139 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:27 +0200
+Subject: [PATCH] brcmfmac: revise handling events in receive path
+
+Move event handling out of brcmf_netif_rx() avoiding the need
+to pass a flag. This flag is only ever true for USB hosts as
+other interface use separate brcmf_rx_event() function.
+
+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/bus.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h
+@@ -216,7 +216,7 @@ bool brcmf_c_prec_enq(struct device *dev
+ 		      int prec);
+ 
+ /* Receive frame for delivery to OS.  Callee disposes of rxp. */
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp, bool handle_evnt);
++void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp, bool handle_event);
+ /* Receive async event packet from firmware. Callee disposes of rxp. */
+ void brcmf_rx_event(struct device *dev, struct sk_buff *rxp);
+ 
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -298,18 +298,11 @@ void brcmf_txflowblock(struct device *de
+ 	brcmf_fws_bus_blocked(drvr, state);
+ }
+ 
+-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb,
+-		    bool handle_event)
++void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
+ {
+-	skb->protocol = eth_type_trans(skb, ifp->ndev);
+-
+ 	if (skb->pkt_type == PACKET_MULTICAST)
+ 		ifp->stats.multicast++;
+ 
+-	/* Process special event packets */
+-	if (handle_event)
+-		brcmf_fweh_process_skb(ifp->drvr, skb);
+-
+ 	if (!(ifp->ndev->flags & IFF_UP)) {
+ 		brcmu_pkt_buf_free_skb(skb);
+ 		return;
+@@ -329,7 +322,7 @@ void brcmf_netif_rx(struct brcmf_if *ifp
+ 		netif_rx_ni(skb);
+ }
+ 
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_evnt)
++void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event)
+ {
+ 	struct brcmf_if *ifp;
+ 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+@@ -348,10 +341,17 @@ void brcmf_rx_frame(struct device *dev,
+ 		return;
+ 	}
+ 
+-	if (brcmf_proto_is_reorder_skb(skb))
++	skb->protocol = eth_type_trans(skb, ifp->ndev);
++
++	if (brcmf_proto_is_reorder_skb(skb)) {
+ 		brcmf_proto_rxreorder(ifp, skb);
+-	else
+-		brcmf_netif_rx(ifp, skb, handle_evnt);
++	} else {
++		/* Process special event packets */
++		if (handle_event)
++			brcmf_fweh_process_skb(ifp->drvr, skb);
++
++		brcmf_netif_rx(ifp, skb);
++	}
+ }
+ 
+ void brcmf_rx_event(struct device *dev, struct sk_buff *skb)
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+@@ -221,8 +221,7 @@ int brcmf_get_next_free_bsscfgidx(struct
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+ 			  enum brcmf_netif_stop_reason reason, bool state);
+ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
+-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb,
+-		    bool handle_event);
++void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
+ void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
+ int __init brcmf_core_init(void);
+ void __exit brcmf_core_exit(void);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
+@@ -1668,7 +1668,7 @@ void brcmf_fws_rxreorder(struct brcmf_if
+ 	/* validate flags and flow id */
+ 	if (flags == 0xFF) {
+ 		brcmf_err("invalid flags...so ignore this packet\n");
+-		brcmf_netif_rx(ifp, pkt, false);
++		brcmf_netif_rx(ifp, pkt);
+ 		return;
+ 	}
+ 
+@@ -1680,7 +1680,7 @@ void brcmf_fws_rxreorder(struct brcmf_if
+ 		if (rfi == NULL) {
+ 			brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+ 				  flow_id);
+-			brcmf_netif_rx(ifp, pkt, false);
++			brcmf_netif_rx(ifp, pkt);
+ 			return;
+ 		}
+ 
+@@ -1705,7 +1705,7 @@ void brcmf_fws_rxreorder(struct brcmf_if
+ 		rfi = kzalloc(buf_size, GFP_ATOMIC);
+ 		if (rfi == NULL) {
+ 			brcmf_err("failed to alloc buffer\n");
+-			brcmf_netif_rx(ifp, pkt, false);
++			brcmf_netif_rx(ifp, pkt);
+ 			return;
+ 		}
+ 
+@@ -1819,7 +1819,7 @@ void brcmf_fws_rxreorder(struct brcmf_if
+ netif_rx:
+ 	skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+ 		__skb_unlink(pkt, &reorder_list);
+-		brcmf_netif_rx(ifp, pkt, false);
++		brcmf_netif_rx(ifp, pkt);
+ 	}
+ }
+ 
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+@@ -1157,7 +1157,7 @@ brcmf_msgbuf_process_rx_complete(struct
+ 		brcmu_pkt_buf_free_skb(skb);
+ 		return;
+ 	}
+-	brcmf_netif_rx(ifp, skb, false);
++	brcmf_netif_rx(ifp, skb);
+ }
+ 
+ 
diff --git a/package/kernel/mac80211/patches/349-0008-brcmfmac-create-common-function-for-handling-brcmf_p.patch b/package/kernel/mac80211/patches/349-0008-brcmfmac-create-common-function-for-handling-brcmf_p.patch
new file mode 100644
index 0000000000000000000000000000000000000000..08ea235fddc7caf06eb4b196c51765cd4adbee2f
--- /dev/null
+++ b/package/kernel/mac80211/patches/349-0008-brcmfmac-create-common-function-for-handling-brcmf_p.patch
@@ -0,0 +1,88 @@
+From: Arend van Spriel <arend@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:28 +0200
+Subject: [PATCH] brcmfmac: create common function for handling
+ brcmf_proto_hdrpull()
+
+In receive path brcmf_proto_hdrpull() needs to be called and handled
+similar in brcmf_rx_frame() and brcmf_rx_event(). Move that duplicated
+code in separate function.
+
+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/core.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+@@ -322,26 +322,35 @@ void brcmf_netif_rx(struct brcmf_if *ifp
+ 		netif_rx_ni(skb);
+ }
+ 
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event)
++static int brcmf_rx_hdrpull(struct brcmf_pub *drvr, struct sk_buff *skb,
++			    struct brcmf_if **ifp)
+ {
+-	struct brcmf_if *ifp;
+-	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+-	struct brcmf_pub *drvr = bus_if->drvr;
+ 	int ret;
+ 
+-	brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
+-
+ 	/* process and remove protocol-specific header */
+-	ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
++	ret = brcmf_proto_hdrpull(drvr, true, skb, ifp);
+ 
+-	if (ret || !ifp || !ifp->ndev) {
++	if (ret || !(*ifp) || !(*ifp)->ndev) {
+ 		if (ret != -ENODATA && ifp)
+-			ifp->stats.rx_errors++;
++			(*ifp)->stats.rx_errors++;
+ 		brcmu_pkt_buf_free_skb(skb);
+-		return;
++		return -ENODATA;
+ 	}
+ 
+-	skb->protocol = eth_type_trans(skb, ifp->ndev);
++	skb->protocol = eth_type_trans(skb, (*ifp)->ndev);
++	return 0;
++}
++
++void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event)
++{
++	struct brcmf_if *ifp;
++	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
++	struct brcmf_pub *drvr = bus_if->drvr;
++
++	brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
++
++	if (brcmf_rx_hdrpull(drvr, skb, &ifp))
++		return;
+ 
+ 	if (brcmf_proto_is_reorder_skb(skb)) {
+ 		brcmf_proto_rxreorder(ifp, skb);
+@@ -359,21 +368,11 @@ void brcmf_rx_event(struct device *dev,
+ 	struct brcmf_if *ifp;
+ 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ 	struct brcmf_pub *drvr = bus_if->drvr;
+-	int ret;
+ 
+ 	brcmf_dbg(EVENT, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
+ 
+-	/* process and remove protocol-specific header */
+-	ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
+-
+-	if (ret || !ifp || !ifp->ndev) {
+-		if (ret != -ENODATA && ifp)
+-			ifp->stats.rx_errors++;
+-		brcmu_pkt_buf_free_skb(skb);
++	if (brcmf_rx_hdrpull(drvr, skb, &ifp))
+ 		return;
+-	}
+-
+-	skb->protocol = eth_type_trans(skb, ifp->ndev);
+ 
+ 	brcmf_fweh_process_skb(ifp->drvr, skb);
+ 	brcmu_pkt_buf_free_skb(skb);
diff --git a/package/kernel/mac80211/patches/861-brcmfmac-register-wiphy-s-during-module_init.patch b/package/kernel/mac80211/patches/861-brcmfmac-register-wiphy-s-during-module_init.patch
index 7803733fdab829bc3a1275ef83b1f4b527f69842..8cf9ea28f56c9e268a6f5ab3d9aef6bd1102a256 100644
--- a/package/kernel/mac80211/patches/861-brcmfmac-register-wiphy-s-during-module_init.patch
+++ b/package/kernel/mac80211/patches/861-brcmfmac-register-wiphy-s-during-module_init.patch
@@ -13,7 +13,7 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
 
 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
-@@ -1417,6 +1417,7 @@ int __init brcmf_core_init(void)
+@@ -1232,6 +1232,7 @@ int __init brcmf_core_init(void)
  {
  	if (!schedule_work(&brcmf_driver_work))
  		return -EBUSY;
@@ -23,7 +23,7 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
  }
 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c
-@@ -418,6 +418,7 @@ struct brcmf_fw {
+@@ -444,6 +444,7 @@ struct brcmf_fw {
  	u16 bus_nr;
  	void (*done)(struct device *dev, const struct firmware *fw,
  		     void *nvram_image, u32 nvram_len);
@@ -31,7 +31,7 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
  };
  
  static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
-@@ -452,6 +453,8 @@ static void brcmf_fw_request_nvram_done(
+@@ -478,6 +479,8 @@ static void brcmf_fw_request_nvram_done(
  		goto fail;
  
  	fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length);
@@ -40,7 +40,7 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
  	kfree(fwctx);
  	return;
  
-@@ -459,6 +462,8 @@ fail:
+@@ -485,6 +488,8 @@ fail:
  	brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
  	release_firmware(fwctx->code);
  	device_release_driver(fwctx->dev);
@@ -49,7 +49,7 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
  	kfree(fwctx);
  }
  
-@@ -474,6 +479,8 @@ static void brcmf_fw_request_code_done(c
+@@ -500,6 +505,8 @@ static void brcmf_fw_request_code_done(c
  	/* only requested code so done here */
  	if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) {
  		fwctx->done(fwctx->dev, fw, NULL, 0);
@@ -58,7 +58,7 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
  		kfree(fwctx);
  		return;
  	}
-@@ -491,6 +498,8 @@ static void brcmf_fw_request_code_done(c
+@@ -517,6 +524,8 @@ static void brcmf_fw_request_code_done(c
  fail:
  	brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
  	device_release_driver(fwctx->dev);
@@ -67,7 +67,7 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
  	kfree(fwctx);
  }
  
-@@ -502,6 +511,8 @@ int brcmf_fw_get_firmwares_pcie(struct d
+@@ -528,6 +537,8 @@ int brcmf_fw_get_firmwares_pcie(struct d
  				u16 domain_nr, u16 bus_nr)
  {
  	struct brcmf_fw *fwctx;
@@ -76,7 +76,7 @@ Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
  
  	brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev));
  	if (!fw_cb || !code)
-@@ -522,9 +533,17 @@ int brcmf_fw_get_firmwares_pcie(struct d
+@@ -548,9 +559,17 @@ int brcmf_fw_get_firmwares_pcie(struct d
  	fwctx->domain_nr = domain_nr;
  	fwctx->bus_nr = bus_nr;