/*
 * mmnif.c	---	memmory mapped interface
 *
 * Virutal IP Interface for the concept processor SCC
 *
 * virutally tested under Windows 7
 *
 * Carl-Benedikt Krüger 2011
 *
 */

#include "mmnif.h"			/* definitions */

#ifdef WIN32
#include "mailbox.h"		/* mailbox_ptr_t */
#else
#include <metalsvm/mailbox.h>	/* mailbox_ptr_t */
#endif

#include <lwip/netif.h>		/* lwip netif */
#include <lwip/stats.h>		/* inteface stats */
#include <netif/etharp.h>	/* ethernet arp packets */
#include <lwip/ip.h>		/* struct iphdr*/
#include <lwip/tcpip.h>		/* tcpip_input()*/

//#define DEBUG_MMNIF

#ifdef DEBUG_MMNIF
#include "util.h"			/* hex dump */
#endif

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <system/threads/bthread.h>

typedef bthread_sem_t sem_t;
typedef bthread_t tid_t;

/*  "interrupt" of the other virutal network card*/
extern HANDLE remote_process_event;
/*  HANDLE to the other Process (for WPM and RPM)*/
extern HANDLE hProc;


#define DEBUGPRINTF(x,...)  printf(x,__VA_ARGS__)
#else
#define DEBUGPRINTF(x,...)  kprintf(x,##__VA_ARGS__)

#include <metalsvm/semaphore.h>
#include <metalsvm/spinlock.h>

#include <asm/RCCE_lib.h>
#include <asm/RCCE.h>
#include <asm/RCCE_lib.h>
#include <asm/iRCCE.h>
#include <asm/iRCCE_lib.h>

#include <asm/SCC_API.h>

#include <metalsvm/time.h>


#endif

#define MMNIF_TX_QUEUELEN		4
#define MMNIF_RX_QUEUELEN		8

#define MMNIF_RX_BUFFERLEN		1792
#define MMNIF_TX_BUFFERLEN		1792

#define MMNIF_CORES			2

#define MMNIF_WORKER_BUDGET		2

#define MMNIF_POLL_BUDGET		0x100000
#define MMNIF_WAIT_BUDGET		0x2

/* decide whether it's polling mode or not 
 */
static int no_irq = 0;

/* this will be set by open() and close() and shows wether the driver is running or not
*/
static int active = 0;

/* decide wheter it's uses locking or not
 */
static int disable_locking = 0;

/* decide whether deliver work to a worker thread or instantly process all packets
 */
static int instant_process = 1;

/* IP address of the local core and the router core to get packets forwarded
 */
static unsigned int own_ip_address = 0xC0A80000;	/* 192.168.0.0 */

static unsigned int router_ip_address = 0xC0A80001;	/* 192.168.0.1 */

/* "message passing buffer" specific constants:
 * - start address
 * - size
 */
char*			mpb_start_address = NULL;
unsigned int	mpb_size = NULL;


/* Register offset for the CONFIG and LOCK registers */
#define RCK_GLCFG0          0x10
#define RCK_GLCFG1          0x18
#define RCK_TILEID          0x100

#define RCK_TILE_SIZE       0x01000000

/* Start address of the local register bank */
static int local_crb = 0xF8000000;

/* just set and reset the irq */
static int pulse_irq = 0;

/* Mask of the interrupt bits */
#define RCK_INTR_MASK       0x00000002
#define RCK_NMI_MASK        0x00000001

/*
 * the memory mapped network device
 */
static struct netif* mmnif_dev = NULL;

/* thread variables */
static tid_t	worker_thread;
static tid_t	polling_thread;

typedef struct mmnif_device_stats 
{
	/* device stats (granularity in packets):
	 * - recieve errors
	 * - recieve successes
	 * - recieved bytes
	 * - transmit errors
	 * - transmit successes
	 * - transmitted bytes
	 */
	unsigned int rx_err;
	unsigned int rx;

	unsigned int rx_bytes;

	unsigned int tx_err;
	unsigned int tx;

	unsigned int tx_bytes;

	/* Heuristics :
	 * - how many times an budget overflow occured
	 * - how many times the polling thread polled without recieving a new message
	 * - how many messages are recieved via interrupt
	 * - how many messages are recieved via the polling thread
	 */
	unsigned int bdg_overflow;
	unsigned int pll_empty;
	unsigned int rx_intr;
	unsigned int rx_poll;

} mmnif_device_stats_t;

typedef struct mm_rx_buffer
{
	/* memory rx buffer build
	 * - queued : how many packets are in the queue
	 * - pos : which is the next packet to be worked on
	 * - pending : how many packets are pending
	 * - iv_intr : inform via interrupt or not
	 * - lock: semaphore to lock the local variables to be multi access save
	 * 
	 * Note: this section will soon be complexer.
	 * I won't use a single buffer the whole time. I think i will use an descripor table
	 * and a table which descriptor is in use and use the buffer space dynamically with 
	 * descriptors
	 * Also timeouts should be checked so if one core dies the buffer space is not blocked
	 * all the time
	 */
	uint8_t			queued;
	uint8_t			pos;
	uint8_t			pending;

	uint8_t			iv_intr;

//	sem_t			lock;
        spinlock_t              lock;

//	uint32_t		timestamp[MMNIF_RX_QUEUELEN];
//	uint32_t		bitmap[MMNIF_RX_QUEUELEN];

//	void*			rx_desc[MMNIF_CORES * MMNIF_RX_QUEUELEN];
//	uint8_t			rx_inuse[MMNIF_CORES * MMNIF_RX_QUEUELEN]; /* bits 1: pending 2: finished 3: free ...*/
//	uint8_t			fin;
//	uint8_t*		data[MMNIF_RX_QUEUELEN];
} mm_rx_buffer_t;

typedef struct mmnif
{
	struct mmnif_device_stats	stats;

	/* Interface constants:
	 * - ehternet address
	 * - local ip address
	 */
        struct eth_addr*		ethaddr;
        uint32_t			ipaddr;
	
	/* memory interaction variables:
	 * - transmit queue
	 * - pointer to transmit buffer
	 * - pointer to recive buffer
	 */
        uint8_t				tx_queue;
        uint8_t*			tx_buff[MMNIF_TX_QUEUELEN];
        mm_rx_buffer_t*			rx_buff;

        sem_t				com_poll;

	/* comunication mailbox
	 */
        mailbox_ptr_t			mbox;
}mmnif_t;


#ifdef WIN32

__inline int get_my_core_no(void)
{
#ifndef RECV
	return 1;
#else
	return 0;
#endif
}

#else

__inline int get_my_core_no(void)
{
  unsigned int tmp;
  unsigned int pid;
  unsigned int x,y,z;
  /* Determine the local IP address from the core number in the 
   * tile ID register 
   */
  tmp = ReadConfigReg(local_crb + RCK_TILEID);
  x = (tmp>>3) & 0x0f; /* bits 06:03 */
  y = (tmp>>7) & 0x0f; /* bits 10:07 */
  z = (tmp   ) & 0x07; /* bits 02:00 */
  pid = 12*y + 2*x + z;
  /* Add 1 to the processor ID to avoid *.*.*.0 IP addresses */
  return pid;
}

#endif


/*
 *	memory maped interface helper functions
 */

/* read the queue value from the remote buffer
 * and return it.
 */
__inline uint8_t mmnif_read_rx_queue(uint32_t dest_ip)
{
#ifdef WIN32
	/* Read the value of the forgein process
	 * form rx_buff->queued
	 */
	mm_rx_buffer_t	hdr;
	while(!ReadProcessMemory(hProc,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,&hdr,sizeof(hdr),NULL));
	return hdr.queued;
#else
	mm_rx_buffer_t	hdr;
	memcpy(&hdr,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,sizeof(hdr));
	return hdr.queued;
#endif
};

/* read the pos value from the remote buffer
 * and return it.
 */
__inline uint8_t mmnif_read_rx_pos(uint32_t dest_ip)
{
#ifdef WIN32
	/* Read the value of the forgein process
	 * form rx_buff->pos
	 */
	mm_rx_buffer_t	hdr;
	while(!ReadProcessMemory(hProc,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,&hdr,sizeof(hdr),NULL));
	return hdr.pos;
#else
	mm_rx_buffer_t	hdr;
	memcpy(&hdr,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,sizeof(hdr));
	return hdr.pos;
#endif
};

/* read the inv_intr from the remote buffer
 * and return it.
 */
__inline uint8_t mmnif_read_rx_inv_intr(uint32_t dest_ip)
{
#ifdef WIN32
	/* Read the value of the forgein process
	 * form rx_buff->inv_intr
	 */
	mm_rx_buffer_t	hdr;
	while(!ReadProcessMemory(hProc,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,&hdr,sizeof(hdr),NULL));
	return hdr.iv_intr;
#else
	mm_rx_buffer_t	hdr;
	memcpy(&hdr,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,sizeof(hdr));
	return hdr.iv_intr;
#endif
};

/* read the pending value from the remote buffer
 * and return it.
 */
__inline uint8_t mmnif_read_rx_pending(uint32_t dest_ip)
{
#ifdef WIN32
	/* Read the value of the forgein process
	 * form rx_buff->pending
	 */
	mm_rx_buffer_t	hdr;
	while(!ReadProcessMemory(hProc,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,&hdr,sizeof(hdr),NULL));
	return hdr.pending;
#else
	mm_rx_buffer_t	hdr;
	memcpy(&hdr,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,sizeof(hdr));
	return hdr.pending;
#endif
};

/* write data to the remote buffer
 * 
 */
__inline int mmnif_write_rx_buff(uint32_t dest_ip, uint32_t pos,void* data)
{
#ifdef WIN32
	/* we assume this is a correct buffer 
	 * therefore here is no further error checking
	 */
	uint32_t nr_of_bytes_written = 0;
	uint16_t length = *((uint16_t*)data);
	while(!WriteProcessMemory(hProc,(char*)mpb_start_address +(dest_ip -1 ) * mpb_size + sizeof(mm_rx_buffer_t)+ pos * MMNIF_RX_BUFFERLEN,data,length+2,&nr_of_bytes_written));
	return nr_of_bytes_written;
#else
	uint16_t length = *((uint16_t*)data);
	memcpy((char*)mpb_start_address +(dest_ip -1 ) * mpb_size + sizeof(mm_rx_buffer_t)+ pos * MMNIF_RX_BUFFERLEN,data,length+2);
	return 1;
#endif
};

__inline int mmnif_write_rx_buffl(uint32_t dest_ip, uint32_t pos,void* data,uint16_t length)
{
#ifdef WIN32
	/* we assume this is a correct buffer 
	 * therefore here is no further error checking
	 */
	uint32_t nr_of_bytes_written = 0;
	while(!WriteProcessMemory(hProc,(char*)mpb_start_address +(dest_ip -1 ) * mpb_size + sizeof(mm_rx_buffer_t)+ pos * MMNIF_RX_BUFFERLEN,&length,2,&nr_of_bytes_written));
	while(!WriteProcessMemory(hProc,(char*)mpb_start_address +(dest_ip -1 ) * mpb_size + sizeof(mm_rx_buffer_t)+ pos * MMNIF_RX_BUFFERLEN + 2,data,length,&nr_of_bytes_written));
	return nr_of_bytes_written+2;
#else
//	uint16_t length = *((uint16_t*)data);
	memcpy((char*)mpb_start_address +(dest_ip -1 ) * mpb_size + sizeof(mm_rx_buffer_t)+ pos * MMNIF_RX_BUFFERLEN,&length,2);
	memcpy((char*)mpb_start_address +(dest_ip -1 ) * mpb_size + sizeof(mm_rx_buffer_t)+ pos * MMNIF_RX_BUFFERLEN + 2,data,length);
	return 1;
#endif
};

/* write the new queue value to the remote buffer
 * 
 */
__inline int mmnif_write_rx_queue(uint32_t dest_ip,uint8_t queue)
{
	/* tell the remote buffer/process
	 * that there is another packet in the queue
	 */
#ifdef WIN32
	uint32_t nr_of_bytes_written = 0;
	while(!WriteProcessMemory(hProc,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size ,&queue,1,&nr_of_bytes_written));
	return nr_of_bytes_written;
#else

	memcpy((char*)mpb_start_address + ( dest_ip -1 ) * mpb_size ,&queue,1);

	return 1;
#endif
};

/* write the new queue value to the remote buffer
 * 
 */
__inline int mmnif_write_rx_pending(uint32_t dest_ip,uint8_t pending)
{
	/* tell the remote buffer/process
	 * that there is another packet in the queue
	 */
#ifdef WIN32
	uint32_t nr_of_bytes_written = 0;
	while(!WriteProcessMemory(hProc,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size + 2 ,&pending,1,&nr_of_bytes_written));
	return nr_of_bytes_written;
#else

	memcpy((char*)mpb_start_address + ( dest_ip -1 ) * mpb_size + 2 , &pending,1);

	return 1;
#endif
};

/* trigger an interrupt on the remote processor
 * 
 */
__inline int mmnif_trigger_irq(dest_ip)
{
#ifdef WIN32
	return PulseEvent(remote_process_event);
#else
#if 0
	/* NOTE: have to check how the remote interrupt managment works
	 * on the SCC
	 */
	mmnif_t*		mmnif = mmnif_dev->state;
	int				core  = (dest_ip - 1) % 2;
	int				irq_address = mmnif->crb[dest_ip-1];
	unsigned int	value;

	if (core == 0) irq_address += RCK_GLCFG0;
	else		   irq_address += RCK_GLCFG1;

	/**/
	value = ReadConfigReg((void*)irq_address);

	if ((!pulse_irq) && (value & RCK_INTR_MASK))
	{
		value &= (~(RCK_INTR_MASK|RCK_NMI_MASK));
		SetConfigReg((void*)irq_address,value);
	}

	value |= RCK_INTR_MASK;
	SetConfigReg((void*) irq_address,value);

	/**/
	if (pulse_irq)
	{
		value &= (~(RCK_INTR_MASK|RCK_NMI_MASK));
		SetConfigReg((void*)irq_address,value);
	}
#endif

	int tmp, x, y, z, addr;

	int ue = dest_ip -1;

	z = Z_PID(RC_COREID[ue]);
	x = X_PID(RC_COREID[ue]);
	y = Y_PID(RC_COREID[ue]);
	addr = CRB_ADDR(x,y) + (z==0 ? GLCFG0 : GLCFG1);

	// send interrupt to ue
	do {
		NOP1;
		tmp=ReadConfigReg(addr);
	} while(tmp & 2);
	tmp |= 2;
	SetConfigReg(addr, tmp);

	return 0;
#endif
};

/* mmnif_device_schedule() : 
 * if there is no interupt used to indicate new packets
 * this creates a polling thread which looks for data
 * itself
 */
__inline int mmnif_device_schedule()
{
#ifdef WIN32
	bthread_create(&polling_thread,NULL,mmnif_poll,NULL);
	return NULL;
#else
	create_kernel_task(&polling_thread,mmnif_poll,NULL);
	return NULL;
#endif
}
/* mmnif_worker_schedule() : 
 * if there is no interupt used to indicate new packets
 * this creates a polling thread which looks for data
 * itself
 */
__inline int mmnif_worker_schedule()
{
#ifdef WIN32
	bthread_create(&worker_thread,NULL,mmnif_worker,NULL);
	return NULL;
#else
	create_kernel_task(&worker_thread,mmnif_worker,NULL);
	return NULL;
#endif
}
/* Allocate Shared Memory for communication this could be:
 * - in Message Passing Buffer
 * - Shared Memory Address Space (0x8000000 + )
 * 
 * Note: under windows this is kernel space so we take arbitrary 0x41000000 here
 */
__inline void* mmnif_shmalloc()
{
#ifdef WIN32
	mpb_size = sizeof(mm_rx_buffer_t) + MMNIF_RX_QUEUELEN* MMNIF_RX_BUFFERLEN;
	mpb_start_address = VirtualAlloc((char*)0x41000000 /*+ 
						(mpb_size) * (own_ip_address - router_ip_address)*/, 
						mpb_size *MMNIF_CORES,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);

	return (char*)0x41000000 + (mpb_size) * (own_ip_address - router_ip_address);
#else
	mpb_size = (sizeof(mm_rx_buffer_t) + MMNIF_RX_QUEUELEN* MMNIF_RX_BUFFERLEN);

	mpb_start_address = RCCE_shmalloc(mpb_size*MMNIF_CORES);
	return mpb_start_address + (mpb_size) * (own_ip_address - router_ip_address);
#endif
}

/* mmnif_lock_rx_hdr(): lock the header of mm_rx_buffer
 * so there is no race condition on the variables
 */
__inline void mmnif_lock_rx_hdr(int dest_ip)
{
#ifdef WIN32
	mm_rx_buffer_t	hdr;
	if(disable_locking) return;
	while(!ReadProcessMemory(hProc,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,&hdr,sizeof(hdr),NULL));
	sem_wait(&hdr.lock);
#else
	if(disable_locking) return;
	mm_rx_buffer_t* hdr = (char*)mpb_start_address + ( dest_ip -1 ) * mpb_size;
//	sem_wait(&hdr->lock);
        spinlock_lock(&hdr->lock);
#endif
}
/* mmnif_unlock_rx_hdr(): unlock the header
 * again
 */
__inline void mmnif_unlock_rx_hdr(int dest_ip)
{
#ifdef WIN32
	mm_rx_buffer_t	hdr;
	if(disable_locking) return;
	while(!ReadProcessMemory(hProc,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,&hdr,sizeof(hdr),NULL));
	sem_post(&hdr.lock);
#else
	if(disable_locking) return;
	mm_rx_buffer_t* hdr = (char*)mpb_start_address + ( dest_ip -1 ) * mpb_size;
        spinlock_unlock(&hdr->lock);
#endif
}

/* mmnif_timestamp(): genereate a timestamp for the 
 * packets
 */
__inline int mmnif_timestamp()
{
#ifdef WIN32
	return GetTickCount();
#else
	return get_clock_tick();
#endif
}


/* mmnif_get_device_stats(): Returns a copy of the
 * current device
 */
mmnif_device_stats_t mmnif_get_device_stats()
{
	mmnif_device_stats_t stats = {0};

	if(!mmnif_dev)
		DEBUGPRINTF("mmnif_get_device_stats(): the device is not initialized yet.\n");
	else
		stats = ((mmnif_t*)mmnif_dev->state)->stats;

	return stats;
}


/* mmnif_print_stats(): Print the devices stats of the
 * current device
 */
void mmnif_print_stats()
{
	mmnif_t* mmnif;

	if (!mmnif_dev)
	{
		DEBUGPRINTF("mmnif_print_stats(): the device is not initialized yet.\n");
		return;
	}
	
	mmnif = (mmnif_t*)mmnif_dev->state;

	DEBUGPRINTF("/dev/mmnif - stats:\n");
	DEBUGPRINTF("Received: %d packets successfull\n",mmnif->stats.rx);
	DEBUGPRINTF("Received: %d bytes\n",mmnif->stats.rx_bytes);
	DEBUGPRINTF("interrupts: %d\n",mmnif->stats.rx_intr);
	DEBUGPRINTF("polling: %d\n",mmnif->stats.rx_poll);
	DEBUGPRINTF("Received: %d packets containuing errors\n",mmnif->stats.rx_err);
	DEBUGPRINTF("Transmitted: %d packests successfull\n",mmnif->stats.tx);
	DEBUGPRINTF("Transmitted: %d bytes\n",mmnif->stats.tx_bytes);
	DEBUGPRINTF("Transmitted: %d packests were dropped due to errors\n",mmnif->stats.tx_err);

}


/*
 *	memory maped interface main functions
 */

/* mmnif_get_destination(): low level transmid helper function
 * this function deals with some HW details, it checks to wich core this packet
 * should be routed and returns the destination
 */

uint8_t mmnif_get_destination(struct netif* netif, struct pbuf* p)
{
	struct ip_hdr*	iphdr;
	uint8_t			core;
	uint8_t*		ip4addr;
	uint8_t			addr[4];
	uint32_t		netmask = 0xFFFFFF00;


	/* grab the destination ip address out of the ip header
	 * for internal routing the last ocet is interpreted as core ID.
	 */

	iphdr = (struct ip_hdr*)(p->payload);
	ip4addr = &iphdr->dest.addr;
	
	/* revert the address to host format */
	addr[3] = ip4addr[0]; addr[2] = ip4addr[1]; 
	addr[1] = ip4addr[2]; addr[0] = ip4addr[3];

	/* check if the ip address is in the Local Area Network of the 48 cores */

	/* if it's not the same network the router core is addressed 
	 * Note: the router core is core 1
	 */

	if (!((netmask & *(uint32_t*)addr) == (netmask & own_ip_address) ))
		return 1;



	core = addr[0];

	/* check if the address is legitimata else return router core again */
	if ((core) < 1 || (core > MMNIF_CORES)) 
		core  = 1;

	return core;

}

/*
 * Transmid a packet (called by the lwip)
 */
err_t mmnif_tx(struct netif* netif, struct pbuf* p)
{
	mmnif_t*		mmnif = netif->state;
	uint8_t			slot = mmnif->tx_queue;
	uint8_t			queued;
	uint8_t			pos;
	uint8_t			pending;
	uint32_t		i;
	struct pbuf*	q; /* interator */
	uint8_t			build_buff = TRUE;
	uint8_t			dest_intr = FALSE;
	uint32_t		dest_ip = mmnif_get_destination(netif,p);

	/* take a place in the tx_queue */
	mmnif->tx_queue++;

	/* Perform serveral sanity checks on the packet and the buffers:
	 * - is the queue full?
	 * - is the output packet to big?
	 *
	 *
	 * HINT: MMNIF_TX_QUEUELEN should be 1 as long there is no mutex availible to ensure
	 * just one thread is writing to pos and queue of the mm_rx_buff
	 */

	if (mmnif->tx_queue >= MMNIF_TX_QUEUELEN) 
	{
		DEBUGPRINTF("mmnif_tx(): too many packets at once for tx_queue\n");
		goto drop_packet;
	}

	if (p->tot_len > MMNIF_TX_BUFFERLEN) 
	{
		DEBUGPRINTF("mmnif_tx(): packet is longer than 1792 bytes\n");
		goto drop_packet;
	}

	/* check if the pbuf consists only of one element
	 * if that is the case it would be much overhead
	 * copying that packet again
	 */
	if (!p->next)
		build_buff = FALSE;


	if (build_buff)
	{
		/* write packet length to start transmit buffer */
		*((unsigned short*)mmnif->tx_buff[slot]) = p->tot_len;

		/* build the payload out of the p's 
		 * ensure that the packet is in one memory chunk stored in the transmid buffer
		 */
		for (q = p, i = 2; q != 0; q = q->next) 
		{
			memcpy(mmnif->tx_buff[slot] + i, q->payload, q->len);
			i += q->len;
		}
	}
	else
	{
		/* because there is no copy operation to the tx_slots
		 * we don't need a place in the queue anymore
		 */
		mmnif->tx_queue--;
	}
	/* get the palce the router core is looking for the packet */

	/* lock the dest_ip mm_rx_buffer_hdr */
	mmnif_lock_rx_hdr(dest_ip);

	/* read and edit needed values */
	queued = mmnif_read_rx_queue(dest_ip);
	pos	   = mmnif_read_rx_pos(dest_ip);

	pending  = mmnif_read_rx_pending(dest_ip);

	pending++;
	mmnif_write_rx_pending(dest_ip,pending);

	/* and unlock the dest_ip mm_rx_buffer_hdr */
	mmnif_unlock_rx_hdr(dest_ip);

	/* check if there is a space in the queue without overwriting another packet */
	if ((queued + pending) > MMNIF_RX_QUEUELEN)
	{
		DEBUGPRINTF("mmnif_tx(): too many packet's at once for the remote queue : q:%d p:%d\n",queued , pending);
		goto drop_packet;
	}

	pos = (pos + queued + pending -1) % MMNIF_RX_QUEUELEN;

	/* write buffer to buffer & increment the queued packet count 
	 * this can be safely done without locking because this place is
	 * reserved for us because it has the status "pending"
	 */
	if (build_buff)
		mmnif_write_rx_buff(dest_ip, pos, mmnif->tx_buff[slot]);
	else
		mmnif_write_rx_buffl(dest_ip, pos, p->payload,p->tot_len);

	/* like above ensure we are the only ones editing the hdr */
	mmnif_lock_rx_hdr(dest_ip);

	queued = mmnif_read_rx_queue(dest_ip);
	pending  = mmnif_read_rx_pending(dest_ip);
	queued++;
	pending--;

	dest_intr = mmnif_read_rx_inv_intr(dest_ip);

#ifdef DEBUG_MMNIF
	DEBUGPRINTF("\n SEND 0x%.8X with length: %d\n",(char*)mpb_start_address + (dest_ip -1)*mpb_size + pos * 1792,p->tot_len +2);
	hex_dump(p->tot_len, p->payload);
#endif

	mmnif_write_rx_queue(dest_ip, queued);
	mmnif_write_rx_pending(dest_ip, pending);

	mmnif_unlock_rx_hdr(dest_ip);

	/* if driver is not in polling mode inform core that a message has arrived */
	if (dest_intr)
		mmnif_trigger_irq(dest_ip);

	/* free the transmid queue*/
	if(build_buff)
		mmnif->tx_queue--;

	/* gather stats:
	 * - firstly for lwip
	 * - secondly for us
	 */
	
	LINK_STATS_INC(link.xmit);

	mmnif->stats.tx++;
	mmnif->stats.tx_bytes += p->tot_len;

	return ERR_OK;

drop_packet: /* packet is lost. clean up and gather stats */

	mmnif->tx_queue--;

	LINK_STATS_INC(link.drop);

	mmnif->stats.tx_err++;

	return ERR_IF;
}

/* mmnif_link_layer(): wrapper function called by ip_output()
 * adding all needed headers for the link layer
 * because we have no link layer and everything is reliable we don't need
 * to add anything so we just pass it to our tx function
 */
static void mmnif_link_layer(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
{
	netif->linkoutput(netif,q);	
}


/*
 * Init the device (called from lwip)
 * It's invoked in netif_add
 */
err_t mmnif_init(struct netif* netif)
{
	mmnif_t* mmnif;
	uint32_t i;
	static int num = 0;
	uint16_t speed = 4000;


	/* Alloc and clear memory for the device struct
	 */
#ifdef WIN32
	mmnif =  malloc(sizeof(mmnif_t));
#else
	mmnif =  kmalloc(sizeof(mmnif_t));
#endif
	if (!mmnif) 
	{
		DEBUGPRINTF("mmnif init():out of memory\n");
		return ERR_MEM;
	}
	memset(mmnif, 0, sizeof(mmnif_t));

	/* Alloc and clear shared memory for rx_buff
	 */
	mmnif->rx_buff = mmnif_shmalloc();
	if (!(mmnif->rx_buff)) 
	{
		DEBUGPRINTF("mmnif init(): allocating shared memory failed\n");
		return ERR_MEM;
	}
	memset(mmnif->rx_buff, 0, mpb_size);

	/* init the lock for the hdr
	 */
//	sem_init(&mmnif->rx_buff->lock,1);
        spinlock_init(&mmnif->rx_buff->lock);

	/* init the sems for communication art
	 */

	sem_init(&mmnif->com_poll,1);

	/* since there is no possibilty to create a full semaphore we just block it manually
	 */
	sem_wait(&mmnif->com_poll);

	/* inform via interrupt should be the dafault
	 */
	if (!no_irq)
		mmnif->rx_buff->iv_intr = TRUE;

	/* Alloc and clear internal memory for tx_buff
	 */
#ifdef WIN32
	mmnif->tx_buff[0] = malloc(MMNIF_TX_QUEUELEN * MMNIF_TX_BUFFERLEN);
#else
	mmnif->tx_buff[0] = kmalloc(MMNIF_TX_QUEUELEN * MMNIF_TX_BUFFERLEN);
#endif
	if (!(mmnif->tx_buff[0])) 
	{
		DEBUGPRINTF("mmnif init: out of memory tx\n");
#ifdef WIN32
		free(mmnif->rx_buff);
		free(mmnif);
#else
//		kfree(mmnif->rx_buff);
//		kfree(mmnif);
#endif
		return ERR_MEM;
	}
	mmnif->tx_queue = 0;	
	memset(mmnif->tx_buff[0], 0, MMNIF_TX_QUEUELEN * MMNIF_TX_BUFFERLEN);

	for (i = 0; i < MMNIF_TX_QUEUELEN -1 ; i++)
		mmnif->tx_buff[i+1] = mmnif->tx_buff[i] + MMNIF_TX_BUFFERLEN;

	/* initialize the mailbox system */
	mailbox_ptr_init(&mmnif->mbox);

	/* pass the device state to lwip */
	netif->state = mmnif;
	mmnif_dev = netif;

	/* Generate MAC address */
	mmnif_dev->hwaddr[0] = 0x11;mmnif_dev->hwaddr[1] = 0x22;mmnif_dev->hwaddr[2] = 0x33;
	mmnif_dev->hwaddr[3] = 0x44;mmnif_dev->hwaddr[4] = 0x55;mmnif_dev->hwaddr[5] = get_my_core_no()*0x11 +0x66;

	/*
	 * Initialize the snmp variables and counters inside the struct netif.
	 * The last argument should be replaced with your link speed, in units
	 * of bits per second.
	 */
	NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, speed);

	/* administrative details */
	netif->name[0] = 'e';
	netif->name[1] = 'n';
	netif->num = num;
	num++;
	/* downward functions */
	netif->output = mmnif_link_layer;
	/* there is no special link layer just the ip layer */
	netif->linkoutput = mmnif_tx;
	/* maximum transfer unit */
	netif->mtu = 1500;
	/* broadcast capability, keep all default flags*/
	netif->flags |= NETIF_FLAG_BROADCAST /*| NETIF_FLAG_ETHARP*/ | NETIF_FLAG_LINK_UP; 
	/* hardware address length */
	netif->hwaddr_len = 6;

	mmnif->ethaddr = (struct eth_addr *)netif->hwaddr;

	active = TRUE;

#ifdef MMNIF_DEBUG
	DEBUGPRINTF("mmnif init complete\n");
#endif

	return NULL;
}

/*
 * Receive a packet : recieve, pack it up and pass over to higher levels
 */
static void mmnif_rx(struct netif* netif)
{
	mmnif_t*		mmnif = netif->state;
	uint16_t		length,i;
	struct pbuf*	p = NULL;
	struct pbuf*	q;
	char*			data;
	uint32_t		pos;
	uint8_t			queued;

	/* retrieve pointer to actual data array */
	data = (char*) mmnif->rx_buff + sizeof(mm_rx_buffer_t);
	/* retrice position wich is needed to be worked on */
	pos = (mmnif->rx_buff->pos % MMNIF_RX_QUEUELEN) * MMNIF_RX_BUFFERLEN;

	/* The packet length is stored in the first 2 bytes but does not include
     * the header. Check for reasonable sizes before processing the data to
	 * prevent nasty memory overflow errors.
     */
	length = *((uint16_t*) (data + pos));

	/* If length is zero return silently */
	if (length == 0)
	{
//		mmnif->rx_buff->pos++;
//		mmnif->rx_buff->queued--;
		return;
	}
	if (length < sizeof(struct ip_hdr) ||length > netif->mtu)
	{
		DEBUGPRINTF("mmnif_rx(): illegal packet length %d => drop the packet\n",length);
		goto drop_packet;
	}

	/* From now on there is a real packet and it
	 * has to be worked on
	 */


#ifdef DEBUG_MMNIF
	DEBUGPRINTF("\n RECIEVED - 0x%.8X with legth: %d\n",data + pos,length+2);
	hex_dump(length+2,data + pos);
#endif
	
	/* drop the length word of the packet data since it's no longer needed*/
	pos += 2;

	
	/* Build the pbuf for the packet so the lwip 
	 * and other higher layer can handle it
	 */
	p = pbuf_alloc(PBUF_RAW, length, PBUF_POOL);
	if (!p) 
	{
		DEBUGPRINTF("mmnif_rx(): low on mem - packet dropped\n");
		goto drop_packet;
	}
	
	/* copy packet to pbuf structure going through linked list */
	for (q=p, i = 0; q!=NULL; q=q->next) 
	{
		memcpy((uint8_t*)q->payload + i,&data[pos+i],q->len);
		i +=q->len;
	}

	/* everything is copied to a new buffer so it's save to release 
	 * the old one for new incoming packets
	 */

	mmnif_lock_rx_hdr(own_ip_address & 0xFF);

	mmnif->rx_buff->pos++;
	mmnif->rx_buff->queued--;

	mmnif_unlock_rx_hdr(own_ip_address & 0xFF);

	/* using the mailbox to hand the buffer to the incoming packet thread
	 * so the "interrupt" itself is not taking to long
	 */
	mailbox_ptr_post(&mmnif->mbox, (void*)p);

	/* gather some stats and leave the rx handler */
	LINK_STATS_INC(link.xmit);
	mmnif->stats.rx++;

	mmnif->stats.rx_bytes += p->tot_len;

	if (mmnif->rx_buff->iv_intr)
		mmnif->stats.rx_intr++;
	else
		mmnif->stats.rx_poll++;

	return;

drop_packet:
	/* packet is lost so gather stats and leave the rx handler*/
        mmnif_lock_rx_hdr(own_ip_address & 0xFF);

        mmnif->rx_buff->pos++;
        mmnif->rx_buff->queued--;

        mmnif_unlock_rx_hdr(own_ip_address & 0xFF);

        LINK_STATS_INC(link.drop);
	mmnif->stats.rx_err++;
	return;
}

/*
 * The wait implementation which is processing all incoming packets 
 */
static int mmnif_wait(struct netif* netif, uint32_t poll, int budget)
{	
	mmnif_t*			mmnif = netif->state;
	struct eth_hdr *	ethhdr;
	struct pbuf*		p = NULL;
	int					err = ERR_OK;
	unsigned int		npackets = 0;
	unsigned int		quota = 0;

	if (budget > mmnif->rx_buff->queued)
	{
		quota = mmnif->rx_buff->queued;
	}
	else
	{
		quota = budget;

		mmnif->stats.bdg_overflow++;
		if (mmnif->stats.bdg_overflow >= MMNIF_WAIT_BUDGET)
		 {
		 /*   enable polling and disable interrupts
		  *	  (only if polling isn't enabled anyways)
		  */
			 if (mmnif->rx_buff->iv_intr == TRUE)
			 {
					mmnif_lock_rx_hdr(own_ip_address && 0xff);
					mmnif->rx_buff->iv_intr = FALSE;
					mmnif_unlock_rx_hdr(own_ip_address && 0xff);
#ifdef DEBUG_MMNIF
					DEBUGPRINTF("mmnif_wait(): heuristical polling enables\n");
#endif
					sem_post(&mmnif->com_poll);
			 }
				
			 mmnif->stats.bdg_overflow = 0;
		 }

	}

	/* process up to quota packets from the receive queue */
	while (npackets <= quota)
	{
		/* fetch new data from mmnif_rx() if there is any */
		if (poll) 
		{
			/* if there is no data return immeadieatly */
			if (mailbox_ptr_tryfetch(&(mmnif->mbox), (void**) &p))
				return err;
		}
		else 
		{
			mailbox_ptr_fetch(&(mmnif->mbox), (void**) &p);
		}

		/* if there is data, pass it up to the lwip
		 * so he can handle it properly
		 */

		/* full packet send to tcpip_thread to process */
		if ((err = mmnif_dev->input(p, mmnif_dev)) != ERR_OK) 
		{
			DEBUGPRINTF("mmnif_poll: IP input error\n");
			pbuf_free(p);
		}

		npackets++;
	}

	/* Note : i will add an return error wich indicates that 
	 * there is no budget left but messages in the queue
	 */
	return err;

}

/* 
 * worker thread	
 */
int mmnif_worker(void* e)
{
#ifdef DEBUG_MMNIF
	DEBUGPRINTF("Waiting for work to do!!!\n");
#endif
	while (active)
		mmnif_wait(mmnif_dev,0,MMNIF_WORKER_BUDGET);

	return NULL;
}

/*
 * the poll function wich is used if no interrupt wake up our mmnif_rx functions
 */
int mmnif_poll(void* e)
{
	mmnif_t*	mmnif;
	unsigned int diff = mmnif_timestamp();

	if (!mmnif_dev)
	{
		DEBUGPRINTF("mmnif_poll(): the driver is not initialized yet\n");
		return -1;
	}
	
	mmnif = (mmnif_t*) mmnif_dev->state;

#ifdef DEBUG_MMNIF
	DEBUGPRINTF("Polling for work to do!!!! ONBBBB  0x%.8X  BBBB\n\n",mmnif->rx_buff);
#endif

	if (!no_irq)
	{
		sem_wait(&mmnif->com_poll);
	}

	/*run while driver is up*/
	while (active)
	{
		while (!mmnif->rx_buff->queued)
		{
                        mmnif->stats.pll_empty++;
			if (mmnif->stats.pll_empty >= MMNIF_POLL_BUDGET)
			{
			/*   enable interrupts and suspend polling
			 * 
			 */
				mmnif_lock_rx_hdr(own_ip_address && 0xff);
				mmnif->rx_buff->iv_intr = TRUE;
				mmnif_unlock_rx_hdr(own_ip_address && 0xff);
#ifdef DEBUG_MMNIF
				DEBUGPRINTF("mmnif_poll(): heuristical interrupts enabled\n");
#endif
				sem_wait(&mmnif->com_poll);
			}     
                        mmnif->stats.pll_empty = 0;
		}
                mmnif->stats.pll_empty=0;
		mmnif_rx(mmnif_dev);

		if (instant_process)
			mmnif_wait(mmnif_dev,0,MMNIF_WORKER_BUDGET);
	}

	return NULL;
}

/* mmnif_irqhandler(): handles incoming interrupts
 * its just a local wrapper for mmnif_rx()
 */
void mmnif_irqhandler()
{
	mmnif_t*	mmnif;
	/* return if mmnif_dev is not yet initialized*/
	if (!mmnif_dev)
	{
		DEBUGPRINTF("mmnif_irqhandler(): the driver is not initialized yet\n");
		return;
	}
	
	mmnif = (mmnif_t*) mmnif_dev->state;

	while (mmnif->rx_buff->queued)
	{
		mmnif_rx(mmnif_dev);

		if (instant_process)
			mmnif_wait(mmnif_dev,0,MMNIF_WORKER_BUDGET);
	}
}

/*
 * Open the interface should be called by kernel to use this network interface
 */
int mmnif_open()
{
	struct ip_addr	ipaddr;
	struct ip_addr	netmask;
	struct ip_addr	gw;

	/* calculate my own ip address from core number
	 * Note: core 1 is the router core
	 */
	IP4_ADDR(&gw, 0,0,0,0);
	IP4_ADDR(&ipaddr, 192,168,0,get_my_core_no() +1);
	IP4_ADDR(&netmask, 255,255,255,0);

	own_ip_address+= get_my_core_no() +1;

#ifdef WIN32
	mmnif_dev = malloc(sizeof(struct netif));
#else
	mmnif_dev = kmalloc(sizeof(struct netif));
#endif
	/* register our Memory Mapped Virtual IP interface in the lwip stack
	 * and tell him how to use the interface:
	 * - mmnif_dev : the device data storage
	 * - ipaddr : the ip address wich should be used
	 * - gw : the gateway wicht should be used
	 * - mmnif_init : the initialization which has to be done in order to use our interface 
	 * - ethernet_input : tells him that he should get ethernet input (inclusice ARP)
	 *
	 * Note: Ethernet Input will be removed because its NOT needed and will  
	 * be replaced with ip_input
	 */
	if (!netif_add(mmnif_dev, &ipaddr, &netmask, &gw, NULL,(netif_init_fn)mmnif_init, tcpip_input/*ethernet_input*/)) 
	{
		DEBUGPRINTF("mmnif_open() : unable to add network interface\n");
		return -1;
	}

	/* set our network interface to the default interface for lwip*/
	netif_set_default(mmnif_dev);
	/* tell lwip all initialization is done and we want to set it ab*/
	netif_set_up(mmnif_dev);

	/* test if interface is really up */
	if (!netif_is_up(mmnif_dev)) 
	{
		DEBUGPRINTF("mmnif_open(): network interface is not up\n");
		return -2;
	}

	/* indicate that the driver is active now*/
	active = TRUE;

	/* If interrupts are not used we immediately add the polling function
     * to the queue which would otherwise be done through the IRQ handler.
     */
	mmnif_device_schedule();

	/* Start the device worker thread wich actually processes the incoming
	 * packet's this is not done in the "interrupt handler" to shorten them up
	 */
	if (!instant_process)
		mmnif_worker_schedule();

#ifdef DEBUG_MMNIF
	DEBUGPRINTF("mmnif_dev is open\n");
#endif

	return 0;

}

/*
 * close the interface should be called by kernel to close this interface and release resources
 * Note: it's temporarly empty. Support will be added.
 */
int mmnif_close()
{
	mmnif_t* mmnif;

	if (!mmnif_dev)
	{
		DEBUGPRINTF("mmnif_close(): you closed the device before it was properly opened -.-* \n");
	}

	mmnif = (mmnif_t*)mmnif_dev->state;
	/* indicate that the driver is not active anymore 
	 * - this will stop the polling thread i.e.
	 */

	/* resources has to be freed here
	 * will be added soon ;)
	 */

	active = FALSE;
#ifdef WIN32
	free(mmnif->tx_buff);
	free(mmnif_dev);
	VirtualFree(mpb_start_address,NULL,NULL);
#else
	kfree(mmnif->tx_buff[0],MMNIF_TX_QUEUELEN * MMNIF_TX_BUFFERLEN);
	kfree(mmnif_dev,sizeof(mmnif_t));
	RCCE_shfree(mpb_start_address);
#endif
	return NULL;
}