Ver Fonte

qcacld-3.0: Support SAP fast BSS transition in driver

When driver receive auth request frame with FT algorithm,
offload it to hostapd.

When driver receive (re)assoc request whith FT-PSK in RSN IE.
offload it to hostapd.

Filter FT related IE from hostapd, and append it to the end
of the (re)assoc response frame

Change-Id: Id11cce6898615bb6b0cb361ea7b23ea2014f0bae
CRs-Fixed: 4202696
Ben Wang há 2 anos atrás
pai
commit
dafdfb98ff

+ 2 - 0
core/hdd/inc/wlan_hdd_main.h

@@ -835,6 +835,7 @@ struct hdd_fw_txrx_stats {
  * @acs_in_progress: In progress acs flag for an adapter
  * @client_count: client count per dot11_mode
  * @country_ie_updated: country ie is updated or not by hdd hostapd
+ * @during_auth_offload: auth mgmt frame is offloading to hostapd
  */
 struct hdd_ap_ctx {
 	struct hdd_hostapd_state hostapd_state;
@@ -855,6 +856,7 @@ struct hdd_ap_ctx {
 	qdf_atomic_t acs_in_progress;
 	uint16_t client_count[QCA_WLAN_802_11_MODE_INVALID];
 	bool country_ie_updated;
+	bool during_auth_offload;
 };
 
 /**

+ 44 - 0
core/hdd/src/wlan_hdd_assoc.c

@@ -2244,6 +2244,50 @@ static inline void hdd_translate_sae_rsn_to_csr_auth(int8_t auth_suite[4],
 }
 #endif
 
+void *hdd_filter_ft_info(const uint8_t *frame, size_t len,
+			 uint32_t *ft_info_len)
+{
+	uint32_t ft_ie_len, md_ie_len, rsn_ie_len, ie_len;
+	const uint8_t *rsn_ie, *md_ie, *ft_ie;
+	void *ft_info;
+
+	ft_ie_len = 0;
+	md_ie_len = 0;
+	rsn_ie_len = 0;
+	ie_len = len - DOT11F_FF_CAPABILITIES_LEN - DOT11F_FF_STATUS_LEN
+			   - DOT11F_IE_AID_MAX_LEN - sizeof(tSirMacMgmtHdr);
+	rsn_ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_RSN, frame, ie_len);
+
+	if (rsn_ie) {
+		rsn_ie_len = rsn_ie[1] + 2;
+		QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
+			(void *)rsn_ie, rsn_ie_len);
+	}
+	md_ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_MOBILITYDOMAIN,
+					 frame, ie_len);
+	if (md_ie) {
+		md_ie_len = md_ie[1] + 2;
+		QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
+			(void *)md_ie, md_ie_len);
+	}
+	ft_ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_FTINFO, frame, ie_len);
+	if (ft_ie)
+		ft_ie_len = ft_ie[1] + 2;
+
+	*ft_info_len = rsn_ie_len + md_ie_len + ft_ie_len;
+	ft_info = qdf_mem_malloc(*ft_info_len);
+	if (!ft_info)
+		return NULL;
+	if (rsn_ie_len)
+		qdf_mem_copy(ft_info, rsn_ie, rsn_ie_len);
+	if (md_ie_len)
+		qdf_mem_copy(ft_info + rsn_ie_len, md_ie, md_ie_len);
+	if (ft_ie_len)
+		qdf_mem_copy(ft_info + rsn_ie_len + md_ie_len,
+			     ft_ie, ft_ie_len);
+	return ft_info;
+}
+
 /**
  * hdd_translate_rsn_to_csr_auth_type() - Translate RSN to CSR auth type
  * @auth_suite: auth suite

+ 6 - 0
core/hdd/src/wlan_hdd_cfg80211.c

@@ -20304,6 +20304,7 @@ static int wlan_hdd_add_key_vdev(mac_handle_t mac_handle,
 	int errno;
 	enum wlan_crypto_cipher_type cipher;
 	bool ft_mode = false;
+	struct hdd_ap_ctx *hdd_ap_ctx;
 
 	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
 
@@ -20355,6 +20356,11 @@ static int wlan_hdd_add_key_vdev(mac_handle_t mac_handle,
 	switch (adapter->device_mode) {
 	case QDF_SAP_MODE:
 	case QDF_P2P_GO_MODE:
+		hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter);
+		if (hdd_ap_ctx->during_auth_offload) {
+			hdd_err("don't need install key during auth");
+			return -EINVAL;
+		}
 		errno = wlan_hdd_add_key_sap(adapter, pairwise,
 					     key_index, cipher);
 		break;

+ 4 - 1
core/hdd/src/wlan_hdd_hostapd.c

@@ -4880,8 +4880,9 @@ int wlan_hdd_cfg80211_update_apies(struct hdd_adapter *adapter)
 			      WLAN_EID_INTERWORKING);
 	wlan_hdd_add_extra_ie(adapter, genie, &total_ielen,
 			      WLAN_EID_ADVERTISEMENT_PROTOCOL);
-
 	wlan_hdd_add_extra_ie(adapter, genie, &total_ielen, WLAN_ELEMID_RSNXE);
+	wlan_hdd_add_extra_ie(adapter, genie, &total_ielen,
+			      WLAN_ELEMID_MOBILITY_DOMAIN);
 #ifdef FEATURE_WLAN_WAPI
 	if (QDF_SAP_MODE == adapter->device_mode) {
 		wlan_hdd_add_extra_ie(adapter, genie, &total_ielen,
@@ -4918,6 +4919,8 @@ int wlan_hdd_cfg80211_update_apies(struct hdd_adapter *adapter)
 				     &proberesp_ies_len);
 	wlan_hdd_add_extra_ie(adapter, proberesp_ies, &proberesp_ies_len,
 			      WLAN_ELEMID_RSNXE);
+	wlan_hdd_add_extra_ie(adapter, proberesp_ies, &proberesp_ies_len,
+			      WLAN_ELEMID_MOBILITY_DOMAIN);
 
 	if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) {
 		update_ie.ieBufferlength = proberesp_ies_len;

+ 14 - 0
core/hdd/src/wlan_hdd_hostapd.h

@@ -44,6 +44,20 @@ struct hdd_adapter *hdd_wlan_create_ap_dev(struct hdd_context *hdd_ctx,
 enum csr_akm_type
 hdd_translate_rsn_to_csr_auth_type(uint8_t auth_suite[4]);
 
+/**
+ * hdd_filter_ft_info() -
+ * This function to filter fast BSS transition related IE
+ * @frame: pointer to the input frame.
+ * @len: input frame length.
+ * @ft_info_len: store the total length of FT related IE.
+ *
+ * Return: pointer to a buffer which stored the FT related IE
+ * This is a malloced memory that must be freed by the caller
+ */
+
+void *hdd_filter_ft_info(const uint8_t *frame,
+			 size_t len, uint32_t *ft_info_len);
+
 /**
  * hdd_softap_set_channel_change() -
  * This function to support SAP channel change with CSA IE

+ 46 - 2
core/hdd/src/wlan_hdd_p2p.c

@@ -290,6 +290,10 @@ static int __wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	uint16_t auth_algo;
 	QDF_STATUS qdf_status;
 	int ret;
+	uint32_t ft_info_len = 0;
+	const uint8_t  *assoc_resp;
+	void *ft_info;
+	struct hdd_ap_ctx *hdd_ap_ctx;
 
 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
 		hdd_err("Command not allowed in FTM mode");
@@ -305,6 +309,7 @@ static int __wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 
 	type = WLAN_HDD_GET_TYPE_FRM_FC(buf[0]);
 	sub_type = WLAN_HDD_GET_SUBTYPE_FRM_FC(buf[0]);
+	hdd_debug("type %d, sub_type %d", type, sub_type);
 
 	/* When frame to be transmitted is auth mgmt, then trigger
 	 * sme_send_mgmt_tx to send auth frame without need for policy manager.
@@ -326,6 +331,12 @@ static int __wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 					      sizeof(struct wlan_frame_hdr));
 			if (auth_algo == eSIR_AUTH_TYPE_PASN)
 				goto off_chan_tx;
+			if ((auth_algo == eSIR_FT_AUTH) &&
+			    (adapter->device_mode == QDF_SAP_MODE ||
+			     adapter->device_mode == QDF_P2P_GO_MODE)) {
+				hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter);
+				hdd_ap_ctx->during_auth_offload = false;
+			}
 		}
 
 		qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_SME,
@@ -335,7 +346,32 @@ static int __wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 					      adapter->vdev_id, buf, len);
 
 		if (QDF_IS_STATUS_SUCCESS(qdf_status))
-			return 0;
+			return qdf_status_to_os_return(qdf_status);
+		else
+			return -EINVAL;
+	}
+	/* Only when SAP working on Fast BSS transition mode. Driver offload
+	 * (re)assoc request to hostapd. Here driver receive (re)assoc response
+	 * frame from hostapd.
+	 */
+	if ((adapter->device_mode == QDF_SAP_MODE ||
+	     adapter->device_mode == QDF_P2P_GO_MODE) &&
+	    (type == SIR_MAC_MGMT_FRAME) &&
+	    (sub_type == SIR_MAC_MGMT_ASSOC_RSP ||
+	     sub_type == SIR_MAC_MGMT_REASSOC_RSP)) {
+		assoc_resp = &((struct ieee80211_mgmt *)buf)->u.assoc_resp.variable[0];
+		ft_info = hdd_filter_ft_info(assoc_resp, len, &ft_info_len);
+		if (!ft_info || !ft_info_len)
+			return -EINVAL;
+		hdd_debug("get ft_info_len from Assoc rsp :%d", ft_info_len);
+		hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter);
+		qdf_status = wlansap_update_ft_info(hdd_ap_ctx->sap_context,
+				((struct ieee80211_mgmt *)buf)->da,
+				ft_info, ft_info_len, 0);
+		qdf_mem_free(ft_info);
+
+		if (QDF_IS_STATUS_SUCCESS(qdf_status))
+			return qdf_status_to_os_return(qdf_status);
 		else
 			return -EINVAL;
 	}
@@ -1064,6 +1100,7 @@ __hdd_indicate_mgmt_frame_to_user(struct hdd_adapter *adapter,
 	bool is_pasn_auth_frame = false;
 	struct hdd_adapter *assoc_adapter;
 	bool eht_capab;
+	struct hdd_ap_ctx *hdd_ap_ctx;
 
 	hdd_debug("Frame Type = %d Frame Length = %d freq = %d",
 		  frame_type, frm_len, rx_freq);
@@ -1092,8 +1129,15 @@ __hdd_indicate_mgmt_frame_to_user(struct hdd_adapter *adapter,
 		       WLAN_AUTH_FRAME_MIN_LEN)) {
 		auth_algo = *(uint16_t *)(pb_frames +
 					  sizeof(struct wlan_frame_hdr));
-		if (auth_algo == eSIR_AUTH_TYPE_PASN)
+		if (auth_algo == eSIR_AUTH_TYPE_PASN) {
 			is_pasn_auth_frame = true;
+		} else if (auth_algo == eSIR_FT_AUTH) {
+			if (adapter->device_mode == QDF_SAP_MODE ||
+			    adapter->device_mode == QDF_P2P_GO_MODE) {
+				hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter);
+				hdd_ap_ctx->during_auth_offload = true;
+			}
+		}
 	}
 
 	/* Get adapter from Destination mac address of the frame */

+ 15 - 0
core/mac/inc/sir_api.h

@@ -1085,6 +1085,9 @@ struct assoc_ind {
 	const uint8_t *owe_ie;
 	uint32_t owe_ie_len;
 	uint16_t owe_status;
+	const uint8_t *ft_ie;
+	uint32_t ft_ie_len;
+	uint16_t ft_status;
 	bool need_assoc_rsp_tx_cb;
 	tSirMacAddr peer_mld_addr;
 };
@@ -1099,6 +1102,16 @@ struct owe_assoc_ind {
 	struct assoc_ind *assoc_ind;
 };
 
+/**
+ * struct ft_assoc_ind - ft association indication
+ * @node: List entry element
+ * @assoc_ind: pointer to assoc ind
+ */
+struct ft_assoc_ind {
+	qdf_list_node_t node;
+	struct assoc_ind *assoc_ind;
+};
+
 /* / Definition for Association confirm */
 /* / ---> MAC */
 struct assoc_cnf {
@@ -1111,6 +1124,8 @@ struct assoc_cnf {
 	enum wlan_status_code mac_status_code;
 	uint8_t *owe_ie;
 	uint32_t owe_ie_len;
+	uint8_t *ft_ie;
+	uint32_t ft_ie_len;
 	bool need_assoc_rsp_tx_cb;
 };
 

+ 5 - 0
core/mac/src/pe/include/lim_global.h

@@ -136,6 +136,7 @@ typedef enum eLimMlmStates {
 	eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE,
 	eLIM_MLM_WT_FT_REASSOC_RSP_STATE,
 	eLIM_MLM_WT_SAE_AUTH_STATE,
+	eLIM_MLM_WT_FT_AUTH_STATE,
 } tLimMlmStates;
 
 /* 11h channel switch states */
@@ -246,6 +247,8 @@ typedef struct tLimPreAuthTable {
  * @he_capable:     802.11ax HE capability
  * @owe_ie:         Pointer to OWE IE
  * @owe_ie_len:     Length of OWE IE
+ * @ft_ie:         Pointer to FT related IE
+ * @ft_ie_len:     Length of FT related IE
  * @eht_capable:     802.11be EHT capability
  */
 struct lim_sta_context {
@@ -272,6 +275,8 @@ struct lim_sta_context {
 	bool force_1x1;
 	uint8_t *owe_ie;
 	uint32_t owe_ie_len;
+	uint8_t *ft_ie;
+	uint32_t ft_ie_len;
 #ifdef WLAN_FEATURE_11BE
 	bool eht_capable;
 #endif

+ 17 - 0
core/mac/src/pe/lim/lim_process_assoc_req_frame.c

@@ -2668,6 +2668,14 @@ void lim_process_assoc_req_frame(struct mac_context *mac_ctx,
 			sub_type, GET_LIM_SYSTEM_ROLE(session),
 			QDF_MAC_ADDR_REF(hdr->sa));
 			return;
+		} else if (sta_ds->mlmStaContext.akm_type == ANI_AKM_TYPE_FT_RSN_PSK) {
+			pe_debug("FT Assoc Req, delete STA hash entry");
+			lim_release_peer_idx(mac_ctx, sta_ds->assocId, session);
+			if (dph_delete_hash_entry(mac_ctx, hdr->sa,
+						  sta_ds->assocId,
+						  &session->dph.dphHashTable)
+			    != QDF_STATUS_SUCCESS)
+				pe_err("error deleting hash entry");
 		} else if (!sta_ds->rmfEnabled && (sub_type == LIM_REASSOC)) {
 			/*
 			 * SAP should send reassoc response with reject code
@@ -3442,6 +3450,15 @@ QDF_STATUS lim_send_mlm_assoc_ind(struct mac_context *mac_ctx,
 			qdf_mem_free(assoc_ind);
 			return QDF_STATUS_E_INVAL;
 		}
+
+		pe_debug("assoc_ind->akm_type:%d ", assoc_ind->akm_type);
+		if (assoc_ind->akm_type == ANI_AKM_TYPE_FT_RSN_PSK) {
+			lim_send_sme_mgmt_frame_ind(mac_ctx, sub_type,
+			   qdf_nbuf_data(assoc_req->assoc_req_buf),
+			   qdf_nbuf_len(assoc_req->assoc_req_buf),
+			   session_entry->smeSessionId,
+			   0, 0, RXMGMT_FLAG_NONE);
+		}
 		lim_post_sme_message(mac_ctx, LIM_MLM_ASSOC_IND,
 				     (uint32_t *)assoc_ind);
 		qdf_mem_free(assoc_ind);

+ 72 - 0
core/mac/src/pe/lim/lim_process_auth_frame.c

@@ -508,6 +508,73 @@ static inline void  lim_process_sae_auth_frame(struct mac_context *mac_ctx,
 {}
 #endif
 
+static void lim_process_ft_auth_frame(struct mac_context *mac_ctx,
+				      uint8_t *rx_pkt_info,
+				      struct pe_session *pe_session)
+{
+	tpSirMacMgmtHdr mac_hdr;
+	uint32_t frame_len;
+	uint8_t *body_ptr;
+	enum rxmgmt_flags rx_flags = RXMGMT_FLAG_NONE;
+	uint16_t auth_algo;
+
+	mac_hdr = WMA_GET_RX_MAC_HEADER(rx_pkt_info);
+	body_ptr = WMA_GET_RX_MPDU_DATA(rx_pkt_info);
+	frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_pkt_info);
+
+	pe_debug("FT Auth RX type %d subtype %d from " QDF_MAC_ADDR_FMT,
+		 mac_hdr->fc.type, mac_hdr->fc.subType,
+		 QDF_MAC_ADDR_REF(mac_hdr->sa));
+
+	if (LIM_IS_AP_ROLE(pe_session)) {
+		struct tLimPreAuthNode *sta_pre_auth_ctx;
+
+		rx_flags = RXMGMT_FLAG_EXTERNAL_AUTH;
+		/* Extract pre-auth context for the STA, if any. */
+		sta_pre_auth_ctx = lim_search_pre_auth_list(mac_ctx,
+							    mac_hdr->sa);
+		if (sta_pre_auth_ctx) {
+			pe_debug("STA Auth ctx have ininted");
+			/* Pre-auth context exists for the STA */
+			if (sta_pre_auth_ctx->mlmState == eLIM_MLM_WT_FT_AUTH_STATE) {
+				pe_warn("previous Auth not completed, don't process this auth frame");
+				return;
+			}
+			lim_delete_pre_auth_node(mac_ctx, mac_hdr->sa);
+		}
+
+		/* Create entry for this STA in pre-auth list */
+		sta_pre_auth_ctx = lim_acquire_free_pre_auth_node(mac_ctx,
+			&mac_ctx->lim.gLimPreAuthTimerTable);
+		if (!sta_pre_auth_ctx) {
+			pe_warn("Max pre-auth nodes reached ");
+			lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGW);
+			return;
+		}
+		pe_debug("Alloc new data: %pK peer", sta_pre_auth_ctx);
+		auth_algo = *(uint16_t *)body_ptr;
+		lim_print_mac_addr(mac_ctx, mac_hdr->sa, LOGD);
+		qdf_mem_copy((uint8_t *)sta_pre_auth_ctx->peerMacAddr,
+			     mac_hdr->sa, sizeof(tSirMacAddr));
+		sta_pre_auth_ctx->mlmState = eLIM_MLM_WT_FT_AUTH_STATE;
+		sta_pre_auth_ctx->authType = (tAniAuthType) auth_algo;
+		sta_pre_auth_ctx->fSeen = 0;
+		sta_pre_auth_ctx->fTimerStarted = 0;
+		sta_pre_auth_ctx->seq_num =
+				((mac_hdr->seqControl.seqNumHi << 4) |
+				(mac_hdr->seqControl.seqNumLo));
+		sta_pre_auth_ctx->timestamp = qdf_mc_timer_get_system_ticks();
+		lim_add_pre_auth_node(mac_ctx, sta_pre_auth_ctx);
+		lim_send_sme_mgmt_frame_ind(mac_ctx, mac_hdr->fc.subType,
+			(uint8_t *)mac_hdr,
+			frame_len + sizeof(tSirMacMgmtHdr),
+			pe_session->smeSessionId,
+			WMA_GET_RX_FREQ(rx_pkt_info),
+			WMA_GET_RX_RSSI_NORMALIZED(rx_pkt_info),
+			rx_flags);
+	}
+}
+
 static uint8_t
 lim_get_pasn_peer_vdev_id(struct mac_context *mac, uint8_t *bssid)
 {
@@ -1669,6 +1736,11 @@ lim_process_auth_frame(struct mac_context *mac_ctx, uint8_t *rx_pkt_info,
 		lim_process_pasn_auth_frame(mac_ctx, pe_session->vdev_id,
 					    rx_pkt_info);
 		goto free;
+	} else if (auth_alg == eSIR_FT_AUTH && LIM_IS_AP_ROLE(pe_session)) {
+		pe_debug("Auth Frame auth_alg  eSIR_FT_AUTH");
+			lim_process_ft_auth_frame(mac_ctx,
+						  rx_pkt_info, pe_session);
+		goto free;
 	} else if ((sir_convert_auth_frame2_struct(mac_ctx, body_ptr,
 				frame_len, rx_auth_frame) != QDF_STATUS_SUCCESS)
 				|| (!is_auth_valid(mac_ctx, rx_auth_frame,

+ 5 - 0
core/mac/src/pe/lim/lim_process_sme_req_messages.c

@@ -6638,6 +6638,8 @@ void __lim_process_sme_assoc_cnf_new(struct mac_context *mac_ctx, uint32_t msg_t
 			eLIM_MLM_LINK_ESTABLISHED_STATE;
 		sta_ds->mlmStaContext.owe_ie = assoc_cnf.owe_ie;
 		sta_ds->mlmStaContext.owe_ie_len = assoc_cnf.owe_ie_len;
+		sta_ds->mlmStaContext.ft_ie = assoc_cnf.ft_ie;
+		sta_ds->mlmStaContext.ft_ie_len = assoc_cnf.ft_ie_len;
 		pe_debug("sending Assoc Rsp frame to STA assoc id=%d, tx cb %d",
 			 sta_ds->assocId, assoc_cnf.need_assoc_rsp_tx_cb);
 		lim_send_assoc_rsp_mgmt_frame(
@@ -6648,6 +6650,8 @@ void __lim_process_sme_assoc_cnf_new(struct mac_context *mac_ctx, uint32_t msg_t
 					assoc_cnf.need_assoc_rsp_tx_cb);
 		sta_ds->mlmStaContext.owe_ie = NULL;
 		sta_ds->mlmStaContext.owe_ie_len = 0;
+		sta_ds->mlmStaContext.ft_ie = NULL;
+		sta_ds->mlmStaContext.ft_ie_len = 0;
 		goto end;
 	} else {
 		uint8_t add_pre_auth_context = true;
@@ -6691,6 +6695,7 @@ end:
 		session_entry->parsedAssocReq[sta_ds->assocId] = NULL;
 	}
 	qdf_mem_free(assoc_cnf.owe_ie);
+	qdf_mem_free(assoc_cnf.ft_ie);
 }
 
 static void

+ 23 - 2
core/mac/src/pe/lim/lim_send_management_frames.c

@@ -1906,9 +1906,10 @@ lim_send_assoc_rsp_mgmt_frame(struct mac_context *mac_ctx,
 
 	bytes += sizeof(tSirMacMgmtHdr) + payload + mlo_ie_len;
 
-	if (sta)
+	if (sta) {
 		bytes += sta->mlmStaContext.owe_ie_len;
-
+		bytes += sta->mlmStaContext.ft_ie_len;
+	}
 	qdf_status = cds_packet_alloc((uint16_t) bytes, (void **)&frame,
 				      (void **)&packet);
 	if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
@@ -2028,6 +2029,13 @@ lim_send_assoc_rsp_mgmt_frame(struct mac_context *mac_ctx,
 		payload += sta->mlmStaContext.owe_ie_len;
 	}
 
+	if (sta && sta->mlmStaContext.ft_ie_len) {
+		qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload,
+			     sta->mlmStaContext.ft_ie,
+			     sta->mlmStaContext.ft_ie_len);
+		payload += sta->mlmStaContext.ft_ie_len;
+	}
+
 	if (sta && mlo_ie_len) {
 		qdf_status = lim_fill_complete_mlo_ie(pe_session, mlo_ie_len,
 				      frame + sizeof(tSirMacMgmtHdr) + payload);
@@ -6610,6 +6618,19 @@ void lim_send_mgmt_frame_tx(struct mac_context *mac_ctx,
 		if (auth_algo == eSIR_AUTH_TYPE_SAE)
 			lim_handle_sae_auth_retry(mac_ctx, vdev_id,
 						  mb_msg->data, msg_len);
+		if (auth_algo == eSIR_FT_AUTH) {
+			struct tLimPreAuthNode *sta_pre_auth_ctx;
+
+			sta_pre_auth_ctx = lim_search_pre_auth_list(mac_ctx,
+				((tpSirMacMgmtHdr)(mb_msg->data))->da);
+			pe_debug("FT Auth TX to " QDF_MAC_ADDR_FMT,
+				 QDF_MAC_ADDR_REF(((tpSirMacMgmtHdr)(mb_msg->data))->da));
+			if (sta_pre_auth_ctx) {
+				pe_debug("STA is AUTHENTICATED_STATE");
+				sta_pre_auth_ctx->mlmState =
+					eLIM_MLM_AUTHENTICATED_STATE;
+			}
+		}
 	}
 	mac_ctx->auth_ack_status = LIM_ACK_NOT_RCD;
 	lim_send_frame(mac_ctx, vdev_id, mb_msg->data, msg_len);

+ 14 - 0
core/sap/inc/sap_api.h

@@ -1501,6 +1501,20 @@ QDF_STATUS wlansap_update_owe_info(struct sap_context *sap_ctx,
 				   uint8_t *peer, const uint8_t *ie,
 				   uint32_t ie_len, uint16_t owe_status);
 
+/**
+ * wlansap_update_ft_info() - Update FT info
+ * @sap_ctx: sap context
+ * @peer: peer mac
+ * @ie: IE from hostapd
+ * @ie_len: IE length
+ * @ft_status: wlan status codes
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlansap_update_ft_info(struct sap_context *sap_ctx,
+				  uint8_t *peer, const uint8_t *ie,
+				  uint32_t ie_len, uint16_t ft_status);
+
 /**
  * wlansap_filter_ch_based_acs() -filter out channel based on acs
  * @sap_ctx: sap context

+ 33 - 0
core/sap/src/sap_fsm.c

@@ -1898,6 +1898,27 @@ static bool sap_save_owe_pending_assoc_ind(struct sap_context *sap_ctx,
 	return true;
 }
 
+static bool sap_save_ft_pending_assoc_ind(struct sap_context *sap_ctx,
+					  struct assoc_ind *sme_assoc_ind)
+{
+	struct ft_assoc_ind *assoc_ind;
+	QDF_STATUS status;
+
+	assoc_ind = qdf_mem_malloc(sizeof(*assoc_ind));
+	if (!assoc_ind)
+		return false;
+	assoc_ind->assoc_ind = sme_assoc_ind;
+	status = qdf_list_insert_back(&sap_ctx->ft_pending_assoc_ind_list,
+				      &assoc_ind->node);
+	if (QDF_STATUS_SUCCESS != status) {
+		qdf_mem_free(assoc_ind);
+		return false;
+	}
+	qdf_event_set(&sap_ctx->ft_pending_event);
+
+	return true;
+}
+
 #ifdef FEATURE_RADAR_HISTORY
 /* Last cac result */
 static struct prev_cac_result prev_cac_history;
@@ -2313,6 +2334,18 @@ QDF_STATUS sap_signal_hdd_event(struct sap_context *sap_ctx,
 			}
 			csr_roaminfo->owe_pending_assoc_ind = NULL;
 		}
+
+		if (csr_roaminfo->ft_pending_assoc_ind) {
+			if (!sap_save_ft_pending_assoc_ind(sap_ctx,
+			    csr_roaminfo->ft_pending_assoc_ind)) {
+				sap_err("Failed to save ft assoc ind");
+				qdf_mem_free(csr_roaminfo->ft_pending_assoc_ind);
+				csr_roaminfo->ft_pending_assoc_ind = NULL;
+				qdf_mem_free(sap_ap_event);
+				return QDF_STATUS_E_INVAL;
+			}
+			csr_roaminfo->ft_pending_assoc_ind = NULL;
+		}
 		break;
 	case eSAP_START_BSS_EVENT:
 		sap_ap_event->sapHddEventCode = eSAP_START_BSS_EVENT;

+ 2 - 0
core/sap/src/sap_internal.h

@@ -229,6 +229,8 @@ struct sap_context {
 	/* Disabled mcs13 by sap or not */
 	bool disabled_mcs13;
 	qdf_list_t owe_pending_assoc_ind_list;
+	qdf_list_t ft_pending_assoc_ind_list;
+	qdf_event_t ft_pending_event;
 	uint32_t freq_before_ch_switch;
 #ifdef WLAN_FEATURE_P2P_P2P_STA
 /*

+ 134 - 1
core/sap/src/sap_module.c

@@ -239,6 +239,14 @@ static QDF_STATUS wlansap_owe_init(struct sap_context *sap_ctx)
 	return QDF_STATUS_SUCCESS;
 }
 
+static QDF_STATUS wlansap_ft_init(struct sap_context *sap_ctx)
+{
+	qdf_list_create(&sap_ctx->ft_pending_assoc_ind_list, 0);
+	qdf_event_create(&sap_ctx->ft_pending_event);
+
+	return QDF_STATUS_SUCCESS;
+}
+
 static void wlansap_owe_cleanup(struct sap_context *sap_ctx)
 {
 	struct mac_context *mac;
@@ -289,11 +297,65 @@ static void wlansap_owe_cleanup(struct sap_context *sap_ctx)
 	}
 }
 
+static void wlansap_ft_cleanup(struct sap_context *sap_ctx)
+{
+	struct mac_context *mac;
+	struct ft_assoc_ind *ft_assoc_ind;
+	struct assoc_ind *assoc_ind = NULL;
+	qdf_list_node_t *node = NULL, *next_node = NULL;
+	QDF_STATUS status;
+
+	if (!sap_ctx) {
+		sap_err("Invalid SAP context");
+		return;
+	}
+
+	mac = sap_get_mac_context();
+	if (!mac) {
+		sap_err("Invalid MAC context");
+		return;
+	}
+
+	if (QDF_STATUS_SUCCESS !=
+	    qdf_list_peek_front(&sap_ctx->ft_pending_assoc_ind_list,
+				&node)) {
+		sap_debug("Failed to find assoc ind list");
+		return;
+	}
+
+	while (node) {
+		qdf_list_peek_next(&sap_ctx->ft_pending_assoc_ind_list,
+				   node, &next_node);
+		ft_assoc_ind = qdf_container_of(node, struct ft_assoc_ind,
+						node);
+		status = qdf_list_remove_node(
+				    &sap_ctx->ft_pending_assoc_ind_list, node);
+		if (status == QDF_STATUS_SUCCESS) {
+			assoc_ind = ft_assoc_ind->assoc_ind;
+			qdf_mem_free(ft_assoc_ind);
+			assoc_ind->ft_ie = NULL;
+			assoc_ind->ft_ie_len = 0;
+			assoc_ind->ft_status = STATUS_UNSPECIFIED_FAILURE;
+			qdf_mem_free(assoc_ind);
+		} else {
+			sap_err("Failed to remove assoc ind");
+		}
+		node = next_node;
+		next_node = NULL;
+	}
+}
+
 static void wlansap_owe_deinit(struct sap_context *sap_ctx)
 {
 	qdf_list_destroy(&sap_ctx->owe_pending_assoc_ind_list);
 }
 
+static void wlansap_ft_deinit(struct sap_context *sap_ctx)
+{
+	qdf_list_destroy(&sap_ctx->ft_pending_assoc_ind_list);
+	qdf_event_destroy(&sap_ctx->ft_pending_event);
+}
+
 QDF_STATUS sap_init_ctx(struct sap_context *sap_ctx,
 			 enum QDF_OPMODE mode,
 			 uint8_t *addr, uint32_t session_id, bool reinit)
@@ -334,6 +396,11 @@ QDF_STATUS sap_init_ctx(struct sap_context *sap_ctx,
 			sap_err("OWE init failed");
 			return QDF_STATUS_E_FAILURE;
 		}
+		status = wlansap_ft_init(sap_ctx);
+		if (QDF_STATUS_SUCCESS != status) {
+			sap_err("FT init failed");
+			return QDF_STATUS_E_FAILURE;
+		}
 	}
 
 	return QDF_STATUS_SUCCESS;
@@ -351,9 +418,10 @@ QDF_STATUS sap_deinit_ctx(struct sap_context *sap_ctx)
 		return QDF_STATUS_E_FAULT;
 	}
 
+	wlansap_ft_cleanup(sap_ctx);
+	wlansap_ft_deinit(sap_ctx);
 	wlansap_owe_cleanup(sap_ctx);
 	wlansap_owe_deinit(sap_ctx);
-
 	mac = sap_get_mac_context();
 	if (!mac) {
 		sap_err("Invalid MAC context");
@@ -3000,6 +3068,71 @@ QDF_STATUS wlansap_update_owe_info(struct sap_context *sap_ctx,
 	return status;
 }
 
+QDF_STATUS wlansap_update_ft_info(struct sap_context *sap_ctx,
+				  uint8_t *peer, const uint8_t *ie,
+				  uint32_t ie_len, uint16_t ft_status)
+{
+	struct mac_context *mac;
+	struct ft_assoc_ind *ft_assoc_ind;
+	struct assoc_ind *assoc_ind = NULL;
+	qdf_list_node_t *node = NULL, *next_node = NULL;
+	QDF_STATUS status;
+
+	if (!sap_ctx) {
+		sap_err("Invalid SAP context");
+		return QDF_STATUS_E_FAULT;
+	}
+
+	mac = sap_get_mac_context();
+	if (!mac) {
+		sap_err("Invalid MAC context");
+		return QDF_STATUS_E_FAULT;
+	}
+	status = qdf_wait_single_event(&sap_ctx->ft_pending_event,
+				       500);
+	if (!QDF_IS_STATUS_SUCCESS(status)) {
+		sap_err("wait for ft pending event timeout");
+		wlansap_ft_cleanup(sap_ctx);
+		return QDF_STATUS_E_FAULT;
+	}
+
+	if (QDF_STATUS_SUCCESS !=
+		qdf_list_peek_front(&sap_ctx->ft_pending_assoc_ind_list,
+				    &next_node)) {
+		sap_err("Failed to find ft assoc ind list");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	do {
+		node = next_node;
+		ft_assoc_ind = qdf_container_of(node, struct ft_assoc_ind, node);
+		if (qdf_mem_cmp(peer,
+				ft_assoc_ind->assoc_ind->peerMacAddr,
+				QDF_MAC_ADDR_SIZE) == 0) {
+			status = qdf_list_remove_node(&sap_ctx->ft_pending_assoc_ind_list,
+						      node);
+			if (status != QDF_STATUS_SUCCESS) {
+				sap_err("Failed to remove ft assoc ind");
+				return status;
+			}
+			assoc_ind = ft_assoc_ind->assoc_ind;
+			qdf_mem_free(ft_assoc_ind);
+			break;
+		}
+	} while (QDF_STATUS_SUCCESS ==
+		 qdf_list_peek_next(&sap_ctx->ft_pending_assoc_ind_list,
+				    node, &next_node));
+
+	if (assoc_ind) {
+		assoc_ind->ft_ie = ie;
+		assoc_ind->ft_ie_len = ie_len;
+		assoc_ind->ft_status = ft_status;
+		status = sme_update_ft_info(mac, assoc_ind);
+		qdf_mem_free(assoc_ind);
+	}
+	return status;
+}
+
 bool wlansap_is_channel_present_in_acs_list(uint32_t freq,
 					    uint32_t *ch_freq_list,
 					    uint8_t ch_count)

+ 13 - 0
core/sme/inc/csr_api.h

@@ -593,6 +593,8 @@ struct csr_roam_info {
 	struct sir_sae_info *sae_info;
 #endif
 	struct assoc_ind *owe_pending_assoc_ind;
+	struct assoc_ind *ft_pending_assoc_ind;
+
 	struct qdf_mac_addr peer_mld;
 #ifdef WLAN_FEATURE_SAP_ACS_OPTIMIZE
 	uint32_t chan_info_freq;
@@ -859,6 +861,17 @@ static inline void csr_packetdump_timer_start(void) {}
 QDF_STATUS csr_update_owe_info(struct mac_context *mac,
 			       struct assoc_ind *assoc_ind);
 
+/**
+ * csr_update_ft_info() - Update FT info
+ * @mac: mac context
+ * @assoc_ind: assoc ind
+ *
+ * Return: QDF_STATUS
+ */
+
+QDF_STATUS csr_update_ft_info(struct mac_context *mac,
+			      struct assoc_ind *assoc_ind);
+
 typedef void (*csr_ani_callback)(int8_t *ani, void *context);
 
 /*

+ 11 - 0
core/sme/inc/sme_api.h

@@ -4236,6 +4236,17 @@ QDF_STATUS sme_update_hidden_ssid_status_cb(mac_handle_t mac_handle,
 QDF_STATUS sme_update_owe_info(struct mac_context *mac,
 			       struct assoc_ind *assoc_ind);
 
+/**
+ * sme_update_ft_info() - Update FT info
+ * @mac: mac context
+ * @assoc_ind: assoc ind
+ *
+ * Return: QDF_STATUS
+ */
+
+QDF_STATUS sme_update_ft_info(struct mac_context *mac,
+			      struct assoc_ind *assoc_ind);
+
 #ifdef WLAN_MWS_INFO_DEBUGFS
 /**
  * sme_get_mws_coex_info() - SME API to get the coex information

+ 14 - 0
core/sme/src/common/sme_api.c

@@ -15765,6 +15765,20 @@ QDF_STATUS sme_update_owe_info(struct mac_context *mac,
 	return status;
 }
 
+QDF_STATUS sme_update_ft_info(struct mac_context *mac,
+			      struct assoc_ind *assoc_ind)
+{
+	QDF_STATUS status;
+
+	status = sme_acquire_global_lock(&mac->sme);
+	if (QDF_IS_STATUS_SUCCESS(status)) {
+		status = csr_update_ft_info(mac, assoc_ind);
+		sme_release_global_lock(&mac->sme);
+	}
+
+	return status;
+}
+
 #ifdef WLAN_MWS_INFO_DEBUGFS
 QDF_STATUS
 sme_get_mws_coex_info(mac_handle_t mac_handle, uint32_t vdev_id,

+ 32 - 1
core/sme/src/csr/csr_api_roam.c

@@ -4006,12 +4006,19 @@ csr_roam_chk_lnk_assoc_ind(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr)
 		} else {
 			roam_info->fAuthRequired = true;
 		}
+		sme_debug("Receive AUTH_TYPE of %d", csr_akm_type);
 		if (csr_akm_type == eCSR_AUTH_TYPE_OWE) {
 			roam_info->owe_pending_assoc_ind = qdf_mem_malloc(
 							    sizeof(*pAssocInd));
 			if (roam_info->owe_pending_assoc_ind)
 				qdf_mem_copy(roam_info->owe_pending_assoc_ind,
 					     pAssocInd, sizeof(*pAssocInd));
+		} else if (csr_akm_type == eCSR_AUTH_TYPE_FT_RSN_PSK) {
+			roam_info->ft_pending_assoc_ind = qdf_mem_malloc(
+			    sizeof(*pAssocInd));
+			if (roam_info->ft_pending_assoc_ind)
+				qdf_mem_copy(roam_info->ft_pending_assoc_ind,
+					     pAssocInd, sizeof(*pAssocInd));
 		}
 		status = csr_roam_call_callback(mac_ctx, sessionId,
 					roam_info, eCSR_ROAM_INFRA_IND,
@@ -4042,8 +4049,10 @@ csr_roam_chk_lnk_assoc_ind(struct mac_context *mac_ctx, tSirSmeRsp *msg_ptr)
 			}
 		}
 	}
+	sme_debug("csr_akm_type: %d", csr_akm_type);
 
-	if (csr_akm_type != eCSR_AUTH_TYPE_OWE) {
+	if (csr_akm_type != eCSR_AUTH_TYPE_OWE &&
+	    csr_akm_type != eCSR_AUTH_TYPE_FT_RSN_PSK) {
 		if ((opmode == QDF_SAP_MODE || opmode == QDF_P2P_GO_MODE) &&
 		    roam_info->status_code != eSIR_SME_ASSOC_REFUSED)
 			pAssocInd->need_assoc_rsp_tx_cb = true;
@@ -6140,6 +6149,15 @@ QDF_STATUS csr_send_assoc_cnf_msg(struct mac_context *mac,
 				     pAssocInd->owe_ie_len);
 			pMsg->owe_ie_len = pAssocInd->owe_ie_len;
 		}
+
+		if (pAssocInd->ft_ie_len) {
+			pMsg->ft_ie = qdf_mem_malloc(pAssocInd->ft_ie_len);
+			if (!pMsg->ft_ie)
+				return QDF_STATUS_E_NOMEM;
+			qdf_mem_copy(pMsg->ft_ie, pAssocInd->ft_ie,
+				     pAssocInd->ft_ie_len);
+			pMsg->ft_ie_len = pAssocInd->ft_ie_len;
+		}
 		pMsg->need_assoc_rsp_tx_cb = pAssocInd->need_assoc_rsp_tx_cb;
 
 		msg.type = pMsg->messageType;
@@ -7713,6 +7731,19 @@ QDF_STATUS csr_update_owe_info(struct mac_context *mac,
 	return status;
 }
 
+QDF_STATUS csr_update_ft_info(struct mac_context *mac,
+			      struct assoc_ind *assoc_ind)
+{
+	QDF_STATUS status;
+
+	/* Send Association completion message to PE */
+	status = assoc_ind->ft_status ? QDF_STATUS_E_INVAL : QDF_STATUS_SUCCESS;
+	assoc_ind->need_assoc_rsp_tx_cb = true;
+	status = csr_send_assoc_cnf_msg(mac, assoc_ind, status,
+					assoc_ind->ft_status);
+	return status;
+}
+
 /**
  * csr_set_sap_ser_params() - API to fill serialization parameters for
  * SAP requests