new file mode 100644
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Copyright(c) 2019-2022, Celeno Communications Ltd. */
+
+#include "chip.h"
+#include "phy.h"
+#include "fw.h"
+#include "sta.h"
+#include "enhanced_tim.h"
+#include "key.h"
+
+#define DECRYPT_CCMPSUCCESS_FLAGS (RX_FLAG_DECRYPTED | RX_FLAG_MIC_STRIPPED)
+
+void cl_vif_key_init(struct cl_vif *cl_vif)
+{
+ INIT_LIST_HEAD(&cl_vif->key_list_head);
+}
+
+static struct cl_key_conf *cl_vif_key_find(struct cl_vif *cl_vif,
+ struct ieee80211_key_conf *key_conf_in)
+{
+ struct cl_key_conf *key, *tmp, *out = NULL;
+
+ if (!key_conf_in)
+ return NULL;
+
+ list_for_each_entry_safe(key, tmp, &cl_vif->key_list_head, list) {
+ struct ieee80211_key_conf *key_conf = key->key_conf;
+
+ if (key_conf_in->keyidx != key_conf->keyidx)
+ continue;
+
+ out = key;
+ break;
+ }
+
+ return out;
+}
+
+static struct ieee80211_key_conf *cl_vif_key_conf_default(struct cl_vif *cl_vif)
+{
+ struct cl_key_conf *key, *tmp;
+ struct ieee80211_key_conf *out = NULL;
+
+ list_for_each_entry_safe(key, tmp, &cl_vif->key_list_head, list) {
+ if (key->key_conf->keyidx != cl_vif->key_idx_default)
+ continue;
+
+ out = key->key_conf;
+ break;
+ }
+
+ return out;
+}
+
+static int cl_vif_key_add(struct cl_vif *cl_vif, struct ieee80211_key_conf *key_conf)
+{
+ struct cl_key_conf *key = NULL, *old_key = NULL;
+
+ key = kzalloc(sizeof(*key), GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+
+ if (!list_empty(&cl_vif->key_list_head))
+ old_key = list_first_entry(&cl_vif->key_list_head, struct cl_key_conf, list);
+
+ cl_vif->key_idx_default = old_key ? old_key->key_conf->keyidx : key_conf->keyidx;
+ key->key_conf = key_conf;
+ list_add_tail(&key->list, &cl_vif->key_list_head);
+
+ return 0;
+}
+
+static void cl_vif_key_del(struct cl_vif *cl_vif, struct ieee80211_key_conf *key_conf_in)
+{
+ struct cl_key_conf *key, *tmp;
+
+ if (!key_conf_in)
+ return;
+
+ list_for_each_entry_safe(key, tmp, &cl_vif->key_list_head, list) {
+ struct ieee80211_key_conf *key_conf = key->key_conf;
+
+ if (key_conf_in->keyidx != key_conf->keyidx)
+ continue;
+
+ list_del(&key->list);
+ kfree(key);
+ }
+
+ if (!list_empty(&cl_vif->key_list_head)) {
+ struct cl_key_conf *new_key = list_first_entry(&cl_vif->key_list_head,
+ struct cl_key_conf, list);
+
+ cl_vif->key_idx_default = new_key->key_conf->keyidx;
+ }
+}
+
+static int cl_vif_key_check_and_add(struct cl_vif *cl_vif,
+ struct ieee80211_key_conf *key_conf)
+{
+ struct cl_key_conf *key = cl_vif_key_find(cl_vif, key_conf);
+
+ if (key) {
+ cl_dbg_warn(cl_vif->cl_hw,
+ "[%s] error: previous key found. delete old key and add new key\n",
+ __func__);
+ cl_vif_key_del(cl_vif, key->key_conf);
+ }
+
+ return cl_vif_key_add(cl_vif, key_conf);
+}
+
+static inline void cl_ccmp_hdr2pn(u8 *pn, u8 *hdr)
+{
+ pn[0] = hdr[7];
+ pn[1] = hdr[6];
+ pn[2] = hdr[5];
+ pn[3] = hdr[4];
+ pn[4] = hdr[1];
+ pn[5] = hdr[0];
+}
+
+static int cl_key_validate_pn(struct cl_hw *cl_hw, struct cl_sta *cl_sta, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ int hdrlen = 0, res = 0;
+ u8 pn[IEEE80211_CCMP_PN_LEN];
+ u8 tid = 0;
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ tid = ieee80211_get_tid(hdr);
+
+ cl_ccmp_hdr2pn(pn, skb->data + hdrlen);
+ res = memcmp(pn, cl_sta->rx_pn[tid], IEEE80211_CCMP_PN_LEN);
+ if (res < 0) {
+ cl_hw->rx_info.pkt_drop_invalid_pn++;
+ return -1;
+ }
+
+ memcpy(cl_sta->rx_pn[tid], pn, IEEE80211_CCMP_PN_LEN);
+
+ return 0;
+}
+
+void cl_vif_key_deinit(struct cl_vif *cl_vif)
+{
+ struct cl_key_conf *key, *tmp;
+
+ list_for_each_entry_safe(key, tmp, &cl_vif->key_list_head, list) {
+ list_del(&key->list);
+ kfree(key);
+ }
+}
+
+static int cl_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 {
+ error = cl_vif_key_check_and_add((struct cl_vif *)vif->drv_priv, key);
+ }
+
+ return error;
+}
+
+static int cl_cmd_disable_key(struct cl_hw *cl_hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ int ret = 0;
+ struct cl_sta *cl_sta = NULL;
+ struct cl_tx_queue *tx_queue = &cl_hw->tx_queues->single[HIGH_PRIORITY_QUEUE];
+
+ if (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_sta->stop_tx = true;
+ cl_single_cfm_clear_tim_bit_sta(cl_hw, cl_sta->sta_idx);
+ cl_agg_cfm_clear_tim_bit_sta(cl_hw, cl_sta);
+ 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);
+
+ if (!list_empty(&tx_queue->hdrs)) {
+ spin_lock_bh(&cl_hw->tx_lock_single);
+ cl_enhanced_tim_set_tx_single(cl_hw, HIGH_PRIORITY_QUEUE,
+ tx_queue->hw_index,
+ false, tx_queue->cl_sta,
+ tx_queue->tid);
+ spin_unlock_bh(&cl_hw->tx_lock_single);
+ }
+ } else {
+ cl_vif_key_del((struct cl_vif *)vif->drv_priv, key);
+ }
+
+ ret = cl_msg_tx_key_del(cl_hw, key->hw_key_idx);
+
+ if (cl_sta)
+ cl_sta->stop_tx = false;
+
+ return ret;
+}
+
+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 = cl_cmd_set_key(cl_hw, vif, sta, key);
+ break;
+
+ case DISABLE_KEY:
+ error = cl_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)
+ return cl_vif_key_conf_default(cl_sta->cl_vif);
+
+ 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;
+}
+
+int cl_key_handle_pn_validation(struct cl_hw *cl_hw, struct sk_buff *skb,
+ struct cl_sta *cl_sta)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ ieee80211_is_frag(hdr))
+ return CL_PN_VALID_STATE_NOT_NEEDED;
+
+ if (!(status->flag & DECRYPT_CCMPSUCCESS_FLAGS))
+ return CL_PN_VALID_STATE_NOT_NEEDED;
+
+ if (!cl_sta)
+ return CL_PN_VALID_STATE_NOT_NEEDED;
+
+ if (cl_key_validate_pn(cl_hw, cl_sta, skb))
+ return CL_PN_VALID_STATE_FAILED;
+
+ status = IEEE80211_SKB_RXCB(skb);
+ status->flag |= RX_FLAG_PN_VALIDATED;
+
+ return CL_PN_VALID_STATE_SUCCESS;
+}