* GPL LICENSE SUMMARY
  *
  * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2019 Intel Corporation
+ * Copyright (C) 2018 - 2020 Intel Corporation
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
  * BSD LICENSE
  *
  * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2019 Intel Corporation
+ * Copyright (C) 2018 - 2020 Intel Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "mvm.h"
 #include "constants.h"
 
+struct iwl_mvm_pasn_sta {
+       struct list_head list;
+       struct iwl_mvm_int_sta int_sta;
+       u8 addr[ETH_ALEN];
+};
+
 static int iwl_mvm_ftm_responder_set_bw_v1(struct cfg80211_chan_def *chandef,
                                           u8 *bw, u8 *ctrl_ch_position)
 {
        return iwl_mvm_send_cmd(mvm, &hcmd);
 }
 
+int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,
+                                     struct ieee80211_vif *vif,
+                                     u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
+                                     u8 *hltk, u32 hltk_len)
+{
+       int ret;
+       struct iwl_mvm_pasn_sta *sta;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       sta = kmalloc(sizeof(*sta), GFP_KERNEL);
+       if (!sta)
+               return -ENOBUFS;
+
+       ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr, cipher, tk,
+                                  tk_len);
+       if (ret) {
+               kfree(sta);
+               return ret;
+       }
+
+       // TODO: set the HLTK to fw
+
+       memcpy(sta->addr, addr, ETH_ALEN);
+       list_add_tail(&sta->list, &mvm->resp_pasn_list);
+       return 0;
+}
+
+static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm,
+                                     struct ieee80211_vif *vif,
+                                     struct iwl_mvm_pasn_sta *sta)
+{
+       list_del(&sta->list);
+       iwl_mvm_rm_sta_id(mvm, vif, sta->int_sta.sta_id);
+       iwl_mvm_dealloc_int_sta(mvm, &sta->int_sta);
+       kfree(sta);
+}
+
+int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif, u8 *addr)
+{
+       struct iwl_mvm_pasn_sta *sta, *prev;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) {
+               if (!memcmp(sta->addr, addr, ETH_ALEN)) {
+                       iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
+                       return 0;
+               }
+       }
+
+       IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr);
+       return -EINVAL;
+}
+
 int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        return ret;
 }
 
+void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_pasn_sta *sta, *prev;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list)
+               iwl_mvm_resp_del_pasn_sta(mvm, vif, sta);
+}
+
 void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm,
                                   struct ieee80211_vif *vif)
 {
        if (!vif->bss_conf.ftm_responder)
                return;
 
+       iwl_mvm_ftm_responder_clear(mvm, vif);
        iwl_mvm_ftm_start_responder(mvm, vif);
 }
 
 
 
        iwl_mvm_update_quotas(mvm, false, NULL);
 
+       iwl_mvm_ftm_responder_clear(mvm, vif);
+
        /*
         * This is not very nice, but the simplest:
         * For older FWs removing the mcast sta before the bcast station may
 
                int responses[IWL_MVM_TOF_MAX_APS];
        } ftm_initiator;
 
+       struct list_head resp_pasn_list;
+
        struct {
                u8 d0i3_resp;
        } cmd_ver;
                                   struct ieee80211_vif *vif);
 void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm,
                                 struct iwl_rx_cmd_buffer *rxb);
+int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif, u8 *addr);
+int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm,
+                                     struct ieee80211_vif *vif,
+                                     u8 *addr, u32 cipher, u8 *tk, u32 tk_len,
+                                     u8 *hltk, u32 hltk_len);
+void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif);
 
 /* FTM initiator */
 void iwl_mvm_ftm_restart(struct iwl_mvm *mvm);
 
        INIT_LIST_HEAD(&mvm->async_handlers_list);
        spin_lock_init(&mvm->time_event_lock);
        INIT_LIST_HEAD(&mvm->ftm_initiator.loc_list);
+       INIT_LIST_HEAD(&mvm->resp_pasn_list);
 
        INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk);
        INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
 
 }
 
 static int iwl_mvm_add_int_sta_with_queue(struct iwl_mvm *mvm, int macidx,
-                                         int maccolor,
+                                         int maccolor, u8 *addr,
                                          struct iwl_mvm_int_sta *sta,
                                          u16 *queue, int fifo)
 {
        if (!iwl_mvm_has_new_tx_api(mvm))
                iwl_mvm_enable_aux_snif_queue(mvm, *queue, sta->sta_id, fifo);
 
-       ret = iwl_mvm_add_int_sta_common(mvm, sta, NULL, macidx, maccolor);
+       ret = iwl_mvm_add_int_sta_common(mvm, sta, addr, macidx, maccolor);
        if (ret) {
                if (!iwl_mvm_has_new_tx_api(mvm))
                        iwl_mvm_disable_txq(mvm, NULL, *queue,
        if (ret)
                return ret;
 
-       ret = iwl_mvm_add_int_sta_with_queue(mvm, MAC_INDEX_AUX, 0,
+       ret = iwl_mvm_add_int_sta_with_queue(mvm, MAC_INDEX_AUX, 0, NULL,
                                             &mvm->aux_sta, &mvm->aux_queue,
                                             IWL_MVM_TX_FIFO_MCAST);
        if (ret) {
        lockdep_assert_held(&mvm->mutex);
 
        return iwl_mvm_add_int_sta_with_queue(mvm, mvmvif->id, mvmvif->color,
-                                             &mvm->snif_sta, &mvm->snif_queue,
+                                             NULL, &mvm->snif_sta,
+                                             &mvm->snif_queue,
                                              IWL_MVM_TX_FIFO_BE);
 }
 
 
        return ieee80211_sn_sub(sn, tid_data->next_reclaimed);
 }
+
+int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                        struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher,
+                        u8 *key, u32 key_len)
+{
+       int ret;
+       u16 queue;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct ieee80211_key_conf *keyconf;
+
+       ret = iwl_mvm_allocate_int_sta(mvm, sta, 0,
+                                      NL80211_IFTYPE_UNSPECIFIED,
+                                      IWL_STA_LINK);
+       if (ret)
+               return ret;
+
+       ret = iwl_mvm_add_int_sta_with_queue(mvm, mvmvif->id, mvmvif->color,
+                                            addr, sta, &queue,
+                                            IWL_MVM_TX_FIFO_BE);
+       if (ret)
+               goto out;
+
+       keyconf = kzalloc(sizeof(*keyconf) + key_len, GFP_KERNEL);
+       if (!keyconf) {
+               ret = -ENOBUFS;
+               goto out;
+       }
+
+       keyconf->cipher = cipher;
+       memcpy(keyconf->key, key, key_len);
+       keyconf->keylen = key_len;
+
+       ret = iwl_mvm_send_sta_key(mvm, sta->sta_id, keyconf, false,
+                                  0, NULL, 0, 0, true);
+       kfree(keyconf);
+       return 0;
+out:
+       iwl_mvm_dealloc_int_sta(mvm, sta);
+       return ret;
+}
 
                                       bool disable);
 void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk);
+int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                        struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher,
+                        u8 *key, u32 key_len);
 
 #endif /* __sta_h__ */