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

/* So we can call print_skb and getTcpPorts. */
#include "utils.h"

typedef struct VNetNetIF {
   VNetPort                port;
   struct device           dev;
   char                    devName[8];
   struct enet_statistics  stats;
} VNetNetIF;


static void VNetNetIfFree(VNetJack *this);
static void VNetNetIfReceive(VNetJack *this, struct sk_buff *skb);
static Bool VNetNetIfCycleDetect(VNetJack *this, int generation);

static int  VNetNetifOpen(struct device *dev);
static int  VNetNetifProbe(struct device *dev);
static int  VNetNetifClose(struct device *dev);
static int  VNetNetifStartXmit(struct sk_buff *skb, struct device *dev);
static struct enet_statistics *VNetNetifGetStats(struct device *dev);
static int  VNetNetifSetMAC(struct device *dev, void *addr);
static void VNetNetifSetMulticast(struct device *dev);
#ifdef KERNEL_2_3_43
#if 0
static void VNetNetifTxTimeout(struct device *dev);
#endif
#endif

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

#ifndef KERNEL_2_3_43
/* softnet API emulation */

/*
 *----------------------------------------------------------------------
 *
 * netif_stop_queue --
 *
 *      Stops queue processing.
 * 
 * Results: 
 *      None.
 *
 *----------------------------------------------------------------------
 */
static inline void netif_stop_queue(struct device *dev)
{
   dev->tbusy = 1;
}

/*
 *----------------------------------------------------------------------
 *
 * netif_start_queue --
 *
 *      Enables queue processing. It does not try to start received
 *      frames processing.
 * 
 * Results: 
 *      None.
 *
 *----------------------------------------------------------------------
 */
static inline void netif_start_queue(struct device *dev)
{
   dev->tbusy = 0;
}

/*
 *----------------------------------------------------------------------
 *
 * netif_wake_queue --
 *
 *      Enables queue processing. It schedules receive queue processing.
 * 
 * Results: 
 *      None.
 *
 *----------------------------------------------------------------------
 */
static inline void netif_wake_queue(struct device *dev)
{
   dev->tbusy = 0;
   mark_bh(NET_BH);
}

#endif

#ifdef KERNEL_2_3_43
/*
 *----------------------------------------------------------------------
 *
 * VNetNetIfTxTimeout --
 *
 *      Enables processing of Tx queue after it was stopped for so long.
 *      It should not happen with vmnet system.
 * 
 * Results: 
 *      None.
 *
 * Side effects:
 *      Tx queue enabled, message in log.
 *
 *----------------------------------------------------------------------
 */

#if 0
static void VNetNetifTxTimeout(struct device *dev)
{
   static int netRateLimit = 0;
   
   if (netRateLimit < 10) {
      LOG(0, (KERN_NOTICE "%s: Transmit timeout\n", dev->name));
      netRateLimit++;
   }
   /* We cannot stuck due to hardware, so always wake up processing */
   netif_wake_queue(dev);
}
#endif
#endif

/*
 *----------------------------------------------------------------------
 *
 * VNetNetIf_Create --
 *
 *      Create a net 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
VNetNetIf_Create(char      *devName,
                 VNetPort **ret)
{
   VNetNetIF *netIf = NULL;
   struct device *dev = NULL;
   int retval = 0;
   static unsigned id = 0;
   
   netIf = (struct VNetNetIF*)kmalloc(sizeof(struct VNetNetIF), GFP_KERNEL);
   if (!netIf) {
      retval = -ENOMEM;
      goto out;
   }

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

   netIf->port.jack.peer = NULL;
   netIf->port.jack.numPorts = 1;
   sprintf(netIf->port.jack.name, "netif%d", netIf->port.id);
   netIf->port.jack.private = netIf;
   netIf->port.jack.index = 0;
   netIf->port.jack.procEntry = NULL;
   netIf->port.jack.free = VNetNetIfFree;
   netIf->port.jack.rcv = VNetNetIfReceive;
   netIf->port.jack.cycleDetect = VNetNetIfCycleDetect;
   netIf->port.jack.portsChanged = NULL;
   netIf->port.jack.isBridged = NULL;
   
   /*
    * Make proc entry for this jack.
    */
   
   retval = VNetProc_MakeEntry(NULL, netIf->port.jack.name, S_IFREG,
                               &netIf->port.jack.procEntry);
   if (retval) {
      if (retval == -ENXIO) {
         netIf->port.jack.procEntry = NULL;
      } else {
         netIf->port.jack.procEntry = NULL;
         goto out;
      }
   } else {
#ifdef KERNEL_2_1
      netIf->port.jack.procEntry->read_proc = VNetNetIfProcRead;
      netIf->port.jack.procEntry->data = netIf;
#else
      netIf->port.jack.procEntry = NULL;
#endif
   }

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

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

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

   netIf->port.fileOpRead = NULL;
   netIf->port.fileOpWrite = NULL;
   netIf->port.fileOpIoctl = NULL;
#ifdef KERNEL_2_1
   netIf->port.fileOpPoll = NULL;
#else
   netIf->port.fileOpSelect = NULL;
#endif
   
   dev = &netIf->dev;
   
   memset(dev, 0, sizeof(struct device));
   dev->priv = netIf;
   dev->init = VNetNetifProbe;
   
   memcpy(netIf->devName, devName, sizeof netIf->devName);
#ifdef KERNEL_2_3_99
   strncpy(dev->name, netIf->devName, IFNAMSIZ);
#else
   dev->name = netIf->devName;
#endif

   memset(&netIf->stats, 0, sizeof(struct enet_statistics));
   
   ether_setup(dev); // turns on IFF_BROADCAST, IFF_MULTICAST
   if (register_netdev(dev) != 0) {
      LOG(0, (KERN_NOTICE "%s: could not register network device\n", devName));
      retval = -ENODEV;
      goto out;
   }
   
   /*
    * The initial address assignment for the netIf and ports
    * is not unique across machines.
    *
    * The last two bytes of the address can be modified later
    * to something more likely to be unique.
    */
   
   dev->dev_addr[0] = VMX86_OUI0;
   dev->dev_addr[1] = VMX86_OUI1;
   dev->dev_addr[2] = VMX86_OUI2;
   dev->dev_addr[3] = 1; // xxx
   
   dev->open = &VNetNetifOpen;
   dev->hard_start_xmit = &VNetNetifStartXmit;
   dev->stop = &VNetNetifClose;
   dev->get_stats = &VNetNetifGetStats;
   dev->set_mac_address = &VNetNetifSetMAC;
   dev->set_multicast_list = &VNetNetifSetMulticast;
#ifdef KERNEL_2_3_43
   /*
    * We cannot stuck... If someone will report problems under
    * low memory conditions or some such, we should enable it.
    */
#if 0
   dev->tx_timeout = &VNetNetifTxTimeout;
   dev->watchdog_timeo = TX_TIMEOUT;
#endif
#endif   
   
   *ret = (VNetPort*)netIf;
   return 0;

 out:
   if (netIf->port.jack.procEntry) {
      VNetProc_RemoveEntry(netIf->port.jack.procEntry, NULL);
   }
   if (netIf) {
      kfree(netIf);
   }
   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetNetIfFree --
 *
 *      Free the net interface port.
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
VNetNetIfFree(VNetJack *this)
{
   VNetNetIF *netIf = (VNetNetIF*)this;
   unregister_netdev(&netIf->dev);
   if (this->procEntry) {
      VNetProc_RemoveEntry(this->procEntry, NULL);
   }
   kfree(netIf);
}


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

void
VNetNetIfReceive(VNetJack        *this,
                  struct sk_buff  *skb)
{
   VNetNetIF *netIf = (VNetNetIF*)this->private;
   uint8 *dest = SKB_2_DESTMAC(skb);
   
// XXX 736 stuff
#ifdef PACIA_TRACE   
	char * fname = "VNetNetIfReceive";
	char msg_buf[256];
	struct timeval tv;
	struct timeval now_tv;
	int print = 0; // whether or not to print information
	do_gettimeofday(&tv);

	sprintf (msg_buf, "%s called", fname);
	print_string (msg_buf);
#endif

   if (!NETDEV_UP_AND_RUNNING(&netIf->dev)) {
      goto drop_packet;
   }

   if (!VNetPacketMatch(dest,
                        netIf->dev.dev_addr,
                        (uint8 *)AllMultiFilter, 
                        netIf->dev.flags)) {
      goto drop_packet;
   }
   
   /* send to the host interface */
   skb->dev = &netIf->dev;
   skb->protocol = eth_type_trans(skb, &netIf->dev);
   netif_rx(skb);
   netIf->stats.rx_packets++;

// XXX 736 stuff
#ifdef PACIA_TRACE   
	{
		__u16 src = 0, dst = 0;
		int retval;
	
		/* Set print to 1 iff this is a packet from the server to
		 * the client. */
		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;
		}
		if (print != 0)
		{
			sprintf (msg_buf, "%s skb:", fname);
			print_string (msg_buf);

			// print the skb
			print_skb (skb);
		}
	}
#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);
	}
#endif

   return;
   
 drop_packet:
   DEV_KFREE_SKB(skb, FREE_WRITE);
}


/*
 *----------------------------------------------------------------------
 *
 * VNetNetIfCycleDetect --
 *
 *      Cycle detection algorithm.
 * 
 * Results: 
 *      TRUE if a cycle was detected, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
VNetNetIfCycleDetect(VNetJack *this,
                     int       generation)
{
   VNetNetIF *netIf = (VNetNetIF*)this->private;
   return VNetCycleDetectIf(netIf->devName, generation);
}


/*
 *----------------------------------------------------------------------
 *
 * VNetNetifOpen --
 *
 *      The virtual network's open dev operation. 
 *
 * Results: 
 *      errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetNetifOpen(struct device *dev)
{
   /*
    * The host interface is not available if the hub is bridged.
    *
    * It's actually okay to support both.  We just need
    * to tag packets when VNetXmitPacket gives them to the interface
    * so they can be dropped by VNetBridgeReceive().
    *
    *  if so return -EBUSY;
    */

   netif_start_queue(dev);
#ifndef KERNEL_2_3_43
   /* Softnet does not have interrupt hack ... */
   dev->interrupt = 0;
   /* ... and sets start for us */
   dev->start = 1;
#endif
   // xxx need to change flags
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetNetifProbe --
 *
 *      ???
 *
 * Results: 
 *      0.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetNetifProbe(struct device *dev)
{
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetNetifClose --
 *
 *      The virtual network's close dev operation. 
 *
 * Results: 
 *      errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetNetifClose(struct device *dev)
{
#ifndef KERNEL_2_3_43
   /* Softnet generic layer clears it for us */
   dev->start = 0;
#endif
   netif_stop_queue(dev);
   // xxx need to change flags
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetNetifStartXmit --
 *
 *      The virtual network's start xmit dev operation. 
 *
 * Results: 
 *      ???, 0.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetNetifStartXmit(struct sk_buff  *skb,
                   struct device   *dev)
{
   VNetNetIF *netIf = (VNetNetIF*)dev->priv;

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

	sprintf (msg_buf, "%s called", fname);
	print_string (msg_buf);
#endif

   if(skb == NULL) {
#ifndef KERNEL_2_1
      dev_tint(dev);
#endif
      return 0;
   }

   /* Block a timer-based transmit from overlapping.  This could better be
    * done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
    * If this ever occurs the queue layer is doing something evil!
    */
#ifndef KERNEL_2_3_43
   /* Softnet does not play with tbusy... */
#ifdef KERNEL_2_1
   if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
#else
   if (set_bit(0, (void*)&dev->tbusy) != 0)
#endif
   {
      LOG(0, (KERN_NOTICE "%s: transmitter access conflict.\n", dev->name));
      return 1;
   }
#endif

// XXX 736 stuff
/* We have to use the sk_buff before calling VNetSend, because
 * VNetSend might free the buffer. */
#ifdef PACIA_TRACE   
	{
		__u16 src = 0, dst = 0;
		int retval;
	
		/* Set print to 1 iff this is a packet from the server to
		 * the client. */
		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;
		}
		if (print != 0)
		{
			sprintf (msg_buf, "%s skb:", fname);
			print_string (msg_buf);

			// print the skb
			print_skb (skb);
		}
	}
#endif // PACIA_TRACE   

   VNetSend(&netIf->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

   netIf->stats.tx_packets++;
#ifndef KERNEL_2_3_43
   dev->tbusy = 0;
#endif
   dev->trans_start = jiffies;

   return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * VNetNetifSetMAC --
 *
 *      Sets MAC address (i.e. via ifconfig) of netif device.
 *
 * Results: 
 *      Errno.
 *
 * Side effects:
 *      The MAC address may be changed.
 *
 *----------------------------------------------------------------------
 */
int
VNetNetifSetMAC(struct device *dev, void *p)
{
   struct sockaddr *addr = p;
   memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
   return 0; // XXX should check for 00:50:56:XX:XX:XX?
}

/*
 *----------------------------------------------------------------------
 *
 * VNetNetifSetMulticast --
 *
 *      Sets or clears the multicast address list.  This information
 *      comes from an array in dev->mc_list, and with a counter in
 *      dev->mc_count.
 *
 *      Since host-only network ifaces can't be bridged, it's debatable
 *      whether this is at all useful, but at least now you can turn it 
 *      on from ifconfig without getting an ioctl error.
 * Results: 
 *      Void.
 *
 * Side effects:
 *      Multicast address list might get changed.
 *
 *----------------------------------------------------------------------
 */
void
VNetNetifSetMulticast(struct device *dev)
{

}

/*
 *----------------------------------------------------------------------
 *
 * VNetNetifGetStats --
 *
 *      The virtual network's get stats dev operation. 
 *
 * Results: 
 *      A struct full of stats.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

struct enet_statistics *
VNetNetifGetStats(struct device *dev)
{
   VNetNetIF *netIf = (VNetNetIF*)dev->priv;
   return &(netIf->stats);
}


/*
 *----------------------------------------------------------------------
 *
 * VNetNetIfProcRead --
 *
 *      Callback for read operation on this netif entry in vnets proc fs.
 *
 * Results: 
 *      Length of read operation.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
#ifdef KERNEL_2_1
int
VNetNetIfProcRead(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
{
   VNetNetIF *netIf = (VNetNetIF*)data; 
   int len = 0;
   
   if (!netIf) {
      return len;
   }
   
   len += VNetPrintPort(&netIf->port, page+len);

   len += sprintf(page+len, "dev %s ", netIf->devName);

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