/*
 * ianENC28J60.c
 *
 *  Created on: Jan 2, 2021
 *      Author: ianst
 *      Updated March 29 2023 - moved  	NextPacketLocation.Val = EPkt.Pre.NextPacketPointer; // Save the location where the hardware will write the next packet to
 *       So it always gets updated
 */


#include "ianENC28J60.h"
#include "udpftype.h"
#include "main.h"
#include "armbits.h"
#include "scheduler.h"
#include "registry.h"
#include "TCPIPConfig.h"
#include "setup.h"
#include <string.h>

//Ian - Always use FULL_DUPLEX or you will run into the late collision silicon bug - the workaround is too unpredictable
//29-05-2020 Added PP_memcpy() to prevent register tearing in peek/poke (also speeds up aligned copies)
#define FALSE (0)
#define TRUE (1)

// 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	ENSR  ((0x7<<5) | 0x1F)	// System Reset command does not use an address.
								//   It requires 0x1F, however.
#define ETHER_IP	(0x00u)
#define ETHER_ARP	(0x06u)

// Pseudo Functions
#define LOW(a) 					((a) & 0xFF)
#define HIGH(a) 				(((a)>>8) & 0xFF)
#define ReadMACReg(Address)    WriteGenReg((uint8_t)Address, 0, RCR)
#define WriteReg(Address, Data)  WriteGenReg(Address, Data, WCR)
#define BFCReg(Address, Data)  WriteGenReg(Address, Data, BFC)
#define BFSReg(Address, Data)  WriteGenReg(Address, Data, BFS)
#define MACPut(Val) WriteGenReg(0, Val, WBM)
#define MACGet()  ((uint8_t)WriteGenReg(0, 0, RBM).Val)

// Prototypes of functions intended for MAC layer use only.
void BankSel(uint16_t Register);
REG WriteGenReg(uint8_t Address, uint8_t Data, uint8_t Command);
void WriteRegW(uint8_t Address,uint16_t Data);

// Internal MAC level variables and flags.
static uint16_t_VAL NextPacketLocation;
static uint16_t_VAL CurrentPacketLocation;
ENC_PACKET  EPkt ;

volatile  uint16_t IPSTATE=0;
uint16_t _Identifier = 0;
volatile uint32_t ETH_Dropped_Packets=0;
volatile uint32_t ETH_ENC_Resets=0;
volatile uint32_t ETH_ENC_Tx_Resets=0;
volatile uint32_t ETH_ENC_Rx_Resets=0;
volatile uint32_t ETH_UDP_Xmits=0;
volatile uint32_t ETH_ARP_Responses=0;
volatile uint32_t ETH_ICMP_Responses=0;


volatile uint16_t end_Ptr;

//int size=sizeof(ENC_PREAMBLE)+sizeof(ETHER_HEADER)+ sizeof(IP_HEADER)+sizeof(UDP_HEADER);



void DMA_Init(DMA_HandleTypeDef *hdma)
{
 uint32_t tmp = 0U;

 /* Disable the peripheral */
 DMA_DISABLE(hdma);


 /* Get the CR register value */
 tmp = hdma->Instance->CR;

 /* Clear CHSEL, MBURST, PBURST, PL, MSIZE, PSIZE, MINC, PINC, CIRC, DIR, CT and DBM bits */
  tmp &= ((uint32_t)~(DMA_SxCR_CHSEL | DMA_SxCR_MBURST | DMA_SxCR_PBURST | \
                      DMA_SxCR_PL    | DMA_SxCR_MSIZE  | DMA_SxCR_PSIZE  | \
                      DMA_SxCR_MINC  | DMA_SxCR_PINC   | DMA_SxCR_CIRC   | \
                      DMA_SxCR_DIR   | DMA_SxCR_CT     | DMA_SxCR_DBM));

  /* Prepare the DMA Stream configuration */
  tmp |=  hdma->Init.Channel             | hdma->Init.Direction        |
          hdma->Init.PeriphInc           | hdma->Init.MemInc           |
          hdma->Init.PeriphDataAlignment | hdma->Init.MemDataAlignment |
          hdma->Init.Mode                | hdma->Init.Priority;
 /* Write to DMA Stream CR register */
 hdma->Instance->CR = tmp;
 /* Clear all interrupt flags */
 DMA1->LIFCR = (DMA_LIFCR_CTCIF3 |DMA_LIFCR_CHTIF3 |DMA_LIFCR_CTEIF3 | DMA_LIFCR_CDMEIF3 | DMA_LIFCR_CFEIF3);
 DMA1->HIFCR = (DMA_HIFCR_CTCIF4 |DMA_HIFCR_CHTIF4 |DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4 | DMA_HIFCR_CFEIF4);

}

void DMA_SPI_ENC_Send_Get_NW(uint8_t * T_Buff, uint8_t * R_Buff, uint32_t Len )
{
	    DMA_DISABLE(&hdma_spi_enc_rx);
	    DMA_DISABLE(&hdma_spi_enc_tx);
		hdma_spi_enc_tx.Instance->M0AR=(uint32_t)T_Buff;
		hdma_spi_enc_tx.Instance->PAR=(uint32_t)&SPI_ENC_P->DR;
		hdma_spi_enc_tx.Instance->NDTR =Len ;
	    hdma_spi_enc_rx.Instance->M0AR=(uint32_t)R_Buff;
		hdma_spi_enc_rx.Instance->PAR=(uint32_t)&SPI_ENC_P->DR;
	    hdma_spi_enc_rx.Instance->NDTR =Len ;
	        /* Enable Tx DMA Request */
	    DMA_Init(&hdma_spi_enc_rx);
	    DMA_Init(&hdma_spi_enc_tx);
	    DMA_ENABLE(&hdma_spi_enc_rx);
	    DMA_ENABLE(&hdma_spi_enc_tx);
	    SPI_ENC_P->CR2|=(SPI_CR2_RXDMAEN|SPI_CR2_TXDMAEN);
}

void DMA_SPI_ENC_Send_Get(uint8_t * T_Buff, uint8_t * R_Buff, uint32_t Len )
{
	DMA_SPI_ENC_Send_Get_NW( T_Buff,  R_Buff, Len );
	//	    Wait for DMA to finish
	while ((ENC_ETH_DMA_RX_COMPLETE)==0){};
	DMA_DISABLE(&hdma_spi_enc_rx);
	DMA_DISABLE(&hdma_spi_enc_tx);
	SPI_ENC_P->CR2&=~(SPI_CR2_RXDMAEN|SPI_CR2_TXDMAEN);
}

void WriteRegW(uint8_t Address,uint16_t Data)
{
	WriteReg(Address, ((uint16_t_VAL)Data).v[0]);
	WriteReg(Address+1, ((uint16_t_VAL)Data).v[1]);
}


REG WriteGenReg(uint8_t Address, uint8_t Data, uint8_t Command)
{
	REG Dummy;
	uint8_t buft;
	ENC_CS_LOW;
  //Datasheet page 82
	buft = Command | Address;	// Send the opcode and address.
	DMA_SPI_ENC_Send_Get(&buft,&Dummy.Val,1);
	if (Command!=ENSR) DMA_SPI_ENC_Send_Get(&Data,&Dummy.Val,1);
	if (Command==RCR)  DMA_SPI_ENC_Send_Get(&Data,&Dummy.Val,1);
 //Datasheet page 82
	ENC_CS_HIGH;
	return Dummy;
}//end WriteGenReg


void MACInit(void)
{
	ENC_CS_HIGH;
    memcpy(&IPConfig,&IPConfigF,sizeof(IPConfig));
    memcpy(&IPC_Local,&IPConfig,sizeof(IP_CONFIG));


	// 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
	Wait_N_uS(1000);
	// Execute the System Reset command
    WriteGenReg(0, 0, ENSR);
	 //WriteGenReg(0, 0, ENSR);

	// Wait for the oscillator start up timer and PHY to become ready

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

    BankSel(ERXSTL);

	WriteRegW(ERXSTL,RXSTART); //Datasheet page 17
	WriteRegW(ERXRDPTL,RXSTOP);

	WriteRegW(ERXNDL,RXSTOP);
	WriteRegW(ETXSTL,TXSTART);
	WriteRegW(EWRPTL,TXSTART);

	// Wait for the oscillator start up timer and PHY to become ready
	Wait_N_uS(300);  //Datasheet page 5



	while ( ~(ReadMACReg(ESTAT).Val) & ESTAT_CLKRDY) {};

	MACPut(0x00); // Write a permanent per packet control byte of 0x00



	BankSel(MACON1); // Enter Bank 2 and configure the MAC
	// Enable the receive portion of the MAC
	//WriteReg((uint8_t)MACON1, MACON1_TXPAUS | MACON1_RXPAUS | MACON1_MARXEN);
	WriteReg((uint8_t)MACON1,  MACON1_MARXEN);
	// Pad packets to 60 bytes, add CRC, and check Type/Length field - duplex dependant.
	WriteReg((uint8_t)MACON3, (MACON3_PADCFG0*1) | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX);
	WriteReg((uint8_t)MABBIPG, 0x15);
	// 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)
    //debug WriteReg((uint8_t)MACON4, MACON4_DEFER);
//	WriteReg((uint8_t)MACON4, MACON4_BPEN|MACON4_NOBKOFF);
	// 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((uint8_t)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.
	WriteRegW((uint8_t)MAIPGL, 0xc12);
	// Set the maximum packet size which the controller will accept
	WriteRegW((uint8_t)MAMXFLL,6+6+2+1500+4);  // 1518 is the IEEE 802.3 specified limit
	BankSel(MAADR1);                        // Enter Bank 3 and initialize physical MAC address registers
    WriteReg((uint8_t)MAADR1, IPC_Local.MyMACAddr.v[0]);
    WriteReg((uint8_t)MAADR2, IPC_Local.MyMACAddr.v[1]);
    WriteReg((uint8_t)MAADR3, IPC_Local.MyMACAddr.v[2]);
    WriteReg((uint8_t)MAADR4, IPC_Local.MyMACAddr.v[3]);
    WriteReg((uint8_t)MAADR5, IPC_Local.MyMACAddr.v[4]);
    WriteReg((uint8_t)MAADR6, IPC_Local.MyMACAddr.v[5]);

	WriteReg((uint8_t)ECOCON, 0x00);	    // Output off (0V) Disable the CLKOUT output to reduce EMI generation
	WritePHYReg(PHCON2, PHCON2_HDLDIS);    	// Disable half duplex loopback in PHY.
	WritePHYReg(PHLCON, 0x3472);            // Configure LEDA to display LINK status, LEDB to display TX/RX activity
	WritePHYReg(PHCON1, PHCON1_PDPXMD); 	// Set the MAC and PHY into the full duplex state - Return to default Bank 0 done by WritePHYReg
	BFSReg(ECON1, ECON1_RXEN);              // Enable packet reception
	ETH_ENC_Resets++;
	IPSTATE=0;
}//end MACInit




void MACFlush(void)
{
	BFSReg(ECON1, ECON1_TXRST);
	BFCReg(ECON1, ECON1_TXRST);
	BFCReg(ESTAT, ESTAT_TXABRT | ESTAT_LATECOL);
	BFCReg(EIR, EIR_TXERIF | EIR_TXIF);
	BankSel(ETXSTL);	//  Bank 0
	WriteRegW(ETXNDL,end_Ptr);
	BFSReg(ECON1, ECON1_TXRTS);
}

BOOL MACIsTxReady(void)
{
	if(ReadMACReg(ESTAT).ESTATbits.TXABRT)
    {
		/*if(ReadMACReg(ESTAT).ESTATbits.LATECOL)
		{
			MACFlush(); //debug retry tried - was never called = LATECOL is not detected
	        ETH_ENC_Tx_Resets++;
	        return 0;
		}
		else*/
		{
			BFSReg(ECON1, ECON1_TXRST);
			BFCReg(ECON1, ECON1_TXRST);
			BFCReg(ESTAT, ESTAT_TXABRT | ESTAT_LATECOL);
			BFCReg(EIR, EIR_TXERIF | EIR_TXIF);
	        ETH_ENC_Tx_Resets++;
		}

    }
	 return !(ReadMACReg(ECON1).ECON1bits.TXRTS);
}

void MACDiscardRx(void)
{
	uint16_t NewRXRDLocation;
	NewRXRDLocation = NextPacketLocation.Val - 1;
	if(NewRXRDLocation > RXSTOP)   NewRXRDLocation = 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.
	BankSel(ERXRDPTL);	//  Bank 0
    WriteRegW(ERXRDPTL, NewRXRDLocation);
}




void MACArrayONW(uint8_t cmd, uint16_t len, uint32_t Offset)
{
    ENC_CS_LOW;
	DMA_SPI_ENC_Send_Get((unsigned char *)&cmd,(unsigned char *)&cmd,1);
    DMA_SPI_ENC_Send_Get_NW((unsigned char *)&EPkt+Offset,(unsigned char *)&EPkt+Offset,len);
} //end MACArrayONW

void MACNWFinish(void)
{
    while ((ENC_ETH_DMA_RX_COMPLETE)==0){};
    DMA_DISABLE(&hdma_spi_enc_rx);
	DMA_DISABLE(&hdma_spi_enc_tx);
	SPI_ENC_P->CR2&=~(SPI_CR2_RXDMAEN|SPI_CR2_TXDMAEN);
 //Datasheet page 82
	ENC_CS_HIGH;
}

void WritePHYReg(uint8_t Register, uint16_t Data)
{
	// Write the register address
	BankSel(MIREGADR);
	WriteReg((uint8_t)MIREGADR, Register);

	// Write the data
	WriteRegW((uint8_t)MIWRL, ((uint16_t_VAL*)&Data)->Val);

	Wait_N_uS(11);
	// Wait until the PHY register has been written
	BankSel(MISTAT);
	while((ReadMACReg(MISTAT).Val&1)>0);
	BankSel(ERDPTL);	// Return to Bank 0
}//end WritePHYReg

uint16_t PhyReg[21];
void GetPhy(void)
{
	PhyReg[0]=ReadPHYReg((uint8_t)0);
	PhyReg[1]=ReadPHYReg((uint8_t)1);
	PhyReg[2]=ReadPHYReg((uint8_t)2);
	PhyReg[3]=ReadPHYReg((uint8_t)3);
	PhyReg[16]=ReadPHYReg((uint8_t)16);
	PhyReg[17]=ReadPHYReg((uint8_t)17);
	PhyReg[18]=ReadPHYReg((uint8_t)18);
	PhyReg[19]=ReadPHYReg((uint8_t)19);
	PhyReg[20]=ReadPHYReg((uint8_t)20);
}

uint16_t ReadPHYReg(uint8_t Register)
{
	uint16_t Data;
	// Write the register address
	BankSel(MIREGADR);
	WriteReg((uint8_t)MIREGADR, Register);
	BankSel(MICMD);

	WriteReg((uint8_t)MICMD, 1);
	Wait_N_uS(11); //wait until phy register read
	BankSel(MISTAT);
	while(ReadMACReg((uint8_t)MISTAT).MISTATbits.BUSY);
	BankSel(MICMD);
	WriteReg((uint8_t)MICMD, 0);
	BankSel(MIRDH);

	// Read  the data high
	Data=((uint16_t)(ReadMACReg((uint8_t)MIRDH).Val))<<8;
	// Read  the data low
	Data|=((uint16_t)ReadMACReg((uint8_t)MIRDL).Val);

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


void BankSel(uint16_t Register)
{
	static uint16_t oldbank=0x5000; //initialise to impossible value - force 1st use
	if ((oldbank&0xff00)!=(Register&0xff00))
	{
	 BFCReg(ECON1, ECON1_BSEL1 | ECON1_BSEL0);
	 if (((uint16_t_VAL*)&Register)->v[1]>0) BFSReg(ECON1, ((uint16_t_VAL*)&Register)->v[1]);  // Optimisation if (((uint16_t_VAL*)&Register)->v[1]>0)
	 oldbank=Register;
	}
}//end BankSel


/*****************************************************************************
  Function:
	uint16_t CalcIPChecksum(uint8_t* buffer, uint16_t count)

  Summary:
	Calculates an IP checksum value.

  Description:
	This function calculates an IP checksum over an array of input data.  The
	checksum is the 16-bit one's complement of one's complement sum of all
	words in the data (with zero-padding if an odd number of bytes are
	summed).  This checksum is defined in RFC 793.

  Precondition:
	buffer is uint16_t aligned (even memory address) on 16- and 32-bit PICs.

  Parameters:
	buffer - pointer to the data to be checksummed
	count  - number of bytes to be checksummed

  Returns:
	The calculated checksum.

  ***************************************************************************/
uint16_t CalcIPChecksum(uint8_t* buffer, uint16_t count)
{
	uint16_t i;
	uint16_t *val;
	uint32_t_VAL sum = {0x00000000ul};

	i = count >> 1;
	val = (uint16_t*)buffer;

	// Calculate the sum of all words
	while(i--)
		sum.Val += (uint32_t)*val++;

	// Add in the sum of the remaining byte, if present
	if(((uint16_t_VAL*)&count)->bits.b0)
		sum.Val += (uint32_t)*(uint8_t*)val;

	// Do an end-around carry (one's complement arrithmatic)
	sum.Val = (uint32_t)sum.w[0] + (uint32_t)sum.w[1];

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

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

BOOL IPCheckHeader(void)
{
    // Make sure that this is an IPv4 packet.
    if((EPkt.IPH.VersionIHL & 0xf0) != IP_VERSION)	return FALSE;

	// Throw this packet away if it is a fragment.
	if(EPkt.IPH.FragmentInfo & 0xFF1F)		return FALSE;

	// Validate the IP header.   A corrupt header will have a nonzero checksum.
    if(CalcIPChecksum((uint8_t *)&EPkt.IPH,sizeof(IP_HEADER)))      return FALSE;
    return TRUE;
}




void UDPSendNW( uint16_t len)
{
	// Set the SPI write pointer to the beginning of the transmit buffer (post per packet control byte)
	BankSel(EWRPTL);
	WriteRegW(EWRPTL, TXSTART+1);
	// Calculate where to put the TXND pointer , Write the TXND pointer
	end_Ptr=sizeof(IP_HEADER)+sizeof(UDP_HEADER)+len+sizeof(ETHER_HEADER) + TXSTART;

	// Set the per-packet control byte and write the Ethernet destination
	memcpy(&EPkt.ETHH.DestMACAddr.v[0],&EPkt.Pre.SourceMACAddr.v[0],sizeof(MAC_ADDR));
	memcpy(&EPkt.ETHH.SourceMACAddr.v[0],&IPConfig.IP.MyMACAddr,sizeof(MAC_ADDR));
	// Write our MAC address in the Ethernet source field
	_Identifier++;
	// Write the appropriate Ethernet Type uint16_t for the protocol being used
	EPkt.ETHH.Type.v[0]			= 0x08;
	EPkt.ETHH.Type.v[1]			= ETHER_IP;
    EPkt.IPH.VersionIHL       	= IP_VERSION | IP_IHL;
    EPkt.IPH.TypeOfService    	= IP_SERVICE;
    EPkt.IPH.TotalLength      	= BE2LEINT16(sizeof(UDP_HEADER)+ sizeof(IP_HEADER)+ len);
    EPkt.IPH.Identification   	= BE2LEINT16(_Identifier);
    EPkt.IPH.FragmentInfo     	= 0;
    EPkt.IPH.TimeToLive       	= MY_IP_TTL;
    EPkt.IPH.Protocol         	= IP_PROT_UDP;
    EPkt.IPH.HeaderChecksum   	= 0;
    EPkt.IPH.DestAddress.Val 	= EPkt.IPH.SourceAddress.Val;
	EPkt.IPH.SourceAddress.Val 	= IPC_Local.MyIPAddr.Val;
	EPkt.IPH.HeaderChecksum		= CalcIPChecksum((uint8_t*)&EPkt.IPH, sizeof(IP_HEADER));
    EPkt.UDPH.Length            = BE2LEINT16(len + sizeof(UDP_HEADER));
	EPkt.UDPH.Checksum 			= 0x0000;
    MACArrayONW(WBM,sizeof(ETHER_HEADER) + sizeof(UDP_HEADER)+ sizeof(IP_HEADER)+len,offsetof(ENC_PACKET,ETHH));
    IPSTATE=1;
	ETH_UDP_Xmits++;
}

void UDPSendMsgNW( uint16_t len,uint8_t * IP)
{
	// Set the SPI write pointer to the beginning of the transmit buffer (post per packet control byte)
	BankSel(EWRPTL);
	WriteRegW(EWRPTL, TXSTART+1);
	// Calculate where to put the TXND pointer , Write the TXND pointer
	end_Ptr=sizeof(IP_HEADER)+sizeof(UDP_HEADER)+len+sizeof(ETHER_HEADER) + TXSTART;

	// Set the per-packet control byte and write the Ethernet destination
	memcpy(&EPkt.ETHH.DestMACAddr.v[0],&IPConfig.IP.MyMACAddr,sizeof(MAC_ADDR));
	EPkt.ETHH.DestMACAddr.v[5]=IP[3];
	memcpy(&EPkt.ETHH.SourceMACAddr.v[0],&IPConfig.IP.MyMACAddr,sizeof(MAC_ADDR));

	// Write our MAC address in the Ethernet source field
	_Identifier++;
	// Write the appropriate Ethernet Type uint16_t for the protocol being used
	EPkt.ETHH.Type.v[0]=0x08;
	EPkt.ETHH.Type.v[1]=ETHER_IP;
    EPkt.IPH.VersionIHL       	= IP_VERSION | IP_IHL;
    EPkt.IPH.TypeOfService    	= IP_SERVICE;
    EPkt.IPH.TotalLength      	= BE2LEINT16(sizeof(UDP_HEADER)+ sizeof(IP_HEADER)+ len);
    EPkt.IPH.Identification   	= BE2LEINT16(_Identifier);
    EPkt.IPH.FragmentInfo     	= 0;
    EPkt.IPH.TimeToLive       	= MY_IP_TTL;
    EPkt.IPH.Protocol         	= IP_PROT_UDP;
    EPkt.IPH.HeaderChecksum   	= 0;
    memcpy(&EPkt.IPH.DestAddress.Val,IP,4);
	EPkt.IPH.SourceAddress.Val 	= IPC_Local.MyIPAddr.Val;
	EPkt.IPH.HeaderChecksum		= CalcIPChecksum((uint8_t*)&EPkt.IPH, sizeof(IP_HEADER));
	// Generate the UDP header
    EPkt.UDPH.SourcePort        =  1000 ;
    EPkt.UDPH.DestinationPort   =  1000 ;
    EPkt.UDPH.Length            = BE2LEINT16(len + sizeof(UDP_HEADER));
	EPkt.UDPH.Checksum 			= 0x0000;
    MACArrayONW(WBM,sizeof(ETHER_HEADER) + sizeof(UDP_HEADER)+ sizeof(IP_HEADER)+len,offsetof(ENC_PACKET,ETHH));
    IPSTATE=1;
	ETH_UDP_Xmits++;
}

void ICMPProcess(void)
{
    uint8_t * Buf;
    uint16_t len;
    Buf=(uint8_t *)&EPkt.UDPH;
    len=BE2LEINT16(EPkt.IPH.TotalLength);
	if((Buf[0] == 0x08u)&&(Buf[1] == 0x00u)&&(len<MAX_UDP_PAYLOAD))
	{
		// Calculate new Type, Code, and Checksum values
		Buf[0] = 0x00;	// Type: 0 (ICMP echo/ping reply)
		Buf[2] += 8;	// Subtract 0x0800 from the checksum
		if(Buf[2] < 8u)
		{
			Buf[3]++;
			if(Buf[3] == 0u)	Buf[2]++;
		}
// Create IP header in TX memory
	    EPkt.IPH.VersionIHL       = IP_VERSION | IP_IHL;
    	EPkt.IPH.TypeOfService    = IP_SERVICE;
    	EPkt.IPH.TotalLength      = BE2LEINT16(len);
	    EPkt.IPH.Identification   = ++_Identifier;
   	 	EPkt.IPH.FragmentInfo     = 0;
	    EPkt.IPH.TimeToLive       = MY_IP_TTL;
    	EPkt.IPH.Protocol         = IP_PROT_ICMP;
 	   	EPkt.IPH.HeaderChecksum   = 0;
    	EPkt.IPH.DestAddress.Val  = EPkt.IPH.SourceAddress.Val;
		EPkt.IPH.SourceAddress 	  = IPC_Local.MyIPAddr;
    	EPkt.IPH.HeaderChecksum   = CalcIPChecksum((uint8_t*)&EPkt.IPH, sizeof(IP_HEADER));
    	BankSel(EWRPTL);
    	WriteRegW(EWRPTL, TXSTART+1);
// Write the TXND pointer into the registers, given dataLen
		end_Ptr= len+sizeof(ETHER_HEADER)+TXSTART;
// Set the per-packet control byte and write the Ethernet destination
		memcpy(&EPkt.ETHH.DestMACAddr,&EPkt.Pre.SourceMACAddr,sizeof(MAC_ADDR));
		memcpy(&EPkt.ETHH.SourceMACAddr,&IPC_Local.MyMACAddr,sizeof(MAC_ADDR));
// Write our MAC address in the Ethernet source field
// Write the appropriate Ethernet Type uint16_t for the protocol being used
		EPkt.ETHH.Type.v[0]=0x08;
		EPkt.ETHH.Type.v[1]=ETHER_IP;
// Copy ICMP response into the TX memory
		MACArrayONW(WBM,len+sizeof(ETHER_HEADER),offsetof(ENC_PACKET,ETHH));
        IPSTATE=1;
		ETH_ICMP_Responses++;
	}
}


void ARPProcess(void)
{
	ARP_PACKET * packet;
	packet=(ARP_PACKET *)&EPkt.IPH;
// Validate the ARP packet is ours
	if (packet->HardwareType != HW_ETHERNET_LE    ||
		packet->MACAddrLen != sizeof(MAC_ADDR)  ||
	  	packet->ProtocolLen != sizeof(IP_ADDR)||
		packet->TargetIPAddr.Val != IPC_Local.MyIPAddr.Val
		)
	{
 		ETH_Dropped_Packets++;
		return;
    }

// Reply if request
	if(packet->Operation == ARP_OPERATION_REQ_LE)
	{
   		packet->Operation = ARP_OPERATION_RESP_LE;
 		memcpy(&packet->TargetMACAddr, &EPkt.Pre.SourceMACAddr, sizeof(MAC_ADDR));
	   	packet->TargetIPAddr = packet->SenderIPAddr;;
		memcpy(&packet->SenderMACAddr, &IPC_Local.MyMACAddr, sizeof(MAC_ADDR));
    	packet->SenderIPAddr  = IPC_Local.MyIPAddr;
    	BankSel(EWRPTL);
    	WriteRegW(EWRPTL, TXSTART+1);
// Calculate where to put the TXND pointer
		end_Ptr = sizeof(ARP_PACKET)+ sizeof(ETHER_HEADER)+TXSTART;

// Set the per-packet control byte and write the Ethernet destination
		memcpy(&EPkt.ETHH.DestMACAddr,&EPkt.Pre.SourceMACAddr,sizeof(MAC_ADDR));
		memcpy(&EPkt.ETHH.SourceMACAddr,&IPC_Local.MyMACAddr,sizeof(MAC_ADDR));
// Write our MAC address in the Ethernet source field
// Write the appropriate Ethernet Type uint16_t for the protocol being used
		EPkt.ETHH.Type.v[0]=0x08;
		EPkt.ETHH.Type.v[1]=ETHER_ARP;
		MACArrayONW(WBM,sizeof(ARP_PACKET)+sizeof(ETHER_HEADER),offsetof(ENC_PACKET,ETHH));
        IPSTATE=1;
		ETH_ARP_Responses++;
	}
    else ETH_Dropped_Packets++;
}

void pp_memcpy(uint8_t *dst,uint8_t * src ,uint32_t len)
{
	uint32_t * src32;
	uint32_t * dst32;
	uint16_t * src16;
	uint16_t * dst16;
	uint32_t  tmplen;
	if (((((uint32_t)src|(uint32_t)dst)&3)==0) &&  (((uint32_t)len>>2)>0))
	{
		src32=(uint32_t *)src;
		dst32=(uint32_t *)dst;
		tmplen=len>>2;
		len-=(tmplen<<2);
		while (tmplen-->0)
		{
			*dst32++=*src32++;
		}

		src=(uint8_t *)src32;
		dst=(uint8_t *)dst32;
	}
	if (((((uint32_t)src|(uint32_t)dst)&1)==0) &&  (((uint32_t)len>>1)>0))
	{
		src16=(uint16_t *)src;
		dst16=(uint16_t *)dst;
		tmplen=len>>1;
		len-=(tmplen<<1);
		while (tmplen-->0)
		{
			*dst16++=*src16++;
		}

		src=(uint8_t *)src16;
		dst=(uint8_t *)dst16;
	}
	if (len>0)
	{
   	memcpy(dst,src ,len);
	}
}


void pp_mem_set(uint8_t *dst,uint8_t * src ,uint32_t len)
{
	uint32_t * src32;
	uint32_t * dst32;
	uint16_t * src16;
	uint16_t * dst16;
	uint8_t * src8;
	uint8_t * dst8;
	if ((((uint32_t)src|(uint32_t)dst|(uint32_t)len)&3)==0)
	{
		src32=(uint32_t *)src;
		dst32=(uint32_t *)dst;
		len>>=2;
		while (len-->0)
		{
			*dst32++|=*src32++;
		}
	}
	else
	{
		if ((((uint32_t)src|(uint32_t)dst|(uint32_t)len)&1)==0)
			{
				src16=(uint16_t *)src;
				dst16=(uint16_t *)dst;
				len>>=1;
				while (len-->0)
				{
					*dst16++|=*src16++;
				}
			}
			else
			{
				src8=(uint8_t *)src;
				dst8=(uint8_t *)dst;
				while (len-->0)
				{
					*dst8++|=*src8++;
				}
			}
	}
}

void pp_mem_clear(uint8_t *dst,uint8_t * src ,uint32_t len)
{
	uint32_t * src32;
	uint32_t * dst32;
	uint16_t * src16;
	uint16_t * dst16;
	uint8_t * src8;
	uint8_t * dst8;
	if ((((uint32_t)src|(uint32_t)dst|(uint32_t)len)&3)==0)
	{
		src32=(uint32_t *)src;
		dst32=(uint32_t *)dst;
		len>>=2;
		while (len-->0)
		{
			*dst32++&=~*src32++;
		}
	}
	else
	{
		if ((((uint32_t)src|(uint32_t)dst|(uint32_t)len)&1)==0)
		{
			src16=(uint16_t *)src;
			dst16=(uint16_t *)dst;
			len>>=1;
			while (len-->0)
			{
				*dst16++&=~*src16++;
			}
			}
			else
			{
					src8=(uint8_t *)src;
					dst8=(uint8_t *)dst;
					while (len-->0)
					{
						*dst8++&=~*src8++;
					}
			}
	}
}


void UDPPeek(UDPPP * PPPkt)
{
 PPPkt->hdr.cmd=UDP_PEEK_REPLY;
 pp_memcpy((unsigned char *)&PPPkt->pl.add,(unsigned char *)PPPkt->pl.add,PPPkt->hdr.len);
 UDPSendNW(PPPkt->hdr.len+sizeof(UDPPPH));
}

void UDPGetPPInfo(UDPPP * PPPkt)
{
 PP_INFO * p;
 p=(PP_INFO *)PPPkt;
 p->Cmd=UDP_GET_PP_INFO_REPLY;
 p->MaxLen=MAX_UDP_PAYLOAD-sizeof(PP_INFO)-sizeof(uint32_t);
 p->RegAdr=(uint32_t)&Reg_Reg_Name;
 p->RegEntSize=sizeof(RegEnt);
 UDPSendNW(  sizeof(PP_INFO) );
}



void UDPPokeByName(UDPPPN *PPPkt)
{
 RegEnt *R;
 R=RegistryFindByName((char *)PPPkt->name);
 if (R!=0)
 {
  memcpy(PPPkt->pl.add+R->ptr,PPPkt->pl.dat,MIN(PPPkt->hdr.len,R->len));
 }
}

void UDPPoke(UDPPP * PPPkt)
{
 PPPkt->hdr.cmd=UDP_POKE_REPLY;
 pp_memcpy((unsigned char *)PPPkt->pl.add,PPPkt->pl.dat,PPPkt->hdr.len);
 UDPSendNW(  sizeof(UDPPPH) );
}

void UDPBitSet(UDPPP * PPPkt)
{
 PPPkt->hdr.cmd=UDP_BIT_SET_REPLY;
 pp_mem_set((unsigned char *)PPPkt->pl.add,PPPkt->pl.dat,PPPkt->hdr.len);
 UDPSendNW(  sizeof(UDPPPH) );
}

void UDPBitClear(UDPPP * PPPkt)
{
 PPPkt->hdr.cmd=UDP_BIT_CLEAR_REPLY;
 pp_mem_clear((unsigned char *)PPPkt->pl.add,PPPkt->pl.dat,PPPkt->hdr.len);
 UDPSendNW(  sizeof(UDPPPH) );
}


__NO_RETURN __STATIC_INLINE void NVIC_SystemReset_local(void)
{
  __DSB();                                                     /* Ensure all outstanding memory accesses included
                                                              buffered write are completed before reset */
  SCB->AIRCR  = ((0x5FA << SCB_AIRCR_VECTKEY_Pos)      |
                 (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
                 SCB_AIRCR_SYSRESETREQ_Msk);                   /* Keep priority group unchanged */
  __DSB();                                                     /* Ensure completion of memory access */
  while(1);                                                    /* wait until reset */
}



void UDPReset(UDPPP * PPPkt)
{
 IPSTATE=6;
 Initialised=0;
 TIM4->CR1 = 0;   // Disable scheduler timer
 PPPkt->hdr.cmd=UDP_RESET_REPLY;
 UDPSendNW(sizeof(UDPPPH));
 MACNWFinish();

// Transmit the packet
 MACFlush();
 while (!MACIsTxReady()){};
 NVIC_SystemReset();
 while(1) {};

}


#define OPTKEY1 0x45670123l
#define OPTKEY2 0xCDEF89ABl

#if (FLASH_CONFIG_SECTOR_BLOCKSIZE==1024)
void    EraseFlashSector(volatile uint32_t Sector )
{
	FLASH_TypeDef * flash=(FLASH_TypeDef *)FLASH_R_BASE;
	flash->KEYR=OPTKEY1;
	flash->KEYR=OPTKEY2;
	while((flash->SR&FLASH_SR_BSY)>0);
	flash->CR|=(2<<8);  //2=32bits
	flash->CR|=(FLASH_CR_SER);
	flash->CR&=~((0x0f)<<3);
	flash->CR|=((Sector&0x0f)<<3);
	flash->CR|=FLASH_CR_STRT;
	while((flash->SR&FLASH_SR_BSY)>0);
}


void ProgFlashWords(UDPPP * PPPkt)
{
	FLASH_TypeDef * flash=(FLASH_TypeDef *)FLASH_R_BASE;
	 uint32_t  len;
	 volatile uint32_t * Prog_Adr;
	 volatile uint32_t * Prog_Di;
	 Prog_Adr= (volatile uint32_t * )PPPkt->pl.add;
	 Prog_Di=(volatile uint32_t * )&PPPkt->pl.dat;
	 len=PPPkt->hdr.len;
	 if (((((uint32_t)FLASH_CONFIG_SECTOR_BLOCKSIZE)-1)&(uint32_t)Prog_Adr)==0)
	 {
	   EraseFlashSector( FLASH_CONFIG_SECTOR_NO );
	 }
	 flash->CR|=(2<<8);  //2=32bits
	 flash->CR|=(FLASH_CR_PG);
	 while((flash->SR&FLASH_SR_BSY)>0);
	 len>>=2;
	 while (len-->0)
	 {
		 *Prog_Adr++= *Prog_Di++;
		 while((flash->SR&FLASH_SR_BSY)>0);
	 }
}


#else
void    EraseFlashSector( uint32_t Sector )
{
	FLASH_TypeDef * flash=(FLASH_TypeDef *)FLASH_R_BASE;
	flash->KEYR=OPTKEY1;
	flash->KEYR=OPTKEY2;
	while((flash->SR&FLASH_SR_BSY)>0);
	flash->CR|=(2<<8);  //2=32bits
	flash->CR|=(FLASH_CR_SER);
	flash->CR&=~((0x0f)<<3);
	flash->CR|=((Sector&0x0f)<<3);
	flash->CR|=FLASH_CR_STRT;
	while((flash->SR&FLASH_SR_BSY)>0);
}


void ProgFlashWords(UDPPP * PPPkt)
{
 FLASH_TypeDef * flash=(FLASH_TypeDef *)FLASH_R_BASE;
 uint32_t  len;
 volatile uint32_t * Prog_Adr;
 volatile uint32_t * Prog_Di;

 Prog_Adr= (volatile uint32_t * )PPPkt->pl.add;
 Prog_Di=(volatile uint32_t * )&PPPkt->pl.dat;
 len=PPPkt->hdr.len;
 if (((((uint32_t)FLASH_CONFIG_SECTOR_BLOCKSIZE)-1)&(uint32_t)Prog_Adr)==0)
 {
   EraseFlashSector( FLASH_CONFIG_SECTOR_NO );
 }
 flash->CR|=(2<<8);  //2=32bits
 flash->CR|=(FLASH_CR_PG);
 while((flash->SR&FLASH_SR_BSY)>0);
 len+=3;
 len>>=2;
 while (len-->0)
 {
	 *Prog_Adr++= *Prog_Di++;
	 while((flash->SR&FLASH_SR_BSY)>0);
 }
}

#endif




void UDPEraseProgFlash(UDPPP * PPPkt)
{
 uint16_t Prog_Key=12345;
 if (Prog_Key==UDPFLASHKEY)
 {
  /* stop the scheduler*/
  SchedulerShield=1;  //set to 0 by slowest task in scheduler.c
  SCHEDULER_TIMER->CR1 &=~( TIM_CR1_CEN);

  ProgFlashWords(PPPkt);
  /* start the scheduler*/
  SCHEDULER_TIMER->CR1 |=( TIM_CR1_CEN);
  PPPkt->hdr.cmd=UDP_ERASE_PROG_FLASH_REPLY;
  UDPSendNW( sizeof(UDPPPH) );

 }
}

void UDPProcess(void)
{
 uint16_t length;
 UDPPP * PPPkt;
 PPPkt = (UDPPP *)&EPkt.PL[0];
 length           = BE2LEINT16(EPkt.UDPH.Length);
 if ((length>0)&&(length<=MAX_UDP_PAYLOAD))
 {
    switch (PPPkt->hdr.cmd)
    {
     	case UDP_GET_PP_INFO: 		UDPGetPPInfo(PPPkt); 			break;
     	case UDP_PEEK:				UDPPeek(PPPkt);      			break;
     	case UDP_POKE:      		UDPPoke(PPPkt);      			break;

     	case UDP_BIT_SET:      		UDPBitSet(PPPkt);       		break;
     	case UDP_BIT_CLEAR:     	UDPBitClear(PPPkt);     		break;

     	case UDP_ERASE_PROG_FLASH:	UDPEraseProgFlash(PPPkt);		break;
     	case UDP_RESET:       		UDPReset(PPPkt);				break;
     	case UDP_POKE_BY_NAME:      UDPPokeByName((UDPPPN *)PPPkt);	break;
     	default:  					ETH_Dropped_Packets++;			break;
    }
  }
}


void MACGetPacket(void)
{
	volatile uint8_t PacketCount;
	// Test if at least one packet has been received and is waiting

	BankSel(EPKTCNT);
	if(ReadMACReg(EIR).EIRbits.RXERIF)
	{
		//MACInit();
		 //NVIC_SystemReset();
		// Receive section stalled - so reinitialise

		BankSel(ERDPTL);
		BFCReg(ECON1, ECON1_RXEN);//y              // Disable packet reception
		BFCReg(EIR, EIR_RXERIF); //y
		NextPacketLocation.Val = RXSTART;
		WriteRegW(ERXSTL,RXSTART); //y
		WriteRegW(ERXRDPTL,RXSTOP);//y
		WriteRegW(ERXNDL,RXSTOP); //y

		BFSReg(ECON1, ECON1_RXEN);             //y // Enable packet reception
		ETH_ENC_Rx_Resets++;
		IPSTATE=0;
		return;
	}
	PacketCount = ReadMACReg((uint8_t)EPKTCNT).Val;
	BankSel(ERDPTL);
	if(PacketCount == 0u)  { IPSTATE=0;  return;}
	// Set the SPI read pointer to the beginning of the next unprocessed packet
	CurrentPacketLocation.Val = NextPacketLocation.Val;
	WriteRegW(ERDPTL, CurrentPacketLocation.Val);
	// Obtain the MAC header from the Ethernet buffer
    MACArrayONW(RBM,sizeof(ENC_PREAMBLE),offsetof(ENC_PACKET,Pre)); //debug
    MACNWFinish();
	NextPacketLocation.Val = EPkt.Pre.NextPacketPointer; // Save the location where the hardware will write the next packet to
	// The EtherType field are in big endian.
    if( !((EPkt.Pre.Type.v[0] == 0x08u) && ((EPkt.Pre.Type.v[1] == ETHER_IP) || (EPkt.Pre.Type.v[1] == ETHER_ARP))) )
    {
    	EPkt.Pre.Type.v[0]=MAC_UNKNOWN;
	 	ETH_Dropped_Packets++;
     	MACDiscardRx();
     	IPSTATE=0;
	 	return ;
    }
	// Validate the data returned from the ENC28J60.
	if((EPkt.Pre.NextPacketPointer > RXSTOP) ||
 	   (((uint8_t)EPkt.Pre.NextPacketPointer))&1 ||
	   EPkt.Pre.StatusVector.bits.Zero ||
	   EPkt.Pre.StatusVector.bits.CRCError ||
	   (EPkt.Pre.StatusVector.bits.ByteCount > 1518u) ||
	   !EPkt.Pre.StatusVector.bits.ReceiveOk)
	{
		// MACInit will set IPSTATE to 0
	    MACInit();
	    return;
	}
    if (EPkt.Pre.StatusVector.bits.ByteCount>MAX_ETHTX) //check for too big
    {
	 ETH_Dropped_Packets++;
     MACDiscardRx();
     IPSTATE=0;
     return;
    }
    else
    {
	 MACArrayONW(RBM,EPkt.Pre.StatusVector.bits.ByteCount,offsetof(ENC_PACKET,IPH) ) ;
	 IPSTATE=2;
	 return;
    }
}

SEND_BY_NAME_STRUCT * SBNLast=0;
SEND_BY_NAME_STRUCT * SBNFirst=0;
void SendByName(SEND_BY_NAME_STRUCT * S)
{
 S->Busy=1;
 S->Next=0;
 if (SBNLast!=0) SBNLast->Next=S;
 SBNLast=S;
 if (SBNFirst==0)  SBNFirst=S;
}

void DoSendByName(void)
{
 UDPPPN *PPPkt = (UDPPPN *)&EPkt.PL[0];
 //do we have a packet to send?
 if (SBNFirst==0) return;
 PPPkt->hdr.cmd=UDP_POKE_BY_NAME;
 PPPkt->hdr.len=SBNFirst->Len;
 memcpy(PPPkt->name,SBNFirst->Name,REGNAMELEN);
 memcpy(PPPkt->pl.dat,SBNFirst->Data,SBNFirst->Len);
 PPPkt->pl.add=SBNFirst->Offset;
 UDPSendMsgNW(PPPkt->hdr.len+sizeof(UDPPPN),SBNFirst->IP);
 SBNFirst->Busy=0;
 if (SBNFirst->Next==0) SBNLast=0;
 SBNFirst=SBNFirst->Next;
 IPSTATE=1;
}

void IPDo()
{

   if (IPSTATE==1) // Send Packet from spi to enc proceeding - if finished then flush
   {
   	if(ENC_ETH_DMA_RX_COMPLETE)
       {
    	  MACNWFinish();
   	   	  MACFlush();
          IPSTATE=0;
       } //Packet in ENC Tx buffer ready for sending
   	  // return;//reduce cpu load by not dropping thru
    }

   if (IPSTATE==0)
    {
    	MACGetPacket(); // Sets IPSTATE 0 if nothing useful found - 2 otherwise -RX DMA in progress
    	return; //reduce cpu load by not dropping thru to
    }

   if (IPSTATE==2)
   {
       if (ENC_ETH_DMA_RX_COMPLETE)
       {
        MACNWFinish();
   	    MACDiscardRx();
   	    IPSTATE=3;
       }
   	  // return;//reduce cpu load by not dropping thru
   }


    if (IPSTATE==3)
    {
     if(MACIsTxReady())
     {
       	IPSTATE=0;
     	// Dispatch the packet to the appropriate handler
    	 switch(EPkt.Pre.Type.v[1])
    	 {
    	   case MAC_ARP: ARPProcess();  break;
    	   case MAC_IP:
    	   if(!IPCheckHeader( )) {ETH_Dropped_Packets++;	 break;}
           switch(EPkt.IPH.Protocol)
           {
    		  case IP_PROT_UDP: UDPProcess(); 	 break;
    	      case IP_PROT_ICMP:ICMPProcess(); 	 break;
              default: ETH_Dropped_Packets++; 	 break;
      	   } break;
    	   default: 	ETH_Dropped_Packets++;
    	   break;
       	  }
    	// return; //reduce cpu load by not dropping thru
     }
    }



  //  if (IPSTATE==0)
  //  {
    // if (MACIsTxReady())
    // {
       //DoSendByName();
   //  }
 //  }
}


