/************************************************************/
/*                                                          */
/* 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->len = sizeof (plist);
	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_KERNEL | __GFP_REPEAT);

	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];

	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;

	} 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, size_t count, loff_t * ppos)
{
	ApplMon_Dev *devExt = filp->private_data;
	int rc;
	char fcn;
	struct monData {
		char fcn;
		char data[APPLMON_BUFSIZE];
	} *monData;

	if (!devExt)
		return -EBADF;

	if (count <= 4)
		return -EINVAL;

	monData = (void *) buf;

	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 (count <= APPLMON_BUFSIZE) {
			copy_from_user(devExt->buffer, monData->data, count);
			return (applMonGen(fcn, devExt, devExt->buffer, count));
		} 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 (count <= APPLMON_BUFSIZE)
			copy_from_user(devExt->sample, monData->data, count);
		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 ====================*/
