From 3feca6da7b0571cf17afc9f0e40d0ede9059c271 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Sat, 4 Sep 2021 05:47:27 +0200
Subject: [PATCH 01/72] mac80211: update to latest HEAD

Signed-off-by: John Crispin <john@phrozen.org>
---
 package/kernel/mac80211/Makefile              |   15 +-
 package/kernel/mac80211/ath.mk                |    5 +-
 package/kernel/mac80211/broadcom.mk           |    4 +-
 .../mac80211/files/lib/netifd/mac80211.sh     |   36 -
 .../files/lib/netifd/wireless/mac80211.sh     |  214 ++-
 .../mac80211/files/lib/wifi/mac80211.sh       |  110 +-
 .../patches/ath/120-owl-loader-compat.patch   |   53 -
 .../patches/ath/402-ath_regd_optional.patch   |    2 +-
 .../080-ath10k_thermal_config.patch           |    2 +-
 ...21-ath10k_init_devices_synchronously.patch |    0
 ...h10k-increase-rx-buffer-size-to-2048.patch |    0
 .../930-ath10k_add_tpt_led_trigger.patch      |    0
 ...rolling-support-for-various-chipsets.patch |    2 +-
 ...75-ath10k-use-tpt-trigger-by-default.patch |    0
 ...980-ath10k-fix-max-antenna-gain-unit.patch |    0
 ...-power-reduction-for-US-regulatory-d.patch |    0
 ...h10k-Try-to-get-mac-address-from-dts.patch |   42 +
 .../201-ath5k-WAR-for-AR71xx-PCI-bug.patch    |    0
 .../411-ath5k_allow_adhoc_and_ap.patch        |    0
 .../420-ath5k_disable_fast_cc.patch           |    0
 .../430-add_ath5k_platform.patch              |    0
 .../{ath => ath5k}/432-ath5k_add_pciids.patch |    0
 .../440-ath5k_channel_bw_debugfs.patch        |    0
 ...w-reset-AHB-WMAC-interface-on-AR91xx.patch |    0
 ..._hw-issue-external-reset-for-QCA955x.patch |    0
 ...h9k-force-rx_clear-when-disabling-rx.patch |    0
 ...erpret-requested-txpower-in-EIRP-dom.patch |    0
 ...power-reduction-for-US-regulatory-do.patch |    0
 .../401-ath9k_blink_default.patch             |    0
 .../410-ath9k_allow_adhoc_and_ap.patch        |    0
 ...abled-MFP-capability-unconditionally.patch |    0
 .../500-ath9k_eeprom_debugfs.patch            |    0
 .../{ath => ath9k}/501-ath9k_ahb_init.patch   |    0
 .../510-ath9k_intr_mitigation_tweak.patch     |    0
 .../511-ath9k_reduce_rxbuf.patch              |    0
 .../512-ath9k_channelbw_debugfs.patch         |    0
 .../513-ath9k_add_pci_ids.patch               |    0
 .../{ath => ath9k}/530-ath9k_extra_leds.patch |    0
 .../531-ath9k_extra_platform_leds.patch       |    0
 .../540-ath9k_reduce_ani_interval.patch       |    0
 .../542-ath9k_debugfs_diag.patch              |    0
 .../543-ath9k_entropy_from_adc.patch          |    0
 ...544-ath9k-ar933x-usb-hang-workaround.patch |    0
 .../545-ath9k_ani_ws_detect.patch             |    0
 .../547-ath9k_led_defstate_fix.patch          |    0
 .../548-ath9k_enable_gpio_chip.patch          |    0
 .../549-ath9k_enable_gpio_buttons.patch       |    0
 .../550-ath9k-disable-bands-via-dt.patch      |    0
 .../551-ath9k_ubnt_uap_plus_hsr.patch         |    2 +-
 .../552-ath9k-ahb_of.patch}                   |    0
 .../553-ath9k_of_gpio_mask.patch              |    0
 .../602-rt2x00-introduce-rt2x00eeprom.patch   |    2 +-
 ...07-rt2x00-add_platform_data_mac_addr.patch |   11 +-
 .../100-remove-cryptoapi-dependencies.patch   |  699 ---------
 .../patches/subsys/130-disable-fils.patch     |   32 -
 ...aes-cmac-switch-to-shash-CMAC-driver.patch |  230 ---
 .../132-mac80211-remove-cmac-dependency.patch |   10 -
 ...320-mac80211_hwsim-add-6GHz-channels.patch |  123 ++
 ...211_hwsim-make-6-GHz-channels-usable.patch |   74 +
 ...-remove-legacy-minstrel-rate-control.patch |    2 +-
 ...te-control-support-for-encap-offload.patch |   43 +-
 ...introduce-aql_enable-node-in-debugfs.patch |  111 ++
 ...ange-struct-txq_info-for-fewer-holes.patch |   39 +
 ...to-a-virtual-time-based-airtime-sche.patch | 1277 +++++++++++++++++
 ...bling-4-address-mode-on-a-sta-vif-af.patch |   72 +
 ...on-API-to-configure-SAR-power-limita.patch |  398 +++++
 ...mac80211-add-ieee80211_set_sar_specs.patch |   51 +
 ...eck-per-vif-offload_flags-in-Tx-path.patch |   26 +
 .../500-mac80211_configure_antenna_gain.patch |   16 +-
 ...the-dst-buffer-to-of_get_mac_address.patch |  237 +++
 70 files changed, 2777 insertions(+), 1163 deletions(-)
 delete mode 100644 package/kernel/mac80211/files/lib/netifd/mac80211.sh
 delete mode 100644 package/kernel/mac80211/patches/ath/120-owl-loader-compat.patch
 rename package/kernel/mac80211/patches/{ath => ath10k}/080-ath10k_thermal_config.patch (97%)
 rename package/kernel/mac80211/patches/{ath => ath10k}/921-ath10k_init_devices_synchronously.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath10k}/922-ath10k-increase-rx-buffer-size-to-2048.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath10k}/930-ath10k_add_tpt_led_trigger.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath10k}/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch (99%)
 rename package/kernel/mac80211/patches/{ath => ath10k}/975-ath10k-use-tpt-trigger-by-default.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath10k}/980-ath10k-fix-max-antenna-gain-unit.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath10k}/981-ath10k-adjust-tx-power-reduction-for-US-regulatory-d.patch (100%)
 create mode 100644 package/kernel/mac80211/patches/ath10k/984-ath10k-Try-to-get-mac-address-from-dts.patch
 rename package/kernel/mac80211/patches/{ath => ath5k}/201-ath5k-WAR-for-AR71xx-PCI-bug.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath5k}/411-ath5k_allow_adhoc_and_ap.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath5k}/420-ath5k_disable_fast_cc.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath5k}/430-add_ath5k_platform.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath5k}/432-ath5k_add_pciids.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath5k}/440-ath5k_channel_bw_debugfs.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/350-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/351-ath9k_hw-issue-external-reset-for-QCA955x.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/354-ath9k-force-rx_clear-when-disabling-rx.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/356-Revert-ath9k-interpret-requested-txpower-in-EIRP-dom.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/365-ath9k-adjust-tx-power-reduction-for-US-regulatory-do.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/401-ath9k_blink_default.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/410-ath9k_allow_adhoc_and_ap.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/450-ath9k-enabled-MFP-capability-unconditionally.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/500-ath9k_eeprom_debugfs.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/501-ath9k_ahb_init.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/510-ath9k_intr_mitigation_tweak.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/511-ath9k_reduce_rxbuf.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/512-ath9k_channelbw_debugfs.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/513-ath9k_add_pci_ids.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/530-ath9k_extra_leds.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/531-ath9k_extra_platform_leds.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/540-ath9k_reduce_ani_interval.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/542-ath9k_debugfs_diag.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/543-ath9k_entropy_from_adc.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/544-ath9k-ar933x-usb-hang-workaround.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/545-ath9k_ani_ws_detect.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/547-ath9k_led_defstate_fix.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/548-ath9k_enable_gpio_chip.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/549-ath9k_enable_gpio_buttons.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/550-ath9k-disable-bands-via-dt.patch (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/551-ath9k_ubnt_uap_plus_hsr.patch (99%)
 rename package/kernel/mac80211/patches/{ath/552-ahb_of.patch => ath9k/552-ath9k-ahb_of.patch} (100%)
 rename package/kernel/mac80211/patches/{ath => ath9k}/553-ath9k_of_gpio_mask.patch (100%)
 delete mode 100644 package/kernel/mac80211/patches/subsys/100-remove-cryptoapi-dependencies.patch
 delete mode 100644 package/kernel/mac80211/patches/subsys/130-disable-fils.patch
 delete mode 100644 package/kernel/mac80211/patches/subsys/131-Revert-mac80211-aes-cmac-switch-to-shash-CMAC-driver.patch
 delete mode 100644 package/kernel/mac80211/patches/subsys/132-mac80211-remove-cmac-dependency.patch
 create mode 100644 package/kernel/mac80211/patches/subsys/320-mac80211_hwsim-add-6GHz-channels.patch
 create mode 100644 package/kernel/mac80211/patches/subsys/321-mac80211_hwsim-make-6-GHz-channels-usable.patch
 create mode 100644 package/kernel/mac80211/patches/subsys/380-mac80211-introduce-aql_enable-node-in-debugfs.patch
 create mode 100644 package/kernel/mac80211/patches/subsys/381-mac80211-rearrange-struct-txq_info-for-fewer-holes.patch
 create mode 100644 package/kernel/mac80211/patches/subsys/382-mac80211-Switch-to-a-virtual-time-based-airtime-sche.patch
 create mode 100644 package/kernel/mac80211/patches/subsys/383-mac80211-fix-enabling-4-address-mode-on-a-sta-vif-af.patch
 create mode 100644 package/kernel/mac80211/patches/subsys/384-nl80211-add-common-API-to-configure-SAR-power-limita.patch
 create mode 100644 package/kernel/mac80211/patches/subsys/385-mac80211-add-ieee80211_set_sar_specs.patch
 create mode 100644 package/kernel/mac80211/patches/subsys/386-mac80211-check-per-vif-offload_flags-in-Tx-path.patch
 create mode 100644 package/kernel/mac80211/patches/subsys/782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch

diff --git a/package/kernel/mac80211/Makefile b/package/kernel/mac80211/Makefile
index dd9ec172ca..dd39c2d069 100644
--- a/package/kernel/mac80211/Makefile
+++ b/package/kernel/mac80211/Makefile
@@ -98,7 +98,7 @@ PKG_CONFIG_DEPENDS += \
 define KernelPackage/cfg80211
   $(call KernelPackage/mac80211/Default)
   TITLE:=cfg80211 - wireless configuration API
-  DEPENDS+= +iw +wireless-regdb
+  DEPENDS+= +iw +iwinfo +wireless-regdb
   ABI_VERSION:=$(PKG_VERSION)-$(PKG_RELEASE)
   FILES:= \
 	$(PKG_BUILD_DIR)/compat/compat.ko \
@@ -127,7 +127,7 @@ define KernelPackage/mac80211
   $(call KernelPackage/mac80211/Default)
   TITLE:=Linux 802.11 Wireless Networking Stack
   # +kmod-crypto-cmac is a runtime only dependency of net/mac80211/aes_cmac.c
-  DEPENDS+= +kmod-cfg80211 +hostapd-common
+  DEPENDS+= +kmod-cfg80211 +kmod-crypto-cmac +kmod-crypto-ccm +kmod-crypto-gcm +hostapd-common
   KCONFIG:=\
 	CONFIG_AVERAGE=y
   FILES:= $(PKG_BUILD_DIR)/net/mac80211/mac80211.ko
@@ -501,9 +501,14 @@ define Build/Patch
 	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/build,build/)
 	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/subsys,subsys/)
 	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath,ath/)
+	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath5k,ath5k/)
+	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath9k,ath9k/)
+	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath10k,ath10k/)
+	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath11k,ath11k/)
 	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/rt2x00,rt2x00/)
 	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/mwl,mwl/)
 	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/brcm,brcm/)
+	$(call PatchDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/rtl,rtl/)
 	$(if $(QUILT),touch $(PKG_BUILD_DIR)/.quilt_used)
 endef
 
@@ -511,9 +516,14 @@ define Quilt/Refresh/Package
 	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/build,build/)
 	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/subsys,subsys/)
 	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath,ath/)
+	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath5k,ath5k/)
+	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath9k,ath9k/)
+	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath10k,ath10k/)
+	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/ath11k,ath11k/)
 	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/rt2x00,rt2x00/)
 	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/mwl,mwl/)
 	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/brcm,brcm/)
+	$(call Quilt/RefreshDir,$(PKG_BUILD_DIR),$(PATCH_DIR)/rtl,rtl/)
 endef
 
 define Build/Compile
@@ -539,7 +549,6 @@ endef
 define KernelPackage/cfg80211/install
 	$(INSTALL_DIR) $(1)/lib/wifi $(1)/lib/netifd/wireless
 	$(INSTALL_DATA) ./files/lib/wifi/mac80211.sh $(1)/lib/wifi
-	$(INSTALL_DATA) ./files/lib/netifd/mac80211.sh $(1)/lib/netifd
 	$(INSTALL_BIN) ./files/lib/netifd/wireless/mac80211.sh $(1)/lib/netifd/wireless
 	$(INSTALL_DIR) $(1)/etc/hotplug.d/ieee80211
 	$(INSTALL_DATA) ./files/mac80211.hotplug $(1)/etc/hotplug.d/ieee80211/10-wifi-detect
diff --git a/package/kernel/mac80211/ath.mk b/package/kernel/mac80211/ath.mk
index 24abb910ff..ad2860a98e 100644
--- a/package/kernel/mac80211/ath.mk
+++ b/package/kernel/mac80211/ath.mk
@@ -34,7 +34,7 @@ ifdef CONFIG_PACKAGE_MAC80211_TRACING
 	WIL6210_TRACING
 endif
 
-config-$(call config_package,ath) += ATH_CARDS ATH_COMMON ATH_REG_DYNAMIC_USER_REG_HINTS
+config-$(call config_package,ath) += ATH_CARDS ATH_COMMON
 config-$(CONFIG_PACKAGE_ATH_DEBUG) += ATH_DEBUG ATH10K_DEBUG ATH9K_STATION_STATISTICS
 config-$(CONFIG_PACKAGE_ATH_DFS) += ATH9K_DFS_CERTIFIED ATH10K_DFS_CERTIFIED
 config-$(CONFIG_PACKAGE_ATH_SPECTRAL) += ATH9K_COMMON_SPECTRAL ATH10K_SPECTRAL
@@ -43,9 +43,10 @@ config-$(call config_package,ath9k) += ATH9K
 config-$(call config_package,ath9k-common) += ATH9K_COMMON
 config-$(call config_package,owl-loader) += ATH9K_PCI_NO_EEPROM
 config-$(CONFIG_TARGET_ath79) += ATH9K_AHB
+config-$(CONFIG_TARGET_ar71xx) += ATH9K_AHB
 config-$(CONFIG_TARGET_ipq40xx) += ATH10K_AHB
 config-$(CONFIG_PCI) += ATH9K_PCI
-config-$(CONFIG_ATH_USER_REGD) += ATH_USER_REGD
+config-$(CONFIG_ATH_USER_REGD) += ATH_USER_REGD ATH_REG_DYNAMIC_USER_REG_HINTS
 config-$(CONFIG_ATH9K_HWRNG) += ATH9K_HWRNG
 config-$(CONFIG_ATH9K_SUPPORT_PCOEM) += ATH9K_PCOEM
 config-$(CONFIG_ATH9K_TX99) += ATH9K_TX99
diff --git a/package/kernel/mac80211/broadcom.mk b/package/kernel/mac80211/broadcom.mk
index fb576c5809..473bbf597c 100644
--- a/package/kernel/mac80211/broadcom.mk
+++ b/package/kernel/mac80211/broadcom.mk
@@ -209,7 +209,7 @@ config PACKAGE_B43_USE_BCMA
 		default "16,28,29,30" if TARGET_bcm47xx_mips74k
 		default "5,6,7,8,9,10,11,13,15,16,28,29,30"
 		help
-		  This is a comma seperated list of core revision numbers.
+		  This is a comma separated list of core revision numbers.
 
 		  Example (keep files for rev5 only):
 		    5
@@ -224,7 +224,7 @@ config PACKAGE_B43_USE_BCMA
 		default "N,HT" if TARGET_bcm47xx_mips74k
 		default "G,N,LP,HT"
 		help
-		  This is a comma seperated list of PHY types:
+		  This is a comma separated list of PHY types:
 		    A  => A-PHY
 		    AG => Dual A-PHY G-PHY
 		    G  => G-PHY
diff --git a/package/kernel/mac80211/files/lib/netifd/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/mac80211.sh
deleted file mode 100644
index 92e5c0e395..0000000000
--- a/package/kernel/mac80211/files/lib/netifd/mac80211.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-mac80211_phy_to_path() {
-	local phy="$1"
-
-	[ -x /usr/bin/readlink -a -h /sys/class/ieee80211/${phy} ] || return
-
-	local path="$(readlink -f /sys/class/ieee80211/${phy}/device)"
-	[ -n "$path" ] || return
-
-	path="${path##/sys/devices/}"
-	case "$path" in
-		platform*/pci*) path="${path##platform/}";;
-	esac
-
-	local p
-	local seq=""
-	for p in $(ls /sys/class/ieee80211/$phy/device/ieee80211); do
-		[ "$p" = "$phy" ] && {
-			echo "$path${seq:++$seq}"
-			break
-		}
-
-		seq=$((${seq:-0} + 1))
-	done
-}
-
-mac80211_path_to_phy() {
-	local path="$1"
-
-	local p
-	for p in $(ls /sys/class/ieee80211); do
-		local cur="$(mac80211_phy_to_path "$p")"
-		case "$cur" in
-			*$path) echo "$p"; return;;
-		esac
-	done
-}
diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
index 9a9c35fb5f..97c567031e 100644
--- a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
+++ b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
@@ -1,7 +1,6 @@
 #!/bin/sh
 . /lib/netifd/netifd-wireless.sh
 . /lib/netifd/hostapd.sh
-. /lib/netifd/mac80211.sh
 
 init_wireless_driver "$@"
 
@@ -26,11 +25,11 @@ drv_mac80211_init_device_config() {
 	hostapd_common_add_device_config
 
 	config_add_string path phy 'macaddr:macaddr'
-	config_add_string hwmode
 	config_add_string tx_burst
 	config_add_string distance
 	config_add_int beacon_int chanbw frag rts
 	config_add_int rxantenna txantenna antenna_gain txpower
+	config_add_int num_global_macaddr
 	config_add_boolean noscan ht_coex acs_exclude_dfs
 	config_add_array ht_capab
 	config_add_array channels
@@ -44,11 +43,26 @@ drv_mac80211_init_device_config() {
 		su_beamformee \
 		mu_beamformer \
 		mu_beamformee \
+		he_su_beamformer \
+		he_su_beamformee \
+		he_mu_beamformer \
 		vht_txop_ps \
 		htc_vht \
 		rx_antenna_pattern \
-		tx_antenna_pattern
-	config_add_int vht_max_a_mpdu_len_exp vht_max_mpdu vht_link_adapt vht160 rx_stbc tx_stbc
+		tx_antenna_pattern \
+		he_spr_sr_control \
+		he_twt_required
+	config_add_int \
+		beamformer_antennas \
+		beamformee_antennas \
+		vht_max_a_mpdu_len_exp \
+		vht_max_mpdu \
+		vht_link_adapt \
+		vht160 \
+		rx_stbc \
+		tx_stbc \
+		he_bss_color \
+		he_spr_non_srg_obss_pd_max_offset
 	config_add_boolean \
 		ldpc \
 		greenfield \
@@ -96,6 +110,23 @@ mac80211_add_capabilities() {
 	export -n -- "$__var=$__out"
 }
 
+mac80211_add_he_capabilities() {
+	local __out= oifs
+
+	oifs="$IFS"
+	IFS=:
+	for capab in "$@"; do
+		set -- $capab
+		[ "$(($4))" -gt 0 ] || continue
+		[ "$(((0x$2) & $3))" -gt 0 ] || {
+			eval "$1=0"
+			continue
+		}
+		append base_cfg "$1=1" "$N"
+	done
+	IFS="$oifs"
+}
+
 mac80211_hostapd_setup_base() {
 	local phy="$1"
 
@@ -119,6 +150,9 @@ mac80211_hostapd_setup_base() {
 	[ "$noscan" -gt 0 ] && hostapd_noscan=1
 	[ "$tx_burst" = 0 ] && tx_burst=
 
+	chan_ofs=0
+	[ "$band" = "6g" ] && chan_ofs=1
+
 	ieee80211n=1
 	ht_capab=
 	case "$htmode" in
@@ -126,7 +160,7 @@ mac80211_hostapd_setup_base() {
 		HT40*|VHT40|VHT80|VHT160|HE40|HE80|HE160)
 			case "$hwmode" in
 				a)
-					case "$(( ($channel / 4) % 2 ))" in
+					case "$(( (($channel / 4) + $chan_ofs) % 2 ))" in
 						1) ht_capab="[HT40+]";;
 						0) ht_capab="[HT40-]";;
 					esac
@@ -200,7 +234,7 @@ mac80211_hostapd_setup_base() {
 	case "$htmode" in
 		VHT20|HE20) enable_ac=1;;
 		VHT40|HE40)
-			case "$(( ($channel / 4) % 2 ))" in
+			case "$(( (($channel / 4) + $chan_ofs) % 2 ))" in
 				1) idx=$(($channel + 2));;
 				0) idx=$(($channel - 2));;
 			esac
@@ -208,7 +242,7 @@ mac80211_hostapd_setup_base() {
 			vht_center_seg0=$idx
 		;;
 		VHT80|HE80)
-			case "$(( ($channel / 4) % 4 ))" in
+			case "$(( (($channel / 4) + $chan_ofs) % 4 ))" in
 				1) idx=$(($channel + 6));;
 				2) idx=$(($channel + 2));;
 				3) idx=$(($channel - 2));;
@@ -219,15 +253,35 @@ mac80211_hostapd_setup_base() {
 			vht_center_seg0=$idx
 		;;
 		VHT160|HE160)
-			case "$channel" in
-				36|40|44|48|52|56|60|64) idx=50;;
-				100|104|108|112|116|120|124|128) idx=114;;
-			esac
+			if [ "$band" = "6g" ]; then
+				case "$channel" in
+					1|5|9|13|17|21|25|29) idx=15;;
+					33|37|41|45|49|53|57|61) idx=47;;
+					65|69|73|77|81|85|89|93) idx=79;;
+					97|101|105|109|113|117|121|125) idx=111;;
+					129|133|137|141|145|149|153|157) idx=143;;
+					161|165|169|173|177|181|185|189) idx=175;;
+					193|197|201|205|209|213|217|221) idx=207;;
+				esac
+			else
+				case "$channel" in
+					36|40|44|48|52|56|60|64) idx=50;;
+					100|104|108|112|116|120|124|128) idx=114;;
+				esac
+			fi
 			enable_ac=1
 			vht_oper_chwidth=2
 			vht_center_seg0=$idx
 		;;
 	esac
+	[ "$band" = "6g" ] && {
+		op_class=
+		case "$htmode" in
+			HE20) op_class=131;;
+			HE*) op_class=$((132 + $vht_oper_chwidth))
+		esac
+		[ -n "$op_class" ] && append base_cfg "op_class=$op_class" "$N"
+	}
 	[ "$hwmode" = "a" ] || enable_ac=0
 
 	if [ "$enable_ac" != "0" ]; then
@@ -242,6 +296,8 @@ mac80211_hostapd_setup_base() {
 			mu_beamformee:1 \
 			vht_txop_ps:1 \
 			htc_vht:1 \
+			beamformee_antennas:4 \
+			beamformer_antennas:4 \
 			rx_antenna_pattern:1 \
 			tx_antenna_pattern:1 \
 			vht_max_a_mpdu_len_exp:7 \
@@ -282,6 +338,18 @@ mac80211_hostapd_setup_base() {
 			RX-STBC-123:0x700:0x300:1 \
 			RX-STBC-1234:0x700:0x400:1 \
 
+		[ "$(($vht_cap & 0x800))" -gt 0 -a "$su_beamformer" -gt 0 ] && {
+			cap_ant="$(( ( ($vht_cap >> 16) & 3 ) + 1 ))"
+			[ "$cap_ant" -gt "$beamformer_antennas" ] && cap_ant="$beamformer_antennas"
+			[ "$cap_ant" -gt 1 ] && vht_capab="$vht_capab[SOUNDING-DIMENSION-$cap_ant]"
+		}
+
+		[ "$(($vht_cap & 0x1000))" -gt 0 -a "$su_beamformee" -gt 0 ] && {
+			cap_ant="$(( ( ($vht_cap >> 13) & 3 ) + 1 ))"
+			[ "$cap_ant" -gt "$beamformee_antennas" ] && cap_ant="$beamformee_antennas"
+			[ "$cap_ant" -gt 1 ] && vht_capab="$vht_capab[BF-ANTENNA-$cap_ant]"
+		}
+
 		# supported Channel widths
 		vht160_hw=0
 		[ "$(($vht_cap & 12))" -eq 4 -a 1 -le "$vht160" ] && \
@@ -337,16 +405,62 @@ mac80211_hostapd_setup_base() {
 	esac
 
 	if [ "$enable_ax" != "0" ]; then
+		json_get_vars \
+			he_su_beamformer:1 \
+			he_su_beamformee:0 \
+			he_mu_beamformer:1 \
+			he_twt_required:0 \
+			he_spr_sr_control:0 \
+			he_spr_non_srg_obss_pd_max_offset:1 \
+			he_bss_color
+
+		he_phy_cap=$(iw phy "$phy" info | awk -F "[()]" '/HE PHY Capabilities/ { print $2 }' | head -1)
+		he_phy_cap=${he_phy_cap:2}
+		he_mac_cap=$(iw phy "$phy" info | awk -F "[()]" '/HE MAC Capabilities/ { print $2 }' | head -1)
+		he_mac_cap=${he_mac_cap:2}
+
 		append base_cfg "ieee80211ax=1" "$N"
+		[ -n "$he_bss_color" ] && append base_cfg "he_bss_color=$he_bss_color" "$N"
 		[ "$hwmode" = "a" ] && {
 			append base_cfg "he_oper_chwidth=$vht_oper_chwidth" "$N"
 			append base_cfg "he_oper_centr_freq_seg0_idx=$vht_center_seg0" "$N"
 		}
+
+		mac80211_add_he_capabilities \
+			he_su_beamformer:${he_phy_cap:6:2}:0x80:$he_su_beamformer \
+			he_su_beamformee:${he_phy_cap:8:2}:0x1:$he_su_beamformee \
+			he_mu_beamformer:${he_phy_cap:8:2}:0x2:$he_mu_beamformer \
+			he_spr_sr_control:${he_phy_cap:14:2}:0x1:$he_spr_sr_control \
+			he_twt_required:${he_mac_cap:0:2}:0x6:$he_twt_required
+
+		[ "$he_spr_sr_control" -gt 0 ] && append base_cfg "he_spr_non_srg_obss_pd_max_offset=$he_spr_non_srg_obss_pd_max_offset" "$N"
+
 		append base_cfg "he_default_pe_duration=4" "$N"
 		append base_cfg "he_rts_threshold=1023" "$N"
-		append base_cfg "he_su_beamformer=1" "$N"
-		append base_cfg "he_su_beamformee=1" "$N"
-		append base_cfg "he_mu_beamformer=1" "$N"
+		append base_cfg "he_mu_edca_qos_info_param_count=0" "$N"
+		append base_cfg "he_mu_edca_qos_info_q_ack=0" "$N"
+		append base_cfg "he_mu_edca_qos_info_queue_request=0" "$N"
+		append base_cfg "he_mu_edca_qos_info_txop_request=0" "$N"
+		append base_cfg "he_mu_edca_ac_be_aifsn=8" "$N"
+		append base_cfg "he_mu_edca_ac_be_aci=0" "$N"
+		append base_cfg "he_mu_edca_ac_be_ecwmin=9" "$N"
+		append base_cfg "he_mu_edca_ac_be_ecwmax=10" "$N"
+		append base_cfg "he_mu_edca_ac_be_timer=255" "$N"
+		append base_cfg "he_mu_edca_ac_bk_aifsn=15" "$N"
+		append base_cfg "he_mu_edca_ac_bk_aci=1" "$N"
+		append base_cfg "he_mu_edca_ac_bk_ecwmin=9" "$N"
+		append base_cfg "he_mu_edca_ac_bk_ecwmax=10" "$N"
+		append base_cfg "he_mu_edca_ac_bk_timer=255" "$N"
+		append base_cfg "he_mu_edca_ac_vi_ecwmin=5" "$N"
+		append base_cfg "he_mu_edca_ac_vi_ecwmax=7" "$N"
+		append base_cfg "he_mu_edca_ac_vi_aifsn=5" "$N"
+		append base_cfg "he_mu_edca_ac_vi_aci=2" "$N"
+		append base_cfg "he_mu_edca_ac_vi_timer=255" "$N"
+		append base_cfg "he_mu_edca_ac_vo_aifsn=5" "$N"
+		append base_cfg "he_mu_edca_ac_vo_aci=3" "$N"
+		append base_cfg "he_mu_edca_ac_vo_ecwmin=5" "$N"
+		append base_cfg "he_mu_edca_ac_vo_ecwmax=7" "$N"
+		append base_cfg "he_mu_edca_ac_vo_timer=255" "$N"
 	fi
 
 	hostapd_prepare_device_config "$hostapd_conf_file" nl80211
@@ -401,6 +515,7 @@ mac80211_get_addr() {
 
 mac80211_generate_mac() {
 	local phy="$1"
+	local multiple_bssid="$2"
 	local id="${macidx:-0}"
 
 	local ref="$(cat /sys/class/ieee80211/${phy}/macaddress)"
@@ -424,9 +539,16 @@ mac80211_generate_mac() {
 	local mask6=$6
 
 	local oIFS="$IFS"; IFS=":"; set -- $ref; IFS="$oIFS"
-
+	[ "$multiple_bssid" -eq 1 ] && {
+               printf "02:%s:%s:%s:%s:%02x" $b1 $2 $3 $4 $5 $macidx
+               return
+    }
 	macidx=$(($id + 1))
-	[ "$((0x$mask1))" -gt 0 ] && {
+
+	local use_global=0
+	[ "$id" -gt 0 -a "$macidx" -le "$num_global_macaddr" ] && use_global=1
+
+	[ "$((0x$mask1))" -gt 0 -a "$use_global" -lt 1 ] && {
 		b1="0x$1"
 		[ "$id" -gt 0 ] && \
 			b1=$(($b1 ^ ((($id - !($b1 & 2)) << 2)) | 0x2))
@@ -434,7 +556,7 @@ mac80211_generate_mac() {
 		return
 	}
 
-	[ "$((0x$mask6))" -lt 255 ] && {
+	[ "$((0x$mask6))" -lt 255 -a "$use_global" -gt 0 ] && {
 		printf "%s:%s:%s:%s:%s:%02x" $1 $2 $3 $4 $5 $(( 0x$6 ^ $id ))
 		return
 	}
@@ -449,7 +571,7 @@ mac80211_generate_mac() {
 find_phy() {
 	[ -n "$phy" -a -d /sys/class/ieee80211/$phy ] && return 0
 	[ -n "$path" ] && {
-		phy="$(mac80211_path_to_phy "$path")"
+		phy="$(iwinfo nl80211 phyname "path=$path")"
 		[ -n "$phy" ] && return 0
 	}
 	[ -n "$macaddr" ] && {
@@ -528,11 +650,12 @@ mac80211_iw_interface_add() {
 		rc="$?"
 	}
 
-	[ "$rc" != 0 ] && wireless_setup_failed INTERFACE_CREATION_FAILED
+	[ "$rc" != 0 ] && echo "Failed to create interface $ifname"
 	return $rc
 }
 
 mac80211_prepare_vif() {
+	local multiple_bssid=$1
 	json_select config
 
 	json_get_vars ifname mode ssid wds powersave macaddr enable wpa_psk_file vlan_file
@@ -546,7 +669,7 @@ mac80211_prepare_vif() {
 	json_select ..
 
 	[ -n "$macaddr" ] || {
-		macaddr="$(mac80211_generate_mac $phy)"
+		macaddr="$(mac80211_generate_mac $phy $multiple_bssid)"
 		macidx="$(($macidx + 1))"
 	}
 
@@ -689,14 +812,8 @@ mac80211_prepare_iw_htmode() {
 	case "$htmode" in
 		VHT20|HT20) iw_htmode=HT20;;
 		HT40*|VHT40|VHT160)
-			case "$hwmode" in
-				a)
-					case "$(( ($channel / 4) % 2 ))" in
-						1) iw_htmode="HT40+" ;;
-						0) iw_htmode="HT40-";;
-					esac
-				;;
-				*)
+			case "$band" in
+				2g)
 					case "$htmode" in
 						HT40+) iw_htmode="HT40+";;
 						HT40-) iw_htmode="HT40-";;
@@ -709,6 +826,12 @@ mac80211_prepare_iw_htmode() {
 						;;
 					esac
 				;;
+				*)
+					case "$(( ($channel / 4) % 2 ))" in
+						1) iw_htmode="HT40+" ;;
+						0) iw_htmode="HT40-";;
+					esac
+				;;
 			esac
 			[ "$auto_channel" -gt 0 ] && iw_htmode="HT40+"
 		;;
@@ -818,7 +941,6 @@ mac80211_setup_vif() {
 		mesh)
 			wireless_vif_parse_encryption
 			[ -z "$htmode" ] && htmode="NOHT";
-			freq="$(get_freq "$phy" "$channel")"
 			if [ "$wpa" -gt 0 -o "$auto_channel" -gt 0 ] || chan_is_dfs "$phy" "$channel"; then
 				mac80211_setup_supplicant $vif_enable || failed=1
 			else
@@ -832,7 +954,6 @@ mac80211_setup_vif() {
 		adhoc)
 			wireless_vif_parse_encryption
 			if [ "$wpa" -gt 0 -o "$auto_channel" -gt 0 ]; then
-				freq="$(get_freq "$phy" "$channel")"
 				mac80211_setup_supplicant_noctl $vif_enable || failed=1
 			else
 				mac80211_setup_adhoc $vif_enable
@@ -849,10 +970,30 @@ mac80211_setup_vif() {
 
 get_freq() {
 	local phy="$1"
-	local chan="$2"
-	iw "$phy" info | grep -E -m1 "(\* ${chan:-....} MHz${chan:+|\\[$chan\\]})" | grep MHz | awk '{print $2}'
+	local channel="$2"
+	local band="$3"
+
+	case "$band" in
+		2g) band="1:";;
+		5g) band="2:";;
+		60g) band="3:";;
+		6g) band="4:";;
+	esac
+
+	iw "$phy" info | awk -v band="$band" -v channel="[$channel]" '
+
+$1 ~ /Band/ {
+	band_match = band == $2
+}
+
+band_match && $3 == "MHz" && $4 == channel {
+	print $2
+	exit
+}
+'
 }
 
+
 chan_is_dfs() {
 	local phy="$1"
 	local chan="$2"
@@ -896,7 +1037,9 @@ drv_mac80211_setup() {
 		country chanbw distance \
 		txpower antenna_gain \
 		rxantenna txantenna \
-		frag rts beacon_int:100 htmode
+		frag rts beacon_int:100 htmode \
+		multiple_bssid:0 \
+		num_global_macaddr
 	json_get_values basic_rate_list basic_rate
 	json_get_values scan_list scan_list
 	json_select ..
@@ -933,7 +1076,7 @@ drv_mac80211_setup() {
 	done
 
 	# convert channel to frequency
-	[ "$auto_channel" -gt 0 ] || freq="$(get_freq "$phy" "$channel")"
+	[ "$auto_channel" -gt 0 ] || freq="$(get_freq "$phy" "$channel" "$band")"
 
 	[ -n "$country" ] && {
 		iw reg get | grep -q "^country $country:" || {
@@ -958,6 +1101,7 @@ drv_mac80211_setup() {
 	set_default txantenna 0xffffffff
 	set_default distance 0
 	set_default antenna_gain 0
+	set_default num_global_macaddr 1
 
 	[ "$txantenna" = "all" ] && txantenna=0xffffffff
 	[ "$rxantenna" = "all" ] && rxantenna=0xffffffff
@@ -989,7 +1133,7 @@ drv_mac80211_setup() {
 	mac80211_prepare_iw_htmode
 	for_each_interface "sta adhoc mesh monitor" mac80211_prepare_vif
 	NEWAPLIST=
-	for_each_interface "ap" mac80211_prepare_vif
+	for_each_interface "ap" mac80211_prepare_vif ${multiple_bssid}
 	NEW_MD5=$(test -e "${hostapd_conf_file}" && md5sum ${hostapd_conf_file})
 	OLD_MD5=$(uci -q -P /var/state get wireless._${phy}.md5)
 	if [ "${NEWAPLIST}" != "${OLDAPLIST}" ]; then
diff --git a/package/kernel/mac80211/files/lib/wifi/mac80211.sh b/package/kernel/mac80211/files/lib/wifi/mac80211.sh
index 3e99f06693..6aa46b0c74 100644
--- a/package/kernel/mac80211/files/lib/wifi/mac80211.sh
+++ b/package/kernel/mac80211/files/lib/wifi/mac80211.sh
@@ -1,5 +1,4 @@
 #!/bin/sh
-. /lib/netifd/mac80211.sh
 
 append DRIVERS "mac80211"
 
@@ -11,7 +10,7 @@ lookup_phy() {
 	local devpath
 	config_get devpath "$device" path
 	[ -n "$devpath" ] && {
-		phy="$(mac80211_path_to_phy "$devpath")"
+		phy="$(iwinfo nl80211 phyname "path=$devpath")"
 		[ -n "$phy" ] && return
 	}
 
@@ -57,6 +56,85 @@ check_mac80211_device() {
 	[ "$phy" = "$dev" ] && found=1
 }
 
+
+__get_band_defaults() {
+	local phy="$1"
+
+	( iw phy "$phy" info; echo ) | awk '
+BEGIN {
+        bands = ""
+}
+
+($1 == "Band" || $1 == "") && band {
+        if (channel) {
+		mode="NOHT"
+		if (ht) mode="HT20"
+		if (vht && band != "1:") mode="VHT80"
+		if (he) mode="HE80"
+		if (he && band == "1:") mode="HE20"
+                sub("\\[", "", channel)
+                sub("\\]", "", channel)
+                bands = bands band channel ":" mode " "
+        }
+        band=""
+}
+
+$1 == "Band" {
+        band = $2
+        channel = ""
+	vht = ""
+	ht = ""
+	he = ""
+}
+
+$0 ~ "Capabilities:" {
+	ht=1
+}
+
+$0 ~ "VHT Capabilities" {
+	vht=1
+}
+
+$0 ~ "HE Iftypes" {
+	he=1
+}
+
+$1 == "*" && $3 == "MHz" && $0 !~ /disabled/ && band && !channel {
+        channel = $4
+}
+
+END {
+        print bands
+}'
+}
+
+get_band_defaults() {
+	local phy="$1"
+
+	for c in $(__get_band_defaults "$phy"); do
+		local band="${c%%:*}"
+		c="${c#*:}"
+		local chan="${c%%:*}"
+		c="${c#*:}"
+		local mode="${c%%:*}"
+
+		case "$band" in
+			1) band=2g;;
+			2) band=5g;;
+			3) band=60g;;
+			4) band=6g;;
+			*) band="";;
+		esac
+
+		[ -n "$band" ] || continue
+		[ -n "$mode_band" -a "$band" = "6g" ] && return
+
+		mode_band="$band"
+		channel="$chan"
+		htmode="$mode"
+	done
+}
+
 detect_mac80211() {
 	devidx=0
 	config_load wireless
@@ -75,28 +153,14 @@ detect_mac80211() {
 		config_foreach check_mac80211_device wifi-device
 		[ "$found" -gt 0 ] && continue
 
-		mode_band="g"
-		channel="11"
+		mode_band=""
+		channel=""
 		htmode=""
 		ht_capab=""
 
-		iw phy "$dev" info | grep -q 'Capabilities:' && htmode=HT20
-
-		iw phy "$dev" info | grep -q '\* 5... MHz \[' && {
-			mode_band="a"
-			channel=$(iw phy "$dev" info | grep '\* 5... MHz \[' | grep '(disabled)' -v -m 1 | sed 's/[^[]*\[\|\].*//g')
-			iw phy "$dev" info | grep -q 'VHT Capabilities' && htmode="VHT80"
-		}
-
-		iw phy "$dev" info | grep -q '\* 5.... MHz \[' && {
-			mode_band="ad"
-			channel=$(iw phy "$dev" info | grep '\* 5.... MHz \[' | grep '(disabled)' -v -m 1 | sed 's/[^[]*\[\|\|\].*//g')
-			iw phy "$dev" info | grep -q 'Capabilities:' && htmode="HT20"
-		}
+		get_band_defaults "$dev"
 
-		[ -n "$htmode" ] && ht_capab="set wireless.radio${devidx}.htmode=$htmode"
-
-		path="$(mac80211_phy_to_path "$dev")"
+		path="$(iwinfo nl80211 path "$dev")"
 		if [ -n "$path" ]; then
 			dev_id="set wireless.radio${devidx}.path='$path'"
 		else
@@ -106,10 +170,10 @@ detect_mac80211() {
 		uci -q batch <<-EOF
 			set wireless.radio${devidx}=wifi-device
 			set wireless.radio${devidx}.type=mac80211
-			set wireless.radio${devidx}.channel=${channel}
-			set wireless.radio${devidx}.hwmode=11${mode_band}
 			${dev_id}
-			${ht_capab}
+			set wireless.radio${devidx}.channel=${channel}
+			set wireless.radio${devidx}.band=${mode_band}
+			set wireless.radio${devidx}.htmode=$htmode
 			set wireless.radio${devidx}.disabled=1
 
 			set wireless.default_radio${devidx}=wifi-iface
diff --git a/package/kernel/mac80211/patches/ath/120-owl-loader-compat.patch b/package/kernel/mac80211/patches/ath/120-owl-loader-compat.patch
deleted file mode 100644
index d1d6c9e2e3..0000000000
--- a/package/kernel/mac80211/patches/ath/120-owl-loader-compat.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From: Christian Lamparter <chunkeey@gmail.com>
-Date: Sat, 16 Nov 2019 19:25:24 +0100
-Subject: [PATCH] owl_loader: compatibility patch
-
-This patch includes OpenWrt specific changes that are
-not included in the upstream owl-loader.
-
-This includes a platform data handling changes for ar71xx.
-
-Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
-
---- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
-+++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
-@@ -103,6 +103,7 @@ static void owl_fw_cb(const struct firmw
- {
- 	struct pci_dev *pdev = (struct pci_dev *)context;
- 	struct owl_ctx *ctx = (struct owl_ctx *)pci_get_drvdata(pdev);
-+	struct ath9k_platform_data *pdata = dev_get_platdata(&pdev->dev);
- 	struct pci_bus *bus;
- 
- 	complete(&ctx->eeprom_load);
-@@ -118,6 +119,16 @@ static void owl_fw_cb(const struct firmw
- 		goto release;
- 	}
- 
-+	if (pdata) {
-+		memcpy(pdata->eeprom_data, fw->data, fw->size);
-+
-+		/*
-+		 * eeprom has been successfully loaded - pass the data to ath9k
-+		 * but remove the eeprom_name, so it doesn't try to load it too.
-+		 */
-+		pdata->eeprom_name = NULL;
-+	}
-+
- 	if (ath9k_pci_fixup(pdev, (const u16 *)fw->data, fw->size))
- 		goto release;
- 
-@@ -137,8 +148,14 @@ release:
- static const char *owl_get_eeprom_name(struct pci_dev *pdev)
- {
- 	struct device *dev = &pdev->dev;
-+	struct ath9k_platform_data *pdata;
- 	char *eeprom_name;
- 
-+	/* try the existing platform data first */
-+	pdata = dev_get_platdata(dev);
-+	if (pdata && pdata->eeprom_name)
-+		return pdata->eeprom_name;
-+
- 	dev_dbg(dev, "using auto-generated eeprom filename\n");
- 
- 	eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
diff --git a/package/kernel/mac80211/patches/ath/402-ath_regd_optional.patch b/package/kernel/mac80211/patches/ath/402-ath_regd_optional.patch
index bf87d3551a..3c9180b113 100644
--- a/package/kernel/mac80211/patches/ath/402-ath_regd_optional.patch
+++ b/package/kernel/mac80211/patches/ath/402-ath_regd_optional.patch
@@ -82,7 +82,7 @@
  	help
 --- a/local-symbols
 +++ b/local-symbols
-@@ -85,6 +85,7 @@ ADM8211=
+@@ -86,6 +86,7 @@ ADM8211=
  ATH_COMMON=
  WLAN_VENDOR_ATH=
  ATH_DEBUG=
diff --git a/package/kernel/mac80211/patches/ath/080-ath10k_thermal_config.patch b/package/kernel/mac80211/patches/ath10k/080-ath10k_thermal_config.patch
similarity index 97%
rename from package/kernel/mac80211/patches/ath/080-ath10k_thermal_config.patch
rename to package/kernel/mac80211/patches/ath10k/080-ath10k_thermal_config.patch
index de6f9d9bb0..9ce44fd288 100644
--- a/package/kernel/mac80211/patches/ath/080-ath10k_thermal_config.patch
+++ b/package/kernel/mac80211/patches/ath10k/080-ath10k_thermal_config.patch
@@ -37,7 +37,7 @@
  void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature);
 --- a/local-symbols
 +++ b/local-symbols
-@@ -142,6 +142,7 @@ ATH10K_SNOC=
+@@ -145,6 +145,7 @@ ATH10K_SNOC=
  ATH10K_DEBUG=
  ATH10K_DEBUGFS=
  ATH10K_SPECTRAL=
diff --git a/package/kernel/mac80211/patches/ath/921-ath10k_init_devices_synchronously.patch b/package/kernel/mac80211/patches/ath10k/921-ath10k_init_devices_synchronously.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/921-ath10k_init_devices_synchronously.patch
rename to package/kernel/mac80211/patches/ath10k/921-ath10k_init_devices_synchronously.patch
diff --git a/package/kernel/mac80211/patches/ath/922-ath10k-increase-rx-buffer-size-to-2048.patch b/package/kernel/mac80211/patches/ath10k/922-ath10k-increase-rx-buffer-size-to-2048.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/922-ath10k-increase-rx-buffer-size-to-2048.patch
rename to package/kernel/mac80211/patches/ath10k/922-ath10k-increase-rx-buffer-size-to-2048.patch
diff --git a/package/kernel/mac80211/patches/ath/930-ath10k_add_tpt_led_trigger.patch b/package/kernel/mac80211/patches/ath10k/930-ath10k_add_tpt_led_trigger.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/930-ath10k_add_tpt_led_trigger.patch
rename to package/kernel/mac80211/patches/ath10k/930-ath10k_add_tpt_led_trigger.patch
diff --git a/package/kernel/mac80211/patches/ath/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch b/package/kernel/mac80211/patches/ath10k/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch
similarity index 99%
rename from package/kernel/mac80211/patches/ath/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch
rename to package/kernel/mac80211/patches/ath10k/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch
index ce8effe3c3..fa007e73a1 100644
--- a/package/kernel/mac80211/patches/ath/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch
+++ b/package/kernel/mac80211/patches/ath10k/974-ath10k_add-LED-and-GPIO-controlling-support-for-various-chipsets.patch
@@ -114,7 +114,7 @@ v13:
  ath10k_core-$(CONFIG_DEV_COREDUMP) += coredump.o
 --- a/local-symbols
 +++ b/local-symbols
-@@ -145,6 +145,7 @@ ATH10K_DEBUG=
+@@ -146,6 +146,7 @@ ATH10K_DEBUG=
  ATH10K_DEBUGFS=
  ATH10K_SPECTRAL=
  ATH10K_THERMAL=
diff --git a/package/kernel/mac80211/patches/ath/975-ath10k-use-tpt-trigger-by-default.patch b/package/kernel/mac80211/patches/ath10k/975-ath10k-use-tpt-trigger-by-default.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/975-ath10k-use-tpt-trigger-by-default.patch
rename to package/kernel/mac80211/patches/ath10k/975-ath10k-use-tpt-trigger-by-default.patch
diff --git a/package/kernel/mac80211/patches/ath/980-ath10k-fix-max-antenna-gain-unit.patch b/package/kernel/mac80211/patches/ath10k/980-ath10k-fix-max-antenna-gain-unit.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/980-ath10k-fix-max-antenna-gain-unit.patch
rename to package/kernel/mac80211/patches/ath10k/980-ath10k-fix-max-antenna-gain-unit.patch
diff --git a/package/kernel/mac80211/patches/ath/981-ath10k-adjust-tx-power-reduction-for-US-regulatory-d.patch b/package/kernel/mac80211/patches/ath10k/981-ath10k-adjust-tx-power-reduction-for-US-regulatory-d.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/981-ath10k-adjust-tx-power-reduction-for-US-regulatory-d.patch
rename to package/kernel/mac80211/patches/ath10k/981-ath10k-adjust-tx-power-reduction-for-US-regulatory-d.patch
diff --git a/package/kernel/mac80211/patches/ath10k/984-ath10k-Try-to-get-mac-address-from-dts.patch b/package/kernel/mac80211/patches/ath10k/984-ath10k-Try-to-get-mac-address-from-dts.patch
new file mode 100644
index 0000000000..5f427f6b8f
--- /dev/null
+++ b/package/kernel/mac80211/patches/ath10k/984-ath10k-Try-to-get-mac-address-from-dts.patch
@@ -0,0 +1,42 @@
+From 22fb5991a44c78ff18ec0082dc90c809356eb893 Mon Sep 17 00:00:00 2001
+From: Ansuel Smith <ansuelsmth@gmail.com>
+Date: Sun, 27 Sep 2020 19:23:35 +0200
+Subject: [PATCH 1/2] ath10k: Try to get mac-address from dts
+
+Most of embedded device that have the ath10k wifi integrated store the
+mac-address in nvmem partitions. Try to fetch the mac-address using the
+standard 'of_get_mac_address' than in all the check also try to fetch the
+address using the nvmem api searching for a defined 'mac-address' cell.
+Mac-address defined in the dts have priority than any other address found.
+
+Tested-on: QCA9984 hw1.0 PCI 10.4
+
+Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
+---
+ drivers/net/wireless/ath/ath10k/core.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
+index 5f4e12196..9ed7b9883 100644
+--- a/drivers/net/wireless/ath/ath10k/core.c
++++ b/drivers/net/wireless/ath/ath10k/core.c
+@@ -8,6 +8,7 @@
+ #include <linux/module.h>
+ #include <linux/firmware.h>
+ #include <linux/of.h>
++#include <linux/of_net.h>
+ #include <linux/property.h>
+ #include <linux/dmi.h>
+ #include <linux/ctype.h>
+@@ -3062,6 +3068,8 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
+ 
+ 	device_get_mac_address(ar->dev, ar->mac_addr, sizeof(ar->mac_addr));
+ 
++	of_get_mac_address(ar->dev->of_node, ar->mac_addr);
++
+ 	ret = ath10k_core_init_firmware_features(ar);
+ 	if (ret) {
+ 		ath10k_err(ar, "fatal problem with firmware features: %d\n",
+-- 
+2.27.0
+
diff --git a/package/kernel/mac80211/patches/ath/201-ath5k-WAR-for-AR71xx-PCI-bug.patch b/package/kernel/mac80211/patches/ath5k/201-ath5k-WAR-for-AR71xx-PCI-bug.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/201-ath5k-WAR-for-AR71xx-PCI-bug.patch
rename to package/kernel/mac80211/patches/ath5k/201-ath5k-WAR-for-AR71xx-PCI-bug.patch
diff --git a/package/kernel/mac80211/patches/ath/411-ath5k_allow_adhoc_and_ap.patch b/package/kernel/mac80211/patches/ath5k/411-ath5k_allow_adhoc_and_ap.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/411-ath5k_allow_adhoc_and_ap.patch
rename to package/kernel/mac80211/patches/ath5k/411-ath5k_allow_adhoc_and_ap.patch
diff --git a/package/kernel/mac80211/patches/ath/420-ath5k_disable_fast_cc.patch b/package/kernel/mac80211/patches/ath5k/420-ath5k_disable_fast_cc.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/420-ath5k_disable_fast_cc.patch
rename to package/kernel/mac80211/patches/ath5k/420-ath5k_disable_fast_cc.patch
diff --git a/package/kernel/mac80211/patches/ath/430-add_ath5k_platform.patch b/package/kernel/mac80211/patches/ath5k/430-add_ath5k_platform.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/430-add_ath5k_platform.patch
rename to package/kernel/mac80211/patches/ath5k/430-add_ath5k_platform.patch
diff --git a/package/kernel/mac80211/patches/ath/432-ath5k_add_pciids.patch b/package/kernel/mac80211/patches/ath5k/432-ath5k_add_pciids.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/432-ath5k_add_pciids.patch
rename to package/kernel/mac80211/patches/ath5k/432-ath5k_add_pciids.patch
diff --git a/package/kernel/mac80211/patches/ath/440-ath5k_channel_bw_debugfs.patch b/package/kernel/mac80211/patches/ath5k/440-ath5k_channel_bw_debugfs.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/440-ath5k_channel_bw_debugfs.patch
rename to package/kernel/mac80211/patches/ath5k/440-ath5k_channel_bw_debugfs.patch
diff --git a/package/kernel/mac80211/patches/ath/350-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch b/package/kernel/mac80211/patches/ath9k/350-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/350-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch
rename to package/kernel/mac80211/patches/ath9k/350-ath9k_hw-reset-AHB-WMAC-interface-on-AR91xx.patch
diff --git a/package/kernel/mac80211/patches/ath/351-ath9k_hw-issue-external-reset-for-QCA955x.patch b/package/kernel/mac80211/patches/ath9k/351-ath9k_hw-issue-external-reset-for-QCA955x.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/351-ath9k_hw-issue-external-reset-for-QCA955x.patch
rename to package/kernel/mac80211/patches/ath9k/351-ath9k_hw-issue-external-reset-for-QCA955x.patch
diff --git a/package/kernel/mac80211/patches/ath/354-ath9k-force-rx_clear-when-disabling-rx.patch b/package/kernel/mac80211/patches/ath9k/354-ath9k-force-rx_clear-when-disabling-rx.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/354-ath9k-force-rx_clear-when-disabling-rx.patch
rename to package/kernel/mac80211/patches/ath9k/354-ath9k-force-rx_clear-when-disabling-rx.patch
diff --git a/package/kernel/mac80211/patches/ath/356-Revert-ath9k-interpret-requested-txpower-in-EIRP-dom.patch b/package/kernel/mac80211/patches/ath9k/356-Revert-ath9k-interpret-requested-txpower-in-EIRP-dom.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/356-Revert-ath9k-interpret-requested-txpower-in-EIRP-dom.patch
rename to package/kernel/mac80211/patches/ath9k/356-Revert-ath9k-interpret-requested-txpower-in-EIRP-dom.patch
diff --git a/package/kernel/mac80211/patches/ath/365-ath9k-adjust-tx-power-reduction-for-US-regulatory-do.patch b/package/kernel/mac80211/patches/ath9k/365-ath9k-adjust-tx-power-reduction-for-US-regulatory-do.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/365-ath9k-adjust-tx-power-reduction-for-US-regulatory-do.patch
rename to package/kernel/mac80211/patches/ath9k/365-ath9k-adjust-tx-power-reduction-for-US-regulatory-do.patch
diff --git a/package/kernel/mac80211/patches/ath/401-ath9k_blink_default.patch b/package/kernel/mac80211/patches/ath9k/401-ath9k_blink_default.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/401-ath9k_blink_default.patch
rename to package/kernel/mac80211/patches/ath9k/401-ath9k_blink_default.patch
diff --git a/package/kernel/mac80211/patches/ath/410-ath9k_allow_adhoc_and_ap.patch b/package/kernel/mac80211/patches/ath9k/410-ath9k_allow_adhoc_and_ap.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/410-ath9k_allow_adhoc_and_ap.patch
rename to package/kernel/mac80211/patches/ath9k/410-ath9k_allow_adhoc_and_ap.patch
diff --git a/package/kernel/mac80211/patches/ath/450-ath9k-enabled-MFP-capability-unconditionally.patch b/package/kernel/mac80211/patches/ath9k/450-ath9k-enabled-MFP-capability-unconditionally.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/450-ath9k-enabled-MFP-capability-unconditionally.patch
rename to package/kernel/mac80211/patches/ath9k/450-ath9k-enabled-MFP-capability-unconditionally.patch
diff --git a/package/kernel/mac80211/patches/ath/500-ath9k_eeprom_debugfs.patch b/package/kernel/mac80211/patches/ath9k/500-ath9k_eeprom_debugfs.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/500-ath9k_eeprom_debugfs.patch
rename to package/kernel/mac80211/patches/ath9k/500-ath9k_eeprom_debugfs.patch
diff --git a/package/kernel/mac80211/patches/ath/501-ath9k_ahb_init.patch b/package/kernel/mac80211/patches/ath9k/501-ath9k_ahb_init.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/501-ath9k_ahb_init.patch
rename to package/kernel/mac80211/patches/ath9k/501-ath9k_ahb_init.patch
diff --git a/package/kernel/mac80211/patches/ath/510-ath9k_intr_mitigation_tweak.patch b/package/kernel/mac80211/patches/ath9k/510-ath9k_intr_mitigation_tweak.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/510-ath9k_intr_mitigation_tweak.patch
rename to package/kernel/mac80211/patches/ath9k/510-ath9k_intr_mitigation_tweak.patch
diff --git a/package/kernel/mac80211/patches/ath/511-ath9k_reduce_rxbuf.patch b/package/kernel/mac80211/patches/ath9k/511-ath9k_reduce_rxbuf.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/511-ath9k_reduce_rxbuf.patch
rename to package/kernel/mac80211/patches/ath9k/511-ath9k_reduce_rxbuf.patch
diff --git a/package/kernel/mac80211/patches/ath/512-ath9k_channelbw_debugfs.patch b/package/kernel/mac80211/patches/ath9k/512-ath9k_channelbw_debugfs.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/512-ath9k_channelbw_debugfs.patch
rename to package/kernel/mac80211/patches/ath9k/512-ath9k_channelbw_debugfs.patch
diff --git a/package/kernel/mac80211/patches/ath/513-ath9k_add_pci_ids.patch b/package/kernel/mac80211/patches/ath9k/513-ath9k_add_pci_ids.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/513-ath9k_add_pci_ids.patch
rename to package/kernel/mac80211/patches/ath9k/513-ath9k_add_pci_ids.patch
diff --git a/package/kernel/mac80211/patches/ath/530-ath9k_extra_leds.patch b/package/kernel/mac80211/patches/ath9k/530-ath9k_extra_leds.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/530-ath9k_extra_leds.patch
rename to package/kernel/mac80211/patches/ath9k/530-ath9k_extra_leds.patch
diff --git a/package/kernel/mac80211/patches/ath/531-ath9k_extra_platform_leds.patch b/package/kernel/mac80211/patches/ath9k/531-ath9k_extra_platform_leds.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/531-ath9k_extra_platform_leds.patch
rename to package/kernel/mac80211/patches/ath9k/531-ath9k_extra_platform_leds.patch
diff --git a/package/kernel/mac80211/patches/ath/540-ath9k_reduce_ani_interval.patch b/package/kernel/mac80211/patches/ath9k/540-ath9k_reduce_ani_interval.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/540-ath9k_reduce_ani_interval.patch
rename to package/kernel/mac80211/patches/ath9k/540-ath9k_reduce_ani_interval.patch
diff --git a/package/kernel/mac80211/patches/ath/542-ath9k_debugfs_diag.patch b/package/kernel/mac80211/patches/ath9k/542-ath9k_debugfs_diag.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/542-ath9k_debugfs_diag.patch
rename to package/kernel/mac80211/patches/ath9k/542-ath9k_debugfs_diag.patch
diff --git a/package/kernel/mac80211/patches/ath/543-ath9k_entropy_from_adc.patch b/package/kernel/mac80211/patches/ath9k/543-ath9k_entropy_from_adc.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/543-ath9k_entropy_from_adc.patch
rename to package/kernel/mac80211/patches/ath9k/543-ath9k_entropy_from_adc.patch
diff --git a/package/kernel/mac80211/patches/ath/544-ath9k-ar933x-usb-hang-workaround.patch b/package/kernel/mac80211/patches/ath9k/544-ath9k-ar933x-usb-hang-workaround.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/544-ath9k-ar933x-usb-hang-workaround.patch
rename to package/kernel/mac80211/patches/ath9k/544-ath9k-ar933x-usb-hang-workaround.patch
diff --git a/package/kernel/mac80211/patches/ath/545-ath9k_ani_ws_detect.patch b/package/kernel/mac80211/patches/ath9k/545-ath9k_ani_ws_detect.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/545-ath9k_ani_ws_detect.patch
rename to package/kernel/mac80211/patches/ath9k/545-ath9k_ani_ws_detect.patch
diff --git a/package/kernel/mac80211/patches/ath/547-ath9k_led_defstate_fix.patch b/package/kernel/mac80211/patches/ath9k/547-ath9k_led_defstate_fix.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/547-ath9k_led_defstate_fix.patch
rename to package/kernel/mac80211/patches/ath9k/547-ath9k_led_defstate_fix.patch
diff --git a/package/kernel/mac80211/patches/ath/548-ath9k_enable_gpio_chip.patch b/package/kernel/mac80211/patches/ath9k/548-ath9k_enable_gpio_chip.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/548-ath9k_enable_gpio_chip.patch
rename to package/kernel/mac80211/patches/ath9k/548-ath9k_enable_gpio_chip.patch
diff --git a/package/kernel/mac80211/patches/ath/549-ath9k_enable_gpio_buttons.patch b/package/kernel/mac80211/patches/ath9k/549-ath9k_enable_gpio_buttons.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/549-ath9k_enable_gpio_buttons.patch
rename to package/kernel/mac80211/patches/ath9k/549-ath9k_enable_gpio_buttons.patch
diff --git a/package/kernel/mac80211/patches/ath/550-ath9k-disable-bands-via-dt.patch b/package/kernel/mac80211/patches/ath9k/550-ath9k-disable-bands-via-dt.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/550-ath9k-disable-bands-via-dt.patch
rename to package/kernel/mac80211/patches/ath9k/550-ath9k-disable-bands-via-dt.patch
diff --git a/package/kernel/mac80211/patches/ath/551-ath9k_ubnt_uap_plus_hsr.patch b/package/kernel/mac80211/patches/ath9k/551-ath9k_ubnt_uap_plus_hsr.patch
similarity index 99%
rename from package/kernel/mac80211/patches/ath/551-ath9k_ubnt_uap_plus_hsr.patch
rename to package/kernel/mac80211/patches/ath9k/551-ath9k_ubnt_uap_plus_hsr.patch
index acb9ad443c..cd2bdbf1a0 100644
--- a/package/kernel/mac80211/patches/ath/551-ath9k_ubnt_uap_plus_hsr.patch
+++ b/package/kernel/mac80211/patches/ath9k/551-ath9k_ubnt_uap_plus_hsr.patch
@@ -371,7 +371,7 @@
  
 --- a/local-symbols
 +++ b/local-symbols
-@@ -112,6 +112,7 @@ ATH9K_WOW=
+@@ -113,6 +113,7 @@ ATH9K_WOW=
  ATH9K_RFKILL=
  ATH9K_CHANNEL_CONTEXT=
  ATH9K_PCOEM=
diff --git a/package/kernel/mac80211/patches/ath/552-ahb_of.patch b/package/kernel/mac80211/patches/ath9k/552-ath9k-ahb_of.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/552-ahb_of.patch
rename to package/kernel/mac80211/patches/ath9k/552-ath9k-ahb_of.patch
diff --git a/package/kernel/mac80211/patches/ath/553-ath9k_of_gpio_mask.patch b/package/kernel/mac80211/patches/ath9k/553-ath9k_of_gpio_mask.patch
similarity index 100%
rename from package/kernel/mac80211/patches/ath/553-ath9k_of_gpio_mask.patch
rename to package/kernel/mac80211/patches/ath9k/553-ath9k_of_gpio_mask.patch
diff --git a/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch b/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch
index e74d9a9aa0..1c52132da6 100644
--- a/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch
+++ b/package/kernel/mac80211/patches/rt2x00/602-rt2x00-introduce-rt2x00eeprom.patch
@@ -1,6 +1,6 @@
 --- a/local-symbols
 +++ b/local-symbols
-@@ -332,6 +332,7 @@ RT2X00_LIB_FIRMWARE=
+@@ -333,6 +333,7 @@ RT2X00_LIB_FIRMWARE=
  RT2X00_LIB_CRYPTO=
  RT2X00_LIB_LEDS=
  RT2X00_LIB_DEBUGFS=
diff --git a/package/kernel/mac80211/patches/rt2x00/607-rt2x00-add_platform_data_mac_addr.patch b/package/kernel/mac80211/patches/rt2x00/607-rt2x00-add_platform_data_mac_addr.patch
index b5b2c61037..79f99ffdf4 100644
--- a/package/kernel/mac80211/patches/rt2x00/607-rt2x00-add_platform_data_mac_addr.patch
+++ b/package/kernel/mac80211/patches/rt2x00/607-rt2x00-add_platform_data_mac_addr.patch
@@ -1,19 +1,18 @@
 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
 +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
-@@ -990,8 +990,13 @@ static void rt2x00lib_rate(struct ieee80
+@@ -990,6 +990,12 @@ static void rt2x00lib_rate(struct ieee80
  
  void rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr)
  {
 +	struct rt2x00_platform_data *pdata;
- 	const char *mac_addr;
- 
++
 +	pdata = rt2x00dev->dev->platform_data;
 +	if (pdata && pdata->mac_address)
 +		ether_addr_copy(eeprom_mac_addr, pdata->mac_address);
 +
- 	mac_addr = of_get_mac_address(rt2x00dev->dev->of_node);
- 	if (!IS_ERR(mac_addr))
- 		ether_addr_copy(eeprom_mac_addr, mac_addr);
+ 	of_get_mac_address(rt2x00dev->dev->of_node, eeprom_mac_addr);
+ 
+ 	if (!is_valid_ether_addr(eeprom_mac_addr)) {
 --- a/include/linux/rt2x00_platform.h
 +++ b/include/linux/rt2x00_platform.h
 @@ -14,6 +14,7 @@
diff --git a/package/kernel/mac80211/patches/subsys/100-remove-cryptoapi-dependencies.patch b/package/kernel/mac80211/patches/subsys/100-remove-cryptoapi-dependencies.patch
deleted file mode 100644
index ca02dfb06f..0000000000
--- a/package/kernel/mac80211/patches/subsys/100-remove-cryptoapi-dependencies.patch
+++ /dev/null
@@ -1,699 +0,0 @@
---- a/net/mac80211/Makefile
-+++ b/net/mac80211/Makefile
-@@ -7,7 +7,6 @@ mac80211-y := \
- 	driver-ops.o \
- 	sta_info.o \
- 	wep.o \
--	aead_api.o \
- 	wpa.o \
- 	scan.o offchannel.o \
- 	ht.o agg-tx.o agg-rx.o \
-@@ -19,8 +18,8 @@ mac80211-y := \
- 	rate.o \
- 	michael.o \
- 	tkip.o \
-+	aes_ccm.o \
- 	aes_cmac.o \
--	aes_gmac.o \
- 	fils_aead.o \
- 	cfg.o \
- 	ethtool.o \
---- a/net/mac80211/aead_api.c
-+++ /dev/null
-@@ -1,113 +0,0 @@
--// SPDX-License-Identifier: GPL-2.0-only
--/*
-- * Copyright 2003-2004, Instant802 Networks, Inc.
-- * Copyright 2005-2006, Devicescape Software, Inc.
-- * Copyright 2014-2015, Qualcomm Atheros, Inc.
-- *
-- * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
-- */
--
--#include <linux/kernel.h>
--#include <linux/types.h>
--#include <linux/err.h>
--#include <linux/scatterlist.h>
--#include <crypto/aead.h>
--
--#include "aead_api.h"
--
--int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len,
--		 u8 *data, size_t data_len, u8 *mic)
--{
--	size_t mic_len = crypto_aead_authsize(tfm);
--	struct scatterlist sg[3];
--	struct aead_request *aead_req;
--	int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
--	u8 *__aad;
--	int ret;
--
--	aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC);
--	if (!aead_req)
--		return -ENOMEM;
--
--	__aad = (u8 *)aead_req + reqsize;
--	memcpy(__aad, aad, aad_len);
--
--	sg_init_table(sg, 3);
--	sg_set_buf(&sg[0], __aad, aad_len);
--	sg_set_buf(&sg[1], data, data_len);
--	sg_set_buf(&sg[2], mic, mic_len);
--
--	aead_request_set_tfm(aead_req, tfm);
--	aead_request_set_crypt(aead_req, sg, sg, data_len, b_0);
--	aead_request_set_ad(aead_req, sg[0].length);
--
--	ret = crypto_aead_encrypt(aead_req);
--	kfree_sensitive(aead_req);
--
--	return ret;
--}
--
--int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len,
--		 u8 *data, size_t data_len, u8 *mic)
--{
--	size_t mic_len = crypto_aead_authsize(tfm);
--	struct scatterlist sg[3];
--	struct aead_request *aead_req;
--	int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
--	u8 *__aad;
--	int err;
--
--	if (data_len == 0)
--		return -EINVAL;
--
--	aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC);
--	if (!aead_req)
--		return -ENOMEM;
--
--	__aad = (u8 *)aead_req + reqsize;
--	memcpy(__aad, aad, aad_len);
--
--	sg_init_table(sg, 3);
--	sg_set_buf(&sg[0], __aad, aad_len);
--	sg_set_buf(&sg[1], data, data_len);
--	sg_set_buf(&sg[2], mic, mic_len);
--
--	aead_request_set_tfm(aead_req, tfm);
--	aead_request_set_crypt(aead_req, sg, sg, data_len + mic_len, b_0);
--	aead_request_set_ad(aead_req, sg[0].length);
--
--	err = crypto_aead_decrypt(aead_req);
--	kfree_sensitive(aead_req);
--
--	return err;
--}
--
--struct crypto_aead *
--aead_key_setup_encrypt(const char *alg, const u8 key[],
--		       size_t key_len, size_t mic_len)
--{
--	struct crypto_aead *tfm;
--	int err;
--
--	tfm = crypto_alloc_aead(alg, 0, CRYPTO_ALG_ASYNC);
--	if (IS_ERR(tfm))
--		return tfm;
--
--	err = crypto_aead_setkey(tfm, key, key_len);
--	if (err)
--		goto free_aead;
--	err = crypto_aead_setauthsize(tfm, mic_len);
--	if (err)
--		goto free_aead;
--
--	return tfm;
--
--free_aead:
--	crypto_free_aead(tfm);
--	return ERR_PTR(err);
--}
--
--void aead_key_free(struct crypto_aead *tfm)
--{
--	crypto_free_aead(tfm);
--}
---- a/net/mac80211/aead_api.h
-+++ /dev/null
-@@ -1,23 +0,0 @@
--/* SPDX-License-Identifier: GPL-2.0-only */
--
--#ifndef _AEAD_API_H
--#define _AEAD_API_H
--
--#include <crypto/aead.h>
--#include <linux/crypto.h>
--
--struct crypto_aead *
--aead_key_setup_encrypt(const char *alg, const u8 key[],
--		       size_t key_len, size_t mic_len);
--
--int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
--		 size_t aad_len, u8 *data,
--		 size_t data_len, u8 *mic);
--
--int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
--		 size_t aad_len, u8 *data,
--		 size_t data_len, u8 *mic);
--
--void aead_key_free(struct crypto_aead *tfm);
--
--#endif /* _AEAD_API_H */
---- a/net/mac80211/aes_ccm.h
-+++ b/net/mac80211/aes_ccm.h
-@@ -7,39 +7,17 @@
- #ifndef AES_CCM_H
- #define AES_CCM_H
- 
--#include "aead_api.h"
-+#include <linux/crypto.h>
- 
--#define CCM_AAD_LEN	32
--
--static inline struct crypto_aead *
--ieee80211_aes_key_setup_encrypt(const u8 key[], size_t key_len, size_t mic_len)
--{
--	return aead_key_setup_encrypt("ccm(aes)", key, key_len, mic_len);
--}
--
--static inline int
--ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm,
--			  u8 *b_0, u8 *aad, u8 *data,
--			  size_t data_len, u8 *mic)
--{
--	return aead_encrypt(tfm, b_0, aad + 2,
--			    be16_to_cpup((__be16 *)aad),
--			    data, data_len, mic);
--}
--
--static inline int
--ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm,
--			  u8 *b_0, u8 *aad, u8 *data,
--			  size_t data_len, u8 *mic)
--{
--	return aead_decrypt(tfm, b_0, aad + 2,
--			    be16_to_cpup((__be16 *)aad),
--			    data, data_len, mic);
--}
--
--static inline void ieee80211_aes_key_free(struct crypto_aead *tfm)
--{
--	return aead_key_free(tfm);
--}
-+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[],
-+						      size_t key_len,
-+						      size_t mic_len);
-+void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad,
-+			       u8 *data, size_t data_len, u8 *mic,
-+			       size_t mic_len);
-+int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad,
-+			      u8 *data, size_t data_len, u8 *mic,
-+			      size_t mic_len);
-+void ieee80211_aes_key_free(struct crypto_cipher *tfm);
- 
- #endif /* AES_CCM_H */
---- /dev/null
-+++ b/net/mac80211/aes_gcm.c
-@@ -0,0 +1,109 @@
-+/*
-+ * Copyright 2014-2015, Qualcomm Atheros, Inc.
-+ *
-+ * 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.
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/types.h>
-+#include <linux/err.h>
-+#include <crypto/aead.h>
-+
-+#include <net/mac80211.h>
-+#include "key.h"
-+#include "aes_gcm.h"
-+
-+int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
-+			      u8 *data, size_t data_len, u8 *mic)
-+{
-+	struct scatterlist sg[3];
-+	struct aead_request *aead_req;
-+	int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
-+	u8 *__aad;
-+
-+	aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC);
-+	if (!aead_req)
-+		return -ENOMEM;
-+
-+	__aad = (u8 *)aead_req + reqsize;
-+	memcpy(__aad, aad, GCM_AAD_LEN);
-+
-+	sg_init_table(sg, 3);
-+	sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad));
-+	sg_set_buf(&sg[1], data, data_len);
-+	sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN);
-+
-+	aead_request_set_tfm(aead_req, tfm);
-+	aead_request_set_crypt(aead_req, sg, sg, data_len, j_0);
-+	aead_request_set_ad(aead_req, sg[0].length);
-+
-+	crypto_aead_encrypt(aead_req);
-+	kzfree(aead_req);
-+	return 0;
-+}
-+
-+int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
-+			      u8 *data, size_t data_len, u8 *mic)
-+{
-+	struct scatterlist sg[3];
-+	struct aead_request *aead_req;
-+	int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
-+	u8 *__aad;
-+	int err;
-+
-+	if (data_len == 0)
-+		return -EINVAL;
-+
-+	aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC);
-+	if (!aead_req)
-+		return -ENOMEM;
-+
-+	__aad = (u8 *)aead_req + reqsize;
-+	memcpy(__aad, aad, GCM_AAD_LEN);
-+
-+	sg_init_table(sg, 3);
-+	sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad));
-+	sg_set_buf(&sg[1], data, data_len);
-+	sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN);
-+
-+	aead_request_set_tfm(aead_req, tfm);
-+	aead_request_set_crypt(aead_req, sg, sg,
-+			       data_len + IEEE80211_GCMP_MIC_LEN, j_0);
-+	aead_request_set_ad(aead_req, sg[0].length);
-+
-+	err = crypto_aead_decrypt(aead_req);
-+	kzfree(aead_req);
-+
-+	return err;
-+}
-+
-+struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
-+							size_t key_len)
-+{
-+	struct crypto_aead *tfm;
-+	int err;
-+
-+	tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
-+	if (IS_ERR(tfm))
-+		return tfm;
-+
-+	err = crypto_aead_setkey(tfm, key, key_len);
-+	if (err)
-+		goto free_aead;
-+	err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN);
-+	if (err)
-+		goto free_aead;
-+
-+	return tfm;
-+
-+free_aead:
-+	crypto_free_aead(tfm);
-+	return ERR_PTR(err);
-+}
-+
-+void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
-+{
-+	crypto_free_aead(tfm);
-+}
---- a/net/mac80211/aes_gcm.h
-+++ b/net/mac80211/aes_gcm.h
-@@ -6,38 +6,30 @@
- #ifndef AES_GCM_H
- #define AES_GCM_H
- 
--#include "aead_api.h"
-+#include <linux/crypto.h>
- 
--#define GCM_AAD_LEN	32
--
--static inline int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm,
--					    u8 *j_0, u8 *aad,  u8 *data,
--					    size_t data_len, u8 *mic)
-+static inline void
-+ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
-+			  u8 *data, size_t data_len, u8 *mic)
- {
--	return aead_encrypt(tfm, j_0, aad + 2,
--			    be16_to_cpup((__be16 *)aad),
--			    data, data_len, mic);
- }
- 
--static inline int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm,
--					    u8 *j_0, u8 *aad, u8 *data,
--					    size_t data_len, u8 *mic)
-+static inline int
-+ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
-+			  u8 *data, size_t data_len, u8 *mic)
- {
--	return aead_decrypt(tfm, j_0, aad + 2,
--			    be16_to_cpup((__be16 *)aad),
--			    data, data_len, mic);
-+    return -EOPNOTSUPP;
- }
- 
- static inline struct crypto_aead *
- ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], size_t key_len)
- {
--	return aead_key_setup_encrypt("gcm(aes)", key,
--				      key_len, IEEE80211_GCMP_MIC_LEN);
-+    return NULL;
- }
- 
--static inline void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
-+static inline void
-+ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
- {
--	return aead_key_free(tfm);
- }
- 
- #endif /* AES_GCM_H */
---- a/net/mac80211/wpa.c
-+++ b/net/mac80211/wpa.c
-@@ -312,7 +312,8 @@ ieee80211_crypto_tkip_decrypt(struct iee
- }
- 
- 
--static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad)
-+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
-+				u16 data_len)
- {
- 	__le16 mask_fc;
- 	int a4_included, mgmt;
-@@ -342,14 +343,8 @@ static void ccmp_special_blocks(struct s
- 	else
- 		qos_tid = 0;
- 
--	/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
--	 * mode authentication are not allowed to collide, yet both are derived
--	 * from this vector b_0. We only set L := 1 here to indicate that the
--	 * data size can be represented in (L+1) bytes. The CCM layer will take
--	 * care of storing the data length in the top (L+1) bytes and setting
--	 * and clearing the other bits as is required to derive the two IVs.
--	 */
--	b_0[0] = 0x1;
-+	/* First block, b_0 */
-+	b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
- 
- 	/* Nonce: Nonce Flags | A2 | PN
- 	 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
-@@ -357,6 +352,8 @@ static void ccmp_special_blocks(struct s
- 	b_0[1] = qos_tid | (mgmt << 4);
- 	memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
- 	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
-+	/* l(m) */
-+	put_unaligned_be16(data_len, &b_0[14]);
- 
- 	/* AAD (extra authenticate-only data) / masked 802.11 header
- 	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */
-@@ -413,7 +410,7 @@ static int ccmp_encrypt_skb(struct ieee8
- 	u8 *pos;
- 	u8 pn[6];
- 	u64 pn64;
--	u8 aad[CCM_AAD_LEN];
-+	u8 aad[2 * AES_BLOCK_SIZE];
- 	u8 b_0[AES_BLOCK_SIZE];
- 
- 	if (info->control.hw_key &&
-@@ -468,9 +465,11 @@ static int ccmp_encrypt_skb(struct ieee8
- 		return 0;
- 
- 	pos += IEEE80211_CCMP_HDR_LEN;
--	ccmp_special_blocks(skb, pn, b_0, aad);
--	return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
--					 skb_put(skb, mic_len));
-+	ccmp_special_blocks(skb, pn, b_0, aad, len);
-+	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
-+				  skb_put(skb, mic_len), mic_len);
-+
-+	return 0;
- }
- 
- 
-@@ -543,13 +542,13 @@ ieee80211_crypto_ccmp_decrypt(struct iee
- 			u8 aad[2 * AES_BLOCK_SIZE];
- 			u8 b_0[AES_BLOCK_SIZE];
- 			/* hardware didn't decrypt/verify MIC */
--			ccmp_special_blocks(skb, pn, b_0, aad);
-+			ccmp_special_blocks(skb, pn, b_0, aad, data_len);
- 
- 			if (ieee80211_aes_ccm_decrypt(
- 				    key->u.ccmp.tfm, b_0, aad,
- 				    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
- 				    data_len,
--				    skb->data + skb->len - mic_len))
-+				    skb->data + skb->len - mic_len, mic_len))
- 				return RX_DROP_UNUSABLE;
- 		}
- 
-@@ -646,7 +645,7 @@ static int gcmp_encrypt_skb(struct ieee8
- 	u8 *pos;
- 	u8 pn[6];
- 	u64 pn64;
--	u8 aad[GCM_AAD_LEN];
-+	u8 aad[2 * AES_BLOCK_SIZE];
- 	u8 j_0[AES_BLOCK_SIZE];
- 
- 	if (info->control.hw_key &&
-@@ -703,8 +702,10 @@ static int gcmp_encrypt_skb(struct ieee8
- 
- 	pos += IEEE80211_GCMP_HDR_LEN;
- 	gcmp_special_blocks(skb, pn, j_0, aad);
--	return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
--					 skb_put(skb, IEEE80211_GCMP_MIC_LEN));
-+	ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
-+				  skb_put(skb, IEEE80211_GCMP_MIC_LEN));
-+
-+	return 0;
- }
- 
- ieee80211_tx_result
-@@ -1133,9 +1134,9 @@ ieee80211_crypto_aes_gmac_encrypt(struct
- 	struct ieee80211_key *key = tx->key;
- 	struct ieee80211_mmie_16 *mmie;
- 	struct ieee80211_hdr *hdr;
--	u8 aad[GMAC_AAD_LEN];
-+	u8 aad[20];
- 	u64 pn64;
--	u8 nonce[GMAC_NONCE_LEN];
-+	u8 nonce[12];
- 
- 	if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
- 		return TX_DROP;
-@@ -1181,7 +1182,7 @@ ieee80211_crypto_aes_gmac_decrypt(struct
- 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- 	struct ieee80211_key *key = rx->key;
- 	struct ieee80211_mmie_16 *mmie;
--	u8 aad[GMAC_AAD_LEN], *mic, ipn[6], nonce[GMAC_NONCE_LEN];
-+	u8 aad[20], *mic, ipn[6], nonce[12];
- 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- 
- 	if (!ieee80211_is_mgmt(hdr->frame_control))
---- /dev/null
-+++ b/net/mac80211/aes_ccm.c
-@@ -0,0 +1,144 @@
-+/*
-+ * Copyright 2003-2004, Instant802 Networks, Inc.
-+ * Copyright 2005-2006, Devicescape Software, Inc.
-+ *
-+ * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
-+ *
-+ * 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.
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/types.h>
-+#include <linux/err.h>
-+#include <crypto/aead.h>
-+#include <crypto/aes.h>
-+
-+#include <net/mac80211.h>
-+#include "key.h"
-+#include "aes_ccm.h"
-+
-+static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, u8 *s_0,
-+			    u8 *a, u8 *b)
-+{
-+	int i;
-+
-+	crypto_cipher_encrypt_one(tfm, b, b_0);
-+
-+	/* Extra Authenticate-only data (always two AES blocks) */
-+	for (i = 0; i < AES_BLOCK_SIZE; i++)
-+		aad[i] ^= b[i];
-+	crypto_cipher_encrypt_one(tfm, b, aad);
-+
-+	aad += AES_BLOCK_SIZE;
-+
-+	for (i = 0; i < AES_BLOCK_SIZE; i++)
-+		aad[i] ^= b[i];
-+	crypto_cipher_encrypt_one(tfm, a, aad);
-+
-+	/* Mask out bits from auth-only-b_0 */
-+	b_0[0] &= 0x07;
-+
-+	/* S_0 is used to encrypt T (= MIC) */
-+	b_0[14] = 0;
-+	b_0[15] = 0;
-+	crypto_cipher_encrypt_one(tfm, s_0, b_0);
-+}
-+
-+
-+void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad,
-+			       u8 *data, size_t data_len, u8 *mic,
-+			       size_t mic_len)
-+{
-+	int i, j, last_len, num_blocks;
-+	u8 b[AES_BLOCK_SIZE];
-+	u8 s_0[AES_BLOCK_SIZE];
-+	u8 e[AES_BLOCK_SIZE];
-+	u8 *pos, *cpos;
-+
-+	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
-+	last_len = data_len % AES_BLOCK_SIZE;
-+	aes_ccm_prepare(tfm, b_0, aad, s_0, b, b);
-+
-+	/* Process payload blocks */
-+	pos = data;
-+	cpos = data;
-+	for (j = 1; j <= num_blocks; j++) {
-+		int blen = (j == num_blocks && last_len) ?
-+			last_len : AES_BLOCK_SIZE;
-+
-+		/* Authentication followed by encryption */
-+		for (i = 0; i < blen; i++)
-+			b[i] ^= pos[i];
-+		crypto_cipher_encrypt_one(tfm, b, b);
-+
-+		b_0[14] = (j >> 8) & 0xff;
-+		b_0[15] = j & 0xff;
-+		crypto_cipher_encrypt_one(tfm, e, b_0);
-+		for (i = 0; i < blen; i++)
-+			*cpos++ = *pos++ ^ e[i];
-+	}
-+
-+	for (i = 0; i < mic_len; i++)
-+		mic[i] = b[i] ^ s_0[i];
-+}
-+
-+int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad,
-+			      u8 *data, size_t data_len, u8 *mic,
-+			      size_t mic_len)
-+{
-+	int i, j, last_len, num_blocks;
-+	u8 *pos, *cpos;
-+	u8 a[AES_BLOCK_SIZE];
-+	u8 b[AES_BLOCK_SIZE];
-+	u8 s_0[AES_BLOCK_SIZE];
-+
-+	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
-+	last_len = data_len % AES_BLOCK_SIZE;
-+	aes_ccm_prepare(tfm, b_0, aad, s_0, a, b);
-+
-+	/* Process payload blocks */
-+	cpos = data;
-+	pos = data;
-+	for (j = 1; j <= num_blocks; j++) {
-+		int blen = (j == num_blocks && last_len) ?
-+			last_len : AES_BLOCK_SIZE;
-+
-+		/* Decryption followed by authentication */
-+		b_0[14] = (j >> 8) & 0xff;
-+		b_0[15] = j & 0xff;
-+		crypto_cipher_encrypt_one(tfm, b, b_0);
-+		for (i = 0; i < blen; i++) {
-+			*pos = *cpos++ ^ b[i];
-+			a[i] ^= *pos++;
-+		}
-+		crypto_cipher_encrypt_one(tfm, a, a);
-+	}
-+
-+	for (i = 0; i < mic_len; i++) {
-+		if ((mic[i] ^ s_0[i]) != a[i])
-+			return -1;
-+	}
-+
-+	return 0;
-+}
-+
-+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[],
-+						      size_t key_len,
-+						      size_t mic_len)
-+{
-+	struct crypto_cipher *tfm;
-+
-+	tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
-+	if (!IS_ERR(tfm))
-+		crypto_cipher_setkey(tfm, key, key_len);
-+
-+	return tfm;
-+}
-+
-+
-+void ieee80211_aes_key_free(struct crypto_cipher *tfm)
-+{
-+	crypto_free_cipher(tfm);
-+}
---- a/net/mac80211/Kconfig
-+++ b/net/mac80211/Kconfig
-@@ -6,8 +6,6 @@ config MAC80211
- 	depends on CRYPTO
- 	select BPAUTO_CRYPTO_LIB_ARC4
- 	depends on CRYPTO_AES
--	depends on CRYPTO_CCM
--	depends on CRYPTO_GCM
- 	depends on CRYPTO_CMAC
- 	depends on CRC32
- 	help
---- a/net/mac80211/aes_gmac.h
-+++ b/net/mac80211/aes_gmac.h
-@@ -12,10 +12,22 @@
- #define GMAC_MIC_LEN	16
- #define GMAC_NONCE_LEN	12
- 
--struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
--						 size_t key_len);
--int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
--		       const u8 *data, size_t data_len, u8 *mic);
--void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm);
-+static inline struct crypto_aead *
-+ieee80211_aes_gmac_key_setup(const u8 key[], size_t key_len)
-+{
-+	return NULL;
-+}
-+
-+static inline int
-+ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
-+		   const u8 *data, size_t data_len, u8 *mic)
-+{
-+	return -EOPNOTSUPP;
-+}
-+
-+static inline void
-+ieee80211_aes_gmac_key_free(struct crypto_aead *tfm)
-+{
-+}
- 
- #endif /* AES_GMAC_H */
---- a/net/mac80211/key.h
-+++ b/net/mac80211/key.h
-@@ -89,7 +89,7 @@ struct ieee80211_key {
- 			 * Management frames.
- 			 */
- 			u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
--			struct crypto_aead *tfm;
-+			struct crypto_cipher *tfm;
- 			u32 replays; /* dot11RSNAStatsCCMPReplays */
- 		} ccmp;
- 		struct {
diff --git a/package/kernel/mac80211/patches/subsys/130-disable-fils.patch b/package/kernel/mac80211/patches/subsys/130-disable-fils.patch
deleted file mode 100644
index 9c6e971f9d..0000000000
--- a/package/kernel/mac80211/patches/subsys/130-disable-fils.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-Disable FILS support, since it pulls in crypto hash support
-
---- a/net/mac80211/fils_aead.h
-+++ b/net/mac80211/fils_aead.h
-@@ -7,7 +7,7 @@
- #ifndef FILS_AEAD_H
- #define FILS_AEAD_H
- 
--#if LINUX_VERSION_IS_GEQ(4,3,0)
-+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */
- int fils_encrypt_assoc_req(struct sk_buff *skb,
- 			   struct ieee80211_mgd_assoc_data *assoc_data);
- int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata,
---- a/net/mac80211/fils_aead.c
-+++ b/net/mac80211/fils_aead.c
-@@ -1,4 +1,4 @@
--#if LINUX_VERSION_IS_GEQ(4,3,0)
-+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */
- // SPDX-License-Identifier: GPL-2.0-only
- /*
-  * FILS AEAD for (Re)Association Request/Response frames
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -591,7 +591,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
- 			   NL80211_FEATURE_MAC_ON_CREATE |
- 			   NL80211_FEATURE_USERSPACE_MPM |
- 			   NL80211_FEATURE_FULL_AP_CLIENT_STATE;
--#if LINUX_VERSION_IS_GEQ(4,3,0)
-+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */
- 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_STA);
- #endif
- 	wiphy_ext_feature_set(wiphy,
diff --git a/package/kernel/mac80211/patches/subsys/131-Revert-mac80211-aes-cmac-switch-to-shash-CMAC-driver.patch b/package/kernel/mac80211/patches/subsys/131-Revert-mac80211-aes-cmac-switch-to-shash-CMAC-driver.patch
deleted file mode 100644
index c3bf7ccc7a..0000000000
--- a/package/kernel/mac80211/patches/subsys/131-Revert-mac80211-aes-cmac-switch-to-shash-CMAC-driver.patch
+++ /dev/null
@@ -1,230 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sat, 7 Oct 2017 09:37:28 +0200
-Subject: [PATCH] Revert "mac80211: aes-cmac: switch to shash CMAC
- driver"
-
-This reverts commit 26717828b75dd5c46e97f7f4a9b937d038bb2852.
-Reduces mac80211 dependencies for LEDE
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/aes_cmac.c
-+++ b/net/mac80211/aes_cmac.c
-@@ -19,67 +19,151 @@
- #define CMAC_TLEN_256 16 /* CMAC TLen = 128 bits (16 octets) */
- #define AAD_LEN 20
- 
--static const u8 zero[CMAC_TLEN_256];
- 
--void ieee80211_aes_cmac(struct crypto_shash *tfm, const u8 *aad,
-+void gf_mulx(u8 *pad)
-+{
-+	int i, carry;
-+
-+	carry = pad[0] & 0x80;
-+	for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
-+		pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
-+	pad[AES_BLOCK_SIZE - 1] <<= 1;
-+	if (carry)
-+		pad[AES_BLOCK_SIZE - 1] ^= 0x87;
-+}
-+
-+void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,
-+		     const u8 *addr[], const size_t *len, u8 *mac,
-+		     size_t mac_len)
-+{
-+	u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
-+	const u8 *pos, *end;
-+	size_t i, e, left, total_len;
-+
-+	memset(cbc, 0, AES_BLOCK_SIZE);
-+
-+	total_len = 0;
-+	for (e = 0; e < num_elem; e++)
-+		total_len += len[e];
-+	left = total_len;
-+
-+	e = 0;
-+	pos = addr[0];
-+	end = pos + len[0];
-+
-+	while (left >= AES_BLOCK_SIZE) {
-+		for (i = 0; i < AES_BLOCK_SIZE; i++) {
-+			cbc[i] ^= *pos++;
-+			if (pos >= end) {
-+				e++;
-+				pos = addr[e];
-+				end = pos + len[e];
-+			}
-+		}
-+		if (left > AES_BLOCK_SIZE)
-+			crypto_cipher_encrypt_one(tfm, cbc, cbc);
-+		left -= AES_BLOCK_SIZE;
-+	}
-+
-+	memset(pad, 0, AES_BLOCK_SIZE);
-+	crypto_cipher_encrypt_one(tfm, pad, pad);
-+	gf_mulx(pad);
-+
-+	if (left || total_len == 0) {
-+		for (i = 0; i < left; i++) {
-+			cbc[i] ^= *pos++;
-+			if (pos >= end) {
-+				e++;
-+				pos = addr[e];
-+				end = pos + len[e];
-+			}
-+		}
-+		cbc[left] ^= 0x80;
-+		gf_mulx(pad);
-+	}
-+
-+	for (i = 0; i < AES_BLOCK_SIZE; i++)
-+		pad[i] ^= cbc[i];
-+	crypto_cipher_encrypt_one(tfm, pad, pad);
-+	memcpy(mac, pad, mac_len);
-+}
-+
-+
-+void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
- 			const u8 *data, size_t data_len, u8 *mic)
- {
--	SHASH_DESC_ON_STACK(desc, tfm);
--	u8 out[AES_BLOCK_SIZE];
-+	const u8 *addr[4];
-+	size_t len[4];
-+	u8 zero[CMAC_TLEN];
- 	const __le16 *fc;
- 
--	desc->tfm = tfm;
--
--	crypto_shash_init(desc);
--	crypto_shash_update(desc, aad, AAD_LEN);
-+	memset(zero, 0, CMAC_TLEN);
-+	addr[0] = aad;
-+	len[0] = AAD_LEN;
- 	fc = (const __le16 *)aad;
- 	if (ieee80211_is_beacon(*fc)) {
- 		/* mask Timestamp field to zero */
--		crypto_shash_update(desc, zero, 8);
--		crypto_shash_update(desc, data + 8, data_len - 8 - CMAC_TLEN);
-+		addr[1] = zero;
-+		len[1] = 8;
-+		addr[2] = data + 8;
-+		len[2] = data_len - 8 - CMAC_TLEN;
-+		addr[3] = zero;
-+		len[3] = CMAC_TLEN;
-+		aes_cmac_vector(tfm, 4, addr, len, mic, CMAC_TLEN);
- 	} else {
--		crypto_shash_update(desc, data, data_len - CMAC_TLEN);
-+		addr[1] = data;
-+		len[1] = data_len - CMAC_TLEN;
-+		addr[2] = zero;
-+		len[2] = CMAC_TLEN;
-+		aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN);
- 	}
--	crypto_shash_finup(desc, zero, CMAC_TLEN, out);
--
--	memcpy(mic, out, CMAC_TLEN);
- }
- 
--void ieee80211_aes_cmac_256(struct crypto_shash *tfm, const u8 *aad,
-+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad,
- 			    const u8 *data, size_t data_len, u8 *mic)
- {
--	SHASH_DESC_ON_STACK(desc, tfm);
-+	const u8 *addr[4];
-+	size_t len[4];
-+	u8 zero[CMAC_TLEN_256];
- 	const __le16 *fc;
- 
--	desc->tfm = tfm;
--
--	crypto_shash_init(desc);
--	crypto_shash_update(desc, aad, AAD_LEN);
-+	memset(zero, 0, CMAC_TLEN_256);
-+	addr[0] = aad;
-+	len[0] = AAD_LEN;
-+	addr[1] = data;
- 	fc = (const __le16 *)aad;
- 	if (ieee80211_is_beacon(*fc)) {
- 		/* mask Timestamp field to zero */
--		crypto_shash_update(desc, zero, 8);
--		crypto_shash_update(desc, data + 8,
--				    data_len - 8 - CMAC_TLEN_256);
-+		addr[1] = zero;
-+		len[1] = 8;
-+		addr[2] = data + 8;
-+		len[2] = data_len - 8 - CMAC_TLEN_256;
-+		addr[3] = zero;
-+		len[3] = CMAC_TLEN_256;
-+		aes_cmac_vector(tfm, 4, addr, len, mic, CMAC_TLEN_256);
- 	} else {
--		crypto_shash_update(desc, data, data_len - CMAC_TLEN_256);
-+		addr[1] = data;
-+		len[1] = data_len - CMAC_TLEN_256;
-+		addr[2] = zero;
-+		len[2] = CMAC_TLEN_256;
-+		aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN_256);
- 	}
--	crypto_shash_finup(desc, zero, CMAC_TLEN_256, mic);
- }
- 
--struct crypto_shash *ieee80211_aes_cmac_key_setup(const u8 key[],
--						  size_t key_len)
-+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[],
-+						   size_t key_len)
- {
--	struct crypto_shash *tfm;
-+	struct crypto_cipher *tfm;
- 
--	tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
-+	tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
- 	if (!IS_ERR(tfm))
--		crypto_shash_setkey(tfm, key, key_len);
-+		crypto_cipher_setkey(tfm, key, key_len);
- 
- 	return tfm;
- }
- 
--void ieee80211_aes_cmac_key_free(struct crypto_shash *tfm)
-+
-+void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm)
- {
--	crypto_free_shash(tfm);
-+	crypto_free_cipher(tfm);
- }
---- a/net/mac80211/aes_cmac.h
-+++ b/net/mac80211/aes_cmac.h
-@@ -7,14 +7,13 @@
- #define AES_CMAC_H
- 
- #include <linux/crypto.h>
--#include <crypto/hash.h>
- 
--struct crypto_shash *ieee80211_aes_cmac_key_setup(const u8 key[],
--						  size_t key_len);
--void ieee80211_aes_cmac(struct crypto_shash *tfm, const u8 *aad,
-+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[],
-+						   size_t key_len);
-+void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
- 			const u8 *data, size_t data_len, u8 *mic);
--void ieee80211_aes_cmac_256(struct crypto_shash *tfm, const u8 *aad,
-+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad,
- 			    const u8 *data, size_t data_len, u8 *mic);
--void ieee80211_aes_cmac_key_free(struct crypto_shash *tfm);
-+void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm);
- 
- #endif /* AES_CMAC_H */
---- a/net/mac80211/key.h
-+++ b/net/mac80211/key.h
-@@ -94,7 +94,7 @@ struct ieee80211_key {
- 		} ccmp;
- 		struct {
- 			u8 rx_pn[IEEE80211_CMAC_PN_LEN];
--			struct crypto_shash *tfm;
-+			struct crypto_cipher *tfm;
- 			u32 replays; /* dot11RSNAStatsCMACReplays */
- 			u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
- 		} aes_cmac;
diff --git a/package/kernel/mac80211/patches/subsys/132-mac80211-remove-cmac-dependency.patch b/package/kernel/mac80211/patches/subsys/132-mac80211-remove-cmac-dependency.patch
deleted file mode 100644
index df67d2f101..0000000000
--- a/package/kernel/mac80211/patches/subsys/132-mac80211-remove-cmac-dependency.patch
+++ /dev/null
@@ -1,10 +0,0 @@
---- a/net/mac80211/Kconfig
-+++ b/net/mac80211/Kconfig
-@@ -6,7 +6,6 @@ config MAC80211
- 	depends on CRYPTO
- 	select BPAUTO_CRYPTO_LIB_ARC4
- 	depends on CRYPTO_AES
--	depends on CRYPTO_CMAC
- 	depends on CRC32
- 	help
- 	  This option enables the hardware independent IEEE 802.11
diff --git a/package/kernel/mac80211/patches/subsys/320-mac80211_hwsim-add-6GHz-channels.patch b/package/kernel/mac80211/patches/subsys/320-mac80211_hwsim-add-6GHz-channels.patch
new file mode 100644
index 0000000000..a7c09f00bc
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/320-mac80211_hwsim-add-6GHz-channels.patch
@@ -0,0 +1,123 @@
+From: Ramon Fontes <ramonreisfontes@gmail.com>
+Date: Sun, 27 Dec 2020 00:11:55 -0300
+Subject: [PATCH] mac80211_hwsim: add 6GHz channels
+
+Advertise 6GHz channels to mac80211.
+
+Signed-off-by: Ramon Fontes <ramonreisfontes@gmail.com>
+Link: https://lore.kernel.org/r/20201227031155.81161-1-ramonreisfontes@gmail.com
+[reword commit message]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/drivers/net/wireless/mac80211_hwsim.c
++++ b/drivers/net/wireless/mac80211_hwsim.c
+@@ -311,6 +311,12 @@ static struct net_device *hwsim_mon; /*
+ 	.hw_value = (_freq), \
+ }
+ 
++#define CHAN6G(_freq) { \
++	.band = NL80211_BAND_6GHZ, \
++	.center_freq = (_freq), \
++	.hw_value = (_freq), \
++}
++
+ static const struct ieee80211_channel hwsim_channels_2ghz[] = {
+ 	CHAN2G(2412), /* Channel 1 */
+ 	CHAN2G(2417), /* Channel 2 */
+@@ -377,6 +383,68 @@ static const struct ieee80211_channel hw
+ 	CHAN5G(5925), /* Channel 185 */
+ };
+ 
++static const struct ieee80211_channel hwsim_channels_6ghz[] = {
++	CHAN6G(5955), /* Channel 1 */
++	CHAN6G(5975), /* Channel 5 */
++	CHAN6G(5995), /* Channel 9 */
++	CHAN6G(6015), /* Channel 13 */
++	CHAN6G(6035), /* Channel 17 */
++	CHAN6G(6055), /* Channel 21 */
++	CHAN6G(6075), /* Channel 25 */
++	CHAN6G(6095), /* Channel 29 */
++	CHAN6G(6115), /* Channel 33 */
++	CHAN6G(6135), /* Channel 37 */
++	CHAN6G(6155), /* Channel 41 */
++	CHAN6G(6175), /* Channel 45 */
++	CHAN6G(6195), /* Channel 49 */
++	CHAN6G(6215), /* Channel 53 */
++	CHAN6G(6235), /* Channel 57 */
++	CHAN6G(6255), /* Channel 61 */
++	CHAN6G(6275), /* Channel 65 */
++	CHAN6G(6295), /* Channel 69 */
++	CHAN6G(6315), /* Channel 73 */
++	CHAN6G(6335), /* Channel 77 */
++	CHAN6G(6355), /* Channel 81 */
++	CHAN6G(6375), /* Channel 85 */
++	CHAN6G(6395), /* Channel 89 */
++	CHAN6G(6415), /* Channel 93 */
++	CHAN6G(6435), /* Channel 97 */
++	CHAN6G(6455), /* Channel 181 */
++	CHAN6G(6475), /* Channel 105 */
++	CHAN6G(6495), /* Channel 109 */
++	CHAN6G(6515), /* Channel 113 */
++	CHAN6G(6535), /* Channel 117 */
++	CHAN6G(6555), /* Channel 121 */
++	CHAN6G(6575), /* Channel 125 */
++	CHAN6G(6595), /* Channel 129 */
++	CHAN6G(6615), /* Channel 133 */
++	CHAN6G(6635), /* Channel 137 */
++	CHAN6G(6655), /* Channel 141 */
++	CHAN6G(6675), /* Channel 145 */
++	CHAN6G(6695), /* Channel 149 */
++	CHAN6G(6715), /* Channel 153 */
++	CHAN6G(6735), /* Channel 157 */
++	CHAN6G(6755), /* Channel 161 */
++	CHAN6G(6775), /* Channel 165 */
++	CHAN6G(6795), /* Channel 169 */
++	CHAN6G(6815), /* Channel 173 */
++	CHAN6G(6835), /* Channel 177 */
++	CHAN6G(6855), /* Channel 181 */
++	CHAN6G(6875), /* Channel 185 */
++	CHAN6G(6895), /* Channel 189 */
++	CHAN6G(6915), /* Channel 193 */
++	CHAN6G(6935), /* Channel 197 */
++	CHAN6G(6955), /* Channel 201 */
++	CHAN6G(6975), /* Channel 205 */
++	CHAN6G(6995), /* Channel 209 */
++	CHAN6G(7015), /* Channel 213 */
++	CHAN6G(7035), /* Channel 217 */
++	CHAN6G(7055), /* Channel 221 */
++	CHAN6G(7075), /* Channel 225 */
++	CHAN6G(7095), /* Channel 229 */
++	CHAN6G(7115), /* Channel 233 */
++};
++
+ #define NUM_S1G_CHANS_US 51
+ static struct ieee80211_channel hwsim_channels_s1g[NUM_S1G_CHANS_US];
+ 
+@@ -548,6 +616,7 @@ struct mac80211_hwsim_data {
+ 	struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
+ 	struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)];
+ 	struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)];
++	struct ieee80211_channel channels_6ghz[ARRAY_SIZE(hwsim_channels_6ghz)];
+ 	struct ieee80211_channel channels_s1g[ARRAY_SIZE(hwsim_channels_s1g)];
+ 	struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)];
+ 	struct ieee80211_iface_combination if_combination;
+@@ -578,7 +647,8 @@ struct mac80211_hwsim_data {
+ 		struct ieee80211_channel *channel;
+ 		unsigned long next_start, start, end;
+ 	} survey_data[ARRAY_SIZE(hwsim_channels_2ghz) +
+-		      ARRAY_SIZE(hwsim_channels_5ghz)];
++		      ARRAY_SIZE(hwsim_channels_5ghz) +
++		      ARRAY_SIZE(hwsim_channels_6ghz)];
+ 
+ 	struct ieee80211_channel *channel;
+ 	u64 beacon_int	/* beacon interval in us */;
+@@ -3149,6 +3219,8 @@ static int mac80211_hwsim_new_radio(stru
+ 		sizeof(hwsim_channels_2ghz));
+ 	memcpy(data->channels_5ghz, hwsim_channels_5ghz,
+ 		sizeof(hwsim_channels_5ghz));
++	memcpy(data->channels_6ghz, hwsim_channels_6ghz,
++		sizeof(hwsim_channels_6ghz));
+ 	memcpy(data->channels_s1g, hwsim_channels_s1g,
+ 	       sizeof(hwsim_channels_s1g));
+ 	memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates));
diff --git a/package/kernel/mac80211/patches/subsys/321-mac80211_hwsim-make-6-GHz-channels-usable.patch b/package/kernel/mac80211/patches/subsys/321-mac80211_hwsim-make-6-GHz-channels-usable.patch
new file mode 100644
index 0000000000..4bac10eefe
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/321-mac80211_hwsim-make-6-GHz-channels-usable.patch
@@ -0,0 +1,74 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 24 May 2021 11:46:09 +0200
+Subject: [PATCH] mac80211_hwsim: make 6 GHz channels usable
+
+The previous commit that claimed to add 6 GHz channels didn't actually make
+them usable, since the 6 GHz band was not registered with mac80211.
+
+Fixes: 28881922abd7 ("mac80211_hwsim: add 6GHz channels")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/wireless/mac80211_hwsim.c
++++ b/drivers/net/wireless/mac80211_hwsim.c
+@@ -2968,15 +2968,19 @@ static void mac80211_hwsim_he_capab(stru
+ {
+ 	u16 n_iftype_data;
+ 
+-	if (sband->band == NL80211_BAND_2GHZ) {
++	switch (sband->band) {
++	case NL80211_BAND_2GHZ:
+ 		n_iftype_data = ARRAY_SIZE(he_capa_2ghz);
+ 		sband->iftype_data =
+ 			(struct ieee80211_sband_iftype_data *)he_capa_2ghz;
+-	} else if (sband->band == NL80211_BAND_5GHZ) {
++		break;
++	case NL80211_BAND_5GHZ:
++	case NL80211_BAND_6GHZ:
+ 		n_iftype_data = ARRAY_SIZE(he_capa_5ghz);
+ 		sband->iftype_data =
+ 			(struct ieee80211_sband_iftype_data *)he_capa_5ghz;
+-	} else {
++		break;
++	default:
+ 		return;
+ 	}
+ 
+@@ -3265,6 +3269,12 @@ static int mac80211_hwsim_new_radio(stru
+ 			sband->vht_cap.vht_mcs.tx_mcs_map =
+ 				sband->vht_cap.vht_mcs.rx_mcs_map;
+ 			break;
++		case NL80211_BAND_6GHZ:
++			sband->channels = data->channels_6ghz;
++			sband->n_channels = ARRAY_SIZE(hwsim_channels_6ghz);
++			sband->bitrates = data->rates + 4;
++			sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4;
++			break;
+ 		case NL80211_BAND_S1GHZ:
+ 			memcpy(&sband->s1g_cap, &hwsim_s1g_cap,
+ 			       sizeof(sband->s1g_cap));
+@@ -3275,6 +3285,13 @@ static int mac80211_hwsim_new_radio(stru
+ 			continue;
+ 		}
+ 
++		mac80211_hwsim_he_capab(sband);
++
++		hw->wiphy->bands[band] = sband;
++
++		if (band == NL80211_BAND_6GHZ)
++			continue;
++
+ 		sband->ht_cap.ht_supported = true;
+ 		sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ 				    IEEE80211_HT_CAP_GRN_FLD |
+@@ -3288,10 +3305,6 @@ static int mac80211_hwsim_new_radio(stru
+ 		sband->ht_cap.mcs.rx_mask[0] = 0xff;
+ 		sband->ht_cap.mcs.rx_mask[1] = 0xff;
+ 		sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+-
+-		mac80211_hwsim_he_capab(sband);
+-
+-		hw->wiphy->bands[band] = sband;
+ 	}
+ 
+ 	/* By default all radios belong to the first group */
diff --git a/package/kernel/mac80211/patches/subsys/339-mac80211-remove-legacy-minstrel-rate-control.patch b/package/kernel/mac80211/patches/subsys/339-mac80211-remove-legacy-minstrel-rate-control.patch
index 96ee595ac1..1cab2eb194 100644
--- a/package/kernel/mac80211/patches/subsys/339-mac80211-remove-legacy-minstrel-rate-control.patch
+++ b/package/kernel/mac80211/patches/subsys/339-mac80211-remove-legacy-minstrel-rate-control.patch
@@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
 
 --- a/net/mac80211/Makefile
 +++ b/net/mac80211/Makefile
-@@ -55,11 +55,9 @@ mac80211-$(CONFIG_PM) += pm.o
+@@ -56,11 +56,9 @@ mac80211-$(CONFIG_PM) += pm.o
  CFLAGS_trace.o := -I$(src)
  
  rc80211_minstrel-y := \
diff --git a/package/kernel/mac80211/patches/subsys/376-mac80211-add-rate-control-support-for-encap-offload.patch b/package/kernel/mac80211/patches/subsys/376-mac80211-add-rate-control-support-for-encap-offload.patch
index 43a4a1334d..f22b0d9849 100644
--- a/package/kernel/mac80211/patches/subsys/376-mac80211-add-rate-control-support-for-encap-offload.patch
+++ b/package/kernel/mac80211/patches/subsys/376-mac80211-add-rate-control-support-for-encap-offload.patch
@@ -7,24 +7,6 @@ The software rate control cannot deal with encap offload, so fix it.
 Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
 ---
 
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -2024,6 +2024,15 @@ static inline void ieee80211_tx_skb(stru
- 	ieee80211_tx_skb_tid(sdata, skb, 7);
- }
- 
-+static inline bool ieee80211_is_tx_data(struct sk_buff *skb)
-+{
-+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-+
-+	return info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP ||
-+	       ieee80211_is_data(hdr->frame_control);
-+}
-+
- u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
- 			       struct ieee802_11_elems *elems,
- 			       u64 filter, u32 crc, u8 *transmitter_bssid,
 --- a/net/mac80211/rate.c
 +++ b/net/mac80211/rate.c
 @@ -297,15 +297,11 @@ void ieee80211_check_rate_mask(struct ie
@@ -117,3 +99,28 @@ Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
  
  	if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
  		struct sta_info *sta = container_of(txq->sta, struct sta_info,
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -6728,4 +6728,22 @@ struct sk_buff *ieee80211_get_fils_disco
+ struct sk_buff *
+ ieee80211_get_unsol_bcast_probe_resp_tmpl(struct ieee80211_hw *hw,
+ 					  struct ieee80211_vif *vif);
++
++/**
++ * ieee80211_is_tx_data - check if frame is a data frame
++ *
++ * The function is used to check if a frame is a data frame. Frames with
++ * hardware encapsulation enabled are data frames.
++ *
++ * @skb: the frame to be transmitted.
++ */
++static inline bool ieee80211_is_tx_data(struct sk_buff *skb)
++{
++	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++	struct ieee80211_hdr *hdr = (void *) skb->data;
++
++	return info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP ||
++	       ieee80211_is_data(hdr->frame_control);
++}
++
+ #endif /* MAC80211_H */
diff --git a/package/kernel/mac80211/patches/subsys/380-mac80211-introduce-aql_enable-node-in-debugfs.patch b/package/kernel/mac80211/patches/subsys/380-mac80211-introduce-aql_enable-node-in-debugfs.patch
new file mode 100644
index 0000000000..b21b671c10
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/380-mac80211-introduce-aql_enable-node-in-debugfs.patch
@@ -0,0 +1,111 @@
+From: Lorenzo Bianconi <lorenzo@kernel.org>
+Date: Sat, 9 Jan 2021 18:57:51 +0100
+Subject: [PATCH] mac80211: introduce aql_enable node in debugfs
+
+Introduce aql_enable node in debugfs in order to enable/disable aql.
+This is useful for debugging purpose.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
+Link: https://lore.kernel.org/r/e7a934d5d84e4796c4f97ea5de4e66c824296b07.1610214851.git.lorenzo@kernel.org
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -281,6 +281,56 @@ static const struct file_operations aql_
+ 	.llseek = default_llseek,
+ };
+ 
++static ssize_t aql_enable_read(struct file *file, char __user *user_buf,
++			       size_t count, loff_t *ppos)
++{
++	char buf[3];
++	int len;
++
++	len = scnprintf(buf, sizeof(buf), "%d\n",
++			!static_key_false(&aql_disable.key));
++
++	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
++}
++
++static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
++				size_t count, loff_t *ppos)
++{
++	bool aql_disabled = static_key_false(&aql_disable.key);
++	char buf[3];
++	size_t len;
++
++	if (count > sizeof(buf))
++		return -EINVAL;
++
++	if (copy_from_user(buf, user_buf, count))
++		return -EFAULT;
++
++	buf[sizeof(buf) - 1] = '\0';
++	len = strlen(buf);
++	if (len > 0 && buf[len - 1] == '\n')
++		buf[len - 1] = 0;
++
++	if (buf[0] == '0' && buf[1] == '\0') {
++		if (!aql_disabled)
++			static_branch_inc(&aql_disable);
++	} else if (buf[0] == '1' && buf[1] == '\0') {
++		if (aql_disabled)
++			static_branch_dec(&aql_disable);
++	} else {
++		return -EINVAL;
++	}
++
++	return count;
++}
++
++static const struct file_operations aql_enable_ops = {
++	.write = aql_enable_write,
++	.read = aql_enable_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
+ static ssize_t force_tx_status_read(struct file *file,
+ 				    char __user *user_buf,
+ 				    size_t count,
+@@ -569,6 +619,7 @@ void debugfs_hw_add(struct ieee80211_loc
+ 	DEBUGFS_ADD(power);
+ 	DEBUGFS_ADD(hw_conf);
+ 	DEBUGFS_ADD_MODE(force_tx_status, 0600);
++	DEBUGFS_ADD_MODE(aql_enable, 0600);
+ 
+ 	if (local->ops->wake_tx_queue)
+ 		DEBUGFS_ADD_MODE(aqm, 0600);
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1140,6 +1140,8 @@ enum mac80211_scan_state {
+ 	SCAN_ABORT,
+ };
+ 
++DECLARE_STATIC_KEY_FALSE(aql_disable);
++
+ struct ieee80211_local {
+ 	/* embed the driver visible part.
+ 	 * don't cast (use the static inlines below), but we keep
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3887,6 +3887,8 @@ void __ieee80211_schedule_txq(struct iee
+ }
+ EXPORT_SYMBOL(__ieee80211_schedule_txq);
+ 
++DEFINE_STATIC_KEY_FALSE(aql_disable);
++
+ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
+ 				 struct ieee80211_txq *txq)
+ {
+@@ -3896,6 +3898,9 @@ bool ieee80211_txq_airtime_check(struct
+ 	if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
+ 		return true;
+ 
++	if (static_branch_unlikely(&aql_disable))
++		return true;
++
+ 	if (!txq->sta)
+ 		return true;
+ 
diff --git a/package/kernel/mac80211/patches/subsys/381-mac80211-rearrange-struct-txq_info-for-fewer-holes.patch b/package/kernel/mac80211/patches/subsys/381-mac80211-rearrange-struct-txq_info-for-fewer-holes.patch
new file mode 100644
index 0000000000..708ad6f460
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/381-mac80211-rearrange-struct-txq_info-for-fewer-holes.patch
@@ -0,0 +1,39 @@
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Fri, 18 Jun 2021 13:41:44 +0300
+Subject: [PATCH] mac80211: rearrange struct txq_info for fewer holes
+
+We can slightly decrease the size of struct txq_info by
+rearranging some fields for fewer holes, so do that.
+
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
+Link: https://lore.kernel.org/r/iwlwifi.20210618133832.1bf019a1fe2e.Ib54622b8d6dc1a9a7dc484e573c073119450538b@changeid
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -5,7 +5,7 @@
+  * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
+  * Copyright 2007-2010	Johannes Berg <johannes@sipsolutions.net>
+  * Copyright 2013-2015  Intel Mobile Communications GmbH
+- * Copyright (C) 2018-2020 Intel Corporation
++ * Copyright (C) 2018-2021 Intel Corporation
+  */
+ 
+ #ifndef IEEE80211_I_H
+@@ -848,9 +848,12 @@ struct txq_info {
+ 	struct fq_tin tin;
+ 	struct codel_vars def_cvars;
+ 	struct codel_stats cstats;
+-	struct sk_buff_head frags;
+-	struct list_head schedule_order;
++
+ 	u16 schedule_round;
++	struct list_head schedule_order;
++
++	struct sk_buff_head frags;
++
+ 	unsigned long flags;
+ 
+ 	/* keep last! */
diff --git a/package/kernel/mac80211/patches/subsys/382-mac80211-Switch-to-a-virtual-time-based-airtime-sche.patch b/package/kernel/mac80211/patches/subsys/382-mac80211-Switch-to-a-virtual-time-based-airtime-sche.patch
new file mode 100644
index 0000000000..ba78f7a142
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/382-mac80211-Switch-to-a-virtual-time-based-airtime-sche.patch
@@ -0,0 +1,1277 @@
+From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@redhat.com>
+Date: Wed, 23 Jun 2021 15:47:55 +0200
+Subject: [PATCH] mac80211: Switch to a virtual time-based airtime scheduler
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This switches the airtime scheduler in mac80211 to use a virtual
+time-based scheduler instead of the round-robin scheduler used before.
+This has a couple of advantages:
+
+- No need to sync up the round-robin scheduler in firmware/hardware with
+  the round-robin airtime scheduler.
+
+- If several stations are eligible for transmission we can schedule both
+  of them; no need to hard-block the scheduling rotation until the head
+  of the queue has used up its quantum.
+
+- The check of whether a station is eligible for transmission becomes
+  simpler (in ieee80211_txq_may_transmit()).
+
+The drawback is that scheduling becomes slightly more expensive, as we
+need to maintain an rbtree of TXQs sorted by virtual time. This means
+that ieee80211_register_airtime() becomes O(logN) in the number of
+currently scheduled TXQs because it can change the order of the
+scheduled stations. We mitigate this overhead by only resorting when a
+station changes position in the tree, and hopefully N rarely grows too
+big (it's only TXQs currently backlogged, not all associated stations),
+so it shouldn't be too big of an issue.
+
+To prevent divisions in the fast path, we maintain both station sums and
+pre-computed reciprocals of the sums. This turns the fast-path operation
+into a multiplication, with divisions only happening as the number of
+active stations change (to re-compute the current sum of all active
+station weights). To prevent this re-computation of the reciprocal from
+happening too frequently, we use a time-based notion of station
+activity, instead of updating the weight every time a station gets
+scheduled or de-scheduled. As queues can oscillate between empty and
+occupied quite frequently, this can significantly cut down on the number
+of re-computations. It also has the added benefit of making the station
+airtime calculation independent on whether the queue happened to have
+drained at the time an airtime value was accounted.
+
+Co-developed-by: Yibo Zhao <yiboz@codeaurora.org>
+Signed-off-by: Yibo Zhao <yiboz@codeaurora.org>
+Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
+Link: https://lore.kernel.org/r/20210623134755.235545-1-toke@redhat.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -6552,9 +6552,6 @@ static inline void ieee80211_txq_schedul
+ {
+ }
+ 
+-void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
+-			      struct ieee80211_txq *txq, bool force);
+-
+ /**
+  * ieee80211_schedule_txq - schedule a TXQ for transmission
+  *
+@@ -6567,11 +6564,7 @@ void __ieee80211_schedule_txq(struct iee
+  * The driver may call this function if it has buffered packets for
+  * this TXQ internally.
+  */
+-static inline void
+-ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
+-{
+-	__ieee80211_schedule_txq(hw, txq, true);
+-}
++void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
+ 
+ /**
+  * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
+@@ -6583,12 +6576,8 @@ ieee80211_schedule_txq(struct ieee80211_
+  * The driver may set force=true if it has buffered packets for this TXQ
+  * internally.
+  */
+-static inline void
+-ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
+-		     bool force)
+-{
+-	__ieee80211_schedule_txq(hw, txq, force);
+-}
++void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
++			  bool force);
+ 
+ /**
+  * ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1442,6 +1442,38 @@ static void sta_apply_mesh_params(struct
+ #endif
+ }
+ 
++static void sta_apply_airtime_params(struct ieee80211_local *local,
++				     struct sta_info *sta,
++				     struct station_parameters *params)
++{
++	u8 ac;
++
++	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
++		struct airtime_sched_info *air_sched = &local->airtime[ac];
++		struct airtime_info *air_info = &sta->airtime[ac];
++		struct txq_info *txqi;
++		u8 tid;
++
++		spin_lock_bh(&air_sched->lock);
++		for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
++			if (air_info->weight == params->airtime_weight ||
++			    !sta->sta.txq[tid] ||
++			    ac != ieee80211_ac_from_tid(tid))
++				continue;
++
++			airtime_weight_set(air_info, params->airtime_weight);
++
++			txqi = to_txq_info(sta->sta.txq[tid]);
++			if (RB_EMPTY_NODE(&txqi->schedule_order))
++				continue;
++
++			ieee80211_update_airtime_weight(local, air_sched,
++							0, true);
++		}
++		spin_unlock_bh(&air_sched->lock);
++	}
++}
++
+ static int sta_apply_parameters(struct ieee80211_local *local,
+ 				struct sta_info *sta,
+ 				struct station_parameters *params)
+@@ -1629,7 +1661,8 @@ static int sta_apply_parameters(struct i
+ 		sta_apply_mesh_params(local, sta, params);
+ 
+ 	if (params->airtime_weight)
+-		sta->airtime_weight = params->airtime_weight;
++		sta_apply_airtime_params(local, sta, params);
++
+ 
+ 	/* set the STA state after all sta info from usermode has been set */
+ 	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -216,14 +216,14 @@ static ssize_t aql_txq_limit_read(struct
+ 			"VI	%u		%u\n"
+ 			"BE	%u		%u\n"
+ 			"BK	%u		%u\n",
+-			local->aql_txq_limit_low[IEEE80211_AC_VO],
+-			local->aql_txq_limit_high[IEEE80211_AC_VO],
+-			local->aql_txq_limit_low[IEEE80211_AC_VI],
+-			local->aql_txq_limit_high[IEEE80211_AC_VI],
+-			local->aql_txq_limit_low[IEEE80211_AC_BE],
+-			local->aql_txq_limit_high[IEEE80211_AC_BE],
+-			local->aql_txq_limit_low[IEEE80211_AC_BK],
+-			local->aql_txq_limit_high[IEEE80211_AC_BK]);
++			local->airtime[IEEE80211_AC_VO].aql_txq_limit_low,
++			local->airtime[IEEE80211_AC_VO].aql_txq_limit_high,
++			local->airtime[IEEE80211_AC_VI].aql_txq_limit_low,
++			local->airtime[IEEE80211_AC_VI].aql_txq_limit_high,
++			local->airtime[IEEE80211_AC_BE].aql_txq_limit_low,
++			local->airtime[IEEE80211_AC_BE].aql_txq_limit_high,
++			local->airtime[IEEE80211_AC_BK].aql_txq_limit_low,
++			local->airtime[IEEE80211_AC_BK].aql_txq_limit_high);
+ 	return simple_read_from_buffer(user_buf, count, ppos,
+ 				       buf, len);
+ }
+@@ -255,11 +255,11 @@ static ssize_t aql_txq_limit_write(struc
+ 	if (ac >= IEEE80211_NUM_ACS)
+ 		return -EINVAL;
+ 
+-	q_limit_low_old = local->aql_txq_limit_low[ac];
+-	q_limit_high_old = local->aql_txq_limit_high[ac];
++	q_limit_low_old = local->airtime[ac].aql_txq_limit_low;
++	q_limit_high_old = local->airtime[ac].aql_txq_limit_high;
+ 
+-	local->aql_txq_limit_low[ac] = q_limit_low;
+-	local->aql_txq_limit_high[ac] = q_limit_high;
++	local->airtime[ac].aql_txq_limit_low = q_limit_low;
++	local->airtime[ac].aql_txq_limit_high = q_limit_high;
+ 
+ 	mutex_lock(&local->sta_mtx);
+ 	list_for_each_entry(sta, &local->sta_list, list) {
+@@ -382,6 +382,46 @@ static const struct file_operations forc
+ 	.llseek = default_llseek,
+ };
+ 
++static ssize_t airtime_read(struct file *file,
++			    char __user *user_buf,
++			    size_t count,
++			    loff_t *ppos)
++{
++	struct ieee80211_local *local = file->private_data;
++	char buf[200];
++	u64 v_t[IEEE80211_NUM_ACS];
++	u64 wt[IEEE80211_NUM_ACS];
++	int len = 0, ac;
++
++	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
++		spin_lock_bh(&local->airtime[ac].lock);
++		v_t[ac] = local->airtime[ac].v_t;
++		wt[ac] = local->airtime[ac].weight_sum;
++		spin_unlock_bh(&local->airtime[ac].lock);
++	}
++	len = scnprintf(buf, sizeof(buf),
++			"\tVO         VI         BE         BK\n"
++			"Virt-t\t%-10llu %-10llu %-10llu %-10llu\n"
++			"Weight\t%-10llu %-10llu %-10llu %-10llu\n",
++			v_t[0],
++			v_t[1],
++			v_t[2],
++			v_t[3],
++			wt[0],
++			wt[1],
++			wt[2],
++			wt[3]);
++
++	return simple_read_from_buffer(user_buf, count, ppos,
++				       buf, len);
++}
++
++static const struct file_operations airtime_ops = {
++	.read = airtime_read,
++	.open = simple_open,
++	.llseek = default_llseek,
++};
++
+ #ifdef CONFIG_PM
+ static ssize_t reset_write(struct file *file, const char __user *user_buf,
+ 			   size_t count, loff_t *ppos)
+@@ -624,7 +664,11 @@ void debugfs_hw_add(struct ieee80211_loc
+ 	if (local->ops->wake_tx_queue)
+ 		DEBUGFS_ADD_MODE(aqm, 0600);
+ 
+-	DEBUGFS_ADD_MODE(airtime_flags, 0600);
++	if (wiphy_ext_feature_isset(local->hw.wiphy,
++				    NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) {
++		DEBUGFS_ADD_MODE(airtime, 0600);
++		DEBUGFS_ADD_MODE(airtime_flags, 0600);
++	}
+ 
+ 	DEBUGFS_ADD(aql_txq_limit);
+ 	debugfs_create_u32("aql_threshold", 0600,
+--- a/net/mac80211/debugfs_netdev.c
++++ b/net/mac80211/debugfs_netdev.c
+@@ -513,6 +513,34 @@ static ssize_t ieee80211_if_fmt_aqm(
+ }
+ IEEE80211_IF_FILE_R(aqm);
+ 
++static ssize_t ieee80211_if_fmt_airtime(
++	const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
++{
++	struct ieee80211_local *local = sdata->local;
++	struct ieee80211_txq *txq = sdata->vif.txq;
++	struct airtime_info *air_info;
++	int len;
++
++	if (!txq)
++		return 0;
++
++	spin_lock_bh(&local->airtime[txq->ac].lock);
++	air_info = to_airtime_info(txq);
++	len = scnprintf(buf,
++			buflen,
++			"RX: %llu us\nTX: %llu us\nWeight: %u\n"
++			"Virt-T: %lld us\n",
++			air_info->rx_airtime,
++			air_info->tx_airtime,
++			air_info->weight,
++			air_info->v_t);
++	spin_unlock_bh(&local->airtime[txq->ac].lock);
++
++	return len;
++}
++
++IEEE80211_IF_FILE_R(airtime);
++
+ IEEE80211_IF_FILE(multicast_to_unicast, u.ap.multicast_to_unicast, HEX);
+ 
+ /* IBSS attributes */
+@@ -661,8 +689,10 @@ static void add_common_files(struct ieee
+ 
+ 	if (sdata->local->ops->wake_tx_queue &&
+ 	    sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
+-	    sdata->vif.type != NL80211_IFTYPE_NAN)
++	    sdata->vif.type != NL80211_IFTYPE_NAN) {
+ 		DEBUGFS_ADD(aqm);
++		DEBUGFS_ADD(airtime);
++	}
+ }
+ 
+ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
+--- a/net/mac80211/debugfs_sta.c
++++ b/net/mac80211/debugfs_sta.c
+@@ -202,7 +202,7 @@ static ssize_t sta_airtime_read(struct f
+ 	size_t bufsz = 400;
+ 	char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
+ 	u64 rx_airtime = 0, tx_airtime = 0;
+-	s64 deficit[IEEE80211_NUM_ACS];
++	u64 v_t[IEEE80211_NUM_ACS];
+ 	ssize_t rv;
+ 	int ac;
+ 
+@@ -210,18 +210,18 @@ static ssize_t sta_airtime_read(struct f
+ 		return -ENOMEM;
+ 
+ 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+-		spin_lock_bh(&local->active_txq_lock[ac]);
++		spin_lock_bh(&local->airtime[ac].lock);
+ 		rx_airtime += sta->airtime[ac].rx_airtime;
+ 		tx_airtime += sta->airtime[ac].tx_airtime;
+-		deficit[ac] = sta->airtime[ac].deficit;
+-		spin_unlock_bh(&local->active_txq_lock[ac]);
++		v_t[ac] = sta->airtime[ac].v_t;
++		spin_unlock_bh(&local->airtime[ac].lock);
+ 	}
+ 
+ 	p += scnprintf(p, bufsz + buf - p,
+ 		"RX: %llu us\nTX: %llu us\nWeight: %u\n"
+-		"Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
+-		rx_airtime, tx_airtime, sta->airtime_weight,
+-		deficit[0], deficit[1], deficit[2], deficit[3]);
++		"Virt-T: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
++		rx_airtime, tx_airtime, sta->airtime[0].weight,
++		v_t[0], v_t[1], v_t[2], v_t[3]);
+ 
+ 	rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+ 	kfree(buf);
+@@ -236,11 +236,11 @@ static ssize_t sta_airtime_write(struct
+ 	int ac;
+ 
+ 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+-		spin_lock_bh(&local->active_txq_lock[ac]);
++		spin_lock_bh(&local->airtime[ac].lock);
+ 		sta->airtime[ac].rx_airtime = 0;
+ 		sta->airtime[ac].tx_airtime = 0;
+-		sta->airtime[ac].deficit = sta->airtime_weight;
+-		spin_unlock_bh(&local->active_txq_lock[ac]);
++		sta->airtime[ac].v_t = 0;
++		spin_unlock_bh(&local->airtime[ac].lock);
+ 	}
+ 
+ 	return count;
+@@ -263,10 +263,10 @@ static ssize_t sta_aql_read(struct file
+ 		return -ENOMEM;
+ 
+ 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+-		spin_lock_bh(&local->active_txq_lock[ac]);
++		spin_lock_bh(&local->airtime[ac].lock);
+ 		q_limit_l[ac] = sta->airtime[ac].aql_limit_low;
+ 		q_limit_h[ac] = sta->airtime[ac].aql_limit_high;
+-		spin_unlock_bh(&local->active_txq_lock[ac]);
++		spin_unlock_bh(&local->airtime[ac].lock);
+ 		q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);
+ 	}
+ 
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -840,20 +840,16 @@ 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
+  * @schedule_order: used with ieee80211_local->active_txqs
+- * @schedule_round: counter to prevent infinite loops on TXQ scheduling
++ * @frags: used to keep fragments created after dequeue
+  */
+ struct txq_info {
+ 	struct fq_tin tin;
+ 	struct codel_vars def_cvars;
+ 	struct codel_stats cstats;
+-
+-	u16 schedule_round;
+-	struct list_head schedule_order;
++	struct rb_node schedule_order;
+ 
+ 	struct sk_buff_head frags;
+-
+ 	unsigned long flags;
+ 
+ 	/* keep last! */
+@@ -930,6 +926,8 @@ struct ieee80211_sub_if_data {
+ 	struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
+ 	struct mac80211_qos_map __rcu *qos_map;
+ 
++	struct airtime_info airtime[IEEE80211_NUM_ACS];
++
+ 	struct work_struct csa_finalize_work;
+ 	bool csa_block_tx; /* write-protected by sdata_lock and local->mtx */
+ 	struct cfg80211_chan_def csa_chandef;
+@@ -1143,6 +1141,44 @@ enum mac80211_scan_state {
+ 	SCAN_ABORT,
+ };
+ 
++/**
++ * struct airtime_sched_info - state used for airtime scheduling and AQL
++ *
++ * @lock: spinlock that protects all the fields in this struct
++ * @active_txqs: rbtree of currently backlogged queues, sorted by virtual time
++ * @schedule_pos: the current position maintained while a driver walks the tree
++ *                with ieee80211_next_txq()
++ * @active_list: list of struct airtime_info structs that were active within
++ *               the last AIRTIME_ACTIVE_DURATION (100 ms), used to compute
++ *               weight_sum
++ * @last_weight_update: used for rate limiting walking active_list
++ * @last_schedule_time: tracks the last time a transmission was scheduled; used
++ *                      for catching up v_t if no stations are eligible for
++ *                      transmission.
++ * @v_t: global virtual time; queues with v_t < this are eligible for
++ *       transmission
++ * @weight_sum: total sum of all active stations used for dividing airtime
++ * @weight_sum_reciprocal: reciprocal of weight_sum (to avoid divisions in fast
++ *                         path - see comment above
++ *                         IEEE80211_RECIPROCAL_DIVISOR_64)
++ * @aql_txq_limit_low: AQL limit when total outstanding airtime
++ *                     is < IEEE80211_AQL_THRESHOLD
++ * @aql_txq_limit_high: AQL limit when total outstanding airtime
++ *                      is > IEEE80211_AQL_THRESHOLD
++ */
++struct airtime_sched_info {
++	spinlock_t lock;
++	struct rb_root_cached active_txqs;
++	struct rb_node *schedule_pos;
++	struct list_head active_list;
++	u64 last_weight_update;
++	u64 last_schedule_activity;
++	u64 v_t;
++	u64 weight_sum;
++	u64 weight_sum_reciprocal;
++	u32 aql_txq_limit_low;
++	u32 aql_txq_limit_high;
++};
+ DECLARE_STATIC_KEY_FALSE(aql_disable);
+ 
+ struct ieee80211_local {
+@@ -1156,13 +1192,8 @@ struct ieee80211_local {
+ 	struct codel_params cparams;
+ 
+ 	/* protects active_txqs and txqi->schedule_order */
+-	spinlock_t active_txq_lock[IEEE80211_NUM_ACS];
+-	struct list_head active_txqs[IEEE80211_NUM_ACS];
+-	u16 schedule_round[IEEE80211_NUM_ACS];
+-
++	struct airtime_sched_info airtime[IEEE80211_NUM_ACS];
+ 	u16 airtime_flags;
+-	u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
+-	u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
+ 	u32 aql_threshold;
+ 	atomic_t aql_total_pending_airtime;
+ 
+@@ -1581,6 +1612,125 @@ static inline bool txq_has_queue(struct
+ 	return !(skb_queue_empty(&txqi->frags) && !txqi->tin.backlog_packets);
+ }
+ 
++static inline struct airtime_info *to_airtime_info(struct ieee80211_txq *txq)
++{
++	struct ieee80211_sub_if_data *sdata;
++	struct sta_info *sta;
++
++	if (txq->sta) {
++		sta = container_of(txq->sta, struct sta_info, sta);
++		return &sta->airtime[txq->ac];
++	}
++
++	sdata = vif_to_sdata(txq->vif);
++	return &sdata->airtime[txq->ac];
++}
++
++/* To avoid divisions in the fast path, we keep pre-computed reciprocals for
++ * airtime weight calculations. There are two different weights to keep track
++ * of: The per-station weight and the sum of weights per phy.
++ *
++ * For the per-station weights (kept in airtime_info below), we use 32-bit
++ * reciprocals with a devisor of 2^19. This lets us keep the multiplications and
++ * divisions for the station weights as 32-bit operations at the cost of a bit
++ * of rounding error for high weights; but the choice of divisor keeps rounding
++ * errors <10% for weights <2^15, assuming no more than 8ms of airtime is
++ * reported at a time.
++ *
++ * For the per-phy sum of weights the values can get higher, so we use 64-bit
++ * operations for those with a 32-bit divisor, which should avoid any
++ * significant rounding errors.
++ */
++#define IEEE80211_RECIPROCAL_DIVISOR_64 0x100000000ULL
++#define IEEE80211_RECIPROCAL_SHIFT_64 32
++#define IEEE80211_RECIPROCAL_DIVISOR_32 0x80000U
++#define IEEE80211_RECIPROCAL_SHIFT_32 19
++
++static inline void airtime_weight_set(struct airtime_info *air_info, u16 weight)
++{
++	if (air_info->weight == weight)
++		return;
++
++	air_info->weight = weight;
++	if (weight) {
++		air_info->weight_reciprocal =
++			IEEE80211_RECIPROCAL_DIVISOR_32 / weight;
++	} else {
++		air_info->weight_reciprocal = 0;
++	}
++}
++
++static inline void airtime_weight_sum_set(struct airtime_sched_info *air_sched,
++					  int weight_sum)
++{
++	if (air_sched->weight_sum == weight_sum)
++		return;
++
++	air_sched->weight_sum = weight_sum;
++	if (air_sched->weight_sum) {
++		air_sched->weight_sum_reciprocal = IEEE80211_RECIPROCAL_DIVISOR_64;
++		do_div(air_sched->weight_sum_reciprocal, air_sched->weight_sum);
++	} else {
++		air_sched->weight_sum_reciprocal = 0;
++	}
++}
++
++/* A problem when trying to enforce airtime fairness is that we want to divide
++ * the airtime between the currently *active* stations. However, basing this on
++ * the instantaneous queue state of stations doesn't work, as queues tend to
++ * oscillate very quickly between empty and occupied, leading to the scheduler
++ * thinking only a single station is active when deciding whether to allow
++ * transmission (and thus not throttling correctly).
++ *
++ * To fix this we use a timer-based notion of activity: a station is considered
++ * active if it has been scheduled within the last 100 ms; we keep a separate
++ * list of all the stations considered active in this manner, and lazily update
++ * the total weight of active stations from this list (filtering the stations in
++ * the list by their 'last active' time).
++ *
++ * We add one additional safeguard to guard against stations that manage to get
++ * scheduled every 100 ms but don't transmit a lot of data, and thus don't use
++ * up any airtime. Such stations would be able to get priority for an extended
++ * period of time if they do start transmitting at full capacity again, and so
++ * we add an explicit maximum for how far behind a station is allowed to fall in
++ * the virtual airtime domain. This limit is set to a relatively high value of
++ * 20 ms because the main mechanism for catching up idle stations is the active
++ * state as described above; i.e., the hard limit should only be hit in
++ * pathological cases.
++ */
++#define AIRTIME_ACTIVE_DURATION (100 * NSEC_PER_MSEC)
++#define AIRTIME_MAX_BEHIND 20000 /* 20 ms */
++
++static inline bool airtime_is_active(struct airtime_info *air_info, u64 now)
++{
++	return air_info->last_scheduled >= now - AIRTIME_ACTIVE_DURATION;
++}
++
++static inline void airtime_set_active(struct airtime_sched_info *air_sched,
++				      struct airtime_info *air_info, u64 now)
++{
++	air_info->last_scheduled = now;
++	air_sched->last_schedule_activity = now;
++	list_move_tail(&air_info->list, &air_sched->active_list);
++}
++
++static inline bool airtime_catchup_v_t(struct airtime_sched_info *air_sched,
++				       u64 v_t, u64 now)
++{
++	air_sched->v_t = v_t;
++	return true;
++}
++
++static inline void init_airtime_info(struct airtime_info *air_info,
++				     struct airtime_sched_info *air_sched)
++{
++	atomic_set(&air_info->aql_tx_pending, 0);
++	air_info->aql_limit_low = air_sched->aql_txq_limit_low;
++	air_info->aql_limit_high = air_sched->aql_txq_limit_high;
++	airtime_weight_set(air_info, IEEE80211_DEFAULT_AIRTIME_WEIGHT);
++	INIT_LIST_HEAD(&air_info->list);
++}
++
+ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
+ {
+ 	return ether_addr_equal(raddr, addr) ||
+@@ -1821,6 +1971,14 @@ int ieee80211_tx_control_port(struct wip
+ 			      u64 *cookie);
+ int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev,
+ 			      const u8 *buf, size_t len);
++void ieee80211_resort_txq(struct ieee80211_hw *hw,
++			  struct ieee80211_txq *txq);
++void ieee80211_unschedule_txq(struct ieee80211_hw *hw,
++			      struct ieee80211_txq *txq,
++			      bool purge);
++void ieee80211_update_airtime_weight(struct ieee80211_local *local,
++				     struct airtime_sched_info *air_sched,
++				     u64 now, bool force);
+ 
+ /* HT */
+ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -2067,6 +2067,9 @@ int ieee80211_if_add(struct ieee80211_lo
+ 		}
+ 	}
+ 
++	for (i = 0; i < IEEE80211_NUM_ACS; i++)
++		init_airtime_info(&sdata->airtime[i], &local->airtime[i]);
++
+ 	ieee80211_set_default_queues(sdata);
+ 
+ 	sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -693,10 +693,13 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+ 	spin_lock_init(&local->queue_stop_reason_lock);
+ 
+ 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+-		INIT_LIST_HEAD(&local->active_txqs[i]);
+-		spin_lock_init(&local->active_txq_lock[i]);
+-		local->aql_txq_limit_low[i] = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
+-		local->aql_txq_limit_high[i] =
++		struct airtime_sched_info *air_sched = &local->airtime[i];
++
++		air_sched->active_txqs = RB_ROOT_CACHED;
++		INIT_LIST_HEAD(&air_sched->active_list);
++		spin_lock_init(&air_sched->lock);
++		air_sched->aql_txq_limit_low = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
++		air_sched->aql_txq_limit_high =
+ 			IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H;
+ 	}
+ 
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -1573,12 +1573,8 @@ static void sta_ps_start(struct sta_info
+ 
+ 	for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
+ 		struct ieee80211_txq *txq = sta->sta.txq[tid];
+-		struct txq_info *txqi = to_txq_info(txq);
+ 
+-		spin_lock(&local->active_txq_lock[txq->ac]);
+-		if (!list_empty(&txqi->schedule_order))
+-			list_del_init(&txqi->schedule_order);
+-		spin_unlock(&local->active_txq_lock[txq->ac]);
++		ieee80211_unschedule_txq(&local->hw, txq, false);
+ 
+ 		if (txq_has_queue(txq))
+ 			set_bit(tid, &sta->txq_buffered_tids);
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -426,15 +426,11 @@ struct sta_info *sta_info_alloc(struct i
+ 	if (sta_prepare_rate_control(local, sta, gfp))
+ 		goto free_txq;
+ 
+-	sta->airtime_weight = IEEE80211_DEFAULT_AIRTIME_WEIGHT;
+ 
+ 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ 		skb_queue_head_init(&sta->ps_tx_buf[i]);
+ 		skb_queue_head_init(&sta->tx_filtered[i]);
+-		sta->airtime[i].deficit = sta->airtime_weight;
+-		atomic_set(&sta->airtime[i].aql_tx_pending, 0);
+-		sta->airtime[i].aql_limit_low = local->aql_txq_limit_low[i];
+-		sta->airtime[i].aql_limit_high = local->aql_txq_limit_high[i];
++		init_airtime_info(&sta->airtime[i], &local->airtime[i]);
+ 	}
+ 
+ 	for (i = 0; i < IEEE80211_NUM_TIDS; i++)
+@@ -1898,24 +1894,59 @@ void ieee80211_sta_set_buffered(struct i
+ }
+ EXPORT_SYMBOL(ieee80211_sta_set_buffered);
+ 
+-void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
+-				    u32 tx_airtime, u32 rx_airtime)
++void ieee80211_register_airtime(struct ieee80211_txq *txq,
++				u32 tx_airtime, u32 rx_airtime)
+ {
+-	struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+-	struct ieee80211_local *local = sta->sdata->local;
+-	u8 ac = ieee80211_ac_from_tid(tid);
++	struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif);
++	struct ieee80211_local *local = sdata->local;
++	u64 weight_sum, weight_sum_reciprocal;
++	struct airtime_sched_info *air_sched;
++	struct airtime_info *air_info;
+ 	u32 airtime = 0;
+ 
+-	if (sta->local->airtime_flags & AIRTIME_USE_TX)
++	air_sched = &local->airtime[txq->ac];
++	air_info = to_airtime_info(txq);
++
++	if (local->airtime_flags & AIRTIME_USE_TX)
+ 		airtime += tx_airtime;
+-	if (sta->local->airtime_flags & AIRTIME_USE_RX)
++	if (local->airtime_flags & AIRTIME_USE_RX)
+ 		airtime += rx_airtime;
+ 
+-	spin_lock_bh(&local->active_txq_lock[ac]);
+-	sta->airtime[ac].tx_airtime += tx_airtime;
+-	sta->airtime[ac].rx_airtime += rx_airtime;
+-	sta->airtime[ac].deficit -= airtime;
+-	spin_unlock_bh(&local->active_txq_lock[ac]);
++	/* Weights scale so the unit weight is 256 */
++	airtime <<= 8;
++
++	spin_lock_bh(&air_sched->lock);
++
++	air_info->tx_airtime += tx_airtime;
++	air_info->rx_airtime += rx_airtime;
++
++	if (air_sched->weight_sum) {
++		weight_sum = air_sched->weight_sum;
++		weight_sum_reciprocal = air_sched->weight_sum_reciprocal;
++	} else {
++		weight_sum = air_info->weight;
++		weight_sum_reciprocal = air_info->weight_reciprocal;
++	}
++
++	/* Round the calculation of global vt */
++	air_sched->v_t += (u64)((airtime + (weight_sum >> 1)) *
++				weight_sum_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_64;
++	air_info->v_t += (u32)((airtime + (air_info->weight >> 1)) *
++			       air_info->weight_reciprocal) >> IEEE80211_RECIPROCAL_SHIFT_32;
++	ieee80211_resort_txq(&local->hw, txq);
++
++	spin_unlock_bh(&air_sched->lock);
++}
++
++void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
++				    u32 tx_airtime, u32 rx_airtime)
++{
++	struct ieee80211_txq *txq = pubsta->txq[tid];
++
++	if (!txq)
++		return;
++
++	ieee80211_register_airtime(txq, tx_airtime, rx_airtime);
+ }
+ EXPORT_SYMBOL(ieee80211_sta_register_airtime);
+ 
+@@ -2364,7 +2395,7 @@ void sta_set_sinfo(struct sta_info *sta,
+ 	}
+ 
+ 	if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT))) {
+-		sinfo->airtime_weight = sta->airtime_weight;
++		sinfo->airtime_weight = sta->airtime[0].weight;
+ 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT);
+ 	}
+ 
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -135,18 +135,25 @@ enum ieee80211_agg_stop_reason {
+ #define AIRTIME_USE_TX		BIT(0)
+ #define AIRTIME_USE_RX		BIT(1)
+ 
++
+ struct airtime_info {
+ 	u64 rx_airtime;
+ 	u64 tx_airtime;
+-	s64 deficit;
++	u64 v_t;
++	u64 last_scheduled;
++	struct list_head list;
+ 	atomic_t aql_tx_pending; /* Estimated airtime for frames pending */
+ 	u32 aql_limit_low;
+ 	u32 aql_limit_high;
++	u32 weight_reciprocal;
++	u16 weight;
+ };
+ 
+ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
+ 					  struct sta_info *sta, u8 ac,
+ 					  u16 tx_airtime, bool tx_completed);
++void ieee80211_register_airtime(struct ieee80211_txq *txq,
++				u32 tx_airtime, u32 rx_airtime);
+ 
+ struct sta_info;
+ 
+@@ -515,7 +522,6 @@ struct ieee80211_fragment_cache {
+  * @tid_seq: per-TID sequence numbers for sending to this STA
+  * @airtime: per-AC struct airtime_info describing airtime statistics for this
+  *	station
+- * @airtime_weight: station weight for airtime fairness calculation purposes
+  * @ampdu_mlme: A-MPDU state machine state
+  * @mesh: mesh STA information
+  * @debugfs_dir: debug filesystem directory dentry
+@@ -646,7 +652,6 @@ struct sta_info {
+ 	u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
+ 
+ 	struct airtime_info airtime[IEEE80211_NUM_ACS];
+-	u16 airtime_weight;
+ 
+ 	/*
+ 	 * Aggregation information, locked with lock.
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -972,6 +972,25 @@ static void __ieee80211_tx_status(struct
+ 		if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked)
+ 			ieee80211_frame_acked(sta, skb);
+ 
++	} else if (wiphy_ext_feature_isset(local->hw.wiphy,
++					   NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) {
++		struct ieee80211_sub_if_data *sdata;
++		struct ieee80211_txq *txq;
++		u32 airtime;
++
++		/* Account airtime to multicast queue */
++		sdata = ieee80211_sdata_from_skb(local, skb);
++
++		if (sdata && (txq = sdata->vif.txq)) {
++			airtime = info->status.tx_time ?:
++				ieee80211_calc_expected_tx_airtime(hw,
++								   &sdata->vif,
++								   NULL,
++								   skb->len,
++								   false);
++
++			ieee80211_register_airtime(txq, airtime, 0);
++		}
+ 	}
+ 
+ 	/* SNMP counters
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -18,6 +18,7 @@
+ #include <linux/bitmap.h>
+ #include <linux/rcupdate.h>
+ #include <linux/export.h>
++#include <linux/timekeeping.h>
+ #include <net/net_namespace.h>
+ #include <net/ieee80211_radiotap.h>
+ #include <net/cfg80211.h>
+@@ -1489,7 +1490,7 @@ void ieee80211_txq_init(struct ieee80211
+ 	codel_vars_init(&txqi->def_cvars);
+ 	codel_stats_init(&txqi->cstats);
+ 	__skb_queue_head_init(&txqi->frags);
+-	INIT_LIST_HEAD(&txqi->schedule_order);
++	RB_CLEAR_NODE(&txqi->schedule_order);
+ 
+ 	txqi->txq.vif = &sdata->vif;
+ 
+@@ -1533,9 +1534,7 @@ void ieee80211_txq_purge(struct ieee8021
+ 	ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
+ 	spin_unlock_bh(&fq->lock);
+ 
+-	spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]);
+-	list_del_init(&txqi->schedule_order);
+-	spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]);
++	ieee80211_unschedule_txq(&local->hw, &txqi->txq, true);
+ }
+ 
+ void ieee80211_txq_set_params(struct ieee80211_local *local)
+@@ -3797,102 +3796,259 @@ EXPORT_SYMBOL(ieee80211_tx_dequeue);
+ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
+ {
+ 	struct ieee80211_local *local = hw_to_local(hw);
++	struct airtime_sched_info *air_sched;
++	u64 now = ktime_get_boottime_ns();
+ 	struct ieee80211_txq *ret = NULL;
+-	struct txq_info *txqi = NULL, *head = NULL;
+-	bool found_eligible_txq = false;
++	struct airtime_info *air_info;
++	struct txq_info *txqi = NULL;
++	struct rb_node *node;
++	bool first = false;
+ 
+-	spin_lock_bh(&local->active_txq_lock[ac]);
++	air_sched = &local->airtime[ac];
++	spin_lock_bh(&air_sched->lock);
+ 
+- begin:
+-	txqi = list_first_entry_or_null(&local->active_txqs[ac],
+-					struct txq_info,
+-					schedule_order);
+-	if (!txqi)
++	node = air_sched->schedule_pos;
++
++begin:
++	if (!node) {
++		node = rb_first_cached(&air_sched->active_txqs);
++		first = true;
++	} else {
++		node = rb_next(node);
++	}
++
++	if (!node)
+ 		goto out;
+ 
+-	if (txqi == head) {
+-		if (!found_eligible_txq)
+-			goto out;
+-		else
+-			found_eligible_txq = false;
++	txqi = container_of(node, struct txq_info, schedule_order);
++	air_info = to_airtime_info(&txqi->txq);
++
++	if (air_info->v_t > air_sched->v_t &&
++	    (!first || !airtime_catchup_v_t(air_sched, air_info->v_t, now)))
++		goto out;
++
++	if (!ieee80211_txq_airtime_check(hw, &txqi->txq)) {
++		first = false;
++		goto begin;
+ 	}
+ 
+-	if (!head)
+-		head = txqi;
++	air_sched->schedule_pos = node;
++	air_sched->last_schedule_activity = now;
++	ret = &txqi->txq;
++out:
++	spin_unlock_bh(&air_sched->lock);
++	return ret;
++}
++EXPORT_SYMBOL(ieee80211_next_txq);
+ 
+-	if (txqi->txq.sta) {
+-		struct sta_info *sta = container_of(txqi->txq.sta,
+-						    struct sta_info, sta);
+-		bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
+-		s64 deficit = sta->airtime[txqi->txq.ac].deficit;
++static void __ieee80211_insert_txq(struct rb_root_cached *root,
++				   struct txq_info *txqi)
++{
++	struct rb_node **new = &root->rb_root.rb_node;
++	struct airtime_info *old_air, *new_air;
++	struct rb_node *parent = NULL;
++	struct txq_info *__txqi;
++	bool leftmost = true;
++
++	while (*new) {
++		parent = *new;
++		__txqi = rb_entry(parent, struct txq_info, schedule_order);
++		old_air = to_airtime_info(&__txqi->txq);
++		new_air = to_airtime_info(&txqi->txq);
+ 
+-		if (aql_check)
+-			found_eligible_txq = true;
++		if (new_air->v_t <= old_air->v_t) {
++			new = &parent->rb_left;
++		} else {
++			new = &parent->rb_right;
++			leftmost = false;
++		}
++	}
+ 
+-		if (deficit < 0)
+-			sta->airtime[txqi->txq.ac].deficit +=
+-				sta->airtime_weight;
+-
+-		if (deficit < 0 || !aql_check) {
+-			list_move_tail(&txqi->schedule_order,
+-				       &local->active_txqs[txqi->txq.ac]);
+-			goto begin;
++	rb_link_node(&txqi->schedule_order, parent, new);
++	rb_insert_color_cached(&txqi->schedule_order, root, leftmost);
++}
++
++void ieee80211_resort_txq(struct ieee80211_hw *hw,
++			  struct ieee80211_txq *txq)
++{
++	struct airtime_info *air_info = to_airtime_info(txq);
++	struct ieee80211_local *local = hw_to_local(hw);
++	struct txq_info *txqi = to_txq_info(txq);
++	struct airtime_sched_info *air_sched;
++
++	air_sched = &local->airtime[txq->ac];
++
++	lockdep_assert_held(&air_sched->lock);
++
++	if (!RB_EMPTY_NODE(&txqi->schedule_order)) {
++		struct airtime_info *a_prev = NULL, *a_next = NULL;
++		struct txq_info *t_prev, *t_next;
++		struct rb_node *n_prev, *n_next;
++
++		/* Erasing a node can cause an expensive rebalancing operation,
++		 * so we check the previous and next nodes first and only remove
++		 * and re-insert if the current node is not already in the
++		 * correct position.
++		 */
++		if ((n_prev = rb_prev(&txqi->schedule_order)) != NULL) {
++			t_prev = container_of(n_prev, struct txq_info,
++					      schedule_order);
++			a_prev = to_airtime_info(&t_prev->txq);
++		}
++
++		if ((n_next = rb_next(&txqi->schedule_order)) != NULL) {
++			t_next = container_of(n_next, struct txq_info,
++					      schedule_order);
++			a_next = to_airtime_info(&t_next->txq);
+ 		}
++
++		if ((!a_prev || a_prev->v_t <= air_info->v_t) &&
++		    (!a_next || a_next->v_t > air_info->v_t))
++			return;
++
++		if (air_sched->schedule_pos == &txqi->schedule_order)
++			air_sched->schedule_pos = n_prev;
++
++		rb_erase_cached(&txqi->schedule_order,
++				&air_sched->active_txqs);
++		RB_CLEAR_NODE(&txqi->schedule_order);
++		__ieee80211_insert_txq(&air_sched->active_txqs, txqi);
+ 	}
++}
++
++void ieee80211_update_airtime_weight(struct ieee80211_local *local,
++				     struct airtime_sched_info *air_sched,
++				     u64 now, bool force)
++{
++	struct airtime_info *air_info, *tmp;
++	u64 weight_sum = 0;
++
++	if (unlikely(!now))
++		now = ktime_get_boottime_ns();
++
++	lockdep_assert_held(&air_sched->lock);
++
++	if (!force && (air_sched->last_weight_update <
++		       now - AIRTIME_ACTIVE_DURATION))
++		return;
++
++	list_for_each_entry_safe(air_info, tmp,
++				 &air_sched->active_list, list) {
++		if (airtime_is_active(air_info, now))
++			weight_sum += air_info->weight;
++		else
++			list_del_init(&air_info->list);
++	}
++	airtime_weight_sum_set(air_sched, weight_sum);
++	air_sched->last_weight_update = now;
++}
+ 
++void ieee80211_schedule_txq(struct ieee80211_hw *hw,
++			    struct ieee80211_txq *txq)
++	__acquires(txq_lock) __releases(txq_lock)
++{
++	struct ieee80211_local *local = hw_to_local(hw);
++	struct txq_info *txqi = to_txq_info(txq);
++	struct airtime_sched_info *air_sched;
++	u64 now = ktime_get_boottime_ns();
++	struct airtime_info *air_info;
++	u8 ac = txq->ac;
++	bool was_active;
+ 
+-	if (txqi->schedule_round == local->schedule_round[ac])
++	air_sched = &local->airtime[ac];
++	air_info = to_airtime_info(txq);
++
++	spin_lock_bh(&air_sched->lock);
++	was_active = airtime_is_active(air_info, now);
++	airtime_set_active(air_sched, air_info, now);
++
++	if (!RB_EMPTY_NODE(&txqi->schedule_order))
+ 		goto out;
+ 
+-	list_del_init(&txqi->schedule_order);
+-	txqi->schedule_round = local->schedule_round[ac];
+-	ret = &txqi->txq;
++	/* If the station has been inactive for a while, catch up its v_t so it
++	 * doesn't get indefinite priority; see comment above the definition of
++	 * AIRTIME_MAX_BEHIND.
++	 */
++	if ((!was_active && air_info->v_t < air_sched->v_t) ||
++	    air_info->v_t < air_sched->v_t - AIRTIME_MAX_BEHIND)
++		air_info->v_t = air_sched->v_t;
++
++	ieee80211_update_airtime_weight(local, air_sched, now, !was_active);
++	__ieee80211_insert_txq(&air_sched->active_txqs, txqi);
+ 
+ out:
+-	spin_unlock_bh(&local->active_txq_lock[ac]);
+-	return ret;
++	spin_unlock_bh(&air_sched->lock);
+ }
+-EXPORT_SYMBOL(ieee80211_next_txq);
++EXPORT_SYMBOL(ieee80211_schedule_txq);
+ 
+-void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
+-			      struct ieee80211_txq *txq,
+-			      bool force)
++static void __ieee80211_unschedule_txq(struct ieee80211_hw *hw,
++				       struct ieee80211_txq *txq,
++				       bool purge)
+ {
+ 	struct ieee80211_local *local = hw_to_local(hw);
+ 	struct txq_info *txqi = to_txq_info(txq);
++	struct airtime_sched_info *air_sched;
++	struct airtime_info *air_info;
+ 
+-	spin_lock_bh(&local->active_txq_lock[txq->ac]);
++	air_sched = &local->airtime[txq->ac];
++	air_info = to_airtime_info(&txqi->txq);
+ 
+-	if (list_empty(&txqi->schedule_order) &&
+-	    (force || !skb_queue_empty(&txqi->frags) ||
+-	     txqi->tin.backlog_packets)) {
+-		/* If airtime accounting is active, always enqueue STAs at the
+-		 * head of the list to ensure that they only get moved to the
+-		 * back by the airtime DRR scheduler once they have a negative
+-		 * deficit. A station that already has a negative deficit will
+-		 * get immediately moved to the back of the list on the next
+-		 * call to ieee80211_next_txq().
+-		 */
+-		if (txqi->txq.sta && local->airtime_flags &&
+-		    wiphy_ext_feature_isset(local->hw.wiphy,
+-					    NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+-			list_add(&txqi->schedule_order,
+-				 &local->active_txqs[txq->ac]);
+-		else
+-			list_add_tail(&txqi->schedule_order,
+-				      &local->active_txqs[txq->ac]);
++	lockdep_assert_held(&air_sched->lock);
++
++	if (purge) {
++		list_del_init(&air_info->list);
++		ieee80211_update_airtime_weight(local, air_sched, 0, true);
+ 	}
+ 
+-	spin_unlock_bh(&local->active_txq_lock[txq->ac]);
++	if (RB_EMPTY_NODE(&txqi->schedule_order))
++		return;
++
++	if (air_sched->schedule_pos == &txqi->schedule_order)
++		air_sched->schedule_pos = rb_prev(&txqi->schedule_order);
++
++	if (!purge)
++		airtime_set_active(air_sched, air_info,
++				   ktime_get_boottime_ns());
++
++	rb_erase_cached(&txqi->schedule_order,
++			&air_sched->active_txqs);
++	RB_CLEAR_NODE(&txqi->schedule_order);
++}
++
++void ieee80211_unschedule_txq(struct ieee80211_hw *hw,
++			      struct ieee80211_txq *txq,
++			      bool purge)
++	__acquires(txq_lock) __releases(txq_lock)
++{
++	struct ieee80211_local *local = hw_to_local(hw);
++
++	spin_lock_bh(&local->airtime[txq->ac].lock);
++	__ieee80211_unschedule_txq(hw, txq, purge);
++	spin_unlock_bh(&local->airtime[txq->ac].lock);
++}
++
++void ieee80211_return_txq(struct ieee80211_hw *hw,
++			  struct ieee80211_txq *txq, bool force)
++{
++	struct ieee80211_local *local = hw_to_local(hw);
++	struct txq_info *txqi = to_txq_info(txq);
++
++	spin_lock_bh(&local->airtime[txq->ac].lock);
++
++	if (!RB_EMPTY_NODE(&txqi->schedule_order) && !force &&
++	    !txq_has_queue(txq))
++		__ieee80211_unschedule_txq(hw, txq, false);
++
++	spin_unlock_bh(&local->airtime[txq->ac].lock);
+ }
+-EXPORT_SYMBOL(__ieee80211_schedule_txq);
++EXPORT_SYMBOL(ieee80211_return_txq);
+ 
+ DEFINE_STATIC_KEY_FALSE(aql_disable);
+ 
+ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
+ 				 struct ieee80211_txq *txq)
+ {
+-	struct sta_info *sta;
++	struct airtime_info *air_info = to_airtime_info(txq);
+ 	struct ieee80211_local *local = hw_to_local(hw);
+ 
+ 	if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
+@@ -3907,15 +4063,12 @@ bool ieee80211_txq_airtime_check(struct
+ 	if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
+ 		return true;
+ 
+-	sta = container_of(txq->sta, struct sta_info, sta);
+-	if (atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
+-	    sta->airtime[txq->ac].aql_limit_low)
++	if (atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_low)
+ 		return true;
+ 
+ 	if (atomic_read(&local->aql_total_pending_airtime) <
+ 	    local->aql_threshold &&
+-	    atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
+-	    sta->airtime[txq->ac].aql_limit_high)
++	    atomic_read(&air_info->aql_tx_pending) < air_info->aql_limit_high)
+ 		return true;
+ 
+ 	return false;
+@@ -3925,60 +4078,59 @@ EXPORT_SYMBOL(ieee80211_txq_airtime_chec
+ bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
+ 				struct ieee80211_txq *txq)
+ {
++	struct txq_info *first_txqi = NULL, *txqi = to_txq_info(txq);
+ 	struct ieee80211_local *local = hw_to_local(hw);
+-	struct txq_info *iter, *tmp, *txqi = to_txq_info(txq);
+-	struct sta_info *sta;
+-	u8 ac = txq->ac;
++	struct airtime_sched_info *air_sched;
++	struct airtime_info *air_info;
++	struct rb_node *node = NULL;
++	bool ret = false;
++	u64 now;
+ 
+-	spin_lock_bh(&local->active_txq_lock[ac]);
+ 
+-	if (!txqi->txq.sta)
+-		goto out;
++	if (!ieee80211_txq_airtime_check(hw, txq))
++		return false;
++
++	air_sched = &local->airtime[txq->ac];
++	spin_lock_bh(&air_sched->lock);
+ 
+-	if (list_empty(&txqi->schedule_order))
++	if (RB_EMPTY_NODE(&txqi->schedule_order))
+ 		goto out;
+ 
+-	list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
+-				 schedule_order) {
+-		if (iter == txqi)
+-			break;
++	now = ktime_get_boottime_ns();
+ 
+-		if (!iter->txq.sta) {
+-			list_move_tail(&iter->schedule_order,
+-				       &local->active_txqs[ac]);
+-			continue;
+-		}
+-		sta = container_of(iter->txq.sta, struct sta_info, sta);
+-		if (sta->airtime[ac].deficit < 0)
+-			sta->airtime[ac].deficit += sta->airtime_weight;
+-		list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
++	/* Like in ieee80211_next_txq(), make sure the first station in the
++	 * scheduling order is eligible for transmission to avoid starvation.
++	 */
++	node = rb_first_cached(&air_sched->active_txqs);
++	if (node) {
++		first_txqi = container_of(node, struct txq_info,
++					  schedule_order);
++		air_info = to_airtime_info(&first_txqi->txq);
++
++		if (air_sched->v_t < air_info->v_t)
++			airtime_catchup_v_t(air_sched, air_info->v_t, now);
+ 	}
+ 
+-	sta = container_of(txqi->txq.sta, struct sta_info, sta);
+-	if (sta->airtime[ac].deficit >= 0)
+-		goto out;
+-
+-	sta->airtime[ac].deficit += sta->airtime_weight;
+-	list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
+-	spin_unlock_bh(&local->active_txq_lock[ac]);
++	air_info = to_airtime_info(&txqi->txq);
++	if (air_info->v_t <= air_sched->v_t) {
++		air_sched->last_schedule_activity = now;
++		ret = true;
++	}
+ 
+-	return false;
+ out:
+-	if (!list_empty(&txqi->schedule_order))
+-		list_del_init(&txqi->schedule_order);
+-	spin_unlock_bh(&local->active_txq_lock[ac]);
+-
+-	return true;
++	spin_unlock_bh(&air_sched->lock);
++	return ret;
+ }
+ EXPORT_SYMBOL(ieee80211_txq_may_transmit);
+ 
+ void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
+ {
+ 	struct ieee80211_local *local = hw_to_local(hw);
++	struct airtime_sched_info *air_sched = &local->airtime[ac];
+ 
+-	spin_lock_bh(&local->active_txq_lock[ac]);
+-	local->schedule_round[ac]++;
+-	spin_unlock_bh(&local->active_txq_lock[ac]);
++	spin_lock_bh(&air_sched->lock);
++	air_sched->schedule_pos = NULL;
++	spin_unlock_bh(&air_sched->lock);
+ }
+ EXPORT_SYMBOL(ieee80211_txq_schedule_start);
+ 
diff --git a/package/kernel/mac80211/patches/subsys/383-mac80211-fix-enabling-4-address-mode-on-a-sta-vif-af.patch b/package/kernel/mac80211/patches/subsys/383-mac80211-fix-enabling-4-address-mode-on-a-sta-vif-af.patch
new file mode 100644
index 0000000000..c1f77ff5d9
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/383-mac80211-fix-enabling-4-address-mode-on-a-sta-vif-af.patch
@@ -0,0 +1,72 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 2 Jul 2021 06:57:53 +0200
+Subject: [PATCH] mac80211: fix enabling 4-address mode on a sta vif after
+ assoc
+
+Notify the driver about the 4-address mode change and also send a nulldata
+packet to the AP to notify it about the change
+
+Fixes: 1ff4e8f2dec8 ("mac80211: notify the driver when a sta uses 4-address mode")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -152,6 +152,8 @@ static int ieee80211_change_iface(struct
+ 				  struct vif_params *params)
+ {
+ 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++	struct ieee80211_local *local = sdata->local;
++	struct sta_info *sta;
+ 	int ret;
+ 
+ 	ret = ieee80211_if_change_type(sdata, type);
+@@ -162,7 +164,24 @@ static int ieee80211_change_iface(struct
+ 		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
+ 		ieee80211_check_fast_rx_iface(sdata);
+ 	} else if (type == NL80211_IFTYPE_STATION && params->use_4addr >= 0) {
++		struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
++
++		if (params->use_4addr == ifmgd->use_4addr)
++			return 0;
++
+ 		sdata->u.mgd.use_4addr = params->use_4addr;
++		if (!ifmgd->associated)
++			return 0;
++
++		mutex_lock(&local->sta_mtx);
++		sta = sta_info_get(sdata, ifmgd->bssid);
++		if (sta)
++			drv_sta_set_4addr(local, sdata, &sta->sta,
++					  params->use_4addr);
++		mutex_unlock(&local->sta_mtx);
++
++		if (params->use_4addr)
++			ieee80211_send_4addr_nullfunc(local, sdata);
+ 	}
+ 
+ 	if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2215,6 +2215,8 @@ void ieee80211_dynamic_ps_timer(struct t
+ void ieee80211_send_nullfunc(struct ieee80211_local *local,
+ 			     struct ieee80211_sub_if_data *sdata,
+ 			     bool powersave);
++void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
++				   struct ieee80211_sub_if_data *sdata);
+ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
+ 			     struct ieee80211_hdr *hdr, bool ack, u16 tx_time);
+ 
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -1115,8 +1115,8 @@ void ieee80211_send_nullfunc(struct ieee
+ 	ieee80211_tx_skb(sdata, skb);
+ }
+ 
+-static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
+-					  struct ieee80211_sub_if_data *sdata)
++void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
++				   struct ieee80211_sub_if_data *sdata)
+ {
+ 	struct sk_buff *skb;
+ 	struct ieee80211_hdr *nullfunc;
diff --git a/package/kernel/mac80211/patches/subsys/384-nl80211-add-common-API-to-configure-SAR-power-limita.patch b/package/kernel/mac80211/patches/subsys/384-nl80211-add-common-API-to-configure-SAR-power-limita.patch
new file mode 100644
index 0000000000..0c9ae3595d
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/384-nl80211-add-common-API-to-configure-SAR-power-limita.patch
@@ -0,0 +1,398 @@
+From: Carl Huang <cjhuang@codeaurora.org>
+Date: Thu, 3 Dec 2020 05:37:26 -0500
+Subject: [PATCH] nl80211: add common API to configure SAR power limitations
+
+NL80211_CMD_SET_SAR_SPECS is added to configure SAR from
+user space. NL80211_ATTR_SAR_SPEC is used to pass the SAR
+power specification when used with NL80211_CMD_SET_SAR_SPECS.
+
+Wireless driver needs to register SAR type, supported frequency
+ranges to wiphy, so user space can query it. The index in
+frequency range is used to specify which sub band the power
+limitation applies to. The SAR type is for compatibility, so later
+other SAR mechanism can be implemented without breaking the user
+space SAR applications.
+
+Normal process is user space queries the SAR capability, and
+gets the index of supported frequency ranges and associates the
+power limitation with this index and sends to kernel.
+
+Here is an example of message send to kernel:
+8c 00 00 00 08 00 01 00 00 00 00 00 38 00 2b 81
+08 00 01 00 00 00 00 00 2c 00 02 80 14 00 00 80
+08 00 02 00 00 00 00 00 08 00 01 00 38 00 00 00
+14 00 01 80 08 00 02 00 01 00 00 00 08 00 01 00
+48 00 00 00
+
+NL80211_CMD_SET_SAR_SPECS:  0x8c
+NL80211_ATTR_WIPHY:     0x01(phy idx is 0)
+NL80211_ATTR_SAR_SPEC:  0x812b (NLA_NESTED)
+NL80211_SAR_ATTR_TYPE:  0x00 (NL80211_SAR_TYPE_POWER)
+NL80211_SAR_ATTR_SPECS: 0x8002 (NLA_NESTED)
+freq range 0 power: 0x38 in 0.25dbm unit (14dbm)
+freq range 1 power: 0x48 in 0.25dbm unit (18dbm)
+
+Signed-off-by: Carl Huang <cjhuang@codeaurora.org>
+Reviewed-by: Brian Norris <briannorris@chromium.org>
+Reviewed-by: Abhishek Kumar <kuabhs@chromium.org>
+Link: https://lore.kernel.org/r/20201203103728.3034-2-cjhuang@codeaurora.org
+[minor edits, NLA parse cleanups]
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -1737,6 +1737,54 @@ struct station_info {
+ 	u8 connected_to_as;
+ };
+ 
++/**
++ * struct cfg80211_sar_sub_specs - sub specs limit
++ * @power: power limitation in 0.25dbm
++ * @freq_range_index: index the power limitation applies to
++ */
++struct cfg80211_sar_sub_specs {
++	s32 power;
++	u32 freq_range_index;
++};
++
++/**
++ * struct cfg80211_sar_specs - sar limit specs
++ * @type: it's set with power in 0.25dbm or other types
++ * @num_sub_specs: number of sar sub specs
++ * @sub_specs: memory to hold the sar sub specs
++ */
++struct cfg80211_sar_specs {
++	enum nl80211_sar_type type;
++	u32 num_sub_specs;
++	struct cfg80211_sar_sub_specs sub_specs[];
++};
++
++
++/**
++ * @struct cfg80211_sar_chan_ranges - sar frequency ranges
++ * @start_freq:  start range edge frequency
++ * @end_freq:    end range edge frequency
++ */
++struct cfg80211_sar_freq_ranges {
++	u32 start_freq;
++	u32 end_freq;
++};
++
++/**
++ * struct cfg80211_sar_capa - sar limit capability
++ * @type: it's set via power in 0.25dbm or other types
++ * @num_freq_ranges: number of frequency ranges
++ * @freq_ranges: memory to hold the freq ranges.
++ *
++ * Note: WLAN driver may append new ranges or split an existing
++ * range to small ones and then append them.
++ */
++struct cfg80211_sar_capa {
++	enum nl80211_sar_type type;
++	u32 num_freq_ranges;
++	const struct cfg80211_sar_freq_ranges *freq_ranges;
++};
++
+ #if IS_ENABLED(CPTCFG_CFG80211)
+ /**
+  * cfg80211_get_station - retrieve information about a given station
+@@ -4259,6 +4307,8 @@ struct cfg80211_ops {
+ 				  struct cfg80211_tid_config *tid_conf);
+ 	int	(*reset_tid_config)(struct wiphy *wiphy, struct net_device *dev,
+ 				    const u8 *peer, u8 tids);
++	int	(*set_sar_specs)(struct wiphy *wiphy,
++				 struct cfg80211_sar_specs *sar);
+ };
+ 
+ /*
+@@ -5030,6 +5080,8 @@ struct wiphy {
+ 
+ 	u8 max_data_retry_count;
+ 
++	const struct cfg80211_sar_capa *sar_capa;
++
+ 	char priv[] __aligned(NETDEV_ALIGN);
+ };
+ 
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -405,6 +405,18 @@ nl80211_unsol_bcast_probe_resp_policy[NL
+ 						       .len = IEEE80211_MAX_DATA_LEN }
+ };
+ 
++static const struct nla_policy
++sar_specs_policy[NL80211_SAR_ATTR_SPECS_MAX + 1] = {
++	[NL80211_SAR_ATTR_SPECS_POWER] = { .type = NLA_S32 },
++	[NL80211_SAR_ATTR_SPECS_RANGE_INDEX] = {.type = NLA_U32 },
++};
++
++static const struct nla_policy
++sar_policy[NL80211_SAR_ATTR_MAX + 1] = {
++	[NL80211_SAR_ATTR_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_SAR_TYPE),
++	[NL80211_SAR_ATTR_SPECS] = NLA_POLICY_NESTED_ARRAY(sar_specs_policy),
++};
++
+ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
+ 	[0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
+ 	[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
+@@ -739,6 +751,7 @@ static const struct nla_policy nl80211_p
+ 	[NL80211_ATTR_SAE_PWE] =
+ 		NLA_POLICY_RANGE(NLA_U8, NL80211_SAE_PWE_HUNT_AND_PECK,
+ 				 NL80211_SAE_PWE_BOTH),
++	[NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy),
+ 	[NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT },
+ };
+ 
+@@ -2117,6 +2130,56 @@ fail:
+ 	return -ENOBUFS;
+ }
+ 
++static int
++nl80211_put_sar_specs(struct cfg80211_registered_device *rdev,
++		      struct sk_buff *msg)
++{
++	struct nlattr *sar_capa, *specs, *sub_freq_range;
++	u8 num_freq_ranges;
++	int i;
++
++	if (!rdev->wiphy.sar_capa)
++		return 0;
++
++	num_freq_ranges = rdev->wiphy.sar_capa->num_freq_ranges;
++
++	sar_capa = nla_nest_start(msg, NL80211_ATTR_SAR_SPEC);
++	if (!sar_capa)
++		return -ENOSPC;
++
++	if (nla_put_u32(msg, NL80211_SAR_ATTR_TYPE, rdev->wiphy.sar_capa->type))
++		goto fail;
++
++	specs = nla_nest_start(msg, NL80211_SAR_ATTR_SPECS);
++	if (!specs)
++		goto fail;
++
++	/* report supported freq_ranges */
++	for (i = 0; i < num_freq_ranges; i++) {
++		sub_freq_range = nla_nest_start(msg, i + 1);
++		if (!sub_freq_range)
++			goto fail;
++
++		if (nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_START_FREQ,
++				rdev->wiphy.sar_capa->freq_ranges[i].start_freq))
++			goto fail;
++
++		if (nla_put_u32(msg, NL80211_SAR_ATTR_SPECS_END_FREQ,
++				rdev->wiphy.sar_capa->freq_ranges[i].end_freq))
++			goto fail;
++
++		nla_nest_end(msg, sub_freq_range);
++	}
++
++	nla_nest_end(msg, specs);
++	nla_nest_end(msg, sar_capa);
++
++	return 0;
++fail:
++	nla_nest_cancel(msg, sar_capa);
++	return -ENOBUFS;
++}
++
+ struct nl80211_dump_wiphy_state {
+ 	s64 filter_wiphy;
+ 	long start;
+@@ -2366,6 +2429,8 @@ static int nl80211_send_wiphy(struct cfg
+ 			CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST);
+ 			CMD(update_connect_params, UPDATE_CONNECT_PARAMS);
+ 			CMD(update_ft_ies, UPDATE_FT_IES);
++			if (rdev->wiphy.sar_capa)
++				CMD(set_sar_specs, SET_SAR_SPECS);
+ 		}
+ #undef CMD
+ 
+@@ -2691,6 +2756,11 @@ static int nl80211_send_wiphy(struct cfg
+ 
+ 		if (nl80211_put_tid_config_support(rdev, msg))
+ 			goto nla_put_failure;
++		state->split_start++;
++		break;
++	case 16:
++		if (nl80211_put_sar_specs(rdev, msg))
++			goto nla_put_failure;
+ 
+ 		/* done */
+ 		state->split_start = 0;
+@@ -14713,6 +14783,111 @@ static void nl80211_post_doit(__genl_con
+ 	}
+ }
+ 
++static int nl80211_set_sar_sub_specs(struct cfg80211_registered_device *rdev,
++				     struct cfg80211_sar_specs *sar_specs,
++				     struct nlattr *spec[], int index)
++{
++	u32 range_index, i;
++
++	if (!sar_specs || !spec)
++		return -EINVAL;
++
++	if (!spec[NL80211_SAR_ATTR_SPECS_POWER] ||
++	    !spec[NL80211_SAR_ATTR_SPECS_RANGE_INDEX])
++		return -EINVAL;
++
++	range_index = nla_get_u32(spec[NL80211_SAR_ATTR_SPECS_RANGE_INDEX]);
++
++	/* check if range_index exceeds num_freq_ranges */
++	if (range_index >= rdev->wiphy.sar_capa->num_freq_ranges)
++		return -EINVAL;
++
++	/* check if range_index duplicates */
++	for (i = 0; i < index; i++) {
++		if (sar_specs->sub_specs[i].freq_range_index == range_index)
++			return -EINVAL;
++	}
++
++	sar_specs->sub_specs[index].power =
++		nla_get_s32(spec[NL80211_SAR_ATTR_SPECS_POWER]);
++
++	sar_specs->sub_specs[index].freq_range_index = range_index;
++
++	return 0;
++}
++
++static int nl80211_set_sar_specs(struct sk_buff *skb, struct genl_info *info)
++{
++	struct cfg80211_registered_device *rdev = info->user_ptr[0];
++	struct nlattr *spec[NL80211_SAR_ATTR_SPECS_MAX + 1];
++	struct nlattr *tb[NL80211_SAR_ATTR_MAX + 1];
++	struct cfg80211_sar_specs *sar_spec;
++	enum nl80211_sar_type type;
++	struct nlattr *spec_list;
++	u32 specs;
++	int rem, err;
++
++	if (!rdev->wiphy.sar_capa || !rdev->ops->set_sar_specs)
++		return -EOPNOTSUPP;
++
++	if (!info->attrs[NL80211_ATTR_SAR_SPEC])
++		return -EINVAL;
++
++	nla_parse_nested(tb, NL80211_SAR_ATTR_MAX,
++			 info->attrs[NL80211_ATTR_SAR_SPEC],
++			 NULL, NULL);
++
++	if (!tb[NL80211_SAR_ATTR_TYPE] || !tb[NL80211_SAR_ATTR_SPECS])
++		return -EINVAL;
++
++	type = nla_get_u32(tb[NL80211_SAR_ATTR_TYPE]);
++	if (type != rdev->wiphy.sar_capa->type)
++		return -EINVAL;
++
++	specs = 0;
++	nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem)
++		specs++;
++
++	if (specs > rdev->wiphy.sar_capa->num_freq_ranges)
++		return -EINVAL;
++
++	sar_spec = kzalloc(sizeof(*sar_spec) +
++			   specs * sizeof(struct cfg80211_sar_sub_specs),
++			   GFP_KERNEL);
++	if (!sar_spec)
++		return -ENOMEM;
++
++	sar_spec->type = type;
++	specs = 0;
++	nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) {
++		nla_parse_nested(spec, NL80211_SAR_ATTR_SPECS_MAX,
++				 spec_list, NULL, NULL);
++
++		switch (type) {
++		case NL80211_SAR_TYPE_POWER:
++			if (nl80211_set_sar_sub_specs(rdev, sar_spec,
++						      spec, specs)) {
++				err = -EINVAL;
++				goto error;
++			}
++			break;
++		default:
++			err = -EINVAL;
++			goto error;
++		}
++		specs++;
++	}
++
++	sar_spec->num_sub_specs = specs;
++
++	rdev->cur_cmd_info = info;
++	err = rdev_set_sar_specs(rdev, sar_spec);
++	rdev->cur_cmd_info = NULL;
++error:
++	kfree(sar_spec);
++	return err;
++}
++
+ static __genl_const struct genl_ops nl80211_ops[] = {
+ 	{
+ 		.cmd = NL80211_CMD_GET_WIPHY,
+@@ -15576,6 +15751,14 @@ static const struct genl_small_ops nl802
+ 		.internal_flags = NL80211_FLAG_NEED_NETDEV |
+ 				  NL80211_FLAG_NEED_RTNL,
+ 	},
++	{
++		.cmd = NL80211_CMD_SET_SAR_SPECS,
++		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
++		.doit = nl80211_set_sar_specs,
++		.flags = GENL_UNS_ADMIN_PERM,
++		.internal_flags = NL80211_FLAG_NEED_WIPHY |
++				  NL80211_FLAG_NEED_RTNL,
++	},
+ };
+ 
+ static struct genl_family nl80211_fam __genl_ro_after_init = {
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -1356,4 +1356,16 @@ static inline int rdev_reset_tid_config(
+ 	return ret;
+ }
+ 
++static inline int rdev_set_sar_specs(struct cfg80211_registered_device *rdev,
++				     struct cfg80211_sar_specs *sar)
++{
++	int ret;
++
++	trace_rdev_set_sar_specs(&rdev->wiphy, sar);
++	ret = rdev->ops->set_sar_specs(&rdev->wiphy, sar);
++	trace_rdev_return_int(&rdev->wiphy, ret);
++
++	return ret;
++}
++
+ #endif /* __CFG80211_RDEV_OPS */
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -3551,6 +3551,25 @@ TRACE_EVENT(rdev_reset_tid_config,
+ 	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", tids: 0x%x",
+ 		  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tids)
+ );
++
++TRACE_EVENT(rdev_set_sar_specs,
++	TP_PROTO(struct wiphy *wiphy, struct cfg80211_sar_specs *sar),
++	TP_ARGS(wiphy, sar),
++	TP_STRUCT__entry(
++		WIPHY_ENTRY
++		__field(u16, type)
++		__field(u16, num)
++	),
++	TP_fast_assign(
++		WIPHY_ASSIGN;
++		__entry->type = sar->type;
++		__entry->num = sar->num_sub_specs;
++
++	),
++	TP_printk(WIPHY_PR_FMT ", Set type:%d, num_specs:%d",
++		  WIPHY_PR_ARG, __entry->type, __entry->num)
++);
++
+ #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
+ 
+ #undef TRACE_INCLUDE_PATH
diff --git a/package/kernel/mac80211/patches/subsys/385-mac80211-add-ieee80211_set_sar_specs.patch b/package/kernel/mac80211/patches/subsys/385-mac80211-add-ieee80211_set_sar_specs.patch
new file mode 100644
index 0000000000..c351bc812a
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/385-mac80211-add-ieee80211_set_sar_specs.patch
@@ -0,0 +1,51 @@
+From: Carl Huang <cjhuang@codeaurora.org>
+Date: Thu, 3 Dec 2020 05:37:27 -0500
+Subject: [PATCH] mac80211: add ieee80211_set_sar_specs
+
+This change registers ieee80211_set_sar_specs to
+mac80211_config_ops, so cfg80211 can call it.
+
+Signed-off-by: Carl Huang <cjhuang@codeaurora.org>
+Reviewed-by: Brian Norris <briannorris@chromium.org>
+Reviewed-by: Abhishek Kumar <kuabhs@chromium.org>
+Link: https://lore.kernel.org/r/20201203103728.3034-3-cjhuang@codeaurora.org
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -4207,6 +4207,8 @@ struct ieee80211_ops {
+ 				   struct ieee80211_vif *vif);
+ 	void (*sta_set_4addr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ 			      struct ieee80211_sta *sta, bool enabled);
++	int (*set_sar_specs)(struct ieee80211_hw *hw,
++			     const struct cfg80211_sar_specs *sar);
+ 	void (*sta_set_decap_offload)(struct ieee80211_hw *hw,
+ 				      struct ieee80211_vif *vif,
+ 				      struct ieee80211_sta *sta, bool enabled);
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -4136,6 +4136,17 @@ static int ieee80211_reset_tid_config(st
+ 	return ret;
+ }
+ 
++static int ieee80211_set_sar_specs(struct wiphy *wiphy,
++				   struct cfg80211_sar_specs *sar)
++{
++	struct ieee80211_local *local = wiphy_priv(wiphy);
++
++	if (!local->ops->set_sar_specs)
++		return -EOPNOTSUPP;
++
++	return local->ops->set_sar_specs(&local->hw, sar);
++}
++
+ const struct cfg80211_ops mac80211_config_ops = {
+ 	.add_virtual_intf = ieee80211_add_iface,
+ 	.del_virtual_intf = ieee80211_del_iface,
+@@ -4239,4 +4250,5 @@ const struct cfg80211_ops mac80211_confi
+ 	.probe_mesh_link = ieee80211_probe_mesh_link,
+ 	.set_tid_config = ieee80211_set_tid_config,
+ 	.reset_tid_config = ieee80211_reset_tid_config,
++	.set_sar_specs = ieee80211_set_sar_specs,
+ };
diff --git a/package/kernel/mac80211/patches/subsys/386-mac80211-check-per-vif-offload_flags-in-Tx-path.patch b/package/kernel/mac80211/patches/subsys/386-mac80211-check-per-vif-offload_flags-in-Tx-path.patch
new file mode 100644
index 0000000000..cfad1c3927
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/386-mac80211-check-per-vif-offload_flags-in-Tx-path.patch
@@ -0,0 +1,26 @@
+From: Ryder Lee <ryder.lee@mediatek.com>
+Date: Fri, 18 Jun 2021 04:38:59 +0800
+Subject: [PATCH] mac80211: check per vif offload_flags in Tx path
+
+offload_flags has been introduced to indicate encap status of each interface.
+An interface can encap offload at runtime, or if it has some extra limitations
+it can simply override the flags, so it's more flexible to check offload_flags
+in Tx path.
+
+Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
+Link: https://lore.kernel.org/r/177785418cf407808bf3a44760302d0647076990.1623961575.git.ryder.lee@mediatek.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -3309,6 +3309,9 @@ static bool ieee80211_amsdu_aggregate(st
+ 	if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
+ 		return false;
+ 
++	if (sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)
++		return false;
++
+ 	if (skb_is_gso(skb))
+ 		return false;
+ 
diff --git a/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch b/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch
index febe42eb9d..b2ee61a6dc 100644
--- a/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch
+++ b/package/kernel/mac80211/patches/subsys/500-mac80211_configure_antenna_gain.patch
@@ -1,6 +1,6 @@
 --- a/include/net/cfg80211.h
 +++ b/include/net/cfg80211.h
-@@ -3745,6 +3745,7 @@ struct mgmt_frame_regs {
+@@ -3793,6 +3793,7 @@ struct mgmt_frame_regs {
   *	(as advertised by the nl80211 feature flag.)
   * @get_tx_power: store the current TX power into the dbm variable;
   *	return 0 if successful
@@ -8,7 +8,7 @@
   *
   * @set_wds_peer: set the WDS peer for a WDS interface
   *
-@@ -4067,6 +4068,7 @@ struct cfg80211_ops {
+@@ -4115,6 +4116,7 @@ struct cfg80211_ops {
  				enum nl80211_tx_power_setting type, int mbm);
  	int	(*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
  				int *dbm);
@@ -57,7 +57,7 @@
  	__NL80211_ATTR_AFTER_LAST,
 --- a/net/mac80211/cfg.c
 +++ b/net/mac80211/cfg.c
-@@ -2709,6 +2709,19 @@ static int ieee80211_get_tx_power(struct
+@@ -2761,6 +2761,19 @@ static int ieee80211_get_tx_power(struct
  	return 0;
  }
  
@@ -77,7 +77,7 @@
  static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev,
  				  const u8 *addr)
  {
-@@ -4139,6 +4152,7 @@ const struct cfg80211_ops mac80211_confi
+@@ -4202,6 +4215,7 @@ const struct cfg80211_ops mac80211_confi
  	.set_wiphy_params = ieee80211_set_wiphy_params,
  	.set_tx_power = ieee80211_set_tx_power,
  	.get_tx_power = ieee80211_get_tx_power,
@@ -87,7 +87,7 @@
  	CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
 --- a/net/mac80211/ieee80211_i.h
 +++ b/net/mac80211/ieee80211_i.h
-@@ -1390,6 +1390,7 @@ struct ieee80211_local {
+@@ -1426,6 +1426,7 @@ struct ieee80211_local {
  	int dynamic_ps_forced_timeout;
  
  	int user_power_level; /* in dBm, for all interfaces */
@@ -129,15 +129,15 @@
  	local->hw.max_mtu = IEEE80211_MAX_DATA_LEN;
 --- a/net/wireless/nl80211.c
 +++ b/net/wireless/nl80211.c
-@@ -740,6 +740,7 @@ static const struct nla_policy nl80211_p
- 		NLA_POLICY_RANGE(NLA_U8, NL80211_SAE_PWE_HUNT_AND_PECK,
+@@ -753,6 +753,7 @@ static const struct nla_policy nl80211_p
  				 NL80211_SAE_PWE_BOTH),
+ 	[NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy),
  	[NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT },
 +	[NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
  };
  
  /* policy for the key attributes */
-@@ -3248,6 +3249,20 @@ static int nl80211_set_wiphy(struct sk_b
+@@ -3318,6 +3319,20 @@ static int nl80211_set_wiphy(struct sk_b
  		if (result)
  			return result;
  	}
diff --git a/package/kernel/mac80211/patches/subsys/782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch b/package/kernel/mac80211/patches/subsys/782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch
new file mode 100644
index 0000000000..5d94362155
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/782-net-next-1-of-net-pass-the-dst-buffer-to-of_get_mac_address.patch
@@ -0,0 +1,237 @@
+From 83216e3988cd196183542937c9bd58b279f946af Mon Sep 17 00:00:00 2001
+From: Michael Walle <michael@walle.cc>
+Date: Mon, 12 Apr 2021 19:47:17 +0200
+Subject: of: net: pass the dst buffer to of_get_mac_address()
+
+of_get_mac_address() returns a "const void*" pointer to a MAC address.
+Lately, support to fetch the MAC address by an NVMEM provider was added.
+But this will only work with platform devices. It will not work with
+PCI devices (e.g. of an integrated root complex) and esp. not with DSA
+ports.
+
+There is an of_* variant of the nvmem binding which works without
+devices. The returned data of a nvmem_cell_read() has to be freed after
+use. On the other hand the return of_get_mac_address() points to some
+static data without a lifetime. The trick for now, was to allocate a
+device resource managed buffer which is then returned. This will only
+work if we have an actual device.
+
+Change it, so that the caller of of_get_mac_address() has to supply a
+buffer where the MAC address is written to. Unfortunately, this will
+touch all drivers which use the of_get_mac_address().
+
+Usually the code looks like:
+
+  const char *addr;
+  addr = of_get_mac_address(np);
+  if (!IS_ERR(addr))
+    ether_addr_copy(ndev->dev_addr, addr);
+
+This can then be simply rewritten as:
+
+  of_get_mac_address(np, ndev->dev_addr);
+
+Sometimes is_valid_ether_addr() is used to test the MAC address.
+of_get_mac_address() already makes sure, it just returns a valid MAC
+address. Thus we can just test its return code. But we have to be
+careful if there are still other sources for the MAC address before the
+of_get_mac_address(). In this case we have to keep the
+is_valid_ether_addr() call.
+
+The following coccinelle patch was used to convert common cases to the
+new style. Afterwards, I've manually gone over the drivers and fixed the
+return code variable: either used a new one or if one was already
+available use that. Mansour Moufid, thanks for that coccinelle patch!
+
+<spml>
+@a@
+identifier x;
+expression y, z;
+@@
+- x = of_get_mac_address(y);
++ x = of_get_mac_address(y, z);
+  <...
+- ether_addr_copy(z, x);
+  ...>
+
+@@
+identifier a.x;
+@@
+- if (<+... x ...+>) {}
+
+@@
+identifier a.x;
+@@
+  if (<+... x ...+>) {
+      ...
+  }
+- else {}
+
+@@
+identifier a.x;
+expression e;
+@@
+- if (<+... x ...+>@e)
+-     {}
+- else
++ if (!(e))
+      {...}
+
+@@
+expression x, y, z;
+@@
+- x = of_get_mac_address(y, z);
++ of_get_mac_address(y, z);
+  ... when != x
+</spml>
+
+All drivers, except drivers/net/ethernet/aeroflex/greth.c, were
+compile-time tested.
+
+Suggested-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: Michael Walle <michael@walle.cc>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ arch/arm/mach-mvebu/kirkwood.c                     |  3 +-
+ arch/powerpc/sysdev/tsi108_dev.c                   |  5 +-
+ drivers/net/ethernet/aeroflex/greth.c              |  6 +--
+ drivers/net/ethernet/allwinner/sun4i-emac.c        | 10 ++--
+ drivers/net/ethernet/altera/altera_tse_main.c      |  7 +--
+ drivers/net/ethernet/arc/emac_main.c               |  8 +--
+ drivers/net/ethernet/atheros/ag71xx.c              |  7 +--
+ drivers/net/ethernet/broadcom/bcm4908_enet.c       |  7 +--
+ drivers/net/ethernet/broadcom/bcmsysport.c         |  7 +--
+ drivers/net/ethernet/broadcom/bgmac-bcma.c         | 10 ++--
+ drivers/net/ethernet/broadcom/bgmac-platform.c     | 11 ++--
+ drivers/net/ethernet/cadence/macb_main.c           | 11 ++--
+ drivers/net/ethernet/cavium/octeon/octeon_mgmt.c   |  8 +--
+ drivers/net/ethernet/cavium/thunder/thunder_bgx.c  |  5 +-
+ drivers/net/ethernet/davicom/dm9000.c              | 10 ++--
+ drivers/net/ethernet/ethoc.c                       |  6 +--
+ drivers/net/ethernet/ezchip/nps_enet.c             |  7 +--
+ drivers/net/ethernet/freescale/fec_main.c          |  7 +--
+ drivers/net/ethernet/freescale/fec_mpc52xx.c       |  7 +--
+ drivers/net/ethernet/freescale/fman/mac.c          |  9 ++--
+ .../net/ethernet/freescale/fs_enet/fs_enet-main.c  |  5 +-
+ drivers/net/ethernet/freescale/gianfar.c           |  8 +--
+ drivers/net/ethernet/freescale/ucc_geth.c          |  5 +-
+ drivers/net/ethernet/hisilicon/hisi_femac.c        |  7 +--
+ drivers/net/ethernet/hisilicon/hix5hd2_gmac.c      |  7 +--
+ drivers/net/ethernet/lantiq_xrx200.c               |  7 +--
+ drivers/net/ethernet/marvell/mv643xx_eth.c         |  5 +-
+ drivers/net/ethernet/marvell/mvneta.c              |  6 +--
+ .../net/ethernet/marvell/prestera/prestera_main.c  | 11 ++--
+ drivers/net/ethernet/marvell/pxa168_eth.c          |  9 +---
+ drivers/net/ethernet/marvell/sky2.c                |  8 ++-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c        | 11 ++--
+ drivers/net/ethernet/micrel/ks8851_common.c        |  7 ++-
+ drivers/net/ethernet/microchip/lan743x_main.c      |  5 +-
+ drivers/net/ethernet/nxp/lpc_eth.c                 |  4 +-
+ drivers/net/ethernet/qualcomm/qca_spi.c            | 10 ++--
+ drivers/net/ethernet/qualcomm/qca_uart.c           |  9 +---
+ drivers/net/ethernet/renesas/ravb_main.c           | 12 +++--
+ drivers/net/ethernet/renesas/sh_eth.c              |  5 +-
+ .../net/ethernet/samsung/sxgbe/sxgbe_platform.c    | 13 ++---
+ drivers/net/ethernet/socionext/sni_ave.c           | 10 ++--
+ .../net/ethernet/stmicro/stmmac/dwmac-anarion.c    |  2 +-
+ .../ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c    |  2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-generic.c    |  2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c    |  2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-intel-plat.c |  2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-ipq806x.c    |  2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c    |  2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-mediatek.c   |  2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c  |  2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-meson8b.c    |  2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c  |  2 +-
+ .../ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c    |  2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c     |  2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-socfpga.c    |  2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c    |  2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c  |  2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c  |  2 +-
+ drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c  |  2 +-
+ .../net/ethernet/stmicro/stmmac/dwmac-visconti.c   |  2 +-
+ drivers/net/ethernet/stmicro/stmmac/stmmac.h       |  2 +-
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c  |  2 +-
+ .../net/ethernet/stmicro/stmmac/stmmac_platform.c  | 14 ++---
+ .../net/ethernet/stmicro/stmmac/stmmac_platform.h  |  2 +-
+ drivers/net/ethernet/ti/am65-cpsw-nuss.c           | 19 ++++---
+ drivers/net/ethernet/ti/cpsw.c                     |  7 +--
+ drivers/net/ethernet/ti/cpsw_new.c                 |  7 +--
+ drivers/net/ethernet/ti/davinci_emac.c             |  8 +--
+ drivers/net/ethernet/ti/netcp_core.c               |  7 +--
+ drivers/net/ethernet/wiznet/w5100-spi.c            |  8 ++-
+ drivers/net/ethernet/wiznet/w5100.c                |  2 +-
+ drivers/net/ethernet/xilinx/ll_temac_main.c        |  8 +--
+ drivers/net/ethernet/xilinx/xilinx_axienet_main.c  | 15 +++---
+ drivers/net/ethernet/xilinx/xilinx_emaclite.c      |  8 +--
+ drivers/net/wireless/ath/ath9k/init.c              |  5 +-
+ drivers/net/wireless/mediatek/mt76/eeprom.c        |  9 +---
+ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c     |  6 +--
+ drivers/of/of_net.c                                | 60 ++++++++++------------
+ drivers/staging/octeon/ethernet.c                  | 10 ++--
+ drivers/staging/wfx/main.c                         |  7 ++-
+ include/linux/of_net.h                             |  6 +--
+ include/net/dsa.h                                  |  2 +-
+ net/dsa/dsa2.c                                     |  2 +-
+ net/dsa/slave.c                                    |  2 +-
+ net/ethernet/eth.c                                 | 11 ++--
+ 85 files changed, 218 insertions(+), 364 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -618,7 +618,6 @@ static int ath9k_of_init(struct ath_soft
+ 	struct ath_hw *ah = sc->sc_ah;
+ 	struct ath_common *common = ath9k_hw_common(ah);
+ 	enum ath_bus_type bus_type = common->bus_ops->ath_bus_type;
+-	const char *mac;
+ 	char eeprom_name[100];
+ 	int ret;
+ 
+@@ -641,9 +640,7 @@ static int ath9k_of_init(struct ath_soft
+ 		ah->ah_flags |= AH_NO_EEP_SWAP;
+ 	}
+ 
+-	mac = of_get_mac_address(np);
+-	if (!IS_ERR(mac))
+-		ether_addr_copy(common->macaddr, mac);
++	of_get_mac_address(np, common->macaddr);
+ 
+ 	return 0;
+ }
+--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
++++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
+@@ -90,15 +90,9 @@ out_put_node:
+ void
+ mt76_eeprom_override(struct mt76_dev *dev)
+ {
+-#ifdef CONFIG_OF
+ 	struct device_node *np = dev->dev->of_node;
+-	const u8 *mac = NULL;
+ 
+-	if (np)
+-		mac = of_get_mac_address(np);
+-	if (!IS_ERR_OR_NULL(mac))
+-		ether_addr_copy(dev->macaddr, mac);
+-#endif
++	of_get_mac_address(np, dev->macaddr);
+ 
+ 	if (!is_valid_ether_addr(dev->macaddr)) {
+ 		eth_random_addr(dev->macaddr);
+--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
++++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+@@ -990,11 +990,7 @@ static void rt2x00lib_rate(struct ieee80
+ 
+ void rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr)
+ {
+-	const char *mac_addr;
+-
+-	mac_addr = of_get_mac_address(rt2x00dev->dev->of_node);
+-	if (!IS_ERR(mac_addr))
+-		ether_addr_copy(eeprom_mac_addr, mac_addr);
++	of_get_mac_address(rt2x00dev->dev->of_node, eeprom_mac_addr);
+ 
+ 	if (!is_valid_ether_addr(eeprom_mac_addr)) {
+ 		eth_random_addr(eeprom_mac_addr);
-- 
2.25.1

