// start with delay plugin ... see README

/*
 * Copyright (c) 2008 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: priq.c  
 * Author: Ken Wong
 * Email: kenw@arl.wustl.edu
 * Organization: Applied Research Laboratory
 * 
 * Derived from: pluginFramework/nstat.c
 *
 * Date Created: 12/10/2008 
 * 
 * Description: priority queues (4)
 *
 * Modification History:
 *
 */


#include <memory.h>
#include "plugin_api.h"
#include "plugin_dl.h"

#include "scratch_rings_WU.h"

//#ifdef DL_ORDERED
//    SIGNAL dl_sink_packet_sig;
//    SIGNAL dl_source_packet_sig;
//#endif

//-----------------------------------------------------------
// typedefs, unions, enums
//-----------------------------------------------------------
struct item_tag {
    unsigned int	buf_handle;	// meta-packet
    unsigned int	out_port;	// .
    unsigned int	qid;		// .
    unsigned int	l3_pkt_len;	// .
    struct item_tag	*next;
};

struct queue_tag {
    unsigned long	npkts;		// #pkts in queue
    unsigned long	nbytes;		// #bytes in queue
    unsigned long	maxinq;		// max #pkts in queue
    unsigned long	ndrops;		// #pkts dropped from queue
    struct item_tag	*hd;		// head ptr
    struct item_tag	*tl;		// tail ptr
    struct item_tag	*free_hd;	// free list
};


//-----------------------------------------------------------
// 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

#define	N	3	// 1 + #flow types
const unsigned int SLEEP_CYCLES = 14000;	// cycles between
						//   callbacks (10 usec)
__declspec(gp_reg) unsigned int pluginId;	// plugin id (0...7)
__declspec(shared gp_reg) unsigned int npkts[N];// total #pkts (type k) where
						//   0 is "other"
__declspec(shared gp_reg) unsigned int ndrops[N];// total #pkts dropped (type k)

#define RESQ		64		// reserved pkt queue

//------
#define UNLOCKED 0
#define LOCKED   1
__declspec(shared gp_reg) unsigned int queue_lock;

//XXX #define	MAX_QUEUE_SZ	35000
#define	MAX_QUEUE_SZ	20000
__declspec(shared, sram) struct queue_tag queue;

//------


#include "plugin_helpers.h"

//-----------------------------------------------------------
// Function prototypes
//-----------------------------------------------------------
void handle_pkt_user();
void handle_msg_user();
void plugin_init_user();
int queue_init( __declspec(shared, sram) struct queue_tag *qptr );

static void wait_packet_signal(SIGNAL *);
static void send_packet_signal(SIGNAL *);

// forward reference
void helper_send_from_queue_to_QM(
			__declspec(shared,sram) struct queue_tag *qptr );
struct item_tag *queue_alloc(__declspec(shared, sram) struct queue_tag *queue);
void queue_free(__declspec(shared, sram) struct queue_tag *queue,
		struct item_tag *item );
int queue_enq(
	__declspec(shared, sram) struct queue_tag *qptr,
	unsigned int buf_handle, 	unsigned int out_port, 
	unsigned int qid,		unsigned int l3_pkt_len );
int queue_pop( __declspec(shared, sram) struct queue_tag *qptr );


//-----------------------------------------------------------
// New helper functions 
//-----------------------------------------------------------

#define	FormRawQid(out_port,qid)	((((out_port)+1) << 13) | qid)

// handle errors
#define	BAD_QUEUE_INIT_ERR	1	// bad queue_init()
#define	BAD_ENQ_ERR		2	// bad queue_enq()
#define	BAD_POP_ERR		3	// bad queue_pop()
#define	BAD_NXTBLK		4	// bad nextBlk
#define	BAD_OUT_PORT		6	// bad QID in meta-pkt
#define	BAD_QID			7	// bad QID in meta-pkt
#define	BAD_HANDLE_A		8	// bad buffer handle in meta-pkt
#define	BAD_HANDLE_B		9	// bad buffer handle in meta-pkt = 0

__declspec(shared gp_reg) unsigned int nerrs;		// #errors
__declspec(shared sram) unsigned int errno[5];		// 1st 5 errors
__declspec(shared sram) unsigned int nerrsA;		// #handle A errs
__declspec(shared sram) unsigned int nerrsB;		// #handle B errs

#define NX	32
__declspec(shared sram) unsigned int	xdata[NX];	// extra data
__declspec(shared sram) unsigned int	nxdata;		// #xdata valid
__declspec(shared sram) unsigned int	nxget;

static __forceinline void
helper_set_errno( __declspec(local_mem) unsigned int n ) {
    if( nerrs < 5 )	errno[nerrs] = n;
    ++nerrs;
}

// set ring_out qid given output port# and external qid
static __forceinline void
helper_set_meta_qid(	__declspec(gp_reg) unsigned int out_port,
			__declspec(gp_reg) unsigned int xqid ) {
    ring_out.plugin_qm_data_out.qid = (out_port+1 << 13) | xqid;
}

// reset global counters
//
static __forceinline void
reset_counters( void ) {
    npkts[0] = 0;	npkts[1];	npkts[2];
    ndrops[0] = 0;	ndrops[1];	ndrops[2];

    nerrs = 0;
    errno[0] = 0;	errno[1] = 0;	errno[2] = 0;	errno[3] = 0;
    errno[4] = 0;

    nxdata = 0;		nxget = 0;
    nerrsA = 0;		nerrsB = 0;

    sleep( SLEEP_CYCLES );		// not sure if I need this
    helper_plugin_cntr_zero( 0 );
    helper_plugin_cntr_zero( 1 );
    helper_plugin_cntr_zero( 2 );
    helper_plugin_cntr_zero( 3 );
}

// set output to drop pkt
//
static __forceinline void
helper_set_out_to_DROP( ) {
    onl_api_drop();
}



static __forceinline void
helper_set_xdata( unsigned int x ) {
    if( nxdata < NX ) {
	xdata[nxdata] = x;
	++nxdata;
    }
}

// output debug msg with 2 args
//
static __forceinline void
helper_sram_dbgmsg_str_1ul( char *cstr, unsigned long xval ) {
    __declspec(sram) char sram_dbgmsg_buf[28];
    __declspec(local_mem) char lmem_dbgmsg_buf[28];
    __declspec(gp_reg)	int n;

    n = strlen_sram( cstr );
    strcpy_sram( sram_dbgmsg_buf, cstr );
    sram_dbgmsg_buf[n] = ' ';
    sram_dbgmsg_buf[n+1] = '\0';
    ++n;
    helper_ultoa_sram( xval, sram_dbgmsg_buf+n, 28-n );

    memcpy_lmem_sram( lmem_dbgmsg_buf, (void *)sram_dbgmsg_buf, 28 );
    onl_api_debug_message( msgNextBlock, lmem_dbgmsg_buf );
}


// output control msg with 3 unsigned longs
//
void
helper_sram_outmsg_3ul(	unsigned long x0,
			unsigned long x1,
			unsigned long x2,
			__declspec(local_mem) char *outmsgstr ) {
    __declspec(sram) char	SPACE[2] = " ";
    __declspec(sram) char	sram_msg_buf[28];
    __declspec(sram) char	sram_tmpstr[16];

    helper_ultoa_sram( x0, sram_msg_buf, 28 );
    strcat_sram( sram_msg_buf, SPACE );
    helper_ultoa_sram( x1, sram_tmpstr, 16 );
    strcat_sram( sram_msg_buf, sram_tmpstr );
    strcat_sram( sram_msg_buf, SPACE );
    helper_ultoa_sram( x2, sram_tmpstr, 16 );
    strcat_sram( sram_msg_buf, sram_tmpstr );
    memcpy_lmem_sram( outmsgstr, sram_msg_buf, 28 );
}


// output control msg with 5 unsigned longs
//
void
helper_sram_outmsg_5ul(	unsigned long x0,
			unsigned long x1,
			unsigned long x2,
			unsigned long x3,
			unsigned long x4,
			__declspec(local_mem) char *outmsgstr ) {
    __declspec(sram) char	SPACE[2] = " ";
    __declspec(sram) char	sram_msg_buf[28];
    __declspec(sram) char	sram_tmpstr[4];

    helper_ultoa_sram( x0, sram_msg_buf, 28 );
    strcat_sram( sram_msg_buf, SPACE );
    helper_ultoa_sram( x1, sram_tmpstr, 4 );
    strcat_sram( sram_msg_buf, sram_tmpstr );
    strcat_sram( sram_msg_buf, SPACE );
    helper_ultoa_sram( x2, sram_tmpstr, 4 );
    strcat_sram( sram_msg_buf, sram_tmpstr );
    strcat_sram( sram_msg_buf, SPACE );
    helper_ultoa_sram( x3, sram_tmpstr, 4 );
    strcat_sram( sram_msg_buf, sram_tmpstr );
    strcat_sram( sram_msg_buf, SPACE );
    helper_ultoa_sram( x4, sram_tmpstr, 4 );
    strcat_sram( sram_msg_buf, sram_tmpstr );
    memcpy_lmem_sram( outmsgstr, sram_msg_buf, 28 );
}


// output control msg with xdata[]
//
void
helper_sram_outmsg_xdata( __declspec(local_mem) char *outmsgstr ) {
    __declspec(sram) char	SPACE[2] = " ";
    __declspec(sram) char	sram_msg_buf[28];
    __declspec(sram) char	sram_tmpstr[16];
    __declspec(gp_reg) int	i;

    if( nxget >= nxdata )	return;

    sram_msg_buf[0] = '\0';
    for( i=0; i<1; i++ ) {
    	if( nxget >= nxdata )	break;
    	if( i > 0 )	strcat_sram( sram_msg_buf, SPACE );
	helper_ultoa_sram( xdata[nxget], sram_tmpstr, 16 );
	strcat_sram( sram_msg_buf, sram_tmpstr );
	++nxget;
    }
    memcpy_lmem_sram( outmsgstr, sram_msg_buf, 28 );
}


// check meta-packet fields
// 	ring_out.plugin_qm_data_out.qid = (1+out_port)*8192 + 64
// 					qid = 41024 for out_port = 4
// 	ring_out.plugin_qm_data_out.buf_handle_lo24:
// 		rightmost 3 bits = 0
// 		rightmost 24 bits != 0
// 	out_port in {0:4}
//
void
helper_check_meta( plugin_out_data my_ring_out ) {
//    __declspec(sram) char	tmpstr1[28] = "Bad out_port";
//    __declspec(sram) char	tmpstr2[28] = "Bad qid";
    __declspec(sram) char	tmpstr3[28] = "Bad bufhandle";

//    if( my_ring_out.plugin_qm_data_out.out_port != 4 ) {	// output port
//    	helper_set_errno( BAD_OUT_PORT );
//	helper_sram_dbgmsg_str_1ul( tmpstr1,
//					ring_out.plugin_qm_data_out.out_port );
//    }
//    if( my_ring_out.plugin_qm_data_out.qid != 41024 ) {		// QID
//    	helper_set_errno( BAD_QID );
//	helper_sram_dbgmsg_str_1ul( tmpstr2,
//					ring_out.plugin_qm_data_out.qid );
//    }
//    								// buffer handle
    if( (my_ring_out.plugin_qm_data_out.buf_handle_lo24 & 0x7) != 0 ) {
//	helper_sram_dbgmsg_str_1ul( tmpstr3,
//				ring_out.plugin_qm_data_out.buf_handle_lo24 );
	helper_set_xdata( my_ring_out.plugin_qm_data_out.buf_handle_lo24 );
    	helper_set_errno( BAD_HANDLE_A );
    	++nerrsA;
    }
    if( my_ring_out.plugin_qm_data_out.buf_handle_lo24 == 0 ) {
//	helper_sram_dbgmsg_str_1ul( tmpstr3,
//				ring_out.plugin_qm_data_out.buf_handle_lo24 );
	helper_set_xdata( my_ring_out.plugin_qm_data_out.buf_handle_lo24 );
    	helper_set_errno( BAD_HANDLE_B );
    	++nerrsB;
    }
}


// set output to DO_NOTHING
//
static __forceinline void
helper_set_out_to_DO_NOTHING( ) {
    dlNextBlock = DO_NOTHING;
}


// Same as dl_sink_packet() but don't do any signalling and assume pkt
// goes to QM
//
static __forceinline void
helper_send_from_queue_to_QM( __declspec(shared,sram) struct queue_tag *qptr ) {
    plugin_out_data	my_ring_out;	// ring data to next block
    int		rc;

    my_ring_out.plugin_qm_data_out.out_port		= qptr->hd->out_port;
    my_ring_out.plugin_qm_data_out.qid			= qptr->hd->qid;
    my_ring_out.plugin_qm_data_out.l3_pkt_len		= qptr->hd->l3_pkt_len;
    my_ring_out.plugin_qm_data_out.buf_handle_lo24	= qptr->hd->buf_handle;

    rc = queue_pop( qptr );
    if( rc != 0 )	helper_set_errno( BAD_POP_ERR );

helper_check_meta( my_ring_out );

    scr_ring_put_buffer_3word( PLUGIN_TO_QM_RING, my_ring_out.i, 0 );
}


// set ring_out data for default for nextBlk
//
static __forceinline void
helper_set_meta_default( __declspec(gp_reg) int nextBlk ) {
    dlNextBlock = nextBlk;

    // ignore TX and XSCALE for now

    if( nextBlk == QM ) {
	onl_api_update_ring_out_to_qm(
    		ring_in.buf_handle_lo24, 
    		(ring_in.uc_mc_bits >> 3) & 0x7,
		ring_in.qid, 
		ring_in.l3_pkt_len);
    } else if( nextBlk == DROP ) {
	onl_api_update_ring_out_to_freelist( ring_in.buf_handle_lo24 );
    } else if( nextBlk == MUX ) {
	onl_api_update_ring_out_to_mux(
    		ring_in.buf_handle_lo24, 
    		(ring_in.uc_mc_bits >> 3) & 0x7,
		ring_in.in_port,
		ring_in.plugin_tag, 
		ring_in.stats_index, 
		0, 
		ring_in.qid, 
		ring_in.l3_pkt_len);
    } else if(	nextBlk == PACKET_IN_RING_0	||
		(nextBlk == PACKET_IN_RING_1)	||
		(nextBlk == PACKET_IN_RING_2)	||
		(nextBlk == PACKET_IN_RING_3)	||
		(nextBlk == PACKET_IN_RING_4)		) {
	onl_api_update_ring_out_to_plugin(
    		ring_in.buf_handle_lo24, 
    		(ring_in.uc_mc_bits >> 3) & 0x7,
		ring_in.in_port,
		ring_in.plugin_tag,
		ring_in.stats_index, 
		0,
		ring_in.qid, 
		ring_in.nh_eth_daddr_hi32,
		ring_in.nh_eth_daddr_lo16,
		ring_in.eth_type,
		ring_in.uc_mc_bits,
		ring_in.l3_pkt_len);
    } else if( nextBlk == DO_NOTHING ) {
    	// do nothing
    } else {					// all other options
    	helper_set_errno( BAD_NXTBLK );
    }
}


//-----------------------------------------------------------
// Begin Normal Functions
//-----------------------------------------------------------
void handle_pkt_user( )  {
    __declspec(gp_reg) unsigned int out_port;
    __declspec(gp_reg) unsigned int qid;
    unsigned long	ninq;
    // debug stuff
#ifdef DEBUG
    __declspec(local_mem) char dbgmsg1[8] = "qid=1";
    __declspec(local_mem) char dbgmsg2[8] = "qid>1";
    char	dbgmsg[12] = "GOT PKT ";
    char	dbgmsg3[8] = "QID = ";
    char	dbgmsg4[8] = "ninq = ";
#endif

#ifdef DEBUG
    helper_sram_dbgmsg_str_1ul( dbgmsg, npkts );
#endif

    qid = ring_in.qid & 0x1fff;			// external qid
    out_port = (ring_in.uc_mc_bits >> 3) & 0x7;

#ifdef DEBUG
    helper_sram_dbgmsg_str_1ul( dbgmsg3, qid );
#endif

    if( qid == 1 ) {
    	++npkts[1];
    	onl_api_plugin_cntr_inc(pluginId, 1);
    } else if( qid == 2 ) {
    	++npkts[2];
    	onl_api_plugin_cntr_inc(pluginId, 2);
    } else if( qid == 3 ) {
    	// ++npkts[3];
    	onl_api_plugin_cntr_inc(pluginId, 3);
    } else {			// BAD QID
    	// ++npkts[0];
    	onl_api_plugin_cntr_inc(pluginId, 0);
    }

    if( qid == 1 ) {
#ifdef DEBUG
	onl_api_debug_message( msgNextBlock, dbgmsg1 );
#endif
	helper_set_meta_default( QM );
	qid = RESQ;
	helper_set_meta_qid( out_port, qid );
helper_check_meta( ring_out );
    } else {
#ifdef DEBUG
	onl_api_debug_message( msgNextBlock, dbgmsg2 );
#endif
	qid = FormRawQid( out_port, RESQ );
	ninq = queue_enq( &queue, ring_in.buf_handle_lo24,
					out_port, qid, ring_in.l3_pkt_len );
#ifdef DEBUG
	helper_sram_dbgmsg_str_1ul( dbgmsg4, ninq );
#endif
	if( ninq == -1 ) {		// hard limit
	    helper_set_errno( BAD_ENQ_ERR );
	    ++ndrops[2];		// number of drops
	    helper_set_out_to_DROP( );
	} else {
	    helper_set_out_to_DO_NOTHING( );
	}
    }
}

void handle_msg_user(){}

void plugin_init_user()
{
    if(ctx() == 0)
    {
	reset_counters( );

	queue_lock = UNLOCKED;
	if (queue_init( &queue ) != 0 ) {
	    helper_set_errno( BAD_QUEUE_INIT_ERR );
	}

#ifdef DEBUGX
	__set_timestamp( 0 );
#endif
    }
}



/**
	----------------------------------------------------------------
 @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);	// assumes do nothing if an invalid value
}


/* handle control messages */
// op codes:
//   get:
//	=npkts	get npkts[k] for all k
//	=ndrops	get ndrops[k] for all k
//	=maxinq	get maxinq[k] for all k
//	=errno	get errno[k] for all k
//	=qstats	get queue counts (queue.npkts, queue.nbytes, queue.ndrops)
//   miscellaneous:
//	reset	reset npkts[], ndrops[], and errno[] counters
//	qreset	reset queues
//
// TO DO:
// o delete unnecessary msg types
//
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(local_mem) char lmem_tmpstr[8];
    __declspec(sram) char sram_inmsgstr[28];

    char GET_npkts[8]	= "=npkts";
    char GET_ndrops[8]	= "=ndrops";
    char GET_maxinq[8]	= "=maxinq";
    char GET_errno[8]	= "=errno";
    char GET_qstats[8]	= "=qstats";
    char GET_xdata[8]	= "=xdata";
    char GET_nerrs[8]	= "=nerrs";
    char RESET[8]	= "reset";
    char BAD_op_err[8]	= "BAD OP";		// error msgs
    char NO_arg_err[8]	= "NO ARG";

//XXX    for(i=0; i<8; ++i) { message[i] = 0; }
    // to get rid of the compiler error:  "Incorrect use of register variable
    // message - check for & operator or non-constant buffer index"
    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_npkts, 8) == 0 ) {
	helper_sram_outmsg_3ul( npkts[0], npkts[1], npkts[2], outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_ndrops, 8) == 0 ) {
	helper_sram_outmsg_3ul( ndrops[0], ndrops[1], ndrops[2], outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_maxinq, 8) == 0 ) {
	helper_sram_outmsg_3ul( queue.maxinq, 0, 0, outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_errno, 8) == 0 ) {
	helper_sram_outmsg_5ul( errno[0], errno[1], errno[2], errno[3],
							errno[4], outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_qstats, 8) == 0 ) {
	helper_sram_outmsg_3ul(	queue.npkts,
				queue.maxinq, queue.ndrops, outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_xdata, 8) == 0 ) {
	helper_sram_outmsg_xdata( outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, GET_nerrs, 8) == 0 ) {
	helper_sram_outmsg_3ul( nerrsA, nerrsB, 0, outmsgstr );
    } else if( strncmp_sram(sram_inmsgstr, RESET, 5) == 0 ) {
	reset_counters( );
    } else {
    	strcat_lmem( outmsgstr, memcpy_lmem_sram(lmem_tmpstr,BAD_op_err,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
//
//o handle_pkt_user()
//
//	if( going to Q64 )	Forward pkt to QM;
//	else {
//	    Enqueue onto qi where i = Max{ K-65, 67 } and K = qid from ring_in;
//	}
//
//o callback()
//
//	n = Get Q0;
//	if( (n== 0) & (q1 > 0) )
//	    { Forward first pkt from q1 to QM;	return; }
//	n += Get Q1;
//	if( (n == 0) & (q2 > 0) )
//	    { Forward first pkt from q2 to QM;	return; }
//	n += Get Q2;
//	if( (n == 0) & (q3 > 0) )
//	    { Forward first pkt from q3 to QM;	return; }
//
//
//
static int mycount = 0;

void callback()
{
    __declspec( gp_reg ) onl_api_qparams	qparams;

//char	dbgmsg2[8] = "qlen =";
//char	dbgmsg3[8] = "quan =";
//char	dbgmsg4[8] = "thre =";

#ifdef DEBUG
    __declspec(local_mem) char	dbgmsg1[8] = "CB a";
#endif

//++mycount;
//if( mycount >= 100000 ) {
//    mycount = 0;
//    onl_api_getQueueParams( 41024, &qparams );
//    helper_sram_dbgmsg_str_1ul( dbgmsg2, qparams.length );
//    helper_sram_dbgmsg_str_1ul( dbgmsg3, qparams.quantum );
//    helper_sram_dbgmsg_str_1ul( dbgmsg3, qparams.threshold );
//}

//XXX
//  ... n =  #pkts in Q64	// WWW
//  ... if( n == 0 ) {		// WWW
//
    onl_api_getQueueParams( 41024, &qparams );	// 5*8192+64

    if( qparams.length == 0 ) {		// empty high-priority queue
	if( queue.npkts > 0 ) {
#ifdef DEBUG
	    onl_api_debug_message( msgNextBlock, dbgmsg1 );
#endif
	    helper_send_from_queue_to_QM( &queue );
	}
    }

    sleep( SLEEP_CYCLES );

}


/* 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
}

// --------------------------------------------------------------------------
// queueing functions
// --------------------------------------------------------------------------

// initialize queue
int
queue_init( __declspec(shared, sram) struct queue_tag *qptr ) {
    int		i;
    int		K = MAX_QUEUE_SZ-1;
    struct item_tag *item_ptr;

    if ( pluginId == 0)		item_ptr = (struct item_tag *) 0xC0100000;
    else if ( pluginId == 1)	item_ptr = (struct item_tag *) 0xC0200000;
    else if ( pluginId == 2)	item_ptr = (struct item_tag *) 0xC0300000;
    else if ( pluginId == 3)	item_ptr = (struct item_tag *) 0xC0400000;
    else if ( pluginId == 4)	item_ptr = (struct item_tag *) 0xC0500000;
    else			return -1;

    qptr->free_hd = item_ptr;
    qptr->hd = qptr->tl = 0;
    qptr->npkts = 0;
    qptr->nbytes = 0;
    qptr->maxinq = 0;
    qptr->ndrops = 0;

    (item_ptr+K)->next = 0;

    for (i=0; i<K; i++) {
    	item_ptr->next = item_ptr+1;
	++item_ptr;
    }

    return 0;
}

// atomically insert item at end of queue
// 	return number of items if OK; else -1
int
queue_enq(	__declspec(shared, sram) struct queue_tag *qptr,
		unsigned int buf_handle, 
		unsigned int out_port, 
		unsigned int qid, 
		unsigned int l3_pkt_len ) {
    struct item_tag	*item;

    while( queue_lock == LOCKED )	{ ctx_swap(); }
    queue_lock = LOCKED;

	item = queue_alloc( &queue );
	if( item == 0 )	{
	    qptr->ndrops = qptr->ndrops + 1;
	    return -1;
	}

	item->buf_handle = buf_handle;
	item->out_port = out_port;
	item->qid = qid;
	item->l3_pkt_len = l3_pkt_len;
	item->next = 0;

	if( qptr->npkts == 0 )	qptr->hd = item;
	else			qptr->tl->next = item;
	qptr->tl = item;

	++(qptr->npkts);
	if( qptr->npkts > qptr->maxinq )	qptr->maxinq = qptr->npkts;

    queue_lock = UNLOCKED;

    return qptr->npkts;
}

// atomically pop front of list
int
queue_pop( __declspec(shared, sram) struct queue_tag *qptr ) {
    struct item_tag	*item;

    while( queue_lock == LOCKED )	ctx_swap();
    queue_lock = LOCKED;

	if( qptr->npkts <= 0 )	return -1;

	item = qptr->hd;
	qptr->hd = item->next;
	--(qptr->npkts);
	if( qptr->npkts == 0 )	qptr->tl = 0;
	queue_free( qptr, item );

    queue_lock = UNLOCKED;

    return 0;
}

// allocate an item
// CAVEAT:  should only be called while queue_lock == LOCKED
struct item_tag *
queue_alloc( __declspec(shared, sram) struct queue_tag *queue ) {
    struct item_tag *item;

    if( queue->free_hd == 0 )	return 0;

    item = queue->free_hd;
    queue->free_hd = item->next;
    return item;
}

// free an item
// CAVEAT:  should only be called while queue_lock == LOCKED
void
queue_free(	__declspec(shared, sram) struct queue_tag *queue,
		struct item_tag *item ) {
    if( item == 0 )	return;
    item->next = queue->free_hd;
    queue->free_hd = item;
}


