diff --git a/patches/openwrt/0007-mac80211-hostapd-iw-.-update-to-LEDE-42f559ed70897a7b74dd3e6293b42e6d2e511eaa.patch b/patches/openwrt/0007-mac80211-hostapd-iw-.-update.patch
similarity index 92%
rename from patches/openwrt/0007-mac80211-hostapd-iw-.-update-to-LEDE-42f559ed70897a7b74dd3e6293b42e6d2e511eaa.patch
rename to patches/openwrt/0007-mac80211-hostapd-iw-.-update.patch
index 04bf2b2d710d6a97db59d770305b3c2158ee0c92..64eea76e5ddf05d4c120016f8a46efccbee01f6b 100644
--- a/patches/openwrt/0007-mac80211-hostapd-iw-.-update-to-LEDE-42f559ed70897a7b74dd3e6293b42e6d2e511eaa.patch
+++ b/patches/openwrt/0007-mac80211-hostapd-iw-.-update.patch
@@ -1,6 +1,22 @@
 From: Matthias Schiffer <mschiffer@universe-factory.net>
-Date: Wed, 7 Sep 2016 05:04:06 +0200
-Subject: mac80211, hostapd, iw, ...: update to LEDE 42f559ed70897a7b74dd3e6293b42e6d2e511eaa
+Date: Tue, 11 Oct 2016 02:53:43 +0200
+Subject: mac80211, hostapd, iw, ...: update
+
+The following package is updated to
+LEDE 6c2651566cce8f5b3a3d3b976439dee2bac5e07e:
+
+* mac80211
+
+The following packages are updated to
+LEDE 42f559ed70897a7b74dd3e6293b42e6d2e511eaa:
+
+* acx-mac80211
+* ath10k-firmware
+* hostapd
+* iw
+* linux-firmware
+* mt76
+* mwlwifi
 
 diff --git a/package/firmware/ath10k-firmware/Makefile b/package/firmware/ath10k-firmware/Makefile
 index b03d644..624da6a 100644
@@ -698,7 +714,7 @@ index 0000000..bbff8d8
 +
 +$(eval $(call KernelPackage,ath10k-ct))
 diff --git a/package/kernel/mac80211/Makefile b/package/kernel/mac80211/Makefile
-index 30da1cf..5c0ca3f 100644
+index 30da1cf..f2839cd 100644
 --- a/package/kernel/mac80211/Makefile
 +++ b/package/kernel/mac80211/Makefile
 @@ -10,20 +10,21 @@ include $(INCLUDE_DIR)/kernel.mk
@@ -930,18 +946,15 @@ index 30da1cf..5c0ca3f 100644
  	WLAN_VENDOR_INTEL \
  	WLAN_VENDOR_INTERSIL \
  	WLAN_VENDOR_MARVELL \
-@@ -1491,8 +1491,10 @@ endif
+@@ -1491,6 +1491,8 @@ endif
  
  config-$(call config_package,lib80211) += LIB80211 LIB80211_CRYPT_WEP LIB80211_CRYPT_CCMP LIB80211_CRYPT_TKIP
  
 +config-$(call config_package,airo) += AIRO
 +
  config-$(call config_package,ath) += ATH_CARDS ATH_COMMON
--config-$(CONFIG_PACKAGE_ATH_DEBUG) += ATH_DEBUG ATH10K_DEBUG
-+config-$(CONFIG_PACKAGE_ATH_DEBUG) += ATH_DEBUG ATH10K_DEBUG ATH9K_STATION_STATISTICS
+ config-$(CONFIG_PACKAGE_ATH_DEBUG) += ATH_DEBUG ATH10K_DEBUG
  config-$(CONFIG_PACKAGE_ATH_DFS) += ATH9K_DFS_CERTIFIED ATH10K_DFS_CERTIFIED
- 
- config-$(call config_package,ath9k) += ATH9K
 @@ -1501,6 +1503,7 @@ config-$(CONFIG_TARGET_ar71xx) += ATH9K_AHB
  config-$(CONFIG_PCI) += ATH9K_PCI
  config-$(CONFIG_ATH_USER_REGD) += ATH_USER_REGD
@@ -1139,151 +1152,6 @@ index ea229d6..06f3b8b 100644
  			dev_id="	option path	'$path'"
  		else
  			dev_id="	option macaddr	$(cat /sys/class/ieee80211/${dev}/macaddress)"
-diff --git a/package/kernel/mac80211/files/regdb.txt b/package/kernel/mac80211/files/regdb.txt
-index 463ace3..c4a9b2d 100644
---- a/package/kernel/mac80211/files/regdb.txt
-+++ b/package/kernel/mac80211/files/regdb.txt
-@@ -136,19 +136,35 @@ country BF: DFS-FCC
- 	(5490 - 5730 @ 160), (24), DFS
- 	(5735 - 5835 @ 80), (30)
- 
-+# Bulgarian rules as defined by the Communications Regulation Commission in the
-+# following documents:
-+#
-+# Rules for carrying out electronic communications through radio equipment using
-+# radio spectrum, which does not need to be individually assigned (the Rules):
-+# http://www.crc.bg/files/_bg/Pravila_09_06_2015.pdf
-+#
-+# List of radio equipment that uses harmonized within the European Union bands
-+# and electronic communications terminal equipment (the List):
-+# http://www.crc.bg/files/_bg/Spisak_2015.pdf
-+#
-+# Note: The transmit power limits in the 5250-5350 MHz and 5470-5725 MHz bands
-+# can be raised by 3 dBm if TPC is enabled. Refer to BDS EN 301 893 for details.
- country BG: DFS-ETSI
-+	# Wideband data transmission systems (WDTS) in the 2.4GHz ISM band, ref:
-+	# I.22 of the List, BDS EN 300 328
- 	(2402 - 2482 @ 40), (20)
--	(5170 - 5250 @ 80), (20), AUTO-BW
-+	# 5 GHz Radio Local Area Networks (RLANs), ref:
-+	# II.H01 of the List, BDS EN 301 893
-+	(5170 - 5250 @ 80), (23), AUTO-BW
- 	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
-+	# II.H01 of the List, I.54 from the List, BDS EN 301 893
- 	(5490 - 5710 @ 160), (27), DFS
--	# 5 GHz Short Range Devices, ref:
--	# Etsi EN 300 440-1
--	# Etsi EN 300 440-2
--	# http://crc.bg/files/_bg/Spisak_2015.pdf
--	# http://crc.bg/files/_bg/Pravila_2015_resh24.pdf
-+	# Short range devices (SRDs) in the 5725-5875 MHz frequency range, ref:
-+	# I.43 of the List, BDS EN 300 440-2, BDS EN 300 440-1
- 	(5725 - 5875 @ 80), (14)
--	# 60 GHz band channels 1-4, ref: Etsi En 302 567
--	(57000 - 66000 @ 2160), (40)
-+	# 60 GHz Multiple-Gigabit RLAN Systems, ref:
-+	# II.H03 of the List, BDS EN 302 567-2
-+	(57000 - 66000 @ 2160), (40), NO-OUTDOOR
- 
- country BH: DFS-JP
- 	(2402 - 2482 @ 40), (20)
-@@ -275,6 +291,12 @@ country CR: DFS-FCC
- 	(5490 - 5730 @ 20), (24), DFS
- 	(5735 - 5835 @ 20), (30)
- 
-+# http://www.mincom.gob.cu/?q=marcoregulatorio
-+# - Redes Informáticas
-+# Resolución 127, 2011 - Reglamento Banda 2,4 GHz.
-+country CU: DFS-FCC
-+	(2400 - 2483.5 @ 40), (200 mW)
-+
- country CX: DFS-FCC
- 	(2402 - 2482 @ 40), (20)
- 	(5170 - 5250 @ 80), (24), AUTO-BW
-@@ -302,28 +324,41 @@ country CZ: DFS-ETSI
- 	# 60 GHz band channels 1-4, ref: Etsi En 302 567
- 	(57000 - 66000 @ 2160), (40)
- 
--# Data from "Frequenznutzungsplan" (as published in April 2008), downloaded from
--# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38448/publicationFile/2659/Frequenznutzungsplan2008_Id17448pdf.pdf
--# For the 5GHz range also see
--# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38216/publicationFile/6579/WLAN5GHzVfg7_2010_28042010pdf.pdf
--# The values have been reduced by a factor of 2 (3db) for non TPC devices
--# (in other words: devices with TPC can use twice the tx power of this table).
--# Note that the docs do not require TPC for 5150--5250; the reduction to
--# 100mW thus is not strictly required -- however the conservative 100mW
-+# Allocation for the 2.4 GHz band (Vfg 10 / 2013, Allgemeinzuteilung von
-+# Frequenzen für die Nutzung in lokalen Netzwerken; Wireless Local Area
-+# Networks (WLAN-Funkanwendungen).
-+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2013_10_WLAN_2,4GHz_pdf.pdf
-+#
-+# Allocation for the 5 GHz band (Vfg. 7 / 2010, Allgemeinzuteilung von
-+# Frequenzen in den Bereichen 5150 MHz - 5350 MHz und 5470 MHz - 5725 MHz für
-+# Funkanwendungen zur breitbandigen Datenübertragung, WAS/WLAN („Wireless
-+# Access Systems including Wireless Local Area Networks“).
-+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2010_07_WLAN_5GHz_pdf.pdf
-+# The values for the 5 GHz have been reduced by a factor of 2 (3db) for non TPC
-+# devices (in other words: devices with TPC can use twice the tx power of this
-+# table). Note that the docs do not require TPC for 5150--5250; the reduction
-+# to 100mW thus is not strictly required -- however the conservative 100mW
- # limit is used here as the non-interference with radar and satellite
- # apps relies on the attenuation by the building walls only in the
- # absence of DFS; the neighbour countries have 100mW limit here as well.
-+#
-+# The ETSI EN 300 440-1 standard for short range devices in the 5 GHz band has
-+# been implemented in Germany:
-+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2014_69_SRD_pdf.pdf
-+#
-+# Allocation for the 60 GHz band (Allgemeinzuteilung von Frequenzen im
-+# Bereich 57 GHz - 66 GHz für Funkanwendungen für weitbandige
-+# Datenübertragungssysteme; „Multiple Gigabit WAS/RLAN Systems (MGWS)“).
-+# https://www.bundesnetzagentur.de/SharedDocs/Downloads/DE/Sachgebiete/Telekommunikation/Unternehmen_Institutionen/Frequenzen/Allgemeinzuteilungen/2011_08_MGWS_pdf.pdf
- 
- country DE: DFS-ETSI
--	# entries 279004 and 280006
- 	(2400 - 2483.5 @ 40), (100 mW)
--	# entry 303005
- 	(5150 - 5250 @ 80), (100 mW), NO-OUTDOOR, AUTO-BW
--	# entries 304002 and 305002
- 	(5250 - 5350 @ 80), (100 mW), NO-OUTDOOR, DFS, AUTO-BW
--	# entries 308002, 309001 and 310003
- 	(5470 - 5725 @ 160), (500 mW), DFS
--	# 60 GHz band channels 1-4, ref: Etsi En 302 567
-+	# short range devices (ETSI EN 300 440-1)
-+	(5725 - 5875 @ 80), (25 mW)
-+	# 60 GHz band channels 1-4 (ETSI EN 302 567)
- 	(57000 - 66000 @ 2160), (40)
- 
- country DK: DFS-ETSI
-@@ -629,6 +664,9 @@ country KR: DFS-JP
- 	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
- 	(5490 - 5710 @ 160), (30), DFS
- 	(5735 - 5835 @ 80), (30)
-+	# 60 GHz band channels 1-4,
-+	# ref: http://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EB%AC%B4%EC%84%A0%EC%84%A4%EB%B9%84%EA%B7%9C%EC%B9%99
-+	(57000 - 66000 @ 2160), (43)
- 
- country KW: DFS-ETSI
- 	(2402 - 2482 @ 40), (20)
-@@ -844,11 +882,18 @@ country NI: DFS-FCC
- 	(5490 - 5730 @ 160), (24), DFS
- 	(5735 - 5835 @ 80), (30)
- 
-+# Regulation on the use of frequency space without a license and
-+# without notification 2015
-+#
-+# http://wetten.overheid.nl/BWBR0036378/2015-03-05
-+
- country NL: DFS-ETSI
- 	(2402 - 2482 @ 40), (20)
- 	(5170 - 5250 @ 80), (20), NO-OUTDOOR, AUTO-BW
- 	(5250 - 5330 @ 80), (20), NO-OUTDOOR, DFS, AUTO-BW
- 	(5490 - 5710 @ 160), (27), DFS
-+	# short range devices (ETSI EN 300 440-1)
-+	(5725 - 5875 @ 80), (25 mW)
- 	# 60 GHz band channels 1-4, ref: Etsi En 302 567
- 	(57000 - 66000 @ 2160), (40)
- 
 diff --git a/package/kernel/mac80211/patches/004-backports-add-skb_free_frag.patch b/package/kernel/mac80211/patches/004-backports-add-skb_free_frag.patch
 deleted file mode 100644
 index 9adfd8f..0000000
@@ -1382,157 +1250,6 @@ index 9b672a8..0000000
 - /*
 -  * Complicated way of saying: We only backport netdev_rss_key stuff on kernels
 -  * that either already have net_get_random_once() (>= 3.13) or where we've been
-diff --git a/package/kernel/mac80211/patches/006-add-basic-register-field-manipulation-macros.patch b/package/kernel/mac80211/patches/006-add-basic-register-field-manipulation-macros.patch
-new file mode 100644
-index 0000000..a51edf8
---- /dev/null
-+++ b/package/kernel/mac80211/patches/006-add-basic-register-field-manipulation-macros.patch
-@@ -0,0 +1,145 @@
-+From: Jakub Kicinski <jakub.kicinski@netronome.com>
-+Date: Wed, 31 Aug 2016 12:46:44 +0100
-+Subject: [PATCH] add basic register-field manipulation macros
-+
-+Common approach to accessing register fields is to define
-+structures or sets of macros containing mask and shift pair.
-+Operations on the register are then performed as follows:
-+
-+ field = (reg >> shift) & mask;
-+
-+ reg &= ~(mask << shift);
-+ reg |= (field & mask) << shift;
-+
-+Defining shift and mask separately is tedious.  Ivo van Doorn
-+came up with an idea of computing them at compilation time
-+based on a single shifted mask (later refined by Felix) which
-+can be used like this:
-+
-+ #define REG_FIELD 0x000ff000
-+
-+ field = FIELD_GET(REG_FIELD, reg);
-+
-+ reg &= ~REG_FIELD;
-+ reg |= FIELD_PREP(REG_FIELD, field);
-+
-+FIELD_{GET,PREP} macros take care of finding out what the
-+appropriate shift is based on compilation time ffs operation.
-+
-+GENMASK can be used to define registers (which is usually
-+less error-prone and easier to match with datasheets).
-+
-+This approach is the most convenient I've seen so to limit code
-+multiplication let's move the macros to a global header file.
-+Attempts to use static inlines instead of macros failed due
-+to false positive triggering of BUILD_BUG_ON()s, especially with
-+GCC < 6.0.
-+
-+Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
-+Reviewed-by: Dinan Gunawardena <dinan.gunawardena@netronome.com>
-+---
-+ create mode 100644 include/linux/bitfield.h
-+
-+--- /dev/null
-++++ b/include/linux/bitfield.h
-+@@ -0,0 +1,100 @@
-++/*
-++ * Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
-++ * Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
-++ *
-++ * This program is free software; you can redistribute it and/or modify
-++ * it under the terms of the GNU General Public License version 2
-++ * as published by the Free Software Foundation
-++ *
-++ * This program is distributed in the hope that it will be useful,
-++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-++ * GNU General Public License for more details.
-++ */
-++
-++#ifndef _LINUX_BITFIELD_H
-++#define _LINUX_BITFIELD_H
-++
-++#include <linux/bug.h>
-++
-++#ifdef __CHECKER__
-++#define __BUILD_BUG_ON_NOT_POWER_OF_2(n) (0)
-++#else
-++#define __BUILD_BUG_ON_NOT_POWER_OF_2(n)	\
-++	BUILD_BUG_ON(((n) & ((n) - 1)) != 0)
-++#endif
-++
-++/*
-++ * Bitfield access macros
-++ *
-++ * FIELD_{GET,PREP} macros take as first parameter shifted mask
-++ * from which they extract the base mask and shift amount.
-++ * Mask must be a compilation time constant.
-++ *
-++ * Example:
-++ *
-++ *  #define REG_FIELD_A  GENMASK(6, 0)
-++ *  #define REG_FIELD_B  BIT(7)
-++ *  #define REG_FIELD_C  GENMASK(15, 8)
-++ *  #define REG_FIELD_D  GENMASK(31, 16)
-++ *
-++ * Get:
-++ *  a = FIELD_GET(REG_FIELD_A, reg);
-++ *  b = FIELD_GET(REG_FIELD_B, reg);
-++ *
-++ * Set:
-++ *  reg = FIELD_PREP(REG_FIELD_A, 1) |
-++ *	  FIELD_PREP(REG_FIELD_B, 0) |
-++ *	  FIELD_PREP(REG_FIELD_C, c) |
-++ *	  FIELD_PREP(REG_FIELD_D, 0x40);
-++ *
-++ * Modify:
-++ *  reg &= ~REG_FIELD_C;
-++ *  reg |= FIELD_PREP(REG_FIELD_C, c);
-++ */
-++
-++#define __bf_shf(x) (__builtin_ffsll(x) - 1)
-++
-++#define __BF_FIELD_CHECK(_mask, _reg, _val, _pfx)			\
-++	({								\
-++		BUILD_BUG_ON_MSG(!__builtin_constant_p(_mask),		\
-++				 _pfx "mask is not constant");		\
-++		BUILD_BUG_ON_MSG(!(_mask), _pfx "mask is zero");	\
-++		BUILD_BUG_ON_MSG(__builtin_constant_p(_val) ?		\
-++				 ~((_mask) >> __bf_shf(_mask)) & (_val) : 0, \
-++				 _pfx "value too large for the field"); \
-++		BUILD_BUG_ON_MSG((_mask) > (typeof(_reg))~0ull,		\
-++				 _pfx "type of reg too small for mask"); \
-++		__BUILD_BUG_ON_NOT_POWER_OF_2((_mask) +			\
-++					      (1ULL << __bf_shf(_mask))); \
-++	})
-++
-++/**
-++ * FIELD_PREP() - prepare a bitfield element
-++ * @_mask: shifted mask defining the field's length and position
-++ * @_val:  value to put in the field
-++ *
-++ * FIELD_PREP() masks and shifts up the value.  The result should
-++ * be combined with other fields of the bitfield using logical OR.
-++ */
-++#define FIELD_PREP(_mask, _val)						\
-++	({								\
-++		__BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: ");	\
-++		((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask);	\
-++	})
-++
-++/**
-++ * FIELD_GET() - extract a bitfield element
-++ * @_mask: shifted mask defining the field's length and position
-++ * @_reg:  32bit value of entire bitfield
-++ *
-++ * FIELD_GET() extracts the field specified by @_mask from the
-++ * bitfield passed in as @_reg by masking and shifting it down.
-++ */
-++#define FIELD_GET(_mask, _reg)						\
-++	({								\
-++		__BF_FIELD_CHECK(_mask, _reg, 0U, "FIELD_GET: ");	\
-++		(typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask));	\
-++	})
-++
-++#endif
 diff --git a/package/kernel/mac80211/patches/060-no_local_ssb_bcma.patch b/package/kernel/mac80211/patches/060-no_local_ssb_bcma.patch
 index fd1e1cf..8be5fa1 100644
 --- a/package/kernel/mac80211/patches/060-no_local_ssb_bcma.patch
@@ -5571,5068 +5288,3834 @@ index b646ab3..0000000
 - EXPORT_SYMBOL(ieee80211_data_to_8023);
 - 
 - int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
-diff --git a/package/kernel/mac80211/patches/320-ath9k-fix-using-sta-drv_priv-before-initializing-it.patch b/package/kernel/mac80211/patches/320-ath9k-fix-using-sta-drv_priv-before-initializing-it.patch
+diff --git a/package/kernel/mac80211/patches/320-ath9k-Switch-to-using-mac80211-intermediate-software.patch b/package/kernel/mac80211/patches/320-ath9k-Switch-to-using-mac80211-intermediate-software.patch
 new file mode 100644
-index 0000000..aaa6706
+index 0000000..f8b8f86
 --- /dev/null
-+++ b/package/kernel/mac80211/patches/320-ath9k-fix-using-sta-drv_priv-before-initializing-it.patch
-@@ -0,0 +1,33 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Tue, 2 Aug 2016 13:00:01 +0200
-+Subject: [PATCH] ath9k: fix using sta->drv_priv before initializing it
++++ b/package/kernel/mac80211/patches/320-ath9k-Switch-to-using-mac80211-intermediate-software.patch
+@@ -0,0 +1,871 @@
++From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk>
++Date: Wed, 6 Jul 2016 21:34:17 +0200
++Subject: [PATCH] ath9k: Switch to using mac80211 intermediate software queues.
++MIME-Version: 1.0
++Content-Type: text/plain; charset=UTF-8
++Content-Transfer-Encoding: 8bit
 +
-+A station pointer can be passed to the driver on tx, before it has been
-+marked as associated. Since ath9k_sta_state was initializing the entry
-+too late, it resulted in some spurious crashes.
++This switches ath9k over to using the mac80211 intermediate software
++queueing mechanism for data packets. It removes the queueing inside the
++driver, except for the retry queue, and instead pulls from mac80211 when
++a packet is needed. The retry queue is used to store a packet that was
++pulled but can't be sent immediately.
 +
-+Fixes: df3c6eb34da5 ("ath9k: Use sta_state() callback")
-+Cc: stable@vger.kernel.org
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
++The old code path in ath_tx_start that would queue packets has been
++removed completely, as has the qlen limit tunables (since there's no
++longer a queue in the driver to limit).
 +
-+--- a/drivers/net/wireless/ath/ath9k/main.c
-++++ b/drivers/net/wireless/ath/ath9k/main.c
-+@@ -1563,13 +1563,13 @@ static int ath9k_sta_state(struct ieee80
-+ 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-+ 	int ret = 0;
-+ 
-+-	if (old_state == IEEE80211_STA_AUTH &&
-+-	    new_state == IEEE80211_STA_ASSOC) {
-++	if (old_state == IEEE80211_STA_NOTEXIST &&
-++	    new_state == IEEE80211_STA_NONE) {
-+ 		ret = ath9k_sta_add(hw, vif, sta);
-+ 		ath_dbg(common, CONFIG,
-+ 			"Add station: %pM\n", sta->addr);
-+-	} else if (old_state == IEEE80211_STA_ASSOC &&
-+-		   new_state == IEEE80211_STA_AUTH) {
-++	} else if (old_state == IEEE80211_STA_NONE &&
-++		   new_state == IEEE80211_STA_NOTEXIST) {
-+ 		ret = ath9k_sta_remove(hw, vif, sta);
-+ 		ath_dbg(common, CONFIG,
-+ 			"Remove station: %pM\n", sta->addr);
-diff --git a/package/kernel/mac80211/patches/320-cfg80211-add-support-for-non-linear-skbs-in-ieee8021.patch b/package/kernel/mac80211/patches/320-cfg80211-add-support-for-non-linear-skbs-in-ieee8021.patch
-deleted file mode 100644
-index 2eeed22..0000000
---- a/package/kernel/mac80211/patches/320-cfg80211-add-support-for-non-linear-skbs-in-ieee8021.patch
-+++ /dev/null
-@@ -1,159 +0,0 @@
--From: Felix Fietkau <nbd@openwrt.org>
--Date: Tue, 2 Feb 2016 14:39:10 +0100
--Subject: [PATCH] cfg80211: add support for non-linear skbs in
-- ieee80211_amsdu_to_8023s
--
--Signed-off-by: Felix Fietkau <nbd@openwrt.org>
--Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-----
--
----- a/net/wireless/util.c
--+++ b/net/wireless/util.c
--@@ -644,73 +644,75 @@ int ieee80211_data_from_8023(struct sk_b
-- }
-- EXPORT_SYMBOL(ieee80211_data_from_8023);
-- 
--+static struct sk_buff *
--+__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
--+		       int offset, int len)
--+{
--+	struct sk_buff *frame;
--+
--+	if (skb->len - offset < len)
--+		return NULL;
--+
--+	/*
--+	 * Allocate and reserve two bytes more for payload
--+	 * alignment since sizeof(struct ethhdr) is 14.
--+	 */
--+	frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len);
--+
--+	skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
--+	skb_copy_bits(skb, offset, skb_put(frame, len), len);
--+
--+	return frame;
--+}
-- 
-- void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
-- 			      const u8 *addr, enum nl80211_iftype iftype,
-- 			      const unsigned int extra_headroom,
-- 			      bool has_80211_header)
-- {
--+	unsigned int hlen = ALIGN(extra_headroom, 4);
-- 	struct sk_buff *frame = NULL;
-- 	u16 ethertype;
-- 	u8 *payload;
---	const struct ethhdr *eth;
---	int remaining, err;
---	u8 dst[ETH_ALEN], src[ETH_ALEN];
---
---	if (skb_linearize(skb))
---		goto out;
--+	int offset = 0, remaining, err;
--+	struct ethhdr eth;
--+	bool reuse_skb = true;
--+	bool last = false;
-- 
-- 	if (has_80211_header) {
---		err = ieee80211_data_to_8023(skb, addr, iftype);
--+		err = __ieee80211_data_to_8023(skb, &eth, addr, iftype);
-- 		if (err)
-- 			goto out;
---
---		/* skip the wrapping header */
---		eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
---		if (!eth)
---			goto out;
---	} else {
---		eth = (struct ethhdr *) skb->data;
-- 	}
-- 
---	while (skb != frame) {
--+	while (!last) {
--+		unsigned int subframe_len;
--+		int len;
-- 		u8 padding;
---		__be16 len = eth->h_proto;
---		unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
---
---		remaining = skb->len;
---		memcpy(dst, eth->h_dest, ETH_ALEN);
---		memcpy(src, eth->h_source, ETH_ALEN);
-- 
--+		skb_copy_bits(skb, offset, &eth, sizeof(eth));
--+		len = ntohs(eth.h_proto);
--+		subframe_len = sizeof(struct ethhdr) + len;
-- 		padding = (4 - subframe_len) & 0x3;
--+
-- 		/* the last MSDU has no padding */
--+		remaining = skb->len - offset;
-- 		if (subframe_len > remaining)
-- 			goto purge;
-- 
---		skb_pull(skb, sizeof(struct ethhdr));
--+		offset += sizeof(struct ethhdr);
-- 		/* reuse skb for the last subframe */
---		if (remaining <= subframe_len + padding)
--+		last = remaining <= subframe_len + padding;
--+		if (!skb_is_nonlinear(skb) && last) {
--+			skb_pull(skb, offset);
-- 			frame = skb;
---		else {
---			unsigned int hlen = ALIGN(extra_headroom, 4);
---			/*
---			 * Allocate and reserve two bytes more for payload
---			 * alignment since sizeof(struct ethhdr) is 14.
---			 */
---			frame = dev_alloc_skb(hlen + subframe_len + 2);
--+			reuse_skb = true;
--+		} else {
--+			frame = __ieee80211_amsdu_copy(skb, hlen, offset, len);
-- 			if (!frame)
-- 				goto purge;
-- 
---			skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
---			memcpy(skb_put(frame, ntohs(len)), skb->data,
---				ntohs(len));
---
---			eth = (struct ethhdr *)skb_pull(skb, ntohs(len) +
---							padding);
---			if (!eth) {
---				dev_kfree_skb(frame);
---				goto purge;
---			}
--+			offset += len + padding;
-- 		}
-- 
-- 		skb_reset_network_header(frame);
--@@ -719,24 +721,20 @@ void ieee80211_amsdu_to_8023s(struct sk_
-- 
-- 		payload = frame->data;
-- 		ethertype = (payload[6] << 8) | payload[7];
---
-- 		if (likely((ether_addr_equal(payload, rfc1042_header) &&
-- 			    ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
-- 			   ether_addr_equal(payload, bridge_tunnel_header))) {
---			/* remove RFC1042 or Bridge-Tunnel
---			 * encapsulation and replace EtherType */
---			skb_pull(frame, 6);
---			memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
---			memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
---		} else {
---			memcpy(skb_push(frame, sizeof(__be16)), &len,
---				sizeof(__be16));
---			memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
---			memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
--+			eth.h_proto = htons(ethertype);
--+			skb_pull(frame, ETH_ALEN + 2);
-- 		}
--+
--+		memcpy(skb_push(frame, sizeof(eth)), &eth, sizeof(eth));
-- 		__skb_queue_tail(list, frame);
-- 	}
-- 
--+	if (!reuse_skb)
--+		dev_kfree_skb(skb);
--+
-- 	return;
-- 
--  purge:
-diff --git a/package/kernel/mac80211/patches/321-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch b/package/kernel/mac80211/patches/321-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch
-new file mode 100644
-index 0000000..9caa76d
---- /dev/null
-+++ b/package/kernel/mac80211/patches/321-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch
-@@ -0,0 +1,25 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sat, 9 Jul 2016 15:25:24 +0200
-+Subject: [PATCH] ath9k_hw: reset AHB-WMAC interface on AR91xx
++Based on Tim's original patch set, but reworked quite thoroughly.
 +
-+Should fix a few stability issues
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
++Cc: Tim Shepard <shep@alum.mit.edu>
++Cc: Felix Fietkau <nbd@nbd.name>
++Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
 +---
 +
-+--- a/drivers/net/wireless/ath/ath9k/hw.c
-++++ b/drivers/net/wireless/ath/ath9k/hw.c
-+@@ -1398,8 +1398,12 @@ static bool ath9k_hw_set_reset(struct at
-+ 	if (!AR_SREV_9100(ah))
-+ 		REG_WRITE(ah, AR_RC, 0);
-+ 
-+-	if (AR_SREV_9100(ah))
-++	if (AR_SREV_9100(ah)) {
-++		/* Reset the AHB-WMAC interface */
-++		if (ah->external_reset)
-++			ah->external_reset();
-+ 		udelay(50);
-++	}
++--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
++@@ -91,7 +91,6 @@ int ath_descdma_setup(struct ath_softc *
++ #define ATH_RXBUF               512
++ #define ATH_TXBUF               512
++ #define ATH_TXBUF_RESERVE       5
++-#define ATH_MAX_QDEPTH          (ATH_TXBUF / 4 - ATH_TXBUF_RESERVE)
++ #define ATH_TXMAXTRY            13
++ #define ATH_MAX_SW_RETRIES      30
 + 
-+ 	return true;
-+ }
-diff --git a/package/kernel/mac80211/patches/321-mac80211-Parse-legacy-and-HT-rate-in-injected-frames.patch b/package/kernel/mac80211/patches/321-mac80211-Parse-legacy-and-HT-rate-in-injected-frames.patch
-deleted file mode 100644
-index c4155a1..0000000
---- a/package/kernel/mac80211/patches/321-mac80211-Parse-legacy-and-HT-rate-in-injected-frames.patch
-+++ /dev/null
-@@ -1,155 +0,0 @@
--From: Sven Eckelmann <sven@narfation.org>
--Date: Tue, 26 Jan 2016 17:11:13 +0100
--Subject: [PATCH] mac80211: Parse legacy and HT rate in injected frames
--
--Drivers/devices without their own rate control algorithm can get the
--information what rates they should use from either the radiotap header of
--injected frames or from the rate control algorithm. But the parsing of the
--legacy rate information from the radiotap header was removed in commit
--e6a9854b05c1 ("mac80211/drivers: rewrite the rate control API").
--
--The removal of this feature heavily reduced the usefulness of frame
--injection when wanting to simulate specific transmission behavior. Having
--rate parsing together with MCS rates and retry support allows a fine
--grained selection of the tx behavior of injected frames for these kind of
--tests.
--
--Signed-off-by: Sven Eckelmann <sven@narfation.org>
--Cc: Simon Wunderlich <sw@simonwunderlich.de>
--Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-----
--
----- a/include/net/mac80211.h
--+++ b/include/net/mac80211.h
--@@ -708,12 +708,14 @@ enum mac80211_tx_info_flags {
--  *	protocol frame (e.g. EAP)
--  * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
--  *	frame (PS-Poll or uAPSD).
--+ * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
--  *
--  * These flags are used in tx_info->control.flags.
--  */
-- enum mac80211_tx_control_flags {
-- 	IEEE80211_TX_CTRL_PORT_CTRL_PROTO	= BIT(0),
-- 	IEEE80211_TX_CTRL_PS_RESPONSE		= BIT(1),
--+	IEEE80211_TX_CTRL_RATE_INJECT		= BIT(2),
-- };
-- 
-- /*
----- a/net/mac80211/tx.c
--+++ b/net/mac80211/tx.c
--@@ -710,6 +710,10 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021
-- 
-- 	info->control.short_preamble = txrc.short_preamble;
-- 
--+	/* don't ask rate control when rate already injected via radiotap */
--+	if (info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)
--+		return TX_CONTINUE;
--+
-- 	if (tx->sta)
-- 		assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC);
-- 
--@@ -1665,15 +1669,24 @@ void ieee80211_xmit(struct ieee80211_sub
-- 	ieee80211_tx(sdata, sta, skb, false);
-- }
-- 
---static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
--+static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
--+					struct sk_buff *skb)
-- {
-- 	struct ieee80211_radiotap_iterator iterator;
-- 	struct ieee80211_radiotap_header *rthdr =
-- 		(struct ieee80211_radiotap_header *) skb->data;
-- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
--+	struct ieee80211_supported_band *sband =
--+		local->hw.wiphy->bands[info->band];
-- 	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
-- 						   NULL);
-- 	u16 txflags;
--+	u16 rate = 0;
--+	bool rate_found = false;
--+	u8 rate_retries = 0;
--+	u16 rate_flags = 0;
--+	u8 mcs_known, mcs_flags;
--+	int i;
-- 
-- 	info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
-- 		       IEEE80211_TX_CTL_DONTFRAG;
--@@ -1724,6 +1737,35 @@ static bool ieee80211_parse_tx_radiotap(
-- 				info->flags |= IEEE80211_TX_CTL_NO_ACK;
-- 			break;
-- 
--+		case IEEE80211_RADIOTAP_RATE:
--+			rate = *iterator.this_arg;
--+			rate_flags = 0;
--+			rate_found = true;
--+			break;
--+
--+		case IEEE80211_RADIOTAP_DATA_RETRIES:
--+			rate_retries = *iterator.this_arg;
--+			break;
--+
--+		case IEEE80211_RADIOTAP_MCS:
--+			mcs_known = iterator.this_arg[0];
--+			mcs_flags = iterator.this_arg[1];
--+			if (!(mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_MCS))
--+				break;
--+
--+			rate_found = true;
--+			rate = iterator.this_arg[2];
--+			rate_flags = IEEE80211_TX_RC_MCS;
--+
--+			if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_GI &&
--+			    mcs_flags & IEEE80211_RADIOTAP_MCS_SGI)
--+				rate_flags |= IEEE80211_TX_RC_SHORT_GI;
--+
--+			if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW &&
--+			    mcs_flags & IEEE80211_RADIOTAP_MCS_BW_40)
--+				rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
--+			break;
--+
-- 		/*
-- 		 * Please update the file
-- 		 * Documentation/networking/mac80211-injection.txt
--@@ -1738,6 +1780,32 @@ static bool ieee80211_parse_tx_radiotap(
-- 	if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
-- 		return false;
-- 
--+	if (rate_found) {
--+		info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT;
--+
--+		for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
--+			info->control.rates[i].idx = -1;
--+			info->control.rates[i].flags = 0;
--+			info->control.rates[i].count = 0;
--+		}
--+
--+		if (rate_flags & IEEE80211_TX_RC_MCS) {
--+			info->control.rates[0].idx = rate;
--+		} else {
--+			for (i = 0; i < sband->n_bitrates; i++) {
--+				if (rate * 5 != sband->bitrates[i].bitrate)
--+					continue;
--+
--+				info->control.rates[0].idx = i;
--+				break;
--+			}
--+		}
--+
--+		info->control.rates[0].flags = rate_flags;
--+		info->control.rates[0].count = min_t(u8, rate_retries + 1,
--+						     local->hw.max_rate_tries);
--+	}
--+
-- 	/*
-- 	 * remove the radiotap header
-- 	 * iterator->_max_length was sanity-checked against
--@@ -1819,7 +1887,7 @@ netdev_tx_t ieee80211_monitor_start_xmit
-- 		      IEEE80211_TX_CTL_INJECTED;
-- 
-- 	/* process and remove the injection radiotap header */
---	if (!ieee80211_parse_tx_radiotap(skb))
--+	if (!ieee80211_parse_tx_radiotap(local, skb))
-- 		goto fail;
-- 
-- 	rcu_read_lock();
-diff --git a/package/kernel/mac80211/patches/322-ath9k_hw-issue-external-reset-for-QCA9550.patch b/package/kernel/mac80211/patches/322-ath9k_hw-issue-external-reset-for-QCA9550.patch
-new file mode 100644
-index 0000000..5d4e849
---- /dev/null
-+++ b/package/kernel/mac80211/patches/322-ath9k_hw-issue-external-reset-for-QCA9550.patch
-@@ -0,0 +1,125 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sat, 9 Jul 2016 15:26:44 +0200
-+Subject: [PATCH] ath9k_hw: issue external reset for QCA9550
-+
-+The RTC interface on the SoC needs to be reset along with the rest of
-+the WMAC.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/hw.c
-++++ b/drivers/net/wireless/ath/ath9k/hw.c
-+@@ -1275,39 +1275,56 @@ void ath9k_hw_get_delta_slope_vals(struc
-+ 	*coef_exponent = coef_exp - 16;
-+ }
++@@ -145,7 +144,9 @@ int ath_descdma_setup(struct ath_softc *
++ #define BAW_WITHIN(_start, _bawsz, _seqno) \
++ 	((((_seqno) - (_start)) & 4095) < (_bawsz))
 + 
-+-/* AR9330 WAR:
-+- * call external reset function to reset WMAC if:
-+- * - doing a cold reset
-+- * - we have pending frames in the TX queues.
-+- */
-+-static bool ath9k_hw_ar9330_reset_war(struct ath_hw *ah, int type)
-++static bool ath9k_hw_need_external_reset(struct ath_hw *ah, int type)
-+ {
-+-	int i, npend = 0;
-++	int i;
++-#define ATH_AN_2_TID(_an, _tidno)  (&(_an)->tid[(_tidno)])
+++#define ATH_STA_2_TID(_sta, _tidno) ((struct ath_atx_tid *)(_sta)->txq[_tidno]->drv_priv)
+++#define ATH_VIF_2_TID(_vif) ((struct ath_atx_tid *)(_vif)->txq->drv_priv)
+++#define ATH_AN_2_TID(_an, _tidno) ((_an)->sta ? ATH_STA_2_TID((_an)->sta, _tidno) : ATH_VIF_2_TID((_an)->vif))
 + 
-+-	for (i = 0; i < AR_NUM_QCU; i++) {
-+-		npend = ath9k_hw_numtxpending(ah, i);
-+-		if (npend)
-+-			break;
-++	if (type == ATH9K_RESET_COLD)
-++		return true;
-++
-++	if (AR_SREV_9550(ah))
-++		return true;
-++
-++	/* AR9330 WAR:
-++	 * call external reset function to reset WMAC if:
-++	 * - doing a cold reset
-++	 * - we have pending frames in the TX queues.
-++	 */
-++	if (AR_SREV_9330(ah)) {
-++		for (i = 0; i < AR_NUM_QCU; i++) {
-++			if (ath9k_hw_numtxpending(ah, i))
-++				return true;
-++		}
-+ 	}
++ #define IS_HT_RATE(rate)   (rate & 0x80)
++ #define IS_CCK_RATE(rate)  ((rate >= 0x18) && (rate <= 0x1e))
++@@ -164,7 +165,6 @@ struct ath_txq {
++ 	spinlock_t axq_lock;
++ 	u32 axq_depth;
++ 	u32 axq_ampdu_depth;
++-	bool stopped;
++ 	bool axq_tx_inprogress;
++ 	struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
++ 	u8 txq_headidx;
++@@ -232,7 +232,6 @@ struct ath_buf {
 + 
-+-	if (ah->external_reset &&
-+-	    (npend || type == ATH9K_RESET_COLD)) {
-+-		int reset_err = 0;
-++	return false;
-++}
++ struct ath_atx_tid {
++ 	struct list_head list;
++-	struct sk_buff_head buf_q;
++ 	struct sk_buff_head retry_q;
++ 	struct ath_node *an;
++ 	struct ath_txq *txq;
++@@ -247,13 +246,13 @@ struct ath_atx_tid {
++ 	s8 bar_index;
++ 	bool active;
++ 	bool clear_ps_filter;
+++	bool has_queued;
++ };
 + 
-+-		ath_dbg(ath9k_hw_common(ah), RESET,
-+-			"reset MAC via external reset\n");
-++static bool ath9k_hw_external_reset(struct ath_hw *ah, int type)
-++{
-++	int err;
++ struct ath_node {
++ 	struct ath_softc *sc;
++ 	struct ieee80211_sta *sta; /* station struct we're part of */
++ 	struct ieee80211_vif *vif; /* interface with which we're associated */
++-	struct ath_atx_tid tid[IEEE80211_NUM_TIDS];
 + 
-+-		reset_err = ah->external_reset();
-+-		if (reset_err) {
-+-			ath_err(ath9k_hw_common(ah),
-+-				"External reset failed, err=%d\n",
-+-				reset_err);
-+-			return false;
-+-		}
-++	if (!ah->external_reset || !ath9k_hw_need_external_reset(ah, type))
-++		return true;
++ 	u16 maxampdu;
++ 	u8 mpdudensity;
++@@ -276,7 +275,6 @@ struct ath_tx_control {
++ 	struct ath_node *an;
++ 	struct ieee80211_sta *sta;
++ 	u8 paprd;
++-	bool force_channel;
++ };
 + 
-+-		REG_WRITE(ah, AR_RTC_RESET, 1);
-++	ath_dbg(ath9k_hw_common(ah), RESET,
-++		"reset MAC via external reset\n");
-++
-++	err = ah->external_reset();
-++	if (err) {
-++		ath_err(ath9k_hw_common(ah),
-++			"External reset failed, err=%d\n", err);
-++		return false;
-++	}
-++
-++	if (AR_SREV_9550(ah)) {
-++		REG_WRITE(ah, AR_RTC_RESET, 0);
-++		udelay(10);
-+ 	}
 + 
-++	REG_WRITE(ah, AR_RTC_RESET, 1);
-++	udelay(10);
-++
-+ 	return true;
++@@ -293,7 +291,6 @@ struct ath_tx {
++ 	struct ath_descdma txdma;
++ 	struct ath_txq *txq_map[IEEE80211_NUM_ACS];
++ 	struct ath_txq *uapsdq;
++-	u32 txq_max_pending[IEEE80211_NUM_ACS];
++ 	u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32];
++ };
++ 
++@@ -585,6 +582,7 @@ void ath9k_release_buffered_frames(struc
++ 				   u16 tids, int nframes,
++ 				   enum ieee80211_frame_release_type reason,
++ 				   bool more_data);
+++void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue);
++ 
++ /********/
++ /* VIFs */
++--- a/drivers/net/wireless/ath/ath9k/channel.c
+++++ b/drivers/net/wireless/ath/ath9k/channel.c
++@@ -1007,7 +1007,6 @@ static void ath_scan_send_probe(struct a
++ 		goto error;
++ 
++ 	txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
++-	txctl.force_channel = true;
++ 	if (ath_tx_start(sc->hw, skb, &txctl))
++ 		goto error;
++ 
++@@ -1130,7 +1129,6 @@ ath_chanctx_send_vif_ps_frame(struct ath
++ 	memset(&txctl, 0, sizeof(txctl));
++ 	txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
++ 	txctl.sta = sta;
++-	txctl.force_channel = true;
++ 	if (ath_tx_start(sc->hw, skb, &txctl)) {
++ 		ieee80211_free_txskb(sc->hw, skb);
++ 		return false;
++--- a/drivers/net/wireless/ath/ath9k/debug.c
+++++ b/drivers/net/wireless/ath/ath9k/debug.c
++@@ -600,7 +600,6 @@ static int read_file_xmit(struct seq_fil
++ 	PR("MPDUs XRetried:  ", xretries);
++ 	PR("Aggregates:      ", a_aggr);
++ 	PR("AMPDUs Queued HW:", a_queued_hw);
++-	PR("AMPDUs Queued SW:", a_queued_sw);
++ 	PR("AMPDUs Completed:", a_completed);
++ 	PR("AMPDUs Retried:  ", a_retries);
++ 	PR("AMPDUs XRetried: ", a_xretries);
++@@ -629,8 +628,7 @@ static void print_queue(struct ath_softc
++ 	seq_printf(file, "%s: %d ", "qnum", txq->axq_qnum);
++ 	seq_printf(file, "%s: %2d ", "qdepth", txq->axq_depth);
++ 	seq_printf(file, "%s: %2d ", "ampdu-depth", txq->axq_ampdu_depth);
++-	seq_printf(file, "%s: %3d ", "pending", txq->pending_frames);
++-	seq_printf(file, "%s: %d\n", "stopped", txq->stopped);
+++	seq_printf(file, "%s: %3d\n", "pending", txq->pending_frames);
++ 
++ 	ath_txq_unlock(sc, txq);
 + }
++@@ -1208,7 +1206,6 @@ static const char ath9k_gstrings_stats[]
++ 	AMKSTR(d_tx_mpdu_xretries),
++ 	AMKSTR(d_tx_aggregates),
++ 	AMKSTR(d_tx_ampdus_queued_hw),
++-	AMKSTR(d_tx_ampdus_queued_sw),
++ 	AMKSTR(d_tx_ampdus_completed),
++ 	AMKSTR(d_tx_ampdu_retries),
++ 	AMKSTR(d_tx_ampdu_xretries),
++@@ -1288,7 +1285,6 @@ void ath9k_get_et_stats(struct ieee80211
++ 	AWDATA(xretries);
++ 	AWDATA(a_aggr);
++ 	AWDATA(a_queued_hw);
++-	AWDATA(a_queued_sw);
++ 	AWDATA(a_completed);
++ 	AWDATA(a_retries);
++ 	AWDATA(a_xretries);
++@@ -1346,14 +1342,6 @@ int ath9k_init_debug(struct ath_hw *ah)
++ 				    read_file_xmit);
++ 	debugfs_create_devm_seqfile(sc->dev, "queues", sc->debug.debugfs_phy,
++ 				    read_file_queues);
++-	debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
++-			   &sc->tx.txq_max_pending[IEEE80211_AC_BK]);
++-	debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
++-			   &sc->tx.txq_max_pending[IEEE80211_AC_BE]);
++-	debugfs_create_u32("qlen_vi", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
++-			   &sc->tx.txq_max_pending[IEEE80211_AC_VI]);
++-	debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
++-			   &sc->tx.txq_max_pending[IEEE80211_AC_VO]);
++ 	debugfs_create_devm_seqfile(sc->dev, "misc", sc->debug.debugfs_phy,
++ 				    read_file_misc);
++ 	debugfs_create_devm_seqfile(sc->dev, "reset", sc->debug.debugfs_phy,
++--- a/drivers/net/wireless/ath/ath9k/debug.h
+++++ b/drivers/net/wireless/ath/ath9k/debug.h
++@@ -147,7 +147,6 @@ struct ath_interrupt_stats {
++  * @completed: Total MPDUs (non-aggr) completed
++  * @a_aggr: Total no. of aggregates queued
++  * @a_queued_hw: Total AMPDUs queued to hardware
++- * @a_queued_sw: Total AMPDUs queued to software queues
++  * @a_completed: Total AMPDUs completed
++  * @a_retries: No. of AMPDUs retried (SW)
++  * @a_xretries: No. of AMPDUs dropped due to xretries
++@@ -174,7 +173,6 @@ struct ath_tx_stats {
++ 	u32 xretries;
++ 	u32 a_aggr;
++ 	u32 a_queued_hw;
++-	u32 a_queued_sw;
++ 	u32 a_completed;
++ 	u32 a_retries;
++ 	u32 a_xretries;
++--- a/drivers/net/wireless/ath/ath9k/debug_sta.c
+++++ b/drivers/net/wireless/ath/ath9k/debug_sta.c
++@@ -52,8 +52,8 @@ static ssize_t read_file_node_aggr(struc
++ 			 "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE",
++ 			 "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED");
 + 
-+@@ -1360,24 +1377,23 @@ static bool ath9k_hw_set_reset(struct at
-+ 			rst_flags |= AR_RTC_RC_MAC_COLD;
++-	for (tidno = 0, tid = &an->tid[tidno];
++-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
+++	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
+++		tid = ATH_STA_2_TID(an->sta, tidno);
++ 		txq = tid->txq;
++ 		ath_txq_lock(sc, txq);
++ 		if (tid->active) {
++--- a/drivers/net/wireless/ath/ath9k/init.c
+++++ b/drivers/net/wireless/ath/ath9k/init.c
++@@ -358,7 +358,6 @@ static int ath9k_init_queues(struct ath_
++ 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
++ 		sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i);
++ 		sc->tx.txq_map[i]->mac80211_qnum = i;
++-		sc->tx.txq_max_pending[i] = ATH_MAX_QDEPTH;
 + 	}
++ 	return 0;
++ }
++@@ -873,6 +872,7 @@ static void ath9k_set_hw_capab(struct at
++ 	hw->max_rate_tries = 10;
++ 	hw->sta_data_size = sizeof(struct ath_node);
++ 	hw->vif_data_size = sizeof(struct ath_vif);
+++	hw->txq_data_size = sizeof(struct ath_atx_tid);
++ 	hw->extra_tx_headroom = 4;
 + 
-+-	if (AR_SREV_9330(ah)) {
-+-		if (!ath9k_hw_ar9330_reset_war(ah, type))
-+-			return false;
-+-	}
-+-
-+ 	if (ath9k_hw_mci_is_enabled(ah))
-+ 		ar9003_mci_check_gpm_offset(ah);
++ 	hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1;
++--- a/drivers/net/wireless/ath/ath9k/main.c
+++++ b/drivers/net/wireless/ath/ath9k/main.c
++@@ -2695,4 +2695,5 @@ struct ieee80211_ops ath9k_ops = {
++ 	.sw_scan_start	    = ath9k_sw_scan_start,
++ 	.sw_scan_complete   = ath9k_sw_scan_complete,
++ 	.get_txpower        = ath9k_get_txpower,
+++	.wake_tx_queue      = ath9k_wake_tx_queue,
++ };
++--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++++ b/drivers/net/wireless/ath/ath9k/xmit.c
++@@ -65,6 +65,8 @@ static struct ath_buf *ath_tx_setup_buff
++ 					   struct ath_txq *txq,
++ 					   struct ath_atx_tid *tid,
++ 					   struct sk_buff *skb);
+++static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb,
+++			  struct ath_tx_control *txctl);
 + 
-+ 	/* DMA HALT added to resolve ar9300 and ar9580 bus error during
-+-	 * RTC_RC reg read
-++	 * RTC_RC reg read. Also needed for AR9550 external reset
-+ 	 */
-+-	if (AR_SREV_9300(ah) || AR_SREV_9580(ah)) {
-++	if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9550(ah)) {
-+ 		REG_SET_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
-+ 		ath9k_hw_wait(ah, AR_CFG, AR_CFG_HALT_ACK, AR_CFG_HALT_ACK,
-+ 			      20 * AH_WAIT_TIMEOUT);
-+-		REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
-+ 	}
++ enum {
++ 	MCS_HT20,
++@@ -118,6 +120,26 @@ static void ath_tx_queue_tid(struct ath_
++ 		list_add_tail(&tid->list, list);
++ }
 + 
-++	ath9k_hw_external_reset(ah, type);
+++void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue)
+++{
+++	struct ath_softc *sc = hw->priv;
+++	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+++	struct ath_atx_tid *tid = (struct ath_atx_tid *) queue->drv_priv;
+++	struct ath_txq *txq = tid->txq;
 ++
-++	if (AR_SREV_9300(ah) || AR_SREV_9580(ah))
-++		REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
+++	ath_dbg(common, QUEUE, "Waking TX queue: %pM (%d)\n",
+++		queue->sta ? queue->sta->addr : queue->vif->addr,
+++		tid->tidno);
 ++
-+ 	REG_WRITE(ah, AR_RTC_RC, rst_flags);
+++	ath_txq_lock(sc, txq);
+++
+++	tid->has_queued = true;
+++	ath_tx_queue_tid(sc, txq, tid);
+++	ath_txq_schedule(sc, txq);
+++
+++	ath_txq_unlock(sc, txq);
+++}
+++
++ static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
++ {
++ 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
++@@ -160,7 +182,6 @@ static void ath_set_rates(struct ieee802
++ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
++ 			     struct sk_buff *skb)
++ {
++-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++ 	struct ath_frame_info *fi = get_frame_info(skb);
++ 	int q = fi->txq;
 + 
-+ 	REGWRITE_BUFFER_FLUSH(ah);
-diff --git a/package/kernel/mac80211/patches/322-mac80211-add-A-MSDU-tx-support.patch b/package/kernel/mac80211/patches/322-mac80211-add-A-MSDU-tx-support.patch
-deleted file mode 100644
-index e7bfb9c..0000000
---- a/package/kernel/mac80211/patches/322-mac80211-add-A-MSDU-tx-support.patch
-+++ /dev/null
-@@ -1,317 +0,0 @@
--From: Felix Fietkau <nbd@openwrt.org>
--Date: Fri, 5 Feb 2016 01:38:51 +0100
--Subject: [PATCH] mac80211: add A-MSDU tx support
--
--Requires software tx queueing support. frag_list support (for zero-copy)
--is optional.
--
--Signed-off-by: Felix Fietkau <nbd@openwrt.org>
-----
--
----- a/include/net/mac80211.h
--+++ b/include/net/mac80211.h
--@@ -709,6 +709,7 @@ enum mac80211_tx_info_flags {
--  * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
--  *	frame (PS-Poll or uAPSD).
--  * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
--+ * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
--  *
--  * These flags are used in tx_info->control.flags.
--  */
--@@ -716,6 +717,7 @@ enum mac80211_tx_control_flags {
-- 	IEEE80211_TX_CTRL_PORT_CTRL_PROTO	= BIT(0),
-- 	IEEE80211_TX_CTRL_PS_RESPONSE		= BIT(1),
-- 	IEEE80211_TX_CTRL_RATE_INJECT		= BIT(2),
--+	IEEE80211_TX_CTRL_AMSDU			= BIT(3),
-- };
-- 
-- /*
--@@ -1728,6 +1730,7 @@ struct ieee80211_sta_rates {
--  *		  size is min(max_amsdu_len, 7935) bytes.
--  *	Both additional HT limits must be enforced by the low level driver.
--  *	This is defined by the spec (IEEE 802.11-2012 section 8.3.2.2 NOTE 2).
--+ * @max_rc_amsdu_len: Maximum A-MSDU size in bytes recommended by rate control.
--  * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
--  */
-- struct ieee80211_sta {
--@@ -1748,6 +1751,7 @@ struct ieee80211_sta {
-- 	bool mfp;
-- 	u8 max_amsdu_subframes;
-- 	u16 max_amsdu_len;
--+	u16 max_rc_amsdu_len;
-- 
-- 	struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
-- 
--@@ -1961,6 +1965,15 @@ struct ieee80211_txq {
--  *	order and does not need to manage its own reorder buffer or BA session
--  *	timeout.
--  *
--+ * @IEEE80211_HW_TX_AMSDU: Hardware (or driver) supports software aggregated
--+ *	A-MSDU frames. Requires software tx queueing and fast-xmit support.
--+ *	When not using minstrel/minstrel_ht rate control, the driver should
--+ *	limit the maximum A-MSDU size based on the current tx rate by setting
--+ *	max_rc_amsdu_len in struct ieee80211_sta.
--+ *
--+ * @IEEE80211_HW_TX_FRAG_LIST: Hardware (or driver) supports sending frag_list
--+ *	skbs, needed for zero-copy software A-MSDU.
--+ *
--  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
--  */
-- enum ieee80211_hw_flags {
--@@ -1998,6 +2011,8 @@ enum ieee80211_hw_flags {
-- 	IEEE80211_HW_BEACON_TX_STATUS,
-- 	IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
-- 	IEEE80211_HW_SUPPORTS_REORDERING_BUFFER,
--+	IEEE80211_HW_TX_AMSDU,
--+	IEEE80211_HW_TX_FRAG_LIST,
-- 
-- 	/* keep last, obviously */
-- 	NUM_IEEE80211_HW_FLAGS
--@@ -2070,6 +2085,9 @@ enum ieee80211_hw_flags {
--  *	size is smaller (an example is LinkSys WRT120N with FW v1.0.07
--  *	build 002 Jun 18 2012).
--  *
--+ * @max_tx_fragments: maximum number of tx buffers per (A)-MSDU, sum
--+ *	of 1 + skb_shinfo(skb)->nr_frags for each skb in the frag_list.
--+ *
--  * @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX
--  *	(if %IEEE80211_HW_QUEUE_CONTROL is set)
--  *
--@@ -2124,6 +2142,7 @@ struct ieee80211_hw {
-- 	u8 max_rate_tries;
-- 	u8 max_rx_aggregation_subframes;
-- 	u8 max_tx_aggregation_subframes;
--+	u8 max_tx_fragments;
-- 	u8 offchannel_tx_hw_queue;
-- 	u8 radiotap_mcs_details;
-- 	u16 radiotap_vht_details;
----- a/net/mac80211/agg-tx.c
--+++ b/net/mac80211/agg-tx.c
--@@ -935,6 +935,7 @@ void ieee80211_process_addba_resp(struct
-- 				  size_t len)
-- {
-- 	struct tid_ampdu_tx *tid_tx;
--+	struct ieee80211_txq *txq;
-- 	u16 capab, tid;
-- 	u8 buf_size;
-- 	bool amsdu;
--@@ -945,6 +946,10 @@ void ieee80211_process_addba_resp(struct
-- 	buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
-- 	buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes);
-- 
--+	txq = sta->sta.txq[tid];
--+	if (!amsdu && txq)
--+		set_bit(IEEE80211_TXQ_NO_AMSDU, &to_txq_info(txq)->flags);
--+
-- 	mutex_lock(&sta->ampdu_mlme.mtx);
-- 
-- 	tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
----- a/net/mac80211/debugfs.c
--+++ b/net/mac80211/debugfs.c
--@@ -127,6 +127,8 @@ static const char *hw_flag_names[NUM_IEE
-- 	FLAG(BEACON_TX_STATUS),
-- 	FLAG(NEEDS_UNIQUE_STA_ADDR),
-- 	FLAG(SUPPORTS_REORDERING_BUFFER),
--+	FLAG(TX_AMSDU),
--+	FLAG(TX_FRAG_LIST),
-- 
-- 	/* keep last for the build bug below */
-- 	(void *)0x1
----- a/net/mac80211/ieee80211_i.h
--+++ b/net/mac80211/ieee80211_i.h
--@@ -799,6 +799,7 @@ struct mac80211_qos_map {
-- enum txq_info_flags {
-- 	IEEE80211_TXQ_STOP,
-- 	IEEE80211_TXQ_AMPDU,
--+	IEEE80211_TXQ_NO_AMSDU,
-- };
-- 
-- struct txq_info {
----- a/net/mac80211/tx.c
--+++ b/net/mac80211/tx.c
--@@ -1318,6 +1318,10 @@ struct sk_buff *ieee80211_tx_dequeue(str
-- out:
-- 	spin_unlock_bh(&txqi->queue.lock);
-- 
--+	if (skb && skb_has_frag_list(skb) &&
--+	    !ieee80211_hw_check(&local->hw, TX_FRAG_LIST))
--+		skb_linearize(skb);
--+
-- 	return skb;
-- }
-- EXPORT_SYMBOL(ieee80211_tx_dequeue);
--@@ -2757,6 +2761,163 @@ void ieee80211_clear_fast_xmit(struct st
-- 		kfree_rcu(fast_tx, rcu_head);
-- }
-- 
--+static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local,
--+					struct sk_buff *skb, int headroom,
--+					int *subframe_len)
--+{
--+	int amsdu_len = *subframe_len + sizeof(struct ethhdr);
--+	int padding = (4 - amsdu_len) & 3;
--+
--+	if (skb_headroom(skb) < headroom || skb_tailroom(skb) < padding) {
--+		I802_DEBUG_INC(local->tx_expand_skb_head);
--+
--+		if (pskb_expand_head(skb, headroom, padding, GFP_ATOMIC)) {
--+			wiphy_debug(local->hw.wiphy,
--+				    "failed to reallocate TX buffer\n");
--+			return false;
--+		}
--+	}
--+
--+	if (padding) {
--+		*subframe_len += padding;
--+		memset(skb_put(skb, padding), 0, padding);
--+	}
--+
--+	return true;
--+}
--+
--+static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata,
--+					 struct ieee80211_fast_tx *fast_tx,
--+					 struct sk_buff *skb)
--+{
--+	struct ieee80211_local *local = sdata->local;
--+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
--+	struct ieee80211_hdr *hdr;
--+	struct ethhdr amsdu_hdr;
--+	int hdr_len = fast_tx->hdr_len - sizeof(rfc1042_header);
--+	int subframe_len = skb->len - hdr_len;
--+	void *data;
--+	u8 *qc;
--+
--+	if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
--+		return false;
--+
--+	if (info->control.flags & IEEE80211_TX_CTRL_AMSDU)
--+		return true;
--+
--+	if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(amsdu_hdr),
--+					 &subframe_len))
--+		return false;
--+
--+	amsdu_hdr.h_proto = cpu_to_be16(subframe_len);
--+	memcpy(amsdu_hdr.h_source, skb->data + fast_tx->sa_offs, ETH_ALEN);
--+	memcpy(amsdu_hdr.h_dest, skb->data + fast_tx->da_offs, ETH_ALEN);
--+
--+	data = skb_push(skb, sizeof(amsdu_hdr));
--+	memmove(data, data + sizeof(amsdu_hdr), hdr_len);
--+	memcpy(data + hdr_len, &amsdu_hdr, sizeof(amsdu_hdr));
--+
--+	hdr = data;
--+	qc = ieee80211_get_qos_ctl(hdr);
--+	*qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
--+
--+	info->control.flags |= IEEE80211_TX_CTRL_AMSDU;
--+
--+	return true;
--+}
--+
--+static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
--+				      struct sta_info *sta,
--+				      struct ieee80211_fast_tx *fast_tx,
--+				      struct sk_buff *skb)
--+{
--+	struct ieee80211_local *local = sdata->local;
--+	u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
--+	struct ieee80211_txq *txq = sta->sta.txq[tid];
--+	struct txq_info *txqi;
--+	struct sk_buff **frag_tail, *head;
--+	int subframe_len = skb->len - ETH_ALEN;
--+	u8 max_subframes = sta->sta.max_amsdu_subframes;
--+	int max_frags = local->hw.max_tx_fragments;
--+	int max_amsdu_len = sta->sta.max_amsdu_len;
--+	__be16 len;
--+	void *data;
--+	bool ret = false;
--+	int n = 1, nfrags;
--+
--+	if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
--+		return false;
--+
--+	if (!txq)
--+		return false;
--+
--+	txqi = to_txq_info(txq);
--+	if (test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags))
--+		return false;
--+
--+	if (sta->sta.max_rc_amsdu_len)
--+		max_amsdu_len = min_t(int, max_amsdu_len,
--+				      sta->sta.max_rc_amsdu_len);
--+
--+	spin_lock_bh(&txqi->queue.lock);
--+
--+	head = skb_peek_tail(&txqi->queue);
--+	if (!head)
--+		goto out;
--+
--+	if (skb->len + head->len > max_amsdu_len)
--+		goto out;
--+
--+	/*
--+	 * HT A-MPDU limits maximum MPDU size to 4095 bytes. Since aggregation
--+	 * sessions are started/stopped without txq flush, use the limit here
--+	 * to avoid having to de-aggregate later.
--+	 */
--+	if (skb->len + head->len > 4095 &&
--+	    !sta->sta.vht_cap.vht_supported)
--+		goto out;
--+
--+	if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head))
--+		goto out;
--+
--+	nfrags = 1 + skb_shinfo(skb)->nr_frags;
--+	nfrags += 1 + skb_shinfo(head)->nr_frags;
--+	frag_tail = &skb_shinfo(head)->frag_list;
--+	while (*frag_tail) {
--+		nfrags += 1 + skb_shinfo(*frag_tail)->nr_frags;
--+		frag_tail = &(*frag_tail)->next;
--+		n++;
--+	}
--+
--+	if (max_subframes && n > max_subframes)
--+		goto out;
--+
--+	if (max_frags && nfrags > max_frags)
--+		goto out;
--+
--+	if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) + 2,
--+					 &subframe_len))
--+		return false;
--+
--+	ret = true;
--+	data = skb_push(skb, ETH_ALEN + 2);
--+	memmove(data, data + ETH_ALEN + 2, 2 * ETH_ALEN);
--+
--+	data += 2 * ETH_ALEN;
--+	len = cpu_to_be16(subframe_len);
--+	memcpy(data, &len, 2);
--+	memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));
--+
--+	head->len += skb->len;
--+	head->data_len += skb->len;
--+	*frag_tail = skb;
--+
--+out:
--+	spin_unlock_bh(&txqi->queue.lock);
--+
--+	return ret;
--+}
--+
-- static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-- 				struct net_device *dev, struct sta_info *sta,
-- 				struct ieee80211_fast_tx *fast_tx,
--@@ -2811,6 +2972,10 @@ static bool ieee80211_xmit_fast(struct i
-- 
-- 	ieee80211_tx_stats(dev, skb->len + extra_head);
-- 
--+	if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
--+	    ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
--+		return true;
--+
-- 	/* will not be crypto-handled beyond what we do here, so use false
-- 	 * as the may-encrypt argument for the resize to not account for
-- 	 * more room than we already have in 'extra_head'
-diff --git a/package/kernel/mac80211/patches/323-0000-brcmfmac-fix-setting-primary-channel-for-80-MHz-widt.patch b/package/kernel/mac80211/patches/323-0000-brcmfmac-fix-setting-primary-channel-for-80-MHz-widt.patch
-deleted file mode 100644
-index 9277b2c..0000000
---- a/package/kernel/mac80211/patches/323-0000-brcmfmac-fix-setting-primary-channel-for-80-MHz-widt.patch
-+++ /dev/null
-@@ -1,64 +0,0 @@
--From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
--Date: Wed, 20 Jan 2016 16:46:04 +0100
--Subject: [PATCH] brcmfmac: fix setting primary channel for 80 MHz width
--MIME-Version: 1.0
--Content-Type: text/plain; charset=UTF-8
--Content-Transfer-Encoding: 8bit
--
--First of all it changes the way we calculate primary channel offset. If
--we use e.g. 80 MHz channel with primary frequency 5180 MHz (which means
--center frequency is 5210 MHz) it makes sense to calculate primary offset
--as -30 MHz.
--Then it fixes values we compare primary_offset with. We were comparing
--offset in MHz against -2 or 2 which was resulting in picking a wrong
--primary channel.
--
--Signed-off-by: Rafał Miłecki <zajec5@gmail.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
--@@ -247,7 +247,7 @@ static u16 chandef_to_chanspec(struct br
-- 	brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
-- 		  ch->chan->center_freq, ch->center_freq1, ch->width);
-- 	ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
---	primary_offset = ch->center_freq1 - ch->chan->center_freq;
--+	primary_offset = ch->chan->center_freq - ch->center_freq1;
-- 	switch (ch->width) {
-- 	case NL80211_CHAN_WIDTH_20:
-- 	case NL80211_CHAN_WIDTH_20_NOHT:
--@@ -256,24 +256,21 @@ static u16 chandef_to_chanspec(struct br
-- 		break;
-- 	case NL80211_CHAN_WIDTH_40:
-- 		ch_inf.bw = BRCMU_CHAN_BW_40;
---		if (primary_offset < 0)
--+		if (primary_offset > 0)
-- 			ch_inf.sb = BRCMU_CHAN_SB_U;
-- 		else
-- 			ch_inf.sb = BRCMU_CHAN_SB_L;
-- 		break;
-- 	case NL80211_CHAN_WIDTH_80:
-- 		ch_inf.bw = BRCMU_CHAN_BW_80;
---		if (primary_offset < 0) {
---			if (primary_offset < -CH_10MHZ_APART)
---				ch_inf.sb = BRCMU_CHAN_SB_UU;
---			else
---				ch_inf.sb = BRCMU_CHAN_SB_UL;
---		} else {
---			if (primary_offset > CH_10MHZ_APART)
---				ch_inf.sb = BRCMU_CHAN_SB_LL;
---			else
---				ch_inf.sb = BRCMU_CHAN_SB_LU;
---		}
--+		if (primary_offset == -30)
--+			ch_inf.sb = BRCMU_CHAN_SB_LL;
--+		else if (primary_offset == -10)
--+			ch_inf.sb = BRCMU_CHAN_SB_LU;
--+		else if (primary_offset == 10)
--+			ch_inf.sb = BRCMU_CHAN_SB_UL;
--+		else
--+			ch_inf.sb = BRCMU_CHAN_SB_UU;
-- 		break;
-- 	case NL80211_CHAN_WIDTH_80P80:
-- 	case NL80211_CHAN_WIDTH_160:
-diff --git a/package/kernel/mac80211/patches/323-0001-brcmfmac-analyze-descriptors-of-current-component-on.patch b/package/kernel/mac80211/patches/323-0001-brcmfmac-analyze-descriptors-of-current-component-on.patch
-deleted file mode 100644
-index d7018da..0000000
---- a/package/kernel/mac80211/patches/323-0001-brcmfmac-analyze-descriptors-of-current-component-on.patch
-+++ /dev/null
-@@ -1,51 +0,0 @@
--From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
--Date: Tue, 26 Jan 2016 17:57:01 +0100
--Subject: [PATCH] brcmfmac: analyze descriptors of current component only
--MIME-Version: 1.0
--Content-Type: text/plain; charset=UTF-8
--Content-Transfer-Encoding: 8bit
--
--So far we were looking for address descriptors without a check for
--crossing current component border. In case of dealing with unsupported
--descriptor or descriptor missing at all the code would incorrectly get
--data from another component.
--
--Consider this binary-described component from BCM4366 EROM:
--4bf83b01	TAG==CI		CID==0x83b
--20080201	TAG==CI		PORTS==0+1	WRAPPERS==0+1
--18400035	TAG==ADDR	SZ_SZD		TYPE_SLAVE
--00050000
--18107085	TAG==ADDR	SZ_4K		TYPE_SWRAP
--
--Driver was assigning invalid base address to this core:
--brcmfmac:  [6 ] core 0x83b:32 base 0x18109000 wrap 0x18107000
--which came from totally different component defined in EROM:
--43b36701	TAG==CI		CID==0x367
--00000201	TAG==CI		PORTS==0+1	WRAPPERS==0+0
--18109005	TAG==ADDR	SZ_4K		TYPE_SLAVE
--
--This change will also allow us to support components without wrapper
--address in the future.
--
--Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
--Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
-----
--
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
--@@ -803,7 +803,14 @@ static int brcmf_chip_dmp_get_regaddr(st
-- 				*eromaddr -= 4;
-- 				return -EFAULT;
-- 			}
---		} while (desc != DMP_DESC_ADDRESS);
--+		} while (desc != DMP_DESC_ADDRESS &&
--+			 desc != DMP_DESC_COMPONENT);
--+
--+		/* stop if we crossed current component border */
--+		if (desc == DMP_DESC_COMPONENT) {
--+			*eromaddr -= 4;
--+			return 0;
--+		}
-- 
-- 		/* skip upper 32-bit address descriptor */
-- 		if (val & DMP_DESC_ADDRSIZE_GT32)
-diff --git a/package/kernel/mac80211/patches/323-0002-brcmfmac-allow-storing-PMU-core-without-wrapper-addr.patch b/package/kernel/mac80211/patches/323-0002-brcmfmac-allow-storing-PMU-core-without-wrapper-addr.patch
-deleted file mode 100644
-index 045ab49..0000000
---- a/package/kernel/mac80211/patches/323-0002-brcmfmac-allow-storing-PMU-core-without-wrapper-addr.patch
-+++ /dev/null
-@@ -1,28 +0,0 @@
--From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
--Date: Tue, 26 Jan 2016 17:57:02 +0100
--Subject: [PATCH] brcmfmac: allow storing PMU core without wrapper address
--MIME-Version: 1.0
--Content-Type: text/plain; charset=UTF-8
--Content-Transfer-Encoding: 8bit
--
--Separated PMU core can be found in new devices and should be used for
--accessing PMU registers (which were routed through ChipCommon so far).
--This core is one of exceptions that doesn't have or need wrapper address
--to be still safely accessible.
--
--Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
--Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
-----
--
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
--@@ -883,7 +883,8 @@ int brcmf_chip_dmp_erom_scan(struct brcm
-- 		rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S;
-- 
-- 		/* need core with ports */
---		if (nmw + nsw == 0)
--+		if (nmw + nsw == 0 &&
--+		    id != BCMA_CORE_PMU)
-- 			continue;
-- 
-- 		/* try to obtain register address info */
-diff --git a/package/kernel/mac80211/patches/323-0003-brcmfmac-read-extended-capabilities-of-ChipCommon-co.patch b/package/kernel/mac80211/patches/323-0003-brcmfmac-read-extended-capabilities-of-ChipCommon-co.patch
-deleted file mode 100644
-index 7b7ba4f..0000000
---- a/package/kernel/mac80211/patches/323-0003-brcmfmac-read-extended-capabilities-of-ChipCommon-co.patch
-+++ /dev/null
-@@ -1,43 +0,0 @@
--From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
--Date: Tue, 26 Jan 2016 17:57:03 +0100
--Subject: [PATCH] brcmfmac: read extended capabilities of ChipCommon core
--MIME-Version: 1.0
--Content-Type: text/plain; charset=UTF-8
--Content-Transfer-Encoding: 8bit
--
--This is an extra bitfield with info about some present hardware.
--
--Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
--Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
-----
--
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
--@@ -1025,6 +1025,9 @@ static int brcmf_chip_setup(struct brcmf
-- 	/* get chipcommon capabilites */
-- 	pub->cc_caps = chip->ops->read32(chip->ctx,
-- 					 CORE_CC_REG(base, capabilities));
--+	pub->cc_caps_ext = chip->ops->read32(chip->ctx,
--+					     CORE_CC_REG(base,
--+							 capabilities_ext));
-- 
-- 	/* get pmu caps & rev */
-- 	if (pub->cc_caps & CC_CAP_PMU) {
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
--@@ -27,6 +27,7 @@
--  * @chip: chip identifier.
--  * @chiprev: chip revision.
--  * @cc_caps: chipcommon core capabilities.
--+ * @cc_caps_ext: chipcommon core extended capabilities.
--  * @pmucaps: PMU capabilities.
--  * @pmurev: PMU revision.
--  * @rambase: RAM base address (only applicable for ARM CR4 chips).
--@@ -38,6 +39,7 @@ struct brcmf_chip {
-- 	u32 chip;
-- 	u32 chiprev;
-- 	u32 cc_caps;
--+	u32 cc_caps_ext;
-- 	u32 pmucaps;
-- 	u32 pmurev;
-- 	u32 rambase;
-diff --git a/package/kernel/mac80211/patches/323-0004-brcmfmac-access-PMU-registers-using-standalone-PMU-c.patch b/package/kernel/mac80211/patches/323-0004-brcmfmac-access-PMU-registers-using-standalone-PMU-c.patch
-deleted file mode 100644
-index 2af6fd9..0000000
---- a/package/kernel/mac80211/patches/323-0004-brcmfmac-access-PMU-registers-using-standalone-PMU-c.patch
-+++ /dev/null
-@@ -1,148 +0,0 @@
--From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
--Date: Tue, 26 Jan 2016 17:57:04 +0100
--Subject: [PATCH] brcmfmac: access PMU registers using standalone PMU core if
-- available
--MIME-Version: 1.0
--Content-Type: text/plain; charset=UTF-8
--Content-Transfer-Encoding: 8bit
--
--On recent Broadcom chipsets PMU is present as separated core and it
--can't be accessed using ChipCommon anymore as it fails with e.g.:
--[   18.198412] Unhandled fault: imprecise external abort (0x1406) at 0xb6da200f
--
--Add a new helper function that will return a proper core that should be
--used for accessing PMU registers.
--
--Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
--Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
-----
--
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
--@@ -1014,6 +1014,7 @@ static int brcmf_chip_setup(struct brcmf
-- {
-- 	struct brcmf_chip *pub;
-- 	struct brcmf_core_priv *cc;
--+	struct brcmf_core *pmu;
-- 	u32 base;
-- 	u32 val;
-- 	int ret = 0;
--@@ -1030,9 +1031,10 @@ static int brcmf_chip_setup(struct brcmf
-- 							 capabilities_ext));
-- 
-- 	/* get pmu caps & rev */
--+	pmu = brcmf_chip_get_pmu(pub); /* after reading cc_caps_ext */
-- 	if (pub->cc_caps & CC_CAP_PMU) {
-- 		val = chip->ops->read32(chip->ctx,
---					CORE_CC_REG(base, pmucapabilities));
--+					CORE_CC_REG(pmu->base, pmucapabilities));
-- 		pub->pmurev = val & PCAP_REV_MASK;
-- 		pub->pmucaps = val;
-- 	}
--@@ -1131,6 +1133,23 @@ struct brcmf_core *brcmf_chip_get_chipco
-- 	return &cc->pub;
-- }
-- 
--+struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub)
--+{
--+	struct brcmf_core *cc = brcmf_chip_get_chipcommon(pub);
--+	struct brcmf_core *pmu;
--+
--+	/* See if there is separated PMU core available */
--+	if (cc->rev >= 35 &&
--+	    pub->cc_caps_ext & BCMA_CC_CAP_EXT_AOB_PRESENT) {
--+		pmu = brcmf_chip_get_core(pub, BCMA_CORE_PMU);
--+		if (pmu)
--+			return pmu;
--+	}
--+
--+	/* Fallback to ChipCommon core for older hardware */
--+	return cc;
--+}
--+
-- bool brcmf_chip_iscoreup(struct brcmf_core *pub)
-- {
-- 	struct brcmf_core_priv *core;
--@@ -1301,6 +1320,7 @@ bool brcmf_chip_sr_capable(struct brcmf_
-- {
-- 	u32 base, addr, reg, pmu_cc3_mask = ~0;
-- 	struct brcmf_chip_priv *chip;
--+	struct brcmf_core *pmu = brcmf_chip_get_pmu(pub);
-- 
-- 	brcmf_dbg(TRACE, "Enter\n");
-- 
--@@ -1320,9 +1340,9 @@ bool brcmf_chip_sr_capable(struct brcmf_
-- 	case BRCM_CC_4335_CHIP_ID:
-- 	case BRCM_CC_4339_CHIP_ID:
-- 		/* read PMU chipcontrol register 3 */
---		addr = CORE_CC_REG(base, chipcontrol_addr);
--+		addr = CORE_CC_REG(pmu->base, chipcontrol_addr);
-- 		chip->ops->write32(chip->ctx, addr, 3);
---		addr = CORE_CC_REG(base, chipcontrol_data);
--+		addr = CORE_CC_REG(pmu->base, chipcontrol_data);
-- 		reg = chip->ops->read32(chip->ctx, addr);
-- 		return (reg & pmu_cc3_mask) != 0;
-- 	case BRCM_CC_43430_CHIP_ID:
--@@ -1330,12 +1350,12 @@ bool brcmf_chip_sr_capable(struct brcmf_
-- 		reg = chip->ops->read32(chip->ctx, addr);
-- 		return reg != 0;
-- 	default:
---		addr = CORE_CC_REG(base, pmucapabilities_ext);
--+		addr = CORE_CC_REG(pmu->base, pmucapabilities_ext);
-- 		reg = chip->ops->read32(chip->ctx, addr);
-- 		if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
-- 			return false;
-- 
---		addr = CORE_CC_REG(base, retention_ctl);
--+		addr = CORE_CC_REG(pmu->base, retention_ctl);
-- 		reg = chip->ops->read32(chip->ctx, addr);
-- 		return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
-- 			       PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
--@@ -85,6 +85,7 @@ struct brcmf_chip *brcmf_chip_attach(voi
-- void brcmf_chip_detach(struct brcmf_chip *chip);
-- struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid);
-- struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip);
--+struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub);
-- bool brcmf_chip_iscoreup(struct brcmf_core *core);
-- void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset);
-- void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
--@@ -3615,7 +3615,6 @@ brcmf_sdio_drivestrengthinit(struct brcm
-- 	const struct sdiod_drive_str *str_tab = NULL;
-- 	u32 str_mask;
-- 	u32 str_shift;
---	u32 base;
-- 	u32 i;
-- 	u32 drivestrength_sel = 0;
-- 	u32 cc_data_temp;
--@@ -3658,14 +3657,15 @@ brcmf_sdio_drivestrengthinit(struct brcm
-- 	}
-- 
-- 	if (str_tab != NULL) {
--+		struct brcmf_core *pmu = brcmf_chip_get_pmu(ci);
--+
-- 		for (i = 0; str_tab[i].strength != 0; i++) {
-- 			if (drivestrength >= str_tab[i].strength) {
-- 				drivestrength_sel = str_tab[i].sel;
-- 				break;
-- 			}
-- 		}
---		base = brcmf_chip_get_chipcommon(ci)->base;
---		addr = CORE_CC_REG(base, chipcontrol_addr);
--+		addr = CORE_CC_REG(pmu->base, chipcontrol_addr);
-- 		brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
-- 		cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
-- 		cc_data_temp &= ~str_mask;
--@@ -3835,8 +3835,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdi
-- 		goto fail;
-- 
-- 	/* set PMUControl so a backplane reset does PMU state reload */
---	reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base,
---			       pmucontrol);
--+	reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol);
-- 	reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);
-- 	if (err)
-- 		goto fail;
-diff --git a/package/kernel/mac80211/patches/323-0005-brcmfmac-add-support-for-14e4-4365-PCI-ID-with-BCM43.patch b/package/kernel/mac80211/patches/323-0005-brcmfmac-add-support-for-14e4-4365-PCI-ID-with-BCM43.patch
-deleted file mode 100644
-index 35887fc..0000000
---- a/package/kernel/mac80211/patches/323-0005-brcmfmac-add-support-for-14e4-4365-PCI-ID-with-BCM43.patch
-+++ /dev/null
-@@ -1,38 +0,0 @@
--From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
--Date: Tue, 26 Jan 2016 17:57:05 +0100
--Subject: [PATCH] brcmfmac: add support for 14e4:4365 PCI ID with BCM4366
-- chipset
--MIME-Version: 1.0
--Content-Type: text/plain; charset=UTF-8
--Content-Transfer-Encoding: 8bit
--
--On Broadcom ARM routers BCM4366 cards are available with 14e4:4365 ID.
--Unfortunately this ID was already used by Broadcom for cards with
--BCM43142, a totally different chipset requiring SoftMAC driver. To avoid
--a conflict between brcmfmac and bcma use more specific ID entry with
--subvendor and subdevice specified.
--
--Signed-off-by: Rafał Miłecki <zajec5@gmail.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
--@@ -1951,6 +1951,9 @@ static const struct dev_pm_ops brcmf_pci
-- 
-- #define BRCMF_PCIE_DEVICE(dev_id)	{ BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\
-- 	PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
--+#define BRCMF_PCIE_DEVICE_SUB(dev_id, subvend, subdev)	{ \
--+	BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\
--+	subvend, subdev, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
-- 
-- static struct pci_device_id brcmf_pcie_devid_table[] = {
-- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID),
--@@ -1966,6 +1969,7 @@ static struct pci_device_id brcmf_pcie_d
-- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID),
-- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID),
-- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID),
--+	BRCMF_PCIE_DEVICE_SUB(0x4365, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4365),
-- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID),
-- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID),
-- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID),
-diff --git a/package/kernel/mac80211/patches/323-ath9k-Fix-programming-of-minCCA-power-threshold.patch b/package/kernel/mac80211/patches/323-ath9k-Fix-programming-of-minCCA-power-threshold.patch
-new file mode 100644
-index 0000000..59ac29b
---- /dev/null
-+++ b/package/kernel/mac80211/patches/323-ath9k-Fix-programming-of-minCCA-power-threshold.patch
-@@ -0,0 +1,26 @@
-+From: Sven Eckelmann <sven@narfation.org>
-+Date: Fri, 17 Jun 2016 11:58:20 +0200
-+Subject: [PATCH] ath9k: Fix programming of minCCA power threshold
-+
-+The function ar9003_hw_apply_minccapwr_thresh takes as second parameter not
-+a pointer to the channel but a boolean value describing whether the channel
-+is 2.4GHz or not. This broke (according to the origin commit) the ETSI
-+regulatory compliance on 5GHz channels.
-+
-+Fixes: 3533bf6b15a0 ("ath9k: Fix regulatory compliance")
-+Signed-off-by: Sven Eckelmann <sven@narfation.org>
-+Cc: Simon Wunderlich <sw@simonwunderlich.de>
-+Cc: Sujith Manoharan <c_manoha@qca.qualcomm.com>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
-++++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
-+@@ -4175,7 +4175,7 @@ static void ath9k_hw_ar9300_set_board_va
-+ 	if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah) && !AR_SREV_9531(ah))
-+ 		ar9003_hw_internal_regulator_apply(ah);
-+ 	ar9003_hw_apply_tuning_caps(ah);
-+-	ar9003_hw_apply_minccapwr_thresh(ah, chan);
-++	ar9003_hw_apply_minccapwr_thresh(ah, is2ghz);
-+ 	ar9003_hw_txend_to_xpa_off_apply(ah, is2ghz);
-+ 	ar9003_hw_thermometer_apply(ah);
-+ 	ar9003_hw_thermo_cal_apply(ah);
-diff --git a/package/kernel/mac80211/patches/324-ath9k_hw-fix-spectral-scan-on-AR9285-and-newer.patch b/package/kernel/mac80211/patches/324-ath9k_hw-fix-spectral-scan-on-AR9285-and-newer.patch
-new file mode 100644
-index 0000000..b6f4868
---- /dev/null
-+++ b/package/kernel/mac80211/patches/324-ath9k_hw-fix-spectral-scan-on-AR9285-and-newer.patch
-@@ -0,0 +1,86 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Mon, 11 Jul 2016 10:34:37 +0200
-+Subject: [PATCH] ath9k_hw: fix spectral scan on AR9285 and newer
-+
-+The register layout of AR_PHY_SPECTRAL_SCAN has changed, only AR9280
-+uses the old layout
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
-++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
-+@@ -476,6 +476,7 @@ static void ar9002_hw_set_bt_ant_diversi
-+ static void ar9002_hw_spectral_scan_config(struct ath_hw *ah,
-+ 				    struct ath_spec_scan *param)
-+ {
-++	u32 repeat_bit;
-+ 	u8 count;
-+ 
-+ 	if (!param->enabled) {
-+@@ -486,12 +487,15 @@ static void ar9002_hw_spectral_scan_conf
-+ 	REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_FFT_ENA);
-+ 	REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE);
-+ 
-++	if (AR_SREV_9280(ah))
-++		repeat_bit = AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT;
-++	else
-++		repeat_bit = AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI;
-++
-+ 	if (param->short_repeat)
-+-		REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
-+-			    AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);
-++		REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, repeat_bit);
-+ 	else
-+-		REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN,
-+-			    AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);
-++		REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN, repeat_bit);
-+ 
-+ 	/* on AR92xx, the highest bit of count will make the the chip send
-+ 	 * spectral samples endlessly. Check if this really was intended,
-+@@ -499,15 +503,25 @@ static void ar9002_hw_spectral_scan_conf
-+ 	 */
-+ 	count = param->count;
-+ 	if (param->endless) {
-+-		if (AR_SREV_9271(ah))
-+-			count = 0;
-+-		else
-++		if (AR_SREV_9280(ah))
-+ 			count = 0x80;
-++		else
-++			count = 0;
-+ 	} else if (count & 0x80)
-+ 		count = 0x7f;
-++	else if (!count)
-++		count = 1;
-++
-++	if (AR_SREV_9280(ah)) {
-++		REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
-++			      AR_PHY_SPECTRAL_SCAN_COUNT, count);
-++	} else {
-++		REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
-++			      AR_PHY_SPECTRAL_SCAN_COUNT_KIWI, count);
-++		REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
-++			    AR_PHY_SPECTRAL_SCAN_PHYERR_MASK_SELECT);
-++	}
-+ 
-+-	REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
-+-		      AR_PHY_SPECTRAL_SCAN_COUNT, count);
-+ 	REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
-+ 		      AR_PHY_SPECTRAL_SCAN_PERIOD, param->period);
-+ 	REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
-+--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h
-++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
-+@@ -177,8 +177,11 @@
-+ #define AR_PHY_SPECTRAL_SCAN_PERIOD_S		8
-+ #define AR_PHY_SPECTRAL_SCAN_COUNT		0x00FF0000  /* Number of reports, reg 68, bits 16-23*/
-+ #define AR_PHY_SPECTRAL_SCAN_COUNT_S		16
-++#define AR_PHY_SPECTRAL_SCAN_COUNT_KIWI		0x0FFF0000  /* Number of reports, reg 68, bits 16-27*/
-++#define AR_PHY_SPECTRAL_SCAN_COUNT_KIWI_S	16
-+ #define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT	0x01000000  /* Short repeat, reg 68, bit 24*/
-+-#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S	24  /* Short repeat, reg 68, bit 24*/
-++#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI	0x10000000  /* Short repeat, reg 68, bit 28*/
-++#define AR_PHY_SPECTRAL_SCAN_PHYERR_MASK_SELECT	0x40000000
-+ 
-+ #define AR_PHY_RX_DELAY           0x9914
-+ #define AR_PHY_SEARCH_START_DELAY 0x9918
-diff --git a/package/kernel/mac80211/patches/324-brcmfmac-treat-NULL-character-in-NVRAM-as-separator.patch b/package/kernel/mac80211/patches/324-brcmfmac-treat-NULL-character-in-NVRAM-as-separator.patch
-deleted file mode 100644
-index 6ce60f1..0000000
---- a/package/kernel/mac80211/patches/324-brcmfmac-treat-NULL-character-in-NVRAM-as-separator.patch
-+++ /dev/null
-@@ -1,32 +0,0 @@
--From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
--Date: Sun, 31 Jan 2016 12:14:34 +0100
--Subject: [PATCH] brcmfmac: treat NULL character in NVRAM as separator
--MIME-Version: 1.0
--Content-Type: text/plain; charset=UTF-8
--Content-Transfer-Encoding: 8bit
--
--Platform NVRAM (stored on a flash partition) has entries separated by a
--NULL (\0) char. Our parsing code switches from VALUE state to IDLE
--whenever it meets a NULL (\0). When that happens our IDLE handler should
--simply consume it and analyze whatever is placed ahead.
--
--This fixes harmless warnings spamming debugging output:
--[  155.165624] brcmfmac: brcmf_nvram_handle_idle warning: ln=1:col=20: ignoring invalid character
--[  155.180806] brcmfmac: brcmf_nvram_handle_idle warning: ln=1:col=44: ignoring invalid character
--[  155.195971] brcmfmac: brcmf_nvram_handle_idle warning: ln=1:col=63: ignoring invalid character
--
--Signed-off-by: Rafał Miłecki <zajec5@gmail.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
--@@ -93,7 +93,7 @@ static enum nvram_parser_state brcmf_nvr
-- 	c = nvp->data[nvp->pos];
-- 	if (c == '\n')
-- 		return COMMENT;
---	if (is_whitespace(c))
--+	if (is_whitespace(c) || c == '\0')
-- 		goto proceed;
-- 	if (c == '#')
-- 		return COMMENT;
-diff --git a/package/kernel/mac80211/patches/325-ath9k_hw-fix-duplicate-and-partially-wrong-definitio.patch b/package/kernel/mac80211/patches/325-ath9k_hw-fix-duplicate-and-partially-wrong-definitio.patch
-new file mode 100644
-index 0000000..6685f33
---- /dev/null
-+++ b/package/kernel/mac80211/patches/325-ath9k_hw-fix-duplicate-and-partially-wrong-definitio.patch
-@@ -0,0 +1,57 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Mon, 11 Jul 2016 11:31:39 +0200
-+Subject: [PATCH] ath9k_hw: fix duplicate (and partially wrong) definition
-+ of AR_CH0_THERM
-+
-+AR_PHY_65NM_CH0_THERM and AR_CH0_THERM were supposed to refer to the
-+same register, however they had different SREV checks.
-+
-+Remove the duplicate and use the checks. Since there were other SREV
-+checks present in the only place that uses this, this will probaby not
-+affect runtime behavior.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
-++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
-+@@ -689,13 +689,6 @@
-+ #define AR_CH0_TOP_XPABIASLVL (AR_SREV_9550(ah) ? 0x3c0 : 0x300)
-+ #define AR_CH0_TOP_XPABIASLVL_S (AR_SREV_9550(ah) ? 6 : 8)
-+ 
-+-#define AR_CH0_THERM	(AR_SREV_9300(ah) ? 0x16290 : \
-+-				((AR_SREV_9485(ah) ? 0x1628c : 0x16294)))
-+-#define AR_CH0_THERM_XPABIASLVL_MSB 0x3
-+-#define AR_CH0_THERM_XPABIASLVL_MSB_S 0
-+-#define AR_CH0_THERM_XPASHORT2GND 0x4
-+-#define AR_CH0_THERM_XPASHORT2GND_S 2
-+-
-+ #define AR_SWITCH_TABLE_COM_ALL (0xffff)
-+ #define AR_SWITCH_TABLE_COM_ALL_S (0)
-+ #define AR_SWITCH_TABLE_COM_AR9462_ALL (0xffffff)
-+@@ -712,15 +705,17 @@
-+ #define AR_SWITCH_TABLE_ALL (0xfff)
-+ #define AR_SWITCH_TABLE_ALL_S (0)
-+ 
-+-#define AR_PHY_65NM_CH0_THERM       (AR_SREV_9300(ah) ? 0x16290 :\
-+-				     ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16294 : 0x1628c))
-++#define AR_CH0_THERM       (AR_SREV_9300(ah) ? 0x16290 :\
-++			    ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16294 : 0x1628c))
-++#define AR_CH0_THERM_XPABIASLVL_MSB 0x3
-++#define AR_CH0_THERM_XPABIASLVL_MSB_S 0
-++#define AR_CH0_THERM_XPASHORT2GND 0x4
-++#define AR_CH0_THERM_XPASHORT2GND_S 2
-+ 
-+-#define AR_PHY_65NM_CH0_THERM_LOCAL   0x80000000
-+-#define AR_PHY_65NM_CH0_THERM_LOCAL_S 31
-+-#define AR_PHY_65NM_CH0_THERM_START   0x20000000
-+-#define AR_PHY_65NM_CH0_THERM_START_S 29
-+-#define AR_PHY_65NM_CH0_THERM_SAR_ADC_OUT   0x0000ff00
-+-#define AR_PHY_65NM_CH0_THERM_SAR_ADC_OUT_S 8
-++#define AR_CH0_THERM_LOCAL   0x80000000
-++#define AR_CH0_THERM_START   0x20000000
-++#define AR_CH0_THERM_SAR_ADC_OUT   0x0000ff00
-++#define AR_CH0_THERM_SAR_ADC_OUT_S 8
-+ 
-+ #define AR_CH0_TOP2		(AR_SREV_9300(ah) ? 0x1628c : \
-+ 					(AR_SREV_9462(ah) ? 0x16290 : 0x16284))
-diff --git a/package/kernel/mac80211/patches/325-brcmfmac-sdio-Increase-the-default-timeouts-a-bit.patch b/package/kernel/mac80211/patches/325-brcmfmac-sdio-Increase-the-default-timeouts-a-bit.patch
-deleted file mode 100644
-index 012dea1..0000000
---- a/package/kernel/mac80211/patches/325-brcmfmac-sdio-Increase-the-default-timeouts-a-bit.patch
-+++ /dev/null
-@@ -1,41 +0,0 @@
--From: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
--Date: Mon, 25 Jan 2016 11:47:29 +0100
--Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
--
--On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
--the card responds very quickly most of the time, unfortunately during
--initialisation it sometimes seems to take just a bit over 2 seconds to
--respond.
--
--This results intialization failing with message like:
--  brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
--  brcmf_bus_start: failed: -52
--  brcmf_sdio_firmware_callback: dongle is not responding
--
--Increasing the timeout to allow for a bit more headroom allows the
--card to initialize reliably.
--
--A quick search online after diagnosing/fixing this showed that Google
--has a similar patch in their ChromeOS tree, so this doesn't seem
--specific to the board I'm using.
--
--Signed-off-by: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
--Reviewed-by: Julian Calaby <julian.calaby@gmail.com>
--Acked-by: Arend van Spriel <arend@broadcom.com>
--Reviewed-by: Douglas Anderson <dianders@chromium.org>
--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
--@@ -45,8 +45,8 @@
-- #include "chip.h"
-- #include "firmware.h"
-- 
---#define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2000)
---#define CTL_DONE_TIMEOUT	msecs_to_jiffies(2000)
--+#define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2500)
--+#define CTL_DONE_TIMEOUT	msecs_to_jiffies(2500)
-- 
-- #ifdef DEBUG
-- 
-diff --git a/package/kernel/mac80211/patches/326-ath9k-make-NF-load-complete-quickly-and-reliably.patch b/package/kernel/mac80211/patches/326-ath9k-make-NF-load-complete-quickly-and-reliably.patch
-deleted file mode 100644
-index 71f7a40..0000000
---- a/package/kernel/mac80211/patches/326-ath9k-make-NF-load-complete-quickly-and-reliably.patch
-+++ /dev/null
-@@ -1,87 +0,0 @@
--From: Miaoqing Pan <miaoqing@codeaurora.org>
--Date: Fri, 5 Feb 2016 09:45:50 +0800
--Subject: [PATCH] ath9k: make NF load complete quickly and reliably
--
--Make NF load complete quickly and reliably. NF load execution
--is delayed by HW to end of frame if frame Rx or Tx is ongoing.
--Increasing timeout to max frame duration. If NF cal is ongoing
--before NF load, stop it before load, and restart it afterwards.
--
--Signed-off-by: Miaoqing Pan <miaoqing@codeaurora.org>
-----
--
----- a/drivers/net/wireless/ath/ath9k/calib.c
--+++ b/drivers/net/wireless/ath/ath9k/calib.c
--@@ -241,6 +241,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
-- 	u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
-- 	struct ath_common *common = ath9k_hw_common(ah);
-- 	s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
--+	u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL);
-- 
-- 	if (ah->caldata)
-- 		h = ah->caldata->nfCalHist;
--@@ -264,6 +265,16 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
-- 	}
-- 
-- 	/*
--+	 * stop NF cal if ongoing to ensure NF load completes immediately
--+	 * (or after end rx/tx frame if ongoing)
--+	 */
--+	if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
--+		REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
--+		REG_RMW_BUFFER_FLUSH(ah);
--+		ENABLE_REG_RMW_BUFFER(ah);
--+	}
--+
--+	/*
-- 	 * Load software filtered NF value into baseband internal minCCApwr
-- 	 * variable.
-- 	 */
--@@ -276,18 +287,33 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
-- 
-- 	/*
-- 	 * Wait for load to complete, should be fast, a few 10s of us.
---	 * The max delay was changed from an original 250us to 10000us
---	 * since 250us often results in NF load timeout and causes deaf
---	 * condition during stress testing 12/12/2009
--+	 * The max delay was changed from an original 250us to 22.2 msec.
--+	 * This would increase timeout to the longest possible frame
--+	 * (11n max length 22.1 msec)
-- 	 */
---	for (j = 0; j < 10000; j++) {
--+	for (j = 0; j < 22200; j++) {
-- 		if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
---		     AR_PHY_AGC_CONTROL_NF) == 0)
--+			      AR_PHY_AGC_CONTROL_NF) == 0)
-- 			break;
-- 		udelay(10);
-- 	}
-- 
-- 	/*
--+	 * Restart NF so it can continue.
--+	 */
--+	if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
--+		ENABLE_REG_RMW_BUFFER(ah);
--+		if (bb_agc_ctl & AR_PHY_AGC_CONTROL_ENABLE_NF)
--+			REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
--+				    AR_PHY_AGC_CONTROL_ENABLE_NF);
--+		if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NO_UPDATE_NF)
--+			REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
--+				    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
--+		REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
--+		REG_RMW_BUFFER_FLUSH(ah);
--+	}
--+
--+	/*
-- 	 * We timed out waiting for the noisefloor to load, probably due to an
-- 	 * in-progress rx. Simply return here and allow the load plenty of time
-- 	 * to complete before the next calibration interval.  We need to avoid
--@@ -296,7 +322,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
-- 	 * here, the baseband nf cal will just be capped by our present
-- 	 * noisefloor until the next calibration timer.
-- 	 */
---	if (j == 10000) {
--+	if (j == 22200) {
-- 		ath_dbg(common, ANY,
-- 			"Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
-- 			REG_READ(ah, AR_PHY_AGC_CONTROL));
-diff --git a/package/kernel/mac80211/patches/326-ath9k_hw-simplify-ar9003_hw_per_calibration.patch b/package/kernel/mac80211/patches/326-ath9k_hw-simplify-ar9003_hw_per_calibration.patch
-new file mode 100644
-index 0000000..999d993
---- /dev/null
-+++ b/package/kernel/mac80211/patches/326-ath9k_hw-simplify-ar9003_hw_per_calibration.patch
-@@ -0,0 +1,88 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Mon, 11 Jul 2016 11:34:47 +0200
-+Subject: [PATCH] ath9k_hw: simplify ar9003_hw_per_calibration
-+
-+Reduce indentation, use a variable to save a few pointer dereferences
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
-++++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
-+@@ -75,50 +75,49 @@ static bool ar9003_hw_per_calibration(st
-+ 				      struct ath9k_cal_list *currCal)
-+ {
-+ 	struct ath9k_hw_cal_data *caldata = ah->caldata;
-+-	/* Cal is assumed not done until explicitly set below */
-+-	bool iscaldone = false;
-++	const struct ath9k_percal_data *cur_caldata = currCal->calData;
-+ 
-+ 	/* Calibration in progress. */
-+ 	if (currCal->calState == CAL_RUNNING) {
-+ 		/* Check to see if it has finished. */
-+-		if (!(REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)) {
-+-			/*
-+-			* Accumulate cal measures for active chains
-+-			*/
-+-			currCal->calData->calCollect(ah);
-+-			ah->cal_samples++;
-++		if (REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)
-++			return false;
-+ 
-+-			if (ah->cal_samples >=
-+-			    currCal->calData->calNumSamples) {
-+-				unsigned int i, numChains = 0;
-+-				for (i = 0; i < AR9300_MAX_CHAINS; i++) {
-+-					if (rxchainmask & (1 << i))
-+-						numChains++;
-+-				}
-++		/*
-++		* Accumulate cal measures for active chains
-++		*/
-++		cur_caldata->calCollect(ah);
-++		ah->cal_samples++;
-+ 
-+-				/*
-+-				* Process accumulated data
-+-				*/
-+-				currCal->calData->calPostProc(ah, numChains);
-++		if (ah->cal_samples >= cur_caldata->calNumSamples) {
-++			unsigned int i, numChains = 0;
-++			for (i = 0; i < AR9300_MAX_CHAINS; i++) {
-++				if (rxchainmask & (1 << i))
-++					numChains++;
-++			}
-+ 
-+-				/* Calibration has finished. */
-+-				caldata->CalValid |= currCal->calData->calType;
-+-				currCal->calState = CAL_DONE;
-+-				iscaldone = true;
-+-			} else {
-++			/*
-++			* Process accumulated data
-++			*/
-++			cur_caldata->calPostProc(ah, numChains);
-++
-++			/* Calibration has finished. */
-++			caldata->CalValid |= cur_caldata->calType;
-++			currCal->calState = CAL_DONE;
-++			return true;
-++		} else {
-+ 			/*
-+ 			 * Set-up collection of another sub-sample until we
-+ 			 * get desired number
-+ 			 */
-+ 			ar9003_hw_setup_calibration(ah, currCal);
-+-			}
-+ 		}
-+-	} else if (!(caldata->CalValid & currCal->calData->calType)) {
-++	} else if (!(caldata->CalValid & cur_caldata->calType)) {
-+ 		/* If current cal is marked invalid in channel, kick it off */
-+ 		ath9k_hw_reset_calibration(ah, currCal);
-+ 	}
-+ 
-+-	return iscaldone;
-++	return false;
-+ }
-+ 
-+ static int ar9003_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
-diff --git a/package/kernel/mac80211/patches/327-ath9k_hw-get-rid-of-some-duplicate-code-in-calibrati.patch b/package/kernel/mac80211/patches/327-ath9k_hw-get-rid-of-some-duplicate-code-in-calibrati.patch
-new file mode 100644
-index 0000000..b7f3823
---- /dev/null
-+++ b/package/kernel/mac80211/patches/327-ath9k_hw-get-rid-of-some-duplicate-code-in-calibrati.patch
-@@ -0,0 +1,94 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Mon, 11 Jul 2016 11:35:20 +0200
-+Subject: [PATCH] ath9k_hw: get rid of some duplicate code in calibration
-+ init
-+
-+Remove a misleading debug message as well
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
-++++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
-+@@ -1373,6 +1373,26 @@ static void ar9003_hw_cl_cal_post_proc(s
-+ 	}
-+ }
-+ 
-++static void ar9003_hw_init_cal_common(struct ath_hw *ah)
-++{
-++	struct ath9k_hw_cal_data *caldata = ah->caldata;
-++
-++	/* Initialize list pointers */
-++	ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
-++
-++	INIT_CAL(&ah->iq_caldata);
-++	INSERT_CAL(ah, &ah->iq_caldata);
-++
-++	/* Initialize current pointer to first element in list */
-++	ah->cal_list_curr = ah->cal_list;
-++
-++	if (ah->cal_list_curr)
-++		ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
-++
-++	if (caldata)
-++		caldata->CalValid = 0;
-++}
-++
-+ static bool ar9003_hw_init_cal_pcoem(struct ath_hw *ah,
-+ 				     struct ath9k_channel *chan)
-+ {
-+@@ -1532,21 +1552,7 @@ skip_tx_iqcal:
-+ 	/* Revert chainmask to runtime parameters */
-+ 	ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
-+ 
-+-	/* Initialize list pointers */
-+-	ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
-+-
-+-	INIT_CAL(&ah->iq_caldata);
-+-	INSERT_CAL(ah, &ah->iq_caldata);
-+-	ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n");
-+-
-+-	/* Initialize current pointer to first element in list */
-+-	ah->cal_list_curr = ah->cal_list;
-+-
-+-	if (ah->cal_list_curr)
-+-		ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
-+-
-+-	if (caldata)
-+-		caldata->CalValid = 0;
-++	ar9003_hw_init_cal_common(ah);
-+ 
-+ 	return true;
-+ }
-+@@ -1577,8 +1583,6 @@ static bool do_ar9003_agc_cal(struct ath
-+ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
-+ 				   struct ath9k_channel *chan)
-+ {
-+-	struct ath_common *common = ath9k_hw_common(ah);
-+-	struct ath9k_hw_cal_data *caldata = ah->caldata;
-+ 	bool txiqcal_done = false;
-+ 	bool status = true;
-+ 	bool run_agc_cal = false, sep_iq_cal = false;
-+@@ -1676,21 +1680,7 @@ skip_tx_iqcal:
-+ 	/* Revert chainmask to runtime parameters */
-+ 	ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
-+ 
-+-	/* Initialize list pointers */
-+-	ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
-+-
-+-	INIT_CAL(&ah->iq_caldata);
-+-	INSERT_CAL(ah, &ah->iq_caldata);
-+-	ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n");
-+-
-+-	/* Initialize current pointer to first element in list */
-+-	ah->cal_list_curr = ah->cal_list;
-+-
-+-	if (ah->cal_list_curr)
-+-		ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
-+-
-+-	if (caldata)
-+-		caldata->CalValid = 0;
-++	ar9003_hw_init_cal_common(ah);
-+ 
-+ 	return true;
-+ }
-diff --git a/package/kernel/mac80211/patches/327-mac80211-Remove-MPP-table-entries-with-MPath.patch b/package/kernel/mac80211/patches/327-mac80211-Remove-MPP-table-entries-with-MPath.patch
-deleted file mode 100644
-index f7f9df9..0000000
---- a/package/kernel/mac80211/patches/327-mac80211-Remove-MPP-table-entries-with-MPath.patch
-+++ /dev/null
-@@ -1,54 +0,0 @@
--From: Henning Rogge <hrogge@gmail.com>
--Date: Wed, 3 Feb 2016 13:58:36 +0100
--Subject: [PATCH] mac80211: Remove MPP table entries with MPath
--
--Make the mesh_path_del() function remove all mpp table entries
--that are proxied by the removed mesh path.
--
--Acked-by: Bob Copeland <me@bobcopeland.com>
--Signed-off-by: Henning Rogge <henning.rogge@fkie.fraunhofer.de>
--Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-----
--
----- a/net/mac80211/mesh_pathtbl.c
--+++ b/net/mac80211/mesh_pathtbl.c
--@@ -835,6 +835,29 @@ void mesh_path_flush_by_nexthop(struct s
-- 	rcu_read_unlock();
-- }
-- 
--+static void mpp_flush_by_proxy(struct ieee80211_sub_if_data *sdata,
--+			       const u8 *proxy)
--+{
--+	struct mesh_table *tbl;
--+	struct mesh_path *mpp;
--+	struct mpath_node *node;
--+	int i;
--+
--+	rcu_read_lock();
--+	read_lock_bh(&pathtbl_resize_lock);
--+	tbl = resize_dereference_mpp_paths();
--+	for_each_mesh_entry(tbl, node, i) {
--+		mpp = node->mpath;
--+		if (ether_addr_equal(mpp->mpp, proxy)) {
--+			spin_lock(&tbl->hashwlock[i]);
--+			__mesh_path_del(tbl, node);
--+			spin_unlock(&tbl->hashwlock[i]);
--+		}
--+	}
--+	read_unlock_bh(&pathtbl_resize_lock);
--+	rcu_read_unlock();
--+}
--+
-- static void table_flush_by_iface(struct mesh_table *tbl,
-- 				 struct ieee80211_sub_if_data *sdata)
-- {
--@@ -892,6 +915,9 @@ int mesh_path_del(struct ieee80211_sub_i
-- 	int hash_idx;
-- 	int err = 0;
-- 
--+	/* flush relevant mpp entries first */
--+	mpp_flush_by_proxy(sdata, addr);
--+
-- 	read_lock_bh(&pathtbl_resize_lock);
-- 	tbl = resize_dereference_mesh_paths();
-- 	hash_idx = mesh_table_hash(addr, sdata, tbl);
-diff --git a/package/kernel/mac80211/patches/328-mac80211-let-unused-MPP-table-entries-timeout.patch b/package/kernel/mac80211/patches/328-mac80211-let-unused-MPP-table-entries-timeout.patch
-deleted file mode 100644
-index 740993c..0000000
---- a/package/kernel/mac80211/patches/328-mac80211-let-unused-MPP-table-entries-timeout.patch
-+++ /dev/null
-@@ -1,104 +0,0 @@
--From: Henning Rogge <hrogge@gmail.com>
--Date: Wed, 3 Feb 2016 13:58:37 +0100
--Subject: [PATCH] mac80211: let unused MPP table entries timeout
--
--Remember the last time when a mpp table entry is used for
--rx or tx and remove them after MESH_PATH_EXPIRE time.
--
--Acked-by: Bob Copeland <me@bobcopeland.com>
--Signed-off-by: Henning Rogge <henning.rogge@fkie.fraunhofer.de>
--Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-----
--
----- a/net/mac80211/mesh_pathtbl.c
--+++ b/net/mac80211/mesh_pathtbl.c
--@@ -942,6 +942,46 @@ enddel:
-- }
-- 
-- /**
--+ * mpp_path_del - delete a mesh proxy path from the table
--+ *
--+ * @addr: addr address (ETH_ALEN length)
--+ * @sdata: local subif
--+ *
--+ * Returns: 0 if successful
--+ */
--+static int mpp_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
--+{
--+	struct mesh_table *tbl;
--+	struct mesh_path *mpath;
--+	struct mpath_node *node;
--+	struct hlist_head *bucket;
--+	int hash_idx;
--+	int err = 0;
--+
--+	read_lock_bh(&pathtbl_resize_lock);
--+	tbl = resize_dereference_mpp_paths();
--+	hash_idx = mesh_table_hash(addr, sdata, tbl);
--+	bucket = &tbl->hash_buckets[hash_idx];
--+
--+	spin_lock(&tbl->hashwlock[hash_idx]);
--+	hlist_for_each_entry(node, bucket, list) {
--+		mpath = node->mpath;
--+		if (mpath->sdata == sdata &&
--+		    ether_addr_equal(addr, mpath->dst)) {
--+			__mesh_path_del(tbl, node);
--+			goto enddel;
--+		}
--+	}
--+
--+	err = -ENXIO;
--+enddel:
--+	mesh_paths_generation++;
--+	spin_unlock(&tbl->hashwlock[hash_idx]);
--+	read_unlock_bh(&pathtbl_resize_lock);
--+	return err;
--+}
--+
--+/**
--  * mesh_path_tx_pending - sends pending frames in a mesh path queue
--  *
--  * @mpath: mesh path to activate
--@@ -1157,6 +1197,17 @@ void mesh_path_expire(struct ieee80211_s
-- 		     time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
-- 			mesh_path_del(mpath->sdata, mpath->dst);
-- 	}
--+
--+	tbl = rcu_dereference(mpp_paths);
--+	for_each_mesh_entry(tbl, node, i) {
--+		if (node->mpath->sdata != sdata)
--+			continue;
--+		mpath = node->mpath;
--+		if ((!(mpath->flags & MESH_PATH_FIXED)) &&
--+		    time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
--+			mpp_path_del(mpath->sdata, mpath->dst);
--+	}
--+
-- 	rcu_read_unlock();
-- }
-- 
----- a/net/mac80211/rx.c
--+++ b/net/mac80211/rx.c
--@@ -2291,6 +2291,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
-- 			spin_lock_bh(&mppath->state_lock);
-- 			if (!ether_addr_equal(mppath->mpp, mpp_addr))
-- 				memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
--+			mppath->exp_time = jiffies;
-- 			spin_unlock_bh(&mppath->state_lock);
-- 		}
-- 		rcu_read_unlock();
----- a/net/mac80211/tx.c
--+++ b/net/mac80211/tx.c
--@@ -2171,8 +2171,11 @@ static struct sk_buff *ieee80211_build_h
-- 					mpp_lookup = true;
-- 			}
-- 
---			if (mpp_lookup)
--+			if (mpp_lookup) {
-- 				mppath = mpp_path_lookup(sdata, skb->data);
--+				if (mppath)
--+					mppath->exp_time = jiffies;
--+			}
-- 
-- 			if (mppath && mpath)
-- 				mesh_path_del(mpath->sdata, mpath->dst);
-diff --git a/package/kernel/mac80211/patches/329-mac80211-Unify-mesh-and-mpp-path-removal-function.patch b/package/kernel/mac80211/patches/329-mac80211-Unify-mesh-and-mpp-path-removal-function.patch
-deleted file mode 100644
-index 0c36b1d..0000000
---- a/package/kernel/mac80211/patches/329-mac80211-Unify-mesh-and-mpp-path-removal-function.patch
-+++ /dev/null
-@@ -1,143 +0,0 @@
--From: Henning Rogge <hrogge@gmail.com>
--Date: Wed, 3 Feb 2016 13:58:38 +0100
--Subject: [PATCH] mac80211: Unify mesh and mpp path removal function
--
--mpp_path_del() and mesh_path_del() are mostly the same function.
--Move common code into a new static function.
--
--Acked-by: Bob Copeland <me@bobcopeland.com>
--Signed-off-by: Henning Rogge <henning.rogge@fkie.fraunhofer.de>
--Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-----
--
----- a/net/mac80211/mesh_pathtbl.c
--+++ b/net/mac80211/mesh_pathtbl.c
--@@ -55,16 +55,21 @@ int mpp_paths_generation;
-- static DEFINE_RWLOCK(pathtbl_resize_lock);
-- 
-- 
--+static inline struct mesh_table *resize_dereference_paths(
--+	struct mesh_table __rcu *table)
--+{
--+	return rcu_dereference_protected(table,
--+					lockdep_is_held(&pathtbl_resize_lock));
--+}
--+
-- static inline struct mesh_table *resize_dereference_mesh_paths(void)
-- {
---	return rcu_dereference_protected(mesh_paths,
---		lockdep_is_held(&pathtbl_resize_lock));
--+	return resize_dereference_paths(mesh_paths);
-- }
-- 
-- static inline struct mesh_table *resize_dereference_mpp_paths(void)
-- {
---	return rcu_dereference_protected(mpp_paths,
---		lockdep_is_held(&pathtbl_resize_lock));
--+	return resize_dereference_paths(mpp_paths);
-- }
-- 
-- /*
--@@ -899,14 +904,17 @@ void mesh_path_flush_by_iface(struct iee
-- }
-- 
-- /**
--- * mesh_path_del - delete a mesh path from the table
--+ * table_path_del - delete a path from the mesh or mpp table
--  *
--- * @addr: dst address (ETH_ALEN length)
--+ * @tbl: mesh or mpp path table
--  * @sdata: local subif
--+ * @addr: dst address (ETH_ALEN length)
--  *
--  * Returns: 0 if successful
--  */
---int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
--+static int table_path_del(struct mesh_table __rcu *rcu_tbl,
--+			  struct ieee80211_sub_if_data *sdata,
--+			  const u8 *addr)
-- {
-- 	struct mesh_table *tbl;
-- 	struct mesh_path *mpath;
--@@ -915,11 +923,7 @@ int mesh_path_del(struct ieee80211_sub_i
-- 	int hash_idx;
-- 	int err = 0;
-- 
---	/* flush relevant mpp entries first */
---	mpp_flush_by_proxy(sdata, addr);
---
---	read_lock_bh(&pathtbl_resize_lock);
---	tbl = resize_dereference_mesh_paths();
--+	tbl = resize_dereference_paths(rcu_tbl);
-- 	hash_idx = mesh_table_hash(addr, sdata, tbl);
-- 	bucket = &tbl->hash_buckets[hash_idx];
-- 
--@@ -935,9 +939,30 @@ int mesh_path_del(struct ieee80211_sub_i
-- 
-- 	err = -ENXIO;
-- enddel:
---	mesh_paths_generation++;
-- 	spin_unlock(&tbl->hashwlock[hash_idx]);
--+	return err;
--+}
--+
--+/**
--+ * mesh_path_del - delete a mesh path from the table
--+ *
--+ * @addr: dst address (ETH_ALEN length)
--+ * @sdata: local subif
--+ *
--+ * Returns: 0 if successful
--+ */
--+int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
--+{
--+	int err = 0;
--+
--+	/* flush relevant mpp entries first */
--+	mpp_flush_by_proxy(sdata, addr);
--+
--+	read_lock_bh(&pathtbl_resize_lock);
--+	err = table_path_del(mesh_paths, sdata, addr);
--+	mesh_paths_generation++;
-- 	read_unlock_bh(&pathtbl_resize_lock);
--+
-- 	return err;
-- }
-- 
--@@ -951,33 +976,13 @@ enddel:
--  */
-- static int mpp_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
-- {
---	struct mesh_table *tbl;
---	struct mesh_path *mpath;
---	struct mpath_node *node;
---	struct hlist_head *bucket;
---	int hash_idx;
-- 	int err = 0;
-- 
-- 	read_lock_bh(&pathtbl_resize_lock);
---	tbl = resize_dereference_mpp_paths();
---	hash_idx = mesh_table_hash(addr, sdata, tbl);
---	bucket = &tbl->hash_buckets[hash_idx];
---
---	spin_lock(&tbl->hashwlock[hash_idx]);
---	hlist_for_each_entry(node, bucket, list) {
---		mpath = node->mpath;
---		if (mpath->sdata == sdata &&
---		    ether_addr_equal(addr, mpath->dst)) {
---			__mesh_path_del(tbl, node);
---			goto enddel;
---		}
---	}
---
---	err = -ENXIO;
---enddel:
---	mesh_paths_generation++;
---	spin_unlock(&tbl->hashwlock[hash_idx]);
--+	err = table_path_del(mpp_paths, sdata, addr);
--+	mpp_paths_generation++;
-- 	read_unlock_bh(&pathtbl_resize_lock);
--+
-- 	return err;
-- }
-- 
-diff --git a/package/kernel/mac80211/patches/329-mac80211-fix-check-for-buffered-powersave-frames-wit.patch b/package/kernel/mac80211/patches/329-mac80211-fix-check-for-buffered-powersave-frames-wit.patch
-new file mode 100644
-index 0000000..38e541c
---- /dev/null
-+++ b/package/kernel/mac80211/patches/329-mac80211-fix-check-for-buffered-powersave-frames-wit.patch
-@@ -0,0 +1,21 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Mon, 11 Jul 2016 15:07:06 +0200
-+Subject: [PATCH] mac80211: fix check for buffered powersave frames with txq
-+
-+The logic was inverted here, set the bit if frames are pending.
-+
-+Fixes: ba8c3d6f16a1 ("mac80211: add an intermediate software queue implementation")
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/net/mac80211/rx.c
-++++ b/net/mac80211/rx.c
-+@@ -1268,7 +1268,7 @@ static void sta_ps_start(struct sta_info
-+ 	for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
-+ 		struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
-+ 
-+-		if (!txqi->tin.backlog_packets)
-++		if (txqi->tin.backlog_packets)
-+ 			set_bit(tid, &sta->txq_buffered_tids);
-+ 		else
-+ 			clear_bit(tid, &sta->txq_buffered_tids);
-diff --git a/package/kernel/mac80211/patches/330-ath10k-fix-rx-status-reporting-for-A-MSDU-subframes.patch b/package/kernel/mac80211/patches/330-ath10k-fix-rx-status-reporting-for-A-MSDU-subframes.patch
-new file mode 100644
-index 0000000..a6031b9
---- /dev/null
-+++ b/package/kernel/mac80211/patches/330-ath10k-fix-rx-status-reporting-for-A-MSDU-subframes.patch
-@@ -0,0 +1,36 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sun, 17 Jul 2016 12:49:59 +0200
-+Subject: [PATCH] ath10k: fix rx status reporting for A-MSDU subframes
-+
-+Patch by Nagarajan, Ashok Raj <arnagara@qti.qualcomm.com>
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
-++++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
-+@@ -1525,7 +1525,7 @@ static void ath10k_htt_rx_h_filter(struc
-+ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
-+ {
-+ 	struct ath10k *ar = htt->ar;
-+-	static struct ieee80211_rx_status rx_status;
-++	struct ieee80211_rx_status *rx_status = &htt->rx_status;
-+ 	struct sk_buff_head amsdu;
-+ 	int ret;
-+ 
-+@@ -1549,11 +1549,11 @@ static int ath10k_htt_rx_handle_amsdu(st
-+ 		return ret;
-+ 	}
-+ 
-+-	ath10k_htt_rx_h_ppdu(ar, &amsdu, &rx_status, 0xffff);
-++	ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
-+ 	ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
-+-	ath10k_htt_rx_h_filter(ar, &amsdu, &rx_status);
-+-	ath10k_htt_rx_h_mpdu(ar, &amsdu, &rx_status);
-+-	ath10k_htt_rx_h_deliver(ar, &amsdu, &rx_status);
-++	ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
-++	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
-++	ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
-+ 
-+ 	return 0;
-+ }
-diff --git a/package/kernel/mac80211/patches/330-mac80211-minstrel-Change-expected-throughput-unit-ba.patch b/package/kernel/mac80211/patches/330-mac80211-minstrel-Change-expected-throughput-unit-ba.patch
-deleted file mode 100644
-index 4dc6d66..0000000
---- a/package/kernel/mac80211/patches/330-mac80211-minstrel-Change-expected-throughput-unit-ba.patch
-+++ /dev/null
-@@ -1,51 +0,0 @@
--From: Sven Eckelmann <sven.eckelmann@open-mesh.com>
--Date: Tue, 2 Feb 2016 08:12:26 +0100
--Subject: [PATCH] mac80211: minstrel: Change expected throughput unit back to
-- Kbps
--
--The change from cur_tp to the function
--minstrel_get_tp_avg/minstrel_ht_get_tp_avg changed the unit used for the
--current throughput. For example in minstrel_ht the correct
--conversion between them would be:
--
--    mrs->cur_tp / 10 == minstrel_ht_get_tp_avg(..).
--
--This factor 10 must also be included in the calculation of
--minstrel_get_expected_throughput and minstrel_ht_get_expected_throughput to
--return values with the unit [Kbps] instead of [10Kbps]. Otherwise routing
--algorithms like B.A.T.M.A.N. V will make incorrect decision based on these
--values. Its kernel based implementation expects expected_throughput always
--to have the unit [Kbps] and not sometimes [10Kbps] and sometimes [Kbps].
--
--The same requirement has iw or olsrdv2's nl80211 based statistics module
--which retrieve the same data via NL80211_STA_INFO_TX_BITRATE.
--
--Cc: stable@vger.kernel.org
--Fixes: 6a27b2c40b48 ("mac80211: restructure per-rate throughput calculation into function")
--Signed-off-by: Sven Eckelmann <sven@open-mesh.com>
--Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-----
--
----- a/net/mac80211/rc80211_minstrel.c
--+++ b/net/mac80211/rc80211_minstrel.c
--@@ -711,7 +711,7 @@ static u32 minstrel_get_expected_through
-- 	 * computing cur_tp
-- 	 */
-- 	tmp_mrs = &mi->r[idx].stats;
---	tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma);
--+	tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma) * 10;
-- 	tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024;
-- 
-- 	return tmp_cur_tp;
----- a/net/mac80211/rc80211_minstrel_ht.c
--+++ b/net/mac80211/rc80211_minstrel_ht.c
--@@ -1335,7 +1335,8 @@ static u32 minstrel_ht_get_expected_thro
-- 	prob = mi->groups[i].rates[j].prob_ewma;
-- 
-- 	/* convert tp_avg from pkt per second in kbps */
---	tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * AVG_PKT_SIZE * 8 / 1024;
--+	tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * 10;
--+	tp_avg = tp_avg * AVG_PKT_SIZE * 8 / 1024;
-- 
-- 	return tp_avg;
-- }
-diff --git a/package/kernel/mac80211/patches/331-brcmfmac-Increase-nr-of-supported-flowrings.patch b/package/kernel/mac80211/patches/331-brcmfmac-Increase-nr-of-supported-flowrings.patch
-deleted file mode 100644
-index 1fd016f..0000000
---- a/package/kernel/mac80211/patches/331-brcmfmac-Increase-nr-of-supported-flowrings.patch
-+++ /dev/null
-@@ -1,307 +0,0 @@
--From: Hante Meuleman <meuleman@broadcom.com>
--Date: Sun, 7 Feb 2016 18:08:24 +0100
--Subject: [PATCH] brcmfmac: Increase nr of supported flowrings.
--MIME-Version: 1.0
--Content-Type: text/plain; charset=UTF-8
--Content-Transfer-Encoding: 8bit
--
--New generation devices have firmware which has more than 256 flowrings.
--E.g. following debugging message comes from 14e4:4365 BCM4366:
--[  194.606245] brcmfmac: brcmf_pcie_init_ringbuffers Nr of flowrings is 264
--
--At various code places (related to flowrings) we were using u8 which
--could lead to storing wrong number or infinite loops when indexing with
--this type. This issue was quite easy to spot in brcmf_flowring_detach
--where it led to infinite loop e.g. on failed initialization.
--
--This patch switches code to proper types and increases the maximum
--number of supported flowrings to 512.
--
--Originally this change was sent in September 2015, but back it was
--causing a regression on BCM43602 resulting in:
--Unable to handle kernel NULL pointer dereference at virtual address ...
--
--The reason for this regression was missing update (s/u8/u16) of struct
--brcmf_flowring_ring. This problem was handled in 9f64df9 ("brcmfmac: Fix
--bug in flowring management."). Starting with that it's safe to apply
--this original patch as it doesn't cause a regression anymore.
--
--This patch fixes an infinite loop on BCM4366 which is supported since
--4.4 so it makes sense to apply it to stable 4.4+.
--
--Cc: <stable@vger.kernel.org> # 4.4+
--Reviewed-by: Arend Van Spriel <arend@broadcom.com>
--Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
--Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
--Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
--Signed-off-by: Arend van Spriel <arend@broadcom.com>
--Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
-----
--
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
--@@ -32,7 +32,7 @@
-- #define BRCMF_FLOWRING_LOW		(BRCMF_FLOWRING_HIGH - 256)
-- #define BRCMF_FLOWRING_INVALID_IFIDX	0xff
-- 
---#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16)
--+#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] * 2 + fifo + ifidx * 16)
-- #define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16)
-- 
-- static const u8 brcmf_flowring_prio2fifo[] = {
--@@ -68,7 +68,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f
-- 			  u8 prio, u8 ifidx)
-- {
-- 	struct brcmf_flowring_hash *hash;
---	u8 hash_idx;
--+	u16 hash_idx;
-- 	u32 i;
-- 	bool found;
-- 	bool sta;
--@@ -88,6 +88,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f
-- 	}
-- 	hash_idx =  sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
-- 			  BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
--+	hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
-- 	found = false;
-- 	hash = flow->hash;
-- 	for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
--@@ -98,6 +99,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f
-- 			break;
-- 		}
-- 		hash_idx++;
--+		hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
-- 	}
-- 	if (found)
-- 		return hash[hash_idx].flowid;
--@@ -111,7 +113,7 @@ u32 brcmf_flowring_create(struct brcmf_f
-- {
-- 	struct brcmf_flowring_ring *ring;
-- 	struct brcmf_flowring_hash *hash;
---	u8 hash_idx;
--+	u16 hash_idx;
-- 	u32 i;
-- 	bool found;
-- 	u8 fifo;
--@@ -131,6 +133,7 @@ u32 brcmf_flowring_create(struct brcmf_f
-- 	}
-- 	hash_idx =  sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
-- 			  BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
--+	hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
-- 	found = false;
-- 	hash = flow->hash;
-- 	for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
--@@ -140,6 +143,7 @@ u32 brcmf_flowring_create(struct brcmf_f
-- 			break;
-- 		}
-- 		hash_idx++;
--+		hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
-- 	}
-- 	if (found) {
-- 		for (i = 0; i < flow->nrofrings; i++) {
--@@ -169,7 +173,7 @@ u32 brcmf_flowring_create(struct brcmf_f
-- }
-- 
-- 
---u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid)
--+u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid)
-- {
-- 	struct brcmf_flowring_ring *ring;
-- 
--@@ -179,7 +183,7 @@ u8 brcmf_flowring_tid(struct brcmf_flowr
-- }
-- 
-- 
---static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid,
--+static void brcmf_flowring_block(struct brcmf_flowring *flow, u16 flowid,
-- 				 bool blocked)
-- {
-- 	struct brcmf_flowring_ring *ring;
--@@ -228,10 +232,10 @@ static void brcmf_flowring_block(struct
-- }
-- 
-- 
---void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid)
--+void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid)
-- {
-- 	struct brcmf_flowring_ring *ring;
---	u8 hash_idx;
--+	u16 hash_idx;
-- 	struct sk_buff *skb;
-- 
-- 	ring = flow->rings[flowid];
--@@ -253,7 +257,7 @@ void brcmf_flowring_delete(struct brcmf_
-- }
-- 
-- 
---u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid,
--+u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,
-- 			   struct sk_buff *skb)
-- {
-- 	struct brcmf_flowring_ring *ring;
--@@ -279,7 +283,7 @@ u32 brcmf_flowring_enqueue(struct brcmf_
-- }
-- 
-- 
---struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid)
--+struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid)
-- {
-- 	struct brcmf_flowring_ring *ring;
-- 	struct sk_buff *skb;
--@@ -300,7 +304,7 @@ struct sk_buff *brcmf_flowring_dequeue(s
-- }
-- 
-- 
---void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid,
--+void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,
-- 			     struct sk_buff *skb)
-- {
-- 	struct brcmf_flowring_ring *ring;
--@@ -311,7 +315,7 @@ void brcmf_flowring_reinsert(struct brcm
-- }
-- 
-- 
---u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid)
--+u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid)
-- {
-- 	struct brcmf_flowring_ring *ring;
-- 
--@@ -326,7 +330,7 @@ u32 brcmf_flowring_qlen(struct brcmf_flo
-- }
-- 
-- 
---void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid)
--+void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid)
-- {
-- 	struct brcmf_flowring_ring *ring;
-- 
--@@ -340,10 +344,10 @@ void brcmf_flowring_open(struct brcmf_fl
-- }
-- 
-- 
---u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid)
--+u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid)
-- {
-- 	struct brcmf_flowring_ring *ring;
---	u8 hash_idx;
--+	u16 hash_idx;
-- 
-- 	ring = flow->rings[flowid];
-- 	hash_idx = ring->hash_id;
--@@ -384,7 +388,7 @@ void brcmf_flowring_detach(struct brcmf_
-- 	struct brcmf_pub *drvr = bus_if->drvr;
-- 	struct brcmf_flowring_tdls_entry *search;
-- 	struct brcmf_flowring_tdls_entry *remove;
---	u8 flowid;
--+	u16 flowid;
-- 
-- 	for (flowid = 0; flowid < flow->nrofrings; flowid++) {
-- 		if (flow->rings[flowid])
--@@ -408,7 +412,7 @@ void brcmf_flowring_configure_addr_mode(
-- 	struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);
-- 	struct brcmf_pub *drvr = bus_if->drvr;
-- 	u32 i;
---	u8 flowid;
--+	u16 flowid;
-- 
-- 	if (flow->addr_mode[ifidx] != addr_mode) {
-- 		for (i = 0; i < ARRAY_SIZE(flow->hash); i++) {
--@@ -434,7 +438,7 @@ void brcmf_flowring_delete_peer(struct b
-- 	struct brcmf_flowring_tdls_entry *prev;
-- 	struct brcmf_flowring_tdls_entry *search;
-- 	u32 i;
---	u8 flowid;
--+	u16 flowid;
-- 	bool sta;
-- 
-- 	sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
--@@ -16,7 +16,7 @@
-- #define BRCMFMAC_FLOWRING_H
-- 
-- 
---#define BRCMF_FLOWRING_HASHSIZE		256
--+#define BRCMF_FLOWRING_HASHSIZE		512		/* has to be 2^x */
-- #define BRCMF_FLOWRING_INVALID_ID	0xFFFFFFFF
-- 
-- 
--@@ -24,7 +24,7 @@ struct brcmf_flowring_hash {
-- 	u8 mac[ETH_ALEN];
-- 	u8 fifo;
-- 	u8 ifidx;
---	u8 flowid;
--+	u16 flowid;
-- };
-- 
-- enum ring_status {
--@@ -61,16 +61,16 @@ u32 brcmf_flowring_lookup(struct brcmf_f
-- 			  u8 prio, u8 ifidx);
-- u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
-- 			  u8 prio, u8 ifidx);
---void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid);
---void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid);
---u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid);
---u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid,
--+void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid);
--+void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid);
--+u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid);
--+u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,
-- 			   struct sk_buff *skb);
---struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid);
---void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid,
--+struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid);
--+void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,
-- 			     struct sk_buff *skb);
---u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid);
---u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid);
--+u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid);
--+u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid);
-- struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings);
-- void brcmf_flowring_detach(struct brcmf_flowring *flow);
-- void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx,
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
--@@ -677,7 +677,7 @@ static u32 brcmf_msgbuf_flowring_create(
-- }
-- 
-- 
---static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid)
--+static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u16 flowid)
-- {
-- 	struct brcmf_flowring *flow = msgbuf->flow;
-- 	struct brcmf_commonring *commonring;
--@@ -1310,7 +1310,7 @@ int brcmf_proto_msgbuf_rx_trigger(struct
-- }
-- 
-- 
---void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid)
--+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid)
-- {
-- 	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
-- 	struct msgbuf_tx_flowring_delete_req *delete;
--@@ -1415,6 +1415,13 @@ int brcmf_proto_msgbuf_attach(struct brc
-- 	u32 count;
-- 
-- 	if_msgbuf = drvr->bus_if->msgbuf;
--+
--+	if (if_msgbuf->nrof_flowrings >= BRCMF_FLOWRING_HASHSIZE) {
--+		brcmf_err("driver not configured for this many flowrings %d\n",
--+			  if_msgbuf->nrof_flowrings);
--+		if_msgbuf->nrof_flowrings = BRCMF_FLOWRING_HASHSIZE - 1;
--+	}
--+
-- 	msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL);
-- 	if (!msgbuf)
-- 		goto fail;
----- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
--+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
--@@ -33,7 +33,7 @@
-- 
-- 
-- int brcmf_proto_msgbuf_rx_trigger(struct device *dev);
---void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid);
--+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid);
-- int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr);
-- void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr);
-- #else
-diff --git a/package/kernel/mac80211/patches/331-mac80211-End-the-MPSP-even-if-EOSP-frame-was-not-rec.patch b/package/kernel/mac80211/patches/331-mac80211-End-the-MPSP-even-if-EOSP-frame-was-not-rec.patch
-new file mode 100644
-index 0000000..5d8a8fb
---- /dev/null
-+++ b/package/kernel/mac80211/patches/331-mac80211-End-the-MPSP-even-if-EOSP-frame-was-not-rec.patch
-@@ -0,0 +1,42 @@
-+From: Masashi Honma <masashi.honma@gmail.com>
-+Date: Wed, 13 Jul 2016 16:04:35 +0900
-+Subject: [PATCH] mac80211: End the MPSP even if EOSP frame was not received
-+
-+The mesh STA sends QoS frame with EOSP (end of service period)
-+subfiled=1 to end the MPSP(mesh peer service period). Previously, if
-+the frame was not acked by peer, the mesh STA did not end the MPSP.
-+This patch ends the MPSP even if the QoS frame was no acked.
-+
-+Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
-+---
-+
-+--- a/net/mac80211/status.c
-++++ b/net/mac80211/status.c
-+@@ -784,6 +784,13 @@ void ieee80211_tx_status(struct ieee8021
-+ 			clear_sta_flag(sta, WLAN_STA_SP);
-+ 
-+ 		acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
-++
-++		/* mesh Peer Service Period support */
-++		if (ieee80211_vif_is_mesh(&sta->sdata->vif) &&
-++		    ieee80211_is_data_qos(fc))
-++			ieee80211_mpsp_trigger_process(
-++				ieee80211_get_qos_ctl(hdr), sta, true, acked);
-++
-+ 		if (!acked && test_sta_flag(sta, WLAN_STA_PS_STA)) {
-+ 			/*
-+ 			 * The STA is in power save mode, so assume
-+@@ -794,13 +801,6 @@ void ieee80211_tx_status(struct ieee8021
-+ 			return;
-+ 		}
-+ 
-+-		/* mesh Peer Service Period support */
-+-		if (ieee80211_vif_is_mesh(&sta->sdata->vif) &&
-+-		    ieee80211_is_data_qos(fc))
-+-			ieee80211_mpsp_trigger_process(
-+-					ieee80211_get_qos_ctl(hdr),
-+-					sta, true, acked);
-+-
-+ 		if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL) &&
-+ 		    (ieee80211_is_data(hdr->frame_control)) &&
-+ 		    (rates_idx != -1))
-diff --git a/package/kernel/mac80211/patches/332-ath10k-implement-NAPI-support.patch b/package/kernel/mac80211/patches/332-ath10k-implement-NAPI-support.patch
-new file mode 100644
-index 0000000..c6cc145
---- /dev/null
-+++ b/package/kernel/mac80211/patches/332-ath10k-implement-NAPI-support.patch
-@@ -0,0 +1,642 @@
-+From: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
-+Date: Thu, 21 Jul 2016 11:50:00 +0530
-+Subject: [PATCH] ath10k: implement NAPI support
-+
-+Add NAPI support for rx and tx completion. NAPI poll is scheduled
-+from interrupt handler. The design is as below
-+
-+ - on interrupt
-+     - schedule napi and mask interrupts
-+ - on poll
-+   - process all pipes (no actual Tx/Rx)
-+   - process Rx within budget
-+   - if quota exceeds budget reschedule napi poll by returning budget
-+   - process Tx completions and update budget if necessary
-+   - process Tx fetch indications (pull-push)
-+   - push any other pending Tx (if possible)
-+   - before resched or napi completion replenish htt rx ring buffer
-+   - if work done < budget, complete napi poll and unmask interrupts
-+
-+This change also get rid of two tasklets (intr_tq and txrx_compl_task).
-+
-+Measured peak throughput with NAPI on IPQ4019 platform in controlled
-+environment. No noticeable reduction in throughput is seen and also
-+observed improvements in CPU usage. Approx. 15% CPU usage got reduced
-+in UDP uplink case.
-+
-+DL: AP DUT Tx
-+UL: AP DUT Rx
-+
-+IPQ4019 (avg. cpu usage %)
-+========
-+                TOT              +NAPI
-+              ===========      =============
-+TCP DL       644 Mbps (42%)    645 Mbps (36%)
-+TCP UL       673 Mbps (30%)    675 Mbps (26%)
-+UDP DL       682 Mbps (49%)    680 Mbps (49%)
-+UDP UL       720 Mbps (28%)    717 Mbps (11%)
-+
-+Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath10k/ahb.c
-++++ b/drivers/net/wireless/ath/ath10k/ahb.c
-+@@ -462,13 +462,13 @@ static void ath10k_ahb_halt_chip(struct
-+ static irqreturn_t ath10k_ahb_interrupt_handler(int irq, void *arg)
-+ {
-+ 	struct ath10k *ar = arg;
-+-	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-+ 
-+ 	if (!ath10k_pci_irq_pending(ar))
-+ 		return IRQ_NONE;
-+ 
-+ 	ath10k_pci_disable_and_clear_legacy_irq(ar);
-+-	tasklet_schedule(&ar_pci->intr_tq);
-++	ath10k_pci_irq_msi_fw_mask(ar);
-++	napi_schedule(&ar->napi);
++@@ -171,14 +192,6 @@ static void ath_txq_skb_done(struct ath_
++ 	if (WARN_ON(--txq->pending_frames < 0))
++ 		txq->pending_frames = 0;
 + 
-+ 	return IRQ_HANDLED;
++-	if (txq->stopped &&
++-	    txq->pending_frames < sc->tx.txq_max_pending[q]) {
++-		if (ath9k_is_chanctx_enabled())
++-			ieee80211_wake_queue(sc->hw, info->hw_queue);
++-		else
++-			ieee80211_wake_queue(sc->hw, q);
++-		txq->stopped = false;
++-	}
 + }
-+@@ -831,7 +831,7 @@ static int ath10k_ahb_probe(struct platf
-+ 		goto err_resource_deinit;
-+ 	}
-+ 
-+-	ath10k_pci_init_irq_tasklets(ar);
-++	ath10k_pci_init_napi(ar);
 + 
-+ 	ret = ath10k_ahb_request_irq_legacy(ar);
-+ 	if (ret)
-+--- a/drivers/net/wireless/ath/ath10k/core.c
-++++ b/drivers/net/wireless/ath/ath10k/core.c
-+@@ -2226,6 +2226,8 @@ struct ath10k *ath10k_core_create(size_t
-+ 	INIT_WORK(&ar->register_work, ath10k_core_register_work);
-+ 	INIT_WORK(&ar->restart_work, ath10k_core_restart);
++ static struct ath_atx_tid *
++@@ -188,9 +201,47 @@ ath_get_skb_tid(struct ath_softc *sc, st
++ 	return ATH_AN_2_TID(an, tidno);
++ }
 + 
-++	init_dummy_netdev(&ar->napi_dev);
+++static struct sk_buff *
+++ath_tid_pull(struct ath_atx_tid *tid)
+++{
+++	struct ath_softc *sc = tid->an->sc;
+++	struct ieee80211_hw *hw = sc->hw;
+++	struct ath_tx_control txctl = {
+++		.txq = tid->txq,
+++		.sta = tid->an->sta,
+++	};
+++	struct sk_buff *skb;
+++	struct ath_frame_info *fi;
+++	int q;
 ++
-+ 	ret = ath10k_debug_create(ar);
-+ 	if (ret)
-+ 		goto err_free_aux_wq;
-+--- a/drivers/net/wireless/ath/ath10k/core.h
-++++ b/drivers/net/wireless/ath/ath10k/core.h
-+@@ -65,6 +65,10 @@
-+ #define ATH10K_KEEPALIVE_MAX_IDLE 3895
-+ #define ATH10K_KEEPALIVE_MAX_UNRESPONSIVE 3900
-+ 
-++/* NAPI poll budget */
-++#define ATH10K_NAPI_BUDGET      64
-++#define ATH10K_NAPI_QUOTA_LIMIT 60
+++	if (!tid->has_queued)
+++		return NULL;
 ++
-+ struct ath10k;
-+ 
-+ enum ath10k_bus {
-+@@ -933,6 +937,10 @@ struct ath10k {
-+ 	struct ath10k_thermal thermal;
-+ 	struct ath10k_wow wow;
-+ 
-++	/* NAPI */
-++	struct net_device napi_dev;
-++	struct napi_struct napi;
+++	skb = ieee80211_tx_dequeue(hw, container_of((void*)tid, struct ieee80211_txq, drv_priv));
+++	if (!skb) {
+++		tid->has_queued = false;
+++		return NULL;
+++	}
 ++
-+ 	/* must be last */
-+ 	u8 drv_priv[0] __aligned(sizeof(void *));
-+ };
-+--- a/drivers/net/wireless/ath/ath10k/htt.h
-++++ b/drivers/net/wireless/ath/ath10k/htt.h
-+@@ -1666,7 +1666,6 @@ struct ath10k_htt {
-+ 
-+ 	/* This is used to group tx/rx completions separately and process them
-+ 	 * in batches to reduce cache stalls */
-+-	struct tasklet_struct txrx_compl_task;
-+ 	struct sk_buff_head rx_compl_q;
-+ 	struct sk_buff_head rx_in_ord_compl_q;
-+ 	struct sk_buff_head tx_fetch_ind_q;
-+@@ -1799,5 +1798,6 @@ int ath10k_htt_tx(struct ath10k_htt *htt
-+ 		  struct sk_buff *msdu);
-+ void ath10k_htt_rx_pktlog_completion_handler(struct ath10k *ar,
-+ 					     struct sk_buff *skb);
-++int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget);
-+ 
-+ #endif
-+--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
-++++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
-+@@ -34,7 +34,6 @@
-+ #define HTT_RX_RING_REFILL_RESCHED_MS 5
-+ 
-+ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
-+-static void ath10k_htt_txrx_compl_task(unsigned long ptr);
-+ 
-+ static struct sk_buff *
-+ ath10k_htt_rx_find_skb_paddr(struct ath10k *ar, u32 paddr)
-+@@ -226,7 +225,6 @@ int ath10k_htt_rx_ring_refill(struct ath
-+ void ath10k_htt_rx_free(struct ath10k_htt *htt)
+++	if (ath_tx_prepare(hw, skb, &txctl)) {
+++		ieee80211_free_txskb(hw, skb);
+++		return NULL;
+++	}
+++
+++	q = skb_get_queue_mapping(skb);
+++	if (tid->txq == sc->tx.txq_map[q]) {
+++		fi = get_frame_info(skb);
+++		fi->txq = q;
+++		++tid->txq->pending_frames;
+++	}
+++
+++	return skb;
+++ }
+++
+++
++ static bool ath_tid_has_buffered(struct ath_atx_tid *tid)
 + {
-+ 	del_timer_sync(&htt->rx_ring.refill_retry_timer);
-+-	tasklet_kill(&htt->txrx_compl_task);
-+ 
-+ 	skb_queue_purge(&htt->rx_compl_q);
-+ 	skb_queue_purge(&htt->rx_in_ord_compl_q);
-+@@ -520,9 +518,6 @@ int ath10k_htt_rx_alloc(struct ath10k_ht
-+ 	skb_queue_head_init(&htt->tx_fetch_ind_q);
-+ 	atomic_set(&htt->num_mpdus_ready, 0);
-+ 
-+-	tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
-+-		     (unsigned long)htt);
-+-
-+ 	ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n",
-+ 		   htt->rx_ring.size, htt->rx_ring.fill_level);
-+ 	return 0;
-+@@ -958,7 +953,7 @@ static void ath10k_process_rx(struct ath
-+ 	trace_ath10k_rx_hdr(ar, skb->data, skb->len);
-+ 	trace_ath10k_rx_payload(ar, skb->data, skb->len);
-+ 
-+-	ieee80211_rx(ar->hw, skb);
-++	ieee80211_rx_napi(ar->hw, NULL, skb, &ar->napi);
++-	return !skb_queue_empty(&tid->buf_q) || !skb_queue_empty(&tid->retry_q);
+++	return !skb_queue_empty(&tid->retry_q) || tid->has_queued;
 + }
 + 
-+ static int ath10k_htt_rx_nwifi_hdrlen(struct ath10k *ar,
-+@@ -1527,7 +1522,7 @@ static int ath10k_htt_rx_handle_amsdu(st
-+ 	struct ath10k *ar = htt->ar;
-+ 	struct ieee80211_rx_status *rx_status = &htt->rx_status;
-+ 	struct sk_buff_head amsdu;
-+-	int ret;
-++	int ret, num_msdus;
-+ 
-+ 	__skb_queue_head_init(&amsdu);
-+ 
-+@@ -1549,13 +1544,14 @@ static int ath10k_htt_rx_handle_amsdu(st
-+ 		return ret;
-+ 	}
++ static struct sk_buff *ath_tid_dequeue(struct ath_atx_tid *tid)
++@@ -199,46 +250,11 @@ static struct sk_buff *ath_tid_dequeue(s
 + 
-++	num_msdus = skb_queue_len(&amsdu);
-+ 	ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
-+ 	ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
-+ 	ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
-+ 	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
-+ 	ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
++ 	skb = __skb_dequeue(&tid->retry_q);
++ 	if (!skb)
++-		skb = __skb_dequeue(&tid->buf_q);
+++		skb = ath_tid_pull(tid);
 + 
-+-	return 0;
-++	return num_msdus;
++ 	return skb;
 + }
 + 
-+ static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt,
-+@@ -1579,15 +1575,6 @@ static void ath10k_htt_rx_proc_rx_ind(st
-+ 		mpdu_count += mpdu_ranges[i].mpdu_count;
-+ 
-+ 	atomic_add(mpdu_count, &htt->num_mpdus_ready);
-+-
-+-	tasklet_schedule(&htt->txrx_compl_task);
-+-}
-+-
-+-static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt)
++-/*
++- * ath_tx_tid_change_state:
++- * - clears a-mpdu flag of previous session
++- * - force sequence number allocation to fix next BlockAck Window
++- */
++-static void
++-ath_tx_tid_change_state(struct ath_softc *sc, struct ath_atx_tid *tid)
 +-{
-+-	atomic_inc(&htt->num_mpdus_ready);
++-	struct ath_txq *txq = tid->txq;
++-	struct ieee80211_tx_info *tx_info;
++-	struct sk_buff *skb, *tskb;
++-	struct ath_buf *bf;
++-	struct ath_frame_info *fi;
 +-
-+-	tasklet_schedule(&htt->txrx_compl_task);
-+ }
-+ 
-+ static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar,
-+@@ -1772,14 +1759,15 @@ static void ath10k_htt_rx_h_rx_offload_p
-+ 			RX_FLAG_MMIC_STRIPPED;
-+ }
-+ 
-+-static void ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
-+-				       struct sk_buff_head *list)
-++static int ath10k_htt_rx_h_rx_offload(struct ath10k *ar,
-++				      struct sk_buff_head *list)
-+ {
-+ 	struct ath10k_htt *htt = &ar->htt;
-+ 	struct ieee80211_rx_status *status = &htt->rx_status;
-+ 	struct htt_rx_offload_msdu *rx;
-+ 	struct sk_buff *msdu;
-+ 	size_t offset;
-++	int num_msdu = 0;
-+ 
-+ 	while ((msdu = __skb_dequeue(list))) {
-+ 		/* Offloaded frames don't have Rx descriptor. Instead they have
-+@@ -1819,10 +1807,12 @@ static void ath10k_htt_rx_h_rx_offload(s
-+ 		ath10k_htt_rx_h_rx_offload_prot(status, msdu);
-+ 		ath10k_htt_rx_h_channel(ar, status, NULL, rx->vdev_id);
-+ 		ath10k_process_rx(ar, status, msdu);
-++		num_msdu++;
-+ 	}
-++	return num_msdu;
-+ }
-+ 
-+-static void ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
-++static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
-+ {
-+ 	struct ath10k_htt *htt = &ar->htt;
-+ 	struct htt_resp *resp = (void *)skb->data;
-+@@ -1835,12 +1825,12 @@ static void ath10k_htt_rx_in_ord_ind(str
-+ 	u8 tid;
-+ 	bool offload;
-+ 	bool frag;
-+-	int ret;
-++	int ret, num_msdus = 0;
-+ 
-+ 	lockdep_assert_held(&htt->rx_ring.lock);
-+ 
-+ 	if (htt->rx_confused)
-+-		return;
-++		return -EIO;
-+ 
-+ 	skb_pull(skb, sizeof(resp->hdr));
-+ 	skb_pull(skb, sizeof(resp->rx_in_ord_ind));
-+@@ -1859,7 +1849,7 @@ static void ath10k_htt_rx_in_ord_ind(str
-+ 
-+ 	if (skb->len < msdu_count * sizeof(*resp->rx_in_ord_ind.msdu_descs)) {
-+ 		ath10k_warn(ar, "dropping invalid in order rx indication\n");
-+-		return;
-++		return -EINVAL;
-+ 	}
-+ 
-+ 	/* The event can deliver more than 1 A-MSDU. Each A-MSDU is later
-+@@ -1870,14 +1860,14 @@ static void ath10k_htt_rx_in_ord_ind(str
-+ 	if (ret < 0) {
-+ 		ath10k_warn(ar, "failed to pop paddr list: %d\n", ret);
-+ 		htt->rx_confused = true;
-+-		return;
-++		return -EIO;
-+ 	}
-+ 
-+ 	/* Offloaded frames are very different and need to be handled
-+ 	 * separately.
-+ 	 */
-+ 	if (offload)
-+-		ath10k_htt_rx_h_rx_offload(ar, &list);
-++		num_msdus = ath10k_htt_rx_h_rx_offload(ar, &list);
-+ 
-+ 	while (!skb_queue_empty(&list)) {
-+ 		__skb_queue_head_init(&amsdu);
-+@@ -1890,6 +1880,7 @@ static void ath10k_htt_rx_in_ord_ind(str
-+ 			 * better to report something than nothing though. This
-+ 			 * should still give an idea about rx rate to the user.
-+ 			 */
-++			num_msdus += skb_queue_len(&amsdu);
-+ 			ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
-+ 			ath10k_htt_rx_h_filter(ar, &amsdu, status);
-+ 			ath10k_htt_rx_h_mpdu(ar, &amsdu, status);
-+@@ -1902,9 +1893,10 @@ static void ath10k_htt_rx_in_ord_ind(str
-+ 			ath10k_warn(ar, "failed to extract amsdu: %d\n", ret);
-+ 			htt->rx_confused = true;
-+ 			__skb_queue_purge(&list);
-+-			return;
-++			return -EIO;
-+ 		}
-+ 	}
-++	return num_msdus;
-+ }
-+ 
-+ static void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
-+@@ -2267,7 +2259,6 @@ bool ath10k_htt_t2h_msg_handler(struct a
-+ 	}
-+ 	case HTT_T2H_MSG_TYPE_TX_COMPL_IND:
-+ 		ath10k_htt_rx_tx_compl_ind(htt->ar, skb);
-+-		tasklet_schedule(&htt->txrx_compl_task);
-+ 		break;
-+ 	case HTT_T2H_MSG_TYPE_SEC_IND: {
-+ 		struct ath10k *ar = htt->ar;
-+@@ -2284,7 +2275,7 @@ bool ath10k_htt_t2h_msg_handler(struct a
-+ 	case HTT_T2H_MSG_TYPE_RX_FRAG_IND: {
-+ 		ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
-+ 				skb->data, skb->len);
-+-		ath10k_htt_rx_frag_handler(htt);
-++		atomic_inc(&htt->num_mpdus_ready);
-+ 		break;
-+ 	}
-+ 	case HTT_T2H_MSG_TYPE_TEST:
-+@@ -2322,8 +2313,7 @@ bool ath10k_htt_t2h_msg_handler(struct a
-+ 		break;
-+ 	}
-+ 	case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: {
-+-		skb_queue_tail(&htt->rx_in_ord_compl_q, skb);
-+-		tasklet_schedule(&htt->txrx_compl_task);
-++		__skb_queue_tail(&htt->rx_in_ord_compl_q, skb);
-+ 		return false;
-+ 	}
-+ 	case HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND:
-+@@ -2349,7 +2339,6 @@ bool ath10k_htt_t2h_msg_handler(struct a
-+ 			break;
-+ 		}
-+ 		skb_queue_tail(&htt->tx_fetch_ind_q, tx_fetch_ind);
-+-		tasklet_schedule(&htt->txrx_compl_task);
-+ 		break;
-+ 	}
-+ 	case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM:
-+@@ -2378,27 +2367,77 @@ void ath10k_htt_rx_pktlog_completion_han
-+ }
-+ EXPORT_SYMBOL(ath10k_htt_rx_pktlog_completion_handler);
-+ 
-+-static void ath10k_htt_txrx_compl_task(unsigned long ptr)
-++int ath10k_htt_txrx_compl_task(struct ath10k *ar, int budget)
-+ {
-+-	struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
-+-	struct ath10k *ar = htt->ar;
-++	struct ath10k_htt *htt = &ar->htt;
-+ 	struct htt_tx_done tx_done = {};
-+-	struct sk_buff_head rx_ind_q;
-+ 	struct sk_buff_head tx_ind_q;
-+ 	struct sk_buff *skb;
-+ 	unsigned long flags;
-+-	int num_mpdus;
-++	int quota = 0, done, num_rx_msdus;
-++	bool resched_napi = false;
-+ 
-+-	__skb_queue_head_init(&rx_ind_q);
-+ 	__skb_queue_head_init(&tx_ind_q);
-+ 
-+-	spin_lock_irqsave(&htt->rx_in_ord_compl_q.lock, flags);
-+-	skb_queue_splice_init(&htt->rx_in_ord_compl_q, &rx_ind_q);
-+-	spin_unlock_irqrestore(&htt->rx_in_ord_compl_q.lock, flags);
-++	/* Since in-ord-ind can deliver more than 1 A-MSDU in single event,
-++	 * process it first to utilize full available quota.
-++	 */
-++	while (quota < budget) {
-++		if (skb_queue_empty(&htt->rx_in_ord_compl_q))
-++			break;
-+ 
-+-	spin_lock_irqsave(&htt->tx_fetch_ind_q.lock, flags);
-+-	skb_queue_splice_init(&htt->tx_fetch_ind_q, &tx_ind_q);
-+-	spin_unlock_irqrestore(&htt->tx_fetch_ind_q.lock, flags);
-++		skb = __skb_dequeue(&htt->rx_in_ord_compl_q);
-++		if (!skb) {
-++			resched_napi = true;
-++			goto exit;
-++		}
-++
-++		spin_lock_bh(&htt->rx_ring.lock);
-++		num_rx_msdus = ath10k_htt_rx_in_ord_ind(ar, skb);
-++		spin_unlock_bh(&htt->rx_ring.lock);
-++		if (num_rx_msdus < 0) {
-++			resched_napi = true;
-++			goto exit;
-++		}
-++
-++		dev_kfree_skb_any(skb);
-++		if (num_rx_msdus > 0)
-++			quota += num_rx_msdus;
-++
-++		if ((quota > ATH10K_NAPI_QUOTA_LIMIT) &&
-++		    !skb_queue_empty(&htt->rx_in_ord_compl_q)) {
-++			resched_napi = true;
-++			goto exit;
-++		}
-++	}
-++
-++	while (quota < budget) {
-++		/* no more data to receive */
-++		if (!atomic_read(&htt->num_mpdus_ready))
-++			break;
-++
-++		num_rx_msdus = ath10k_htt_rx_handle_amsdu(htt);
-++		if (num_rx_msdus < 0) {
-++			resched_napi = true;
-++			goto exit;
-++		}
-++
-++		quota += num_rx_msdus;
-++		atomic_dec(&htt->num_mpdus_ready);
-++		if ((quota > ATH10K_NAPI_QUOTA_LIMIT) &&
-++		    atomic_read(&htt->num_mpdus_ready)) {
-++			resched_napi = true;
-++			goto exit;
-++		}
-++	}
-++
-++	/* From NAPI documentation:
-++	 *  The napi poll() function may also process TX completions, in which
-++	 *  case if it processes the entire TX ring then it should count that
-++	 *  work as the rest of the budget.
-++	 */
-++	if ((quota < budget) && !kfifo_is_empty(&htt->txdone_fifo))
-++		quota = budget;
-+ 
-+ 	/* kfifo_get: called only within txrx_tasklet so it's neatly serialized.
-+ 	 * From kfifo_get() documentation:
-+@@ -2408,27 +2447,22 @@ static void ath10k_htt_txrx_compl_task(u
-+ 	while (kfifo_get(&htt->txdone_fifo, &tx_done))
-+ 		ath10k_txrx_tx_unref(htt, &tx_done);
-+ 
-++	spin_lock_irqsave(&htt->tx_fetch_ind_q.lock, flags);
-++	skb_queue_splice_init(&htt->tx_fetch_ind_q, &tx_ind_q);
-++	spin_unlock_irqrestore(&htt->tx_fetch_ind_q.lock, flags);
-++
-+ 	while ((skb = __skb_dequeue(&tx_ind_q))) {
-+ 		ath10k_htt_rx_tx_fetch_ind(ar, skb);
-+ 		dev_kfree_skb_any(skb);
-+ 	}
-+ 
-+-	num_mpdus = atomic_read(&htt->num_mpdus_ready);
++-	skb_queue_walk_safe(&tid->buf_q, skb, tskb) {
++-		fi = get_frame_info(skb);
++-		bf = fi->bf;
 +-
-+-	while (num_mpdus) {
-+-		if (ath10k_htt_rx_handle_amsdu(htt))
-+-			break;
++-		tx_info = IEEE80211_SKB_CB(skb);
++-		tx_info->flags &= ~IEEE80211_TX_CTL_AMPDU;
 +-
-+-		num_mpdus--;
-+-		atomic_dec(&htt->num_mpdus_ready);
-+-	}
++-		if (bf)
++-			continue;
 +-
-+-	while ((skb = __skb_dequeue(&rx_ind_q))) {
-+-		spin_lock_bh(&htt->rx_ring.lock);
-+-		ath10k_htt_rx_in_ord_ind(ar, skb);
-+-		spin_unlock_bh(&htt->rx_ring.lock);
-+-		dev_kfree_skb_any(skb);
++-		bf = ath_tx_setup_buffer(sc, txq, tid, skb);
++-		if (!bf) {
++-			__skb_unlink(skb, &tid->buf_q);
++-			ath_txq_skb_done(sc, txq, skb);
++-			ieee80211_free_txskb(sc->hw, skb);
++-			continue;
++-		}
 +-	}
 +-
-++exit:
-+ 	ath10k_htt_rx_msdu_buff_replenish(htt);
-++	/* In case of rx failure or more data to read, report budget
-++	 * to reschedule NAPI poll
-++	 */
-++	done = resched_napi ? budget : quota;
-++
-++	return done;
-+ }
-++EXPORT_SYMBOL(ath10k_htt_txrx_compl_task);
-+--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
-++++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
-+@@ -388,8 +388,6 @@ void ath10k_htt_tx_free(struct ath10k_ht
-+ {
-+ 	int size;
-+ 
-+-	tasklet_kill(&htt->txrx_compl_task);
-+-
-+ 	idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar);
-+ 	idr_destroy(&htt->pending_tx);
-+ 
-+--- a/drivers/net/wireless/ath/ath10k/pci.c
-++++ b/drivers/net/wireless/ath/ath10k/pci.c
-+@@ -1502,12 +1502,10 @@ void ath10k_pci_hif_send_complete_check(
-+ 	ath10k_ce_per_engine_service(ar, pipe);
-+ }
-+ 
-+-void ath10k_pci_kill_tasklet(struct ath10k *ar)
-++static void ath10k_pci_rx_retry_sync(struct ath10k *ar)
-+ {
-+ 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-+ 
-+-	tasklet_kill(&ar_pci->intr_tq);
++-}
 +-
-+ 	del_timer_sync(&ar_pci->rx_post_retry);
-+ }
-+ 
-+@@ -1566,7 +1564,7 @@ void ath10k_pci_hif_get_default_pipe(str
-+ 						 ul_pipe, dl_pipe);
-+ }
-+ 
-+-static void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar)
-++void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar)
++ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
 + {
-+ 	u32 val;
-+ 
-+@@ -1747,7 +1745,7 @@ void ath10k_pci_ce_deinit(struct ath10k
++ 	struct ath_txq *txq = tid->txq;
++@@ -873,20 +889,16 @@ static int ath_compute_num_delims(struct
 + 
-+ void ath10k_pci_flush(struct ath10k *ar)
++ static struct ath_buf *
++ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
++-			struct ath_atx_tid *tid, struct sk_buff_head **q)
+++			struct ath_atx_tid *tid)
 + {
-+-	ath10k_pci_kill_tasklet(ar);
-++	ath10k_pci_rx_retry_sync(ar);
-+ 	ath10k_pci_buffer_cleanup(ar);
-+ }
-+ 
-+@@ -2754,35 +2752,53 @@ static irqreturn_t ath10k_pci_interrupt_
-+ 		return IRQ_NONE;
-+ 	}
++ 	struct ieee80211_tx_info *tx_info;
++ 	struct ath_frame_info *fi;
++-	struct sk_buff *skb;
+++	struct sk_buff *skb, *first_skb = NULL;
++ 	struct ath_buf *bf;
++ 	u16 seqno;
 + 
-+-	if (ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_LEGACY) {
-+-		if (!ath10k_pci_irq_pending(ar))
-+-			return IRQ_NONE;
++ 	while (1) {
++-		*q = &tid->retry_q;
++-		if (skb_queue_empty(*q))
++-			*q = &tid->buf_q;
 +-
-+-		ath10k_pci_disable_and_clear_legacy_irq(ar);
-+-	}
-++	if ((ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_LEGACY) &&
-++	    !ath10k_pci_irq_pending(ar))
-++		return IRQ_NONE;
-+ 
-+-	tasklet_schedule(&ar_pci->intr_tq);
-++	ath10k_pci_disable_and_clear_legacy_irq(ar);
-++	ath10k_pci_irq_msi_fw_mask(ar);
-++	napi_schedule(&ar->napi);
-+ 
-+ 	return IRQ_HANDLED;
-+ }
++-		skb = skb_peek(*q);
+++		skb = ath_tid_dequeue(tid);
++ 		if (!skb)
++ 			break;
 + 
-+-static void ath10k_pci_tasklet(unsigned long data)
-++static int ath10k_pci_napi_poll(struct napi_struct *ctx, int budget)
-+ {
-+-	struct ath10k *ar = (struct ath10k *)data;
-+-	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-++	struct ath10k *ar = container_of(ctx, struct ath10k, napi);
-++	int done = 0;
-+ 
-+ 	if (ath10k_pci_has_fw_crashed(ar)) {
-+-		ath10k_pci_irq_disable(ar);
-+ 		ath10k_pci_fw_crashed_clear(ar);
-+ 		ath10k_pci_fw_crashed_dump(ar);
-+-		return;
-++		napi_complete(ctx);
-++		return done;
-+ 	}
++@@ -898,7 +910,6 @@ ath_tx_get_tid_subframe(struct ath_softc
++ 			bf->bf_state.stale = false;
 + 
-+ 	ath10k_ce_per_engine_service_any(ar);
++ 		if (!bf) {
++-			__skb_unlink(skb, *q);
++ 			ath_txq_skb_done(sc, txq, skb);
++ 			ieee80211_free_txskb(sc->hw, skb);
++ 			continue;
++@@ -927,8 +938,19 @@ ath_tx_get_tid_subframe(struct ath_softc
++ 		seqno = bf->bf_state.seqno;
 + 
-+-	/* Re-enable legacy irq that was disabled in the irq handler */
-+-	if (ar_pci->oper_irq_mode == ATH10K_PCI_IRQ_LEGACY)
-++	done = ath10k_htt_txrx_compl_task(ar, budget);
++ 		/* do not step over block-ack window */
++-		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno))
+++		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
+++			__skb_queue_tail(&tid->retry_q, skb);
 ++
-++	if (done < budget) {
-++		napi_complete(ctx);
-++		/* In case of MSI, it is possible that interrupts are received
-++		 * while NAPI poll is inprogress. So pending interrupts that are
-++		 * received after processing all copy engine pipes by NAPI poll
-++		 * will not be handled again. This is causing failure to
-++		 * complete boot sequence in x86 platform. So before enabling
-++		 * interrupts safer to check for pending interrupts for
-++		 * immediate servicing.
-++		 */
-++		if (CE_INTERRUPT_SUMMARY(ar)) {
-++			napi_reschedule(&ar->napi);
-++			goto out;
+++			/* If there are other skbs in the retry q, they are
+++			 * probably within the BAW, so loop immediately to get
+++			 * one of them. Otherwise the queue can get stuck. */
+++			if (!skb_queue_is_first(&tid->retry_q, skb) && skb != first_skb) {
+++				if(!first_skb) /* infinite loop prevention */
+++					first_skb = skb;
+++				continue;
+++			}
++ 			break;
 ++		}
-+ 		ath10k_pci_enable_legacy_irq(ar);
-++		ath10k_pci_irq_msi_fw_unmask(ar);
-++	}
-++
-++out:
-++	return done;
-+ }
 + 
-+ static int ath10k_pci_request_irq_msi(struct ath10k *ar)
-+@@ -2840,11 +2856,11 @@ static void ath10k_pci_free_irq(struct a
-+ 	free_irq(ar_pci->pdev->irq, ar);
-+ }
++ 		if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) {
++ 			struct ath_tx_status ts = {};
++@@ -936,7 +958,6 @@ ath_tx_get_tid_subframe(struct ath_softc
 + 
-+-void ath10k_pci_init_irq_tasklets(struct ath10k *ar)
-++void ath10k_pci_init_napi(struct ath10k *ar)
-+ {
-+-	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-+-
-+-	tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long)ar);
-++	netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_pci_napi_poll,
-++		       ATH10K_NAPI_BUDGET);
-++	napi_enable(&ar->napi);
++ 			INIT_LIST_HEAD(&bf_head);
++ 			list_add(&bf->list, &bf_head);
++-			__skb_unlink(skb, *q);
++ 			ath_tx_update_baw(sc, tid, seqno);
++ 			ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
++ 			continue;
++@@ -948,11 +969,10 @@ ath_tx_get_tid_subframe(struct ath_softc
++ 	return NULL;
 + }
 + 
-+ static int ath10k_pci_init_irq(struct ath10k *ar)
-+@@ -2852,7 +2868,7 @@ static int ath10k_pci_init_irq(struct at
-+ 	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
-+ 	int ret;
-+ 
-+-	ath10k_pci_init_irq_tasklets(ar);
-++	ath10k_pci_init_napi(ar);
-+ 
-+ 	if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_AUTO)
-+ 		ath10k_info(ar, "limiting irq mode to: %d\n",
-+@@ -3113,7 +3129,8 @@ int ath10k_pci_setup_resource(struct ath
-+ 
-+ void ath10k_pci_release_resource(struct ath10k *ar)
++-static bool
+++static int
++ ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq,
++ 		 struct ath_atx_tid *tid, struct list_head *bf_q,
++-		 struct ath_buf *bf_first, struct sk_buff_head *tid_q,
++-		 int *aggr_len)
+++		 struct ath_buf *bf_first)
 + {
-+-	ath10k_pci_kill_tasklet(ar);
-++	ath10k_pci_rx_retry_sync(ar);
-++	netif_napi_del(&ar->napi);
-+ 	ath10k_pci_ce_deinit(ar);
-+ 	ath10k_pci_free_pipes(ar);
-+ }
-+@@ -3274,7 +3291,7 @@ static int ath10k_pci_probe(struct pci_d
-+ 
-+ err_free_irq:
-+ 	ath10k_pci_free_irq(ar);
-+-	ath10k_pci_kill_tasklet(ar);
-++	ath10k_pci_rx_retry_sync(ar);
-+ 
-+ err_deinit_irq:
-+ 	ath10k_pci_deinit_irq(ar);
-+--- a/drivers/net/wireless/ath/ath10k/pci.h
-++++ b/drivers/net/wireless/ath/ath10k/pci.h
-+@@ -177,8 +177,6 @@ struct ath10k_pci {
-+ 	/* Operating interrupt mode */
-+ 	enum ath10k_pci_irq_mode oper_irq_mode;
-+ 
-+-	struct tasklet_struct intr_tq;
-+-
-+ 	struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX];
-+ 
-+ 	/* Copy Engine used for Diagnostic Accesses */
-+@@ -294,8 +292,7 @@ void ath10k_pci_free_pipes(struct ath10k
-+ void ath10k_pci_free_pipes(struct ath10k *ar);
-+ void ath10k_pci_rx_replenish_retry(unsigned long ptr);
-+ void ath10k_pci_ce_deinit(struct ath10k *ar);
-+-void ath10k_pci_init_irq_tasklets(struct ath10k *ar);
-+-void ath10k_pci_kill_tasklet(struct ath10k *ar);
-++void ath10k_pci_init_napi(struct ath10k *ar);
-+ int ath10k_pci_init_pipes(struct ath10k *ar);
-+ int ath10k_pci_init_config(struct ath10k *ar);
-+ void ath10k_pci_rx_post(struct ath10k *ar);
-+@@ -303,6 +300,7 @@ void ath10k_pci_flush(struct ath10k *ar)
-+ void ath10k_pci_enable_legacy_irq(struct ath10k *ar);
-+ bool ath10k_pci_irq_pending(struct ath10k *ar);
-+ void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar);
-++void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar);
-+ int ath10k_pci_wait_for_target_init(struct ath10k *ar);
-+ int ath10k_pci_setup_resource(struct ath10k *ar);
-+ void ath10k_pci_release_resource(struct ath10k *ar);
-diff --git a/package/kernel/mac80211/patches/332-cfg80211-fix-faulty-variable-initialization-in-ieee8.patch b/package/kernel/mac80211/patches/332-cfg80211-fix-faulty-variable-initialization-in-ieee8.patch
-deleted file mode 100644
-index e414f23..0000000
---- a/package/kernel/mac80211/patches/332-cfg80211-fix-faulty-variable-initialization-in-ieee8.patch
-+++ /dev/null
-@@ -1,22 +0,0 @@
--From: Felix Fietkau <nbd@openwrt.org>
--Date: Mon, 8 Feb 2016 14:24:36 +0100
--Subject: [PATCH] cfg80211: fix faulty variable initialization in
-- ieee80211_amsdu_to_8023s
--
--reuse_skb is set to true if the code decides to use the last segment.
--Fixes a memory leak
--
--Signed-off-by: Felix Fietkau <nbd@openwrt.org>
-----
--
----- a/net/wireless/util.c
--+++ b/net/wireless/util.c
--@@ -676,7 +676,7 @@ void ieee80211_amsdu_to_8023s(struct sk_
-- 	u8 *payload;
-- 	int offset = 0, remaining, err;
-- 	struct ethhdr eth;
---	bool reuse_skb = true;
--+	bool reuse_skb = false;
-- 	bool last = false;
-- 
-- 	if (has_80211_header) {
-diff --git a/package/kernel/mac80211/patches/333-ath9k-fix-client-mode-beacon-configuration.patch b/package/kernel/mac80211/patches/333-ath9k-fix-client-mode-beacon-configuration.patch
-new file mode 100644
-index 0000000..d008ceb
---- /dev/null
-+++ b/package/kernel/mac80211/patches/333-ath9k-fix-client-mode-beacon-configuration.patch
-@@ -0,0 +1,69 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Tue, 26 Jul 2016 08:05:10 +0200
-+Subject: [PATCH] ath9k: fix client mode beacon configuration
-+
-+For pure station mode, iter_data.primary_beacon_vif was used and passed
-+to ath_beacon_config, but not set to the station vif.
-+This was causing the following warning:
-+
-+[  100.310919] ------------[ cut here ]------------
-+[  100.315683] WARNING: CPU: 0 PID: 7 at compat-wireless-2016-06-20/drivers/net/wireless/ath/ath9k/beacon.c:642 ath9k_calculate_summary_state+0x250/0x60c [ath9k]()
-+[  100.402028] CPU: 0 PID: 7 Comm: kworker/u2:1 Tainted: G        W       4.4.15 #5
-+[  100.409676] Workqueue: phy0 ieee80211_ibss_leave [mac80211]
-+[  100.415351] Stack : 8736e98c 870b4b20 87a25b54 800a6800 8782a080 80400d63 8039b96c 00000007
-+[  100.415351]    803c5edc 87875914 80400000 800a47cc 87a25b54 800a6800 803a0fd8 80400000
-+[  100.415351]    00000003 87875914 80400000 80094ae0 87a25b54 8787594c 00000000 801ef308
-+[  100.415351]    803ffe70 801ef300 87193d58 87b3a400 87b3ad00 70687930 00000000 00000000
-+[  100.415351]    00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
-+[  100.415351]    ...
-+[  100.451703] Call Trace:
-+[  100.454235] [<800a6800>] vprintk_default+0x24/0x30
-+[  100.459110] [<800a47cc>] printk+0x2c/0x38
-+[  100.463190] [<800a6800>] vprintk_default+0x24/0x30
-+[  100.468072] [<80094ae0>] print_worker_info+0x148/0x174
-+[  100.473378] [<801ef308>] serial8250_console_putchar+0x0/0x44
-+[  100.479122] [<801ef300>] wait_for_xmitr+0xc4/0xcc
-+[  100.484014] [<87193d58>] ieee80211_ibss_leave+0xb90/0x1900 [mac80211]
-+[  100.490590] [<80081604>] warn_slowpath_common+0xa0/0xd0
-+[  100.495922] [<801a359c>] dump_stack+0x14/0x28
-+[  100.500350] [<80071a00>] show_stack+0x50/0x84
-+[  100.504784] [<80081604>] warn_slowpath_common+0xa0/0xd0
-+[  100.510106] [<87024c60>] ath9k_calculate_summary_state+0x250/0x60c [ath9k]
-+[  100.517105] [<800816b8>] warn_slowpath_null+0x18/0x24
-+[  100.522256] [<87024c60>] ath9k_calculate_summary_state+0x250/0x60c [ath9k]
-+[  100.529273] [<87025418>] ath9k_set_txpower+0x148/0x498 [ath9k]
-+[  100.535302] [<871d2c64>] cleanup_module+0xa74/0xd4c [mac80211]
-+[  100.541237] [<801ef308>] serial8250_console_putchar+0x0/0x44
-+[  100.547042] [<800a5d18>] wake_up_klogd+0x54/0x68
-+[  100.551730] [<800a6650>] vprintk_emit+0x404/0x43c
-+[  100.556623] [<871b9db8>] ieee80211_sta_rx_notify+0x258/0x32c [mac80211]
-+[  100.563475] [<871ba6a4>] ieee80211_sta_rx_queued_mgmt+0x63c/0x734 [mac80211]
-+[  100.570693] [<871aa49c>] ieee80211_tx_prepare_skb+0x210/0x230 [mac80211]
-+[  100.577609] [<800af5d4>] mod_timer+0x15c/0x190
-+[  100.582220] [<871ba8b8>] ieee80211_sta_work+0xfc/0xe1c [mac80211]
-+[  100.588539] [<871940b4>] ieee80211_ibss_leave+0xeec/0x1900 [mac80211]
-+[  100.595122] [<8009ec84>] dequeue_task_fair+0x44/0x130
-+[  100.600281] [<80092a34>] process_one_work+0x1f8/0x334
-+[  100.605454] [<80093830>] worker_thread+0x2b4/0x408
-+[  100.610317] [<8009357c>] worker_thread+0x0/0x408
-+[  100.615019] [<8009357c>] worker_thread+0x0/0x408
-+[  100.619705] [<80097b68>] kthread+0xdc/0xe8
-+[  100.623886] [<80097a8c>] kthread+0x0/0xe8
-+[  100.627961] [<80060878>] ret_from_kernel_thread+0x14/0x1c
-+[  100.633448]
-+[  100.634956] ---[ end trace aafbe57e9ae6862f ]---
-+
-+Fixes: cfda2d8e2314 ("ath9k: Fix beacon configuration for addition/removal of interfaces")
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/main.c
-++++ b/drivers/net/wireless/ath/ath9k/main.c
-+@@ -1154,6 +1154,7 @@ void ath9k_calculate_summary_state(struc
-+ 		bool changed = (iter_data.primary_sta != ctx->primary_sta);
-+ 
-+ 		if (iter_data.primary_sta) {
-++			iter_data.primary_beacon_vif = iter_data.primary_sta;
-+ 			iter_data.beacons = true;
-+ 			ath9k_set_assoc_state(sc, iter_data.primary_sta,
-+ 					      changed);
-diff --git a/package/kernel/mac80211/patches/333-cfg80211-reuse-existing-page-fragments-in-A-MSDU-rx.patch b/package/kernel/mac80211/patches/333-cfg80211-reuse-existing-page-fragments-in-A-MSDU-rx.patch
-deleted file mode 100644
-index 6e2d0cf..0000000
---- a/package/kernel/mac80211/patches/333-cfg80211-reuse-existing-page-fragments-in-A-MSDU-rx.patch
-+++ /dev/null
-@@ -1,132 +0,0 @@
--From: Felix Fietkau <nbd@openwrt.org>
--Date: Mon, 8 Feb 2016 14:33:19 +0100
--Subject: [PATCH] cfg80211: reuse existing page fragments in A-MSDU rx
--
--This massively reduces data copying and thus improves rx performance
--
--Signed-off-by: Felix Fietkau <nbd@openwrt.org>
-----
--
----- a/net/wireless/util.c
--+++ b/net/wireless/util.c
--@@ -644,23 +644,93 @@ int ieee80211_data_from_8023(struct sk_b
-- }
-- EXPORT_SYMBOL(ieee80211_data_from_8023);
-- 
--+static void
--+__frame_add_frag(struct sk_buff *skb, struct page *page,
--+		 void *ptr, int len, int size)
--+{
--+	struct skb_shared_info *sh = skb_shinfo(skb);
--+	int page_offset;
--+
--+	atomic_inc(&page->_count);
--+	page_offset = ptr - page_address(page);
--+	skb_add_rx_frag(skb, sh->nr_frags, page, page_offset, len, size);
--+}
--+
--+static void
--+__ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame,
--+			    int offset, int len)
--+{
--+	struct skb_shared_info *sh = skb_shinfo(skb);
--+	const skb_frag_t *frag = &sh->frags[-1];
--+	struct page *frag_page;
--+	void *frag_ptr;
--+	int frag_len, frag_size;
--+	int head_size = skb->len - skb->data_len;
--+	int cur_len;
--+
--+	frag_page = virt_to_head_page(skb->head);
--+	frag_ptr = skb->data;
--+	frag_size = head_size;
--+
--+	while (offset >= frag_size) {
--+		offset -= frag_size;
--+		frag++;
--+		frag_page = skb_frag_page(frag);
--+		frag_ptr = skb_frag_address(frag);
--+		frag_size = skb_frag_size(frag);
--+	}
--+
--+	frag_ptr += offset;
--+	frag_len = frag_size - offset;
--+
--+	cur_len = min(len, frag_len);
--+
--+	__frame_add_frag(frame, frag_page, frag_ptr, cur_len, frag_size);
--+	len -= cur_len;
--+
--+	while (len > 0) {
--+		frag++;
--+		frag_len = skb_frag_size(frag);
--+		cur_len = min(len, frag_len);
--+		__frame_add_frag(frame, skb_frag_page(frag),
--+				 skb_frag_address(frag), cur_len, frag_len);
--+		len -= cur_len;
--+	}
--+}
--+
-- static struct sk_buff *
-- __ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
---		       int offset, int len)
--+		       int offset, int len, bool reuse_frag)
-- {
-- 	struct sk_buff *frame;
--+	int cur_len = len;
-- 
-- 	if (skb->len - offset < len)
-- 		return NULL;
-- 
-- 	/*
--+	 * When reusing framents, copy some data to the head to simplify
--+	 * ethernet header handling and speed up protocol header processing
--+	 * in the stack later.
--+	 */
--+	if (reuse_frag)
--+		cur_len = min_t(int, len, 32);
--+
--+	/*
-- 	 * Allocate and reserve two bytes more for payload
-- 	 * alignment since sizeof(struct ethhdr) is 14.
-- 	 */
---	frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len);
--+	frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + cur_len);
-- 
-- 	skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
---	skb_copy_bits(skb, offset, skb_put(frame, len), len);
--+	skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
--+
--+	len -= cur_len;
--+	if (!len)
--+		return frame;
--+
--+	offset += cur_len;
--+	__ieee80211_amsdu_copy_frag(skb, frame, offset, len);
-- 
-- 	return frame;
-- }
--@@ -676,6 +746,7 @@ void ieee80211_amsdu_to_8023s(struct sk_
-- 	u8 *payload;
-- 	int offset = 0, remaining, err;
-- 	struct ethhdr eth;
--+	bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
-- 	bool reuse_skb = false;
-- 	bool last = false;
-- 
--@@ -703,12 +774,13 @@ void ieee80211_amsdu_to_8023s(struct sk_
-- 		offset += sizeof(struct ethhdr);
-- 		/* reuse skb for the last subframe */
-- 		last = remaining <= subframe_len + padding;
---		if (!skb_is_nonlinear(skb) && last) {
--+		if (!skb_is_nonlinear(skb) && !reuse_frag && last) {
-- 			skb_pull(skb, offset);
-- 			frame = skb;
-- 			reuse_skb = true;
-- 		} else {
---			frame = __ieee80211_amsdu_copy(skb, hlen, offset, len);
--+			frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
--+						       reuse_frag);
-- 			if (!frame)
-- 				goto purge;
-- 
-diff --git a/package/kernel/mac80211/patches/334-mac80211-fix-purging-multicast-PS-buffer-queue.patch b/package/kernel/mac80211/patches/334-mac80211-fix-purging-multicast-PS-buffer-queue.patch
-new file mode 100644
-index 0000000..dfcc6e4
---- /dev/null
-+++ b/package/kernel/mac80211/patches/334-mac80211-fix-purging-multicast-PS-buffer-queue.patch
-@@ -0,0 +1,54 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Tue, 2 Aug 2016 11:11:13 +0200
-+Subject: [PATCH] mac80211: fix purging multicast PS buffer queue
-+
-+The code currently assumes that buffered multicast PS frames don't have
-+a pending ACK frame for tx status reporting.
-+However, hostapd sends a broadcast deauth frame on teardown for which tx
-+status is requested. This can lead to the "Have pending ack frames"
-+warning on module reload.
-+Fix this by using ieee80211_free_txskb/ieee80211_purge_tx_queue.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/net/mac80211/cfg.c
-++++ b/net/mac80211/cfg.c
-+@@ -868,7 +868,7 @@ static int ieee80211_stop_ap(struct wiph
++ #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
++ 	struct ath_buf *bf = bf_first, *bf_prev = NULL;
++@@ -962,12 +982,13 @@ ath_tx_form_aggr(struct ath_softc *sc, s
++ 	struct ieee80211_tx_info *tx_info;
++ 	struct ath_frame_info *fi;
++ 	struct sk_buff *skb;
++-	bool closed = false;
+++
 + 
-+ 	/* free all potentially still buffered bcast frames */
-+ 	local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
-+-	skb_queue_purge(&sdata->u.ap.ps.bc_buf);
-++	ieee80211_purge_tx_queue(&local->hw, &sdata->u.ap.ps.bc_buf);
++ 	bf = bf_first;
++ 	aggr_limit = ath_lookup_rate(sc, bf, tid);
 + 
-+ 	mutex_lock(&local->mtx);
-+ 	ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
-+--- a/net/mac80211/tx.c
-++++ b/net/mac80211/tx.c
-+@@ -368,7 +368,7 @@ static void purge_old_ps_buffers(struct
-+ 		skb = skb_dequeue(&ps->bc_buf);
-+ 		if (skb) {
-+ 			purged++;
-+-			dev_kfree_skb(skb);
-++			ieee80211_free_txskb(&local->hw, skb);
++-	do {
+++	while (bf)
+++	{
++ 		skb = bf->bf_mpdu;
++ 		fi = get_frame_info(skb);
++ 
++@@ -976,12 +997,12 @@ ath_tx_form_aggr(struct ath_softc *sc, s
++ 		if (nframes) {
++ 			if (aggr_limit < al + bpad + al_delta ||
++ 			    ath_lookup_legacy(bf) || nframes >= h_baw)
++-				break;
+++				goto stop;
++ 
++ 			tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
++ 			if ((tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) ||
++ 			    !(tx_info->flags & IEEE80211_TX_CTL_AMPDU))
++-				break;
+++				goto stop;
 + 		}
-+ 		total += skb_queue_len(&ps->bc_buf);
-+ 	}
-+@@ -451,7 +451,7 @@ ieee80211_tx_h_multicast_ps_buf(struct i
-+ 	if (skb_queue_len(&ps->bc_buf) >= AP_MAX_BC_BUFFER) {
-+ 		ps_dbg(tx->sdata,
-+ 		       "BC TX buffer full - dropping the oldest frame\n");
-+-		dev_kfree_skb(skb_dequeue(&ps->bc_buf));
-++		ieee80211_free_txskb(&tx->local->hw, skb_dequeue(&ps->bc_buf));
-+ 	} else
-+ 		tx->local->total_ps_buffered++;
 + 
-+@@ -4276,7 +4276,7 @@ ieee80211_get_buffered_bc(struct ieee802
-+ 			sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
-+ 		if (!ieee80211_tx_prepare(sdata, &tx, NULL, skb))
-+ 			break;
-+-		dev_kfree_skb_any(skb);
-++		ieee80211_free_txskb(hw, skb);
-+ 	}
++ 		/* add padding for previous frame to aggregation length */
++@@ -1003,20 +1024,18 @@ ath_tx_form_aggr(struct ath_softc *sc, s
++ 			ath_tx_addto_baw(sc, tid, bf);
++ 		bf->bf_state.ndelim = ndelim;
 + 
-+ 	info = IEEE80211_SKB_CB(skb);
-diff --git a/package/kernel/mac80211/patches/334-mac80211-fix-wiphy-supported_band-access.patch b/package/kernel/mac80211/patches/334-mac80211-fix-wiphy-supported_band-access.patch
-deleted file mode 100644
-index f8f4f09..0000000
---- a/package/kernel/mac80211/patches/334-mac80211-fix-wiphy-supported_band-access.patch
-+++ /dev/null
-@@ -1,36 +0,0 @@
--From: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
--Date: Wed, 10 Feb 2016 16:08:17 +0100
--Subject: [PATCH] mac80211: fix wiphy supported_band access
--
--Fix wiphy supported_band access in tx radiotap parsing. In particular,
--info->band is always set to 0 (IEEE80211_BAND_2GHZ) since it has not
--assigned yet. This cause a kernel crash on 5GHz only devices.
--Move ieee80211_parse_tx_radiotap() after info->band assignment
--
--Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
-----
--
----- a/net/mac80211/tx.c
--+++ b/net/mac80211/tx.c
--@@ -1890,10 +1890,6 @@ netdev_tx_t ieee80211_monitor_start_xmit
-- 	info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
-- 		      IEEE80211_TX_CTL_INJECTED;
-- 
---	/* process and remove the injection radiotap header */
---	if (!ieee80211_parse_tx_radiotap(local, skb))
---		goto fail;
---
-- 	rcu_read_lock();
-- 
-- 	/*
--@@ -1955,6 +1951,10 @@ netdev_tx_t ieee80211_monitor_start_xmit
-- 		goto fail_rcu;
-- 
-- 	info->band = chandef->chan->band;
--+	/* process and remove the injection radiotap header */
--+	if (!ieee80211_parse_tx_radiotap(local, skb))
--+		goto fail_rcu;
--+
-- 	ieee80211_xmit(sdata, NULL, skb);
-- 	rcu_read_unlock();
-- 
-diff --git a/package/kernel/mac80211/patches/335-ath9k-use-ieee80211_tx_status_noskb-where-possible.patch b/package/kernel/mac80211/patches/335-ath9k-use-ieee80211_tx_status_noskb-where-possible.patch
-new file mode 100644
-index 0000000..dbb5b90
---- /dev/null
-+++ b/package/kernel/mac80211/patches/335-ath9k-use-ieee80211_tx_status_noskb-where-possible.patch
-@@ -0,0 +1,305 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Tue, 2 Aug 2016 12:12:18 +0200
-+Subject: [PATCH] ath9k: use ieee80211_tx_status_noskb where possible
-+
-+It removes the need for undoing the padding changes to skb->data and it
-+improves performance by eliminating one tx status lookup per MPDU in the
-+status path. It is also useful for preparing a follow-up fix to better
-+handle powersave filtering.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/xmit.c
-++++ b/drivers/net/wireless/ath/ath9k/xmit.c
-+@@ -50,9 +50,11 @@ static u16 bits_per_symbol[][2] = {
-+ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
-+ 			       struct ath_atx_tid *tid, struct sk_buff *skb);
-+ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
-+-			    int tx_flags, struct ath_txq *txq);
-++			    int tx_flags, struct ath_txq *txq,
-++			    struct ieee80211_sta *sta);
-+ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
-+ 				struct ath_txq *txq, struct list_head *bf_q,
-++				struct ieee80211_sta *sta,
-+ 				struct ath_tx_status *ts, int txok);
-+ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
-+ 			     struct list_head *head, bool internal);
-+@@ -77,6 +79,22 @@ enum {
-+ /* Aggregation logic */
-+ /*********************/
-+ 
-++static void ath_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
-++{
-++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-++	struct ieee80211_sta *sta = info->status.status_driver_data[0];
-++
-++	if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
-++		ieee80211_tx_status(hw, skb);
-++		return;
++-		__skb_unlink(skb, tid_q);
++ 		list_add_tail(&bf->list, bf_q);
++ 		if (bf_prev)
++ 			bf_prev->bf_next = bf;
++ 
++ 		bf_prev = bf;
++ 
++-		bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
++-		if (!bf) {
++-			closed = true;
++-			break;
++-		}
++-	} while (ath_tid_has_buffered(tid));
++-
+++		bf = ath_tx_get_tid_subframe(sc, txq, tid);
 ++	}
-++
-++	if (sta)
-++		ieee80211_tx_status_noskb(hw, sta, info);
-++
-++	dev_kfree_skb(skb);
-++}
-++
-+ void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq)
-+ 	__acquires(&txq->axq_lock)
+++	goto finish;
+++stop:
+++	__skb_queue_tail(&tid->retry_q, bf->bf_mpdu);
+++finish:
++ 	bf = bf_first;
++ 	bf->bf_lastbf = bf_prev;
++ 
++@@ -1027,9 +1046,7 @@ ath_tx_form_aggr(struct ath_softc *sc, s
++ 		TX_STAT_INC(txq->axq_qnum, a_aggr);
++ 	}
++ 
++-	*aggr_len = al;
++-
++-	return closed;
+++	return al;
++ #undef PADBYTES
++ }
++ 
++@@ -1406,18 +1423,15 @@ static void ath_tx_fill_desc(struct ath_
++ static void
++ ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq,
++ 		  struct ath_atx_tid *tid, struct list_head *bf_q,
++-		  struct ath_buf *bf_first, struct sk_buff_head *tid_q)
+++		  struct ath_buf *bf_first)
 + {
-+@@ -92,6 +110,7 @@ void ath_txq_unlock(struct ath_softc *sc
-+ void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
-+ 	__releases(&txq->axq_lock)
++ 	struct ath_buf *bf = bf_first, *bf_prev = NULL;
++-	struct sk_buff *skb;
++ 	int nframes = 0;
++ 
++ 	do {
++ 		struct ieee80211_tx_info *tx_info;
++-		skb = bf->bf_mpdu;
++ 
++ 		nframes++;
++-		__skb_unlink(skb, tid_q);
++ 		list_add_tail(&bf->list, bf_q);
++ 		if (bf_prev)
++ 			bf_prev->bf_next = bf;
++@@ -1426,13 +1440,15 @@ ath_tx_form_burst(struct ath_softc *sc,
++ 		if (nframes >= 2)
++ 			break;
++ 
++-		bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
+++		bf = ath_tx_get_tid_subframe(sc, txq, tid);
++ 		if (!bf)
++ 			break;
++ 
++ 		tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
++-		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
+++		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+++			__skb_queue_tail(&tid->retry_q, bf->bf_mpdu);
++ 			break;
+++		}
++ 
++ 		ath_set_rates(tid->an->vif, tid->an->sta, bf, false);
++ 	} while (1);
++@@ -1443,34 +1459,33 @@ static bool ath_tx_sched_aggr(struct ath
 + {
-++	struct ieee80211_hw *hw = sc->hw;
-+ 	struct sk_buff_head q;
-+ 	struct sk_buff *skb;
++ 	struct ath_buf *bf;
++ 	struct ieee80211_tx_info *tx_info;
++-	struct sk_buff_head *tid_q;
++ 	struct list_head bf_q;
++ 	int aggr_len = 0;
++-	bool aggr, last = true;
+++	bool aggr;
 + 
-+@@ -100,7 +119,7 @@ void ath_txq_unlock_complete(struct ath_
-+ 	spin_unlock_bh(&txq->axq_lock);
++ 	if (!ath_tid_has_buffered(tid))
++ 		return false;
 + 
-+ 	while ((skb = __skb_dequeue(&q)))
-+-		ieee80211_tx_status(sc->hw, skb);
-++		ath_tx_status(hw, skb);
-+ }
++ 	INIT_LIST_HEAD(&bf_q);
 + 
-+ static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq,
-+@@ -268,7 +287,7 @@ static void ath_tx_flush_tid(struct ath_
-+ 		}
++-	bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
+++	bf = ath_tx_get_tid_subframe(sc, txq, tid);
++ 	if (!bf)
++ 		return false;
++ 
++ 	tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
++ 	aggr = !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
++ 	if ((aggr && txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) ||
++-		(!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) {
+++	    (!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) {
+++		__skb_queue_tail(&tid->retry_q, bf->bf_mpdu);
++ 		*stop = true;
++ 		return false;
++ 	}
++ 
++ 	ath_set_rates(tid->an->vif, tid->an->sta, bf, false);
++ 	if (aggr)
++-		last = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf,
++-					tid_q, &aggr_len);
+++		aggr_len = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf);
++ 	else
++-		ath_tx_form_burst(sc, txq, tid, &bf_q, bf, tid_q);
+++		ath_tx_form_burst(sc, txq, tid, &bf_q, bf);
 + 
-+ 		list_add_tail(&bf->list, &bf_head);
-+-		ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
-++		ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, &ts, 0);
++ 	if (list_empty(&bf_q))
++ 		return false;
++@@ -1513,9 +1528,6 @@ int ath_tx_aggr_start(struct ath_softc *
++ 		an->mpdudensity = density;
 + 	}
 + 
-+ 	if (sendbar) {
-+@@ -333,12 +352,12 @@ static void ath_tid_drain(struct ath_sof
-+ 		bf = fi->bf;
++-	/* force sequence number allocation for pending frames */
++-	ath_tx_tid_change_state(sc, txtid);
++-
++ 	txtid->active = true;
++ 	*ssn = txtid->seq_start = txtid->seq_next;
++ 	txtid->bar_index = -1;
++@@ -1540,7 +1552,6 @@ void ath_tx_aggr_stop(struct ath_softc *
++ 	ath_txq_lock(sc, txq);
++ 	txtid->active = false;
++ 	ath_tx_flush_tid(sc, txtid);
++-	ath_tx_tid_change_state(sc, txtid);
++ 	ath_txq_unlock_complete(sc, txq);
++ }
++ 
++@@ -1550,14 +1561,12 @@ void ath_tx_aggr_sleep(struct ieee80211_
++ 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++ 	struct ath_atx_tid *tid;
++ 	struct ath_txq *txq;
++-	bool buffered;
++ 	int tidno;
++ 
++ 	ath_dbg(common, XMIT, "%s called\n", __func__);
 + 
-+ 		if (!bf) {
-+-			ath_tx_complete(sc, skb, ATH_TX_ERROR, txq);
-++			ath_tx_complete(sc, skb, ATH_TX_ERROR, txq, NULL);
++-	for (tidno = 0, tid = &an->tid[tidno];
++-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
++-
+++	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
+++		tid = ATH_AN_2_TID(an, tidno);
++ 		txq = tid->txq;
++ 
++ 		ath_txq_lock(sc, txq);
++@@ -1567,13 +1576,12 @@ void ath_tx_aggr_sleep(struct ieee80211_
 + 			continue;
 + 		}
 + 
-+ 		list_add_tail(&bf->list, &bf_head);
-+-		ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
-++		ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, &ts, 0);
++-		buffered = ath_tid_has_buffered(tid);
+++		if (!skb_queue_empty(&tid->retry_q))
+++			ieee80211_sta_set_buffered(sta, tid->tidno, true);
++ 
++ 		list_del_init(&tid->list);
++ 
++ 		ath_txq_unlock(sc, txq);
++-
++-		ieee80211_sta_set_buffered(sta, tidno, buffered);
 + 	}
 + }
 + 
-+@@ -441,12 +460,11 @@ static void ath_tx_count_frames(struct a
++@@ -1586,19 +1594,16 @@ void ath_tx_aggr_wakeup(struct ath_softc
 + 
-+ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
-+ 				 struct ath_buf *bf, struct list_head *bf_q,
-++				 struct ieee80211_sta *sta,
-+ 				 struct ath_tx_status *ts, int txok)
-+ {
-+ 	struct ath_node *an = NULL;
-+ 	struct sk_buff *skb;
-+-	struct ieee80211_sta *sta;
-+-	struct ieee80211_hw *hw = sc->hw;
-+ 	struct ieee80211_hdr *hdr;
-+ 	struct ieee80211_tx_info *tx_info;
-+ 	struct ath_atx_tid *tid = NULL;
-+@@ -475,12 +493,7 @@ static void ath_tx_complete_aggr(struct
-+ 	for (i = 0; i < ts->ts_rateindex; i++)
-+ 		retries += rates[i].count;
++ 	ath_dbg(common, XMIT, "%s called\n", __func__);
 + 
-+-	rcu_read_lock();
-+-
-+-	sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2);
-+ 	if (!sta) {
-+-		rcu_read_unlock();
++-	for (tidno = 0, tid = &an->tid[tidno];
++-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
 +-
-+ 		INIT_LIST_HEAD(&bf_head);
-+ 		while (bf) {
-+ 			bf_next = bf->bf_next;
-+@@ -488,7 +501,7 @@ static void ath_tx_complete_aggr(struct
-+ 			if (!bf->bf_state.stale || bf_next != NULL)
-+ 				list_move_tail(&bf->list, &bf_head);
-+ 
-+-			ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, 0);
-++			ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, ts, 0);
+++	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
+++		tid = ATH_AN_2_TID(an, tidno);
++ 		txq = tid->txq;
 + 
-+ 			bf = bf_next;
++ 		ath_txq_lock(sc, txq);
++ 		tid->clear_ps_filter = true;
++-
++ 		if (ath_tid_has_buffered(tid)) {
++ 			ath_tx_queue_tid(sc, txq, tid);
++ 			ath_txq_schedule(sc, txq);
 + 		}
-+@@ -598,7 +611,7 @@ static void ath_tx_complete_aggr(struct
-+ 								ts);
-+ 			}
-+ 
-+-			ath_tx_complete_buf(sc, bf, txq, &bf_head, ts,
-++			ath_tx_complete_buf(sc, bf, txq, &bf_head, sta, ts,
-+ 				!txfail);
-+ 		} else {
-+ 			if (tx_info->flags & IEEE80211_TX_STATUS_EOSP) {
-+@@ -619,7 +632,8 @@ static void ath_tx_complete_aggr(struct
-+ 					ath_tx_update_baw(sc, tid, seqno);
-+ 
-+ 					ath_tx_complete_buf(sc, bf, txq,
-+-							    &bf_head, ts, 0);
-++							    &bf_head, NULL, ts,
-++							    0);
-+ 					bar_index = max_t(int, bar_index,
-+ 						ATH_BA_INDEX(seq_first, seqno));
-+ 					break;
-+@@ -663,8 +677,6 @@ static void ath_tx_complete_aggr(struct
-+ 		ath_txq_lock(sc, txq);
-+ 	}
-+ 
-+-	rcu_read_unlock();
 +-
-+ 	if (needreset)
-+ 		ath9k_queue_reset(sc, RESET_TYPE_TX_ERROR);
++ 		ath_txq_unlock_complete(sc, txq);
++ 	}
 + }
-+@@ -679,7 +691,10 @@ static void ath_tx_process_buffer(struct
-+ 				  struct ath_tx_status *ts, struct ath_buf *bf,
-+ 				  struct list_head *bf_head)
-+ {
-++	struct ieee80211_hw *hw = sc->hw;
-+ 	struct ieee80211_tx_info *info;
-++	struct ieee80211_sta *sta;
-++	struct ieee80211_hdr *hdr;
-+ 	bool txok, flush;
-+ 
-+ 	txok = !(ts->ts_status & ATH9K_TXERR_MASK);
-+@@ -692,6 +707,10 @@ static void ath_tx_process_buffer(struct
++@@ -1621,11 +1626,6 @@ void ath_tx_aggr_resume(struct ath_softc
 + 
-+ 	ts->duration = ath9k_hw_get_duration(sc->sc_ah, bf->bf_desc,
-+ 					     ts->ts_rateindex);
-++
-++	hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
-++	sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2);
-++
-+ 	if (!bf_isampdu(bf)) {
-+ 		if (!flush) {
-+ 			info = IEEE80211_SKB_CB(bf->bf_mpdu);
-+@@ -700,9 +719,9 @@ static void ath_tx_process_buffer(struct
-+ 			ath_tx_rc_status(sc, bf, ts, 1, txok ? 0 : 1, txok);
-+ 			ath_dynack_sample_tx_ts(sc->sc_ah, bf->bf_mpdu, ts);
-+ 		}
-+-		ath_tx_complete_buf(sc, bf, txq, bf_head, ts, txok);
-++		ath_tx_complete_buf(sc, bf, txq, bf_head, sta, ts, txok);
-+ 	} else
-+-		ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok);
-++		ath_tx_complete_aggr(sc, txq, bf, bf_head, sta, ts, txok);
++ 	tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
 + 
-+ 	if (!flush)
-+ 		ath_txq_schedule(sc, txq);
-+@@ -938,7 +957,7 @@ ath_tx_get_tid_subframe(struct ath_softc
-+ 			list_add(&bf->list, &bf_head);
-+ 			__skb_unlink(skb, *q);
-+ 			ath_tx_update_baw(sc, tid, seqno);
-+-			ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
-++			ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, &ts, 0);
-+ 			continue;
-+ 		}
++-	if (ath_tid_has_buffered(tid)) {
++-		ath_tx_queue_tid(sc, txq, tid);
++-		ath_txq_schedule(sc, txq);
++-	}
++-
++ 	ath_txq_unlock_complete(sc, txq);
++ }
 + 
-+@@ -1847,6 +1866,7 @@ static void ath_drain_txq_list(struct at
-+  */
-+ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq)
-+ {
-++	rcu_read_lock();
-+ 	ath_txq_lock(sc, txq);
++@@ -1641,7 +1641,6 @@ void ath9k_release_buffered_frames(struc
++ 	struct ieee80211_tx_info *info;
++ 	struct list_head bf_q;
++ 	struct ath_buf *bf_tail = NULL, *bf;
++-	struct sk_buff_head *tid_q;
++ 	int sent = 0;
++ 	int i;
 + 
-+ 	if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
-+@@ -1865,6 +1885,7 @@ void ath_draintxq(struct ath_softc *sc,
-+ 	ath_drain_txq_list(sc, txq, &txq->axq_q);
++@@ -1656,11 +1655,10 @@ void ath9k_release_buffered_frames(struc
 + 
-+ 	ath_txq_unlock_complete(sc, txq);
-++	rcu_read_unlock();
-+ }
++ 		ath_txq_lock(sc, tid->txq);
++ 		while (nframes > 0) {
++-			bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid, &tid_q);
+++			bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid);
++ 			if (!bf)
++ 				break;
 + 
-+ bool ath_drain_all_txq(struct ath_softc *sc)
-+@@ -2487,7 +2508,8 @@ void ath_tx_cabq(struct ieee80211_hw *hw
-+ /*****************/
++-			__skb_unlink(bf->bf_mpdu, tid_q);
++ 			list_add_tail(&bf->list, &bf_q);
++ 			ath_set_rates(tid->an->vif, tid->an->sta, bf, true);
++ 			if (bf_isampdu(bf)) {
++@@ -1675,7 +1673,7 @@ void ath9k_release_buffered_frames(struc
++ 			sent++;
++ 			TX_STAT_INC(txq->axq_qnum, a_queued_hw);
 + 
-+ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
-+-			    int tx_flags, struct ath_txq *txq)
-++			    int tx_flags, struct ath_txq *txq,
-++			    struct ieee80211_sta *sta)
-+ {
-+ 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-+ 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-+@@ -2507,15 +2529,17 @@ static void ath_tx_complete(struct ath_s
-+ 			tx_info->flags |= IEEE80211_TX_STAT_ACK;
-+ 	}
++-			if (an->sta && !ath_tid_has_buffered(tid))
+++			if (an->sta && skb_queue_empty(&tid->retry_q))
++ 				ieee80211_sta_set_buffered(an->sta, i, false);
++ 		}
++ 		ath_txq_unlock_complete(sc, tid->txq);
++@@ -1902,13 +1900,7 @@ bool ath_drain_all_txq(struct ath_softc
++ 		if (!ATH_TXQ_SETUP(sc, i))
++ 			continue;
 + 
-+-	padpos = ieee80211_hdrlen(hdr->frame_control);
-+-	padsize = padpos & 3;
-+-	if (padsize && skb->len>padpos+padsize) {
 +-		/*
-+-		 * Remove MAC header padding before giving the frame back to
-+-		 * mac80211.
++-		 * The caller will resume queues with ieee80211_wake_queues.
++-		 * Mark the queue as not stopped to prevent ath_tx_complete
++-		 * from waking the queue too early.
 +-		 */
-+-		memmove(skb->data + padsize, skb->data, padpos);
-+-		skb_pull(skb, padsize);
-++	if (tx_info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
-++		padpos = ieee80211_hdrlen(hdr->frame_control);
-++		padsize = padpos & 3;
-++		if (padsize && skb->len>padpos+padsize) {
-++			/*
-++			 * Remove MAC header padding before giving the frame back to
-++			 * mac80211.
-++			 */
-++			memmove(skb->data + padsize, skb->data, padpos);
-++			skb_pull(skb, padsize);
-++		}
++ 		txq = &sc->tx.txq[i];
++-		txq->stopped = false;
++ 		ath_draintxq(sc, txq);
 + 	}
 + 
-+ 	spin_lock_irqsave(&sc->sc_pm_lock, flags);
-+@@ -2530,12 +2554,14 @@ static void ath_tx_complete(struct ath_s
-+ 	}
-+ 	spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
++@@ -2308,15 +2300,12 @@ int ath_tx_start(struct ieee80211_hw *hw
++ 	struct ath_txq *txq = txctl->txq;
++ 	struct ath_atx_tid *tid = NULL;
++ 	struct ath_buf *bf;
++-	bool queue, ps_resp;
+++	bool ps_resp;
++ 	int q, ret;
 + 
-+-	__skb_queue_tail(&txq->complete_q, skb);
-+ 	ath_txq_skb_done(sc, txq, skb);
-++	tx_info->status.status_driver_data[0] = sta;
-++	__skb_queue_tail(&txq->complete_q, skb);
-+ }
++ 	if (vif)
++ 		avp = (void *)vif->drv_priv;
 + 
-+ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
-+ 				struct ath_txq *txq, struct list_head *bf_q,
-++				struct ieee80211_sta *sta,
-+ 				struct ath_tx_status *ts, int txok)
-+ {
-+ 	struct sk_buff *skb = bf->bf_mpdu;
-+@@ -2563,7 +2589,7 @@ static void ath_tx_complete_buf(struct a
-+ 			complete(&sc->paprd_complete);
-+ 	} else {
-+ 		ath_debug_stat_tx(sc, bf, ts, txq, tx_flags);
-+-		ath_tx_complete(sc, skb, tx_flags, txq);
-++		ath_tx_complete(sc, skb, tx_flags, txq, sta);
-+ 	}
-+ skip_tx_complete:
-+ 	/* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't
-+@@ -2715,10 +2741,12 @@ void ath_tx_tasklet(struct ath_softc *sc
-+ 	u32 qcumask = ((1 << ATH9K_NUM_TX_QUEUES) - 1) & ah->intr_txqs;
-+ 	int i;
++-	if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
++-		txctl->force_channel = true;
++-
++ 	ps_resp = !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE);
 + 
-++	rcu_read_lock();
-+ 	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
-+ 		if (ATH_TXQ_SETUP(sc, i) && (qcumask & (1 << i)))
-+ 			ath_tx_processq(sc, &sc->tx.txq[i]);
-+ 	}
-++	rcu_read_unlock();
-+ }
++ 	ret = ath_tx_prepare(hw, skb, txctl);
++@@ -2331,63 +2320,13 @@ int ath_tx_start(struct ieee80211_hw *hw
 + 
-+ void ath_tx_edma_tasklet(struct ath_softc *sc)
-+@@ -2732,6 +2760,7 @@ void ath_tx_edma_tasklet(struct ath_soft
-+ 	struct list_head *fifo_list;
-+ 	int status;
++ 	q = skb_get_queue_mapping(skb);
 + 
-++	rcu_read_lock();
-+ 	for (;;) {
-+ 		if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
-+ 			break;
-+@@ -2802,6 +2831,7 @@ void ath_tx_edma_tasklet(struct ath_soft
-+ 		ath_tx_process_buffer(sc, txq, &ts, bf, &bf_head);
-+ 		ath_txq_unlock_complete(sc, txq);
+++	if (ps_resp)
+++		txq = sc->tx.uapsdq;
+++
++ 	ath_txq_lock(sc, txq);
++ 	if (txq == sc->tx.txq_map[q]) {
++ 		fi->txq = q;
++-		if (++txq->pending_frames > sc->tx.txq_max_pending[q] &&
++-		    !txq->stopped) {
++-			if (ath9k_is_chanctx_enabled())
++-				ieee80211_stop_queue(sc->hw, info->hw_queue);
++-			else
++-				ieee80211_stop_queue(sc->hw, q);
++-			txq->stopped = true;
++-		}
++-	}
++-
++-	queue = ieee80211_is_data_present(hdr->frame_control);
++-
++-	/* If chanctx, queue all null frames while NOA could be there */
++-	if (ath9k_is_chanctx_enabled() &&
++-	    ieee80211_is_nullfunc(hdr->frame_control) &&
++-	    !txctl->force_channel)
++-		queue = true;
++-
++-	/* Force queueing of all frames that belong to a virtual interface on
++-	 * a different channel context, to ensure that they are sent on the
++-	 * correct channel.
++-	 */
++-	if (((avp && avp->chanctx != sc->cur_chan) ||
++-	     sc->cur_chan->stopped) && !txctl->force_channel) {
++-		if (!txctl->an)
++-			txctl->an = &avp->mcast_node;
++-		queue = true;
++-		ps_resp = false;
++-	}
++-
++-	if (txctl->an && queue)
++-		tid = ath_get_skb_tid(sc, txctl->an, skb);
++-
++-	if (ps_resp) {
++-		ath_txq_unlock(sc, txq);
++-		txq = sc->tx.uapsdq;
++-		ath_txq_lock(sc, txq);
++-	} else if (txctl->an && queue) {
++-		WARN_ON(tid->txq != txctl->txq);
++-
++-		if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
++-			tid->clear_ps_filter = true;
++-
++-		/*
++-		 * Add this frame to software queue for scheduling later
++-		 * for aggregation.
++-		 */
++-		TX_STAT_INC(txq->axq_qnum, a_queued_sw);
++-		__skb_queue_tail(&tid->buf_q, skb);
++-		if (!txctl->an->sleeping)
++-			ath_tx_queue_tid(sc, txq, tid);
++-
++-		ath_txq_schedule(sc, txq);
++-		goto out;
+++		++txq->pending_frames;
 + 	}
-++	rcu_read_unlock();
-+ }
 + 
-+ /*****************/
-diff --git a/package/kernel/mac80211/patches/335-mac80211-minstrel_ht-set-A-MSDU-tx-limits-based-on-s.patch b/package/kernel/mac80211/patches/335-mac80211-minstrel_ht-set-A-MSDU-tx-limits-based-on-s.patch
-deleted file mode 100644
-index acaacf7..0000000
---- a/package/kernel/mac80211/patches/335-mac80211-minstrel_ht-set-A-MSDU-tx-limits-based-on-s.patch
-+++ /dev/null
-@@ -1,61 +0,0 @@
--From: Felix Fietkau <nbd@openwrt.org>
--Date: Thu, 18 Feb 2016 19:30:05 +0100
--Subject: [PATCH] mac80211: minstrel_ht: set A-MSDU tx limits based on selected
-- max_prob_rate
--
--Prevents excessive A-MSDU aggregation at low data rates or bad
--conditions.
--
--Signed-off-by: Felix Fietkau <nbd@openwrt.org>
-----
--
----- a/net/mac80211/rc80211_minstrel_ht.c
--+++ b/net/mac80211/rc80211_minstrel_ht.c
--@@ -883,6 +883,39 @@ minstrel_ht_set_rate(struct minstrel_pri
-- 	ratetbl->rate[offset].flags = flags;
-- }
-- 
--+static int
--+minstrel_ht_get_max_amsdu_len(struct minstrel_ht_sta *mi)
--+{
--+	int group = mi->max_prob_rate / MCS_GROUP_RATES;
--+	const struct mcs_group *g = &minstrel_mcs_groups[group];
--+	int rate = mi->max_prob_rate % MCS_GROUP_RATES;
--+
--+	/* Disable A-MSDU if max_prob_rate is bad */
--+	if (mi->groups[group].rates[rate].prob_ewma < MINSTREL_FRAC(50, 100))
--+		return 1;
--+
--+	/* If the rate is slower than single-stream MCS1, make A-MSDU limit small */
--+	if (g->duration[rate] > MCS_DURATION(1, 0, 52))
--+		return 500;
--+
--+	/*
--+	 * If the rate is slower than single-stream MCS4, limit A-MSDU to usual
--+	 * data packet size
--+	 */
--+	if (g->duration[rate] > MCS_DURATION(1, 0, 104))
--+		return 1500;
--+
--+	/*
--+	 * If the rate is slower than single-stream MCS7, limit A-MSDU to twice
--+	 * the usual data packet size
--+	 */
--+	if (g->duration[rate] > MCS_DURATION(1, 0, 260))
--+		return 3000;
--+
--+	/* unlimited */
--+	return 0;
--+}
--+
-- static void
-- minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
-- {
--@@ -907,6 +940,7 @@ minstrel_ht_update_rates(struct minstrel
-- 		minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_prob_rate);
-- 	}
-- 
--+	mi->sta->max_rc_amsdu_len = minstrel_ht_get_max_amsdu_len(mi);
-- 	rates->rate[i].idx = -1;
-- 	rate_control_set_rates(mp->hw, mi->sta, rates);
-- }
-diff --git a/package/kernel/mac80211/patches/336-ath9k-improve-powersave-filter-handling.patch b/package/kernel/mac80211/patches/336-ath9k-improve-powersave-filter-handling.patch
-new file mode 100644
-index 0000000..67a6c63
---- /dev/null
-+++ b/package/kernel/mac80211/patches/336-ath9k-improve-powersave-filter-handling.patch
-@@ -0,0 +1,70 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Tue, 2 Aug 2016 12:13:35 +0200
-+Subject: [PATCH] ath9k: improve powersave filter handling
-+
-+For non-aggregated frames, ath9k was leaving handling of powersave
-+filtered packets to mac80211. This can be too slow if the intermediate
-+queue is already filled with packets and mac80211 does not immediately
-+send a new packet via drv_tx().
-+
-+Improve response time with filtered frames by triggering clearing the
-+powersave filter internally.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/xmit.c
-++++ b/drivers/net/wireless/ath/ath9k/xmit.c
-+@@ -461,13 +461,13 @@ static void ath_tx_count_frames(struct a
-+ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
-+ 				 struct ath_buf *bf, struct list_head *bf_q,
-+ 				 struct ieee80211_sta *sta,
-++				 struct ath_atx_tid *tid,
-+ 				 struct ath_tx_status *ts, int txok)
-+ {
-+ 	struct ath_node *an = NULL;
-+ 	struct sk_buff *skb;
-+ 	struct ieee80211_hdr *hdr;
-+ 	struct ieee80211_tx_info *tx_info;
-+-	struct ath_atx_tid *tid = NULL;
-+ 	struct ath_buf *bf_next, *bf_last = bf->bf_lastbf;
-+ 	struct list_head bf_head;
-+ 	struct sk_buff_head bf_pending;
-+@@ -509,7 +509,6 @@ static void ath_tx_complete_aggr(struct
++ 	bf = ath_tx_setup_buffer(sc, txq, tid, skb);
++@@ -2871,9 +2810,8 @@ void ath_tx_node_init(struct ath_softc *
++ 	struct ath_atx_tid *tid;
++ 	int tidno, acno;
++ 
++-	for (tidno = 0, tid = &an->tid[tidno];
++-	     tidno < IEEE80211_NUM_TIDS;
++-	     tidno++, tid++) {
+++	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
+++		tid = ATH_AN_2_TID(an, tidno);
++ 		tid->an        = an;
++ 		tid->tidno     = tidno;
++ 		tid->seq_start = tid->seq_next = 0;
++@@ -2881,11 +2819,14 @@ void ath_tx_node_init(struct ath_softc *
++ 		tid->baw_head  = tid->baw_tail = 0;
++ 		tid->active	   = false;
++ 		tid->clear_ps_filter = true;
++-		__skb_queue_head_init(&tid->buf_q);
+++		tid->has_queued  = false;
++ 		__skb_queue_head_init(&tid->retry_q);
++ 		INIT_LIST_HEAD(&tid->list);
++ 		acno = TID_TO_WME_AC(tidno);
++ 		tid->txq = sc->tx.txq_map[acno];
+++
+++		if (!an->sta)
+++			break; /* just one multicast ath_atx_tid */
 + 	}
++ }
 + 
-+ 	an = (struct ath_node *)sta->drv_priv;
-+-	tid = ath_get_skb_tid(sc, an, skb);
-+ 	seq_first = tid->seq_start;
-+ 	isba = ts->ts_flags & ATH9K_TX_BA;
++@@ -2895,9 +2836,8 @@ void ath_tx_node_cleanup(struct ath_soft
++ 	struct ath_txq *txq;
++ 	int tidno;
 + 
-+@@ -695,6 +694,7 @@ static void ath_tx_process_buffer(struct
-+ 	struct ieee80211_tx_info *info;
-+ 	struct ieee80211_sta *sta;
-+ 	struct ieee80211_hdr *hdr;
-++	struct ath_atx_tid *tid = NULL;
-+ 	bool txok, flush;
-+ 
-+ 	txok = !(ts->ts_status & ATH9K_TXERR_MASK);
-+@@ -710,6 +710,12 @@ static void ath_tx_process_buffer(struct
-+ 
-+ 	hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
-+ 	sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr1, hdr->addr2);
-++	if (sta) {
-++		struct ath_node *an = (struct ath_node *)sta->drv_priv;
-++		tid = ath_get_skb_tid(sc, an, bf->bf_mpdu);
-++		if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY))
-++			tid->clear_ps_filter = true;
-++	}
++-	for (tidno = 0, tid = &an->tid[tidno];
++-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
++-
+++	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
+++		tid = ATH_AN_2_TID(an, tidno);
++ 		txq = tid->txq;
 + 
-+ 	if (!bf_isampdu(bf)) {
-+ 		if (!flush) {
-+@@ -721,7 +727,7 @@ static void ath_tx_process_buffer(struct
-+ 		}
-+ 		ath_tx_complete_buf(sc, bf, txq, bf_head, sta, ts, txok);
-+ 	} else
-+-		ath_tx_complete_aggr(sc, txq, bf, bf_head, sta, ts, txok);
-++		ath_tx_complete_aggr(sc, txq, bf, bf_head, sta, tid, ts, txok);
++ 		ath_txq_lock(sc, txq);
++@@ -2909,6 +2849,9 @@ void ath_tx_node_cleanup(struct ath_soft
++ 		tid->active = false;
 + 
-+ 	if (!flush)
-+ 		ath_txq_schedule(sc, txq);
-diff --git a/package/kernel/mac80211/patches/336-mac80211-minstrel_ht-set-default-tx-aggregation-time.patch b/package/kernel/mac80211/patches/336-mac80211-minstrel_ht-set-default-tx-aggregation-time.patch
++ 		ath_txq_unlock(sc, txq);
+++
+++		if (!an->sta)
+++			break; /* just one multicast ath_atx_tid */
++ 	}
++ }
++ 
+diff --git a/package/kernel/mac80211/patches/320-cfg80211-add-support-for-non-linear-skbs-in-ieee8021.patch b/package/kernel/mac80211/patches/320-cfg80211-add-support-for-non-linear-skbs-in-ieee8021.patch
 deleted file mode 100644
-index 32a2ad6..0000000
---- a/package/kernel/mac80211/patches/336-mac80211-minstrel_ht-set-default-tx-aggregation-time.patch
+index 2eeed22..0000000
+--- a/package/kernel/mac80211/patches/320-cfg80211-add-support-for-non-linear-skbs-in-ieee8021.patch
 +++ /dev/null
-@@ -1,31 +0,0 @@
+@@ -1,159 +0,0 @@
 -From: Felix Fietkau <nbd@openwrt.org>
--Date: Thu, 18 Feb 2016 19:45:33 +0100
--Subject: [PATCH] mac80211: minstrel_ht: set default tx aggregation timeout to
-- 0
--
--The value 5000 was put here with the addition of the timeout field to
--ieee80211_start_tx_ba_session. It was originally added in mac80211 to
--save resources for drivers like iwlwifi, which only supports a limited
--number of concurrent aggregation sessions.
--
--Since iwlwifi does not use minstrel_ht and other drivers don't need
--this, 0 is a better default - especially since there have been
--recent reports of aggregation setup related issues reproduced with
--ath9k. This should improve stability without causing any adverse
--effects.
+-Date: Tue, 2 Feb 2016 14:39:10 +0100
+-Subject: [PATCH] cfg80211: add support for non-linear skbs in
+- ieee80211_amsdu_to_8023s
 -
--Cc: stable@vger.kernel.org
 -Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
 ----
 -
----- a/net/mac80211/rc80211_minstrel_ht.c
--+++ b/net/mac80211/rc80211_minstrel_ht.c
--@@ -692,7 +692,7 @@ minstrel_aggr_check(struct ieee80211_sta
-- 	if (likely(sta->ampdu_mlme.tid_tx[tid]))
-- 		return;
-- 
---	ieee80211_start_tx_ba_session(pubsta, tid, 5000);
--+	ieee80211_start_tx_ba_session(pubsta, tid, 0);
+---- a/net/wireless/util.c
+-+++ b/net/wireless/util.c
+-@@ -644,73 +644,75 @@ int ieee80211_data_from_8023(struct sk_b
 - }
+- EXPORT_SYMBOL(ieee80211_data_from_8023);
 - 
-- static void
-diff --git a/package/kernel/mac80211/patches/337-ath9k-Switch-to-using-mac80211-intermediate-software.patch b/package/kernel/mac80211/patches/337-ath9k-Switch-to-using-mac80211-intermediate-software.patch
-new file mode 100644
-index 0000000..adfd6df
---- /dev/null
-+++ b/package/kernel/mac80211/patches/337-ath9k-Switch-to-using-mac80211-intermediate-software.patch
-@@ -0,0 +1,951 @@
-+From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk>
-+Date: Wed, 6 Jul 2016 21:34:17 +0200
-+Subject: [PATCH] ath9k: Switch to using mac80211 intermediate software queues.
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
-+
-+This switches ath9k over to using the mac80211 intermediate software
-+queueing mechanism for data packets. It removes the queueing inside the
-+driver, except for the retry queue, and instead pulls from mac80211 when
-+a packet is needed. The retry queue is used to store a packet that was
-+pulled but can't be sent immediately.
-+
-+The old code path in ath_tx_start that would queue packets has been
-+removed completely, as has the qlen limit tunables (since there's no
-+longer a queue in the driver to limit).
-+
-+Based on Tim's original patch set, but reworked quite thoroughly.
-+
-+Cc: Tim Shepard <shep@alum.mit.edu>
-+Cc: Felix Fietkau <nbd@nbd.name>
-+Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
-++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
-+@@ -91,7 +91,6 @@ int ath_descdma_setup(struct ath_softc *
-+ #define ATH_RXBUF               512
-+ #define ATH_TXBUF               512
-+ #define ATH_TXBUF_RESERVE       5
-+-#define ATH_MAX_QDEPTH          (ATH_TXBUF / 4 - ATH_TXBUF_RESERVE)
-+ #define ATH_TXMAXTRY            13
-+ #define ATH_MAX_SW_RETRIES      30
-+ 
-+@@ -145,7 +144,7 @@ int ath_descdma_setup(struct ath_softc *
-+ #define BAW_WITHIN(_start, _bawsz, _seqno) \
-+ 	((((_seqno) - (_start)) & 4095) < (_bawsz))
-+ 
-+-#define ATH_AN_2_TID(_an, _tidno)  (&(_an)->tid[(_tidno)])
-++#define ATH_AN_2_TID(_an, _tidno) ath_node_to_tid(_an, _tidno)
-+ 
-+ #define IS_HT_RATE(rate)   (rate & 0x80)
-+ #define IS_CCK_RATE(rate)  ((rate >= 0x18) && (rate <= 0x1e))
-+@@ -164,7 +163,6 @@ struct ath_txq {
-+ 	spinlock_t axq_lock;
-+ 	u32 axq_depth;
-+ 	u32 axq_ampdu_depth;
-+-	bool stopped;
-+ 	bool axq_tx_inprogress;
-+ 	struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
-+ 	u8 txq_headidx;
-+@@ -232,7 +230,6 @@ struct ath_buf {
-+ 
-+ struct ath_atx_tid {
-+ 	struct list_head list;
-+-	struct sk_buff_head buf_q;
-+ 	struct sk_buff_head retry_q;
-+ 	struct ath_node *an;
-+ 	struct ath_txq *txq;
-+@@ -247,13 +244,13 @@ struct ath_atx_tid {
-+ 	s8 bar_index;
-+ 	bool active;
-+ 	bool clear_ps_filter;
-++	bool has_queued;
-+ };
-+ 
-+ struct ath_node {
-+ 	struct ath_softc *sc;
-+ 	struct ieee80211_sta *sta; /* station struct we're part of */
-+ 	struct ieee80211_vif *vif; /* interface with which we're associated */
-+-	struct ath_atx_tid tid[IEEE80211_NUM_TIDS];
-+ 
-+ 	u16 maxampdu;
-+ 	u8 mpdudensity;
-+@@ -276,7 +273,6 @@ struct ath_tx_control {
-+ 	struct ath_node *an;
-+ 	struct ieee80211_sta *sta;
-+ 	u8 paprd;
-+-	bool force_channel;
-+ };
-+ 
-+ 
-+@@ -293,7 +289,6 @@ struct ath_tx {
-+ 	struct ath_descdma txdma;
-+ 	struct ath_txq *txq_map[IEEE80211_NUM_ACS];
-+ 	struct ath_txq *uapsdq;
-+-	u32 txq_max_pending[IEEE80211_NUM_ACS];
-+ 	u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32];
-+ };
-+ 
-+@@ -421,6 +416,22 @@ struct ath_offchannel {
-+ 	int duration;
-+ };
-+ 
-++static inline struct ath_atx_tid *
-++ath_node_to_tid(struct ath_node *an, u8 tidno)
-++{
-++	struct ieee80211_sta *sta = an->sta;
-++	struct ieee80211_vif *vif = an->vif;
-++	struct ieee80211_txq *txq;
-++
-++	BUG_ON(!vif);
-++	if (sta)
-++		txq = sta->txq[tidno % ARRAY_SIZE(sta->txq)];
-++	else
-++		txq = vif->txq;
-++
-++	return (struct ath_atx_tid *) txq->drv_priv;
-++}
-++
-+ #define case_rtn_string(val) case val: return #val
-+ 
-+ #define ath_for_each_chanctx(_sc, _ctx)                             \
-+@@ -575,7 +586,6 @@ void ath_tx_edma_tasklet(struct ath_soft
-+ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
-+ 		      u16 tid, u16 *ssn);
-+ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
-+-void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid);
-+ 
-+ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an);
-+ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
-+@@ -585,6 +595,7 @@ void ath9k_release_buffered_frames(struc
-+ 				   u16 tids, int nframes,
-+ 				   enum ieee80211_frame_release_type reason,
-+ 				   bool more_data);
-++void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue);
-+ 
-+ /********/
-+ /* VIFs */
-+--- a/drivers/net/wireless/ath/ath9k/channel.c
-++++ b/drivers/net/wireless/ath/ath9k/channel.c
-+@@ -1007,7 +1007,6 @@ static void ath_scan_send_probe(struct a
-+ 		goto error;
-+ 
-+ 	txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
-+-	txctl.force_channel = true;
-+ 	if (ath_tx_start(sc->hw, skb, &txctl))
-+ 		goto error;
+-+static struct sk_buff *
+-+__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
+-+		       int offset, int len)
+-+{
+-+	struct sk_buff *frame;
+-+
+-+	if (skb->len - offset < len)
+-+		return NULL;
+-+
+-+	/*
+-+	 * Allocate and reserve two bytes more for payload
+-+	 * alignment since sizeof(struct ethhdr) is 14.
+-+	 */
+-+	frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len);
+-+
+-+	skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
+-+	skb_copy_bits(skb, offset, skb_put(frame, len), len);
+-+
+-+	return frame;
+-+}
+- 
+- void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
+- 			      const u8 *addr, enum nl80211_iftype iftype,
+- 			      const unsigned int extra_headroom,
+- 			      bool has_80211_header)
+- {
+-+	unsigned int hlen = ALIGN(extra_headroom, 4);
+- 	struct sk_buff *frame = NULL;
+- 	u16 ethertype;
+- 	u8 *payload;
+--	const struct ethhdr *eth;
+--	int remaining, err;
+--	u8 dst[ETH_ALEN], src[ETH_ALEN];
+--
+--	if (skb_linearize(skb))
+--		goto out;
+-+	int offset = 0, remaining, err;
+-+	struct ethhdr eth;
+-+	bool reuse_skb = true;
+-+	bool last = false;
+- 
+- 	if (has_80211_header) {
+--		err = ieee80211_data_to_8023(skb, addr, iftype);
+-+		err = __ieee80211_data_to_8023(skb, &eth, addr, iftype);
+- 		if (err)
+- 			goto out;
+--
+--		/* skip the wrapping header */
+--		eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
+--		if (!eth)
+--			goto out;
+--	} else {
+--		eth = (struct ethhdr *) skb->data;
+- 	}
+- 
+--	while (skb != frame) {
+-+	while (!last) {
+-+		unsigned int subframe_len;
+-+		int len;
+- 		u8 padding;
+--		__be16 len = eth->h_proto;
+--		unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
+--
+--		remaining = skb->len;
+--		memcpy(dst, eth->h_dest, ETH_ALEN);
+--		memcpy(src, eth->h_source, ETH_ALEN);
+- 
+-+		skb_copy_bits(skb, offset, &eth, sizeof(eth));
+-+		len = ntohs(eth.h_proto);
+-+		subframe_len = sizeof(struct ethhdr) + len;
+- 		padding = (4 - subframe_len) & 0x3;
+-+
+- 		/* the last MSDU has no padding */
+-+		remaining = skb->len - offset;
+- 		if (subframe_len > remaining)
+- 			goto purge;
+- 
+--		skb_pull(skb, sizeof(struct ethhdr));
+-+		offset += sizeof(struct ethhdr);
+- 		/* reuse skb for the last subframe */
+--		if (remaining <= subframe_len + padding)
+-+		last = remaining <= subframe_len + padding;
+-+		if (!skb_is_nonlinear(skb) && last) {
+-+			skb_pull(skb, offset);
+- 			frame = skb;
+--		else {
+--			unsigned int hlen = ALIGN(extra_headroom, 4);
+--			/*
+--			 * Allocate and reserve two bytes more for payload
+--			 * alignment since sizeof(struct ethhdr) is 14.
+--			 */
+--			frame = dev_alloc_skb(hlen + subframe_len + 2);
+-+			reuse_skb = true;
+-+		} else {
+-+			frame = __ieee80211_amsdu_copy(skb, hlen, offset, len);
+- 			if (!frame)
+- 				goto purge;
+- 
+--			skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
+--			memcpy(skb_put(frame, ntohs(len)), skb->data,
+--				ntohs(len));
+--
+--			eth = (struct ethhdr *)skb_pull(skb, ntohs(len) +
+--							padding);
+--			if (!eth) {
+--				dev_kfree_skb(frame);
+--				goto purge;
+--			}
+-+			offset += len + padding;
+- 		}
+- 
+- 		skb_reset_network_header(frame);
+-@@ -719,24 +721,20 @@ void ieee80211_amsdu_to_8023s(struct sk_
+- 
+- 		payload = frame->data;
+- 		ethertype = (payload[6] << 8) | payload[7];
+--
+- 		if (likely((ether_addr_equal(payload, rfc1042_header) &&
+- 			    ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
+- 			   ether_addr_equal(payload, bridge_tunnel_header))) {
+--			/* remove RFC1042 or Bridge-Tunnel
+--			 * encapsulation and replace EtherType */
+--			skb_pull(frame, 6);
+--			memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
+--			memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
+--		} else {
+--			memcpy(skb_push(frame, sizeof(__be16)), &len,
+--				sizeof(__be16));
+--			memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
+--			memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
+-+			eth.h_proto = htons(ethertype);
+-+			skb_pull(frame, ETH_ALEN + 2);
+- 		}
+-+
+-+		memcpy(skb_push(frame, sizeof(eth)), &eth, sizeof(eth));
+- 		__skb_queue_tail(list, frame);
+- 	}
+- 
+-+	if (!reuse_skb)
+-+		dev_kfree_skb(skb);
+-+
+- 	return;
+- 
+-  purge:
+diff --git a/package/kernel/mac80211/patches/321-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch b/package/kernel/mac80211/patches/321-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch
+new file mode 100644
+index 0000000..9caa76d
+--- /dev/null
++++ b/package/kernel/mac80211/patches/321-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch
+@@ -0,0 +1,25 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Sat, 9 Jul 2016 15:25:24 +0200
++Subject: [PATCH] ath9k_hw: reset AHB-WMAC interface on AR91xx
++
++Should fix a few stability issues
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/wireless/ath/ath9k/hw.c
+++++ b/drivers/net/wireless/ath/ath9k/hw.c
++@@ -1398,8 +1398,12 @@ static bool ath9k_hw_set_reset(struct at
++ 	if (!AR_SREV_9100(ah))
++ 		REG_WRITE(ah, AR_RC, 0);
 + 
-+@@ -1130,7 +1129,6 @@ ath_chanctx_send_vif_ps_frame(struct ath
-+ 	memset(&txctl, 0, sizeof(txctl));
-+ 	txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
-+ 	txctl.sta = sta;
-+-	txctl.force_channel = true;
-+ 	if (ath_tx_start(sc->hw, skb, &txctl)) {
-+ 		ieee80211_free_txskb(sc->hw, skb);
-+ 		return false;
-+--- a/drivers/net/wireless/ath/ath9k/debug.c
-++++ b/drivers/net/wireless/ath/ath9k/debug.c
-+@@ -600,7 +600,6 @@ static int read_file_xmit(struct seq_fil
-+ 	PR("MPDUs XRetried:  ", xretries);
-+ 	PR("Aggregates:      ", a_aggr);
-+ 	PR("AMPDUs Queued HW:", a_queued_hw);
-+-	PR("AMPDUs Queued SW:", a_queued_sw);
-+ 	PR("AMPDUs Completed:", a_completed);
-+ 	PR("AMPDUs Retried:  ", a_retries);
-+ 	PR("AMPDUs XRetried: ", a_xretries);
-+@@ -629,8 +628,7 @@ static void print_queue(struct ath_softc
-+ 	seq_printf(file, "%s: %d ", "qnum", txq->axq_qnum);
-+ 	seq_printf(file, "%s: %2d ", "qdepth", txq->axq_depth);
-+ 	seq_printf(file, "%s: %2d ", "ampdu-depth", txq->axq_ampdu_depth);
-+-	seq_printf(file, "%s: %3d ", "pending", txq->pending_frames);
-+-	seq_printf(file, "%s: %d\n", "stopped", txq->stopped);
-++	seq_printf(file, "%s: %3d\n", "pending", txq->pending_frames);
++-	if (AR_SREV_9100(ah))
+++	if (AR_SREV_9100(ah)) {
+++		/* Reset the AHB-WMAC interface */
+++		if (ah->external_reset)
+++			ah->external_reset();
++ 		udelay(50);
+++	}
 + 
-+ 	ath_txq_unlock(sc, txq);
++ 	return true;
 + }
-+@@ -1208,7 +1206,6 @@ static const char ath9k_gstrings_stats[]
-+ 	AMKSTR(d_tx_mpdu_xretries),
-+ 	AMKSTR(d_tx_aggregates),
-+ 	AMKSTR(d_tx_ampdus_queued_hw),
-+-	AMKSTR(d_tx_ampdus_queued_sw),
-+ 	AMKSTR(d_tx_ampdus_completed),
-+ 	AMKSTR(d_tx_ampdu_retries),
-+ 	AMKSTR(d_tx_ampdu_xretries),
-+@@ -1288,7 +1285,6 @@ void ath9k_get_et_stats(struct ieee80211
-+ 	AWDATA(xretries);
-+ 	AWDATA(a_aggr);
-+ 	AWDATA(a_queued_hw);
-+-	AWDATA(a_queued_sw);
-+ 	AWDATA(a_completed);
-+ 	AWDATA(a_retries);
-+ 	AWDATA(a_xretries);
-+@@ -1346,14 +1342,6 @@ int ath9k_init_debug(struct ath_hw *ah)
-+ 				    read_file_xmit);
-+ 	debugfs_create_devm_seqfile(sc->dev, "queues", sc->debug.debugfs_phy,
-+ 				    read_file_queues);
-+-	debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
-+-			   &sc->tx.txq_max_pending[IEEE80211_AC_BK]);
-+-	debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
-+-			   &sc->tx.txq_max_pending[IEEE80211_AC_BE]);
-+-	debugfs_create_u32("qlen_vi", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
-+-			   &sc->tx.txq_max_pending[IEEE80211_AC_VI]);
-+-	debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
-+-			   &sc->tx.txq_max_pending[IEEE80211_AC_VO]);
-+ 	debugfs_create_devm_seqfile(sc->dev, "misc", sc->debug.debugfs_phy,
-+ 				    read_file_misc);
-+ 	debugfs_create_devm_seqfile(sc->dev, "reset", sc->debug.debugfs_phy,
-+--- a/drivers/net/wireless/ath/ath9k/debug.h
-++++ b/drivers/net/wireless/ath/ath9k/debug.h
-+@@ -147,7 +147,6 @@ struct ath_interrupt_stats {
-+  * @completed: Total MPDUs (non-aggr) completed
-+  * @a_aggr: Total no. of aggregates queued
-+  * @a_queued_hw: Total AMPDUs queued to hardware
-+- * @a_queued_sw: Total AMPDUs queued to software queues
-+  * @a_completed: Total AMPDUs completed
-+  * @a_retries: No. of AMPDUs retried (SW)
-+  * @a_xretries: No. of AMPDUs dropped due to xretries
-+@@ -174,7 +173,6 @@ struct ath_tx_stats {
-+ 	u32 xretries;
-+ 	u32 a_aggr;
-+ 	u32 a_queued_hw;
-+-	u32 a_queued_sw;
-+ 	u32 a_completed;
-+ 	u32 a_retries;
-+ 	u32 a_xretries;
-+--- a/drivers/net/wireless/ath/ath9k/debug_sta.c
-++++ b/drivers/net/wireless/ath/ath9k/debug_sta.c
-+@@ -52,8 +52,8 @@ static ssize_t read_file_node_aggr(struc
-+ 			 "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE",
-+ 			 "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED");
-+ 
-+-	for (tidno = 0, tid = &an->tid[tidno];
-+-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
-++	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
-++		tid = ath_node_to_tid(an, tidno);
-+ 		txq = tid->txq;
-+ 		ath_txq_lock(sc, txq);
-+ 		if (tid->active) {
-+--- a/drivers/net/wireless/ath/ath9k/init.c
-++++ b/drivers/net/wireless/ath/ath9k/init.c
-+@@ -358,7 +358,6 @@ static int ath9k_init_queues(struct ath_
-+ 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
-+ 		sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i);
-+ 		sc->tx.txq_map[i]->mac80211_qnum = i;
-+-		sc->tx.txq_max_pending[i] = ATH_MAX_QDEPTH;
-+ 	}
-+ 	return 0;
+diff --git a/package/kernel/mac80211/patches/321-mac80211-Parse-legacy-and-HT-rate-in-injected-frames.patch b/package/kernel/mac80211/patches/321-mac80211-Parse-legacy-and-HT-rate-in-injected-frames.patch
+deleted file mode 100644
+index c4155a1..0000000
+--- a/package/kernel/mac80211/patches/321-mac80211-Parse-legacy-and-HT-rate-in-injected-frames.patch
++++ /dev/null
+@@ -1,155 +0,0 @@
+-From: Sven Eckelmann <sven@narfation.org>
+-Date: Tue, 26 Jan 2016 17:11:13 +0100
+-Subject: [PATCH] mac80211: Parse legacy and HT rate in injected frames
+-
+-Drivers/devices without their own rate control algorithm can get the
+-information what rates they should use from either the radiotap header of
+-injected frames or from the rate control algorithm. But the parsing of the
+-legacy rate information from the radiotap header was removed in commit
+-e6a9854b05c1 ("mac80211/drivers: rewrite the rate control API").
+-
+-The removal of this feature heavily reduced the usefulness of frame
+-injection when wanting to simulate specific transmission behavior. Having
+-rate parsing together with MCS rates and retry support allows a fine
+-grained selection of the tx behavior of injected frames for these kind of
+-tests.
+-
+-Signed-off-by: Sven Eckelmann <sven@narfation.org>
+-Cc: Simon Wunderlich <sw@simonwunderlich.de>
+-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+----
+-
+---- a/include/net/mac80211.h
+-+++ b/include/net/mac80211.h
+-@@ -708,12 +708,14 @@ enum mac80211_tx_info_flags {
+-  *	protocol frame (e.g. EAP)
+-  * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
+-  *	frame (PS-Poll or uAPSD).
+-+ * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
+-  *
+-  * These flags are used in tx_info->control.flags.
+-  */
+- enum mac80211_tx_control_flags {
+- 	IEEE80211_TX_CTRL_PORT_CTRL_PROTO	= BIT(0),
+- 	IEEE80211_TX_CTRL_PS_RESPONSE		= BIT(1),
+-+	IEEE80211_TX_CTRL_RATE_INJECT		= BIT(2),
+- };
+- 
+- /*
+---- a/net/mac80211/tx.c
+-+++ b/net/mac80211/tx.c
+-@@ -710,6 +710,10 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021
+- 
+- 	info->control.short_preamble = txrc.short_preamble;
+- 
+-+	/* don't ask rate control when rate already injected via radiotap */
+-+	if (info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)
+-+		return TX_CONTINUE;
+-+
+- 	if (tx->sta)
+- 		assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC);
+- 
+-@@ -1665,15 +1669,24 @@ void ieee80211_xmit(struct ieee80211_sub
+- 	ieee80211_tx(sdata, sta, skb, false);
+- }
+- 
+--static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
+-+static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
+-+					struct sk_buff *skb)
+- {
+- 	struct ieee80211_radiotap_iterator iterator;
+- 	struct ieee80211_radiotap_header *rthdr =
+- 		(struct ieee80211_radiotap_header *) skb->data;
+- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+-+	struct ieee80211_supported_band *sband =
+-+		local->hw.wiphy->bands[info->band];
+- 	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
+- 						   NULL);
+- 	u16 txflags;
+-+	u16 rate = 0;
+-+	bool rate_found = false;
+-+	u8 rate_retries = 0;
+-+	u16 rate_flags = 0;
+-+	u8 mcs_known, mcs_flags;
+-+	int i;
+- 
+- 	info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
+- 		       IEEE80211_TX_CTL_DONTFRAG;
+-@@ -1724,6 +1737,35 @@ static bool ieee80211_parse_tx_radiotap(
+- 				info->flags |= IEEE80211_TX_CTL_NO_ACK;
+- 			break;
+- 
+-+		case IEEE80211_RADIOTAP_RATE:
+-+			rate = *iterator.this_arg;
+-+			rate_flags = 0;
+-+			rate_found = true;
+-+			break;
+-+
+-+		case IEEE80211_RADIOTAP_DATA_RETRIES:
+-+			rate_retries = *iterator.this_arg;
+-+			break;
+-+
+-+		case IEEE80211_RADIOTAP_MCS:
+-+			mcs_known = iterator.this_arg[0];
+-+			mcs_flags = iterator.this_arg[1];
+-+			if (!(mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_MCS))
+-+				break;
+-+
+-+			rate_found = true;
+-+			rate = iterator.this_arg[2];
+-+			rate_flags = IEEE80211_TX_RC_MCS;
+-+
+-+			if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_GI &&
+-+			    mcs_flags & IEEE80211_RADIOTAP_MCS_SGI)
+-+				rate_flags |= IEEE80211_TX_RC_SHORT_GI;
+-+
+-+			if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW &&
+-+			    mcs_flags & IEEE80211_RADIOTAP_MCS_BW_40)
+-+				rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+-+			break;
+-+
+- 		/*
+- 		 * Please update the file
+- 		 * Documentation/networking/mac80211-injection.txt
+-@@ -1738,6 +1780,32 @@ static bool ieee80211_parse_tx_radiotap(
+- 	if (ret != -ENOENT) /* ie, if we didn't simply run out of fields */
+- 		return false;
+- 
+-+	if (rate_found) {
+-+		info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT;
+-+
+-+		for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+-+			info->control.rates[i].idx = -1;
+-+			info->control.rates[i].flags = 0;
+-+			info->control.rates[i].count = 0;
+-+		}
+-+
+-+		if (rate_flags & IEEE80211_TX_RC_MCS) {
+-+			info->control.rates[0].idx = rate;
+-+		} else {
+-+			for (i = 0; i < sband->n_bitrates; i++) {
+-+				if (rate * 5 != sband->bitrates[i].bitrate)
+-+					continue;
+-+
+-+				info->control.rates[0].idx = i;
+-+				break;
+-+			}
+-+		}
+-+
+-+		info->control.rates[0].flags = rate_flags;
+-+		info->control.rates[0].count = min_t(u8, rate_retries + 1,
+-+						     local->hw.max_rate_tries);
+-+	}
+-+
+- 	/*
+- 	 * remove the radiotap header
+- 	 * iterator->_max_length was sanity-checked against
+-@@ -1819,7 +1887,7 @@ netdev_tx_t ieee80211_monitor_start_xmit
+- 		      IEEE80211_TX_CTL_INJECTED;
+- 
+- 	/* process and remove the injection radiotap header */
+--	if (!ieee80211_parse_tx_radiotap(skb))
+-+	if (!ieee80211_parse_tx_radiotap(local, skb))
+- 		goto fail;
+- 
+- 	rcu_read_lock();
+diff --git a/package/kernel/mac80211/patches/322-ath9k_hw-issue-external-reset-for-QCA9550.patch b/package/kernel/mac80211/patches/322-ath9k_hw-issue-external-reset-for-QCA9550.patch
+new file mode 100644
+index 0000000..5d4e849
+--- /dev/null
++++ b/package/kernel/mac80211/patches/322-ath9k_hw-issue-external-reset-for-QCA9550.patch
+@@ -0,0 +1,125 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Sat, 9 Jul 2016 15:26:44 +0200
++Subject: [PATCH] ath9k_hw: issue external reset for QCA9550
++
++The RTC interface on the SoC needs to be reset along with the rest of
++the WMAC.
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/wireless/ath/ath9k/hw.c
+++++ b/drivers/net/wireless/ath/ath9k/hw.c
++@@ -1275,39 +1275,56 @@ void ath9k_hw_get_delta_slope_vals(struc
++ 	*coef_exponent = coef_exp - 16;
 + }
-+@@ -873,6 +872,7 @@ static void ath9k_set_hw_capab(struct at
-+ 	hw->max_rate_tries = 10;
-+ 	hw->sta_data_size = sizeof(struct ath_node);
-+ 	hw->vif_data_size = sizeof(struct ath_vif);
-++	hw->txq_data_size = sizeof(struct ath_atx_tid);
-+ 	hw->extra_tx_headroom = 4;
-+ 
-+ 	hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1;
-+--- a/drivers/net/wireless/ath/ath9k/main.c
-++++ b/drivers/net/wireless/ath/ath9k/main.c
-+@@ -1897,9 +1897,11 @@ static int ath9k_ampdu_action(struct iee
-+ 	bool flush = false;
-+ 	int ret = 0;
-+ 	struct ieee80211_sta *sta = params->sta;
-++	struct ath_node *an = (struct ath_node *)sta->drv_priv;
-+ 	enum ieee80211_ampdu_mlme_action action = params->action;
-+ 	u16 tid = params->tid;
-+ 	u16 *ssn = &params->ssn;
-++	struct ath_atx_tid *atid;
-+ 
-+ 	mutex_lock(&sc->mutex);
-+ 
-+@@ -1932,9 +1934,9 @@ static int ath9k_ampdu_action(struct iee
-+ 		ath9k_ps_restore(sc);
-+ 		break;
-+ 	case IEEE80211_AMPDU_TX_OPERATIONAL:
-+-		ath9k_ps_wakeup(sc);
-+-		ath_tx_aggr_resume(sc, sta, tid);
-+-		ath9k_ps_restore(sc);
-++		atid = ath_node_to_tid(an, tid);
-++		atid->baw_size = IEEE80211_MIN_AMPDU_BUF <<
-++			        sta->ht_cap.ampdu_factor;
-+ 		break;
-+ 	default:
-+ 		ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n");
-+@@ -2696,4 +2698,5 @@ struct ieee80211_ops ath9k_ops = {
-+ 	.sw_scan_start	    = ath9k_sw_scan_start,
-+ 	.sw_scan_complete   = ath9k_sw_scan_complete,
-+ 	.get_txpower        = ath9k_get_txpower,
-++	.wake_tx_queue      = ath9k_wake_tx_queue,
-+ };
-+--- a/drivers/net/wireless/ath/ath9k/xmit.c
-++++ b/drivers/net/wireless/ath/ath9k/xmit.c
-+@@ -67,6 +67,8 @@ static struct ath_buf *ath_tx_setup_buff
-+ 					   struct ath_txq *txq,
-+ 					   struct ath_atx_tid *tid,
-+ 					   struct sk_buff *skb);
-++static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb,
-++			  struct ath_tx_control *txctl);
 + 
-+ enum {
-+ 	MCS_HT20,
-+@@ -137,6 +139,26 @@ static void ath_tx_queue_tid(struct ath_
-+ 		list_add_tail(&tid->list, list);
-+ }
++-/* AR9330 WAR:
++- * call external reset function to reset WMAC if:
++- * - doing a cold reset
++- * - we have pending frames in the TX queues.
++- */
++-static bool ath9k_hw_ar9330_reset_war(struct ath_hw *ah, int type)
+++static bool ath9k_hw_need_external_reset(struct ath_hw *ah, int type)
++ {
++-	int i, npend = 0;
+++	int i;
 + 
-++void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *queue)
-++{
-++	struct ath_softc *sc = hw->priv;
-++	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-++	struct ath_atx_tid *tid = (struct ath_atx_tid *) queue->drv_priv;
-++	struct ath_txq *txq = tid->txq;
-++
-++	ath_dbg(common, QUEUE, "Waking TX queue: %pM (%d)\n",
-++		queue->sta ? queue->sta->addr : queue->vif->addr,
-++		tid->tidno);
-++
-++	ath_txq_lock(sc, txq);
-++
-++	tid->has_queued = true;
-++	ath_tx_queue_tid(sc, txq, tid);
-++	ath_txq_schedule(sc, txq);
++-	for (i = 0; i < AR_NUM_QCU; i++) {
++-		npend = ath9k_hw_numtxpending(ah, i);
++-		if (npend)
++-			break;
+++	if (type == ATH9K_RESET_COLD)
+++		return true;
 ++
-++	ath_txq_unlock(sc, txq);
-++}
+++	if (AR_SREV_9550(ah))
+++		return true;
 ++
-+ static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
-+ {
-+ 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-+@@ -179,7 +201,6 @@ static void ath_set_rates(struct ieee802
-+ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
-+ 			     struct sk_buff *skb)
-+ {
-+-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+ 	struct ath_frame_info *fi = get_frame_info(skb);
-+ 	int q = fi->txq;
-+ 
-+@@ -190,14 +211,6 @@ static void ath_txq_skb_done(struct ath_
-+ 	if (WARN_ON(--txq->pending_frames < 0))
-+ 		txq->pending_frames = 0;
-+ 
-+-	if (txq->stopped &&
-+-	    txq->pending_frames < sc->tx.txq_max_pending[q]) {
-+-		if (ath9k_is_chanctx_enabled())
-+-			ieee80211_wake_queue(sc->hw, info->hw_queue);
-+-		else
-+-			ieee80211_wake_queue(sc->hw, q);
-+-		txq->stopped = false;
-+-	}
-+ }
+++	/* AR9330 WAR:
+++	 * call external reset function to reset WMAC if:
+++	 * - doing a cold reset
+++	 * - we have pending frames in the TX queues.
+++	 */
+++	if (AR_SREV_9330(ah)) {
+++		for (i = 0; i < AR_NUM_QCU; i++) {
+++			if (ath9k_hw_numtxpending(ah, i))
+++				return true;
+++		}
++ 	}
 + 
-+ static struct ath_atx_tid *
-+@@ -207,9 +220,48 @@ ath_get_skb_tid(struct ath_softc *sc, st
-+ 	return ATH_AN_2_TID(an, tidno);
-+ }
++-	if (ah->external_reset &&
++-	    (npend || type == ATH9K_RESET_COLD)) {
++-		int reset_err = 0;
+++	return false;
+++}
 + 
-++static struct sk_buff *
-++ath_tid_pull(struct ath_atx_tid *tid)
++-		ath_dbg(ath9k_hw_common(ah), RESET,
++-			"reset MAC via external reset\n");
+++static bool ath9k_hw_external_reset(struct ath_hw *ah, int type)
 ++{
-++	struct ieee80211_txq *txq = container_of((void*)tid, struct ieee80211_txq, drv_priv);
-++	struct ath_softc *sc = tid->an->sc;
-++	struct ieee80211_hw *hw = sc->hw;
-++	struct ath_tx_control txctl = {
-++		.txq = tid->txq,
-++		.sta = tid->an->sta,
-++	};
-++	struct sk_buff *skb;
-++	struct ath_frame_info *fi;
-++	int q;
-++
-++	if (!tid->has_queued)
-++		return NULL;
-++
-++	skb = ieee80211_tx_dequeue(hw, txq);
-++	if (!skb) {
-++		tid->has_queued = false;
-++		return NULL;
-++	}
-++
-++	if (ath_tx_prepare(hw, skb, &txctl)) {
-++		ieee80211_free_txskb(hw, skb);
-++		return NULL;
-++	}
+++	int err;
++ 
++-		reset_err = ah->external_reset();
++-		if (reset_err) {
++-			ath_err(ath9k_hw_common(ah),
++-				"External reset failed, err=%d\n",
++-				reset_err);
++-			return false;
++-		}
+++	if (!ah->external_reset || !ath9k_hw_need_external_reset(ah, type))
+++		return true;
++ 
++-		REG_WRITE(ah, AR_RTC_RESET, 1);
+++	ath_dbg(ath9k_hw_common(ah), RESET,
+++		"reset MAC via external reset\n");
 ++
-++	q = skb_get_queue_mapping(skb);
-++	if (tid->txq == sc->tx.txq_map[q]) {
-++		fi = get_frame_info(skb);
-++		fi->txq = q;
-++		++tid->txq->pending_frames;
+++	err = ah->external_reset();
+++	if (err) {
+++		ath_err(ath9k_hw_common(ah),
+++			"External reset failed, err=%d\n", err);
+++		return false;
 ++	}
 ++
-++	return skb;
-++ }
-++
+++	if (AR_SREV_9550(ah)) {
+++		REG_WRITE(ah, AR_RTC_RESET, 0);
+++		udelay(10);
++ 	}
++ 
+++	REG_WRITE(ah, AR_RTC_RESET, 1);
+++	udelay(10);
 ++
-+ static bool ath_tid_has_buffered(struct ath_atx_tid *tid)
-+ {
-+-	return !skb_queue_empty(&tid->buf_q) || !skb_queue_empty(&tid->retry_q);
-++	return !skb_queue_empty(&tid->retry_q) || tid->has_queued;
++ 	return true;
 + }
 + 
-+ static struct sk_buff *ath_tid_dequeue(struct ath_atx_tid *tid)
-+@@ -218,46 +270,11 @@ static struct sk_buff *ath_tid_dequeue(s
-+ 
-+ 	skb = __skb_dequeue(&tid->retry_q);
-+ 	if (!skb)
-+-		skb = __skb_dequeue(&tid->buf_q);
-++		skb = ath_tid_pull(tid);
-+ 
-+ 	return skb;
-+ }
++@@ -1360,24 +1377,23 @@ static bool ath9k_hw_set_reset(struct at
++ 			rst_flags |= AR_RTC_RC_MAC_COLD;
++ 	}
 + 
-+-/*
-+- * ath_tx_tid_change_state:
-+- * - clears a-mpdu flag of previous session
-+- * - force sequence number allocation to fix next BlockAck Window
-+- */
-+-static void
-+-ath_tx_tid_change_state(struct ath_softc *sc, struct ath_atx_tid *tid)
-+-{
-+-	struct ath_txq *txq = tid->txq;
-+-	struct ieee80211_tx_info *tx_info;
-+-	struct sk_buff *skb, *tskb;
-+-	struct ath_buf *bf;
-+-	struct ath_frame_info *fi;
-+-
-+-	skb_queue_walk_safe(&tid->buf_q, skb, tskb) {
-+-		fi = get_frame_info(skb);
-+-		bf = fi->bf;
-+-
-+-		tx_info = IEEE80211_SKB_CB(skb);
-+-		tx_info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-+-
-+-		if (bf)
-+-			continue;
-+-
-+-		bf = ath_tx_setup_buffer(sc, txq, tid, skb);
-+-		if (!bf) {
-+-			__skb_unlink(skb, &tid->buf_q);
-+-			ath_txq_skb_done(sc, txq, skb);
-+-			ieee80211_free_txskb(sc->hw, skb);
-+-			continue;
-+-		}
++-	if (AR_SREV_9330(ah)) {
++-		if (!ath9k_hw_ar9330_reset_war(ah, type))
++-			return false;
 +-	}
 +-
-+-}
-+-
-+ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
-+ {
-+ 	struct ath_txq *txq = tid->txq;
-+@@ -898,20 +915,16 @@ static int ath_compute_num_delims(struct
++ 	if (ath9k_hw_mci_is_enabled(ah))
++ 		ar9003_mci_check_gpm_offset(ah);
 + 
-+ static struct ath_buf *
-+ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
-+-			struct ath_atx_tid *tid, struct sk_buff_head **q)
-++			struct ath_atx_tid *tid)
++ 	/* DMA HALT added to resolve ar9300 and ar9580 bus error during
++-	 * RTC_RC reg read
+++	 * RTC_RC reg read. Also needed for AR9550 external reset
++ 	 */
++-	if (AR_SREV_9300(ah) || AR_SREV_9580(ah)) {
+++	if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9550(ah)) {
++ 		REG_SET_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
++ 		ath9k_hw_wait(ah, AR_CFG, AR_CFG_HALT_ACK, AR_CFG_HALT_ACK,
++ 			      20 * AH_WAIT_TIMEOUT);
++-		REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
++ 	}
++ 
+++	ath9k_hw_external_reset(ah, type);
+++
+++	if (AR_SREV_9300(ah) || AR_SREV_9580(ah))
+++		REG_CLR_BIT(ah, AR_CFG, AR_CFG_HALT_REQ);
+++
++ 	REG_WRITE(ah, AR_RTC_RC, rst_flags);
++ 
++ 	REGWRITE_BUFFER_FLUSH(ah);
+diff --git a/package/kernel/mac80211/patches/322-mac80211-add-A-MSDU-tx-support.patch b/package/kernel/mac80211/patches/322-mac80211-add-A-MSDU-tx-support.patch
+deleted file mode 100644
+index e7bfb9c..0000000
+--- a/package/kernel/mac80211/patches/322-mac80211-add-A-MSDU-tx-support.patch
++++ /dev/null
+@@ -1,317 +0,0 @@
+-From: Felix Fietkau <nbd@openwrt.org>
+-Date: Fri, 5 Feb 2016 01:38:51 +0100
+-Subject: [PATCH] mac80211: add A-MSDU tx support
+-
+-Requires software tx queueing support. frag_list support (for zero-copy)
+-is optional.
+-
+-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+----
+-
+---- a/include/net/mac80211.h
+-+++ b/include/net/mac80211.h
+-@@ -709,6 +709,7 @@ enum mac80211_tx_info_flags {
+-  * @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
+-  *	frame (PS-Poll or uAPSD).
+-  * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
+-+ * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
+-  *
+-  * These flags are used in tx_info->control.flags.
+-  */
+-@@ -716,6 +717,7 @@ enum mac80211_tx_control_flags {
+- 	IEEE80211_TX_CTRL_PORT_CTRL_PROTO	= BIT(0),
+- 	IEEE80211_TX_CTRL_PS_RESPONSE		= BIT(1),
+- 	IEEE80211_TX_CTRL_RATE_INJECT		= BIT(2),
+-+	IEEE80211_TX_CTRL_AMSDU			= BIT(3),
+- };
+- 
+- /*
+-@@ -1728,6 +1730,7 @@ struct ieee80211_sta_rates {
+-  *		  size is min(max_amsdu_len, 7935) bytes.
+-  *	Both additional HT limits must be enforced by the low level driver.
+-  *	This is defined by the spec (IEEE 802.11-2012 section 8.3.2.2 NOTE 2).
+-+ * @max_rc_amsdu_len: Maximum A-MSDU size in bytes recommended by rate control.
+-  * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
+-  */
+- struct ieee80211_sta {
+-@@ -1748,6 +1751,7 @@ struct ieee80211_sta {
+- 	bool mfp;
+- 	u8 max_amsdu_subframes;
+- 	u16 max_amsdu_len;
+-+	u16 max_rc_amsdu_len;
+- 
+- 	struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
+- 
+-@@ -1961,6 +1965,15 @@ struct ieee80211_txq {
+-  *	order and does not need to manage its own reorder buffer or BA session
+-  *	timeout.
+-  *
+-+ * @IEEE80211_HW_TX_AMSDU: Hardware (or driver) supports software aggregated
+-+ *	A-MSDU frames. Requires software tx queueing and fast-xmit support.
+-+ *	When not using minstrel/minstrel_ht rate control, the driver should
+-+ *	limit the maximum A-MSDU size based on the current tx rate by setting
+-+ *	max_rc_amsdu_len in struct ieee80211_sta.
+-+ *
+-+ * @IEEE80211_HW_TX_FRAG_LIST: Hardware (or driver) supports sending frag_list
+-+ *	skbs, needed for zero-copy software A-MSDU.
+-+ *
+-  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
+-  */
+- enum ieee80211_hw_flags {
+-@@ -1998,6 +2011,8 @@ enum ieee80211_hw_flags {
+- 	IEEE80211_HW_BEACON_TX_STATUS,
+- 	IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
+- 	IEEE80211_HW_SUPPORTS_REORDERING_BUFFER,
+-+	IEEE80211_HW_TX_AMSDU,
+-+	IEEE80211_HW_TX_FRAG_LIST,
+- 
+- 	/* keep last, obviously */
+- 	NUM_IEEE80211_HW_FLAGS
+-@@ -2070,6 +2085,9 @@ enum ieee80211_hw_flags {
+-  *	size is smaller (an example is LinkSys WRT120N with FW v1.0.07
+-  *	build 002 Jun 18 2012).
+-  *
+-+ * @max_tx_fragments: maximum number of tx buffers per (A)-MSDU, sum
+-+ *	of 1 + skb_shinfo(skb)->nr_frags for each skb in the frag_list.
+-+ *
+-  * @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX
+-  *	(if %IEEE80211_HW_QUEUE_CONTROL is set)
+-  *
+-@@ -2124,6 +2142,7 @@ struct ieee80211_hw {
+- 	u8 max_rate_tries;
+- 	u8 max_rx_aggregation_subframes;
+- 	u8 max_tx_aggregation_subframes;
+-+	u8 max_tx_fragments;
+- 	u8 offchannel_tx_hw_queue;
+- 	u8 radiotap_mcs_details;
+- 	u16 radiotap_vht_details;
+---- a/net/mac80211/agg-tx.c
+-+++ b/net/mac80211/agg-tx.c
+-@@ -935,6 +935,7 @@ void ieee80211_process_addba_resp(struct
+- 				  size_t len)
+- {
+- 	struct tid_ampdu_tx *tid_tx;
+-+	struct ieee80211_txq *txq;
+- 	u16 capab, tid;
+- 	u8 buf_size;
+- 	bool amsdu;
+-@@ -945,6 +946,10 @@ void ieee80211_process_addba_resp(struct
+- 	buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
+- 	buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes);
+- 
+-+	txq = sta->sta.txq[tid];
+-+	if (!amsdu && txq)
+-+		set_bit(IEEE80211_TXQ_NO_AMSDU, &to_txq_info(txq)->flags);
+-+
+- 	mutex_lock(&sta->ampdu_mlme.mtx);
+- 
+- 	tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
+---- a/net/mac80211/debugfs.c
+-+++ b/net/mac80211/debugfs.c
+-@@ -127,6 +127,8 @@ static const char *hw_flag_names[NUM_IEE
+- 	FLAG(BEACON_TX_STATUS),
+- 	FLAG(NEEDS_UNIQUE_STA_ADDR),
+- 	FLAG(SUPPORTS_REORDERING_BUFFER),
+-+	FLAG(TX_AMSDU),
+-+	FLAG(TX_FRAG_LIST),
+- 
+- 	/* keep last for the build bug below */
+- 	(void *)0x1
+---- a/net/mac80211/ieee80211_i.h
+-+++ b/net/mac80211/ieee80211_i.h
+-@@ -799,6 +799,7 @@ struct mac80211_qos_map {
+- enum txq_info_flags {
+- 	IEEE80211_TXQ_STOP,
+- 	IEEE80211_TXQ_AMPDU,
+-+	IEEE80211_TXQ_NO_AMSDU,
+- };
+- 
+- struct txq_info {
+---- a/net/mac80211/tx.c
+-+++ b/net/mac80211/tx.c
+-@@ -1318,6 +1318,10 @@ struct sk_buff *ieee80211_tx_dequeue(str
+- out:
+- 	spin_unlock_bh(&txqi->queue.lock);
+- 
+-+	if (skb && skb_has_frag_list(skb) &&
+-+	    !ieee80211_hw_check(&local->hw, TX_FRAG_LIST))
+-+		skb_linearize(skb);
+-+
+- 	return skb;
+- }
+- EXPORT_SYMBOL(ieee80211_tx_dequeue);
+-@@ -2757,6 +2761,163 @@ void ieee80211_clear_fast_xmit(struct st
+- 		kfree_rcu(fast_tx, rcu_head);
+- }
+- 
+-+static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local,
+-+					struct sk_buff *skb, int headroom,
+-+					int *subframe_len)
+-+{
+-+	int amsdu_len = *subframe_len + sizeof(struct ethhdr);
+-+	int padding = (4 - amsdu_len) & 3;
+-+
+-+	if (skb_headroom(skb) < headroom || skb_tailroom(skb) < padding) {
+-+		I802_DEBUG_INC(local->tx_expand_skb_head);
+-+
+-+		if (pskb_expand_head(skb, headroom, padding, GFP_ATOMIC)) {
+-+			wiphy_debug(local->hw.wiphy,
+-+				    "failed to reallocate TX buffer\n");
+-+			return false;
+-+		}
+-+	}
+-+
+-+	if (padding) {
+-+		*subframe_len += padding;
+-+		memset(skb_put(skb, padding), 0, padding);
+-+	}
+-+
+-+	return true;
+-+}
+-+
+-+static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata,
+-+					 struct ieee80211_fast_tx *fast_tx,
+-+					 struct sk_buff *skb)
+-+{
+-+	struct ieee80211_local *local = sdata->local;
+-+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+-+	struct ieee80211_hdr *hdr;
+-+	struct ethhdr amsdu_hdr;
+-+	int hdr_len = fast_tx->hdr_len - sizeof(rfc1042_header);
+-+	int subframe_len = skb->len - hdr_len;
+-+	void *data;
+-+	u8 *qc;
+-+
+-+	if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
+-+		return false;
+-+
+-+	if (info->control.flags & IEEE80211_TX_CTRL_AMSDU)
+-+		return true;
+-+
+-+	if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(amsdu_hdr),
+-+					 &subframe_len))
+-+		return false;
+-+
+-+	amsdu_hdr.h_proto = cpu_to_be16(subframe_len);
+-+	memcpy(amsdu_hdr.h_source, skb->data + fast_tx->sa_offs, ETH_ALEN);
+-+	memcpy(amsdu_hdr.h_dest, skb->data + fast_tx->da_offs, ETH_ALEN);
+-+
+-+	data = skb_push(skb, sizeof(amsdu_hdr));
+-+	memmove(data, data + sizeof(amsdu_hdr), hdr_len);
+-+	memcpy(data + hdr_len, &amsdu_hdr, sizeof(amsdu_hdr));
+-+
+-+	hdr = data;
+-+	qc = ieee80211_get_qos_ctl(hdr);
+-+	*qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+-+
+-+	info->control.flags |= IEEE80211_TX_CTRL_AMSDU;
+-+
+-+	return true;
+-+}
+-+
+-+static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
+-+				      struct sta_info *sta,
+-+				      struct ieee80211_fast_tx *fast_tx,
+-+				      struct sk_buff *skb)
+-+{
+-+	struct ieee80211_local *local = sdata->local;
+-+	u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+-+	struct ieee80211_txq *txq = sta->sta.txq[tid];
+-+	struct txq_info *txqi;
+-+	struct sk_buff **frag_tail, *head;
+-+	int subframe_len = skb->len - ETH_ALEN;
+-+	u8 max_subframes = sta->sta.max_amsdu_subframes;
+-+	int max_frags = local->hw.max_tx_fragments;
+-+	int max_amsdu_len = sta->sta.max_amsdu_len;
+-+	__be16 len;
+-+	void *data;
+-+	bool ret = false;
+-+	int n = 1, nfrags;
+-+
+-+	if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
+-+		return false;
+-+
+-+	if (!txq)
+-+		return false;
+-+
+-+	txqi = to_txq_info(txq);
+-+	if (test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags))
+-+		return false;
+-+
+-+	if (sta->sta.max_rc_amsdu_len)
+-+		max_amsdu_len = min_t(int, max_amsdu_len,
+-+				      sta->sta.max_rc_amsdu_len);
+-+
+-+	spin_lock_bh(&txqi->queue.lock);
+-+
+-+	head = skb_peek_tail(&txqi->queue);
+-+	if (!head)
+-+		goto out;
+-+
+-+	if (skb->len + head->len > max_amsdu_len)
+-+		goto out;
+-+
+-+	/*
+-+	 * HT A-MPDU limits maximum MPDU size to 4095 bytes. Since aggregation
+-+	 * sessions are started/stopped without txq flush, use the limit here
+-+	 * to avoid having to de-aggregate later.
+-+	 */
+-+	if (skb->len + head->len > 4095 &&
+-+	    !sta->sta.vht_cap.vht_supported)
+-+		goto out;
+-+
+-+	if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head))
+-+		goto out;
+-+
+-+	nfrags = 1 + skb_shinfo(skb)->nr_frags;
+-+	nfrags += 1 + skb_shinfo(head)->nr_frags;
+-+	frag_tail = &skb_shinfo(head)->frag_list;
+-+	while (*frag_tail) {
+-+		nfrags += 1 + skb_shinfo(*frag_tail)->nr_frags;
+-+		frag_tail = &(*frag_tail)->next;
+-+		n++;
+-+	}
+-+
+-+	if (max_subframes && n > max_subframes)
+-+		goto out;
+-+
+-+	if (max_frags && nfrags > max_frags)
+-+		goto out;
+-+
+-+	if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) + 2,
+-+					 &subframe_len))
+-+		return false;
+-+
+-+	ret = true;
+-+	data = skb_push(skb, ETH_ALEN + 2);
+-+	memmove(data, data + ETH_ALEN + 2, 2 * ETH_ALEN);
+-+
+-+	data += 2 * ETH_ALEN;
+-+	len = cpu_to_be16(subframe_len);
+-+	memcpy(data, &len, 2);
+-+	memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));
+-+
+-+	head->len += skb->len;
+-+	head->data_len += skb->len;
+-+	*frag_tail = skb;
+-+
+-+out:
+-+	spin_unlock_bh(&txqi->queue.lock);
+-+
+-+	return ret;
+-+}
+-+
+- static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
+- 				struct net_device *dev, struct sta_info *sta,
+- 				struct ieee80211_fast_tx *fast_tx,
+-@@ -2811,6 +2972,10 @@ static bool ieee80211_xmit_fast(struct i
+- 
+- 	ieee80211_tx_stats(dev, skb->len + extra_head);
+- 
+-+	if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
+-+	    ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
+-+		return true;
+-+
+- 	/* will not be crypto-handled beyond what we do here, so use false
+- 	 * as the may-encrypt argument for the resize to not account for
+- 	 * more room than we already have in 'extra_head'
+diff --git a/package/kernel/mac80211/patches/323-0000-brcmfmac-fix-setting-primary-channel-for-80-MHz-widt.patch b/package/kernel/mac80211/patches/323-0000-brcmfmac-fix-setting-primary-channel-for-80-MHz-widt.patch
+deleted file mode 100644
+index 9277b2c..0000000
+--- a/package/kernel/mac80211/patches/323-0000-brcmfmac-fix-setting-primary-channel-for-80-MHz-widt.patch
++++ /dev/null
+@@ -1,64 +0,0 @@
+-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+-Date: Wed, 20 Jan 2016 16:46:04 +0100
+-Subject: [PATCH] brcmfmac: fix setting primary channel for 80 MHz width
+-MIME-Version: 1.0
+-Content-Type: text/plain; charset=UTF-8
+-Content-Transfer-Encoding: 8bit
+-
+-First of all it changes the way we calculate primary channel offset. If
+-we use e.g. 80 MHz channel with primary frequency 5180 MHz (which means
+-center frequency is 5210 MHz) it makes sense to calculate primary offset
+-as -30 MHz.
+-Then it fixes values we compare primary_offset with. We were comparing
+-offset in MHz against -2 or 2 which was resulting in picking a wrong
+-primary channel.
+-
+-Signed-off-by: Rafał Miłecki <zajec5@gmail.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
+-@@ -247,7 +247,7 @@ static u16 chandef_to_chanspec(struct br
+- 	brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
+- 		  ch->chan->center_freq, ch->center_freq1, ch->width);
+- 	ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
+--	primary_offset = ch->center_freq1 - ch->chan->center_freq;
+-+	primary_offset = ch->chan->center_freq - ch->center_freq1;
+- 	switch (ch->width) {
+- 	case NL80211_CHAN_WIDTH_20:
+- 	case NL80211_CHAN_WIDTH_20_NOHT:
+-@@ -256,24 +256,21 @@ static u16 chandef_to_chanspec(struct br
+- 		break;
+- 	case NL80211_CHAN_WIDTH_40:
+- 		ch_inf.bw = BRCMU_CHAN_BW_40;
+--		if (primary_offset < 0)
+-+		if (primary_offset > 0)
+- 			ch_inf.sb = BRCMU_CHAN_SB_U;
+- 		else
+- 			ch_inf.sb = BRCMU_CHAN_SB_L;
+- 		break;
+- 	case NL80211_CHAN_WIDTH_80:
+- 		ch_inf.bw = BRCMU_CHAN_BW_80;
+--		if (primary_offset < 0) {
+--			if (primary_offset < -CH_10MHZ_APART)
+--				ch_inf.sb = BRCMU_CHAN_SB_UU;
+--			else
+--				ch_inf.sb = BRCMU_CHAN_SB_UL;
+--		} else {
+--			if (primary_offset > CH_10MHZ_APART)
+--				ch_inf.sb = BRCMU_CHAN_SB_LL;
+--			else
+--				ch_inf.sb = BRCMU_CHAN_SB_LU;
+--		}
+-+		if (primary_offset == -30)
+-+			ch_inf.sb = BRCMU_CHAN_SB_LL;
+-+		else if (primary_offset == -10)
+-+			ch_inf.sb = BRCMU_CHAN_SB_LU;
+-+		else if (primary_offset == 10)
+-+			ch_inf.sb = BRCMU_CHAN_SB_UL;
+-+		else
+-+			ch_inf.sb = BRCMU_CHAN_SB_UU;
+- 		break;
+- 	case NL80211_CHAN_WIDTH_80P80:
+- 	case NL80211_CHAN_WIDTH_160:
+diff --git a/package/kernel/mac80211/patches/323-0001-brcmfmac-analyze-descriptors-of-current-component-on.patch b/package/kernel/mac80211/patches/323-0001-brcmfmac-analyze-descriptors-of-current-component-on.patch
+deleted file mode 100644
+index d7018da..0000000
+--- a/package/kernel/mac80211/patches/323-0001-brcmfmac-analyze-descriptors-of-current-component-on.patch
++++ /dev/null
+@@ -1,51 +0,0 @@
+-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+-Date: Tue, 26 Jan 2016 17:57:01 +0100
+-Subject: [PATCH] brcmfmac: analyze descriptors of current component only
+-MIME-Version: 1.0
+-Content-Type: text/plain; charset=UTF-8
+-Content-Transfer-Encoding: 8bit
+-
+-So far we were looking for address descriptors without a check for
+-crossing current component border. In case of dealing with unsupported
+-descriptor or descriptor missing at all the code would incorrectly get
+-data from another component.
+-
+-Consider this binary-described component from BCM4366 EROM:
+-4bf83b01	TAG==CI		CID==0x83b
+-20080201	TAG==CI		PORTS==0+1	WRAPPERS==0+1
+-18400035	TAG==ADDR	SZ_SZD		TYPE_SLAVE
+-00050000
+-18107085	TAG==ADDR	SZ_4K		TYPE_SWRAP
+-
+-Driver was assigning invalid base address to this core:
+-brcmfmac:  [6 ] core 0x83b:32 base 0x18109000 wrap 0x18107000
+-which came from totally different component defined in EROM:
+-43b36701	TAG==CI		CID==0x367
+-00000201	TAG==CI		PORTS==0+1	WRAPPERS==0+0
+-18109005	TAG==ADDR	SZ_4K		TYPE_SLAVE
+-
+-This change will also allow us to support components without wrapper
+-address in the future.
+-
+-Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+----
+-
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+-@@ -803,7 +803,14 @@ static int brcmf_chip_dmp_get_regaddr(st
+- 				*eromaddr -= 4;
+- 				return -EFAULT;
+- 			}
+--		} while (desc != DMP_DESC_ADDRESS);
+-+		} while (desc != DMP_DESC_ADDRESS &&
+-+			 desc != DMP_DESC_COMPONENT);
+-+
+-+		/* stop if we crossed current component border */
+-+		if (desc == DMP_DESC_COMPONENT) {
+-+			*eromaddr -= 4;
+-+			return 0;
+-+		}
+- 
+- 		/* skip upper 32-bit address descriptor */
+- 		if (val & DMP_DESC_ADDRSIZE_GT32)
+diff --git a/package/kernel/mac80211/patches/323-0002-brcmfmac-allow-storing-PMU-core-without-wrapper-addr.patch b/package/kernel/mac80211/patches/323-0002-brcmfmac-allow-storing-PMU-core-without-wrapper-addr.patch
+deleted file mode 100644
+index 045ab49..0000000
+--- a/package/kernel/mac80211/patches/323-0002-brcmfmac-allow-storing-PMU-core-without-wrapper-addr.patch
++++ /dev/null
+@@ -1,28 +0,0 @@
+-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+-Date: Tue, 26 Jan 2016 17:57:02 +0100
+-Subject: [PATCH] brcmfmac: allow storing PMU core without wrapper address
+-MIME-Version: 1.0
+-Content-Type: text/plain; charset=UTF-8
+-Content-Transfer-Encoding: 8bit
+-
+-Separated PMU core can be found in new devices and should be used for
+-accessing PMU registers (which were routed through ChipCommon so far).
+-This core is one of exceptions that doesn't have or need wrapper address
+-to be still safely accessible.
+-
+-Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+----
+-
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+-@@ -883,7 +883,8 @@ int brcmf_chip_dmp_erom_scan(struct brcm
+- 		rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S;
+- 
+- 		/* need core with ports */
+--		if (nmw + nsw == 0)
+-+		if (nmw + nsw == 0 &&
+-+		    id != BCMA_CORE_PMU)
+- 			continue;
+- 
+- 		/* try to obtain register address info */
+diff --git a/package/kernel/mac80211/patches/323-0003-brcmfmac-read-extended-capabilities-of-ChipCommon-co.patch b/package/kernel/mac80211/patches/323-0003-brcmfmac-read-extended-capabilities-of-ChipCommon-co.patch
+deleted file mode 100644
+index 7b7ba4f..0000000
+--- a/package/kernel/mac80211/patches/323-0003-brcmfmac-read-extended-capabilities-of-ChipCommon-co.patch
++++ /dev/null
+@@ -1,43 +0,0 @@
+-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+-Date: Tue, 26 Jan 2016 17:57:03 +0100
+-Subject: [PATCH] brcmfmac: read extended capabilities of ChipCommon core
+-MIME-Version: 1.0
+-Content-Type: text/plain; charset=UTF-8
+-Content-Transfer-Encoding: 8bit
+-
+-This is an extra bitfield with info about some present hardware.
+-
+-Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+----
+-
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+-@@ -1025,6 +1025,9 @@ static int brcmf_chip_setup(struct brcmf
+- 	/* get chipcommon capabilites */
+- 	pub->cc_caps = chip->ops->read32(chip->ctx,
+- 					 CORE_CC_REG(base, capabilities));
+-+	pub->cc_caps_ext = chip->ops->read32(chip->ctx,
+-+					     CORE_CC_REG(base,
+-+							 capabilities_ext));
+- 
+- 	/* get pmu caps & rev */
+- 	if (pub->cc_caps & CC_CAP_PMU) {
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
+-@@ -27,6 +27,7 @@
+-  * @chip: chip identifier.
+-  * @chiprev: chip revision.
+-  * @cc_caps: chipcommon core capabilities.
+-+ * @cc_caps_ext: chipcommon core extended capabilities.
+-  * @pmucaps: PMU capabilities.
+-  * @pmurev: PMU revision.
+-  * @rambase: RAM base address (only applicable for ARM CR4 chips).
+-@@ -38,6 +39,7 @@ struct brcmf_chip {
+- 	u32 chip;
+- 	u32 chiprev;
+- 	u32 cc_caps;
+-+	u32 cc_caps_ext;
+- 	u32 pmucaps;
+- 	u32 pmurev;
+- 	u32 rambase;
+diff --git a/package/kernel/mac80211/patches/323-0004-brcmfmac-access-PMU-registers-using-standalone-PMU-c.patch b/package/kernel/mac80211/patches/323-0004-brcmfmac-access-PMU-registers-using-standalone-PMU-c.patch
+deleted file mode 100644
+index 2af6fd9..0000000
+--- a/package/kernel/mac80211/patches/323-0004-brcmfmac-access-PMU-registers-using-standalone-PMU-c.patch
++++ /dev/null
+@@ -1,148 +0,0 @@
+-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+-Date: Tue, 26 Jan 2016 17:57:04 +0100
+-Subject: [PATCH] brcmfmac: access PMU registers using standalone PMU core if
+- available
+-MIME-Version: 1.0
+-Content-Type: text/plain; charset=UTF-8
+-Content-Transfer-Encoding: 8bit
+-
+-On recent Broadcom chipsets PMU is present as separated core and it
+-can't be accessed using ChipCommon anymore as it fails with e.g.:
+-[   18.198412] Unhandled fault: imprecise external abort (0x1406) at 0xb6da200f
+-
+-Add a new helper function that will return a proper core that should be
+-used for accessing PMU registers.
+-
+-Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+-Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+----
+-
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c
+-@@ -1014,6 +1014,7 @@ static int brcmf_chip_setup(struct brcmf
+- {
+- 	struct brcmf_chip *pub;
+- 	struct brcmf_core_priv *cc;
+-+	struct brcmf_core *pmu;
+- 	u32 base;
+- 	u32 val;
+- 	int ret = 0;
+-@@ -1030,9 +1031,10 @@ static int brcmf_chip_setup(struct brcmf
+- 							 capabilities_ext));
+- 
+- 	/* get pmu caps & rev */
+-+	pmu = brcmf_chip_get_pmu(pub); /* after reading cc_caps_ext */
+- 	if (pub->cc_caps & CC_CAP_PMU) {
+- 		val = chip->ops->read32(chip->ctx,
+--					CORE_CC_REG(base, pmucapabilities));
+-+					CORE_CC_REG(pmu->base, pmucapabilities));
+- 		pub->pmurev = val & PCAP_REV_MASK;
+- 		pub->pmucaps = val;
+- 	}
+-@@ -1131,6 +1133,23 @@ struct brcmf_core *brcmf_chip_get_chipco
+- 	return &cc->pub;
+- }
+- 
+-+struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub)
+-+{
+-+	struct brcmf_core *cc = brcmf_chip_get_chipcommon(pub);
+-+	struct brcmf_core *pmu;
+-+
+-+	/* See if there is separated PMU core available */
+-+	if (cc->rev >= 35 &&
+-+	    pub->cc_caps_ext & BCMA_CC_CAP_EXT_AOB_PRESENT) {
+-+		pmu = brcmf_chip_get_core(pub, BCMA_CORE_PMU);
+-+		if (pmu)
+-+			return pmu;
+-+	}
+-+
+-+	/* Fallback to ChipCommon core for older hardware */
+-+	return cc;
+-+}
+-+
+- bool brcmf_chip_iscoreup(struct brcmf_core *pub)
+- {
+- 	struct brcmf_core_priv *core;
+-@@ -1301,6 +1320,7 @@ bool brcmf_chip_sr_capable(struct brcmf_
+- {
+- 	u32 base, addr, reg, pmu_cc3_mask = ~0;
+- 	struct brcmf_chip_priv *chip;
+-+	struct brcmf_core *pmu = brcmf_chip_get_pmu(pub);
+- 
+- 	brcmf_dbg(TRACE, "Enter\n");
+- 
+-@@ -1320,9 +1340,9 @@ bool brcmf_chip_sr_capable(struct brcmf_
+- 	case BRCM_CC_4335_CHIP_ID:
+- 	case BRCM_CC_4339_CHIP_ID:
+- 		/* read PMU chipcontrol register 3 */
+--		addr = CORE_CC_REG(base, chipcontrol_addr);
+-+		addr = CORE_CC_REG(pmu->base, chipcontrol_addr);
+- 		chip->ops->write32(chip->ctx, addr, 3);
+--		addr = CORE_CC_REG(base, chipcontrol_data);
+-+		addr = CORE_CC_REG(pmu->base, chipcontrol_data);
+- 		reg = chip->ops->read32(chip->ctx, addr);
+- 		return (reg & pmu_cc3_mask) != 0;
+- 	case BRCM_CC_43430_CHIP_ID:
+-@@ -1330,12 +1350,12 @@ bool brcmf_chip_sr_capable(struct brcmf_
+- 		reg = chip->ops->read32(chip->ctx, addr);
+- 		return reg != 0;
+- 	default:
+--		addr = CORE_CC_REG(base, pmucapabilities_ext);
+-+		addr = CORE_CC_REG(pmu->base, pmucapabilities_ext);
+- 		reg = chip->ops->read32(chip->ctx, addr);
+- 		if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
+- 			return false;
+- 
+--		addr = CORE_CC_REG(base, retention_ctl);
+-+		addr = CORE_CC_REG(pmu->base, retention_ctl);
+- 		reg = chip->ops->read32(chip->ctx, addr);
+- 		return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
+- 			       PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h
+-@@ -85,6 +85,7 @@ struct brcmf_chip *brcmf_chip_attach(voi
+- void brcmf_chip_detach(struct brcmf_chip *chip);
+- struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid);
+- struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip);
+-+struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub);
+- bool brcmf_chip_iscoreup(struct brcmf_core *core);
+- void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset);
+- void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+-@@ -3615,7 +3615,6 @@ brcmf_sdio_drivestrengthinit(struct brcm
+- 	const struct sdiod_drive_str *str_tab = NULL;
+- 	u32 str_mask;
+- 	u32 str_shift;
+--	u32 base;
+- 	u32 i;
+- 	u32 drivestrength_sel = 0;
+- 	u32 cc_data_temp;
+-@@ -3658,14 +3657,15 @@ brcmf_sdio_drivestrengthinit(struct brcm
+- 	}
+- 
+- 	if (str_tab != NULL) {
+-+		struct brcmf_core *pmu = brcmf_chip_get_pmu(ci);
+-+
+- 		for (i = 0; str_tab[i].strength != 0; i++) {
+- 			if (drivestrength >= str_tab[i].strength) {
+- 				drivestrength_sel = str_tab[i].sel;
+- 				break;
+- 			}
+- 		}
+--		base = brcmf_chip_get_chipcommon(ci)->base;
+--		addr = CORE_CC_REG(base, chipcontrol_addr);
+-+		addr = CORE_CC_REG(pmu->base, chipcontrol_addr);
+- 		brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
+- 		cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
+- 		cc_data_temp &= ~str_mask;
+-@@ -3835,8 +3835,7 @@ brcmf_sdio_probe_attach(struct brcmf_sdi
+- 		goto fail;
+- 
+- 	/* set PMUControl so a backplane reset does PMU state reload */
+--	reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base,
+--			       pmucontrol);
+-+	reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol);
+- 	reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);
+- 	if (err)
+- 		goto fail;
+diff --git a/package/kernel/mac80211/patches/323-0005-brcmfmac-add-support-for-14e4-4365-PCI-ID-with-BCM43.patch b/package/kernel/mac80211/patches/323-0005-brcmfmac-add-support-for-14e4-4365-PCI-ID-with-BCM43.patch
+deleted file mode 100644
+index 35887fc..0000000
+--- a/package/kernel/mac80211/patches/323-0005-brcmfmac-add-support-for-14e4-4365-PCI-ID-with-BCM43.patch
++++ /dev/null
+@@ -1,38 +0,0 @@
+-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+-Date: Tue, 26 Jan 2016 17:57:05 +0100
+-Subject: [PATCH] brcmfmac: add support for 14e4:4365 PCI ID with BCM4366
+- chipset
+-MIME-Version: 1.0
+-Content-Type: text/plain; charset=UTF-8
+-Content-Transfer-Encoding: 8bit
+-
+-On Broadcom ARM routers BCM4366 cards are available with 14e4:4365 ID.
+-Unfortunately this ID was already used by Broadcom for cards with
+-BCM43142, a totally different chipset requiring SoftMAC driver. To avoid
+-a conflict between brcmfmac and bcma use more specific ID entry with
+-subvendor and subdevice specified.
+-
+-Signed-off-by: Rafał Miłecki <zajec5@gmail.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
+-@@ -1951,6 +1951,9 @@ static const struct dev_pm_ops brcmf_pci
+- 
+- #define BRCMF_PCIE_DEVICE(dev_id)	{ BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\
+- 	PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
+-+#define BRCMF_PCIE_DEVICE_SUB(dev_id, subvend, subdev)	{ \
+-+	BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\
+-+	subvend, subdev, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 }
+- 
+- static struct pci_device_id brcmf_pcie_devid_table[] = {
+- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID),
+-@@ -1966,6 +1969,7 @@ static struct pci_device_id brcmf_pcie_d
+- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID),
+- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID),
+- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID),
+-+	BRCMF_PCIE_DEVICE_SUB(0x4365, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4365),
+- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID),
+- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID),
+- 	BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID),
+diff --git a/package/kernel/mac80211/patches/323-ath9k-Fix-programming-of-minCCA-power-threshold.patch b/package/kernel/mac80211/patches/323-ath9k-Fix-programming-of-minCCA-power-threshold.patch
+new file mode 100644
+index 0000000..59ac29b
+--- /dev/null
++++ b/package/kernel/mac80211/patches/323-ath9k-Fix-programming-of-minCCA-power-threshold.patch
+@@ -0,0 +1,26 @@
++From: Sven Eckelmann <sven@narfation.org>
++Date: Fri, 17 Jun 2016 11:58:20 +0200
++Subject: [PATCH] ath9k: Fix programming of minCCA power threshold
++
++The function ar9003_hw_apply_minccapwr_thresh takes as second parameter not
++a pointer to the channel but a boolean value describing whether the channel
++is 2.4GHz or not. This broke (according to the origin commit) the ETSI
++regulatory compliance on 5GHz channels.
++
++Fixes: 3533bf6b15a0 ("ath9k: Fix regulatory compliance")
++Signed-off-by: Sven Eckelmann <sven@narfation.org>
++Cc: Simon Wunderlich <sw@simonwunderlich.de>
++Cc: Sujith Manoharan <c_manoha@qca.qualcomm.com>
++---
++
++--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
++@@ -4175,7 +4175,7 @@ static void ath9k_hw_ar9300_set_board_va
++ 	if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah) && !AR_SREV_9531(ah))
++ 		ar9003_hw_internal_regulator_apply(ah);
++ 	ar9003_hw_apply_tuning_caps(ah);
++-	ar9003_hw_apply_minccapwr_thresh(ah, chan);
+++	ar9003_hw_apply_minccapwr_thresh(ah, is2ghz);
++ 	ar9003_hw_txend_to_xpa_off_apply(ah, is2ghz);
++ 	ar9003_hw_thermometer_apply(ah);
++ 	ar9003_hw_thermo_cal_apply(ah);
+diff --git a/package/kernel/mac80211/patches/324-ath9k_hw-fix-spectral-scan-on-AR9285-and-newer.patch b/package/kernel/mac80211/patches/324-ath9k_hw-fix-spectral-scan-on-AR9285-and-newer.patch
+new file mode 100644
+index 0000000..b6f4868
+--- /dev/null
++++ b/package/kernel/mac80211/patches/324-ath9k_hw-fix-spectral-scan-on-AR9285-and-newer.patch
+@@ -0,0 +1,86 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Mon, 11 Jul 2016 10:34:37 +0200
++Subject: [PATCH] ath9k_hw: fix spectral scan on AR9285 and newer
++
++The register layout of AR_PHY_SPECTRAL_SCAN has changed, only AR9280
++uses the old layout
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
++@@ -476,6 +476,7 @@ static void ar9002_hw_set_bt_ant_diversi
++ static void ar9002_hw_spectral_scan_config(struct ath_hw *ah,
++ 				    struct ath_spec_scan *param)
 + {
-+ 	struct ieee80211_tx_info *tx_info;
-+ 	struct ath_frame_info *fi;
-+-	struct sk_buff *skb;
-++	struct sk_buff *skb, *first_skb = NULL;
-+ 	struct ath_buf *bf;
-+ 	u16 seqno;
-+ 
-+ 	while (1) {
-+-		*q = &tid->retry_q;
-+-		if (skb_queue_empty(*q))
-+-			*q = &tid->buf_q;
-+-
-+-		skb = skb_peek(*q);
-++		skb = ath_tid_dequeue(tid);
-+ 		if (!skb)
-+ 			break;
-+ 
-+@@ -923,7 +936,6 @@ ath_tx_get_tid_subframe(struct ath_softc
-+ 			bf->bf_state.stale = false;
-+ 
-+ 		if (!bf) {
-+-			__skb_unlink(skb, *q);
-+ 			ath_txq_skb_done(sc, txq, skb);
-+ 			ieee80211_free_txskb(sc->hw, skb);
-+ 			continue;
-+@@ -952,8 +964,19 @@ ath_tx_get_tid_subframe(struct ath_softc
-+ 		seqno = bf->bf_state.seqno;
-+ 
-+ 		/* do not step over block-ack window */
-+-		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno))
-++		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
-++			__skb_queue_tail(&tid->retry_q, skb);
-++
-++			/* If there are other skbs in the retry q, they are
-++			 * probably within the BAW, so loop immediately to get
-++			 * one of them. Otherwise the queue can get stuck. */
-++			if (!skb_queue_is_first(&tid->retry_q, skb) && skb != first_skb) {
-++				if(!first_skb) /* infinite loop prevention */
-++					first_skb = skb;
-++				continue;
-++			}
-+ 			break;
-++		}
-+ 
-+ 		if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) {
-+ 			struct ath_tx_status ts = {};
-+@@ -961,7 +984,6 @@ ath_tx_get_tid_subframe(struct ath_softc
+++	u32 repeat_bit;
++ 	u8 count;
 + 
-+ 			INIT_LIST_HEAD(&bf_head);
-+ 			list_add(&bf->list, &bf_head);
-+-			__skb_unlink(skb, *q);
-+ 			ath_tx_update_baw(sc, tid, seqno);
-+ 			ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, &ts, 0);
-+ 			continue;
-+@@ -973,11 +995,10 @@ ath_tx_get_tid_subframe(struct ath_softc
-+ 	return NULL;
-+ }
++ 	if (!param->enabled) {
++@@ -486,12 +487,15 @@ static void ar9002_hw_spectral_scan_conf
++ 	REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_FFT_ENA);
++ 	REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE);
 + 
-+-static bool
-++static int
-+ ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq,
-+ 		 struct ath_atx_tid *tid, struct list_head *bf_q,
-+-		 struct ath_buf *bf_first, struct sk_buff_head *tid_q,
-+-		 int *aggr_len)
-++		 struct ath_buf *bf_first)
-+ {
-+ #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
-+ 	struct ath_buf *bf = bf_first, *bf_prev = NULL;
-+@@ -987,12 +1008,13 @@ ath_tx_form_aggr(struct ath_softc *sc, s
-+ 	struct ieee80211_tx_info *tx_info;
-+ 	struct ath_frame_info *fi;
-+ 	struct sk_buff *skb;
-+-	bool closed = false;
+++	if (AR_SREV_9280(ah))
+++		repeat_bit = AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT;
+++	else
+++		repeat_bit = AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI;
 ++
++ 	if (param->short_repeat)
++-		REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
++-			    AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);
+++		REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, repeat_bit);
++ 	else
++-		REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN,
++-			    AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);
+++		REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN, repeat_bit);
 + 
-+ 	bf = bf_first;
-+ 	aggr_limit = ath_lookup_rate(sc, bf, tid);
-+ 
-+-	do {
-++	while (bf)
-++	{
-+ 		skb = bf->bf_mpdu;
-+ 		fi = get_frame_info(skb);
-+ 
-+@@ -1001,12 +1023,12 @@ ath_tx_form_aggr(struct ath_softc *sc, s
-+ 		if (nframes) {
-+ 			if (aggr_limit < al + bpad + al_delta ||
-+ 			    ath_lookup_legacy(bf) || nframes >= h_baw)
-+-				break;
-++				goto stop;
-+ 
-+ 			tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
-+ 			if ((tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) ||
-+ 			    !(tx_info->flags & IEEE80211_TX_CTL_AMPDU))
-+-				break;
-++				goto stop;
-+ 		}
-+ 
-+ 		/* add padding for previous frame to aggregation length */
-+@@ -1028,20 +1050,18 @@ ath_tx_form_aggr(struct ath_softc *sc, s
-+ 			ath_tx_addto_baw(sc, tid, bf);
-+ 		bf->bf_state.ndelim = ndelim;
-+ 
-+-		__skb_unlink(skb, tid_q);
-+ 		list_add_tail(&bf->list, bf_q);
-+ 		if (bf_prev)
-+ 			bf_prev->bf_next = bf;
-+ 
-+ 		bf_prev = bf;
-+ 
-+-		bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
-+-		if (!bf) {
-+-			closed = true;
-+-			break;
-+-		}
-+-	} while (ath_tid_has_buffered(tid));
-+-
-++		bf = ath_tx_get_tid_subframe(sc, txq, tid);
-++	}
-++	goto finish;
-++stop:
-++	__skb_queue_tail(&tid->retry_q, bf->bf_mpdu);
-++finish:
-+ 	bf = bf_first;
-+ 	bf->bf_lastbf = bf_prev;
-+ 
-+@@ -1052,9 +1072,7 @@ ath_tx_form_aggr(struct ath_softc *sc, s
-+ 		TX_STAT_INC(txq->axq_qnum, a_aggr);
-+ 	}
-+ 
-+-	*aggr_len = al;
-+-
-+-	return closed;
-++	return al;
-+ #undef PADBYTES
-+ }
++ 	/* on AR92xx, the highest bit of count will make the the chip send
++ 	 * spectral samples endlessly. Check if this really was intended,
++@@ -499,15 +503,25 @@ static void ar9002_hw_spectral_scan_conf
++ 	 */
++ 	count = param->count;
++ 	if (param->endless) {
++-		if (AR_SREV_9271(ah))
++-			count = 0;
++-		else
+++		if (AR_SREV_9280(ah))
++ 			count = 0x80;
+++		else
+++			count = 0;
++ 	} else if (count & 0x80)
++ 		count = 0x7f;
+++	else if (!count)
+++		count = 1;
+++
+++	if (AR_SREV_9280(ah)) {
+++		REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
+++			      AR_PHY_SPECTRAL_SCAN_COUNT, count);
+++	} else {
+++		REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
+++			      AR_PHY_SPECTRAL_SCAN_COUNT_KIWI, count);
+++		REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
+++			    AR_PHY_SPECTRAL_SCAN_PHYERR_MASK_SELECT);
+++	}
 + 
-+@@ -1431,18 +1449,15 @@ static void ath_tx_fill_desc(struct ath_
-+ static void
-+ ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq,
-+ 		  struct ath_atx_tid *tid, struct list_head *bf_q,
-+-		  struct ath_buf *bf_first, struct sk_buff_head *tid_q)
-++		  struct ath_buf *bf_first)
-+ {
-+ 	struct ath_buf *bf = bf_first, *bf_prev = NULL;
-+-	struct sk_buff *skb;
-+ 	int nframes = 0;
++-	REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
++-		      AR_PHY_SPECTRAL_SCAN_COUNT, count);
++ 	REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
++ 		      AR_PHY_SPECTRAL_SCAN_PERIOD, param->period);
++ 	REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
++--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.h
+++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.h
++@@ -177,8 +177,11 @@
++ #define AR_PHY_SPECTRAL_SCAN_PERIOD_S		8
++ #define AR_PHY_SPECTRAL_SCAN_COUNT		0x00FF0000  /* Number of reports, reg 68, bits 16-23*/
++ #define AR_PHY_SPECTRAL_SCAN_COUNT_S		16
+++#define AR_PHY_SPECTRAL_SCAN_COUNT_KIWI		0x0FFF0000  /* Number of reports, reg 68, bits 16-27*/
+++#define AR_PHY_SPECTRAL_SCAN_COUNT_KIWI_S	16
++ #define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT	0x01000000  /* Short repeat, reg 68, bit 24*/
++-#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S	24  /* Short repeat, reg 68, bit 24*/
+++#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_KIWI	0x10000000  /* Short repeat, reg 68, bit 28*/
+++#define AR_PHY_SPECTRAL_SCAN_PHYERR_MASK_SELECT	0x40000000
 + 
-+ 	do {
-+ 		struct ieee80211_tx_info *tx_info;
-+-		skb = bf->bf_mpdu;
++ #define AR_PHY_RX_DELAY           0x9914
++ #define AR_PHY_SEARCH_START_DELAY 0x9918
+diff --git a/package/kernel/mac80211/patches/324-brcmfmac-treat-NULL-character-in-NVRAM-as-separator.patch b/package/kernel/mac80211/patches/324-brcmfmac-treat-NULL-character-in-NVRAM-as-separator.patch
+deleted file mode 100644
+index 6ce60f1..0000000
+--- a/package/kernel/mac80211/patches/324-brcmfmac-treat-NULL-character-in-NVRAM-as-separator.patch
++++ /dev/null
+@@ -1,32 +0,0 @@
+-From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+-Date: Sun, 31 Jan 2016 12:14:34 +0100
+-Subject: [PATCH] brcmfmac: treat NULL character in NVRAM as separator
+-MIME-Version: 1.0
+-Content-Type: text/plain; charset=UTF-8
+-Content-Transfer-Encoding: 8bit
+-
+-Platform NVRAM (stored on a flash partition) has entries separated by a
+-NULL (\0) char. Our parsing code switches from VALUE state to IDLE
+-whenever it meets a NULL (\0). When that happens our IDLE handler should
+-simply consume it and analyze whatever is placed ahead.
+-
+-This fixes harmless warnings spamming debugging output:
+-[  155.165624] brcmfmac: brcmf_nvram_handle_idle warning: ln=1:col=20: ignoring invalid character
+-[  155.180806] brcmfmac: brcmf_nvram_handle_idle warning: ln=1:col=44: ignoring invalid character
+-[  155.195971] brcmfmac: brcmf_nvram_handle_idle warning: ln=1:col=63: ignoring invalid character
+-
+-Signed-off-by: Rafał Miłecki <zajec5@gmail.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
+-@@ -93,7 +93,7 @@ static enum nvram_parser_state brcmf_nvr
+- 	c = nvp->data[nvp->pos];
+- 	if (c == '\n')
+- 		return COMMENT;
+--	if (is_whitespace(c))
+-+	if (is_whitespace(c) || c == '\0')
+- 		goto proceed;
+- 	if (c == '#')
+- 		return COMMENT;
+diff --git a/package/kernel/mac80211/patches/325-ath9k_hw-fix-duplicate-and-partially-wrong-definitio.patch b/package/kernel/mac80211/patches/325-ath9k_hw-fix-duplicate-and-partially-wrong-definitio.patch
+new file mode 100644
+index 0000000..6685f33
+--- /dev/null
++++ b/package/kernel/mac80211/patches/325-ath9k_hw-fix-duplicate-and-partially-wrong-definitio.patch
+@@ -0,0 +1,57 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Mon, 11 Jul 2016 11:31:39 +0200
++Subject: [PATCH] ath9k_hw: fix duplicate (and partially wrong) definition
++ of AR_CH0_THERM
++
++AR_PHY_65NM_CH0_THERM and AR_CH0_THERM were supposed to refer to the
++same register, however they had different SREV checks.
++
++Remove the duplicate and use the checks. Since there were other SREV
++checks present in the only place that uses this, this will probaby not
++affect runtime behavior.
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
++@@ -689,13 +689,6 @@
++ #define AR_CH0_TOP_XPABIASLVL (AR_SREV_9550(ah) ? 0x3c0 : 0x300)
++ #define AR_CH0_TOP_XPABIASLVL_S (AR_SREV_9550(ah) ? 6 : 8)
 + 
-+ 		nframes++;
-+-		__skb_unlink(skb, tid_q);
-+ 		list_add_tail(&bf->list, bf_q);
-+ 		if (bf_prev)
-+ 			bf_prev->bf_next = bf;
-+@@ -1451,13 +1466,15 @@ ath_tx_form_burst(struct ath_softc *sc,
-+ 		if (nframes >= 2)
-+ 			break;
++-#define AR_CH0_THERM	(AR_SREV_9300(ah) ? 0x16290 : \
++-				((AR_SREV_9485(ah) ? 0x1628c : 0x16294)))
++-#define AR_CH0_THERM_XPABIASLVL_MSB 0x3
++-#define AR_CH0_THERM_XPABIASLVL_MSB_S 0
++-#define AR_CH0_THERM_XPASHORT2GND 0x4
++-#define AR_CH0_THERM_XPASHORT2GND_S 2
++-
++ #define AR_SWITCH_TABLE_COM_ALL (0xffff)
++ #define AR_SWITCH_TABLE_COM_ALL_S (0)
++ #define AR_SWITCH_TABLE_COM_AR9462_ALL (0xffffff)
++@@ -712,15 +705,17 @@
++ #define AR_SWITCH_TABLE_ALL (0xfff)
++ #define AR_SWITCH_TABLE_ALL_S (0)
 + 
-+-		bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
-++		bf = ath_tx_get_tid_subframe(sc, txq, tid);
-+ 		if (!bf)
-+ 			break;
++-#define AR_PHY_65NM_CH0_THERM       (AR_SREV_9300(ah) ? 0x16290 :\
++-				     ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16294 : 0x1628c))
+++#define AR_CH0_THERM       (AR_SREV_9300(ah) ? 0x16290 :\
+++			    ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16294 : 0x1628c))
+++#define AR_CH0_THERM_XPABIASLVL_MSB 0x3
+++#define AR_CH0_THERM_XPABIASLVL_MSB_S 0
+++#define AR_CH0_THERM_XPASHORT2GND 0x4
+++#define AR_CH0_THERM_XPASHORT2GND_S 2
 + 
-+ 		tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
-+-		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
-++		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
-++			__skb_queue_tail(&tid->retry_q, bf->bf_mpdu);
-+ 			break;
-++		}
++-#define AR_PHY_65NM_CH0_THERM_LOCAL   0x80000000
++-#define AR_PHY_65NM_CH0_THERM_LOCAL_S 31
++-#define AR_PHY_65NM_CH0_THERM_START   0x20000000
++-#define AR_PHY_65NM_CH0_THERM_START_S 29
++-#define AR_PHY_65NM_CH0_THERM_SAR_ADC_OUT   0x0000ff00
++-#define AR_PHY_65NM_CH0_THERM_SAR_ADC_OUT_S 8
+++#define AR_CH0_THERM_LOCAL   0x80000000
+++#define AR_CH0_THERM_START   0x20000000
+++#define AR_CH0_THERM_SAR_ADC_OUT   0x0000ff00
+++#define AR_CH0_THERM_SAR_ADC_OUT_S 8
 + 
-+ 		ath_set_rates(tid->an->vif, tid->an->sta, bf, false);
-+ 	} while (1);
-+@@ -1468,34 +1485,33 @@ static bool ath_tx_sched_aggr(struct ath
++ #define AR_CH0_TOP2		(AR_SREV_9300(ah) ? 0x1628c : \
++ 					(AR_SREV_9462(ah) ? 0x16290 : 0x16284))
+diff --git a/package/kernel/mac80211/patches/325-brcmfmac-sdio-Increase-the-default-timeouts-a-bit.patch b/package/kernel/mac80211/patches/325-brcmfmac-sdio-Increase-the-default-timeouts-a-bit.patch
+deleted file mode 100644
+index 012dea1..0000000
+--- a/package/kernel/mac80211/patches/325-brcmfmac-sdio-Increase-the-default-timeouts-a-bit.patch
++++ /dev/null
+@@ -1,41 +0,0 @@
+-From: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+-Date: Mon, 25 Jan 2016 11:47:29 +0100
+-Subject: [PATCH] brcmfmac: sdio: Increase the default timeouts a bit
+-
+-On a Radxa Rock2 board with a Ampak AP6335 (Broadcom 4339 core) it seems
+-the card responds very quickly most of the time, unfortunately during
+-initialisation it sometimes seems to take just a bit over 2 seconds to
+-respond.
+-
+-This results intialization failing with message like:
+-  brcmf_c_preinit_dcmds: Retreiving cur_etheraddr failed, -52
+-  brcmf_bus_start: failed: -52
+-  brcmf_sdio_firmware_callback: dongle is not responding
+-
+-Increasing the timeout to allow for a bit more headroom allows the
+-card to initialize reliably.
+-
+-A quick search online after diagnosing/fixing this showed that Google
+-has a similar patch in their ChromeOS tree, so this doesn't seem
+-specific to the board I'm using.
+-
+-Signed-off-by: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+-Reviewed-by: Julian Calaby <julian.calaby@gmail.com>
+-Acked-by: Arend van Spriel <arend@broadcom.com>
+-Reviewed-by: Douglas Anderson <dianders@chromium.org>
+-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
+-@@ -45,8 +45,8 @@
+- #include "chip.h"
+- #include "firmware.h"
+- 
+--#define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2000)
+--#define CTL_DONE_TIMEOUT	msecs_to_jiffies(2000)
+-+#define DCMD_RESP_TIMEOUT	msecs_to_jiffies(2500)
+-+#define CTL_DONE_TIMEOUT	msecs_to_jiffies(2500)
+- 
+- #ifdef DEBUG
+- 
+diff --git a/package/kernel/mac80211/patches/326-ath9k-make-NF-load-complete-quickly-and-reliably.patch b/package/kernel/mac80211/patches/326-ath9k-make-NF-load-complete-quickly-and-reliably.patch
+deleted file mode 100644
+index 71f7a40..0000000
+--- a/package/kernel/mac80211/patches/326-ath9k-make-NF-load-complete-quickly-and-reliably.patch
++++ /dev/null
+@@ -1,87 +0,0 @@
+-From: Miaoqing Pan <miaoqing@codeaurora.org>
+-Date: Fri, 5 Feb 2016 09:45:50 +0800
+-Subject: [PATCH] ath9k: make NF load complete quickly and reliably
+-
+-Make NF load complete quickly and reliably. NF load execution
+-is delayed by HW to end of frame if frame Rx or Tx is ongoing.
+-Increasing timeout to max frame duration. If NF cal is ongoing
+-before NF load, stop it before load, and restart it afterwards.
+-
+-Signed-off-by: Miaoqing Pan <miaoqing@codeaurora.org>
+----
+-
+---- a/drivers/net/wireless/ath/ath9k/calib.c
+-+++ b/drivers/net/wireless/ath/ath9k/calib.c
+-@@ -241,6 +241,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
+- 	u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
+- 	struct ath_common *common = ath9k_hw_common(ah);
+- 	s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
+-+	u32 bb_agc_ctl = REG_READ(ah, AR_PHY_AGC_CONTROL);
+- 
+- 	if (ah->caldata)
+- 		h = ah->caldata->nfCalHist;
+-@@ -264,6 +265,16 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
+- 	}
+- 
+- 	/*
+-+	 * stop NF cal if ongoing to ensure NF load completes immediately
+-+	 * (or after end rx/tx frame if ongoing)
+-+	 */
+-+	if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
+-+		REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+-+		REG_RMW_BUFFER_FLUSH(ah);
+-+		ENABLE_REG_RMW_BUFFER(ah);
+-+	}
+-+
+-+	/*
+- 	 * Load software filtered NF value into baseband internal minCCApwr
+- 	 * variable.
+- 	 */
+-@@ -276,18 +287,33 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
+- 
+- 	/*
+- 	 * Wait for load to complete, should be fast, a few 10s of us.
+--	 * The max delay was changed from an original 250us to 10000us
+--	 * since 250us often results in NF load timeout and causes deaf
+--	 * condition during stress testing 12/12/2009
+-+	 * The max delay was changed from an original 250us to 22.2 msec.
+-+	 * This would increase timeout to the longest possible frame
+-+	 * (11n max length 22.1 msec)
+- 	 */
+--	for (j = 0; j < 10000; j++) {
+-+	for (j = 0; j < 22200; j++) {
+- 		if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
+--		     AR_PHY_AGC_CONTROL_NF) == 0)
+-+			      AR_PHY_AGC_CONTROL_NF) == 0)
+- 			break;
+- 		udelay(10);
+- 	}
+- 
+- 	/*
+-+	 * Restart NF so it can continue.
+-+	 */
+-+	if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NF) {
+-+		ENABLE_REG_RMW_BUFFER(ah);
+-+		if (bb_agc_ctl & AR_PHY_AGC_CONTROL_ENABLE_NF)
+-+			REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+-+				    AR_PHY_AGC_CONTROL_ENABLE_NF);
+-+		if (bb_agc_ctl & AR_PHY_AGC_CONTROL_NO_UPDATE_NF)
+-+			REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+-+				    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+-+		REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+-+		REG_RMW_BUFFER_FLUSH(ah);
+-+	}
+-+
+-+	/*
+- 	 * We timed out waiting for the noisefloor to load, probably due to an
+- 	 * in-progress rx. Simply return here and allow the load plenty of time
+- 	 * to complete before the next calibration interval.  We need to avoid
+-@@ -296,7 +322,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, s
+- 	 * here, the baseband nf cal will just be capped by our present
+- 	 * noisefloor until the next calibration timer.
+- 	 */
+--	if (j == 10000) {
+-+	if (j == 22200) {
+- 		ath_dbg(common, ANY,
+- 			"Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
+- 			REG_READ(ah, AR_PHY_AGC_CONTROL));
+diff --git a/package/kernel/mac80211/patches/326-ath9k_hw-simplify-ar9003_hw_per_calibration.patch b/package/kernel/mac80211/patches/326-ath9k_hw-simplify-ar9003_hw_per_calibration.patch
+new file mode 100644
+index 0000000..999d993
+--- /dev/null
++++ b/package/kernel/mac80211/patches/326-ath9k_hw-simplify-ar9003_hw_per_calibration.patch
+@@ -0,0 +1,88 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Mon, 11 Jul 2016 11:34:47 +0200
++Subject: [PATCH] ath9k_hw: simplify ar9003_hw_per_calibration
++
++Reduce indentation, use a variable to save a few pointer dereferences
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
++@@ -75,50 +75,49 @@ static bool ar9003_hw_per_calibration(st
++ 				      struct ath9k_cal_list *currCal)
 + {
-+ 	struct ath_buf *bf;
-+ 	struct ieee80211_tx_info *tx_info;
-+-	struct sk_buff_head *tid_q;
-+ 	struct list_head bf_q;
-+ 	int aggr_len = 0;
-+-	bool aggr, last = true;
-++	bool aggr;
-+ 
-+ 	if (!ath_tid_has_buffered(tid))
-+ 		return false;
-+ 
-+ 	INIT_LIST_HEAD(&bf_q);
-+ 
-+-	bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
-++	bf = ath_tx_get_tid_subframe(sc, txq, tid);
-+ 	if (!bf)
-+ 		return false;
-+ 
-+ 	tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
-+ 	aggr = !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
-+ 	if ((aggr && txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) ||
-+-		(!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) {
-++	    (!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) {
-++		__skb_queue_tail(&tid->retry_q, bf->bf_mpdu);
-+ 		*stop = true;
-+ 		return false;
-+ 	}
-+ 
-+ 	ath_set_rates(tid->an->vif, tid->an->sta, bf, false);
-+ 	if (aggr)
-+-		last = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf,
-+-					tid_q, &aggr_len);
-++		aggr_len = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf);
-+ 	else
-+-		ath_tx_form_burst(sc, txq, tid, &bf_q, bf, tid_q);
-++		ath_tx_form_burst(sc, txq, tid, &bf_q, bf);
-+ 
-+ 	if (list_empty(&bf_q))
-+ 		return false;
-+@@ -1538,9 +1554,6 @@ int ath_tx_aggr_start(struct ath_softc *
-+ 		an->mpdudensity = density;
-+ 	}
-+ 
-+-	/* force sequence number allocation for pending frames */
-+-	ath_tx_tid_change_state(sc, txtid);
-+-
-+ 	txtid->active = true;
-+ 	*ssn = txtid->seq_start = txtid->seq_next;
-+ 	txtid->bar_index = -1;
-+@@ -1565,7 +1578,6 @@ void ath_tx_aggr_stop(struct ath_softc *
-+ 	ath_txq_lock(sc, txq);
-+ 	txtid->active = false;
-+ 	ath_tx_flush_tid(sc, txtid);
-+-	ath_tx_tid_change_state(sc, txtid);
-+ 	ath_txq_unlock_complete(sc, txq);
-+ }
++ 	struct ath9k_hw_cal_data *caldata = ah->caldata;
++-	/* Cal is assumed not done until explicitly set below */
++-	bool iscaldone = false;
+++	const struct ath9k_percal_data *cur_caldata = currCal->calData;
 + 
-+@@ -1575,14 +1587,12 @@ void ath_tx_aggr_sleep(struct ieee80211_
-+ 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-+ 	struct ath_atx_tid *tid;
-+ 	struct ath_txq *txq;
-+-	bool buffered;
-+ 	int tidno;
++ 	/* Calibration in progress. */
++ 	if (currCal->calState == CAL_RUNNING) {
++ 		/* Check to see if it has finished. */
++-		if (!(REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)) {
++-			/*
++-			* Accumulate cal measures for active chains
++-			*/
++-			currCal->calData->calCollect(ah);
++-			ah->cal_samples++;
+++		if (REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)
+++			return false;
 + 
-+ 	ath_dbg(common, XMIT, "%s called\n", __func__);
++-			if (ah->cal_samples >=
++-			    currCal->calData->calNumSamples) {
++-				unsigned int i, numChains = 0;
++-				for (i = 0; i < AR9300_MAX_CHAINS; i++) {
++-					if (rxchainmask & (1 << i))
++-						numChains++;
++-				}
+++		/*
+++		* Accumulate cal measures for active chains
+++		*/
+++		cur_caldata->calCollect(ah);
+++		ah->cal_samples++;
 + 
-+-	for (tidno = 0, tid = &an->tid[tidno];
-+-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
-+-
-++	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
-++		tid = ath_node_to_tid(an, tidno);
-+ 		txq = tid->txq;
++-				/*
++-				* Process accumulated data
++-				*/
++-				currCal->calData->calPostProc(ah, numChains);
+++		if (ah->cal_samples >= cur_caldata->calNumSamples) {
+++			unsigned int i, numChains = 0;
+++			for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+++				if (rxchainmask & (1 << i))
+++					numChains++;
+++			}
 + 
-+ 		ath_txq_lock(sc, txq);
-+@@ -1592,13 +1602,12 @@ void ath_tx_aggr_sleep(struct ieee80211_
-+ 			continue;
++-				/* Calibration has finished. */
++-				caldata->CalValid |= currCal->calData->calType;
++-				currCal->calState = CAL_DONE;
++-				iscaldone = true;
++-			} else {
+++			/*
+++			* Process accumulated data
+++			*/
+++			cur_caldata->calPostProc(ah, numChains);
+++
+++			/* Calibration has finished. */
+++			caldata->CalValid |= cur_caldata->calType;
+++			currCal->calState = CAL_DONE;
+++			return true;
+++		} else {
++ 			/*
++ 			 * Set-up collection of another sub-sample until we
++ 			 * get desired number
++ 			 */
++ 			ar9003_hw_setup_calibration(ah, currCal);
++-			}
 + 		}
-+ 
-+-		buffered = ath_tid_has_buffered(tid);
-++		if (!skb_queue_empty(&tid->retry_q))
-++			ieee80211_sta_set_buffered(sta, tid->tidno, true);
-+ 
-+ 		list_del_init(&tid->list);
-+ 
-+ 		ath_txq_unlock(sc, txq);
-+-
-+-		ieee80211_sta_set_buffered(sta, tidno, buffered);
++-	} else if (!(caldata->CalValid & currCal->calData->calType)) {
+++	} else if (!(caldata->CalValid & cur_caldata->calType)) {
++ 		/* If current cal is marked invalid in channel, kick it off */
++ 		ath9k_hw_reset_calibration(ah, currCal);
 + 	}
-+ }
-+ 
-+@@ -1611,49 +1620,20 @@ void ath_tx_aggr_wakeup(struct ath_softc
-+ 
-+ 	ath_dbg(common, XMIT, "%s called\n", __func__);
 + 
-+-	for (tidno = 0, tid = &an->tid[tidno];
-+-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
-+-
-++	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
-++		tid = ath_node_to_tid(an, tidno);
-+ 		txq = tid->txq;
++-	return iscaldone;
+++	return false;
++ }
 + 
-+ 		ath_txq_lock(sc, txq);
-+ 		tid->clear_ps_filter = true;
-+-
-+ 		if (ath_tid_has_buffered(tid)) {
-+ 			ath_tx_queue_tid(sc, txq, tid);
-+ 			ath_txq_schedule(sc, txq);
-+ 		}
-+-
-+ 		ath_txq_unlock_complete(sc, txq);
++ static int ar9003_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
+diff --git a/package/kernel/mac80211/patches/327-ath9k_hw-get-rid-of-some-duplicate-code-in-calibrati.patch b/package/kernel/mac80211/patches/327-ath9k_hw-get-rid-of-some-duplicate-code-in-calibrati.patch
+new file mode 100644
+index 0000000..b7f3823
+--- /dev/null
++++ b/package/kernel/mac80211/patches/327-ath9k_hw-get-rid-of-some-duplicate-code-in-calibrati.patch
+@@ -0,0 +1,94 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Mon, 11 Jul 2016 11:35:20 +0200
++Subject: [PATCH] ath9k_hw: get rid of some duplicate code in calibration
++ init
++
++Remove a misleading debug message as well
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
++@@ -1373,6 +1373,26 @@ static void ar9003_hw_cl_cal_post_proc(s
 + 	}
 + }
 + 
-+-void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
-+-			u16 tidno)
-+-{
-+-	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-+-	struct ath_atx_tid *tid;
-+-	struct ath_node *an;
-+-	struct ath_txq *txq;
-+-
-+-	ath_dbg(common, XMIT, "%s called\n", __func__);
-+-
-+-	an = (struct ath_node *)sta->drv_priv;
-+-	tid = ATH_AN_2_TID(an, tidno);
-+-	txq = tid->txq;
-+-
-+-	ath_txq_lock(sc, txq);
-+-
-+-	tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
+++static void ar9003_hw_init_cal_common(struct ath_hw *ah)
+++{
+++	struct ath9k_hw_cal_data *caldata = ah->caldata;
+++
+++	/* Initialize list pointers */
+++	ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
+++
+++	INIT_CAL(&ah->iq_caldata);
+++	INSERT_CAL(ah, &ah->iq_caldata);
+++
+++	/* Initialize current pointer to first element in list */
+++	ah->cal_list_curr = ah->cal_list;
+++
+++	if (ah->cal_list_curr)
+++		ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
+++
+++	if (caldata)
+++		caldata->CalValid = 0;
+++}
+++
++ static bool ar9003_hw_init_cal_pcoem(struct ath_hw *ah,
++ 				     struct ath9k_channel *chan)
++ {
++@@ -1532,21 +1552,7 @@ skip_tx_iqcal:
++ 	/* Revert chainmask to runtime parameters */
++ 	ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
++ 
++-	/* Initialize list pointers */
++-	ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
 +-
-+-	if (ath_tid_has_buffered(tid)) {
-+-		ath_tx_queue_tid(sc, txq, tid);
-+-		ath_txq_schedule(sc, txq);
-+-	}
++-	INIT_CAL(&ah->iq_caldata);
++-	INSERT_CAL(ah, &ah->iq_caldata);
++-	ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n");
 +-
-+-	ath_txq_unlock_complete(sc, txq);
-+-}
++-	/* Initialize current pointer to first element in list */
++-	ah->cal_list_curr = ah->cal_list;
 +-
-+ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
-+ 				   struct ieee80211_sta *sta,
-+ 				   u16 tids, int nframes,
-+@@ -1666,7 +1646,6 @@ void ath9k_release_buffered_frames(struc
-+ 	struct ieee80211_tx_info *info;
-+ 	struct list_head bf_q;
-+ 	struct ath_buf *bf_tail = NULL, *bf;
-+-	struct sk_buff_head *tid_q;
-+ 	int sent = 0;
-+ 	int i;
-+ 
-+@@ -1681,11 +1660,10 @@ void ath9k_release_buffered_frames(struc
-+ 
-+ 		ath_txq_lock(sc, tid->txq);
-+ 		while (nframes > 0) {
-+-			bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid, &tid_q);
-++			bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid);
-+ 			if (!bf)
-+ 				break;
-+ 
-+-			__skb_unlink(bf->bf_mpdu, tid_q);
-+ 			list_add_tail(&bf->list, &bf_q);
-+ 			ath_set_rates(tid->an->vif, tid->an->sta, bf, true);
-+ 			if (bf_isampdu(bf)) {
-+@@ -1700,7 +1678,7 @@ void ath9k_release_buffered_frames(struc
-+ 			sent++;
-+ 			TX_STAT_INC(txq->axq_qnum, a_queued_hw);
-+ 
-+-			if (an->sta && !ath_tid_has_buffered(tid))
-++			if (an->sta && skb_queue_empty(&tid->retry_q))
-+ 				ieee80211_sta_set_buffered(an->sta, i, false);
-+ 		}
-+ 		ath_txq_unlock_complete(sc, tid->txq);
-+@@ -1929,13 +1907,7 @@ bool ath_drain_all_txq(struct ath_softc
-+ 		if (!ATH_TXQ_SETUP(sc, i))
-+ 			continue;
-+ 
-+-		/*
-+-		 * The caller will resume queues with ieee80211_wake_queues.
-+-		 * Mark the queue as not stopped to prevent ath_tx_complete
-+-		 * from waking the queue too early.
-+-		 */
-+ 		txq = &sc->tx.txq[i];
-+-		txq->stopped = false;
-+ 		ath_draintxq(sc, txq);
-+ 	}
-+ 
-+@@ -2334,16 +2306,14 @@ int ath_tx_start(struct ieee80211_hw *hw
-+ 	struct ath_softc *sc = hw->priv;
-+ 	struct ath_txq *txq = txctl->txq;
-+ 	struct ath_atx_tid *tid = NULL;
-++	struct ath_node *an = NULL;
-+ 	struct ath_buf *bf;
-+-	bool queue, ps_resp;
-++	bool ps_resp;
-+ 	int q, ret;
-+ 
-+ 	if (vif)
-+ 		avp = (void *)vif->drv_priv;
-+ 
-+-	if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
-+-		txctl->force_channel = true;
++-	if (ah->cal_list_curr)
++-		ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
 +-
-+ 	ps_resp = !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE);
-+ 
-+ 	ret = ath_tx_prepare(hw, skb, txctl);
-+@@ -2358,63 +2328,18 @@ int ath_tx_start(struct ieee80211_hw *hw
++-	if (caldata)
++-		caldata->CalValid = 0;
+++	ar9003_hw_init_cal_common(ah);
 + 
-+ 	q = skb_get_queue_mapping(skb);
++ 	return true;
++ }
++@@ -1577,8 +1583,6 @@ static bool do_ar9003_agc_cal(struct ath
++ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
++ 				   struct ath9k_channel *chan)
++ {
++-	struct ath_common *common = ath9k_hw_common(ah);
++-	struct ath9k_hw_cal_data *caldata = ah->caldata;
++ 	bool txiqcal_done = false;
++ 	bool status = true;
++ 	bool run_agc_cal = false, sep_iq_cal = false;
++@@ -1676,21 +1680,7 @@ skip_tx_iqcal:
++ 	/* Revert chainmask to runtime parameters */
++ 	ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
 + 
-+-	ath_txq_lock(sc, txq);
-+-	if (txq == sc->tx.txq_map[q]) {
-+-		fi->txq = q;
-+-		if (++txq->pending_frames > sc->tx.txq_max_pending[q] &&
-+-		    !txq->stopped) {
-+-			if (ath9k_is_chanctx_enabled())
-+-				ieee80211_stop_queue(sc->hw, info->hw_queue);
-+-			else
-+-				ieee80211_stop_queue(sc->hw, q);
-+-			txq->stopped = true;
-+-		}
-+-	}
-+-
-+-	queue = ieee80211_is_data_present(hdr->frame_control);
-+-
-+-	/* If chanctx, queue all null frames while NOA could be there */
-+-	if (ath9k_is_chanctx_enabled() &&
-+-	    ieee80211_is_nullfunc(hdr->frame_control) &&
-+-	    !txctl->force_channel)
-+-		queue = true;
++-	/* Initialize list pointers */
++-	ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL;
 +-
-+-	/* Force queueing of all frames that belong to a virtual interface on
-+-	 * a different channel context, to ensure that they are sent on the
-+-	 * correct channel.
-+-	 */
-+-	if (((avp && avp->chanctx != sc->cur_chan) ||
-+-	     sc->cur_chan->stopped) && !txctl->force_channel) {
-+-		if (!txctl->an)
-+-			txctl->an = &avp->mcast_node;
-+-		queue = true;
-+-		ps_resp = false;
-+-	}
++-	INIT_CAL(&ah->iq_caldata);
++-	INSERT_CAL(ah, &ah->iq_caldata);
++-	ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n");
 +-
-+-	if (txctl->an && queue)
-+-		tid = ath_get_skb_tid(sc, txctl->an, skb);
++-	/* Initialize current pointer to first element in list */
++-	ah->cal_list_curr = ah->cal_list;
 +-
-+-	if (ps_resp) {
-+-		ath_txq_unlock(sc, txq);
-++	if (ps_resp)
-+ 		txq = sc->tx.uapsdq;
-+-		ath_txq_lock(sc, txq);
-+-	} else if (txctl->an && queue) {
-+-		WARN_ON(tid->txq != txctl->txq);
++-	if (ah->cal_list_curr)
++-		ath9k_hw_reset_calibration(ah, ah->cal_list_curr);
 +-
-+-		if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
-+-			tid->clear_ps_filter = true;
++-	if (caldata)
++-		caldata->CalValid = 0;
+++	ar9003_hw_init_cal_common(ah);
++ 
++ 	return true;
++ }
+diff --git a/package/kernel/mac80211/patches/327-mac80211-Remove-MPP-table-entries-with-MPath.patch b/package/kernel/mac80211/patches/327-mac80211-Remove-MPP-table-entries-with-MPath.patch
+deleted file mode 100644
+index f7f9df9..0000000
+--- a/package/kernel/mac80211/patches/327-mac80211-Remove-MPP-table-entries-with-MPath.patch
++++ /dev/null
+@@ -1,54 +0,0 @@
+-From: Henning Rogge <hrogge@gmail.com>
+-Date: Wed, 3 Feb 2016 13:58:36 +0100
+-Subject: [PATCH] mac80211: Remove MPP table entries with MPath
+-
+-Make the mesh_path_del() function remove all mpp table entries
+-that are proxied by the removed mesh path.
+-
+-Acked-by: Bob Copeland <me@bobcopeland.com>
+-Signed-off-by: Henning Rogge <henning.rogge@fkie.fraunhofer.de>
+-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+----
+-
+---- a/net/mac80211/mesh_pathtbl.c
+-+++ b/net/mac80211/mesh_pathtbl.c
+-@@ -835,6 +835,29 @@ void mesh_path_flush_by_nexthop(struct s
+- 	rcu_read_unlock();
+- }
+- 
+-+static void mpp_flush_by_proxy(struct ieee80211_sub_if_data *sdata,
+-+			       const u8 *proxy)
+-+{
+-+	struct mesh_table *tbl;
+-+	struct mesh_path *mpp;
+-+	struct mpath_node *node;
+-+	int i;
+-+
+-+	rcu_read_lock();
+-+	read_lock_bh(&pathtbl_resize_lock);
+-+	tbl = resize_dereference_mpp_paths();
+-+	for_each_mesh_entry(tbl, node, i) {
+-+		mpp = node->mpath;
+-+		if (ether_addr_equal(mpp->mpp, proxy)) {
+-+			spin_lock(&tbl->hashwlock[i]);
+-+			__mesh_path_del(tbl, node);
+-+			spin_unlock(&tbl->hashwlock[i]);
+-+		}
+-+	}
+-+	read_unlock_bh(&pathtbl_resize_lock);
+-+	rcu_read_unlock();
+-+}
+-+
+- static void table_flush_by_iface(struct mesh_table *tbl,
+- 				 struct ieee80211_sub_if_data *sdata)
+- {
+-@@ -892,6 +915,9 @@ int mesh_path_del(struct ieee80211_sub_i
+- 	int hash_idx;
+- 	int err = 0;
+- 
+-+	/* flush relevant mpp entries first */
+-+	mpp_flush_by_proxy(sdata, addr);
+-+
+- 	read_lock_bh(&pathtbl_resize_lock);
+- 	tbl = resize_dereference_mesh_paths();
+- 	hash_idx = mesh_table_hash(addr, sdata, tbl);
+diff --git a/package/kernel/mac80211/patches/328-ath9k_hw-implement-temperature-compensation-support-.patch b/package/kernel/mac80211/patches/328-ath9k_hw-implement-temperature-compensation-support-.patch
+new file mode 100644
+index 0000000..cff32ad
+--- /dev/null
++++ b/package/kernel/mac80211/patches/328-ath9k_hw-implement-temperature-compensation-support-.patch
+@@ -0,0 +1,97 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Mon, 11 Jul 2016 11:35:55 +0200
++Subject: [PATCH] ath9k_hw: implement temperature compensation support for
++ AR9003+
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
++@@ -33,6 +33,7 @@ struct coeff {
++ 
++ enum ar9003_cal_types {
++ 	IQ_MISMATCH_CAL = BIT(0),
+++	TEMP_COMP_CAL = BIT(1),
++ };
 + 
-+-		/*
-+-		 * Add this frame to software queue for scheduling later
-+-		 * for aggregation.
-+-		 */
-+-		TX_STAT_INC(txq->axq_qnum, a_queued_sw);
-+-		__skb_queue_tail(&tid->buf_q, skb);
-+-		if (!txctl->an->sleeping)
-+-			ath_tx_queue_tid(sc, txq, tid);
-++	if (txctl->sta) {
-++		an = (struct ath_node *) sta->drv_priv;
-++		tid = ath_get_skb_tid(sc, an, skb);
-++	}
++ static void ar9003_hw_setup_calibration(struct ath_hw *ah,
++@@ -58,6 +59,12 @@ static void ar9003_hw_setup_calibration(
++ 		/* Kick-off cal */
++ 		REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL);
++ 		break;
+++	case TEMP_COMP_CAL:
+++		ath_dbg(common, CALIBRATE,
+++			"starting Temperature Compensation Calibration\n");
+++		REG_SET_BIT(ah, AR_CH0_THERM, AR_CH0_THERM_LOCAL);
+++		REG_SET_BIT(ah, AR_CH0_THERM, AR_CH0_THERM_START);
+++		break;
++ 	default:
++ 		ath_err(common, "Invalid calibration type\n");
++ 		break;
++@@ -86,7 +93,8 @@ static bool ar9003_hw_per_calibration(st
++ 		/*
++ 		* Accumulate cal measures for active chains
++ 		*/
++-		cur_caldata->calCollect(ah);
+++		if (cur_caldata->calCollect)
+++			cur_caldata->calCollect(ah);
++ 		ah->cal_samples++;
++ 
++ 		if (ah->cal_samples >= cur_caldata->calNumSamples) {
++@@ -99,7 +107,8 @@ static bool ar9003_hw_per_calibration(st
++ 			/*
++ 			* Process accumulated data
++ 			*/
++-			cur_caldata->calPostProc(ah, numChains);
+++			if (cur_caldata->calPostProc)
+++				cur_caldata->calPostProc(ah, numChains);
++ 
++ 			/* Calibration has finished. */
++ 			caldata->CalValid |= cur_caldata->calType;
++@@ -314,9 +323,16 @@ static const struct ath9k_percal_data iq
++ 	ar9003_hw_iqcalibrate
++ };
 + 
-+-		ath_txq_schedule(sc, txq);
-+-		goto out;
-++	ath_txq_lock(sc, txq);
-++	if (txq == sc->tx.txq_map[q]) {
-++		fi->txq = q;
-++		++txq->pending_frames;
+++static const struct ath9k_percal_data temp_cal_single_sample = {
+++	TEMP_COMP_CAL,
+++	MIN_CAL_SAMPLES,
+++	PER_MAX_LOG_COUNT,
+++};
+++
++ static void ar9003_hw_init_cal_settings(struct ath_hw *ah)
++ {
++ 	ah->iq_caldata.calData = &iq_cal_single_sample;
+++	ah->temp_caldata.calData = &temp_cal_single_sample;
++ 
++ 	if (AR_SREV_9300_20_OR_LATER(ah)) {
++ 		ah->enabled_cals |= TX_IQ_CAL;
++@@ -324,7 +340,7 @@ static void ar9003_hw_init_cal_settings(
++ 			ah->enabled_cals |= TX_IQ_ON_AGC_CAL;
 + 	}
 + 
-+ 	bf = ath_tx_setup_buffer(sc, txq, tid, skb);
-+@@ -2907,9 +2832,8 @@ void ath_tx_node_init(struct ath_softc *
-+ 	struct ath_atx_tid *tid;
-+ 	int tidno, acno;
++-	ah->supp_cals = IQ_MISMATCH_CAL;
+++	ah->supp_cals = IQ_MISMATCH_CAL | TEMP_COMP_CAL;
++ }
++ 
++ #define OFF_UPPER_LT 24
++@@ -1383,6 +1399,9 @@ static void ar9003_hw_init_cal_common(st
++ 	INIT_CAL(&ah->iq_caldata);
++ 	INSERT_CAL(ah, &ah->iq_caldata);
++ 
+++	INIT_CAL(&ah->temp_caldata);
+++	INSERT_CAL(ah, &ah->temp_caldata);
+++
++ 	/* Initialize current pointer to first element in list */
++ 	ah->cal_list_curr = ah->cal_list;
++ 
++--- a/drivers/net/wireless/ath/ath9k/hw.h
+++++ b/drivers/net/wireless/ath/ath9k/hw.h
++@@ -830,6 +830,7 @@ struct ath_hw {
++ 	/* Calibration */
++ 	u32 supp_cals;
++ 	struct ath9k_cal_list iq_caldata;
+++	struct ath9k_cal_list temp_caldata;
++ 	struct ath9k_cal_list adcgain_caldata;
++ 	struct ath9k_cal_list adcdc_caldata;
++ 	struct ath9k_cal_list *cal_list;
+diff --git a/package/kernel/mac80211/patches/328-mac80211-let-unused-MPP-table-entries-timeout.patch b/package/kernel/mac80211/patches/328-mac80211-let-unused-MPP-table-entries-timeout.patch
+deleted file mode 100644
+index 740993c..0000000
+--- a/package/kernel/mac80211/patches/328-mac80211-let-unused-MPP-table-entries-timeout.patch
++++ /dev/null
+@@ -1,104 +0,0 @@
+-From: Henning Rogge <hrogge@gmail.com>
+-Date: Wed, 3 Feb 2016 13:58:37 +0100
+-Subject: [PATCH] mac80211: let unused MPP table entries timeout
+-
+-Remember the last time when a mpp table entry is used for
+-rx or tx and remove them after MESH_PATH_EXPIRE time.
+-
+-Acked-by: Bob Copeland <me@bobcopeland.com>
+-Signed-off-by: Henning Rogge <henning.rogge@fkie.fraunhofer.de>
+-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+----
+-
+---- a/net/mac80211/mesh_pathtbl.c
+-+++ b/net/mac80211/mesh_pathtbl.c
+-@@ -942,6 +942,46 @@ enddel:
+- }
+- 
+- /**
+-+ * mpp_path_del - delete a mesh proxy path from the table
+-+ *
+-+ * @addr: addr address (ETH_ALEN length)
+-+ * @sdata: local subif
+-+ *
+-+ * Returns: 0 if successful
+-+ */
+-+static int mpp_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+-+{
+-+	struct mesh_table *tbl;
+-+	struct mesh_path *mpath;
+-+	struct mpath_node *node;
+-+	struct hlist_head *bucket;
+-+	int hash_idx;
+-+	int err = 0;
+-+
+-+	read_lock_bh(&pathtbl_resize_lock);
+-+	tbl = resize_dereference_mpp_paths();
+-+	hash_idx = mesh_table_hash(addr, sdata, tbl);
+-+	bucket = &tbl->hash_buckets[hash_idx];
+-+
+-+	spin_lock(&tbl->hashwlock[hash_idx]);
+-+	hlist_for_each_entry(node, bucket, list) {
+-+		mpath = node->mpath;
+-+		if (mpath->sdata == sdata &&
+-+		    ether_addr_equal(addr, mpath->dst)) {
+-+			__mesh_path_del(tbl, node);
+-+			goto enddel;
+-+		}
+-+	}
+-+
+-+	err = -ENXIO;
+-+enddel:
+-+	mesh_paths_generation++;
+-+	spin_unlock(&tbl->hashwlock[hash_idx]);
+-+	read_unlock_bh(&pathtbl_resize_lock);
+-+	return err;
+-+}
+-+
+-+/**
+-  * mesh_path_tx_pending - sends pending frames in a mesh path queue
+-  *
+-  * @mpath: mesh path to activate
+-@@ -1157,6 +1197,17 @@ void mesh_path_expire(struct ieee80211_s
+- 		     time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
+- 			mesh_path_del(mpath->sdata, mpath->dst);
+- 	}
+-+
+-+	tbl = rcu_dereference(mpp_paths);
+-+	for_each_mesh_entry(tbl, node, i) {
+-+		if (node->mpath->sdata != sdata)
+-+			continue;
+-+		mpath = node->mpath;
+-+		if ((!(mpath->flags & MESH_PATH_FIXED)) &&
+-+		    time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE))
+-+			mpp_path_del(mpath->sdata, mpath->dst);
+-+	}
+-+
+- 	rcu_read_unlock();
+- }
+- 
+---- a/net/mac80211/rx.c
+-+++ b/net/mac80211/rx.c
+-@@ -2291,6 +2291,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
+- 			spin_lock_bh(&mppath->state_lock);
+- 			if (!ether_addr_equal(mppath->mpp, mpp_addr))
+- 				memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
+-+			mppath->exp_time = jiffies;
+- 			spin_unlock_bh(&mppath->state_lock);
+- 		}
+- 		rcu_read_unlock();
+---- a/net/mac80211/tx.c
+-+++ b/net/mac80211/tx.c
+-@@ -2171,8 +2171,11 @@ static struct sk_buff *ieee80211_build_h
+- 					mpp_lookup = true;
+- 			}
+- 
+--			if (mpp_lookup)
+-+			if (mpp_lookup) {
+- 				mppath = mpp_path_lookup(sdata, skb->data);
+-+				if (mppath)
+-+					mppath->exp_time = jiffies;
+-+			}
+- 
+- 			if (mppath && mpath)
+- 				mesh_path_del(mpath->sdata, mpath->dst);
+diff --git a/package/kernel/mac80211/patches/329-mac80211-Unify-mesh-and-mpp-path-removal-function.patch b/package/kernel/mac80211/patches/329-mac80211-Unify-mesh-and-mpp-path-removal-function.patch
+deleted file mode 100644
+index 0c36b1d..0000000
+--- a/package/kernel/mac80211/patches/329-mac80211-Unify-mesh-and-mpp-path-removal-function.patch
++++ /dev/null
+@@ -1,143 +0,0 @@
+-From: Henning Rogge <hrogge@gmail.com>
+-Date: Wed, 3 Feb 2016 13:58:38 +0100
+-Subject: [PATCH] mac80211: Unify mesh and mpp path removal function
+-
+-mpp_path_del() and mesh_path_del() are mostly the same function.
+-Move common code into a new static function.
+-
+-Acked-by: Bob Copeland <me@bobcopeland.com>
+-Signed-off-by: Henning Rogge <henning.rogge@fkie.fraunhofer.de>
+-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+----
+-
+---- a/net/mac80211/mesh_pathtbl.c
+-+++ b/net/mac80211/mesh_pathtbl.c
+-@@ -55,16 +55,21 @@ int mpp_paths_generation;
+- static DEFINE_RWLOCK(pathtbl_resize_lock);
+- 
+- 
+-+static inline struct mesh_table *resize_dereference_paths(
+-+	struct mesh_table __rcu *table)
+-+{
+-+	return rcu_dereference_protected(table,
+-+					lockdep_is_held(&pathtbl_resize_lock));
+-+}
+-+
+- static inline struct mesh_table *resize_dereference_mesh_paths(void)
+- {
+--	return rcu_dereference_protected(mesh_paths,
+--		lockdep_is_held(&pathtbl_resize_lock));
+-+	return resize_dereference_paths(mesh_paths);
+- }
+- 
+- static inline struct mesh_table *resize_dereference_mpp_paths(void)
+- {
+--	return rcu_dereference_protected(mpp_paths,
+--		lockdep_is_held(&pathtbl_resize_lock));
+-+	return resize_dereference_paths(mpp_paths);
+- }
+- 
+- /*
+-@@ -899,14 +904,17 @@ void mesh_path_flush_by_iface(struct iee
+- }
+- 
+- /**
+-- * mesh_path_del - delete a mesh path from the table
+-+ * table_path_del - delete a path from the mesh or mpp table
+-  *
+-- * @addr: dst address (ETH_ALEN length)
+-+ * @tbl: mesh or mpp path table
+-  * @sdata: local subif
+-+ * @addr: dst address (ETH_ALEN length)
+-  *
+-  * Returns: 0 if successful
+-  */
+--int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+-+static int table_path_del(struct mesh_table __rcu *rcu_tbl,
+-+			  struct ieee80211_sub_if_data *sdata,
+-+			  const u8 *addr)
+- {
+- 	struct mesh_table *tbl;
+- 	struct mesh_path *mpath;
+-@@ -915,11 +923,7 @@ int mesh_path_del(struct ieee80211_sub_i
+- 	int hash_idx;
+- 	int err = 0;
+- 
+--	/* flush relevant mpp entries first */
+--	mpp_flush_by_proxy(sdata, addr);
+--
+--	read_lock_bh(&pathtbl_resize_lock);
+--	tbl = resize_dereference_mesh_paths();
+-+	tbl = resize_dereference_paths(rcu_tbl);
+- 	hash_idx = mesh_table_hash(addr, sdata, tbl);
+- 	bucket = &tbl->hash_buckets[hash_idx];
+- 
+-@@ -935,9 +939,30 @@ int mesh_path_del(struct ieee80211_sub_i
+- 
+- 	err = -ENXIO;
+- enddel:
+--	mesh_paths_generation++;
+- 	spin_unlock(&tbl->hashwlock[hash_idx]);
+-+	return err;
+-+}
+-+
+-+/**
+-+ * mesh_path_del - delete a mesh path from the table
+-+ *
+-+ * @addr: dst address (ETH_ALEN length)
+-+ * @sdata: local subif
+-+ *
+-+ * Returns: 0 if successful
+-+ */
+-+int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+-+{
+-+	int err = 0;
+-+
+-+	/* flush relevant mpp entries first */
+-+	mpp_flush_by_proxy(sdata, addr);
+-+
+-+	read_lock_bh(&pathtbl_resize_lock);
+-+	err = table_path_del(mesh_paths, sdata, addr);
+-+	mesh_paths_generation++;
+- 	read_unlock_bh(&pathtbl_resize_lock);
+-+
+- 	return err;
+- }
+- 
+-@@ -951,33 +976,13 @@ enddel:
+-  */
+- static int mpp_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr)
+- {
+--	struct mesh_table *tbl;
+--	struct mesh_path *mpath;
+--	struct mpath_node *node;
+--	struct hlist_head *bucket;
+--	int hash_idx;
+- 	int err = 0;
+- 
+- 	read_lock_bh(&pathtbl_resize_lock);
+--	tbl = resize_dereference_mpp_paths();
+--	hash_idx = mesh_table_hash(addr, sdata, tbl);
+--	bucket = &tbl->hash_buckets[hash_idx];
+--
+--	spin_lock(&tbl->hashwlock[hash_idx]);
+--	hlist_for_each_entry(node, bucket, list) {
+--		mpath = node->mpath;
+--		if (mpath->sdata == sdata &&
+--		    ether_addr_equal(addr, mpath->dst)) {
+--			__mesh_path_del(tbl, node);
+--			goto enddel;
+--		}
+--	}
+--
+--	err = -ENXIO;
+--enddel:
+--	mesh_paths_generation++;
+--	spin_unlock(&tbl->hashwlock[hash_idx]);
+-+	err = table_path_del(mpp_paths, sdata, addr);
+-+	mpp_paths_generation++;
+- 	read_unlock_bh(&pathtbl_resize_lock);
+-+
+- 	return err;
+- }
+- 
+diff --git a/package/kernel/mac80211/patches/329-mac80211-fix-check-for-buffered-powersave-frames-wit.patch b/package/kernel/mac80211/patches/329-mac80211-fix-check-for-buffered-powersave-frames-wit.patch
+new file mode 100644
+index 0000000..38e541c
+--- /dev/null
++++ b/package/kernel/mac80211/patches/329-mac80211-fix-check-for-buffered-powersave-frames-wit.patch
+@@ -0,0 +1,21 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Mon, 11 Jul 2016 15:07:06 +0200
++Subject: [PATCH] mac80211: fix check for buffered powersave frames with txq
++
++The logic was inverted here, set the bit if frames are pending.
++
++Fixes: ba8c3d6f16a1 ("mac80211: add an intermediate software queue implementation")
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/net/mac80211/rx.c
+++++ b/net/mac80211/rx.c
++@@ -1268,7 +1268,7 @@ static void sta_ps_start(struct sta_info
++ 	for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
++ 		struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
++ 
++-		if (!txqi->tin.backlog_packets)
+++		if (txqi->tin.backlog_packets)
++ 			set_bit(tid, &sta->txq_buffered_tids);
++ 		else
++ 			clear_bit(tid, &sta->txq_buffered_tids);
+diff --git a/package/kernel/mac80211/patches/330-ath10k-fix-rx-status-reporting-for-A-MSDU-subframes.patch b/package/kernel/mac80211/patches/330-ath10k-fix-rx-status-reporting-for-A-MSDU-subframes.patch
+new file mode 100644
+index 0000000..a6031b9
+--- /dev/null
++++ b/package/kernel/mac80211/patches/330-ath10k-fix-rx-status-reporting-for-A-MSDU-subframes.patch
+@@ -0,0 +1,36 @@
++From: Felix Fietkau <nbd@nbd.name>
++Date: Sun, 17 Jul 2016 12:49:59 +0200
++Subject: [PATCH] ath10k: fix rx status reporting for A-MSDU subframes
++
++Patch by Nagarajan, Ashok Raj <arnagara@qti.qualcomm.com>
++
++Signed-off-by: Felix Fietkau <nbd@nbd.name>
++---
++
++--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
++@@ -1525,7 +1525,7 @@ static void ath10k_htt_rx_h_filter(struc
++ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
++ {
++ 	struct ath10k *ar = htt->ar;
++-	static struct ieee80211_rx_status rx_status;
+++	struct ieee80211_rx_status *rx_status = &htt->rx_status;
++ 	struct sk_buff_head amsdu;
++ 	int ret;
 + 
-+-	for (tidno = 0, tid = &an->tid[tidno];
-+-	     tidno < IEEE80211_NUM_TIDS;
-+-	     tidno++, tid++) {
-++	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
-++		tid = ath_node_to_tid(an, tidno);
-+ 		tid->an        = an;
-+ 		tid->tidno     = tidno;
-+ 		tid->seq_start = tid->seq_next = 0;
-+@@ -2917,11 +2841,14 @@ void ath_tx_node_init(struct ath_softc *
-+ 		tid->baw_head  = tid->baw_tail = 0;
-+ 		tid->active	   = false;
-+ 		tid->clear_ps_filter = true;
-+-		__skb_queue_head_init(&tid->buf_q);
-++		tid->has_queued  = false;
-+ 		__skb_queue_head_init(&tid->retry_q);
-+ 		INIT_LIST_HEAD(&tid->list);
-+ 		acno = TID_TO_WME_AC(tidno);
-+ 		tid->txq = sc->tx.txq_map[acno];
-++
-++		if (!an->sta)
-++			break; /* just one multicast ath_atx_tid */
++@@ -1549,11 +1549,11 @@ static int ath10k_htt_rx_handle_amsdu(st
++ 		return ret;
 + 	}
-+ }
-+ 
-+@@ -2931,9 +2858,8 @@ void ath_tx_node_cleanup(struct ath_soft
-+ 	struct ath_txq *txq;
-+ 	int tidno;
 + 
-+-	for (tidno = 0, tid = &an->tid[tidno];
-+-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
-+-
-++	for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
-++		tid = ath_node_to_tid(an, tidno);
-+ 		txq = tid->txq;
-+ 
-+ 		ath_txq_lock(sc, txq);
-+@@ -2945,6 +2871,9 @@ void ath_tx_node_cleanup(struct ath_soft
-+ 		tid->active = false;
++-	ath10k_htt_rx_h_ppdu(ar, &amsdu, &rx_status, 0xffff);
+++	ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
++ 	ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
++-	ath10k_htt_rx_h_filter(ar, &amsdu, &rx_status);
++-	ath10k_htt_rx_h_mpdu(ar, &amsdu, &rx_status);
++-	ath10k_htt_rx_h_deliver(ar, &amsdu, &rx_status);
+++	ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
+++	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+++	ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
 + 
-+ 		ath_txq_unlock(sc, txq);
-++
-++		if (!an->sta)
-++			break; /* just one multicast ath_atx_tid */
-+ 	}
++ 	return 0;
 + }
-+ 
+diff --git a/package/kernel/mac80211/patches/330-mac80211-minstrel-Change-expected-throughput-unit-ba.patch b/package/kernel/mac80211/patches/330-mac80211-minstrel-Change-expected-throughput-unit-ba.patch
+deleted file mode 100644
+index 4dc6d66..0000000
+--- a/package/kernel/mac80211/patches/330-mac80211-minstrel-Change-expected-throughput-unit-ba.patch
++++ /dev/null
+@@ -1,51 +0,0 @@
+-From: Sven Eckelmann <sven.eckelmann@open-mesh.com>
+-Date: Tue, 2 Feb 2016 08:12:26 +0100
+-Subject: [PATCH] mac80211: minstrel: Change expected throughput unit back to
+- Kbps
+-
+-The change from cur_tp to the function
+-minstrel_get_tp_avg/minstrel_ht_get_tp_avg changed the unit used for the
+-current throughput. For example in minstrel_ht the correct
+-conversion between them would be:
+-
+-    mrs->cur_tp / 10 == minstrel_ht_get_tp_avg(..).
+-
+-This factor 10 must also be included in the calculation of
+-minstrel_get_expected_throughput and minstrel_ht_get_expected_throughput to
+-return values with the unit [Kbps] instead of [10Kbps]. Otherwise routing
+-algorithms like B.A.T.M.A.N. V will make incorrect decision based on these
+-values. Its kernel based implementation expects expected_throughput always
+-to have the unit [Kbps] and not sometimes [10Kbps] and sometimes [Kbps].
+-
+-The same requirement has iw or olsrdv2's nl80211 based statistics module
+-which retrieve the same data via NL80211_STA_INFO_TX_BITRATE.
+-
+-Cc: stable@vger.kernel.org
+-Fixes: 6a27b2c40b48 ("mac80211: restructure per-rate throughput calculation into function")
+-Signed-off-by: Sven Eckelmann <sven@open-mesh.com>
+-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+----
+-
+---- a/net/mac80211/rc80211_minstrel.c
+-+++ b/net/mac80211/rc80211_minstrel.c
+-@@ -711,7 +711,7 @@ static u32 minstrel_get_expected_through
+- 	 * computing cur_tp
+- 	 */
+- 	tmp_mrs = &mi->r[idx].stats;
+--	tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma);
+-+	tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma) * 10;
+- 	tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024;
+- 
+- 	return tmp_cur_tp;
+---- a/net/mac80211/rc80211_minstrel_ht.c
+-+++ b/net/mac80211/rc80211_minstrel_ht.c
+-@@ -1335,7 +1335,8 @@ static u32 minstrel_ht_get_expected_thro
+- 	prob = mi->groups[i].rates[j].prob_ewma;
+- 
+- 	/* convert tp_avg from pkt per second in kbps */
+--	tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * AVG_PKT_SIZE * 8 / 1024;
+-+	tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * 10;
+-+	tp_avg = tp_avg * AVG_PKT_SIZE * 8 / 1024;
+- 
+- 	return tp_avg;
+- }
+diff --git a/package/kernel/mac80211/patches/331-brcmfmac-Increase-nr-of-supported-flowrings.patch b/package/kernel/mac80211/patches/331-brcmfmac-Increase-nr-of-supported-flowrings.patch
+deleted file mode 100644
+index 1fd016f..0000000
+--- a/package/kernel/mac80211/patches/331-brcmfmac-Increase-nr-of-supported-flowrings.patch
++++ /dev/null
+@@ -1,307 +0,0 @@
+-From: Hante Meuleman <meuleman@broadcom.com>
+-Date: Sun, 7 Feb 2016 18:08:24 +0100
+-Subject: [PATCH] brcmfmac: Increase nr of supported flowrings.
+-MIME-Version: 1.0
+-Content-Type: text/plain; charset=UTF-8
+-Content-Transfer-Encoding: 8bit
+-
+-New generation devices have firmware which has more than 256 flowrings.
+-E.g. following debugging message comes from 14e4:4365 BCM4366:
+-[  194.606245] brcmfmac: brcmf_pcie_init_ringbuffers Nr of flowrings is 264
+-
+-At various code places (related to flowrings) we were using u8 which
+-could lead to storing wrong number or infinite loops when indexing with
+-this type. This issue was quite easy to spot in brcmf_flowring_detach
+-where it led to infinite loop e.g. on failed initialization.
+-
+-This patch switches code to proper types and increases the maximum
+-number of supported flowrings to 512.
+-
+-Originally this change was sent in September 2015, but back it was
+-causing a regression on BCM43602 resulting in:
+-Unable to handle kernel NULL pointer dereference at virtual address ...
+-
+-The reason for this regression was missing update (s/u8/u16) of struct
+-brcmf_flowring_ring. This problem was handled in 9f64df9 ("brcmfmac: Fix
+-bug in flowring management."). Starting with that it's safe to apply
+-this original patch as it doesn't cause a regression anymore.
+-
+-This patch fixes an infinite loop on BCM4366 which is supported since
+-4.4 so it makes sense to apply it to stable 4.4+.
+-
+-Cc: <stable@vger.kernel.org> # 4.4+
+-Reviewed-by: Arend Van Spriel <arend@broadcom.com>
+-Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
+-Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
+-Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
+-Signed-off-by: Arend van Spriel <arend@broadcom.com>
+-Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+----
+-
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
+-@@ -32,7 +32,7 @@
+- #define BRCMF_FLOWRING_LOW		(BRCMF_FLOWRING_HIGH - 256)
+- #define BRCMF_FLOWRING_INVALID_IFIDX	0xff
+- 
+--#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16)
+-+#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] * 2 + fifo + ifidx * 16)
+- #define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16)
+- 
+- static const u8 brcmf_flowring_prio2fifo[] = {
+-@@ -68,7 +68,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f
+- 			  u8 prio, u8 ifidx)
+- {
+- 	struct brcmf_flowring_hash *hash;
+--	u8 hash_idx;
+-+	u16 hash_idx;
+- 	u32 i;
+- 	bool found;
+- 	bool sta;
+-@@ -88,6 +88,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f
+- 	}
+- 	hash_idx =  sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
+- 			  BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
+-+	hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
+- 	found = false;
+- 	hash = flow->hash;
+- 	for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
+-@@ -98,6 +99,7 @@ u32 brcmf_flowring_lookup(struct brcmf_f
+- 			break;
+- 		}
+- 		hash_idx++;
+-+		hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
+- 	}
+- 	if (found)
+- 		return hash[hash_idx].flowid;
+-@@ -111,7 +113,7 @@ u32 brcmf_flowring_create(struct brcmf_f
+- {
+- 	struct brcmf_flowring_ring *ring;
+- 	struct brcmf_flowring_hash *hash;
+--	u8 hash_idx;
+-+	u16 hash_idx;
+- 	u32 i;
+- 	bool found;
+- 	u8 fifo;
+-@@ -131,6 +133,7 @@ u32 brcmf_flowring_create(struct brcmf_f
+- 	}
+- 	hash_idx =  sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
+- 			  BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
+-+	hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
+- 	found = false;
+- 	hash = flow->hash;
+- 	for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
+-@@ -140,6 +143,7 @@ u32 brcmf_flowring_create(struct brcmf_f
+- 			break;
+- 		}
+- 		hash_idx++;
+-+		hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);
+- 	}
+- 	if (found) {
+- 		for (i = 0; i < flow->nrofrings; i++) {
+-@@ -169,7 +173,7 @@ u32 brcmf_flowring_create(struct brcmf_f
+- }
+- 
+- 
+--u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid)
+-+u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid)
+- {
+- 	struct brcmf_flowring_ring *ring;
+- 
+-@@ -179,7 +183,7 @@ u8 brcmf_flowring_tid(struct brcmf_flowr
+- }
+- 
+- 
+--static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid,
+-+static void brcmf_flowring_block(struct brcmf_flowring *flow, u16 flowid,
+- 				 bool blocked)
+- {
+- 	struct brcmf_flowring_ring *ring;
+-@@ -228,10 +232,10 @@ static void brcmf_flowring_block(struct
+- }
+- 
+- 
+--void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid)
+-+void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid)
+- {
+- 	struct brcmf_flowring_ring *ring;
+--	u8 hash_idx;
+-+	u16 hash_idx;
+- 	struct sk_buff *skb;
+- 
+- 	ring = flow->rings[flowid];
+-@@ -253,7 +257,7 @@ void brcmf_flowring_delete(struct brcmf_
+- }
+- 
+- 
+--u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid,
+-+u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,
+- 			   struct sk_buff *skb)
+- {
+- 	struct brcmf_flowring_ring *ring;
+-@@ -279,7 +283,7 @@ u32 brcmf_flowring_enqueue(struct brcmf_
+- }
+- 
+- 
+--struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid)
+-+struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid)
+- {
+- 	struct brcmf_flowring_ring *ring;
+- 	struct sk_buff *skb;
+-@@ -300,7 +304,7 @@ struct sk_buff *brcmf_flowring_dequeue(s
+- }
+- 
+- 
+--void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid,
+-+void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,
+- 			     struct sk_buff *skb)
+- {
+- 	struct brcmf_flowring_ring *ring;
+-@@ -311,7 +315,7 @@ void brcmf_flowring_reinsert(struct brcm
+- }
+- 
+- 
+--u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid)
+-+u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid)
+- {
+- 	struct brcmf_flowring_ring *ring;
+- 
+-@@ -326,7 +330,7 @@ u32 brcmf_flowring_qlen(struct brcmf_flo
+- }
+- 
+- 
+--void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid)
+-+void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid)
+- {
+- 	struct brcmf_flowring_ring *ring;
+- 
+-@@ -340,10 +344,10 @@ void brcmf_flowring_open(struct brcmf_fl
+- }
+- 
+- 
+--u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid)
+-+u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid)
+- {
+- 	struct brcmf_flowring_ring *ring;
+--	u8 hash_idx;
+-+	u16 hash_idx;
+- 
+- 	ring = flow->rings[flowid];
+- 	hash_idx = ring->hash_id;
+-@@ -384,7 +388,7 @@ void brcmf_flowring_detach(struct brcmf_
+- 	struct brcmf_pub *drvr = bus_if->drvr;
+- 	struct brcmf_flowring_tdls_entry *search;
+- 	struct brcmf_flowring_tdls_entry *remove;
+--	u8 flowid;
+-+	u16 flowid;
+- 
+- 	for (flowid = 0; flowid < flow->nrofrings; flowid++) {
+- 		if (flow->rings[flowid])
+-@@ -408,7 +412,7 @@ void brcmf_flowring_configure_addr_mode(
+- 	struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);
+- 	struct brcmf_pub *drvr = bus_if->drvr;
+- 	u32 i;
+--	u8 flowid;
+-+	u16 flowid;
+- 
+- 	if (flow->addr_mode[ifidx] != addr_mode) {
+- 		for (i = 0; i < ARRAY_SIZE(flow->hash); i++) {
+-@@ -434,7 +438,7 @@ void brcmf_flowring_delete_peer(struct b
+- 	struct brcmf_flowring_tdls_entry *prev;
+- 	struct brcmf_flowring_tdls_entry *search;
+- 	u32 i;
+--	u8 flowid;
+-+	u16 flowid;
+- 	bool sta;
+- 
+- 	sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
+-@@ -16,7 +16,7 @@
+- #define BRCMFMAC_FLOWRING_H
+- 
+- 
+--#define BRCMF_FLOWRING_HASHSIZE		256
+-+#define BRCMF_FLOWRING_HASHSIZE		512		/* has to be 2^x */
+- #define BRCMF_FLOWRING_INVALID_ID	0xFFFFFFFF
+- 
+- 
+-@@ -24,7 +24,7 @@ struct brcmf_flowring_hash {
+- 	u8 mac[ETH_ALEN];
+- 	u8 fifo;
+- 	u8 ifidx;
+--	u8 flowid;
+-+	u16 flowid;
+- };
+- 
+- enum ring_status {
+-@@ -61,16 +61,16 @@ u32 brcmf_flowring_lookup(struct brcmf_f
+- 			  u8 prio, u8 ifidx);
+- u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
+- 			  u8 prio, u8 ifidx);
+--void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid);
+--void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid);
+--u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid);
+--u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid,
+-+void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid);
+-+void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid);
+-+u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid);
+-+u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,
+- 			   struct sk_buff *skb);
+--struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid);
+--void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid,
+-+struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid);
+-+void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,
+- 			     struct sk_buff *skb);
+--u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u8 flowid);
+--u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u8 flowid);
+-+u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid);
+-+u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid);
+- struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings);
+- void brcmf_flowring_detach(struct brcmf_flowring *flow);
+- void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx,
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c
+-@@ -677,7 +677,7 @@ static u32 brcmf_msgbuf_flowring_create(
+- }
+- 
+- 
+--static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid)
+-+static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u16 flowid)
+- {
+- 	struct brcmf_flowring *flow = msgbuf->flow;
+- 	struct brcmf_commonring *commonring;
+-@@ -1310,7 +1310,7 @@ int brcmf_proto_msgbuf_rx_trigger(struct
+- }
+- 
+- 
+--void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid)
+-+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid)
+- {
+- 	struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+- 	struct msgbuf_tx_flowring_delete_req *delete;
+-@@ -1415,6 +1415,13 @@ int brcmf_proto_msgbuf_attach(struct brc
+- 	u32 count;
+- 
+- 	if_msgbuf = drvr->bus_if->msgbuf;
+-+
+-+	if (if_msgbuf->nrof_flowrings >= BRCMF_FLOWRING_HASHSIZE) {
+-+		brcmf_err("driver not configured for this many flowrings %d\n",
+-+			  if_msgbuf->nrof_flowrings);
+-+		if_msgbuf->nrof_flowrings = BRCMF_FLOWRING_HASHSIZE - 1;
+-+	}
+-+
+- 	msgbuf = kzalloc(sizeof(*msgbuf), GFP_KERNEL);
+- 	if (!msgbuf)
+- 		goto fail;
+---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
+-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h
+-@@ -33,7 +33,7 @@
+- 
+- 
+- int brcmf_proto_msgbuf_rx_trigger(struct device *dev);
+--void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid);
+-+void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid);
+- int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr);
+- void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr);
+- #else
+diff --git a/package/kernel/mac80211/patches/332-cfg80211-fix-faulty-variable-initialization-in-ieee8.patch b/package/kernel/mac80211/patches/332-cfg80211-fix-faulty-variable-initialization-in-ieee8.patch
+deleted file mode 100644
+index e414f23..0000000
+--- a/package/kernel/mac80211/patches/332-cfg80211-fix-faulty-variable-initialization-in-ieee8.patch
++++ /dev/null
+@@ -1,22 +0,0 @@
+-From: Felix Fietkau <nbd@openwrt.org>
+-Date: Mon, 8 Feb 2016 14:24:36 +0100
+-Subject: [PATCH] cfg80211: fix faulty variable initialization in
+- ieee80211_amsdu_to_8023s
+-
+-reuse_skb is set to true if the code decides to use the last segment.
+-Fixes a memory leak
+-
+-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+----
+-
+---- a/net/wireless/util.c
+-+++ b/net/wireless/util.c
+-@@ -676,7 +676,7 @@ void ieee80211_amsdu_to_8023s(struct sk_
+- 	u8 *payload;
+- 	int offset = 0, remaining, err;
+- 	struct ethhdr eth;
+--	bool reuse_skb = true;
+-+	bool reuse_skb = false;
+- 	bool last = false;
+- 
+- 	if (has_80211_header) {
+diff --git a/package/kernel/mac80211/patches/333-cfg80211-reuse-existing-page-fragments-in-A-MSDU-rx.patch b/package/kernel/mac80211/patches/333-cfg80211-reuse-existing-page-fragments-in-A-MSDU-rx.patch
+deleted file mode 100644
+index 6e2d0cf..0000000
+--- a/package/kernel/mac80211/patches/333-cfg80211-reuse-existing-page-fragments-in-A-MSDU-rx.patch
++++ /dev/null
+@@ -1,132 +0,0 @@
+-From: Felix Fietkau <nbd@openwrt.org>
+-Date: Mon, 8 Feb 2016 14:33:19 +0100
+-Subject: [PATCH] cfg80211: reuse existing page fragments in A-MSDU rx
+-
+-This massively reduces data copying and thus improves rx performance
+-
+-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+----
+-
+---- a/net/wireless/util.c
+-+++ b/net/wireless/util.c
+-@@ -644,23 +644,93 @@ int ieee80211_data_from_8023(struct sk_b
+- }
+- EXPORT_SYMBOL(ieee80211_data_from_8023);
+- 
+-+static void
+-+__frame_add_frag(struct sk_buff *skb, struct page *page,
+-+		 void *ptr, int len, int size)
+-+{
+-+	struct skb_shared_info *sh = skb_shinfo(skb);
+-+	int page_offset;
+-+
+-+	atomic_inc(&page->_count);
+-+	page_offset = ptr - page_address(page);
+-+	skb_add_rx_frag(skb, sh->nr_frags, page, page_offset, len, size);
+-+}
+-+
+-+static void
+-+__ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame,
+-+			    int offset, int len)
+-+{
+-+	struct skb_shared_info *sh = skb_shinfo(skb);
+-+	const skb_frag_t *frag = &sh->frags[-1];
+-+	struct page *frag_page;
+-+	void *frag_ptr;
+-+	int frag_len, frag_size;
+-+	int head_size = skb->len - skb->data_len;
+-+	int cur_len;
+-+
+-+	frag_page = virt_to_head_page(skb->head);
+-+	frag_ptr = skb->data;
+-+	frag_size = head_size;
+-+
+-+	while (offset >= frag_size) {
+-+		offset -= frag_size;
+-+		frag++;
+-+		frag_page = skb_frag_page(frag);
+-+		frag_ptr = skb_frag_address(frag);
+-+		frag_size = skb_frag_size(frag);
+-+	}
+-+
+-+	frag_ptr += offset;
+-+	frag_len = frag_size - offset;
+-+
+-+	cur_len = min(len, frag_len);
+-+
+-+	__frame_add_frag(frame, frag_page, frag_ptr, cur_len, frag_size);
+-+	len -= cur_len;
+-+
+-+	while (len > 0) {
+-+		frag++;
+-+		frag_len = skb_frag_size(frag);
+-+		cur_len = min(len, frag_len);
+-+		__frame_add_frag(frame, skb_frag_page(frag),
+-+				 skb_frag_address(frag), cur_len, frag_len);
+-+		len -= cur_len;
+-+	}
+-+}
+-+
+- static struct sk_buff *
+- __ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
+--		       int offset, int len)
+-+		       int offset, int len, bool reuse_frag)
+- {
+- 	struct sk_buff *frame;
+-+	int cur_len = len;
+- 
+- 	if (skb->len - offset < len)
+- 		return NULL;
+- 
+- 	/*
+-+	 * When reusing framents, copy some data to the head to simplify
+-+	 * ethernet header handling and speed up protocol header processing
+-+	 * in the stack later.
+-+	 */
+-+	if (reuse_frag)
+-+		cur_len = min_t(int, len, 32);
+-+
+-+	/*
+- 	 * Allocate and reserve two bytes more for payload
+- 	 * alignment since sizeof(struct ethhdr) is 14.
+- 	 */
+--	frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + len);
+-+	frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + cur_len);
+- 
+- 	skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
+--	skb_copy_bits(skb, offset, skb_put(frame, len), len);
+-+	skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
+-+
+-+	len -= cur_len;
+-+	if (!len)
+-+		return frame;
+-+
+-+	offset += cur_len;
+-+	__ieee80211_amsdu_copy_frag(skb, frame, offset, len);
+- 
+- 	return frame;
+- }
+-@@ -676,6 +746,7 @@ void ieee80211_amsdu_to_8023s(struct sk_
+- 	u8 *payload;
+- 	int offset = 0, remaining, err;
+- 	struct ethhdr eth;
+-+	bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
+- 	bool reuse_skb = false;
+- 	bool last = false;
+- 
+-@@ -703,12 +774,13 @@ void ieee80211_amsdu_to_8023s(struct sk_
+- 		offset += sizeof(struct ethhdr);
+- 		/* reuse skb for the last subframe */
+- 		last = remaining <= subframe_len + padding;
+--		if (!skb_is_nonlinear(skb) && last) {
+-+		if (!skb_is_nonlinear(skb) && !reuse_frag && last) {
+- 			skb_pull(skb, offset);
+- 			frame = skb;
+- 			reuse_skb = true;
+- 		} else {
+--			frame = __ieee80211_amsdu_copy(skb, hlen, offset, len);
+-+			frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
+-+						       reuse_frag);
+- 			if (!frame)
+- 				goto purge;
+- 
+diff --git a/package/kernel/mac80211/patches/334-mac80211-fix-wiphy-supported_band-access.patch b/package/kernel/mac80211/patches/334-mac80211-fix-wiphy-supported_band-access.patch
+deleted file mode 100644
+index f8f4f09..0000000
+--- a/package/kernel/mac80211/patches/334-mac80211-fix-wiphy-supported_band-access.patch
++++ /dev/null
+@@ -1,36 +0,0 @@
+-From: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
+-Date: Wed, 10 Feb 2016 16:08:17 +0100
+-Subject: [PATCH] mac80211: fix wiphy supported_band access
+-
+-Fix wiphy supported_band access in tx radiotap parsing. In particular,
+-info->band is always set to 0 (IEEE80211_BAND_2GHZ) since it has not
+-assigned yet. This cause a kernel crash on 5GHz only devices.
+-Move ieee80211_parse_tx_radiotap() after info->band assignment
+-
+-Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
+----
+-
+---- a/net/mac80211/tx.c
+-+++ b/net/mac80211/tx.c
+-@@ -1890,10 +1890,6 @@ netdev_tx_t ieee80211_monitor_start_xmit
+- 	info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
+- 		      IEEE80211_TX_CTL_INJECTED;
+- 
+--	/* process and remove the injection radiotap header */
+--	if (!ieee80211_parse_tx_radiotap(local, skb))
+--		goto fail;
+--
+- 	rcu_read_lock();
+- 
+- 	/*
+-@@ -1955,6 +1951,10 @@ netdev_tx_t ieee80211_monitor_start_xmit
+- 		goto fail_rcu;
+- 
+- 	info->band = chandef->chan->band;
+-+	/* process and remove the injection radiotap header */
+-+	if (!ieee80211_parse_tx_radiotap(local, skb))
+-+		goto fail_rcu;
+-+
+- 	ieee80211_xmit(sdata, NULL, skb);
+- 	rcu_read_unlock();
+- 
+diff --git a/package/kernel/mac80211/patches/335-mac80211-minstrel_ht-set-A-MSDU-tx-limits-based-on-s.patch b/package/kernel/mac80211/patches/335-mac80211-minstrel_ht-set-A-MSDU-tx-limits-based-on-s.patch
+deleted file mode 100644
+index acaacf7..0000000
+--- a/package/kernel/mac80211/patches/335-mac80211-minstrel_ht-set-A-MSDU-tx-limits-based-on-s.patch
++++ /dev/null
+@@ -1,61 +0,0 @@
+-From: Felix Fietkau <nbd@openwrt.org>
+-Date: Thu, 18 Feb 2016 19:30:05 +0100
+-Subject: [PATCH] mac80211: minstrel_ht: set A-MSDU tx limits based on selected
+- max_prob_rate
+-
+-Prevents excessive A-MSDU aggregation at low data rates or bad
+-conditions.
+-
+-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+----
+-
+---- a/net/mac80211/rc80211_minstrel_ht.c
+-+++ b/net/mac80211/rc80211_minstrel_ht.c
+-@@ -883,6 +883,39 @@ minstrel_ht_set_rate(struct minstrel_pri
+- 	ratetbl->rate[offset].flags = flags;
+- }
+- 
+-+static int
+-+minstrel_ht_get_max_amsdu_len(struct minstrel_ht_sta *mi)
+-+{
+-+	int group = mi->max_prob_rate / MCS_GROUP_RATES;
+-+	const struct mcs_group *g = &minstrel_mcs_groups[group];
+-+	int rate = mi->max_prob_rate % MCS_GROUP_RATES;
+-+
+-+	/* Disable A-MSDU if max_prob_rate is bad */
+-+	if (mi->groups[group].rates[rate].prob_ewma < MINSTREL_FRAC(50, 100))
+-+		return 1;
+-+
+-+	/* If the rate is slower than single-stream MCS1, make A-MSDU limit small */
+-+	if (g->duration[rate] > MCS_DURATION(1, 0, 52))
+-+		return 500;
+-+
+-+	/*
+-+	 * If the rate is slower than single-stream MCS4, limit A-MSDU to usual
+-+	 * data packet size
+-+	 */
+-+	if (g->duration[rate] > MCS_DURATION(1, 0, 104))
+-+		return 1500;
+-+
+-+	/*
+-+	 * If the rate is slower than single-stream MCS7, limit A-MSDU to twice
+-+	 * the usual data packet size
+-+	 */
+-+	if (g->duration[rate] > MCS_DURATION(1, 0, 260))
+-+		return 3000;
+-+
+-+	/* unlimited */
+-+	return 0;
+-+}
+-+
+- static void
+- minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
+- {
+-@@ -907,6 +940,7 @@ minstrel_ht_update_rates(struct minstrel
+- 		minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_prob_rate);
+- 	}
+- 
+-+	mi->sta->max_rc_amsdu_len = minstrel_ht_get_max_amsdu_len(mi);
+- 	rates->rate[i].idx = -1;
+- 	rate_control_set_rates(mp->hw, mi->sta, rates);
+- }
+diff --git a/package/kernel/mac80211/patches/336-mac80211-minstrel_ht-set-default-tx-aggregation-time.patch b/package/kernel/mac80211/patches/336-mac80211-minstrel_ht-set-default-tx-aggregation-time.patch
+deleted file mode 100644
+index 32a2ad6..0000000
+--- a/package/kernel/mac80211/patches/336-mac80211-minstrel_ht-set-default-tx-aggregation-time.patch
++++ /dev/null
+@@ -1,31 +0,0 @@
+-From: Felix Fietkau <nbd@openwrt.org>
+-Date: Thu, 18 Feb 2016 19:45:33 +0100
+-Subject: [PATCH] mac80211: minstrel_ht: set default tx aggregation timeout to
+- 0
+-
+-The value 5000 was put here with the addition of the timeout field to
+-ieee80211_start_tx_ba_session. It was originally added in mac80211 to
+-save resources for drivers like iwlwifi, which only supports a limited
+-number of concurrent aggregation sessions.
+-
+-Since iwlwifi does not use minstrel_ht and other drivers don't need
+-this, 0 is a better default - especially since there have been
+-recent reports of aggregation setup related issues reproduced with
+-ath9k. This should improve stability without causing any adverse
+-effects.
+-
+-Cc: stable@vger.kernel.org
+-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+----
+-
+---- a/net/mac80211/rc80211_minstrel_ht.c
+-+++ b/net/mac80211/rc80211_minstrel_ht.c
+-@@ -692,7 +692,7 @@ minstrel_aggr_check(struct ieee80211_sta
+- 	if (likely(sta->ampdu_mlme.tid_tx[tid]))
+- 		return;
+- 
+--	ieee80211_start_tx_ba_session(pubsta, tid, 5000);
+-+	ieee80211_start_tx_ba_session(pubsta, tid, 0);
+- }
+- 
+- static void
 diff --git a/package/kernel/mac80211/patches/337-mac80211-minstrel_ht-fix-a-logic-error-in-RTS-CTS-ha.patch b/package/kernel/mac80211/patches/337-mac80211-minstrel_ht-fix-a-logic-error-in-RTS-CTS-ha.patch
 deleted file mode 100644
 index 229351b..0000000
@@ -10706,98 +9189,6 @@ index 56cd94a..0000000
 - 			    !ether_addr_equal(bssid, hdr->addr1))
 - 				return false;
 - 		}
-diff --git a/package/kernel/mac80211/patches/338-mac80211-fix-tim-recalculation-after-PS-response.patch b/package/kernel/mac80211/patches/338-mac80211-fix-tim-recalculation-after-PS-response.patch
-new file mode 100644
-index 0000000..6c0852e
---- /dev/null
-+++ b/package/kernel/mac80211/patches/338-mac80211-fix-tim-recalculation-after-PS-response.patch
-@@ -0,0 +1,31 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Fri, 26 Aug 2016 21:57:16 +0200
-+Subject: [PATCH] mac80211: fix tim recalculation after PS response
-+
-+Handle the case where the mac80211 intermediate queues are empty and the
-+driver has buffered frames
-+
-+Fixes: ba8c3d6f16a1 ("mac80211: add an intermediate software queue implementation")
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/net/mac80211/sta_info.c
-++++ b/net/mac80211/sta_info.c
-+@@ -1616,7 +1616,6 @@ ieee80211_sta_ps_deliver_response(struct
-+ 
-+ 		sta_info_recalc_tim(sta);
-+ 	} else {
-+-		unsigned long tids = sta->txq_buffered_tids & driver_release_tids;
-+ 		int tid;
-+ 
-+ 		/*
-+@@ -1648,7 +1647,8 @@ ieee80211_sta_ps_deliver_response(struct
-+ 		for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
-+ 			struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
-+ 
-+-			if (!(tids & BIT(tid)) || txqi->tin.backlog_packets)
-++			if (!(driver_release_tids & BIT(tid)) ||
-++			    txqi->tin.backlog_packets)
-+ 				continue;
-+ 
-+ 			sta_info_recalc_tim(sta);
-diff --git a/package/kernel/mac80211/patches/339-ath9k-fix-moredata-bit-in-PS-buffered-frame-release.patch b/package/kernel/mac80211/patches/339-ath9k-fix-moredata-bit-in-PS-buffered-frame-release.patch
-new file mode 100644
-index 0000000..49b37e4
---- /dev/null
-+++ b/package/kernel/mac80211/patches/339-ath9k-fix-moredata-bit-in-PS-buffered-frame-release.patch
-@@ -0,0 +1,49 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sun, 28 Aug 2016 13:13:01 +0200
-+Subject: [PATCH] ath9k: fix moredata bit in PS buffered frame release
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/xmit.c
-++++ b/drivers/net/wireless/ath/ath9k/xmit.c
-+@@ -1634,6 +1634,21 @@ void ath_tx_aggr_wakeup(struct ath_softc
-+ 	}
-+ }
-+ 
-++static void
-++ath9k_set_moredata(struct ath_softc *sc, struct ath_buf *bf, bool val)
-++{
-++	struct ieee80211_hdr *hdr;
-++	u16 mask = cpu_to_le16(IEEE80211_FCTL_MOREDATA);
-++	u16 mask_val = mask * val;
-++
-++	hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data;
-++	if ((hdr->frame_control & mask) != mask_val) {
-++		hdr->frame_control = (hdr->frame_control & ~mask) | mask_val;
-++		dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
-++			sizeof(*hdr), DMA_TO_DEVICE);
-++	}
-++}
-++
-+ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
-+ 				   struct ieee80211_sta *sta,
-+ 				   u16 tids, int nframes,
-+@@ -1664,6 +1679,7 @@ void ath9k_release_buffered_frames(struc
-+ 			if (!bf)
-+ 				break;
-+ 
-++			ath9k_set_moredata(sc, bf, true);
-+ 			list_add_tail(&bf->list, &bf_q);
-+ 			ath_set_rates(tid->an->vif, tid->an->sta, bf, true);
-+ 			if (bf_isampdu(bf)) {
-+@@ -1687,6 +1703,9 @@ void ath9k_release_buffered_frames(struc
-+ 	if (list_empty(&bf_q))
-+ 		return;
-+ 
-++	if (!more_data)
-++		ath9k_set_moredata(sc, bf_tail, false);
-++
-+ 	info = IEEE80211_SKB_CB(bf_tail->bf_mpdu);
-+ 	info->flags |= IEEE80211_TX_STATUS_EOSP;
-+ 
 diff --git a/package/kernel/mac80211/patches/339-cfg80211-add-radiotap-VHT-info-to-rtap_namespace_siz.patch b/package/kernel/mac80211/patches/339-cfg80211-add-radiotap-VHT-info-to-rtap_namespace_siz.patch
 deleted file mode 100644
 index 15d6cd0..0000000
@@ -10825,34 +9216,6 @@ index 15d6cd0..0000000
 - 	/*
 - 	 * add more here as they are defined in radiotap.h
 - 	 */
-diff --git a/package/kernel/mac80211/patches/340-ath9k-clear-potentially-stale-EOSP-status-bit-in-int.patch b/package/kernel/mac80211/patches/340-ath9k-clear-potentially-stale-EOSP-status-bit-in-int.patch
-new file mode 100644
-index 0000000..929da25
---- /dev/null
-+++ b/package/kernel/mac80211/patches/340-ath9k-clear-potentially-stale-EOSP-status-bit-in-int.patch
-@@ -0,0 +1,22 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sun, 28 Aug 2016 13:13:42 +0200
-+Subject: [PATCH] ath9k: clear potentially stale EOSP status bit in
-+ intermediate queues
-+
-+Prevents spurious ieee80211_sta_eosp calls.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/xmit.c
-++++ b/drivers/net/wireless/ath/ath9k/xmit.c
-+@@ -945,7 +945,8 @@ ath_tx_get_tid_subframe(struct ath_softc
-+ 		bf->bf_lastbf = bf;
-+ 
-+ 		tx_info = IEEE80211_SKB_CB(skb);
-+-		tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT;
-++		tx_info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
-++				    IEEE80211_TX_STATUS_EOSP);
-+ 
-+ 		/*
-+ 		 * No aggregation session is running, but there may be frames
 diff --git a/package/kernel/mac80211/patches/340-mac80211-fix-parsing-of-40Mhz-in-injected-radiotap-h.patch b/package/kernel/mac80211/patches/340-mac80211-fix-parsing-of-40Mhz-in-injected-radiotap-h.patch
 deleted file mode 100644
 index de1b386..0000000
@@ -10895,52 +9258,6 @@ index de1b386..0000000
 - 				rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
 - 			break;
 - 
-diff --git a/package/kernel/mac80211/patches/341-ath9k-release-PS-buffered-frames-as-A-MPDU-if-enable.patch b/package/kernel/mac80211/patches/341-ath9k-release-PS-buffered-frames-as-A-MPDU-if-enable.patch
-new file mode 100644
-index 0000000..1cc1667
---- /dev/null
-+++ b/package/kernel/mac80211/patches/341-ath9k-release-PS-buffered-frames-as-A-MPDU-if-enable.patch
-@@ -0,0 +1,40 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sun, 28 Aug 2016 13:15:10 +0200
-+Subject: [PATCH] ath9k: release PS buffered frames as A-MPDU if enabled
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/xmit.c
-++++ b/drivers/net/wireless/ath/ath9k/xmit.c
-+@@ -1660,10 +1660,11 @@ void ath9k_release_buffered_frames(struc
-+ 	struct ath_node *an = (struct ath_node *)sta->drv_priv;
-+ 	struct ath_txq *txq = sc->tx.uapsdq;
-+ 	struct ieee80211_tx_info *info;
-++	struct ath_frame_info *fi;
-+ 	struct list_head bf_q;
-+ 	struct ath_buf *bf_tail = NULL, *bf;
-+ 	int sent = 0;
-+-	int i;
-++	int n, i;
-+ 
-+ 	INIT_LIST_HEAD(&bf_q);
-+ 	for (i = 0; tids && nframes; i++, tids >>= 1) {
-+@@ -1683,10 +1684,15 @@ void ath9k_release_buffered_frames(struc
-+ 			ath9k_set_moredata(sc, bf, true);
-+ 			list_add_tail(&bf->list, &bf_q);
-+ 			ath_set_rates(tid->an->vif, tid->an->sta, bf, true);
-+-			if (bf_isampdu(bf)) {
-++			if (bf_isampdu(bf))
-+ 				ath_tx_addto_baw(sc, tid, bf);
-+-				bf->bf_state.bf_type &= ~BUF_AGGR;
-++			if (bf_isaggr(bf)) {
-++				fi = get_frame_info(bf->bf_mpdu);
-++				n = ath_compute_num_delims(sc, tid, bf,
-++							   fi->framelen, true);
-++				bf->bf_state.ndelim = n;
-+ 			}
-++
-+ 			if (bf_tail)
-+ 				bf_tail->bf_next = bf;
-+ 
 diff --git a/package/kernel/mac80211/patches/341-mac80211-parse-VHT-info-in-injected-frames.patch b/package/kernel/mac80211/patches/341-mac80211-parse-VHT-info-in-injected-frames.patch
 deleted file mode 100644
 index ac1f251..0000000
@@ -11012,31 +9329,6 @@ index ac1f251..0000000
 - 		} else {
 - 			for (i = 0; i < sband->n_bitrates; i++) {
 - 				if (rate * 5 != sband->bitrates[i].bitrate)
-diff --git a/package/kernel/mac80211/patches/342-ath9k-report-tx-status-on-EOSP.patch b/package/kernel/mac80211/patches/342-ath9k-report-tx-status-on-EOSP.patch
-new file mode 100644
-index 0000000..80a3074
---- /dev/null
-+++ b/package/kernel/mac80211/patches/342-ath9k-report-tx-status-on-EOSP.patch
-@@ -0,0 +1,19 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sun, 28 Aug 2016 13:23:27 +0200
-+Subject: [PATCH] ath9k: report tx status on EOSP
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/xmit.c
-++++ b/drivers/net/wireless/ath/ath9k/xmit.c
-+@@ -86,7 +86,8 @@ static void ath_tx_status(struct ieee802
-+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+ 	struct ieee80211_sta *sta = info->status.status_driver_data[0];
-+ 
-+-	if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
-++	if (info->flags & (IEEE80211_TX_CTL_REQ_TX_STATUS |
-++			   IEEE80211_TX_STATUS_EOSP)) {
-+ 		ieee80211_tx_status(hw, skb);
-+ 		return;
-+ 	}
 diff --git a/package/kernel/mac80211/patches/342-mac80211-do-not-pass-injected-frames-without-a-valid.patch b/package/kernel/mac80211/patches/342-mac80211-do-not-pass-injected-frames-without-a-valid.patch
 deleted file mode 100644
 index d7452c2..0000000
@@ -11066,123 +9358,6 @@ index d7452c2..0000000
 - 		info->control.rates[0].flags = rate_flags;
 - 		info->control.rates[0].count = min_t(u8, rate_retries + 1,
 - 						     local->hw.max_rate_tries);
-diff --git a/package/kernel/mac80211/patches/343-ath9k-fix-block-ack-window-tracking-issues.patch b/package/kernel/mac80211/patches/343-ath9k-fix-block-ack-window-tracking-issues.patch
-new file mode 100644
-index 0000000..007a8d7d
---- /dev/null
-+++ b/package/kernel/mac80211/patches/343-ath9k-fix-block-ack-window-tracking-issues.patch
-@@ -0,0 +1,111 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Tue, 30 Aug 2016 12:44:08 +0200
-+Subject: [PATCH] ath9k: fix block-ack window tracking issues
-+
-+Ensure that a buffer gets tracked as part of the block-ack window as
-+soon as it's dequeued from the tid for the first time. Ensure that
-+double calls to ath_tx_addto_baw (e.g. on retransmission) don't cause
-+any issues.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/xmit.c
-++++ b/drivers/net/wireless/ath/ath9k/xmit.c
-+@@ -62,7 +62,7 @@ static void ath_tx_rc_status(struct ath_
-+ 			     struct ath_tx_status *ts, int nframes, int nbad,
-+ 			     int txok);
-+ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
-+-			      int seqno);
-++			      struct ath_buf *bf);
-+ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
-+ 					   struct ath_txq *txq,
-+ 					   struct ath_atx_tid *tid,
-+@@ -300,7 +300,7 @@ static void ath_tx_flush_tid(struct ath_
-+ 		}
-+ 
-+ 		if (fi->baw_tracked) {
-+-			ath_tx_update_baw(sc, tid, bf->bf_state.seqno);
-++			ath_tx_update_baw(sc, tid, bf);
-+ 			sendbar = true;
-+ 		}
-+ 
-+@@ -316,10 +316,15 @@ static void ath_tx_flush_tid(struct ath_
-+ }
-+ 
-+ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
-+-			      int seqno)
-++			      struct ath_buf *bf)
-+ {
-++	struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu);
-++	u16 seqno = bf->bf_state.seqno;
-+ 	int index, cindex;
-+ 
-++	if (!fi->baw_tracked)
-++		return;
-++
-+ 	index  = ATH_BA_INDEX(tid->seq_start, seqno);
-+ 	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
-+ 
-+@@ -340,6 +345,9 @@ static void ath_tx_addto_baw(struct ath_
-+ 	u16 seqno = bf->bf_state.seqno;
-+ 	int index, cindex;
-+ 
-++	if (fi->baw_tracked)
-++		return;
-++
-+ 	index  = ATH_BA_INDEX(tid->seq_start, seqno);
-+ 	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
-+ 	__set_bit(cindex, tid->tx_buf);
-+@@ -616,7 +624,7 @@ static void ath_tx_complete_aggr(struct
-+ 			 * complete the acked-ones/xretried ones; update
-+ 			 * block-ack window
-+ 			 */
-+-			ath_tx_update_baw(sc, tid, seqno);
-++			ath_tx_update_baw(sc, tid, bf);
-+ 
-+ 			if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) {
-+ 				memcpy(tx_info->control.rates, rates, sizeof(rates));
-+@@ -646,7 +654,7 @@ static void ath_tx_complete_aggr(struct
-+ 				 * run out of tx buf.
-+ 				 */
-+ 				if (!tbf) {
-+-					ath_tx_update_baw(sc, tid, seqno);
-++					ath_tx_update_baw(sc, tid, bf);
-+ 
-+ 					ath_tx_complete_buf(sc, bf, txq,
-+ 							    &bf_head, NULL, ts,
-+@@ -986,11 +994,14 @@ ath_tx_get_tid_subframe(struct ath_softc
-+ 
-+ 			INIT_LIST_HEAD(&bf_head);
-+ 			list_add(&bf->list, &bf_head);
-+-			ath_tx_update_baw(sc, tid, seqno);
-++			ath_tx_update_baw(sc, tid, bf);
-+ 			ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, &ts, 0);
-+ 			continue;
-+ 		}
-+ 
-++		if (bf_isampdu(bf))
-++			ath_tx_addto_baw(sc, tid, bf);
-++
-+ 		return bf;
-+ 	}
-+ 
-+@@ -1048,8 +1059,6 @@ ath_tx_form_aggr(struct ath_softc *sc, s
-+ 		bf->bf_next = NULL;
-+ 
-+ 		/* link buffers of this frame to the aggregate */
-+-		if (!fi->baw_tracked)
-+-			ath_tx_addto_baw(sc, tid, bf);
-+ 		bf->bf_state.ndelim = ndelim;
-+ 
-+ 		list_add_tail(&bf->list, bf_q);
-+@@ -1685,8 +1694,6 @@ void ath9k_release_buffered_frames(struc
-+ 			ath9k_set_moredata(sc, bf, true);
-+ 			list_add_tail(&bf->list, &bf_q);
-+ 			ath_set_rates(tid->an->vif, tid->an->sta, bf, true);
-+-			if (bf_isampdu(bf))
-+-				ath_tx_addto_baw(sc, tid, bf);
-+ 			if (bf_isaggr(bf)) {
-+ 				fi = get_frame_info(bf->bf_mpdu);
-+ 				n = ath_compute_num_delims(sc, tid, bf,
 diff --git a/package/kernel/mac80211/patches/343-mac80211-minstrel_ht-improve-sample-rate-skip-logic.patch b/package/kernel/mac80211/patches/343-mac80211-minstrel_ht-improve-sample-rate-skip-logic.patch
 deleted file mode 100644
 index 55ff817..0000000
@@ -15851,76 +14026,6 @@ index c20d40c..0000000
 - 
 - #define DOT11_DEFAULT_RTS_LEN		2347
 - #define DOT11_DEFAULT_FRAG_LEN		2346
-diff --git a/package/kernel/mac80211/patches/344-mac80211-send-delBA-on-unexpected-BlockAck-data-fram.patch b/package/kernel/mac80211/patches/344-mac80211-send-delBA-on-unexpected-BlockAck-data-fram.patch
-new file mode 100644
-index 0000000..3bbca22
---- /dev/null
-+++ b/package/kernel/mac80211/patches/344-mac80211-send-delBA-on-unexpected-BlockAck-data-fram.patch
-@@ -0,0 +1,64 @@
-+From: Johannes Berg <johannes.berg@intel.com>
-+Date: Mon, 29 Aug 2016 23:25:18 +0300
-+Subject: [PATCH] mac80211: send delBA on unexpected BlockAck data frames
-+
-+When we receive data frames with ACK policy BlockAck, send
-+delBA as requested by the 802.11 spec. Since this would be
-+happening for every frame inside an A-MPDU if it's really
-+received outside a session, limit it to a single attempt.
-+
-+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-+---
-+
-+--- a/net/mac80211/agg-rx.c
-++++ b/net/mac80211/agg-rx.c
-+@@ -388,8 +388,10 @@ void __ieee80211_start_rx_ba_session(str
-+ 	}
-+ 
-+ end:
-+-	if (status == WLAN_STATUS_SUCCESS)
-++	if (status == WLAN_STATUS_SUCCESS) {
-+ 		__set_bit(tid, sta->ampdu_mlme.agg_session_valid);
-++		__clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
-++	}
-+ 	mutex_unlock(&sta->ampdu_mlme.mtx);
-+ 
-+ end_no_lock:
-+--- a/net/mac80211/rx.c
-++++ b/net/mac80211/rx.c
-+@@ -1072,8 +1072,15 @@ static void ieee80211_rx_reorder_ampdu(s
-+ 	tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
-+ 
-+ 	tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
-+-	if (!tid_agg_rx)
-++	if (!tid_agg_rx) {
-++		if (ack_policy == IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK &&
-++		    !test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
-++		    !test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
-++			ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
-++					     WLAN_BACK_RECIPIENT,
-++					     WLAN_REASON_QSTA_REQUIRE_SETUP);
-+ 		goto dont_reorder;
-++	}
-+ 
-+ 	/* qos null data frames are excluded */
-+ 	if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
-+--- a/net/mac80211/sta_info.h
-++++ b/net/mac80211/sta_info.h
-+@@ -230,6 +230,8 @@ struct tid_ampdu_rx {
-+  * @tid_rx_stop_requested:  bitmap indicating which BA sessions per TID the
-+  *	driver requested to close until the work for it runs
-+  * @agg_session_valid: bitmap indicating which TID has a rx BA session open on
-++ * @unexpected_agg: bitmap indicating which TID already sent a delBA due to
-++ *	unexpected aggregation related frames outside a session
-+  * @work: work struct for starting/stopping aggregation
-+  * @tid_tx: aggregation info for Tx per TID
-+  * @tid_start_tx: sessions where start was requested
-+@@ -244,6 +246,7 @@ struct sta_ampdu_mlme {
-+ 	unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-+ 	unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-+ 	unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-++	unsigned long unexpected_agg[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-+ 	/* tx */
-+ 	struct work_struct work;
-+ 	struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS];
 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
 deleted file mode 100644
 index 39f4383..0000000
@@ -15981,38 +14086,6 @@ index 39f4383..0000000
 - 	}
 - }
 - 
-diff --git a/package/kernel/mac80211/patches/345-mac80211-send-delBA-on-unexpected-BlockAck-Request.patch b/package/kernel/mac80211/patches/345-mac80211-send-delBA-on-unexpected-BlockAck-Request.patch
-new file mode 100644
-index 0000000..c3d3118
---- /dev/null
-+++ b/package/kernel/mac80211/patches/345-mac80211-send-delBA-on-unexpected-BlockAck-Request.patch
-@@ -0,0 +1,26 @@
-+From: Johannes Berg <johannes.berg@intel.com>
-+Date: Mon, 29 Aug 2016 23:25:19 +0300
-+Subject: [PATCH] mac80211: send delBA on unexpected BlockAck Request
-+
-+If we don't have a BA session, send delBA, as requested by the
-+IEEE 802.11 spec. Apply the same limit of sending such a delBA
-+only once as in the previous patch.
-+
-+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-+---
-+
-+--- a/net/mac80211/rx.c
-++++ b/net/mac80211/rx.c
-+@@ -2537,6 +2537,12 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_
-+ 
-+ 		tid = le16_to_cpu(bar_data.control) >> 12;
-+ 
-++		if (!test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
-++		    !test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
-++			ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
-++					     WLAN_BACK_RECIPIENT,
-++					     WLAN_REASON_QSTA_REQUIRE_SETUP);
-++
-+ 		tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]);
-+ 		if (!tid_agg_rx)
-+ 			return RX_DROP_MONITOR;
 diff --git a/package/kernel/mac80211/patches/346-brcmfmac-uninitialized-ret-variable.patch b/package/kernel/mac80211/patches/346-brcmfmac-uninitialized-ret-variable.patch
 deleted file mode 100644
 index 3c9ed42..0000000
@@ -16040,119 +14113,6 @@ index 3c9ed42..0000000
 - 
 - 	brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
 - 		  write, fn, addr, regsz);
-diff --git a/package/kernel/mac80211/patches/346-mac80211-fix-sequence-number-assignment-for-PS-respo.patch b/package/kernel/mac80211/patches/346-mac80211-fix-sequence-number-assignment-for-PS-respo.patch
-new file mode 100644
-index 0000000..a82d12f
---- /dev/null
-+++ b/package/kernel/mac80211/patches/346-mac80211-fix-sequence-number-assignment-for-PS-respo.patch
-@@ -0,0 +1,107 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sun, 4 Sep 2016 17:46:24 +0200
-+Subject: [PATCH] mac80211: fix sequence number assignment for PS response
-+ frames
-+
-+When using intermediate queues, sequence number allocation is deferred
-+until dequeue. This doesn't work for PS response frames, which bypass
-+those queues.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/net/mac80211/tx.c
-++++ b/net/mac80211/tx.c
-+@@ -792,6 +792,36 @@ static __le16 ieee80211_tx_next_seq(stru
-+ 	return ret;
-+ }
-+ 
-++static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
-++					  struct ieee80211_vif *vif,
-++					  struct ieee80211_sta *pubsta,
-++					  struct sk_buff *skb)
-++{
-++	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-++	struct ieee80211_txq *txq = NULL;
-++
-++	if ((info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) ||
-++	    (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
-++		return NULL;
-++
-++	if (!ieee80211_is_data(hdr->frame_control))
-++		return NULL;
-++
-++	if (pubsta) {
-++		u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
-++
-++		txq = pubsta->txq[tid];
-++	} else if (vif) {
-++		txq = vif->txq;
-++	}
-++
-++	if (!txq)
-++		return NULL;
-++
-++	return to_txq_info(txq);
-++}
-++
-+ static ieee80211_tx_result debug_noinline
-+ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
-+ {
-+@@ -849,7 +879,8 @@ ieee80211_tx_h_sequence(struct ieee80211
-+ 	tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
-+ 	tx->sta->tx_stats.msdu[tid]++;
-+ 
-+-	if (!tx->sta->sta.txq[0])
-++	if (!ieee80211_get_txq(tx->local, info->control.vif, &tx->sta->sta,
-++			       tx->skb))
-+ 		hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
-+ 
-+ 	return TX_CONTINUE;
-+@@ -1238,36 +1269,6 @@ ieee80211_tx_prepare(struct ieee80211_su
-+ 	return TX_CONTINUE;
-+ }
-+ 
-+-static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
-+-					  struct ieee80211_vif *vif,
-+-					  struct ieee80211_sta *pubsta,
-+-					  struct sk_buff *skb)
-+-{
-+-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-+-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+-	struct ieee80211_txq *txq = NULL;
-+-
-+-	if ((info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) ||
-+-	    (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
-+-		return NULL;
-+-
-+-	if (!ieee80211_is_data(hdr->frame_control))
-+-		return NULL;
-+-
-+-	if (pubsta) {
-+-		u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
-+-
-+-		txq = pubsta->txq[tid];
-+-	} else if (vif) {
-+-		txq = vif->txq;
-+-	}
-+-
-+-	if (!txq)
-+-		return NULL;
-+-
-+-	return to_txq_info(txq);
-+-}
-+-
-+ static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb)
-+ {
-+ 	IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
-+@@ -3265,7 +3266,7 @@ static bool ieee80211_xmit_fast(struct i
-+ 
-+ 	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-+ 		*ieee80211_get_qos_ctl(hdr) = tid;
-+-		if (!sta->sta.txq[0])
-++		if (!ieee80211_get_txq(local, &sdata->vif, &sta->sta, skb))
-+ 			hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
-+ 	} else {
-+ 		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
 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
 deleted file mode 100644
 index d1deb6e..0000000
@@ -20836,14 +18796,13 @@ index e151a12..c40598d 100644
   			return result;
   	}
 diff --git a/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch b/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch
-index 5a5e464..69147f6 100644
+index 5a5e464..0b25749 100644
 --- a/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch
 +++ b/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch
 @@ -1,16 +1,16 @@
  --- a/drivers/net/wireless/ath/ath9k/ath9k.h
  +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
--@@ -814,6 +814,9 @@ static inline int ath9k_dump_btcoex(stru
-+@@ -827,6 +827,9 @@ static inline int ath9k_dump_btcoex(stru
+ @@ -814,6 +814,9 @@ static inline int ath9k_dump_btcoex(stru
 + #ifdef CPTCFG_MAC80211_LEDS
   void ath_init_leds(struct ath_softc *sc);
   void ath_deinit_leds(struct ath_softc *sc);
@@ -20856,7 +18815,7 @@ index 5a5e464..69147f6 100644
   static inline void ath_init_leds(struct ath_softc *sc)
   {
 -@@ -953,6 +956,13 @@ void ath_ant_comb_scan(struct ath_softc
-+@@ -963,6 +966,13 @@ void ath_ant_comb_scan(struct ath_softc
++@@ -950,6 +953,13 @@ void ath_ant_comb_scan(struct ath_softc
   
   #define ATH9K_NUM_CHANCTX  2 /* supports 2 operating channels */
   
@@ -20865,7 +18824,7 @@ index 5a5e464..69147f6 100644
   	struct ieee80211_hw *hw;
   	struct device *dev;
 -@@ -1005,9 +1015,8 @@ struct ath_softc {
-+@@ -1015,9 +1025,8 @@ struct ath_softc {
++@@ -1002,9 +1012,8 @@ struct ath_softc {
   	spinlock_t chan_lock;
   
   #ifdef CPTCFG_MAC80211_LEDS
@@ -21124,7 +19083,7 @@ index 3c5e9f5..c2d2781 100644
   
   	if (i == 0) {
 diff --git a/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch b/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch
-index e83c6bf..6edef09 100644
+index e83c6bf..4615643 100644
 --- a/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch
 +++ b/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch
 @@ -1,6 +1,6 @@
@@ -21167,7 +19126,7 @@ index e83c6bf..6edef09 100644
   	bool htc_reset_init;
   
 -@@ -1066,6 +1074,7 @@ void ath9k_hw_check_nav(struct ath_hw *a
-+@@ -1067,6 +1075,7 @@ void ath9k_hw_check_nav(struct ath_hw *a
++@@ -1068,6 +1076,7 @@ void ath9k_hw_check_nav(struct ath_hw *a
   bool ath9k_hw_check_alive(struct ath_hw *ah);
   
   bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
@@ -21449,7 +19408,7 @@ index 0000000..5d84cf0
 + }
 diff --git a/package/kernel/mac80211/patches/548-ath9k_enable_gpio_chip.patch b/package/kernel/mac80211/patches/548-ath9k_enable_gpio_chip.patch
 new file mode 100644
-index 0000000..1330dfe
+index 0000000..de7c0ac
 --- /dev/null
 +++ b/package/kernel/mac80211/patches/548-ath9k_enable_gpio_chip.patch
 @@ -0,0 +1,234 @@
@@ -21473,7 +19432,7 @@ index 0000000..1330dfe
 + 
 + #include "common.h"
 + #include "debug.h"
-+@@ -973,6 +974,14 @@ struct ath_led {
++@@ -960,6 +961,14 @@ struct ath_led {
 + 	struct led_classdev cdev;
 + };
 + 
@@ -21488,7 +19447,7 @@ index 0000000..1330dfe
 + struct ath_softc {
 + 	struct ieee80211_hw *hw;
 + 	struct device *dev;
-+@@ -1027,6 +1036,9 @@ struct ath_softc {
++@@ -1014,6 +1023,9 @@ struct ath_softc {
 + #ifdef CPTCFG_MAC80211_LEDS
 + 	const char *led_default_trigger;
 + 	struct list_head leds;
@@ -21689,7 +19648,7 @@ index 0000000..1330dfe
 + /*******************/
 diff --git a/package/kernel/mac80211/patches/549-ath9k_enable_gpio_buttons.patch b/package/kernel/mac80211/patches/549-ath9k_enable_gpio_buttons.patch
 new file mode 100644
-index 0000000..f86b015
+index 0000000..b9d1883
 --- /dev/null
 +++ b/package/kernel/mac80211/patches/549-ath9k_enable_gpio_buttons.patch
 @@ -0,0 +1,149 @@
@@ -21705,7 +19664,7 @@ index 0000000..f86b015
 +---
 +--- a/drivers/net/wireless/ath/ath9k/ath9k.h
 ++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
-+@@ -1038,6 +1038,7 @@ struct ath_softc {
++@@ -1025,6 +1025,7 @@ struct ath_softc {
 + 	struct list_head leds;
 + #ifdef CONFIG_GPIOLIB
 + 	struct ath9k_gpio_chip *gpiochip;
diff --git a/patches/openwrt/0076-kernel-add-fix-for-CVE-2016-7117.patch b/patches/openwrt/0073-kernel-add-fix-for-CVE-2016-7117.patch
similarity index 100%
rename from patches/openwrt/0076-kernel-add-fix-for-CVE-2016-7117.patch
rename to patches/openwrt/0073-kernel-add-fix-for-CVE-2016-7117.patch
diff --git a/patches/openwrt/0073-mac80211-fix-packet-loss-on-fq-reordering.patch b/patches/openwrt/0073-mac80211-fix-packet-loss-on-fq-reordering.patch
deleted file mode 100644
index 6328ba4ce5b8111e17ba55dd517fa18d09365e73..0000000000000000000000000000000000000000
--- a/patches/openwrt/0073-mac80211-fix-packet-loss-on-fq-reordering.patch
+++ /dev/null
@@ -1,636 +0,0 @@
-From: Matthias Schiffer <mschiffer@universe-factory.net>
-Date: Fri, 30 Sep 2016 16:57:44 +0200
-Subject: mac80211: fix packet loss on fq reordering
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
-
-Backport of LEDE a194ffd4a89588bc75aeb9a27f59c36afd3d24bd
-
-diff --git a/package/kernel/mac80211/patches/346-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch b/package/kernel/mac80211/patches/346-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch
-new file mode 100644
-index 0000000..8ceed51
---- /dev/null
-+++ b/package/kernel/mac80211/patches/346-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch
-@@ -0,0 +1,478 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sun, 4 Sep 2016 17:46:24 +0200
-+Subject: [PATCH] mac80211: fix sequence number assignment for PS response
-+ frames
-+
-+When using intermediate queues, sequence number allocation is deferred
-+until dequeue. This doesn't work for PS response frames, which bypass
-+those queues.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/net/mac80211/tx.c
-++++ b/net/mac80211/tx.c
-+@@ -38,6 +38,12 @@
-+ #include "wme.h"
-+ #include "rate.h"
-+ 
-++static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx);
-++static bool ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
-++				       struct sta_info *sta, u8 pn_offs,
-++				       struct ieee80211_key_conf *key_conf,
-++				       struct sk_buff *skb);
-++
-+ /* misc utils */
-+ 
-+ static inline void ieee80211_tx_stats(struct net_device *dev, u32 len)
-+@@ -849,8 +855,7 @@ ieee80211_tx_h_sequence(struct ieee80211
-+ 	tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
-+ 	tx->sta->tx_stats.msdu[tid]++;
-+ 
-+-	if (!tx->sta->sta.txq[0])
-+-		hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
-++	hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
-+ 
-+ 	return TX_CONTINUE;
-+ }
-+@@ -1398,6 +1403,7 @@ void ieee80211_txq_init(struct ieee80211
-+ 	fq_tin_init(&txqi->tin);
-+ 	fq_flow_init(&txqi->def_flow);
-+ 	codel_vars_init(&txqi->def_cvars);
-++	__skb_queue_head_init(&txqi->frags);
-+ 
-+ 	txqi->txq.vif = &sdata->vif;
-+ 
-+@@ -1420,6 +1426,7 @@ void ieee80211_txq_purge(struct ieee8021
-+ 	struct fq_tin *tin = &txqi->tin;
-+ 
-+ 	fq_tin_reset(fq, tin, fq_skb_free_func);
-++	ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
-+ }
-+ 
-+ int ieee80211_txq_setup_flows(struct ieee80211_local *local)
-+@@ -1476,12 +1483,19 @@ struct sk_buff *ieee80211_tx_dequeue(str
-+ 	struct sk_buff *skb = NULL;
-+ 	struct fq *fq = &local->fq;
-+ 	struct fq_tin *tin = &txqi->tin;
-++	struct ieee80211_tx_info *info;
-+ 
-+ 	spin_lock_bh(&fq->lock);
-+ 
-+ 	if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
-+ 		goto out;
-+ 
-++	/* Make sure fragments stay together. */
-++	skb = __skb_dequeue(&txqi->frags);
-++	if (skb)
-++		goto out;
-++
-++begin:
-+ 	skb = fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
-+ 	if (!skb)
-+ 		goto out;
-+@@ -1489,16 +1503,38 @@ struct sk_buff *ieee80211_tx_dequeue(str
-+ 	ieee80211_set_skb_vif(skb, txqi);
-+ 
-+ 	hdr = (struct ieee80211_hdr *)skb->data;
-+-	if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
-++	info = IEEE80211_SKB_CB(skb);
-++	if (txq->sta && info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
-+ 		struct sta_info *sta = container_of(txq->sta, struct sta_info,
-+ 						    sta);
-+-		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-++		u8 pn_offs = 0;
-+ 
-+-		hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid);
-+-		if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
-+-			info->flags |= IEEE80211_TX_CTL_AMPDU;
-+-		else
-+-			info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-++		if (info->control.hw_key)
-++			pn_offs = ieee80211_padded_hdrlen(hw, hdr->frame_control);
-++
-++		ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
-++					   info->control.hw_key, skb);
-++	} else {
-++		struct ieee80211_tx_data tx = { };
-++
-++		__skb_queue_head_init(&tx.skbs);
-++		tx.local = local;
-++		tx.skb = skb;
-++		tx.hdrlen = ieee80211_padded_hdrlen(hw, hdr->frame_control);
-++		if (txq->sta) {
-++			tx.sta = container_of(txq->sta, struct sta_info, sta);
-++			tx.sdata = tx.sta->sdata;
-++		} else {
-++			tx.sdata = vif_to_sdata(info->control.vif);
-++		}
-++
-++		if (invoke_tx_handlers_late(&tx))
-++			goto begin;
-++
-++		skb = __skb_dequeue(&tx.skbs);
-++
-++		if (!skb_queue_empty(&tx.skbs))
-++			skb_queue_splice_tail(&tx.skbs, &txqi->frags);
-+ 	}
-+ 
-+ out:
-+@@ -1512,6 +1548,47 @@ out:
-+ }
-+ EXPORT_SYMBOL(ieee80211_tx_dequeue);
-+ 
-++static bool ieee80211_queue_skb(struct ieee80211_local *local,
-++				struct ieee80211_sub_if_data *sdata,
-++				struct sta_info *sta,
-++				struct sk_buff *skb)
-++{
-++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-++	struct fq *fq = &local->fq;
-++	struct ieee80211_vif *vif;
-++	struct txq_info *txqi;
-++	struct ieee80211_sta *pubsta;
-++
-++	if (!local->ops->wake_tx_queue ||
-++	    sdata->vif.type == NL80211_IFTYPE_MONITOR)
-++		return false;
-++
-++	if (sta && sta->uploaded)
-++		pubsta = &sta->sta;
-++	else
-++		pubsta = NULL;
-++
-++	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-++		sdata = container_of(sdata->bss,
-++				     struct ieee80211_sub_if_data, u.ap);
-++
-++	vif = &sdata->vif;
-++	txqi = ieee80211_get_txq(local, vif, pubsta, skb);
-++
-++	if (!txqi)
-++		return false;
-++
-++	info->control.vif = vif;
-++
-++	spin_lock_bh(&fq->lock);
-++	ieee80211_txq_enqueue(local, txqi, skb);
-++	spin_unlock_bh(&fq->lock);
-++
-++	drv_wake_tx_queue(local, txqi);
-++
-++	return true;
-++}
-++
-+ static bool ieee80211_tx_frags(struct ieee80211_local *local,
-+ 			       struct ieee80211_vif *vif,
-+ 			       struct ieee80211_sta *sta,
-+@@ -1519,9 +1596,7 @@ static bool ieee80211_tx_frags(struct ie
-+ 			       bool txpending)
-+ {
-+ 	struct ieee80211_tx_control control = {};
-+-	struct fq *fq = &local->fq;
-+ 	struct sk_buff *skb, *tmp;
-+-	struct txq_info *txqi;
-+ 	unsigned long flags;
-+ 
-+ 	skb_queue_walk_safe(skbs, skb, tmp) {
-+@@ -1536,21 +1611,6 @@ static bool ieee80211_tx_frags(struct ie
-+ 		}
-+ #endif
-+ 
-+-		txqi = ieee80211_get_txq(local, vif, sta, skb);
-+-		if (txqi) {
-+-			info->control.vif = vif;
-+-
-+-			__skb_unlink(skb, skbs);
-+-
-+-			spin_lock_bh(&fq->lock);
-+-			ieee80211_txq_enqueue(local, txqi, skb);
-+-			spin_unlock_bh(&fq->lock);
-+-
-+-			drv_wake_tx_queue(local, txqi);
-+-
-+-			continue;
-+-		}
-+-
-+ 		spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-+ 		if (local->queue_stop_reasons[q] ||
-+ 		    (!txpending && !skb_queue_empty(&local->pending[q]))) {
-+@@ -1671,10 +1731,13 @@ static bool __ieee80211_tx(struct ieee80
-+ /*
-+  * Invoke TX handlers, return 0 on success and non-zero if the
-+  * frame was dropped or queued.
-++ *
-++ * The handlers are split into an early and late part. The latter is everything
-++ * that can be sensitive to reordering, and will be deferred to after packets
-++ * are dequeued from the intermediate queues (when they are enabled).
-+  */
-+-static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
-++static int invoke_tx_handlers_early(struct ieee80211_tx_data *tx)
-+ {
-+-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-+ 	ieee80211_tx_result res = TX_DROP;
-+ 
-+ #define CALL_TXH(txh) \
-+@@ -1688,16 +1751,42 @@ static int invoke_tx_handlers(struct iee
-+ 	CALL_TXH(ieee80211_tx_h_check_assoc);
-+ 	CALL_TXH(ieee80211_tx_h_ps_buf);
-+ 	CALL_TXH(ieee80211_tx_h_check_control_port_protocol);
-+-	CALL_TXH(ieee80211_tx_h_select_key);
-++
-+ 	if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
-+ 		CALL_TXH(ieee80211_tx_h_rate_ctrl);
-+ 
-++ txh_done:
-++	if (unlikely(res == TX_DROP)) {
-++		I802_DEBUG_INC(tx->local->tx_handlers_drop);
-++		if (tx->skb)
-++			ieee80211_free_txskb(&tx->local->hw, tx->skb);
-++		else
-++			ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs);
-++		return -1;
-++	} else if (unlikely(res == TX_QUEUED)) {
-++		I802_DEBUG_INC(tx->local->tx_handlers_queued);
-++		return -1;
-++	}
-++
-++	return 0;
-++}
-++
-++/*
-++ * Late handlers can be called while the sta lock is held. Handlers that can
-++ * cause packets to be generated will cause deadlock!
-++ */
-++static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx)
-++{
-++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-++	ieee80211_tx_result res = TX_CONTINUE;
-++
-+ 	if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) {
-+ 		__skb_queue_tail(&tx->skbs, tx->skb);
-+ 		tx->skb = NULL;
-+ 		goto txh_done;
-+ 	}
-+ 
-++	CALL_TXH(ieee80211_tx_h_select_key);
-+ 	CALL_TXH(ieee80211_tx_h_michael_mic_add);
-+ 	CALL_TXH(ieee80211_tx_h_sequence);
-+ 	CALL_TXH(ieee80211_tx_h_fragment);
-+@@ -1724,6 +1813,15 @@ static int invoke_tx_handlers(struct iee
-+ 	return 0;
-+ }
-+ 
-++static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
-++{
-++	int r = invoke_tx_handlers_early(tx);
-++	if (r)
-++		return r;
-++
-++	return invoke_tx_handlers_late(tx);
-++}
-++
-+ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
-+ 			      struct ieee80211_vif *vif, struct sk_buff *skb,
-+ 			      int band, struct ieee80211_sta **sta)
-+@@ -1798,7 +1896,13 @@ static bool ieee80211_tx(struct ieee8021
-+ 		info->hw_queue =
-+ 			sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
-+ 
-+-	if (!invoke_tx_handlers(&tx))
-++	if (invoke_tx_handlers_early(&tx))
-++		return false;
-++
-++	if (ieee80211_queue_skb(local, sdata, tx.sta, tx.skb))
-++		return true;
-++
-++	if (!invoke_tx_handlers_late(&tx))
-+ 		result = __ieee80211_tx(local, &tx.skbs, led_len,
-+ 					tx.sta, txpending);
-+ 
-+@@ -3181,7 +3285,7 @@ out:
-+ }
-+ 
-+ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-+-				struct net_device *dev, struct sta_info *sta,
-++				struct sta_info *sta,
-+ 				struct ieee80211_fast_tx *fast_tx,
-+ 				struct sk_buff *skb)
-+ {
-+@@ -3192,9 +3296,9 @@ static bool ieee80211_xmit_fast(struct i
-+ 	struct ethhdr eth;
-+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+ 	struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
-+-	struct ieee80211_tx_data tx;
-+-	ieee80211_tx_result r;
-+ 	struct tid_ampdu_tx *tid_tx = NULL;
-++	ieee80211_tx_result r;
-++	struct ieee80211_tx_data tx;
-+ 	u8 tid = IEEE80211_NUM_TIDS;
-+ 
-+ 	/* control port protocol needs a lot of special handling */
-+@@ -3232,8 +3336,6 @@ static bool ieee80211_xmit_fast(struct i
-+ 			return true;
-+ 	}
-+ 
-+-	ieee80211_tx_stats(dev, skb->len + extra_head);
-+-
-+ 	if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
-+ 	    ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
-+ 		return true;
-+@@ -3262,24 +3364,7 @@ static bool ieee80211_xmit_fast(struct i
-+ 	info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
-+ 		      IEEE80211_TX_CTL_DONTFRAG |
-+ 		      (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
-+-
-+-	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-+-		*ieee80211_get_qos_ctl(hdr) = tid;
-+-		if (!sta->sta.txq[0])
-+-			hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
-+-	} else {
-+-		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
-+-		hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
-+-		sdata->sequence_number += 0x10;
-+-	}
-+-
-+-	if (skb_shinfo(skb)->gso_size)
-+-		sta->tx_stats.msdu[tid] +=
-+-			DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
-+-	else
-+-		sta->tx_stats.msdu[tid]++;
-+-
-+-	info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
-++	info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT;
-+ 
-+ 	__skb_queue_head_init(&tx.skbs);
-+ 
-+@@ -3305,22 +3390,71 @@ static bool ieee80211_xmit_fast(struct i
-+ 		}
-+ 	}
-+ 
-++	if (ieee80211_queue_skb(local, sdata, sta, skb))
-++		return true;
-++
-++	ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
-++				   &fast_tx->key->conf, skb);
-++
-++	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-++		sdata = container_of(sdata->bss,
-++				     struct ieee80211_sub_if_data, u.ap);
-++
-++	__skb_queue_tail(&tx.skbs, skb);
-++	ieee80211_tx_frags(local, &sdata->vif, &sta->sta, &tx.skbs, false);
-++
-++	return true;
-++}
-++
-++/*
-++ * Can be called while the sta lock is held. Anything that can cause packets to
-++ * be generated will cause deadlock!
-++ */
-++static bool ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
-++				       struct sta_info *sta, u8 pn_offs,
-++				       struct ieee80211_key_conf *key_conf,
-++				       struct sk_buff *skb)
-++{
-++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-++	struct ieee80211_hdr *hdr = (void *)skb->data;
-++	u8 tid = IEEE80211_NUM_TIDS;
-++
-++	ieee80211_tx_stats(skb->dev, skb->len);
-++
-++	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-++		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-++		*ieee80211_get_qos_ctl(hdr) = tid;
-++		hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
-++	} else {
-++		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
-++		hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
-++		sdata->sequence_number += 0x10;
-++	}
-++
-++	if (skb_shinfo(skb)->gso_size)
-++		sta->tx_stats.msdu[tid] +=
-++			DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
-++	else
-++		sta->tx_stats.msdu[tid]++;
-++
-++	info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
-++
-+ 	/* statistics normally done by ieee80211_tx_h_stats (but that
-+ 	 * has to consider fragmentation, so is more complex)
-+ 	 */
-+ 	sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
-+ 	sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
-+ 
-+-	if (fast_tx->pn_offs) {
-++	if (pn_offs) {
-+ 		u64 pn;
-+-		u8 *crypto_hdr = skb->data + fast_tx->pn_offs;
-++		u8 *crypto_hdr = skb->data + pn_offs;
-+ 
-+-		switch (fast_tx->key->conf.cipher) {
-++		switch (key_conf->cipher) {
-+ 		case WLAN_CIPHER_SUITE_CCMP:
-+ 		case WLAN_CIPHER_SUITE_CCMP_256:
-+ 		case WLAN_CIPHER_SUITE_GCMP:
-+ 		case WLAN_CIPHER_SUITE_GCMP_256:
-+-			pn = atomic64_inc_return(&fast_tx->key->conf.tx_pn);
-++			pn = atomic64_inc_return(&key_conf->tx_pn);
-+ 			crypto_hdr[0] = pn;
-+ 			crypto_hdr[1] = pn >> 8;
-+ 			crypto_hdr[4] = pn >> 16;
-+@@ -3331,12 +3465,6 @@ static bool ieee80211_xmit_fast(struct i
-+ 		}
-+ 	}
-+ 
-+-	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-+-		sdata = container_of(sdata->bss,
-+-				     struct ieee80211_sub_if_data, u.ap);
-+-
-+-	__skb_queue_tail(&tx.skbs, skb);
-+-	ieee80211_tx_frags(local, &sdata->vif, &sta->sta, &tx.skbs, false);
-+ 	return true;
-+ }
-+ 
-+@@ -3364,7 +3492,7 @@ void __ieee80211_subif_start_xmit(struct
-+ 		fast_tx = rcu_dereference(sta->fast_tx);
-+ 
-+ 		if (fast_tx &&
-+-		    ieee80211_xmit_fast(sdata, dev, sta, fast_tx, skb))
-++		    ieee80211_xmit_fast(sdata, sta, fast_tx, skb))
-+ 			goto out;
-+ 	}
-+ 
-+--- a/include/net/mac80211.h
-++++ b/include/net/mac80211.h
-+@@ -715,6 +715,7 @@ enum mac80211_tx_info_flags {
-+  *	frame (PS-Poll or uAPSD).
-+  * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
-+  * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
-++ * @IEEE80211_TX_CTRL_FAST_XMIT: This frame is going through the fast_xmit path
-+  *
-+  * These flags are used in tx_info->control.flags.
-+  */
-+@@ -723,6 +724,7 @@ enum mac80211_tx_control_flags {
-+ 	IEEE80211_TX_CTRL_PS_RESPONSE		= BIT(1),
-+ 	IEEE80211_TX_CTRL_RATE_INJECT		= BIT(2),
-+ 	IEEE80211_TX_CTRL_AMSDU			= BIT(3),
-++	IEEE80211_TX_CTRL_FAST_XMIT		= BIT(4),
-+ };
-+ 
-+ /*
-+--- a/net/mac80211/ieee80211_i.h
-++++ b/net/mac80211/ieee80211_i.h
-+@@ -814,11 +814,13 @@ enum txq_info_flags {
-+  * @def_flow: used as a fallback flow when a packet destined to @tin hashes to
-+  *	a fq_flow which is already owned by a different tin
-+  * @def_cvars: codel vars for @def_flow
-++ * @frags: used to keep fragments created after dequeue
-+  */
-+ struct txq_info {
-+ 	struct fq_tin tin;
-+ 	struct fq_flow def_flow;
-+ 	struct codel_vars def_cvars;
-++	struct sk_buff_head frags;
-+ 	unsigned long flags;
-+ 
-+ 	/* keep last! */
-diff --git a/package/kernel/mac80211/patches/346-mac80211-fix-sequence-number-assignment-for-PS-respo.patch b/package/kernel/mac80211/patches/346-mac80211-fix-sequence-number-assignment-for-PS-respo.patch
-deleted file mode 100644
-index a82d12f..0000000
---- a/package/kernel/mac80211/patches/346-mac80211-fix-sequence-number-assignment-for-PS-respo.patch
-+++ /dev/null
-@@ -1,107 +0,0 @@
--From: Felix Fietkau <nbd@nbd.name>
--Date: Sun, 4 Sep 2016 17:46:24 +0200
--Subject: [PATCH] mac80211: fix sequence number assignment for PS response
-- frames
--
--When using intermediate queues, sequence number allocation is deferred
--until dequeue. This doesn't work for PS response frames, which bypass
--those queues.
--
--Signed-off-by: Felix Fietkau <nbd@nbd.name>
-----
--
----- a/net/mac80211/tx.c
--+++ b/net/mac80211/tx.c
--@@ -792,6 +792,36 @@ static __le16 ieee80211_tx_next_seq(stru
-- 	return ret;
-- }
-- 
--+static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
--+					  struct ieee80211_vif *vif,
--+					  struct ieee80211_sta *pubsta,
--+					  struct sk_buff *skb)
--+{
--+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
--+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
--+	struct ieee80211_txq *txq = NULL;
--+
--+	if ((info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) ||
--+	    (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
--+		return NULL;
--+
--+	if (!ieee80211_is_data(hdr->frame_control))
--+		return NULL;
--+
--+	if (pubsta) {
--+		u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
--+
--+		txq = pubsta->txq[tid];
--+	} else if (vif) {
--+		txq = vif->txq;
--+	}
--+
--+	if (!txq)
--+		return NULL;
--+
--+	return to_txq_info(txq);
--+}
--+
-- static ieee80211_tx_result debug_noinline
-- ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
-- {
--@@ -849,7 +879,8 @@ ieee80211_tx_h_sequence(struct ieee80211
-- 	tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
-- 	tx->sta->tx_stats.msdu[tid]++;
-- 
---	if (!tx->sta->sta.txq[0])
--+	if (!ieee80211_get_txq(tx->local, info->control.vif, &tx->sta->sta,
--+			       tx->skb))
-- 		hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
-- 
-- 	return TX_CONTINUE;
--@@ -1238,36 +1269,6 @@ ieee80211_tx_prepare(struct ieee80211_su
-- 	return TX_CONTINUE;
-- }
-- 
---static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
---					  struct ieee80211_vif *vif,
---					  struct ieee80211_sta *pubsta,
---					  struct sk_buff *skb)
---{
---	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
---	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
---	struct ieee80211_txq *txq = NULL;
---
---	if ((info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) ||
---	    (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
---		return NULL;
---
---	if (!ieee80211_is_data(hdr->frame_control))
---		return NULL;
---
---	if (pubsta) {
---		u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
---
---		txq = pubsta->txq[tid];
---	} else if (vif) {
---		txq = vif->txq;
---	}
---
---	if (!txq)
---		return NULL;
---
---	return to_txq_info(txq);
---}
---
-- static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb)
-- {
-- 	IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
--@@ -3265,7 +3266,7 @@ static bool ieee80211_xmit_fast(struct i
-- 
-- 	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-- 		*ieee80211_get_qos_ctl(hdr) = tid;
---		if (!sta->sta.txq[0])
--+		if (!ieee80211_get_txq(local, &sdata->vif, &sta->sta, skb))
-- 			hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
-- 	} else {
-- 		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
-diff --git a/package/kernel/mac80211/patches/522-mac80211_configure_antenna_gain.patch b/package/kernel/mac80211/patches/522-mac80211_configure_antenna_gain.patch
-index c40598d..aba065e 100644
---- a/package/kernel/mac80211/patches/522-mac80211_configure_antenna_gain.patch
-+++ b/package/kernel/mac80211/patches/522-mac80211_configure_antenna_gain.patch
-@@ -18,7 +18,7 @@
-  				const u8 *addr);
- --- a/include/net/mac80211.h
- +++ b/include/net/mac80211.h
--@@ -1317,6 +1317,7 @@ enum ieee80211_smps_mode {
-+@@ -1319,6 +1319,7 @@ enum ieee80211_smps_mode {
-   *
-   * @power_level: requested transmit power (in dBm), backward compatibility
-   *	value only that is set to the minimum of all interfaces
-@@ -26,7 +26,7 @@
-   *
-   * @chandef: the channel definition to tune to
-   * @radar_enabled: whether radar detection is enabled
--@@ -1337,6 +1338,7 @@ enum ieee80211_smps_mode {
-+@@ -1339,6 +1340,7 @@ enum ieee80211_smps_mode {
-  struct ieee80211_conf {
-  	u32 flags;
-  	int power_level, dynamic_ps_timeout;
-@@ -87,7 +87,7 @@
-  	CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
- --- a/net/mac80211/ieee80211_i.h
- +++ b/net/mac80211/ieee80211_i.h
--@@ -1338,6 +1338,7 @@ struct ieee80211_local {
-+@@ -1340,6 +1340,7 @@ struct ieee80211_local {
-  	int dynamic_ps_forced_timeout;
-  
-  	int user_power_level; /* in dBm, for all interfaces */
diff --git a/patches/openwrt/0074-ath9k-revert-temperature-compensation-support-patch-FS-111.patch b/patches/openwrt/0074-ath9k-revert-temperature-compensation-support-patch-FS-111.patch
new file mode 100644
index 0000000000000000000000000000000000000000..803db695c7c4a1c7bf3af838d0ebae15980f2d12
--- /dev/null
+++ b/patches/openwrt/0074-ath9k-revert-temperature-compensation-support-patch-FS-111.patch
@@ -0,0 +1,124 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Tue, 11 Oct 2016 02:54:27 +0200
+Subject: ath9k: revert temperature compensation support patch (FS#111)
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+
+Backport of LEDE 3e4d0e3e77dcf9b2116e5ed53f30e2bf53b1c6b7
+
+diff --git a/package/kernel/mac80211/patches/328-ath9k_hw-implement-temperature-compensation-support-.patch b/package/kernel/mac80211/patches/328-ath9k_hw-implement-temperature-compensation-support-.patch
+deleted file mode 100644
+index cff32ad..0000000
+--- a/package/kernel/mac80211/patches/328-ath9k_hw-implement-temperature-compensation-support-.patch
++++ /dev/null
+@@ -1,97 +0,0 @@
+-From: Felix Fietkau <nbd@nbd.name>
+-Date: Mon, 11 Jul 2016 11:35:55 +0200
+-Subject: [PATCH] ath9k_hw: implement temperature compensation support for
+- AR9003+
+-
+-Signed-off-by: Felix Fietkau <nbd@nbd.name>
+----
+-
+---- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+-+++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+-@@ -33,6 +33,7 @@ struct coeff {
+- 
+- enum ar9003_cal_types {
+- 	IQ_MISMATCH_CAL = BIT(0),
+-+	TEMP_COMP_CAL = BIT(1),
+- };
+- 
+- static void ar9003_hw_setup_calibration(struct ath_hw *ah,
+-@@ -58,6 +59,12 @@ static void ar9003_hw_setup_calibration(
+- 		/* Kick-off cal */
+- 		REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL);
+- 		break;
+-+	case TEMP_COMP_CAL:
+-+		ath_dbg(common, CALIBRATE,
+-+			"starting Temperature Compensation Calibration\n");
+-+		REG_SET_BIT(ah, AR_CH0_THERM, AR_CH0_THERM_LOCAL);
+-+		REG_SET_BIT(ah, AR_CH0_THERM, AR_CH0_THERM_START);
+-+		break;
+- 	default:
+- 		ath_err(common, "Invalid calibration type\n");
+- 		break;
+-@@ -86,7 +93,8 @@ static bool ar9003_hw_per_calibration(st
+- 		/*
+- 		* Accumulate cal measures for active chains
+- 		*/
+--		cur_caldata->calCollect(ah);
+-+		if (cur_caldata->calCollect)
+-+			cur_caldata->calCollect(ah);
+- 		ah->cal_samples++;
+- 
+- 		if (ah->cal_samples >= cur_caldata->calNumSamples) {
+-@@ -99,7 +107,8 @@ static bool ar9003_hw_per_calibration(st
+- 			/*
+- 			* Process accumulated data
+- 			*/
+--			cur_caldata->calPostProc(ah, numChains);
+-+			if (cur_caldata->calPostProc)
+-+				cur_caldata->calPostProc(ah, numChains);
+- 
+- 			/* Calibration has finished. */
+- 			caldata->CalValid |= cur_caldata->calType;
+-@@ -314,9 +323,16 @@ static const struct ath9k_percal_data iq
+- 	ar9003_hw_iqcalibrate
+- };
+- 
+-+static const struct ath9k_percal_data temp_cal_single_sample = {
+-+	TEMP_COMP_CAL,
+-+	MIN_CAL_SAMPLES,
+-+	PER_MAX_LOG_COUNT,
+-+};
+-+
+- static void ar9003_hw_init_cal_settings(struct ath_hw *ah)
+- {
+- 	ah->iq_caldata.calData = &iq_cal_single_sample;
+-+	ah->temp_caldata.calData = &temp_cal_single_sample;
+- 
+- 	if (AR_SREV_9300_20_OR_LATER(ah)) {
+- 		ah->enabled_cals |= TX_IQ_CAL;
+-@@ -324,7 +340,7 @@ static void ar9003_hw_init_cal_settings(
+- 			ah->enabled_cals |= TX_IQ_ON_AGC_CAL;
+- 	}
+- 
+--	ah->supp_cals = IQ_MISMATCH_CAL;
+-+	ah->supp_cals = IQ_MISMATCH_CAL | TEMP_COMP_CAL;
+- }
+- 
+- #define OFF_UPPER_LT 24
+-@@ -1383,6 +1399,9 @@ static void ar9003_hw_init_cal_common(st
+- 	INIT_CAL(&ah->iq_caldata);
+- 	INSERT_CAL(ah, &ah->iq_caldata);
+- 
+-+	INIT_CAL(&ah->temp_caldata);
+-+	INSERT_CAL(ah, &ah->temp_caldata);
+-+
+- 	/* Initialize current pointer to first element in list */
+- 	ah->cal_list_curr = ah->cal_list;
+- 
+---- a/drivers/net/wireless/ath/ath9k/hw.h
+-+++ b/drivers/net/wireless/ath/ath9k/hw.h
+-@@ -830,6 +830,7 @@ struct ath_hw {
+- 	/* Calibration */
+- 	u32 supp_cals;
+- 	struct ath9k_cal_list iq_caldata;
+-+	struct ath9k_cal_list temp_caldata;
+- 	struct ath9k_cal_list adcgain_caldata;
+- 	struct ath9k_cal_list adcdc_caldata;
+- 	struct ath9k_cal_list *cal_list;
+diff --git a/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch b/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch
+index 4615643..6edef09 100644
+--- a/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch
++++ b/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch
+@@ -84,7 +84,7 @@
+  	bool reset_power_on;
+  	bool htc_reset_init;
+  
+-@@ -1068,6 +1076,7 @@ void ath9k_hw_check_nav(struct ath_hw *a
++@@ -1067,6 +1075,7 @@ void ath9k_hw_check_nav(struct ath_hw *a
+  bool ath9k_hw_check_alive(struct ath_hw *ah);
+  
+  bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
diff --git a/patches/openwrt/0074-mac80211-fix-tx-issue-with-CCMP-PN-generated-in-hardware.patch b/patches/openwrt/0074-mac80211-fix-tx-issue-with-CCMP-PN-generated-in-hardware.patch
deleted file mode 100644
index f464421c73c77d96e157143cbd2fda3e56de4275..0000000000000000000000000000000000000000
--- a/patches/openwrt/0074-mac80211-fix-tx-issue-with-CCMP-PN-generated-in-hardware.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-From: Matthias Schiffer <mschiffer@universe-factory.net>
-Date: Fri, 30 Sep 2016 16:57:57 +0200
-Subject: mac80211: fix tx issue with CCMP PN generated in hardware
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
-
-Backport of LEDE f3747020e202883a43729fc245986f9e36289d6c
-
-diff --git a/package/kernel/mac80211/patches/346-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch b/package/kernel/mac80211/patches/346-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch
-index 8ceed51..aba1ff4 100644
---- a/package/kernel/mac80211/patches/346-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch
-+++ b/package/kernel/mac80211/patches/346-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch
-@@ -404,7 +404,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
-  	sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
-  
- -	if (fast_tx->pn_offs) {
--+	if (pn_offs) {
-++	if (pn_offs && (key_conf->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
-  		u64 pn;
- -		u8 *crypto_hdr = skb->data + fast_tx->pn_offs;
- +		u8 *crypto_hdr = skb->data + pn_offs;
diff --git a/patches/openwrt/0075-ath9k-remove-patch-causing-stability-issues-with-powersave-devices-FS-176.patch b/patches/openwrt/0075-ath9k-remove-patch-causing-stability-issues-with-powersave-devices-FS-176.patch
deleted file mode 100644
index ddfb6445981caa6123a7ab2bc575a1e55002206e..0000000000000000000000000000000000000000
--- a/patches/openwrt/0075-ath9k-remove-patch-causing-stability-issues-with-powersave-devices-FS-176.patch
+++ /dev/null
@@ -1,1513 +0,0 @@
-From: Matthias Schiffer <mschiffer@universe-factory.net>
-Date: Fri, 30 Sep 2016 16:58:01 +0200
-Subject: ath9k: remove patch causing stability issues with powersave devices (FS#176)
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
-
-Backport of LEDE fc88eb3fdfce6d39b4c62158cf6f42605a360a1e
-
-diff --git a/package/kernel/mac80211/patches/341-ath9k-release-PS-buffered-frames-as-A-MPDU-if-enable.patch b/package/kernel/mac80211/patches/341-ath9k-release-PS-buffered-frames-as-A-MPDU-if-enable.patch
-deleted file mode 100644
-index 1cc1667..0000000
---- a/package/kernel/mac80211/patches/341-ath9k-release-PS-buffered-frames-as-A-MPDU-if-enable.patch
-+++ /dev/null
-@@ -1,40 +0,0 @@
--From: Felix Fietkau <nbd@nbd.name>
--Date: Sun, 28 Aug 2016 13:15:10 +0200
--Subject: [PATCH] ath9k: release PS buffered frames as A-MPDU if enabled
--
--Signed-off-by: Felix Fietkau <nbd@nbd.name>
-----
--
----- a/drivers/net/wireless/ath/ath9k/xmit.c
--+++ b/drivers/net/wireless/ath/ath9k/xmit.c
--@@ -1660,10 +1660,11 @@ void ath9k_release_buffered_frames(struc
-- 	struct ath_node *an = (struct ath_node *)sta->drv_priv;
-- 	struct ath_txq *txq = sc->tx.uapsdq;
-- 	struct ieee80211_tx_info *info;
--+	struct ath_frame_info *fi;
-- 	struct list_head bf_q;
-- 	struct ath_buf *bf_tail = NULL, *bf;
-- 	int sent = 0;
---	int i;
--+	int n, i;
-- 
-- 	INIT_LIST_HEAD(&bf_q);
-- 	for (i = 0; tids && nframes; i++, tids >>= 1) {
--@@ -1683,10 +1684,15 @@ void ath9k_release_buffered_frames(struc
-- 			ath9k_set_moredata(sc, bf, true);
-- 			list_add_tail(&bf->list, &bf_q);
-- 			ath_set_rates(tid->an->vif, tid->an->sta, bf, true);
---			if (bf_isampdu(bf)) {
--+			if (bf_isampdu(bf))
-- 				ath_tx_addto_baw(sc, tid, bf);
---				bf->bf_state.bf_type &= ~BUF_AGGR;
--+			if (bf_isaggr(bf)) {
--+				fi = get_frame_info(bf->bf_mpdu);
--+				n = ath_compute_num_delims(sc, tid, bf,
--+							   fi->framelen, true);
--+				bf->bf_state.ndelim = n;
-- 			}
--+
-- 			if (bf_tail)
-- 				bf_tail->bf_next = bf;
-- 
-diff --git a/package/kernel/mac80211/patches/341-ath9k-report-tx-status-on-EOSP.patch b/package/kernel/mac80211/patches/341-ath9k-report-tx-status-on-EOSP.patch
-new file mode 100644
-index 0000000..80a3074
---- /dev/null
-+++ b/package/kernel/mac80211/patches/341-ath9k-report-tx-status-on-EOSP.patch
-@@ -0,0 +1,19 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sun, 28 Aug 2016 13:23:27 +0200
-+Subject: [PATCH] ath9k: report tx status on EOSP
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/xmit.c
-++++ b/drivers/net/wireless/ath/ath9k/xmit.c
-+@@ -86,7 +86,8 @@ static void ath_tx_status(struct ieee802
-+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+ 	struct ieee80211_sta *sta = info->status.status_driver_data[0];
-+ 
-+-	if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
-++	if (info->flags & (IEEE80211_TX_CTL_REQ_TX_STATUS |
-++			   IEEE80211_TX_STATUS_EOSP)) {
-+ 		ieee80211_tx_status(hw, skb);
-+ 		return;
-+ 	}
-diff --git a/package/kernel/mac80211/patches/342-ath9k-fix-block-ack-window-tracking-issues.patch b/package/kernel/mac80211/patches/342-ath9k-fix-block-ack-window-tracking-issues.patch
-new file mode 100644
-index 0000000..fea147b
---- /dev/null
-+++ b/package/kernel/mac80211/patches/342-ath9k-fix-block-ack-window-tracking-issues.patch
-@@ -0,0 +1,114 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Tue, 30 Aug 2016 12:44:08 +0200
-+Subject: [PATCH] ath9k: fix block-ack window tracking issues
-+
-+Ensure that a buffer gets tracked as part of the block-ack window as
-+soon as it's dequeued from the tid for the first time. Ensure that
-+double calls to ath_tx_addto_baw (e.g. on retransmission) don't cause
-+any issues.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/drivers/net/wireless/ath/ath9k/xmit.c
-++++ b/drivers/net/wireless/ath/ath9k/xmit.c
-+@@ -62,7 +62,7 @@ static void ath_tx_rc_status(struct ath_
-+ 			     struct ath_tx_status *ts, int nframes, int nbad,
-+ 			     int txok);
-+ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
-+-			      int seqno);
-++			      struct ath_buf *bf);
-+ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
-+ 					   struct ath_txq *txq,
-+ 					   struct ath_atx_tid *tid,
-+@@ -300,7 +300,7 @@ static void ath_tx_flush_tid(struct ath_
-+ 		}
-+ 
-+ 		if (fi->baw_tracked) {
-+-			ath_tx_update_baw(sc, tid, bf->bf_state.seqno);
-++			ath_tx_update_baw(sc, tid, bf);
-+ 			sendbar = true;
-+ 		}
-+ 
-+@@ -316,10 +316,15 @@ static void ath_tx_flush_tid(struct ath_
-+ }
-+ 
-+ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
-+-			      int seqno)
-++			      struct ath_buf *bf)
-+ {
-++	struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu);
-++	u16 seqno = bf->bf_state.seqno;
-+ 	int index, cindex;
-+ 
-++	if (!fi->baw_tracked)
-++		return;
-++
-+ 	index  = ATH_BA_INDEX(tid->seq_start, seqno);
-+ 	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
-+ 
-+@@ -340,6 +345,9 @@ static void ath_tx_addto_baw(struct ath_
-+ 	u16 seqno = bf->bf_state.seqno;
-+ 	int index, cindex;
-+ 
-++	if (fi->baw_tracked)
-++		return;
-++
-+ 	index  = ATH_BA_INDEX(tid->seq_start, seqno);
-+ 	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
-+ 	__set_bit(cindex, tid->tx_buf);
-+@@ -616,7 +624,7 @@ static void ath_tx_complete_aggr(struct
-+ 			 * complete the acked-ones/xretried ones; update
-+ 			 * block-ack window
-+ 			 */
-+-			ath_tx_update_baw(sc, tid, seqno);
-++			ath_tx_update_baw(sc, tid, bf);
-+ 
-+ 			if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) {
-+ 				memcpy(tx_info->control.rates, rates, sizeof(rates));
-+@@ -646,7 +654,7 @@ static void ath_tx_complete_aggr(struct
-+ 				 * run out of tx buf.
-+ 				 */
-+ 				if (!tbf) {
-+-					ath_tx_update_baw(sc, tid, seqno);
-++					ath_tx_update_baw(sc, tid, bf);
-+ 
-+ 					ath_tx_complete_buf(sc, bf, txq,
-+ 							    &bf_head, NULL, ts,
-+@@ -986,11 +994,14 @@ ath_tx_get_tid_subframe(struct ath_softc
-+ 
-+ 			INIT_LIST_HEAD(&bf_head);
-+ 			list_add(&bf->list, &bf_head);
-+-			ath_tx_update_baw(sc, tid, seqno);
-++			ath_tx_update_baw(sc, tid, bf);
-+ 			ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, &ts, 0);
-+ 			continue;
-+ 		}
-+ 
-++		if (bf_isampdu(bf))
-++			ath_tx_addto_baw(sc, tid, bf);
-++
-+ 		return bf;
-+ 	}
-+ 
-+@@ -1048,8 +1059,6 @@ ath_tx_form_aggr(struct ath_softc *sc, s
-+ 		bf->bf_next = NULL;
-+ 
-+ 		/* link buffers of this frame to the aggregate */
-+-		if (!fi->baw_tracked)
-+-			ath_tx_addto_baw(sc, tid, bf);
-+ 		bf->bf_state.ndelim = ndelim;
-+ 
-+ 		list_add_tail(&bf->list, bf_q);
-+@@ -1684,10 +1693,8 @@ void ath9k_release_buffered_frames(struc
-+ 			ath9k_set_moredata(sc, bf, true);
-+ 			list_add_tail(&bf->list, &bf_q);
-+ 			ath_set_rates(tid->an->vif, tid->an->sta, bf, true);
-+-			if (bf_isampdu(bf)) {
-+-				ath_tx_addto_baw(sc, tid, bf);
-++			if (bf_isampdu(bf))
-+ 				bf->bf_state.bf_type &= ~BUF_AGGR;
-+-			}
-+ 			if (bf_tail)
-+ 				bf_tail->bf_next = bf;
-+ 
-diff --git a/package/kernel/mac80211/patches/342-ath9k-report-tx-status-on-EOSP.patch b/package/kernel/mac80211/patches/342-ath9k-report-tx-status-on-EOSP.patch
-deleted file mode 100644
-index 80a3074..0000000
---- a/package/kernel/mac80211/patches/342-ath9k-report-tx-status-on-EOSP.patch
-+++ /dev/null
-@@ -1,19 +0,0 @@
--From: Felix Fietkau <nbd@nbd.name>
--Date: Sun, 28 Aug 2016 13:23:27 +0200
--Subject: [PATCH] ath9k: report tx status on EOSP
--
--Signed-off-by: Felix Fietkau <nbd@nbd.name>
-----
--
----- a/drivers/net/wireless/ath/ath9k/xmit.c
--+++ b/drivers/net/wireless/ath/ath9k/xmit.c
--@@ -86,7 +86,8 @@ static void ath_tx_status(struct ieee802
-- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-- 	struct ieee80211_sta *sta = info->status.status_driver_data[0];
-- 
---	if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) {
--+	if (info->flags & (IEEE80211_TX_CTL_REQ_TX_STATUS |
--+			   IEEE80211_TX_STATUS_EOSP)) {
-- 		ieee80211_tx_status(hw, skb);
-- 		return;
-- 	}
-diff --git a/package/kernel/mac80211/patches/343-ath9k-fix-block-ack-window-tracking-issues.patch b/package/kernel/mac80211/patches/343-ath9k-fix-block-ack-window-tracking-issues.patch
-deleted file mode 100644
-index 007a8d7d..0000000
---- a/package/kernel/mac80211/patches/343-ath9k-fix-block-ack-window-tracking-issues.patch
-+++ /dev/null
-@@ -1,111 +0,0 @@
--From: Felix Fietkau <nbd@nbd.name>
--Date: Tue, 30 Aug 2016 12:44:08 +0200
--Subject: [PATCH] ath9k: fix block-ack window tracking issues
--
--Ensure that a buffer gets tracked as part of the block-ack window as
--soon as it's dequeued from the tid for the first time. Ensure that
--double calls to ath_tx_addto_baw (e.g. on retransmission) don't cause
--any issues.
--
--Signed-off-by: Felix Fietkau <nbd@nbd.name>
-----
--
----- a/drivers/net/wireless/ath/ath9k/xmit.c
--+++ b/drivers/net/wireless/ath/ath9k/xmit.c
--@@ -62,7 +62,7 @@ static void ath_tx_rc_status(struct ath_
-- 			     struct ath_tx_status *ts, int nframes, int nbad,
-- 			     int txok);
-- static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
---			      int seqno);
--+			      struct ath_buf *bf);
-- static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc,
-- 					   struct ath_txq *txq,
-- 					   struct ath_atx_tid *tid,
--@@ -300,7 +300,7 @@ static void ath_tx_flush_tid(struct ath_
-- 		}
-- 
-- 		if (fi->baw_tracked) {
---			ath_tx_update_baw(sc, tid, bf->bf_state.seqno);
--+			ath_tx_update_baw(sc, tid, bf);
-- 			sendbar = true;
-- 		}
-- 
--@@ -316,10 +316,15 @@ static void ath_tx_flush_tid(struct ath_
-- }
-- 
-- static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
---			      int seqno)
--+			      struct ath_buf *bf)
-- {
--+	struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu);
--+	u16 seqno = bf->bf_state.seqno;
-- 	int index, cindex;
-- 
--+	if (!fi->baw_tracked)
--+		return;
--+
-- 	index  = ATH_BA_INDEX(tid->seq_start, seqno);
-- 	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
-- 
--@@ -340,6 +345,9 @@ static void ath_tx_addto_baw(struct ath_
-- 	u16 seqno = bf->bf_state.seqno;
-- 	int index, cindex;
-- 
--+	if (fi->baw_tracked)
--+		return;
--+
-- 	index  = ATH_BA_INDEX(tid->seq_start, seqno);
-- 	cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
-- 	__set_bit(cindex, tid->tx_buf);
--@@ -616,7 +624,7 @@ static void ath_tx_complete_aggr(struct
-- 			 * complete the acked-ones/xretried ones; update
-- 			 * block-ack window
-- 			 */
---			ath_tx_update_baw(sc, tid, seqno);
--+			ath_tx_update_baw(sc, tid, bf);
-- 
-- 			if (rc_update && (acked_cnt == 1 || txfail_cnt == 1)) {
-- 				memcpy(tx_info->control.rates, rates, sizeof(rates));
--@@ -646,7 +654,7 @@ static void ath_tx_complete_aggr(struct
-- 				 * run out of tx buf.
-- 				 */
-- 				if (!tbf) {
---					ath_tx_update_baw(sc, tid, seqno);
--+					ath_tx_update_baw(sc, tid, bf);
-- 
-- 					ath_tx_complete_buf(sc, bf, txq,
-- 							    &bf_head, NULL, ts,
--@@ -986,11 +994,14 @@ ath_tx_get_tid_subframe(struct ath_softc
-- 
-- 			INIT_LIST_HEAD(&bf_head);
-- 			list_add(&bf->list, &bf_head);
---			ath_tx_update_baw(sc, tid, seqno);
--+			ath_tx_update_baw(sc, tid, bf);
-- 			ath_tx_complete_buf(sc, bf, txq, &bf_head, NULL, &ts, 0);
-- 			continue;
-- 		}
-- 
--+		if (bf_isampdu(bf))
--+			ath_tx_addto_baw(sc, tid, bf);
--+
-- 		return bf;
-- 	}
-- 
--@@ -1048,8 +1059,6 @@ ath_tx_form_aggr(struct ath_softc *sc, s
-- 		bf->bf_next = NULL;
-- 
-- 		/* link buffers of this frame to the aggregate */
---		if (!fi->baw_tracked)
---			ath_tx_addto_baw(sc, tid, bf);
-- 		bf->bf_state.ndelim = ndelim;
-- 
-- 		list_add_tail(&bf->list, bf_q);
--@@ -1685,8 +1694,6 @@ void ath9k_release_buffered_frames(struc
-- 			ath9k_set_moredata(sc, bf, true);
-- 			list_add_tail(&bf->list, &bf_q);
-- 			ath_set_rates(tid->an->vif, tid->an->sta, bf, true);
---			if (bf_isampdu(bf))
---				ath_tx_addto_baw(sc, tid, bf);
-- 			if (bf_isaggr(bf)) {
-- 				fi = get_frame_info(bf->bf_mpdu);
-- 				n = ath_compute_num_delims(sc, tid, bf,
-diff --git a/package/kernel/mac80211/patches/343-mac80211-send-delBA-on-unexpected-BlockAck-data-fram.patch b/package/kernel/mac80211/patches/343-mac80211-send-delBA-on-unexpected-BlockAck-data-fram.patch
-new file mode 100644
-index 0000000..3bbca22
---- /dev/null
-+++ b/package/kernel/mac80211/patches/343-mac80211-send-delBA-on-unexpected-BlockAck-data-fram.patch
-@@ -0,0 +1,64 @@
-+From: Johannes Berg <johannes.berg@intel.com>
-+Date: Mon, 29 Aug 2016 23:25:18 +0300
-+Subject: [PATCH] mac80211: send delBA on unexpected BlockAck data frames
-+
-+When we receive data frames with ACK policy BlockAck, send
-+delBA as requested by the 802.11 spec. Since this would be
-+happening for every frame inside an A-MPDU if it's really
-+received outside a session, limit it to a single attempt.
-+
-+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-+---
-+
-+--- a/net/mac80211/agg-rx.c
-++++ b/net/mac80211/agg-rx.c
-+@@ -388,8 +388,10 @@ void __ieee80211_start_rx_ba_session(str
-+ 	}
-+ 
-+ end:
-+-	if (status == WLAN_STATUS_SUCCESS)
-++	if (status == WLAN_STATUS_SUCCESS) {
-+ 		__set_bit(tid, sta->ampdu_mlme.agg_session_valid);
-++		__clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
-++	}
-+ 	mutex_unlock(&sta->ampdu_mlme.mtx);
-+ 
-+ end_no_lock:
-+--- a/net/mac80211/rx.c
-++++ b/net/mac80211/rx.c
-+@@ -1072,8 +1072,15 @@ static void ieee80211_rx_reorder_ampdu(s
-+ 	tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
-+ 
-+ 	tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
-+-	if (!tid_agg_rx)
-++	if (!tid_agg_rx) {
-++		if (ack_policy == IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK &&
-++		    !test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
-++		    !test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
-++			ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
-++					     WLAN_BACK_RECIPIENT,
-++					     WLAN_REASON_QSTA_REQUIRE_SETUP);
-+ 		goto dont_reorder;
-++	}
-+ 
-+ 	/* qos null data frames are excluded */
-+ 	if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
-+--- a/net/mac80211/sta_info.h
-++++ b/net/mac80211/sta_info.h
-+@@ -230,6 +230,8 @@ struct tid_ampdu_rx {
-+  * @tid_rx_stop_requested:  bitmap indicating which BA sessions per TID the
-+  *	driver requested to close until the work for it runs
-+  * @agg_session_valid: bitmap indicating which TID has a rx BA session open on
-++ * @unexpected_agg: bitmap indicating which TID already sent a delBA due to
-++ *	unexpected aggregation related frames outside a session
-+  * @work: work struct for starting/stopping aggregation
-+  * @tid_tx: aggregation info for Tx per TID
-+  * @tid_start_tx: sessions where start was requested
-+@@ -244,6 +246,7 @@ struct sta_ampdu_mlme {
-+ 	unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-+ 	unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-+ 	unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-++	unsigned long unexpected_agg[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-+ 	/* tx */
-+ 	struct work_struct work;
-+ 	struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS];
-diff --git a/package/kernel/mac80211/patches/344-mac80211-send-delBA-on-unexpected-BlockAck-Request.patch b/package/kernel/mac80211/patches/344-mac80211-send-delBA-on-unexpected-BlockAck-Request.patch
-new file mode 100644
-index 0000000..c3d3118
---- /dev/null
-+++ b/package/kernel/mac80211/patches/344-mac80211-send-delBA-on-unexpected-BlockAck-Request.patch
-@@ -0,0 +1,26 @@
-+From: Johannes Berg <johannes.berg@intel.com>
-+Date: Mon, 29 Aug 2016 23:25:19 +0300
-+Subject: [PATCH] mac80211: send delBA on unexpected BlockAck Request
-+
-+If we don't have a BA session, send delBA, as requested by the
-+IEEE 802.11 spec. Apply the same limit of sending such a delBA
-+only once as in the previous patch.
-+
-+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-+---
-+
-+--- a/net/mac80211/rx.c
-++++ b/net/mac80211/rx.c
-+@@ -2537,6 +2537,12 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_
-+ 
-+ 		tid = le16_to_cpu(bar_data.control) >> 12;
-+ 
-++		if (!test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
-++		    !test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
-++			ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
-++					     WLAN_BACK_RECIPIENT,
-++					     WLAN_REASON_QSTA_REQUIRE_SETUP);
-++
-+ 		tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]);
-+ 		if (!tid_agg_rx)
-+ 			return RX_DROP_MONITOR;
-diff --git a/package/kernel/mac80211/patches/344-mac80211-send-delBA-on-unexpected-BlockAck-data-fram.patch b/package/kernel/mac80211/patches/344-mac80211-send-delBA-on-unexpected-BlockAck-data-fram.patch
-deleted file mode 100644
-index 3bbca22..0000000
---- a/package/kernel/mac80211/patches/344-mac80211-send-delBA-on-unexpected-BlockAck-data-fram.patch
-+++ /dev/null
-@@ -1,64 +0,0 @@
--From: Johannes Berg <johannes.berg@intel.com>
--Date: Mon, 29 Aug 2016 23:25:18 +0300
--Subject: [PATCH] mac80211: send delBA on unexpected BlockAck data frames
--
--When we receive data frames with ACK policy BlockAck, send
--delBA as requested by the 802.11 spec. Since this would be
--happening for every frame inside an A-MPDU if it's really
--received outside a session, limit it to a single attempt.
--
--Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-----
--
----- a/net/mac80211/agg-rx.c
--+++ b/net/mac80211/agg-rx.c
--@@ -388,8 +388,10 @@ void __ieee80211_start_rx_ba_session(str
-- 	}
-- 
-- end:
---	if (status == WLAN_STATUS_SUCCESS)
--+	if (status == WLAN_STATUS_SUCCESS) {
-- 		__set_bit(tid, sta->ampdu_mlme.agg_session_valid);
--+		__clear_bit(tid, sta->ampdu_mlme.unexpected_agg);
--+	}
-- 	mutex_unlock(&sta->ampdu_mlme.mtx);
-- 
-- end_no_lock:
----- a/net/mac80211/rx.c
--+++ b/net/mac80211/rx.c
--@@ -1072,8 +1072,15 @@ static void ieee80211_rx_reorder_ampdu(s
-- 	tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
-- 
-- 	tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
---	if (!tid_agg_rx)
--+	if (!tid_agg_rx) {
--+		if (ack_policy == IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK &&
--+		    !test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
--+		    !test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
--+			ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
--+					     WLAN_BACK_RECIPIENT,
--+					     WLAN_REASON_QSTA_REQUIRE_SETUP);
-- 		goto dont_reorder;
--+	}
-- 
-- 	/* qos null data frames are excluded */
-- 	if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
----- a/net/mac80211/sta_info.h
--+++ b/net/mac80211/sta_info.h
--@@ -230,6 +230,8 @@ struct tid_ampdu_rx {
--  * @tid_rx_stop_requested:  bitmap indicating which BA sessions per TID the
--  *	driver requested to close until the work for it runs
--  * @agg_session_valid: bitmap indicating which TID has a rx BA session open on
--+ * @unexpected_agg: bitmap indicating which TID already sent a delBA due to
--+ *	unexpected aggregation related frames outside a session
--  * @work: work struct for starting/stopping aggregation
--  * @tid_tx: aggregation info for Tx per TID
--  * @tid_start_tx: sessions where start was requested
--@@ -244,6 +246,7 @@ struct sta_ampdu_mlme {
-- 	unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-- 	unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-- 	unsigned long agg_session_valid[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
--+	unsigned long unexpected_agg[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
-- 	/* tx */
-- 	struct work_struct work;
-- 	struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS];
-diff --git a/package/kernel/mac80211/patches/345-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch b/package/kernel/mac80211/patches/345-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch
-new file mode 100644
-index 0000000..aba1ff4
---- /dev/null
-+++ b/package/kernel/mac80211/patches/345-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch
-@@ -0,0 +1,478 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Sun, 4 Sep 2016 17:46:24 +0200
-+Subject: [PATCH] mac80211: fix sequence number assignment for PS response
-+ frames
-+
-+When using intermediate queues, sequence number allocation is deferred
-+until dequeue. This doesn't work for PS response frames, which bypass
-+those queues.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/net/mac80211/tx.c
-++++ b/net/mac80211/tx.c
-+@@ -38,6 +38,12 @@
-+ #include "wme.h"
-+ #include "rate.h"
-+ 
-++static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx);
-++static bool ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
-++				       struct sta_info *sta, u8 pn_offs,
-++				       struct ieee80211_key_conf *key_conf,
-++				       struct sk_buff *skb);
-++
-+ /* misc utils */
-+ 
-+ static inline void ieee80211_tx_stats(struct net_device *dev, u32 len)
-+@@ -849,8 +855,7 @@ ieee80211_tx_h_sequence(struct ieee80211
-+ 	tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
-+ 	tx->sta->tx_stats.msdu[tid]++;
-+ 
-+-	if (!tx->sta->sta.txq[0])
-+-		hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
-++	hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
-+ 
-+ 	return TX_CONTINUE;
-+ }
-+@@ -1398,6 +1403,7 @@ void ieee80211_txq_init(struct ieee80211
-+ 	fq_tin_init(&txqi->tin);
-+ 	fq_flow_init(&txqi->def_flow);
-+ 	codel_vars_init(&txqi->def_cvars);
-++	__skb_queue_head_init(&txqi->frags);
-+ 
-+ 	txqi->txq.vif = &sdata->vif;
-+ 
-+@@ -1420,6 +1426,7 @@ void ieee80211_txq_purge(struct ieee8021
-+ 	struct fq_tin *tin = &txqi->tin;
-+ 
-+ 	fq_tin_reset(fq, tin, fq_skb_free_func);
-++	ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
-+ }
-+ 
-+ int ieee80211_txq_setup_flows(struct ieee80211_local *local)
-+@@ -1476,12 +1483,19 @@ struct sk_buff *ieee80211_tx_dequeue(str
-+ 	struct sk_buff *skb = NULL;
-+ 	struct fq *fq = &local->fq;
-+ 	struct fq_tin *tin = &txqi->tin;
-++	struct ieee80211_tx_info *info;
-+ 
-+ 	spin_lock_bh(&fq->lock);
-+ 
-+ 	if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
-+ 		goto out;
-+ 
-++	/* Make sure fragments stay together. */
-++	skb = __skb_dequeue(&txqi->frags);
-++	if (skb)
-++		goto out;
-++
-++begin:
-+ 	skb = fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
-+ 	if (!skb)
-+ 		goto out;
-+@@ -1489,16 +1503,38 @@ struct sk_buff *ieee80211_tx_dequeue(str
-+ 	ieee80211_set_skb_vif(skb, txqi);
-+ 
-+ 	hdr = (struct ieee80211_hdr *)skb->data;
-+-	if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
-++	info = IEEE80211_SKB_CB(skb);
-++	if (txq->sta && info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
-+ 		struct sta_info *sta = container_of(txq->sta, struct sta_info,
-+ 						    sta);
-+-		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-++		u8 pn_offs = 0;
-+ 
-+-		hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid);
-+-		if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
-+-			info->flags |= IEEE80211_TX_CTL_AMPDU;
-+-		else
-+-			info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-++		if (info->control.hw_key)
-++			pn_offs = ieee80211_padded_hdrlen(hw, hdr->frame_control);
-++
-++		ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
-++					   info->control.hw_key, skb);
-++	} else {
-++		struct ieee80211_tx_data tx = { };
-++
-++		__skb_queue_head_init(&tx.skbs);
-++		tx.local = local;
-++		tx.skb = skb;
-++		tx.hdrlen = ieee80211_padded_hdrlen(hw, hdr->frame_control);
-++		if (txq->sta) {
-++			tx.sta = container_of(txq->sta, struct sta_info, sta);
-++			tx.sdata = tx.sta->sdata;
-++		} else {
-++			tx.sdata = vif_to_sdata(info->control.vif);
-++		}
-++
-++		if (invoke_tx_handlers_late(&tx))
-++			goto begin;
-++
-++		skb = __skb_dequeue(&tx.skbs);
-++
-++		if (!skb_queue_empty(&tx.skbs))
-++			skb_queue_splice_tail(&tx.skbs, &txqi->frags);
-+ 	}
-+ 
-+ out:
-+@@ -1512,6 +1548,47 @@ out:
-+ }
-+ EXPORT_SYMBOL(ieee80211_tx_dequeue);
-+ 
-++static bool ieee80211_queue_skb(struct ieee80211_local *local,
-++				struct ieee80211_sub_if_data *sdata,
-++				struct sta_info *sta,
-++				struct sk_buff *skb)
-++{
-++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-++	struct fq *fq = &local->fq;
-++	struct ieee80211_vif *vif;
-++	struct txq_info *txqi;
-++	struct ieee80211_sta *pubsta;
-++
-++	if (!local->ops->wake_tx_queue ||
-++	    sdata->vif.type == NL80211_IFTYPE_MONITOR)
-++		return false;
-++
-++	if (sta && sta->uploaded)
-++		pubsta = &sta->sta;
-++	else
-++		pubsta = NULL;
-++
-++	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-++		sdata = container_of(sdata->bss,
-++				     struct ieee80211_sub_if_data, u.ap);
-++
-++	vif = &sdata->vif;
-++	txqi = ieee80211_get_txq(local, vif, pubsta, skb);
-++
-++	if (!txqi)
-++		return false;
-++
-++	info->control.vif = vif;
-++
-++	spin_lock_bh(&fq->lock);
-++	ieee80211_txq_enqueue(local, txqi, skb);
-++	spin_unlock_bh(&fq->lock);
-++
-++	drv_wake_tx_queue(local, txqi);
-++
-++	return true;
-++}
-++
-+ static bool ieee80211_tx_frags(struct ieee80211_local *local,
-+ 			       struct ieee80211_vif *vif,
-+ 			       struct ieee80211_sta *sta,
-+@@ -1519,9 +1596,7 @@ static bool ieee80211_tx_frags(struct ie
-+ 			       bool txpending)
-+ {
-+ 	struct ieee80211_tx_control control = {};
-+-	struct fq *fq = &local->fq;
-+ 	struct sk_buff *skb, *tmp;
-+-	struct txq_info *txqi;
-+ 	unsigned long flags;
-+ 
-+ 	skb_queue_walk_safe(skbs, skb, tmp) {
-+@@ -1536,21 +1611,6 @@ static bool ieee80211_tx_frags(struct ie
-+ 		}
-+ #endif
-+ 
-+-		txqi = ieee80211_get_txq(local, vif, sta, skb);
-+-		if (txqi) {
-+-			info->control.vif = vif;
-+-
-+-			__skb_unlink(skb, skbs);
-+-
-+-			spin_lock_bh(&fq->lock);
-+-			ieee80211_txq_enqueue(local, txqi, skb);
-+-			spin_unlock_bh(&fq->lock);
-+-
-+-			drv_wake_tx_queue(local, txqi);
-+-
-+-			continue;
-+-		}
-+-
-+ 		spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-+ 		if (local->queue_stop_reasons[q] ||
-+ 		    (!txpending && !skb_queue_empty(&local->pending[q]))) {
-+@@ -1671,10 +1731,13 @@ static bool __ieee80211_tx(struct ieee80
-+ /*
-+  * Invoke TX handlers, return 0 on success and non-zero if the
-+  * frame was dropped or queued.
-++ *
-++ * The handlers are split into an early and late part. The latter is everything
-++ * that can be sensitive to reordering, and will be deferred to after packets
-++ * are dequeued from the intermediate queues (when they are enabled).
-+  */
-+-static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
-++static int invoke_tx_handlers_early(struct ieee80211_tx_data *tx)
-+ {
-+-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-+ 	ieee80211_tx_result res = TX_DROP;
-+ 
-+ #define CALL_TXH(txh) \
-+@@ -1688,16 +1751,42 @@ static int invoke_tx_handlers(struct iee
-+ 	CALL_TXH(ieee80211_tx_h_check_assoc);
-+ 	CALL_TXH(ieee80211_tx_h_ps_buf);
-+ 	CALL_TXH(ieee80211_tx_h_check_control_port_protocol);
-+-	CALL_TXH(ieee80211_tx_h_select_key);
-++
-+ 	if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
-+ 		CALL_TXH(ieee80211_tx_h_rate_ctrl);
-+ 
-++ txh_done:
-++	if (unlikely(res == TX_DROP)) {
-++		I802_DEBUG_INC(tx->local->tx_handlers_drop);
-++		if (tx->skb)
-++			ieee80211_free_txskb(&tx->local->hw, tx->skb);
-++		else
-++			ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs);
-++		return -1;
-++	} else if (unlikely(res == TX_QUEUED)) {
-++		I802_DEBUG_INC(tx->local->tx_handlers_queued);
-++		return -1;
-++	}
-++
-++	return 0;
-++}
-++
-++/*
-++ * Late handlers can be called while the sta lock is held. Handlers that can
-++ * cause packets to be generated will cause deadlock!
-++ */
-++static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx)
-++{
-++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-++	ieee80211_tx_result res = TX_CONTINUE;
-++
-+ 	if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) {
-+ 		__skb_queue_tail(&tx->skbs, tx->skb);
-+ 		tx->skb = NULL;
-+ 		goto txh_done;
-+ 	}
-+ 
-++	CALL_TXH(ieee80211_tx_h_select_key);
-+ 	CALL_TXH(ieee80211_tx_h_michael_mic_add);
-+ 	CALL_TXH(ieee80211_tx_h_sequence);
-+ 	CALL_TXH(ieee80211_tx_h_fragment);
-+@@ -1724,6 +1813,15 @@ static int invoke_tx_handlers(struct iee
-+ 	return 0;
-+ }
-+ 
-++static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
-++{
-++	int r = invoke_tx_handlers_early(tx);
-++	if (r)
-++		return r;
-++
-++	return invoke_tx_handlers_late(tx);
-++}
-++
-+ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
-+ 			      struct ieee80211_vif *vif, struct sk_buff *skb,
-+ 			      int band, struct ieee80211_sta **sta)
-+@@ -1798,7 +1896,13 @@ static bool ieee80211_tx(struct ieee8021
-+ 		info->hw_queue =
-+ 			sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
-+ 
-+-	if (!invoke_tx_handlers(&tx))
-++	if (invoke_tx_handlers_early(&tx))
-++		return false;
-++
-++	if (ieee80211_queue_skb(local, sdata, tx.sta, tx.skb))
-++		return true;
-++
-++	if (!invoke_tx_handlers_late(&tx))
-+ 		result = __ieee80211_tx(local, &tx.skbs, led_len,
-+ 					tx.sta, txpending);
-+ 
-+@@ -3181,7 +3285,7 @@ out:
-+ }
-+ 
-+ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-+-				struct net_device *dev, struct sta_info *sta,
-++				struct sta_info *sta,
-+ 				struct ieee80211_fast_tx *fast_tx,
-+ 				struct sk_buff *skb)
-+ {
-+@@ -3192,9 +3296,9 @@ static bool ieee80211_xmit_fast(struct i
-+ 	struct ethhdr eth;
-+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+ 	struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
-+-	struct ieee80211_tx_data tx;
-+-	ieee80211_tx_result r;
-+ 	struct tid_ampdu_tx *tid_tx = NULL;
-++	ieee80211_tx_result r;
-++	struct ieee80211_tx_data tx;
-+ 	u8 tid = IEEE80211_NUM_TIDS;
-+ 
-+ 	/* control port protocol needs a lot of special handling */
-+@@ -3232,8 +3336,6 @@ static bool ieee80211_xmit_fast(struct i
-+ 			return true;
-+ 	}
-+ 
-+-	ieee80211_tx_stats(dev, skb->len + extra_head);
-+-
-+ 	if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
-+ 	    ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
-+ 		return true;
-+@@ -3262,24 +3364,7 @@ static bool ieee80211_xmit_fast(struct i
-+ 	info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
-+ 		      IEEE80211_TX_CTL_DONTFRAG |
-+ 		      (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
-+-
-+-	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-+-		*ieee80211_get_qos_ctl(hdr) = tid;
-+-		if (!sta->sta.txq[0])
-+-			hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
-+-	} else {
-+-		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
-+-		hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
-+-		sdata->sequence_number += 0x10;
-+-	}
-+-
-+-	if (skb_shinfo(skb)->gso_size)
-+-		sta->tx_stats.msdu[tid] +=
-+-			DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
-+-	else
-+-		sta->tx_stats.msdu[tid]++;
-+-
-+-	info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
-++	info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT;
-+ 
-+ 	__skb_queue_head_init(&tx.skbs);
-+ 
-+@@ -3305,22 +3390,71 @@ static bool ieee80211_xmit_fast(struct i
-+ 		}
-+ 	}
-+ 
-++	if (ieee80211_queue_skb(local, sdata, sta, skb))
-++		return true;
-++
-++	ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
-++				   &fast_tx->key->conf, skb);
-++
-++	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-++		sdata = container_of(sdata->bss,
-++				     struct ieee80211_sub_if_data, u.ap);
-++
-++	__skb_queue_tail(&tx.skbs, skb);
-++	ieee80211_tx_frags(local, &sdata->vif, &sta->sta, &tx.skbs, false);
-++
-++	return true;
-++}
-++
-++/*
-++ * Can be called while the sta lock is held. Anything that can cause packets to
-++ * be generated will cause deadlock!
-++ */
-++static bool ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
-++				       struct sta_info *sta, u8 pn_offs,
-++				       struct ieee80211_key_conf *key_conf,
-++				       struct sk_buff *skb)
-++{
-++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-++	struct ieee80211_hdr *hdr = (void *)skb->data;
-++	u8 tid = IEEE80211_NUM_TIDS;
-++
-++	ieee80211_tx_stats(skb->dev, skb->len);
-++
-++	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-++		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-++		*ieee80211_get_qos_ctl(hdr) = tid;
-++		hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
-++	} else {
-++		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
-++		hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
-++		sdata->sequence_number += 0x10;
-++	}
-++
-++	if (skb_shinfo(skb)->gso_size)
-++		sta->tx_stats.msdu[tid] +=
-++			DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
-++	else
-++		sta->tx_stats.msdu[tid]++;
-++
-++	info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
-++
-+ 	/* statistics normally done by ieee80211_tx_h_stats (but that
-+ 	 * has to consider fragmentation, so is more complex)
-+ 	 */
-+ 	sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
-+ 	sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
-+ 
-+-	if (fast_tx->pn_offs) {
-++	if (pn_offs && (key_conf->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
-+ 		u64 pn;
-+-		u8 *crypto_hdr = skb->data + fast_tx->pn_offs;
-++		u8 *crypto_hdr = skb->data + pn_offs;
-+ 
-+-		switch (fast_tx->key->conf.cipher) {
-++		switch (key_conf->cipher) {
-+ 		case WLAN_CIPHER_SUITE_CCMP:
-+ 		case WLAN_CIPHER_SUITE_CCMP_256:
-+ 		case WLAN_CIPHER_SUITE_GCMP:
-+ 		case WLAN_CIPHER_SUITE_GCMP_256:
-+-			pn = atomic64_inc_return(&fast_tx->key->conf.tx_pn);
-++			pn = atomic64_inc_return(&key_conf->tx_pn);
-+ 			crypto_hdr[0] = pn;
-+ 			crypto_hdr[1] = pn >> 8;
-+ 			crypto_hdr[4] = pn >> 16;
-+@@ -3331,12 +3465,6 @@ static bool ieee80211_xmit_fast(struct i
-+ 		}
-+ 	}
-+ 
-+-	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
-+-		sdata = container_of(sdata->bss,
-+-				     struct ieee80211_sub_if_data, u.ap);
-+-
-+-	__skb_queue_tail(&tx.skbs, skb);
-+-	ieee80211_tx_frags(local, &sdata->vif, &sta->sta, &tx.skbs, false);
-+ 	return true;
-+ }
-+ 
-+@@ -3364,7 +3492,7 @@ void __ieee80211_subif_start_xmit(struct
-+ 		fast_tx = rcu_dereference(sta->fast_tx);
-+ 
-+ 		if (fast_tx &&
-+-		    ieee80211_xmit_fast(sdata, dev, sta, fast_tx, skb))
-++		    ieee80211_xmit_fast(sdata, sta, fast_tx, skb))
-+ 			goto out;
-+ 	}
-+ 
-+--- a/include/net/mac80211.h
-++++ b/include/net/mac80211.h
-+@@ -715,6 +715,7 @@ enum mac80211_tx_info_flags {
-+  *	frame (PS-Poll or uAPSD).
-+  * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
-+  * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
-++ * @IEEE80211_TX_CTRL_FAST_XMIT: This frame is going through the fast_xmit path
-+  *
-+  * These flags are used in tx_info->control.flags.
-+  */
-+@@ -723,6 +724,7 @@ enum mac80211_tx_control_flags {
-+ 	IEEE80211_TX_CTRL_PS_RESPONSE		= BIT(1),
-+ 	IEEE80211_TX_CTRL_RATE_INJECT		= BIT(2),
-+ 	IEEE80211_TX_CTRL_AMSDU			= BIT(3),
-++	IEEE80211_TX_CTRL_FAST_XMIT		= BIT(4),
-+ };
-+ 
-+ /*
-+--- a/net/mac80211/ieee80211_i.h
-++++ b/net/mac80211/ieee80211_i.h
-+@@ -814,11 +814,13 @@ enum txq_info_flags {
-+  * @def_flow: used as a fallback flow when a packet destined to @tin hashes to
-+  *	a fq_flow which is already owned by a different tin
-+  * @def_cvars: codel vars for @def_flow
-++ * @frags: used to keep fragments created after dequeue
-+  */
-+ struct txq_info {
-+ 	struct fq_tin tin;
-+ 	struct fq_flow def_flow;
-+ 	struct codel_vars def_cvars;
-++	struct sk_buff_head frags;
-+ 	unsigned long flags;
-+ 
-+ 	/* keep last! */
-diff --git a/package/kernel/mac80211/patches/345-mac80211-send-delBA-on-unexpected-BlockAck-Request.patch b/package/kernel/mac80211/patches/345-mac80211-send-delBA-on-unexpected-BlockAck-Request.patch
-deleted file mode 100644
-index c3d3118..0000000
---- a/package/kernel/mac80211/patches/345-mac80211-send-delBA-on-unexpected-BlockAck-Request.patch
-+++ /dev/null
-@@ -1,26 +0,0 @@
--From: Johannes Berg <johannes.berg@intel.com>
--Date: Mon, 29 Aug 2016 23:25:19 +0300
--Subject: [PATCH] mac80211: send delBA on unexpected BlockAck Request
--
--If we don't have a BA session, send delBA, as requested by the
--IEEE 802.11 spec. Apply the same limit of sending such a delBA
--only once as in the previous patch.
--
--Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-----
--
----- a/net/mac80211/rx.c
--+++ b/net/mac80211/rx.c
--@@ -2537,6 +2537,12 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_
-- 
-- 		tid = le16_to_cpu(bar_data.control) >> 12;
-- 
--+		if (!test_bit(tid, rx->sta->ampdu_mlme.agg_session_valid) &&
--+		    !test_and_set_bit(tid, rx->sta->ampdu_mlme.unexpected_agg))
--+			ieee80211_send_delba(rx->sdata, rx->sta->sta.addr, tid,
--+					     WLAN_BACK_RECIPIENT,
--+					     WLAN_REASON_QSTA_REQUIRE_SETUP);
--+
-- 		tid_agg_rx = rcu_dereference(rx->sta->ampdu_mlme.tid_rx[tid]);
-- 		if (!tid_agg_rx)
-- 			return RX_DROP_MONITOR;
-diff --git a/package/kernel/mac80211/patches/346-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch b/package/kernel/mac80211/patches/346-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch
-deleted file mode 100644
-index aba1ff4..0000000
---- a/package/kernel/mac80211/patches/346-mac80211-Move-reorder-sensitive-TX-handlers-to-after.patch
-+++ /dev/null
-@@ -1,478 +0,0 @@
--From: Felix Fietkau <nbd@nbd.name>
--Date: Sun, 4 Sep 2016 17:46:24 +0200
--Subject: [PATCH] mac80211: fix sequence number assignment for PS response
-- frames
--
--When using intermediate queues, sequence number allocation is deferred
--until dequeue. This doesn't work for PS response frames, which bypass
--those queues.
--
--Signed-off-by: Felix Fietkau <nbd@nbd.name>
-----
--
----- a/net/mac80211/tx.c
--+++ b/net/mac80211/tx.c
--@@ -38,6 +38,12 @@
-- #include "wme.h"
-- #include "rate.h"
-- 
--+static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx);
--+static bool ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
--+				       struct sta_info *sta, u8 pn_offs,
--+				       struct ieee80211_key_conf *key_conf,
--+				       struct sk_buff *skb);
--+
-- /* misc utils */
-- 
-- static inline void ieee80211_tx_stats(struct net_device *dev, u32 len)
--@@ -849,8 +855,7 @@ ieee80211_tx_h_sequence(struct ieee80211
-- 	tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
-- 	tx->sta->tx_stats.msdu[tid]++;
-- 
---	if (!tx->sta->sta.txq[0])
---		hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
--+	hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
-- 
-- 	return TX_CONTINUE;
-- }
--@@ -1398,6 +1403,7 @@ void ieee80211_txq_init(struct ieee80211
-- 	fq_tin_init(&txqi->tin);
-- 	fq_flow_init(&txqi->def_flow);
-- 	codel_vars_init(&txqi->def_cvars);
--+	__skb_queue_head_init(&txqi->frags);
-- 
-- 	txqi->txq.vif = &sdata->vif;
-- 
--@@ -1420,6 +1426,7 @@ void ieee80211_txq_purge(struct ieee8021
-- 	struct fq_tin *tin = &txqi->tin;
-- 
-- 	fq_tin_reset(fq, tin, fq_skb_free_func);
--+	ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
-- }
-- 
-- int ieee80211_txq_setup_flows(struct ieee80211_local *local)
--@@ -1476,12 +1483,19 @@ struct sk_buff *ieee80211_tx_dequeue(str
-- 	struct sk_buff *skb = NULL;
-- 	struct fq *fq = &local->fq;
-- 	struct fq_tin *tin = &txqi->tin;
--+	struct ieee80211_tx_info *info;
-- 
-- 	spin_lock_bh(&fq->lock);
-- 
-- 	if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
-- 		goto out;
-- 
--+	/* Make sure fragments stay together. */
--+	skb = __skb_dequeue(&txqi->frags);
--+	if (skb)
--+		goto out;
--+
--+begin:
-- 	skb = fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
-- 	if (!skb)
-- 		goto out;
--@@ -1489,16 +1503,38 @@ struct sk_buff *ieee80211_tx_dequeue(str
-- 	ieee80211_set_skb_vif(skb, txqi);
-- 
-- 	hdr = (struct ieee80211_hdr *)skb->data;
---	if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
--+	info = IEEE80211_SKB_CB(skb);
--+	if (txq->sta && info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
-- 		struct sta_info *sta = container_of(txq->sta, struct sta_info,
-- 						    sta);
---		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
--+		u8 pn_offs = 0;
-- 
---		hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid);
---		if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
---			info->flags |= IEEE80211_TX_CTL_AMPDU;
---		else
---			info->flags &= ~IEEE80211_TX_CTL_AMPDU;
--+		if (info->control.hw_key)
--+			pn_offs = ieee80211_padded_hdrlen(hw, hdr->frame_control);
--+
--+		ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
--+					   info->control.hw_key, skb);
--+	} else {
--+		struct ieee80211_tx_data tx = { };
--+
--+		__skb_queue_head_init(&tx.skbs);
--+		tx.local = local;
--+		tx.skb = skb;
--+		tx.hdrlen = ieee80211_padded_hdrlen(hw, hdr->frame_control);
--+		if (txq->sta) {
--+			tx.sta = container_of(txq->sta, struct sta_info, sta);
--+			tx.sdata = tx.sta->sdata;
--+		} else {
--+			tx.sdata = vif_to_sdata(info->control.vif);
--+		}
--+
--+		if (invoke_tx_handlers_late(&tx))
--+			goto begin;
--+
--+		skb = __skb_dequeue(&tx.skbs);
--+
--+		if (!skb_queue_empty(&tx.skbs))
--+			skb_queue_splice_tail(&tx.skbs, &txqi->frags);
-- 	}
-- 
-- out:
--@@ -1512,6 +1548,47 @@ out:
-- }
-- EXPORT_SYMBOL(ieee80211_tx_dequeue);
-- 
--+static bool ieee80211_queue_skb(struct ieee80211_local *local,
--+				struct ieee80211_sub_if_data *sdata,
--+				struct sta_info *sta,
--+				struct sk_buff *skb)
--+{
--+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
--+	struct fq *fq = &local->fq;
--+	struct ieee80211_vif *vif;
--+	struct txq_info *txqi;
--+	struct ieee80211_sta *pubsta;
--+
--+	if (!local->ops->wake_tx_queue ||
--+	    sdata->vif.type == NL80211_IFTYPE_MONITOR)
--+		return false;
--+
--+	if (sta && sta->uploaded)
--+		pubsta = &sta->sta;
--+	else
--+		pubsta = NULL;
--+
--+	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
--+		sdata = container_of(sdata->bss,
--+				     struct ieee80211_sub_if_data, u.ap);
--+
--+	vif = &sdata->vif;
--+	txqi = ieee80211_get_txq(local, vif, pubsta, skb);
--+
--+	if (!txqi)
--+		return false;
--+
--+	info->control.vif = vif;
--+
--+	spin_lock_bh(&fq->lock);
--+	ieee80211_txq_enqueue(local, txqi, skb);
--+	spin_unlock_bh(&fq->lock);
--+
--+	drv_wake_tx_queue(local, txqi);
--+
--+	return true;
--+}
--+
-- static bool ieee80211_tx_frags(struct ieee80211_local *local,
-- 			       struct ieee80211_vif *vif,
-- 			       struct ieee80211_sta *sta,
--@@ -1519,9 +1596,7 @@ static bool ieee80211_tx_frags(struct ie
-- 			       bool txpending)
-- {
-- 	struct ieee80211_tx_control control = {};
---	struct fq *fq = &local->fq;
-- 	struct sk_buff *skb, *tmp;
---	struct txq_info *txqi;
-- 	unsigned long flags;
-- 
-- 	skb_queue_walk_safe(skbs, skb, tmp) {
--@@ -1536,21 +1611,6 @@ static bool ieee80211_tx_frags(struct ie
-- 		}
-- #endif
-- 
---		txqi = ieee80211_get_txq(local, vif, sta, skb);
---		if (txqi) {
---			info->control.vif = vif;
---
---			__skb_unlink(skb, skbs);
---
---			spin_lock_bh(&fq->lock);
---			ieee80211_txq_enqueue(local, txqi, skb);
---			spin_unlock_bh(&fq->lock);
---
---			drv_wake_tx_queue(local, txqi);
---
---			continue;
---		}
---
-- 		spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-- 		if (local->queue_stop_reasons[q] ||
-- 		    (!txpending && !skb_queue_empty(&local->pending[q]))) {
--@@ -1671,10 +1731,13 @@ static bool __ieee80211_tx(struct ieee80
-- /*
--  * Invoke TX handlers, return 0 on success and non-zero if the
--  * frame was dropped or queued.
--+ *
--+ * The handlers are split into an early and late part. The latter is everything
--+ * that can be sensitive to reordering, and will be deferred to after packets
--+ * are dequeued from the intermediate queues (when they are enabled).
--  */
---static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
--+static int invoke_tx_handlers_early(struct ieee80211_tx_data *tx)
-- {
---	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-- 	ieee80211_tx_result res = TX_DROP;
-- 
-- #define CALL_TXH(txh) \
--@@ -1688,16 +1751,42 @@ static int invoke_tx_handlers(struct iee
-- 	CALL_TXH(ieee80211_tx_h_check_assoc);
-- 	CALL_TXH(ieee80211_tx_h_ps_buf);
-- 	CALL_TXH(ieee80211_tx_h_check_control_port_protocol);
---	CALL_TXH(ieee80211_tx_h_select_key);
--+
-- 	if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
-- 		CALL_TXH(ieee80211_tx_h_rate_ctrl);
-- 
--+ txh_done:
--+	if (unlikely(res == TX_DROP)) {
--+		I802_DEBUG_INC(tx->local->tx_handlers_drop);
--+		if (tx->skb)
--+			ieee80211_free_txskb(&tx->local->hw, tx->skb);
--+		else
--+			ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs);
--+		return -1;
--+	} else if (unlikely(res == TX_QUEUED)) {
--+		I802_DEBUG_INC(tx->local->tx_handlers_queued);
--+		return -1;
--+	}
--+
--+	return 0;
--+}
--+
--+/*
--+ * Late handlers can be called while the sta lock is held. Handlers that can
--+ * cause packets to be generated will cause deadlock!
--+ */
--+static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx)
--+{
--+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
--+	ieee80211_tx_result res = TX_CONTINUE;
--+
-- 	if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) {
-- 		__skb_queue_tail(&tx->skbs, tx->skb);
-- 		tx->skb = NULL;
-- 		goto txh_done;
-- 	}
-- 
--+	CALL_TXH(ieee80211_tx_h_select_key);
-- 	CALL_TXH(ieee80211_tx_h_michael_mic_add);
-- 	CALL_TXH(ieee80211_tx_h_sequence);
-- 	CALL_TXH(ieee80211_tx_h_fragment);
--@@ -1724,6 +1813,15 @@ static int invoke_tx_handlers(struct iee
-- 	return 0;
-- }
-- 
--+static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
--+{
--+	int r = invoke_tx_handlers_early(tx);
--+	if (r)
--+		return r;
--+
--+	return invoke_tx_handlers_late(tx);
--+}
--+
-- bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
-- 			      struct ieee80211_vif *vif, struct sk_buff *skb,
-- 			      int band, struct ieee80211_sta **sta)
--@@ -1798,7 +1896,13 @@ static bool ieee80211_tx(struct ieee8021
-- 		info->hw_queue =
-- 			sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
-- 
---	if (!invoke_tx_handlers(&tx))
--+	if (invoke_tx_handlers_early(&tx))
--+		return false;
--+
--+	if (ieee80211_queue_skb(local, sdata, tx.sta, tx.skb))
--+		return true;
--+
--+	if (!invoke_tx_handlers_late(&tx))
-- 		result = __ieee80211_tx(local, &tx.skbs, led_len,
-- 					tx.sta, txpending);
-- 
--@@ -3181,7 +3285,7 @@ out:
-- }
-- 
-- static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
---				struct net_device *dev, struct sta_info *sta,
--+				struct sta_info *sta,
-- 				struct ieee80211_fast_tx *fast_tx,
-- 				struct sk_buff *skb)
-- {
--@@ -3192,9 +3296,9 @@ static bool ieee80211_xmit_fast(struct i
-- 	struct ethhdr eth;
-- 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-- 	struct ieee80211_hdr *hdr = (void *)fast_tx->hdr;
---	struct ieee80211_tx_data tx;
---	ieee80211_tx_result r;
-- 	struct tid_ampdu_tx *tid_tx = NULL;
--+	ieee80211_tx_result r;
--+	struct ieee80211_tx_data tx;
-- 	u8 tid = IEEE80211_NUM_TIDS;
-- 
-- 	/* control port protocol needs a lot of special handling */
--@@ -3232,8 +3336,6 @@ static bool ieee80211_xmit_fast(struct i
-- 			return true;
-- 	}
-- 
---	ieee80211_tx_stats(dev, skb->len + extra_head);
---
-- 	if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
-- 	    ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
-- 		return true;
--@@ -3262,24 +3364,7 @@ static bool ieee80211_xmit_fast(struct i
-- 	info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
-- 		      IEEE80211_TX_CTL_DONTFRAG |
-- 		      (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
---
---	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
---		*ieee80211_get_qos_ctl(hdr) = tid;
---		if (!sta->sta.txq[0])
---			hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
---	} else {
---		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
---		hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
---		sdata->sequence_number += 0x10;
---	}
---
---	if (skb_shinfo(skb)->gso_size)
---		sta->tx_stats.msdu[tid] +=
---			DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
---	else
---		sta->tx_stats.msdu[tid]++;
---
---	info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
--+	info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT;
-- 
-- 	__skb_queue_head_init(&tx.skbs);
-- 
--@@ -3305,22 +3390,71 @@ static bool ieee80211_xmit_fast(struct i
-- 		}
-- 	}
-- 
--+	if (ieee80211_queue_skb(local, sdata, sta, skb))
--+		return true;
--+
--+	ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
--+				   &fast_tx->key->conf, skb);
--+
--+	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
--+		sdata = container_of(sdata->bss,
--+				     struct ieee80211_sub_if_data, u.ap);
--+
--+	__skb_queue_tail(&tx.skbs, skb);
--+	ieee80211_tx_frags(local, &sdata->vif, &sta->sta, &tx.skbs, false);
--+
--+	return true;
--+}
--+
--+/*
--+ * Can be called while the sta lock is held. Anything that can cause packets to
--+ * be generated will cause deadlock!
--+ */
--+static bool ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
--+				       struct sta_info *sta, u8 pn_offs,
--+				       struct ieee80211_key_conf *key_conf,
--+				       struct sk_buff *skb)
--+{
--+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
--+	struct ieee80211_hdr *hdr = (void *)skb->data;
--+	u8 tid = IEEE80211_NUM_TIDS;
--+
--+	ieee80211_tx_stats(skb->dev, skb->len);
--+
--+	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
--+		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
--+		*ieee80211_get_qos_ctl(hdr) = tid;
--+		hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
--+	} else {
--+		info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
--+		hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
--+		sdata->sequence_number += 0x10;
--+	}
--+
--+	if (skb_shinfo(skb)->gso_size)
--+		sta->tx_stats.msdu[tid] +=
--+			DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
--+	else
--+		sta->tx_stats.msdu[tid]++;
--+
--+	info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
--+
-- 	/* statistics normally done by ieee80211_tx_h_stats (but that
-- 	 * has to consider fragmentation, so is more complex)
-- 	 */
-- 	sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
-- 	sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
-- 
---	if (fast_tx->pn_offs) {
--+	if (pn_offs && (key_conf->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
-- 		u64 pn;
---		u8 *crypto_hdr = skb->data + fast_tx->pn_offs;
--+		u8 *crypto_hdr = skb->data + pn_offs;
-- 
---		switch (fast_tx->key->conf.cipher) {
--+		switch (key_conf->cipher) {
-- 		case WLAN_CIPHER_SUITE_CCMP:
-- 		case WLAN_CIPHER_SUITE_CCMP_256:
-- 		case WLAN_CIPHER_SUITE_GCMP:
-- 		case WLAN_CIPHER_SUITE_GCMP_256:
---			pn = atomic64_inc_return(&fast_tx->key->conf.tx_pn);
--+			pn = atomic64_inc_return(&key_conf->tx_pn);
-- 			crypto_hdr[0] = pn;
-- 			crypto_hdr[1] = pn >> 8;
-- 			crypto_hdr[4] = pn >> 16;
--@@ -3331,12 +3465,6 @@ static bool ieee80211_xmit_fast(struct i
-- 		}
-- 	}
-- 
---	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
---		sdata = container_of(sdata->bss,
---				     struct ieee80211_sub_if_data, u.ap);
---
---	__skb_queue_tail(&tx.skbs, skb);
---	ieee80211_tx_frags(local, &sdata->vif, &sta->sta, &tx.skbs, false);
-- 	return true;
-- }
-- 
--@@ -3364,7 +3492,7 @@ void __ieee80211_subif_start_xmit(struct
-- 		fast_tx = rcu_dereference(sta->fast_tx);
-- 
-- 		if (fast_tx &&
---		    ieee80211_xmit_fast(sdata, dev, sta, fast_tx, skb))
--+		    ieee80211_xmit_fast(sdata, sta, fast_tx, skb))
-- 			goto out;
-- 	}
-- 
----- a/include/net/mac80211.h
--+++ b/include/net/mac80211.h
--@@ -715,6 +715,7 @@ enum mac80211_tx_info_flags {
--  *	frame (PS-Poll or uAPSD).
--  * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
--  * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
--+ * @IEEE80211_TX_CTRL_FAST_XMIT: This frame is going through the fast_xmit path
--  *
--  * These flags are used in tx_info->control.flags.
--  */
--@@ -723,6 +724,7 @@ enum mac80211_tx_control_flags {
-- 	IEEE80211_TX_CTRL_PS_RESPONSE		= BIT(1),
-- 	IEEE80211_TX_CTRL_RATE_INJECT		= BIT(2),
-- 	IEEE80211_TX_CTRL_AMSDU			= BIT(3),
--+	IEEE80211_TX_CTRL_FAST_XMIT		= BIT(4),
-- };
-- 
-- /*
----- a/net/mac80211/ieee80211_i.h
--+++ b/net/mac80211/ieee80211_i.h
--@@ -814,11 +814,13 @@ enum txq_info_flags {
--  * @def_flow: used as a fallback flow when a packet destined to @tin hashes to
--  *	a fq_flow which is already owned by a different tin
--  * @def_cvars: codel vars for @def_flow
--+ * @frags: used to keep fragments created after dequeue
--  */
-- struct txq_info {
-- 	struct fq_tin tin;
-- 	struct fq_flow def_flow;
-- 	struct codel_vars def_cvars;
--+	struct sk_buff_head frags;
-- 	unsigned long flags;
-- 
-- 	/* keep last! */