// SPDX-License-Identifier: BSD-2-Clause
/*
 * Copyright (c) 2021 MediaTek Inc.
 */

/*
 * Id: tdls.c#1
 */

/*! \file tdls.c
 *    \brief This file includes IEEE802.11z TDLS support.
 */


/*******************************************************************************
 *                         C O M P I L E R   F L A G S
 *******************************************************************************
 */

/*******************************************************************************
 *                    E X T E R N A L   R E F E R E N C E S
 *******************************************************************************
 */
#include "precomp.h"

#if CFG_SUPPORT_TDLS
#include "tdls.h"
#include "gl_cfg80211.h"
#include "queue.h"

/*******************************************************************************
 *                              C O N S T A N T S
 *******************************************************************************
 */

/*******************************************************************************
 *                             D A T A   T Y P E S
 *******************************************************************************
 */

/*******************************************************************************
 *                            P U B L I C   D A T A
 *******************************************************************************
 */
uint8_t g_arTdlsLink[MAXNUM_TDLS_PEER] = {
		0,
		0,
		0,
		0
	};

/*******************************************************************************
 *                           P R I V A T E   D A T A
 *******************************************************************************
 */
static u_int8_t fgIsPtiTimeoutSkip = FALSE;

/*******************************************************************************
 *                                 M A C R O S
 *******************************************************************************
 */
#define ELEM_ID_LINK_IDENTIFIER_LENGTH 16

#define	TDLS_KEY_TIMEOUT_INTERVAL 43200

#define UNREACH_ABLE 25
#define TDLS_REASON_CODE_UNREACHABLE  25
#define TDLS_REASON_CODE_UNSPECIFIED  26

#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25
#define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26

/*******************************************************************************
 *                   F U N C T I O N   D E C L A R A T I O N S
 *******************************************************************************
 */

/*******************************************************************************
 *                              F U N C T I O N S
 *******************************************************************************
 */

/*----------------------------------------------------------------------------*/
/*!
 * \brief This routine is called to hadel TDLS link oper from nl80211.
 *
 * \param[in] pvAdapter Pointer to the Adapter structure.
 * \param[in]
 * \param[in]
 * \param[in] buf includes RSN IE + FT IE + Lifetimeout IE
 *
 * \retval WLAN_STATUS_SUCCESS
 * \retval WLAN_STATUS_INVALID_LENGTH
 */
/*----------------------------------------------------------------------------*/
uint32_t TdlsexLinkMgt(struct ADAPTER *prAdapter,
		       void *pvSetBuffer, uint32_t u4SetBufferLen,
		       uint32_t *pu4SetInfoLen)
{
	/* from supplicant -- wpa_supplicant_tdls_peer_addset() */

	struct STA_RECORD *prStaRec;
	struct BSS_INFO *prBssInfo;
	struct TDLS_CMD_LINK_MGT *prCmd;

	prCmd = (struct TDLS_CMD_LINK_MGT *) pvSetBuffer;
	prBssInfo = prAdapter->prAisBssInfo;

	DBGLOG(TDLS, INFO, "u4SetBufferLen=%d", u4SetBufferLen);

#if 1
	/* AIS only */
	if (prBssInfo->eCurrentOPMode == OP_MODE_INFRASTRUCTURE) {
		prStaRec = prBssInfo->prStaRecOfAP;
		if (prStaRec == NULL)
			return 0;
	} else {
		return -EINVAL;
	}
#endif

	DBGLOG(TDLS, INFO, "prCmd->ucActionCode=%d, prCmd->ucDialogToken=%d",
		prCmd->ucActionCode, prCmd->ucDialogToken);

	prStaRec = prBssInfo->prStaRecOfAP;

	switch (prCmd->ucActionCode) {

	case TDLS_FRM_ACTION_DISCOVERY_REQ:
		if (prStaRec == NULL)
			return 0;
		if (TdlsDataFrameSend_DISCOVERY_REQ(prAdapter,
					    prStaRec,
					    prCmd->aucPeer,
					    prCmd->ucActionCode,
					    prCmd->ucDialogToken,
					    prCmd->u2StatusCode,
					    (uint8_t *) (prCmd->aucSecBuf),
					    prCmd->u4SecBufLen)
						    != TDLS_STATUS_SUCCESS) {
			return -1;
		}

		break;

	case TDLS_FRM_ACTION_SETUP_REQ:
		if (prStaRec == NULL)
			return 0;
		prStaRec = cnmGetTdlsPeerByAddress(prAdapter,
				prAdapter->prAisBssInfo->ucBssIndex,
				prCmd->aucPeer);
		g_arTdlsLink[prStaRec->ucTdlsIndex] = 0;
		if (TdlsDataFrameSend_SETUP_REQ(prAdapter,
					prStaRec,
					prCmd->aucPeer,
					prCmd->ucActionCode,
					prCmd->ucDialogToken,
					prCmd->u2StatusCode,
					(uint8_t *) (prCmd->aucSecBuf),
					prCmd->u4SecBufLen)
						!= TDLS_STATUS_SUCCESS) {
			return -1;
		}

		break;

	case TDLS_FRM_ACTION_SETUP_RSP:
		/* fix sigma bug 5.2.4.2, 5.2.4.7, we sent Status code decline,
		 * but the sigma recogniezis it as scucess, and it will fail
		 */
		/* if(prCmd->u2StatusCode != 0) */
		if (prBssInfo->fgTdlsIsProhibited)
			return 0;

		if (TdlsDataFrameSend_SETUP_RSP(prAdapter,
					prStaRec,
					prCmd->aucPeer,
					prCmd->ucActionCode,
					prCmd->ucDialogToken,
					prCmd->u2StatusCode,
					(uint8_t *) (prCmd->aucSecBuf),
					prCmd->u4SecBufLen)
						!= TDLS_STATUS_SUCCESS) {
			return -1;
		}

		break;

	case TDLS_FRM_ACTION_DISCOVERY_RSP:
		if (TdlsDataFrameSend_DISCOVERY_RSP(prAdapter,
					    prStaRec,
					    prCmd->aucPeer,
					    prCmd->ucActionCode,
					    prCmd->ucDialogToken,
					    prCmd->u2StatusCode,
					    (uint8_t *) (prCmd->aucSecBuf),
					    prCmd->u4SecBufLen)
						!= TDLS_STATUS_SUCCESS) {
			return -1;
		}

		break;

	case TDLS_FRM_ACTION_CONFIRM:
		if (TdlsDataFrameSend_CONFIRM(prAdapter,
					      prStaRec,
					      prCmd->aucPeer,
					      prCmd->ucActionCode,
					      prCmd->ucDialogToken,
					      prCmd->u2StatusCode,
					      (uint8_t *) (prCmd->aucSecBuf),
					      prCmd->u4SecBufLen)
						!= TDLS_STATUS_SUCCESS) {
			return -1;
		}
		break;

	case TDLS_FRM_ACTION_TEARDOWN:

		prStaRec = cnmGetTdlsPeerByAddress(prAdapter,
				prAdapter->prAisBssInfo->ucBssIndex,
				prCmd->aucPeer);
		if (prCmd->u2StatusCode == TDLS_REASON_CODE_UNREACHABLE)
			g_arTdlsLink[prStaRec->ucTdlsIndex] = 0;

		if (TdlsDataFrameSend_TearDown(prAdapter,
					       prStaRec,
					       prCmd->aucPeer,
					       prCmd->ucActionCode,
					       prCmd->ucDialogToken,
					       prCmd->u2StatusCode,
					       (uint8_t *) (prCmd->aucSecBuf),
					       prCmd->u4SecBufLen)
						!= TDLS_STATUS_SUCCESS) {
			return -1;
		}
		break;

	default:
		return -EINVAL;
	}

	return 0;

}

/*----------------------------------------------------------------------------*/
/*!
 * \brief This routine is called to hadel TDLS link mgt from nl80211.
 *
 * \param[in] pvAdapter Pointer to the Adapter structure.
 * \param[in]
 * \param[in]
 * \param[in] buf includes RSN IE + FT IE + Lifetimeout IE
 *
 * \retval WLAN_STATUS_SUCCESS
 * \retval WLAN_STATUS_INVALID_LENGTH
 */
/*----------------------------------------------------------------------------*/
uint32_t TdlsexLinkOper(struct ADAPTER *prAdapter,
			void *pvSetBuffer, uint32_t u4SetBufferLen,
			uint32_t *pu4SetInfoLen)
{
	/* from supplicant -- wpa_supplicant_tdls_peer_addset() */
	uint16_t i;
	struct STA_RECORD *prStaRec;

	struct TDLS_CMD_LINK_OPER *prCmd;

	prCmd = (struct TDLS_CMD_LINK_OPER *) pvSetBuffer;

	DBGLOG(TDLS, INFO, "prCmd->oper=%d, u4SetBufferLen=%d",
		prCmd->oper, u4SetBufferLen);

	switch (prCmd->oper) {

	case TDLS_ENABLE_LINK:

		for (i = 0; i < MAXNUM_TDLS_PEER; i++) {
			if (!g_arTdlsLink[i]) {
				g_arTdlsLink[i] = 1;
				prStaRec =
				cnmGetTdlsPeerByAddress(prAdapter,
					prAdapter->prAisBssInfo->ucBssIndex,
					prCmd->aucPeerMac);
				prStaRec->ucTdlsIndex = i;
				break;
			}
		}

		break;
	case TDLS_DISABLE_LINK:

		prStaRec = cnmGetTdlsPeerByAddress(prAdapter,
				prAdapter->prAisBssInfo->ucBssIndex,
				prCmd->aucPeerMac);

		g_arTdlsLink[prStaRec->ucTdlsIndex] = 0;
		if (IS_DLS_STA(prStaRec))
			cnmStaRecFree(prAdapter, prStaRec);

		break;
	default:
		return 0;
	}

	return 0;
}

/*----------------------------------------------------------------------------*/
/*!
 * \brief This routine is called to append general IEs.
 *
 * \param[in] pvAdapter Pointer to the Adapter structure.
 * \param[in]
 *
 * \retval append length
 */
/*----------------------------------------------------------------------------*/
uint32_t TdlsFrameGeneralIeAppend(struct ADAPTER *prAdapter,
				  struct STA_RECORD *prStaRec, uint8_t *pPkt)
{
	struct GLUE_INFO *prGlueInfo;
	struct BSS_INFO *prBssInfo;
	struct PM_PROFILE_SETUP_INFO *prPmProfSetupInfo;
	uint32_t u4NonHTPhyType;
	uint16_t u2SupportedRateSet;
	uint8_t aucAllSupportedRates[RATE_NUM_SW] = { 0 };
	uint8_t ucAllSupportedRatesLen;
	uint8_t ucSupRatesLen;
	uint8_t ucExtSupRatesLen;
	uint32_t u4PktLen, u4IeLen;

	/* init */
	prGlueInfo = (struct GLUE_INFO *) prAdapter->prGlueInfo;
	prBssInfo = prAdapter->prAisBssInfo;	/* AIS only */

	prPmProfSetupInfo = &prBssInfo->rPmProfSetupInfo;
	u4PktLen = 0;

	/* 3. Frame Formation - (5) Supported Rates element */
	/* use all sup rate we can support */
	u4NonHTPhyType = prStaRec->ucNonHTBasicPhyType;
	u2SupportedRateSet =
		rNonHTPhyAttributes[u4NonHTPhyType].u2SupportedRateSet;
	rateGetDataRatesFromRateSet(u2SupportedRateSet, 0,
				    aucAllSupportedRates,
				    &ucAllSupportedRatesLen);

	ucSupRatesLen = ((ucAllSupportedRatesLen >
			  ELEM_MAX_LEN_SUP_RATES) ?
			 ELEM_MAX_LEN_SUP_RATES : ucAllSupportedRatesLen);

	ucExtSupRatesLen = ucAllSupportedRatesLen - ucSupRatesLen;

	if (ucSupRatesLen) {
		SUP_RATES_IE(pPkt)->ucId = ELEM_ID_SUP_RATES;
		SUP_RATES_IE(pPkt)->ucLength = ucSupRatesLen;
		kalMemCopy(SUP_RATES_IE(pPkt)->aucSupportedRates,
			   aucAllSupportedRates, ucSupRatesLen);

		u4IeLen = IE_SIZE(pPkt);
		pPkt += u4IeLen;
		u4PktLen += u4IeLen;
	}

	/* 3. Frame Formation - (7) Extended sup rates element */
	if (ucExtSupRatesLen) {

		EXT_SUP_RATES_IE(pPkt)->ucId = ELEM_ID_EXTENDED_SUP_RATES;
		EXT_SUP_RATES_IE(pPkt)->ucLength = ucExtSupRatesLen;

		kalMemCopy(EXT_SUP_RATES_IE(pPkt)->aucExtSupportedRates,
			   &aucAllSupportedRates[ucSupRatesLen],
			   ucExtSupRatesLen);

		u4IeLen = IE_SIZE(pPkt);
		pPkt += u4IeLen;
		u4PktLen += u4IeLen;
	}

	/* 3. Frame Formation - (8) Supported channels element */
	SUPPORTED_CHANNELS_IE(pPkt)->ucId = ELEM_ID_SUP_CHS;
	SUPPORTED_CHANNELS_IE(pPkt)->ucLength = 2;
	SUPPORTED_CHANNELS_IE(pPkt)->ucChannelNum[0] = 1;
	SUPPORTED_CHANNELS_IE(pPkt)->ucChannelNum[1] = 13;

	if (prAdapter->fgEnable5GBand == TRUE) {
		SUPPORTED_CHANNELS_IE(pPkt)->ucLength = 10;
		SUPPORTED_CHANNELS_IE(pPkt)->ucChannelNum[2] = 36;
		SUPPORTED_CHANNELS_IE(pPkt)->ucChannelNum[3] = 4;
		SUPPORTED_CHANNELS_IE(pPkt)->ucChannelNum[4] = 52;
		SUPPORTED_CHANNELS_IE(pPkt)->ucChannelNum[5] = 4;
		SUPPORTED_CHANNELS_IE(pPkt)->ucChannelNum[6] = 149;
		SUPPORTED_CHANNELS_IE(pPkt)->ucChannelNum[7] = 4;
		SUPPORTED_CHANNELS_IE(pPkt)->ucChannelNum[8] = 165;
		SUPPORTED_CHANNELS_IE(pPkt)->ucChannelNum[9] = 1;
	}

	u4IeLen = IE_SIZE(pPkt);
	pPkt += u4IeLen;
	u4PktLen += u4IeLen;

	return u4PktLen;
}

/*!
 * \brief This routine is called to transmit a TDLS data frame.
 *
 * \param[in] pvAdapter Pointer to the Adapter structure.
 * \param[in]
 * \param[in]
 * \param[in] buf includes RSN IE + FT IE + Lifetimeout IE
 *
 * \retval WLAN_STATUS_SUCCESS
 * \retval WLAN_STATUS_INVALID_LENGTH
 */
/*----------------------------------------------------------------------------*/
uint32_t
TdlsDataFrameSend_TearDown(struct ADAPTER *prAdapter,
			   struct STA_RECORD *prStaRec,
			   uint8_t *pPeerMac,
			   uint8_t ucActionCode,
			   uint8_t ucDialogToken, uint16_t u2StatusCode,
			   uint8_t *pAppendIe, uint32_t AppendIeLen)
{

	struct GLUE_INFO *prGlueInfo;
	struct BSS_INFO *prBssInfo;
	struct PM_PROFILE_SETUP_INFO *prPmProfSetupInfo;
	struct sk_buff *prMsduInfo;
	uint8_t *pPkt;
	uint32_t u4PktLen, u4IeLen;
	uint16_t ReasonCode;

	/* allocate/init packet */
	prGlueInfo = (struct GLUE_INFO *) prAdapter->prGlueInfo;
	prBssInfo = prAdapter->prAisBssInfo;	/* AIS only */

	prPmProfSetupInfo = &prBssInfo->rPmProfSetupInfo;
	u4PktLen = 0;

	prMsduInfo = kalPacketAllocWithHeadroom(prGlueInfo, 1600,
						&pPkt);
	if (prMsduInfo == NULL)
		return TDLS_STATUS_RESOURCES;

	prMsduInfo->dev = prGlueInfo->prDevHandler;
	if (prMsduInfo->dev == NULL) {
		kalPacketFree(prGlueInfo, prMsduInfo);
		return TDLS_STATUS_FAIL;
	}

	/* make up frame content */
	/* 1. 802.3 header */
	kalMemCopy(pPkt, pPeerMac, TDLS_FME_MAC_ADDR_LEN);
	pPkt += TDLS_FME_MAC_ADDR_LEN;
	kalMemCopy(pPkt, prBssInfo->aucOwnMacAddr,
		   TDLS_FME_MAC_ADDR_LEN);
	pPkt += TDLS_FME_MAC_ADDR_LEN;
	*(uint16_t *) pPkt = htons(TDLS_FRM_PROT_TYPE);
	pPkt += 2;
	u4PktLen += TDLS_FME_MAC_ADDR_LEN * 2 + 2;

	/* 2. payload type */
	*pPkt = TDLS_FRM_PAYLOAD_TYPE;
	pPkt++;
	u4PktLen++;

	/* 3. Frame Formation - (1) Category */
	*pPkt = TDLS_FRM_CATEGORY;
	pPkt++;
	u4PktLen++;

	/* 3. Frame Formation - (2) Action */
	*pPkt = ucActionCode;
	pPkt++;
	u4PktLen++;

	/* 3. Frame Formation - status code */

	ReasonCode = u2StatusCode;


	kalMemCopy(pPkt, &ReasonCode, 2);
	pPkt = pPkt + 2;
	u4PktLen = u4PktLen + 2;

	if (pAppendIe != NULL) {
		if ((ucActionCode != TDLS_FRM_ACTION_TEARDOWN) ||
		    ((ucActionCode == TDLS_FRM_ACTION_TEARDOWN)
		     && (prStaRec != NULL))) {
			kalMemCopy(pPkt, pAppendIe, AppendIeLen);
			LR_TDLS_FME_FIELD_FILL(AppendIeLen);
		}
	}

	/* 7. Append Supported Operating Classes IE */
	if (ucActionCode != TDLS_FRM_ACTION_TEARDOWN) {
		/* Note: if we do not put the IE, Marvell STA will
		 *		decline our TDLS setup request
		 */
		u4IeLen = rlmDomainSupOperatingClassIeFill(pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);
	}

	/* 3. Frame Formation - (16) Link identifier element */
	TDLS_LINK_IDENTIFIER_IE(pPkt)->ucId =
		ELEM_ID_LINK_IDENTIFIER;
	TDLS_LINK_IDENTIFIER_IE(pPkt)->ucLength = 18;

	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aBSSID,
		   prBssInfo->aucBSSID, 6);
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aInitiator,
		   prBssInfo->aucOwnMacAddr, 6);
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aResponder,
		   pPeerMac, 6);

	u4IeLen = IE_SIZE(pPkt);
	pPkt += u4IeLen;
	u4PktLen += u4IeLen;

	/* 5. Update packet length */
	prMsduInfo->len = u4PktLen;

	/* if(u2StatusCode == UNREACH_ABLE ){ */
	/* g_arTdlsLink[prStaRec->ucTdlsIndex] = FALSE; */
	/* } */


	/* 5. send the data frame */
	wlanHardStartXmit(prMsduInfo, prMsduInfo->dev);

	return TDLS_STATUS_SUCCESS;
}

/*!
 * \brief This routine is called to transmit a TDLS data frame.
 *
 * \param[in] pvAdapter Pointer to the Adapter structure.
 * \param[in]
 * \param[in]
 * \param[in] buf includes RSN IE + FT IE + Lifetimeout IE
 *
 * \retval WLAN_STATUS_SUCCESS
 * \retval WLAN_STATUS_INVALID_LENGTH
 */
/*----------------------------------------------------------------------------*/
uint32_t			/* TDLS_STATUS */
TdlsDataFrameSend_SETUP_REQ(struct ADAPTER *prAdapter,
			    struct STA_RECORD *prStaRec,
			    uint8_t *pPeerMac,
			    uint8_t ucActionCode,
			    uint8_t ucDialogToken, uint16_t u2StatusCode,
			    uint8_t *pAppendIe, uint32_t AppendIeLen)
{

	struct GLUE_INFO *prGlueInfo;
	struct BSS_INFO *prBssInfo;
	struct PM_PROFILE_SETUP_INFO *prPmProfSetupInfo;
	struct sk_buff *prMsduInfo;
	uint8_t *pPkt;
	uint32_t u4PktLen, u4IeLen;
	uint16_t u2CapInfo;

	/* allocate/init packet */
	prGlueInfo = (struct GLUE_INFO *) prAdapter->prGlueInfo;
	prBssInfo = prAdapter->prAisBssInfo;

	prPmProfSetupInfo = &prBssInfo->rPmProfSetupInfo;
	u4PktLen = 0;

	if (prStaRec == NULL)
		return TDLS_STATUS_FAIL;

	prMsduInfo = kalPacketAllocWithHeadroom(prGlueInfo, 512, &pPkt);
	if (prMsduInfo == NULL)
		return TDLS_STATUS_RESOURCES;

	prMsduInfo->dev = prGlueInfo->prDevHandler;
	if (prMsduInfo->dev == NULL) {
		kalPacketFree(prGlueInfo, prMsduInfo);
		return TDLS_STATUS_FAIL;
	}

	/* make up frame content */
	/* 1. 802.3 header */
	kalMemCopy(pPkt, pPeerMac, TDLS_FME_MAC_ADDR_LEN);
	LR_TDLS_FME_FIELD_FILL(TDLS_FME_MAC_ADDR_LEN);
	kalMemCopy(pPkt, prBssInfo->aucOwnMacAddr, TDLS_FME_MAC_ADDR_LEN);
	LR_TDLS_FME_FIELD_FILL(TDLS_FME_MAC_ADDR_LEN);
	*(uint16_t *) pPkt = htons(TDLS_FRM_PROT_TYPE);
	LR_TDLS_FME_FIELD_FILL(2);

	/* 2. payload type */
	*pPkt = TDLS_FRM_PAYLOAD_TYPE;
	LR_TDLS_FME_FIELD_FILL(1);

	/*
	 * 3.1 Category
	 * 3.2 Action
	 * 3.3 Dialog Token
	 * 3.4 Capability
	 * 3.5 Supported rates
	 * 3.6 Country
	 * 3.7 Extended supported rates
	 * 3.8 Supported Channels (optional)
	 * 3.9 RSNIE (optional)
	 * 3.10 Extended Capabilities
	 * 3.11 QoS Capability
	 * 3.12 FTIE (optional)
	 * 3.13 Timeout Interval (optional)
	 * 3.14 Supported Regulatory Classes (optional)
	 * 3.15 HT Capabilities
	 * 3.16 20/40 BSS Coexistence
	 * 3.17 Link Identifier
	 */

	/* 3.1 Category */
	*pPkt = TDLS_FRM_CATEGORY;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.2 Action */
	*pPkt = ucActionCode;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.3 Dialog Token */
	*pPkt = ucDialogToken;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.4 Capability */
	u2CapInfo = assocBuildCapabilityInfo(prAdapter, prStaRec);
	WLAN_SET_FIELD_16(pPkt, u2CapInfo);
	LR_TDLS_FME_FIELD_FILL(2);

	/*
	 * 3.5 Supported rates
	 * 3.7 Extended supported rates
	 * 3.8 Supported Channels (optional)
	 */
	u4IeLen = TdlsFrameGeneralIeAppend(prAdapter, prStaRec, pPkt);
	LR_TDLS_FME_FIELD_FILL(u4IeLen);

	/* 3.6 Country */
	/* 3.14 Supported Regulatory Classes (optional) */
	/* Note: if we do not put the IE, Marvell STA will decline
	 * our TDLS setup request
	 */
	u4IeLen = rlmDomainSupOperatingClassIeFill(pPkt);
	LR_TDLS_FME_FIELD_FILL(u4IeLen);

	/* 3.10 Extended Capabilities */
	EXT_CAP_IE(pPkt)->ucId = ELEM_ID_EXTENDED_CAP;
	EXT_CAP_IE(pPkt)->ucLength = 5;

	EXT_CAP_IE(pPkt)->aucCapabilities[0] = 0x00; /* bit0 ~ bit7 */
	EXT_CAP_IE(pPkt)->aucCapabilities[1] = 0x00; /* bit8 ~ bit15 */
	EXT_CAP_IE(pPkt)->aucCapabilities[2] = 0x00; /* bit16 ~ bit23 */
	EXT_CAP_IE(pPkt)->aucCapabilities[3] = 0x00; /* bit24 ~ bit31 */
	EXT_CAP_IE(pPkt)->aucCapabilities[4] = 0x00; /* bit32 ~ bit39 */

	EXT_CAP_IE(pPkt)->aucCapabilities[3] |= BIT((28 - 24));
	EXT_CAP_IE(pPkt)->aucCapabilities[3] |= BIT((30 - 24));
	EXT_CAP_IE(pPkt)->aucCapabilities[4] |= BIT((37 - 32));

	u4IeLen = IE_SIZE(pPkt);
	LR_TDLS_FME_FIELD_FILL(u4IeLen);

	/* Append extra IEs */
	/*
	 * 3.9 RSNIE (optional)
	 * 3.12 FTIE (optional)
	 * 3.13 Timeout Interval (optional)
	 */
	if (pAppendIe != NULL) {
		kalMemCopy(pPkt, pAppendIe, AppendIeLen);
		LR_TDLS_FME_FIELD_FILL(AppendIeLen);
	}

	/* 3.11 QoS Capability */
	/* HT WMM IE append */
	if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucQoS)) {
		u4IeLen = mqmGenerateWmmInfoIEByStaRec(prAdapter, prBssInfo,
							prStaRec, pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);
		u4IeLen = mqmGenerateWmmParamIEByParam(prAdapter, prBssInfo,
							pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);
	}

	/* 3.15 HT Capabilities */
	if ((prAdapter->rWifiVar.ucAvailablePhyTypeSet &
		PHY_TYPE_SET_802_11N)) {
		u4IeLen = rlmFillHtCapIEByAdapter(prAdapter, prBssInfo, pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);
	}
#if CFG_SUPPORT_802_11AC
	if (prAdapter->rWifiVar.ucAvailablePhyTypeSet &
		PHY_TYPE_SET_802_11AC) {
		u4IeLen = rlmFillVhtCapIEByAdapter(prAdapter, prBssInfo, pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);
	}
#endif

	/* 3.16 20/40 BSS Coexistence */
	BSS_20_40_COEXIST_IE(pPkt)->ucId = ELEM_ID_20_40_BSS_COEXISTENCE;
	BSS_20_40_COEXIST_IE(pPkt)->ucLength = 1;
	BSS_20_40_COEXIST_IE(pPkt)->ucData = 0x01;
	LR_TDLS_FME_FIELD_FILL(3);

	/* 3.17 Link Identifier */
	TDLS_LINK_IDENTIFIER_IE(pPkt)->ucId = ELEM_ID_LINK_IDENTIFIER;
	TDLS_LINK_IDENTIFIER_IE(pPkt)->ucLength = 18;
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aBSSID,
		prBssInfo->aucBSSID, 6);
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aInitiator,
		prBssInfo->aucOwnMacAddr, 6);
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aResponder,
		pPeerMac, 6);

	u4IeLen = IE_SIZE(pPkt);
	LR_TDLS_FME_FIELD_FILL(u4IeLen);

	/* 4. Update packet length */
	prMsduInfo->len = u4PktLen;

	DBGLOG(TDLS, INFO, "wlanHardStartXmit, u4PktLen=%d", u4PktLen);

	/* 5. send the data frame */
	wlanHardStartXmit(prMsduInfo, prMsduInfo->dev);

	return TDLS_STATUS_SUCCESS;
}

uint32_t
TdlsDataFrameSend_SETUP_RSP(struct ADAPTER *prAdapter,
			    struct STA_RECORD *prStaRec,
			    uint8_t *pPeerMac,
			    uint8_t ucActionCode,
			    uint8_t ucDialogToken, uint16_t u2StatusCode,
			    uint8_t *pAppendIe, uint32_t AppendIeLen)
{
	struct GLUE_INFO *prGlueInfo;
	struct BSS_INFO *prBssInfo;
	struct PM_PROFILE_SETUP_INFO *prPmProfSetupInfo;
	struct sk_buff *prMsduInfo;
	uint8_t *pPkt;
	uint32_t u4PktLen, u4IeLen;
	uint16_t u2CapInfo;

	/* allocate/init packet */
	prGlueInfo = (struct GLUE_INFO *) prAdapter->prGlueInfo;
	prBssInfo = prAdapter->prAisBssInfo;
	prPmProfSetupInfo = &prBssInfo->rPmProfSetupInfo;
	u4PktLen = 0;

	if (prStaRec == NULL)
		return TDLS_STATUS_FAIL;

	prMsduInfo = kalPacketAllocWithHeadroom(prGlueInfo, 512, &pPkt);
	if (prMsduInfo == NULL)
		return TDLS_STATUS_RESOURCES;

	prMsduInfo->dev = prGlueInfo->prDevHandler;
	if (prMsduInfo->dev == NULL) {
		kalPacketFree(prGlueInfo, prMsduInfo);
		return TDLS_STATUS_FAIL;
	}

	/* make up frame content */
	/* 1. 802.3 header */
	kalMemCopy(pPkt, pPeerMac, TDLS_FME_MAC_ADDR_LEN);
	LR_TDLS_FME_FIELD_FILL(TDLS_FME_MAC_ADDR_LEN);
	kalMemCopy(pPkt, prBssInfo->aucOwnMacAddr, TDLS_FME_MAC_ADDR_LEN);
	LR_TDLS_FME_FIELD_FILL(TDLS_FME_MAC_ADDR_LEN);
	*(uint16_t *) pPkt = htons(TDLS_FRM_PROT_TYPE);
	LR_TDLS_FME_FIELD_FILL(2);

	/* 2. payload type */
	*pPkt = TDLS_FRM_PAYLOAD_TYPE;
	LR_TDLS_FME_FIELD_FILL(1);

	/*
	 * 3.1 Category
	 * 3.2 Action
	 * 3.3 Status Code
	 * 3.4 Dialog Token
	 *** Below IE should be only included for Status Code=0 (Successful)
	 * 3.5 Capability
	 * 3.6 Supported rates
	 * 3.7 Country
	 * 3.8 Extended supported rates
	 * 3.9 Supported Channels (optional)
	 * 3.10 RSNIE (optional)
	 * 3.11 Extended Capabilities
	 * 3.12 QoS Capability
	 * 3.13 FTIE (optional)
	 * 3.14 Timeout Interval (optional)
	 * 3.15 Supported Regulatory Classes (optional)
	 * 3.16 HT Capabilities
	 * 3.17 20/40 BSS Coexistence
	 * 3.18 Link Identifier
	 */

	/* 3.1 Category */
	*pPkt = TDLS_FRM_CATEGORY;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.2 Action */
	*pPkt = ucActionCode;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.3 Status Code */
	kalMemCopy(pPkt, &u2StatusCode, 2);
	LR_TDLS_FME_FIELD_FILL(2);

	/* 3.4 Dialog Token */
	*pPkt = ucDialogToken;
	LR_TDLS_FME_FIELD_FILL(1);

	if (u2StatusCode == 0) {
		/* 3.5 Capability */
		u2CapInfo = assocBuildCapabilityInfo(prAdapter, prStaRec);
		WLAN_SET_FIELD_16(pPkt, u2CapInfo);
		LR_TDLS_FME_FIELD_FILL(2);

		/*
		 * 3.6 Supported rates
		 * 3.8 Extended supported rates
		 * 3.9 Supported Channels (optional)
		 */
		u4IeLen = TdlsFrameGeneralIeAppend(prAdapter, prStaRec, pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);

		/* 3.7 Country */
		/* 3.15 Supported Regulatory Classes (optional) */
		/* Note: if we do not put the IE, Marvell STA will decline
		 * our TDLS setup request
		 */
		u4IeLen = rlmDomainSupOperatingClassIeFill(pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);

		/* 3.11 Extended Capabilities */
		EXT_CAP_IE(pPkt)->ucId = ELEM_ID_EXTENDED_CAP;
		EXT_CAP_IE(pPkt)->ucLength = 5;

		EXT_CAP_IE(pPkt)->aucCapabilities[0] = 0x00; /* bit0 ~ bit7 */
		EXT_CAP_IE(pPkt)->aucCapabilities[1] = 0x00; /* bit8 ~ bit15 */
		EXT_CAP_IE(pPkt)->aucCapabilities[2] = 0x00; /* bit16 ~ bit23 */
		EXT_CAP_IE(pPkt)->aucCapabilities[3] = 0x00; /* bit24 ~ bit31 */
		EXT_CAP_IE(pPkt)->aucCapabilities[4] = 0x00; /* bit32 ~ bit39 */

		EXT_CAP_IE(pPkt)->aucCapabilities[3] |= BIT((28 - 24));
		EXT_CAP_IE(pPkt)->aucCapabilities[3] |= BIT((30 - 24));
		EXT_CAP_IE(pPkt)->aucCapabilities[4] |= BIT((37 - 32));

		u4IeLen = IE_SIZE(pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);

		/* 3.12 QoS Capability */
		if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucQoS)) {
			/* Add WMM IE *//* try to reuse p2p path */
			u4IeLen = mqmGenerateWmmInfoIEByStaRec(prAdapter,
						prBssInfo, prStaRec, pPkt);
			LR_TDLS_FME_FIELD_FILL(u4IeLen);

			u4IeLen = mqmGenerateWmmParamIEByParam(prAdapter,
						prBssInfo, pPkt);
			LR_TDLS_FME_FIELD_FILL(u4IeLen);
		}

		/* 3.16 HT Capabilities */
		if (prAdapter->rWifiVar.ucAvailablePhyTypeSet &
		     PHY_TYPE_SET_802_11N) {
			u4IeLen = rlmFillHtCapIEByAdapter(prAdapter, prBssInfo,
							  pPkt);
			LR_TDLS_FME_FIELD_FILL(u4IeLen);
		}
#if CFG_SUPPORT_802_11AC
		if (prAdapter->rWifiVar.ucAvailablePhyTypeSet &
		    PHY_TYPE_SET_802_11AC) {
			u4IeLen = rlmFillVhtCapIEByAdapter(prAdapter, prBssInfo,
							   pPkt);
			LR_TDLS_FME_FIELD_FILL(u4IeLen);
		}
#endif

		/* 3.17 20/40 BSS Coexistence */
		BSS_20_40_COEXIST_IE(pPkt)->ucId =
			ELEM_ID_20_40_BSS_COEXISTENCE;
		BSS_20_40_COEXIST_IE(pPkt)->ucLength = 1;
		BSS_20_40_COEXIST_IE(pPkt)->ucData = 0x01;
		LR_TDLS_FME_FIELD_FILL(3);

		/* 3.18 Link Identifier */
		TDLS_LINK_IDENTIFIER_IE(pPkt)->ucId = ELEM_ID_LINK_IDENTIFIER;
		TDLS_LINK_IDENTIFIER_IE(pPkt)->ucLength = 18;

		kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aBSSID,
			   prBssInfo->aucBSSID, 6);
		kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aInitiator,
			   pPeerMac, 6);
		kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aResponder,
			   prBssInfo->aucOwnMacAddr, 6);

		u4IeLen = IE_SIZE(pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);

		/* Append extra IEs */
		/*
		 * 3.10 RSNIE (optional)
		 * 3.13 FTIE (optional)
		 * 3.14 Timeout Interval (optional)
		 */
		if (pAppendIe != NULL) {
			kalMemCopy(pPkt, pAppendIe, AppendIeLen);
			LR_TDLS_FME_FIELD_FILL(AppendIeLen);
		}
	}

	/* 4. Update packet length */
	prMsduInfo->len = u4PktLen;

	/* 5. send the data frame */
	wlanHardStartXmit(prMsduInfo, prMsduInfo->dev);

	return TDLS_STATUS_SUCCESS;
}

uint32_t
TdlsDataFrameSend_CONFIRM(struct ADAPTER *prAdapter,
			  struct STA_RECORD *prStaRec,
			  uint8_t *pPeerMac,
			  uint8_t ucActionCode,
			  uint8_t ucDialogToken, uint16_t u2StatusCode,
			  uint8_t *pAppendIe, uint32_t AppendIeLen)
{

	struct GLUE_INFO *prGlueInfo;
	struct BSS_INFO *prBssInfo;
	struct PM_PROFILE_SETUP_INFO *prPmProfSetupInfo;
	struct sk_buff *prMsduInfo;
	uint8_t *pPkt;
	uint32_t u4PktLen, u4IeLen;

	/* allocate/init packet */
	prGlueInfo = (struct GLUE_INFO *) prAdapter->prGlueInfo;
	prBssInfo = prAdapter->prAisBssInfo;

	prPmProfSetupInfo = &prBssInfo->rPmProfSetupInfo;
	u4PktLen = 0;

	prMsduInfo = kalPacketAllocWithHeadroom(prGlueInfo, 512, &pPkt);
	if (prMsduInfo == NULL)
		return TDLS_STATUS_RESOURCES;

	prMsduInfo->dev = prGlueInfo->prDevHandler;
	if (prMsduInfo->dev == NULL) {
		kalPacketFree(prGlueInfo, prMsduInfo);
		return TDLS_STATUS_FAIL;
	}

	/* make up frame content */
	/* 1. 802.3 header */
	kalMemCopy(pPkt, pPeerMac, TDLS_FME_MAC_ADDR_LEN);
	LR_TDLS_FME_FIELD_FILL(TDLS_FME_MAC_ADDR_LEN);
	kalMemCopy(pPkt, prBssInfo->aucOwnMacAddr, TDLS_FME_MAC_ADDR_LEN);
	LR_TDLS_FME_FIELD_FILL(TDLS_FME_MAC_ADDR_LEN);
	*(uint16_t *) pPkt = htons(TDLS_FRM_PROT_TYPE);
	LR_TDLS_FME_FIELD_FILL(2);

	/* 2. payload type */
	*pPkt = TDLS_FRM_PAYLOAD_TYPE;
	LR_TDLS_FME_FIELD_FILL(1);

	/*
	 * 3.1 Category
	 * 3.2 Action
	 * 3.3 Status Code
	 * 3.4 Dialog Token
	 *** 3.5 - 3.9 should be only included for Status Code=0 (Successful)
	 * 3.5 RSNIE (optional)
	 * 3.6 EDCA Parameter Set
	 * 3.7 FTIE (optional)
	 * 3.8 Timeout Interval (optional)
	 * 3.9 HT Operation (optional)
	 * 3.10 Link Identifier
	 */

	/* 3.1 Category */
	*pPkt = TDLS_FRM_CATEGORY;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.2 Action */
	*pPkt = ucActionCode;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.3 Status Code */
	kalMemCopy(pPkt, &u2StatusCode, 2);
	LR_TDLS_FME_FIELD_FILL(2);

	/* 3.4 Dialog Token */
	*pPkt = ucDialogToken;
	LR_TDLS_FME_FIELD_FILL(1);

	if (u2StatusCode == 0) {
		/*
		 * 3.5 RSNIE (optional)
		 * 3.7 FTIE (optional)
		 * 3.8 Timeout Interval (optional)
		 */
		if (pAppendIe) {
			kalMemCopy(pPkt, pAppendIe, AppendIeLen);
			LR_TDLS_FME_FIELD_FILL(AppendIeLen);
		}

		/* 3.6 EDCA Parameter Set */
		/* HT WMM IE append */
		if (IS_FEATURE_ENABLED(prAdapter->rWifiVar.ucQoS)) {
			u4IeLen = mqmGenerateWmmInfoIEByStaRec(prAdapter,
					prBssInfo, prStaRec, pPkt);
			LR_TDLS_FME_FIELD_FILL(u4IeLen);
			u4IeLen = mqmGenerateWmmParamIEByParam(prAdapter,
					prBssInfo, pPkt);
			LR_TDLS_FME_FIELD_FILL(u4IeLen);
		}
	}

	/* 3.10 Link Identifier */
	TDLS_LINK_IDENTIFIER_IE(pPkt)->ucId = ELEM_ID_LINK_IDENTIFIER;
	TDLS_LINK_IDENTIFIER_IE(pPkt)->ucLength = 18;
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aBSSID,
		prBssInfo->aucBSSID, 6);
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aInitiator,
		prBssInfo->aucOwnMacAddr, 6);
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aResponder,
		pPeerMac, 6);

	u4IeLen = IE_SIZE(pPkt);
	LR_TDLS_FME_FIELD_FILL(u4IeLen);

	/* 4. Update packet length */
	prMsduInfo->len = u4PktLen;

	/* 5. send the data frame */
	wlanHardStartXmit(prMsduInfo, prMsduInfo->dev);

	return TDLS_STATUS_SUCCESS;
}

/*
 * \brief This routine is called to transmit a TDLS data frame.
 *
 * \param[in] pvAdapter Pointer to the Adapter structure.
 * \param[in]
 * \param[in]
 * \param[in] buf includes RSN IE + FT IE + Lifetimeout IE
 *
 * \retval WLAN_STATUS_SUCCESS
 * \retval WLAN_STATUS_INVALID_LENGTH
 */
/*----------------------------------------------------------------------------*/
uint32_t			/* TDLS_STATUS */
TdlsDataFrameSend_DISCOVERY_REQ(struct ADAPTER *prAdapter,
				struct STA_RECORD *prStaRec,
				uint8_t *pPeerMac,
				uint8_t ucActionCode,
				uint8_t ucDialogToken, uint16_t u2StatusCode,
				uint8_t *pAppendIe, uint32_t AppendIeLen)
{
	struct GLUE_INFO *prGlueInfo;
	struct BSS_INFO *prBssInfo;
	struct PM_PROFILE_SETUP_INFO *prPmProfSetupInfo;
	struct sk_buff *prMsduInfo;
	struct MSDU_INFO *prMsduInfoMgmt;
	uint8_t *pPkt, *pucInitiator, *pucResponder;
	uint32_t u4PktLen, u4IeLen;

	prGlueInfo = (struct GLUE_INFO *) prAdapter->prGlueInfo;

	if (prStaRec != NULL)
		prBssInfo = prAdapter->prAisBssInfo;
	else
		return TDLS_STATUS_FAIL;

	/* allocate/init packet */
	prPmProfSetupInfo = &prBssInfo->rPmProfSetupInfo;
	u4PktLen = 0;
	prMsduInfo = NULL;
	prMsduInfoMgmt = NULL;

	/* make up frame content */
	prMsduInfo = kalPacketAllocWithHeadroom(prGlueInfo, 512, &pPkt);
	if (prMsduInfo == NULL)
		return TDLS_STATUS_RESOURCES;

	prMsduInfo->dev = prGlueInfo->prDevHandler;
	if (prMsduInfo->dev == NULL) {
		kalPacketFree(prGlueInfo, prMsduInfo);
		return TDLS_STATUS_FAIL;
	}

	/* 1. 802.3 header */
	kalMemCopy(pPkt, pPeerMac, TDLS_FME_MAC_ADDR_LEN);
	LR_TDLS_FME_FIELD_FILL(TDLS_FME_MAC_ADDR_LEN);
	kalMemCopy(pPkt, prBssInfo->aucOwnMacAddr, TDLS_FME_MAC_ADDR_LEN);
	LR_TDLS_FME_FIELD_FILL(TDLS_FME_MAC_ADDR_LEN);
	*(uint16_t *) pPkt = htons(TDLS_FRM_PROT_TYPE);
	LR_TDLS_FME_FIELD_FILL(2);

	/* 2. payload type */
	*pPkt = TDLS_FRM_PAYLOAD_TYPE;
	LR_TDLS_FME_FIELD_FILL(1);

	/*
	 * 3.1 Category
	 * 3.2 Action
	 * 3.3 Dialog Token
	 * 3.4 Link Identifier
	 */

	/* 3.1 Category */
	*pPkt = TDLS_FRM_CATEGORY;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.2 Action */
	*pPkt = ucActionCode;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.3 Dialog Token */
	*pPkt = ucDialogToken;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.4 Link Identifier */
	TDLS_LINK_IDENTIFIER_IE(pPkt)->ucId = ELEM_ID_LINK_IDENTIFIER;
	TDLS_LINK_IDENTIFIER_IE(pPkt)->ucLength = 18;
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aBSSID,
			prBssInfo->aucBSSID, 6);
	pucInitiator = prBssInfo->aucOwnMacAddr;
	pucResponder = pPeerMac;
	prStaRec->flgTdlsIsInitiator = TRUE;
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aInitiator, pucInitiator, 6);
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aResponder, pucResponder, 6);
	u4IeLen = IE_SIZE(pPkt);
	LR_TDLS_FME_FIELD_FILL(u4IeLen);

	/* 4. Update packet length */
	prMsduInfo->len = u4PktLen;

	/* 5. send the data frame */
	wlanHardStartXmit(prMsduInfo, prMsduInfo->dev);

	return TDLS_STATUS_SUCCESS;
}

uint32_t
TdlsDataFrameSend_DISCOVERY_RSP(struct ADAPTER *prAdapter,
				struct STA_RECORD *prStaRec,
				uint8_t *pPeerMac,
				uint8_t ucActionCode,
				uint8_t ucDialogToken, uint16_t u2StatusCode,
				uint8_t *pAppendIe, uint32_t AppendIeLen)
{
	struct GLUE_INFO *prGlueInfo;
	struct BSS_INFO *prBssInfo;
	struct PM_PROFILE_SETUP_INFO *prPmProfSetupInfo;
	struct MSDU_INFO *prMsduInfoMgmt;
	uint8_t *pPkt, *pucInitiator, *pucResponder;
	uint32_t u4PktLen, u4IeLen;
	uint16_t u2CapInfo;
	struct WLAN_MAC_HEADER *prHdr;

	prGlueInfo = (struct GLUE_INFO *) prAdapter->prGlueInfo;

	/* sanity check */
	if (prStaRec != NULL)
		prBssInfo = prAdapter->prAisBssInfo;
	else
		return TDLS_STATUS_FAIL;

	/* allocate/init packet */
	prPmProfSetupInfo = &prBssInfo->rPmProfSetupInfo;
	u4PktLen = 0;
	prMsduInfoMgmt = NULL;

	/* make up frame content */
	prMsduInfoMgmt = (struct MSDU_INFO *) cnmMgtPktAlloc(prAdapter,
					PUBLIC_ACTION_MAX_LEN);
	if (prMsduInfoMgmt == NULL)
		return TDLS_STATUS_RESOURCES;

	pPkt = (uint8_t *) prMsduInfoMgmt->prPacket;
	prHdr = (struct WLAN_MAC_HEADER *) pPkt;

	/* 1. 802.11 header */
	prHdr->u2FrameCtrl = MAC_FRAME_ACTION;
	prHdr->u2DurationID = 0;
	kalMemCopy(prHdr->aucAddr1, pPeerMac, TDLS_FME_MAC_ADDR_LEN);
	kalMemCopy(prHdr->aucAddr2, prBssInfo->aucOwnMacAddr,
		TDLS_FME_MAC_ADDR_LEN);
	kalMemCopy(prHdr->aucAddr3, prBssInfo->aucBSSID, TDLS_FME_MAC_ADDR_LEN);
	prHdr->u2SeqCtrl = 0;
	LR_TDLS_FME_FIELD_FILL(sizeof(struct WLAN_MAC_HEADER));


	/*
	 * 3.1 Category
	 * 3.2 Action
	 * 3.3 Dialog Token
	 * 3.4 Capability
	 * 3.5 Supported rates
	 * 3.6 Extended supported rates
	 * 3.7 Supported Channels (optional)
	 * 3.8 RSNIE
	 * 3.9 Extended Capabilities
	 * 3.10 FTIE
	 * 3.11 Timeout Interval (optional)
	 * 3.12 Supported Regulatory Classes (optional)
	 * 3.13 HT Capabilities
	 * 3.14 20/40 BSS Coexistence
	 * 3.15 Link Identifier
	 */

	/* 3.1 Category */
	*pPkt = CATEGORY_PUBLIC_ACTION;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.2 Action */
	*pPkt = ucActionCode;
	LR_TDLS_FME_FIELD_FILL(1);

	/* 3.3 Dialog Token */
	*pPkt = ucDialogToken;
	LR_TDLS_FME_FIELD_FILL(1);


	/* 3.4 Capability */
	u2CapInfo = assocBuildCapabilityInfo(prAdapter, prStaRec);
	WLAN_SET_FIELD_16(pPkt, u2CapInfo);
	LR_TDLS_FME_FIELD_FILL(2);

	/*
	 * 3.5 Supported rates
	 * 3.6 Extended supported rates
	 * 3.7 Supported Channels (optional)
	 */
	u4IeLen = TdlsFrameGeneralIeAppend(prAdapter, prStaRec, pPkt);
	LR_TDLS_FME_FIELD_FILL(u4IeLen);

	/* 3.9 Extended Capabilities */
	EXT_CAP_IE(pPkt)->ucId = ELEM_ID_EXTENDED_CAP;
	EXT_CAP_IE(pPkt)->ucLength = 5;

	EXT_CAP_IE(pPkt)->aucCapabilities[0] = 0x00; /* bit0 ~ bit7 */
	EXT_CAP_IE(pPkt)->aucCapabilities[1] = 0x00; /* bit8 ~ bit15 */
	EXT_CAP_IE(pPkt)->aucCapabilities[2] = 0x00; /* bit16 ~ bit23 */
	EXT_CAP_IE(pPkt)->aucCapabilities[3] = 0x00; /* bit24 ~ bit31 */
	EXT_CAP_IE(pPkt)->aucCapabilities[4] = 0x00; /* bit32 ~ bit39 */

	/* bit 28 TDLS_EX_CAP_PEER_UAPSD */
	EXT_CAP_IE(pPkt)->aucCapabilities[3] |= BIT((28 - 24));
	/* bit 30 TDLS_EX_CAP_CHAN_SWITCH */
	EXT_CAP_IE(pPkt)->aucCapabilities[3] |= BIT((30 - 24));
	/* bit 37 TDLS_EX_CAP_TDLS */
	EXT_CAP_IE(pPkt)->aucCapabilities[4] |= BIT((37 - 32));

	u4IeLen = IE_SIZE(pPkt);
	LR_TDLS_FME_FIELD_FILL(u4IeLen);

	/*
	 * 3.8 RSNIE
	 * 3.10 FTIE
	 * 3.11 Timeout Interval (optional)
	 */
	if (pAppendIe != NULL) {
		kalMemCopy(pPkt, pAppendIe, AppendIeLen);
		LR_TDLS_FME_FIELD_FILL(AppendIeLen);
	}

	/* 3.12 Supported Regulatory Classes (optional) */
	/* Note: if we do not put the IE, Marvell STA will
	 * decline our TDLS setup request
	 */
	u4IeLen = rlmDomainSupOperatingClassIeFill(pPkt);
	LR_TDLS_FME_FIELD_FILL(u4IeLen);

	/* 3.13 HT Capabilities */
	if (prAdapter->rWifiVar.ucAvailablePhyTypeSet & PHY_TYPE_SET_802_11N) {
		u4IeLen = rlmFillHtCapIEByAdapter(prAdapter, prBssInfo, pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);
	}
#if CFG_SUPPORT_802_11AC
	if (prAdapter->rWifiVar.ucAvailablePhyTypeSet & PHY_TYPE_SET_802_11AC) {
		u4IeLen = rlmFillVhtCapIEByAdapter(prAdapter, prBssInfo, pPkt);
		LR_TDLS_FME_FIELD_FILL(u4IeLen);
	}
#endif

	/* 3.14 20/40 BSS Coexistence */
	BSS_20_40_COEXIST_IE(pPkt)->ucId = ELEM_ID_20_40_BSS_COEXISTENCE;
	BSS_20_40_COEXIST_IE(pPkt)->ucLength = 1;
	BSS_20_40_COEXIST_IE(pPkt)->ucData = 0x01;
	LR_TDLS_FME_FIELD_FILL(3);

	/* 3.15 Link Identifier */
	TDLS_LINK_IDENTIFIER_IE(pPkt)->ucId = ELEM_ID_LINK_IDENTIFIER;
	TDLS_LINK_IDENTIFIER_IE(pPkt)->ucLength = 18;
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aBSSID,
		   prBssInfo->aucBSSID, 6);
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aInitiator,
		   pPeerMac, 6);
	kalMemCopy(TDLS_LINK_IDENTIFIER_IE(pPkt)->aResponder,
		   prBssInfo->aucOwnMacAddr, 6);

	u4IeLen = IE_SIZE(pPkt);
	LR_TDLS_FME_FIELD_FILL(u4IeLen);
	pucInitiator = pPeerMac;
	pucResponder = prBssInfo->aucOwnMacAddr;
	if (prStaRec != NULL)
		prStaRec->flgTdlsIsInitiator = FALSE;

	if (prMsduInfoMgmt != NULL) {
		prMsduInfoMgmt->ucPacketType = TX_PACKET_TYPE_MGMT;
		prMsduInfoMgmt->ucStaRecIndex =
		prBssInfo->prStaRecOfAP->ucIndex;
		prMsduInfoMgmt->ucBssIndex = prBssInfo->ucBssIndex;
		prMsduInfoMgmt->ucMacHeaderLength =
				WLAN_MAC_MGMT_HEADER_LEN;
		prMsduInfoMgmt->fgIs802_1x = FALSE;
		prMsduInfoMgmt->fgIs802_11 = TRUE;
		prMsduInfoMgmt->u2FrameLength = u4PktLen;
		prMsduInfoMgmt->ucTxSeqNum = nicIncreaseTxSeqNum(prAdapter);
		prMsduInfoMgmt->pfTxDoneHandler = NULL;

		/* Send them to HW queue */
		nicTxEnqueueMsdu(prAdapter, prMsduInfoMgmt);
	}

	return TDLS_STATUS_SUCCESS;
}

/*----------------------------------------------------------------------------*/
/*! \brief  This routine is called to send a command to TDLS module.
 *
 * \param[in] prGlueInfo	Pointer to the Adapter structure
 * \param[in] prInBuf		A pointer to the command string buffer
 * \param[in] u4InBufLen	The length of the buffer
 * \param[out] None
 *
 * \retval None
 */
/*----------------------------------------------------------------------------*/
void TdlsexEventHandle(struct GLUE_INFO *prGlueInfo,
		       uint8_t *prInBuf, uint32_t u4InBufLen)
{
	uint32_t u4EventId;

	DBGLOG(TDLS, INFO, "TdlsexEventHandle\n");

	/* sanity check */
	if ((prGlueInfo == NULL) || (prInBuf == NULL))
		return;		/* shall not be here */

	/* handle */
	u4EventId = *(uint32_t *) prInBuf;
	u4InBufLen -= 4;

	switch (u4EventId) {
	case TDLS_HOST_EVENT_TEAR_DOWN:
		DBGLOG(TDLS, INFO, "TDLS_HOST_EVENT_TEAR_DOWN\n");
		TdlsEventTearDown(prGlueInfo, prInBuf + 4, u4InBufLen);
		break;

	case TDLS_HOST_EVENT_TX_DONE:

		break;
	}
}

/*----------------------------------------------------------------------------*/
/*! \brief  This routine is called to do tear down.
 *
 * \param[in] prGlueInfo	Pointer to the Adapter structure
 * \param[in] prInBuf		A pointer to the command string buffer,
 *					from u4EventSubId
 * \param[in] u4InBufLen	The length of the buffer
 * \param[out] None
 *
 * \retval None
 *
 */
/*----------------------------------------------------------------------------*/
void TdlsEventTearDown(struct GLUE_INFO *prGlueInfo,
		       uint8_t *prInBuf, uint32_t u4InBufLen)
{
	struct STA_RECORD *prStaRec;
	uint16_t u2ReasonCode = TDLS_REASON_CODE_NONE;
	uint32_t u4TearDownSubId;
	uint8_t *pMac, aucZeroMac[6];

	/* init */
	u4TearDownSubId = *(uint32_t *) prInBuf;
	kalMemZero(aucZeroMac, sizeof(aucZeroMac));
	pMac = aucZeroMac;

	prStaRec = cnmGetStaRecByIndex(prGlueInfo->prAdapter,
				       *(prInBuf + 4));

	/* sanity check */
	if (prStaRec == NULL)
		return;

	pMac = prStaRec->aucMacAddr;

	if (fgIsPtiTimeoutSkip == TRUE) {
		/* skip PTI timeout event */
		if (u4TearDownSubId == TDLS_HOST_EVENT_TD_PTI_TIMEOUT)
			return;
	}

	if (u4TearDownSubId == TDLS_HOST_EVENT_TD_PTI_TIMEOUT) {
		DBGLOG(TDLS, INFO,
	       "TDLS_HOST_EVENT_TD_PTI_TIMEOUT TDLS_REASON_CODE_UNSPECIFIED\n");
		u2ReasonCode = TDLS_REASON_CODE_UNSPECIFIED;

		cfg80211_tdls_oper_request(prGlueInfo->prDevHandler,
				prStaRec->aucMacAddr,
				NL80211_TDLS_TEARDOWN,
				WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE,
				GFP_ATOMIC);
	}

	if (u4TearDownSubId == TDLS_HOST_EVENT_TD_AGE_TIMEOUT) {
		DBGLOG(TDLS, INFO,
	       "TDLS_HOST_EVENT_TD_AGE_TIMEOUT TDLS_REASON_CODE_UNREACHABLE\n");
		u2ReasonCode = TDLS_REASON_CODE_UNREACHABLE;

		cfg80211_tdls_oper_request(prGlueInfo->prDevHandler,
				prStaRec->aucMacAddr, NL80211_TDLS_TEARDOWN,
				WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE,
				GFP_ATOMIC);

	}

	DBGLOG(TDLS, INFO, "\n\n u2ReasonCode = %u\n\n",
	       u2ReasonCode);
}

void TdlsBssExtCapParse(struct STA_RECORD *prStaRec,
			uint8_t *pucIE)
{
	uint8_t *pucIeExtCap;

	/* sanity check */
	if ((prStaRec == NULL) || (pucIE == NULL))
		return;

	if (IE_ID(pucIE) != ELEM_ID_EXTENDED_CAP)
		return;

	/*
	 *  from bit0 ~
	 *  bit 38: TDLS Prohibited
	 *  The TDLS Prohibited subfield indicates whether the use of TDLS is
	 *	prohibited.
	 *  The field is set to 1 to indicate that TDLS is prohibited
	 *	and to 0 to indicate that TDLS is allowed.
	 */
	if (IE_LEN(pucIE) < 5)
		return;		/* we need 39/8 = 5 bytes */

	/* init */
	prStaRec->fgTdlsIsProhibited = FALSE;
	prStaRec->fgTdlsIsChSwProhibited = FALSE;

	/* parse */
	pucIeExtCap = pucIE + 2;
	pucIeExtCap += 4;	/* shift to the byte we care about */

	if ((*pucIeExtCap) & BIT(38 - 32))
		prStaRec->fgTdlsIsProhibited = TRUE;
	if ((*pucIeExtCap) & BIT(39 - 32))
		prStaRec->fgTdlsIsChSwProhibited = TRUE;

}

/*----------------------------------------------------------------------------*/
/*!
 * \brief        Generate CMD_ID_SET_TDLS_CH_SW command
 *
 * \param[in]
 *
 * \return none
 */
/*----------------------------------------------------------------------------*/
uint32_t
TdlsSendChSwControlCmd(struct ADAPTER *prAdapter,
		       void *pvSetBuffer, uint32_t u4SetBufferLen,
		       uint32_t *pu4SetInfoLen)
{

	struct CMD_TDLS_CH_SW rCmdTdlsChSwCtrl;

	ASSERT(prAdapter);

	/* send command packet for scan */
	kalMemZero(&rCmdTdlsChSwCtrl,
		   sizeof(struct CMD_TDLS_CH_SW));

	rCmdTdlsChSwCtrl.fgIsTDLSChSwProhibit =
		prAdapter->prAisBssInfo->fgTdlsIsChSwProhibited;

	wlanSendSetQueryCmd(prAdapter,
			    CMD_ID_SET_TDLS_CH_SW,
			    TRUE,
			    FALSE, FALSE, NULL, NULL,
			    sizeof(struct CMD_TDLS_CH_SW),
			    (uint8_t *)&rCmdTdlsChSwCtrl, NULL, 0);
	return TDLS_STATUS_SUCCESS;
}

uint32_t
TdlsTxCtrl(struct ADAPTER *prAdapter,
	   struct BSS_INFO *prBssInfo, u_int8_t fgEnable)
{
	int i;
	struct STA_RECORD *prStaRec;

	for (i = 0; i < CFG_STA_REC_NUM; i++) {
		prStaRec = &prAdapter->arStaRec[i];

		if (prStaRec->eStaType != STA_TYPE_DLS_PEER)
			continue;

		if (prStaRec->fgIsInUse && prStaRec->ucBssIndex
					== prBssInfo->ucBssIndex) {
			qmSetStaRecTxAllowed(prAdapter, prStaRec, fgEnable);
			DBGLOG(TDLS, EVENT, "TDLS STA[%d], TX ctrl=%d\n",
							i, fgEnable);
		}
	}

	return TDLS_STATUS_SUCCESS;
}
#endif /* CFG_SUPPORT_TDLS */

/* End of tdls.c */
