/* ARP vrstva komunikacniho modulu pro Ethernet */

#include "stdint.h"
#include "ARP.h"
#include "net.h"
#include "eth.h"
#include "ip.h"
#include "enc28J60.h"

extern unsigned char my_MAC[MAC_ADR_SIZE];					// MAC adresa HTTP serveru
extern unsigned char my_IP[IP_ADR_SIZE];					// IP adresa HTTP serveru

uint8_t Local_ARP_cache[MAX_ARP_CACHE][IP_ADR_SIZE+MAC_ADR_SIZE];	// Lokalni ARP cache (IP->MAC), nejpre IP pak MAC
uint32_t Timeout_ARP_cache[MAX_ARP_CACHE];							// Casove znacky kdy byl zaznam do cache pridan

/* --------------------------------------------------------------
   Vyplni ARP hlavicku
 ------------------------------------------------------------- */
void ARP_Set_ARP_Header(uint8_t *buffer, uint16_t OPcode, uint8_t *Source_MAC, uint8_t *Source_IP, 
						uint8_t *Dest_MAC, uint8_t *Dest_IP)
{
	uint8_t i;
	buffer[OFFSET_ARP_Htype+0] = 0x00;							// OxOOO1 - Ethernet
	buffer[OFFSET_ARP_Htype+1] = 0x01;
	buffer[OFFSET_ARP_Ptype+0] = 0x08;							// 0x0800 - IPv4
	buffer[OFFSET_ARP_Ptype+1] = 0x00;
	buffer[OFFSET_ARP_Hlen] = 6;								// pocet bytu MAC adresy
	buffer[OFFSET_ARP_Plen] = 4;								// pocet bytu IP adresy
	buffer[OFFSET_ARP_OPcode+0] = (0xFF00&OPcode)>>8;			// OP kod ARP
	buffer[OFFSET_ARP_OPcode+1] = (0x00FF&OPcode);
	for(i=0;i<MAC_ADR_SIZE;i++)
	{
		buffer[OFFSET_ARP_SourceMAC+i] = Source_MAC[i];			// Zdrojova MAC adresa
		buffer[OFFSET_ARP_DestMAC+i] = Dest_MAC[i];				// Cilova MAC adresa
	}
	for(i=0;i<IP_ADR_SIZE;i++)
	{
		buffer[OFFSET_ARP_SourceIP+i] = Source_IP[i];			// Zdrojova IP adresa
		buffer[OFFSET_ARP_DestIP+i] = Dest_IP[i];				// cilova IP adresa
	}
}

/* --------------------------------------------------------------
 Odesle ARP odpoved na dotaz "WHO IS xxx.xxx.xxx.xxx"
 ------------------------------------------------------------- */
void ARP_Send_ARP_Reply(uint8_t *RX_buffer, uint8_t *TX_buffer)
{
	uint8_t sourceMAC[MAC_ADR_SIZE];
	uint8_t destMAC[MAC_ADR_SIZE];
	uint8_t sourceIP[IP_ADR_SIZE];
	uint8_t destIP[IP_ADR_SIZE];
	uint8_t i;

	// Vytvoreni ETH hlavicky
	for (i=0;i<MAC_ADR_SIZE;i++)
	{
		destMAC[i] = RX_buffer[OFFSET_ETH_SourceMAC+i];		// Nastavi zdrojovou MAC z prichoziho paketu
															// ARP requestu do cilove MAC ARP reply															
		sourceMAC[i] = my_MAC[i];							// Jako zdrojovou MAC adresu nastavi lokalni adresu
	}
	ETH_Set_ETH_Header(TX_buffer, destMAC, sourceMAC, ETH_Type_ARP);// Nastaveni ETH hlavicky pro ARP odpoved
	
	// Vytvoreni ARP hlavicky
	for (i=0;i<IP_ADR_SIZE;i++)
	{
		destIP[i] = RX_buffer[OFFSET_ARP_SourceIP+i];				// Jako cilovou IP adresu nastavi Zdrojovou 
		sourceIP[i] = my_IP[i];										// IP adresu prijatou v ARP paketu
	}
	
	ARP_Set_ARP_Header(TX_buffer, ARP_OP_REPLY, 
	   				   sourceMAC, sourceIP, destMAC, destIP);		// Nastaveni ARP hlavicky pro odpoved
	enc28j60PacketSend(ETH_Header_len+ARP_Header_len,TX_buffer,0);	// Odeslani odpovedi na ARP request
}

/* ---------------------------------------------------------------
 Funkce se podiva do lokalni ARP cache jestli nezna pozadovanou
 MAC adresu a v pripade, ze ji nezna odesle "WHO IS" dotaz
 do site a pocka na jeho odpoved
 -------------------------------------------------------------- */
 uint8_t ARP_Get_MAC(uint8_t *IP_address, uint8_t *MAC_address)
 {
	uint8_t i, j, temp;

	// Prohledani lokalni ARP cache
	for(i=0;i<MAX_ARP_CACHE;i++)							// Prochazeni jednotlivych ARP zaznamu
	{
		for(j=0;j<IP_ADR_SIZE;j++)							// Prochazeni IP adresy ve vybranem zaznamu
		{
			temp = IP_address[j];
			if(Local_ARP_cache[i][j]!=temp) break;
		}
		if(j==IP_ADR_SIZE)									// Nalezen zaznam o MAC
		{
			for(j=0;j<MAC_ADR_SIZE;j++)
			{
				MAC_address[j]=Local_ARP_cache[i][IP_ADR_SIZE+j];	// Zkopirovani MAC z ARP cache do navratove promenne
			}
			return(MAC_Found);								// Vrati nalezenou MAC adresu ktera patri zadane IP
		}
	}
	// MAC nenalezena v lokalni ARP cache -> vyhledani pomoci dotazu "WHO IS"
	
	return(MAC_NotFound);
 }

/* ---------------------------------------------------------------
 Funkce prida zaznam do lokalni ARP cache
 -------------------------------------------------------------- */
uint8_t ARP_InsertCacheEntry(uint8_t *IP_address, uint8_t *MAC_address)
{
	uint8_t index = 0;
	uint8_t i, j;
	// Kontrola, jestli se zaznam v ARP cache jiz nenachazi
	for(i=0;i<MAX_ARP_CACHE;i++)
	{
		for(j=0;j<IP_ADR_SIZE;j++)
		{
			if(Local_ARP_cache[i][j]!=IP_address[j])
			{
				break;
			}
		}
		if(j>=IP_ADR_SIZE)								// Overeni jestli se shodovaly vsechny byte IP adresy
		{
			index=i;
			break;										// Zaznam nalezen
		}
	}	
	
	// Vyhledani volneho mista v pameti (prvni byte "IP" je nastaven na 0)
	if(index>=MAX_ARP_CACHE)									// Overeni jestli byl nalezen jiz existujici zaznam
	{
		for(index=0;index<MAX_ARP_CACHE;index++)
		{
			if(Local_ARP_cache[index][0]==0)
			{
				break;
			}
		}
		if(index>=MAX_ARP_CACHE)								// Overeni jestli bylo dosazeno v hledani konce pameti (=pamet plna)
		{
			return(MAC_Full);
		}
	}
	// Vlozeni zaznamu	
	j=0;
	for(i=0;i<IP_ADR_SIZE;i++)
	{
		Local_ARP_cache[index][i] = IP_address[j++];		// Pridani casti IP adresy od pozice 0 do pozice 3
	}
	j=0;
	for(;i<IP_ADR_SIZE+MAC_ADR_SIZE;i++)
	{
		Local_ARP_cache[index][i] = MAC_address[j++];		// Pridani casti MAC adresy od pozice 4 do pozice 9
	}
	Timeout_ARP_cache[index]=0;								// Vynulovani timeoutu zaznamu

	
	return(MAC_Inserted);
}

/* ---------------------------------------------------------------
 Funkce prida zaznam do lokalni ARP cache z prichoziho paketu
 -------------------------------------------------------------- */
uint8_t ARP_InsertCacheEntryFromRX(uint8_t *RX_buffer)
{
	uint8_t *ukazatel_IP, *ukazatel_MAC;

	ukazatel_IP = IP_Get_SourceIP(RX_buffer);
	ukazatel_MAC = ETH_Get_SourceMAC(RX_buffer);

	return(ARP_InsertCacheEntry(ukazatel_IP, ukazatel_MAC));	
}

/* ---------------------------------------------------------------
 Funkce vymaze celou lokalni ARP cache (vcetne timeoutu)
 -------------------------------------------------------------- */
void ARP_InitCahce(void)
{
	uint8_t i,j;
	// Smazani vsech zaznamu IP->MAC z ARP cache
	for(i=0;i<MAX_ARP_CACHE;i++)
	{
		for(j=0;j<IP_ADR_SIZE+MAC_ADR_SIZE;j++)
		{
			Local_ARP_cache[i][j] = 0;
		}
	}
	
	// Smazani Timeoutu ARP cache
	for(i=0;i<MAX_ARP_CACHE;i++)
	{
		Timeout_ARP_cache[i]=0;	
	}
}

/* Konec souboru */
