/* * 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 /* mailbox_ptr_t */ #endif #include /* lwip netif */ #include /* inteface stats */ #include /* ethernet arp packets */ #include /* struct iphdr*/ #include /* tcpip_input()*/ #define DEBUG_MMNIF #ifdef DEBUG_MMNIF #include "util.h" /* hex dump */ #endif #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include #include 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 #include #include #include #include #include #include #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 5 /* decide whether it's polling mode or not */ static int no_irq = 1; /* 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 = 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; static int crb = 0xE0000000; /* just set and reset the irq */ static int pulse_irq = 1; /* 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 * - transmit errors * - transmit successes */ unsigned int rx_err; unsigned int rx; unsigned int rx_bytes; unsigned int tx_err; unsigned int tx; unsigned int tx_bytes; /* Heuristics : * */ 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 * * And this buffer needs a lock as soon as more as cores are availible =/ */ uint8_t queued; uint8_t pos; uint8_t pending; uint8_t iv_intr; sem_t lock; // 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; /* config register banks */ void* crb[MMNIF_CORES]; /* 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 return *(uint8_t*)(mpb_start_address + (dest_ip -1 ) * mpb_size ); #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 return *(uint8_t*)(mpb_start_address + (dest_ip -1 ) * mpb_size +1); #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 return *(uint8_t*)(mpb_start_address + (dest_ip -1 ) * mpb_size +2); #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 return *(uint8_t*)(mpb_start_address + (dest_ip -1 ) * mpb_size +2); #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(mpb_start_address + (dest_ip -1 ) * mpb_size +2,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, &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 /* 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 }; /* 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() { /* Right now every core has the same buffer for every incoming packet * this will be removed and a buffer for each Core will be implemented * * but i'm first testing with two cores/processes. */ #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); /* We choose a arbitrary address first * until i know how to properly allocate shared memory */ // RCCE_shmalloc_init(0x80000000,48*8192); mpb_start_address = RCCE_shmalloc(mpb_size*MMNIF_CORES); //mpb_start_address = 0x8000000+ mpb_size * (own_ip_address - router_ip_address); //SHMalloc(&mpb_start_address); //mpb_start_address = kmalloc(mpb_size*MMNIF_CORES); // mpb_start_address = 0xC0000000; 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); #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; sem_post(&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\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\n"); 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); mmnif_write_rx_queue(dest_ip, queued); mmnif_write_rx_pending(dest_ip, pending); mmnif_unlock_rx_hdr(dest_ip); #ifdef DEBUG_MMNIF DEBUGPRINTF("\n SEND 0x%.8X\n",(char*)mmnif->rx_buff + 2 + pos * 1792); hex_dump(p->tot_len +2, p->payload); #endif /* 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, sizeof(mm_rx_buffer_t) + MMNIF_RX_QUEUELEN* MMNIF_RX_BUFFERLEN); /* init the lock for the hdr */ sem_init(&mmnif->rx_buff->lock,1); /* inform via interrupt should be the dafault */ if (!no_irq) mmnif->rx_buff->iv_intr = TRUE; for (i = 0; i < MMNIF_CORES;i+=2) { mmnif->crb[i] = crb + RCK_TILE_SIZE*(i/2); mmnif->crb[i+1] = crb + RCK_TILE_SIZE*(i/2); } /* 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\n",data + pos); 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->rx_buff->pos++; mmnif->rx_buff->queued--; 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 (budget overflow too often) * { * enable polling and disable interrupts * } */ } /* 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(); unsigned int tmp32 = 0; 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 /*run while driver is up*/ while (active) { while (!mmnif->rx_buff->queued) { tmp32 = mmnif_timestamp(); diff = diff - tmp32 > 0 ? diff - tmp32 : tmp32 - diff; mmnif->stats.pll_empty++; /* if (too many emtpy runs) * { * enable interrupts and suspend polling * } */ } mmnif->stats.pll_empty--; // Sleep(10); mmnif_rx(mmnif_dev); } 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); } /* * 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,0,0,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. */ if (no_irq) 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 */ 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); #else // kfree(mmnif->tx_buff); // kfree(mmnif_dev); #endif return NULL; }