#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.

// 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];

static BOOLEAN _bAdapterOK;

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

void Ne2000Read(USHORT sAddr, PXCHAR pBuf, USHORT sLength)
{
//    DCHAR iDummy;
	DSHORT sLen;

	sLen = sLength;

    // 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;
	USHORT2PXCHAR_L(sAddr, (PXCHAR)(NE2000_BASE+NIC_RMT_ADDR_LSB));
	USHORT2PXCHAR_L(sLen, (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, pBuf, sLen);

    // 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_SIZE + NE2000_HEADER_SIZE)
	{
		return FALSE;
	}
	return TRUE;
}

void _IPReceive(USHORT sAddress)
{
	DCHAR iVal;
	DSHORT sAddr;
	DCHAR iHeadLen;

	sAddr = sAddress;

	Ne2000Read(sAddr, IP_pReceiveHeader, IP_DATA);
	Adapter_iReceiveLen = PXCHAR2USHORT((PXCHAR)(IP_pReceiveHeader+IP_LEN));
	if (Adapter_iReceiveLen <= IP_DATA || Adapter_iReceiveLen > (NE2000_MAX_SIZE - EP_DATA))
	{	// error length
		return;
	}

	iHeadLen = (IP_pReceiveHeader[IP_VERLEN] & 0x0f) << 2;
	sAddr += iHeadLen;
	Adapter_iReceiveLen -= iHeadLen;

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

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

void _ReadPacket()
{
	DSHORT iType;
	DSHORT sAddr;
	XSHORT iVlanID;
	XCHAR pVLAN[VLAN_HEADER_SIZE];

	sAddr = _iCurrentPacket;
	sAddr <<= 8;
	Ne2000Read(sAddr, Mac_pReceiveHeader, NE2000_HEADER_SIZE + EP_DATA);
	sAddr += NE2000_HEADER_SIZE + EP_DATA;

	p_memset_ip_0(Adapter_pReceiveIPAddress);

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

	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)
	{
		Ne2000Read(sAddr, pVLAN, VLAN_HEADER_SIZE);
		sAddr += VLAN_HEADER_SIZE;
		iVlanID = PXCHAR2USHORT(pVLAN) & VLAN_ID_MASK;
		if (VLAN_bEnable && iVlanID != VLAN_iID)
		{
			return;
		}
		iType = PXCHAR2USHORT((PXCHAR)(pVLAN + 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(sAddr);
	}
	else if (iType == EPT_ARP)
	{
		// DHCP will use ARP before Discover_bFinished is TRUE
//		if (!Discover_bFinished)	return;
		Ne2000Read(sAddr, (PXCHAR)Adapter_pReceivePacket, ARP_LENGTH);
		ArpRun();
	}
	else if (iType == EPT_PPPOE_INIT)
	{
		Ne2000Read(sAddr, PPPoE_pHead, PPPOE_INIT_INFO);
		sAddr += PPPOE_INIT_INFO;
		Adapter_iReceiveLen -= PPPOE_INIT_INFO;
		Ne2000Read(sAddr, (PXCHAR)Adapter_pReceivePacket, Adapter_iReceiveLen);
		PPPoE_InitRun(); 
	}
    else if (iType == EPT_PPPOE)
	{
		Ne2000Read(sAddr, PPPoE_pHead, PPPOE_INFO);
		sAddr += PPPOE_INFO;
		Adapter_iReceiveLen -= PPPOE_INFO;
		if (PPPoE_IsIP())
		{
			_IPReceive(sAddr);
		}
		else
		{
			Ne2000Read(sAddr, (PXCHAR)Adapter_pReceivePacket, 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))
    {
		_bAdapterOK = 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))
    {
		_bAdapterOK = 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;

	_bAdapterOK = TRUE;
	return TRUE;
}

void Ne2000Write(USHORT sAddr, PXCHAR pBuf, USHORT sLen)
{
//	XCHAR iDummy;

//	iDummy = XBYTE[NE2000_BASE + NIC_COMMAND];

    // Set iCount and destination address
	XBYTE[NE2000_BASE + NIC_COMMAND] = CR_PAGE0;
	USHORT2PXCHAR_L(sAddr, (PXCHAR)(NE2000_BASE+NIC_RMT_ADDR_LSB));
	USHORT2PXCHAR_L(sLen, (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, pBuf, sLen);

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

// 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;
	PXCHAR_D pCur;

	if (!_bAdapterOK)	return;

	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 (XBYTE[NE2000_BASE + NIC_INTR_STATUS] != ISR_EMPTY)	return;

	Ne2000Write(NE2000_RAM_BASE, pCur, iLen);

    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] = NE2000_NIC_XMIT_START;

	USHORT2PXCHAR_L((iLen < NE2000_MIN_SIZE) ? NE2000_MIN_SIZE : 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;

	while (1)
	{
		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 	
			}
		}
	}
    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 iCurrent;

	if (!_bAdapterOK)	return;

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

		if (iCurrent != _iCurrentPacket)
		{
			_ReadPacket();	        // Read packet
			_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.
			XBYTE[NE2000_BASE+NIC_BOUNDARY] = (_iCurrentPacket == NE2000_NIC_PAGE_START) ? (NE2000_NIC_PAGE_STOP - 1) : (_iCurrentPacket - 1);
		}
		else
		{
			break;
		}
	}

/*    iCurrent = _CardGetCurrent();
	while (iCurrent != _iCurrentPacket)
    {
        _ReadPacket();	        // Read packet

        // 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;
        }
    }
*/
}

