/******************************************************************************
 *
 * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
 *
 *
 ******************************************************************************/
#define _RTL8192E_XMIT_C_

//#include <drv_types.h>
#include <rtl8192e_hal.h>

 s32	rtl8192e_init_xmit_priv(_adapter *padapter)
{
	HAL_DATA_TYPE	*pHalData = GET_HAL_DATA(padapter);
	struct xmit_priv 	*pxmitpriv = &padapter->xmitpriv;
	
	#ifdef CONFIG_TX_EARLY_MODE
	pHalData->bEarlyModeEnable = padapter->registrypriv.early_mode;
	#endif
	pxmitpriv->hw_ssn_seq_no = rtw_get_hwseq_no(padapter);	
	
	pxmitpriv->nqos_ssn = 0;
	return _SUCCESS;
}

void _dbg_dump_tx_info(_adapter	*padapter,int frame_tag, u8 *ptxdesc)
{
	u8 bDumpTxPkt;
	u8 bDumpTxDesc = _FALSE;
	rtw_hal_get_def_var(padapter, HAL_DEF_DBG_DUMP_TXPKT, &(bDumpTxPkt));

	if(bDumpTxPkt ==1){//dump txdesc for data frame
		DBG_871X("dump tx_desc for data frame\n");
		if((frame_tag&0x0f) == DATA_FRAMETAG){	
			bDumpTxDesc = _TRUE;		
		}
	}	
	else if(bDumpTxPkt ==2){//dump txdesc for mgnt frame
		DBG_871X("dump tx_desc for mgnt frame\n");
		if((frame_tag&0x0f) == MGNT_FRAMETAG){	
			bDumpTxDesc = _TRUE;		
		}
	}	
	else if(bDumpTxPkt ==3){//dump early info
	}

	if(bDumpTxDesc){
		//	ptxdesc->txdw4 = cpu_to_le32(0x00001006);//RTS Rate=24M
		//	ptxdesc->txdw6 = 0x6666f800;
		DBG_8192C("=====================================\n");
		DBG_8192C("Offset00(0x%08x)\n",*((u32 *)(ptxdesc)));
		DBG_8192C("Offset04(0x%08x)\n",*((u32 *)(ptxdesc+4)));
		DBG_8192C("Offset08(0x%08x)\n",*((u32 *)(ptxdesc+8)));
		DBG_8192C("Offset12(0x%08x)\n",*((u32 *)(ptxdesc+12)));
		DBG_8192C("Offset16(0x%08x)\n",*((u32 *)(ptxdesc+16)));
		DBG_8192C("Offset20(0x%08x)\n",*((u32 *)(ptxdesc+20)));
		DBG_8192C("Offset24(0x%08x)\n",*((u32 *)(ptxdesc+24)));
		DBG_8192C("Offset28(0x%08x)\n",*((u32 *)(ptxdesc+28)));
		DBG_8192C("Offset32(0x%08x)\n",*((u32 *)(ptxdesc+32)));
		DBG_8192C("Offset36(0x%08x)\n",*((u32 *)(ptxdesc+36)));
		DBG_8192C("=====================================\n");
	}

}

/*
 * Description:
 *	Aggregation packets and send to hardware
 *
 * Return:
 *	0	Success
 *	-1	Hardware resource(TX FIFO) not ready
 *	-2	Software resource(xmitbuf) not ready
 */
#ifdef CONFIG_TX_EARLY_MODE

//#define DBG_EMINFO

#if RTL8188E_EARLY_MODE_PKT_NUM_10 == 1	
	#define EARLY_MODE_MAX_PKT_NUM	10
#else
	#define EARLY_MODE_MAX_PKT_NUM	5
#endif


struct EMInfo{
	u8 	EMPktNum;
	u16  EMPktLen[EARLY_MODE_MAX_PKT_NUM];
};


void
InsertEMContent_8192E(
	struct EMInfo *pEMInfo,
	IN pu1Byte	VirtualAddress)
{

#if RTL8188E_EARLY_MODE_PKT_NUM_10 == 1
	u1Byte index=0;
	u4Byte	dwtmp=0;
#endif

	_rtw_memset(VirtualAddress, 0, EARLY_MODE_INFO_SIZE);
	if(pEMInfo->EMPktNum==0)
		return;

	#ifdef DBG_EMINFO
	{
		int i;
		DBG_8192C("\n%s ==> pEMInfo->EMPktNum =%d\n",__FUNCTION__,pEMInfo->EMPktNum);
		for(i=0;i< EARLY_MODE_MAX_PKT_NUM;i++){
			DBG_8192C("%s ==> pEMInfo->EMPktLen[%d] =%d\n",__FUNCTION__,i,pEMInfo->EMPktLen[i]);
		}

	}
	#endif
	
#if RTL8188E_EARLY_MODE_PKT_NUM_10 == 1
	SET_EARLYMODE_PKTNUM(VirtualAddress, pEMInfo->EMPktNum);

	if(pEMInfo->EMPktNum == 1){
		dwtmp = pEMInfo->EMPktLen[0];
	}else{
		dwtmp = pEMInfo->EMPktLen[0];
		dwtmp += ((dwtmp%4)?(4-dwtmp%4):0)+4;
		dwtmp += pEMInfo->EMPktLen[1];
	}
	SET_EARLYMODE_LEN0(VirtualAddress, dwtmp);
	if(pEMInfo->EMPktNum <= 3){
		dwtmp = pEMInfo->EMPktLen[2];
	}else{
		dwtmp = pEMInfo->EMPktLen[2];
		dwtmp += ((dwtmp%4)?(4-dwtmp%4):0)+4;
		dwtmp += pEMInfo->EMPktLen[3];
	}
	SET_EARLYMODE_LEN1(VirtualAddress, dwtmp);
	if(pEMInfo->EMPktNum <= 5){
		dwtmp = pEMInfo->EMPktLen[4];
	}else{
		dwtmp = pEMInfo->EMPktLen[4];
		dwtmp += ((dwtmp%4)?(4-dwtmp%4):0)+4;
		dwtmp += pEMInfo->EMPktLen[5];
	}
	SET_EARLYMODE_LEN2_1(VirtualAddress, dwtmp&0xF);
	SET_EARLYMODE_LEN2_2(VirtualAddress, dwtmp>>4);
	if(pEMInfo->EMPktNum <= 7){
		dwtmp = pEMInfo->EMPktLen[6];
	}else{
		dwtmp = pEMInfo->EMPktLen[6];
		dwtmp += ((dwtmp%4)?(4-dwtmp%4):0)+4;
		dwtmp += pEMInfo->EMPktLen[7];
	}
	SET_EARLYMODE_LEN3(VirtualAddress, dwtmp);
	if(pEMInfo->EMPktNum <= 9){
		dwtmp = pEMInfo->EMPktLen[8];
	}else{
		dwtmp = pEMInfo->EMPktLen[8];
		dwtmp += ((dwtmp%4)?(4-dwtmp%4):0)+4;
		dwtmp += pEMInfo->EMPktLen[9];
	}
	SET_EARLYMODE_LEN4(VirtualAddress, dwtmp);
#else	
	SET_EARLYMODE_PKTNUM(VirtualAddress, pEMInfo->EMPktNum);
	SET_EARLYMODE_LEN0(VirtualAddress, pEMInfo->EMPktLen[0]);
	SET_EARLYMODE_LEN1(VirtualAddress, pEMInfo->EMPktLen[1]);
	SET_EARLYMODE_LEN2_1(VirtualAddress, pEMInfo->EMPktLen[2]&0xF);
	SET_EARLYMODE_LEN2_2(VirtualAddress, pEMInfo->EMPktLen[2]>>4);
	SET_EARLYMODE_LEN3(VirtualAddress, pEMInfo->EMPktLen[3]);
	SET_EARLYMODE_LEN4(VirtualAddress, pEMInfo->EMPktLen[4]);
#endif	
	//RT_PRINT_DATA(COMP_SEND, DBG_LOUD, "EMHdr:", VirtualAddress, 8);

}



void UpdateEarlyModeInfo8192E(struct xmit_priv *pxmitpriv,struct xmit_buf *pxmitbuf )
{
	//_adapter *padapter, struct xmit_frame *pxmitframe,struct tx_servq	*ptxservq
	int index,j;
	u16 offset,pktlen;
	PTXDESC_8192E ptxdesc;
	
	u8 *pmem,*pEMInfo_mem;
	s8 node_num_0=0,node_num_1=0;
	struct EMInfo eminfo;
	struct agg_pkt_info *paggpkt;
	struct xmit_frame *pframe = (struct xmit_frame*)pxmitbuf->priv_data;	
	pmem= pframe->buf_addr;	
	
	#ifdef DBG_EMINFO			
	DBG_8192C("\n%s ==> agg_num:%d\n",__FUNCTION__, pframe->agg_num);
	for(index=0;index<pframe->agg_num;index++){
		offset = 	pxmitpriv->agg_pkt[index].offset;
		pktlen = pxmitpriv->agg_pkt[index].pkt_len;
		DBG_8192C("%s ==> agg_pkt[%d].offset=%d\n",__FUNCTION__,index,offset);
		DBG_8192C("%s ==> agg_pkt[%d].pkt_len=%d\n",__FUNCTION__,index,pktlen);
	}
	#endif
	
	if( pframe->agg_num > EARLY_MODE_MAX_PKT_NUM)
	{	
		node_num_0 = pframe->agg_num;
		node_num_1= EARLY_MODE_MAX_PKT_NUM-1;
	}
	
	for(index=0;index<pframe->agg_num;index++){

		offset = pxmitpriv->agg_pkt[index].offset;
		pktlen = pxmitpriv->agg_pkt[index].pkt_len;		

		_rtw_memset(&eminfo,0,sizeof(struct EMInfo));
		if( pframe->agg_num > EARLY_MODE_MAX_PKT_NUM){
			if(node_num_0 > EARLY_MODE_MAX_PKT_NUM){
				eminfo.EMPktNum = EARLY_MODE_MAX_PKT_NUM;
				node_num_0--;
			}
			else{
				eminfo.EMPktNum = node_num_1;
				node_num_1--;				
			}			
		}
		else{
			eminfo.EMPktNum = pframe->agg_num-(index+1);	
		}				
		for(j=0;j< eminfo.EMPktNum ;j++){
			eminfo.EMPktLen[j] = pxmitpriv->agg_pkt[index+1+j].pkt_len+4;// 4 bytes CRC
		}
			
		if(pmem){
			if(index==0){
				ptxdesc = (PTXDESC_8192E)(pmem);
				pEMInfo_mem = ((u8 *)ptxdesc)+TXDESC_SIZE;				
			}
			else{
				pmem = pmem + pxmitpriv->agg_pkt[index-1].offset;
				ptxdesc = (PTXDESC_8192E)(pmem);
				pEMInfo_mem = ((u8 *)ptxdesc)+TXDESC_SIZE;					
			}
			
			#ifdef DBG_EMINFO
			DBG_8192C("%s ==> desc.pkt_len=%d\n",__FUNCTION__,ptxdesc->pktlen);
			#endif
			InsertEMContent_8192E(&eminfo,pEMInfo_mem);
		}	
		
		
	} 
	_rtw_memset(pxmitpriv->agg_pkt,0,sizeof(struct agg_pkt_info)*MAX_AGG_PKT_NUM);

}
#endif

#if ((DEV_BUS_TYPE == RT_USB_INTERFACE) ||(DEV_BUS_TYPE == RT_SDIO_INTERFACE))
void rtl8192e_cal_txdesc_chksum(u8 *ptxdesc)
{
	u16	*usPtr = (u16*)ptxdesc;
	u32 count = 16;	// (32 bytes / 2 bytes per XOR) => 16 times
	u32 index;
	u16 checksum = 0;

	//Clear first
	SET_TX_DESC_TX_DESC_CHECKSUM_92E(ptxdesc, 0);

	for(index = 0 ; index < count ; index++){
		checksum = checksum ^ le16_to_cpu(*(usPtr + index));
	}

	SET_TX_DESC_TX_DESC_CHECKSUM_92E(ptxdesc, checksum);
}
#endif

void fill_txdesc_sectype(struct pkt_attrib *pattrib, u8 *ptxdesc)
{
	if ((pattrib->encrypt > 0) && !pattrib->bswenc)
	{
		switch (pattrib->encrypt)
		{	
			//SEC_TYPE : 0:NO_ENC,1:WEP40/TKIP,2:WAPI,3:AES
			case _WEP40_:
			case _WEP104_:
			case _TKIP_:
			case _TKIP_WTMIC_:	
					SET_TX_DESC_SEC_TYPE_92E(ptxdesc, 0x1);
					break;
#ifdef CONFIG_WAPI_SUPPORT
			case _SMS4_:
					SET_TX_DESC_SEC_TYPE_92E(ptxdesc, 0x2);
				break;
#endif
			case _AES_:
					SET_TX_DESC_SEC_TYPE_92E(ptxdesc, 0x3);
					break;
			case _NO_PRIVACY_:
			default:
					SET_TX_DESC_SEC_TYPE_92E(ptxdesc, 0x0);
					break;
		
		}
		
	}

}
void fill_txdesc_vcs(struct pkt_attrib *pattrib, u8 *ptxdesc)
{
	//DBG_8192C("cvs_mode=%d\n", pattrib->vcs_mode);	

	SET_TX_DESC_HW_RTS_ENABLE_92E(ptxdesc, 0);

	switch(pattrib->vcs_mode)
	{
		case RTS_CTS:
			SET_TX_DESC_RTS_ENABLE_92E(ptxdesc, 1);
			break;
		case CTS_TO_SELF:
			SET_TX_DESC_CTS2SELF_92E(ptxdesc, 1);
			break;
		case NONE_VCS:
		default:
			break;		
	}
	#if 0 //to do 
	// Protection mode related
	if(pTcb->bRTSEnable || pTcb->bCTSEnable)
	{
		SET_TX_DESC_CCA_RTS_92E(pDesc, pTcb->RTSCCA);

		//SET_TX_DESC_RTS_ENABLE_92E(pDesc, ((pTcb->bRTSEnable && !pTcb->bCTSEnable) ? 1 : 0));
		//SET_TX_DESC_CTS2SELF_92E(pDesc, ((pTcb->bCTSEnable) ? 1 : 0));

		SET_TX_DESC_CTROL_STBC_92E(pDesc, ((pTcb->bRTSSTBC) ? 1 : 0));
		SET_TX_DESC_RTS_SHORT_92E(pDesc, pTcb->bRTSShort);
		SET_TX_DESC_RTS_RATE_92E(pDesc, MRateToHwRate((u1Byte)pTcb->RTSRate));

		if(pMgntInfo->ForcedProtectionMode == PROTECTION_MODE_FORCE_ENABLE)
	    	{
		    SET_TX_DESC_RTS_RATE_FB_LIMIT_92E(pDesc, 1);
	    	}
		else
		{
			SET_TX_DESC_RTS_RATE_FB_LIMIT_92E(pDesc, 0xF);
		}
	}
	#endif
}

u8 
BWMapping_92E(
	IN	PADAPTER		Adapter,
	IN	struct pkt_attrib	*pattrib
)
{
	u8	BWSettingOfDesc = 0;
	PHAL_DATA_TYPE	pHalData = GET_HAL_DATA(Adapter);

	//DBG_871X("BWMapping pHalData->CurrentChannelBW %d, pattrib->bwmode %d \n",pHalData->CurrentChannelBW,pattrib->bwmode);

	if(pHalData->CurrentChannelBW== CHANNEL_WIDTH_40)
	{
		if(pattrib->bwmode == CHANNEL_WIDTH_40)
			BWSettingOfDesc = 1;
		else
			BWSettingOfDesc = 0;
	}
	else
		BWSettingOfDesc = 0;
	
	//if(pTcb->bBTTxPacket)
	//	BWSettingOfDesc = 0;

	return BWSettingOfDesc;
}

u8 
SCMapping_92E(
	IN	PADAPTER		Adapter,
	IN	struct pkt_attrib	*pattrib
)
{
	u8	SCSettingOfDesc = 0;
	PHAL_DATA_TYPE	pHalData = GET_HAL_DATA(Adapter);
	//DBG_871X("SCMapping: pHalData->CurrentChannelBW %d, pHalData->nCur80MhzPrimeSC %d, pHalData->nCur40MhzPrimeSC %d \n",pHalData->CurrentChannelBW,pHalData->nCur80MhzPrimeSC,pHalData->nCur40MhzPrimeSC);
	
	if(pHalData->CurrentChannelBW == CHANNEL_WIDTH_80)
	{
		if(pattrib->bwmode == CHANNEL_WIDTH_80)
		{
			SCSettingOfDesc = VHT_DATA_SC_DONOT_CARE;
		}
		else if(pattrib->bwmode == CHANNEL_WIDTH_40)
		{
			if(pHalData->nCur80MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER)
				SCSettingOfDesc = VHT_DATA_SC_40_LOWER_OF_80MHZ;
			else if(pHalData->nCur80MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_UPPER)
				SCSettingOfDesc = VHT_DATA_SC_40_UPPER_OF_80MHZ;
			else
				DBG_871X("%s- CurrentChannelBW:%d, SCMapping: DONOT CARE Mode Setting\n", __func__, pHalData->CurrentChannelBW);
		}
		else
		{
			if((pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER) && (pHalData->nCur80MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER))
				SCSettingOfDesc = VHT_DATA_SC_20_LOWEST_OF_80MHZ;
			else if((pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_UPPER) && (pHalData->nCur80MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER))
				SCSettingOfDesc = VHT_DATA_SC_20_LOWER_OF_80MHZ;
			else if((pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER) && (pHalData->nCur80MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_UPPER))
				SCSettingOfDesc = VHT_DATA_SC_20_UPPER_OF_80MHZ;
			else if((pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_UPPER) && (pHalData->nCur80MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_UPPER))
				SCSettingOfDesc = VHT_DATA_SC_20_UPPERST_OF_80MHZ;
			else
				DBG_871X("%s- CurrentChannelBW:%d, SCMapping: DONOT CARE Mode Setting\n", __func__, pHalData->CurrentChannelBW);
		}
	}
	else if(pHalData->CurrentChannelBW== CHANNEL_WIDTH_40)
	{
		//DBG_871X("SCMapping: HT Case: pHalData->CurrentChannelBW %d, pHalData->nCur40MhzPrimeSC %d \n",pHalData->CurrentChannelBW,pHalData->nCur40MhzPrimeSC);

		if(pattrib->bwmode == CHANNEL_WIDTH_40)
		{
			SCSettingOfDesc = VHT_DATA_SC_DONOT_CARE;
		}
		else if(pattrib->bwmode == CHANNEL_WIDTH_20)
		{
			if(pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_UPPER)
			{
				SCSettingOfDesc = VHT_DATA_SC_20_UPPER_OF_80MHZ;
			}
			else if(pHalData->nCur40MhzPrimeSC == HAL_PRIME_CHNL_OFFSET_LOWER)
			{
				SCSettingOfDesc = VHT_DATA_SC_20_LOWER_OF_80MHZ;
			}
			else
			{
				SCSettingOfDesc = VHT_DATA_SC_DONOT_CARE;
			}
				
		}
	}
	else
	{
		SCSettingOfDesc = VHT_DATA_SC_DONOT_CARE;
	}

	return SCSettingOfDesc;
}

void fill_txdesc_phy(PADAPTER padapter, struct pkt_attrib *pattrib, u8 *ptxdesc)
{
	//DBG_8192C("bwmode=%d, ch_off=%d\n", pattrib->bwmode, pattrib->ch_offset);

	if(pattrib->ht_en)
	{
		// Set Bandwidth and sub-channel settings.
		SET_TX_DESC_DATA_BW_92E(ptxdesc, BWMapping_92E(padapter,pattrib));

		SET_TX_DESC_DATA_SC_92E(ptxdesc, SCMapping_92E(padapter,pattrib));
	}
}

void rtl8192e_fixed_rate(_adapter *padapter,u8 *ptxdesc)
{
	if(padapter->fix_rate!= 0xFF){
	
		SET_TX_DESC_USE_RATE_92E(ptxdesc, 1);
		//if(pHalData->INIDATA_RATE[pattrib->mac_id] & BIT(7))
      		{
			if(padapter->fix_rate & BIT(7))
				SET_TX_DESC_DATA_SHORT_92E(ptxdesc, 1);
		}
		SET_TX_DESC_TX_RATE_92E(ptxdesc, (padapter->fix_rate & 0x7F));
		//SET_TX_DESC_DISABLE_FB_92E(ptxdesc,1);
		if (!padapter->data_fb)
			SET_TX_DESC_DISABLE_FB_92E(ptxdesc,1);
		
		//ptxdesc->datarate = padapter->fix_rate;
	}
	
}

//
// Description: In normal chip, we should send some packet to Hw which will be used by Fw
//		in FW LPS mode. The function is to fill the Tx descriptor of this packets, then
//		Fw can tell Hw to send these packet derectly.
//
void rtl8192e_fill_fake_txdesc(
	PADAPTER	padapter,
	u8*			pDesc,
	u32			BufferLen,
	u8			IsPsPoll,
	u8			IsBTQosNull,
	u8			bDataFrame)
{
	struct mlme_ext_priv	*pmlmeext = &padapter->mlmeextpriv;
	struct xmit_priv 		*pxmitpriv = &padapter->xmitpriv;

	// Clear all status
	_rtw_memset(pDesc, 0, TXDESC_SIZE);

	SET_TX_DESC_OFFSET_92E(pDesc, (TXDESC_SIZE+OFFSET_SZ));

	SET_TX_DESC_PKT_SIZE_92E(pDesc, BufferLen);
	
	if (pmlmeext->cur_wireless_mode & WIRELESS_11B) {
		SET_TX_DESC_RATE_ID_92E(pDesc, RATEID_IDX_B);
	} else {
		SET_TX_DESC_RATE_ID_92E(pDesc, RATEID_IDX_G);
	}

	SET_TX_DESC_QUEUE_SEL_92E(pDesc,  QSLT_MGNT);

	//Set NAVUSEHDR to prevent Ps-poll AId filed to be changed to error vlaue by Hw.
	if (IsPsPoll)
	{
		SET_TX_DESC_NAV_USE_HDR_92E(pDesc, 1); 
	}
	else
	{
		SET_TX_DESC_EN_HWSEQ_92E(pDesc, 1); // Hw set sequence number
		SET_TX_DESC_HWSEQ_SEL_92E(pDesc, pxmitpriv->hw_ssn_seq_no);
	}

	SET_TX_DESC_USE_RATE_92E(pDesc, 1);
	SET_TX_DESC_TX_RATE_92E(pDesc, MRateToHwRate(pmlmeext->tx_rate));

	//
	// Encrypt the data frame if under security mode excepct null data. Suggested by CCW.
	//
	if (_TRUE ==bDataFrame)
	{		
		struct pkt_attrib pattrib;
		pattrib.encrypt = padapter->securitypriv.dot11PrivacyAlgrthm;
		pattrib.bswenc = _FALSE;
		fill_txdesc_sectype(&pattrib, pDesc);		
	}

#if defined(CONFIG_USB_HCI) || defined(CONFIG_SDIO_HCI)
	// USB interface drop packet if the checksum of descriptor isn't correct.
	// Using this checksum can let hardware recovery from packet bulk out error (e.g. Cancel URC, Bulk out error.).
	rtl8192e_cal_txdesc_chksum(pDesc);
#endif
}

