/*
 * Copyright (c) 2012-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.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/*
 * DOC: csr_tdls_process.c
 *
 * Implementation for the TDLS interface to PE.
 */
#ifdef FEATURE_WLAN_TDLS

#include "ani_global.h"          /* for tpAniSirGlobal */
#include "csr_inside_api.h"
#include "sme_inside.h"

#include "csr_support.h"

#include "host_diag_core_log.h"
#include "host_diag_core_event.h"
#include "csr_internal.h"

/*
 * common routine to remove TDLS cmd from SME command list..
 * commands are removed after getting reponse from PE.
 */
static QDF_STATUS csr_tdls_remove_sme_cmd(tpAniSirGlobal pMac,
			eSmeCommandType cmdType, uint8_t session_id)
{
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	tListElem *pEntry;
	tSmeCmd *pCommand;

	pEntry = csr_nonscan_active_ll_peek_head(pMac, LL_ACCESS_LOCK);
	if (pEntry) {
		pCommand = GET_BASE_ADDR(pEntry, tSmeCmd, Link);
		if (cmdType == pCommand->command) {
			if (csr_nonscan_active_ll_remove_entry(pMac,
						pEntry, LL_ACCESS_LOCK)) {
				qdf_mem_zero(&pCommand->u.tdlsCmd,
					     sizeof(tTdlsCmd));
				csr_release_command(pMac, pCommand);
				status = QDF_STATUS_SUCCESS;
			}
		}
	}
	return status;
}

/**
 * csr_tdls_send_mgmt_req() - to send a TDLS frame to PE through SME
 * @hHal: HAL context
 * @sessionId: SME session id
 * @tdlsSendMgmt: tdls mgmt pointer
 *
 * TDLS request API, called from HDD to send a TDLS frame in SME/CSR and
 * send message to PE to trigger TDLS discovery procedure.
 */
QDF_STATUS csr_tdls_send_mgmt_req(tHalHandle hHal, uint8_t sessionId,
				  tCsrTdlsSendMgmt *tdlsSendMgmt)
{
	tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
	tSmeCmd *tdlsSendMgmtCmd;
	tTdlsSendMgmtCmdInfo *tdlsSendMgmtCmdInfo;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	/* If connected and in Infra. Only then allow this */
	if (!CSR_IS_SESSION_VALID(pMac, sessionId) ||
			!csr_is_conn_state_connected_infra(pMac, sessionId) ||
			(NULL == tdlsSendMgmt))
		return status;
	tdlsSendMgmtCmd = csr_get_command_buffer(pMac);
	if (!tdlsSendMgmtCmd)
		return status;
	tdlsSendMgmtCmdInfo = &tdlsSendMgmtCmd->u.tdlsCmd.u.tdlsSendMgmtCmdInfo;
	tdlsSendMgmtCmd->sessionId = sessionId;
	tdlsSendMgmtCmdInfo->frameType = tdlsSendMgmt->frameType;
	tdlsSendMgmtCmdInfo->dialog = tdlsSendMgmt->dialog;
	tdlsSendMgmtCmdInfo->statusCode = tdlsSendMgmt->statusCode;
	tdlsSendMgmtCmdInfo->responder = tdlsSendMgmt->responder;
	tdlsSendMgmtCmdInfo->peerCapability = tdlsSendMgmt->peerCapability;
	qdf_mem_copy(tdlsSendMgmtCmdInfo->peerMac, tdlsSendMgmt->peerMac,
			sizeof(tSirMacAddr));

	if ((0 != tdlsSendMgmt->len) && (NULL != tdlsSendMgmt->buf)) {
		tdlsSendMgmtCmdInfo->buf = qdf_mem_malloc(tdlsSendMgmt->len);
		if (NULL == tdlsSendMgmtCmdInfo->buf) {
			status = QDF_STATUS_E_NOMEM;
			sme_err("Alloc Failed");
			return status;
		}
		qdf_mem_copy(tdlsSendMgmtCmdInfo->buf, tdlsSendMgmt->buf,
				tdlsSendMgmt->len);
		tdlsSendMgmtCmdInfo->len = tdlsSendMgmt->len;
	} else {
		tdlsSendMgmtCmdInfo->buf = NULL;
		tdlsSendMgmtCmdInfo->len = 0;
	}

	tdlsSendMgmtCmd->command = eSmeCommandTdlsSendMgmt;
	tdlsSendMgmtCmd->u.tdlsCmd.size = sizeof(tTdlsSendMgmtCmdInfo);
	csr_queue_sme_command(pMac, tdlsSendMgmtCmd, false);
	status = QDF_STATUS_SUCCESS;

	return status;
}

/*
 * TDLS request API, called from HDD to add a TDLS peer
 */
QDF_STATUS csr_tdls_change_peer_sta(tHalHandle hHal, uint8_t sessionId,
				    const tSirMacAddr peerMac,
				    tCsrStaParams *pstaParams)
{
	tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
	tSmeCmd *tdlsAddStaCmd;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	/* If connected and in Infra. Only then allow this */
	if (CSR_IS_SESSION_VALID(pMac, sessionId) &&
	    csr_is_conn_state_connected_infra(pMac, sessionId) &&
	    (NULL != peerMac) && (NULL != pstaParams)) {

		tdlsAddStaCmd = csr_get_command_buffer(pMac);

		if (tdlsAddStaCmd) {
			tTdlsAddStaCmdInfo *tdlsAddStaCmdInfo =
				&tdlsAddStaCmd->u.tdlsCmd.u.tdlsAddStaCmdInfo;

			tdlsAddStaCmdInfo->tdlsAddOper = TDLS_OPER_UPDATE;

			tdlsAddStaCmd->sessionId = sessionId;

			qdf_mem_copy(tdlsAddStaCmdInfo->peermac.bytes,
				     peerMac, QDF_MAC_ADDR_SIZE);
			tdlsAddStaCmdInfo->capability = pstaParams->capability;
			tdlsAddStaCmdInfo->uapsdQueues =
				pstaParams->uapsd_queues;
			tdlsAddStaCmdInfo->maxSp = pstaParams->max_sp;
			qdf_mem_copy(tdlsAddStaCmdInfo->extnCapability,
				     pstaParams->extn_capability,
				     sizeof(pstaParams->extn_capability));

			tdlsAddStaCmdInfo->htcap_present =
				pstaParams->htcap_present;
			if (pstaParams->htcap_present)
				qdf_mem_copy(&tdlsAddStaCmdInfo->HTCap,
					     &pstaParams->HTCap,
					     sizeof(pstaParams->HTCap));
			else
				qdf_mem_set(&tdlsAddStaCmdInfo->HTCap,
					    sizeof(pstaParams->HTCap), 0);

			tdlsAddStaCmdInfo->vhtcap_present =
				pstaParams->vhtcap_present;
			if (pstaParams->vhtcap_present)
				qdf_mem_copy(&tdlsAddStaCmdInfo->VHTCap,
					     &pstaParams->VHTCap,
					     sizeof(pstaParams->VHTCap));
			else
				qdf_mem_set(&tdlsAddStaCmdInfo->VHTCap,
					    sizeof(pstaParams->VHTCap), 0);

			tdlsAddStaCmdInfo->supportedRatesLen =
				pstaParams->supported_rates_len;

			if (0 != pstaParams->supported_rates_len)
				qdf_mem_copy(&tdlsAddStaCmdInfo->supportedRates,
					     pstaParams->supported_rates,
					     pstaParams->supported_rates_len);

			tdlsAddStaCmd->command = eSmeCommandTdlsAddPeer;
			tdlsAddStaCmd->u.tdlsCmd.size =
				sizeof(tTdlsAddStaCmdInfo);
			csr_queue_sme_command(pMac, tdlsAddStaCmd, false);
			status = QDF_STATUS_SUCCESS;
		}
	}

	return status;
}

/*
 * TDLS request API, called from HDD to Send Link Establishment Parameters
 */
QDF_STATUS csr_tdls_send_link_establish_params(tHalHandle hHal,
					       uint8_t sessionId,
					       const tSirMacAddr peerMac,
					       tCsrTdlsLinkEstablishParams *
					       tdlsLinkEstablishParams)
{
	tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
	tSmeCmd *tdlsLinkEstablishCmd;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	/* If connected and in Infra. Only then allow this */
	if (CSR_IS_SESSION_VALID(pMac, sessionId) &&
	    csr_is_conn_state_connected_infra(pMac, sessionId) &&
	    (NULL != peerMac)) {
		tdlsLinkEstablishCmd = csr_get_command_buffer(pMac);

		if (tdlsLinkEstablishCmd) {
			tTdlsLinkEstablishCmdInfo *tdlsLinkEstablishCmdInfo =
				&tdlsLinkEstablishCmd->u.tdlsCmd.u.
				tdlsLinkEstablishCmdInfo;

			tdlsLinkEstablishCmd->sessionId = sessionId;

			qdf_mem_copy(tdlsLinkEstablishCmdInfo->peermac.bytes,
				     peerMac, QDF_MAC_ADDR_SIZE);
			tdlsLinkEstablishCmdInfo->isBufSta =
				tdlsLinkEstablishParams->isBufSta;
			tdlsLinkEstablishCmdInfo->isResponder =
				tdlsLinkEstablishParams->isResponder;
			tdlsLinkEstablishCmdInfo->maxSp =
				tdlsLinkEstablishParams->maxSp;
			tdlsLinkEstablishCmdInfo->uapsdQueues =
				tdlsLinkEstablishParams->uapsdQueues;
			tdlsLinkEstablishCmdInfo->isOffChannelSupported =
				tdlsLinkEstablishParams->isOffChannelSupported;
			qdf_mem_copy(tdlsLinkEstablishCmdInfo->
				     supportedChannels,
				     tdlsLinkEstablishParams->supportedChannels,
				     tdlsLinkEstablishParams->
				     supportedChannelsLen);
			tdlsLinkEstablishCmdInfo->supportedChannelsLen =
				tdlsLinkEstablishParams->supportedChannelsLen;
			qdf_mem_copy(tdlsLinkEstablishCmdInfo->
				     supportedOperClasses,
				     tdlsLinkEstablishParams->
				     supportedOperClasses,
				     tdlsLinkEstablishParams->
				     supportedOperClassesLen);
			tdlsLinkEstablishCmdInfo->supportedOperClassesLen =
			tdlsLinkEstablishParams->supportedOperClassesLen;
			tdlsLinkEstablishCmdInfo->isResponder =
				tdlsLinkEstablishParams->isResponder;
			tdlsLinkEstablishCmdInfo->maxSp =
				tdlsLinkEstablishParams->maxSp;
			tdlsLinkEstablishCmdInfo->uapsdQueues =
				tdlsLinkEstablishParams->uapsdQueues;
			tdlsLinkEstablishCmd->command =
				eSmeCommandTdlsLinkEstablish;
			tdlsLinkEstablishCmd->u.tdlsCmd.size =
				sizeof(tTdlsLinkEstablishCmdInfo);
			csr_queue_sme_command(pMac, tdlsLinkEstablishCmd,
						false);
			status = QDF_STATUS_SUCCESS;
		}
	}

	return status;
}

/*
 * TDLS request API, called from HDD to add a TDLS peer
 */
QDF_STATUS csr_tdls_add_peer_sta(tHalHandle hHal, uint8_t sessionId,
				 const tSirMacAddr peerMac)
{
	tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
	tSmeCmd *tdlsAddStaCmd;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	/* If connected and in Infra. Only then allow this */
	if (CSR_IS_SESSION_VALID(pMac, sessionId) &&
	    csr_is_conn_state_connected_infra(pMac, sessionId) &&
	    (NULL != peerMac)) {
		tdlsAddStaCmd = csr_get_command_buffer(pMac);

		if (tdlsAddStaCmd) {
			tTdlsAddStaCmdInfo *tdlsAddStaCmdInfo =
				&tdlsAddStaCmd->u.tdlsCmd.u.tdlsAddStaCmdInfo;

			tdlsAddStaCmd->sessionId = sessionId;
			tdlsAddStaCmdInfo->tdlsAddOper = TDLS_OPER_ADD;

			qdf_mem_copy(tdlsAddStaCmdInfo->peermac.bytes,
				     peerMac, QDF_MAC_ADDR_SIZE);

			tdlsAddStaCmd->command = eSmeCommandTdlsAddPeer;
			tdlsAddStaCmd->u.tdlsCmd.size =
				sizeof(tTdlsAddStaCmdInfo);
			csr_queue_sme_command(pMac, tdlsAddStaCmd, false);
			status = QDF_STATUS_SUCCESS;
		}
	}

	return status;
}

/*
 * TDLS request API, called from HDD to delete a TDLS peer
 */
QDF_STATUS csr_tdls_del_peer_sta(tHalHandle hHal, uint8_t sessionId,
				 const tSirMacAddr peerMac)
{
	tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
	tSmeCmd *tdlsDelStaCmd;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	/* If connected and in Infra. Only then allow this */
	if (CSR_IS_SESSION_VALID(pMac, sessionId) &&
	    csr_is_conn_state_connected_infra(pMac, sessionId) &&
	    (NULL != peerMac)) {
		tdlsDelStaCmd = csr_get_command_buffer(pMac);

		if (tdlsDelStaCmd) {
			tTdlsDelStaCmdInfo *tdlsDelStaCmdInfo =
				&tdlsDelStaCmd->u.tdlsCmd.u.tdlsDelStaCmdInfo;

			tdlsDelStaCmd->sessionId = sessionId;

			qdf_mem_copy(tdlsDelStaCmdInfo->peermac.bytes,
				     peerMac, QDF_MAC_ADDR_SIZE);

			tdlsDelStaCmd->command = eSmeCommandTdlsDelPeer;
			tdlsDelStaCmd->u.tdlsCmd.size =
				sizeof(tTdlsDelStaCmdInfo);
			csr_queue_sme_command(pMac, tdlsDelStaCmd, false);
			status = QDF_STATUS_SUCCESS;
		}
	}

	return status;
}

/*
 * TDLS messages sent to PE .
 */
static QDF_STATUS tdls_send_message(tpAniSirGlobal pMac, uint16_t msg_type,
				    void *msg_data, uint32_t msg_size)
{

	tSirMbMsg *pMsg = (tSirMbMsg *) msg_data;

	pMsg->type = msg_type;
	pMsg->msgLen = (uint16_t) (msg_size);
	QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
		  ("sending msg = %d"), pMsg->type);
	if (umac_send_mb_message_to_mac(pMsg) != QDF_STATUS_SUCCESS) {
		sme_err("Cannot send message");
		return QDF_STATUS_E_FAILURE;
	}

	return QDF_STATUS_SUCCESS;
}

static QDF_STATUS csr_tdls_process_send_mgmt(tpAniSirGlobal pMac, tSmeCmd *cmd)
{
	tTdlsSendMgmtCmdInfo *tdlsSendMgmtCmdInfo =
		&cmd->u.tdlsCmd.u.tdlsSendMgmtCmdInfo;
	tSirTdlsSendMgmtReq *tdlsSendMgmtReq = NULL;
	tCsrRoamSession *pSession = CSR_GET_SESSION(pMac, cmd->sessionId);
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	if (NULL == pSession) {
		sme_err("pSession is NULL");
		return QDF_STATUS_E_FAILURE;
	}
	if (NULL == pSession->pConnectBssDesc) {
		sme_err("BSS Description is not present");
		return QDF_STATUS_E_FAILURE;
	}

	tdlsSendMgmtReq =
		qdf_mem_malloc(sizeof(tSirTdlsSendMgmtReq) +
			       tdlsSendMgmtCmdInfo->len);
	if (NULL == tdlsSendMgmtReq)
		status = QDF_STATUS_E_NOMEM;
	else
		status = QDF_STATUS_SUCCESS;

	if (!QDF_IS_STATUS_SUCCESS(status)) {
		sme_err("alloc failed");
		return status;
	}
	tdlsSendMgmtReq->sessionId = cmd->sessionId;
	/* Using dialog as transactionId. This can be used to match
	 * response with request
	 */
	tdlsSendMgmtReq->transactionId = tdlsSendMgmtCmdInfo->dialog;
	tdlsSendMgmtReq->reqType = tdlsSendMgmtCmdInfo->frameType;
	tdlsSendMgmtReq->dialog = tdlsSendMgmtCmdInfo->dialog;
	tdlsSendMgmtReq->statusCode = tdlsSendMgmtCmdInfo->statusCode;
	tdlsSendMgmtReq->responder = tdlsSendMgmtCmdInfo->responder;
	tdlsSendMgmtReq->peerCapability = tdlsSendMgmtCmdInfo->peerCapability;

	qdf_mem_copy(tdlsSendMgmtReq->bssid.bytes,
		     pSession->pConnectBssDesc->bssId, QDF_MAC_ADDR_SIZE);

	qdf_mem_copy(tdlsSendMgmtReq->peer_mac.bytes,
		     tdlsSendMgmtCmdInfo->peerMac, QDF_MAC_ADDR_SIZE);

	if (tdlsSendMgmtCmdInfo->len && tdlsSendMgmtCmdInfo->buf) {
		qdf_mem_copy(tdlsSendMgmtReq->addIe, tdlsSendMgmtCmdInfo->buf,
			     tdlsSendMgmtCmdInfo->len);

	}
	/* Send the request to PE. */
	sme_debug("sending TDLS Mgmt Frame req to PE");
	status = tdls_send_message(pMac, eWNI_SME_TDLS_SEND_MGMT_REQ,
				   (void *)tdlsSendMgmtReq,
				   sizeof(tSirTdlsSendMgmtReq) +
				   tdlsSendMgmtCmdInfo->len);
	if (!QDF_IS_STATUS_SUCCESS(status))
		sme_err("Failed to send request to MAC");

	if (tdlsSendMgmtCmdInfo->len && tdlsSendMgmtCmdInfo->buf) {
		/* Done with the buf. Free it. */
		qdf_mem_free(tdlsSendMgmtCmdInfo->buf);
		tdlsSendMgmtCmdInfo->buf = NULL;
		tdlsSendMgmtCmdInfo->len = 0;
	}

	return status;
}

static QDF_STATUS csr_tdls_process_add_sta(tpAniSirGlobal pMac, tSmeCmd *cmd)
{
	tTdlsAddStaCmdInfo *tdlsAddStaCmdInfo =
		&cmd->u.tdlsCmd.u.tdlsAddStaCmdInfo;
	tSirTdlsAddStaReq *tdlsAddStaReq = NULL;
	tCsrRoamSession *pSession = CSR_GET_SESSION(pMac, cmd->sessionId);
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	if (NULL == pSession) {
		sme_err("pSession is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	if (NULL == pSession->pConnectBssDesc) {
		sme_err("BSS description is not present");
		return QDF_STATUS_E_FAILURE;
	}

	tdlsAddStaReq = qdf_mem_malloc(sizeof(tSirTdlsAddStaReq));
	if (NULL == tdlsAddStaReq)
		status = QDF_STATUS_E_NOMEM;
	else
		status = QDF_STATUS_SUCCESS;

	if (!QDF_IS_STATUS_SUCCESS(status)) {
		sme_err("alloc failed");
		return status;
	}
	tdlsAddStaReq->sessionId = cmd->sessionId;
	tdlsAddStaReq->tdlsAddOper = tdlsAddStaCmdInfo->tdlsAddOper;
	/* Using dialog as transactionId. This can be used to match
	 * response with request
	 */
	tdlsAddStaReq->transactionId = 0;

	qdf_mem_copy(tdlsAddStaReq->bssid.bytes,
		     pSession->pConnectBssDesc->bssId, QDF_MAC_ADDR_SIZE);

	qdf_copy_macaddr(&tdlsAddStaReq->peermac,
			 &tdlsAddStaCmdInfo->peermac);

	tdlsAddStaReq->capability = tdlsAddStaCmdInfo->capability;
	tdlsAddStaReq->uapsd_queues = tdlsAddStaCmdInfo->uapsdQueues;
	tdlsAddStaReq->max_sp = tdlsAddStaCmdInfo->maxSp;

	qdf_mem_copy(tdlsAddStaReq->extn_capability,
		     tdlsAddStaCmdInfo->extnCapability, SIR_MAC_MAX_EXTN_CAP);
	tdlsAddStaReq->htcap_present = tdlsAddStaCmdInfo->htcap_present;
	qdf_mem_copy(&tdlsAddStaReq->htCap,
		     &tdlsAddStaCmdInfo->HTCap,
		     sizeof(tdlsAddStaCmdInfo->HTCap));
	tdlsAddStaReq->vhtcap_present = tdlsAddStaCmdInfo->vhtcap_present;
	qdf_mem_copy(&tdlsAddStaReq->vhtCap,
		     &tdlsAddStaCmdInfo->VHTCap,
		     sizeof(tdlsAddStaCmdInfo->VHTCap));
	tdlsAddStaReq->supported_rates_length =
		tdlsAddStaCmdInfo->supportedRatesLen;
	qdf_mem_copy(&tdlsAddStaReq->supported_rates,
		     tdlsAddStaCmdInfo->supportedRates,
		     tdlsAddStaCmdInfo->supportedRatesLen);

	sme_debug("sending TDLS Add Sta req to PE");
	status = tdls_send_message(pMac, eWNI_SME_TDLS_ADD_STA_REQ,
				   (void *)tdlsAddStaReq,
				   sizeof(tSirTdlsAddStaReq));
	if (!QDF_IS_STATUS_SUCCESS(status))
		sme_err("Failed to send request to MAC");

	return status;
}

static QDF_STATUS csr_tdls_process_del_sta(tpAniSirGlobal pMac, tSmeCmd *cmd)
{
	tTdlsDelStaCmdInfo *tdlsDelStaCmdInfo =
		&cmd->u.tdlsCmd.u.tdlsDelStaCmdInfo;
	tSirTdlsDelStaReq *tdlsDelStaReq = NULL;
	tCsrRoamSession *pSession = CSR_GET_SESSION(pMac, cmd->sessionId);
	QDF_STATUS status = QDF_STATUS_E_FAILURE;

	if (NULL == pSession) {
		sme_err("pSession is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	if (NULL == pSession->pConnectBssDesc) {
		sme_err("BSS description is not present");
		return QDF_STATUS_E_FAILURE;
	}

	tdlsDelStaReq = qdf_mem_malloc(sizeof(tSirTdlsDelStaReq));
	if (NULL == tdlsDelStaReq)
		status = QDF_STATUS_E_NOMEM;
	else
		status = QDF_STATUS_SUCCESS;

	if (!QDF_IS_STATUS_SUCCESS(status)) {
		sme_err("alloc failed");
		return status;
	}
	tdlsDelStaReq->sessionId = cmd->sessionId;
	/* Using dialog as transactionId. This can be used to match
	 * response with request
	 */
	tdlsDelStaReq->transactionId = 0;

	qdf_mem_copy(tdlsDelStaReq->bssid.bytes,
		     pSession->pConnectBssDesc->bssId, QDF_MAC_ADDR_SIZE);

	qdf_copy_macaddr(&tdlsDelStaReq->peermac,
			 &tdlsDelStaCmdInfo->peermac);

	sme_debug("sending TDLS Del Sta " MAC_ADDRESS_STR " req to PE",
		MAC_ADDR_ARRAY(tdlsDelStaCmdInfo->peermac.bytes));
	status = tdls_send_message(pMac, eWNI_SME_TDLS_DEL_STA_REQ,
				   (void *)tdlsDelStaReq,
				   sizeof(tSirTdlsDelStaReq));
	if (!QDF_IS_STATUS_SUCCESS(status))
		sme_err("Failed to send request to MAC");

	return status;
}

/*
 * commands received from CSR
 */
QDF_STATUS csr_tdls_process_cmd(tpAniSirGlobal pMac, tSmeCmd *cmd)
{
	eSmeCommandType cmdType = cmd->command;
	bool status = true;

	switch (cmdType) {
	case eSmeCommandTdlsSendMgmt:
	{
		status = csr_tdls_process_send_mgmt(pMac, cmd);
		if (QDF_IS_STATUS_SUCCESS(status))
			status = false;
	}
	break;
	case eSmeCommandTdlsAddPeer:
	{
		status = csr_tdls_process_add_sta(pMac, cmd);
		if (QDF_IS_STATUS_SUCCESS(status))
			status = false;
	}
	break;
	case eSmeCommandTdlsDelPeer:
	{
		status = csr_tdls_process_del_sta(pMac, cmd);
		if (QDF_IS_STATUS_SUCCESS(status))
			status = false;
	}
	break;
	case eSmeCommandTdlsLinkEstablish:
	{
		status = csr_tdls_process_link_establish(pMac, cmd);
		if (QDF_IS_STATUS_SUCCESS(status))
			status = false;
	}
	break;
	default:
		/* TODO: Add defualt handling */
		break;
	}
	return status;
}

QDF_STATUS csr_tdls_process_link_establish(tpAniSirGlobal pMac, tSmeCmd *cmd)
{
	tTdlsLinkEstablishCmdInfo *tdlsLinkEstablishCmdInfo =
		&cmd->u.tdlsCmd.u.tdlsLinkEstablishCmdInfo;
	tSirTdlsLinkEstablishReq *tdlsLinkEstablishReq = NULL;
	QDF_STATUS status = QDF_STATUS_E_FAILURE;
	tCsrRoamSession *pSession = CSR_GET_SESSION(pMac, cmd->sessionId);

	if (NULL == pSession) {
		sme_err("pSession is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	tdlsLinkEstablishReq = qdf_mem_malloc(sizeof(tSirTdlsLinkEstablishReq));

	if (tdlsLinkEstablishReq == NULL) {
		sme_err("alloc failed");
		return QDF_STATUS_E_NOMEM;
	}
	tdlsLinkEstablishReq->sessionId = cmd->sessionId;
	/* Using dialog as transactionId. This can be used to match
	 * response with request
	 */
	tdlsLinkEstablishReq->transactionId = 0;
	qdf_copy_macaddr(&tdlsLinkEstablishReq->peermac,
			 &tdlsLinkEstablishCmdInfo->peermac);
	qdf_mem_copy(tdlsLinkEstablishReq->bssid.bytes,
		     pSession->pConnectBssDesc->bssId, QDF_MAC_ADDR_SIZE);
	qdf_mem_copy(tdlsLinkEstablishReq->supportedChannels,
		     tdlsLinkEstablishCmdInfo->supportedChannels,
		     tdlsLinkEstablishCmdInfo->supportedChannelsLen);
	tdlsLinkEstablishReq->supportedChannelsLen =
		tdlsLinkEstablishCmdInfo->supportedChannelsLen;
	qdf_mem_copy(tdlsLinkEstablishReq->supportedOperClasses,
		     tdlsLinkEstablishCmdInfo->supportedOperClasses,
		     tdlsLinkEstablishCmdInfo->supportedOperClassesLen);
	tdlsLinkEstablishReq->supportedOperClassesLen =
		tdlsLinkEstablishCmdInfo->supportedOperClassesLen;
	tdlsLinkEstablishReq->isBufSta = tdlsLinkEstablishCmdInfo->isBufSta;
	tdlsLinkEstablishReq->isResponder =
		tdlsLinkEstablishCmdInfo->isResponder;
	tdlsLinkEstablishReq->uapsdQueues =
		tdlsLinkEstablishCmdInfo->uapsdQueues;
	tdlsLinkEstablishReq->maxSp = tdlsLinkEstablishCmdInfo->maxSp;

	sme_debug("sending TDLS Link Establish Request to PE");
	status = tdls_send_message(pMac, eWNI_SME_TDLS_LINK_ESTABLISH_REQ,
				   (void *)tdlsLinkEstablishReq,
				   sizeof(tSirTdlsLinkEstablishReq));
	if (!QDF_IS_STATUS_SUCCESS(status))
		sme_err("Failed to send request to MAC");

	return status;
}

/*
 * TDLS Message processor, will be called after TDLS message recieved from
 * PE
 */
QDF_STATUS tdls_msg_processor(tpAniSirGlobal pMac, uint16_t msgType,
			      void *pMsgBuf)
{
	tCsrRoamInfo roamInfo = { 0 };
	eCsrRoamResult roamResult;
	tSirSmeRsp *sme_rsp = pMsgBuf;
	tSirTdlsAddStaRsp *addStaRsp = (tSirTdlsAddStaRsp *) pMsgBuf;
	tSirTdlsDelStaRsp *delStaRsp = (tSirTdlsDelStaRsp *) pMsgBuf;
	tpSirTdlsDelStaInd pSirTdlsDelStaInd = (tpSirTdlsDelStaInd) pMsgBuf;
	tpSirTdlsDelAllPeerInd pSirTdlsDelAllPeerInd =
		(tpSirTdlsDelAllPeerInd) pMsgBuf;
	tpSirMgmtTxCompletionInd tdls_tx_comp_ind =
		(tpSirMgmtTxCompletionInd) pMsgBuf;
	tSirTdlsLinkEstablishReqRsp *linkEstablishReqRsp =
		(tSirTdlsLinkEstablishReqRsp *) pMsgBuf;
	tSirTdlsEventnotify *tevent = (tSirTdlsEventnotify *) pMsgBuf;

	switch (msgType) {
	case eWNI_SME_TDLS_SEND_MGMT_RSP:
	{
		tSirSmeRsp *msg = (tSirSmeRsp *) pMsgBuf;
		tCsrRoamInfo roam_info = {0};

		/* remove pending eSmeCommandTdlsDiscovery command */
		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_INFO,
			FL("sme_rsp->sessionId[%d] eSmeCommandTdlsSendMgmt"),
			sme_rsp->sessionId);
		csr_tdls_remove_sme_cmd(pMac, eSmeCommandTdlsSendMgmt,
				sme_rsp->sessionId);

		if (eSIR_SME_SUCCESS != msg->statusCode) {
			/* Tx failed, so there wont be any ack confirmation*/
			/* Indicate ack failure to upper layer */
			roamInfo.reasonCode = 0;
			csr_roam_call_callback(pMac, msg->sessionId,
					&roam_info, 0,
					eCSR_ROAM_RESULT_MGMT_TX_COMPLETE_IND,
					0);
		}
		break;
	}
	case eWNI_SME_TDLS_ADD_STA_RSP:
		qdf_copy_macaddr(&roamInfo.peerMac, &addStaRsp->peermac);
		roamInfo.staId = addStaRsp->staId;
		roamInfo.ucastSig = addStaRsp->ucastSig;
		roamInfo.bcastSig = addStaRsp->bcastSig;
		roamInfo.statusCode = addStaRsp->statusCode;
		/*
		 * register peer with TL, we have to go through HDD as
		 * this is the only way to register any STA with TL.
		 */
		if (addStaRsp->tdlsAddOper == TDLS_OPER_ADD)
			roamResult = eCSR_ROAM_RESULT_ADD_TDLS_PEER;
		else    /* addStaRsp->tdlsAddOper must be TDLS_OPER_UPDATE */
			roamResult = eCSR_ROAM_RESULT_UPDATE_TDLS_PEER;
		csr_roam_call_callback(pMac, addStaRsp->sessionId,
				&roamInfo, 0, eCSR_ROAM_TDLS_STATUS_UPDATE,
				roamResult);

		/* remove pending eSmeCommandTdlsDiscovery command */
		csr_tdls_remove_sme_cmd(pMac, eSmeCommandTdlsAddPeer,
					addStaRsp->sessionId);
		break;
	case eWNI_SME_TDLS_DEL_STA_RSP:
		qdf_copy_macaddr(&roamInfo.peerMac, &delStaRsp->peermac);
		roamInfo.staId = delStaRsp->staId;
		roamInfo.statusCode = delStaRsp->statusCode;
		/*
		 * register peer with TL, we have to go through HDD as
		 * this is the only way to register any STA with TL.
		 */
		csr_roam_call_callback(pMac, delStaRsp->sessionId,
				&roamInfo, 0,
				eCSR_ROAM_TDLS_STATUS_UPDATE,
				eCSR_ROAM_RESULT_DELETE_TDLS_PEER);

		csr_tdls_remove_sme_cmd(pMac, eSmeCommandTdlsDelPeer,
					delStaRsp->sessionId);
		break;
	case eWNI_SME_TDLS_DEL_STA_IND:
		qdf_copy_macaddr(&roamInfo.peerMac,
				 &pSirTdlsDelStaInd->peermac);
		roamInfo.staId = pSirTdlsDelStaInd->staId;
		roamInfo.reasonCode = pSirTdlsDelStaInd->reasonCode;

		/* Sending the TEARDOWN indication to HDD. */
		csr_roam_call_callback(pMac,
				pSirTdlsDelStaInd->sessionId, &roamInfo,
				0, eCSR_ROAM_TDLS_STATUS_UPDATE,
				eCSR_ROAM_RESULT_TEARDOWN_TDLS_PEER_IND);
		break;
	case eWNI_SME_TDLS_DEL_ALL_PEER_IND:
		/* Sending the TEARDOWN indication to HDD. */
		csr_roam_call_callback(pMac,
				pSirTdlsDelAllPeerInd->sessionId,
				&roamInfo, 0,
				eCSR_ROAM_TDLS_STATUS_UPDATE,
				eCSR_ROAM_RESULT_DELETE_ALL_TDLS_PEER_IND);
		break;
	case eWNI_SME_MGMT_FRM_TX_COMPLETION_IND:
		roamInfo.reasonCode =
			tdls_tx_comp_ind->txCompleteStatus;

		csr_roam_call_callback(pMac,
				tdls_tx_comp_ind->sessionId,
				&roamInfo, 0,
				eCSR_ROAM_RESULT_MGMT_TX_COMPLETE_IND,
				0);
		break;
	case eWNI_SME_TDLS_LINK_ESTABLISH_RSP:
		csr_roam_call_callback(pMac,
				linkEstablishReqRsp->sessionId,
				&roamInfo, 0,
				eCSR_ROAM_TDLS_STATUS_UPDATE,
				eCSR_ROAM_RESULT_LINK_ESTABLISH_REQ_RSP);
		/* remove pending eSmeCommandTdlsLinkEstablish command */
		csr_tdls_remove_sme_cmd(pMac, eSmeCommandTdlsLinkEstablish,
				linkEstablishReqRsp->sessionId);
		break;
	case eWNI_SME_TDLS_SHOULD_DISCOVER:
		qdf_copy_macaddr(&roamInfo.peerMac, &tevent->peermac);
		roamInfo.reasonCode = tevent->peer_reason;
		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
				"%s: eWNI_SME_TDLS_SHOULD_DISCOVER for peer mac: "
				MAC_ADDRESS_STR " peer_reason: %d",
				__func__, MAC_ADDR_ARRAY(tevent->peermac.bytes),
				tevent->peer_reason);
		csr_roam_call_callback(pMac, tevent->sessionId, &roamInfo,
				0, eCSR_ROAM_TDLS_STATUS_UPDATE,
				eCSR_ROAM_RESULT_TDLS_SHOULD_DISCOVER);
		break;
	case eWNI_SME_TDLS_SHOULD_TEARDOWN:
		qdf_copy_macaddr(&roamInfo.peerMac, &tevent->peermac);
		roamInfo.reasonCode = tevent->peer_reason;
		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
				"%s: eWNI_SME_TDLS_SHOULD_TEARDOWN for peer mac: "
				MAC_ADDRESS_STR " peer_reason: %d",
				__func__, MAC_ADDR_ARRAY(tevent->peermac.bytes),
				tevent->peer_reason);
		csr_roam_call_callback(pMac, tevent->sessionId, &roamInfo,
				0, eCSR_ROAM_TDLS_STATUS_UPDATE,
				eCSR_ROAM_RESULT_TDLS_SHOULD_TEARDOWN);
		break;
	case eWNI_SME_TDLS_PEER_DISCONNECTED:
		qdf_copy_macaddr(&roamInfo.peerMac, &tevent->peermac);
		roamInfo.reasonCode = tevent->peer_reason;
		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
				"%s: eWNI_SME_TDLS_PEER_DISCONNECTED for peer mac: "
				MAC_ADDRESS_STR " peer_reason: %d",
				__func__, MAC_ADDR_ARRAY(tevent->peermac.bytes),
				tevent->peer_reason);
		csr_roam_call_callback(pMac, tevent->sessionId, &roamInfo,
				0, eCSR_ROAM_TDLS_STATUS_UPDATE,
				eCSR_ROAM_RESULT_TDLS_SHOULD_PEER_DISCONNECTED);
		break;
	case eWNI_SME_TDLS_CONNECTION_TRACKER_NOTIFICATION:
		qdf_copy_macaddr(&roamInfo.peerMac, &tevent->peermac);
		roamInfo.reasonCode = tevent->peer_reason;
		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_DEBUG,
			  "%s: eWNI_SME_TDLS_CONNECTION_TRACKER_NOTIFICATION for peer mac: "
			  MAC_ADDRESS_STR " peer_reason: %d",
			  __func__, MAC_ADDR_ARRAY(tevent->peermac.bytes),
			  tevent->peer_reason);
		csr_roam_call_callback(pMac, tevent->sessionId, &roamInfo,
				0, eCSR_ROAM_TDLS_STATUS_UPDATE,
			eCSR_ROAM_RESULT_TDLS_CONNECTION_TRACKER_NOTIFICATION);
		break;
	default:
		break;
	}

	return QDF_STATUS_SUCCESS;
}
#endif
