/*********************************************************************
 *
 *	Medium Access Control (MAC) Layer for Microchip ENC28J60
 *  Module for Microchip TCP/IP Stack
 *	 -Provides access to ENC28J60 Ethernet controller
 *	 -Reference: ENC28J60 Data sheet, IEEE 802.3 Standard
 *
 *********************************************************************
 * FileName:        ENC28J60.c
 * Dependencies:    ENC28J60.h
 *					MAC.h
 *					string.h
 *                  StackTsk.h
 *                  Helpers.h
 *					Delay.h
 * Processor:       PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F, PIC32
 * Compiler:        Microchip C32 v1.00 or higher
 *					Microchip C30 v3.01 or higher
 *					Microchip C18 v3.13 or higher
 *					HI-TECH PICC-18 STD 9.50PL3 or higher
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * Copyright (C) 2002-2008 Microchip Technology Inc.  All rights 
 * reserved.
 *
 * Microchip licenses to you the right to use, modify, copy, and 
 * distribute: 
 * (i)  the Software when embedded on a Microchip microcontroller or 
 *      digital signal controller product ("Device") which is 
 *      integrated into Licensee's product; or
 * (ii) ONLY the Software driver source files ENC28J60.c and 
 *      ENC28J60.h ported to a non-Microchip device used in 
 *      conjunction with a Microchip ethernet controller for the 
 *      sole purpose of interfacing with the ethernet controller. 
 *
 * You should refer to the license agreement accompanying this 
 * Software for additional information regarding your rights and 
 * obligations.
 *
 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT 
 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT 
 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL 
 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR 
 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF 
 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS 
 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE 
 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER 
 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT 
 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
 *
 *
 * Author               Date   		Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Howard Schlunder		6/28/04	Original
 * Howard Schlunder		10/8/04	Cleanup
 * Howard Schlunder		10/19/04 Small optimizations and more cleanup
 * Howard Schlunder		11/29/04 Added Set/GetCLKOUT
 * Howard Schlunder		12/23/05 Added B1 silicon errata workarounds
 * Howard Schlunder		1/09/06	Added comments and minor mods
 * Howard Schlunder		1/18/06 Added more silicon errata workarounds
 * Howard Schlunder		6/16/06 Synchronized with PIC18F97J60 code
 * Howard Schlunder		7/17/06 Updated TestMemory() for C30
 * Howard Schlunder		8/07/06	Added SetRXHashTableEntry() function
********************************************************************/
#define __ENC28J60_C

#include "TCPIP Stack/TCPIP.h"


// Make sure that this hardware profile has an ENC28J60 in it
#if defined(ENC_CS_TRIS)


/** D E F I N I T I O N S ****************************************************/
// IMPORTANT SPI NOTE: The code in this file expects that the SPI interrupt 
//		flag (ENC_SPI_IF) be clear at all times.  If the SPI is shared with 
//		other hardware, the other code should clear the ENC_SPI_IF when it is 
//		done using the SPI.

// Since the ENC28J60 doesn't support auto-negotiation, full-duplex mode is 
// not compatible with most switches/routers.  If a dedicated network is used 
// where the duplex of the remote node can be manually configured, you may 
// change this configuration.  Otherwise, half duplex should always be used.
#define HALF_DUPLEX
//#define FULL_DUPLEX
//#define LEDB_DUPLEX

// Pseudo Functions
#define LOW(a) 					((a) & 0xFF)
#define HIGH(a) 				(((a)>>8) & 0xFF)

// ENC28J60 Opcodes (to be ORed with a 5 bit address)
#define	WCR (0x2<<5)			// Write Control Register command
#define BFS (0x4<<5)			// Bit Field Set command
#define	BFC (0x5<<5)			// Bit Field Clear command
#define	RCR (0x0<<5)			// Read Control Register command
#define RBM ((0x1<<5) | 0x1A)	// Read Buffer Memory command
#define	WBM ((0x3<<5) | 0x1A) // Write Buffer Memory command
#define	SR  ((0x7<<5) | 0x1F)	// System Reset command does not use an address.  
								//   It requires 0x1F, however.

// PIC32MX460F512L USB PIM on Explorer 16 SPI port 1 can't operate with a 
// maximum 20MHz SPI clock.  74HCT4053 analog mux chips damage the SPI SDI 
// signal too much.
#if defined(__PIC32MX__) && defined(EXPLORER_16) && defined(_USB) && (ENC_SSPBUF == SPI1BUF)
	#define ENC_MAX_SPI_FREQ	(13333333ul)	// Hz
#else
	#define ENC_MAX_SPI_FREQ	(20000000ul)	// Hz
#endif

#define ETHER_IP	(0x00u)
#define ETHER_ARP	(0x06u)

// A header appended at the start of all RX frames by the hardware
typedef struct  __attribute__((aligned(2), packed))
{
    WORD			NextPacketPointer;
    RXSTATUS		StatusVector;

    MAC_ADDR        DestMACAddr;
    MAC_ADDR        SourceMACAddr;
    WORD_VAL        Type;
} ENC_PREAMBLE;


// Prototypes of functions intended for MAC layer use only.
static void BankSel(WORD Register);
static REG ReadETHReg(BYTE Address);
static REG ReadMACReg(BYTE Address);
static void WriteReg(BYTE Address, BYTE Data);
static void BFCReg(BYTE Address, BYTE Data);
static void BFSReg(BYTE Address, BYTE Data);
static void SendSystemReset(void);
//static void GetRegs(void);
//void Get8KBRAM(void);

// Internal MAC level variables and flags.
static WORD_VAL NextPacketLocation;
static WORD_VAL CurrentPacketLocation;
static BOOL WasDiscarded;
static BYTE ENCRevID;


//NOTE: All code in this module expects Bank 0 to be currently selected.  If code ever changes the bank, it must restore it to Bank 0 before returning.

/******************************************************************************
 * Function:        void MACInit(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        MACInit sets up the PIC's SPI module and all the 
 *					registers in the ENC28J60 so that normal operation can 
 *					begin.
 *
 * Note:            None
 *****************************************************************************/
void MACInit(void)
{
	BYTE i;

	// Set up the SPI module on the PIC for communications with the ENC28J60
	ENC_CS_IO = 1;
	ENC_CS_TRIS = 0;		// Make the Chip Select pin an output

#if defined(__18CXX)
    ENC_SCK_TRIS = 0;
    ENC_SDO_TRIS = 0;
    ENC_SDI_TRIS = 1;
#endif

	// If the RESET pin is connected, take the chip out of reset
#if defined(ENC_RST_IO)
	ENC_RST_IO 		= 1;			
	ENC_RST_TRIS 	= 0;
#endif
	
    // Set up SPI
#if defined(__18CXX)
	ENC_SPICON1 = 0x20;		// SSPEN bit is set, SPI in master mode, FOSC/4, 
							//   IDLE state is low level
	ENC_SPI_IF = 0;
	ENC_SPISTATbits.CKE = 1;// Transmit data on rising edge of clock
	ENC_SPISTATbits.SMP = 0;// Input sampled at middle of data output time
#elif defined(__C30__)
    ENC_SPISTAT = 0;    	// clear SPI
	#if defined(__PIC24H__) || defined(__dsPIC33F__)
	    ENC_SPICON1 = 0x0F; 	// 1:1 primary prescale, 5:1 secondary prescale	(8MHz  @ 40MIPS)
	//    ENC_SPICON1 = 0x1E; 	// 4:1 primary prescale, 1:1 secondary prescale	(10MHz @ 40MIPS, Doesn't work.  CLKRDY is incorrectly reported as being clear.  Problem caused by dsPIC33/PIC24H ES silicon bug.)
	#elif defined(__PIC24F__)
	//    ENC_SPICON1 = 0x1F; 	// 1:1 prescale	broken on PIC24F ES silicon 	(16MHz @ 16MIPS)
	    ENC_SPICON1 = 0x1B; 	// 1:1 primary prescale, 2:1 secondary prescale	(8MHz  @ 16MIPS)
	#else	// dsPIC30F
	    ENC_SPICON1 = 0x17; 	// 1:1 primary prescale, 3:1 secondary prescale	(10MHz @ 30MIPS)
	#endif
    ENC_SPICON2 = 0;
    ENC_SPICON1bits.CKE = 1;
    ENC_SPICON1bits.MSTEN = 1;
    ENC_SPISTATbits.SPIEN = 1;
#elif defined(__C32__)
	ENC_SPIBRG = (GetPeripheralClock()-1ul)/2ul/ENC_MAX_SPI_FREQ;
    ENC_SPICON1bits.CKE = 1;
    ENC_SPICON1bits.MSTEN = 1;
	ENC_SPICON1bits.ON = 1;
#endif

	// RESET the entire ENC28J60, clearing all registers
	// Also wait for CLKRDY to become set.
	// Bit 3 in ESTAT is an unimplemented bit.  If it reads out as '1' that
	// means the part is in RESET or there is something wrong with the SPI 
	// connection.  This loop makes sure that we can communicate with the 
	// ENC28J60 before proceeding.
	do
	{
		SendSystemReset();	
		i = ReadETHReg(ESTAT).Val;
	} while((i & 0x08) || (~i & ESTAT_CLKRDY));

	// Start up in Bank 0 and configure the receive buffer boundary pointers 
	// and the buffer write protect pointer (receive buffer read pointer)
	WasDiscarded = TRUE;
	NextPacketLocation.Val = RXSTART;

	WriteReg(ERXSTL, LOW(RXSTART));
	WriteReg(ERXSTH, HIGH(RXSTART));
	WriteReg(ERXRDPTL, LOW(RXSTOP));	// Write low byte first
	WriteReg(ERXRDPTH, HIGH(RXSTOP));	// Write high byte last
	WriteReg(ERXNDL, LOW(RXSTOP));
	WriteReg(ERXNDH, HIGH(RXSTOP));
	WriteReg(ETXSTL, LOW(TXSTART));
	WriteReg(ETXSTH, HIGH(TXSTART));

	// Write a permanant per packet control byte of 0x00
	WriteReg(EWRPTL, LOW(TXSTART));
	WriteReg(EWRPTH, HIGH(TXSTART));
	MACPut(0x00);


	// Enter Bank 1 and configure Receive Filters 
	// (No need to reconfigure - Unicast OR Broadcast with CRC checking is 
	// acceptable)
	// Write ERXFCON_CRCEN only to ERXFCON to enter promiscuous mode

	// Promiscious mode example:
	//BankSel(ERXFCON);
	//WriteReg((BYTE)ERXFCON, ERXFCON_CRCEN);
	
	// Enter Bank 2 and configure the MAC
	BankSel(MACON1);

	// Enable the receive portion of the MAC
	WriteReg((BYTE)MACON1, MACON1_TXPAUS | MACON1_RXPAUS | MACON1_MARXEN);
	
	// Pad packets to 60 bytes, add CRC, and check Type/Length field.
#if defined(FULL_DUPLEX)
	WriteReg((BYTE)MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX);
	WriteReg((BYTE)MABBIPG, 0x15);	
#else
	WriteReg((BYTE)MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN);
	WriteReg((BYTE)MABBIPG, 0x12);	
#endif

    // Allow infinite deferals if the medium is continuously busy 
    // (do not time out a transmission if the half duplex medium is 
    // completely saturated with other people's data)
    WriteReg((BYTE)MACON4, MACON4_DEFER);

	// Late collisions occur beyond 63+8 bytes (8 bytes for preamble/start of frame delimiter)
	// 55 is all that is needed for IEEE 802.3, but ENC28J60 B5 errata for improper link pulse 
	// collisions will occur less often with a larger number.
    WriteReg((BYTE)MACLCON2, 63);
	
	// Set non-back-to-back inter-packet gap to 9.6us.  The back-to-back 
	// inter-packet gap (MABBIPG) is set by MACSetDuplex() which is called 
	// later.
	WriteReg((BYTE)MAIPGL, 0x12);
	WriteReg((BYTE)MAIPGH, 0x0C);

	// Set the maximum packet size which the controller will accept
	WriteReg((BYTE)MAMXFLL, LOW(6+6+2+1500+4));	 // 1518 is the IEEE 802.3 specified limit
	WriteReg((BYTE)MAMXFLH, HIGH(6+6+2+1500+4)); // 1518 is the IEEE 802.3 specified limit
	
    // Enter Bank 3 and initialize physical MAC address registers
	BankSel(MAADR1);
    WriteReg((BYTE)MAADR1, AppConfig.MyMACAddr.v[0]);
    WriteReg((BYTE)MAADR2, AppConfig.MyMACAddr.v[1]);
    WriteReg((BYTE)MAADR3, AppConfig.MyMACAddr.v[2]);
    WriteReg((BYTE)MAADR4, AppConfig.MyMACAddr.v[3]);
    WriteReg((BYTE)MAADR5, AppConfig.MyMACAddr.v[4]);
    WriteReg((BYTE)MAADR6, AppConfig.MyMACAddr.v[5]);

	// Disable the CLKOUT output to reduce EMI generation
	WriteReg((BYTE)ECOCON, 0x00);	// Output off (0V)
	//WriteReg((BYTE)ECOCON, 0x01);	// 25.000MHz
	//WriteReg((BYTE)ECOCON, 0x03);	// 8.3333MHz (*4 with PLL is 33.3333MHz)

	// Get the Rev ID so that we can implement the correct errata workarounds
	ENCRevID = ReadETHReg((BYTE)EREVID).Val;

	// Disable half duplex loopback in PHY.  Bank bits changed to Bank 2 as a 
	// side effect.
	WritePHYReg(PHCON2, PHCON2_HDLDIS);

	// Configure LEDA to display LINK status, LEDB to display TX/RX activity
	SetLEDConfig(0x3472);
	
	// Set the MAC and PHY into the proper duplex state
#if defined(FULL_DUPLEX)
	WritePHYReg(PHCON1, PHCON1_PDPXMD);
#elif defined(HALF_DUPLEX)
	WritePHYReg(PHCON1, 0x0000);
#else
	// Use the external LEDB polarity to determine weather full or half duplex 
	// communication mode should be set.  
	{
		REG Register;
		PHYREG PhyReg;
		
		// Read the PHY duplex mode
		PhyReg = ReadPHYReg(PHCON1);
		DuplexState = PhyReg.PHCON1bits.PDPXMD;
	
		// Set the MAC to the proper duplex mode
		BankSel(MACON3);
		Register = ReadMACReg((BYTE)MACON3);
		Register.MACON3bits.FULDPX = PhyReg.PHCON1bits.PDPXMD;
		WriteReg((BYTE)MACON3, Register.Val);
	
		// Set the back-to-back inter-packet gap time to IEEE specified 
		// requirements.  The meaning of the MABBIPG value changes with the duplex
		// state, so it must be updated in this function.
		// In full duplex, 0x15 represents 9.6us; 0x12 is 9.6us in half duplex
		WriteReg((BYTE)MABBIPG, PhyReg.PHCON1bits.PDPXMD ? 0x15 : 0x12);	
	}
#endif

	BankSel(ERDPTL);		// Return to default Bank 0

	// Enable packet reception
	BFSReg(ECON1, ECON1_RXEN);
}//end MACInit


/******************************************************************************
 * Function:        BOOL MACIsLinked(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          TRUE: If the PHY reports that a link partner is present 
 *						  and the link has been up continuously since the last 
 *						  call to MACIsLinked()
 *					FALSE: If the PHY reports no link partner, or the link went 
 *						   down momentarily since the last call to MACIsLinked()
 *
 * Side Effects:    None
 *
 * Overview:        Returns the PHSTAT1.LLSTAT bit.
 *
 * Note:            None
 *****************************************************************************/
BOOL MACIsLinked(void)
{
	// LLSTAT is a latching low link status bit.  Therefore, if the link 
	// goes down and comes back up before a higher level stack program calls
	// MACIsLinked(), MACIsLinked() will still return FALSE.  The next 
	// call to MACIsLinked() will return TRUE (unless the link goes down 
	// again).
	return ReadPHYReg(PHSTAT1).PHSTAT1bits.LLSTAT;
}


/******************************************************************************
 * Function:        BOOL MACIsTxReady(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          TRUE: If no Ethernet transmission is in progress
 *					FALSE: If a previous transmission was started, and it has 
 *						   not completed yet.  While FALSE, the data in the 
 *						   transmit buffer and the TXST/TXND pointers must not
 *						   be changed.
 *
 * Side Effects:    None
 *
 * Overview:        Returns the ECON1.TXRTS bit
 *
 * Note:            None
 *****************************************************************************/
BOOL MACIsTxReady(void)
{
	return !ReadETHReg(ECON1).ECON1bits.TXRTS;
}


/******************************************************************************
 * Function:        void MACDiscardRx(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Marks the last received packet (obtained using 
 *					MACGetHeader())as being processed and frees the buffer 
 *					memory associated with it
 *
 * Note:            Is is safe to call this function multiple times between 
 *					MACGetHeader() calls.  Extra packets won't be thrown away 
 *					until MACGetHeader() makes it available.
 *****************************************************************************/
void MACDiscardRx(void)
{
	WORD_VAL NewRXRDLocation;

	// Make sure the current packet was not already discarded
	if(WasDiscarded)
		return;
	WasDiscarded = TRUE;
	
	// Decrement the next packet pointer before writing it into 
	// the ERXRDPT registers.  This is a silicon errata workaround.
	// RX buffer wrapping must be taken into account if the 
	// NextPacketLocation is precisely RXSTART.
	NewRXRDLocation.Val = NextPacketLocation.Val - 1;
	if(NewRXRDLocation.Val > RXSTOP)
	{
		NewRXRDLocation.Val = RXSTOP;
	}

	// Decrement the RX packet counter register, EPKTCNT
	BFSReg(ECON2, ECON2_PKTDEC);

	// Move the receive read pointer to unwrite-protect the memory used by the 
	// last packet.  The writing order is important: set the low byte first, 
	// high byte last.
	WriteReg(ERXRDPTL, NewRXRDLocation.v[0]);
	WriteReg(ERXRDPTH, NewRXRDLocation.v[1]);
}


/******************************************************************************
 * Function:        WORD MACGetFreeRxSize(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          A WORD estimate of how much RX buffer space is free at 
 *					the present time.
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            None
 *****************************************************************************/
WORD MACGetFreeRxSize(void)
{
	WORD_VAL ReadPT, WritePT;

	// Read the Ethernet hardware buffer write pointer.  Because packets can be 
	// received at any time, it can change between reading the low and high 
	// bytes.  A loop is necessary to make certain a proper low/high byte pair
	// is read.
	BankSel(EPKTCNT);
	do {
		// Save EPKTCNT in a temporary location
		ReadPT.v[0] = ReadETHReg((BYTE)EPKTCNT).Val;
	
		BankSel(ERXWRPTL);
		WritePT.v[0] = ReadETHReg(ERXWRPTL).Val;
		WritePT.v[1] = ReadETHReg(ERXWRPTH).Val;
	
		BankSel(EPKTCNT);
	} while(ReadETHReg((BYTE)EPKTCNT).Val != ReadPT.v[0]);
	
	// Determine where the write protection pointer is
	BankSel(ERXRDPTL);
	ReadPT.v[0] = ReadETHReg(ERXRDPTL).Val;
	ReadPT.v[1] = ReadETHReg(ERXRDPTH).Val;
	
	// Calculate the difference between the pointers, taking care to account 
	// for buffer wrapping conditions
	if(WritePT.Val > ReadPT.Val)
	{
		return (RXSTOP - RXSTART) - (WritePT.Val - ReadPT.Val);
	}
	else if(WritePT.Val == ReadPT.Val)
	{
		return RXSIZE - 1;
	}
	else
    {
		return ReadPT.Val - WritePT.Val - 1;
	}
}

/******************************************************************************
 * Function:        BOOL MACGetHeader(MAC_ADDR *remote, BYTE* type)
 *
 * PreCondition:    None
 *
 * Input:           *remote: Location to store the Source MAC address of the 
 *							 received frame.
 *					*type: Location of a BYTE to store the constant 
 *						   MAC_UNKNOWN, ETHER_IP, or ETHER_ARP, representing 
 *						   the contents of the Ethernet type field.
 *
 * Output:          TRUE: If a packet was waiting in the RX buffer.  The 
 *						  remote, and type values are updated.
 *					FALSE: If a packet was not pending.  remote and type are 
 *						   not changed.
 *
 * Side Effects:    Last packet is discarded if MACDiscardRx() hasn't already
 *					been called.
 *
 * Overview:        None
 *
 * Note:            None
 *****************************************************************************/
BOOL MACGetHeader(MAC_ADDR *remote, BYTE* type)
{
	ENC_PREAMBLE header;
	BYTE PacketCount;

	// Test if at least one packet has been received and is waiting
	BankSel(EPKTCNT);
	PacketCount = ReadETHReg((BYTE)EPKTCNT).Val;
	BankSel(ERDPTL);
	if(PacketCount == 0u)
        return FALSE;

	// Make absolutely certain that any previous packet was discarded
	if(WasDiscarded == FALSE)
	{
		MACDiscardRx();
		return FALSE;
	}

	// Set the SPI read pointer to the beginning of the next unprocessed packet
	CurrentPacketLocation.Val = NextPacketLocation.Val;
	WriteReg(ERDPTL, CurrentPacketLocation.v[0]);
	WriteReg(ERDPTH, CurrentPacketLocation.v[1]);

	// Obtain the MAC header from the Ethernet buffer
	MACGetArray((BYTE*)&header, sizeof(header));

	// The EtherType field, like most items transmitted on the Ethernet medium
	// are in big endian.
	header.Type.Val = swaps(header.Type.Val);

	// Validate the data returned from the ENC28J60.  Random data corruption, 
	// such as if a single SPI bit error occurs while communicating or a 
	// momentary power glitch could cause this to occur in rare circumstances.
	if(header.NextPacketPointer > RXSTOP || ((BYTE_VAL*)(&header.NextPacketPointer))->bits.b0 ||
	   header.StatusVector.bits.Zero ||
	   header.StatusVector.bits.CRCError ||
	   header.StatusVector.bits.ByteCount > 1518u ||
	   !header.StatusVector.bits.ReceiveOk)
	{
		Reset();
	}

	// Save the location where the hardware will write the next packet to
	NextPacketLocation.Val = header.NextPacketPointer;

	// Return the Ethernet frame's Source MAC address field to the caller
	// This parameter is useful for replying to requests without requiring an 
	// ARP cycle.
    memcpy((void*)remote->v, (void*)header.SourceMACAddr.v, sizeof(*remote));

	// Return a simplified version of the EtherType field to the caller
    *type = MAC_UNKNOWN;
    if( (header.Type.v[1] == 0x08u) && 
    	((header.Type.v[0] == ETHER_IP) || (header.Type.v[0] == ETHER_ARP)) )
    {
    	*type = header.Type.v[0];
    }

    // Mark this packet as discardable
    WasDiscarded = FALSE;	
	return TRUE;
}


/******************************************************************************
 * Function:        void MACPutHeader(MAC_ADDR *remote, BYTE type, WORD dataLen)
 *
 * PreCondition:    MACIsTxReady() must return TRUE.
 *
 * Input:           *remote: Pointer to memory which contains the destination
 * 							 MAC address (6 bytes)
 *					type: The constant ETHER_ARP or ETHER_IP, defining which 
 *						  value to write into the Ethernet header's type field.
 *					dataLen: Length of the Ethernet data payload
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        None
 *
 * Note:            Because of the dataLen parameter, it is probably 
 *					advantagous to call this function immediately before 
 *					transmitting a packet rather than initially when the 
 *					packet is first created.  The order in which the packet
 *					is constructed (header first or data first) is not 
 *					important.
 *****************************************************************************/
void MACPutHeader(MAC_ADDR *remote, BYTE type, WORD dataLen)
{
	// Set the SPI write pointer to the beginning of the transmit buffer (post per packet control byte)
	WriteReg(EWRPTL, LOW(TXSTART+1));
	WriteReg(EWRPTH, HIGH(TXSTART+1));

	// Calculate where to put the TXND pointer
    dataLen += (WORD)sizeof(ETHER_HEADER) + TXSTART;

	// Write the TXND pointer into the registers, given the dataLen given
	WriteReg(ETXNDL, ((WORD_VAL*)&dataLen)->v[0]);
	WriteReg(ETXNDH, ((WORD_VAL*)&dataLen)->v[1]);

	// Set the per-packet control byte and write the Ethernet destination 
	// address
    MACPutArray((BYTE*)remote, sizeof(*remote));

	// Write our MAC address in the Ethernet source field
	MACPutArray((BYTE*)&AppConfig.MyMACAddr, sizeof(AppConfig.MyMACAddr));

	// Write the appropriate Ethernet Type WORD for the protocol being used
    MACPut(0x08);
    MACPut((type == MAC_IP) ? ETHER_IP : ETHER_ARP);
}

/******************************************************************************
 * Function:        void MACFlush(void)
 *
 * PreCondition:    A packet has been created by calling MACPut() and 
 *					MACPutHeader().
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        MACFlush causes the current TX packet to be sent out on 
 *					the Ethernet medium.  The hardware MAC will take control
 *					and handle CRC generation, collision retransmission and 
 *					other details.
 *
 * Note:			After transmission completes (MACIsTxReady() returns TRUE), 
 *					the packet can be modified and transmitted again by calling 
 *					MACFlush() again.  Until MACPutHeader() or MACPut() is 
 *					called (in the TX data area), the data in the TX buffer 
 *					will not be corrupted.
 *****************************************************************************/
void MACFlush(void)
{
	// Reset transmit logic if a TX Error has previously occured
	// This is a silicon errata workaround
	BFSReg(ECON1, ECON1_TXRST);
	BFCReg(ECON1, ECON1_TXRST);
	BFCReg(EIR, EIR_TXERIF | EIR_TXIF);

	// Start the transmission
	// After transmission completes (MACIsTxReady() returns TRUE), the packet 
	// can be modified and transmitted again by calling MACFlush() again.
	// Until MACPutHeader() is called, the data in the TX buffer will not be 
	// corrupted.
	BFSReg(ECON1, ECON1_TXRTS);

	// Revision B5 and B7 silicon errata workaround
	if(ENCRevID == 0x05u || ENCRevID == 0x07u)
	{
		WORD AttemptCounter = 0x0000;
		while(!(ReadETHReg(EIR).Val & (EIR_TXERIF | EIR_TXIF)) && (++AttemptCounter < 1000));
		if(ReadETHReg(EIR).EIRbits.TXERIF || (AttemptCounter >= 1000))
		{
			WORD_VAL ReadPtrSave;
			WORD_VAL TXEnd;
			TXSTATUS TXStatus;
			BYTE i;

			// Cancel the previous transmission if it has become stuck set
			BFCReg(ECON1, ECON1_TXRTS);

			// Save the current read pointer (controlled by application)
			ReadPtrSave.v[0] = ReadETHReg(ERDPTL).Val;
			ReadPtrSave.v[1] = ReadETHReg(ERDPTH).Val;

			// Get the location of the transmit status vector
			TXEnd.v[0] = ReadETHReg(ETXNDL).Val;
			TXEnd.v[1] = ReadETHReg(ETXNDH).Val;
			TXEnd.Val++;
			
			// Read the transmit status vector
			WriteReg(ERDPTL, TXEnd.v[0]);
			WriteReg(ERDPTH, TXEnd.v[1]);
			MACGetArray((BYTE*)&TXStatus, sizeof(TXStatus));

			// Implement retransmission if a late collision occured (this can 
			// happen on B5 when certain link pulses arrive at the same time 
			// as the transmission)
			for(i = 0; i < 16u; i++)
			{
				if(ReadETHReg(EIR).EIRbits.TXERIF && TXStatus.bits.LateCollision)
				{
					// Reset the TX logic
					BFSReg(ECON1, ECON1_TXRST);
					BFCReg(ECON1, ECON1_TXRST);
					BFCReg(EIR, EIR_TXERIF | EIR_TXIF);

					// Transmit the packet again
					BFSReg(ECON1, ECON1_TXRTS);
					while(!(ReadETHReg(EIR).Val & (EIR_TXERIF | EIR_TXIF)));

					// Cancel the previous transmission if it has become stuck set
					BFCReg(ECON1, ECON1_TXRTS);

					// Read transmit status vector
					WriteReg(ERDPTL, TXEnd.v[0]);
					WriteReg(ERDPTH, TXEnd.v[1]);
					MACGetArray((BYTE*)&TXStatus, sizeof(TXStatus));
				}
				else
				{
					break;
				}
			}

			// Restore the current read pointer
			WriteReg(ERDPTL, ReadPtrSave.v[0]);
			WriteReg(ERDPTH, ReadPtrSave.v[1]);
		}
	}
}


/******************************************************************************
 * Function:        void MACSetReadPtrInRx(WORD offset)
 *
 * PreCondition:    A packet has been obtained by calling MACGetHeader() and 
 *					getting a TRUE result.
 *
 * Input:           offset: WORD specifying how many bytes beyond the Ethernet 
 *							header's type field to relocate the SPI read 
 *							pointer.
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        SPI read pointer are updated.  All calls to 
 *					MACGet() and MACGetArray() will use these new values.
 *
 * Note:			RXSTOP must be statically defined as being > RXSTART for 
 *					this function to work correctly.  In other words, do not 
 *					define an RX buffer which spans the 0x1FFF->0x0000 memory
 *					boundary.
 *****************************************************************************/
void MACSetReadPtrInRx(WORD offset)
{
	WORD_VAL ReadPT;

	// Determine the address of the beginning of the entire packet
	// and adjust the address to the desired location
	ReadPT.Val = CurrentPacketLocation.Val + sizeof(ENC_PREAMBLE) + offset;
	
	// Since the receive buffer is circular, adjust if a wraparound is needed
	if(ReadPT.Val > RXSTOP)
		ReadPT.Val -= RXSIZE;
	
	// Set the SPI read pointer to the new calculated value
	WriteReg(ERDPTL, ReadPT.v[0]);
	WriteReg(ERDPTH, ReadPT.v[1]);
}


/******************************************************************************
 * Function:        WORD MACSetWritePtr(WORD Address)
 *
 * PreCondition:    None
 *
 * Input:           Address: Address to seek to
 *
 * Output:          WORD: Old EWRPT location
 *
 * Side Effects:    None
 *
 * Overview:        SPI write pointer is updated.  All calls to 
 *					MACPut() and MACPutArray() will use this new value.
 *
 * Note:			None
 *****************************************************************************/
WORD MACSetWritePtr(WORD address)
{
	WORD_VAL oldVal;

	oldVal.v[0] = ReadETHReg(EWRPTL).Val;
	oldVal.v[1] = ReadETHReg(EWRPTH).Val;

	// Set the SPI write pointer to the new calculated value
	WriteReg(EWRPTL, ((WORD_VAL*)&address)->v[0]);
	WriteReg(EWRPTH, ((WORD_VAL*)&address)->v[1]);

	return oldVal.Val;
}

/******************************************************************************
 * Function:        WORD MACSetReadPtr(WORD Address)
 *
 * PreCondition:    None
 *
 * Input:           Address: Address to seek to
 *
 * Output:          WORD: Old ERDPT value
 *
 * Side Effects:    None
 *
 * Overview:        SPI write pointer is updated.  All calls to 
 *					MACPut() and MACPutArray() will use this new value.
 *
 * Note:			None
 *****************************************************************************/
WORD MACSetReadPtr(WORD address)
{
	WORD_VAL oldVal;

	oldVal.v[0] = ReadETHReg(ERDPTL).Val;
	oldVal.v[1] = ReadETHReg(ERDPTH).Val;

	// Set the SPI write pointer to the new calculated value
	WriteReg(ERDPTL, ((WORD_VAL*)&address)->v[0]);
	WriteReg(ERDPTH, ((WORD_VAL*)&address)->v[1]);

	return oldVal.Val;
}


/******************************************************************************
 * Function:        WORD MACCalcRxChecksum(WORD offset, WORD len)
 *
 * PreCondition:    None
 *
 * Input:           offset	- Number of bytes beyond the beginning of the 
 *							Ethernet data (first byte after the type field) 
 *							where the checksum should begin
 *					len		- Total number of bytes to include in the checksum
 *
 * Output:          16-bit checksum as defined by RFC 793.
 *
 * Side Effects:    None
 *
 * Overview:        This function performs a checksum calculation in the MAC
 *                  buffer itself
 *
 * Note:            None
 *****************************************************************************/
WORD MACCalcRxChecksum(WORD offset, WORD len)
{
	WORD_VAL temp;
	WORD_VAL RDSave;

	// Add the offset requested by firmware plus the Ethernet header
	temp.Val = CurrentPacketLocation.Val + sizeof(ENC_PREAMBLE) + offset;
	if(temp.Val > RXSTOP)		// Adjust value if a wrap is needed
	{
		temp.Val -= RXSIZE;
	}

	RDSave.v[0] = ReadETHReg(ERDPTL).Val;
	RDSave.v[1] = ReadETHReg(ERDPTH).Val;

	WriteReg(ERDPTL, temp.v[0]);
	WriteReg(ERDPTH, temp.v[1]);
	
	temp.Val = CalcIPBufferChecksum(len);

	WriteReg(ERDPTL, RDSave.v[0]);
	WriteReg(ERDPTH, RDSave.v[1]);

	return temp.Val;
}


/******************************************************************************
 * Function:        WORD CalcIPBufferChecksum(WORD len)
 *
 * PreCondition:    Read buffer pointer set to starting of checksum data
 *
 * Input:           len: Total number of bytes to calculate the checksum over. 
 *						 The first byte included in the checksum is the byte 
 *						 pointed to by ERDPT, which is updated by calls to 
 *						 MACSetReadPtr(), MACGet(), MACGetArray(), 
 *						 MACGetHeader(), etc.
 *
 * Output:          16-bit checksum as defined by RFC 793
 *
 * Side Effects:    None
 *
 * Overview:        This function performs a checksum calculation in the MAC
 *                  buffer itself.  The ENC28J60 has a hardware DMA module 
 *					which can calculate the checksum faster than software, so 
 *					this function replaces the CaclIPBufferChecksum() function 
 *					defined in the helpers.c file.  Through the use of 
 *					preprocessor defines, this replacement is automatic.
 *
 * Note:            This function works either in the RX buffer area or the TX
 *					buffer area.  No validation is done on the len parameter.
 *****************************************************************************/
WORD CalcIPBufferChecksum(WORD len)
{
	WORD_VAL Start;
	DWORD_VAL Checksum = {0x00000000ul};
	WORD ChunkLen;
	WORD DataBuffer[10];
	WORD *DataPtr;

	// Save the SPI read pointer starting address
	Start.v[0] = ReadETHReg(ERDPTL).Val;
	Start.v[1] = ReadETHReg(ERDPTH).Val;

	while(len)
	{
		// Obtain a chunk of data (less SPI overhead compared 
		// to requesting one byte at a time)
		ChunkLen = len > sizeof(DataBuffer) ? sizeof(DataBuffer) : len;
		MACGetArray((BYTE*)DataBuffer, ChunkLen);

		len -= ChunkLen;

		// Take care of a last odd numbered data byte
		if(((WORD_VAL*)&ChunkLen)->bits.b0)
		{
			((BYTE*)DataBuffer)[ChunkLen] = 0x00;
			ChunkLen++;
		}

		// Calculate the checksum over this chunk
		DataPtr = DataBuffer;
		while(ChunkLen)
		{
			Checksum.Val += *DataPtr++;
			ChunkLen -= 2;
		}
	}

	// Restore old read pointer location
	WriteReg(ERDPTL, Start.v[0]);
	WriteReg(ERDPTH, Start.v[1]);
	
	// Do an end-around carry (one's complement arrithmatic)
	Checksum.Val = (DWORD)Checksum.w[0] + (DWORD)Checksum.w[1];

	// Do another end-around carry in case if the prior add 
	// caused a carry out
	Checksum.w[0] += Checksum.w[1];

	// Return the resulting checksum
	return ~Checksum.w[0];
}


/******************************************************************************
 * Function:        void MACMemCopyAsync(WORD destAddr, WORD sourceAddr, WORD len)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 *
 * Input:           destAddr:	Destination address in the Ethernet memory to 
 *								copy to.  If the MSb is set, the current EWRPT 
 *								value will be used instead.
 *					sourceAddr:	Source address to read from.  If the MSb is 
 *								set, the current ERDPT value will be used 
 *								instead.
 *					len:		Number of bytes to copy
 *
 * Output:          Byte read from the ENC28J60's RAM
 *
 * Side Effects:    None
 *
 * Overview:        Bytes are asynchrnously transfered within the buffer.  Call 
 *					MACIsMemCopyDone() to see when the transfer is complete.  
 *
 * Note:            If a prior transfer is already in progress prior to 
 *					calling this function, this function will block until it 
 *					can start this transfer.
 *
 *					If a negative value is used for the sourceAddr or destAddr 
 *					parameters, then that pointer will get updated with the 
 *					next address after the read or write.
 *****************************************************************************/
void MACMemCopyAsync(WORD destAddr, WORD sourceAddr, WORD len)
{
	WORD_VAL ReadSave, WriteSave;
	BOOL UpdateWritePointer = FALSE;
	BOOL UpdateReadPointer = FALSE;

	if(((WORD_VAL*)&destAddr)->bits.b15)
	{
		UpdateWritePointer = TRUE;
		((WORD_VAL*)&destAddr)->v[0] = ReadETHReg(EWRPTL).Val;
		((WORD_VAL*)&destAddr)->v[1] = ReadETHReg(EWRPTH).Val;
	}
	if(((WORD_VAL*)&sourceAddr)->bits.b15)
	{
		UpdateReadPointer = TRUE;
		((WORD_VAL*)&sourceAddr)->v[0] = ReadETHReg(ERDPTL).Val;
		((WORD_VAL*)&sourceAddr)->v[1] = ReadETHReg(ERDPTH).Val;
	}

	// Handle special conditions where len == 0 or len == 1
	// The DMA module is not capable of handling those corner cases
	if(len <= 1u)
	{
		if(!UpdateReadPointer)
		{
			ReadSave.v[0] = ReadETHReg(ERDPTL).Val;
			ReadSave.v[1] = ReadETHReg(ERDPTH).Val;
		}
		if(!UpdateWritePointer)
		{
			WriteSave.v[0] = ReadETHReg(EWRPTL).Val;
			WriteSave.v[1] = ReadETHReg(EWRPTH).Val;
		}
		WriteReg(ERDPTL, ((WORD_VAL*)&sourceAddr)->v[0]);
		WriteReg(ERDPTH, ((WORD_VAL*)&sourceAddr)->v[1]);
		WriteReg(EWRPTL, ((WORD_VAL*)&destAddr)->v[0]);
		WriteReg(EWRPTH, ((WORD_VAL*)&destAddr)->v[1]);
		while(len--)
			MACPut(MACGet());
		if(!UpdateReadPointer)
		{
			WriteReg(ERDPTL, ReadSave.v[0]);
			WriteReg(ERDPTH, ReadSave.v[1]);
		}
		if(!UpdateWritePointer)
		{
			WriteReg(EWRPTL, WriteSave.v[0]);
			WriteReg(EWRPTH, WriteSave.v[1]);
		}
	}
	else
	{
		if(UpdateWritePointer)
		{
			WriteSave.Val = destAddr + len;
			WriteReg(EWRPTL, WriteSave.v[0]);
			WriteReg(EWRPTH, WriteSave.v[1]);
		}
		len += sourceAddr - 1;
		while(ReadETHReg(ECON1).ECON1bits.DMAST);
		WriteReg(EDMASTL, ((WORD_VAL*)&sourceAddr)->v[0]);
		WriteReg(EDMASTH, ((WORD_VAL*)&sourceAddr)->v[1]);
		WriteReg(EDMADSTL, ((WORD_VAL*)&destAddr)->v[0]);
		WriteReg(EDMADSTH, ((WORD_VAL*)&destAddr)->v[1]);
		if((sourceAddr <= RXSTOP) && (len > RXSTOP)) //&& (sourceAddr >= RXSTART))
			len -= RXSIZE;
		WriteReg(EDMANDL, ((WORD_VAL*)&len)->v[0]);
		WriteReg(EDMANDH, ((WORD_VAL*)&len)->v[1]);
		BFCReg(ECON1, ECON1_CSUMEN);
		BFSReg(ECON1, ECON1_DMAST);
		if(UpdateReadPointer)
		{
			len++;
			if((sourceAddr <= RXSTOP) && (len > RXSTOP)) //&& (sourceAddr >= RXSTART))
				len -= RXSIZE;
			WriteReg(ERDPTL, ((WORD_VAL*)&len)->v[0]);
			WriteReg(ERDPTH, ((WORD_VAL*)&len)->v[1]);
		}
	}
}

BOOL MACIsMemCopyDone(void)
{
	return !ReadETHReg(ECON1).ECON1bits.DMAST;
}


/******************************************************************************
 * Function:        BYTE MACGet()
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 * 					ERDPT must point to the place to read from.
 *
 * Input:           None
 *
 * Output:          Byte read from the ENC28J60's RAM
 *
 * Side Effects:    None
 *
 * Overview:        MACGet returns the byte pointed to by ERDPT and 
 *					increments ERDPT so MACGet() can be called again.  The 
 *					increment will follow the receive buffer wrapping boundary.
 *
 * Note:            None
 *****************************************************************************/
BYTE MACGet()
{
	BYTE Result;

	ENC_CS_IO = 0;

	#if defined(__C32__)
	{
		// Send the opcode and read a byte in one 16-bit operation
		ENC_SPICON1bits.MODE16 = 1;
		ENC_SSPBUF = RBM<<8 | 0x00;	// Send Read Buffer Memory command plus 8 dummy bits to generate clocks for the return result
		while(!ENC_SPI_IF);			// Wait until WORD is transmitted
		ENC_SPICON1bits.MODE16 = 0;
	}
	#elif defined(__C30__)
	{
		// Send the opcode and read a byte in one 16-bit operation
		ENC_SPISTATbits.SPIEN = 0;
		ENC_SPICON1bits.MODE16 = 1;
		ENC_SPISTATbits.SPIEN = 1;
		ENC_SSPBUF = RBM<<8 | 0x00;	// Send Read Buffer Memory command plus 8 dummy bits to generate clocks for the return result
		while(!ENC_SPI_IF);			// Wait until WORD is transmitted
		ENC_SPISTATbits.SPIEN = 0;
		ENC_SPICON1bits.MODE16 = 0;
		ENC_SPISTATbits.SPIEN = 1;
	}
	#else
	{
		// Send the opcode and read a byte in two 8-bit operations
		ENC_SPI_IF = 0;
		ENC_SSPBUF = RBM;
		while(!ENC_SPI_IF);		// Wait until opcode/address is transmitted.
		Result = ENC_SSPBUF;
		ENC_SPI_IF = 0;
		ENC_SSPBUF = 0;			// Send a dummy byte to receive the register 
								//   contents.
		while(!ENC_SPI_IF);		// Wait until register is received.
	}
	#endif

	ENC_SPI_IF = 0;
	Result = ENC_SSPBUF;
	ENC_CS_IO = 1;

	return Result;
}//end MACGet


/******************************************************************************
 * Function:        WORD MACGetArray(BYTE *val, WORD len)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 * 					ERDPT must point to the place to read from.
 *
 * Input:           *val: Pointer to storage location
 *					len:  Number of bytes to read from the data buffer.
 *
 * Output:          Byte(s) of data read from the data buffer.
 *
 * Side Effects:    None
 *
 * Overview:        Burst reads several sequential bytes from the data buffer 
 *					and places them into local memory.  With SPI burst support, 
 *					it performs much faster than multiple MACGet() calls.
 *					ERDPT is incremented after each byte, following the same 
 *					rules as MACGet().
 *
 * Note:            None
 *****************************************************************************/
WORD MACGetArray(BYTE *val, WORD len)
{
// Workaround needed on HPC Explorer (classic) board to prevent interference 
// with I2C temperature sensor on the same SPI wires
#if defined(__18F8722) || defined(_18F8722) ||	defined(__18F8723) || defined(_18F8723)
	WORD i;
	BYTE Dummy;

	i = len;
	Dummy = 0xFF;
	while(i--)
	{
		if(((BYTE_VAL*)&Dummy)->bits.b0)
		{
			// End bust operation
			ENC_CS_IO = 1;
			((BYTE_VAL*)&Dummy)->bits.b0 = 0;

			// Start the burst operation
			ENC_CS_IO = 0;
			ENC_SPI_IF = 0;
			ENC_SSPBUF = RBM;		// Send the Read Buffer Memory opcode.
			while(!ENC_SPI_IF);		// Wait until opcode/address is transmitted.
		}
		else
			Dummy = 0xFF;

		ENC_SPI_IF = 0;
		ENC_SSPBUF = 0;		// Send a dummy byte to receive a byte 
		if(val)
		{
			while(!ENC_SPI_IF);	// Wait until byte is received.
			*val++ = ENC_SSPBUF;
		}
		else
			while(!ENC_SPI_IF);	// Wait until byte is received.
	}

	ENC_CS_IO = 1;
	ENC_SPI_IF = 0;
	
	return len;
#else
	WORD i;
	BYTE Dummy;

	// Start the burst operation
	ENC_CS_IO = 0;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = RBM;		// Send the Read Buffer Memory opcode.
	i = 0;		
	if(val)
		val--;
	while(!ENC_SPI_IF);		// Wait until opcode/address is transmitted.
	Dummy = ENC_SSPBUF;
	ENC_SPI_IF = 0;

	#if defined(__C32__)
	{
		DWORD_VAL dwv;

		// Read the data, 4 bytes at a time, for as long as possible
		if(len >= 4)
		{
			ENC_SPICON1bits.MODE32 = 1;
			while(1)
			{			
				ENC_SSPBUF = 0x00000000;	// Send a dummy DWORD to generate 32 clocks
				i += 4;
				while(!ENC_SPI_IF);			// Wait until DWORD is transmitted
				ENC_SPI_IF = 0;
				dwv.Val = ENC_SSPBUF;
				if(val)
				{
					*(++val) = dwv.v[3];
					*(++val) = dwv.v[2];
					*(++val) = dwv.v[1];
					*(++val) = dwv.v[0];
				}
				if(len - i < 4)
					break;
			};
			ENC_SPICON1bits.MODE32 = 0;
		}
	}
	#elif defined(__C30__)
	{
		WORD_VAL wv;

		// Read the data, 2 bytes at a time, for as long as possible
		if(len >= 2)
		{
			ENC_SPISTATbits.SPIEN = 0;
			ENC_SPICON1bits.MODE16 = 1;
			ENC_SPISTATbits.SPIEN = 1;
			while(1)
			{			
				ENC_SSPBUF = 0x0000;	// Send a dummy WORD to generate 32 clocks
				i += 2;
				while(!ENC_SPI_IF);		// Wait until WORD is transmitted
				ENC_SPI_IF = 0;
				wv.Val = ENC_SSPBUF;
				if(val)
				{
					*(++val) = wv.v[1];
					*(++val) = wv.v[0];
				}
				if(len - i < 2)
					break;
			};
			ENC_SPISTATbits.SPIEN = 0;
			ENC_SPICON1bits.MODE16 = 0;
			ENC_SPISTATbits.SPIEN = 1;
		}
	}
	#endif

	// Read the data
	while(i<len)
	{
		ENC_SSPBUF = 0;		// Send a dummy byte to receive a byte 
		i++;
		if(val)
		{
			val++;
			while(!ENC_SPI_IF);	// Wait until byte is received.
			*val = ENC_SSPBUF;
		}
		else
		{
			while(!ENC_SPI_IF);	// Wait until byte is received.
			Dummy = ENC_SSPBUF;
		}
		ENC_SPI_IF = 0;
	};

	// Terminate the burst operation
	ENC_CS_IO = 1;

	return i;
#endif
}//end MACGetArray


/******************************************************************************
 * Function:        void MACPut(BYTE val)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 * 					EWRPT must point to the location to begin writing.
 *
 * Input:           Byte to write into the ENC28J60 buffer memory
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        MACPut outputs the Write Buffer Memory opcode/constant 
 *					(8 bits) and data to write (8 bits) over the SPI.  
 *					EWRPT is incremented after the write.
 *
 * Note:            None
 *****************************************************************************/
void MACPut(BYTE val)
{
	BYTE Dummy;

	ENC_CS_IO = 0;
	ENC_SPI_IF = 0;

	#if defined(__C32__)
	{
		// Send the Write Buffer Memory and data, in on 16-bit write
		ENC_SPICON1bits.MODE16 = 1;
		ENC_SSPBUF = (WBM<<8) | (WORD)val;	// Start sending the WORD
		while(!ENC_SPI_IF);					// Wait until WORD is transmitted
		ENC_SPICON1bits.MODE16 = 0;
	}
	#elif defined(__C30__)
	{
		// Send the Write Buffer Memory and data, in on 16-bit write
		ENC_SPISTATbits.SPIEN = 0;
		ENC_SPICON1bits.MODE16 = 1;
		ENC_SPISTATbits.SPIEN = 1;
		ENC_SSPBUF = (WBM<<8) | (WORD)val;	// Start sending the WORD
		while(!ENC_SPI_IF);					// Wait until WORD is transmitted
		ENC_SPISTATbits.SPIEN = 0;
		ENC_SPICON1bits.MODE16 = 0;
		ENC_SPISTATbits.SPIEN = 1;
	}
	#else
	{
		ENC_SSPBUF = WBM;		// Send the opcode and constant.
		while(!ENC_SPI_IF);		// Wait until opcode/constant is transmitted.
		Dummy = ENC_SSPBUF;
		ENC_SPI_IF = 0;
		ENC_SSPBUF = val;		// Send the byte to be writen.
		while(!ENC_SPI_IF);		// Wait until finished transmitting
	}
	#endif
	
	Dummy = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_CS_IO = 1;
}//end MACPut


/******************************************************************************
 * Function:        void MACPutArray(BYTE *val, WORD len)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 * 					EWRPT must point to the location to begin writing.
 *
 * Input:           *val: Pointer to source of bytes to copy.
 *					len:  Number of bytes to write to the data buffer.
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        MACPutArray writes several sequential bytes to the 
 *					ENC28J60 RAM.  It performs faster than multiple MACPut()
 *					calls.  EWRPT is incremented by len.
 *
 * Note:            None
 *****************************************************************************/
void MACPutArray(BYTE *val, WORD len)
{
// Workaround needed on HPC Explorer (classic) board to prevent interference 
// with I2C temperature sensor on the same SPI wires
#if defined(__18F8722) || defined(_18F8722) ||	defined(__18F8723) || defined(_18F8723)
	WORD i;
	BYTE Dummy;

	i = len;
	Dummy = 0xFF;
	while(i--)
	{
		if(((BYTE_VAL*)&Dummy)->bits.b0)
		{
			// End bust operation
			ENC_CS_IO = 1;
			((BYTE_VAL*)&Dummy)->bits.b0 = 0;

			// Start the burst operation
			ENC_CS_IO = 0;
			ENC_SPI_IF = 0;
			ENC_SSPBUF = WBM;		// Send the Read Buffer Memory opcode.
			while(!ENC_SPI_IF);		// Wait until opcode/address is transmitted.
		}
		else
			Dummy = 0xFF;

		ENC_SPI_IF = 0;
		ENC_SSPBUF = *val++;	// Send byte
		while(!ENC_SPI_IF);		// Wait until byte is sent
	}

	ENC_SPI_IF = 0;
	ENC_CS_IO = 1;
	
	return;
#else
	BYTE Dummy;

	// Select the chip and send the proper opcode
	ENC_CS_IO = 0;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = WBM;		// Send the Write Buffer Memory opcode
	while(!ENC_SPI_IF);		// Wait until opcode/constant is transmitted.
	Dummy = ENC_SSPBUF;
	ENC_SPI_IF = 0;

	#if defined(__C32__)
	{
		DWORD_VAL dwv;

		// Send the data, 4 bytes at a time, for as long as possible
		if(len >= 4)
		{
			dwv.v[3] = *val++;
			dwv.v[2] = *val++;
			dwv.v[1] = *val++;
			dwv.v[0] = *val++;
			ENC_SPICON1bits.MODE32 = 1;
			while(1)
			{			
				ENC_SSPBUF = dwv.Val;		// Start sending the DWORD
				len -= 4;
				if(len < 4)
					break;
				dwv.v[3] = *val++;
				dwv.v[2] = *val++;
				dwv.v[1] = *val++;
				dwv.v[0] = *val++;
				while(!ENC_SPI_IF);			// Wait until DWORD is transmitted
				Dummy = ENC_SSPBUF;
				ENC_SPI_IF = 0;
			};
			while(!ENC_SPI_IF);				// Wait until DWORD is transmitted
			Dummy = ENC_SSPBUF;
			ENC_SPI_IF = 0;
			ENC_SPICON1bits.MODE32 = 0;
		}
	}
	#elif defined(__C30__)
	{
		WORD_VAL wv;
		
		// Send the data, 2 bytes at a time, for as long as possible
		if(len >= 2)
		{
			wv.v[1] = *val++;
			wv.v[0] = *val++;
			ENC_SPISTATbits.SPIEN = 0;
			ENC_SPICON1bits.MODE16 = 1;
			ENC_SPISTATbits.SPIEN = 1;
			while(1)
			{			
				ENC_SSPBUF = wv.Val;		// Start sending the WORD
				len -= 2;
				if(len < 2)
					break;
				wv.v[1] = *val++;
				wv.v[0] = *val++;
				while(!ENC_SPI_IF);			// Wait until WORD is transmitted
				Dummy = ENC_SSPBUF;
				ENC_SPI_IF = 0;
			};
			while(!ENC_SPI_IF);				// Wait until WORD is transmitted
			Dummy = ENC_SSPBUF;
			ENC_SPI_IF = 0;
			ENC_SPISTATbits.SPIEN = 0;
			ENC_SPICON1bits.MODE16 = 0;
			ENC_SPISTATbits.SPIEN = 1;
		}
	}
	#endif
	
	// Send the data, one byte at a time
	while(len)
	{
		ENC_SSPBUF = *val;	// Start sending the byte
		val++;				// Increment after writing to ENC_SSPBUF to increase speed
		len--;				// Decrement after writing to ENC_SSPBUF to increase speed
		while(!ENC_SPI_IF);	// Wait until byte is transmitted
		Dummy = ENC_SSPBUF;
		ENC_SPI_IF = 0;
	};

	// Terminate the burst operation
	ENC_CS_IO = 1;
#endif
}//end MACPutArray


#if defined(__18CXX)
/******************************************************************************
 * Function:        void MACPutROMArray(ROM BYTE *val, WORD len)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 * 					EWRPT must point to the location to begin writing.
 *
 * Input:           *val: Pointer to source of bytes to copy.
 *					len:  Number of bytes to write to the data buffer.
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        MACPutArray writes several sequential bytes to the 
 *					ENC28J60 RAM.  It performs faster than multiple MACPut()
 *					calls.  EWRPT is incremented by len.
 *
 * Note:            None
 *****************************************************************************/
void MACPutROMArray(ROM BYTE *val, WORD len)
{
// Workaround needed on HPC Explorer (classic) board to prevent interference 
// with I2C temperature sensor on the same SPI wires
#if defined(__18F8722) || defined(_18F8722) ||	defined(__18F8723) || defined(_18F8723)
	WORD i;
	BYTE Dummy;

	i = len;
	Dummy = 0xFF;
	while(i--)
	{
		if(((BYTE_VAL*)&Dummy)->bits.b0)
		{
			// End bust operation
			ENC_CS_IO = 1;
			((BYTE_VAL*)&Dummy)->bits.b0 = 0;

			// Start the burst operation
			ENC_CS_IO = 0;
			ENC_SPI_IF = 0;
			ENC_SSPBUF = WBM;		// Send the Read Buffer Memory opcode.
			while(!ENC_SPI_IF);		// Wait until opcode/address is transmitted.
		}
		else
			Dummy = 0xFF;

		ENC_SPI_IF = 0;
		ENC_SSPBUF = *val++;	// Send byte
		while(!ENC_SPI_IF);		// Wait until byte is sent
	}

	ENC_SPI_IF = 0;
	ENC_CS_IO = 1;
	
	return;
#else
	BYTE Dummy;

	// Select the chip and send the proper opcode
	ENC_CS_IO = 0;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = WBM;		// Send the Write Buffer Memory opcode
	while(!ENC_SPI_IF);		// Wait until opcode/constant is transmitted.
	Dummy = ENC_SSPBUF;
	ENC_SPI_IF = 0;

	// Send the data
	while(len)
	{
		ENC_SSPBUF = *val;	// Start sending the byte
		val++;				// Increment after writing to ENC_SSPBUF to increase speed
		len--;				// Decrement after writing to ENC_SSPBUF to increase speed
		while(!ENC_SPI_IF);	// Wait until byte is transmitted
		Dummy = ENC_SSPBUF;
		ENC_SPI_IF = 0;
	};

	// Terminate the burst operation
	ENC_CS_IO = 1;
#endif
}//end MACPutROMArray
#endif

/******************************************************************************
 * Function:        static void SendSystemReset(void)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        SendSystemReset sends the System Reset SPI command to 
 *					the Ethernet controller.  It resets all register contents 
 *					(except for ECOCON) and returns the device to the power 
 *					on default state.
 *
 * Note:            None
 *****************************************************************************/
static void SendSystemReset(void)
{
	BYTE Dummy;

	// Note: The power save feature may prevent the reset from executing, so 
	// we must make sure that the device is not in power save before issuing 
	// a reset.
	BFCReg(ECON2, ECON2_PWRSV);

	// Give some opportunity for the regulator to reach normal regulation and 
	// have all clocks running
	DelayMs(1);

	// Execute the System Reset command
	ENC_CS_IO = 0;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = SR;
	while(!ENC_SPI_IF);		// Wait until the command is transmitted.
	Dummy = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_CS_IO = 1;

	// Wait for the oscillator start up timer and PHY to become ready
	DelayMs(1);
}//end SendSystemReset


/******************************************************************************
 * Function:        REG ReadETHReg(BYTE Address)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 * 					Bank select bits must be set corresponding to the register 
 * 					to read from.
 *
 * Input:           5 bit address of the ETH control register to read from.
 *					  The top 3 bits must be 0.
 *
 * Output:          Byte read from the Ethernet controller's ETH register.
 *
 * Side Effects:    None
 *
 * Overview:        ReadETHReg sends the 8 bit RCR opcode/Address byte over 
 *					the SPI and then retrives the register contents in the 
 *					next 8 SPI clocks.
 *
 * Note:            This routine cannot be used to access MAC/MII or PHY 
 *					registers.  Use ReadMACReg() or ReadPHYReg() for that 
 *					purpose.  
 *****************************************************************************/
static REG ReadETHReg(BYTE Address)
{
	REG r;

	// Select the chip and send the Read Control Register opcode/address
	ENC_CS_IO = 0;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = RCR | Address;	
		
	while(!ENC_SPI_IF);		// Wait until the opcode/address is transmitted
	r.Val = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = 0;			// Send a dummy byte to receive the register 
							//   contents
	while(!ENC_SPI_IF);		// Wait until the register is received
	r.Val = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_CS_IO = 1;

	return r;
}//end ReadETHReg


/******************************************************************************
 * Function:        REG ReadMACReg(BYTE Address)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 * 					Bank select bits must be set corresponding to the register 
 * 					to read from.
 *
 * Input:           5 bit address of the MAC or MII register to read from.
 *					  The top 3 bits must be 0.
 *
 * Output:          Byte read from the Ethernet controller's MAC/MII register.
 *
 * Side Effects:    None
 *
 * Overview:        ReadMACReg sends the 8 bit RCR opcode/Address byte as well 
 *					as a dummy byte over the SPI and then retrives the 
 *					register contents in the last 8 SPI clocks.
 *
 * Note:            This routine cannot be used to access ETH or PHY 
 *					registers.  Use ReadETHReg() or ReadPHYReg() for that 
 *					purpose.  
 *****************************************************************************/
static REG ReadMACReg(BYTE Address)
{
	REG r;

	ENC_CS_IO = 0;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = RCR | Address;	// Send the Read Control Register opcode and 
								//   address.
	while(!ENC_SPI_IF);			// Wait until opcode/address is transmitted.
	r.Val = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = 0;				// Send a dummy byte 
	while(!ENC_SPI_IF);			// Wait for the dummy byte to be transmitted
	r.Val = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = 0;				// Send another dummy byte to receive the register 
								//   contents.
	while(!ENC_SPI_IF);			// Wait until register is received.
	r.Val = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_CS_IO = 1;
	
	return r;
}//end ReadMACReg


/******************************************************************************
 * Function:        ReadPHYReg
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 *
 * Input:           Address of the PHY register to read from.
 *
 * Output:          16 bits of data read from the PHY register.
 *
 * Side Effects:    None
 *
 * Overview:        ReadPHYReg performs an MII read operation.  While in 
 *					progress, it simply polls the MII BUSY bit wasting time 
 *					(10.24us).
 *
 * Note:            None
 *****************************************************************************/
PHYREG ReadPHYReg(BYTE Register)
{
	PHYREG Result;

	// Set the right address and start the register read operation
	BankSel(MIREGADR);
	WriteReg((BYTE)MIREGADR, Register);
	WriteReg((BYTE)MICMD, MICMD_MIIRD);	

	// Loop to wait until the PHY register has been read through the MII
	// This requires 10.24us
	BankSel(MISTAT);
	while(ReadMACReg((BYTE)MISTAT).MISTATbits.BUSY);

	// Stop reading
	BankSel(MIREGADR);
	WriteReg((BYTE)MICMD, 0x00);	
	
	// Obtain results and return
	Result.VAL.v[0] = ReadMACReg((BYTE)MIRDL).Val;
	Result.VAL.v[1] = ReadMACReg((BYTE)MIRDH).Val;

	BankSel(ERDPTL);	// Return to Bank 0
	return Result;
}//end ReadPHYReg


/******************************************************************************
 * Function:        void WriteReg(BYTE Address, BYTE Data)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 * 					Bank select bits must be set corresponding to the register 
 *					to modify.
 *
 * Input:           5 bit address of the ETH, MAC, or MII register to modify.  
 *					  The top 3 bits must be 0.  
 *					Byte to be written into the register.
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        WriteReg sends the 8 bit WCR opcode/Address byte over the 
 *					SPI and then sends the data to write in the next 8 SPI 
 *					clocks.
 *
 * Note:            This routine is almost identical to the BFCReg() and 
 *					BFSReg() functions.  It is seperate to maximize speed.  
 *					Unlike the ReadETHReg/ReadMACReg functions, WriteReg() 
 *					can write to any ETH or MAC register.  Writing to PHY 
 *					registers must be accomplished with WritePHYReg().
 *****************************************************************************/
static void WriteReg(BYTE Address, BYTE Data)
{		
	BYTE Dummy;

	ENC_CS_IO = 0;
	ENC_SPI_IF = 0;

	#if defined(__C32__)
	{
		// Send the Write Buffer Memory and data, in on 16-bit write
		ENC_SPICON1bits.MODE16 = 1;
		ENC_SSPBUF = ((WCR | Address)<<8) | (WORD)Data;	// Start sending the WORD
		while(!ENC_SPI_IF);					// Wait until WORD is transmitted
		ENC_SPICON1bits.MODE16 = 0;
	}
	#else
	{
		ENC_SSPBUF = WCR | Address;	// Send the opcode and address.
		while(!ENC_SPI_IF);			// Wait until opcode/constant is transmitted.
		Dummy = ENC_SSPBUF;
		ENC_SPI_IF = 0;
		ENC_SSPBUF = Data;			// Send the byte to be writen.
		while(!ENC_SPI_IF);			// Wait until finished transmitting
	}
	#endif
	
	Dummy = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_CS_IO = 1;
}//end WriteReg


/******************************************************************************
 * Function:        void BFCReg(BYTE Address, BYTE Data)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 * 					Bank select bits must be set corresponding to the register 
 *					  to modify.
 *
 * Input:           5 bit address of the register to modify.  The top 3 bits 
 *					  must be 0.  
 *					Byte to be used with the Bit Field Clear operation.
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        BFCReg sends the 8 bit BFC opcode/Address byte over the 
 *					SPI and then sends the data in the next 8 SPI clocks.
 *
 * Note:            This routine is almost identical to the WriteReg() and 
 *					BFSReg() functions.  It is separate to maximize speed.  
 *					BFCReg() must only be used on ETH registers.
 *****************************************************************************/
static void BFCReg(BYTE Address, BYTE Data)
{
	BYTE Dummy;

	ENC_CS_IO = 0;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = BFC | Address;	// Send the opcode and address.
	while(!ENC_SPI_IF);			// Wait until opcode/address is transmitted.
	Dummy = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = Data;			// Send the byte to be writen.
	while(!ENC_SPI_IF);			// Wait until register is written.
	Dummy = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_CS_IO = 1;
}//end BFCReg


/******************************************************************************
 * Function:        void BFSReg(BYTE Address, BYTE Data)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 * 					Bank select bits must be set corresponding to the register 
 *					to modify.
 *
 * Input:           5 bit address of the register to modify.  The top 3 bits 
 *					  must be 0.  
 *					Byte to be used with the Bit Field Set operation.
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        BFSReg sends the 8 bit BFC opcode/Address byte over the 
 *					SPI and then sends the data in the next 8 SPI clocks.
 *
 * Note:            This routine is almost identical to the WriteReg() and 
 *					BFCReg() functions.  It is separate to maximize speed.
 *					BFSReg() must only be used on ETH registers.
 *****************************************************************************/
static void BFSReg(BYTE Address, BYTE Data)
{
	BYTE Dummy;

	ENC_CS_IO = 0;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = BFS | Address;	// Send the opcode and address.
	while(!ENC_SPI_IF);			// Wait until opcode/address is transmitted.
	Dummy = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_SSPBUF = Data;			// Send the byte to be writen.
	while(!ENC_SPI_IF);			// Wait until register is written.
	Dummy = ENC_SSPBUF;
	ENC_SPI_IF = 0;
	ENC_CS_IO = 1;
}//end BFSReg


/******************************************************************************
 * Function:        WritePHYReg
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 *
 * Input:           Address of the PHY register to write to.
 *					16 bits of data to write to PHY register.
 *
 * Output:          None
 *
 * Side Effects:    Alters bank bits to point to Bank 3
 *
 * Overview:        WritePHYReg performs an MII write operation.  While in 
 *					progress, it simply polls the MII BUSY bit wasting time.
 *
 * Note:            None
 *****************************************************************************/
void WritePHYReg(BYTE Register, WORD Data)
{
	// Write the register address
	BankSel(MIREGADR);
	WriteReg((BYTE)MIREGADR, Register);
	
	// Write the data
	// Order is important: write low byte first, high byte last
	WriteReg((BYTE)MIWRL, ((WORD_VAL*)&Data)->v[0]);	
	WriteReg((BYTE)MIWRH, ((WORD_VAL*)&Data)->v[1]);

	// Wait until the PHY register has been written
	BankSel(MISTAT);
	while(ReadMACReg((BYTE)MISTAT).MISTATbits.BUSY);

	BankSel(ERDPTL);	// Return to Bank 0
}//end WritePHYReg


/******************************************************************************
 * Function:        BankSel
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 *
 * Input:           Register address with the high byte containing the 2 bank 
 *					  select 2 bits.
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        BankSel takes the high byte of a register address and 
 *					changes the bank select bits in ETHCON1 to match.
 *
 * Note:            None
 *****************************************************************************/
static void BankSel(WORD Register) 
{
	BFCReg(ECON1, ECON1_BSEL1 | ECON1_BSEL0);
	BFSReg(ECON1, ((WORD_VAL*)&Register)->v[1]);
}//end BankSel


/******************************************************************************
 * Function:        void MACPowerDown(void)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        MACPowerDown puts the ENC28J60 in low power sleep mode. In
 *					sleep mode, no packets can be transmitted or received.  
 *					All MAC and PHY registers should not be accessed.
 *
 * Note:            If a packet is being transmitted while this function is 
 * 					called, this function will block until it is it complete.
 *					If anything is being received, it will be completed.
 *****************************************************************************/
void MACPowerDown(void)
{
	// Disable packet reception
	BFCReg(ECON1, ECON1_RXEN);

	// Make sure any last packet which was in-progress when RXEN was cleared 
	// is completed
	while(ReadETHReg(ESTAT).ESTATbits.RXBUSY);

	// If a packet is being transmitted, wait for it to finish
	while(ReadETHReg(ECON1).ECON1bits.TXRTS);
	
	// Enter sleep mode
	BFSReg(ECON2, ECON2_PWRSV);
}//end MACPowerDown


/******************************************************************************
 * Function:        void MACPowerUp(void)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        MACPowerUp returns the ENC28J60 back to normal operation
 *					after a previous call to MACPowerDown().  Calling this 
 *					function when already powered up will have no effect.
 *
 * Note:            If a link partner is present, it will take 10s of 
 *					milliseconds before a new link will be established after
 *					waking up.  While not linked, packets which are 
 *					transmitted will most likely be lost.  MACIsLinked() can 
 *					be called to determine if a link is established.
 *****************************************************************************/
void MACPowerUp(void)
{	
	// Leave power down mode
	BFCReg(ECON2, ECON2_PWRSV);

	// Wait for the 300us Oscillator Startup Timer (OST) to time out.  This 
	// delay is required for the PHY module to return to an operational state.
	while(!ReadETHReg(ESTAT).ESTATbits.CLKRDY);
	
	// Enable packet reception
	BFSReg(ECON1, ECON1_RXEN);
}//end MACPowerUp


/******************************************************************************
 * Function:        void SetCLKOUT(BYTE NewConfig)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 *
 * Input:           NewConfig - 0x00: CLKOUT disabled (pin driven low)
 *								0x01: Divide by 1 (25 MHz)
 *								0x02: Divide by 2 (12.5 MHz)
 *								0x03: Divide by 3 (8.333333 MHz)
 *								0x04: Divide by 4 (6.25 MHz, POR default)
 *								0x05: Divide by 8 (3.125 MHz)
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Writes the value of NewConfig into the ECOCON register.  
 *					The CLKOUT pin will beginning outputting the new frequency
 *					immediately.
 *
 * Note:            
 *****************************************************************************/
void SetCLKOUT(BYTE NewConfig)
{	
	BankSel(ECOCON);
	WriteReg((BYTE)ECOCON, NewConfig);
	BankSel(ERDPTL);
}//end SetCLKOUT


/******************************************************************************
 * Function:        BYTE GetCLKOUT(void)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 *
 * Input:           None
 *
 * Output:          BYTE - 0x00: CLKOUT disabled (pin driven low)
 *						   0x01: Divide by 1 (25 MHz)
 *						   0x02: Divide by 2 (12.5 MHz)
 *						   0x03: Divide by 3 (8.333333 MHz)
 *						   0x04: Divide by 4 (6.25 MHz, POR default)
 *						   0x05: Divide by 8 (3.125 MHz)
 *						   0x06: Reserved
 *						   0x07: Reserved
 *
 * Side Effects:    None
 *
 * Overview:        Returns the current value of the ECOCON register.
 *
 * Note:            None
 *****************************************************************************/
BYTE GetCLKOUT(void)
{	
	BYTE i;

	BankSel(ECOCON);
	i = ReadETHReg((BYTE)ECOCON).Val;
	BankSel(ERDPTL);
	return i;
}//end GetCLKOUT


/******************************************************************************
 * Function:        void SetRXHashTableEntry(MAC_ADDR DestMACAddr)
 *
 * PreCondition:    SPI bus must be initialized (done in MACInit()).
 *
 * Input:           DestMACAddr: 6 byte group destination MAC address to allow 
 *								 through the Hash Table Filter
 *
 * Output:          Sets the appropriate bit in the EHT* registers to allow 
 *					packets sent to DestMACAddr to be received if the Hash 
 *					Table receive filter is enabled
 *
 * Side Effects:    None
 *
 * Overview:        Calculates a CRC-32 using polynomial 0x4C11DB7 and then, 
 *					using bits 28:23 of the CRC, sets the appropriate bit in 
 *					the EHT* registers
 *
 * Note:            This code is commented out to save code space on systems 
 *					that do not need this function.  Change the "#if 0" line 
 *					to "#if 1" to uncomment it.
 *****************************************************************************/
#if 0
void SetRXHashTableEntry(MAC_ADDR DestMACAddr)
{
	DWORD_VAL CRC = {0xFFFFFFFF};
	BYTE HTRegister;
	BYTE i, j;

	// Calculate a CRC-32 over the 6 byte MAC address 
	// using polynomial 0x4C11DB7
	for(i = 0; i < sizeof(MAC_ADDR); i++)
	{
		BYTE  crcnext;
	
		// shift in 8 bits
		for(j = 0; j < 8; j++)
		{
			crcnext = 0;
			if(((BYTE_VAL*)&(CRC.v[3]))->bits.b7)
				crcnext = 1;
			crcnext ^= (((BYTE_VAL*)&DestMACAddr.v[i])->bits.b0);
	
			CRC.Val <<= 1;
			if(crcnext)
				CRC.Val ^= 0x4C11DB7;
			// next bit
			DestMACAddr.v[i] >>= 1;
		}
	}
	
	// CRC-32 calculated, now extract bits 28:23
	// Bits 25:23 define where within the Hash Table byte the bit needs to be set
	// Bits 28:26 define which of the 8 Hash Table bytes that bits 25:23 apply to
	i = CRC.v[3] & 0x1F;
	HTRegister = (i >> 2) + (BYTE)EHT0;
	i = (i << 1) & 0x06;
	((BYTE_VAL*)&i)->bits.b0 = ((BYTE_VAL*)&CRC.v[2])->bits.b7;
	
	// Set the proper bit in the Hash Table
	BankSel(EHT0);
	BFSReg(HTRegister, 1<<i);

	BankSel(ERDPTL);			// Return to Bank 0
}
#endif

//// GetRegs is a function for debugging purposes only.  It will read all 
//// registers and store them in the PIC's RAM so they can be viewed with 
//// the ICD2.
//REG Regs[4][32];
//void GetRegs(void)
//{
//	BYTE i;
//	
//	BankSel(0x000);
//	for(i=0; i<0x1A; i++)
//		Regs[0][i] = ReadETHReg(i);
//	for(i=0x1B; i<32; i++)
//		Regs[0][i] = ReadETHReg(i);
//
//	BankSel(0x100);
//	for(i=0; i<0x1A; i++)
//		Regs[1][i] = ReadETHReg(i);
//	for(i=0x1B; i<32; i++)
//		Regs[1][i] = ReadETHReg(i);
//
//	BankSel(0x200);
//	for(i=0; i<5; i++)
//		Regs[2][i] = ReadMACReg(i);
//	Regs[2][5] = ReadETHReg(i);
//	for(i=6; i<0x0F; i++)
//		Regs[2][i] = ReadMACReg(i);
//	Regs[2][0x0F] = ReadETHReg(i);
//	for(i=0x10; i<0x13; i++)
//		Regs[2][i] = ReadMACReg(i);
//	Regs[2][0x13] = ReadETHReg(i);
//	for(i=0x14; i<0x1A; i++)
//		Regs[2][i] = ReadMACReg(i);
//	for(i=0x1B; i<32; i++)
//		Regs[2][i] = ReadETHReg(i);
//
//	BankSel(0x300);
//	for(i=0; i<0x06; i++)
//		Regs[3][i] = ReadMACReg(i);
//	for(i=6; i<0x0A; i++)
//		Regs[3][i] = ReadETHReg(i);
//	Regs[3][0x0A] = ReadMACReg(i);
//	for(i=0x0B; i<0x1A; i++)
//		Regs[3][i] = ReadETHReg(i);
//	for(i=0x1B; i<32; i++)
//		Regs[3][i] = ReadETHReg(i);
//
//	Regs[0][0x1A].Val = 0;
//	Regs[1][0x1A].Val = 0;
//	Regs[2][0x1A].Val = 0;
//	Regs[3][0x1A].Val = 0;
//
//	BankSel(ERDPTL);
//
//	return;
//}

//// Get8KBMem is a function intended for debugging purposes.  It will read all 
//// Ethernet RAM and output it in hex out the UART
//void Get8KBMem(void)
//{
//	WORD_VAL i;
//	BYTE v;
//	WORD_VAL RDSave;
//
//	RDSave.v[0] = ReadETHReg(ERDPTL).Val;
//	RDSave.v[1] = ReadETHReg(ERDPTH).Val;
//
//	for(i.Val = 0; i.Val < 8192; i.Val++)
//	{	
//		WriteReg(ERDPTL, i.v[0]);
//		WriteReg(ERDPTH, i.v[1]);
//		v = MACGet();
//
//		putcUART('0');
//		while(BusyUART());
//		putcUART('x');
//		while(BusyUART());
//		putcUART(btohexa_high(v));
//		while(BusyUART());
//		putcUART(btohexa_low(v));
//		while(BusyUART());
//	}
//
//	WriteReg(ERDPTL, RDSave.v[0]);
//	WriteReg(ERDPTH, RDSave.v[1]);
//
//}

#endif //#if defined(ENC_CS_TRIS)
