several changes, stats, optimizing copy process etc
This commit is contained in:
parent
8c6e679ba0
commit
243a2b4f92
1 changed files with 179 additions and 21 deletions
|
@ -59,6 +59,8 @@ extern HANDLE hProc;
|
||||||
|
|
||||||
#define MMNIF_CORES 2
|
#define MMNIF_CORES 2
|
||||||
|
|
||||||
|
#define MMNIF_WORKER_BUDGET 5
|
||||||
|
|
||||||
/* decide whether it's polling mode or not
|
/* decide whether it's polling mode or not
|
||||||
*/
|
*/
|
||||||
static int no_irq = 0;
|
static int no_irq = 0;
|
||||||
|
@ -86,10 +88,24 @@ unsigned int mpb_size = NULL;
|
||||||
|
|
||||||
|
|
||||||
/* Register offset for the CONFIG and LOCK registers */
|
/* Register offset for the CONFIG and LOCK registers */
|
||||||
|
#define RCK_GLCFG0 0x10
|
||||||
|
#define RCK_GLCFG1 0x18
|
||||||
#define RCK_TILEID 0x100
|
#define RCK_TILEID 0x100
|
||||||
|
|
||||||
|
#define RCK_TILE_SIZE 0x01000000
|
||||||
|
|
||||||
/* Start address of the local register bank */
|
/* Start address of the local register bank */
|
||||||
static int local_crb = 0xF8000000;
|
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
|
* the memory mapped network device
|
||||||
|
@ -111,11 +127,21 @@ typedef struct mmnif_device_stats
|
||||||
unsigned int rx_err;
|
unsigned int rx_err;
|
||||||
unsigned int rx;
|
unsigned int rx;
|
||||||
|
|
||||||
unsigned int rx_intr;
|
unsigned int rx_bytes;
|
||||||
unsigned int rx_poll;
|
|
||||||
|
|
||||||
unsigned int tx_err;
|
unsigned int tx_err;
|
||||||
unsigned int tx;
|
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;
|
} mmnif_device_stats_t;
|
||||||
|
|
||||||
typedef struct mm_rx_buffer
|
typedef struct mm_rx_buffer
|
||||||
|
@ -123,6 +149,7 @@ typedef struct mm_rx_buffer
|
||||||
/* memory rx buffer build
|
/* memory rx buffer build
|
||||||
* - queued : how many packets are in the queue
|
* - queued : how many packets are in the queue
|
||||||
* - pos : which is the next packet to be worked on
|
* - pos : which is the next packet to be worked on
|
||||||
|
* - pending : how many packets are pending
|
||||||
* - iv_intr : inform via interrupt or not
|
* - iv_intr : inform via interrupt or not
|
||||||
* - lock: semaphore to lock the local variables to be multi access save
|
* - lock: semaphore to lock the local variables to be multi access save
|
||||||
*
|
*
|
||||||
|
@ -135,6 +162,7 @@ typedef struct mm_rx_buffer
|
||||||
*/
|
*/
|
||||||
uint8_t queued;
|
uint8_t queued;
|
||||||
uint8_t pos;
|
uint8_t pos;
|
||||||
|
uint8_t pending;
|
||||||
|
|
||||||
uint8_t iv_intr;
|
uint8_t iv_intr;
|
||||||
|
|
||||||
|
@ -165,6 +193,9 @@ typedef struct mmnif
|
||||||
uint8_t tx_queue;
|
uint8_t tx_queue;
|
||||||
uint8_t* tx_buff[MMNIF_TX_QUEUELEN];
|
uint8_t* tx_buff[MMNIF_TX_QUEUELEN];
|
||||||
mm_rx_buffer_t* rx_buff;
|
mm_rx_buffer_t* rx_buff;
|
||||||
|
|
||||||
|
/* config register banks */
|
||||||
|
void* crb[MMNIF_CORES];
|
||||||
|
|
||||||
/* comunication mailbox
|
/* comunication mailbox
|
||||||
*/
|
*/
|
||||||
|
@ -243,14 +274,14 @@ __inline uint8_t mmnif_read_rx_pos(uint32_t dest_ip)
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/* read the pos value from the remote buffer
|
/* read the inv_intr from the remote buffer
|
||||||
* and return it.
|
* and return it.
|
||||||
*/
|
*/
|
||||||
__inline uint8_t mmnif_read_rx_inv_intr(uint32_t dest_ip)
|
__inline uint8_t mmnif_read_rx_inv_intr(uint32_t dest_ip)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
/* Read the value of the forgein process
|
/* Read the value of the forgein process
|
||||||
* form rx_buff->pos
|
* form rx_buff->inv_intr
|
||||||
*/
|
*/
|
||||||
mm_rx_buffer_t hdr;
|
mm_rx_buffer_t hdr;
|
||||||
while(!ReadProcessMemory(hProc,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,&hdr,sizeof(hdr),NULL));
|
while(!ReadProcessMemory(hProc,(char*)mpb_start_address + ( dest_ip -1 ) * mpb_size,&hdr,sizeof(hdr),NULL));
|
||||||
|
@ -260,6 +291,23 @@ __inline uint8_t mmnif_read_rx_inv_intr(uint32_t dest_ip)
|
||||||
#endif
|
#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
|
/* write data to the remote buffer
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -318,6 +366,26 @@ __inline int mmnif_write_rx_queue(uint32_t dest_ip,uint8_t queue)
|
||||||
#endif
|
#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 ,&queue,1);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
/* trigger an interrupt on the remote processor
|
/* trigger an interrupt on the remote processor
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -329,7 +397,33 @@ __inline int mmnif_trigger_irq(dest_ip)
|
||||||
/* NOTE: have to check how the remote interrupt managment works
|
/* NOTE: have to check how the remote interrupt managment works
|
||||||
* on the SCC
|
* on the SCC
|
||||||
*/
|
*/
|
||||||
return NULL;
|
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 = inportl((void*)irq_address);
|
||||||
|
|
||||||
|
if (!pulse_irq) && (value & RCK_INTR_MASK))
|
||||||
|
{
|
||||||
|
value &= (~(RCK_INTR_MASK|RCK_NMI_MASK));
|
||||||
|
outportl((void*)irq_address,value);
|
||||||
|
}
|
||||||
|
|
||||||
|
value |= RCK_INTR_MASK;
|
||||||
|
outportl((void*) irq_address,value);
|
||||||
|
|
||||||
|
/**/
|
||||||
|
if (pulse_irq)
|
||||||
|
{
|
||||||
|
value &= (~(RCK_INTR_MASK|NMI_MASK));
|
||||||
|
outportl((void*)irq_address,value);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -430,17 +524,15 @@ __inline int mmnif_timestamp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* mmnif_get_device_stats():
|
/* mmnif_get_device_stats(): Returns a copy of the
|
||||||
*
|
* current device
|
||||||
*/
|
*/
|
||||||
mmnif_device_stats_t mmnif_get_device_stats()
|
mmnif_device_stats_t mmnif_get_device_stats()
|
||||||
{
|
{
|
||||||
mmnif_device_stats_t stats = {0};
|
mmnif_device_stats_t stats = {0};
|
||||||
|
|
||||||
if(!mmnif_dev)
|
if(!mmnif_dev)
|
||||||
{
|
|
||||||
DEBUGPRINTF("mmnif_get_device_stats(): the device is not initialized yet.");
|
DEBUGPRINTF("mmnif_get_device_stats(): the device is not initialized yet.");
|
||||||
}
|
|
||||||
else
|
else
|
||||||
stats = ((mmnif_t*)mmnif_dev->state)->stats;
|
stats = ((mmnif_t*)mmnif_dev->state)->stats;
|
||||||
|
|
||||||
|
@ -448,8 +540,8 @@ mmnif_device_stats_t mmnif_get_device_stats()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* mmnif_print_stats():
|
/* mmnif_print_stats(): Print the devices stats of the
|
||||||
*
|
* current device
|
||||||
*/
|
*/
|
||||||
void mmnif_print_stats()
|
void mmnif_print_stats()
|
||||||
{
|
{
|
||||||
|
@ -465,10 +557,12 @@ void mmnif_print_stats()
|
||||||
|
|
||||||
DEBUGPRINTF("/dev/mmnif - stats: \n");
|
DEBUGPRINTF("/dev/mmnif - stats: \n");
|
||||||
DEBUGPRINTF("Received: %d packets successfull\n",mmnif->stats.rx);
|
DEBUGPRINTF("Received: %d packets successfull\n",mmnif->stats.rx);
|
||||||
|
DEBUGPRINTF("Received: %d bytes",mmnif->stats.rx_bytes);
|
||||||
DEBUGPRINTF("interrupts: %d\n",mmnif->stats.rx_intr);
|
DEBUGPRINTF("interrupts: %d\n",mmnif->stats.rx_intr);
|
||||||
DEBUGPRINTF("polling: %d\n",mmnif->stats.rx_poll);
|
DEBUGPRINTF("polling: %d\n",mmnif->stats.rx_poll);
|
||||||
DEBUGPRINTF("Received: %d packets containuing errors\n",mmnif->stats.rx_err);
|
DEBUGPRINTF("Received: %d packets containuing errors\n",mmnif->stats.rx_err);
|
||||||
DEBUGPRINTF("Transmitted: %d packests successfull",mmnif->stats.tx);
|
DEBUGPRINTF("Transmitted: %d packests successfull",mmnif->stats.tx);
|
||||||
|
DEBUGPRINTF("Transmitted: %d bytes",mmnif->stats.tx_bytes);
|
||||||
DEBUGPRINTF("Transmitted: %d packests were dropped due to errors",mmnif->stats.tx_err);
|
DEBUGPRINTF("Transmitted: %d packests were dropped due to errors",mmnif->stats.tx_err);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -530,10 +624,11 @@ err_t mmnif_tx(struct netif* netif, struct pbuf* p)
|
||||||
{
|
{
|
||||||
mmnif_t* mmnif = netif->state;
|
mmnif_t* mmnif = netif->state;
|
||||||
uint8_t slot = mmnif->tx_queue;
|
uint8_t slot = mmnif->tx_queue;
|
||||||
uint32_t i;
|
|
||||||
struct pbuf* q; /* interator */
|
|
||||||
uint8_t queued;
|
uint8_t queued;
|
||||||
uint8_t pos;
|
uint8_t pos;
|
||||||
|
uint8_t pending;
|
||||||
|
uint32_t i;
|
||||||
|
struct pbuf* q; /* interator */
|
||||||
uint8_t build_buff = TRUE;
|
uint8_t build_buff = TRUE;
|
||||||
uint8_t dest_intr = FALSE;
|
uint8_t dest_intr = FALSE;
|
||||||
uint32_t dest_ip = mmnif_get_destination(netif,p);
|
uint32_t dest_ip = mmnif_get_destination(netif,p);
|
||||||
|
@ -593,26 +688,51 @@ err_t mmnif_tx(struct netif* netif, struct pbuf* p)
|
||||||
}
|
}
|
||||||
/* get the palce the router core is looking for the packet */
|
/* 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);
|
mmnif_lock_rx_hdr(dest_ip);
|
||||||
|
|
||||||
|
/* read and edit needed values */
|
||||||
queued = mmnif_read_rx_queue(dest_ip);
|
queued = mmnif_read_rx_queue(dest_ip);
|
||||||
pos = mmnif_read_rx_pos(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);
|
mmnif_unlock_rx_hdr(dest_ip);
|
||||||
|
|
||||||
pos = (pos + queued) % MMNIF_RX_QUEUELEN;
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
/* write buffer to buffer & increment the queued packet count */
|
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)
|
if (build_buff)
|
||||||
mmnif_write_rx_buff(dest_ip, pos, mmnif->tx_buff[slot]);
|
mmnif_write_rx_buff(dest_ip, pos, mmnif->tx_buff[slot]);
|
||||||
else
|
else
|
||||||
mmnif_write_rx_buffl(dest_ip, pos, p->payload,p->tot_len);
|
mmnif_write_rx_buffl(dest_ip, pos, p->payload,p->tot_len);
|
||||||
|
|
||||||
queued++;
|
/* like above ensure we are the only ones editing the hdr */
|
||||||
|
|
||||||
mmnif_lock_rx_hdr(dest_ip);
|
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_queue(dest_ip, queued);
|
||||||
|
mmnif_write_rx_pending(dest_ip, pending);
|
||||||
|
|
||||||
mmnif_unlock_rx_hdr(dest_ip);
|
mmnif_unlock_rx_hdr(dest_ip);
|
||||||
|
|
||||||
|
@ -622,7 +742,7 @@ err_t mmnif_tx(struct netif* netif, struct pbuf* p)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* if driver is not in polling mode inform core that a message has arrived */
|
/* if driver is not in polling mode inform core that a message has arrived */
|
||||||
if (!no_irq)
|
if (dest_intr)
|
||||||
mmnif_trigger_irq(dest_ip);
|
mmnif_trigger_irq(dest_ip);
|
||||||
|
|
||||||
/* free the transmid queue*/
|
/* free the transmid queue*/
|
||||||
|
@ -637,6 +757,7 @@ err_t mmnif_tx(struct netif* netif, struct pbuf* p)
|
||||||
LINK_STATS_INC(link.xmit);
|
LINK_STATS_INC(link.xmit);
|
||||||
|
|
||||||
mmnif->stats.tx++;
|
mmnif->stats.tx++;
|
||||||
|
mmnif->stats.tx_bytes += p->tot_len;
|
||||||
|
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
|
|
||||||
|
@ -697,8 +818,21 @@ err_t mmnif_init(struct netif* netif)
|
||||||
}
|
}
|
||||||
memset(mmnif->rx_buff, 0, (sizeof(mm_rx_buffer_t) + MMNIF_RX_QUEUELEN* MMNIF_RX_BUFFERLEN)* (MMNIF_CORES-1));
|
memset(mmnif->rx_buff, 0, (sizeof(mm_rx_buffer_t) + MMNIF_RX_QUEUELEN* MMNIF_RX_BUFFERLEN)* (MMNIF_CORES-1));
|
||||||
|
|
||||||
|
/* init the lock for the hdr
|
||||||
|
*/
|
||||||
sem_init(&mmnif->rx_buff->lock,1);
|
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
|
/* Alloc and clear internal memory for tx_buff
|
||||||
*/
|
*/
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
@ -851,6 +985,8 @@ static void mmnif_rx(struct netif* netif)
|
||||||
LINK_STATS_INC(link.xmit);
|
LINK_STATS_INC(link.xmit);
|
||||||
mmnif->stats.rx++;
|
mmnif->stats.rx++;
|
||||||
|
|
||||||
|
mmnif->stats.rx_bytes += p->tot_len;
|
||||||
|
|
||||||
if (no_irq)
|
if (no_irq)
|
||||||
mmnif->stats.rx_poll++;
|
mmnif->stats.rx_poll++;
|
||||||
else
|
else
|
||||||
|
@ -877,7 +1013,22 @@ static int mmnif_wait(struct netif* netif, uint32_t poll, int budget)
|
||||||
struct pbuf* p = NULL;
|
struct pbuf* p = NULL;
|
||||||
int err = ERR_OK;
|
int err = ERR_OK;
|
||||||
unsigned int npackets = 0;
|
unsigned int npackets = 0;
|
||||||
unsigned int quota = budget > mmnif->rx_buff->queued ? mmnif->rx_buff->queued : budget;
|
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 */
|
/* process up to quota packets from the receive queue */
|
||||||
while (npackets <= quota)
|
while (npackets <= quota)
|
||||||
|
@ -921,7 +1072,7 @@ static int mmnif_wait(struct netif* netif, uint32_t poll, int budget)
|
||||||
int mmnif_worker(void* e)
|
int mmnif_worker(void* e)
|
||||||
{
|
{
|
||||||
while (active)
|
while (active)
|
||||||
mmnif_wait(mmnif_dev,0,5);
|
mmnif_wait(mmnif_dev,0,MMNIF_WORKER_BUDGET);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -946,11 +1097,18 @@ int mmnif_poll(void* e)
|
||||||
/*run while driver is up*/
|
/*run while driver is up*/
|
||||||
while (active)
|
while (active)
|
||||||
{
|
{
|
||||||
while (mmnif->rx_buff->queued)
|
while (!mmnif->rx_buff->queued)
|
||||||
{
|
{
|
||||||
tmp32 = mmnif_timestamp();
|
tmp32 = mmnif_timestamp();
|
||||||
diff = diff - tmp32 > 0 ? diff - tmp32 : tmp32 - diff;
|
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);
|
// Sleep(10);
|
||||||
mmnif_rx(mmnif_dev);
|
mmnif_rx(mmnif_dev);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue