[openwrt/openwrt] mac80211: rework encapsulation offload support

LEDE Commits lede-commits at lists.infradead.org
Sat Aug 22 01:02:28 EDT 2020


nbd pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/e7f7101182f84b5ecdbfac5764b642512feef870

commit e7f7101182f84b5ecdbfac5764b642512feef870
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Mon Aug 17 18:27:25 2020 +0200

    mac80211: rework encapsulation offload support
    
    Fix a number of deficiencies in the existing API
    
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 ...80211-rework-tx-encapsulation-offload-API.patch | 651 +++++++++++++++++++++
 ...reduce-duplication-in-tx-status-functions.patch | 197 +++++++
 ...move-tx-status-call-to-ieee80211_sta_regi.patch |  26 +
 ...80211-optimize-station-connection-monitor.patch | 174 ++++++
 ...ap-NEED_TXPROCESSING-and-HW_80211_ENCAP-t.patch | 227 +++++++
 ...ify-802.3-offload-and-802.11-tx-status-co.patch | 159 +++++
 ...d-missing-queue-hash-initialization-to-80.patch |  25 +
 ...eck-and-refresh-aggregation-session-in-en.patch |  45 ++
 ...pport-using-ieee80211_tx_status_ext-to-fr.patch |  63 ++
 ...tend-ieee80211_tx_status_ext-to-support-b.patch |  53 ++
 ...tify-the-driver-when-a-sta-uses-4-address.patch | 109 ++++
 ...ip-encap-offload-for-tx-multicast-control.patch | 158 +++++
 ...t-info-control.hw_key-for-encap-offload-p.patch |  31 +
 .../500-mac80211_configure_antenna_gain.patch      |   6 +-
 14 files changed, 1921 insertions(+), 3 deletions(-)

diff --git a/package/kernel/mac80211/patches/subsys/314-mac80211-rework-tx-encapsulation-offload-API.patch b/package/kernel/mac80211/patches/subsys/314-mac80211-rework-tx-encapsulation-offload-API.patch
new file mode 100644
index 0000000000..22697718f8
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/314-mac80211-rework-tx-encapsulation-offload-API.patch
@@ -0,0 +1,651 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Thu, 13 Aug 2020 15:37:11 +0200
+Subject: [PATCH] mac80211: rework tx encapsulation offload API
+
+The current API (which lets the driver turn on/off per vif directly) has a
+number of limitations:
+- it does not deal with AP_VLAN
+- conditions for enabling (no tkip, no monitor) are only checked at
+  add_interface time
+- no way to indicate 4-addr support
+
+In order to address this, store offload flags in struct ieee80211_vif
+(easy to extend for decap offload later). mac80211 initially sets the enable
+flag, but gives the driver a chance to modify it before its settings are
+applied. In addition to the .add_interface op, a .update_vif_offload op is
+introduced, which can be used for runtime changes.
+
+If a driver can't disable encap offload at runtime, or if it has some extra
+limitations, it can simply override the flags within those ops.
+
+Support for encap offload with 4-address mode interfaces can be enabled
+by setting a flag from .add_interface or .update_vif_offload.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/drivers/net/wireless/ath/ath11k/mac.c
++++ b/drivers/net/wireless/ath/ath11k/mac.c
+@@ -4150,6 +4150,35 @@ static int ath11k_set_he_mu_sounding_mod
+ 	return ret;
+ }
+ 
++static void ath11k_mac_op_update_vif_offload(struct ieee80211_hw *hw,
++					    struct ieee80211_vif *vif)
++{
++	struct ath11k *ar = hw->priv;
++	struct ath11k_base *ab = ar->ab;
++	struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
++	u32 param_id, param_value;
++	int ret;
++
++	param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE;
++	if (ath11k_frame_mode != ATH11K_HW_TXRX_ETHERNET ||
++	    (vif->type != NL80211_IFTYPE_STATION &&
++	     vif->type != NL80211_IFTYPE_AP))
++		vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++
++	if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)
++		param_value = ATH11K_HW_TXRX_ETHERNET;
++	else
++		param_value = ATH11K_HW_TXRX_NATIVE_WIFI;
++
++	ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
++					    param_id, param_value);
++	if (ret) {
++		ath11k_warn(ab, "failed to set vdev %d tx encap mode: %d\n",
++			    arvif->vdev_id, ret);
++		vif->offload_flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++	}
++}
++
+ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
+ 				       struct ieee80211_vif *vif)
+ {
+@@ -4159,7 +4188,6 @@ static int ath11k_mac_op_add_interface(s
+ 	struct vdev_create_params vdev_param = {0};
+ 	struct peer_create_params peer_param;
+ 	u32 param_id, param_value;
+-	int hw_encap = 0;
+ 	u16 nss;
+ 	int i;
+ 	int ret;
+@@ -4253,30 +4281,7 @@ static int ath11k_mac_op_add_interface(s
+ 	list_add(&arvif->list, &ar->arvifs);
+ 	spin_unlock_bh(&ar->data_lock);
+ 
+-	param_id = WMI_VDEV_PARAM_TX_ENCAP_TYPE;
+-	if (ath11k_frame_mode == ATH11K_HW_TXRX_ETHERNET)
+-		switch (vif->type) {
+-		case NL80211_IFTYPE_STATION:
+-		case NL80211_IFTYPE_AP_VLAN:
+-		case NL80211_IFTYPE_AP:
+-			hw_encap = 1;
+-			break;
+-		default:
+-			break;
+-		}
+-
+-	if (ieee80211_set_hw_80211_encap(vif, hw_encap))
+-		param_value = ATH11K_HW_TXRX_ETHERNET;
+-	else
+-		param_value = ATH11K_HW_TXRX_NATIVE_WIFI;
+-
+-	ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
+-					    param_id, param_value);
+-	if (ret) {
+-		ath11k_warn(ab, "failed to set vdev %d tx encap mode: %d\n",
+-			    arvif->vdev_id, ret);
+-		goto err_vdev_del;
+-	}
++	ath11k_mac_op_update_vif_offload(hw, vif);
+ 
+ 	nss = get_num_chains(ar->cfg_tx_chainmask) ? : 1;
+ 	ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
+@@ -5599,6 +5604,7 @@ static const struct ieee80211_ops ath11k
+ 	.reconfig_complete              = ath11k_mac_op_reconfig_complete,
+ 	.add_interface                  = ath11k_mac_op_add_interface,
+ 	.remove_interface		= ath11k_mac_op_remove_interface,
++	.update_vif_offload		= ath11k_mac_op_update_vif_offload,
+ 	.config                         = ath11k_mac_op_config,
+ 	.bss_info_changed               = ath11k_mac_op_bss_info_changed,
+ 	.configure_filter		= ath11k_mac_op_configure_filter,
+@@ -5852,6 +5858,7 @@ static int __ath11k_mac_register(struct
+ 	ieee80211_hw_set(ar->hw, QUEUE_CONTROL);
+ 	ieee80211_hw_set(ar->hw, SUPPORTS_TX_FRAG);
+ 	ieee80211_hw_set(ar->hw, REPORTS_LOW_ACK);
++	ieee80211_hw_set(ar->hw, SUPPORTS_TX_ENCAP_OFFLOAD);
+ 	if (ht_cap & WMI_HT_CAP_ENABLED) {
+ 		ieee80211_hw_set(ar->hw, AMPDU_AGGREGATION);
+ 		ieee80211_hw_set(ar->hw, TX_AMPDU_SETUP_IN_HW);
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1603,6 +1603,21 @@ enum ieee80211_vif_flags {
+ 	IEEE80211_VIF_GET_NOA_UPDATE		= BIT(3),
+ };
+ 
++
++/**
++ * enum ieee80211_offload_flags - virtual interface offload flags
++ *
++ * @IEEE80211_OFFLOAD_ENCAP_ENABLED: tx encapsulation offload is enabled
++ *	The driver supports sending frames passed as 802.3 frames by mac80211.
++ *	It must also support sending 802.11 packets for the same interface.
++ * @IEEE80211_OFFLOAD_ENCAP_4ADDR: support 4-address mode encapsulation offload
++ */
++
++enum ieee80211_offload_flags {
++	IEEE80211_OFFLOAD_ENCAP_ENABLED		= BIT(0),
++	IEEE80211_OFFLOAD_ENCAP_4ADDR		= BIT(1),
++};
++
+ /**
+  * struct ieee80211_vif - per-interface data
+  *
+@@ -1623,6 +1638,11 @@ enum ieee80211_vif_flags {
+  *	these need to be set (or cleared) when the interface is added
+  *	or, if supported by the driver, the interface type is changed
+  *	at runtime, mac80211 will never touch this field
++ * @offloaad_flags: hardware offload capabilities/flags for this interface.
++ *	These are initialized by mac80211 before calling .add_interface,
++ *	.change_interface or .update_vif_offload and updated by the driver
++ *	within these ops, based on supported features or runtime change
++ *	restrictions.
+  * @hw_queue: hardware queue for each AC
+  * @cab_queue: content-after-beacon (DTIM beacon really) queue, AP mode only
+  * @chanctx_conf: The channel context this interface is assigned to, or %NULL
+@@ -1659,6 +1679,7 @@ struct ieee80211_vif {
+ 	struct ieee80211_chanctx_conf __rcu *chanctx_conf;
+ 
+ 	u32 driver_flags;
++	u32 offload_flags;
+ 
+ #ifdef CPTCFG_MAC80211_DEBUGFS
+ 	struct dentry *debugfs_dir;
+@@ -2325,6 +2346,9 @@ struct ieee80211_txq {
+  *	aggregating MPDUs with the same keyid, allowing mac80211 to keep Tx
+  *	A-MPDU sessions active while rekeying with Extended Key ID.
+  *
++ * @IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD: Hardware supports tx encapsulation
++ *	offload
++ *
+  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
+  */
+ enum ieee80211_hw_flags {
+@@ -2377,6 +2401,7 @@ enum ieee80211_hw_flags {
+ 	IEEE80211_HW_SUPPORTS_MULTI_BSSID,
+ 	IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
+ 	IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT,
++	IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
+ 
+ 	/* keep last, obviously */
+ 	NUM_IEEE80211_HW_FLAGS
+@@ -3811,6 +3836,8 @@ enum ieee80211_reconfig_type {
+  * @set_tid_config: Apply TID specific configurations. This callback may sleep.
+  * @reset_tid_config: Reset TID specific configuration for the peer.
+  *	This callback may sleep.
++ * @update_vif_config: Update virtual interface offload flags
++ *	This callback may sleep.
+  */
+ struct ieee80211_ops {
+ 	void (*tx)(struct ieee80211_hw *hw,
+@@ -4122,6 +4149,8 @@ struct ieee80211_ops {
+ 	int (*reset_tid_config)(struct ieee80211_hw *hw,
+ 				struct ieee80211_vif *vif,
+ 				struct ieee80211_sta *sta, u8 tids);
++	void (*update_vif_offload)(struct ieee80211_hw *hw,
++				   struct ieee80211_vif *vif);
+ };
+ 
+ /**
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -504,6 +504,7 @@ static int ieee80211_del_key(struct wiph
+ 	struct ieee80211_local *local = sdata->local;
+ 	struct sta_info *sta;
+ 	struct ieee80211_key *key = NULL;
++	bool recalc_offload = false;
+ 	int ret;
+ 
+ 	mutex_lock(&local->sta_mtx);
+@@ -528,6 +529,7 @@ static int ieee80211_del_key(struct wiph
+ 		goto out_unlock;
+ 	}
+ 
++	recalc_offload = key->conf.cipher == WLAN_CIPHER_SUITE_TKIP;
+ 	ieee80211_key_free(key, sdata->vif.type == NL80211_IFTYPE_STATION);
+ 
+ 	ret = 0;
+@@ -535,6 +537,9 @@ static int ieee80211_del_key(struct wiph
+ 	mutex_unlock(&local->key_mtx);
+ 	mutex_unlock(&local->sta_mtx);
+ 
++	if (recalc_offload)
++		ieee80211_recalc_offload(local);
++
+ 	return ret;
+ }
+ 
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -408,6 +408,7 @@ static const char *hw_flag_names[] = {
+ 	FLAG(SUPPORTS_MULTI_BSSID),
+ 	FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
+ 	FLAG(AMPDU_KEYBORDER_SUPPORT),
++	FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
+ #undef FLAG
+ };
+ 
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -1385,4 +1385,19 @@ static inline int drv_reset_tid_config(s
+ 
+ 	return ret;
+ }
++
++static inline void drv_update_vif_offload(struct ieee80211_local *local,
++					  struct ieee80211_sub_if_data *sdata)
++{
++	might_sleep();
++	check_sdata_in_driver(sdata);
++
++	if (!local->ops->update_vif_offload)
++		return;
++
++	trace_drv_update_vif_offload(local, sdata);
++	local->ops->update_vif_offload(&local->hw, &sdata->vif);
++	trace_drv_return_void(local);
++}
++
+ #endif /* __MAC80211_DRIVER_OPS */
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -990,8 +990,6 @@ struct ieee80211_sub_if_data {
+ 	} debugfs;
+ #endif
+ 
+-	bool hw_80211_encap;
+-
+ 	/* must be last, dynamically sized area in this! */
+ 	struct ieee80211_vif vif;
+ };
+@@ -1769,6 +1767,7 @@ void ieee80211_del_virtual_monitor(struc
+ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+ 			      bool update_bss);
++void ieee80211_recalc_offload(struct ieee80211_local *local);
+ 
+ static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
+ {
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -43,6 +43,7 @@
+  */
+ 
+ static void ieee80211_iface_work(struct work_struct *work);
++static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata);
+ 
+ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+ {
+@@ -348,6 +349,99 @@ static int ieee80211_check_queues(struct
+ 	return 0;
+ }
+ 
++static bool ieee80211_iftype_supports_encap_offload(enum nl80211_iftype iftype)
++{
++	switch (iftype) {
++	case NL80211_IFTYPE_AP:
++	case NL80211_IFTYPE_P2P_GO:
++	case NL80211_IFTYPE_P2P_CLIENT:
++	case NL80211_IFTYPE_STATION:
++		return true;
++	default:
++		return false;
++	}
++}
++
++static bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdata)
++{
++	struct ieee80211_local *local = sdata->local;
++	struct ieee80211_key *key;
++	u32 flags;
++
++	flags = sdata->vif.offload_flags;
++
++	if (ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) &&
++	    ieee80211_iftype_supports_encap_offload(sdata->vif.type)) {
++		flags |= IEEE80211_OFFLOAD_ENCAP_ENABLED;
++		mutex_lock(&local->key_mtx);
++		list_for_each_entry(key, &sdata->key_list, list) {
++			if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
++			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
++			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 ||
++			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256)
++				continue;
++			if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP ||
++			    !(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
++				flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++		}
++		mutex_unlock(&local->key_mtx);
++
++		if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
++		    local->hw.wiphy->frag_threshold != (u32)-1)
++			flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++
++		if (local->monitors)
++			flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++	} else {
++		flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
++	}
++
++	if (sdata->vif.offload_flags == flags)
++		return false;
++
++	sdata->vif.offload_flags = flags;
++	return true;
++}
++
++
++static void ieee80211_recalc_sdata_offload(struct ieee80211_sub_if_data *sdata)
++{
++	struct ieee80211_local *local = sdata->local;
++	struct ieee80211_sub_if_data *vsdata;
++
++	if (ieee80211_set_sdata_offload_flags(sdata)) {
++		drv_update_vif_offload(local, sdata);
++		ieee80211_set_vif_encap_ops(sdata);
++	}
++
++	list_for_each_entry(vsdata, &local->interfaces, list) {
++		if (vsdata->vif.type != NL80211_IFTYPE_AP_VLAN ||
++		    vsdata->bss != &sdata->u.ap)
++			continue;
++
++		ieee80211_set_vif_encap_ops(vsdata);
++	}
++}
++
++void ieee80211_recalc_offload(struct ieee80211_local *local)
++{
++	struct ieee80211_sub_if_data *sdata;
++
++	if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD))
++		return;
++
++	mutex_lock(&local->iflist_mtx);
++
++	list_for_each_entry(sdata, &local->interfaces, list) {
++		if (!ieee80211_sdata_running(sdata))
++			continue;
++
++		ieee80211_recalc_sdata_offload(sdata);
++	}
++
++	mutex_unlock(&local->iflist_mtx);
++}
++
+ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
+ 				    const int offset)
+ {
+@@ -587,6 +681,7 @@ int ieee80211_do_open(struct wireless_de
+ 		if (rtnl_dereference(sdata->bss->beacon)) {
+ 			ieee80211_vif_vlan_copy_chanctx(sdata);
+ 			netif_carrier_on(dev);
++			ieee80211_set_vif_encap_ops(sdata);
+ 		} else {
+ 			netif_carrier_off(dev);
+ 		}
+@@ -616,6 +711,7 @@ int ieee80211_do_open(struct wireless_de
+ 
+ 		ieee80211_adjust_monitor_flags(sdata, 1);
+ 		ieee80211_configure_filter(local);
++		ieee80211_recalc_offload(local);
+ 		mutex_lock(&local->mtx);
+ 		ieee80211_recalc_idle(local);
+ 		mutex_unlock(&local->mtx);
+@@ -625,10 +721,13 @@ int ieee80211_do_open(struct wireless_de
+ 	default:
+ 		if (coming_up) {
+ 			ieee80211_del_virtual_monitor(local);
++			ieee80211_set_sdata_offload_flags(sdata);
+ 
+ 			res = drv_add_interface(local, sdata);
+ 			if (res)
+ 				goto err_stop;
++
++			ieee80211_set_vif_encap_ops(sdata);
+ 			res = ieee80211_check_queues(sdata,
+ 				ieee80211_vif_type_p2p(&sdata->vif));
+ 			if (res)
+@@ -1286,61 +1385,6 @@ static const struct net_device_ops ieee8
+ 
+ };
+ 
+-static void __ieee80211_set_hw_80211_encap(struct ieee80211_sub_if_data *sdata,
+-					   bool enable)
+-{
+-	sdata->dev->netdev_ops = enable ? &ieee80211_dataif_8023_ops :
+-					  &ieee80211_dataif_ops;
+-	sdata->hw_80211_encap = enable;
+-}
+-
+-bool ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable)
+-{
+-	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+-	struct ieee80211_local *local = sdata->local;
+-	struct ieee80211_sub_if_data *iter;
+-	struct ieee80211_key *key;
+-
+-	mutex_lock(&local->iflist_mtx);
+-	list_for_each_entry(iter, &local->interfaces, list) {
+-		struct ieee80211_sub_if_data *disable = NULL;
+-
+-		if (vif->type == NL80211_IFTYPE_MONITOR) {
+-			disable = iter;
+-			__ieee80211_set_hw_80211_encap(iter, false);
+-		} else if (iter->vif.type == NL80211_IFTYPE_MONITOR) {
+-			disable = sdata;
+-			enable = false;
+-		}
+-		if (disable)
+-			sdata_dbg(disable,
+-				  "disable hw 80211 encap due to mon co-exist\n");
+-	}
+-	mutex_unlock(&local->iflist_mtx);
+-
+-	if (enable == sdata->hw_80211_encap)
+-		return enable;
+-
+-	if (!sdata->dev)
+-		return false;
+-
+-	if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
+-	    (local->hw.wiphy->frag_threshold != (u32)-1))
+-		enable = false;
+-
+-	mutex_lock(&sdata->local->key_mtx);
+-	list_for_each_entry(key, &sdata->key_list, list) {
+-		if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)
+-			enable = false;
+-	}
+-	mutex_unlock(&sdata->local->key_mtx);
+-
+-	__ieee80211_set_hw_80211_encap(sdata, enable);
+-
+-	return enable;
+-}
+-EXPORT_SYMBOL(ieee80211_set_hw_80211_encap);
+-
+ static void ieee80211_if_free(struct net_device *dev)
+ {
+ 	free_percpu(netdev_tstats(dev));
+@@ -1371,6 +1415,51 @@ static void ieee80211_if_setup_no_queue(
+ #endif
+ }
+ 
++static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata)
++{
++	struct ieee80211_local *local = sdata->local;
++	struct ieee80211_sub_if_data *bss = sdata;
++	struct ieee80211_key *key;
++	bool enabled;
++
++	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
++		if (!sdata->bss)
++			return;
++
++		bss = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
++	}
++
++	if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) ||
++	    !ieee80211_iftype_supports_encap_offload(bss->vif.type))
++		return;
++
++	enabled = bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED;
++	if (sdata->wdev.use_4addr &&
++	    !(bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_4ADDR))
++		enabled = false;
++
++	/*
++	 * Encapsulation offload cannot be used with software crypto, and a per-VLAN
++	 * key may have been set
++	 */
++	if (enabled && sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
++		mutex_lock(&local->key_mtx);
++		list_for_each_entry(key, &sdata->key_list, list) {
++			if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
++			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
++			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 ||
++			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256)
++				continue;
++			if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
++				enabled = false;
++		}
++		mutex_unlock(&local->key_mtx);
++	}
++
++	sdata->dev->netdev_ops = enabled ? &ieee80211_dataif_8023_ops :
++					   &ieee80211_dataif_ops;
++}
++
+ static void ieee80211_iface_work(struct work_struct *work)
+ {
+ 	struct ieee80211_sub_if_data *sdata =
+@@ -1553,7 +1642,6 @@ static void ieee80211_setup_sdata(struct
+ 	sdata->vif.bss_conf.txpower = INT_MIN; /* unset */
+ 
+ 	sdata->noack_map = 0;
+-	sdata->hw_80211_encap = false;
+ 
+ 	/* only monitor/p2p-device differ */
+ 	if (sdata->dev) {
+@@ -1688,6 +1776,7 @@ static int ieee80211_runtime_change_ifty
+ 
+ 	ieee80211_teardown_sdata(sdata);
+ 
++	ieee80211_set_sdata_offload_flags(sdata);
+ 	ret = drv_change_interface(local, sdata, internal_type, p2p);
+ 	if (ret)
+ 		type = ieee80211_vif_type_p2p(&sdata->vif);
+@@ -1700,6 +1789,7 @@ static int ieee80211_runtime_change_ifty
+ 	ieee80211_check_queues(sdata, type);
+ 
+ 	ieee80211_setup_sdata(sdata, type);
++	ieee80211_set_vif_encap_ops(sdata);
+ 
+ 	err = ieee80211_do_open(&sdata->wdev, false);
+ 	WARN(err, "type change: do_open returned %d", err);
+--- a/net/mac80211/key.c
++++ b/net/mac80211/key.c
+@@ -177,13 +177,6 @@ static int ieee80211_key_enable_hw_accel
+ 		}
+ 	}
+ 
+-	/* TKIP countermeasures don't work in encap offload mode */
+-	if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP &&
+-	    sdata->hw_80211_encap) {
+-		sdata_dbg(sdata, "TKIP is not allowed in hw 80211 encap mode\n");
+-		return -EINVAL;
+-	}
+-
+ 	ret = drv_set_key(key->local, SET_KEY, sdata,
+ 			  sta ? &sta->sta : NULL, &key->conf);
+ 
+@@ -219,14 +212,6 @@ static int ieee80211_key_enable_hw_accel
+ 	case WLAN_CIPHER_SUITE_CCMP_256:
+ 	case WLAN_CIPHER_SUITE_GCMP:
+ 	case WLAN_CIPHER_SUITE_GCMP_256:
+-		/* We cannot do software crypto of data frames with
+-		 * encapsulation offload enabled. However for 802.11w to
+-		 * function properly we need cmac/gmac keys.
+-		 */
+-		if (sdata->hw_80211_encap)
+-			return -EINVAL;
+-		/* Fall through */
+-
+ 	case WLAN_CIPHER_SUITE_AES_CMAC:
+ 	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ 	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+@@ -824,6 +809,7 @@ int ieee80211_key_link(struct ieee80211_
+ 	 */
+ 	bool delay_tailroom = sdata->vif.type == NL80211_IFTYPE_STATION;
+ 	int ret = -EOPNOTSUPP;
++	bool recalc_offload = false;
+ 
+ 	mutex_lock(&sdata->local->key_mtx);
+ 
+@@ -864,11 +850,15 @@ int ieee80211_key_link(struct ieee80211_
+ 	key->local = sdata->local;
+ 	key->sdata = sdata;
+ 	key->sta = sta;
++	recalc_offload = !old_key && key->conf.cipher == WLAN_CIPHER_SUITE_TKIP;
+ 
+ 	increment_tailroom_need_count(sdata);
+ 
+ 	ret = ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
+ 
++	if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
++		recalc_offload = true;
++
+ 	if (!ret) {
+ 		ieee80211_debugfs_key_add(key);
+ 		ieee80211_key_destroy(old_key, delay_tailroom);
+@@ -879,6 +869,9 @@ int ieee80211_key_link(struct ieee80211_
+  out:
+ 	mutex_unlock(&sdata->local->key_mtx);
+ 
++	if (recalc_offload)
++		ieee80211_recalc_offload(sdata->local);
++
+ 	return ret;
+ }
+ 
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -2733,6 +2733,12 @@ TRACE_EVENT(drv_get_ftm_responder_stats,
+ 	)
+ );
+ 
++DEFINE_EVENT(local_sdata_addr_evt, drv_update_vif_offload,
++	TP_PROTO(struct ieee80211_local *local,
++		 struct ieee80211_sub_if_data *sdata),
++	TP_ARGS(local, sdata)
++);
++
+ #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
+ 
+ #undef TRACE_INCLUDE_PATH
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4270,11 +4270,6 @@ netdev_tx_t ieee80211_subif_start_xmit_8
+ 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ 	struct sta_info *sta;
+ 
+-	if (WARN_ON(!sdata->hw_80211_encap)) {
+-		kfree_skb(skb);
+-		return NETDEV_TX_OK;
+-	}
+-
+ 	if (unlikely(skb->len < ETH_HLEN)) {
+ 		kfree_skb(skb);
+ 		return NETDEV_TX_OK;
diff --git a/package/kernel/mac80211/patches/subsys/315-mac80211-reduce-duplication-in-tx-status-functions.patch b/package/kernel/mac80211/patches/subsys/315-mac80211-reduce-duplication-in-tx-status-functions.patch
new file mode 100644
index 0000000000..8b664d6895
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/315-mac80211-reduce-duplication-in-tx-status-functions.patch
@@ -0,0 +1,197 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Mon, 17 Aug 2020 13:16:59 +0200
+Subject: [PATCH] mac80211: reduce duplication in tx status functions
+
+Move redundant functionality from __ieee80211_tx_status into
+ieee80211_tx_status_ext. Preparation for unifying with the 802.3 tx status
+codepath.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -184,18 +184,6 @@ static void ieee80211_frame_acked(struct
+ 	struct ieee80211_mgmt *mgmt = (void *) skb->data;
+ 	struct ieee80211_local *local = sta->local;
+ 	struct ieee80211_sub_if_data *sdata = sta->sdata;
+-	struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+-
+-	if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
+-		sta->status_stats.last_ack = jiffies;
+-		if (txinfo->status.is_valid_ack_signal) {
+-			sta->status_stats.last_ack_signal =
+-					 (s8)txinfo->status.ack_signal;
+-			sta->status_stats.ack_signal_filled = true;
+-			ewma_avg_signal_add(&sta->status_stats.avg_ack_signal,
+-					    -txinfo->status.ack_signal);
+-		}
+-	}
+ 
+ 	if (ieee80211_is_data_qos(mgmt->frame_control)) {
+ 		struct ieee80211_hdr *hdr = (void *) skb->data;
+@@ -899,7 +887,8 @@ void ieee80211_tx_monitor(struct ieee802
+ }
+ 
+ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
+-				  struct ieee80211_tx_status *status)
++				  struct ieee80211_tx_status *status,
++				  int rates_idx, int retry_count)
+ {
+ 	struct sk_buff *skb = status->skb;
+ 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+@@ -908,8 +897,6 @@ static void __ieee80211_tx_status(struct
+ 	struct sta_info *sta;
+ 	__le16 fc;
+ 	struct ieee80211_supported_band *sband;
+-	int retry_count;
+-	int rates_idx;
+ 	bool send_to_cooked;
+ 	bool acked;
+ 	bool noack_success;
+@@ -918,8 +905,6 @@ static void __ieee80211_tx_status(struct
+ 	int tid = IEEE80211_NUM_TIDS;
+ 	u16 tx_time_est;
+ 
+-	rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+-
+ 	sband = local->hw.wiphy->bands[info->band];
+ 	fc = hdr->frame_control;
+ 
+@@ -996,24 +981,14 @@ static void __ieee80211_tx_status(struct
+ 		if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
+ 			ieee80211_handle_filtered_frame(local, sta, skb);
+ 			return;
+-		} else {
++		} else if (ieee80211_is_data_present(fc)) {
+ 			if (!acked && !noack_success)
+-				sta->status_stats.retry_failed++;
+-			sta->status_stats.retry_count += retry_count;
+-
+-			if (ieee80211_is_data_present(fc)) {
+-				if (!acked && !noack_success)
+-					sta->status_stats.msdu_failed[tid]++;
++				sta->status_stats.msdu_failed[tid]++;
+ 
+-				sta->status_stats.msdu_retries[tid] +=
+-					retry_count;
+-			}
++			sta->status_stats.msdu_retries[tid] +=
++				retry_count;
+ 		}
+ 
+-		rate_control_tx_status(local, sband, status);
+-		if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+-			ieee80211s_update_metric(local, sta, status);
+-
+ 		if (!(info->flags & IEEE80211_TX_CTL_INJECTED) && acked)
+ 			ieee80211_frame_acked(sta, skb);
+ 
+@@ -1038,20 +1013,6 @@ static void __ieee80211_tx_status(struct
+ 							     true);
+ 			ieee80211_info_set_tx_time_est(info, 0);
+ 		}
+-
+-		if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
+-			if (acked) {
+-				if (sta->status_stats.lost_packets)
+-					sta->status_stats.lost_packets = 0;
+-
+-				/* Track when last TDLS packet was ACKed */
+-				sta->status_stats.last_pkt_time = jiffies;
+-			} else if (noack_success) {
+-				/* nothing to do here, do not account as lost */
+-			} else {
+-				ieee80211_lost_packet(sta, info);
+-			}
+-		}
+ 	}
+ 
+ 	/* SNMP counters
+@@ -1135,7 +1096,7 @@ void ieee80211_tx_status(struct ieee8021
+ 	if (sta)
+ 		status.sta = &sta->sta;
+ 
+-	__ieee80211_tx_status(hw, &status);
++	ieee80211_tx_status_ext(hw, &status);
+ 	rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(ieee80211_tx_status);
+@@ -1148,7 +1109,7 @@ void ieee80211_tx_status_ext(struct ieee
+ 	struct ieee80211_sta *pubsta = status->sta;
+ 	struct ieee80211_supported_band *sband;
+ 	struct sta_info *sta;
+-	int retry_count;
++	int rates_idx, retry_count;
+ 	bool acked, noack_success;
+ 
+ 	if (pubsta) {
+@@ -1158,13 +1119,7 @@ void ieee80211_tx_status_ext(struct ieee
+ 			sta->tx_stats.last_rate_info = *status->rate;
+ 	}
+ 
+-	if (status->skb)
+-		return __ieee80211_tx_status(hw, status);
+-
+-	if (!status->sta)
+-		return;
+-
+-	ieee80211_tx_get_rates(hw, info, &retry_count);
++	rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+ 
+ 	sband = hw->wiphy->bands[info->band];
+ 
+@@ -1176,20 +1131,30 @@ void ieee80211_tx_status_ext(struct ieee
+ 			sta->status_stats.retry_failed++;
+ 		sta->status_stats.retry_count += retry_count;
+ 
+-		if (acked) {
+-			sta->status_stats.last_ack = jiffies;
++		if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
++			if (acked) {
++				sta->status_stats.last_ack = jiffies;
+ 
+-			if (sta->status_stats.lost_packets)
+-				sta->status_stats.lost_packets = 0;
++				if (sta->status_stats.lost_packets)
++					sta->status_stats.lost_packets = 0;
+ 
+-			/* Track when last packet was ACKed */
+-			sta->status_stats.last_pkt_time = jiffies;
+-		} else if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
+-			return;
+-		} else if (noack_success) {
+-			/* nothing to do here, do not account as lost */
+-		} else {
+-			ieee80211_lost_packet(sta, info);
++				/* Track when last packet was ACKed */
++				sta->status_stats.last_pkt_time = jiffies;
++
++				if (info->status.is_valid_ack_signal) {
++					sta->status_stats.last_ack_signal =
++							 (s8)info->status.ack_signal;
++					sta->status_stats.ack_signal_filled = true;
++					ewma_avg_signal_add(&sta->status_stats.avg_ack_signal,
++							    -info->status.ack_signal);
++				}
++			} else if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
++				return;
++			} else if (noack_success) {
++				/* nothing to do here, do not account as lost */
++			} else {
++				ieee80211_lost_packet(sta, info);
++			}
+ 		}
+ 
+ 		rate_control_tx_status(local, sband, status);
+@@ -1197,6 +1162,10 @@ void ieee80211_tx_status_ext(struct ieee
+ 			ieee80211s_update_metric(local, sta, status);
+ 	}
+ 
++	if (status->skb)
++		return __ieee80211_tx_status(hw, status, rates_idx,
++					     retry_count);
++
+ 	if (acked || noack_success) {
+ 		I802_DEBUG_INC(local->dot11TransmittedFrameCount);
+ 		if (!pubsta)
diff --git a/package/kernel/mac80211/patches/subsys/316-mac80211-remove-tx-status-call-to-ieee80211_sta_regi.patch b/package/kernel/mac80211/patches/subsys/316-mac80211-remove-tx-status-call-to-ieee80211_sta_regi.patch
new file mode 100644
index 0000000000..168d6458a5
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/316-mac80211-remove-tx-status-call-to-ieee80211_sta_regi.patch
@@ -0,0 +1,26 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Mon, 17 Aug 2020 13:29:12 +0200
+Subject: [PATCH] mac80211: remove tx status call to
+ ieee80211_sta_register_airtime
+
+All drivers using airtime fairness are calling ieee80211_sta_register_airtime
+directly
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -997,12 +997,6 @@ static void __ieee80211_tx_status(struct
+ 			ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
+ 						acked, info->status.tx_time);
+ 
+-		if (info->status.tx_time &&
+-		    wiphy_ext_feature_isset(local->hw.wiphy,
+-					    NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+-			ieee80211_sta_register_airtime(&sta->sta, tid,
+-						       info->status.tx_time, 0);
+-
+ 		if ((tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
+ 			/* Do this here to avoid the expensive lookup of the sta
+ 			 * in ieee80211_report_used_skb().
diff --git a/package/kernel/mac80211/patches/subsys/317-mac80211-optimize-station-connection-monitor.patch b/package/kernel/mac80211/patches/subsys/317-mac80211-optimize-station-connection-monitor.patch
new file mode 100644
index 0000000000..ed9efb2b0d
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/317-mac80211-optimize-station-connection-monitor.patch
@@ -0,0 +1,174 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Mon, 17 Aug 2020 13:29:56 +0200
+Subject: [PATCH] mac80211: optimize station connection monitor
+
+Calling mod_timer for every rx/tx packet can be quite expensive.
+Instead of constantly updating the timer, we can simply let it run out
+and check the timestamp of the last ACK or rx packet to re-arm it.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -2045,8 +2045,6 @@ 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_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
+-			     struct ieee80211_hdr *hdr);
+ 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
+@@ -2432,23 +2432,6 @@ static void ieee80211_set_disassoc(struc
+ 	sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
+ }
+ 
+-void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
+-			     struct ieee80211_hdr *hdr)
+-{
+-	/*
+-	 * We can postpone the mgd.timer whenever receiving unicast frames
+-	 * from AP because we know that the connection is working both ways
+-	 * at that time. But multicast frames (and hence also beacons) must
+-	 * be ignored here, because we need to trigger the timer during
+-	 * data idle periods for sending the periodic probe request to the
+-	 * AP we're connected to.
+-	 */
+-	if (is_multicast_ether_addr(hdr->addr1))
+-		return;
+-
+-	ieee80211_sta_reset_conn_monitor(sdata);
+-}
+-
+ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
+ {
+ 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+@@ -2521,21 +2504,13 @@ void ieee80211_sta_tx_notify(struct ieee
+ {
+ 	ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
+ 
+-	if (!ieee80211_is_data(hdr->frame_control))
+-	    return;
+-
+-	if (ieee80211_is_any_nullfunc(hdr->frame_control) &&
+-	    sdata->u.mgd.probe_send_count > 0) {
+-		if (ack)
+-			ieee80211_sta_reset_conn_monitor(sdata);
+-		else
+-			sdata->u.mgd.nullfunc_failed = true;
+-		ieee80211_queue_work(&sdata->local->hw, &sdata->work);
++	if (!ieee80211_is_any_nullfunc(hdr->frame_control) ||
++	    !sdata->u.mgd.probe_send_count)
+ 		return;
+-	}
+ 
+-	if (ack)
+-		ieee80211_sta_reset_conn_monitor(sdata);
++	if (!ack)
++		sdata->u.mgd.nullfunc_failed = true;
++	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+ }
+ 
+ static void ieee80211_mlme_send_probe_req(struct ieee80211_sub_if_data *sdata,
+@@ -3600,8 +3575,8 @@ static bool ieee80211_assoc_success(stru
+ 	 * Start timer to probe the connection to the AP now.
+ 	 * Also start the timer that will detect beacon loss.
+ 	 */
+-	ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
+ 	ieee80211_sta_reset_beacon_monitor(sdata);
++	ieee80211_sta_reset_conn_monitor(sdata);
+ 
+ 	ret = true;
+  out:
+@@ -4569,10 +4544,26 @@ static void ieee80211_sta_conn_mon_timer
+ 		from_timer(sdata, t, u.mgd.conn_mon_timer);
+ 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ 	struct ieee80211_local *local = sdata->local;
++	struct sta_info *sta;
++	unsigned long timeout;
+ 
+ 	if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
+ 		return;
+ 
++	sta = sta_info_get(sdata, ifmgd->bssid);
++	if (!sta)
++		return;
++
++	timeout = sta->status_stats.last_ack;
++	if (time_before(sta->status_stats.last_ack, sta->rx_stats.last_rx))
++		timeout = sta->rx_stats.last_rx;
++	timeout += IEEE80211_CONNECTION_IDLE_TIME;
++
++	if (time_is_before_jiffies(timeout)) {
++		mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(timeout));
++		return;
++	}
++
+ 	ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
+ }
+ 
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -1811,9 +1811,6 @@ ieee80211_rx_h_sta_process(struct ieee80
+ 			sta->rx_stats.last_rate = sta_stats_encode_rate(status);
+ 	}
+ 
+-	if (rx->sdata->vif.type == NL80211_IFTYPE_STATION)
+-		ieee80211_sta_rx_notify(rx->sdata, hdr);
+-
+ 	sta->rx_stats.fragments++;
+ 
+ 	u64_stats_update_begin(&rx->sta->rx_stats.syncp);
+@@ -4148,7 +4145,6 @@ void ieee80211_check_fast_rx(struct sta_
+ 			fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr2);
+ 			fastrx.expected_ds_bits = 0;
+ 		} else {
+-			fastrx.sta_notify = sdata->u.mgd.probe_send_count > 0;
+ 			fastrx.da_offs = offsetof(struct ieee80211_hdr, addr1);
+ 			fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr3);
+ 			fastrx.expected_ds_bits =
+@@ -4378,11 +4374,6 @@ static bool ieee80211_invoke_fast_rx(str
+ 	    pskb_trim(skb, skb->len - fast_rx->icv_len))
+ 		goto drop;
+ 
+-	if (unlikely(fast_rx->sta_notify)) {
+-		ieee80211_sta_rx_notify(rx->sdata, hdr);
+-		fast_rx->sta_notify = false;
+-	}
+-
+ 	/* statistics part of ieee80211_rx_h_sta_process() */
+ 	if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
+ 		stats->last_signal = status->signal;
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -336,7 +336,6 @@ struct ieee80211_fast_tx {
+  * @expected_ds_bits: from/to DS bits expected
+  * @icv_len: length of the MIC if present
+  * @key: bool indicating encryption is expected (key is set)
+- * @sta_notify: notify the MLME code (once)
+  * @internal_forward: forward froms internally on AP/VLAN type interfaces
+  * @uses_rss: copy of USES_RSS hw flag
+  * @da_offs: offset of the DA in the header (for header conversion)
+@@ -352,7 +351,6 @@ struct ieee80211_fast_rx {
+ 	__le16 expected_ds_bits;
+ 	u8 icv_len;
+ 	u8 key:1,
+-	   sta_notify:1,
+ 	   internal_forward:1,
+ 	   uses_rss:1;
+ 	u8 da_offs, sa_offs;
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -1227,9 +1227,6 @@ void ieee80211_tx_status_8023(struct iee
+ 	sta->status_stats.retry_count += retry_count;
+ 
+ 	if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
+-		if (acked && vif->type == NL80211_IFTYPE_STATION)
+-			ieee80211_sta_reset_conn_monitor(sdata);
+-
+ 		sta->status_stats.last_ack = jiffies;
+ 		if (info->flags & IEEE80211_TX_STAT_ACK) {
+ 			if (sta->status_stats.lost_packets)
diff --git a/package/kernel/mac80211/patches/subsys/318-mac80211-swap-NEED_TXPROCESSING-and-HW_80211_ENCAP-t.patch b/package/kernel/mac80211/patches/subsys/318-mac80211-swap-NEED_TXPROCESSING-and-HW_80211_ENCAP-t.patch
new file mode 100644
index 0000000000..a0ce6a2b15
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/318-mac80211-swap-NEED_TXPROCESSING-and-HW_80211_ENCAP-t.patch
@@ -0,0 +1,227 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Mon, 17 Aug 2020 13:35:32 +0200
+Subject: [PATCH] mac80211: swap NEED_TXPROCESSING and HW_80211_ENCAP tx
+ flags
+
+In order to unify the tx status path, the hw 802.11 encapsulation flag
+needs to survive the trip to the tx status call.
+Since we don't have any free bits in info->flags, we need to move one.
+IEEE80211_TX_INTFL_NEED_TXPROCESSING is only used internally in mac80211,
+and only before the call into the driver.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/drivers/net/wireless/ath/ath11k/dp_tx.c
++++ b/drivers/net/wireless/ath/ath11k/dp_tx.c
+@@ -14,7 +14,7 @@ ath11k_dp_tx_get_encap_type(struct ath11
+ {
+ 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ 
+-	if (tx_info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP)
++	if (tx_info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
+ 		return HAL_TCL_ENCAP_TYPE_ETHERNET;
+ 
+ 	return HAL_TCL_ENCAP_TYPE_NATIVE_WIFI;
+@@ -93,7 +93,7 @@ int ath11k_dp_tx(struct ath11k *ar, stru
+ 	if (test_bit(ATH11K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))
+ 		return -ESHUTDOWN;
+ 
+-	if (!(info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) &&
++	if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
+ 	    !ieee80211_is_data(hdr->frame_control))
+ 		return -ENOTSUPP;
+ 
+--- a/drivers/net/wireless/ath/ath11k/mac.c
++++ b/drivers/net/wireless/ath/ath11k/mac.c
+@@ -3749,7 +3749,7 @@ static int ath11k_mac_mgmt_tx_wmi(struct
+ 		return -ENOSPC;
+ 
+ 	info = IEEE80211_SKB_CB(skb);
+-	if (!(info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP)) {
++	if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) {
+ 		if ((ieee80211_is_action(hdr->frame_control) ||
+ 		     ieee80211_is_deauth(hdr->frame_control) ||
+ 		     ieee80211_is_disassoc(hdr->frame_control)) &&
+@@ -3876,7 +3876,7 @@ static void ath11k_mac_op_tx(struct ieee
+ 	bool is_prb_rsp;
+ 	int ret;
+ 
+-	if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) {
++	if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
+ 		skb_cb->flags |= ATH11K_SKB_HW_80211_ENCAP;
+ 	} else if (ieee80211_is_mgmt(hdr->frame_control)) {
+ 		is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control);
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -720,9 +720,8 @@ struct ieee80211_bss_conf {
+  * @IEEE80211_TX_INTFL_OFFCHAN_TX_OK: Internal to mac80211. Used to indicate
+  *	that a frame can be transmitted while the queues are stopped for
+  *	off-channel operation.
+- * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211,
+- *	used to indicate that a pending frame requires TX processing before
+- *	it can be sent out.
++ * @IEEE80211_TX_CTL_HW_80211_ENCAP: This frame uses hardware encapsulation
++ *	(header conversion)
+  * @IEEE80211_TX_INTFL_RETRIED: completely internal to mac80211,
+  *	used to indicate that a frame was already retried due to PS
+  * @IEEE80211_TX_INTFL_DONT_ENCRYPT: completely internal to mac80211,
+@@ -791,7 +790,7 @@ enum mac80211_tx_info_flags {
+ 	IEEE80211_TX_STAT_AMPDU_NO_BACK		= BIT(11),
+ 	IEEE80211_TX_CTL_RATE_CTRL_PROBE	= BIT(12),
+ 	IEEE80211_TX_INTFL_OFFCHAN_TX_OK	= BIT(13),
+-	IEEE80211_TX_INTFL_NEED_TXPROCESSING	= BIT(14),
++	IEEE80211_TX_CTL_HW_80211_ENCAP		= BIT(14),
+ 	IEEE80211_TX_INTFL_RETRIED		= BIT(15),
+ 	IEEE80211_TX_INTFL_DONT_ENCRYPT		= BIT(16),
+ 	IEEE80211_TX_CTL_NO_PS_BUFFER		= BIT(17),
+@@ -823,8 +822,9 @@ enum mac80211_tx_info_flags {
+  * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
+  * @IEEE80211_TX_CTRL_FAST_XMIT: This frame is going through the fast_xmit path
+  * @IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP: This frame skips mesh path lookup
+- * @IEEE80211_TX_CTRL_HW_80211_ENCAP: This frame uses hardware encapsulation
+- *	(header conversion)
++ * @IEEE80211_TX_INTCFL_NEED_TXPROCESSING: completely internal to mac80211,
++ *	used to indicate that a pending frame requires TX processing before
++ *	it can be sent out.
+  *
+  * These flags are used in tx_info->control.flags.
+  */
+@@ -835,7 +835,7 @@ enum mac80211_tx_control_flags {
+ 	IEEE80211_TX_CTRL_AMSDU			= BIT(3),
+ 	IEEE80211_TX_CTRL_FAST_XMIT		= BIT(4),
+ 	IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP	= BIT(5),
+-	IEEE80211_TX_CTRL_HW_80211_ENCAP	= BIT(6),
++	IEEE80211_TX_INTCFL_NEED_TXPROCESSING	= BIT(6),
+ };
+ 
+ /*
+--- a/net/mac80211/mesh_hwmp.c
++++ b/net/mac80211/mesh_hwmp.c
+@@ -212,7 +212,7 @@ static void prepare_frame_for_deferred_t
+ 	skb->priority = 7;
+ 
+ 	info->control.vif = &sdata->vif;
+-	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++	info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ 	ieee80211_set_qos_hdr(sdata, skb);
+ 	ieee80211_mps_set_frame_flags(sdata, NULL, hdr);
+ }
+@@ -1163,7 +1163,7 @@ int mesh_nexthop_resolve(struct ieee8021
+ 	if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN)
+ 		skb_to_free = skb_dequeue(&mpath->frame_queue);
+ 
+-	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++	info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ 	ieee80211_set_qos_hdr(sdata, skb);
+ 	skb_queue_tail(&mpath->frame_queue, skb);
+ 	if (skb_to_free)
+--- a/net/mac80211/mesh_ps.c
++++ b/net/mac80211/mesh_ps.c
+@@ -432,7 +432,7 @@ static void mpsp_qos_null_append(struct
+ 
+ 	info = IEEE80211_SKB_CB(new_skb);
+ 	info->control.vif = &sdata->vif;
+-	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++	info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ 
+ 	__skb_queue_tail(frames, new_skb);
+ }
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -2896,7 +2896,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
+ 	fwd_hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_RETRY);
+ 	info = IEEE80211_SKB_CB(fwd_skb);
+ 	memset(info, 0, sizeof(*info));
+-	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++	info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ 	info->control.vif = &rx->sdata->vif;
+ 	info->control.jiffies = jiffies;
+ 	if (is_multicast_ether_addr(fwd_hdr->addr1)) {
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -66,8 +66,8 @@ static void ieee80211_handle_filtered_fr
+ 
+ 	info->control.jiffies = jiffies;
+ 	info->control.vif = &sta->sdata->vif;
+-	info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING |
+-		       IEEE80211_TX_INTFL_RETRANSMISSION;
++	info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
++	info->flags |= IEEE80211_TX_INTFL_RETRANSMISSION;
+ 	info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
+ 
+ 	sta->status_stats.filtered++;
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -530,7 +530,7 @@ ieee80211_tx_h_unicast_ps_buf(struct iee
+ 
+ 		info->control.jiffies = jiffies;
+ 		info->control.vif = &tx->sdata->vif;
+-		info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++		info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ 		info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
+ 		skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb);
+ 		spin_unlock(&sta->ps_lock);
+@@ -1132,7 +1132,7 @@ static bool ieee80211_tx_prep_agg(struct
+ 				       tx->sta->sta.addr, tx->sta->sta.aid);
+ 			}
+ 			info->control.vif = &tx->sdata->vif;
+-			info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++			info->control.flags |= IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ 			info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
+ 			__skb_queue_tail(&tid_tx->pending, skb);
+ 			if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER)
+@@ -1177,7 +1177,7 @@ ieee80211_tx_prepare(struct ieee80211_su
+ 	 * we are doing the needed processing, so remove the flag
+ 	 * now.
+ 	 */
+-	info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING;
++	info->control.flags &= ~IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
+ 
+ 	hdr = (struct ieee80211_hdr *) skb->data;
+ 
+@@ -1256,7 +1256,7 @@ static struct txq_info *ieee80211_get_tx
+ 	    (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
+ 		return NULL;
+ 
+-	if (!(info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) &&
++	if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) &&
+ 	    unlikely(!ieee80211_is_data_present(hdr->frame_control))) {
+ 		if ((!ieee80211_is_mgmt(hdr->frame_control) ||
+ 		     ieee80211_is_bufferable_mmpdu(hdr->frame_control) ||
+@@ -3640,7 +3640,7 @@ begin:
+ 	else
+ 		info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+ 
+-	if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP)
++	if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)
+ 		goto encap_out;
+ 
+ 	if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
+@@ -4253,7 +4253,7 @@ static void ieee80211_8023_xmit(struct i
+ 		sdata = container_of(sdata->bss,
+ 				     struct ieee80211_sub_if_data, u.ap);
+ 
+-	info->control.flags |= IEEE80211_TX_CTRL_HW_80211_ENCAP;
++	info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP;
+ 	info->control.vif = &sdata->vif;
+ 
+ 	ieee80211_tx_8023(sdata, skb, skb->len, sta, false);
+@@ -4357,7 +4357,7 @@ static bool ieee80211_tx_pending_skb(str
+ 
+ 	sdata = vif_to_sdata(info->control.vif);
+ 
+-	if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
++	if (info->control.flags & IEEE80211_TX_INTCFL_NEED_TXPROCESSING) {
+ 		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ 		if (unlikely(!chanctx_conf)) {
+ 			dev_kfree_skb(skb);
+@@ -4365,7 +4365,7 @@ static bool ieee80211_tx_pending_skb(str
+ 		}
+ 		info->band = chanctx_conf->def.chan->band;
+ 		result = ieee80211_tx(sdata, NULL, skb, true, 0);
+-	} else if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) {
++	} else if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
+ 		if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+ 			dev_kfree_skb(skb);
+ 			return true;
diff --git a/package/kernel/mac80211/patches/subsys/319-mac80211-unify-802.3-offload-and-802.11-tx-status-co.patch b/package/kernel/mac80211/patches/subsys/319-mac80211-unify-802.3-offload-and-802.11-tx-status-co.patch
new file mode 100644
index 0000000000..7bb54f499b
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/319-mac80211-unify-802.3-offload-and-802.11-tx-status-co.patch
@@ -0,0 +1,159 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Mon, 17 Aug 2020 13:54:19 +0200
+Subject: [PATCH] mac80211: unify 802.3 (offload) and 802.11 tx status
+ codepath
+
+Make ieee80211_tx_status_8023 call ieee80211_tx_status_ext, similar to
+ieee80211_tx_status.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -903,7 +903,6 @@ static void __ieee80211_tx_status(struct
+ 	struct ieee80211_bar *bar;
+ 	int shift = 0;
+ 	int tid = IEEE80211_NUM_TIDS;
+-	u16 tx_time_est;
+ 
+ 	sband = local->hw.wiphy->bands[info->band];
+ 	fc = hdr->frame_control;
+@@ -996,17 +995,6 @@ static void __ieee80211_tx_status(struct
+ 		    ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
+ 			ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
+ 						acked, info->status.tx_time);
+-
+-		if ((tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
+-			/* Do this here to avoid the expensive lookup of the sta
+-			 * in ieee80211_report_used_skb().
+-			 */
+-			ieee80211_sta_update_pending_airtime(local, sta,
+-							     skb_get_queue_mapping(skb),
+-							     tx_time_est,
+-							     true);
+-			ieee80211_info_set_tx_time_est(info, 0);
+-		}
+ 	}
+ 
+ 	/* SNMP counters
+@@ -1102,9 +1090,11 @@ void ieee80211_tx_status_ext(struct ieee
+ 	struct ieee80211_tx_info *info = status->info;
+ 	struct ieee80211_sta *pubsta = status->sta;
+ 	struct ieee80211_supported_band *sband;
+-	struct sta_info *sta;
++	struct sk_buff *skb = status->skb;
++	struct sta_info *sta = NULL;
+ 	int rates_idx, retry_count;
+ 	bool acked, noack_success;
++	u16 tx_time_est;
+ 
+ 	if (pubsta) {
+ 		sta = container_of(pubsta, struct sta_info, sta);
+@@ -1156,7 +1146,18 @@ void ieee80211_tx_status_ext(struct ieee
+ 			ieee80211s_update_metric(local, sta, status);
+ 	}
+ 
+-	if (status->skb)
++	if (skb && (tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
++		/* Do this here to avoid the expensive lookup of the sta
++		 * in ieee80211_report_used_skb().
++		 */
++		ieee80211_sta_update_pending_airtime(local, sta,
++						     skb_get_queue_mapping(skb),
++						     tx_time_est,
++						     true);
++		ieee80211_info_set_tx_time_est(info, 0);
++	}
++
++	if (skb && !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
+ 		return __ieee80211_tx_status(hw, status, rates_idx,
+ 					     retry_count);
+ 
+@@ -1171,6 +1172,12 @@ void ieee80211_tx_status_ext(struct ieee
+ 	} else {
+ 		I802_DEBUG_INC(local->dot11FailedCount);
+ 	}
++
++	if (!skb)
++		return;
++
++	ieee80211_report_used_skb(local, skb, false);
++	dev_kfree_skb(skb);
+ }
+ EXPORT_SYMBOL(ieee80211_tx_status_ext);
+ 
+@@ -1197,66 +1204,23 @@ void ieee80211_tx_status_8023(struct iee
+ 			      struct ieee80211_vif *vif,
+ 			      struct sk_buff *skb)
+ {
+-	struct ieee80211_local *local = hw_to_local(hw);
+ 	struct ieee80211_sub_if_data *sdata;
+-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
++	struct ieee80211_tx_status status = {
++		.skb = skb,
++		.info = IEEE80211_SKB_CB(skb),
++	};
+ 	struct sta_info *sta;
+-	int retry_count;
+-	int rates_idx;
+-	bool acked;
+ 
+ 	sdata = vif_to_sdata(vif);
+ 
+-	acked = info->flags & IEEE80211_TX_STAT_ACK;
+-	rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+-
+ 	rcu_read_lock();
+ 
+-	if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
+-		goto counters_update;
+-
+-	if (IS_ERR(sta))
+-		goto counters_update;
+-
+-	if (!acked)
+-		sta->status_stats.retry_failed++;
+-
+-	if (rates_idx != -1)
+-		sta->tx_stats.last_rate = info->status.rates[rates_idx];
+-
+-	sta->status_stats.retry_count += retry_count;
+-
+-	if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) {
+-		sta->status_stats.last_ack = jiffies;
+-		if (info->flags & IEEE80211_TX_STAT_ACK) {
+-			if (sta->status_stats.lost_packets)
+-				sta->status_stats.lost_packets = 0;
++	if (!ieee80211_lookup_ra_sta(sdata, skb, &sta) && !IS_ERR(sta))
++		status.sta = &sta->sta;
+ 
+-			sta->status_stats.last_pkt_time = jiffies;
+-		} else {
+-			ieee80211_lost_packet(sta, info);
+-		}
+-	}
++	ieee80211_tx_status_ext(hw, &status);
+ 
+-counters_update:
+ 	rcu_read_unlock();
+-	ieee80211_led_tx(local);
+-
+-	if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
+-	    !(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED))
+-		goto skip_stats_update;
+-
+-	I802_DEBUG_INC(local->dot11TransmittedFrameCount);
+-	if (is_multicast_ether_addr(skb->data))
+-		I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount);
+-	if (retry_count > 0)
+-		I802_DEBUG_INC(local->dot11RetryCount);
+-	if (retry_count > 1)
+-		I802_DEBUG_INC(local->dot11MultipleRetryCount);
+-
+-skip_stats_update:
+-	ieee80211_report_used_skb(local, skb, false);
+-	dev_kfree_skb(skb);
+ }
+ EXPORT_SYMBOL(ieee80211_tx_status_8023);
+ 
diff --git a/package/kernel/mac80211/patches/subsys/320-mac80211-add-missing-queue-hash-initialization-to-80.patch b/package/kernel/mac80211/patches/subsys/320-mac80211-add-missing-queue-hash-initialization-to-80.patch
new file mode 100644
index 0000000000..1ec22dbbc8
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/320-mac80211-add-missing-queue-hash-initialization-to-80.patch
@@ -0,0 +1,25 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Mon, 17 Aug 2020 13:55:56 +0200
+Subject: [PATCH] mac80211: add missing queue/hash initialization to 802.3
+ xmit
+
+Fixes AQL for encap-offloaded tx
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4206,6 +4206,12 @@ static void ieee80211_8023_xmit(struct i
+ 	if (is_zero_ether_addr(ra))
+ 		goto out_free;
+ 
++	if (local->ops->wake_tx_queue) {
++		u16 queue = __ieee80211_select_queue(sdata, sta, skb);
++		skb_set_queue_mapping(skb, queue);
++		skb_get_hash(skb);
++	}
++
+ 	multicast = is_multicast_ether_addr(ra);
+ 
+ 	if (sta)
diff --git a/package/kernel/mac80211/patches/subsys/321-mac80211-check-and-refresh-aggregation-session-in-en.patch b/package/kernel/mac80211/patches/subsys/321-mac80211-check-and-refresh-aggregation-session-in-en.patch
new file mode 100644
index 0000000000..5c149e7d95
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/321-mac80211-check-and-refresh-aggregation-session-in-en.patch
@@ -0,0 +1,45 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Mon, 17 Aug 2020 21:11:25 +0200
+Subject: [PATCH] mac80211: check and refresh aggregation session in encap
+ offload tx
+
+Update the last_tx timestamp to avoid tearing down the aggregation session
+early. Fall back to the slow path if the session setup is still running
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4195,6 +4195,8 @@ static void ieee80211_8023_xmit(struct i
+ 	bool authorized = false;
+ 	bool multicast;
+ 	unsigned char *ra = ehdr->h_dest;
++	struct tid_ampdu_tx *tid_tx;
++	u8 tid;
+ 
+ 	if (IS_ERR(sta) || (sta && !sta->uploaded))
+ 		sta = NULL;
+@@ -4232,6 +4234,22 @@ static void ieee80211_8023_xmit(struct i
+ 
+ 	memset(info, 0, sizeof(*info));
+ 
++	if (sta) {
++		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
++		tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
++		if (tid_tx) {
++			if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
++				/* fall back to non-offload slow path */
++				__ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
++				return;
++			}
++
++			info->flags |= IEEE80211_TX_CTL_AMPDU;
++			if (tid_tx->timeout)
++				tid_tx->last_tx = jiffies;
++		}
++	}
++
+ 	if (unlikely(!multicast && skb->sk &&
+ 		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
+ 		info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
diff --git a/package/kernel/mac80211/patches/subsys/322-mac80211-support-using-ieee80211_tx_status_ext-to-fr.patch b/package/kernel/mac80211/patches/subsys/322-mac80211-support-using-ieee80211_tx_status_ext-to-fr.patch
new file mode 100644
index 0000000000..5469a419bf
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/322-mac80211-support-using-ieee80211_tx_status_ext-to-fr.patch
@@ -0,0 +1,63 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Thu, 20 Aug 2020 17:27:00 +0200
+Subject: [PATCH] mac80211: support using ieee80211_tx_status_ext to free
+ skbs without status info
+
+For encap-offloaded packets, ieee80211_free_txskb cannot be used, since it
+does not have the vif pointer.
+Using ieee80211_tx_status_ext for this purpose has the advantage of being able
+avoid an extra station lookup for AQL
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -1103,6 +1103,21 @@ void ieee80211_tx_status_ext(struct ieee
+ 			sta->tx_stats.last_rate_info = *status->rate;
+ 	}
+ 
++	if (skb && (tx_time_est =
++		    ieee80211_info_get_tx_time_est(IEEE80211_SKB_CB(skb))) > 0) {
++		/* Do this here to avoid the expensive lookup of the sta
++		 * in ieee80211_report_used_skb().
++		 */
++		ieee80211_sta_update_pending_airtime(local, sta,
++						     skb_get_queue_mapping(skb),
++						     tx_time_est,
++						     true);
++		ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0);
++	}
++
++	if (!status->info)
++		goto free;
++
+ 	rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+ 
+ 	sband = hw->wiphy->bands[info->band];
+@@ -1146,17 +1161,6 @@ void ieee80211_tx_status_ext(struct ieee
+ 			ieee80211s_update_metric(local, sta, status);
+ 	}
+ 
+-	if (skb && (tx_time_est = ieee80211_info_get_tx_time_est(info)) > 0) {
+-		/* Do this here to avoid the expensive lookup of the sta
+-		 * in ieee80211_report_used_skb().
+-		 */
+-		ieee80211_sta_update_pending_airtime(local, sta,
+-						     skb_get_queue_mapping(skb),
+-						     tx_time_est,
+-						     true);
+-		ieee80211_info_set_tx_time_est(info, 0);
+-	}
+-
+ 	if (skb && !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP))
+ 		return __ieee80211_tx_status(hw, status, rates_idx,
+ 					     retry_count);
+@@ -1173,6 +1177,7 @@ void ieee80211_tx_status_ext(struct ieee
+ 		I802_DEBUG_INC(local->dot11FailedCount);
+ 	}
+ 
++free:
+ 	if (!skb)
+ 		return;
+ 
diff --git a/package/kernel/mac80211/patches/subsys/323-mac80211-extend-ieee80211_tx_status_ext-to-support-b.patch b/package/kernel/mac80211/patches/subsys/323-mac80211-extend-ieee80211_tx_status_ext-to-support-b.patch
new file mode 100644
index 0000000000..c0f2b7b10a
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/323-mac80211-extend-ieee80211_tx_status_ext-to-support-b.patch
@@ -0,0 +1,53 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Fri, 21 Aug 2020 05:49:07 +0200
+Subject: [PATCH] mac80211: extend ieee80211_tx_status_ext to support bulk
+ free
+
+Store processed skbs ready to be freed in a list so the driver bulk free them
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1092,12 +1092,14 @@ ieee80211_info_get_tx_time_est(struct ie
+  * @info: Basic tx status information
+  * @skb: Packet skb (can be NULL if not provided by the driver)
+  * @rate: The TX rate that was used when sending the packet
++ * @free_list: list where processed skbs are stored to be free'd by the driver
+  */
+ struct ieee80211_tx_status {
+ 	struct ieee80211_sta *sta;
+ 	struct ieee80211_tx_info *info;
+ 	struct sk_buff *skb;
+ 	struct rate_info *rate;
++	struct list_head *free_list;
+ };
+ 
+ /**
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -1053,7 +1053,10 @@ static void __ieee80211_tx_status(struct
+ 	 * with this test...
+ 	 */
+ 	if (!local->monitors && (!send_to_cooked || !local->cooked_mntrs)) {
+-		dev_kfree_skb(skb);
++		if (status->free_list)
++			list_add_tail(&skb->list, status->free_list);
++		else
++			dev_kfree_skb(skb);
+ 		return;
+ 	}
+ 
+@@ -1182,7 +1185,10 @@ free:
+ 		return;
+ 
+ 	ieee80211_report_used_skb(local, skb, false);
+-	dev_kfree_skb(skb);
++	if (status->free_list)
++		list_add_tail(&skb->list, status->free_list);
++	else
++		dev_kfree_skb(skb);
+ }
+ EXPORT_SYMBOL(ieee80211_tx_status_ext);
+ 
diff --git a/package/kernel/mac80211/patches/subsys/324-mac80211-notify-the-driver-when-a-sta-uses-4-address.patch b/package/kernel/mac80211/patches/subsys/324-mac80211-notify-the-driver-when-a-sta-uses-4-address.patch
new file mode 100644
index 0000000000..abfb5b76d0
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/324-mac80211-notify-the-driver-when-a-sta-uses-4-address.patch
@@ -0,0 +1,109 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Fri, 21 Aug 2020 05:51:58 +0200
+Subject: [PATCH] mac80211: notify the driver when a sta uses 4-address
+ mode
+
+This is needed for encapsulation offload of 4-address mode packets
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -3840,6 +3840,8 @@ enum ieee80211_reconfig_type {
+  *	This callback may sleep.
+  * @update_vif_config: Update virtual interface offload flags
+  *	This callback may sleep.
++ * @sta_set_4addr: Called to notify the driver when a station starts/stops using
++ *	4-address mode
+  */
+ struct ieee80211_ops {
+ 	void (*tx)(struct ieee80211_hw *hw,
+@@ -4153,6 +4155,8 @@ struct ieee80211_ops {
+ 				struct ieee80211_sta *sta, u8 tids);
+ 	void (*update_vif_offload)(struct ieee80211_hw *hw,
+ 				   struct ieee80211_vif *vif);
++	void (*sta_set_4addr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++			      struct ieee80211_sta *sta, bool enabled);
+ };
+ 
+ /**
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1698,6 +1698,7 @@ static int ieee80211_change_station(stru
+ 
+ 			rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
+ 			__ieee80211_check_fast_rx_iface(vlansdata);
++			drv_sta_set_4addr(local, sta->sdata, &sta->sta, true);
+ 		}
+ 
+ 		if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -1400,4 +1400,18 @@ static inline void drv_update_vif_offloa
+ 	trace_drv_return_void(local);
+ }
+ 
++static inline void drv_sta_set_4addr(struct ieee80211_local *local,
++				     struct ieee80211_sub_if_data *sdata,
++				     struct ieee80211_sta *sta, bool enabled)
++{
++	sdata = get_bss_sdata(sdata);
++	if (!check_sdata_in_driver(sdata))
++		return;
++
++	trace_drv_sta_set_4addr(local, sdata, sta, enabled);
++	if (local->ops->sta_set_4addr)
++		local->ops->sta_set_4addr(&local->hw, &sdata->vif, sta, enabled);
++	trace_drv_return_void(local);
++}
++
+ #endif /* __MAC80211_DRIVER_OPS */
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -3518,6 +3518,9 @@ static bool ieee80211_assoc_success(stru
+ 		goto out;
+ 	}
+ 
++	if (sdata->wdev.use_4addr)
++		drv_sta_set_4addr(local, sdata, &sta->sta, true);
++
+ 	mutex_unlock(&sdata->local->sta_mtx);
+ 
+ 	/*
+--- a/net/mac80211/trace.h
++++ b/net/mac80211/trace.h
+@@ -2739,6 +2739,33 @@ DEFINE_EVENT(local_sdata_addr_evt, drv_u
+ 	TP_ARGS(local, sdata)
+ );
+ 
++TRACE_EVENT(drv_sta_set_4addr,
++	TP_PROTO(struct ieee80211_local *local,
++		 struct ieee80211_sub_if_data *sdata,
++		 struct ieee80211_sta *sta, bool enabled),
++
++	TP_ARGS(local, sdata, sta, enabled),
++
++	TP_STRUCT__entry(
++		LOCAL_ENTRY
++		VIF_ENTRY
++		STA_ENTRY
++		__field(bool, enabled)
++	),
++
++	TP_fast_assign(
++		LOCAL_ASSIGN;
++		VIF_ASSIGN;
++		STA_ASSIGN;
++		__entry->enabled = enabled;
++	),
++
++	TP_printk(
++		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " enabled:%d",
++		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->enabled
++	)
++);
++
+ #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
+ 
+ #undef TRACE_INCLUDE_PATH
diff --git a/package/kernel/mac80211/patches/subsys/325-mac80211-skip-encap-offload-for-tx-multicast-control.patch b/package/kernel/mac80211/patches/subsys/325-mac80211-skip-encap-offload-for-tx-multicast-control.patch
new file mode 100644
index 0000000000..6ea9c3c913
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/325-mac80211-skip-encap-offload-for-tx-multicast-control.patch
@@ -0,0 +1,158 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Fri, 21 Aug 2020 05:54:10 +0200
+Subject: [PATCH] mac80211: skip encap offload for tx multicast/control
+ packets
+
+This simplifies the checks in the encap offload tx handler and allows using
+it in cases where software crypto is used for multicast packets, e.g. when
+using an AP_VLAN.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -378,7 +378,8 @@ static bool ieee80211_set_sdata_offload_
+ 			if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
+ 			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
+ 			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 ||
+-			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256)
++			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256 ||
++			    !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ 				continue;
+ 			if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP ||
+ 			    !(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+@@ -1448,7 +1449,8 @@ static void ieee80211_set_vif_encap_ops(
+ 			if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
+ 			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 ||
+ 			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 ||
+-			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256)
++			    key->conf.cipher == WLAN_CIPHER_SUITE_BIP_CMAC_256 ||
++			    !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ 				continue;
+ 			if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+ 				enabled = false;
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4190,88 +4190,47 @@ static void ieee80211_8023_xmit(struct i
+ 				struct sk_buff *skb)
+ {
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+-	struct ethhdr *ehdr = (struct ethhdr *)skb->data;
+ 	struct ieee80211_local *local = sdata->local;
+-	bool authorized = false;
+-	bool multicast;
+-	unsigned char *ra = ehdr->h_dest;
+ 	struct tid_ampdu_tx *tid_tx;
+ 	u8 tid;
+ 
+-	if (IS_ERR(sta) || (sta && !sta->uploaded))
+-		sta = NULL;
+-
+-	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+-	    (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER)))
+-		ra = sdata->u.mgd.bssid;
+-
+-	if (is_zero_ether_addr(ra))
+-		goto out_free;
+-
+ 	if (local->ops->wake_tx_queue) {
+ 		u16 queue = __ieee80211_select_queue(sdata, sta, skb);
+ 		skb_set_queue_mapping(skb, queue);
+ 		skb_get_hash(skb);
+ 	}
+ 
+-	multicast = is_multicast_ether_addr(ra);
+-
+-	if (sta)
+-		authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
+-
+-	if (!multicast && !authorized &&
+-	    (ehdr->h_proto != sdata->control_port_protocol ||
+-	     !ether_addr_equal(sdata->vif.addr, ehdr->h_source)))
+-		goto out_free;
+-
+-	if (multicast && sdata->vif.type == NL80211_IFTYPE_AP &&
+-	    !atomic_read(&sdata->u.ap.num_mcast_sta))
+-		goto out_free;
+-
+ 	if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) &&
+ 	    test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
+ 		goto out_free;
+ 
+ 	memset(info, 0, sizeof(*info));
+ 
+-	if (sta) {
+-		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+-		tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
+-		if (tid_tx) {
+-			if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
+-				/* fall back to non-offload slow path */
+-				__ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
+-				return;
+-			}
+-
+-			info->flags |= IEEE80211_TX_CTL_AMPDU;
+-			if (tid_tx->timeout)
+-				tid_tx->last_tx = jiffies;
++	tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
++	tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
++	if (tid_tx) {
++		if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
++			/* fall back to non-offload slow path */
++			__ieee80211_subif_start_xmit(skb, dev, 0, 0, NULL);
++			return;
+ 		}
++
++		info->flags |= IEEE80211_TX_CTL_AMPDU;
++		if (tid_tx->timeout)
++			tid_tx->last_tx = jiffies;
+ 	}
+ 
+-	if (unlikely(!multicast && skb->sk &&
++	if (unlikely(skb->sk &&
+ 		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS))
+ 		info->ack_frame_id = ieee80211_store_ack_skb(local, skb,
+ 							     &info->flags, NULL);
+ 
+-	if (unlikely(sdata->control_port_protocol == ehdr->h_proto)) {
+-		if (sdata->control_port_no_encrypt)
+-			info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+-		info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
+-	}
+-
+-	if (multicast)
+-		info->flags |= IEEE80211_TX_CTL_NO_ACK;
+-
+ 	info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
+ 
+ 	ieee80211_tx_stats(dev, skb->len);
+ 
+-	if (sta) {
+-		sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
+-		sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
+-	}
++	sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
++	sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
+ 
+ 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ 		sdata = container_of(sdata->bss,
+@@ -4292,6 +4251,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8
+ 					    struct net_device *dev)
+ {
+ 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++	struct ethhdr *ehdr = (struct ethhdr *)skb->data;
+ 	struct sta_info *sta;
+ 
+ 	if (unlikely(skb->len < ETH_HLEN)) {
+@@ -4303,6 +4263,10 @@ netdev_tx_t ieee80211_subif_start_xmit_8
+ 
+ 	if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
+ 		kfree_skb(skb);
++	else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
++			  !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
++			  sdata->control_port_protocol == ehdr->h_proto))
++		ieee80211_subif_start_xmit(skb, dev);
+ 	else
+ 		ieee80211_8023_xmit(sdata, dev, sta, skb);
+ 
diff --git a/package/kernel/mac80211/patches/subsys/326-mac80211-set-info-control.hw_key-for-encap-offload-p.patch b/package/kernel/mac80211/patches/subsys/326-mac80211-set-info-control.hw_key-for-encap-offload-p.patch
new file mode 100644
index 0000000000..2b7579b24e
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/326-mac80211-set-info-control.hw_key-for-encap-offload-p.patch
@@ -0,0 +1,31 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Fri, 21 Aug 2020 06:03:45 +0200
+Subject: [PATCH] mac80211: set info->control.hw_key for encap offload
+ packets
+
+This is needed for drivers that don't do the key lookup themselves
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -4191,6 +4191,7 @@ static void ieee80211_8023_xmit(struct i
+ {
+ 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ 	struct ieee80211_local *local = sdata->local;
++	struct ieee80211_key *key;
+ 	struct tid_ampdu_tx *tid_tx;
+ 	u8 tid;
+ 
+@@ -4239,6 +4240,10 @@ static void ieee80211_8023_xmit(struct i
+ 	info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP;
+ 	info->control.vif = &sdata->vif;
+ 
++	key = rcu_dereference(sta->ptk[sta->ptk_idx]);
++	if (key)
++		info->control.hw_key = &key->conf;
++
+ 	ieee80211_tx_8023(sdata, skb, skb->len, sta, false);
+ 
+ 	return;
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 b816146c2a..1487e10f42 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
@@ -57,7 +57,7 @@
  	__NL80211_ATTR_AFTER_LAST,
 --- a/net/mac80211/cfg.c
 +++ b/net/mac80211/cfg.c
-@@ -2610,6 +2610,19 @@ static int ieee80211_get_tx_power(struct
+@@ -2615,6 +2615,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)
  {
-@@ -4040,6 +4053,7 @@ const struct cfg80211_ops mac80211_confi
+@@ -4045,6 +4058,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
-@@ -1385,6 +1385,7 @@ struct ieee80211_local {
+@@ -1383,6 +1383,7 @@ struct ieee80211_local {
  	int dynamic_ps_forced_timeout;
  
  	int user_power_level; /* in dBm, for all interfaces */



More information about the lede-commits mailing list