/*
 * Copyright (c) 2009 Ken Wong and Washington University in St. Louis.
 * All rights reserved
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *    3. The name of the author or Washington University may not be used 
 *       to endorse or promote products derived from this source code 
 *       without specific prior written permission.
 *    4. Conditions of any other entities that contributed to this are also
 *       met. If a copyright notice is present from another entity, it must
 *       be maintained in redistributions of the source code.
 *
 * THIS INTELLECTUAL PROPERTY (WHICH MAY INCLUDE BUT IS NOT LIMITED TO SOFTWARE,
 * FIRMWARE, VHDL, etc) IS PROVIDED BY THE AUTHOR AND WASHINGTON UNIVERSITY 
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR WASHINGTON UNIVERSITY 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS INTELLECTUAL PROPERTY, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * */

/*
 * File: stringSub.c 
 * Author: Ken Wong
 * Email: kenw@arl.wustl.edu
 * Organization: Applied Research Laboratory
 * 
 * Derived from:  vowels.c
 *
 * Date Created:  05/5/09
 * 
 * Description:	Replace one string (e.g., "hello") with another (e.g., "adieu")
 *		in the payload of UDP pkts
 *
 * Modification History:
 *   5 May 2009		Ken Wong:
 *   			- Original assumed pkt payloads were 32 bytes or less.
 *   			  This version removes that assumption.
 *   			- Completely changed handle_pkt_user() to include
 *   			  two user-selectable algorithms (SCAN_DRAM, SCAN_LMEM)
 *   			- Changed and added control messages
 *   21 July 2008	Jessica Codr:  creation
 *
 */

#include <string.h>
#include <memory.h>
#include "plugin_api.h"
#include "plugin_dl.h"

// plugin counters
#define	PKT_COUNT	0	// #pkts received by handle_pkt_user()
#define BYTE_COUNT	1	// sum of all #bytes in payloads
#define MOD_COUNT	2	// #words changed (modified)
#define ERR_COUNT	3	// #errors


//-----------------------------------------------------------
// typedefs, unions, enums
//-----------------------------------------------------------
enum algorithms { SCAN_DRAM=0, SCAN_LMEM };

//-----------------------------------------------------------
// Global variables/Registers
//-----------------------------------------------------------
//
// >> thread-specific globals <<
//
__declspec(gp_reg) int dlNextBlock;  // where to send packets to next
__declspec(gp_reg) int dlFromBlock;  // where to get packets from
__declspec(gp_reg) int msgNextBlock; // where to send control messages to next
__declspec(gp_reg) int msgFromBlock; // where to get control messages from

// see ring_formats.h for struct definitions
volatile __declspec(gp_reg) plc_plugin_data ring_in;	// ring data from PLC
volatile __declspec(gp_reg) plugin_out_data ring_out;	// ring data to nxt blk

const unsigned int SLEEP_CYCLES = 14000;	// cycles between
						//   callbacks (10 usec)
__declspec(gp_reg) unsigned int pluginId;	// plugin id (0...7)

// >> user globals <<
__declspec(shared gp_reg) unsigned int npkts;
__declspec(shared gp_reg) unsigned int nbytes;
__declspec(shared gp_reg) unsigned int nmods;	// #words modified
__declspec(shared gp_reg) unsigned int debug_on;
__declspec(shared gp_reg) unsigned int alg;

#ifdef DEBUG
__declspec(shared gp_reg) unsigned int x0;	// used in debugging w/ file4.in
__declspec(shared gp_reg) unsigned int x1;
__declspec(shared gp_reg) unsigned int x2;
#endif

__declspec(shared sram) unsigned int first = 1;

//------


#include <plugin_helpers.h>

//-----------------------------------------------------------
// Function prototypes
//-----------------------------------------------------------
void handle_pkt_user();
void handle_msg_user();
void callback_user();
void plugin_init_user();


//-----------------------------------------------------------
// New helper functions 
//-----------------------------------------------------------

// handle errors

#define BAD_NXTBLK      1       // bad dlNextBlock

__declspec(shared gp_reg) unsigned int nerrs;		// #errors
volatile __declspec(shared sram) unsigned int errno[5];	// 1st 5 errors

// record error number
//									<<<<<
static __forceinline void
helper_set_errno( __declspec(local_mem) unsigned int n ) {
    if( nerrs < 5 )	errno[nerrs] = n;
    ++nerrs;
    onl_api_plugin_cntr_inc(pluginId, 3);	// external error counter
}

// output control msg with 1 unsigned long
//
void
helper_sram_outmsg_str_1ul(	char *cstr, unsigned long x0,
			__declspec(local_mem) char *outmsgstr ) {
    __declspec(sram) char	sram_msg_buf[28];
    __declspec(gp_reg)	int n;

    n = strlen_sram( cstr );
    strcpy_sram( sram_msg_buf, cstr );
    sram_msg_buf[n] = ' ';
    sram_msg_buf[n+1] = '\0';
    ++n;

    helper_ultoa_sram( x0, sram_msg_buf, 28-n );
    memcpy_lmem_sram( outmsgstr, sram_msg_buf, 28 );
}

// set a counter to a specific value
static __forceinline void
helper_plugin_cntr_set( int pluginId, int counter_id, int new_value ) {
    get_pcntr( pluginId, counter_id );
    WU_loadGlobalRegister( stats_regnum, new_value, stats_cerr );
}


// reset global counters
//									<<<<<
static __forceinline void
reset_counters( void ) {
    npkts = 0;
    nbytes = 0;
    nmods = 0;

    nerrs = 0;
    errno[0] = 0;	errno[1] = 0;	errno[2] = 0;	errno[3] = 0;
    errno[4] = 0;

    sleep( SLEEP_CYCLES );		// not sure if I need this

    helper_plugin_cntr_zero( PKT_COUNT );
    helper_plugin_cntr_zero( BYTE_COUNT );
    helper_plugin_cntr_zero( MOD_COUNT );
    helper_plugin_cntr_zero( ERR_COUNT );
}


// do a reset
//
static __forceinline void
do_reset( void ) {
    debug_on = 0;
    alg = SCAN_DRAM;
    reset_counters( );
}


//-----------------------------------------------------------
// Begin Normal Functions
//-----------------------------------------------------------
//									<<<<<
//

// these macros and functions are used by handle_pkt_user()
//
#define is_hello_dram(q)	\
	( (*q == 'h') && (*(q+1) == 'e') && (*(q+2) == 'l')		\
		&& (*(q+3) == 'l') && (*(q+4) == 'o') )
#define set_adieu_dram(q)	\
{ *q = 'a';  *(q+1) = 'd';  *(q+2) = 'i'; *(q+3) = 'e';  *(q+4) = 'u'; }

// return #leading chars matching "hello" in ring buffer; i.e., may
// need to wrap around ring
//
//	k		index into my_payload[]
//	my_payload[]	payload buffer
//
static __forceinline unsigned int
nmatch_hello_lmem(	unsigned int		k,
//ZZZ		__declspec(sram) char	my_payload[] ) {
		__declspec(local_mem) char	my_payload[] ) {

    if( my_payload[k] != 'h' )	return 0;
    k = (k+1)%64;

    if( my_payload[k] != 'e' )	return 1;
    k = (k+1)%64;

    if( my_payload[k] != 'l' )	return 2;
    k = (k+1)%64;

    if( my_payload[k] != 'l' )	return 3;
    k = (k+1)%64;

    if( my_payload[k] != 'o' )	return 4;

    return 5;
}

// set characters to 'adieu' (no '\0'); i.e., may need to wrap around ring
//
//	k		index into my_payload[]
//	my_payload[]	payload buffer
//
static __forceinline void
set_adieu_lmem(	unsigned int		k,
//ZZZ		__declspec(sram) char	my_payload[] ) {
		__declspec(local_mem) char	my_payload[] ) {

    my_payload[k] = 'a';
    k = (k+1)%64;

    my_payload[k] = 'd';
    k = (k+1)%64;

    my_payload[k] = 'i';
    k = (k+1)%64;

    my_payload[k] = 'e';
    k = (k+1)%64;

    my_payload[k] = 'u';
}

//
void handle_pkt_user()  {
    __declspec(gp_reg) buf_handle_t	buf_handle;
    __declspec(gp_reg) onl_api_buf_desc	bufDescriptor;
    __declspec(local_mem) unsigned int	ipv4HdrPtr;
    __declspec(local_mem) unsigned int	udpHdrPtr;
    __declspec(sram) unsigned int	bufDescPtr;
    __declspec(sram) unsigned int	dramBufferPtr;
    __declspec(gp_reg) onl_api_ip_hdr	ipv4_hdr;
    __declspec(gp_reg) onl_api_udp_hdr	udp_hdr;
    __declspec(sram) unsigned int	payloadPtr;
    __declspec(sram) int		payloadSz;
    __declspec(dram) char* __declspec(gp_reg)	q;
    int			i;
    __declspec(gp_reg) unsigned int	my_nmods; // #words modified in one pkt 

    my_nmods = 0;

    ++npkts;
    onl_api_plugin_cntr_inc(pluginId, PKT_COUNT);

    onl_api_get_buf_handle( &buf_handle ); 
    bufDescPtr	= onl_api_getBufferDescriptorPtr( buf_handle );
    onl_api_readBufferDescriptor( bufDescPtr, &bufDescriptor );
    dramBufferPtr = onl_api_getBufferPtr( buf_handle );
    ipv4HdrPtr	= onl_api_getIpv4HdrPtr( dramBufferPtr, bufDescriptor.offset );
    onl_api_readIpv4Hdr( ipv4HdrPtr, &ipv4_hdr );       
    udpHdrPtr	= onl_api_getUdpHdrPtr( ipv4HdrPtr, ipv4_hdr.ip_hl );
    onl_api_readUdpHdr( udpHdrPtr, &udp_hdr );   
    payloadPtr	= onl_api_getUdpPacketPayloadPtr( udpHdrPtr );

    q = (__declspec(dram) char *) payloadPtr;
    payloadSz = udp_hdr.uh_ulen - UDP_HEADER_SIZE_IN_BYTES;
	#ifdef DEBUG
	    helper_sram_dbgmsg_str_1ul( "payloadSz", payloadSz );
	#endif
    nbytes += payloadSz;
    helper_plugin_cntr_set(pluginId, BYTE_COUNT, nbytes );

    switch( alg ) {
	case SCAN_DRAM:			// scan pkt in dram
		#ifdef DEBUG
		    helper_sram_dbgmsg_str_1ul( "alg= ", alg );
		#endif
	    for( i=0; i<(payloadSz-4); ) {
		if( is_hello_dram(q) ) {
		    set_adieu_dram( q );
		   // could use strcmp_sdram() and strncpy_sdram() if the
		   // developers would give the compiler some dram
		    ++my_nmods;
		   q += 5;
		   i += 5;
		} else {
		   q++;
		   i++;
		}
	    }
		#ifdef DEBUG
		if( npkts == 1 )	x0 = my_nmods;
		else if( npkts == 2 )	x1 = my_nmods;
		else			x2 = my_nmods;
		#endif
		#ifdef DEBUG
		    helper_sram_dbgmsg_str_1ul( "my_nmods", my_nmods );
		#endif
	    break;
	default:	// SCAN_LMEM - scan lmem chunks which are read from dram
	    {
//ZZZ	    __declspec(sram) char			my_payload[64];
	    __declspec(local_mem) char			my_payload[64];
	    __declspec(dram) char* qread;
	    __declspec(dram) char* qwrite;
	    int			n_togo;		// #bytes to go in outer loop
	    int			j;		// index into chunk
	    unsigned int	k;		// index into my_payload[]
	    unsigned int	bufndx;		// 0: my_payload[0:31],
	    					// 1: my_payload[32:63]
	    unsigned int	dirty[2];	// 1 if chunk[i] was modified
	    unsigned int	nb;		// #bytes in chunk to process
	    unsigned int	nmatch;		// #leading bytes matching "hello"
	    #ifdef DEBUG
	    unsigned int	nr;		// #chunks read
	    #endif

		#ifdef DEBUG
		    helper_sram_dbgmsg_str_1ul( "alg= ", alg );
		#endif
	    bufndx = 0;
	    dirty[0] = 0;	dirty[1] = 0;

		// read first 2 chunks (even if only need 1 chunk)
	    qread = ( __declspec(dram) char* ) ( (unsigned int)q & 0xfffffff8 );
	    					// align to quadword boundary
	    qwrite = qread;
	    onl_api_ua_read_8W_dram( (unsigned int) qread,
	    					(void *) &my_payload[0] );
	    qread += 32;
	    onl_api_ua_read_8W_dram( (unsigned int) qread,
	    					(void *) &my_payload[32] );
	    #ifdef DEBUG
	    nr = 2;
	    #endif
	    nb = 32 - ((unsigned int)q & 07);	// rightmost bytes in chunk 0
	    k = 32 - nb;

		// process payload
	    for( n_togo=payloadSz; n_togo>0;  ) {
	    		// scan 1 chunk plus maybe a little bit of next chunk
			// do until cross chunk boundary
		while( ((bufndx == 0) && (k < 32)) ||
					((bufndx == 1) && (k > 31)) ) {
		    if( n_togo <= 0 )	break;
		    nmatch = nmatch_hello_lmem( k, my_payload );
		    				// could go past end of pkt
		    if( nmatch == 0 ) {
			k = (k+1)%64;
			--n_togo;
		    } else if( nmatch == 5 ) {
		    		#ifdef DEBUG
				helper_sram_dbgmsg_3ul( k, n_togo, nmatch );
				#endif
			set_adieu_lmem( k, my_payload );
			++my_nmods;
			dirty[bufndx] = 1;
			if( (k >= 28) && (k <= 31) )		dirty[1] = 1; 
			else if( (k >= 60) && (k <= 63) )	dirty[0] = 1; 
			if( k < 59 )	k += 5;
			else		k = (k+5)%64;
			n_togo -= 5;
		    } else {	// match 1-4 chars
			k = (k+nmatch)%64;
			n_togo -= nmatch;
		    }
	        }

			#ifdef DEBUG
			helper_sram_dbgmsg_3ul( k, n_togo, my_nmods  );
			helper_sram_dbgmsg_str_1ul( "n_togo", n_togo ;
			helper_sram_dbgmsg_str_1ul( "my_nmods", my_nmods );
			#endif

			// write out chunk if dirty
			//	may overwrite part of udp header if payload
			//	in 1st chunk is not aligned on quadword
		if( dirty[bufndx] ) {
		    if( bufndx == 0 )
			onl_api_ua_write_8W_dram( (unsigned int) qwrite,
	    					(void *) &my_payload[0] );
		    else
			onl_api_ua_write_8W_dram( (unsigned int) qwrite,
	    					(void *) &my_payload[32] );
		    dirty[bufndx] = 0;
		}
		qwrite += 32;

			// read next chunk
			// 	last chunk may contain extra bytes (as much
			// 	as 21 bytes, a whole chunk) but since the
			// 	buffer is 2 KB, you'll just be overwriting
			// 	unused memory
			// I would like to get rid of the if-test, but doing
			// so causes a spill error.  So, I choose a condition
			// that makes the code equivalent to what I want.
		if( n_togo > 1 ) {
		//if( n_togo > 27 ) {
		     qread += 32;
		     if( bufndx == 0 ) {
			onl_api_ua_read_8W_dram( (unsigned int) qread,
	    					(void *) &my_payload[0] );
		     } else {
			onl_api_ua_read_8W_dram( (unsigned int) qread,
	    					(void *) &my_payload[32] );
		     }
		     #ifdef DEBUG
		     ++nr;
		     #endif
		}
		bufndx = (bufndx+1) % 2;
	    }
	    	#ifdef DEBUG
		if( first ) {
		    first = 0;
		    x1 = nr;
		    x2 = k;
		}
		#endif

		#ifdef DEBUG
		if( npkts == 1 )	x0 = my_nmods;
		else if( npkts == 2 )	x1 = my_nmods;
		else			x2 = my_nmods;
		#endif

	    }
    }

    nmods += my_nmods;
    helper_plugin_cntr_set(pluginId, MOD_COUNT, nmods );

    // recompute checksum and write out new udp hdr
    udp_hdr.uh_sum = onl_api_udp_cksum(&ipv4_hdr, &udp_hdr, udpHdrPtr);
    onl_api_writeUdpHdr(udpHdrPtr, &udp_hdr);

    if ( helper_set_meta_default( dlNextBlock ) != 0 ) {
        helper_set_errno( BAD_NXTBLK );
    }
}

void handle_msg_user(){}

void callback_user() {
}

void plugin_init_user() {
    do_reset( );
}


/**
	----------------------------------------------------------------
 @User: YOU SHOULD NOT NEED TO MAKE ANY CHANGES TO THE REST OF THIS FILE
	----------------------------------------------------------------
*/

/* handle packets */
void handle_pkt()
{
  dl_source_packet( dlFromBlock );

  handle_pkt_user( );

  dl_sink_packet( dlNextBlock );

}


/* handle control messages */
//									<<<<<
// op codes:
//   set:
//	alg= X	set algorithm to X where 0 means SCAN_DRAM and anything else
//		  means SCAN_LMEM
//   get:
//   	=vers	display version number
//	=counts	display counts (npkts, nbytes, nmods)
//	=alg	display algorithm
//	=errno	display errno[0], ... , errno[4]
//   miscellaneous:
//	reset	reset parameters, nbytes[], nmods[], errno[] counters, etc.
//	debug	toggle debug_on
//
void handle_msg()
{
    // assume messages are at most 8 words for now
    __declspec(gp_reg) unsigned int i;
    __declspec(gp_reg) unsigned int message[8];
    __declspec(gp_reg) onl_api_ctrl_msg_hdr hdr;
    __declspec(local_mem) char inmsgstr[28];			// inbound
    __declspec(local_mem) char outmsgstr[28];			// outbound
    __declspec(sram) char sram_inmsgstr[28];
    __declspec(sram) char vers[4] = "1.0";

    char GET_vers[8]	= "=vers";
    char GET_counts[8]	= "=counts";
    char GET_alg[8]	= "=alg";
    char GET_errno[8]	= "=errno";

    char SET_alg[8]	= "alg=";

    char RESET[8]	= "reset";
    char DEBUG_op[8]	= "debug";

    char OK_msg[4]	= "OK";
    char BAD_OP_msg[8]	= "BAD OP";
    char NEED_ARG_msg[12]= "NEED ARG(S)";
    char BAD_NARGS_msg[12]= "BAD #ARGS =";

    // expand for-loop to get rid of the compiler error:
    //					"Incorrect use of register variable
    message[0] = 0;    message[1] = 0;    message[2] = 0;    message[3] = 0;
    message[4] = 0;    message[5] = 0;    message[6] = 0;    message[7] = 0;

    dl_source_message(msgFromBlock, message);

    hdr.value = message[0];
    if( hdr.type != CM_CONTROLMSG )	return;
    if( hdr.response_requested != 1 )	return;

    onl_api_intarr2str( &message[1], inmsgstr );

    outmsgstr[0] = '\0';
    memcpy_sram_lmem( sram_inmsgstr, inmsgstr, 28 );

    if( strncmp_sram(sram_inmsgstr, GET_vers, 5) == 0 ) {
	memcpy_lmem_sram( outmsgstr, (void *)vers, 4 );
    } else if( strncmp_sram(sram_inmsgstr, GET_counts, 7) == 0 ) {
	    helper_sram_outmsg_3ul( npkts, nbytes, nmods, outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_alg, 4) == 0 ) {
	helper_sram_outmsg_1ul( alg, outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_errno, 6) == 0 ) {
    		#ifdef DEBUG
		helper_sram_outmsg_3ul( x0, x1, x2, outmsgstr );
		#endif
	helper_sram_outmsg_5ul( errno[0], errno[1], errno[2], errno[3],
							errno[4], outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, SET_alg, 4) == 0 ) {
	char    *cmnd_word;             // points to input command field
	char    *alg_word;             // points to input algorithm field
	unsigned int    nwords;
	unsigned int    xalg;

	nwords = helper_count_words( sram_inmsgstr );
	if( nwords != 2 ) {
	    helper_sram_outmsg_str_1ul( BAD_NARGS_msg, nwords, outmsgstr );
	} else {
	    cmnd_word = helper_tokenize( sram_inmsgstr );       // get command
	    alg_word = helper_tokenize( cmnd_word+strlen(cmnd_word)+1 );
	    xalg = helper_atou_sram( alg_word );
	    helper_sram_outmsg_1ul( xalg, outmsgstr );
	    alg = xalg;
	}
    } else if( strncmp_sram(sram_inmsgstr, RESET, 5) == 0 ) {
	do_reset( );
    } else if( strncmp_sram(sram_inmsgstr, DEBUG_op, 5) == 0 ) {
    	debug_on = (debug_on+1) & 0x1;
	helper_sram_outmsg_1ul( debug_on, outmsgstr );
    } else {
	memcpy_lmem_sram( outmsgstr, BAD_OP_msg, 8 );
    }


    if( onl_api_str2intarr(outmsgstr, &message[1]) < 0 )	return;

    hdr.type = CM_CONTROLMSGRSP;
    hdr.response_requested = 0;
    hdr.num_words = 7;
    message[0] = hdr.value;

    dl_sink_message(msgNextBlock, message);
}


// handle periodic functionality (NOT USED)
void callback()
{
}


/* take care of any setup that needs to be done before processing begins */
void plugin_init()
{
  /* set the default next block to be the Queue Manager */
  dlNextBlock = QM;

  /* by default, get packets and get and put control messages from input rings
   * based on which microengine we are currently running on; this assumes a
   * default one to one mapping */
  switch(__ME())
  {
    case 0x7:
      pluginId = 0;
      dlFromBlock  = PACKET_IN_RING_0;
      msgFromBlock = MESSAGE_IN_RING_0;
      msgNextBlock = MESSAGE_OUT_RING_0;
      break;
    case 0x10:
      pluginId = 1;
      dlFromBlock  = PACKET_IN_RING_1;
      msgFromBlock = MESSAGE_IN_RING_1;
      msgNextBlock = MESSAGE_OUT_RING_1;

      break;
    case 0x11:
      pluginId = 2;
      dlFromBlock  = PACKET_IN_RING_2;
      msgFromBlock = MESSAGE_IN_RING_2;
      msgNextBlock = MESSAGE_OUT_RING_2;  
      break;
    case 0x12:
      pluginId = 3;
      dlFromBlock  = PACKET_IN_RING_3;
      msgFromBlock = MESSAGE_IN_RING_3;
      msgNextBlock = MESSAGE_OUT_RING_3;    
      break;
    case 0x13:
      pluginId = 4;
      dlFromBlock  = PACKET_IN_RING_4;
      msgFromBlock = MESSAGE_IN_RING_4;
      msgNextBlock = MESSAGE_OUT_RING_4;
      break;
    default:  // keep the compiler happy
      pluginId = 0;
      dlFromBlock  = PACKET_IN_RING_0;
      msgFromBlock = MESSAGE_IN_RING_0;
      msgNextBlock = MESSAGE_OUT_RING_0;
      break;
  }

  plugin_init_user(); // user hook
}


/* entry point */
void main()
{
  int c;

  /* do initialization */
  plugin_init();
  dl_sink_init();
  dl_source_init();

  /* get the current thread's context number (0-7) */
  c = ctx();

  if(c >= FIRST_PACKET_THREAD && c <= LAST_PACKET_THREAD)
  {
    while(1)
    {
      handle_pkt();
    }
  }
#ifdef MESSAGE_THREAD
  else if(c == MESSAGE_THREAD)
  {
    while(1)
    {
      handle_msg();
    }
  }
#endif
#ifdef CALLBACK_THREAD
  else if(c == CALLBACK_THREAD)
  {
    while(1)
    {
      callback();
    }
  }
#endif
}

