xref: /openbmc/linux/drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c (revision fac59652993f075d57860769c99045b3ca18780d)
15c75a208SJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
25c75a208SJohannes Berg /*
3b4f1b0b3SEmmanuel Grumbach  * Copyright (C) 2022 - 2024 Intel Corporation
45c75a208SJohannes Berg  */
55c75a208SJohannes Berg #include <linux/kernel.h>
65c75a208SJohannes Berg #include <net/mac80211.h>
75c75a208SJohannes Berg #include "mvm.h"
85c75a208SJohannes Berg #include "fw/api/context.h"
95c75a208SJohannes Berg #include "fw/api/datapath.h"
105c75a208SJohannes Berg 
iwl_mvm_get_sec_sta_mask(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * keyconf)115c75a208SJohannes Berg static u32 iwl_mvm_get_sec_sta_mask(struct iwl_mvm *mvm,
125c75a208SJohannes Berg 				    struct ieee80211_vif *vif,
135c75a208SJohannes Berg 				    struct ieee80211_sta *sta,
145c75a208SJohannes Berg 				    struct ieee80211_key_conf *keyconf)
155c75a208SJohannes Berg {
165c75a208SJohannes Berg 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
17aea99650SJohannes Berg 	struct iwl_mvm_vif_link_info *link_info = &mvmvif->deflink;
185c75a208SJohannes Berg 
19aea99650SJohannes Berg 	lockdep_assert_held(&mvm->mutex);
205c75a208SJohannes Berg 
21aea99650SJohannes Berg 	if (keyconf->link_id >= 0) {
22aea99650SJohannes Berg 		link_info = mvmvif->link[keyconf->link_id];
23aea99650SJohannes Berg 		if (!link_info)
24aea99650SJohannes Berg 			return 0;
255c75a208SJohannes Berg 	}
265c75a208SJohannes Berg 
278df43e34SJohannes Berg 	/* AP group keys are per link and should be on the mcast/bcast STA */
28aea99650SJohannes Berg 	if (vif->type == NL80211_IFTYPE_AP &&
298df43e34SJohannes Berg 	    !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
308df43e34SJohannes Berg 		/* IGTK/BIGTK to bcast STA */
318df43e34SJohannes Berg 		if (keyconf->keyidx >= 4)
328df43e34SJohannes Berg 			return BIT(link_info->bcast_sta.sta_id);
338df43e34SJohannes Berg 		/* GTK for data to mcast STA */
34aea99650SJohannes Berg 		return BIT(link_info->mcast_sta.sta_id);
358df43e34SJohannes Berg 	}
365c75a208SJohannes Berg 
37aea99650SJohannes Berg 	/* for client mode use the AP STA also for group keys */
38aea99650SJohannes Berg 	if (!sta && vif->type == NL80211_IFTYPE_STATION)
39aea99650SJohannes Berg 		sta = mvmvif->ap_sta;
40aea99650SJohannes Berg 
41aea99650SJohannes Berg 	/* During remove the STA was removed and the group keys come later
42aea99650SJohannes Berg 	 * (which sounds like a bad sequence, but remember that to mac80211 the
43aea99650SJohannes Berg 	 * group keys have no sta pointer), so we don't have a STA now.
44aea99650SJohannes Berg 	 * Since this happens for group keys only, just use the link_info as
45aea99650SJohannes Berg 	 * the group keys are per link; make sure that is the case by checking
46aea99650SJohannes Berg 	 * we do have a link_id or are not doing MLO.
47aea99650SJohannes Berg 	 * Of course the same can be done during add as well, but we must do
48aea99650SJohannes Berg 	 * it during remove, since we don't have the mvmvif->ap_sta pointer.
49aea99650SJohannes Berg 	 */
5098d8a003SIlan Peer 	if (!sta && (keyconf->link_id >= 0 || !ieee80211_vif_is_mld(vif)))
51aea99650SJohannes Berg 		return BIT(link_info->ap_sta_id);
52aea99650SJohannes Berg 
5366a588bfSJohannes Berg 	/* STA should be non-NULL now, but iwl_mvm_sta_fw_id_mask() checks */
54aea99650SJohannes Berg 
5566a588bfSJohannes Berg 	/* pass link_id to filter by it if not -1 (GTK on client) */
5666a588bfSJohannes Berg 	return iwl_mvm_sta_fw_id_mask(mvm, sta, keyconf->link_id);
575c75a208SJohannes Berg }
585c75a208SJohannes Berg 
iwl_mvm_get_sec_flags(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * keyconf)590945f976SAvraham Stern u32 iwl_mvm_get_sec_flags(struct iwl_mvm *mvm,
605c75a208SJohannes Berg 			  struct ieee80211_vif *vif,
615c75a208SJohannes Berg 			  struct ieee80211_sta *sta,
625c75a208SJohannes Berg 			  struct ieee80211_key_conf *keyconf)
635c75a208SJohannes Berg {
645c75a208SJohannes Berg 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
65b4f1b0b3SEmmanuel Grumbach 	bool pairwise = keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE;
66b4f1b0b3SEmmanuel Grumbach 	bool igtk = keyconf->keyidx == 4 || keyconf->keyidx == 5;
675c75a208SJohannes Berg 	u32 flags = 0;
685c75a208SJohannes Berg 
690c9a8f90SJohannes Berg 	lockdep_assert_held(&mvm->mutex);
700c9a8f90SJohannes Berg 
71b4f1b0b3SEmmanuel Grumbach 	if (!pairwise)
725c75a208SJohannes Berg 		flags |= IWL_SEC_KEY_FLAG_MCAST_KEY;
735c75a208SJohannes Berg 
745c75a208SJohannes Berg 	switch (keyconf->cipher) {
755c75a208SJohannes Berg 	case WLAN_CIPHER_SUITE_WEP104:
765c75a208SJohannes Berg 		flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;
775c75a208SJohannes Berg 		fallthrough;
785c75a208SJohannes Berg 	case WLAN_CIPHER_SUITE_WEP40:
795c75a208SJohannes Berg 		flags |= IWL_SEC_KEY_FLAG_CIPHER_WEP;
805c75a208SJohannes Berg 		break;
815c75a208SJohannes Berg 	case WLAN_CIPHER_SUITE_TKIP:
825c75a208SJohannes Berg 		flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP;
835c75a208SJohannes Berg 		break;
845c75a208SJohannes Berg 	case WLAN_CIPHER_SUITE_AES_CMAC:
855c75a208SJohannes Berg 	case WLAN_CIPHER_SUITE_CCMP:
865c75a208SJohannes Berg 		flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP;
875c75a208SJohannes Berg 		break;
885c75a208SJohannes Berg 	case WLAN_CIPHER_SUITE_GCMP_256:
895c75a208SJohannes Berg 	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
905c75a208SJohannes Berg 		flags |= IWL_SEC_KEY_FLAG_KEY_SIZE;
915c75a208SJohannes Berg 		fallthrough;
925c75a208SJohannes Berg 	case WLAN_CIPHER_SUITE_GCMP:
935c75a208SJohannes Berg 	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
945c75a208SJohannes Berg 		flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP;
955c75a208SJohannes Berg 		break;
965c75a208SJohannes Berg 	}
975c75a208SJohannes Berg 
980c9a8f90SJohannes Berg 	if (!sta && vif->type == NL80211_IFTYPE_STATION)
990c9a8f90SJohannes Berg 		sta = mvmvif->ap_sta;
1005c75a208SJohannes Berg 
101b4f1b0b3SEmmanuel Grumbach 	/*
102b4f1b0b3SEmmanuel Grumbach 	 * If we are installing an iGTK (in AP or STA mode), we need to tell
103b4f1b0b3SEmmanuel Grumbach 	 * the firmware this key will en/decrypt MGMT frames.
104b4f1b0b3SEmmanuel Grumbach 	 * Same goes if we are installing a pairwise key for an MFP station.
105b4f1b0b3SEmmanuel Grumbach 	 * In case we're installing a groupwise key (which is not an iGTK),
106b4f1b0b3SEmmanuel Grumbach 	 * then, we will not use this key for MGMT frames.
10700cab8f3SIlan Peer 	 */
108b4f1b0b3SEmmanuel Grumbach 	if ((!IS_ERR_OR_NULL(sta) && sta->mfp && pairwise) || igtk)
1095c75a208SJohannes Berg 		flags |= IWL_SEC_KEY_FLAG_MFP;
1105c75a208SJohannes Berg 
1115c75a208SJohannes Berg 	return flags;
1125c75a208SJohannes Berg }
1135c75a208SJohannes Berg 
1148642ddb2SJohannes Berg struct iwl_mvm_sta_key_update_data {
1158642ddb2SJohannes Berg 	struct ieee80211_sta *sta;
1168642ddb2SJohannes Berg 	u32 old_sta_mask;
1178642ddb2SJohannes Berg 	u32 new_sta_mask;
1188642ddb2SJohannes Berg 	int err;
1198642ddb2SJohannes Berg };
1208642ddb2SJohannes Berg 
iwl_mvm_mld_update_sta_key(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key,void * _data)1218642ddb2SJohannes Berg static void iwl_mvm_mld_update_sta_key(struct ieee80211_hw *hw,
1228642ddb2SJohannes Berg 				       struct ieee80211_vif *vif,
1238642ddb2SJohannes Berg 				       struct ieee80211_sta *sta,
1248642ddb2SJohannes Berg 				       struct ieee80211_key_conf *key,
1258642ddb2SJohannes Berg 				       void *_data)
1268642ddb2SJohannes Berg {
1278642ddb2SJohannes Berg 	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);
1288642ddb2SJohannes Berg 	struct iwl_mvm_sta_key_update_data *data = _data;
1298642ddb2SJohannes Berg 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
1308642ddb2SJohannes Berg 	struct iwl_sec_key_cmd cmd = {
1318642ddb2SJohannes Berg 		.action = cpu_to_le32(FW_CTXT_ACTION_MODIFY),
1328642ddb2SJohannes Berg 		.u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask),
1338642ddb2SJohannes Berg 		.u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask),
1348642ddb2SJohannes Berg 		.u.modify.key_id = cpu_to_le32(key->keyidx),
1358642ddb2SJohannes Berg 		.u.modify.key_flags =
1368642ddb2SJohannes Berg 			cpu_to_le32(iwl_mvm_get_sec_flags(mvm, vif, sta, key)),
1378642ddb2SJohannes Berg 	};
1388642ddb2SJohannes Berg 	int err;
1398642ddb2SJohannes Berg 
1408642ddb2SJohannes Berg 	/* only need to do this for pairwise keys (link_id == -1) */
1418642ddb2SJohannes Berg 	if (sta != data->sta || key->link_id >= 0)
1428642ddb2SJohannes Berg 		return;
1438642ddb2SJohannes Berg 
144*32411625SJohannes Berg 	err = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);
1458642ddb2SJohannes Berg 
1468642ddb2SJohannes Berg 	if (err)
1478642ddb2SJohannes Berg 		data->err = err;
1488642ddb2SJohannes Berg }
1498642ddb2SJohannes Berg 
iwl_mvm_mld_update_sta_keys(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta,u32 old_sta_mask,u32 new_sta_mask)1508642ddb2SJohannes Berg int iwl_mvm_mld_update_sta_keys(struct iwl_mvm *mvm,
1518642ddb2SJohannes Berg 				struct ieee80211_vif *vif,
1528642ddb2SJohannes Berg 				struct ieee80211_sta *sta,
1538642ddb2SJohannes Berg 				u32 old_sta_mask,
1548642ddb2SJohannes Berg 				u32 new_sta_mask)
1558642ddb2SJohannes Berg {
1568642ddb2SJohannes Berg 	struct iwl_mvm_sta_key_update_data data = {
1578642ddb2SJohannes Berg 		.sta = sta,
1588642ddb2SJohannes Berg 		.old_sta_mask = old_sta_mask,
1598642ddb2SJohannes Berg 		.new_sta_mask = new_sta_mask,
1608642ddb2SJohannes Berg 	};
1618642ddb2SJohannes Berg 
162*32411625SJohannes Berg 	ieee80211_iter_keys(mvm->hw, vif, iwl_mvm_mld_update_sta_key,
1638642ddb2SJohannes Berg 			    &data);
1648642ddb2SJohannes Berg 	return data.err;
1658642ddb2SJohannes Berg }
1668642ddb2SJohannes Berg 
__iwl_mvm_sec_key_del(struct iwl_mvm * mvm,u32 sta_mask,u32 key_flags,u32 keyidx,u32 flags)1675c75a208SJohannes Berg static int __iwl_mvm_sec_key_del(struct iwl_mvm *mvm, u32 sta_mask,
1685c75a208SJohannes Berg 				 u32 key_flags, u32 keyidx, u32 flags)
1695c75a208SJohannes Berg {
1705c75a208SJohannes Berg 	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);
1715c75a208SJohannes Berg 	struct iwl_sec_key_cmd cmd = {
1725c75a208SJohannes Berg 		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
1735c75a208SJohannes Berg 		.u.remove.sta_mask = cpu_to_le32(sta_mask),
1745c75a208SJohannes Berg 		.u.remove.key_id = cpu_to_le32(keyidx),
1755c75a208SJohannes Berg 		.u.remove.key_flags = cpu_to_le32(key_flags),
1765c75a208SJohannes Berg 	};
1775c75a208SJohannes Berg 
1785c75a208SJohannes Berg 	return iwl_mvm_send_cmd_pdu(mvm, cmd_id, flags, sizeof(cmd), &cmd);
1795c75a208SJohannes Berg }
1805c75a208SJohannes Berg 
iwl_mvm_mld_send_key(struct iwl_mvm * mvm,u32 sta_mask,u32 key_flags,struct ieee80211_key_conf * keyconf)1810945f976SAvraham Stern int iwl_mvm_mld_send_key(struct iwl_mvm *mvm, u32 sta_mask, u32 key_flags,
1825c75a208SJohannes Berg 			 struct ieee80211_key_conf *keyconf)
1835c75a208SJohannes Berg {
1845c75a208SJohannes Berg 	u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);
1855c75a208SJohannes Berg 	struct iwl_sec_key_cmd cmd = {
1865c75a208SJohannes Berg 		.action = cpu_to_le32(FW_CTXT_ACTION_ADD),
1875c75a208SJohannes Berg 		.u.add.sta_mask = cpu_to_le32(sta_mask),
1885c75a208SJohannes Berg 		.u.add.key_id = cpu_to_le32(keyconf->keyidx),
1895c75a208SJohannes Berg 		.u.add.key_flags = cpu_to_le32(key_flags),
1905c75a208SJohannes Berg 		.u.add.tx_seq = cpu_to_le64(atomic64_read(&keyconf->tx_pn)),
1915c75a208SJohannes Berg 	};
19263745236SGregory Greenman 	int max_key_len = sizeof(cmd.u.add.key);
1935c75a208SJohannes Berg 	int ret;
1945c75a208SJohannes Berg 
19563745236SGregory Greenman 	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
19663745236SGregory Greenman 	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)
19763745236SGregory Greenman 		max_key_len -= IWL_SEC_WEP_KEY_OFFSET;
19863745236SGregory Greenman 
19963745236SGregory Greenman 	if (WARN_ON(keyconf->keylen > max_key_len))
2005c75a208SJohannes Berg 		return -EINVAL;
2015c75a208SJohannes Berg 
202aea99650SJohannes Berg 	if (WARN_ON(!sta_mask))
203aea99650SJohannes Berg 		return -EINVAL;
204aea99650SJohannes Berg 
2055c75a208SJohannes Berg 	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
2065c75a208SJohannes Berg 	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)
2075c75a208SJohannes Berg 		memcpy(cmd.u.add.key + IWL_SEC_WEP_KEY_OFFSET, keyconf->key,
2085c75a208SJohannes Berg 		       keyconf->keylen);
2095c75a208SJohannes Berg 	else
2105c75a208SJohannes Berg 		memcpy(cmd.u.add.key, keyconf->key, keyconf->keylen);
2115c75a208SJohannes Berg 
2125c75a208SJohannes Berg 	if (keyconf->cipher == WLAN_CIPHER_SUITE_TKIP) {
2135c75a208SJohannes Berg 		memcpy(cmd.u.add.tkip_mic_rx_key,
2145c75a208SJohannes Berg 		       keyconf->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
2155c75a208SJohannes Berg 		       8);
2165c75a208SJohannes Berg 		memcpy(cmd.u.add.tkip_mic_tx_key,
2175c75a208SJohannes Berg 		       keyconf->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY,
2185c75a208SJohannes Berg 		       8);
2195c75a208SJohannes Berg 	}
2205c75a208SJohannes Berg 
2215c75a208SJohannes Berg 	ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);
2225c75a208SJohannes Berg 	if (ret)
2235c75a208SJohannes Berg 		return ret;
2245c75a208SJohannes Berg 
2255c75a208SJohannes Berg 	/*
2265c75a208SJohannes Berg 	 * For WEP, the same key is used for multicast and unicast so need to
2275c75a208SJohannes Berg 	 * upload it again. If this fails, remove the original as well.
2285c75a208SJohannes Berg 	 */
2295c75a208SJohannes Berg 	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
2305c75a208SJohannes Berg 	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {
2315c75a208SJohannes Berg 		cmd.u.add.key_flags ^= cpu_to_le32(IWL_SEC_KEY_FLAG_MCAST_KEY);
2325c75a208SJohannes Berg 		ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd);
2335c75a208SJohannes Berg 		if (ret)
2345c75a208SJohannes Berg 			__iwl_mvm_sec_key_del(mvm, sta_mask, key_flags,
2355c75a208SJohannes Berg 					      keyconf->keyidx, 0);
2365c75a208SJohannes Berg 	}
2375c75a208SJohannes Berg 
2385c75a208SJohannes Berg 	return ret;
2395c75a208SJohannes Berg }
2405c75a208SJohannes Berg 
iwl_mvm_sec_key_add(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * keyconf)2410945f976SAvraham Stern int iwl_mvm_sec_key_add(struct iwl_mvm *mvm,
2420945f976SAvraham Stern 			struct ieee80211_vif *vif,
2430945f976SAvraham Stern 			struct ieee80211_sta *sta,
2440945f976SAvraham Stern 			struct ieee80211_key_conf *keyconf)
2450945f976SAvraham Stern {
2460945f976SAvraham Stern 	u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf);
2470945f976SAvraham Stern 	u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf);
248d615ea32SJohannes Berg 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
249d615ea32SJohannes Berg 	struct iwl_mvm_vif_link_info *mvm_link = NULL;
250d615ea32SJohannes Berg 	int ret;
2510945f976SAvraham Stern 
252d615ea32SJohannes Berg 	if (keyconf->keyidx == 4 || keyconf->keyidx == 5) {
253d615ea32SJohannes Berg 		unsigned int link_id = 0;
254d615ea32SJohannes Berg 
255d615ea32SJohannes Berg 		/* set to -1 for non-MLO right now */
256d615ea32SJohannes Berg 		if (keyconf->link_id >= 0)
257d615ea32SJohannes Berg 			link_id = keyconf->link_id;
258d615ea32SJohannes Berg 
259d615ea32SJohannes Berg 		mvm_link = mvmvif->link[link_id];
260d615ea32SJohannes Berg 		if (WARN_ON(!mvm_link))
261d615ea32SJohannes Berg 			return -EINVAL;
262d615ea32SJohannes Berg 
263d615ea32SJohannes Berg 		if (mvm_link->igtk) {
264d615ea32SJohannes Berg 			IWL_DEBUG_MAC80211(mvm, "remove old IGTK %d\n",
265d615ea32SJohannes Berg 					   mvm_link->igtk->keyidx);
266d615ea32SJohannes Berg 			ret = iwl_mvm_sec_key_del(mvm, vif, sta,
267d615ea32SJohannes Berg 						  mvm_link->igtk);
268d615ea32SJohannes Berg 			if (ret)
269d615ea32SJohannes Berg 				IWL_ERR(mvm,
270d615ea32SJohannes Berg 					"failed to remove old IGTK (ret=%d)\n",
271d615ea32SJohannes Berg 					ret);
272d615ea32SJohannes Berg 		}
273d615ea32SJohannes Berg 
274d615ea32SJohannes Berg 		WARN_ON(mvm_link->igtk);
275d615ea32SJohannes Berg 	}
276d615ea32SJohannes Berg 
277d615ea32SJohannes Berg 	ret = iwl_mvm_mld_send_key(mvm, sta_mask, key_flags, keyconf);
278d615ea32SJohannes Berg 	if (ret)
279d615ea32SJohannes Berg 		return ret;
280d615ea32SJohannes Berg 
281d615ea32SJohannes Berg 	if (mvm_link)
282d615ea32SJohannes Berg 		mvm_link->igtk = keyconf;
283d615ea32SJohannes Berg 
284d615ea32SJohannes Berg 	/* We don't really need this, but need it to be not invalid,
285d615ea32SJohannes Berg 	 * and if we switch links multiple times it might go to be
286d615ea32SJohannes Berg 	 * invalid when removed.
287d615ea32SJohannes Berg 	 */
288d615ea32SJohannes Berg 	keyconf->hw_key_idx = 0;
289d615ea32SJohannes Berg 
290d615ea32SJohannes Berg 	return 0;
2910945f976SAvraham Stern }
2920945f976SAvraham Stern 
_iwl_mvm_sec_key_del(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * keyconf,u32 flags)2935c75a208SJohannes Berg static int _iwl_mvm_sec_key_del(struct iwl_mvm *mvm,
2945c75a208SJohannes Berg 				struct ieee80211_vif *vif,
2955c75a208SJohannes Berg 				struct ieee80211_sta *sta,
2965c75a208SJohannes Berg 				struct ieee80211_key_conf *keyconf,
2975c75a208SJohannes Berg 				u32 flags)
2985c75a208SJohannes Berg {
2995c75a208SJohannes Berg 	u32 sta_mask = iwl_mvm_get_sec_sta_mask(mvm, vif, sta, keyconf);
3005c75a208SJohannes Berg 	u32 key_flags = iwl_mvm_get_sec_flags(mvm, vif, sta, keyconf);
301d615ea32SJohannes Berg 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
3025c75a208SJohannes Berg 	int ret;
3035c75a208SJohannes Berg 
304aea99650SJohannes Berg 	if (WARN_ON(!sta_mask))
305aea99650SJohannes Berg 		return -EINVAL;
306aea99650SJohannes Berg 
307d615ea32SJohannes Berg 	if (keyconf->keyidx == 4 || keyconf->keyidx == 5) {
308d615ea32SJohannes Berg 		struct iwl_mvm_vif_link_info *mvm_link;
309d615ea32SJohannes Berg 		unsigned int link_id = 0;
310d615ea32SJohannes Berg 
311d615ea32SJohannes Berg 		/* set to -1 for non-MLO right now */
312d615ea32SJohannes Berg 		if (keyconf->link_id >= 0)
313d615ea32SJohannes Berg 			link_id = keyconf->link_id;
314d615ea32SJohannes Berg 
315d615ea32SJohannes Berg 		mvm_link = mvmvif->link[link_id];
316d615ea32SJohannes Berg 		if (WARN_ON(!mvm_link))
317d615ea32SJohannes Berg 			return -EINVAL;
318d615ea32SJohannes Berg 
319d615ea32SJohannes Berg 		if (mvm_link->igtk == keyconf) {
320d615ea32SJohannes Berg 			/* no longer in HW - mark for later */
321d615ea32SJohannes Berg 			mvm_link->igtk->hw_key_idx = STA_KEY_IDX_INVALID;
322d615ea32SJohannes Berg 			mvm_link->igtk = NULL;
323d615ea32SJohannes Berg 		}
324d615ea32SJohannes Berg 	}
325d615ea32SJohannes Berg 
3265c75a208SJohannes Berg 	ret = __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags, keyconf->keyidx,
3275c75a208SJohannes Berg 				    flags);
3285c75a208SJohannes Berg 	if (ret)
3295c75a208SJohannes Berg 		return ret;
3305c75a208SJohannes Berg 
3315c75a208SJohannes Berg 	/* For WEP, delete the key again as unicast */
3325c75a208SJohannes Berg 	if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
3335c75a208SJohannes Berg 	    keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {
3345c75a208SJohannes Berg 		key_flags ^= IWL_SEC_KEY_FLAG_MCAST_KEY;
3355c75a208SJohannes Berg 		ret = __iwl_mvm_sec_key_del(mvm, sta_mask, key_flags,
3365c75a208SJohannes Berg 					    keyconf->keyidx, flags);
3375c75a208SJohannes Berg 	}
3385c75a208SJohannes Berg 
3395c75a208SJohannes Berg 	return ret;
3405c75a208SJohannes Berg }
3415c75a208SJohannes Berg 
iwl_mvm_sec_key_del(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * keyconf)3425c75a208SJohannes Berg int iwl_mvm_sec_key_del(struct iwl_mvm *mvm,
3435c75a208SJohannes Berg 			struct ieee80211_vif *vif,
3445c75a208SJohannes Berg 			struct ieee80211_sta *sta,
3455c75a208SJohannes Berg 			struct ieee80211_key_conf *keyconf)
3465c75a208SJohannes Berg {
3475c75a208SJohannes Berg 	return _iwl_mvm_sec_key_del(mvm, vif, sta, keyconf, 0);
3485c75a208SJohannes Berg }
3495c75a208SJohannes Berg 
iwl_mvm_sec_key_remove_ap_iter(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key,void * data)3505c75a208SJohannes Berg static void iwl_mvm_sec_key_remove_ap_iter(struct ieee80211_hw *hw,
3515c75a208SJohannes Berg 					   struct ieee80211_vif *vif,
3525c75a208SJohannes Berg 					   struct ieee80211_sta *sta,
3535c75a208SJohannes Berg 					   struct ieee80211_key_conf *key,
3545c75a208SJohannes Berg 					   void *data)
3555c75a208SJohannes Berg {
3565c75a208SJohannes Berg 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
357072573f6SJohannes Berg 	unsigned int link_id = (uintptr_t)data;
3585c75a208SJohannes Berg 
3595c75a208SJohannes Berg 	if (key->hw_key_idx == STA_KEY_IDX_INVALID)
3605c75a208SJohannes Berg 		return;
3615c75a208SJohannes Berg 
3625c75a208SJohannes Berg 	if (sta)
3635c75a208SJohannes Berg 		return;
3645c75a208SJohannes Berg 
365072573f6SJohannes Berg 	if (key->link_id >= 0 && key->link_id != link_id)
366072573f6SJohannes Berg 		return;
367072573f6SJohannes Berg 
3685c75a208SJohannes Berg 	_iwl_mvm_sec_key_del(mvm, vif, NULL, key, CMD_ASYNC);
3695c75a208SJohannes Berg 	key->hw_key_idx = STA_KEY_IDX_INVALID;
3705c75a208SJohannes Berg }
3715c75a208SJohannes Berg 
iwl_mvm_sec_key_remove_ap(struct iwl_mvm * mvm,struct ieee80211_vif * vif,struct iwl_mvm_vif_link_info * link,unsigned int link_id)3725c75a208SJohannes Berg void iwl_mvm_sec_key_remove_ap(struct iwl_mvm *mvm,
373ba9eef6bSGregory Greenman 			       struct ieee80211_vif *vif,
374072573f6SJohannes Berg 			       struct iwl_mvm_vif_link_info *link,
375072573f6SJohannes Berg 			       unsigned int link_id)
3765c75a208SJohannes Berg {
3775c75a208SJohannes Berg 	u32 sec_key_id = WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD);
3785c75a208SJohannes Berg 	u8 sec_key_ver = iwl_fw_lookup_cmd_ver(mvm->fw, sec_key_id, 0);
3795c75a208SJohannes Berg 
3806a2a71e5SJohannes Berg 	if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION ||
381ba9eef6bSGregory Greenman 			 link->ap_sta_id == IWL_MVM_INVALID_STA))
3825c75a208SJohannes Berg 		return;
3835c75a208SJohannes Berg 
3845c75a208SJohannes Berg 	if (!sec_key_ver)
3855c75a208SJohannes Berg 		return;
3865c75a208SJohannes Berg 
387*32411625SJohannes Berg 	ieee80211_iter_keys(mvm->hw, vif,
3885c75a208SJohannes Berg 			    iwl_mvm_sec_key_remove_ap_iter,
389072573f6SJohannes Berg 			    (void *)(uintptr_t)link_id);
3905c75a208SJohannes Berg }
391