/* **********************************************************
 * Copyright (C) 1998-2000 VMware, Inc.
 * All Rights Reserved
 * **********************************************************/

#include "driver-config.h"

#ifdef KERNEL_2_1
#  define EXPORT_SYMTAB
#  ifndef __KERNEL__
#    define __KERNEL__
#  endif
#endif

#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif

#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#ifdef KERNEL_2_1
#include <linux/poll.h>
#include <asm/uaccess.h>
#endif

#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/mm.h>
#include <linux/skbuff.h>
#include <linux/if_ether.h>
#include <linux/sockios.h>
#include <net/sock.h>

#define __KERNEL_SYSCALLS__
#include <asm/io.h>

#include <linux/proc_fs.h>
#include <linux/file.h>

#include "vm_oui.h"
#include "vnet.h"
#include "vnetInt.h"

#include "print_string.h"
#include <linux/time.h>

#ifdef PACIA_TRACE
#include "utils.h"
#include "ports.h"
#include "global_tracking.h"

static struct sk_buff * curr_skb;  // the current sk_buff of interest
static struct timeval begin_time;  // time when it first entered vmnet 
static direction curr_dir;

#endif // PACIA_TRACE

#ifdef TOTAL_BYTES_TRACE
#include "global_tracking.h"
#include "utils.h"
#include "ports.h"

histogram bytes_per_packet;
histogram bytes_per_message;
histogram data_bytes_per_message;
histogram data_packets_per_message;
histogram control_packets_per_message;
histogram total_packets_per_message;

size_t control_packets_sent = 0;
size_t total_bytes_sent = 0;
size_t total_data_bytes_sent = 0;
size_t total_packets_sent = 0;

int current_msg_id;

int message_byte_count = 0;
int message_data_byte_count = 0;
int message_data_packet_count = 0;
int message_control_packet_count = 0;
int message_total_packet_count = 0;


#endif // TOTAL_BYTES_TRACE

typedef struct VNetUserIFStats {
   unsigned    read;
   unsigned    written;
   unsigned    queued;
   unsigned    droppedDown;
   unsigned    droppedMismatch;
   unsigned    droppedOverflow;
} VNetUserIFStats;

typedef struct VNetUserIF {
   VNetPort               port;
   struct sk_buff_head    packetQueue;
#ifndef KERNEL_2_3_1
   struct wait_queue     *waitQueue;
#else
   wait_queue_head_t      waitQueue;
#endif
   VNetUserIFStats        stats;
} VNetUserIF;


static void VNetUserIfFree(VNetJack *this);
static void VNetUserIfReceive(VNetJack *this, struct sk_buff *skb);

static int  VNetUserIfRead(VNetPort *port, struct file *filp,
                           char *buf, size_t count);
static int  VNetUserIfWrite(VNetPort *port, struct file *filp,
                            const char *buf, size_t count);
static int  VNetUserIfIoctl(VNetPort *this, struct file *filp,
                            unsigned int iocmd, unsigned long ioarg);   
#ifdef KERNEL_2_1
static int  VNetUserIfPoll(VNetPort *this, struct file *filp,
                           poll_table *wait);
#else
static int  VNetUserIfSelect(VNetPort *this, struct file *filp,
                             int sel_type, select_table *wait);
#endif

#ifdef KERNEL_2_1
static int VNetUserIfProcRead(char *page, char **start, off_t off,
                              int count, int *eof, void *data);
#endif


#ifdef TOTAL_BYTES_TRACE



//
// Histogram routines
//

void initHistogram(histogram *h, int numBuckets, int minValue, int maxValue, char *name)
{
  h->numBuckets = numBuckets;
  h->bucketSize = (maxValue - minValue)/numBuckets;
  h->minValue = minValue;
  h->maxValue = maxValue;
  h->range = maxValue - minValue;
  //  h->arr = kmalloc(sizeof(int)*(numBuckets+4));
  memset(h->arr, 0, sizeof(int)*(numBuckets+4));
  strcpy(h->name, name);
}

void addHistogram(histogram *h, int bucketVal, int inc)
{
  int index;

  if (bucketVal == h->minValue) {
    h->arr[h->numBuckets] += inc;
  } 
  if (bucketVal == h->maxValue) {
    h->arr[h->numBuckets+1] += inc;
  }

  index = (bucketVal - h->minValue) / h->bucketSize;
  // on the border, put in lower bucket (unless already in lowest bucket)
  if ( (bucketVal - h->minValue) % h->bucketSize == 0 && index != 0) {
    index--;
  }

  // check for overflow/underflow
  if (index >= h->numBuckets) {
    h->arr[h->numBuckets+2] += inc;
  } else if (index < 0) {
    h->arr[h->numBuckets+3] += inc;
  } else {
    h->arr[index] += inc;
  }


}


void printHistogramMergeZero(histogram *h)
{
  int i;
  int start_bucket, end_bucket;
  int total = 0;
  int cumulative = 0;
  char buf[1000];

  for (i=0; i < h->numBuckets; i++) {
    total += h->arr[i];
  }

  total += h->arr[h->numBuckets+2] + h->arr[h->numBuckets+3];

  start_bucket = h->minValue;
  end_bucket = h->minValue + h->bucketSize;

  print_string(h->name);

  for (i = 0; i < h->numBuckets; i++) {

    if (i == (h->numBuckets-1) || h->arr[i] != 0 || h->arr[i+1] != 0) {

      cumulative += h->arr[i];

      sprintf(buf, "%5d-%5d: %10d (%2.1f)", 
	      start_bucket, end_bucket,
	      h->arr[i], (total == 0) ? 0 : (float)cumulative/(float)total * (float)100);
      print_string(buf);
      start_bucket = end_bucket;
    }
    end_bucket = end_bucket + h->bucketSize;
  }

  sprintf(buf, "\nOVERFLOW:  %10d", h->arr[h->numBuckets+2]);
  print_string(buf);
  sprintf(buf, "UNDERFLOW: %10d", h->arr[h->numBuckets+3]);
  print_string(buf);
  sprintf(buf, "EXACTLY %5d: %10d", h->minValue, h->arr[h->numBuckets]);
  print_string(buf);
  sprintf(buf, "EXACTLY %5d: %10d", h->maxValue, h->arr[h->numBuckets+1]);
  print_string(buf);
}
  
void printHistogram(histogram *h)
{
  int i;
  int start_bucket, end_bucket;
  int total = 0;
  int cumulative = 0;
  char buf[1000];
  int highest_nonzero = 0;

  for (i=0; i < h->numBuckets; i++) {
    total += h->arr[i];
    if (h->arr[i] != 0) {
      highest_nonzero = i;
    }
  }

  print_string(h->name);

  total += h->arr[h->numBuckets+2] + h->arr[h->numBuckets+3];

  start_bucket = h->minValue;
  end_bucket = h->minValue + h->bucketSize;

  for (i = 0; i < highest_nonzero; i++) {

    cumulative += h->arr[i];
    
    sprintf(buf, "%5d-%5d: %10d (%2.1f)",
	    start_bucket, end_bucket,
	    h->arr[i], (total == 0) ? 0 : (float)cumulative/(float)total * (float)100);
    print_string(buf);
    start_bucket = end_bucket;
    end_bucket = end_bucket + h->bucketSize;
  }

  sprintf(buf, "\nOVERFLOW:  %10d", h->arr[h->numBuckets+2]);
  print_string(buf);
  sprintf(buf, "UNDERFLOW: %10d", h->arr[h->numBuckets+3]);
  print_string(buf);
  sprintf(buf, "EXACTLY %5d: %10d", h->minValue, h->arr[h->numBuckets]);
  print_string(buf);
  sprintf(buf, "EXACTLY %5d: %10d\n", h->maxValue, h->arr[h->numBuckets+1]);
  print_string(buf);
}

void printHistogramWeightedPct(histogram *h, float pct)
{
  int i;
  int start_bucket, end_bucket, last_bucket;
  int total = 0;
  int weighted_total = 0;
  int cumulative = 0;
  int bucket_cum = 0;
  int checkpoint_val;
  int checkpoint_interval;
  int weighted_cumulative = 0;
  char buf[1000];

  print_string(h->name);

  for (i=0; i < h->numBuckets; i++) {
    total += h->arr[i];
    weighted_total +=  (h->minValue + (h->bucketSize * i)) * h->arr[i];
  }

  total += h->arr[h->numBuckets+2] + h->arr[h->numBuckets+3];

  pct = pct/100.0;
  checkpoint_val = weighted_total *pct;
  checkpoint_interval = weighted_total*pct;

  // to avoid infinite loop
  if (checkpoint_interval == 0) checkpoint_interval = 1;

  start_bucket = h->minValue;
  end_bucket = h->minValue + h->bucketSize;

  for (i = 0; i < h->numBuckets; i++) {

    cumulative += h->arr[i];
    weighted_cumulative += (h->minValue + (h->bucketSize * i)) * h->arr[i];
    bucket_cum += h->arr[i];

    if (h->arr[i]) last_bucket = end_bucket;

    if (weighted_cumulative >= checkpoint_val) {
      sprintf(buf, "%5d-%5d: %10d (%4.1f) (%4.1f)\n", 
	      start_bucket, end_bucket,
	      bucket_cum, (total == 0) ? 0 : (float)cumulative/(float)total * (float)100,
	      (weighted_total == 0) ? 0 : (float)weighted_cumulative/(float)weighted_total * (float)100);
      print_string(buf);

      start_bucket = end_bucket;

      bucket_cum = 0;
      while (checkpoint_val <= weighted_cumulative) {
	checkpoint_val += checkpoint_interval;
      }
    } else if (i == h->numBuckets-1 && bucket_cum > 0) {
      sprintf(buf, "%5d-%5d: %10d (%2.1f)\n", 
	      start_bucket, last_bucket,
	      bucket_cum, (total == 0) ? 0 : (float)cumulative/(float)total * (float)100);
      print_string(buf);
      start_bucket = end_bucket;

      bucket_cum = 0;
    }
    end_bucket = end_bucket + h->bucketSize;
  }

  sprintf(buf, "\nOVERFLOW:  %10d", h->arr[h->numBuckets+2]);
  print_string(buf);
  sprintf(buf, "UNDERFLOW: %10d", h->arr[h->numBuckets+3]);
  print_string(buf);
  sprintf(buf, "EXACTLY %5d: %10d", h->minValue, h->arr[h->numBuckets]);
  print_string(buf);
  sprintf(buf, "EXACTLY %5d: %10d\n", h->maxValue, h->arr[h->numBuckets+1]);
  print_string(buf);
}

#endif // TOTAL_BYTES_DEF thingee

/*
 *----------------------------------------------------------------------
 *
 * VNetUserIf_Create --
 *
 *      Create a user level port to the wonderful world of virtual
 *      networking.
 * 
 * Results: 
 *      Errno. Also returns an allocated port to connect to,
 *      NULL on error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetUserIf_Create(VNetPort **ret)
{
   VNetUserIF *userIf = NULL;
   static unsigned id = 0;
   int retval;
   
   userIf = (struct VNetUserIF*)kmalloc(sizeof(struct VNetUserIF), GFP_KERNEL);
   if (!userIf) {
      return -ENOMEM;
   }

#ifdef PACIA_TRACE   
{
	char msg_buf[256];
	char * fname = "VNetUserIf_Create";
	sprintf (msg_buf, "%s: called\n", fname);
	print_string (msg_buf);
}
#endif // PACIA_TRACE   

   /*
    * Initialize fields.
    */
   
   userIf->port.id = id++;

   userIf->port.jack.peer = NULL;
   userIf->port.jack.numPorts = 1;
   sprintf(userIf->port.jack.name, "userif%d", userIf->port.id);
   userIf->port.jack.private = userIf;
   userIf->port.jack.index = 0;
   userIf->port.jack.procEntry = NULL;
   userIf->port.jack.free = VNetUserIfFree;
   userIf->port.jack.rcv = VNetUserIfReceive;
   userIf->port.jack.cycleDetect = NULL;
   userIf->port.jack.portsChanged = NULL;
   userIf->port.jack.isBridged = NULL;

   /*
    * Make proc entry for this jack.
    */
   
   retval = VNetProc_MakeEntry(NULL, userIf->port.jack.name, S_IFREG,
                               &userIf->port.jack.procEntry);
   if (retval) {
      if (retval == -ENXIO) {
         userIf->port.jack.procEntry = NULL;
      } else {
         kfree(userIf);
         return retval;
      }
   } else {
#ifdef KERNEL_2_1
      userIf->port.jack.procEntry->read_proc = VNetUserIfProcRead;
      userIf->port.jack.procEntry->data = userIf;
#else
      userIf->port.jack.procEntry = NULL;
#endif
   }

   /*
    * Rest of fields.
    */
   
   userIf->port.flags = IFF_RUNNING;

   memset(userIf->port.paddr, 0, sizeof userIf->port.paddr);
   memset(userIf->port.ladrf, 0, sizeof userIf->port.ladrf);

   userIf->port.paddr[0] = VMX86_OUI0;
   userIf->port.paddr[1] = VMX86_OUI1;
   userIf->port.paddr[2] = VMX86_OUI2;

   userIf->port.fileOpRead = VNetUserIfRead;
   userIf->port.fileOpWrite = VNetUserIfWrite;
   userIf->port.fileOpIoctl = VNetUserIfIoctl;
#ifdef KERNEL_2_1
   userIf->port.fileOpPoll = VNetUserIfPoll;
#else
   userIf->port.fileOpSelect = VNetUserIfSelect;
#endif
   
   skb_queue_head_init(&(userIf->packetQueue));
#ifndef KERNEL_2_3_1
   userIf->waitQueue = NULL;
#else
   init_waitqueue_head(&userIf->waitQueue);
#endif

   memset(&userIf->stats, 0, sizeof(VNetUserIFStats));
   
   *ret = (VNetPort*)userIf;
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetUserIfFree --
 *
 *      Free the user interface port.
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
VNetUserIfFree(VNetJack *this)
{
   VNetUserIF *userIf = (VNetUserIF*)this;
   struct sk_buff *skb;

#ifdef PACIA_TRACE   
{
	char msg_buf[256];
	char * fname = "VNetUserIfFree";
	sprintf (msg_buf, "%s: called\n", fname);
	print_string (msg_buf);
}
#endif // PACIA_TRACE   

   for (;;) {
      skb = skb_dequeue(&userIf->packetQueue);
      if (skb == NULL) {
	 break;
      }
      DEV_KFREE_SKB(skb, FREE_WRITE);
   }

   if (this->procEntry) {
      VNetProc_RemoveEntry(this->procEntry, NULL);
   }

   kfree(userIf);
}


/*
 *----------------------------------------------------------------------
 *
 * VNetUserIfReceive --
 *
 *      This jack is receiving a packet. Take appropriate action.
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      Frees skb.
 *
 *----------------------------------------------------------------------
 */

void
VNetUserIfReceive(VNetJack        *this,
                  struct sk_buff  *skb)
{
   VNetUserIF *userIf = (VNetUserIF*)this->private;
   uint8 *dest = SKB_2_DESTMAC(skb);
   
   if (!UP_AND_RUNNING(userIf->port.flags)) {
      userIf->stats.droppedDown++;
      goto drop_packet;
   }
   
   if (!VNetPacketMatch(dest,
                        userIf->port.paddr,
                        userIf->port.ladrf,
                        userIf->port.flags)) {
      userIf->stats.droppedMismatch++;
      goto drop_packet;
   }
   
   if (skb_queue_len(&userIf->packetQueue) >= VNET_MAX_QLEN) {
      userIf->stats.droppedOverflow++;
      goto drop_packet;
   }
   
   userIf->stats.queued++;

#ifndef KERNEL_2_3_43
   start_bh_atomic();
#endif
   skb_queue_tail(&userIf->packetQueue, skb);
#ifndef KERNEL_2_3_43
   end_bh_atomic();
#endif
   wake_up(&userIf->waitQueue);
   return;
   
 drop_packet:
   DEV_KFREE_SKB(skb, FREE_WRITE);
}


/*
 *----------------------------------------------------------------------
 *
 * VNetUserIfRead --
 *
 *      The virtual network's read file operation. Reads the next pending
 *      packet for this network connection.
 *
 * Results: 
 *      On success the len of the packet received,
 *      else if no packet waiting and nonblocking 0,
 *      else -errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int 
VNetUserIfRead(VNetPort     *port,
               struct file  *filp,
               char         *buf,
               size_t        count)
{
   VNetUserIF *userIf = (VNetUserIF*)port->jack.private;
   struct sk_buff *skb;
#ifdef KERNEL_2_1
#ifdef KERNEL_2_3_43
   /* All 2.3.1 and newer should use this code path... */
   int ret;
   DECLARE_WAITQUEUE(wait, current);
#else
   int i;
   sigset_t sigs;
#endif
#endif

// XXX 736 stuff
#ifdef PACIA_TRACE   
	char * fname = "VNetUserIfRead";
	char msg_buf[256];
  struct timeval tv;
  struct timeval now_tv;
  int print = 0; // whether or not to print information
  do_gettimeofday(&tv);
#endif


#ifdef KERNEL_2_3_43
   add_wait_queue(&userIf->waitQueue, &wait);
   current->state = TASK_INTERRUPTIBLE;
   for (;;) {
      ret = -EAGAIN;
      skb = skb_dequeue(&userIf->packetQueue);
      if (skb != NULL || filp->f_flags & O_NONBLOCK) {
         break;
      }
      ret = -EINTR;
      if (signal_pending(current)) {
         break;
      }
      schedule();
   }
   current->state = TASK_RUNNING;
   remove_wait_queue(&userIf->waitQueue, &wait);
   if (!skb)
      return ret;
#else   
   for (;;) {
      start_bh_atomic();
      skb = skb_dequeue(&userIf->packetQueue);
      end_bh_atomic();
      if (skb != NULL || filp->f_flags & O_NONBLOCK) {
	 break;
      }
      interruptible_sleep_on(&userIf->waitQueue);
#ifdef KERNEL_2_1
      signandsets(&sigs, &current->signal, &current->blocked);
      for (i = 0; i < _NSIG_WORDS; i++) {
	 if (sigs.sig[i] != 0) {
	    return -EINTR;
	 }
      }
#else
      if (current->signal & ~current->blocked) {
	 return -EINTR;
      }
#endif
   }

   if (!skb) {
      return -EAGAIN;
   }
#endif

/* Set print to 1 iff this is a packet from the server to the
 * client. */
#ifdef PACIA_TRACE   
{
	__u16 src = 0, dst = 0;
	int retval;
	retval = getTcpPorts (skb, &src, &dst);
	if (retval != 0
		&& (src == DEFAULT_SERVER_PORT
			|| src == DEFAULT_CLIENT_PORT)
		&& (dst == DEFAULT_CLIENT_PORT
			|| dst == DEFAULT_SERVER_PORT)
		)
	{
		print = 1;
	}
}
#endif // PACIA_TRACE   

// XXX  736 stuff
// don't print this until we know it's returning something
#ifdef PACIA_TRACE   
	if (print != 0)
	{
		sprintf (msg_buf, "%s: called on port id %u", fname, port->id);
		print_string (msg_buf);
	}
#endif // PACIA_TRACE   

// XXX  736 stuff
#ifdef PACIA_TRACE   
	if (print != 0)
	{
		// print the skb
		print_skb (skb);
	}
#endif // PACIA_TRACE   

   userIf->stats.read++;
   count = (count < skb->len) ? count : skb->len;
   memcpy_tofs(buf, skb->data, count);

   DEV_KFREE_SKB(skb, FREE_WRITE);

#ifdef PACIA_TRACE   
if (print != 0)
{
	char buf_buf[80];
	size_t i;
	int num_x = 0, num_z = 0;

	for (i = 0; i < count; i++)
	{
		if (buf[i] == 'X')
		{
			num_x++;
		}
		if (buf[i] == 'Z')
		{
			num_z++;
		}
	}

	sprintf (buf_buf, "%u Xs, %u Zs", num_x, num_z);
	print_string (buf_buf);
}
#endif // PACIA_TRACE   

#ifdef PACIA_TRACE   
	if (print != 0)
	{
		unsigned int foo_us;
		do_gettimeofday(&now_tv);
	  	foo_us = abs (1000000*(now_tv.tv_sec  - tv.tv_sec));
		foo_us += abs (now_tv.tv_usec - tv.tv_usec);
		sprintf (msg_buf, "%s: call took %u us", fname, foo_us);
		print_string (msg_buf);

		/*
		if (curr_dir == in
			&& skb == curr_skb)
		*/
		{
			unsigned int elapsed_us;
	  		elapsed_us = abs (1000000*(now_tv.tv_sec - begin_time.tv_sec));
			elapsed_us += abs (now_tv.tv_usec - begin_time.tv_usec);
			sprintf (msg_buf, "Message 0x%x received! took %u us",
					curr_skb, elapsed_us);
			print_string (msg_buf);
			curr_skb = NULL;
		}
	}
#endif

   return count;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetUserIfWrite --
 *
 *      The virtual network's write file operation. Send the raw packet
 *      to the network.
 *
 * Results: 
 *      On success the count of bytes written else errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int 
VNetUserIfWrite(VNetPort     *port,
                struct file  *filp,
                const char   *buf,
                size_t        count)
{
   VNetUserIF *userIf = (VNetUserIF*)port->jack.private;
   struct sk_buff *skb;
   int *msg_id; 

#ifdef PACIA_TRACE   
	char msg_buf[256];
	char * fname = "VNetUserIfWrite";
	struct timeval tv;
 	struct timeval now_tv;
	int print = 0;
	do_gettimeofday(&tv);
#endif

   /*
    * Check size
    */
   
   if (count < sizeof (struct ethhdr)) {
      return -EINVAL;
   }
   
   /*
    * Allocate an sk_buff.
    */
   
   skb = dev_alloc_skb(count + 7);
   if (skb == NULL) {
      // XXX obey O_NONBLOCK?
      return -ENOBUFS;
   }
#ifndef KERNEL_2_1
   skb->free = 1;
#endif
   
   skb_reserve(skb, 2);
   
   /*
    * Copy the data and send it.
    */
   
   userIf->stats.written++;
   memcpy_fromfs(skb_put(skb, count), buf, count);

/* Set print to 1 iff this is a packet from the client to the
 * server. */
#ifdef PACIA_TRACE   
{
	__u16 src = 0, dst = 0;
	int retval;
	retval = getTcpPorts (skb, &src, &dst);
	if (retval != 0
		&& (src == DEFAULT_SERVER_PORT
			|| src == DEFAULT_CLIENT_PORT)
		&& (dst == DEFAULT_CLIENT_PORT
			|| dst == DEFAULT_SERVER_PORT)
		)
	{
		print = 1;

		/* set up the stuff to track the time it takes for this
		 * message to get through vmnet */
		begin_time.tv_sec = tv.tv_sec;
		begin_time.tv_usec = tv.tv_usec;
  		curr_dir = out;
		curr_skb = skb;
	}
}
#endif // PACIA_TRACE   

#ifdef TOTAL_BYTES_TRACE
	{
		/* Check the ports. */
	__u16 src = 0, dst = 0;
	int retval;
	retval = getTcpPorts (skb, &src, &dst);
	if (retval != 0
		&& (src == DEFAULT_SERVER_PORT
			|| src == DEFAULT_CLIENT_PORT)
		&& (dst == DEFAULT_CLIENT_PORT
			|| dst == DEFAULT_SERVER_PORT)
		)
		{
		  //		print_skb (skb);
		  /* Subtract 66 because that's the overhead of a
		   * TCP/IP packet. */
		  total_bytes_sent += count;
		  total_data_bytes_sent += (count - 66);
		  total_packets_sent++;
		  
		  // assumption: messages with count = 66 are control msgs
		  if (count == 66) {
		    // control packet
		    control_packets_sent++;
		    
		  } 


		    // data packet
		  //		    message_data_packet_count++;
		  //		    message_data_byte_count += (count-66);
		  //		  }

		  // for all packets
		  //		  message_byte_count += count;
		  //		  message_total_packet_count++;
		  //		}
		  
		  //		  addHistogram(&bytes_per_packet, count, 1);
		  
		  // detect if beginning of message
		  //		  if (count > 68 && buf[66] == 'B') {
		  //		    msg_id = (int *)&buf[67];

		  //		    if (*msg_id != current_msg_id) {
		      // new message, write out old message data if appropr.
		      
		  //		      if (current_msg_id != -1 && 
		  //			  message_total_packet_count != 0) {
		  //			addHistogram(&bytes_per_message, 
		  //				     message_byte_count, 1);
	//			addHistogram(&data_bytes_per_message, 
		  //				     message_data_byte_count, 1);
		  //			addHistogram(&data_packets_per_message,
		  //				     message_data_packet_count, 1);
		  //			addHistogram(&control_packets_per_message,
		  //				     message_control_packet_count, 1);
		  //			addHistogram(&total_packets_per_message,
		  //				     message_total_packet_count, 1);
		  //		      }

		  //		      message_byte_count = 0;
		  //		      message_data_byte_count = 0;
		  //		      message_data_packet_count = 0;
		  //		      message_control_packet_count = 0;
		  //		      message_total_packet_count = 0;
		  //		      current_msg_id = *msg_id;
		  //		    }
		  //		  }
		}
	
		
	}
#endif // TOTAL_BYTES_TRACE

#ifdef PACIA_TRACE   
if (print != 0)
{
	char buf_buf[count + 2];
	size_t i;
	int num_x = 0, num_z = 0;

	sprintf (msg_buf, "%s: called on port id %u", fname, port->id);
	print_string (msg_buf);

	for (i = 0; i < count; i++)
	{
		if (buf[i] == 'X')
		{
			num_x++;
		}
		if (buf[i] == 'Z')
		{
			num_z++;
		}
	}
	sprintf (buf_buf, "%u Xs, %u Zs", num_x, num_z);
	print_string (buf_buf);
}
#endif // PACIA_TRACE   
   
// XXX  736 stuff
#ifdef PACIA_TRACE   
	if (print != 0)
	{
		// print the skb
		print_skb (skb);
	}
#endif // PACIA_TRACE   

   VNetSend(&userIf->port.jack, skb);

#ifdef PACIA_TRACE   
	if (print != 0)
	{
		unsigned int foo_us;
		do_gettimeofday(&now_tv);
	  	foo_us = abs (1000000*(now_tv.tv_sec  - tv.tv_sec));
		foo_us += abs (now_tv.tv_usec - tv.tv_usec);
		sprintf (msg_buf, "%s: call took %u us", fname, foo_us);
		print_string (msg_buf);
	}
#endif

   return count;
}


#ifdef KERNEL_2_1
/*
 *----------------------------------------------------------------------
 *
 * VNetUserIfPoll --
 *
 *      The virtual network's file poll operation.
 *
 * Results: 
 *      Return POLLIN if success, else sleep and return 0.
 *      FIXME: Should not we always return POLLOUT?
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetUserIfPoll(VNetPort     *port,
               struct file  *filp,
	       poll_table   *wait)
{
   VNetUserIF *userIf = (VNetUserIF*)port->jack.private;
   
#ifdef KERNEL_2_3_43
   poll_wait(filp, &userIf->waitQueue, wait);
   if (!skb_queue_empty(&userIf->packetQueue)) {
      return POLLIN;
   }
#else
   start_bh_atomic();
   if (!skb_queue_empty(&userIf->packetQueue)) {
      end_bh_atomic();
      return 1;
   }
   poll_wait(filp, &userIf->waitQueue, wait);
   end_bh_atomic();
#endif
   return 0;
}

#else

/*
 *----------------------------------------------------------------------
 *
 * VNetUserIfSelect --
 *
 *      The virtual network's file select operation.
 *
 * Results: 
 *      Return 1 if success, else sleep and return 0.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static int
VNetUserIfSelect(VNetPort      *port,
                 struct file   *filp,
                 int            sel_type,
                 select_table  *wait)
{
   VNetUserIF *userIf = (VNetUserIF*)port->jack.private;
   
#ifdef PACIA_TRACE   
{
	char msg_buf[256];
	char * fname = "VNetUserIfSelect";
	sprintf (msg_buf, "%s: called\n", fname);
	print_string (msg_buf);
}
#endif // PACIA_TRACE   

   switch (sel_type) {
   case SEL_IN:
      start_bh_atomic();
      if (!skb_queue_empty(&userIf->packetQueue)) {
	 end_bh_atomic();
	 return 1;
      }
      select_wait(&userIf->waitQueue, wait);
      end_bh_atomic();
      break;
   case SEL_EX:
      // XXX should it be 0 or 1?
      break;
   case SEL_OUT:
      return 1;
   }
   return 0;
}

#endif

/*
 *----------------------------------------------------------------------
 *
 * VNetFileOpIoctl --
 *
 *      The virtual network's ioctl file operation. This is used for
 *      setup of the connection. Currently supported commands are
 *      (taken from sockios.h):
 *
 *      SIOCGIFADDR - get ethernet address          - ioarg OUT: 6 bytes
 *      SIOCSIFADDR - set ethernet address          - ioarg IN:  6 bytes
 *      SIOCGIFFLAGS - get flags                    - ioarg OUT  4 bytes
 *      SIOCSIFFLAGS - set flags                    - ioarg IN:  4 bytes
 *      SIOCGIFBR - get bridging status             - ioarg OUT: 4 bytes
 *      SIOCSIFBR - set bridge peer interface       - ioarg IN:  8 bytes
 *
 *      Private ioctl calls, taken from device-private ioctl space 
 *      in sockios.h, and defined in includes/vm_oui.h:
 *
 *      SIOCSKEEP (0x89F0) - set dealloc flag       - ioarg IN:  1 byte
 *      SIOCGKEEP (0x89F1) - get dealloc flag       - ioarg OUT: 1 byte
 *         By default all hubs (and their associated interfaces) are 
 *         deallocated from memory when their last open port is closed.  
 *         Setting this true "keeps" the hub and thus we will not 
 *         deallocate it until the module is unloaded.
 *
 *      SIOCSLADRF (0x89F2) - set logical address filter (for 
 *         filtering multicast packets)             - ioarg IN:  8 bytes
 *
 *
 *      Supported flags are (taken from if.h):
 *
 *      IFF_UP - ready to receive packets             - OFF by default
 *      IFF_BROADCAST - receive broadcast packets     - OFF by default
 *      IFF_DEBUG - turn on debugging                 - OFF by default
 *      IFF_PROMISC - promiscuous mode                - OFF by default
 *      IFF_MULTICAST - receive multicast packets     - OFF by default
 *      IFF_ALLMULTI - receive all multicast packets 
 *            (like IFF_PROMISC but with multicast)   - OFF by default
 *
 * Results: 
 *      On success 0 else errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetUserIfIoctl(VNetPort       *port,
                struct file    *filp,
                unsigned int    iocmd,
                unsigned long   ioarg)
{
   // VNetUserIF *userIf = (VNetUserIF*)port->jack.private;

   switch (iocmd) {
   default:
      return -ENOIOCTLCMD;
      break;
   }
   
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetUserIfProcRead --
 *
 *      Callback for read operation on this userif entry in vnets proc fs.
 *
 * Results: 
 *      Length of read operation.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
#ifdef KERNEL_2_1
int
VNetUserIfProcRead(char    *page,   // IN/OUT: buffer to write into
                   char   **start,  // OUT: 0 if file < 4k, else offset into page
                   off_t    off,    // IN: offset of read into the file
                   int      count,  // IN: maximum number of bytes to read
                   int     *eof,    // OUT: TRUE if there is nothing more to read
                   void    *data)   // IN: client data - not used
{
   VNetUserIF *userIf = (VNetUserIF*)data; 
   int len = 0;
   
   if (!userIf) {
      return len;
   }
   
   len += VNetPrintPort(&userIf->port, page+len);
   
   len += sprintf(page+len, "read %u written %u queued %u ",
                  userIf->stats.read,
                  userIf->stats.written,
                  userIf->stats.queued);
   
   len += sprintf(page+len, "dropped.down %u dropped.mismatch %u dropped.overflow %u ",
                  userIf->stats.droppedDown,
                  userIf->stats.droppedMismatch,
                  userIf->stats.droppedOverflow);

   len += sprintf(page+len, "\n");
   
   *start = 0;
   *eof   = 1;
   return len;
}
#endif
