/************************************************************/
/*                                                          */
/* Module ID  - monmain.c                                   */
/*                                                          */
/* Function   - Interface to the CP APPLMON system service. */
/*                                                          */
/* Called By  - N/A.                                        */
/*                                                          */
/* Calling To - N/A.                                        */
/*                                                          */
/* Parameters - See individual entry points.                */
/*                                                          */
/* Notes      - (1) ....................................... */
/*                                                          */
/*              (2) ....................................... */
/*                                                          */
/*                                                          */
/* Name       - Neale Ferguson.                             */
/*                                                          */
/* Date       - January, 2000.                              */
/*                                                          */
/*                                                          */
/* Associated    - (1) Refer To ........................... */
/* Documentation                                            */
/*                 (2) Refer To ........................... */
/*                                                          */
/************************************************************/
 
/************************************************************/
/*                                                          */
/*                     DEFINES                              */
/*                     -------                              */
/*                                                          */
/************************************************************/
 
#ifndef __KERNEL__
#  define __KERNEL__
#endif
#ifndef MODULE
#  define MODULE
#endif
 
#define __NO_VERSION__ /* don't define kernel_verion in module.h */
 
/*=============== End of Defines ===========================*/
 
/************************************************************/
/*                                                          */
/*              INCLUDE STATEMENTS                          */
/*              ------------------                          */
/*                                                          */
/************************************************************/
 
#include <linux/module.h>
#include <linux/version.h>
 
#include <linux/kernel.h>	/* printk() */
#include <linux/slab.h>		/* kmalloc() */
#include <linux/fs.h>     	/* everything... */
#include <linux/errno.h>  	/* error codes */
#include <linux/types.h>  	/* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h>        /* O_ACCMODE */
 
#include <asm/system.h>   	/* cli(), *_flags */
#include <asm/segment.h>  	/* memcpy and such */
#include <asm/ebcdic.h>   	/* ebcdic stuff */
#include <asm/uaccess.h>  	/* copy to/from user space */
 
#include "cpint.h"        	/* local definitions */
#include "applmon.h"      	/* specific definitions */
 
/*================== End of Include Statements =============*/
 
/************************************************************/
/*                                                          */
/*              TYPE DEFINITIONS                            */
/*              ----------------                            */
/*                                                          */
/************************************************************/
 
/*================== End of Type Definitions ===============*/
 
/************************************************************/
/*                                                          */
/*             FUNCTION PROTOTYPES                          */
/*             -------------------                          */
/*                                                          */
/************************************************************/
 
static int inline applMonGen(unsigned char, ApplMon_Dev *, 
			     char *, short);
 
/*================== End of Prototypes =====================*/
 
/************************************************************/
/*                                                          */
/*           GLOBAL VARIABLE DECLARATIONS                   */
/*           ----------------------------                   */
/*                                                          */
/************************************************************/
 
ApplMon_Dev *applmon_devices;
 
/*============== End of Variable Declarations ==============*/
 
/************************************************************/
/*                                                          */
/* Name       - applMonGen.                                 */
/*                                                          */
/* Function   - Issues DIAG DC to talk to monitor service.  */
/*                                                          */
/* Parameters - fcn    - Function code.                     */
/*              devExt - Device extension.                  */
/*                                                          */
/************************************************************/
 
static int inline
applMonGen(unsigned char fcn, ApplMon_Dev *devExt, 
	   char *buffer, short bufLen)
{
 int rc;
 ApPlist *plist;
 
   if ((fcn < APPLMON_START_REC) ||
       (fcn > APPLMON_START_CON))
      return -EINVAL;
 
   plist = &devExt->plist;
 
   plist->fcn     = fcn;
   plist->bufAddr = buffer;
   plist->bufLen  = bufLen;  
 
#ifdef __s390x__
   asm volatile ("LRAG  2,0(%1)\n\t"
                 ".long 0x832300dc\n\t"
                 "LGFR  %0,3\n\t"
                 : "=d" (rc)
                 : "a" (plist)
                 : "2", "3");
#else
   asm volatile ("LRA   2,0(%1)\n\t"
                 ".long 0x832300dc\n\t"
                 "LR    %0,3\n\t"
                 : "=d" (rc)
                 : "a" (plist)
                 : "2", "3");
#endif
 
   if (rc != 0)
      rc = -EINVAL;
 
   return(rc);
}
 
/*===================== End of Function ====================*/
 
/************************************************************/
/*                                                          */
/* Name       - applmon_open.                               */
/*                                                          */
/* Function   - Open the application monitor device.        */
/*                                                          */
/* Parameters - inode -                                     */
/*              filp  -                                     */
/*                                                          */
/************************************************************/
 
int
applmon_open (struct inode *inode, struct file *filp)
{
 int num  = MINOR(inode->i_rdev);
 CPInt_Dev   *dev;
 ApplMon_Dev *devExt;
 
    if (num >= cpint_nr_devs)
       return -ENODEV;
 
    dev = &cpint_devices[num];
 
    devExt = kmalloc(sizeof(ApplMon_Dev) + 2 * APPLMON_BUFSIZE, 
		     GFP_ATOMIC);
 
    if (!devExt)
       return -ENOMEM;
 
    memset(devExt, 0, sizeof(ApplMon_Dev) + 2 * APPLMON_BUFSIZE);
    devExt->buffer = (char *) devExt + sizeof(ApplMon_Dev);
    devExt->sample = devExt->buffer + APPLMON_BUFSIZE;
 
    /*-----------------------------------------------------*/
    /* use filp->private_data to point to the device extn  */
    /*-----------------------------------------------------*/
    devExt->dev           = dev;
    filp->private_data    = devExt;
    devExt->plist.code    = APPLMON_DIAG_CODE;
    devExt->plist.len     = sizeof(devExt->plist);
    devExt->plist.prodid  = &devExt->product_id[0];
 
    MOD_INC_USE_COUNT;

    return 0;
}
 
/*===================== End of Function ====================*/
 
/************************************************************/
/*                                                          */
/* Name       - applmon_release.                            */
/*                                                          */
/* Function   - Close a device.                             */
/*                                                          */
/* Parameters - inode -                                     */
/*              filp  -                                     */
/*                                                          */
/************************************************************/
 
int
applmon_release (struct inode *inode, struct file *filp)
{
 ApplMon_Dev *devExt = filp->private_data;
 
   if (devExt) {
      applMonGen(APPLMON_STOP_REC, devExt, 
                 devExt->sample, APPLMON_BUFSIZE);
      kfree(devExt);
      filp->private_data = NULL;
 
      MOD_DEC_USE_COUNT;
   }
   else
      return -EBADF;
 
   return(0);
}
 
/*===================== End of Function ====================*/
 
/************************************************************/
/*                                                          */
/* Name       - applmon_write.                              */
/*                                                          */
/* Function   - Perform action based on command code:       */
/*              1. Write an event record		    */
/*		2. Place data in the sample buffer          */
/*                                                          */
/* Parameters - inode -                                     */
/*              filp  -                                     */
/*              buf   - Pointer to write buffer.            */
/*              count - Size of write buffer.               */
/*                                                          */
/************************************************************/
 
ssize_t
applmon_write (struct file *filp, const char *buf,
               count_t count, loff_t *ppos)
{
 ApplMon_Dev *devExt = filp->private_data;
 int rc;
 char monLen, fcn;
 struct monData {
   char fcn;
   char len;
   char data[APPLMON_BUFSIZE];
 } *monData;
 
    if (!devExt)
	return -EBADF;

    if (count <= 4)
       return -EINVAL;
 
    monData = (void *) buf;
 
    copy_from_user(&monLen, &monData->len, sizeof(monLen));
    copy_from_user(&fcn, &monData->fcn, sizeof(fcn));
 
    switch (fcn) {

	case APPLMON_START_REC :
           if ((devExt->status & APRECSTART) != APRECSTART) { 
	      if ((rc = applMonGen(fcn, devExt, 
                                   devExt->sample, APPLMON_BUFSIZE)) != 0)
	         return(rc);
              else
                 devExt->status |= APRECSTART;
	   }
	   else
              return -EBADRQC;
	break;

	case APPLMON_STOP_REC :
           if (devExt->status != 0) {
	      if ((rc = applMonGen(fcn, devExt, 
                                   devExt->sample, APPLMON_BUFSIZE)) != 0)
		 return(rc);
	      else
		 devExt->status = 0;
	   }
	   else
	      return -EBADRQC;
	break;

	case APPLMON_GEN_EVENT :
	   if (monLen <= APPLMON_BUFSIZE) {
	      copy_from_user(devExt->buffer, monData->data, monLen);
	      return(applMonGen(fcn, devExt, devExt->buffer, monLen));
	   }
	   else
	      return -E2BIG;
	break;

        case APPLMON_START_CON :
	   if ((devExt->status & APCONSTART) != APCONSTART) {
	      if ((rc = applMonGen(fcn, devExt, 
                                   devExt->sample, APPLMON_BUFSIZE)) != 0)
		 return(rc);
	      else
		 devExt->status |= APCONSTART;
	   }
           else
	      return -EBADRQC;
	break;
	    
	case APPLMON_PUT_SAMP :
	   if (monLen <= APPLMON_BUFSIZE)
	      copy_from_user(devExt->sample, monData->data, monLen);
	   else
	      return -E2BIG;
	break;

	default :
	   return -EINVAL;
    }

    filp->f_pos = 0;
    return count;
}
 
/*===================== End of Function ====================*/

/************************************************************/
/*                                                          */
/* Name       - applmon_ioctl.                              */
/*                                                          */
/* Function   -                                             */
/*                                                          */
/* Parameters - inode -                                     */
/*              filp  -                                     */
/*              cmd   - Command to be issued.               */
/*              arg   - Argument.                           */
/*                                                          */
/************************************************************/

int
applmon_ioctl (struct inode *inode, struct file *filp,
             unsigned int cmd, unsigned long arg)
{
 ApplMon_Dev *devExt = filp->private_data;

   if (devExt) {
      switch (cmd) {
         case MONSETPROD :      /* Set the product ID       */
            copy_from_user(devExt->product_id, (char *) arg, 
                           sizeof(devExt->product_id));
         break;
 
         default :
            return -EINVAL;
      }
   }
   else
      return -EBADF;

   return 0;
}

/*===================== End of Function ====================*/
