/*
 * Copyright 2015 Mario Richter, Peter Stephan
 * This file is part of NeMo.
 *
 * NeMo 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.
 *
 * NeMo 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 NeMo.  If not, see <http://www.gnu.org/licenses/>.
 */

/*!
 * \file enc28j60.c
 * \ingroup	ethernet
 * \brief	Treiber-Routinen für den Ethernet-Chip ENC28J60 und Ansteuerung der SPI-Schnittstelle
 */

/*!
 * \defgroup ethernet Treiber für ENC28J60
 * \ingroup ethernet
 * {
 * 		\defgroup spi Ansteuerung der SPI-Schnittstelle
 *    \defgroup eth_debug Debug-Funktionen
 * }
 */

#include "main.h"

volatile uint8_t  enc28CurrentBank;					      //!< Enthält die aktuell eingestellte Registerbank
uint8_t           enc28RevisionId;								//!< Revision ID des ENC28J60
uint16_t          enc28NextPacketPointer;					//!< Adresse des nächsten Packets im Buffer des ENC28J60
uint8_t           myMacAddr[6];                   //!< Eigene MAC-Adresse
uint8_t EEMEM     eeMacAddr[6];                   //!< Eigene MAC-Adresse (EEPROM)



/* %%%%%%%%%%%%%%%%%%%% ENC28J60 Interface %%%%%%%%%%%%%%%%%%%% */

/*!
 * \ingroup ethernet
 * \brief SPI und ENC28J60 initialisieren
 * 
 * Diese Funktion konfiguriert die SPI-Schnittstelle und führt einen Reset mit anschließender
 * Initialisierung des ENC28J60 durch. 
 */

void enc28Init(void)
{	
	uint16_t  tmp;
	uint8_t   i = 0;
  
  //if(debugLevel & DEBUGLVL_ENC_INFO)
  usart_send_line_P(PSTR("Init Enc28J60..."));
	
	spiInit();																		          // SPI initialisieren
	enc28SoftwareReset(); 												          // ENC28J60 resetten

  /* Lese MAC-Adresse aus dem EEPROM */
  for(i = 0; i < 6; i++)
  {
    myMacAddr[i] = eeprom_read_byte(&eeMacAddr[i]);       // Byte holen
  }

  // Sind alle Bytes gleichzeitig 0 oder 255, dann ist was faul ...
  if(((myMacAddr[0] == 255) && (myMacAddr[1] == 255) && (myMacAddr[2] == 255) &&
      (myMacAddr[3] == 255) && (myMacAddr[4] == 255) && (myMacAddr[5] == 255)) ||
     ((myMacAddr[0] == 0)   && (myMacAddr[1] == 0)   && (myMacAddr[2] == 0) &&
      (myMacAddr[3] == 0)   && (myMacAddr[4] == 0)   && (myMacAddr[5] == 0)))
  {
    myMacAddr[0] = ENC_MAC_ADDRESS1;                      // Standard-MAC-Adresse verwenden
    myMacAddr[1] = ENC_MAC_ADDRESS2;
    myMacAddr[2] = ENC_MAC_ADDRESS3;
    myMacAddr[3] = ENC_MAC_ADDRESS4;
    myMacAddr[4] = ENC_MAC_ADDRESS5;
    myMacAddr[5] = ENC_MAC_ADDRESS6;
    usart_send_line("Use default MAC!");
    eeprom_write_block(myMacAddr, eeMacAddr, 6);          // Standard-MAC ins EEPROM speichern
  }
  
  // Nach dem Reset mind. 1ms warten (siehe Errata ENC28J60)
  _delay_ms(1.0);
	
	// Revision ID des ENC28J60 auslesen und ausgeben
	enc28RevisionId = enc28ReadControlReg(ENC_ADR_EREVID);
	//if(debugLevel & DEBUGLVL_ENC_INFO)
  {
    usart_send_string_P(PSTR("ENC28J60 Rev ID: 0x"));
    usart_send_hexnumber(enc28RevisionId, 1);
    usart_send_line("");
  }

	// Adressen der Sende- und Empfangsbuffer festlegen
	enc28WriteControlReg(ENC_ADR_ERXSTH, (uint8_t)((ENC_MEM_RX_START & 0x1F00) >> 8));
	enc28WriteControlReg(ENC_ADR_ERXSTL, (uint8_t) (ENC_MEM_RX_START & 0x00FF));
	enc28WriteControlReg(ENC_ADR_ERXNDH, (uint8_t)((ENC_MEM_RX_END & 0x1F00) >> 8));
	enc28WriteControlReg(ENC_ADR_ERXNDL, (uint8_t) (ENC_MEM_RX_END & 0x00FF));
	enc28WriteControlReg(ENC_ADR_ETXSTH, (uint8_t)((ENC_MEM_TX_START & 0x1F00) >> 8));
	enc28WriteControlReg(ENC_ADR_ETXSTL, (uint8_t) (ENC_MEM_TX_START & 0x00FF));
	enc28WriteControlReg(ENC_ADR_ETXNDH, (uint8_t)((ENC_MEM_TX_END & 0x1F00) >> 8));
	enc28WriteControlReg(ENC_ADR_ETXNDL, (uint8_t) (ENC_MEM_TX_END & 0x00FF));
	
	enc28NextPacketPointer = ENC_MEM_RX_START;
	
	// Warte bis interner Oszillator des ENC28J60 stabil läuft
	while(!(enc28ReadControlReg(ENC_ADR_ESTAT) & (1 << ENC_BIT_CLKRDY)));
	
	// MAC Receiver enablen, Pause Control Bits freigeben
	enc28WriteControlReg(ENC_ADR_MACON1, (1 << ENC_BIT_MARXEN) | (1 << ENC_BIT_RXPAUS) | (1 << ENC_BIT_TXPAUS));
	
	// Padding, CRC, Framelängenüberprüfung und Betriebsmodus setzen
#ifdef ENC_FULL_DUPLEX
	enc28WriteControlReg(ENC_ADR_MACON3, 
			(1 << ENC_BIT_PADCFG0) | (1 << ENC_BIT_TXCRCEN) | (1 << ENC_BIT_FRMLNEN) | (1 << ENC_BIT_FULDPLX));
#else
	enc28WriteControlReg(ENC_ADR_MACON3, 
			(1 << ENC_BIT_PADCFG0) | (1 << ENC_BIT_TXCRCEN) | (1 << ENC_BIT_FRMLNEN));
#endif

	// Defer Transmission Enable Bit (nötig für IEEE 802.3 Kompatibilität)
	enc28WriteControlReg(ENC_ADR_MACON4, (1 << ENC_BIT_DEFER));
	
	// Maximale Framelänge
	enc28WriteControlReg(ENC_ADR_MAMXFLH, (uint8_t)((ENC_MAX_FRAMELEN & 0xFF00) >> 8));
	enc28WriteControlReg(ENC_ADR_MAMXFLL, (uint8_t) (ENC_MAX_FRAMELEN & 0x00FF));

	// Back-to-Back Inter-Packet Gap
#ifdef ENC_FULL_DUPLEX
	enc28WriteControlReg(ENC_ADR_MABBIPG, 0x15);
#else
	enc28WriteControlReg(ENC_ADR_MABBIPG, 0x12);
#endif

	// Non Back-to-Back Inter-Packet Gap
	enc28WriteControlReg(ENC_ADR_MAIPGL, 0x12);

#ifndef ENC_FULL_DUPLEX
	enc28WriteControlReg(ENC_ADR_MAIPGH, 0x0C);
#endif

	// Eigene MAC-Adresse
	enc28WriteControlReg(ENC_ADR_MAADR6, myMacAddr[5]);
	enc28WriteControlReg(ENC_ADR_MAADR5, myMacAddr[4]);
	enc28WriteControlReg(ENC_ADR_MAADR4, myMacAddr[3]);
	enc28WriteControlReg(ENC_ADR_MAADR3, myMacAddr[2]);
	enc28WriteControlReg(ENC_ADR_MAADR2, myMacAddr[1]);
	enc28WriteControlReg(ENC_ADR_MAADR1, myMacAddr[0]);
	
#ifdef ENC_FULL_DUPLEX
	// Full Duplex aktivieren
	enc28WritePhyReg(ENC_ADR_PHCON1, (0x0000 | (1 << ENC_BIT_PDPXMD)));
#else
	// Half Duplex aktivieren
	enc28WritePhyReg(ENC_ADR_PHCON1, 0x0000);
	
	// Loopback für Half-Duplex deaktivieren (Pakete nur senden und nicht zurück an MAC spiegeln)
	enc28WritePhyReg(ENC_ADR_PHCON2, (0x0000 | (1 << ENC_BIT_HDLDIS)));
#endif

	// LEDs konfigurieren: LEDA (gelb) RX/TX, LEDB (grün) Link, kurze Stretch-Zeit
	enc28WritePhyReg(ENC_ADR_PHLCON, (0x0000 | 
			(7 << ENC_BIT_LACFG0) | (4 << ENC_BIT_LBCFG0) | (1 << ENC_BIT_STRCH)));
	
	// Empfangsinterrupt bei neuen Packets konfigurieren
	enc28BitFieldSet(ENC_ADR_EIE, (1 << ENC_BIT_INTIE) | (1 << ENC_BIT_PKTIE));
	
	// Empfang freigeben
	enc28BitFieldSet(ENC_ADR_ECON1, (1 << ENC_BIT_RXEN));
	
	// PHY Identifier holen und ausgeben
	if(debugLevel & DEBUGLVL_ENC_INFO)
  {
    tmp = enc28ReadPhyReg(ENC_ADR_PHID2);
    usart_send_string_P(PSTR("PHY P/N: 0x"));
    usart_send_hexnumber((uint8_t)((tmp & 0x03F0) >> 4), 1);
    usart_send_string_P(PSTR("  PHY Rev: 0x"));
    usart_send_hexnumber((uint8_t)(tmp & 0x000F), 1);
    usart_send_string_P(PSTR("  PHY ID: 0x"));
    usart_send_hexnumber((uint8_t)((tmp & 0xFC00) >> 10), 1);
    
    tmp = enc28ReadPhyReg(ENC_ADR_PHID1);
    usart_send_hexnumber((uint8_t)((tmp & 0xFF00) >> 8), 1);
    usart_send_hexnumber((uint8_t)(tmp & 0x00FF), 1);

    usart_send_line("");
  }
}


/*!
 * \ingroup ethernet
 * \brief Abholen eines Packets aus dem Buffer des ENC28J60
 * 
 * Diese Funktion holt ein neues Packet aus dem Buffer des ENC28J60 ab und legt es in den
 * internen Buffer. Außerdem wird das Packet als ausgelesen markiert und der Packet Counter
 * des ENC28J60 erniedrigt, so dass der Buffer frei für neue Packets wird.
 * 
 * \param buf Zeiger auf den Packet-Buffer, in das das Packet abgelegt wird.
 * \return Länge des gelesenen Packets in Bytes.
 * \sa enc28SendPacket
 */

uint16_t enc28GetPacket(uint8_t * buf)
{
	uint8_t packetCount;
  uint8_t statusInfo[6];
  uint16_t length, status;
  
  // Prüfe sicherheitshalber den Packet Counter, ob überhaupt Packets verfügbar sind
  packetCount = enc28ReadControlReg(ENC_ADR_EPKTCNT);  
  if(debugLevel & DEBUGLVL_ENC_INFO)
  {
    usart_send_string_P(PSTR("Packet Count: 0x"));
    usart_send_hexnumber(packetCount, 1);
    usart_send_line("");
  }
  
  // Keine Packets vorhanden -> gleich zurück, es gibt nichts zu tun
  if(packetCount == 0)
  {
    if(debugLevel & DEBUGLVL_ENC_ERROR)
    {
      usart_send_line_P(PSTR("ENC28J60 Error: No Packets!"));
    }
    return 0;
  }
  
  // Adresse des Lesezeigers für nächstes Packet setzen
	enc28WriteControlReg(ENC_ADR_ERDPTL, (uint8_t) (enc28NextPacketPointer & 0x00FF));
	enc28WriteControlReg(ENC_ADR_ERDPTH, (uint8_t)((enc28NextPacketPointer & 0x1F00) >> 8));
  
  // Statusinfo und Header auslesen (erste 6 Bytes) und auspacken
  
  enc28ReadBufferMem(statusInfo, 6);  
  enc28NextPacketPointer  = statusInfo[0];
  enc28NextPacketPointer |= ((uint16_t)statusInfo[1]) << 8;
  length  = statusInfo[2];
  length |= ((uint16_t)statusInfo[3]) << 8;
  status  = statusInfo[4];
  status |= ((uint16_t)statusInfo[5]) << 8;
  
  if(debugLevel & DEBUGLVL_ENC_INFO)
  {
    usart_send_string_P(PSTR("Length: 0x"));
    usart_send_hexnumber(statusInfo[3], 1);
    usart_send_hexnumber(statusInfo[2], 1);
    usart_send_string_P(PSTR("  Status: 0x"));
    usart_send_hexnumber(statusInfo[5], 1);
    usart_send_hexnumber(statusInfo[4], 1);  
    usart_wait_send();  
    usart_send_string_P(PSTR("  Next Packet: 0x"));
    usart_send_hexnumber((uint8_t)((enc28NextPacketPointer & 0x1F00) >> 8), 1);
    usart_send_hexnumber((uint8_t)(enc28NextPacketPointer & 0x00FF), 1);
    usart_send_line("");
  }
  
  // Wenn Packet länger als interner Buffer ist, einfach abschneiden
  if(length > PACKET_BUF_SIZE)
    length = PACKET_BUF_SIZE;
  
  // Packet in den internen Buffer holen
  enc28ReadBufferMem(pack.buf, length);
  
  // ERXRD-Pointer weiterziehen, Buffer für neues Packet freigeben
  // ACHTUNG: Laut Errata des ENC28J60 muss ERXRDPT immer mit UNGERADER Adresse programmiert werden.
  // Die folgende Logik ist aus dem Errata-Sheet übernommen und stellt dies sicher, indem ERXRDPT
  // immer auf die Adresse des nächsten Packets MINUS 1 programmiert wird (die Adresse ist immer
  // gerade, da der ENC immer auf gerade Adressen aufrundet). Beginnt es bei der Startadresse des
  // Buffers, wird ERXRDPT auf das Bufferende programmiert.  
  if(enc28NextPacketPointer == ENC_MEM_RX_START)
	{
		enc28WriteControlReg(ENC_ADR_ERXRDPTL, (uint8_t) (ENC_MEM_RX_END & 0x00FF));
		enc28WriteControlReg(ENC_ADR_ERXRDPTH, (uint8_t)((ENC_MEM_RX_END & 0x1F00) >> 8));
	} 
  else
  {
		enc28WriteControlReg( ENC_ADR_ERXRDPTL, (uint8_t)((enc28NextPacketPointer-1) & 0xFF) );
		enc28WriteControlReg( ENC_ADR_ERXRDPTH, (uint8_t)(((enc28NextPacketPointer-1) & 0xFF00) >> 8));
	}
  
  // Falls Debuglevel aktiv, gesamtes Packet auf USART ausgeben
  if(debugLevel & DEBUGLVL_ENC_PACKET)
    printMem(pack.buf, length);
  
  // Dekrementiere den Packet Counter
  enc28BitFieldSet(ENC_ADR_ECON2, (1 << ENC_BIT_PKTDEC));
  return length;
}


/*!
 * \ingroup ethernet
 * \brief Senden eines Packets (Ablage in Buffer des ENC28J60)
 *
 * Diese Funktion sendet ein Packet aus dem internen Buffer. Das Packet wird zunächst
 * an den ENC28J60 übertragen, anschließend wird der Sendevorgang ausgelöst.
 *
 * \param buf Zeiger auf den Packet-Buffer, in dem das zu sendende Packet abgelegt ist.
 * \param length Länge des zu sendenden Packets in Byte.
 * \sa enc28GetPacket
 */

void enc28SendPacket(uint8_t * buf, uint16_t length)
{
  uint8_t waitTxRdy = 50;             // 50 * 200µs = 10ms maximale Wartezeit
  uint8_t perPacketCtrl = 0;          // Per Packet Control Byte

  // Warten, bis evtl. vorher gesendetes Packet fertig übertragen und der Transmitter bereit ist
  while((enc28ReadControlReg(ENC_ADR_ECON1) & (1 << ENC_BIT_TXRTS)) && (waitTxRdy > 0))
  {
    _delay_us(200.0);
    waitTxRdy--;
  }

  // Ist der Transmitter noch immer nicht bereit, dann führen wir einen Reset durch.
  // Siehe Errata zum ENC28J60: Transmitlogik kann sich u.U. aufhängen -> Reset notwendig.
  if(enc28ReadControlReg(ENC_ADR_ECON1) & (1 << ENC_BIT_TXRTS))
  {
    if(debugLevel & DEBUGLVL_ENC_INFO)
    {
      usart_send_line_P(PSTR("ENC28 TX Reset"));
    }

    enc28BitFieldSet(ENC_ADR_ECON1, (1 << ENC_BIT_TXRST));       // Reset auslösen
    enc28BitFieldClear(ENC_ADR_ECON1, (1 << ENC_BIT_TXRST));
  }

  // Startadresse für Packet programmieren
  enc28WriteControlReg(ENC_ADR_ETXSTL, (uint8_t)((ENC_MEM_TX_START & 0x00FF)));
  enc28WriteControlReg(ENC_ADR_ETXSTH, (uint8_t)((ENC_MEM_TX_START & 0xFF00) >> 8));

  // Endadresse für Packet programmieren
  enc28WriteControlReg(ENC_ADR_ETXNDL, (uint8_t)(((ENC_MEM_TX_START + length) & 0x00FF)));
  enc28WriteControlReg(ENC_ADR_ETXNDH, (uint8_t)(((ENC_MEM_TX_START + length) & 0xFF00) >> 8));

  // Schreibzeiger programmieren
  enc28WriteControlReg(ENC_ADR_EWRPTL, (uint8_t)((ENC_MEM_TX_START & 0x00FF)));
  enc28WriteControlReg(ENC_ADR_EWRPTH, (uint8_t)((ENC_MEM_TX_START & 0xFF00) >> 8));

  // Per Packet Control Byte schreiben (LSB = 0 -> globale Einstellungen verwenden)
  enc28WriteBufferMem(&perPacketCtrl, 1);

  // Packet übertragen
  enc28WriteBufferMem(buf, length);

  // Errata: TXIF könnte durch Reset erneut gesetzt worden sein, deshalb nochmal löschen.
  enc28BitFieldClear(ENC_ADR_EIR, (1 << ENC_BIT_TXERIF));

  // Neues Packet senden
  enc28BitFieldSet(ENC_ADR_ECON1, (1 << ENC_BIT_TXRTS));
}


/* %%%%%%%%%%%%%%%%%% ENC28J60 Kommunikation %%%%%%%%%%%%%%%%%% */

/*!
 * \ingroup ethernet
 * \brief Daten aus dem Puffer des ENC28J60 lesen
 * 
 * Mit dieser Funktion werden Daten aus dem Empfangs- oder Sendebuffer des ENC28J60 gelesen.
 * Die Funktion liest die angegebene Anzahl Bytes aus dem Buffer und schreibt diese an die
 * angegebene Adresse. Hierzu sollte vorher das AUTOINC-Bit im ECON2-Register des ENC28J60
 * gesetzt worden sein, damit die Adressen im ENC28J60 automatisch inkrementiert werden.
 * 
 * \param buf Adresse des Buffers, in den die gelesenen Daten geschrieben werden
 * \param len Anzahl der zu lesenden Bytes
 * 
 * \sa enc28WriteBufferMem
 */

void enc28ReadBufferMem(uint8_t * buf, uint16_t len)
{
	if((len == 0) || (buf == 0))								// Sind übergebene Daten unsinnig?
		return;
	
	ENC_CS_ACTIVE();														// Chip Select freigeben
	
	spiWrite(ENC_SPI_RBM | 0x1A);								// Op-Code plus 0x1A zum Lesen des Buffer Memory
	
	while(len > 0)
	{
		spiWrite(0x00);														// Dummy Write zum Auslesen der Daten
		*buf = spiRead();													// Gelesene Daten in Buffer ablegen
		buf++;																		// Buffer weiterziehen
		len--;																		// Anzahl zu lesender Bytes dekrementieren
	}
	
	ENC_CS_INACTIVE();													// Chip Select wegnehmen
}


/*!
 * \ingroup ethernet
 * \brief Daten in den Puffer des ENC28J60 schreiben
 * 
 * Mit dieser Funktion werden Daten in dem Empfangs- oder Sendebuffer des ENC28J60 geschrieben.
 * Die Funktion schreibt die angegebene Anzahl Bytes in den Buffer. Hierzu sollte vorher das
 * AUTOINC-Bit im ECON2-Register des ENC28J60 gesetzt worden sein, damit die Adressen im
 * ENC28J60 automatisch inkrementiert werden.
 * 
 * \param buf Adresse des Buffers, der die zu schreibenden Daten enthält
 * \param len Anzahl der zu schreibenden Bytes
 * 
 * \sa enc28ReadBufferMem
 */

void enc28WriteBufferMem(uint8_t * buf, uint16_t len)
{
	if((len == 0) || (buf == 0))								// Sind übergebene Daten unsinnig?
		return;
	
	ENC_CS_ACTIVE();														// Chip Select freigeben
	
	spiWrite(ENC_SPI_WBM | 0x1A);								// Op-Code plus 0x1A zum Schreiben des Buffer Memory
	
	while(len > 0)
	{
		spiWrite(*buf);														// Daten übergeben
		buf++;																		// Buffer weiterziehen
		len--;																		// Anzahl zu schreibender Bytes dekrementieren
	}
	
	ENC_CS_INACTIVE();													// Chip Select wegnehmen
}


/*!
 * \ingroup ethernet
 * \brief Auslesen eines ENC28J60-Kontrollregisters
 * 
 * Ãœber diese Funktion kann ein beliebiges ETH-, MAC- oder MII-Register des ENC28J60 in
 * beliebiger Reihenfolge ausgelesen werden. Die Bankadresse des Registers ist in den zwei 
 * höchstwertigen Bits der Adresse enthalten. Diese wird vor dem Auslesen ggf. umgeschaltet, falls
 * aktuell nicht die korrekte Bank selektiert ist. Das dritthöchstwertige Bit der Adresse ist bei
 * MAC- und MII-Registern gesetzt, um anzuzeigen, dass hier ein zusätzlicher SPI-Lesezyklus
 * notwendig ist.
 * 
 * \param adr Die zu lesende Adresse. Die zwei höchstwertigen Bits bilden hierbei die Bankadresse,
 * das folgende Bit die Kennzeichnung von MAC- und MII-Registern, und die fünf niederwertigsten
 * Bits geben die Registeradresse innerhalb der Bank an (siehe Datenblatt, Kap. 3.1).
 * \return Der zurückgelesene Wert des Registers.
 * 
 * \sa enc28WriteControlReg
 */

uint8_t enc28ReadControlReg(uint8_t adr)
{
	uint8_t regAddr;
	
	enc28SetBank(adr);													// Einstellen der korrekten Registerbank	
	regAddr = adr & ENC_REG_ADDR_MASK;					// Registeradresse ausmaskieren	
	
	ENC_CS_ACTIVE();														// Chip Select freigeben
	
	spiWrite(ENC_SPI_RCR | regAddr);						// Befehl und Registeradresse übermitteln
	spiWrite(0x00);															// Dummy-Write zum Auslesen der Antwort (ETH-Register)
	
	if(adr & ENC_MAC_MII_MASK)									// Zusätzlicher Dummy-Write für MAC- und MII-Register
		spiWrite(0x00);
	
	ENC_CS_INACTIVE();													// Chip Select entfernen
	
	return spiRead();
}


/*!
 * \ingroup ethernet
 * \brief Schreiben eines ENC28J60-Kontrollregisters
 * 
 * Ãœber diese Funktion kann ein beliebiges ETH-, MAC- oder MII-Register des ENC28J60 in
 * beliebiger Reihenfolge geschrieben werden. Die Bankadresse des Registers ist in den zwei 
 * höchstwertigen Bits der Adresse enthalten. Diese wird vor dem Auslesen ggf. umgeschaltet, falls
 * aktuell nicht die korrekte Bank selektiert ist.
 * 
 * \param adr Die zu beschreibende Adresse. Die zwei höchstwertigen Bits bilden hierbei die Bankadresse,
 * das folgende Bit die Kennzeichnung von MAC- und MII-Registern, und die fünf niederwertigsten
 * Bits geben die Registeradresse innerhalb der Bank an (siehe Datenblatt, Kap. 3.1).
 * \param data Die zu schreibenden Daten.
 * 
 * \sa enc28ReadControlReg
 */

void enc28WriteControlReg(uint8_t adr, uint8_t data)
{
	uint8_t regAddr;
	
	enc28SetBank(adr);													// Einstellen der korrekten Registerbank	
	regAddr = adr & ENC_REG_ADDR_MASK;					// Registeradresse ausmaskieren
	
	ENC_CS_ACTIVE();														// Chip Select freigeben
	
	spiWrite(ENC_SPI_WCR | regAddr);						// Befehl und Registeradresse übermitteln
	spiWrite(data);															// Zu schreibende Daten übergeben
	
	ENC_CS_INACTIVE();													// Chip Select wegnehmen
}


/*!
 * \ingroup ethernet
 * \brief Auslesen eines ENC28J60-PHY-Registers
 * 
 * Ãœber diese Funktion kann ein PHY-Register des ENC28J60 ausgelesen werden. PHY-Register können
 * nicht direkt gelesen werden, sondern sie werden über ein MII (Media Independent Interface)
 * kontrolliert. Diesem wird zunächst die Adresse übergeben, anschließend wird das Auslesen
 * gestartet. Nach Beendigung können die Daten abgeholt werden und werden zurückgegeben. 
 * 
 * \param adr Die zu lesende Adresse des PHY-Registers.
 * \return Der zurückgelesene Wert des PHY-Registers.
 * 
 * \sa enc28WritePhyReg
 */

uint16_t enc28ReadPhyReg(uint8_t adr)
{
	uint16_t phyData;
	
	// Adresse des zu lesenden PHY-Registers ins Adressregister schreiben
	enc28WriteControlReg(ENC_ADR_MIREGADR, adr);
	
	// MIIRD-Bit zum Auslesen des PHY-Registers setzen und Wartezeit abwarten
	enc28BitFieldSet(ENC_ADR_MICMD, (1 << ENC_BIT_MIIRD));	
	_delay_us(10.3);
	
	// BUSY-Flag pollen und warten, bis Operation beendet
	while(enc28ReadControlReg(ENC_ADR_MISTAT) & (1 << ENC_BIT_BUSY));
	
	// MIIRD-Bit löschen
	enc28BitFieldClear(ENC_ADR_MICMD, (1 << ENC_BIT_MIIRD));
	
	// 2 Bytes Daten aus den Registern holen und Antwort zusammenbauen
	phyData  = (uint16_t)enc28ReadControlReg(ENC_ADR_MIRDH) << 8;
	phyData |= (uint16_t)enc28ReadControlReg(ENC_ADR_MIRDL);
	
	// Antwort zurückgeben
	return phyData;
}


/*!
 * \ingroup ethernet
 * \brief Beschreiben eines ENC28J60-PHY-Registers
 * 
 * Ãœber diese Funktion kann ein PHY-Register des ENC28J60 beschrieben werden. PHY-Register können
 * nicht direkt geschrieben werden, sondern sie werden über ein MII (Media Independent Interface)
 * kontrolliert. Diesem werden zunächst die Adresse und die zu schreibenden Daten übergeben,
 * anschließend wird das Schreiben gestartet. Es wird anschließend auf die Beendigung des 
 * Schreibvorgangs gewartet (11Âµs Delay + Pollen BUSY-Bit), um Kollisionen mit nachfolgenden
 * Operationen zu vermeiden. 
 * 
 * \param adr Die zu beschreibende Adresse des PHY-Registers.
 * \param phyData Der ins PHY-Register zu schreibende Wert.
 * 
 * \sa enc28ReadPhyReg
 */

void enc28WritePhyReg(uint8_t adr, uint16_t phyData)
{
	// Adresse des zu lesenden PHY-Registers ins Adressregister schreiben
	enc28WriteControlReg(ENC_ADR_MIREGADR, adr);
	
	// Untere 8 Bit Daten ins MIWRL-Register ablegen
	enc28WriteControlReg(ENC_ADR_MIWRL, (uint8_t)(phyData & 0x00FF));
	
	// Obere 8 Bit Daten ins MIWRH-Register ablegen. Dadurch wird Schreibvorgang getriggert.
	enc28WriteControlReg(ENC_ADR_MIWRH, (uint8_t)((phyData & 0xFF00) >> 8));
	_delay_us(10.3);
	
	// BUSY-Flag pollen und warten, bis Operation beendet
	while(enc28ReadControlReg(ENC_ADR_MISTAT) & (1 << ENC_BIT_BUSY));
}


/*!
 * \ingroup ethernet
 * \brief Setzen von Bits eines Kontrollregisters
 * 
 * Ãœber diese Funktion können bis zu 8 Bits gleichzeitig in einem beliebigen ETH-Register gesetzt werden.
 * Die Funktion setzt die in der Maske gewählten Bits im Register der angegebenen Adresse.
 * 
 * \param adr Die Adresse des Registers, in dem Bits gesetzt werden sollen.
 * \param mask Die Maske zum Setzen der Bits (1 = Bit setzen, 0 = keine Veränderung).
 * 
 * \sa enc28BitFieldClear
 */

void enc28BitFieldSet(uint8_t adr, uint8_t mask)
{
	uint8_t regAddr;
	
	enc28SetBank(adr);													// Einstellen der korrekten Registerbank	
	regAddr = adr & ENC_REG_ADDR_MASK;					// Registeradresse ausmaskieren
	
	ENC_CS_ACTIVE();														// Chip Select freigeben
	
	spiWrite(ENC_SPI_BFS | regAddr);						// Befehl und Registeradresse übermitteln
	spiWrite(mask);															// Maske übermitteln
	
	ENC_CS_INACTIVE();													// Chip Select wegnehmen
}


/*!
 * \ingroup ethernet
 * \brief Löschen von Bits eines Kontrollregisters
 * 
 * Ãœber diese Funktion können bis zu 8 Bits gleichzeitig in einem beliebigen ETH-Register gelöscht werden.
 * Die Funktion löscht die in der Maske gewählten Bits im Register der angegebenen Adresse.
 * 
 * \param adr Die Adresse des Registers, in dem Bits gelöscht werden sollen.
 * \param mask Die Maske zum Löschen der Bits (1 = Bit löschen, 0 = keine Veränderung).
 * 
 * \sa enc28BitFieldSet
 */

void enc28BitFieldClear(uint8_t adr, uint8_t mask)
{
	uint8_t regAddr;
	
	enc28SetBank(adr);													// Einstellen der korrekten Registerbank	
	regAddr = adr & ENC_REG_ADDR_MASK;					// Registeradresse ausmaskieren
	
	ENC_CS_ACTIVE();														// Chip Select freigeben
	
	spiWrite(ENC_SPI_BFC | regAddr);						// Befehl und Registeradresse übermitteln
	spiWrite(mask);															// Maske übermitteln
	
	ENC_CS_INACTIVE();													// Chip Select wegnehmen
}


/*!
 * \ingroup ethernet
 * \brief Software-Reset des ENC28J60 durchführen
 * 
 * Diese Funktion führt einen Software-Reset des ENC28J60 durch. Hierbei werden alle Register auf die
 * Standard-Werte zurückgesetzt, der Bufferspeicher behält jedoch seine Daten. Nach einem Reset sollten
 * alle PHY-Register für 50Âµs nicht beschrieben werden. 
*/

void enc28SoftwareReset(void)
{
	ENC_CS_ACTIVE();													// Chip Select freigeben
	
	spiWrite(ENC_SPI_SRC | 0x1F);							// Op-Code plus 0x1F für System Reset Command
	
	ENC_CS_INACTIVE();												// Chip Select wegnehmen
}


/*!
 * \ingroup ethernet
 * \brief Ãœberprüfen und Wechseln der Registerbank
 * 
 * Diese Funktion prüft und wechselt ggf. die Registerbank, falls das Register, dessen Adresse
 * übergeben wurde, nicht auf der aktuell ausgewählten Bank liegt. 
 * 
 * \param adr Die Adresse, deren zugehörige Bank ausgewählt werden soll.
 * 
 * \sa enc28ReadControlReg, enc28WriteControlReg, enc28BitFieldSet, enc28BitFieldClear
 */

void enc28SetBank(uint8_t adr)
{
	uint8_t bankAddr;
	
	if((adr & ENC_REG_ADDR_MASK) < 0x1A)																			// für Adressen >= 0x1A keine Bankumschaltung notwendig
	{
		bankAddr = (adr & ENC_BANK_ADDR_MASK) >> ENC_BANK_ADDR_SHIFT;						// Bankadresse des Registers ausmaskieren
		
		if(bankAddr != enc28CurrentBank)																				// Bankumschaltung notwendig?
		{
			enc28BitFieldClear(ENC_ADR_ECON1, ENC_ECON1_BSEL_MASK);								// Bank Select-Bits erst mal löschen
			
			if(bankAddr != 0)
			{
				enc28BitFieldSet(ENC_ADR_ECON1, bankAddr & ENC_ECON1_BSEL_MASK); 		// Notwendige Bits zur Bankumschaltung setzen
			}
			
			enc28CurrentBank = bankAddr;																					// Aktuelle Bank merken
		}
	}
}

/* %%%%%%%%%%%%%%%%%%%%%% SPI Funktionen %%%%%%%%%%%%%%%%%%%%%% */

/*!
 * \ingroup spi
 * \brief Initialisierung der SPI-Schnittstelle
 * 
 * Diese Funktion initialisiert die SPI-Schnittstelle zur Kommunikation mit dem ENC28J60
 * Ethernet-Chip. Hierbei werden MOSI, SCK und CS als Ausgänge definiert, MISO als Eingang.
 * Der Chip Select wird deaktiviert (high). Anschließend wird die SPI-Schnittstelle 
 * für den Master-Mode mit halber Systemfrequenz (SPI2X = 1) konfiguriert. CPOL und CPHA sind 0,
 * d.h. es wird bei steigender Clock-Flanke gesampelt und bei fallender das nächste Bit angelegt.
 * Im Ruhezustand ist der Clock low. 
 * 
 * \sa spiWrite, spiRead
 */

void spiInit(void)
{
	// MOSI, SCK und CS als Ausgänge definieren
	ENC_DDR |= (1 << ENC_PIN_SCK) | (1 << ENC_PIN_SI) | (1 << ENC_PIN_CS);
	
	// CS löschen (ENC28J60 nicht selektiert)
	ENC_CS_INACTIVE();
	
	// MISO als Eingang definieren
	ENC_DDR &= ~(1 << ENC_PIN_SO);	
	
	// SPI konfigurieren: Master, f = fosc/2, CPOL = CPHA = 0
	SPCR = (1 << SPE) | (1 << MSTR);
	SPSR = (1 << SPI2X);
}


/*!
 * \ingroup spi
 * \brief Senden eines Bytes über die SPI-Schnittstelle
 * 
 * Diese Funktion sendet ein Byte über die SPI-Schnittstelle und wartet anschließend auf das
 * Ende der Ãœbertragung. Der Chip-Select wird nicht beeinflusst und muss extern entsprechend
 * gesetzt werden.
 * 
 * \param data Die zu sendenden Daten (1 Byte)
 * 
 * \sa spiInit, spiRead
 */

void spiWrite(uint8_t data)
{
	// Zu sendende Daten an SPI übergeben
	SPDR = data;
	
	// Auf Ende der Ãœbertragung warten
	while(!(SPSR & (1 << SPIF)));
}


/*!
 * \ingroup spi
 * \brief Auslesen des Datenregisters der SPI
 * 
 * Diese Funktion liest das Datenregister der SPI-Schnittstelle aus und gibt die Daten zurück.
 * Sollen aktiv Daten über die SPI gelesen werden, muss vorher ein Dummy-Transfer über einen 
 * Aufruf von \ref spiWrite durchgeführt werden. Die Funktion selbst sendet keine Daten über
 * die Schnittstelle.
 * 
 * \return Die Daten aus dem Empfangsregister (1 Byte)
 * 
 * \sa spiInit, spiWrite
 */

uint8_t spiRead(void)
{
	// SPI-Datenregister zurückgeben
	return (uint8_t)SPDR;
}


/* %%%%%%%%%%%%%%%%%%%%%% Debug-Funktionen %%%%%%%%%%%%%%%%%%%%%% */

/*!
 * \ingroup enc_debug
 * \brief Ausgabe eines Speicherinhalts
 * 
 * Diese Funktion gibt den Speicherinhalt an der angegebenen Adresse und Länge auf der USART aus.
 * 
 * \param buf Zeiger auf den auszugebenden Buffer.
 * \param len Anzahl der auszugebenden Bytes.
 */

void printMem(uint8_t * buf, uint16_t len)
{
  uint8_t i;
  uint16_t cnt = 0;
  
  while(cnt < len)
  {
    for(i = 0; (i < 8) && (cnt < len); i++)
    {
      usart_send_hexnumber(buf[cnt++], 1);
      usart_send_hexnumber(buf[cnt++], 1);
      usart_send_char(' ');
    }
    
    usart_send_char('\r');
    usart_send_char('\n');    
    usart_wait_send();    
  }  
}
