Webserver+3d print

Dependents:   Nucleo

cyclone_tcp/drivers/enc28j60.c

Committer:
Sergunb
Date:
2017-02-04
Revision:
0:8918a71cdbe9

File content as of revision 0:8918a71cdbe9:

/**
 * @file enc28j60.c
 * @brief ENC28J60 Ethernet controller
 *
 * @section License
 *
 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved.
 *
 * This file is part of CycloneTCP Open.
 *
 * 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 2
 * 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, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.7.6
 **/

//Switch to the appropriate trace level
#define TRACE_LEVEL NIC_TRACE_LEVEL

//Dependencies
#include <limits.h>
#include "core/net.h"
#include "drivers/enc28j60.h"
#include "debug.h"


/**
 * @brief ENC28J60 driver
 **/

const NicDriver enc28j60Driver =
{
   NIC_TYPE_ETHERNET,
   ETH_MTU,
   enc28j60Init,
   enc28j60Tick,
   enc28j60EnableIrq,
   enc28j60DisableIrq,
   enc28j60EventHandler,
   enc28j60SendPacket,
   enc28j60SetMulticastFilter,
   NULL,
   NULL,
   NULL,
   TRUE,
   TRUE,
   TRUE,
   FALSE
};


/**
 * @brief ENC28J60 controller initialization
 * @param[in] interface Underlying network interface
 * @return Error code
 **/

error_t enc28j60Init(NetInterface *interface)
{
   uint8_t revisionId;
   Enc28j60Context *context;

   //Debug message
   TRACE_INFO("Initializing ENC28J60 Ethernet controller...\r\n");

   //Initialize SPI
   interface->spiDriver->init();
   //Initialize external interrupt line
   interface->extIntDriver->init();

   //Issue a system reset
   enc28j60SoftReset(interface);

   //After issuing the reset command, wait at least 1ms in firmware
   //for the device to be ready
   sleep(10);

   //Point to the driver context
   context = (Enc28j60Context *) interface->nicContext;

   //Initialize driver specific variables
   context->currentBank = UINT16_MAX;
   context->nextPacket = ENC28J60_RX_BUFFER_START;

   //Allocate RX buffer
   context->rxBuffer = memPoolAlloc(ETH_MAX_FRAME_SIZE);
   //Failed to allocate memory?
   if(context->rxBuffer == NULL)
      return ERROR_OUT_OF_MEMORY;

   //Read silicon revision ID
   revisionId = enc28j60ReadReg(interface, ENC28J60_REG_EREVID);

   //Debug message
   TRACE_INFO("ENC28J60 revision ID: 0x%02X\r\n", revisionId);

   //Disable CLKOUT output
   enc28j60WriteReg(interface, ENC28J60_REG_ECOCON, 0x00);

   //Set the MAC address
   enc28j60WriteReg(interface, ENC28J60_REG_MAADR1, interface->macAddr.b[0]);
   enc28j60WriteReg(interface, ENC28J60_REG_MAADR2, interface->macAddr.b[1]);
   enc28j60WriteReg(interface, ENC28J60_REG_MAADR3, interface->macAddr.b[2]);
   enc28j60WriteReg(interface, ENC28J60_REG_MAADR4, interface->macAddr.b[3]);
   enc28j60WriteReg(interface, ENC28J60_REG_MAADR5, interface->macAddr.b[4]);
   enc28j60WriteReg(interface, ENC28J60_REG_MAADR6, interface->macAddr.b[5]);

   //Set receive buffer location
   enc28j60WriteReg(interface, ENC28J60_REG_ERXSTL, LSB(ENC28J60_RX_BUFFER_START));
   enc28j60WriteReg(interface, ENC28J60_REG_ERXSTH, MSB(ENC28J60_RX_BUFFER_START));
   enc28j60WriteReg(interface, ENC28J60_REG_ERXNDL, LSB(ENC28J60_RX_BUFFER_STOP));
   enc28j60WriteReg(interface, ENC28J60_REG_ERXNDH, MSB(ENC28J60_RX_BUFFER_STOP));

   //The ERXRDPT register defines a location within the FIFO
   //where the receive hardware is forbidden to write to
   enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTL, LSB(ENC28J60_RX_BUFFER_STOP));
   enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTH, MSB(ENC28J60_RX_BUFFER_STOP));

   //Configure the receive filters
   enc28j60WriteReg(interface, ENC28J60_REG_ERXFCON, ERXFCON_UCEN |
      ERXFCON_CRCEN | ERXFCON_HTEN | ERXFCON_BCEN);

   //Initialize the hash table
   enc28j60WriteReg(interface, ENC28J60_REG_EHT0, 0x00);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT1, 0x00);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT2, 0x00);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT3, 0x00);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT4, 0x00);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT5, 0x00);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT6, 0x00);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT7, 0x00);

   //Pull the MAC out of reset
   enc28j60WriteReg(interface, ENC28J60_REG_MACON2, 0x00);

   //Enable the MAC to receive frames
   enc28j60WriteReg(interface, ENC28J60_REG_MACON1,
      MACON1_TXPAUS | MACON1_RXPAUS | MACON1_MARXEN);

   //Enable automatic padding to at least 60 bytes, always append a valid CRC
   //and check frame length. MAC can operate in half-duplex or full-duplex mode
#if (ENC28J60_FULL_DUPLEX_SUPPORT == ENABLED)
   enc28j60WriteReg(interface, ENC28J60_REG_MACON3, MACON3_PADCFG(1) |
      MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX);
#else
   enc28j60WriteReg(interface, ENC28J60_REG_MACON3, MACON3_PADCFG(1) |
      MACON3_TXCRCEN | MACON3_FRMLNEN);
#endif

   //When the medium is occupied, the MAC will wait indefinitely for it to
   //become free when attempting to transmit
   enc28j60WriteReg(interface, ENC28J60_REG_MACON4, MACON4_DEFER);

   //Maximum frame length that can be received or transmitted (1518 bytes)
   enc28j60WriteReg(interface, ENC28J60_REG_MAMXFLL, LSB(1518));
   enc28j60WriteReg(interface, ENC28J60_REG_MAMXFLH, MSB(1518));

   //Configure the back-to-back inter-packet gap register
#if (ENC28J60_FULL_DUPLEX_SUPPORT == ENABLED)
   enc28j60WriteReg(interface, ENC28J60_REG_MABBIPG, 0x15);
#else
   enc28j60WriteReg(interface, ENC28J60_REG_MABBIPG, 0x12);
#endif

   //Configure the non-back-to-back inter-packet gap register
   enc28j60WriteReg(interface, ENC28J60_REG_MAIPGL, 0x12);
   enc28j60WriteReg(interface, ENC28J60_REG_MAIPGH, 0x0C);

   //Collision window register
   enc28j60WriteReg(interface, ENC28J60_REG_MACLCON2, 63);

   //Set the PHY to the proper duplex mode
#if (ENC28J60_FULL_DUPLEX_SUPPORT == ENABLED)
   enc28j60WritePhyReg(interface, ENC28J60_PHY_REG_PHCON1, PHCON1_PDPXMD);
#else
   enc28j60WritePhyReg(interface, ENC28J60_PHY_REG_PHCON1, 0x0000);
#endif

   //Disable half-duplex loopback in PHY
   enc28j60WritePhyReg(interface, ENC28J60_PHY_REG_PHCON2, PHCON2_HDLDIS);

   //LEDA displays link status and LEDB displays TX/RX activity
   enc28j60WritePhyReg(interface, ENC28J60_PHY_REG_PHLCON,
      PHLCON_LACFG(4) | PHLCON_LBCFG(7) | PHLCON_LFRQ(0) | PHLCON_STRCH);

   //Clear interrupt flags
   enc28j60WriteReg(interface, ENC28J60_REG_EIR, 0x00);

   //Configure interrupts as desired
   enc28j60WriteReg(interface, ENC28J60_REG_EIE, EIE_INTIE |
      EIE_PKTIE | EIE_LINKIE | EIE_TXIE | EIE_TXERIE);

   //Configure PHY interrupts as desired
   enc28j60WritePhyReg(interface, ENC28J60_PHY_REG_PHIE,
      PHIE_PLNKIE | PHIE_PGEIE);

   //Set RXEN to enable reception
   enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_RXEN);

   //Dump registers for debugging purpose
   enc28j60DumpReg(interface);
   enc28j60DumpPhyReg(interface);

   //Accept any packets from the upper layer
   osSetEvent(&interface->nicTxEvent);

   //Force the TCP/IP stack to poll the link state at startup
   interface->nicEvent = TRUE;
   //Notify the TCP/IP stack of the event
   osSetEvent(&netEvent);

   //Successful initialization
   return NO_ERROR;
}


/**
 * @brief ENC28J60 timer handler
 * @param[in] interface Underlying network interface
 **/

void enc28j60Tick(NetInterface *interface)
{
}


/**
 * @brief Enable interrupts
 * @param[in] interface Underlying network interface
 **/

void enc28j60EnableIrq(NetInterface *interface)
{
   //Enable interrupts
   interface->extIntDriver->enableIrq();
}


/**
 * @brief Disable interrupts
 * @param[in] interface Underlying network interface
 **/

void enc28j60DisableIrq(NetInterface *interface)
{
   //Disable interrupts
   interface->extIntDriver->disableIrq();
}


/**
 * @brief ENC28J60 interrupt service routine
 * @param[in] interface Underlying network interface
 * @return TRUE if a higher priority task must be woken. Else FALSE is returned
 **/

bool_t enc28j60IrqHandler(NetInterface *interface)
{
   bool_t flag;
   uint8_t status;

   //This flag will be set if a higher priority task must be woken
   flag = FALSE;

   //Clear the INTIE bit, immediately after an interrupt event
   enc28j60ClearBit(interface, ENC28J60_REG_EIE, EIE_INTIE);

   //Read interrupt status register
   status = enc28j60ReadReg(interface, ENC28J60_REG_EIR);

   //Link status change?
   if(status & EIR_LINKIF)
   {
      //Disable LINKIE interrupt
      enc28j60ClearBit(interface, ENC28J60_REG_EIE, EIE_LINKIE);

      //Set event flag
      interface->nicEvent = TRUE;
      //Notify the TCP/IP stack of the event
      flag |= osSetEventFromIsr(&netEvent);
   }

   //Packet received?
   if(status & EIR_PKTIF)
   {
      //Disable PKTIE interrupt
      enc28j60ClearBit(interface, ENC28J60_REG_EIE, EIE_PKTIE);

      //Set event flag
      interface->nicEvent = TRUE;
      //Notify the TCP/IP stack of the event
      flag |= osSetEventFromIsr(&netEvent);
   }

   //Packet transmission complete?
   if(status & (EIR_TXIF | EIE_TXERIE))
   {
      //Clear interrupt flags
      enc28j60ClearBit(interface, ENC28J60_REG_EIR, EIR_TXIF | EIE_TXERIE);

      //Notify the TCP/IP stack that the transmitter is ready to send
      flag |= osSetEventFromIsr(&interface->nicTxEvent);
   }

   //Once the interrupt has been serviced, the INTIE bit
   //is set again to re-enable interrupts
   enc28j60SetBit(interface, ENC28J60_REG_EIE, EIE_INTIE);

   //A higher priority task must be woken?
   return flag;
}


/**
 * @brief ENC28J60 event handler
 * @param[in] interface Underlying network interface
 **/

void enc28j60EventHandler(NetInterface *interface)
{
   error_t error;
   uint16_t status;
   uint16_t value;

   //Read interrupt status register
   status = enc28j60ReadReg(interface, ENC28J60_REG_EIR);

   //Check whether the link state has changed
   if(status & EIR_LINKIF)
   {
      //Clear PHY interrupts flags
      enc28j60ReadPhyReg(interface, ENC28J60_PHY_REG_PHIR);
      //Clear interrupt flag
      enc28j60ClearBit(interface, ENC28J60_REG_EIR, EIR_LINKIF);
      //Read PHY status register
      value = enc28j60ReadPhyReg(interface, ENC28J60_PHY_REG_PHSTAT2);

      //Check link state
      if(value & PHSTAT2_LSTAT)
      {
         //Link speed
         interface->linkSpeed = NIC_LINK_SPEED_10MBPS;

#if (ENC28J60_FULL_DUPLEX_SUPPORT == ENABLED)
         //Full-duplex mode
         interface->duplexMode = NIC_FULL_DUPLEX_MODE;
#else
         //Half-duplex mode
         interface->duplexMode = NIC_HALF_DUPLEX_MODE;
#endif
         //Link is up
         interface->linkState = TRUE;
      }
      else
      {
         //Link is down
         interface->linkState = FALSE;
      }

      //Process link state change event
      nicNotifyLinkChange(interface);
   }

   //Check whether a packet has been received?
   if(status & EIR_PKTIF)
   {
      //Clear interrupt flag
      enc28j60ClearBit(interface, ENC28J60_REG_EIR, EIR_PKTIF);

      //Process all pending packets
      do
      {
         //Read incoming packet
         error = enc28j60ReceivePacket(interface);

         //No more data in the receive buffer?
      } while(error != ERROR_BUFFER_EMPTY);
   }

   //Re-enable LINKIE and PKTIE interrupts
   enc28j60SetBit(interface, ENC28J60_REG_EIE, EIE_LINKIE | EIE_PKTIE);
}


/**
 * @brief Send a packet
 * @param[in] interface Underlying network interface
 * @param[in] buffer Multi-part buffer containing the data to send
 * @param[in] offset Offset to the first data byte
 * @return Error code
 **/

error_t enc28j60SendPacket(NetInterface *interface,
   const NetBuffer *buffer, size_t offset)
{
   size_t length;

   //Retrieve the length of the packet
   length = netBufferGetLength(buffer) - offset;

   //Check the frame length
   if(length > 1536)
   {
      //The transmitter can accept another packet
      osSetEvent(&interface->nicTxEvent);
      //Report an error
      return ERROR_INVALID_LENGTH;
   }

   //Make sure the link is up before transmitting the frame
   if(!interface->linkState)
   {
      //The transmitter can accept another packet
      osSetEventFromIsr(&interface->nicTxEvent);
      //Drop current packet
      return NO_ERROR;
   }

   //It is recommended to reset the transmit logic before
   //attempting to transmit a packet
   enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_TXRST);
   enc28j60ClearBit(interface, ENC28J60_REG_ECON1, ECON1_TXRST);

   //Interrupt flags should be cleared after the reset is completed
   enc28j60ClearBit(interface, ENC28J60_REG_EIR, EIR_TXIF | EIR_TXERIF);

   //Set transmit buffer location
   enc28j60WriteReg(interface, ENC28J60_REG_ETXSTL, LSB(ENC28J60_TX_BUFFER_START));
   enc28j60WriteReg(interface, ENC28J60_REG_ETXSTH, MSB(ENC28J60_TX_BUFFER_START));

   //Point to start of transmit buffer
   enc28j60WriteReg(interface, ENC28J60_REG_EWRPTL, LSB(ENC28J60_TX_BUFFER_START));
   enc28j60WriteReg(interface, ENC28J60_REG_EWRPTH, MSB(ENC28J60_TX_BUFFER_START));

   //Copy the data to the transmit buffer
   enc28j60WriteBuffer(interface, buffer, offset);

   //ETXND should point to the last byte in the data payload
   enc28j60WriteReg(interface, ENC28J60_REG_ETXNDL, LSB(ENC28J60_TX_BUFFER_START + length));
   enc28j60WriteReg(interface, ENC28J60_REG_ETXNDH, MSB(ENC28J60_TX_BUFFER_START + length));

   //Start transmission
   enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_TXRTS);

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief Receive a packet
 * @param[in] interface Underlying network interface
 * @return Error code
 **/

error_t enc28j60ReceivePacket(NetInterface *interface)
{
   error_t error;
   uint16_t n;
   uint16_t status;
   Enc28j60Context *context;

   //Point to the driver context
   context = (Enc28j60Context *) interface->nicContext;

   //Any packet pending in the receive buffer?
   if(enc28j60ReadReg(interface, ENC28J60_REG_EPKTCNT))
   {
      //Point to the start of the received packet
      enc28j60WriteReg(interface, ENC28J60_REG_ERDPTL, LSB(context->nextPacket));
      enc28j60WriteReg(interface, ENC28J60_REG_ERDPTH, MSB(context->nextPacket));

      //Read the first two bytes, which are the address of the next packet
      enc28j60ReadBuffer(interface, (uint8_t *) &context->nextPacket, sizeof(uint16_t));
      //Get the length of the received frame in bytes
      enc28j60ReadBuffer(interface, (uint8_t *) &n, sizeof(uint16_t));
      //Read the receive status vector (RSV)
      enc28j60ReadBuffer(interface, (uint8_t *) &status, sizeof(uint16_t));

      //Make sure no error occurred
      if(status & RSV_RECEIVED_OK)
      {
         //Limit the number of data to read
         n = MIN(n, ETH_MAX_FRAME_SIZE);
         //Read the Ethernet frame
         enc28j60ReadBuffer(interface, context->rxBuffer, n);
         //Valid packet received
         error = NO_ERROR;
      }
      else
      {
         //The received packet contains an error
         error = ERROR_INVALID_PACKET;
      }

      //Advance the ERXRDPT pointer, taking care to wrap back at the
      //end of the received memory buffer
      if(context->nextPacket == ENC28J60_RX_BUFFER_START)
      {
         enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTL, LSB(ENC28J60_RX_BUFFER_STOP));
         enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTH, MSB(ENC28J60_RX_BUFFER_STOP));
      }
      else
      {
         enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTL, LSB(context->nextPacket - 1));
         enc28j60WriteReg(interface, ENC28J60_REG_ERXRDPTH, MSB(context->nextPacket - 1));
      }

      //Decrement the packet counter
      enc28j60SetBit(interface, ENC28J60_REG_ECON2, ECON2_PKTDEC);
   }
   else
   {
      //No more data in the receive buffer
      error = ERROR_BUFFER_EMPTY;
   }

   //Check whether a valid packet has been received
   if(!error)
   {
      //Pass the packet to the upper layer
      nicProcessPacket(interface, context->rxBuffer, n);
   }

   //Return status code
   return error;
}


/**
 * @brief Configure multicast MAC address filtering
 * @param[in] interface Underlying network interface
 * @return Error code
 **/

error_t enc28j60SetMulticastFilter(NetInterface *interface)
{
   uint_t i;
   uint_t k;
   uint32_t crc;
   uint8_t hashTable[8];
   MacFilterEntry *entry;

   //Debug message
   TRACE_DEBUG("Updating ENC28J60 hash table...\r\n");

   //Clear hash table
   memset(hashTable, 0, sizeof(hashTable));

   //The MAC filter table contains the multicast MAC addresses
   //to accept when receiving an Ethernet frame
   for(i = 0; i < MAC_MULTICAST_FILTER_SIZE; i++)
   {
      //Point to the current entry
      entry = &interface->macMulticastFilter[i];

      //Valid entry?
      if(entry->refCount > 0)
      {
         //Compute CRC over the current MAC address
         crc = enc28j60CalcCrc(&entry->addr, sizeof(MacAddr));
         //Calculate the corresponding index in the table
         k = (crc >> 23) & 0x3F;
         //Update hash table contents
         hashTable[k / 8] |= (1 << (k % 8));
      }
   }

   //Write the hash table to the ENC28J60 controller
   enc28j60WriteReg(interface, ENC28J60_REG_EHT0, hashTable[0]);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT1, hashTable[1]);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT2, hashTable[2]);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT3, hashTable[3]);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT4, hashTable[4]);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT5, hashTable[5]);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT6, hashTable[6]);
   enc28j60WriteReg(interface, ENC28J60_REG_EHT7, hashTable[7]);

   //Debug message
   TRACE_DEBUG("  EHT0 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT0));
   TRACE_DEBUG("  EHT1 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT1));
   TRACE_DEBUG("  EHT2 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT2));
   TRACE_DEBUG("  EHT3 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT3));
   TRACE_DEBUG("  EHT0 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT4));
   TRACE_DEBUG("  EHT1 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT5));
   TRACE_DEBUG("  EHT2 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT6));
   TRACE_DEBUG("  EHT3 = %02" PRIX8 "\r\n", enc28j60ReadReg(interface, ENC28J60_REG_EHT7));

   //Successful processing
   return NO_ERROR;
}


/**
 * @brief ENC28J60 controller reset
 * @param[in] interface Underlying network interface
 **/

void enc28j60SoftReset(NetInterface *interface)
{
   //Pull the CS pin low
   interface->spiDriver->assertCs();

   //Write opcode
   interface->spiDriver->transfer(ENC28J60_CMD_SRC);

   //Terminate the operation by raising the CS pin
   interface->spiDriver->deassertCs();
}


/**
 * @brief Bank selection
 * @param[in] interface Underlying network interface
 * @param[in] address Register address
 **/

void enc28j60SelectBank(NetInterface *interface, uint16_t address)
{
   uint16_t bank;
   Enc28j60Context *context;

   //Point to the driver context
   context = (Enc28j60Context *) interface->nicContext;

   //Get the bank number from the specified address
   bank = address & REG_BANK_MASK;

   //Rewrite the bank number only if a change is detected
   if(bank != context->currentBank)
   {
      //Select specified bank
      switch(bank)
      {
      case BANK_0:
         //Select bank 0
         enc28j60ClearBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL1 | ECON1_BSEL0);
         break;
      case BANK_1:
         //Select bank 1
         enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL0);
         enc28j60ClearBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL1);
         break;
      case BANK_2:
         //Select bank 2
         enc28j60ClearBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL0);
         enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL1);
         break;
      case BANK_3:
         //Select bank 3
         enc28j60SetBit(interface, ENC28J60_REG_ECON1, ECON1_BSEL1 | ECON1_BSEL0);
         break;
      default:
         //Invalid bank
         break;
      }

      //Save bank number
      context->currentBank = bank;
   }
}


/**
 * @brief Write ENC28J60 register
 * @param[in] interface Underlying network interface
 * @param[in] address Register address
 * @param[in] data Register value
 **/

void enc28j60WriteReg(NetInterface *interface, uint16_t address, uint8_t data)
{
   //Make sure the corresponding bank is selected
   enc28j60SelectBank(interface, address);

   //Pull the CS pin low
   interface->spiDriver->assertCs();

   //Write opcode and register address
   interface->spiDriver->transfer(ENC28J60_CMD_WCR | (address & REG_ADDR_MASK));
   //Write register value
   interface->spiDriver->transfer(data);

   //Terminate the operation by raising the CS pin
   interface->spiDriver->deassertCs();
}


/**
 * @brief Read ENC28J60 register
 * @param[in] interface Underlying network interface
 * @param[in] address Register address
 * @return Register value
 **/

uint8_t enc28j60ReadReg(NetInterface *interface, uint16_t address)
{
   uint16_t data;

   //Make sure the corresponding bank is selected
   enc28j60SelectBank(interface, address);

   //Pull the CS pin low
   interface->spiDriver->assertCs();

   //Write opcode and register address
   interface->spiDriver->transfer(ENC28J60_CMD_RCR | (address & REG_ADDR_MASK));

   //When reading MAC or MII registers, a dummy byte is first shifted out
   if((address & REG_TYPE_MASK) != ETH_REG_TYPE)
      interface->spiDriver->transfer(0x00);

   //Read register contents
   data = interface->spiDriver->transfer(0x00);

   //Terminate the operation by raising the CS pin
   interface->spiDriver->deassertCs();

   //Return register contents
   return data;
}


/**
 * @brief Write PHY register
 * @param[in] interface Underlying network interface
 * @param[in] address PHY register address
 * @param[in] data Register value
 **/

void enc28j60WritePhyReg(NetInterface *interface, uint16_t address, uint16_t data)
{
   //Write register address
   enc28j60WriteReg(interface, ENC28J60_REG_MIREGADR, address & REG_ADDR_MASK);

   //Write the lower 8 bits
   enc28j60WriteReg(interface, ENC28J60_REG_MIWRL, LSB(data));
   //Write the upper 8 bits
   enc28j60WriteReg(interface, ENC28J60_REG_MIWRH, MSB(data));

   //Wait until the PHY register has been written
   while(enc28j60ReadReg(interface, ENC28J60_REG_MISTAT) & MISTAT_BUSY);
}


/**
 * @brief Read PHY register
 * @param[in] interface Underlying network interface
 * @param[in] address PHY register address
 * @return Register value
 **/

uint16_t enc28j60ReadPhyReg(NetInterface *interface, uint16_t address)
{
   uint16_t data;

   //Write register address
   enc28j60WriteReg(interface, ENC28J60_REG_MIREGADR, address & REG_ADDR_MASK);

   //Start read operation
   enc28j60WriteReg(interface, ENC28J60_REG_MICMD, MICMD_MIIRD);
   //Wait for the read operation to complete
   while(enc28j60ReadReg(interface, ENC28J60_REG_MISTAT) & MISTAT_BUSY);
   //Clear command register
   enc28j60WriteReg(interface, ENC28J60_REG_MICMD, 0);

   //Read the lower 8 bits
   data = enc28j60ReadReg(interface, ENC28J60_REG_MIRDL);
   //Read the upper 8 bits
   data |= enc28j60ReadReg(interface, ENC28J60_REG_MIRDH) << 8;

   //Return register contents
   return data;
}


/**
 * @brief Write SRAM buffer
 * @param[in] interface Underlying network interface
 * @param[in] buffer Multi-part buffer containing the data to be written
 * @param[in] offset Offset to the first data byte
 **/

void enc28j60WriteBuffer(NetInterface *interface,
   const NetBuffer *buffer, size_t offset)
{
   uint_t i;
   size_t j;
   size_t n;
   uint8_t *p;

   //Pull the CS pin low
   interface->spiDriver->assertCs();

   //Write opcode
   interface->spiDriver->transfer(ENC28J60_CMD_WBM);
   //Write per-packet control byte
   interface->spiDriver->transfer(0x00);

   //Loop through data chunks
   for(i = 0; i < buffer->chunkCount; i++)
   {
      //Is there any data to copy from the current chunk?
      if(offset < buffer->chunk[i].length)
      {
         //Point to the first byte to be read
         p = (uint8_t *) buffer->chunk[i].address + offset;
         //Compute the number of bytes to copy at a time
         n = buffer->chunk[i].length - offset;

         //Copy data to SRAM buffer
         for(j = 0; j < n; j++)
            interface->spiDriver->transfer(p[j]);

         //Process the next block from the start
         offset = 0;
      }
      else
      {
         //Skip the current chunk
         offset -= buffer->chunk[i].length;
      }
   }

   //Terminate the operation by raising the CS pin
   interface->spiDriver->deassertCs();
}


/**
 * @brief Read SRAM buffer
 * @param[in] interface Underlying network interface
 * @param[in] data Buffer where to store the incoming data
 * @param[in] length Number of data to read
 **/

void enc28j60ReadBuffer(NetInterface *interface,
   uint8_t *data, size_t length)
{
   size_t i;

   //Pull the CS pin low
   interface->spiDriver->assertCs();

   //Write opcode
   interface->spiDriver->transfer(ENC28J60_CMD_RBM);

   //Copy data from SRAM buffer
   for(i = 0; i < length; i++)
      data[i] = interface->spiDriver->transfer(0x00);

   //Terminate the operation by raising the CS pin
   interface->spiDriver->deassertCs();
}


/**
 * @brief Set bit field
 * @param[in] interface Underlying network interface
 * @param[in] address Register address
 * @param[in] mask Bits to set in the target register
 **/

void enc28j60SetBit(NetInterface *interface, uint16_t address, uint16_t mask)
{
   //Pull the CS pin low
   interface->spiDriver->assertCs();

   //Write opcode and register address
   interface->spiDriver->transfer(ENC28J60_CMD_BFS | (address & REG_ADDR_MASK));
   //Write bit mask
   interface->spiDriver->transfer(mask);

   //Terminate the operation by raising the CS pin
   interface->spiDriver->deassertCs();
}


/**
 * @brief Clear bit field
 * @param[in] interface Underlying network interface
 * @param[in] address Register address
 * @param[in] mask Bits to clear in the target register
 **/

void enc28j60ClearBit(NetInterface *interface, uint16_t address, uint16_t mask)
{
   //Pull the CS pin low
   interface->spiDriver->assertCs();

   //Write opcode and register address
   interface->spiDriver->transfer(ENC28J60_CMD_BFC | (address & REG_ADDR_MASK));
   //Write bit mask
   interface->spiDriver->transfer(mask);

   //Terminate the operation by raising the CS pin
   interface->spiDriver->deassertCs();
}


/**
 * @brief CRC calculation using the polynomial 0x4C11DB7
 * @param[in] data Pointer to the data over which to calculate the CRC
 * @param[in] length Number of bytes to process
 * @return Resulting CRC value
 **/

uint32_t enc28j60CalcCrc(const void *data, size_t length)
{
   uint_t i;
   uint_t j;

   //Point to the data over which to calculate the CRC
   const uint8_t *p = (uint8_t *) data;
   //CRC preset value
   uint32_t crc = 0xFFFFFFFF;

   //Loop through data
   for(i = 0; i < length; i++)
   {
      //The message is processed bit by bit
      for(j = 0; j < 8; j++)
      {
         //Update CRC value
         if(((crc >> 31) ^ (p[i] >> j)) & 0x01)
            crc = (crc << 1) ^ 0x04C11DB7;
         else
            crc = crc << 1;
      }
   }

   //Return CRC value
   return crc;
}


/**
 * @brief Dump registers for debugging purpose
 * @param[in] interface Underlying network interface
 **/

void enc28j60DumpReg(NetInterface *interface)
{
#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
   uint8_t i;
   uint8_t bank;
   uint16_t address;

   //Display header
   TRACE_DEBUG("    Bank 0  Bank 1  Bank 2  Bank 3\r\n");

   //Loop through register addresses
   for(i = 0; i < 32; i++)
   {
      //Display register address
      TRACE_DEBUG("%02" PRIX8 ": ", i);

      //Loop through bank numbers
      for(bank = 0; bank < 4; bank++)
      {
         //Format register address
         address = (bank << 8) | i;

         //MAC and MII registers require a specific read sequence
         if(address >= 0x200 && address <= 0x219)
            address |= MAC_REG_TYPE;
         else if(address >= 0x300 && address <= 0x305)
            address |= MAC_REG_TYPE;
         else if(address == 0x30A)
            address |= MAC_REG_TYPE;

         //Display register contents
         TRACE_DEBUG("0x%02" PRIX8 "    ", enc28j60ReadReg(interface, address));
      }

      //Jump to the following line
      TRACE_DEBUG("\r\n");
   }

   //Terminate with a line feed
   TRACE_DEBUG("\r\n");
#endif
}


/**
 * @brief Dump PHY registers for debugging purpose
 * @param[in] interface Underlying network interface
 **/

void enc28j60DumpPhyReg(NetInterface *interface)
{
#if (TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
   uint8_t i;

   //Loop through PHY registers
   for(i = 0; i < 32; i++)
   {
      //Display current PHY register
      TRACE_DEBUG("%02" PRIX8 ": 0x%04" PRIX16 "\r\n", i, enc28j60ReadPhyReg(interface, i));
   }

   //Terminate with a line feed
   TRACE_DEBUG("\r\n");
#endif
}