#include "PalmType.h"
#include "version.h"
#include "PalmChip.h"
#include "p_util.h"
#include "Ne2000.h"
#include "p_tcpip.h"           
#include "v_task.h"
//#include "my_tcpip.h"

// the Novel 2000 Ethernet controller.

// These are for the current packet being indicated.

static DCHAR _iHeaderSize;

// The value to write to the adapter for the next receive buffer that is free.
static XCHAR _iCurrentPacket, _iNextPacket;
//static XCHAR _pReceiveHeader[NE2000_HEADER_MAX_SIZE];

void _CardWaitStatus(UCHAR iStatus)
{
    while (1) 
    {
        if (XBYTE[NE2000_BASE + NIC_INTR_STATUS] & iStatus) 
        {
            return;
        }
    }
}

// Copies data from the card to memory.
void _CardCopyUp(USHORT iReceiveLen, PXCHAR pReceivePacket)
{
    DCHAR iDummy;
	DSHORT iLen;

	iLen = iReceiveLen;

    // Read the Command Register, to make sure it is ready for a write
    iDummy = XBYTE[NE2000_BASE + NIC_COMMAND];

    // Set iCount and destination address)
	XBYTE[NE2000_BASE + NIC_COMMAND] = CR_PAGE0;
    XBYTE[NE2000_BASE + NIC_RMT_ADDR_LSB] = _iHeaderSize;		// LSB(Adapter_ReceiveStart);
    XBYTE[NE2000_BASE + NIC_RMT_ADDR_MSB] = _iCurrentPacket;	// MSB(Adapter_ReceiveStart);
//    XBYTE[NE2000_BASE + NIC_RMT_COUNT_LSB] = (UCHAR)iReceiveLen;				// LSB(BufferLength)
//    XBYTE[NE2000_BASE + NIC_RMT_COUNT_MSB] = P_HIBYTE(iReceiveLen);				// MSB(BufferLength)
	USHORT2PXCHAR_L(iLen, (PXCHAR)(NE2000_BASE+NIC_RMT_COUNT_LSB));

    // Set direction (Read)
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_START + CR_PAGE0 + CR_DMA_READ;

    // Repeatedly read from port
	p_inp(NE2000_BASE + NIC_RACK_NIC, pReceivePacket, iLen);

    // Wait for DMA to complete
    _CardWaitStatus(ISR_DMA_DONE);
}

// Gets the value of the CURRENT NIC register and stores it in _iCurrentPacket
UCHAR _CardGetCurrent()
{
    DCHAR iCurrent;

    // Have to go to page 1 to read this register
    XBYTE[NE2000_BASE+NIC_COMMAND] = CR_START + CR_NO_DMA + CR_PAGE1;
    iCurrent = XBYTE[NE2000_BASE+NIC_CURRENT];
    XBYTE[NE2000_BASE+NIC_COMMAND] =  CR_START + CR_NO_DMA + CR_PAGE0;

    return iCurrent;
}

BOOLEAN _CardCheckHeader(PXCHAR pReceiveHeader)
{
	DCHAR iTmp;

	iTmp = pReceiveHeader[HEADER_STATUS];
	if (iTmp & 0x5e)
	{	// Header Status Err
		return FALSE;
	}
	Adapter_bReceiveBroadcast = (iTmp & 0x20) ? TRUE : FALSE;

    _iNextPacket = pReceiveHeader[HEADER_NEXT_PACKET];
	if (_iNextPacket < NE2000_NIC_PAGE_START || _iNextPacket >= NE2000_NIC_PAGE_STOP)
	{	// Header Val Err
		return FALSE;
	}

	Adapter_iReceiveLen = PXCHAR2USHORT_L((PXCHAR)(pReceiveHeader+HEADER_PACKET_LENGTH_LOW));
	// Header Len Err
	if (Adapter_iReceiveLen > NE2000_MAX_RECEIVE)
	{
		return FALSE;
	}
/*	if (Adapter_iReceiveLen < NE2000_MIN_RECEIVE)
	{
		return FALSE;
	}
*/
	return TRUE;
}

void _IPReceive()
{
	DCHAR iVal;

    _CardCopyUp(IP_DATA, IP_pReceiveHeader);
    if (IP_pReceiveHeader[0] != IP_FIRST_BYTE)
    {	// strange ip version, we don't handle such ip packet
		return;
    }
    _iHeaderSize += IP_DATA; 

//	Adapter_iReceiveLen = P_MAKEWORD(IP_pReceiveHeader[IP_LEN+1], IP_pReceiveHeader[IP_LEN]);
	Adapter_iReceiveLen = PXCHAR2USHORT((PXCHAR)(IP_pReceiveHeader+IP_LEN));
	if (Adapter_iReceiveLen <= IP_DATA || Adapter_iReceiveLen > NE2000_MAX_RECEIVE)
	{	// error length
		return;
	}

	Adapter_iReceiveLen -= IP_DATA;

	iVal = IP_pReceiveHeader[IP_PROTO];
    if (iVal == IPT_UDP)
    {
/*		if (Adapter_iReceiveLen <= UDP_DATA)
		{
			return;
		}
*/
		_CardCopyUp(Adapter_iReceiveLen, (PXCHAR)Adapter_pReceivePacket);
		UdpRun();
    }
	else
	{
		if (!Discover_bFinished)	return;
		if (Adapter_bReceiveBroadcast)	return;

		if (iVal == IPT_TCP)
		{
			_CardCopyUp(Adapter_iReceiveLen, (PXCHAR)Adapter_pReceivePacket);
			TcpRun();
		}
	    else if (iVal == IPT_ICMP)
		{
			_CardCopyUp(Adapter_iReceiveLen, (PXCHAR)Adapter_pReceivePacket);
			IcmpRun();
		}
/*		else if (iVal == IPT_RSVP)
		{
			OutputString(IDS_IPT_RSVP);
		}
*/
    }
}

void _ReadPacket()
{
	DSHORT iType;
	DSHORT iVlanID;

    _iHeaderSize = 0;

    _CardCopyUp(NE2000_HEADER_SIZE+EP_DATA, Mac_pReceiveHeader);
	p_memset_ip_0(Adapter_pReceiveIPAddress);

	if (!_CardCheckHeader(Mac_pReceiveHeader))
	{
		_iNextPacket = 0;
		return;
	}

    _iHeaderSize = NE2000_HEADER_SIZE+EP_DATA;
	iType = PXCHAR2USHORT((PXCHAR)(Mac_pReceiveHeader+NE2000_HEADER_SIZE+EP_TYPE));

	if (Mac_pReceiveHeader[NE2000_HEADER_SIZE+EP_DST] != 0xff)
	{
		Adapter_bReceiveBroadcast = FALSE;
	}

    // Check if it is VLAN packet
	if (iType == EPT_VLAN)
	{
		_CardCopyUp(VLAN_HEADER_SIZE, Mac_pReceiveHeader);
		iVlanID = PXCHAR2USHORT(Mac_pReceiveHeader) & VLAN_ID_MASK;
		if (VLAN_bEnable && iVlanID != VLAN_iID)
		{
			return;
		}
		iType = PXCHAR2USHORT((PXCHAR)(Mac_pReceiveHeader + VLAN_HEADER_SIZE - VLAN_TCI));
	}

    // Check if it is tcp/ip packet
	if (iType == EPT_IP)
	{
		if (Adapter_bReceiveBroadcast)
		{
			if (Discover_bFinished)	
			{
				if (!DhcpRenewing())
				{
					return;
				}
			}
		}
		_IPReceive();
	}
	else if (iType == EPT_ARP)
	{
		// DHCP will use ARP before Discover_bFinished is TRUE
//		if (!Discover_bFinished)	return;
		_CardCopyUp(ARP_LENGTH, (PXCHAR)Adapter_pReceivePacket);
		ArpRun();
	}
	else if (iType == EPT_PPPOE_INIT)
	{
		_CardCopyUp(PPPOE_INIT_INFO, PPPoE_pHead);
		_iHeaderSize += PPPOE_INIT_INFO;
		Adapter_iReceiveLen -= PPPOE_INIT_INFO;
		_CardCopyUp(Adapter_iReceiveLen, (PXCHAR)Adapter_pReceivePacket);
//		_iHeaderSize += Adapter_iReceiveLen;
		PPPoE_InitRun(); 
	}
    else if (iType == EPT_PPPOE)
	{
	 	_CardCopyUp(PPPOE_INFO, PPPoE_pHead);
		_iHeaderSize += PPPOE_INFO;
		Adapter_iReceiveLen -= PPPOE_INFO;
		if (PPPoE_IsIP())
		{
			_IPReceive();
		}
		else
		{
			_CardCopyUp(Adapter_iReceiveLen, (PXCHAR)Adapter_pReceivePacket);
//			_iHeaderSize += Adapter_iReceiveLen;
			PPPoE_Run();
		}
	}
    // We do not welcome other type of packets
}

BOOLEAN Ne2000Initialize()
{
    DCHAR iTmp;

    iTmp = XBYTE[NE2000_BASE + NIC_COMMAND];					// Check that Command register reflects this last command
    if (!(iTmp & CR_STOP))
    {
		Adapter_bOK = FALSE;
        return FALSE;
    }

    XBYTE[NE2000_BASE + NIC_RMT_COUNT_LSB] = 55;				// Do initialization errata
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_DMA_READ + CR_START;	// Setup for a read
    XBYTE[NE2000_BASE + NIC_INTR_MASK] = 0x0;					// Mask Interrupts
    XBYTE[NE2000_BASE + NIC_DATA_CONFIG] = DCR_FIFO_8_BYTE + DCR_NORMAL + DCR_BYTE_WIDE;    // Setup the Adapter for reading ram
    XBYTE[NE2000_BASE + NIC_XMIT_CONFIG] = TCR_NO_LOOPBACK;	    // Clear transmit configuration.
    XBYTE[NE2000_BASE + NIC_RCV_CONFIG] = 0;				    // Clear receive configuration.
    XBYTE[NE2000_BASE + NIC_INTR_STATUS] = 0xFF;			    // Clear any interrupts
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_NO_DMA + CR_STOP;	    // Stop the chip
    XBYTE[NE2000_BASE + NIC_RMT_COUNT_LSB] = 0;					// Clear any DMA values
    XBYTE[NE2000_BASE + NIC_RMT_COUNT_MSB] = 0;				    // Clear any DMA values

    // Wait for the reset to complete.
	_CardWaitStatus(ISR_RESET);

    XBYTE[NE2000_BASE + NIC_XMIT_CONFIG] = TCR_LOOPBACK;	    // Put card in loopback mode
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_NO_DMA + CR_START;    // Start the chip.
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_STOP + CR_NO_DMA;	    // Stop the chip

    // Setup the card based on the initialization information
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_STOP + CR_NO_DMA + CR_PAGE0;	    // Write to and read from CR to make sure it is there.
    iTmp = XBYTE[NE2000_BASE + NIC_COMMAND];

    if ((iTmp & (CR_STOP + CR_NO_DMA + CR_PAGE0)) != (CR_STOP + CR_NO_DMA + CR_PAGE0))
    {
		Adapter_bOK = FALSE;
        return FALSE;
    }

    // Set up the registers in the correct sequence, as defined by the 8390 specification.
    XBYTE[NE2000_BASE + NIC_DATA_CONFIG] = DCR_BYTE_WIDE + DCR_NORMAL + DCR_FIFO_8_BYTE;
    XBYTE[NE2000_BASE + NIC_RMT_COUNT_MSB] = 0;
    XBYTE[NE2000_BASE + NIC_RMT_COUNT_LSB] = 0;
    XBYTE[NE2000_BASE + NIC_RCV_CONFIG] = RCR_REJECT_ERR + RCR_BROADCAST + RCR_MULTICAST;
//    XBYTE[NE2000_BASE + NIC_RCV_CONFIG] = RCR_REJECT_ERR;
    XBYTE[NE2000_BASE + NIC_XMIT_CONFIG] = TCR_LOOPBACK;
    XBYTE[NE2000_BASE + NIC_BOUNDARY] = NE2000_NIC_PAGE_START;
    XBYTE[NE2000_BASE + NIC_PAGE_START] = NE2000_NIC_PAGE_START;
    XBYTE[NE2000_BASE + NIC_PAGE_STOP] = NE2000_NIC_PAGE_STOP;

    XBYTE[NE2000_BASE + NIC_INTR_STATUS] = 0xff;	    
//    XBYTE[NE2000_BASE + NIC_INTR_MASK] = IMR_RCV + IMR_XMIT + IMR_XMIT_ERR + IMR_OVERFLOW;// Enable interrupts.
//    XBYTE[NE2000_BASE + NIC_INTR_MASK] = IMR_XMIT + IMR_XMIT_ERR;   // Enable interrupts.
    XBYTE[NE2000_BASE + NIC_INTR_MASK] = 0;		// Disable interrupts

    // Move to page 1 to write the station address
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_STOP + CR_NO_DMA + CR_PAGE1;

	p_memcpy_mac((PXCHAR)(NE2000_BASE + NIC_PHYS_ADDR), Local_pMacAddress);
	p_memset_x((PXCHAR)(NE2000_BASE + NIC_MC_ADDR), 0x00, 8);
	XBYTE[NE2000_BASE + NIC_MC_ADDR + 2] = 0x04;	// the related MAC is 01005e010205, multicast ip is 224.1.2.5.

    // Write out the current receive buffer to receive into
    _iCurrentPacket = NE2000_NIC_PAGE_START + 1;
    XBYTE[NE2000_BASE + NIC_CURRENT] = _iCurrentPacket;

    // move back to page 0 and start the card...
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_STOP + CR_NO_DMA + CR_PAGE0;
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_START + CR_NO_DMA + CR_PAGE0;

    // ... but it is still in loopback mode.
    // Start up the adapter.
    XBYTE[NE2000_BASE+NIC_XMIT_CONFIG] = TCR_NO_LOOPBACK;

	Adapter_bOK = TRUE;
	return TRUE;
}

// This routine examines if the packet at the head of the packet list can be copied to the adapter, and does so.
void Ne2000Send(USHORT iSendLen, PXCHAR pSendPacket)
{
    DCHAR iVal;
	DSHORT iLen;
	XCHAR iDummy;
	XSHORT iCount;
	PXCHAR_D pCur;

	iLen = iSendLen;

	if (VLAN_bEnable)
	{
		pCur = (PXCHAR)(pSendPacket - VLAN_HEADER_SIZE);
		p_memcpy_x2x(pCur, pSendPacket, EP_TYPE);
		USHORT2PXCHAR(EPT_VLAN, (PXCHAR)(pCur + EP_TYPE));
		USHORT2PXCHAR(VLAN_iID, (PXCHAR)(pCur + EP_TYPE + VLAN_TCI));
		iLen += VLAN_HEADER_SIZE;
	}
	else
	{
		pCur = pSendPacket;
	}

	if (iLen < NE2000_MIN_SEND)
	{
		iLen = NE2000_MIN_SEND;
	}
/*
	iVal = P_HIBYTE(iLen);
	if ((_iCurrentPacket-NE2000_NIC_PAGE_START) <= iVal)
	{
		iVal = NE2000_NIC_PAGE_STOP - 1 - iVal;
	}
	else
	{
		iVal = _iCurrentPacket - iVal - 1;
	}
*/
	if (_iCurrentPacket < (NE2000_NIC_PAGE_START+6))
	{
		iVal = NE2000_NIC_PAGE_STOP - 6;
	}
	else
	{
		iVal = _iCurrentPacket - 6;
	}

//	if (XBYTE[NE2000_BASE + NIC_INTR_STATUS] != ISR_EMPTY)	return;

    // Read the Command Register, to make sure it is ready for a write
	iDummy = XBYTE[NE2000_BASE + NIC_COMMAND];

    // Do Write errata as described on pages 1-143 and 1-144 of the 1992 LAN databook
	XBYTE[NE2000_BASE + NIC_COMMAND] = CR_PAGE0;
    XBYTE[NE2000_BASE + NIC_RMT_ADDR_LSB] = 0;  // The LSB of NE2000_RAM_BASE is 0 
    XBYTE[NE2000_BASE + NIC_RMT_ADDR_MSB] = iVal;
    XBYTE[NE2000_BASE + NIC_RMT_COUNT_LSB] = 0x2;
    XBYTE[NE2000_BASE + NIC_RMT_COUNT_MSB] = 0x0;

    // Set direction (Read)
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_START + CR_PAGE0 + CR_DMA_READ;

    // Read from port
    iDummy = XBYTE[NE2000_BASE + NIC_RACK_NIC];
    iDummy = XBYTE[NE2000_BASE + NIC_RACK_NIC];

    // Wait for addr to change
    while (1) 
    {
		if (XBYTE[NE2000_BASE + NIC_CRDA_LSB] || XBYTE[NE2000_BASE + NIC_CRDA_MSB] != iVal)
		{
			break;
		}
    }

    // Set iCount and destination address
	XBYTE[NE2000_BASE + NIC_COMMAND] = CR_PAGE0;
    XBYTE[NE2000_BASE + NIC_RMT_ADDR_LSB] = 0;						// The LSB of NE2000_RAM_BASE is 0 
    XBYTE[NE2000_BASE + NIC_RMT_ADDR_MSB] = iVal;
//    XBYTE[NE2000_BASE + NIC_RMT_COUNT_LSB] = iLow;					// LSB(Length)
//    XBYTE[NE2000_BASE + NIC_RMT_COUNT_MSB] = iHigh;                 // MSB(Length)
	USHORT2PXCHAR_L(iLen, (PXCHAR)(NE2000_BASE+NIC_RMT_COUNT_LSB));

    // Set direction (Write)
    XBYTE[NE2000_BASE + NIC_COMMAND] = CR_START + CR_PAGE0 + CR_DMA_WRITE;

    // Repeatedly write to out port
	p_outp(NE2000_BASE + NIC_RACK_NIC, pCur, iLen);

    // Wait for DMA to complete
	_CardWaitStatus(ISR_DMA_DONE);

    XBYTE[NE2000_BASE + NIC_INTR_MASK] = IMR_XMIT + IMR_XMIT_ERR;   // Enable interrupts.

    // Prepare the NIC registers for transmission.
    XBYTE[NE2000_BASE+NIC_XMIT_START] = iVal;

//    XBYTE[NE2000_BASE+NIC_XMIT_COUNT_MSB] = iHigh;	// MSB(Length)
//    XBYTE[NE2000_BASE+NIC_XMIT_COUNT_LSB] = iLow;		// LSB(Length)
	USHORT2PXCHAR_L(iLen, (PXCHAR)(NE2000_BASE+NIC_XMIT_COUNT_LSB));

    // Start transmission, check for power failure first.
    iVal = XBYTE[NE2000_BASE + NIC_COMMAND];
    XBYTE[NE2000_BASE+NIC_COMMAND] = CR_START + CR_XMIT + CR_NO_DMA;

	iCount = 0xffff;
	while (iCount)
	{
		if (XBYTE[IO_HI_DIN] & NE2000_PIO_INT_OR)
		{
		    // Get any new interrupts
			iVal = XBYTE[NE2000_BASE+NIC_INTR_STATUS];
		    if (iVal != ISR_EMPTY)
			{
				// Acknowledge the interrupt
				XBYTE[NE2000_BASE+NIC_INTR_STATUS] = iVal;
			}
	        if (iVal & (ISR_XMIT+ISR_XMIT_ERR))
		    {
				break;								// Wait for the current send to complete 	
			}
		}
		iCount --;
	}
    XBYTE[NE2000_BASE + NIC_INTR_MASK] = 0;							// Disable interrupts.
}

// This is the real interrupt handler for receive/overflow interrupt.
// Called when a receive interrupt is received. It first indicates all packets on the card and finally indicates ReceiveComplete().
void Ne2000Run()
{
//    DCHAR iNumber, iCurrent;
    DCHAR iCurrent;

    iCurrent = _CardGetCurrent();
//	iNumber = 0;
//	while (iCurrent != _iCurrentPacket && iNumber < 10)
	while (iCurrent != _iCurrentPacket)
    {
        _ReadPacket();	        // Read packet
//		iNumber ++;

        // Move to next packet.
        iCurrent = _CardGetCurrent();
		_iCurrentPacket = (_iNextPacket) ? _iNextPacket : iCurrent;

        // Sets the value of the "boundary" NIC register to one behind _iCurrentPacket, 
        // to prevent packets from being received on top of un-indicated ones.
        // Have to be careful with "one behind NicNextPacket" when NicNextPacket is the first buffer in receive area.
        if (_iCurrentPacket == NE2000_NIC_PAGE_START) 
        {
            XBYTE[NE2000_BASE+NIC_BOUNDARY] = NE2000_NIC_PAGE_STOP-1;
        } 
        else 
        {
            XBYTE[NE2000_BASE+NIC_BOUNDARY] = _iCurrentPacket-1;
        }
    }
}

