/* 
 * $Source: /b/cvsroot//wu_arl/msr/rp/plugins/delay/delay.c,v $
 * $Author: kenw $
 * $Date: 2004/08/10 17:10:39 $
 * $Revision: 1.13 $
 *
 * Changes:
 *
 */

// Notation:
//	- this:  struct rp_instance * (ptr to plugin instance)
// Post-Conditions:
//   pdelay_load
//	- Plugin class (pdelay_class) structure is initialized
//	- pdelay_callback() is registered with kernel as a callback function
//	- global_pdelay_inst_ptr = NULL
//	- pdelay_get_class() is registered with PCU
//   pdelay_create_instance
//	- Plugin instance memory is allocated and initialized
//	  (base class and its extension):
//	  this->rpclass = &pdelay_class
//	  ... other members of base class ...
//	  this->{qlen, pkt_count, drop_count, fwd_count,
//			earliest_depart_time, delay_time, qhead} =
//				{ 0, 0, 0, 0, 0, DELAY_TIME, NULL }
//	- global_pdelay_inst_ptr = this != NULL
//   pdelay_bind_instance
//	- Class extension variables except delay_time are reset:
//	  this->{qlen, pkt_count, drop_count, fwd_count,
//			earliest_depart_time, delay_time, qhead} =
//				{ 0, 0, 0, 0, 0, delay_time, NULL }
//   pdelay_handle_packet
//	- Pkt queue this->qhead contains pkts P such that
//		P.earliest_depart_time > current time
//   pdelay_unbind_instance
//	- Pkt queue is empty
//	- this->drop_count = number of pkts in queue before being emptied
//   pdelay_free_instance
//	- Plugin instance memory is free
//   pdelay_unload
//	- All instances are free
//	- Class is not registered with PCU
//	- pdelay_callback() is no longer a registered callback function
//

#include "stdinc.h"

#include "pdelay.h"

MOD_MISC("pdelay")

/* Local definition and initialization of class object.  Not
 * necessary that it be static.  fredk*/
struct rp_class pdelay_class;
struct rp_instance *global_pdelay_inst_ptr;

void pdelay_init_class (void);

void pdelay_callback(void);

// Note:  Assumes myinst->qlen > 0
void
pdelay_fwdpkts (					// --| forward packets |--
	u_int32_t curr_time,		//
	struct pdelay_instance *myinst,	// instance addr
	int who				// 0: called by pdelay_handle_packet
					// 1: called by pdelay_callback
) {
  struct msr_bufhdr_t	*buffer;	// pkt buffer

  while (curr_time >= myinst->earliest_depart_time) {
    buffer = msr_firstBuffer(&myinst->qhead);
    if (buffer == NULL) {
	MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
		     "pdelay_fwdpkts(%d): qlen > 0 but no valid packet!\n", who));
	return;
    }

    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
		   "pdelay_fwdpkt(%d):  Total %d, QLen %d, Dropped %d, Forwarded = %d\n",
		   who, myinst->pkt_count, myinst->qlen, myinst->drop_count, myinst->fwd_count));

    msr_removeBuffer(&myinst->qhead, buffer);
    myinst->qlen--;
	

    if (PLUGIN_IP_FWD_FCT(buffer) == 0) {
	myinst->fwd_count++;
	MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
		     "pdelay_fwdpkt(%d): Fwd packet at %d, fwd_count = %d, qlen = %d\n", 
		     who, curr_time, myinst->fwd_count, myinst->qlen));
    } else {		// pkt forwarding failed
	MSR_DEBUG((MSR_DEBUG_IPRX | MSR_DEBUG_LEVEL_ERROR,
	     "pdelay_fwdpkt(%d): PLUGIN_IP_FWD_FCT failed; dropping 0x%08x\n",
			     who, (u_int32_t)buffer));
    }
	
    if (myinst->qlen > 0) {		// check next packet
#ifdef PLUGIN_DEBUG
	MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
		     "pdelay_fwdpkt(%d): checking for more packets!\n", who));
#endif
	buffer = msr_firstBuffer(&myinst->qhead);
	if (buffer == NULL) {
	  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
		"pdelay_fwdpkt(%d): Error, qlen > 0 but msr_firstBuffer returns NULL!\n", who));
	  return;
	}
	myinst->earliest_depart_time = buffer->time_fwd;
    }
  }
}

// buffer->time_fwd = arrival time + delay
void
pdelay_callback(void) {
  struct pdelay_instance *myinst;
  struct msr_bufhdr_t	*buffer;	// pkt buffer
  u_int32_t	curr_time;
  int		s	= 0;

  if (global_pdelay_inst_ptr == NULL) {
#ifdef PLUGIN_DEBUG
    curr_time = PLUGIN_CPU_CLOCK_1MSEC_FCT();
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "pdelay_callback: no instance created yet! Current time is: %d\n", curr_time));
#endif
    return;
  }

  myinst = (struct pdelay_instance *) global_pdelay_inst_ptr;
  if (myinst == NULL) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
		 "pdelay_callback: instance created, but inst_ptr is NULL!!\n"));
    return;
  }

#ifdef PLUGIN_DEBUG
  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	       "pdelay_callback: number in queue - %d\n", myinst->qlen));
#endif
  if (myinst->qlen > 0) {	// Get current time; check for expired delays
    s = PLUGIN_SPLCLOCK_FCT();	// Block clock interrupts

    curr_time = PLUGIN_CPU_CLOCK_1MSEC_FCT();
#ifdef PLUGIN_DEBUG
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
		 "pdelay_callback: Current time %d, Earliest packet dequeue time %d\n", 
		 curr_time, myinst->earliest_depart_time));
#endif

    pdelay_fwdpkts (curr_time, myinst, 1);

    PLUGIN_SPLX_FCT(s); 
  }
}


void 
pdelay_init_class (void)			// --| initialize plugin class |--
{
  int poll_freq = cbPeriod;	// callback period
  int ticks = PLUGIN_MSR_USEC2TICKS_FCT(poll_freq);

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	"pdelay_init_class:  poll_freq = %d, ticks = %d, MSR_CLOCK_HANDLER_PCU_ID = x%x\n",
	poll_freq, ticks, MSR_CLOCK_HANDLER_PCU_ID));

  pdelay_class.classid = PDELAY_ID; /* unique id 0xcafebabe */
  pdelay_class.itype   = RP_INTERFACE_TYPE_HLIST;
  pdelay_class.create_instance = pdelay_create_instance;

  // Any other class specific initialization
  global_pdelay_inst_ptr = NULL;

  // set up periodic callback
  if (PLUGIN_MSR_CLOCK_HANDLER_FCT(pdelay_callback, MSR_CLOCK_HANDLER_PCU_ID, ticks) < 0) {
      // error registering the handler
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
          "pdelay_init_class: Error registering periodic callback\n"));
  }
  return;
}

/* return the class structure to the caller */
struct rp_class *pdelay_get_class(void) {
  /* Used by the plugin framework to get class pointer */
  return &pdelay_class;
}

/* Shouldn't we have a class destroy or remove method - that is,
 * the inverse operation of above??? fredk */

/* ----------- class methods ----------- */

/* create and initialize an instance of pdelay,
 * Do we really need to pass a pointer to myclass and the instance id ?? fredk 
 * */
struct rp_instance *
pdelay_create_instance (			// --| create instance |--
	struct rp_class *myclass,
	u_int32_t instanceid)
{
  struct pdelay_instance *myinst; 
  int			s;

  /* Must Do's: 
   *   1) allocate memory
   *   2) Initialize class pointer with your class object
   *   3) initialize method pointers with your instances
   *   4) Other global (base class) fields in rp_instance will
   *      be initialized by teh framework. In the future these other
   *      fields may not be visible directly to instances.
   *   5) Perform any instance specific processing
   *   */

  // XXX kenw:  Should disable interrupts so that (myinst != 0) really means that
  //		instance has been completely created
  s = PLUGIN_SPLCLOCK_FCT();

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO, "delay: create_instance called\n"));

  MSR_PLUGIN_MALLOC(myinst, struct pdelay_instance *, sizeof(struct pdelay_instance), M_MSR, M_WAITOK);

  if (myinst == NULL)
    return NULL;

  myinst->rootinstance.rpclass = &pdelay_class;

  myinst->rootinstance.handle_packet   = pdelay_handle_packet;
  myinst->rootinstance.free_instance   = pdelay_free_instance;
  myinst->rootinstance.bind_instance   = pdelay_bind_instance;
  myinst->rootinstance.unbind_instance = pdelay_unbind_instance;
  myinst->rootinstance.handle_msg      = pdelay_handle_msg;

  msr_initBuffer(&myinst->qhead);	// Instance specific processing
  myinst->qlen       = 0;
  myinst->pkt_count  = 0;
  myinst->drop_count = 0;
  myinst->fwd_count  = 0;
  myinst->max_qlen  = 0;
  myinst->delay_time = DELAY_TIME;
  myinst->earliest_depart_time = 0;

  global_pdelay_inst_ptr = (struct rp_instance *) myinst;	// global:  used later

  PLUGIN_SPLX_FCT(s);

  /* return pointer to newly created instance.  Not, the semantics are that
   * the class owns this memory and so the class (free instance) should
   * free it! fredk */

  return (struct rp_instance *)myinst;
}

void
pdelay_handle_packet(				// --| handle incoming pkts |--
  struct rp_instance *this,		// points to instance structure
  void *bufferList)			// points to list of pkt buffers
{
  struct pdelay_instance *inst	= (struct pdelay_instance *)this;
  u_int32_t		curr_time;	// Current time in ms
  struct msr_bufhdr_t	*buffer;	// incoming pkt buffer
  int			s;

  s = PLUGIN_SPLCLOCK_FCT();

  buffer = msr_firstBuffer(bufferList);
  if (buffer == NULL) {	// get 1st (only) pkt on buffer list
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
          "pdelay_handle_packet: No incoming pkt!\n"));
    return;
  }

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
        "pdelay_handle_packet: Total %d, QLen %d, Drop %d, Fwd %d\n",
        inst->pkt_count, inst->qlen, inst->drop_count, inst->fwd_count));

  msr_removeBuffer(bufferList, buffer);		// rm pkt from pkt list
  inst->pkt_count++;

  curr_time = PLUGIN_CPU_CLOCK_1MSEC_FCT();
  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
          "pdelay_handle_packet: Got packet at time - %d\n", curr_time));
  buffer->time_fwd = curr_time + inst->delay_time;	// when pkt should go out

  msr_addBuffer(&inst->qhead, buffer);		// queue pkt
  inst->qlen++;
  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
	     "pdelay_handle_packet: Adding 0x%08x to internal queue (List head 0x%08x, len %d)\n",
	     (u_int32_t)buffer, (u_int32_t)msr_firstBuffer(&inst->qhead), inst->qlen));
  if (inst->max_qlen < inst->qlen)	inst->max_qlen = inst->qlen;
  if (inst->qlen == 1) {
    inst->earliest_depart_time = curr_time + inst->delay_time;
  }
MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,		// ZZZ
	     "pdelay_handle_packet: Insert at %d, earliest_depart = %d, qlen = %d\n",
					curr_time, inst->earliest_depart_time, inst->qlen));

  pdelay_fwdpkts (curr_time, inst, 0);

  PLUGIN_SPLX_FCT(s);

  return;

}

// Perform any necessary actions then free memory for this instance
void 
pdelay_free_instance(struct rp_instance *this)		/* --| */
{
  MSR_PLUGIN_FREE(this, M_MSR);
}

// Called when instance bound to a filter
// Note: this->bound_fid == filter id this instance is bound to. 
void 
pdelay_bind_instance(struct rp_instance *this)		/* --| */
{
  struct pdelay_instance *inst = (struct pdelay_instance *)this;
  /* make sure we start of with our packet counter zeroed */
  msr_initBuffer(&inst->qhead);
  inst->qlen       = 0;
  inst->pkt_count  = 0;
  inst->drop_count = 0;
  inst->fwd_count  = 0;
  return;
}

// Called when instance unbound from a filter
// Note: this->bound_fid == filter id this instance is currently bound to. 
void 
pdelay_unbind_instance(struct rp_instance *this)	/* --| */
{
  struct pdelay_instance *inst = (struct pdelay_instance *)this;
  struct msr_bufhdr_t *hdr;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
        "Delay Unbind: Total = %d, qlen == %d, Dropped = %d, Fwded = %d\n",
        inst->pkt_count, inst->qlen, inst->drop_count, inst->fwd_count));

  if (inst->qlen != 0) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
          "pdelay_unbind_instance: Must first drop %d packets in queue\n",
          inst->qlen));
    hdr = msr_firstBuffer(&inst->qhead);
    while (hdr != NULL) {
      PLUGIN_IPBUF_FREE_FCT (hdr);
      inst->qlen--;
      inst->drop_count++;
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
            "Delay Unbind: qlen == %d, Dropped %d\n",
            inst->qlen, inst->drop_count));
      hdr = msr_nextBuffer(hdr);
    }
  }
    
  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	    "pdelay_unbind_instance: Instance %d Being Unbound from filter id %d. Packet count = %d\n",
	    inst->rootinstance.instanceid, inst->rootinstance.bound_fid));
  return;
}


/* ----------- loadable kernel module support ----------- */


/*  This defines the kernel entry point for our loadable
 *  kernel module (LKM).  It should be of type MISC and
 *  modload(8) and modunload(8) are used to load, unload 
 *  the kernel (using the /dev/lkm device).  modstat can 
 *  be used to see if the module has been loaded and its status.
 *
 * Miscellaneous Modules - there is no existing (NetBSD) interfaces in the
 * kernel that uses these modules.  This allows crossbow to manage these
 * modules once they are loaded without the std NetBSD kernel getting in the
 * way.  
 *
 */

/* XXX int or void return type? */

#ifdef PLUGIN_DEBUG
int
pdelay (struct lkm_table *lkmtp, int cmd, int ver)		/* --| */
{
  struct kernel_plugin_fct_struct *fctPtr;
  extern struct kernel_plugin_fct_struct  MSR_kernel_plugin_fcts;
  fctPtr = &MSR_kernel_plugin_fcts;
#else
int 
pdelay (struct lkm_table *lkmtp, int cmd, int ver, struct kernel_plugin_fct_struct *fctPtr) 
{
#endif
    // Do NOT put anything before the kernel_plugin_fcts is set, especially if it uses any 
    // of the MSR_ macros!!!
    if (kernel_plugin_fcts == NULL) {
	kernel_plugin_fcts = fctPtr;
	kernel_plugin_variables = fctPtr->pluginVariables;
    }
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	       "pdelay: entry function entered\n"));
    MSR_PLUGIN_DISPATCH(lkmtp, cmd, ver, pdelay_load, pdelay_unload, PLUGIN_LKM_NOFUNC_FCT);
    // We do not come back from the macro MSR_PLUGIN_DISPATCH, it has a return() in it for every case
}


// This function is called each time the module is loaded. 
int
pdelay_load(struct lkm_table *lkmtp, int cmd) 		/* --| */
{
  int err;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "pdelay_load: entered\n"));

  /*
   * Don't load twice! (lkmexists() is exported by kern_lkm.c)
   */
  if (PLUGIN_LKM_EXISTS_FCT(lkmtp)) {
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
		 "pdelay_load: returning EEXIST\n"));
      return (EEXIST);
  }

  /* initialize class */
  pdelay_init_class();

  err = PLUGIN_PCU_REGISTER_CLASS_FCT(pdelay_get_class());

  if (err != RP_OK) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
	  "pdelay_load: Error (err = %d) encountered registering class.\n", err));
    return -1;
  }

  return 0;
}

// This function is called each time the module is unloaded. 
int 
pdelay_unload(struct lkm_table *lkmtp, int cmd) 		/* --| */
{
  struct rp_class *rpclass;
  u_int32_t cid;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "pdelay_unload: entered about to call pdelay_get_class()\n"));

  rpclass = pdelay_get_class();

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "pdelay_unload: got rpclass = %p\n", rpclass));

  cid = rpclass->classid;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "pdelay_unload: got cid = 0x%lx\n", cid));

  // deregister callback
  if (PLUGIN_MSR_HANDLER_RESET_FCT(MSR_CLOCK_HANDLER_PCU_ID, 0) < 0) {
    // error deregistering the handler
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
	"pdelay_init_class: Error registering periodic callback\n"));
    return -1;
  }

  if (PLUGIN_PCU_FREE_ALL_INSTANCES_FCT(rpclass) != RP_OK) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
	     "pdelay_unload: Error freeing all instances for class %d\n", cid));
    return -1;
  }

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	     "pdelay_unload: back from free_all_instances, now calling deregister_class\n"));

  if (PLUGIN_PCU_DEREGISTER_CLASS_FCT(rpclass) != RP_OK) {
     MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
               "pdelay_unload: Error deregistering class\n", cid));
  }

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
	"pdelay_unload: Unloaded class %d\n", cid));

  return 0;
}

/* Inplement your own message handling protocol here 
 * The buffer is 44 Bytes long and the same buffer will be sent back to the
 * CP.  So, overwrite the contents.
 * The first 32-bit word is the Instance ID.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Request msg (2 words):  0) Instance ID, 1) Command
 * Reply msg (4 words):  0) #pkts handled, 1) #pkts dropped, 2) #pkts forwarded,
 * 			3) current delay time (msec)
 * Request codes:	0) Display pkt queue (Debug).  Return counts, and delay
 *			1) Return counts and delay
 *			2) Change delay; Return counts and delay
 *			*) Return counts and delay
 * */
int
pdelay_handle_msg(						/* --| */
  struct rp_instance *this,	// points to instance structure
  void *buf,			// points to request/reply buffer
  u_int8_t flags,		// flags
  u_int8_t seq,			// sequence number
  u_int8_t *len)		// IN:  request is in buf[0] thru buf[*len/4-1]
				// OUT:	reply is in buf[0] thru buf[*len/4-1]
{
  struct pdelay_instance *inst = (struct pdelay_instance *)this;
  u_int32_t	*vals	= (u_int32_t *)buf;
  u_int32_t	id	= (u_int32_t)ntohl(*vals);
  u_int32_t	cmnd	= (u_int32_t)ntohl(*(vals + 1));

  /* The first 4 byte word is the Instance Number for us. */
  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
         "pdelay_handle_msg: Flags 0x%x, Len %d, Seq %d, Instance %ud\n",
	    flags, *len, seq, this->instanceid));

  switch (cmnd) {
  case 0:
    {
    struct msr_bufhdr_t *buffer;
    int		i;
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
		   "Printing %d Headers in Queue\n", inst->qlen));
    if (inst->qlen > 0) {
      i = 0;
      for (buffer = msr_firstBuffer(&inst->qhead);
			buffer != NULL; buffer = msr_nextBuffer(buffer)) {
          MSR_DEBUG( (MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
                  "%d) Header 0x%08x, atmlen = %d\n",
		  i, (u_int32_t)buffer, buffer->atmlen) );
          i++;
      }
    }
    }
    break;
  case 1:	// get delay (well, it's already in every reply msg)
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
		   "Command %d (Get delay)\n", cmnd));
    break;
  case 2:	// change delay
    {
    u_int32_t	new_delay = (u_int32_t)ntohl(*(vals + 2));
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
		   "Command %d (Change delay) to %d msec\n", cmnd, new_delay));
    inst->delay_time = new_delay;
    }
    break;
  case 3:	// reset counters
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
		   "Command %d (reset counters)\n", cmnd));
    inst->pkt_count = 0;
    inst->drop_count = 0;
    inst->fwd_count = 0;
    inst->max_qlen = 0;
    break;
  case 4:	// change callback period
    {
    int	poll_freq;
    int ticks;
    cbPeriod = (u_int32_t)ntohl(*(vals + 2));
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
	   "Command %d (Change cbPeriod) to %d\n", cmnd, cbPeriod));

    poll_freq = cbPeriod;
    ticks = PLUGIN_MSR_USEC2TICKS_FCT(poll_freq);

    if (PLUGIN_MSR_CLOCK_HANDLER_FCT(pdelay_callback,
			MSR_CLOCK_HANDLER_PCU_ID, ticks) < 0) {
      // error registering the handler
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
            "pdelay_handle_msg: Error registering periodic callback\n"));
    }
    }
    break;
  default:
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
		   "Default:  command %d\n", cmnd));
    break;
  }
  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
	"Delay Counts: Total %d, Dropped %d, Forwarded %d, Delay %d, Max Queued %d\n", 
	inst->pkt_count, inst->drop_count, inst->fwd_count, inst->delay_time,
	inst->max_qlen));

  *vals++ = (u_int32_t)htonl(inst->pkt_count);
  *vals++ = (u_int32_t)htonl(inst->drop_count);
  *vals++ = (u_int32_t)htonl(inst->fwd_count);
  *vals++ = (u_int32_t)htonl(inst->max_qlen);
  *vals++ = (u_int32_t)htonl(inst->delay_time);
  *vals++ = (u_int32_t)htonl(cbPeriod);
  *len = ((char *)vals) - ((char *)buf);
  return 0;
}

