/*-------------------------------------------------------------------------
   Network function copy right information

   Copyright (C) 2006-2009. Lin, Rongrong <woody@palmmicro.com>
                            Tang, Li      <tangli@palmmicro.com>
                            
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This software 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-------------------------------------------------------------------------*/

#include "version.h"
#include "type.h"
#ifdef RTL8019AS
#include "ne2000.h"
#elif defined DM9003EP
#include "dm9000.h"
#elif defined KSZ8842
#include "ksz8842.h"
#endif
#include "ar168.h"
#include "core.h"
#include "apps.h"
#include "tcpip.h"
#include "bank2.h"

#define NET_DISCOVER_TIME			120		// the maximum time delay before restarting initliazation procedure

USHORT _sLayer2Qos;
BOOLEAN _bVLan;

#ifdef RTL8019AS
USHORT _sCurPacketAddr;
USHORT _sPrevPacketAddr;
BOOLEAN _bMiniRunned;
#endif

#if defined DM9003EP || defined KSZ8842
BOOLEAN _bPort1Linked;
BOOLEAN _bPort2Linked;
BOOLEAN _bPort1Data;
#endif

#ifdef KSZ8842
extern UCHAR _iRegBank;
#define NET_HEADER_SIZE		KSZ8842_HEADER_SIZE
#else
#define NET_HEADER_SIZE		DM9000_HEADER_SIZE
#endif

BOOLEAN Net_bConnected;
BOOLEAN Dhcp_bRenewing;

UCHAR Adapter_pPacketBuf[ETHERNET_MAX_SIZE + 1 + IP_DATA_MAX_SIZE + 1];

PACKET_LIST * _pFragList;

UCHAR _iDiscoverTimer;

void NetInit()
{
#ifdef CALL_NONE
	_bVLan = FALSE;
#else
	USHORT sVLanID;

	sVLanID = OptionsGetShort(OPT_VLAN_TAG) & VLAN_ID_MASK;
	if (sVLanID)
	{
		_sLayer2Qos = OptionsGetByte(OPT_QOS_PRIORITY) & 0x07;
		_sLayer2Qos <<= 13;
		_sLayer2Qos += sVLanID; 
		_bVLan = TRUE;
	}
	else
	{
		_bVLan = FALSE;
		_sLayer2Qos = 0;
	}
#endif

#ifdef RTL8019AS
	_bMiniRunned = FALSE;
	_sPrevPacketAddr = _sCurPacketAddr = NE2000_RAM_BASE + XMIT_BUF_LEN;
#endif

#if defined DM9003EP || defined KSZ8842
	_bPort1Linked = _bPort2Linked = FALSE;
#endif

	Net_bConnected = Sys_iIpType ? FALSE : TRUE;	// set TRUE when using static ip
	Dhcp_bRenewing = FALSE;
	_iDiscoverTimer = 0;

	_pFragList = NULL;

	// MAC layer
	memset(Adapter_pPacketBuf, 0xff, HW_ALEN);
	memcpy((PCHAR)(Adapter_pPacketBuf + EP_SRC), Sys_pMacAddress, HW_ALEN);
	USHORT2PCHAR((Sys_iIpType == IP_TYPE_PPPOE) ? EPT_PPPOE : EPT_IP, (PCHAR)(Adapter_pPacketBuf + EP_TYPE));

	if (Sys_iIpType != IP_TYPE_STATIC)
	{
		memset(Sys_pIpAddress, 0, IP_ALEN);
	}

#ifdef RTL8019AS
	Ne2000Init();
#elif defined DM9003EP
	Dm9003Init();
#elif defined KSZ8842
	Ksz8842Init();
#endif

	IpInit();
	ArpInit();
	UdpInit();
//	UdpDebugOpen();
	SyslogOpen();
}

#ifndef CALL_NONE

void NetDiscover()
{
	if (Sys_iIpType == IP_TYPE_DHCP)
	{
		DhcpInit();
		DhcpDiscover();
#ifdef OEM_IP20
		TaskUIHandler(UI_STBY_DISPLAY_IDS2, IDS_DHCP_START);
#else
		TaskUIHandler(UI_STBY_DISPLAY_IDS1, IDS_DHCP_START);
#endif
	}
	else if (Sys_iIpType == IP_TYPE_PPPOE)
	{
		PppoeInit();
		PppoeDiscover();
#ifdef OEM_IP20
		TaskUIHandler(UI_STBY_DISPLAY_IDS2, IDS_PPPOE_START);
#else
		TaskUIHandler(UI_STBY_DISPLAY_IDS1, IDS_PPPOE_START);
#endif
	}
}

#if defined DM9003EP || defined KSZ8842
void NetLinkTimer()
{
	BOOLEAN bLinked;

	if (!Net_bConnected)	return;

#ifdef DM9003EP
	bLinked = (dm9k_read_port_reg(1, SW_PSTATUS) & 0x01) ? TRUE : FALSE;
#elif defined KSZ8842
	// check status in P1SR[5]/link good
	bLinked = (ksz8842_read_reg_byte(REG_P1SR_BANK, REG_P1SR_OFFSET) & 0x20) ? TRUE : FALSE;
#endif
	if (bLinked != _bPort1Linked)
	{
		_bPort1Linked = bLinked;
		TaskUIHandler(UI_CALL_DISPLAY_IDS0, bLinked ? IDS_PORT1_LINKED : IDS_PORT1_UNPLUGGED);
	}

#ifdef DM9003EP
	bLinked = (dm9k_read_port_reg(0, SW_PSTATUS) & 0x01) ? TRUE : FALSE;
#elif defined KSZ8842
	// check status in P2SR[5]/link good
	bLinked = (ksz8842_read_reg_byte(REG_P2SR_BANK, REG_P2SR_OFFSET) & 0x20) ? TRUE : FALSE;
#endif
	if (bLinked != _bPort2Linked)
	{
		_bPort2Linked = bLinked;
		TaskUIHandler(UI_CALL_DISPLAY_IDS1, bLinked ? IDS_PORT2_LINKED : IDS_PORT2_UNPLUGGED);
	}
/*
	if (_bPort1Data)
	{
		UdpDebugString("data from port1");
	}
	else
	{
		UdpDebugString("data from port2");
	}
*/
}
#endif

#endif	// !CALL_NONE

void NetTimer()
{
#ifndef CALL_NONE

#if defined DM9003EP || defined KSZ8842
	NetLinkTimer();
#endif

	if (Sys_iIpType)
	{
		if (!Net_bConnected)
		{
			_iDiscoverTimer ++;
			if (_iDiscoverTimer == NET_DISCOVER_TIME)
			{
				_iDiscoverTimer = 0;
				NetDiscover();
				return;
			}
		}
		
		if (Sys_iIpType == IP_TYPE_DHCP)
		{
			DhcpTimer();
		}
		else if (Sys_iIpType == IP_TYPE_PPPOE)
		{
			PppoeTimer();
		}
	}
#endif

	if (Net_bConnected)
	{
#ifndef CALL_NONE
		DnsTimer();
		SntpTimer();
#ifdef CALL_SIP
		StunTimer();
#endif
#endif
		TftpTimer();
	}

	ArpTimer();
}

#if defined DM9003EP || defined KSZ8842
void _SaveFragData(PACKET_LIST * p)
{
	PCHAR pData;

	if (p->sLen)
	{
		pData = (PCHAR)malloc(p->sLen + 1);		// KSZ8842 always reads even bytes
		if (pData)
		{
			NetRead(pData, p->sLen);
			p->sAddr = (USHORT)pData;
		}
	}
}
#endif

void _FragAddTail(PACKET_LIST * p)
{
	PACKET_LIST * pCur;
	PACKET_LIST * pPrev;
	PACKET_LIST * pNext;

	pCur = (PACKET_LIST *)malloc(sizeof(PACKET_LIST));
	if (!pCur)	return;

	memcpy((PCHAR)pCur, (PCHAR)p, sizeof(PACKET_LIST));
	// add to tail, copy pRecvIP data point
	pPrev = _pFragList;
	do
	{
//		pPrev->pRecvIP = pCur->pRecvIP;
//		memcpy4(pPrev->pRecvIP, pCur->pRecvIP);
		pNext = pPrev->next;
		if (pNext)
		{
			pPrev = pNext;
		}
		else
		{
			pPrev->next = pCur;
			break;
		}
	} while (1);
}

void FreePacketList()
{
	PACKET_LIST * pCur;
	PACKET_LIST * pNext;

	pCur = _pFragList;
	while (pCur)
	{
#if defined DM9003EP || defined KSZ8842
		if (pCur->sAddr)
		{
			free((void *)pCur->sAddr);
		}
#endif
		pNext = pCur->next;
		free(pCur);
		pCur = pNext;
	}
	_pFragList = NULL;
}

PACKET_LIST * _CheckIpHead(PACKET_LIST * p, PCHAR pHead)
{
	UCHAR iHeadLen;
	USHORT sFragOffset;
	PACKET_LIST * pCur;
/*	UCHAR iLen;
	USHORT sCheckSum;
	ULONG lSum;
	PCHAR pExtra;
*/

	p->sLen = PCHAR2USHORT((PCHAR)(pHead + IP_LEN));
/*	if (p->sLen < IP_DATA || p->sLen > (ETHERNET_MAX_SIZE - EP_DATA))
	{	// error length
		return NULL;
	}
*/
	iHeadLen = (pHead[IP_VERLEN] & 0x0f) << 2;

/*
	// check IP checksum
	sCheckSum = PCHAR2USHORT_L((PCHAR)(pHead + IP_CKSUM));
	USHORT2PCHAR(0, (PCHAR)(pHead + IP_CKSUM));
	lSum = short_sum(pHead, IP_DATA);
	if (iHeadLen > IP_DATA)
    {
		iLen = iHeadLen - IP_DATA;
		pExtra = (PCHAR)malloc(iLen);
		if (pExtra)
		{
			Ne2000Read(p->sAddr + IP_DATA, pExtra, iLen);
			lSum += short_sum(pExtra, iLen);
			free(pExtra);
		}
    }
	if (sCheckSum != ip_checksum(lSum))
	{
		UdpDebugString("IP checksum error");
		return NULL;
	}
*/	
	p->sLen -= iHeadLen;
#ifdef RTL8019AS
	p->sAddr += iHeadLen;
#elif defined DM9003EP || defined KSZ8842
	p->sAddr = 0;	// NULL
	if (iHeadLen > IP_DATA)
	{
		iHeadLen -= IP_DATA;
		NetReadBypass(iHeadLen);
	}
#endif
//	p->pRecvIP = (PCHAR)(pHead + IP_SRC);
	memcpy4(p->pRecvIP, (PCHAR)(pHead + IP_SRC));
	p->next = NULL;

	sFragOffset = PCHAR2USHORT((PCHAR)(pHead + IP_FRAGOFF)) & 0x3fff;
	if (sFragOffset)
	{
		if (sFragOffset & IP_MF)
		{
			if (sFragOffset & IP_FRAGOFF_MASK)
			{
//				UdpDebugString("more fragment");
				if (_pFragList)
				{
#if defined DM9003EP || defined KSZ8842
					_SaveFragData(p);
#endif
					_FragAddTail(p);
				}
				// first fragment lost, discard this too
			}
			else
			{
//				UdpDebugString("first fragment");
				// free previous fragments, start a new one. 
				// we only handle 1 fragment IP packet
				FreePacketList();
				pCur = (PACKET_LIST *)malloc(sizeof(PACKET_LIST));
				_pFragList = pCur;	// make me head
				if (pCur)
				{
#if defined DM9003EP || defined KSZ8842
					_SaveFragData(p);
#endif
					memcpy((PCHAR)pCur, (PCHAR)p, sizeof(PACKET_LIST));
				}
			}
			return NULL;	// handle it later
		}
		else
		{
//			UdpDebugString("last fragment");
			if (_pFragList)
			{
				_FragAddTail(p);
				p = _pFragList;
//				_pFragList = NULL;
			}
			else
			{
				return NULL;	// first fragment lost, discard it
			}
		}
	}
	return p;
}

#ifdef RTL8019AS

BOOLEAN _IPReceive(PACKET_LIST * p, BOOLEAN bMini)
{
	UCHAR iVal;
	PACKET_LIST * pCur;
	UCHAR pHead[IP_DATA];
	BOOLEAN bRet;

	Ne2000Read(p->sAddr, pHead, IP_DATA);

	pCur = _CheckIpHead(p, pHead);

	if (!pCur)	return FALSE;

	bRet = FALSE;
	iVal = pHead[IP_PROTO];
    if (iVal == IPT_UDP)
    {
		if (bMini)
		{
			bRet = UdpMiniRun(pCur);
		}
		else
		{
			UdpRun(pCur);
		}
    }
	else if (!bMini)
	{
		if (Net_bConnected)
		{
			if (iVal == IPT_TCP)
			{
#ifndef CALL_NONE
				Ne2000Read(pCur->sAddr, Adapter_pReceivePacket, pCur->sLen);
				TaskTcpRun(pCur);
#endif
			}
			else if (iVal == IPT_ICMP)
			{
				IcmpRun(pCur);
			}
		}
    }

	if (_pFragList == pCur)
	{
		FreePacketList();
	}

	return bRet;
}

void _HandleARP(USHORT sAddr, BOOLEAN bMini)
{
	UCHAR pBuf[ARP_LENGTH];

	if (!bMini)
	{
		Ne2000Read(sAddr, pBuf, ARP_LENGTH);
		ArpRun(pBuf);
	}
}

#ifndef CALL_NONE
void _HandlePppoeInit(USHORT sAddr, PCHAR pSrcMac, BOOLEAN bMini)
{
	UCHAR pHead[PPPOE_INIT_INFO];
	USHORT sLen;

	if (Sys_iIpType == IP_TYPE_PPPOE && !bMini)
	{
		Ne2000Read(sAddr, pHead, PPPOE_INIT_INFO);
		sLen = PCHAR2USHORT((PCHAR)(pHead + PPPOE_LENGTH));
		if (sLen)
		{
			Ne2000Read(sAddr + PPPOE_INIT_INFO, Adapter_pReceivePacket, sLen);
		}
		PppoeInitRun(pHead, pSrcMac, sLen);
	}
}

void _HandlePppoeSession(USHORT sAddr, PCHAR pPppoeHead)
{
	USHORT sLen;

	sLen = PCHAR2USHORT((PCHAR)(pPppoeHead + PPPOE_LENGTH));
	if (sLen > 2)
	{
		Ne2000Read(sAddr, Adapter_pReceivePacket, sLen - 2);
		PppoeSessionRun(pPppoeHead);
	}
}
#endif

UCHAR _UpdateNextPacket(UCHAR iCurPacket)
{
	USHORT s;
	UCHAR iNext;

	s = iCurPacket;
	s <<= 8;
	Ne2000Read(s + HEADER_NEXT_PACKET, &iNext, 1);
	_bMiniRunned = FALSE;

	return iNext;
}

UCHAR ReadPacket(UCHAR iCurPacket, BOOLEAN bMini)
{
	USHORT sType;
	PACKET_LIST packet;
	UCHAR pHead[NE2000_HEADER_SIZE + EP_DATA];	// MAC head, also used as VLAN head and PPPoE head
	UCHAR iStatus, iNextPacket;

	_sPrevPacketAddr = _sCurPacketAddr;
	_sCurPacketAddr = iCurPacket;
	_sCurPacketAddr <<= 8;
	packet.sAddr = _sCurPacketAddr;
	Ne2000Read(packet.sAddr, pHead, NE2000_HEADER_SIZE + EP_DATA);

	iStatus = pHead[HEADER_STATUS];
	iNextPacket = pHead[HEADER_NEXT_PACKET];
	if (iStatus & 0x5e || (iNextPacket < NE2000_NIC_PAGE_START || iNextPacket >= NE2000_NIC_PAGE_STOP))
	{	// Header error
		return 0;
	}
/*
	s = PCHAR2USHORT_L((PCHAR)(pHead + HEADER_PACKET_LEN_LOW));
	if (s < (ETHERNET_MIN_SIZE + NE2000_HEADER_SIZE) || s > (ETHERNET_MAX_SIZE + NE2000_HEADER_SIZE))
	{	// Header len error
		return 0;
	}
*/
	packet.sAddr += NE2000_HEADER_SIZE + EP_DATA;
	sType = PCHAR2USHORT((PCHAR)(pHead + NE2000_HEADER_SIZE + EP_TYPE));
#ifndef CALL_NONE	// never enable VLAN in page0
	if (_bVLan)
	{
		if (sType == EPT_VLAN)	    // Check if it is VLAN packet
		{
			Ne2000Read(packet.sAddr, pHead, VLAN_HEADER_SIZE);	// the first 4 bytes of pHead can be used now as VLAN head
			packet.sAddr += VLAN_HEADER_SIZE;
//			LcdDebugVal(PCHAR2USHORT(pHead), 10, 3);
			if ((_sLayer2Qos & VLAN_ID_MASK) != (PCHAR2USHORT(pHead) & VLAN_ID_MASK))	goto SkippedPacket;
			sType = PCHAR2USHORT((PCHAR)(pHead + VLAN_TCI));
		}
		else	goto SkippedPacket;
	}
#endif

	// Check if it is tcp/ip packet
	if (sType == EPT_IP)
	{
#ifndef	OEM_AUTOTEST
		if (iStatus & 0x20)
		{	// this is a broadcast packet, check if we need it
			if (Net_bConnected && !Dhcp_bRenewing)	
			{	
				goto SkippedPacket;
			}
		}
#endif
		if (!_IPReceive(&packet, bMini))
		{	
			if (!bMini && _bMiniRunned)
			{
				iNextPacket = _UpdateNextPacket(iCurPacket);
			}
			goto End;
		}
	}
	else if (sType == EPT_ARP)
	{
		_HandleARP(packet.sAddr, bMini);
		goto End;
	}
#ifndef CALL_NONE
	else if (sType == EPT_PPPOE_INIT)
	{
		_HandlePppoeInit(packet.sAddr, (PCHAR)(pHead + NE2000_HEADER_SIZE + EP_SRC), bMini);
		goto End;
	}
	else if (sType == EPT_PPPOE)
	{
		if (Sys_iIpType == IP_TYPE_PPPOE)
		{
			Ne2000Read(packet.sAddr, pHead, PPPOE_INFO);	// the first 8 bytes of pHead can be used now as PPPoE head
			packet.sAddr += PPPOE_INFO;
			if (PppoeCheckHead(pHead, (PCHAR)(pHead + NE2000_HEADER_SIZE + EP_SRC)))
			{
				if (PCHAR2USHORT((PCHAR)(pHead + PPPOE_PROTOCOL_ID)) == PPP_PROT_IP)
				{
					if (!Net_bConnected)	goto End;
					if (!_IPReceive(&packet, bMini))
					{	
						if (!bMini && _bMiniRunned)
						{
							iNextPacket = _UpdateNextPacket(iCurPacket);
						}
					}
					else
					{
						goto SkippedPacket;
					}
				}
				else
				{
					_HandlePppoeSession(packet.sAddr, pHead);
				}
			}
		}
		goto End;
	}
#endif

    // We do not welcome other type of packets
SkippedPacket:
	if (bMini)
	{	// mini run handled this packet, mark it
		Ne2000Write(_sPrevPacketAddr + HEADER_NEXT_PACKET, &iNextPacket, 1);
		_bMiniRunned = TRUE;
		_sCurPacketAddr = _sPrevPacketAddr;
	}
End:
	return iNextPacket;
}

#elif defined DM9003EP || defined KSZ8842

void _HandleARP()
{
	UCHAR pBuf[ARP_LENGTH];

	NetRead(pBuf, ARP_LENGTH);
	ArpRun(pBuf);
}

#ifndef CALL_NONE

void _HandlePppoeInit(PCHAR pSrcMac)
{
	UCHAR pHead[PPPOE_INIT_INFO];
	USHORT sLen;

	if (Sys_iIpType == IP_TYPE_PPPOE)
	{
		NetRead(pHead, PPPOE_INIT_INFO);
		sLen = PCHAR2USHORT((PCHAR)(pHead + PPPOE_LENGTH));
		if (sLen)
		{
			NetRead(Adapter_pReceivePacket, sLen);
		}
		PppoeInitRun(pHead, pSrcMac, sLen);
	}
}

void _HandlePppoeSession(PCHAR pPppoeHead)
{
	USHORT sLen;

	sLen = PCHAR2USHORT((PCHAR)(pPppoeHead + PPPOE_LENGTH));
	if (sLen > 2)
	{
		NetRead(Adapter_pReceivePacket, sLen - 2);
		PppoeSessionRun(pPppoeHead);
	}
}

#endif

void _IPReceive()
{
	UCHAR iVal;
	PACKET_LIST packet;
	PACKET_LIST * pCur;
	UCHAR pIpHead[IP_DATA];

	NetRead(pIpHead, IP_DATA);

	pCur = _CheckIpHead(&packet, pIpHead);
	if (!pCur)
	{	
		return;
	}

	iVal = pIpHead[IP_PROTO];
	if (iVal == IPT_UDP)
	{
		UdpRun(pCur);
	}
	else if (Net_bConnected)
	{
		if (iVal == IPT_TCP)
		{
#ifndef CALL_NONE
			NetRead(Adapter_pReceivePacket, pCur->sLen);
			TaskTcpRun(pCur);
#endif
		}
		else if (iVal == IPT_ICMP)
		{
			IcmpRun(pCur);
		}
	}

	if (_pFragList == pCur)
	{
		FreePacketList();
	}
}

void HandlePacket()
{
	USHORT sType;
	UCHAR pHead[NET_HEADER_SIZE + EP_DATA];
#ifdef KSZ8842
	UCHAR iStatus;
#endif

	NetRead(pHead, NET_HEADER_SIZE + EP_DATA);

#ifdef KSZ8842
	iStatus = pHead[0];
	if (iStatus & 0x07)		return;		// receive error
	if (!(pHead[1] & 0x80))	return;		// bad frame
	_bPort1Data = (pHead[1] & 0x01) ? TRUE : FALSE;
#else
//	_bPort1Data = (dm9k_read_reg(NIC_RSR) & 0x0c) ? TRUE : FALSE;
	_bPort1Data = (pHead[1] & 0x0c) ? TRUE : FALSE;
//	_sLenRemain = PCHAR2USHORT_L((PCHAR)(pHead + 2));
#endif

	sType = PCHAR2USHORT((PCHAR)(pHead + NET_HEADER_SIZE + EP_TYPE));

#ifndef CALL_NONE	// never enable VLAN in page0
	if (_bVLan)
	{
		if (sType == EPT_VLAN)	    // Check if it is VLAN packet
		{
			NetRead(pHead, VLAN_HEADER_SIZE);	// use first 4 bytes of pHead as VLAN head
			if ((_sLayer2Qos & VLAN_ID_MASK) != (PCHAR2USHORT(pHead) & VLAN_ID_MASK))	return;
			sType = PCHAR2USHORT((PCHAR)(pHead + VLAN_TCI));
		}
#ifndef OEM_VLAN
		else	return;
#endif
	}
#endif

	if (sType == EPT_IP)
	{
#ifndef	OEM_AUTOTEST
#ifdef KSZ8842
		if (iStatus & 0xc0)
#else
		if (memcmp((PCHAR)(pHead + NET_HEADER_SIZE), Sys_pMacAddress, HW_ALEN))
#endif
		{	// this is a broadcast packet, check if we need it
			if (Net_bConnected && !Dhcp_bRenewing)	
			{	
				return;
			}
		}
#endif
		_IPReceive();
	}
	else if (sType == EPT_ARP)
	{
		_HandleARP();
	}
#ifndef CALL_NONE
	else if (sType == EPT_PPPOE_INIT)
	{
		_HandlePppoeInit((PCHAR)(pHead + NET_HEADER_SIZE + EP_SRC));
	}
	else if (sType == EPT_PPPOE)
	{
		if (Sys_iIpType == IP_TYPE_PPPOE)
		{
			NetRead(pHead, PPPOE_INFO);		// use first 8 bytes of pHead as PPPoE head
			if (PppoeCheckHead(pHead, (PCHAR)(pHead + NET_HEADER_SIZE + EP_SRC)))
			{
				if (PCHAR2USHORT((PCHAR)(pHead + PPPOE_PROTOCOL_ID)) == PPP_PROT_IP)
				{
					if (Net_bConnected)
					{
						_IPReceive();
					}
				}
				else
				{
					_HandlePppoeSession(pHead);
				}
			}
		}
	}
#endif
}

#endif

#ifdef DM9003EP

ULONG _crc_32(PCHAR pCRC_Data, UCHAR iLen)
{
	ULONG lCRC, lCarry;
	UCHAR i, j;

	lCRC = 0xffffffff;
	for (j = 0; j < iLen; j ++)
	{
		lCarry = (lCRC ^ *pCRC_Data++) & 0xff;
		for (i = 0; i < 8; i ++)
		{
			if (lCarry & 1)
			{
				lCarry = rr_32x8(lCarry, 1) ^ 0xedb88320;
			}
			else
			{
				lCarry = rr_32x8(lCarry, 1);
			}
		}
		lCRC = (rr_32x8(lCRC, 8) & 0x00ffffff) ^ lCarry;
    }
	return (~lCRC);
}


const UCHAR pSetM[HW_ALEN + HW_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
						 0x01, 0x02, 0x03, 0x04, 0x05, 0x06};

void dm9k_set_multicast()
{
	UCHAR i, iHashData;

	for (i = 0; i < 8; i ++)
	{
		dm9k_write_reg(NIC_MAR + i, 0x00);
	}

	iHashData = ~(_crc_32(&pSetM[0], HW_ALEN)) & 0x3f;
//	UdpDebugVal(iHashData, 16);
	dm9k_write_reg(NIC_MAR + (iHashData / 8) , 0x01 << (iHashData % 8));

	iHashData = ~(_crc_32(&pSetM[HW_ALEN], HW_ALEN)) & 0x3f;
//	UdpDebugVal(iHashData, 16);
	i = dm9k_read_reg(NIC_MAR + (iHashData / 8));
	dm9k_write_reg(NIC_MAR + (iHashData / 8) , i | (0x01 << (iHashData % 8)));
}

void dm9k_init()
{
	UCHAR i;

	// reset
	dm9k_write_reg(NIC_NCR, 0x01);
	while((dm9k_read_reg(NIC_NCR) & 0x01) == 0x01);

//	dm9k_write_reg(NIC_NCR, 0x20);

	dm9k_write_reg(NIC_ISR, 0x0F);	// clear all interrupt
	dm9k_write_reg(NIC_IMR, 0x80);	// disable all interrupt

	// set multicast
	dm9k_set_multicast();	// can NOT omit!

	dm9k_write_reg(NIC_FCR, 0x20);		// enable flow control

	// set mac
	for (i = 0; i < HW_ALEN; i ++)
	{
		dm9k_write_reg(NIC_PAR + i, Sys_pMacAddress[i]);
    }

#ifndef CALL_NONE
	if (_bVLan)
	{
		dm9k_write_reg(SW_VLANCR, 0x01);	// enable 802.1Q
		for (i = 0; i < SW_PORT_NUM; i ++)
		{
			if (i != 2)
			{
				dm9k_write_reg(SW_PINDEX, i);
				dm9k_write_reg(SW_PPRI, 0x80);
				dm9k_write_reg(SW_VLAN_TAGL, (UCHAR)_sLayer2Qos);
				dm9k_write_reg(SW_VLAN_TAGH, (UCHAR)(_sLayer2Qos >> 8));
			}
		}

#ifdef OEM_VLAN
		dm9k_write_reg(SW_VLANCR, 0x21);	// enable 802.1Q, Unicast packet can across VLAN boundary
		dm9k_write_reg(SW_PINDEX, 1);		// use port 1 as "up-link" to a none-VLAN connection
		dm9k_write_reg(SW_PPRI, 0x00);
#endif
	}

//	dm9k_write_reg(NIC_MEM_SIZE, 0x0c);	// 12x256 TX memory size is enough, will cause problem!
//	dm9k_write_reg(NIC_RX_CSC, 0x03);	// enable RX check sum, will cause problem!
	dm9k_write_reg(SW_PINDEX, 3);
	dm9k_write_reg(SW_PRATE, 0x66);		// host port ingress/egress rate control, 2Mbps
	dm9k_write_reg(SW_PBW, 0x10);		// host port broadcast rate control
#endif

	if (Sys_iIpType != IP_TYPE_PPPOE)
	{
		dm9k_write_reg(NIC_TX_CSC, 0x07);	// enable TX check sum
	}

//	dm9k_write_reg(NIC_UP_BUS, 0x21);	// v5 new
	dm9k_write_reg(NIC_UP_BUS, 0x61);	// v5 new
	dm9k_write_reg(SW_59H, 0xe1);	// v5 new

	dm9k_read_reg(NIC_POCR);	// receive overflow counter
	dm9k_read_reg(NIC_NSR);		// network status
	dm9k_write_reg(NIC_TCR, 0x00);	// TX control
//	dm9k_write_reg(NIC_RCR, 0x31);	// RX control
	dm9k_write_reg(NIC_RCR, 0x01);	// RX control

//	dm9k_write_reg(NIC_IMR, 0x82);
}

void Dm9003Init()
{
	UCHAR i;
	USHORT s;

/*	// reset switch, PHY, set aging: 256s
	dm9k_write_reg(SW_SCR, 0x40);
	while((dm9k_read_reg(SW_SCR) & 0x40) == 0x40);

	dm9k_write_reg(SW_SCR, 0x20);
	while((dm9k_read_reg(SW_SCR) & 0x20) == 0x20);
*/
	dm9k_write_reg(SW_SCR, 0x03);

	// reset VLAN mapping table
	for (i = SW_VLAN_MAPPING_BEGIN; i <= SW_VLAN_MAPPING_END; i ++)
	{
		dm9k_write_reg(i, 0x0b);
	}

	// reset Per-Port VID
	for (i = 0; i < SW_PORT_NUM; i ++)
	{
		if (i != 2)
		{
			dm9k_write_reg(SW_PINDEX, i);
			dm9k_write_reg(SW_VLAN_TAGL, 1);
		}
	}

	dm9k_write_reg(SW_VLANCR, 0);	// reset VLAN control

	// reset Per-Port switch control
	for (i = 0; i < SW_PORT_NUM; i ++)
	{
		if (i != 2)
		{
			dm9k_write_reg(SW_PINDEX, i);
			dm9k_write_reg(SW_PCTRL, 0);
			dm9k_write_reg(SW_PRATE, 0);
			dm9k_write_reg(SW_PBW, 0);
			dm9k_write_reg(SW_PPRI, 0);
			dm9k_write_reg(SW_VLAN_TAGH, 0);
		}
	}

	// set phy mode
//	dm9k_write_phy(0x00, 0x8000);
//	while((dm9k_read_phy(0x00) & 0x8000) == 0x8000);
	dm9k_write_phy(0x04, 0x05e1);
	dm9k_write_phy(0x00, 0x1000);
//	dm9k_write_phy(0x00, 0x0100);

	dm9k_init();

	for (s = 0; s < 6000; s ++)
	{
		delay1ms();
	}
}

#elif defined KSZ8842

void Ksz8842Init()
{
	UCHAR i, j;
	USHORT s;
//	USHORT sVal;
	UCHAR pMac[HW_ALEN];
	PSHORT p;

	_iRegBank = 0xff;

	// read base address
//	if (_reg_read_word(REG_BASE_ADDR_BANK, REG_BASE_ADDR_OFFSET) != 0x0300)	return;									

	// read device ID
//	if ((_reg_read_word(REG_SIDER_BANK, REG_CHIP_ID_OFFSET) & 0xfff0) != 0x8800)	return;		

	// global reset, GRR[0]
/*	ksz8842_or_reg_byte(REG_GLOBAL_CTRL_BANK, REG_GLOBAL_CTRL_OFFSET, 0x01);
	for (i = 0; i < 100; i ++)
	{
		delay1ms();
	}
	ksz8842_and_reg_byte(REG_GLOBAL_CTRL_BANK, REG_GLOBAL_CTRL_OFFSET, ~0x01);
*/
	// write QMU MAC, MARL/M/H
	for (i = 0; i < HW_ALEN; i += 2)
	{
		pMac[i] = Sys_pMacAddress[i + 1];
		pMac[i + 1] = Sys_pMacAddress[i];
	}
	p = (PSHORT)pMac;
	j = 2;
	for (i = REG_ADDR_0_OFFSET; i <= REG_ADDR_4_OFFSET; i += 2)
	{
		ksz8842_write_reg_word(REG_ADDR_0_BANK, i, p[j]);
		j --;
	}
/*
	// write switch MAC, MACAR1/2/3
	pMac[HW_ALEN - 1] ++;
	j = REG_MACAR1_OFFSET;
	for (i = 0; i < 3; i ++)
	{
		ksz8842_write_reg_word(REG_MACAR1_BANK, j, p[i]);
		j += 2;
	}
*/

#ifndef CALL_NONE
	if (_bVLan)
	{
		// VID control, P1VIDCR/P2VIDCR/P3VIDCR
		ksz8842_write_reg_word(REG_P1VIDCR_BANK, REG_P1VIDCR_OFFSET, _sLayer2Qos);
		ksz8842_write_reg_word(REG_P2VIDCR_BANK, REG_P2VIDCR_OFFSET, _sLayer2Qos);
		ksz8842_write_reg_word(REG_P3VIDCR_BANK, REG_P3VIDCR_OFFSET, _sLayer2Qos);

		// write VLAN table
		ksz8842_write_reg_word(REG_IADR4_BANK, REG_IADR4_OFFSET, _sLayer2Qos);
		ksz8842_write_reg_word(REG_IADR5_BANK, REG_IADR5_OFFSET, 0x0f);
		ksz8842_write_reg_word(REG_IACR_BANK, REG_IACR_OFFSET, 0x0400);

		ksz8842_write_reg_word(REG_IADR5_BANK, REG_IADR5_OFFSET, 0);
		for (i = 1; i < 16; i ++)
		{
			ksz8842_write_reg_word(REG_IACR_BANK, REG_IACR_OFFSET, 0x0400 + i);
		}

		// 802.1Q VLAN enable, SGCR2[15]. VLAN table must be set up before the operation
		ksz8842_or_reg_word(REG_SGCR2_BANK, REG_SGCR2_OFFSET, 0x8000);

		// 802.1p priority classification enable, P3CR1[5]
		// tag insertion, P3CR1[2]
		// tag removal, P3CR1[1]
		// normal
		// unicast port-VLAN mismatch discard, SGCR2[7]
		ksz8842_or_reg_byte(REG_SGCR2_BANK, REG_SGCR2_OFFSET, 0x80);
		ksz8842_or_reg_byte(REG_P1CR1_BANK, REG_P1CR1_OFFSET, 0x24);
		ksz8842_and_reg_byte(REG_P1CR1_BANK, REG_P1CR1_OFFSET, ~0x02);
		ksz8842_or_reg_byte(REG_P2CR1_BANK, REG_P2CR1_OFFSET, 0x24);
		ksz8842_or_reg_byte(REG_P3CR1_BANK, REG_P3CR1_OFFSET, 0x24);

#ifdef OEM_VLAN
		// unicast port-VLAN mismatch discard, SGCR2[7]
		ksz8842_and_reg_byte(REG_SGCR2_BANK, REG_SGCR2_OFFSET, ~0x80);
		// port1 as none-VLAN uplink
		ksz8842_and_reg_byte(REG_P1CR1_BANK, REG_P1CR1_OFFSET, ~0x04);
		ksz8842_or_reg_byte(REG_P1CR1_BANK, REG_P1CR1_OFFSET, 0x02);
#endif
	}
	else
	{
		// 802.1Q VLAN enable, SGCR2[15]. 
		ksz8842_and_reg_word(REG_SGCR2_BANK, REG_SGCR2_OFFSET, ~0x8000);
	}

	// set host port ingress rate control, 2Mbps, P3IRCR
	ksz8842_write_reg_word(REG_P3IRCR_BANK, REG_P3IRCR_OFFSET, 0x6666);

	// set host port egress rate control, 2Mbps, P3ERCR
	ksz8842_write_reg_word(REG_P3ERCR_BANK, REG_P3ERCR_OFFSET, 0x6666);

	// Ingress Limit Mode, P3CR3[3-2]
//	ksz8842_or_reg_byte(REG_P3CR3_BANK, REG_P3CR3_OFFSET, 0x08);	// limit and count broadcast and multicast frames only
#endif

	// enable QMU transmit flow control / transmit padding / transmit CRC, TXCR
	ksz8842_write_reg_word(REG_TX_CTRL_BANK, REG_TX_CTRL_OFFSET, TX_CTRL_FLOW_ENABLE | TX_CTRL_PAD_ENABLE | TX_CTRL_CRC_ENABLE);	

	// enable QMU receive flow control / receive all broadcast frames / receive all multicast frames / receive unicast frames / receive strip the CRC, RXCR
	ksz8842_write_reg_word(REG_RX_CTRL_BANK, REG_RX_CTRL_OFFSET, RX_CTRL_FLOW_ENABLE | RX_CTRL_BROADCAST | RX_CTRL_ALL_MULTICAST | RX_CTRL_UNICAST | RX_CTRL_STRIP_CRC);

	// enable QMU transmit frame data pointer auto increment, TXFDPR
	ksz8842_write_reg_word(REG_TX_ADDR_PTR_BANK, REG_TX_ADDR_PTR_OFFSET, 0x4000);

	// enable QMU receive frame data pointer auto increment, RXFDPR
	ksz8842_write_reg_word(REG_RX_ADDR_PTR_BANK, REG_RX_ADDR_PTR_OFFSET, 0x4000);

	// configure QMU receive high water mark to 2KBytes to avoid loss packets (big packet size) under flow control, QRFCR[12]
	// actually we do not care if there is big packet lost!
//	sVal = _reg_read_word(REG_RX_WATERMARK_BANK, REG_RX_WATERMARK_OFFSET);
//	ksz8842_write_reg_word(REG_RX_WATERMARK_BANK, REG_RX_WATERMARK_OFFSET, sVal | RX_HIGH_WATERMARK_2KB);

	// enable aggressive back off algorithm in half duplex mode, SGCR1[8]
	ksz8842_or_reg_word(REG_SGCR1_BANK, REG_SGCR1_OFFSET, 0x0100);

	// enable no excessive collision drop, SGCR2[3]
	ksz8842_or_reg_byte(REG_SGCR2_BANK, REG_SGCR2_OFFSET, 0x08);

	// enable full-duplex flow control on Switch Host port, SGCR3[5]
	ksz8842_or_reg_byte(REG_SGCR3_BANK, REG_SGCR3_OFFSET, 0x20);

	// enable port 1 force flow control / back pressure / transmit / receive, P1CR2
	ksz8842_write_reg_word(REG_P1CR2_BANK, REG_P1CR2_OFFSET, 0x1e07);

	// enable port 2 force flow control / back pressure / transmit / receive, P2CR2
	ksz8842_write_reg_word(REG_P2CR2_BANK, REG_P2CR2_OFFSET, 0x1e07);

	// restart Port 1 auto-negotiation, P1CR4[13]
	ksz8842_or_reg_word(REG_P1CR4_BANK, REG_P1CR4_OFFSET, 0x2000);

	// restart Port 2 auto-negotiation, P2CR4[13]
	ksz8842_or_reg_word(REG_P2CR4_BANK, REG_P2CR4_OFFSET, 0x2000);

	// clear the interrupts status, ISR
	ksz8842_write_reg_word(REG_INT_STATUS_BANK, REG_INT_STATUS_OFFSET, 0xffff);

	// enable QMU transmit, TXCR[0]
	ksz8842_or_reg_byte(REG_TX_CTRL_BANK, REG_TX_CTRL_OFFSET, 0x01);

	// enable QMU receive, RXCR[0]
	ksz8842_or_reg_byte(REG_RX_CTRL_BANK, REG_RX_CTRL_OFFSET, 0x01);

	// enable Switch engine, SIDER[0]
	ksz8842_or_reg_byte(REG_SIDER_BANK, REG_SIDER_OFFSET, 0x01);
/*
	// check status in P1SR[6]/AN done, P1SR[5]/link good
	if (_reg_read_byte(REG_P1SR_BANK, REG_P1SR_OFFSET) & 0x20)
	{
		while (!(_reg_read_byte(REG_P1SR_BANK, REG_P1SR_OFFSET) & 0x40));
		_reg_and_word(REG_P1CR4_BANK, REG_P1CR4_OFFSET, ~0x0800);
	}
	else
	{	// Power Down, P1CR4[11], can NOT check link while power down!
		ksz8842_or_reg_word(REG_P1CR4_BANK, REG_P1CR4_OFFSET, 0x0800);
	}

	// check status in P2SR[6]/AN done, P2SR[5]/link good
	if (_reg_read_byte(REG_P2SR_BANK, REG_P2SR_OFFSET) & 0x20)
	{
		while (!(_reg_read_byte(REG_P2SR_BANK, REG_P2SR_OFFSET) & 0x40));
	}
*/

	for (s = 0; s < 4000; s ++)
	{
		delay1ms();
	}
}

void Ksz8842ReadBypass(USHORT sLen)
{
	PCHAR p;

	p = malloc(sLen + 1);	// Ksz8842Read always reads even bytes
	if (p)
	{
		Ksz8842Read(p, sLen);
		free(p);
	}
}
#endif

