/*********************************************
 * vim:sw=8:ts=8:si:et
 * To use the above modeline in vim you must have "set modeline" in your .vimrc
 * Author: Guido Socher 
 * Copyright: GPL V2
 * http://www.gnu.org/licenses/gpl.html
 *
 * Ported to STM32F4 by Jiri Pittner 2013 (jiri@pittnerovi.com)
 *
 * Based on the enc28j60.c file from the AVRlib library by Pascal Stang.
 * For AVRlib See http://www.procyonengineering.com/
 * Used with explicit permission of Pascal Stang.
 *
 * Title: Microchip ENC28J60 Ethernet Interface Driver
 * Chip type           : ATMEGA88 with ENC28J60
 *
 * Original file adapted for Arduino use
 *********************************************/
#include "enc28j60.h"
#include <string.h>
//TODO: interrupt, DMA for sending/receiving a buffer

/////////////////////////////////////////////////////////////////
#ifdef AVR_SOURCE
#include <avr/io.h>
#include <avr/interrupt.h>  //AT: for cli() and sei()
#define delayMicroseconds(x) delay_xs(XTAL*(x)/4/1000000)

//PIN connections: there is just one SPI on atmega
#define ENC28J60_CONTROL_PORT   PORTB
#define ENC28J60_CONTROL_DDR    DDRB
#define ENC28J60_CONTROL_CS PB4
#define ENC28J60_CONTROL_SO PB6
#define ENC28J60_CONTROL_SI PB5
#define ENC28J60_CONTROL_SCK PB7
//interrupt0 connected but unused here

#define CSACTIVE cbi(ENC28J60_CONTROL_PORT,ENC28J60_CONTROL_CS)
#define CSPASSIVE sbi(ENC28J60_CONTROL_PORT,ENC28J60_CONTROL_CS)
#define waitspi() while(!(SPSR&(1<<SPIF)))
#endif

///////////////////////////////////////////////////////////////////
#ifdef ARM_SOURCE37
//PIN connections: SPI1 as AF5: RESET=PC5, INT=PC4, CS=PC6, SCK=PC7, MISO=PC8, MOSI=PC9
#include "../lib/inc/stm32f37x.h"
#include "../lib/inc/stm32f37x_conf.h"
#include "../lib/inc/peripherals/stm32f37x_spi.h"
#include "../delay.h"
#define delayMicroseconds(x) delay_us(x)

#define SPI_FLAG_TXE SPI_I2S_FLAG_TXE
#define SPI_FLAG_RXNE SPI_I2S_FLAG_RXNE
#define SPI_FLAG_BSY SPI_I2S_FLAG_BSY

#define CSACTIVE GPIO_ResetBits(GPIOC, GPIO_Pin_6)
#define CSPASSIVE GPIO_SetBits(GPIOC, GPIO_Pin_6)
#define waitspi() while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY)==SET)

void SPI1_SendByte(u8 byte) {
u32 LIS302DLTimeout = 0x1000;

while (SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_TXE) == RESET) 
{
//@@@@@if((LIS302DLTimeout--) == 0) return;
}
/* Send a Byte through the SPI peripheral */
SPI_SendData8(SPI1, byte);
}

uint8_t SPI1_GetByte() {
u32 LIS302DLTimeout = 0x1000;
/* Wait to receive a Byte */
while (SPI_I2S_GetFlagStatus(SPI1,  SPI_FLAG_RXNE) == RESET) {
                                    if((LIS302DLTimeout--) == 0) return 0x00; //really needed at least on the 37!!!!
                                        }
/* Return the Byte read from the SPI bus */
//uint8_t s; do { s= SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY); }while(s==SET);

uint8_t r= SPI_ReceiveData8(SPI1);
return r;
}


void spi_write(uint8_t *msg, uint16_t len, uint8_t release)
{
CSACTIVE;
while (SPI_I2S_GetFlagStatus(SPI1,  SPI_I2S_FLAG_RXNE) == SET) SPI_ReceiveData8(SPI1); //flush rx fifo
while(len--)
        {
        SPI1_SendByte(*msg);
        *msg++ = SPI1_GetByte();
        }
FlagStatus s;
do {
        s= SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY);
        }while(s==SET);
delay_us(3);
if(release) CSPASSIVE;
}




#endif 
///////////////////////////////////////////////////////////////////

#ifdef ARM_SOURCE4
//PIN connections: SPI1 as AF5, RESET= PB8 INT= PB5 CS= PA0 SCK= PA5 MOSI= PA7 MISO= PA6
#include "../lib/inc/stm32f4_discovery.h"
#include "../lib/inc/stm32f4xx_conf.h"
#include "../lib/inc/peripherals/stm32f4xx_spi.h"
#include "../delay.h"
#define delayMicroseconds(x) delay_us(x)

#define CSACTIVE GPIO_ResetBits(GPIOA, GPIO_Pin_0)
#define CSPASSIVE GPIO_SetBits(GPIOA, GPIO_Pin_0)
#define waitspi() while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY)==SET)

void SPI1_SendByte(u8 byte) {
u32 LIS302DLTimeout = 0x1000;

while (SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_TXE) == RESET) 
{
//@@@@@if((LIS302DLTimeout--) == 0) return;
}

/* Send a Byte through the SPI peripheral */
SPI_I2S_SendData(SPI1, byte);
//{ char c[32]; sprintf(c,"T%02x ",byte); print(USART2,c); }
//uint8_t s; do { s= SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY); }while(s==SET);

}

uint8_t SPI1_GetByte() {
u32 LIS302DLTimeout = 0x1000;
/* Wait to receive a Byte */
while (SPI_I2S_GetFlagStatus(SPI1,  SPI_FLAG_RXNE) == RESET) {
                                    if((LIS302DLTimeout--) == 0) return 0x00;
                                        }

/* Return the Byte read from the SPI bus */
//uint8_t s; do { s= SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY); }while(s==SET);

uint8_t r= SPI_I2S_ReceiveData(SPI1);
//{ char c[32]; sprintf(c,"R%02x ",r); print(USART2,c); }
return r;
}


void spi_write(uint8_t *msg, uint16_t len, uint8_t release)
{
CSACTIVE;
while (SPI_I2S_GetFlagStatus(SPI1,  SPI_I2S_FLAG_RXNE) == SET) SPI_I2S_ReceiveData(SPI1); //flush rx fifo
while(len--)
        {
        SPI1_SendByte(*msg);
        *msg++ = SPI1_GetByte();
        }
FlagStatus s;
do {
        s= SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY);
        }while(s==SET);
delay_us(3);
if(release) CSPASSIVE;
}




#endif

////////////////////////////////////////////////////////////////////

static uint8_t Enc28j60Bank;
static int16_t gNextPacketPtr;
 static uint8_t erxfcon;



uint8_t enc28j60ReadOp(uint8_t op, uint8_t address)
{
        CSACTIVE;
#ifdef AVR_SOURCE
        // issue read command
        SPDR = op | (address & ADDR_MASK);
        waitspi();
        // read data
        SPDR = 0x00;
        waitspi();
        // do dummy read if needed (for mac and mii, see datasheet page 29)
        if(address & 0x80)
        {
                SPDR = 0x00;
                waitspi();
        }
uint8_t r =  SPDR;
#endif

#if defined(ARM_SOURCE4) || defined(ARM_SOURCE37)
SPI1_SendByte(op | (address & ADDR_MASK));
waitspi();
uint8_t dummy = SPI1_GetByte();
waitspi();
SPI1_SendByte(0x00);
waitspi();
// do dummy read if needed (for mac and mii, see datasheet page 29)
         if(address & 0x80)
                 {
                 dummy = SPI1_GetByte();
                 waitspi();
                 SPI1_SendByte(0x00);
                 }
waitspi();
uint8_t r = SPI1_GetByte(); 
waitspi();
//{ char z[100]; sprintf(z,"ReadOp %02x %02x\n",dummy,r); printsimple(USART2,z); }
#endif

        // release CS
        CSPASSIVE;
return r;
}

void enc28j60WriteOp(uint8_t op, uint8_t address, uint8_t data)
{
        CSACTIVE;
        // issue write command
#ifdef AVR_SOURCE
        SPDR = op | (address & ADDR_MASK);
        waitspi();
        // write data
        SPDR = data;
        waitspi();
#endif

#if defined(ARM_SOURCE4) || defined(ARM_SOURCE37)
SPI1_SendByte(op | (address & ADDR_MASK));
waitspi();
uint8_t dummy = SPI1_GetByte();
SPI1_SendByte(data);
waitspi();
dummy = SPI1_GetByte();
waitspi();
#endif
        CSPASSIVE;
}


void enc28j60ReadBuffer(uint16_t len, uint8_t* data)
{
        CSACTIVE;
#ifdef AVR_SOURCE
        // issue read command
        SPDR = ENC28J60_READ_BUF_MEM;
        waitspi();
        while(len)
        {
                len--;
                // read data
                SPDR = 0x00;
                waitspi();
                *data = SPDR;
                data++;
        }
        *data='\0';
#endif
#if defined(ARM_SOURCE4) ||defined(ARM_SOURCE37)
        SPI1_SendByte(ENC28J60_READ_BUF_MEM);
        uint8_t dummy = SPI1_GetByte();
        memset(data,0,len);
        spi_write(data,len,1); 
        data[len]=0;
#endif
        CSPASSIVE;
}

void enc28j60WriteBuffer(uint16_t len, uint8_t* data)
{
CSACTIVE;
#ifdef AVR_SOURCE
        // issue write command
        SPDR = ENC28J60_WRITE_BUF_MEM;
        waitspi();
        while(len)
        {
                len--;
                // write data
                SPDR = *data;
                data++;
                waitspi();
        }
#endif
#if defined(ARM_SOURCE4) || defined(ARM_SOURCE37)
        SPI1_SendByte(ENC28J60_WRITE_BUF_MEM);
        uint8_t dummy = SPI1_GetByte();
        spi_write(data,len,1);
#endif
CSPASSIVE;
}

//higher level routines not directly MCU-dependent

void enc28j60SetBank(uint8_t address)
{
        // set the bank (if needed)
        if((address & BANK_MASK) != Enc28j60Bank)
        {
                // set the bank
                enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0));
                enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5);
                Enc28j60Bank = (address & BANK_MASK);
        }
}

uint8_t enc28j60Read(uint8_t address)
{
        // set the bank
        enc28j60SetBank(address);
        // do the read
        return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address);
}

// read upper 8 bits
uint16_t enc28j60PhyReadH(uint8_t address)
{

	// Set the right address and start the register read operation
	enc28j60Write(MIREGADR, address);
	enc28j60Write(MICMD, MICMD_MIIRD);
        //_delay_loop_1(40); // 10us
	    delayMicroseconds(15);	

	// wait until the PHY read completes
	while(enc28j60Read(MISTAT) & MISTAT_BUSY);

	// reset reading bit
	enc28j60Write(MICMD, 0x00);
	
	return (enc28j60Read(MIRDH));
}

void enc28j60Write(uint8_t address, uint8_t data)
{
        // set the bank
        enc28j60SetBank(address);
        // do the write
        enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data);
}

void enc28j60PhyWrite(uint8_t address, uint16_t data)
{
        // set the PHY register address
        enc28j60Write(MIREGADR, address);
        // write the PHY data
        enc28j60Write(MIWRL, data);
        enc28j60Write(MIWRH, data>>8);
        // wait until the PHY write completes
        uint8_t r;
        while((r=enc28j60Read(MISTAT)) & MISTAT_BUSY){
                //_delay_loop_1(40); // 10us
				delayMicroseconds(15);
        }
}

void enc28j60clkout(uint8_t clk)
{
        //setup clkout: 2 is 12.5MHz:
	enc28j60Write(ECOCON, clk & 0x7);
}

void enc28j60Init(uint8_t* macaddr)
{
	// initialize I/O
        //	
#ifdef AVR_SOURCE
     sbi(ENC28J60_CONTROL_DDR,ENC28J60_CONTROL_CS);
     CSPASSIVE; // ss=0
     sbi(ENC28J60_CONTROL_DDR,ENC28J60_CONTROL_SI);
     sbi(ENC28J60_CONTROL_DDR,ENC28J60_CONTROL_SCK);
     cbi(ENC28J60_CONTROL_DDR,ENC28J60_CONTROL_SO);
     cbi(ENC28J60_CONTROL_PORT,ENC28J60_CONTROL_SCK);
     cbi(ENC28J60_CONTROL_PORT,ENC28J60_CONTROL_SI);
     cbi(ENC28J60_CONTROL_PORT,ENC28J60_CONTROL_SO);//no pullup
	
	//
	// initialize SPI interface

	// SPI speed and master mode
	// AT: Original driver speed with Fosc/2 clock (max speed):
    SPCR = (1<<SPE)|(1<<MSTR);
	// AT: Use this alternate initialization with SPRO for slower Fosc/8 speed. 
	// Allows the use of some other slower SPI peripherals without having to switch speeds:
    // SPCR = (1<<SPE)|(1<<MSTR) | (1<<SPR0);
    
	SPSR |= (1<<SPI2X);
#endif

#ifdef ARM_SOURCE4
//interrupt and reset pins left in default high-Z for now

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    //RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //only for reset and int

    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_25MHz;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    //CS pin is not AF but regular output
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);


    SPI_InitTypeDef SPI_InitStructure;
    SPI_I2S_DeInit(SPI1);
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_Init(SPI1, &SPI_InitStructure);

    SPI_Cmd(SPI1, ENABLE);
#endif

#ifdef ARM_SOURCE37
//interrupt and reset pins left in default high-Z for now

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);

    SPI_I2S_DeInit(SPI1);

    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_7|GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_Init(GPIOC, &GPIO_InitStruct);

    //CS pin is not AF but regular output
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_Init(GPIOC, &GPIO_InitStruct);
    CSPASSIVE;
    //while(1) {GPIO_SetBits(GPIOC, GPIO_Pin_7); delay_ms(1); GPIO_ResetBits(GPIOC, GPIO_Pin_7); delay_ms(1);}

    //@@@reset and interrupt not used presently

    GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_5);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_5);


    SPI_Cmd(SPI1, DISABLE);
    SPI_I2S_DeInit(SPI1);
    SPI_InitTypeDef SPI_InitStructure;
    SPI_StructInit(&SPI_InitStructure);
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //OK for 72mhz main clock
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_Init(SPI1, &SPI_InitStructure);

    //needed on the F37 architecture, does not exist of F4!!!
    SPI_RxFIFOThresholdConfig(SPI1, SPI_RxFIFOThreshold_QF);

    SPI_Cmd(SPI1, ENABLE);
    /*
    printf("SPI1 init done\n");
    while(1) {CSACTIVE; SPI1_SendByte(0xaa);
                uint8_t x=SPI1_GetByte();
                int s;
                do {
                                s= SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY);
                                        }while(s==SET);

                CSPASSIVE;delay_ms(1);}
     */
#endif


	// perform system reset
	enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
        //_delay_loop_2(0); // 20ms
	delay_ms(50);
	// check CLKRDY bit to see if reset is complete
        // The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait.
	//while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY));
	// do bank 0 stuff
	// initialize receive buffer
	// 16-bit transfers, must write low byte first
	// set receive buffer start address
	gNextPacketPtr = RXSTART_INIT;
        // Rx start
	enc28j60Write(ERXSTL, RXSTART_INIT&0xFF);
	enc28j60Write(ERXSTH, RXSTART_INIT>>8);
	// set receive pointer address
	enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF);
	enc28j60Write(ERXRDPTH, RXSTART_INIT>>8);
	// RX end
	enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF);
	enc28j60Write(ERXNDH, RXSTOP_INIT>>8);
	// TX start
	enc28j60Write(ETXSTL, TXSTART_INIT&0xFF);
	enc28j60Write(ETXSTH, TXSTART_INIT>>8);
	// TX end
	enc28j60Write(ETXNDL, TXSTOP_INIT&0xFF);
	enc28j60Write(ETXNDH, TXSTOP_INIT>>8);
	// do bank 1 stuff, packet filter:
        // For broadcast packets we allow only ARP packtets
        // All other packets should be unicast only for our mac (MAADR)
        //
        // The pattern to match on is therefore
        // Type     ETH.DST
        // ARP      BROADCAST
        // 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9
        // in binary these poitions are:11 0000 0011 1111
        // This is hex 303F->EPMM0=0x3f,EPMM1=0x30
	enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN);
        erxfcon = ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN;
	enc28j60Write(EPMM0, 0x3f);
	enc28j60Write(EPMM1, 0x30);
	enc28j60Write(EPMCSL, 0xf9);
	enc28j60Write(EPMCSH, 0xf7);
        //
        //
	// do bank 2 stuff
	// enable MAC receive
	enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
	// bring MAC out of reset
	enc28j60Write(MACON2, 0x00);
	// enable automatic padding to 60bytes and CRC operations
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
	// set inter-frame gap (non-back-to-back)
	enc28j60Write(MAIPGL, 0x12);
	enc28j60Write(MAIPGH, 0x0C);
	// set inter-frame gap (back-to-back)
	enc28j60Write(MABBIPG, 0x12);
	// Set the maximum packet size which the controller will accept
        // Do not send packets longer than MAX_FRAMELEN:
	enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF);	
	enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8);
	// do bank 3 stuff
        // write MAC address
        // NOTE: MAC address in ENC28J60 is byte-backward
        enc28j60Write(MAADR5, macaddr[0]);
        enc28j60Write(MAADR4, macaddr[1]);
        enc28j60Write(MAADR3, macaddr[2]);
        enc28j60Write(MAADR2, macaddr[3]);
        enc28j60Write(MAADR1, macaddr[4]);
        enc28j60Write(MAADR0, macaddr[5]);
	// no loopback of transmitted frames
	enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS);
	// switch to bank 0
	enc28j60SetBank(ECON1);
	// enable interrutps
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);
	// enable packet reception
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
}

// read the revision of the chip:
uint8_t enc28j60getrev(void)
{
	return(enc28j60Read(EREVID));
}

// link status
uint8_t enc28j60linkup(void)
{
  // bit 10 (= bit 2 in upper reg)
	return (enc28j60PhyReadH(PHSTAT2) >> 2) & 1;
}

void enc28j60PacketSend(uint16_t len, uint8_t* packet)
{
        // Check no transmit in progress
        while (enc28j60ReadOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS)
        {
                // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12.
                if( (enc28j60Read(EIR) & EIR_TXERIF) ) {
                        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
                        enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
                }
        }
	// Set the write pointer to start of transmit buffer area
	enc28j60Write(EWRPTL, TXSTART_INIT&0xFF);
	enc28j60Write(EWRPTH, TXSTART_INIT>>8);
	// Set the TXND pointer to correspond to the packet size given
	enc28j60Write(ETXNDL, (TXSTART_INIT+len)&0xFF);
	enc28j60Write(ETXNDH, (TXSTART_INIT+len)>>8);
	// write per-packet control byte (0x00 means use macon3 settings)
	enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
	// copy the packet into the transmit buffer
	enc28j60WriteBuffer(len, packet);
	// send the contents of the transmit buffer onto the network
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
}

// just probe if there might be a packet
uint8_t enc28j60hasRxPkt(void)
{
	if( enc28j60Read(EPKTCNT) ==0 ){
		return(0);
        }
        return(1);
}

// Gets a packet from the network receive buffer, if one is available.
// The packet will by headed by an ethernet header.
//      maxlen  The maximum acceptable length of a retrieved packet.
//      packet  Pointer where packet data should be stored.
// Returns: Packet length in bytes if a packet was retrieved, zero otherwise.
uint16_t enc28j60PacketReceive(uint16_t maxlen, uint8_t* packet)
{
	uint16_t rxstat;
	uint16_t len;
	// check if a packet has been received and buffered
	//if( !(enc28j60Read(EIR) & EIR_PKTIF) )
        // The above does not work. See Rev. B4 Silicon Errata point 6.
	if( enc28j60Read(EPKTCNT) ==0 ){
		return(0);
        }

	// Set the read pointer to the start of the received packet
	enc28j60Write(ERDPTL, (gNextPacketPtr &0xFF));
	enc28j60Write(ERDPTH, (gNextPacketPtr)>>8);
	// read the next packet pointer
	gNextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
	gNextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
	// read the packet length (see datasheet page 43)
	len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
	len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
        len-=4; //remove the CRC count
	// read the receive status (see datasheet page 43)
	rxstat  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
	rxstat |= ((uint16_t)enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0))<<8;
	// limit retrieve length
        if (len>maxlen-1){
                len=maxlen-1;
        }
        // check CRC and symbol errors (see datasheet page 44, table 7-3):
        // The ERXFCON.CRCEN is set by default. Normally we should not
        // need to check this.
        if ((rxstat & 0x80)==0){
                // invalid
                len=0;
        }else{
                // copy the packet from the receive buffer
                enc28j60ReadBuffer(len, packet);
        }
	// Move the RX read pointer to the start of the next received packet
	// This frees the memory we just read out
	//enc28j60Write(ERXRDPTL, (gNextPacketPtr &0xFF));
	//enc28j60Write(ERXRDPTH, (gNextPacketPtr)>>8);
        //
        // Move the RX read pointer to the start of the next received packet
        // This frees the memory we just read out. 
        // However, compensate for the errata point 13, rev B4: never write an even address!
        // gNextPacketPtr is always an even address if RXSTOP_INIT is odd.
        if (gNextPacketPtr -1 > RXSTOP_INIT){ // RXSTART_INIT is zero, no test for gNextPacketPtr less than RXSTART_INIT.
                enc28j60Write(ERXRDPTL, (RXSTOP_INIT)&0xFF);
                enc28j60Write(ERXRDPTH, (RXSTOP_INIT)>>8);
        } else {
                enc28j60Write(ERXRDPTL, (gNextPacketPtr-1)&0xFF);
                enc28j60Write(ERXRDPTH, (gNextPacketPtr-1)>>8);
        }
	// decrement the packet counter indicate we are done with this packet
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
	return(len);
}




void enc28j60PowerDown() {
 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN);
 while(enc28j60Read(ESTAT) & ESTAT_RXBUSY);
 while(enc28j60Read(ECON1) & ECON1_TXRTS);
 enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV);
}

void enc28j60PowerUp() {
 enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV);
 while(!enc28j60Read(ESTAT) & ESTAT_CLKRDY);
}


// A number of utility functions to enable/disable broadcast and multicast bits
void enc28j60EnableBroadcast( void ) {
	erxfcon |= ERXFCON_BCEN;
	enc28j60Write(ERXFCON, erxfcon);
}

void enc28j60DisableBroadcast( void ) {
	erxfcon &= (0xff ^ ERXFCON_BCEN);
	enc28j60Write(ERXFCON, erxfcon);
}

void enc28j60EnableMulticast( void ) {
	erxfcon |= ERXFCON_MCEN;
	enc28j60Write(ERXFCON, erxfcon);
}

void enc28j60DisableMulticast( void ) {
	erxfcon &= (0xff ^ ERXFCON_MCEN);
	enc28j60Write(ERXFCON, erxfcon);
}

