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


#define __NO_VERSION__   // keep linker happy

#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/init.h>

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

#include <linux/skbuff.h>
#include <linux/atm.h>

#include "probe.h"


/************************************************************************/


/* Since we use the file operations struct, we can't use the special proc
 * output provisions - we have to use a standard read function, which is
 * this function */
static ssize_t proc_output(  struct file *file,   /* The file read */ 
                         char *buf, /* The buffer to put data to (in the
                                     * user segment) */
                         size_t len,
			 loff_t *offset)  /* The length of the buffer */
{
  static int finished = 0;
  int i;
  char message[512], temp[128];
  unsigned long tous_tot=0;
  unsigned long frus_tot=0;
  int tot1 = 0, tot2 = 0, tot3 = 0, tot4 = 0, tot5 = 0;

//printk("NPROBE: proc_output: fin %d len %d\n",finished,len);

  /* We return 0 to indicate end of file, that we have no more information.
   * Otherwise, processes will continue to read from us in an endless loop. */
  if (finished) {
    finished = 0;
    return 0;
  }

  /* We use put_user to copy the string from the kernel's memory segment
   * to the memory segment of the process that called us. get_user, BTW, is
   * used for the reverse. */
  sprintf(message, "nprobe: 0x%p %d %d %ld %d\n", 
	  np, nprobe_maxdev, FIFO_SIZE, np->num_bufs,
	  __this_module.uc.usecount.counter);

  for(i=0;i<US_CHANNELS;i++)
    {
      int tous_delta = np->x[i].tous_in - np->x[i].tous_out;
      int frus_delta = np->x[i].frus_in - np->x[i].frus_out;
      sprintf(temp,"%d : tous %d,%d : frus %d,%d : (%d %d)\n",
	      i,
	      np->x[i].tous_in, np->x[i].tous_out,
	      np->x[i].frus_in, np->x[i].frus_out,
	      tous_delta, frus_delta
	      );
      strcat(message, temp);
      
      tous_tot+=tous_delta;
      frus_tot+=frus_delta;
    }

  sprintf(temp,"X : tous_tot %d, frus_tot %d,  tot %d\n",
	  tous_tot, frus_tot, tous_tot + frus_tot
      );
  strcat(message, temp);

  sprintf(temp,"X : %d skb_alloc's failed. Low water mark since last fail is %d\n",
	  np->alloc_failed_cnt, np->alloc_low_water);

  strcat(message, temp);

#ifdef MARK_BUFFERS
  for ( i=0; i<np->num_bufs; i++ )
    {
	unsigned long foo = *((unsigned long*)((np->bufs[i]->head)+4));
	if( foo == 0x87654321 ) tot1++;
	if( foo == 0x80605040 ) tot2++;
	if( foo == 0xAA000001 ) tot3++;
	if( foo == 0xAA000002 ) tot4++;
	if( foo == 0x55000001 ) tot5++;
    }
  
  sprintf(temp,"X : found %d+%d = %d of %d buffers with OnCardFreeList magic\n",
	  tot1, tot2, tot1+tot2, i );
  strcat(message, temp);

  sprintf(temp,"X : found %d+%d = %d of %d buffers with RX magic (in user space)\n",
	  tot3, tot4, tot3+tot4, i );
  strcat(message, temp);

  sprintf(temp,"X : found %d buffers returned from user space\n",
	  tot5, i );
  strcat(message, temp);
#endif

  for(i=0; i<len && message[i]; i++) 
    put_user(message[i], buf+i);


//printk("NPROBE: proc_output: = X%sX\n",message);

  /* Notice, we assume here that the size of the message is below len, or
   * it will be received cut. In a real life situation, if the size of the
   * message is less than len then we'd return len and on the second call 
   * start filling the buffer with the len+1'th byte of the message. */
  finished = 1; 

  return i;  /* Return the number of bytes "read" */
}


/* This function receives input from the user when the user writes to
 * the /proc file. */
static ssize_t proc_input(  struct file *file,   /* The file itself */
                        const char *buf,     /* The buffer with the input */
                        size_t length,
			loff_t *offset)          /* The buffer's length */
{

#if 0
  int i;

  /* Put the input into Message, where module_output will later be 
   * able to use it */
  for(i=0; i<MESSAGE_LENGTH-1 && i<length; i++)
    Message[i] = get_user(buf+i);
  Message[i] = '\0';  /* we want a standard, zero terminated string */
  
  /* We need to return the number of input characters used */
  return i;
#else

  // writing to proc file causes a FIFO reset

  printk("NPROBE: proc input called, length %d, string XXX%sXXX\n",
	 length, buf);

  //  reset_fifo();  NOT USED ANYMORE??

  np->alloc_failed_cnt = 0;
  np->alloc_low_water = FIFO_SIZE;

  return length; // infinite sink
#endif

}


/* This function decides whether to allow an operation (return zero) or
 * not allow it (return a non-zero which indicates why it is not allowed).
 *
 * The operation can be one of the following values:
 * 0 - Execute (run the "file" - meaningless in our case)
 * 2 - Write (input to the kernel module)
 * 4 - Read (output from the kernel module)
 *
 * This is the real function that checks file permissions. The permissions
 * returned by ls -l are for referece only, and can be overridden here. 
 */
static int proc_perms(struct inode *inode, int op)
{
//printk("NPROBE: proc_permission\n");
  /* We allow everybody to read from our module, but only root (uid 0) 
   * may write to it */ 
  //  if (op == 4 || (op == 2 && current->euid == 0))
  if (op == 4 || op == 2)
    return 0; 

  /* If it's anything else, access is denied */
  return -EACCES;
}


/* The file is opened - we don't really care about that, but it does mean
 * we need to increment the module's reference count. */
static int proc_open(struct inode *inode, struct file *file)
{

  MOD_INC_USE_COUNT;

//  printk("NPROBE: proc_open : now open %d times\n", __this_module.uc.usecount.counter );

#if 0 /* paranoia to detect copy-on-write behaviour */
  if( __this_module.uc.usecount.counter >1 )
    {
      printk("NPROBE: stage2 magic found %x %x\n",np->magic,np->end_magic);

      if( np->magic != NP_MAGIC2 ||  np->end_magic != NP_MAGIC2 )
	{
	  printk("NPROBE: stage2 magic was incorrect.\n");
	}

      np->magic     = NP_MAGIC3;
      np->end_magic = NP_MAGIC3;
    
    }

#endif

   if( __this_module.uc.usecount.counter == 1 )
     {
       // reset magics to stage1
       np->end_magic = np->magic = NP_MAGIC;
     }
 
  return 0;
}


/* The file is closed - again, interesting only because of the reference
 * count. */
static int proc_close(struct inode *inode, struct file *file)
{
//  printk("NPROBE: proc_close. Was open %d times\n",
//	 __this_module.uc.usecount.counter);

  // the following works around a horible situation where after the
  // us-client forks it tries to shut a single fd twice.
  if( __this_module.uc.usecount.counter > 0 )
    MOD_DEC_USE_COUNT;

  return 0;
}



/* File operations for our proc file. This is where we place pointers
 * to all the functions called when somebody tries to do something to
 * our file. NULL means we don't want to deal with something. */
static struct file_operations File_Ops_4_Our_Proc_File =
  {
    NULL,  /* lseek */
    proc_output,  /* "read" from the file */
    proc_input,   /* "write" to the file */
    NULL,  /* readdir */
    NULL,  /* select */
    NULL,  /* ioctl */
    NULL,  /* mmap */
    proc_open,    /* Somebody opened the file */
    NULL,
    proc_close    /* Somebody closed the file */
    /* etc. etc. etc. (they are all given in /usr/include/linux/fs.h).
     * Since we don't put anything here, the system will keep the default
     * data, which in Unix is zeros (NULLs when taken as pointers). */
};



/* Inode operations for our proc file. We need it so we'll have some
 * place to specify the file operations structure we want to use, and
 * the function we use for permissions. It's also possible to specify
 * functions to be called for anything else which could be done to an
 * inode (although we don't bother, we just put NULL). */
static struct inode_operations Inode_Ops_4_Our_Proc_File =
  {
    &File_Ops_4_Our_Proc_File,
    NULL, /* create */
    NULL, /* lookup */
    NULL, /* link */
    NULL, /* unlink */
    NULL, /* symlink */
    NULL, /* mkdir */
    NULL, /* rmdir */
    NULL, /* mknod */
    NULL, /* rename */
    NULL, /* readlink */
    NULL, /* follow_link */
    NULL, /* readpage */
    NULL, /* writepage */
    NULL, /* bmap */
    NULL, /* truncate */
    proc_perms /* check for permissions */
  };

#define PROC_NAME "nprobe"

/* Directory entry */
struct proc_dir_entry Our_Proc_File = 
  {
    0, /* Inode number - ignore, it will be filled by 
        * proc_register_dynamic */
    sizeof(PROC_NAME)-1, /* Length of the file name */
    PROC_NAME, /* The file name */
    S_IFREG | S_IRUGO | S_IWUSR, /* File mode - this is a regular file which 
                        * can be read by its owner, its group, and everybody
                        * else. Also, its owner can write to it.
                        *
                        * Actually, this field is just for reference, it's
                        * module_permission that does the actual check. It 
                        * could use this field, but in our implementation it
                        * doesn't, for simplicity. */
    1,  /* Number of links (directories where the file is referenced) */
    0, 0,  /* The uid and gid for the file - we give it to root */
    0, /* The size of the file reported by ls. */
    &Inode_Ops_4_Our_Proc_File, /* A pointer to the inode structure for
                                 * the file, if we need it. In our case we
                                 * do, because we need a write function. */
    NULL  /* The read function for the file. Irrelevant, because we put it
           * in the inode structure above */
  }; 


/************************************************************************/



















