1127 lines
29 KiB
C
1127 lines
29 KiB
C
/*
|
|
* mmnif.c --- memmory mapped interface
|
|
*
|
|
* Virutal IP Interface for the concept processor SCC
|
|
*
|
|
* virutally tested under Windows 7
|
|
*
|
|
* Carl-Benedikt Krüger 2011
|
|
*
|
|
*
|
|
* EXPERIMENTAL VERSION
|
|
*/
|
|
|
|
|
|
#include "mmnif.h" /* definitions */
|
|
|
|
#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()*/
|
|
#if defined(CONFIG_LWIP) && defined(CONFIG_ROCKCREEK)
|
|
|
|
#ifdef WIN32
|
|
|
|
#define kmalloc malloc
|
|
#define kfree(x,y) free(x)
|
|
#define RCCE_shfree(x) VirtualFree(x,NULL,NULL);
|
|
#define RCCE_shmalloc(x) VirtualAlloc((char*)0x41000000,x,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
|
|
#include "mailbox.h" /* mailbox_ptr_t */
|
|
#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;
|
|
extern HANDLE remote_process_mutex;
|
|
extern HANDLE own_process_mutex;
|
|
/* HANDLE to the other Process (for WPM and RPM)*/
|
|
extern HANDLE hProc;
|
|
#define DEBUGPRINTF(x,...) printf(x,__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#include <metalsvm/mailbox.h> /* mailbox_ptr_t */
|
|
|
|
#include <metalsvm/semaphore.h>
|
|
#include <metalsvm/spinlock.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>
|
|
|
|
#define DEBUGPRINTF(x,...) kprintf(x,##__VA_ARGS__)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define DEBUG_MMNIF
|
|
//#define DEBUG_MMNIF_PACKET
|
|
|
|
#ifdef DEBUG_MMNIF
|
|
#include "util.h" /* hex dump */
|
|
#endif
|
|
|
|
/* define constants
|
|
* regarding the driver & its configuration
|
|
*/
|
|
|
|
#define MMNIF_TX_BUFFERLEN 1792
|
|
#define MMNIF_TX_QUEUELEN 4
|
|
|
|
#define MMNIF_RX_BUFFERLEN 8192
|
|
#define MMNIF_MAX_DESCRIPTORS 32
|
|
|
|
#define MMNIF_CORES 48
|
|
|
|
#define MMNIF_POLL_BUDGET 0x100000
|
|
|
|
#define MMNIF_STATUS_FREE 0x00
|
|
#define MMNIF_STATUS_PENDING 0x01
|
|
#define MMNIF_STATUS_RDY 0x02
|
|
#define MMNIF_STATUS_INPROC 0x03
|
|
#define MMNIF_STATUS_PROC 0x04
|
|
|
|
|
|
/* 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;
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/* receive descror structure */
|
|
typedef struct rx_desc
|
|
{
|
|
uint8_t stat;
|
|
uint16_t len;
|
|
uint32_t addr;
|
|
|
|
} rx_desc_t;
|
|
|
|
/* receive ring buffer structure */
|
|
typedef struct mm_rx_buffer
|
|
{
|
|
/* iv_intr: inform via interrupt
|
|
* states whether the interface wants to recieve an interrupt for
|
|
* incoming packet
|
|
*/
|
|
uint16_t iv_intr;
|
|
/* memory "pseudo-ring/heap"
|
|
* packets are always in one single chunk of memory
|
|
* head : head of allocated memory region
|
|
* tail : tail of allocated memory region
|
|
*/
|
|
uint16_t head;
|
|
uint16_t tail;
|
|
spinlock_t rlock;
|
|
/* descritpor queue
|
|
* desc_table : descriptor table
|
|
* dcount : descriptor's free in queue
|
|
* dread : next descriptor to read
|
|
* dwrite : next descriptor to write
|
|
* dlock : lock to protect these members
|
|
*/
|
|
rx_desc_t desc_table[MMNIF_MAX_DESCRIPTORS];
|
|
uint8_t dcount;
|
|
uint8_t dread;
|
|
uint8_t dwrite;
|
|
spinlock_t dlock;
|
|
} 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;
|
|
|
|
/* lock to protect members
|
|
*/
|
|
spinlock_t lock;
|
|
|
|
/*
|
|
*/
|
|
sem_t com_poll;
|
|
|
|
}mmnif_t;
|
|
|
|
|
|
#ifdef WIN32
|
|
__inline int RCCE_ue(void){
|
|
#ifndef RECV
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* memory maped interface helper functions
|
|
*/
|
|
#ifdef WIN32
|
|
#define __MEMCPY(x,y,z) memcpy(x,y,z)
|
|
#else
|
|
#define __MEMCPY(x,y,z) memcpy(x,y,z)
|
|
#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
|
|
}
|
|
|
|
/* trigger an interrupt on the remote processor
|
|
* so he knows there is a packet to read
|
|
*/
|
|
__inline int mmnif_trigger_irq(dest_ip)
|
|
{
|
|
#ifdef WIN32
|
|
return SetEvent(remote_process_event);
|
|
#else
|
|
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_get_device_stats(): Returns a copy of the
|
|
* current device
|
|
*/
|
|
mmnif_device_stats_t mmnif_get_device_stats(void)
|
|
{
|
|
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(void)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* mmnif_rxbuff_alloc():
|
|
* this function allocates a continues chunk of memory
|
|
* right inside of the buffer which is used for communication
|
|
* with the remote end
|
|
*/
|
|
uint32_t mmnif_rxbuff_alloc(uint8_t dest,uint16_t len)
|
|
{
|
|
mm_rx_buffer_t* rb = (mm_rx_buffer_t*)((char*)mpb_start_address + ( dest -1 ) * mpb_size);
|
|
char* memblock = (char*)rb + sizeof(mm_rx_buffer_t);
|
|
|
|
uint32_t ret;
|
|
|
|
spinlock_lock(&rb->dlock);
|
|
|
|
if (rb->dcount)
|
|
{
|
|
if (rb->tail > rb->head)
|
|
{
|
|
if (MMNIF_RX_BUFFERLEN - rb->tail > len)
|
|
{
|
|
rb->desc_table[rb->dwrite].stat = MMNIF_STATUS_PENDING;
|
|
ret = memblock + rb->tail;
|
|
rb->desc_table[rb->dwrite].addr = ret;
|
|
rb->desc_table[rb->dwrite].len = len;
|
|
rb->dcount--;
|
|
rb->dwrite = (++rb->dwrite)%MMNIF_MAX_DESCRIPTORS;
|
|
|
|
rb->tail = (rb->tail + len);
|
|
spinlock_unlock(&rb->dlock);
|
|
return ret;
|
|
}
|
|
else if (rb->head > len)
|
|
{
|
|
rb->desc_table[rb->dwrite].stat = MMNIF_STATUS_PENDING;
|
|
ret = memblock;
|
|
rb->desc_table[rb->dwrite].addr = ret;
|
|
rb->desc_table[rb->dwrite].len = len;
|
|
rb->dcount--;
|
|
rb->dwrite = (++rb->dwrite)%MMNIF_MAX_DESCRIPTORS;
|
|
|
|
rb->tail = len;
|
|
spinlock_unlock(&rb->dlock);
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
spinlock_unlock(&rb->dlock);
|
|
return NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rb->head - rb->tail > len)
|
|
{
|
|
rb->desc_table[rb->dwrite].stat = MMNIF_STATUS_PENDING;
|
|
ret = memblock + rb->tail;
|
|
rb->desc_table[rb->dwrite].addr = ret;
|
|
rb->desc_table[rb->dwrite].len = len;
|
|
rb->dcount--;
|
|
rb->dwrite = (++rb->dwrite)%MMNIF_MAX_DESCRIPTORS;
|
|
|
|
rb->tail = (rb->tail + len);
|
|
spinlock_unlock(&rb->dlock);
|
|
return ret;
|
|
}
|
|
else if (rb->tail == rb->head)
|
|
{
|
|
if (MMNIF_RX_BUFFERLEN - rb->tail < len)
|
|
{
|
|
rb->tail = 0;
|
|
if (rb->dread == rb->dwrite)
|
|
rb->head = 0;
|
|
}
|
|
|
|
rb->desc_table[rb->dwrite].stat = MMNIF_STATUS_PENDING;
|
|
ret = memblock + rb->tail;
|
|
rb->desc_table[rb->dwrite].addr = ret;
|
|
rb->desc_table[rb->dwrite].len = len;
|
|
rb->dcount--;
|
|
rb->dwrite = (++rb->dwrite)%MMNIF_MAX_DESCRIPTORS;
|
|
|
|
rb->tail = (rb->tail + len);
|
|
spinlock_unlock(&rb->dlock);
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
spinlock_unlock(&rb->dlock);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
spinlock_unlock(&rb->dlock);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* mmnif_commit_packet: this function set the state of the (in advance)
|
|
* allocated packet to RDY so the recieve queue knows that it can be
|
|
* processed further
|
|
*/
|
|
int mmnif_commit_packet(uint8_t dest,uint32_t addr)
|
|
{
|
|
mm_rx_buffer_t* rb = (mm_rx_buffer_t*)((char*)mpb_start_address + ( dest -1 ) * mpb_size);
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < MMNIF_MAX_DESCRIPTORS; i++)
|
|
{
|
|
if (rb->desc_table[i].addr == addr && rb->desc_table[i].stat == MMNIF_STATUS_PENDING)
|
|
{
|
|
rb->desc_table[i].stat = MMNIF_STATUS_RDY;
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* mmnif_rxbuff_free() : the opposite to mmnif_rxbuff_alloc() a from the receiver
|
|
* already processed chunk of memory is freed so that it can be allocated again
|
|
*/
|
|
void mmnif_rxbuff_free(void)
|
|
{
|
|
mmnif_t* mmnif = mmnif_dev->state;
|
|
mm_rx_buffer_t* b = mmnif->rx_buff;
|
|
uint32_t i,j;
|
|
uint32_t rpos;
|
|
|
|
spinlock_lock(&b->dlock);
|
|
|
|
rpos = b->dread;
|
|
|
|
for (i = 0, j = rpos; i < MMNIF_MAX_DESCRIPTORS; i++)
|
|
{
|
|
j = (j+i)%MMNIF_MAX_DESCRIPTORS;
|
|
|
|
if (b->desc_table[j].stat == MMNIF_STATUS_PROC)
|
|
{
|
|
b->dcount++;
|
|
b->dread = (b->dread +1)%MMNIF_MAX_DESCRIPTORS;
|
|
b->desc_table[j].stat = MMNIF_STATUS_FREE;
|
|
|
|
if (b->tail > b->head)
|
|
{
|
|
b->head += b->desc_table[j].len;
|
|
}
|
|
else
|
|
{
|
|
if ( (b->desc_table[(j+1)%MMNIF_MAX_DESCRIPTORS].stat != MMNIF_STATUS_FREE )
|
|
&& ( b->desc_table[j].addr > b->desc_table[(j+1)%MMNIF_MAX_DESCRIPTORS].addr))
|
|
{
|
|
b->head = 0;
|
|
|
|
}
|
|
else
|
|
{
|
|
b->head += b->desc_table[j].len;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
spinlock_unlock(&b->dlock);
|
|
}
|
|
/*
|
|
* 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;
|
|
|
|
uint32_t write_address;
|
|
uint32_t i;
|
|
struct pbuf* q; /* interator */
|
|
|
|
uint8_t build_buff = TRUE;
|
|
uint32_t dest_ip = mmnif_get_destination(netif,p);
|
|
|
|
mm_rx_buffer_t* rb = (mm_rx_buffer_t*)((char*)mpb_start_address + ( dest_ip -1 ) * mpb_size);
|
|
|
|
#ifdef WIN32
|
|
ReadProcessMemory(hProc, (char*)mpb_start_address + ( dest_ip -1 ) * mpb_size, (char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,mpb_size,NULL);
|
|
WaitForSingleObject(remote_process_mutex,INFINITE);
|
|
#endif
|
|
|
|
spinlock_lock(&mmnif->lock);
|
|
mmnif->tx_queue++;
|
|
spinlock_unlock(&mmnif->lock);
|
|
|
|
|
|
/* Perform serveral sanity checks on the packet and the buffers:
|
|
* - is the queue full?
|
|
* - is the output packet to big?
|
|
*/
|
|
|
|
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 %d bytes\n",MMNIF_TX_BUFFERLEN);
|
|
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)
|
|
{
|
|
/* 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 = 0; q != 0; q = q->next)
|
|
{
|
|
memcpy(mmnif->tx_buff[slot] + i, q->payload, q->len);
|
|
i += q->len;
|
|
}
|
|
}
|
|
|
|
/* allocate memory for the packet in the remote buffer */
|
|
|
|
write_address = mmnif_rxbuff_alloc(dest_ip,p->tot_len);
|
|
|
|
if (!write_address)
|
|
goto drop_packet;
|
|
|
|
/* 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)
|
|
memcpy(write_address,mmnif->tx_buff[slot],p->tot_len);
|
|
else
|
|
memcpy(write_address,p->payload,p->tot_len);
|
|
|
|
|
|
if (mmnif_commit_packet(dest_ip,write_address))
|
|
{
|
|
DEBUGPRINTF("mmnif_tx(): packet somehow lost during commit\n");
|
|
}
|
|
|
|
#ifdef DEBUG_MMNIF_PACKET
|
|
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
|
|
|
|
/* release the tx_queue because it's finished */
|
|
spinlock_lock(&mmnif->lock);
|
|
mmnif->tx_queue--;
|
|
spinlock_unlock(&mmnif->lock);
|
|
|
|
/* just gather some stats */
|
|
LINK_STATS_INC(link.xmit);
|
|
|
|
mmnif->stats.tx++;
|
|
mmnif->stats.tx_bytes += p->tot_len;
|
|
|
|
#ifdef WIN32
|
|
WriteProcessMemory(hProc, (char*)mpb_start_address + ( dest_ip -1 ) * mpb_size, (char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,mpb_size,NULL);
|
|
SetEvent(remote_process_event);
|
|
ReleaseMutex(remote_process_mutex);
|
|
#endif
|
|
|
|
if (rb->iv_intr)
|
|
mmnif_trigger_irq(dest_ip);
|
|
|
|
return ERR_OK;
|
|
|
|
drop_packet:
|
|
/* drop packet for one or another reason
|
|
*/
|
|
spinlock_lock(&mmnif->lock);
|
|
mmnif->tx_queue--;
|
|
spinlock_unlock(&mmnif->lock);
|
|
|
|
LINK_STATS_INC(link.drop);
|
|
mmnif->stats.tx_err++;
|
|
|
|
#ifdef WIN32
|
|
WriteProcessMemory(hProc, (char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,mpb_size,NULL);
|
|
#endif
|
|
|
|
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;
|
|
int num = 0;
|
|
|
|
/* Alloc and clear memory for the device struct
|
|
*/
|
|
mmnif = kmalloc(sizeof(mmnif_t));
|
|
|
|
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
|
|
*/
|
|
mpb_size = (sizeof(mm_rx_buffer_t) + MMNIF_RX_BUFFERLEN);
|
|
mpb_start_address = RCCE_shmalloc(mpb_size*MMNIF_CORES);
|
|
|
|
mmnif->rx_buff = mpb_start_address + (mpb_size) * (own_ip_address - router_ip_address);
|
|
if (!(mpb_start_address))
|
|
{
|
|
DEBUGPRINTF("mmnif init(): allocating shared memory failed\n");
|
|
return ERR_MEM;
|
|
}
|
|
memset(mmnif->rx_buff, 0, mpb_size);
|
|
|
|
/* set initial values
|
|
*/
|
|
mmnif->rx_buff->dcount = MMNIF_MAX_DESCRIPTORS;
|
|
|
|
/* init the lock's for the hdr
|
|
*/
|
|
|
|
spinlock_init(&mmnif->rx_buff->rlock);
|
|
spinlock_init(&mmnif->rx_buff->dlock);
|
|
|
|
spinlock_init(&mmnif->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,0);
|
|
|
|
/* inform via interrupt should be the dafault
|
|
*/
|
|
if (!no_irq)
|
|
mmnif->rx_buff->iv_intr = TRUE;
|
|
|
|
/* Alloc and clear internal memory for tx_buff
|
|
*/
|
|
mmnif->tx_buff[0] = kmalloc(MMNIF_TX_QUEUELEN * MMNIF_TX_BUFFERLEN);
|
|
|
|
if (!(mmnif->tx_buff[0]))
|
|
{
|
|
DEBUGPRINTF("mmnif init: out of memory tx\n");
|
|
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;
|
|
|
|
/* pass the device state to lwip */
|
|
netif->state = mmnif;
|
|
mmnif_dev = netif;
|
|
|
|
/* 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_LINK_UP;
|
|
/* hardware address length */
|
|
netif->hwaddr_len = 6;
|
|
|
|
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;
|
|
mm_rx_buffer_t* b = mmnif->rx_buff;
|
|
|
|
uint16_t length;
|
|
struct pbuf* p = NULL;
|
|
struct pbuf* q;
|
|
char* packet;
|
|
|
|
uint32_t i,j;
|
|
uint8_t rdesc = 0xFF;
|
|
|
|
err_t err = NULL;
|
|
|
|
#ifdef WIN32
|
|
ReadProcessMemory(hProc, (char*)mpb_start_address + ( !((own_ip_address && 0xFF)-1) ) * mpb_size, (char*)mpb_start_address + ( !((own_ip_address && 0xFF)-1) ) * mpb_size,mpb_size,NULL);
|
|
WaitForSingleObject(own_process_mutex,INFINITE);
|
|
#endif
|
|
|
|
spinlock_lock(&b->rlock);
|
|
|
|
/* check if this call to mmnif_rx makes any sense
|
|
*/
|
|
if (b->desc_table[b->dread].stat == MMNIF_STATUS_FREE)
|
|
{
|
|
spinlock_unlock(&b->rlock);
|
|
#ifdef WIN32
|
|
WriteProcessMemory(hProc, (char*)mpb_start_address + ( !((own_ip_address && 0xFF)-1) ) * mpb_size, (char*)mpb_start_address + ( !((own_ip_address && 0xFF)-1) ) * mpb_size,mpb_size,NULL);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/* search the packet whose transmission is finished
|
|
*/
|
|
for (i = 0,j = b->dread; i < MMNIF_MAX_DESCRIPTORS; i++)
|
|
{
|
|
if (b->desc_table[(j + i)% MMNIF_MAX_DESCRIPTORS].stat == MMNIF_STATUS_RDY)
|
|
{
|
|
rdesc = (j + i)% MMNIF_MAX_DESCRIPTORS;
|
|
b->desc_table[rdesc].stat = MMNIF_STATUS_INPROC;
|
|
packet = (char*)b->desc_table[rdesc].addr;
|
|
length = b->desc_table[rdesc].len;
|
|
break;
|
|
}
|
|
}
|
|
|
|
spinlock_unlock(&b->rlock);
|
|
|
|
/* if there is no packet finished we encountered a random error
|
|
*/
|
|
if (rdesc == 0xFF)
|
|
{
|
|
#ifdef WIN32
|
|
WriteProcessMemory(hProc, (char*)mpb_start_address + ( !((own_ip_address && 0xFF)-1) ) * mpb_size, (char*)mpb_start_address + ( !((own_ip_address && 0xFF)-1) ) * mpb_size,mpb_size,NULL);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/* If length is zero return silently
|
|
*/
|
|
if (length == 0)
|
|
{
|
|
DEBUGPRINTF("mmnif_rx(): empty packet error\n");
|
|
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_PACKET
|
|
DEBUGPRINTF("\n RECIEVED - 0x%.8X with legth: %d\n",packet,length);
|
|
hex_dump(length,packet);
|
|
#endif
|
|
|
|
/* 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,&packet[i],q->len);
|
|
i +=q->len;
|
|
}
|
|
|
|
/* indicate that the copy process is done and the packet can be freed
|
|
* note that we did not lock here because we are the only one editing this value
|
|
*/
|
|
mmnif->rx_buff->desc_table[rdesc].stat = MMNIF_STATUS_PROC;
|
|
|
|
/* everything is copied to a new buffer so it's save to release
|
|
* the old one for new incoming packets
|
|
*/
|
|
|
|
mmnif_rxbuff_free();
|
|
|
|
/* full packet send to tcpip_thread to process */
|
|
if ((err = mmnif_dev->input(p, mmnif_dev)) != ERR_OK)
|
|
{
|
|
DEBUGPRINTF("mmnif_rx: IP input error\n");
|
|
pbuf_free(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++;
|
|
|
|
#ifdef WIN32
|
|
WriteProcessMemory(hProc, (char*)mpb_start_address + ( !((own_ip_address && 0xFF)-1) ) * mpb_size, (char*)mpb_start_address + ( !((own_ip_address && 0xFF)-1) ) * mpb_size,mpb_size,NULL);
|
|
ReleaseMutex(own_process_mutex);
|
|
#endif
|
|
|
|
return;
|
|
|
|
drop_packet:
|
|
|
|
spinlock_lock(&mmnif->rx_buff->rlock);
|
|
/*error handling*/
|
|
spinlock_unlock(&mmnif->rx_buff->rlock);
|
|
|
|
LINK_STATS_INC(link.drop);
|
|
mmnif->stats.rx_err++;
|
|
|
|
#ifdef WIN32
|
|
WriteProcessMemory(hProc, (char*)mpb_start_address + ( !((own_ip_address && 0xFF)-1) ) * mpb_size, (char*)mpb_start_address + ( !((own_ip_address && 0xFF)-1) ) * mpb_size,mpb_size,NULL);
|
|
#endif
|
|
return;
|
|
}
|
|
/* mmnif_irqhandler():
|
|
* handles the incomint interrupts
|
|
*/
|
|
void mmnif_irqhandler(void)
|
|
{
|
|
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->dcount < MMNIF_MAX_DESCRIPTORS)
|
|
{
|
|
mmnif_rx(mmnif_dev);
|
|
|
|
// if (instant_process)
|
|
// mmnif_wait(mmnif_dev,1,MMNIF_WORKER_BUDGET);
|
|
}
|
|
}
|
|
/*
|
|
* the poll function wich is used if no interrupt wake up our mmnif_rx functions
|
|
*/
|
|
int mmnif_poll(void* e)
|
|
{
|
|
mmnif_t* mmnif;
|
|
|
|
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("mmnif_poll(): polling thread launched",mmnif->rx_buff);
|
|
#endif
|
|
|
|
if (!no_irq)
|
|
{
|
|
sem_wait(&mmnif->com_poll,0);
|
|
}
|
|
|
|
/*run while driver is up*/
|
|
while (active)
|
|
{
|
|
while (!mmnif->rx_buff->dcount == MMNIF_MAX_DESCRIPTORS)
|
|
{
|
|
mmnif->stats.pll_empty++;
|
|
if (mmnif->stats.pll_empty >= MMNIF_POLL_BUDGET)
|
|
{
|
|
/* enable interrupts and suspend polling
|
|
*
|
|
*/
|
|
mmnif->rx_buff->iv_intr = TRUE;
|
|
mmnif->stats.pll_empty = 0;
|
|
#ifdef DEBUG_MMNIF
|
|
DEBUGPRINTF("mmnif_poll(): heuristical interrupts enabled\n");
|
|
#endif
|
|
sem_wait(&mmnif->com_poll,0);
|
|
}
|
|
/* uncomment this to test only polling
|
|
*/
|
|
// mmnif->stats.pll_empty = 0;
|
|
}
|
|
|
|
mmnif->stats.pll_empty=0;
|
|
mmnif_rx(mmnif_dev);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
/*
|
|
* Open the interface should be called by kernel to use this network interface
|
|
*/
|
|
int mmnif_open(void)
|
|
{
|
|
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,RCCE_ue() +1);
|
|
IP4_ADDR(&netmask, 255,255,255,0);
|
|
|
|
own_ip_address+= RCCE_ue() +1;
|
|
|
|
mmnif_dev = kmalloc(sizeof(struct netif));
|
|
|
|
/* 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))
|
|
{
|
|
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();
|
|
|
|
|
|
// mmnif_retrigger_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(void)
|
|
{
|
|
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.
|
|
*/
|
|
|
|
active = FALSE;
|
|
|
|
kfree(mmnif->tx_buff[0],MMNIF_TX_QUEUELEN * MMNIF_TX_BUFFERLEN);
|
|
kfree(mmnif_dev,sizeof(mmnif_t));
|
|
RCCE_shfree(mpb_start_address);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|