/************************************************************/
/*                                                          */
/* Module ID  - cmdmain.c                                   */
/*                                                          */
/* Function   - Allow privileged caller to invoke a CP      */
/*              command and read response.                  */
/*                                                          */
/* Called By  - N/A.                                        */
/*                                                          */
/* Calling To - N/A.                                        */
/*                                                          */
/* Parameters - See individual entry points.                */
/*                                                          */
/* Notes      - (1) Code published under GPL. Copyright     */
/*                  Neale Ferguson, Sterling Software.      */
/*                                                          */
/*                                                          */
/* 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 "cpcmd.h"        	/* specific definitions */
 
/*================== End of Include Statements =============*/
 
/************************************************************/
/*                                                          */
/*              TYPE DEFINITIONS                            */
/*              ----------------                            */
/*                                                          */
/************************************************************/
 
/*================== End of Type Definitions ===============*/
 
/************************************************************/
/*                                                          */
/*             FUNCTION PROTOTYPES                          */
/*             -------------------                          */
/*                                                          */
/************************************************************/
 
/*================== End of Prototypes =====================*/
 
/************************************************************/
/*                                                          */
/*           GLOBAL VARIABLE DECLARATIONS                   */
/*           ----------------------------                   */
/*                                                          */
/************************************************************/
 
 
/*============== End of Variable Declarations ==============*/
 
/************************************************************/
/*                                                          */
/* Name       - cpcmd_open.                                 */
/*                                                          */
/* Function   - Open the CP command device.                 */
/*                                                          */
/* Parameters - inode -                                     */
/*              filp  -                                     */
/*                                                          */
/************************************************************/
 
int
cpcmd_open (struct inode *inode, struct file *filp)
{
 int num  = MINOR(inode->i_rdev);
 CPInt_Dev *dev;
 CPCmd_Dev *devExt;
 
    if (num >= cpint_nr_devs)
       return -ENODEV;
 
    dev = &cpint_devices[num];
 
    devExt  = kmalloc(sizeof(CPCmd_Dev), GFP_ATOMIC);
 
    if (!devExt)
       return -ENOMEM;
 
    memset(devExt, 0, sizeof(CPCmd_Dev));
    devExt->dev = dev;
 
    /*--------------------------------------------------*/
    /* use filp->private_data to point to device data   */
    /*--------------------------------------------------*/
    filp->private_data = devExt;
 
    MOD_INC_USE_COUNT;
    return 0;
}
 
/*===================== End of Function ====================*/
 
/************************************************************/
/*                                                          */
/* Name       - cpcmd_release.                              */
/*                                                          */
/* Function   - Close a device.                             */
/*                                                          */
/* Parameters - inode -                                     */
/*              filp  -                                     */
/*                                                          */
/************************************************************/
 
int
cpcmd_release (struct inode *inode, struct file *filp)
{
 CPCmd_Dev *devExt = filp->private_data;
 
   if (devExt->data)
      kfree(devExt->data);
 
   kfree(devExt);
 
   MOD_DEC_USE_COUNT;
 
   return(0);
}
 
/*===================== End of Function ====================*/
 
/************************************************************/
/*                                                          */
/* Name       - cpcmd_read.                                 */
/*                                                          */
/* Function   - Read the results of a CP command.           */
/*                                                          */
/* Parameters - inode -                                     */
/*              filp  -                                     */
/*              buf   - Pointer to read buffer.             */
/*              count - Size of read buffer.                */
/*                                                          */
/************************************************************/
 
ssize_t
cpcmd_read (struct file *filp, char *buf,
            count_t count, loff_t *ppos)
{
 CPCmd_Dev *devExt = filp->private_data;
 unsigned long f_pos = (unsigned long)(filp->f_pos);
 
    if (f_pos > devExt->size)
        return 0;
 
    if (f_pos + count > devExt->size)
        count = devExt->size - f_pos;
 
    if (!devExt->data)
        return 0;
 
    copy_to_user(buf, &devExt->data[f_pos], count);
 
    filp->f_pos += count;
    if (filp->f_pos > devExt->size) {
       filp->f_pos = 0;
       kfree(devExt->data);
       devExt->data = NULL;
    }
 
    return count;
}
 
/*===================== End of Function ====================*/
 
/************************************************************/
/*                                                          */
/* Name       - cpcmd_write.                                */
/*                                                          */
/* Function   - Execute a CP command and place result into  */
/*              a response buffer.                          */
/*                                                          */
/* Parameters - inode -                                     */
/*              filp  -                                     */
/*              buf   - Pointer to read buffer.             */
/*              count - Size of read buffer.                */
/*                                                          */
/************************************************************/
 
ssize_t
cpcmd_write (struct file *filp, const char *buf,
             count_t count, loff_t *ppos)
{
 CPCmd_Dev *devExt = filp->private_data;
 const long mask   = 0x60000000L;
 int cc = -1,
     rspSize,
     l_cmdOp;
 
    if (count > sizeof(devExt->cmd))
       return -E2BIG;
 
    devExt->count = count;
    if (!devExt->data) {
        devExt->data = kmalloc(CPRESP, GFP_ATOMIC);
        if (!devExt->data)
            return -ENOMEM;
        devExt->size = CPRESP;
        memset(devExt->data, 0, CPRESP);
    }
 
    copy_from_user(devExt->cmd, buf, devExt->count);
 
    for (l_cmdOp = 0; l_cmdOp < devExt->count; l_cmdOp++) {
	if (devExt->cmd[l_cmdOp] != ' ')
	   break;
    }
    if (l_cmdOp == devExt->count)
	return -EINVAL;

    for (; l_cmdOp < devExt->count; l_cmdOp++) {
	if (devExt->cmd[l_cmdOp] == ' ')
	   break;
    }

    ASCEBC(devExt->cmd, devExt->count);

    /*------------------------------------------------------*/
    /* Check if we uppercase everything or just the command */
    /*------------------------------------------------------*/
    if (devExt->flag & UPCASE) 
    	EBC_TOUPPER(devExt->cmd, l_cmdOp);
    else
	EBC_TOUPPER(devExt->cmd, devExt->count);

    while (cc != 0)
    {
#ifdef __s390x__
       asm volatile ("LRAG  2,0(%3)\t/* Get cmd address */\n\t"
                     "LR    4,%4\t/* Get length of command */\n\t"
                     "OG    4,%7\t/* Set flags */\n\t"
                     "LRAG  3,0(%5)\t/* Get response address */\n\t"
                     "LR    5,%6\t/* Get response length */\n\t"
		     "AHI   5,-1\t/* Leave room for eol */\n\t"
                     ".long 0x83240008\t/* Issue command */\n\t"
		     "IPM   %1\t\t/* Get CC */\n\t"
		     "SRL   %1,28\t/* Shuffle down */\n\t"
                     "LGFR  %0,4\t/* Keep return code */\n\t"
                     "LR    %2,5\t/* Get response length */\n\t"
                     : "=d" (devExt->rc), "=d" (cc), "=d" (rspSize)
                     : "a" (devExt->cmd), "d" (devExt->count),
                       "a" (devExt->data), "d" (devExt->size),
		       "m" (mask)
                     : "cc", "2", "3", "4", "5");
#else
       asm volatile ("LRA   2,0(%3)\t/* Get cmd address */\n\t"
                     "LR    4,%4\t/* Get length of command */\n\t"
                     "O     4,%7\t/* Set flags */\n\t"
                     "LRA   3,0(%5)\t/* Get response address */\n\t"
                     "LR    5,%6\t/* Get response length */\n\t"
                     "AHI   5,-1\t/* Leave room for eol */\n\t"
                     ".long 0x83240008\t/* Issue command */\n\t"
                     "IPM   %1\t\t/* Get CC */\n\t"
                     "SRL   %1,28\t/* Shuffle down */\n\t"
                     "LR    %0,4\t/* Keep return code */\n\t"
                     "LR    %2,5\t/* Get response length */\n\t"
                     : "=d" (devExt->rc), "=d" (cc), "=d" (rspSize)
                     : "a" (devExt->cmd), "d" (devExt->count),
                       "a" (devExt->data), "d" (devExt->size),
                       "m" (mask)
                     : "cc", "2", "3", "4", "5");
#endif
       if (cc != 0) {
	  if (rspSize <= CPRSPLIM) {
             kfree(devExt->data);
             devExt->size += rspSize + 1;
             devExt->data = kmalloc(devExt->size, GFP_ATOMIC);
             if (!devExt->data)
                return -ENOMEM;
             memset(devExt->data, 0, devExt->size);
          }
	  else
	     return -ENOMEM;
       }
       else
          devExt->size = rspSize;
    }
    EBCASC(devExt->data, devExt->size);
 
    filp->f_pos = 0;
    return count;
}
 
/*===================== End of Function ====================*/

/************************************************************/
/*                                                          */
/* Name       - cpcmd_ioctl.                                */
/*                                                          */
/* Function   - Perform IOCTL functions for CP interface:   */
/*		1. Retrieve the return code of the last     */
/*                 command issued.			    */
/*                                                          */
/* Parameters - inode -                                     */
/*              filp  -                                     */
/*              cmd   - Command to be issued.               */
/*              arg   - Argument.                           */
/*                                                          */
/************************************************************/

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

   if (devExt != NULL) {
      switch (cmd) {
         case CPIOCRC : 	/* Return the return code   */
	    if (copy_to_user((char *) arg, 
                             &devExt->rc,
                             sizeof(devExt->rc)))
	       return -EFAULT;
            return 0;
	    break;

	 case CPIOUPC :		/* Only uppercase command   */
	    devExt->flag |= UPCASE;
	    return 0;
	    break;

	 default :
	    return -EINVAL;
      }
   }
   else
      return -ENODEV;

}

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