/*
 * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/**
 * DOC: lim_process_mlm_host_roam.c
 *
 * Host based roaming MLM implementation
 */
#include "cds_api.h"
#include "wni_cfg.h"
#include "ani_global.h"
#include "sir_api.h"
#include "sir_params.h"
#include "cfg_api.h"

#include "sch_api.h"
#include "utils_api.h"
#include "lim_utils.h"
#include "lim_assoc_utils.h"
#include "lim_prop_exts_utils.h"
#include "lim_security_utils.h"
#include "lim_send_messages.h"
#include "lim_send_messages.h"
#include "lim_session_utils.h"
#include <lim_ft.h>
#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM
#include "host_diag_core_log.h"
#endif
#include "wma_if.h"
#include "rrm_api.h"
static void lim_handle_sme_reaasoc_result(tpAniSirGlobal, tSirResultCodes,
		uint16_t, tpPESession);
/**
 * lim_process_mlm_reassoc_req() - process mlm reassoc request.
 *
 * @mac_ctx:     pointer to Global MAC structure
 * @msg:  pointer to the MLM message buffer
 *
 * This function is called to process MLM_REASSOC_REQ message
 * from SME
 *
 * Return: None
 */
void lim_process_mlm_reassoc_req(tpAniSirGlobal mac_ctx, uint32_t *msg)
{
	uint8_t channel, sec_ch_offset;
	struct tLimPreAuthNode *auth_node;
	tLimMlmReassocReq *reassoc_req;
	tLimMlmReassocCnf reassoc_cnf;
	tpPESession session;

	if (msg == NULL) {
		pe_err("Buffer is Pointing to NULL");
		return;
	}

	reassoc_req = (tLimMlmReassocReq *) msg;
	session = pe_find_session_by_session_id(mac_ctx,
			reassoc_req->sessionId);
	if (NULL == session) {
		pe_err("Session Does not exist for given sessionId: %d",
			reassoc_req->sessionId);
		qdf_mem_free(reassoc_req);
		return;
	}

	pe_debug("ReAssoc Req on session: %d role: %d mlm: %d " MAC_ADDRESS_STR,
		reassoc_req->sessionId, GET_LIM_SYSTEM_ROLE(session),
		session->limMlmState, MAC_ADDR_ARRAY(reassoc_req->peerMacAddr));

	if (LIM_IS_AP_ROLE(session) ||
		(session->limMlmState !=
		eLIM_MLM_LINK_ESTABLISHED_STATE)) {
		/*
		 * Received Reassoc request in invalid state or
		 * in AP role.Return Reassoc confirm with Invalid
		 * parameters code.
		 */

		pe_warn("unexpect msg state: %X role: %d MAC" MAC_ADDRESS_STR,
			session->limMlmState, GET_LIM_SYSTEM_ROLE(session),
			MAC_ADDR_ARRAY(reassoc_req->peerMacAddr));
		lim_print_mlm_state(mac_ctx, LOGW, session->limMlmState);
		reassoc_cnf.resultCode = eSIR_SME_INVALID_PARAMETERS;
		reassoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
		goto end;
	}

	if (session->pLimMlmReassocReq)
		qdf_mem_free(session->pLimMlmReassocReq);

	/*
	 * Hold Re-Assoc request as part of Session, knock-out mac_ctx
	 * Hold onto Reassoc request parameters
	 */
	session->pLimMlmReassocReq = reassoc_req;

	/* See if we have pre-auth context with new AP */
	auth_node = lim_search_pre_auth_list(mac_ctx, session->limReAssocbssId);

	if (!auth_node && (qdf_mem_cmp(reassoc_req->peerMacAddr,
					    session->bssId,
					    sizeof(tSirMacAddr)))) {
		/*
		 * Either pre-auth context does not exist AND
		 * we are not reassociating with currently
		 * associated AP.
		 * Return Reassoc confirm with not authenticated
		 */
		reassoc_cnf.resultCode = eSIR_SME_STA_NOT_AUTHENTICATED;
		reassoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;

		goto end;
	}
	/* assign the sessionId to the timer object */
	mac_ctx->lim.limTimers.gLimReassocFailureTimer.sessionId =
		reassoc_req->sessionId;
	session->limPrevMlmState = session->limMlmState;
	session->limMlmState = eLIM_MLM_WT_REASSOC_RSP_STATE;
	MTRACE(mac_trace(mac_ctx, TRACE_CODE_MLM_STATE, session->peSessionId,
			 session->limMlmState));

	/* Derive channel from BSS description and store it at CFG. */
	channel = session->limReassocChannelId;
	sec_ch_offset = session->reAssocHtSecondaryChannelOffset;

	/* Apply previously set configuration at HW */
	lim_apply_configuration(mac_ctx, session);

	/* store the channel switch sessionEntry in the lim global var */
	session->channelChangeReasonCode =
		LIM_SWITCH_CHANNEL_REASSOC;

	/* Switch channel to the new Operating channel for Reassoc */
	lim_set_channel(mac_ctx, channel,
			session->ch_center_freq_seg0,
			session->ch_center_freq_seg1,
			session->ch_width,
			session->maxTxPower,
			session->peSessionId, 0, 0);

	return;
end:
	reassoc_cnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
	/* Update PE sessio Id */
	reassoc_cnf.sessionId = reassoc_req->sessionId;
	/* Free up buffer allocated for reassocReq */
	qdf_mem_free(reassoc_req);
	session->pLimReAssocReq = NULL;
	lim_post_sme_message(mac_ctx, LIM_MLM_REASSOC_CNF,
			     (uint32_t *) &reassoc_cnf);
}

/**
 * lim_handle_sme_reaasoc_result() - Handle the reassoc result
 * @pMac: Global MAC Context
 * @resultCode: Result code
 * @protStatusCode: Protocol Status Code
 * @psessionEntry: PE Session
 *
 * This function is called to process reassoc failures
 * upon receiving REASSOC_CNF with a failure code or
 * MLM_REASSOC_CNF with a success code in case of STA role
 *
 * Return: None
 */
static void lim_handle_sme_reaasoc_result(tpAniSirGlobal pMac,
		tSirResultCodes resultCode, uint16_t protStatusCode,
		tpPESession psessionEntry)
{
	tpDphHashNode pStaDs = NULL;
	uint8_t smesessionId;
	uint16_t smetransactionId;

	if (psessionEntry == NULL) {
		pe_err("psessionEntry is NULL");
		return;
	}
	smesessionId = psessionEntry->smeSessionId;
	smetransactionId = psessionEntry->transactionId;
	if (resultCode != eSIR_SME_SUCCESS) {
		pStaDs =
			dph_get_hash_entry(pMac, DPH_STA_HASH_INDEX_PEER,
					   &psessionEntry->dph.dphHashTable);
		if (pStaDs != NULL) {
			pStaDs->mlmStaContext.disassocReason =
				eSIR_MAC_UNSPEC_FAILURE_REASON;
			pStaDs->mlmStaContext.cleanupTrigger =
				eLIM_JOIN_FAILURE;
			pStaDs->mlmStaContext.resultCode = resultCode;
			pStaDs->mlmStaContext.protStatusCode = protStatusCode;
			lim_cleanup_rx_path(pMac, pStaDs, psessionEntry);
			/* Cleanup if add bss failed */
			if (psessionEntry->add_bss_failed) {
				dph_delete_hash_entry(pMac,
					 pStaDs->staAddr, pStaDs->assocId,
					 &psessionEntry->dph.dphHashTable);
				goto error;
			}
			return;
		}
	}
error:
	/* Delete teh session if REASSOC failure occurred. */
	if (resultCode != eSIR_SME_SUCCESS) {
		if (NULL != psessionEntry) {
			pe_delete_session(pMac, psessionEntry);
			psessionEntry = NULL;
		}
	}
	lim_send_sme_join_reassoc_rsp(pMac, eWNI_SME_REASSOC_RSP, resultCode,
		protStatusCode, psessionEntry, smesessionId, smetransactionId);
}

/**
 * lim_process_mlm_reassoc_cnf() - process mlm reassoc cnf msg
 *
 * @mac_ctx:       Pointer to Global MAC structure
 * @msg_buf:       A pointer to the MLM message buffer
 *
 * This function is called to process MLM_REASSOC_CNF message from MLM State
 * machine.
 *
 * @Return: void
 */
void lim_process_mlm_reassoc_cnf(tpAniSirGlobal mac_ctx, uint32_t *msg_buf)
{
	tpPESession session;
	tLimMlmReassocCnf *lim_mlm_reassoc_cnf;

	if (msg_buf == NULL) {
		pe_err("Buffer is Pointing to NULL");
		return;
	}
	lim_mlm_reassoc_cnf = (tLimMlmReassocCnf *) msg_buf;
	session = pe_find_session_by_session_id(mac_ctx,
				lim_mlm_reassoc_cnf->sessionId);
	if (session == NULL) {
		pe_err("session Does not exist for given session Id");
		return;
	}
	if ((session->limSmeState != eLIM_SME_WT_REASSOC_STATE) ||
	    LIM_IS_AP_ROLE(session)) {
		/*
		 * Should not have received Reassocication confirm
		 * from MLM in other states OR on AP.
		 */
		pe_err("Rcv unexpected MLM_REASSOC_CNF role: %d sme 0x%X",
			GET_LIM_SYSTEM_ROLE(session), session->limSmeState);
		return;
	}

	/*
	 * Upon Reassoc success or failure, freeup the cached preauth request,
	 * to ensure that channel switch is now allowed following any change in
	 * HT params.
	 */
	if (session->ftPEContext.pFTPreAuthReq) {
		pe_debug("Freeing pFTPreAuthReq: %p",
			session->ftPEContext.pFTPreAuthReq);
		if (session->ftPEContext.pFTPreAuthReq->pbssDescription) {
			qdf_mem_free(
			  session->ftPEContext.pFTPreAuthReq->pbssDescription);
			session->ftPEContext.pFTPreAuthReq->pbssDescription =
									NULL;
		}
		qdf_mem_free(session->ftPEContext.pFTPreAuthReq);
		session->ftPEContext.pFTPreAuthReq = NULL;
		session->ftPEContext.ftPreAuthSession = false;
	}

#ifdef WLAN_FEATURE_ROAM_OFFLOAD
	if (session->bRoamSynchInProgress) {
		pe_debug("LFR3:Re-set the LIM Ctxt Roam Synch In Progress");
		session->bRoamSynchInProgress = false;
	}
#endif

	pe_debug("Rcv MLM_REASSOC_CNF with result code: %d",
		lim_mlm_reassoc_cnf->resultCode);
	if (lim_mlm_reassoc_cnf->resultCode == eSIR_SME_SUCCESS) {
		/* Successful Reassociation */
		pe_debug("*** Reassociated with new BSS ***");

		session->limSmeState = eLIM_SME_LINK_EST_STATE;
		MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE,
		      session->peSessionId, session->limSmeState));

		/* Need to send Reassoc rsp with Reassoc success to Host. */
		lim_send_sme_join_reassoc_rsp(mac_ctx, eWNI_SME_REASSOC_RSP,
					lim_mlm_reassoc_cnf->resultCode,
					lim_mlm_reassoc_cnf->protStatusCode,
					session, session->smeSessionId,
					session->transactionId);
	} else if (lim_mlm_reassoc_cnf->resultCode
			== eSIR_SME_REASSOC_REFUSED) {
		/*
		 * Reassociation failure With the New AP but we still have the
		 * link with the Older AP
		 */
		session->limSmeState = eLIM_SME_LINK_EST_STATE;
		MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE,
		       session->peSessionId, session->limSmeState));

		/* Need to send Reassoc rsp with Assoc failure to Host. */
		lim_send_sme_join_reassoc_rsp(mac_ctx, eWNI_SME_REASSOC_RSP,
					lim_mlm_reassoc_cnf->resultCode,
					lim_mlm_reassoc_cnf->protStatusCode,
					session, session->smeSessionId,
					session->transactionId);
	} else {
		/* Reassociation failure */
		session->limSmeState = eLIM_SME_JOIN_FAILURE_STATE;
		MTRACE(mac_trace(mac_ctx, TRACE_CODE_SME_STATE,
					   session->peSessionId, session->limSmeState));
		/* Need to send Reassoc rsp with Assoc failure to Host. */
		lim_handle_sme_reaasoc_result(mac_ctx,
					lim_mlm_reassoc_cnf->resultCode,
					lim_mlm_reassoc_cnf->protStatusCode,
					session);
	}

	if (session->pLimReAssocReq) {
		qdf_mem_free(session->pLimReAssocReq);
		session->pLimReAssocReq = NULL;
	}
}

/**
 * lim_process_sta_mlm_add_bss_rsp_ft() - Handle the ADD BSS response
 * @pMac: Global MAC context
 * @limMsgQ: ADD BSS Parameters
 * @psessionEntry: PE Session
 *
 * Function to handle WMA_ADD_BSS_RSP, in FT reassoc state.
 * Send ReAssociation Request.
 *
 *Return: None
 */
void lim_process_sta_mlm_add_bss_rsp_ft(tpAniSirGlobal pMac,
		struct scheduler_msg *limMsgQ, tpPESession psessionEntry)
{
	tLimMlmReassocCnf mlmReassocCnf; /* keep sme */
	tpDphHashNode pStaDs = NULL;
	tpAddStaParams pAddStaParams = NULL;
	uint32_t listenInterval = WNI_CFG_LISTEN_INTERVAL_STADEF;
	tpAddBssParams pAddBssParams = (tpAddBssParams) limMsgQ->bodyptr;
	uint32_t selfStaDot11Mode = 0;

	/* Sanity Checks */

	if (pAddBssParams == NULL) {
		pe_err("Invalid parameters");
		goto end;
	}
	if (eLIM_MLM_WT_ADD_BSS_RSP_FT_REASSOC_STATE !=
	    psessionEntry->limMlmState) {
		goto end;
	}

	pStaDs = dph_add_hash_entry(pMac, pAddBssParams->bssId,
					DPH_STA_HASH_INDEX_PEER,
					&psessionEntry->dph.dphHashTable);
	if (pStaDs == NULL) {
		/* Could not add hash table entry */
		pe_err("could not add hash entry at DPH for");
		lim_print_mac_addr(pMac, pAddBssParams->staContext.staMac,
				   LOGE);
		goto end;
	}
	/* Prepare and send Reassociation request frame */
	/* start reassoc timer. */
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
	if (psessionEntry->bRoamSynchInProgress != true) {
#endif
		pMac->lim.limTimers.gLimReassocFailureTimer.sessionId =
			psessionEntry->peSessionId;
		/* / Start reassociation failure timer */
		MTRACE(mac_trace
			(pMac, TRACE_CODE_TIMER_ACTIVATE,
			 psessionEntry->peSessionId, eLIM_REASSOC_FAIL_TIMER));
		if (tx_timer_activate
			(&pMac->lim.limTimers.gLimReassocFailureTimer)
			!= TX_SUCCESS) {
			/* / Could not start reassoc failure timer. */
			/* Log error */
			pe_err("could not start Reassoc failure timer");
			/* Return Reassoc confirm with */
			/* Resources Unavailable */
			mlmReassocCnf.resultCode =
				eSIR_SME_RESOURCES_UNAVAILABLE;
			mlmReassocCnf.protStatusCode =
				eSIR_MAC_UNSPEC_FAILURE_STATUS;
			goto end;
		}
		pMac->lim.pSessionEntry = psessionEntry;
		if (NULL == pMac->lim.pSessionEntry->pLimMlmReassocRetryReq) {
			/* Take a copy of reassoc request for retrying */
			pMac->lim.pSessionEntry->pLimMlmReassocRetryReq =
				qdf_mem_malloc(sizeof(tLimMlmReassocReq));
			if (NULL ==
				pMac->lim.pSessionEntry->pLimMlmReassocRetryReq)
				goto end;
			qdf_mem_copy(pMac->lim.pSessionEntry->
					pLimMlmReassocRetryReq,
					psessionEntry->pLimMlmReassocReq,
					sizeof(tLimMlmReassocReq));
		}
		pMac->lim.reAssocRetryAttempt = 0;
		lim_send_reassoc_req_with_ft_ies_mgmt_frame(pMac,
				psessionEntry->
				pLimMlmReassocReq,
				psessionEntry);
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
	} else {
		pe_debug("LFR3:Do not activate timer and dont send the reassoc");
	}
#endif
	psessionEntry->limPrevMlmState = psessionEntry->limMlmState;
	psessionEntry->limMlmState = eLIM_MLM_WT_FT_REASSOC_RSP_STATE;
	MTRACE(mac_trace
		       (pMac, TRACE_CODE_MLM_STATE, psessionEntry->peSessionId,
		       eLIM_MLM_WT_FT_REASSOC_RSP_STATE));
	pe_debug("Set the mlm state: %d session: %d",
		       psessionEntry->limMlmState, psessionEntry->peSessionId);

	psessionEntry->bssIdx = (uint8_t) pAddBssParams->bssIdx;

	/* Success, handle below */
	pStaDs->bssId = pAddBssParams->bssIdx;
	/* STA Index(genr by HAL) for the BSS entry is stored here */
	pStaDs->staIndex = pAddBssParams->staContext.staIdx;
	pStaDs->ucUcastSig = pAddBssParams->staContext.ucUcastSig;
	pStaDs->ucBcastSig = pAddBssParams->staContext.ucBcastSig;

	rrm_cache_mgmt_tx_power(pMac, pAddBssParams->txMgmtPower,
			psessionEntry);

	pAddStaParams = qdf_mem_malloc(sizeof(tAddStaParams));
	if (NULL == pAddStaParams) {
		pe_err("Unable to allocate memory during ADD_STA");
		goto end;
	}

	/* / Add STA context at MAC HW (BMU, RHP & TFP) */
	qdf_mem_copy((uint8_t *) pAddStaParams->staMac,
		     (uint8_t *) psessionEntry->selfMacAddr,
		     sizeof(tSirMacAddr));

	qdf_mem_copy((uint8_t *) pAddStaParams->bssId,
		     psessionEntry->bssId, sizeof(tSirMacAddr));

	pAddStaParams->staType = STA_ENTRY_SELF;
	pAddStaParams->status = QDF_STATUS_SUCCESS;
	pAddStaParams->respReqd = 1;

	/* Update  PE session ID */
	pAddStaParams->sessionId = psessionEntry->peSessionId;
	pAddStaParams->smesessionId = psessionEntry->smeSessionId;

	/* This will indicate HAL to "allocate" a new STA index */
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
	if (psessionEntry->bRoamSynchInProgress != true)
#endif
		pAddStaParams->staIdx = STA_INVALID_IDX;
	pAddStaParams->updateSta = false;

	pAddStaParams->shortPreambleSupported =
		(uint8_t) psessionEntry->beaconParams.fShortPreamble;
	lim_populate_peer_rate_set(pMac, &pAddStaParams->supportedRates, NULL,
				   false, psessionEntry, NULL, NULL);

	if (psessionEntry->htCapability) {
		pAddStaParams->htCapable = psessionEntry->htCapability;
		pAddStaParams->vhtCapable = psessionEntry->vhtCapability;
		pAddStaParams->ch_width = psessionEntry->ch_width;
		pAddStaParams->greenFieldCapable =
			lim_get_ht_capability(pMac, eHT_GREENFIELD,
					      psessionEntry);
		pAddStaParams->mimoPS =
			lim_get_ht_capability(pMac, eHT_MIMO_POWER_SAVE,
					      psessionEntry);
		pAddStaParams->rifsMode =
			lim_get_ht_capability(pMac, eHT_RIFS_MODE,
					psessionEntry);
		pAddStaParams->lsigTxopProtection =
			lim_get_ht_capability(pMac, eHT_LSIG_TXOP_PROTECTION,
					      psessionEntry);
		pAddStaParams->maxAmpduDensity =
			lim_get_ht_capability(pMac, eHT_MPDU_DENSITY,
					psessionEntry);
		pAddStaParams->maxAmpduSize =
			lim_get_ht_capability(pMac, eHT_MAX_RX_AMPDU_FACTOR,
					      psessionEntry);
		pAddStaParams->maxAmsduSize =
			lim_get_ht_capability(pMac, eHT_MAX_AMSDU_LENGTH,
					      psessionEntry);
		pAddStaParams->max_amsdu_num =
			lim_get_ht_capability(pMac, eHT_MAX_AMSDU_NUM,
					      psessionEntry);
		pAddStaParams->fDsssCckMode40Mhz =
			lim_get_ht_capability(pMac, eHT_DSSS_CCK_MODE_40MHZ,
					      psessionEntry);
		pAddStaParams->fShortGI20Mhz =
			lim_get_ht_capability(pMac, eHT_SHORT_GI_20MHZ,
					psessionEntry);
		pAddStaParams->fShortGI40Mhz =
			lim_get_ht_capability(pMac, eHT_SHORT_GI_40MHZ,
					psessionEntry);
	}

	if (wlan_cfg_get_int(pMac, WNI_CFG_LISTEN_INTERVAL, &listenInterval) !=
	    eSIR_SUCCESS)
		pe_err("Couldn't get LISTEN_INTERVAL");
	pAddStaParams->listenInterval = (uint16_t) listenInterval;

	wlan_cfg_get_int(pMac, WNI_CFG_DOT11_MODE, &selfStaDot11Mode);
	pAddStaParams->encryptType = psessionEntry->encryptType;
	pAddStaParams->maxTxPower = psessionEntry->maxTxPower;

	/* Lets save this for when we receive the Reassoc Rsp */
	psessionEntry->ftPEContext.pAddStaReq = pAddStaParams;

	if (pAddBssParams != NULL) {
		qdf_mem_free(pAddBssParams);
		pAddBssParams = NULL;
		limMsgQ->bodyptr = NULL;
	}
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
	if (psessionEntry->bRoamSynchInProgress) {
		pe_debug("LFR3:Prep and save AddStaReq for post-assoc-rsp");
		lim_process_assoc_rsp_frame(pMac, pMac->roam.pReassocResp,
					    LIM_REASSOC, psessionEntry);
	}
#endif
	return;

end:
	/* Free up buffer allocated for reassocReq */
	if (psessionEntry != NULL)
		if (psessionEntry->pLimMlmReassocReq != NULL) {
			qdf_mem_free(psessionEntry->pLimMlmReassocReq);
			psessionEntry->pLimMlmReassocReq = NULL;
		}

	if (pAddBssParams != NULL) {
		qdf_mem_free(pAddBssParams);
		pAddBssParams = NULL;
		limMsgQ->bodyptr = NULL;
	}

	mlmReassocCnf.resultCode = eSIR_SME_FT_REASSOC_FAILURE;
	mlmReassocCnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
	/* Update PE session Id */
	if (psessionEntry != NULL)
		mlmReassocCnf.sessionId = psessionEntry->peSessionId;
	else
		mlmReassocCnf.sessionId = 0;

	lim_post_sme_message(pMac, LIM_MLM_REASSOC_CNF,
			     (uint32_t *) &mlmReassocCnf);
}

/**
 * lim_process_mlm_ft_reassoc_req() - Handle the Reassoc request
 * @pMac: Global MAC context
 * @pMsgBuf: Buffer which holds the data
 * @psessionEntry: PE Session
 *
 *  This function handles the Reassoc Req from SME
 *
 *  Return: None
 */
void lim_process_mlm_ft_reassoc_req(tpAniSirGlobal pMac, uint32_t *pMsgBuf,
				    tpPESession psessionEntry)
{
	uint8_t smeSessionId = 0;
	uint16_t transactionId = 0;
	uint8_t chanNum = 0;
	tLimMlmReassocReq *pMlmReassocReq;
	uint16_t caps;
	uint32_t val;
	struct scheduler_msg msgQ = {0};
	tSirRetStatus retCode;
	uint32_t teleBcnEn = 0;

	chanNum = psessionEntry->currentOperChannel;
	lim_get_session_info(pMac, (uint8_t *) pMsgBuf, &smeSessionId,
			     &transactionId);
	psessionEntry->smeSessionId = smeSessionId;
	psessionEntry->transactionId = transactionId;

#ifdef FEATURE_WLAN_DIAG_SUPPORT_LIM    /* FEATURE_WLAN_DIAG_SUPPORT */
	lim_diag_event_report(pMac, WLAN_PE_DIAG_REASSOCIATING,
			psessionEntry, 0, 0);
#endif

	/* Nothing to be done if the session is not in STA mode */
	if (!LIM_IS_STA_ROLE(psessionEntry)) {
		pe_err("psessionEntry is not in STA mode");
		return;
	}

	if (NULL == psessionEntry->ftPEContext.pAddBssReq) {
		pe_err("pAddBssReq is NULL");
		return;
	}
	pMlmReassocReq = qdf_mem_malloc(sizeof(tLimMlmReassocReq));
	if (NULL == pMlmReassocReq) {
		pe_err("call to AllocateMemory failed for mlmReassocReq");
		return;
	}

	qdf_mem_copy(pMlmReassocReq->peerMacAddr,
		     psessionEntry->bssId, sizeof(tSirMacAddr));

	if (wlan_cfg_get_int(pMac, WNI_CFG_REASSOCIATION_FAILURE_TIMEOUT,
			(uint32_t *) &pMlmReassocReq->reassocFailureTimeout)
	    != eSIR_SUCCESS) {
		/**
		 * Could not get ReassocFailureTimeout value
		 * from CFG. Log error.
		 */
		pe_err("could not retrieve ReassocFailureTimeout value");
		qdf_mem_free(pMlmReassocReq);
		return;
	}

	if (cfg_get_capability_info(pMac, &caps, psessionEntry) !=
			eSIR_SUCCESS) {
		/**
		 * Could not get Capabilities value
		 * from CFG. Log error.
		 */
		pe_err("could not get Capabilities value");
		qdf_mem_free(pMlmReassocReq);
		return;
	}

	lim_update_caps_info_for_bss(pMac, &caps,
		psessionEntry->pLimReAssocReq->bssDescription.capabilityInfo);
	pe_debug("Capabilities info FT Reassoc: 0x%X", caps);

	pMlmReassocReq->capabilityInfo = caps;

	/* Update PE sessionId */
	pMlmReassocReq->sessionId = psessionEntry->peSessionId;

	/* If telescopic beaconing is enabled, set listen interval
	   to WNI_CFG_TELE_BCN_MAX_LI
	 */
	if (wlan_cfg_get_int(pMac, WNI_CFG_TELE_BCN_WAKEUP_EN, &teleBcnEn) !=
	    eSIR_SUCCESS) {
		pe_err("Couldn't get WNI_CFG_TELE_BCN_WAKEUP_EN");
		qdf_mem_free(pMlmReassocReq);
		return;
	}

	if (teleBcnEn) {
		if (wlan_cfg_get_int(pMac, WNI_CFG_TELE_BCN_MAX_LI, &val) !=
		    eSIR_SUCCESS) {
			/**
			 * Could not get ListenInterval value
			 * from CFG. Log error.
			 */
			pe_err("could not retrieve ListenInterval");
			qdf_mem_free(pMlmReassocReq);
			return;
		}
	} else {
		if (wlan_cfg_get_int(pMac, WNI_CFG_LISTEN_INTERVAL, &val) !=
		    eSIR_SUCCESS) {
			/**
			 * Could not get ListenInterval value
			 * from CFG. Log error.
			 */
			pe_err("could not retrieve ListenInterval");
			qdf_mem_free(pMlmReassocReq);
			return;
		}
	}
	if (lim_set_link_state
		    (pMac, eSIR_LINK_PREASSOC_STATE, psessionEntry->bssId,
		    psessionEntry->selfMacAddr, NULL, NULL) != eSIR_SUCCESS) {
		qdf_mem_free(pMlmReassocReq);
		return;
	}

	pMlmReassocReq->listenInterval = (uint16_t) val;
	psessionEntry->pLimMlmReassocReq = pMlmReassocReq;

	/* we need to defer the message until we get response back from HAL */
	SET_LIM_PROCESS_DEFD_MESGS(pMac, false);

	msgQ.type = SIR_HAL_ADD_BSS_REQ;
	msgQ.reserved = 0;
	msgQ.bodyptr = psessionEntry->ftPEContext.pAddBssReq;
	msgQ.bodyval = 0;

	pe_debug("Sending SIR_HAL_ADD_BSS_REQ");
	MTRACE(mac_trace_msg_tx(pMac, psessionEntry->peSessionId, msgQ.type));
	retCode = wma_post_ctrl_msg(pMac, &msgQ);
	if (eSIR_SUCCESS != retCode) {
		qdf_mem_free(psessionEntry->ftPEContext.pAddBssReq);
		pe_err("Posting ADD_BSS_REQ to HAL failed, reason: %X",
			retCode);
	}

	psessionEntry->ftPEContext.pAddBssReq = NULL;
	return;
}

