mbed OS5
Fork of UIPEthernet by
Diff: utility/Enc28J60Network.cpp
- Revision:
- 4:d774541a34da
- Parent:
- 3:5b17e4656dd0
- Child:
- 8:4acb22344932
--- a/utility/Enc28J60Network.cpp Sat Dec 20 11:10:40 2014 +0000 +++ b/utility/Enc28J60Network.cpp Sun Mar 08 20:26:56 2015 +0000 @@ -1,695 +1,733 @@ -/* - Enc28J60Network.h - UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. - - Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de> - All rights reserved. - - based on enc28j60.c file from the AVRlib library by Pascal Stang. - For AVRlib See http://www.procyonengineering.com/ - - Modified (ported to mbed) by Zoltan Hudak <hudakz@inbox.com> - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -#include "Enc28J60Network.h" -#include "mbed.h" - -extern "C" -{ -#include "enc28j60.h" -#include "uip.h" -} -#define DMARUNNING 1 -#define DMANEWPACKET 2 - -#define waitspi() while(!(SPSR & (1 << SPIF))) - Enc28J60Network::Enc28J60Network(PinName mosi, PinName miso, PinName sclk, PinName cs) : - MemoryPool(TXSTART_INIT + 1, TXSTOP_INIT - TXSTART_INIT), - - // 1 byte in between RX_STOP_INIT and pool to allow prepending of controlbyte - bank(0xff), - _spi(mosi, miso, sclk), - _cs(cs) { } - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::init(uint8_t* macaddr) { - - // initialize SPI interface - - _spi.format(8, 0); // 8bit, mode 0 - _spi.frequency(7000000); // 7MHz - wait(1); // 1 second for stable state - - // initialize I/O - _cs = 1; // ss=0 - - // initialize SPI interface - // master mode and Fosc/2 clock: - //SPCR = (1<<SPE)|(1<<MSTR); - //SPSR |= (1<<SPI2X); - // perform system reset - writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); - wait(0.020); // 20ms - - // check CLKRDY bit to see if reset is complete - // The CLKRDY does not work. See Rev. B4 Silicon Errata point. Just wait. - //while(!(readReg(ESTAT) & ESTAT_CLKRDY)); - // do bank 0 stuff - // initialize receive buffer - // 16-bit transfers, must write low byte first - // set receive buffer start address - nextPacketPtr = RXSTART_INIT; - - // Rx start - writeRegPair(ERXSTL, RXSTART_INIT); - - // set receive pointer address - writeRegPair(ERXRDPTL, RXSTART_INIT); - - // RX end - writeRegPair(ERXNDL, RXSTOP_INIT); - - // TX start - //writeRegPair(ETXSTL, TXSTART_INIT); - // TX end - //writeRegPair(ETXNDL, TXSTOP_INIT); - // 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 - //TODO define specific pattern to receive dhcp-broadcast packages instead of setting ERFCON_BCEN! - writeReg(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN | ERXFCON_BCEN); - writeRegPair(EPMM0, 0x303f); - writeRegPair(EPMCSL, 0xf7f9); - - // - // - // do bank 2 stuff - // enable MAC receive - // and bring MAC out of reset (writes 0x00 to MACON2) - writeRegPair(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS); - - // enable automatic padding to 60bytes and CRC operations - writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN); - - // set inter-frame gap (non-back-to-back) - writeRegPair(MAIPGL, 0x0C12); - - // set inter-frame gap (back-to-back) - writeReg(MABBIPG, 0x12); - - // Set the maximum packet size which the controller will accept - // Do not send packets longer than MAX_FRAMELEN: - writeRegPair(MAMXFLL, MAX_FRAMELEN); - - // do bank 3 stuff - // write MAC address - // NOTE: MAC address in ENC28J60 is byte-backward - writeReg(MAADR5, macaddr[0]); - writeReg(MAADR4, macaddr[1]); - writeReg(MAADR3, macaddr[2]); - writeReg(MAADR2, macaddr[3]); - writeReg(MAADR1, macaddr[4]); - writeReg(MAADR0, macaddr[5]); - - // no loopback of transmitted frames - phyWrite(PHCON2, PHCON2_HDLDIS); - - // switch to bank 0 - setBank(ECON1); - - // enable interrutps - writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE | EIE_PKTIE); - - // enable packet reception - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); - clkout(2); // change clkout from 6.25MHz to 12.5MHz - wait(0.000060); // 60us - - //Configure leds - phyWrite(PHLCON, 0x476); -} - -/** - * @brief - * @note - * @param - * @retval - */ -memhandle Enc28J60Network::receivePacket(void) { - uint16_t rxstat; - uint16_t len; - // check if a packet has been received and buffered - - //if( !(readReg(EIR) & EIR_PKTIF) ){ - // The above does not work. See Rev. B4 Silicon Errata point 6. - if(readReg(EPKTCNT) != 0) { - uint16_t readPtr = nextPacketPtr + 6; - // Set the read pointer to the start of the received packet - - writeRegPair(ERDPTL, nextPacketPtr); - - // read the next packet pointer - nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); - nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; - - // read the packet length (see datasheet page 43) - len = readOp(ENC28J60_READ_BUF_MEM, 0); - len |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; - len -= 4; //remove the CRC count - - // read the receive status (see datasheet page 43) - rxstat = readOp(ENC28J60_READ_BUF_MEM, 0); - rxstat |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; - - // decrement the packet counter indicate we are done with this packet - writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); - - // 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) { - receivePkt.begin = readPtr; - receivePkt.size = len; - return UIP_RECEIVEBUFFERHANDLE; - } - - // Move the RX read pointer to the start of the next received packet - // This frees the memory we just read out - setERXRDPT(); - } - - return(NOBLOCK); -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::setERXRDPT(void) { - writeRegPair(ERXRDPTL, nextPacketPtr == RXSTART_INIT ? RXSTOP_INIT : nextPacketPtr - 1); -} - -/** - * @brief - * @note - * @param - * @retval - */ -memaddress Enc28J60Network::blockSize(memhandle handle) { - return handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::sendPacket(memhandle handle) { - memblock* packet = &blocks[handle]; - uint16_t start = packet->begin - 1; - uint16_t end = start + packet->size; - - // backup data at control-byte position - uint8_t data = readByte(start); - // write control-byte (if not 0 anyway) - - if(data) - writeByte(start, 0); - -#ifdef ENC28J60DEBUG - pc.print("sendPacket("); - pc.print(handle); - pc.print(") ["); - pc.print(start); - pc.print("-"); - pc.print(end); - pc.println("]:"); - for(uint16_t i = start; i <= end; i++) { - pc.print(readByte(i), HEX); - pc.print(" "); - } - - pc.println(); -#endif - // TX start - - writeRegPair(ETXSTL, start); - - // Set the TXND pointer to correspond to the packet size given - writeRegPair(ETXNDL, end); - - // send the contents of the transmit buffer onto the network - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); - - // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. - if((readReg(EIR) & EIR_TXERIF)) { - writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); - } - - //restore data on control-byte position - if(data) - writeByte(start, data); -} - -/** - * @brief - * @note - * @param - * @retval - */ -uint16_t Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) { - memblock* packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle]; - memaddress start = packet->begin + position; - - writeRegPair(ERDPTL, start); - - if(len > packet->size - position) - len = packet->size - position; - return len; -} - -/** - * @brief - * @note - * @param - * @retval - */ -uint16_t Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) { - len = setReadPtr(handle, position, len); - readBuffer(len, buffer); - return len; -} - -/** - * @brief - * @note - * @param - * @retval - */ -uint16_t Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) { - memblock* packet = &blocks[handle]; - uint16_t start = packet->begin + position; - - writeRegPair(EWRPTL, start); - - if(len > packet->size - position) - len = packet->size - position; - writeBuffer(len, buffer); - return len; -} - -/** - * @brief - * @note - * @param - * @retval - */ -uint8_t Enc28J60Network::readByte(uint16_t addr) { - uint8_t result; - - writeRegPair(ERDPTL, addr); - - _cs = 0; - - // issue read command - _spi.write(ENC28J60_READ_BUF_MEM); - - // read data - result = _spi.write(0x00); - _cs = 1; - return(result); -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::writeByte(uint16_t addr, uint8_t data) { - writeRegPair(EWRPTL, addr); - - _cs = 0; - - // issue write command - _spi.write(ENC28J60_WRITE_BUF_MEM); - - // write data - _spi.write(data); - _cs = 1; -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::copyPacket -( - memhandle dest_pkt, - memaddress dest_pos, - memhandle src_pkt, - memaddress src_pos, - uint16_t len -) { - memblock* dest = &blocks[dest_pkt]; - memblock* src = src_pkt == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[src_pkt]; - memblock_mv_cb(dest->begin + dest_pos, src->begin + src_pos, len); - - // Move the RX read pointer to the start of the next received packet - // This frees the memory we just read out - setERXRDPT(); -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::memblock_mv_cb(uint16_t dest, uint16_t src, uint16_t len) { - - //as ENC28J60 DMA is unable to copy single bytes: - - if(len == 1) { - writeByte(dest, readByte(src)); - } - else { - - // calculate address of last byte - len += src - 1; - - /* 1. Appropriately program the EDMAST, EDMAND - and EDMADST register pairs. The EDMAST - registers should point to the first byte to copy - from, the EDMAND registers should point to the - last byte to copy and the EDMADST registers - should point to the first byte in the destination - range. The destination range will always be - linear, never wrapping at any values except from - 8191 to 0 (the 8-Kbyte memory boundary). - Extreme care should be taken when - programming the start and end pointers to - prevent a never ending DMA operation which - would overwrite the entire 8-Kbyte buffer. - */ - writeRegPair(EDMASTL, src); - writeRegPair(EDMADSTL, dest); - - if((src <= RXSTOP_INIT) && (len > RXSTOP_INIT)) - len -= (RXSTOP_INIT - RXSTART_INIT); - writeRegPair(EDMANDL, len); - - /* - 2. If an interrupt at the end of the copy process is - desired, set EIE.DMAIE and EIE.INTIE and - clear EIR.DMAIF. - - 3. Verify that ECON1.CSUMEN is clear. */ - writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_CSUMEN); - - /* 4. Start the DMA copy by setting ECON1.DMAST. */ - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST); - - // wait until runnig DMA is completed - while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST); - } -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::freePacket(void) { - setERXRDPT(); -} - -/** - * @brief - * @note - * @param - * @retval - */ -uint8_t Enc28J60Network::readOp(uint8_t op, uint8_t address) { - uint8_t result; - - _cs = 0; - - // issue read command - _spi.write(op | (address & ADDR_MASK)); - - // read data - result = _spi.write(0x00); - - // do dummy read if needed (for mac and mii, see datasheet page 29) - if(address & 0x80) - result = _spi.write(0x00); - - // release CS - _cs = 1; - return(result); -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) { - _cs = 0; - - // issue write command - _spi.write(op | (address & ADDR_MASK)); - - // write data - _spi.write(data); - _cs = 1; -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) { - _cs = 0; - - // issue read command - _spi.write(ENC28J60_READ_BUF_MEM); - while(len) { - len--; - - // read data - *data = _spi.write(0x00); - data++; - } - - *data = '\0'; - _cs = 1; -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) { - _cs = 0; - - // issue write command - _spi.write(ENC28J60_WRITE_BUF_MEM); - while(len) { - len--; - - // write data - _spi.write(*data); - data++; - } - - _cs = 1; -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::setBank(uint8_t address) { - - // set the bank (if needed) - - if((address & BANK_MASK) != bank) { - - // set the bank - writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1 | ECON1_BSEL0)); - writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK) >> 5); - bank = (address & BANK_MASK); - } -} - -/** - * @brief - * @note - * @param - * @retval - */ -uint8_t Enc28J60Network::readReg(uint8_t address) { - - // set the bank - - setBank(address); - - // do the read - return readOp(ENC28J60_READ_CTRL_REG, address); -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::writeReg(uint8_t address, uint8_t data) { - - // set the bank - - setBank(address); - - // do the write - writeOp(ENC28J60_WRITE_CTRL_REG, address, data); -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) { - - // set the bank - - setBank(address); - - // do the write - writeOp(ENC28J60_WRITE_CTRL_REG, address, (data & 0xFF)); - writeOp(ENC28J60_WRITE_CTRL_REG, address + 1, (data) >> 8); -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::phyWrite(uint8_t address, uint16_t data) { - - // set the PHY register address - - writeReg(MIREGADR, address); - - // write the PHY data - writeRegPair(MIWRL, data); - - // wait until the PHY write completes - while(readReg(MISTAT) & MISTAT_BUSY) { - wait(0.000015); - } -} - -/** - * @brief - * @note - * @param - * @retval - */ -void Enc28J60Network::clkout(uint8_t clk) { - - //setup clkout: 2 is 12.5MHz: - - writeReg(ECOCON, clk & 0x7); -} - -// read the revision of the chip: -uint8_t Enc28J60Network::getRev(void) { - return(readReg(EREVID)); -} - -/** - * @brief - * @note - * @param - * @retval - */ -uint16_t Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) { - uint8_t spdr; - uint16_t t; - uint16_t i; - - len = setReadPtr(handle, pos, len) - 1; - _cs = 0; - - // issue read command - spdr = _spi.write(ENC28J60_READ_BUF_MEM); - for(i = 0; i < len; i += 2) { - - // read data - spdr = _spi.write(0x00); - t = spdr << 8; - spdr = _spi.write(0x00); - t += spdr; - sum += t; - if(sum < t) { - sum++; /* carry */ - } - } - - if(i == len) { - spdr = _spi.write(0x00); - t = (spdr << 8) + 0; - sum += t; - if(sum < t) { - sum++; /* carry */ - } - } - - _cs = 1; - - /* Return sum in host byte order. */ - return sum; -} - +/* + Enc28J60Network.cpp + UIPEthernet network driver for Microchip ENC28J60 Ethernet Interface. + + Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de> + All rights reserved. + + based on enc28j60.c file from the AVRlib library by Pascal Stang. + For AVRlib See http://www.procyonengineering.com/ + + Modified (ported to mbed) by Zoltan Hudak <hudakz@inbox.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "Enc28J60Network.h" +#include <mbed.h> + +extern "C" +{ +#include "enc28j60.h" +#include "uip.h" +} +#ifdef ENC28J60DEBUG + #include "HardwareSerial.h" +#endif +uint16_t Enc28J60Network:: nextPacketPtr; +uint8_t Enc28J60Network:: bank = 0xff; + +struct memblock Enc28J60Network:: receivePkt; + +/** + * @brief + * @note + * @param + * @retval + */ +Enc28J60Network::Enc28J60Network(PinName mosi, PinName miso, PinName sclk, PinName cs) : + MemoryPool(), + _spi(mosi, miso, sclk), + _cs(cs) +{ } + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::init(uint8_t* macaddr) { + MemoryPool::init(); // 1 byte in between RX_STOP_INIT and pool to allow prepending of controlbyte + + // initialize SPI interface + _spi.format(8, 0); // 8bit, mode 0 + _spi.frequency(7000000); // 7MHz + wait(1); // 1 second for stable state + + // initialize I/O + _cs = 1; // ss=0 + + // perform system reset + writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); + wait_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(!(readReg(ESTAT) & ESTAT_CLKRDY)); + // do bank 0 stuff + // initialize receive buffer + // 16-bit transfers, must write low byte first + // set receive buffer start address + nextPacketPtr = RXSTART_INIT; + + // Rx start + writeRegPair(ERXSTL, RXSTART_INIT); + + // set receive pointer address + writeRegPair(ERXRDPTL, RXSTART_INIT); + + // RX end + writeRegPair(ERXNDL, RXSTOP_INIT); + + // TX start + //writeRegPair(ETXSTL, TXSTART_INIT); + // TX end + //writeRegPair(ETXNDL, TXSTOP_INIT); + // 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 + //TODO define specific pattern to receive dhcp-broadcast packages instead of setting ERFCON_BCEN! + writeReg(ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_PMEN | ERXFCON_BCEN); + writeRegPair(EPMM0, 0x303f); + writeRegPair(EPMCSL, 0xf7f9); + + // + // + // do bank 2 stuff + // enable MAC receive + // and bring MAC out of reset (writes 0x00 to MACON2) + writeRegPair(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS); + + // enable automatic padding to 60bytes and CRC operations + writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN); + + // set inter-frame gap (non-back-to-back) + writeRegPair(MAIPGL, 0x0C12); + + // set inter-frame gap (back-to-back) + writeReg(MABBIPG, 0x12); + + // Set the maximum packet size which the controller will accept + // Do not send packets longer than MAX_FRAMELEN: + writeRegPair(MAMXFLL, MAX_FRAMELEN); + + // do bank 3 stuff + // write MAC address + // NOTE: MAC address in ENC28J60 is byte-backward + writeReg(MAADR5, macaddr[0]); + writeReg(MAADR4, macaddr[1]); + writeReg(MAADR3, macaddr[2]); + writeReg(MAADR2, macaddr[3]); + writeReg(MAADR1, macaddr[4]); + writeReg(MAADR0, macaddr[5]); + + // no loopback of transmitted frames + phyWrite(PHCON2, PHCON2_HDLDIS); + + // switch to bank 0 + setBank(ECON1); + + // enable interrutps + writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE | EIE_PKTIE); + + // enable packet reception + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + + clkout(2); // change clkout from 6.25MHz to 12.5MHz + wait_us(60); // 60us + + //Configure leds + phyWrite(PHLCON, 0x476); +} + +/** + * @brief + * @note + * @param + * @retval + */ +memhandle Enc28J60Network::receivePacket(void) { + uint8_t rxstat; + uint16_t len; + // check if a packet has been received and buffered + + //if( !(readReg(EIR) & EIR_PKTIF) ){ + // The above does not work. See Rev. B4 Silicon Errata point 6. + if(readReg(EPKTCNT) != 0) { + uint16_t readPtr = nextPacketPtr + + 6 > RXSTOP_INIT ? nextPacketPtr + + 6 - + RXSTOP_INIT + + RXSTART_INIT : nextPacketPtr + + 6; + // Set the read pointer to the start of the received packet + + writeRegPair(ERDPTL, nextPacketPtr); + + // read the next packet pointer + nextPacketPtr = readOp(ENC28J60_READ_BUF_MEM, 0); + nextPacketPtr |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + + // read the packet length (see datasheet page 43) + len = readOp(ENC28J60_READ_BUF_MEM, 0); + len |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; + len -= 4; //remove the CRC count + + // read the receive status (see datasheet page 43) + rxstat = readOp(ENC28J60_READ_BUF_MEM, 0); + + //rxstat |= readOp(ENC28J60_READ_BUF_MEM, 0) << 8; +#ifdef ENC28J60DEBUG + Serial.print("receivePacket ["); + Serial.print(readPtr, HEX); + Serial.print("-"); + Serial.print((readPtr + len) % (RXSTOP_INIT + 1), HEX); + Serial.print("], next: "); + Serial.print(nextPacketPtr, HEX); + Serial.print(", stat: "); + Serial.print(rxstat, HEX); + Serial.print(", count: "); + Serial.print(readReg(EPKTCNT)); + Serial.print(" -> "); + Serial.println((rxstat & 0x80) != 0 ? "OK" : "failed"); +#endif + // decrement the packet counter indicate we are done with this packet + + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); + + // 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) { + receivePkt.begin = readPtr; + receivePkt.size = len; + return UIP_RECEIVEBUFFERHANDLE; + } + + // Move the RX read pointer to the start of the next received packet + // This frees the memory we just read out + setERXRDPT(); + } + + return(NOBLOCK); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::setERXRDPT(void) { + writeRegPair(ERXRDPTL, nextPacketPtr == RXSTART_INIT ? RXSTOP_INIT : nextPacketPtr - 1); +} + +/** + * @brief + * @note + * @param + * @retval + */ +memaddress Enc28J60Network::blockSize(memhandle handle) { + return handle == NOBLOCK ? 0 : handle == UIP_RECEIVEBUFFERHANDLE ? receivePkt.size : blocks[handle].size; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::sendPacket(memhandle handle) { + memblock* packet = &blocks[handle]; + uint16_t start = packet->begin - 1; + uint16_t end = start + packet->size; + + // backup data at control-byte position + uint8_t data = readByte(start); + // write control-byte (if not 0 anyway) + + if(data) + writeByte(start, 0); + +#ifdef ENC28J60DEBUG + Serial.print("sendPacket("); + Serial.print(handle); + Serial.print(") ["); + Serial.print(start, HEX); + Serial.print("-"); + Serial.print(end, HEX); + Serial.print("]: "); + for(uint16_t i = start; i <= end; i++) { + Serial.print(readByte(i), HEX); + Serial.print(" "); + } + + Serial.println(); +#endif + // TX start + + writeRegPair(ETXSTL, start); + + // Set the TXND pointer to correspond to the packet size given + writeRegPair(ETXNDL, end); + + // send the contents of the transmit buffer onto the network + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); + + // Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. + if((readReg(EIR) & EIR_TXERIF)) { + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); + } + + //restore data on control-byte position + if(data) + writeByte(start, data); +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint16_t Enc28J60Network::setReadPtr(memhandle handle, memaddress position, uint16_t len) { + memblock* packet = handle == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[handle]; + memaddress start = handle == UIP_RECEIVEBUFFERHANDLE + && packet->begin + + position > RXSTOP_INIT ? packet->begin + + position - + RXSTOP_INIT + + RXSTART_INIT : packet->begin + + position; + + writeRegPair(ERDPTL, start); + + if(len > packet->size - position) + len = packet->size - position; + return len; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint16_t Enc28J60Network::readPacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) { + len = setReadPtr(handle, position, len); + readBuffer(len, buffer); + return len; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint16_t Enc28J60Network::writePacket(memhandle handle, memaddress position, uint8_t* buffer, uint16_t len) { + memblock* packet = &blocks[handle]; + uint16_t start = packet->begin + position; + + writeRegPair(EWRPTL, start); + + if(len > packet->size - position) + len = packet->size - position; + writeBuffer(len, buffer); + return len; +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t Enc28J60Network::readByte(uint16_t addr) { + uint8_t result; + + writeRegPair(ERDPTL, addr); + + _cs = 0; + + // issue read command + _spi.write(ENC28J60_READ_BUF_MEM); + + // read data + result = _spi.write(0x00); + _cs = 1; + return(result); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::writeByte(uint16_t addr, uint8_t data) { + writeRegPair(EWRPTL, addr); + + _cs = 0; + + // issue write command + _spi.write(ENC28J60_WRITE_BUF_MEM); + + // write data + _spi.write(data); + _cs = 1; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::copyPacket +( + memhandle dest_pkt, + memaddress dest_pos, + memhandle src_pkt, + memaddress src_pos, + uint16_t len +) { + memblock* dest = &blocks[dest_pkt]; + memblock* src = src_pkt == UIP_RECEIVEBUFFERHANDLE ? &receivePkt : &blocks[src_pkt]; + memaddress start = src_pkt == UIP_RECEIVEBUFFERHANDLE + && src->begin + + src_pos > RXSTOP_INIT ? src->begin + + src_pos - + RXSTOP_INIT + + RXSTART_INIT : src->begin + + src_pos; + enc28J60_mempool_block_move_callback(dest->begin + dest_pos, start, len); + + // Move the RX read pointer to the start of the next received packet + // This frees the memory we just read out + setERXRDPT(); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::freePacket(void) { + setERXRDPT(); +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t Enc28J60Network::readOp(uint8_t op, uint8_t address) { + uint8_t result; + + _cs = 0; + + // issue read command + _spi.write(op | (address & ADDR_MASK)); + + // read data + result = _spi.write(0x00); + + // do dummy read if needed (for mac and mii, see datasheet page 29) + if(address & 0x80) + result = _spi.write(0x00); + + // release CS + _cs = 1; + return(result); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::writeOp(uint8_t op, uint8_t address, uint8_t data) { + _cs = 0; + + // issue write command + _spi.write(op | (address & ADDR_MASK)); + + // write data + _spi.write(data); + _cs = 1; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::readBuffer(uint16_t len, uint8_t* data) { + _cs = 0; + + // issue read command + _spi.write(ENC28J60_READ_BUF_MEM); + while(len) { + len--; + + // read data + *data = _spi.write(0x00); + data++; + } + + *data = '\0'; + _cs = 1; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::writeBuffer(uint16_t len, uint8_t* data) { + _cs = 0; + + // issue write command + _spi.write(ENC28J60_WRITE_BUF_MEM); + while(len) { + len--; + + // write data + _spi.write(*data); + data++; + } + + _cs = 1; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::setBank(uint8_t address) { + + // set the bank (if needed) + + if((address & BANK_MASK) != bank) { + + // set the bank + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1 | ECON1_BSEL0)); + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK) >> 5); + bank = (address & BANK_MASK); + } +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint8_t Enc28J60Network::readReg(uint8_t address) { + + // set the bank + + setBank(address); + + // do the read + return readOp(ENC28J60_READ_CTRL_REG, address); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::writeReg(uint8_t address, uint8_t data) { + + // set the bank + + setBank(address); + + // do the write + writeOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::writeRegPair(uint8_t address, uint16_t data) { + + // set the bank + + setBank(address); + + // do the write + writeOp(ENC28J60_WRITE_CTRL_REG, address, (data & 0xFF)); + writeOp(ENC28J60_WRITE_CTRL_REG, address + 1, (data) >> 8); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::phyWrite(uint8_t address, uint16_t data) { + + // set the PHY register address + + writeReg(MIREGADR, address); + + // write the PHY data + writeRegPair(MIWRL, data); + + // wait until the PHY write completes + while(readReg(MISTAT) & MISTAT_BUSY) { + wait_us(15); + } +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint16_t Enc28J60Network::phyRead(uint8_t address) { + writeReg(MIREGADR, address); + writeReg(MICMD, MICMD_MIIRD); + + // wait until the PHY read completes + while(readReg(MISTAT) & MISTAT_BUSY) { + wait_us(15); + } //and MIRDH + + writeReg(MICMD, 0); + return(readReg(MIRDL) | readReg(MIRDH) << 8); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::clkout(uint8_t clk) { + + //setup clkout: 2 is 12.5MHz: + + writeReg(ECOCON, clk & 0x7); +} + +// read the revision of the chip: +uint8_t Enc28J60Network::getrev(void) { + return(readReg(EREVID)); +} + +/** + * @brief + * @note + * @param + * @retval + */ +uint16_t Enc28J60Network::chksum(uint16_t sum, memhandle handle, memaddress pos, uint16_t len) { + uint8_t spdr; + uint16_t t; + uint16_t i; + + len = setReadPtr(handle, pos, len) - 1; + _cs = 0; + + // issue read command + spdr = _spi.write(ENC28J60_READ_BUF_MEM); + for(i = 0; i < len; i += 2) { + + // read data + spdr = _spi.write(0x00); + t = spdr << 8; + spdr = _spi.write(0x00); + t += spdr; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + } + + if(i == len) { + spdr = _spi.write(0x00); + t = (spdr << 8) + 0; + sum += t; + if(sum < t) { + sum++; /* carry */ + } + } + + _cs = 1; + + /* Return sum in host byte order. */ + return sum; +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::powerOff(void) { + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); + wait_ms(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); + wait_ms(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); +} + +/** + * @brief + * @note + * @param + * @retval + */ +void Enc28J60Network::powerOn(void) { + writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); + wait_ms(50); + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + wait_ms(50); +} + +/** + * @brief + * @note + * @param + * @retval + */ +bool Enc28J60Network::linkStatus(void) { + return(phyRead(PHSTAT2) & 0x0400) > 0; +}