vmlogrdr.c revision 4f0076f77fb64889d4e5e425b63333e5764b446d
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * drivers/s390/char/vmlogrdr.c
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	character device driver for reading z/VM system service records
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
69f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber *	Copyright IBM Corp. 2004, 2009
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	character device driver for reading z/VM system service records,
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Version 1.0
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Author(s): Xenia Tkatschow <xenia@us.ibm.com>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		   Stefan Weinhuber <wein@de.ibm.com>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
135466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky
145466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky#define KMSG_COMPONENT "vmlogrdr"
155466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
165466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/atomic.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/cpcmd.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/debug.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/ebcdic.h>
28c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky#include <net/iucv/iucv.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kmod.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/cdev.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/device.h>
32764a4a8e54cdd6efc5928f876aa9e35778f22377Jonathan Corbet#include <linux/smp_lock.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n"
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 "                            Stefan Weinhuber (wein@de.ibm.com)");
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION ("Character device driver for reading z/VM "
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    "system service records.");
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The size of the buffer for iucv data transfer is one page,
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but in addition to the data we read from iucv we also
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * place an integer and some characters into that buffer,
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * so the maximum size for record data is a little less then
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * one page.
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NET_BUFFER_SIZE	(PAGE_SIZE - sizeof(int) - sizeof(FENCE))
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The elements that are concurrently accessed by bottom halves are
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * connection_established, iucv_path_severed, local_interrupt_buffer
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and receive_ready. The first three can be protected by
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * priv_lock.  receive_ready is atomic, so it can be incremented and
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * decremented without holding a lock.
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The variable dev_in_use needs to be protected by the lock, since
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it's a flag used by open to make sure that the device is opened only
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * by one user at the same time.
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct vmlogrdr_priv_t {
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char system_service[8];
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char internal_name[8];
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char recording_name[8];
66c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	struct iucv_path *path;
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int connection_established;
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int iucv_path_severed;
69c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	struct iucv_message local_interrupt_buffer;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_t receive_ready;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor_num;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char * buffer;
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char * current_position;
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int remaining;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ulong residual_length;
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buffer_free;
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int dev_in_use; /* 1: already opened, 0: not opened*/
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spinlock_t priv_lock;
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct device  *device;
807f021ce1957504cacc78896de857b90293badabcCornelia Huck	struct device  *class_device;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int autorecording;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int autopurge;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * File operation structure for vmlogrdr devices
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int vmlogrdr_open(struct inode *, struct file *);
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int vmlogrdr_release(struct inode *, struct file *);
91d2c993d845781d160a7ef759a3e65c6892c4a270Heiko Carstensstatic ssize_t vmlogrdr_read (struct file *filp, char __user *data,
92d2c993d845781d160a7ef759a3e65c6892c4a270Heiko Carstens			      size_t count, loff_t * ppos);
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
94d54b1fdb1d9f82e375a299e22bd366aad52d4c34Arjan van de Venstatic const struct file_operations vmlogrdr_fops = {
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner   = THIS_MODULE,
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open    = vmlogrdr_open,
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release = vmlogrdr_release,
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read    = vmlogrdr_read,
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
102c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 ipuser[16]);
103c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 ipuser[16]);
104c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic void vmlogrdr_iucv_message_pending(struct iucv_path *,
105c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					  struct iucv_message *);
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
108c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic struct iucv_handler vmlogrdr_iucv_handler = {
109c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	.path_complete	 = vmlogrdr_iucv_path_complete,
110c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	.path_severed	 = vmlogrdr_iucv_path_severed,
111c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	.message_pending = vmlogrdr_iucv_message_pending,
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1152b67fc46061b2171fb8fbb55d1ac717abd533569Heiko Carstensstatic DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue);
1162b67fc46061b2171fb8fbb55d1ac717abd533569Heiko Carstensstatic DECLARE_WAIT_QUEUE_HEAD(read_wait_queue);
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * pointer to system service private structure
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * minor number 0 --> logrec
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * minor number 1 --> account
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * minor number 2 --> symptom
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct vmlogrdr_priv_t sys_ser[] = {
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ .system_service = "*LOGREC ",
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .internal_name  = "logrec",
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .recording_name = "EREP",
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .minor_num      = 0,
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .buffer_free    = 1,
131cb629a01bb5bca951287e761c590a5686c6ca416Milind Arun Choudhary	  .priv_lock	  = __SPIN_LOCK_UNLOCKED(sys_ser[0].priv_lock),
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .autorecording  = 1,
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .autopurge      = 1,
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ .system_service = "*ACCOUNT",
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .internal_name  = "account",
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .recording_name = "ACCOUNT",
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .minor_num      = 1,
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .buffer_free    = 1,
140cb629a01bb5bca951287e761c590a5686c6ca416Milind Arun Choudhary	  .priv_lock	  = __SPIN_LOCK_UNLOCKED(sys_ser[1].priv_lock),
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .autorecording  = 1,
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .autopurge      = 1,
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ .system_service = "*SYMPTOM",
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .internal_name  = "symptom",
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .recording_name = "SYMPTOM",
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .minor_num      = 2,
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .buffer_free    = 1,
149cb629a01bb5bca951287e761c590a5686c6ca416Milind Arun Choudhary	  .priv_lock	  = __SPIN_LOCK_UNLOCKED(sys_ser[2].priv_lock),
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .autorecording  = 1,
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  .autopurge      = 1,
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAXMINOR  (sizeof(sys_ser)/sizeof(struct vmlogrdr_priv_t))
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char FENCE[] = {"EOR"};
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int vmlogrdr_major = 0;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct cdev  *vmlogrdr_cdev = NULL;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int recording_class_AB;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
163c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
165c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	struct vmlogrdr_priv_t * logptr = path->private;
166c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock(&logptr->priv_lock);
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr->connection_established = 1;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&logptr->priv_lock);
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wake_up(&conn_wait_queue);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
174c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
176c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	struct vmlogrdr_priv_t * logptr = path->private;
177c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	u8 reason = (u8) ipuser[8];
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1795466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky	pr_err("vmlogrdr: connection severed with reason %i\n", reason);
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	iucv_path_sever(path, NULL);
182c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	kfree(path);
183c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->path = NULL;
184c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock(&logptr->priv_lock);
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr->connection_established = 0;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr->iucv_path_severed = 1;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&logptr->priv_lock);
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wake_up(&conn_wait_queue);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* just in case we're sleeping waiting for a record */
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wake_up_interruptible(&read_wait_queue);
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
196c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic void vmlogrdr_iucv_message_pending(struct iucv_path *path,
197c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					  struct iucv_message *msg)
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
199c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	struct vmlogrdr_priv_t * logptr = path->private;
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * This function is the bottom half so it should be quick.
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Copy the external interrupt data into our local eib and increment
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * the usage count
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock(&logptr->priv_lock);
207c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	memcpy(&logptr->local_interrupt_buffer, msg, sizeof(*msg));
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_inc(&logptr->receive_ready);
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&logptr->priv_lock);
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wake_up_interruptible(&read_wait_queue);
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
214c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_get_recording_class_AB(void)
215c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char cp_command[]="QUERY COMMAND RECORDING ";
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char cp_response[80];
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *tail;
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int len,i;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2216b979de395c7e1b7e59f74a870e1d1911853eccbChristian Borntraeger	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len = strnlen(cp_response,sizeof(cp_response));
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// now the parsing
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tail=strnchr(cp_response,len,'=');
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!tail)
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tail++;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!strncmp("ANY",tail,3))
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 1;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!strncmp("NONE",tail,4))
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * expect comma separated list of classes here, if one of them
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * is A or B return 1 otherwise 0
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        for (i=tail-cp_response; i<len; i++)
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ( cp_response[i]=='A' || cp_response[i]=='B' )
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 1;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
243c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_recording(struct vmlogrdr_priv_t * logptr,
244c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky			      int action, int purge)
245c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char cp_command[80];
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char cp_response[160];
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *onoff, *qid_string;
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cp_command, 0x00, sizeof(cp_command));
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cp_response, 0x00, sizeof(cp_response));
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        onoff = ((action == 1) ? "ON" : "OFF");
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	qid_string = ((recording_class_AB == 1) ? " QID * " : "");
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        /*
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * The recording commands needs to be called with option QID
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * for guests that have previlege classes A or B.
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Purging has to be done as separate step, because recording
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * can't be switched on as long as records are on the queue.
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Doing both at the same time doesn't work.
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (purge) {
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		snprintf(cp_command, sizeof(cp_command),
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 "RECORDING %s PURGE %s",
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 logptr->recording_name,
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 qid_string);
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2716b979de395c7e1b7e59f74a870e1d1911853eccbChristian Borntraeger		cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cp_command, 0x00, sizeof(cp_command));
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cp_response, 0x00, sizeof(cp_response));
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	snprintf(cp_command, sizeof(cp_command), "RECORDING %s %s %s",
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		logptr->recording_name,
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		onoff,
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		qid_string);
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2816b979de395c7e1b7e59f74a870e1d1911853eccbChristian Borntraeger	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The recording command will usually answer with 'Command complete'
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * on success, but when the specific service was never connected
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * before then there might be an additional informational message
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * 'HCPCRC8072I Recording entry not found' before the
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         * 'Command complete'. So I use strstr rather then the strncmp.
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (strstr(cp_response,"Command complete"))
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
296c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_open (struct inode *inode, struct file *filp)
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int dev_num = 0;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct vmlogrdr_priv_t * logptr = NULL;
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int connect_rc = 0;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_num = iminor(inode);
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev_num > MAXMINOR)
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr = &sys_ser[dev_num];
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * only allow for blocking reads to be open
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (filp->f_flags & O_NONBLOCK)
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOSYS;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Besure this device hasn't already been opened */
315764a4a8e54cdd6efc5928f876aa9e35778f22377Jonathan Corbet	lock_kernel();
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&logptr->priv_lock);
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (logptr->dev_in_use)	{
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_bh(&logptr->priv_lock);
319764a4a8e54cdd6efc5928f876aa9e35778f22377Jonathan Corbet		unlock_kernel();
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
322c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->dev_in_use = 1;
323c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->connection_established = 0;
324c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->iucv_path_severed = 0;
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_set(&logptr->receive_ready, 0);
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr->buffer_free = 1;
327c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	spin_unlock_bh(&logptr->priv_lock);
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* set the file options */
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	filp->private_data = logptr;
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	filp->f_op = &vmlogrdr_fops;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* start recording for this service*/
334c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	if (logptr->autorecording) {
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = vmlogrdr_recording(logptr,1,logptr->autopurge);
336c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		if (ret)
3375466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky			pr_warning("vmlogrdr: failed to start "
3385466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky				   "recording automatically\n");
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* create connection to the system service */
342c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->path = iucv_path_alloc(10, 0, GFP_KERNEL);
343c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	if (!logptr->path)
344c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out_dev;
345c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	connect_rc = iucv_path_connect(logptr->path, &vmlogrdr_iucv_handler,
346c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				       logptr->system_service, NULL, NULL,
347c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				       logptr);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (connect_rc) {
3495466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky		pr_err("vmlogrdr: iucv connection to %s "
3505466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky		       "failed with rc %i \n",
3515466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky		       logptr->system_service, connect_rc);
352c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out_path;
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We've issued the connect and now we must wait for a
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * ConnectionComplete or ConnectinSevered Interrupt
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * before we can continue to process.
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wait_event(conn_wait_queue, (logptr->connection_established)
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   || (logptr->iucv_path_severed));
361c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	if (logptr->iucv_path_severed)
362c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out_record;
363764a4a8e54cdd6efc5928f876aa9e35778f22377Jonathan Corbet 	ret = nonseekable_open(inode, filp);
364764a4a8e54cdd6efc5928f876aa9e35778f22377Jonathan Corbet	unlock_kernel();
365764a4a8e54cdd6efc5928f876aa9e35778f22377Jonathan Corbet	return ret;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
367c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_record:
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (logptr->autorecording)
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmlogrdr_recording(logptr,0,logptr->autopurge);
370c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_path:
371c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	kfree(logptr->path);	/* kfree(NULL) is ok. */
372c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->path = NULL;
373c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_dev:
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr->dev_in_use = 0;
375764a4a8e54cdd6efc5928f876aa9e35778f22377Jonathan Corbet	unlock_kernel();
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
380c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_release (struct inode *inode, struct file *filp)
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct vmlogrdr_priv_t * logptr = filp->private_data;
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
38666b494a7178cbd84d8fc0e5f1e92d81fb6ec9f6eUrsula Braun	iucv_path_sever(logptr->path, NULL);
38766b494a7178cbd84d8fc0e5f1e92d81fb6ec9f6eUrsula Braun	kfree(logptr->path);
38866b494a7178cbd84d8fc0e5f1e92d81fb6ec9f6eUrsula Braun	logptr->path = NULL;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (logptr->autorecording) {
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ret)
3925466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky			pr_warning("vmlogrdr: failed to stop "
3935466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky				   "recording automatically\n");
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr->dev_in_use = 0;
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv)
402c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc, *temp;
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* we need to keep track of two data sizes here:
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * The number of bytes we need to receive from iucv and
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * the total number of bytes we actually write into the buffer.
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int user_data_count, iucv_data_count;
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char * buffer;
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (atomic_read(&priv->receive_ready)) {
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_bh(&priv->priv_lock);
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (priv->residual_length){
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* receive second half of a record */
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			iucv_data_count = priv->residual_length;
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			user_data_count = 0;
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = priv->buffer;
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* receive a new record:
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * We need to return the total length of the record
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                         * + size of FENCE in the first 4 bytes of the buffer.
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		         */
423c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky			iucv_data_count = priv->local_interrupt_buffer.length;
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			user_data_count = sizeof(int);
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			temp = (int*)priv->buffer;
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			*temp= iucv_data_count + sizeof(FENCE);
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = priv->buffer + sizeof(int);
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
430025dfdafe77f20b3890981a394774baab7b9c827Frederik Schwarzer		 * If the record is bigger than our buffer, we receive only
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * a part of it. We can get the rest later.
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (iucv_data_count > NET_BUFFER_SIZE)
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			iucv_data_count = NET_BUFFER_SIZE;
435c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		rc = iucv_message_receive(priv->path,
436c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					  &priv->local_interrupt_buffer,
437c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					  0, buffer, iucv_data_count,
438c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					  &priv->residual_length);
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_bh(&priv->priv_lock);
440025dfdafe77f20b3890981a394774baab7b9c827Frederik Schwarzer		/* An rc of 5 indicates that the record was bigger than
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * the buffer, which is OK for us. A 9 indicates that the
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * record was purged befor we could receive it.
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc == 5)
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rc = 0;
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc == 9)
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			atomic_set(&priv->receive_ready, 0);
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = 1;
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!rc) {
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->buffer_free = 0;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 		user_data_count += iucv_data_count;
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->current_position = priv->buffer;
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (priv->residual_length == 0){
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* the whole record has been captured,
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * now add the fence */
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			atomic_dec(&priv->receive_ready);
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = priv->buffer + user_data_count;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(buffer, FENCE, sizeof(FENCE));
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			user_data_count += sizeof(FENCE);
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->remaining = user_data_count;
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
470c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_read(struct file *filp, char __user *data,
471c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky			     size_t count, loff_t * ppos)
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct vmlogrdr_priv_t * priv = filp->private_data;
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (priv->buffer_free) {
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = vmlogrdr_receive_data(priv);
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc) {
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rc = wait_event_interruptible(read_wait_queue,
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					atomic_read(&priv->receive_ready));
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (rc)
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return rc;
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* copy only up to end of record */
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count > priv->remaining)
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count = priv->remaining;
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (copy_to_user(data, priv->current_position, count))
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*ppos += count;
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->current_position += count;
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->remaining -= count;
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* if all data has been transferred, set buffer free */
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (priv->remaining == 0)
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->buffer_free = 1;
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
503c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_autopurge_store(struct device * dev,
504c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					struct device_attribute *attr,
505c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					const char * buf, size_t count)
506c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
507dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ssize_t ret = count;
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (buf[0]) {
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '0':
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->autopurge=0;
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '1':
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->autopurge=1;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EINVAL;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
524c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_autopurge_show(struct device *dev,
525c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				       struct device_attribute *attr,
526c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				       char *buf)
527c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
528dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return sprintf(buf, "%u\n", priv->autopurge);
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEVICE_ATTR(autopurge, 0644, vmlogrdr_autopurge_show,
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   vmlogrdr_autopurge_store);
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
537c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_purge_store(struct device * dev,
538c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				    struct device_attribute *attr,
539c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				    const char * buf, size_t count)
540c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char cp_command[80];
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char cp_response[80];
544dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (buf[0] != '1')
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cp_command, 0x00, sizeof(cp_command));
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cp_response, 0x00, sizeof(cp_response));
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        /*
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * The recording command needs to be called with option QID
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * for guests that have previlege classes A or B.
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Other guests will not recognize the command and we have to
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * issue the same command without the QID parameter.
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (recording_class_AB)
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		snprintf(cp_command, sizeof(cp_command),
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 "RECORDING %s PURGE QID * ",
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 priv->recording_name);
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		snprintf(cp_command, sizeof(cp_command),
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 "RECORDING %s PURGE ",
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 priv->recording_name);
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5686b979de395c7e1b7e59f74a870e1d1911853eccbChristian Borntraeger	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEVICE_ATTR(purge, 0200, NULL, vmlogrdr_purge_store);
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
577c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_autorecording_store(struct device *dev,
578c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					    struct device_attribute *attr,
579c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					    const char *buf, size_t count)
580c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
581dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ssize_t ret = count;
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (buf[0]) {
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '0':
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->autorecording=0;
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '1':
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->autorecording=1;
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EINVAL;
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
598c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_autorecording_show(struct device *dev,
599c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					   struct device_attribute *attr,
600c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					   char *buf)
601c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
602dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return sprintf(buf, "%u\n", priv->autorecording);
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEVICE_ATTR(autorecording, 0644, vmlogrdr_autorecording_show,
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   vmlogrdr_autorecording_store);
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
611c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_recording_store(struct device * dev,
612c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					struct device_attribute *attr,
613c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					const char * buf, size_t count)
614c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
615dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ssize_t ret;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (buf[0]) {
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '0':
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = vmlogrdr_recording(priv,0,0);
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '1':
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = vmlogrdr_recording(priv,1,0);
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EINVAL;
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret)
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return count;
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEVICE_ATTR(recording, 0200, NULL, vmlogrdr_recording_store);
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
639c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_recording_status_show(struct device_driver *driver,
640c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					      char *buf)
641c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char cp_command[] = "QUERY RECORDING ";
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int len;
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6466b979de395c7e1b7e59f74a870e1d1911853eccbChristian Borntraeger	cpcmd(cp_command, buf, 4096, NULL);
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len = strlen(buf);
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return len;
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DRIVER_ATTR(recording_status, 0444, vmlogrdr_recording_status_show,
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   NULL);
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct attribute *vmlogrdr_attrs[] = {
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	&dev_attr_autopurge.attr,
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	&dev_attr_purge.attr,
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	&dev_attr_autorecording.attr,
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	&dev_attr_recording.attr,
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NULL,
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6639f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuberstatic int vmlogrdr_pm_prepare(struct device *dev)
6649f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber{
6659f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	int rc;
6664f0076f77fb64889d4e5e425b63333e5764b446dMartin Schwidefsky	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
6679f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber
6689f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	rc = 0;
6699f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	if (priv) {
6709f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber		spin_lock_bh(&priv->priv_lock);
6719f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber		if (priv->dev_in_use)
6729f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber			rc = -EBUSY;
6739f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber		spin_unlock_bh(&priv->priv_lock);
6749f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	}
6759f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	if (rc)
6769f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber		pr_err("vmlogrdr: device %s is busy. Refuse to suspend.\n",
6779f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber		       dev_name(dev));
6789f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	return rc;
6799f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber}
6809f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber
6819f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber
6829f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuberstatic struct dev_pm_ops vmlogrdr_pm_ops = {
6839f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	.prepare = vmlogrdr_pm_prepare,
6849f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber};
6859f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct attribute_group vmlogrdr_attr_group = {
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.attrs = vmlogrdr_attrs,
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
69056b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartmanstatic struct class *vmlogrdr_class;
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct device_driver vmlogrdr_driver = {
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name = "vmlogrdr",
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.bus  = &iucv_bus,
6949f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	.pm = &vmlogrdr_pm_ops,
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
698c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_register_driver(void)
699c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
702c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	/* Register with iucv driver */
703c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	ret = iucv_register(&vmlogrdr_iucv_handler, 1);
7042f6f2521bcfff108e02d97e3a326f995996ce95fMartin Schwidefsky	if (ret)
705c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out;
706c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = driver_register(&vmlogrdr_driver);
7082f6f2521bcfff108e02d97e3a326f995996ce95fMartin Schwidefsky	if (ret)
709c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out_iucv;
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = driver_create_file(&vmlogrdr_driver,
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 &driver_attr_recording_status);
7132f6f2521bcfff108e02d97e3a326f995996ce95fMartin Schwidefsky	if (ret)
714c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out_driver;
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71656b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartman	vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr");
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IS_ERR(vmlogrdr_class)) {
718c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		ret = PTR_ERR(vmlogrdr_class);
719c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		vmlogrdr_class = NULL;
720c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out_attr;
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
724c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_attr:
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);
726c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_driver:
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver_unregister(&vmlogrdr_driver);
728c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_iucv:
729c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	iucv_unregister(&vmlogrdr_iucv_handler, 1);
730c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout:
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
735c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic void vmlogrdr_unregister_driver(void)
736c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
73756b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartman	class_destroy(vmlogrdr_class);
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_class = NULL;
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status);
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver_unregister(&vmlogrdr_driver);
741c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	iucv_unregister(&vmlogrdr_iucv_handler, 1);
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
745c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
746c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct device *dev;
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
75088abaab4f9b08381e30e737980a1c49d6b524dfcEric Sesterhenn	dev = kzalloc(sizeof(struct device), GFP_KERNEL);
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev) {
7521bf5b2853925cf92bfc5f0eddb68a8ed18782845Cornelia Huck		dev_set_name(dev, priv->internal_name);
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->bus = &iucv_bus;
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->parent = iucv_root;
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->driver = &vmlogrdr_driver;
7564f0076f77fb64889d4e5e425b63333e5764b446dMartin Schwidefsky		dev_set_drvdata(dev, priv);
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * The release function could be called after the
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * module has been unloaded. It's _only_ task is to
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * free the struct. Therefore, we specify kfree()
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * directly here. (Probably a little bit obfuscating
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * but legitime ...).
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->release = (void (*)(struct device *))kfree;
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = device_register(dev);
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret)
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = sysfs_create_group(&dev->kobj, &vmlogrdr_attr_group);
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret) {
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		device_unregister(dev);
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
776ea9e42f627a45f979b4977009724eb114406e3c7Greg Kroah-Hartman	priv->class_device = device_create(vmlogrdr_class, dev,
777ea9e42f627a45f979b4977009724eb114406e3c7Greg Kroah-Hartman					   MKDEV(vmlogrdr_major,
778ea9e42f627a45f979b4977009724eb114406e3c7Greg Kroah-Hartman						 priv->minor_num),
779ea9e42f627a45f979b4977009724eb114406e3c7Greg Kroah-Hartman					   priv, "%s", dev_name(dev));
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IS_ERR(priv->class_device)) {
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = PTR_ERR(priv->class_device);
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->class_device=NULL;
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sysfs_remove_group(&dev->kobj, &vmlogrdr_attr_group);
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		device_unregister(dev);
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->device = dev;
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
792c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv)
793c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
7947f021ce1957504cacc78896de857b90293badabcCornelia Huck	device_destroy(vmlogrdr_class, MKDEV(vmlogrdr_major, priv->minor_num));
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (priv->device != NULL) {
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sysfs_remove_group(&priv->device->kobj, &vmlogrdr_attr_group);
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		device_unregister(priv->device);
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->device=NULL;
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
804c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_register_cdev(dev_t dev)
805c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = 0;
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cdev = cdev_alloc();
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!vmlogrdr_cdev) {
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cdev->owner = THIS_MODULE;
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cdev->ops = &vmlogrdr_fops;
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cdev->dev = dev;
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = cdev_add(vmlogrdr_cdev, vmlogrdr_cdev->dev, MAXMINOR);
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!rc)
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// cleanup: cdev is not fully registered, no cdev_del here!
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kobject_put(&vmlogrdr_cdev->kobj);
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cdev=NULL;
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
825c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic void vmlogrdr_cleanup(void)
826c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        int i;
828c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vmlogrdr_cdev) {
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cdev_del(vmlogrdr_cdev);
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmlogrdr_cdev=NULL;
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i < MAXMINOR; ++i ) {
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmlogrdr_unregister_device(&sys_ser[i]);
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_page((unsigned long)sys_ser[i].buffer);
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_unregister_driver();
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vmlogrdr_major) {
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unregister_chrdev_region(MKDEV(vmlogrdr_major, 0), MAXMINOR);
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmlogrdr_major=0;
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
845f60d89108f3481ca11672b82cf7e67171e050ce4Christian Borntraegerstatic int __init vmlogrdr_init(void)
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_t dev;
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (! MACHINE_IS_VM) {
8525466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky		pr_err("not running under VM, driver not loaded.\n");
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        recording_class_AB = vmlogrdr_get_recording_class_AB();
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = alloc_chrdev_region(&dev, 0, MAXMINOR, "vmlogrdr");
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return rc;
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_major = MAJOR(dev);
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc=vmlogrdr_register_driver();
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto cleanup;
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i < MAXMINOR; ++i ) {
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!sys_ser[i].buffer) {
8703cb2cea15e707dd030b3293d6d08183da369d291Marcin Slusarz			rc = -ENOMEM;
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sys_ser[i].current_position = sys_ser[i].buffer;
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc=vmlogrdr_register_device(&sys_ser[i]);
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc)
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto cleanup;
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = vmlogrdr_register_cdev(dev);
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto cleanup;
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldscleanup:
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cleanup();
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
892f60d89108f3481ca11672b82cf7e67171e050ce4Christian Borntraegerstatic void __exit vmlogrdr_exit(void)
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cleanup();
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(vmlogrdr_init);
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(vmlogrdr_exit);
901