11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	character device driver for reading z/VM system service records
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
59f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber *	Copyright IBM Corp. 2004, 2009
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	character device driver for reading z/VM system service records,
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Version 1.0
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Author(s): Xenia Tkatschow <xenia@us.ibm.com>
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		   Stefan Weinhuber <wein@de.ibm.com>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
125466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky
135466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky#define KMSG_COMPONENT "vmlogrdr"
145466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
155466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
185a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.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>
2360063497a95e716c9a689af3be2687d261f115b4Arun Sharma#include <linux/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>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n"
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 "                            Stefan Weinhuber (wein@de.ibm.com)");
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION ("Character device driver for reading z/VM "
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    "system service records.");
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The size of the buffer for iucv data transfer is one page,
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but in addition to the data we read from iucv we also
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * place an integer and some characters into that buffer,
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * so the maximum size for record data is a little less then
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * one page.
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NET_BUFFER_SIZE	(PAGE_SIZE - sizeof(int) - sizeof(FENCE))
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The elements that are concurrently accessed by bottom halves are
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * connection_established, iucv_path_severed, local_interrupt_buffer
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and receive_ready. The first three can be protected by
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * priv_lock.  receive_ready is atomic, so it can be incremented and
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * decremented without holding a lock.
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The variable dev_in_use needs to be protected by the lock, since
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it's a flag used by open to make sure that the device is opened only
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * by one user at the same time.
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct vmlogrdr_priv_t {
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char system_service[8];
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char internal_name[8];
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char recording_name[8];
65c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	struct iucv_path *path;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int connection_established;
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int iucv_path_severed;
68c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	struct iucv_message local_interrupt_buffer;
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_t receive_ready;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int minor_num;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char * buffer;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char * current_position;
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int remaining;
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ulong residual_length;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buffer_free;
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int dev_in_use; /* 1: already opened, 0: not opened*/
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spinlock_t priv_lock;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct device  *device;
797f021ce1957504cacc78896de857b90293badabcCornelia Huck	struct device  *class_device;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int autorecording;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int autopurge;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * File operation structure for vmlogrdr devices
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int vmlogrdr_open(struct inode *, struct file *);
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int vmlogrdr_release(struct inode *, struct file *);
90d2c993d845781d160a7ef759a3e65c6892c4a270Heiko Carstensstatic ssize_t vmlogrdr_read (struct file *filp, char __user *data,
91d2c993d845781d160a7ef759a3e65c6892c4a270Heiko Carstens			      size_t count, loff_t * ppos);
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
93d54b1fdb1d9f82e375a299e22bd366aad52d4c34Arjan van de Venstatic const struct file_operations vmlogrdr_fops = {
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner   = THIS_MODULE,
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open    = vmlogrdr_open,
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release = vmlogrdr_release,
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read    = vmlogrdr_read,
986038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann	.llseek  = no_llseek,
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{
216bf2106ae114807772506e62cbf8a7d8e2c5403a1Joe Perches	static const 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;
250ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	int rc;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
252ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	onoff = ((action == 1) ? "ON" : "OFF");
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	qid_string = ((recording_class_AB == 1) ? " QID * " : "");
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
255ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	/*
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * The recording commands needs to be called with option QID
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * for guests that have previlege classes A or B.
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Purging has to be done as separate step, because recording
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * can't be switched on as long as records are on the queue.
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Doing both at the same time doesn't work.
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
262ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	if (purge && (action == 1)) {
263ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber		memset(cp_command, 0x00, sizeof(cp_command));
264ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber		memset(cp_response, 0x00, sizeof(cp_response));
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		snprintf(cp_command, sizeof(cp_command),
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 "RECORDING %s PURGE %s",
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 logptr->recording_name,
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 qid_string);
2696b979de395c7e1b7e59f74a870e1d1911853eccbChristian Borntraeger		cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cp_command, 0x00, sizeof(cp_command));
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cp_response, 0x00, sizeof(cp_response));
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	snprintf(cp_command, sizeof(cp_command), "RECORDING %s %s %s",
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		logptr->recording_name,
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		onoff,
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		qid_string);
2786b979de395c7e1b7e59f74a870e1d1911853eccbChristian Borntraeger	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The recording command will usually answer with 'Command complete'
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * on success, but when the specific service was never connected
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * before then there might be an additional informational message
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * 'HCPCRC8072I Recording entry not found' before the
283ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	 * 'Command complete'. So I use strstr rather then the strncmp.
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (strstr(cp_response,"Command complete"))
286ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber		rc = 0;
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
288ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber		rc = -EIO;
289ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	/*
290ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	 * If we turn recording off, we have to purge any remaining records
291ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	 * afterwards, as a large number of queued records may impact z/VM
292ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	 * performance.
293ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	 */
294ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	if (purge && (action == 0)) {
295ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber		memset(cp_command, 0x00, sizeof(cp_command));
296ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber		memset(cp_response, 0x00, sizeof(cp_response));
297ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber		snprintf(cp_command, sizeof(cp_command),
298ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber			 "RECORDING %s PURGE %s",
299ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber			 logptr->recording_name,
300ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber			 qid_string);
301ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber		cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
302ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	}
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
304ca768b663131ca644689fcadc9ca092dcc96a758Stefan Weinhuber	return rc;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
308c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_open (struct inode *inode, struct file *filp)
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int dev_num = 0;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct vmlogrdr_priv_t * logptr = NULL;
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int connect_rc = 0;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_num = iminor(inode);
3169784bd4f1a6ea736ad9bf241f5a965e0a2913a5eHeiko Carstens	if (dev_num >= MAXMINOR)
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr = &sys_ser[dev_num];
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * only allow for blocking reads to be open
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (filp->f_flags & O_NONBLOCK)
324745e967a49b26725cc9f30105ff67c8fee8926d6Heiko Carstens		return -EOPNOTSUPP;
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Besure this device hasn't already been opened */
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_bh(&logptr->priv_lock);
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (logptr->dev_in_use)	{
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_bh(&logptr->priv_lock);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
332c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->dev_in_use = 1;
333c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->connection_established = 0;
334c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->iucv_path_severed = 0;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_set(&logptr->receive_ready, 0);
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr->buffer_free = 1;
337c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	spin_unlock_bh(&logptr->priv_lock);
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* set the file options */
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	filp->private_data = logptr;
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* start recording for this service*/
343c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	if (logptr->autorecording) {
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = vmlogrdr_recording(logptr,1,logptr->autopurge);
345c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		if (ret)
3465466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky			pr_warning("vmlogrdr: failed to start "
3475466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky				   "recording automatically\n");
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* create connection to the system service */
351c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->path = iucv_path_alloc(10, 0, GFP_KERNEL);
352c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	if (!logptr->path)
353c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out_dev;
354c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	connect_rc = iucv_path_connect(logptr->path, &vmlogrdr_iucv_handler,
355c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				       logptr->system_service, NULL, NULL,
356c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				       logptr);
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (connect_rc) {
3585466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky		pr_err("vmlogrdr: iucv connection to %s "
3595466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky		       "failed with rc %i \n",
3605466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky		       logptr->system_service, connect_rc);
361c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out_path;
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We've issued the connect and now we must wait for a
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * ConnectionComplete or ConnectinSevered Interrupt
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * before we can continue to process.
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wait_event(conn_wait_queue, (logptr->connection_established)
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   || (logptr->iucv_path_severed));
370c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	if (logptr->iucv_path_severed)
371c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out_record;
3723b47f9d5ec646bc50148c664ce5895ff9837699fMartin Schwidefsky	nonseekable_open(inode, filp);
3733b47f9d5ec646bc50148c664ce5895ff9837699fMartin Schwidefsky	return 0;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
375c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_record:
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (logptr->autorecording)
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmlogrdr_recording(logptr,0,logptr->autopurge);
378c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_path:
379c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	kfree(logptr->path);	/* kfree(NULL) is ok. */
380c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	logptr->path = NULL;
381c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_dev:
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr->dev_in_use = 0;
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EIO;
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
387c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_release (struct inode *inode, struct file *filp)
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct vmlogrdr_priv_t * logptr = filp->private_data;
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
39366b494a7178cbd84d8fc0e5f1e92d81fb6ec9f6eUrsula Braun	iucv_path_sever(logptr->path, NULL);
39466b494a7178cbd84d8fc0e5f1e92d81fb6ec9f6eUrsula Braun	kfree(logptr->path);
39566b494a7178cbd84d8fc0e5f1e92d81fb6ec9f6eUrsula Braun	logptr->path = NULL;
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (logptr->autorecording) {
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ret)
3995466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky			pr_warning("vmlogrdr: failed to stop "
4005466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky				   "recording automatically\n");
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	logptr->dev_in_use = 0;
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
408c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_receive_data(struct vmlogrdr_priv_t *priv)
409c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc, *temp;
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* we need to keep track of two data sizes here:
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * The number of bytes we need to receive from iucv and
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * the total number of bytes we actually write into the buffer.
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int user_data_count, iucv_data_count;
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char * buffer;
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (atomic_read(&priv->receive_ready)) {
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_bh(&priv->priv_lock);
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (priv->residual_length){
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* receive second half of a record */
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			iucv_data_count = priv->residual_length;
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			user_data_count = 0;
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = priv->buffer;
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* receive a new record:
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * We need to return the total length of the record
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                         * + size of FENCE in the first 4 bytes of the buffer.
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		         */
430c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky			iucv_data_count = priv->local_interrupt_buffer.length;
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			user_data_count = sizeof(int);
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			temp = (int*)priv->buffer;
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			*temp= iucv_data_count + sizeof(FENCE);
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = priv->buffer + sizeof(int);
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
437025dfdafe77f20b3890981a394774baab7b9c827Frederik Schwarzer		 * If the record is bigger than our buffer, we receive only
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * a part of it. We can get the rest later.
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (iucv_data_count > NET_BUFFER_SIZE)
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			iucv_data_count = NET_BUFFER_SIZE;
442c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		rc = iucv_message_receive(priv->path,
443c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					  &priv->local_interrupt_buffer,
444c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					  0, buffer, iucv_data_count,
445c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					  &priv->residual_length);
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_bh(&priv->priv_lock);
447025dfdafe77f20b3890981a394774baab7b9c827Frederik Schwarzer		/* An rc of 5 indicates that the record was bigger than
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * the buffer, which is OK for us. A 9 indicates that the
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * record was purged befor we could receive it.
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc == 5)
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rc = 0;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc == 9)
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			atomic_set(&priv->receive_ready, 0);
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = 1;
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!rc) {
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->buffer_free = 0;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 		user_data_count += iucv_data_count;
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->current_position = priv->buffer;
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (priv->residual_length == 0){
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* the whole record has been captured,
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * now add the fence */
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			atomic_dec(&priv->receive_ready);
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			buffer = priv->buffer + user_data_count;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(buffer, FENCE, sizeof(FENCE));
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			user_data_count += sizeof(FENCE);
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->remaining = user_data_count;
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
477c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_read(struct file *filp, char __user *data,
478c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky			     size_t count, loff_t * ppos)
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct vmlogrdr_priv_t * priv = filp->private_data;
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (priv->buffer_free) {
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc = vmlogrdr_receive_data(priv);
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc) {
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rc = wait_event_interruptible(read_wait_queue,
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					atomic_read(&priv->receive_ready));
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (rc)
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return rc;
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* copy only up to end of record */
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count > priv->remaining)
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count = priv->remaining;
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (copy_to_user(data, priv->current_position, count))
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*ppos += count;
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->current_position += count;
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->remaining -= count;
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* if all data has been transferred, set buffer free */
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (priv->remaining == 0)
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->buffer_free = 1;
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
510c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_autopurge_store(struct device * dev,
511c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					struct device_attribute *attr,
512c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					const char * buf, size_t count)
513c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
514dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ssize_t ret = count;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (buf[0]) {
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '0':
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->autopurge=0;
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '1':
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->autopurge=1;
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EINVAL;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_autopurge_show(struct device *dev,
532c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				       struct device_attribute *attr,
533c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				       char *buf)
534c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
535dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return sprintf(buf, "%u\n", priv->autopurge);
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEVICE_ATTR(autopurge, 0644, vmlogrdr_autopurge_show,
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   vmlogrdr_autopurge_store);
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
544c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_purge_store(struct device * dev,
545c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				    struct device_attribute *attr,
546c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky				    const char * buf, size_t count)
547c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char cp_command[80];
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char cp_response[80];
551dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (buf[0] != '1')
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cp_command, 0x00, sizeof(cp_command));
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cp_response, 0x00, sizeof(cp_response));
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        /*
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * The recording command needs to be called with option QID
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * for guests that have previlege classes A or B.
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Other guests will not recognize the command and we have to
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * issue the same command without the QID parameter.
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (recording_class_AB)
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		snprintf(cp_command, sizeof(cp_command),
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 "RECORDING %s PURGE QID * ",
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 priv->recording_name);
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		snprintf(cp_command, sizeof(cp_command),
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 "RECORDING %s PURGE ",
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 priv->recording_name);
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5756b979de395c7e1b7e59f74a870e1d1911853eccbChristian Borntraeger	cpcmd(cp_command, cp_response, sizeof(cp_response), NULL);
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return count;
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEVICE_ATTR(purge, 0200, NULL, vmlogrdr_purge_store);
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
584c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_autorecording_store(struct device *dev,
585c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					    struct device_attribute *attr,
586c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					    const char *buf, size_t count)
587c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
588dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ssize_t ret = count;
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (buf[0]) {
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '0':
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->autorecording=0;
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '1':
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->autorecording=1;
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EINVAL;
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
605c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_autorecording_show(struct device *dev,
606c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					   struct device_attribute *attr,
607c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					   char *buf)
608c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
609dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return sprintf(buf, "%u\n", priv->autorecording);
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEVICE_ATTR(autorecording, 0644, vmlogrdr_autorecording_show,
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   vmlogrdr_autorecording_store);
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
618c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_recording_store(struct device * dev,
619c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					struct device_attribute *attr,
620c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					const char * buf, size_t count)
621c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
622dff59b64af94dc588044d70f3708cb835055c5b6Greg Kroah-Hartman	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ssize_t ret;
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (buf[0]) {
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '0':
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = vmlogrdr_recording(priv,0,0);
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case '1':
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = vmlogrdr_recording(priv,1,0);
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EINVAL;
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret)
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return count;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEVICE_ATTR(recording, 0200, NULL, vmlogrdr_recording_store);
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
646c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic ssize_t vmlogrdr_recording_status_show(struct device_driver *driver,
647c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky					      char *buf)
648c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
650bf2106ae114807772506e62cbf8a7d8e2c5403a1Joe Perches	static const char cp_command[] = "QUERY RECORDING ";
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int len;
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6536b979de395c7e1b7e59f74a870e1d1911853eccbChristian Borntraeger	cpcmd(cp_command, buf, 4096, NULL);
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len = strlen(buf);
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return len;
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DRIVER_ATTR(recording_status, 0444, vmlogrdr_recording_status_show,
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   NULL);
65972f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ottstatic struct attribute *vmlogrdr_drv_attrs[] = {
66072f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ott	&driver_attr_recording_status.attr,
66172f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ott	NULL,
66272f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ott};
66372f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ottstatic struct attribute_group vmlogrdr_drv_attr_group = {
66472f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ott	.attrs = vmlogrdr_drv_attrs,
66572f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ott};
66672f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ottstatic const struct attribute_group *vmlogrdr_drv_attr_groups[] = {
66772f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ott	&vmlogrdr_drv_attr_group,
66872f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ott	NULL,
66972f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ott};
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct attribute *vmlogrdr_attrs[] = {
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	&dev_attr_autopurge.attr,
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	&dev_attr_purge.attr,
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	&dev_attr_autorecording.attr,
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	&dev_attr_recording.attr,
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	NULL,
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
67876e0377b2450af577077c7e4e00ae6da738d1884Sebastian Ottstatic struct attribute_group vmlogrdr_attr_group = {
67976e0377b2450af577077c7e4e00ae6da738d1884Sebastian Ott	.attrs = vmlogrdr_attrs,
68076e0377b2450af577077c7e4e00ae6da738d1884Sebastian Ott};
68176e0377b2450af577077c7e4e00ae6da738d1884Sebastian Ottstatic const struct attribute_group *vmlogrdr_attr_groups[] = {
68276e0377b2450af577077c7e4e00ae6da738d1884Sebastian Ott	&vmlogrdr_attr_group,
68376e0377b2450af577077c7e4e00ae6da738d1884Sebastian Ott	NULL,
68476e0377b2450af577077c7e4e00ae6da738d1884Sebastian Ott};
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6869f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuberstatic int vmlogrdr_pm_prepare(struct device *dev)
6879f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber{
6889f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	int rc;
6894f0076f77fb64889d4e5e425b63333e5764b446dMartin Schwidefsky	struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev);
6909f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber
6919f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	rc = 0;
6929f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	if (priv) {
6939f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber		spin_lock_bh(&priv->priv_lock);
6949f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber		if (priv->dev_in_use)
6959f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber			rc = -EBUSY;
6969f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber		spin_unlock_bh(&priv->priv_lock);
6979f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	}
6989f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	if (rc)
6999f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber		pr_err("vmlogrdr: device %s is busy. Refuse to suspend.\n",
7009f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber		       dev_name(dev));
7019f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	return rc;
7029f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber}
7039f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber
7049f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber
705471452104b8520337ae2fb48c4e61cd4896e025dAlexey Dobriyanstatic const struct dev_pm_ops vmlogrdr_pm_ops = {
7069f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	.prepare = vmlogrdr_pm_prepare,
7079f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber};
7089f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber
70956b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartmanstatic struct class *vmlogrdr_class;
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct device_driver vmlogrdr_driver = {
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name = "vmlogrdr",
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.bus  = &iucv_bus,
7139f62fa1618987e9fadb2eef86d35ef168ddf5a1fStefan Weinhuber	.pm = &vmlogrdr_pm_ops,
71472f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ott	.groups = vmlogrdr_drv_attr_groups,
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
717c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_register_driver(void)
718c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	/* Register with iucv driver */
722c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	ret = iucv_register(&vmlogrdr_iucv_handler, 1);
7232f6f2521bcfff108e02d97e3a326f995996ce95fMartin Schwidefsky	if (ret)
724c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out;
725c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = driver_register(&vmlogrdr_driver);
7272f6f2521bcfff108e02d97e3a326f995996ce95fMartin Schwidefsky	if (ret)
728c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		goto out_iucv;
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
73056b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartman	vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr");
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IS_ERR(vmlogrdr_class)) {
732c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		ret = PTR_ERR(vmlogrdr_class);
733c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky		vmlogrdr_class = NULL;
73472f6e3a8bc956fddf6e004ae9b804977d1458e77Sebastian Ott		goto out_driver;
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
738c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_driver:
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver_unregister(&vmlogrdr_driver);
740c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout_iucv:
741c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	iucv_unregister(&vmlogrdr_iucv_handler, 1);
742c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskyout:
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
747c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic void vmlogrdr_unregister_driver(void)
748c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
74956b2293595b2eb52cc2aa2baf92c6cfa8265f9d5Greg Kroah-Hartman	class_destroy(vmlogrdr_class);
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_class = NULL;
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	driver_unregister(&vmlogrdr_driver);
752c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky	iucv_unregister(&vmlogrdr_iucv_handler, 1);
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
756c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
757c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct device *dev;
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
76188abaab4f9b08381e30e737980a1c49d6b524dfcEric Sesterhenn	dev = kzalloc(sizeof(struct device), GFP_KERNEL);
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev) {
763ef283688f54cc87c421f2a05ddf75fded6f4ad86Kees Cook		dev_set_name(dev, "%s", priv->internal_name);
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->bus = &iucv_bus;
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->parent = iucv_root;
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->driver = &vmlogrdr_driver;
76776e0377b2450af577077c7e4e00ae6da738d1884Sebastian Ott		dev->groups = vmlogrdr_attr_groups;
7684f0076f77fb64889d4e5e425b63333e5764b446dMartin Schwidefsky		dev_set_drvdata(dev, priv);
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * The release function could be called after the
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * module has been unloaded. It's _only_ task is to
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * free the struct. Therefore, we specify kfree()
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * directly here. (Probably a little bit obfuscating
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * but legitime ...).
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->release = (void (*)(struct device *))kfree;
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = device_register(dev);
780c6304933274f8e3cc6983d496456757ac8ab2e0bSebastian Ott	if (ret) {
781c6304933274f8e3cc6983d496456757ac8ab2e0bSebastian Ott		put_device(dev);
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
783c6304933274f8e3cc6983d496456757ac8ab2e0bSebastian Ott	}
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
785ea9e42f627a45f979b4977009724eb114406e3c7Greg Kroah-Hartman	priv->class_device = device_create(vmlogrdr_class, dev,
786ea9e42f627a45f979b4977009724eb114406e3c7Greg Kroah-Hartman					   MKDEV(vmlogrdr_major,
787ea9e42f627a45f979b4977009724eb114406e3c7Greg Kroah-Hartman						 priv->minor_num),
788ea9e42f627a45f979b4977009724eb114406e3c7Greg Kroah-Hartman					   priv, "%s", dev_name(dev));
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (IS_ERR(priv->class_device)) {
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = PTR_ERR(priv->class_device);
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->class_device=NULL;
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		device_unregister(dev);
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	priv->device = dev;
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
800c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv)
801c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
8027f021ce1957504cacc78896de857b90293badabcCornelia Huck	device_destroy(vmlogrdr_class, MKDEV(vmlogrdr_major, priv->minor_num));
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (priv->device != NULL) {
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		device_unregister(priv->device);
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		priv->device=NULL;
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic int vmlogrdr_register_cdev(dev_t dev)
812c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = 0;
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cdev = cdev_alloc();
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!vmlogrdr_cdev) {
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cdev->owner = THIS_MODULE;
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cdev->ops = &vmlogrdr_fops;
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cdev->dev = dev;
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = cdev_add(vmlogrdr_cdev, vmlogrdr_cdev->dev, MAXMINOR);
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!rc)
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// cleanup: cdev is not fully registered, no cdev_del here!
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kobject_put(&vmlogrdr_cdev->kobj);
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cdev=NULL;
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
832c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefskystatic void vmlogrdr_cleanup(void)
833c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky{
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        int i;
835c9101c5b3f1d018efa36d12cdcde89955642c73dMartin Schwidefsky
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vmlogrdr_cdev) {
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cdev_del(vmlogrdr_cdev);
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmlogrdr_cdev=NULL;
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i < MAXMINOR; ++i ) {
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmlogrdr_unregister_device(&sys_ser[i]);
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_page((unsigned long)sys_ser[i].buffer);
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_unregister_driver();
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vmlogrdr_major) {
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unregister_chrdev_region(MKDEV(vmlogrdr_major, 0), MAXMINOR);
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmlogrdr_major=0;
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
852f60d89108f3481ca11672b82cf7e67171e050ce4Christian Borntraegerstatic int __init vmlogrdr_init(void)
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_t dev;
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (! MACHINE_IS_VM) {
8595466c2e43e78068cfe634b07fe36dd7a6a7af535Martin Schwidefsky		pr_err("not running under VM, driver not loaded.\n");
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        recording_class_AB = vmlogrdr_get_recording_class_AB();
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = alloc_chrdev_region(&dev, 0, MAXMINOR, "vmlogrdr");
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return rc;
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_major = MAJOR(dev);
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc=vmlogrdr_register_driver();
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto cleanup;
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i < MAXMINOR; ++i ) {
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!sys_ser[i].buffer) {
8773cb2cea15e707dd030b3293d6d08183da369d291Marcin Slusarz			rc = -ENOMEM;
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sys_ser[i].current_position = sys_ser[i].buffer;
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rc=vmlogrdr_register_device(&sys_ser[i]);
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (rc)
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto cleanup;
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = vmlogrdr_register_cdev(dev);
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto cleanup;
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldscleanup:
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cleanup();
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
899f60d89108f3481ca11672b82cf7e67171e050ce4Christian Borntraegerstatic void __exit vmlogrdr_exit(void)
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmlogrdr_cleanup();
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(vmlogrdr_init);
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(vmlogrdr_exit);
908