#include "version.h"
#include "type.h"
#include "ar168.h"
#include "core.h"
#include "apps.h"
#include "tcpip.h"
#include "bank3.h"

#include "_tcp.h"

// local variables
struct tcb _pTcbData[TCB_TSIZE];

BOOLEAN _bReceiveAckFlag;        // acknowledged field is valid 
BOOLEAN _bReceiveRstFlag;        // reset the connection 
BOOLEAN _bReceiveSynFlag;        // synchronize sequence number 
BOOLEAN _bReceiveFinFlag;        // sender has reached end of its stream 

PCHAR _pReceive;		// received TCP packet including TCP head
UCHAR _pReceiveIP[IP_ALEN];		// remote IP of receive TCP packet
PCHAR _pReceiveData;	// received TCP data
USHORT _sReceiveDataLen;

USHORT Tcp_sSrcPort;

/**********************************************************************
 ** TCP misc functions                                            **
 **********************************************************************
 */

USHORT min(USHORT sData1, USHORT sData2)
{
	USHORT sVal;

	sVal = sData1 - sData2;
	if (sVal & 0x8000)		return sData1;
	else					return sData2;
}

/* seq_cmp - Compare two sequance */
/* Return value:	SEQ_PLUS if Seq1 precedes Seq2 
					SEQ_MINUS if Seq1 follows Seq2
					SEQ_EQUAL if Seq1 equal Seq2 	
*/
UCHAR seq_cmp(ULONG lSeq1, ULONG lSeq2)
{
	lSeq1 -= lSeq2;
	if (lSeq1 == 0)
	{
		return SEQ_EQUAL;
	}
	if (lSeq1 & 0x80000000)
	{
		return SEQ_MINUS;
	}
	return SEQ_PLUS;
}

/* seq_cmp - add sLength to sequence number */
void seq_add(USHORT sLength, PCHAR pSeq)
{
	ULONG lSeq;

	lSeq = PCHAR2ULONG(pSeq);
    lSeq += sLength;
	ULONG2PCHAR(lSeq, (PCHAR)pSeq);
}

TCP_HANDLE tcb_alloc()
{
	UCHAR i;
	TCP_HANDLE pTcb;

	pTcb = _pTcbData;
	for (i = 0; i < TCB_TSIZE; i ++, pTcb ++)
	{
		if (pTcb->iState == TCPS_CLOSED)
		{
			return pTcb;
		}
	}
	return NULL;
}

USHORT tcp_howmuch()
{
	USHORT sLen;

	sLen = _sReceiveDataLen;
	if (_bReceiveSynFlag)
	{
		sLen ++;
	}
	if (_bReceiveFinFlag)
	{
		sLen ++;
	}
	return sLen;
}

void tcp_iss(TCP_HANDLE pTcb)
{
	UCHAR pSeq[SEQ_LEN];

	rand_array(pSeq, SEQ_LEN);
	pTcb->seq_snext = PCHAR2ULONG(pSeq);
	pTcb->seq_suna = pTcb->seq_snext;
}
/**********************************************************************
 ** TCP receive functions                                            **
 **********************************************************************
 */
BOOLEAN tcp_ok(TCP_HANDLE pTcb)
{
	ULONG lTcpSeq;

	if (pTcb->iState < TCPS_SYNRCVD)	return TRUE;

	lTcpSeq = PCHAR2ULONG((PCHAR)(_pReceive + TCP_SEQ));
	if (lTcpSeq ==  pTcb->seq_rnext)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

/* tcp_acked - handle inbound ACKs */
UCHAR tcp_acked(TCP_HANDLE pTcb)
{
	ULONG lAck;
	ULONG lVal1;
	UCHAR iVal2;
	USHORT sAcked;

	if (!_bReceiveAckFlag)		return TCP_SYSERROR;

	lAck = PCHAR2ULONG((PCHAR)(_pReceive + TCP_ACK));
	lVal1 = lAck - pTcb->seq_suna;
	if (lVal1 == 0 || (lVal1 & 0x80000000))		
	{
		return 0;
	}
	sAcked = (USHORT)lVal1;

	iVal2 = seq_cmp(lAck, pTcb->seq_snext);
	if (iVal2 == SEQ_PLUS)
	{
		if (pTcb->iState == TCPS_SYNRCVD)
		{
			tcp_reset();
		}
		else
		{
			tcp_ackit(pTcb);
		}
		return 1;
	}

	pTcb->seq_suna = lAck;
	pTcb->iTimer = 0;
	pTcb->iRetry = 0;

	if (pTcb->iCode & TCPF_SYN)
	{
		sAcked --;
		pTcb->iCode &= ~TCPF_SYN;
		pTcb->iFlags &= ~TCBF_FIRSTSEND;
		pTcb->iEvent |= TCP_EVENT_CONNECT;
	}
	if ((pTcb->iCode & TCPF_FIN) && (iVal2 == SEQ_EQUAL))
	{
		sAcked --;
		pTcb->iCode &= ~TCPF_FIN;
		pTcb->iFlags &= ~TCBF_SNDFIN;
	}
	pTcb->sSbStart += sAcked;
	if (pTcb->sSbStart >= TCP_SNDBUF_SIZE)
	{
		pTcb->sSbStart -= TCP_SNDBUF_SIZE;
	}
//	UdpDebugInfo("tcp_acked(): ", pTcb->sSbCount, sAcked);
	pTcb->sSbCount -= sAcked;

	return 2;
}

/* tcp_data - process an input segment's data section */
void tcp_data(TCP_HANDLE pTcb)
{
	if (_sReceiveDataLen)
	{
		pTcb->seq_rnext += _sReceiveDataLen;
		pTcb->iFlags |= TCBF_NEEDOUT;
		pTcb->iEvent |= TCP_EVENT_DATA;
	}
	if (_bReceiveFinFlag)
	{
		pTcb->seq_rnext ++;
		pTcb->iFlags |= TCBF_RDONE | TCBF_NEEDOUT;
		pTcb->iEvent |= TCP_EVENT_CLOSE;
    }
}

void tcp_callback(TCP_HANDLE pTcb)
{
	// call Application layer functions
	if (pTcb->sCallBack)
	{
		TaskRunTcp(pTcb, _sReceiveDataLen, _pReceiveData, pTcb->sCallBack, pTcb->iBankOffset);
	}
}

/* tcp_listen - do LISTEN state processing */
void tcp_listen(TCP_HANDLE pTcb)
{
	PCHAR pBuf;
	TCP_HANDLE pNewTcb;

	if (_bReceiveRstFlag)	return;
	if (_bReceiveAckFlag || !_bReceiveSynFlag)
    {
        tcp_reset();
        return;
    }

	pNewTcb = tcb_alloc();
	if (!pNewTcb)
	{
		UdpDebugString("No TCB to open a new TCP!");
		return;
	}
 	pBuf = (PCHAR)malloc(TCP_SNDBUF_SIZE);
	if (!pBuf)	
	{
		tcp_reset();
		UdpDebugString("No space to open a new TCP!");
		heap_debug();
		return;
	}
	
	pNewTcb->pSndBuf = pBuf;
	pNewTcb->iState = TCPS_SYNRCVD;
	pNewTcb->sLocalPort = pTcb->sLocalPort;
	pNewTcb->iBankOffset = pTcb->iBankOffset;
	pNewTcb->sCallBack = pTcb->sCallBack;
    pNewTcb->iCode = TCPF_SYN;
    pNewTcb->iFlags = TCBF_NEEDOUT;
	memcpy4(pNewTcb->pRemoteIP, _pReceiveIP);
	pNewTcb->sRemotePort = PCHAR2USHORT((PCHAR)(_pReceive+TCP_SPORT));
	pNewTcb->seq_rnext = PCHAR2ULONG((PCHAR)(_pReceive + TCP_SEQ));
	tcp_iss(pNewTcb);
	pNewTcb->seq_rnext ++;
	pNewTcb->iEvent |= TCP_EVENT_ACCEPT;

	tcp_callback(pNewTcb);
}

/* tcp_synsent - do SYN_SENT state processing */
void tcp_synsent(TCP_HANDLE pTcb)
{
	ULONG lAck;

	lAck = PCHAR2ULONG((PCHAR)(_pReceive + TCP_ACK));
    if (_bReceiveAckFlag && ((lAck <= pTcb->seq_suna) || (lAck > pTcb->seq_snext)))
    {
		tcp_reset();
		return;
    }
	if (_bReceiveRstFlag)
	{
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}
    if (!_bReceiveSynFlag)        return;

    pTcb->seq_rnext = PCHAR2ULONG((PCHAR)(_pReceive + TCP_SEQ));
	tcp_acked(pTcb);
	pTcb->seq_rnext ++;
	pTcb->iFlags |= TCBF_NEEDOUT;
    if (pTcb->iCode & TCPF_SYN)   /* our SYN not Acked */
    {
        pTcb->iState = TCPS_SYNRCVD;
    }
    else
    {
        pTcb->iState = TCPS_ESTABLISHED;
    }
	tcp_callback(pTcb);
}

/* tcp_synrcvd - do SYN_RCVD state processing */
void tcp_synrcvd(TCP_HANDLE pTcb)
{
	if (_bReceiveRstFlag)
	{
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}
	if (_bReceiveSynFlag)
	{
		tcp_reset();
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}
    if (tcp_acked(pTcb) == TCP_SYSERROR)
	{
		return;
	}
	pTcb->iState = TCPS_ESTABLISHED; 
	tcp_data(pTcb);
	if (pTcb->iFlags & TCBF_RDONE)
	{
		pTcb->iState = TCPS_CLOSEWAIT; 
	}

	tcp_callback(pTcb);
}

/* tcp_established - do ESTABLISHED state processing */
void tcp_established(TCP_HANDLE pTcb)
{
	if (_bReceiveRstFlag)
	{
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}
	if (_bReceiveSynFlag)
	{
		tcp_reset();
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}

    if (tcp_acked(pTcb) == TCP_SYSERROR)		return;

	tcp_data(pTcb);	/* for data + FIN ACKing */

	if (pTcb->iFlags & TCBF_RDONE)
	{
		pTcb->iState = TCPS_CLOSEWAIT;
	}
	tcp_callback(pTcb);
}

/* tcp_fin1 - do FIN_WAIT_1 state processing */
void tcp_fin1(TCP_HANDLE pTcb)
{
	if (_bReceiveRstFlag)
	{
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}
	if (_bReceiveSynFlag)
	{
		tcp_reset();
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}

    if (tcp_acked(pTcb) == TCP_SYSERROR)		return;

	tcp_data(pTcb);

	if (pTcb->iFlags & TCBF_RDONE)
    {
        if (pTcb->iCode & TCPF_FIN)   /* FIN not Acked */
		{
            pTcb->iState = TCPS_CLOSING;
		}
        else
        {
			/* wake closer now */
			pTcb->iEvent |= TCP_EVENT_ABORT;
        }
    }
    else 
    {
		if (!(pTcb->iCode & TCPF_FIN))
		{
			pTcb->iState = TCPS_FINWAIT2;
		}
    }
	tcp_callback(pTcb);
}

/* tcp_fin2 - do FIN_WAIT_2 state processing */
void tcp_fin2(TCP_HANDLE pTcb)
{
	if (_bReceiveRstFlag)
	{
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}
	if (_bReceiveSynFlag)
	{
		tcp_reset();
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}

    if (tcp_acked(pTcb) == TCP_SYSERROR)		return;

	tcp_data(pTcb);

    if (pTcb->iFlags & TCBF_RDONE)
    {
		/* wake closer now */
		pTcb->iEvent |= TCP_EVENT_ABORT;
    }
	tcp_callback(pTcb);
}

/* tcp_closing - do CLOSING state processing */
void tcp_closing(TCP_HANDLE pTcb)
{
	if (_bReceiveRstFlag)
	{
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}
	if (_bReceiveSynFlag)
	{
		tcp_reset();
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}

    tcp_acked(pTcb);
    if (!(pTcb->iCode & TCPF_FIN))
    {
		/* wake closer now */
		pTcb->iEvent |= TCP_EVENT_ABORT;
	}
	tcp_callback(pTcb);
}

/* tcp_closewait - do CLOSE_WAIT state input processing */
void tcp_closewait(TCP_HANDLE pTcb)
{
	if (_bReceiveRstFlag)
	{
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}
	if (_bReceiveSynFlag)
	{
		tcp_reset();
		pTcb->iEvent |= TCP_EVENT_RESET;
		return;
	}

    tcp_acked(pTcb);
	tcp_callback(pTcb);
}

/* tcp_lastack - do LAST_ACK state input processing */
void tcp_lastack(TCP_HANDLE pTcb)
{
	tcp_closing(pTcb);
}

/**********************************************************************
 ** TCP send functions                                               **
 **********************************************************************
 */
USHORT tcp_tosend(TCP_HANDLE pTcb)
{
	USHORT sSend;

	sSend = (USHORT)(pTcb->seq_snext - pTcb->seq_suna);
	sSend = pTcb->sSbCount - sSend;
	if (sSend & 0x8000)		return 0;

	if (pTcb->iCode & TCPF_SYN)		sSend ++;
	if (pTcb->iFlags & TCBF_SNDFIN)		sSend ++;
	return sSend;
}

USHORT _TcpCheckSum(USHORT sLen, PCHAR pData, PCHAR pSrcIP, PCHAR pDstIP)
{
	ULONG lSum;

	// 12 bytes pseudo-header checksum
	lSum = short_sum(pSrcIP, IP_ALEN);
	lSum += short_sum(pDstIP, IP_ALEN);
	lSum += P_XCHGBYTE(sLen + IPT_TCP);

	USHORT2PCHAR(0, (PCHAR)(pData + TCP_CKSUM));

	lSum += short_sum(pData, sLen);
	return ip_checksum(lSum);
}

void tcp_reset()
{
	PCHAR pSend;
	USHORT sSegLen;

	/* no RESETs to RESETs */
    if (_bReceiveRstFlag)	return;

	pSend = Adapter_pIpBuf;
	if (_bReceiveAckFlag)
	{
		memcpy4((PCHAR)(pSend+TCP_SEQ), (PCHAR)(_pReceive + TCP_ACK));
		pSend[TCP_CODE] = TCPF_RST;
	}
	else
	{
		memset((PCHAR)(pSend+TCP_SEQ), 0, SEQ_LEN);
		pSend[TCP_CODE] = TCPF_RST|TCPF_ACK;
	}
	sSegLen = tcp_howmuch();
    memcpy4((PCHAR)(pSend+TCP_ACK), (PCHAR)(_pReceive + TCP_SEQ));
    seq_add(sSegLen, (PCHAR)(pSend+TCP_ACK));
	USHORT2PCHAR(0, (PCHAR)(pSend+TCP_WINDOW));
	USHORT2PCHAR(0, (PCHAR)(pSend+TCP_URGPTR));
	pSend[TCP_OFFSET] = TCPHOFFSET;
	memcpy((PCHAR)(pSend+TCP_SPORT), (PCHAR)(_pReceive+TCP_DPORT), 2);
	memcpy((PCHAR)(pSend+TCP_DPORT), (PCHAR)(_pReceive+TCP_SPORT), 2);
	USHORT2PCHAR_L(_TcpCheckSum(TCP_DATA, pSend, Sys_pIpAddress, _pReceiveIP), (PCHAR)(pSend + TCP_CKSUM));

	TaskIpSendData(TCP_DATA, _pReceiveIP, IPT_TCP);
}

void tcp_ackit(TCP_HANDLE pTcb)
{
	PCHAR pSend;

	if (_bReceiveRstFlag)		return;
	pSend = Adapter_pIpBuf;

	ULONG2PCHAR(pTcb->seq_snext, (PCHAR)(pSend+TCP_SEQ));
	ULONG2PCHAR(pTcb->seq_rnext, (PCHAR)(pSend+TCP_ACK));
	pSend[TCP_CODE] = TCPF_ACK;
	USHORT2PCHAR(TCP_RWINDOW_SIZE, (PCHAR)(pSend+TCP_WINDOW));
	USHORT2PCHAR(0, (PCHAR)(pSend+TCP_URGPTR));
	pSend[TCP_OFFSET] = TCPHOFFSET;
	memcpy((PCHAR)(pSend+TCP_SPORT), (PCHAR)(_pReceive+TCP_DPORT), 2);
	memcpy((PCHAR)(pSend+TCP_DPORT), (PCHAR)(_pReceive+TCP_SPORT), 2);
	USHORT2PCHAR_L(_TcpCheckSum(TCP_DATA, pSend, Sys_pIpAddress, _pReceiveIP), (PCHAR)(pSend + TCP_CKSUM));

	TaskIpSendData(TCP_DATA, _pReceiveIP, IPT_TCP);
}

const UCHAR _cSynFlags[4] = {TPO_MSS, 4, TCP_MSS_HIGH, TCP_MSS_LOW};	// next 4: 1, 1, 4, 2

void tcp_send(TCP_HANDLE pTcb, BOOLEAN bRexmit)
{
	UCHAR iHeadLen;
	USHORT sOffset, sDataLen, sIndex;
	ULONG lSeq;
	PCHAR pSend;
	PCHAR pSendData;

	// compute the packet length and offset in sndbuf
	if (bRexmit || (pTcb->iCode & TCPF_SYN))
	{
		sOffset = 0;
	}
	else
	{
		sOffset = (USHORT)(pTcb->seq_snext - pTcb->seq_suna);
	}
	sDataLen = pTcb->sSbCount - sOffset;
	sDataLen = min(sDataLen, TCP_SND_MSS);

	// fill TCP head
	pSend = Adapter_pIpBuf;
	USHORT2PCHAR(TCP_RWINDOW_SIZE, (PCHAR)(pSend+TCP_WINDOW));
	USHORT2PCHAR(0, (PCHAR)(pSend+TCP_URGPTR));
	USHORT2PCHAR(pTcb->sLocalPort, (PCHAR)(pSend+TCP_SPORT));
	USHORT2PCHAR(pTcb->sRemotePort, (PCHAR)(pSend+TCP_DPORT));

	if (!bRexmit)	lSeq = pTcb->seq_snext;
	else			lSeq = pTcb->seq_suna;
	ULONG2PCHAR(lSeq, (PCHAR)(pSend+TCP_SEQ));
	ULONG2PCHAR(pTcb->seq_rnext, (PCHAR)(pSend+TCP_ACK));

	if ((pTcb->iFlags & TCBF_SNDFIN) && ((ULONG)(lSeq + sDataLen) == pTcb->seq_slast))
	{
		pTcb->iCode |= TCPF_FIN;
	}
	pSend[TCP_CODE] = pTcb->iCode;
	if (!(pTcb->iFlags & TCBF_FIRSTSEND))
	{
		pSend[TCP_CODE] |= TCPF_ACK;
	}
    if (pTcb->iCode & TCPF_SYN)
    {
		memcpy4((PCHAR)(pSend + TCP_DATA), _cSynFlags);
		pSend[TCP_OFFSET] = 0x60;
    }
    else
	{
		pSend[TCP_OFFSET] = TCPHOFFSET;
	}
	iHeadLen = pSend[TCP_OFFSET] >> 2;
	if (sDataLen)	pSend[TCP_CODE] |= TCPF_PSH;

	// fill Tcp Data
	pSendData = (PCHAR)(pSend + iHeadLen);
	sOffset += pTcb->sSbStart;
	for (sIndex = 0; sIndex < sDataLen; sIndex ++)
	{
		if (sOffset >= TCP_SNDBUF_SIZE)	
		{
			sOffset -= TCP_SNDBUF_SIZE;
		}
		*pSendData = pTcb->pSndBuf[sOffset];
		pSendData ++;
		sOffset ++;
	}

	pTcb->iFlags &= ~TCBF_NEEDOUT;
	if (bRexmit)
	{
		lSeq = pTcb->seq_suna + sDataLen;
		if (lSeq > pTcb->seq_snext)		pTcb->seq_snext = lSeq;
	}
	else
	{
		sIndex = sDataLen;
		if (pTcb->iCode & TCPF_SYN)		sIndex ++;
		if (pTcb->iCode & TCPF_FIN)		sIndex ++;
		pTcb->seq_snext += sIndex;
	}

	sDataLen += iHeadLen;
	// compute a checksum 
	USHORT2PCHAR_L(_TcpCheckSum(sDataLen, pSend, Sys_pIpAddress, pTcb->pRemoteIP), (PCHAR)(pSend + TCP_CKSUM));

	// send the packet
	TaskIpSendData(sDataLen, pTcb->pRemoteIP, IPT_TCP);

//	UdpDebugTcpHead(iHeadLen);
}


/**********************************************************************
 ** API functions                                                    **
 **********************************************************************
 */

void TcpInit()
{
	memset((PCHAR)_pTcbData, 0, TCB_LENGTH*TCB_TSIZE);
	Tcp_sSrcPort = TCP_SRC_PORT + rand();;
}

void TcpSetTimeout(TCP_HANDLE pTcb, UCHAR iTimeout)
{
	if (pTcb)
	{
		pTcb->iUserTimeout = iTimeout;
	}
}

UCHAR TcpGetEvent(TCP_HANDLE pTcb)
{
	if (!pTcb)	return 0;
	return pTcb->iEvent;
}

void TcpClearEvent(TCP_HANDLE pTcb)
{
	if (pTcb)
	{
		pTcb->iEvent = 0;
	}
}

void TcpGetRemoteIP(TCP_HANDLE pTcb, PCHAR pRemoteIP)
{
	if (pTcb)
	{
		memcpy4(pRemoteIP, pTcb->pRemoteIP);
	}
}

TCP_HANDLE TcpListen(USHORT sSrcPort, UCHAR iBankOffset, USHORT sCallBack)
{
	TCP_HANDLE pTcb;

	pTcb = tcb_alloc();
	if (pTcb)
	{
		pTcb->iState = TCPS_LISTEN;
		pTcb->sLocalPort = sSrcPort;
		pTcb->iBankOffset = iBankOffset;
		pTcb->sCallBack = sCallBack;
	}

	return pTcb;
}

TCP_HANDLE TcpOpen(PCHAR pDstIP, USHORT sDstPort, USHORT sSrcPort, UCHAR iBankOffset, USHORT sCallBack)
{
	TCP_HANDLE pTcb;
	PCHAR pBuf;

	pTcb = tcb_alloc();
	if (!pTcb)		return (TCP_HANDLE)NULL;

	pBuf = (PCHAR)malloc(TCP_SNDBUF_SIZE);
	if (!pBuf)		return (TCP_HANDLE)NULL;

    pTcb->iState = TCPS_SYNSENT;
    pTcb->iCode = TCPF_SYN;
	pTcb->iFlags = TCBF_NEEDOUT | TCBF_FIRSTSEND;
	pTcb->iBankOffset = iBankOffset;
	pTcb->sCallBack = sCallBack;
	if (!sSrcPort)
	{
		pTcb->sLocalPort = Tcp_sSrcPort;
		Tcp_sSrcPort ++;
		if (Tcp_sSrcPort >= (TCP_SRC_PORT + 1000))
		{
			Tcp_sSrcPort = TCP_SRC_PORT;
		}
	}
	else
	{
		pTcb->sLocalPort = sSrcPort;
	}
	pTcb->sRemotePort = sDstPort;
	memcpy4(pTcb->pRemoteIP, pDstIP);
	pTcb->pSndBuf = pBuf;
	tcp_iss(pTcb);

	tcp_send(pTcb, FALSE);
	return pTcb;
}

/* TcpClose - will not send FIN packet, need to call tcp_send to send packet out */

void TcpClose(TCP_HANDLE pTcb)
{
	if (!pTcb)		return;

    if (pTcb->iState == TCPS_ESTABLISHED)
	{
		pTcb->iState = TCPS_FINWAIT1;
	}
    else if (pTcb->iState == TCPS_CLOSEWAIT)
	{
        pTcb->iState = TCPS_LASTACK;
	}
	else
	{
		return;
	}

	pTcb->iFlags |= TCBF_SNDFIN | TCBF_NEEDOUT;
	pTcb->seq_slast = pTcb->seq_suna + pTcb->sSbCount;
}

/* TcpFree -  Free the resources of this socket */
void TcpFree(TCP_HANDLE pTcb)
{
	if (pTcb)
	{
		if (pTcb->pSndBuf)
		{
			free(pTcb->pSndBuf);
		}
		memset((PCHAR)pTcb, 0, TCB_LENGTH);
	}
}

BOOLEAN TcpCanWrite(TCP_HANDLE pTcb, USHORT sMaxLen)
{
	if (pTcb->iState != TCPS_ESTABLISHED && pTcb->iState != TCPS_CLOSEWAIT)
	{
		return FALSE;
	}

//	UdpDebugInfo("TcpCanWrite(): ", pTcb->sSbCount, 0);
	if ((USHORT)( pTcb->sSbCount + sMaxLen) > TCP_SNDBUF_SIZE)
	{
		return FALSE;
	}
	else
	{
		return TRUE;
	}
}

void TcpWriteData(TCP_HANDLE pTcb, PCHAR pData, USHORT sLen)
{
	USHORT sIndex, sOffset;

	if ((USHORT)(pTcb->sSbCount + sLen) > TCP_SNDBUF_SIZE)
	{
		UdpDebugString("Tcp Send Buffer Overflow");
		return;
	}

	if (pTcb)
	{
		sOffset = pTcb->sSbStart + pTcb->sSbCount;
		for (sIndex = 0; sIndex < sLen; sIndex ++)
		{
			if (sOffset >= TCP_SNDBUF_SIZE)	
			{
				sOffset -= TCP_SNDBUF_SIZE;
			}
			pTcb->pSndBuf[sOffset] = *pData;
			pData ++;
			sOffset ++;
		}
		pTcb->sSbCount += sLen;
		pTcb->iFlags |= TCBF_NEEDOUT;
	}
}

void TcpWriteStr(TCP_HANDLE pTcb, PCHAR pStr)
{
	TcpWriteData(pTcb, pStr, strlen(pStr));
}

void TcpStartSend(TCP_HANDLE pTcb)
{
	UCHAR i;
	USHORT sSend;
	if (!pTcb)		return;
	if (pTcb->iState == TCPS_CLOSED)	return;
	 
	for (i = 0; i < 2; i ++)
	{
		sSend = tcp_tosend(pTcb);
		if (!sSend)
		{
			if (pTcb->iFlags & TCBF_NEEDOUT)
			{
				tcp_ackit(pTcb);	/* just an ACK */
			}
			return;
		}
		else
		{
			tcp_send(pTcb, FALSE);
		}
	}
}

void TcpRun(PACKET_LIST * p)
{
	UCHAR i, iVal, iHeadLen;
#ifdef VERIFY_CHECKSUM
	USHORT sCheckSum;
#endif
	USHORT sLen;
	TCP_HANDLE pTempTcb;
	TCP_HANDLE pTcb;
	TCP_HANDLE pListenTcb;
	PCHAR pRecvIP;

	pRecvIP = p->pRecvIP;
	sLen = p->sLen;

	if (sLen > IP_DATA_MAX_SIZE)	return;

	_pReceive = Adapter_pReceivePacket;
	
	memcpy4(_pReceiveIP, pRecvIP);

	// check if packet length is valid
	iHeadLen = (_pReceive[TCP_OFFSET] & 0xf0) >> 2;
	if (sLen < iHeadLen)
	{
//		UdpDebugString("Tcp packet length error!");
		return;
	}
	_sReceiveDataLen = sLen - iHeadLen;
	_pReceiveData = (PCHAR)(_pReceive + iHeadLen);

	// verify TCP checksum
#ifdef VERIFY_CHECKSUM
	sCheckSum = PCHAR2USHORT_L((PCHAR)(_pReceive+TCP_CKSUM));
//	if (sCheckSum != _TcpCheckSum(sLen, _pReceive, pRecvIP, (PCHAR)(pRecvIP + IP_ALEN)))
	if (sCheckSum != _TcpCheckSum(sLen, _pReceive, pRecvIP, Sys_pIpAddress))
	{
		UdpDebugString("Tcp Checksum Error!");
		return;
	}
#endif

	iVal = _pReceive[TCP_CODE];
	_bReceiveAckFlag = (iVal & TCPF_ACK) ? TRUE : FALSE;
    _bReceiveRstFlag = (iVal & TCPF_RST) ? TRUE : FALSE;
    _bReceiveSynFlag = (iVal & TCPF_SYN) ? TRUE : FALSE;
    _bReceiveFinFlag = (iVal & TCPF_FIN) ? TRUE : FALSE;

	// do TCP port demultiplexing
	pListenTcb = NULL;
	pTcb = NULL;
	pTempTcb = _pTcbData;
	for (i = 0; i < TCB_TSIZE; i ++, pTempTcb ++)
	{
		if (pTempTcb->iState == TCPS_CLOSED)	continue;
		if (pTempTcb->sLocalPort == PCHAR2USHORT((PCHAR)(_pReceive+TCP_DPORT)))
		{
			if (!memcmp(pTempTcb->pRemoteIP, pRecvIP, IP_ALEN)
				&& pTempTcb->sRemotePort == PCHAR2USHORT((PCHAR)(_pReceive+TCP_SPORT)))
			{
				pTcb = pTempTcb;
				break;
		
			}
			if (pTempTcb->iState == TCPS_LISTEN)
			{
				pListenTcb = pTempTcb;
			}
		}
	}

	if (!pTcb)	
	{
		if (_bReceiveSynFlag)	pTcb = pListenTcb;
	}
	if (!pTcb)	
	{
//		UdpDebugString("Unknown Tcp data");
		tcp_reset();
		return;
	}

	pTcb->iUserTimer = 0;
	// handle packets according to current state
	if (tcp_ok(pTcb))
	{
		switch (pTcb->iState)
		{
		case TCPS_LISTEN:
			tcp_listen(pTcb);
			break;
		case TCPS_SYNSENT:
			tcp_synsent(pTcb);
			break;
		case TCPS_SYNRCVD:
			tcp_synrcvd(pTcb);
			break;
		case TCPS_ESTABLISHED:
			tcp_established(pTcb);
			break;
		case TCPS_FINWAIT1:
			tcp_fin1(pTcb);
			break;
		case TCPS_FINWAIT2:
			tcp_fin2(pTcb);
			break;
		case TCPS_CLOSING:
			tcp_closing(pTcb);
			break;
		case TCPS_CLOSEWAIT:
			tcp_closewait(pTcb);
			break;
		case TCPS_LASTACK:
			tcp_lastack(pTcb);
			break;
		}
	}
	else
	{
		tcp_ackit(pTcb);
	}
}

const UCHAR _cTcpRetryTimer[TCP_MAX_RETRY] = {TCP_MINRXT, TCP_MINRXT, TCP_MINRXT*2, TCP_MINRXT*2, TCP_MINRXT*4};

void TcpTimer()
{
	UCHAR i;
	TCP_HANDLE pTcb;
	
	pTcb = _pTcbData;
	for (i = 0; i < TCB_TSIZE; i ++, pTcb ++)
	{
		if (pTcb->iState == TCPS_CLOSED || pTcb->iState == TCPS_LISTEN)	continue;
		if (!pTcb->sCallBack)		continue;

		if (pTcb->iUserTimeout)
		{
			pTcb->iUserTimer ++;
			if (pTcb->iUserTimer >= pTcb->iUserTimeout)
			{
				pTcb->iEvent = TCP_EVENT_USERTO;
				TaskRunTcp(pTcb, 0, 0, pTcb->sCallBack, pTcb->iBankOffset);
				continue;
			}
		}
		// handle retransmission
		if (pTcb->seq_suna != pTcb->seq_snext)
		{
			pTcb->iTimer ++;
			if (pTcb->iTimer >= _cTcpRetryTimer[pTcb->iRetry])
			{
				pTcb->iTimer = 0;
				pTcb->iRetry ++;
				if (pTcb->iRetry < TCP_MAX_RETRY)
				{
					UdpDebugString("tcp retry");
					pTcb->iFlags |= TCBF_NEEDOUT;
					tcp_send(pTcb, TRUE);
				}
				else
				{
					pTcb->iEvent = TCP_EVENT_RETRYTO;
					TaskRunTcp(pTcb, 0, 0, pTcb->sCallBack, pTcb->iBankOffset);
				}
			}
		}
	}
}

