diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index c10abca55fde..63bd3d5aa707 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1829,9 +1829,17 @@ struct ieee80211_cipher_scheme {
  *
  * @SET_KEY: a key is set
  * @DISABLE_KEY: a key must be disabled
+ *
+ * Additional driver commands for COMPAT Extended Key ID support:
+ *
+ * @ENABLE_KEY_RX: Rx acceleration can be activated for a key
+ * @DISABLE_KEY_RX: Rx acceleration must be deactivated for a key
  */
 enum set_key_cmd {
-	SET_KEY, DISABLE_KEY,
+	SET_KEY,
+	DISABLE_KEY,
+	ENABLE_KEY_RX,
+	DISABLE_KEY_RX,
 };
 
 /**
@@ -2248,6 +2256,10 @@ struct ieee80211_txq {
  * @IEEE80211_HW_EXT_KEY_ID_NATIVE: Driver and hardware are supporting Extended
  *	Key ID and can handle two unicast keys per station for Rx and Tx.
  *
+ * @IEEE80211_HW_EXT_KEY_ID_COMPAT: Driver and hardware support Extended Key ID
+ *	when mac80211 handles Rx decryption during transition from one keyid to
+ *	the next.
+ *
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -2300,6 +2312,7 @@ enum ieee80211_hw_flags {
 	IEEE80211_HW_SUPPORTS_MULTI_BSSID,
 	IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
 	IEEE80211_HW_EXT_KEY_ID_NATIVE,
+	IEEE80211_HW_EXT_KEY_ID_COMPAT,
 
 	/* keep last, obviously */
 	NUM_IEEE80211_HW_FLAGS
@@ -2660,6 +2673,36 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
    Mac80211 will not queue any new frames for a deleted key to the driver.
  */
 
+/**
+ * DOC: Extended Key ID support
+ *
+ * Mac80211 supports "Extended Key ID" from IEEE 802.11-2016, allowing to rekey
+ * the in-use unicast key with no impact for ongoing transmissions.
+ *
+ * There are two ways mac80211 drivers can support Extended Key ID:
+ * 1) Native
+ *    When using "Native" Extended Key ID mode mac80211 can install two unicast
+ *    keys per station to the driver, using the two key IDs "0" and "1".
+ *    Compatible drivers/cards can simply set @IEEE80211_HW_EXT_KEY_ID_NATIVE,
+ *    allowing mac80211 to install two unicast keys per station to the driver
+ *    with %SET_KEY.
+ *
+ * 2) Compatibility
+ *    This mode is for drivers and cards which are not able to handle two
+ *    unicast key for a station for Rx. For drivers setting
+ *    @IEEE80211_HW_EXT_KEY_ID_COMPAT mac80211 will make sure that never more
+ *    than one unicast key is active for Rx in the hardware, falling back to
+ *    software decryption while installing a new unicast key. Divers using this
+ *    mode must implement the additional key commands %ENABLE_KEY_RX and
+ *    %DISABLE_KEY_RX to allow switching Rx crypto offload on and off without
+ *    impact for Tx. Drivers also must not activate Rx crypto offload when
+ *    %SET_KEY is called for a key with @IEEE80211_KEY_FLAG_NO_AUTO_TX set.
+ *    Compatibility mode will only be really lossless when there is a clean cut
+ *    over to the new key and MPDUs using both key IDs are not mixed. It can
+ *    also cause CPU spikes when falling back to software encryption, depending
+ *    on the amount of Rx packets at that time.
+ */
+
 /**
  * DOC: Powersave support
  *
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index aa6f23e1a457..9c4899aaf346 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -222,6 +222,7 @@ static const char *hw_flag_names[] = {
 	FLAG(SUPPORTS_MULTI_BSSID),
 	FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
 	FLAG(EXT_KEY_ID_NATIVE),
+	FLAG(EXT_KEY_ID_COMPAT),
 #undef FLAG
 };
 
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 20bf9db7a388..bd38167916ad 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -127,7 +127,9 @@ static void decrease_tailroom_need_count(struct ieee80211_sub_if_data *sdata,
 static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 {
 	struct ieee80211_sub_if_data *sdata = key->sdata;
+	struct ieee80211_local *local = key->local;
 	struct sta_info *sta;
+	bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE;
 	int ret = -EOPNOTSUPP;
 
 	might_sleep();
@@ -150,10 +152,10 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 		return -EINVAL;
 	}
 
-	if (!key->local->ops->set_key)
+	if (!local->ops->set_key)
 		goto out_unsupported;
 
-	assert_key_lock(key->local);
+	assert_key_lock(local);
 
 	sta = key->sta;
 
@@ -161,8 +163,8 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 	 * If this is a per-STA GTK, check if it
 	 * is supported; if not, return.
 	 */
-	if (sta && !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE) &&
-	    !ieee80211_hw_check(&key->local->hw, SUPPORTS_PER_STA_GTK))
+	if (sta && !pairwise &&
+	    !ieee80211_hw_check(&local->hw, SUPPORTS_PER_STA_GTK))
 		goto out_unsupported;
 
 	if (sta && !sta->uploaded)
@@ -173,13 +175,33 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 		 * The driver doesn't know anything about VLAN interfaces.
 		 * Hence, don't send GTKs for VLAN interfaces to the driver.
 		 */
-		if (!(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+		if (!pairwise) {
 			ret = 1;
 			goto out_unsupported;
 		}
 	}
 
-	ret = drv_set_key(key->local, SET_KEY, sdata,
+	if (key->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX) {
+		/* EXT_KEY_ID_COMPAT drivers may scramble the payload when
+		 * using the wrong HW key for decryption. Therefore only use SW
+		 * decryption for the critical window.
+		 */
+		if (sta && pairwise && !local->wowlan &&
+		    ieee80211_hw_check(&local->hw, EXT_KEY_ID_COMPAT) &&
+		    sta->ptk_idx != key->conf.keyidx) {
+			struct ieee80211_key *old;
+
+			old = key_mtx_dereference(local,
+						  sta->ptk[sta->ptk_idx]);
+			if (old) {
+				if (drv_set_key(local, DISABLE_KEY_RX,
+						sdata, &sta->sta, &old->conf))
+					return -EINVAL;
+			}
+		}
+	}
+
+	ret = drv_set_key(local, SET_KEY, sdata,
 			  sta ? &sta->sta : NULL, &key->conf);
 
 	if (!ret) {
@@ -221,7 +243,7 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 		/* all of these we can do in software - if driver can */
 		if (ret == 1)
 			return 0;
-		if (ieee80211_hw_check(&key->local->hw, SW_CRYPTO_CONTROL))
+		if (ieee80211_hw_check(&local->hw, SW_CRYPTO_CONTROL))
 			return -EINVAL;
 		return 0;
 	default:
@@ -276,6 +298,10 @@ int ieee80211_set_tx_key(struct ieee80211_key *key)
 	sta->ptk_idx = key->conf.keyidx;
 	ieee80211_check_fast_xmit(sta);
 
+	if (ieee80211_hw_check(&local->hw, EXT_KEY_ID_COMPAT) &&
+	    key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
+		key->flags |= KEY_FLAG_DELAYED_RX_ACCEL;
+
 	return 0;
 }
 
@@ -1063,6 +1089,31 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,
 	mutex_unlock(&local->key_mtx);
 }
 
+/* EXT_KEY_ID_COMPAT support can't install PTK keys to the card/driver for
+ * hardware decryption as long as the remote sta may use both keyids. Those
+ * cards are not aware that the keyid must be checked and try to decrypt the
+ * payload with the wrong key, which would effectively scrambling it. This
+ * worker is therefore used to activate Rx hardware decryption when we assume
+ * the remote sta has switched over to the new key.
+ */
+void delayed_rx_accel_work(struct work_struct *wk)
+{
+	struct sta_info *sta;
+	struct ieee80211_local *local;
+	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_key *key;
+
+	sta = container_of(wk, struct sta_info, delayed_rx_accel_wk);
+	local = sta->local;
+	sdata = sta->sdata;
+
+	mutex_lock(&local->key_mtx);
+	key = key_mtx_dereference(local, sta->ptk[sta->ptk_idx]);
+	drv_set_key(local, ENABLE_KEY_RX, sdata, &sta->sta, &key->conf);
+
+	mutex_unlock(&local->key_mtx);
+}
+
 void ieee80211_delayed_tailroom_dec(struct work_struct *wk)
 {
 	struct ieee80211_sub_if_data *sdata;
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index f06fbd03d235..21e8618b1d55 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -31,11 +31,15 @@ struct sta_info;
  *	in the hardware for TX crypto hardware acceleration.
  * @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped.
  * @KEY_FLAG_CIPHER_SCHEME: This key is for a hardware cipher scheme
+ * @KEY_FLAG_DELAYED_RX_ACCEL: This key has to use Rx SW decryption till we get
+ *	at least one MPDU from the remote sta encrypted with the key. (Needed
+ *	for COMPAT Extended ID support.)
  */
 enum ieee80211_internal_key_flags {
 	KEY_FLAG_UPLOADED_TO_HARDWARE	= BIT(0),
 	KEY_FLAG_TAINTED		= BIT(1),
 	KEY_FLAG_CIPHER_SCHEME		= BIT(2),
+	KEY_FLAG_DELAYED_RX_ACCEL	= BIT(3),
 };
 
 enum ieee80211_internal_tkip_state {
@@ -165,5 +169,6 @@ void ieee80211_reset_crypto_tx_tailroom(struct ieee80211_sub_if_data *sdata);
 	rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx)))
 
 void ieee80211_delayed_tailroom_dec(struct work_struct *wk);
+void delayed_rx_accel_work(struct work_struct *wk);
 
 #endif /* IEEE80211_KEY_H */
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index c212615f1bdc..2ce747d43a81 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1058,11 +1058,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
 	switch (ieee80211_extended_key_id) {
 	case 2:
-		/* Force on */
-		ieee80211_hw_set(&local->hw, EXT_KEY_ID_NATIVE);
+		/* Force on when driver is not supporting COMPAT mode */
+		if (!ieee80211_hw_check(&local->hw, EXT_KEY_ID_COMPAT))
+			ieee80211_hw_set(&local->hw, EXT_KEY_ID_NATIVE);
 		/* fall trough */
 	case 1:
-		if (ieee80211_hw_check(&local->hw, EXT_KEY_ID_NATIVE))
+		if (ieee80211_hw_check(&local->hw, EXT_KEY_ID_COMPAT) ||
+		    ieee80211_hw_check(&local->hw, EXT_KEY_ID_NATIVE))
 			wiphy_ext_feature_set(local->hw.wiphy,
 					      NL80211_EXT_FEATURE_EXT_KEY_ID);
 		wiphy_info(hw->wiphy, "Extended Key ID support enabled.\n");
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 4a03c18b39a8..e3e4fb1073d6 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2032,6 +2032,12 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 		if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
 			return RX_DROP_MONITOR;
 
+		if (unlikely(rx->key->flags & KEY_FLAG_DELAYED_RX_ACCEL)) {
+			rx->key->flags &= ~KEY_FLAG_DELAYED_RX_ACCEL;
+			ieee80211_queue_work(&rx->local->hw,
+					     &rx->sta->delayed_rx_accel_wk);
+		}
+
 		/* TODO: add threshold stuff again */
 	} else {
 		return RX_DROP_MONITOR;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 7c61f6aee873..41ea1d05a946 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -132,6 +132,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
 	if (ieee80211_vif_is_mesh(&sdata->vif))
 		mesh_sta_cleanup(sta);
 
+	cancel_work_sync(&sta->delayed_rx_accel_wk);
 	cancel_work_sync(&sta->drv_deliver_wk);
 
 	/*
@@ -326,6 +327,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 	spin_lock_init(&sta->ps_lock);
 	INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
 	INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
+	INIT_WORK(&sta->delayed_rx_accel_wk, delayed_rx_accel_work);
 	mutex_init(&sta->ampdu_mlme.mtx);
 #ifdef CONFIG_MAC80211_MESH
 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 71f7e4973329..45f7cbfe9698 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -450,6 +450,8 @@ struct ieee80211_sta_rx_stats {
  * @sdata: virtual interface this station belongs to
  * @ptk: peer keys negotiated with this station, if any
  * @ptk_idx: last installed peer key index
+ * @delayed_rx_accel_wk: Used to activate Rx crypto offload only after
+ *	we have	seen one MPDU encrypted with the key.
  * @gtk: group keys negotiated with this station, if any
  * @rate_ctrl: rate control algorithm reference
  * @rate_ctrl_lock: spinlock used to protect rate control data
@@ -530,6 +532,7 @@ struct sta_info {
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
 	struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS];
+	struct work_struct delayed_rx_accel_wk;
 	u8 ptk_idx;
 	struct rate_control_ref *rate_ctrl;
 	void *rate_ctrl_priv;
