/*
========================================================================
Routine Description:
	Build the traffic response frame body.

Arguments:
	pAd				- WLAN control block pointer
	pFrameBuf		- frame
	pFrameLen		- frame length
	pTDLS			- TDLS link entry

Return Value:
	None

Note:
========================================================================
*/
static VOID TDLS_UAPSD_TrafficRspBuild(
	IN	PRTMP_ADAPTER				pAd,
	OUT PUCHAR						pFrameBuf,
	OUT PULONG						pFrameLen,
	IN	PRT_802_11_TDLS				pTDLS,
	IN	UCHAR						PeerToken)
{
	/* fill action code */
	TDLS_InsertActField(pAd, (pFrameBuf + *pFrameLen), pFrameLen,
						CATEGORY_TDLS, TDLS_ACTION_CODE_PEER_TRAFFIC_RESPONSE);

	/* fill Dialog Token */
	TDLS_InsertDialogToken(pAd, (pFrameBuf + *pFrameLen), pFrameLen,
							PeerToken);

	/* fill link identifier */
	if (pTDLS->bInitiator)
	{
		TDLS_InsertLinkIdentifierIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen,
									pTDLS->MacAddr, pAd->CurrentAddress);
	}
	else
	{
		TDLS_InsertLinkIdentifierIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen,
									pAd->CurrentAddress, pTDLS->MacAddr);
	}
}
/*
========================================================================
Routine Description:
	Build the traffic indication frame payload.

Arguments:
	pAd				- WLAN control block pointer
	pFrameBuf		- frame
	pFrameLen		- frame length
	pTDLS			- TDLS link entry

Return Value:
	None

Note:
========================================================================
*/
static VOID TDLS_UAPSD_TrafficIndPayloadBuild(
	IN	PRTMP_ADAPTER				pAd,
	OUT PUCHAR						pFrameBuf,
	OUT PULONG						pFrameLen,
	IN	PRT_802_11_TDLS				pTDLS)
{
	UCHAR RemoteFrameType = PROTO_NAME_TDLS;
	ULONG TempLen;
	UCHAR Token;			// Dialog token


	/* fill remote frame type */
	MakeOutgoingFrame(pFrameBuf,		&TempLen,
						1,				&RemoteFrameType,
						END_OF_ARGS);
	*pFrameLen = TempLen;

	/* fill action code */
	TDLS_InsertActField(pAd, (pFrameBuf + *pFrameLen), pFrameLen,
						CATEGORY_TDLS, TDLS_ACTION_CODE_PEER_TRAFFIC_INDICATION);

	/* fill Dialog Token */
	TDLS_UAPSD_DIALOG_GET(pAd, Token);
	TDLS_InsertDialogToken(pAd, (pFrameBuf + *pFrameLen), pFrameLen,
							Token);

	/* fill link identifier */
	if (pTDLS->bInitiator)
		TDLS_InsertLinkIdentifierIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen,
									pTDLS->MacAddr, pAd->CurrentAddress);
	else
		TDLS_InsertLinkIdentifierIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen,
									pAd->CurrentAddress, pTDLS->MacAddr);

	/* fill PU buffer status */
	TDLS_InsertPuBufferStatus(pAd, (pFrameBuf + *pFrameLen), pFrameLen,
							pTDLS->MacAddr);
}
/*
========================================================================
Routine Description:
	Simulate to receive a TDLS Traffic indication from a peer.

Arguments:
	pAd				- WLAN control block pointer
	Argc			- the number of input parameters
	*pArgv			- input parameters

Return Value:
	None

Note:
	1. Command Format:
		iwpriv ra0 set tdls=53_[PEER MAC]
========================================================================
*/
static VOID TDLS_UAPSD_CmdSimTrafficIndRcv(
	IN	PRTMP_ADAPTER				pAd,
	IN	INT32						Argc,
	IN	CHAR						*pArgv)
{
	UCHAR PeerMac[6];
	MLME_QUEUE_ELEM *pElem = NULL;
	PUCHAR pOutBuffer = NULL;
	ULONG FrameLen = 0;
	INT32 LinkId;
	RT_802_11_TDLS *pTDLS = NULL;
	NDIS_STATUS	NStatus = NDIS_STATUS_SUCCESS;
	UCHAR RemoteFrameType = PROTO_NAME_TDLS;
	ULONG TempLen;
	UCHAR Token;			// Dialog token


	/* get MAC address */
	TDLS_UAPSD_CmdUtilMacGet(&pArgv, PeerMac);

	/* allocate resources */
	NStatus = MlmeAllocateMemory(pAd, &pOutBuffer);
	if (NStatus	!= NDIS_STATUS_SUCCESS)
		goto LabelExit;

	/* make up a virtual traffic indication frame */
	/* search TDLS entry */
	LinkId = TDLS_SearchLinkId(pAd, PeerMac);
	if (TDLS_UAPSD_IS_LINK_INVALID(LinkId))
	{
		DBGPRINT(RT_DEBUG_ERROR,
				("%s: can not find the LinkId!\n", __FUNCTION__));
		TDLS_UAPSD_REBUILD_LINK(pAd, PeerMac);
		goto LabelExit;
	}
	pTDLS = TDLS_UAPSD_ENTRY_GET(pAd, LinkId);

	/* build the frame */
	/* fill remote frame type */
	MakeOutgoingFrame(pOutBuffer,		&TempLen,
						1,				&RemoteFrameType,
						END_OF_ARGS);
	FrameLen = TempLen;

	/* fill action code */
	TDLS_InsertActField(pAd, (pOutBuffer + FrameLen), &FrameLen,
						CATEGORY_TDLS, TDLS_ACTION_CODE_SETUP_REQUEST);

	/* fill Dialog Token */
	TDLS_UAPSD_DIALOG_GET(pAd, Token);
	TDLS_InsertDialogToken(pAd, (pOutBuffer + FrameLen), &FrameLen,
							Token);

	/* fill link identifier */
	TDLS_InsertLinkIdentifierIE(pAd, (pOutBuffer + FrameLen), &FrameLen,
								pTDLS->MacAddr, pAd->CurrentAddress);

	/* fill PU buffer status */
	TDLS_InsertPuBufferStatus(pAd, (pOutBuffer + FrameLen), &FrameLen,
							pTDLS->MacAddr);

	if (FrameLen <= 0)
		goto LabelExit;

/*	hex_dump("TDLS traffic indication send pack", pOutBuffer, FrameLen); */

	/* allocate resources */
	os_alloc_mem(NULL, (UCHAR **)&pElem, sizeof(MLME_QUEUE_ELEM));
	if (pElem == NULL)
		goto LabelExit;

	/* copy the indication frame */
	FrameLen += LENGTH_802_11 + LENGTH_802_1_H;
	pElem->MsgLen = LENGTH_802_11 + LENGTH_802_1_H + FrameLen;

	/* copy payload */
	memcpy(pElem->Msg + LENGTH_802_11 + LENGTH_802_1_H,
			pOutBuffer, FrameLen);

	/* handle it */
	TDLS_UAPSD_PeerTrafficIndAction(pAd, pElem);

	/* free resources */
LabelExit:
	if (pElem != NULL)
		os_free_mem(NULL, pElem);
	if (pOutBuffer != NULL)
		MlmeFreeMemory(pAd, pOutBuffer);
}
/*
==========================================================================
	Description:
	    
	IRQL = PASSIVE_LEVEL
==========================================================================
*/
VOID
TDLS_BuildSetupRequest(
	IN	PRTMP_ADAPTER	pAd,
	OUT PUCHAR	pFrameBuf,
	OUT PULONG	pFrameLen,
	IN	PRT_802_11_TDLS	pTDLS)
{
	ULONG			Timeout = TDLS_TIMEOUT;
	BOOLEAN			TimerCancelled;
	/* fill action code */
	TDLS_InsertActField(pAd, (pFrameBuf + *pFrameLen), pFrameLen, CATEGORY_TDLS, TDLS_ACTION_CODE_SETUP_REQUEST);

	/* fill Dialog Token */
	pAd->StaCfg.TdlsDialogToken++;
	if (pAd->StaCfg.TdlsDialogToken == 0)
		pAd->StaCfg.TdlsDialogToken++;

	pTDLS->Token = pAd->StaCfg.TdlsDialogToken;
	TDLS_InsertDialogToken(pAd, (pFrameBuf + *pFrameLen), pFrameLen, pTDLS->Token);

	/* fill link identifier */
	TDLS_InsertLinkIdentifierIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen, pAd->CurrentAddress, pTDLS->MacAddr);

	// fill capability
	TDLS_InsertCapIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen);

	// fill ssid
	TDLS_InsertSSIDIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen);

	// fill support rate
	TDLS_InsertSupportRateIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen);

	// fill ext rate
	TDLS_InsertExtRateIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen);

	// fill Qos Capability
	TDLS_InsertQosCapIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen);

#ifdef DOT11_N_SUPPORT
	// fill HT Capability
	TDLS_InsertHtCapIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen);

	// fill 20/40 BSS Coexistence (7.3.2.61)
#ifdef DOT11N_DRAFT3
	TDLS_InsertBSSCoexistenceIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen);
#endif // DOT11N_DRAFT3 //
#endif // DOT11_N_SUPPORT //

	// fill  Extended Capabilities (7.3.2.27)
	TDLS_InsertExtCapIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen);

	// TPK Handshake if RSNA Enabled
	// Pack TPK Message 1 here! 
	if (((pAd->StaCfg.AuthMode == Ndis802_11AuthModeWPA2) || (pAd->StaCfg.AuthMode == Ndis802_11AuthModeWPA2PSK)) &&
		((pAd->StaCfg.WepStatus == Ndis802_11Encryption2Enabled) || (pAd->StaCfg.WepStatus == Ndis802_11Encryption3Enabled)))
	{		
		UCHAR			CipherTmp[64] = {0};
		UCHAR			CipherTmpLen = 0;
		FT_FTIE			FtIe;
		ULONG			KeyLifetime = TDLS_KEY_TIMEOUT;	// sec
		ULONG			tmp;
		UCHAR			Length;
		
		// RSNIE (7.3.2.25)
		CipherTmpLen = CipherSuiteTDLSLen;	
		if (pAd->StaCfg.WepStatus == Ndis802_11Encryption2Enabled)
			NdisMoveMemory(CipherTmp, CipherSuiteTDLSWpa2PskTkip, CipherTmpLen);
		else
			NdisMoveMemory(CipherTmp, CipherSuiteTDLSWpa2PskAes, CipherTmpLen);
		
		// update AKM
		if (pAd->StaCfg.AuthMode == Ndis802_11AuthModeWPA2)
			CipherTmp[19] = TDLS_AKM_SUITE_1X;
		
		// Insert RSN_IE to outgoing frame
		MakeOutgoingFrame((pFrameBuf + *pFrameLen),	&tmp,
				CipherTmpLen,						&CipherTmp,
				END_OF_ARGS);

		*pFrameLen = *pFrameLen + tmp;
	
			
		// FTIE (7.3.2.48)
		NdisZeroMemory(&FtIe, sizeof(FtIe));
		Length =  sizeof(FtIe);

		// generate SNonce
		GenRandom(pAd, pAd->CurrentAddress, FtIe.SNonce);
		hex_dump("TDLS - Generate SNonce ", FtIe.SNonce, 32);
		NdisMoveMemory(pTDLS->SNonce, FtIe.SNonce, 32);
		
		TDLS_InsertFTIE(
				pAd, 
				(pFrameBuf + *pFrameLen), 
				pFrameLen, 
				Length, 
				FtIe.MICCtr,
				FtIe.MIC, 
				FtIe.ANonce, 
				FtIe.SNonce);

		
		// Timeout Interval (7.3.2.49)
		TDLS_InsertTimeoutIntervalIE(
				pAd, 
				(pFrameBuf + *pFrameLen), 
				pFrameLen, 
				2, /* key lifetime interval */
				KeyLifetime);

		pTDLS->KeyLifetime = KeyLifetime;

	}

	// ==>> Set sendout timer
	RTMPCancelTimer(&pTDLS->Timer, &TimerCancelled);
	RTMPSetTimer(&pTDLS->Timer, Timeout);
	// ==>> State Change

	pTDLS->Status = TDLS_MODE_WAIT_RESPONSE;

}
/*
==========================================================================
	Description:
	    
	IRQL = PASSIVE_LEVEL
==========================================================================
*/
VOID
TDLS_BuildSetupConfirm(
	IN	PRTMP_ADAPTER	pAd,
	OUT PUCHAR	pFrameBuf,
	OUT PULONG	pFrameLen,
	IN	PRT_802_11_TDLS	pTDLS,
	IN	UCHAR	RsnLen,
	IN	PUCHAR	pRsnIe,
	IN	UCHAR	FTLen,
	IN	PUCHAR	pFTIe,
	IN	UCHAR	TILen,
	IN	PUCHAR	pTIIe,
	IN	UINT16	StatusCode)
{
	/* fill action code */
	TDLS_InsertActField(pAd, (pFrameBuf + *pFrameLen), pFrameLen, CATEGORY_TDLS, TDLS_ACTION_CODE_SETUP_CONFIRM);

	/* fill status code */
	TDLS_InsertStatusCode(pAd, (pFrameBuf + *pFrameLen), pFrameLen, StatusCode);

	/* fill Dialog Token */
	TDLS_InsertDialogToken(pAd, (pFrameBuf + *pFrameLen), pFrameLen, pTDLS->Token);

	/* fill link identifier */
	TDLS_InsertLinkIdentifierIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen, pAd->CurrentAddress, pTDLS->MacAddr);

	// fill Qos Capability
	TDLS_InsertEDCAParameterSetIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen, pTDLS);

#ifdef DOT11_N_SUPPORT
	// fill HT Capability
	TDLS_InsertHtCapIE(pAd, (pFrameBuf + *pFrameLen), pFrameLen);
#endif // DOT11_N_SUPPORT //

	// TPK Handshake if RSNA Enabled
	// Pack TPK Message 3 here! 
	if (((pAd->StaCfg.AuthMode == Ndis802_11AuthModeWPA2) || (pAd->StaCfg.AuthMode == Ndis802_11AuthModeWPA2PSK)) &&
		((pAd->StaCfg.WepStatus == Ndis802_11Encryption2Enabled) || (pAd->StaCfg.WepStatus == Ndis802_11Encryption3Enabled)))
	{		
		FT_FTIE	*ft;
		ULONG	tmp;
			
		// RSNIE (7.3.2.25)			
		// Insert RSN_IE of the Peer TDLS to outgoing frame
		MakeOutgoingFrame((pFrameBuf + *pFrameLen),	&tmp,
							RsnLen,					pRsnIe,
							END_OF_ARGS);
		*pFrameLen = *pFrameLen + tmp;
		
				
		// FTIE (7.3.2.48)
		// Construct FTIE (IE + Length + MIC Control + MIC + ANonce + SNonce)
	
		// point to the element of IE
		ft = (FT_FTIE *)(pFTIe + 2);	
		// set MIC field to zero before MIC calculation
		NdisZeroMemory(ft->MIC, 16);

		////////////////////////////////////////////////////////////////////////
		// The MIC field of FTIE shall be calculated on the concatenation, in the following order, of
		// 1. MAC_I (6 bytes)
		// 2. MAC_R (6 bytes)
		// 3. Transaction Sequence = 2 (1 byte)
		// 4. Link Identifier (20 bytes)
		// 5. RSN IE without the IE header (20 bytes)
		// 6. Timeout Interval IE (7 bytes)
		// 7. FTIE without the IE header, with the MIC field of FTIE set to zero (82 bytes)	
		{
			UCHAR	content[512];
			ULONG	c_len = 0;
			ULONG	tmp_len = 0;
			UCHAR	Seq = 3;
			UCHAR	mic[16];
			UCHAR	LinkIdentifier[20];
			UINT	tmp_aes_len = 0;

			NdisZeroMemory(LinkIdentifier, 20);
			LinkIdentifier[0] = IE_TDLS_LINK_IDENTIFIER;
			LinkIdentifier[1] = 18;
			NdisMoveMemory(&LinkIdentifier[2], pAd->CommonCfg.Bssid, 6);
			NdisMoveMemory(&LinkIdentifier[8], pAd->CurrentAddress, 6);
			NdisMoveMemory(&LinkIdentifier[14], pTDLS->MacAddr, 6);

			NdisZeroMemory(mic, sizeof(mic));

			/* make a header frame for calculating MIC. */
			MakeOutgoingFrame(content,					&tmp_len,
								MAC_ADDR_LEN,			pAd->CurrentAddress,
								MAC_ADDR_LEN,			pTDLS->MacAddr,
								1,						&Seq,
								END_OF_ARGS);
			c_len += tmp_len;					

			/* concatenate Link Identifier */			
			MakeOutgoingFrame(content + c_len,		&tmp_len,
								20,					LinkIdentifier,
								END_OF_ARGS);
			c_len += tmp_len;					

				
			/* concatenate RSNIE */
			MakeOutgoingFrame(content + c_len,		&tmp_len,
								20,					(pRsnIe + 2),
								END_OF_ARGS);
			c_len += tmp_len;					

			/* concatenate Timeout Interval IE */
			MakeOutgoingFrame(content + c_len,     &tmp_len,
								7,					pTIIe,
								END_OF_ARGS);
			c_len += tmp_len;

			/* concatenate FTIE */			
			MakeOutgoingFrame(content + c_len,		&tmp_len,
								sizeof(FT_FTIE),	(PUCHAR)ft,
								END_OF_ARGS);
			c_len += tmp_len;	

			/* Calculate MIC */				
			//AES_128_CMAC(pTDLS->TPK, content, c_len, mic);

			/* Compute AES-128-CMAC over the concatenation */
			tmp_aes_len = AES_KEY128_LENGTH;
    		AES_CMAC(content, c_len, pTDLS->TPK, 16, mic, &tmp_aes_len);

			// Fill Mic to ft struct
			NdisMoveMemory(ft->MIC, mic, 16);
		}
		////////////////////////////////////////////////////////////////////////
	
		// Insert FT_IE to outgoing frame
		TDLS_InsertFTIE(
					pAd, 
					(pFrameBuf + *pFrameLen), 
					pFrameLen, 
					sizeof(FT_FTIE), 
					ft->MICCtr, 
					ft->MIC, 
					ft->ANonce, 
					ft->SNonce);

		// Timeout Interval (7.3.2.49)
		// Insert TI_IE to outgoing frame
		TDLS_InsertTimeoutIntervalIE(
					pAd, 
					(pFrameBuf + *pFrameLen), 
					pFrameLen, 
					2, /* key lifetime interval */
					pTDLS->KeyLifetime);

	}
}