new file mode 100644
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "key.h"
+#include "fw/msg_tx.h"
+#include "fw/fw_msg.h"
+#include "sta.h"
+#include "tx/single_cfm.h"
+#include "tx/agg_cfm.h"
+#include "tx/tx_queue.h"
+
+static int cmd_set_key(struct cl_hw *cl_hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ int error = 0;
+ struct mm_key_add_cfm *key_add_cfm;
+ u8 cipher_suite = 0;
+
+ /* Retrieve the cipher suite selector */
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ cipher_suite = MAC_CIPHER_SUITE_WEP40;
+ break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ cipher_suite = MAC_CIPHER_SUITE_WEP104;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ cipher_suite = MAC_CIPHER_SUITE_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ cipher_suite = MAC_CIPHER_SUITE_CCMP;
+ break;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ cipher_suite = MAC_CIPHER_SUITE_GCMP;
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ return -EOPNOTSUPP;
+ default:
+ return -EINVAL;
+ }
+
+ error = cl_msg_tx_key_add(cl_hw, vif, sta, key, cipher_suite);
+ if (error)
+ return error;
+
+ key_add_cfm = (struct mm_key_add_cfm *)(cl_hw->msg_cfm_params[MM_KEY_ADD_CFM]);
+ if (!key_add_cfm)
+ return -ENOMSG;
+
+ if (key_add_cfm->status != 0) {
+ cl_dbg_verbose(cl_hw, "Status Error (%u)\n", key_add_cfm->status);
+ cl_msg_tx_free_cfm_params(cl_hw, MM_KEY_ADD_CFM);
+ return -EIO;
+ }
+
+ /* Save the index retrieved from firmware */
+ key->hw_key_idx = key_add_cfm->hw_key_idx;
+
+ cl_msg_tx_free_cfm_params(cl_hw, MM_KEY_ADD_CFM);
+
+ /*
+ * Now inform mac80211 about our choices regarding header fields generation:
+ * we let mac80211 take care of all generations
+ */
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+
+ if (sta) {
+ struct cl_sta *cl_sta = (struct cl_sta *)sta->drv_priv;
+
+ cl_sta->key_conf = key;
+ } else {
+ struct cl_vif *cl_vif = (struct cl_vif *)vif->drv_priv;
+
+ cl_vif->key_conf = key;
+ }
+
+ return error;
+}
+
+static int cmd_disable_key(struct cl_hw *cl_hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ if (sta) {
+ struct cl_sta *cl_sta = (struct cl_sta *)sta->drv_priv;
+
+ cl_sta->key_conf = NULL;
+ cl_sta->key_disable = true;
+
+ /*
+ * Make sure there aren't any packets in firmware before deleting the key,
+ * otherwise they will be transmitted without encryption.
+ */
+ cl_txq_flush_sta(cl_hw, cl_sta);
+ cl_single_cfm_poll_empty_sta(cl_hw, cl_sta->sta_idx);
+ cl_agg_cfm_poll_empty_sta(cl_hw, cl_sta);
+ } else {
+ struct cl_vif *cl_vif = (struct cl_vif *)vif->drv_priv;
+
+ cl_vif->key_conf = NULL;
+ }
+
+ return cl_msg_tx_key_del(cl_hw, key->hw_key_idx);
+}
+
+int cl_key_set(struct cl_hw *cl_hw,
+ enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case SET_KEY:
+ error = cmd_set_key(cl_hw, vif, sta, key);
+ break;
+
+ case DISABLE_KEY:
+ error = cmd_disable_key(cl_hw, vif, sta, key);
+ break;
+
+ default:
+ error = -EINVAL;
+ break;
+ }
+
+ return error;
+}
+
+struct ieee80211_key_conf *cl_key_get(struct cl_sta *cl_sta)
+{
+ if (cl_sta->key_conf)
+ return cl_sta->key_conf;
+
+ if (cl_sta->cl_vif->key_conf)
+ return cl_sta->cl_vif->key_conf;
+
+ return NULL;
+}
+
+bool cl_key_is_cipher_ccmp_gcmp(struct ieee80211_key_conf *keyconf)
+{
+ u32 cipher;
+
+ if (!keyconf)
+ return false;
+
+ cipher = keyconf->cipher;
+
+ return ((cipher == WLAN_CIPHER_SUITE_CCMP) ||
+ (cipher == WLAN_CIPHER_SUITE_GCMP) ||
+ (cipher == WLAN_CIPHER_SUITE_GCMP_256));
+}
+
+void cl_key_ccmp_gcmp_pn_to_hdr(u8 *hdr, u64 pn, int key_id)
+{
+ hdr[0] = pn;
+ hdr[1] = pn >> 8;
+ hdr[2] = 0;
+ hdr[3] = 0x20 | (key_id << 6);
+ hdr[4] = pn >> 16;
+ hdr[5] = pn >> 24;
+ hdr[6] = pn >> 32;
+ hdr[7] = pn >> 40;
+}
+
+u8 cl_key_get_cipher_len(struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_key_conf *key_conf = tx_info->control.hw_key;
+
+ if (key_conf) {
+ switch (key_conf->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ return IEEE80211_WEP_IV_LEN;
+ case WLAN_CIPHER_SUITE_TKIP:
+ return IEEE80211_TKIP_IV_LEN;
+ case WLAN_CIPHER_SUITE_CCMP:
+ return IEEE80211_CCMP_HDR_LEN;
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ return IEEE80211_CCMP_256_HDR_LEN;
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ return IEEE80211_GCMP_HDR_LEN;
+ }
+ }
+
+ return 0;
+}