/*
 * 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_send_frames_host_roam.c
 *
 * Send management frames for host based roaming
 */
#include "sir_api.h"
#include "ani_global.h"
#include "sir_mac_prot_def.h"
#include "cfg_api.h"
#include "utils_api.h"
#include "lim_types.h"
#include "lim_utils.h"
#include "lim_security_utils.h"
#include "lim_prop_exts_utils.h"
#include "dot11f.h"
#include "lim_sta_hash_api.h"
#include "sch_api.h"
#include "lim_send_messages.h"
#include "lim_assoc_utils.h"
#include "lim_ft.h"
#ifdef WLAN_FEATURE_11W
#include "wni_cfg.h"
#endif

#include "lim_ft_defs.h"
#include "lim_session.h"
#include "qdf_types.h"
#include "qdf_trace.h"
#include "cds_utils.h"
#include "sme_trace.h"
#include "rrm_api.h"

#include "wma_types.h"

/**
 * lim_send_reassoc_req_with_ft_ies_mgmt_frame() - Send Reassoc Req with FTIEs.
 *
 * @mac_ctx: Handle to mac context
 * @mlm_reassoc_req: Original reassoc request
 * @pe_session: PE session information
 *
 * It builds a reassoc request with FT IEs and sends it to AP through WMA.
 * Then it creates assoc request and stores it for sending after join
 * confirmation.
 *
 * Return: None
 */
void lim_send_reassoc_req_with_ft_ies_mgmt_frame(tpAniSirGlobal mac_ctx,
	tLimMlmReassocReq *mlm_reassoc_req,
	tpPESession pe_session)
{
	static tDot11fReAssocRequest frm;
	uint16_t caps;
	uint8_t *frame;
	uint32_t bytes, payload, status;
	uint8_t qos_enabled, wme_enabled, wsm_enabled;
	void *packet;
	QDF_STATUS qdf_status;
	uint8_t power_caps_populated = false;
	uint16_t ft_ies_length = 0;
	uint8_t *body;
	uint16_t add_ie_len;
	uint8_t *add_ie;
	uint8_t *wps_ie = NULL;
	uint8_t tx_flag = 0;
	uint8_t sme_sessionid = 0;
	bool vht_enabled = false;
	tpSirMacMgmtHdr mac_hdr;
	tftSMEContext *ft_sme_context;

	if (NULL == pe_session)
		return;

	sme_sessionid = pe_session->smeSessionId;

	/* check this early to avoid unncessary operation */
	if (NULL == pe_session->pLimReAssocReq)
		return;

	add_ie_len = pe_session->pLimReAssocReq->addIEAssoc.length;
	add_ie = pe_session->pLimReAssocReq->addIEAssoc.addIEdata;
	pe_debug("called in state: %d", pe_session->limMlmState);

	qdf_mem_set((uint8_t *) &frm, sizeof(frm), 0);

	caps = mlm_reassoc_req->capabilityInfo;
#if defined(FEATURE_WLAN_WAPI)
	/*
	 * According to WAPI standard:
	 * 7.3.1.4 Capability Information field
	 * In WAPI, non-AP STAs within an ESS set the Privacy subfield
	 * to 0 in transmitted Association or Reassociation management
	 * frames. APs ignore the Privacy subfield within received
	 * Association and Reassociation management frames.
	 */
	if (pe_session->encryptType == eSIR_ED_WPI)
		((tSirMacCapabilityInfo *) &caps)->privacy = 0;
#endif
	swap_bit_field16(caps, (uint16_t *) &frm.Capabilities);

	frm.ListenInterval.interval = mlm_reassoc_req->listenInterval;

	/*
	 * Get the old bssid of the older AP.
	 * The previous ap bssid is stored in the FT Session
	 * while creating the PE FT Session for reassociation.
	 */
	qdf_mem_copy((uint8_t *)frm.CurrentAPAddress.mac,
			pe_session->prev_ap_bssid, sizeof(tSirMacAddr));

	populate_dot11f_ssid2(mac_ctx, &frm.SSID);
	populate_dot11f_supp_rates(mac_ctx, POPULATE_DOT11F_RATES_OPERATIONAL,
		&frm.SuppRates, pe_session);

	qos_enabled = (pe_session->limQosEnabled) &&
		      SIR_MAC_GET_QOS(pe_session->limReassocBssCaps);

	wme_enabled = (pe_session->limWmeEnabled) &&
		      LIM_BSS_CAPS_GET(WME, pe_session->limReassocBssQosCaps);

	wsm_enabled = (pe_session->limWsmEnabled) && wme_enabled &&
		      LIM_BSS_CAPS_GET(WSM, pe_session->limReassocBssQosCaps);

	if (pe_session->lim11hEnable &&
	    pe_session->pLimReAssocReq->spectrumMgtIndicator == true) {
		power_caps_populated = true;

		populate_dot11f_power_caps(mac_ctx, &frm.PowerCaps,
					   LIM_REASSOC, pe_session);
		populate_dot11f_supp_channels(mac_ctx, &frm.SuppChannels,
			LIM_REASSOC, pe_session);
	}
	if (mac_ctx->rrm.rrmPEContext.rrmEnable &&
	    SIR_MAC_GET_RRM(pe_session->limCurrentBssCaps)) {
		if (power_caps_populated == false) {
			power_caps_populated = true;
			populate_dot11f_power_caps(mac_ctx, &frm.PowerCaps,
				LIM_REASSOC, pe_session);
		}
	}

	if (qos_enabled)
		populate_dot11f_qos_caps_station(mac_ctx, pe_session,
						&frm.QOSCapsStation);

	populate_dot11f_ext_supp_rates(mac_ctx,
		POPULATE_DOT11F_RATES_OPERATIONAL, &frm.ExtSuppRates,
		pe_session);

	if (mac_ctx->rrm.rrmPEContext.rrmEnable &&
	    SIR_MAC_GET_RRM(pe_session->limCurrentBssCaps))
		populate_dot11f_rrm_ie(mac_ctx, &frm.RRMEnabledCap, pe_session);

	/*
	 * Ideally this should be enabled for 11r also. But 11r does
	 * not follow the usual norm of using the Opaque object
	 * for rsnie and fties. Instead we just add the rsnie and fties
	 * at the end of the pack routine for 11r.
	 * This should ideally! be fixed.
	 */
	/*
	 * The join request *should* contain zero or one of the WPA and RSN
	 * IEs.  The payload send along with the request is a
	 * 'tSirSmeJoinReq'; the IE portion is held inside a 'tSirRSNie':
	 *
	 *     typedef struct sSirRSNie
	 *     {
	 *         uint16_t       length;
	 *         uint8_t        rsnIEdata[SIR_MAC_MAX_IE_LENGTH+2];
	 *     } tSirRSNie, *tpSirRSNie;
	 *
	 * So, we should be able to make the following two calls harmlessly,
	 * since they do nothing if they don't find the given IE in the
	 * bytestream with which they're provided.
	 *
	 * The net effect of this will be to faithfully transmit whatever
	 * security IE is in the join request.

	 * However, if we're associating for the purpose of WPS
	 * enrollment, and we've been configured to indicate that by
	 * eliding the WPA or RSN IE, we just skip this:
	 */
	if (!pe_session->is11Rconnection) {
		if (add_ie_len && add_ie)
			wps_ie = limGetWscIEPtr(mac_ctx, add_ie, add_ie_len);
		if (NULL == wps_ie) {
			populate_dot11f_rsn_opaque(mac_ctx,
				&(pe_session->pLimReAssocReq->rsnIE),
				&frm.RSNOpaque);
			populate_dot11f_wpa_opaque(mac_ctx,
				&(pe_session->pLimReAssocReq->rsnIE),
				&frm.WPAOpaque);
		}
#ifdef FEATURE_WLAN_ESE
		if (pe_session->pLimReAssocReq->cckmIE.length) {
			populate_dot11f_ese_cckm_opaque(mac_ctx,
				&(pe_session->pLimReAssocReq->cckmIE),
				&frm.ESECckmOpaque);
		}
#endif
	}
#ifdef FEATURE_WLAN_ESE
	/*
	 * ESE Version IE will be included in re-association request
	 * when ESE is enabled on DUT through ini and it is also
	 * advertised by the peer AP to which we are trying to
	 * associate to.
	 */
	if (pe_session->is_ese_version_ie_present &&
		mac_ctx->roam.configParam.isEseIniFeatureEnabled)
		populate_dot11f_ese_version(&frm.ESEVersion);
	/* For ESE Associations fill the ESE IEs */
	if (pe_session->isESEconnection &&
	    pe_session->pLimReAssocReq->isESEFeatureIniEnabled) {
#ifndef FEATURE_DISABLE_RM
		populate_dot11f_ese_rad_mgmt_cap(&frm.ESERadMgmtCap);
#endif
	}
#endif /* FEATURE_WLAN_ESE */

	/* include WME EDCA IE as well */
	if (wme_enabled) {
		populate_dot11f_wmm_info_station_per_session(mac_ctx,
			pe_session, &frm.WMMInfoStation);
		if (wsm_enabled)
			populate_dot11f_wmm_caps(&frm.WMMCaps);
#ifdef FEATURE_WLAN_ESE
		if (pe_session->isESEconnection) {
			uint32_t phymode;
			uint8_t rate;

			populate_dot11f_re_assoc_tspec(mac_ctx, &frm,
				pe_session);

			/*
			 * Populate the TSRS IE if TSPEC is included in
			 * the reassoc request
			 */
			lim_get_phy_mode(mac_ctx, &phymode, pe_session);
			if (phymode == WNI_CFG_PHY_MODE_11G ||
			    phymode == WNI_CFG_PHY_MODE_11A)
				rate = TSRS_11AG_RATE_6MBPS;
			else
				rate = TSRS_11B_RATE_5_5MBPS;

			if (pe_session->pLimReAssocReq->eseTspecInfo.
			    numTspecs) {
				tSirMacESETSRSIE tsrs_ie;

				tsrs_ie.tsid = 0;
				tsrs_ie.rates[0] = rate;
				populate_dot11_tsrsie(mac_ctx, &tsrs_ie,
					&frm.ESETrafStrmRateSet,
					sizeof(uint8_t));
			}
		}
#endif
	}

	ft_sme_context = &mac_ctx->roam.roamSession[sme_sessionid].ftSmeContext;
	if (pe_session->htCapability &&
	    mac_ctx->lim.htCapabilityPresentInBeacon) {
		populate_dot11f_ht_caps(mac_ctx, pe_session, &frm.HTCaps);
	}
	if (pe_session->pLimReAssocReq->bssDescription.mdiePresent &&
	    (ft_sme_context->addMDIE == true)
#if defined FEATURE_WLAN_ESE
	    && !pe_session->isESEconnection
#endif
	    ) {
		populate_mdie(mac_ctx, &frm.MobilityDomain,
			pe_session->pLimReAssocReq->bssDescription.mdie);
	}
	if (pe_session->vhtCapability &&
	    pe_session->vhtCapabilityPresentInBeacon) {
		pe_debug("Populate VHT IEs in Re-Assoc Request");
		populate_dot11f_vht_caps(mac_ctx, pe_session, &frm.VHTCaps);
		vht_enabled = true;
		populate_dot11f_ext_cap(mac_ctx, vht_enabled, &frm.ExtCap,
			pe_session);
	}
	if (!vht_enabled &&
			pe_session->is_vendor_specific_vhtcaps) {
		pe_debug("Populate Vendor VHT IEs in Re-Assoc Request");
		frm.vendor_vht_ie.present = 1;
		frm.vendor_vht_ie.type =
			pe_session->vendor_specific_vht_ie_type;
		frm.vendor_vht_ie.sub_type =
			pe_session->vendor_specific_vht_ie_sub_type;
		frm.vendor_vht_ie.VHTCaps.present = 1;
		populate_dot11f_vht_caps(mac_ctx, pe_session,
				&frm.vendor_vht_ie.VHTCaps);
		vht_enabled = true;
	}

	if (lim_is_session_he_capable(pe_session)) {
		pe_debug("Populate HE IEs");
		populate_dot11f_he_caps(mac_ctx, pe_session,
					&frm.vendor_he_cap);
	}

	status = dot11f_get_packed_re_assoc_request_size(mac_ctx, &frm,
			&payload);
	if (DOT11F_FAILED(status)) {
		pe_err("Failure in size calculation (0x%08x)", status);
		/* We'll fall back on the worst case scenario: */
		payload = sizeof(tDot11fReAssocRequest);
	} else if (DOT11F_WARNED(status)) {
		pe_warn("Warnings in size calculation (0x%08x)", status);
	}

	bytes = payload + sizeof(tSirMacMgmtHdr) + add_ie_len;

	pe_debug("FT IE Reassoc Req %d",
		ft_sme_context->reassoc_ft_ies_length);

	if (pe_session->is11Rconnection)
		ft_ies_length = ft_sme_context->reassoc_ft_ies_length;

	qdf_status = cds_packet_alloc((uint16_t) bytes + ft_ies_length,
				 (void **)&frame, (void **)&packet);
	if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
		pe_session->limMlmState = pe_session->limPrevMlmState;
		MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_MLM_STATE,
				 pe_session->peSessionId,
				 pe_session->limMlmState));
		pe_err("Failed to alloc memory %d", bytes);
		goto end;
	}
	/* Paranoia: */
	qdf_mem_set(frame, bytes + ft_ies_length, 0);

	lim_print_mac_addr(mac_ctx, pe_session->limReAssocbssId, LOGD);
	/* Next, we fill out the buffer descriptor: */
	lim_populate_mac_header(mac_ctx, frame, SIR_MAC_MGMT_FRAME,
		SIR_MAC_MGMT_REASSOC_REQ, pe_session->limReAssocbssId,
		pe_session->selfMacAddr);
	mac_hdr = (tpSirMacMgmtHdr) frame;
	/* That done, pack the ReAssoc Request: */
	status = dot11f_pack_re_assoc_request(mac_ctx, &frm, frame +
					       sizeof(tSirMacMgmtHdr),
					       payload, &payload);
	if (DOT11F_FAILED(status)) {
		pe_err("Failure in pack (0x%08x)", status);
		cds_packet_free((void *)packet);
		goto end;
	} else if (DOT11F_WARNED(status)) {
		pe_warn("Warnings in pack (0x%08x)", status);
	}

	pe_debug("*** Sending Re-Assoc Request length: %d %d to",
		       bytes, payload);

	if (pe_session->assocReq != NULL) {
		qdf_mem_free(pe_session->assocReq);
		pe_session->assocReq = NULL;
		pe_session->assocReqLen = 0;
	}

	if (add_ie_len) {
		qdf_mem_copy(frame + sizeof(tSirMacMgmtHdr) + payload,
			     add_ie, add_ie_len);
		payload += add_ie_len;
	}

	pe_session->assocReq = qdf_mem_malloc(payload);
	if (NULL == pe_session->assocReq) {
		pe_err("Failed to alloc memory");
	} else {
		/*
		 * Store the Assoc request. This is sent to csr/hdd in
		 * join cnf response.
		 */
		qdf_mem_copy(pe_session->assocReq,
			     frame + sizeof(tSirMacMgmtHdr), payload);
		pe_session->assocReqLen = payload;
	}

	if (pe_session->is11Rconnection && ft_sme_context->reassoc_ft_ies) {
		int i = 0;

		body = frame + bytes;
		for (i = 0; i < ft_ies_length; i++) {
			*body = ft_sme_context->reassoc_ft_ies[i];
			body++;
		}
	}
	pe_debug("Re-assoc Req Frame is:");
	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG,
			   (uint8_t *) frame, (bytes + ft_ies_length));

	if ((NULL != pe_session->ftPEContext.pFTPreAuthReq) &&
	    (SIR_BAND_5_GHZ == lim_get_rf_band(
	     pe_session->ftPEContext.pFTPreAuthReq->preAuthchannelNum)))
		tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME;
	else if ((SIR_BAND_5_GHZ ==
		  lim_get_rf_band(pe_session->currentOperChannel))
		 || (pe_session->pePersona == QDF_P2P_CLIENT_MODE)
		 || (pe_session->pePersona == QDF_P2P_GO_MODE))
		tx_flag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME;

	if (NULL != pe_session->assocReq) {
		qdf_mem_free(pe_session->assocReq);
		pe_session->assocReq = NULL;
		pe_session->assocReqLen = 0;
	}
	if (ft_ies_length) {
		pe_session->assocReq = qdf_mem_malloc(ft_ies_length);
		if (NULL == pe_session->assocReq) {
			pe_err("Failed to alloc memory for FT IEs");
			pe_session->assocReqLen = 0;
		} else {
			/*
			 * Store the FT IEs. This is sent to csr/hdd in
			 * join cnf response.
			 */
			qdf_mem_copy(pe_session->assocReq,
				ft_sme_context->reassoc_ft_ies, ft_ies_length);
			pe_session->assocReqLen = ft_ies_length;
		}
	} else {
		pe_debug("FT IEs not present");
		pe_session->assocReqLen = 0;
	}

#ifdef FEATURE_WLAN_DIAG_SUPPORT
	lim_diag_event_report(mac_ctx, WLAN_PE_DIAG_REASSOC_START_EVENT,
			      pe_session, eSIR_SUCCESS, eSIR_SUCCESS);
#endif
	MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT,
			 pe_session->peSessionId, mac_hdr->fc.subType));
	qdf_status = wma_tx_frame(mac_ctx, packet,
				(uint16_t) (bytes + ft_ies_length),
				TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, 7,
				lim_tx_complete, frame, tx_flag, sme_sessionid,
				0);
	MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE,
		       pe_session->peSessionId, qdf_status));
	if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
		pe_err("Failed to send Re-Assoc Request: %X!", qdf_status);
	}

end:
	/* Free up buffer allocated for mlmAssocReq */
	qdf_mem_free(mlm_reassoc_req);
	pe_session->pLimMlmReassocReq = NULL;

}

/**
 * lim_send_retry_reassoc_req_frame() - Retry for reassociation
 * @pMac: Global MAC Context
 * @pMlmReassocReq: Request buffer to be sent
 * @psessionEntry: PE Session
 *
 * Return: None
 */
void lim_send_retry_reassoc_req_frame(tpAniSirGlobal pMac,
				      tLimMlmReassocReq *pMlmReassocReq,
				      tpPESession psessionEntry)
{
	tLimMlmReassocCnf mlmReassocCnf;        /* keep sme */
	tLimMlmReassocReq *pTmpMlmReassocReq = NULL;
	if (NULL == pTmpMlmReassocReq) {
		pTmpMlmReassocReq = qdf_mem_malloc(sizeof(tLimMlmReassocReq));
		if (NULL == pTmpMlmReassocReq)
			goto end;
		qdf_mem_copy(pTmpMlmReassocReq, pMlmReassocReq,
			     sizeof(tLimMlmReassocReq));
	}
	/* Prepare and send Reassociation request frame */
	/* start reassoc timer. */
	pMac->lim.limTimers.gLimReassocFailureTimer.sessionId =
		psessionEntry->peSessionId;
	/* Start reassociation failure timer */
	MTRACE(qdf_trace(QDF_MODULE_ID_PE, 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 Reassociation failure timer");
		/* Return Reassoc confirm with */
		/* Resources Unavailable */
		mlmReassocCnf.resultCode = eSIR_SME_RESOURCES_UNAVAILABLE;
		mlmReassocCnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
		goto end;
	}

	lim_send_reassoc_req_with_ft_ies_mgmt_frame(pMac, pTmpMlmReassocReq,
						    psessionEntry);
	return;

end:
	/* Free up buffer allocated for reassocReq */
	if (pMlmReassocReq != NULL) {
		qdf_mem_free(pMlmReassocReq);
		pMlmReassocReq = NULL;
	}
	if (pTmpMlmReassocReq != NULL) {
		qdf_mem_free(pTmpMlmReassocReq);
		pTmpMlmReassocReq = NULL;
	}
	mlmReassocCnf.resultCode = eSIR_SME_FT_REASSOC_FAILURE;
	mlmReassocCnf.protStatusCode = eSIR_MAC_UNSPEC_FAILURE_STATUS;
	/* Update PE sessio Id */
	mlmReassocCnf.sessionId = psessionEntry->peSessionId;

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

/**
 * lim_send_reassoc_req_mgmt_frame() - Send the reassociation frame
 * @pMac: Global MAC Context
 * @pMlmReassocReq: Reassociation request buffer to be sent
 * @psessionEntry: PE Session
 *
 * Return: None
 */
void lim_send_reassoc_req_mgmt_frame(tpAniSirGlobal pMac,
				tLimMlmReassocReq *pMlmReassocReq,
				tpPESession psessionEntry)
{
	static tDot11fReAssocRequest frm;
	uint16_t caps;
	uint8_t *pFrame;
	uint32_t nBytes, nPayload, nStatus;
	uint8_t fQosEnabled, fWmeEnabled, fWsmEnabled;
	void *pPacket;
	QDF_STATUS qdf_status;
	uint16_t nAddIELen;
	uint8_t *pAddIE;
	uint8_t *wpsIe = NULL;
	uint8_t txFlag = 0;
	uint8_t PowerCapsPopulated = false;
	uint8_t smeSessionId = 0;
	bool isVHTEnabled = false;
	tpSirMacMgmtHdr pMacHdr;

	if (NULL == psessionEntry)
		return;

	smeSessionId = psessionEntry->smeSessionId;
	if (NULL == psessionEntry->pLimReAssocReq)
		return;
	nAddIELen = psessionEntry->pLimReAssocReq->addIEAssoc.length;
	pAddIE = psessionEntry->pLimReAssocReq->addIEAssoc.addIEdata;

	qdf_mem_set((uint8_t *) &frm, sizeof(frm), 0);

	caps = pMlmReassocReq->capabilityInfo;
#if defined(FEATURE_WLAN_WAPI)
	/*
	 * CR: 262463 :
	 * According to WAPI standard:
	 * 7.3.1.4 Capability Information field
	 * In WAPI, non-AP STAs within an ESS set the Privacy subfield to 0 in
	 * transmitted. Association or Reassociation management frames. APs
	 * ignore the Privacy subfield within received Association and
	 * Reassociation management frames.
	 */
	if (psessionEntry->encryptType == eSIR_ED_WPI)
		((tSirMacCapabilityInfo *) &caps)->privacy = 0;
#endif
	swap_bit_field16(caps, (uint16_t *) &frm.Capabilities);

	frm.ListenInterval.interval = pMlmReassocReq->listenInterval;

	qdf_mem_copy((uint8_t *) frm.CurrentAPAddress.mac,
		     (uint8_t *) psessionEntry->bssId, 6);

	populate_dot11f_ssid2(pMac, &frm.SSID);
	populate_dot11f_supp_rates(pMac, POPULATE_DOT11F_RATES_OPERATIONAL,
				   &frm.SuppRates, psessionEntry);

	fQosEnabled = (psessionEntry->limQosEnabled) &&
		      SIR_MAC_GET_QOS(psessionEntry->limReassocBssCaps);

	fWmeEnabled = (psessionEntry->limWmeEnabled) &&
		     LIM_BSS_CAPS_GET(WME, psessionEntry->limReassocBssQosCaps);

	fWsmEnabled = (psessionEntry->limWsmEnabled) && fWmeEnabled &&
		     LIM_BSS_CAPS_GET(WSM, psessionEntry->limReassocBssQosCaps);

	if (psessionEntry->lim11hEnable &&
	    psessionEntry->pLimReAssocReq->spectrumMgtIndicator == true) {
		PowerCapsPopulated = true;
		populate_dot11f_power_caps(pMac, &frm.PowerCaps, LIM_REASSOC,
					   psessionEntry);
		populate_dot11f_supp_channels(pMac, &frm.SuppChannels,
				LIM_REASSOC, psessionEntry);
	}
	if (pMac->rrm.rrmPEContext.rrmEnable &&
	    SIR_MAC_GET_RRM(psessionEntry->limCurrentBssCaps)) {
		if (PowerCapsPopulated == false) {
			PowerCapsPopulated = true;
			populate_dot11f_power_caps(pMac, &frm.PowerCaps,
						   LIM_REASSOC, psessionEntry);
		}
	}

	if (fQosEnabled)
		populate_dot11f_qos_caps_station(pMac, psessionEntry,
						&frm.QOSCapsStation);

	populate_dot11f_ext_supp_rates(pMac, POPULATE_DOT11F_RATES_OPERATIONAL,
				       &frm.ExtSuppRates, psessionEntry);

	if (pMac->rrm.rrmPEContext.rrmEnable &&
	    SIR_MAC_GET_RRM(psessionEntry->limCurrentBssCaps))
		populate_dot11f_rrm_ie(pMac, &frm.RRMEnabledCap, psessionEntry);
	/* The join request *should* contain zero or one of the WPA and RSN */
	/* IEs.  The payload send along with the request is a */
	/* 'tSirSmeJoinReq'; the IE portion is held inside a 'tSirRSNie': */

	/*     typedef struct sSirRSNie */
	/*     { */
	/*         uint16_t       length; */
	/*         uint8_t        rsnIEdata[SIR_MAC_MAX_IE_LENGTH+2]; */
	/*     } tSirRSNie, *tpSirRSNie; */

	/* So, we should be able to make the following two calls harmlessly, */
	/* since they do nothing if they don't find the given IE in the */
	/* bytestream with which they're provided. */

	/* The net effect of this will be to faithfully transmit whatever */
	/* security IE is in the join request. */

	/**However*, if we're associating for the purpose of WPS */
	/* enrollment, and we've been configured to indicate that by */
	/* eliding the WPA or RSN IE, we just skip this: */
	if (nAddIELen && pAddIE)
		wpsIe = limGetWscIEPtr(pMac, pAddIE, nAddIELen);
	if (NULL == wpsIe) {
		populate_dot11f_rsn_opaque(pMac,
				&(psessionEntry->pLimReAssocReq->rsnIE),
				&frm.RSNOpaque);
		populate_dot11f_wpa_opaque(pMac,
				&(psessionEntry->pLimReAssocReq->rsnIE),
				&frm.WPAOpaque);
#if defined(FEATURE_WLAN_WAPI)
		populate_dot11f_wapi_opaque(pMac,
					    &(psessionEntry->pLimReAssocReq->
					      rsnIE), &frm.WAPIOpaque);
#endif /* defined(FEATURE_WLAN_WAPI) */
	}
	/* include WME EDCA IE as well */
	if (fWmeEnabled) {
		populate_dot11f_wmm_info_station_per_session(pMac,
				psessionEntry, &frm.WMMInfoStation);

		if (fWsmEnabled)
			populate_dot11f_wmm_caps(&frm.WMMCaps);
	}

	if (psessionEntry->htCapability &&
	    pMac->lim.htCapabilityPresentInBeacon) {
		populate_dot11f_ht_caps(pMac, psessionEntry, &frm.HTCaps);
	}
	if (psessionEntry->vhtCapability &&
	    psessionEntry->vhtCapabilityPresentInBeacon) {
		pe_warn("Populate VHT IEs in Re-Assoc Request");
		populate_dot11f_vht_caps(pMac, psessionEntry, &frm.VHTCaps);
		isVHTEnabled = true;
	}
	populate_dot11f_ext_cap(pMac, isVHTEnabled, &frm.ExtCap, psessionEntry);

	if (lim_is_session_he_capable(psessionEntry)) {
		pe_debug("Populate HE IEs");
		populate_dot11f_he_caps(pMac, psessionEntry,
					&frm.vendor_he_cap);
	}

	nStatus =
		dot11f_get_packed_re_assoc_request_size(pMac, &frm, &nPayload);
	if (DOT11F_FAILED(nStatus)) {
		pe_err("Fail to get size:ReassocReq: (0x%08x)", nStatus);
		/* We'll fall back on the worst case scenario: */
		nPayload = sizeof(tDot11fReAssocRequest);
	} else if (DOT11F_WARNED(nStatus)) {
		pe_err("warning for size:ReAssoc Req: (0x%08x)", nStatus);
	}

	nBytes = nPayload + sizeof(tSirMacMgmtHdr) + nAddIELen;

	qdf_status = cds_packet_alloc((uint16_t) nBytes, (void **)&pFrame,
				      (void **)&pPacket);
	if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
		psessionEntry->limMlmState = psessionEntry->limPrevMlmState;
		MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_MLM_STATE,
				 psessionEntry->peSessionId,
				 psessionEntry->limMlmState));
		pe_err("Failed to alloc %d bytes for a ReAssociation Req",
			nBytes);
		goto end;
	}
	/* Paranoia: */
	qdf_mem_set(pFrame, nBytes, 0);

	/* Next, we fill out the buffer descriptor: */
	lim_populate_mac_header(pMac, pFrame, SIR_MAC_MGMT_FRAME,
		SIR_MAC_MGMT_REASSOC_REQ, psessionEntry->limReAssocbssId,
		psessionEntry->selfMacAddr);
	pMacHdr = (tpSirMacMgmtHdr) pFrame;

	/* That done, pack the Probe Request: */
	nStatus = dot11f_pack_re_assoc_request(pMac, &frm, pFrame +
					       sizeof(tSirMacMgmtHdr),
					       nPayload, &nPayload);
	if (DOT11F_FAILED(nStatus)) {
		pe_err("Fail to pack a Re-Assoc Req: (0x%08x)", nStatus);
		cds_packet_free((void *)pPacket);
		goto end;
	} else if (DOT11F_WARNED(nStatus)) {
		pe_warn("warning packing a Re-AssocReq: (0x%08x)", nStatus);
	}

	pe_debug("*** Sending Re-Association Request length: %d" "to", nBytes);

	if (psessionEntry->assocReq != NULL) {
		qdf_mem_free(psessionEntry->assocReq);
		psessionEntry->assocReq = NULL;
		psessionEntry->assocReqLen = 0;
	}

	if (nAddIELen) {
		qdf_mem_copy(pFrame + sizeof(tSirMacMgmtHdr) + nPayload,
			     pAddIE, nAddIELen);
		nPayload += nAddIELen;
	}

	psessionEntry->assocReq = qdf_mem_malloc(nPayload);
	if (NULL == psessionEntry->assocReq) {
		pe_err("Unable to allocate mem for assoc req");
	} else {
		/* Store the Assocrequest. It is sent to csr in joincnfrsp */
		qdf_mem_copy(psessionEntry->assocReq,
			     pFrame + sizeof(tSirMacMgmtHdr), nPayload);
		psessionEntry->assocReqLen = nPayload;
	}

	if ((SIR_BAND_5_GHZ ==
		lim_get_rf_band(psessionEntry->currentOperChannel))
			|| (psessionEntry->pePersona == QDF_P2P_CLIENT_MODE) ||
			(psessionEntry->pePersona == QDF_P2P_GO_MODE))
		txFlag |= HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME;

	if (psessionEntry->pePersona == QDF_P2P_CLIENT_MODE ||
		psessionEntry->pePersona == QDF_STA_MODE)
		txFlag |= HAL_USE_PEER_STA_REQUESTED_MASK;

#ifdef FEATURE_WLAN_DIAG_SUPPORT
	lim_diag_event_report(pMac, WLAN_PE_DIAG_REASSOC_START_EVENT,
			      psessionEntry, eSIR_SUCCESS, eSIR_SUCCESS);
#endif
	MTRACE(qdf_trace(QDF_MODULE_ID_PE, TRACE_CODE_TX_MGMT,
			 psessionEntry->peSessionId, pMacHdr->fc.subType));
	qdf_status =
		wma_tx_frame(pMac, pPacket,
			   (uint16_t) (sizeof(tSirMacMgmtHdr) + nPayload),
			   TXRX_FRM_802_11_MGMT, ANI_TXDIR_TODS, 7,
			   lim_tx_complete, pFrame, txFlag, smeSessionId, 0);
	MTRACE(qdf_trace
		       (QDF_MODULE_ID_PE, TRACE_CODE_TX_COMPLETE,
		       psessionEntry->peSessionId, qdf_status));
	if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
		pe_err("Failed to send Re-Association Request: %X!",
			qdf_status);
		/* Pkt will be freed up by the callback */
	}

end:
	/* Free up buffer allocated for mlmAssocReq */
	qdf_mem_free(pMlmReassocReq);
	psessionEntry->pLimMlmReassocReq = NULL;

}

