/******************************************************************************
*                                                                             *
*   Copyright 2005 University of Cambridge Computer Laboratory.               *
*                                                                             *
*   This file is part of Nprobe.                                              *
*                                                                             *
*   Nprobe is free software; you can redistribute it and/or modify            *
*   it under the terms of the GNU General Public License as published by      *
*   the Free Software Foundation; either version 2 of the License, or         *
*   (at your option) any later version.                                       *
*                                                                             *
*   Nprobe is distributed in the hope that it will be useful,                 *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of            *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
*   GNU General Public License for more details.                              *
*                                                                             *
*   You should have received a copy of the GNU General Public License         *
*   along with Nprobe; if not, write to the Free Software                     *
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
*                                                                             *
******************************************************************************/


#include <linux/config.h>
#include <linux/module.h>

#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
#include <linux/if_arp.h>
#include <linux/if_slip.h>
#include <linux/init.h>

#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <asm/page.h>
#include "probe.h"

#define NETPROBE_VERSION    "0.1-iap10"
static void reset_fifo( void );

//char *orig_np = NULL;
np_t * np = NULL;
int nprobe_maxdev = 1;		/* Can be overridden with insmod! */


MODULE_PARM(nprobe_maxdev, "i");
MODULE_LICENSE("GPL");

#ifdef MODULE
static int nprobe_init(void)
#else	/* !MODULE */
__initfunc(int nprobe_init(struct device *dummy))
#endif	/* !MODULE */
{

    int status = 0;
    //    int size;
    int i,j;

    printk("NPROBE: version %s, max=%d, loaded at %p\n",
	   NETPROBE_VERSION, nprobe_maxdev, &nprobe_init );

    /* get us a /proc entry */
    if( nprobe_init_proc() < 0 )
      return -ENODEV;

    np = (np_t*) __get_free_pages(GFP_KERNEL, get_order(sizeof(np_t)));
    

    printk("NPROBE: allocated structure size %d order %d, at %p\n",
	   sizeof(np_t),get_order(sizeof(np_t)),np );

    memset( np, 0, sizeof( np_t ) );

    np->end_magic = np->magic = NP_MAGIC;

    np->alloc_low_water = FIFO_SIZE;

    for(i=0;i<US_CHANNELS;i++)
      {
	spin_lock_init(&(np->x[i].lock));
      }

    // paranoia code
    for ( i=0; i<FIFO_SIZE; i++ )
      {
	for(j=0;j<US_CHANNELS;j++)
	  {
	  np->frus_skb[j][i] = (struct sk_buff *) 0x05555555;
	  np->tous_skb[j][i] = (struct sk_buff *) 0x0aaaaaaa;
	  }
      }

    
    for ( i=0; i<FIFO_SIZE; i++ )
    {
      struct sk_buff * skb;
      
      skb = probe_alloc_skb();

      if ( skb == NULL )
	{
	  printk("NPROBE: Only able to allocate %d buffers.\n",i );
	  break;
	}	
      np->bufs[i] = skb;

#ifdef MARK_BUFFERS
      *((unsigned long*)((skb->head)+4)) = 0xdeadbeef;
#endif

    }

    np->num_bufs = i;
    printk("NPROBE: Got %d buffers.\n",i );

    reset_fifo(); // setup pointers etc.


#ifdef MODULE
    return status;
#else
    /* Return "not found", so that dev_init() will unlink
     * the placeholder device entry for us.
     */
    return ENODEV;
#endif
      }


#ifdef MODULE

int
init_module(void)
{
	if (sizeof(struct sk_buff) != 172)
		printk("<0>Size of sk_buff changed, expect breakage.\n");
	return nprobe_init();
}

void
cleanup_module(void)
{
  int i;

  printk("NPROBE: Unload called.\n");

  for ( i=0; i<np->num_bufs; i++ )
    {
	printk("NPROBE: freed buf [%d] %p %p\n",
	       i,np->bufs[i],np->bufs[i]->head); 
      dev_kfree_skb( np->bufs[i] );
    }

  printk("NPROBE: All buffers freed.\n");

  nprobe_cleanup_proc();

  printk("NPROBE: Proc unregistered.\n");

  //  kfree(orig_np);
  free_pages( (unsigned long)np, get_order(sizeof(np_t)));

  printk("NPROBE: cleanup complete.\n");
}
#endif /* MODULE */




static void reset_fifo( void )
{
  int i,j;
  int c[US_CHANNELS];

  // this is very scary -- should have a proper mutex around it !!!!!
  // SOS22: We're only ever called from module initialisation, so it isn't
  // all that bad.

  printk("NPROBE: reset_fifo with %d bufs.\n",np->num_bufs);
  
  local_irq_disable();

  for(i=0;i<US_CHANNELS;i++)
    c[i] = 0;

  for(i=0, j=0; i<np->num_bufs; i++, j=(j+1) % US_CHANNELS )
    {
      int x;
      x = c[j]++;
      np->frus_skb[j][x] = np->bufs[i];      
    }
  
  for(i=0;i<US_CHANNELS;i++)
    {
      np->x[i].frus_in = c[i];
      np->x[i].tous_in = np->x[i].tous_out = np->x[i].frus_out = 0;
    }
  
  local_irq_enable();

  for(i=0;i<US_CHANNELS;i++)
    {
      printk("NPROBE: [%d] reset_fifo tous %d,%d : frus %d,%d\n",
	     i,
	     np->x[i].tous_in, np->x[i].tous_out,
	     np->x[i].frus_in, np->x[i].frus_out );
    }


}
