[kernel] r5834 - in dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390: . debian

Dann Frazier dannf at costa.debian.org
Sun Feb 12 19:58:21 UTC 2006


Author: dannf
Date: Sun Feb 12 19:57:42 2006
New Revision: 5834

Modified:
   dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/debian/changelog
   dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/linux-2.4.27-s390.diff
Log:
* Build fix for 206_s390-sacf-fix.diff (CAN-2004-0887) from new
  kernel-tree.

Modified: dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/debian/changelog
==============================================================================
--- dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/debian/changelog	(original)
+++ dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/debian/changelog	Sun Feb 12 19:57:42 2006
@@ -3,8 +3,10 @@
   * Non-maintainer upload by the Security Team
   * Use kernel-tree-2.4.27-10sarge2
   * Regenerate linux-2.4.27-s390.diff to apply to updated source tree
+  * Build fix for 206_s390-sacf-fix.diff (CAN-2004-0887) from new
+    kernel-tree.
 
- -- dann frazier <dannf at debian.org>  Mon, 30 Jan 2006 22:51:47 -0700
+ -- dann frazier <dannf at debian.org>  Sun, 12 Feb 2006 12:54:44 -0700
 
 kernel-patch-2.4.27-s390 (2.4.27-2) unstable; urgency=high
 

Modified: dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/linux-2.4.27-s390.diff
==============================================================================
--- dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/linux-2.4.27-s390.diff	(original)
+++ dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/linux-2.4.27-s390.diff	Sun Feb 12 19:57:42 2006
@@ -1,1486 +1,660 @@
-diff -urN kernel-source-2.4.27-2.4.27.orig/Documentation/Configure.help Documentation/Configure.help
---- kernel-source-2.4.27-2.4.27.orig/Documentation/Configure.help	2006-01-30 22:23:45.000000000 -0700
-+++ Documentation/Configure.help	2006-01-30 22:25:24.000000000 -0700
-@@ -6385,6 +6385,16 @@
- 
-   See <file:Documentation/networking/ip-sysctl.txt> for details.
- 
-+Prepare net_device struct for shared IPv6 cards
-+CONFIG_SHARED_IPV6_CARDS
-+  This prepares the net_device structure to contain a card user instance
-+  id. On some systems, e.g. IBM zSeries, networking cards can be shared.
-+  In order to make IPv6 autoconfiguration useful, each user of the
-+  networking card will get a different id which is used for unique
-+  address generation (the id is used in the EUI-64 generation).
+diff -urN kernel-source-2.4.27.orig/arch/s390/appldata/appldata_base.c arch/s390/appldata/appldata_base.c
+--- kernel-source-2.4.27.orig/arch/s390/appldata/appldata_base.c	1969-12-31 17:00:00.000000000 -0700
++++ arch/s390/appldata/appldata_base.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,650 @@
++/*
++ * arch/s390/appldata/appldata_base.c
++ *
++ * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
++ * Exports appldata_register_ops() and appldata_unregister_ops() for the
++ * data gathering modules.
++ *
++ * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH.
++ *
++ * Author: Gerald Schaefer <geraldsc at de.ibm.com>
++ */
++ 
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/errno.h> 
++#include <asm/uaccess.h>
++#include <asm/io.h>
++#include <linux/interrupt.h>
++#include <linux/proc_fs.h>
++#include <asm/timer.h>
++#include <linux/sysctl.h>
 +
-+  Only say yes on IBM zSeries or S/390 systems.
++#include "appldata.h"
 +
- The SCTP Protocol (EXPERIMENTAL)
- CONFIG_IP_SCTP
-   Stream Control Transmission Protocol
-@@ -8070,7 +8080,7 @@
- QDIO base support for IBM S/390 and zSeries
- CONFIG_QDIO
-   This driver provides the Queued Direct I/O base support for the
--  IBM S/390 (G5 and G6) and eServer zSeries (z800 and z900).
-+  IBM S/390 (G5 and G6) and eServer zSeries (z800, z900 and z990).
- 
-   For details please refer to the documentation provided by IBM at
-   <http://www10.software.ibm.com/developerworks/opensource/linux390>
-@@ -8088,6 +8098,61 @@
- 
-   If unsure, say N.
- 
-+IBM S/390 and zSeries OSA-Express and HiperSockets device driver
-+CONFIG_QETH
-+  This driver supports the IBM S/390 and zSeries OSA Express adapters
-+  in QDIO mode (all media types), HiperSockets interfaces and VM GuestLAN
-+  interfaces in QDIO and HIPER mode.
 +
-+  For details please refer to the documentation provided by IBM at
-+  <http://www10.software.ibm.com/developerworks/opensource/linux390>
++#define MY_PRINT_NAME       "appldata"		/* for debug messages, etc. */
++#define APPLDATA_INTERVAL   60			/* default monitoring 
++						   interval in seconds */
++#define VIRTUAL_SECOND	    0x0F4240000		/* nr. of TOD clock units
++						   for one second */
++#ifndef CONFIG_ARCH_S390X
 +
-+  This driver is also available as a module (code which can be
-+  inserted in and removed from the running kernel whenever you
-+  want). If you want to compile it as a module, say 'M' here and
-+  read file Documentation/modules.txt.
++#define APPLDATA_START_INTERVAL_REC 0x00   	/* Function codes for */
++#define APPLDATA_STOP_REC	    0x01	/* DIAG 0xDC	  */
++#define APPLDATA_GEN_EVENT_RECORD   0x02
++#define APPLDATA_START_CONFIG_REC   0x03
 +
-+IPv6 support for qeth
-+CONFIG_QETH_IPV6
-+  If CONFIG_QETH is switched on, this option will include IPv6
-+  support in the qeth device driver.
++#else
 +
-+IEEE 802.1q VLAN support for qeth
-+CONFIG_QETH_VLAN
-+  If CONFIG_QETH is switched on, this option will include IEEE
-+  802.1q VLAN support in the qeth device driver.
++#define APPLDATA_START_INTERVAL_REC 0x80
++#define APPLDATA_STOP_REC   	    0x81
++#define APPLDATA_GEN_EVENT_RECORD   0x82
++#define APPLDATA_START_CONFIG_REC   0x83
 +
-+Performance statistics for the qeth drivers
-+CONFIG_QETH_PERF_STATS
-+  When switched on, this option will add a file in the proc-fs
-+  (/proc/qeth_perf_stats) containing performance statistics. It
-+  may slightly impact performance, so this is only recommended for
-+  internal tuning of the device driver.
++#endif /* CONFIG_ARCH_S390X */
 +
-+FCP adapter driver for IBM eServer zSeries
-+CONFIG_ZFCP
-+  This driver supports the IBM eServer zSeries 800/900 FCP adapter.
-+  If you want to access SCSI devices attached to your zSeries
-+  by means of Fibre Channel interfaces say Y.
-+  For details please refer to the documentation provided by IBM at
-+  <http://www10.software.ibm.com/developerworks/opensource/linux390>
 +
-+  This driver is also available as a module ( = code which can be
-+  inserted in and removed from the running kernel whenever you want).
-+  The module will be called zfcp.o. If you want to compile it as a
-+  module, say M here and read <file:Documentation/modules.txt>.
++/*
++ * Parameter list for DIAGNOSE X'DC'
++ */
++#ifndef CONFIG_ARCH_S390X
++struct appldata_parameter_list {
++	u16 diag;		/* The DIAGNOSE code X'00DC'          */
++	u8  function;		/* The function code for the DIAGNOSE */
++	u8  parlist_length;	/* Length of the parameter list       */
++	u32 product_id_addr;	/* Address of the 16-byte product ID  */
++	u16 reserved;
++	u16 buffer_length;	/* Length of the application data buffer  */
++	u32 buffer_addr;	/* Address of the application data buffer */
++};
++#else
++struct appldata_parameter_list {
++	u16 diag;
++	u8  function;
++	u8  parlist_length;
++	u32 unused01;
++	u16 reserved;
++	u16 buffer_length;
++	u32 unused02;
++	u64 product_id_addr;
++	u64 buffer_addr;
++};
++#endif /* CONFIG_ARCH_S390X */
 +
-+HBA API support for the IBM eServer z990 (GA2) FCP adapter driver
-+CONFIG_ZFCP_HBAAPI
-+  Say Y here to include HBA API (FC-HBA) support for z990 (GA2).
++/*
++ * /proc entries (sysctl) 
++ */
++static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
++static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
++		   			void *buffer, size_t *lenp);
++static int appldata_interval_handler(ctl_table *ctl, int write, struct file *filp,
++					   void *buffer, size_t *lenp);
 +
-+  This support is also available as a separate module.
-+  If you want to compile it as a module, say M here and read
-+  <file:Documentation/modules.txt>.  The module will be called
-+  zfcp_hbaapi.o.
++static struct ctl_table_header *appldata_sysctl_header;
++static struct ctl_table appldata_table[] = {
++	{
++		.ctl_name	= CTL_APPLDATA_TIMER,
++		.procname	= "timer",
++		.mode		= S_IRUGO | S_IWUSR,
++		.proc_handler	= &appldata_timer_handler,
++	},
++	{
++		.ctl_name	= CTL_APPLDATA_INTERVAL,
++		.procname	= "interval",
++		.mode		= S_IRUGO | S_IWUSR,
++		.proc_handler	= &appldata_interval_handler,
++	},
++	{ .ctl_name = 0 }
++};
 +
-+  If unsure, say N.
++static struct ctl_table appldata_dir_table[] = {
++	{
++		.ctl_name	= CTL_APPLDATA,
++		.procname	= appldata_proc_name,
++		.maxlen		= 0,
++		.mode		= S_IRUGO | S_IXUGO,
++		.child		= appldata_table,
++	},
++	{ .ctl_name = 0 }
++};
 +
- SGI WD93C93 SCSI Driver
- CONFIG_SCSI_SGIWD93
-   Say Y here to support the on-board WD93C93 SCSI controller found (a)
-@@ -25422,6 +25487,49 @@
-   You should only select this option if you know what you are
-   doing and want to exploit this feature.
- 
-+CONFIG_VIRT_TIMER
-+  This provides a kernel interface for virtual CPU timers. 
++/*
++ * Timer
++ */
++static spinlock_t appldata_timer_lock = SPIN_LOCK_UNLOCKED;
++static struct vtimer_list appldata_timer[NR_CPUS];
++static int appldata_interval = APPLDATA_INTERVAL;
++static int appldata_timer_active;
++static atomic_t appldata_expire_count = ATOMIC_INIT(0);
++static struct appldata_mod_vtimer_args {
++	struct vtimer_list *timer;
++	u64    expires;
++} appldata_mod_vtimer_args;
 +
-+CONFIG_APPLDATA_BASE
-+  This option provides a kernel interface for creating and updating
-+  z/VM APPLDATA monitor records. The monitor records are updated at
-+  given time intervals, once the timer is started.
++/*
++ * Tasklet
++ */
++static struct tasklet_struct appldata_tasklet_struct;
 +
-+CONFIG_APPLDATA_MEM
-+  This option provides memory management related data to the Linux -
-+  z/VM Monitor Stream, for example, the paging/swapping rate and the
-+  utilisation.
++/*
++ * Hook list
++ */
++static spinlock_t appldata_ops_lock = SPIN_LOCK_UNLOCKED;
++static LIST_HEAD(appldata_ops_list);
 +
-+CONFIG_APPLDATA_OS
-+  This option provides operating system related data to the Linux -
-+  z/VM Monitor Stream, for example, the CPU utilisation.
 +
-+CONFIG_APPLDATA_NET_SUM
-+  This option provides network related data to the Linux - z/VM
-+  Monitor Stream. The data gives a total sum of network I/O
-+  statistics, no per-interface data.
++/************************* timer, tasklet, DIAG ******************************/
++/*
++ * appldata_timer_function()
++ *
++ * schedule tasklet and reschedule timer
++ */
++static void appldata_timer_function(unsigned long data, struct pt_regs *regs)
++{
++	P_DEBUG("   -= Timer =-\n");
++	P_DEBUG("CPU: %i, expire: %i\n", smp_processor_id(),
++		atomic_read(&appldata_expire_count));
++	if (atomic_dec_and_test(&appldata_expire_count)) {
++		atomic_set(&appldata_expire_count, smp_num_cpus);
++		tasklet_schedule((struct tasklet_struct *) data);
++	}
++}
 +
-+Collaborative memory management
-+CONFIG_CMM
-+  Select this option, if you want to enable the kernel interface
-+  to reduce the memory size of the system. This is accomplished
-+  by allocating pages of memory and put them "on hold". This only
-+  makes sense for a system running under VM where the unused pages
-+  will be reused by VM for other guest systems. The interface
-+  allows an external monitor to balance memory of many systems.
-+  Everybody who wants to run Linux under VM should select this
-+  option.
-+		
-+/proc interface to cooperative memory management
-+CONFIG_CMM_PROC
-+  Select this option to enable the /proc interface to the
-+  cooperative memory management. 
++/*
++ * appldata_tasklet_function()
++ *
++ * call data gathering function for each (active) module
++ */
++static void appldata_tasklet_function(unsigned long data)
++{
++	struct list_head *lh;
++	struct appldata_ops *ops;
++	int i;
 +
-+IUCV special message interface to cooperative memory management
-+CONFIG_CMM_IUCV
-+  Select this option to enable the special message interface to
-+  the cooperative memory management. 
++	P_DEBUG("  -= Tasklet =-\n");
++	i = 0;
++	spin_lock(&appldata_ops_lock);
++	list_for_each(lh, &appldata_ops_list) {
++		ops = list_entry(lh, struct appldata_ops, list);
++		P_DEBUG("list_for_each loop: %i) active = %u, name = %s\n",
++			++i, ops->active, ops->name);
++		if (ops->active == 1) {
++			ops->callback(ops->data);
++		}
++	}
++	spin_unlock(&appldata_ops_lock);
++}
 +
- Support for IBM-style disk-labels (S/390)
- CONFIG_S390_PARTITION
-   Enable this option to assure standard IBM labels on the DASDs.
-@@ -25432,13 +25540,13 @@
- Support for DASD hard disks
- CONFIG_DASD
-   Enable this option if you want to access DASDs directly utilizing
--  S/390s channel subsystem commands. This is necessary for running
-+  S/390's or zSeries' channel subsystem commands. This is necessary for running
-   natively on a single image or an LPAR.
- 
- Support for ECKD hard disks
- CONFIG_DASD_ECKD
-   ECKD (Extended Count Key Data) devices are the most commonly used 
--  devices on S/390s. You should enable this option unless you are 
-+  devices on zSeries and S/390. You should enable this option unless you are 
-   very sure you have no ECKD device.
- 
- ECKD demand loading
-@@ -25464,6 +25572,14 @@
- CONFIG_DASD_AUTO_DIAG
-   This option enables demand loading of the DIAG module. 
- 
-+Support for Channel Measurement on DASD devices
-+CONFIG_S390_CMF
-+  Select this option if you want to run applications that read
-+  statistical data about DASD I/O from the Channel Measurement
-+  Facility.
-+  If you say "M" here, two modules, "dasd_cmb.o" and "cmf.o",
-+  will be created. If unsure, say "N".
++/*
++ * appldata_mod_vtimer_wrap()
++ *
++ * wrapper function for mod_virt_timer(), because smp_call_function_on()
++ * accepts only one parameter.
++ */
++static void appldata_mod_vtimer_wrap(struct appldata_mod_vtimer_args *args) {
++	mod_virt_timer(args->timer, args->expires);
++}
 +
- Merge some code into the kernel to make the image IPLable
- CONFIG_IPLABLE
-   If you want to use the produced kernel to IPL directly from a
-@@ -25491,45 +25607,49 @@
-   system console.  Available only if 3270 support is compiled in
-   statically.
- 
--Support for HWC line mode terminal
--CONFIG_HWC
--  Include support for IBM HWC line-mode terminals.
--
--Console on HWC line mode terminal
--CONFIG_HWC_CONSOLE
--  Include support for using an IBM HWC line-mode terminal as the Linux
-+Support for SCLP
-+CONFIG_SCLP
-+  Include support for the IBM SCLP interface to the service element. 
++/*
++ * appldata_diag()
++ *
++ * prepare parameter list, issue DIAG 0xDC
++ */
++static int appldata_diag(char record_nr, u16 function, unsigned long buffer,
++			u16 length)
++{
++	unsigned long ry;
++	struct appldata_product_id {
++		char prod_nr[7];			/* product nr. */
++		char prod_fn[2];			/* product function */
++		char record_nr;				/* record nr. */
++		char version_nr[2];			/* version */
++		char release_nr[2];			/* release */
++		char mod_lvl[2];			/* modification lvl. */
++	} appldata_product_id = {
++	/* all strings are EBCDIC, record_nr is byte */
++		.prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4,
++				0xE7, 0xD2, 0xD9},	/* "LINUXKR" */
++		.prod_fn    = {0xD5, 0xD3},		/* "NL" */
++		.record_nr  = record_nr,
++		.version_nr = {0xF2, 0xF4},		/* "24" */
++		.release_nr = {0xF0, 0xF1},		/* "01" */
++		.mod_lvl    = {0xF0, 0xF0},		/* "00" */
++	};
++	struct appldata_parameter_list appldata_parameter_list = {
++				.diag = 0xDC,
++				.function = function,
++				.parlist_length = 
++					sizeof(appldata_parameter_list),
++				.buffer_length = length,
++				.product_id_addr = 
++					(unsigned long) &appldata_product_id,
++				.buffer_addr = virt_to_phys((void *) buffer)
++	};
 +
-+Support for SCLP line mode terminal
-+CONFIG_SCLP_TTY
-+  Include support for IBM SCLP line-mode terminals.
++        if (!MACHINE_IS_VM)
++                return -ENOSYS;
++	ry = -1;
++	asm volatile(
++			"diag %1,%0,0xDC\n\t"
++			: "=d" (ry) : "d" (&(appldata_parameter_list)) : "cc");
++	return (int) ry;
++}
++/********************** timer, tasklet, DIAG <END> ***************************/
 +
-+Support for console on SCLP line mode terminal
-+CONFIG_SCLP_CONSOLE
-+  Include support for using an IBM SCLP line-mode terminal as a Linux
-   system console.
- 
--Control Program Identification
--CONFIG_HWC_CPI
--  Allows for Control Program Identification via the HWC interface,
--  i.e. provides a mean to pass an OS instance name (system name)
--  to the machine.
--
--  This option should only be selected as a module since the
--  system name has to be passed as module parameter. The module
--  will be called hwc_cpi.o.
-+Support for SCLP VT220-compatible terminal
-+CONFIG_SCLP_VT220_TTY
-+  Include support for an IBM SCLP VT220-compatible terminal.
 +
-+Support for console on SCLP VT220-compatible terminal
-+CONFIG_SCLP_VT220_CONSOLE
-+  Include support for using an IBM SCLP VT220-compatible terminal as a Linux
-+  system console.
++/****************************** /proc stuff **********************************/
++/*
++ * appldata_timer_handler()
++ * 
++ * Start/Stop timer, show status of timer (0 = not active, 1 = active)
++ */
++static int
++appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
++			   void *buffer, size_t *lenp)
++{
++	int len, i;
++	u64 per_cpu_interval;
++	char buf[2];
 +
-+Control-Program Identification
-+CONFIG_SCLP_CPI
-+  This option enables the hardware console interface for system
-+  identification. This is commonly used for workload management and
-+  gives you a nice name for the system on the service element.
-+  Please select this option as a module since built-in operation is
-+  completely untested.
-+  You should only select this option if you know what you are doing,
-+  need this feature and intend to run your kernel in LPAR.
- 
- S/390 tape device support
- CONFIG_S390_TAPE
-   Select this option if you want to access channel-attached tape
-   devices on IBM S/390 or zSeries.
--  If you select this option you will also want to select at
--  least one of the tape interface options and one of the tape
--  hardware options in order to access a tape device.
-+  If you select this option you will also want to select at least
-+  one of the hardware options in order to access a tape device.
-   This option is also available as a module. The module will be
-   called tape390.o and include all selected interfaces.
-   The hardware drivers will be seperate modules.
-   If unsure, say "Y".
- 
--Support for tape character devices
--CONFIG_S390_TAPE_CHAR
--  Select this option if you want to access your channel-attached
--  tape devices using the character device interface.
--  This interface is similar to other Linux tape devices like
--  SCSI-Tapes (st) and the floppy tape device (ftape).
--  If unsure, say "Y".
--
- Support for tape block devices
- CONFIG_S390_TAPE_BLOCK
-   Select this option if you want to access your channel-attached tape
-@@ -25539,22 +25659,12 @@
-   Documentation/s390/TAPE for further information about creating
-   volumes for and using this interface.  It is safe to say "Y" here.
- 
--Support for 3490 tape hardware
--CONFIG_S390_TAPE_3490
--  Select this option if you want to access IBM 3490 magnetic
-+Support for 3480/3490 tape hardware
-+CONFIG_S390_TAPE_34XX
-+  Select this option if you want to access IBM 3480/3490 magnetic
-   tape subsystems and 100% compatibles.
-   This option is also available as a module. The module will be
--  called tape3490.o. If CONFIG_S390_TAPE is selected as a module,
--  this hardware driver cannot be built-in but is only available
--  as a module.
--  It is safe to say "Y" here.
--
--Support for 3480 tape hardware
--CONFIG_S390_TAPE_3480
--  Select this option if you want to access IBM 3480 magnetic
--  tape subsystems and 100% compatibles.
--  This option is also available as a module. The module will be
--  called tape3480.o. If CONFIG_S390_TAPE is selected as a module,
-+  called tape_34xx.o. If CONFIG_S390_TAPE is selected as a module,
-   this hardware driver cannot be built-in but is only available
-   as a module.
-   It is safe to say "Y" here.
-@@ -25574,7 +25684,11 @@
-   or zSeries as a disk.  This is useful as a _fast_ swap device if you
-   want to access more than 2G of memory when running in 31 bit mode.
-   This option is also available as a module which will be called
--  xpram.o.  If unsure, say "N".
-+  xpram.o.  If unsure, say M.
++	if (!*lenp || filp->f_pos) {
++		*lenp = 0;
++		return 0;
++	}
++	if (!write) {
++		len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n");
++		if (len > *lenp)
++			len = *lenp;
++		if (copy_to_user(buffer, buf, len))
++			return -EFAULT;
++		goto out;
++	}
++	
++	len = *lenp;
++	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
++		return -EFAULT;
 +
-+CONFIG_DCSSBLK
-+  A block device driver for DCSS segments. It can be used to create
-+  a filesystem in such a segment.
- 
- Fast IRQ handling
- CONFIG_FAST_IRQ
-@@ -25584,11 +25698,51 @@
-   interrupts which will also be processed before leaving the interrupt
-   context.  This speeds up the I/O a lot. Say "Y".
- 
--IUCV device support (VM only)
-+IUCV support (VM only)
- CONFIG_IUCV
-   Select this option if you want to use inter-user communication
--  vehicle networking under VM or VIF.  This option is also available
--  as a module which will be called iucv.o. If unsure, say "Y".
-+  under VM or VIF. If unsure, say "Y" to enable a fast communication
-+  link between VM guests. At boot time the user ID of the guest needs
-+  to be passed to the kernel. Note that both kernels need to be
-+  compiled with this option and both need to be booted with the user ID
-+  of the other VM guest.
++	per_cpu_interval = (u64) (appldata_interval / smp_num_cpus) *
++				VIRTUAL_SECOND;
++	spin_lock(&appldata_timer_lock);
++	if ((buf[0] == '1') && (!appldata_timer_active)) {
++		for (i = 0; i < smp_num_cpus; i++) {
++			appldata_timer[i].expires = per_cpu_interval;
++			smp_call_function_on(add_virt_timer_periodic,
++						&appldata_timer[i], 0, 1, i);
++		}
++		appldata_timer_active = 1;
++		P_STATUS("Monitoring timer started.\n");
++	} else if ((buf[0] == '0') && (appldata_timer_active)) {
++		for (i = 0; i < smp_num_cpus; i++) {
++			smp_call_function_on((void *) del_virt_timer,
++						&appldata_timer[i],
++						0, 1, i);
++		}
++		appldata_timer_active = 0;
++		P_STATUS("Monitoring timer stopped.\n");
++	}
++	spin_unlock(&appldata_timer_lock);
++out:
++	*lenp = len;
++	filp->f_pos += len;
++	return 0;
++}
 +
-+IUCV network device support (VM only)
-+CONFIG_NETIUCV
-+  Select this option if you want to use inter-user communication
-+  vehicle networking under VM or VIF. It enables a fast communication
-+  link between VM guests. Using ifconfig a point-to-point connection
-+  can be established to the Linux for zSeries and S/390 system
-+  running on the other VM guest. This option is also available
-+  as a module which will be called netiucv.o. If unsure, say "Y".
++/*
++ * appldata_interval_handler()
++ * 
++ * Set timer interval for collection of data (in seconds), show current
++ * timer interval.
++ */
++static int
++appldata_interval_handler(ctl_table *ctl, int write, struct file *filp,
++			   void *buffer, size_t *lenp)
++{
++	int len, i;
++	u64 per_cpu_interval;
++	char buf[16];
++	
++	if (!*lenp || filp->f_pos) {
++		*lenp = 0;
++		return 0;
++	}
++	if (!write) {
++		len = sprintf(buf, "%i\n", appldata_interval);
++		if (len > *lenp)
++			len = *lenp;
++		if (copy_to_user(buffer, buf, len))
++			return -EFAULT;
++		goto out;
++	}
 +
-+IUCV special message support (VM only)
-+CONFIG_SMSGIUCV
-+  Select this option if you want to be able to receive SMSG messages
-+  from other VM guest systems.
++	len = *lenp;
++	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) {
++		return -EFAULT;
++	}
++	sscanf(buf, "%i", &i);
++	if (i >= smp_num_cpus) {
++		spin_lock(&appldata_timer_lock);
++		per_cpu_interval = (u64) (i / smp_num_cpus) * VIRTUAL_SECOND;
++		appldata_interval = i;
++		if (appldata_timer_active) {
++			for (i = 0; i < smp_num_cpus; i++) {
++				appldata_mod_vtimer_args.timer =
++							&appldata_timer[i];
++				appldata_mod_vtimer_args.expires =
++							per_cpu_interval;
++				smp_call_function_on(
++					(void *) appldata_mod_vtimer_wrap,
++					&appldata_mod_vtimer_args,
++					0, 1, i);
++			}
++		}
++		spin_unlock(&appldata_timer_lock);
++		P_STATUS("Monitoring interval set to %u seconds.\n",
++			appldata_interval);
++	} else {
++		P_ERROR("Timer interval has to be >= [nr. cpus] seconds, i.e. %i seconds!\n",
++			smp_num_cpus);
++		return -EINVAL;
++	}
++out:
++	*lenp = len;
++	filp->f_pos += len;
++	return 0;
++}
 +
-+Support for the z/VM recording system services (VM only)
-+CONFIG_VMLOGRDR
-+  Select this option if you want to be able to receive records collected 
-+  by the z/VM recording system services, eg. from *LOGREC. This option 
-+  should be build as a module since the actual service to connect to 
-+  has to be specified at module load time. The module will be called 
-+  vmlogrdr.o.
-+  This driver depends on the IUCV support driver. 
++/*
++ * appldata_generic_handler()
++ * 
++ * Generic start/stop monitoring and DIAG, show status of 
++ * monitoring (0 = not in process, 1 = in process)
++ */
++static int
++appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
++			   void *buffer, size_t *lenp)
++{
++	struct appldata_ops *ops;
++	int rc, len;
++	char buf[2];
 +
-+Process warning machine checks
-+CONFIG_MACHCHK_WARNING
-+  Select this option if you want the machine check handler on IBM S/390 or 
-+  zSeries to process warning machine checks (e.g. on power failures). 
-+  If unsure, say "Y".
++	ops = ctl->data;
++	if (!*lenp || filp->f_pos) {
++		*lenp = 0;
++		return 0;
++	}
++	if (!write) {
++		len = sprintf(buf, ops->active ? "1\n" : "0\n");
++		if (len > *lenp)
++			len = *lenp;
++		if (copy_to_user(buffer, buf, len))
++			return -EFAULT;
++		goto out;
++	}
 +
-+Use chscs for Common I/O
-+CONFIG_CHSC
-+  Select this option if you want the s390 common I/O layer to use information
-+  obtained by channel subsystem calls. This will enable Linux to process link
-+  failures and resource accessibility events. Moreover, if you have procfs
-+  enabled, you'll be able to toggle chpids logically offline and online. Even
-+  if you don't understand what this means, you should say "Y".
- 
- Process warning machine checks
- CONFIG_MACHCHK_WARNING
-@@ -25625,6 +25779,15 @@
-   (and some other stuff like libraries and such) is needed for
-   executing 31 bit applications.  It is safe to say "Y".
- 
-+Lan Channel Station (LCS) Interface
-+CONFIG_LCS
-+  Select this option if you want to use LCS networking  on IBM S/390 
-+  or zSeries. This device driver supports Token Ring (IEEE 802.5),
-+  FDDI (IEEE 802.7) and Ethernet. 
-+  It will use the channel device configuration if this is available.  
-+  This option is also available as a module which will be
-+  called lcs.o . If you do not know what it is, it's safe to say "Y".
++	len = *lenp;
++	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
++		return -EFAULT;
 +
- Channel Device Configuration
- CONFIG_CHANDEV
-   The channel device layer is a layer to provide a consistent
-@@ -25661,6 +25824,19 @@
-   For more info see the chandev manpage usually distributed in
-   <file:Documentation/s390/chandev.8> in the Linux source tree.
- 
-+IBM S/390 and zSeries PCICC and PCICA device driver
-+CONFIG_Z90CRYPT
-+  This driver supports the IBM S/390 and zSeries Cryptographic
-+  Coprocessor (PCICC) and Crytocraphic Accelerator (PCICA) adapters.
++	spin_lock_bh(&appldata_ops_lock);
++	if ((buf[0] == '1') && (ops->active == 0)) {
++		ops->active = 1;
++		ops->callback(ops->data);	// init record
++		rc = appldata_diag(ops->record_nr, 
++					APPLDATA_START_INTERVAL_REC,
++					(unsigned long) ops->data, ops->size);
++		if (rc != 0) {
++			P_ERROR("START DIAG 0xDC for %s failed, "
++				"return code: %d\n", ops->name, rc);
++			ops->active = 0;
++		} else {
++			P_STATUS("Monitoring %s data enabled, "
++				"DIAG 0xDC started.\n", ops->name);
++		}
++	} else if ((buf[0] == '0') && (ops->active == 1)) {
++		ops->active = 0;
++		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
++				(unsigned long) ops->data, ops->size);
++		if (rc != 0) {
++			P_ERROR("STOP DIAG 0xDC for %s failed, "
++				"return code: %d\n", ops->name, rc);
++		} else {
++			P_STATUS("Monitoring %s data disabled, "
++				"DIAG 0xDC stopped.\n", ops->name);
++		}
++	}
++	spin_unlock_bh(&appldata_ops_lock);
++out:
++	*lenp = len;
++	filp->f_pos += len;
++	return 0;
++}
++/*************************** /proc stuff <END> *******************************/
 +
-+  For details please refer to the documentation provided by IBM at
-+  <http://www10.software.ibm.com/developerworks/opensource/linux390>.
 +
-+  This driver is also available as a module (code which can be
-+  inserted in and removed from the running kernel whenever you
-+  want). If you want to compile it as a module, say 'M' here and
-+  read file Documentation/modules.txt.
++/************************* module-ops management *****************************/
++/*
++ * appldata_register_ops()
++ *
++ * update ops list, register /proc entries
++ */
++int appldata_register_ops(struct appldata_ops *ops)
++{
++	struct list_head *lh;
++	struct appldata_ops *tmp_ops;
++	int rc, i;
++	
++	rc = 0;
++	i = 0;
 +
- SAB3036 tuner support
- CONFIG_TUNER_3036
-   Say Y here to include support for Philips SAB3036 compatible tuners.
-diff -urN kernel-source-2.4.27-2.4.27.orig/Documentation/DocBook/Makefile Documentation/DocBook/Makefile
---- kernel-source-2.4.27-2.4.27.orig/Documentation/DocBook/Makefile	2004-08-07 17:26:04.000000000 -0600
-+++ Documentation/DocBook/Makefile	2006-01-30 22:25:23.000000000 -0700
-@@ -2,7 +2,7 @@
- 	   kernel-api.sgml parportbook.sgml kernel-hacking.sgml \
- 	   kernel-locking.sgml via-audio.sgml mousedrivers.sgml sis900.sgml \
- 	   deviceiobook.sgml procfs-guide.sgml tulip-user.sgml \
--	   journal-api.sgml libata.sgml
-+	   journal-api.sgml zfcp-hba-api.sgml libata.sgml
- 
- PS	:=	$(patsubst %.sgml, %.ps, $(BOOKS))
- PDF	:=	$(patsubst %.sgml, %.pdf, $(BOOKS))
-@@ -159,6 +159,14 @@
- 	$(TOPDIR)/scripts/docgen   $(JBDSOURCES) \
- 		<journal-api.tmpl >journal-api.sgml
- 
-+ZFCPHBAAPISOURCES :=  $(TOPDIR)/drivers/s390/scsi/zh.h \
-+	       $(TOPDIR)/drivers/s390/scsi/zh_main.c \
-+	       $(TOPDIR)/drivers/s390/scsi/zfcp_zh.h \
-+	       $(TOPDIR)/drivers/s390/scsi/zfcp_zh.c
++	if ((ops->size > APPLDATA_MAX_REC_SIZE) ||
++		(ops->size < 0)){
++		P_ERROR("Invalid size of %s record = %i, maximum = %i!\n",
++			ops->name, ops->size, APPLDATA_MAX_REC_SIZE);
++		rc = -ENOMEM;
++		goto out;
++	}
++	if ((ops->ctl_nr == CTL_APPLDATA) ||
++	    (ops->ctl_nr == CTL_APPLDATA_TIMER) ||
++	    (ops->ctl_nr == CTL_APPLDATA_INTERVAL)) {
++		P_ERROR("ctl_nr %i already in use!\n", ops->ctl_nr);
++		rc = -EBUSY;
++		goto out;
++	}
++	ops->ctl_table = kmalloc(4*sizeof(struct ctl_table), GFP_KERNEL);
++	if (ops->ctl_table == NULL) {
++		P_ERROR("Not enough memory for %s ctl_table!\n", ops->name);
++		rc = -ENOMEM;
++		goto out;
++	}
++	memset(ops->ctl_table, 0, 4*sizeof(struct ctl_table));
 +
-+zfcp-hba-api.sgml: zfcp-hba-api.tmpl $(ZFCPHBAAPISOURCES)
-+	$(TOPDIR)/scripts/docgen   $(ZFCPHBAAPISOURCES) \
-+		<zfcp-hba-api.tmpl >zfcp-hba-api.sgml
- 
- DVI	:=	$(patsubst %.sgml, %.dvi, $(BOOKS))
- AUX	:=	$(patsubst %.sgml, %.aux, $(BOOKS))
-diff -urN kernel-source-2.4.27-2.4.27.orig/Documentation/DocBook/zfcp-hba-api.tmpl Documentation/DocBook/zfcp-hba-api.tmpl
---- kernel-source-2.4.27-2.4.27.orig/Documentation/DocBook/zfcp-hba-api.tmpl	1969-12-31 17:00:00.000000000 -0700
-+++ Documentation/DocBook/zfcp-hba-api.tmpl	2006-01-30 22:25:23.000000000 -0700
-@@ -0,0 +1,747 @@
-+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN"[]>
++	spin_lock_bh(&appldata_ops_lock);
++	list_for_each(lh, &appldata_ops_list) {
++		tmp_ops = list_entry(lh, struct appldata_ops, list);
++		P_DEBUG("register_ops loop: %i) name = %s, ctl = %i\n",
++			++i, tmp_ops->name, tmp_ops->ctl_nr);
++		P_DEBUG("Comparing %s (ctl %i) with %s (ctl %i)\n",
++			tmp_ops->name, tmp_ops->ctl_nr, ops->name,
++			ops->ctl_nr);
++		if (strncmp(tmp_ops->name, ops->name,
++				APPLDATA_PROC_NAME_LENGTH) == 0) {
++			spin_unlock_bh(&appldata_ops_lock);
++			P_ERROR("Name \"%s\" already exists!\n", ops->name);
++			kfree(ops->ctl_table);
++			rc = -EBUSY;
++			goto out;
++		}
++		if (tmp_ops->ctl_nr == ops->ctl_nr) {
++			spin_unlock_bh(&appldata_ops_lock);
++			P_ERROR("ctl_nr %i already registered!\n", ops->ctl_nr);
++			kfree(ops->ctl_table);
++			rc = -EBUSY;
++			goto out;
++		}
++	}
++	list_add(&ops->list, &appldata_ops_list);
++	spin_unlock_bh(&appldata_ops_lock);
 +
-+<!--  ZFCP HBA API Kernel Interfaces. -->
-+<!--  Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, -->
-+<!--                     IBM Corporation -->
-+<!--  Permission is granted to copy, distribute and/or modify this -->
-+<!--  document under the terms of the GNU Free Documentation License, -->
-+<!--  Version 1.1 or any later version published by the Free Software -->
-+<!--  Foundation; with no Invariant Sections, no Front-Cover Texts and -->
-+<!--  no Back-Cover Texts.  A copy of the license is included in the -->
-+<!--  section entitled "GNU Free Documentation License". -->
++	ops->ctl_table[0].ctl_name = CTL_APPLDATA;
++	ops->ctl_table[0].procname = appldata_proc_name;
++	ops->ctl_table[0].maxlen   = 0;
++	ops->ctl_table[0].mode     = S_IRUGO | S_IXUGO;
++	ops->ctl_table[0].child    = &ops->ctl_table[2];
 +
-+<book id="ZFCPHBAAPI">
++	ops->ctl_table[1].ctl_name = 0;
 +
-+<!-- book header -->
-+  <bookinfo>
-+    <title>ZFCP HBA API Kernel Interfaces</title>
++	ops->ctl_table[2].ctl_name = ops->ctl_nr;
++	ops->ctl_table[2].procname = ops->name;
++	ops->ctl_table[2].mode     = S_IRUGO | S_IWUSR;
++	ops->ctl_table[2].proc_handler = appldata_generic_handler;
++	ops->ctl_table[2].data = ops;
 +
-+    <revhistory>
-+      <revision>
-+	<revnumber>v1.0 </revnumber>
-+	<date>2003/09/24</date>
-+	<authorinitials>AH</authorinitials>
-+	<revremark>
-+	  <remark>Initial version.</remark>
-+	</revremark>
-+      </revision>
-+      <revision>
-+	<revnumber>v1.1 </revnumber>
-+	<date>2003/11/14</date>
-+	<authorinitials>AH</authorinitials>
-+	<revremark>
-+	  <remark>Completed interface description for module
-+	    <filename>zfcp_hbaapi</filename>.
-+	  </remark>
-+<!--     Added Stefan V&ouml;lkel as author. -->
-+<!--     Removed improper license statement regarding ZFCP HBA API Library. -->
-+<!--     Added Introduction and Bibliography. -->
-+<!--     Added section about misc device provided by module zfcp_hbaapi. -->
-+<!--     Added section about callback functions of module zfcp_hbaapi. -->
-+	</revremark>
-+      </revision>
-+      <revision>
-+	<revnumber>v1.2 </revnumber>
-+	<date>2003/11/19</date>
-+	<authorinitials>AH</authorinitials>
-+	<revremark>
-+	  <remark>Completed interface description for module
-+	    <filename>zfcp</filename>.
-+	  </remark>
-+<!--     Added section about intra-kernel interfaces of module zfcp. -->
-+<!--     Added section about callbacks and hooks in module zfcp. -->
-+	</revremark>
-+      </revision>
-+    </revhistory>
++	ops->ctl_table[3].ctl_name = 0;
 +
-+    <authorgroup>
-+      <author>
-+	<firstname>Andreas</firstname>
-+	<surname>Herrman</surname>
-+	<affiliation>
-+	  <address><email>aherrman at de.ibm.com</email></address>
-+	</affiliation>
-+      </author>
-+      <author>
-+	<firstname>Stefan</firstname>
-+	<surname>V&ouml;lkel</surname>
-+	<affiliation>
-+	  <address><email>Stefan.Voelkel at millenux.com</email></address>
-+	</affiliation>
-+      </author>
-+    </authorgroup>
++	ops->sysctl_header = register_sysctl_table(ops->ctl_table,1);
++	ops->ctl_table[2].de->owner = ops->owner;
++	P_STATUS("%s-ops registered!\n", ops->name);
++out:
++	return rc;
++}
 +
-+    <copyright>
-+      <year>2003</year>
-+      <holder>IBM Corp.</holder>
-+    </copyright>
++/*
++ * appldata_unregister_ops()
++ *
++ * update ops list, unregister /proc entries, stop DIAG if necessary
++ */
++void appldata_unregister_ops(struct appldata_ops *ops)
++{
++	int rc;
 +
-+    <legalnotice>
-+      <para>
-+	The Kernel parts of ZFCP HBA API are released under
-+	the GNU	General Public License (GPL). ZFCP HBA API Library is released
-+	under the ...
-+      </para>
-+    </legalnotice>
++	unregister_sysctl_table(ops->sysctl_header);
++	kfree(ops->ctl_table);
++	if (ops->active == 1) {
++		ops->active = 0;
++		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
++				(unsigned long) ops->data, ops->size);
++		if (rc != 0) {
++			P_ERROR("STOP DIAG 0xDC for %s failed, "
++				"return code: %d\n", ops->name, rc);
++		} else {
++			P_STATUS("Monitoring %s data disabled, "
++				"DIAG 0xDC stopped.\n", ops->name);
++		}
 +
-+    <abstract>
-+      <para>
-+	This document describes Intra-Kernel and Kernel-User-Space interfaces
-+	of ZFCP HBA API.
-+      </para>
-+      <para>
-+	ZFCP HBA API is an implementation of
-+	<citation><xref linkend="bib.fchba"></citation>
-+	for the ZFCP device driver for Linux on zSeries.
-+      </para>
-+      <para>
-+	This is the first version of the document. It is written in DocBook 4.1.
-+	Please let me know if you find any markup and other errors.
-+      </para>
-+    </abstract>
-+ </bookinfo>
++	}
++	spin_lock_bh(&appldata_ops_lock);
++	list_del(&ops->list);
++	spin_unlock_bh(&appldata_ops_lock);
++	P_STATUS("%s-ops unregistered!\n", ops->name);
++}
++/********************** module-ops management <END> **************************/
 +
-+ <toc></toc>
 +
-+  <!-- introduction -->
-+  <chapter id="cha.introduction">
-+    <title>Introduction</title>
-+    <para>
-+      ZFCP HBA API is an implementation of
-+      <citation><xref linkend="bib.fchba"></citation>
-+      for the ZFCP device driver. The ZFCP device driver is a FCP device driver
-+      for Linux on zSeries. This documentation describes the ZFCP HBA API for
-+      Linux Kernel 2.4. ZFCP HBA API consists of the following parts.
-+    </para>
-+    <itemizedlist>
-+      <listitem>
-+	<para>
-+	  ZFCP HBA API Library - a shared library which provides the
-+	  API defined in <citation><xref linkend="bib.fchba"></citation>.
-+	</para>
-+      </listitem>
-+      <listitem>
-+	<para>
-+	  The kernel module <filename>zfcp_hbaapi</filename> which
-+	  provides a misc device with IO controls for communication
-+	  between kernel and user space. The module registers callback
-+	  functions in the ZFCP device driver and is able to use
-+	  functionality provided by the ZFCP device driver. This
-+	  module is the connection between ZFCP HBA API Library and
-+	  the ZFCP devcie driver.
-+	</para>
-+      </listitem>
-+      <listitem>
-+	<para>
-+	  Finally the ZFCP device driver contains hooks at which
-+	  callback functions of the <filename>zfcp_hbaapi</filename>
-+	  kernel module are invoked.  Furthermore the device driver
-+	  provides functionality which can be used by the
-+	  <filename>zfcp_hbaapi</filename> kernel module.
-+	</para>
-+      </listitem>
-+    </itemizedlist>
-+    <para>
-+      This documentation describes the kernel parts of ZFCP HBA API.
-+      Separate documentation for ZFCP HBA API Library exists.
-+    </para>
-+  </chapter>
++/******************************* init / exit *********************************/
++/*
++ * appldata_init()
++ *
++ * init timer and tasklet, register /proc entries
++ */
++static int __init appldata_init(void)
++{
++	int i;
++	
++	P_DEBUG("sizeof(parameter_list) = %lu\n",
++		sizeof(struct appldata_parameter_list));
 +
-+  <!-- documentation about zfcp_hbaapi module -->
-+  <chapter id="cha.zfcphbaapi">
-+    <title>Kernel Module <filename>zfcp_hbaapi</filename></title>
-+    <section id="sec.zfcphbaapioverview">
-+      <title>Overview</title>
-+      <para>
-+	The module <filename>zfcp_hbaapi</filename> is the interface
-+	between the ZFCP HBA API Library and the ZFCP device
-+	driver. It provides a misc device which is used for
-+	communication between kernel and user space.  The module
-+	registers callback functions in the ZFCP device driver which
-+	are invoked when certain events occur. Furthermore it calls
-+	functions provided by the ZFCP device driver to collect data
-+	for the ZFCP HBA API Library.
-+      </para>
-+    </section>
-+    <section>
-+      <title>Device File</title>
-+      <para>
-+	The module <filename>zfcp_hbaapi</filename> provides a misc
-+	device. The corresponding device node should be named
-+	<filename>/dev/zfcp_hbaapi</filename> - ZFCP HBA API Library
-+	expects this name for the device file.  When the module is
-+	loaded the device node can be generated using the commands:
-+      </para>
-+      <screen>
-+#>minor=`cat /proc/misc | awk "\\$2==\\"zfcp_hbaapi\\" {print \\$1}"`
-+#>mknod /dev/zfcp_hbaapi c 10 $minor
-+      </screen>
-+      <section>
-+	<title>Module Paramters</title>
-+	<para>The following parameters can be set for the module.</para>
-+	<table>
-+	  <title>Module Parameters</title>
-+	  <tgroup cols='3' colsep='1' rowsep='1'>
-+	    <thead>
-+	      <row>
-+		<entry>Parameter</entry>
-+		<entry>Description</entry>
-+		<entry>Default Value</entry>
-+	      </row>
-+	    </thead>
-+	    <tbody>
-+	      <row>
-+		<entry><para><parameter>maxshared</parameter></para></entry>
-+		<entry><para>Maximum number of events in the shared event queue.
-+		  </para></entry>
-+		<entry><para><parameter>20</parameter></para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para><parameter>maxpolled</parameter></para></entry>
-+		<entry><para>Maximum number of events in the polled event queue.
-+		  </para></entry>
-+		<entry><para><parameter>20</parameter></para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para><parameter>minor</parameter></para></entry>
-+		<entry><para>
-+		    Minor number for the misc device to be registered.
-+		  </para></entry>
-+		<entry><para>
-+		    <symbol>MISC_DYNAMIC_MINOR</symbol>
-+		  </para></entry>
-+	      </row>
-+	    </tbody>
-+	  </tgroup>
-+	</table>
-+      </section>
-+      <section>
-+	<title>File Operations</title>
-+	<para>
-+	  The module <filename>zfcp_hbaapi</filename> defines the
-+	  methods <function>open</function>,
-+	  <function>read</function>, <function>release</function>, and
-+	  <function>ioctl</function> for the misc device.
-+	</para>
-+	<section>
-+	  <title>Reference</title>
-+	  <para></para>
-+!Fdrivers/s390/scsi/zh_main.c zh_ioctl
-+!Fdrivers/s390/scsi/zh_main.c zh_read
-+!Fdrivers/s390/scsi/zh_main.c zh_release
-+!Fdrivers/s390/scsi/zh_main.c zh_open
-+	</section>
-+      </section>
-+      <section>
-+	<title>IO Controls</title>
-+	<para>
-+	  The next table gives an overview about IO controls of the misc
-+	  device, the name of corresponding internal helper functions and
-+	  argument types (if any).
-+	</para>
-+	<table frame='all'><title>IO Controls</title>
-+	  <tgroup cols='2' colsep='1' rowsep='1'>
-+	    <thead>
-+	      <row>
-+		<entry><para>Name</para></entry>
-+		<entry><para>Helper Function, Argument Type</para></entry>
-+	      </row>
-+	    </thead>
-+	    <tbody>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_GET_ADAPTERATTRIBUTES</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_get_adapterattributes</function>,
-+		    <type>struct zh_get_adapterattributes</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_GET_PORTATTRIBUTES</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_get_portattributes</function>,
-+		    <type>struct zh_get_portattributes</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_GET_PORTSTATISTICS</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_get_portstatistics</function>,
-+		    <type>struct zh_get_portstatistics</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_GET_DPORTATTRIBUTES</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_get_dportattributes</function>,
-+		    <type>struct zh_get_portattributes</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_GET_RNID</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_get_rnid</function>,
-+		    <type>struct zh_get_rnid</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_SEND_RNID</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_send_rnid</function>,
-+		    <type>struct zh_send_rnid</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_SEND_CT</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_send_ct</function>,
-+		    <type>struct zh_send_ct</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_SCSI_INQUIRY</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_scsi_inquiry</function>,
-+		    <type>struct zh_scsi_inquiry</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_SCSI_READ_CAPACITY</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_scsi_read_capacity</function>,
-+		    <type>struct zh_scsi_read_capacity</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_SCSI_REPORT_LUNS</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_scsi_report_luns</function>,
-+		    <type>struct zh_scsi_report_luns</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_GET_EVENT_BUFFER</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_get_event_buffer</function>,
-+		    <type>struct zh_get_event_buffer</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_GET_CONFIG</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_get_config</function>,
-+		    <type>struct zh_get_config</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_CLEAR_CONFIG</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_clear_config</function>,
-+		    <type>struct zh_clear_config</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_EVENT_START</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_event_start</function>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_EVENT_STOP</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_event_stop</function>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_EVENT</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_event</function>,
-+		    <type>struct zh_event</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		  <symbol>ZH_IOC_EVENT_INSERT</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_event_insert</function>
-+		  </para></entry>
-+	      </row>
-+	    </tbody>
-+	  </tgroup>
-+	</table>
-+	<para>
-+	  If <filename>zfcp_hbaapi</filename> is compiled for 64 bit
-+	  architecture, two additional IO controls exist. They are
-+	  used for 32 bit IO control conversion:
-+	</para>
-+	<table frame='all'>
-+	  <title>IO Controls (on 64 bit architecture only)</title>
-+	  <tgroup cols='2' colsep='1' rowsep='1'>
-+	    <thead>
-+	      <row>
-+		<entry><para>Name</para></entry>
-+		<entry><para>Helper Function, Argument Type</para></entry>
-+	      </row>
-+	    </thead>
-+	    <tbody>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_SEND_CT32</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_send_ct32</function>,
-+		    <type>struct zh_send_ct32</type>
-+		  </para></entry>
-+	      </row>
-+	      <row>
-+		<entry><para>
-+		    <symbol>ZH_IOC_SCSI_REPORT_LUNS32</symbol>
-+		  </para></entry>
-+		<entry><para>
-+		    <function>zh_ioc_scsi_report_luns</function>,
-+		    <type>struct zh_scsi_report_luns32</type>
-+		  </para></entry>
-+	      </row>
-+	    </tbody>
-+	  </tgroup>
-+	</table>
-+      </section>
-+      <section>
-+	<title>Reference</title>
-+	<para></para>
-+!Fdrivers/s390/scsi/zh.h zh_get_adapterattributes
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_adapterattributes
-+!Fdrivers/s390/scsi/zh.h zh_get_portattributes
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_portattributes
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_dportattributes
-+!Fdrivers/s390/scsi/zh.h zh_get_portstatistics
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_portstatistics
-+!Fdrivers/s390/scsi/zh.h zh_get_rnid
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_rnid
-+!Fdrivers/s390/scsi/zh.h zh_send_rnid
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_send_rnid
-+!Fdrivers/s390/scsi/zh.h zh_send_ct
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_send_ct
-+!Fdrivers/s390/scsi/zh.h zh_scsi_inquiry
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_scsi_inquiry
-+!Fdrivers/s390/scsi/zh.h zh_scsi_read_capacity
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_scsi_read_capacity
-+!Fdrivers/s390/scsi/zh.h zh_scsi_report_luns
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_scsi_report_luns
-+!Fdrivers/s390/scsi/zh.h zh_get_event_buffer
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_event_buffer
-+!Fdrivers/s390/scsi/zh.h zh_get_config
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_config
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_clear_config
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_event_start
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_event_stop
-+!Fdrivers/s390/scsi/zh.h zh_event
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_event
-+!Fdrivers/s390/scsi/zh_main.c zh_ioc_event_insert
-+      </section>
-+    </section>
-+    <section>
-+      <title>Callback functions</title>
-+      <para>
-+	For event notification <filename>zfcp_hbaapi</filename>
-+	registers a bunch of callback functions at the ZFCP device
-+	driver.  In zfcp certain hooks exist where those callbacks are
-+	invoked.
-+      </para>
-+      <section>
-+	<title>Reference</title>
-+	<para></para>
-+!Fdrivers/s390/scsi/zh_main.c zh_cb_adapter_add
-+!Fdrivers/s390/scsi/zh_main.c zh_cb_port_add
-+!Fdrivers/s390/scsi/zh_main.c zh_cb_unit_add
-+!Fdrivers/s390/scsi/zh_main.c zh_cb_incomming_els
-+!Fdrivers/s390/scsi/zh_main.c zh_cb_link_down
-+!Fdrivers/s390/scsi/zh_main.c zh_cb_link_up
-+      </section>
-+    </section>
-+  </chapter>
++	for (i = 0; i < smp_num_cpus; i++) {
++		smp_call_function_on((void *) init_virt_timer,
++					&appldata_timer[i],
++					0, 1, i);
++		appldata_timer[i].function = appldata_timer_function;
++		appldata_timer[i].data = (unsigned long)
++						&appldata_tasklet_struct;
++	}
++	atomic_set(&appldata_expire_count, smp_num_cpus);
 +
-+  <!-- changed/new functions and structures in zfcp -->
-+  <!-- documentation about zfcp module -->
-+  <chapter id="cha.zfcp">
-+    <title>Kernel Module <filename>zfcp</filename></title>
-+    <para>
-+      The module <filename>zfcp</filename> provides (new) interfaces for
-+      ZFCP HBA API. Furthermore hooks are integrated at which callback functions
-+      for event notification are invoked.
-+    </para>
++	appldata_sysctl_header = register_sysctl_table(appldata_dir_table, 1);
++#ifdef MODULE
++	appldata_dir_table[0].de->owner = THIS_MODULE;
++	appldata_table[0].de->owner = THIS_MODULE;
++	appldata_table[1].de->owner = THIS_MODULE;
++#endif
++	
++	tasklet_init(&appldata_tasklet_struct, appldata_tasklet_function, 0);
++	P_DEBUG("Base interface initialized.\n");
++	return 0;
++}
 +
-+    <section>
-+      <title>Intra-Kernel Interface</title>
-+      <para>The ZFCP device driver exports the following functions</para>
-+      <glosslist>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_callbacks_register</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>Register callbacks for event handling. Called from
-+	      <function>zh_init</function> - the init function of module
-+	      <filename>zfcp_hbaapi</filename>.</para>
-+	  </glossdef>
-+	</glossentry>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_callbacks_unregister</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>Unregister callbacks for event handling. Called from
-+	      <function>zh_exit</function> - the exit function of module
-+	      <filename>zfcp_hbaapi</filename>.</para>
-+	  </glossdef>
-+	</glossentry>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_get_config</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>For each adapter, port and unit configured in module
-+	      <filename>zfcp</filename> the corresponding callback
-+	      function is called.</para>
-+	  </glossdef>
-+	</glossentry>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_get_adapter_attributes</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>Collect attributes of an adapter.</para>
-+	  </glossdef>
-+	</glossentry>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_get_port_attributes</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>Collect attributes of an adapter port. Calls FSF
-+	      command <command>ExchangePortData</command>.</para>
-+	  </glossdef>
-+	</glossentry>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_port_statistics</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>Collect statistics of an adapter port. Calls FSF
-+	      command <command>ExchangePortData</command>.</para>
-+	  </glossdef>
-+	</glossentry>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_get_dport_attributes</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>Collect attributes of a discovered port. Sends a
-+	      FC-GS-2 request <command>GA_NXT</command> to the Name
-+	      Server Directory Service.</Para>
-+	  </glossdef>
-+	</glossentry>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_assert_fclun_zero</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>Checks whether an unit with FC LUN
-+	      <parameter>0x0</parameter> is configured in
-+	      <filename>zfcp</filename> for a certain port. If not it
-+	      creates a <filename>zfcp</filename> unit structure for FC
-+	      LUN <parameter>0x0</parameter> for this port.</para>
-+	  </glossdef>
-+	</glossentry>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_send_scsi</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>Send a SCSI command to a FC LUN.</para>
-+	  </glossdef>
-+	</glossentry>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_send_els</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>Send ELS commands (according to FC-FS).</para>
-+	  </glossdef>
-+	</glossentry>
-+	<glossentry>
-+	  <glossterm>
-+	    <function>zfcp_zh_send_ct</function>
-+	  </glossterm>
-+	  <glossdef>
-+	    <para>Send Generic Service commands according to FC-GS-4).
-+	    </para>
-+	  </glossdef>
-+	</glossentry>
-+      </glosslist>
-+      <note>
-+	<para>In <function>zfcp_zh_send_ct</function> currently only
-+	  requests to the Name Server Directory Service are
-+	  supported.</para>
-+      </note>
-+      <section>
-+	<title>Reference</title>
-+	<para></para>
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_callbacks_register
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_callbacks_unregister
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_get_config
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_get_adapter_attributes
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_get_port_attributes
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_get_port_statistics
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_get_dport_attributes
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_send_ct
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_send_els
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_send_scsi
-+!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_assert_fclun_zero
-+      </section>
-+    </section>
-+    <section>
-+      <title>Callbacks for Event Handling</title>
-+      <para>
-+	To enable event delivery as required by <citation><xref
-+	linkend="bib.fchba"></citation>, some callback functions of
-+	module <filename>zfcp_hbaapi</filename> must be called from
-+	module <filename>zfcp</filename>.
-+      </para>
-+      <para>
-+	The following table gives an overview of the callbacks into
-+	module <filename>zfcp_hbaapi</filename> and their hooks in
-+	<filename>zfcp</filename>.
-+      </para>
-+      <table frame='all'><title>Callbacks</title>
-+	<tgroup cols='2' colsep='1' rowsep='1'>
-+	  <thead>
-+	    <row>
-+	      <entry><para>Callback</para></entry>
-+	      <entry><para>
-+		  Hook in module <filename>zfcp</filename>
-+		</para></entry>
-+	    </row>
-+	  </thead>
-+	  <tbody>
-+	    <row>
-+	      <entry><para>
-+		  <function>adapter_add</function>
-+		</para></entry>
-+	      <entry><para>
-+		  <function>zfcp_fsf_exchange_config_data_handler</function>
-+		</para></entry>
-+	    </row>
-+	    <row>
-+	      <entry><para>
-+		  <function>port_add</function>
-+		</para></entry>
-+	      <entry><para>
-+		  <function>zfcp_port_enqueue</function>
-+		</para></entry>
-+	    </row>
-+	    <row>
-+	      <entry><para>
-+		  <function>unit_add</function>
-+		</para></entry>
-+	      <entry><para>
-+		  <function>zfcp_unit_enqueue</function>
-+		</para></entry>
-+	    </row>
-+	    <row>
-+	      <entry><para>
-+		  <function>incoming_els</function>
-+		</para></entry>
-+	      <entry><para>
-+		  <function>zfcp_fsf_incoming_els</function>
-+		</para></entry>
-+	    </row>
-+	    <row>
-+	      <entry><para>
-+		  <function>link_down</function>
-+		</para></entry>
-+	      <entry><para>
-+		  <function>zfcp_status_read_handler</function>,
-+		  <function>zfcp_fsf_protstatus_eval</function>
-+		</para></entry>
-+	    </row>
-+	    <row>
-+	      <entry><para>
-+		  <function>link_up</function>
-+		</para></entry>
-+	      <entry><para>
-+		  <function>zfcp_status_read_handler</function>
-+		</para></entry>
-+	    </row>
-+	  </tbody>
-+	</tgroup>
-+      </table>
-+    </section>
-+  </chapter>
-+
-+  <!-- Bibliography -->
-+  <bibliography>
-+    <biblioentry id="bib.fchba" xreflabel="FC-HBA">
-+      <title>
-+	Working Draft. Information technology - Fibre Channel HBA API (FC-HBA)
-+      </title>
-+      <orgname>The T11 Technical Committee</orgname>
-+      <!-- 	<pubdate> -->
-+      <!-- 	  Revision 11, 2003-10-29, -->
-+      <!-- 	  <ulink url="http://www.t11.org/"></ulink> -->
-+      <!-- 	</pubdate> -->
-+    </biblioentry>
-+  </bibliography>
-+
-+</book>
-+<!-- Keep this comment at the end of the file
-+Local variables:
-+mode: sgml
-+sgml-always-quote-attributes:t
-+sgml-auto-insert-required-elements:t
-+sgml-balanced-tag-edit:t
-+sgml-exposed-tags:nil
-+sgml-general-insert-case:lower
-+sgml-indent-data:t
-+sgml-indent-step:2
-+sgml-local-catalogs:nil
-+sgml-local-ecat-files:nil
-+sgml-minimize-attributes:nil
-+sgml-namecase-general:t
-+sgml-omittag:t
-+sgml-shorttag:t
-+sgml-tag-region-if-active:t
-+End:
-+-->
-diff -urN kernel-source-2.4.27-2.4.27.orig/Documentation/ioctl-number.txt Documentation/ioctl-number.txt
---- kernel-source-2.4.27-2.4.27.orig/Documentation/ioctl-number.txt	2004-08-07 17:26:04.000000000 -0600
-+++ Documentation/ioctl-number.txt	2006-01-30 22:25:23.000000000 -0700
-@@ -107,6 +107,7 @@
- 'W'	00-1F	linux/wanrouter.h	conflict!
- 'X'	all	linux/xfs_fs.h
- 'Y'	all	linux/cyclades.h
-+'Z'	all	linux/s390net.h		S390 networking
- 'a'	all				ATM on linux
- 					<http://lrcwww.epfl.ch/linux-atm/magic.html>
- 'b'	00-FF				bit3 vme host bridge
-@@ -183,6 +184,8 @@
- 0xB1	00-1F	PPPoX			<mailto:mostrows at styx.uwaterloo.ca>
- 0xCB	00-1F	CBM serial IEC bus	in development:
- 					<mailto:michael.klein at puffin.lb.shuttle.de>
-+0xDD	00-3F	ZFCP device driver	see drivers/s390/scsi/
-+					<mailto:aherrman at de.ibm.com>
- 0xF3    00-3F   linux/sisfb.h 		SiS framebuffer device driver
- 					<mailto:thomas at winischhofer.net>
- 0xFE	00-9F	Logical Volume Manager	<mailto:linux-lvm at sistina.com>
-diff -urN kernel-source-2.4.27-2.4.27.orig/Documentation/s390/TAPE Documentation/s390/TAPE
---- kernel-source-2.4.27-2.4.27.orig/Documentation/s390/TAPE	2001-07-25 15:12:01.000000000 -0600
-+++ Documentation/s390/TAPE	2006-01-30 22:25:23.000000000 -0700
-@@ -1,122 +0,0 @@
--Channel attached Tape device driver 
--
-------------------------------WARNING-----------------------------------------
--This driver is considered to be EXPERIMENTAL. Do NOT use it in 
--production environments. Feel free to test it and report problems back to us. 
-------------------------------------------------------------------------------
--
--The LINUX for zSeries tape device driver manages channel attached tape drives 
--which are compatible to IBM 3480 or IBM 3490 magnetic tape subsystems. This 
--includes various models of these devices (for example the 3490E). 
--
--
--Tape driver features 
--
--The device driver supports a maximum of 128 tape devices. 
--No official LINUX device major number is assigned to the zSeries tape device 
--driver. It allocates major numbers dynamically and reports them on system 
--startup. 
--Typically it will get major number 254 for both the character device front-end 
--and the block device front-end. 
--
--The tape device driver needs no kernel parameters. All supported devices 
--present are detected on driver initialization at system startup or module load.
--The devices detected are ordered by their subchannel numbers. The device with 
--the lowest subchannel number becomes device 0, the next one will be device 1 
--and so on. 
--
--
--Tape character device front-end 
--
--The usual way to read or write to the tape device is through the character 
--device front-end. The zSeries tape device driver provides two character devices
--for each physical device -- the first of these will rewind automatically when 
--it is closed, the second will not rewind automatically. 
--
--The character device nodes are named /dev/rtibm0 (rewinding) and /dev/ntibm0 
--(non-rewinding) for the first device, /dev/rtibm1 and /dev/ntibm1 for the 
--second, and so on. 
--
--The character device front-end can be used as any other LINUX tape device. You 
--can write to it and read from it using LINUX facilities such as GNU tar. The 
--tool mt can be used to perform control operations, such as rewinding the tape 
--or skipping a file. 
--
--Most LINUX tape software should work with either tape character device. 
--
--
--Tape block device front-end 
--
--The tape device may also be accessed as a block device in read-only mode. 
--This could be used for software installation in the same way as it is used with 
--other operation systems on the zSeries platform (and most LINUX 
--distributions are shipped on compact disk using ISO9660 filesystems). 
--
--One block device node is provided for each physical device. These are named 
--/dev/btibm0 for the first device, /dev/btibm1 for the second and so on. 
--You should only use the ISO9660 filesystem on LINUX for zSeries tapes because 
--the physical tape devices cannot perform fast seeks and the ISO9660 system is 
--optimized for this situation. 
--
--
--Tape block device example 
--
--In this example a tape with an ISO9660 filesystem is created using the first 
--tape device. ISO9660 filesystem support must be built into your system kernel
--for this. 
--The mt command is used to issue tape commands and the mkisofs command to 
--create an ISO9660 filesystem: 
--
--- create a LINUX directory (somedir) with the contents of the filesystem 
--     mkdir somedir
--     cp contents somedir 
--
--- insert a tape 
--
--- ensure the tape is at the beginning 
--     mt -f /dev/ntibm0 rewind 
--
--- set the blocksize of the character driver. The blocksize 2048 bytes
--  is commonly used on ISO9660 CD-Roms
--     mt -f /dev/ntibm0 setblk 2048 
--
--- write the filesystem to the character device driver 
--     mkisofs -o /dev/ntibm0 somedir 
--
--- rewind the tape again 
--     mt -f /dev/ntibm0 rewind 
--
--- Now you can mount your new filesystem as a block device: 
--     mount -t iso9660 -o ro,block=2048 /dev/btibm0 /mnt 
--
--TODO List 
--
--   - Driver has to be stabelized still
--
--BUGS 
--
--This driver is considered BETA, which means some weaknesses may still
--be in it.
--If an error occurs which cannot be handled by the code you will get a 
--sense-data dump.In that case please do the following: 
--
--1. set the tape driver debug level to maximum: 
--     echo 6 >/proc/s390dbf/tape/level 
--
--2. re-perform the actions which produced the bug. (Hopefully the bug will 
--   reappear.) 
--
--3. get a snapshot from the debug-feature: 
--     cat /proc/s390dbf/tape/hex_ascii >somefile 
--
--4. Now put the snapshot together with a detailed description of the situation 
--   that led to the bug: 
-- - Which tool did you use? 
-- - Which hardware do you have? 
-- - Was your tape unit online? 
-- - Is it a shared tape unit? 
--
--5. Send an email with your bug report to: 
--     mailto:Linux390 at de.ibm.com 
--
--
-diff -urN kernel-source-2.4.27-2.4.27.orig/Documentation/s390/xip2.8 Documentation/s390/xip2.8
---- kernel-source-2.4.27-2.4.27.orig/Documentation/s390/xip2.8	1969-12-31 17:00:00.000000000 -0700
-+++ Documentation/s390/xip2.8	2006-01-30 22:25:23.000000000 -0700
-@@ -0,0 +1,67 @@
-+.TH XIP2 8 "8 December 2003" "Linux 2.4" "Linux Programmer's Manual"
-+.SH NAME
-+xip2 \- mount a xip2 file system
-+.SH "DESCRIPTION"
-+This man page describes mount options specific to the xip2 filesystem. Have a 
-+look at the man page of 
-+.BR mount (2)
-+for information about what mount options are and how mount is used overall.
-+.SH "Mount options inherited from ext2"
-+The `xip2' file system is derived from the second extended filesystem (ext2).
-+It supports the following mount options which are inherited from ext2:
-+.TP
-+.BR bsddf " / " minixdf
-+Set the behaviour for the
-+.I statfs
-+system call. The
-+.B minixdf
-+behaviour is to return in the
-+.I f_blocks
-+field the total number of blocks of the file system, while the
-+.B bsddf
-+behaviour (which is the default) is to subtract the overhead blocks
-+used by the ext2 file system and not available for file storage.
-+.TP
-+.BR errors=continue " / " errors=remount-ro " / " errors=panic
-+Define the behaviour when an error is encountered.
-+(Either ignore errors and just mark the file system erroneous and continue,
-+or remount the file system read-only, or panic and halt the system.) 
-+Note that  mounting read-only does not change anything because the xip2 file 
-+system is read-only only anyway.
-+.TP
-+.BR grpid " or " bsdgroups " / " nogrpid " or " sysvgroups
-+These options are accepted but ignored.
-+.TP
-+\fBresgid=\fP\fIn\fP and \fBresuid=\fP\fIn\fP
-+These options are accepted but ignored.
-+.TP
-+.BI sb= n
-+Instead of block 1, use block
-+.I n
-+as superblock. This could be useful when the filesystem has been damaged.
-+The block number here uses 1k units. Thus, if you want to use logical
-+block 32768 on a filesystem with 4k blocks, use "sb=131072". Note that the 
-+xip2 file system always works with 4k blocks.
-+.TP
-+.BR grpquota " / " noquota " / " quota " / " usrquota
-+These options are accepted but ignored.
-+
-+.TP
-+.BR nouid32
-+Disables 32-bit UIDs and GIDs.  This is for interoperability with older
-+kernels which only store and expect 16-bit values.
-+.SH "Mount options specific for xip2"
-+The 'xip2' file system supports only one mount option that is specific for its
-+use:
-+.TP
-+.BI memarea= <name>
-+This mount-option is mandatory. It specifies the name of the memory segment to
-+be used. In case of running on zSeries z/VM, available memory segment names
-+correspond with available z/VM DCSS.
-+.SH "SEE ALSO"
-+.BR mount (8)
-+.SH BUGS
-+As of today, and mostly due to the stable code base inherited from the second 
-+extended filesystem, no known bugs exist. In case you think you encountered 
-+one, please report it to Carsten Otte
-+.B <cotte at de.ibm.com>
-diff -urN kernel-source-2.4.27-2.4.27.orig/Makefile Makefile
---- kernel-source-2.4.27-2.4.27.orig/Makefile	2006-01-30 22:23:45.000000000 -0700
-+++ Makefile	2006-01-30 22:25:25.000000000 -0700
-@@ -292,7 +292,7 @@
- boot: vmlinux
- 	@$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C arch/$(ARCH)/boot
- 
--vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o init/do_mounts.o linuxsubdirs
-+vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o init/do_mounts.o Kerntypes linuxsubdirs
- 	$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o init/do_mounts.o \
- 		--start-group \
- 		$(CORE_FILES) \
-@@ -303,6 +303,11 @@
- 		-o vmlinux
- 	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
- 
-+Kerntypes: init/kerntypes.o
-+	@if [ -f init/kerntypes.o ]; then \
-+		mv init/kerntypes.o Kerntypes; \
-+	fi
++/*
++ * appldata_exit()
++ * 
++ * stop timer and tasklet, unregister /proc entries
++ */
++static void __exit appldata_exit(void)
++{
++	struct list_head *lh;
++	struct appldata_ops *ops;
++	int rc, i;
++	
++	P_DEBUG("Unloading module ...\n");
++	/*
++	 * ops list should be empty, but just in case something went wrong...
++	 */
++	spin_lock_bh(&appldata_ops_lock);
++	list_for_each(lh, &appldata_ops_list) {
++		ops = list_entry(lh, struct appldata_ops, list);
++		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
++				(unsigned long) ops->data, ops->size);
++		if (rc != 0) {
++			P_ERROR("STOP DIAG 0xDC for %s failed, "
++				"return code: %d\n", ops->name, rc);
++		}
++	}
++	spin_unlock_bh(&appldata_ops_lock);
++	for (i = 0; i < smp_num_cpus; i++) {
++		smp_call_function_on((void *) del_virt_timer, &appldata_timer[i],
++					0, 1, i);
++	}
++	appldata_timer_active = 0;
 +
- symlinks:
- 	rm -f include/asm
- 	( cd include ; ln -sf asm-$(ARCH) asm)
-@@ -357,6 +362,9 @@
- 	 echo > .ver1
- 	@echo \#define LINUX_COMPILE_DOMAIN \"`cat .ver1 | $(uts_truncate)`\" >> .ver
- 	@echo \#define LINUX_COMPILER \"`$(CC) $(CFLAGS) -v 2>&1 | tail -n 1`\" >> .ver
-+	@echo \__linux_compile_version_id__`echo $(KERNELRELEASE) | tr -c '[0-9A-Za-z\n]' '_'`__`hostname | tr -c '[0-9A-Za-z\n]' '_'`__`LANG=C date | tr -c '[0-9A-Za-z\n]' '_'` > .ver1
-+	@echo \#define LINUX_COMPILE_VERSION_ID `cat .ver1`  >> .ver
-+	@echo "typedef char* `cat .ver1`_t;"  >> .ver
- 	@mv -f .ver $@
- 	@rm -f .ver1
- 
-@@ -373,6 +381,9 @@
- init/version.o: init/version.c include/linux/compile.h include/config/MARKER
- 	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) -DUTS_MACHINE='"$(ARCH)"' -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o init/version.o init/version.c
- 
-+init/kerntypes.o: init/kerntypes.c include/config/MARKER include/linux/compile.h
-+	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -gstabs -c -o $*.o $<
++	unregister_sysctl_table(appldata_sysctl_header);
++	
++	tasklet_kill(&appldata_tasklet_struct);
 +
- init/main.o: init/main.c include/config/MARKER
- 	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o $@ $<
- 
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/Makefile arch/s390/Makefile
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/Makefile	2002-08-02 18:39:43.000000000 -0600
-+++ arch/s390/Makefile	2006-01-30 22:25:24.000000000 -0700
-@@ -23,15 +23,18 @@
- LINKFLAGS =-T $(TOPDIR)/arch/s390/vmlinux.lds $(LDFLAGS)
- endif
- 
-+CFLAGS_ARCH := -m31
- CFLAGS_PIPE := -pipe
- CFLAGS_NSR  := -fno-strength-reduce
--CFLAGS := $(CFLAGS) $(CFLAGS_PIPE) $(CFLAGS_NSR)
-+CFLAGS := $(CFLAGS) $(CFLAGS_ARCH) $(CFLAGS_PIPE) $(CFLAGS_NSR)
-+AFLAGS := $(AFLAGS) $(CFLAGS_ARCH)
- 
- HEAD := arch/s390/kernel/head.o arch/s390/kernel/init_task.o
- 
- SUBDIRS := $(SUBDIRS) arch/s390/mm arch/s390/kernel arch/s390/lib \
--           drivers/s390 arch/s390/math-emu
--CORE_FILES := arch/s390/mm/mm.o arch/s390/kernel/kernel.o $(CORE_FILES)
-+           arch/s390/appldata drivers/s390 arch/s390/math-emu
-+CORE_FILES := arch/s390/mm/mm.o arch/s390/kernel/kernel.o \
-+              arch/s390/appldata/appldata.o $(CORE_FILES)
- DRIVERS := $(DRIVERS) drivers/s390/io.o
- LIBS := $(TOPDIR)/arch/s390/lib/lib.a $(LIBS) $(TOPDIR)/arch/s390/lib/lib.a
- 
-@@ -39,7 +42,7 @@
-   CORE_FILES := $(CORE_FILES) arch/s390/math-emu/math-emu.o
- endif
- 
--all: image listing
-+all: image
- 
- listing: vmlinux
- 	@$(MAKEBOOT) listing
-@@ -47,6 +50,9 @@
- arch/s390/kernel: dummy
- 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/kernel
- 
-+arch/s390/appldata: dummy
-+	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/appldata
++	P_DEBUG("... module unloaded!\n");
++}
++/**************************** init / exit <END> ******************************/
 +
- arch/s390/mm: dummy
- 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/mm
- 
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/Makefile arch/s390/appldata/Makefile
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/Makefile	1969-12-31 17:00:00.000000000 -0700
-+++ arch/s390/appldata/Makefile	2006-01-30 22:25:24.000000000 -0700
-@@ -0,0 +1,13 @@
-+#
-+# Linux - VM Monitor Stream, Stage 1
-+#
 +
-+O_TARGET := appldata.o
++module_init(appldata_init);
++module_exit(appldata_exit);
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Gerald Schaefer");
++MODULE_DESCRIPTION("Linux-VM Monitor Stream, base infrastructure");
 +
-+obj-$(CONFIG_APPLDATA_BASE) += appldata_base.o
-+obj-$(CONFIG_APPLDATA_MEM) += appldata_mem.o
-+obj-$(CONFIG_APPLDATA_OS) += appldata_os.o
-+obj-$(CONFIG_APPLDATA_NET_SUM) += appldata_net_sum.o
-+export-objs += appldata_base.o
++EXPORT_SYMBOL_GPL(appldata_register_ops);
++EXPORT_SYMBOL_GPL(appldata_unregister_ops);
 +
-+include $(TOPDIR)/Rules.make
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata.h arch/s390/appldata/appldata.h
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata.h	1969-12-31 17:00:00.000000000 -0700
-+++ arch/s390/appldata/appldata.h	2006-01-30 22:25:24.000000000 -0700
++#ifdef MODULE
++/*
++ * Kernel symbols needed by appldata_mem and appldata_os modules.
++ * However, if this file is compiled as a module (for testing only), these
++ * symbols are not exported. In this case, we define them locally and export
++ * those.
++ */
++void si_swapinfo(struct sysinfo *val)
++{
++	val->freeswap = -1ul;
++	val->totalswap = -1ul;
++}
++unsigned long avenrun[3] = {-1 - FIXED_1/200, -1 - FIXED_1/200,
++				-1 - FIXED_1/200};
++int nr_threads = -1;
++#endif /* MODULE */
++EXPORT_SYMBOL_GPL(si_swapinfo);
++EXPORT_SYMBOL_GPL(page_cache_size);
++EXPORT_SYMBOL_GPL(nr_threads);
++EXPORT_SYMBOL_GPL(avenrun);
+diff -urN kernel-source-2.4.27.orig/arch/s390/appldata/appldata.h arch/s390/appldata/appldata.h
+--- kernel-source-2.4.27.orig/arch/s390/appldata/appldata.h	1969-12-31 17:00:00.000000000 -0700
++++ arch/s390/appldata/appldata.h	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,58 @@
 +/*
 + * arch/s390/appldata/appldata.h
@@ -1540,669 +714,372 @@
 +
 +extern int appldata_register_ops(struct appldata_ops *ops);
 +extern void appldata_unregister_ops(struct appldata_ops *ops);
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata_base.c arch/s390/appldata/appldata_base.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata_base.c	1969-12-31 17:00:00.000000000 -0700
-+++ arch/s390/appldata/appldata_base.c	2006-01-30 22:25:24.000000000 -0700
-@@ -0,0 +1,650 @@
+diff -urN kernel-source-2.4.27.orig/arch/s390/appldata/appldata_mem.c arch/s390/appldata/appldata_mem.c
+--- kernel-source-2.4.27.orig/arch/s390/appldata/appldata_mem.c	1969-12-31 17:00:00.000000000 -0700
++++ arch/s390/appldata/appldata_mem.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,165 @@
 +/*
-+ * arch/s390/appldata/appldata_base.c
++ * arch/s390/appldata/appldata_mem.c
 + *
-+ * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
-+ * Exports appldata_register_ops() and appldata_unregister_ops() for the
-+ * data gathering modules.
++ * Data gathering module for Linux-VM Monitor Stream, Stage 1.
++ * Collects data related to memory management.
 + *
 + * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH.
 + *
 + * Author: Gerald Schaefer <geraldsc at de.ibm.com>
 + */
-+ 
++
 +#include <linux/config.h>
 +#include <linux/module.h>
 +#include <linux/init.h>
 +#include <linux/slab.h>
 +#include <linux/errno.h> 
-+#include <asm/uaccess.h>
++#include <linux/kernel_stat.h>
 +#include <asm/io.h>
-+#include <linux/interrupt.h>
-+#include <linux/proc_fs.h>
-+#include <asm/timer.h>
-+#include <linux/sysctl.h>
 +
 +#include "appldata.h"
 +
 +
-+#define MY_PRINT_NAME       "appldata"		/* for debug messages, etc. */
-+#define APPLDATA_INTERVAL   60			/* default monitoring 
-+						   interval in seconds */
-+#define VIRTUAL_SECOND	    0x0F4240000		/* nr. of TOD clock units
-+						   for one second */
-+#ifndef CONFIG_ARCH_S390X
++#define MY_PRINT_NAME "appldata_mem"		/* for debug messages, etc. */
++#define P2K(x) ((x) << (PAGE_SHIFT - 10))	/* Converts #Pages to KB */
 +
-+#define APPLDATA_START_INTERVAL_REC 0x00   	/* Function codes for */
-+#define APPLDATA_STOP_REC	    0x01	/* DIAG 0xDC	  */
-+#define APPLDATA_GEN_EVENT_RECORD   0x02
-+#define APPLDATA_START_CONFIG_REC   0x03
++/*
++ * Memory data
++ */
++struct appldata_mem_data {
++	u64 timestamp;
++	u32 sync_count_1;       /* after VM collected the record data, */
++	u32 sync_count_2;	/* sync_count_1 and sync_count_2 should be the
++				   same. If not, the record has been updated on
++				   the Linux side while VM was collecting the
++				   (possibly corrupt) data */
 +
-+#else
++	u64 pgpgin;		/* pages read from disk  */
++	u64 pgpgout;		/* pages written to disk */
++	u64 pswpin;		/* pages swapped in  */
++	u64 pswpout;		/* pages swapped out */
 +
-+#define APPLDATA_START_INTERVAL_REC 0x80
-+#define APPLDATA_STOP_REC   	    0x81
-+#define APPLDATA_GEN_EVENT_RECORD   0x82
-+#define APPLDATA_START_CONFIG_REC   0x83
++	u64 sharedram;		/* sharedram is currently set to 0 */
 +
-+#endif /* CONFIG_ARCH_S390X */
++	u64 totalram;		/* total main memory size */
++	u64 freeram;		/* free main memory size  */
++	u64 totalhigh;		/* total high memory size */
++	u64 freehigh;		/* free high memory size  */
 +
++	u64 bufferram;		/* memory reserved for buffers, free cache */
++	u64 cached;		/* size of (used) cache, w/o buffers */
++	u64 totalswap;		/* total swap space size */
++	u64 freeswap;		/* free swap space */
++} appldata_mem_data;
 +
-+/*
-+ * Parameter list for DIAGNOSE X'DC'
-+ */
-+#ifndef CONFIG_ARCH_S390X
-+struct appldata_parameter_list {
-+	u16 diag;		/* The DIAGNOSE code X'00DC'          */
-+	u8  function;		/* The function code for the DIAGNOSE */
-+	u8  parlist_length;	/* Length of the parameter list       */
-+	u32 product_id_addr;	/* Address of the 16-byte product ID  */
-+	u16 reserved;
-+	u16 buffer_length;	/* Length of the application data buffer  */
-+	u32 buffer_addr;	/* Address of the application data buffer */
-+};
-+#else
-+struct appldata_parameter_list {
-+	u16 diag;
-+	u8  function;
-+	u8  parlist_length;
-+	u32 unused01;
-+	u16 reserved;
-+	u16 buffer_length;
-+	u32 unused02;
-+	u64 product_id_addr;
-+	u64 buffer_addr;
-+};
-+#endif /* CONFIG_ARCH_S390X */
++
++static inline void appldata_debug_print(struct appldata_mem_data *mem_data)
++{
++	P_DEBUG("--- MEM - RECORD ---\n");
++	P_DEBUG("pgpgin    = %8llu KB\n", ULL(mem_data->pgpgin));
++	P_DEBUG("pgpgout   = %8llu KB\n", ULL(mem_data->pgpgout));
++	P_DEBUG("pswpin    = %8llu Pages\n", ULL(mem_data->pswpin));
++	P_DEBUG("pswpout   = %8llu Pages\n", ULL(mem_data->pswpout));
++	P_DEBUG("sharedram = %8llu KB\n", ULL(mem_data->sharedram));
++	P_DEBUG("totalram  = %8llu KB\n", ULL(mem_data->totalram));
++	P_DEBUG("freeram   = %8llu KB\n", ULL(mem_data->freeram));
++	P_DEBUG("totalhigh = %8llu KB\n", ULL(mem_data->totalhigh));
++	P_DEBUG("freehigh  = %8llu KB\n", ULL(mem_data->freehigh));
++	P_DEBUG("bufferram = %8llu KB\n", ULL(mem_data->bufferram));
++	P_DEBUG("cached    = %8llu KB\n", ULL(mem_data->cached));
++	P_DEBUG("totalswap = %8llu KB\n", ULL(mem_data->totalswap));
++	P_DEBUG("freeswap  = %8llu KB\n", ULL(mem_data->freeswap));
++	P_DEBUG("sync_count_1 = %u\n", mem_data->sync_count_1);
++	P_DEBUG("sync_count_2 = %u\n", mem_data->sync_count_2);
++	P_DEBUG("timestamp    = %llX\n", ULL(mem_data->timestamp));
++}
 +
 +/*
-+ * /proc entries (sysctl) 
++ * appldata_get_mem_data()
++ *
++ * gather memory data
 + */
-+static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata";
-+static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
-+		   			void *buffer, size_t *lenp);
-+static int appldata_interval_handler(ctl_table *ctl, int write, struct file *filp,
-+					   void *buffer, size_t *lenp);
++static void appldata_get_mem_data(void *data)
++{
++	struct sysinfo val;
++	struct appldata_mem_data *mem_data;
++	
++	mem_data = data;
++	mem_data->sync_count_1++;
 +
-+static struct ctl_table_header *appldata_sysctl_header;
-+static struct ctl_table appldata_table[] = {
-+	{
-+		.ctl_name	= CTL_APPLDATA_TIMER,
-+		.procname	= "timer",
-+		.mode		= S_IRUGO | S_IWUSR,
-+		.proc_handler	= &appldata_timer_handler,
-+	},
-+	{
-+		.ctl_name	= CTL_APPLDATA_INTERVAL,
-+		.procname	= "interval",
-+		.mode		= S_IRUGO | S_IWUSR,
-+		.proc_handler	= &appldata_interval_handler,
-+	},
-+	{ .ctl_name = 0 }
-+};
++	mem_data->pgpgin  = kstat.pgpgin >> 1;
++	mem_data->pgpgout = kstat.pgpgout >> 1;
++	mem_data->pswpin  = kstat.pswpin;
++	mem_data->pswpout = kstat.pswpout;
 +
-+static struct ctl_table appldata_dir_table[] = {
-+	{
-+		.ctl_name	= CTL_APPLDATA,
-+		.procname	= appldata_proc_name,
-+		.maxlen		= 0,
-+		.mode		= S_IRUGO | S_IXUGO,
-+		.child		= appldata_table,
-+	},
-+	{ .ctl_name = 0 }
-+};
++	si_meminfo(&val);
++	
++	mem_data->sharedram = val.sharedram;
++	mem_data->totalram  = P2K(val.totalram);
++	mem_data->freeram   = P2K(val.freeram);
++	mem_data->totalhigh = P2K(val.totalhigh);
++	mem_data->freehigh  = P2K(val.freehigh);
++	mem_data->bufferram = P2K(val.bufferram);
++	mem_data->cached    = P2K(page_cache_size - 
++					val.bufferram);
 +
-+/*
-+ * Timer
-+ */
-+static spinlock_t appldata_timer_lock = SPIN_LOCK_UNLOCKED;
-+static struct vtimer_list appldata_timer[NR_CPUS];
-+static int appldata_interval = APPLDATA_INTERVAL;
-+static int appldata_timer_active;
-+static atomic_t appldata_expire_count = ATOMIC_INIT(0);
-+static struct appldata_mod_vtimer_args {
-+	struct vtimer_list *timer;
-+	u64    expires;
-+} appldata_mod_vtimer_args;
++	si_swapinfo(&val);
 +
-+/*
-+ * Tasklet
-+ */
-+static struct tasklet_struct appldata_tasklet_struct;
++	mem_data->totalswap = P2K(val.totalswap);
++	mem_data->freeswap  = P2K(val.freeswap);
++	
++	mem_data->timestamp = get_clock();
++	mem_data->sync_count_2++;
++	appldata_debug_print(mem_data);
++}
 +
-+/*
-+ * Hook list
-+ */
-+static spinlock_t appldata_ops_lock = SPIN_LOCK_UNLOCKED;
-+static LIST_HEAD(appldata_ops_list);
++
++static struct appldata_ops ops = {
++	.ctl_nr    = CTL_APPLDATA_MEM,
++	.name      = "mem",
++	.record_nr = APPLDATA_RECORD_MEM_ID,
++	.size	   = sizeof(struct appldata_mem_data),
++	.callback  = &appldata_get_mem_data,
++	.data      = &appldata_mem_data,
++	.owner     = THIS_MODULE,
++};
 +
 +
-+/************************* timer, tasklet, DIAG ******************************/
 +/*
-+ * appldata_timer_function()
++ * appldata_mem_init()
 + *
-+ * schedule tasklet and reschedule timer
++ * init_data, register ops
 + */
-+static void appldata_timer_function(unsigned long data, struct pt_regs *regs)
++static int __init appldata_mem_init(void)
 +{
-+	P_DEBUG("   -= Timer =-\n");
-+	P_DEBUG("CPU: %i, expire: %i\n", smp_processor_id(),
-+		atomic_read(&appldata_expire_count));
-+	if (atomic_dec_and_test(&appldata_expire_count)) {
-+		atomic_set(&appldata_expire_count, smp_num_cpus);
-+		tasklet_schedule((struct tasklet_struct *) data);
++	int rc;
++
++	P_DEBUG("sizeof(mem) = %lu\n", sizeof(struct appldata_mem_data));
++
++	rc = appldata_register_ops(&ops);
++	if (rc != 0) {
++		P_ERROR("Error registering ops, rc = %i\n", rc);
++	} else {
++		P_DEBUG("%s-ops registered!\n", ops.name);
 +	}
++	return rc;
 +}
 +
 +/*
-+ * appldata_tasklet_function()
-+ *
-+ * call data gathering function for each (active) module
++ * appldata_mem_exit()
++ * 
++ * unregister ops
 + */
-+static void appldata_tasklet_function(unsigned long data)
++static void __exit appldata_mem_exit(void)
 +{
-+	struct list_head *lh;
-+	struct appldata_ops *ops;
-+	int i;
-+
-+	P_DEBUG("  -= Tasklet =-\n");
-+	i = 0;
-+	spin_lock(&appldata_ops_lock);
-+	list_for_each(lh, &appldata_ops_list) {
-+		ops = list_entry(lh, struct appldata_ops, list);
-+		P_DEBUG("list_for_each loop: %i) active = %u, name = %s\n",
-+			++i, ops->active, ops->name);
-+		if (ops->active == 1) {
-+			ops->callback(ops->data);
-+		}
-+	}
-+	spin_unlock(&appldata_ops_lock);
++	appldata_unregister_ops(&ops);
++	P_DEBUG("%s-ops unregistered!\n", ops.name);
 +}
 +
++
++module_init(appldata_mem_init);
++module_exit(appldata_mem_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Gerald Schaefer");
++MODULE_DESCRIPTION("Linux-VM Monitor Stream, MEMORY statistics");
+diff -urN kernel-source-2.4.27.orig/arch/s390/appldata/appldata_net_sum.c arch/s390/appldata/appldata_net_sum.c
+--- kernel-source-2.4.27.orig/arch/s390/appldata/appldata_net_sum.c	1969-12-31 17:00:00.000000000 -0700
++++ arch/s390/appldata/appldata_net_sum.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,184 @@
 +/*
-+ * appldata_mod_vtimer_wrap()
++ * arch/s390/appldata/appldata_net_sum.c
 + *
-+ * wrapper function for mod_virt_timer(), because smp_call_function_on()
-+ * accepts only one parameter.
++ * Data gathering module for Linux-VM Monitor Stream, Stage 1.
++ * Collects accumulated network statistics (Packets received/transmitted,
++ * dropped, errors, ...).
++ *
++ * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH.
++ *
++ * Author: Gerald Schaefer <geraldsc at de.ibm.com>
 + */
-+static void appldata_mod_vtimer_wrap(struct appldata_mod_vtimer_args *args) {
-+	mod_virt_timer(args->timer, args->expires);
-+}
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/errno.h> 
++#include <linux/kernel_stat.h>
++#include <linux/netdevice.h>
++
++#include "appldata.h"
++
++
++#define MY_PRINT_NAME	"appldata_net_sum"	/* for debug messages, etc. */
 +
 +/*
-+ * appldata_diag()
-+ *
-+ * prepare parameter list, issue DIAG 0xDC
++ * Network data
 + */
-+static int appldata_diag(char record_nr, u16 function, unsigned long buffer,
-+			u16 length)
++struct appldata_net_sum_data {
++	u64 timestamp;
++	u32 sync_count_1;	/* after VM collected the record data, */
++	u32 sync_count_2;	/* sync_count_1 and sync_count_2 should be the
++				   same. If not, the record has been updated on
++				   the Linux side while VM was collecting the
++				   (possibly corrupt) data */
++
++	u32 nr_interfaces;	/* nr. of network interfaces being monitored */
++
++	u32 padding;		/* next value is 64-bit aligned, so these */
++				/* 4 byte would be padded out by compiler */
++
++	u64 rx_packets;		/* total packets received        */
++	u64 tx_packets;		/* total packets transmitted     */
++	u64 rx_bytes;		/* total bytes received          */
++	u64 tx_bytes;		/* total bytes transmitted       */
++	u64 rx_errors;		/* bad packets received          */
++	u64 tx_errors;		/* packet transmit problems      */
++	u64 rx_dropped;		/* no space in linux buffers     */
++	u64 tx_dropped;		/* no space available in linux   */
++	u64 collisions;		/* collisions while transmitting */
++} appldata_net_sum_data;
++
++
++static inline void appldata_print_debug(struct appldata_net_sum_data *net_data)
 +{
-+	unsigned long ry;
-+	struct appldata_product_id {
-+		char prod_nr[7];			/* product nr. */
-+		char prod_fn[2];			/* product function */
-+		char record_nr;				/* record nr. */
-+		char version_nr[2];			/* version */
-+		char release_nr[2];			/* release */
-+		char mod_lvl[2];			/* modification lvl. */
-+	} appldata_product_id = {
-+	/* all strings are EBCDIC, record_nr is byte */
-+		.prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4,
-+				0xE7, 0xD2, 0xD9},	/* "LINUXKR" */
-+		.prod_fn    = {0xD5, 0xD3},		/* "NL" */
-+		.record_nr  = record_nr,
-+		.version_nr = {0xF2, 0xF4},		/* "24" */
-+		.release_nr = {0xF0, 0xF1},		/* "01" */
-+		.mod_lvl    = {0xF0, 0xF0},		/* "00" */
-+	};
-+	struct appldata_parameter_list appldata_parameter_list = {
-+				.diag = 0xDC,
-+				.function = function,
-+				.parlist_length = 
-+					sizeof(appldata_parameter_list),
-+				.buffer_length = length,
-+				.product_id_addr = 
-+					(unsigned long) &appldata_product_id,
-+				.buffer_addr = virt_to_phys((void *) buffer)
-+	};
++	P_DEBUG("--- NET - RECORD ---\n");
 +
-+        if (!MACHINE_IS_VM)
-+                return -ENOSYS;
-+	ry = -1;
-+	asm volatile(
-+			"diag %1,%0,0xDC\n\t"
-+			: "=d" (ry) : "d" (&(appldata_parameter_list)) : "cc");
-+	return (int) ry;
-+}
-+/********************** timer, tasklet, DIAG <END> ***************************/
++	P_DEBUG("nr_interfaces = %u\n", net_data->nr_interfaces);
++	P_DEBUG("rx_packets    = %8llu\n", ULL(net_data->rx_packets));
++	P_DEBUG("tx_packets    = %8llu\n", ULL(net_data->tx_packets));
++	P_DEBUG("rx_bytes      = %8llu\n", ULL(net_data->rx_bytes));
++	P_DEBUG("tx_bytes      = %8llu\n", ULL(net_data->tx_bytes));
++	P_DEBUG("rx_errors     = %8llu\n", ULL(net_data->rx_errors));
++	P_DEBUG("tx_errors     = %8llu\n", ULL(net_data->tx_errors));
++	P_DEBUG("rx_dropped    = %8llu\n", ULL(net_data->rx_dropped));
++	P_DEBUG("tx_dropped    = %8llu\n", ULL(net_data->tx_dropped));
++	P_DEBUG("collisions    = %8llu\n", ULL(net_data->collisions));
 +
++	P_DEBUG("sync_count_1 = %u\n", net_data->sync_count_1);
++	P_DEBUG("sync_count_2 = %u\n", net_data->sync_count_2);
++	P_DEBUG("timestamp    = %llX\n", ULL(net_data->timestamp));
++}
 +
-+/****************************** /proc stuff **********************************/
 +/*
-+ * appldata_timer_handler()
-+ * 
-+ * Start/Stop timer, show status of timer (0 = not active, 1 = active)
++ * appldata_get_net_sum_data()
++ *
++ * gather accumulated network statistics
 + */
-+static int
-+appldata_timer_handler(ctl_table *ctl, int write, struct file *filp,
-+			   void *buffer, size_t *lenp)
++static void appldata_get_net_sum_data(void *data)
 +{
-+	int len, i;
-+	u64 per_cpu_interval;
-+	char buf[2];
++	int i;
++	struct appldata_net_sum_data *net_data;
++	struct net_device *dev;
++	struct net_device_stats *stats;
++	unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes, rx_errors,
++			tx_errors, rx_dropped, tx_dropped, collisions;
 +
-+	if (!*lenp || filp->f_pos) {
-+		*lenp = 0;
-+		return 0;
-+	}
-+	if (!write) {
-+		len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n");
-+		if (len > *lenp)
-+			len = *lenp;
-+		if (copy_to_user(buffer, buf, len))
-+			return -EFAULT;
-+		goto out;
-+	}
-+	
-+	len = *lenp;
-+	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
-+		return -EFAULT;
++	net_data = data;
++	net_data->sync_count_1++;
 +
-+	per_cpu_interval = (u64) (appldata_interval / smp_num_cpus) *
-+				VIRTUAL_SECOND;
-+	spin_lock(&appldata_timer_lock);
-+	if ((buf[0] == '1') && (!appldata_timer_active)) {
-+		for (i = 0; i < smp_num_cpus; i++) {
-+			appldata_timer[i].expires = per_cpu_interval;
-+			smp_call_function_on(add_virt_timer_periodic,
-+						&appldata_timer[i], 0, 1, i);
-+		}
-+		appldata_timer_active = 1;
-+		P_STATUS("Monitoring timer started.\n");
-+	} else if ((buf[0] == '0') && (appldata_timer_active)) {
-+		for (i = 0; i < smp_num_cpus; i++) {
-+			smp_call_function_on((void *) del_virt_timer,
-+						&appldata_timer[i],
-+						0, 1, i);
++	i = 0;
++	rx_packets = 0;
++	tx_packets = 0;
++	rx_bytes   = 0;
++	tx_bytes   = 0;
++	rx_errors  = 0;
++	tx_errors  = 0;
++	rx_dropped = 0;
++	tx_dropped = 0;
++	collisions = 0;
++	read_lock(&dev_base_lock);
++	for (dev = dev_base; dev != NULL; dev = dev->next) {
++		if (dev->get_stats == NULL) {
++			continue;
 +		}
-+		appldata_timer_active = 0;
-+		P_STATUS("Monitoring timer stopped.\n");
++		stats = dev->get_stats(dev);
++		rx_packets += stats->rx_packets;
++		tx_packets += stats->tx_packets;
++		rx_bytes   += stats->rx_bytes;
++		tx_bytes   += stats->tx_bytes;
++		rx_errors  += stats->rx_errors;
++		tx_errors  += stats->tx_errors;
++		rx_dropped += stats->rx_dropped;
++		tx_dropped += stats->tx_dropped;
++		collisions += stats->collisions;
++		i++;
 +	}
-+	spin_unlock(&appldata_timer_lock);
-+out:
-+	*lenp = len;
-+	filp->f_pos += len;
-+	return 0;
++	read_unlock(&dev_base_lock);
++	net_data->nr_interfaces = i;
++	net_data->rx_packets = rx_packets;
++	net_data->tx_packets = tx_packets;
++	net_data->rx_bytes   = rx_bytes;
++	net_data->tx_bytes   = tx_bytes;
++	net_data->rx_errors  = rx_errors;
++	net_data->tx_errors  = tx_errors;
++	net_data->rx_dropped = rx_dropped;
++	net_data->tx_dropped = tx_dropped;
++	net_data->collisions = collisions;
++
++	net_data->timestamp = get_clock();
++	net_data->sync_count_2++;
++	appldata_print_debug(net_data);
 +}
 +
++
++static struct appldata_ops ops = {
++	.ctl_nr    = CTL_APPLDATA_NET_SUM,
++	.name	   = "net_sum",
++	.record_nr = APPLDATA_RECORD_NET_SUM_ID,
++	.size	   = sizeof(struct appldata_net_sum_data),
++	.callback  = &appldata_get_net_sum_data,
++	.data      = &appldata_net_sum_data,
++	.owner     = THIS_MODULE,
++};
++
++
 +/*
-+ * appldata_interval_handler()
-+ * 
-+ * Set timer interval for collection of data (in seconds), show current
-+ * timer interval.
++ * appldata_net_init()
++ *
++ * init data, register ops
 + */
-+static int
-+appldata_interval_handler(ctl_table *ctl, int write, struct file *filp,
-+			   void *buffer, size_t *lenp)
++static int __init appldata_net_init(void)
 +{
-+	int len, i;
-+	u64 per_cpu_interval;
-+	char buf[16];
-+	
-+	if (!*lenp || filp->f_pos) {
-+		*lenp = 0;
-+		return 0;
-+	}
-+	if (!write) {
-+		len = sprintf(buf, "%i\n", appldata_interval);
-+		if (len > *lenp)
-+			len = *lenp;
-+		if (copy_to_user(buffer, buf, len))
-+			return -EFAULT;
-+		goto out;
-+	}
++	int rc;
 +
-+	len = *lenp;
-+	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len)) {
-+		return -EFAULT;
-+	}
-+	sscanf(buf, "%i", &i);
-+	if (i >= smp_num_cpus) {
-+		spin_lock(&appldata_timer_lock);
-+		per_cpu_interval = (u64) (i / smp_num_cpus) * VIRTUAL_SECOND;
-+		appldata_interval = i;
-+		if (appldata_timer_active) {
-+			for (i = 0; i < smp_num_cpus; i++) {
-+				appldata_mod_vtimer_args.timer =
-+							&appldata_timer[i];
-+				appldata_mod_vtimer_args.expires =
-+							per_cpu_interval;
-+				smp_call_function_on(
-+					(void *) appldata_mod_vtimer_wrap,
-+					&appldata_mod_vtimer_args,
-+					0, 1, i);
-+			}
-+		}
-+		spin_unlock(&appldata_timer_lock);
-+		P_STATUS("Monitoring interval set to %u seconds.\n",
-+			appldata_interval);
++	P_DEBUG("sizeof(net) = %lu\n", sizeof(struct appldata_net_sum_data));
++
++	rc = appldata_register_ops(&ops);
++	if (rc != 0) {
++		P_ERROR("Error registering ops, rc = %i\n", rc);
 +	} else {
-+		P_ERROR("Timer interval has to be >= [nr. cpus] seconds, i.e. %i seconds!\n",
-+			smp_num_cpus);
-+		return -EINVAL;
++		P_DEBUG("%s-ops registered!\n", ops.name);
 +	}
-+out:
-+	*lenp = len;
-+	filp->f_pos += len;
-+	return 0;
++	return rc;
 +}
 +
 +/*
-+ * appldata_generic_handler()
++ * appldata_net_exit()
 + * 
-+ * Generic start/stop monitoring and DIAG, show status of 
-+ * monitoring (0 = not in process, 1 = in process)
++ * unregister ops
 + */
-+static int
-+appldata_generic_handler(ctl_table *ctl, int write, struct file *filp,
-+			   void *buffer, size_t *lenp)
++static void __exit appldata_net_exit(void)
 +{
-+	struct appldata_ops *ops;
-+	int rc, len;
-+	char buf[2];
++	appldata_unregister_ops(&ops);
++	P_DEBUG("%s-ops unregistered!\n", ops.name);
++}
 +
-+	ops = ctl->data;
-+	if (!*lenp || filp->f_pos) {
-+		*lenp = 0;
-+		return 0;
-+	}
-+	if (!write) {
-+		len = sprintf(buf, ops->active ? "1\n" : "0\n");
-+		if (len > *lenp)
-+			len = *lenp;
-+		if (copy_to_user(buffer, buf, len))
-+			return -EFAULT;
-+		goto out;
-+	}
-+
-+	len = *lenp;
-+	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
-+		return -EFAULT;
-+
-+	spin_lock_bh(&appldata_ops_lock);
-+	if ((buf[0] == '1') && (ops->active == 0)) {
-+		ops->active = 1;
-+		ops->callback(ops->data);	// init record
-+		rc = appldata_diag(ops->record_nr, 
-+					APPLDATA_START_INTERVAL_REC,
-+					(unsigned long) ops->data, ops->size);
-+		if (rc != 0) {
-+			P_ERROR("START DIAG 0xDC for %s failed, "
-+				"return code: %d\n", ops->name, rc);
-+			ops->active = 0;
-+		} else {
-+			P_STATUS("Monitoring %s data enabled, "
-+				"DIAG 0xDC started.\n", ops->name);
-+		}
-+	} else if ((buf[0] == '0') && (ops->active == 1)) {
-+		ops->active = 0;
-+		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
-+				(unsigned long) ops->data, ops->size);
-+		if (rc != 0) {
-+			P_ERROR("STOP DIAG 0xDC for %s failed, "
-+				"return code: %d\n", ops->name, rc);
-+		} else {
-+			P_STATUS("Monitoring %s data disabled, "
-+				"DIAG 0xDC stopped.\n", ops->name);
-+		}
-+	}
-+	spin_unlock_bh(&appldata_ops_lock);
-+out:
-+	*lenp = len;
-+	filp->f_pos += len;
-+	return 0;
-+}
-+/*************************** /proc stuff <END> *******************************/
-+
-+
-+/************************* module-ops management *****************************/
-+/*
-+ * appldata_register_ops()
-+ *
-+ * update ops list, register /proc entries
-+ */
-+int appldata_register_ops(struct appldata_ops *ops)
-+{
-+	struct list_head *lh;
-+	struct appldata_ops *tmp_ops;
-+	int rc, i;
-+	
-+	rc = 0;
-+	i = 0;
-+
-+	if ((ops->size > APPLDATA_MAX_REC_SIZE) ||
-+		(ops->size < 0)){
-+		P_ERROR("Invalid size of %s record = %i, maximum = %i!\n",
-+			ops->name, ops->size, APPLDATA_MAX_REC_SIZE);
-+		rc = -ENOMEM;
-+		goto out;
-+	}
-+	if ((ops->ctl_nr == CTL_APPLDATA) ||
-+	    (ops->ctl_nr == CTL_APPLDATA_TIMER) ||
-+	    (ops->ctl_nr == CTL_APPLDATA_INTERVAL)) {
-+		P_ERROR("ctl_nr %i already in use!\n", ops->ctl_nr);
-+		rc = -EBUSY;
-+		goto out;
-+	}
-+	ops->ctl_table = kmalloc(4*sizeof(struct ctl_table), GFP_KERNEL);
-+	if (ops->ctl_table == NULL) {
-+		P_ERROR("Not enough memory for %s ctl_table!\n", ops->name);
-+		rc = -ENOMEM;
-+		goto out;
-+	}
-+	memset(ops->ctl_table, 0, 4*sizeof(struct ctl_table));
-+
-+	spin_lock_bh(&appldata_ops_lock);
-+	list_for_each(lh, &appldata_ops_list) {
-+		tmp_ops = list_entry(lh, struct appldata_ops, list);
-+		P_DEBUG("register_ops loop: %i) name = %s, ctl = %i\n",
-+			++i, tmp_ops->name, tmp_ops->ctl_nr);
-+		P_DEBUG("Comparing %s (ctl %i) with %s (ctl %i)\n",
-+			tmp_ops->name, tmp_ops->ctl_nr, ops->name,
-+			ops->ctl_nr);
-+		if (strncmp(tmp_ops->name, ops->name,
-+				APPLDATA_PROC_NAME_LENGTH) == 0) {
-+			spin_unlock_bh(&appldata_ops_lock);
-+			P_ERROR("Name \"%s\" already exists!\n", ops->name);
-+			kfree(ops->ctl_table);
-+			rc = -EBUSY;
-+			goto out;
-+		}
-+		if (tmp_ops->ctl_nr == ops->ctl_nr) {
-+			spin_unlock_bh(&appldata_ops_lock);
-+			P_ERROR("ctl_nr %i already registered!\n", ops->ctl_nr);
-+			kfree(ops->ctl_table);
-+			rc = -EBUSY;
-+			goto out;
-+		}
-+	}
-+	list_add(&ops->list, &appldata_ops_list);
-+	spin_unlock_bh(&appldata_ops_lock);
-+
-+	ops->ctl_table[0].ctl_name = CTL_APPLDATA;
-+	ops->ctl_table[0].procname = appldata_proc_name;
-+	ops->ctl_table[0].maxlen   = 0;
-+	ops->ctl_table[0].mode     = S_IRUGO | S_IXUGO;
-+	ops->ctl_table[0].child    = &ops->ctl_table[2];
-+
-+	ops->ctl_table[1].ctl_name = 0;
-+
-+	ops->ctl_table[2].ctl_name = ops->ctl_nr;
-+	ops->ctl_table[2].procname = ops->name;
-+	ops->ctl_table[2].mode     = S_IRUGO | S_IWUSR;
-+	ops->ctl_table[2].proc_handler = appldata_generic_handler;
-+	ops->ctl_table[2].data = ops;
-+
-+	ops->ctl_table[3].ctl_name = 0;
-+
-+	ops->sysctl_header = register_sysctl_table(ops->ctl_table,1);
-+	ops->ctl_table[2].de->owner = ops->owner;
-+	P_STATUS("%s-ops registered!\n", ops->name);
-+out:
-+	return rc;
-+}
-+
-+/*
-+ * appldata_unregister_ops()
-+ *
-+ * update ops list, unregister /proc entries, stop DIAG if necessary
-+ */
-+void appldata_unregister_ops(struct appldata_ops *ops)
-+{
-+	int rc;
-+
-+	unregister_sysctl_table(ops->sysctl_header);
-+	kfree(ops->ctl_table);
-+	if (ops->active == 1) {
-+		ops->active = 0;
-+		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
-+				(unsigned long) ops->data, ops->size);
-+		if (rc != 0) {
-+			P_ERROR("STOP DIAG 0xDC for %s failed, "
-+				"return code: %d\n", ops->name, rc);
-+		} else {
-+			P_STATUS("Monitoring %s data disabled, "
-+				"DIAG 0xDC stopped.\n", ops->name);
-+		}
-+
-+	}
-+	spin_lock_bh(&appldata_ops_lock);
-+	list_del(&ops->list);
-+	spin_unlock_bh(&appldata_ops_lock);
-+	P_STATUS("%s-ops unregistered!\n", ops->name);
-+}
-+/********************** module-ops management <END> **************************/
-+
-+
-+/******************************* init / exit *********************************/
-+/*
-+ * appldata_init()
-+ *
-+ * init timer and tasklet, register /proc entries
-+ */
-+static int __init appldata_init(void)
-+{
-+	int i;
-+	
-+	P_DEBUG("sizeof(parameter_list) = %lu\n",
-+		sizeof(struct appldata_parameter_list));
-+
-+	for (i = 0; i < smp_num_cpus; i++) {
-+		smp_call_function_on((void *) init_virt_timer,
-+					&appldata_timer[i],
-+					0, 1, i);
-+		appldata_timer[i].function = appldata_timer_function;
-+		appldata_timer[i].data = (unsigned long)
-+						&appldata_tasklet_struct;
-+	}
-+	atomic_set(&appldata_expire_count, smp_num_cpus);
-+
-+	appldata_sysctl_header = register_sysctl_table(appldata_dir_table, 1);
-+#ifdef MODULE
-+	appldata_dir_table[0].de->owner = THIS_MODULE;
-+	appldata_table[0].de->owner = THIS_MODULE;
-+	appldata_table[1].de->owner = THIS_MODULE;
-+#endif
-+	
-+	tasklet_init(&appldata_tasklet_struct, appldata_tasklet_function, 0);
-+	P_DEBUG("Base interface initialized.\n");
-+	return 0;
-+}
-+
-+/*
-+ * appldata_exit()
-+ * 
-+ * stop timer and tasklet, unregister /proc entries
-+ */
-+static void __exit appldata_exit(void)
-+{
-+	struct list_head *lh;
-+	struct appldata_ops *ops;
-+	int rc, i;
-+	
-+	P_DEBUG("Unloading module ...\n");
-+	/*
-+	 * ops list should be empty, but just in case something went wrong...
-+	 */
-+	spin_lock_bh(&appldata_ops_lock);
-+	list_for_each(lh, &appldata_ops_list) {
-+		ops = list_entry(lh, struct appldata_ops, list);
-+		rc = appldata_diag(ops->record_nr, APPLDATA_STOP_REC,
-+				(unsigned long) ops->data, ops->size);
-+		if (rc != 0) {
-+			P_ERROR("STOP DIAG 0xDC for %s failed, "
-+				"return code: %d\n", ops->name, rc);
-+		}
-+	}
-+	spin_unlock_bh(&appldata_ops_lock);
-+	for (i = 0; i < smp_num_cpus; i++) {
-+		smp_call_function_on((void *) del_virt_timer, &appldata_timer[i],
-+					0, 1, i);
-+	}
-+	appldata_timer_active = 0;
-+
-+	unregister_sysctl_table(appldata_sysctl_header);
-+	
-+	tasklet_kill(&appldata_tasklet_struct);
-+
-+	P_DEBUG("... module unloaded!\n");
-+}
-+/**************************** init / exit <END> ******************************/
 +
++module_init(appldata_net_init);
++module_exit(appldata_net_exit);
 +
-+module_init(appldata_init);
-+module_exit(appldata_exit);
 +MODULE_LICENSE("GPL");
 +MODULE_AUTHOR("Gerald Schaefer");
-+MODULE_DESCRIPTION("Linux-VM Monitor Stream, base infrastructure");
-+
-+EXPORT_SYMBOL_GPL(appldata_register_ops);
-+EXPORT_SYMBOL_GPL(appldata_unregister_ops);
-+
-+#ifdef MODULE
-+/*
-+ * Kernel symbols needed by appldata_mem and appldata_os modules.
-+ * However, if this file is compiled as a module (for testing only), these
-+ * symbols are not exported. In this case, we define them locally and export
-+ * those.
-+ */
-+void si_swapinfo(struct sysinfo *val)
-+{
-+	val->freeswap = -1ul;
-+	val->totalswap = -1ul;
-+}
-+unsigned long avenrun[3] = {-1 - FIXED_1/200, -1 - FIXED_1/200,
-+				-1 - FIXED_1/200};
-+int nr_threads = -1;
-+#endif /* MODULE */
-+EXPORT_SYMBOL_GPL(si_swapinfo);
-+EXPORT_SYMBOL_GPL(page_cache_size);
-+EXPORT_SYMBOL_GPL(nr_threads);
-+EXPORT_SYMBOL_GPL(avenrun);
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata_mem.c arch/s390/appldata/appldata_mem.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata_mem.c	1969-12-31 17:00:00.000000000 -0700
-+++ arch/s390/appldata/appldata_mem.c	2006-01-30 22:25:24.000000000 -0700
-@@ -0,0 +1,165 @@
++MODULE_DESCRIPTION("Linux-VM Monitor Stream, accumulated network statistics");
+diff -urN kernel-source-2.4.27.orig/arch/s390/appldata/appldata_os.c arch/s390/appldata/appldata_os.c
+--- kernel-source-2.4.27.orig/arch/s390/appldata/appldata_os.c	1969-12-31 17:00:00.000000000 -0700
++++ arch/s390/appldata/appldata_os.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,206 @@
 +/*
-+ * arch/s390/appldata/appldata_mem.c
++ * arch/s390/appldata/appldata_os.c
 + *
 + * Data gathering module for Linux-VM Monitor Stream, Stage 1.
-+ * Collects data related to memory management.
++ * Collects misc. OS related data (CPU utilization, running processes).
 + *
 + * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH.
 + *
@@ -2215,555 +1092,215 @@
 +#include <linux/slab.h>
 +#include <linux/errno.h> 
 +#include <linux/kernel_stat.h>
-+#include <asm/io.h>
++#include <linux/netdevice.h>
 +
 +#include "appldata.h"
 +
 +
-+#define MY_PRINT_NAME "appldata_mem"		/* for debug messages, etc. */
-+#define P2K(x) ((x) << (PAGE_SHIFT - 10))	/* Converts #Pages to KB */
++#define MY_PRINT_NAME	"appldata_os"		/* for debug messages, etc. */
++#define LOAD_INT(x) ((x) >> FSHIFT)
++#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
 +
 +/*
-+ * Memory data
++ * OS data
 + */
-+struct appldata_mem_data {
++struct appldata_os_per_cpu {
++	u32 per_cpu_user;	/* timer ticks spent in user mode   */
++	u32 per_cpu_nice;	/* ... spent with modified priority */
++	u32 per_cpu_system;	/* ... spent in kernel mode         */
++	u32 per_cpu_idle;	/* ... spent in idle mode           */
++};
++
++struct appldata_os_data {
 +	u64 timestamp;
-+	u32 sync_count_1;       /* after VM collected the record data, */
++	u32 sync_count_1;	/* after VM collected the record data, */
 +	u32 sync_count_2;	/* sync_count_1 and sync_count_2 should be the
 +				   same. If not, the record has been updated on
 +				   the Linux side while VM was collecting the
 +				   (possibly corrupt) data */
 +
-+	u64 pgpgin;		/* pages read from disk  */
-+	u64 pgpgout;		/* pages written to disk */
-+	u64 pswpin;		/* pages swapped in  */
-+	u64 pswpout;		/* pages swapped out */
++	u32 nr_cpus;		/* number of (virtual) CPUs        */
++	u32 per_cpu_size;	/* size of the per-cpu data struct */
++	u32 cpu_offset;		/* offset of the first per-cpu data struct */
 +
-+	u64 sharedram;		/* sharedram is currently set to 0 */
++	u32 nr_running;		/* number of runnable threads       */
++	u32 nr_threads;		/* number of threads                */
 +
-+	u64 totalram;		/* total main memory size */
-+	u64 freeram;		/* free main memory size  */
-+	u64 totalhigh;		/* total high memory size */
-+	u64 freehigh;		/* free high memory size  */
++	u32 avenrun[3];		/* average nr. of running processes during */
++				/* the last 1, 5 and 15 minutes */
++	
++	/* per cpu data */
++	struct appldata_os_per_cpu os_cpu[0];
++};
 +
-+	u64 bufferram;		/* memory reserved for buffers, free cache */
-+	u64 cached;		/* size of (used) cache, w/o buffers */
-+	u64 totalswap;		/* total swap space size */
-+	u64 freeswap;		/* free swap space */
-+} appldata_mem_data;
++static struct appldata_os_data *appldata_os_data;
 +
 +
-+static inline void appldata_debug_print(struct appldata_mem_data *mem_data)
++static inline void appldata_print_debug(struct appldata_os_data *os_data)
 +{
-+	P_DEBUG("--- MEM - RECORD ---\n");
-+	P_DEBUG("pgpgin    = %8llu KB\n", ULL(mem_data->pgpgin));
-+	P_DEBUG("pgpgout   = %8llu KB\n", ULL(mem_data->pgpgout));
-+	P_DEBUG("pswpin    = %8llu Pages\n", ULL(mem_data->pswpin));
-+	P_DEBUG("pswpout   = %8llu Pages\n", ULL(mem_data->pswpout));
-+	P_DEBUG("sharedram = %8llu KB\n", ULL(mem_data->sharedram));
-+	P_DEBUG("totalram  = %8llu KB\n", ULL(mem_data->totalram));
-+	P_DEBUG("freeram   = %8llu KB\n", ULL(mem_data->freeram));
-+	P_DEBUG("totalhigh = %8llu KB\n", ULL(mem_data->totalhigh));
-+	P_DEBUG("freehigh  = %8llu KB\n", ULL(mem_data->freehigh));
-+	P_DEBUG("bufferram = %8llu KB\n", ULL(mem_data->bufferram));
-+	P_DEBUG("cached    = %8llu KB\n", ULL(mem_data->cached));
-+	P_DEBUG("totalswap = %8llu KB\n", ULL(mem_data->totalswap));
-+	P_DEBUG("freeswap  = %8llu KB\n", ULL(mem_data->freeswap));
-+	P_DEBUG("sync_count_1 = %u\n", mem_data->sync_count_1);
-+	P_DEBUG("sync_count_2 = %u\n", mem_data->sync_count_2);
-+	P_DEBUG("timestamp    = %llX\n", ULL(mem_data->timestamp));
++	int i;
++	unsigned int a0, a1, a2;
++
++	P_DEBUG("--- OS - RECORD ---\n");
++	P_DEBUG("nr_threads   = %u\n", os_data->nr_threads);
++	P_DEBUG("nr_running   = %u\n", os_data->nr_running);
++	P_DEBUG("avenrun(int) = %8x / %8x / %8x\n", os_data->avenrun[0],
++		os_data->avenrun[1], os_data->avenrun[2]);
++	a0 = os_data->avenrun[0];
++	a1 = os_data->avenrun[1];
++	a2 = os_data->avenrun[2];
++	P_DEBUG("avenrun(float) = %d.%02d / %d.%02d / %d.%02d\n",
++		LOAD_INT(a0), LOAD_FRAC(a0), LOAD_INT(a1), LOAD_FRAC(a1),
++		LOAD_INT(a2), LOAD_FRAC(a2));
++
++	for (i = 0; i < smp_num_cpus; i++) {
++		P_DEBUG("cpu%u : user = %u, nice = %u, system = %u, "
++			"idle = %u\n",
++				i,
++				os_data->os_cpu[i].per_cpu_user,
++				os_data->os_cpu[i].per_cpu_nice,
++				os_data->os_cpu[i].per_cpu_system,
++				os_data->os_cpu[i].per_cpu_idle);
++	}
++
++	P_DEBUG("sync_count_1 = %u\n", os_data->sync_count_1);
++	P_DEBUG("sync_count_2 = %u\n", os_data->sync_count_2);
++	P_DEBUG("timestamp    = %llX\n", ULL(os_data->timestamp));
 +}
 +
 +/*
-+ * appldata_get_mem_data()
++ * appldata_get_os_data()
 + *
-+ * gather memory data
++ * gather OS data
 + */
-+static void appldata_get_mem_data(void *data)
++static void appldata_get_os_data(void *data)
 +{
-+	struct sysinfo val;
-+	struct appldata_mem_data *mem_data;
-+	
-+	mem_data = data;
-+	mem_data->sync_count_1++;
++	int i;
++	struct appldata_os_data *os_data;
 +
-+	mem_data->pgpgin  = kstat.pgpgin >> 1;
-+	mem_data->pgpgout = kstat.pgpgout >> 1;
-+	mem_data->pswpin  = kstat.pswpin;
-+	mem_data->pswpout = kstat.pswpout;
++	os_data = data;
++	os_data->sync_count_1++;
++	os_data->nr_cpus = smp_num_cpus;
 +
-+	si_meminfo(&val);
-+	
-+	mem_data->sharedram = val.sharedram;
-+	mem_data->totalram  = P2K(val.totalram);
-+	mem_data->freeram   = P2K(val.freeram);
-+	mem_data->totalhigh = P2K(val.totalhigh);
-+	mem_data->freehigh  = P2K(val.freehigh);
-+	mem_data->bufferram = P2K(val.bufferram);
-+	mem_data->cached    = P2K(page_cache_size - 
-+					val.bufferram);
++	os_data->nr_threads = nr_threads;
++	os_data->nr_running = nr_running;
++	os_data->avenrun[0] = (u32) avenrun[0] + (FIXED_1/200);
++	os_data->avenrun[1] = (u32) avenrun[1] + (FIXED_1/200);
++	os_data->avenrun[2] = (u32) avenrun[2] + (FIXED_1/200);
 +
-+	si_swapinfo(&val);
++	for (i = 0; i < smp_num_cpus; i++) {
++		os_data->os_cpu[i].per_cpu_user = 
++				    kstat.per_cpu_user[cpu_logical_map(i)];
++		os_data->os_cpu[i].per_cpu_nice =
++				    kstat.per_cpu_nice[cpu_logical_map(i)];
++		os_data->os_cpu[i].per_cpu_system =
++				    kstat.per_cpu_system[cpu_logical_map(i)];
++		os_data->os_cpu[i].per_cpu_idle = jiffies - (
++					os_data->os_cpu[i].per_cpu_user
++				      + os_data->os_cpu[i].per_cpu_nice
++				      + os_data->os_cpu[i].per_cpu_system);
++	}
 +
-+	mem_data->totalswap = P2K(val.totalswap);
-+	mem_data->freeswap  = P2K(val.freeswap);
-+	
-+	mem_data->timestamp = get_clock();
-+	mem_data->sync_count_2++;
-+	appldata_debug_print(mem_data);
++	os_data->timestamp = get_clock();
++	os_data->sync_count_2++;
++	appldata_print_debug(os_data);
 +}
 +
 +
 +static struct appldata_ops ops = {
-+	.ctl_nr    = CTL_APPLDATA_MEM,
-+	.name      = "mem",
-+	.record_nr = APPLDATA_RECORD_MEM_ID,
-+	.size	   = sizeof(struct appldata_mem_data),
-+	.callback  = &appldata_get_mem_data,
-+	.data      = &appldata_mem_data,
++	.ctl_nr    = CTL_APPLDATA_OS,
++	.name	   = "os",
++	.record_nr = APPLDATA_RECORD_OS_ID,
++	.callback  = &appldata_get_os_data,
 +	.owner     = THIS_MODULE,
 +};
 +
 +
 +/*
-+ * appldata_mem_init()
++ * appldata_os_init()
 + *
-+ * init_data, register ops
++ * init data, register ops
 + */
-+static int __init appldata_mem_init(void)
++static int __init appldata_os_init(void)
 +{
-+	int rc;
++	int rc, size;
 +
-+	P_DEBUG("sizeof(mem) = %lu\n", sizeof(struct appldata_mem_data));
++	size = sizeof(struct appldata_os_data) + 
++		(smp_num_cpus * sizeof(struct appldata_os_per_cpu));
++	if (size > APPLDATA_MAX_REC_SIZE) {
++		P_ERROR("Size of record = %i, bigger than maximum (%i)!\n",
++			size, APPLDATA_MAX_REC_SIZE);
++		rc = -ENOMEM;
++		goto out;
++	}
++	P_DEBUG("sizeof(os) = %i, sizeof(os_cpu) = %lu\n", size,
++		sizeof(struct appldata_os_per_cpu));
++
++	appldata_os_data = kmalloc(size, GFP_DMA);
++	if (appldata_os_data == NULL) {
++		P_ERROR("No memory for %s!\n", ops.name);
++		rc = -ENOMEM;
++		goto out;
++	}
++	memset(appldata_os_data, 0, size);
++
++	appldata_os_data->per_cpu_size = sizeof(struct appldata_os_per_cpu);
++	appldata_os_data->cpu_offset   = offsetof(struct appldata_os_data,
++							os_cpu);
++	P_DEBUG("cpu offset = %u\n", appldata_os_data->cpu_offset);
 +
++	ops.data = appldata_os_data;
++	ops.size = size;
 +	rc = appldata_register_ops(&ops);
 +	if (rc != 0) {
 +		P_ERROR("Error registering ops, rc = %i\n", rc);
++		kfree(appldata_os_data);
 +	} else {
 +		P_DEBUG("%s-ops registered!\n", ops.name);
 +	}
++out:
 +	return rc;
 +}
 +
 +/*
-+ * appldata_mem_exit()
++ * appldata_os_exit()
 + * 
 + * unregister ops
 + */
-+static void __exit appldata_mem_exit(void)
++static void __exit appldata_os_exit(void)
 +{
 +	appldata_unregister_ops(&ops);
++	kfree(appldata_os_data);
 +	P_DEBUG("%s-ops unregistered!\n", ops.name);
 +}
 +
 +
-+module_init(appldata_mem_init);
-+module_exit(appldata_mem_exit);
++module_init(appldata_os_init);
++module_exit(appldata_os_exit);
 +
 +MODULE_LICENSE("GPL");
 +MODULE_AUTHOR("Gerald Schaefer");
-+MODULE_DESCRIPTION("Linux-VM Monitor Stream, MEMORY statistics");
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata_net_sum.c arch/s390/appldata/appldata_net_sum.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata_net_sum.c	1969-12-31 17:00:00.000000000 -0700
-+++ arch/s390/appldata/appldata_net_sum.c	2006-01-30 22:25:24.000000000 -0700
-@@ -0,0 +1,184 @@
-+/*
-+ * arch/s390/appldata/appldata_net_sum.c
-+ *
-+ * Data gathering module for Linux-VM Monitor Stream, Stage 1.
-+ * Collects accumulated network statistics (Packets received/transmitted,
-+ * dropped, errors, ...).
-+ *
-+ * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH.
-+ *
-+ * Author: Gerald Schaefer <geraldsc at de.ibm.com>
-+ */
++MODULE_DESCRIPTION("Linux-VM Monitor Stream, OS statistics");
+diff -urN kernel-source-2.4.27.orig/arch/s390/appldata/Makefile arch/s390/appldata/Makefile
+--- kernel-source-2.4.27.orig/arch/s390/appldata/Makefile	1969-12-31 17:00:00.000000000 -0700
++++ arch/s390/appldata/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,13 @@
++#
++# Linux - VM Monitor Stream, Stage 1
++#
 +
-+#include <linux/config.h>
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/slab.h>
-+#include <linux/errno.h> 
-+#include <linux/kernel_stat.h>
-+#include <linux/netdevice.h>
++O_TARGET := appldata.o
 +
-+#include "appldata.h"
-+
-+
-+#define MY_PRINT_NAME	"appldata_net_sum"	/* for debug messages, etc. */
-+
-+/*
-+ * Network data
-+ */
-+struct appldata_net_sum_data {
-+	u64 timestamp;
-+	u32 sync_count_1;	/* after VM collected the record data, */
-+	u32 sync_count_2;	/* sync_count_1 and sync_count_2 should be the
-+				   same. If not, the record has been updated on
-+				   the Linux side while VM was collecting the
-+				   (possibly corrupt) data */
-+
-+	u32 nr_interfaces;	/* nr. of network interfaces being monitored */
-+
-+	u32 padding;		/* next value is 64-bit aligned, so these */
-+				/* 4 byte would be padded out by compiler */
-+
-+	u64 rx_packets;		/* total packets received        */
-+	u64 tx_packets;		/* total packets transmitted     */
-+	u64 rx_bytes;		/* total bytes received          */
-+	u64 tx_bytes;		/* total bytes transmitted       */
-+	u64 rx_errors;		/* bad packets received          */
-+	u64 tx_errors;		/* packet transmit problems      */
-+	u64 rx_dropped;		/* no space in linux buffers     */
-+	u64 tx_dropped;		/* no space available in linux   */
-+	u64 collisions;		/* collisions while transmitting */
-+} appldata_net_sum_data;
-+
-+
-+static inline void appldata_print_debug(struct appldata_net_sum_data *net_data)
-+{
-+	P_DEBUG("--- NET - RECORD ---\n");
-+
-+	P_DEBUG("nr_interfaces = %u\n", net_data->nr_interfaces);
-+	P_DEBUG("rx_packets    = %8llu\n", ULL(net_data->rx_packets));
-+	P_DEBUG("tx_packets    = %8llu\n", ULL(net_data->tx_packets));
-+	P_DEBUG("rx_bytes      = %8llu\n", ULL(net_data->rx_bytes));
-+	P_DEBUG("tx_bytes      = %8llu\n", ULL(net_data->tx_bytes));
-+	P_DEBUG("rx_errors     = %8llu\n", ULL(net_data->rx_errors));
-+	P_DEBUG("tx_errors     = %8llu\n", ULL(net_data->tx_errors));
-+	P_DEBUG("rx_dropped    = %8llu\n", ULL(net_data->rx_dropped));
-+	P_DEBUG("tx_dropped    = %8llu\n", ULL(net_data->tx_dropped));
-+	P_DEBUG("collisions    = %8llu\n", ULL(net_data->collisions));
-+
-+	P_DEBUG("sync_count_1 = %u\n", net_data->sync_count_1);
-+	P_DEBUG("sync_count_2 = %u\n", net_data->sync_count_2);
-+	P_DEBUG("timestamp    = %llX\n", ULL(net_data->timestamp));
-+}
-+
-+/*
-+ * appldata_get_net_sum_data()
-+ *
-+ * gather accumulated network statistics
-+ */
-+static void appldata_get_net_sum_data(void *data)
-+{
-+	int i;
-+	struct appldata_net_sum_data *net_data;
-+	struct net_device *dev;
-+	struct net_device_stats *stats;
-+	unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes, rx_errors,
-+			tx_errors, rx_dropped, tx_dropped, collisions;
-+
-+	net_data = data;
-+	net_data->sync_count_1++;
-+
-+	i = 0;
-+	rx_packets = 0;
-+	tx_packets = 0;
-+	rx_bytes   = 0;
-+	tx_bytes   = 0;
-+	rx_errors  = 0;
-+	tx_errors  = 0;
-+	rx_dropped = 0;
-+	tx_dropped = 0;
-+	collisions = 0;
-+	read_lock(&dev_base_lock);
-+	for (dev = dev_base; dev != NULL; dev = dev->next) {
-+		if (dev->get_stats == NULL) {
-+			continue;
-+		}
-+		stats = dev->get_stats(dev);
-+		rx_packets += stats->rx_packets;
-+		tx_packets += stats->tx_packets;
-+		rx_bytes   += stats->rx_bytes;
-+		tx_bytes   += stats->tx_bytes;
-+		rx_errors  += stats->rx_errors;
-+		tx_errors  += stats->tx_errors;
-+		rx_dropped += stats->rx_dropped;
-+		tx_dropped += stats->tx_dropped;
-+		collisions += stats->collisions;
-+		i++;
-+	}
-+	read_unlock(&dev_base_lock);
-+	net_data->nr_interfaces = i;
-+	net_data->rx_packets = rx_packets;
-+	net_data->tx_packets = tx_packets;
-+	net_data->rx_bytes   = rx_bytes;
-+	net_data->tx_bytes   = tx_bytes;
-+	net_data->rx_errors  = rx_errors;
-+	net_data->tx_errors  = tx_errors;
-+	net_data->rx_dropped = rx_dropped;
-+	net_data->tx_dropped = tx_dropped;
-+	net_data->collisions = collisions;
-+
-+	net_data->timestamp = get_clock();
-+	net_data->sync_count_2++;
-+	appldata_print_debug(net_data);
-+}
-+
-+
-+static struct appldata_ops ops = {
-+	.ctl_nr    = CTL_APPLDATA_NET_SUM,
-+	.name	   = "net_sum",
-+	.record_nr = APPLDATA_RECORD_NET_SUM_ID,
-+	.size	   = sizeof(struct appldata_net_sum_data),
-+	.callback  = &appldata_get_net_sum_data,
-+	.data      = &appldata_net_sum_data,
-+	.owner     = THIS_MODULE,
-+};
-+
-+
-+/*
-+ * appldata_net_init()
-+ *
-+ * init data, register ops
-+ */
-+static int __init appldata_net_init(void)
-+{
-+	int rc;
-+
-+	P_DEBUG("sizeof(net) = %lu\n", sizeof(struct appldata_net_sum_data));
-+
-+	rc = appldata_register_ops(&ops);
-+	if (rc != 0) {
-+		P_ERROR("Error registering ops, rc = %i\n", rc);
-+	} else {
-+		P_DEBUG("%s-ops registered!\n", ops.name);
-+	}
-+	return rc;
-+}
-+
-+/*
-+ * appldata_net_exit()
-+ * 
-+ * unregister ops
-+ */
-+static void __exit appldata_net_exit(void)
-+{
-+	appldata_unregister_ops(&ops);
-+	P_DEBUG("%s-ops unregistered!\n", ops.name);
-+}
-+
-+
-+module_init(appldata_net_init);
-+module_exit(appldata_net_exit);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Gerald Schaefer");
-+MODULE_DESCRIPTION("Linux-VM Monitor Stream, accumulated network statistics");
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata_os.c arch/s390/appldata/appldata_os.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata_os.c	1969-12-31 17:00:00.000000000 -0700
-+++ arch/s390/appldata/appldata_os.c	2006-01-30 22:25:24.000000000 -0700
-@@ -0,0 +1,206 @@
-+/*
-+ * arch/s390/appldata/appldata_os.c
-+ *
-+ * Data gathering module for Linux-VM Monitor Stream, Stage 1.
-+ * Collects misc. OS related data (CPU utilization, running processes).
-+ *
-+ * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH.
-+ *
-+ * Author: Gerald Schaefer <geraldsc at de.ibm.com>
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/slab.h>
-+#include <linux/errno.h> 
-+#include <linux/kernel_stat.h>
-+#include <linux/netdevice.h>
-+
-+#include "appldata.h"
-+
-+
-+#define MY_PRINT_NAME	"appldata_os"		/* for debug messages, etc. */
-+#define LOAD_INT(x) ((x) >> FSHIFT)
-+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
-+
-+/*
-+ * OS data
-+ */
-+struct appldata_os_per_cpu {
-+	u32 per_cpu_user;	/* timer ticks spent in user mode   */
-+	u32 per_cpu_nice;	/* ... spent with modified priority */
-+	u32 per_cpu_system;	/* ... spent in kernel mode         */
-+	u32 per_cpu_idle;	/* ... spent in idle mode           */
-+};
-+
-+struct appldata_os_data {
-+	u64 timestamp;
-+	u32 sync_count_1;	/* after VM collected the record data, */
-+	u32 sync_count_2;	/* sync_count_1 and sync_count_2 should be the
-+				   same. If not, the record has been updated on
-+				   the Linux side while VM was collecting the
-+				   (possibly corrupt) data */
-+
-+	u32 nr_cpus;		/* number of (virtual) CPUs        */
-+	u32 per_cpu_size;	/* size of the per-cpu data struct */
-+	u32 cpu_offset;		/* offset of the first per-cpu data struct */
-+
-+	u32 nr_running;		/* number of runnable threads       */
-+	u32 nr_threads;		/* number of threads                */
-+
-+	u32 avenrun[3];		/* average nr. of running processes during */
-+				/* the last 1, 5 and 15 minutes */
-+	
-+	/* per cpu data */
-+	struct appldata_os_per_cpu os_cpu[0];
-+};
-+
-+static struct appldata_os_data *appldata_os_data;
-+
-+
-+static inline void appldata_print_debug(struct appldata_os_data *os_data)
-+{
-+	int i;
-+	unsigned int a0, a1, a2;
-+
-+	P_DEBUG("--- OS - RECORD ---\n");
-+	P_DEBUG("nr_threads   = %u\n", os_data->nr_threads);
-+	P_DEBUG("nr_running   = %u\n", os_data->nr_running);
-+	P_DEBUG("avenrun(int) = %8x / %8x / %8x\n", os_data->avenrun[0],
-+		os_data->avenrun[1], os_data->avenrun[2]);
-+	a0 = os_data->avenrun[0];
-+	a1 = os_data->avenrun[1];
-+	a2 = os_data->avenrun[2];
-+	P_DEBUG("avenrun(float) = %d.%02d / %d.%02d / %d.%02d\n",
-+		LOAD_INT(a0), LOAD_FRAC(a0), LOAD_INT(a1), LOAD_FRAC(a1),
-+		LOAD_INT(a2), LOAD_FRAC(a2));
-+
-+	for (i = 0; i < smp_num_cpus; i++) {
-+		P_DEBUG("cpu%u : user = %u, nice = %u, system = %u, "
-+			"idle = %u\n",
-+				i,
-+				os_data->os_cpu[i].per_cpu_user,
-+				os_data->os_cpu[i].per_cpu_nice,
-+				os_data->os_cpu[i].per_cpu_system,
-+				os_data->os_cpu[i].per_cpu_idle);
-+	}
-+
-+	P_DEBUG("sync_count_1 = %u\n", os_data->sync_count_1);
-+	P_DEBUG("sync_count_2 = %u\n", os_data->sync_count_2);
-+	P_DEBUG("timestamp    = %llX\n", ULL(os_data->timestamp));
-+}
-+
-+/*
-+ * appldata_get_os_data()
-+ *
-+ * gather OS data
-+ */
-+static void appldata_get_os_data(void *data)
-+{
-+	int i;
-+	struct appldata_os_data *os_data;
-+
-+	os_data = data;
-+	os_data->sync_count_1++;
-+	os_data->nr_cpus = smp_num_cpus;
-+
-+	os_data->nr_threads = nr_threads;
-+	os_data->nr_running = nr_running;
-+	os_data->avenrun[0] = (u32) avenrun[0] + (FIXED_1/200);
-+	os_data->avenrun[1] = (u32) avenrun[1] + (FIXED_1/200);
-+	os_data->avenrun[2] = (u32) avenrun[2] + (FIXED_1/200);
-+
-+	for (i = 0; i < smp_num_cpus; i++) {
-+		os_data->os_cpu[i].per_cpu_user = 
-+				    kstat.per_cpu_user[cpu_logical_map(i)];
-+		os_data->os_cpu[i].per_cpu_nice =
-+				    kstat.per_cpu_nice[cpu_logical_map(i)];
-+		os_data->os_cpu[i].per_cpu_system =
-+				    kstat.per_cpu_system[cpu_logical_map(i)];
-+		os_data->os_cpu[i].per_cpu_idle = jiffies - (
-+					os_data->os_cpu[i].per_cpu_user
-+				      + os_data->os_cpu[i].per_cpu_nice
-+				      + os_data->os_cpu[i].per_cpu_system);
-+	}
-+
-+	os_data->timestamp = get_clock();
-+	os_data->sync_count_2++;
-+	appldata_print_debug(os_data);
-+}
-+
-+
-+static struct appldata_ops ops = {
-+	.ctl_nr    = CTL_APPLDATA_OS,
-+	.name	   = "os",
-+	.record_nr = APPLDATA_RECORD_OS_ID,
-+	.callback  = &appldata_get_os_data,
-+	.owner     = THIS_MODULE,
-+};
-+
-+
-+/*
-+ * appldata_os_init()
-+ *
-+ * init data, register ops
-+ */
-+static int __init appldata_os_init(void)
-+{
-+	int rc, size;
-+
-+	size = sizeof(struct appldata_os_data) + 
-+		(smp_num_cpus * sizeof(struct appldata_os_per_cpu));
-+	if (size > APPLDATA_MAX_REC_SIZE) {
-+		P_ERROR("Size of record = %i, bigger than maximum (%i)!\n",
-+			size, APPLDATA_MAX_REC_SIZE);
-+		rc = -ENOMEM;
-+		goto out;
-+	}
-+	P_DEBUG("sizeof(os) = %i, sizeof(os_cpu) = %lu\n", size,
-+		sizeof(struct appldata_os_per_cpu));
-+
-+	appldata_os_data = kmalloc(size, GFP_DMA);
-+	if (appldata_os_data == NULL) {
-+		P_ERROR("No memory for %s!\n", ops.name);
-+		rc = -ENOMEM;
-+		goto out;
-+	}
-+	memset(appldata_os_data, 0, size);
-+
-+	appldata_os_data->per_cpu_size = sizeof(struct appldata_os_per_cpu);
-+	appldata_os_data->cpu_offset   = offsetof(struct appldata_os_data,
-+							os_cpu);
-+	P_DEBUG("cpu offset = %u\n", appldata_os_data->cpu_offset);
-+
-+	ops.data = appldata_os_data;
-+	ops.size = size;
-+	rc = appldata_register_ops(&ops);
-+	if (rc != 0) {
-+		P_ERROR("Error registering ops, rc = %i\n", rc);
-+		kfree(appldata_os_data);
-+	} else {
-+		P_DEBUG("%s-ops registered!\n", ops.name);
-+	}
-+out:
-+	return rc;
-+}
-+
-+/*
-+ * appldata_os_exit()
-+ * 
-+ * unregister ops
-+ */
-+static void __exit appldata_os_exit(void)
-+{
-+	appldata_unregister_ops(&ops);
-+	kfree(appldata_os_data);
-+	P_DEBUG("%s-ops unregistered!\n", ops.name);
-+}
-+
-+
-+module_init(appldata_os_init);
-+module_exit(appldata_os_exit);
++obj-$(CONFIG_APPLDATA_BASE) += appldata_base.o
++obj-$(CONFIG_APPLDATA_MEM) += appldata_mem.o
++obj-$(CONFIG_APPLDATA_OS) += appldata_os.o
++obj-$(CONFIG_APPLDATA_NET_SUM) += appldata_net_sum.o
++export-objs += appldata_base.o
 +
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Gerald Schaefer");
-+MODULE_DESCRIPTION("Linux-VM Monitor Stream, OS statistics");
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/config.in arch/s390/config.in
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/config.in	2003-11-28 11:26:19.000000000 -0700
-+++ arch/s390/config.in	2006-01-30 22:25:24.000000000 -0700
++include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27.orig/arch/s390/config.in arch/s390/config.in
+--- kernel-source-2.4.27.orig/arch/s390/config.in	2003-11-28 11:26:19.000000000 -0700
++++ arch/s390/config.in	2006-02-12 12:47:23.000000000 -0700
 @@ -62,6 +62,33 @@
  bool 'Show crashed user process info' CONFIG_PROCESS_DEBUG
  bool 'Pseudo page fault support' CONFIG_PFAULT
@@ -2798,9 +1335,9 @@
  endmenu
  
  source drivers/s390/Config.in
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/defconfig arch/s390/defconfig
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/defconfig	2006-01-30 22:23:45.000000000 -0700
-+++ arch/s390/defconfig	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390/defconfig arch/s390/defconfig
+--- kernel-source-2.4.27.orig/arch/s390/defconfig	2006-02-08 04:06:25.000000000 -0700
++++ arch/s390/defconfig	2006-02-12 12:47:23.000000000 -0700
 @@ -48,8 +48,8 @@
  CONFIG_QDIO=m
  # CONFIG_QDIO_PERF_STATS is not set
@@ -2977,9 +1514,9 @@
  CONFIG_EXT2_FS=y
  # CONFIG_SYSV_FS is not set
  # CONFIG_UDF_FS is not set
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/entry.S arch/s390/kernel/entry.S
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/entry.S	2006-01-30 22:23:45.000000000 -0700
-+++ arch/s390/kernel/entry.S	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390/kernel/entry.S arch/s390/kernel/entry.S
+--- kernel-source-2.4.27.orig/arch/s390/kernel/entry.S	2006-02-08 04:06:25.000000000 -0700
++++ arch/s390/kernel/entry.S	2006-02-12 12:47:23.000000000 -0700
 @@ -690,7 +690,12 @@
  io_int_handler:
  	SAVE_ALL_BASE
@@ -3064,9 +1601,9 @@
  .Lschedtail:   .long  schedule_tail
 -
 +.Laccount_ticks:.long account_ticks
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/process.c arch/s390/kernel/process.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/process.c	2004-02-18 06:36:30.000000000 -0700
-+++ arch/s390/kernel/process.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390/kernel/process.c arch/s390/kernel/process.c
+--- kernel-source-2.4.27.orig/arch/s390/kernel/process.c	2004-02-18 06:36:30.000000000 -0700
++++ arch/s390/kernel/process.c	2006-02-12 12:47:23.000000000 -0700
 @@ -43,13 +43,27 @@
  #include <asm/io.h>
  #include <asm/processor.h>
@@ -3146,9 +1683,9 @@
  	}
  }
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/s390_ksyms.c arch/s390/kernel/s390_ksyms.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/s390_ksyms.c	2004-02-18 06:36:30.000000000 -0700
-+++ arch/s390/kernel/s390_ksyms.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390/kernel/s390_ksyms.c arch/s390/kernel/s390_ksyms.c
+--- kernel-source-2.4.27.orig/arch/s390/kernel/s390_ksyms.c	2004-02-18 06:36:30.000000000 -0700
++++ arch/s390/kernel/s390_ksyms.c	2006-02-12 12:47:23.000000000 -0700
 @@ -7,9 +7,13 @@
  #include <linux/module.h>
  #include <linux/smp.h>
@@ -3194,9 +1731,9 @@
 +/* urandom read needed for z90crypt */
 +extern struct file_operations urandom_fops;
 +EXPORT_SYMBOL_GPL(urandom_fops);
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/setup.c arch/s390/kernel/setup.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/setup.c	2003-08-25 05:44:40.000000000 -0600
-+++ arch/s390/kernel/setup.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390/kernel/setup.c arch/s390/kernel/setup.c
+--- kernel-source-2.4.27.orig/arch/s390/kernel/setup.c	2003-08-25 05:44:40.000000000 -0600
++++ arch/s390/kernel/setup.c	2006-02-12 12:47:23.000000000 -0700
 @@ -276,9 +276,9 @@
  
  static int __init conmode_setup(char *str)
@@ -3280,9 +1817,9 @@
  	_machine_power_off();
  }
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/smp.c arch/s390/kernel/smp.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/smp.c	2002-11-28 16:53:11.000000000 -0700
-+++ arch/s390/kernel/smp.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390/kernel/smp.c arch/s390/kernel/smp.c
+--- kernel-source-2.4.27.orig/arch/s390/kernel/smp.c	2002-11-28 16:53:11.000000000 -0700
++++ arch/s390/kernel/smp.c	2006-02-12 12:47:23.000000000 -0700
 @@ -92,7 +92,7 @@
  
  extern void reipl(unsigned long devno);
@@ -3440,9 +1977,9 @@
  }
  
  /*
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/time.c arch/s390/kernel/time.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/time.c	2003-06-13 08:51:32.000000000 -0600
-+++ arch/s390/kernel/time.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390/kernel/time.c arch/s390/kernel/time.c
+--- kernel-source-2.4.27.orig/arch/s390/kernel/time.c	2003-06-13 08:51:32.000000000 -0600
++++ arch/s390/kernel/time.c	2006-02-12 12:47:23.000000000 -0700
 @@ -4,8 +4,8 @@
   *  S390 version
   *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
@@ -4280,9 +2817,9 @@
 +	start_hz_timer();
 +#endif
  }
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/traps.c arch/s390/kernel/traps.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/traps.c	2006-01-30 22:23:49.000000000 -0700
-+++ arch/s390/kernel/traps.c	2006-01-30 22:26:06.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390/kernel/traps.c arch/s390/kernel/traps.c
+--- kernel-source-2.4.27.orig/arch/s390/kernel/traps.c	2006-02-08 04:06:26.000000000 -0700
++++ arch/s390/kernel/traps.c	2006-02-12 12:48:44.000000000 -0700
 @@ -54,6 +54,9 @@
  extern pgm_check_handler_t do_segment_exception;
  extern pgm_check_handler_t do_page_exception;
@@ -4293,6 +2830,17 @@
  #ifdef CONFIG_PFAULT
  extern int pfault_init(void);
  extern void pfault_fini(void);
+@@ -624,8 +627,8 @@
+         siginfo_t info;
+ 
+ 	/* Set user psw back to home space mode. */
+-	if (regs->psw.mask & PSW_MASK_PSTATE)
+-		regs->psw.mask |= PSW_ASC_HOME;
++	if (regs->psw.mask & PSW_PROBLEM_STATE)
++		regs->psw.mask |= _PSW_HOME_SPACE_MODE;
+ 	/* Send SIGILL. */
+         info.si_signo = SIGILL;
+         info.si_errno = 0;
 @@ -642,7 +645,7 @@
          int i;
  
@@ -4312,22 +2860,53 @@
  #ifdef CONFIG_PFAULT
  	if (MACHINE_IS_VM) {
  		/* request the 0x2603 external interrupt */
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/mm/Makefile arch/s390/mm/Makefile
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/mm/Makefile	2001-07-25 15:12:01.000000000 -0600
-+++ arch/s390/mm/Makefile	2006-01-30 22:25:24.000000000 -0700
-@@ -9,6 +9,8 @@
+diff -urN kernel-source-2.4.27.orig/arch/s390/Makefile arch/s390/Makefile
+--- kernel-source-2.4.27.orig/arch/s390/Makefile	2002-08-02 18:39:43.000000000 -0600
++++ arch/s390/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -23,15 +23,18 @@
+ LINKFLAGS =-T $(TOPDIR)/arch/s390/vmlinux.lds $(LDFLAGS)
+ endif
  
- O_TARGET := mm.o
++CFLAGS_ARCH := -m31
+ CFLAGS_PIPE := -pipe
+ CFLAGS_NSR  := -fno-strength-reduce
+-CFLAGS := $(CFLAGS) $(CFLAGS_PIPE) $(CFLAGS_NSR)
++CFLAGS := $(CFLAGS) $(CFLAGS_ARCH) $(CFLAGS_PIPE) $(CFLAGS_NSR)
++AFLAGS := $(AFLAGS) $(CFLAGS_ARCH)
  
--obj-y	 := init.o fault.o ioremap.o extable.o
-+obj-y	 := init.o fault.o ioremap.o extable.o dcss.o
-+obj-$(CONFIG_CMM) += cmm.o
-+export-objs	:= dcss.o cmm.o
+ HEAD := arch/s390/kernel/head.o arch/s390/kernel/init_task.o
  
- include $(TOPDIR)/Rules.make
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/mm/cmm.c arch/s390/mm/cmm.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/mm/cmm.c	1969-12-31 17:00:00.000000000 -0700
-+++ arch/s390/mm/cmm.c	2006-01-30 22:25:24.000000000 -0700
+ SUBDIRS := $(SUBDIRS) arch/s390/mm arch/s390/kernel arch/s390/lib \
+-           drivers/s390 arch/s390/math-emu
+-CORE_FILES := arch/s390/mm/mm.o arch/s390/kernel/kernel.o $(CORE_FILES)
++           arch/s390/appldata drivers/s390 arch/s390/math-emu
++CORE_FILES := arch/s390/mm/mm.o arch/s390/kernel/kernel.o \
++              arch/s390/appldata/appldata.o $(CORE_FILES)
+ DRIVERS := $(DRIVERS) drivers/s390/io.o
+ LIBS := $(TOPDIR)/arch/s390/lib/lib.a $(LIBS) $(TOPDIR)/arch/s390/lib/lib.a
+ 
+@@ -39,7 +42,7 @@
+   CORE_FILES := $(CORE_FILES) arch/s390/math-emu/math-emu.o
+ endif
+ 
+-all: image listing
++all: image
+ 
+ listing: vmlinux
+ 	@$(MAKEBOOT) listing
+@@ -47,6 +50,9 @@
+ arch/s390/kernel: dummy
+ 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/kernel
+ 
++arch/s390/appldata: dummy
++	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/appldata
++
+ arch/s390/mm: dummy
+ 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/mm
+ 
+diff -urN kernel-source-2.4.27.orig/arch/s390/mm/cmm.c arch/s390/mm/cmm.c
+--- kernel-source-2.4.27.orig/arch/s390/mm/cmm.c	1969-12-31 17:00:00.000000000 -0700
++++ arch/s390/mm/cmm.c	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,448 @@
 +/*
 + *  arch/s390/mm/cmm.c
@@ -4777,9 +3356,9 @@
 +EXPORT_SYMBOL(cmm_set_timeout);
 +
 +MODULE_LICENSE("GPL");
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/mm/dcss.c arch/s390/mm/dcss.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/mm/dcss.c	1969-12-31 17:00:00.000000000 -0700
-+++ arch/s390/mm/dcss.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390/mm/dcss.c arch/s390/mm/dcss.c
+--- kernel-source-2.4.27.orig/arch/s390/mm/dcss.c	1969-12-31 17:00:00.000000000 -0700
++++ arch/s390/mm/dcss.c	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,504 @@
 +/*
 + * File...........: arch/s390/mm/dcss.c
@@ -5285,9 +3864,9 @@
 +EXPORT_SYMBOL(segment_load);
 +EXPORT_SYMBOL(segment_unload);
 +EXPORT_SYMBOL(segment_replace);
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/mm/init.c arch/s390/mm/init.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390/mm/init.c	2004-02-18 06:36:30.000000000 -0700
-+++ arch/s390/mm/init.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390/mm/init.c arch/s390/mm/init.c
+--- kernel-source-2.4.27.orig/arch/s390/mm/init.c	2004-02-18 06:36:30.000000000 -0700
++++ arch/s390/mm/init.c	2006-02-12 12:47:23.000000000 -0700
 @@ -69,6 +69,8 @@
  
  void diag10(unsigned long addr)
@@ -5297,49 +3876,22 @@
          asm volatile ("diag %0,%0,0x10" : : "a" (addr));
  }
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/Makefile arch/s390x/Makefile
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/Makefile	2002-08-02 18:39:43.000000000 -0600
-+++ arch/s390x/Makefile	2006-01-30 22:25:24.000000000 -0700
-@@ -24,19 +24,22 @@
- endif
- MODFLAGS += -fpic
- 
-+CFLAGS_ARCH := -m64
- CFLAGS_PIPE := -pipe
- CFLAGS_NSR  := -fno-strength-reduce
--CFLAGS := $(CFLAGS) $(CFLAGS_PIPE) $(CFLAGS_NSR)
-+CFLAGS := $(CFLAGS) $(CFLAGS_ARCH) $(CFLAGS_PIPE) $(CFLAGS_NSR)
-+AFLAGS := $(AFLAGS) $(CFLAGS_ARCH)
- 
- HEAD := arch/s390x/kernel/head.o arch/s390x/kernel/init_task.o
- 
- SUBDIRS := $(SUBDIRS) arch/s390x/mm arch/s390x/kernel arch/s390x/lib \
--           drivers/s390
--CORE_FILES := arch/s390x/mm/mm.o arch/s390x/kernel/kernel.o $(CORE_FILES)
-+           arch/s390/appldata drivers/s390
-+CORE_FILES := arch/s390x/mm/mm.o arch/s390x/kernel/kernel.o \
-+              arch/s390/appldata/appldata.o $(CORE_FILES)
- DRIVERS := $(DRIVERS) drivers/s390/io.o
- LIBS := $(TOPDIR)/arch/s390x/lib/lib.a $(LIBS) $(TOPDIR)/arch/s390x/lib/lib.a
- 
--all: image listing
-+all: image
+diff -urN kernel-source-2.4.27.orig/arch/s390/mm/Makefile arch/s390/mm/Makefile
+--- kernel-source-2.4.27.orig/arch/s390/mm/Makefile	2001-07-25 15:12:01.000000000 -0600
++++ arch/s390/mm/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -9,6 +9,8 @@
  
- listing: vmlinux
- 	@$(MAKEBOOT) listing
-@@ -44,6 +47,9 @@
- arch/s390x/kernel: dummy
- 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390x/kernel
+ O_TARGET := mm.o
  
-+arch/s390/appldata: dummy
-+	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/appldata
-+
- arch/s390x/mm: dummy
- 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390x/mm
+-obj-y	 := init.o fault.o ioremap.o extable.o
++obj-y	 := init.o fault.o ioremap.o extable.o dcss.o
++obj-$(CONFIG_CMM) += cmm.o
++export-objs	:= dcss.o cmm.o
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/config.in arch/s390x/config.in
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/config.in	2003-11-28 11:26:19.000000000 -0700
-+++ arch/s390x/config.in	2006-01-30 22:25:24.000000000 -0700
+ include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27.orig/arch/s390x/config.in arch/s390x/config.in
+--- kernel-source-2.4.27.orig/arch/s390x/config.in	2003-11-28 11:26:19.000000000 -0700
++++ arch/s390x/config.in	2006-02-12 12:47:23.000000000 -0700
 @@ -65,8 +65,34 @@
  bool 'Show crashed user process info' CONFIG_PROCESS_DEBUG
  bool 'Pseudo page fault support' CONFIG_PFAULT
@@ -5375,9 +3927,9 @@
  
  source drivers/s390/Config.in
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/defconfig arch/s390x/defconfig
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/defconfig	2006-01-30 22:23:45.000000000 -0700
-+++ arch/s390x/defconfig	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/defconfig arch/s390x/defconfig
+--- kernel-source-2.4.27.orig/arch/s390x/defconfig	2006-02-08 04:06:25.000000000 -0700
++++ arch/s390x/defconfig	2006-02-12 12:47:23.000000000 -0700
 @@ -49,8 +49,8 @@
  CONFIG_QDIO=m
  # CONFIG_QDIO_PERF_STATS is not set
@@ -5551,9 +4103,9 @@
  CONFIG_EXT2_FS=y
  # CONFIG_SYSV_FS is not set
  # CONFIG_UDF_FS is not set
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/entry.S arch/s390x/kernel/entry.S
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/entry.S	2006-01-30 22:23:45.000000000 -0700
-+++ arch/s390x/kernel/entry.S	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/kernel/entry.S arch/s390x/kernel/entry.S
+--- kernel-source-2.4.27.orig/arch/s390x/kernel/entry.S	2006-02-08 04:06:25.000000000 -0700
++++ arch/s390x/kernel/entry.S	2006-02-12 12:47:23.000000000 -0700
 @@ -722,7 +722,12 @@
          .globl io_int_handler
  io_int_handler:
@@ -5626,9 +4178,9 @@
  	brasl   %r14,s390_do_machine_check
  mcck_return:
          RESTORE_ALL 0
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/ioctl32.c arch/s390x/kernel/ioctl32.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/ioctl32.c	2006-01-30 22:23:44.000000000 -0700
-+++ arch/s390x/kernel/ioctl32.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/kernel/ioctl32.c arch/s390x/kernel/ioctl32.c
+--- kernel-source-2.4.27.orig/arch/s390x/kernel/ioctl32.c	2006-02-08 04:06:23.000000000 -0700
++++ arch/s390x/kernel/ioctl32.c	2006-02-12 12:47:23.000000000 -0700
 @@ -34,7 +34,9 @@
  #include <asm/types.h>
  #include <asm/uaccess.h>
@@ -5783,9 +4335,9 @@
  };
  
  #define NR_IOCTL32_HANDLERS	(sizeof(ioctl32_handler_table) /	\
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/linux32.c arch/s390x/kernel/linux32.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/linux32.c	2006-01-30 22:23:48.000000000 -0700
-+++ arch/s390x/kernel/linux32.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/kernel/linux32.c arch/s390x/kernel/linux32.c
+--- kernel-source-2.4.27.orig/arch/s390x/kernel/linux32.c	2006-02-08 04:06:26.000000000 -0700
++++ arch/s390x/kernel/linux32.c	2006-02-12 12:47:23.000000000 -0700
 @@ -4428,7 +4428,7 @@
      ret = sys_newstat(tmp, &s);
      set_fs (old_fs);
@@ -5822,9 +4374,9 @@
  		error = -ENOMEM;
  	}
  	up_write(&current->mm->mmap_sem);
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/process.c arch/s390x/kernel/process.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/process.c	2004-02-18 06:36:30.000000000 -0700
-+++ arch/s390x/kernel/process.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/kernel/process.c arch/s390x/kernel/process.c
+--- kernel-source-2.4.27.orig/arch/s390x/kernel/process.c	2004-02-18 06:36:30.000000000 -0700
++++ arch/s390x/kernel/process.c	2006-02-12 12:47:23.000000000 -0700
 @@ -43,9 +43,24 @@
  #include <asm/io.h>
  #include <asm/processor.h>
@@ -5900,9 +4452,9 @@
  	}
  }
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/s390_ksyms.c arch/s390x/kernel/s390_ksyms.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/s390_ksyms.c	2004-02-18 06:36:30.000000000 -0700
-+++ arch/s390x/kernel/s390_ksyms.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/kernel/s390_ksyms.c arch/s390x/kernel/s390_ksyms.c
+--- kernel-source-2.4.27.orig/arch/s390x/kernel/s390_ksyms.c	2004-02-18 06:36:30.000000000 -0700
++++ arch/s390x/kernel/s390_ksyms.c	2006-02-12 12:47:23.000000000 -0700
 @@ -9,10 +9,14 @@
  #include <linux/mm.h>
  #include <linux/smp.h>
@@ -5949,9 +4501,9 @@
 +/* urandom read needed for z90crypt */
 +extern struct file_operations urandom_fops;
 +EXPORT_SYMBOL_GPL(urandom_fops);
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/setup.c arch/s390x/kernel/setup.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/setup.c	2003-08-25 05:44:40.000000000 -0600
-+++ arch/s390x/kernel/setup.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/kernel/setup.c arch/s390x/kernel/setup.c
+--- kernel-source-2.4.27.orig/arch/s390x/kernel/setup.c	2003-08-25 05:44:40.000000000 -0600
++++ arch/s390x/kernel/setup.c	2006-02-12 12:47:23.000000000 -0700
 @@ -164,9 +164,9 @@
  
  static int __init conmode_setup(char *str)
@@ -6035,9 +4587,9 @@
  	_machine_power_off();
  }
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/smp.c arch/s390x/kernel/smp.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/smp.c	2003-06-13 08:51:32.000000000 -0600
-+++ arch/s390x/kernel/smp.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/kernel/smp.c arch/s390x/kernel/smp.c
+--- kernel-source-2.4.27.orig/arch/s390x/kernel/smp.c	2003-06-13 08:51:32.000000000 -0600
++++ arch/s390x/kernel/smp.c	2006-02-12 12:47:23.000000000 -0700
 @@ -92,7 +92,7 @@
  
  extern void reipl(unsigned long devno);
@@ -6304,9 +4856,9 @@
  EXPORT_SYMBOL(smp_num_cpus);
  EXPORT_SYMBOL(smp_call_function);
 +
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/time.c arch/s390x/kernel/time.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/time.c	2003-06-13 08:51:32.000000000 -0600
-+++ arch/s390x/kernel/time.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/kernel/time.c arch/s390x/kernel/time.c
+--- kernel-source-2.4.27.orig/arch/s390x/kernel/time.c	2003-06-13 08:51:32.000000000 -0600
++++ arch/s390x/kernel/time.c	2006-02-12 12:47:23.000000000 -0700
 @@ -4,8 +4,8 @@
   *  S390 version
   *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
@@ -7090,9 +5642,9 @@
 +	start_hz_timer();
 +#endif
  }
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/traps.c arch/s390x/kernel/traps.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/traps.c	2002-11-28 16:53:11.000000000 -0700
-+++ arch/s390x/kernel/traps.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/kernel/traps.c arch/s390x/kernel/traps.c
+--- kernel-source-2.4.27.orig/arch/s390x/kernel/traps.c	2002-11-28 16:53:11.000000000 -0700
++++ arch/s390x/kernel/traps.c	2006-02-12 12:47:23.000000000 -0700
 @@ -56,6 +56,9 @@
  extern pgm_check_handler_t do_segment_exception;
  extern pgm_check_handler_t do_region_exception;
@@ -7113,22 +5665,49 @@
  #ifdef CONFIG_PFAULT
  	if (MACHINE_IS_VM) {
  		/* request the 0x2603 external interrupt */
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/Makefile arch/s390x/mm/Makefile
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/Makefile	2001-02-13 15:13:44.000000000 -0700
-+++ arch/s390x/mm/Makefile	2006-01-30 22:25:24.000000000 -0700
-@@ -9,6 +9,8 @@
+diff -urN kernel-source-2.4.27.orig/arch/s390x/Makefile arch/s390x/Makefile
+--- kernel-source-2.4.27.orig/arch/s390x/Makefile	2002-08-02 18:39:43.000000000 -0600
++++ arch/s390x/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -24,19 +24,22 @@
+ endif
+ MODFLAGS += -fpic
  
- O_TARGET := mm.o
++CFLAGS_ARCH := -m64
+ CFLAGS_PIPE := -pipe
+ CFLAGS_NSR  := -fno-strength-reduce
+-CFLAGS := $(CFLAGS) $(CFLAGS_PIPE) $(CFLAGS_NSR)
++CFLAGS := $(CFLAGS) $(CFLAGS_ARCH) $(CFLAGS_PIPE) $(CFLAGS_NSR)
++AFLAGS := $(AFLAGS) $(CFLAGS_ARCH)
  
--obj-y	 := init.o fault.o ioremap.o extable.o
-+obj-y	 := init.o fault.o ioremap.o extable.o dcss.o
-+obj-$(CONFIG_CMM) += cmm.o
-+export-objs	:= dcss.o cmm.o
+ HEAD := arch/s390x/kernel/head.o arch/s390x/kernel/init_task.o
  
- include $(TOPDIR)/Rules.make
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/cmm.c arch/s390x/mm/cmm.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/cmm.c	1969-12-31 17:00:00.000000000 -0700
-+++ arch/s390x/mm/cmm.c	2006-01-30 22:25:24.000000000 -0700
+ SUBDIRS := $(SUBDIRS) arch/s390x/mm arch/s390x/kernel arch/s390x/lib \
+-           drivers/s390
+-CORE_FILES := arch/s390x/mm/mm.o arch/s390x/kernel/kernel.o $(CORE_FILES)
++           arch/s390/appldata drivers/s390
++CORE_FILES := arch/s390x/mm/mm.o arch/s390x/kernel/kernel.o \
++              arch/s390/appldata/appldata.o $(CORE_FILES)
+ DRIVERS := $(DRIVERS) drivers/s390/io.o
+ LIBS := $(TOPDIR)/arch/s390x/lib/lib.a $(LIBS) $(TOPDIR)/arch/s390x/lib/lib.a
+ 
+-all: image listing
++all: image
+ 
+ listing: vmlinux
+ 	@$(MAKEBOOT) listing
+@@ -44,6 +47,9 @@
+ arch/s390x/kernel: dummy
+ 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390x/kernel
+ 
++arch/s390/appldata: dummy
++	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/appldata
++
+ arch/s390x/mm: dummy
+ 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390x/mm
+ 
+diff -urN kernel-source-2.4.27.orig/arch/s390x/mm/cmm.c arch/s390x/mm/cmm.c
+--- kernel-source-2.4.27.orig/arch/s390x/mm/cmm.c	1969-12-31 17:00:00.000000000 -0700
++++ arch/s390x/mm/cmm.c	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,448 @@
 +/*
 + *  arch/s390/mm/cmm.c
@@ -7578,9 +6157,9 @@
 +EXPORT_SYMBOL(cmm_set_timeout);
 +
 +MODULE_LICENSE("GPL");
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/dcss.c arch/s390x/mm/dcss.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/dcss.c	1969-12-31 17:00:00.000000000 -0700
-+++ arch/s390x/mm/dcss.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/mm/dcss.c arch/s390x/mm/dcss.c
+--- kernel-source-2.4.27.orig/arch/s390x/mm/dcss.c	1969-12-31 17:00:00.000000000 -0700
++++ arch/s390x/mm/dcss.c	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,504 @@
 +/*
 + * File...........: arch/s390/mm/dcss.c
@@ -8086,9 +6665,9 @@
 +EXPORT_SYMBOL(segment_load);
 +EXPORT_SYMBOL(segment_unload);
 +EXPORT_SYMBOL(segment_replace);
-diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/init.c arch/s390x/mm/init.c
---- kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/init.c	2004-02-18 06:36:30.000000000 -0700
-+++ arch/s390x/mm/init.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/arch/s390x/mm/init.c arch/s390x/mm/init.c
+--- kernel-source-2.4.27.orig/arch/s390x/mm/init.c	2004-02-18 06:36:30.000000000 -0700
++++ arch/s390x/mm/init.c	2006-02-12 12:47:23.000000000 -0700
 @@ -172,7 +172,7 @@
  
  void diag10(unsigned long addr)
@@ -8098,20204 +6677,17773 @@
                  return;
          asm volatile ("sam31\n\t"
                        "diag %0,%0,0x10\n\t"
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/block/Makefile drivers/block/Makefile
---- kernel-source-2.4.27-2.4.27.orig/drivers/block/Makefile	2004-08-07 17:26:04.000000000 -0600
-+++ drivers/block/Makefile	2006-01-30 22:25:26.000000000 -0700
-@@ -10,7 +10,7 @@
- 
- O_TARGET := block.o
- 
--export-objs	:= ll_rw_blk.o blkpg.o loop.o DAC960.o genhd.o acsi.o
-+export-objs	:= ll_rw_blk.o blkpg.o elevator.o loop.o DAC960.o genhd.o acsi.o
+diff -urN kernel-source-2.4.27.orig/arch/s390x/mm/Makefile arch/s390x/mm/Makefile
+--- kernel-source-2.4.27.orig/arch/s390x/mm/Makefile	2001-02-13 15:13:44.000000000 -0700
++++ arch/s390x/mm/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -9,6 +9,8 @@
  
- obj-y	:= ll_rw_blk.o blkpg.o genhd.o elevator.o
+ O_TARGET := mm.o
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/block/elevator.c drivers/block/elevator.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/block/elevator.c	2003-06-13 08:51:32.000000000 -0600
-+++ drivers/block/elevator.c	2006-01-30 22:25:26.000000000 -0700
-@@ -219,3 +219,9 @@
- 	*elevator = type;
- 	elevator->queue_ID = queue_ID++;
- }
-+
-+EXPORT_SYMBOL(elevator_init);
-+EXPORT_SYMBOL(elevator_linus_merge);
-+EXPORT_SYMBOL(elevator_linus_merge_req);
-+EXPORT_SYMBOL(elevator_noop_merge);
-+EXPORT_SYMBOL(elevator_noop_merge_req);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/char/tty_io.c drivers/char/tty_io.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/char/tty_io.c	2006-01-30 22:23:46.000000000 -0700
-+++ drivers/char/tty_io.c	2006-01-30 22:25:26.000000000 -0700
-@@ -144,8 +144,10 @@
- extern void au1x00_serial_console_init(void);
- extern int rs_8xx_init(void);
- extern void mac_scc_console_init(void);
--extern void hwc_console_init(void);
--extern void hwc_tty_init(void);
-+extern void sclp_console_init(void);
-+extern void sclp_tty_init(void);
-+extern void sclp_vt220_con_init(void);
-+extern void sclp_vt220_tty_init(void);
- extern void con3215_init(void);
- extern void tty3215_init(void);
- extern void tub3270_con_init(void);
-@@ -2520,8 +2522,63 @@
- #endif /* CONFIG_DEVFS_FS */
- }
- 
-+/*
-+ * Register a tty device described by <driver>, with minor number <minor>,
-+ * device name <name> and in the /dev directory given by <dir>.
-+ */
-+void tty_register_devfs_name (struct tty_driver *driver, unsigned int flags,
-+			      unsigned minor, devfs_handle_t dir,
-+			      const char *name)
-+{
-+#ifdef CONFIG_DEVFS_FS
-+	umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR;
-+	kdev_t device = MKDEV (driver->major, minor);
-+
-+	switch (device) {
-+		case TTY_DEV:
-+		case PTMX_DEV:
-+			mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
-+			break;
-+		default:
-+			if (driver->major == PTY_MASTER_MAJOR)
-+				flags |= DEVFS_FL_AUTO_OWNER;
-+			break;
-+	}
-+	if ( (minor <  driver->minor_start) || 
-+	     (minor >= driver->minor_start + driver->num) ) {
-+		printk(KERN_ERR "Attempt to register invalid minor number "
-+		       "with devfs (%d:%d).\n", (int)driver->major,(int)minor);
-+		return;
-+	}
-+#  ifdef CONFIG_UNIX98_PTYS
-+	if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) &&
-+	     (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) )
-+		flags |= DEVFS_FL_CURRENT_OWNER;
-+#  endif
-+	devfs_register (dir, name, flags | DEVFS_FL_DEFAULT,
-+			driver->major, minor, mode, &tty_fops, NULL);
-+#endif /* CONFIG_DEVFS_FS */
-+}
-+
-+void tty_unregister_devfs_name (struct tty_driver *driver, unsigned minor,
-+				devfs_handle_t dir, const char *name)
-+{
-+#ifdef CONFIG_DEVFS_FS
-+	void * handle;
-+
-+	handle = devfs_find_handle (dir, name, driver->major, minor,
-+				    DEVFS_SPECIAL_CHR, 0);
-+	devfs_unregister (handle);
-+#endif /* CONFIG_DEVFS_FS */
-+}
-+
-+extern void tty_unregister_devfs_name (struct tty_driver *driver,
-+				       unsigned minor, devfs_handle_t dir,
-+				       const char *name);
- EXPORT_SYMBOL(tty_register_devfs);
- EXPORT_SYMBOL(tty_unregister_devfs);
-+EXPORT_SYMBOL(tty_register_devfs_name);
-+EXPORT_SYMBOL(tty_unregister_devfs_name);
- 
- /*
-  * Called by a tty driver to register itself.
-@@ -2692,8 +2749,11 @@
- #ifdef CONFIG_TN3215
- 	con3215_init();
- #endif
--#ifdef CONFIG_HWC
--        hwc_console_init();
-+#ifdef CONFIG_SCLP_CONSOLE
-+        sclp_console_init();
-+#endif
-+#ifdef CONFIG_SCLP_VT220_CONSOLE
-+        sclp_vt220_con_init();
- #endif
- #ifdef CONFIG_STDIO_CONSOLE
- 	stdio_console_init();
-@@ -2864,8 +2924,11 @@
- #ifdef CONFIG_TN3215
- 	tty3215_init();
- #endif
--#ifdef CONFIG_HWC
--	hwc_tty_init();
-+#ifdef CONFIG_SCLP_TTY
-+	sclp_tty_init();
-+#endif
-+#ifdef CONFIG_SCLP_VT220_TTY
-+	sclp_vt220_tty_init();
- #endif
- #ifdef CONFIG_A2232
- 	a2232board_init();
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/Config.in drivers/s390/Config.in
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/Config.in	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/s390/Config.in	2006-01-30 22:25:25.000000000 -0700
-@@ -9,6 +9,7 @@
- fi
- dep_bool '   Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM
- tristate 'XPRAM disk support' CONFIG_BLK_DEV_XPRAM
-+tristate 'z/VM discontiguos saved segments (DCSS) block device driver' CONFIG_DCSSBLK
- 
- comment 'S/390 block device drivers'
- 
-@@ -29,6 +30,7 @@
-       bool     '   Automatic activation of DIAG module' CONFIG_DASD_AUTO_DIAG
-     fi
-   fi
-+  dep_tristate '  Support for Channel Measurement on DASD devices' CONFIG_S390_CMF $CONFIG_DASD
- fi
+-obj-y	 := init.o fault.o ioremap.o extable.o
++obj-y	 := init.o fault.o ioremap.o extable.o dcss.o
++obj-$(CONFIG_CMM) += cmm.o
++export-objs	:= dcss.o cmm.o
  
- endmenu
-@@ -52,20 +54,26 @@
- if [ "$CONFIG_TN3215" = "y" ]; then
-   bool 'Support for console on 3215 line mode terminal' CONFIG_TN3215_CONSOLE
- fi
--bool 'Support for HWC line mode terminal' CONFIG_HWC
--if [ "$CONFIG_HWC" = "y" ]; then
--  bool '   console on HWC line mode terminal' CONFIG_HWC_CONSOLE
--  tristate '   Control-Program Identification' CONFIG_HWC_CPI
-+bool 'Support for SCLP' CONFIG_SCLP
-+if [ "$CONFIG_SCLP" = "y" ]; then
-+  bool '   Support for SCLP line mode terminal' CONFIG_SCLP_TTY
-+  if [ "$CONFIG_SCLP_TTY" = "y" ]; then
-+    bool '   Support for console on SCLP line mode terminal' CONFIG_SCLP_CONSOLE
-+  fi
-+  bool '   Support for SCLP VT220-compatible terminal' CONFIG_SCLP_VT220_TTY
-+  if [ "$CONFIG_SCLP_VT220_TTY" = "y" ]; then
-+    bool '   Support for console on SCLP VT220-compatible terminal' CONFIG_SCLP_VT220_CONSOLE
-+  fi
-+  tristate '   Control-Program Identification' CONFIG_SCLP_CPI
- fi
- tristate 'S/390 tape device support' CONFIG_S390_TAPE
- if [ "$CONFIG_S390_TAPE" != "n" ]; then
-   comment 'S/390 tape interface support'
--  bool '   Support for tape character devices' CONFIG_S390_TAPE_CHAR
-   bool '   Support for tape block devices' CONFIG_S390_TAPE_BLOCK
-   comment 'S/390 tape hardware support'
--  bool '   Support for 3490 tape hardware' CONFIG_S390_TAPE_3490
--  bool '   Support for 3480 tape hardware' CONFIG_S390_TAPE_3480
-+  dep_tristate '   Support for 3480/3490 tape hardware' CONFIG_S390_TAPE_34XX $CONFIG_S390_TAPE
- fi
-+dep_tristate 'Support for the z/VM recording system services (VM only)' CONFIG_VMLOGRDR $CONFIG_IUCV
- endmenu
+ include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27.orig/Documentation/Configure.help Documentation/Configure.help
+--- kernel-source-2.4.27.orig/Documentation/Configure.help	2006-02-08 04:06:25.000000000 -0700
++++ Documentation/Configure.help	2006-02-12 12:47:23.000000000 -0700
+@@ -6385,6 +6385,16 @@
  
- if [ "$CONFIG_NET" = "y" ]; then
-@@ -88,9 +96,38 @@
- 	define_bool CONFIG_HOTPLUG y
-     fi
+   See <file:Documentation/networking/ip-sysctl.txt> for details.
  
-+    if [ "$CONFIG_NET_ETHERNET" != "n" -o "$CONFIG_TR" != "n" ]; then
-+      tristate 'Lan Channel Station Interface' CONFIG_LCS
-+    fi
++Prepare net_device struct for shared IPv6 cards
++CONFIG_SHARED_IPV6_CARDS
++  This prepares the net_device structure to contain a card user instance
++  id. On some systems, e.g. IBM zSeries, networking cards can be shared.
++  In order to make IPv6 autoconfiguration useful, each user of the
++  networking card will get a different id which is used for unique
++  address generation (the id is used in the EUI-64 generation).
 +
-+    if [ "$CONFIG_QDIO" != "n" -a "$CONFIG_CHANDEV" = "y" -a "$CONFIG_IP_MULTICAST" = "y" ]; then
-+      dep_tristate 'Support for Gigabit Ethernet' CONFIG_QETH $CONFIG_QDIO
-+      if [ "$CONFIG_QETH" != "n" ]; then
-+        comment 'Gigabit Ethernet default settings'
-+	if [ "$CONFIG_IPV6" = "y" -o "$CONFIG_IPV6" = "$CONFIG_QETH" ]; then
-+		bool '   IPv6 support for qeth' CONFIG_QETH_IPV6
-+	else    
-+		define_bool CONFIG_QETH_IPV6 n
-+	fi
-+	if [ "$CONFIG_VLAN_8021Q" = "y" -o "$CONFIG_VLAN_8021Q" = "$CONFIG_QETH" ]; then
-+		bool '   VLAN support for qeth' CONFIG_QETH_VLAN
-+	else    
-+		define_bool CONFIG_QETH_VLAN n
-+	fi
-+        bool '   Performance statistics in /proc' CONFIG_QETH_PERF_STATS
-+      fi
-+    fi
-     tristate 'CTC device support' CONFIG_CTC
--    tristate 'IUCV device support (VM only)' CONFIG_IUCV
-+    tristate 'CTCMPC device support' CONFIG_MPC
-+    tristate 'IUCV support (VM only)' CONFIG_IUCV
-+    dep_tristate 'IUCV network device support (VM only)' CONFIG_NETIUCV $CONFIG_IUCV
-+    dep_tristate 'IUCV special message support (VM only)' CONFIG_SMSGIUCV $CONFIG_IUCV
-   fi
-   endmenu
- fi
- 
++  Only say yes on IBM zSeries or S/390 systems.
 +
-+mainmenu_option next_comment
-+comment 'Miscellaneous'
-+  tristate 'Z90CRYPT support' CONFIG_Z90CRYPT
-+endmenu
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/Makefile drivers/s390/Makefile
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/Makefile	2003-06-13 08:51:35.000000000 -0600
-+++ drivers/s390/Makefile	2006-01-30 22:25:25.000000000 -0700
-@@ -4,11 +4,16 @@
+ The SCTP Protocol (EXPERIMENTAL)
+ CONFIG_IP_SCTP
+   Stream Control Transmission Protocol
+@@ -8070,7 +8080,7 @@
+ QDIO base support for IBM S/390 and zSeries
+ CONFIG_QDIO
+   This driver provides the Queued Direct I/O base support for the
+-  IBM S/390 (G5 and G6) and eServer zSeries (z800 and z900).
++  IBM S/390 (G5 and G6) and eServer zSeries (z800, z900 and z990).
  
- O_TARGET := io.o
+   For details please refer to the documentation provided by IBM at
+   <http://www10.software.ibm.com/developerworks/opensource/linux390>
+@@ -8088,6 +8098,61 @@
  
--subdir-y := block char misc net
-+subdir-y := block char misc net scsi
- subdir-m := $(subdir-y)
+   If unsure, say N.
  
- obj-y := s390io.o s390mach.o s390dyn.o ccwcache.o sysinfo.o
- export-objs += ccwcache.o s390dyn.o s390io.o
-+obj-$(CONFIG_QDIO) += qdio.o
-+export-objs += qdio.o
++IBM S/390 and zSeries OSA-Express and HiperSockets device driver
++CONFIG_QETH
++  This driver supports the IBM S/390 and zSeries OSA Express adapters
++  in QDIO mode (all media types), HiperSockets interfaces and VM GuestLAN
++  interfaces in QDIO and HIPER mode.
 +
-+obj-$(CONFIG_S390_CMF) += cmf.o
-+export-objs += cmf.o
++  For details please refer to the documentation provided by IBM at
++  <http://www10.software.ibm.com/developerworks/opensource/linux390>
++
++  This driver is also available as a module (code which can be
++  inserted in and removed from the running kernel whenever you
++  want). If you want to compile it as a module, say 'M' here and
++  read file Documentation/modules.txt.
++
++IPv6 support for qeth
++CONFIG_QETH_IPV6
++  If CONFIG_QETH is switched on, this option will include IPv6
++  support in the qeth device driver.
++
++IEEE 802.1q VLAN support for qeth
++CONFIG_QETH_VLAN
++  If CONFIG_QETH is switched on, this option will include IEEE
++  802.1q VLAN support in the qeth device driver.
++
++Performance statistics for the qeth drivers
++CONFIG_QETH_PERF_STATS
++  When switched on, this option will add a file in the proc-fs
++  (/proc/qeth_perf_stats) containing performance statistics. It
++  may slightly impact performance, so this is only recommended for
++  internal tuning of the device driver.
++
++FCP adapter driver for IBM eServer zSeries
++CONFIG_ZFCP
++  This driver supports the IBM eServer zSeries 800/900 FCP adapter.
++  If you want to access SCSI devices attached to your zSeries
++  by means of Fibre Channel interfaces say Y.
++  For details please refer to the documentation provided by IBM at
++  <http://www10.software.ibm.com/developerworks/opensource/linux390>
++
++  This driver is also available as a module ( = code which can be
++  inserted in and removed from the running kernel whenever you want).
++  The module will be called zfcp.o. If you want to compile it as a
++  module, say M here and read <file:Documentation/modules.txt>.
++
++HBA API support for the IBM eServer z990 (GA2) FCP adapter driver
++CONFIG_ZFCP_HBAAPI
++  Say Y here to include HBA API (FC-HBA) support for z990 (GA2).
++
++  This support is also available as a separate module.
++  If you want to compile it as a module, say M here and read
++  <file:Documentation/modules.txt>.  The module will be called
++  zfcp_hbaapi.o.
++
++  If unsure, say N.
++
+ SGI WD93C93 SCSI Driver
+ CONFIG_SCSI_SGIWD93
+   Say Y here to support the on-board WD93C93 SCSI controller found (a)
+@@ -25422,6 +25487,49 @@
+   You should only select this option if you know what you are
+   doing and want to exploit this feature.
  
- obj-y += $(foreach dir,$(subdir-y),$(dir)/s390-$(dir).o)
++CONFIG_VIRT_TIMER
++  This provides a kernel interface for virtual CPU timers. 
++
++CONFIG_APPLDATA_BASE
++  This option provides a kernel interface for creating and updating
++  z/VM APPLDATA monitor records. The monitor records are updated at
++  given time intervals, once the timer is started.
++
++CONFIG_APPLDATA_MEM
++  This option provides memory management related data to the Linux -
++  z/VM Monitor Stream, for example, the paging/swapping rate and the
++  utilisation.
++
++CONFIG_APPLDATA_OS
++  This option provides operating system related data to the Linux -
++  z/VM Monitor Stream, for example, the CPU utilisation.
++
++CONFIG_APPLDATA_NET_SUM
++  This option provides network related data to the Linux - z/VM
++  Monitor Stream. The data gives a total sum of network I/O
++  statistics, no per-interface data.
++
++Collaborative memory management
++CONFIG_CMM
++  Select this option, if you want to enable the kernel interface
++  to reduce the memory size of the system. This is accomplished
++  by allocating pages of memory and put them "on hold". This only
++  makes sense for a system running under VM where the unused pages
++  will be reused by VM for other guest systems. The interface
++  allows an external monitor to balance memory of many systems.
++  Everybody who wants to run Linux under VM should select this
++  option.
++		
++/proc interface to cooperative memory management
++CONFIG_CMM_PROC
++  Select this option to enable the /proc interface to the
++  cooperative memory management. 
++
++IUCV special message interface to cooperative memory management
++CONFIG_CMM_IUCV
++  Select this option to enable the special message interface to
++  the cooperative memory management. 
++
+ Support for IBM-style disk-labels (S/390)
+ CONFIG_S390_PARTITION
+   Enable this option to assure standard IBM labels on the DASDs.
+@@ -25432,13 +25540,13 @@
+ Support for DASD hard disks
+ CONFIG_DASD
+   Enable this option if you want to access DASDs directly utilizing
+-  S/390s channel subsystem commands. This is necessary for running
++  S/390's or zSeries' channel subsystem commands. This is necessary for running
+   natively on a single image or an LPAR.
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/Makefile drivers/s390/block/Makefile
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/Makefile	2001-08-05 14:12:41.000000000 -0600
-+++ drivers/s390/block/Makefile	2006-01-30 22:25:25.000000000 -0700
-@@ -17,6 +17,8 @@
- obj-$(CONFIG_DASD_FBA)  += dasd_fba_mod.o
- obj-$(CONFIG_DASD_DIAG) += dasd_diag_mod.o
- obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
-+obj-$(CONFIG_DCSSBLK) += dcssblk.o
-+obj-$(CONFIG_S390_CMF) += dasd_cmb.o
+ Support for ECKD hard disks
+ CONFIG_DASD_ECKD
+   ECKD (Extended Count Key Data) devices are the most commonly used 
+-  devices on S/390s. You should enable this option unless you are 
++  devices on zSeries and S/390. You should enable this option unless you are 
+   very sure you have no ECKD device.
  
- include $(TOPDIR)/Rules.make
+ ECKD demand loading
+@@ -25464,6 +25572,14 @@
+ CONFIG_DASD_AUTO_DIAG
+   This option enables demand loading of the DIAG module. 
  
-@@ -31,4 +33,3 @@
++Support for Channel Measurement on DASD devices
++CONFIG_S390_CMF
++  Select this option if you want to run applications that read
++  statistical data about DASD I/O from the Channel Measurement
++  Facility.
++  If you say "M" here, two modules, "dasd_cmb.o" and "cmf.o",
++  will be created. If unsure, say "N".
++
+ Merge some code into the kernel to make the image IPLable
+ CONFIG_IPLABLE
+   If you want to use the produced kernel to IPL directly from a
+@@ -25491,45 +25607,49 @@
+   system console.  Available only if 3270 support is compiled in
+   statically.
  
- dasd_diag_mod.o: $(dasd_diag_mod-objs)
- 	$(LD) -r -o $@ $(dasd_diag_mod-objs)
+-Support for HWC line mode terminal
+-CONFIG_HWC
+-  Include support for IBM HWC line-mode terminals.
 -
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd.c drivers/s390/block/dasd.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd.c	2004-08-07 17:26:05.000000000 -0600
-+++ drivers/s390/block/dasd.c	2006-01-30 22:25:25.000000000 -0700
-@@ -6,7 +6,7 @@
-  * Bugreports.to..: <Linux390 at de.ibm.com>
-  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
-  *
-- * $Revision: 1.311 $
-+ * $Revision: 1.298.2.14 $
-  *
-  * History of changes (starts July 2000)
-  * 11/09/00 complete redesign after code review
-@@ -311,7 +311,7 @@
-         /* and add the remaining subranges                                 */
- 	for (start = index = from, end = -EINVAL; index <= to; index++) {
-                 
--                if (dasd_devindex_from_devno(index) > 0) {
-+                if (dasd_devindex_from_devno(index) >= 0) {
-                         /* current device is already in range */
-                         MESSAGE (KERN_DEBUG,
-                                  "dasd_add_range %04x-%04x: "
-@@ -741,6 +741,15 @@
- 
- 	memset (major_info->gendisk.flags, 0, DASD_PER_MAJOR * sizeof (char));
- 
-+	/* init label array */
-+	major_info->gendisk.label_arr = (devfs_handle_t *)
-+		kmalloc (DASD_PER_MAJOR * sizeof (devfs_handle_t), GFP_KERNEL);
-+	if(major_info->gendisk.label_arr == NULL)
-+		goto out_gd_label_arr;
+-Console on HWC line mode terminal
+-CONFIG_HWC_CONSOLE
+-  Include support for using an IBM HWC line-mode terminal as the Linux
++Support for SCLP
++CONFIG_SCLP
++  Include support for the IBM SCLP interface to the service element. 
 +
-+	memset (major_info->gendisk.label_arr, 0,
-+		DASD_PER_MAJOR * sizeof(devfs_handle_t));
++Support for SCLP line mode terminal
++CONFIG_SCLP_TTY
++  Include support for IBM SCLP line-mode terminals.
 +
-         /* register blockdevice */
- 	rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations);
- 	if (rc < 0) {
-@@ -861,6 +870,9 @@
- 	}
++Support for console on SCLP line mode terminal
++CONFIG_SCLP_CONSOLE
++  Include support for using an IBM SCLP line-mode terminal as a Linux
+   system console.
  
- out_reg_blkdev:
-+	kfree (major_info->gendisk.label_arr);
+-Control Program Identification
+-CONFIG_HWC_CPI
+-  Allows for Control Program Identification via the HWC interface,
+-  i.e. provides a mean to pass an OS instance name (system name)
+-  to the machine.
+-
+-  This option should only be selected as a module since the
+-  system name has to be passed as module parameter. The module
+-  will be called hwc_cpi.o.
++Support for SCLP VT220-compatible terminal
++CONFIG_SCLP_VT220_TTY
++  Include support for an IBM SCLP VT220-compatible terminal.
 +
-+out_gd_label_arr:
- 	kfree (major_info->gendisk.flags);
- 
- out_gd_flags:
-@@ -916,6 +928,7 @@
- 		major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
- 	}
++Support for console on SCLP VT220-compatible terminal
++CONFIG_SCLP_VT220_CONSOLE
++  Include support for using an IBM SCLP VT220-compatible terminal as a Linux
++  system console.
++
++Control-Program Identification
++CONFIG_SCLP_CPI
++  This option enables the hardware console interface for system
++  identification. This is commonly used for workload management and
++  gives you a nice name for the system on the service element.
++  Please select this option as a module since built-in operation is
++  completely untested.
++  You should only select this option if you know what you are doing,
++  need this feature and intend to run your kernel in LPAR.
  
-+	kfree (major_info->gendisk.label_arr);
- 	kfree (major_info->gendisk.flags);
- 	kfree (major_info->gendisk.de_arr);
+ S/390 tape device support
+ CONFIG_S390_TAPE
+   Select this option if you want to access channel-attached tape
+   devices on IBM S/390 or zSeries.
+-  If you select this option you will also want to select at
+-  least one of the tape interface options and one of the tape
+-  hardware options in order to access a tape device.
++  If you select this option you will also want to select at least
++  one of the hardware options in order to access a tape device.
+   This option is also available as a module. The module will be
+   called tape390.o and include all selected interfaces.
+   The hardware drivers will be seperate modules.
+   If unsure, say "Y".
  
-@@ -2148,11 +2161,10 @@
-                         if (cqr == ERR_PTR(-ENOMEM)) {
-                                 break;
-                         }
+-Support for tape character devices
+-CONFIG_S390_TAPE_CHAR
+-  Select this option if you want to access your channel-attached
+-  tape devices using the character device interface.
+-  This interface is similar to other Linux tape devices like
+-  SCSI-Tapes (st) and the floppy tape device (ftape).
+-  If unsure, say "Y".
 -
--                        MESSAGE (KERN_EMERG,
--                                 "(%04x) CCW creation failed "
--                                 "on request %p",
--                                 device->devinfo.devno, req);
-+                        DEV_MESSAGE (KERN_EMERG, device,
-+                                     "CCW creation failed "
-+                                     "on request %p rc = %ld",
-+                                     req, PTR_ERR(cqr));
-                         dasd_dequeue_request (queue,req);
-                         dasd_end_request (req, 0);
-                         continue;
-@@ -2434,6 +2446,10 @@
-                 era = dasd_era_recover;
-         }
-         
-+	/* process channel measurement facility configuration while 
-+	   the channel is idle */
-+	cmf_device_callback(&device->cdev);
-+
-         switch (era) {
-         case dasd_era_none:
- 		check_then_set(&cqr->status, 
-@@ -3157,12 +3173,15 @@
-                 spin_lock_irqsave (&range_lock, flags);
-                 list_for_each (l, &dasd_range_head.list) {
-                         temp = list_entry (l, dasd_range_t, list);
--                        if (device->devinfo.devno >= temp->from && device->devinfo.devno <= temp->to) {
-+                        if (device->devinfo.devno >= temp->from && 
-+                            device->devinfo.devno <= temp->to) {
-                                 spin_unlock_irqrestore (&range_lock, flags);
-                                 if (intval)
--                                        temp->features |= DASD_FEATURE_READONLY;
-+                                        temp->features |= 
-+                                                DASD_FEATURE_READONLY;
-                                 else
--                                        temp->features &= ~DASD_FEATURE_READONLY;
-+                                        temp->features &= 
-+                                                ~DASD_FEATURE_READONLY;
-                                 goto continue_blkroset;
-                         }
-                         devindex += temp->to - temp->from + 1;
-@@ -3717,7 +3736,8 @@
-         unsigned long flags;
- 	int i, devno;
- 
--	/* find out devno of leaving device: CIO has already deleted this information ! */
-+	/* find out devno of leaving device: CIO has already deleted this */
-+        /* information !                                                  */
-         devno = -ENODEV;
- 	device = NULL;
- 	list_for_each (l, &dasd_major_info) {
-@@ -3815,7 +3835,7 @@
- 	}
- 
-         if (device &&
--            device->level >= DASD_STATE_READY) {
-+            device->level >= DASD_STATE_NEW) {
-                 s390irq_spin_lock_irqsave (device->devinfo.irq, 
-                                            flags);
- 		DEV_MESSAGE (KERN_DEBUG, device, "%s",
-@@ -3876,6 +3896,7 @@
-         int i;
-         dasd_device_t* device;
-         dasd_lowmem_t *lowmem;
-+        struct list_head *lmem, *next;
-         int rc;
- 
- 
-@@ -3892,7 +3913,9 @@
-         memset (device, 0, sizeof (dasd_device_t));
-         dasd_plug_device (device);
-         INIT_LIST_HEAD (&device->lowmem_pool);
--        
-+
-+	cmf_device_init(&device->cdev, devno);
-+       
-         /* allocate pages for lowmem pool */
-         for (i = 0; i < DASD_LOWMEM_PAGES; i++) {
-                 
-@@ -3906,7 +3929,8 @@
- 
-         if (i < DASD_LOWMEM_PAGES) {
-                 /* didn't get the needed lowmem pages */
--                list_for_each_entry (lowmem, &device->lowmem_pool, list) {
-+                list_for_each_safe (lmem, next, &device->lowmem_pool) {
-+                        lowmem = list_entry (lmem, dasd_lowmem_t, list);
-                         MESSAGE (KERN_DEBUG,
-                                  "<devno: %04x> not enough memory - "
-                                  "Free page again :%p",
-@@ -3926,6 +3950,7 @@
- dasd_state_new_to_del (dasd_device_t **addr, int devno)
- {
-         dasd_lowmem_t *lowmem;
-+        struct list_head *l,*n;
- 
-         dasd_device_t *device = *addr;
- 
-@@ -3935,7 +3960,9 @@
-         }
- 
-         /* free lowmem_pool */
--        list_for_each_entry (lowmem, &device->lowmem_pool, list) {
-+	list_for_each_safe (l, n, &device->lowmem_pool) {
-+                lowmem = list_entry (l, dasd_lowmem_t, list);
-+                list_del(&lowmem->list);
-                 free_page ((unsigned long) lowmem);
-         }
- 
-@@ -4655,17 +4682,15 @@
- 		   loff_t * offset)
- {
- 	loff_t len;
--	loff_t n = *offset;
--	unsigned pos = n;
- 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
- 
--	if (n != pos || pos >= p_info->len) {
-+	if (*offset >= p_info->len) {
- 		return 0;	/* EOF */
- 	} else {
--		len = MIN (user_len, (p_info->len - pos));
--		if (copy_to_user (user_buf, &(p_info->data[pos]), len))
-+		len = MIN (user_len, (p_info->len - *offset));
-+		if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
- 			return -EFAULT;
--		*offset = pos + len;
-+		(*offset) += len;
- 		return len;	/* number of bytes "read" */
- 	}
- }
-@@ -5188,6 +5213,7 @@
-                          "/proc/dasd/statistics: only 'set' and "
-                          "'reset' are supported verbs");
- 
-+		vfree (buffer);
-         	return -EINVAL;
- 	}
- 
-@@ -5243,6 +5269,7 @@
-         
- 
- #endif /* DASD_PROFILE */
-+        vfree (buffer);
- 	return user_len;
- }
- 
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd_3990_erp.c drivers/s390/block/dasd_3990_erp.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd_3990_erp.c	2004-02-18 06:36:31.000000000 -0700
-+++ drivers/s390/block/dasd_3990_erp.c	2006-01-30 22:25:25.000000000 -0700
-@@ -5,7 +5,7 @@
-  * Bugreports.to..: <Linux390 at de.ibm.com>
-  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
-  *
-- * $Revision: 1.52 $
-+ * $Revision: 1.52.2.4 $
-  *
-  * History of changes:
-  * 05/14/01 fixed PL030160GTO (BUG() in erp_action_5)
-@@ -585,9 +585,8 @@
-                              ioinfo[irq]->opm);
- 
- 		/* reset status to queued to handle the request again... */
--		check_then_set (&erp->status,
--                                CQR_STATUS_ERROR,
--                                CQR_STATUS_QUEUED);
-+		if (erp->status > CQR_STATUS_QUEUED)
-+                        erp->status = CQR_STATUS_QUEUED;
- 
-                 erp->retries = 1;
-                 
-@@ -598,12 +597,10 @@
-                              "opm=%x) -> permanent error",
-                              erp->dstat->lpum,
-                              ioinfo[irq]->opm);
--                
--                /* post request with permanent error */
--                check_then_set (&erp->status,
--                                CQR_STATUS_ERROR,
--                                CQR_STATUS_FAILED);
- 
-+                /* post request with permanent error */
-+		if (erp->status > CQR_STATUS_QUEUED)
-+                        erp->status = CQR_STATUS_FAILED;
-         }
-         
- } /* end dasd_3990_erp_alternate_path */
-@@ -757,6 +754,12 @@
+ Support for tape block devices
+ CONFIG_S390_TAPE_BLOCK
+   Select this option if you want to access your channel-attached tape
+@@ -25539,22 +25659,12 @@
+   Documentation/s390/TAPE for further information about creating
+   volumes for and using this interface.  It is safe to say "Y" here.
  
-                         dasd_3990_erp_block_queue (erp,
-                                                    30);
-+                } else if (sense[25] == 0x1E) {	/* busy */
-+                        DEV_MESSAGE (KERN_INFO, device,
-+                                     "busy - redriving request later, "
-+                                     "%d retries left",
-+                                     erp->retries);
-+                        dasd_3990_erp_block_queue (erp, 1);
-                 } else {
- 			DEV_MESSAGE (KERN_INFO, device, 
-                                      "redriving request immediately, "
-@@ -768,7 +771,6 @@
-                                         CQR_STATUS_QUEUED);
-                 }
-         }
+-Support for 3490 tape hardware
+-CONFIG_S390_TAPE_3490
+-  Select this option if you want to access IBM 3490 magnetic
++Support for 3480/3490 tape hardware
++CONFIG_S390_TAPE_34XX
++  Select this option if you want to access IBM 3480/3490 magnetic
+   tape subsystems and 100% compatibles.
+   This option is also available as a module. The module will be
+-  called tape3490.o. If CONFIG_S390_TAPE is selected as a module,
+-  this hardware driver cannot be built-in but is only available
+-  as a module.
+-  It is safe to say "Y" here.
 -
- 	return erp;
- 
- } /* end dasd_3990_erp_action_4 */
-@@ -2386,7 +2388,7 @@
-                 switch (sense[28]) {
-                 case 0x17:
-                         /* issue a Diagnostic Control command with an 
--                                * Inhibit Write subcommand and controler modifier */
-+                         * Inhibit Write subcommand and controler modifier */
-                         erp = dasd_3990_erp_DCTL (erp,
-                                                   0x20);
-                         break;
-@@ -2603,9 +2605,9 @@
-                                      "Data recovered during retry with PCI "
-                                      "fetch mode active");
-                         
--                        /* not possible to handle this situation in Linux */    
--                        panic("Invalid data - No way to inform appliction about "
--                              "the possibly incorret data");
-+                        /* not possible to handle this situation in Linux */
-+                        panic("Invalid data - No way to inform application "
-+                              "about the possibly incorrect data");
- 			break;
- 
- 		case 0x1D:	/* state-change pending */
-@@ -2617,6 +2619,12 @@
-                                                       sense);
- 			break;
- 
-+		case 0x1E:	/* busy */
-+                        DEV_MESSAGE (KERN_DEBUG, device, "%s",
-+                                     "Busy condition exists "
-+                                     "for the subsystem or device");
-+                        erp = dasd_3990_erp_action_4 (erp, sense);
-+			break;
- 		default:	
-                         ;       /* all others errors - default erp  */
- 		}
-@@ -2699,7 +2707,8 @@
- 	if (!erp) {
-                 if (cqr->retries <= 0) {
-                         DEV_MESSAGE (KERN_ERR, device, "%s",
--                                     "Unable to allocate ERP request (NO retries left)");
-+                                     "Unable to allocate ERP request "
-+                                     "(NO retries left)");
-                 
-                         check_then_set (&cqr->status,
-                                         CQR_STATUS_ERROR,
-@@ -2709,7 +2718,8 @@
+-Support for 3480 tape hardware
+-CONFIG_S390_TAPE_3480
+-  Select this option if you want to access IBM 3480 magnetic
+-  tape subsystems and 100% compatibles.
+-  This option is also available as a module. The module will be
+-  called tape3480.o. If CONFIG_S390_TAPE is selected as a module,
++  called tape_34xx.o. If CONFIG_S390_TAPE is selected as a module,
+   this hardware driver cannot be built-in but is only available
+   as a module.
+   It is safe to say "Y" here.
+@@ -25574,7 +25684,11 @@
+   or zSeries as a disk.  This is useful as a _fast_ swap device if you
+   want to access more than 2G of memory when running in 31 bit mode.
+   This option is also available as a module which will be called
+-  xpram.o.  If unsure, say "N".
++  xpram.o.  If unsure, say M.
++
++CONFIG_DCSSBLK
++  A block device driver for DCSS segments. It can be used to create
++  a filesystem in such a segment.
  
-                 } else {
-                         DEV_MESSAGE (KERN_ERR, device,
--                                     "Unable to allocate ERP request (%i retries left)",
-+                                     "Unable to allocate ERP request "
-+                                     "(%i retries left)",
-                                      cqr->retries);
-                 
-                         if (!timer_pending(&device->timer)) {
-@@ -3169,8 +3179,9 @@
-                 dasd_chanq_enq_head (&device->queue,
-                                      erp);
-         } else {
--                if ((erp->status == CQR_STATUS_FILLED ) || (erp != device->queue.head)) {
--                        /* something strange happened - log the error and panic */
-+                if ((erp->status == CQR_STATUS_FILLED ) || 
-+                    (erp != device->queue.head)) {
-+                        /* something strange happened - log error and panic */
-                         /* print current erp_chain */
-                         DEV_MESSAGE (KERN_DEBUG, device, "%s",
-                                      "ERP chain at END of ERP-ACTION");
-@@ -3188,7 +3199,8 @@
-                                                      temp_erp->refers);
-                                 }
-                         }
--                        panic ("Problems with ERP chain!!! Please report to linux390 at de.ibm.com");
-+                        panic ("Problems with ERP chain!!! "
-+                               "Please report to linux390 at de.ibm.com");
-                 }
+ Fast IRQ handling
+ CONFIG_FAST_IRQ
+@@ -25584,11 +25698,51 @@
+   interrupts which will also be processed before leaving the interrupt
+   context.  This speeds up the I/O a lot. Say "Y".
  
-         }
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd_cmb.c drivers/s390/block/dasd_cmb.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd_cmb.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/block/dasd_cmb.c	2006-01-30 22:25:25.000000000 -0700
-@@ -0,0 +1,236 @@
-+/*
-+ * linux/drivers/s390/block/dasd_cmb.c ($Revision: 1.7.6.2 $)
-+ *
-+ * Linux on zSeries Channel Measurement Facility support
-+ *  (dasd device driver interface)
-+ *
-+ * Copyright 2000,2003 IBM Corporation
-+ *
-+ * Author: Arnd Bergmann <arndb at de.ibm.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2, or (at your option)
-+ * any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-+ */
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <asm/cmb.h>
-+#include <asm/ioctl32.h>
-+
-+#include "dasd_int.h"
+-IUCV device support (VM only)
++IUCV support (VM only)
+ CONFIG_IUCV
+   Select this option if you want to use inter-user communication
+-  vehicle networking under VM or VIF.  This option is also available
+-  as a module which will be called iucv.o. If unsure, say "Y".
++  under VM or VIF. If unsure, say "Y" to enable a fast communication
++  link between VM guests. At boot time the user ID of the guest needs
++  to be passed to the kernel. Note that both kernels need to be
++  compiled with this option and both need to be booted with the user ID
++  of the other VM guest.
 +
-+/* This mutex protects us from a race between enable and disable for
-+ * a single device. Making it global instead of per device reduces
-+ * the memory requirement and makes it possible to use a single
-+ * completion handler and return value */
-+static DECLARE_MUTEX(cmf_setup_mutex);
-+static DECLARE_COMPLETION(cmf_setup_completion);
-+static int cmf_setup_return;
++IUCV network device support (VM only)
++CONFIG_NETIUCV
++  Select this option if you want to use inter-user communication
++  vehicle networking under VM or VIF. It enables a fast communication
++  link between VM guests. Using ifconfig a point-to-point connection
++  can be established to the Linux for zSeries and S/390 system
++  running on the other VM guest. This option is also available
++  as a module which will be called netiucv.o. If unsure, say "Y".
 +
-+static void
-+dasd_cmf_enable_callback(struct cmf_device *cdev)
-+{
-+	cdev->callback = NULL;
-+	cmf_setup_return = set_cmf(cdev, 2);
-+	complete(&cmf_setup_completion);
-+}
++IUCV special message support (VM only)
++CONFIG_SMSGIUCV
++  Select this option if you want to be able to receive SMSG messages
++  from other VM guest systems.
 +
-+static void
-+dasd_cmf_disable_callback(struct cmf_device *cdev)
-+{
-+	cdev->callback = NULL;
-+	cmf_setup_return = set_cmf(cdev, 0);
-+	complete(&cmf_setup_completion);
-+}
++Support for the z/VM recording system services (VM only)
++CONFIG_VMLOGRDR
++  Select this option if you want to be able to receive records collected 
++  by the z/VM recording system services, eg. from *LOGREC. This option 
++  should be build as a module since the actual service to connect to 
++  has to be specified at module load time. The module will be called 
++  vmlogrdr.o.
++  This driver depends on the IUCV support driver. 
 +
-+static inline int
-+dasd_cmf_device_busy(struct dasd_device_t *device)
-+{
-+	ccw_req_t *cqr;
-+	for (cqr = device->queue.head; cqr; cqr = cqr->next) {
-+		if (cqr->status == CQR_STATUS_IN_IO)
-+			return 1;
-+	}
-+	return 0;
-+}
++Process warning machine checks
++CONFIG_MACHCHK_WARNING
++  Select this option if you want the machine check handler on IBM S/390 or 
++  zSeries to process warning machine checks (e.g. on power failures). 
++  If unsure, say "Y".
 +
-+static int
-+dasd_ioctl_cmf_enable(void *inp, int no, long args)
-+{
-+	struct dasd_device_t *device;
-+	int ret;
++Use chscs for Common I/O
++CONFIG_CHSC
++  Select this option if you want the s390 common I/O layer to use information
++  obtained by channel subsystem calls. This will enable Linux to process link
++  failures and resource accessibility events. Moreover, if you have procfs
++  enabled, you'll be able to toggle chpids logically offline and online. Even
++  if you don't understand what this means, you should say "Y".
+ 
+ Process warning machine checks
+ CONFIG_MACHCHK_WARNING
+@@ -25625,6 +25779,15 @@
+   (and some other stuff like libraries and such) is needed for
+   executing 31 bit applications.  It is safe to say "Y".
+ 
++Lan Channel Station (LCS) Interface
++CONFIG_LCS
++  Select this option if you want to use LCS networking  on IBM S/390 
++  or zSeries. This device driver supports Token Ring (IEEE 802.5),
++  FDDI (IEEE 802.7) and Ethernet. 
++  It will use the channel device configuration if this is available.  
++  This option is also available as a module which will be
++  called lcs.o . If you do not know what it is, it's safe to say "Y".
 +
-+	device = dasd_device_from_kdev (((struct inode*)inp)->i_rdev);
-+	if (!device)
-+		return -EINVAL;
-+	
-+	if (down_interruptible(&cmf_setup_mutex))
-+		return -ERESTARTSYS;
+ Channel Device Configuration
+ CONFIG_CHANDEV
+   The channel device layer is a layer to provide a consistent
+@@ -25661,6 +25824,19 @@
+   For more info see the chandev manpage usually distributed in
+   <file:Documentation/s390/chandev.8> in the Linux source tree.
+ 
++IBM S/390 and zSeries PCICC and PCICA device driver
++CONFIG_Z90CRYPT
++  This driver supports the IBM S/390 and zSeries Cryptographic
++  Coprocessor (PCICC) and Crytocraphic Accelerator (PCICA) adapters.
 +
-+	/* the device may already be enabled, in this case
-+	   we just reset the cmb to 0 */
-+	if (!list_empty(&device->cdev.cmb_list)) {
-+		ret = 0;
-+		goto out_reset;
-+	}
-+		
-+	ret = enable_cmf(&device->cdev);
-+	if (ret)
-+		goto out;
++  For details please refer to the documentation provided by IBM at
++  <http://www10.software.ibm.com/developerworks/opensource/linux390>.
 +
-+	MOD_INC_USE_COUNT;
++  This driver is also available as a module (code which can be
++  inserted in and removed from the running kernel whenever you
++  want). If you want to compile it as a module, say 'M' here and
++  read file Documentation/modules.txt.
 +
-+	spin_lock_irq(device->cdev.ccwlock);
-+	if (!dasd_cmf_device_busy(device)) {
-+		ret = set_cmf(&device->cdev, 2);
-+		spin_unlock_irq(device->cdev.ccwlock);
-+	} else {
-+		device->cdev.callback = &dasd_cmf_enable_callback;
-+		spin_unlock_irq(device->cdev.ccwlock);
-+		wait_for_completion(&cmf_setup_completion);
-+		ret = cmf_setup_return;
-+	}
+ SAB3036 tuner support
+ CONFIG_TUNER_3036
+   Say Y here to include support for Philips SAB3036 compatible tuners.
+diff -urN kernel-source-2.4.27.orig/Documentation/DocBook/Makefile Documentation/DocBook/Makefile
+--- kernel-source-2.4.27.orig/Documentation/DocBook/Makefile	2004-08-07 17:26:04.000000000 -0600
++++ Documentation/DocBook/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -2,7 +2,7 @@
+ 	   kernel-api.sgml parportbook.sgml kernel-hacking.sgml \
+ 	   kernel-locking.sgml via-audio.sgml mousedrivers.sgml sis900.sgml \
+ 	   deviceiobook.sgml procfs-guide.sgml tulip-user.sgml \
+-	   journal-api.sgml libata.sgml
++	   journal-api.sgml zfcp-hba-api.sgml libata.sgml
+ 
+ PS	:=	$(patsubst %.sgml, %.ps, $(BOOKS))
+ PDF	:=	$(patsubst %.sgml, %.pdf, $(BOOKS))
+@@ -159,6 +159,14 @@
+ 	$(TOPDIR)/scripts/docgen   $(JBDSOURCES) \
+ 		<journal-api.tmpl >journal-api.sgml
+ 
++ZFCPHBAAPISOURCES :=  $(TOPDIR)/drivers/s390/scsi/zh.h \
++	       $(TOPDIR)/drivers/s390/scsi/zh_main.c \
++	       $(TOPDIR)/drivers/s390/scsi/zfcp_zh.h \
++	       $(TOPDIR)/drivers/s390/scsi/zfcp_zh.c
 +
-+	if (ret) {
-+		disable_cmf(&device->cdev);
-+		MOD_DEC_USE_COUNT;
-+	}
++zfcp-hba-api.sgml: zfcp-hba-api.tmpl $(ZFCPHBAAPISOURCES)
++	$(TOPDIR)/scripts/docgen   $(ZFCPHBAAPISOURCES) \
++		<zfcp-hba-api.tmpl >zfcp-hba-api.sgml
+ 
+ DVI	:=	$(patsubst %.sgml, %.dvi, $(BOOKS))
+ AUX	:=	$(patsubst %.sgml, %.aux, $(BOOKS))
+diff -urN kernel-source-2.4.27.orig/Documentation/DocBook/zfcp-hba-api.tmpl Documentation/DocBook/zfcp-hba-api.tmpl
+--- kernel-source-2.4.27.orig/Documentation/DocBook/zfcp-hba-api.tmpl	1969-12-31 17:00:00.000000000 -0700
++++ Documentation/DocBook/zfcp-hba-api.tmpl	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,747 @@
++<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN"[]>
 +
-+out_reset:
-+	cmf_reset(&device->cdev);
-+out:
-+	up(&cmf_setup_mutex);
-+	return ret;
-+}
-+
-+static int
-+dasd_ioctl_cmf_disable(void *inp, int no, long args)
-+{
-+	struct dasd_device_t *device;
-+	int ret;
-+
-+	device = dasd_device_from_kdev (((struct inode*)inp)->i_rdev);
-+	if (!device)
-+		return -EINVAL;
-+
-+	if (down_interruptible(&cmf_setup_mutex))
-+		return -ERESTARTSYS;
-+
-+	spin_lock_irq(device->cdev.ccwlock);
-+
-+	if (!dasd_cmf_device_busy(device)) {
-+		ret = set_cmf(&device->cdev, 0);
-+		spin_unlock_irq(device->cdev.ccwlock);
-+	} else {
-+		device->cdev.callback = &dasd_cmf_disable_callback;
-+		spin_unlock_irq(device->cdev.ccwlock);
-+		wait_for_completion(&cmf_setup_completion);
-+		ret = cmf_setup_return;
-+	}
-+
-+	if(!ret) {
-+		disable_cmf(&device->cdev);
-+		MOD_DEC_USE_COUNT;
-+	}
-+	up(&cmf_setup_mutex);
-+	return ret;
-+
-+}
-+
-+static int
-+dasd_ioctl_readall_cmb(void *inp, int no, long args)
-+{
-+	struct dasd_device_t *device;
-+	struct cmbdata * udata;
-+	struct cmbdata data;
-+	size_t size;
-+	int ret;
-+
-+	device = dasd_device_from_kdev (((struct inode*)inp)->i_rdev);
-+	if (!device)
-+		return -EINVAL;
-+	udata = (void *) args;
-+	size = _IOC_SIZE(no);
-+
-+	if (!access_ok(VERIFY_WRITE, udata, size))
-+		return -EFAULT;
-+	ret = cmf_readall(&device->cdev, &data);
-+	if (ret)
-+		return ret;
-+	if (copy_to_user(udata, &data, min(size, sizeof(*udata))))
-+		return -EFAULT;
-+	return 0;
-+}
-+
-+/* module initialization below here. dasd already provides a mechanism
-+ * to dynamically register ioctl functions, so we simply use this.
-+ * FIXME: register ioctl32 functions as well. */
-+static inline int
-+ioctl_reg(unsigned int no, dasd_ioctl_fn_t handler)
-+{
-+	int ret;
-+	ret = dasd_ioctl_no_register(THIS_MODULE, no, handler);
-+	if (ret)
-+		return ret;
-+
-+	ret = register_ioctl32_conversion(no, sys_ioctl);
-+	if (ret)
-+		dasd_ioctl_no_unregister(THIS_MODULE, no, handler);
-+
-+	return ret;
-+}
-+
-+static inline void
-+ioctl_unreg(unsigned int no, dasd_ioctl_fn_t handler)
-+{
-+	dasd_ioctl_no_unregister(THIS_MODULE, no, handler);
-+	unregister_ioctl32_conversion(no);
-+
-+}
-+
-+static void
-+dasd_cmf_exit(void)
-+{
-+	ioctl_unreg(BIODASDCMFENABLE,  dasd_ioctl_cmf_enable);
-+	ioctl_unreg(BIODASDCMFDISABLE, dasd_ioctl_cmf_disable);
-+	ioctl_unreg(BIODASDREADALLCMB, dasd_ioctl_readall_cmb);
-+}
-+
-+static int __init
-+dasd_cmf_init(void)
-+{
-+	int ret;
-+	ret = ioctl_reg (BIODASDCMFENABLE, dasd_ioctl_cmf_enable);
-+	if (ret)
-+		goto err;
-+	ret = ioctl_reg (BIODASDCMFDISABLE, dasd_ioctl_cmf_disable);
-+	if (ret)
-+		goto err;
-+	ret = ioctl_reg (BIODASDREADALLCMB, dasd_ioctl_readall_cmb);
-+	if (ret)
-+		goto err;
-+
-+	return 0;
-+err:
-+	dasd_cmf_exit();
-+
-+	return ret;
-+}
-+
-+module_init(dasd_cmf_init);
-+module_exit(dasd_cmf_exit);
-+
-+MODULE_AUTHOR("Arnd Bergmann <arndb at de.ibm.com>");
-+MODULE_LICENSE("GPL");
-+MODULE_DESCRIPTION("channel measurement facility interface for dasd\n"
-+		   "Copyright 2003 IBM Corporation\n");
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd_diag.c drivers/s390/block/dasd_diag.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd_diag.c	2004-02-18 06:36:31.000000000 -0700
-+++ drivers/s390/block/dasd_diag.c	2006-01-30 22:25:25.000000000 -0700
-@@ -6,7 +6,7 @@
-  * Bugreports.to..: <Linux390 at de.ibm.com>
-  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
-  *
-- * $Revision: 1.49 $
-+ * $Revision: 1.47.6.2 $
-  *
-  * History of changes
-  * 07/13/00 Added fixup sections for diagnoses ans saved some registers
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd_fba.c drivers/s390/block/dasd_fba.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd_fba.c	2004-02-18 06:36:31.000000000 -0700
-+++ drivers/s390/block/dasd_fba.c	2006-01-30 22:25:25.000000000 -0700
-@@ -4,7 +4,7 @@
-  * Bugreports.to..: <Linux390 at de.ibm.com>
-  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
-  *
-- * $Revision: 1.50 $
-+ * $Revision: 1.49.6.3 $
-  *
-  * History of changes
-  *          fixed partition handling and HDIO_GETGEO
-@@ -94,14 +94,18 @@
-         return rc;
- }
- 
--static inline void
-+static inline int
- locate_record (ccw1_t * ccw, LO_fba_data_t * LO_data, int rw, int block_nr,
- 	       int block_ct, ccw_req_t* cqr, dasd_device_t* device)
- {
-+        int errcode;
-+
- 	memset (LO_data, 0, sizeof (LO_fba_data_t));
- 	ccw->cmd_code = DASD_FBA_CCW_LOCATE;
- 	ccw->count = 8;
--	dasd_set_normalized_cda (ccw, __pa (LO_data), cqr, device);
-+	if ((errcode = dasd_set_normalized_cda (ccw, __pa (LO_data), cqr, 
-+                                                device)))
-+                return errcode;
- 	if (rw == WRITE)
- 		LO_data->operation.cmd = 0x5;
- 	else if (rw == READ)
-@@ -110,6 +114,8 @@
- 		LO_data->operation.cmd = 0x8;
- 	LO_data->blk_nr = block_nr;
- 	LO_data->blk_ct = block_ct;
-+
-+        return 0;
- }
- 
- static int
-@@ -248,7 +254,7 @@
- 	    stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
- 		    return dasd_era_none;
- 
--	switch (device->devinfo.sid_data.dev_model) {
-+	switch (device->devinfo.sid_data.dev_type) {
- 	case 0x3370:
- 		return dasd_3370_erp_examine (cqr, stat);
- 	case 0x9336:
-@@ -292,7 +298,8 @@
- 	int byt_per_blk = device->sizes.bp_block;
-         unsigned long reloc_sector = req->sector + 
-                 device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect;
--        
-+        int errcode;
-+
- 	if (req->cmd == READ) {
- 		rw_cmd = DASD_FBA_CCW_READ;
- 	} else if (req->cmd == WRITE) {
-@@ -337,29 +344,30 @@
- 	LO_data = rw_cp->data + sizeof (DE_fba_data_t);
- 	ccw = rw_cp->cpaddr;
- 
--	if (define_extent (ccw, DE_data, req->cmd, byt_per_blk,
--                           reloc_sector, req->nr_sectors, rw_cp, device)) {
-+	if ((errcode = define_extent (ccw, DE_data, req->cmd, byt_per_blk,
-+                                      reloc_sector, req->nr_sectors, rw_cp, 
-+                                      device)))
-                 goto clear_rw_cp;
--        }
-+
- 	ccw->flags |= CCW_FLAG_CC;
-         ccw ++;
--        locate_record (ccw, LO_data, req->cmd, 0, 
--                       private->rdc_data.mode.bits.data_chain ? bhct : 1, rw_cp, device);
--        if (ccw->cda == 0) {
-+        if ((errcode = locate_record (ccw, LO_data, req->cmd, 0, 
-+                                      private->rdc_data.mode.bits.data_chain ? 
-+                                      bhct : 1, rw_cp, device)))
-                 goto clear_rw_cp;
--        }
-+
-         ccw->flags |= CCW_FLAG_CC;
-         
--        bh = req -> bh;
--        i = 0;
--        while ( bh != NULL ) {
-+        for (bh = req->bh, i = 0; bh != NULL; ) {
-                 for (size = 0; size < bh->b_size; size += byt_per_blk) {
-                         ccw ++;
-                         ccw->cmd_code = rw_cmd;
-                         ccw->count = byt_per_blk;
--                        if (dasd_set_normalized_cda (ccw,__pa (bh->b_data + size), rw_cp, device)) {
-+                        if ((errcode = dasd_set_normalized_cda (ccw,
-+                                               __pa (bh->b_data + size), 
-+                                               rw_cp, device))) 
-                                 goto clear_rw_cp;
--                        }
-+                        
-                         if (private->rdc_data.mode.bits.data_chain) {
-                                 ccw->flags |= CCW_FLAG_DC;
-                         } else {
-@@ -372,23 +380,25 @@
-                         ccw++;
-                         i++;
-                         LO_data++;
--                        locate_record (ccw, LO_data, req->cmd, i, 1, rw_cp, device);
--                        if (ccw->cda == 0) {
-+                        if ((errcode = locate_record (ccw, LO_data, req->cmd,
-+                                                      i, 1, rw_cp, device)))
-                                 goto clear_rw_cp;
--                        }
-+                        
-                         ccw->flags |= CCW_FLAG_CC;
-                 }
-         }
- 	ccw->flags &= ~(CCW_FLAG_DC | CCW_FLAG_CC);
--
- 	rw_cp->device = device;
- 	rw_cp->expires = 5 * TOD_MIN;		/* 5 minutes */
- 	rw_cp->req = req;
-+	rw_cp->lpm = LPM_ANYPATH;
-+	rw_cp->retries = 256;
-+	rw_cp->buildclk = get_clock ();
- 	check_then_set (&rw_cp->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED);
-         goto out;
-  clear_rw_cp:
-         dasd_free_request (rw_cp, device);
--        rw_cp = NULL;
-+        rw_cp = ERR_PTR(errcode);
-  out:
- 	return rw_cp;
- }
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd_int.h drivers/s390/block/dasd_int.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd_int.h	2004-02-18 06:36:31.000000000 -0700
-+++ drivers/s390/block/dasd_int.h	2006-01-30 22:25:25.000000000 -0700
-@@ -5,7 +5,7 @@
-  * Bugreports.to..: <Linux390 at de.ibm.com>
-  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
-  *
-- * $Revision: 1.36 $
-+ * $Revision: 1.30.4.5 $
-  *
-  * History of changes (starts July 2000)
-  * 02/01/01 added dynamic registration of ioctls
-@@ -15,6 +15,7 @@
- #define DASD_INT_H
- 
- #include <asm/dasd.h>
-+#include <asm/cmb.h>
- 
- #define CONFIG_DASD_DYNAMIC
- 
-@@ -443,6 +444,7 @@
-         atomic_t plugged;
-         int stopped;                         /* device (do_IO) was stopped */
-         struct list_head lowmem_pool;
-+        struct cmf_device cdev;
- }  dasd_device_t;
- 
- /* reasons why device (do_IO) was stopped */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dcssblk.c drivers/s390/block/dcssblk.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dcssblk.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/block/dcssblk.c	2006-01-30 22:25:25.000000000 -0700
-@@ -0,0 +1,780 @@
-+/*
-+ * dcssblk.c -- the S/390 block driver for dcss memory
-+ *
-+ * Author: Carsten Otte
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/version.h>
-+#ifdef CONFIG_PROC_FS
-+#include <linux/proc_fs.h>
-+#endif	
-+#include <linux/devfs_fs_kernel.h>
-+#include <linux/ctype.h>  /* isdigit, isxdigit */
-+#include <linux/errno.h>
-+#include <linux/init.h>
-+#include <linux/slab.h>
-+#include <linux/blk.h>
-+#include <linux/blkpg.h>
-+#include <linux/hdreg.h>  /* HDIO_GETGEO */
-+#include <linux/kdev_t.h>
-+#include <asm/uaccess.h>
-+#include <asm/dcss.h>
++<!--  ZFCP HBA API Kernel Interfaces. -->
++<!--  Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, -->
++<!--                     IBM Corporation -->
++<!--  Permission is granted to copy, distribute and/or modify this -->
++<!--  document under the terms of the GNU Free Documentation License, -->
++<!--  Version 1.1 or any later version published by the Free Software -->
++<!--  Foundation; with no Invariant Sections, no Front-Cover Texts and -->
++<!--  no Back-Cover Texts.  A copy of the license is included in the -->
++<!--  section entitled "GNU Free Documentation License". -->
 +
-+#define PRINT_DEBUG(x...)	printk(KERN_DEBUG DCSSBLK_NAME " debug:" x)
-+#define PRINT_INFO(x...)	printk(KERN_INFO DCSSBLK_NAME " info:" x)
-+#define PRINT_WARN(x...)	printk(KERN_WARNING DCSSBLK_NAME " warning:" x)
-+#define PRINT_ERR(x...)		printk(KERN_ERR DCSSBLK_NAME " error:" x)
-+#define DCSSBLK_NAME "dcssblk"
++<book id="ZFCPHBAAPI">
 +
-+#ifdef CONFIG_PROC_FS
-+static struct proc_dir_entry *dcssblk_proc_root_entry;
-+static struct proc_dir_entry *dcssblk_add_entry;
-+static struct proc_dir_entry *dcssblk_remove_entry;
-+static struct proc_dir_entry *dcssblk_list_entry;
-+#endif
-+static devfs_handle_t dcssblk_devfs_dir;
-+unsigned int dcssblk_blksizes[1<<MINORBITS];
++<!-- book header -->
++  <bookinfo>
++    <title>ZFCP HBA API Kernel Interfaces</title>
 +
-+static int dcssblk_open (struct inode *inode, struct file *filp);
-+static int dcssblk_release (struct inode *inode, struct file *filp);
++    <revhistory>
++      <revision>
++	<revnumber>v1.0 </revnumber>
++	<date>2003/09/24</date>
++	<authorinitials>AH</authorinitials>
++	<revremark>
++	  <remark>Initial version.</remark>
++	</revremark>
++      </revision>
++      <revision>
++	<revnumber>v1.1 </revnumber>
++	<date>2003/11/14</date>
++	<authorinitials>AH</authorinitials>
++	<revremark>
++	  <remark>Completed interface description for module
++	    <filename>zfcp_hbaapi</filename>.
++	  </remark>
++<!--     Added Stefan V&ouml;lkel as author. -->
++<!--     Removed improper license statement regarding ZFCP HBA API Library. -->
++<!--     Added Introduction and Bibliography. -->
++<!--     Added section about misc device provided by module zfcp_hbaapi. -->
++<!--     Added section about callback functions of module zfcp_hbaapi. -->
++	</revremark>
++      </revision>
++      <revision>
++	<revnumber>v1.2 </revnumber>
++	<date>2003/11/19</date>
++	<authorinitials>AH</authorinitials>
++	<revremark>
++	  <remark>Completed interface description for module
++	    <filename>zfcp</filename>.
++	  </remark>
++<!--     Added section about intra-kernel interfaces of module zfcp. -->
++<!--     Added section about callbacks and hooks in module zfcp. -->
++	</revremark>
++      </revision>
++    </revhistory>
 +
-+static struct block_device_operations dcssblk_devops =
-+	{
-+		owner:   THIS_MODULE,
-+		open:    dcssblk_open,
-+		release: dcssblk_release,
-+	};
++    <authorgroup>
++      <author>
++	<firstname>Andreas</firstname>
++	<surname>Herrman</surname>
++	<affiliation>
++	  <address><email>aherrman at de.ibm.com</email></address>
++	</affiliation>
++      </author>
++      <author>
++	<firstname>Stefan</firstname>
++	<surname>V&ouml;lkel</surname>
++	<affiliation>
++	  <address><email>Stefan.Voelkel at millenux.com</email></address>
++	</affiliation>
++      </author>
++    </authorgroup>
 +
-+typedef struct _dcss_device_t {
-+	struct list_head lh;
-+	devfs_handle_t     devfs_entry;
-+	unsigned int  use_count;
-+	unsigned long start;
-+	unsigned long end;
-+	int               segment_type;
-+	unsigned char save_pending;
-+	unsigned char is_shared;
-+#ifdef CONFIG_PROC_FS
-+	struct proc_dir_entry *dcssblk_subdir_entry;
-+	struct proc_dir_entry *dcssblk_save_entry;
-+	struct proc_dir_entry *dcssblk_shared_entry;
-+#endif
-+	char name [9];
-+	kdev_t             kdev;
-+} dcss_device_t;
++    <copyright>
++      <year>2003</year>
++      <holder>IBM Corp.</holder>
++    </copyright>
 +
-+typedef struct _dcssblk_proc_private_t {
-+	char name[8];
-+	int bytes_written;
-+} dcssblk_proc_private_t;
++    <legalnotice>
++      <para>
++	The Kernel parts of ZFCP HBA API are released under
++	the GNU	General Public License (GPL). ZFCP HBA API Library is released
++	under the ...
++      </para>
++    </legalnotice>
 +
-+static int dcssblk_major;
-+static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices);
-+static rwlock_t dcssblk_devices_lock = RW_LOCK_UNLOCKED;
++    <abstract>
++      <para>
++	This document describes Intra-Kernel and Kernel-User-Space interfaces
++	of ZFCP HBA API.
++      </para>
++      <para>
++	ZFCP HBA API is an implementation of
++	<citation><xref linkend="bib.fchba"></citation>
++	for the ZFCP device driver for Linux on zSeries.
++      </para>
++      <para>
++	This is the first version of the document. It is written in DocBook 4.1.
++	Please let me know if you find any markup and other errors.
++      </para>
++    </abstract>
++ </bookinfo>
 +
++ <toc></toc>
 +
-+MODULE_LICENSE("GPL");
++  <!-- introduction -->
++  <chapter id="cha.introduction">
++    <title>Introduction</title>
++    <para>
++      ZFCP HBA API is an implementation of
++      <citation><xref linkend="bib.fchba"></citation>
++      for the ZFCP device driver. The ZFCP device driver is a FCP device driver
++      for Linux on zSeries. This documentation describes the ZFCP HBA API for
++      Linux Kernel 2.4. ZFCP HBA API consists of the following parts.
++    </para>
++    <itemizedlist>
++      <listitem>
++	<para>
++	  ZFCP HBA API Library - a shared library which provides the
++	  API defined in <citation><xref linkend="bib.fchba"></citation>.
++	</para>
++      </listitem>
++      <listitem>
++	<para>
++	  The kernel module <filename>zfcp_hbaapi</filename> which
++	  provides a misc device with IO controls for communication
++	  between kernel and user space. The module registers callback
++	  functions in the ZFCP device driver and is able to use
++	  functionality provided by the ZFCP device driver. This
++	  module is the connection between ZFCP HBA API Library and
++	  the ZFCP devcie driver.
++	</para>
++      </listitem>
++      <listitem>
++	<para>
++	  Finally the ZFCP device driver contains hooks at which
++	  callback functions of the <filename>zfcp_hbaapi</filename>
++	  kernel module are invoked.  Furthermore the device driver
++	  provides functionality which can be used by the
++	  <filename>zfcp_hbaapi</filename> kernel module.
++	</para>
++      </listitem>
++    </itemizedlist>
++    <para>
++      This documentation describes the kernel parts of ZFCP HBA API.
++      Separate documentation for ZFCP HBA API Library exists.
++    </para>
++  </chapter>
 +
-+/*
-+ * get a minor number. needs to be called with
-+ * write_lock(dcssblk_devices_lock) and the 
-+ * device needs to be enqueued before the lock is
-+ * freed.
-+ */
-+static int dcssblk_assign_free_minor (dcss_device_t* device) {
-+	unsigned int minor,found;
-+	struct list_head* entry;
-+	if (device == NULL)
-+		return -EINVAL;
-+	for (minor=0; minor< (1<<MINORBITS); minor++) {
-+		found = 0;
-+		// test if minor available
-+		list_for_each (entry, &dcssblk_devices)
-+			if (minor == MINOR((list_entry(entry, dcss_device_t, 
-+						       lh)->kdev)))
-+				found++;
-+		if (!found) break; // got unused minor
-+	}
-+	if (found)
-+		return -EBUSY;
-+	device->kdev = MKDEV(dcssblk_major, minor);
-+	return 0;
-+}
-+
-+/*
-+ * get the dcss_device_t from dcssblk_devices 
-+ * with the struct device supplied.
-+ * needs to be called with read_lock(dcssblk_devices_lock)
-+ */
-+static dcss_device_t * dcssblk_get_device_by_minor (unsigned int minor) {
-+	struct list_head* entry;
-+	list_for_each (entry, &dcssblk_devices) 
-+		if (MINOR(list_entry(entry, dcss_device_t, lh)->kdev)==minor) {
-+			return list_entry(entry,dcss_device_t, lh);
-+		}
-+	return NULL;
-+}
-+
-+/*
-+ * get the dcss_device_t from dcssblk_devices 
-+ * with the segment name supplied.
-+ * needs to be called with read_lock(dcssblk_devices_lock)
-+ */
-+static dcss_device_t * dcssblk_get_device_by_name (char* name) {
-+	struct list_head* entry;
-+	list_for_each (entry, &dcssblk_devices) 
-+		if (!strcmp (name, list_entry (entry, dcss_device_t, lh)
-+			     ->name)) {
-+			return list_entry(entry,dcss_device_t, lh);
-+		}
-+	return NULL;
-+}
-+
-+static void dcssblk_do_save (dcss_device_t* device) {
-+	segment_replace (device->name);
-+}
-+
-+/*
-+ * device attribute for switching shared/nonshared
-+ * operation 
-+ */
-+static int dcssblk_shared_store  (struct file *file, 
-+				      const char *buffer,
-+				      unsigned long count, void *data) {
-+	dcss_device_t* device = data;
-+	char* buf;
-+	int rc;
-+	long value;
-+
-+	if (device == NULL) {
-+		rc = -EINVAL;
-+		goto out_nobuf;
-+	}
-+	write_lock(&dcssblk_devices_lock);
-+	if (device->use_count) {
-+		rc = -EBUSY;
-+		write_unlock (&dcssblk_devices_lock);
-+		goto out_nobuf;
-+	}
-+
-+	/*
-+	 * fetch buffer from userland
-+	 */
-+	buf = kmalloc(count,GFP_ATOMIC);
-+	if (buf == NULL) {
-+		rc = -ENOMEM;
-+		write_unlock(&dcssblk_devices_lock);
-+		goto out_nobuf;
-+	}
-+	if (copy_from_user(buf, buffer, count)) {
-+		rc = -EFAULT;
-+		write_unlock(&dcssblk_devices_lock);
-+		goto out;	
-+	}
-+	value = simple_strtoul(buf, &buf, 10);
-+	
-+	if (value) {
-+		// reload segment in shared mode
-+		segment_unload (device->name);
-+		rc = segment_load (device->name, SEGMENT_SHARED_RO, 
-+				   &device->start, &device->end);
-+		if (rc < 0) {
-+			PRINT_WARN ("SEGMENT %s NOT RELOADED RC=%d\n",
-+				    device->name, rc);
-+			goto removeseg;
-+		}
-+		device->segment_type = rc;
-+		device->is_shared = 1;
-+		rc = count;
-+	} else {
-+		// reload segment in exclusive mode
-+		segment_unload (device->name);
-+		rc = segment_load (device->name, SEGMENT_EXCLUSIVE_RW, 
-+				   &device->start, &device->end);
-+		if (rc < 0) {
-+			PRINT_WARN("SEGMENT %s NOT RELOADED RC=%d\n",
-+				   device->name, rc);
-+			goto removeseg;
-+		}
-+		device->segment_type = rc;
-+		device->is_shared = 0;
-+		rc = count;
-+	}
-+	switch (device->segment_type) {
-+	case SEGMENT_SHARED_RO:
-+	case SEGMENT_EXCLUSIVE_RO:
-+		set_device_ro (device->kdev, 1);
-+		break;
-+	case SEGMENT_SHARED_RW:
-+	case SEGMENT_EXCLUSIVE_RW:
-+		set_device_ro (device->kdev, 0);
-+		break;
-+	}
-+	if (value && ((device->segment_type == SEGMENT_EXCLUSIVE_RO) ||
-+		      (device->segment_type == SEGMENT_EXCLUSIVE_RW))) {
-+		PRINT_WARN( 
-+			   "dcssblk: could not get shared copy of segment %s\n",
-+			   device->name);
-+		device->is_shared = 0;
-+		rc = -EPERM;
-+	}
-+	if ((value == 0) && ((device->segment_type == SEGMENT_SHARED_RO) ||
-+			     (device->segment_type == SEGMENT_SHARED_RW))) {
-+		PRINT_WARN(
-+			   "dcssblk: could not get exclusive copy of segment %s\n",
-+			   device->name);
-+		device->is_shared = 1;
-+		rc = -EPERM;
-+	}
-+	write_unlock(&dcssblk_devices_lock);
-+	goto out;
-+
-+ removeseg:
-+	PRINT_WARN (
-+		    "dcssblk: could not reload segment %s, removing it!\n",
-+		    device->name);
-+	list_del(&device->lh);
-+	write_unlock(&dcssblk_devices_lock);
-+#ifdef CONFIG_PROC_FS
-+	remove_proc_entry("save", device->dcssblk_subdir_entry);
-+	remove_proc_entry("shared", device->dcssblk_subdir_entry);
-+	remove_proc_entry(device->name, dcssblk_proc_root_entry);
-+#endif
-+	devfs_unregister(device->devfs_entry );
-+
-+	kfree (device);	
-+	MOD_DEC_USE_COUNT; // permanent
-+ out:
-+	kfree (buf);
-+ out_nobuf:
-+	return rc;
++  <!-- documentation about zfcp_hbaapi module -->
++  <chapter id="cha.zfcphbaapi">
++    <title>Kernel Module <filename>zfcp_hbaapi</filename></title>
++    <section id="sec.zfcphbaapioverview">
++      <title>Overview</title>
++      <para>
++	The module <filename>zfcp_hbaapi</filename> is the interface
++	between the ZFCP HBA API Library and the ZFCP device
++	driver. It provides a misc device which is used for
++	communication between kernel and user space.  The module
++	registers callback functions in the ZFCP device driver which
++	are invoked when certain events occur. Furthermore it calls
++	functions provided by the ZFCP device driver to collect data
++	for the ZFCP HBA API Library.
++      </para>
++    </section>
++    <section>
++      <title>Device File</title>
++      <para>
++	The module <filename>zfcp_hbaapi</filename> provides a misc
++	device. The corresponding device node should be named
++	<filename>/dev/zfcp_hbaapi</filename> - ZFCP HBA API Library
++	expects this name for the device file.  When the module is
++	loaded the device node can be generated using the commands:
++      </para>
++      <screen>
++#>minor=`cat /proc/misc | awk "\\$2==\\"zfcp_hbaapi\\" {print \\$1}"`
++#>mknod /dev/zfcp_hbaapi c 10 $minor
++      </screen>
++      <section>
++	<title>Module Paramters</title>
++	<para>The following parameters can be set for the module.</para>
++	<table>
++	  <title>Module Parameters</title>
++	  <tgroup cols='3' colsep='1' rowsep='1'>
++	    <thead>
++	      <row>
++		<entry>Parameter</entry>
++		<entry>Description</entry>
++		<entry>Default Value</entry>
++	      </row>
++	    </thead>
++	    <tbody>
++	      <row>
++		<entry><para><parameter>maxshared</parameter></para></entry>
++		<entry><para>Maximum number of events in the shared event queue.
++		  </para></entry>
++		<entry><para><parameter>20</parameter></para></entry>
++	      </row>
++	      <row>
++		<entry><para><parameter>maxpolled</parameter></para></entry>
++		<entry><para>Maximum number of events in the polled event queue.
++		  </para></entry>
++		<entry><para><parameter>20</parameter></para></entry>
++	      </row>
++	      <row>
++		<entry><para><parameter>minor</parameter></para></entry>
++		<entry><para>
++		    Minor number for the misc device to be registered.
++		  </para></entry>
++		<entry><para>
++		    <symbol>MISC_DYNAMIC_MINOR</symbol>
++		  </para></entry>
++	      </row>
++	    </tbody>
++	  </tgroup>
++	</table>
++      </section>
++      <section>
++	<title>File Operations</title>
++	<para>
++	  The module <filename>zfcp_hbaapi</filename> defines the
++	  methods <function>open</function>,
++	  <function>read</function>, <function>release</function>, and
++	  <function>ioctl</function> for the misc device.
++	</para>
++	<section>
++	  <title>Reference</title>
++	  <para></para>
++!Fdrivers/s390/scsi/zh_main.c zh_ioctl
++!Fdrivers/s390/scsi/zh_main.c zh_read
++!Fdrivers/s390/scsi/zh_main.c zh_release
++!Fdrivers/s390/scsi/zh_main.c zh_open
++	</section>
++      </section>
++      <section>
++	<title>IO Controls</title>
++	<para>
++	  The next table gives an overview about IO controls of the misc
++	  device, the name of corresponding internal helper functions and
++	  argument types (if any).
++	</para>
++	<table frame='all'><title>IO Controls</title>
++	  <tgroup cols='2' colsep='1' rowsep='1'>
++	    <thead>
++	      <row>
++		<entry><para>Name</para></entry>
++		<entry><para>Helper Function, Argument Type</para></entry>
++	      </row>
++	    </thead>
++	    <tbody>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_GET_ADAPTERATTRIBUTES</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_get_adapterattributes</function>,
++		    <type>struct zh_get_adapterattributes</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_GET_PORTATTRIBUTES</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_get_portattributes</function>,
++		    <type>struct zh_get_portattributes</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_GET_PORTSTATISTICS</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_get_portstatistics</function>,
++		    <type>struct zh_get_portstatistics</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_GET_DPORTATTRIBUTES</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_get_dportattributes</function>,
++		    <type>struct zh_get_portattributes</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_GET_RNID</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_get_rnid</function>,
++		    <type>struct zh_get_rnid</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_SEND_RNID</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_send_rnid</function>,
++		    <type>struct zh_send_rnid</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_SEND_CT</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_send_ct</function>,
++		    <type>struct zh_send_ct</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_SCSI_INQUIRY</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_scsi_inquiry</function>,
++		    <type>struct zh_scsi_inquiry</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_SCSI_READ_CAPACITY</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_scsi_read_capacity</function>,
++		    <type>struct zh_scsi_read_capacity</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_SCSI_REPORT_LUNS</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_scsi_report_luns</function>,
++		    <type>struct zh_scsi_report_luns</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_GET_EVENT_BUFFER</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_get_event_buffer</function>,
++		    <type>struct zh_get_event_buffer</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_GET_CONFIG</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_get_config</function>,
++		    <type>struct zh_get_config</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_CLEAR_CONFIG</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_clear_config</function>,
++		    <type>struct zh_clear_config</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_EVENT_START</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_event_start</function>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_EVENT_STOP</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_event_stop</function>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_EVENT</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_event</function>,
++		    <type>struct zh_event</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		  <symbol>ZH_IOC_EVENT_INSERT</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_event_insert</function>
++		  </para></entry>
++	      </row>
++	    </tbody>
++	  </tgroup>
++	</table>
++	<para>
++	  If <filename>zfcp_hbaapi</filename> is compiled for 64 bit
++	  architecture, two additional IO controls exist. They are
++	  used for 32 bit IO control conversion:
++	</para>
++	<table frame='all'>
++	  <title>IO Controls (on 64 bit architecture only)</title>
++	  <tgroup cols='2' colsep='1' rowsep='1'>
++	    <thead>
++	      <row>
++		<entry><para>Name</para></entry>
++		<entry><para>Helper Function, Argument Type</para></entry>
++	      </row>
++	    </thead>
++	    <tbody>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_SEND_CT32</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_send_ct32</function>,
++		    <type>struct zh_send_ct32</type>
++		  </para></entry>
++	      </row>
++	      <row>
++		<entry><para>
++		    <symbol>ZH_IOC_SCSI_REPORT_LUNS32</symbol>
++		  </para></entry>
++		<entry><para>
++		    <function>zh_ioc_scsi_report_luns</function>,
++		    <type>struct zh_scsi_report_luns32</type>
++		  </para></entry>
++	      </row>
++	    </tbody>
++	  </tgroup>
++	</table>
++      </section>
++      <section>
++	<title>Reference</title>
++	<para></para>
++!Fdrivers/s390/scsi/zh.h zh_get_adapterattributes
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_adapterattributes
++!Fdrivers/s390/scsi/zh.h zh_get_portattributes
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_portattributes
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_dportattributes
++!Fdrivers/s390/scsi/zh.h zh_get_portstatistics
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_portstatistics
++!Fdrivers/s390/scsi/zh.h zh_get_rnid
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_rnid
++!Fdrivers/s390/scsi/zh.h zh_send_rnid
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_send_rnid
++!Fdrivers/s390/scsi/zh.h zh_send_ct
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_send_ct
++!Fdrivers/s390/scsi/zh.h zh_scsi_inquiry
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_scsi_inquiry
++!Fdrivers/s390/scsi/zh.h zh_scsi_read_capacity
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_scsi_read_capacity
++!Fdrivers/s390/scsi/zh.h zh_scsi_report_luns
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_scsi_report_luns
++!Fdrivers/s390/scsi/zh.h zh_get_event_buffer
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_event_buffer
++!Fdrivers/s390/scsi/zh.h zh_get_config
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_get_config
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_clear_config
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_event_start
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_event_stop
++!Fdrivers/s390/scsi/zh.h zh_event
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_event
++!Fdrivers/s390/scsi/zh_main.c zh_ioc_event_insert
++      </section>
++    </section>
++    <section>
++      <title>Callback functions</title>
++      <para>
++	For event notification <filename>zfcp_hbaapi</filename>
++	registers a bunch of callback functions at the ZFCP device
++	driver.  In zfcp certain hooks exist where those callbacks are
++	invoked.
++      </para>
++      <section>
++	<title>Reference</title>
++	<para></para>
++!Fdrivers/s390/scsi/zh_main.c zh_cb_adapter_add
++!Fdrivers/s390/scsi/zh_main.c zh_cb_port_add
++!Fdrivers/s390/scsi/zh_main.c zh_cb_unit_add
++!Fdrivers/s390/scsi/zh_main.c zh_cb_incomming_els
++!Fdrivers/s390/scsi/zh_main.c zh_cb_link_down
++!Fdrivers/s390/scsi/zh_main.c zh_cb_link_up
++      </section>
++    </section>
++  </chapter>
 +
-+}
++  <!-- changed/new functions and structures in zfcp -->
++  <!-- documentation about zfcp module -->
++  <chapter id="cha.zfcp">
++    <title>Kernel Module <filename>zfcp</filename></title>
++    <para>
++      The module <filename>zfcp</filename> provides (new) interfaces for
++      ZFCP HBA API. Furthermore hooks are integrated at which callback functions
++      for event notification are invoked.
++    </para>
 +
-+/*
-+ * device attribute for showing status of "shared / non-shared"
-+ */
-+static int dcssblk_shared_status(char *buffer, char **start, off_t offset,
-+				int count, int *eof, void *data) {
-+	dcss_device_t* device = data;
-+	
-+	*eof = 1;
-+	return sprintf(buffer, device->is_shared ? "1\n" : "0\n");
-+}
-+
-+/*
-+ * device attribute for save operation on current copy
-+ * of the segment. If the segment is busy, saving will
-+ * become pending until it gets released which can be
-+ * undone by storing a non-true value to this entry
-+ */
-+static int dcssblk_save_store  (struct file *file, 
-+				    const char *buffer,
-+				    unsigned long count, void *data) {
-+	dcss_device_t* device = data;
-+	char* buf;
-+	int rc,value;
++    <section>
++      <title>Intra-Kernel Interface</title>
++      <para>The ZFCP device driver exports the following functions</para>
++      <glosslist>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_callbacks_register</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>Register callbacks for event handling. Called from
++	      <function>zh_init</function> - the init function of module
++	      <filename>zfcp_hbaapi</filename>.</para>
++	  </glossdef>
++	</glossentry>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_callbacks_unregister</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>Unregister callbacks for event handling. Called from
++	      <function>zh_exit</function> - the exit function of module
++	      <filename>zfcp_hbaapi</filename>.</para>
++	  </glossdef>
++	</glossentry>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_get_config</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>For each adapter, port and unit configured in module
++	      <filename>zfcp</filename> the corresponding callback
++	      function is called.</para>
++	  </glossdef>
++	</glossentry>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_get_adapter_attributes</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>Collect attributes of an adapter.</para>
++	  </glossdef>
++	</glossentry>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_get_port_attributes</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>Collect attributes of an adapter port. Calls FSF
++	      command <command>ExchangePortData</command>.</para>
++	  </glossdef>
++	</glossentry>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_port_statistics</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>Collect statistics of an adapter port. Calls FSF
++	      command <command>ExchangePortData</command>.</para>
++	  </glossdef>
++	</glossentry>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_get_dport_attributes</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>Collect attributes of a discovered port. Sends a
++	      FC-GS-2 request <command>GA_NXT</command> to the Name
++	      Server Directory Service.</Para>
++	  </glossdef>
++	</glossentry>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_assert_fclun_zero</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>Checks whether an unit with FC LUN
++	      <parameter>0x0</parameter> is configured in
++	      <filename>zfcp</filename> for a certain port. If not it
++	      creates a <filename>zfcp</filename> unit structure for FC
++	      LUN <parameter>0x0</parameter> for this port.</para>
++	  </glossdef>
++	</glossentry>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_send_scsi</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>Send a SCSI command to a FC LUN.</para>
++	  </glossdef>
++	</glossentry>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_send_els</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>Send ELS commands (according to FC-FS).</para>
++	  </glossdef>
++	</glossentry>
++	<glossentry>
++	  <glossterm>
++	    <function>zfcp_zh_send_ct</function>
++	  </glossterm>
++	  <glossdef>
++	    <para>Send Generic Service commands according to FC-GS-4).
++	    </para>
++	  </glossdef>
++	</glossentry>
++      </glosslist>
++      <note>
++	<para>In <function>zfcp_zh_send_ct</function> currently only
++	  requests to the Name Server Directory Service are
++	  supported.</para>
++      </note>
++      <section>
++	<title>Reference</title>
++	<para></para>
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_callbacks_register
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_callbacks_unregister
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_get_config
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_get_adapter_attributes
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_get_port_attributes
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_get_port_statistics
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_get_dport_attributes
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_send_ct
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_send_els
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_send_scsi
++!Fdrivers/s390/scsi/zfcp_zh.c zfcp_zh_assert_fclun_zero
++      </section>
++    </section>
++    <section>
++      <title>Callbacks for Event Handling</title>
++      <para>
++	To enable event delivery as required by <citation><xref
++	linkend="bib.fchba"></citation>, some callback functions of
++	module <filename>zfcp_hbaapi</filename> must be called from
++	module <filename>zfcp</filename>.
++      </para>
++      <para>
++	The following table gives an overview of the callbacks into
++	module <filename>zfcp_hbaapi</filename> and their hooks in
++	<filename>zfcp</filename>.
++      </para>
++      <table frame='all'><title>Callbacks</title>
++	<tgroup cols='2' colsep='1' rowsep='1'>
++	  <thead>
++	    <row>
++	      <entry><para>Callback</para></entry>
++	      <entry><para>
++		  Hook in module <filename>zfcp</filename>
++		</para></entry>
++	    </row>
++	  </thead>
++	  <tbody>
++	    <row>
++	      <entry><para>
++		  <function>adapter_add</function>
++		</para></entry>
++	      <entry><para>
++		  <function>zfcp_fsf_exchange_config_data_handler</function>
++		</para></entry>
++	    </row>
++	    <row>
++	      <entry><para>
++		  <function>port_add</function>
++		</para></entry>
++	      <entry><para>
++		  <function>zfcp_port_enqueue</function>
++		</para></entry>
++	    </row>
++	    <row>
++	      <entry><para>
++		  <function>unit_add</function>
++		</para></entry>
++	      <entry><para>
++		  <function>zfcp_unit_enqueue</function>
++		</para></entry>
++	    </row>
++	    <row>
++	      <entry><para>
++		  <function>incoming_els</function>
++		</para></entry>
++	      <entry><para>
++		  <function>zfcp_fsf_incoming_els</function>
++		</para></entry>
++	    </row>
++	    <row>
++	      <entry><para>
++		  <function>link_down</function>
++		</para></entry>
++	      <entry><para>
++		  <function>zfcp_status_read_handler</function>,
++		  <function>zfcp_fsf_protstatus_eval</function>
++		</para></entry>
++	    </row>
++	    <row>
++	      <entry><para>
++		  <function>link_up</function>
++		</para></entry>
++	      <entry><para>
++		  <function>zfcp_status_read_handler</function>
++		</para></entry>
++	    </row>
++	  </tbody>
++	</tgroup>
++      </table>
++    </section>
++  </chapter>
 +
-+	if (device == NULL) {
-+		rc = -EINVAL;
-+		goto out_nobuf;
-+	}
-+	read_lock(&dcssblk_devices_lock);
-+	/*
-+	 * fetch buffer from userland
-+	 */
-+	buf = kmalloc(count,GFP_ATOMIC);
-+	if (buf == NULL) {
-+		rc = -ENOMEM;
-+		write_unlock(&dcssblk_devices_lock);
-+		goto out_nobuf;
-+	}
-+	if (copy_from_user(buf, buffer, count)) {
-+		rc = -EFAULT;
-+		write_unlock(&dcssblk_devices_lock);
-+		goto out;	
-+	}
-+	value = simple_strtoul(buf, &buf, 10);
-+	if (value) {
-+		if (device->use_count == 0) {
-+			/* device is idle => we save immediately */
-+			PRINT_WARN ("saving segment %s\n", device->name);
-+			dcssblk_do_save (device);
-+		}  else {
-+			/* device is busy => we save it when it becomes
-+			   idle in dcssblk_release */
-+			PRINT_WARN ("segment %s is currently busy\n",
-+				    device->name);
-+			PRINT_WARN ("segment %s will be saved when it becomes idle\n",
-+				    device->name);
-+			device->save_pending = 1;
-+		}
-+	} else {
-+		if (device->save_pending) {
-+			/* device is busy & the user wants to undo his save
-+			   request */
-+			device->save_pending = 0;
-+			PRINT_WARN ("deactivating pending save for segment %s\n",
-+				    device->name);
-+		}
-+	}
-+	read_unlock(&dcssblk_devices_lock);
-+	rc = count;
-+ out:
-+	kfree (buf);
-+ out_nobuf:
-+	return rc;
-+}
++  <!-- Bibliography -->
++  <bibliography>
++    <biblioentry id="bib.fchba" xreflabel="FC-HBA">
++      <title>
++	Working Draft. Information technology - Fibre Channel HBA API (FC-HBA)
++      </title>
++      <orgname>The T11 Technical Committee</orgname>
++      <!-- 	<pubdate> -->
++      <!-- 	  Revision 11, 2003-10-29, -->
++      <!-- 	  <ulink url="http://www.t11.org/"></ulink> -->
++      <!-- 	</pubdate> -->
++    </biblioentry>
++  </bibliography>
 +
-+/*
-+ * device attribute for showing status of "save pending"
-+ */
-+static int dcssblk_save_status(char *buffer, char **start, off_t offset,
-+				int count, int *eof, void *data) {
-+	dcss_device_t* device = data;
-+	
-+	*eof = 1;
-+	return sprintf(buffer, device->save_pending ? "1\n" : "0\n");
-+}
++</book>
++<!-- Keep this comment at the end of the file
++Local variables:
++mode: sgml
++sgml-always-quote-attributes:t
++sgml-auto-insert-required-elements:t
++sgml-balanced-tag-edit:t
++sgml-exposed-tags:nil
++sgml-general-insert-case:lower
++sgml-indent-data:t
++sgml-indent-step:2
++sgml-local-catalogs:nil
++sgml-local-ecat-files:nil
++sgml-minimize-attributes:nil
++sgml-namecase-general:t
++sgml-omittag:t
++sgml-shorttag:t
++sgml-tag-region-if-active:t
++End:
++-->
+diff -urN kernel-source-2.4.27.orig/Documentation/ioctl-number.txt Documentation/ioctl-number.txt
+--- kernel-source-2.4.27.orig/Documentation/ioctl-number.txt	2004-08-07 17:26:04.000000000 -0600
++++ Documentation/ioctl-number.txt	2006-02-12 12:47:23.000000000 -0700
+@@ -107,6 +107,7 @@
+ 'W'	00-1F	linux/wanrouter.h	conflict!
+ 'X'	all	linux/xfs_fs.h
+ 'Y'	all	linux/cyclades.h
++'Z'	all	linux/s390net.h		S390 networking
+ 'a'	all				ATM on linux
+ 					<http://lrcwww.epfl.ch/linux-atm/magic.html>
+ 'b'	00-FF				bit3 vme host bridge
+@@ -183,6 +184,8 @@
+ 0xB1	00-1F	PPPoX			<mailto:mostrows at styx.uwaterloo.ca>
+ 0xCB	00-1F	CBM serial IEC bus	in development:
+ 					<mailto:michael.klein at puffin.lb.shuttle.de>
++0xDD	00-3F	ZFCP device driver	see drivers/s390/scsi/
++					<mailto:aherrman at de.ibm.com>
+ 0xF3    00-3F   linux/sisfb.h 		SiS framebuffer device driver
+ 					<mailto:thomas at winischhofer.net>
+ 0xFE	00-9F	Logical Volume Manager	<mailto:linux-lvm at sistina.com>
+diff -urN kernel-source-2.4.27.orig/Documentation/s390/TAPE Documentation/s390/TAPE
+--- kernel-source-2.4.27.orig/Documentation/s390/TAPE	2001-07-25 15:12:01.000000000 -0600
++++ Documentation/s390/TAPE	2006-02-12 12:47:23.000000000 -0700
+@@ -1,122 +0,0 @@
+-Channel attached Tape device driver 
+-
+------------------------------WARNING-----------------------------------------
+-This driver is considered to be EXPERIMENTAL. Do NOT use it in 
+-production environments. Feel free to test it and report problems back to us. 
+------------------------------------------------------------------------------
+-
+-The LINUX for zSeries tape device driver manages channel attached tape drives 
+-which are compatible to IBM 3480 or IBM 3490 magnetic tape subsystems. This 
+-includes various models of these devices (for example the 3490E). 
+-
+-
+-Tape driver features 
+-
+-The device driver supports a maximum of 128 tape devices. 
+-No official LINUX device major number is assigned to the zSeries tape device 
+-driver. It allocates major numbers dynamically and reports them on system 
+-startup. 
+-Typically it will get major number 254 for both the character device front-end 
+-and the block device front-end. 
+-
+-The tape device driver needs no kernel parameters. All supported devices 
+-present are detected on driver initialization at system startup or module load.
+-The devices detected are ordered by their subchannel numbers. The device with 
+-the lowest subchannel number becomes device 0, the next one will be device 1 
+-and so on. 
+-
+-
+-Tape character device front-end 
+-
+-The usual way to read or write to the tape device is through the character 
+-device front-end. The zSeries tape device driver provides two character devices
+-for each physical device -- the first of these will rewind automatically when 
+-it is closed, the second will not rewind automatically. 
+-
+-The character device nodes are named /dev/rtibm0 (rewinding) and /dev/ntibm0 
+-(non-rewinding) for the first device, /dev/rtibm1 and /dev/ntibm1 for the 
+-second, and so on. 
+-
+-The character device front-end can be used as any other LINUX tape device. You 
+-can write to it and read from it using LINUX facilities such as GNU tar. The 
+-tool mt can be used to perform control operations, such as rewinding the tape 
+-or skipping a file. 
+-
+-Most LINUX tape software should work with either tape character device. 
+-
+-
+-Tape block device front-end 
+-
+-The tape device may also be accessed as a block device in read-only mode. 
+-This could be used for software installation in the same way as it is used with 
+-other operation systems on the zSeries platform (and most LINUX 
+-distributions are shipped on compact disk using ISO9660 filesystems). 
+-
+-One block device node is provided for each physical device. These are named 
+-/dev/btibm0 for the first device, /dev/btibm1 for the second and so on. 
+-You should only use the ISO9660 filesystem on LINUX for zSeries tapes because 
+-the physical tape devices cannot perform fast seeks and the ISO9660 system is 
+-optimized for this situation. 
+-
+-
+-Tape block device example 
+-
+-In this example a tape with an ISO9660 filesystem is created using the first 
+-tape device. ISO9660 filesystem support must be built into your system kernel
+-for this. 
+-The mt command is used to issue tape commands and the mkisofs command to 
+-create an ISO9660 filesystem: 
+-
+-- create a LINUX directory (somedir) with the contents of the filesystem 
+-     mkdir somedir
+-     cp contents somedir 
+-
+-- insert a tape 
+-
+-- ensure the tape is at the beginning 
+-     mt -f /dev/ntibm0 rewind 
+-
+-- set the blocksize of the character driver. The blocksize 2048 bytes
+-  is commonly used on ISO9660 CD-Roms
+-     mt -f /dev/ntibm0 setblk 2048 
+-
+-- write the filesystem to the character device driver 
+-     mkisofs -o /dev/ntibm0 somedir 
+-
+-- rewind the tape again 
+-     mt -f /dev/ntibm0 rewind 
+-
+-- Now you can mount your new filesystem as a block device: 
+-     mount -t iso9660 -o ro,block=2048 /dev/btibm0 /mnt 
+-
+-TODO List 
+-
+-   - Driver has to be stabelized still
+-
+-BUGS 
+-
+-This driver is considered BETA, which means some weaknesses may still
+-be in it.
+-If an error occurs which cannot be handled by the code you will get a 
+-sense-data dump.In that case please do the following: 
+-
+-1. set the tape driver debug level to maximum: 
+-     echo 6 >/proc/s390dbf/tape/level 
+-
+-2. re-perform the actions which produced the bug. (Hopefully the bug will 
+-   reappear.) 
+-
+-3. get a snapshot from the debug-feature: 
+-     cat /proc/s390dbf/tape/hex_ascii >somefile 
+-
+-4. Now put the snapshot together with a detailed description of the situation 
+-   that led to the bug: 
+- - Which tool did you use? 
+- - Which hardware do you have? 
+- - Was your tape unit online? 
+- - Is it a shared tape unit? 
+-
+-5. Send an email with your bug report to: 
+-     mailto:Linux390 at de.ibm.com 
+-
+-
+diff -urN kernel-source-2.4.27.orig/Documentation/s390/xip2.8 Documentation/s390/xip2.8
+--- kernel-source-2.4.27.orig/Documentation/s390/xip2.8	1969-12-31 17:00:00.000000000 -0700
++++ Documentation/s390/xip2.8	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,67 @@
++.TH XIP2 8 "8 December 2003" "Linux 2.4" "Linux Programmer's Manual"
++.SH NAME
++xip2 \- mount a xip2 file system
++.SH "DESCRIPTION"
++This man page describes mount options specific to the xip2 filesystem. Have a 
++look at the man page of 
++.BR mount (2)
++for information about what mount options are and how mount is used overall.
++.SH "Mount options inherited from ext2"
++The `xip2' file system is derived from the second extended filesystem (ext2).
++It supports the following mount options which are inherited from ext2:
++.TP
++.BR bsddf " / " minixdf
++Set the behaviour for the
++.I statfs
++system call. The
++.B minixdf
++behaviour is to return in the
++.I f_blocks
++field the total number of blocks of the file system, while the
++.B bsddf
++behaviour (which is the default) is to subtract the overhead blocks
++used by the ext2 file system and not available for file storage.
++.TP
++.BR errors=continue " / " errors=remount-ro " / " errors=panic
++Define the behaviour when an error is encountered.
++(Either ignore errors and just mark the file system erroneous and continue,
++or remount the file system read-only, or panic and halt the system.) 
++Note that  mounting read-only does not change anything because the xip2 file 
++system is read-only only anyway.
++.TP
++.BR grpid " or " bsdgroups " / " nogrpid " or " sysvgroups
++These options are accepted but ignored.
++.TP
++\fBresgid=\fP\fIn\fP and \fBresuid=\fP\fIn\fP
++These options are accepted but ignored.
++.TP
++.BI sb= n
++Instead of block 1, use block
++.I n
++as superblock. This could be useful when the filesystem has been damaged.
++The block number here uses 1k units. Thus, if you want to use logical
++block 32768 on a filesystem with 4k blocks, use "sb=131072". Note that the 
++xip2 file system always works with 4k blocks.
++.TP
++.BR grpquota " / " noquota " / " quota " / " usrquota
++These options are accepted but ignored.
 +
-+/*
-+ * device attribute for adding devices
-+ */
-+static int dcssblk_add_store (struct file *file, 
-+				  const char *buffer,
-+				  unsigned long count, void *data)
-+{
-+	int rc=0, i;
-+	char* buf;
-+	dcss_device_t* dcssdev;
-+	struct list_head* entry;
-+
-+	MOD_INC_USE_COUNT; // released at end of func
-+
-+	/*
-+	 * fetch buffer from userland
-+	 */
-+	buf = kmalloc(count+1,GFP_KERNEL);
-+	if (buf == NULL) {
-+		rc = -ENOMEM;
-+		goto out_nobuf;
-+	}
-+	if (copy_from_user(buf, buffer, count)) {
-+		rc = -EFAULT;
-+		goto out;
-+	}
-+	
-+
-+	/*
-+	 * check input parameters
-+	 */
-+	for (i=0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i<count); i++) {
-+		buf[i] = toupper(buf[i]);
-+	}
-+	*(buf+i) = '\0';
-+	if ((i==0) || (i>8)) {
-+		rc = -ENAMETOOLONG;
-+		goto out;
-+	}
-+	/*
-+	 * already loaded?
-+	 */
-+	read_lock(&dcssblk_devices_lock);
-+	list_for_each (entry, &dcssblk_devices) {
-+		if (!strcmp(buf, list_entry(entry, dcss_device_t, lh)->name)) {
-+			read_unlock(&dcssblk_devices_lock);
-+			PRINT_WARN ("SEGMENT %s ALREADY LOADED!\n", buf);
-+			rc = -EEXIST;
-+			goto out;
-+		}
-+	}
-+	read_unlock(&dcssblk_devices_lock);
-+	/*
-+	 * try to get the dcss
-+	 */
-+	dcssdev = kmalloc (sizeof(dcss_device_t),GFP_KERNEL);
-+	if (dcssdev == NULL) {
-+		rc = -ENOMEM;
-+		goto out;
-+	}
-+	memset (dcssdev, 0, sizeof(dcss_device_t));
-+	memcpy (dcssdev->name, buf, i);
-+	dcssdev->name[i+1] = '\0';
-+	INIT_LIST_HEAD(&dcssdev->lh);
-+	/*
-+	 * load the segment
-+	 */
-+	rc = segment_load (dcssdev->name, SEGMENT_SHARED_RO, 
-+			   &dcssdev->start, &dcssdev->end);
-+	if (rc < 0) {
-+		PRINT_WARN ("SEGMENT %s NOT LOADED RC=%d\n",
-+			    dcssdev->name, rc);
-+		goto free_dcssdev;
-+	}
-+	if (rc == SEGMENT_EXCLUSIVE_RO || rc == SEGMENT_EXCLUSIVE_RW)
-+		dcssdev->is_shared = 0;
-+	else
-+		dcssdev->is_shared = 1;
-+	PRINT_WARN ("LOADED SEGMENT %s from %08lx to %08lx\n",dcssdev->name,dcssdev->start,dcssdev->end);
-+	dcssdev->segment_type = rc;
-+	dcssdev->save_pending = 0;
-+	/*
-+	 * get minor
-+	 */
-+	write_lock(&dcssblk_devices_lock);
-+	rc = dcssblk_assign_free_minor(dcssdev);
-+	if (rc) {
-+		write_unlock (&dcssblk_devices_lock);
-+		goto unload_seg;
-+	}
-+	/*
-+	 * create devfs device node
-+	 */
-+	dcssdev->devfs_entry = devfs_register (dcssblk_devfs_dir,dcssdev->name,
-+					       DEVFS_FL_DEFAULT,
-+					       MAJOR(dcssdev->kdev),
-+					       MINOR(dcssdev->kdev),
-+					       S_IFBLK | S_IRUSR | S_IWUSR,
-+					       &dcssblk_devops,NULL);
-+	/*
-+	 * create procfs subdirectory
-+	 */
-+#ifdef CONFIG_PROC_FS
-+	dcssdev->dcssblk_subdir_entry =  proc_mkdir (dcssdev->name, 
-+						     dcssblk_proc_root_entry);
-+	dcssdev->dcssblk_shared_entry = 
-+		create_proc_entry ("shared",
-+				   S_IRUSR|S_IWUSR,
-+				   dcssdev->dcssblk_subdir_entry);
-+	dcssdev->dcssblk_save_entry =
-+		create_proc_entry ("save",
-+				   S_IRUSR|S_IWUSR,
-+				   dcssdev->dcssblk_subdir_entry);
-+	dcssdev->dcssblk_shared_entry->write_proc = 
-+		dcssblk_shared_store;
-+	dcssdev->dcssblk_shared_entry->read_proc = 
-+		dcssblk_shared_status;
-+	dcssdev->dcssblk_save_entry->write_proc =
-+		dcssblk_save_store;
-+	dcssdev->dcssblk_save_entry->read_proc =
-+		dcssblk_save_status;
-+	dcssdev->dcssblk_shared_entry->data =
-+		dcssdev;
-+	dcssdev->dcssblk_save_entry->data =
-+		dcssdev;
-+#endif
-+
-+	/*
-+	 * enqueue
-+	 */
-+	list_add_tail (&dcssdev->lh, &dcssblk_devices);
-+	write_unlock(&dcssblk_devices_lock);
-+	switch (dcssdev->segment_type) {
-+	case SEGMENT_SHARED_RO:
-+	case SEGMENT_EXCLUSIVE_RO:
-+		set_device_ro (dcssdev->kdev, 1);
-+		break;
-+	case SEGMENT_SHARED_RW:
-+	case SEGMENT_EXCLUSIVE_RW:
-+		set_device_ro (dcssdev->kdev, 0);
-+		break;
-+	}
-+	PRINT_DEBUG ("SEGMENT %s loaded successfully\n",
-+		     dcssdev->name);
-+	rc = count;
-+	MOD_INC_USE_COUNT; // second time -> permanent
-+	goto out;
-+ unload_seg:
-+	segment_unload (dcssdev->name);
-+ free_dcssdev:
-+	kfree (dcssdev);
-+ out:
-+	kfree (buf);
-+ out_nobuf:
-+	MOD_DEC_USE_COUNT;
-+	return rc;
-+}
-+
-+/*
-+ * device attribute for removing devices
-+ */
-+static int dcssblk_remove_store  (struct file *file, 
-+				      const char *buffer,
-+				      unsigned long count, void *data) {
-+	dcss_device_t* device;
-+	char * buf;
-+	int rc=count,i;
-+
-+	MOD_INC_USE_COUNT;
-+	/*
-+	 * fetch buffer from userland
-+	 */
-+	buf = kmalloc(count,GFP_KERNEL);
-+	if (buf == NULL) {
-+		rc = -ENOMEM;
-+		goto out_nobuf;
-+	}
-+	if (copy_from_user(buf, buffer, count)) {
-+		rc = -EFAULT;
-+		goto out;
-+	}
-+	for (i=0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i<count); i++) {
-+			buf[i] = toupper(buf[i]);
-+	}
-+	*(buf+i) = '\0';
-+	write_lock(&dcssblk_devices_lock);
-+	device = dcssblk_get_device_by_name (buf);
-+	if (device == NULL) {
-+		rc = -ENODEV;
-+		write_unlock (&dcssblk_devices_lock);
-+		goto out;
-+	}
-+	if (device->use_count != 0) {
-+		rc = -EBUSY;
-+		write_unlock (&dcssblk_devices_lock);
-+		goto out;
-+	}
-+	list_del(&device->lh);
-+	write_unlock(&dcssblk_devices_lock);
-+	segment_unload (device->name);
-+#ifdef CONFIG_PROC_FS
-+	remove_proc_entry("save", device->dcssblk_subdir_entry);
-+	remove_proc_entry("shared", device->dcssblk_subdir_entry);
-+	remove_proc_entry(device->name, dcssblk_proc_root_entry);
-+#endif
-+	devfs_unregister(device->devfs_entry );
-+	
-+	PRINT_DEBUG ("SEGMENT %s unloaded successfully\n",
-+		     device->name);
-+	kfree (device);	
-+	rc = count;
-+	MOD_DEC_USE_COUNT; // permanent
-+ out:
-+	kfree (buf);
-+ out_nobuf:
-+	MOD_DEC_USE_COUNT;
-+	return rc;
-+}
++.TP
++.BR nouid32
++Disables 32-bit UIDs and GIDs.  This is for interoperability with older
++kernels which only store and expect 16-bit values.
++.SH "Mount options specific for xip2"
++The 'xip2' file system supports only one mount option that is specific for its
++use:
++.TP
++.BI memarea= <name>
++This mount-option is mandatory. It specifies the name of the memory segment to
++be used. In case of running on zSeries z/VM, available memory segment names
++correspond with available z/VM DCSS.
++.SH "SEE ALSO"
++.BR mount (8)
++.SH BUGS
++As of today, and mostly due to the stable code base inherited from the second 
++extended filesystem, no known bugs exist. In case you think you encountered 
++one, please report it to Carsten Otte
++.B <cotte at de.ibm.com>
+diff -urN kernel-source-2.4.27.orig/drivers/block/elevator.c drivers/block/elevator.c
+--- kernel-source-2.4.27.orig/drivers/block/elevator.c	2003-06-13 08:51:32.000000000 -0600
++++ drivers/block/elevator.c	2006-02-12 12:47:23.000000000 -0700
+@@ -219,3 +219,9 @@
+ 	*elevator = type;
+ 	elevator->queue_ID = queue_ID++;
+ }
 +
++EXPORT_SYMBOL(elevator_init);
++EXPORT_SYMBOL(elevator_linus_merge);
++EXPORT_SYMBOL(elevator_linus_merge_req);
++EXPORT_SYMBOL(elevator_noop_merge);
++EXPORT_SYMBOL(elevator_noop_merge_req);
+diff -urN kernel-source-2.4.27.orig/drivers/block/Makefile drivers/block/Makefile
+--- kernel-source-2.4.27.orig/drivers/block/Makefile	2004-08-07 17:26:04.000000000 -0600
++++ drivers/block/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -10,7 +10,7 @@
+ 
+ O_TARGET := block.o
+ 
+-export-objs	:= ll_rw_blk.o blkpg.o loop.o DAC960.o genhd.o acsi.o
++export-objs	:= ll_rw_blk.o blkpg.o elevator.o loop.o DAC960.o genhd.o acsi.o
+ 
+ obj-y	:= ll_rw_blk.o blkpg.o genhd.o elevator.o
+ 
+diff -urN kernel-source-2.4.27.orig/drivers/char/tty_io.c drivers/char/tty_io.c
+--- kernel-source-2.4.27.orig/drivers/char/tty_io.c	2006-02-08 04:06:25.000000000 -0700
++++ drivers/char/tty_io.c	2006-02-12 12:47:23.000000000 -0700
+@@ -144,8 +144,10 @@
+ extern void au1x00_serial_console_init(void);
+ extern int rs_8xx_init(void);
+ extern void mac_scc_console_init(void);
+-extern void hwc_console_init(void);
+-extern void hwc_tty_init(void);
++extern void sclp_console_init(void);
++extern void sclp_tty_init(void);
++extern void sclp_vt220_con_init(void);
++extern void sclp_vt220_tty_init(void);
+ extern void con3215_init(void);
+ extern void tty3215_init(void);
+ extern void tub3270_con_init(void);
+@@ -2520,8 +2522,63 @@
+ #endif /* CONFIG_DEVFS_FS */
+ }
+ 
 +/*
-+ * device attribute for listing devices
++ * Register a tty device described by <driver>, with minor number <minor>,
++ * device name <name> and in the /dev directory given by <dir>.
 + */
-+static int dcssblk_list_store(char *buffer, char **start, off_t offset,
-+				int count, int *eof, void *data)
-+{
-+	unsigned int minor, len;
-+	struct list_head* entry;
-+
-+	len = 0;
-+	read_lock(&dcssblk_devices_lock);
-+	for (minor=0; minor< (1<<MINORBITS); minor++) {
-+		// test if minor available
-+		list_for_each (entry, &dcssblk_devices)
-+			if (minor == MINOR((list_entry(entry, dcss_device_t, 
-+						       lh)->kdev))) {
-+				len += sprintf(buffer + len, "%i\t%s\n", minor,
-+					list_entry(entry, dcss_device_t,
-+							lh)->name);
-+			}
-+	}
-+	read_unlock(&dcssblk_devices_lock);
-+	*eof = 1;
-+	return len;
-+}
-+
-+static int dcssblk_open (struct inode *inode, struct file *filp)
-+{
-+	dcss_device_t *dcssdev;
-+	int rc;
-+	write_lock(&dcssblk_devices_lock);
-+	if ((dcssdev=dcssblk_get_device_by_minor(MINOR(inode->i_rdev)))
-+	    == NULL) {		
-+		rc=-ENODEV;
-+		goto out_unlock;
-+	}
-+	dcssdev->use_count ++;
-+	rc = 0;
-+ out_unlock:
-+	write_unlock(&dcssblk_devices_lock);
-+      	return rc;
-+}
-+
-+static int dcssblk_release (struct inode *inode, struct file *filp)
++void tty_register_devfs_name (struct tty_driver *driver, unsigned int flags,
++			      unsigned minor, devfs_handle_t dir,
++			      const char *name)
 +{
-+	dcss_device_t *dcssdev;
-+	int rc;
-+	write_lock(&dcssblk_devices_lock);
-+	if ((dcssdev=dcssblk_get_device_by_minor(MINOR(inode->i_rdev)))
-+	    == NULL) {
-+		rc=-ENODEV;
-+		goto out_unlock;
-+	}
-+	dcssdev->use_count --;
-+	if ((dcssdev->use_count==0) && (dcssdev->save_pending)) {
-+		PRINT_WARN ("Segment %s became idle and is being saved\n",
-+			    dcssdev->name);
-+		dcssblk_do_save (dcssdev);
-+		dcssdev->save_pending = 0;
-+	}
-+	rc = 0;
-+ out_unlock:
-+	write_unlock(&dcssblk_devices_lock);
-+	return rc;
-+}
-+
-+static int dcssblk_make_request (request_queue_t * q, int rw, 
-+				 struct buffer_head * bh) {
-+	dcss_device_t *dcssdev;
-+	unsigned long index;
-+	unsigned long page_addr;
-+	unsigned long source_addr;
-+	unsigned long bytes;
-+
-+	read_lock(&dcssblk_devices_lock);
-+	dcssdev = dcssblk_get_device_by_minor (MINOR(bh->b_rdev));
-+	read_unlock(&dcssblk_devices_lock);
++#ifdef CONFIG_DEVFS_FS
++	umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR;
++	kdev_t device = MKDEV (driver->major, minor);
 +
-+	if (dcssdev == NULL)
-+		/* No such device. */
-+		goto fail;
-+	if ((bh->b_rsector & 3) != 0 || (bh->b_size & 4095) != 0)
-+		/* Request is not page-aligned. */
-+		goto fail;
-+	if ((bh->b_size + (bh->b_rsector<<9)) 
-+	    > (dcssdev->end - dcssdev->start + 1))
-+		/* Request beyond end of DCSS segment. */
-+		goto fail;     
-+	index = (bh->b_rsector >> 3);
-+	page_addr = (unsigned long) bh->b_data;
-+	source_addr = dcssdev->start + (index<<12);
-+	bytes = bh->b_size;
-+	if ((page_addr & 4095) != 0 || (bytes & 4095) != 0)
-+		/* More paranoia. */
-+		goto fail;
-+	if ((rw == READ) || (rw == READA)) {
-+		memcpy ((void*)page_addr, (void*)source_addr, 
-+			bytes);
-+	} else {
-+		memcpy ((void*)source_addr, (void*)page_addr, 
-+			bytes);
++	switch (device) {
++		case TTY_DEV:
++		case PTMX_DEV:
++			mode |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
++			break;
++		default:
++			if (driver->major == PTY_MASTER_MAJOR)
++				flags |= DEVFS_FL_AUTO_OWNER;
++			break;
 +	}
-+	bh->b_end_io(bh, 1);
-+	return 0;
-+ fail:
-+	bh->b_end_io(bh, 0);
-+	return 0;
-+}
-+
-+
-+/*
-+ * setup the block device related things
-+ */
-+static int __init dcssblk_setup_blkdev(void)
-+{
-+	request_queue_t *q;
-+	int i,rc;
-+
-+	for (i=0; i < (1<<MINORBITS); i++)
-+		dcssblk_blksizes[i] = PAGE_SIZE;
-+
-+	/*
-+	 * Register blockdev
-+	 */
-+	rc = register_blkdev(0, DCSSBLK_NAME, &dcssblk_devops);
-+	if (rc < 0) {
-+		PRINT_ERR("Can't get register major\n");
-+		return rc;
++	if ( (minor <  driver->minor_start) || 
++	     (minor >= driver->minor_start + driver->num) ) {
++		printk(KERN_ERR "Attempt to register invalid minor number "
++		       "with devfs (%d:%d).\n", (int)driver->major,(int)minor);
++		return;
 +	}
-+	dcssblk_major = rc;
-+	blksize_size[dcssblk_major] = dcssblk_blksizes;
-+	hardsect_size[dcssblk_major] = dcssblk_blksizes;
-+
-+	
-+	/*
-+	 * Assign the other needed values: make request function, sizes and
-+	 * hardsect size. All the minor devices feature the same value.
-+	 */
-+
-+	q = BLK_DEFAULT_QUEUE(dcssblk_major);
-+	blk_queue_make_request (q, dcssblk_make_request);
-+	return 0;
++#  ifdef CONFIG_UNIX98_PTYS
++	if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) &&
++	     (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) )
++		flags |= DEVFS_FL_CURRENT_OWNER;
++#  endif
++	devfs_register (dir, name, flags | DEVFS_FL_DEFAULT,
++			driver->major, minor, mode, &tty_fops, NULL);
++#endif /* CONFIG_DEVFS_FS */
 +}
 +
-+static void dcssblk_unregister_blkdev(void) {
-+	int rc;
-+	rc = unregister_blkdev (dcssblk_major, DCSSBLK_NAME);
-+	if (rc) {
-+		PRINT_ERR ("Can't unregister blockdev\n");
-+	}	
-+} 
-+
-+/* 
-+ * Register procfs entries
-+ */
-+static int dcssblk_register_procfs(void)
++void tty_unregister_devfs_name (struct tty_driver *driver, unsigned minor,
++				devfs_handle_t dir, const char *name)
 +{
-+#ifdef CONFIG_PROC_FS
-+	dcssblk_proc_root_entry =  proc_mkdir ("dcssblk", &proc_root);
-+	dcssblk_add_entry = create_proc_entry ("add",
-+					       S_IFREG | S_IWUSR,
-+					       dcssblk_proc_root_entry);
-+
-+	dcssblk_remove_entry = create_proc_entry ("remove",
-+						  S_IFREG | S_IWUSR,
-+						  dcssblk_proc_root_entry);
-+
-+	dcssblk_list_entry = create_proc_entry ("list",
-+						  S_IFREG | S_IRUGO,
-+						  dcssblk_proc_root_entry);
-+
-+	dcssblk_add_entry->write_proc = dcssblk_add_store;
-+	dcssblk_add_entry->owner = THIS_MODULE;
-+	dcssblk_remove_entry->write_proc = dcssblk_remove_store;
-+	dcssblk_remove_entry->owner = THIS_MODULE;
-+	dcssblk_list_entry->read_proc = dcssblk_list_store;
-+	dcssblk_list_entry->owner = THIS_MODULE;
-+#endif
-+	dcssblk_devfs_dir = devfs_mk_dir (NULL, 
-+					  "dcssblk", 
-+					  NULL);
-+	return 0;
++#ifdef CONFIG_DEVFS_FS
++	void * handle;
 +
++	handle = devfs_find_handle (dir, name, driver->major, minor,
++				    DEVFS_SPECIAL_CHR, 0);
++	devfs_unregister (handle);
++#endif /* CONFIG_DEVFS_FS */
 +}
 +
-+static void dcssblk_unregister_procfs(void) {
-+	devfs_unregister(dcssblk_devfs_dir);
-+#ifdef CONFIG_PROC_FS
-+	remove_proc_entry ("list", dcssblk_proc_root_entry);
-+	remove_proc_entry ("remove", dcssblk_proc_root_entry);
-+	remove_proc_entry ("add", dcssblk_proc_root_entry);
-+	remove_proc_entry ("dcssblk", NULL);
++extern void tty_unregister_devfs_name (struct tty_driver *driver,
++				       unsigned minor, devfs_handle_t dir,
++				       const char *name);
+ EXPORT_SYMBOL(tty_register_devfs);
+ EXPORT_SYMBOL(tty_unregister_devfs);
++EXPORT_SYMBOL(tty_register_devfs_name);
++EXPORT_SYMBOL(tty_unregister_devfs_name);
+ 
+ /*
+  * Called by a tty driver to register itself.
+@@ -2692,8 +2749,11 @@
+ #ifdef CONFIG_TN3215
+ 	con3215_init();
+ #endif
+-#ifdef CONFIG_HWC
+-        hwc_console_init();
++#ifdef CONFIG_SCLP_CONSOLE
++        sclp_console_init();
 +#endif
-+}
-+
-+/*
-+ * Finally, the init/exit functions.
-+ */
-+static void __exit dcssblk_exit(void)
-+{
-+	dcssblk_unregister_procfs();
-+	dcssblk_unregister_blkdev();
-+}
++#ifdef CONFIG_SCLP_VT220_CONSOLE
++        sclp_vt220_con_init();
+ #endif
+ #ifdef CONFIG_STDIO_CONSOLE
+ 	stdio_console_init();
+@@ -2864,8 +2924,11 @@
+ #ifdef CONFIG_TN3215
+ 	tty3215_init();
+ #endif
+-#ifdef CONFIG_HWC
+-	hwc_tty_init();
++#ifdef CONFIG_SCLP_TTY
++	sclp_tty_init();
++#endif
++#ifdef CONFIG_SCLP_VT220_TTY
++	sclp_vt220_tty_init();
+ #endif
+ #ifdef CONFIG_A2232
+ 	a2232board_init();
+diff -urN kernel-source-2.4.27.orig/drivers/s390/block/dasd_3990_erp.c drivers/s390/block/dasd_3990_erp.c
+--- kernel-source-2.4.27.orig/drivers/s390/block/dasd_3990_erp.c	2004-02-18 06:36:31.000000000 -0700
++++ drivers/s390/block/dasd_3990_erp.c	2006-02-12 12:47:23.000000000 -0700
+@@ -5,7 +5,7 @@
+  * Bugreports.to..: <Linux390 at de.ibm.com>
+  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
+  *
+- * $Revision: 1.52 $
++ * $Revision: 1.52.2.4 $
+  *
+  * History of changes:
+  * 05/14/01 fixed PL030160GTO (BUG() in erp_action_5)
+@@ -585,9 +585,8 @@
+                              ioinfo[irq]->opm);
+ 
+ 		/* reset status to queued to handle the request again... */
+-		check_then_set (&erp->status,
+-                                CQR_STATUS_ERROR,
+-                                CQR_STATUS_QUEUED);
++		if (erp->status > CQR_STATUS_QUEUED)
++                        erp->status = CQR_STATUS_QUEUED;
+ 
+                 erp->retries = 1;
+                 
+@@ -598,12 +597,10 @@
+                              "opm=%x) -> permanent error",
+                              erp->dstat->lpum,
+                              ioinfo[irq]->opm);
+-                
+-                /* post request with permanent error */
+-                check_then_set (&erp->status,
+-                                CQR_STATUS_ERROR,
+-                                CQR_STATUS_FAILED);
+ 
++                /* post request with permanent error */
++		if (erp->status > CQR_STATUS_QUEUED)
++                        erp->status = CQR_STATUS_FAILED;
+         }
+         
+ } /* end dasd_3990_erp_alternate_path */
+@@ -757,6 +754,12 @@
+ 
+                         dasd_3990_erp_block_queue (erp,
+                                                    30);
++                } else if (sense[25] == 0x1E) {	/* busy */
++                        DEV_MESSAGE (KERN_INFO, device,
++                                     "busy - redriving request later, "
++                                     "%d retries left",
++                                     erp->retries);
++                        dasd_3990_erp_block_queue (erp, 1);
+                 } else {
+ 			DEV_MESSAGE (KERN_INFO, device, 
+                                      "redriving request immediately, "
+@@ -768,7 +771,6 @@
+                                         CQR_STATUS_QUEUED);
+                 }
+         }
+-
+ 	return erp;
+ 
+ } /* end dasd_3990_erp_action_4 */
+@@ -2386,7 +2388,7 @@
+                 switch (sense[28]) {
+                 case 0x17:
+                         /* issue a Diagnostic Control command with an 
+-                                * Inhibit Write subcommand and controler modifier */
++                         * Inhibit Write subcommand and controler modifier */
+                         erp = dasd_3990_erp_DCTL (erp,
+                                                   0x20);
+                         break;
+@@ -2603,9 +2605,9 @@
+                                      "Data recovered during retry with PCI "
+                                      "fetch mode active");
+                         
+-                        /* not possible to handle this situation in Linux */    
+-                        panic("Invalid data - No way to inform appliction about "
+-                              "the possibly incorret data");
++                        /* not possible to handle this situation in Linux */
++                        panic("Invalid data - No way to inform application "
++                              "about the possibly incorrect data");
+ 			break;
+ 
+ 		case 0x1D:	/* state-change pending */
+@@ -2617,6 +2619,12 @@
+                                                       sense);
+ 			break;
+ 
++		case 0x1E:	/* busy */
++                        DEV_MESSAGE (KERN_DEBUG, device, "%s",
++                                     "Busy condition exists "
++                                     "for the subsystem or device");
++                        erp = dasd_3990_erp_action_4 (erp, sense);
++			break;
+ 		default:	
+                         ;       /* all others errors - default erp  */
+ 		}
+@@ -2699,7 +2707,8 @@
+ 	if (!erp) {
+                 if (cqr->retries <= 0) {
+                         DEV_MESSAGE (KERN_ERR, device, "%s",
+-                                     "Unable to allocate ERP request (NO retries left)");
++                                     "Unable to allocate ERP request "
++                                     "(NO retries left)");
+                 
+                         check_then_set (&cqr->status,
+                                         CQR_STATUS_ERROR,
+@@ -2709,7 +2718,8 @@
+ 
+                 } else {
+                         DEV_MESSAGE (KERN_ERR, device,
+-                                     "Unable to allocate ERP request (%i retries left)",
++                                     "Unable to allocate ERP request "
++                                     "(%i retries left)",
+                                      cqr->retries);
+                 
+                         if (!timer_pending(&device->timer)) {
+@@ -3169,8 +3179,9 @@
+                 dasd_chanq_enq_head (&device->queue,
+                                      erp);
+         } else {
+-                if ((erp->status == CQR_STATUS_FILLED ) || (erp != device->queue.head)) {
+-                        /* something strange happened - log the error and panic */
++                if ((erp->status == CQR_STATUS_FILLED ) || 
++                    (erp != device->queue.head)) {
++                        /* something strange happened - log error and panic */
+                         /* print current erp_chain */
+                         DEV_MESSAGE (KERN_DEBUG, device, "%s",
+                                      "ERP chain at END of ERP-ACTION");
+@@ -3188,7 +3199,8 @@
+                                                      temp_erp->refers);
+                                 }
+                         }
+-                        panic ("Problems with ERP chain!!! Please report to linux390 at de.ibm.com");
++                        panic ("Problems with ERP chain!!! "
++                               "Please report to linux390 at de.ibm.com");
+                 }
+ 
+         }
+diff -urN kernel-source-2.4.27.orig/drivers/s390/block/dasd.c drivers/s390/block/dasd.c
+--- kernel-source-2.4.27.orig/drivers/s390/block/dasd.c	2004-08-07 17:26:05.000000000 -0600
++++ drivers/s390/block/dasd.c	2006-02-12 12:47:23.000000000 -0700
+@@ -6,7 +6,7 @@
+  * Bugreports.to..: <Linux390 at de.ibm.com>
+  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
+  *
+- * $Revision: 1.311 $
++ * $Revision: 1.298.2.14 $
+  *
+  * History of changes (starts July 2000)
+  * 11/09/00 complete redesign after code review
+@@ -311,7 +311,7 @@
+         /* and add the remaining subranges                                 */
+ 	for (start = index = from, end = -EINVAL; index <= to; index++) {
+                 
+-                if (dasd_devindex_from_devno(index) > 0) {
++                if (dasd_devindex_from_devno(index) >= 0) {
+                         /* current device is already in range */
+                         MESSAGE (KERN_DEBUG,
+                                  "dasd_add_range %04x-%04x: "
+@@ -741,6 +741,15 @@
+ 
+ 	memset (major_info->gendisk.flags, 0, DASD_PER_MAJOR * sizeof (char));
+ 
++	/* init label array */
++	major_info->gendisk.label_arr = (devfs_handle_t *)
++		kmalloc (DASD_PER_MAJOR * sizeof (devfs_handle_t), GFP_KERNEL);
++	if(major_info->gendisk.label_arr == NULL)
++		goto out_gd_label_arr;
 +
-+static int __init dcssblk_init(void)
-+{
-+	int rc;
-+	PRINT_DEBUG ("DCSSBLOCK INIT\n");
-+	rc = dcssblk_register_procfs();
-+	if (rc) goto out;
-+	rc = dcssblk_setup_blkdev();
-+	if (rc) {
-+		dcssblk_unregister_procfs();
-+		goto out;
-+	}
-+ out:
-+	return rc;
-+}
++	memset (major_info->gendisk.label_arr, 0,
++		DASD_PER_MAJOR * sizeof(devfs_handle_t));
 +
-+module_init(dcssblk_init);
-+module_exit(dcssblk_exit);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/Makefile drivers/s390/char/Makefile
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/Makefile	2001-10-11 10:43:29.000000000 -0600
-+++ drivers/s390/char/Makefile	2006-01-30 22:25:26.000000000 -0700
-@@ -4,31 +4,39 @@
+         /* register blockdevice */
+ 	rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations);
+ 	if (rc < 0) {
+@@ -861,6 +870,9 @@
+ 	}
  
- O_TARGET := s390-char.o
+ out_reg_blkdev:
++	kfree (major_info->gendisk.label_arr);
++
++out_gd_label_arr:
+ 	kfree (major_info->gendisk.flags);
  
--list-multi	:= tub3270.o tape390.o
--export-objs	:= hwc_rw.o
-+list-multi	:= 	tub3270.o \
-+			tape390.o
+ out_gd_flags:
+@@ -916,6 +928,7 @@
+ 		major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
+ 	}
+ 
++	kfree (major_info->gendisk.label_arr);
+ 	kfree (major_info->gendisk.flags);
+ 	kfree (major_info->gendisk.de_arr);
+ 
+@@ -2148,11 +2161,10 @@
+                         if (cqr == ERR_PTR(-ENOMEM)) {
+                                 break;
+                         }
+-
+-                        MESSAGE (KERN_EMERG,
+-                                 "(%04x) CCW creation failed "
+-                                 "on request %p",
+-                                 device->devinfo.devno, req);
++                        DEV_MESSAGE (KERN_EMERG, device,
++                                     "CCW creation failed "
++                                     "on request %p rc = %ld",
++                                     req, PTR_ERR(cqr));
+                         dasd_dequeue_request (queue,req);
+                         dasd_end_request (req, 0);
+                         continue;
+@@ -2434,6 +2446,10 @@
+                 era = dasd_era_recover;
+         }
+         
++	/* process channel measurement facility configuration while 
++	   the channel is idle */
++	cmf_device_callback(&device->cdev);
 +
-+export-objs	:=	sclp.o \
-+			tape_core.o \
-+			tape_devmap.o \
-+			tape_std.o
+         switch (era) {
+         case dasd_era_none:
+ 		check_then_set(&cqr->status, 
+@@ -3157,12 +3173,15 @@
+                 spin_lock_irqsave (&range_lock, flags);
+                 list_for_each (l, &dasd_range_head.list) {
+                         temp = list_entry (l, dasd_range_t, list);
+-                        if (device->devinfo.devno >= temp->from && device->devinfo.devno <= temp->to) {
++                        if (device->devinfo.devno >= temp->from && 
++                            device->devinfo.devno <= temp->to) {
+                                 spin_unlock_irqrestore (&range_lock, flags);
+                                 if (intval)
+-                                        temp->features |= DASD_FEATURE_READONLY;
++                                        temp->features |= 
++                                                DASD_FEATURE_READONLY;
+                                 else
+-                                        temp->features &= ~DASD_FEATURE_READONLY;
++                                        temp->features &= 
++                                                ~DASD_FEATURE_READONLY;
+                                 goto continue_blkroset;
+                         }
+                         devindex += temp->to - temp->from + 1;
+@@ -3717,7 +3736,8 @@
+         unsigned long flags;
+ 	int i, devno;
  
- tub3270-objs := tuball.o tubfs.o tubtty.o \
-                      tubttyaid.o tubttybld.o tubttyscl.o \
-                      tubttyrcl.o tubttysiz.o
+-	/* find out devno of leaving device: CIO has already deleted this information ! */
++	/* find out devno of leaving device: CIO has already deleted this */
++        /* information !                                                  */
+         devno = -ENODEV;
+ 	device = NULL;
+ 	list_for_each (l, &dasd_major_info) {
+@@ -3815,7 +3835,7 @@
+ 	}
  
--tape390-$(CONFIG_S390_TAPE_CHAR) += tapechar.o
--tape390-$(CONFIG_S390_TAPE_BLOCK) += tapeblock.o
--tape390-$(CONFIG_S390_TAPE_3480) += tape3480.o tape34xx.o
--tape390-$(CONFIG_S390_TAPE_3490) += tape3490.o tape34xx.o
--tape390-objs := tape.o $(sort $(tape390-y))
-+tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o
-+tape-objs := tape_core.o tape_devmap.o tape_proc.o tape_std.o tape_char.o \
-+	$(sort $(tape-y))
-+obj-$(CONFIG_S390_TAPE) += tape390.o
-+obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
+         if (device &&
+-            device->level >= DASD_STATE_READY) {
++            device->level >= DASD_STATE_NEW) {
+                 s390irq_spin_lock_irqsave (device->devinfo.irq, 
+                                            flags);
+ 		DEV_MESSAGE (KERN_DEBUG, device, "%s",
+@@ -3876,6 +3896,7 @@
+         int i;
+         dasd_device_t* device;
+         dasd_lowmem_t *lowmem;
++        struct list_head *lmem, *next;
+         int rc;
  
- obj-y += ctrlchar.o
- obj-$(CONFIG_TN3215) += con3215.o
--obj-$(CONFIG_HWC) += hwc_con.o hwc_rw.o hwc_tty.o
--obj-$(CONFIG_HWC_CPI) += hwc_cpi.o
-+obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o
-+obj-$(CONFIG_SCLP_TTY) += sclp_tty.o
-+obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
-+obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o
-+obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
- obj-$(CONFIG_TN3270) += tub3270.o
--obj-$(CONFIG_S390_TAPE) += tape390.o
-+obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o
  
- include $(TOPDIR)/Rules.make
+@@ -3892,7 +3913,9 @@
+         memset (device, 0, sizeof (dasd_device_t));
+         dasd_plug_device (device);
+         INIT_LIST_HEAD (&device->lowmem_pool);
+-        
++
++	cmf_device_init(&device->cdev, devno);
++       
+         /* allocate pages for lowmem pool */
+         for (i = 0; i < DASD_LOWMEM_PAGES; i++) {
+                 
+@@ -3906,7 +3929,8 @@
  
- tub3270.o: $(tub3270-objs)
- 	$(LD) -r -o $@ $(tub3270-objs)
+         if (i < DASD_LOWMEM_PAGES) {
+                 /* didn't get the needed lowmem pages */
+-                list_for_each_entry (lowmem, &device->lowmem_pool, list) {
++                list_for_each_safe (lmem, next, &device->lowmem_pool) {
++                        lowmem = list_entry (lmem, dasd_lowmem_t, list);
+                         MESSAGE (KERN_DEBUG,
+                                  "<devno: %04x> not enough memory - "
+                                  "Free page again :%p",
+@@ -3926,6 +3950,7 @@
+ dasd_state_new_to_del (dasd_device_t **addr, int devno)
+ {
+         dasd_lowmem_t *lowmem;
++        struct list_head *l,*n;
  
--tape390.o: $(tape390-objs)
--	$(LD) -r -o $@ $(tape390-objs)
-+tape390.o:	$(tape-objs)
-+	$(LD) -r -o $@ $(tape-objs)
+         dasd_device_t *device = *addr;
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/ctrlchar.c drivers/s390/char/ctrlchar.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/ctrlchar.c	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/s390/char/ctrlchar.c	2006-01-30 22:25:26.000000000 -0700
-@@ -9,6 +9,7 @@
+@@ -3935,7 +3960,9 @@
+         }
  
- #include <linux/config.h>
- #include <linux/stddef.h>
-+#include <linux/errno.h>
- #include <linux/sysrq.h>
- #include <linux/ctype.h>
- #include <linux/interrupt.h>
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc.h drivers/s390/char/hwc.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc.h	2002-11-28 16:53:14.000000000 -0700
-+++ drivers/s390/char/hwc.h	2006-01-30 22:25:26.000000000 -0700
-@@ -1,275 +0,0 @@
--/*
-- *  drivers/s390/char/hwc.h
-- * 
-- *
-- *  S390 version
-- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-- *
-- * 
-- * 
-- */
--
--#ifndef __HWC_H__
--#define __HWC_H__
--
--#define HWC_EXT_INT_PARAM_ADDR	0xFFFFFFF8
--#define HWC_EXT_INT_PARAM_PEND 0x00000001
--
--#define ET_OpCmd		0x01
--#define ET_Msg		0x02
--#define ET_StateChange	0x08
--#define ET_PMsgCmd		0x09
--#define ET_CntlProgOpCmd	0x20
--#define ET_CntlProgIdent	0x0B
--#define ET_SigQuiesce	0x1D
--
--#define ET_OpCmd_Mask	0x80000000
--#define ET_Msg_Mask		0x40000000
--#define ET_StateChange_Mask	0x01000000
--#define ET_PMsgCmd_Mask	0x00800000
--#define ET_CtlProgOpCmd_Mask	0x00000001
--#define ET_CtlProgIdent_Mask	0x00200000
--#define ET_SigQuiesce_Mask	0x00000008
--
--#define GMF_DOM		0x8000
--#define GMF_SndAlrm	0x4000
--#define GMF_HoldMsg	0x2000
--
--#define LTF_CntlText	0x8000
--#define LTF_LabelText	0x4000
--#define LTF_DataText	0x2000
--#define LTF_EndText	0x1000
--#define LTF_PromptText	0x0800
--
--#define HWC_COMMAND_INITIATED	0
--#define HWC_BUSY		2
--#define HWC_NOT_OPERATIONAL	3
--
--#define hwc_cmdw_t u32;
--
--#define HWC_CMDW_READDATA 0x00770005
--
--#define HWC_CMDW_WRITEDATA 0x00760005
--
--#define HWC_CMDW_WRITEMASK 0x00780005
--
--#define GDS_ID_MDSMU		0x1310
--
--#define GDS_ID_MDSRouteInfo	0x1311
--
--#define GDS_ID_AgUnWrkCorr	0x1549
--
--#define GDS_ID_SNACondReport	0x1532
--
--#define GDS_ID_CPMSU		0x1212
--
--#define GDS_ID_RoutTargInstr	0x154D
--
--#define GDS_ID_OpReq		0x8070
--
--#define GDS_ID_TextCmd		0x1320
--
--#define GDS_KEY_SelfDefTextMsg	0x31
--
--#define _HWCB_HEADER	u16	length; \
--			u8	function_code; \
--			u8	control_mask[3]; \
--			u16	response_code;
--
--#define _EBUF_HEADER 	u16	length; \
--			u8	type; \
--			u8	flags; \
--			u16	_reserved;
--
--typedef struct {
--	_EBUF_HEADER
--} __attribute__ ((packed)) 
--
--evbuf_t;
--
--#define _MDB_HEADER	u16	length; \
--			u16	type; \
--			u32	tag; \
--			u32	revision_code;
--
--#define _GO_HEADER	u16	length; \
--			u16	type; \
--			u32	domid; \
--			u8	hhmmss_time[8]; \
--			u8	th_time[3]; \
--			u8	_reserved_0; \
--			u8	dddyyyy_date[7]; \
--			u8	_reserved_1; \
--			u16	general_msg_flags; \
--			u8	_reserved_2[10]; \
--			u8	originating_system_name[8]; \
--			u8	job_guest_name[8];
--
--#define _MTO_HEADER	u16	length; \
--			u16	type; \
--			u16	line_type_flags; \
--			u8	alarm_control; \
--			u8	_reserved[3];
--
--typedef struct {
--	_GO_HEADER
--} __attribute__ ((packed)) 
--
--go_t;
--
--typedef struct {
--	go_t go;
--} __attribute__ ((packed)) 
--
--mdb_body_t;
--
--typedef struct {
--	_MDB_HEADER
--	mdb_body_t mdb_body;
--} __attribute__ ((packed)) 
--
--mdb_t;
--
--typedef struct {
--	_EBUF_HEADER
--	mdb_t mdb;
--} __attribute__ ((packed)) 
--
--msgbuf_t;
--
--typedef struct {
--	_HWCB_HEADER
--	msgbuf_t msgbuf;
--} __attribute__ ((packed)) 
--
--write_hwcb_t;
--
--typedef struct {
--	_MTO_HEADER
--} __attribute__ ((packed)) 
--
--mto_t;
--
--static write_hwcb_t write_hwcb_template =
--{
--	sizeof (write_hwcb_t),
--	0x00,
--	{
--		0x00,
--		0x00,
--		0x00
--	},
--	0x0000,
--	{
--		sizeof (msgbuf_t),
--		ET_Msg,
--		0x00,
--		0x0000,
--		{
--			sizeof (mdb_t),
--			0x0001,
--			0xD4C4C240,
--			0x00000001,
--			{
--				{
--					sizeof (go_t),
--					0x0001
--
--				}
--			}
--		}
--	}
--};
--
--static mto_t mto_template =
--{
--	sizeof (mto_t),
--	0x0004,
--	LTF_EndText,
--	0x00
--};
--
--typedef u32 _hwcb_mask_t;
--
--typedef struct {
--	_HWCB_HEADER
--	u16 _reserved;
--	u16 mask_length;
--	_hwcb_mask_t cp_receive_mask;
--	_hwcb_mask_t cp_send_mask;
--	_hwcb_mask_t hwc_receive_mask;
--	_hwcb_mask_t hwc_send_mask;
--} __attribute__ ((packed)) 
--
--init_hwcb_t;
--
--static init_hwcb_t init_hwcb_template =
--{
--	sizeof (init_hwcb_t),
--	0x00,
--	{
--		0x00,
--		0x00,
--		0x00
--	},
--	0x0000,
--	0x0000,
--	sizeof (_hwcb_mask_t),
--	ET_OpCmd_Mask | ET_PMsgCmd_Mask |
--	ET_StateChange_Mask | ET_SigQuiesce_Mask,
--	ET_Msg_Mask | ET_PMsgCmd_Mask | ET_CtlProgIdent_Mask
--};
--
--typedef struct {
--	_EBUF_HEADER
--	u8 validity_hwc_active_facility_mask:1;
--	u8 validity_hwc_receive_mask:1;
--	u8 validity_hwc_send_mask:1;
--	u8 validity_read_data_function_mask:1;
--	u16 _zeros:12;
--	u16 mask_length;
--	u64 hwc_active_facility_mask;
--	_hwcb_mask_t hwc_receive_mask;
--	_hwcb_mask_t hwc_send_mask;
--	u32 read_data_function_mask;
--} __attribute__ ((packed)) 
--
--statechangebuf_t;
--
--#define _GDS_VECTOR_HEADER	u16	length; \
--				u16	gds_id;
--
--#define _GDS_SUBVECTOR_HEADER	u8	length; \
--				u8	key;
+         /* free lowmem_pool */
+-        list_for_each_entry (lowmem, &device->lowmem_pool, list) {
++	list_for_each_safe (l, n, &device->lowmem_pool) {
++                lowmem = list_entry (l, dasd_lowmem_t, list);
++                list_del(&lowmem->list);
+                 free_page ((unsigned long) lowmem);
+         }
+ 
+@@ -4655,17 +4682,15 @@
+ 		   loff_t * offset)
+ {
+ 	loff_t len;
+-	loff_t n = *offset;
+-	unsigned pos = n;
+ 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+ 
+-	if (n != pos || pos >= p_info->len) {
++	if (*offset >= p_info->len) {
+ 		return 0;	/* EOF */
+ 	} else {
+-		len = MIN (user_len, (p_info->len - pos));
+-		if (copy_to_user (user_buf, &(p_info->data[pos]), len))
++		len = MIN (user_len, (p_info->len - *offset));
++		if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
+ 			return -EFAULT;
+-		*offset = pos + len;
++		(*offset) += len;
+ 		return len;	/* number of bytes "read" */
+ 	}
+ }
+@@ -5188,6 +5213,7 @@
+                          "/proc/dasd/statistics: only 'set' and "
+                          "'reset' are supported verbs");
+ 
++		vfree (buffer);
+         	return -EINVAL;
+ 	}
+ 
+@@ -5243,6 +5269,7 @@
+         
+ 
+ #endif /* DASD_PROFILE */
++        vfree (buffer);
+ 	return user_len;
+ }
+ 
+diff -urN kernel-source-2.4.27.orig/drivers/s390/block/dasd_cmb.c drivers/s390/block/dasd_cmb.c
+--- kernel-source-2.4.27.orig/drivers/s390/block/dasd_cmb.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/block/dasd_cmb.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,236 @@
++/*
++ * linux/drivers/s390/block/dasd_cmb.c ($Revision: 1.7.6.2 $)
++ *
++ * Linux on zSeries Channel Measurement Facility support
++ *  (dasd device driver interface)
++ *
++ * Copyright 2000,2003 IBM Corporation
++ *
++ * Author: Arnd Bergmann <arndb at de.ibm.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, or (at your option)
++ * any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <asm/cmb.h>
++#include <asm/ioctl32.h>
++
++#include "dasd_int.h"
++
++/* This mutex protects us from a race between enable and disable for
++ * a single device. Making it global instead of per device reduces
++ * the memory requirement and makes it possible to use a single
++ * completion handler and return value */
++static DECLARE_MUTEX(cmf_setup_mutex);
++static DECLARE_COMPLETION(cmf_setup_completion);
++static int cmf_setup_return;
++
++static void
++dasd_cmf_enable_callback(struct cmf_device *cdev)
++{
++	cdev->callback = NULL;
++	cmf_setup_return = set_cmf(cdev, 2);
++	complete(&cmf_setup_completion);
++}
++
++static void
++dasd_cmf_disable_callback(struct cmf_device *cdev)
++{
++	cdev->callback = NULL;
++	cmf_setup_return = set_cmf(cdev, 0);
++	complete(&cmf_setup_completion);
++}
++
++static inline int
++dasd_cmf_device_busy(struct dasd_device_t *device)
++{
++	ccw_req_t *cqr;
++	for (cqr = device->queue.head; cqr; cqr = cqr->next) {
++		if (cqr->status == CQR_STATUS_IN_IO)
++			return 1;
++	}
++	return 0;
++}
++
++static int
++dasd_ioctl_cmf_enable(void *inp, int no, long args)
++{
++	struct dasd_device_t *device;
++	int ret;
++
++	device = dasd_device_from_kdev (((struct inode*)inp)->i_rdev);
++	if (!device)
++		return -EINVAL;
++	
++	if (down_interruptible(&cmf_setup_mutex))
++		return -ERESTARTSYS;
++
++	/* the device may already be enabled, in this case
++	   we just reset the cmb to 0 */
++	if (!list_empty(&device->cdev.cmb_list)) {
++		ret = 0;
++		goto out_reset;
++	}
++		
++	ret = enable_cmf(&device->cdev);
++	if (ret)
++		goto out;
++
++	MOD_INC_USE_COUNT;
++
++	spin_lock_irq(device->cdev.ccwlock);
++	if (!dasd_cmf_device_busy(device)) {
++		ret = set_cmf(&device->cdev, 2);
++		spin_unlock_irq(device->cdev.ccwlock);
++	} else {
++		device->cdev.callback = &dasd_cmf_enable_callback;
++		spin_unlock_irq(device->cdev.ccwlock);
++		wait_for_completion(&cmf_setup_completion);
++		ret = cmf_setup_return;
++	}
++
++	if (ret) {
++		disable_cmf(&device->cdev);
++		MOD_DEC_USE_COUNT;
++	}
++
++out_reset:
++	cmf_reset(&device->cdev);
++out:
++	up(&cmf_setup_mutex);
++	return ret;
++}
++
++static int
++dasd_ioctl_cmf_disable(void *inp, int no, long args)
++{
++	struct dasd_device_t *device;
++	int ret;
++
++	device = dasd_device_from_kdev (((struct inode*)inp)->i_rdev);
++	if (!device)
++		return -EINVAL;
++
++	if (down_interruptible(&cmf_setup_mutex))
++		return -ERESTARTSYS;
++
++	spin_lock_irq(device->cdev.ccwlock);
++
++	if (!dasd_cmf_device_busy(device)) {
++		ret = set_cmf(&device->cdev, 0);
++		spin_unlock_irq(device->cdev.ccwlock);
++	} else {
++		device->cdev.callback = &dasd_cmf_disable_callback;
++		spin_unlock_irq(device->cdev.ccwlock);
++		wait_for_completion(&cmf_setup_completion);
++		ret = cmf_setup_return;
++	}
++
++	if(!ret) {
++		disable_cmf(&device->cdev);
++		MOD_DEC_USE_COUNT;
++	}
++	up(&cmf_setup_mutex);
++	return ret;
++
++}
++
++static int
++dasd_ioctl_readall_cmb(void *inp, int no, long args)
++{
++	struct dasd_device_t *device;
++	struct cmbdata * udata;
++	struct cmbdata data;
++	size_t size;
++	int ret;
++
++	device = dasd_device_from_kdev (((struct inode*)inp)->i_rdev);
++	if (!device)
++		return -EINVAL;
++	udata = (void *) args;
++	size = _IOC_SIZE(no);
++
++	if (!access_ok(VERIFY_WRITE, udata, size))
++		return -EFAULT;
++	ret = cmf_readall(&device->cdev, &data);
++	if (ret)
++		return ret;
++	if (copy_to_user(udata, &data, min(size, sizeof(*udata))))
++		return -EFAULT;
++	return 0;
++}
++
++/* module initialization below here. dasd already provides a mechanism
++ * to dynamically register ioctl functions, so we simply use this.
++ * FIXME: register ioctl32 functions as well. */
++static inline int
++ioctl_reg(unsigned int no, dasd_ioctl_fn_t handler)
++{
++	int ret;
++	ret = dasd_ioctl_no_register(THIS_MODULE, no, handler);
++	if (ret)
++		return ret;
++
++	ret = register_ioctl32_conversion(no, sys_ioctl);
++	if (ret)
++		dasd_ioctl_no_unregister(THIS_MODULE, no, handler);
++
++	return ret;
++}
++
++static inline void
++ioctl_unreg(unsigned int no, dasd_ioctl_fn_t handler)
++{
++	dasd_ioctl_no_unregister(THIS_MODULE, no, handler);
++	unregister_ioctl32_conversion(no);
++
++}
++
++static void
++dasd_cmf_exit(void)
++{
++	ioctl_unreg(BIODASDCMFENABLE,  dasd_ioctl_cmf_enable);
++	ioctl_unreg(BIODASDCMFDISABLE, dasd_ioctl_cmf_disable);
++	ioctl_unreg(BIODASDREADALLCMB, dasd_ioctl_readall_cmb);
++}
++
++static int __init
++dasd_cmf_init(void)
++{
++	int ret;
++	ret = ioctl_reg (BIODASDCMFENABLE, dasd_ioctl_cmf_enable);
++	if (ret)
++		goto err;
++	ret = ioctl_reg (BIODASDCMFDISABLE, dasd_ioctl_cmf_disable);
++	if (ret)
++		goto err;
++	ret = ioctl_reg (BIODASDREADALLCMB, dasd_ioctl_readall_cmb);
++	if (ret)
++		goto err;
++
++	return 0;
++err:
++	dasd_cmf_exit();
++
++	return ret;
++}
++
++module_init(dasd_cmf_init);
++module_exit(dasd_cmf_exit);
++
++MODULE_AUTHOR("Arnd Bergmann <arndb at de.ibm.com>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("channel measurement facility interface for dasd\n"
++		   "Copyright 2003 IBM Corporation\n");
+diff -urN kernel-source-2.4.27.orig/drivers/s390/block/dasd_diag.c drivers/s390/block/dasd_diag.c
+--- kernel-source-2.4.27.orig/drivers/s390/block/dasd_diag.c	2004-02-18 06:36:31.000000000 -0700
++++ drivers/s390/block/dasd_diag.c	2006-02-12 12:47:23.000000000 -0700
+@@ -6,7 +6,7 @@
+  * Bugreports.to..: <Linux390 at de.ibm.com>
+  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
+  *
+- * $Revision: 1.49 $
++ * $Revision: 1.47.6.2 $
+  *
+  * History of changes
+  * 07/13/00 Added fixup sections for diagnoses ans saved some registers
+diff -urN kernel-source-2.4.27.orig/drivers/s390/block/dasd_fba.c drivers/s390/block/dasd_fba.c
+--- kernel-source-2.4.27.orig/drivers/s390/block/dasd_fba.c	2004-02-18 06:36:31.000000000 -0700
++++ drivers/s390/block/dasd_fba.c	2006-02-12 12:47:23.000000000 -0700
+@@ -4,7 +4,7 @@
+  * Bugreports.to..: <Linux390 at de.ibm.com>
+  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
+  *
+- * $Revision: 1.50 $
++ * $Revision: 1.49.6.3 $
+  *
+  * History of changes
+  *          fixed partition handling and HDIO_GETGEO
+@@ -94,14 +94,18 @@
+         return rc;
+ }
+ 
+-static inline void
++static inline int
+ locate_record (ccw1_t * ccw, LO_fba_data_t * LO_data, int rw, int block_nr,
+ 	       int block_ct, ccw_req_t* cqr, dasd_device_t* device)
+ {
++        int errcode;
++
+ 	memset (LO_data, 0, sizeof (LO_fba_data_t));
+ 	ccw->cmd_code = DASD_FBA_CCW_LOCATE;
+ 	ccw->count = 8;
+-	dasd_set_normalized_cda (ccw, __pa (LO_data), cqr, device);
++	if ((errcode = dasd_set_normalized_cda (ccw, __pa (LO_data), cqr, 
++                                                device)))
++                return errcode;
+ 	if (rw == WRITE)
+ 		LO_data->operation.cmd = 0x5;
+ 	else if (rw == READ)
+@@ -110,6 +114,8 @@
+ 		LO_data->operation.cmd = 0x8;
+ 	LO_data->blk_nr = block_nr;
+ 	LO_data->blk_ct = block_ct;
++
++        return 0;
+ }
+ 
+ static int
+@@ -248,7 +254,7 @@
+ 	    stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
+ 		    return dasd_era_none;
+ 
+-	switch (device->devinfo.sid_data.dev_model) {
++	switch (device->devinfo.sid_data.dev_type) {
+ 	case 0x3370:
+ 		return dasd_3370_erp_examine (cqr, stat);
+ 	case 0x9336:
+@@ -292,7 +298,8 @@
+ 	int byt_per_blk = device->sizes.bp_block;
+         unsigned long reloc_sector = req->sector + 
+                 device->major_info->gendisk.part[MINOR (req->rq_dev)].start_sect;
+-        
++        int errcode;
++
+ 	if (req->cmd == READ) {
+ 		rw_cmd = DASD_FBA_CCW_READ;
+ 	} else if (req->cmd == WRITE) {
+@@ -337,29 +344,30 @@
+ 	LO_data = rw_cp->data + sizeof (DE_fba_data_t);
+ 	ccw = rw_cp->cpaddr;
+ 
+-	if (define_extent (ccw, DE_data, req->cmd, byt_per_blk,
+-                           reloc_sector, req->nr_sectors, rw_cp, device)) {
++	if ((errcode = define_extent (ccw, DE_data, req->cmd, byt_per_blk,
++                                      reloc_sector, req->nr_sectors, rw_cp, 
++                                      device)))
+                 goto clear_rw_cp;
+-        }
++
+ 	ccw->flags |= CCW_FLAG_CC;
+         ccw ++;
+-        locate_record (ccw, LO_data, req->cmd, 0, 
+-                       private->rdc_data.mode.bits.data_chain ? bhct : 1, rw_cp, device);
+-        if (ccw->cda == 0) {
++        if ((errcode = locate_record (ccw, LO_data, req->cmd, 0, 
++                                      private->rdc_data.mode.bits.data_chain ? 
++                                      bhct : 1, rw_cp, device)))
+                 goto clear_rw_cp;
+-        }
++
+         ccw->flags |= CCW_FLAG_CC;
+         
+-        bh = req -> bh;
+-        i = 0;
+-        while ( bh != NULL ) {
++        for (bh = req->bh, i = 0; bh != NULL; ) {
+                 for (size = 0; size < bh->b_size; size += byt_per_blk) {
+                         ccw ++;
+                         ccw->cmd_code = rw_cmd;
+                         ccw->count = byt_per_blk;
+-                        if (dasd_set_normalized_cda (ccw,__pa (bh->b_data + size), rw_cp, device)) {
++                        if ((errcode = dasd_set_normalized_cda (ccw,
++                                               __pa (bh->b_data + size), 
++                                               rw_cp, device))) 
+                                 goto clear_rw_cp;
+-                        }
++                        
+                         if (private->rdc_data.mode.bits.data_chain) {
+                                 ccw->flags |= CCW_FLAG_DC;
+                         } else {
+@@ -372,23 +380,25 @@
+                         ccw++;
+                         i++;
+                         LO_data++;
+-                        locate_record (ccw, LO_data, req->cmd, i, 1, rw_cp, device);
+-                        if (ccw->cda == 0) {
++                        if ((errcode = locate_record (ccw, LO_data, req->cmd,
++                                                      i, 1, rw_cp, device)))
+                                 goto clear_rw_cp;
+-                        }
++                        
+                         ccw->flags |= CCW_FLAG_CC;
+                 }
+         }
+ 	ccw->flags &= ~(CCW_FLAG_DC | CCW_FLAG_CC);
 -
--typedef struct {
--	_GDS_VECTOR_HEADER
--} __attribute__ ((packed)) 
--
--gds_vector_t;
--
--typedef struct {
--	_GDS_SUBVECTOR_HEADER
--} __attribute__ ((packed)) 
--
--gds_subvector_t;
--
--typedef struct {
--	_HWCB_HEADER
--} __attribute__ ((packed)) 
--
--read_hwcb_t;
--
--static read_hwcb_t read_hwcb_template =
--{
--	PAGE_SIZE,
--	0x00,
--	{
--		0x00,
--		0x00,
--		0x80
--	}
--};
--
--#endif				/* __HWC_H__ */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_con.c drivers/s390/char/hwc_con.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_con.c	2002-08-02 18:39:44.000000000 -0600
-+++ drivers/s390/char/hwc_con.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1,89 +0,0 @@
--/*
-- *  drivers/s390/char/hwc_con.c
-- *    HWC line mode console driver
-- *
-- *  S390 version
-- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-- */
--
--#include <linux/config.h>
--#include <linux/kernel.h>
--#include <linux/major.h>
--#include <linux/errno.h>
--#include <linux/kdev_t.h>
--#include <linux/string.h>
--#include <linux/console.h>
--#include <linux/fs.h>
--#include <linux/init.h>
--
--#include "hwc_rw.h"
--
--#ifdef CONFIG_HWC_CONSOLE
--
--#define  hwc_console_major 4
--#define  hwc_console_minor 64
--#define  hwc_console_name  "console"
--
--void hwc_console_write (struct console *, const char *, unsigned int);
--kdev_t hwc_console_device (struct console *);
--void hwc_console_unblank (void);
--
--#define  HWC_CON_PRINT_HEADER "hwc console driver: "
--
--struct console hwc_console = {
--	name:	hwc_console_name,
--	write:	hwc_console_write,
--	device:	hwc_console_device,
--	unblank:hwc_console_unblank,
--	flags:	CON_PRINTBUFFER,
--};
--
--void 
--hwc_console_write (
--			  struct console *console,
--			  const char *message,
--			  unsigned int count)
--{
--
--	if (console->device (console) != hwc_console.device (&hwc_console)) {
--
--		hwc_printk (KERN_WARNING HWC_CON_PRINT_HEADER
--			    "hwc_console_write() called with wrong "
--			    "device number");
--		return;
--	}
--	hwc_write (0, message, count);
--}
--
--kdev_t 
--hwc_console_device (struct console * c)
--{
--	return MKDEV (hwc_console_major, hwc_console_minor);
--}
--
--void 
--hwc_console_unblank (void)
--{
--	hwc_unblank ();
--}
--
--#endif
--
--void __init 
--hwc_console_init (void)
--{
--	if (!MACHINE_HAS_HWC)
--		return;
--
--	if (hwc_init () == 0) {
--#ifdef CONFIG_HWC_CONSOLE
--
--		if (CONSOLE_IS_HWC)
--			register_console (&hwc_console);
--#endif
--	} else
--		panic (HWC_CON_PRINT_HEADER "hwc initialisation failed !");
--
--	return;
--}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_cpi.c drivers/s390/char/hwc_cpi.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_cpi.c	2001-10-11 10:43:29.000000000 -0600
-+++ drivers/s390/char/hwc_cpi.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1,211 +0,0 @@
--
--/*
-- * Author: Martin Peschke <mpeschke at de.ibm.com>
-- * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation
-- */
--
--#include <linux/string.h>
--#include <linux/ctype.h>
--#include <linux/module.h>
--#include <linux/init.h>
--#include <linux/errno.h>
--#include <linux/slab.h>
--#include <linux/version.h>
--#include <asm/semaphore.h>
--#include <asm/ebcdic.h>
--#include "hwc_rw.h"
--#include "hwc.h"
--
--#define CPI_RETRIES		3
--#define CPI_SLEEP_TICKS		50
--
--#define CPI_LENGTH_SYSTEM_TYPE	8
--#define CPI_LENGTH_SYSTEM_NAME	8
--#define CPI_LENGTH_SYSPLEX_NAME	8
--
--typedef struct {
--	_EBUF_HEADER
--	u8 id_format;
--	u8 reserved0;
--	u8 system_type[CPI_LENGTH_SYSTEM_TYPE];
--	u64 reserved1;
--	u8 system_name[CPI_LENGTH_SYSTEM_NAME];
--	u64 reserved2;
--	u64 system_level;
--	u64 reserved3;
--	u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME];
--	u8 reserved4[16];
--} __attribute__ ((packed)) 
--
--cpi_evbuf_t;
--
--typedef struct _cpi_hwcb_t {
--	_HWCB_HEADER
--	cpi_evbuf_t cpi_evbuf;
--} __attribute__ ((packed)) 
--
--cpi_hwcb_t;
--
--cpi_hwcb_t *cpi_hwcb;
--
--static int __init cpi_module_init (void);
--static void __exit cpi_module_exit (void);
--
--module_init (cpi_module_init);
--module_exit (cpi_module_exit);
--
--MODULE_AUTHOR (
--		      "Martin Peschke, IBM Deutschland Entwicklung GmbH "
--		      "<mpeschke at de.ibm.com>");
--
--MODULE_DESCRIPTION (
--  "identify this operating system instance to the S/390 or zSeries hardware");
--
--static char *system_name = NULL;
--MODULE_PARM (system_name, "s");
--MODULE_PARM_DESC (system_name, "e.g. hostname - max. 8 characters");
--
--static char *sysplex_name = NULL;
--#ifdef ALLOW_SYSPLEX_NAME
--MODULE_PARM (sysplex_name, "s");
--MODULE_PARM_DESC (sysplex_name, "if applicable - max. 8 characters");
--#endif
--
--static char *system_type = "LINUX";
--
--hwc_request_t cpi_request =
--{};
--
--hwc_callback_t cpi_callback;
--
--static DECLARE_MUTEX_LOCKED (sem);
--
--static int __init 
--cpi_module_init (void)
--{
--	int retval;
--	int system_type_length;
--	int system_name_length;
--	int sysplex_name_length = 0;
--	int retries;
--
--	if (!MACHINE_HAS_HWC) {
--		printk ("cpi: bug: hardware console not present\n");
--		retval = -EINVAL;
--		goto out;
--	}
--	if (!system_type) {
--		printk ("cpi: bug: no system type specified\n");
--		retval = -EINVAL;
--		goto out;
--	}
--	system_type_length = strlen (system_type);
--	if (system_type_length > CPI_LENGTH_SYSTEM_NAME) {
--		printk ("cpi: bug: system type has length of %i characters - "
--			"only %i characters supported\n",
--			system_type_length,
--			CPI_LENGTH_SYSTEM_TYPE);
--		retval = -EINVAL;
--		goto out;
--	}
--	if (!system_name) {
--		printk ("cpi: no system name specified\n");
--		retval = -EINVAL;
--		goto out;
--	}
--	system_name_length = strlen (system_name);
--	if (system_name_length > CPI_LENGTH_SYSTEM_NAME) {
--		printk ("cpi: system name has length of %i characters - "
--			"only %i characters supported\n",
--			system_name_length,
--			CPI_LENGTH_SYSTEM_NAME);
--		retval = -EINVAL;
--		goto out;
--	}
--	if (sysplex_name) {
--		sysplex_name_length = strlen (sysplex_name);
--		if (sysplex_name_length > CPI_LENGTH_SYSPLEX_NAME) {
--			printk ("cpi: sysplex name has length of %i characters - "
--				"only %i characters supported\n",
--				sysplex_name_length,
--				CPI_LENGTH_SYSPLEX_NAME);
--			retval = -EINVAL;
--			goto out;
--		}
--	}
--	cpi_hwcb = kmalloc (sizeof (cpi_hwcb_t), GFP_KERNEL);
--	if (!cpi_hwcb) {
--		printk ("cpi: no storage to fulfill request\n");
--		retval = -ENOMEM;
--		goto out;
--	}
--	memset (cpi_hwcb, 0, sizeof (cpi_hwcb_t));
--
--	cpi_hwcb->length = sizeof (cpi_hwcb_t);
--	cpi_hwcb->cpi_evbuf.length = sizeof (cpi_evbuf_t);
--	cpi_hwcb->cpi_evbuf.type = 0x0B;
--
--	memset (cpi_hwcb->cpi_evbuf.system_type, ' ', CPI_LENGTH_SYSTEM_TYPE);
--	memcpy (cpi_hwcb->cpi_evbuf.system_type, system_type, system_type_length);
--	HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.system_type, CPI_LENGTH_SYSTEM_TYPE);
--	EBC_TOUPPER (cpi_hwcb->cpi_evbuf.system_type, CPI_LENGTH_SYSTEM_TYPE);
--
--	memset (cpi_hwcb->cpi_evbuf.system_name, ' ', CPI_LENGTH_SYSTEM_NAME);
--	memcpy (cpi_hwcb->cpi_evbuf.system_name, system_name, system_name_length);
--	HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.system_name, CPI_LENGTH_SYSTEM_NAME);
--	EBC_TOUPPER (cpi_hwcb->cpi_evbuf.system_name, CPI_LENGTH_SYSTEM_NAME);
--
--	cpi_hwcb->cpi_evbuf.system_level = LINUX_VERSION_CODE;
--
--	if (sysplex_name) {
--		memset (cpi_hwcb->cpi_evbuf.sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME);
--		memcpy (cpi_hwcb->cpi_evbuf.sysplex_name, sysplex_name, sysplex_name_length);
--		HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
--		EBC_TOUPPER (cpi_hwcb->cpi_evbuf.sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
--	}
--	cpi_request.block = cpi_hwcb;
--	cpi_request.word = HWC_CMDW_WRITEDATA;
--	cpi_request.callback = cpi_callback;
--
--	for (retries = CPI_RETRIES; retries; retries--) {
--		retval = hwc_send (&cpi_request);
--		if (retval) {
--
--			set_current_state (TASK_INTERRUPTIBLE);
--			schedule_timeout (CPI_SLEEP_TICKS);
--		} else {
--
--			down (&sem);
--
--			switch (cpi_hwcb->response_code) {
--			case 0x0020:
--				printk ("cpi: succeeded\n");
--				break;
--			default:
--				printk ("cpi: failed with response code 0x%x\n",
--					cpi_hwcb->response_code);
--			}
--			goto free;
--		}
--	}
--
--	printk ("cpi: failed (%i)\n", retval);
--
--      free:
--	kfree (cpi_hwcb);
--
--      out:
--	return retval;
--}
--
--static void __exit 
--cpi_module_exit (void)
--{
--	printk ("cpi: exit\n");
--}
--
--void 
--cpi_callback (hwc_request_t * req)
--{
--	up (&sem);
--}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_rw.c drivers/s390/char/hwc_rw.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_rw.c	2002-11-28 16:53:14.000000000 -0700
-+++ drivers/s390/char/hwc_rw.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1,2458 +0,0 @@
--/*
-- *  drivers/s390/char/hwc_rw.c
-- *     driver: reading from and writing to system console on S/390 via HWC
-- *
-- *  S390 version
-- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-- *
-- * 
-- *
-- * 
-- * 
-- * 
-- */
--
--#include <linux/kernel.h>
--#include <linux/string.h>
--#include <linux/errno.h>
--#include <linux/ctype.h>
--#include <linux/mm.h>
--#include <linux/timer.h>
--#include <linux/bootmem.h>
--#include <linux/module.h>
--
--#include <asm/ebcdic.h>
--#include <asm/uaccess.h>
--#include <asm/types.h>
--#include <asm/bitops.h>
--#include <asm/setup.h>
--#include <asm/page.h>
--#include <asm/s390_ext.h>
--#include <asm/irq.h>
--
--#ifndef MIN
--#define MIN(a,b) (((a<b) ? a : b))
--#endif
--
--extern void ctrl_alt_del (void);
--
--#define HWC_RW_PRINT_HEADER "hwc low level driver: "
--
--#define  USE_VM_DETECTION
--
--#define  DEFAULT_CASE_DELIMITER '%'
--
--#undef DUMP_HWC_INIT_ERROR
--
--#undef DUMP_HWC_WRITE_ERROR
--
--#undef DUMP_HWC_WRITE_LIST_ERROR
--
--#undef DUMP_HWC_READ_ERROR
--
--#undef DUMP_HWCB_INPUT
--
--#undef BUFFER_STRESS_TEST
--
--typedef struct {
--	unsigned char *next;
--	unsigned short int mto_char_sum;
--	unsigned char mto_number;
--	unsigned char times_lost;
--	unsigned short int mto_number_lost;
--	unsigned long int mto_char_sum_lost;
--} __attribute__ ((packed)) 
--
--hwcb_list_t;
--
--#define MAX_HWCB_ROOM (PAGE_SIZE - sizeof(hwcb_list_t))
--
--#define MAX_MESSAGE_SIZE (MAX_HWCB_ROOM - sizeof(write_hwcb_t))
--
--#define BUF_HWCB hwc_data.hwcb_list_tail
--#define OUT_HWCB hwc_data.hwcb_list_head
--#define ALL_HWCB_MTO hwc_data.mto_number
--#define ALL_HWCB_CHAR hwc_data.mto_char_sum
--
--#define _LIST(hwcb) ((hwcb_list_t*)(&(hwcb)[PAGE_SIZE-sizeof(hwcb_list_t)]))
--
--#define _HWCB_CHAR(hwcb) (_LIST(hwcb)->mto_char_sum)
--
--#define _HWCB_MTO(hwcb) (_LIST(hwcb)->mto_number)
--
--#define _HWCB_CHAR_LOST(hwcb) (_LIST(hwcb)->mto_char_sum_lost)
--
--#define _HWCB_MTO_LOST(hwcb) (_LIST(hwcb)->mto_number_lost)
--
--#define _HWCB_TIMES_LOST(hwcb) (_LIST(hwcb)->times_lost)
--
--#define _HWCB_NEXT(hwcb) (_LIST(hwcb)->next)
--
--#define BUF_HWCB_CHAR _HWCB_CHAR(BUF_HWCB)
--
--#define BUF_HWCB_MTO _HWCB_MTO(BUF_HWCB)
--
--#define BUF_HWCB_NEXT _HWCB_NEXT(BUF_HWCB)
--
--#define OUT_HWCB_CHAR _HWCB_CHAR(OUT_HWCB)
--
--#define OUT_HWCB_MTO _HWCB_MTO(OUT_HWCB)
--
--#define OUT_HWCB_NEXT _HWCB_NEXT(OUT_HWCB)
--
--#define BUF_HWCB_CHAR_LOST _HWCB_CHAR_LOST(BUF_HWCB)
--
--#define BUF_HWCB_MTO_LOST _HWCB_MTO_LOST(BUF_HWCB)
--
--#define OUT_HWCB_CHAR_LOST _HWCB_CHAR_LOST(OUT_HWCB)
--
--#define OUT_HWCB_MTO_LOST _HWCB_MTO_LOST(OUT_HWCB)
--
--#define BUF_HWCB_TIMES_LOST _HWCB_TIMES_LOST(BUF_HWCB)
--
--#include  "hwc.h"
--
--#define __HWC_RW_C__
--#include "hwc_rw.h"
--#undef __HWC_RW_C__
--
--static unsigned char _obuf[MAX_HWCB_ROOM];
--
--static unsigned char
-- _page[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE)));
--
--typedef unsigned long kmem_pages_t;
--
--#define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3)
--
--#define HWC_WTIMER_RUNS	1
--#define HWC_FLUSH		2
--#define HWC_INIT		4
--#define HWC_BROKEN		8
--#define HWC_INTERRUPT		16
--#define HWC_PTIMER_RUNS	32
--
--static struct {
--
--	hwc_ioctls_t ioctls;
--
--	hwc_ioctls_t init_ioctls;
--
--	unsigned char *hwcb_list_head;
--
--	unsigned char *hwcb_list_tail;
--
--	unsigned short int mto_number;
--
--	unsigned int mto_char_sum;
--
--	unsigned char hwcb_count;
--
--	unsigned long kmem_start;
--
--	unsigned long kmem_end;
--
--	kmem_pages_t kmem_pages;
--
--	unsigned char *obuf;
--
--	unsigned short int obuf_cursor;
--
--	unsigned short int obuf_count;
--
--	unsigned short int obuf_start;
--
--	unsigned char *page;
--
--	u32 current_servc;
--
--	unsigned char *current_hwcb;
--
--	unsigned char write_nonprio:1;
--	unsigned char write_prio:1;
--	unsigned char read_nonprio:1;
--	unsigned char read_prio:1;
--	unsigned char read_statechange:1;
--	unsigned char sig_quiesce:1;
--
--	unsigned char flags;
--
--	hwc_high_level_calls_t *calls;
--
--	hwc_request_t *request;
--
--	spinlock_t lock;
--
--	struct timer_list write_timer;
--
--	struct timer_list poll_timer;
--} hwc_data =
--{
--	{
--	},
--	{
--		8,
--		    0,
--		    80,
--		    1,
--		    MAX_KMEM_PAGES,
--		    MAX_KMEM_PAGES,
--
--		    0,
--
--		    0x6c
--
--	},
--	    NULL,
--	    NULL,
--	    0,
--	    0,
--	    0,
--	    0,
--	    0,
--	    0,
--	    _obuf,
--	    0,
--	    0,
--	    0,
--	    _page,
--	    0,
--	    NULL,
--	    0,
--	    0,
--	    0,
--	    0,
--	    0,
--	    0,
--	    0,
--	    NULL,
--	    NULL
--
--};
--
--static unsigned long cr0 __attribute__ ((aligned (8)));
--static unsigned long cr0_save __attribute__ ((aligned (8)));
--static unsigned char psw_mask __attribute__ ((aligned (8)));
--
--static ext_int_info_t ext_int_info_hwc;
--
--#define DELAYED_WRITE 0
--#define IMMEDIATE_WRITE 1
--
--static signed int do_hwc_write (int from_user, unsigned char *,
--				unsigned int,
--				unsigned char);
--
--unsigned char hwc_ip_buf[512];
--
--static asmlinkage int 
--internal_print (char write_time, char *fmt,...)
--{
--	va_list args;
--	int i;
--
--	va_start (args, fmt);
--	i = vsprintf (hwc_ip_buf, fmt, args);
--	va_end (args);
--	return do_hwc_write (0, hwc_ip_buf, i, write_time);
--}
--
--int 
--hwc_printk (const char *fmt,...)
--{
--	va_list args;
--	int i;
--	unsigned long flags;
--	int retval;
--
--	spin_lock_irqsave (&hwc_data.lock, flags);
--
--	i = vsprintf (hwc_ip_buf, fmt, args);
--	va_end (args);
--	retval = do_hwc_write (0, hwc_ip_buf, i, IMMEDIATE_WRITE);
--
--	spin_unlock_irqrestore (&hwc_data.lock, flags);
--
--	return retval;
--}
--
--#ifdef DUMP_HWCB_INPUT
--
--static void 
--dump_storage_area (unsigned char *area, unsigned short int count)
--{
--	unsigned short int index;
--	ioctl_nl_t old_final_nl;
--
--	if (!area || !count)
--		return;
--
--	old_final_nl = hwc_data.ioctls.final_nl;
--	hwc_data.ioctls.final_nl = 1;
--
--	internal_print (DELAYED_WRITE, "\n%8x   ", area);
--
--	for (index = 0; index < count; index++) {
--
--		if (area[index] <= 0xF)
--			internal_print (DELAYED_WRITE, "0%x", area[index]);
--		else
--			internal_print (DELAYED_WRITE, "%x", area[index]);
--
--		if ((index & 0xF) == 0xF)
--			internal_print (DELAYED_WRITE, "\n%8x   ",
--					&area[index + 1]);
--		else if ((index & 3) == 3)
--			internal_print (DELAYED_WRITE, " ");
--	}
--
--	internal_print (IMMEDIATE_WRITE, "\n");
--
--	hwc_data.ioctls.final_nl = old_final_nl;
--}
--#endif
--
--static inline u32 
--service_call (
--		     u32 hwc_command_word,
--		     unsigned char hwcb[])
--{
--	unsigned int condition_code = 1;
--
--	__asm__ __volatile__ ("L 1, 0(%0) \n\t"
--			      "LRA 2, 0(%1) \n\t"
--			      ".long 0xB2200012 \n\t"
--			      :
--			      :"a" (&hwc_command_word), "a" (hwcb)
--			      :"1", "2", "memory");
--
--	__asm__ __volatile__ ("IPM %0 \n\t"
--			      "SRL %0, 28 \n\t"
--			      :"=r" (condition_code));
--
--	return condition_code;
--}
--
--static inline unsigned long 
--hwc_ext_int_param (void)
--{
--	u32 param;
--
--	__asm__ __volatile__ ("L %0,128\n\t"
--			      :"=r" (param));
--
--	return (unsigned long) param;
--}
--
--static int 
--prepare_write_hwcb (void)
--{
--	write_hwcb_t *hwcb;
--
--	if (!BUF_HWCB)
--		return -ENOMEM;
--
--	BUF_HWCB_MTO = 0;
--	BUF_HWCB_CHAR = 0;
--
--	hwcb = (write_hwcb_t *) BUF_HWCB;
--
--	memcpy (hwcb, &write_hwcb_template, sizeof (write_hwcb_t));
--
--	return 0;
--}
--
--static int 
--sane_write_hwcb (void)
--{
--	unsigned short int lost_msg;
--	unsigned int lost_char;
--	unsigned char lost_hwcb;
--	unsigned char *bad_addr;
--	unsigned long page;
--	int page_nr;
--
--	if (!OUT_HWCB)
--		return -ENOMEM;
--
--	if ((unsigned long) OUT_HWCB & 0xFFF) {
--
--		bad_addr = OUT_HWCB;
--
--#ifdef DUMP_HWC_WRITE_LIST_ERROR
--		__asm__ ("LHI 1,0xe30\n\t"
--			 "LRA 2,0(%0) \n\t"
--			 "J .+0 \n\t"
--	      :
--	      :	 "a" (bad_addr)
--	      :	 "1", "2");
--#endif
--
--		hwc_data.kmem_pages = 0;
--		if ((unsigned long) BUF_HWCB & 0xFFF) {
--
--			lost_hwcb = hwc_data.hwcb_count;
--			lost_msg = ALL_HWCB_MTO;
--			lost_char = ALL_HWCB_CHAR;
--
--			OUT_HWCB = NULL;
--			BUF_HWCB = NULL;
--			ALL_HWCB_MTO = 0;
--			ALL_HWCB_CHAR = 0;
--			hwc_data.hwcb_count = 0;
--		} else {
--
--			lost_hwcb = hwc_data.hwcb_count - 1;
--			lost_msg = ALL_HWCB_MTO - BUF_HWCB_MTO;
--			lost_char = ALL_HWCB_CHAR - BUF_HWCB_CHAR;
--			OUT_HWCB = BUF_HWCB;
--			ALL_HWCB_MTO = BUF_HWCB_MTO;
--			ALL_HWCB_CHAR = BUF_HWCB_CHAR;
--			hwc_data.hwcb_count = 1;
--			page = (unsigned long) BUF_HWCB;
--
--			if (page >= hwc_data.kmem_start &&
--			    page <= hwc_data.kmem_end) {
--
--				page_nr = (int)
--				    ((page - hwc_data.kmem_start) >> 12);
--				set_bit (page_nr, &hwc_data.kmem_pages);
--			}
--		}
--
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--		       "found invalid HWCB at address 0x%lx. List corrupted. "
--			   "Lost %i HWCBs with %i characters within up to %i "
--			   "messages. Saved %i HWCB with last %i characters i"
--				       "within up to %i messages.\n",
--				       (unsigned long) bad_addr,
--				       lost_hwcb, lost_char, lost_msg,
--				       hwc_data.hwcb_count,
--				       ALL_HWCB_CHAR, ALL_HWCB_MTO);
--	}
--	return 0;
--}
--
--static int 
--reuse_write_hwcb (void)
--{
--	int retval;
--
--	if (hwc_data.hwcb_count < 2)
--#ifdef DUMP_HWC_WRITE_LIST_ERROR
--		__asm__ ("LHI 1,0xe31\n\t"
--			 "LRA 2,0(%0)\n\t"
--			 "LRA 3,0(%1)\n\t"
--			 "J .+0 \n\t"
--	      :
--	      :	 "a" (BUF_HWCB), "a" (OUT_HWCB)
--	      :	 "1", "2", "3");
--#else
--		return -EPERM;
--#endif
--
--	if (hwc_data.current_hwcb == OUT_HWCB) {
--
--		if (hwc_data.hwcb_count > 2) {
--
--			BUF_HWCB_NEXT = OUT_HWCB_NEXT;
--
--			BUF_HWCB = OUT_HWCB_NEXT;
--
--			OUT_HWCB_NEXT = BUF_HWCB_NEXT;
--
--			BUF_HWCB_NEXT = NULL;
--		}
--	} else {
--
--		BUF_HWCB_NEXT = OUT_HWCB;
--
--		BUF_HWCB = OUT_HWCB;
--
--		OUT_HWCB = OUT_HWCB_NEXT;
--
--		BUF_HWCB_NEXT = NULL;
--	}
--
--	BUF_HWCB_TIMES_LOST += 1;
--	BUF_HWCB_CHAR_LOST += BUF_HWCB_CHAR;
--	BUF_HWCB_MTO_LOST += BUF_HWCB_MTO;
--	ALL_HWCB_MTO -= BUF_HWCB_MTO;
--	ALL_HWCB_CHAR -= BUF_HWCB_CHAR;
--
--	retval = prepare_write_hwcb ();
--
--	if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "reached my own limit of "
--			    "allowed buffer space for output (%i HWCBs = %li "
--			  "bytes), skipped content of oldest HWCB %i time(s) "
--				       "(%i lines = %i characters)\n",
--				       hwc_data.ioctls.max_hwcb,
--				       hwc_data.ioctls.max_hwcb * PAGE_SIZE,
--				       BUF_HWCB_TIMES_LOST,
--				       BUF_HWCB_MTO_LOST,
--				       BUF_HWCB_CHAR_LOST);
--	else
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "page allocation failed, "
--			   "could not expand buffer for output (currently in "
--			     "use: %i HWCBs = %li bytes), skipped content of "
--			"oldest HWCB %i time(s) (%i lines = %i characters)\n",
--				       hwc_data.hwcb_count,
--				       hwc_data.hwcb_count * PAGE_SIZE,
--				       BUF_HWCB_TIMES_LOST,
--				       BUF_HWCB_MTO_LOST,
--				       BUF_HWCB_CHAR_LOST);
--
--	return retval;
--}
--
--static int 
--allocate_write_hwcb (void)
--{
--	unsigned char *page;
--	int page_nr;
--
--	if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
--		return -ENOMEM;
--
--	page_nr = find_first_zero_bit (&hwc_data.kmem_pages, MAX_KMEM_PAGES);
--	if (page_nr < hwc_data.ioctls.kmem_hwcb) {
--
--		page = (unsigned char *)
--		    (hwc_data.kmem_start + (page_nr << 12));
--		set_bit (page_nr, &hwc_data.kmem_pages);
--	} else
--		page = (unsigned char *) __get_free_page (GFP_ATOMIC | GFP_DMA);
--
--	if (!page)
--		return -ENOMEM;
--
--	if (!OUT_HWCB)
--		OUT_HWCB = page;
--	else
--		BUF_HWCB_NEXT = page;
--
--	BUF_HWCB = page;
--
--	BUF_HWCB_NEXT = NULL;
--
--	hwc_data.hwcb_count++;
--
--	prepare_write_hwcb ();
--
--	BUF_HWCB_TIMES_LOST = 0;
--	BUF_HWCB_MTO_LOST = 0;
--	BUF_HWCB_CHAR_LOST = 0;
--
--#ifdef BUFFER_STRESS_TEST
--
--	internal_print (
--			       DELAYED_WRITE,
--			       "*** " HWC_RW_PRINT_HEADER
--			    "page #%i at 0x%x for buffering allocated. ***\n",
--			       hwc_data.hwcb_count, page);
--
--#endif
--
--	return 0;
--}
--
--static int 
--release_write_hwcb (void)
--{
--	unsigned long page;
--	int page_nr;
--
--	if (!hwc_data.hwcb_count)
--		return -ENODATA;
--
--	if (hwc_data.hwcb_count == 1) {
--
--		prepare_write_hwcb ();
--
--		ALL_HWCB_CHAR = 0;
--		ALL_HWCB_MTO = 0;
--		BUF_HWCB_TIMES_LOST = 0;
--		BUF_HWCB_MTO_LOST = 0;
--		BUF_HWCB_CHAR_LOST = 0;
--	} else {
--		page = (unsigned long) OUT_HWCB;
--
--		ALL_HWCB_MTO -= OUT_HWCB_MTO;
--		ALL_HWCB_CHAR -= OUT_HWCB_CHAR;
--		hwc_data.hwcb_count--;
--
--		OUT_HWCB = OUT_HWCB_NEXT;
--
--		if (page >= hwc_data.kmem_start &&
--		    page <= hwc_data.kmem_end) {
--			/*memset((void *) page, 0, PAGE_SIZE); */
--
--			page_nr = (int) ((page - hwc_data.kmem_start) >> 12);
--			clear_bit (page_nr, &hwc_data.kmem_pages);
--		} else
--			free_page (page);
--#ifdef BUFFER_STRESS_TEST
--
--		internal_print (
--				       DELAYED_WRITE,
--				       "*** " HWC_RW_PRINT_HEADER
--			 "page at 0x%x released, %i pages still in use ***\n",
--				       page, hwc_data.hwcb_count);
--
--#endif
--	}
--	return 0;
--}
--
--static int 
--add_mto (
--		unsigned char *message,
--		unsigned short int count)
--{
--	unsigned short int mto_size;
--	write_hwcb_t *hwcb;
--	mto_t *mto;
--	void *dest;
--
--	if (!BUF_HWCB)
--		return -ENOMEM;
--
--	if (BUF_HWCB == hwc_data.current_hwcb)
--		return -ENOMEM;
--
--	mto_size = sizeof (mto_t) + count;
--
--	hwcb = (write_hwcb_t *) BUF_HWCB;
--
--	if ((MAX_HWCB_ROOM - hwcb->length) < mto_size)
--		return -ENOMEM;
--
--	mto = (mto_t *) (((unsigned long) hwcb) + hwcb->length);
--
--	memcpy (mto, &mto_template, sizeof (mto_t));
--
--	dest = (void *) (((unsigned long) mto) + sizeof (mto_t));
--
--	memcpy (dest, message, count);
--
--	mto->length += count;
--
--	hwcb->length += mto_size;
--	hwcb->msgbuf.length += mto_size;
--	hwcb->msgbuf.mdb.length += mto_size;
--
--	BUF_HWCB_MTO++;
--	ALL_HWCB_MTO++;
--	BUF_HWCB_CHAR += count;
--	ALL_HWCB_CHAR += count;
--
--	return count;
--}
--
--static int write_event_data_1 (void);
--
--static void 
--do_poll_hwc (unsigned long data)
--{
--	unsigned long flags;
--
--	spin_lock_irqsave (&hwc_data.lock, flags);
--
--	write_event_data_1 ();
--
--	spin_unlock_irqrestore (&hwc_data.lock, flags);
--}
--
--void 
--start_poll_hwc (void)
--{
--	init_timer (&hwc_data.poll_timer);
--	hwc_data.poll_timer.function = do_poll_hwc;
--	hwc_data.poll_timer.data = (unsigned long) NULL;
--	hwc_data.poll_timer.expires = jiffies + 2 * HZ;
--	add_timer (&hwc_data.poll_timer);
--	hwc_data.flags |= HWC_PTIMER_RUNS;
--}
--
--static int 
--write_event_data_1 (void)
--{
--	unsigned short int condition_code;
--	int retval;
--	write_hwcb_t *hwcb = (write_hwcb_t *) OUT_HWCB;
--
--	if ((!hwc_data.write_prio) &&
--	    (!hwc_data.write_nonprio) &&
--	    hwc_data.read_statechange)
--		return -EOPNOTSUPP;
--
--	if (hwc_data.current_servc)
--		return -EBUSY;
--
--	retval = sane_write_hwcb ();
--	if (retval < 0)
--		return -EIO;
--
--	if (!OUT_HWCB_MTO)
--		return -ENODATA;
--
--	if (!hwc_data.write_nonprio && hwc_data.write_prio)
--		hwcb->msgbuf.type = ET_PMsgCmd;
--	else
--		hwcb->msgbuf.type = ET_Msg;
--
--	condition_code = service_call (HWC_CMDW_WRITEDATA, OUT_HWCB);
--
--#ifdef DUMP_HWC_WRITE_ERROR
--	if (condition_code != HWC_COMMAND_INITIATED)
--		__asm__ ("LHI 1,0xe20\n\t"
--			 "L 2,0(%0)\n\t"
--			 "LRA 3,0(%1)\n\t"
--			 "J .+0 \n\t"
--	      :
--	      :	 "a" (&condition_code), "a" (OUT_HWCB)
--	      :	 "1", "2", "3");
--#endif
--
--	switch (condition_code) {
--	case HWC_COMMAND_INITIATED:
--		hwc_data.current_servc = HWC_CMDW_WRITEDATA;
--		hwc_data.current_hwcb = OUT_HWCB;
--		retval = condition_code;
--		break;
--	case HWC_BUSY:
--		retval = -EBUSY;
--		break;
--	case HWC_NOT_OPERATIONAL:
--		start_poll_hwc ();
--	default:
--		retval = -EIO;
--	}
--
--	return retval;
--}
--
--static void 
--flush_hwcbs (void)
--{
--	while (hwc_data.hwcb_count > 1)
--		release_write_hwcb ();
--
--	release_write_hwcb ();
--
--	hwc_data.flags &= ~HWC_FLUSH;
--}
--
--static int 
--write_event_data_2 (u32 ext_int_param)
--{
--	write_hwcb_t *hwcb;
--	int retval = 0;
--
--#ifdef DUMP_HWC_WRITE_ERROR
--	if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR)
--	    != (unsigned long) hwc_data.current_hwcb) {
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "write_event_data_2 : "
--				       "HWCB address does not fit "
--				       "(expected: 0x%lx, got: 0x%lx).\n",
--				       (unsigned long) hwc_data.current_hwcb,
--				       ext_int_param);
--		return -EINVAL;
--	}
--#endif
--
--	hwcb = (write_hwcb_t *) OUT_HWCB;
--
--#ifdef DUMP_HWC_WRITE_LIST_ERROR
--	if (((unsigned char *) hwcb) != hwc_data.current_hwcb) {
--		__asm__ ("LHI 1,0xe22\n\t"
--			 "LRA 2,0(%0)\n\t"
--			 "LRA 3,0(%1)\n\t"
--			 "LRA 4,0(%2)\n\t"
--			 "LRA 5,0(%3)\n\t"
--			 "J .+0 \n\t"
--	      :
--	      :	 "a" (OUT_HWCB),
--			 "a" (hwc_data.current_hwcb),
--			 "a" (BUF_HWCB),
--			 "a" (hwcb)
--	      :	 "1", "2", "3", "4", "5");
--	}
--#endif
--
--#ifdef DUMP_HWC_WRITE_ERROR
--	if (hwcb->response_code != 0x0020) {
--		__asm__ ("LHI 1,0xe21\n\t"
--			 "LRA 2,0(%0)\n\t"
--			 "LRA 3,0(%1)\n\t"
--			 "LRA 4,0(%2)\n\t"
--			 "LH 5,0(%3)\n\t"
--			 "SRL 5,8\n\t"
--			 "J .+0 \n\t"
--	      :
--	      :	 "a" (OUT_HWCB), "a" (hwc_data.current_hwcb),
--			 "a" (BUF_HWCB),
--			 "a" (&(hwc_data.hwcb_count))
--	      :	 "1", "2", "3", "4", "5");
--	}
--#endif
--
--	switch (hwcb->response_code) {
--	case 0x0020:
--
--		retval = OUT_HWCB_CHAR;
--		release_write_hwcb ();
--		break;
--	case 0x0040:
--	case 0x0340:
--	case 0x40F0:
--		if (!hwc_data.read_statechange) {
--			hwcb->response_code = 0;
--			start_poll_hwc ();
--		}
--		retval = -EIO;
--		break;
--	default:
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "write_event_data_2 : "
--				       "failed operation "
--				       "(response code: 0x%x "
--				       "HWCB address: 0x%x).\n",
--				       hwcb->response_code,
--				       hwcb);
--		retval = -EIO;
--	}
--
--	if (retval == -EIO) {
--
--		hwcb->control_mask[0] = 0;
--		hwcb->control_mask[1] = 0;
--		hwcb->control_mask[2] = 0;
--		hwcb->response_code = 0;
--	}
--	hwc_data.current_servc = 0;
--	hwc_data.current_hwcb = NULL;
--
--	if (hwc_data.flags & HWC_FLUSH)
--		flush_hwcbs ();
--
--	return retval;
--}
--
--static void 
--do_put_line (
--		    unsigned char *message,
--		    unsigned short count)
--{
--
--	if (add_mto (message, count) != count) {
--
--		if (allocate_write_hwcb () < 0)
--			reuse_write_hwcb ();
--
--#ifdef DUMP_HWC_WRITE_LIST_ERROR
--		if (add_mto (message, count) != count)
--			__asm__ ("LHI 1,0xe32\n\t"
--				 "LRA 2,0(%0)\n\t"
--				 "L 3,0(%1)\n\t"
--				 "LRA 4,0(%2)\n\t"
--				 "LRA 5,0(%3)\n\t"
--				 "J .+0 \n\t"
--		      :
--		      :	 "a" (message), "a" (&hwc_data.kmem_pages),
--				 "a" (BUF_HWCB), "a" (OUT_HWCB)
--		      :	 "1", "2", "3", "4", "5");
--#else
--		add_mto (message, count);
--#endif
--	}
--}
--
--static void 
--put_line (
--		 unsigned char *message,
--		 unsigned short count)
--{
--
--	if ((!hwc_data.obuf_start) && (hwc_data.flags & HWC_WTIMER_RUNS)) {
--		del_timer (&hwc_data.write_timer);
--		hwc_data.flags &= ~HWC_WTIMER_RUNS;
--	}
--	hwc_data.obuf_start += count;
--
--	do_put_line (message, count);
--
--	hwc_data.obuf_start -= count;
--}
--
--static void 
--set_alarm (void)
--{
--	write_hwcb_t *hwcb;
--
--	if ((!BUF_HWCB) || (BUF_HWCB == hwc_data.current_hwcb))
--		allocate_write_hwcb ();
--
--	hwcb = (write_hwcb_t *) BUF_HWCB;
--	hwcb->msgbuf.mdb.mdb_body.go.general_msg_flags |= GMF_SndAlrm;
--}
--
--static void 
--hwc_write_timeout (unsigned long data)
--{
--	unsigned long flags;
--
--	spin_lock_irqsave (&hwc_data.lock, flags);
--
--	hwc_data.obuf_start = hwc_data.obuf_count;
--	if (hwc_data.obuf_count)
--		put_line (hwc_data.obuf, hwc_data.obuf_count);
--	hwc_data.obuf_start = 0;
--
--	hwc_data.obuf_cursor = 0;
--	hwc_data.obuf_count = 0;
--
--	write_event_data_1 ();
--
--	spin_unlock_irqrestore (&hwc_data.lock, flags);
--}
--
--static int 
--do_hwc_write (
--		     int from_user,
--		     unsigned char *msg,
--		     unsigned int count,
--		     unsigned char write_time)
--{
--	unsigned int i_msg = 0;
--	unsigned short int spaces = 0;
--	unsigned int processed_characters = 0;
--	unsigned char ch;
--	unsigned short int obuf_count;
--	unsigned short int obuf_cursor;
--	unsigned short int obuf_columns;
--
--	if (hwc_data.obuf_start) {
--		obuf_cursor = 0;
--		obuf_count = 0;
--		obuf_columns = MIN (hwc_data.ioctls.columns,
--				    MAX_MESSAGE_SIZE - hwc_data.obuf_start);
--	} else {
--		obuf_cursor = hwc_data.obuf_cursor;
--		obuf_count = hwc_data.obuf_count;
--		obuf_columns = hwc_data.ioctls.columns;
--	}
--
--	for (i_msg = 0; i_msg < count; i_msg++) {
--		if (from_user)
--			get_user (ch, msg + i_msg);
--		else
--			ch = msg[i_msg];
--
--		processed_characters++;
--
--		if ((obuf_cursor == obuf_columns) &&
--
--		    (ch != '\n') &&
--
--		    (ch != '\t')) {
--			put_line (&hwc_data.obuf[hwc_data.obuf_start],
--				  obuf_columns);
--			obuf_cursor = 0;
--			obuf_count = 0;
--		}
--		switch (ch) {
--
--		case '\n':
--
--			put_line (&hwc_data.obuf[hwc_data.obuf_start],
--				  obuf_count);
--			obuf_cursor = 0;
--			obuf_count = 0;
--			break;
--
--		case '\a':
--
--			hwc_data.obuf_start += obuf_count;
--			set_alarm ();
--			hwc_data.obuf_start -= obuf_count;
--
--			break;
--
--		case '\t':
--
--			do {
--				if (obuf_cursor < obuf_columns) {
--					hwc_data.obuf[hwc_data.obuf_start +
--						      obuf_cursor]
--					    = HWC_ASCEBC (' ');
--					obuf_cursor++;
--				} else
--					break;
--			} while (obuf_cursor % hwc_data.ioctls.width_htab);
--
--			break;
--
--		case '\f':
--		case '\v':
--
--			spaces = obuf_cursor;
--			put_line (&hwc_data.obuf[hwc_data.obuf_start],
--				  obuf_count);
--			obuf_count = obuf_cursor;
--			while (spaces) {
--				hwc_data.obuf[hwc_data.obuf_start +
--					      obuf_cursor - spaces]
--				    = HWC_ASCEBC (' ');
--				spaces--;
--			}
--
--			break;
--
--		case '\b':
--
--			if (obuf_cursor)
--				obuf_cursor--;
--			break;
--
--		case '\r':
--
--			obuf_cursor = 0;
--			break;
--
--		case 0x00:
--
--			put_line (&hwc_data.obuf[hwc_data.obuf_start],
--				  obuf_count);
--			obuf_cursor = 0;
--			obuf_count = 0;
--			goto out;
--
--		default:
--
--			if (isprint (ch))
--				hwc_data.obuf[hwc_data.obuf_start +
--					      obuf_cursor++]
--				    = HWC_ASCEBC (ch);
--		}
--		if (obuf_cursor > obuf_count)
--			obuf_count = obuf_cursor;
--	}
--
--	if (obuf_cursor) {
--
--		if (hwc_data.obuf_start ||
--		    (hwc_data.ioctls.final_nl == 0)) {
--
--			put_line (&hwc_data.obuf[hwc_data.obuf_start],
--				  obuf_count);
--			obuf_cursor = 0;
--			obuf_count = 0;
--		} else {
--
--			if (hwc_data.ioctls.final_nl > 0) {
--
--				if (hwc_data.flags & HWC_WTIMER_RUNS) {
--
--					mod_timer (&hwc_data.write_timer,
--						   jiffies + hwc_data.ioctls.final_nl * HZ / 10);
--				} else {
--
--					init_timer (&hwc_data.write_timer);
--					hwc_data.write_timer.function =
--					    hwc_write_timeout;
--					hwc_data.write_timer.data =
--					    (unsigned long) NULL;
--					hwc_data.write_timer.expires =
--					    jiffies +
--					    hwc_data.ioctls.final_nl * HZ / 10;
--					add_timer (&hwc_data.write_timer);
--					hwc_data.flags |= HWC_WTIMER_RUNS;
--				}
--			} else;
--
--		}
--	} else;
--
--      out:
--
--	if (!hwc_data.obuf_start) {
--		hwc_data.obuf_cursor = obuf_cursor;
--		hwc_data.obuf_count = obuf_count;
--	}
--	if (write_time == IMMEDIATE_WRITE)
--		write_event_data_1 ();
--
--	return processed_characters;
--}
--
--signed int 
--hwc_write (int from_user, const unsigned char *msg, unsigned int count)
--{
--	unsigned long flags;
--	int retval;
--
--	spin_lock_irqsave (&hwc_data.lock, flags);
--
--	retval = do_hwc_write (from_user, (unsigned char *) msg,
--			       count, IMMEDIATE_WRITE);
--
--	spin_unlock_irqrestore (&hwc_data.lock, flags);
--
--	return retval;
--}
--
--unsigned int 
--hwc_chars_in_buffer (unsigned char flag)
--{
--	unsigned short int number = 0;
--	unsigned long flags;
--
--	spin_lock_irqsave (&hwc_data.lock, flags);
--
--	if (flag & IN_HWCB)
--		number += ALL_HWCB_CHAR;
--
--	if (flag & IN_WRITE_BUF)
--		number += hwc_data.obuf_cursor;
--
--	spin_unlock_irqrestore (&hwc_data.lock, flags);
--
--	return number;
--}
--
--static inline int 
--nr_setbits (kmem_pages_t arg)
--{
--	int i;
--	int nr = 0;
--
--	for (i = 0; i < (sizeof (arg) << 3); i++) {
--		if (arg & 1)
--			nr++;
--		arg >>= 1;
--	}
--
--	return nr;
--}
--
--unsigned int 
--hwc_write_room (unsigned char flag)
--{
--	unsigned int number = 0;
--	unsigned long flags;
--	write_hwcb_t *hwcb;
--
--	spin_lock_irqsave (&hwc_data.lock, flags);
--
--	if (flag & IN_HWCB) {
--
--		if (BUF_HWCB) {
--			hwcb = (write_hwcb_t *) BUF_HWCB;
--			number += MAX_HWCB_ROOM - hwcb->length;
--		}
--		number += (hwc_data.ioctls.kmem_hwcb -
--			   nr_setbits (hwc_data.kmem_pages)) *
--		    (MAX_HWCB_ROOM -
--		     (sizeof (write_hwcb_t) + sizeof (mto_t)));
--	}
--	if (flag & IN_WRITE_BUF)
--		number += MAX_HWCB_ROOM - hwc_data.obuf_cursor;
--
--	spin_unlock_irqrestore (&hwc_data.lock, flags);
--
--	return number;
--}
--
--void 
--hwc_flush_buffer (unsigned char flag)
--{
--	unsigned long flags;
--
--	spin_lock_irqsave (&hwc_data.lock, flags);
--
--	if (flag & IN_HWCB) {
--		if (hwc_data.current_servc != HWC_CMDW_WRITEDATA)
--			flush_hwcbs ();
--		else
--			hwc_data.flags |= HWC_FLUSH;
--	}
--	if (flag & IN_WRITE_BUF) {
--		hwc_data.obuf_cursor = 0;
--		hwc_data.obuf_count = 0;
--	}
--	spin_unlock_irqrestore (&hwc_data.lock, flags);
--}
--
--unsigned short int 
--seperate_cases (unsigned char *buf, unsigned short int count)
--{
--
--	unsigned short int i_in;
--
--	unsigned short int i_out = 0;
--
--	unsigned char _case = 0;
--
--	for (i_in = 0; i_in < count; i_in++) {
--
--		if (buf[i_in] == hwc_data.ioctls.delim) {
--
--			if ((i_in + 1 < count) &&
--			    (buf[i_in + 1] == hwc_data.ioctls.delim)) {
--
--				buf[i_out] = hwc_data.ioctls.delim;
--
--				i_out++;
--
--				i_in++;
--
--			} else
--				_case = ~_case;
--
--		} else {
--
--			if (_case) {
--
--				if (hwc_data.ioctls.tolower)
--					buf[i_out] = _ebc_toupper[buf[i_in]];
--
--				else
--					buf[i_out] = _ebc_tolower[buf[i_in]];
--
--			} else
--				buf[i_out] = buf[i_in];
--
--			i_out++;
--		}
--	}
--
--	return i_out;
--}
--
--#ifdef DUMP_HWCB_INPUT
--
--static int 
--gds_vector_name (u16 id, unsigned char name[])
--{
--	int retval = 0;
--
--	switch (id) {
--	case GDS_ID_MDSMU:
--		name = "Multiple Domain Support Message Unit";
--		break;
--	case GDS_ID_MDSRouteInfo:
--		name = "MDS Routing Information";
--		break;
--	case GDS_ID_AgUnWrkCorr:
--		name = "Agent Unit of Work Correlator";
--		break;
--	case GDS_ID_SNACondReport:
--		name = "SNA Condition Report";
--		break;
--	case GDS_ID_CPMSU:
--		name = "CP Management Services Unit";
--		break;
--	case GDS_ID_RoutTargInstr:
--		name = "Routing and Targeting Instructions";
--		break;
--	case GDS_ID_OpReq:
--		name = "Operate Request";
--		break;
--	case GDS_ID_TextCmd:
--		name = "Text Command";
--		break;
--
--	default:
--		name = "unknown GDS variable";
--		retval = -EINVAL;
--	}
--
--	return retval;
--}
--#endif
--
--inline static gds_vector_t *
--find_gds_vector (
--			gds_vector_t * start, void *end, u16 id)
--{
--	gds_vector_t *vec;
--	gds_vector_t *retval = NULL;
--
--	vec = start;
--
--	while (((void *) vec) < end) {
--		if (vec->gds_id == id) {
--
--#ifdef DUMP_HWCB_INPUT
--			int retval_name;
--			unsigned char name[64];
--
--			retval_name = gds_vector_name (id, name);
--			internal_print (
--					       DELAYED_WRITE,
--					       HWC_RW_PRINT_HEADER
--					  "%s at 0x%x up to 0x%x, length: %d",
--					       name,
--					       (unsigned long) vec,
--				      ((unsigned long) vec) + vec->length - 1,
--					       vec->length);
--			if (retval_name < 0)
--				internal_print (
--						       IMMEDIATE_WRITE,
--						       ", id: 0x%x\n",
--						       vec->gds_id);
--			else
--				internal_print (
--						       IMMEDIATE_WRITE,
--						       "\n");
--#endif
--
--			retval = vec;
--			break;
--		}
--		vec = (gds_vector_t *) (((unsigned long) vec) + vec->length);
--	}
--
--	return retval;
--}
--
--inline static gds_subvector_t *
--find_gds_subvector (
--			   gds_subvector_t * start, void *end, u8 key)
--{
--	gds_subvector_t *subvec;
--	gds_subvector_t *retval = NULL;
--
--	subvec = start;
--
--	while (((void *) subvec) < end) {
--		if (subvec->key == key) {
--			retval = subvec;
--			break;
--		}
--		subvec = (gds_subvector_t *)
--		    (((unsigned long) subvec) + subvec->length);
--	}
--
--	return retval;
--}
--
--inline static int 
--get_input (void *start, void *end)
--{
--	int count;
--
--	count = ((unsigned long) end) - ((unsigned long) start);
--
--	if (hwc_data.ioctls.tolower)
--		EBC_TOLOWER (start, count);
--
--	if (hwc_data.ioctls.delim)
--		count = seperate_cases (start, count);
--
--	HWC_EBCASC_STR (start, count);
--
--	if (hwc_data.ioctls.echo)
--		do_hwc_write (0, start, count, IMMEDIATE_WRITE);
--
--	if (hwc_data.calls != NULL)
--		if (hwc_data.calls->move_input != NULL)
--			(hwc_data.calls->move_input) (start, count);
--
--	return count;
--}
--
--inline static int 
--eval_selfdeftextmsg (gds_subvector_t * start, void *end)
--{
--	gds_subvector_t *subvec;
--	void *subvec_data;
--	void *subvec_end;
--	int retval = 0;
--
--	subvec = start;
--
--	while (((void *) subvec) < end) {
--		subvec = find_gds_subvector (subvec, end, 0x30);
--		if (!subvec)
--			break;
--		subvec_data = (void *)
--		    (((unsigned long) subvec) +
--		     sizeof (gds_subvector_t));
--		subvec_end = (void *)
--		    (((unsigned long) subvec) + subvec->length);
--		retval += get_input (subvec_data, subvec_end);
--		subvec = (gds_subvector_t *) subvec_end;
--	}
--
--	return retval;
--}
--
--inline static int 
--eval_textcmd (gds_subvector_t * start, void *end)
--{
--	gds_subvector_t *subvec;
--	gds_subvector_t *subvec_data;
--	void *subvec_end;
--	int retval = 0;
--
--	subvec = start;
--
--	while (((void *) subvec) < end) {
--		subvec = find_gds_subvector (
--					 subvec, end, GDS_KEY_SelfDefTextMsg);
--		if (!subvec)
--			break;
--		subvec_data = (gds_subvector_t *)
--		    (((unsigned long) subvec) +
--		     sizeof (gds_subvector_t));
--		subvec_end = (void *)
--		    (((unsigned long) subvec) + subvec->length);
--		retval += eval_selfdeftextmsg (subvec_data, subvec_end);
--		subvec = (gds_subvector_t *) subvec_end;
--	}
--
--	return retval;
--}
--
--inline static int 
--eval_cpmsu (gds_vector_t * start, void *end)
--{
--	gds_vector_t *vec;
--	gds_subvector_t *vec_data;
--	void *vec_end;
--	int retval = 0;
--
--	vec = start;
--
--	while (((void *) vec) < end) {
--		vec = find_gds_vector (vec, end, GDS_ID_TextCmd);
--		if (!vec)
--			break;
--		vec_data = (gds_subvector_t *)
--		    (((unsigned long) vec) + sizeof (gds_vector_t));
--		vec_end = (void *) (((unsigned long) vec) + vec->length);
--		retval += eval_textcmd (vec_data, vec_end);
--		vec = (gds_vector_t *) vec_end;
--	}
--
--	return retval;
--}
--
--inline static int 
--eval_mdsmu (gds_vector_t * start, void *end)
--{
--	gds_vector_t *vec;
--	gds_vector_t *vec_data;
--	void *vec_end;
--	int retval = 0;
--
--	vec = find_gds_vector (start, end, GDS_ID_CPMSU);
--	if (vec) {
--		vec_data = (gds_vector_t *)
--		    (((unsigned long) vec) + sizeof (gds_vector_t));
--		vec_end = (void *) (((unsigned long) vec) + vec->length);
--		retval = eval_cpmsu (vec_data, vec_end);
--	}
--	return retval;
--}
--
--static int 
--eval_evbuf (gds_vector_t * start, void *end)
--{
--	gds_vector_t *vec;
--	gds_vector_t *vec_data;
--	void *vec_end;
--	int retval = 0;
--
--	vec = find_gds_vector (start, end, GDS_ID_MDSMU);
--	if (vec) {
--		vec_data = (gds_vector_t *)
--		    (((unsigned long) vec) + sizeof (gds_vector_t));
--		vec_end = (void *) (((unsigned long) vec) + vec->length);
--		retval = eval_mdsmu (vec_data, vec_end);
--	}
--	return retval;
--}
--
--static inline int 
--eval_hwc_receive_mask (_hwcb_mask_t mask)
--{
--
--	hwc_data.write_nonprio
--	    = ((mask & ET_Msg_Mask) == ET_Msg_Mask);
--
--	hwc_data.write_prio
--	    = ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask);
--
--	if (hwc_data.write_prio || hwc_data.write_nonprio) {
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "can write messages\n");
--		return 0;
--	} else {
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "can not write messages\n");
--		return -1;
--	}
--}
--
--static inline int 
--eval_hwc_send_mask (_hwcb_mask_t mask)
--{
--
--	hwc_data.read_statechange
--	    = ((mask & ET_StateChange_Mask) == ET_StateChange_Mask);
--	if (hwc_data.read_statechange)
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				     "can read state change notifications\n");
--	else
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				 "can not read state change notifications\n");
--
--	hwc_data.sig_quiesce
--	    = ((mask & ET_SigQuiesce_Mask) == ET_SigQuiesce_Mask);
--	if (hwc_data.sig_quiesce)
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "can receive signal quiesce\n");
--	else
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "can not receive signal quiesce\n");
--
--	hwc_data.read_nonprio
--	    = ((mask & ET_OpCmd_Mask) == ET_OpCmd_Mask);
--	if (hwc_data.read_nonprio)
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "can read commands\n");
--
--	hwc_data.read_prio
--	    = ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask);
--	if (hwc_data.read_prio)
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "can read priority commands\n");
--
--	if (hwc_data.read_prio || hwc_data.read_nonprio) {
--		return 0;
--	} else {
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--				     "can not read commands from operator\n");
--		return -1;
--	}
--}
--
--static int 
--eval_statechangebuf (statechangebuf_t * scbuf)
--{
--	int retval = 0;
--
--	internal_print (
--			       DELAYED_WRITE,
--			       HWC_RW_PRINT_HEADER
--			       "HWC state change detected\n");
--
--	if (scbuf->validity_hwc_active_facility_mask) {
--
--	}
--	if (scbuf->validity_hwc_receive_mask) {
--
--		if (scbuf->mask_length != 4) {
--#ifdef DUMP_HWC_INIT_ERROR
--			__asm__ ("LHI 1,0xe50\n\t"
--				 "LRA 2,0(%0)\n\t"
--				 "J .+0 \n\t"
--		      :
--		      :	 "a" (scbuf)
--		      :	 "1", "2");
--#endif
--		} else {
--
--			retval += eval_hwc_receive_mask
--			    (scbuf->hwc_receive_mask);
--		}
--	}
--	if (scbuf->validity_hwc_send_mask) {
--
--		if (scbuf->mask_length != 4) {
--#ifdef DUMP_HWC_INIT_ERROR
--			__asm__ ("LHI 1,0xe51\n\t"
--				 "LRA 2,0(%0)\n\t"
--				 "J .+0 \n\t"
--		      :
--		      :	 "a" (scbuf)
--		      :	 "1", "2");
--#endif
--		} else {
--
--			retval += eval_hwc_send_mask
--			    (scbuf->hwc_send_mask);
--		}
--	}
--	if (scbuf->validity_read_data_function_mask) {
--
--	}
--	return retval;
--}
--
--#ifdef CONFIG_SMP
--extern unsigned long cpu_online_map;
--static volatile unsigned long cpu_quiesce_map;
--
--static void 
--do_load_quiesce_psw (void)
--{
--	psw_t quiesce_psw;
--
--	clear_bit (smp_processor_id (), &cpu_quiesce_map);
--	if (smp_processor_id () == 0) {
--
--		while (cpu_quiesce_map != 0) ;
--
--		quiesce_psw.mask = _DW_PSW_MASK;
--		quiesce_psw.addr = 0xfff;
--		__load_psw (quiesce_psw);
--	}
--	signal_processor (smp_processor_id (), sigp_stop);
--}
--
--static void 
--do_machine_quiesce (void)
--{
--	cpu_quiesce_map = cpu_online_map;
--	smp_call_function (do_load_quiesce_psw, NULL, 0, 0);
--	do_load_quiesce_psw ();
--}
--
--#else
--static void 
--do_machine_quiesce (void)
--{
--	psw_t quiesce_psw;
--
--	quiesce_psw.mask = _DW_PSW_MASK;
--	queisce_psw.addr = 0xfff;
--	__load_psw (quiesce_psw);
--}
--
--#endif
--
--static int 
--process_evbufs (void *start, void *end)
--{
--	int retval = 0;
--	evbuf_t *evbuf;
--	void *evbuf_end;
--	gds_vector_t *evbuf_data;
--
--	evbuf = (evbuf_t *) start;
--	while (((void *) evbuf) < end) {
--		evbuf_data = (gds_vector_t *)
--		    (((unsigned long) evbuf) + sizeof (evbuf_t));
--		evbuf_end = (void *) (((unsigned long) evbuf) + evbuf->length);
--		switch (evbuf->type) {
--		case ET_OpCmd:
--		case ET_CntlProgOpCmd:
--		case ET_PMsgCmd:
--#ifdef DUMP_HWCB_INPUT
--
--			internal_print (
--					       DELAYED_WRITE,
--					       HWC_RW_PRINT_HEADER
--					       "event buffer "
--					   "at 0x%x up to 0x%x, length: %d\n",
--					       (unsigned long) evbuf,
--					       (unsigned long) (evbuf_end - 1),
--					       evbuf->length);
--			dump_storage_area ((void *) evbuf, evbuf->length);
--#endif
--			retval += eval_evbuf (evbuf_data, evbuf_end);
--			break;
--		case ET_StateChange:
--			retval += eval_statechangebuf
--			    ((statechangebuf_t *) evbuf);
--			break;
--		case ET_SigQuiesce:
--
--			_machine_restart = do_machine_quiesce;
--			_machine_halt = do_machine_quiesce;
--			_machine_power_off = do_machine_quiesce;
--			ctrl_alt_del ();
--			break;
--		default:
--			internal_print (
--					       DELAYED_WRITE,
--					       HWC_RW_PRINT_HEADER
--					       "unconditional read: "
--					       "unknown event buffer found, "
--					       "type 0x%x",
--					       evbuf->type);
--			retval = -ENOSYS;
--		}
--		evbuf = (evbuf_t *) evbuf_end;
--	}
--	return retval;
--}
--
--static int 
--unconditional_read_1 (void)
--{
--	unsigned short int condition_code;
--	read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
--	int retval;
--
--#if 0
--
--	if ((!hwc_data.read_prio) && (!hwc_data.read_nonprio))
--		return -EOPNOTSUPP;
--
--	if (hwc_data.current_servc)
--		return -EBUSY;
--#endif
--
--	memset (hwcb, 0x00, PAGE_SIZE);
--	memcpy (hwcb, &read_hwcb_template, sizeof (read_hwcb_t));
--
--	condition_code = service_call (HWC_CMDW_READDATA, hwc_data.page);
--
--#ifdef DUMP_HWC_READ_ERROR
--	if (condition_code == HWC_NOT_OPERATIONAL)
--		__asm__ ("LHI 1,0xe40\n\t"
--			 "L 2,0(%0)\n\t"
--			 "LRA 3,0(%1)\n\t"
--			 "J .+0 \n\t"
--	      :
--	      :	 "a" (&condition_code), "a" (hwc_data.page)
--	      :	 "1", "2", "3");
--#endif
--
--	switch (condition_code) {
--	case HWC_COMMAND_INITIATED:
--		hwc_data.current_servc = HWC_CMDW_READDATA;
--		hwc_data.current_hwcb = hwc_data.page;
--		retval = condition_code;
--		break;
--	case HWC_BUSY:
--		retval = -EBUSY;
--		break;
--	default:
--		retval = -EIO;
--	}
--
--	return retval;
--}
--
--static int 
--unconditional_read_2 (u32 ext_int_param)
--{
--	read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
--
--#ifdef DUMP_HWC_READ_ERROR
--	if ((hwcb->response_code != 0x0020) &&
--	    (hwcb->response_code != 0x0220) &&
--	    (hwcb->response_code != 0x60F0) &&
--	    (hwcb->response_code != 0x62F0))
--		__asm__ ("LHI 1,0xe41\n\t"
--			 "LRA 2,0(%0)\n\t"
--			 "L 3,0(%1)\n\t"
--			 "J .+0\n\t"
--	      :
--	      :	 "a" (hwc_data.page), "a" (&(hwcb->response_code))
--	      :	 "1", "2", "3");
--#endif
--
--	hwc_data.current_servc = 0;
--	hwc_data.current_hwcb = NULL;
--
--	switch (hwcb->response_code) {
--
--	case 0x0020:
--	case 0x0220:
--		return process_evbufs (
--		     (void *) (((unsigned long) hwcb) + sizeof (read_hwcb_t)),
--			    (void *) (((unsigned long) hwcb) + hwcb->length));
--
--	case 0x60F0:
--	case 0x62F0:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "unconditional read: "
--				     "got interrupt and tried to read input, "
--				  "but nothing found (response code=0x%x).\n",
--				       hwcb->response_code);
--		return 0;
--
--	case 0x0100:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--			 "unconditional read: HWCB boundary violation - this "
--			 "must not occur in a correct driver, please contact "
--				       "author\n");
--		return -EIO;
--
--	case 0x0300:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "unconditional read: "
--			"insufficient HWCB length - this must not occur in a "
--				   "correct driver, please contact author\n");
--		return -EIO;
--
--	case 0x01F0:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--				       "unconditional read: "
--			 "invalid command - this must not occur in a correct "
--				       "driver, please contact author\n");
--		return -EIO;
--
--	case 0x40F0:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--			       "unconditional read: invalid function code\n");
--		return -EIO;
--
--	case 0x70F0:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--			      "unconditional read: invalid selection mask\n");
--		return -EIO;
--
--	case 0x0040:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--				 "unconditional read: HWC equipment check\n");
--		return -EIO;
--
--	default:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--			"unconditional read: invalid response code %x - this "
--			 "must not occur in a correct driver, please contact "
--				       "author\n",
--				       hwcb->response_code);
--		return -EIO;
--	}
--}
--
--static int 
--write_event_mask_1 (void)
--{
--	unsigned int condition_code;
--	int retval;
--
--	condition_code = service_call (HWC_CMDW_WRITEMASK, hwc_data.page);
--
--#ifdef DUMP_HWC_INIT_ERROR
--
--	if (condition_code == HWC_NOT_OPERATIONAL)
--		__asm__ ("LHI 1,0xe10\n\t"
--			 "L 2,0(%0)\n\t"
--			 "LRA 3,0(%1)\n\t"
--			 "J .+0\n\t"
--	      :
--	      :	 "a" (&condition_code), "a" (hwc_data.page)
--	      :	 "1", "2", "3");
--#endif
--
--	switch (condition_code) {
--	case HWC_COMMAND_INITIATED:
--		hwc_data.current_servc = HWC_CMDW_WRITEMASK;
--		hwc_data.current_hwcb = hwc_data.page;
--		retval = condition_code;
--		break;
--	case HWC_BUSY:
--		retval = -EBUSY;
--		break;
--	default:
--		retval = -EIO;
--	}
--
--	return retval;
--}
--
--static int 
--write_event_mask_2 (u32 ext_int_param)
--{
--	init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page;
--	int retval = 0;
--
--	if (hwcb->response_code != 0x0020) {
--#ifdef DUMP_HWC_INIT_ERROR
--		__asm__ ("LHI 1,0xe11\n\t"
--			 "LRA 2,0(%0)\n\t"
--			 "L 3,0(%1)\n\t"
--			 "J .+0\n\t"
--	      :
--	      :	 "a" (hwcb), "a" (&(hwcb->response_code))
--	      :	 "1", "2", "3");
--#else
--		retval = -1;
--#endif
--	} else {
--		if (hwcb->mask_length != 4) {
--#ifdef DUMP_HWC_INIT_ERROR
--			__asm__ ("LHI 1,0xe52\n\t"
--				 "LRA 2,0(%0)\n\t"
--				 "J .+0 \n\t"
--		      :
--		      :	 "a" (hwcb)
--		      :	 "1", "2");
--#endif
--		} else {
--			retval += eval_hwc_receive_mask
--			    (hwcb->hwc_receive_mask);
--			retval += eval_hwc_send_mask (hwcb->hwc_send_mask);
--		}
--	}
--
--	hwc_data.current_servc = 0;
--	hwc_data.current_hwcb = NULL;
--
--	return retval;
--}
--
--static int 
--set_hwc_ioctls (hwc_ioctls_t * ioctls, char correct)
--{
--	int retval = 0;
--	hwc_ioctls_t tmp;
--
--	if (ioctls->width_htab > MAX_MESSAGE_SIZE) {
--		if (correct)
--			tmp.width_htab = MAX_MESSAGE_SIZE;
--		else
--			retval = -EINVAL;
--	} else
--		tmp.width_htab = ioctls->width_htab;
--
--	tmp.echo = ioctls->echo;
--
--	if (ioctls->columns > MAX_MESSAGE_SIZE) {
--		if (correct)
--			tmp.columns = MAX_MESSAGE_SIZE;
--		else
--			retval = -EINVAL;
--	} else
--		tmp.columns = ioctls->columns;
--
--	tmp.final_nl = ioctls->final_nl;
--
--	if (ioctls->max_hwcb < 2) {
--		if (correct)
--			tmp.max_hwcb = 2;
--		else
--			retval = -EINVAL;
--	} else
--		tmp.max_hwcb = ioctls->max_hwcb;
--
--	tmp.tolower = ioctls->tolower;
--
--	if (ioctls->kmem_hwcb > ioctls->max_hwcb) {
--		if (correct)
--			tmp.kmem_hwcb = ioctls->max_hwcb;
--		else
--			retval = -EINVAL;
--	} else
--		tmp.kmem_hwcb = ioctls->kmem_hwcb;
--
--	if (ioctls->kmem_hwcb > MAX_KMEM_PAGES) {
--		if (correct)
--			ioctls->kmem_hwcb = MAX_KMEM_PAGES;
--		else
--			retval = -EINVAL;
--	}
--	if (ioctls->kmem_hwcb < 2) {
--		if (correct)
--			ioctls->kmem_hwcb = 2;
--		else
--			retval = -EINVAL;
--	}
--	tmp.delim = ioctls->delim;
--
--	if (!(retval < 0))
--		hwc_data.ioctls = tmp;
--
--	return retval;
--}
--
--int 
--do_hwc_init (void)
--{
--	int retval;
--
--	memcpy (hwc_data.page, &init_hwcb_template, sizeof (init_hwcb_t));
--
--	do {
--
--		retval = write_event_mask_1 ();
--
--		if (retval == -EBUSY) {
--
--			hwc_data.flags |= HWC_INIT;
--
--			__ctl_store (cr0, 0, 0);
--			cr0_save = cr0;
--			cr0 |= 0x00000200;
--			cr0 &= 0xFFFFF3AC;
--			__ctl_load (cr0, 0, 0);
--
--			asm volatile ("STOSM %0,0x01"
--				      :"=m" (psw_mask)::"memory");
--
--			while (!(hwc_data.flags & HWC_INTERRUPT))
--				barrier ();
--
--			asm volatile ("STNSM %0,0xFE"
--				      :"=m" (psw_mask)::"memory");
--
--			__ctl_load (cr0_save, 0, 0);
--
--			hwc_data.flags &= ~HWC_INIT;
--		}
--	} while (retval == -EBUSY);
--
--	if (retval == -EIO) {
--		hwc_data.flags |= HWC_BROKEN;
--		printk (HWC_RW_PRINT_HEADER "HWC not operational\n");
--	}
--	return retval;
--}
--
--void hwc_interrupt_handler (struct pt_regs *regs, __u16 code);
--
--int 
--hwc_init (void)
--{
--	int retval;
--
--#ifdef BUFFER_STRESS_TEST
--
--	init_hwcb_t *hwcb;
--	int i;
--
--#endif
--
--	if (register_early_external_interrupt (0x2401, hwc_interrupt_handler,
--					       &ext_int_info_hwc) != 0)
--		panic ("Couldn't request external interrupts 0x2401");
--
--	spin_lock_init (&hwc_data.lock);
--
--#ifdef USE_VM_DETECTION
--
--	if (MACHINE_IS_VM) {
--
--		if (hwc_data.init_ioctls.columns > 76)
--			hwc_data.init_ioctls.columns = 76;
--		hwc_data.init_ioctls.tolower = 1;
--		if (!hwc_data.init_ioctls.delim)
--			hwc_data.init_ioctls.delim = DEFAULT_CASE_DELIMITER;
--	} else {
--		hwc_data.init_ioctls.tolower = 0;
--		hwc_data.init_ioctls.delim = 0;
--	}
--#endif
--	retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
--
--	hwc_data.kmem_start = (unsigned long)
--	    alloc_bootmem_low_pages (hwc_data.ioctls.kmem_hwcb * PAGE_SIZE);
--	hwc_data.kmem_end = hwc_data.kmem_start +
--	    hwc_data.ioctls.kmem_hwcb * PAGE_SIZE - 1;
--
--	retval = do_hwc_init ();
--
--	ctl_set_bit (0, 9);
--
--#ifdef BUFFER_STRESS_TEST
--
--	internal_print (
--			       DELAYED_WRITE,
--			       HWC_RW_PRINT_HEADER
--			       "use %i bytes for buffering.\n",
--			       hwc_data.ioctls.kmem_hwcb * PAGE_SIZE);
--	for (i = 0; i < 500; i++) {
--		hwcb = (init_hwcb_t *) BUF_HWCB;
--		internal_print (
--				       DELAYED_WRITE,
--				       HWC_RW_PRINT_HEADER
--			  "This is stress test message #%i, free: %i bytes\n",
--				       i,
--			     MAX_HWCB_ROOM - (hwcb->length + sizeof (mto_t)));
--	}
--
--#endif
--
--	return /*retval */ 0;
--}
--
--signed int 
--hwc_register_calls (hwc_high_level_calls_t * calls)
--{
--	if (calls == NULL)
--		return -EINVAL;
--
--	if (hwc_data.calls != NULL)
--		return -EBUSY;
--
--	hwc_data.calls = calls;
--	return 0;
--}
--
--signed int 
--hwc_unregister_calls (hwc_high_level_calls_t * calls)
--{
--	if (hwc_data.calls == NULL)
--		return -EINVAL;
--
--	if (calls != hwc_data.calls)
--		return -EINVAL;
--
--	hwc_data.calls = NULL;
--	return 0;
--}
--
--int 
--hwc_send (hwc_request_t * req)
--{
--	unsigned long flags;
--	int retval;
--	int cc;
--
--	spin_lock_irqsave (&hwc_data.lock, flags);
--	if (!req || !req->callback || !req->block) {
--		retval = -EINVAL;
--		goto unlock;
--	}
--	if (hwc_data.request) {
--		retval = -ENOTSUPP;
--		goto unlock;
--	}
--	cc = service_call (req->word, req->block);
--	switch (cc) {
--	case 0:
--		hwc_data.request = req;
--		hwc_data.current_servc = req->word;
--		hwc_data.current_hwcb = req->block;
--		retval = 0;
--		break;
--	case 2:
--		retval = -EBUSY;
--		break;
--	default:
--		retval = -ENOSYS;
--
--	}
--      unlock:
--	spin_unlock_irqrestore (&hwc_data.lock, flags);
--	return retval;
--}
--
--EXPORT_SYMBOL (hwc_send);
--
--void 
--do_hwc_callback (u32 ext_int_param)
--{
--	if (!hwc_data.request || !hwc_data.request->callback)
--		return;
--	if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR)
--	    != (unsigned long) hwc_data.request->block)
--		return;
--	hwc_data.request->callback (hwc_data.request);
--	hwc_data.request = NULL;
--	hwc_data.current_hwcb = NULL;
--	hwc_data.current_servc = 0;
--}
--
--void 
--hwc_do_interrupt (u32 ext_int_param)
--{
--	u32 finished_hwcb = ext_int_param & HWC_EXT_INT_PARAM_ADDR;
--	u32 evbuf_pending = ext_int_param & HWC_EXT_INT_PARAM_PEND;
--
--	if (hwc_data.flags & HWC_PTIMER_RUNS) {
--		del_timer (&hwc_data.poll_timer);
--		hwc_data.flags &= ~HWC_PTIMER_RUNS;
--	}
--	if (finished_hwcb) {
--
--		if ((unsigned long) hwc_data.current_hwcb != finished_hwcb) {
--			internal_print (
--					       DELAYED_WRITE,
--					       HWC_RW_PRINT_HEADER
--					       "interrupt: mismatch: "
--					       "ext. int param. (0x%x) vs. "
--					       "current HWCB (0x%x)\n",
--					       ext_int_param,
--					       hwc_data.current_hwcb);
--		} else {
--			if (hwc_data.request) {
--
--				do_hwc_callback (ext_int_param);
--			} else {
--
--				switch (hwc_data.current_servc) {
--
--				case HWC_CMDW_WRITEMASK:
--
--					write_event_mask_2 (ext_int_param);
--					break;
--
--				case HWC_CMDW_WRITEDATA:
--
--					write_event_data_2 (ext_int_param);
--					break;
--
--				case HWC_CMDW_READDATA:
--
--					unconditional_read_2 (ext_int_param);
--					break;
--				default:
--				}
--			}
--		}
--	} else {
--
--		if (hwc_data.current_hwcb) {
--			internal_print (
--					       DELAYED_WRITE,
--					       HWC_RW_PRINT_HEADER
--					       "interrupt: mismatch: "
--					       "ext. int. param. (0x%x) vs. "
--					       "current HWCB (0x%x)\n",
--					       ext_int_param,
--					       hwc_data.current_hwcb);
--		}
--	}
--
--	if (evbuf_pending) {
--
--		unconditional_read_1 ();
--	} else {
--
--		write_event_data_1 ();
--	}
--
--	if (!hwc_data.calls || !hwc_data.calls->wake_up)
--		return;
--	(hwc_data.calls->wake_up) ();
--}
--
--void 
--hwc_interrupt_handler (struct pt_regs *regs, __u16 code)
--{
--	int cpu = smp_processor_id ();
--
--	u32 ext_int_param = hwc_ext_int_param ();
--
--	irq_enter (cpu, 0x2401);
--
--	if (hwc_data.flags & HWC_INIT) {
--
--		hwc_data.flags |= HWC_INTERRUPT;
--	} else if (hwc_data.flags & HWC_BROKEN) {
--
--		if (!do_hwc_init ()) {
--			hwc_data.flags &= ~HWC_BROKEN;
--			internal_print (DELAYED_WRITE,
--					HWC_RW_PRINT_HEADER
--					"delayed HWC setup after"
--					" temporary breakdown"
--					" (ext. int. parameter=0x%x)\n",
--					ext_int_param);
--		}
--	} else {
--		spin_lock (&hwc_data.lock);
--		hwc_do_interrupt (ext_int_param);
--		spin_unlock (&hwc_data.lock);
--	}
--	irq_exit (cpu, 0x2401);
--}
--
--void 
--hwc_unblank (void)
--{
--
--	spin_lock (&hwc_data.lock);
--	spin_unlock (&hwc_data.lock);
--
--	__ctl_store (cr0, 0, 0);
--	cr0_save = cr0;
--	cr0 |= 0x00000200;
--	cr0 &= 0xFFFFF3AC;
--	__ctl_load (cr0, 0, 0);
--
--	asm volatile ("STOSM %0,0x01":"=m" (psw_mask)::"memory");
--
--	while (ALL_HWCB_CHAR)
--		barrier ();
--
--	asm volatile ("STNSM %0,0xFE":"=m" (psw_mask)::"memory");
--
--	__ctl_load (cr0_save, 0, 0);
--}
--
--int 
--hwc_ioctl (unsigned int cmd, unsigned long arg)
--{
--	hwc_ioctls_t tmp = hwc_data.ioctls;
--	int retval = 0;
--	unsigned long flags;
--	unsigned int obuf;
--
--	spin_lock_irqsave (&hwc_data.lock, flags);
--
--	switch (cmd) {
--
--	case TIOCHWCSHTAB:
--		if (get_user (tmp.width_htab, (ioctl_htab_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCSECHO:
--		if (get_user (tmp.echo, (ioctl_echo_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCSCOLS:
--		if (get_user (tmp.columns, (ioctl_cols_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCSNL:
--		if (get_user (tmp.final_nl, (ioctl_nl_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCSOBUF:
--		if (get_user (obuf, (unsigned int *) arg))
--			goto fault;
--		if (obuf & 0xFFF)
--			tmp.max_hwcb = (((obuf | 0xFFF) + 1) >> 12);
--		else
--			tmp.max_hwcb = (obuf >> 12);
--		break;
--
--	case TIOCHWCSCASE:
--		if (get_user (tmp.tolower, (ioctl_case_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCSDELIM:
--		if (get_user (tmp.delim, (ioctl_delim_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCSINIT:
--		retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
--		break;
--
--	case TIOCHWCGHTAB:
--		if (put_user (tmp.width_htab, (ioctl_htab_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCGECHO:
--		if (put_user (tmp.echo, (ioctl_echo_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCGCOLS:
--		if (put_user (tmp.columns, (ioctl_cols_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCGNL:
--		if (put_user (tmp.final_nl, (ioctl_nl_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCGOBUF:
--		if (put_user (tmp.max_hwcb, (ioctl_obuf_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCGKBUF:
--		if (put_user (tmp.kmem_hwcb, (ioctl_obuf_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCGCASE:
--		if (put_user (tmp.tolower, (ioctl_case_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCGDELIM:
--		if (put_user (tmp.delim, (ioctl_delim_t *) arg))
--			goto fault;
--		break;
--#if 0
--
--	case TIOCHWCGINIT:
--		if (put_user (&hwc_data.init_ioctls, (hwc_ioctls_t *) arg))
--			goto fault;
--		break;
--
--	case TIOCHWCGCURR:
--		if (put_user (&hwc_data.ioctls, (hwc_ioctls_t *) arg))
--			goto fault;
--		break;
--#endif
--
--	default:
--		goto noioctlcmd;
--	}
--
--	if (_IOC_DIR (cmd) == _IOC_WRITE)
--		retval = set_hwc_ioctls (&tmp, 0);
--
--	goto out;
--
--      fault:
--	retval = -EFAULT;
--	goto out;
--      noioctlcmd:
--	retval = -ENOIOCTLCMD;
--      out:
--	spin_unlock_irqrestore (&hwc_data.lock, flags);
--	return retval;
--}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_rw.h drivers/s390/char/hwc_rw.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_rw.h	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/hwc_rw.h	2006-01-30 22:25:26.000000000 -0700
-@@ -1,132 +0,0 @@
--/*
-- *  drivers/s390/char/hwc_rw.h
-- *    interface to the HWC-read/write driver 
-- *
-- *  S390 version
-- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-- */
--
--#ifndef __HWC_RW_H__
--#define __HWC_RW_H__
--
--#include <linux/ioctl.h>
--
--typedef struct {
--
--	void (*move_input) (unsigned char *, unsigned int);
--
--	void (*wake_up) (void);
--} hwc_high_level_calls_t;
--
--struct _hwc_request;
--
--typedef void hwc_callback_t (struct _hwc_request *);
--
--typedef struct _hwc_request {
--	void *block;
--	u32 word;
--	hwc_callback_t *callback;
--	void *data;
--} __attribute__ ((packed)) 
--
--hwc_request_t;
--
--#define HWC_ASCEBC(x) ((MACHINE_IS_VM ? _ascebc[x] : _ascebc_500[x]))
--
--#define HWC_EBCASC_STR(s,c) ((MACHINE_IS_VM ? EBCASC(s,c) : EBCASC_500(s,c)))
--
--#define HWC_ASCEBC_STR(s,c) ((MACHINE_IS_VM ? ASCEBC(s,c) : ASCEBC_500(s,c)))
--
--#define IN_HWCB      1
--#define IN_WRITE_BUF 2
--#define IN_BUFS_TOTAL        (IN_HWCB | IN_WRITE_BUF)
--
--typedef unsigned short int ioctl_htab_t;
--typedef unsigned char ioctl_echo_t;
--typedef unsigned short int ioctl_cols_t;
--typedef signed char ioctl_nl_t;
--typedef unsigned short int ioctl_obuf_t;
--typedef unsigned char ioctl_case_t;
--typedef unsigned char ioctl_delim_t;
--
--typedef struct {
--	ioctl_htab_t width_htab;
--	ioctl_echo_t echo;
--	ioctl_cols_t columns;
--	ioctl_nl_t final_nl;
--	ioctl_obuf_t max_hwcb;
--	ioctl_obuf_t kmem_hwcb;
--	ioctl_case_t tolower;
--	ioctl_delim_t delim;
--} hwc_ioctls_t;
--
--static hwc_ioctls_t _hwc_ioctls;
--
--#define HWC_IOCTL_LETTER 'B'
--
--#define TIOCHWCSHTAB	_IOW(HWC_IOCTL_LETTER, 0, _hwc_ioctls.width_htab)
--
--#define TIOCHWCSECHO	_IOW(HWC_IOCTL_LETTER, 1, _hwc_ioctls.echo)
--
--#define TIOCHWCSCOLS	_IOW(HWC_IOCTL_LETTER, 2, _hwc_ioctls.columns)
--
--#define TIOCHWCSNL	_IOW(HWC_IOCTL_LETTER, 4, _hwc_ioctls.final_nl)
--
--#define TIOCHWCSOBUF	_IOW(HWC_IOCTL_LETTER, 5, _hwc_ioctls.max_hwcb)
--
--#define TIOCHWCSINIT	_IO(HWC_IOCTL_LETTER, 6)
--
--#define TIOCHWCSCASE	_IOW(HWC_IOCTL_LETTER, 7, _hwc_ioctls.tolower)
--
--#define TIOCHWCSDELIM	_IOW(HWC_IOCTL_LETTER, 9, _hwc_ioctls.delim)
--
--#define TIOCHWCGHTAB	_IOR(HWC_IOCTL_LETTER, 10, _hwc_ioctls.width_htab)
--
--#define TIOCHWCGECHO	_IOR(HWC_IOCTL_LETTER, 11, _hwc_ioctls.echo)
--
--#define TIOCHWCGCOLS	_IOR(HWC_IOCTL_LETTER, 12, _hwc_ioctls.columns)
--
--#define TIOCHWCGNL	_IOR(HWC_IOCTL_LETTER, 14, _hwc_ioctls.final_nl)
--
--#define TIOCHWCGOBUF	_IOR(HWC_IOCTL_LETTER, 15, _hwc_ioctls.max_hwcb)
--
--#define TIOCHWCGINIT	_IOR(HWC_IOCTL_LETTER, 16, _hwc_ioctls)
--
--#define TIOCHWCGCASE	_IOR(HWC_IOCTL_LETTER, 17, _hwc_ioctls.tolower)
--
--#define TIOCHWCGDELIM	_IOR(HWC_IOCTL_LETTER, 19, _hwc_ioctls.delim)
--
--#define TIOCHWCGKBUF	_IOR(HWC_IOCTL_LETTER, 20, _hwc_ioctls.max_hwcb)
--
--#define TIOCHWCGCURR	_IOR(HWC_IOCTL_LETTER, 21, _hwc_ioctls)
--
--#ifndef __HWC_RW_C__
--
--extern int hwc_init (void);
--
--extern int hwc_write (int from_user, const unsigned char *, unsigned int);
--
--extern unsigned int hwc_chars_in_buffer (unsigned char);
--
--extern unsigned int hwc_write_room (unsigned char);
--
--extern void hwc_flush_buffer (unsigned char);
--
--extern void hwc_unblank (void);
--
--extern signed int hwc_ioctl (unsigned int, unsigned long);
--
--extern void do_hwc_interrupt (void);
--
--extern int hwc_printk (const char *,...);
--
--extern signed int hwc_register_calls (hwc_high_level_calls_t *);
--
--extern signed int hwc_unregister_calls (hwc_high_level_calls_t *);
--
--extern int hwc_send (hwc_request_t *);
--
--#endif
--
--#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_tty.c drivers/s390/char/hwc_tty.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_tty.c	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/hwc_tty.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1,273 +0,0 @@
--/*
-- *  drivers/s390/char/hwc_tty.c
-- *    HWC line mode terminal driver.
-- *
-- *  S390 version
-- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-- *
-- *  Thanks to Martin Schwidefsky.
-- */
--
--#include <linux/config.h>
--#include <linux/major.h>
--#include <linux/termios.h>
--#include <linux/tty.h>
--#include <linux/tty_driver.h>
--#include <linux/sched.h>
--#include <linux/mm.h>
--#include <linux/devfs_fs_kernel.h>
--#include <linux/init.h>
--
--#include <asm/uaccess.h>
--
--#include "hwc_rw.h"
--#include "ctrlchar.h"
--
--#define HWC_TTY_PRINT_HEADER "hwc tty driver: "
--
--#define HWC_TTY_BUF_SIZE 512
--
--typedef struct {
--
--	struct tty_struct *tty;
--
--	unsigned char buf[HWC_TTY_BUF_SIZE];
--
--	unsigned short int buf_count;
--
--	spinlock_t lock;
--
--	hwc_high_level_calls_t calls;
--} hwc_tty_data_struct;
--
--static hwc_tty_data_struct hwc_tty_data =
--{ /* NULL/0 */ };
--static struct tty_driver hwc_tty_driver;
--static struct tty_struct *hwc_tty_table[1];
--static struct termios *hwc_tty_termios[1];
--static struct termios *hwc_tty_termios_locked[1];
--static int hwc_tty_refcount = 0;
--
--extern struct termios tty_std_termios;
--
--void hwc_tty_wake_up (void);
--void hwc_tty_input (unsigned char *, unsigned int);
--
--static int 
--hwc_tty_open (struct tty_struct *tty,
--	      struct file *filp)
--{
--
--	if (MINOR (tty->device) - tty->driver.minor_start)
--		return -ENODEV;
--
--	tty->driver_data = &hwc_tty_data;
--	hwc_tty_data.buf_count = 0;
--	hwc_tty_data.tty = tty;
--	tty->low_latency = 0;
--
--	hwc_tty_data.calls.wake_up = hwc_tty_wake_up;
--	hwc_tty_data.calls.move_input = hwc_tty_input;
--	hwc_register_calls (&(hwc_tty_data.calls));
--
--	return 0;
--}
--
--static void 
--hwc_tty_close (struct tty_struct *tty,
--	       struct file *filp)
--{
--	if (MINOR (tty->device) != tty->driver.minor_start) {
--		printk (KERN_WARNING HWC_TTY_PRINT_HEADER
--			"do not close hwc tty because of wrong device number");
--		return;
--	}
--	if (tty->count > 1)
--		return;
--
--	hwc_tty_data.tty = NULL;
--
--	hwc_unregister_calls (&(hwc_tty_data.calls));
--}
--
--static int 
--hwc_tty_write_room (struct tty_struct *tty)
--{
--	int retval;
--
--	retval = hwc_write_room (IN_BUFS_TOTAL);
--	return retval;
--}
--
--static int 
--hwc_tty_write (struct tty_struct *tty,
--	       int from_user,
--	       const unsigned char *buf,
--	       int count)
--{
--	int retval;
--
--	if (hwc_tty_data.buf_count > 0) {
--		hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
--		hwc_tty_data.buf_count = 0;
--	}
--	retval = hwc_write (from_user, buf, count);
--	return retval;
--}
--
--static void 
--hwc_tty_put_char (struct tty_struct *tty,
--		  unsigned char ch)
--{
--	unsigned long flags;
--
--	spin_lock_irqsave (&hwc_tty_data.lock, flags);
--	if (hwc_tty_data.buf_count >= HWC_TTY_BUF_SIZE) {
--		hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
--		hwc_tty_data.buf_count = 0;
--	}
--	hwc_tty_data.buf[hwc_tty_data.buf_count] = ch;
--	hwc_tty_data.buf_count++;
--	spin_unlock_irqrestore (&hwc_tty_data.lock, flags);
--}
--
--static void 
--hwc_tty_flush_chars (struct tty_struct *tty)
--{
--	unsigned long flags;
--
--	spin_lock_irqsave (&hwc_tty_data.lock, flags);
--	hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
--	hwc_tty_data.buf_count = 0;
--	spin_unlock_irqrestore (&hwc_tty_data.lock, flags);
--}
--
--static int 
--hwc_tty_chars_in_buffer (struct tty_struct *tty)
--{
--	int retval;
--
--	retval = hwc_chars_in_buffer (IN_BUFS_TOTAL);
--	return retval;
--}
--
--static void 
--hwc_tty_flush_buffer (struct tty_struct *tty)
--{
--	hwc_tty_wake_up ();
--}
--
--static int 
--hwc_tty_ioctl (
--		      struct tty_struct *tty,
--		      struct file *file,
--		      unsigned int cmd,
--		      unsigned long arg)
--{
--	if (tty->flags & (1 << TTY_IO_ERROR))
--		return -EIO;
--
--	return hwc_ioctl (cmd, arg);
--}
--
--void 
--hwc_tty_wake_up (void)
--{
--	if (hwc_tty_data.tty == NULL)
--		return;
--	if ((hwc_tty_data.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
--	    hwc_tty_data.tty->ldisc.write_wakeup)
--		(hwc_tty_data.tty->ldisc.write_wakeup) (hwc_tty_data.tty);
--	wake_up_interruptible (&hwc_tty_data.tty->write_wait);
--}
--
--void 
--hwc_tty_input (unsigned char *buf, unsigned int count)
--{
--	struct tty_struct *tty = hwc_tty_data.tty;
--
--	if (tty != NULL) {
--		char *cchar;
--		if ((cchar = ctrlchar_handle (buf, count, tty))) {
--			if (cchar == (char *) -1)
--				return;
--			tty->flip.count++;
--			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
--			*tty->flip.char_buf_ptr++ = *cchar;
--		} else {
--
--			memcpy (tty->flip.char_buf_ptr, buf, count);
--			if (count < 2 || (
--					 strncmp (buf + count - 2, "^n", 2) ||
--				    strncmp (buf + count - 2, "\0252n", 2))) {
--				tty->flip.char_buf_ptr[count] = '\n';
--				count++;
--			} else
--				count -= 2;
--			memset (tty->flip.flag_buf_ptr, TTY_NORMAL, count);
--			tty->flip.char_buf_ptr += count;
--			tty->flip.flag_buf_ptr += count;
--			tty->flip.count += count;
--		}
--		tty_flip_buffer_push (tty);
--		hwc_tty_wake_up ();
--	}
--}
--
--void 
--hwc_tty_init (void)
--{
--	if (!CONSOLE_IS_HWC)
--		return;
--
--	ctrlchar_init ();
--
--	memset (&hwc_tty_driver, 0, sizeof (struct tty_driver));
--	memset (&hwc_tty_data, 0, sizeof (hwc_tty_data_struct));
--	hwc_tty_driver.magic = TTY_DRIVER_MAGIC;
--	hwc_tty_driver.driver_name = "tty_hwc";
--	hwc_tty_driver.name = "ttyS";
--	hwc_tty_driver.name_base = 0;
--	hwc_tty_driver.major = TTY_MAJOR;
--	hwc_tty_driver.minor_start = 64;
--	hwc_tty_driver.num = 1;
--	hwc_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
--	hwc_tty_driver.subtype = SYSTEM_TYPE_TTY;
--	hwc_tty_driver.init_termios = tty_std_termios;
--	hwc_tty_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
--	hwc_tty_driver.init_termios.c_oflag = ONLCR;
--	hwc_tty_driver.init_termios.c_lflag = ISIG | ECHO;
--	hwc_tty_driver.flags = TTY_DRIVER_REAL_RAW;
--	hwc_tty_driver.refcount = &hwc_tty_refcount;
--
--	hwc_tty_driver.table = hwc_tty_table;
--	hwc_tty_driver.termios = hwc_tty_termios;
--	hwc_tty_driver.termios_locked = hwc_tty_termios_locked;
--
--	hwc_tty_driver.open = hwc_tty_open;
--	hwc_tty_driver.close = hwc_tty_close;
--	hwc_tty_driver.write = hwc_tty_write;
--	hwc_tty_driver.put_char = hwc_tty_put_char;
--	hwc_tty_driver.flush_chars = hwc_tty_flush_chars;
--	hwc_tty_driver.write_room = hwc_tty_write_room;
--	hwc_tty_driver.chars_in_buffer = hwc_tty_chars_in_buffer;
--	hwc_tty_driver.flush_buffer = hwc_tty_flush_buffer;
--	hwc_tty_driver.ioctl = hwc_tty_ioctl;
--
--	hwc_tty_driver.throttle = NULL;
--	hwc_tty_driver.unthrottle = NULL;
--	hwc_tty_driver.send_xchar = NULL;
--	hwc_tty_driver.set_termios = NULL;
--	hwc_tty_driver.set_ldisc = NULL;
--	hwc_tty_driver.stop = NULL;
--	hwc_tty_driver.start = NULL;
--	hwc_tty_driver.hangup = NULL;
--	hwc_tty_driver.break_ctl = NULL;
--	hwc_tty_driver.wait_until_sent = NULL;
--	hwc_tty_driver.read_proc = NULL;
--	hwc_tty_driver.write_proc = NULL;
--
--	if (tty_register_driver (&hwc_tty_driver))
--		panic ("Couldn't register hwc_tty driver\n");
--}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp.c drivers/s390/char/sclp.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/sclp.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,854 @@
-+/*
-+ *  drivers/s390/char/sclp.c
-+ *     core function to access sclp interface
-+ *
-+ *  S390 version
-+ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-+ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/module.h>
-+#include <linux/kmod.h>
-+#include <linux/bootmem.h>
-+#include <linux/errno.h>
-+#include <linux/ptrace.h>
-+#include <linux/slab.h>
-+#include <linux/spinlock.h>
-+#include <linux/interrupt.h>
-+#include <linux/timer.h>
-+#include <linux/init.h>
-+#include <linux/reboot.h>
-+#include <asm/irq.h>
-+#include <asm/s390_ext.h>
-+#include <asm/processor.h>
-+
-+#include "sclp.h"
-+
-+#define SCLP_CORE_PRINT_HEADER "sclp low level driver: "
-+
-+/* Structure for register_early_external_interrupt. */
-+static ext_int_info_t ext_int_info_hwc;
-+
-+/* spinlock to protect global variables of sclp_core */
-+static spinlock_t sclp_lock;
-+
-+/* Mask of valid sclp events */
-+static sccb_mask_t sclp_receive_mask;
-+static sccb_mask_t sclp_send_mask;
-+
-+/* List of registered event types */
-+static struct list_head sclp_reg_list;
-+
-+/* sccb queue */
-+static struct list_head sclp_req_queue;
-+
-+/* sccb for unconditional read */
-+static struct sclp_req sclp_read_req;
-+static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
-+/* sccb for write mask sccb */
-+static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
-+
-+/* Timer for init mask retries. */
-+static struct timer_list retry_timer;
-+
-+/* Timer for busy retries. */
-+static struct timer_list sclp_busy_timer;
-+
-+static volatile unsigned long sclp_status = 0;
-+/* some status flags */
-+#define SCLP_INIT		0
-+#define SCLP_RUNNING		1
-+#define SCLP_READING		2
-+#define SCLP_SHUTDOWN		3
-+
-+#define SCLP_INIT_POLL_INTERVAL	1
-+#define SCLP_BUSY_POLL_INTERVAL	1
-+
-+#define SCLP_COMMAND_INITIATED	0
-+#define SCLP_BUSY		2
-+#define SCLP_NOT_OPERATIONAL	3
-+
-+/*
-+ * assembler instruction for Service Call
-+ */
-+static int
-+__service_call(sclp_cmdw_t command, void *sccb)
-+{
-+	int cc;
-+
-+	/*
-+	 *  Mnemonic:	SERVC	Rx, Ry	[RRE]
-+	 *
-+	 *  Rx: SCLP command word
-+	 *  Ry: address of SCCB
-+	 */
-+	__asm__ __volatile__(
-+		"   .insn rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */
-+		"   ipm	  %0\n"
-+		"   srl	  %0,28"
-+		: "=&d" (cc)
-+		: "d" (command), "a" (__pa(sccb))
-+		: "cc", "memory" );
-+	/*
-+	 * cc == 0:   Service Call succesful initiated
-+	 * cc == 2:   SCLP busy, new Service Call not initiated,
-+	 *	      new SCCB unchanged
-+	 * cc == 3:   SCLP function not operational
-+	 */
-+	if (cc == SCLP_NOT_OPERATIONAL)
-+		return -EIO;
-+	if (cc == SCLP_BUSY)
-+		return -EBUSY;
-+	return 0;
-+}
-+
-+static void
-+sclp_start_request(void)
-+{
-+	struct sclp_req *req;
-+	int rc;
-+	unsigned long flags;
-+
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	/* quick exit if sclp is already in use */
-+	if (test_bit(SCLP_RUNNING, &sclp_status)) {
-+		spin_unlock_irqrestore(&sclp_lock, flags);
-+		return;
-+	}
-+	/* Try to start requests from the request queue. */
-+	while (!list_empty(&sclp_req_queue)) {
-+		req = list_entry(sclp_req_queue.next, struct sclp_req, list);
-+		rc = __service_call(req->command, req->sccb);
-+		if (rc == 0) {
-+			/* Sucessfully started request. */
-+			req->status = SCLP_REQ_RUNNING;
-+			/* Request active. Set running indication. */
-+			set_bit(SCLP_RUNNING, &sclp_status);
-+			break;
-+		}
-+		if (rc == -EBUSY) {
-+			/**
-+			 * SCLP is busy but no request is running.
-+			 * Try again later.
-+			 */
-+			if (!timer_pending(&sclp_busy_timer) ||
-+			    !mod_timer(&sclp_busy_timer,
-+				       jiffies + SCLP_BUSY_POLL_INTERVAL*HZ)) {
-+				sclp_busy_timer.function =
-+					(void *) sclp_start_request;
-+				sclp_busy_timer.expires =
-+					jiffies + SCLP_BUSY_POLL_INTERVAL*HZ;
-+				add_timer(&sclp_busy_timer);
-+			}
-+			break;
-+		}
-+		/* Request failed. */
-+		req->status = SCLP_REQ_FAILED;
-+		list_del(&req->list);
-+		if (req->callback) {
-+			spin_unlock_irqrestore(&sclp_lock, flags);
-+			req->callback(req, req->callback_data);
-+			spin_lock_irqsave(&sclp_lock, flags);
-+		}
-+	}
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+}
-+
-+static int
-+sclp_process_evbufs(struct sccb_header *sccb)
-+{
-+	int result;
-+	unsigned long flags;
-+	struct evbuf_header *evbuf;
-+	struct list_head *l;
-+	struct sclp_register *t;
-+
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	evbuf = (struct evbuf_header *) (sccb + 1);
-+	result = 0;
-+	while ((addr_t) evbuf < (addr_t) sccb + sccb->length) {
-+		/* check registered event */
-+		t = NULL;
-+		list_for_each(l, &sclp_reg_list) {
-+			t = list_entry(l, struct sclp_register, list);
-+			if (t->receive_mask & (1 << (32 - evbuf->type))) {
-+				if (t->receiver_fn != NULL) {
-+					spin_unlock_irqrestore(&sclp_lock,
-+							       flags);
-+					t->receiver_fn(evbuf);
-+					spin_lock_irqsave(&sclp_lock, flags);
-+				}
-+				break;
-+			}
-+			else
-+				t = NULL;
-+		}
-+		/* Check for unrequested event buffer */
-+		if (t == NULL)
-+			result = -ENOSYS;
-+		evbuf = (struct evbuf_header *)
-+				((addr_t) evbuf + evbuf->length);
-+	}
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	return result;
-+}
-+
-+char *
-+sclp_error_message(u16 rc)
-+{
-+	static struct {
-+		u16 code; char *msg;
-+	} sclp_errors[] = {
-+		{ 0x0000, "No response code stored (machine malfunction)" },
-+		{ 0x0020, "Normal Completion" },
-+		{ 0x0040, "SCLP equipment check" },
-+		{ 0x0100, "SCCB boundary violation" },
-+		{ 0x01f0, "Invalid command" },
-+		{ 0x0220, "Normal Completion; suppressed buffers pending" },
-+		{ 0x0300, "Insufficient SCCB length" },
-+		{ 0x0340, "Contained SCLP equipment check" },
-+		{ 0x05f0, "Target resource in improper state" },
-+		{ 0x40f0, "Invalid function code/not installed" },
-+		{ 0x60f0, "No buffers stored" },
-+		{ 0x62f0, "No buffers stored; suppressed buffers pending" },
-+		{ 0x70f0, "Invalid selection mask" },
-+		{ 0x71f0, "Event buffer exceeds available space" },
-+		{ 0x72f0, "Inconsistent lengths" },
-+		{ 0x73f0, "Event buffer syntax error" }
-+	};
-+	int i;
-+	for (i = 0; i < sizeof(sclp_errors)/sizeof(sclp_errors[0]); i++)
-+		if (rc == sclp_errors[i].code)
-+			return sclp_errors[i].msg;
-+	return "Invalid response code";
-+}
-+
-+/*
-+ * postprocessing of unconditional read service call
-+ */
-+static void
-+sclp_unconditional_read_cb(struct sclp_req *read_req, void *data)
-+{
-+	struct sccb_header *sccb;
-+
-+	sccb = read_req->sccb;
-+	if (sccb->response_code == 0x0020 ||
-+	    sccb->response_code == 0x0220) {
-+		if (sclp_process_evbufs(sccb) != 0)
-+			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
-+			       "unconditional read: "
-+			       "unrequested event buffer received.\n");
-+	}
-+
-+	if (sccb->response_code != 0x0020)
-+		printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
-+		       "unconditional read: %s (response code=0x%x).\n",
-+		       sclp_error_message(sccb->response_code),
-+		       sccb->response_code);
-+
-+	clear_bit(SCLP_READING, &sclp_status);
-+}
-+
-+/*
-+ * Function to queue Read Event Data/Unconditional Read
-+ */
-+static void
-+__sclp_unconditional_read(void)
-+{
-+	struct sccb_header *sccb;
-+	struct sclp_req *read_req;
-+
-+	/*
-+	 * Don't try to initiate Unconditional Read if we are not able to
-+	 * receive anything
-+	 */
-+	if (sclp_receive_mask == 0)
-+		return;
-+	/* Don't try reading if a read is already outstanding */
-+	if (test_and_set_bit(SCLP_READING, &sclp_status))
-+		return;
-+	/* Initialize read sccb */
-+	sccb = (struct sccb_header *) sclp_read_sccb;
-+	clear_page(sccb);
-+	sccb->length = PAGE_SIZE;
-+	sccb->function_code = 0;	/* unconditional read */
-+	sccb->control_mask[2] = 0x80;	/* variable length response */
-+	/* Initialize request structure */
-+	read_req = &sclp_read_req;
-+	read_req->command = SCLP_CMDW_READDATA;
-+	read_req->status = SCLP_REQ_QUEUED;
-+	read_req->callback = sclp_unconditional_read_cb;
-+	read_req->sccb = sccb;
-+	/* Add read request to the head of queue */
-+	list_add(&read_req->list, &sclp_req_queue);
-+}
-+
-+/* Bit masks to interpret external interruption parameter contents. */
-+#define EXT_INT_SCCB_MASK		0xfffffff8
-+#define EXT_INT_STATECHANGE_PENDING	0x00000002
-+#define EXT_INT_EVBUF_PENDING		0x00000001
-+
-+/*
-+ * Handler for service-signal external interruptions
-+ */
-+static void
-+sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
-+{
-+	u32 ext_int_param, finished_sccb, evbuf_pending;
-+	struct list_head *l;
-+	struct sclp_req *req, *tmp;
-+	int cpu;
-+
-+	spin_lock(&sclp_lock);
-+	/*
-+	 * Only process interrupt if sclp is initialized.
-+	 * This avoids strange effects for a pending request
-+	 * from before the last re-ipl.
-+	 */
-+	if (!test_bit(SCLP_INIT, &sclp_status)) {
-+		/* Now clear the running bit */
-+		clear_bit(SCLP_RUNNING, &sclp_status);
-+		spin_unlock(&sclp_lock);
-+		return;
-+	}
-+	ext_int_param = S390_lowcore.ext_params;
-+	finished_sccb = ext_int_param & EXT_INT_SCCB_MASK;
-+	evbuf_pending = ext_int_param & (EXT_INT_EVBUF_PENDING |
-+					 EXT_INT_STATECHANGE_PENDING);
-+	cpu = smp_processor_id();
-+	irq_enter(cpu, 0x2401);
-+	req = NULL;
-+	if (finished_sccb != 0U) {
-+		list_for_each(l, &sclp_req_queue) {
-+			tmp = list_entry(l, struct sclp_req, list);
-+			if (finished_sccb == (u32)(addr_t) tmp->sccb) {
-+				list_del(&tmp->list);
-+				req = tmp;
-+				break;
-+			}
-+		}
-+	}
-+	spin_unlock(&sclp_lock);
-+	/* Perform callback */
-+	if (req != NULL) {
-+		req->status = SCLP_REQ_DONE;
-+		if (req->callback != NULL)
-+			req->callback(req, req->callback_data);
-+	}
-+	spin_lock(&sclp_lock);
-+	/* Head queue a read sccb if an event buffer is pending */
-+	if (evbuf_pending)
-+		__sclp_unconditional_read();
-+	/* Now clear the running bit if SCLP indicated a finished SCCB */
-+	if (finished_sccb != 0U)
-+		clear_bit(SCLP_RUNNING, &sclp_status);
-+	spin_unlock(&sclp_lock);
-+	/* and start next request on the queue */
-+	sclp_start_request();
-+	irq_exit(cpu, 0x2401);
-+}
-+
-+/*
-+ * Wait synchronously for external interrupt of sclp. We may not receive
-+ * any other external interrupt, so we disable all other external interrupts
-+ * in control register 0.
-+ */
-+void
-+sclp_sync_wait(void)
-+{
-+	unsigned long psw_mask;
-+	unsigned long cr0, cr0_sync;
-+
-+	/* Prevent BH from executing. */
-+	local_bh_disable();
-+	/*
-+	 * save cr0
-+	 * enable service signal external interruption (cr0.22)
-+	 * disable cr0.20-21, cr0.25, cr0.27, cr0.30-31
-+	 * don't touch any other bit in cr0
-+	 */
-+	__ctl_store(cr0, 0, 0);
-+	cr0_sync = cr0;
-+	cr0_sync |= 0x00000200;
-+	cr0_sync &= 0xFFFFF3AC;
-+	__ctl_load(cr0_sync, 0, 0);
-+
-+	/* enable external interruptions (PSW-mask.7) */
-+	asm volatile ("STOSM 0(%1),0x01"
-+		      : "=m" (psw_mask) : "a" (&psw_mask) : "memory");
-+
-+	/* wait until ISR signals receipt of interrupt */
-+	while (test_bit(SCLP_RUNNING, &sclp_status)) {
-+		barrier();
-+		cpu_relax();
-+	}
-+
-+	/* disable external interruptions */
-+	asm volatile ("SSM 0(%0)"
-+		      : : "a" (&psw_mask) : "memory");
-+
-+	/* restore cr0 */
-+	__ctl_load(cr0, 0, 0);
-+	__local_bh_enable();
-+}
-+
-+/*
-+ * Queue an SCLP request. Request will immediately be processed if queue is
-+ * empty.
-+ */
-+void
-+sclp_add_request(struct sclp_req *req)
-+{
-+	unsigned long flags;
-+
-+	if (!test_bit(SCLP_INIT, &sclp_status)) {
-+		req->status = SCLP_REQ_FAILED;
-+		if (req->callback != NULL)
-+			req->callback(req, req->callback_data);
-+		return;
-+	}
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	/* queue the request */
-+	req->status = SCLP_REQ_QUEUED;
-+	list_add_tail(&req->list, &sclp_req_queue);
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	/* try to start the first request on the queue */
-+	sclp_start_request();
-+}
-+
-+/* state change notification */
-+struct sclp_statechangebuf {
-+	struct evbuf_header	header;
-+	u8		validity_sclp_active_facility_mask : 1;
-+	u8		validity_sclp_receive_mask : 1;
-+	u8		validity_sclp_send_mask : 1;
-+	u8		validity_read_data_function_mask : 1;
-+	u16		_zeros : 12;
-+	u16		mask_length;
-+	u64		sclp_active_facility_mask;
-+	sccb_mask_t	sclp_receive_mask;
-+	sccb_mask_t	sclp_send_mask;
-+	u32		read_data_function_mask;
-+} __attribute__((packed));
-+
-+static inline void
-+__sclp_notify_state_change(void)
-+{
-+	struct list_head *l;
-+	struct sclp_register *t;
-+	sccb_mask_t receive_mask, send_mask;
-+
-+	list_for_each(l, &sclp_reg_list) {
-+		t = list_entry(l, struct sclp_register, list);
-+		receive_mask = t->receive_mask & sclp_receive_mask;
-+		send_mask = t->send_mask & sclp_send_mask;
-+		if (t->sclp_receive_mask != receive_mask ||
-+		    t->sclp_send_mask != send_mask) {
-+			t->sclp_receive_mask = receive_mask;
-+			t->sclp_send_mask = send_mask;
-+			if (t->state_change_fn != NULL)
-+				t->state_change_fn(t);
-+		}
-+	}
-+}
-+
-+static void
-+sclp_state_change(struct evbuf_header *evbuf)
-+{
-+	unsigned long flags;
-+	struct sclp_statechangebuf *scbuf;
-+
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	scbuf = (struct sclp_statechangebuf *) evbuf;
-+
-+	if (scbuf->validity_sclp_receive_mask) {
-+		if (scbuf->mask_length != sizeof(sccb_mask_t))
-+			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
-+			       "state change event with mask length %i\n",
-+			       scbuf->mask_length);
-+		else
-+			/* set new receive mask */
-+			sclp_receive_mask = scbuf->sclp_receive_mask;
-+	}
-+
-+	if (scbuf->validity_sclp_send_mask) {
-+		if (scbuf->mask_length != sizeof(sccb_mask_t))
-+			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
-+			       "state change event with mask length %i\n",
-+			       scbuf->mask_length);
-+		else
-+			/* set new send mask */
-+			sclp_send_mask = scbuf->sclp_send_mask;
-+	}
-+
-+	__sclp_notify_state_change();
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+}
-+
-+static struct sclp_register sclp_state_change_event = {
-+	.receive_mask = EvTyp_StateChange_Mask,
-+	.receiver_fn = sclp_state_change
-+};
-+
-+
-+/*
-+ * SCLP quiesce event handler
-+ */
-+#ifdef CONFIG_SMP
-+static void
-+do_load_quiesce_psw(void * __unused)
-+{
-+	psw_t quiesce_psw;
-+	unsigned long status;
-+	int i;
-+
-+	if (smp_processor_id() != 0)
-+		signal_processor(smp_processor_id(), sigp_stop);
-+	/* Wait for all other cpus to enter stopped state */
-+	i = 1;
-+	while (i < smp_num_cpus) {
-+		switch (signal_processor_ps(&status, 0, i, sigp_sense)) {
-+		case sigp_order_code_accepted:
-+		case sigp_status_stored:
-+			/* Check for stopped and check stop state */
-+			if (test_bit(6, &status) || test_bit(4, &status))
-+				i++;
-+			break;
-+		case sigp_busy:
-+			break;
-+		case sigp_not_operational:
-+			i++;
-+			break;
-+		}
-+	}
-+	/* Quiesce the last cpu with the special psw */
-+	quiesce_psw.mask = _DW_PSW_MASK;
-+	quiesce_psw.addr = 0xfff;
-+	__load_psw(quiesce_psw);
-+}
-+
-+static void
-+do_machine_quiesce(void)
-+{
-+	smp_call_function(do_load_quiesce_psw, NULL, 0, 0);
-+	do_load_quiesce_psw(NULL);
-+}
-+#else
-+static void
-+do_machine_quiesce(void)
-+{
-+	psw_t quiesce_psw;
-+
-+	quiesce_psw.mask = _DW_PSW_MASK;
-+	quiesce_psw.addr = 0xfff;
-+	__load_psw(quiesce_psw);
-+}
-+#endif
-+
-+extern void ctrl_alt_del(void);
-+
-+static void
-+sclp_quiesce(struct evbuf_header *evbuf)
-+{
-+	/*
-+	 * We got a "shutdown" request.
-+	 * Add a call to an appropriate "shutdown" routine here. This
-+	 * routine should set all PSWs to 'disabled-wait', 'stopped'
-+	 * or 'check-stopped' - except 1 PSW which needs to carry a
-+	 * special bit pattern called 'quiesce PSW'.
-+	 */
-+	_machine_restart = (void *) do_machine_quiesce;
-+	_machine_halt = do_machine_quiesce;
-+	_machine_power_off = do_machine_quiesce;
-+	ctrl_alt_del();
-+}
-+
-+static struct sclp_register sclp_quiesce_event = {
-+	.receive_mask = EvTyp_SigQuiesce_Mask,
-+	.receiver_fn = sclp_quiesce
-+};
-+
-+/* initialisation of SCLP */
-+struct init_sccb {
-+	struct sccb_header header;
-+	u16 _reserved;
-+	u16 mask_length;
-+	sccb_mask_t receive_mask;
-+	sccb_mask_t send_mask;
-+	sccb_mask_t sclp_send_mask;
-+	sccb_mask_t sclp_receive_mask;
-+} __attribute__((packed));
-+
-+static void sclp_init_mask_retry(unsigned long);
-+
-+static int
-+sclp_init_mask(void)
-+{
-+	unsigned long flags;
-+	struct init_sccb *sccb;
-+	struct sclp_req *req;
-+	struct list_head *l;
-+	struct sclp_register *t;
-+	int rc;
-+
-+	sccb = (struct init_sccb *) sclp_init_sccb;
-+	/* stick the request structure to the end of the init sccb page */
-+	req = (struct sclp_req *) ((addr_t) sccb + PAGE_SIZE) - 1;
-+
-+	/* SCLP setup concerning receiving and sending Event Buffers */
-+	req->command = SCLP_CMDW_WRITEMASK;
-+	req->status = SCLP_REQ_QUEUED;
-+	req->callback = NULL;
-+	req->sccb = sccb;
-+	/* setup sccb for writemask command */
-+	memset(sccb, 0, sizeof(struct init_sccb));
-+	sccb->header.length = sizeof(struct init_sccb);
-+	sccb->mask_length = sizeof(sccb_mask_t);
-+	/* copy in the sccb mask of the registered event types */
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	if (!test_bit(SCLP_SHUTDOWN, &sclp_status)) {
-+		list_for_each(l, &sclp_reg_list) {
-+			t = list_entry(l, struct sclp_register, list);
-+			sccb->receive_mask |= t->receive_mask;
-+			sccb->send_mask |= t->send_mask;
-+		}
-+	}
-+	sccb->sclp_receive_mask = 0;
-+	sccb->sclp_send_mask = 0;
-+	if (test_bit(SCLP_INIT, &sclp_status)) {
-+		/* add request to sclp queue */
-+		list_add_tail(&req->list, &sclp_req_queue);
-+		spin_unlock_irqrestore(&sclp_lock, flags);
-+		/* and start if SCLP is idle */
-+		sclp_start_request();
-+		/* now wait for completion */
-+		while (req->status != SCLP_REQ_DONE &&
-+		       req->status != SCLP_REQ_FAILED)
-+			sclp_sync_wait();
-+		spin_lock_irqsave(&sclp_lock, flags);
-+	} else {
-+		/*
-+		 * Special case for the very first write mask command.
-+		 * The interrupt handler is not removing request from
-+		 * the request queue and doesn't call callbacks yet
-+		 * because there might be an pending old interrupt
-+		 * after a Re-IPL. We have to receive and ignore it.
-+		 */
-+		do {
-+			rc = __service_call(req->command, req->sccb);
-+			if (rc == 0)
-+				set_bit(SCLP_RUNNING, &sclp_status);
-+			spin_unlock_irqrestore(&sclp_lock, flags);
-+			if (rc == -EIO)
-+				return -ENOSYS;
-+			sclp_sync_wait();
-+			spin_lock_irqsave(&sclp_lock, flags);
-+		} while (rc == -EBUSY);
-+	}
-+	if (sccb->header.response_code != 0x0020) {
-+		/* WRITEMASK failed - we cannot rely on receiving a state
-+		   change event, so initially, polling is the only alternative
-+		   for us to ever become operational. */
-+		if (!test_bit(SCLP_SHUTDOWN, &sclp_status) &&
-+		    (!timer_pending(&retry_timer) ||
-+		     !mod_timer(&retry_timer,
-+			       jiffies + SCLP_INIT_POLL_INTERVAL*HZ))) {
-+			retry_timer.function = sclp_init_mask_retry;
-+			retry_timer.data = 0;
-+			retry_timer.expires = jiffies +
-+				SCLP_INIT_POLL_INTERVAL*HZ;
-+			add_timer(&retry_timer);
-+		}
-+	} else {
-+		sclp_receive_mask = sccb->sclp_receive_mask;
-+		sclp_send_mask = sccb->sclp_send_mask;
-+		__sclp_notify_state_change();
-+	}
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	return 0;
-+}
-+
-+static void
-+sclp_init_mask_retry(unsigned long data) 
-+{
-+	sclp_init_mask();
-+}
-+
-+/* Reboot event handler - reset send and receive mask to prevent pending SCLP
-+ * events from interfering with rebooted system. */
-+static int
-+sclp_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
-+{
-+	unsigned long flags;
-+
-+	/* Note: need spinlock to maintain atomicity when accessing global
-+         * variables. */
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	set_bit(SCLP_SHUTDOWN, &sclp_status);
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	sclp_init_mask();
-+	return NOTIFY_DONE;
-+}
-+
-+static struct notifier_block sclp_reboot_notifier = {
-+	.notifier_call = sclp_reboot_event
-+};
-+
-+/*
-+ * sclp setup function. Called early (no kmalloc!) from sclp_console_init().
-+ */
-+static int
-+sclp_init(void)
-+{
-+	int rc;
-+
-+	if (test_bit(SCLP_INIT, &sclp_status))
-+		/* Already initialized. */
-+		return 0;
-+
-+	spin_lock_init(&sclp_lock);
-+	INIT_LIST_HEAD(&sclp_req_queue);
-+
-+	/* init event list */
-+	INIT_LIST_HEAD(&sclp_reg_list);
-+	list_add(&sclp_state_change_event.list, &sclp_reg_list);
-+	list_add(&sclp_quiesce_event.list, &sclp_reg_list);
-+
-+	rc = register_reboot_notifier(&sclp_reboot_notifier);
-+	if (rc)
-+		return rc;
-+
-+	/*
-+	 * request the 0x2401 external interrupt
-+	 * The sclp driver is initialized early (before kmalloc works). We
-+	 * need to use register_early_external_interrupt.
-+	 */
-+	if (register_early_external_interrupt(0x2401, sclp_interrupt_handler,
-+					      &ext_int_info_hwc) != 0)
-+		return -EBUSY;
-+
-+	/* enable service-signal external interruptions,
-+	 * Control Register 0 bit 22 := 1
-+	 * (besides PSW bit 7 must be set to 1 sometimes for external
-+	 * interruptions)
-+	 */
-+	ctl_set_bit(0, 9);
-+
-+	init_timer(&retry_timer);
-+	init_timer(&sclp_busy_timer);
-+	/* do the initial write event mask */
-+	rc = sclp_init_mask();
-+	if (rc == 0) {
-+		/* Ok, now everything is setup right. */
-+		set_bit(SCLP_INIT, &sclp_status);
-+		return 0;
-+	}
-+
-+	/* The sclp_init_mask failed. SCLP is broken, unregister and exit. */
-+	ctl_clear_bit(0,9);
-+	unregister_early_external_interrupt(0x2401, sclp_interrupt_handler,
-+					    &ext_int_info_hwc);
-+
-+	return rc;
-+}
-+
-+/*
-+ * Register the SCLP event listener identified by REG. Return 0 on success.
-+ * Some error codes and their meaning:
-+ *
-+ *  -ENODEV = SCLP interface is not supported on this machine
-+ *   -EBUSY = there is already a listener registered for the requested
-+ *            event type
-+ *     -EIO = SCLP interface is currently not operational
-+ */
-+int
-+sclp_register(struct sclp_register *reg)
-+{
-+	unsigned long flags;
-+	struct list_head *l;
-+	struct sclp_register *t;
-+
-+	if (!MACHINE_HAS_SCLP)
-+		return -ENODEV;
-+
-+	if (!test_bit(SCLP_INIT, &sclp_status))
-+		sclp_init();
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	/* check already registered event masks for collisions */
-+	list_for_each(l, &sclp_reg_list) {
-+		t = list_entry(l, struct sclp_register, list);
-+		if (t->receive_mask & reg->receive_mask ||
-+		    t->send_mask & reg->send_mask) {
-+			spin_unlock_irqrestore(&sclp_lock, flags);
-+			return -EBUSY;
-+		}
-+	}
-+	/*
-+	 * set present mask to 0 to trigger state change
-+	 * callback in sclp_init_mask
-+	 */
-+	reg->sclp_receive_mask = 0;
-+	reg->sclp_send_mask = 0;
-+	list_add(&reg->list, &sclp_reg_list);
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	sclp_init_mask();
-+	return 0;
-+}
-+
-+/*
-+ * Unregister the SCLP event listener identified by REG.
-+ */
-+void
-+sclp_unregister(struct sclp_register *reg)
-+{
-+	unsigned long flags;
-+
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	list_del(&reg->list);
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	sclp_init_mask();
-+}
-+
-+#define	SCLP_EVBUF_PROCESSED	0x80
-+
-+/*
-+ * Traverse array of event buffers contained in SCCB and remove all buffers
-+ * with a set "processed" flag. Return the number of unprocessed buffers.
-+ */
-+int
-+sclp_remove_processed(struct sccb_header *sccb)
-+{
-+	struct evbuf_header *evbuf;
-+	int unprocessed;
-+	u16 remaining;
-+
-+	evbuf = (struct evbuf_header *) (sccb + 1);
-+	unprocessed = 0;
-+	remaining = sccb->length - sizeof(struct sccb_header);
-+	while (remaining > 0) {
-+		remaining -= evbuf->length;
-+		if (evbuf->flags & SCLP_EVBUF_PROCESSED) {
-+			sccb->length -= evbuf->length;
-+			memcpy((void *) evbuf,
-+			       (void *) ((addr_t) evbuf + evbuf->length),
-+			       remaining);
-+		} else {
-+			unprocessed++;
-+			evbuf = (struct evbuf_header *)
-+					((addr_t) evbuf + evbuf->length);
-+		}
-+	}
-+
-+	return unprocessed;
-+}
-+
-+module_init(sclp_init);
-+
-+EXPORT_SYMBOL(sclp_add_request);
-+EXPORT_SYMBOL(sclp_sync_wait);
-+EXPORT_SYMBOL(sclp_register);
-+EXPORT_SYMBOL(sclp_unregister);
-+EXPORT_SYMBOL(sclp_error_message);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp.h drivers/s390/char/sclp.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/sclp.h	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,157 @@
-+/*
-+ *  drivers/s390/char/sclp.h
-+ *
-+ *  S390 version
-+ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-+ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ */
-+
-+#ifndef __SCLP_H__
-+#define __SCLP_H__
-+
-+#include <linux/types.h>
-+#include <linux/list.h>
-+
-+#include <asm/ebcdic.h>
-+
-+/* maximum number of pages concerning our own memory management */
-+#define MAX_KMEM_PAGES (sizeof(unsigned long) << 3)
-+#define MAX_CONSOLE_PAGES	4
-+
-+#define EvTyp_OpCmd		0x01
-+#define EvTyp_Msg		0x02
-+#define EvTyp_StateChange	0x08
-+#define EvTyp_PMsgCmd		0x09
-+#define EvTyp_CntlProgOpCmd	0x20
-+#define EvTyp_CntlProgIdent	0x0B
-+#define EvTyp_SigQuiesce	0x1D
-+#define EvTyp_VT220Msg		0x1A
-+
-+#define EvTyp_OpCmd_Mask	0x80000000
-+#define EvTyp_Msg_Mask		0x40000000
-+#define EvTyp_StateChange_Mask	0x01000000
-+#define EvTyp_PMsgCmd_Mask	0x00800000
-+#define EvTyp_CtlProgOpCmd_Mask	0x00000001
-+#define EvTyp_CtlProgIdent_Mask	0x00200000
-+#define EvTyp_SigQuiesce_Mask	0x00000008
-+#define EvTyp_VT220Msg_Mask	0x00000040
-+
-+#define GnrlMsgFlgs_DOM		0x8000
-+#define GnrlMsgFlgs_SndAlrm	0x4000
-+#define GnrlMsgFlgs_HoldMsg	0x2000
-+
-+#define LnTpFlgs_CntlText	0x8000
-+#define LnTpFlgs_LabelText	0x4000
-+#define LnTpFlgs_DataText	0x2000
-+#define LnTpFlgs_EndText	0x1000
-+#define LnTpFlgs_PromptText	0x0800
-+
-+typedef unsigned int sclp_cmdw_t;
-+
-+#define SCLP_CMDW_READDATA	0x00770005
-+#define SCLP_CMDW_WRITEDATA	0x00760005
-+#define SCLP_CMDW_WRITEMASK	0x00780005
-+
-+#define GDS_ID_MDSMU		0x1310
-+#define GDS_ID_MDSRouteInfo	0x1311
-+#define GDS_ID_AgUnWrkCorr	0x1549
-+#define GDS_ID_SNACondReport	0x1532
-+#define GDS_ID_CPMSU		0x1212
-+#define GDS_ID_RoutTargInstr	0x154D
-+#define GDS_ID_OpReq		0x8070
-+#define GDS_ID_TextCmd		0x1320
-+
-+#define GDS_KEY_SelfDefTextMsg	0x31
-+
-+typedef u32 sccb_mask_t;	/* ATTENTION: assumes 32bit mask !!! */
-+
-+struct sccb_header {
-+	u16	length;
-+	u8	function_code;
-+	u8	control_mask[3];
-+	u16	response_code;
-+} __attribute__((packed));
-+
-+struct gds_subvector {
-+	u8	length;
-+	u8	key;
-+} __attribute__((packed));
-+
-+struct gds_vector {
-+	u16	length;
-+	u16	gds_id;
-+} __attribute__((packed));
-+
-+struct evbuf_header {
-+	u16	length;
-+	u8	type;
-+	u8	flags;
-+	u16	_reserved;
-+} __attribute__((packed));
-+
-+struct sclp_req {
-+	struct list_head list;		/* list_head for request queueing. */
-+	sclp_cmdw_t command;		/* sclp command to execute */
-+	void	*sccb;			/* pointer to the sccb to execute */
-+	char	status;			/* status of this request */
-+	/* Callback that is called after reaching final status. */
-+	void (*callback)(struct sclp_req *, void *data);
-+	void *callback_data;
-+};
-+
-+#define SCLP_REQ_FILLED	  0x00	/* request is ready to be processed */
-+#define SCLP_REQ_QUEUED	  0x01	/* request is queued to be processed */
-+#define SCLP_REQ_RUNNING  0x02	/* request is currently running */
-+#define SCLP_REQ_DONE	  0x03	/* request is completed successfully */
-+#define SCLP_REQ_FAILED	  0x05	/* request is finally failed */
-+
-+/* function pointers that a high level driver has to use for registration */
-+/* of some routines it wants to be called from the low level driver */
-+struct sclp_register {
-+	struct list_head list;
-+	/* event masks this user is registered for */
-+	sccb_mask_t receive_mask;
-+	sccb_mask_t send_mask;
-+	/* actually present events */
-+	sccb_mask_t sclp_receive_mask;
-+	sccb_mask_t sclp_send_mask;
-+	/* called if event type availability changes */
-+	void (*state_change_fn)(struct sclp_register *);
-+	/* called for events in cp_receive_mask/sclp_receive_mask */
-+	void (*receiver_fn)(struct evbuf_header *);
-+};
-+
-+/* externals from sclp.c */
-+void sclp_add_request(struct sclp_req *req);
-+void sclp_sync_wait(void);
-+int sclp_register(struct sclp_register *reg);
-+void sclp_unregister(struct sclp_register *reg);
-+char *sclp_error_message(u16 response_code);
-+int sclp_remove_processed(struct sccb_header *sccb);
-+
-+/* useful inlines */
-+
-+/* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */
-+/* translate single character from ASCII to EBCDIC */
-+static inline unsigned char
-+sclp_ascebc(unsigned char ch)
-+{
-+	return (MACHINE_IS_VM) ? _ascebc[ch] : _ascebc_500[ch];
-+}
-+
-+/* translate string from EBCDIC to ASCII */
-+static inline void
-+sclp_ebcasc_str(unsigned char *str, int nr)
-+{
-+	(MACHINE_IS_VM) ? EBCASC(str, nr) : EBCASC_500(str, nr);
-+}
-+
-+/* translate string from ASCII to EBCDIC */
-+static inline void
-+sclp_ascebc_str(unsigned char *str, int nr)
-+{
-+	(MACHINE_IS_VM) ? ASCEBC(str, nr) : ASCEBC_500(str, nr);
-+}
-+
-+#endif	 /* __SCLP_H__ */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_con.c drivers/s390/char/sclp_con.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_con.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/sclp_con.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,245 @@
-+/*
-+ *  drivers/s390/char/sclp_con.c
-+ *    SCLP line mode console driver
-+ *
-+ *  S390 version
-+ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-+ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/kmod.h>
-+#include <linux/console.h>
-+#include <linux/init.h>
-+#include <linux/timer.h>
-+#include <linux/sched.h>
-+#include <linux/bootmem.h>
-+
-+#include "sclp.h"
-+#include "sclp_rw.h"
-+
-+#define SCLP_CON_PRINT_HEADER "sclp console driver: "
-+
-+#define sclp_console_major 4		/* TTYAUX_MAJOR */
-+#define sclp_console_minor 64
-+#define sclp_console_name  "ttyS"
-+
-+/* Lock to guard over changes to global variables */
-+static spinlock_t sclp_con_lock;
-+/* List of free pages that can be used for console output buffering */
-+static struct list_head sclp_con_pages;
-+/* List of full struct sclp_buffer structures ready for output */
-+static struct list_head sclp_con_outqueue;
-+/* Counter how many buffers are emitted (max 1) and how many */
-+/* are on the output queue. */
-+static int sclp_con_buffer_count;
-+/* Pointer to current console buffer */
-+static struct sclp_buffer *sclp_conbuf;
-+/* Timer for delayed output of console messages */
-+static struct timer_list sclp_con_timer;
-+
-+/* Output format for console messages */
-+static unsigned short sclp_con_columns;
-+static unsigned short sclp_con_width_htab;
-+
-+static void
-+sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
-+{
-+	unsigned long flags;
-+	struct sclp_buffer *next;
-+	void *page;
-+
-+	/* Ignore return code - because console-writes aren't critical,
-+	   we do without a sophisticated error recovery mechanism.  */
-+	page = sclp_unmake_buffer(buffer);
-+	spin_lock_irqsave(&sclp_con_lock, flags);
-+	/* Remove buffer from outqueue */
-+	list_del(&buffer->list);
-+	sclp_con_buffer_count--;
-+	list_add_tail((struct list_head *) page, &sclp_con_pages);
-+	/* Check if there is a pending buffer on the out queue. */
-+	next = NULL;
-+	if (!list_empty(&sclp_con_outqueue))
-+		next = list_entry(sclp_con_outqueue.next,
-+				  struct sclp_buffer, list);
-+	spin_unlock_irqrestore(&sclp_con_lock, flags);
-+	if (next != NULL)
-+		sclp_emit_buffer(next, sclp_conbuf_callback);
-+}
-+
-+static inline void
-+sclp_conbuf_emit(void)
-+{
-+	struct sclp_buffer* buffer;
-+	unsigned long flags;
-+	int count;
-+
-+	spin_lock_irqsave(&sclp_con_lock, flags);
-+	buffer = sclp_conbuf;
-+	sclp_conbuf = NULL;
-+	if (buffer == NULL) {
-+		spin_unlock_irqrestore(&sclp_con_lock, flags);
-+		return;
-+	}
-+	list_add_tail(&buffer->list, &sclp_con_outqueue);
-+	count = sclp_con_buffer_count++;
-+	spin_unlock_irqrestore(&sclp_con_lock, flags);
-+	if (count == 0)
-+		sclp_emit_buffer(buffer, sclp_conbuf_callback);
-+}
-+
-+/*
-+ * When this routine is called from the timer then we flush the
-+ * temporary write buffer without further waiting on a final new line.
-+ */
-+static void
-+sclp_console_timeout(unsigned long data)
-+{
-+	sclp_conbuf_emit();
-+}
-+
-+/*
-+ * Writes the given message to S390 system console
-+ */
-+static void
-+sclp_console_write(struct console *console, const char *message,
-+		   unsigned int count)
-+{
-+	unsigned long flags;
-+	void *page;
-+	int written;
-+
-+	if (count == 0)
-+		return;
-+	spin_lock_irqsave(&sclp_con_lock, flags);
-+	/*
-+	 * process escape characters, write message into buffer,
-+	 * send buffer to SCLP
-+	 */
-+	do {
-+		/* make sure we have a console output buffer */
-+		if (sclp_conbuf == NULL) {
-+			while (list_empty(&sclp_con_pages)) {
-+				spin_unlock_irqrestore(&sclp_con_lock, flags);
-+				sclp_sync_wait();
-+				spin_lock_irqsave(&sclp_con_lock, flags);
-+			}
-+			page = sclp_con_pages.next;
-+			list_del((struct list_head *) page);
-+			sclp_conbuf = sclp_make_buffer(page, sclp_con_columns,
-+						       sclp_con_width_htab);
-+		}
-+		/* try to write the string to the current output buffer */
-+		written = sclp_write(sclp_conbuf, (const unsigned char *)
-+				     message, count, 0);
-+		if (written == -EFAULT || written == count)
-+			break;
-+		/*
-+		 * Not all characters could be written to the current
-+		 * output buffer. Emit the buffer, create a new buffer
-+		 * and then output the rest of the string.
-+		 */
-+		spin_unlock_irqrestore(&sclp_con_lock, flags);
-+		sclp_conbuf_emit();
-+		spin_lock_irqsave(&sclp_con_lock, flags);
-+		message += written;
-+		count -= written;
-+	} while (count > 0);
-+	/* Setup timer to output current console buffer after 1/10 second */
-+	if (sclp_conbuf != NULL && sclp_chars_in_buffer(sclp_conbuf) != 0 &&
-+	    !timer_pending(&sclp_con_timer)) {
-+		init_timer(&sclp_con_timer);
-+		sclp_con_timer.function = sclp_console_timeout;
-+		sclp_con_timer.data = 0UL;
-+		sclp_con_timer.expires = jiffies + HZ/10;
-+		add_timer(&sclp_con_timer);
-+	}
-+	spin_unlock_irqrestore(&sclp_con_lock, flags);
-+}
-+
-+/* returns the device number of the SCLP console */
-+static kdev_t
-+sclp_console_device(struct console *c)
-+{
-+	return	mk_kdev(sclp_console_major, sclp_console_minor);
-+}
-+
-+/*
-+ * This routine is called from panic when the kernel
-+ * is going to give up. We have to make sure that all buffers
-+ * will be flushed to the SCLP.
-+ */
-+static void
-+sclp_console_unblank(void)
-+{
-+	unsigned long flags;
-+
-+	sclp_conbuf_emit();
-+	spin_lock_irqsave(&sclp_con_lock, flags);
-+	if (timer_pending(&sclp_con_timer))
-+		del_timer(&sclp_con_timer);
-+	while (sclp_con_buffer_count > 0) {
-+		spin_unlock_irqrestore(&sclp_con_lock, flags);
-+		sclp_sync_wait();
-+		spin_lock_irqsave(&sclp_con_lock, flags);
-+	}
-+	spin_unlock_irqrestore(&sclp_con_lock, flags);
-+}
-+
-+/*
-+ * used to register the SCLP console to the kernel and to
-+ * give printk necessary information
-+ */
-+static struct console sclp_console =
-+{
-+	.name = sclp_console_name,
-+	.write = sclp_console_write,
-+	.device = sclp_console_device,
-+	.unblank = sclp_console_unblank,
-+	.flags = CON_PRINTBUFFER,
-+	.index = 0 /* ttyS0 */
-+};
-+
-+/*
-+ * called by console_init() in drivers/char/tty_io.c at boot-time.
-+ */
-+void __init
-+sclp_console_init(void)
-+{
-+	void *page;
-+	int i;
-+
-+	if (!CONSOLE_IS_SCLP)
-+		return;
-+	if (sclp_rw_init() != 0)
-+		return;
-+	/* Allocate pages for output buffering */
-+	INIT_LIST_HEAD(&sclp_con_pages);
-+	for (i = 0; i < MAX_CONSOLE_PAGES; i++) {
-+		page = alloc_bootmem_low_pages(PAGE_SIZE);
-+		if (page == NULL)
-+			return;
-+		list_add_tail((struct list_head *) page, &sclp_con_pages);
-+	}
-+	INIT_LIST_HEAD(&sclp_con_outqueue);
-+	spin_lock_init(&sclp_con_lock);
-+	sclp_con_buffer_count = 0;
-+	sclp_conbuf = NULL;
-+	init_timer(&sclp_con_timer);
-+
-+	/* Set output format */
-+	if (MACHINE_IS_VM)
-+		/*
-+		 * save 4 characters for the CPU number
-+		 * written at start of each line by VM/CP
-+		 */
-+		sclp_con_columns = 76;
-+	else
-+		sclp_con_columns = 80;
-+	sclp_con_width_htab = 8;
-+
-+	/* enable printk-access to this driver */
-+	register_console(&sclp_console);
-+}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_cpi.c drivers/s390/char/sclp_cpi.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_cpi.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/sclp_cpi.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,244 @@
-+/*
-+ * Author: Martin Peschke <mpeschke at de.ibm.com>
-+ * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation
-+ *
-+ * SCLP Control-Program Identification.
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/kmod.h>
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/timer.h>
-+#include <linux/string.h>
-+#include <linux/errno.h>
-+#include <linux/slab.h>
-+#include <asm/ebcdic.h>
-+#include <asm/semaphore.h>
-+
-+#include "sclp.h"
-+#include "sclp_rw.h"
-+
-+#define CPI_LENGTH_SYSTEM_TYPE	8
-+#define CPI_LENGTH_SYSTEM_NAME	8
-+#define CPI_LENGTH_SYSPLEX_NAME	8
-+
-+struct cpi_evbuf {
-+	struct evbuf_header header;
-+	u8	id_format;
-+	u8	reserved0;
-+	u8	system_type[CPI_LENGTH_SYSTEM_TYPE];
-+	u64	reserved1;
-+	u8	system_name[CPI_LENGTH_SYSTEM_NAME];
-+	u64	reserved2;
-+	u64	system_level;
-+	u64	reserved3;
-+	u8	sysplex_name[CPI_LENGTH_SYSPLEX_NAME];
-+	u8	reserved4[16];
-+} __attribute__((packed));
-+
-+struct cpi_sccb {
-+	struct sccb_header header;
-+	struct cpi_evbuf cpi_evbuf;
-+} __attribute__((packed));
-+
-+/* Event type structure for write message and write priority message */
-+static struct sclp_register sclp_cpi_event =
-+{
-+	.send_mask = EvTyp_CtlProgIdent_Mask
-+};
-+
-+MODULE_AUTHOR(
-+	"Martin Peschke, IBM Deutschland Entwicklung GmbH "
-+	"<mpeschke at de.ibm.com>");
-+
-+MODULE_DESCRIPTION(
-+	"identify this operating system instance to the S/390 "
-+	"or zSeries hardware");
-+
-+static char *system_name = NULL;
-+MODULE_PARM(system_name, "s");
-+MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters");
-+
-+static char *sysplex_name = NULL;
-+#ifdef ALLOW_SYSPLEX_NAME
-+MODULE_PARM(sysplex_name, "s");
-+MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters");
-+#endif
-+
-+/* use default value for this field (as well as for system level) */
-+static char *system_type = "LINUX";
-+
-+static int
-+cpi_check_parms(void)
-+{
-+	/* reject if no system type specified */
-+	if (!system_type) {
-+		printk("cpi: bug: no system type specified\n");
-+		return -EINVAL;
-+	}
-+
-+	/* reject if system type larger than 8 characters */
-+	if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) {
-+		printk("cpi: bug: system type has length of %li characters - "
-+		       "only %i characters supported\n",
-+		       strlen(system_type), CPI_LENGTH_SYSTEM_TYPE);
-+		return -EINVAL;
-+	}
-+
-+	/* reject if no system name specified */
-+	if (!system_name) {
-+		printk("cpi: no system name specified\n");
-+		return -EINVAL;
-+	}
-+
-+	/* reject if system name larger than 8 characters */
-+	if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) {
-+		printk("cpi: system name has length of %li characters - "
-+		       "only %i characters supported\n",
-+		       strlen(system_name), CPI_LENGTH_SYSTEM_NAME);
-+		return -EINVAL;
-+	}
-+
-+	/* reject if specified sysplex name larger than 8 characters */
-+	if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) {
-+		printk("cpi: sysplex name has length of %li characters"
-+		       " - only %i characters supported\n",
-+		       strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME);
-+		return -EINVAL;
-+	}
-+	return 0;
-+}
-+
-+static void
-+cpi_callback(struct sclp_req *req, void *data)
-+{
-+	struct semaphore *sem;
-+
-+	sem = (struct semaphore *) data;
-+	up(sem);
-+}
-+
-+static struct sclp_req *
-+cpi_prepare_req(void)
-+{
-+	struct sclp_req *req;
-+	struct cpi_sccb *sccb;
-+	struct cpi_evbuf *evb;
-+
-+	req = (struct sclp_req *) kmalloc(sizeof(struct sclp_req), GFP_KERNEL);
-+	if (req == NULL)
-+		return ERR_PTR(-ENOMEM);
-+	sccb = (struct cpi_sccb *) get_free_page(GFP_KERNEL | GFP_DMA);
-+	if (sccb == NULL) {
-+		kfree(req);
-+		return ERR_PTR(-ENOMEM);
-+	}
-+	memset(sccb, 0, sizeof(struct cpi_sccb));
-+
-+	/* setup SCCB for Control-Program Identification */
-+	sccb->header.length = sizeof(struct cpi_sccb);
-+	sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf);
-+	sccb->cpi_evbuf.header.type = 0x0B;
-+	evb = &sccb->cpi_evbuf;
-+
-+	/* set system type */
-+	memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE);
-+	memcpy(evb->system_type, system_type, strlen(system_type));
-+	sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE);
-+	EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE);
-+
-+	/* set system name */
-+	memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME);
-+	memcpy(evb->system_name, system_name, strlen(system_name));
-+	sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME);
-+	EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME);
-+
-+	/* set sytem level */
-+	evb->system_level = LINUX_VERSION_CODE;
-+
-+	/* set sysplex name */
-+	if (sysplex_name) {
-+		memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME);
-+		memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name));
-+		sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
-+		EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
-+	}
-+
-+	/* prepare request data structure presented to SCLP driver */
-+	req->command = SCLP_CMDW_WRITEDATA;
-+	req->sccb = sccb;
-+	req->status = SCLP_REQ_FILLED;
-+	req->callback = cpi_callback;
-+	return req;
-+}
-+
-+static void
-+cpi_free_req(struct sclp_req *req)
-+{
-+	free_page((unsigned long) req->sccb);
-+	kfree(req);
-+}
-+
-+static int __init
-+cpi_module_init(void)
-+{
-+	struct semaphore sem;
-+	struct sclp_req *req;
-+	int rc;
-+
-+	rc = cpi_check_parms();
-+	if (rc)
-+		return rc;
-+
-+	rc = sclp_register(&sclp_cpi_event);
-+	if (rc) {
-+		/* could not register sclp event. Die. */
-+		printk("cpi: could not register to hardware console.\n");
-+		return -EINVAL;
-+	}
-+	if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) {
-+		printk("cpi: no control program identification support\n");
-+		sclp_unregister(&sclp_cpi_event);
-+		return -ENOTSUPP;
-+	}
-+
-+	req = cpi_prepare_req();
-+	if (IS_ERR(req)) {
-+		printk("cpi: couldn't allocate request\n");
-+		sclp_unregister(&sclp_cpi_event);
-+		return PTR_ERR(req);
-+	}
-+
-+	/* Prepare semaphore */
-+	sema_init(&sem, 0);
-+	req->callback_data = &sem;
-+	/* Add request to sclp queue */
-+	sclp_add_request(req);
-+	/* make "insmod" sleep until callback arrives */
-+	down(&sem);
-+
-+	rc = ((struct cpi_sccb *) req->sccb)->header.response_code;
-+	if (rc != 0x0020) {
-+		printk("cpi: failed with response code 0x%x\n", rc);
-+		rc = -ECOMM;
-+	} else
-+		rc = 0;
-+
-+	cpi_free_req(req);
-+	sclp_unregister(&sclp_cpi_event);
-+
-+	return rc;
-+}
-+
-+
-+static void __exit cpi_module_exit(void)
-+{
-+}
-+
-+
-+/* declare driver module init/cleanup functions */
-+module_init(cpi_module_init);
-+module_exit(cpi_module_exit);
-+
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_rw.c drivers/s390/char/sclp_rw.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_rw.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/sclp_rw.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,496 @@
-+/*
-+ *  drivers/s390/char/sclp_rw.c
-+ *     driver: reading from and writing to system console on S/390 via SCLP
-+ *
-+ *  S390 version
-+ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-+ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/kmod.h>
-+#include <linux/types.h>
-+#include <linux/errno.h>
-+#include <linux/string.h>
-+#include <linux/spinlock.h>
-+#include <linux/ctype.h>
-+#include <asm/uaccess.h>
-+
-+#include "sclp.h"
-+#include "sclp_rw.h"
-+
-+#define SCLP_RW_PRINT_HEADER "sclp low level driver: "
-+
-+/*
-+ * The room for the SCCB (only for writing) is not equal to a pages size
-+ * (as it is specified as the maximum size in the the SCLP ducumentation)
-+ * because of the additional data structure described above.
-+ */
-+#define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer))
-+
-+/* Event type structure for write message and write priority message */
-+static struct sclp_register sclp_rw_event = {
-+	.send_mask = EvTyp_Msg_Mask | EvTyp_PMsgCmd_Mask
-+};
-+
-+/*
-+ * Setup a sclp write buffer. Gets a page as input (4K) and returns
-+ * a pointer to a struct sclp_buffer structure that is located at the
-+ * end of the input page. This reduces the buffer space by a few
-+ * bytes but simplifies things.
-+ */
-+struct sclp_buffer *
-+sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
-+{
-+	struct sclp_buffer *buffer;
-+	struct write_sccb *sccb;
-+
-+	sccb = (struct write_sccb *) page;
-+	/*
-+	 * We keep the struct sclp_buffer structure at the end
-+	 * of the sccb page.
-+	 */
-+	buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1;
-+	buffer->sccb = sccb;
-+	buffer->retry_count = 0;
-+	init_timer(&buffer->retry_timer);
-+	buffer->mto_number = 0;
-+	buffer->mto_char_sum = 0;
-+	buffer->current_line = NULL;
-+	buffer->current_length = 0;
-+	buffer->columns = columns;
-+	buffer->htab = htab;
-+
-+	/* initialize sccb */
-+	memset(sccb, 0, sizeof(struct write_sccb));
-+	sccb->header.length = sizeof(struct write_sccb);
-+	sccb->msg_buf.header.length = sizeof(struct msg_buf);
-+	sccb->msg_buf.header.type = EvTyp_Msg;
-+	sccb->msg_buf.mdb.header.length = sizeof(struct mdb);
-+	sccb->msg_buf.mdb.header.type = 1;
-+	sccb->msg_buf.mdb.header.tag = 0xD4C4C240;	/* ebcdic "MDB " */
-+	sccb->msg_buf.mdb.header.revision_code = 1;
-+	sccb->msg_buf.mdb.go.length = sizeof(struct go);
-+	sccb->msg_buf.mdb.go.type = 1;
-+
-+	return buffer;
-+}
-+
-+/*
-+ * Return a pointer to the orignal page that has been used to create
-+ * the buffer.
-+ */
-+void *
-+sclp_unmake_buffer(struct sclp_buffer *buffer)
-+{
-+	return buffer->sccb;
-+}
-+
-+/*
-+ * Initialize a new Message Text Object (MTO) at the end of the provided buffer
-+ * with enough room for max_len characters. Return 0 on success.
-+ */
-+static int
-+sclp_initialize_mto(struct sclp_buffer *buffer, int max_len)
-+{
-+	struct write_sccb *sccb;
-+	struct mto *mto;
-+	int mto_size;
-+
-+	/* max size of new Message Text Object including message text  */
-+	mto_size = sizeof(struct mto) + max_len;
-+
-+	/* check if current buffer sccb can contain the mto */
-+	sccb = buffer->sccb;
-+	if ((MAX_SCCB_ROOM - sccb->header.length) < mto_size)
-+		return -ENOMEM;
-+
-+	/* find address of new message text object */
-+	mto = (struct mto *)(((addr_t) sccb) + sccb->header.length);
-+
-+	/*
-+	 * fill the new Message-Text Object,
-+	 * starting behind the former last byte of the SCCB
-+	 */
-+	memset(mto, 0, sizeof(struct mto));
-+	mto->length = sizeof(struct mto);
-+	mto->type = 4;	/* message text object */
-+	mto->line_type_flags = LnTpFlgs_EndText; /* end text */
-+
-+	/* set pointer to first byte after struct mto. */
-+	buffer->current_line = (char *) (mto + 1);
-+	buffer->current_length = 0;
-+
-+	return 0;
-+}
-+
-+/*
-+ * Finalize MTO initialized by sclp_initialize_mto(), updating the sizes of
-+ * MTO, enclosing MDB, event buffer and SCCB.
-+ */
-+static void
-+sclp_finalize_mto(struct sclp_buffer *buffer)
-+{
-+	struct write_sccb *sccb;
-+	struct mto *mto;
-+	int str_len, mto_size;
-+
-+	str_len = buffer->current_length;
-+	buffer->current_line = NULL;
-+	buffer->current_length = 0;
-+
-+	/* real size of new Message Text Object including message text	*/
-+	mto_size = sizeof(struct mto) + str_len;
-+
-+	/* find address of new message text object */
-+	sccb = buffer->sccb;
-+	mto = (struct mto *)(((addr_t) sccb) + sccb->header.length);
-+
-+	/* set size of message text object */
-+	mto->length = mto_size;
-+
-+	/*
-+	 * update values of sizes
-+	 * (SCCB, Event(Message) Buffer, Message Data Block)
-+	 */
-+	sccb->header.length += mto_size;
-+	sccb->msg_buf.header.length += mto_size;
-+	sccb->msg_buf.mdb.header.length += mto_size;
-+
-+	/*
-+	 * count number of buffered messages (= number of Message Text
-+	 * Objects) and number of buffered characters
-+	 * for the SCCB currently used for buffering and at all
-+	 */
-+	buffer->mto_number++;
-+	buffer->mto_char_sum += str_len;
-+}
-+
-+/*
-+ * processing of a message including escape characters,
-+ * returns number of characters written to the output sccb
-+ * ("processed" means that is not guaranteed that the character have already
-+ *  been sent to the SCLP but that it will be done at least next time the SCLP
-+ *  is not busy)
-+ */
-+int
-+sclp_write(struct sclp_buffer *buffer,
-+	   const unsigned char *msg, int count, int from_user)
-+{
-+	int spaces, i_msg;
-+	char ch;
-+	int rc;
-+
-+	/*
-+	 * parse msg for escape sequences (\t,\v ...) and put formated
-+	 * msg into an mto (created by sclp_initialize_mto).
-+	 *
-+	 * We have to do this work ourselfs because there is no support for
-+	 * these characters on the native machine and only partial support
-+	 * under VM (Why does VM interpret \n but the native machine doesn't ?)
-+	 *
-+	 * Depending on i/o-control setting the message is always written
-+	 * immediately or we wait for a final new line maybe coming with the
-+	 * next message. Besides we avoid a buffer overrun by writing its
-+	 * content.
-+	 *
-+	 * RESTRICTIONS:
-+	 *
-+	 * \r and \b work within one line because we are not able to modify
-+	 * previous output that have already been accepted by the SCLP.
-+	 *
-+	 * \t combined with following \r is not correctly represented because
-+	 * \t is expanded to some spaces but \r does not know about a
-+	 * previous \t and decreases the current position by one column.
-+	 * This is in order to a slim and quick implementation.
-+	 */
-+	for (i_msg = 0; i_msg < count; i_msg++) {
-+		if (from_user) {
-+			if (get_user(ch, msg + i_msg) != 0)
-+				return -EFAULT;
-+		} else
-+			ch = msg[i_msg];
-+
-+		switch (ch) {
-+		case '\n':	/* new line, line feed (ASCII)	*/
-+			/* check if new mto needs to be created */
-+			if (buffer->current_line == NULL) {
-+				rc = sclp_initialize_mto(buffer, 0);
-+				if (rc)
-+					return i_msg;
-+			}
-+			sclp_finalize_mto(buffer);
-+			break;
-+		case '\a':	/* bell, one for several times	*/
-+			/* set SCLP sound alarm bit in General Object */
-+			buffer->sccb->msg_buf.mdb.go.general_msg_flags |=
-+				GnrlMsgFlgs_SndAlrm;
-+			break;
-+		case '\t':	/* horizontal tabulator	 */
-+			/* check if new mto needs to be created */
-+			if (buffer->current_line == NULL) {
-+				rc = sclp_initialize_mto(buffer,
-+							 buffer->columns);
-+				if (rc)
-+					return i_msg;
-+			}
-+			/* "go to (next htab-boundary + 1, same line)" */
-+			do {
-+				if (buffer->current_length >= buffer->columns)
-+					break;
-+				/* ok, add a blank */
-+				*buffer->current_line++ = 0x40;
-+				buffer->current_length++;
-+			} while (buffer->current_length % buffer->htab);
-+			break;
-+		case '\f':	/* form feed  */
-+		case '\v':	/* vertical tabulator  */
-+			/* "go to (actual column, actual line + 1)" */
-+			/* = new line, leading spaces */
-+			if (buffer->current_line != NULL) {
-+				spaces = buffer->current_length;
-+				sclp_finalize_mto(buffer);
-+				rc = sclp_initialize_mto(buffer,
-+							 buffer->columns);
-+				if (rc)
-+					return i_msg;
-+				memset(buffer->current_line, 0x40, spaces);
-+				buffer->current_line += spaces;
-+				buffer->current_length = spaces;
-+			} else {
-+				/* one an empty line this is the same as \n */
-+				rc = sclp_initialize_mto(buffer,
-+							 buffer->columns);
-+				if (rc)
-+					return i_msg;
-+				sclp_finalize_mto(buffer);
-+			}
-+			break;
-+		case '\b':	/* backspace  */
-+			/* "go to (actual column - 1, actual line)" */
-+			/* decrement counter indicating position, */
-+			/* do not remove last character */
-+			if (buffer->current_line != NULL &&
-+			    buffer->current_length > 0) {
-+				buffer->current_length--;
-+				buffer->current_line--;
-+			}
-+			break;
-+		case 0x00:	/* end of string  */
-+			/* transfer current line to SCCB */
-+			if (buffer->current_line != NULL)
-+				sclp_finalize_mto(buffer);
-+			/* skip the rest of the message including the 0 byte */
-+			i_msg = count - 1;
-+			break;
-+		default:	/* no escape character	*/
-+			/* do not output unprintable characters */
-+			if (!isprint(ch))
-+				break;
-+			/* check if new mto needs to be created */
-+			if (buffer->current_line == NULL) {
-+				rc = sclp_initialize_mto(buffer,
-+							 buffer->columns);
-+				if (rc)
-+					return i_msg;
-+			}
-+			*buffer->current_line++ = sclp_ascebc(ch);
-+			buffer->current_length++;
-+			break;
-+		}
-+		/* check if current mto is full */
-+		if (buffer->current_line != NULL &&
-+		    buffer->current_length >= buffer->columns)
-+			sclp_finalize_mto(buffer);
-+	}
-+
-+	/* return number of processed characters */
-+	return i_msg;
-+}
-+
-+/*
-+ * Return the number of free bytes in the sccb
-+ */
-+int
-+sclp_buffer_space(struct sclp_buffer *buffer)
-+{
-+	int count;
-+
-+	count = MAX_SCCB_ROOM - buffer->sccb->header.length;
-+	if (buffer->current_line != NULL)
-+		count -= sizeof(struct mto) + buffer->current_length;
-+	return count;
-+}
-+
-+/*
-+ * Return number of characters in buffer
-+ */
-+int
-+sclp_chars_in_buffer(struct sclp_buffer *buffer)
-+{
-+	int count;
-+
-+	count = buffer->mto_char_sum;
-+	if (buffer->current_line != NULL)
-+		count += buffer->current_length;
-+	return count;
-+}
-+
-+/*
-+ * sets or provides some values that influence the drivers behaviour
-+ */
-+void
-+sclp_set_columns(struct sclp_buffer *buffer, unsigned short columns)
-+{
-+	buffer->columns = columns;
-+	if (buffer->current_line != NULL &&
-+	    buffer->current_length > buffer->columns)
-+		sclp_finalize_mto(buffer);
-+}
-+
-+void
-+sclp_set_htab(struct sclp_buffer *buffer, unsigned short htab)
-+{
-+	buffer->htab = htab;
-+}
-+
-+/*
-+ * called by sclp_console_init and/or sclp_tty_init
-+ */
-+int
-+sclp_rw_init(void)
-+{
-+	static int init_done = 0;
-+	int rc;
-+
-+	if (init_done)
-+		return 0;
-+
-+	rc = sclp_register(&sclp_rw_event);
-+	if (rc == 0)
-+		init_done = 1;
-+	return rc;
-+}
-+
-+static void
-+sclp_buffer_retry(unsigned long data)
-+{
-+	struct sclp_buffer *buffer = (struct sclp_buffer *) data;
-+	buffer->request.status = SCLP_REQ_FILLED;
-+	buffer->sccb->header.response_code = 0x0000;
-+	sclp_add_request(&buffer->request);
-+}
-+
-+#define SCLP_BUFFER_MAX_RETRY		5
-+#define	SCLP_BUFFER_RETRY_INTERVAL	2
-+
-+/*
-+ * second half of Write Event Data-function that has to be done after
-+ * interruption indicating completion of Service Call.
-+ */
-+static void
-+sclp_writedata_callback(struct sclp_req *request, void *data)
-+{
-+	int rc;
-+	struct sclp_buffer *buffer;
-+	struct write_sccb *sccb;
-+
-+	buffer = (struct sclp_buffer *) data;
-+	sccb = buffer->sccb;
-+
-+	if (request->status == SCLP_REQ_FAILED) {
-+		if (buffer->callback != NULL)
-+			buffer->callback(buffer, -EIO);
-+		return;
-+	}
-+	/* check SCLP response code and choose suitable action	*/
-+	switch (sccb->header.response_code) {
-+	case 0x0020 :
-+		/* Normal completion, buffer processed, message(s) sent */
-+		rc = 0;
-+		break;
-+
-+	case 0x0340: /* Contained SCLP equipment check */
-+		if (buffer->retry_count++ > SCLP_BUFFER_MAX_RETRY) {
-+			rc = -EIO;
-+			break;
-+		}
-+		/* remove processed buffers and requeue rest */
-+		if (sclp_remove_processed((struct sccb_header *) sccb) > 0) {
-+			/* not all buffers were processed */
-+			sccb->header.response_code = 0x0000;
-+			buffer->request.status = SCLP_REQ_FILLED;
-+			sclp_add_request(request);
-+			return;
-+		}
-+		rc = 0;
-+		break;
-+
-+	case 0x0040: /* SCLP equipment check */
-+	case 0x05f0: /* Target resource in improper state */
-+		if (buffer->retry_count++ > SCLP_BUFFER_MAX_RETRY) {
-+			rc = -EIO;
-+			break;
-+		}
-+		/* wait some time, then retry request */
-+		buffer->retry_timer.function = sclp_buffer_retry;
-+		buffer->retry_timer.data = (unsigned long) buffer;
-+		buffer->retry_timer.expires = jiffies +
-+						SCLP_BUFFER_RETRY_INTERVAL*HZ;
-+		add_timer(&buffer->retry_timer);
-+		return;
-+
-+	default:
-+		if (sccb->header.response_code == 0x71f0)
-+			rc = -ENOMEM;
-+		else
-+			rc = -EINVAL;
-+		break;
-+	}
-+	if (buffer->callback != NULL)
-+		buffer->callback(buffer, rc);
-+}
-+
-+/*
-+ * Setup the request structure in the struct sclp_buffer to do SCLP Write
-+ * Event Data and pass the request to the core SCLP loop.
-+ */
-+void
-+sclp_emit_buffer(struct sclp_buffer *buffer,
-+		 void (*callback)(struct sclp_buffer *, int))
-+{
-+	struct write_sccb *sccb;
-+
-+	/* add current line if there is one */
-+	if (buffer->current_line != NULL)
-+		sclp_finalize_mto(buffer);
-+
-+	/* Are there messages in the output buffer ? */
-+	if (buffer->mto_number == 0) {
-+		if (callback != NULL)
-+			callback(buffer, 0);
-+		return;
-+	}
-+
-+	sccb = buffer->sccb;
-+	if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask)
-+		/* Use normal write message */
-+		sccb->msg_buf.header.type = EvTyp_Msg;
-+	else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask)
-+		/* Use write priority message */
-+		sccb->msg_buf.header.type = EvTyp_PMsgCmd;
-+	else {
-+		if (callback != NULL)
-+			callback(buffer, -ENOSYS);
-+		return;
-+	}
-+	buffer->request.command = SCLP_CMDW_WRITEDATA;
-+	buffer->request.status = SCLP_REQ_FILLED;
-+	buffer->request.callback = sclp_writedata_callback;
-+	buffer->request.callback_data = buffer;
-+	buffer->request.sccb = sccb;
-+	buffer->callback = callback;
-+	sclp_add_request(&buffer->request);
-+}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_rw.h drivers/s390/char/sclp_rw.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_rw.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/sclp_rw.h	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,98 @@
-+/*
-+ *  drivers/s390/char/sclp_rw.h
-+ *    interface to the SCLP-read/write driver
-+ *
-+ *  S390 version
-+ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-+ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ */
-+
-+#ifndef __SCLP_RW_H__
-+#define __SCLP_RW_H__
-+
-+#include <linux/list.h>
-+#include <linux/timer.h>
-+
-+struct mto {
-+	u16 length;
-+	u16 type;
-+	u16 line_type_flags;
-+	u8  alarm_control;
-+	u8  _reserved[3];
-+} __attribute__((packed));
-+
-+struct go {
-+	u16 length;
-+	u16 type;
-+	u32 domid;
-+	u8  hhmmss_time[8];
-+	u8  th_time[3];
-+	u8  reserved_0;
-+	u8  dddyyyy_date[7];
-+	u8  _reserved_1;
-+	u16 general_msg_flags;
-+	u8  _reserved_2[10];
-+	u8  originating_system_name[8];
-+	u8  job_guest_name[8];
-+} __attribute__((packed));
-+
-+struct mdb_header {
-+	u16 length;
-+	u16 type;
-+	u32 tag;
-+	u32 revision_code;
-+} __attribute__((packed));
-+
-+struct mdb {
-+	struct mdb_header header;
-+	struct go go;
-+} __attribute__((packed));
-+
-+struct msg_buf {
-+	struct evbuf_header header;
-+	struct mdb mdb;
-+} __attribute__((packed));
-+
-+struct write_sccb {
-+	struct sccb_header header;
-+	struct msg_buf msg_buf;
-+} __attribute__((packed));
-+
-+/* The number of empty mto buffers that can be contained in a single sccb. */
-+#define NR_EMPTY_MTO_PER_SCCB ((PAGE_SIZE - sizeof(struct sclp_buffer) - \
-+			sizeof(struct write_sccb)) / sizeof(struct mto))
-+
-+/*
-+ * data structure for information about list of SCCBs (only for writing),
-+ * will be located at the end of a SCCBs page
-+ */
-+struct sclp_buffer {
-+	struct list_head list;		/* list_head for sccb_info chain */
-+	struct sclp_req request;
-+	struct write_sccb *sccb;
-+	char *current_line;
-+	int current_length;
-+	int retry_count;
-+	struct timer_list retry_timer;
-+	/* output format settings */
-+	unsigned short columns;
-+	unsigned short htab;
-+	/* statistics about this buffer */
-+	unsigned int mto_char_sum;	/* # chars in sccb */
-+	unsigned int mto_number;	/* # mtos in sccb */
-+	/* Callback that is called after reaching final status. */
-+	void (*callback)(struct sclp_buffer *, int);
-+};
-+
-+int sclp_rw_init(void);
-+struct sclp_buffer *sclp_make_buffer(void *, unsigned short, unsigned short);
-+void *sclp_unmake_buffer(struct sclp_buffer *);
-+int sclp_buffer_space(struct sclp_buffer *);
-+int sclp_write(struct sclp_buffer *buffer, const unsigned char *, int, int);
-+void sclp_emit_buffer(struct sclp_buffer *,void (*)(struct sclp_buffer *,int));
-+void sclp_set_columns(struct sclp_buffer *, unsigned short);
-+void sclp_set_htab(struct sclp_buffer *, unsigned short);
-+int sclp_chars_in_buffer(struct sclp_buffer *);
-+
-+#endif	/* __SCLP_RW_H__ */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_tty.c drivers/s390/char/sclp_tty.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_tty.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/sclp_tty.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,828 @@
-+/*
-+ *  drivers/s390/char/sclp_tty.c
-+ *    SCLP line mode terminal driver.
-+ *
-+ *  S390 version
-+ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-+ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/kmod.h>
-+#include <linux/tty.h>
-+#include <linux/tty_driver.h>
-+#include <linux/sched.h>
-+#include <linux/wait.h>
-+#include <linux/slab.h>
-+#include <linux/interrupt.h>
-+#include <asm/uaccess.h>
-+
-+#include "ctrlchar.h"
-+#include "sclp.h"
-+#include "sclp_rw.h"
-+#include "sclp_tty.h"
-+
-+#define SCLP_TTY_PRINT_HEADER "sclp tty driver: "
-+
-+/*
-+ * size of a buffer that collects single characters coming in
-+ * via sclp_tty_put_char()
-+ */
-+#define SCLP_TTY_BUF_SIZE 512
-+
-+/*
-+ * There is exactly one SCLP terminal, so we can keep things simple
-+ * and allocate all variables statically.
-+ */
-+
-+/* Lock to guard over changes to global variables. */
-+static spinlock_t sclp_tty_lock;
-+/* List of free pages that can be used for console output buffering. */
-+static struct list_head sclp_tty_pages;
-+/* List of full struct sclp_buffer structures ready for output. */
-+static struct list_head sclp_tty_outqueue;
-+/* Counter how many buffers are emitted. */
-+static int sclp_tty_buffer_count;
-+/* Pointer to current console buffer. */
-+static struct sclp_buffer *sclp_ttybuf;
-+/* Timer for delayed output of console messages. */
-+static struct timer_list sclp_tty_timer;
-+/* Waitqueue to wait for buffers to get empty. */
-+static wait_queue_head_t sclp_tty_waitq;
-+
-+static struct tty_struct *sclp_tty;
-+static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE];
-+static unsigned short int sclp_tty_chars_count;
-+
-+static struct tty_driver sclp_tty_driver;
-+static struct tty_struct * sclp_tty_table[1];
-+static struct termios * sclp_tty_termios[1];
-+static struct termios * sclp_tty_termios_locked[1];
-+static int sclp_tty_refcount = 0;
-+
-+extern struct termios  tty_std_termios;
-+
-+static struct sclp_ioctls sclp_ioctls;
-+static struct sclp_ioctls sclp_ioctls_init =
-+{
-+	8,			/* 1 hor. tab. = 8 spaces */
-+	0,			/* no echo of input by this driver */
-+	80,			/* 80 characters/line */
-+	1,			/* write after 1/10 s without final new line */
-+	MAX_KMEM_PAGES,		/* quick fix: avoid __alloc_pages */
-+	MAX_KMEM_PAGES,		/* take 32/64 pages from kernel memory, */
-+	0,			/* do not convert to lower case */
-+	0x6c			/* to seprate upper and lower case */
-+				/* ('%' in EBCDIC) */
-+};
-+
-+/* This routine is called whenever we try to open a SCLP terminal. */
-+static int
-+sclp_tty_open(struct tty_struct *tty, struct file *filp)
-+{
-+	sclp_tty = tty;
-+	tty->driver_data = NULL;
-+	tty->low_latency = 0;
-+	return 0;
-+}
-+
-+/* This routine is called when the SCLP terminal is closed. */
-+static void
-+sclp_tty_close(struct tty_struct *tty, struct file *filp)
-+{
-+	if (tty->count > 1)
-+		return;
-+	sclp_tty = NULL;
-+}
-+
-+/* execute commands to control the i/o behaviour of the SCLP tty at runtime */
-+static int
-+sclp_tty_ioctl(struct tty_struct *tty, struct file * file,
-+	       unsigned int cmd, unsigned long arg)
-+{
-+	unsigned long flags;
-+	unsigned int obuf;
-+	int check;
-+	int rc;
-+
-+	if (tty->flags & (1 << TTY_IO_ERROR))
-+		return -EIO;
-+	rc = 0;
-+	check = 0;
-+	switch (cmd) {
-+	case TIOCSCLPSHTAB:
-+		/* set width of horizontal tab	*/
-+		if (get_user(sclp_ioctls.htab, (unsigned short *) arg))
-+			rc = -EFAULT;
-+		else
-+			check = 1;
-+		break;
-+	case TIOCSCLPGHTAB:
-+		/* get width of horizontal tab	*/
-+		if (put_user(sclp_ioctls.htab, (unsigned short *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPSECHO:
-+		/* enable/disable echo of input */
-+		if (get_user(sclp_ioctls.echo, (unsigned char *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPGECHO:
-+		/* Is echo of input enabled ?  */
-+		if (put_user(sclp_ioctls.echo, (unsigned char *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPSCOLS:
-+		/* set number of columns for output  */
-+		if (get_user(sclp_ioctls.columns, (unsigned short *) arg))
-+			rc = -EFAULT;
-+		else
-+			check = 1;
-+		break;
-+	case TIOCSCLPGCOLS:
-+		/* get number of columns for output  */
-+		if (put_user(sclp_ioctls.columns, (unsigned short *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPSNL:
-+		/* enable/disable writing without final new line character  */
-+		if (get_user(sclp_ioctls.final_nl, (signed char *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPGNL:
-+		/* Is writing without final new line character enabled ?  */
-+		if (put_user(sclp_ioctls.final_nl, (signed char *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPSOBUF:
-+		/*
-+		 * set the maximum buffers size for output, will be rounded
-+		 * up to next 4kB boundary and stored as number of SCCBs
-+		 * (4kB Buffers) limitation: 256 x 4kB
-+		 */
-+		if (get_user(obuf, (unsigned int *) arg) == 0) {
-+			if (obuf & 0xFFF)
-+				sclp_ioctls.max_sccb = (obuf >> 12) + 1;
-+			else
-+				sclp_ioctls.max_sccb = (obuf >> 12);
-+		} else
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPGOBUF:
-+		/* get the maximum buffers size for output  */
-+		obuf = sclp_ioctls.max_sccb << 12;
-+		if (put_user(obuf, (unsigned int *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPGKBUF:
-+		/* get the number of buffers got from kernel at startup */
-+		if (put_user(sclp_ioctls.kmem_sccb, (unsigned short *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPSCASE:
-+		/* enable/disable conversion from upper to lower case */
-+		if (get_user(sclp_ioctls.tolower, (unsigned char *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPGCASE:
-+		/* Is conversion from upper to lower case of input enabled? */
-+		if (put_user(sclp_ioctls.tolower, (unsigned char *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPSDELIM:
-+		/*
-+		 * set special character used for separating upper and
-+		 * lower case, 0x00 disables this feature
-+		 */
-+		if (get_user(sclp_ioctls.delim, (unsigned char *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPGDELIM:
-+		/*
-+		 * get special character used for separating upper and
-+		 * lower case, 0x00 disables this feature
-+		 */
-+		if (put_user(sclp_ioctls.delim, (unsigned char *) arg))
-+			rc = -EFAULT;
-+		break;
-+	case TIOCSCLPSINIT:
-+		/* set initial (default) sclp ioctls  */
-+		sclp_ioctls = sclp_ioctls_init;
-+		check = 1;
-+		break;
-+	default:
-+		rc = -ENOIOCTLCMD;
-+		break;
-+	}
-+	if (check) {
-+		spin_lock_irqsave(&sclp_tty_lock, flags);
-+		if (sclp_ttybuf != NULL) {
-+			sclp_set_htab(sclp_ttybuf, sclp_ioctls.htab);
-+			sclp_set_columns(sclp_ttybuf, sclp_ioctls.columns);
-+		}
-+		spin_unlock_irqrestore(&sclp_tty_lock, flags);
-+	}
-+	return rc;
-+}
-+
-+/*
-+ * This routine returns the numbers of characters the tty driver
-+ * will accept for queuing to be written.  This number is subject
-+ * to change as output buffers get emptied, or if the output flow
-+ * control is acted. This is not an exact number because not every
-+ * character needs the same space in the sccb. The worst case is
-+ * a string of newlines. Every newlines creates a new mto which
-+ * needs 8 bytes.
-+ */
-+static int
-+sclp_tty_write_room (struct tty_struct *tty)
-+{
-+	unsigned long flags;
-+	struct list_head *l;
-+	int count;
-+
-+	spin_lock_irqsave(&sclp_tty_lock, flags);
-+	count = 0;
-+	if (sclp_ttybuf != NULL)
-+		count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct mto);
-+	list_for_each(l, &sclp_tty_pages)
-+		count += NR_EMPTY_MTO_PER_SCCB;
-+	spin_unlock_irqrestore(&sclp_tty_lock, flags);
-+	return count;
-+}
-+
-+static void
-+sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
-+{
-+	unsigned long flags;
-+	struct sclp_buffer *next;
-+	void *page;
-+
-+	/* Ignore return code - because tty-writes aren't critical,
-+	   we do without a sophisticated error recovery mechanism.  */
-+	page = sclp_unmake_buffer(buffer);
-+	spin_lock_irqsave(&sclp_tty_lock, flags);
-+	/* Remove buffer from outqueue */
-+	list_del(&buffer->list);
-+	sclp_tty_buffer_count--;
-+	list_add_tail((struct list_head *) page, &sclp_tty_pages);
-+	/* Check if there is a pending buffer on the out queue. */
-+	next = NULL;
-+	if (!list_empty(&sclp_tty_outqueue))
-+		next = list_entry(sclp_tty_outqueue.next,
-+				  struct sclp_buffer, list);
-+	spin_unlock_irqrestore(&sclp_tty_lock, flags);
-+	if (next != NULL)
-+		sclp_emit_buffer(next, sclp_ttybuf_callback);
-+	wake_up(&sclp_tty_waitq);
-+	/* check if the tty needs a wake up call */
-+	if (sclp_tty != NULL) {
-+		if ((sclp_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-+		    sclp_tty->ldisc.write_wakeup)
-+			(sclp_tty->ldisc.write_wakeup)(sclp_tty);
-+		wake_up_interruptible(&sclp_tty->write_wait);
-+	}
-+}
-+
-+static inline void
-+__sclp_ttybuf_emit(struct sclp_buffer *buffer)
-+{
-+	unsigned long flags;
-+	int count;
-+
-+	spin_lock_irqsave(&sclp_tty_lock, flags);
-+	list_add_tail(&buffer->list, &sclp_tty_outqueue);
-+	count = sclp_tty_buffer_count++;
-+	spin_unlock_irqrestore(&sclp_tty_lock, flags);
-+
-+	if (count == 0)
-+		sclp_emit_buffer(buffer, sclp_ttybuf_callback);
-+}
-+
-+/*
-+ * When this routine is called from the timer then we flush the
-+ * temporary write buffer.
-+ */
-+static void
-+sclp_tty_timeout(unsigned long data)
-+{
-+	unsigned long flags;
-+	struct sclp_buffer *buf;
-+
-+	spin_lock_irqsave(&sclp_tty_lock, flags);
-+	buf = sclp_ttybuf;
-+	sclp_ttybuf = NULL;
-+	spin_unlock_irqrestore(&sclp_tty_lock, flags);
-+
-+	if (buf != NULL) {
-+		__sclp_ttybuf_emit(buf);
-+	}
-+}
-+
-+/*
-+ * Write a string to the sclp tty.
-+ */
-+static void
-+sclp_tty_write_string(const unsigned char *str, int count, int from_user)
-+{
-+	unsigned long flags;
-+	void *page;
-+	int written;
-+	struct sclp_buffer *buf;
-+
-+	if (count <= 0)
-+		return;
-+	spin_lock_irqsave(&sclp_tty_lock, flags);
-+	do {
-+		/* Create a sclp output buffer if none exists yet */
-+		if (sclp_ttybuf == NULL) {
-+			while (list_empty(&sclp_tty_pages)) {
-+				spin_unlock_irqrestore(&sclp_tty_lock, flags);
-+				if (in_interrupt())
-+					sclp_sync_wait();
-+				else
-+					wait_event(sclp_tty_waitq,
-+						!list_empty(&sclp_tty_pages));
-+				spin_lock_irqsave(&sclp_tty_lock, flags);
-+			}
-+			page = sclp_tty_pages.next;
-+			list_del((struct list_head *) page);
-+			sclp_ttybuf = sclp_make_buffer(page,
-+						       sclp_ioctls.columns,
-+						       sclp_ioctls.htab);
-+		}
-+		/* try to write the string to the current output buffer */
-+		written = sclp_write(sclp_ttybuf, str, count, from_user);
-+		if (written == -EFAULT || written == count)
-+			break;
-+		/*
-+		 * Not all characters could be written to the current
-+		 * output buffer. Emit the buffer, create a new buffer
-+		 * and then output the rest of the string.
-+		 */
-+		buf = sclp_ttybuf;
-+		sclp_ttybuf = NULL;
-+		spin_unlock_irqrestore(&sclp_tty_lock, flags);
-+		__sclp_ttybuf_emit(buf);
-+		spin_lock_irqsave(&sclp_tty_lock, flags);
-+		str += written;
-+		count -= written;
-+	} while (count > 0);
-+	/* Setup timer to output current console buffer after 1/10 second */
-+	if (sclp_ioctls.final_nl) {
-+		if (sclp_ttybuf != NULL &&
-+		    sclp_chars_in_buffer(sclp_ttybuf) != 0 &&
-+		    !timer_pending(&sclp_tty_timer)) {
-+			init_timer(&sclp_tty_timer);
-+			sclp_tty_timer.function = sclp_tty_timeout;
-+			sclp_tty_timer.data = 0UL;
-+			sclp_tty_timer.expires = jiffies + HZ/10;
-+			add_timer(&sclp_tty_timer);
-+		}
-+	} else {
-+		if (sclp_ttybuf != NULL &&
-+		    sclp_chars_in_buffer(sclp_ttybuf) != 0) {
-+			buf = sclp_ttybuf;
-+			sclp_ttybuf = NULL;
-+			spin_unlock_irqrestore(&sclp_tty_lock, flags);
-+			__sclp_ttybuf_emit(buf);
-+			spin_lock_irqsave(&sclp_tty_lock, flags);
-+		}
-+	}
-+	spin_unlock_irqrestore(&sclp_tty_lock, flags);
-+}
-+
-+/*
-+ * This routine is called by the kernel to write a series of characters to the
-+ * tty device. The characters may come from user space or kernel space. This
-+ * routine will return the number of characters actually accepted for writing.
-+ */
-+static int
-+sclp_tty_write(struct tty_struct *tty, int from_user,
-+	       const unsigned char *buf, int count)
-+{
-+	if (sclp_tty_chars_count > 0) {
-+		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
-+		sclp_tty_chars_count = 0;
-+	}
-+	sclp_tty_write_string(buf, count, from_user);
-+	return count;
-+}
-+
-+/*
-+ * This routine is called by the kernel to write a single character to the tty
-+ * device. If the kernel uses this routine, it must call the flush_chars()
-+ * routine (if defined) when it is done stuffing characters into the driver.
-+ *
-+ * Characters provided to sclp_tty_put_char() are buffered by the SCLP driver.
-+ * If the given character is a '\n' the contents of the SCLP write buffer
-+ * - including previous characters from sclp_tty_put_char() and strings from
-+ * sclp_write() without final '\n' - will be written.
-+ */
-+static void
-+sclp_tty_put_char(struct tty_struct *tty, unsigned char ch)
-+{
-+	sclp_tty_chars[sclp_tty_chars_count++] = ch;
-+	if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) {
-+		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
-+		sclp_tty_chars_count = 0;
-+	}
-+}
-+
-+/*
-+ * This routine is called by the kernel after it has written a series of
-+ * characters to the tty device using put_char().
-+ */
-+static void
-+sclp_tty_flush_chars(struct tty_struct *tty)
-+{
-+	if (sclp_tty_chars_count > 0) {
-+		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
-+		sclp_tty_chars_count = 0;
-+	}
-+}
-+
-+/*
-+ * This routine returns the number of characters in the write buffer of the
-+ * SCLP driver. The provided number includes all characters that are stored
-+ * in the SCCB (will be written next time the SCLP is not busy) as well as
-+ * characters in the write buffer (will not be written as long as there is a
-+ * final line feed missing).
-+ */
-+static int
-+sclp_tty_chars_in_buffer(struct tty_struct *tty)
-+{
-+	unsigned long flags;
-+	struct list_head *l;
-+	struct sclp_buffer *t;
-+	int count;
-+
-+	spin_lock_irqsave(&sclp_tty_lock, flags);
-+	count = 0;
-+	if (sclp_ttybuf != NULL)
-+		count = sclp_chars_in_buffer(sclp_ttybuf);
-+	list_for_each(l, &sclp_tty_outqueue) {
-+		t = list_entry(l, struct sclp_buffer, list);
-+		count += sclp_chars_in_buffer(sclp_ttybuf);
-+	}
-+	spin_unlock_irqrestore(&sclp_tty_lock, flags);
-+	return count;
-+}
-+
-+/*
-+ * removes all content from buffers of low level driver
-+ */
-+static void
-+sclp_tty_flush_buffer(struct tty_struct *tty)
-+{
-+	if (sclp_tty_chars_count > 0) {
-+		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
-+		sclp_tty_chars_count = 0;
-+	}
-+}
-+
-+/*
-+ * push input to tty
-+ */
-+static void
-+sclp_tty_input(unsigned char* buf, unsigned int count)
-+{
-+	unsigned int cchar;
-+
-+	/*
-+	 * If this tty driver is currently closed
-+	 * then throw the received input away.
-+	 */
-+	if (sclp_tty == NULL)
-+		return;
-+	cchar = ctrlchar_handle(buf, count, sclp_tty);
-+	switch (cchar & CTRLCHAR_MASK) {
-+	case CTRLCHAR_SYSRQ:
-+		break;
-+	case CTRLCHAR_CTRL:
-+		sclp_tty->flip.count++;
-+		*sclp_tty->flip.flag_buf_ptr++ = TTY_NORMAL;
-+		*sclp_tty->flip.char_buf_ptr++ = cchar;
-+		tty_flip_buffer_push(sclp_tty);
-+		break;
-+	case CTRLCHAR_NONE:
-+		/* send (normal) input to line discipline */
-+		memcpy(sclp_tty->flip.char_buf_ptr, buf, count);
-+		if (count < 2 ||
-+		    (strncmp ((const char *) buf + count - 2, "^n", 2) &&
-+		     strncmp ((const char *) buf + count - 2, "\0252n", 2))) {
-+			sclp_tty->flip.char_buf_ptr[count] = '\n';
-+			count++;
-+		} else
-+			count -= 2;
-+		memset(sclp_tty->flip.flag_buf_ptr, TTY_NORMAL, count);
-+		sclp_tty->flip.char_buf_ptr += count;
-+		sclp_tty->flip.flag_buf_ptr += count;
-+		sclp_tty->flip.count += count;
-+		tty_flip_buffer_push(sclp_tty);
-+		break;
-+	}
-+}
-+
-+/*
-+ * get a EBCDIC string in upper/lower case,
-+ * find out characters in lower/upper case separated by a special character,
-+ * modifiy original string,
-+ * returns length of resulting string
-+ */
-+static int
-+sclp_switch_cases(unsigned char *buf, int count,
-+		  unsigned char delim, int tolower)
-+{
-+	unsigned char *ip, *op;
-+	int toggle;
-+
-+	/* initially changing case is off */
-+	toggle = 0;
-+	ip = op = buf;
-+	while (count-- > 0) {
-+		/* compare with special character */
-+		if (*ip == delim) {
-+			/* followed by another special character? */
-+			if (count && ip[1] == delim) {
-+				/*
-+				 * ... then put a single copy of the special
-+				 * character to the output string
-+				 */
-+				*op++ = *ip++;
-+				count--;
-+			} else
-+				/*
-+				 * ... special character follower by a normal
-+				 * character toggles the case change behaviour
-+				 */
-+				toggle = ~toggle;
-+			/* skip special character */
-+			ip++;
-+		} else
-+			/* not the special character */
-+			if (toggle)
-+				/* but case switching is on */
-+				if (tolower)
-+					/* switch to uppercase */
-+					*op++ = _ebc_toupper[(int) *ip++];
-+				else
-+					/* switch to lowercase */
-+					*op++ = _ebc_tolower[(int) *ip++];
-+			else
-+				/* no case switching, copy the character */
-+				*op++ = *ip++;
-+	}
-+	/* return length of reformatted string. */
-+	return op - buf;
-+}
-+
-+static void
-+sclp_get_input(unsigned char *start, unsigned char *end)
-+{
-+	int count;
-+
-+	count = end - start;
-+	/*
-+	 * if set in ioctl convert EBCDIC to lower case
-+	 * (modify original input in SCCB)
-+	 */
-+	if (sclp_ioctls.tolower)
-+		EBC_TOLOWER(start, count);
-+
-+	/*
-+	 * if set in ioctl find out characters in lower or upper case
-+	 * (depends on current case) separated by a special character,
-+	 * works on EBCDIC
-+	 */
-+	if (sclp_ioctls.delim)
-+		count = sclp_switch_cases(start, count,
-+					  sclp_ioctls.delim,
-+					  sclp_ioctls.tolower);
-+
-+	/* convert EBCDIC to ASCII (modify original input in SCCB) */
-+	sclp_ebcasc_str(start, count);
-+
-+	/* if set in ioctl write operators input to console  */
-+	if (sclp_ioctls.echo)
-+		sclp_tty_write(sclp_tty, 0, start, count);
-+
-+	/* transfer input to high level driver */
-+	sclp_tty_input(start, count);
-+}
-+
-+static inline struct gds_vector *
-+find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id)
-+{
-+	struct gds_vector *vec;
-+
-+	for (vec = start; vec < end; (void *) vec += vec->length)
-+		if (vec->gds_id == id)
-+			return vec;
-+	return NULL;
-+}
-+
-+static inline struct gds_subvector *
-+find_gds_subvector(struct gds_subvector *start,
-+		   struct gds_subvector *end, u8 key)
-+{
-+	struct gds_subvector *subvec;
-+
-+	for (subvec = start; subvec < end; (void *) subvec += subvec->length)
-+		if (subvec->key == key)
-+			return subvec;
-+	return NULL;
-+}
-+
-+static inline void
-+sclp_eval_selfdeftextmsg(struct gds_subvector *start,
-+			 struct gds_subvector *end)
-+{
-+	struct gds_subvector *subvec;
-+
-+	subvec = start;
-+	while (subvec < end) {
-+		subvec = find_gds_subvector(subvec, end, 0x30);
-+		if (!subvec)
-+			break;
-+		sclp_get_input((unsigned char *)(subvec + 1),
-+			       (unsigned char *) subvec + subvec->length);
-+		(void *) subvec += subvec->length;
-+	}
-+}
-+
-+static inline void
-+sclp_eval_textcmd(struct gds_subvector *start,
-+		  struct gds_subvector *end)
-+{
-+	struct gds_subvector *subvec;
-+
-+	subvec = start;
-+	while (subvec < end) {
-+		subvec = find_gds_subvector(subvec, end,
-+					    GDS_KEY_SelfDefTextMsg);
-+		if (!subvec)
-+			break;
-+		sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1),
-+					 (void *)subvec + subvec->length);
-+		(void *) subvec += subvec->length;
-+	}
-+}
-+
-+static inline void
-+sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end)
-+{
-+	struct gds_vector *vec;
-+
-+	vec = start;
-+	while (vec < end) {
-+		vec = find_gds_vector(vec, end, GDS_ID_TextCmd);
-+		if (!vec)
-+			break;
-+		sclp_eval_textcmd((struct gds_subvector *)(vec + 1),
-+				  (void *) vec + vec->length);
-+		(void *) vec += vec->length;
-+	}
-+}
-+
-+
-+static inline void
-+sclp_eval_mdsmu(struct gds_vector *start, void *end)
-+{
-+	struct gds_vector *vec;
-+
-+	vec = find_gds_vector(start, end, GDS_ID_CPMSU);
-+	if (vec)
-+		sclp_eval_cpmsu(vec + 1, (void *) vec + vec->length);
-+}
-+
-+static void
-+sclp_tty_receiver(struct evbuf_header *evbuf)
-+{
-+	struct gds_vector *start, *end, *vec;
-+
-+	start = (struct gds_vector *)(evbuf + 1);
-+	end = (void *) evbuf + evbuf->length;
-+	vec = find_gds_vector(start, end, GDS_ID_MDSMU);
-+	if (vec)
-+		sclp_eval_mdsmu(vec + 1, (void *) vec + vec->length);
-+}
-+
-+static void
-+sclp_tty_state_change(struct sclp_register *reg)
-+{
-+}
-+
-+static struct sclp_register sclp_input_event =
-+{
-+	.receive_mask = EvTyp_OpCmd_Mask | EvTyp_PMsgCmd_Mask,
-+	.state_change_fn = sclp_tty_state_change,
-+	.receiver_fn = sclp_tty_receiver
-+};
-+
-+void
-+sclp_tty_init(void)
-+{
-+	void *page;
-+	int i;
-+	int rc;
-+
-+	if (!CONSOLE_IS_SCLP)
-+		return;
-+	rc = sclp_rw_init();
-+	if (rc != 0) {
-+		printk(KERN_ERR SCLP_TTY_PRINT_HEADER
-+		       "could not register tty - "
-+		       "sclp_rw_init returned %d\n", rc);
-+		return;
-+	}
-+	/* Allocate pages for output buffering */
-+	INIT_LIST_HEAD(&sclp_tty_pages);
-+	for (i = 0; i < MAX_KMEM_PAGES; i++) {
-+		page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
-+		if (page == NULL)
-+			return;
-+		list_add_tail((struct list_head *) page, &sclp_tty_pages);
-+	}
-+	INIT_LIST_HEAD(&sclp_tty_outqueue);
-+	spin_lock_init(&sclp_tty_lock);
-+	init_waitqueue_head(&sclp_tty_waitq);
-+	init_timer(&sclp_tty_timer);
-+	sclp_ttybuf = NULL;
-+	sclp_tty_buffer_count = 0;
-+	if (MACHINE_IS_VM) {
-+		/*
-+		 * save 4 characters for the CPU number
-+		 * written at start of each line by VM/CP
-+		 */
-+		sclp_ioctls_init.columns = 76;
-+		/* case input lines to lowercase */
-+		sclp_ioctls_init.tolower = 1;
-+	}
-+	sclp_ioctls = sclp_ioctls_init;
-+	sclp_tty_chars_count = 0;
-+	sclp_tty = NULL;
-+
-+	ctrlchar_init();
-+
-+	if (sclp_register(&sclp_input_event) != 0)
-+		return;
-+
-+	memset (&sclp_tty_driver, 0, sizeof(struct tty_driver));
-+	sclp_tty_driver.magic = TTY_DRIVER_MAGIC;
-+	sclp_tty_driver.driver_name = "sclp_line";
-+	sclp_tty_driver.name = "ttyS";
-+	sclp_tty_driver.name_base = 0;
-+	sclp_tty_driver.major = TTY_MAJOR;
-+	sclp_tty_driver.minor_start = 64;
-+	sclp_tty_driver.num = 1;
-+	sclp_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
-+	sclp_tty_driver.subtype = SYSTEM_TYPE_TTY;
-+	sclp_tty_driver.init_termios = tty_std_termios;
-+	sclp_tty_driver.flags = TTY_DRIVER_REAL_RAW;
-+	sclp_tty_driver.refcount = &sclp_tty_refcount;
-+	/* sclp_tty_driver.proc_entry ?	 */
-+	sclp_tty_driver.table = sclp_tty_table;
-+	sclp_tty_driver.termios = sclp_tty_termios;
-+	sclp_tty_driver.termios_locked = sclp_tty_termios_locked;
-+	sclp_tty_driver.open = sclp_tty_open;
-+	sclp_tty_driver.close = sclp_tty_close;
-+	sclp_tty_driver.write = sclp_tty_write;
-+	sclp_tty_driver.put_char = sclp_tty_put_char;
-+	sclp_tty_driver.flush_chars = sclp_tty_flush_chars;
-+	sclp_tty_driver.write_room = sclp_tty_write_room;
-+	sclp_tty_driver.chars_in_buffer = sclp_tty_chars_in_buffer;
-+	sclp_tty_driver.flush_buffer = sclp_tty_flush_buffer;
-+	sclp_tty_driver.ioctl = sclp_tty_ioctl;
-+	/*
-+	 * No need for these function because they would be only called when
-+	 * the line discipline is close to full. That means that there must be
-+	 * collected nearly 4kB of input data. I suppose it is very difficult
-+	 * for the operator to enter lines quickly enough to let overrun the
-+	 * line discipline. Besides the n_tty line discipline does not try to
-+	 * call such functions if the pointers are set to NULL. Finally I have
-+	 * no idea what to do within these function. I can not prevent the
-+	 * operator and the SCLP to deliver input. Because of the reasons
-+	 * above it seems not worth to implement a buffer mechanism.
-+	 */
-+	sclp_tty_driver.throttle = NULL;
-+	sclp_tty_driver.unthrottle = NULL;
-+	sclp_tty_driver.send_xchar = NULL;
-+	sclp_tty_driver.set_termios = NULL;
-+	sclp_tty_driver.set_ldisc = NULL;
-+	sclp_tty_driver.stop = NULL;
-+	sclp_tty_driver.start = NULL;
-+	sclp_tty_driver.hangup = NULL;
-+	sclp_tty_driver.break_ctl = NULL;
-+	sclp_tty_driver.wait_until_sent = NULL;
-+	sclp_tty_driver.read_proc = NULL;
-+	sclp_tty_driver.write_proc = NULL;
-+
-+	rc = tty_register_driver(&sclp_tty_driver);
-+	if (rc != 0)
-+		printk(KERN_ERR SCLP_TTY_PRINT_HEADER
-+		       "could not register tty - "
-+		       "sclp_drv_register returned %d\n", rc);
-+}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_tty.h drivers/s390/char/sclp_tty.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_tty.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/sclp_tty.h	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,67 @@
-+/*
-+ *  drivers/s390/char/sclp_tty.h
-+ *    interface to the SCLP-read/write driver
-+ *
-+ *  S390 version
-+ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-+ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ */
-+
-+#ifndef __SCLP_TTY_H__
-+#define __SCLP_TTY_H__
-+
-+#include <linux/ioctl.h>
-+
-+/* This is the type of data structures storing sclp ioctl setting. */
-+struct sclp_ioctls {
-+	unsigned short htab;
-+	unsigned char echo;
-+	unsigned short columns;
-+	unsigned char final_nl;
-+	unsigned short max_sccb;
-+	unsigned short kmem_sccb;	/* can't be modified at run time */
-+	unsigned char tolower;
-+	unsigned char delim;
-+};
-+
-+/* must be unique, FIXME: must be added in Documentation/ioctl_number.txt */
-+#define SCLP_IOCTL_LETTER 'B'
-+
-+/* set width of horizontal tabulator */
-+#define TIOCSCLPSHTAB	_IOW(SCLP_IOCTL_LETTER, 0, unsigned short)
-+/* enable/disable echo of input (independent from line discipline) */
-+#define TIOCSCLPSECHO	_IOW(SCLP_IOCTL_LETTER, 1, unsigned char)
-+/* set number of colums for output */
-+#define TIOCSCLPSCOLS	_IOW(SCLP_IOCTL_LETTER, 2, unsigned short)
-+/* enable/disable writing without final new line character */
-+#define TIOCSCLPSNL	_IOW(SCLP_IOCTL_LETTER, 4, signed char)
-+/* set the maximum buffers size for output, rounded up to next 4kB boundary */
-+#define TIOCSCLPSOBUF	_IOW(SCLP_IOCTL_LETTER, 5, unsigned short)
-+/* set initial (default) sclp ioctls */
-+#define TIOCSCLPSINIT	_IO(SCLP_IOCTL_LETTER, 6)
-+/* enable/disable conversion from upper to lower case of input */
-+#define TIOCSCLPSCASE	_IOW(SCLP_IOCTL_LETTER, 7, unsigned char)
-+/* set special character used for separating upper and lower case, */
-+/* 0x00 disables this feature */
-+#define TIOCSCLPSDELIM	_IOW(SCLP_IOCTL_LETTER, 9, unsigned char)
-+
-+/* get width of horizontal tabulator */
-+#define TIOCSCLPGHTAB	_IOR(SCLP_IOCTL_LETTER, 10, unsigned short)
-+/* Is echo of input enabled ? (independent from line discipline) */
-+#define TIOCSCLPGECHO	_IOR(SCLP_IOCTL_LETTER, 11, unsigned char)
-+/* get number of colums for output */
-+#define TIOCSCLPGCOLS	_IOR(SCLP_IOCTL_LETTER, 12, unsigned short)
-+/* Is writing without final new line character enabled ? */
-+#define TIOCSCLPGNL	_IOR(SCLP_IOCTL_LETTER, 14, signed char)
-+/* get the maximum buffers size for output */
-+#define TIOCSCLPGOBUF	_IOR(SCLP_IOCTL_LETTER, 15, unsigned short)
-+/* Is conversion from upper to lower case of input enabled ? */
-+#define TIOCSCLPGCASE	_IOR(SCLP_IOCTL_LETTER, 17, unsigned char)
-+/* get special character used for separating upper and lower case, */
-+/* 0x00 disables this feature */
-+#define TIOCSCLPGDELIM	_IOR(SCLP_IOCTL_LETTER, 19, unsigned char)
-+/* get the number of buffers/pages got from kernel at startup */
-+#define TIOCSCLPGKBUF	_IOR(SCLP_IOCTL_LETTER, 20, unsigned short)
-+
-+#endif	/* __SCLP_TTY_H__ */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_vt220.c drivers/s390/char/sclp_vt220.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp_vt220.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/sclp_vt220.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,817 @@
+ 	rw_cp->device = device;
+ 	rw_cp->expires = 5 * TOD_MIN;		/* 5 minutes */
+ 	rw_cp->req = req;
++	rw_cp->lpm = LPM_ANYPATH;
++	rw_cp->retries = 256;
++	rw_cp->buildclk = get_clock ();
+ 	check_then_set (&rw_cp->status, CQR_STATUS_EMPTY, CQR_STATUS_FILLED);
+         goto out;
+  clear_rw_cp:
+         dasd_free_request (rw_cp, device);
+-        rw_cp = NULL;
++        rw_cp = ERR_PTR(errcode);
+  out:
+ 	return rw_cp;
+ }
+diff -urN kernel-source-2.4.27.orig/drivers/s390/block/dasd_int.h drivers/s390/block/dasd_int.h
+--- kernel-source-2.4.27.orig/drivers/s390/block/dasd_int.h	2004-02-18 06:36:31.000000000 -0700
++++ drivers/s390/block/dasd_int.h	2006-02-12 12:47:23.000000000 -0700
+@@ -5,7 +5,7 @@
+  * Bugreports.to..: <Linux390 at de.ibm.com>
+  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
+  *
+- * $Revision: 1.36 $
++ * $Revision: 1.30.4.5 $
+  *
+  * History of changes (starts July 2000)
+  * 02/01/01 added dynamic registration of ioctls
+@@ -15,6 +15,7 @@
+ #define DASD_INT_H
+ 
+ #include <asm/dasd.h>
++#include <asm/cmb.h>
+ 
+ #define CONFIG_DASD_DYNAMIC
+ 
+@@ -443,6 +444,7 @@
+         atomic_t plugged;
+         int stopped;                         /* device (do_IO) was stopped */
+         struct list_head lowmem_pool;
++        struct cmf_device cdev;
+ }  dasd_device_t;
+ 
+ /* reasons why device (do_IO) was stopped */
+diff -urN kernel-source-2.4.27.orig/drivers/s390/block/dcssblk.c drivers/s390/block/dcssblk.c
+--- kernel-source-2.4.27.orig/drivers/s390/block/dcssblk.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/block/dcssblk.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,780 @@
 +/*
-+ *  drivers/s390/char/sclp_vt220.c
-+ *    SCLP VT220 terminal driver.
++ * dcssblk.c -- the S/390 block driver for dcss memory
 + *
-+ *  S390 version
-+ *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Peter Oberparleiter <Peter.Oberparleiter at de.ibm.com>
++ * Author: Carsten Otte
 + */
 +
-+#include <linux/config.h>
++#include <linux/module.h>
 +#include <linux/version.h>
-+#include <linux/spinlock.h>
-+#include <linux/list.h>
-+#include <linux/wait.h>
-+#include <linux/timer.h>
-+#include <linux/kernel.h>
-+#include <linux/tty.h>
-+#include <linux/tty_driver.h>
-+#include <linux/sched.h>
++#ifdef CONFIG_PROC_FS
++#include <linux/proc_fs.h>
++#endif	
++#include <linux/devfs_fs_kernel.h>
++#include <linux/ctype.h>  /* isdigit, isxdigit */
 +#include <linux/errno.h>
-+#include <linux/mm.h>
-+#include <linux/major.h>
-+#include <linux/console.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/blk.h>
++#include <linux/blkpg.h>
++#include <linux/hdreg.h>  /* HDIO_GETGEO */
 +#include <linux/kdev_t.h>
-+#include <linux/bootmem.h>
-+#include <linux/interrupt.h>
 +#include <asm/uaccess.h>
-+#include "sclp.h"
-+
-+#define SCLP_VT220_PRINT_HEADER 	"sclp vt220 tty driver: "
-+#define SCLP_VT220_MAJOR		TTY_MAJOR
-+#define SCLP_VT220_MINOR		65
-+#define SCLP_VT220_DRIVER_NAME		"sclp_vt220"
-+#define SCLP_VT220_DEVICE_NAME		"ttyS"
-+#define SCLP_VT220_CONSOLE_NAME		"ttyS"
-+#define SCLP_VT220_CONSOLE_INDEX	1	/* console=ttyS1 */
-+
-+/* Representation of a single write request */
-+struct sclp_vt220_request {
-+	struct list_head list;
-+	struct sclp_req sclp_req;
-+	int retry_count;
-+	struct timer_list retry_timer;
-+};
-+
-+/* VT220 SCCB */
-+struct sclp_vt220_sccb {
-+	struct sccb_header header;
-+	struct evbuf_header evbuf;
-+};
-+
-+#define SCLP_VT220_MAX_CHARS_PER_BUFFER	(PAGE_SIZE - \
-+					 sizeof(struct sclp_vt220_request) - \
-+					 sizeof(struct sclp_vt220_sccb))
-+
-+/* Structures and data needed to register tty driver */
-+static struct tty_driver sclp_vt220_driver;
-+static int sclp_vt220_refcount;
-+static struct tty_struct * sclp_vt220_table[1];
-+static struct termios * sclp_vt220_termios[1];
-+static struct termios * sclp_vt220_termios_locked[1];
-+
-+/* The tty_struct that the kernel associated with us */
-+static struct tty_struct *sclp_vt220_tty;
-+
-+/* Lock to protect internal data from concurrent access */
-+static spinlock_t sclp_vt220_lock;
-+
-+/* List of empty pages to be used as write request buffers */
-+static struct list_head sclp_vt220_empty;
-+
-+/* List of pending requests */
-+static struct list_head sclp_vt220_outqueue;
-+
-+/* Number of requests in outqueue */
-+static int sclp_vt220_outqueue_count;
-+
-+/* Wait queue used to delay write requests while we've run out of buffers */
-+static wait_queue_head_t sclp_vt220_waitq;
-+
-+/* Timer used for delaying write requests to merge subsequent messages into
-+ * a single buffer */
-+static struct timer_list sclp_vt220_timer;
-+
-+/* Pointer to current request buffer which has been partially filled but not
-+ * yet sent */
-+static struct sclp_vt220_request *sclp_vt220_current_request;
-+
-+/* Number of characters in current request buffer */
-+static int sclp_vt220_buffered_chars;
-+
-+/* Flag indicating whether this driver has already been initialized */
-+static int sclp_vt220_initialized = 0;
-+
-+/* Flag indicating that sclp_vt220_current_request should really
-+ * have been already queued but wasn't because the SCLP was processing
-+ * another buffer */
-+static int sclp_vt220_flush_later;
-+
-+static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf);
-+static void __sclp_vt220_emit(struct sclp_vt220_request *request);
-+static void sclp_vt220_emit_current(void);
-+
-+/* Registration structure for our interest in SCLP event buffers */
-+static struct sclp_register sclp_vt220_register = {
-+	.send_mask		= EvTyp_VT220Msg_Mask,
-+	.receive_mask		= EvTyp_VT220Msg_Mask,
-+	.state_change_fn	= NULL,
-+	.receiver_fn		= sclp_vt220_receiver_fn
-+};
-+
-+
-+/*
-+ * Put provided request buffer back into queue and check emit pending
-+ * buffers if necessary.
-+ */
-+static void
-+sclp_vt220_process_queue(struct sclp_vt220_request* request)
-+{
-+	unsigned long flags;
-+	struct sclp_vt220_request *next;
-+	void *page;
-+
-+	/* Put buffer back to list of empty buffers */
-+	page = request->sclp_req.sccb;
-+	spin_lock_irqsave(&sclp_vt220_lock, flags);
-+	/* Move request from outqueue to empty queue */
-+	list_del(&request->list);
-+	sclp_vt220_outqueue_count--;
-+	list_add_tail((struct list_head *) page, &sclp_vt220_empty);
-+	/* Check if there is a pending buffer on the out queue. */
-+	next = NULL;
-+	if (!list_empty(&sclp_vt220_outqueue))
-+		next = list_entry(sclp_vt220_outqueue.next,
-+				  struct sclp_vt220_request, list);
-+	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-+	if (next != NULL)
-+		__sclp_vt220_emit(next);
-+	else if (sclp_vt220_flush_later)
-+		sclp_vt220_emit_current();
-+	wake_up(&sclp_vt220_waitq);
-+	/* Check if the tty needs a wake up call */
-+	if (sclp_vt220_tty != NULL) {
-+		if ((sclp_vt220_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-+		    (sclp_vt220_tty->ldisc.write_wakeup != NULL))
-+			(sclp_vt220_tty->ldisc.write_wakeup)(sclp_vt220_tty);
-+		wake_up_interruptible(&sclp_vt220_tty->write_wait);
-+	}
-+}
-+
-+/*
-+ * Retry sclp write request after waiting some time for an sclp equipment
-+ * check to pass.
-+ */
-+static void
-+sclp_vt220_retry(unsigned long data)
-+{
-+	struct sclp_vt220_request *request;
-+	struct sclp_vt220_sccb *sccb;
-+
-+	request = (struct sclp_vt220_request *) data;
-+	request->sclp_req.status = SCLP_REQ_FILLED;
-+	sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
-+	sccb->header.response_code = 0x0000;
-+	sclp_add_request(&request->sclp_req);
-+}
-+
-+#define SCLP_BUFFER_MAX_RETRY		5
-+#define	SCLP_BUFFER_RETRY_INTERVAL	2
-+
-+/*
-+ * Callback through which the result of a write request is reported by the
-+ * SCLP.
-+ */
-+static void
-+sclp_vt220_callback(struct sclp_req *request, void *data)
-+{
-+	struct sclp_vt220_request *vt220_request;
-+	struct sclp_vt220_sccb *sccb;
-+
-+	vt220_request = (struct sclp_vt220_request *) data;
-+	if (request->status == SCLP_REQ_FAILED) {
-+		sclp_vt220_process_queue(vt220_request);
-+		return;
-+	}
-+	sccb = (struct sclp_vt220_sccb *) vt220_request->sclp_req.sccb;
-+
-+	/* Check SCLP response code and choose suitable action	*/
-+	switch (sccb->header.response_code) {
-+	case 0x0020 :
-+		break;
-+
-+	case 0x05f0: /* Target resource in improper state */
-+		break;
-+
-+	case 0x0340: /* Contained SCLP equipment check */
-+		if (vt220_request->retry_count++ > SCLP_BUFFER_MAX_RETRY)
-+			break;
-+		/* Remove processed buffers and requeue rest */
-+		if (sclp_remove_processed((struct sccb_header *) sccb) > 0) {
-+			/* Not all buffers were processed */
-+			sccb->header.response_code = 0x0000;
-+			vt220_request->sclp_req.status = SCLP_REQ_FILLED;
-+			sclp_add_request(request);
-+			return;
-+		}
-+		break;
-+
-+	case 0x0040: /* SCLP equipment check */
-+		if (vt220_request->retry_count++ > SCLP_BUFFER_MAX_RETRY)
-+			break;
-+		/* Wait some time, then retry request */
-+		vt220_request->retry_timer.function = sclp_vt220_retry;
-+		vt220_request->retry_timer.data =
-+			(unsigned long) vt220_request;
-+		vt220_request->retry_timer.expires =
-+			jiffies + SCLP_BUFFER_RETRY_INTERVAL*HZ;
-+		add_timer(&vt220_request->retry_timer);
-+		return;
-+
-+	default:
-+		break;
-+	}
-+	sclp_vt220_process_queue(vt220_request);
-+}
-+
-+/*
-+ * Emit vt220 request buffer to SCLP.
-+ */
-+static void
-+__sclp_vt220_emit(struct sclp_vt220_request *request)
-+{
-+	if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) {
-+		request->sclp_req.status = SCLP_REQ_FAILED;
-+		sclp_vt220_callback(&request->sclp_req, (void *) request);
-+		return;
-+	}
-+	request->sclp_req.command = SCLP_CMDW_WRITEDATA;
-+	request->sclp_req.status = SCLP_REQ_FILLED;
-+	request->sclp_req.callback = sclp_vt220_callback;
-+	request->sclp_req.callback_data = (void *) request;
-+
-+	sclp_add_request(&request->sclp_req);
-+}
-+
-+/*
-+ * Queue and emit given request.
-+ */
-+static void
-+sclp_vt220_emit(struct sclp_vt220_request *request)
-+{
-+	unsigned long flags;
-+	int count;
-+
-+	spin_lock_irqsave(&sclp_vt220_lock, flags);
-+	list_add_tail(&request->list, &sclp_vt220_outqueue);
-+	count = sclp_vt220_outqueue_count++;
-+	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-+	/* Emit only the first buffer immediately - callback takes care of
-+	 * the rest */
-+	if (count == 0)
-+		__sclp_vt220_emit(request);
-+}
-+
-+/*
-+ * Queue and emit current request.
-+ */
-+static void
-+sclp_vt220_emit_current(void)
-+{
-+	unsigned long flags;
-+	struct sclp_vt220_request *request;
-+	struct sclp_vt220_sccb *sccb;
-+
-+	spin_lock_irqsave(&sclp_vt220_lock, flags);
-+	request = NULL;
-+	if (sclp_vt220_current_request != NULL) {
-+		sccb = (struct sclp_vt220_sccb *) 
-+				sclp_vt220_current_request->sclp_req.sccb;
-+		/* Only emit buffers with content */
-+		if (sccb->header.length != sizeof(struct sclp_vt220_sccb)) {
-+			request = sclp_vt220_current_request;
-+			sclp_vt220_current_request = NULL;
-+			if (timer_pending(&sclp_vt220_timer))
-+				del_timer(&sclp_vt220_timer);
-+		}
-+		sclp_vt220_flush_later = 0;
-+	}
-+	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-+	if (request != NULL)
-+		sclp_vt220_emit(request);
-+}
-+
-+#define SCLP_NORMAL_WRITE	0x00
-+
-+/*
-+ * Helper function to initialize a page with the sclp request structure.
-+ */
-+static struct sclp_vt220_request *
-+sclp_vt220_initialize_page(void *page)
-+{
-+	struct sclp_vt220_request *request;
-+	struct sclp_vt220_sccb *sccb;
-+
-+	/* Place request structure at end of page */
-+	request = ((struct sclp_vt220_request *)
-+			((addr_t) page + PAGE_SIZE)) - 1;
-+	init_timer(&request->retry_timer);
-+	request->retry_count = 0;
-+	request->sclp_req.sccb = page;
-+	/* SCCB goes at start of page */
-+	sccb = (struct sclp_vt220_sccb *) page;
-+	memset((void *) sccb, 0, sizeof(struct sclp_vt220_sccb));
-+	sccb->header.length = sizeof(struct sclp_vt220_sccb);
-+	sccb->header.function_code = SCLP_NORMAL_WRITE;
-+	sccb->header.response_code = 0x0000;
-+	sccb->evbuf.type = EvTyp_VT220Msg;
-+	sccb->evbuf.length = sizeof(struct evbuf_header);
-+
-+	return request;
-+}
-+
-+static inline unsigned int
-+sclp_vt220_space_left(struct sclp_vt220_request *request)
-+{
-+	struct sclp_vt220_sccb *sccb;
-+	sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
-+	return PAGE_SIZE - sizeof(struct sclp_vt220_request) -
-+	       sccb->header.length;
-+}
++#include <asm/dcss.h>
 +
-+static inline unsigned int
-+sclp_vt220_chars_stored(struct sclp_vt220_request *request)
-+{
-+	struct sclp_vt220_sccb *sccb;
-+	sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
-+	return sccb->evbuf.length - sizeof(struct evbuf_header);
-+}
++#define PRINT_DEBUG(x...)	printk(KERN_DEBUG DCSSBLK_NAME " debug:" x)
++#define PRINT_INFO(x...)	printk(KERN_INFO DCSSBLK_NAME " info:" x)
++#define PRINT_WARN(x...)	printk(KERN_WARNING DCSSBLK_NAME " warning:" x)
++#define PRINT_ERR(x...)		printk(KERN_ERR DCSSBLK_NAME " error:" x)
++#define DCSSBLK_NAME "dcssblk"
 +
-+/*
-+ * Add msg to buffer associated with request. Return the number of characters
-+ * added or -EFAULT on error.
-+ */
-+static int
-+sclp_vt220_add_msg(struct sclp_vt220_request *request,
-+		   const unsigned char *msg, int count, int from_user,
-+		   int convertlf)
-+{
-+	struct sclp_vt220_sccb *sccb;
-+	void *buffer;
-+	unsigned char c;
-+	int from;
-+	int to;
++#ifdef CONFIG_PROC_FS
++static struct proc_dir_entry *dcssblk_proc_root_entry;
++static struct proc_dir_entry *dcssblk_add_entry;
++static struct proc_dir_entry *dcssblk_remove_entry;
++static struct proc_dir_entry *dcssblk_list_entry;
++#endif
++static devfs_handle_t dcssblk_devfs_dir;
++unsigned int dcssblk_blksizes[1<<MINORBITS];
 +
-+	if (count > sclp_vt220_space_left(request))
-+		count = sclp_vt220_space_left(request);
-+	if (count <= 0)
-+		return 0;
++static int dcssblk_open (struct inode *inode, struct file *filp);
++static int dcssblk_release (struct inode *inode, struct file *filp);
 +
-+	sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
-+	buffer = (void *) ((addr_t) sccb + sccb->header.length);
++static struct block_device_operations dcssblk_devops =
++	{
++		owner:   THIS_MODULE,
++		open:    dcssblk_open,
++		release: dcssblk_release,
++	};
 +
-+	if (convertlf) {
-+		/* Perform Linefeed conversion (0x0a -> 0x0a 0x0d)*/
-+		for (from=0, to=0;
-+		     (from < count) && (to < sclp_vt220_space_left(request));
-+		     from++) {
-+			/* Retrieve character */
-+			if (from_user) {
-+				if (get_user(c, msg + from) != 0)
-+					return -EFAULT;
-+			} else
-+				c = msg[from];
-+			/* Perform conversion */
-+			if (c == 0x0a) {
-+				if (to + 1 < sclp_vt220_space_left(request)) {
-+					((unsigned char *) buffer)[to++] = c;
-+					((unsigned char *) buffer)[to++] = 0x0d;
-+				} else
-+					break;
++typedef struct _dcss_device_t {
++	struct list_head lh;
++	devfs_handle_t     devfs_entry;
++	unsigned int  use_count;
++	unsigned long start;
++	unsigned long end;
++	int               segment_type;
++	unsigned char save_pending;
++	unsigned char is_shared;
++#ifdef CONFIG_PROC_FS
++	struct proc_dir_entry *dcssblk_subdir_entry;
++	struct proc_dir_entry *dcssblk_save_entry;
++	struct proc_dir_entry *dcssblk_shared_entry;
++#endif
++	char name [9];
++	kdev_t             kdev;
++} dcss_device_t;
 +
-+			} else
-+				((unsigned char *) buffer)[to++] = c;
-+		}
-+		sccb->header.length += to;
-+		sccb->evbuf.length += to;
-+		return from;
-+	} else {
-+		if (from_user) {
-+			if (copy_from_user(buffer, (void *) msg, count) != 0)
-+				return -EFAULT;
-+		}
-+		else
-+			memcpy(buffer, (const void *) msg, count);
-+		sccb->header.length += count;
-+		sccb->evbuf.length += count;
-+		return count;
-+	}
-+}
++typedef struct _dcssblk_proc_private_t {
++	char name[8];
++	int bytes_written;
++} dcssblk_proc_private_t;
++
++static int dcssblk_major;
++static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices);
++static rwlock_t dcssblk_devices_lock = RW_LOCK_UNLOCKED;
++
++
++MODULE_LICENSE("GPL");
 +
 +/*
-+ * Emit buffer after having waited long enough for more data to arrive.
++ * get a minor number. needs to be called with
++ * write_lock(dcssblk_devices_lock) and the 
++ * device needs to be enqueued before the lock is
++ * freed.
 + */
-+static void
-+sclp_vt220_timeout(unsigned long data)
-+{
-+	sclp_vt220_emit_current();
++static int dcssblk_assign_free_minor (dcss_device_t* device) {
++	unsigned int minor,found;
++	struct list_head* entry;
++	if (device == NULL)
++		return -EINVAL;
++	for (minor=0; minor< (1<<MINORBITS); minor++) {
++		found = 0;
++		// test if minor available
++		list_for_each (entry, &dcssblk_devices)
++			if (minor == MINOR((list_entry(entry, dcss_device_t, 
++						       lh)->kdev)))
++				found++;
++		if (!found) break; // got unused minor
++	}
++	if (found)
++		return -EBUSY;
++	device->kdev = MKDEV(dcssblk_major, minor);
++	return 0;
 +}
 +
-+#define BUFFER_MAX_DELAY	HZ/2
-+
-+/* 
-+ * Internal implementation of the write function. Write COUNT bytes of data
-+ * from memory at BUF which may reside in user space (specified by FROM_USER)
-+ * to the SCLP interface. In case that the data does not fit into the current
-+ * write buffer, emit the current one and allocate a new one. If there are no
-+ * more empty buffers available, wait until one gets emptied. If DO_SCHEDULE
-+ * is non-zero, the buffer will be scheduled for emitting after a timeout -
-+ * otherwise the user has to explicitly call the flush function.
-+ * A non-zero CONVERTLF parameter indicates that 0x0a characters in the message
-+ * buffer should be converted to 0x0a 0x0d. After completion, return the number
-+ * of bytes written.
++/*
++ * get the dcss_device_t from dcssblk_devices 
++ * with the struct device supplied.
++ * needs to be called with read_lock(dcssblk_devices_lock)
 + */
-+static int
-+__sclp_vt220_write(int from_user, const unsigned char *buf, int count,
-+		   int do_schedule, int convertlf)
-+{
-+	unsigned long flags;
-+	void *page;
-+	int written;
-+	int overall_written;
-+
-+	if (count <= 0)
-+		return 0;
-+	overall_written = 0;
-+	spin_lock_irqsave(&sclp_vt220_lock, flags);
-+	do {
-+		/* Create a sclp output buffer if none exists yet */
-+		if (sclp_vt220_current_request == NULL) {
-+			while (list_empty(&sclp_vt220_empty)) {
-+				spin_unlock_irqrestore(&sclp_vt220_lock,
-+						       flags);
-+				if (in_interrupt())
-+					sclp_sync_wait();
-+				else
-+					wait_event(sclp_vt220_waitq,
-+						!list_empty(&sclp_vt220_empty));
-+				spin_lock_irqsave(&sclp_vt220_lock, flags);
-+			}
-+			page = (void *) sclp_vt220_empty.next;
-+			list_del((struct list_head *) page);
-+			sclp_vt220_current_request =
-+				sclp_vt220_initialize_page(page);
++static dcss_device_t * dcssblk_get_device_by_minor (unsigned int minor) {
++	struct list_head* entry;
++	list_for_each (entry, &dcssblk_devices) 
++		if (MINOR(list_entry(entry, dcss_device_t, lh)->kdev)==minor) {
++			return list_entry(entry,dcss_device_t, lh);
 +		}
-+		/* Try to write the string to the current request buffer */
-+		written = sclp_vt220_add_msg(sclp_vt220_current_request,
-+				buf, count, from_user, convertlf);
-+		if (written > 0)
-+			overall_written += written;
-+		if (written == -EFAULT || written == count)
-+			break;
-+		/*
-+		 * Not all characters could be written to the current
-+		 * output buffer. Emit the buffer, create a new buffer
-+		 * and then output the rest of the string.
-+		 */
-+		spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-+		sclp_vt220_emit_current();
-+		spin_lock_irqsave(&sclp_vt220_lock, flags);
-+		buf += written;
-+		count -= written;
-+	} while (count > 0);
-+	/* Setup timer to output current console buffer after some time */
-+	if (sclp_vt220_current_request != NULL &&
-+	    !timer_pending(&sclp_vt220_timer) && do_schedule) {
-+		sclp_vt220_timer.function = sclp_vt220_timeout;
-+		sclp_vt220_timer.data = 0UL;
-+		sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY;
-+		add_timer(&sclp_vt220_timer);
-+	}
-+	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-+	return overall_written;
++	return NULL;
 +}
 +
 +/*
-+ * This routine is called by the kernel to write a series of
-+ * characters to the tty device.  The characters may come from
-+ * user space or kernel space.  This routine will return the
-+ * number of characters actually accepted for writing.
++ * get the dcss_device_t from dcssblk_devices 
++ * with the segment name supplied.
++ * needs to be called with read_lock(dcssblk_devices_lock)
 + */
-+static int
-+sclp_vt220_write(struct tty_struct * tty, int from_user,
-+		 const unsigned char *buf, int count)
-+{
-+	return __sclp_vt220_write(from_user, buf, count, 1, 0);
++static dcss_device_t * dcssblk_get_device_by_name (char* name) {
++	struct list_head* entry;
++	list_for_each (entry, &dcssblk_devices) 
++		if (!strcmp (name, list_entry (entry, dcss_device_t, lh)
++			     ->name)) {
++			return list_entry(entry,dcss_device_t, lh);
++		}
++	return NULL;
 +}
 +
-+#define SCLP_VT220_SESSION_ENDED	0x01
-+#define	SCLP_VT220_SESSION_STARTED	0x80
-+#define SCLP_VT220_SESSION_DATA		0x00
++static void dcssblk_do_save (dcss_device_t* device) {
++	segment_replace (device->name);
++}
 +
 +/*
-+ * Called by the SCLP to report incoming event buffers.
++ * device attribute for switching shared/nonshared
++ * operation 
 + */
-+static void
-+sclp_vt220_receiver_fn(struct evbuf_header *evbuf)
-+{
-+	char *buffer;
-+	unsigned int count;
-+
-+	/* Ignore input if device is not open */
-+	if (sclp_vt220_tty == NULL)
-+		return;
++static int dcssblk_shared_store  (struct file *file, 
++				      const char *buffer,
++				      unsigned long count, void *data) {
++	dcss_device_t* device = data;
++	char* buf;
++	int rc;
++	long value;
 +
-+	buffer = (char *) ((addr_t) evbuf + sizeof(struct evbuf_header));
-+	count = evbuf->length - sizeof(struct evbuf_header);
++	if (device == NULL) {
++		rc = -EINVAL;
++		goto out_nobuf;
++	}
++	write_lock(&dcssblk_devices_lock);
++	if (device->use_count) {
++		rc = -EBUSY;
++		write_unlock (&dcssblk_devices_lock);
++		goto out_nobuf;
++	}
 +
-+	switch (*buffer) {
-+	case SCLP_VT220_SESSION_ENDED:
-+	case SCLP_VT220_SESSION_STARTED:
++	/*
++	 * fetch buffer from userland
++	 */
++	buf = kmalloc(count,GFP_ATOMIC);
++	if (buf == NULL) {
++		rc = -ENOMEM;
++		write_unlock(&dcssblk_devices_lock);
++		goto out_nobuf;
++	}
++	if (copy_from_user(buf, buffer, count)) {
++		rc = -EFAULT;
++		write_unlock(&dcssblk_devices_lock);
++		goto out;	
++	}
++	value = simple_strtoul(buf, &buf, 10);
++	
++	if (value) {
++		// reload segment in shared mode
++		segment_unload (device->name);
++		rc = segment_load (device->name, SEGMENT_SHARED_RO, 
++				   &device->start, &device->end);
++		if (rc < 0) {
++			PRINT_WARN ("SEGMENT %s NOT RELOADED RC=%d\n",
++				    device->name, rc);
++			goto removeseg;
++		}
++		device->segment_type = rc;
++		device->is_shared = 1;
++		rc = count;
++	} else {
++		// reload segment in exclusive mode
++		segment_unload (device->name);
++		rc = segment_load (device->name, SEGMENT_EXCLUSIVE_RW, 
++				   &device->start, &device->end);
++		if (rc < 0) {
++			PRINT_WARN("SEGMENT %s NOT RELOADED RC=%d\n",
++				   device->name, rc);
++			goto removeseg;
++		}
++		device->segment_type = rc;
++		device->is_shared = 0;
++		rc = count;
++	}
++	switch (device->segment_type) {
++	case SEGMENT_SHARED_RO:
++	case SEGMENT_EXCLUSIVE_RO:
++		set_device_ro (device->kdev, 1);
 +		break;
-+	case SCLP_VT220_SESSION_DATA:
-+		/* Send input to line discipline */
-+		buffer++;
-+		count--;
-+		/* Prevent buffer overrun by discarding input. Note that
-+		 * because buffer_push works asynchronously, we cannot wait
-+		 * for the buffer to be emptied. */
-+		if (count + sclp_vt220_tty->flip.count > TTY_FLIPBUF_SIZE)
-+			count = TTY_FLIPBUF_SIZE - sclp_vt220_tty->flip.count;
-+		memcpy(sclp_vt220_tty->flip.char_buf_ptr, buffer, count);
-+		memset(sclp_vt220_tty->flip.flag_buf_ptr, TTY_NORMAL, count);
-+		sclp_vt220_tty->flip.char_buf_ptr += count;
-+		sclp_vt220_tty->flip.flag_buf_ptr += count;
-+		sclp_vt220_tty->flip.count += count;
-+		tty_flip_buffer_push(sclp_vt220_tty);
++	case SEGMENT_SHARED_RW:
++	case SEGMENT_EXCLUSIVE_RW:
++		set_device_ro (device->kdev, 0);
 +		break;
 +	}
++	if (value && ((device->segment_type == SEGMENT_EXCLUSIVE_RO) ||
++		      (device->segment_type == SEGMENT_EXCLUSIVE_RW))) {
++		PRINT_WARN( 
++			   "dcssblk: could not get shared copy of segment %s\n",
++			   device->name);
++		device->is_shared = 0;
++		rc = -EPERM;
++	}
++	if ((value == 0) && ((device->segment_type == SEGMENT_SHARED_RO) ||
++			     (device->segment_type == SEGMENT_SHARED_RW))) {
++		PRINT_WARN(
++			   "dcssblk: could not get exclusive copy of segment %s\n",
++			   device->name);
++		device->is_shared = 1;
++		rc = -EPERM;
++	}
++	write_unlock(&dcssblk_devices_lock);
++	goto out;
++
++ removeseg:
++	PRINT_WARN (
++		    "dcssblk: could not reload segment %s, removing it!\n",
++		    device->name);
++	list_del(&device->lh);
++	write_unlock(&dcssblk_devices_lock);
++#ifdef CONFIG_PROC_FS
++	remove_proc_entry("save", device->dcssblk_subdir_entry);
++	remove_proc_entry("shared", device->dcssblk_subdir_entry);
++	remove_proc_entry(device->name, dcssblk_proc_root_entry);
++#endif
++	devfs_unregister(device->devfs_entry );
++
++	kfree (device);	
++	MOD_DEC_USE_COUNT; // permanent
++ out:
++	kfree (buf);
++ out_nobuf:
++	return rc;
++
 +}
 +
 +/*
-+ * This routine is called when a particular tty device is opened.
++ * device attribute for showing status of "shared / non-shared"
 + */
-+static int
-+sclp_vt220_open(struct tty_struct * tty, struct file * filp)
-+{
-+	sclp_vt220_tty = tty;
-+	tty->driver_data = NULL;
-+	tty->low_latency = 0;
-+	return 0;
++static int dcssblk_shared_status(char *buffer, char **start, off_t offset,
++				int count, int *eof, void *data) {
++	dcss_device_t* device = data;
++	
++	*eof = 1;
++	return sprintf(buffer, device->is_shared ? "1\n" : "0\n");
 +}
 +
 +/*
-+ * This routine is called when a particular tty device is closed.
++ * device attribute for save operation on current copy
++ * of the segment. If the segment is busy, saving will
++ * become pending until it gets released which can be
++ * undone by storing a non-true value to this entry
 + */
-+static void
-+sclp_vt220_close(struct tty_struct * tty, struct file * filp)
-+{
-+	if (tty->count > 1)
-+		return;
-+	sclp_vt220_tty = NULL;
++static int dcssblk_save_store  (struct file *file, 
++				    const char *buffer,
++				    unsigned long count, void *data) {
++	dcss_device_t* device = data;
++	char* buf;
++	int rc,value;
++
++	if (device == NULL) {
++		rc = -EINVAL;
++		goto out_nobuf;
++	}
++	read_lock(&dcssblk_devices_lock);
++	/*
++	 * fetch buffer from userland
++	 */
++	buf = kmalloc(count,GFP_ATOMIC);
++	if (buf == NULL) {
++		rc = -ENOMEM;
++		write_unlock(&dcssblk_devices_lock);
++		goto out_nobuf;
++	}
++	if (copy_from_user(buf, buffer, count)) {
++		rc = -EFAULT;
++		write_unlock(&dcssblk_devices_lock);
++		goto out;	
++	}
++	value = simple_strtoul(buf, &buf, 10);
++	if (value) {
++		if (device->use_count == 0) {
++			/* device is idle => we save immediately */
++			PRINT_WARN ("saving segment %s\n", device->name);
++			dcssblk_do_save (device);
++		}  else {
++			/* device is busy => we save it when it becomes
++			   idle in dcssblk_release */
++			PRINT_WARN ("segment %s is currently busy\n",
++				    device->name);
++			PRINT_WARN ("segment %s will be saved when it becomes idle\n",
++				    device->name);
++			device->save_pending = 1;
++		}
++	} else {
++		if (device->save_pending) {
++			/* device is busy & the user wants to undo his save
++			   request */
++			device->save_pending = 0;
++			PRINT_WARN ("deactivating pending save for segment %s\n",
++				    device->name);
++		}
++	}
++	read_unlock(&dcssblk_devices_lock);
++	rc = count;
++ out:
++	kfree (buf);
++ out_nobuf:
++	return rc;
 +}
 +
 +/*
-+ * This routine is called by the kernel to write a single
-+ * character to the tty device.  If the kernel uses this routine,
-+ * it must call the flush_chars() routine (if defined) when it is
-+ * done stuffing characters into the driver.
-+ *
-+ * NOTE: include/linux/tty_driver.h specifies that a character should be
-+ * ignored if there is no room in the queue. This driver implements a different
-+ * semantic in that it will block when there is no more room left.
++ * device attribute for showing status of "save pending"
 + */
-+static void
-+sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch)
-+{
-+	__sclp_vt220_write(0, &ch, 1, 0, 0);
++static int dcssblk_save_status(char *buffer, char **start, off_t offset,
++				int count, int *eof, void *data) {
++	dcss_device_t* device = data;
++	
++	*eof = 1;
++	return sprintf(buffer, device->save_pending ? "1\n" : "0\n");
 +}
 +
 +/*
-+ * This routine is called by the kernel after it has written a
-+ * series of characters to the tty device using put_char().  
++ * device attribute for adding devices
 + */
-+static void
-+sclp_vt220_flush_chars(struct tty_struct *tty)
++static int dcssblk_add_store (struct file *file, 
++				  const char *buffer,
++				  unsigned long count, void *data)
 +{
-+	if (sclp_vt220_outqueue_count == 0)
-+		sclp_vt220_emit_current();
++	int rc=0, i;
++	char* buf;
++	dcss_device_t* dcssdev;
++	struct list_head* entry;
++
++	MOD_INC_USE_COUNT; // released at end of func
++
++	/*
++	 * fetch buffer from userland
++	 */
++	buf = kmalloc(count+1,GFP_KERNEL);
++	if (buf == NULL) {
++		rc = -ENOMEM;
++		goto out_nobuf;
++	}
++	if (copy_from_user(buf, buffer, count)) {
++		rc = -EFAULT;
++		goto out;
++	}
++	
++
++	/*
++	 * check input parameters
++	 */
++	for (i=0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i<count); i++) {
++		buf[i] = toupper(buf[i]);
++	}
++	*(buf+i) = '\0';
++	if ((i==0) || (i>8)) {
++		rc = -ENAMETOOLONG;
++		goto out;
++	}
++	/*
++	 * already loaded?
++	 */
++	read_lock(&dcssblk_devices_lock);
++	list_for_each (entry, &dcssblk_devices) {
++		if (!strcmp(buf, list_entry(entry, dcss_device_t, lh)->name)) {
++			read_unlock(&dcssblk_devices_lock);
++			PRINT_WARN ("SEGMENT %s ALREADY LOADED!\n", buf);
++			rc = -EEXIST;
++			goto out;
++		}
++	}
++	read_unlock(&dcssblk_devices_lock);
++	/*
++	 * try to get the dcss
++	 */
++	dcssdev = kmalloc (sizeof(dcss_device_t),GFP_KERNEL);
++	if (dcssdev == NULL) {
++		rc = -ENOMEM;
++		goto out;
++	}
++	memset (dcssdev, 0, sizeof(dcss_device_t));
++	memcpy (dcssdev->name, buf, i);
++	dcssdev->name[i+1] = '\0';
++	INIT_LIST_HEAD(&dcssdev->lh);
++	/*
++	 * load the segment
++	 */
++	rc = segment_load (dcssdev->name, SEGMENT_SHARED_RO, 
++			   &dcssdev->start, &dcssdev->end);
++	if (rc < 0) {
++		PRINT_WARN ("SEGMENT %s NOT LOADED RC=%d\n",
++			    dcssdev->name, rc);
++		goto free_dcssdev;
++	}
++	if (rc == SEGMENT_EXCLUSIVE_RO || rc == SEGMENT_EXCLUSIVE_RW)
++		dcssdev->is_shared = 0;
 +	else
-+		sclp_vt220_flush_later = 1;
++		dcssdev->is_shared = 1;
++	PRINT_WARN ("LOADED SEGMENT %s from %08lx to %08lx\n",dcssdev->name,dcssdev->start,dcssdev->end);
++	dcssdev->segment_type = rc;
++	dcssdev->save_pending = 0;
++	/*
++	 * get minor
++	 */
++	write_lock(&dcssblk_devices_lock);
++	rc = dcssblk_assign_free_minor(dcssdev);
++	if (rc) {
++		write_unlock (&dcssblk_devices_lock);
++		goto unload_seg;
++	}
++	/*
++	 * create devfs device node
++	 */
++	dcssdev->devfs_entry = devfs_register (dcssblk_devfs_dir,dcssdev->name,
++					       DEVFS_FL_DEFAULT,
++					       MAJOR(dcssdev->kdev),
++					       MINOR(dcssdev->kdev),
++					       S_IFBLK | S_IRUSR | S_IWUSR,
++					       &dcssblk_devops,NULL);
++	/*
++	 * create procfs subdirectory
++	 */
++#ifdef CONFIG_PROC_FS
++	dcssdev->dcssblk_subdir_entry =  proc_mkdir (dcssdev->name, 
++						     dcssblk_proc_root_entry);
++	dcssdev->dcssblk_shared_entry = 
++		create_proc_entry ("shared",
++				   S_IRUSR|S_IWUSR,
++				   dcssdev->dcssblk_subdir_entry);
++	dcssdev->dcssblk_save_entry =
++		create_proc_entry ("save",
++				   S_IRUSR|S_IWUSR,
++				   dcssdev->dcssblk_subdir_entry);
++	dcssdev->dcssblk_shared_entry->write_proc = 
++		dcssblk_shared_store;
++	dcssdev->dcssblk_shared_entry->read_proc = 
++		dcssblk_shared_status;
++	dcssdev->dcssblk_save_entry->write_proc =
++		dcssblk_save_store;
++	dcssdev->dcssblk_save_entry->read_proc =
++		dcssblk_save_status;
++	dcssdev->dcssblk_shared_entry->data =
++		dcssdev;
++	dcssdev->dcssblk_save_entry->data =
++		dcssdev;
++#endif
++
++	/*
++	 * enqueue
++	 */
++	list_add_tail (&dcssdev->lh, &dcssblk_devices);
++	write_unlock(&dcssblk_devices_lock);
++	switch (dcssdev->segment_type) {
++	case SEGMENT_SHARED_RO:
++	case SEGMENT_EXCLUSIVE_RO:
++		set_device_ro (dcssdev->kdev, 1);
++		break;
++	case SEGMENT_SHARED_RW:
++	case SEGMENT_EXCLUSIVE_RW:
++		set_device_ro (dcssdev->kdev, 0);
++		break;
++	}
++	PRINT_DEBUG ("SEGMENT %s loaded successfully\n",
++		     dcssdev->name);
++	rc = count;
++	MOD_INC_USE_COUNT; // second time -> permanent
++	goto out;
++ unload_seg:
++	segment_unload (dcssdev->name);
++ free_dcssdev:
++	kfree (dcssdev);
++ out:
++	kfree (buf);
++ out_nobuf:
++	MOD_DEC_USE_COUNT;
++	return rc;
 +}
 +
 +/*
-+ * This routine returns the numbers of characters the tty driver
-+ * will accept for queuing to be written.  This number is subject
-+ * to change as output buffers get emptied, or if the output flow
-+ * control is acted.
++ * device attribute for removing devices
 + */
-+static int
-+sclp_vt220_write_room(struct tty_struct *tty)
-+{
-+	unsigned long flags;
-+	struct list_head *l;
-+	int count;
++static int dcssblk_remove_store  (struct file *file, 
++				      const char *buffer,
++				      unsigned long count, void *data) {
++	dcss_device_t* device;
++	char * buf;
++	int rc=count,i;
 +
-+	spin_lock_irqsave(&sclp_vt220_lock, flags);
-+	count = 0;
-+	if (sclp_vt220_current_request != NULL)
-+		count = sclp_vt220_space_left(sclp_vt220_current_request);
-+	list_for_each(l, &sclp_vt220_empty)
-+		count += SCLP_VT220_MAX_CHARS_PER_BUFFER;
-+	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-+	return count;
++	MOD_INC_USE_COUNT;
++	/*
++	 * fetch buffer from userland
++	 */
++	buf = kmalloc(count,GFP_KERNEL);
++	if (buf == NULL) {
++		rc = -ENOMEM;
++		goto out_nobuf;
++	}
++	if (copy_from_user(buf, buffer, count)) {
++		rc = -EFAULT;
++		goto out;
++	}
++	for (i=0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i<count); i++) {
++			buf[i] = toupper(buf[i]);
++	}
++	*(buf+i) = '\0';
++	write_lock(&dcssblk_devices_lock);
++	device = dcssblk_get_device_by_name (buf);
++	if (device == NULL) {
++		rc = -ENODEV;
++		write_unlock (&dcssblk_devices_lock);
++		goto out;
++	}
++	if (device->use_count != 0) {
++		rc = -EBUSY;
++		write_unlock (&dcssblk_devices_lock);
++		goto out;
++	}
++	list_del(&device->lh);
++	write_unlock(&dcssblk_devices_lock);
++	segment_unload (device->name);
++#ifdef CONFIG_PROC_FS
++	remove_proc_entry("save", device->dcssblk_subdir_entry);
++	remove_proc_entry("shared", device->dcssblk_subdir_entry);
++	remove_proc_entry(device->name, dcssblk_proc_root_entry);
++#endif
++	devfs_unregister(device->devfs_entry );
++	
++	PRINT_DEBUG ("SEGMENT %s unloaded successfully\n",
++		     device->name);
++	kfree (device);	
++	rc = count;
++	MOD_DEC_USE_COUNT; // permanent
++ out:
++	kfree (buf);
++ out_nobuf:
++	MOD_DEC_USE_COUNT;
++	return rc;
 +}
 +
 +/*
-+ * Return number of buffered chars.
++ * device attribute for listing devices
 + */
-+static int
-+sclp_vt220_chars_in_buffer(struct tty_struct *tty)
++static int dcssblk_list_store(char *buffer, char **start, off_t offset,
++				int count, int *eof, void *data)
 +{
-+	unsigned long flags;
-+	struct list_head *l;
-+	struct sclp_vt220_request *r;
-+	int count;
++	unsigned int minor, len;
++	struct list_head* entry;
 +
-+	spin_lock_irqsave(&sclp_vt220_lock, flags);
-+	count = 0;
-+	if (sclp_vt220_current_request != NULL)
-+		count = sclp_vt220_chars_stored(sclp_vt220_current_request);
-+	list_for_each(l, &sclp_vt220_outqueue) {
-+		r = list_entry(l, struct sclp_vt220_request, list);
-+		count += sclp_vt220_chars_stored(r);
++	len = 0;
++	read_lock(&dcssblk_devices_lock);
++	for (minor=0; minor< (1<<MINORBITS); minor++) {
++		// test if minor available
++		list_for_each (entry, &dcssblk_devices)
++			if (minor == MINOR((list_entry(entry, dcss_device_t, 
++						       lh)->kdev))) {
++				len += sprintf(buffer + len, "%i\t%s\n", minor,
++					list_entry(entry, dcss_device_t,
++							lh)->name);
++			}
 +	}
-+	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-+	return count;
++	read_unlock(&dcssblk_devices_lock);
++	*eof = 1;
++	return len;
 +}
 +
-+static void
-+__sclp_vt220_flush_buffer(void)
++static int dcssblk_open (struct inode *inode, struct file *filp)
 +{
-+	unsigned long flags;
-+
-+	sclp_vt220_emit_current();
-+	spin_lock_irqsave(&sclp_vt220_lock, flags);
-+	if (timer_pending(&sclp_vt220_timer))
-+		del_timer(&sclp_vt220_timer);
-+	while (sclp_vt220_outqueue_count > 0) {
-+		spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-+		sclp_sync_wait();
-+		spin_lock_irqsave(&sclp_vt220_lock, flags);
++	dcss_device_t *dcssdev;
++	int rc;
++	write_lock(&dcssblk_devices_lock);
++	if ((dcssdev=dcssblk_get_device_by_minor(MINOR(inode->i_rdev)))
++	    == NULL) {		
++		rc=-ENODEV;
++		goto out_unlock;
 +	}
-+	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++	dcssdev->use_count ++;
++	rc = 0;
++ out_unlock:
++	write_unlock(&dcssblk_devices_lock);
++      	return rc;
 +}
 +
-+/*
-+ * Pass on all buffers to the hardware. Return only when there are no more
-+ * buffers pending.
-+ */
-+static void
-+sclp_vt220_flush_buffer(struct tty_struct *tty)
++static int dcssblk_release (struct inode *inode, struct file *filp)
 +{
-+	sclp_vt220_emit_current();
++	dcss_device_t *dcssdev;
++	int rc;
++	write_lock(&dcssblk_devices_lock);
++	if ((dcssdev=dcssblk_get_device_by_minor(MINOR(inode->i_rdev)))
++	    == NULL) {
++		rc=-ENODEV;
++		goto out_unlock;
++	}
++	dcssdev->use_count --;
++	if ((dcssdev->use_count==0) && (dcssdev->save_pending)) {
++		PRINT_WARN ("Segment %s became idle and is being saved\n",
++			    dcssdev->name);
++		dcssblk_do_save (dcssdev);
++		dcssdev->save_pending = 0;
++	}
++	rc = 0;
++ out_unlock:
++	write_unlock(&dcssblk_devices_lock);
++	return rc;
 +}
 +
-+/*
-+ * Initialize all relevant components and register driver with system.
-+ */
-+static void
-+__sclp_vt220_init(int early)
-+{
-+	void *page;
-+	int i;
++static int dcssblk_make_request (request_queue_t * q, int rw, 
++				 struct buffer_head * bh) {
++	dcss_device_t *dcssdev;
++	unsigned long index;
++	unsigned long page_addr;
++	unsigned long source_addr;
++	unsigned long bytes;
 +
-+	if (sclp_vt220_initialized)
-+		return;
-+	sclp_vt220_initialized = 1;
-+	spin_lock_init(&sclp_vt220_lock);
-+	INIT_LIST_HEAD(&sclp_vt220_empty);
-+	INIT_LIST_HEAD(&sclp_vt220_outqueue);
-+	init_waitqueue_head(&sclp_vt220_waitq);
-+	init_timer(&sclp_vt220_timer);
-+	sclp_vt220_current_request = NULL;
-+	sclp_vt220_buffered_chars = 0;
-+	sclp_vt220_outqueue_count = 0;
-+	sclp_vt220_tty = NULL;
-+	sclp_vt220_refcount = 0;
-+	sclp_vt220_flush_later = 0;
++	read_lock(&dcssblk_devices_lock);
++	dcssdev = dcssblk_get_device_by_minor (MINOR(bh->b_rdev));
++	read_unlock(&dcssblk_devices_lock);
 +
-+	/* Allocate pages for output buffering */
-+	for (i = 0; i < (early ? MAX_CONSOLE_PAGES : MAX_KMEM_PAGES); i++) {
-+		if (early)
-+			page = alloc_bootmem_low_pages(PAGE_SIZE);
-+		else
-+			page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
-+		if (page == NULL)
-+			return;
-+		list_add_tail((struct list_head *) page, &sclp_vt220_empty);
++	if (dcssdev == NULL)
++		/* No such device. */
++		goto fail;
++	if ((bh->b_rsector & 3) != 0 || (bh->b_size & 4095) != 0)
++		/* Request is not page-aligned. */
++		goto fail;
++	if ((bh->b_size + (bh->b_rsector<<9)) 
++	    > (dcssdev->end - dcssdev->start + 1))
++		/* Request beyond end of DCSS segment. */
++		goto fail;     
++	index = (bh->b_rsector >> 3);
++	page_addr = (unsigned long) bh->b_data;
++	source_addr = dcssdev->start + (index<<12);
++	bytes = bh->b_size;
++	if ((page_addr & 4095) != 0 || (bytes & 4095) != 0)
++		/* More paranoia. */
++		goto fail;
++	if ((rw == READ) || (rw == READA)) {
++		memcpy ((void*)page_addr, (void*)source_addr, 
++			bytes);
++	} else {
++		memcpy ((void*)source_addr, (void*)page_addr, 
++			bytes);
 +	}
++	bh->b_end_io(bh, 1);
++	return 0;
++ fail:
++	bh->b_end_io(bh, 0);
++	return 0;
 +}
 +
++
 +/*
-+ * Register driver with SCLP and Linux and initialize internal tty structures.
++ * setup the block device related things
 + */
-+void __init
-+sclp_vt220_tty_init(void)
++static int __init dcssblk_setup_blkdev(void)
 +{
-+	int rc;
-+
-+	/* Note: we're not testing for CONSOLE_IS_SCLP here to preserve
-+	 * symmetry between VM and LPAR systems regarding ttyS1. */
-+	__sclp_vt220_init(0);
-+	rc = sclp_register(&sclp_vt220_register);
-+	if (rc != 0) {
-+		printk(KERN_ERR SCLP_VT220_PRINT_HEADER
-+		       "could not register tty - "
-+		       "sclp_register returned %d\n", rc);
-+		return;
-+	}
++	request_queue_t *q;
++	int i,rc;
 +
-+	memset (&sclp_vt220_driver, 0, sizeof(struct tty_driver));
-+	sclp_vt220_driver.magic	= TTY_DRIVER_MAGIC;
-+	sclp_vt220_driver.driver_name = SCLP_VT220_DRIVER_NAME;
-+	sclp_vt220_driver.name = SCLP_VT220_DEVICE_NAME;
-+	sclp_vt220_driver.name_base = 0;
-+	sclp_vt220_driver.major = SCLP_VT220_MAJOR;
-+	sclp_vt220_driver.minor_start = SCLP_VT220_MINOR;
-+	sclp_vt220_driver.num = 1;
-+	sclp_vt220_driver.type = TTY_DRIVER_TYPE_SYSTEM;
-+	sclp_vt220_driver.subtype = SYSTEM_TYPE_TTY;
-+	sclp_vt220_driver.init_termios = tty_std_termios;
-+	sclp_vt220_driver.flags = TTY_DRIVER_REAL_RAW;
-+	sclp_vt220_driver.refcount = &sclp_vt220_refcount;
-+	sclp_vt220_driver.table = sclp_vt220_table;
-+	sclp_vt220_driver.termios = sclp_vt220_termios;
-+	sclp_vt220_driver.termios_locked = sclp_vt220_termios_locked;
++	for (i=0; i < (1<<MINORBITS); i++)
++		dcssblk_blksizes[i] = PAGE_SIZE;
 +
-+	/* Required callbacks */
-+	sclp_vt220_driver.open = sclp_vt220_open;
-+	sclp_vt220_driver.close = sclp_vt220_close;
-+	sclp_vt220_driver.write = sclp_vt220_write;
-+	sclp_vt220_driver.put_char = sclp_vt220_put_char;
-+	sclp_vt220_driver.flush_chars = sclp_vt220_flush_chars;
-+	sclp_vt220_driver.write_room = sclp_vt220_write_room;
-+	sclp_vt220_driver.chars_in_buffer = sclp_vt220_chars_in_buffer;
-+	sclp_vt220_driver.flush_buffer = sclp_vt220_flush_buffer;
++	/*
++	 * Register blockdev
++	 */
++	rc = register_blkdev(0, DCSSBLK_NAME, &dcssblk_devops);
++	if (rc < 0) {
++		PRINT_ERR("Can't get register major\n");
++		return rc;
++	}
++	dcssblk_major = rc;
++	blksize_size[dcssblk_major] = dcssblk_blksizes;
++	hardsect_size[dcssblk_major] = dcssblk_blksizes;
 +
-+	/* Unsupported callbacks */
-+	sclp_vt220_driver.ioctl = NULL;
-+	sclp_vt220_driver.throttle = NULL;
-+	sclp_vt220_driver.unthrottle = NULL;
-+	sclp_vt220_driver.send_xchar = NULL;
-+	sclp_vt220_driver.set_termios = NULL;
-+	sclp_vt220_driver.set_ldisc = NULL;
-+	sclp_vt220_driver.stop = NULL;
-+	sclp_vt220_driver.start = NULL;
-+	sclp_vt220_driver.hangup = NULL;
-+	sclp_vt220_driver.break_ctl = NULL;
-+	sclp_vt220_driver.wait_until_sent = NULL;
-+	sclp_vt220_driver.read_proc = NULL;
-+	sclp_vt220_driver.write_proc = NULL;
++	
++	/*
++	 * Assign the other needed values: make request function, sizes and
++	 * hardsect size. All the minor devices feature the same value.
++	 */
 +
-+	rc = tty_register_driver(&sclp_vt220_driver);
-+	if (rc != 0)
-+		printk(KERN_ERR SCLP_VT220_PRINT_HEADER
-+		       "could not register tty - "
-+		       "sclp_drv_register returned %d\n", rc);
++	q = BLK_DEFAULT_QUEUE(dcssblk_major);
++	blk_queue_make_request (q, dcssblk_make_request);
++	return 0;
 +}
 +
-+#ifdef CONFIG_SCLP_VT220_CONSOLE
++static void dcssblk_unregister_blkdev(void) {
++	int rc;
++	rc = unregister_blkdev (dcssblk_major, DCSSBLK_NAME);
++	if (rc) {
++		PRINT_ERR ("Can't unregister blockdev\n");
++	}	
++} 
 +
-+static void
-+sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count)
++/* 
++ * Register procfs entries
++ */
++static int dcssblk_register_procfs(void)
 +{
-+	__sclp_vt220_write(0, (const unsigned char *) buf, count, 1, 1);
++#ifdef CONFIG_PROC_FS
++	dcssblk_proc_root_entry =  proc_mkdir ("dcssblk", &proc_root);
++	dcssblk_add_entry = create_proc_entry ("add",
++					       S_IFREG | S_IWUSR,
++					       dcssblk_proc_root_entry);
++
++	dcssblk_remove_entry = create_proc_entry ("remove",
++						  S_IFREG | S_IWUSR,
++						  dcssblk_proc_root_entry);
++
++	dcssblk_list_entry = create_proc_entry ("list",
++						  S_IFREG | S_IRUGO,
++						  dcssblk_proc_root_entry);
++
++	dcssblk_add_entry->write_proc = dcssblk_add_store;
++	dcssblk_add_entry->owner = THIS_MODULE;
++	dcssblk_remove_entry->write_proc = dcssblk_remove_store;
++	dcssblk_remove_entry->owner = THIS_MODULE;
++	dcssblk_list_entry->read_proc = dcssblk_list_store;
++	dcssblk_list_entry->owner = THIS_MODULE;
++#endif
++	dcssblk_devfs_dir = devfs_mk_dir (NULL, 
++					  "dcssblk", 
++					  NULL);
++	return 0;
++
 +}
 +
-+static kdev_t
-+sclp_vt220_con_device(struct console *c)
-+{
-+	return mk_kdev(SCLP_VT220_MAJOR, SCLP_VT220_MINOR);
++static void dcssblk_unregister_procfs(void) {
++	devfs_unregister(dcssblk_devfs_dir);
++#ifdef CONFIG_PROC_FS
++	remove_proc_entry ("list", dcssblk_proc_root_entry);
++	remove_proc_entry ("remove", dcssblk_proc_root_entry);
++	remove_proc_entry ("add", dcssblk_proc_root_entry);
++	remove_proc_entry ("dcssblk", NULL);
++#endif
 +}
 +
 +/*
-+ * This routine is called from panic when the kernel is going to give up.
-+ * We have to make sure that all buffers will be flushed to the SCLP.
-+ * Note that this function may be called from within an interrupt context.
++ * Finally, the init/exit functions.
 + */
-+static void
-+sclp_vt220_con_unblank(void)
++static void __exit dcssblk_exit(void)
 +{
-+	__sclp_vt220_flush_buffer();
++	dcssblk_unregister_procfs();
++	dcssblk_unregister_blkdev();
 +}
 +
-+/* Structure needed to register with printk */
-+static struct console sclp_vt220_console =
-+{
-+	.name = SCLP_VT220_CONSOLE_NAME,
-+	.write = sclp_vt220_con_write,
-+	.device = sclp_vt220_con_device,
-+	.unblank = sclp_vt220_con_unblank,
-+	.flags = CON_PRINTBUFFER,
-+	.index = SCLP_VT220_CONSOLE_INDEX
-+};
-+
-+void
-+sclp_vt220_con_init(void)
++static int __init dcssblk_init(void)
 +{
-+        if (!CONSOLE_IS_SCLP)
-+		return;
-+	__sclp_vt220_init(1);
-+	/* Attach linux console */
-+	register_console(&sclp_vt220_console);
++	int rc;
++	PRINT_DEBUG ("DCSSBLOCK INIT\n");
++	rc = dcssblk_register_procfs();
++	if (rc) goto out;
++	rc = dcssblk_setup_blkdev();
++	if (rc) {
++		dcssblk_unregister_procfs();
++		goto out;
++	}
++ out:
++	return rc;
 +}
 +
-+#endif /* CONFIG_SCLP_VT220_CONSOLE */
-+
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape.c drivers/s390/char/tape.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape.c	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tape.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1,1120 +0,0 @@
--
--/***********************************************************************
-- *  drivers/s390/char/tape.c
-- *    tape device driver for S/390 and zSeries tapes.
-- *
-- *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-- *    Author(s): Carsten Otte <cotte at de.ibm.com>
-- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-- *
-- ***********************************************************************
-- */
--
--#include "tapedefs.h"
++module_init(dcssblk_init);
++module_exit(dcssblk_exit);
+diff -urN kernel-source-2.4.27.orig/drivers/s390/block/Makefile drivers/s390/block/Makefile
+--- kernel-source-2.4.27.orig/drivers/s390/block/Makefile	2001-08-05 14:12:41.000000000 -0600
++++ drivers/s390/block/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -17,6 +17,8 @@
+ obj-$(CONFIG_DASD_FBA)  += dasd_fba_mod.o
+ obj-$(CONFIG_DASD_DIAG) += dasd_diag_mod.o
+ obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
++obj-$(CONFIG_DCSSBLK) += dcssblk.o
++obj-$(CONFIG_S390_CMF) += dasd_cmb.o
+ 
+ include $(TOPDIR)/Rules.make
+ 
+@@ -31,4 +33,3 @@
+ 
+ dasd_diag_mod.o: $(dasd_diag_mod-objs)
+ 	$(LD) -r -o $@ $(dasd_diag_mod-objs)
 -
--#include <linux/config.h>
--#include <linux/stddef.h>
--#include <linux/kernel.h>
--#include <linux/version.h>
--#include <linux/proc_fs.h>
--#include <linux/init.h>
--#include <asm/types.h>
--#include <asm/ccwcache.h>
--#include <asm/idals.h>
--#include <asm/ebcdic.h>
--#include <linux/compatmac.h>
--#ifdef MODULE
--#include <linux/module.h>
--#endif   
--#include <asm/debug.h>
--#ifdef CONFIG_S390_TAPE_DYNAMIC
--#include <asm/s390dyn.h>
--#endif
--#include "tape.h"
--#ifdef CONFIG_S390_TAPE_3490
--#include "tape3490.h"
--#endif
--#ifdef CONFIG_S390_TAPE_3480
--#include "tape3480.h"
--#endif
--#ifdef CONFIG_S390_TAPE_BLOCK
--#include "tapeblock.h"
--#endif
--#ifdef CONFIG_S390_TAPE_CHAR
--#include "tapechar.h"
--#endif
--#ifdef CONFIG_PROC_FS
--#include <linux/vmalloc.h>
--#endif
--#define PRINTK_HEADER "T390:"
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/ctrlchar.c drivers/s390/char/ctrlchar.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/ctrlchar.c	2003-08-25 05:44:42.000000000 -0600
++++ drivers/s390/char/ctrlchar.c	2006-02-12 12:47:23.000000000 -0700
+@@ -9,6 +9,7 @@
+ 
+ #include <linux/config.h>
+ #include <linux/stddef.h>
++#include <linux/errno.h>
+ #include <linux/sysrq.h>
+ #include <linux/ctype.h>
+ #include <linux/interrupt.h>
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/hwc_con.c drivers/s390/char/hwc_con.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/hwc_con.c	2002-08-02 18:39:44.000000000 -0600
++++ drivers/s390/char/hwc_con.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1,89 +0,0 @@
+-/*
+- *  drivers/s390/char/hwc_con.c
+- *    HWC line mode console driver
+- *
+- *  S390 version
+- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
+- */
 -
+-#include <linux/config.h>
+-#include <linux/kernel.h>
+-#include <linux/major.h>
+-#include <linux/errno.h>
+-#include <linux/kdev_t.h>
+-#include <linux/string.h>
+-#include <linux/console.h>
+-#include <linux/fs.h>
+-#include <linux/init.h>
 -
--/* state handling routines */
--inline void tapestate_set (tape_info_t * ti, int newstate);
--inline int tapestate_get (tape_info_t * ti);
--void tapestate_event (tape_info_t * ti, int event);
+-#include "hwc_rw.h"
 -
--/* our globals */
--tape_info_t *first_tape_info = NULL;
--tape_discipline_t *first_discipline = NULL;
--tape_frontend_t *first_frontend = NULL;
--devreg_t* tape_devreg[128];
--int devregct=0;
+-#ifdef CONFIG_HWC_CONSOLE
 -
--#ifdef TAPE_DEBUG
--debug_info_t *tape_debug_area = NULL;
--#endif
+-#define  hwc_console_major 4
+-#define  hwc_console_minor 64
+-#define  hwc_console_name  "console"
 -
--char* state_verbose[TS_SIZE]={
--    "TS_UNUSED",  "TS_IDLE", "TS_DONE", "TS_FAILED",
--    "TS_BLOCK_INIT",
--    "TS_BSB_INIT",
--    "TS_BSF_INIT",
--    "TS_DSE_INIT",
--    "TS_EGA_INIT",
--    "TS_FSB_INIT",
--    "TS_FSF_INIT",
--    "TS_LDI_INIT",
--    "TS_LBL_INIT",
--    "TS_MSE_INIT",
--    "TS_NOP_INIT",
--    "TS_RBA_INIT",
--    "TS_RBI_INIT",
--    "TS_RBU_INIT",
--    "TS_RBL_INIT",
--    "TS_RDC_INIT",
--    "TS_RFO_INIT",
--    "TS_RSD_INIT",
--    "TS_REW_INIT",
--    "TS_REW_RELEASE_INIT",
--    "TS_RUN_INIT",
--    "TS_SEN_INIT",
--    "TS_SID_INIT",
--    "TS_SNP_INIT",
--    "TS_SPG_INIT",
--    "TS_SWI_INIT",
--    "TS_SMR_INIT",
--    "TS_SYN_INIT",
--    "TS_TIO_INIT",
--    "TS_UNA_INIT",
--    "TS_WRI_INIT",
--    "TS_WTM_INIT",
--    "TS_NOT_OPER"};
+-void hwc_console_write (struct console *, const char *, unsigned int);
+-kdev_t hwc_console_device (struct console *);
+-void hwc_console_unblank (void);
 -
--char* event_verbose[TE_SIZE]= {
--    "TE_START", "TE_DONE", "TE_FAILED", "TE_ERROR", "TE_OTHER"};
+-#define  HWC_CON_PRINT_HEADER "hwc console driver: "
 -
--/* our root devfs handle */
--#ifdef CONFIG_DEVFS_FS
--devfs_handle_t tape_devfs_root_entry;
+-struct console hwc_console = {
+-	name:	hwc_console_name,
+-	write:	hwc_console_write,
+-	device:	hwc_console_device,
+-	unblank:hwc_console_unblank,
+-	flags:	CON_PRINTBUFFER,
+-};
 -
--inline void
--tape_mkdevfsroots (tape_info_t* ti) 
+-void 
+-hwc_console_write (
+-			  struct console *console,
+-			  const char *message,
+-			  unsigned int count)
 -{
--    char devno [5];
--    sprintf (devno,"%04x",ti->devinfo.devno);
--    ti->devfs_dir=devfs_mk_dir (tape_devfs_root_entry, devno, ti);
+-
+-	if (console->device (console) != hwc_console.device (&hwc_console)) {
+-
+-		hwc_printk (KERN_WARNING HWC_CON_PRINT_HEADER
+-			    "hwc_console_write() called with wrong "
+-			    "device number");
+-		return;
+-	}
+-	hwc_write (0, message, count);
 -}
 -
--inline void
--tape_rmdevfsroots (tape_info_t* ti)
+-kdev_t 
+-hwc_console_device (struct console * c)
 -{
--    devfs_unregister (ti->devfs_dir);
+-	return MKDEV (hwc_console_major, hwc_console_minor);
 -}
--#endif
--
--#ifdef CONFIG_PROC_FS
--/* our proc tapedevices entry */
--static struct proc_dir_entry *tape_devices_entry;
--
--typedef struct {
--	char *data;
--	int len;
--} tempinfo_t;
--
 -
--static int
--tape_devices_open (struct inode *inode, struct file *file)
+-void 
+-hwc_console_unblank (void)
 -{
--    int size=80;
--    tape_info_t* ti;
--    tempinfo_t* tempinfo;
--    char* data;
--    int pos=0;
--    tempinfo = kmalloc (sizeof(tempinfo_t),GFP_KERNEL);
--    if (!tempinfo)
--        return -ENOMEM;
--    for (ti=first_tape_info;ti!=NULL;ti=ti->next)
--        size+=80; // FIXME: Guess better!
--    data=vmalloc(size);
--    if (!data) {
--        kfree (tempinfo);
--        return -ENOMEM;
--    }
--    pos+=sprintf(data+pos,"TapeNo\tDevNo\tCuType\tCuModel\tDevType\tDevModel\tState\n");
--    for (ti=first_tape_info;ti!=NULL;ti=ti->next) {
--        pos+=sprintf(data+pos,"%d\t%04X\t%04X\t%02X\t%04X\t%02X\t\t%s\n",ti->rew_minor/2,
--                     ti->devinfo.devno,ti->devinfo.sid_data.cu_type,
--                     ti->devinfo.sid_data.cu_model,ti->devinfo.sid_data.dev_type,
--                     ti->devinfo.sid_data.dev_model,((tapestate_get(ti) >= 0) &&
--                                                       (tapestate_get(ti) < TS_SIZE)) ?
--                     state_verbose[tapestate_get (ti)] : "TS UNKNOWN");
--    }
--    tempinfo->len=pos;
--    tempinfo->data=data;
--    file->private_data= (void*) tempinfo;
--#ifdef MODULE
--    MOD_INC_USE_COUNT;
--#endif
--    return 0;
+-	hwc_unblank ();
 -}
 -
--static ssize_t
--tape_devices_read (struct file *file, char *user_buf, size_t user_len, loff_t * offset)
+-#endif
+-
+-void __init 
+-hwc_console_init (void)
 -{
--	loff_t len;
--	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+-	if (!MACHINE_HAS_HWC)
+-		return;
 -
--	if (*offset >= p_info->len) {
--		return 0;	/* EOF */
--	} else {
--		len =  user_len<(p_info->len - *offset)?user_len:(p_info->len - *offset);
--		if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
--			return -EFAULT;
--		(*offset) += len;
--		return len;	/* number of bytes "read" */
--	}
--}
+-	if (hwc_init () == 0) {
+-#ifdef CONFIG_HWC_CONSOLE
 -
--static int
--tape_devices_release (struct inode *inode, struct file *file)
--{
--	int rc = 0;
--	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
--	if (p_info) {
--		if (p_info->data)
--			vfree (p_info->data);
--		kfree (p_info);
--	}
--#ifdef MODULE
--        MOD_DEC_USE_COUNT;
+-		if (CONSOLE_IS_HWC)
+-			register_console (&hwc_console);
 -#endif
--	return rc;
+-	} else
+-		panic (HWC_CON_PRINT_HEADER "hwc initialisation failed !");
+-
+-	return;
 -}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/hwc_cpi.c drivers/s390/char/hwc_cpi.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/hwc_cpi.c	2001-10-11 10:43:29.000000000 -0600
++++ drivers/s390/char/hwc_cpi.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1,211 +0,0 @@
 -
--static struct file_operations tape_devices_file_ops =
--{
--	read:tape_devices_read,	/* read */
--	open:tape_devices_open,	/* open */
--	release:tape_devices_release,	/* close */
--};
+-/*
+- * Author: Martin Peschke <mpeschke at de.ibm.com>
+- * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation
+- */
 -
--static struct inode_operations tape_devices_inode_ops =
--{
--#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
--	default_file_ops:&tape_devices_file_ops		/* file ops */
--#endif				/* LINUX_IS_24 */
--};
--#endif /* CONFIG_PROC_FS */
+-#include <linux/string.h>
+-#include <linux/ctype.h>
+-#include <linux/module.h>
+-#include <linux/init.h>
+-#include <linux/errno.h>
+-#include <linux/slab.h>
+-#include <linux/version.h>
+-#include <asm/semaphore.h>
+-#include <asm/ebcdic.h>
+-#include "hwc_rw.h"
+-#include "hwc.h"
 -
--/* SECTION: Parameters for tape */
--char *tape[256] = { NULL, };
+-#define CPI_RETRIES		3
+-#define CPI_SLEEP_TICKS		50
 -
--#ifndef MODULE
--static char tape_parm_string[1024] __initdata = { 0, };
--static void
--tape_split_parm_string (char *str)
--{
--	char *tmp = str;
--	int count = 0;
--	while (tmp != NULL && *tmp != '\0') {
--		char *end;
--		int len;
--		end = strchr (tmp, ',');
--		if (end == NULL) {
--			len = strlen (tmp) + 1;
--		} else {
--			len = (long) end - (long) tmp + 1;
--			*end = '\0';
--			end++;
--		}
--		tape[count] = kmalloc (len * sizeof (char), GFP_ATOMIC);
--		if (tape[count] == NULL) {
--			printk (KERN_WARNING PRINTK_HEADER
--				"can't store tape= parameter no %d\n",
--				count + 1);
--			break;
--		}
--		memset (tape[count], 0, len * sizeof (char));
--		memcpy (tape[count], tmp, len * sizeof (char));
--		count++;
--		tmp = end;
--	};
--}
+-#define CPI_LENGTH_SYSTEM_TYPE	8
+-#define CPI_LENGTH_SYSTEM_NAME	8
+-#define CPI_LENGTH_SYSPLEX_NAME	8
 -
--void __init
--tape_parm_setup (char *str, int *ints)
--{
--	int len = strlen (tape_parm_string);
--	if (len != 0) {
--		strcat (tape_parm_string, ",");
--	}
--	strcat (tape_parm_string, str);
--}
+-typedef struct {
+-	_EBUF_HEADER
+-	u8 id_format;
+-	u8 reserved0;
+-	u8 system_type[CPI_LENGTH_SYSTEM_TYPE];
+-	u64 reserved1;
+-	u8 system_name[CPI_LENGTH_SYSTEM_NAME];
+-	u64 reserved2;
+-	u64 system_level;
+-	u64 reserved3;
+-	u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME];
+-	u8 reserved4[16];
+-} __attribute__ ((packed)) 
 -
--int __init
--tape_parm_call_setup (char *str)
--{
--	int dummy;
--	tape_parm_setup (str, &dummy);
--	return 1;
--}
+-cpi_evbuf_t;
 -
--#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,16))
--__setup("tape=", tape_parm_call_setup);
--#endif   /* kernel <2.2.19 */
--#endif   /* not defined MODULE */
+-typedef struct _cpi_hwcb_t {
+-	_HWCB_HEADER
+-	cpi_evbuf_t cpi_evbuf;
+-} __attribute__ ((packed)) 
 -
--static inline int
--tape_parm_strtoul (char *str, char **stra)
--{
--	char *temp = str;
--	int val;
--	if (*temp == '0') {
--		temp++;		/* strip leading zero */
--		if (*temp == 'x')
--			temp++;	/* strip leading x */
--	}
--	val = simple_strtoul (temp, &temp, 16);	/* interpret anything as hex */
--	*stra = temp;
--	return val;
--}
+-cpi_hwcb_t;
 -
--static inline devreg_t *
--tape_create_devreg (int devno)
--{
--	devreg_t *devreg = kmalloc (sizeof (devreg_t), GFP_KERNEL);
--	if (devreg != NULL) {
--		memset (devreg, 0, sizeof (devreg_t));
--		devreg->ci.devno = devno;
--		devreg->flag = DEVREG_TYPE_DEVNO;
--		devreg->oper_func = tape_oper_handler;
--	}
--	return devreg;
--}
+-cpi_hwcb_t *cpi_hwcb;
 -
--static inline void
--tape_parm_parse (char **str)
+-static int __init cpi_module_init (void);
+-static void __exit cpi_module_exit (void);
+-
+-module_init (cpi_module_init);
+-module_exit (cpi_module_exit);
+-
+-MODULE_AUTHOR (
+-		      "Martin Peschke, IBM Deutschland Entwicklung GmbH "
+-		      "<mpeschke at de.ibm.com>");
+-
+-MODULE_DESCRIPTION (
+-  "identify this operating system instance to the S/390 or zSeries hardware");
+-
+-static char *system_name = NULL;
+-MODULE_PARM (system_name, "s");
+-MODULE_PARM_DESC (system_name, "e.g. hostname - max. 8 characters");
+-
+-static char *sysplex_name = NULL;
+-#ifdef ALLOW_SYSPLEX_NAME
+-MODULE_PARM (sysplex_name, "s");
+-MODULE_PARM_DESC (sysplex_name, "if applicable - max. 8 characters");
+-#endif
+-
+-static char *system_type = "LINUX";
+-
+-hwc_request_t cpi_request =
+-{};
+-
+-hwc_callback_t cpi_callback;
+-
+-static DECLARE_MUTEX_LOCKED (sem);
+-
+-static int __init 
+-cpi_module_init (void)
 -{
--	char *temp;
--	int from, to,i,irq=0,rc,retries=0,tape_num=0;
--        s390_dev_info_t dinfo;
--        tape_info_t* ti,*tempti;
--        tape_discipline_t* disc;
--        long lockflags;
--	if (*str==NULL) {
--            /* no params present -> leave */
--            return;
--	}
--	while (*str) {
--		temp = *str;
--		from = 0;
--		to = 0;
+-	int retval;
+-	int system_type_length;
+-	int system_name_length;
+-	int sysplex_name_length = 0;
+-	int retries;
 -
--                /* turn off autodetect mode, if any range is present */
--                from = tape_parm_strtoul (temp, &temp);
--                to = from;
--                if (*temp == '-') {
--                    temp++;
--                    to = tape_parm_strtoul (temp, &temp);
--                }
--                for (i=from;i<=to;i++) {
--                    retries=0;
--                    // register for attch/detach of a devno
--                    tape_devreg[devregct]=tape_create_devreg(i);
--                    if (tape_devreg[devregct]==NULL) {
--                        PRINT_WARN ("Could not create devreg for devno %04x, dyn. attach for this devno deactivated.\n",i);
--                    } else {
--                        s390_device_register (tape_devreg[devregct++]);
--                    }
--                    // we are activating a device if it is present
--                    for (irq = get_irq_first(); irq!=-ENODEV; irq=get_irq_next(irq)) {
--                        rc = get_dev_info_by_irq (irq, &dinfo);
--                     
--                        disc = first_discipline;
--                        while ((dinfo.devno == i) && (disc != NULL) && (disc->cu_type != dinfo.sid_data.cu_type))
--                            disc = (tape_discipline_t *) (disc->next);
--                        if ((disc == NULL) || (rc == -ENODEV) || (i!=dinfo.devno)) {
--                            continue;
--                        }
--#ifdef TAPE_DEBUG
--                        debug_text_event (tape_debug_area,3,"det irq:  ");
--                        debug_int_event (tape_debug_area,3,irq);
--                        debug_text_event (tape_debug_area,3,"cu:       ");
--                        debug_int_event (tape_debug_area,3,disc->cu_type);
--#endif /* TAPE_DEBUG */
--                        PRINT_INFO ("using devno %04x with discipline %04x on irq %d as tape device %d\n",dinfo.devno,dinfo.sid_data.cu_type,irq,tape_num/2);
--                        /* Allocate tape structure  */
--                        ti = kmalloc (sizeof (tape_info_t), GFP_ATOMIC);
--                        if (ti == NULL) {
--#ifdef TAPE_DEBUG
--                            debug_text_exception (tape_debug_area,3,"ti:no mem ");
--#endif /* TAPE_DEBUG */
--                            PRINT_INFO ("tape: can't allocate memory for "
--                                        "tape info structure\n");
--                            continue;
--                        }
--                        memset(ti,0,sizeof(tape_info_t));
--                        ti->discipline = disc;
--                        disc->tape = ti;
--                        rc = tape_setup (ti, irq, tape_num);
--                        if (rc) {
--#ifdef TAPE_DEBUG
--                            debug_text_event (tape_debug_area,3,"tsetup err");
--                            debug_int_exception (tape_debug_area,3,rc);
--#endif /* TAPE_DEBUG */
--                            kfree (ti);
--                        } else {
--                            s390irq_spin_lock_irqsave (irq, lockflags);
--                            if (first_tape_info == NULL) {
--                                first_tape_info = ti;
--                            } else {
--                                tempti = first_tape_info;
--                                while (tempti->next != NULL)
--                                    tempti = tempti->next;
--                                tempti->next = ti;
--                            }
--                            s390irq_spin_unlock_irqrestore (irq, lockflags);
--                        }
--                    }
--                    tape_num+=2;
--                }
--                str++;
--        }
--}
+-	if (!MACHINE_HAS_HWC) {
+-		printk ("cpi: bug: hardware console not present\n");
+-		retval = -EINVAL;
+-		goto out;
+-	}
+-	if (!system_type) {
+-		printk ("cpi: bug: no system type specified\n");
+-		retval = -EINVAL;
+-		goto out;
+-	}
+-	system_type_length = strlen (system_type);
+-	if (system_type_length > CPI_LENGTH_SYSTEM_NAME) {
+-		printk ("cpi: bug: system type has length of %i characters - "
+-			"only %i characters supported\n",
+-			system_type_length,
+-			CPI_LENGTH_SYSTEM_TYPE);
+-		retval = -EINVAL;
+-		goto out;
+-	}
+-	if (!system_name) {
+-		printk ("cpi: no system name specified\n");
+-		retval = -EINVAL;
+-		goto out;
+-	}
+-	system_name_length = strlen (system_name);
+-	if (system_name_length > CPI_LENGTH_SYSTEM_NAME) {
+-		printk ("cpi: system name has length of %i characters - "
+-			"only %i characters supported\n",
+-			system_name_length,
+-			CPI_LENGTH_SYSTEM_NAME);
+-		retval = -EINVAL;
+-		goto out;
+-	}
+-	if (sysplex_name) {
+-		sysplex_name_length = strlen (sysplex_name);
+-		if (sysplex_name_length > CPI_LENGTH_SYSPLEX_NAME) {
+-			printk ("cpi: sysplex name has length of %i characters - "
+-				"only %i characters supported\n",
+-				sysplex_name_length,
+-				CPI_LENGTH_SYSPLEX_NAME);
+-			retval = -EINVAL;
+-			goto out;
+-		}
+-	}
+-	cpi_hwcb = kmalloc (sizeof (cpi_hwcb_t), GFP_KERNEL);
+-	if (!cpi_hwcb) {
+-		printk ("cpi: no storage to fulfill request\n");
+-		retval = -ENOMEM;
+-		goto out;
+-	}
+-	memset (cpi_hwcb, 0, sizeof (cpi_hwcb_t));
 -
+-	cpi_hwcb->length = sizeof (cpi_hwcb_t);
+-	cpi_hwcb->cpi_evbuf.length = sizeof (cpi_evbuf_t);
+-	cpi_hwcb->cpi_evbuf.type = 0x0B;
 -
--/* SECTION: Managing wrappers for ccwcache */
+-	memset (cpi_hwcb->cpi_evbuf.system_type, ' ', CPI_LENGTH_SYSTEM_TYPE);
+-	memcpy (cpi_hwcb->cpi_evbuf.system_type, system_type, system_type_length);
+-	HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.system_type, CPI_LENGTH_SYSTEM_TYPE);
+-	EBC_TOUPPER (cpi_hwcb->cpi_evbuf.system_type, CPI_LENGTH_SYSTEM_TYPE);
 -
--#define TAPE_EMERGENCY_REQUESTS 16
+-	memset (cpi_hwcb->cpi_evbuf.system_name, ' ', CPI_LENGTH_SYSTEM_NAME);
+-	memcpy (cpi_hwcb->cpi_evbuf.system_name, system_name, system_name_length);
+-	HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.system_name, CPI_LENGTH_SYSTEM_NAME);
+-	EBC_TOUPPER (cpi_hwcb->cpi_evbuf.system_name, CPI_LENGTH_SYSTEM_NAME);
 -
--static ccw_req_t *tape_emergency_req[TAPE_EMERGENCY_REQUESTS] =
--{NULL,};
--static spinlock_t tape_emergency_req_lock = SPIN_LOCK_UNLOCKED;
+-	cpi_hwcb->cpi_evbuf.system_level = LINUX_VERSION_CODE;
 -
--static void
--tape_init_emergency_req (void)
--{
--	int i;
--	for (i = 0; i < TAPE_EMERGENCY_REQUESTS; i++) {
--		tape_emergency_req[i] = (ccw_req_t *) get_free_page (GFP_KERNEL);
+-	if (sysplex_name) {
+-		memset (cpi_hwcb->cpi_evbuf.sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME);
+-		memcpy (cpi_hwcb->cpi_evbuf.sysplex_name, sysplex_name, sysplex_name_length);
+-		HWC_ASCEBC_STR (cpi_hwcb->cpi_evbuf.sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
+-		EBC_TOUPPER (cpi_hwcb->cpi_evbuf.sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
 -	}
--}
+-	cpi_request.block = cpi_hwcb;
+-	cpi_request.word = HWC_CMDW_WRITEDATA;
+-	cpi_request.callback = cpi_callback;
 -
--#ifdef MODULE // We only cleanup the emergency requests on module unload.
--static void
--tape_cleanup_emergency_req (void)
--{
--	int i;
--	for (i = 0; i < TAPE_EMERGENCY_REQUESTS; i++) {
--		if (tape_emergency_req[i])
--			free_page ((long) (tape_emergency_req[i]));
--		else
--			printk (KERN_WARNING PRINTK_HEADER "losing one page for 'in-use' emergency request\n");
--	}
--}
--#endif
+-	for (retries = CPI_RETRIES; retries; retries--) {
+-		retval = hwc_send (&cpi_request);
+-		if (retval) {
 -
--ccw_req_t *
--tape_alloc_request (char *magic, int cplength, int datasize)
--{
--	ccw_req_t *rv = NULL;
--	int i;
--	if ((rv = ccw_alloc_request (magic, cplength, datasize)) != NULL) {
--		return rv;
--	}
--	if (cplength * sizeof (ccw1_t) + datasize + sizeof (ccw_req_t) > PAGE_SIZE) {
--		return NULL;
--	}
--	spin_lock (&tape_emergency_req_lock);
--	for (i = 0; i < TAPE_EMERGENCY_REQUESTS; i++) {
--		if (tape_emergency_req[i] != NULL) {
--			rv = tape_emergency_req[i];
--			tape_emergency_req[i] = NULL;
+-			set_current_state (TASK_INTERRUPTIBLE);
+-			schedule_timeout (CPI_SLEEP_TICKS);
+-		} else {
+-
+-			down (&sem);
+-
+-			switch (cpi_hwcb->response_code) {
+-			case 0x0020:
+-				printk ("cpi: succeeded\n");
+-				break;
+-			default:
+-				printk ("cpi: failed with response code 0x%x\n",
+-					cpi_hwcb->response_code);
+-			}
+-			goto free;
 -		}
 -	}
--	spin_unlock (&tape_emergency_req_lock);
--	if (rv) {
--		memset (rv, 0, PAGE_SIZE);
--		rv->cache = (kmem_cache_t *) (tape_emergency_req + i);
--		strncpy ((char *) (&rv->magic), magic, 4);
--		ASCEBC ((char *) (&rv->magic), 4);
--		rv->cplength = cplength;
--		rv->datasize = datasize;
--		rv->data = (void *) ((long) rv + PAGE_SIZE - datasize);
--		rv->cpaddr = (ccw1_t *) ((long) rv + sizeof (ccw_req_t));
--	}
--	return rv;
+-
+-	printk ("cpi: failed (%i)\n", retval);
+-
+-      free:
+-	kfree (cpi_hwcb);
+-
+-      out:
+-	return retval;
 -}
 -
--void
--tape_free_request (ccw_req_t * request)
+-static void __exit 
+-cpi_module_exit (void)
 -{
--	if (request->cache >= (kmem_cache_t *) tape_emergency_req &&
--	    request->cache <= (kmem_cache_t *) (tape_emergency_req + TAPE_EMERGENCY_REQUESTS)) {
--		*((ccw_req_t **) (request->cache)) = request;
--	} else {
--		clear_normalized_cda ((ccw1_t *) (request->cpaddr));	// avoid memory leak caused by modeset_byte
--		ccw_free_request (request);
--	}
+-	printk ("cpi: exit\n");
 -}
 -
+-void 
+-cpi_callback (hwc_request_t * req)
+-{
+-	up (&sem);
+-}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/hwc.h drivers/s390/char/hwc.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/hwc.h	2002-11-28 16:53:14.000000000 -0700
++++ drivers/s390/char/hwc.h	2006-02-12 12:47:23.000000000 -0700
+@@ -1,275 +0,0 @@
 -/*
-- * Allocate a ccw request and reserve it for tape driver
+- *  drivers/s390/char/hwc.h
+- * 
+- *
+- *  S390 version
+- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
+- *
+- * 
+- * 
 - */
--inline
-- ccw_req_t *
--tape_alloc_ccw_req (tape_info_t * ti, int cplength, int datasize)
--{
--	char tape_magic_id[] = "tape";
--	ccw_req_t *cqr = NULL;
 -
--	if (!ti)
--		return NULL;
--	cqr = tape_alloc_request (tape_magic_id, cplength, datasize);
+-#ifndef __HWC_H__
+-#define __HWC_H__
 -
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--		PRINT_WARN ("empty CQR generated\n");
--#endif
--	}
--	cqr->magic = TAPE_MAGIC;	/* sets an identifier for tape driver   */
--	cqr->device = ti;	/* save pointer to tape info    */
--	return cqr;
--}
+-#define HWC_EXT_INT_PARAM_ADDR	0xFFFFFFF8
+-#define HWC_EXT_INT_PARAM_PEND 0x00000001
 -
--/*
-- * Find the tape_info_t structure associated with irq
-- */
--static inline tape_info_t *
--tapedev_find_info (int irq)
--{
--	tape_info_t *ti;
+-#define ET_OpCmd		0x01
+-#define ET_Msg		0x02
+-#define ET_StateChange	0x08
+-#define ET_PMsgCmd		0x09
+-#define ET_CntlProgOpCmd	0x20
+-#define ET_CntlProgIdent	0x0B
+-#define ET_SigQuiesce	0x1D
 -
--	ti = first_tape_info;
--	if (ti != NULL)
--		do {
--			if (ti->devinfo.irq == irq)
--				break;
--		} while ((ti = (tape_info_t *) ti->next) != NULL);
--	return ti;
--}
+-#define ET_OpCmd_Mask	0x80000000
+-#define ET_Msg_Mask		0x40000000
+-#define ET_StateChange_Mask	0x01000000
+-#define ET_PMsgCmd_Mask	0x00800000
+-#define ET_CtlProgOpCmd_Mask	0x00000001
+-#define ET_CtlProgIdent_Mask	0x00200000
+-#define ET_SigQuiesce_Mask	0x00000008
 -
--#define QUEUE_THRESHOLD 5
+-#define GMF_DOM		0x8000
+-#define GMF_SndAlrm	0x4000
+-#define GMF_HoldMsg	0x2000
 -
--/*
-- * Tape interrupt routine, called from Ingo's I/O layer
-- */
--void
--tape_irq (int irq, void *int_parm, struct pt_regs *regs)
--{
--	tape_info_t *ti = tapedev_find_info (irq);
+-#define LTF_CntlText	0x8000
+-#define LTF_LabelText	0x4000
+-#define LTF_DataText	0x2000
+-#define LTF_EndText	0x1000
+-#define LTF_PromptText	0x0800
 -
--	/* analyse devstat and fire event */
--	if (ti->devstat.dstat & DEV_STAT_UNIT_CHECK) {
--		tapestate_event (ti, TE_ERROR);
--	} else if (ti->devstat.dstat & (DEV_STAT_DEV_END)) {
--		tapestate_event (ti, TE_DONE);
--	} else
--		tapestate_event (ti, TE_OTHER);
--}
+-#define HWC_COMMAND_INITIATED	0
+-#define HWC_BUSY		2
+-#define HWC_NOT_OPERATIONAL	3
 -
--int 
--tape_oper_handler ( int irq, struct _devreg *dreg) {
--    tape_info_t* ti=first_tape_info;
--    tape_info_t* newtape;
--    int rc,tape_num,retries=0,i;
--    s390_dev_info_t dinfo;
--    tape_discipline_t* disc;
--#ifdef CONFIG_DEVFS_FS
--    tape_frontend_t* frontend;
--#endif
--    long lockflags;
--    while ((ti!=NULL) && (ti->devinfo.irq!=irq)) 
--        ti=ti->next;
--    if (ti!=NULL) {
--        // irq is (still) used by tape. tell ingo to try again later
--        PRINT_WARN ("Oper handler for irq %d called while irq still (internaly?) used.\n",irq);
--        return -EAGAIN;
--    }
--    // irq is not used by tape
--    rc = get_dev_info_by_irq (irq, &dinfo);
--    if (rc == -ENODEV) {
--        retries++;
--        rc = get_dev_info_by_irq (irq, &dinfo);
--        if (retries > 5) {
--            PRINT_WARN ("No device information for new dev. could be retrieved.\n");
--            return -ENODEV;
--        }
--    }
--    disc = first_discipline;
--    while ((disc != NULL) && (disc->cu_type != dinfo.sid_data.cu_type))
--        disc = (tape_discipline_t *) (disc->next);
--    if (disc == NULL)
--        PRINT_WARN ("No matching discipline for cu_type %x found, ignoring device %04x.\n",dinfo.sid_data.cu_type,dinfo.devno);
--    if (rc == -ENODEV) 
--        PRINT_WARN ("No device information for new dev. could be retrieved.\n");
--    if ((disc == NULL) || (rc == -ENODEV))
--        return -ENODEV;
--    
--    /* Allocate tape structure  */
--    ti = kmalloc (sizeof (tape_info_t), GFP_ATOMIC);
--    if (ti == NULL) {
--        PRINT_INFO ( "tape: can't allocate memory for "
--                    "tape info structure\n");
--        return -ENOBUFS;
--    }
--    memset(ti,0,sizeof(tape_info_t));
--    ti->discipline = disc;
--    disc->tape = ti;
--    tape_num=0;
--    if (*tape) {
--        // we have static device ranges, so fingure out the tape_num of the attached tape
--        for (i=0;i<devregct;i++)
--            if (tape_devreg[i]->ci.devno==dinfo.devno) {
--                tape_num=2*i;
--                break;
--            }
--    } else {
--        // we are running in autoprobe mode, find a free tape_num
--        newtape=first_tape_info;
--        while (newtape!=NULL) {
--            if (newtape->rew_minor==tape_num) {
--                // tape num in use. try next one
--                tape_num+=2;
--                newtape=first_tape_info;
--            } else {
--                // tape num not used by newtape. look at next tape info
--                newtape=newtape->next;
--            }
--        }
--    }
--    rc = tape_setup (ti, irq, tape_num);
--    if (rc) {
--        kfree (ti);
--        return -ENOBUFS;
--    }
--#ifdef CONFIG_DEVFS_FS
--    for (frontend=first_frontend;frontend!=NULL;frontend=frontend->next) 
--        frontend->mkdevfstree(ti);
--#endif
--    s390irq_spin_lock_irqsave (irq,lockflags);
--    if (first_tape_info == NULL) {
--        first_tape_info = ti;
--    } else {
--        newtape = first_tape_info;
--        while (newtape->next != NULL)
--            newtape = newtape->next;
--        newtape->next = ti;
--    }
--    s390irq_spin_unlock_irqrestore (irq, lockflags);
--    return 0;
--}
+-#define hwc_cmdw_t u32;
 -
+-#define HWC_CMDW_READDATA 0x00770005
 -
--static void
--tape_noper_handler ( int irq, int status ) {
--    tape_info_t *ti=first_tape_info;
--    tape_info_t *lastti;
--#ifdef CONFIG_DEVFS_FS
--    tape_frontend_t *frontend;
--#endif
--    long lockflags;
--    s390irq_spin_lock_irqsave(irq,lockflags);
--    while (ti!=NULL && ti->devinfo.irq!=irq) ti=ti->next;
--    if (ti==NULL) return;
--    if (tapestate_get(ti)!=TS_UNUSED) {
--        // device is in use!
--        PRINT_WARN ("Tape #%d was detached while it was busy. Expect errors!",ti->blk_minor/2);
--        tapestate_set(ti,TS_NOT_OPER);
--        ti->rc=-ENODEV; 
--	ti->wanna_wakeup=1;
--	switch (tapestate_get(ti)) {
--	case TS_REW_RELEASE_INIT:
--	    tapestate_set(ti,TS_NOT_OPER);
--	    wake_up (&ti->wq);
--	    break;
--#ifdef CONFIG_S390_TAPE_BLOCK
--	case TS_BLOCK_INIT:
--	    tapestate_set(ti,TS_NOT_OPER);
--	    schedule_tapeblock_exec_IO(ti);
--	    break;
--#endif
--	default:
--	    tapestate_set(ti,TS_NOT_OPER);
--	    wake_up_interruptible (&ti->wq);
--	}
--    } else {
--        // device is unused!
--        PRINT_WARN ("Tape #%d was detached.\n",ti->blk_minor/2);
--        if (ti==first_tape_info) {
--            first_tape_info=ti->next;
--        } else {
--            lastti=first_tape_info;
--            while (lastti->next!=ti) lastti=lastti->next;
--            lastti->next=ti->next;
--        }
--#ifdef CONFIG_DEVFS_FS
--        for (frontend=first_frontend;frontend!=NULL;frontend=frontend->next)
--            frontend->rmdevfstree(ti);
--        tape_rmdevfsroots(ti);
--#endif
--        kfree(ti);
--    }
--    s390irq_spin_unlock_irqrestore(irq,lockflags);
--    return;
--}
+-#define HWC_CMDW_WRITEDATA 0x00760005
 -
+-#define HWC_CMDW_WRITEMASK 0x00780005
 -
--void
--tape_dump_sense (devstat_t * stat)
--{
--#ifdef TAPE_DEBUG
--        int sl;
--#endif
--#if 0
+-#define GDS_ID_MDSMU		0x1310
 -
--	PRINT_WARN ("------------I/O resulted in unit check:-----------\n");
--	for (sl = 0; sl < 4; sl++) {
--		PRINT_WARN ("Sense:");
--		for (sct = 0; sct < 8; sct++) {
--			PRINT_WARN (" %2d:0x%02X", 8 * sl + sct,
--				    stat->ii.sense.data[8 * sl + sct]);
--		}
--		PRINT_WARN ("\n");
--	}
--	PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X "
--		    " %02X%02X%02X%02X %02X%02X%02X%02X \n",
--		    stat->ii.sense.data[0], stat->ii.sense.data[1],
--		    stat->ii.sense.data[2], stat->ii.sense.data[3],
--		    stat->ii.sense.data[4], stat->ii.sense.data[5],
--		    stat->ii.sense.data[6], stat->ii.sense.data[7],
--		    stat->ii.sense.data[8], stat->ii.sense.data[9],
--		    stat->ii.sense.data[10], stat->ii.sense.data[11],
--		    stat->ii.sense.data[12], stat->ii.sense.data[13],
--		    stat->ii.sense.data[14], stat->ii.sense.data[15]);
--	PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X "
--		    " %02X%02X%02X%02X %02X%02X%02X%02X \n",
--		    stat->ii.sense.data[16], stat->ii.sense.data[17],
--		    stat->ii.sense.data[18], stat->ii.sense.data[19],
--		    stat->ii.sense.data[20], stat->ii.sense.data[21],
--		    stat->ii.sense.data[22], stat->ii.sense.data[23],
--		    stat->ii.sense.data[24], stat->ii.sense.data[25],
--		    stat->ii.sense.data[26], stat->ii.sense.data[27],
--		    stat->ii.sense.data[28], stat->ii.sense.data[29],
--		    stat->ii.sense.data[30], stat->ii.sense.data[31]);
--#endif
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,3,"SENSE:");
--        for (sl=0;sl<31;sl++) {
--            debug_int_event (tape_debug_area,3,stat->ii.sense.data[sl]);
--        }
--        debug_int_exception (tape_debug_area,3,stat->ii.sense.data[31]);
--#endif
--}
+-#define GDS_ID_MDSRouteInfo	0x1311
 -
--/*
-- * Setup tape_info_t structure of a tape device
-- */
--int
--tape_setup (tape_info_t * ti, int irq, int minor)
--{
--	long lockflags;
--	int rc = 0;
+-#define GDS_ID_AgUnWrkCorr	0x1549
 -
--        if (minor>254) {
--            PRINT_WARN ("Device id %d on irq %d will not be accessible since this driver is restricted to 128 devices.\n",minor/2,irq);
--            return -EINVAL;
--        }
--	rc = get_dev_info_by_irq (irq, &(ti->devinfo));
--	if (rc == -ENODEV) {	/* end of device list */
--		return rc;
--	}
--	ti->rew_minor = minor;
--	ti->nor_minor = minor + 1;
--	ti->blk_minor = minor;
--#ifdef CONFIG_DEVFS_FS
--        tape_mkdevfsroots(ti);
--#endif
--	/* Register IRQ */
--#ifdef CONFIG_S390_TAPE_DYNAMIC
--        rc = s390_request_irq_special (irq, tape_irq, tape_noper_handler,0, "tape", &(ti->devstat));
--#else
--	rc = s390_request_irq (irq, tape_irq, 0, "tape", &(ti->devstat));
--#endif
--	s390irq_spin_lock_irqsave (irq, lockflags);
--	ti->next = NULL;
--	if (rc)
--            PRINT_WARN ("Cannot register irq %d, rc=%d\n", irq, rc);
--	init_waitqueue_head (&ti->wq);
--	ti->kernbuf = ti->userbuf = ti->discdata = NULL;
--	tapestate_set (ti, TS_UNUSED);
--        ti->discdata=NULL;
--	ti->discipline->setup_assist (ti);
--        ti->wanna_wakeup=0;
--	s390irq_spin_unlock_irqrestore (irq, lockflags);
--	return rc;
--}
+-#define GDS_ID_SNACondReport	0x1532
 -
--/*
-- *      tape_init will register the driver for each tape.
-- */
--int
--tape_init (void)
--{
--	long lockflags;
--	s390_dev_info_t dinfo;
--	tape_discipline_t *disc;
--	tape_info_t *ti = NULL, *tempti = NULL;
--        char *opt_char,*opt_block,*opt_3490,*opt_3480;
--	int irq = 0, rc, retries = 0, tape_num = 0;
--        static int initialized=0;
+-#define GDS_ID_CPMSU		0x1212
+-
+-#define GDS_ID_RoutTargInstr	0x154D
+-
+-#define GDS_ID_OpReq		0x8070
+-
+-#define GDS_ID_TextCmd		0x1320
+-
+-#define GDS_KEY_SelfDefTextMsg	0x31
+-
+-#define _HWCB_HEADER	u16	length; \
+-			u8	function_code; \
+-			u8	control_mask[3]; \
+-			u16	response_code;
+-
+-#define _EBUF_HEADER 	u16	length; \
+-			u8	type; \
+-			u8	flags; \
+-			u16	_reserved;
+-
+-typedef struct {
+-	_EBUF_HEADER
+-} __attribute__ ((packed)) 
+-
+-evbuf_t;
+-
+-#define _MDB_HEADER	u16	length; \
+-			u16	type; \
+-			u32	tag; \
+-			u32	revision_code;
+-
+-#define _GO_HEADER	u16	length; \
+-			u16	type; \
+-			u32	domid; \
+-			u8	hhmmss_time[8]; \
+-			u8	th_time[3]; \
+-			u8	_reserved_0; \
+-			u8	dddyyyy_date[7]; \
+-			u8	_reserved_1; \
+-			u16	general_msg_flags; \
+-			u8	_reserved_2[10]; \
+-			u8	originating_system_name[8]; \
+-			u8	job_guest_name[8];
+-
+-#define _MTO_HEADER	u16	length; \
+-			u16	type; \
+-			u16	line_type_flags; \
+-			u8	alarm_control; \
+-			u8	_reserved[3];
+-
+-typedef struct {
+-	_GO_HEADER
+-} __attribute__ ((packed)) 
+-
+-go_t;
+-
+-typedef struct {
+-	go_t go;
+-} __attribute__ ((packed)) 
 -
--        if (initialized) // Only init the devices once
--            return 0;
--        initialized=1;
+-mdb_body_t;
 -
--#ifdef TAPE_DEBUG
--        tape_debug_area = debug_register ( "tape", 3, 2, 10);
--        debug_register_view(tape_debug_area,&debug_hex_ascii_view);
--        debug_text_event (tape_debug_area,3,"begin init");
--#endif /* TAPE_DEBUG */
+-typedef struct {
+-	_MDB_HEADER
+-	mdb_body_t mdb_body;
+-} __attribute__ ((packed)) 
 -
--        /* print banner */        
--        PRINT_WARN ("IBM S/390 Tape Device Driver (v1.01).\n");
--        PRINT_WARN ("(C) IBM Deutschland Entwicklung GmbH, 2000\n");
--        opt_char=opt_block=opt_3480=opt_3490="not present";
--#ifdef CONFIG_S390_TAPE_CHAR
--        opt_char="built in";
--#endif
--#ifdef CONFIG_S390_TAPE_BLOCK
--        opt_block="built in";
--#endif
--#ifdef CONFIG_S390_TAPE_3480
--        opt_3480="built in";
--#endif
--#ifdef CONFIG_S390_TAPE_3490
--        opt_3490="built in";
--#endif
--        /* print feature info */
--        PRINT_WARN ("character device frontend   : %s\n",opt_char);
--        PRINT_WARN ("block device frontend       : %s\n",opt_block);
--        PRINT_WARN ("support for 3480 compatible : %s\n",opt_3480);
--        PRINT_WARN ("support for 3490 compatible : %s\n",opt_3490);
--        
--#ifndef MODULE
--        tape_split_parm_string(tape_parm_string);
--#endif
--        if (*tape) 
--            PRINT_INFO ("Using ranges supplied in parameters, disabling autoprobe mode.\n");
--        else
--            PRINT_INFO ("No parameters supplied, enabling autoprobe mode for all supported devices.\n");
--#ifdef CONFIG_S390_TAPE_3490
--        if (*tape)
--            first_discipline = tape3490_init (0); // no autoprobe for devices
--        else
--            first_discipline = tape3490_init (1); // do autoprobe since no parm specified
--	first_discipline->next = NULL;
--#endif
+-mdb_t;
 -
--#ifdef CONFIG_S390_TAPE_3480
--        if (first_discipline == NULL) {
--            if (*tape)
--                first_discipline = tape3480_init (0); // no autoprobe for devices
--            else 
--                first_discipline = tape3480_init (1); // do autoprobe since no parm specified
--            first_discipline->next = NULL;
--        } else {
--            if (*tape)
--                first_discipline->next = tape3480_init (0); // no autoprobe for devices
--            else
--                first_discipline->next = tape3480_init (1); // do autoprobe since no parm specified
--            ((tape_discipline_t*) (first_discipline->next))->next=NULL;
--        }
--#endif
--#ifdef CONFIG_DEVFS_FS
--        tape_devfs_root_entry=devfs_mk_dir (NULL, "tape", NULL);
--#endif CONFIG_DEVFS_FS
+-typedef struct {
+-	_EBUF_HEADER
+-	mdb_t mdb;
+-} __attribute__ ((packed)) 
 -
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,3,"dev detect");
--#endif /* TAPE_DEBUG */
--	/* Allocate the tape structures */
--        if (*tape!=NULL) {
--            // we have parameters, continue with parsing the parameters and set the devices online
--            tape_parm_parse (tape);
--        } else {
--            // we are running in autodetect mode, search all devices for compatibles
--            for (irq = get_irq_first(); irq!=-ENODEV; irq=get_irq_next(irq)) {
--		rc = get_dev_info_by_irq (irq, &dinfo);
--		disc = first_discipline;
--		while ((disc != NULL) && (disc->cu_type != dinfo.sid_data.cu_type))
--                    disc = (tape_discipline_t *) (disc->next);
--		if ((disc == NULL) || (rc == -ENODEV))
--                    continue;
--#ifdef TAPE_DEBUG
--                debug_text_event (tape_debug_area,3,"det irq:  ");
--                debug_int_event (tape_debug_area,3,irq);
--                debug_text_event (tape_debug_area,3,"cu:       ");
--                debug_int_event (tape_debug_area,3,disc->cu_type);
--#endif /* TAPE_DEBUG */
--                PRINT_INFO ("using devno %04x with discipline %04x on irq %d as tape device %d\n",dinfo.devno,dinfo.sid_data.cu_type,irq,tape_num/2);
--		/* Allocate tape structure  */
--		ti = kmalloc (sizeof (tape_info_t), GFP_ATOMIC);
--		if (ti == NULL) {
--#ifdef TAPE_DEBUG
--                    debug_text_exception (tape_debug_area,3,"ti:no mem ");
--#endif /* TAPE_DEBUG */
--                    PRINT_INFO ("tape: can't allocate memory for "
--				    "tape info structure\n");
--                    continue;
--		}
--		memset(ti,0,sizeof(tape_info_t));
--		ti->discipline = disc;
--		disc->tape = ti;
--		rc = tape_setup (ti, irq, tape_num);
--		if (rc) {
--#ifdef TAPE_DEBUG
--                    debug_text_event (tape_debug_area,3,"tsetup err");
--                    debug_int_exception (tape_debug_area,3,rc);
--#endif /* TAPE_DEBUG */
--                    kfree (ti);
--		} else {
--                    s390irq_spin_lock_irqsave (irq, lockflags);
--                    if (first_tape_info == NULL) {
--                        first_tape_info = ti;
--                    } else {
--                        tempti = first_tape_info;
--                        while (tempti->next != NULL)
--                            tempti = tempti->next;
--                        tempti->next = ti;
--                    }
--                    tape_num += 2;
--                    s390irq_spin_unlock_irqrestore (irq, lockflags);
--		}
--            }
--        }
--            
--	/* Allocate local buffer for the ccwcache       */
--	tape_init_emergency_req ();
--#ifdef CONFIG_PROC_FS
--#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
--	tape_devices_entry = create_proc_entry ("tapedevices",
--						S_IFREG | S_IRUGO | S_IWUSR,
--						&proc_root);
--	tape_devices_entry->proc_fops = &tape_devices_file_ops;
--	tape_devices_entry->proc_iops = &tape_devices_inode_ops;
--#else
--	tape_devices_entry = (struct proc_dir_entry *) kmalloc 
--            (sizeof (struct proc_dir_entry), GFP_ATOMIC);
--        if (tape_devices_entry) {
--            memset (tape_devices_entry, 0, sizeof (struct proc_dir_entry));
--            tape_devices_entry->name = "tapedevices";
--            tape_devices_entry->namelen = strlen ("tapedevices");
--            tape_devices_entry->low_ino = 0;
--            tape_devices_entry->mode = (S_IFREG | S_IRUGO | S_IWUSR);
--            tape_devices_entry->nlink = 1;
--            tape_devices_entry->uid = 0;
--            tape_devices_entry->gid = 0;
--            tape_devices_entry->size = 0;
--            tape_devices_entry->get_info = NULL;
--            tape_devices_entry->ops = &tape_devices_inode_ops;
--            proc_register (&proc_root, tape_devices_entry);
--	}
--#endif
--#endif /* CONFIG_PROC_FS */
+-msgbuf_t;
 -
--	return 0;
--}
+-typedef struct {
+-	_HWCB_HEADER
+-	msgbuf_t msgbuf;
+-} __attribute__ ((packed)) 
 -
--#ifdef MODULE
--MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte (cotte at de.ibm.com)");
--MODULE_DESCRIPTION("Linux for S/390 channel attached tape device driver");
--MODULE_PARM (tape, "1-" __MODULE_STRING (256) "s");
+-write_hwcb_t;
 -
--int
--init_module (void)
--{
--#ifdef CONFIG_S390_TAPE_CHAR
--	tapechar_init ();
--#endif
--#ifdef CONFIG_S390_TAPE_BLOCK
--	tapeblock_init ();
--#endif
--        return 0;
--}
+-typedef struct {
+-	_MTO_HEADER
+-} __attribute__ ((packed)) 
 -
--void
--cleanup_module (void)
+-mto_t;
+-
+-static write_hwcb_t write_hwcb_template =
 -{
--        tape_info_t *ti ,*temp;
--        tape_frontend_t* frontend, *tempfe;
--        tape_discipline_t* disc ,*tempdi;
--        int i;
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"cleaup mod");
--#endif /* TAPE_DEBUG */
+-	sizeof (write_hwcb_t),
+-	0x00,
+-	{
+-		0x00,
+-		0x00,
+-		0x00
+-	},
+-	0x0000,
+-	{
+-		sizeof (msgbuf_t),
+-		ET_Msg,
+-		0x00,
+-		0x0000,
+-		{
+-			sizeof (mdb_t),
+-			0x0001,
+-			0xD4C4C240,
+-			0x00000001,
+-			{
+-				{
+-					sizeof (go_t),
+-					0x0001
 -
--        if (*tape) {
--            // we are running with parameters. we'll now deregister from our devno's
--            for (i=0;i<devregct;i++) {
--                s390_device_unregister(tape_devreg[devregct]);
--            }
--        }
--	ti = first_tape_info;
--	while (ti != NULL) {
--		temp = ti;
--		ti = ti->next;
--                //cleanup a device 
--#ifdef TAPE_DEBUG
--                debug_text_event (tape_debug_area,6,"free irq:");
--                debug_int_event (tape_debug_area,6,temp->devinfo.irq);
--#endif /* TAPE_DEBUG */
--		free_irq (temp->devinfo.irq, &(temp->devstat));
--                if (temp->discdata) kfree (temp->discdata);
--                if (temp->kernbuf) kfree (temp->kernbuf);
--                if (temp->cqr) tape_free_request(temp->cqr);
--#ifdef CONFIG_DEVFS_FS
--                for (frontend=first_frontend;frontend!=NULL;frontend=frontend->next)
--                    frontend->rmdevfstree(temp);
--                tape_rmdevfsroots(temp);
--#endif
--		kfree (temp);
--	}
--#ifdef CONFIG_DEVFS_FS
--        devfs_unregister (tape_devfs_root_entry);
--#endif CONFIG_DEVFS_FS
--#ifdef CONFIG_PROC_FS
--#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
--	remove_proc_entry ("tapedevices", &proc_root);
--#else
--	proc_unregister (&proc_root, tape_devices_entry->low_ino);
--	kfree (tape_devices_entry);
--#endif				/* LINUX_IS_24 */
--#endif 
--#ifdef CONFIG_S390_TAPE_CHAR
--	tapechar_uninit();
--#endif
--#ifdef CONFIG_S390_TAPE_BLOCK
--        tapeblock_uninit();
--#endif
--        frontend=first_frontend;
--	while (frontend != NULL) {
--		tempfe = frontend;
--		frontend = frontend->next;
--		kfree (tempfe);
--	}
--        disc=first_discipline;
--	while (disc != NULL) {
--                if (*tape)
--                    disc->shutdown(0);
--                else
--                    disc->shutdown(1);
--		tempdi = disc;
--		disc = disc->next;
--		kfree (tempdi);
+-				}
+-			}
+-		}
 -	}
--	/* Deallocate the local buffer for the ccwcache         */
--	tape_cleanup_emergency_req ();
--#ifdef TAPE_DEBUG
--        debug_unregister (tape_debug_area);
--#endif /* TAPE_DEBUG */
--}
--#endif				/* MODULE */
+-};
 -
--inline void
--tapestate_set (tape_info_t * ti, int newstate)
+-static mto_t mto_template =
 -{
--    if (ti->tape_state == TS_NOT_OPER) {
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,3,"ts_set err");
--        debug_text_exception (tape_debug_area,3,"dev n.oper");
--#endif /* TAPE_DEBUG */
--    } else {
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,4,"ts. dev:  ");
--        debug_int_event (tape_debug_area,4,ti->blk_minor);
--        debug_text_event (tape_debug_area,4,"old ts:   ");
--        debug_text_event (tape_debug_area,4,(((tapestate_get (ti) < TS_SIZE) &&
--                                             (tapestate_get (ti) >=0 )) ?
--                                            state_verbose[tapestate_get (ti)] :
--                                            "UNKNOWN TS"));
--        debug_text_event (tape_debug_area,4,"new ts:   ");
--        debug_text_event (tape_debug_area,4,(((newstate < TS_SIZE) &&
--                                              (newstate >= 0)) ?
--                                             state_verbose[newstate] :
--                                             "UNKNOWN TS"));
--#endif /* TAPE_DEBUG */
--	ti->tape_state = newstate;
--    }
--}
+-	sizeof (mto_t),
+-	0x0004,
+-	LTF_EndText,
+-	0x00
+-};
 -
--inline int
--tapestate_get (tape_info_t * ti)
+-typedef u32 _hwcb_mask_t;
+-
+-typedef struct {
+-	_HWCB_HEADER
+-	u16 _reserved;
+-	u16 mask_length;
+-	_hwcb_mask_t cp_receive_mask;
+-	_hwcb_mask_t cp_send_mask;
+-	_hwcb_mask_t hwc_receive_mask;
+-	_hwcb_mask_t hwc_send_mask;
+-} __attribute__ ((packed)) 
+-
+-init_hwcb_t;
+-
+-static init_hwcb_t init_hwcb_template =
 -{
--	return (ti->tape_state);
--}
+-	sizeof (init_hwcb_t),
+-	0x00,
+-	{
+-		0x00,
+-		0x00,
+-		0x00
+-	},
+-	0x0000,
+-	0x0000,
+-	sizeof (_hwcb_mask_t),
+-	ET_OpCmd_Mask | ET_PMsgCmd_Mask |
+-	ET_StateChange_Mask | ET_SigQuiesce_Mask,
+-	ET_Msg_Mask | ET_PMsgCmd_Mask | ET_CtlProgIdent_Mask
+-};
 -
--void
--tapestate_event (tape_info_t * ti, int event)
+-typedef struct {
+-	_EBUF_HEADER
+-	u8 validity_hwc_active_facility_mask:1;
+-	u8 validity_hwc_receive_mask:1;
+-	u8 validity_hwc_send_mask:1;
+-	u8 validity_read_data_function_mask:1;
+-	u16 _zeros:12;
+-	u16 mask_length;
+-	u64 hwc_active_facility_mask;
+-	_hwcb_mask_t hwc_receive_mask;
+-	_hwcb_mask_t hwc_send_mask;
+-	u32 read_data_function_mask;
+-} __attribute__ ((packed)) 
+-
+-statechangebuf_t;
+-
+-#define _GDS_VECTOR_HEADER	u16	length; \
+-				u16	gds_id;
+-
+-#define _GDS_SUBVECTOR_HEADER	u8	length; \
+-				u8	key;
+-
+-typedef struct {
+-	_GDS_VECTOR_HEADER
+-} __attribute__ ((packed)) 
+-
+-gds_vector_t;
+-
+-typedef struct {
+-	_GDS_SUBVECTOR_HEADER
+-} __attribute__ ((packed)) 
+-
+-gds_subvector_t;
+-
+-typedef struct {
+-	_HWCB_HEADER
+-} __attribute__ ((packed)) 
+-
+-read_hwcb_t;
+-
+-static read_hwcb_t read_hwcb_template =
 -{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"te! dev:  ");
--        debug_int_event (tape_debug_area,6,ti->blk_minor);
--        debug_text_event (tape_debug_area,6,"event:");
--        debug_text_event (tape_debug_area,6,((event >=0) &&
--                                            (event < TE_SIZE)) ?
--                         event_verbose[event] : "TE UNKNOWN");
--        debug_text_event (tape_debug_area,6,"state:");
--        debug_text_event (tape_debug_area,6,((tapestate_get(ti) >= 0) &&
--                                            (tapestate_get(ti) < TS_SIZE)) ?
--                         state_verbose[tapestate_get (ti)] :
--                         "TS UNKNOWN");
--#endif /* TAPE_DEBUG */    
--        if (event == TE_ERROR) { 
--            ti->discipline->error_recovery(ti);
--        } else {
--            if ((event >= 0) &&
--                (event < TE_SIZE) &&
--                (tapestate_get (ti) >= 0) &&
--                (tapestate_get (ti) < TS_SIZE) &&
--                ((*(ti->discipline->event_table))[tapestate_get (ti)][event] != NULL))
--		((*(ti->discipline->event_table))[tapestate_get (ti)][event]) (ti);
--            else {
--#ifdef TAPE_DEBUG
--                debug_text_exception (tape_debug_area,3,"TE UNEXPEC");
--#endif /* TAPE_DEBUG */
--		ti->discipline->default_handler (ti);
--            }
--        }
--}
+-	PAGE_SIZE,
+-	0x00,
+-	{
+-		0x00,
+-		0x00,
+-		0x80
+-	}
+-};
 -
+-#endif				/* __HWC_H__ */
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/hwc_rw.c drivers/s390/char/hwc_rw.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/hwc_rw.c	2002-11-28 16:53:14.000000000 -0700
++++ drivers/s390/char/hwc_rw.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1,2458 +0,0 @@
 -/*
-- * Overrides for Emacs so that we follow Linus's tabbing style.
-- * Emacs will notice this stuff at the end of the file and automatically
-- * adjust the settings for this buffer only.  This must remain at the end
-- * of the file.
-- * ---------------------------------------------------------------------------
-- * Local variables:
-- * c-indent-level: 4
-- * c-brace-imaginary-offset: 0
-- * c-brace-offset: -4
-- * c-argdecl-indent: 4
-- * c-label-offset: -4
-- * c-continued-statement-offset: 4
-- * c-continued-brace-offset: 0
-- * indent-tabs-mode: nil
-- * tab-width: 8
-- * End:
-- */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape.h drivers/s390/char/tape.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape.h	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tape.h	2006-01-30 22:25:26.000000000 -0700
-@@ -1,203 +1,436 @@
--/***************************************************************************
+- *  drivers/s390/char/hwc_rw.c
+- *     driver: reading from and writing to system console on S/390 via HWC
 - *
-+/*
-  *  drivers/s390/char/tape.h
-- *    tape device driver for 3480/3490E tapes.
-+ *    tape device driver for 3480/3490E/3590 tapes.
-  *
-  *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-+ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
-  *    Author(s): Carsten Otte <cotte at de.ibm.com>
-- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *  S390 version
+- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
 - *
-- ****************************************************************************
-+ *		 Tuan Ngo-Anh <ngoanh at de.ibm.com>
-+ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ *		 Stefan Bader <shbader at de.ibm.com>
-  */
- 
- #ifndef _TAPE_H
+- * 
+- *
+- * 
+- * 
+- * 
+- */
+-
+-#include <linux/kernel.h>
+-#include <linux/string.h>
+-#include <linux/errno.h>
+-#include <linux/ctype.h>
+-#include <linux/mm.h>
+-#include <linux/timer.h>
+-#include <linux/bootmem.h>
+-#include <linux/module.h>
+-
+-#include <asm/ebcdic.h>
+-#include <asm/uaccess.h>
+-#include <asm/types.h>
+-#include <asm/bitops.h>
+-#include <asm/setup.h>
+-#include <asm/page.h>
+-#include <asm/s390_ext.h>
+-#include <asm/irq.h>
+-
+-#ifndef MIN
+-#define MIN(a,b) (((a<b) ? a : b))
+-#endif
+-
+-extern void ctrl_alt_del (void);
+-
+-#define HWC_RW_PRINT_HEADER "hwc low level driver: "
+-
+-#define  USE_VM_DETECTION
+-
+-#define  DEFAULT_CASE_DELIMITER '%'
 -
- #define _TAPE_H
-+
- #include <linux/config.h>
- #include <linux/blkdev.h>
+-#undef DUMP_HWC_INIT_ERROR
 -
--#define  MAX_TAPES                     7        /* Max tapes supported is 7*/
--#define TAPE_MAGIC 0xE3C1D7C5       /* is ebcdic-"TAPE" */
+-#undef DUMP_HWC_WRITE_ERROR
 -
--typedef enum {
--    TS_UNUSED=0, TS_IDLE, TS_DONE, TS_FAILED,
--    TS_BLOCK_INIT,
--    TS_BSB_INIT,
--    TS_BSF_INIT,
--    TS_DSE_INIT,
--    TS_EGA_INIT,
--    TS_FSB_INIT,
--    TS_FSF_INIT,
--    TS_LDI_INIT,
--    TS_LBL_INIT,
--    TS_MSE_INIT,
--    TS_NOP_INIT,
--    TS_RBA_INIT,
--    TS_RBI_INIT,
--    TS_RBU_INIT,
--    TS_RBL_INIT,
--    TS_RDC_INIT,
--    TS_RFO_INIT,
--    TS_RSD_INIT,
--    TS_REW_INIT,
--    TS_REW_RELEASE_INIT,
--    TS_RUN_INIT,
--    TS_SEN_INIT,
--    TS_SID_INIT,
--    TS_SNP_INIT,
--    TS_SPG_INIT,
--    TS_SWI_INIT,
--    TS_SMR_INIT,
--    TS_SYN_INIT,
--    TS_TIO_INIT,
--    TS_UNA_INIT,
--    TS_WRI_INIT,
--    TS_WTM_INIT,
--    TS_NOT_OPER,
--    TS_SIZE } tape_stat;
+-#undef DUMP_HWC_WRITE_LIST_ERROR
 -
--struct _tape_info_t; //Forward declaration
+-#undef DUMP_HWC_READ_ERROR
 -
--typedef enum {
--    TE_START=0, TE_DONE, TE_FAILED, TE_ERROR, TE_OTHER,
--    TE_SIZE } tape_events;
+-#undef DUMP_HWCB_INPUT
 -
--typedef void (*tape_disc_shutdown_t) (int);
--typedef void (*tape_event_handler_t) (struct _tape_info_t*);
--typedef ccw_req_t* (*tape_ccwgen_t)(struct _tape_info_t* ti,int count);
--typedef ccw_req_t* (*tape_reqgen_t)(struct request* req,struct _tape_info_t* ti,int tapeblock_major);
--typedef ccw_req_t* (*tape_rwblock_t)(const char* data,size_t count,struct _tape_info_t* ti);
--typedef void (*tape_freeblock_t)(ccw_req_t* cqr,struct _tape_info_t* ti);
--typedef void (*tape_setup_assist_t) (struct _tape_info_t*);
-+#include <linux/kernel.h>
-+#include <linux/version.h>
-+#include <linux/module.h>
-+#include <linux/mtio.h>
-+#include <linux/interrupt.h>
-+#include <linux/timer.h>
-+#include <asm/debug.h>
-+#include <asm/idals.h>
-+#include <asm/s390dyn.h>
- #ifdef CONFIG_DEVFS_FS
--typedef void (*tape_devfs_handler_t) (struct _tape_info_t*);
-+#include <linux/devfs_fs_kernel.h>
- #endif
--typedef tape_event_handler_t tape_event_table_t[TS_SIZE][TE_SIZE];
--typedef struct _tape_discipline_t {
--    unsigned int cu_type;
--    tape_setup_assist_t setup_assist;
--    tape_event_handler_t error_recovery;
--    tape_reqgen_t bread;
--    tape_freeblock_t free_bread;
--    tape_rwblock_t write_block;
--    tape_freeblock_t free_write_block;
--    tape_rwblock_t read_block;
--    tape_freeblock_t free_read_block;
--    tape_ccwgen_t mtfsf;
--    tape_ccwgen_t mtbsf;
--    tape_ccwgen_t mtfsr;
--    tape_ccwgen_t mtbsr;
--    tape_ccwgen_t mtweof;
--    tape_ccwgen_t mtrew;
--    tape_ccwgen_t mtoffl;
--    tape_ccwgen_t mtnop;
--    tape_ccwgen_t mtbsfm;
--    tape_ccwgen_t mtfsfm;
--    tape_ccwgen_t mteom;
--    tape_ccwgen_t mterase;
--    tape_ccwgen_t mtsetdensity;
--    tape_ccwgen_t mtseek;
--    tape_ccwgen_t mttell;
--    tape_ccwgen_t mtsetdrvbuffer;
--    tape_ccwgen_t mtlock;
--    tape_ccwgen_t mtunlock;
--    tape_ccwgen_t mtload;
--    tape_ccwgen_t mtunload;
--    tape_ccwgen_t mtcompression;
--    tape_ccwgen_t mtsetpart;
--    tape_ccwgen_t mtmkpart;
--    tape_ccwgen_t mtiocget;
--    tape_ccwgen_t mtiocpos;
--    tape_disc_shutdown_t shutdown;
--    int (*discipline_ioctl_overload)(struct inode *,struct file*, unsigned int,unsigned long);
--    tape_event_table_t* event_table;
--    tape_event_handler_t default_handler;
--    struct _tape_info_t* tape; /* pointer for backreference */
--    void* next;
--} tape_discipline_t  __attribute__ ((aligned(8)));
- 
--typedef struct _tape_frontend_t {
--    tape_setup_assist_t device_setup;
-+/*
-+ * macros s390 debug feature (dbf)
-+ */
-+#define DBF_EVENT(d_level, d_str...) \
-+do { \
-+	debug_sprintf_event(TAPE_DBF_AREA, d_level, d_str); \
-+} while (0)
-+
-+#define DBF_EXCEPTION(d_level, d_str...) \
-+do { \
-+	debug_sprintf_exception(TAPE_DBF_AREA, d_level, d_str); \
-+} while (0)
-+
-+#define TAPE_VERSION_MAJOR 2
-+#define TAPE_VERSION_MINOR 0
-+#define TAPE_MAGIC "tape"
-+
-+#define TAPE_MINORS_PER_DEV 2       /* two minors per device */
-+#define TAPEBLOCK_HSEC_SIZE	2048
-+#define TAPEBLOCK_HSEC_S2B	2
-+#define TAPEBLOCK_RETRIES	5
-+
-+/* Event types for hotplug */
-+#define TAPE_HOTPLUG_CHAR_ADD     1
-+#define TAPE_HOTPLUG_BLOCK_ADD    2
-+#define TAPE_HOTPLUG_CHAR_REMOVE  3
-+#define TAPE_HOTPLUG_BLOCK_REMOVE 4
-+
-+enum tape_medium_state {
-+	MS_UNKNOWN,
-+	MS_LOADED,
-+	MS_UNLOADED,
-+	MS_SIZE
-+};
-+
-+enum tape_op {
-+	TO_BLOCK,	/* Block read */
-+	TO_BSB,		/* Backward space block */
-+	TO_BSF,		/* Backward space filemark */
-+	TO_DSE,		/* Data security erase */
-+	TO_FSB,		/* Forward space block */
-+	TO_FSF,		/* Forward space filemark */
-+	TO_LBL,		/* Locate block label */
-+	TO_NOP,		/* No operation */
-+	TO_RBA,		/* Read backward */
-+	TO_RBI,		/* Read block information */
-+	TO_RFO,		/* Read forward */
-+	TO_REW,		/* Rewind tape */
-+	TO_RUN,		/* Rewind and unload tape */
-+	TO_WRI,		/* Write block */
-+	TO_WTM,		/* Write tape mark */
-+	TO_MSEN,	/* Medium sense */
-+	TO_LOAD,	/* Load tape */
-+	TO_READ_CONFIG, /* Read configuration data */
-+	TO_READ_ATTMSG, /* Read attention message */
-+	TO_DIS,		/* Tape display */
-+	TO_ASSIGN,	/* Assign tape to channel path */
-+	TO_UNASSIGN,	/* Unassign tape from channel path */
-+	TO_BREAKASS,    /* Break the assignment of another host */
-+	TO_SIZE		/* #entries in tape_op_t */
-+};
-+
-+/* Forward declaration */
-+struct tape_device;
-+
-+/* The tape device list lock */
-+extern rwlock_t	  tape_dev_lock;
-+
-+/* Tape CCW request */
-+struct tape_request {
-+	struct list_head list;		/* list head for request queueing. */
-+	struct tape_device *device;	/* tape device of this request */
-+	ccw1_t *cpaddr;			/* address of the channel program. */
-+	void *cpdata;			/* pointer to ccw data. */
-+	char status;			/* status of this request */
-+	int options;			/* options for execution. */
-+	int retries;			/* retry counter for error recovery. */
-+
-+	/*
-+	 * This timer can be used to automatically cancel a request after
-+	 * some time. Specifically the assign request seems to lockup under
-+	 * certain circumstances.
-+	 */
-+	struct timer_list	timeout;
-+
-+	enum			tape_op op;
-+	int			rc;
-+	atomic_t		ref_count;
-+
-+	/* Callback for delivering final status. */
-+	void (*callback)(struct tape_request *, void *);
-+	void *callback_data;
-+};
-+
-+/* tape_request->status can be: */
-+#define TAPE_REQUEST_INIT	0x00	/* request is ready to be processed */
-+#define TAPE_REQUEST_QUEUED	0x01	/* request is queued to be processed */
-+#define TAPE_REQUEST_IN_IO	0x02	/* request is currently in IO */
-+#define TAPE_REQUEST_DONE	0x03	/* request is completed. */
-+
-+/* Function type for magnetic tape commands */
-+typedef int (*tape_mtop_fn)(struct tape_device *, int);
-+
-+/* Size of the array containing the mtops for a discipline */
-+#define TAPE_NR_MTOPS (MTMKPART+1)
-+
-+/* Tape Discipline */
-+struct tape_discipline {
-+	struct list_head list;
-+	struct module *owner;
-+	unsigned int cu_type;
-+	int  (*setup_device)(struct tape_device *);
-+	void (*cleanup_device)(struct tape_device *);
-+	int (*assign)(struct tape_device *);
-+	int (*unassign)(struct tape_device *);
-+	int (*force_unassign)(struct tape_device *);
-+	int (*irq)(struct tape_device *, struct tape_request *);
-+	struct tape_request *(*read_block)(struct tape_device *, size_t);
-+	struct tape_request *(*write_block)(struct tape_device *, size_t);
-+	void (*process_eov)(struct tape_device*);
-+	/* Block device stuff. */
-+	struct tape_request *(*bread)(struct tape_device *, struct request *);
-+	void (*check_locate)(struct tape_device *, struct tape_request *);
-+	void (*free_bread)(struct tape_request *);
-+	/* ioctl function for additional ioctls. */
-+	int (*ioctl_fn)(struct tape_device *, unsigned int, unsigned long);
-+	/* Array of tape commands with TAPE_NR_MTOPS entries */
-+	tape_mtop_fn *mtop_array;
-+};
-+
-+/*
-+ * The discipline irq function either returns an error code (<0) which
-+ * means that the request has failed with an error or one of the following:
-+ */
-+#define TAPE_IO_SUCCESS 0	/* request successful */
-+#define TAPE_IO_PENDING 1	/* request still running */
-+#define TAPE_IO_RETRY	2	/* retry to current request */
-+#define TAPE_IO_STOP	3	/* stop the running request */
-+
-+/* Char Frontend Data */
-+struct tape_char_data {
-+	/* Idal buffer to temporaily store character data */
-+	struct idal_buffer *	idal_buf;
-+	/* Block size (in bytes) of the character device (0=auto) */
-+	int			block_size;
-+#ifdef CONFIG_DEVFS_FS
-+	/* tape/<DEVNO>/char subdirectory in devfs */
-+	devfs_handle_t		devfs_char_dir;
-+	/* tape/<DEVNO>/char/nonrewinding entry in devfs */
-+	devfs_handle_t		devfs_nonrewinding;
-+	/* tape/<DEVNO>/char/rewinding entry in devfs */
-+	devfs_handle_t		devfs_rewinding;
-+#endif /* CONFIG_DEVFS_FS */
-+};
-+
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+/* Block Frontend Data */
-+struct tape_blk_data
-+{
-+	/* Block device request queue. */
-+	request_queue_t		request_queue;
-+	/* Block frontend tasklet */
-+	struct tasklet_struct	tasklet;
-+	/* Current position on the tape. */
-+	unsigned int		block_position;
-+	/* The start of the block device image file */
-+	unsigned int		start_block_id;
- #ifdef CONFIG_DEVFS_FS
--    tape_devfs_handler_t mkdevfstree;
--    tape_devfs_handler_t rmdevfstree;
-+	/* tape/<DEVNO>/block subdirectory in devfs */
-+	devfs_handle_t		devfs_block_dir;
-+	/* tape/<DEVNO>/block/disc entry in devfs */
-+	devfs_handle_t		devfs_disc;
-+#endif /* CONFIG_DEVFS_FS */
-+};
- #endif
--    void* next;
--} tape_frontend_t  __attribute__ ((aligned(8)));
- 
-+#define TAPE_STATUS_INIT		0x00000001
-+#define TAPE_STATUS_ASSIGN_M		0x00000002
-+#define TAPE_STATUS_ASSIGN_A		0x00000004
-+#define TAPE_STATUS_OPEN		0x00000008
-+#define TAPE_STATUS_BLOCKDEV		0x00000010
-+#define TAPE_STATUS_BOXED		0x20000000
-+#define TAPE_STATUS_NOACCESS		0x40000000
-+#define TAPE_STATUS_NOT_OPER		0x80000000
-+
-+#define TAPE_SET_STATE(td,st) \
-+	do { \
-+		tape_state_set(td, td->tape_status | (st)); \
-+	} while(0)
-+#define TAPE_CLEAR_STATE(td,st) \
-+	do { \
-+		tape_state_set(td, td->tape_status & ~(st)); \
-+	} while(0)
-+
-+#define TAPE_UNUSED(td)		(!TAPE_OPEN(td))
-+#define TAPE_INIT(td)		(td->tape_status & TAPE_STATUS_INIT)
-+#define TAPE_ASSIGNED(td)	( \
-+					td->tape_status & ( \
-+						TAPE_STATUS_ASSIGN_M | \
-+						TAPE_STATUS_ASSIGN_A \
-+					) \
-+				)
-+#define TAPE_OPEN(td)		(td->tape_status & TAPE_STATUS_OPEN)
-+#define TAPE_BLOCKDEV(td)	(td->tape_status & TAPE_STATUS_BLOCKDEV)
-+#define TAPE_BOXED(td)		(td->tape_status & TAPE_STATUS_BOXED)
-+#define TAPE_NOACCESS(td)	(td->tape_status & TAPE_STATUS_NOACCESS)
-+#define TAPE_NOT_OPER(td)	(td->tape_status & TAPE_STATUS_NOT_OPER)
-+
-+/* Tape Info */
-+struct tape_device {
-+	/* Device discipline information. */
-+	struct tape_discipline *discipline;
-+	void *			discdata;
-+
-+	/* Generic status bits */
-+	long			tape_generic_status;
-+	unsigned int		tape_status;
-+	enum tape_medium_state	medium_state;
-+
-+	/* Number of tapemarks required for correct termination */
-+	int			required_tapemarks;
-+
-+	/* Waitqueue for state changes and device flags */
-+	wait_queue_head_t	state_change_wq;
-+	unsigned char *		modeset_byte;
-+
-+	/* Reference count. */
-+	atomic_t		ref_count;
-+
-+	/* For persistent assign */
-+	spinlock_t		assign_lock;
-+
-+	/* Request queue. */
-+	struct list_head	req_queue;
-+	atomic_t		bh_scheduled;
-+	struct tq_struct	bh_task;
-+
-+	/* Common i/o stuff. */
-+	s390_dev_info_t		devinfo;
-+	devstat_t		devstat;
-+
-+	/* each tape device has two minors */
-+	int			first_minor;
- 
--typedef struct _tape_info_t {
--    wait_queue_head_t wq;
--    s390_dev_info_t devinfo;             /* device info from Common I/O */
--    int     wanna_wakeup;
--    int     rew_minor;                  /* minor number for the rewinding tape */
--    int     nor_minor;                  /* minor number for the nonrewinding tape */
--    int     blk_minor;                  /* minor number for the block device */
--    devstat_t devstat;	           /* contains irq, devno, status */
--    size_t  block_size;             /* block size of tape        */
--    int    drive_type;              /* Code indicating type of drive */
--    struct file *rew_filp;	           /* backpointer to file structure */
--    struct file *nor_filp;
--    struct file *blk_filp;
--    int tape_state;  /* State of the device. See tape_stat */
--    int rc;          /* Return code. */
--    tape_discipline_t* discipline;
--    request_queue_t request_queue;
--    struct request* current_request;
--    int blk_retries;
--    long position;
--    int medium_is_unloaded;  // Becomes true when a unload-type operation was issued, false again when medium-insert was detected
--    ccw_req_t* cqr;
--    atomic_t bh_scheduled;
--    struct tq_struct bh_tq;
- #ifdef CONFIG_DEVFS_FS
--    devfs_handle_t devfs_dir;             /* devfs handle for tape/DEVNO directory */
--    devfs_handle_t devfs_char_dir;        /* devfs handle for tape/DEVNO/char directory */
--    devfs_handle_t devfs_block_dir;       /* devfs handle for tape/DEVNO/block directory */
--    devfs_handle_t devfs_nonrewinding;    /* devfs handle for tape/DEVNO/char/nonrewinding device */
--    devfs_handle_t devfs_rewinding;       /* devfs handle for tape/DEVNO/char/rewinding device */
--    devfs_handle_t devfs_disc;            /* devfs handle for tape/DEVNO/block/disc device */
-+	/* Toplevel devfs directory. */
-+	devfs_handle_t		devfs_dir;
-+#endif /* CONFIG_DEVFS_FS */
-+	/* Character device frontend data */
-+	struct tape_char_data	char_data;
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+	/* Block dev frontend data */
-+	struct tape_blk_data	blk_data;
- #endif
--    void* discdata;
--    void* kernbuf;
--    void* userbuf;
--    void*  next;
--} tape_info_t  __attribute__ ((aligned(8)));
-+};
- 
--/* tape initialisation functions */
--int tape_init(void);
--int tape_setup (tape_info_t * ti, int irq, int minor);
-+/* Externals from tape_core.c */
-+struct tape_request *tape_alloc_request(int cplength, int datasize);
-+struct tape_request *tape_put_request(struct tape_request *);
-+struct tape_request *tape_clone_request(struct tape_request *);
-+int tape_do_io(struct tape_device *, struct tape_request *);
-+int tape_do_io_async(struct tape_device *, struct tape_request *);
-+int tape_do_io_interruptible(struct tape_device *, struct tape_request *);
-+void tape_schedule_bh(struct tape_device *);
-+void tape_hotplug_event(struct tape_device *, int, int);
-+
-+static inline int
-+tape_do_io_free(struct tape_device *device, struct tape_request *request)
-+{
-+	int rc;
-+
-+	rc = tape_do_io(device, request);
-+	tape_put_request(request);
-+	return rc;
-+}
-+
-+int tape_oper_handler(int irq, devreg_t *devreg);
-+void tape_noper_handler(int irq, int status);
-+int tape_open(struct tape_device *);
-+int tape_release(struct tape_device *);
-+int tape_assign(struct tape_device *, int type);
-+int tape_unassign(struct tape_device *, int type);
-+int tape_mtop(struct tape_device *, int, int);
-+
-+/* Externals from tape_devmap.c */
-+int tape_devmap_init(void);
-+void tape_devmap_exit(void);
-+
-+struct tape_device *tape_get_device(int devindex);
-+struct tape_device *tape_get_device_by_devno(int devno);
-+struct tape_device *tape_clone_device(struct tape_device *);
-+void tape_put_device(struct tape_device *);
-+
-+void tape_auto_detect(void);
-+void tape_add_devices(struct tape_discipline *);
-+void tape_remove_devices(struct tape_discipline *);
-+
-+extern int tape_max_devindex;
-+
-+/* Externals from tape_char.c */
-+int tapechar_init(void);
-+void tapechar_exit(void);
-+int  tapechar_setup_device(struct tape_device *);
-+void tapechar_cleanup_device(struct tape_device *);
-+
-+/* Externals from tape_block.c */
-+int tapeblock_init (void);
-+void tapeblock_exit(void);
-+int tapeblock_setup_device(struct tape_device *);
-+void tapeblock_cleanup_device(struct tape_device *);
-+void tapeblock_medium_change(struct tape_device *);
-+
-+/* Discipline functions */
-+int tape_register_discipline(struct tape_discipline *);
-+void tape_unregister_discipline(struct tape_discipline *);
-+struct tape_discipline *tape_get_discipline(int cu_type);
-+void tape_put_discipline(struct tape_discipline *);
-+int tape_enable_device(struct tape_device *, struct tape_discipline *);
-+void tape_disable_device(struct tape_device *device, int gone);
- 
--/* functoins for alloc'ing ccw stuff */
--inline  ccw_req_t * tape_alloc_ccw_req (tape_info_t* ti, int cplength, int datasize);
--void tape_free_request (ccw_req_t * request);
-+/* tape initialisation functions */
-+void tape_proc_init (void);
-+void tape_proc_cleanup (void);
- 
- /* a function for dumping device sense info */
--void tape_dump_sense (devstat_t * stat);
+-#undef BUFFER_STRESS_TEST
+-
+-typedef struct {
+-	unsigned char *next;
+-	unsigned short int mto_char_sum;
+-	unsigned char mto_number;
+-	unsigned char times_lost;
+-	unsigned short int mto_number_lost;
+-	unsigned long int mto_char_sum_lost;
+-} __attribute__ ((packed)) 
+-
+-hwcb_list_t;
+-
+-#define MAX_HWCB_ROOM (PAGE_SIZE - sizeof(hwcb_list_t))
+-
+-#define MAX_MESSAGE_SIZE (MAX_HWCB_ROOM - sizeof(write_hwcb_t))
+-
+-#define BUF_HWCB hwc_data.hwcb_list_tail
+-#define OUT_HWCB hwc_data.hwcb_list_head
+-#define ALL_HWCB_MTO hwc_data.mto_number
+-#define ALL_HWCB_CHAR hwc_data.mto_char_sum
+-
+-#define _LIST(hwcb) ((hwcb_list_t*)(&(hwcb)[PAGE_SIZE-sizeof(hwcb_list_t)]))
 -
--#ifdef CONFIG_S390_TAPE_DYNAMIC
--/* functions for dyn. dev. attach/detach */
--int tape_oper_handler ( int irq, struct _devreg *dreg);
+-#define _HWCB_CHAR(hwcb) (_LIST(hwcb)->mto_char_sum)
+-
+-#define _HWCB_MTO(hwcb) (_LIST(hwcb)->mto_number)
+-
+-#define _HWCB_CHAR_LOST(hwcb) (_LIST(hwcb)->mto_char_sum_lost)
+-
+-#define _HWCB_MTO_LOST(hwcb) (_LIST(hwcb)->mto_number_lost)
+-
+-#define _HWCB_TIMES_LOST(hwcb) (_LIST(hwcb)->times_lost)
+-
+-#define _HWCB_NEXT(hwcb) (_LIST(hwcb)->next)
+-
+-#define BUF_HWCB_CHAR _HWCB_CHAR(BUF_HWCB)
+-
+-#define BUF_HWCB_MTO _HWCB_MTO(BUF_HWCB)
+-
+-#define BUF_HWCB_NEXT _HWCB_NEXT(BUF_HWCB)
+-
+-#define OUT_HWCB_CHAR _HWCB_CHAR(OUT_HWCB)
+-
+-#define OUT_HWCB_MTO _HWCB_MTO(OUT_HWCB)
+-
+-#define OUT_HWCB_NEXT _HWCB_NEXT(OUT_HWCB)
+-
+-#define BUF_HWCB_CHAR_LOST _HWCB_CHAR_LOST(BUF_HWCB)
+-
+-#define BUF_HWCB_MTO_LOST _HWCB_MTO_LOST(BUF_HWCB)
+-
+-#define OUT_HWCB_CHAR_LOST _HWCB_CHAR_LOST(OUT_HWCB)
+-
+-#define OUT_HWCB_MTO_LOST _HWCB_MTO_LOST(OUT_HWCB)
+-
+-#define BUF_HWCB_TIMES_LOST _HWCB_TIMES_LOST(BUF_HWCB)
+-
+-#include  "hwc.h"
+-
+-#define __HWC_RW_C__
+-#include "hwc_rw.h"
+-#undef __HWC_RW_C__
+-
+-static unsigned char _obuf[MAX_HWCB_ROOM];
+-
+-static unsigned char
+- _page[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE)));
+-
+-typedef unsigned long kmem_pages_t;
+-
+-#define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3)
+-
+-#define HWC_WTIMER_RUNS	1
+-#define HWC_FLUSH		2
+-#define HWC_INIT		4
+-#define HWC_BROKEN		8
+-#define HWC_INTERRUPT		16
+-#define HWC_PTIMER_RUNS	32
+-
+-static struct {
+-
+-	hwc_ioctls_t ioctls;
+-
+-	hwc_ioctls_t init_ioctls;
+-
+-	unsigned char *hwcb_list_head;
+-
+-	unsigned char *hwcb_list_tail;
+-
+-	unsigned short int mto_number;
+-
+-	unsigned int mto_char_sum;
+-
+-	unsigned char hwcb_count;
+-
+-	unsigned long kmem_start;
+-
+-	unsigned long kmem_end;
+-
+-	kmem_pages_t kmem_pages;
+-
+-	unsigned char *obuf;
+-
+-	unsigned short int obuf_cursor;
+-
+-	unsigned short int obuf_count;
+-
+-	unsigned short int obuf_start;
+-
+-	unsigned char *page;
+-
+-	u32 current_servc;
+-
+-	unsigned char *current_hwcb;
+-
+-	unsigned char write_nonprio:1;
+-	unsigned char write_prio:1;
+-	unsigned char read_nonprio:1;
+-	unsigned char read_prio:1;
+-	unsigned char read_statechange:1;
+-	unsigned char sig_quiesce:1;
+-
+-	unsigned char flags;
+-
+-	hwc_high_level_calls_t *calls;
+-
+-	hwc_request_t *request;
+-
+-	spinlock_t lock;
+-
+-	struct timer_list write_timer;
+-
+-	struct timer_list poll_timer;
+-} hwc_data =
+-{
+-	{
+-	},
+-	{
+-		8,
+-		    0,
+-		    80,
+-		    1,
+-		    MAX_KMEM_PAGES,
+-		    MAX_KMEM_PAGES,
+-
+-		    0,
+-
+-		    0x6c
+-
+-	},
+-	    NULL,
+-	    NULL,
+-	    0,
+-	    0,
+-	    0,
+-	    0,
+-	    0,
+-	    0,
+-	    _obuf,
+-	    0,
+-	    0,
+-	    0,
+-	    _page,
+-	    0,
+-	    NULL,
+-	    0,
+-	    0,
+-	    0,
+-	    0,
+-	    0,
+-	    0,
+-	    0,
+-	    NULL,
+-	    NULL
+-
+-};
+-
+-static unsigned long cr0 __attribute__ ((aligned (8)));
+-static unsigned long cr0_save __attribute__ ((aligned (8)));
+-static unsigned char psw_mask __attribute__ ((aligned (8)));
+-
+-static ext_int_info_t ext_int_info_hwc;
+-
+-#define DELAYED_WRITE 0
+-#define IMMEDIATE_WRITE 1
+-
+-static signed int do_hwc_write (int from_user, unsigned char *,
+-				unsigned int,
+-				unsigned char);
+-
+-unsigned char hwc_ip_buf[512];
+-
+-static asmlinkage int 
+-internal_print (char write_time, char *fmt,...)
+-{
+-	va_list args;
+-	int i;
+-
+-	va_start (args, fmt);
+-	i = vsprintf (hwc_ip_buf, fmt, args);
+-	va_end (args);
+-	return do_hwc_write (0, hwc_ip_buf, i, write_time);
+-}
+-
+-int 
+-hwc_printk (const char *fmt,...)
+-{
+-	va_list args;
+-	int i;
+-	unsigned long flags;
+-	int retval;
+-
+-	spin_lock_irqsave (&hwc_data.lock, flags);
+-
+-	i = vsprintf (hwc_ip_buf, fmt, args);
+-	va_end (args);
+-	retval = do_hwc_write (0, hwc_ip_buf, i, IMMEDIATE_WRITE);
+-
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-
+-	return retval;
+-}
+-
+-#ifdef DUMP_HWCB_INPUT
+-
+-static void 
+-dump_storage_area (unsigned char *area, unsigned short int count)
+-{
+-	unsigned short int index;
+-	ioctl_nl_t old_final_nl;
+-
+-	if (!area || !count)
+-		return;
+-
+-	old_final_nl = hwc_data.ioctls.final_nl;
+-	hwc_data.ioctls.final_nl = 1;
+-
+-	internal_print (DELAYED_WRITE, "\n%8x   ", area);
+-
+-	for (index = 0; index < count; index++) {
+-
+-		if (area[index] <= 0xF)
+-			internal_print (DELAYED_WRITE, "0%x", area[index]);
+-		else
+-			internal_print (DELAYED_WRITE, "%x", area[index]);
+-
+-		if ((index & 0xF) == 0xF)
+-			internal_print (DELAYED_WRITE, "\n%8x   ",
+-					&area[index + 1]);
+-		else if ((index & 3) == 3)
+-			internal_print (DELAYED_WRITE, " ");
+-	}
+-
+-	internal_print (IMMEDIATE_WRITE, "\n");
+-
+-	hwc_data.ioctls.final_nl = old_final_nl;
+-}
 -#endif
-+void tape_dump_sense(struct tape_device *, struct tape_request *);
-+void tape_dump_sense_dbf(struct tape_device *, struct tape_request *);
- 
- /* functions for handling the status of a device */
--inline void tapestate_set (tape_info_t * ti, int newstate);
--inline int tapestate_get (tape_info_t * ti);
--void tapestate_event (tape_info_t * ti, int event);
--extern char* state_verbose[TS_SIZE];
--extern char* event_verbose[TE_SIZE];
 -
--/****************************************************************************/
+-static inline u32 
+-service_call (
+-		     u32 hwc_command_word,
+-		     unsigned char hwcb[])
+-{
+-	unsigned int condition_code = 1;
 -
--/* Some linked lists for storing plugins and devices */
--extern tape_info_t *first_tape_info;
--extern tape_discipline_t *first_discipline;
--extern tape_frontend_t *first_frontend;
-+inline void tape_state_set (struct tape_device *, unsigned int status);
-+inline void tape_med_state_set(struct tape_device *, enum tape_medium_state);
-+const char *tape_state_string(struct tape_device *);
-+
-+/* Tape 3480/3490 init/exit functions. */
-+int tape_34xx_init(void);
-+void tape_34xx_exit(void);
- 
- /* The debug area */
--#ifdef TAPE_DEBUG
--extern debug_info_t *tape_debug_area;
+-	__asm__ __volatile__ ("L 1, 0(%0) \n\t"
+-			      "LRA 2, 0(%1) \n\t"
+-			      ".long 0xB2200012 \n\t"
+-			      :
+-			      :"a" (&hwc_command_word), "a" (hwcb)
+-			      :"1", "2", "memory");
+-
+-	__asm__ __volatile__ ("IPM %0 \n\t"
+-			      "SRL %0, 28 \n\t"
+-			      :"=r" (condition_code));
+-
+-	return condition_code;
+-}
+-
+-static inline unsigned long 
+-hwc_ext_int_param (void)
+-{
+-	u32 param;
+-
+-	__asm__ __volatile__ ("L %0,128\n\t"
+-			      :"=r" (param));
+-
+-	return (unsigned long) param;
+-}
+-
+-static int 
+-prepare_write_hwcb (void)
+-{
+-	write_hwcb_t *hwcb;
+-
+-	if (!BUF_HWCB)
+-		return -ENOMEM;
+-
+-	BUF_HWCB_MTO = 0;
+-	BUF_HWCB_CHAR = 0;
+-
+-	hwcb = (write_hwcb_t *) BUF_HWCB;
+-
+-	memcpy (hwcb, &write_hwcb_template, sizeof (write_hwcb_t));
+-
+-	return 0;
+-}
+-
+-static int 
+-sane_write_hwcb (void)
+-{
+-	unsigned short int lost_msg;
+-	unsigned int lost_char;
+-	unsigned char lost_hwcb;
+-	unsigned char *bad_addr;
+-	unsigned long page;
+-	int page_nr;
+-
+-	if (!OUT_HWCB)
+-		return -ENOMEM;
+-
+-	if ((unsigned long) OUT_HWCB & 0xFFF) {
+-
+-		bad_addr = OUT_HWCB;
+-
+-#ifdef DUMP_HWC_WRITE_LIST_ERROR
+-		__asm__ ("LHI 1,0xe30\n\t"
+-			 "LRA 2,0(%0) \n\t"
+-			 "J .+0 \n\t"
+-	      :
+-	      :	 "a" (bad_addr)
+-	      :	 "1", "2");
 -#endif
-+extern debug_info_t *TAPE_DBF_AREA;
-+
-+/* functions for building ccws */
-+static inline ccw1_t *
-+tape_ccw_cc(ccw1_t *ccw, __u8 cmd_code, __u16 memsize, void *cda)
-+{
-+	ccw->cmd_code = cmd_code;
-+	ccw->flags = CCW_FLAG_CC;
-+	ccw->count = memsize;
-+	ccw->cda = (__u32)(addr_t) cda;
-+	return ccw + 1;
-+}
-+
-+static inline ccw1_t *
-+tape_ccw_end(ccw1_t *ccw, __u8 cmd_code, __u16 memsize, void *cda)
-+{
-+	ccw->cmd_code = cmd_code;
-+	ccw->flags = 0;
-+	ccw->count = memsize;
-+	ccw->cda = (__u32)(addr_t) cda;
-+	return ccw + 1;
-+}
-+
-+static inline ccw1_t *
-+tape_ccw_cmd(ccw1_t *ccw, __u8 cmd_code)
-+{
-+	ccw->cmd_code = cmd_code;
-+	ccw->flags = 0;
-+	ccw->count = 0;
-+	ccw->cda = (__u32)(addr_t) &ccw->cmd_code;
-+	return ccw + 1;
-+}
-+
-+static inline ccw1_t *
-+tape_ccw_repeat(ccw1_t *ccw, __u8 cmd_code, int count)
-+{
-+	while (count-- > 0) {
-+		ccw->cmd_code = cmd_code;
-+		ccw->flags = CCW_FLAG_CC;
-+		ccw->count = 0;
-+		ccw->cda = (__u32)(addr_t) &ccw->cmd_code;
-+		ccw++;
-+	}
-+	return ccw;
-+}
-+
-+extern inline ccw1_t*
-+tape_ccw_cc_idal(ccw1_t *ccw, __u8 cmd_code, struct idal_buffer *idal)
-+{
-+	ccw->cmd_code = cmd_code;
-+	ccw->flags    = CCW_FLAG_CC;
-+	idal_buffer_set_cda(idal, ccw);
-+	return ccw++;
-+}
-+
-+extern inline ccw1_t*
-+tape_ccw_end_idal(ccw1_t *ccw, __u8 cmd_code, struct idal_buffer *idal)
-+{
-+	ccw->cmd_code = cmd_code;
-+	ccw->flags    = 0;
-+	idal_buffer_set_cda(idal, ccw);
-+	return ccw++;
-+}
-+
-+/* Global vars */
-+extern const char *tape_op_verbose[];
- 
- #endif /* for ifdef tape.h */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3480.c drivers/s390/char/tape3480.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3480.c	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tape3480.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1,156 +0,0 @@
--/***************************************************************************
-- *
-- *  drivers/s390/char/tape3480.c
-- *    tape device discipline for 3480 tapes.
-- *
-- *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-- *    Author(s): Carsten Otte <cotte at de.ibm.com>
-- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-- * 
-- ****************************************************************************
-- */
 -
--#include "tapedefs.h"
--#include <linux/version.h>
--#include <asm/ccwcache.h>	/* CCW allocations      */
--#include <asm/s390dyn.h>
--#include <asm/debug.h>
--#include <linux/compatmac.h>
--#include "tape.h"
--#include "tape34xx.h"
--#include "tape3480.h"
+-		hwc_data.kmem_pages = 0;
+-		if ((unsigned long) BUF_HWCB & 0xFFF) {
+-
+-			lost_hwcb = hwc_data.hwcb_count;
+-			lost_msg = ALL_HWCB_MTO;
+-			lost_char = ALL_HWCB_CHAR;
+-
+-			OUT_HWCB = NULL;
+-			BUF_HWCB = NULL;
+-			ALL_HWCB_MTO = 0;
+-			ALL_HWCB_CHAR = 0;
+-			hwc_data.hwcb_count = 0;
+-		} else {
+-
+-			lost_hwcb = hwc_data.hwcb_count - 1;
+-			lost_msg = ALL_HWCB_MTO - BUF_HWCB_MTO;
+-			lost_char = ALL_HWCB_CHAR - BUF_HWCB_CHAR;
+-			OUT_HWCB = BUF_HWCB;
+-			ALL_HWCB_MTO = BUF_HWCB_MTO;
+-			ALL_HWCB_CHAR = BUF_HWCB_CHAR;
+-			hwc_data.hwcb_count = 1;
+-			page = (unsigned long) BUF_HWCB;
+-
+-			if (page >= hwc_data.kmem_start &&
+-			    page <= hwc_data.kmem_end) {
+-
+-				page_nr = (int)
+-				    ((page - hwc_data.kmem_start) >> 12);
+-				set_bit (page_nr, &hwc_data.kmem_pages);
+-			}
+-		}
+-
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-		       "found invalid HWCB at address 0x%lx. List corrupted. "
+-			   "Lost %i HWCBs with %i characters within up to %i "
+-			   "messages. Saved %i HWCB with last %i characters i"
+-				       "within up to %i messages.\n",
+-				       (unsigned long) bad_addr,
+-				       lost_hwcb, lost_char, lost_msg,
+-				       hwc_data.hwcb_count,
+-				       ALL_HWCB_CHAR, ALL_HWCB_MTO);
+-	}
+-	return 0;
+-}
+-
+-static int 
+-reuse_write_hwcb (void)
+-{
+-	int retval;
+-
+-	if (hwc_data.hwcb_count < 2)
+-#ifdef DUMP_HWC_WRITE_LIST_ERROR
+-		__asm__ ("LHI 1,0xe31\n\t"
+-			 "LRA 2,0(%0)\n\t"
+-			 "LRA 3,0(%1)\n\t"
+-			 "J .+0 \n\t"
+-	      :
+-	      :	 "a" (BUF_HWCB), "a" (OUT_HWCB)
+-	      :	 "1", "2", "3");
+-#else
+-		return -EPERM;
+-#endif
+-
+-	if (hwc_data.current_hwcb == OUT_HWCB) {
+-
+-		if (hwc_data.hwcb_count > 2) {
+-
+-			BUF_HWCB_NEXT = OUT_HWCB_NEXT;
+-
+-			BUF_HWCB = OUT_HWCB_NEXT;
+-
+-			OUT_HWCB_NEXT = BUF_HWCB_NEXT;
+-
+-			BUF_HWCB_NEXT = NULL;
+-		}
+-	} else {
+-
+-		BUF_HWCB_NEXT = OUT_HWCB;
+-
+-		BUF_HWCB = OUT_HWCB;
+-
+-		OUT_HWCB = OUT_HWCB_NEXT;
+-
+-		BUF_HWCB_NEXT = NULL;
+-	}
+-
+-	BUF_HWCB_TIMES_LOST += 1;
+-	BUF_HWCB_CHAR_LOST += BUF_HWCB_CHAR;
+-	BUF_HWCB_MTO_LOST += BUF_HWCB_MTO;
+-	ALL_HWCB_MTO -= BUF_HWCB_MTO;
+-	ALL_HWCB_CHAR -= BUF_HWCB_CHAR;
+-
+-	retval = prepare_write_hwcb ();
+-
+-	if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "reached my own limit of "
+-			    "allowed buffer space for output (%i HWCBs = %li "
+-			  "bytes), skipped content of oldest HWCB %i time(s) "
+-				       "(%i lines = %i characters)\n",
+-				       hwc_data.ioctls.max_hwcb,
+-				       hwc_data.ioctls.max_hwcb * PAGE_SIZE,
+-				       BUF_HWCB_TIMES_LOST,
+-				       BUF_HWCB_MTO_LOST,
+-				       BUF_HWCB_CHAR_LOST);
+-	else
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "page allocation failed, "
+-			   "could not expand buffer for output (currently in "
+-			     "use: %i HWCBs = %li bytes), skipped content of "
+-			"oldest HWCB %i time(s) (%i lines = %i characters)\n",
+-				       hwc_data.hwcb_count,
+-				       hwc_data.hwcb_count * PAGE_SIZE,
+-				       BUF_HWCB_TIMES_LOST,
+-				       BUF_HWCB_MTO_LOST,
+-				       BUF_HWCB_CHAR_LOST);
 -
--tape_event_handler_t tape3480_event_handler_table[TS_SIZE][TE_SIZE] =
+-	return retval;
+-}
+-
+-static int 
+-allocate_write_hwcb (void)
 -{
--    /* {START , DONE, FAILED, ERROR, OTHER } */
--	{NULL, tape34xx_unused_done, NULL, NULL, NULL},	/* TS_UNUSED */
--	{NULL, tape34xx_idle_done, NULL, NULL, NULL},	/* TS_IDLE */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_DONE */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_FAILED */
--	{NULL, tape34xx_block_done, NULL, NULL, NULL},		/* TS_BLOCK_INIT */
--	{NULL, tape34xx_bsb_init_done, NULL, NULL, NULL},	/* TS_BSB_INIT */
--	{NULL, tape34xx_bsf_init_done, NULL, NULL, NULL},	/* TS_BSF_INIT */
--	{NULL, tape34xx_dse_init_done, NULL, NULL, NULL},	/* TS_DSE_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_EGA_INIT */
--	{NULL, tape34xx_fsb_init_done, NULL, NULL, NULL},	/* TS_FSB_INIT */
--	{NULL, tape34xx_fsf_init_done, NULL, NULL, NULL},	/* TS_FSF_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_LDI_INIT */
--	{NULL, tape34xx_lbl_init_done, NULL, NULL, NULL},	/* TS_LBL_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_MSE_INIT */
--	{NULL, tape34xx_nop_init_done, NULL, NULL, NULL},	/* TS_NOP_INIT */
--	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},		/* TS_RBA_INIT */
--	{NULL, tape34xx_rbi_init_done, NULL, NULL, NULL},	/* TS_RBI_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBU_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBL_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RDC_INIT */
--	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},	/* TS_RFO_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RSD_INIT */
--	{NULL, tape34xx_rew_init_done, NULL, NULL, NULL},	/* TS_REW_INIT */
--	{NULL, tape34xx_rew_release_init_done, NULL, NULL, NULL},	/* TS_REW_RELEASE_IMIT */
--	{NULL, tape34xx_run_init_done, NULL, NULL, NULL},	/* TS_RUN_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SEN_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SID_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SNP_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SPG_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SWI_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SMR_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SYN_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_TIO_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_UNA_INIT */
--	{NULL, tape34xx_wri_init_done, NULL, NULL, NULL},	/* TS_WRI_INIT */
--	{NULL, tape34xx_wtm_init_done, NULL, NULL, NULL},	/* TS_WTM_INIT */
--        {NULL, NULL, NULL, NULL, NULL}};     /* TS_NOT_OPER */
+-	unsigned char *page;
+-	int page_nr;
 -
--devreg_t tape3480_devreg = {
--    ci:
--    {hc:
--     {ctype:0x3480}},
--    flag:DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS,
--    oper_func:tape_oper_handler
--};
+-	if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
+-		return -ENOMEM;
 -
+-	page_nr = find_first_zero_bit (&hwc_data.kmem_pages, MAX_KMEM_PAGES);
+-	if (page_nr < hwc_data.ioctls.kmem_hwcb) {
 -
--void
--tape3480_setup_assist (tape_info_t * ti)
--{
--	tape3480_disc_data_t *data = NULL;
--#ifdef TAPE_DEBUG
--    debug_text_event (tape_debug_area,6,"3480 dsetu");
--    debug_text_event (tape_debug_area,6,"dev:");
--    debug_int_event (tape_debug_area,6,ti->blk_minor);
--#endif /* TAPE_DEBUG */
--	while (data == NULL)
--		data = kmalloc (sizeof (tape3480_disc_data_t), GFP_KERNEL);
--	data->modeset_byte = 0x00;
--	ti->discdata = (void *) data;
--}
+-		page = (unsigned char *)
+-		    (hwc_data.kmem_start + (page_nr << 12));
+-		set_bit (page_nr, &hwc_data.kmem_pages);
+-	} else
+-		page = (unsigned char *) __get_free_page (GFP_ATOMIC | GFP_DMA);
 -
+-	if (!page)
+-		return -ENOMEM;
 -
--void
--tape3480_shutdown (int autoprobe) {
--    if (autoprobe)
--	s390_device_unregister(&tape3480_devreg);
--}
+-	if (!OUT_HWCB)
+-		OUT_HWCB = page;
+-	else
+-		BUF_HWCB_NEXT = page;
 -
--tape_discipline_t *
--tape3480_init (int autoprobe)
--{
--	tape_discipline_t *disc;
--#ifdef TAPE_DEBUG
--    debug_text_event (tape_debug_area,3,"3480 init");
--#endif /* TAPE_DEBUG */
--	disc = kmalloc (sizeof (tape_discipline_t), GFP_KERNEL);
--	if (disc == NULL) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,3,"disc:nomem");
--#endif /* TAPE_DEBUG */
--		return disc;
--	}
--	disc->cu_type = 0x3480;
--	disc->setup_assist = tape3480_setup_assist;
--	disc->error_recovery = tape34xx_error_recovery;
--	disc->write_block = tape34xx_write_block;
--	disc->free_write_block = tape34xx_free_write_block;
--	disc->read_block = tape34xx_read_block;
--	disc->free_read_block = tape34xx_free_read_block;
--	disc->mtfsf = tape34xx_mtfsf;
--	disc->mtbsf = tape34xx_mtbsf;
--	disc->mtfsr = tape34xx_mtfsr;
--	disc->mtbsr = tape34xx_mtbsr;
--	disc->mtweof = tape34xx_mtweof;
--	disc->mtrew = tape34xx_mtrew;
--	disc->mtoffl = tape34xx_mtoffl;
--	disc->mtnop = tape34xx_mtnop;
--	disc->mtbsfm = tape34xx_mtbsfm;
--	disc->mtfsfm = tape34xx_mtfsfm;
--	disc->mteom = tape34xx_mteom;
--	disc->mterase = tape34xx_mterase;
--	disc->mtsetdensity = tape34xx_mtsetdensity;
--	disc->mtseek = tape34xx_mtseek;
--	disc->mttell = tape34xx_mttell;
--	disc->mtsetdrvbuffer = tape34xx_mtsetdrvbuffer;
--	disc->mtlock = tape34xx_mtlock;
--	disc->mtunlock = tape34xx_mtunlock;
--	disc->mtload = tape34xx_mtload;
--	disc->mtunload = tape34xx_mtunload;
--	disc->mtcompression = tape34xx_mtcompression;
--	disc->mtsetpart = tape34xx_mtsetpart;
--	disc->mtmkpart = tape34xx_mtmkpart;
--	disc->mtiocget = tape34xx_mtiocget;
--	disc->mtiocpos = tape34xx_mtiocpos;
--	disc->shutdown = tape3480_shutdown;
--	disc->discipline_ioctl_overload = tape34xx_ioctl_overload;
--	disc->event_table = &tape3480_event_handler_table;
--	disc->default_handler = tape34xx_default_handler;
--	disc->bread = tape34xx_bread;
--	disc->free_bread = tape34xx_free_bread;
--	disc->tape = NULL;	/* pointer for backreference */
--	disc->next = NULL;
--	if (autoprobe)
--	    s390_device_register(&tape3480_devreg);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,3,"3480 regis");
--#endif /* TAPE_DEBUG */
--	return disc;
--}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3480.h drivers/s390/char/tape3480.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3480.h	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tape3480.h	2006-01-30 22:25:26.000000000 -0700
-@@ -1,23 +0,0 @@
--/***************************************************************************
-- *
-- *  drivers/s390/char/tape3480.h
-- *    tape device discipline for 3480 tapes.
-- *
-- *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-- *    Author(s): Carsten Otte <cotte at de.ibm.com>
-- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-- *
-- ****************************************************************************
-- */
+-	BUF_HWCB = page;
 -
--#ifndef _TAPE3480_H
+-	BUF_HWCB_NEXT = NULL;
 -
--#define _TAPE3480_H
+-	hwc_data.hwcb_count++;
 -
+-	prepare_write_hwcb ();
 -
--typedef struct _tape3480_disc_data_t {
--    __u8 modeset_byte;
--} tape3480_disc_data_t  __attribute__ ((packed, aligned(8)));
--tape_discipline_t * tape3480_init (int);
--#endif // _TAPE3480_H
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3490.c drivers/s390/char/tape3490.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3490.c	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tape3490.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1,156 +0,0 @@
--/***************************************************************************
-- *
-- *  drivers/s390/char/tape3490.c
-- *    tape device discipline for 3490E tapes.
-- *
-- *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-- *    Author(s): Carsten Otte <cotte at de.ibm.com>
-- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-- *
-- ****************************************************************************
-- */
+-	BUF_HWCB_TIMES_LOST = 0;
+-	BUF_HWCB_MTO_LOST = 0;
+-	BUF_HWCB_CHAR_LOST = 0;
 -
--#include "tapedefs.h"
--#include <linux/version.h>
--#include <asm/ccwcache.h>	/* CCW allocations      */
--#include <asm/s390dyn.h>
--#include <asm/debug.h>
--#include <linux/compatmac.h>
--#include "tape.h"
--#include "tape34xx.h"
--#include "tape3490.h"
+-#ifdef BUFFER_STRESS_TEST
 -
--tape_event_handler_t tape3490_event_handler_table[TS_SIZE][TE_SIZE] =
--{
--    /* {START , DONE, FAILED, ERROR, OTHER } */
--	{NULL, tape34xx_unused_done, NULL, NULL, NULL},	/* TS_UNUSED */
--	{NULL, tape34xx_idle_done, NULL, NULL, NULL},	/* TS_IDLE */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_DONE */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_FAILED */
--	{NULL, tape34xx_block_done, NULL, NULL, NULL},		/* TS_BLOCK_INIT */
--	{NULL, tape34xx_bsb_init_done, NULL, NULL, NULL},	/* TS_BSB_INIT */
--	{NULL, tape34xx_bsf_init_done, NULL, NULL, NULL},	/* TS_BSF_INIT */
--	{NULL, tape34xx_dse_init_done, NULL, NULL, NULL},	/* TS_DSE_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_EGA_INIT */
--	{NULL, tape34xx_fsb_init_done, NULL, NULL, NULL},	/* TS_FSB_INIT */
--	{NULL, tape34xx_fsf_init_done, NULL, NULL, NULL},	/* TS_FSF_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_LDI_INIT */
--	{NULL, tape34xx_lbl_init_done, NULL, NULL, NULL},	/* TS_LBL_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_MSE_INIT */
--	{NULL, tape34xx_nop_init_done, NULL, NULL, NULL},	/* TS_NOP_INIT */
--	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},		/* TS_RBA_INIT */
--	{NULL, tape34xx_rbi_init_done, NULL, NULL, NULL},	/* TS_RBI_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBU_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBL_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RDC_INIT */
--	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},	/* TS_RFO_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RSD_INIT */
--	{NULL, tape34xx_rew_init_done, NULL, NULL, NULL},	/* TS_REW_INIT */
--	{NULL, tape34xx_rew_release_init_done, NULL, NULL, NULL},	/* TS_REW_RELEASE_IMIT */
--	{NULL, tape34xx_run_init_done, NULL, NULL, NULL},	/* TS_RUN_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SEN_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SID_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SNP_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SPG_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SWI_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SMR_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SYN_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_TIO_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_UNA_INIT */
--	{NULL, tape34xx_wri_init_done, NULL, NULL, NULL},	/* TS_WRI_INIT */
--	{NULL, tape34xx_wtm_init_done, NULL, NULL, NULL},	/* TS_WTM_INIT */
--        {NULL, NULL, NULL, NULL, NULL}};     /* TS_NOT_OPER */
+-	internal_print (
+-			       DELAYED_WRITE,
+-			       "*** " HWC_RW_PRINT_HEADER
+-			    "page #%i at 0x%x for buffering allocated. ***\n",
+-			       hwc_data.hwcb_count, page);
 -
--devreg_t tape3490_devreg = {
--    ci:
--    {hc:
--     {ctype:0x3490}},
--    flag:DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS,
--    oper_func:tape_oper_handler
--};
+-#endif
 -
--void
--tape3490_setup_assist (tape_info_t * ti)
--{
--	tape3490_disc_data_t *data = NULL;
--#ifdef TAPE_DEBUG
--    debug_text_event (tape_debug_area,6,"3490 dsetu");
--    debug_text_event (tape_debug_area,6,"dev:");
--    debug_int_event (tape_debug_area,6,ti->blk_minor);
--#endif /* TAPE_DEBUG */
--	while (data == NULL)
--		data = kmalloc (sizeof (tape3490_disc_data_t), GFP_KERNEL);
--	data->modeset_byte = 0x00;
--	ti->discdata = (void *) data;
+-	return 0;
 -}
 -
+-static int 
+-release_write_hwcb (void)
+-{
+-	unsigned long page;
+-	int page_nr;
 -
--void
--tape3490_shutdown (int autoprobe) {
--    if (autoprobe)
--	s390_device_unregister(&tape3490_devreg);
--}
+-	if (!hwc_data.hwcb_count)
+-		return -ENODATA;
 -
+-	if (hwc_data.hwcb_count == 1) {
 -
--tape_discipline_t *
--tape3490_init (int autoprobe)
--{
--	tape_discipline_t *disc;
--#ifdef TAPE_DEBUG
--    debug_text_event (tape_debug_area,3,"3490 init");
--#endif /* TAPE_DEBUG */
--	disc = kmalloc (sizeof (tape_discipline_t), GFP_KERNEL);
--	if (disc == NULL) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,3,"disc:nomem");
--#endif /* TAPE_DEBUG */
--		return disc;
--	}
--	disc->cu_type = 0x3490;
--	disc->setup_assist = tape3490_setup_assist;
--	disc->error_recovery = tape34xx_error_recovery;
--	disc->write_block = tape34xx_write_block;
--	disc->free_write_block = tape34xx_free_write_block;
--	disc->read_block = tape34xx_read_block;
--	disc->free_read_block = tape34xx_free_read_block;
--	disc->mtfsf = tape34xx_mtfsf;
--	disc->mtbsf = tape34xx_mtbsf;
--	disc->mtfsr = tape34xx_mtfsr;
--	disc->mtbsr = tape34xx_mtbsr;
--	disc->mtweof = tape34xx_mtweof;
--	disc->mtrew = tape34xx_mtrew;
--	disc->mtoffl = tape34xx_mtoffl;
--	disc->mtnop = tape34xx_mtnop;
--	disc->mtbsfm = tape34xx_mtbsfm;
--	disc->mtfsfm = tape34xx_mtfsfm;
--	disc->mteom = tape34xx_mteom;
--	disc->mterase = tape34xx_mterase;
--	disc->mtsetdensity = tape34xx_mtsetdensity;
--	disc->mtseek = tape34xx_mtseek;
--	disc->mttell = tape34xx_mttell;
--	disc->mtsetdrvbuffer = tape34xx_mtsetdrvbuffer;
--	disc->mtlock = tape34xx_mtlock;
--	disc->mtunlock = tape34xx_mtunlock;
--	disc->mtload = tape34xx_mtload;
--	disc->mtunload = tape34xx_mtunload;
--	disc->mtcompression = tape34xx_mtcompression;
--	disc->mtsetpart = tape34xx_mtsetpart;
--	disc->mtmkpart = tape34xx_mtmkpart;
--	disc->mtiocget = tape34xx_mtiocget;
--	disc->mtiocpos = tape34xx_mtiocpos;
--	disc->shutdown = tape3490_shutdown;
--	disc->discipline_ioctl_overload = tape34xx_ioctl_overload;
--	disc->event_table = &tape3490_event_handler_table;
--	disc->default_handler = tape34xx_default_handler;
--	disc->bread = tape34xx_bread;
--	disc->free_bread = tape34xx_free_bread;
--	disc->tape = NULL;	/* pointer for backreference */
--	disc->next = NULL;
--	if (autoprobe)
--	    s390_device_register(&tape3490_devreg);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,3,"3490 regis");
--#endif /* TAPE_DEBUG */
--	return disc;
--}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3490.h drivers/s390/char/tape3490.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3490.h	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tape3490.h	2006-01-30 22:25:26.000000000 -0700
-@@ -1,24 +0,0 @@
+-		prepare_write_hwcb ();
 -
--/***************************************************************************
-- *
-- *  drivers/s390/char/tape3490.h
-- *    tape device discipline for 3490E tapes.
-- *
-- *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-- *    Author(s): Carsten Otte <cotte at de.ibm.com>
-- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-- *
-- ****************************************************************************
-- */
+-		ALL_HWCB_CHAR = 0;
+-		ALL_HWCB_MTO = 0;
+-		BUF_HWCB_TIMES_LOST = 0;
+-		BUF_HWCB_MTO_LOST = 0;
+-		BUF_HWCB_CHAR_LOST = 0;
+-	} else {
+-		page = (unsigned long) OUT_HWCB;
 -
--#ifndef _TAPE3490_H
+-		ALL_HWCB_MTO -= OUT_HWCB_MTO;
+-		ALL_HWCB_CHAR -= OUT_HWCB_CHAR;
+-		hwc_data.hwcb_count--;
 -
--#define _TAPE3490_H
+-		OUT_HWCB = OUT_HWCB_NEXT;
 -
+-		if (page >= hwc_data.kmem_start &&
+-		    page <= hwc_data.kmem_end) {
+-			/*memset((void *) page, 0, PAGE_SIZE); */
 -
--typedef struct _tape3490_disc_data_t {
--    __u8 modeset_byte;
--} tape3490_disc_data_t  __attribute__ ((packed, aligned(8)));
--tape_discipline_t * tape3490_init (int);
--#endif // _TAPE3490_H
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape34xx.c drivers/s390/char/tape34xx.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape34xx.c	2002-08-02 18:39:44.000000000 -0600
-+++ drivers/s390/char/tape34xx.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1,2389 +0,0 @@
--/***************************************************************************
-- *
-- *  drivers/s390/char/tape34xx.c
-- *    common tape device discipline for 34xx tapes.
-- *
-- *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-- *    Author(s): Carsten Otte <cotte at de.ibm.com>
-- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-- *
-- ****************************************************************************
-- */
+-			page_nr = (int) ((page - hwc_data.kmem_start) >> 12);
+-			clear_bit (page_nr, &hwc_data.kmem_pages);
+-		} else
+-			free_page (page);
+-#ifdef BUFFER_STRESS_TEST
 -
--#include "tapedefs.h"
--#include <linux/config.h>
--#include <linux/version.h>
--#include <linux/stddef.h>
--#include <linux/kernel.h>
--#include <asm/types.h>
--#include <asm/uaccess.h>
--#include <linux/stat.h>
--#include <linux/proc_fs.h>
--#include <asm/ccwcache.h> 
--#include <asm/idals.h>  
--#ifdef CONFIG_S390_TAPE_DYNAMIC
--#include <asm/s390dyn.h>
--#endif
--#include <asm/debug.h>
--#include <linux/compatmac.h>
--#include "tape.h"
--#include "tape34xx.h"
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       "*** " HWC_RW_PRINT_HEADER
+-			 "page at 0x%x released, %i pages still in use ***\n",
+-				       page, hwc_data.hwcb_count);
 -
--#define PRINTK_HEADER "T34xx:"
+-#endif
+-	}
+-	return 0;
+-}
 -
--tape_event_handler_t tape34xx_event_handler_table[TS_SIZE][TE_SIZE] =
+-static int 
+-add_mto (
+-		unsigned char *message,
+-		unsigned short int count)
 -{
--    /* {START , DONE, FAILED, ERROR, OTHER } */
--	{NULL, tape34xx_unused_done, NULL, NULL, NULL},	/* TS_UNUSED */
--	{NULL, tape34xx_idle_done, NULL, NULL, NULL},	/* TS_IDLE */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_DONE */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_FAILED */
--	{NULL, tape34xx_block_done, NULL, NULL, NULL},		/* TS_BLOCK_INIT */
--	{NULL, tape34xx_bsb_init_done, NULL, NULL, NULL},	/* TS_BSB_INIT */
--	{NULL, tape34xx_bsf_init_done, NULL, NULL, NULL},	/* TS_BSF_INIT */
--	{NULL, tape34xx_dse_init_done, NULL, NULL, NULL},	/* TS_DSE_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_EGA_INIT */
--	{NULL, tape34xx_fsb_init_done, NULL, NULL, NULL},	/* TS_FSB_INIT */
--	{NULL, tape34xx_fsf_init_done, NULL, NULL, NULL},	/* TS_FSF_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_LDI_INIT */
--	{NULL, tape34xx_lbl_init_done, NULL, NULL, NULL},	/* TS_LBL_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_MSE_INIT */
--	{NULL, tape34xx_nop_init_done, NULL, NULL, NULL},	/* TS_NOP_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBA_INIT */
--	{NULL, tape34xx_rbi_init_done, NULL, NULL, NULL},	/* TS_RBI_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBU_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBL_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RDC_INIT */
--	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},	/* TS_RFO_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_RSD_INIT */
--	{NULL, tape34xx_rew_init_done, NULL, NULL, NULL},	/* TS_REW_INIT */
--	{NULL, tape34xx_rew_release_init_done, NULL, NULL, NULL},	/* TS_REW_RELEASE_IMIT */
--	{NULL, tape34xx_run_init_done, NULL, NULL, NULL},	/* TS_RUN_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SEN_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SID_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SNP_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SPG_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SWI_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SMR_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_SYN_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_TIO_INIT */
--	{NULL, NULL, NULL, NULL, NULL},		/* TS_UNA_INIT */
--	{NULL, tape34xx_wri_init_done, NULL, NULL, NULL},	/* TS_WRI_INIT */
--	{NULL, tape34xx_wtm_init_done, NULL, NULL, NULL},	/* TS_WTM_INIT */
--	{NULL, NULL, NULL, NULL, NULL}};        /* TS_NOT_OPER */
+-	unsigned short int mto_size;
+-	write_hwcb_t *hwcb;
+-	mto_t *mto;
+-	void *dest;
+-
+-	if (!BUF_HWCB)
+-		return -ENOMEM;
+-
+-	if (BUF_HWCB == hwc_data.current_hwcb)
+-		return -ENOMEM;
+-
+-	mto_size = sizeof (mto_t) + count;
+-
+-	hwcb = (write_hwcb_t *) BUF_HWCB;
 -
+-	if ((MAX_HWCB_ROOM - hwcb->length) < mto_size)
+-		return -ENOMEM;
 -
--int
--tape34xx_ioctl_overload (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
--{
--	return -EINVAL;		// no additional ioctls
+-	mto = (mto_t *) (((unsigned long) hwcb) + hwcb->length);
+-
+-	memcpy (mto, &mto_template, sizeof (mto_t));
+-
+-	dest = (void *) (((unsigned long) mto) + sizeof (mto_t));
+-
+-	memcpy (dest, message, count);
+-
+-	mto->length += count;
+-
+-	hwcb->length += mto_size;
+-	hwcb->msgbuf.length += mto_size;
+-	hwcb->msgbuf.mdb.length += mto_size;
+-
+-	BUF_HWCB_MTO++;
+-	ALL_HWCB_MTO++;
+-	BUF_HWCB_CHAR += count;
+-	ALL_HWCB_CHAR += count;
 -
+-	return count;
 -}
 -
--ccw_req_t *
--tape34xx_write_block (const char *data, size_t count, tape_info_t * ti)
+-static int write_event_data_1 (void);
+-
+-static void 
+-do_poll_hwc (unsigned long data)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	void *mem;
--	cqr = tape_alloc_ccw_req (ti, 2, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xwbl nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	mem = kmalloc (count, GFP_KERNEL);
--	if (!mem) {
--		tape_free_request (cqr);
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xwbl nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	if (copy_from_user (mem, data, count)) {
--		kfree (mem);
--		tape_free_request (cqr);
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xwbl segf.");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
+-	unsigned long flags;
 -
--	ccw->cmd_code = WRITE_CMD;
--	ccw->flags = 0;
--	ccw->count = count;
--	set_normalized_cda (ccw, (unsigned long) mem);
--	if ((ccw->cda) == 0) {
--		kfree (mem);
--		tape_free_request (cqr);
--		return NULL;
--	}
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = mem;
--	ti->userbuf = (void *) data;
--	tapestate_set (ti, TS_WRI_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xwbl ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	spin_lock_irqsave (&hwc_data.lock, flags);
+-
+-	write_event_data_1 ();
+-
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
 -}
 -
 -void 
--tape34xx_free_write_block (ccw_req_t * cqr, tape_info_t * ti)
+-start_poll_hwc (void)
 -{
--	unsigned long lockflags;
--	ccw1_t *ccw;
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ccw = cqr->cpaddr;
--	ccw++;
--	clear_normalized_cda (ccw);
--	kfree (ti->kernbuf);
--	tape_free_request (cqr);
--	ti->kernbuf = ti->userbuf = NULL;
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xfwb free");
--#endif /* TAPE_DEBUG */
+-	init_timer (&hwc_data.poll_timer);
+-	hwc_data.poll_timer.function = do_poll_hwc;
+-	hwc_data.poll_timer.data = (unsigned long) NULL;
+-	hwc_data.poll_timer.expires = jiffies + 2 * HZ;
+-	add_timer (&hwc_data.poll_timer);
+-	hwc_data.flags |= HWC_PTIMER_RUNS;
 -}
 -
--ccw_req_t *
--tape34xx_read_block (const char *data, size_t count, tape_info_t * ti)
+-static int 
+-write_event_data_1 (void)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	void *mem;
--	cqr = tape_alloc_ccw_req (ti, 2, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xrbl nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	mem = kmalloc (count, GFP_KERNEL);
--	if (!mem) {
--		tape_free_request (cqr);
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xrbl nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
+-	unsigned short int condition_code;
+-	int retval;
+-	write_hwcb_t *hwcb = (write_hwcb_t *) OUT_HWCB;
 -
--	ccw->cmd_code = READ_FORWARD;
--	ccw->flags = 0;
--	ccw->count = count;
--	set_normalized_cda (ccw, (unsigned long) mem);
--	if ((ccw->cda) == 0) {
--		kfree (mem);
--		tape_free_request (cqr);
--		return NULL;
+-	if ((!hwc_data.write_prio) &&
+-	    (!hwc_data.write_nonprio) &&
+-	    hwc_data.read_statechange)
+-		return -EOPNOTSUPP;
+-
+-	if (hwc_data.current_servc)
+-		return -EBUSY;
+-
+-	retval = sane_write_hwcb ();
+-	if (retval < 0)
+-		return -EIO;
+-
+-	if (!OUT_HWCB_MTO)
+-		return -ENODATA;
+-
+-	if (!hwc_data.write_nonprio && hwc_data.write_prio)
+-		hwcb->msgbuf.type = ET_PMsgCmd;
+-	else
+-		hwcb->msgbuf.type = ET_Msg;
+-
+-	condition_code = service_call (HWC_CMDW_WRITEDATA, OUT_HWCB);
+-
+-#ifdef DUMP_HWC_WRITE_ERROR
+-	if (condition_code != HWC_COMMAND_INITIATED)
+-		__asm__ ("LHI 1,0xe20\n\t"
+-			 "L 2,0(%0)\n\t"
+-			 "LRA 3,0(%1)\n\t"
+-			 "J .+0 \n\t"
+-	      :
+-	      :	 "a" (&condition_code), "a" (OUT_HWCB)
+-	      :	 "1", "2", "3");
+-#endif
+-
+-	switch (condition_code) {
+-	case HWC_COMMAND_INITIATED:
+-		hwc_data.current_servc = HWC_CMDW_WRITEDATA;
+-		hwc_data.current_hwcb = OUT_HWCB;
+-		retval = condition_code;
+-		break;
+-	case HWC_BUSY:
+-		retval = -EBUSY;
+-		break;
+-	case HWC_NOT_OPERATIONAL:
+-		start_poll_hwc ();
+-	default:
+-		retval = -EIO;
 -	}
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = mem;
--	ti->userbuf = (void *) data;
--	tapestate_set (ti, TS_RFO_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xrbl ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-
+-	return retval;
 -}
 -
--ccw_req_t *
--tape34xx_read_opposite (tape_info_t * ti,int novalue)
+-static void 
+-flush_hwcbs (void)
 -{
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	size_t count;
--	// first, retrieve the count from the old cqr.
--	cqr = ti->cqr;
--	ccw = cqr->cpaddr;
--	ccw++;
--	count=ccw->count;
--	// free old cqr.
--	clear_normalized_cda (ccw);
--	tape_free_request (cqr);
--	// build new cqr
--	cqr = tape_alloc_ccw_req (ti, 3, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xrop nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
+-	while (hwc_data.hwcb_count > 1)
+-		release_write_hwcb ();
 -
--	ccw->cmd_code = READ_BACKWARD;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = count;
--	set_normalized_cda (ccw, (unsigned long) ti->kernbuf);
--	if ((ccw->cda) == 0) {
--		tape_free_request (cqr);
--		return NULL;
--	}
--	ccw++;
--	ccw->cmd_code = FORSPACEBLOCK;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	ccw->cda = (unsigned long)ccw;
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 1;
--	ccw->cda = (unsigned long)ccw;
--	tapestate_set (ti, TS_RBA_INIT);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xrop ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	release_write_hwcb ();
+-
+-	hwc_data.flags &= ~HWC_FLUSH;
 -}
 -
--void 
--tape34xx_free_read_block (ccw_req_t * cqr, tape_info_t * ti)
+-static int 
+-write_event_data_2 (u32 ext_int_param)
 -{
--	unsigned long lockflags;
--	size_t cpysize;
--	ccw1_t *ccw;
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ccw = cqr->cpaddr;
--	ccw++;
--	cpysize = ccw->count - ti->devstat.rescnt;
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	if (copy_to_user (ti->userbuf, ti->kernbuf, cpysize)) {
--#ifdef TAPE_DEBUG
--	    debug_text_exception (tape_debug_area,6,"xfrb segf.");
--#endif /* TAPE_DEBUG */
+-	write_hwcb_t *hwcb;
+-	int retval = 0;
+-
+-#ifdef DUMP_HWC_WRITE_ERROR
+-	if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR)
+-	    != (unsigned long) hwc_data.current_hwcb) {
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "write_event_data_2 : "
+-				       "HWCB address does not fit "
+-				       "(expected: 0x%lx, got: 0x%lx).\n",
+-				       (unsigned long) hwc_data.current_hwcb,
+-				       ext_int_param);
+-		return -EINVAL;
 -	}
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	clear_normalized_cda (ccw);
--	kfree (ti->kernbuf);
--	tape_free_request (cqr);
--	ti->kernbuf = ti->userbuf = NULL;
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xfrb free");
--#endif /* TAPE_DEBUG */
--}
+-#endif
 -
--/*
-- * The IOCTL interface is implemented in the following section,
-- * excepted the MTRESET, MTSETBLK which are handled by tapechar.c
-- */
--/*
-- * MTFSF: Forward space over 'count' file marks. The tape is positioned
-- * at the EOT (End of Tape) side of the file mark.
-- */
--ccw_req_t *
--tape34xx_mtfsf (tape_info_t * ti, int count)
--{
--	long lockflags;
--	int i;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	if ((count == 0) || (count > 510)) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xfsf parm");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	hwcb = (write_hwcb_t *) OUT_HWCB;
+-
+-#ifdef DUMP_HWC_WRITE_LIST_ERROR
+-	if (((unsigned char *) hwcb) != hwc_data.current_hwcb) {
+-		__asm__ ("LHI 1,0xe22\n\t"
+-			 "LRA 2,0(%0)\n\t"
+-			 "LRA 3,0(%1)\n\t"
+-			 "LRA 4,0(%2)\n\t"
+-			 "LRA 5,0(%3)\n\t"
+-			 "J .+0 \n\t"
+-	      :
+-	      :	 "a" (OUT_HWCB),
+-			 "a" (hwc_data.current_hwcb),
+-			 "a" (BUF_HWCB),
+-			 "a" (hwcb)
+-	      :	 "1", "2", "3", "4", "5");
 -	}
--	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xfsf nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-#endif
+-
+-#ifdef DUMP_HWC_WRITE_ERROR
+-	if (hwcb->response_code != 0x0020) {
+-		__asm__ ("LHI 1,0xe21\n\t"
+-			 "LRA 2,0(%0)\n\t"
+-			 "LRA 3,0(%1)\n\t"
+-			 "LRA 4,0(%2)\n\t"
+-			 "LH 5,0(%3)\n\t"
+-			 "SRL 5,8\n\t"
+-			 "J .+0 \n\t"
+-	      :
+-	      :	 "a" (OUT_HWCB), "a" (hwc_data.current_hwcb),
+-			 "a" (BUF_HWCB),
+-			 "a" (&(hwc_data.hwcb_count))
+-	      :	 "1", "2", "3", "4", "5");
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	for (i = 0; i < count; i++) {
--		ccw->cmd_code = FORSPACEFILE;
--		ccw->flags = CCW_FLAG_CC;
--		ccw->count = 0;
--		ccw->cda = (unsigned long) (&(ccw->cmd_code));
--		ccw++;
+-#endif
+-
+-	switch (hwcb->response_code) {
+-	case 0x0020:
+-
+-		retval = OUT_HWCB_CHAR;
+-		release_write_hwcb ();
+-		break;
+-	case 0x0040:
+-	case 0x0340:
+-	case 0x40F0:
+-		if (!hwc_data.read_statechange) {
+-			hwcb->response_code = 0;
+-			start_poll_hwc ();
+-		}
+-		retval = -EIO;
+-		break;
+-	default:
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "write_event_data_2 : "
+-				       "failed operation "
+-				       "(response code: 0x%x "
+-				       "HWCB address: 0x%x).\n",
+-				       hwcb->response_code,
+-				       hwcb);
+-		retval = -EIO;
 -	}
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_FSF_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xfsf ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-
+-	if (retval == -EIO) {
+-
+-		hwcb->control_mask[0] = 0;
+-		hwcb->control_mask[1] = 0;
+-		hwcb->control_mask[2] = 0;
+-		hwcb->response_code = 0;
+-	}
+-	hwc_data.current_servc = 0;
+-	hwc_data.current_hwcb = NULL;
+-
+-	if (hwc_data.flags & HWC_FLUSH)
+-		flush_hwcbs ();
+-
+-	return retval;
 -}
 -
--/*
-- * MTBSF: Backward space over 'count' file marks. The tape is positioned at
-- * the EOT (End of Tape) side of the last skipped file mark.
-- */
--ccw_req_t *
--tape34xx_mtbsf (tape_info_t * ti, int count)
+-static void 
+-do_put_line (
+-		    unsigned char *message,
+-		    unsigned short count)
 -{
--	long lockflags;
--	int i;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	if ((count == 0) || (count > 510)) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xbsf parm");
--#endif /* TAPE_DEBUG */
--	        return NULL;
--	}
--	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xbsf nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	for (i = 0; i < count; i++) {
--		ccw->cmd_code = BACKSPACEFILE;
--		ccw->flags = CCW_FLAG_CC;
--		ccw->count = 0;
--		ccw->cda = (unsigned long) (&(ccw->cmd_code));
--		ccw++;
+-
+-	if (add_mto (message, count) != count) {
+-
+-		if (allocate_write_hwcb () < 0)
+-			reuse_write_hwcb ();
+-
+-#ifdef DUMP_HWC_WRITE_LIST_ERROR
+-		if (add_mto (message, count) != count)
+-			__asm__ ("LHI 1,0xe32\n\t"
+-				 "LRA 2,0(%0)\n\t"
+-				 "L 3,0(%1)\n\t"
+-				 "LRA 4,0(%2)\n\t"
+-				 "LRA 5,0(%3)\n\t"
+-				 "J .+0 \n\t"
+-		      :
+-		      :	 "a" (message), "a" (&hwc_data.kmem_pages),
+-				 "a" (BUF_HWCB), "a" (OUT_HWCB)
+-		      :	 "1", "2", "3", "4", "5");
+-#else
+-		add_mto (message, count);
+-#endif
 -	}
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_BSF_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xbsf ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
 -}
 -
--/*
-- * MTFSR: Forward space over 'count' tape blocks (blocksize is set
-- * via MTSETBLK.
-- */
--ccw_req_t *
--tape34xx_mtfsr (tape_info_t * ti, int count)
+-static void 
+-put_line (
+-		 unsigned char *message,
+-		 unsigned short count)
+-{
+-
+-	if ((!hwc_data.obuf_start) && (hwc_data.flags & HWC_WTIMER_RUNS)) {
+-		del_timer (&hwc_data.write_timer);
+-		hwc_data.flags &= ~HWC_WTIMER_RUNS;
+-	}
+-	hwc_data.obuf_start += count;
+-
+-	do_put_line (message, count);
+-
+-	hwc_data.obuf_start -= count;
+-}
+-
+-static void 
+-set_alarm (void)
 -{
--	long lockflags;
--	int i;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	if ((count == 0) || (count > 510)) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xfsr parm");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xfsr nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	for (i = 0; i < count; i++) {
--		ccw->cmd_code = FORSPACEBLOCK;
--		ccw->flags = CCW_FLAG_CC;
--		ccw->count = 0;
--		ccw->cda = (unsigned long) (&(ccw->cmd_code));
--		ccw++;
--	}
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_FSB_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xfsr ccwgen");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	write_hwcb_t *hwcb;
+-
+-	if ((!BUF_HWCB) || (BUF_HWCB == hwc_data.current_hwcb))
+-		allocate_write_hwcb ();
+-
+-	hwcb = (write_hwcb_t *) BUF_HWCB;
+-	hwcb->msgbuf.mdb.mdb_body.go.general_msg_flags |= GMF_SndAlrm;
 -}
 -
--/*
-- * MTBSR: Backward space over 'count' tape blocks.
-- * (blocksize is set via MTSETBLK.
-- */
--ccw_req_t *
--tape34xx_mtbsr (tape_info_t * ti, int count)
+-static void 
+-hwc_write_timeout (unsigned long data)
 -{
--	long lockflags;
--	int i;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	if ((count == 0) || (count > 510)) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xbsr parm");
--#endif /* TAPE_DEBUG */   
--	        return NULL;
--	}
--	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xbsr nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	for (i = 0; i < count; i++) {
--		ccw->cmd_code = BACKSPACEBLOCK;
--		ccw->flags = CCW_FLAG_CC;
--		ccw->count = 0;
--		ccw->cda = (unsigned long) (&(ccw->cmd_code));
--		ccw++;
--	}
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_BSB_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xbsr ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	unsigned long flags;
+-
+-	spin_lock_irqsave (&hwc_data.lock, flags);
+-
+-	hwc_data.obuf_start = hwc_data.obuf_count;
+-	if (hwc_data.obuf_count)
+-		put_line (hwc_data.obuf, hwc_data.obuf_count);
+-	hwc_data.obuf_start = 0;
+-
+-	hwc_data.obuf_cursor = 0;
+-	hwc_data.obuf_count = 0;
+-
+-	write_event_data_1 ();
+-
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
 -}
 -
--/*
-- * MTWEOF: Write 'count' file marks at the current position.
-- */
--ccw_req_t *
--tape34xx_mtweof (tape_info_t * ti, int count)
+-static int 
+-do_hwc_write (
+-		     int from_user,
+-		     unsigned char *msg,
+-		     unsigned int count,
+-		     unsigned char write_time)
 -{
--	long lockflags;
--	int i;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	if ((count == 0) || (count > 510)) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xweo parm");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	unsigned int i_msg = 0;
+-	unsigned short int spaces = 0;
+-	unsigned int processed_characters = 0;
+-	unsigned char ch;
+-	unsigned short int obuf_count;
+-	unsigned short int obuf_cursor;
+-	unsigned short int obuf_columns;
+-
+-	if (hwc_data.obuf_start) {
+-		obuf_cursor = 0;
+-		obuf_count = 0;
+-		obuf_columns = MIN (hwc_data.ioctls.columns,
+-				    MAX_MESSAGE_SIZE - hwc_data.obuf_start);
+-	} else {
+-		obuf_cursor = hwc_data.obuf_cursor;
+-		obuf_count = hwc_data.obuf_count;
+-		obuf_columns = hwc_data.ioctls.columns;
 -	}
--	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xweo nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-
+-	for (i_msg = 0; i_msg < count; i_msg++) {
+-		if (from_user)
+-			get_user (ch, msg + i_msg);
+-		else
+-			ch = msg[i_msg];
+-
+-		processed_characters++;
+-
+-		if ((obuf_cursor == obuf_columns) &&
+-
+-		    (ch != '\n') &&
+-
+-		    (ch != '\t')) {
+-			put_line (&hwc_data.obuf[hwc_data.obuf_start],
+-				  obuf_columns);
+-			obuf_cursor = 0;
+-			obuf_count = 0;
+-		}
+-		switch (ch) {
+-
+-		case '\n':
+-
+-			put_line (&hwc_data.obuf[hwc_data.obuf_start],
+-				  obuf_count);
+-			obuf_cursor = 0;
+-			obuf_count = 0;
+-			break;
+-
+-		case '\a':
+-
+-			hwc_data.obuf_start += obuf_count;
+-			set_alarm ();
+-			hwc_data.obuf_start -= obuf_count;
+-
+-			break;
+-
+-		case '\t':
+-
+-			do {
+-				if (obuf_cursor < obuf_columns) {
+-					hwc_data.obuf[hwc_data.obuf_start +
+-						      obuf_cursor]
+-					    = HWC_ASCEBC (' ');
+-					obuf_cursor++;
+-				} else
+-					break;
+-			} while (obuf_cursor % hwc_data.ioctls.width_htab);
+-
+-			break;
+-
+-		case '\f':
+-		case '\v':
+-
+-			spaces = obuf_cursor;
+-			put_line (&hwc_data.obuf[hwc_data.obuf_start],
+-				  obuf_count);
+-			obuf_count = obuf_cursor;
+-			while (spaces) {
+-				hwc_data.obuf[hwc_data.obuf_start +
+-					      obuf_cursor - spaces]
+-				    = HWC_ASCEBC (' ');
+-				spaces--;
+-			}
+-
+-			break;
+-
+-		case '\b':
+-
+-			if (obuf_cursor)
+-				obuf_cursor--;
+-			break;
+-
+-		case '\r':
+-
+-			obuf_cursor = 0;
+-			break;
+-
+-		case 0x00:
+-
+-			put_line (&hwc_data.obuf[hwc_data.obuf_start],
+-				  obuf_count);
+-			obuf_cursor = 0;
+-			obuf_count = 0;
+-			goto out;
+-
+-		default:
+-
+-			if (isprint (ch))
+-				hwc_data.obuf[hwc_data.obuf_start +
+-					      obuf_cursor++]
+-				    = HWC_ASCEBC (ch);
+-		}
+-		if (obuf_cursor > obuf_count)
+-			obuf_count = obuf_cursor;
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	for (i = 0; i < count; i++) {
--		ccw->cmd_code = WRITETAPEMARK;
--		ccw->flags = CCW_FLAG_CC;
--		ccw->count = 1;
--		ccw->cda = (unsigned long) (&(ccw->cmd_code));
--		ccw++;
+-
+-	if (obuf_cursor) {
+-
+-		if (hwc_data.obuf_start ||
+-		    (hwc_data.ioctls.final_nl == 0)) {
+-
+-			put_line (&hwc_data.obuf[hwc_data.obuf_start],
+-				  obuf_count);
+-			obuf_cursor = 0;
+-			obuf_count = 0;
+-		} else {
+-
+-			if (hwc_data.ioctls.final_nl > 0) {
+-
+-				if (hwc_data.flags & HWC_WTIMER_RUNS) {
+-
+-					mod_timer (&hwc_data.write_timer,
+-						   jiffies + hwc_data.ioctls.final_nl * HZ / 10);
+-				} else {
+-
+-					init_timer (&hwc_data.write_timer);
+-					hwc_data.write_timer.function =
+-					    hwc_write_timeout;
+-					hwc_data.write_timer.data =
+-					    (unsigned long) NULL;
+-					hwc_data.write_timer.expires =
+-					    jiffies +
+-					    hwc_data.ioctls.final_nl * HZ / 10;
+-					add_timer (&hwc_data.write_timer);
+-					hwc_data.flags |= HWC_WTIMER_RUNS;
+-				}
+-			} else;
+-
+-		}
+-	} else;
+-
+-      out:
+-
+-	if (!hwc_data.obuf_start) {
+-		hwc_data.obuf_cursor = obuf_cursor;
+-		hwc_data.obuf_count = obuf_count;
 -	}
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	ccw++;
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_WTM_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xweo ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	if (write_time == IMMEDIATE_WRITE)
+-		write_event_data_1 ();
+-
+-	return processed_characters;
 -}
 -
--/*
-- * MTREW: Rewind the tape.
-- */
--ccw_req_t *
--tape34xx_mtrew (tape_info_t * ti, int count)
+-signed int 
+-hwc_write (int from_user, const unsigned char *msg, unsigned int count)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 3, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xrew nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = REWIND;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_REW_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xrew ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	unsigned long flags;
+-	int retval;
+-
+-	spin_lock_irqsave (&hwc_data.lock, flags);
+-
+-	retval = do_hwc_write (from_user, (unsigned char *) msg,
+-			       count, IMMEDIATE_WRITE);
+-
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-
+-	return retval;
 -}
 -
--/*
-- * MTOFFL: Rewind the tape and put the drive off-line.
-- * Implement 'rewind unload'
-- */
--ccw_req_t *
--tape34xx_mtoffl (tape_info_t * ti, int count)
+-unsigned int 
+-hwc_chars_in_buffer (unsigned char flag)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 3, 32);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xoff nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	unsigned short int number = 0;
+-	unsigned long flags;
+-
+-	spin_lock_irqsave (&hwc_data.lock, flags);
+-
+-	if (flag & IN_HWCB)
+-		number += ALL_HWCB_CHAR;
+-
+-	if (flag & IN_WRITE_BUF)
+-		number += hwc_data.obuf_cursor;
+-
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-
+-	return number;
+-}
+-
+-static inline int 
+-nr_setbits (kmem_pages_t arg)
+-{
+-	int i;
+-	int nr = 0;
+-
+-	for (i = 0; i < (sizeof (arg) << 3); i++) {
+-		if (arg & 1)
+-			nr++;
+-		arg >>= 1;
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = REWIND_UNLOAD;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	ccw++;
--	ccw->cmd_code = SENSE;
--	ccw->flags = 0;
--	ccw->count = 32;
--	ccw->cda = (unsigned long) cqr->cpaddr;
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_RUN_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xoff ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-
+-	return nr;
 -}
 -
--/*
-- * MTNOP: 'No operation'.
-- */
--ccw_req_t *
--tape34xx_mtnop (tape_info_t * ti, int count)
+-unsigned int 
+-hwc_write_room (unsigned char flag)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 1, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xnop nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	unsigned int number = 0;
+-	unsigned long flags;
+-	write_hwcb_t *hwcb;
+-
+-	spin_lock_irqsave (&hwc_data.lock, flags);
+-
+-	if (flag & IN_HWCB) {
+-
+-		if (BUF_HWCB) {
+-			hwcb = (write_hwcb_t *) BUF_HWCB;
+-			number += MAX_HWCB_ROOM - hwcb->length;
+-		}
+-		number += (hwc_data.ioctls.kmem_hwcb -
+-			   nr_setbits (hwc_data.kmem_pages)) *
+-		    (MAX_HWCB_ROOM -
+-		     (sizeof (write_hwcb_t) + sizeof (mto_t)));
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) ccw->cmd_code;
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_NOP_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xnop ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	if (flag & IN_WRITE_BUF)
+-		number += MAX_HWCB_ROOM - hwc_data.obuf_cursor;
+-
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-
+-	return number;
 -}
 -
--/*
-- * MTBSFM: Backward space over 'count' file marks.
-- * The tape is positioned at the BOT (Begin Of Tape) side of the
-- * last skipped file mark.
-- */
--ccw_req_t *
--tape34xx_mtbsfm (tape_info_t * ti, int count)
+-void 
+-hwc_flush_buffer (unsigned char flag)
 -{
--	long lockflags;
--	int i;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	if ((count == 0) || (count > 510)) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xbsm parm");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xbsm nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	unsigned long flags;
+-
+-	spin_lock_irqsave (&hwc_data.lock, flags);
+-
+-	if (flag & IN_HWCB) {
+-		if (hwc_data.current_servc != HWC_CMDW_WRITEDATA)
+-			flush_hwcbs ();
+-		else
+-			hwc_data.flags |= HWC_FLUSH;
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	for (i = 0; i < count; i++) {
--		ccw->cmd_code = BACKSPACEFILE;
--		ccw->flags = CCW_FLAG_CC;
--		ccw->count = 0;
--		ccw->cda = (unsigned long) (&(ccw->cmd_code));
--		ccw++;
+-	if (flag & IN_WRITE_BUF) {
+-		hwc_data.obuf_cursor = 0;
+-		hwc_data.obuf_count = 0;
 -	}
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_BSF_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xbsm ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
 -}
 -
--/*
-- * MTFSFM: Forward space over 'count' file marks.
-- * The tape is positioned at the BOT (Begin Of Tape) side
-- * of the last skipped file mark.
-- */
--ccw_req_t *
--tape34xx_mtfsfm (tape_info_t * ti, int count)
+-unsigned short int 
+-seperate_cases (unsigned char *buf, unsigned short int count)
 -{
--	long lockflags;
--	int i;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	if ((count == 0) || (count > 510)) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xfsm parm");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xfsm nomem");
--#endif /* TAPE_DEBUG */	    
--		return NULL;
--	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	for (i = 0; i < count; i++) {
--		ccw->cmd_code = FORSPACEFILE;
--		ccw->flags = CCW_FLAG_CC;
--		ccw->count = 0;
--		ccw->cda = (unsigned long) (&(ccw->cmd_code));
--		ccw++;
+-
+-	unsigned short int i_in;
+-
+-	unsigned short int i_out = 0;
+-
+-	unsigned char _case = 0;
+-
+-	for (i_in = 0; i_in < count; i_in++) {
+-
+-		if (buf[i_in] == hwc_data.ioctls.delim) {
+-
+-			if ((i_in + 1 < count) &&
+-			    (buf[i_in + 1] == hwc_data.ioctls.delim)) {
+-
+-				buf[i_out] = hwc_data.ioctls.delim;
+-
+-				i_out++;
+-
+-				i_in++;
+-
+-			} else
+-				_case = ~_case;
+-
+-		} else {
+-
+-			if (_case) {
+-
+-				if (hwc_data.ioctls.tolower)
+-					buf[i_out] = _ebc_toupper[buf[i_in]];
+-
+-				else
+-					buf[i_out] = _ebc_tolower[buf[i_in]];
+-
+-			} else
+-				buf[i_out] = buf[i_in];
+-
+-			i_out++;
+-		}
 -	}
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_FSF_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xfsm ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-
+-	return i_out;
 -}
 -
--/*
-- * MTEOM: positions at the end of the portion of the tape already used
-- * for recordind data. MTEOM positions after the last file mark, ready for
-- * appending another file.
-- * MTRETEN: Retension the tape, i.e. forward space to end of tape and rewind.
-- */
--ccw_req_t *
--tape34xx_mteom (tape_info_t * ti, int count)
+-#ifdef DUMP_HWCB_INPUT
+-
+-static int 
+-gds_vector_name (u16 id, unsigned char name[])
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 4, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xeom nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	int retval = 0;
+-
+-	switch (id) {
+-	case GDS_ID_MDSMU:
+-		name = "Multiple Domain Support Message Unit";
+-		break;
+-	case GDS_ID_MDSRouteInfo:
+-		name = "MDS Routing Information";
+-		break;
+-	case GDS_ID_AgUnWrkCorr:
+-		name = "Agent Unit of Work Correlator";
+-		break;
+-	case GDS_ID_SNACondReport:
+-		name = "SNA Condition Report";
+-		break;
+-	case GDS_ID_CPMSU:
+-		name = "CP Management Services Unit";
+-		break;
+-	case GDS_ID_RoutTargInstr:
+-		name = "Routing and Targeting Instructions";
+-		break;
+-	case GDS_ID_OpReq:
+-		name = "Operate Request";
+-		break;
+-	case GDS_ID_TextCmd:
+-		name = "Text Command";
+-		break;
+-
+-	default:
+-		name = "unknown GDS variable";
+-		retval = -EINVAL;
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = FORSPACEFILE;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	ccw++;
--	ccw->cmd_code = CCW_CMD_TIC;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (cqr->cpaddr);
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_FSF_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xeom ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-
+-	return retval;
 -}
+-#endif
 -
--/*
-- * MTERASE: erases the tape.
-- */
--ccw_req_t *
--tape34xx_mterase (tape_info_t * ti, int count)
+-inline static gds_vector_t *
+-find_gds_vector (
+-			gds_vector_t * start, void *end, u16 id)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 5, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xera nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	gds_vector_t *vec;
+-	gds_vector_t *retval = NULL;
+-
+-	vec = start;
+-
+-	while (((void *) vec) < end) {
+-		if (vec->gds_id == id) {
+-
+-#ifdef DUMP_HWCB_INPUT
+-			int retval_name;
+-			unsigned char name[64];
+-
+-			retval_name = gds_vector_name (id, name);
+-			internal_print (
+-					       DELAYED_WRITE,
+-					       HWC_RW_PRINT_HEADER
+-					  "%s at 0x%x up to 0x%x, length: %d",
+-					       name,
+-					       (unsigned long) vec,
+-				      ((unsigned long) vec) + vec->length - 1,
+-					       vec->length);
+-			if (retval_name < 0)
+-				internal_print (
+-						       IMMEDIATE_WRITE,
+-						       ", id: 0x%x\n",
+-						       vec->gds_id);
+-			else
+-				internal_print (
+-						       IMMEDIATE_WRITE,
+-						       "\n");
+-#endif
+-
+-			retval = vec;
+-			break;
+-		}
+-		vec = (gds_vector_t *) (((unsigned long) vec) + vec->length);
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = REWIND;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	ccw++;
--	ccw->cmd_code = ERASE_GAP;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	ccw++;
--	ccw->cmd_code = DATA_SEC_ERASE;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_DSE_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xera ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-
+-	return retval;
 -}
 -
--/*
-- * MTSETDENSITY: set tape density.
-- */
--ccw_req_t *
--tape34xx_mtsetdensity (tape_info_t * ti, int count)
+-inline static gds_subvector_t *
+-find_gds_subvector (
+-			   gds_subvector_t * start, void *end, u8 key)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 2, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xden nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	gds_subvector_t *subvec;
+-	gds_subvector_t *retval = NULL;
+-
+-	subvec = start;
+-
+-	while (((void *) subvec) < end) {
+-		if (subvec->key == key) {
+-			retval = subvec;
+-			break;
+-		}
+-		subvec = (gds_subvector_t *)
+-		    (((unsigned long) subvec) + subvec->length);
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_NOP_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xden ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-
+-	return retval;
 -}
 -
--/*
-- * MTSEEK: seek to the specified block.
-- */
--ccw_req_t *
--tape34xx_mtseek (tape_info_t * ti, int count)
+-inline static int 
+-get_input (void *start, void *end)
 -{
--	long lockflags;
--	__u8 *data;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	if ((data = kmalloc (4 * sizeof (__u8), GFP_KERNEL)) == NULL) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xsee nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	data[0] = 0x01;
--	data[1] = data[2] = data[3] = 0x00;
--	if (count >= 4194304) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xsee parm");
--#endif /* TAPE_DEBUG */
--		kfree(data);
--		return NULL;
--	}
--	if (((tape34xx_disc_data_t *) ti->discdata)->modeset_byte & 0x08)	// IDRC on
+-	int count;
 -
--		data[1] = data[1] | 0x80;
--	data[3] += count % 256;
--	data[2] += (count / 256) % 256;
--	data[1] += (count / 65536);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xsee id:");
--	debug_int_event (tape_debug_area,6,count);
--#endif /* TAPE_DEBUG */
--	cqr = tape_alloc_ccw_req (ti, 3, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xsee nomem");
--#endif /* TAPE_DEBUG */
--		kfree (data);
--		return NULL;
+-	count = ((unsigned long) end) - ((unsigned long) start);
+-
+-	if (hwc_data.ioctls.tolower)
+-		EBC_TOLOWER (start, count);
+-
+-	if (hwc_data.ioctls.delim)
+-		count = seperate_cases (start, count);
+-
+-	HWC_EBCASC_STR (start, count);
+-
+-	if (hwc_data.ioctls.echo)
+-		do_hwc_write (0, start, count, IMMEDIATE_WRITE);
+-
+-	if (hwc_data.calls != NULL)
+-		if (hwc_data.calls->move_input != NULL)
+-			(hwc_data.calls->move_input) (start, count);
+-
+-	return count;
+-}
+-
+-inline static int 
+-eval_selfdeftextmsg (gds_subvector_t * start, void *end)
+-{
+-	gds_subvector_t *subvec;
+-	void *subvec_data;
+-	void *subvec_end;
+-	int retval = 0;
+-
+-	subvec = start;
+-
+-	while (((void *) subvec) < end) {
+-		subvec = find_gds_subvector (subvec, end, 0x30);
+-		if (!subvec)
+-			break;
+-		subvec_data = (void *)
+-		    (((unsigned long) subvec) +
+-		     sizeof (gds_subvector_t));
+-		subvec_end = (void *)
+-		    (((unsigned long) subvec) + subvec->length);
+-		retval += get_input (subvec_data, subvec_end);
+-		subvec = (gds_subvector_t *) subvec_end;
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = LOCATE;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 4;
--	set_normalized_cda (ccw, (unsigned long) data);
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = data;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_LBL_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xsee ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-
+-	return retval;
+-}
+-
+-inline static int 
+-eval_textcmd (gds_subvector_t * start, void *end)
+-{
+-	gds_subvector_t *subvec;
+-	gds_subvector_t *subvec_data;
+-	void *subvec_end;
+-	int retval = 0;
+-
+-	subvec = start;
+-
+-	while (((void *) subvec) < end) {
+-		subvec = find_gds_subvector (
+-					 subvec, end, GDS_KEY_SelfDefTextMsg);
+-		if (!subvec)
+-			break;
+-		subvec_data = (gds_subvector_t *)
+-		    (((unsigned long) subvec) +
+-		     sizeof (gds_subvector_t));
+-		subvec_end = (void *)
+-		    (((unsigned long) subvec) + subvec->length);
+-		retval += eval_selfdeftextmsg (subvec_data, subvec_end);
+-		subvec = (gds_subvector_t *) subvec_end;
+-	}
+-
+-	return retval;
 -}
 -
--/*
-- * MTTELL: Tell block. Return the number of block relative to current file.
-- */
--ccw_req_t *
--tape34xx_mttell (tape_info_t * ti, int count)
+-inline static int 
+-eval_cpmsu (gds_vector_t * start, void *end)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	void *mem;
--	cqr = tape_alloc_ccw_req (ti, 2, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xtel nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	mem = kmalloc (8, GFP_KERNEL);
--	if (!mem) {
--		tape_free_request (cqr);
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xtel nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	gds_vector_t *vec;
+-	gds_subvector_t *vec_data;
+-	void *vec_end;
+-	int retval = 0;
+-
+-	vec = start;
+-
+-	while (((void *) vec) < end) {
+-		vec = find_gds_vector (vec, end, GDS_ID_TextCmd);
+-		if (!vec)
+-			break;
+-		vec_data = (gds_subvector_t *)
+-		    (((unsigned long) vec) + sizeof (gds_vector_t));
+-		vec_end = (void *) (((unsigned long) vec) + vec->length);
+-		retval += eval_textcmd (vec_data, vec_end);
+-		vec = (gds_vector_t *) vec_end;
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
 -
--	ccw->cmd_code = READ_BLOCK_ID;
--	ccw->flags = 0;
--	ccw->count = 8;
--	set_normalized_cda (ccw, (unsigned long) mem);
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = mem;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_RBI_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xtel ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	return retval;
 -}
 -
--/*
-- * MTSETDRVBUFFER: Set the tape drive buffer code to number.
-- * Implement NOP.
-- */
--ccw_req_t *
--tape34xx_mtsetdrvbuffer (tape_info_t * ti, int count)
+-inline static int 
+-eval_mdsmu (gds_vector_t * start, void *end)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 2, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xbuf nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	gds_vector_t *vec;
+-	gds_vector_t *vec_data;
+-	void *vec_end;
+-	int retval = 0;
+-
+-	vec = find_gds_vector (start, end, GDS_ID_CPMSU);
+-	if (vec) {
+-		vec_data = (gds_vector_t *)
+-		    (((unsigned long) vec) + sizeof (gds_vector_t));
+-		vec_end = (void *) (((unsigned long) vec) + vec->length);
+-		retval = eval_cpmsu (vec_data, vec_end);
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_NOP_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xbuf ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	return retval;
 -}
 -
--/*
-- * MTLOCK: Locks the tape drive door.
-- * Implement NOP CCW command.
-- */
--ccw_req_t *
--tape34xx_mtlock (tape_info_t * ti, int count)
+-static int 
+-eval_evbuf (gds_vector_t * start, void *end)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 2, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xloc nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	gds_vector_t *vec;
+-	gds_vector_t *vec_data;
+-	void *vec_end;
+-	int retval = 0;
+-
+-	vec = find_gds_vector (start, end, GDS_ID_MDSMU);
+-	if (vec) {
+-		vec_data = (gds_vector_t *)
+-		    (((unsigned long) vec) + sizeof (gds_vector_t));
+-		vec_end = (void *) (((unsigned long) vec) + vec->length);
+-		retval = eval_mdsmu (vec_data, vec_end);
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_NOP_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xloc ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	return retval;
 -}
 -
--/*
-- * MTUNLOCK: Unlocks the tape drive door.
-- * Implement the NOP CCW command.
-- */
--ccw_req_t *
--tape34xx_mtunlock (tape_info_t * ti, int count)
+-static inline int 
+-eval_hwc_receive_mask (_hwcb_mask_t mask)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 2, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xulk nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-
+-	hwc_data.write_nonprio
+-	    = ((mask & ET_Msg_Mask) == ET_Msg_Mask);
+-
+-	hwc_data.write_prio
+-	    = ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask);
+-
+-	if (hwc_data.write_prio || hwc_data.write_nonprio) {
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "can write messages\n");
+-		return 0;
+-	} else {
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "can not write messages\n");
+-		return -1;
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_NOP_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xulk ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
 -}
 -
--/*
-- * MTLOAD: Loads the tape.
-- * This function is not implemented and returns NULL, which causes the Frontend to wait for a medium being loaded.
-- *  The 3480/3490 type Tapes do not support a load command
-- */
--ccw_req_t *
--tape34xx_mtload (tape_info_t * ti, int count)
+-static inline int 
+-eval_hwc_send_mask (_hwcb_mask_t mask)
 -{
--         return NULL;
+-
+-	hwc_data.read_statechange
+-	    = ((mask & ET_StateChange_Mask) == ET_StateChange_Mask);
+-	if (hwc_data.read_statechange)
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				     "can read state change notifications\n");
+-	else
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				 "can not read state change notifications\n");
+-
+-	hwc_data.sig_quiesce
+-	    = ((mask & ET_SigQuiesce_Mask) == ET_SigQuiesce_Mask);
+-	if (hwc_data.sig_quiesce)
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "can receive signal quiesce\n");
+-	else
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "can not receive signal quiesce\n");
+-
+-	hwc_data.read_nonprio
+-	    = ((mask & ET_OpCmd_Mask) == ET_OpCmd_Mask);
+-	if (hwc_data.read_nonprio)
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "can read commands\n");
+-
+-	hwc_data.read_prio
+-	    = ((mask & ET_PMsgCmd_Mask) == ET_PMsgCmd_Mask);
+-	if (hwc_data.read_prio)
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "can read priority commands\n");
+-
+-	if (hwc_data.read_prio || hwc_data.read_nonprio) {
+-		return 0;
+-	} else {
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				     "can not read commands from operator\n");
+-		return -1;
+-	}
 -}
 -
--/*
-- * MTUNLOAD: Rewind the tape and unload it.
-- */
--ccw_req_t *
--tape34xx_mtunload (tape_info_t * ti, int count)
+-static int 
+-eval_statechangebuf (statechangebuf_t * scbuf)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 3, 32);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xunl nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	int retval = 0;
+-
+-	internal_print (
+-			       DELAYED_WRITE,
+-			       HWC_RW_PRINT_HEADER
+-			       "HWC state change detected\n");
+-
+-	if (scbuf->validity_hwc_active_facility_mask) {
+-
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = REWIND_UNLOAD;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	ccw++;
--	ccw->cmd_code = SENSE;
--	ccw->flags = 0;
--	ccw->count = 32;
--	ccw->cda = (unsigned long) cqr->cpaddr;
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_RUN_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xunl ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	if (scbuf->validity_hwc_receive_mask) {
+-
+-		if (scbuf->mask_length != 4) {
+-#ifdef DUMP_HWC_INIT_ERROR
+-			__asm__ ("LHI 1,0xe50\n\t"
+-				 "LRA 2,0(%0)\n\t"
+-				 "J .+0 \n\t"
+-		      :
+-		      :	 "a" (scbuf)
+-		      :	 "1", "2");
+-#endif
+-		} else {
+-
+-			retval += eval_hwc_receive_mask
+-			    (scbuf->hwc_receive_mask);
+-		}
+-	}
+-	if (scbuf->validity_hwc_send_mask) {
+-
+-		if (scbuf->mask_length != 4) {
+-#ifdef DUMP_HWC_INIT_ERROR
+-			__asm__ ("LHI 1,0xe51\n\t"
+-				 "LRA 2,0(%0)\n\t"
+-				 "J .+0 \n\t"
+-		      :
+-		      :	 "a" (scbuf)
+-		      :	 "1", "2");
+-#endif
+-		} else {
+-
+-			retval += eval_hwc_send_mask
+-			    (scbuf->hwc_send_mask);
+-		}
+-	}
+-	if (scbuf->validity_read_data_function_mask) {
+-
+-	}
+-	return retval;
 -}
 -
--/*
-- * MTCOMPRESSION: used to enable compression.
-- * Sets the IDRC on/off.
-- */
--ccw_req_t *
--tape34xx_mtcompression (tape_info_t * ti, int count)
+-#ifdef CONFIG_SMP
+-extern unsigned long cpu_online_map;
+-static volatile unsigned long cpu_quiesce_map;
+-
+-static void 
+-do_load_quiesce_psw (void)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	if ((count < 0) || (count > 1)) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xcom parm");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	if (count == 0)
--		((tape34xx_disc_data_t *) ti->discdata)->modeset_byte = 0x00;		// IDRC off
+-	psw_t quiesce_psw;
 -
--	else
--		((tape34xx_disc_data_t *) ti->discdata)->modeset_byte = 0x08;		// IDRC on
+-	clear_bit (smp_processor_id (), &cpu_quiesce_map);
+-	if (smp_processor_id () == 0) {
 -
--	cqr = tape_alloc_ccw_req (ti, 2, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xcom nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-		while (cpu_quiesce_map != 0) ;
+-
+-		quiesce_psw.mask = _DW_PSW_MASK;
+-		quiesce_psw.addr = 0xfff;
+-		__load_psw (quiesce_psw);
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_NOP_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xcom ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	signal_processor (smp_processor_id (), sigp_stop);
 -}
 -
--/*
-- * MTSTPART: Move the tape head at the partition with the number 'count'.
-- * Implement the NOP CCW command.
-- */
--ccw_req_t *
--tape34xx_mtsetpart (tape_info_t * ti, int count)
+-static void 
+-do_machine_quiesce (void)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 2, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xspa nomem");
--#endif /* TAPE_DEBUG */	    
--		return NULL;
+-	cpu_quiesce_map = cpu_online_map;
+-	smp_call_function (do_load_quiesce_psw, NULL, 0, 0);
+-	do_load_quiesce_psw ();
+-}
+-
+-#else
+-static void 
+-do_machine_quiesce (void)
+-{
+-	psw_t quiesce_psw;
+-
+-	quiesce_psw.mask = _DW_PSW_MASK;
+-	queisce_psw.addr = 0xfff;
+-	__load_psw (quiesce_psw);
+-}
+-
+-#endif
+-
+-static int 
+-process_evbufs (void *start, void *end)
+-{
+-	int retval = 0;
+-	evbuf_t *evbuf;
+-	void *evbuf_end;
+-	gds_vector_t *evbuf_data;
+-
+-	evbuf = (evbuf_t *) start;
+-	while (((void *) evbuf) < end) {
+-		evbuf_data = (gds_vector_t *)
+-		    (((unsigned long) evbuf) + sizeof (evbuf_t));
+-		evbuf_end = (void *) (((unsigned long) evbuf) + evbuf->length);
+-		switch (evbuf->type) {
+-		case ET_OpCmd:
+-		case ET_CntlProgOpCmd:
+-		case ET_PMsgCmd:
+-#ifdef DUMP_HWCB_INPUT
+-
+-			internal_print (
+-					       DELAYED_WRITE,
+-					       HWC_RW_PRINT_HEADER
+-					       "event buffer "
+-					   "at 0x%x up to 0x%x, length: %d\n",
+-					       (unsigned long) evbuf,
+-					       (unsigned long) (evbuf_end - 1),
+-					       evbuf->length);
+-			dump_storage_area ((void *) evbuf, evbuf->length);
+-#endif
+-			retval += eval_evbuf (evbuf_data, evbuf_end);
+-			break;
+-		case ET_StateChange:
+-			retval += eval_statechangebuf
+-			    ((statechangebuf_t *) evbuf);
+-			break;
+-		case ET_SigQuiesce:
+-
+-			_machine_restart = do_machine_quiesce;
+-			_machine_halt = do_machine_quiesce;
+-			_machine_power_off = do_machine_quiesce;
+-			ctrl_alt_del ();
+-			break;
+-		default:
+-			internal_print (
+-					       DELAYED_WRITE,
+-					       HWC_RW_PRINT_HEADER
+-					       "unconditional read: "
+-					       "unknown event buffer found, "
+-					       "type 0x%x",
+-					       evbuf->type);
+-			retval = -ENOSYS;
+-		}
+-		evbuf = (evbuf_t *) evbuf_end;
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_NOP_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xspa ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
+-	return retval;
 -}
 -
--/*
-- * MTMKPART: .... dummy .
-- * Implement the NOP CCW command.
-- */
--ccw_req_t *
--tape34xx_mtmkpart (tape_info_t * ti, int count)
+-static int 
+-unconditional_read_1 (void)
 -{
--	long lockflags;
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	cqr = tape_alloc_ccw_req (ti, 2, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xnpa nomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
+-	unsigned short int condition_code;
+-	read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
+-	int retval;
+-
+-#if 0
+-
+-	if ((!hwc_data.read_prio) && (!hwc_data.read_nonprio))
+-		return -EOPNOTSUPP;
+-
+-	if (hwc_data.current_servc)
+-		return -EBUSY;
+-#endif
+-
+-	memset (hwcb, 0x00, PAGE_SIZE);
+-	memcpy (hwcb, &read_hwcb_template, sizeof (read_hwcb_t));
+-
+-	condition_code = service_call (HWC_CMDW_READDATA, hwc_data.page);
+-
+-#ifdef DUMP_HWC_READ_ERROR
+-	if (condition_code == HWC_NOT_OPERATIONAL)
+-		__asm__ ("LHI 1,0xe40\n\t"
+-			 "L 2,0(%0)\n\t"
+-			 "LRA 3,0(%1)\n\t"
+-			 "J .+0 \n\t"
+-	      :
+-	      :	 "a" (&condition_code), "a" (hwc_data.page)
+-	      :	 "1", "2", "3");
+-#endif
+-
+-	switch (condition_code) {
+-	case HWC_COMMAND_INITIATED:
+-		hwc_data.current_servc = HWC_CMDW_READDATA;
+-		hwc_data.current_hwcb = hwc_data.page;
+-		retval = condition_code;
+-		break;
+-	case HWC_BUSY:
+-		retval = -EBUSY;
+-		break;
+-	default:
+-		retval = -EIO;
 -	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->kernbuf = NULL;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_NOP_INIT);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xnpa ccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
--}
 -
--/*
-- * MTIOCGET: query the tape drive status.
-- */
--ccw_req_t *
--tape34xx_mtiocget (tape_info_t * ti, int count)
--{
--	return NULL;
+-	return retval;
 -}
 -
--/*
-- * MTIOCPOS: query the tape position.
-- */
--ccw_req_t *
--tape34xx_mtiocpos (tape_info_t * ti, int count)
+-static int 
+-unconditional_read_2 (u32 ext_int_param)
 -{
--	return NULL;
--}
+-	read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
 -
--ccw_req_t * tape34xx_bread (struct request *req,tape_info_t* ti,int tapeblock_major) {
--	ccw_req_t *cqr;
--	ccw1_t *ccw;
--	__u8 *data;
--	int s2b = blksize_size[tapeblock_major][ti->blk_minor]/hardsect_size[tapeblock_major][ti->blk_minor];
--	int realcount;
--	int size,bhct = 0;
--	struct buffer_head* bh;
--	for (bh = req->bh; bh; bh = bh->b_reqnext) {
--		if (bh->b_size > blksize_size[tapeblock_major][ti->blk_minor])
--			for (size = 0; size < bh->b_size; size += blksize_size[tapeblock_major][ti->blk_minor])
--				bhct++;
--		else
--			bhct++;
--	}
--	if ((data = kmalloc (4 * sizeof (__u8), GFP_ATOMIC)) == NULL) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,3,"xBREDnomem");
--#endif /* TAPE_DEBUG */
--		return NULL;
--	}
--	data[0] = 0x01;
--	data[1] = data[2] = data[3] = 0x00;
--	realcount=req->sector/s2b;
--	if (((tape34xx_disc_data_t *) ti->discdata)->modeset_byte & 0x08)	// IDRC on
+-#ifdef DUMP_HWC_READ_ERROR
+-	if ((hwcb->response_code != 0x0020) &&
+-	    (hwcb->response_code != 0x0220) &&
+-	    (hwcb->response_code != 0x60F0) &&
+-	    (hwcb->response_code != 0x62F0))
+-		__asm__ ("LHI 1,0xe41\n\t"
+-			 "LRA 2,0(%0)\n\t"
+-			 "L 3,0(%1)\n\t"
+-			 "J .+0\n\t"
+-	      :
+-	      :	 "a" (hwc_data.page), "a" (&(hwcb->response_code))
+-	      :	 "1", "2", "3");
+-#endif
 -
--		data[1] = data[1] | 0x80;
--	data[3] += realcount % 256;
--	data[2] += (realcount / 256) % 256;
--	data[1] += (realcount / 65536);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xBREDid:");
--	debug_int_event (tape_debug_area,6,realcount);
--#endif /* TAPE_DEBUG */
--	cqr = tape_alloc_ccw_req (ti, 2+bhct+1, 0);
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--	        debug_text_exception (tape_debug_area,6,"xBREDnomem");
--#endif /* TAPE_DEBUG */
--		kfree(data);
--		return NULL;
--	}
--	ccw = cqr->cpaddr;
--	ccw->cmd_code = MODE_SET_DB;
--	ccw->flags = CCW_FLAG_CC;
--	ccw->count = 1;
--	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
--	if (realcount!=ti->position) {
--	    ccw++;
--	    ccw->cmd_code = LOCATE;
--	    ccw->flags = CCW_FLAG_CC;
--	    ccw->count = 4;
--	    set_normalized_cda (ccw, (unsigned long) data);
--	}
--	ti->position=realcount+req->nr_sectors/s2b;
--	for (bh=req->bh;bh!=NULL;) {
--	        ccw->flags = CCW_FLAG_CC;
--		if (bh->b_size >= blksize_size[tapeblock_major][ti->blk_minor]) {
--			for (size = 0; size < bh->b_size; size += blksize_size[tapeblock_major][ti->blk_minor]) {
--			        ccw++;
--				ccw->flags = CCW_FLAG_CC;
--				ccw->cmd_code = READ_FORWARD;
--				ccw->count = blksize_size[tapeblock_major][ti->blk_minor];
--				set_normalized_cda (ccw, __pa (bh->b_data + size));
--			}
--			bh = bh->b_reqnext;
--		} else {	/* group N bhs to fit into byt_per_blk */
--			for (size = 0; bh != NULL && size < blksize_size[tapeblock_major][ti->blk_minor];) {
--				ccw++;
--				ccw->flags = CCW_FLAG_DC;
--				ccw->cmd_code = READ_FORWARD;
--				ccw->count = bh->b_size;
--				set_normalized_cda (ccw, __pa (bh->b_data));
--				size += bh->b_size;
--				bh = bh->b_reqnext;
--			}
--			if (size != blksize_size[tapeblock_major][ti->blk_minor]) {
--				PRINT_WARN ("Cannot fulfill small request %d vs. %d (%ld sects)\n",
--					    size,
--					    blksize_size[tapeblock_major][ti->blk_minor],
--					    req->nr_sectors);
--				kfree(data);
--				tape_free_request (cqr);
--				return NULL;
--			}
--		}
+-	hwc_data.current_servc = 0;
+-	hwc_data.current_hwcb = NULL;
+-
+-	switch (hwcb->response_code) {
+-
+-	case 0x0020:
+-	case 0x0220:
+-		return process_evbufs (
+-		     (void *) (((unsigned long) hwcb) + sizeof (read_hwcb_t)),
+-			    (void *) (((unsigned long) hwcb) + hwcb->length));
+-
+-	case 0x60F0:
+-	case 0x62F0:
+-		internal_print (
+-				       IMMEDIATE_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "unconditional read: "
+-				     "got interrupt and tried to read input, "
+-				  "but nothing found (response code=0x%x).\n",
+-				       hwcb->response_code);
+-		return 0;
+-
+-	case 0x0100:
+-		internal_print (
+-				       IMMEDIATE_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-			 "unconditional read: HWCB boundary violation - this "
+-			 "must not occur in a correct driver, please contact "
+-				       "author\n");
+-		return -EIO;
+-
+-	case 0x0300:
+-		internal_print (
+-				       IMMEDIATE_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "unconditional read: "
+-			"insufficient HWCB length - this must not occur in a "
+-				   "correct driver, please contact author\n");
+-		return -EIO;
+-
+-	case 0x01F0:
+-		internal_print (
+-				       IMMEDIATE_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				       "unconditional read: "
+-			 "invalid command - this must not occur in a correct "
+-				       "driver, please contact author\n");
+-		return -EIO;
+-
+-	case 0x40F0:
+-		internal_print (
+-				       IMMEDIATE_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-			       "unconditional read: invalid function code\n");
+-		return -EIO;
+-
+-	case 0x70F0:
+-		internal_print (
+-				       IMMEDIATE_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-			      "unconditional read: invalid selection mask\n");
+-		return -EIO;
+-
+-	case 0x0040:
+-		internal_print (
+-				       IMMEDIATE_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				 "unconditional read: HWC equipment check\n");
+-		return -EIO;
+-
+-	default:
+-		internal_print (
+-				       IMMEDIATE_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-			"unconditional read: invalid response code %x - this "
+-			 "must not occur in a correct driver, please contact "
+-				       "author\n",
+-				       hwcb->response_code);
+-		return -EIO;
 -	}
--	ccw -> flags &= ~(CCW_FLAG_DC);
--	ccw -> flags |= (CCW_FLAG_CC);
--	ccw++;
--	ccw->cmd_code = NOP;
--	ccw->flags = 0;
--	ccw->count = 0;
--	ccw->cda = (unsigned long) (&(ccw->cmd_code));
--	ti->kernbuf = data;
--	ti->userbuf = NULL;
--	tapestate_set (ti, TS_BLOCK_INIT);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xBREDccwg");
--#endif /* TAPE_DEBUG */
--	return cqr;
--}
--void tape34xx_free_bread (ccw_req_t* cqr,struct _tape_info_t* ti) {
--    ccw1_t* ccw;
--    for (ccw=(ccw1_t*)cqr->cpaddr;(ccw->flags & CCW_FLAG_CC)||(ccw->flags & CCW_FLAG_DC);ccw++) 
--	if ((ccw->cmd_code == MODE_SET_DB) ||
--	    (ccw->cmd_code == LOCATE) ||
--	    (ccw->cmd_code == READ_FORWARD))
--	    clear_normalized_cda(ccw);
--    tape_free_request(cqr);
--    kfree(ti->kernbuf);
--    ti->kernbuf=NULL;
 -}
 -
--/* event handlers */
--void
--tape34xx_default_handler (tape_info_t * ti)
+-static int 
+-write_event_mask_1 (void)
 -{
--#ifdef TAPE_DEBUG
--    debug_text_event (tape_debug_area,6,"xdefhandle");
--#endif /* TAPE_DEBUG */
--	PRINT_ERR ("TAPE34XX: An unexpected Unit Check occurred.\n");
--	PRINT_ERR ("TAPE34XX: Please read Documentation/s390/TAPE and report it!\n");
--	PRINT_ERR ("TAPE34XX: Current state is: %s",
--		   (((tapestate_get (ti) < TS_SIZE) && (tapestate_get (ti) >= 0)) ?
--		    state_verbose[tapestate_get (ti)] : "->UNKNOWN STATE<-"));
--	tape_dump_sense (&ti->devstat);
--	ti->rc = -EIO;
--	ti->wanna_wakeup=1;
--        switch (tapestate_get(ti)) {
--        case TS_REW_RELEASE_INIT:
--            tapestate_set(ti,TS_FAILED);
--            wake_up (&ti->wq);
--            break;
--        case TS_BLOCK_INIT:
--            tapestate_set(ti,TS_FAILED);
--            schedule_tapeblock_exec_IO(ti);
--            break;
--        default:
--            tapestate_set(ti,TS_FAILED);
--            wake_up_interruptible (&ti->wq);
--        }      
+-	unsigned int condition_code;
+-	int retval;
+-
+-	condition_code = service_call (HWC_CMDW_WRITEMASK, hwc_data.page);
+-
+-#ifdef DUMP_HWC_INIT_ERROR
+-
+-	if (condition_code == HWC_NOT_OPERATIONAL)
+-		__asm__ ("LHI 1,0xe10\n\t"
+-			 "L 2,0(%0)\n\t"
+-			 "LRA 3,0(%1)\n\t"
+-			 "J .+0\n\t"
+-	      :
+-	      :	 "a" (&condition_code), "a" (hwc_data.page)
+-	      :	 "1", "2", "3");
+-#endif
+-
+-	switch (condition_code) {
+-	case HWC_COMMAND_INITIATED:
+-		hwc_data.current_servc = HWC_CMDW_WRITEMASK;
+-		hwc_data.current_hwcb = hwc_data.page;
+-		retval = condition_code;
+-		break;
+-	case HWC_BUSY:
+-		retval = -EBUSY;
+-		break;
+-	default:
+-		retval = -EIO;
+-	}
+-
+-	return retval;
 -}
 -
--void
--tape34xx_unexpect_uchk_handler (tape_info_t * ti)
+-static int 
+-write_event_mask_2 (u32 ext_int_param)
 -{
--	if ((ti->devstat.ii.sense.data[0] == 0x40) &&
--	    (ti->devstat.ii.sense.data[1] == 0x40) &&
--	    (ti->devstat.ii.sense.data[3] == 0x43)) {
--		// no tape in the drive
--	        PRINT_INFO ("Drive %d not ready. No volume loaded.\n", ti->rew_minor / 2);
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,3,"xuuh nomed");
--#endif /* TAPE_DEBUG */
--		tapestate_set (ti, TS_FAILED);
--		ti->rc = -ENOMEDIUM;
--		ti->wanna_wakeup=1;
--		wake_up_interruptible (&ti->wq);
--	} else if ((ti->devstat.ii.sense.data[0] == 0x42) &&
--		   (ti->devstat.ii.sense.data[1] == 0x44) &&
--		   (ti->devstat.ii.sense.data[3] == 0x3b)) {
--       	        PRINT_INFO ("Media in drive %d was changed!\n",
--			    ti->rew_minor / 2);
--#ifdef TAPE_DEBUG
--		debug_text_event (tape_debug_area,3,"xuuh medchg");
+-	init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page;
+-	int retval = 0;
+-
+-	if (hwcb->response_code != 0x0020) {
+-#ifdef DUMP_HWC_INIT_ERROR
+-		__asm__ ("LHI 1,0xe11\n\t"
+-			 "LRA 2,0(%0)\n\t"
+-			 "L 3,0(%1)\n\t"
+-			 "J .+0\n\t"
+-	      :
+-	      :	 "a" (hwcb), "a" (&(hwcb->response_code))
+-	      :	 "1", "2", "3");
+-#else
+-		retval = -1;
 -#endif
--		/* nothing to do. chan end & dev end will be reported when io is finished */
 -	} else {
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,3,"xuuh unexp");
--	        debug_text_event (tape_debug_area,3,"state:");
--	        debug_text_event (tape_debug_area,3,((tapestate_get (ti) < TS_SIZE) && 
--						     (tapestate_get (ti) >= 0)) ?
--				  state_verbose[tapestate_get (ti)] : 
--				  "TS UNKNOWN");
--#endif /* TAPE_DEBUG */
--		tape34xx_default_handler (ti);
+-		if (hwcb->mask_length != 4) {
+-#ifdef DUMP_HWC_INIT_ERROR
+-			__asm__ ("LHI 1,0xe52\n\t"
+-				 "LRA 2,0(%0)\n\t"
+-				 "J .+0 \n\t"
+-		      :
+-		      :	 "a" (hwcb)
+-		      :	 "1", "2");
+-#endif
+-		} else {
+-			retval += eval_hwc_receive_mask
+-			    (hwcb->hwc_receive_mask);
+-			retval += eval_hwc_send_mask (hwcb->hwc_send_mask);
+-		}
 -	}
+-
+-	hwc_data.current_servc = 0;
+-	hwc_data.current_hwcb = NULL;
+-
+-	return retval;
 -}
 -
--void
--tape34xx_unused_done (tape_info_t * ti)
+-static int 
+-set_hwc_ioctls (hwc_ioctls_t * ioctls, char correct)
 -{
--    if (ti->medium_is_unloaded) {
--	// A medium was inserted in the drive!
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xuui med");
--#endif /* TAPE_DEBUG */
--	PRINT_WARN ("A medium was inserted into the tape.\n");
--	ti->medium_is_unloaded=0;
--    } else {
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,3,"unsol.irq!");
--        debug_text_event (tape_debug_area,3,"dev end");
--        debug_int_exception (tape_debug_area,3,ti->devinfo.irq);
--#endif /* TAPE_DEBUG */
--	PRINT_WARN ("Unsolicited IRQ (Device End) caught in unused state.\n");
--	tape_dump_sense (&ti->devstat);
--    }
--}
+-	int retval = 0;
+-	hwc_ioctls_t tmp;
+-
+-	if (ioctls->width_htab > MAX_MESSAGE_SIZE) {
+-		if (correct)
+-			tmp.width_htab = MAX_MESSAGE_SIZE;
+-		else
+-			retval = -EINVAL;
+-	} else
+-		tmp.width_htab = ioctls->width_htab;
+-
+-	tmp.echo = ioctls->echo;
+-
+-	if (ioctls->columns > MAX_MESSAGE_SIZE) {
+-		if (correct)
+-			tmp.columns = MAX_MESSAGE_SIZE;
+-		else
+-			retval = -EINVAL;
+-	} else
+-		tmp.columns = ioctls->columns;
+-
+-	tmp.final_nl = ioctls->final_nl;
+-
+-	if (ioctls->max_hwcb < 2) {
+-		if (correct)
+-			tmp.max_hwcb = 2;
+-		else
+-			retval = -EINVAL;
+-	} else
+-		tmp.max_hwcb = ioctls->max_hwcb;
+-
+-	tmp.tolower = ioctls->tolower;
+-
+-	if (ioctls->kmem_hwcb > ioctls->max_hwcb) {
+-		if (correct)
+-			tmp.kmem_hwcb = ioctls->max_hwcb;
+-		else
+-			retval = -EINVAL;
+-	} else
+-		tmp.kmem_hwcb = ioctls->kmem_hwcb;
+-
+-	if (ioctls->kmem_hwcb > MAX_KMEM_PAGES) {
+-		if (correct)
+-			ioctls->kmem_hwcb = MAX_KMEM_PAGES;
+-		else
+-			retval = -EINVAL;
+-	}
+-	if (ioctls->kmem_hwcb < 2) {
+-		if (correct)
+-			ioctls->kmem_hwcb = 2;
+-		else
+-			retval = -EINVAL;
+-	}
+-	tmp.delim = ioctls->delim;
 -
+-	if (!(retval < 0))
+-		hwc_data.ioctls = tmp;
 -
--void
--tape34xx_idle_done (tape_info_t * ti)
--{
--    if (ti->medium_is_unloaded) {
--	// A medium was inserted in the drive!
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"xuud med");
--#endif /* TAPE_DEBUG */
--	PRINT_WARN ("A medium was inserted into the tape.\n");
--	ti->medium_is_unloaded=0;
--	wake_up_interruptible (&ti->wq);
--    } else {
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,3,"unsol.irq!");
--        debug_text_event (tape_debug_area,3,"dev end");
--        debug_int_exception (tape_debug_area,3,ti->devinfo.irq);
--#endif /* TAPE_DEBUG */
--	PRINT_WARN ("Unsolicited IRQ (Device End) caught in idle state.\n");
--	tape_dump_sense (&ti->devstat);
--    }
+-	return retval;
 -}
 -
--void
--tape34xx_block_done (tape_info_t * ti)
+-int 
+-do_hwc_init (void)
 -{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"x:bREQdone");
--#endif /* TAPE_DEBUG */
--	tapestate_set(ti,TS_DONE);
--	schedule_tapeblock_exec_IO(ti);
--}
+-	int retval;
 -
--void
--tape34xx_bsf_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"bsf done");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
--}
+-	memcpy (hwc_data.page, &init_hwcb_template, sizeof (init_hwcb_t));
 -
--void
--tape34xx_dse_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"dse done");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
--}
+-	do {
 -
--void
--tape34xx_fsf_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"fsf done");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
--}
+-		retval = write_event_mask_1 ();
 -
--void
--tape34xx_fsb_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"fsb done");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
--}
+-		if (retval == -EBUSY) {
 -
--void
--tape34xx_bsb_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"bsb done");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	ti->wanna_wakeup=1;
--	wake_up (&ti->wq);
--}
+-			hwc_data.flags |= HWC_INIT;
 -
--void
--tape34xx_lbl_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"lbl done");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	//s390irq_spin_unlock(tape->devinfo.irq);
--	ti->wanna_wakeup=1;
--	wake_up (&ti->wq);
--}
+-			__ctl_store (cr0, 0, 0);
+-			cr0_save = cr0;
+-			cr0 |= 0x00000200;
+-			cr0 &= 0xFFFFF3AC;
+-			__ctl_load (cr0, 0, 0);
 -
--void
--tape34xx_nop_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"nop done..");
--        debug_text_exception (tape_debug_area,6,"or rew/rel");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	//s390irq_spin_unlock(tape->devinfo.irq);
--	ti->wanna_wakeup=1;
--	wake_up (&ti->wq);
--}
+-			asm volatile ("STOSM %0,0x01"
+-				      :"=m" (psw_mask)::"memory");
 -
--void
--tape34xx_rfo_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"rfo done");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	ti->wanna_wakeup=1;
--	wake_up (&ti->wq);
+-			while (!(hwc_data.flags & HWC_INTERRUPT))
+-				barrier ();
+-
+-			asm volatile ("STNSM %0,0xFE"
+-				      :"=m" (psw_mask)::"memory");
+-
+-			__ctl_load (cr0_save, 0, 0);
+-
+-			hwc_data.flags &= ~HWC_INIT;
+-		}
+-	} while (retval == -EBUSY);
+-
+-	if (retval == -EIO) {
+-		hwc_data.flags |= HWC_BROKEN;
+-		printk (HWC_RW_PRINT_HEADER "HWC not operational\n");
+-	}
+-	return retval;
 -}
 -
--void
--tape34xx_rbi_init_done (tape_info_t * ti)
+-void hwc_interrupt_handler (struct pt_regs *regs, __u16 code);
+-
+-int 
+-hwc_init (void)
 -{
--	__u8 *data;
--#ifdef TAPE_DEBUG
+-	int retval;
+-
+-#ifdef BUFFER_STRESS_TEST
+-
+-	init_hwcb_t *hwcb;
 -	int i;
--#endif
--	tapestate_set (ti, TS_FAILED);
--	data = ti->kernbuf;
--	ti->rc = data[3];
--	ti->rc += 256 * data[2];
--	ti->rc += 65536 * (data[1] & 0x3F);
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"rbi done");
--        debug_text_event (tape_debug_area,6,"data:");
--	for (i=0;i<8;i++)
--	    debug_int_event (tape_debug_area,6,data[i]);
--#endif
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
--}
 -
--void
--tape34xx_rew_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"rew done");
 -#endif
--	//BH: use irqsave
--	//s390irq_spin_lock(tape->devinfo.irq);
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	//s390irq_spin_unlock(tape->devinfo.irq);
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
--}
 -
--void
--tape34xx_rew_release_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"rewR done");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	//s390irq_spin_unlock(tape->devinfo.irq);
--	ti->wanna_wakeup=1;
--	wake_up (&ti->wq);
--}
+-	if (register_early_external_interrupt (0x2401, hwc_interrupt_handler,
+-					       &ext_int_info_hwc) != 0)
+-		panic ("Couldn't request external interrupts 0x2401");
 -
--void
--tape34xx_run_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"rew done");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
--}
+-	spin_lock_init (&hwc_data.lock);
 -
--void
--tape34xx_wri_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"wri done");
--#endif
--	//BH: use irqsave
--	//s390irq_spin_lock(ti->devinfo.irq);
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	//s390irq_spin_unlock(ti->devinfo.irq);
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
--}
+-#ifdef USE_VM_DETECTION
 -
--void
--tape34xx_wtm_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,3,"wtm done");
--#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
--}
+-	if (MACHINE_IS_VM) {
 -
--/* This function analyses the tape's sense-data in case of a unit-check. If possible,
--   it tries to recover from the error. Else the user is informed about the problem. */
--void
--tape34xx_error_recovery (tape_info_t* ti)
--{
--    __u8* sense=ti->devstat.ii.sense.data;
--    int inhibit_cu_recovery=0;
--    int cu_type=ti->discipline->cu_type;
--    if ((((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)&0x80) inhibit_cu_recovery=1;
--    if (tapestate_get(ti)==TS_BLOCK_INIT) {
--	// no recovery for block device, bottom half will retry...
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    }
--    if (sense[0]&SENSE_COMMAND_REJECT)
--	switch (tapestate_get(ti)) {
--	case TS_BLOCK_INIT:
--	case TS_DSE_INIT:
--	case TS_EGA_INIT:
--	case TS_WRI_INIT:
--	case TS_WTM_INIT:
--	    if (sense[1]&SENSE_WRITE_PROTECT) {
--		// trying to write, but medium is write protected
--		tape34xx_error_recovery_has_failed(ti,EACCES);
--		return;
--	    }
--	default:
--	    tape34xx_error_recovery_HWBUG(ti,1);
--	    return;
--	}
--    // special cases for various tape-states when reaching end of recorded area
--    if (((sense[0]==0x08) || (sense[0]==0x10) || (sense[0]==0x12)) &&
--	((sense[1]==0x40) || (sense[1]==0x0c)))
--	switch (tapestate_get(ti)) {
--	case TS_FSF_INIT:
--	    // Trying to seek beyond end of recorded area
--	    tape34xx_error_recovery_has_failed(ti,EIO);
--	    return;
--	case TS_LBL_INIT:
--	    // Block could not be located.
--	    tape34xx_error_recovery_has_failed(ti,EIO);
--	    return;
--	case TS_RFO_INIT:
--	    // Try to read beyond end of recorded area -> 0 bytes read
--	    tape34xx_error_recovery_has_failed(ti,0);
--	    return;
--	}
--    // Sensing special bits
--    if (sense[0]&SENSE_BUS_OUT_CHECK) {
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    }
--    if (sense[0]&SENSE_DATA_CHECK) {
--	// hardware failure, damaged tape or improper operating conditions
--	switch (sense[3]) {
--	case 0x23:
--	    // a read data check occurred
--	    if ((sense[2]&SENSE_TAPE_SYNC_MODE) ||
--		(inhibit_cu_recovery)) {
--		// data check is not permanent, may be recovered. 
--		// We always use async-mode with cu-recovery, so this should *never* happen.
--		tape34xx_error_recovery_HWBUG(ti,2);
--		return;
--	    } else {
--		// data check is permanent, CU recovery has failed
--		PRINT_WARN("Permanent read error, recovery failed!\n");
--		tape34xx_error_recovery_has_failed(ti,EIO);
--		return;
--	    }
--	case 0x25:
--	    // a write data check occurred
--	    if ((sense[2]&SENSE_TAPE_SYNC_MODE) ||
--		(inhibit_cu_recovery)) {
--		// data check is not permanent, may be recovered.
--		// We always use async-mode with cu-recovery, so this should *never* happen.
--		tape34xx_error_recovery_HWBUG(ti,3);
--		return;
--	    } else {
--		// data check is permanent, cu-recovery has failed
--		PRINT_WARN("Permanent write error, recovery failed!\n");
--		tape34xx_error_recovery_has_failed(ti,EIO);
--		return;
--	    }
--	case 0x26:
--	    // Data Check (read opposite) occurred. We'll recover this.
--	    tape34xx_error_recovery_read_opposite(ti);
--	    return;
--	case 0x28:
--	    // The ID-Mark at the beginning of the tape could not be written. This is fatal, we'll report and exit.
--	    PRINT_WARN("ID-Mark could not be written. Check your hardware!\n");
--	    tape34xx_error_recovery_has_failed(ti,EIO);
--	    return;
--	case 0x31:
--	    // Tape void. Tried to read beyond end of device. We'll report and exit.
--	    PRINT_WARN("Try to read beyond end of recorded area!\n");
--	    tape34xx_error_recovery_has_failed(ti,ENOSPC);
--	    return;
--	case 0x41:
--	    // Record sequence error. cu detected incorrect block-id sequence on tape. We'll report and exit.
--	    PRINT_WARN("Illegal block-id sequence found!\n");
--	    tape34xx_error_recovery_has_failed(ti,EIO);
--	    return;
--	    default:
--	    // well, all data checks for 3480 should result in one of the above erpa-codes. if not -> bug
--	    // On 3490, other data-check conditions do exist.
--		if (cu_type==0x3480) {
--		    tape34xx_error_recovery_HWBUG(ti,4);
--		    return;
--		}
--	}
--    }
--    if (sense[0]&SENSE_OVERRUN) {
--	// A data overrun between cu and drive occurred. The channel speed is to slow! We'll report this and exit!
--	switch (sense[3]) {
--	case 0x40: // overrun error
--	    PRINT_WARN ("Data overrun error between control-unit and drive. Use a faster channel connection, if possible! \n");
--	    tape34xx_error_recovery_has_failed(ti,EIO);
--	    return;
--	default:
--	    // Overrun bit is set, but erpa does not show overrun error. This is a bug.
--	    tape34xx_error_recovery_HWBUG(ti,5);
--	    return;
--	}
--    }
--    if (sense[1]&SENSE_RECORD_SEQUENCE_ERR) {
--	switch (sense[3]) {
--	case 0x41:
--	    // Record sequence error. cu detected incorrect block-id sequence on tape. We'll report and exit.
--	    PRINT_WARN("Illegal block-id sequence found!\n");
--	    tape34xx_error_recovery_has_failed(ti,EIO);
--	    return;
--	default:
--	    // Record sequence error bit is set, but erpa does not show record sequence error. This is a bug.
--	    tape34xx_error_recovery_HWBUG(ti,6);
--	    return;
--	}
--    }
--    // Sensing erpa codes
--    switch (sense[3]) {
--    case 0x00:
--	// Everything is fine, but we got a unit check. Report and ignore!
--	PRINT_WARN ("Non-error sense was found. Unit-check will be ignored, expect errors...\n");
--	return;
--    case 0x21:
--	// Data streaming not operational. Cu switches to interlock mode, we reissue the command.
--	PRINT_WARN ("Data streaming not operational. Switching to interlock-mode! \n");
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    case 0x22:
--	// Path equipment check. Might be drive adapter error, buffer error on the lower interface, internal path not useable, or error during cartridge load.
--	// All of the above are not recoverable
--	PRINT_WARN ("A path equipment check occurred. One of the following conditions occurred:\n");
--	PRINT_WARN ("drive adapter error,buffer error on the lower interface, internal path not useable, error during cartridge load.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x23:
--	// Read data check. Should have been be covered earlier -> Bug!
--	tape34xx_error_recovery_HWBUG(ti,7);
--	return;
--    case 0x24:
--	// Load display check. Load display was command was issued, but the drive is displaying a drive check message. Can be threated as "device end".
--	tape34xx_error_recovery_succeded(ti);
--	return;
--    case 0x25:
--	// Write data check. Should have been covered earlier -> Bug!
--	tape34xx_error_recovery_HWBUG(ti,8);
--	return;
--    case 0x26:
--	// Data check (read opposite). Should have been covered earlier -> Bug!
--	tape34xx_error_recovery_HWBUG(ti,9);
--	return;
--    case 0x27:
--	// Command reject. May indicate illegal channel program or buffer over/underrun. 
--	// Since all channel programms are issued by this driver and ought be correct,
--	// we assume a over/underrun situaltion and retry the channel program.
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    case 0x28:
--	// Write id mark check. Should have beed covered earlier -> bug!
--	tape34xx_error_recovery_HWBUG(ti,10);
--	return;
--    case 0x29:
--	// Function incompatible. Either idrc is on but hardware not capable doing idrc 
--	// or a perform subsystem func is issued and the cu is not online. Anyway, this 
--	// cannot be recovered and is an I/O error.
--	PRINT_WARN ("Function incompatible. Try to switch off idrc! \n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x2a:
--	// Unsolicited environmental data. An internal counter overflows, we can ignore
--	// this and reissue the cmd.
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    case 0x2b:
--	// Environmental data present. Indicates either unload completed ok or read buffered 
--	// log command completed ok. 
--	if (tapestate_get(ti)==TS_RUN_INIT) {
--	    // Rewind unload completed ok.
--	    tape34xx_error_recovery_succeded(ti);
--	    return;
--	}
--	// Since we do not issue read buffered log commands, this should never occur -> bug.
--	tape34xx_error_recovery_HWBUG(ti,11);
--	return;
--    case 0x2c:
--	// Permanent equipment check. cu has tried recovery, but did not succeed. This is an
--	// I/O error.
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x2d:
--	// Data security erase failure.
--	if (tapestate_get(ti)==TS_DSE_INIT) {
--	    // report an I/O error
--	    tape34xx_error_recovery_has_failed(ti,EIO);
--	    return;
--	}
--	// Data security erase failure, but no such command issued. This is a bug.
--	tape34xx_error_recovery_HWBUG(ti,12);
--	return;
--    case 0x2e:
--	// Not capable. This indicates either that the drive fails reading the format id mark
--	// or that that format specified is not supported by the drive. We write a message and
--	// return an I/O error.
--	PRINT_WARN("Drive not capable processing the tape format!");
--	tape34xx_error_recovery_has_failed(ti,EMEDIUMTYPE);
--	return;
--    case 0x2f:
--	// This erpa is reserved. This is a bug.
--	tape34xx_error_recovery_HWBUG(ti,13);
--	return;
--    case 0x30:
--	// The medium is write protected, while trying to write on it. We'll report this.
--	PRINT_WARN("Medium is write protected!\n");
--	tape34xx_error_recovery_has_failed(ti,EACCES);
--	return;
--    case 0x31:
--	// Tape void. Should have beed covered ealier -> bug
--	tape34xx_error_recovery_HWBUG(ti,14);
--	return;
--    case 0x32:
--	// Tension loss. We cannot recover this, it's an I/O error.
--	PRINT_WARN("The drive lost tape tension.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x33:
--	// Load Failure. The catridge was not inserted correctly or the tape is not threaded
--	// correctly. We cannot recover this, the user has to reload the catridge.
--	PRINT_WARN("Cartridge load failure. Reload the cartridge and try again.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x34:
--	// Unload failure. The drive cannot maintain tape tension and control tape movement 
--	// during an unload operation. 
--	PRINT_WARN("Failure during cartridge unload. Please try manually.\n");
--	if (tapestate_get(ti)!=TS_RUN_INIT) {
--	    tape34xx_error_recovery_HWBUG(ti,15);
--	    return;
+-		if (hwc_data.init_ioctls.columns > 76)
+-			hwc_data.init_ioctls.columns = 76;
+-		hwc_data.init_ioctls.tolower = 1;
+-		if (!hwc_data.init_ioctls.delim)
+-			hwc_data.init_ioctls.delim = DEFAULT_CASE_DELIMITER;
+-	} else {
+-		hwc_data.init_ioctls.tolower = 0;
+-		hwc_data.init_ioctls.delim = 0;
 -	}
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x35:
--	// Drive equipment check. One of the following:
--	// - cu cannot recover from a drive detected error
--	// - a check code message is displayed on drive message/load displays
--	// - the cartridge loader does not respond correctly
--	// - a failure occurs during an index, load, or unload cycle
--	PRINT_WARN("Equipment check! Please check the drive and the cartridge loader.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x36:
--	switch (cu_type) {
--	case 0x3480:
--	    // This erpa is reserved for 3480 -> BUG
--	    tape34xx_error_recovery_HWBUG(ti,16);
--	    return;
--	case 0x3490:
--	    // End of data. This is a permanent I/O error, which cannot be recovered.
--	    // A read-type command has reached the end-of-data mark.
--	    tape34xx_error_recovery_has_failed(ti,EIO);
--	    return;
+-#endif
+-	retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
+-
+-	hwc_data.kmem_start = (unsigned long)
+-	    alloc_bootmem_low_pages (hwc_data.ioctls.kmem_hwcb * PAGE_SIZE);
+-	hwc_data.kmem_end = hwc_data.kmem_start +
+-	    hwc_data.ioctls.kmem_hwcb * PAGE_SIZE - 1;
+-
+-	retval = do_hwc_init ();
+-
+-	ctl_set_bit (0, 9);
+-
+-#ifdef BUFFER_STRESS_TEST
+-
+-	internal_print (
+-			       DELAYED_WRITE,
+-			       HWC_RW_PRINT_HEADER
+-			       "use %i bytes for buffering.\n",
+-			       hwc_data.ioctls.kmem_hwcb * PAGE_SIZE);
+-	for (i = 0; i < 500; i++) {
+-		hwcb = (init_hwcb_t *) BUF_HWCB;
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-			  "This is stress test message #%i, free: %i bytes\n",
+-				       i,
+-			     MAX_HWCB_ROOM - (hwcb->length + sizeof (mto_t)));
 -	}
--    case 0x37:
--	// Tape length error. The tape is shorter than reported in the beginning-of-tape data.
--	PRINT_WARN("Tape length error.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x38:
--	// Physical end of tape. A read/write operation reached the physical end of tape.
--	if (tapestate_get(ti)==TS_WRI_INIT ||
--	    tapestate_get(ti)==TS_DSE_INIT ||
--	    tapestate_get(ti)==TS_EGA_INIT ||
--	    tapestate_get(ti)==TS_WTM_INIT){
-- 	    tape34xx_error_recovery_has_failed(ti,ENOSPC);
--	} else {
--	    tape34xx_error_recovery_has_failed(ti,EIO);
+-
+-#endif
+-
+-	return /*retval */ 0;
+-}
+-
+-signed int 
+-hwc_register_calls (hwc_high_level_calls_t * calls)
+-{
+-	if (calls == NULL)
+-		return -EINVAL;
+-
+-	if (hwc_data.calls != NULL)
+-		return -EBUSY;
+-
+-	hwc_data.calls = calls;
+-	return 0;
+-}
+-
+-signed int 
+-hwc_unregister_calls (hwc_high_level_calls_t * calls)
+-{
+-	if (hwc_data.calls == NULL)
+-		return -EINVAL;
+-
+-	if (calls != hwc_data.calls)
+-		return -EINVAL;
+-
+-	hwc_data.calls = NULL;
+-	return 0;
+-}
+-
+-int 
+-hwc_send (hwc_request_t * req)
+-{
+-	unsigned long flags;
+-	int retval;
+-	int cc;
+-
+-	spin_lock_irqsave (&hwc_data.lock, flags);
+-	if (!req || !req->callback || !req->block) {
+-		retval = -EINVAL;
+-		goto unlock;
 -	}
--	return;
--    case 0x39:
--	// Backward at BOT. The drive is at BOT and is requestet to move backward.
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x3a:
--	// Drive switched not ready, but the command needs the drive to be ready.
--	PRINT_WARN("Drive not ready. Turn the ready/not ready switch to ready position and try again.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x3b:
--	// Manual rewind or unload. This causes an I/O error.
--	PRINT_WARN("Medium was rewound or unloaded manually. Expect errors! Please do only use the mtoffl and mtrew ioctl to unload tapes or rewind tapes.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x3c:
--    case 0x3d:
--    case 0x3e:
--    case 0x3f:
--	// These erpas are reserved -> BUG
--	tape34xx_error_recovery_HWBUG(ti,17);
--	return;
--    case 0x40:
--	// Overrun error. This should have been covered earlier -> bug.
--	tape34xx_error_recovery_HWBUG(ti,18);
--	return;
--    case 0x41:
--	// Record sequence error. This should have been covered earlier -> bug.
--	tape34xx_error_recovery_HWBUG(ti,19);
--	return;
--    case 0x42:
--	// Degraded mode. A condition that can cause degraded performace is detected.
--	PRINT_WARN("Subsystem is running in degraded mode. This may compromise your performace.\n");
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    case 0x43:
--	// Drive not ready. Probably swith the ready/not ready switch to ready?
--	PRINT_WARN("The drive is not ready. Maybe no medium in?\n");
--	tape34xx_error_recovery_has_failed(ti,ENOMEDIUM);
--	return;
--    case 0x44:
--	// Locate Block unsuccessfull. We'll report this.
--	if ((tapestate_get(ti)!=TS_BLOCK_INIT) &&
--	    (tapestate_get(ti)!=TS_LBL_INIT)) {
--	    tape34xx_error_recovery_HWBUG(ti,20); // No locate block was issued...
--	    return;
+-	if (hwc_data.request) {
+-		retval = -ENOTSUPP;
+-		goto unlock;
 -	}
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x45:
--	// The drive is assigned elsewhere [to a different channel path/computer].
--	PRINT_WARN("The drive is assigned elsewhere.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x46:
--	// Drive not online. Drive may be switched offline, the power supply may be switched off 
--	// or the drive address may not be set correctly.
--	PRINT_WARN("The drive is not online.");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x47:
--	// Volume fenced. cu reports volume integrity is lost! 
--	PRINT_WARN("Volume fenced. The volume integrity is lost! \n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x48:
--	// Log sense data and retry request. We'll do so...
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    case 0x49:
--	// Bus out check. A parity check error on the bus was found.	PRINT_WARN("Bus out check. A data transfer over the bus was corrupted.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x4a:
--	// Control unit erp failed. We'll report this.
--	PRINT_WARN("The control unit failed recovering an I/O error.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x4b:
--	// Cu and drive incompatible. The drive requests micro-program patches, which are not available on the cu.
--	PRINT_WARN("The drive needs microprogram patches from the control unit, which are not available.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x4c:
--	// Recovered Check-One failure. Cu develops a hardware error, but is able to recover. We'll reissue the command.
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    case 0x4d:
--	switch (cu_type) {
--	case 0x3480:
--	    // This erpa is reserved for 3480 -> bug
--      	    tape34xx_error_recovery_HWBUG(ti,21);
--	    return;
--	case 0x3490:
--	    // Resetting event received. Since the driver does not support resetting event recovery
--	    // (which has to be handled by the I/O Layer), we'll report and retry our command.
--	    tape34xx_error_recovery_do_retry(ti);
--	    return;
+-	cc = service_call (req->word, req->block);
+-	switch (cc) {
+-	case 0:
+-		hwc_data.request = req;
+-		hwc_data.current_servc = req->word;
+-		hwc_data.current_hwcb = req->block;
+-		retval = 0;
+-		break;
+-	case 2:
+-		retval = -EBUSY;
+-		break;
+-	default:
+-		retval = -ENOSYS;
+-
 -	}
--    case 0x4e:
--	switch (cu_type) {
--	case 0x3480:
--	    // This erpa is reserved for 3480 -> bug.
--	    tape34xx_error_recovery_HWBUG(ti,22);
--	    return;
--	case 0x3490:
--	    // Maximum block size exeeded. This indicates, that the block to be written is larger
--	    // than allowed for buffered mode. We'll report this...
--	    PRINT_WARN("Maximum block size for buffered mode exceeded.\n");
--	    tape34xx_error_recovery_has_failed(ti,ENOBUFS);
--	    return;
+-      unlock:
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-	return retval;
+-}
+-
+-EXPORT_SYMBOL (hwc_send);
+-
+-void 
+-do_hwc_callback (u32 ext_int_param)
+-{
+-	if (!hwc_data.request || !hwc_data.request->callback)
+-		return;
+-	if ((ext_int_param & HWC_EXT_INT_PARAM_ADDR)
+-	    != (unsigned long) hwc_data.request->block)
+-		return;
+-	hwc_data.request->callback (hwc_data.request);
+-	hwc_data.request = NULL;
+-	hwc_data.current_hwcb = NULL;
+-	hwc_data.current_servc = 0;
+-}
+-
+-void 
+-hwc_do_interrupt (u32 ext_int_param)
+-{
+-	u32 finished_hwcb = ext_int_param & HWC_EXT_INT_PARAM_ADDR;
+-	u32 evbuf_pending = ext_int_param & HWC_EXT_INT_PARAM_PEND;
+-
+-	if (hwc_data.flags & HWC_PTIMER_RUNS) {
+-		del_timer (&hwc_data.poll_timer);
+-		hwc_data.flags &= ~HWC_PTIMER_RUNS;
 -	}
--    case 0x4f:
--	// These erpas are reserved -> bug
--	tape34xx_error_recovery_HWBUG(ti,23);
--	return;
--    case 0x50:
--	// Read buffered log (Overflow). Cu is running in extended beffered log mode, and a counter overflows.
--	// This should never happen, since we're never running in extended buffered log mode -> bug.
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    case 0x51:
--	// Read buffered log (EOV). EOF processing occurs while the cu is in extended buffered log mode.
--	// This should never happen, since we're never running in extended buffered log mode -> bug.
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    case 0x52:
--	// End of Volume complete. Rewind unload completed ok. We'll report to the user...
--	if (tapestate_get(ti)!=TS_RUN_INIT) {
--	    tape34xx_error_recovery_HWBUG(ti,24);
--	    return;
+-	if (finished_hwcb) {
+-
+-		if ((unsigned long) hwc_data.current_hwcb != finished_hwcb) {
+-			internal_print (
+-					       DELAYED_WRITE,
+-					       HWC_RW_PRINT_HEADER
+-					       "interrupt: mismatch: "
+-					       "ext. int param. (0x%x) vs. "
+-					       "current HWCB (0x%x)\n",
+-					       ext_int_param,
+-					       hwc_data.current_hwcb);
+-		} else {
+-			if (hwc_data.request) {
+-
+-				do_hwc_callback (ext_int_param);
+-			} else {
+-
+-				switch (hwc_data.current_servc) {
+-
+-				case HWC_CMDW_WRITEMASK:
+-
+-					write_event_mask_2 (ext_int_param);
+-					break;
+-
+-				case HWC_CMDW_WRITEDATA:
+-
+-					write_event_data_2 (ext_int_param);
+-					break;
+-
+-				case HWC_CMDW_READDATA:
+-
+-					unconditional_read_2 (ext_int_param);
+-					break;
+-				default:
+-				}
+-			}
+-		}
+-	} else {
+-
+-		if (hwc_data.current_hwcb) {
+-			internal_print (
+-					       DELAYED_WRITE,
+-					       HWC_RW_PRINT_HEADER
+-					       "interrupt: mismatch: "
+-					       "ext. int. param. (0x%x) vs. "
+-					       "current HWCB (0x%x)\n",
+-					       ext_int_param,
+-					       hwc_data.current_hwcb);
+-		}
 -	}
--	tape34xx_error_recovery_succeded(ti);
--	return;
--    case 0x53:
--	// Global command intercept. We'll have to reissue our command.
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    case 0x54:
--	// Channel interface recovery (temporary). This can be recovered by reissuing the command.
--	tape34xx_error_recovery_do_retry(ti);
--	return;
--    case 0x55:
--	// Channel interface recovery (permanent). This cannot be recovered, we'll inform the user.
--	PRINT_WARN("A permanent channel interface error occurred.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x56:
--	// Channel protocol error. This cannot be recovered.
--	PRINT_WARN("A channel protocol error occurred.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x57:
--	switch (cu_type) {
--	case 0x3480:
--	    // Attention intercept. We have to reissue the command.
--	    PRINT_WARN("An attention intercept occurred, which will be recovered.\n");
--	    tape34xx_error_recovery_do_retry(ti);
--	    return;
--	case 0x3490:
--	    // Global status intercept. We have to reissue the command.
--	    PRINT_WARN("An global status intercept was received, which will be recovered.\n");
--	    tape34xx_error_recovery_do_retry(ti);
--	    return;
+-
+-	if (evbuf_pending) {
+-
+-		unconditional_read_1 ();
+-	} else {
+-
+-		write_event_data_1 ();
 -	}
--    case 0x58:
--    case 0x59:
--	// These erpas are reserved -> bug.
--	tape34xx_error_recovery_HWBUG(ti,25);
--	return;
--    case 0x5a:
--	// Tape length incompatible. The tape inserted is too long, 
--	// which could cause damage to the tape or the drive.
--	PRINT_WARN("Tape length incompatible [should be IBM Cartridge System Tape]. May cause damage to drive or tape.n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x5b:
--	// Format 3480 XF incompatible
--	if (sense[1]&SENSE_BEGINNING_OF_TAPE) {
--	    // Everything is fine. The tape will be overwritten in a different format.
--	    tape34xx_error_recovery_do_retry(ti);
--	    return;
+-
+-	if (!hwc_data.calls || !hwc_data.calls->wake_up)
+-		return;
+-	(hwc_data.calls->wake_up) ();
+-}
+-
+-void 
+-hwc_interrupt_handler (struct pt_regs *regs, __u16 code)
+-{
+-	int cpu = smp_processor_id ();
+-
+-	u32 ext_int_param = hwc_ext_int_param ();
+-
+-	irq_enter (cpu, 0x2401);
+-
+-	if (hwc_data.flags & HWC_INIT) {
+-
+-		hwc_data.flags |= HWC_INTERRUPT;
+-	} else if (hwc_data.flags & HWC_BROKEN) {
+-
+-		if (!do_hwc_init ()) {
+-			hwc_data.flags &= ~HWC_BROKEN;
+-			internal_print (DELAYED_WRITE,
+-					HWC_RW_PRINT_HEADER
+-					"delayed HWC setup after"
+-					" temporary breakdown"
+-					" (ext. int. parameter=0x%x)\n",
+-					ext_int_param);
+-		}
+-	} else {
+-		spin_lock (&hwc_data.lock);
+-		hwc_do_interrupt (ext_int_param);
+-		spin_unlock (&hwc_data.lock);
 -	}
--	PRINT_WARN("Tape format is incompatible to the drive, which writes 3480-2 XF.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x5c:
--	// Format 3480-2 XF incompatible
--	PRINT_WARN("Tape format is incompatible to the drive. The drive cannot access 3480-2 XF volumes.\n");
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	return;
--    case 0x5d:
--	// Tape length violation. 
--	PRINT_WARN("Tape length violation [should be IBM Enhanced Capacity Cartridge System Tape]. May cause damage to drive or tape.\n");
--	tape34xx_error_recovery_has_failed(ti,EMEDIUMTYPE);
--	return;
--    case 0x5e:
--	// Compaction algorithm incompatible.
--	PRINT_WARN("The volume is recorded using an incompatible compaction algorith, which is not supported by the control unit.\n");
--	tape34xx_error_recovery_has_failed(ti,EMEDIUMTYPE);
--	return;
--    default:
--	// Reserved erpas -> bug
--	tape34xx_error_recovery_HWBUG(ti,26);
--	return;
--    }
+-	irq_exit (cpu, 0x2401);
+-}
+-
+-void 
+-hwc_unblank (void)
+-{
+-
+-	spin_lock (&hwc_data.lock);
+-	spin_unlock (&hwc_data.lock);
+-
+-	__ctl_store (cr0, 0, 0);
+-	cr0_save = cr0;
+-	cr0 |= 0x00000200;
+-	cr0 &= 0xFFFFF3AC;
+-	__ctl_load (cr0, 0, 0);
+-
+-	asm volatile ("STOSM %0,0x01":"=m" (psw_mask)::"memory");
+-
+-	while (ALL_HWCB_CHAR)
+-		barrier ();
+-
+-	asm volatile ("STNSM %0,0xFE":"=m" (psw_mask)::"memory");
+-
+-	__ctl_load (cr0_save, 0, 0);
 -}
 -
--void tape34xx_error_recovery_has_failed (tape_info_t* ti,int error_id) {
--#ifdef TAPE_DEBUG
--    debug_text_event (tape_debug_area,3,"xerp fail");
--    debug_text_event (tape_debug_area,3,(((tapestate_get (ti) < TS_SIZE) && 
--		      (tapestate_get (ti) >= 0)) ?
--	state_verbose[tapestate_get (ti)] : "UNKNOWN"));
+-int 
+-hwc_ioctl (unsigned int cmd, unsigned long arg)
+-{
+-	hwc_ioctls_t tmp = hwc_data.ioctls;
+-	int retval = 0;
+-	unsigned long flags;
+-	unsigned int obuf;
+-
+-	spin_lock_irqsave (&hwc_data.lock, flags);
+-
+-	switch (cmd) {
+-
+-	case TIOCHWCSHTAB:
+-		if (get_user (tmp.width_htab, (ioctl_htab_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCSECHO:
+-		if (get_user (tmp.echo, (ioctl_echo_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCSCOLS:
+-		if (get_user (tmp.columns, (ioctl_cols_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCSNL:
+-		if (get_user (tmp.final_nl, (ioctl_nl_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCSOBUF:
+-		if (get_user (obuf, (unsigned int *) arg))
+-			goto fault;
+-		if (obuf & 0xFFF)
+-			tmp.max_hwcb = (((obuf | 0xFFF) + 1) >> 12);
+-		else
+-			tmp.max_hwcb = (obuf >> 12);
+-		break;
+-
+-	case TIOCHWCSCASE:
+-		if (get_user (tmp.tolower, (ioctl_case_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCSDELIM:
+-		if (get_user (tmp.delim, (ioctl_delim_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCSINIT:
+-		retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
+-		break;
+-
+-	case TIOCHWCGHTAB:
+-		if (put_user (tmp.width_htab, (ioctl_htab_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCGECHO:
+-		if (put_user (tmp.echo, (ioctl_echo_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCGCOLS:
+-		if (put_user (tmp.columns, (ioctl_cols_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCGNL:
+-		if (put_user (tmp.final_nl, (ioctl_nl_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCGOBUF:
+-		if (put_user (tmp.max_hwcb, (ioctl_obuf_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCGKBUF:
+-		if (put_user (tmp.kmem_hwcb, (ioctl_obuf_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCGCASE:
+-		if (put_user (tmp.tolower, (ioctl_case_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCGDELIM:
+-		if (put_user (tmp.delim, (ioctl_delim_t *) arg))
+-			goto fault;
+-		break;
+-#if 0
+-
+-	case TIOCHWCGINIT:
+-		if (put_user (&hwc_data.init_ioctls, (hwc_ioctls_t *) arg))
+-			goto fault;
+-		break;
+-
+-	case TIOCHWCGCURR:
+-		if (put_user (&hwc_data.ioctls, (hwc_ioctls_t *) arg))
+-			goto fault;
+-		break;
 -#endif
--    if ((tapestate_get(ti)!=TS_UNUSED) && (tapestate_get(ti)!=TS_IDLE)) {
--	tape_dump_sense(&ti->devstat);
--	ti->rc = -error_id;
--	ti->wanna_wakeup=1;
--	switch (tapestate_get(ti)) {
--	case TS_REW_RELEASE_INIT:
--	case TS_RFO_INIT:
--	case TS_RBA_INIT:
--	    tapestate_set(ti,TS_FAILED);
--	    wake_up (&ti->wq);
--	    break;
--	case TS_BLOCK_INIT:
--	    tapestate_set(ti,TS_FAILED);
--	    schedule_tapeblock_exec_IO(ti);
--	    break;
+-
 -	default:
--	    tapestate_set(ti,TS_FAILED);
--	    wake_up_interruptible (&ti->wq);
+-		goto noioctlcmd;
 -	}
--    } else {
--	PRINT_WARN("Recieved an unsolicited IRQ.\n");
--	tape_dump_sense(&ti->devstat);
--    }
--}    
 -
--void tape34xx_error_recovery_succeded(tape_info_t* ti) {
--#ifdef TAPE_DEBUG
--    debug_text_event (tape_debug_area,3,"xerp done");
--    debug_text_event (tape_debug_area,3,(((tapestate_get (ti) < TS_SIZE) && 
--		      (tapestate_get (ti) >= 0)) ?
--	state_verbose[tapestate_get (ti)] : "UNKNOWN"));
--#endif
--    if ((tapestate_get(ti)!=TS_UNUSED) && (tapestate_get(ti)!=TS_DONE)) {
--	tapestate_event (ti, TE_DONE);
--    } else {
--	PRINT_WARN("Recieved an unsolicited IRQ.\n");
--	tape_dump_sense(&ti->devstat);
--    }
--}
+-	if (_IOC_DIR (cmd) == _IOC_WRITE)
+-		retval = set_hwc_ioctls (&tmp, 0);
 -
--void tape34xx_error_recovery_do_retry(tape_info_t* ti) {
--#ifdef TAPE_DEBUG
--    debug_text_event (tape_debug_area,3,"xerp retr");
--    debug_text_event (tape_debug_area,3,(((tapestate_get (ti) < TS_SIZE) && 
--					  (tapestate_get (ti) >= 0)) ?
--					 state_verbose[tapestate_get (ti)] : "UNKNOWN"));
--#endif
--    if ((tapestate_get(ti)!=TS_UNUSED) && (tapestate_get(ti)!=TS_IDLE)) {
--	tape_dump_sense(&ti->devstat);
--	while (do_IO (ti->devinfo.irq, ti->cqr->cpaddr, (unsigned long) ti->cqr, 0x00, ti->cqr->options));
--    } else {
--	PRINT_WARN("Recieved an unsolicited IRQ.\n");
--	tape_dump_sense(&ti->devstat);
--    }
--}
--    
--void 
--tape34xx_error_recovery_read_opposite (tape_info_t* ti) {
--    switch (tapestate_get(ti)) {
--    case TS_RFO_INIT:
--	// We did read forward, but the data could not be read *correctly*.
--	// We will read backward and then skip forward again.
--	ti->cqr=tape34xx_read_opposite(ti,0);
--	if (ti->cqr==NULL)
--	    tape34xx_error_recovery_has_failed(ti,EIO);
--	else
--	    tape34xx_error_recovery_do_retry(ti);
--	break;
--    case TS_RBA_INIT:
--	// We tried to read forward and backward, but hat no success -> failed.
--	tape34xx_error_recovery_has_failed(ti,EIO);
--	break;
--    case TS_BLOCK_INIT:
--	tape34xx_error_recovery_do_retry(ti);
--	break;
--    default:
--	PRINT_WARN("read_opposite_recovery_called_with_state:%s\n",
--		   (((tapestate_get (ti) < TS_SIZE) && 
--		     (tapestate_get (ti) >= 0)) ?
--		    state_verbose[tapestate_get (ti)] : "UNKNOWN"));
--    }
--}
+-	goto out;
 -
--void 
--tape34xx_error_recovery_HWBUG (tape_info_t* ti,int condno) {
--    devstat_t* stat=&ti->devstat;
--    PRINT_WARN("An unexpected condition #%d was caught in tape error recovery.\n",condno);
--    PRINT_WARN("Please report this incident.\n");
--    PRINT_WARN("State of the tape:%s\n",
--	       (((tapestate_get (ti) < TS_SIZE) && 
--		 (tapestate_get (ti) >= 0)) ?
--		state_verbose[tapestate_get (ti)] : "UNKNOWN"));
--    PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X "
--		" %02X%02X%02X%02X %02X%02X%02X%02X \n",
--		stat->ii.sense.data[0], stat->ii.sense.data[1],
--		stat->ii.sense.data[2], stat->ii.sense.data[3],
--		stat->ii.sense.data[4], stat->ii.sense.data[5],
--		stat->ii.sense.data[6], stat->ii.sense.data[7],
--		stat->ii.sense.data[8], stat->ii.sense.data[9],
--		stat->ii.sense.data[10], stat->ii.sense.data[11],
--		stat->ii.sense.data[12], stat->ii.sense.data[13],
--		stat->ii.sense.data[14], stat->ii.sense.data[15]);
--    PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X "
--		" %02X%02X%02X%02X %02X%02X%02X%02X \n",
--		stat->ii.sense.data[16], stat->ii.sense.data[17],
--		stat->ii.sense.data[18], stat->ii.sense.data[19],
--		stat->ii.sense.data[20], stat->ii.sense.data[21],
--		stat->ii.sense.data[22], stat->ii.sense.data[23],
--		stat->ii.sense.data[24], stat->ii.sense.data[25],
--		stat->ii.sense.data[26], stat->ii.sense.data[27],
--		stat->ii.sense.data[28], stat->ii.sense.data[29],
--		stat->ii.sense.data[30], stat->ii.sense.data[31]);
--    tape34xx_error_recovery_has_failed(ti,EIO);
+-      fault:
+-	retval = -EFAULT;
+-	goto out;
+-      noioctlcmd:
+-	retval = -ENOIOCTLCMD;
+-      out:
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-	return retval;
 -}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape34xx.h drivers/s390/char/tape34xx.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape34xx.h	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tape34xx.h	2006-01-30 22:25:26.000000000 -0700
-@@ -1,183 +0,0 @@
--
--/***************************************************************************
-- *
-- *  drivers/s390/char/tape34xx.h
-- *    common tape device discipline for 34xx tapes.
-- *
-- *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-- *    Author(s): Carsten Otte <cotte at de.ibm.com>
-- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/hwc_rw.h drivers/s390/char/hwc_rw.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/hwc_rw.h	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/hwc_rw.h	2006-02-12 12:47:23.000000000 -0700
+@@ -1,132 +0,0 @@
+-/*
+- *  drivers/s390/char/hwc_rw.h
+- *    interface to the HWC-read/write driver 
 - *
-- ****************************************************************************
+- *  S390 version
+- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
 - */
 -
--#ifndef _TAPE34XX_H
+-#ifndef __HWC_RW_H__
+-#define __HWC_RW_H__
 -
--#define _TAPE34XX_H
+-#include <linux/ioctl.h>
+-
+-typedef struct {
+-
+-	void (*move_input) (unsigned char *, unsigned int);
+-
+-	void (*wake_up) (void);
+-} hwc_high_level_calls_t;
+-
+-struct _hwc_request;
+-
+-typedef void hwc_callback_t (struct _hwc_request *);
+-
+-typedef struct _hwc_request {
+-	void *block;
+-	u32 word;
+-	hwc_callback_t *callback;
+-	void *data;
+-} __attribute__ ((packed)) 
+-
+-hwc_request_t;
+-
+-#define HWC_ASCEBC(x) ((MACHINE_IS_VM ? _ascebc[x] : _ascebc_500[x]))
+-
+-#define HWC_EBCASC_STR(s,c) ((MACHINE_IS_VM ? EBCASC(s,c) : EBCASC_500(s,c)))
+-
+-#define HWC_ASCEBC_STR(s,c) ((MACHINE_IS_VM ? ASCEBC(s,c) : ASCEBC_500(s,c)))
+-
+-#define IN_HWCB      1
+-#define IN_WRITE_BUF 2
+-#define IN_BUFS_TOTAL        (IN_HWCB | IN_WRITE_BUF)
+-
+-typedef unsigned short int ioctl_htab_t;
+-typedef unsigned char ioctl_echo_t;
+-typedef unsigned short int ioctl_cols_t;
+-typedef signed char ioctl_nl_t;
+-typedef unsigned short int ioctl_obuf_t;
+-typedef unsigned char ioctl_case_t;
+-typedef unsigned char ioctl_delim_t;
 -
+-typedef struct {
+-	ioctl_htab_t width_htab;
+-	ioctl_echo_t echo;
+-	ioctl_cols_t columns;
+-	ioctl_nl_t final_nl;
+-	ioctl_obuf_t max_hwcb;
+-	ioctl_obuf_t kmem_hwcb;
+-	ioctl_case_t tolower;
+-	ioctl_delim_t delim;
+-} hwc_ioctls_t;
+-
+-static hwc_ioctls_t _hwc_ioctls;
+-
+-#define HWC_IOCTL_LETTER 'B'
+-
+-#define TIOCHWCSHTAB	_IOW(HWC_IOCTL_LETTER, 0, _hwc_ioctls.width_htab)
+-
+-#define TIOCHWCSECHO	_IOW(HWC_IOCTL_LETTER, 1, _hwc_ioctls.echo)
+-
+-#define TIOCHWCSCOLS	_IOW(HWC_IOCTL_LETTER, 2, _hwc_ioctls.columns)
+-
+-#define TIOCHWCSNL	_IOW(HWC_IOCTL_LETTER, 4, _hwc_ioctls.final_nl)
+-
+-#define TIOCHWCSOBUF	_IOW(HWC_IOCTL_LETTER, 5, _hwc_ioctls.max_hwcb)
+-
+-#define TIOCHWCSINIT	_IO(HWC_IOCTL_LETTER, 6)
+-
+-#define TIOCHWCSCASE	_IOW(HWC_IOCTL_LETTER, 7, _hwc_ioctls.tolower)
+-
+-#define TIOCHWCSDELIM	_IOW(HWC_IOCTL_LETTER, 9, _hwc_ioctls.delim)
+-
+-#define TIOCHWCGHTAB	_IOR(HWC_IOCTL_LETTER, 10, _hwc_ioctls.width_htab)
+-
+-#define TIOCHWCGECHO	_IOR(HWC_IOCTL_LETTER, 11, _hwc_ioctls.echo)
+-
+-#define TIOCHWCGCOLS	_IOR(HWC_IOCTL_LETTER, 12, _hwc_ioctls.columns)
+-
+-#define TIOCHWCGNL	_IOR(HWC_IOCTL_LETTER, 14, _hwc_ioctls.final_nl)
+-
+-#define TIOCHWCGOBUF	_IOR(HWC_IOCTL_LETTER, 15, _hwc_ioctls.max_hwcb)
+-
+-#define TIOCHWCGINIT	_IOR(HWC_IOCTL_LETTER, 16, _hwc_ioctls)
+-
+-#define TIOCHWCGCASE	_IOR(HWC_IOCTL_LETTER, 17, _hwc_ioctls.tolower)
+-
+-#define TIOCHWCGDELIM	_IOR(HWC_IOCTL_LETTER, 19, _hwc_ioctls.delim)
+-
+-#define TIOCHWCGKBUF	_IOR(HWC_IOCTL_LETTER, 20, _hwc_ioctls.max_hwcb)
+-
+-#define TIOCHWCGCURR	_IOR(HWC_IOCTL_LETTER, 21, _hwc_ioctls)
+-
+-#ifndef __HWC_RW_C__
+-
+-extern int hwc_init (void);
+-
+-extern int hwc_write (int from_user, const unsigned char *, unsigned int);
+-
+-extern unsigned int hwc_chars_in_buffer (unsigned char);
+-
+-extern unsigned int hwc_write_room (unsigned char);
+-
+-extern void hwc_flush_buffer (unsigned char);
+-
+-extern void hwc_unblank (void);
+-
+-extern signed int hwc_ioctl (unsigned int, unsigned long);
+-
+-extern void do_hwc_interrupt (void);
+-
+-extern int hwc_printk (const char *,...);
+-
+-extern signed int hwc_register_calls (hwc_high_level_calls_t *);
+-
+-extern signed int hwc_unregister_calls (hwc_high_level_calls_t *);
+-
+-extern int hwc_send (hwc_request_t *);
+-
+-#endif
+-
+-#endif
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/hwc_tty.c drivers/s390/char/hwc_tty.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/hwc_tty.c	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/hwc_tty.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1,273 +0,0 @@
 -/*
-- * The CCW commands for the Tape type of command.
+- *  drivers/s390/char/hwc_tty.c
+- *    HWC line mode terminal driver.
+- *
+- *  S390 version
+- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
+- *
+- *  Thanks to Martin Schwidefsky.
 - */
 -
--#define         INVALID_00              0x00    /* Invalid cmd      */
--#define         BACKSPACEBLOCK          0x27    /* Back Space block */
--#define         BACKSPACEFILE           0x2f    /* Back Space file */
--#define         DATA_SEC_ERASE          0x97    /* Data security erase */
--#define         ERASE_GAP               0x17    /* Erase Gap */
--#define         FORSPACEBLOCK           0x37    /* Forward space block */
--#define         FORSPACEFILE            0x3F    /* Forward Space file */
--#define         FORCE_STREAM_CNT        0xEB    /* Forced streaming count #   */
--#define         NOP                     0x03    /* No operation  */
--#define         READ_FORWARD            0x02    /* Read forward */
--#define         REWIND                  0x07    /* Rewind */
--#define         REWIND_UNLOAD           0x0F    /* Rewind and Unload */
--#define         SENSE                   0x04    /* Sense */
--#define         NEW_MODE_SET            0xEB    /* Guess it is Mode set */
--#define         WRITE_CMD               0x01    /* Write */
--#define         WRITETAPEMARK           0x1F    /* Write Tape Mark */
+-#include <linux/config.h>
+-#include <linux/major.h>
+-#include <linux/termios.h>
+-#include <linux/tty.h>
+-#include <linux/tty_driver.h>
+-#include <linux/sched.h>
+-#include <linux/mm.h>
+-#include <linux/devfs_fs_kernel.h>
+-#include <linux/init.h>
 -
--#define         ASSIGN                  0xB7    /* 3420 REJECT,3480 OK  */
--#define         CONTROL_ACCESS          0xE3    /* Set high speed */
--#define         DIAG_MODE_SET           0x0B    /* 3420 NOP, 3480 REJECT*/
--#define         LOAD_DISPLAY            0x9F    /* 3420 REJECT,3480 OK  */
--#define         LOCATE                  0x4F    /* 3420 REJ, 3480 NOP   */
--#define         LOOP_WRITE_TO_READ      0x8B    /* 3480 REJECT        */
--#define         MODE_SET_DB             0xDB    /* 3420 REJECT,3480 OK  */
--#define         MODE_SET_C3             0xC3    /* for 3420                */
--#define         MODE_SET_CB             0xCB    /* for 3420                */
--#define         MODE_SET_D3             0xD3    /* for 3420                */
--#define         READ_BACKWARD           0x0C    /*                      */
--#define         READ_BLOCK_ID           0x22    /* 3420 REJECT,3480 OK  */
--#define         READ_BUFFER             0x12    /* 3420 REJECT,3480 OK  */
--#define         READ_BUFF_LOG           0x24    /* 3420 REJECT,3480 OK  */
--#define         RELEASE                 0xD4    /* 3420 NOP, 3480 REJECT*/
--#define         REQ_TRK_IN_ERROR        0x1B    /* 3420 NOP, 3480 REJECT*/
--#define         RESERVE                 0xF4    /* 3420 NOP, 3480 REJECT*/
--#define         SENSE_GROUP_ID          0x34    /* 3420 REJECT,3480 OK  */
--#define         SENSE_ID                0xE4    /* 3420 REJECT,3480 OK */
--#define         READ_DEV_CHAR           0x64    /* Read device characteristics */
--#define         SET_DIAGNOSE            0x4B    /* 3420 NOP, 3480 REJECT*/
--#define         SET_GROUP_ID            0xAF    /* 3420 REJECT,3480 OK  */
--#define         SET_TAPE_WRITE_IMMED    0xC3    /* for 3480                */
--#define         SUSPEND                 0x5B    /* 3420 REJ, 3480 NOP   */
--#define         SYNC                    0x43    /* Synchronize (flush buffer) */
--#define         UNASSIGN                0xC7    /* 3420 REJECT,3480 OK  */
--#define         PERF_SUBSYS_FUNC        0x77    /* 3490 CMD */
--#define         READ_CONFIG_DATA        0xFA    /* 3490 CMD */
--#define         READ_MESSAGE_ID         0x4E    /* 3490 CMD */
--#define         READ_SUBSYS_DATA        0x3E    /* 3490 CMD */
--#define         SET_INTERFACE_ID        0x73    /* 3490 CMD */
+-#include <asm/uaccess.h>
+-
+-#include "hwc_rw.h"
+-#include "ctrlchar.h"
+-
+-#define HWC_TTY_PRINT_HEADER "hwc tty driver: "
+-
+-#define HWC_TTY_BUF_SIZE 512
+-
+-typedef struct {
+-
+-	struct tty_struct *tty;
+-
+-	unsigned char buf[HWC_TTY_BUF_SIZE];
+-
+-	unsigned short int buf_count;
+-
+-	spinlock_t lock;
+-
+-	hwc_high_level_calls_t calls;
+-} hwc_tty_data_struct;
+-
+-static hwc_tty_data_struct hwc_tty_data =
+-{ /* NULL/0 */ };
+-static struct tty_driver hwc_tty_driver;
+-static struct tty_struct *hwc_tty_table[1];
+-static struct termios *hwc_tty_termios[1];
+-static struct termios *hwc_tty_termios_locked[1];
+-static int hwc_tty_refcount = 0;
+-
+-extern struct termios tty_std_termios;
+-
+-void hwc_tty_wake_up (void);
+-void hwc_tty_input (unsigned char *, unsigned int);
+-
+-static int 
+-hwc_tty_open (struct tty_struct *tty,
+-	      struct file *filp)
+-{
+-
+-	if (MINOR (tty->device) - tty->driver.minor_start)
+-		return -ENODEV;
+-
+-	tty->driver_data = &hwc_tty_data;
+-	hwc_tty_data.buf_count = 0;
+-	hwc_tty_data.tty = tty;
+-	tty->low_latency = 0;
+-
+-	hwc_tty_data.calls.wake_up = hwc_tty_wake_up;
+-	hwc_tty_data.calls.move_input = hwc_tty_input;
+-	hwc_register_calls (&(hwc_tty_data.calls));
+-
+-	return 0;
+-}
+-
+-static void 
+-hwc_tty_close (struct tty_struct *tty,
+-	       struct file *filp)
+-{
+-	if (MINOR (tty->device) != tty->driver.minor_start) {
+-		printk (KERN_WARNING HWC_TTY_PRINT_HEADER
+-			"do not close hwc tty because of wrong device number");
+-		return;
+-	}
+-	if (tty->count > 1)
+-		return;
+-
+-	hwc_tty_data.tty = NULL;
+-
+-	hwc_unregister_calls (&(hwc_tty_data.calls));
+-}
+-
+-static int 
+-hwc_tty_write_room (struct tty_struct *tty)
+-{
+-	int retval;
+-
+-	retval = hwc_write_room (IN_BUFS_TOTAL);
+-	return retval;
+-}
+-
+-static int 
+-hwc_tty_write (struct tty_struct *tty,
+-	       int from_user,
+-	       const unsigned char *buf,
+-	       int count)
+-{
+-	int retval;
+-
+-	if (hwc_tty_data.buf_count > 0) {
+-		hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
+-		hwc_tty_data.buf_count = 0;
+-	}
+-	retval = hwc_write (from_user, buf, count);
+-	return retval;
+-}
+-
+-static void 
+-hwc_tty_put_char (struct tty_struct *tty,
+-		  unsigned char ch)
+-{
+-	unsigned long flags;
+-
+-	spin_lock_irqsave (&hwc_tty_data.lock, flags);
+-	if (hwc_tty_data.buf_count >= HWC_TTY_BUF_SIZE) {
+-		hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
+-		hwc_tty_data.buf_count = 0;
+-	}
+-	hwc_tty_data.buf[hwc_tty_data.buf_count] = ch;
+-	hwc_tty_data.buf_count++;
+-	spin_unlock_irqrestore (&hwc_tty_data.lock, flags);
+-}
 -
--#ifndef MIN
--#define MIN(a,b)                ( (a) < (b) ? (a) : (b) )
--#endif
+-static void 
+-hwc_tty_flush_chars (struct tty_struct *tty)
+-{
+-	unsigned long flags;
 -
+-	spin_lock_irqsave (&hwc_tty_data.lock, flags);
+-	hwc_write (0, hwc_tty_data.buf, hwc_tty_data.buf_count);
+-	hwc_tty_data.buf_count = 0;
+-	spin_unlock_irqrestore (&hwc_tty_data.lock, flags);
+-}
 -
--#define BLOCKSIZE               4096            /* size of the tape rcds */
+-static int 
+-hwc_tty_chars_in_buffer (struct tty_struct *tty)
+-{
+-	int retval;
 -
--#define COMMAND_CHAIN    CCW_FLAG_CC      /* redefine from irq.h */
--#define CHANNEL_END      DEV_STAT_CHN_END /* redefine from irq.h */
--#define DEVICE_END       DEV_STAT_DEV_END /* redefine from irq.h */
--#define UNIT_CHECK       DEV_STAT_UNIT_CHECK  /* redefine from irq.h */
--#define UNIT_EXCEPTION   DEV_STAT_UNIT_EXCEP  /* redefine from irq.h */
--#define CONTROL_UNIT_END DEV_STAT_CU_END      /* redefine from irq.h */
--#define INCORR_LEN       SCHN_STAT_INCORR_LEN /* redefine from irq.h */
+-	retval = hwc_chars_in_buffer (IN_BUFS_TOTAL);
+-	return retval;
+-}
 -
--#define SENSE_COMMAND_REJECT        0x80
--#define SENSE_INTERVENTION_REQUIRED 0x40
--#define SENSE_BUS_OUT_CHECK         0x20
--#define SENSE_EQUIPMENT_CHECK       0x10
--#define SENSE_DATA_CHECK            0x08
--#define SENSE_OVERRUN               0x04
--#define SENSE_DEFERRED_UNIT_CHECK   0x02
--#define SENSE_ASSIGNED_ELSEWHERE    0x01
+-static void 
+-hwc_tty_flush_buffer (struct tty_struct *tty)
+-{
+-	hwc_tty_wake_up ();
+-}
 -
--#define SENSE_LOCATE_FAILURE        0x80
--#define SENSE_DRIVE_ONLINE          0x40
--#define SENSE_RESERVED              0x20
--#define SENSE_RECORD_SEQUENCE_ERR   0x10
--#define SENSE_BEGINNING_OF_TAPE     0x08
--#define SENSE_WRITE_MODE            0x04
--#define SENSE_WRITE_PROTECT         0x02
--#define SENSE_NOT_CAPABLE           0x01
+-static int 
+-hwc_tty_ioctl (
+-		      struct tty_struct *tty,
+-		      struct file *file,
+-		      unsigned int cmd,
+-		      unsigned long arg)
+-{
+-	if (tty->flags & (1 << TTY_IO_ERROR))
+-		return -EIO;
 -
--#define SENSE_CHANNEL_ADAPTER_CODE  0xE0
--#define SENSE_CHANNEL_ADAPTER_LOC   0x10
--#define SENSE_REPORTING_CU          0x08
--#define SENSE_AUTOMATIC_LOADER      0x04
--#define SENSE_TAPE_SYNC_MODE        0x02
--#define SENSE_TAPE_POSITIONING      0x01
+-	return hwc_ioctl (cmd, arg);
+-}
 -
--typedef struct _tape34xx_disc_data_t {
--    __u8 modeset_byte;
--} tape34xx_disc_data_t  __attribute__ ((packed, aligned(8)));
+-void 
+-hwc_tty_wake_up (void)
+-{
+-	if (hwc_tty_data.tty == NULL)
+-		return;
+-	if ((hwc_tty_data.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+-	    hwc_tty_data.tty->ldisc.write_wakeup)
+-		(hwc_tty_data.tty->ldisc.write_wakeup) (hwc_tty_data.tty);
+-	wake_up_interruptible (&hwc_tty_data.tty->write_wait);
+-}
 -
--/* discipline functions */
--int tape34xx_ioctl_overload (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
--ccw_req_t * tape34xx_write_block (const char *data, size_t count, tape_info_t * ti);
--void tape34xx_free_write_block (ccw_req_t * cqr, tape_info_t * ti);
--ccw_req_t * tape34xx_read_block (const char *data, size_t count, tape_info_t * ti);
--void  tape34xx_free_read_block (ccw_req_t * cqr, tape_info_t * ti);
--void  tape34xx_clear_read_block (ccw_req_t * cqr, tape_info_t * ti);
--ccw_req_t * tape34xx_mtfsf (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtbsf (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtfsr (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtbsr (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtweof (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtrew (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtoffl (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtnop (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtbsfm (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtfsfm (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mteom (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mterase (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtsetdensity (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtseek (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mttell (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtsetdrvbuffer (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtlock (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtunlock (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtload (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtunload (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtcompression (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtsetpart (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtmkpart (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtiocget (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_mtiocpos (tape_info_t * ti, int count);
--ccw_req_t * tape34xx_bread (struct request *req, tape_info_t* ti,int tapeblock_major);
--ccw_req_t * tape34xx_bwrite (struct request *req, tape_info_t* ti,int tapeblock_major);
--void tape34xx_free_bread (ccw_req_t*,struct _tape_info_t*);
--void tape34xx_free_bwrite (ccw_req_t*,struct _tape_info_t*);
+-void 
+-hwc_tty_input (unsigned char *buf, unsigned int count)
+-{
+-	struct tty_struct *tty = hwc_tty_data.tty;
 -
--/* Event handlers */
--void tape34xx_default_handler (tape_info_t * ti);
--void tape34xx_unexpect_uchk_handler (tape_info_t * ti);
--void tape34xx_unused_done(tape_info_t* ti);
--void tape34xx_idle_done(tape_info_t* ti);
--void tape34xx_block_done(tape_info_t* ti);
--void tape34xx_bsf_init_done(tape_info_t* ti);
--void tape34xx_dse_init_done(tape_info_t* ti);
--void tape34xx_fsf_init_done(tape_info_t* ti);
--void tape34xx_bsb_init_done(tape_info_t* ti);
--void tape34xx_fsb_init_done(tape_info_t* ti);
--void tape34xx_lbl_init_done(tape_info_t* ti);
--void tape34xx_nop_init_done(tape_info_t* ti);
--void tape34xx_rfo_init_done(tape_info_t* ti);
--void tape34xx_rbi_init_done(tape_info_t* ti);
--void tape34xx_rew_init_done(tape_info_t* ti);
--void tape34xx_rew_release_init_done(tape_info_t* ti);
--void tape34xx_run_init_done(tape_info_t* ti);
--void tape34xx_wri_init_done(tape_info_t* ti);
--void tape34xx_wtm_init_done(tape_info_t* ti);
+-	if (tty != NULL) {
+-		char *cchar;
+-		if ((cchar = ctrlchar_handle (buf, count, tty))) {
+-			if (cchar == (char *) -1)
+-				return;
+-			tty->flip.count++;
+-			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+-			*tty->flip.char_buf_ptr++ = *cchar;
+-		} else {
 -
--extern void schedule_tapeblock_exec_IO (tape_info_t *ti);
+-			memcpy (tty->flip.char_buf_ptr, buf, count);
+-			if (count < 2 || (
+-					 strncmp (buf + count - 2, "^n", 2) ||
+-				    strncmp (buf + count - 2, "\0252n", 2))) {
+-				tty->flip.char_buf_ptr[count] = '\n';
+-				count++;
+-			} else
+-				count -= 2;
+-			memset (tty->flip.flag_buf_ptr, TTY_NORMAL, count);
+-			tty->flip.char_buf_ptr += count;
+-			tty->flip.flag_buf_ptr += count;
+-			tty->flip.count += count;
+-		}
+-		tty_flip_buffer_push (tty);
+-		hwc_tty_wake_up ();
+-	}
+-}
 -
--// the error recovery stuff:
--void tape34xx_error_recovery (tape_info_t* ti);
--void tape34xx_error_recovery_has_failed (tape_info_t* ti,int error_id);
--void tape34xx_error_recovery_succeded(tape_info_t* ti);
--void tape34xx_error_recovery_do_retry(tape_info_t* ti);
--void tape34xx_error_recovery_read_opposite (tape_info_t* ti);
--void  tape34xx_error_recovery_HWBUG (tape_info_t* ti,int condno);
--#endif // _TAPE34XX_H
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3590.c drivers/s390/char/tape3590.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3590.c	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tape3590.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1 +0,0 @@
--// tbd
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3590.h drivers/s390/char/tape3590.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape3590.h	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tape3590.h	2006-01-30 22:25:26.000000000 -0700
-@@ -1 +0,0 @@
--// tbd
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_34xx.c drivers/s390/char/tape_34xx.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_34xx.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/tape_34xx.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,1357 @@
+-void 
+-hwc_tty_init (void)
+-{
+-	if (!CONSOLE_IS_HWC)
+-		return;
+-
+-	ctrlchar_init ();
+-
+-	memset (&hwc_tty_driver, 0, sizeof (struct tty_driver));
+-	memset (&hwc_tty_data, 0, sizeof (hwc_tty_data_struct));
+-	hwc_tty_driver.magic = TTY_DRIVER_MAGIC;
+-	hwc_tty_driver.driver_name = "tty_hwc";
+-	hwc_tty_driver.name = "ttyS";
+-	hwc_tty_driver.name_base = 0;
+-	hwc_tty_driver.major = TTY_MAJOR;
+-	hwc_tty_driver.minor_start = 64;
+-	hwc_tty_driver.num = 1;
+-	hwc_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+-	hwc_tty_driver.subtype = SYSTEM_TYPE_TTY;
+-	hwc_tty_driver.init_termios = tty_std_termios;
+-	hwc_tty_driver.init_termios.c_iflag = IGNBRK | IGNPAR;
+-	hwc_tty_driver.init_termios.c_oflag = ONLCR;
+-	hwc_tty_driver.init_termios.c_lflag = ISIG | ECHO;
+-	hwc_tty_driver.flags = TTY_DRIVER_REAL_RAW;
+-	hwc_tty_driver.refcount = &hwc_tty_refcount;
+-
+-	hwc_tty_driver.table = hwc_tty_table;
+-	hwc_tty_driver.termios = hwc_tty_termios;
+-	hwc_tty_driver.termios_locked = hwc_tty_termios_locked;
+-
+-	hwc_tty_driver.open = hwc_tty_open;
+-	hwc_tty_driver.close = hwc_tty_close;
+-	hwc_tty_driver.write = hwc_tty_write;
+-	hwc_tty_driver.put_char = hwc_tty_put_char;
+-	hwc_tty_driver.flush_chars = hwc_tty_flush_chars;
+-	hwc_tty_driver.write_room = hwc_tty_write_room;
+-	hwc_tty_driver.chars_in_buffer = hwc_tty_chars_in_buffer;
+-	hwc_tty_driver.flush_buffer = hwc_tty_flush_buffer;
+-	hwc_tty_driver.ioctl = hwc_tty_ioctl;
+-
+-	hwc_tty_driver.throttle = NULL;
+-	hwc_tty_driver.unthrottle = NULL;
+-	hwc_tty_driver.send_xchar = NULL;
+-	hwc_tty_driver.set_termios = NULL;
+-	hwc_tty_driver.set_ldisc = NULL;
+-	hwc_tty_driver.stop = NULL;
+-	hwc_tty_driver.start = NULL;
+-	hwc_tty_driver.hangup = NULL;
+-	hwc_tty_driver.break_ctl = NULL;
+-	hwc_tty_driver.wait_until_sent = NULL;
+-	hwc_tty_driver.read_proc = NULL;
+-	hwc_tty_driver.write_proc = NULL;
+-
+-	if (tty_register_driver (&hwc_tty_driver))
+-		panic ("Couldn't register hwc_tty driver\n");
+-}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/Makefile drivers/s390/char/Makefile
+--- kernel-source-2.4.27.orig/drivers/s390/char/Makefile	2001-10-11 10:43:29.000000000 -0600
++++ drivers/s390/char/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -4,31 +4,39 @@
+ 
+ O_TARGET := s390-char.o
+ 
+-list-multi	:= tub3270.o tape390.o
+-export-objs	:= hwc_rw.o
++list-multi	:= 	tub3270.o \
++			tape390.o
++
++export-objs	:=	sclp.o \
++			tape_core.o \
++			tape_devmap.o \
++			tape_std.o
+ 
+ tub3270-objs := tuball.o tubfs.o tubtty.o \
+                      tubttyaid.o tubttybld.o tubttyscl.o \
+                      tubttyrcl.o tubttysiz.o
+ 
+-tape390-$(CONFIG_S390_TAPE_CHAR) += tapechar.o
+-tape390-$(CONFIG_S390_TAPE_BLOCK) += tapeblock.o
+-tape390-$(CONFIG_S390_TAPE_3480) += tape3480.o tape34xx.o
+-tape390-$(CONFIG_S390_TAPE_3490) += tape3490.o tape34xx.o
+-tape390-objs := tape.o $(sort $(tape390-y))
++tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o
++tape-objs := tape_core.o tape_devmap.o tape_proc.o tape_std.o tape_char.o \
++	$(sort $(tape-y))
++obj-$(CONFIG_S390_TAPE) += tape390.o
++obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
+ 
+ obj-y += ctrlchar.o
+ obj-$(CONFIG_TN3215) += con3215.o
+-obj-$(CONFIG_HWC) += hwc_con.o hwc_rw.o hwc_tty.o
+-obj-$(CONFIG_HWC_CPI) += hwc_cpi.o
++obj-$(CONFIG_SCLP) += sclp.o sclp_rw.o
++obj-$(CONFIG_SCLP_TTY) += sclp_tty.o
++obj-$(CONFIG_SCLP_CONSOLE) += sclp_con.o
++obj-$(CONFIG_SCLP_VT220_TTY) += sclp_vt220.o
++obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
+ obj-$(CONFIG_TN3270) += tub3270.o
+-obj-$(CONFIG_S390_TAPE) += tape390.o
++obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o
+ 
+ include $(TOPDIR)/Rules.make
+ 
+ tub3270.o: $(tub3270-objs)
+ 	$(LD) -r -o $@ $(tub3270-objs)
+ 
+-tape390.o: $(tape390-objs)
+-	$(LD) -r -o $@ $(tape390-objs)
++tape390.o:	$(tape-objs)
++	$(LD) -r -o $@ $(tape-objs)
+ 
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/sclp.c drivers/s390/char/sclp.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/sclp.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/sclp.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,854 @@
++/*
++ *  drivers/s390/char/sclp.c
++ *     core function to access sclp interface
++ *
++ *  S390 version
++ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
++ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/kmod.h>
++#include <linux/bootmem.h>
++#include <linux/errno.h>
++#include <linux/ptrace.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/interrupt.h>
++#include <linux/timer.h>
++#include <linux/init.h>
++#include <linux/reboot.h>
++#include <asm/irq.h>
++#include <asm/s390_ext.h>
++#include <asm/processor.h>
++
++#include "sclp.h"
++
++#define SCLP_CORE_PRINT_HEADER "sclp low level driver: "
++
++/* Structure for register_early_external_interrupt. */
++static ext_int_info_t ext_int_info_hwc;
++
++/* spinlock to protect global variables of sclp_core */
++static spinlock_t sclp_lock;
++
++/* Mask of valid sclp events */
++static sccb_mask_t sclp_receive_mask;
++static sccb_mask_t sclp_send_mask;
++
++/* List of registered event types */
++static struct list_head sclp_reg_list;
++
++/* sccb queue */
++static struct list_head sclp_req_queue;
++
++/* sccb for unconditional read */
++static struct sclp_req sclp_read_req;
++static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
++/* sccb for write mask sccb */
++static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
++
++/* Timer for init mask retries. */
++static struct timer_list retry_timer;
++
++/* Timer for busy retries. */
++static struct timer_list sclp_busy_timer;
++
++static volatile unsigned long sclp_status = 0;
++/* some status flags */
++#define SCLP_INIT		0
++#define SCLP_RUNNING		1
++#define SCLP_READING		2
++#define SCLP_SHUTDOWN		3
++
++#define SCLP_INIT_POLL_INTERVAL	1
++#define SCLP_BUSY_POLL_INTERVAL	1
++
++#define SCLP_COMMAND_INITIATED	0
++#define SCLP_BUSY		2
++#define SCLP_NOT_OPERATIONAL	3
++
++/*
++ * assembler instruction for Service Call
++ */
++static int
++__service_call(sclp_cmdw_t command, void *sccb)
++{
++	int cc;
++
++	/*
++	 *  Mnemonic:	SERVC	Rx, Ry	[RRE]
++	 *
++	 *  Rx: SCLP command word
++	 *  Ry: address of SCCB
++	 */
++	__asm__ __volatile__(
++		"   .insn rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */
++		"   ipm	  %0\n"
++		"   srl	  %0,28"
++		: "=&d" (cc)
++		: "d" (command), "a" (__pa(sccb))
++		: "cc", "memory" );
++	/*
++	 * cc == 0:   Service Call succesful initiated
++	 * cc == 2:   SCLP busy, new Service Call not initiated,
++	 *	      new SCCB unchanged
++	 * cc == 3:   SCLP function not operational
++	 */
++	if (cc == SCLP_NOT_OPERATIONAL)
++		return -EIO;
++	if (cc == SCLP_BUSY)
++		return -EBUSY;
++	return 0;
++}
++
++static void
++sclp_start_request(void)
++{
++	struct sclp_req *req;
++	int rc;
++	unsigned long flags;
++
++	spin_lock_irqsave(&sclp_lock, flags);
++	/* quick exit if sclp is already in use */
++	if (test_bit(SCLP_RUNNING, &sclp_status)) {
++		spin_unlock_irqrestore(&sclp_lock, flags);
++		return;
++	}
++	/* Try to start requests from the request queue. */
++	while (!list_empty(&sclp_req_queue)) {
++		req = list_entry(sclp_req_queue.next, struct sclp_req, list);
++		rc = __service_call(req->command, req->sccb);
++		if (rc == 0) {
++			/* Sucessfully started request. */
++			req->status = SCLP_REQ_RUNNING;
++			/* Request active. Set running indication. */
++			set_bit(SCLP_RUNNING, &sclp_status);
++			break;
++		}
++		if (rc == -EBUSY) {
++			/**
++			 * SCLP is busy but no request is running.
++			 * Try again later.
++			 */
++			if (!timer_pending(&sclp_busy_timer) ||
++			    !mod_timer(&sclp_busy_timer,
++				       jiffies + SCLP_BUSY_POLL_INTERVAL*HZ)) {
++				sclp_busy_timer.function =
++					(void *) sclp_start_request;
++				sclp_busy_timer.expires =
++					jiffies + SCLP_BUSY_POLL_INTERVAL*HZ;
++				add_timer(&sclp_busy_timer);
++			}
++			break;
++		}
++		/* Request failed. */
++		req->status = SCLP_REQ_FAILED;
++		list_del(&req->list);
++		if (req->callback) {
++			spin_unlock_irqrestore(&sclp_lock, flags);
++			req->callback(req, req->callback_data);
++			spin_lock_irqsave(&sclp_lock, flags);
++		}
++	}
++	spin_unlock_irqrestore(&sclp_lock, flags);
++}
++
++static int
++sclp_process_evbufs(struct sccb_header *sccb)
++{
++	int result;
++	unsigned long flags;
++	struct evbuf_header *evbuf;
++	struct list_head *l;
++	struct sclp_register *t;
++
++	spin_lock_irqsave(&sclp_lock, flags);
++	evbuf = (struct evbuf_header *) (sccb + 1);
++	result = 0;
++	while ((addr_t) evbuf < (addr_t) sccb + sccb->length) {
++		/* check registered event */
++		t = NULL;
++		list_for_each(l, &sclp_reg_list) {
++			t = list_entry(l, struct sclp_register, list);
++			if (t->receive_mask & (1 << (32 - evbuf->type))) {
++				if (t->receiver_fn != NULL) {
++					spin_unlock_irqrestore(&sclp_lock,
++							       flags);
++					t->receiver_fn(evbuf);
++					spin_lock_irqsave(&sclp_lock, flags);
++				}
++				break;
++			}
++			else
++				t = NULL;
++		}
++		/* Check for unrequested event buffer */
++		if (t == NULL)
++			result = -ENOSYS;
++		evbuf = (struct evbuf_header *)
++				((addr_t) evbuf + evbuf->length);
++	}
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	return result;
++}
++
++char *
++sclp_error_message(u16 rc)
++{
++	static struct {
++		u16 code; char *msg;
++	} sclp_errors[] = {
++		{ 0x0000, "No response code stored (machine malfunction)" },
++		{ 0x0020, "Normal Completion" },
++		{ 0x0040, "SCLP equipment check" },
++		{ 0x0100, "SCCB boundary violation" },
++		{ 0x01f0, "Invalid command" },
++		{ 0x0220, "Normal Completion; suppressed buffers pending" },
++		{ 0x0300, "Insufficient SCCB length" },
++		{ 0x0340, "Contained SCLP equipment check" },
++		{ 0x05f0, "Target resource in improper state" },
++		{ 0x40f0, "Invalid function code/not installed" },
++		{ 0x60f0, "No buffers stored" },
++		{ 0x62f0, "No buffers stored; suppressed buffers pending" },
++		{ 0x70f0, "Invalid selection mask" },
++		{ 0x71f0, "Event buffer exceeds available space" },
++		{ 0x72f0, "Inconsistent lengths" },
++		{ 0x73f0, "Event buffer syntax error" }
++	};
++	int i;
++	for (i = 0; i < sizeof(sclp_errors)/sizeof(sclp_errors[0]); i++)
++		if (rc == sclp_errors[i].code)
++			return sclp_errors[i].msg;
++	return "Invalid response code";
++}
++
 +/*
-+ *  drivers/s390/char/tape_34xx.c
-+ *    tape device discipline for 3480/3490 tapes.
-+ *
-+ *  S390 and zSeries version
-+ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Carsten Otte <cotte at de.ibm.com>
-+ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-+ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ *		 Stefan Bader <shbader at de.ibm.com>
++ * postprocessing of unconditional read service call
 + */
++static void
++sclp_unconditional_read_cb(struct sclp_req *read_req, void *data)
++{
++	struct sccb_header *sccb;
 +
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <asm/tape390.h>
++	sccb = read_req->sccb;
++	if (sccb->response_code == 0x0020 ||
++	    sccb->response_code == 0x0220) {
++		if (sclp_process_evbufs(sccb) != 0)
++			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
++			       "unconditional read: "
++			       "unrequested event buffer received.\n");
++	}
 +
-+#define TAPE_DBF_AREA	tape_34xx_dbf
++	if (sccb->response_code != 0x0020)
++		printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
++		       "unconditional read: %s (response code=0x%x).\n",
++		       sclp_error_message(sccb->response_code),
++		       sccb->response_code);
 +
-+#include "tape.h"
-+#include "tape_std.h"
++	clear_bit(SCLP_READING, &sclp_status);
++}
 +
-+#define PRINTK_HEADER "T34xx:"
++/*
++ * Function to queue Read Event Data/Unconditional Read
++ */
++static void
++__sclp_unconditional_read(void)
++{
++	struct sccb_header *sccb;
++	struct sclp_req *read_req;
++
++	/*
++	 * Don't try to initiate Unconditional Read if we are not able to
++	 * receive anything
++	 */
++	if (sclp_receive_mask == 0)
++		return;
++	/* Don't try reading if a read is already outstanding */
++	if (test_and_set_bit(SCLP_READING, &sclp_status))
++		return;
++	/* Initialize read sccb */
++	sccb = (struct sccb_header *) sclp_read_sccb;
++	clear_page(sccb);
++	sccb->length = PAGE_SIZE;
++	sccb->function_code = 0;	/* unconditional read */
++	sccb->control_mask[2] = 0x80;	/* variable length response */
++	/* Initialize request structure */
++	read_req = &sclp_read_req;
++	read_req->command = SCLP_CMDW_READDATA;
++	read_req->status = SCLP_REQ_QUEUED;
++	read_req->callback = sclp_unconditional_read_cb;
++	read_req->sccb = sccb;
++	/* Add read request to the head of queue */
++	list_add(&read_req->list, &sclp_req_queue);
++}
++
++/* Bit masks to interpret external interruption parameter contents. */
++#define EXT_INT_SCCB_MASK		0xfffffff8
++#define EXT_INT_STATECHANGE_PENDING	0x00000002
++#define EXT_INT_EVBUF_PENDING		0x00000001
 +
 +/*
-+ * Pointer to debug area.
++ * Handler for service-signal external interruptions
 + */
-+debug_info_t *TAPE_DBF_AREA = NULL;
++static void
++sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
++{
++	u32 ext_int_param, finished_sccb, evbuf_pending;
++	struct list_head *l;
++	struct sclp_req *req, *tmp;
++	int cpu;
++
++	spin_lock(&sclp_lock);
++	/*
++	 * Only process interrupt if sclp is initialized.
++	 * This avoids strange effects for a pending request
++	 * from before the last re-ipl.
++	 */
++	if (!test_bit(SCLP_INIT, &sclp_status)) {
++		/* Now clear the running bit */
++		clear_bit(SCLP_RUNNING, &sclp_status);
++		spin_unlock(&sclp_lock);
++		return;
++	}
++	ext_int_param = S390_lowcore.ext_params;
++	finished_sccb = ext_int_param & EXT_INT_SCCB_MASK;
++	evbuf_pending = ext_int_param & (EXT_INT_EVBUF_PENDING |
++					 EXT_INT_STATECHANGE_PENDING);
++	cpu = smp_processor_id();
++	irq_enter(cpu, 0x2401);
++	req = NULL;
++	if (finished_sccb != 0U) {
++		list_for_each(l, &sclp_req_queue) {
++			tmp = list_entry(l, struct sclp_req, list);
++			if (finished_sccb == (u32)(addr_t) tmp->sccb) {
++				list_del(&tmp->list);
++				req = tmp;
++				break;
++			}
++		}
++	}
++	spin_unlock(&sclp_lock);
++	/* Perform callback */
++	if (req != NULL) {
++		req->status = SCLP_REQ_DONE;
++		if (req->callback != NULL)
++			req->callback(req, req->callback_data);
++	}
++	spin_lock(&sclp_lock);
++	/* Head queue a read sccb if an event buffer is pending */
++	if (evbuf_pending)
++		__sclp_unconditional_read();
++	/* Now clear the running bit if SCLP indicated a finished SCCB */
++	if (finished_sccb != 0U)
++		clear_bit(SCLP_RUNNING, &sclp_status);
++	spin_unlock(&sclp_lock);
++	/* and start next request on the queue */
++	sclp_start_request();
++	irq_exit(cpu, 0x2401);
++}
 +
 +/*
-+ * The block ID is the complete marker for a specific tape position.
-+ * It contains a physical part (wrap, segment, format) and a logical
-+ * block number.
++ * Wait synchronously for external interrupt of sclp. We may not receive
++ * any other external interrupt, so we disable all other external interrupts
++ * in control register 0.
 + */
-+#define TBI_FORMAT_3480			0x00
-+#define TBI_FORMAT_3480_2_XF		0x01
-+#define TBI_FORMAT_3480_XF		0x02
-+#define TBI_FORMAT_RESERVED		0x03
++void
++sclp_sync_wait(void)
++{
++	unsigned long psw_mask;
++	unsigned long cr0, cr0_sync;
 +
-+struct tape_34xx_block_id {
-+        unsigned int			tbi_wrap	: 1;
-+        unsigned int			tbi_segment	: 7;
-+        unsigned int			tbi_format	: 2;
-+        unsigned int			tbi_block	: 22;
-+} __attribute__ ((packed));
++	/* Prevent BH from executing. */
++	local_bh_disable();
++	/*
++	 * save cr0
++	 * enable service signal external interruption (cr0.22)
++	 * disable cr0.20-21, cr0.25, cr0.27, cr0.30-31
++	 * don't touch any other bit in cr0
++	 */
++	__ctl_store(cr0, 0, 0);
++	cr0_sync = cr0;
++	cr0_sync |= 0x00000200;
++	cr0_sync &= 0xFFFFF3AC;
++	__ctl_load(cr0_sync, 0, 0);
 +
-+struct sbid_entry {
-+	struct list_head		list;
-+	struct tape_34xx_block_id	bid;
++	/* enable external interruptions (PSW-mask.7) */
++	asm volatile ("STOSM 0(%1),0x01"
++		      : "=m" (psw_mask) : "a" (&psw_mask) : "memory");
++
++	/* wait until ISR signals receipt of interrupt */
++	while (test_bit(SCLP_RUNNING, &sclp_status)) {
++		barrier();
++		cpu_relax();
++	}
++
++	/* disable external interruptions */
++	asm volatile ("SSM 0(%0)"
++		      : : "a" (&psw_mask) : "memory");
++
++	/* restore cr0 */
++	__ctl_load(cr0, 0, 0);
++	__local_bh_enable();
++}
++
++/*
++ * Queue an SCLP request. Request will immediately be processed if queue is
++ * empty.
++ */
++void
++sclp_add_request(struct sclp_req *req)
++{
++	unsigned long flags;
++
++	if (!test_bit(SCLP_INIT, &sclp_status)) {
++		req->status = SCLP_REQ_FAILED;
++		if (req->callback != NULL)
++			req->callback(req, req->callback_data);
++		return;
++	}
++	spin_lock_irqsave(&sclp_lock, flags);
++	/* queue the request */
++	req->status = SCLP_REQ_QUEUED;
++	list_add_tail(&req->list, &sclp_req_queue);
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	/* try to start the first request on the queue */
++	sclp_start_request();
++}
++
++/* state change notification */
++struct sclp_statechangebuf {
++	struct evbuf_header	header;
++	u8		validity_sclp_active_facility_mask : 1;
++	u8		validity_sclp_receive_mask : 1;
++	u8		validity_sclp_send_mask : 1;
++	u8		validity_read_data_function_mask : 1;
++	u16		_zeros : 12;
++	u16		mask_length;
++	u64		sclp_active_facility_mask;
++	sccb_mask_t	sclp_receive_mask;
++	sccb_mask_t	sclp_send_mask;
++	u32		read_data_function_mask;
++} __attribute__((packed));
++
++static inline void
++__sclp_notify_state_change(void)
++{
++	struct list_head *l;
++	struct sclp_register *t;
++	sccb_mask_t receive_mask, send_mask;
++
++	list_for_each(l, &sclp_reg_list) {
++		t = list_entry(l, struct sclp_register, list);
++		receive_mask = t->receive_mask & sclp_receive_mask;
++		send_mask = t->send_mask & sclp_send_mask;
++		if (t->sclp_receive_mask != receive_mask ||
++		    t->sclp_send_mask != send_mask) {
++			t->sclp_receive_mask = receive_mask;
++			t->sclp_send_mask = send_mask;
++			if (t->state_change_fn != NULL)
++				t->state_change_fn(t);
++		}
++	}
++}
++
++static void
++sclp_state_change(struct evbuf_header *evbuf)
++{
++	unsigned long flags;
++	struct sclp_statechangebuf *scbuf;
++
++	spin_lock_irqsave(&sclp_lock, flags);
++	scbuf = (struct sclp_statechangebuf *) evbuf;
++
++	if (scbuf->validity_sclp_receive_mask) {
++		if (scbuf->mask_length != sizeof(sccb_mask_t))
++			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
++			       "state change event with mask length %i\n",
++			       scbuf->mask_length);
++		else
++			/* set new receive mask */
++			sclp_receive_mask = scbuf->sclp_receive_mask;
++	}
++
++	if (scbuf->validity_sclp_send_mask) {
++		if (scbuf->mask_length != sizeof(sccb_mask_t))
++			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
++			       "state change event with mask length %i\n",
++			       scbuf->mask_length);
++		else
++			/* set new send mask */
++			sclp_send_mask = scbuf->sclp_send_mask;
++	}
++
++	__sclp_notify_state_change();
++	spin_unlock_irqrestore(&sclp_lock, flags);
++}
++
++static struct sclp_register sclp_state_change_event = {
++	.receive_mask = EvTyp_StateChange_Mask,
++	.receiver_fn = sclp_state_change
 +};
 +
-+struct tape_34xx_discdata {
-+	/* A list of block id's of the tape segments (for faster seek) */
-+	struct list_head		sbid_list;
++
++/*
++ * SCLP quiesce event handler
++ */
++#ifdef CONFIG_SMP
++static void
++do_load_quiesce_psw(void * __unused)
++{
++	psw_t quiesce_psw;
++	unsigned long status;
++	int i;
++
++	if (smp_processor_id() != 0)
++		signal_processor(smp_processor_id(), sigp_stop);
++	/* Wait for all other cpus to enter stopped state */
++	i = 1;
++	while (i < smp_num_cpus) {
++		switch (signal_processor_ps(&status, 0, i, sigp_sense)) {
++		case sigp_order_code_accepted:
++		case sigp_status_stored:
++			/* Check for stopped and check stop state */
++			if (test_bit(6, &status) || test_bit(4, &status))
++				i++;
++			break;
++		case sigp_busy:
++			break;
++		case sigp_not_operational:
++			i++;
++			break;
++		}
++	}
++	/* Quiesce the last cpu with the special psw */
++	quiesce_psw.mask = _DW_PSW_MASK;
++	quiesce_psw.addr = 0xfff;
++	__load_psw(quiesce_psw);
++}
++
++static void
++do_machine_quiesce(void)
++{
++	smp_call_function(do_load_quiesce_psw, NULL, 0, 0);
++	do_load_quiesce_psw(NULL);
++}
++#else
++static void
++do_machine_quiesce(void)
++{
++	psw_t quiesce_psw;
++
++	quiesce_psw.mask = _DW_PSW_MASK;
++	quiesce_psw.addr = 0xfff;
++	__load_psw(quiesce_psw);
++}
++#endif
++
++extern void ctrl_alt_del(void);
++
++static void
++sclp_quiesce(struct evbuf_header *evbuf)
++{
++	/*
++	 * We got a "shutdown" request.
++	 * Add a call to an appropriate "shutdown" routine here. This
++	 * routine should set all PSWs to 'disabled-wait', 'stopped'
++	 * or 'check-stopped' - except 1 PSW which needs to carry a
++	 * special bit pattern called 'quiesce PSW'.
++	 */
++	_machine_restart = (void *) do_machine_quiesce;
++	_machine_halt = do_machine_quiesce;
++	_machine_power_off = do_machine_quiesce;
++	ctrl_alt_del();
++}
++
++static struct sclp_register sclp_quiesce_event = {
++	.receive_mask = EvTyp_SigQuiesce_Mask,
++	.receiver_fn = sclp_quiesce
++};
++
++/* initialisation of SCLP */
++struct init_sccb {
++	struct sccb_header header;
++	u16 _reserved;
++	u16 mask_length;
++	sccb_mask_t receive_mask;
++	sccb_mask_t send_mask;
++	sccb_mask_t sclp_send_mask;
++	sccb_mask_t sclp_receive_mask;
++} __attribute__((packed));
++
++static void sclp_init_mask_retry(unsigned long);
++
++static int
++sclp_init_mask(void)
++{
++	unsigned long flags;
++	struct init_sccb *sccb;
++	struct sclp_req *req;
++	struct list_head *l;
++	struct sclp_register *t;
++	int rc;
++
++	sccb = (struct init_sccb *) sclp_init_sccb;
++	/* stick the request structure to the end of the init sccb page */
++	req = (struct sclp_req *) ((addr_t) sccb + PAGE_SIZE) - 1;
++
++	/* SCLP setup concerning receiving and sending Event Buffers */
++	req->command = SCLP_CMDW_WRITEMASK;
++	req->status = SCLP_REQ_QUEUED;
++	req->callback = NULL;
++	req->sccb = sccb;
++	/* setup sccb for writemask command */
++	memset(sccb, 0, sizeof(struct init_sccb));
++	sccb->header.length = sizeof(struct init_sccb);
++	sccb->mask_length = sizeof(sccb_mask_t);
++	/* copy in the sccb mask of the registered event types */
++	spin_lock_irqsave(&sclp_lock, flags);
++	if (!test_bit(SCLP_SHUTDOWN, &sclp_status)) {
++		list_for_each(l, &sclp_reg_list) {
++			t = list_entry(l, struct sclp_register, list);
++			sccb->receive_mask |= t->receive_mask;
++			sccb->send_mask |= t->send_mask;
++		}
++	}
++	sccb->sclp_receive_mask = 0;
++	sccb->sclp_send_mask = 0;
++	if (test_bit(SCLP_INIT, &sclp_status)) {
++		/* add request to sclp queue */
++		list_add_tail(&req->list, &sclp_req_queue);
++		spin_unlock_irqrestore(&sclp_lock, flags);
++		/* and start if SCLP is idle */
++		sclp_start_request();
++		/* now wait for completion */
++		while (req->status != SCLP_REQ_DONE &&
++		       req->status != SCLP_REQ_FAILED)
++			sclp_sync_wait();
++		spin_lock_irqsave(&sclp_lock, flags);
++	} else {
++		/*
++		 * Special case for the very first write mask command.
++		 * The interrupt handler is not removing request from
++		 * the request queue and doesn't call callbacks yet
++		 * because there might be an pending old interrupt
++		 * after a Re-IPL. We have to receive and ignore it.
++		 */
++		do {
++			rc = __service_call(req->command, req->sccb);
++			if (rc == 0)
++				set_bit(SCLP_RUNNING, &sclp_status);
++			spin_unlock_irqrestore(&sclp_lock, flags);
++			if (rc == -EIO)
++				return -ENOSYS;
++			sclp_sync_wait();
++			spin_lock_irqsave(&sclp_lock, flags);
++		} while (rc == -EBUSY);
++	}
++	if (sccb->header.response_code != 0x0020) {
++		/* WRITEMASK failed - we cannot rely on receiving a state
++		   change event, so initially, polling is the only alternative
++		   for us to ever become operational. */
++		if (!test_bit(SCLP_SHUTDOWN, &sclp_status) &&
++		    (!timer_pending(&retry_timer) ||
++		     !mod_timer(&retry_timer,
++			       jiffies + SCLP_INIT_POLL_INTERVAL*HZ))) {
++			retry_timer.function = sclp_init_mask_retry;
++			retry_timer.data = 0;
++			retry_timer.expires = jiffies +
++				SCLP_INIT_POLL_INTERVAL*HZ;
++			add_timer(&retry_timer);
++		}
++	} else {
++		sclp_receive_mask = sccb->sclp_receive_mask;
++		sclp_send_mask = sccb->sclp_send_mask;
++		__sclp_notify_state_change();
++	}
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	return 0;
++}
++
++static void
++sclp_init_mask_retry(unsigned long data) 
++{
++	sclp_init_mask();
++}
++
++/* Reboot event handler - reset send and receive mask to prevent pending SCLP
++ * events from interfering with rebooted system. */
++static int
++sclp_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
++{
++	unsigned long flags;
++
++	/* Note: need spinlock to maintain atomicity when accessing global
++         * variables. */
++	spin_lock_irqsave(&sclp_lock, flags);
++	set_bit(SCLP_SHUTDOWN, &sclp_status);
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	sclp_init_mask();
++	return NOTIFY_DONE;
++}
++
++static struct notifier_block sclp_reboot_notifier = {
++	.notifier_call = sclp_reboot_event
 +};
 +
-+/* Internal prototypes */
-+static void tape_34xx_clear_sbid_list(struct tape_device *);
++/*
++ * sclp setup function. Called early (no kmalloc!) from sclp_console_init().
++ */
++static int
++sclp_init(void)
++{
++	int rc;
++
++	if (test_bit(SCLP_INIT, &sclp_status))
++		/* Already initialized. */
++		return 0;
++
++	spin_lock_init(&sclp_lock);
++	INIT_LIST_HEAD(&sclp_req_queue);
++
++	/* init event list */
++	INIT_LIST_HEAD(&sclp_reg_list);
++	list_add(&sclp_state_change_event.list, &sclp_reg_list);
++	list_add(&sclp_quiesce_event.list, &sclp_reg_list);
 +
-+/* 34xx specific functions */
-+static void
-+__tape_34xx_medium_sense_callback(struct tape_request *request, void *data)
-+{
-+	unsigned char *sense = request->cpdata;
++	rc = register_reboot_notifier(&sclp_reboot_notifier);
++	if (rc)
++		return rc;
 +
-+	request->callback = NULL;
++	/*
++	 * request the 0x2401 external interrupt
++	 * The sclp driver is initialized early (before kmalloc works). We
++	 * need to use register_early_external_interrupt.
++	 */
++	if (register_early_external_interrupt(0x2401, sclp_interrupt_handler,
++					      &ext_int_info_hwc) != 0)
++		return -EBUSY;
 +
-+	DBF_EVENT(5, "TO_MSEN[0]: %08x\n", *((unsigned int *) sense));
-+	DBF_EVENT(5, "TO_MSEN[1]: %08x\n", *((unsigned int *) sense+1));
-+	DBF_EVENT(5, "TO_MSEN[2]: %08x\n", *((unsigned int *) sense+2));
-+	DBF_EVENT(5, "TO_MSEN[3]: %08x\n", *((unsigned int *) sense+3));
++	/* enable service-signal external interruptions,
++	 * Control Register 0 bit 22 := 1
++	 * (besides PSW bit 7 must be set to 1 sometimes for external
++	 * interruptions)
++	 */
++	ctl_set_bit(0, 9);
 +
-+	if(sense[0] & SENSE_INTERVENTION_REQUIRED) {
-+		tape_med_state_set(request->device, MS_UNLOADED);
-+	} else {
-+		tape_med_state_set(request->device, MS_LOADED);
++	init_timer(&retry_timer);
++	init_timer(&sclp_busy_timer);
++	/* do the initial write event mask */
++	rc = sclp_init_mask();
++	if (rc == 0) {
++		/* Ok, now everything is setup right. */
++		set_bit(SCLP_INIT, &sclp_status);
++		return 0;
 +	}
 +
-+	if(sense[1] & SENSE_WRITE_PROTECT) {
-+		request->device->tape_generic_status |= GMT_WR_PROT(~0);
-+	} else{
-+		request->device->tape_generic_status &= ~GMT_WR_PROT(~0);
-+	}
++	/* The sclp_init_mask failed. SCLP is broken, unregister and exit. */
++	ctl_clear_bit(0,9);
++	unregister_early_external_interrupt(0x2401, sclp_interrupt_handler,
++					    &ext_int_info_hwc);
 +
-+	tape_put_request(request);
++	return rc;
 +}
 +
-+static int
-+tape_34xx_medium_sense(struct tape_device *device)
++/*
++ * Register the SCLP event listener identified by REG. Return 0 on success.
++ * Some error codes and their meaning:
++ *
++ *  -ENODEV = SCLP interface is not supported on this machine
++ *   -EBUSY = there is already a listener registered for the requested
++ *            event type
++ *     -EIO = SCLP interface is currently not operational
++ */
++int
++sclp_register(struct sclp_register *reg)
 +{
-+	struct tape_request *	request;
-+	int			rc;
++	unsigned long flags;
++	struct list_head *l;
++	struct sclp_register *t;
 +
-+	tape_34xx_clear_sbid_list(device);
++	if (!MACHINE_HAS_SCLP)
++		return -ENODEV;
 +
-+	request = tape_alloc_request(1, 32);
-+	if(IS_ERR(request)) {
-+		DBF_EXCEPTION(6, "MSN fail\n");
-+		return PTR_ERR(request);
++	if (!test_bit(SCLP_INIT, &sclp_status))
++		sclp_init();
++	spin_lock_irqsave(&sclp_lock, flags);
++	/* check already registered event masks for collisions */
++	list_for_each(l, &sclp_reg_list) {
++		t = list_entry(l, struct sclp_register, list);
++		if (t->receive_mask & reg->receive_mask ||
++		    t->send_mask & reg->send_mask) {
++			spin_unlock_irqrestore(&sclp_lock, flags);
++			return -EBUSY;
++		}
 +	}
++	/*
++	 * set present mask to 0 to trigger state change
++	 * callback in sclp_init_mask
++	 */
++	reg->sclp_receive_mask = 0;
++	reg->sclp_send_mask = 0;
++	list_add(&reg->list, &sclp_reg_list);
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	sclp_init_mask();
++	return 0;
++}
 +
-+	request->op = TO_MSEN;
-+	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);
-+	request->callback = __tape_34xx_medium_sense_callback;
-+
-+	rc = tape_do_io_async(device, request);
++/*
++ * Unregister the SCLP event listener identified by REG.
++ */
++void
++sclp_unregister(struct sclp_register *reg)
++{
++	unsigned long flags;
 +
-+	return rc;
++	spin_lock_irqsave(&sclp_lock, flags);
++	list_del(&reg->list);
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	sclp_init_mask();
 +}
 +
-+static void
-+tape_34xx_work_handler(void *data)
++#define	SCLP_EVBUF_PROCESSED	0x80
++
++/*
++ * Traverse array of event buffers contained in SCCB and remove all buffers
++ * with a set "processed" flag. Return the number of unprocessed buffers.
++ */
++int
++sclp_remove_processed(struct sccb_header *sccb)
 +{
-+	struct {
-+		struct tape_device	*device;
-+		enum tape_op		 op;
-+		struct tq_struct	 task;
-+	} *p = data;
++	struct evbuf_header *evbuf;
++	int unprocessed;
++	u16 remaining;
 +
-+	switch(p->op) {
-+		case TO_MSEN:
-+			tape_34xx_medium_sense(p->device);
-+			break;
-+		default:
-+			DBF_EVENT(3, "T34XX: internal error: unknown work\n");
++	evbuf = (struct evbuf_header *) (sccb + 1);
++	unprocessed = 0;
++	remaining = sccb->length - sizeof(struct sccb_header);
++	while (remaining > 0) {
++		remaining -= evbuf->length;
++		if (evbuf->flags & SCLP_EVBUF_PROCESSED) {
++			sccb->length -= evbuf->length;
++			memcpy((void *) evbuf,
++			       (void *) ((addr_t) evbuf + evbuf->length),
++			       remaining);
++		} else {
++			unprocessed++;
++			evbuf = (struct evbuf_header *)
++					((addr_t) evbuf + evbuf->length);
++		}
 +	}
 +
-+	tape_put_device(p->device);
-+	kfree(p);
++	return unprocessed;
 +}
 +
++module_init(sclp_init);
++
++EXPORT_SYMBOL(sclp_add_request);
++EXPORT_SYMBOL(sclp_sync_wait);
++EXPORT_SYMBOL(sclp_register);
++EXPORT_SYMBOL(sclp_unregister);
++EXPORT_SYMBOL(sclp_error_message);
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/sclp_con.c drivers/s390/char/sclp_con.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/sclp_con.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/sclp_con.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,245 @@
 +/*
-+ * This function is currently used to schedule a sense for later execution.
-+ * For example whenever a unsolicited interrupt signals a new tape medium
-+ * and we can't call tape_do_io from that interrupt handler.
++ *  drivers/s390/char/sclp_con.c
++ *    SCLP line mode console driver
++ *
++ *  S390 version
++ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
++ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
 + */
-+static int
-+tape_34xx_schedule_work(struct tape_device *device, enum tape_op op)
-+{
-+	struct {
-+		struct tape_device	*device;
-+		enum tape_op		 op;
-+		struct tq_struct	 task;
-+	} *p;
 +
-+	if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
-+		return -ENOMEM;
-+
-+	memset(p, 0, sizeof(*p));
-+	INIT_LIST_HEAD(&p->task.list);
-+	p->task.routine = tape_34xx_work_handler;
-+	p->task.data    = p;
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kmod.h>
++#include <linux/console.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/sched.h>
++#include <linux/bootmem.h>
 +
-+	p->device = tape_clone_device(device);
-+	p->op     = op;
++#include "sclp.h"
++#include "sclp_rw.h"
 +
-+	schedule_task(&p->task);
++#define SCLP_CON_PRINT_HEADER "sclp console driver: "
 +
-+	return 0;
-+}
++#define sclp_console_major 4		/* TTYAUX_MAJOR */
++#define sclp_console_minor 64
++#define sclp_console_name  "ttyS"
 +
-+/*
-+ * Done Handler is called when dev stat = DEVICE-END (successful operation)
-+ */
-+static int
-+tape_34xx_done(struct tape_device *device, struct tape_request *request)
-+{
-+	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
-+	// FIXME: Maybe only on assign/unassign
-+	TAPE_CLEAR_STATE(device, TAPE_STATUS_BOXED);
++/* Lock to guard over changes to global variables */
++static spinlock_t sclp_con_lock;
++/* List of free pages that can be used for console output buffering */
++static struct list_head sclp_con_pages;
++/* List of full struct sclp_buffer structures ready for output */
++static struct list_head sclp_con_outqueue;
++/* Counter how many buffers are emitted (max 1) and how many */
++/* are on the output queue. */
++static int sclp_con_buffer_count;
++/* Pointer to current console buffer */
++static struct sclp_buffer *sclp_conbuf;
++/* Timer for delayed output of console messages */
++static struct timer_list sclp_con_timer;
 +
-+	return TAPE_IO_SUCCESS;
-+}
++/* Output format for console messages */
++static unsigned short sclp_con_columns;
++static unsigned short sclp_con_width_htab;
 +
-+static inline int
-+tape_34xx_erp_failed(struct tape_device *device,
-+		     struct tape_request *request, int rc)
++static void
++sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
 +{
-+	DBF_EVENT(3, "Error recovery failed for %s\n",
-+		  tape_op_verbose[request->op]);
-+	return rc;
-+}
++	unsigned long flags;
++	struct sclp_buffer *next;
++	void *page;
 +
-+static inline int
-+tape_34xx_erp_succeeded(struct tape_device *device,
-+		       struct tape_request *request)
-+{
-+	DBF_EVENT(3, "Error Recovery successful for %s\n",
-+		  tape_op_verbose[request->op]);
-+	return tape_34xx_done(device, request);
++	/* Ignore return code - because console-writes aren't critical,
++	   we do without a sophisticated error recovery mechanism.  */
++	page = sclp_unmake_buffer(buffer);
++	spin_lock_irqsave(&sclp_con_lock, flags);
++	/* Remove buffer from outqueue */
++	list_del(&buffer->list);
++	sclp_con_buffer_count--;
++	list_add_tail((struct list_head *) page, &sclp_con_pages);
++	/* Check if there is a pending buffer on the out queue. */
++	next = NULL;
++	if (!list_empty(&sclp_con_outqueue))
++		next = list_entry(sclp_con_outqueue.next,
++				  struct sclp_buffer, list);
++	spin_unlock_irqrestore(&sclp_con_lock, flags);
++	if (next != NULL)
++		sclp_emit_buffer(next, sclp_conbuf_callback);
 +}
 +
-+static inline int
-+tape_34xx_erp_retry(struct tape_device *device, struct tape_request *request)
++static inline void
++sclp_conbuf_emit(void)
 +{
-+	DBF_EVENT(3, "xerp retr %s\n",
-+		  tape_op_verbose[request->op]);
-+	return TAPE_IO_RETRY;
++	struct sclp_buffer* buffer;
++	unsigned long flags;
++	int count;
++
++	spin_lock_irqsave(&sclp_con_lock, flags);
++	buffer = sclp_conbuf;
++	sclp_conbuf = NULL;
++	if (buffer == NULL) {
++		spin_unlock_irqrestore(&sclp_con_lock, flags);
++		return;
++	}
++	list_add_tail(&buffer->list, &sclp_con_outqueue);
++	count = sclp_con_buffer_count++;
++	spin_unlock_irqrestore(&sclp_con_lock, flags);
++	if (count == 0)
++		sclp_emit_buffer(buffer, sclp_conbuf_callback);
 +}
 +
 +/*
-+ * This function is called, when no request is outstanding and we get an
-+ * interrupt
++ * When this routine is called from the timer then we flush the
++ * temporary write buffer without further waiting on a final new line.
 + */
-+static int
-+tape_34xx_unsolicited_irq(struct tape_device *device)
++static void
++sclp_console_timeout(unsigned long data)
 +{
-+	if (device->devstat.dstat == 0x85 /* READY */) {
-+		/* A medium was inserted in the drive. */
-+		DBF_EVENT(6, "T34xx: tape load\n");
-+		tape_34xx_schedule_work(device, TO_MSEN);
-+	} else {
-+		DBF_EVENT(3, "T34xx: unsol.irq! dev end: %x\n",
-+			  device->devinfo.irq);
-+		PRINT_WARN("Unsolicited IRQ (Device End) caught.\n");
-+		tape_dump_sense(device, NULL);
-+	}
-+	return TAPE_IO_SUCCESS;
++	sclp_conbuf_emit();
 +}
 +
 +/*
-+ * Read Opposite Error Recovery Function:
-+ * Used, when Read Forward does not work
++ * Writes the given message to S390 system console
 + */
-+static int
-+tape_34xx_erp_read_opposite(struct tape_device *device,
-+			    struct tape_request *request)
++static void
++sclp_console_write(struct console *console, const char *message,
++		   unsigned int count)
 +{
-+	if (request->op == TO_RFO) {
++	unsigned long flags;
++	void *page;
++	int written;
++
++	if (count == 0)
++		return;
++	spin_lock_irqsave(&sclp_con_lock, flags);
++	/*
++	 * process escape characters, write message into buffer,
++	 * send buffer to SCLP
++	 */
++	do {
++		/* make sure we have a console output buffer */
++		if (sclp_conbuf == NULL) {
++			while (list_empty(&sclp_con_pages)) {
++				spin_unlock_irqrestore(&sclp_con_lock, flags);
++				sclp_sync_wait();
++				spin_lock_irqsave(&sclp_con_lock, flags);
++			}
++			page = sclp_con_pages.next;
++			list_del((struct list_head *) page);
++			sclp_conbuf = sclp_make_buffer(page, sclp_con_columns,
++						       sclp_con_width_htab);
++		}
++		/* try to write the string to the current output buffer */
++		written = sclp_write(sclp_conbuf, (const unsigned char *)
++				     message, count, 0);
++		if (written == -EFAULT || written == count)
++			break;
 +		/*
-+		 * We did read forward, but the data could not be read
-+		 * *correctly*. We transform the request to a read backward
-+		 * and try again.
++		 * Not all characters could be written to the current
++		 * output buffer. Emit the buffer, create a new buffer
++		 * and then output the rest of the string.
 +		 */
-+		tape_std_read_backward(device, request);
-+		return tape_34xx_erp_retry(device, request);
++		spin_unlock_irqrestore(&sclp_con_lock, flags);
++		sclp_conbuf_emit();
++		spin_lock_irqsave(&sclp_con_lock, flags);
++		message += written;
++		count -= written;
++	} while (count > 0);
++	/* Setup timer to output current console buffer after 1/10 second */
++	if (sclp_conbuf != NULL && sclp_chars_in_buffer(sclp_conbuf) != 0 &&
++	    !timer_pending(&sclp_con_timer)) {
++		init_timer(&sclp_con_timer);
++		sclp_con_timer.function = sclp_console_timeout;
++		sclp_con_timer.data = 0UL;
++		sclp_con_timer.expires = jiffies + HZ/10;
++		add_timer(&sclp_con_timer);
 +	}
-+	if (request->op != TO_RBA)
-+		PRINT_ERR("read_opposite called with state:%s\n",
-+			  tape_op_verbose[request->op]);
-+	/*
-+	 * We tried to read forward and backward, but hat no
-+	 * success -> failed.
-+	 */
-+	return tape_34xx_erp_failed(device, request, -EIO);
++	spin_unlock_irqrestore(&sclp_con_lock, flags);
 +}
 +
-+static int
-+tape_34xx_erp_bug(struct tape_device *device,
-+		  struct tape_request *request, int no)
++/* returns the device number of the SCLP console */
++static kdev_t
++sclp_console_device(struct console *c)
 +{
-+	if (request->op != TO_ASSIGN) {
-+		PRINT_WARN("An unexpected condition #%d was caught in "
-+			   "tape error recovery.\n", no);
-+		PRINT_WARN("Please report this incident.\n");
-+		if (request)
-+			PRINT_WARN("Operation of tape:%s\n",
-+				   tape_op_verbose[request->op]);
-+		tape_dump_sense(device, request);
-+	}
-+	return tape_34xx_erp_failed(device, request, -EIO);
++	return	mk_kdev(sclp_console_major, sclp_console_minor);
 +}
 +
 +/*
-+ * Handle data overrun between cu and drive. The channel speed might
-+ * be too slow.
++ * This routine is called from panic when the kernel
++ * is going to give up. We have to make sure that all buffers
++ * will be flushed to the SCLP.
 + */
-+static int
-+tape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request)
++static void
++sclp_console_unblank(void)
 +{
-+	if (device->devstat.ii.sense.data[3] == 0x40) {
-+		PRINT_WARN ("Data overrun error between control-unit "
-+			    "and drive. Use a faster channel connection, "
-+			    "if possible! \n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
++	unsigned long flags;
++
++	sclp_conbuf_emit();
++	spin_lock_irqsave(&sclp_con_lock, flags);
++	if (timer_pending(&sclp_con_timer))
++		del_timer(&sclp_con_timer);
++	while (sclp_con_buffer_count > 0) {
++		spin_unlock_irqrestore(&sclp_con_lock, flags);
++		sclp_sync_wait();
++		spin_lock_irqsave(&sclp_con_lock, flags);
 +	}
-+	return tape_34xx_erp_bug(device, request, -1);
++	spin_unlock_irqrestore(&sclp_con_lock, flags);
 +}
-+	
++
 +/*
-+ * Handle record sequence error.
++ * used to register the SCLP console to the kernel and to
++ * give printk necessary information
 + */
-+static int
-+tape_34xx_erp_sequence(struct tape_device *device,
-+		       struct tape_request *request)
++static struct console sclp_console =
 +{
-+	if (device->devstat.ii.sense.data[3] == 0x41) {
-+		/*
-+		 * cu detected incorrect block-id sequence on tape.
-+		 */
-+		PRINT_WARN("Illegal block-id sequence found!\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	}
-+	/*
-+	 * Record sequence error bit is set, but erpa does not
-+	 * show record sequence error.
-+	 */
-+	return tape_34xx_erp_bug(device, request, -2);
-+}
++	.name = sclp_console_name,
++	.write = sclp_console_write,
++	.device = sclp_console_device,
++	.unblank = sclp_console_unblank,
++	.flags = CON_PRINTBUFFER,
++	.index = 0 /* ttyS0 */
++};
 +
 +/*
-+ * This function analyses the tape's sense-data in case of a unit-check.
-+ * If possible, it tries to recover from the error. Else the user is
-+ * informed about the problem.
++ * called by console_init() in drivers/char/tty_io.c at boot-time.
 + */
-+static int
-+tape_34xx_unit_check(struct tape_device *device, struct tape_request *request)
++void __init
++sclp_console_init(void)
 +{
-+	int inhibit_cu_recovery;
-+	__u8* sense;
-+
-+	inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0;
-+	sense = device->devstat.ii.sense.data;
-+
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+	if (request->op == TO_BLOCK) {
-+		/*
-+		 * Recovery for block device requests. Set the block_position
-+		 * to something invalid and retry.
-+		 */
-+		device->blk_data.block_position = -1;
-+		if (request->retries-- <= 0)
-+			return tape_34xx_erp_failed(device, request, -EIO);
-+		else
-+			return tape_34xx_erp_retry(device, request);
-+	}
-+#endif
++	void *page;
++	int i;
 +
-+	if (
-+		sense[0] & SENSE_COMMAND_REJECT &&
-+		sense[1] & SENSE_WRITE_PROTECT
-+	) {
-+		if (
-+			request->op == TO_DSE ||
-+			request->op == TO_WRI ||
-+			request->op == TO_WTM
-+		) {
-+			/* medium is write protected */
-+			return tape_34xx_erp_failed(device, request, -EACCES);
-+		} else {
-+			return tape_34xx_erp_bug(device, request, -3);
-+		}
++	if (!CONSOLE_IS_SCLP)
++		return;
++	if (sclp_rw_init() != 0)
++		return;
++	/* Allocate pages for output buffering */
++	INIT_LIST_HEAD(&sclp_con_pages);
++	for (i = 0; i < MAX_CONSOLE_PAGES; i++) {
++		page = alloc_bootmem_low_pages(PAGE_SIZE);
++		if (page == NULL)
++			return;
++		list_add_tail((struct list_head *) page, &sclp_con_pages);
 +	}
++	INIT_LIST_HEAD(&sclp_con_outqueue);
++	spin_lock_init(&sclp_con_lock);
++	sclp_con_buffer_count = 0;
++	sclp_conbuf = NULL;
++	init_timer(&sclp_con_timer);
 +
-+	/*
-+	 * special cases for various tape-states when reaching
-+	 * end of recorded area
-+	 */
-+	/*
-+	 * FIXME: Maybe a special case of the special case:
-+	 *        sense[0] == SENSE_EQUIPMENT_CHECK &&
-+	 *        sense[1] == SENSE_DRIVE_ONLINE    &&
-+	 *        sense[3] == 0x47 (Volume Fenced)
-+	 *
-+	 *        This was caused by continued FSF or FSR after an
-+	 *        'End Of Data'.
-+	 */
-+	if ((
-+		sense[0] == SENSE_DATA_CHECK      ||
-+		sense[0] == SENSE_EQUIPMENT_CHECK ||
-+		sense[0] == SENSE_EQUIPMENT_CHECK + SENSE_DEFERRED_UNIT_CHECK
-+	) && (
-+		sense[1] == SENSE_DRIVE_ONLINE ||
-+		sense[1] == SENSE_BEGINNING_OF_TAPE + SENSE_WRITE_MODE
-+	)) {
-+		switch (request->op) {
-+		/*
-+		 * sense[0] == SENSE_DATA_CHECK   &&
-+		 * sense[1] == SENSE_DRIVE_ONLINE
-+		 * sense[3] == 0x36 (End Of Data)
-+		 *
-+		 * Further seeks might return a 'Volume Fenced'.
-+		 */
-+		case TO_FSF:
-+		case TO_FSB:
-+			/* Trying to seek beyond end of recorded area */
-+			return tape_34xx_erp_failed(device, request, -ENOSPC);
-+		case TO_BSB:
-+			return tape_34xx_erp_retry(device, request);
++	/* Set output format */
++	if (MACHINE_IS_VM)
 +		/*
-+		 * sense[0] == SENSE_DATA_CHECK   &&
-+		 * sense[1] == SENSE_DRIVE_ONLINE &&
-+		 * sense[3] == 0x36 (End Of Data)
++		 * save 4 characters for the CPU number
++		 * written at start of each line by VM/CP
 +		 */
-+		case TO_LBL:
-+			/* Block could not be located. */
-+			return tape_34xx_erp_failed(device, request, -EIO);
-+		case TO_RFO:
-+			/* Read beyond end of recorded area -> 0 bytes read */
-+			return tape_34xx_erp_failed(device, request, 0);
-+		default:
-+			PRINT_ERR("Invalid op %s in %s:%i\n",
-+				tape_op_verbose[request->op],
-+				__FUNCTION__, __LINE__);
-+			return tape_34xx_erp_failed(device, request, 0);
-+		}
-+	}
++		sclp_con_columns = 76;
++	else
++		sclp_con_columns = 80;
++	sclp_con_width_htab = 8;
 +
-+	/* Sensing special bits */
-+	if (sense[0] & SENSE_BUS_OUT_CHECK)
-+		return tape_34xx_erp_retry(device, request);
++	/* enable printk-access to this driver */
++	register_console(&sclp_console);
++}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/sclp_cpi.c drivers/s390/char/sclp_cpi.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/sclp_cpi.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/sclp_cpi.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,244 @@
++/*
++ * Author: Martin Peschke <mpeschke at de.ibm.com>
++ * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation
++ *
++ * SCLP Control-Program Identification.
++ */
 +
-+	if (sense[0] & SENSE_DATA_CHECK) {
-+		/*
-+		 * hardware failure, damaged tape or improper
-+		 * operating conditions
-+		 */
-+		switch (sense[3]) {
-+		case 0x23:
-+			/* a read data check occurred */
-+			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
-+			    inhibit_cu_recovery)
-+				// data check is not permanent, may be
-+				// recovered. We always use async-mode with
-+				// cu-recovery, so this should *never* happen.
-+				return tape_34xx_erp_bug(device, request, -4);
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/string.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <asm/ebcdic.h>
++#include <asm/semaphore.h>
 +
-+			/* data check is permanent, CU recovery has failed */
-+			PRINT_WARN("Permanent read error\n");
-+			return tape_34xx_erp_failed(device, request, -EIO);
-+		case 0x25:
-+			// a write data check occurred
-+			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
-+			    inhibit_cu_recovery)
-+				// data check is not permanent, may be
-+				// recovered. We always use async-mode with
-+				// cu-recovery, so this should *never* happen.
-+				return tape_34xx_erp_bug(device, request, -5);
++#include "sclp.h"
++#include "sclp_rw.h"
 +
-+			// data check is permanent, cu-recovery has failed
-+			PRINT_WARN("Permanent write error\n");
-+			return tape_34xx_erp_failed(device, request, -EIO);
-+		case 0x26:
-+			/* Data Check (read opposite) occurred. */
-+			return tape_34xx_erp_read_opposite(device, request);
-+		case 0x28:
-+			/* ID-Mark at tape start couldn't be written */
-+			PRINT_WARN("ID-Mark could not be written.\n");
-+			return tape_34xx_erp_failed(device, request, -EIO);
-+		case 0x31:
-+			/* Tape void. Tried to read beyond end of device. */
-+			PRINT_WARN("Read beyond end of recorded area.\n");
-+			return tape_34xx_erp_failed(device, request, -ENOSPC);
-+		case 0x41:
-+			/* Record sequence error. */
-+			PRINT_WARN("Invalid block-id sequence found.\n");
-+			return tape_34xx_erp_failed(device, request, -EIO);
-+		default:
-+			/* all data checks for 3480 should result in one of
-+			 * the above erpa-codes. For 3490, other data-check
-+			 * conditions do exist. */
-+			if (device->discipline->cu_type == 0x3480)
-+				return tape_34xx_erp_bug(device, request, -6);
-+		}
-+	}
++#define CPI_LENGTH_SYSTEM_TYPE	8
++#define CPI_LENGTH_SYSTEM_NAME	8
++#define CPI_LENGTH_SYSPLEX_NAME	8
 +
-+	if (sense[0] & SENSE_OVERRUN)
-+		return tape_34xx_erp_overrun(device, request);
-+	
-+	if (sense[1] & SENSE_RECORD_SEQUENCE_ERR)
-+		return tape_34xx_erp_sequence(device, request);
++struct cpi_evbuf {
++	struct evbuf_header header;
++	u8	id_format;
++	u8	reserved0;
++	u8	system_type[CPI_LENGTH_SYSTEM_TYPE];
++	u64	reserved1;
++	u8	system_name[CPI_LENGTH_SYSTEM_NAME];
++	u64	reserved2;
++	u64	system_level;
++	u64	reserved3;
++	u8	sysplex_name[CPI_LENGTH_SYSPLEX_NAME];
++	u8	reserved4[16];
++} __attribute__((packed));
 +
-+	/* Sensing erpa codes */
-+	switch (sense[3]) {
-+	case 0x00:
-+		/* Unit check with erpa code 0. Report and ignore. */
-+		PRINT_WARN("Non-error sense was found. "
-+			   "Unit-check will be ignored.\n");
-+		return TAPE_IO_SUCCESS;
-+	case 0x21:
-+		/*
-+		 * Data streaming not operational. CU will switch to
-+		 * interlock mode. Reissue the command.
-+		 */
-+		PRINT_WARN("Data streaming not operational. "
-+			   "Switching to interlock-mode.\n");
-+		return tape_34xx_erp_retry(device, request);
-+	case 0x22:
-+		/*
-+		 * Path equipment check. Might be drive adapter error, buffer
-+		 * error on the lower interface, internal path not usable,
-+		 * or error during cartridge load.
-+		 */
-+		PRINT_WARN("A path equipment check occurred. One of the "
-+			   "following conditions occurred:\n");
-+		PRINT_WARN("drive adapter error, buffer error on the lower "
-+			   "interface, internal path not usable, error "
-+			   "during cartridge load.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x24:
-+		/*
-+		 * Load display check. Load display was command was issued,
-+		 * but the drive is displaying a drive check message. Can
-+		 * be threated as "device end".
-+		 */
-+		return tape_34xx_erp_succeeded(device, request);
-+	case 0x27:
-+		/*
-+		 * Command reject. May indicate illegal channel program or
-+		 * buffer over/underrun. Since all channel programs are
-+		 * issued by this driver and ought be correct, we assume a
-+		 * over/underrun situation and retry the channel program.
-+		 */
-+		return tape_34xx_erp_retry(device, request);
-+	case 0x29:
-+		/*
-+		 * Function incompatible. Either the tape is idrc compressed
-+		 * but the hardware isn't capable to do idrc, or a perform
-+		 * subsystem func is issued and the CU is not on-line.
-+		 */
-+		PRINT_WARN ("Function incompatible. Try to switch off idrc\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x2a:
-+		/*
-+		 * Unsolicited environmental data. An internal counter
-+		 * overflows, we can ignore this and reissue the cmd.
-+		 */
-+		return tape_34xx_erp_retry(device, request);
-+	case 0x2b:
-+		/*
-+		 * Environmental data present. Indicates either unload
-+		 * completed ok or read buffered log command completed ok.
-+		 */
-+		if (request->op == TO_RUN) {
-+			tape_med_state_set(device, MS_UNLOADED);
-+			/* Rewind unload completed ok. */
-+			return tape_34xx_erp_succeeded(device, request);
-+		}
-+		/* tape_34xx doesn't use read buffered log commands. */
-+		return tape_34xx_erp_bug(device, request, sense[3]);
-+	case 0x2c:
-+		/*
-+		 * Permanent equipment check. CU has tried recovery, but
-+		 * did not succeed.
-+		 */
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x2d:
-+		/* Data security erase failure. */
-+		if (request->op == TO_DSE)
-+			return tape_34xx_erp_failed(device, request, -EIO);
-+		/* Data security erase failure, but no such command issued. */
-+		return tape_34xx_erp_bug(device, request, sense[3]);
-+	case 0x2e:
-+		/*
-+		 * Not capable. This indicates either that the drive fails
-+		 * reading the format id mark or that that format specified
-+		 * is not supported by the drive.
-+		 */
-+		PRINT_WARN("Drive not capable processing the tape format!");
-+		return tape_34xx_erp_failed(device, request, -EMEDIUMTYPE);
-+	case 0x30:
-+		/* The medium is write protected. */
-+		PRINT_WARN("Medium is write protected!\n");
-+		return tape_34xx_erp_failed(device, request, -EACCES);
-+	case 0x32:
-+		// Tension loss. We cannot recover this, it's an I/O error.
-+		PRINT_WARN("The drive lost tape tension.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x33:
-+		/*
-+		 * Load Failure. The cartridge was not inserted correctly or
-+		 * the tape is not threaded correctly.
-+		 */
-+		PRINT_WARN("Cartridge load failure. Reload the cartridge "
-+			   "and try again.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x34:
-+		/*
-+		 * Unload failure. The drive cannot maintain tape tension
-+		 * and control tape movement during an unload operation.
-+		 */
-+		PRINT_WARN("Failure during cartridge unload. "
-+			   "Please try manually.\n");
-+		if (request->op == TO_RUN)
-+			return tape_34xx_erp_failed(device, request, -EIO);
-+		return tape_34xx_erp_bug(device, request, sense[3]);
-+	case 0x35:
-+		/*
-+		 * Drive equipment check. One of the following:
-+		 * - cu cannot recover from a drive detected error
-+		 * - a check code message is shown on drive display
-+		 * - the cartridge loader does not respond correctly
-+		 * - a failure occurs during an index, load, or unload cycle
-+		 */
-+		PRINT_WARN("Equipment check! Please check the drive and "
-+			   "the cartridge loader.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x36:
-+		if (device->discipline->cu_type == 0x3490)
-+			/* End of data. */
-+			return tape_34xx_erp_failed(device, request, -EIO);
-+		/* This erpa is reserved for 3480 */
-+		return tape_34xx_erp_bug(device,request,sense[3]);
-+	case 0x37:
-+		/*
-+		 * Tape length error. The tape is shorter than reported in
-+		 * the beginning-of-tape data.
-+		 */
-+		PRINT_WARN("Tape length error.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x38:
-+		/*
-+		 * Physical end of tape. A read/write operation reached
-+		 * the physical end of tape.
-+		 */
-+		if (request->op==TO_WRI ||
-+		    request->op==TO_DSE ||
-+		    request->op==TO_WTM)
-+			return tape_34xx_erp_failed(device, request, -ENOSPC);
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x39:
-+		/* Backward at Beginning of tape. */
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x3a:
-+		/* Drive switched to not ready. */
-+		PRINT_WARN("Drive not ready. Turn the ready/not ready switch "
-+			   "to ready position and try again.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x3b:
-+		/* Manual rewind or unload. This causes an I/O error. */
-+		PRINT_WARN("Medium was rewound or unloaded manually.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x42:
-+		/*
-+		 * Degraded mode. A condition that can cause degraded
-+		 * performance is detected.
-+		 */
-+		PRINT_WARN("Subsystem is running in degraded mode.\n");
-+		return tape_34xx_erp_retry(device, request);
-+	case 0x43:
-+		/* Drive not ready. */
-+		tape_med_state_set(device, MS_UNLOADED);
-+		/* SMB: some commands do not need a tape inserted */
-+		if((sense[1] & SENSE_DRIVE_ONLINE)) {
-+			switch(request->op) {
-+				case TO_ASSIGN:
-+				case TO_UNASSIGN:
-+				case TO_DIS:
-+				case TO_NOP:
-+					return tape_34xx_done(device, request);
-+					break;
-+				default:
-+					break;
-+			}
-+		}
-+		PRINT_WARN("The drive is not ready.\n");
-+		return tape_34xx_erp_failed(device, request, -ENOMEDIUM);
-+	case 0x44:
-+		/* Locate Block unsuccessful. */
-+		if (request->op != TO_BLOCK && request->op != TO_LBL)
-+			/* No locate block was issued. */
-+			return tape_34xx_erp_bug(device, request, sense[3]);
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x45:
-+		/* The drive is assigned to a different channel path. */
-+		PRINT_WARN("The drive is assigned elsewhere.\n");
-+		TAPE_SET_STATE(device, TAPE_STATUS_BOXED);
-+		return tape_34xx_erp_failed(device, request, -EPERM);
-+	case 0x46:
-+		/*
-+		 * Drive not on-line. Drive may be switched offline,
-+		 * the power supply may be switched off or
-+		 * the drive address may not be set correctly.
-+		 */
-+		PRINT_WARN("The drive is not on-line.");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x47:
-+		/* Volume fenced. CU reports volume integrity is lost. */
-+		PRINT_WARN("Volume fenced. The volume integrity is lost because\n");
-+		PRINT_WARN("assignment or tape position was lost.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x48:
-+		/* Log sense data and retry request. */
-+		return tape_34xx_erp_retry(device, request);
-+	case 0x49:
-+		/* Bus out check. A parity check error on the bus was found. */
-+		PRINT_WARN("Bus out check. A data transfer over the bus "
-+			   "has been corrupted.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x4a:
-+		/* Control unit erp failed. */
-+		PRINT_WARN("The control unit I/O error recovery failed.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x4b:
-+		/*
-+		 * CU and drive incompatible. The drive requests micro-program
-+		 * patches, which are not available on the CU.
-+		 */
-+		PRINT_WARN("The drive needs microprogram patches from the "
-+			   "control unit, which are not available.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x4c:
-+		/*
-+		 * Recovered Check-One failure. Cu develops a hardware error,
-+		 * but is able to recover.
-+		 */
-+		return tape_34xx_erp_retry(device, request);
-+	case 0x4d:
-+		if (device->discipline->cu_type == 0x3490)
-+			/*
-+			 * Resetting event received. Since the driver does
-+			 * not support resetting event recovery (which has to
-+			 * be handled by the I/O Layer), retry our command.
-+			 */
-+			return tape_34xx_erp_retry(device, request);
-+		/* This erpa is reserved for 3480. */
-+		return tape_34xx_erp_bug(device, request, sense[3]);
-+	case 0x4e:
-+		if (device->discipline->cu_type == 0x3490) {
-+			/*
-+			 * Maximum block size exceeded. This indicates, that
-+			 * the block to be written is larger than allowed for
-+			 * buffered mode.
-+			 */
-+			PRINT_WARN("Maximum block size for buffered "
-+				   "mode exceeded.\n");
-+			return tape_34xx_erp_failed(device, request, -ENOBUFS);
-+		}
-+		/* This erpa is reserved for 3480. */
-+		return tape_34xx_erp_bug(device, request, sense[3]);
-+	case 0x50:
-+		/*
-+		 * Read buffered log (Overflow). CU is running in extended
-+		 * buffered log mode, and a counter overflows. This should
-+		 * never happen, since we're never running in extended
-+		 * buffered log mode.
-+		 */
-+		return tape_34xx_erp_retry(device, request);
-+	case 0x51:
-+		/*
-+		 * Read buffered log (EOV). EOF processing occurs while the
-+		 * CU is in extended buffered log mode. This should never
-+		 * happen, since we're never running in extended buffered
-+		 * log mode.
-+		 */
-+		return tape_34xx_erp_retry(device, request);
-+	case 0x52:
-+		/* End of Volume complete. Rewind unload completed ok. */
-+		if (request->op == TO_RUN) {
-+			/* SMB */
-+			tape_med_state_set(device, MS_UNLOADED);
-+			return tape_34xx_erp_succeeded(device, request);
-+		}
-+		return tape_34xx_erp_bug(device, request, sense[3]);
-+	case 0x53:
-+		/* Global command intercept. */
-+		return tape_34xx_erp_retry(device, request);
-+	case 0x54:
-+		/* Channel interface recovery (temporary). */
-+		return tape_34xx_erp_retry(device, request);
-+	case 0x55:
-+		/* Channel interface recovery (permanent). */
-+		PRINT_WARN("A permanent channel interface error occurred.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x56:
-+		/* Channel protocol error. */
-+		PRINT_WARN("A channel protocol error occurred.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x57:
-+		if (device->discipline->cu_type == 0x3480) {
-+			/* Attention intercept. */
-+			PRINT_WARN("An attention intercept occurred, "
-+				   "which will be recovered.\n");
-+			return tape_34xx_erp_retry(device, request);
-+		} else {
-+			/* Global status intercept. */
-+			PRINT_WARN("An global status intercept was received, "
-+				   "which will be recovered.\n");
-+			return tape_34xx_erp_retry(device, request);
-+		}
-+	case 0x5a:
-+		/*
-+		 * Tape length incompatible. The tape inserted is too long,
-+		 * which could cause damage to the tape or the drive.
-+		 */
-+		PRINT_WARN("Tape length incompatible [should be IBM Cartridge "
-+			   "System Tape]. May cause damage to drive or tape.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x5b:
-+		/* Format 3480 XF incompatible */
-+		if (sense[1] & SENSE_BEGINNING_OF_TAPE)
-+			/* The tape will get overwritten. */
-+			return tape_34xx_erp_retry(device, request);
-+		PRINT_WARN("Tape format is incompatible to the drive, "
-+			   "which writes 3480-2 XF.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x5c:
-+		/* Format 3480-2 XF incompatible */
-+		PRINT_WARN("Tape format is incompatible to the drive. "
-+			   "The drive cannot access 3480-2 XF volumes.\n");
-+		return tape_34xx_erp_failed(device, request, -EIO);
-+	case 0x5d:
-+		/* Tape length violation. */
-+		PRINT_WARN("Tape length violation [should be IBM Enhanced "
-+			   "Capacity Cartridge System Tape]. May cause "
-+			   "damage to drive or tape.\n");
-+		return tape_34xx_erp_failed(device, request, -EMEDIUMTYPE);
-+	case 0x5e:
-+		/* Compaction algorithm incompatible. */
-+		PRINT_WARN("The volume is recorded using an incompatible "
-+			   "compaction algorithm, which is not supported by "
-+			   "the control unit.\n");
-+		return tape_34xx_erp_failed(device, request, -EMEDIUMTYPE);
++struct cpi_sccb {
++	struct sccb_header header;
++	struct cpi_evbuf cpi_evbuf;
++} __attribute__((packed));
++
++/* Event type structure for write message and write priority message */
++static struct sclp_register sclp_cpi_event =
++{
++	.send_mask = EvTyp_CtlProgIdent_Mask
++};
 +
-+		/* The following erpas should have been covered earlier. */
-+	case 0x23: /* Read data check. */
-+	case 0x25: /* Write data check. */
-+	case 0x26: /* Data check (read opposite). */
-+	case 0x28: /* Write id mark check. */
-+	case 0x31: /* Tape void. */
-+	case 0x40: /* Overrun error. */
-+	case 0x41: /* Record sequence error. */
-+		/* All other erpas are reserved for future use. */
-+	default:
-+		return tape_34xx_erp_bug(device, request, sense[3]);
++MODULE_AUTHOR(
++	"Martin Peschke, IBM Deutschland Entwicklung GmbH "
++	"<mpeschke at de.ibm.com>");
++
++MODULE_DESCRIPTION(
++	"identify this operating system instance to the S/390 "
++	"or zSeries hardware");
++
++static char *system_name = NULL;
++MODULE_PARM(system_name, "s");
++MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters");
++
++static char *sysplex_name = NULL;
++#ifdef ALLOW_SYSPLEX_NAME
++MODULE_PARM(sysplex_name, "s");
++MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters");
++#endif
++
++/* use default value for this field (as well as for system level) */
++static char *system_type = "LINUX";
++
++static int
++cpi_check_parms(void)
++{
++	/* reject if no system type specified */
++	if (!system_type) {
++		printk("cpi: bug: no system type specified\n");
++		return -EINVAL;
++	}
++
++	/* reject if system type larger than 8 characters */
++	if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) {
++		printk("cpi: bug: system type has length of %li characters - "
++		       "only %i characters supported\n",
++		       strlen(system_type), CPI_LENGTH_SYSTEM_TYPE);
++		return -EINVAL;
++	}
++
++	/* reject if no system name specified */
++	if (!system_name) {
++		printk("cpi: no system name specified\n");
++		return -EINVAL;
++	}
++
++	/* reject if system name larger than 8 characters */
++	if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) {
++		printk("cpi: system name has length of %li characters - "
++		       "only %i characters supported\n",
++		       strlen(system_name), CPI_LENGTH_SYSTEM_NAME);
++		return -EINVAL;
++	}
++
++	/* reject if specified sysplex name larger than 8 characters */
++	if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) {
++		printk("cpi: sysplex name has length of %li characters"
++		       " - only %i characters supported\n",
++		       strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME);
++		return -EINVAL;
 +	}
++	return 0;
 +}
 +
-+/*
-+ * 3480/3490 interrupt handler
-+ */
-+static int
-+tape_34xx_irq(struct tape_device *device, struct tape_request *request)
++static void
++cpi_callback(struct sclp_req *req, void *data)
 +{
-+	if (request == NULL)
-+		return tape_34xx_unsolicited_irq(device);
++	struct semaphore *sem;
 +
-+	if ((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) &&
-+	    (device->devstat.dstat & DEV_STAT_DEV_END) &&
-+	    (request->op == TO_WRI)) {
-+		/* Write at end of volume */
-+		PRINT_INFO("End of volume\n"); /* XXX */
-+		return tape_34xx_erp_failed(device, request, -ENOSPC);
++	sem = (struct semaphore *) data;
++	up(sem);
++}
++
++static struct sclp_req *
++cpi_prepare_req(void)
++{
++	struct sclp_req *req;
++	struct cpi_sccb *sccb;
++	struct cpi_evbuf *evb;
++
++	req = (struct sclp_req *) kmalloc(sizeof(struct sclp_req), GFP_KERNEL);
++	if (req == NULL)
++		return ERR_PTR(-ENOMEM);
++	sccb = (struct cpi_sccb *) get_free_page(GFP_KERNEL | GFP_DMA);
++	if (sccb == NULL) {
++		kfree(req);
++		return ERR_PTR(-ENOMEM);
 +	}
++	memset(sccb, 0, sizeof(struct cpi_sccb));
 +
-+	if ((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) &&
-+	    (request->op == TO_BSB || request->op == TO_FSB))
-+		DBF_EVENT(5, "Skipped over tapemark\n");
++	/* setup SCCB for Control-Program Identification */
++	sccb->header.length = sizeof(struct cpi_sccb);
++	sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf);
++	sccb->cpi_evbuf.header.type = 0x0B;
++	evb = &sccb->cpi_evbuf;
 +
-+	if (device->devstat.dstat & DEV_STAT_UNIT_CHECK)
-+		return tape_34xx_unit_check(device, request);
++	/* set system type */
++	memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE);
++	memcpy(evb->system_type, system_type, strlen(system_type));
++	sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE);
++	EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE);
 +
-+	if (device->devstat.dstat & DEV_STAT_DEV_END)
-+		return tape_34xx_done(device, request);
++	/* set system name */
++	memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME);
++	memcpy(evb->system_name, system_name, strlen(system_name));
++	sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME);
++	EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME);
 +
-+	DBF_EVENT(6, "xunknownirq\n");
-+	PRINT_ERR("Unexpected interrupt.\n");
-+	PRINT_ERR("Current op is: %s", tape_op_verbose[request->op]);
-+	tape_dump_sense(device, request);
-+	return TAPE_IO_STOP;
++	/* set sytem level */
++	evb->system_level = LINUX_VERSION_CODE;
++
++	/* set sysplex name */
++	if (sysplex_name) {
++		memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME);
++		memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name));
++		sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
++		EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
++	}
++
++	/* prepare request data structure presented to SCLP driver */
++	req->command = SCLP_CMDW_WRITEDATA;
++	req->sccb = sccb;
++	req->status = SCLP_REQ_FILLED;
++	req->callback = cpi_callback;
++	return req;
++}
++
++static void
++cpi_free_req(struct sclp_req *req)
++{
++	free_page((unsigned long) req->sccb);
++	kfree(req);
++}
++
++static int __init
++cpi_module_init(void)
++{
++	struct semaphore sem;
++	struct sclp_req *req;
++	int rc;
++
++	rc = cpi_check_parms();
++	if (rc)
++		return rc;
++
++	rc = sclp_register(&sclp_cpi_event);
++	if (rc) {
++		/* could not register sclp event. Die. */
++		printk("cpi: could not register to hardware console.\n");
++		return -EINVAL;
++	}
++	if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) {
++		printk("cpi: no control program identification support\n");
++		sclp_unregister(&sclp_cpi_event);
++		return -ENOTSUPP;
++	}
++
++	req = cpi_prepare_req();
++	if (IS_ERR(req)) {
++		printk("cpi: couldn't allocate request\n");
++		sclp_unregister(&sclp_cpi_event);
++		return PTR_ERR(req);
++	}
++
++	/* Prepare semaphore */
++	sema_init(&sem, 0);
++	req->callback_data = &sem;
++	/* Add request to sclp queue */
++	sclp_add_request(req);
++	/* make "insmod" sleep until callback arrives */
++	down(&sem);
++
++	rc = ((struct cpi_sccb *) req->sccb)->header.response_code;
++	if (rc != 0x0020) {
++		printk("cpi: failed with response code 0x%x\n", rc);
++		rc = -ECOMM;
++	} else
++		rc = 0;
++
++	cpi_free_req(req);
++	sclp_unregister(&sclp_cpi_event);
++
++	return rc;
++}
++
++
++static void __exit cpi_module_exit(void)
++{
 +}
 +
++
++/* declare driver module init/cleanup functions */
++module_init(cpi_module_init);
++module_exit(cpi_module_exit);
++
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/sclp.h drivers/s390/char/sclp.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/sclp.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/sclp.h	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,157 @@
 +/*
-+ * ioctl_overload
++ *  drivers/s390/char/sclp.h
++ *
++ *  S390 version
++ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
++ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
 + */
-+static int
-+tape_34xx_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)
++
++#ifndef __SCLP_H__
++#define __SCLP_H__
++
++#include <linux/types.h>
++#include <linux/list.h>
++
++#include <asm/ebcdic.h>
++
++/* maximum number of pages concerning our own memory management */
++#define MAX_KMEM_PAGES (sizeof(unsigned long) << 3)
++#define MAX_CONSOLE_PAGES	4
++
++#define EvTyp_OpCmd		0x01
++#define EvTyp_Msg		0x02
++#define EvTyp_StateChange	0x08
++#define EvTyp_PMsgCmd		0x09
++#define EvTyp_CntlProgOpCmd	0x20
++#define EvTyp_CntlProgIdent	0x0B
++#define EvTyp_SigQuiesce	0x1D
++#define EvTyp_VT220Msg		0x1A
++
++#define EvTyp_OpCmd_Mask	0x80000000
++#define EvTyp_Msg_Mask		0x40000000
++#define EvTyp_StateChange_Mask	0x01000000
++#define EvTyp_PMsgCmd_Mask	0x00800000
++#define EvTyp_CtlProgOpCmd_Mask	0x00000001
++#define EvTyp_CtlProgIdent_Mask	0x00200000
++#define EvTyp_SigQuiesce_Mask	0x00000008
++#define EvTyp_VT220Msg_Mask	0x00000040
++
++#define GnrlMsgFlgs_DOM		0x8000
++#define GnrlMsgFlgs_SndAlrm	0x4000
++#define GnrlMsgFlgs_HoldMsg	0x2000
++
++#define LnTpFlgs_CntlText	0x8000
++#define LnTpFlgs_LabelText	0x4000
++#define LnTpFlgs_DataText	0x2000
++#define LnTpFlgs_EndText	0x1000
++#define LnTpFlgs_PromptText	0x0800
++
++typedef unsigned int sclp_cmdw_t;
++
++#define SCLP_CMDW_READDATA	0x00770005
++#define SCLP_CMDW_WRITEDATA	0x00760005
++#define SCLP_CMDW_WRITEMASK	0x00780005
++
++#define GDS_ID_MDSMU		0x1310
++#define GDS_ID_MDSRouteInfo	0x1311
++#define GDS_ID_AgUnWrkCorr	0x1549
++#define GDS_ID_SNACondReport	0x1532
++#define GDS_ID_CPMSU		0x1212
++#define GDS_ID_RoutTargInstr	0x154D
++#define GDS_ID_OpReq		0x8070
++#define GDS_ID_TextCmd		0x1320
++
++#define GDS_KEY_SelfDefTextMsg	0x31
++
++typedef u32 sccb_mask_t;	/* ATTENTION: assumes 32bit mask !!! */
++
++struct sccb_header {
++	u16	length;
++	u8	function_code;
++	u8	control_mask[3];
++	u16	response_code;
++} __attribute__((packed));
++
++struct gds_subvector {
++	u8	length;
++	u8	key;
++} __attribute__((packed));
++
++struct gds_vector {
++	u16	length;
++	u16	gds_id;
++} __attribute__((packed));
++
++struct evbuf_header {
++	u16	length;
++	u8	type;
++	u8	flags;
++	u16	_reserved;
++} __attribute__((packed));
++
++struct sclp_req {
++	struct list_head list;		/* list_head for request queueing. */
++	sclp_cmdw_t command;		/* sclp command to execute */
++	void	*sccb;			/* pointer to the sccb to execute */
++	char	status;			/* status of this request */
++	/* Callback that is called after reaching final status. */
++	void (*callback)(struct sclp_req *, void *data);
++	void *callback_data;
++};
++
++#define SCLP_REQ_FILLED	  0x00	/* request is ready to be processed */
++#define SCLP_REQ_QUEUED	  0x01	/* request is queued to be processed */
++#define SCLP_REQ_RUNNING  0x02	/* request is currently running */
++#define SCLP_REQ_DONE	  0x03	/* request is completed successfully */
++#define SCLP_REQ_FAILED	  0x05	/* request is finally failed */
++
++/* function pointers that a high level driver has to use for registration */
++/* of some routines it wants to be called from the low level driver */
++struct sclp_register {
++	struct list_head list;
++	/* event masks this user is registered for */
++	sccb_mask_t receive_mask;
++	sccb_mask_t send_mask;
++	/* actually present events */
++	sccb_mask_t sclp_receive_mask;
++	sccb_mask_t sclp_send_mask;
++	/* called if event type availability changes */
++	void (*state_change_fn)(struct sclp_register *);
++	/* called for events in cp_receive_mask/sclp_receive_mask */
++	void (*receiver_fn)(struct evbuf_header *);
++};
++
++/* externals from sclp.c */
++void sclp_add_request(struct sclp_req *req);
++void sclp_sync_wait(void);
++int sclp_register(struct sclp_register *reg);
++void sclp_unregister(struct sclp_register *reg);
++char *sclp_error_message(u16 response_code);
++int sclp_remove_processed(struct sccb_header *sccb);
++
++/* useful inlines */
++
++/* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */
++/* translate single character from ASCII to EBCDIC */
++static inline unsigned char
++sclp_ascebc(unsigned char ch)
 +{
-+        if (cmd == TAPE390_DISPLAY) {
-+		struct display_struct disp;
++	return (MACHINE_IS_VM) ? _ascebc[ch] : _ascebc_500[ch];
++}
 +
-+		if(copy_from_user(&disp, (char *) arg, sizeof(disp)) != 0)
-+			return -EFAULT;
++/* translate string from EBCDIC to ASCII */
++static inline void
++sclp_ebcasc_str(unsigned char *str, int nr)
++{
++	(MACHINE_IS_VM) ? EBCASC(str, nr) : EBCASC_500(str, nr);
++}
 +
-+	        return tape_std_display(device, &disp);
-+	} else
-+                return -EINVAL;
++/* translate string from ASCII to EBCDIC */
++static inline void
++sclp_ascebc_str(unsigned char *str, int nr)
++{
++	(MACHINE_IS_VM) ? ASCEBC(str, nr) : ASCEBC_500(str, nr);
++}
++
++#endif	 /* __SCLP_H__ */
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/sclp_rw.c drivers/s390/char/sclp_rw.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/sclp_rw.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/sclp_rw.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,496 @@
++/*
++ *  drivers/s390/char/sclp_rw.c
++ *     driver: reading from and writing to system console on S/390 via SCLP
++ *
++ *  S390 version
++ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
++ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/kmod.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/spinlock.h>
++#include <linux/ctype.h>
++#include <asm/uaccess.h>
++
++#include "sclp.h"
++#include "sclp_rw.h"
++
++#define SCLP_RW_PRINT_HEADER "sclp low level driver: "
++
++/*
++ * The room for the SCCB (only for writing) is not equal to a pages size
++ * (as it is specified as the maximum size in the the SCLP ducumentation)
++ * because of the additional data structure described above.
++ */
++#define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer))
++
++/* Event type structure for write message and write priority message */
++static struct sclp_register sclp_rw_event = {
++	.send_mask = EvTyp_Msg_Mask | EvTyp_PMsgCmd_Mask
++};
++
++/*
++ * Setup a sclp write buffer. Gets a page as input (4K) and returns
++ * a pointer to a struct sclp_buffer structure that is located at the
++ * end of the input page. This reduces the buffer space by a few
++ * bytes but simplifies things.
++ */
++struct sclp_buffer *
++sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
++{
++	struct sclp_buffer *buffer;
++	struct write_sccb *sccb;
++
++	sccb = (struct write_sccb *) page;
++	/*
++	 * We keep the struct sclp_buffer structure at the end
++	 * of the sccb page.
++	 */
++	buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1;
++	buffer->sccb = sccb;
++	buffer->retry_count = 0;
++	init_timer(&buffer->retry_timer);
++	buffer->mto_number = 0;
++	buffer->mto_char_sum = 0;
++	buffer->current_line = NULL;
++	buffer->current_length = 0;
++	buffer->columns = columns;
++	buffer->htab = htab;
++
++	/* initialize sccb */
++	memset(sccb, 0, sizeof(struct write_sccb));
++	sccb->header.length = sizeof(struct write_sccb);
++	sccb->msg_buf.header.length = sizeof(struct msg_buf);
++	sccb->msg_buf.header.type = EvTyp_Msg;
++	sccb->msg_buf.mdb.header.length = sizeof(struct mdb);
++	sccb->msg_buf.mdb.header.type = 1;
++	sccb->msg_buf.mdb.header.tag = 0xD4C4C240;	/* ebcdic "MDB " */
++	sccb->msg_buf.mdb.header.revision_code = 1;
++	sccb->msg_buf.mdb.go.length = sizeof(struct go);
++	sccb->msg_buf.mdb.go.type = 1;
++
++	return buffer;
++}
++
++/*
++ * Return a pointer to the orignal page that has been used to create
++ * the buffer.
++ */
++void *
++sclp_unmake_buffer(struct sclp_buffer *buffer)
++{
++	return buffer->sccb;
 +}
 +
++/*
++ * Initialize a new Message Text Object (MTO) at the end of the provided buffer
++ * with enough room for max_len characters. Return 0 on success.
++ */
 +static int
-+tape_34xx_setup_device(struct tape_device * device)
++sclp_initialize_mto(struct sclp_buffer *buffer, int max_len)
 +{
-+	struct tape_34xx_discdata *discdata;
++	struct write_sccb *sccb;
++	struct mto *mto;
++	int mto_size;
 +
-+	DBF_EVENT(5, "tape_34xx_setup_device(%p)\n", device);
-+	DBF_EVENT(6, "34xx minor1: %x\n", device->first_minor);
-+	discdata = kmalloc(sizeof(struct tape_34xx_discdata), GFP_ATOMIC);
-+	if(discdata) {
-+		memset(discdata, 0, sizeof(struct tape_34xx_discdata));
-+		INIT_LIST_HEAD(&discdata->sbid_list);
-+		device->discdata = discdata;
-+	}
++	/* max size of new Message Text Object including message text  */
++	mto_size = sizeof(struct mto) + max_len;
++
++	/* check if current buffer sccb can contain the mto */
++	sccb = buffer->sccb;
++	if ((MAX_SCCB_ROOM - sccb->header.length) < mto_size)
++		return -ENOMEM;
++
++	/* find address of new message text object */
++	mto = (struct mto *)(((addr_t) sccb) + sccb->header.length);
++
++	/*
++	 * fill the new Message-Text Object,
++	 * starting behind the former last byte of the SCCB
++	 */
++	memset(mto, 0, sizeof(struct mto));
++	mto->length = sizeof(struct mto);
++	mto->type = 4;	/* message text object */
++	mto->line_type_flags = LnTpFlgs_EndText; /* end text */
++
++	/* set pointer to first byte after struct mto. */
++	buffer->current_line = (char *) (mto + 1);
++	buffer->current_length = 0;
 +
-+	if(!TAPE_BOXED(device))
-+		tape_34xx_medium_sense(device);
 +	return 0;
 +}
 +
-+static void
-+tape_34xx_cleanup_device(struct tape_device * device)
-+{
-+	if (device->discdata) {
-+		tape_34xx_clear_sbid_list(device);
-+		kfree(device->discdata);
-+		device->discdata = NULL;
-+	}
-+}
-+
 +/*
-+ * Build up the lookup table...
++ * Finalize MTO initialized by sclp_initialize_mto(), updating the sizes of
++ * MTO, enclosing MDB, event buffer and SCCB.
 + */
 +static void
-+tape_34xx_add_sbid(struct tape_device *device, struct tape_34xx_block_id bid)
++sclp_finalize_mto(struct sclp_buffer *buffer)
 +{
-+	struct tape_34xx_discdata *	discdata = device->discdata;
-+	struct sbid_entry *		new;
-+	struct sbid_entry *		cur;
-+	struct list_head *		l;
-+
-+	if(discdata == NULL)
-+		return;
-+	if((new = kmalloc(sizeof(struct sbid_entry), GFP_ATOMIC)) == NULL)
-+		return;
++	struct write_sccb *sccb;
++	struct mto *mto;
++	int str_len, mto_size;
 +
-+	new->bid = bid;
-+	new->bid.tbi_format = 0;
++	str_len = buffer->current_length;
++	buffer->current_line = NULL;
++	buffer->current_length = 0;
 +
-+	/*
-+	 * Search the position where to insert the new entry. It is possible
-+	 * that the entry should not be added but the block number has to be
-+	 * updated to approximate the logical block, where a segment starts.
-+	 */
-+	list_for_each(l, &discdata->sbid_list) {
-+		cur = list_entry(l, struct sbid_entry, list);
++	/* real size of new Message Text Object including message text	*/
++	mto_size = sizeof(struct mto) + str_len;
 +
-+		/*
-+		 * If the current entry has the same segment and wrap, then
-+		 * there is no new entry needed. Only the block number of the
-+		 * current entry might be adjusted to reflect an earlier start
-+		 * of the segment.
-+		 */
-+		if(
-+			(cur->bid.tbi_segment == new->bid.tbi_segment) &&
-+			(cur->bid.tbi_wrap    == new->bid.tbi_wrap)
-+		) {
-+			if(new->bid.tbi_block < cur->bid.tbi_block) {
-+				cur->bid.tbi_block = new->bid.tbi_block;
-+			}
-+			kfree(new);
-+			break;
-+		}
++	/* find address of new message text object */
++	sccb = buffer->sccb;
++	mto = (struct mto *)(((addr_t) sccb) + sccb->header.length);
 +
-+		/*
-+		 * Otherwise the list is sorted by block number because it
-+		 * is alway ascending while the segment number decreases on
-+		 * the second wrap.
-+		 */
-+		if(cur->bid.tbi_block > new->bid.tbi_block) {
-+			list_add_tail(&new->list, l);
-+			break;
-+		}
-+	}
++	/* set size of message text object */
++	mto->length = mto_size;
 +
 +	/*
-+	 * The loop went through without finding a merge or adding an entry
-+	 * add the new entry to the end of the list.
++	 * update values of sizes
++	 * (SCCB, Event(Message) Buffer, Message Data Block)
 +	 */
-+	if(l == &discdata->sbid_list) {
-+		list_add_tail(&new->list, &discdata->sbid_list);
-+	}
-+
-+	list_for_each(l, &discdata->sbid_list) {
-+		cur = list_entry(l, struct sbid_entry, list);
-+
-+		DBF_EVENT(3, "sbid_list(%03i:%1i:%08i)\n",
-+			cur->bid.tbi_segment, cur->bid.tbi_wrap,
-+			cur->bid.tbi_block);
-+	}
++	sccb->header.length += mto_size;
++	sccb->msg_buf.header.length += mto_size;
++	sccb->msg_buf.mdb.header.length += mto_size;
 +
-+	return;	
++	/*
++	 * count number of buffered messages (= number of Message Text
++	 * Objects) and number of buffered characters
++	 * for the SCCB currently used for buffering and at all
++	 */
++	buffer->mto_number++;
++	buffer->mto_char_sum += str_len;
 +}
 +
 +/*
-+ * Fill hardware positioning information into the given block id. With that
-+ * seeks don't have to go back to the beginning of the tape and are done at
-+ * faster speed because the vicinity of a segment can be located at faster
-+ * speed.
-+ *
-+ * The caller must have set tbi_block.
++ * processing of a message including escape characters,
++ * returns number of characters written to the output sccb
++ * ("processed" means that is not guaranteed that the character have already
++ *  been sent to the SCLP but that it will be done at least next time the SCLP
++ *  is not busy)
 + */
-+static void
-+tape_34xx_merge_sbid(
-+	struct tape_device *		device,
-+	struct tape_34xx_block_id *	bid
-+) {
-+	struct tape_34xx_discdata *	discdata = device->discdata;
-+	struct sbid_entry *		cur;
-+	struct list_head *		l;
-+
-+	bid->tbi_wrap    = 0;
-+	bid->tbi_segment = 1;
-+	bid->tbi_format  = (*device->modeset_byte & 0x08) ?
-+				TBI_FORMAT_3480_XF : TBI_FORMAT_3480;
-+
-+	if(discdata == NULL)
-+		goto tape_34xx_merge_sbid_exit;
-+	if(list_empty(&discdata->sbid_list))
-+		goto tape_34xx_merge_sbid_exit;
++int
++sclp_write(struct sclp_buffer *buffer,
++	   const unsigned char *msg, int count, int from_user)
++{
++	int spaces, i_msg;
++	char ch;
++	int rc;
 +
-+	list_for_each(l, &discdata->sbid_list) {
-+		cur = list_entry(l, struct sbid_entry, list);
++	/*
++	 * parse msg for escape sequences (\t,\v ...) and put formated
++	 * msg into an mto (created by sclp_initialize_mto).
++	 *
++	 * We have to do this work ourselfs because there is no support for
++	 * these characters on the native machine and only partial support
++	 * under VM (Why does VM interpret \n but the native machine doesn't ?)
++	 *
++	 * Depending on i/o-control setting the message is always written
++	 * immediately or we wait for a final new line maybe coming with the
++	 * next message. Besides we avoid a buffer overrun by writing its
++	 * content.
++	 *
++	 * RESTRICTIONS:
++	 *
++	 * \r and \b work within one line because we are not able to modify
++	 * previous output that have already been accepted by the SCLP.
++	 *
++	 * \t combined with following \r is not correctly represented because
++	 * \t is expanded to some spaces but \r does not know about a
++	 * previous \t and decreases the current position by one column.
++	 * This is in order to a slim and quick implementation.
++	 */
++	for (i_msg = 0; i_msg < count; i_msg++) {
++		if (from_user) {
++			if (get_user(ch, msg + i_msg) != 0)
++				return -EFAULT;
++		} else
++			ch = msg[i_msg];
 +
-+		if(cur->bid.tbi_block > bid->tbi_block)
++		switch (ch) {
++		case '\n':	/* new line, line feed (ASCII)	*/
++			/* check if new mto needs to be created */
++			if (buffer->current_line == NULL) {
++				rc = sclp_initialize_mto(buffer, 0);
++				if (rc)
++					return i_msg;
++			}
++			sclp_finalize_mto(buffer);
++			break;
++		case '\a':	/* bell, one for several times	*/
++			/* set SCLP sound alarm bit in General Object */
++			buffer->sccb->msg_buf.mdb.go.general_msg_flags |=
++				GnrlMsgFlgs_SndAlrm;
++			break;
++		case '\t':	/* horizontal tabulator	 */
++			/* check if new mto needs to be created */
++			if (buffer->current_line == NULL) {
++				rc = sclp_initialize_mto(buffer,
++							 buffer->columns);
++				if (rc)
++					return i_msg;
++			}
++			/* "go to (next htab-boundary + 1, same line)" */
++			do {
++				if (buffer->current_length >= buffer->columns)
++					break;
++				/* ok, add a blank */
++				*buffer->current_line++ = 0x40;
++				buffer->current_length++;
++			} while (buffer->current_length % buffer->htab);
++			break;
++		case '\f':	/* form feed  */
++		case '\v':	/* vertical tabulator  */
++			/* "go to (actual column, actual line + 1)" */
++			/* = new line, leading spaces */
++			if (buffer->current_line != NULL) {
++				spaces = buffer->current_length;
++				sclp_finalize_mto(buffer);
++				rc = sclp_initialize_mto(buffer,
++							 buffer->columns);
++				if (rc)
++					return i_msg;
++				memset(buffer->current_line, 0x40, spaces);
++				buffer->current_line += spaces;
++				buffer->current_length = spaces;
++			} else {
++				/* one an empty line this is the same as \n */
++				rc = sclp_initialize_mto(buffer,
++							 buffer->columns);
++				if (rc)
++					return i_msg;
++				sclp_finalize_mto(buffer);
++			}
 +			break;
++		case '\b':	/* backspace  */
++			/* "go to (actual column - 1, actual line)" */
++			/* decrement counter indicating position, */
++			/* do not remove last character */
++			if (buffer->current_line != NULL &&
++			    buffer->current_length > 0) {
++				buffer->current_length--;
++				buffer->current_line--;
++			}
++			break;
++		case 0x00:	/* end of string  */
++			/* transfer current line to SCCB */
++			if (buffer->current_line != NULL)
++				sclp_finalize_mto(buffer);
++			/* skip the rest of the message including the 0 byte */
++			i_msg = count - 1;
++			break;
++		default:	/* no escape character	*/
++			/* do not output unprintable characters */
++			if (!isprint(ch))
++				break;
++			/* check if new mto needs to be created */
++			if (buffer->current_line == NULL) {
++				rc = sclp_initialize_mto(buffer,
++							 buffer->columns);
++				if (rc)
++					return i_msg;
++			}
++			*buffer->current_line++ = sclp_ascebc(ch);
++			buffer->current_length++;
++			break;
++		}
++		/* check if current mto is full */
++		if (buffer->current_line != NULL &&
++		    buffer->current_length >= buffer->columns)
++			sclp_finalize_mto(buffer);
 +	}
 +
-+	/* If block comes before first entries block, use seek from start. */
-+	if(l->prev == &discdata->sbid_list)
-+		goto tape_34xx_merge_sbid_exit;
-+
-+	cur = list_entry(l->prev, struct sbid_entry, list);
-+	bid->tbi_wrap    = cur->bid.tbi_wrap;
-+	bid->tbi_segment = cur->bid.tbi_segment;
-+
-+tape_34xx_merge_sbid_exit:
-+	DBF_EVENT(6, "merged_bid = %08x\n", *((unsigned int *) bid));
-+	return;
++	/* return number of processed characters */
++	return i_msg;
 +}
 +
-+static void
-+tape_34xx_clear_sbid_list(struct tape_device *device)
++/*
++ * Return the number of free bytes in the sccb
++ */
++int
++sclp_buffer_space(struct sclp_buffer *buffer)
 +{
-+	struct list_head *		l;
-+	struct list_head *		n;
-+	struct tape_34xx_discdata *	discdata;
-+
-+	if((discdata = device->discdata) == NULL)
-+		return;
++	int count;
 +
-+	list_for_each_safe(l, n, &discdata->sbid_list) {
-+		list_del(l);
-+		kfree(list_entry(l, struct sbid_entry, list));
-+	}
++	count = MAX_SCCB_ROOM - buffer->sccb->header.length;
++	if (buffer->current_line != NULL)
++		count -= sizeof(struct mto) + buffer->current_length;
++	return count;
 +}
 +
 +/*
-+ * MTTELL: Tell block. Return the number of block relative to current file.
++ * Return number of characters in buffer
 + */
 +int
-+tape_34xx_mttell(struct tape_device *device, int mt_count)
++sclp_chars_in_buffer(struct sclp_buffer *buffer)
 +{
-+	struct tape_34xx_block_id	bid;
-+	int				rc;
++	int count;
 +
-+	rc = tape_std_read_block_id(device, (unsigned int *) &bid);
-+	if (rc)
-+		return rc;
++	count = buffer->mto_char_sum;
++	if (buffer->current_line != NULL)
++		count += buffer->current_length;
++	return count;
++}
 +
-+	/*
-+	 * Build up a lookup table. The format id is ingored.
-+	 */
-+	tape_34xx_add_sbid(device, bid);
++/*
++ * sets or provides some values that influence the drivers behaviour
++ */
++void
++sclp_set_columns(struct sclp_buffer *buffer, unsigned short columns)
++{
++	buffer->columns = columns;
++	if (buffer->current_line != NULL &&
++	    buffer->current_length > buffer->columns)
++		sclp_finalize_mto(buffer);
++}
 +
-+	return bid.tbi_block;
++void
++sclp_set_htab(struct sclp_buffer *buffer, unsigned short htab)
++{
++	buffer->htab = htab;
 +}
 +
 +/*
-+ * MTSEEK: seek to the specified block.
++ * called by sclp_console_init and/or sclp_tty_init
 + */
 +int
-+tape_34xx_mtseek(struct tape_device *device, int mt_count)
++sclp_rw_init(void)
 +{
-+	struct tape_34xx_block_id	bid;
-+
-+	if (mt_count > 0x400000) {
-+		DBF_EXCEPTION(6, "xsee parm\n");
-+		return -EINVAL;
-+	}
++	static int init_done = 0;
++	int rc;
 +
-+	bid.tbi_block   = mt_count;
++	if (init_done)
++		return 0;
 +
-+	/*
-+	 * Set hardware seek information in the block id.
-+	 */
-+	tape_34xx_merge_sbid(device, &bid);
++	rc = sclp_register(&sclp_rw_event);
++	if (rc == 0)
++		init_done = 1;
++	return rc;
++}
 +
-+	return tape_std_seek_block_id(device, *((unsigned int *) &bid));
++static void
++sclp_buffer_retry(unsigned long data)
++{
++	struct sclp_buffer *buffer = (struct sclp_buffer *) data;
++	buffer->request.status = SCLP_REQ_FILLED;
++	buffer->sccb->header.response_code = 0x0000;
++	sclp_add_request(&buffer->request);
 +}
 +
++#define SCLP_BUFFER_MAX_RETRY		5
++#define	SCLP_BUFFER_RETRY_INTERVAL	2
++
 +/*
-+ * Tape block read for 34xx.
++ * second half of Write Event Data-function that has to be done after
++ * interruption indicating completion of Service Call.
 + */
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+struct tape_request *
-+tape_34xx_bread(struct tape_device *device, struct request *req)
++static void
++sclp_writedata_callback(struct sclp_req *request, void *data)
 +{
-+	struct tape_request  *request;
-+	struct buffer_head   *bh;
-+	ccw1_t               *ccw;
-+	int                   count;
-+	int                   size;
++	int rc;
++	struct sclp_buffer *buffer;
++	struct write_sccb *sccb;
 +
-+	DBF_EVENT(6, "tape_34xx_bread(sector=%u,size=%u)\n",
-+		req->sector, req->nr_sectors);
++	buffer = (struct sclp_buffer *) data;
++	sccb = buffer->sccb;
 +
-+	/* Count the number of blocks for the request. */
-+	count = 0;
-+	size  = 0;
-+	for(bh = req->bh; bh; bh = bh->b_reqnext) {
-+		for(size = 0; size < bh->b_size; size += TAPEBLOCK_HSEC_SIZE)
-+			count++;
++	if (request->status == SCLP_REQ_FAILED) {
++		if (buffer->callback != NULL)
++			buffer->callback(buffer, -EIO);
++		return;
 +	}
++	/* check SCLP response code and choose suitable action	*/
++	switch (sccb->header.response_code) {
++	case 0x0020 :
++		/* Normal completion, buffer processed, message(s) sent */
++		rc = 0;
++		break;
 +
-+	/* Allocate the ccw request. */
-+	request = tape_alloc_request(3+count+1, 8);
-+	if (IS_ERR(request))
-+		return request;
-+
-+	/*
-+	 * Setup the tape block id to start the read from. The block number
-+	 * is later compared to the current position to decide whether a
-+	 * locate block is required. If one is needed this block id is used
-+	 * to locate it.
-+	 */
-+	((struct tape_34xx_block_id *) request->cpdata)->tbi_block =
-+		req->sector >> TAPEBLOCK_HSEC_S2B;
-+
-+	/* Setup ccws. */
-+	request->op = TO_BLOCK;
-+	ccw = request->cpaddr;
-+	ccw = tape_ccw_cc(ccw, MODE_SET_DB, 1, device->modeset_byte);
-+
-+	/*
-+	 * We always setup a nop after the mode set ccw. This slot is
-+	 * used in tape_std_check_locate to insert a locate ccw if the
-+	 * current tape position doesn't match the start block to be read.
-+	 * The second nop will be filled with a read block id which is in
-+	 * turn used by tape_34xx_free_bread to populate the segment bid
-+	 * table.
-+	 */
-+	ccw = tape_ccw_cc(ccw, NOP, 0, NULL);
-+	ccw = tape_ccw_cc(ccw, NOP, 0, NULL);
-+
-+	for(bh = req->bh; bh; bh = bh->b_reqnext) {
-+		for(size = 0; size < bh->b_size; size += TAPEBLOCK_HSEC_SIZE) {
-+			ccw->flags    = CCW_FLAG_CC;
-+			ccw->cmd_code = READ_FORWARD;
-+			ccw->count    = TAPEBLOCK_HSEC_SIZE;
-+			set_normalized_cda(ccw, (void *) __pa(bh->b_data+size));
-+			ccw++;
++	case 0x0340: /* Contained SCLP equipment check */
++		if (buffer->retry_count++ > SCLP_BUFFER_MAX_RETRY) {
++			rc = -EIO;
++			break;
 +		}
-+	}
++		/* remove processed buffers and requeue rest */
++		if (sclp_remove_processed((struct sccb_header *) sccb) > 0) {
++			/* not all buffers were processed */
++			sccb->header.response_code = 0x0000;
++			buffer->request.status = SCLP_REQ_FILLED;
++			sclp_add_request(request);
++			return;
++		}
++		rc = 0;
++		break;
 +
-+	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
++	case 0x0040: /* SCLP equipment check */
++	case 0x05f0: /* Target resource in improper state */
++		if (buffer->retry_count++ > SCLP_BUFFER_MAX_RETRY) {
++			rc = -EIO;
++			break;
++		}
++		/* wait some time, then retry request */
++		buffer->retry_timer.function = sclp_buffer_retry;
++		buffer->retry_timer.data = (unsigned long) buffer;
++		buffer->retry_timer.expires = jiffies +
++						SCLP_BUFFER_RETRY_INTERVAL*HZ;
++		add_timer(&buffer->retry_timer);
++		return;
 +
-+	return request;
++	default:
++		if (sccb->header.response_code == 0x71f0)
++			rc = -ENOMEM;
++		else
++			rc = -EINVAL;
++		break;
++	}
++	if (buffer->callback != NULL)
++		buffer->callback(buffer, rc);
 +}
 +
++/*
++ * Setup the request structure in the struct sclp_buffer to do SCLP Write
++ * Event Data and pass the request to the core SCLP loop.
++ */
 +void
-+tape_34xx_free_bread (struct tape_request *request)
++sclp_emit_buffer(struct sclp_buffer *buffer,
++		 void (*callback)(struct sclp_buffer *, int))
 +{
-+	ccw1_t* ccw = request->cpaddr;
++	struct write_sccb *sccb;
 +
-+	if((ccw + 2)->cmd_code == READ_BLOCK_ID) {
-+		struct {
-+			struct tape_34xx_block_id	channel_block_id;
-+			struct tape_34xx_block_id	device_block_id;
-+		} __attribute__ ((packed)) *rbi_data;
++	/* add current line if there is one */
++	if (buffer->current_line != NULL)
++		sclp_finalize_mto(buffer);
 +
-+		rbi_data = request->cpdata;
++	/* Are there messages in the output buffer ? */
++	if (buffer->mto_number == 0) {
++		if (callback != NULL)
++			callback(buffer, 0);
++		return;
++	}
 +
-+		if(!request->device)
-+			DBF_EVENT(6, "tape_34xx_free_bread: no device!\n");
-+		DBF_EVENT(6, "tape_34xx_free_bread: update_sbid\n");
-+		tape_34xx_add_sbid(
-+			request->device,
-+			rbi_data->channel_block_id
-+		);
-+	} else {
-+		DBF_EVENT(3, "tape_34xx_free_bread: no block info\n");
++	sccb = buffer->sccb;
++	if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask)
++		/* Use normal write message */
++		sccb->msg_buf.header.type = EvTyp_Msg;
++	else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask)
++		/* Use write priority message */
++		sccb->msg_buf.header.type = EvTyp_PMsgCmd;
++	else {
++		if (callback != NULL)
++			callback(buffer, -ENOSYS);
++		return;
 +	}
-+
-+	/* Last ccw is a nop and doesn't need clear_normalized_cda */
-+	for (ccw = request->cpaddr; ccw->flags & CCW_FLAG_CC; ccw++)
-+		if (ccw->cmd_code == READ_FORWARD)
-+			clear_normalized_cda(ccw);
-+	tape_put_request(request);
++	buffer->request.command = SCLP_CMDW_WRITEDATA;
++	buffer->request.status = SCLP_REQ_FILLED;
++	buffer->request.callback = sclp_writedata_callback;
++	buffer->request.callback_data = buffer;
++	buffer->request.sccb = sccb;
++	buffer->callback = callback;
++	sclp_add_request(&buffer->request);
 +}
-+
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/sclp_rw.h drivers/s390/char/sclp_rw.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/sclp_rw.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/sclp_rw.h	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,98 @@
 +/*
-+ * check_locate is called just before the tape request is passed to
-+ * the common io layer for execution. It has to check the current
-+ * tape position and insert a locate ccw if it doesn't match the
-+ * start block for the request.
++ *  drivers/s390/char/sclp_rw.h
++ *    interface to the SCLP-read/write driver
++ *
++ *  S390 version
++ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
++ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
 + */
-+void
-+tape_34xx_check_locate(struct tape_device *device, struct tape_request *request)
-+{
-+	struct tape_34xx_block_id *id;
-+	struct tape_34xx_block_id *start;
 +
-+	id = (struct tape_34xx_block_id *) request->cpdata;
++#ifndef __SCLP_RW_H__
++#define __SCLP_RW_H__
 +
-+	/*
-+	 * The tape is already at the correct position. No seek needed.
-+	 */
-+	if (id->tbi_block == device->blk_data.block_position)
-+		return;
++#include <linux/list.h>
++#include <linux/timer.h>
 +
-+	/*
-+	 * In case that the block device image doesn't start at the beginning
-+	 * of the tape, adjust the blocknumber for the locate request.
-+	 */
-+	start = (struct tape_34xx_block_id *) &device->blk_data.start_block_id;
-+	if(start->tbi_block)
-+		id->tbi_block = id->tbi_block + start->tbi_block;
++struct mto {
++	u16 length;
++	u16 type;
++	u16 line_type_flags;
++	u8  alarm_control;
++	u8  _reserved[3];
++} __attribute__((packed));
 +
-+	/*
-+	 * Merge HW positioning information to the block id. This information
-+	 * is used by the device for faster seeks.
-+	 */
-+	tape_34xx_merge_sbid(device, id);
++struct go {
++	u16 length;
++	u16 type;
++	u32 domid;
++	u8  hhmmss_time[8];
++	u8  th_time[3];
++	u8  reserved_0;
++	u8  dddyyyy_date[7];
++	u8  _reserved_1;
++	u16 general_msg_flags;
++	u8  _reserved_2[10];
++	u8  originating_system_name[8];
++	u8  job_guest_name[8];
++} __attribute__((packed));
 +
-+	/*
-+	 * Transform the NOP to a LOCATE entry.
-+	 */
-+	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
-+	tape_ccw_cc(request->cpaddr + 2, READ_BLOCK_ID, 8, request->cpdata);
++struct mdb_header {
++	u16 length;
++	u16 type;
++	u32 tag;
++	u32 revision_code;
++} __attribute__((packed));
 +
-+	return;
-+}
-+#endif
++struct mdb {
++	struct mdb_header header;
++	struct go go;
++} __attribute__((packed));
 +
-+static int
-+tape_34xx_mtweof(struct tape_device *device, int count)
-+{
-+	tape_34xx_clear_sbid_list(device);
-+	return tape_std_mtweof(device, count);
-+}
++struct msg_buf {
++	struct evbuf_header header;
++	struct mdb mdb;
++} __attribute__((packed));
 +
-+/*
-+ * List of 3480/3490 magnetic tape commands.
-+ */
-+static tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] =
-+{
-+	[MTRESET]        = tape_std_mtreset,
-+	[MTFSF]          = tape_std_mtfsf,
-+	[MTBSF]          = tape_std_mtbsf,
-+	[MTFSR]          = tape_std_mtfsr,
-+	[MTBSR]          = tape_std_mtbsr,
-+	[MTWEOF]         = tape_34xx_mtweof,
-+	[MTREW]          = tape_std_mtrew,
-+	[MTOFFL]         = tape_std_mtoffl,
-+	[MTNOP]          = tape_std_mtnop,
-+	[MTRETEN]        = tape_std_mtreten,
-+	[MTBSFM]         = tape_std_mtbsfm,
-+	[MTFSFM]         = tape_std_mtfsfm,
-+	[MTEOM]          = tape_std_mteom,
-+	[MTERASE]        = tape_std_mterase,
-+	[MTRAS1]         = NULL,
-+	[MTRAS2]         = NULL,
-+	[MTRAS3]         = NULL,
-+	[MTSETBLK]       = tape_std_mtsetblk,
-+	[MTSETDENSITY]   = NULL,
-+	[MTSEEK]         = tape_34xx_mtseek,
-+	[MTTELL]         = tape_34xx_mttell,
-+	[MTSETDRVBUFFER] = NULL,
-+	[MTFSS]          = NULL,
-+	[MTBSS]          = NULL,
-+	[MTWSM]          = NULL,
-+	[MTLOCK]         = NULL,
-+	[MTUNLOCK]       = NULL,
-+	[MTLOAD]         = tape_std_mtload,
-+	[MTUNLOAD]       = tape_std_mtunload,
-+	[MTCOMPRESSION]  = tape_std_mtcompression,
-+	[MTSETPART]      = NULL,
-+	[MTMKPART]       = NULL
-+};
++struct write_sccb {
++	struct sccb_header header;
++	struct msg_buf msg_buf;
++} __attribute__((packed));
++
++/* The number of empty mto buffers that can be contained in a single sccb. */
++#define NR_EMPTY_MTO_PER_SCCB ((PAGE_SIZE - sizeof(struct sclp_buffer) - \
++			sizeof(struct write_sccb)) / sizeof(struct mto))
 +
 +/*
-+ * Tape discipline structures for 3480 and 3490.
++ * data structure for information about list of SCCBs (only for writing),
++ * will be located at the end of a SCCBs page
 + */
-+static struct tape_discipline tape_discipline_3480 = {
-+	.owner = THIS_MODULE,
-+	.cu_type = 0x3480,
-+	.setup_device = tape_34xx_setup_device,
-+	.cleanup_device = tape_34xx_cleanup_device,
-+	.process_eov = tape_std_process_eov,
-+	.irq = tape_34xx_irq,
-+	.read_block = tape_std_read_block,
-+	.write_block = tape_std_write_block,
-+	.assign = tape_std_assign,
-+	.unassign = tape_std_unassign,
-+#ifdef TAPE390_FORCE_UNASSIGN
-+	.force_unassign = tape_std_force_unassign,
-+#endif
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+	.bread = tape_34xx_bread,
-+	.free_bread = tape_34xx_free_bread,
-+	.check_locate = tape_34xx_check_locate,
-+#endif
-+	.ioctl_fn = tape_34xx_ioctl,
-+	.mtop_array = tape_34xx_mtop
-+};
-+
-+static struct tape_discipline tape_discipline_3490 = {
-+	.owner = THIS_MODULE,
-+	.cu_type = 0x3490,
-+	.setup_device = tape_34xx_setup_device,
-+	.cleanup_device = tape_34xx_cleanup_device,
-+	.process_eov = tape_std_process_eov,
-+	.irq = tape_34xx_irq,
-+	.read_block = tape_std_read_block,
-+	.write_block = tape_std_write_block,
-+	.assign = tape_std_assign,
-+	.unassign = tape_std_unassign,
-+#ifdef TAPE390_FORCE_UNASSIGN
-+	.force_unassign = tape_std_force_unassign,
-+#endif
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+	.bread = tape_34xx_bread,
-+	.free_bread = tape_34xx_free_bread,
-+	.check_locate = tape_34xx_check_locate,
-+#endif
-+	.ioctl_fn = tape_34xx_ioctl,
-+	.mtop_array = tape_34xx_mtop
++struct sclp_buffer {
++	struct list_head list;		/* list_head for sccb_info chain */
++	struct sclp_req request;
++	struct write_sccb *sccb;
++	char *current_line;
++	int current_length;
++	int retry_count;
++	struct timer_list retry_timer;
++	/* output format settings */
++	unsigned short columns;
++	unsigned short htab;
++	/* statistics about this buffer */
++	unsigned int mto_char_sum;	/* # chars in sccb */
++	unsigned int mto_number;	/* # mtos in sccb */
++	/* Callback that is called after reaching final status. */
++	void (*callback)(struct sclp_buffer *, int);
 +};
 +
-+int
-+tape_34xx_init (void)
-+{
-+	int rc;
-+
-+	TAPE_DBF_AREA = debug_register ( "tape_34xx", 1, 2, 4*sizeof(long));
-+	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
-+
-+	DBF_EVENT(3, "34xx init: $Revision: 1.9.4.5 $\n");
-+	/* Register discipline. */
-+	rc = tape_register_discipline(&tape_discipline_3480);
-+	if (rc == 0) {
-+		rc = tape_register_discipline(&tape_discipline_3490);
-+		if (rc)
-+			tape_unregister_discipline(&tape_discipline_3480);
-+	}
-+	if (rc)
-+		DBF_EVENT(3, "34xx init failed\n");
-+	else
-+		DBF_EVENT(3, "34xx registered\n");
-+	return rc;
-+}
-+
-+void
-+tape_34xx_exit(void)
-+{
-+	tape_unregister_discipline(&tape_discipline_3480);
-+	tape_unregister_discipline(&tape_discipline_3490);
-+	debug_unregister(TAPE_DBF_AREA);
-+}
-+
-+MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH");
-+MODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape "
-+		   "device driver ($Revision: 1.9.4.5 $)");
-+MODULE_LICENSE("GPL");
++int sclp_rw_init(void);
++struct sclp_buffer *sclp_make_buffer(void *, unsigned short, unsigned short);
++void *sclp_unmake_buffer(struct sclp_buffer *);
++int sclp_buffer_space(struct sclp_buffer *);
++int sclp_write(struct sclp_buffer *buffer, const unsigned char *, int, int);
++void sclp_emit_buffer(struct sclp_buffer *,void (*)(struct sclp_buffer *,int));
++void sclp_set_columns(struct sclp_buffer *, unsigned short);
++void sclp_set_htab(struct sclp_buffer *, unsigned short);
++int sclp_chars_in_buffer(struct sclp_buffer *);
 +
-+module_init(tape_34xx_init);
-+module_exit(tape_34xx_exit);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_block.c drivers/s390/char/tape_block.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_block.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/tape_block.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,678 @@
++#endif	/* __SCLP_RW_H__ */
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/sclp_tty.c drivers/s390/char/sclp_tty.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/sclp_tty.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/sclp_tty.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,828 @@
 +/*
-+ *  drivers/s390/char/tape_block.c
-+ *    block device frontend for tape device driver
++ *  drivers/s390/char/sclp_tty.c
++ *    SCLP line mode terminal driver.
 + *
-+ *  S390 and zSeries version
-+ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Carsten Otte <cotte at de.ibm.com>
-+ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-+ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
++ *  S390 version
++ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
++ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
 + */
 +
 +#include <linux/config.h>
 +#include <linux/version.h>
-+#include <linux/module.h>
-+#include <linux/blkdev.h>
-+#include <linux/blk.h>
++#include <linux/kmod.h>
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/sched.h>
++#include <linux/wait.h>
++#include <linux/slab.h>
 +#include <linux/interrupt.h>
-+#include <linux/cdrom.h>
-+
-+#include <asm/debug.h>
-+#include <asm/irq.h>
-+#include <asm/s390dyn.h>
-+
-+#define TAPE_DBF_AREA	tape_core_dbf
++#include <asm/uaccess.h>
 +
-+#include "tape.h"
-+#include "tape_std.h"
++#include "ctrlchar.h"
++#include "sclp.h"
++#include "sclp_rw.h"
++#include "sclp_tty.h"
 +
-+#define PRINTK_HEADER "TBLOCK:"
++#define SCLP_TTY_PRINT_HEADER "sclp tty driver: "
 +
-+#define TAPEBLOCK_DEVFSMODE	0060644	/* brwxrw-rw- */
-+#define TAPEBLOCK_MAX_SEC	100
-+#define TAPEBLOCK_MIN_REQUEUE   3
++/*
++ * size of a buffer that collects single characters coming in
++ * via sclp_tty_put_char()
++ */
++#define SCLP_TTY_BUF_SIZE 512
 +
 +/*
-+ * file operation structure for tape block frontend
++ * There is exactly one SCLP terminal, so we can keep things simple
++ * and allocate all variables statically.
 + */
-+static int tapeblock_open(struct inode *, struct file *);
-+static int tapeblock_release(struct inode *, struct file *);
-+static int tapeblock_ioctl(
-+	struct inode *, struct file *, unsigned int, unsigned long);
 +
-+static struct block_device_operations tapeblock_bdops = {
-+	.owner		= THIS_MODULE,
-+	.open		= tapeblock_open,
-+	.release	= tapeblock_release,
-+	.ioctl          = tapeblock_ioctl,
-+};
++/* Lock to guard over changes to global variables. */
++static spinlock_t sclp_tty_lock;
++/* List of free pages that can be used for console output buffering. */
++static struct list_head sclp_tty_pages;
++/* List of full struct sclp_buffer structures ready for output. */
++static struct list_head sclp_tty_outqueue;
++/* Counter how many buffers are emitted. */
++static int sclp_tty_buffer_count;
++/* Pointer to current console buffer. */
++static struct sclp_buffer *sclp_ttybuf;
++/* Timer for delayed output of console messages. */
++static struct timer_list sclp_tty_timer;
++/* Waitqueue to wait for buffers to get empty. */
++static wait_queue_head_t sclp_tty_waitq;
 +
-+int tapeblock_major = 0;
++static struct tty_struct *sclp_tty;
++static unsigned char sclp_tty_chars[SCLP_TTY_BUF_SIZE];
++static unsigned short int sclp_tty_chars_count;
 +
-+/*
-+ * Some helper inlines
-+ */
-+static inline int tapeblock_size(int minor) {
-+	return blk_size[tapeblock_major][minor];
++static struct tty_driver sclp_tty_driver;
++static struct tty_struct * sclp_tty_table[1];
++static struct termios * sclp_tty_termios[1];
++static struct termios * sclp_tty_termios_locked[1];
++static int sclp_tty_refcount = 0;
++
++extern struct termios  tty_std_termios;
++
++static struct sclp_ioctls sclp_ioctls;
++static struct sclp_ioctls sclp_ioctls_init =
++{
++	8,			/* 1 hor. tab. = 8 spaces */
++	0,			/* no echo of input by this driver */
++	80,			/* 80 characters/line */
++	1,			/* write after 1/10 s without final new line */
++	MAX_KMEM_PAGES,		/* quick fix: avoid __alloc_pages */
++	MAX_KMEM_PAGES,		/* take 32/64 pages from kernel memory, */
++	0,			/* do not convert to lower case */
++	0x6c			/* to seprate upper and lower case */
++				/* ('%' in EBCDIC) */
++};
++
++/* This routine is called whenever we try to open a SCLP terminal. */
++static int
++sclp_tty_open(struct tty_struct *tty, struct file *filp)
++{
++	sclp_tty = tty;
++	tty->driver_data = NULL;
++	tty->low_latency = 0;
++	return 0;
 +}
-+static inline int tapeblock_ssize(int minor) {
-+	return blksize_size[tapeblock_major][minor];
++
++/* This routine is called when the SCLP terminal is closed. */
++static void
++sclp_tty_close(struct tty_struct *tty, struct file *filp)
++{
++	if (tty->count > 1)
++		return;
++	sclp_tty = NULL;
 +}
-+static inline int tapeblock_hw_ssize(int minor) {
-+	return hardsect_size[tapeblock_major][minor];
++
++/* execute commands to control the i/o behaviour of the SCLP tty at runtime */
++static int
++sclp_tty_ioctl(struct tty_struct *tty, struct file * file,
++	       unsigned int cmd, unsigned long arg)
++{
++	unsigned long flags;
++	unsigned int obuf;
++	int check;
++	int rc;
++
++	if (tty->flags & (1 << TTY_IO_ERROR))
++		return -EIO;
++	rc = 0;
++	check = 0;
++	switch (cmd) {
++	case TIOCSCLPSHTAB:
++		/* set width of horizontal tab	*/
++		if (get_user(sclp_ioctls.htab, (unsigned short *) arg))
++			rc = -EFAULT;
++		else
++			check = 1;
++		break;
++	case TIOCSCLPGHTAB:
++		/* get width of horizontal tab	*/
++		if (put_user(sclp_ioctls.htab, (unsigned short *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPSECHO:
++		/* enable/disable echo of input */
++		if (get_user(sclp_ioctls.echo, (unsigned char *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPGECHO:
++		/* Is echo of input enabled ?  */
++		if (put_user(sclp_ioctls.echo, (unsigned char *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPSCOLS:
++		/* set number of columns for output  */
++		if (get_user(sclp_ioctls.columns, (unsigned short *) arg))
++			rc = -EFAULT;
++		else
++			check = 1;
++		break;
++	case TIOCSCLPGCOLS:
++		/* get number of columns for output  */
++		if (put_user(sclp_ioctls.columns, (unsigned short *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPSNL:
++		/* enable/disable writing without final new line character  */
++		if (get_user(sclp_ioctls.final_nl, (signed char *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPGNL:
++		/* Is writing without final new line character enabled ?  */
++		if (put_user(sclp_ioctls.final_nl, (signed char *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPSOBUF:
++		/*
++		 * set the maximum buffers size for output, will be rounded
++		 * up to next 4kB boundary and stored as number of SCCBs
++		 * (4kB Buffers) limitation: 256 x 4kB
++		 */
++		if (get_user(obuf, (unsigned int *) arg) == 0) {
++			if (obuf & 0xFFF)
++				sclp_ioctls.max_sccb = (obuf >> 12) + 1;
++			else
++				sclp_ioctls.max_sccb = (obuf >> 12);
++		} else
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPGOBUF:
++		/* get the maximum buffers size for output  */
++		obuf = sclp_ioctls.max_sccb << 12;
++		if (put_user(obuf, (unsigned int *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPGKBUF:
++		/* get the number of buffers got from kernel at startup */
++		if (put_user(sclp_ioctls.kmem_sccb, (unsigned short *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPSCASE:
++		/* enable/disable conversion from upper to lower case */
++		if (get_user(sclp_ioctls.tolower, (unsigned char *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPGCASE:
++		/* Is conversion from upper to lower case of input enabled? */
++		if (put_user(sclp_ioctls.tolower, (unsigned char *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPSDELIM:
++		/*
++		 * set special character used for separating upper and
++		 * lower case, 0x00 disables this feature
++		 */
++		if (get_user(sclp_ioctls.delim, (unsigned char *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPGDELIM:
++		/*
++		 * get special character used for separating upper and
++		 * lower case, 0x00 disables this feature
++		 */
++		if (put_user(sclp_ioctls.delim, (unsigned char *) arg))
++			rc = -EFAULT;
++		break;
++	case TIOCSCLPSINIT:
++		/* set initial (default) sclp ioctls  */
++		sclp_ioctls = sclp_ioctls_init;
++		check = 1;
++		break;
++	default:
++		rc = -ENOIOCTLCMD;
++		break;
++	}
++	if (check) {
++		spin_lock_irqsave(&sclp_tty_lock, flags);
++		if (sclp_ttybuf != NULL) {
++			sclp_set_htab(sclp_ttybuf, sclp_ioctls.htab);
++			sclp_set_columns(sclp_ttybuf, sclp_ioctls.columns);
++		}
++		spin_unlock_irqrestore(&sclp_tty_lock, flags);
++	}
++	return rc;
 +}
 +
 +/*
-+ * Post finished request.
++ * This routine returns the numbers of characters the tty driver
++ * will accept for queuing to be written.  This number is subject
++ * to change as output buffers get emptied, or if the output flow
++ * control is acted. This is not an exact number because not every
++ * character needs the same space in the sccb. The worst case is
++ * a string of newlines. Every newlines creates a new mto which
++ * needs 8 bytes.
 + */
-+static inline void
-+tapeblock_end_request(struct request *req, int uptodate)
++static int
++sclp_tty_write_room (struct tty_struct *tty)
 +{
-+	if (end_that_request_first(req, uptodate, "tBLK"))
-+		BUG();
-+	end_that_request_last(req);
++	unsigned long flags;
++	struct list_head *l;
++	int count;
++
++	spin_lock_irqsave(&sclp_tty_lock, flags);
++	count = 0;
++	if (sclp_ttybuf != NULL)
++		count = sclp_buffer_space(sclp_ttybuf) / sizeof(struct mto);
++	list_for_each(l, &sclp_tty_pages)
++		count += NR_EMPTY_MTO_PER_SCCB;
++	spin_unlock_irqrestore(&sclp_tty_lock, flags);
++	return count;
 +}
 +
 +static void
-+__tapeblock_end_request(struct tape_request *ccw_req, void *data)
++sclp_ttybuf_callback(struct sclp_buffer *buffer, int rc)
 +{
-+	struct tape_device *device;
-+	struct request     *req;
-+
-+	device = ccw_req->device;
-+	req    = (struct request *) data;
-+	if(!device || !req)
-+		BUG();
-+
-+	tapeblock_end_request(req, ccw_req->rc == 0);
-+	if (ccw_req->rc == 0)
-+		/* Update position. */
-+		device->blk_data.block_position = 
-+			(req->sector + req->nr_sectors) >> TAPEBLOCK_HSEC_S2B;
-+	else
-+		/* We lost the position information due to an error. */
-+		device->blk_data.block_position = -1;
-+
-+	device->discipline->free_bread(ccw_req);
++	unsigned long flags;
++	struct sclp_buffer *next;
++	void *page;
 +
-+	if (!list_empty(&device->req_queue) ||
-+	    !list_empty(&device->blk_data.request_queue.queue_head))
-+		tasklet_schedule(&device->blk_data.tasklet);
++	/* Ignore return code - because tty-writes aren't critical,
++	   we do without a sophisticated error recovery mechanism.  */
++	page = sclp_unmake_buffer(buffer);
++	spin_lock_irqsave(&sclp_tty_lock, flags);
++	/* Remove buffer from outqueue */
++	list_del(&buffer->list);
++	sclp_tty_buffer_count--;
++	list_add_tail((struct list_head *) page, &sclp_tty_pages);
++	/* Check if there is a pending buffer on the out queue. */
++	next = NULL;
++	if (!list_empty(&sclp_tty_outqueue))
++		next = list_entry(sclp_tty_outqueue.next,
++				  struct sclp_buffer, list);
++	spin_unlock_irqrestore(&sclp_tty_lock, flags);
++	if (next != NULL)
++		sclp_emit_buffer(next, sclp_ttybuf_callback);
++	wake_up(&sclp_tty_waitq);
++	/* check if the tty needs a wake up call */
++	if (sclp_tty != NULL) {
++		if ((sclp_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
++		    sclp_tty->ldisc.write_wakeup)
++			(sclp_tty->ldisc.write_wakeup)(sclp_tty);
++		wake_up_interruptible(&sclp_tty->write_wait);
++	}
 +}
 +
-+/*
-+ * Fetch requests from block device queue.
-+ */
 +static inline void
-+__tape_process_blk_queue(struct tape_device *device, struct list_head *new_req)
++__sclp_ttybuf_emit(struct sclp_buffer *buffer)
 +{
-+	request_queue_t     *queue;
-+	struct list_head    *l;
-+	struct request      *req;
-+	struct tape_request *ccw_req;
-+	int                  nr_queued;
-+
-+	if (!TAPE_BLOCKDEV(device)) {
-+		PRINT_WARN("can't process queue. Not a tape blockdevice.\n");
-+		return;
-+	}
-+
-+	nr_queued = 0;
-+	queue     = &device->blk_data.request_queue;
-+
-+	/* Count number of requests on ccw queue. */
-+	list_for_each(l, &device->req_queue)
-+		nr_queued++;
-+
-+	while (
-+		!queue->plugged &&
-+		!list_empty(&queue->queue_head) &&
-+		nr_queued < TAPEBLOCK_MIN_REQUEUE
-+	) {
-+		/* tape_block_next_request(queue); */
-+		req = blkdev_entry_next_request(&queue->queue_head);
++	unsigned long flags;
++	int count;
 +
-+		if (req->cmd == WRITE) {
-+			DBF_EVENT(1, "TBLOCK: Rejecting write request\n");
-+			blkdev_dequeue_request(req);
-+			tapeblock_end_request(req, 0);
-+			continue;
-+		}
-+		ccw_req = device->discipline->bread(device, req);
-+		if (IS_ERR(ccw_req)) {
-+			if (PTR_ERR(ccw_req) == -ENOMEM)
-+				break; /* don't try again */
-+			DBF_EVENT(1, "TBLOCK: bread failed\n");
-+			blkdev_dequeue_request(req);
-+			tapeblock_end_request(req, 0);
-+			continue;
-+		}
-+		blkdev_dequeue_request(req);
-+		ccw_req->callback      = __tapeblock_end_request;
-+		ccw_req->callback_data = (void *) req;
-+		ccw_req->retries       = TAPEBLOCK_RETRIES;
++	spin_lock_irqsave(&sclp_tty_lock, flags);
++	list_add_tail(&buffer->list, &sclp_tty_outqueue);
++	count = sclp_tty_buffer_count++;
++	spin_unlock_irqrestore(&sclp_tty_lock, flags);
 +
-+		list_add_tail(&ccw_req->list, new_req);
-+		nr_queued++;
-+	}
++	if (count == 0)
++		sclp_emit_buffer(buffer, sclp_ttybuf_callback);
 +}
 +
 +/*
-+ * Feed requests to the tape device.
++ * When this routine is called from the timer then we flush the
++ * temporary write buffer.
 + */
-+static inline int
-+tape_queue_requests(struct tape_device *device, struct list_head *new_req)
++static void
++sclp_tty_timeout(unsigned long data)
 +{
-+	struct list_head *l, *n;
-+	struct tape_request *ccw_req;
-+	struct request *req;
-+	int rc, fail;
++	unsigned long flags;
++	struct sclp_buffer *buf;
 +
-+	fail = 0;
-+	list_for_each_safe(l, n, new_req) {
-+		ccw_req = list_entry(l, struct tape_request, list);
-+		list_del(&ccw_req->list);
++	spin_lock_irqsave(&sclp_tty_lock, flags);
++	buf = sclp_ttybuf;
++	sclp_ttybuf = NULL;
++	spin_unlock_irqrestore(&sclp_tty_lock, flags);
 +
-+		rc = tape_do_io_async(device, ccw_req);
-+		if (rc) {
-+			/*
-+			 * Start/enqueueing failed. No retries in
-+			 * this case.
-+			 */
-+			DBF_EVENT(5, "enqueueing failed\n");
-+			req = (struct request *) ccw_req->callback_data;
-+			tapeblock_end_request(req, 0);
-+			device->discipline->free_bread(ccw_req);
-+			fail = 1;
-+		}
++	if (buf != NULL) {
++		__sclp_ttybuf_emit(buf);
 +	}
-+	return fail;
 +}
 +
 +/*
-+ * Tape request queue function. Called from ll_rw_blk.c
++ * Write a string to the sclp tty.
 + */
 +static void
-+tapeblock_request_fn(request_queue_t *queue)
++sclp_tty_write_string(const unsigned char *str, int count, int from_user)
 +{
-+	struct list_head    new_req;
-+	struct tape_device *device;
-+
-+	device = (struct tape_device *) queue->queuedata;
-+	if(device == NULL)
-+		BUG();
++	unsigned long flags;
++	void *page;
++	int written;
++	struct sclp_buffer *buf;
 +
-+	while (!list_empty(&queue->queue_head)) {
-+		INIT_LIST_HEAD(&new_req);
-+		spin_lock(get_irq_lock(device->devinfo.irq));
-+		__tape_process_blk_queue(device, &new_req);
-+		spin_unlock(get_irq_lock(device->devinfo.irq));
++	if (count <= 0)
++		return;
++	spin_lock_irqsave(&sclp_tty_lock, flags);
++	do {
++		/* Create a sclp output buffer if none exists yet */
++		if (sclp_ttybuf == NULL) {
++			while (list_empty(&sclp_tty_pages)) {
++				spin_unlock_irqrestore(&sclp_tty_lock, flags);
++				if (in_interrupt())
++					sclp_sync_wait();
++				else
++					wait_event(sclp_tty_waitq,
++						!list_empty(&sclp_tty_pages));
++				spin_lock_irqsave(&sclp_tty_lock, flags);
++			}
++			page = sclp_tty_pages.next;
++			list_del((struct list_head *) page);
++			sclp_ttybuf = sclp_make_buffer(page,
++						       sclp_ioctls.columns,
++						       sclp_ioctls.htab);
++		}
++		/* try to write the string to the current output buffer */
++		written = sclp_write(sclp_ttybuf, str, count, from_user);
++		if (written == -EFAULT || written == count)
++			break;
 +		/*
-+		 * Now queue the new request to the tape. This needs to be
-+		 * done without the device lock held.
++		 * Not all characters could be written to the current
++		 * output buffer. Emit the buffer, create a new buffer
++		 * and then output the rest of the string.
 +		 */
-+		if (tape_queue_requests(device, &new_req) == 0)
-+			/* All requests queued. Thats enough for now. */
-+			break;
++		buf = sclp_ttybuf;
++		sclp_ttybuf = NULL;
++		spin_unlock_irqrestore(&sclp_tty_lock, flags);
++		__sclp_ttybuf_emit(buf);
++		spin_lock_irqsave(&sclp_tty_lock, flags);
++		str += written;
++		count -= written;
++	} while (count > 0);
++	/* Setup timer to output current console buffer after 1/10 second */
++	if (sclp_ioctls.final_nl) {
++		if (sclp_ttybuf != NULL &&
++		    sclp_chars_in_buffer(sclp_ttybuf) != 0 &&
++		    !timer_pending(&sclp_tty_timer)) {
++			init_timer(&sclp_tty_timer);
++			sclp_tty_timer.function = sclp_tty_timeout;
++			sclp_tty_timer.data = 0UL;
++			sclp_tty_timer.expires = jiffies + HZ/10;
++			add_timer(&sclp_tty_timer);
++		}
++	} else {
++		if (sclp_ttybuf != NULL &&
++		    sclp_chars_in_buffer(sclp_ttybuf) != 0) {
++			buf = sclp_ttybuf;
++			sclp_ttybuf = NULL;
++			spin_unlock_irqrestore(&sclp_tty_lock, flags);
++			__sclp_ttybuf_emit(buf);
++			spin_lock_irqsave(&sclp_tty_lock, flags);
++		}
 +	}
++	spin_unlock_irqrestore(&sclp_tty_lock, flags);
 +}
 +
 +/*
-+ * Returns block frontend request queue for a tape device.
-+ * FIXME: on shutdown make sure ll_rw_blk can put requests on a dead queue.
-+ */
-+static request_queue_t *
-+tapeblock_get_queue(kdev_t kdev)
-+{
-+	struct tape_device *device;
-+	request_queue_t    *queue;
-+
-+	if (major(kdev) != tapeblock_major)
-+		return NULL;
-+
-+	device = tape_get_device(minor(kdev) >> 1);
-+	if (IS_ERR(device))
-+		return NULL;
-+
-+	queue = &device->blk_data.request_queue;
-+	tape_put_device(device);
-+	return queue;
-+}
-+
-+/*
-+ * Acquire the device lock and process queues for the device.
++ * This routine is called by the kernel to write a series of characters to the
++ * tty device. The characters may come from user space or kernel space. This
++ * routine will return the number of characters actually accepted for writing.
 + */
-+static void
-+tapeblock_tasklet(unsigned long data)
++static int
++sclp_tty_write(struct tty_struct *tty, int from_user,
++	       const unsigned char *buf, int count)
 +{
-+	struct list_head    new_req;
-+	struct tape_device *device;
-+
-+	device = (struct tape_device *) data;
-+	while (!list_empty(&device->blk_data.request_queue.queue_head)) {
-+		INIT_LIST_HEAD(&new_req);
-+		spin_lock_irq(get_irq_lock(device->devinfo.irq));
-+		__tape_process_blk_queue(device, &new_req);
-+		spin_unlock_irq(get_irq_lock(device->devinfo.irq));
-+		/*
-+		 * Now queue the new request to the tape. This needs to be
-+		 * done without the device lock held.
-+		 */
-+		if (tape_queue_requests(device, &new_req) == 0)
-+			/* All requests queued. Thats enough for now. */
-+			break;
++	if (sclp_tty_chars_count > 0) {
++		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
++		sclp_tty_chars_count = 0;
 +	}
++	sclp_tty_write_string(buf, count, from_user);
++	return count;
 +}
 +
 +/*
-+ * Create block directory with disc entries
++ * This routine is called by the kernel to write a single character to the tty
++ * device. If the kernel uses this routine, it must call the flush_chars()
++ * routine (if defined) when it is done stuffing characters into the driver.
++ *
++ * Characters provided to sclp_tty_put_char() are buffered by the SCLP driver.
++ * If the given character is a '\n' the contents of the SCLP write buffer
++ * - including previous characters from sclp_tty_put_char() and strings from
++ * sclp_write() without final '\n' - will be written.
 + */
-+static int
-+tapeblock_mkdevfstree (struct tape_device *device)
++static void
++sclp_tty_put_char(struct tty_struct *tty, unsigned char ch)
 +{
-+#ifdef CONFIG_DEVFS_FS
-+	device->blk_data.devfs_block_dir = 
-+		devfs_mk_dir (device->devfs_dir, "block", device);
-+	if (device->blk_data.devfs_block_dir == 0)
-+		return -ENOENT;
-+	device->blk_data.devfs_disc = 
-+		devfs_register(device->blk_data.devfs_block_dir,
-+			       "disc", DEVFS_FL_DEFAULT,
-+			       tapeblock_major, device->first_minor,
-+			       TAPEBLOCK_DEVFSMODE, &tapeblock_bdops, device);
-+	if (device->blk_data.devfs_disc == NULL) {
-+		devfs_unregister(device->blk_data.devfs_block_dir);
-+		return -ENOENT;
++	sclp_tty_chars[sclp_tty_chars_count++] = ch;
++	if (ch == '\n' || sclp_tty_chars_count >= SCLP_TTY_BUF_SIZE) {
++		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
++		sclp_tty_chars_count = 0;
 +	}
-+#endif
-+	return 0;
 +}
 +
 +/*
-+ * Remove devfs entries
++ * This routine is called by the kernel after it has written a series of
++ * characters to the tty device using put_char().
 + */
 +static void
-+tapeblock_rmdevfstree (struct tape_device *device)
++sclp_tty_flush_chars(struct tty_struct *tty)
 +{
-+#ifdef CONFIG_DEVFS_FS
-+	if (device->blk_data.devfs_disc)
-+		devfs_unregister(device->blk_data.devfs_disc);
-+	if (device->blk_data.devfs_block_dir)
-+		devfs_unregister(device->blk_data.devfs_block_dir);
-+#endif
++	if (sclp_tty_chars_count > 0) {
++		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
++		sclp_tty_chars_count = 0;
++	}
 +}
 +
 +/*
-+ * This function is called for every new tapedevice
++ * This routine returns the number of characters in the write buffer of the
++ * SCLP driver. The provided number includes all characters that are stored
++ * in the SCCB (will be written next time the SCLP is not busy) as well as
++ * characters in the write buffer (will not be written as long as there is a
++ * final line feed missing).
 + */
-+int
-+tapeblock_setup_device(struct tape_device * device)
-+{
-+	int rc;
-+
-+	/* FIXME: We should be able to sense the sector size */
-+	blk_size[tapeblock_major][device->first_minor]      = 0;
-+	blksize_size[tapeblock_major][device->first_minor]  =
-+	hardsect_size[tapeblock_major][device->first_minor] =
-+		TAPEBLOCK_HSEC_SIZE;
-+
-+	/* Create devfs entries. */
-+	rc = tapeblock_mkdevfstree(device);
-+	if (rc)
-+		return rc;
-+
-+	/* Setup request queue and initialize gendisk for this device. */
-+	device->blk_data.request_queue.queuedata = tape_clone_device(device);
-+
-+
-+	/* As long as the tasklet is running it may access the device */
-+	tasklet_init(&device->blk_data.tasklet, tapeblock_tasklet,
-+		     (unsigned long) tape_clone_device(device));
-+
-+	blk_init_queue(&device->blk_data.request_queue, tapeblock_request_fn);
-+	blk_queue_headactive(&device->blk_data.request_queue, 0);
-+
-+	tape_hotplug_event(device, tapeblock_major, TAPE_HOTPLUG_BLOCK_ADD);
-+
-+	set_device_ro(mk_kdev(tapeblock_major, device->first_minor), 1);
-+	return 0;
-+}
-+
-+void
-+tapeblock_cleanup_device(struct tape_device *device)
++static int
++sclp_tty_chars_in_buffer(struct tty_struct *tty)
 +{
-+	/* Prevent further requests to the block request queue. */
-+	blk_size[tapeblock_major][device->first_minor] = 0;
-+
-+	tapeblock_rmdevfstree(device);
-+
-+	/* With the tasklet gone the reference is gone as well. */
-+	tasklet_kill(&device->blk_data.tasklet);
-+	tape_put_device(device);
-+
-+	/* Cleanup the request queue. */
-+	blk_cleanup_queue(&device->blk_data.request_queue);
-+
-+	/* Remove reference in private data */
-+	device->blk_data.request_queue.queuedata = NULL;
-+	tape_put_device(device);
++	unsigned long flags;
++	struct list_head *l;
++	struct sclp_buffer *t;
++	int count;
 +
-+	tape_hotplug_event(device, tapeblock_major, TAPE_HOTPLUG_BLOCK_REMOVE);
++	spin_lock_irqsave(&sclp_tty_lock, flags);
++	count = 0;
++	if (sclp_ttybuf != NULL)
++		count = sclp_chars_in_buffer(sclp_ttybuf);
++	list_for_each(l, &sclp_tty_outqueue) {
++		t = list_entry(l, struct sclp_buffer, list);
++		count += sclp_chars_in_buffer(sclp_ttybuf);
++	}
++	spin_unlock_irqrestore(&sclp_tty_lock, flags);
++	return count;
 +}
 +
 +/*
-+ * Detect number of blocks of the tape.
-+ * FIXME: can we extent this to detect the blocks size as well ?
-+ * FIXME: (minor) On 34xx the block id also contains a format specification
-+ *                which is unknown before the block was skipped or read at
-+ *                least once. So detection is sometimes done a second time.
++ * removes all content from buffers of low level driver
 + */
-+int tapeblock_mediumdetect(struct tape_device *device)
++static void
++sclp_tty_flush_buffer(struct tty_struct *tty)
 +{
-+	unsigned int bid;
-+	unsigned int nr_of_blks;
-+	int          rc;
-+
-+	/*
-+	 * Identify the first records format
-+	 */
-+	if((rc = tape_mtop(device, MTFSR, 1)) < 0)
-+		return rc;
-+	if((rc = tape_mtop(device, MTBSR, 1)) < 0)
-+		return rc;
-+
-+	device->blk_data.block_position = 0;
-+	if (tape_std_read_block_id(device, &bid)) {
-+		rc = tape_mtop(device, MTREW, 1);
-+		if (rc) {
-+			device->blk_data.block_position                = -1;
-+			blk_size[tapeblock_major][device->first_minor] =  0;
-+			return rc;
-+		}
-+		bid = 0;
-+	}
-+
-+	if(bid != device->blk_data.start_block_id) {
-+		device->blk_data.start_block_id = bid;
-+		blk_size[tapeblock_major][device->first_minor] =  0;
-+	}
-+
-+	if(blk_size[tapeblock_major][device->first_minor] > 0)
-+		return 0;
-+
-+	PRINT_INFO("Detecting media size...\n");
-+	blk_size[tapeblock_major][device->first_minor] = 0;
-+
-+	rc = tape_mtop(device, MTFSF, 1);
-+	if (rc)
-+		return rc;
-+
-+	rc = tape_mtop(device, MTTELL, 1);
-+	if (rc < 0)
-+		return rc;
-+	nr_of_blks = rc - 1; /* don't count FM */
-+
-+	if (device->blk_data.start_block_id) {
-+		rc = tape_std_seek_block_id(
-+			device,
-+			device->blk_data.start_block_id);
-+	} else {
-+		rc = tape_mtop(device, MTREW, 1);
-+	}
-+	if (rc)
-+		return rc;
-+
-+	rc = tape_mtop(device, MTTELL, 1);
-+	if (rc < 0)
-+		return rc;
-+
-+	/* Don't include start offset */
-+	nr_of_blks -= rc;
-+
-+	PRINT_INFO("Found %i blocks on media\n", nr_of_blks);
-+	if (tapeblock_hw_ssize(device->first_minor) > 1024) {
-+		nr_of_blks *= tapeblock_hw_ssize(device->first_minor) / 1024;
-+	} else {
-+		nr_of_blks /= 1024 / tapeblock_hw_ssize(device->first_minor);
++	if (sclp_tty_chars_count > 0) {
++		sclp_tty_write_string(sclp_tty_chars, sclp_tty_chars_count, 0);
++		sclp_tty_chars_count = 0;
 +	}
-+	PRINT_INFO("Tape block device size is %i KB\n", nr_of_blks);
-+	blk_size[tapeblock_major][device->first_minor] = nr_of_blks;
-+
-+	return 0;
 +}
 +
 +/*
-+ * This function has to be called whenever a new medium has been inserted
-+ * into the drive.
++ * push input to tty
 + */
-+void
-+tapeblock_medium_change(struct tape_device *device) {
-+	device->blk_data.start_block_id                = 0;
-+	blk_size[tapeblock_major][device->first_minor] = 0;
++static void
++sclp_tty_input(unsigned char* buf, unsigned int count)
++{
++	unsigned int cchar;
++
++	/*
++	 * If this tty driver is currently closed
++	 * then throw the received input away.
++	 */
++	if (sclp_tty == NULL)
++		return;
++	cchar = ctrlchar_handle(buf, count, sclp_tty);
++	switch (cchar & CTRLCHAR_MASK) {
++	case CTRLCHAR_SYSRQ:
++		break;
++	case CTRLCHAR_CTRL:
++		sclp_tty->flip.count++;
++		*sclp_tty->flip.flag_buf_ptr++ = TTY_NORMAL;
++		*sclp_tty->flip.char_buf_ptr++ = cchar;
++		tty_flip_buffer_push(sclp_tty);
++		break;
++	case CTRLCHAR_NONE:
++		/* send (normal) input to line discipline */
++		memcpy(sclp_tty->flip.char_buf_ptr, buf, count);
++		if (count < 2 ||
++		    (strncmp ((const char *) buf + count - 2, "^n", 2) &&
++		     strncmp ((const char *) buf + count - 2, "\0252n", 2))) {
++			sclp_tty->flip.char_buf_ptr[count] = '\n';
++			count++;
++		} else
++			count -= 2;
++		memset(sclp_tty->flip.flag_buf_ptr, TTY_NORMAL, count);
++		sclp_tty->flip.char_buf_ptr += count;
++		sclp_tty->flip.flag_buf_ptr += count;
++		sclp_tty->flip.count += count;
++		tty_flip_buffer_push(sclp_tty);
++		break;
++	}
 +}
 +
 +/*
-+ * Block frontend tape device open function.
++ * get a EBCDIC string in upper/lower case,
++ * find out characters in lower/upper case separated by a special character,
++ * modifiy original string,
++ * returns length of resulting string
 + */
-+int
-+tapeblock_open(struct inode *inode, struct file *filp) {
-+	struct tape_device *device;
-+	int                 rc;
-+
-+	if (major(filp->f_dentry->d_inode->i_rdev) != tapeblock_major)
-+		return -ENODEV;
++static int
++sclp_switch_cases(unsigned char *buf, int count,
++		  unsigned char delim, int tolower)
++{
++	unsigned char *ip, *op;
++	int toggle;
 +
-+	MOD_INC_USE_COUNT;
-+	device = tape_get_device(minor(filp->f_dentry->d_inode->i_rdev) >> 1);
-+	if (IS_ERR(device)) {
-+		MOD_DEC_USE_COUNT;
-+		return PTR_ERR(device);
++	/* initially changing case is off */
++	toggle = 0;
++	ip = op = buf;
++	while (count-- > 0) {
++		/* compare with special character */
++		if (*ip == delim) {
++			/* followed by another special character? */
++			if (count && ip[1] == delim) {
++				/*
++				 * ... then put a single copy of the special
++				 * character to the output string
++				 */
++				*op++ = *ip++;
++				count--;
++			} else
++				/*
++				 * ... special character follower by a normal
++				 * character toggles the case change behaviour
++				 */
++				toggle = ~toggle;
++			/* skip special character */
++			ip++;
++		} else
++			/* not the special character */
++			if (toggle)
++				/* but case switching is on */
++				if (tolower)
++					/* switch to uppercase */
++					*op++ = _ebc_toupper[(int) *ip++];
++				else
++					/* switch to lowercase */
++					*op++ = _ebc_tolower[(int) *ip++];
++			else
++				/* no case switching, copy the character */
++				*op++ = *ip++;
 +	}
++	/* return length of reformatted string. */
++	return op - buf;
++}
 +
-+	DBF_EVENT(6, "TBLOCK: open:  %x\n", device->first_minor);
-+
-+	if(device->required_tapemarks) {
-+		DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
-+		PRINT_ERR("TBLOCK: Refusing to open tape with missing"
-+			" end of file marks.\n");
-+		tape_put_device(device);
-+		MOD_DEC_USE_COUNT;
-+		return -EPERM;
-+	}
++static void
++sclp_get_input(unsigned char *start, unsigned char *end)
++{
++	int count;
 +
-+	rc = tape_open(device);
-+	if (rc == 0) {
-+		rc = tape_assign(device, TAPE_STATUS_ASSIGN_A);
-+		if (rc == 0) {
-+			rc = tapeblock_mediumdetect(device);
-+			if (rc == 0) {
-+				TAPE_SET_STATE(device, TAPE_STATUS_BLOCKDEV);
-+				tape_put_device(device);
-+				return 0;
-+			}
-+			tape_unassign(device, TAPE_STATUS_ASSIGN_A);
-+		}
-+		tape_release(device);
-+	}
-+	tape_put_device(device);
-+	MOD_DEC_USE_COUNT;
-+	return rc;
-+}
++	count = end - start;
++	/*
++	 * if set in ioctl convert EBCDIC to lower case
++	 * (modify original input in SCCB)
++	 */
++	if (sclp_ioctls.tolower)
++		EBC_TOLOWER(start, count);
 +
-+/*
-+ * Block frontend tape device release function.
-+ */
-+int
-+tapeblock_release(struct inode *inode, struct file *filp) {
-+	struct tape_device *device;
++	/*
++	 * if set in ioctl find out characters in lower or upper case
++	 * (depends on current case) separated by a special character,
++	 * works on EBCDIC
++	 */
++	if (sclp_ioctls.delim)
++		count = sclp_switch_cases(start, count,
++					  sclp_ioctls.delim,
++					  sclp_ioctls.tolower);
 +
-+	device = tape_get_device(minor(inode->i_rdev) >> 1);
++	/* convert EBCDIC to ASCII (modify original input in SCCB) */
++	sclp_ebcasc_str(start, count);
 +
-+	DBF_EVENT(4, "TBLOCK: release %i\n", device->first_minor);
++	/* if set in ioctl write operators input to console  */
++	if (sclp_ioctls.echo)
++		sclp_tty_write(sclp_tty, 0, start, count);
 +
-+	/* Remove all buffers at device close. */
-+	/* FIXME: can we do that a tape unload ? */
-+	invalidate_buffers(inode->i_rdev);
++	/* transfer input to high level driver */
++	sclp_tty_input(start, count);
++}
 +
-+	if (device->blk_data.start_block_id) {
-+		tape_std_seek_block_id(device, device->blk_data.start_block_id);
-+	} else {
-+		tape_mtop(device, MTREW, 1);
-+	}
-+	TAPE_CLEAR_STATE(device, TAPE_STATUS_BLOCKDEV);
-+	tape_unassign(device, TAPE_STATUS_ASSIGN_A);
-+	tape_release(device);
-+	tape_put_device(device);
-+	MOD_DEC_USE_COUNT;
++static inline struct gds_vector *
++find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id)
++{
++	struct gds_vector *vec;
 +
-+	return 0;
++	for (vec = start; vec < end; (void *) vec += vec->length)
++		if (vec->gds_id == id)
++			return vec;
++	return NULL;
 +}
 +
-+int
-+tapeblock_ioctl(
-+	struct inode	*inode,
-+	struct file	*file,
-+	unsigned int	 command,
-+	unsigned long	 arg
-+) {
-+	int	 rc     = 0;
-+	int	 minor  = minor(inode->i_rdev);
++static inline struct gds_subvector *
++find_gds_subvector(struct gds_subvector *start,
++		   struct gds_subvector *end, u8 key)
++{
++	struct gds_subvector *subvec;
 +
-+	DBF_EVENT(6, "tapeblock_ioctl(%x)\n", command);
++	for (subvec = start; subvec < end; (void *) subvec += subvec->length)
++		if (subvec->key == key)
++			return subvec;
++	return NULL;
++}
 +
-+	switch(command) {
-+		case BLKSSZGET:
-+			if(put_user(tapeblock_ssize(minor), (int *) arg))
-+				rc = -EFAULT;
-+			break;
-+		case BLKGETSIZE:
-+			if(
-+				put_user(
-+					tapeblock_size(minor),
-+					(unsigned long *) arg
-+				)
-+			)
-+				rc = -EFAULT;
-+			break;
-+#ifdef BLKGETSIZE64
-+		case BLKGETSIZE64:
-+			if(put_user(tapeblock_size(minor) << 9, (u64 *) arg))
-+				rc = -EFAULT;
++static inline void
++sclp_eval_selfdeftextmsg(struct gds_subvector *start,
++			 struct gds_subvector *end)
++{
++	struct gds_subvector *subvec;
++
++	subvec = start;
++	while (subvec < end) {
++		subvec = find_gds_subvector(subvec, end, 0x30);
++		if (!subvec)
 +			break;
-+#endif
-+		case CDROMMULTISESSION:
-+		case CDROMREADTOCENTRY:
-+			/* No message for these... */
-+			rc = -EINVAL;
++		sclp_get_input((unsigned char *)(subvec + 1),
++			       (unsigned char *) subvec + subvec->length);
++		(void *) subvec += subvec->length;
++	}
++}
++
++static inline void
++sclp_eval_textcmd(struct gds_subvector *start,
++		  struct gds_subvector *end)
++{
++	struct gds_subvector *subvec;
++
++	subvec = start;
++	while (subvec < end) {
++		subvec = find_gds_subvector(subvec, end,
++					    GDS_KEY_SelfDefTextMsg);
++		if (!subvec)
 +			break;
-+		default:
-+			PRINT_WARN("invalid ioctl 0x%x\n", command);
-+			rc = -EINVAL;
++		sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1),
++					 (void *)subvec + subvec->length);
++		(void *) subvec += subvec->length;
 +	}
-+	return rc;
 +}
 +
-+/*
-+ * Initialize block device frontend.
-+ */
-+int
-+tapeblock_init(void) 
++static inline void
++sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end)
 +{
-+	int rc;
++	struct gds_vector *vec;
 +
-+	/* Register the tape major number to the kernel */
-+#ifdef CONFIG_DEVFS_FS
-+	if (tapeblock_major == 0)
-+		tapeblock_major = devfs_alloc_major(DEVFS_SPECIAL_BLK);
-+#endif
-+	rc = register_blkdev(tapeblock_major, "tBLK", &tapeblock_bdops);
-+	if (rc < 0) {
-+		PRINT_ERR("can't get major %d for block device\n",
-+			  tapeblock_major);
-+		return rc;
++	vec = start;
++	while (vec < end) {
++		vec = find_gds_vector(vec, end, GDS_ID_TextCmd);
++		if (!vec)
++			break;
++		sclp_eval_textcmd((struct gds_subvector *)(vec + 1),
++				  (void *) vec + vec->length);
++		(void *) vec += vec->length;
 +	}
-+	if(tapeblock_major == 0)
-+		tapeblock_major = rc;
++}
 +
-+	/* Allocate memory for kernel block device tables */
-+	rc = -ENOMEM;
-+	blk_size[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL);
-+	if(blk_size[tapeblock_major] == NULL)
-+		goto tapeblock_init_fail;
-+	memset(blk_size[tapeblock_major], 0, 256*sizeof(int));
-+	blksize_size[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL);
-+	if(blksize_size[tapeblock_major] == NULL)
-+		goto tapeblock_init_fail;
-+	memset(blksize_size[tapeblock_major], 0, 256*sizeof(int));
-+	hardsect_size[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL);
-+	if(hardsect_size[tapeblock_major] == NULL)
-+		goto tapeblock_init_fail;
-+	memset(hardsect_size[tapeblock_major], 0, 256*sizeof(int));
-+	max_sectors[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL);
-+	if(max_sectors[tapeblock_major] == NULL)
-+		goto tapeblock_init_fail;
-+	memset(max_sectors[tapeblock_major], 0, 256*sizeof(int));
 +
-+	blk_dev[tapeblock_major].queue = tapeblock_get_queue;
++static inline void
++sclp_eval_mdsmu(struct gds_vector *start, void *end)
++{
++	struct gds_vector *vec;
 +
-+	PRINT_INFO("tape gets major %d for block device\n", tapeblock_major);
-+	DBF_EVENT(3, "TBLOCK: major = %d\n", tapeblock_major);
-+	DBF_EVENT(3, "TBLOCK: init ok\n");
++	vec = find_gds_vector(start, end, GDS_ID_CPMSU);
++	if (vec)
++		sclp_eval_cpmsu(vec + 1, (void *) vec + vec->length);
++}
 +
-+	return 0;
++static void
++sclp_tty_receiver(struct evbuf_header *evbuf)
++{
++	struct gds_vector *start, *end, *vec;
 +
-+tapeblock_init_fail:
-+	if(tapeblock_major > 0) {
-+		if(blk_size[tapeblock_major]) {
-+			kfree(blk_size[tapeblock_major]);
-+			blk_size[tapeblock_major] = NULL;
-+		}
-+		if(blksize_size[tapeblock_major]) {
-+			kfree(blksize_size[tapeblock_major]);
-+			blksize_size[tapeblock_major] = NULL;
-+		}
-+		if(hardsect_size[tapeblock_major]) {
-+			kfree(hardsect_size[tapeblock_major]);
-+			hardsect_size[tapeblock_major] = NULL;
-+		}
-+		if(max_sectors[tapeblock_major]) {
-+			kfree(max_sectors[tapeblock_major]);
-+			max_sectors[tapeblock_major] = NULL;
-+		}
-+#ifdef CONFIG_DEVFS_FS
-+		devfs_unregister_blkdev(tapeblock_major, "tBLK");
-+#else
-+		unregister_blkdev(tapeblock_major, "tBLK");
-+#endif
-+		tapeblock_major = -1;
-+	}
++	start = (struct gds_vector *)(evbuf + 1);
++	end = (void *) evbuf + evbuf->length;
++	vec = find_gds_vector(start, end, GDS_ID_MDSMU);
++	if (vec)
++		sclp_eval_mdsmu(vec + 1, (void *) vec + vec->length);
++}
 +
-+	DBF_EVENT(3, "TBLOCK: init failed(%d)\n", rc);
-+	return rc;
++static void
++sclp_tty_state_change(struct sclp_register *reg)
++{
 +}
 +
-+/*
-+ * Deregister major for block device frontend
-+ */
++static struct sclp_register sclp_input_event =
++{
++	.receive_mask = EvTyp_OpCmd_Mask | EvTyp_PMsgCmd_Mask,
++	.state_change_fn = sclp_tty_state_change,
++	.receiver_fn = sclp_tty_receiver
++};
++
 +void
-+tapeblock_exit(void)
++sclp_tty_init(void)
 +{
-+	if(blk_size[tapeblock_major]) {
-+		kfree(blk_size[tapeblock_major]);
-+		blk_size[tapeblock_major] = NULL;
-+	}
-+	if(blksize_size[tapeblock_major]) {
-+		kfree(blksize_size[tapeblock_major]);
-+		blksize_size[tapeblock_major] = NULL;
++	void *page;
++	int i;
++	int rc;
++
++	if (!CONSOLE_IS_SCLP)
++		return;
++	rc = sclp_rw_init();
++	if (rc != 0) {
++		printk(KERN_ERR SCLP_TTY_PRINT_HEADER
++		       "could not register tty - "
++		       "sclp_rw_init returned %d\n", rc);
++		return;
 +	}
-+	if(hardsect_size[tapeblock_major]) {
-+		kfree(hardsect_size[tapeblock_major]);
-+		hardsect_size[tapeblock_major] = NULL;
++	/* Allocate pages for output buffering */
++	INIT_LIST_HEAD(&sclp_tty_pages);
++	for (i = 0; i < MAX_KMEM_PAGES; i++) {
++		page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
++		if (page == NULL)
++			return;
++		list_add_tail((struct list_head *) page, &sclp_tty_pages);
 +	}
-+	if(max_sectors[tapeblock_major]) {
-+		kfree(max_sectors[tapeblock_major]);
-+		max_sectors[tapeblock_major] = NULL;
++	INIT_LIST_HEAD(&sclp_tty_outqueue);
++	spin_lock_init(&sclp_tty_lock);
++	init_waitqueue_head(&sclp_tty_waitq);
++	init_timer(&sclp_tty_timer);
++	sclp_ttybuf = NULL;
++	sclp_tty_buffer_count = 0;
++	if (MACHINE_IS_VM) {
++		/*
++		 * save 4 characters for the CPU number
++		 * written at start of each line by VM/CP
++		 */
++		sclp_ioctls_init.columns = 76;
++		/* case input lines to lowercase */
++		sclp_ioctls_init.tolower = 1;
 +	}
-+	blk_dev[tapeblock_major].queue = NULL;
-+	unregister_blkdev(tapeblock_major, "tBLK");
-+}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_char.c drivers/s390/char/tape_char.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_char.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/tape_char.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,534 @@
-+/*
-+ *  drivers/s390/char/tape_char.c
-+ *    character device frontend for tape device driver
-+ *
-+ *  S390 and zSeries version
-+ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Carsten Otte <cotte at de.ibm.com>
-+ *               Michael Holzheu <holzheu at de.ibm.com>
-+ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-+ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ *               Stefan Bader <shbader at de.ibm.com>
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/module.h>
-+#include <linux/types.h>
-+#include <linux/proc_fs.h>
-+#include <linux/mtio.h>
-+
-+#include <asm/irq.h>
-+#include <asm/s390dyn.h>
-+#include <asm/uaccess.h>
-+
-+#define TAPE_DBF_AREA	tape_core_dbf
-+
-+#include "tape.h"
-+#include "tape_std.h"
++	sclp_ioctls = sclp_ioctls_init;
++	sclp_tty_chars_count = 0;
++	sclp_tty = NULL;
 +
-+#define PRINTK_HEADER "TCHAR:"
++	ctrlchar_init();
 +
-+#define TAPECHAR_DEVFSMODE	0020644	/* crwxrw-rw- */
-+#define TAPECHAR_MAJOR		0	/* get dynamic major */
++	if (sclp_register(&sclp_input_event) != 0)
++		return;
 +
-+int tapechar_major = TAPECHAR_MAJOR;
++	memset (&sclp_tty_driver, 0, sizeof(struct tty_driver));
++	sclp_tty_driver.magic = TTY_DRIVER_MAGIC;
++	sclp_tty_driver.driver_name = "sclp_line";
++	sclp_tty_driver.name = "ttyS";
++	sclp_tty_driver.name_base = 0;
++	sclp_tty_driver.major = TTY_MAJOR;
++	sclp_tty_driver.minor_start = 64;
++	sclp_tty_driver.num = 1;
++	sclp_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
++	sclp_tty_driver.subtype = SYSTEM_TYPE_TTY;
++	sclp_tty_driver.init_termios = tty_std_termios;
++	sclp_tty_driver.flags = TTY_DRIVER_REAL_RAW;
++	sclp_tty_driver.refcount = &sclp_tty_refcount;
++	/* sclp_tty_driver.proc_entry ?	 */
++	sclp_tty_driver.table = sclp_tty_table;
++	sclp_tty_driver.termios = sclp_tty_termios;
++	sclp_tty_driver.termios_locked = sclp_tty_termios_locked;
++	sclp_tty_driver.open = sclp_tty_open;
++	sclp_tty_driver.close = sclp_tty_close;
++	sclp_tty_driver.write = sclp_tty_write;
++	sclp_tty_driver.put_char = sclp_tty_put_char;
++	sclp_tty_driver.flush_chars = sclp_tty_flush_chars;
++	sclp_tty_driver.write_room = sclp_tty_write_room;
++	sclp_tty_driver.chars_in_buffer = sclp_tty_chars_in_buffer;
++	sclp_tty_driver.flush_buffer = sclp_tty_flush_buffer;
++	sclp_tty_driver.ioctl = sclp_tty_ioctl;
++	/*
++	 * No need for these function because they would be only called when
++	 * the line discipline is close to full. That means that there must be
++	 * collected nearly 4kB of input data. I suppose it is very difficult
++	 * for the operator to enter lines quickly enough to let overrun the
++	 * line discipline. Besides the n_tty line discipline does not try to
++	 * call such functions if the pointers are set to NULL. Finally I have
++	 * no idea what to do within these function. I can not prevent the
++	 * operator and the SCLP to deliver input. Because of the reasons
++	 * above it seems not worth to implement a buffer mechanism.
++	 */
++	sclp_tty_driver.throttle = NULL;
++	sclp_tty_driver.unthrottle = NULL;
++	sclp_tty_driver.send_xchar = NULL;
++	sclp_tty_driver.set_termios = NULL;
++	sclp_tty_driver.set_ldisc = NULL;
++	sclp_tty_driver.stop = NULL;
++	sclp_tty_driver.start = NULL;
++	sclp_tty_driver.hangup = NULL;
++	sclp_tty_driver.break_ctl = NULL;
++	sclp_tty_driver.wait_until_sent = NULL;
++	sclp_tty_driver.read_proc = NULL;
++	sclp_tty_driver.write_proc = NULL;
 +
++	rc = tty_register_driver(&sclp_tty_driver);
++	if (rc != 0)
++		printk(KERN_ERR SCLP_TTY_PRINT_HEADER
++		       "could not register tty - "
++		       "sclp_drv_register returned %d\n", rc);
++}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/sclp_tty.h drivers/s390/char/sclp_tty.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/sclp_tty.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/sclp_tty.h	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,67 @@
 +/*
-+ * Prototypes for file operation functions
++ *  drivers/s390/char/sclp_tty.h
++ *    interface to the SCLP-read/write driver
++ *
++ *  S390 version
++ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
++ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
 + */
-+static ssize_t tapechar_read(struct file *, char *, size_t, loff_t *);
-+static ssize_t tapechar_write(struct file *, const char *, size_t, loff_t *);
-+static int tapechar_open(struct inode *,struct file *);
-+static int tapechar_release(struct inode *,struct file *);
-+static int tapechar_ioctl(struct inode *, struct file *, unsigned int,
-+			  unsigned long);
 +
-+/*
-+ * File operation structure for tape character frontend
-+ */
-+static struct file_operations tape_fops =
-+{
-+	.read = tapechar_read,
-+	.write = tapechar_write,
-+	.ioctl = tapechar_ioctl,
-+	.open = tapechar_open,
-+	.release = tapechar_release,
-+};
++#ifndef __SCLP_TTY_H__
++#define __SCLP_TTY_H__
 +
-+#ifdef CONFIG_DEVFS_FS
-+/*
-+ * Create Char directory with (non)rewinding entries
-+ */
-+static int
-+tapechar_mkdevfstree(struct tape_device *device)
-+{
-+	device->char_data.devfs_char_dir =
-+		devfs_mk_dir(device->devfs_dir, "char", device);
-+	if (device->char_data.devfs_char_dir == NULL)
-+		return -ENOENT;
-+	device->char_data.devfs_nonrewinding =
-+		devfs_register(device->char_data.devfs_char_dir,
-+			       "nonrewinding", DEVFS_FL_DEFAULT,
-+			       tapechar_major, device->first_minor,
-+			       TAPECHAR_DEVFSMODE, &tape_fops, device);
-+	if (device->char_data.devfs_nonrewinding == NULL) {
-+		devfs_unregister(device->char_data.devfs_char_dir);
-+		return -ENOENT;
-+	}
-+	device->char_data.devfs_rewinding =
-+		devfs_register(device->char_data.devfs_char_dir,
-+			       "rewinding", DEVFS_FL_DEFAULT,
-+			       tapechar_major, device->first_minor + 1,
-+			       TAPECHAR_DEVFSMODE, &tape_fops, device);
-+	if (device->char_data.devfs_rewinding == NULL) {
-+		devfs_unregister(device->char_data.devfs_nonrewinding);
-+		devfs_unregister(device->char_data.devfs_char_dir);
-+		return -ENOENT;
-+	}
-+	return 0;
-+}
++#include <linux/ioctl.h>
 +
-+/*
-+ * Remove devfs entries
-+ */
-+static void
-+tapechar_rmdevfstree (struct tape_device *device)
-+{
-+	if (device->char_data.devfs_nonrewinding) 
-+		devfs_unregister(device->char_data.devfs_nonrewinding);
-+	if (device->char_data.devfs_rewinding)
-+		devfs_unregister(device->char_data.devfs_rewinding);
-+	if (device->char_data.devfs_char_dir)
-+		devfs_unregister(device->char_data.devfs_char_dir);
-+}
-+#endif
++/* This is the type of data structures storing sclp ioctl setting. */
++struct sclp_ioctls {
++	unsigned short htab;
++	unsigned char echo;
++	unsigned short columns;
++	unsigned char final_nl;
++	unsigned short max_sccb;
++	unsigned short kmem_sccb;	/* can't be modified at run time */
++	unsigned char tolower;
++	unsigned char delim;
++};
 +
-+/*
-+ * This function is called for every new tapedevice
-+ */
-+int
-+tapechar_setup_device(struct tape_device * device)
-+{
-+#ifdef CONFIG_DEVFS_FS
-+	int rc;
++/* must be unique, FIXME: must be added in Documentation/ioctl_number.txt */
++#define SCLP_IOCTL_LETTER 'B'
 +
-+	rc = tapechar_mkdevfstree(device);
-+	if (rc)
-+		return rc;
-+#endif
++/* set width of horizontal tabulator */
++#define TIOCSCLPSHTAB	_IOW(SCLP_IOCTL_LETTER, 0, unsigned short)
++/* enable/disable echo of input (independent from line discipline) */
++#define TIOCSCLPSECHO	_IOW(SCLP_IOCTL_LETTER, 1, unsigned char)
++/* set number of colums for output */
++#define TIOCSCLPSCOLS	_IOW(SCLP_IOCTL_LETTER, 2, unsigned short)
++/* enable/disable writing without final new line character */
++#define TIOCSCLPSNL	_IOW(SCLP_IOCTL_LETTER, 4, signed char)
++/* set the maximum buffers size for output, rounded up to next 4kB boundary */
++#define TIOCSCLPSOBUF	_IOW(SCLP_IOCTL_LETTER, 5, unsigned short)
++/* set initial (default) sclp ioctls */
++#define TIOCSCLPSINIT	_IO(SCLP_IOCTL_LETTER, 6)
++/* enable/disable conversion from upper to lower case of input */
++#define TIOCSCLPSCASE	_IOW(SCLP_IOCTL_LETTER, 7, unsigned char)
++/* set special character used for separating upper and lower case, */
++/* 0x00 disables this feature */
++#define TIOCSCLPSDELIM	_IOW(SCLP_IOCTL_LETTER, 9, unsigned char)
 +
-+	tape_hotplug_event(device, tapechar_major, TAPE_HOTPLUG_CHAR_ADD);
-+	return 0;
-+	
-+}
++/* get width of horizontal tabulator */
++#define TIOCSCLPGHTAB	_IOR(SCLP_IOCTL_LETTER, 10, unsigned short)
++/* Is echo of input enabled ? (independent from line discipline) */
++#define TIOCSCLPGECHO	_IOR(SCLP_IOCTL_LETTER, 11, unsigned char)
++/* get number of colums for output */
++#define TIOCSCLPGCOLS	_IOR(SCLP_IOCTL_LETTER, 12, unsigned short)
++/* Is writing without final new line character enabled ? */
++#define TIOCSCLPGNL	_IOR(SCLP_IOCTL_LETTER, 14, signed char)
++/* get the maximum buffers size for output */
++#define TIOCSCLPGOBUF	_IOR(SCLP_IOCTL_LETTER, 15, unsigned short)
++/* Is conversion from upper to lower case of input enabled ? */
++#define TIOCSCLPGCASE	_IOR(SCLP_IOCTL_LETTER, 17, unsigned char)
++/* get special character used for separating upper and lower case, */
++/* 0x00 disables this feature */
++#define TIOCSCLPGDELIM	_IOR(SCLP_IOCTL_LETTER, 19, unsigned char)
++/* get the number of buffers/pages got from kernel at startup */
++#define TIOCSCLPGKBUF	_IOR(SCLP_IOCTL_LETTER, 20, unsigned short)
 +
-+void
-+tapechar_cleanup_device(struct tape_device* device)
-+{
-+#ifdef CONFIG_DEVFS_FS
-+	tapechar_rmdevfstree(device);
-+#endif
-+	tape_hotplug_event(device, tapechar_major, TAPE_HOTPLUG_CHAR_REMOVE);
-+}
++#endif	/* __SCLP_TTY_H__ */
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/sclp_vt220.c drivers/s390/char/sclp_vt220.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/sclp_vt220.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/sclp_vt220.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,817 @@
++/*
++ *  drivers/s390/char/sclp_vt220.c
++ *    SCLP VT220 terminal driver.
++ *
++ *  S390 version
++ *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Peter Oberparleiter <Peter.Oberparleiter at de.ibm.com>
++ */
 +
-+static inline int
-+tapechar_check_idalbuffer(struct tape_device *device, size_t block_size)
-+{
-+	struct idal_buffer *new;
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/spinlock.h>
++#include <linux/list.h>
++#include <linux/wait.h>
++#include <linux/timer.h>
++#include <linux/kernel.h>
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/major.h>
++#include <linux/console.h>
++#include <linux/kdev_t.h>
++#include <linux/bootmem.h>
++#include <linux/interrupt.h>
++#include <asm/uaccess.h>
++#include "sclp.h"
 +
-+	/* Idal buffer must be the same size as the requested block size! */
-+	if (device->char_data.idal_buf != NULL &&
-+	    device->char_data.idal_buf->size == block_size)
-+		return 0;
++#define SCLP_VT220_PRINT_HEADER 	"sclp vt220 tty driver: "
++#define SCLP_VT220_MAJOR		TTY_MAJOR
++#define SCLP_VT220_MINOR		65
++#define SCLP_VT220_DRIVER_NAME		"sclp_vt220"
++#define SCLP_VT220_DEVICE_NAME		"ttyS"
++#define SCLP_VT220_CONSOLE_NAME		"ttyS"
++#define SCLP_VT220_CONSOLE_INDEX	1	/* console=ttyS1 */
 +
-+	if(block_size > MAX_BLOCKSIZE) {
-+		DBF_EVENT(3, "Invalid blocksize (%ld > %ld)\n",
-+			block_size, MAX_BLOCKSIZE);
-+		PRINT_ERR("Invalid blocksize (%ld > %ld)\n",
-+			block_size, MAX_BLOCKSIZE);
-+		return -EINVAL;
-+	}
++/* Representation of a single write request */
++struct sclp_vt220_request {
++	struct list_head list;
++	struct sclp_req sclp_req;
++	int retry_count;
++	struct timer_list retry_timer;
++};
 +
-+	/* The current idal buffer is not big enough. Allocate a new one. */
-+	new = idal_buffer_alloc(block_size, 0);
-+	if (new == NULL)
-+		return -ENOMEM;
-+	if (device->char_data.idal_buf != NULL)
-+		idal_buffer_free(device->char_data.idal_buf);	
-+	device->char_data.idal_buf = new;
-+	return 0;
-+}
++/* VT220 SCCB */
++struct sclp_vt220_sccb {
++	struct sccb_header header;
++	struct evbuf_header evbuf;
++};
 +
-+/*
-+ * Tape device read function
-+ */
-+ssize_t
-+tapechar_read (struct file *filp, char *data, size_t count, loff_t *ppos)
-+{
-+	struct tape_device *device;
-+	struct tape_request *request;
-+	size_t block_size;
-+	int rc;
++#define SCLP_VT220_MAX_CHARS_PER_BUFFER	(PAGE_SIZE - \
++					 sizeof(struct sclp_vt220_request) - \
++					 sizeof(struct sclp_vt220_sccb))
 +
-+        DBF_EVENT(6, "TCHAR:read\n");
-+	device = (struct tape_device *) filp->private_data;
++/* Structures and data needed to register tty driver */
++static struct tty_driver sclp_vt220_driver;
++static int sclp_vt220_refcount;
++static struct tty_struct * sclp_vt220_table[1];
++static struct termios * sclp_vt220_termios[1];
++static struct termios * sclp_vt220_termios_locked[1];
 +
-+	/* Check position. */
-+	if (ppos != &filp->f_pos) {
-+		/*
-+		 * "A request was outside the capabilities of the device."
-+		 * This check uses internal knowledge about how pread and
-+		 * read work...
-+		 */
-+	        DBF_EVENT(6, "TCHAR:ppos wrong\n");
-+		return -EOVERFLOW;
-+	}
++/* The tty_struct that the kernel associated with us */
++static struct tty_struct *sclp_vt220_tty;
 +
-+	/*
-+	 * If the tape isn't terminated yet, do it now. And since we then
-+	 * are at the end of the tape there wouldn't be anything to read
-+	 * anyways. So we return immediatly.
-+	 */
-+	if(device->required_tapemarks) {
-+		return tape_std_terminate_write(device);
-+	}
++/* Lock to protect internal data from concurrent access */
++static spinlock_t sclp_vt220_lock;
 +
-+	/* Find out block size to use */
-+	if (device->char_data.block_size != 0) {
-+		if (count < device->char_data.block_size) {
-+			DBF_EVENT(3, "TCHAR:read smaller than block "
-+				     "size was requested\n");
-+			return -EINVAL;
-+		}
-+		block_size = device->char_data.block_size;
-+	} else {
-+		block_size = count;
-+	}
++/* List of empty pages to be used as write request buffers */
++static struct list_head sclp_vt220_empty;
 +
-+	/*
-+	 * Set the idal buffer to the correct size. The fixed block size
-+	 * could have been set some time ago. And the idal buffer is re-
-+	 * leased when the device is closed!
-+	 */
-+	rc = tapechar_check_idalbuffer(device, block_size);
-+	if (rc)
-+		return rc;
++/* List of pending requests */
++static struct list_head sclp_vt220_outqueue;
 +
-+	DBF_EVENT(6, "TCHAR:nbytes: %lx\n", block_size);
-+	/* Let the discipline build the ccw chain. */
-+	request = device->discipline->read_block(device, block_size);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	/* Execute it. */
-+	rc = tape_do_io(device, request);
-+	if (rc == 0) {
-+		rc = block_size - device->devstat.rescnt;
-+		DBF_EVENT(6, "TCHAR:rbytes:  %x\n", rc);
-+		filp->f_pos += rc;
-+		/* Copy data from idal buffer to user space. */
-+		if (idal_buffer_to_user(device->char_data.idal_buf,
-+					data, rc) != 0)
-+			rc = -EFAULT;
-+	}
-+	tape_put_request(request);
-+	return rc;
-+}
++/* Number of requests in outqueue */
++static int sclp_vt220_outqueue_count;
 +
-+/*
-+ * Tape device write function
-+ */
-+ssize_t
-+tapechar_write(struct file *filp, const char *data, size_t count, loff_t *ppos)
-+{
-+	struct tape_device *device;
-+	struct tape_request *request;
-+	size_t block_size;
-+	size_t written;
-+	int nblocks;
-+	int i, rc;
++/* Wait queue used to delay write requests while we've run out of buffers */
++static wait_queue_head_t sclp_vt220_waitq;
 +
-+	DBF_EVENT(6, "TCHAR:write\n");
-+	device = (struct tape_device *) filp->private_data;
-+	/* Check position */
-+	if (ppos != &filp->f_pos) {
-+		/* "A request was outside the capabilities of the device." */
-+	        DBF_EVENT(6, "TCHAR:ppos wrong\n");
-+		return -EOVERFLOW;
-+	}
-+	/* Find out block size and number of blocks */
-+	if (device->char_data.block_size != 0) {
-+		if (count < device->char_data.block_size) {
-+			DBF_EVENT(3, "TCHAR:write smaller than block "
-+				    "size was requested\n");
-+			return -EINVAL;
-+		}
-+		block_size = device->char_data.block_size;
-+		nblocks = count / block_size;
-+	} else {
-+		block_size = count;
-+		nblocks = 1;
-+	}
++/* Timer used for delaying write requests to merge subsequent messages into
++ * a single buffer */
++static struct timer_list sclp_vt220_timer;
 +
-+	/* Set the idal buffer to the correct size. */
-+	rc = tapechar_check_idalbuffer(device, block_size);
-+	if (rc)
-+		return rc;
++/* Pointer to current request buffer which has been partially filled but not
++ * yet sent */
++static struct sclp_vt220_request *sclp_vt220_current_request;
 +
-+	DBF_EVENT(6,"TCHAR:nbytes: %lx\n", block_size);
-+        DBF_EVENT(6, "TCHAR:nblocks: %x\n", nblocks);
-+	/* Let the discipline build the ccw chain. */
-+	request = device->discipline->write_block(device, block_size);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	rc = 0;
-+	written = 0;
-+	for (i = 0; i < nblocks; i++) {
-+		/* Copy data from user space to idal buffer. */
-+		if (idal_buffer_from_user(device->char_data.idal_buf,
-+					  data, block_size)) {
-+			rc = -EFAULT;
-+			break;
-+		}
-+		rc = tape_do_io(device, request);
-+		if (rc)
-+			break;
-+	        DBF_EVENT(6, "TCHAR:wbytes: %lx\n",
-+			  block_size - device->devstat.rescnt); 
-+		filp->f_pos += block_size - device->devstat.rescnt;
-+		written += block_size - device->devstat.rescnt;
-+		if (device->devstat.rescnt != 0)
-+			break;
-+		data += block_size;
-+	}
-+	tape_put_request(request);
++/* Number of characters in current request buffer */
++static int sclp_vt220_buffered_chars;
 +
-+	if (rc == -ENOSPC) {
-+		/*
-+		 * Ok, the device has no more space. It has NOT written
-+		 * the block.
-+		 */
-+		if (device->discipline->process_eov)
-+			device->discipline->process_eov(device);
-+		if (written > 0)
-+			rc = 0;
-+	}
++/* Flag indicating whether this driver has already been initialized */
++static int sclp_vt220_initialized = 0;
 +
-+	/*
-+	 * After doing a write we always need two tapemarks to correctly
-+	 * terminate the tape (one to terminate the file, the second to
-+	 * flag the end of recorded data.
-+	 * Since process_eov positions the tape in front of the written
-+	 * tapemark it doesn't hurt to write two marks again.
-+	 */
-+	if(!rc)
-+		device->required_tapemarks = 2;
++/* Flag indicating that sclp_vt220_current_request should really
++ * have been already queued but wasn't because the SCLP was processing
++ * another buffer */
++static int sclp_vt220_flush_later;
 +
-+	return rc ? rc : written;
-+}
++static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf);
++static void __sclp_vt220_emit(struct sclp_vt220_request *request);
++static void sclp_vt220_emit_current(void);
 +
-+/*
-+ * Character frontend tape device open function.
-+ */
-+int
-+tapechar_open (struct inode *inode, struct file *filp)
-+{
-+	struct tape_device *device;
-+	int minor, rc;
++/* Registration structure for our interest in SCLP event buffers */
++static struct sclp_register sclp_vt220_register = {
++	.send_mask		= EvTyp_VT220Msg_Mask,
++	.receive_mask		= EvTyp_VT220Msg_Mask,
++	.state_change_fn	= NULL,
++	.receiver_fn		= sclp_vt220_receiver_fn
++};
 +
-+	MOD_INC_USE_COUNT;
-+	if (major(filp->f_dentry->d_inode->i_rdev) != tapechar_major)
-+		return -ENODEV;
-+	minor = minor(filp->f_dentry->d_inode->i_rdev);
-+	device = tape_get_device(minor / TAPE_MINORS_PER_DEV);
-+	if (IS_ERR(device)) {
-+		MOD_DEC_USE_COUNT;
-+		return PTR_ERR(device);
-+	}
-+	DBF_EVENT(6, "TCHAR:open: %x\n", minor(inode->i_rdev));
-+	rc = tape_open(device);
-+	if (rc == 0) {
-+		rc = tape_assign(device, TAPE_STATUS_ASSIGN_A);
-+		if (rc == 0) {
-+			filp->private_data = device;
-+			return 0;
-+		}
-+		tape_release(device);
-+	}
-+	tape_put_device(device);
-+	MOD_DEC_USE_COUNT;
-+	return rc;
-+}
 +
 +/*
-+ * Character frontend tape device release function.
++ * Put provided request buffer back into queue and check emit pending
++ * buffers if necessary.
 + */
-+
-+int
-+tapechar_release(struct inode *inode, struct file *filp)
++static void
++sclp_vt220_process_queue(struct sclp_vt220_request* request)
 +{
-+	struct tape_device *device;
-+
-+	device = (struct tape_device *) filp->private_data;
-+	DBF_EVENT(6, "TCHAR:release: %x\n", minor(inode->i_rdev));
-+			
-+	/*
-+	 * If this is the rewinding tape minor then rewind. In that case we
-+	 * write all required tapemarks. Otherwise only one to terminate the
-+	 * file.
-+	 */
-+	if ((minor(inode->i_rdev) & 1) != 0) {
-+		if(device->required_tapemarks)
-+			tape_std_terminate_write(device);
-+		tape_mtop(device, MTREW, 1);
-+	} else {
-+		if(device->required_tapemarks > 1) {
-+			if(tape_mtop(device, MTWEOF, 1) == 0)
-+				device->required_tapemarks--;
-+		}
-+	}
++	unsigned long flags;
++	struct sclp_vt220_request *next;
++	void *page;
 +
-+	if (device->char_data.idal_buf != NULL) {
-+		idal_buffer_free(device->char_data.idal_buf);
-+		device->char_data.idal_buf = NULL;
++	/* Put buffer back to list of empty buffers */
++	page = request->sclp_req.sccb;
++	spin_lock_irqsave(&sclp_vt220_lock, flags);
++	/* Move request from outqueue to empty queue */
++	list_del(&request->list);
++	sclp_vt220_outqueue_count--;
++	list_add_tail((struct list_head *) page, &sclp_vt220_empty);
++	/* Check if there is a pending buffer on the out queue. */
++	next = NULL;
++	if (!list_empty(&sclp_vt220_outqueue))
++		next = list_entry(sclp_vt220_outqueue.next,
++				  struct sclp_vt220_request, list);
++	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++	if (next != NULL)
++		__sclp_vt220_emit(next);
++	else if (sclp_vt220_flush_later)
++		sclp_vt220_emit_current();
++	wake_up(&sclp_vt220_waitq);
++	/* Check if the tty needs a wake up call */
++	if (sclp_vt220_tty != NULL) {
++		if ((sclp_vt220_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
++		    (sclp_vt220_tty->ldisc.write_wakeup != NULL))
++			(sclp_vt220_tty->ldisc.write_wakeup)(sclp_vt220_tty);
++		wake_up_interruptible(&sclp_vt220_tty->write_wait);
 +	}
-+	tape_unassign(device, TAPE_STATUS_ASSIGN_A);
-+	tape_release(device);
-+	filp->private_data = NULL; tape_put_device(device);
-+	MOD_DEC_USE_COUNT;
-+	return 0;
 +}
 +
 +/*
-+ * Tape device io controls.
++ * Retry sclp write request after waiting some time for an sclp equipment
++ * check to pass.
 + */
-+static int
-+tapechar_ioctl(struct inode *inp, struct file *filp,
-+	   unsigned int no, unsigned long data)
++static void
++sclp_vt220_retry(unsigned long data)
 +{
-+	struct tape_device *device;
-+	int rc;
-+
-+	DBF_EVENT(6, "TCHAR:ioct(%x)\n", no);
-+
-+	device = (struct tape_device *) filp->private_data;
-+
-+	if (no == MTIOCTOP) {
-+		struct mtop op;
++	struct sclp_vt220_request *request;
++	struct sclp_vt220_sccb *sccb;
 +
-+		if (copy_from_user(&op, (char *) data, sizeof(op)) != 0)
-+			return -EFAULT;
-+		if (op.mt_count < 0)
-+			return -EINVAL;
++	request = (struct sclp_vt220_request *) data;
++	request->sclp_req.status = SCLP_REQ_FILLED;
++	sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
++	sccb->header.response_code = 0x0000;
++	sclp_add_request(&request->sclp_req);
++}
 +
-+		/*
-+		 * Operations that change tape position should write final
-+		 * tapemarks
-+		 */
-+		switch(op.mt_op) {
-+			case MTFSF:
-+			case MTBSF:
-+			case MTFSR:
-+			case MTBSR:
-+			case MTREW:
-+			case MTOFFL:
-+			case MTEOM:
-+			case MTRETEN:
-+			case MTBSFM:
-+			case MTFSFM:
-+			case MTSEEK:
-+				if(device->required_tapemarks)
-+					tape_std_terminate_write(device);
-+			default:
-+				;
-+		}
-+		rc = tape_mtop(device, op.mt_op, op.mt_count);
++#define SCLP_BUFFER_MAX_RETRY		5
++#define	SCLP_BUFFER_RETRY_INTERVAL	2
 +
-+		if(op.mt_op == MTWEOF && rc == 0) {
-+			if(op.mt_count > device->required_tapemarks)
-+				device->required_tapemarks = 0;
-+			else
-+				device->required_tapemarks -= op.mt_count;
-+		}
-+		return rc;
-+	}
-+	if (no == MTIOCPOS) {
-+		/* MTIOCPOS: query the tape position. */
-+		struct mtpos pos;
++/*
++ * Callback through which the result of a write request is reported by the
++ * SCLP.
++ */
++static void
++sclp_vt220_callback(struct sclp_req *request, void *data)
++{
++	struct sclp_vt220_request *vt220_request;
++	struct sclp_vt220_sccb *sccb;
 +
-+		rc = tape_mtop(device, MTTELL, 1);
-+		if (rc < 0)
-+			return rc;
-+		pos.mt_blkno = rc;
-+		if (copy_to_user((char *) data, &pos, sizeof(pos)) != 0)
-+			return -EFAULT;
-+		return 0;
++	vt220_request = (struct sclp_vt220_request *) data;
++	if (request->status == SCLP_REQ_FAILED) {
++		sclp_vt220_process_queue(vt220_request);
++		return;
 +	}
-+	if (no == MTIOCGET) {
-+		/* MTIOCGET: query the tape drive status. */
-+		struct mtget get;
++	sccb = (struct sclp_vt220_sccb *) vt220_request->sclp_req.sccb;
 +
-+		memset(&get, 0, sizeof(get));
-+		get.mt_type = MT_ISUNKNOWN;
-+		get.mt_resid = device->devstat.rescnt;
-+		get.mt_dsreg = device->tape_status;
-+		/* FIXME: mt_erreg, mt_fileno */
-+		get.mt_gstat = device->tape_generic_status;
++	/* Check SCLP response code and choose suitable action	*/
++	switch (sccb->header.response_code) {
++	case 0x0020 :
++		break;
 +
-+		if(device->medium_state == MS_LOADED) {
-+			rc = tape_mtop(device, MTTELL, 1);
++	case 0x05f0: /* Target resource in improper state */
++		break;
 +
-+			if(rc < 0)
-+				return rc;
++	case 0x0340: /* Contained SCLP equipment check */
++		if (vt220_request->retry_count++ > SCLP_BUFFER_MAX_RETRY)
++			break;
++		/* Remove processed buffers and requeue rest */
++		if (sclp_remove_processed((struct sccb_header *) sccb) > 0) {
++			/* Not all buffers were processed */
++			sccb->header.response_code = 0x0000;
++			vt220_request->sclp_req.status = SCLP_REQ_FILLED;
++			sclp_add_request(request);
++			return;
++		}
++		break;
 +
-+			if(rc == 0)
-+				get.mt_gstat |= GMT_BOT(~0);
++	case 0x0040: /* SCLP equipment check */
++		if (vt220_request->retry_count++ > SCLP_BUFFER_MAX_RETRY)
++			break;
++		/* Wait some time, then retry request */
++		vt220_request->retry_timer.function = sclp_vt220_retry;
++		vt220_request->retry_timer.data =
++			(unsigned long) vt220_request;
++		vt220_request->retry_timer.expires =
++			jiffies + SCLP_BUFFER_RETRY_INTERVAL*HZ;
++		add_timer(&vt220_request->retry_timer);
++		return;
 +
-+			get.mt_blkno = rc;
-+		}
-+		get.mt_erreg = 0;
-+		if (copy_to_user((char *) data, &get, sizeof(get)) != 0)
-+			return -EFAULT;
-+		return 0;
++	default:
++		break;
 +	}
-+	/* Try the discipline ioctl function. */
-+	if (device->discipline->ioctl_fn == NULL)
-+		return -EINVAL;
-+	return device->discipline->ioctl_fn(device, no, data);
++	sclp_vt220_process_queue(vt220_request);
 +}
 +
 +/*
-+ * Initialize character device frontend.
++ * Emit vt220 request buffer to SCLP.
 + */
-+int
-+tapechar_init (void)
++static void
++__sclp_vt220_emit(struct sclp_vt220_request *request)
 +{
-+	int rc;
-+
-+	/* Register the tape major number to the kernel */
-+#ifdef CONFIG_DEVFS_FS
-+	if (tapechar_major == 0)
-+		tapechar_major = devfs_alloc_major(DEVFS_SPECIAL_CHR);
-+#endif
-+        rc = register_chrdev(tapechar_major, "tape", &tape_fops);
-+	if (rc < 0) {
-+		PRINT_ERR("can't get major %d\n", tapechar_major);
-+		DBF_EVENT(3, "TCHAR:initfail\n");
-+		return rc;
++	if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) {
++		request->sclp_req.status = SCLP_REQ_FAILED;
++		sclp_vt220_callback(&request->sclp_req, (void *) request);
++		return;
 +	}
-+	if (tapechar_major == 0)
-+		tapechar_major = rc;  /* accept dynamic major number */
-+	PRINT_INFO("Tape gets major %d for char device\n", tapechar_major);
-+	DBF_EVENT(3, "Tape gets major %d for char device\n", rc);
-+        DBF_EVENT(3, "TCHAR:init ok\n");
-+	return 0;
++	request->sclp_req.command = SCLP_CMDW_WRITEDATA;
++	request->sclp_req.status = SCLP_REQ_FILLED;
++	request->sclp_req.callback = sclp_vt220_callback;
++	request->sclp_req.callback_data = (void *) request;
++
++	sclp_add_request(&request->sclp_req);
 +}
 +
 +/*
-+ * cleanup
++ * Queue and emit given request.
 + */
-+void
-+tapechar_exit(void)
++static void
++sclp_vt220_emit(struct sclp_vt220_request *request)
 +{
-+	unregister_chrdev (tapechar_major, "tape");
++	unsigned long flags;
++	int count;
++
++	spin_lock_irqsave(&sclp_vt220_lock, flags);
++	list_add_tail(&request->list, &sclp_vt220_outqueue);
++	count = sclp_vt220_outqueue_count++;
++	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++	/* Emit only the first buffer immediately - callback takes care of
++	 * the rest */
++	if (count == 0)
++		__sclp_vt220_emit(request);
 +}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_core.c drivers/s390/char/tape_core.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_core.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/tape_core.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,1434 @@
++
 +/*
-+ *  drivers/s390/char/tape_core.c
-+ *    basic function of the tape device driver
-+ *
-+ *  S390 and zSeries version
-+ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Carsten Otte <cotte at de.ibm.com>
-+ *               Michael Holzheu <holzheu at de.ibm.com>
-+ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-+ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ *               Stefan Bader <shbader at de.ibm.com>
++ * Queue and emit current request.
 + */
++static void
++sclp_vt220_emit_current(void)
++{
++	unsigned long flags;
++	struct sclp_vt220_request *request;
++	struct sclp_vt220_sccb *sccb;
 +
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/module.h>
-+#include <linux/init.h>	     // for kernel parameters
-+#include <linux/kmod.h>	     // for requesting modules
-+#include <linux/spinlock.h>  // for locks
-+#include <linux/vmalloc.h>
-+
-+#include <asm/types.h>	     // for variable types
-+#include <asm/irq.h>
-+#include <asm/s390io.h>
-+#include <asm/s390dyn.h>
-+
-+#define TAPE_DBF_AREA	tape_core_dbf
-+
-+#include "tape.h"
-+#include "tape_std.h"
-+
-+#ifdef CONFIG_S390_TAPE_3590
-+#include "tape_3590.h"
-+#endif
++	spin_lock_irqsave(&sclp_vt220_lock, flags);
++	request = NULL;
++	if (sclp_vt220_current_request != NULL) {
++		sccb = (struct sclp_vt220_sccb *) 
++				sclp_vt220_current_request->sclp_req.sccb;
++		/* Only emit buffers with content */
++		if (sccb->header.length != sizeof(struct sclp_vt220_sccb)) {
++			request = sclp_vt220_current_request;
++			sclp_vt220_current_request = NULL;
++			if (timer_pending(&sclp_vt220_timer))
++				del_timer(&sclp_vt220_timer);
++		}
++		sclp_vt220_flush_later = 0;
++	}
++	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++	if (request != NULL)
++		sclp_vt220_emit(request);
++}
 +
-+#define PRINTK_HEADER "T390:"
++#define SCLP_NORMAL_WRITE	0x00
 +
 +/*
-+ * Prototypes for some static functions.
++ * Helper function to initialize a page with the sclp request structure.
 + */
-+static void __tape_do_irq (int, void *, struct pt_regs *);
-+static void __tape_remove_request(struct tape_device *, struct tape_request *);
-+static void tape_timeout_io (unsigned long);
++static struct sclp_vt220_request *
++sclp_vt220_initialize_page(void *page)
++{
++	struct sclp_vt220_request *request;
++	struct sclp_vt220_sccb *sccb;
 +
-+/*
-+ * List of tape disciplines guarded by tape_discipline_lock.
-+ */
-+static struct list_head	tape_disciplines = LIST_HEAD_INIT(tape_disciplines);
-+static spinlock_t tape_discipline_lock   = SPIN_LOCK_UNLOCKED;
++	/* Place request structure at end of page */
++	request = ((struct sclp_vt220_request *)
++			((addr_t) page + PAGE_SIZE)) - 1;
++	init_timer(&request->retry_timer);
++	request->retry_count = 0;
++	request->sclp_req.sccb = page;
++	/* SCCB goes at start of page */
++	sccb = (struct sclp_vt220_sccb *) page;
++	memset((void *) sccb, 0, sizeof(struct sclp_vt220_sccb));
++	sccb->header.length = sizeof(struct sclp_vt220_sccb);
++	sccb->header.function_code = SCLP_NORMAL_WRITE;
++	sccb->header.response_code = 0x0000;
++	sccb->evbuf.type = EvTyp_VT220Msg;
++	sccb->evbuf.length = sizeof(struct evbuf_header);
 +
-+/*
-+ * Pointer to debug area.
-+ */
-+debug_info_t *TAPE_DBF_AREA = NULL;
++	return request;
++}
 +
-+const char *tape_op_verbose[TO_SIZE] =
++static inline unsigned int
++sclp_vt220_space_left(struct sclp_vt220_request *request)
 +{
-+	[TO_BLOCK]		= "BLK",
-+	[TO_BSB]		= "BSB",
-+	[TO_BSF]		= "BSF",
-+	[TO_DSE]		= "DSE",
-+	[TO_FSB]		= "FSB",
-+	[TO_FSF]		= "FSF",
-+	[TO_LBL]		= "LBL",
-+	[TO_NOP]		= "NOP",
-+	[TO_RBA]		= "RBA",
-+	[TO_RBI]		= "RBI",
-+	[TO_RFO]		= "RFO",
-+	[TO_REW]		= "REW",
-+	[TO_RUN]		= "RUN",
-+	[TO_WRI]		= "WRI",
-+	[TO_WTM]		= "WTM",
-+	[TO_MSEN]		= "MSN",
-+	[TO_LOAD]		= "LOA",
-+	[TO_READ_CONFIG]	= "RCF",
-+	[TO_READ_ATTMSG]	= "RAT",
-+	[TO_DIS]		= "DIS",
-+	[TO_ASSIGN]		= "ASS",
-+	[TO_UNASSIGN]		= "UAS",
-+	[TO_BREAKASS]		= "BRK"
-+};
++	struct sclp_vt220_sccb *sccb;
++	sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
++	return PAGE_SIZE - sizeof(struct sclp_vt220_request) -
++	       sccb->header.length;
++}
 +
-+/*
-+ * Inline functions, that have to be defined.
-+ */
-+static inline struct tape_request *
-+tape_get_next_request(struct tape_device *device) {
-+	if(list_empty(&device->req_queue))
-+		return NULL;
-+	return list_entry(device->req_queue.next, struct tape_request, list);
++static inline unsigned int
++sclp_vt220_chars_stored(struct sclp_vt220_request *request)
++{
++	struct sclp_vt220_sccb *sccb;
++	sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
++	return sccb->evbuf.length - sizeof(struct evbuf_header);
 +}
 +
 +/*
-+ * I/O helper function. Adds the request to the request queue
-+ * and starts it if the tape is idle. Has to be called with
-+ * the device lock held.
++ * Add msg to buffer associated with request. Return the number of characters
++ * added or -EFAULT on error.
 + */
-+static inline int
-+__do_IO(struct tape_device *device, struct tape_request *request)
++static int
++sclp_vt220_add_msg(struct sclp_vt220_request *request,
++		   const unsigned char *msg, int count, int from_user,
++		   int convertlf)
 +{
-+	int rc = 0;
-+
-+	if(request->cpaddr == NULL)
-+		BUG();
++	struct sclp_vt220_sccb *sccb;
++	void *buffer;
++	unsigned char c;
++	int from;
++	int to;
 +
-+	if(request->timeout.expires > 0) {
-+		/* Init should be done by caller */
-+		DBF_EVENT(6, "(%04x): starting timed request\n",
-+			device->devstat.devno);
++	if (count > sclp_vt220_space_left(request))
++		count = sclp_vt220_space_left(request);
++	if (count <= 0)
++		return 0;
 +
-+		request->timeout.function = tape_timeout_io;
-+		request->timeout.data     = (unsigned long)
-+			tape_clone_request(request);
-+		add_timer(&request->timeout);
-+	}
++	sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
++	buffer = (void *) ((addr_t) sccb + sccb->header.length);
 +
-+	rc = do_IO(device->devinfo.irq, request->cpaddr,
-+		(unsigned long) request, 0x00, request->options);
++	if (convertlf) {
++		/* Perform Linefeed conversion (0x0a -> 0x0a 0x0d)*/
++		for (from=0, to=0;
++		     (from < count) && (to < sclp_vt220_space_left(request));
++		     from++) {
++			/* Retrieve character */
++			if (from_user) {
++				if (get_user(c, msg + from) != 0)
++					return -EFAULT;
++			} else
++				c = msg[from];
++			/* Perform conversion */
++			if (c == 0x0a) {
++				if (to + 1 < sclp_vt220_space_left(request)) {
++					((unsigned char *) buffer)[to++] = c;
++					((unsigned char *) buffer)[to++] = 0x0d;
++				} else
++					break;
 +
-+	return rc;
++			} else
++				((unsigned char *) buffer)[to++] = c;
++		}
++		sccb->header.length += to;
++		sccb->evbuf.length += to;
++		return from;
++	} else {
++		if (from_user) {
++			if (copy_from_user(buffer, (void *) msg, count) != 0)
++				return -EFAULT;
++		}
++		else
++			memcpy(buffer, (const void *) msg, count);
++		sccb->header.length += count;
++		sccb->evbuf.length += count;
++		return count;
++	}
 +}
 +
++/*
++ * Emit buffer after having waited long enough for more data to arrive.
++ */
 +static void
-+__tape_process_queue(void *data)
++sclp_vt220_timeout(unsigned long data)
 +{
-+	struct tape_device *device = (struct tape_device *) data;
-+	struct list_head *l, *n;
-+	struct tape_request *request;
-+	int rc;
-+
-+	DBF_EVENT(6, "tape_process_queue(%p)\n", device);
-+
-+	/*
-+	 * We were told to be quiet. Do nothing for now.
-+	 */
-+	if (TAPE_NOACCESS(device)) {
-+		return;
-+	}
-+
-+	/*
-+	 * Try to start each request on request queue until one is
-+	 * started successful.
-+	 */
-+	list_for_each_safe(l, n, &device->req_queue) {
-+		request = list_entry(l, struct tape_request, list);
++	sclp_vt220_emit_current();
++}
 +
-+		/* Happens when new request arrive while still doing one. */
-+		if (request->status == TAPE_REQUEST_IN_IO)
-+			break;
++#define BUFFER_MAX_DELAY	HZ/2
 +
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+		if (request->op == TO_BLOCK)
-+			device->discipline->check_locate(device, request);
-+#endif
-+		switch(request->op) {
-+			case TO_MSEN:
-+			case TO_ASSIGN:
-+			case TO_UNASSIGN:
-+			case TO_BREAKASS:
-+				break;
-+			default:
-+				if (TAPE_OPEN(device))
-+					break;
-+				DBF_EVENT(3,
-+					"TAPE(%04x): REQ in UNUSED state\n",
-+					device->devstat.devno);
-+		}
++/* 
++ * Internal implementation of the write function. Write COUNT bytes of data
++ * from memory at BUF which may reside in user space (specified by FROM_USER)
++ * to the SCLP interface. In case that the data does not fit into the current
++ * write buffer, emit the current one and allocate a new one. If there are no
++ * more empty buffers available, wait until one gets emptied. If DO_SCHEDULE
++ * is non-zero, the buffer will be scheduled for emitting after a timeout -
++ * otherwise the user has to explicitly call the flush function.
++ * A non-zero CONVERTLF parameter indicates that 0x0a characters in the message
++ * buffer should be converted to 0x0a 0x0d. After completion, return the number
++ * of bytes written.
++ */
++static int
++__sclp_vt220_write(int from_user, const unsigned char *buf, int count,
++		   int do_schedule, int convertlf)
++{
++	unsigned long flags;
++	void *page;
++	int written;
++	int overall_written;
 +
-+		rc = __do_IO(device, request);
-+		if (rc == 0) {
-+			DBF_EVENT(6, "tape: do_IO success\n");
-+			request->status = TAPE_REQUEST_IN_IO;
-+			break;
++	if (count <= 0)
++		return 0;
++	overall_written = 0;
++	spin_lock_irqsave(&sclp_vt220_lock, flags);
++	do {
++		/* Create a sclp output buffer if none exists yet */
++		if (sclp_vt220_current_request == NULL) {
++			while (list_empty(&sclp_vt220_empty)) {
++				spin_unlock_irqrestore(&sclp_vt220_lock,
++						       flags);
++				if (in_interrupt())
++					sclp_sync_wait();
++				else
++					wait_event(sclp_vt220_waitq,
++						!list_empty(&sclp_vt220_empty));
++				spin_lock_irqsave(&sclp_vt220_lock, flags);
++			}
++			page = (void *) sclp_vt220_empty.next;
++			list_del((struct list_head *) page);
++			sclp_vt220_current_request =
++				sclp_vt220_initialize_page(page);
 +		}
-+		/* Start failed. Remove request and indicate failure. */
-+		if(rc == -EBUSY) {
-+			DBF_EVENT(1, "tape: DOIO request on busy tape\n");
++		/* Try to write the string to the current request buffer */
++		written = sclp_vt220_add_msg(sclp_vt220_current_request,
++				buf, count, from_user, convertlf);
++		if (written > 0)
++			overall_written += written;
++		if (written == -EFAULT || written == count)
 +			break;
-+		}
-+		DBF_EVENT(1, "tape: DOIO failed with er = %i\n", rc);
-+
-+		/* Set final status and remove. */
-+		request->rc = rc;
-+		__tape_remove_request(device, request);
++		/*
++		 * Not all characters could be written to the current
++		 * output buffer. Emit the buffer, create a new buffer
++		 * and then output the rest of the string.
++		 */
++		spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++		sclp_vt220_emit_current();
++		spin_lock_irqsave(&sclp_vt220_lock, flags);
++		buf += written;
++		count -= written;
++	} while (count > 0);
++	/* Setup timer to output current console buffer after some time */
++	if (sclp_vt220_current_request != NULL &&
++	    !timer_pending(&sclp_vt220_timer) && do_schedule) {
++		sclp_vt220_timer.function = sclp_vt220_timeout;
++		sclp_vt220_timer.data = 0UL;
++		sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY;
++		add_timer(&sclp_vt220_timer);
 +	}
++	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++	return overall_written;
 +}
 +
-+static void
-+tape_process_queue(void *data)
++/*
++ * This routine is called by the kernel to write a series of
++ * characters to the tty device.  The characters may come from
++ * user space or kernel space.  This routine will return the
++ * number of characters actually accepted for writing.
++ */
++static int
++sclp_vt220_write(struct tty_struct * tty, int from_user,
++		 const unsigned char *buf, int count)
 +{
-+	unsigned long		flags;
-+	struct tape_device *	device;
-+
-+	device = (struct tape_device *) data;
-+	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
-+	atomic_set(&device->bh_scheduled, 0);
-+	__tape_process_queue(device);
-+	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
++	return __sclp_vt220_write(from_user, buf, count, 1, 0);
 +}
 +
-+void
-+tape_schedule_bh(struct tape_device *device)
-+{
-+	/* Protect against rescheduling, when already running. */
-+	if (atomic_compare_and_swap(0, 1, &device->bh_scheduled))
-+		return;
-+
-+	INIT_LIST_HEAD(&device->bh_task.list);
-+	device->bh_task.sync    = 0;
-+	device->bh_task.routine = tape_process_queue;
-+	device->bh_task.data    = device;
-+
-+	queue_task(&device->bh_task, &tq_immediate);
-+	mark_bh(IMMEDIATE_BH);
-+
-+	return;
-+}
++#define SCLP_VT220_SESSION_ENDED	0x01
++#define	SCLP_VT220_SESSION_STARTED	0x80
++#define SCLP_VT220_SESSION_DATA		0x00
 +
 +/*
-+ * Stop running ccw. Has to be called with the device lock held.
++ * Called by the SCLP to report incoming event buffers.
 + */
-+static inline int
-+__tape_halt_io(struct tape_device *device, struct tape_request *request)
++static void
++sclp_vt220_receiver_fn(struct evbuf_header *evbuf)
 +{
-+	int retries;
-+	int rc;
++	char *buffer;
++	unsigned int count;
 +
-+	/* SMB: This should never happen */
-+	if(request->cpaddr == NULL)
-+		BUG();
++	/* Ignore input if device is not open */
++	if (sclp_vt220_tty == NULL)
++		return;
 +
-+	/* Check if interrupt has already been processed */
-+	if (request->callback == NULL)
-+		return 0;
++	buffer = (char *) ((addr_t) evbuf + sizeof(struct evbuf_header));
++	count = evbuf->length - sizeof(struct evbuf_header);
 +
-+	/* Stop a possibly running timer */
-+	if(request->timeout.expires) {
-+		if(del_timer(&request->timeout) > 0) {
-+			tape_put_request(request);
-+			request->timeout.data = 0L;
-+		}
++	switch (*buffer) {
++	case SCLP_VT220_SESSION_ENDED:
++	case SCLP_VT220_SESSION_STARTED:
++		break;
++	case SCLP_VT220_SESSION_DATA:
++		/* Send input to line discipline */
++		buffer++;
++		count--;
++		/* Prevent buffer overrun by discarding input. Note that
++		 * because buffer_push works asynchronously, we cannot wait
++		 * for the buffer to be emptied. */
++		if (count + sclp_vt220_tty->flip.count > TTY_FLIPBUF_SIZE)
++			count = TTY_FLIPBUF_SIZE - sclp_vt220_tty->flip.count;
++		memcpy(sclp_vt220_tty->flip.char_buf_ptr, buffer, count);
++		memset(sclp_vt220_tty->flip.flag_buf_ptr, TTY_NORMAL, count);
++		sclp_vt220_tty->flip.char_buf_ptr += count;
++		sclp_vt220_tty->flip.flag_buf_ptr += count;
++		sclp_vt220_tty->flip.count += count;
++		tty_flip_buffer_push(sclp_vt220_tty);
++		break;
 +	}
++}
 +
-+	rc = 0;
-+	for (retries = 0; retries < 5; retries++) {
-+		if (retries < 2)
-+			rc = halt_IO(device->devinfo.irq,
-+				     (long) request, request->options);
-+		else
-+			rc = clear_IO(device->devinfo.irq,
-+				      (long) request, request->options);
-+		if (rc == 0)
-+			break;		/* termination successful */
-+		if (rc == -ENODEV)
-+			DBF_EXCEPTION(2, "device gone, retry\n");
-+		else if (rc == -EIO)
-+			DBF_EXCEPTION(2, "I/O error, retry\n");
-+		else if (rc == -EBUSY)
-+			DBF_EXCEPTION(2, "device busy, retry later\n");
-+		else
-+			BUG();
-+	}
-+	if (rc == 0) {
-+		request->rc = -EIO;
-+		request->status = TAPE_REQUEST_DONE;
-+	}
-+	return rc;
++/*
++ * This routine is called when a particular tty device is opened.
++ */
++static int
++sclp_vt220_open(struct tty_struct * tty, struct file * filp)
++{
++	sclp_vt220_tty = tty;
++	tty->driver_data = NULL;
++	tty->low_latency = 0;
++	return 0;
 +}
 +
++/*
++ * This routine is called when a particular tty device is closed.
++ */
 +static void
-+__tape_remove_request(struct tape_device *device, struct tape_request *request)
++sclp_vt220_close(struct tty_struct * tty, struct file * filp)
 +{
-+	/* First remove the request from the queue. */
-+	list_del(&request->list);
-+
-+	/* This request isn't processed any further. */
-+	request->status = TAPE_REQUEST_DONE;
-+
-+	/* Finally, if the callback hasn't been called, do it now. */
-+	if (request->callback != NULL) {
-+		request->callback(request, request->callback_data);
-+		request->callback = NULL;
-+	}
++	if (tty->count > 1)
++		return;
++	sclp_vt220_tty = NULL;
 +}
 +
 +/*
-+ * Tape state functions
++ * This routine is called by the kernel to write a single
++ * character to the tty device.  If the kernel uses this routine,
++ * it must call the flush_chars() routine (if defined) when it is
++ * done stuffing characters into the driver.
++ *
++ * NOTE: include/linux/tty_driver.h specifies that a character should be
++ * ignored if there is no room in the queue. This driver implements a different
++ * semantic in that it will block when there is no more room left.
 + */
++static void
++sclp_vt220_put_char(struct tty_struct *tty, unsigned char ch)
++{
++	__sclp_vt220_write(0, &ch, 1, 0, 0);
++}
++
 +/*
-+ * Printable strings for tape enumerations.
++ * This routine is called by the kernel after it has written a
++ * series of characters to the tty device using put_char().  
 + */
-+const char *tape_state_string(struct tape_device *device) {
-+	char *s = " ???? ";
-+
-+	if (TAPE_NOT_OPER(device)) {
-+		s = "NOT_OP";
-+	} else if (TAPE_NOACCESS(device)) {
-+		s = "NO_ACC";
-+	} else if (TAPE_BOXED(device)) {
-+		s = "BOXED ";
-+	} else if (TAPE_OPEN(device)) {
-+		s = "IN_USE";
-+	} else if (TAPE_ASSIGNED(device)) {
-+		s = "ASSIGN";
-+	} else if (TAPE_INIT(device)) {
-+		s = "INIT  ";
-+	} else if (TAPE_UNUSED(device)) {
-+		s = "UNUSED";
-+	}
-+
-+	return s;
++static void
++sclp_vt220_flush_chars(struct tty_struct *tty)
++{
++	if (sclp_vt220_outqueue_count == 0)
++		sclp_vt220_emit_current();
++	else
++		sclp_vt220_flush_later = 1;
 +}
 +
-+void
-+tape_state_set(struct tape_device *device, unsigned int status)
++/*
++ * This routine returns the numbers of characters the tty driver
++ * will accept for queuing to be written.  This number is subject
++ * to change as output buffers get emptied, or if the output flow
++ * control is acted.
++ */
++static int
++sclp_vt220_write_room(struct tty_struct *tty)
 +{
-+	const char *str;
-+
-+	/* Maybe nothing changed. */
-+	if (device->tape_status == status)
-+		return;
-+
-+	DBF_EVENT(4, "ts. dev:  %x\n", device->first_minor);
-+	str = tape_state_string(device);
-+	DBF_EVENT(4, "old ts:  0x%08x %s\n", device->tape_status, str);
-+
-+	device->tape_status = status;
-+
-+	str = tape_state_string(device);
-+	DBF_EVENT(4, "new ts:  0x%08x %s\n", status, str);
++	unsigned long flags;
++	struct list_head *l;
++	int count;
 +
-+	wake_up(&device->state_change_wq);
++	spin_lock_irqsave(&sclp_vt220_lock, flags);
++	count = 0;
++	if (sclp_vt220_current_request != NULL)
++		count = sclp_vt220_space_left(sclp_vt220_current_request);
++	list_for_each(l, &sclp_vt220_empty)
++		count += SCLP_VT220_MAX_CHARS_PER_BUFFER;
++	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++	return count;
 +}
 +
-+void
-+tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate)
++/*
++ * Return number of buffered chars.
++ */
++static int
++sclp_vt220_chars_in_buffer(struct tty_struct *tty)
 +{
-+	if (device->medium_state == newstate)
-+		return;
++	unsigned long flags;
++	struct list_head *l;
++	struct sclp_vt220_request *r;
++	int count;
 +
-+	switch(newstate){
-+	case MS_UNLOADED:
-+		device->tape_generic_status |= GMT_DR_OPEN(~0);
-+		PRINT_INFO("(%04x): Tape is unloaded\n",
-+			   device->devstat.devno);
-+		break;
-+	case MS_LOADED:
-+		device->tape_generic_status &= ~GMT_DR_OPEN(~0);
-+		PRINT_INFO("(%04x): Tape has been mounted\n",
-+			   device->devstat.devno);
-+		break;
-+	default:
-+		// print nothing
-+		break;
++	spin_lock_irqsave(&sclp_vt220_lock, flags);
++	count = 0;
++	if (sclp_vt220_current_request != NULL)
++		count = sclp_vt220_chars_stored(sclp_vt220_current_request);
++	list_for_each(l, &sclp_vt220_outqueue) {
++		r = list_entry(l, struct sclp_vt220_request, list);
++		count += sclp_vt220_chars_stored(r);
 +	}
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+	tapeblock_medium_change(device);
-+#endif
-+	device->medium_state = newstate;
-+	wake_up(&device->state_change_wq);
++	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++	return count;
 +}
-+	
++
 +static void
-+tape_timeout_io(unsigned long data)
++__sclp_vt220_flush_buffer(void)
 +{
-+	struct tape_request	*request;
-+	struct tape_device	*device;
-+	unsigned long		 flags;
-+
-+	request = (struct tape_request *) data;
-+	device  = request->device;
-+
-+	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
-+	request->timeout.expires = 0;
-+
-+	if(request->callback != NULL) {
-+		DBF_EVENT(3, "TAPE(%04x): %s timeout\n",
-+			device->devstat.devno, tape_op_verbose[request->op]);
-+		PRINT_ERR("TAPE(%04x): %s timeout\n",
-+			device->devstat.devno, tape_op_verbose[request->op]);
++	unsigned long flags;
 +
-+		if(__tape_halt_io(device, request) == 0)
-+			DBF_EVENT(6, "tape_timeout_io: success\n");
-+		else {
-+			DBF_EVENT(2, "tape_timeout_io: halt_io failed\n");
-+			PRINT_ERR("tape_timeout_io: halt_io failed\n");
-+		}
++	sclp_vt220_emit_current();
++	spin_lock_irqsave(&sclp_vt220_lock, flags);
++	if (timer_pending(&sclp_vt220_timer))
++		del_timer(&sclp_vt220_timer);
++	while (sclp_vt220_outqueue_count > 0) {
++		spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++		sclp_sync_wait();
++		spin_lock_irqsave(&sclp_vt220_lock, flags);
 +	}
-+	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
-+	tape_put_request(request);
++	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
 +}
 +
 +/*
-+ * DEVFS Functions
-+ */
-+#ifdef CONFIG_DEVFS_FS
-+devfs_handle_t tape_devfs_root_entry;
-+
-+/*
-+ * Create devfs root entry (devno in hex) for device td
++ * Pass on all buffers to the hardware. Return only when there are no more
++ * buffers pending.
 + */
-+static int
-+tape_mkdevfsroot (struct tape_device* device)
++static void
++sclp_vt220_flush_buffer(struct tty_struct *tty)
 +{
-+	char devno [5];
-+
-+	sprintf(devno, "%04x", device->devinfo.devno);
-+	device->devfs_dir = devfs_mk_dir(tape_devfs_root_entry, devno, device);
-+	return (device->devfs_dir == NULL) ? -ENOMEM : 0;
++	sclp_vt220_emit_current();
 +}
 +
 +/*
-+ * Remove devfs root entry for a device
++ * Initialize all relevant components and register driver with system.
 + */
 +static void
-+tape_rmdevfsroot (struct tape_device *device)
++__sclp_vt220_init(int early)
 +{
-+	if (device->devfs_dir) {
-+		devfs_unregister(device->devfs_dir);
-+		device->devfs_dir = NULL;
++	void *page;
++	int i;
++
++	if (sclp_vt220_initialized)
++		return;
++	sclp_vt220_initialized = 1;
++	spin_lock_init(&sclp_vt220_lock);
++	INIT_LIST_HEAD(&sclp_vt220_empty);
++	INIT_LIST_HEAD(&sclp_vt220_outqueue);
++	init_waitqueue_head(&sclp_vt220_waitq);
++	init_timer(&sclp_vt220_timer);
++	sclp_vt220_current_request = NULL;
++	sclp_vt220_buffered_chars = 0;
++	sclp_vt220_outqueue_count = 0;
++	sclp_vt220_tty = NULL;
++	sclp_vt220_refcount = 0;
++	sclp_vt220_flush_later = 0;
++
++	/* Allocate pages for output buffering */
++	for (i = 0; i < (early ? MAX_CONSOLE_PAGES : MAX_KMEM_PAGES); i++) {
++		if (early)
++			page = alloc_bootmem_low_pages(PAGE_SIZE);
++		else
++			page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
++		if (page == NULL)
++			return;
++		list_add_tail((struct list_head *) page, &sclp_vt220_empty);
 +	}
 +}
-+#endif
 +
 +/*
-+ * Enable tape device
++ * Register driver with SCLP and Linux and initialize internal tty structures.
 + */
-+int
-+tape_request_irq(struct tape_device *device)
++void __init
++sclp_vt220_tty_init(void)
 +{
 +	int rc;
 +
-+	if (device->devinfo.status & DEVINFO_UNFRIENDLY_DEV) {
-+		s390_trigger_resense(device->devinfo.irq);
-+		rc = get_dev_info_by_devno(
-+			device->devinfo.devno,
-+			&device->devinfo
-+		);
-+		if (rc) {
-+			DBF_EVENT(3, "get_dev_info_by_devno returned %d\n", rc);
-+			if (rc == -EUSERS) {
-+				device->devinfo.status |=
-+					DEVINFO_UNFRIENDLY_DEV;
-+				TAPE_SET_STATE(device, TAPE_STATUS_BOXED);
-+			}
-+			return rc;
-+		}
-+	}
-+
-+	/* Register IRQ. */
-+	rc = s390_request_irq_special(
-+		device->devinfo.irq,
-+		__tape_do_irq,
-+		tape_noper_handler,
-+		SA_DOPATHGROUP,
-+		TAPE_MAGIC,
-+		&device->devstat
-+	);
-+	if (rc) {
-+		DBF_EVENT(3, "s390_request_irq_special returned %d\n", rc);
-+		if (rc == -EUSERS) {
-+			TAPE_SET_STATE(device, TAPE_STATUS_BOXED);
-+			device->devinfo.status |= DEVINFO_UNFRIENDLY_DEV;
-+		}
-+	} else {
-+		s390_set_private_data(
-+			device->devinfo.irq,
-+			tape_clone_device(device)
-+		);
-+		TAPE_CLEAR_STATE(device, TAPE_STATUS_BOXED);
-+	}
-+
-+	DBF_EVENT(3, "tape_request_irq returns %d\n", rc);
-+	return rc;
-+}
-+
-+void
-+tape_free_irq(struct tape_device *device)
-+{
-+	if(!(device->devinfo.status & DEVINFO_UNFRIENDLY_DEV)) {
-+		s390_set_private_data(device->devinfo.irq, NULL);
-+		tape_put_device(device);
-+		free_irq(device->devinfo.irq, &device->devstat);
++	/* Note: we're not testing for CONSOLE_IS_SCLP here to preserve
++	 * symmetry between VM and LPAR systems regarding ttyS1. */
++	__sclp_vt220_init(0);
++	rc = sclp_register(&sclp_vt220_register);
++	if (rc != 0) {
++		printk(KERN_ERR SCLP_VT220_PRINT_HEADER
++		       "could not register tty - "
++		       "sclp_register returned %d\n", rc);
++		return;
 +	}
-+}
 +
-+int
-+tape_enable_device(struct tape_device *device,
-+		   struct tape_discipline *discipline)
-+{
-+	int rc;
++	memset (&sclp_vt220_driver, 0, sizeof(struct tty_driver));
++	sclp_vt220_driver.magic	= TTY_DRIVER_MAGIC;
++	sclp_vt220_driver.driver_name = SCLP_VT220_DRIVER_NAME;
++	sclp_vt220_driver.name = SCLP_VT220_DEVICE_NAME;
++	sclp_vt220_driver.name_base = 0;
++	sclp_vt220_driver.major = SCLP_VT220_MAJOR;
++	sclp_vt220_driver.minor_start = SCLP_VT220_MINOR;
++	sclp_vt220_driver.num = 1;
++	sclp_vt220_driver.type = TTY_DRIVER_TYPE_SYSTEM;
++	sclp_vt220_driver.subtype = SYSTEM_TYPE_TTY;
++	sclp_vt220_driver.init_termios = tty_std_termios;
++	sclp_vt220_driver.flags = TTY_DRIVER_REAL_RAW;
++	sclp_vt220_driver.refcount = &sclp_vt220_refcount;
++	sclp_vt220_driver.table = sclp_vt220_table;
++	sclp_vt220_driver.termios = sclp_vt220_termios;
++	sclp_vt220_driver.termios_locked = sclp_vt220_termios_locked;
 +
-+	device->discipline = discipline;
-+	if (!TAPE_INIT(device))
-+		return -EINVAL;
++	/* Required callbacks */
++	sclp_vt220_driver.open = sclp_vt220_open;
++	sclp_vt220_driver.close = sclp_vt220_close;
++	sclp_vt220_driver.write = sclp_vt220_write;
++	sclp_vt220_driver.put_char = sclp_vt220_put_char;
++	sclp_vt220_driver.flush_chars = sclp_vt220_flush_chars;
++	sclp_vt220_driver.write_room = sclp_vt220_write_room;
++	sclp_vt220_driver.chars_in_buffer = sclp_vt220_chars_in_buffer;
++	sclp_vt220_driver.flush_buffer = sclp_vt220_flush_buffer;
 +
-+	rc = tape_request_irq(device);
-+	if(rc && rc != -EUSERS)
-+		return rc;
++	/* Unsupported callbacks */
++	sclp_vt220_driver.ioctl = NULL;
++	sclp_vt220_driver.throttle = NULL;
++	sclp_vt220_driver.unthrottle = NULL;
++	sclp_vt220_driver.send_xchar = NULL;
++	sclp_vt220_driver.set_termios = NULL;
++	sclp_vt220_driver.set_ldisc = NULL;
++	sclp_vt220_driver.stop = NULL;
++	sclp_vt220_driver.start = NULL;
++	sclp_vt220_driver.hangup = NULL;
++	sclp_vt220_driver.break_ctl = NULL;
++	sclp_vt220_driver.wait_until_sent = NULL;
++	sclp_vt220_driver.read_proc = NULL;
++	sclp_vt220_driver.write_proc = NULL;
 +
-+	/* Let the discipline have a go at the device. */
-+	rc = discipline->setup_device(device);
-+	if (rc) {
-+		DBF_EVENT(3, "discipline->setup_device returned %d\n", rc);
-+		tape_free_irq(device);
-+		return rc;
-+	}
++	rc = tty_register_driver(&sclp_vt220_driver);
++	if (rc != 0)
++		printk(KERN_ERR SCLP_VT220_PRINT_HEADER
++		       "could not register tty - "
++		       "sclp_drv_register returned %d\n", rc);
++}
 +
-+#ifdef CONFIG_DEVFS_FS
-+	/* Create devfs entries */
-+	rc = tape_mkdevfsroot(device);
-+	if (rc){
-+		DBF_EVENT(3, "tape_mkdevfsroot returned %d\n", rc);
-+		PRINT_WARN ("Cannot create a devfs directory for "
-+			    "device %04x\n", device->devinfo.devno);
-+		device->discipline->cleanup_device(device);
-+		tape_free_irq(device);
-+		return rc;
-+	}
-+#endif
-+	rc = tapechar_setup_device(device);
-+	if (rc) {
-+		DBF_EVENT(3, "tapechar_setup_device returned %d\n", rc);
-+#ifdef CONFIG_DEVFS_FS
-+		tape_rmdevfsroot(device);
-+#endif
-+		device->discipline->cleanup_device(device);
-+		tape_free_irq(device);
-+		return rc;
-+	}
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+	rc = tapeblock_setup_device(device);
-+	if (rc) {
-+		DBF_EVENT(3, "tapeblock_setup_device returned %d\n", rc);
-+		tapechar_cleanup_device(device);
-+#ifdef CONFIG_DEVFS_FS
-+		tape_rmdevfsroot(device);
-+#endif
-+		device->discipline->cleanup_device(device);
-+		tape_free_irq(device);
-+		return rc;
-+	}
-+#endif
++#ifdef CONFIG_SCLP_VT220_CONSOLE
 +
-+	if(!TAPE_BOXED(device))
-+		TAPE_CLEAR_STATE(device, TAPE_STATUS_INIT);
++static void
++sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count)
++{
++	__sclp_vt220_write(0, (const unsigned char *) buf, count, 1, 1);
++}
 +
-+	return 0;
++static kdev_t
++sclp_vt220_con_device(struct console *c)
++{
++	return mk_kdev(SCLP_VT220_MAJOR, SCLP_VT220_MINOR);
 +}
 +
 +/*
-+ * Disable tape device. Check if there is a running request and
-+ * terminate it. Post all queued requests with -EIO.
++ * This routine is called from panic when the kernel is going to give up.
++ * We have to make sure that all buffers will be flushed to the SCLP.
++ * Note that this function may be called from within an interrupt context.
 + */
-+void
-+tape_disable_device(struct tape_device *device, int gone)
++static void
++sclp_vt220_con_unblank(void)
 +{
-+	struct list_head *	l, *n;
-+	struct tape_request *	request;
-+	unsigned long		flags;
++	__sclp_vt220_flush_buffer();
++}
 +
-+	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
-+	/* Post remaining requests with -EIO */
-+	list_for_each_safe(l, n, &device->req_queue) {
-+		request = list_entry(l, struct tape_request, list);
-+		if (request->status == TAPE_REQUEST_IN_IO && !gone) {
-+			__tape_halt_io(device, request);
-+		}
++/* Structure needed to register with printk */
++static struct console sclp_vt220_console =
++{
++	.name = SCLP_VT220_CONSOLE_NAME,
++	.write = sclp_vt220_con_write,
++	.device = sclp_vt220_con_device,
++	.unblank = sclp_vt220_con_unblank,
++	.flags = CON_PRINTBUFFER,
++	.index = SCLP_VT220_CONSOLE_INDEX
++};
 +
-+		request->rc = -EIO;
-+		request->status = TAPE_REQUEST_DONE;
-+		__tape_remove_request(device, request);
-+	}
++void
++sclp_vt220_con_init(void)
++{
++        if (!CONSOLE_IS_SCLP)
++		return;
++	__sclp_vt220_init(1);
++	/* Attach linux console */
++	register_console(&sclp_vt220_console);
++}
 +
-+	if (TAPE_ASSIGNED(device) && !gone) {
-+		spin_unlock(get_irq_lock(device->devinfo.irq));
-+		if(
-+			tape_unassign(
-+				device,
-+				TAPE_STATUS_ASSIGN_M|TAPE_STATUS_ASSIGN_A
-+			) == 0
-+		) {
-+			printk(KERN_WARNING "%04x: automatically unassigned\n",
-+				device->devinfo.devno);
-+		}
-+		spin_lock(get_irq_lock(device->devinfo.irq));
-+	}
++#endif /* CONFIG_SCLP_VT220_CONSOLE */
 +
-+	TAPE_SET_STATE(device, TAPE_STATUS_NOT_OPER);
-+	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape3480.c drivers/s390/char/tape3480.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape3480.c	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tape3480.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1,156 +0,0 @@
+-/***************************************************************************
+- *
+- *  drivers/s390/char/tape3480.c
+- *    tape device discipline for 3480 tapes.
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s): Carsten Otte <cotte at de.ibm.com>
+- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- * 
+- ****************************************************************************
+- */
+-
+-#include "tapedefs.h"
+-#include <linux/version.h>
+-#include <asm/ccwcache.h>	/* CCW allocations      */
+-#include <asm/s390dyn.h>
+-#include <asm/debug.h>
+-#include <linux/compatmac.h>
+-#include "tape.h"
+-#include "tape34xx.h"
+-#include "tape3480.h"
+-
+-tape_event_handler_t tape3480_event_handler_table[TS_SIZE][TE_SIZE] =
+-{
+-    /* {START , DONE, FAILED, ERROR, OTHER } */
+-	{NULL, tape34xx_unused_done, NULL, NULL, NULL},	/* TS_UNUSED */
+-	{NULL, tape34xx_idle_done, NULL, NULL, NULL},	/* TS_IDLE */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_DONE */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_FAILED */
+-	{NULL, tape34xx_block_done, NULL, NULL, NULL},		/* TS_BLOCK_INIT */
+-	{NULL, tape34xx_bsb_init_done, NULL, NULL, NULL},	/* TS_BSB_INIT */
+-	{NULL, tape34xx_bsf_init_done, NULL, NULL, NULL},	/* TS_BSF_INIT */
+-	{NULL, tape34xx_dse_init_done, NULL, NULL, NULL},	/* TS_DSE_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_EGA_INIT */
+-	{NULL, tape34xx_fsb_init_done, NULL, NULL, NULL},	/* TS_FSB_INIT */
+-	{NULL, tape34xx_fsf_init_done, NULL, NULL, NULL},	/* TS_FSF_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_LDI_INIT */
+-	{NULL, tape34xx_lbl_init_done, NULL, NULL, NULL},	/* TS_LBL_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_MSE_INIT */
+-	{NULL, tape34xx_nop_init_done, NULL, NULL, NULL},	/* TS_NOP_INIT */
+-	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},		/* TS_RBA_INIT */
+-	{NULL, tape34xx_rbi_init_done, NULL, NULL, NULL},	/* TS_RBI_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBU_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBL_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RDC_INIT */
+-	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},	/* TS_RFO_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RSD_INIT */
+-	{NULL, tape34xx_rew_init_done, NULL, NULL, NULL},	/* TS_REW_INIT */
+-	{NULL, tape34xx_rew_release_init_done, NULL, NULL, NULL},	/* TS_REW_RELEASE_IMIT */
+-	{NULL, tape34xx_run_init_done, NULL, NULL, NULL},	/* TS_RUN_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SEN_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SID_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SNP_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SPG_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SWI_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SMR_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SYN_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_TIO_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_UNA_INIT */
+-	{NULL, tape34xx_wri_init_done, NULL, NULL, NULL},	/* TS_WRI_INIT */
+-	{NULL, tape34xx_wtm_init_done, NULL, NULL, NULL},	/* TS_WTM_INIT */
+-        {NULL, NULL, NULL, NULL, NULL}};     /* TS_NOT_OPER */
+-
+-devreg_t tape3480_devreg = {
+-    ci:
+-    {hc:
+-     {ctype:0x3480}},
+-    flag:DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS,
+-    oper_func:tape_oper_handler
+-};
+-
+-
+-void
+-tape3480_setup_assist (tape_info_t * ti)
+-{
+-	tape3480_disc_data_t *data = NULL;
+-#ifdef TAPE_DEBUG
+-    debug_text_event (tape_debug_area,6,"3480 dsetu");
+-    debug_text_event (tape_debug_area,6,"dev:");
+-    debug_int_event (tape_debug_area,6,ti->blk_minor);
+-#endif /* TAPE_DEBUG */
+-	while (data == NULL)
+-		data = kmalloc (sizeof (tape3480_disc_data_t), GFP_KERNEL);
+-	data->modeset_byte = 0x00;
+-	ti->discdata = (void *) data;
+-}
+-
+-
+-void
+-tape3480_shutdown (int autoprobe) {
+-    if (autoprobe)
+-	s390_device_unregister(&tape3480_devreg);
+-}
+-
+-tape_discipline_t *
+-tape3480_init (int autoprobe)
+-{
+-	tape_discipline_t *disc;
+-#ifdef TAPE_DEBUG
+-    debug_text_event (tape_debug_area,3,"3480 init");
+-#endif /* TAPE_DEBUG */
+-	disc = kmalloc (sizeof (tape_discipline_t), GFP_KERNEL);
+-	if (disc == NULL) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,3,"disc:nomem");
+-#endif /* TAPE_DEBUG */
+-		return disc;
+-	}
+-	disc->cu_type = 0x3480;
+-	disc->setup_assist = tape3480_setup_assist;
+-	disc->error_recovery = tape34xx_error_recovery;
+-	disc->write_block = tape34xx_write_block;
+-	disc->free_write_block = tape34xx_free_write_block;
+-	disc->read_block = tape34xx_read_block;
+-	disc->free_read_block = tape34xx_free_read_block;
+-	disc->mtfsf = tape34xx_mtfsf;
+-	disc->mtbsf = tape34xx_mtbsf;
+-	disc->mtfsr = tape34xx_mtfsr;
+-	disc->mtbsr = tape34xx_mtbsr;
+-	disc->mtweof = tape34xx_mtweof;
+-	disc->mtrew = tape34xx_mtrew;
+-	disc->mtoffl = tape34xx_mtoffl;
+-	disc->mtnop = tape34xx_mtnop;
+-	disc->mtbsfm = tape34xx_mtbsfm;
+-	disc->mtfsfm = tape34xx_mtfsfm;
+-	disc->mteom = tape34xx_mteom;
+-	disc->mterase = tape34xx_mterase;
+-	disc->mtsetdensity = tape34xx_mtsetdensity;
+-	disc->mtseek = tape34xx_mtseek;
+-	disc->mttell = tape34xx_mttell;
+-	disc->mtsetdrvbuffer = tape34xx_mtsetdrvbuffer;
+-	disc->mtlock = tape34xx_mtlock;
+-	disc->mtunlock = tape34xx_mtunlock;
+-	disc->mtload = tape34xx_mtload;
+-	disc->mtunload = tape34xx_mtunload;
+-	disc->mtcompression = tape34xx_mtcompression;
+-	disc->mtsetpart = tape34xx_mtsetpart;
+-	disc->mtmkpart = tape34xx_mtmkpart;
+-	disc->mtiocget = tape34xx_mtiocget;
+-	disc->mtiocpos = tape34xx_mtiocpos;
+-	disc->shutdown = tape3480_shutdown;
+-	disc->discipline_ioctl_overload = tape34xx_ioctl_overload;
+-	disc->event_table = &tape3480_event_handler_table;
+-	disc->default_handler = tape34xx_default_handler;
+-	disc->bread = tape34xx_bread;
+-	disc->free_bread = tape34xx_free_bread;
+-	disc->tape = NULL;	/* pointer for backreference */
+-	disc->next = NULL;
+-	if (autoprobe)
+-	    s390_device_register(&tape3480_devreg);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,3,"3480 regis");
+-#endif /* TAPE_DEBUG */
+-	return disc;
+-}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape3480.h drivers/s390/char/tape3480.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape3480.h	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tape3480.h	2006-02-12 12:47:23.000000000 -0700
+@@ -1,23 +0,0 @@
+-/***************************************************************************
+- *
+- *  drivers/s390/char/tape3480.h
+- *    tape device discipline for 3480 tapes.
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s): Carsten Otte <cotte at de.ibm.com>
+- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *
+- ****************************************************************************
+- */
+-
+-#ifndef _TAPE3480_H
+-
+-#define _TAPE3480_H
+-
+-
+-typedef struct _tape3480_disc_data_t {
+-    __u8 modeset_byte;
+-} tape3480_disc_data_t  __attribute__ ((packed, aligned(8)));
+-tape_discipline_t * tape3480_init (int);
+-#endif // _TAPE3480_H
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape3490.c drivers/s390/char/tape3490.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape3490.c	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tape3490.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1,156 +0,0 @@
+-/***************************************************************************
+- *
+- *  drivers/s390/char/tape3490.c
+- *    tape device discipline for 3490E tapes.
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s): Carsten Otte <cotte at de.ibm.com>
+- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *
+- ****************************************************************************
+- */
+-
+-#include "tapedefs.h"
+-#include <linux/version.h>
+-#include <asm/ccwcache.h>	/* CCW allocations      */
+-#include <asm/s390dyn.h>
+-#include <asm/debug.h>
+-#include <linux/compatmac.h>
+-#include "tape.h"
+-#include "tape34xx.h"
+-#include "tape3490.h"
+-
+-tape_event_handler_t tape3490_event_handler_table[TS_SIZE][TE_SIZE] =
+-{
+-    /* {START , DONE, FAILED, ERROR, OTHER } */
+-	{NULL, tape34xx_unused_done, NULL, NULL, NULL},	/* TS_UNUSED */
+-	{NULL, tape34xx_idle_done, NULL, NULL, NULL},	/* TS_IDLE */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_DONE */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_FAILED */
+-	{NULL, tape34xx_block_done, NULL, NULL, NULL},		/* TS_BLOCK_INIT */
+-	{NULL, tape34xx_bsb_init_done, NULL, NULL, NULL},	/* TS_BSB_INIT */
+-	{NULL, tape34xx_bsf_init_done, NULL, NULL, NULL},	/* TS_BSF_INIT */
+-	{NULL, tape34xx_dse_init_done, NULL, NULL, NULL},	/* TS_DSE_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_EGA_INIT */
+-	{NULL, tape34xx_fsb_init_done, NULL, NULL, NULL},	/* TS_FSB_INIT */
+-	{NULL, tape34xx_fsf_init_done, NULL, NULL, NULL},	/* TS_FSF_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_LDI_INIT */
+-	{NULL, tape34xx_lbl_init_done, NULL, NULL, NULL},	/* TS_LBL_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_MSE_INIT */
+-	{NULL, tape34xx_nop_init_done, NULL, NULL, NULL},	/* TS_NOP_INIT */
+-	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},		/* TS_RBA_INIT */
+-	{NULL, tape34xx_rbi_init_done, NULL, NULL, NULL},	/* TS_RBI_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBU_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBL_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RDC_INIT */
+-	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},	/* TS_RFO_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RSD_INIT */
+-	{NULL, tape34xx_rew_init_done, NULL, NULL, NULL},	/* TS_REW_INIT */
+-	{NULL, tape34xx_rew_release_init_done, NULL, NULL, NULL},	/* TS_REW_RELEASE_IMIT */
+-	{NULL, tape34xx_run_init_done, NULL, NULL, NULL},	/* TS_RUN_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SEN_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SID_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SNP_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SPG_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SWI_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SMR_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SYN_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_TIO_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_UNA_INIT */
+-	{NULL, tape34xx_wri_init_done, NULL, NULL, NULL},	/* TS_WRI_INIT */
+-	{NULL, tape34xx_wtm_init_done, NULL, NULL, NULL},	/* TS_WTM_INIT */
+-        {NULL, NULL, NULL, NULL, NULL}};     /* TS_NOT_OPER */
+-
+-devreg_t tape3490_devreg = {
+-    ci:
+-    {hc:
+-     {ctype:0x3490}},
+-    flag:DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS,
+-    oper_func:tape_oper_handler
+-};
+-
+-void
+-tape3490_setup_assist (tape_info_t * ti)
+-{
+-	tape3490_disc_data_t *data = NULL;
+-#ifdef TAPE_DEBUG
+-    debug_text_event (tape_debug_area,6,"3490 dsetu");
+-    debug_text_event (tape_debug_area,6,"dev:");
+-    debug_int_event (tape_debug_area,6,ti->blk_minor);
+-#endif /* TAPE_DEBUG */
+-	while (data == NULL)
+-		data = kmalloc (sizeof (tape3490_disc_data_t), GFP_KERNEL);
+-	data->modeset_byte = 0x00;
+-	ti->discdata = (void *) data;
+-}
+-
+-
+-void
+-tape3490_shutdown (int autoprobe) {
+-    if (autoprobe)
+-	s390_device_unregister(&tape3490_devreg);
+-}
+-
+-
+-tape_discipline_t *
+-tape3490_init (int autoprobe)
+-{
+-	tape_discipline_t *disc;
+-#ifdef TAPE_DEBUG
+-    debug_text_event (tape_debug_area,3,"3490 init");
+-#endif /* TAPE_DEBUG */
+-	disc = kmalloc (sizeof (tape_discipline_t), GFP_KERNEL);
+-	if (disc == NULL) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,3,"disc:nomem");
+-#endif /* TAPE_DEBUG */
+-		return disc;
+-	}
+-	disc->cu_type = 0x3490;
+-	disc->setup_assist = tape3490_setup_assist;
+-	disc->error_recovery = tape34xx_error_recovery;
+-	disc->write_block = tape34xx_write_block;
+-	disc->free_write_block = tape34xx_free_write_block;
+-	disc->read_block = tape34xx_read_block;
+-	disc->free_read_block = tape34xx_free_read_block;
+-	disc->mtfsf = tape34xx_mtfsf;
+-	disc->mtbsf = tape34xx_mtbsf;
+-	disc->mtfsr = tape34xx_mtfsr;
+-	disc->mtbsr = tape34xx_mtbsr;
+-	disc->mtweof = tape34xx_mtweof;
+-	disc->mtrew = tape34xx_mtrew;
+-	disc->mtoffl = tape34xx_mtoffl;
+-	disc->mtnop = tape34xx_mtnop;
+-	disc->mtbsfm = tape34xx_mtbsfm;
+-	disc->mtfsfm = tape34xx_mtfsfm;
+-	disc->mteom = tape34xx_mteom;
+-	disc->mterase = tape34xx_mterase;
+-	disc->mtsetdensity = tape34xx_mtsetdensity;
+-	disc->mtseek = tape34xx_mtseek;
+-	disc->mttell = tape34xx_mttell;
+-	disc->mtsetdrvbuffer = tape34xx_mtsetdrvbuffer;
+-	disc->mtlock = tape34xx_mtlock;
+-	disc->mtunlock = tape34xx_mtunlock;
+-	disc->mtload = tape34xx_mtload;
+-	disc->mtunload = tape34xx_mtunload;
+-	disc->mtcompression = tape34xx_mtcompression;
+-	disc->mtsetpart = tape34xx_mtsetpart;
+-	disc->mtmkpart = tape34xx_mtmkpart;
+-	disc->mtiocget = tape34xx_mtiocget;
+-	disc->mtiocpos = tape34xx_mtiocpos;
+-	disc->shutdown = tape3490_shutdown;
+-	disc->discipline_ioctl_overload = tape34xx_ioctl_overload;
+-	disc->event_table = &tape3490_event_handler_table;
+-	disc->default_handler = tape34xx_default_handler;
+-	disc->bread = tape34xx_bread;
+-	disc->free_bread = tape34xx_free_bread;
+-	disc->tape = NULL;	/* pointer for backreference */
+-	disc->next = NULL;
+-	if (autoprobe)
+-	    s390_device_register(&tape3490_devreg);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,3,"3490 regis");
+-#endif /* TAPE_DEBUG */
+-	return disc;
+-}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape3490.h drivers/s390/char/tape3490.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape3490.h	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tape3490.h	2006-02-12 12:47:23.000000000 -0700
+@@ -1,24 +0,0 @@
+-
+-/***************************************************************************
+- *
+- *  drivers/s390/char/tape3490.h
+- *    tape device discipline for 3490E tapes.
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s): Carsten Otte <cotte at de.ibm.com>
+- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *
+- ****************************************************************************
+- */
+-
+-#ifndef _TAPE3490_H
+-
+-#define _TAPE3490_H
+-
+-
+-typedef struct _tape3490_disc_data_t {
+-    __u8 modeset_byte;
+-} tape3490_disc_data_t  __attribute__ ((packed, aligned(8)));
+-tape_discipline_t * tape3490_init (int);
+-#endif // _TAPE3490_H
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape_34xx.c drivers/s390/char/tape_34xx.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape_34xx.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/tape_34xx.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,1357 @@
++/*
++ *  drivers/s390/char/tape_34xx.c
++ *    tape device discipline for 3480/3490 tapes.
++ *
++ *  S390 and zSeries version
++ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Carsten Otte <cotte at de.ibm.com>
++ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
++ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
++ *		 Stefan Bader <shbader at de.ibm.com>
++ */
 +
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+	tapeblock_cleanup_device(device);
-+#endif
-+	tapechar_cleanup_device(device);
-+#ifdef CONFIG_DEVFS_FS
-+	tape_rmdevfsroot(device);
-+#endif
-+	device->discipline->cleanup_device(device);
-+	tape_free_irq(device);
-+}
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <asm/tape390.h>
++
++#define TAPE_DBF_AREA	tape_34xx_dbf
 +
-+/*
-+ * Find discipline by cu_type.
-+ */
-+struct tape_discipline *
-+tape_get_discipline(int cu_type)
-+{
-+	struct list_head *l;
-+	struct tape_discipline *discipline, *tmp;
++#include "tape.h"
++#include "tape_std.h"
 +
-+	discipline = NULL;
-+	spin_lock(&tape_discipline_lock);
-+	list_for_each(l, &tape_disciplines) {
-+		tmp = list_entry(l, struct tape_discipline, list);
-+		if (tmp->cu_type == cu_type) {
-+			discipline = tmp;
-+			break;
-+		}
-+	}
-+	if (discipline->owner != NULL) {
-+		if (!try_inc_mod_count(discipline->owner))
-+			/* Discipline is currently unloaded! */
-+			discipline = NULL;
-+	}
-+	spin_unlock(&tape_discipline_lock);
-+	return discipline;
-+}
++#define PRINTK_HEADER "T34xx:"
 +
 +/*
-+ * Decrement usage count for discipline.
++ * Pointer to debug area.
 + */
-+void
-+tape_put_discipline(struct tape_discipline *discipline)
-+{
-+	spin_lock(&tape_discipline_lock);
-+	if (discipline->owner)
-+		__MOD_DEC_USE_COUNT(discipline->owner);
-+	spin_unlock(&tape_discipline_lock);
-+}
++debug_info_t *TAPE_DBF_AREA = NULL;
 +
 +/*
-+ * Register backend discipline
++ * The block ID is the complete marker for a specific tape position.
++ * It contains a physical part (wrap, segment, format) and a logical
++ * block number.
 + */
-+int
-+tape_register_discipline(struct tape_discipline *discipline)
-+{
-+	if (!try_inc_mod_count(THIS_MODULE))
-+		/* Tape module is currently unloaded! */
-+		return -ENOSYS;
-+	spin_lock(&tape_discipline_lock);
-+	list_add_tail(&discipline->list, &tape_disciplines);
-+	spin_unlock(&tape_discipline_lock);
-+	/* Now add the tape devices with matching cu_type. */
-+	tape_add_devices(discipline);
-+	return 0;
-+}
++#define TBI_FORMAT_3480			0x00
++#define TBI_FORMAT_3480_2_XF		0x01
++#define TBI_FORMAT_3480_XF		0x02
++#define TBI_FORMAT_RESERVED		0x03
 +
-+/*
-+ * Unregister backend discipline
-+ */
-+void
-+__tape_unregister_discipline(struct tape_discipline *discipline)
-+{
-+	list_del(&discipline->list);
-+	/* Remove tape devices with matching cu_type. */
-+	tape_remove_devices(discipline);
-+	MOD_DEC_USE_COUNT;
-+}
++struct tape_34xx_block_id {
++        unsigned int			tbi_wrap	: 1;
++        unsigned int			tbi_segment	: 7;
++        unsigned int			tbi_format	: 2;
++        unsigned int			tbi_block	: 22;
++} __attribute__ ((packed));
 +
-+void
-+tape_unregister_discipline(struct tape_discipline *discipline)
-+{
-+	struct list_head *l;
++struct sbid_entry {
++	struct list_head		list;
++	struct tape_34xx_block_id	bid;
++};
 +
-+	spin_lock(&tape_discipline_lock);
-+	list_for_each(l, &tape_disciplines) {
-+		if (list_entry(l, struct tape_discipline, list) == discipline){
-+			__tape_unregister_discipline(discipline);
-+			break;
-+		}
-+	}
-+	spin_unlock(&tape_discipline_lock);
-+}
++struct tape_34xx_discdata {
++	/* A list of block id's of the tape segments (for faster seek) */
++	struct list_head		sbid_list;
++};
 +
-+/*
-+ * Allocate a new tape ccw request
-+ */
-+struct tape_request *
-+tape_alloc_request(int cplength, int datasize)
++/* Internal prototypes */
++static void tape_34xx_clear_sbid_list(struct tape_device *);
++
++/* 34xx specific functions */
++static void
++__tape_34xx_medium_sense_callback(struct tape_request *request, void *data)
 +{
-+	struct tape_request *request;
++	unsigned char *sense = request->cpdata;
 +
-+	if (datasize > PAGE_SIZE || (cplength*sizeof(ccw1_t)) > PAGE_SIZE)
-+		BUG();
++	request->callback = NULL;
 +
-+	DBF_EVENT(5, "tape_alloc_request(%d,%d)\n", cplength, datasize);
++	DBF_EVENT(5, "TO_MSEN[0]: %08x\n", *((unsigned int *) sense));
++	DBF_EVENT(5, "TO_MSEN[1]: %08x\n", *((unsigned int *) sense+1));
++	DBF_EVENT(5, "TO_MSEN[2]: %08x\n", *((unsigned int *) sense+2));
++	DBF_EVENT(5, "TO_MSEN[3]: %08x\n", *((unsigned int *) sense+3));
 +
-+	request = (struct tape_request *)
-+		kmalloc(sizeof(struct tape_request), GFP_KERNEL);
-+	if (request == NULL) {
-+		DBF_EXCEPTION(1, "cqra nomem\n");
-+		return ERR_PTR(-ENOMEM);
++	if(sense[0] & SENSE_INTERVENTION_REQUIRED) {
++		tape_med_state_set(request->device, MS_UNLOADED);
++	} else {
++		tape_med_state_set(request->device, MS_LOADED);
 +	}
-+	memset(request, 0, sizeof(struct tape_request));
-+	INIT_LIST_HEAD(&request->list);
-+	atomic_set(&request->ref_count, 1);
 +
-+	/* allocate channel program */
-+	if (cplength > 0) {
-+		request->cpaddr =
-+			kmalloc(cplength*sizeof(ccw1_t), GFP_ATOMIC | GFP_DMA);
-+		if (request->cpaddr == NULL) {
-+			DBF_EXCEPTION(1, "cqra nomem\n");
-+			kfree(request);
-+			return ERR_PTR(-ENOMEM);
-+		}
-+		memset(request->cpaddr, 0, cplength*sizeof(ccw1_t));
-+	}
-+	/* alloc small kernel buffer */
-+	if (datasize > 0) {
-+		request->cpdata = kmalloc(datasize, GFP_KERNEL | GFP_DMA);
-+		if (request->cpdata == NULL) {
-+			DBF_EXCEPTION(1, "cqra nomem\n");
-+			if (request->cpaddr != NULL)
-+				kfree(request->cpaddr);
-+			kfree(request);
-+			return ERR_PTR(-ENOMEM);
-+		}
-+		memset(request->cpdata, 0, datasize);
++	if(sense[1] & SENSE_WRITE_PROTECT) {
++		request->device->tape_generic_status |= GMT_WR_PROT(~0);
++	} else{
++		request->device->tape_generic_status &= ~GMT_WR_PROT(~0);
 +	}
 +
-+	DBF_EVENT(5, "request=%p(%p/%p)\n", request, request->cpaddr,
-+		request->cpdata);
-+
-+	return request;
++	tape_put_request(request);
 +}
 +
-+/*
-+ * Free tape ccw request
-+ */
-+void
-+tape_free_request (struct tape_request * request)
++static int
++tape_34xx_medium_sense(struct tape_device *device)
 +{
-+	DBF_EVENT(5, "tape_free_request(%p)\n", request);
++	struct tape_request *	request;
++	int			rc;
 +
-+	if (request->device != NULL) {
-+		tape_put_device(request->device);
-+		request->device = NULL;
-+	}
-+	if (request->cpdata != NULL) {
-+		kfree(request->cpdata);
-+	}
-+	if (request->cpaddr != NULL) {
-+		kfree(request->cpaddr);
++	tape_34xx_clear_sbid_list(device);
++
++	request = tape_alloc_request(1, 32);
++	if(IS_ERR(request)) {
++		DBF_EXCEPTION(6, "MSN fail\n");
++		return PTR_ERR(request);
 +	}
-+	kfree(request);
-+}
 +
-+struct tape_request *
-+tape_clone_request(struct tape_request *request)
-+{
-+	DBF_EVENT(5, "tape_clone_request(%p) = %i\n", request,
-+		atomic_inc_return(&request->ref_count));
-+	return request;
++	request->op = TO_MSEN;
++	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);
++	request->callback = __tape_34xx_medium_sense_callback;
++
++	rc = tape_do_io_async(device, request);
++
++	return rc;
 +}
 +
-+struct tape_request *
-+tape_put_request(struct tape_request *request)
++static void
++tape_34xx_work_handler(void *data)
 +{
-+	int remain;
++	struct {
++		struct tape_device	*device;
++		enum tape_op		 op;
++		struct tq_struct	 task;
++	} *p = data;
 +
-+	DBF_EVENT(4, "tape_put_request(%p)\n", request);
-+	if((remain = atomic_dec_return(&request->ref_count)) > 0) {
-+		DBF_EVENT(5, "remaining = %i\n", remain);
-+	} else {
-+		tape_free_request(request);
++	switch(p->op) {
++		case TO_MSEN:
++			tape_34xx_medium_sense(p->device);
++			break;
++		default:
++			DBF_EVENT(3, "T34XX: internal error: unknown work\n");
 +	}
 +
-+	return NULL;
++	tape_put_device(p->device);
++	kfree(p);
 +}
 +
 +/*
-+ * Write sense data to console/dbf
++ * This function is currently used to schedule a sense for later execution.
++ * For example whenever a unsolicited interrupt signals a new tape medium
++ * and we can't call tape_do_io from that interrupt handler.
 + */
-+void
-+tape_dump_sense(struct tape_device* device, struct tape_request *request)
++static int
++tape_34xx_schedule_work(struct tape_device *device, enum tape_op op)
 +{
-+	devstat_t *stat;
-+	unsigned int *sptr;
++	struct {
++		struct tape_device	*device;
++		enum tape_op		 op;
++		struct tq_struct	 task;
++	} *p;
 +
-+	stat = &device->devstat;
-+	PRINT_INFO("-------------------------------------------------\n");
-+	PRINT_INFO("DSTAT : %02x  CSTAT: %02x	CPA: %04x\n",
-+		   stat->dstat, stat->cstat, stat->cpa);
-+	PRINT_INFO("DEVICE: %04x\n", device->devinfo.devno);
-+	if (request != NULL)
-+		PRINT_INFO("OP	  : %s\n", tape_op_verbose[request->op]);
-+		
-+	sptr = (unsigned int *) stat->ii.sense.data;
-+	PRINT_INFO("Sense data: %08X %08X %08X %08X \n",
-+		   sptr[0], sptr[1], sptr[2], sptr[3]);
-+	PRINT_INFO("Sense data: %08X %08X %08X %08X \n",
-+		   sptr[4], sptr[5], sptr[6], sptr[7]);
-+	PRINT_INFO("--------------------------------------------------\n");
++	if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
++		return -ENOMEM;
++
++	memset(p, 0, sizeof(*p));
++	INIT_LIST_HEAD(&p->task.list);
++	p->task.routine = tape_34xx_work_handler;
++	p->task.data    = p;
++
++	p->device = tape_clone_device(device);
++	p->op     = op;
++
++	schedule_task(&p->task);
++
++	return 0;
 +}
 +
 +/*
-+ * Write sense data to dbf
++ * Done Handler is called when dev stat = DEVICE-END (successful operation)
 + */
-+void
-+tape_dump_sense_dbf(struct tape_device *device, struct tape_request *request)
++static int
++tape_34xx_done(struct tape_device *device, struct tape_request *request)
 +{
-+	devstat_t *stat = &device->devstat;
-+	unsigned int *sptr;
-+	const char* op;
++	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
++	// FIXME: Maybe only on assign/unassign
++	TAPE_CLEAR_STATE(device, TAPE_STATUS_BOXED);
 +
-+	if (request != NULL)
-+		op = tape_op_verbose[request->op];
-+	else
-+		op = "---";
-+	DBF_EVENT(3, "DSTAT : %02x   CSTAT: %02x\n", stat->dstat,stat->cstat);
-+	DBF_EVENT(3, "DEVICE: %04x OP\t: %s\n", device->devinfo.devno,op);
-+	sptr = (unsigned int *) stat->ii.sense.data;
-+	DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]);
-+	DBF_EVENT(3, "%08x %08x\n", sptr[2], sptr[3]);
-+	DBF_EVENT(3, "%08x %08x\n", sptr[4], sptr[5]);
-+	DBF_EVENT(3, "%08x %08x\n", sptr[6], sptr[7]);
++	return TAPE_IO_SUCCESS;
 +}
 +
 +static inline int
-+__tape_do_io(struct tape_device *device, struct tape_request *request)
++tape_34xx_erp_failed(struct tape_device *device,
++		     struct tape_request *request, int rc)
 +{
-+	if(TAPE_NOT_OPER(device))
-+		return -ENODEV;
-+
-+	/* Some operations may happen even on an unused tape device */
-+	switch(request->op) {
-+		case TO_MSEN:
-+		case TO_ASSIGN:
-+		case TO_UNASSIGN:
-+		case TO_BREAKASS:
-+			break;
-+		default:
-+			if (!TAPE_OPEN(device))
-+				return -ENODEV;
-+	}
-+
-+	/* Add reference to device to the request. This increases the reference
-+	   count. */
-+	request->device = tape_clone_device(device);
-+	request->status = TAPE_REQUEST_QUEUED;
++	DBF_EVENT(3, "Error recovery failed for %s\n",
++		  tape_op_verbose[request->op]);
++	return rc;
++}
 +
-+	list_add_tail(&request->list, &device->req_queue);
-+	__tape_process_queue(device);
++static inline int
++tape_34xx_erp_succeeded(struct tape_device *device,
++		       struct tape_request *request)
++{
++	DBF_EVENT(3, "Error Recovery successful for %s\n",
++		  tape_op_verbose[request->op]);
++	return tape_34xx_done(device, request);
++}
 +
-+	return 0;
++static inline int
++tape_34xx_erp_retry(struct tape_device *device, struct tape_request *request)
++{
++	DBF_EVENT(3, "xerp retr %s\n",
++		  tape_op_verbose[request->op]);
++	return TAPE_IO_RETRY;
 +}
 +
 +/*
-+ * Add the request to the request queue, try to start it if the
-+ * tape is idle. Return without waiting for end of i/o.
++ * This function is called, when no request is outstanding and we get an
++ * interrupt
 + */
-+int
-+tape_do_io_async(struct tape_device *device, struct tape_request *request)
++static int
++tape_34xx_unsolicited_irq(struct tape_device *device)
 +{
-+	int  rc;
-+	long flags;
-+
-+	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
-+	/* Add request to request queue and try to start it. */
-+	rc = __tape_do_io(device, request);
-+	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
-+	return rc;
++	if (device->devstat.dstat == 0x85 /* READY */) {
++		/* A medium was inserted in the drive. */
++		DBF_EVENT(6, "T34xx: tape load\n");
++		tape_34xx_schedule_work(device, TO_MSEN);
++	} else {
++		DBF_EVENT(3, "T34xx: unsol.irq! dev end: %x\n",
++			  device->devinfo.irq);
++		PRINT_WARN("Unsolicited IRQ (Device End) caught.\n");
++		tape_dump_sense(device, NULL);
++	}
++	return TAPE_IO_SUCCESS;
 +}
 +
 +/*
-+ * tape_do_io/__tape_wake_up
-+ * Add the request to the request queue, try to start it if the
-+ * tape is idle and wait uninterruptible for its completion.
++ * Read Opposite Error Recovery Function:
++ * Used, when Read Forward does not work
 + */
-+static void
-+__tape_wake_up(struct tape_request *request, void *data)
++static int
++tape_34xx_erp_read_opposite(struct tape_device *device,
++			    struct tape_request *request)
 +{
-+	request->callback = NULL;
-+	wake_up((wait_queue_head_t *) data);
++	if (request->op == TO_RFO) {
++		/*
++		 * We did read forward, but the data could not be read
++		 * *correctly*. We transform the request to a read backward
++		 * and try again.
++		 */
++		tape_std_read_backward(device, request);
++		return tape_34xx_erp_retry(device, request);
++	}
++	if (request->op != TO_RBA)
++		PRINT_ERR("read_opposite called with state:%s\n",
++			  tape_op_verbose[request->op]);
++	/*
++	 * We tried to read forward and backward, but hat no
++	 * success -> failed.
++	 */
++	return tape_34xx_erp_failed(device, request, -EIO);
 +}
 +
-+int
-+tape_do_io(struct tape_device *device, struct tape_request *request)
++static int
++tape_34xx_erp_bug(struct tape_device *device,
++		  struct tape_request *request, int no)
 +{
-+	wait_queue_head_t	 wq;
-+	long			 flags;
-+	int			 rc;
-+
-+	DBF_EVENT(5, "tape: tape_do_io(%p, %p)\n", device, request);
-+
-+	init_waitqueue_head(&wq);
-+	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
-+	/* Setup callback */
-+	request->callback = __tape_wake_up;
-+	request->callback_data = &wq;
-+	/* Add request to request queue and try to start it. */
-+	rc = __tape_do_io(device, request);
-+	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
-+	if (rc)
-+		return rc;
-+	/* Request added to the queue. Wait for its completion. */
-+	wait_event(wq, (request->callback == NULL));
-+	/* Get rc from request */
-+	return request->rc;
++	if (request->op != TO_ASSIGN) {
++		PRINT_WARN("An unexpected condition #%d was caught in "
++			   "tape error recovery.\n", no);
++		PRINT_WARN("Please report this incident.\n");
++		if (request)
++			PRINT_WARN("Operation of tape:%s\n",
++				   tape_op_verbose[request->op]);
++		tape_dump_sense(device, request);
++	}
++	return tape_34xx_erp_failed(device, request, -EIO);
 +}
 +
 +/*
-+ * tape_do_io_interruptible/__tape_wake_up_interruptible
-+ * Add the request to the request queue, try to start it if the
-+ * tape is idle and wait uninterruptible for its completion.
++ * Handle data overrun between cu and drive. The channel speed might
++ * be too slow.
 + */
-+static void
-+__tape_wake_up_interruptible(struct tape_request *request, void *data)
-+{
-+	request->callback = NULL;
-+	wake_up_interruptible((wait_queue_head_t *) data);
-+}
-+
-+int
-+tape_do_io_interruptible(struct tape_device *device,
-+			 struct tape_request *request)
++static int
++tape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request)
 +{
-+	wait_queue_head_t	 wq;
-+	long			 flags;
-+	int			 rc;
-+
-+	DBF_EVENT(5, "tape: tape_do_io_int(%p, %p)\n", device, request);
-+
-+	init_waitqueue_head(&wq);
-+	// debug paranoia
-+	if(!device) BUG();
-+	if(!request) BUG();
-+
-+	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
-+	/* Setup callback */
-+	request->callback = __tape_wake_up_interruptible;
-+	request->callback_data = &wq;
-+	rc = __tape_do_io(device, request);
-+	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
-+	if (rc)
-+		return rc;
-+	/* Request added to the queue. Wait for its completion. */
-+	rc = wait_event_interruptible(wq, (request->callback == NULL));
-+	if (rc != -ERESTARTSYS)
-+		/* Request finished normally. */
-+		return request->rc;
-+	/* Interrupted by a signal. We have to stop the current request. */
-+	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
-+	rc = __tape_halt_io(device, request);
-+	if (rc == 0) {
-+		DBF_EVENT(3, "IO stopped on irq %d\n", device->devinfo.irq);
-+		rc = -ERESTARTSYS;
++	if (device->devstat.ii.sense.data[3] == 0x40) {
++		PRINT_WARN ("Data overrun error between control-unit "
++			    "and drive. Use a faster channel connection, "
++			    "if possible! \n");
++		return tape_34xx_erp_failed(device, request, -EIO);
 +	}
-+	if(request->callback != NULL)
-+		request->callback = __tape_wake_up;
-+	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
-+	wait_event(wq, (request->callback == NULL));
-+
-+	return rc;
++	return tape_34xx_erp_bug(device, request, -1);
 +}
-+
-+
++	
 +/*
-+ * Tape interrupt routine, called from Ingo's I/O layer
++ * Handle record sequence error.
 + */
-+static void
-+__tape_do_irq (int irq, void *ds, struct pt_regs *regs)
++static int
++tape_34xx_erp_sequence(struct tape_device *device,
++		       struct tape_request *request)
 +{
-+	struct tape_device *device;
-+	struct tape_request *request;
-+	devstat_t *devstat;
-+	int final;
-+	int rc;
-+
-+	devstat = (devstat_t *) ds;
-+	device = (struct tape_device *) s390_get_private_data(irq);
-+	if (device == NULL) {
-+		PRINT_ERR("could not get device structure for irq %d "
-+			  "in interrupt\n", irq);
-+		return;
++	if (device->devstat.ii.sense.data[3] == 0x41) {
++		/*
++		 * cu detected incorrect block-id sequence on tape.
++		 */
++		PRINT_WARN("Illegal block-id sequence found!\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
 +	}
-+	request = (struct tape_request *) devstat->intparm;
++	/*
++	 * Record sequence error bit is set, but erpa does not
++	 * show record sequence error.
++	 */
++	return tape_34xx_erp_bug(device, request, -2);
++}
 +
-+	DBF_EVENT(5, "tape: __tape_do_irq(%p, %p)\n", device, request);
++/*
++ * This function analyses the tape's sense-data in case of a unit-check.
++ * If possible, it tries to recover from the error. Else the user is
++ * informed about the problem.
++ */
++static int
++tape_34xx_unit_check(struct tape_device *device, struct tape_request *request)
++{
++	int inhibit_cu_recovery;
++	__u8* sense;
 +
-+	if(request != NULL) {
-+		if(request->status == TAPE_REQUEST_DONE) {
-+			DBF_EVENT(3, "tape: IO stopped successfully\n");
-+			__tape_remove_request(device, request);
++	inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0;
++	sense = device->devstat.ii.sense.data;
 +
-+			/* Start next request. */
-+			if (!list_empty(&device->req_queue))
-+				tape_schedule_bh(device);
-+			return;
-+		}
++#ifdef CONFIG_S390_TAPE_BLOCK
++	if (request->op == TO_BLOCK) {
++		/*
++		 * Recovery for block device requests. Set the block_position
++		 * to something invalid and retry.
++		 */
++		device->blk_data.block_position = -1;
++		if (request->retries-- <= 0)
++			return tape_34xx_erp_failed(device, request, -EIO);
++		else
++			return tape_34xx_erp_retry(device, request);
++	}
++#endif
 +
-+		/* Interrupt on a canceled request */
-+		if(request != tape_get_next_request(device)) {
-+			DBF_EVENT(3, "tape: late interrupt ingored\n");
-+			return;
++	if (
++		sense[0] & SENSE_COMMAND_REJECT &&
++		sense[1] & SENSE_WRITE_PROTECT
++	) {
++		if (
++			request->op == TO_DSE ||
++			request->op == TO_WRI ||
++			request->op == TO_WTM
++		) {
++			/* medium is write protected */
++			return tape_34xx_erp_failed(device, request, -EACCES);
++		} else {
++			return tape_34xx_erp_bug(device, request, -3);
 +		}
++	}
 +
-+		if(request->timeout.expires) {
-+			/*
-+			 * If the timer was not yet startet the reference to
-+			 * the request has to be dropped here. Otherwise it
-+			 * will be dropped by the timeout handler.
-+			 */
-+			if(del_timer(&request->timeout) > 0)
-+				request->timeout.data = (unsigned long)
-+					tape_put_request(request);
++	/*
++	 * special cases for various tape-states when reaching
++	 * end of recorded area
++	 */
++	/*
++	 * FIXME: Maybe a special case of the special case:
++	 *        sense[0] == SENSE_EQUIPMENT_CHECK &&
++	 *        sense[1] == SENSE_DRIVE_ONLINE    &&
++	 *        sense[3] == 0x47 (Volume Fenced)
++	 *
++	 *        This was caused by continued FSF or FSR after an
++	 *        'End Of Data'.
++	 */
++	if ((
++		sense[0] == SENSE_DATA_CHECK      ||
++		sense[0] == SENSE_EQUIPMENT_CHECK ||
++		sense[0] == SENSE_EQUIPMENT_CHECK + SENSE_DEFERRED_UNIT_CHECK
++	) && (
++		sense[1] == SENSE_DRIVE_ONLINE ||
++		sense[1] == SENSE_BEGINNING_OF_TAPE + SENSE_WRITE_MODE
++	)) {
++		switch (request->op) {
++		/*
++		 * sense[0] == SENSE_DATA_CHECK   &&
++		 * sense[1] == SENSE_DRIVE_ONLINE
++		 * sense[3] == 0x36 (End Of Data)
++		 *
++		 * Further seeks might return a 'Volume Fenced'.
++		 */
++		case TO_FSF:
++		case TO_FSB:
++			/* Trying to seek beyond end of recorded area */
++			return tape_34xx_erp_failed(device, request, -ENOSPC);
++		case TO_BSB:
++			return tape_34xx_erp_retry(device, request);
++		/*
++		 * sense[0] == SENSE_DATA_CHECK   &&
++		 * sense[1] == SENSE_DRIVE_ONLINE &&
++		 * sense[3] == 0x36 (End Of Data)
++		 */
++		case TO_LBL:
++			/* Block could not be located. */
++			return tape_34xx_erp_failed(device, request, -EIO);
++		case TO_RFO:
++			/* Read beyond end of recorded area -> 0 bytes read */
++			return tape_34xx_erp_failed(device, request, 0);
++		default:
++			PRINT_ERR("Invalid op %s in %s:%i\n",
++				tape_op_verbose[request->op],
++				__FUNCTION__, __LINE__);
++			return tape_34xx_erp_failed(device, request, 0);
 +		}
 +	}
 +
-+	if (device->devstat.cstat & SCHN_STAT_INCORR_LEN)
-+		DBF_EVENT(4, "tape: incorrect blocksize\n");
++	/* Sensing special bits */
++	if (sense[0] & SENSE_BUS_OUT_CHECK)
++		return tape_34xx_erp_retry(device, request);
 +
-+	if (device->devstat.dstat != 0x0c){
++	if (sense[0] & SENSE_DATA_CHECK) {
 +		/*
-+		 * Any request that does not come back with channel end
-+		 * and device end is unusual. Log the sense data.
++		 * hardware failure, damaged tape or improper
++		 * operating conditions
 +		 */
-+		DBF_EVENT(3,"-- Tape Interrupthandler --\n");
-+		tape_dump_sense_dbf(device, request);
-+	}
-+	if (TAPE_NOT_OPER(device)) {
-+		DBF_EVENT(6, "tape:device is not operational\n");
-+		return;
-+	}
++		switch (sense[3]) {
++		case 0x23:
++			/* a read data check occurred */
++			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
++			    inhibit_cu_recovery)
++				// data check is not permanent, may be
++				// recovered. We always use async-mode with
++				// cu-recovery, so this should *never* happen.
++				return tape_34xx_erp_bug(device, request, -4);
 +
-+	/* Some status handling */
-+	if(devstat && devstat->dstat & DEV_STAT_UNIT_CHECK) {
-+		unsigned char *sense = devstat->ii.sense.data;
++			/* data check is permanent, CU recovery has failed */
++			PRINT_WARN("Permanent read error\n");
++			return tape_34xx_erp_failed(device, request, -EIO);
++		case 0x25:
++			// a write data check occurred
++			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
++			    inhibit_cu_recovery)
++				// data check is not permanent, may be
++				// recovered. We always use async-mode with
++				// cu-recovery, so this should *never* happen.
++				return tape_34xx_erp_bug(device, request, -5);
 +
-+		if(!(sense[1] & SENSE_DRIVE_ONLINE))
-+			device->tape_generic_status &= ~GMT_ONLINE(~0);
-+	} else {
-+		device->tape_generic_status |= GMT_ONLINE(~0);
++			// data check is permanent, cu-recovery has failed
++			PRINT_WARN("Permanent write error\n");
++			return tape_34xx_erp_failed(device, request, -EIO);
++		case 0x26:
++			/* Data Check (read opposite) occurred. */
++			return tape_34xx_erp_read_opposite(device, request);
++		case 0x28:
++			/* ID-Mark at tape start couldn't be written */
++			PRINT_WARN("ID-Mark could not be written.\n");
++			return tape_34xx_erp_failed(device, request, -EIO);
++		case 0x31:
++			/* Tape void. Tried to read beyond end of device. */
++			PRINT_WARN("Read beyond end of recorded area.\n");
++			return tape_34xx_erp_failed(device, request, -ENOSPC);
++		case 0x41:
++			/* Record sequence error. */
++			PRINT_WARN("Invalid block-id sequence found.\n");
++			return tape_34xx_erp_failed(device, request, -EIO);
++		default:
++			/* all data checks for 3480 should result in one of
++			 * the above erpa-codes. For 3490, other data-check
++			 * conditions do exist. */
++			if (device->discipline->cu_type == 0x3480)
++				return tape_34xx_erp_bug(device, request, -6);
++		}
 +	}
 +
-+	rc = device->discipline->irq(device, request);
-+	/*
-+	 * rc < 0 : request finished unsuccessfully.
-+	 * rc == TAPE_IO_SUCCESS: request finished successfully.
-+	 * rc == TAPE_IO_PENDING: request is still running. Ignore rc.
-+	 * rc == TAPE_IO_RETRY: request finished but needs another go.
-+	 * rc == TAPE_IO_STOP: request needs to get terminated. 
-+	 */
-+	final = 0;
-+	switch (rc) {
-+		case TAPE_IO_SUCCESS:
-+			final = 1;
-+			break;
-+		case TAPE_IO_PENDING:
-+			break;
-+		case TAPE_IO_RETRY:
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+			if (request->op == TO_BLOCK)
-+				device->discipline->check_locate(device, request);
-+#endif
-+			rc = __do_IO(device, request);
-+			if (rc) {
-+				DBF_EVENT(1, "tape: DOIO failed with er = %i\n", rc);
-+				final = 1;
-+			}
-+			break;
-+		case TAPE_IO_STOP:
-+			__tape_halt_io(device, request);
-+			rc = -EIO;
-+			break;
-+		default:
-+			if (rc > 0) {
-+				DBF_EVENT(6, "xunknownrc\n");
-+				PRINT_ERR("Invalid return code from discipline "
-+				  "interrupt function.\n");
-+				rc = -EIO;
++	if (sense[0] & SENSE_OVERRUN)
++		return tape_34xx_erp_overrun(device, request);
++	
++	if (sense[1] & SENSE_RECORD_SEQUENCE_ERR)
++		return tape_34xx_erp_sequence(device, request);
++
++	/* Sensing erpa codes */
++	switch (sense[3]) {
++	case 0x00:
++		/* Unit check with erpa code 0. Report and ignore. */
++		PRINT_WARN("Non-error sense was found. "
++			   "Unit-check will be ignored.\n");
++		return TAPE_IO_SUCCESS;
++	case 0x21:
++		/*
++		 * Data streaming not operational. CU will switch to
++		 * interlock mode. Reissue the command.
++		 */
++		PRINT_WARN("Data streaming not operational. "
++			   "Switching to interlock-mode.\n");
++		return tape_34xx_erp_retry(device, request);
++	case 0x22:
++		/*
++		 * Path equipment check. Might be drive adapter error, buffer
++		 * error on the lower interface, internal path not usable,
++		 * or error during cartridge load.
++		 */
++		PRINT_WARN("A path equipment check occurred. One of the "
++			   "following conditions occurred:\n");
++		PRINT_WARN("drive adapter error, buffer error on the lower "
++			   "interface, internal path not usable, error "
++			   "during cartridge load.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x24:
++		/*
++		 * Load display check. Load display was command was issued,
++		 * but the drive is displaying a drive check message. Can
++		 * be threated as "device end".
++		 */
++		return tape_34xx_erp_succeeded(device, request);
++	case 0x27:
++		/*
++		 * Command reject. May indicate illegal channel program or
++		 * buffer over/underrun. Since all channel programs are
++		 * issued by this driver and ought be correct, we assume a
++		 * over/underrun situation and retry the channel program.
++		 */
++		return tape_34xx_erp_retry(device, request);
++	case 0x29:
++		/*
++		 * Function incompatible. Either the tape is idrc compressed
++		 * but the hardware isn't capable to do idrc, or a perform
++		 * subsystem func is issued and the CU is not on-line.
++		 */
++		PRINT_WARN ("Function incompatible. Try to switch off idrc\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x2a:
++		/*
++		 * Unsolicited environmental data. An internal counter
++		 * overflows, we can ignore this and reissue the cmd.
++		 */
++		return tape_34xx_erp_retry(device, request);
++	case 0x2b:
++		/*
++		 * Environmental data present. Indicates either unload
++		 * completed ok or read buffered log command completed ok.
++		 */
++		if (request->op == TO_RUN) {
++			tape_med_state_set(device, MS_UNLOADED);
++			/* Rewind unload completed ok. */
++			return tape_34xx_erp_succeeded(device, request);
++		}
++		/* tape_34xx doesn't use read buffered log commands. */
++		return tape_34xx_erp_bug(device, request, sense[3]);
++	case 0x2c:
++		/*
++		 * Permanent equipment check. CU has tried recovery, but
++		 * did not succeed.
++		 */
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x2d:
++		/* Data security erase failure. */
++		if (request->op == TO_DSE)
++			return tape_34xx_erp_failed(device, request, -EIO);
++		/* Data security erase failure, but no such command issued. */
++		return tape_34xx_erp_bug(device, request, sense[3]);
++	case 0x2e:
++		/*
++		 * Not capable. This indicates either that the drive fails
++		 * reading the format id mark or that that format specified
++		 * is not supported by the drive.
++		 */
++		PRINT_WARN("Drive not capable processing the tape format!");
++		return tape_34xx_erp_failed(device, request, -EMEDIUMTYPE);
++	case 0x30:
++		/* The medium is write protected. */
++		PRINT_WARN("Medium is write protected!\n");
++		return tape_34xx_erp_failed(device, request, -EACCES);
++	case 0x32:
++		// Tension loss. We cannot recover this, it's an I/O error.
++		PRINT_WARN("The drive lost tape tension.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x33:
++		/*
++		 * Load Failure. The cartridge was not inserted correctly or
++		 * the tape is not threaded correctly.
++		 */
++		PRINT_WARN("Cartridge load failure. Reload the cartridge "
++			   "and try again.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x34:
++		/*
++		 * Unload failure. The drive cannot maintain tape tension
++		 * and control tape movement during an unload operation.
++		 */
++		PRINT_WARN("Failure during cartridge unload. "
++			   "Please try manually.\n");
++		if (request->op == TO_RUN)
++			return tape_34xx_erp_failed(device, request, -EIO);
++		return tape_34xx_erp_bug(device, request, sense[3]);
++	case 0x35:
++		/*
++		 * Drive equipment check. One of the following:
++		 * - cu cannot recover from a drive detected error
++		 * - a check code message is shown on drive display
++		 * - the cartridge loader does not respond correctly
++		 * - a failure occurs during an index, load, or unload cycle
++		 */
++		PRINT_WARN("Equipment check! Please check the drive and "
++			   "the cartridge loader.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x36:
++		if (device->discipline->cu_type == 0x3490)
++			/* End of data. */
++			return tape_34xx_erp_failed(device, request, -EIO);
++		/* This erpa is reserved for 3480 */
++		return tape_34xx_erp_bug(device,request,sense[3]);
++	case 0x37:
++		/*
++		 * Tape length error. The tape is shorter than reported in
++		 * the beginning-of-tape data.
++		 */
++		PRINT_WARN("Tape length error.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x38:
++		/*
++		 * Physical end of tape. A read/write operation reached
++		 * the physical end of tape.
++		 */
++		if (request->op==TO_WRI ||
++		    request->op==TO_DSE ||
++		    request->op==TO_WTM)
++			return tape_34xx_erp_failed(device, request, -ENOSPC);
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x39:
++		/* Backward at Beginning of tape. */
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x3a:
++		/* Drive switched to not ready. */
++		PRINT_WARN("Drive not ready. Turn the ready/not ready switch "
++			   "to ready position and try again.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x3b:
++		/* Manual rewind or unload. This causes an I/O error. */
++		PRINT_WARN("Medium was rewound or unloaded manually.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x42:
++		/*
++		 * Degraded mode. A condition that can cause degraded
++		 * performance is detected.
++		 */
++		PRINT_WARN("Subsystem is running in degraded mode.\n");
++		return tape_34xx_erp_retry(device, request);
++	case 0x43:
++		/* Drive not ready. */
++		tape_med_state_set(device, MS_UNLOADED);
++		/* SMB: some commands do not need a tape inserted */
++		if((sense[1] & SENSE_DRIVE_ONLINE)) {
++			switch(request->op) {
++				case TO_ASSIGN:
++				case TO_UNASSIGN:
++				case TO_DIS:
++				case TO_NOP:
++					return tape_34xx_done(device, request);
++					break;
++				default:
++					break;
 +			}
-+			final = 1;
-+			break;
-+	}
-+	if (final) {
-+		/* This might be an unsolicited interrupt (no request) */
-+		if(request != NULL) {
-+			/* Set ending status. */
-+			request->rc = rc;
-+			__tape_remove_request(device, request);
 +		}
-+		/* Start next request. */
-+		if (!list_empty(&device->req_queue))
-+			tape_schedule_bh(device);
-+	}
-+}
-+
-+/*
-+ * Lock a shared tape for our exclusive use.
-+ */
-+int
-+tape_assign(struct tape_device *device, int type)
-+{
-+	int rc;
-+
-+	spin_lock_irq(&device->assign_lock);
-+
-+	/* The device is already assigned */
-+	rc = 0;
-+	if (!TAPE_ASSIGNED(device)) {
-+		rc = device->discipline->assign(device);
-+
-+		spin_lock(get_irq_lock(device->devinfo.irq));
-+		if (rc) {
-+	        	PRINT_WARN(
-+				"(%04x): assign failed - "
-+				"device might be busy\n",
-+				device->devstat.devno);
-+	        	DBF_EVENT(3,
-+				"(%04x): assign failed "
-+				"- device might be busy\n",
-+				device->devstat.devno);
-+			TAPE_SET_STATE(device, TAPE_STATUS_BOXED);
++		PRINT_WARN("The drive is not ready.\n");
++		return tape_34xx_erp_failed(device, request, -ENOMEDIUM);
++	case 0x44:
++		/* Locate Block unsuccessful. */
++		if (request->op != TO_BLOCK && request->op != TO_LBL)
++			/* No locate block was issued. */
++			return tape_34xx_erp_bug(device, request, sense[3]);
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x45:
++		/* The drive is assigned to a different channel path. */
++		PRINT_WARN("The drive is assigned elsewhere.\n");
++		TAPE_SET_STATE(device, TAPE_STATUS_BOXED);
++		return tape_34xx_erp_failed(device, request, -EPERM);
++	case 0x46:
++		/*
++		 * Drive not on-line. Drive may be switched offline,
++		 * the power supply may be switched off or
++		 * the drive address may not be set correctly.
++		 */
++		PRINT_WARN("The drive is not on-line.");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x47:
++		/* Volume fenced. CU reports volume integrity is lost. */
++		PRINT_WARN("Volume fenced. The volume integrity is lost because\n");
++		PRINT_WARN("assignment or tape position was lost.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x48:
++		/* Log sense data and retry request. */
++		return tape_34xx_erp_retry(device, request);
++	case 0x49:
++		/* Bus out check. A parity check error on the bus was found. */
++		PRINT_WARN("Bus out check. A data transfer over the bus "
++			   "has been corrupted.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x4a:
++		/* Control unit erp failed. */
++		PRINT_WARN("The control unit I/O error recovery failed.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x4b:
++		/*
++		 * CU and drive incompatible. The drive requests micro-program
++		 * patches, which are not available on the CU.
++		 */
++		PRINT_WARN("The drive needs microprogram patches from the "
++			   "control unit, which are not available.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x4c:
++		/*
++		 * Recovered Check-One failure. Cu develops a hardware error,
++		 * but is able to recover.
++		 */
++		return tape_34xx_erp_retry(device, request);
++	case 0x4d:
++		if (device->discipline->cu_type == 0x3490)
++			/*
++			 * Resetting event received. Since the driver does
++			 * not support resetting event recovery (which has to
++			 * be handled by the I/O Layer), retry our command.
++			 */
++			return tape_34xx_erp_retry(device, request);
++		/* This erpa is reserved for 3480. */
++		return tape_34xx_erp_bug(device, request, sense[3]);
++	case 0x4e:
++		if (device->discipline->cu_type == 0x3490) {
++			/*
++			 * Maximum block size exceeded. This indicates, that
++			 * the block to be written is larger than allowed for
++			 * buffered mode.
++			 */
++			PRINT_WARN("Maximum block size for buffered "
++				   "mode exceeded.\n");
++			return tape_34xx_erp_failed(device, request, -ENOBUFS);
++		}
++		/* This erpa is reserved for 3480. */
++		return tape_34xx_erp_bug(device, request, sense[3]);
++	case 0x50:
++		/*
++		 * Read buffered log (Overflow). CU is running in extended
++		 * buffered log mode, and a counter overflows. This should
++		 * never happen, since we're never running in extended
++		 * buffered log mode.
++		 */
++		return tape_34xx_erp_retry(device, request);
++	case 0x51:
++		/*
++		 * Read buffered log (EOV). EOF processing occurs while the
++		 * CU is in extended buffered log mode. This should never
++		 * happen, since we're never running in extended buffered
++		 * log mode.
++		 */
++		return tape_34xx_erp_retry(device, request);
++	case 0x52:
++		/* End of Volume complete. Rewind unload completed ok. */
++		if (request->op == TO_RUN) {
++			/* SMB */
++			tape_med_state_set(device, MS_UNLOADED);
++			return tape_34xx_erp_succeeded(device, request);
++		}
++		return tape_34xx_erp_bug(device, request, sense[3]);
++	case 0x53:
++		/* Global command intercept. */
++		return tape_34xx_erp_retry(device, request);
++	case 0x54:
++		/* Channel interface recovery (temporary). */
++		return tape_34xx_erp_retry(device, request);
++	case 0x55:
++		/* Channel interface recovery (permanent). */
++		PRINT_WARN("A permanent channel interface error occurred.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x56:
++		/* Channel protocol error. */
++		PRINT_WARN("A channel protocol error occurred.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x57:
++		if (device->discipline->cu_type == 0x3480) {
++			/* Attention intercept. */
++			PRINT_WARN("An attention intercept occurred, "
++				   "which will be recovered.\n");
++			return tape_34xx_erp_retry(device, request);
 +		} else {
-+			DBF_EVENT(3, "(%04x): assign lpum = %02x\n",
-+				device->devstat.devno, device->devstat.lpum);
-+			tape_state_set(
-+				device,
-+				(device->tape_status | type) &
-+				(~TAPE_STATUS_BOXED)
-+			);
++			/* Global status intercept. */
++			PRINT_WARN("An global status intercept was received, "
++				   "which will be recovered.\n");
++			return tape_34xx_erp_retry(device, request);
 +		}
-+	} else {
-+		spin_lock(get_irq_lock(device->devinfo.irq));
-+		TAPE_SET_STATE(device, type);
-+	}
-+	spin_unlock(get_irq_lock(device->devinfo.irq));
-+	spin_unlock_irq(&device->assign_lock);
-+
-+	return rc;
-+}
-+
-+/*
-+ * Unlock a shared tape.
-+ */
-+int
-+tape_unassign(struct tape_device *device, int type)
-+{
-+	int	 	rc;
-+
-+	spin_lock_irq(&device->assign_lock);
-+
-+	rc = 0;
-+	spin_lock(get_irq_lock(device->devinfo.irq));
-+	if (!TAPE_ASSIGNED(device)) {
-+		spin_unlock(get_irq_lock(device->devinfo.irq));
-+		spin_unlock_irq(&device->assign_lock);
-+		return 0;
-+	}
-+	TAPE_CLEAR_STATE(device, type);
-+	spin_unlock(get_irq_lock(device->devinfo.irq));
++	case 0x5a:
++		/*
++		 * Tape length incompatible. The tape inserted is too long,
++		 * which could cause damage to the tape or the drive.
++		 */
++		PRINT_WARN("Tape length incompatible [should be IBM Cartridge "
++			   "System Tape]. May cause damage to drive or tape.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x5b:
++		/* Format 3480 XF incompatible */
++		if (sense[1] & SENSE_BEGINNING_OF_TAPE)
++			/* The tape will get overwritten. */
++			return tape_34xx_erp_retry(device, request);
++		PRINT_WARN("Tape format is incompatible to the drive, "
++			   "which writes 3480-2 XF.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x5c:
++		/* Format 3480-2 XF incompatible */
++		PRINT_WARN("Tape format is incompatible to the drive. "
++			   "The drive cannot access 3480-2 XF volumes.\n");
++		return tape_34xx_erp_failed(device, request, -EIO);
++	case 0x5d:
++		/* Tape length violation. */
++		PRINT_WARN("Tape length violation [should be IBM Enhanced "
++			   "Capacity Cartridge System Tape]. May cause "
++			   "damage to drive or tape.\n");
++		return tape_34xx_erp_failed(device, request, -EMEDIUMTYPE);
++	case 0x5e:
++		/* Compaction algorithm incompatible. */
++		PRINT_WARN("The volume is recorded using an incompatible "
++			   "compaction algorithm, which is not supported by "
++			   "the control unit.\n");
++		return tape_34xx_erp_failed(device, request, -EMEDIUMTYPE);
 +
-+	if (!TAPE_ASSIGNED(device)) {
-+		rc = device->discipline->unassign(device);
-+		if (rc) {
-+			PRINT_WARN("(%04x): unassign failed\n",
-+				device->devstat.devno);
-+			DBF_EVENT(3, "(%04x): unassign failed\n",
-+				device->devstat.devno);
-+		} else {
-+			DBF_EVENT(3, "(%04x): unassign lpum = %02x\n",
-+				device->devstat.devno, device->devstat.lpum);
-+		}
++		/* The following erpas should have been covered earlier. */
++	case 0x23: /* Read data check. */
++	case 0x25: /* Write data check. */
++	case 0x26: /* Data check (read opposite). */
++	case 0x28: /* Write id mark check. */
++	case 0x31: /* Tape void. */
++	case 0x40: /* Overrun error. */
++	case 0x41: /* Record sequence error. */
++		/* All other erpas are reserved for future use. */
++	default:
++		return tape_34xx_erp_bug(device, request, sense[3]);
 +	}
-+
-+	spin_unlock_irq(&device->assign_lock);
-+	return rc;
 +}
 +
 +/*
-+ * Tape device open function used by tape_char & tape_block frontends.
++ * 3480/3490 interrupt handler
 + */
-+int
-+tape_open(struct tape_device *device)
++static int
++tape_34xx_irq(struct tape_device *device, struct tape_request *request)
 +{
-+	int rc;
-+
-+	if(TAPE_INIT(device) && TAPE_BOXED(device)) {
-+		rc = tape_request_irq(device);
-+		if (rc) {
-+			if(rc == -EUSERS)
-+				return -EPERM;
-+			return rc;
-+		} else {
-+			TAPE_CLEAR_STATE(device, TAPE_STATUS_INIT);
-+		}
-+	}
++	if (request == NULL)
++		return tape_34xx_unsolicited_irq(device);
 +
-+	spin_lock_irq(&tape_discipline_lock);
-+	spin_lock(get_irq_lock(device->devinfo.irq));
-+	if (TAPE_NOT_OPER(device)) {
-+		DBF_EVENT(6, "TAPE:nodev\n");
-+		rc = -ENODEV;
-+	} else if (TAPE_OPEN(device)) {
-+		DBF_EVENT(6, "TAPE:dbusy\n");
-+		rc = -EBUSY;
-+	} else if (device->discipline != NULL &&
-+		   !try_inc_mod_count(device->discipline->owner)) {
-+		DBF_EVENT(6, "TAPE:nodisc\n");
-+		rc = -ENODEV;
-+	} else {
-+		TAPE_SET_STATE(device, TAPE_STATUS_OPEN);
-+		rc = 0;
++	if ((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) &&
++	    (device->devstat.dstat & DEV_STAT_DEV_END) &&
++	    (request->op == TO_WRI)) {
++		/* Write at end of volume */
++		PRINT_INFO("End of volume\n"); /* XXX */
++		return tape_34xx_erp_failed(device, request, -ENOSPC);
 +	}
-+	spin_unlock(get_irq_lock(device->devinfo.irq));
-+	spin_unlock_irq(&tape_discipline_lock);
-+	return rc;
-+}
 +
-+/*
-+ * Tape device release function used by tape_char & tape_block frontends.
-+ */
-+int
-+tape_release(struct tape_device *device)
-+{
-+	spin_lock_irq(&tape_discipline_lock);
-+	spin_lock(get_irq_lock(device->devinfo.irq));
++	if ((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) &&
++	    (request->op == TO_BSB || request->op == TO_FSB))
++		DBF_EVENT(5, "Skipped over tapemark\n");
 +
-+	if (TAPE_OPEN(device)) {
-+		TAPE_CLEAR_STATE(device, TAPE_STATUS_OPEN);
++	if (device->devstat.dstat & DEV_STAT_UNIT_CHECK)
++		return tape_34xx_unit_check(device, request);
 +
-+		if (device->discipline->owner)
-+			__MOD_DEC_USE_COUNT(device->discipline->owner);	
-+	}
-+	spin_unlock(get_irq_lock(device->devinfo.irq));
-+	spin_unlock_irq(&tape_discipline_lock);
++	if (device->devstat.dstat & DEV_STAT_DEV_END)
++		return tape_34xx_done(device, request);
 +
-+	return 0;
++	DBF_EVENT(6, "xunknownirq\n");
++	PRINT_ERR("Unexpected interrupt.\n");
++	PRINT_ERR("Current op is: %s", tape_op_verbose[request->op]);
++	tape_dump_sense(device, request);
++	return TAPE_IO_STOP;
 +}
 +
 +/*
-+ * Execute a magnetic tape command a number of times.
++ * ioctl_overload
 + */
-+int
-+tape_mtop(struct tape_device *device, int mt_op, int mt_count)
++static int
++tape_34xx_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)
 +{
-+	tape_mtop_fn fn;
-+	int rc;
-+
-+	DBF_EVENT(6, "TAPE:mtio\n");
-+	DBF_EVENT(6, "TAPE:ioop: %x\n", mt_op);
-+	DBF_EVENT(6, "TAPE:arg:  %x\n", mt_count);
++        if (cmd == TAPE390_DISPLAY) {
++		struct display_struct disp;
 +
-+	if (mt_op < 0 || mt_op >= TAPE_NR_MTOPS)
-+		return -EINVAL;
-+	fn = device->discipline->mtop_array[mt_op];
-+	if(fn == NULL)
-+		return -EINVAL;
++		if(copy_from_user(&disp, (char *) arg, sizeof(disp)) != 0)
++			return -EFAULT;
 +
-+	/* We assume that the backends can handle count up to 500. */
-+	if (mt_op == MTBSR  || mt_op == MTFSR  || mt_op == MTFSF  ||
-+	    mt_op == MTBSF  || mt_op == MTFSFM || mt_op == MTBSFM) {
-+		rc = 0;
-+		for (; mt_count > 500; mt_count -= 500)
-+			if ((rc = fn(device, 500)) != 0)
-+				break;
-+		if (rc == 0)
-+			rc = fn(device, mt_count);
++	        return tape_std_display(device, &disp);
 +	} else
-+		rc = fn(device, mt_count);
-+	return rc;
-+
++                return -EINVAL;
 +}
 +
-+void
-+tape_init_disciplines(void)
++static int
++tape_34xx_setup_device(struct tape_device * device)
 +{
-+#ifdef	CONFIG_S390_TAPE_34XX
-+	tape_34xx_init();
-+#endif
-+#ifdef CONFIG_S390_TAPE_34XX_MODULE
-+	request_module("tape_34xx");
-+#endif
++	struct tape_34xx_discdata *discdata;
 +
-+#ifdef CONFIG_S390_TAPE_3590
-+	tape_3590_init();
-+#else
-+	request_module("tape_3590");
-+#endif
-+	tape_auto_detect();
-+}
++	DBF_EVENT(5, "tape_34xx_setup_device(%p)\n", device);
++	DBF_EVENT(6, "34xx minor1: %x\n", device->first_minor);
++	discdata = kmalloc(sizeof(struct tape_34xx_discdata), GFP_ATOMIC);
++	if(discdata) {
++		memset(discdata, 0, sizeof(struct tape_34xx_discdata));
++		INIT_LIST_HEAD(&discdata->sbid_list);
++		device->discdata = discdata;
++	}
 +
-+/*
-+ * Tape init function.
-+ */
-+static int
-+tape_init (void)
-+{
-+	TAPE_DBF_AREA = debug_register ( "tape", 1, 2, 4*sizeof(long));
-+	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
-+	DBF_EVENT(3, "tape init: ($Revision: 1.7.4.8 $)\n");
-+#ifdef CONFIG_DEVFS_FS
-+	tape_devfs_root_entry = devfs_mk_dir (NULL, "tape", NULL);
-+#endif /* CONFIG_DEVFS_FS */
-+	DBF_EVENT(3, "dev detect\n");
-+	/* Parse the parameters. */
-+	tape_devmap_init();
-+#ifdef CONFIG_PROC_FS
-+	tape_proc_init();
-+#endif /* CONFIG_PROC_FS */
-+	tapechar_init();
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+	tapeblock_init();
-+#endif
-+	tape_init_disciplines();
++	if(!TAPE_BOXED(device))
++		tape_34xx_medium_sense(device);
 +	return 0;
 +}
 +
-+/*
-+ * Tape exit function.
-+ */
-+void
-+tape_exit(void)
++static void
++tape_34xx_cleanup_device(struct tape_device * device)
 +{
-+	struct list_head *l, *n;
-+	struct tape_discipline *discipline;
-+
-+	DBF_EVENT(6, "tape exit\n");
-+
-+	/* Cleanup registered disciplines. */
-+	spin_lock(&tape_discipline_lock);
-+	list_for_each_safe(l, n, &tape_disciplines) {
-+		discipline = list_entry(l, struct tape_discipline, list);
-+	       __tape_unregister_discipline(discipline);
++	if (device->discdata) {
++		tape_34xx_clear_sbid_list(device);
++		kfree(device->discdata);
++		device->discdata = NULL;
 +	}
-+	spin_unlock(&tape_discipline_lock);
-+
-+	/* Get rid of the frontends */
-+	tapechar_exit();
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+	tapeblock_exit();
-+#endif
-+#ifdef CONFIG_PROC_FS
-+	tape_proc_cleanup();
-+#endif
-+	tape_devmap_exit();
-+#ifdef CONFIG_DEVFS_FS
-+	devfs_unregister (tape_devfs_root_entry); /* devfs checks for NULL */
-+#endif /* CONFIG_DEVFS_FS */
-+	debug_unregister (TAPE_DBF_AREA);
 +}
 +
 +/*
-+ * Issue an hotplug event
++ * Build up the lookup table...
 + */
-+void tape_hotplug_event(struct tape_device *device, int devmaj, int action) {
-+#ifdef CONFIG_HOTPLUG
-+	char *argv[3];
-+	char *envp[8];
-+	char  devno[20];
-+	char  major[20];
-+	char  minor[20];
++static void
++tape_34xx_add_sbid(struct tape_device *device, struct tape_34xx_block_id bid)
++{
++	struct tape_34xx_discdata *	discdata = device->discdata;
++	struct sbid_entry *		new;
++	struct sbid_entry *		cur;
++	struct list_head *		l;
 +
-+	sprintf(devno, "DEVNO=%04x", device->devinfo.devno);
-+	sprintf(major, "MAJOR=%d",   devmaj);
-+	sprintf(minor, "MINOR=%d",   device->first_minor);
++	if(discdata == NULL)
++		return;
++	if((new = kmalloc(sizeof(struct sbid_entry), GFP_ATOMIC)) == NULL)
++		return;
 +
-+	argv[0] = hotplug_path;
-+	argv[1] = "tape";
-+	argv[2] = NULL;
++	new->bid = bid;
++	new->bid.tbi_format = 0;
 +
-+	envp[0] = "HOME=/";
-+	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++	/*
++	 * Search the position where to insert the new entry. It is possible
++	 * that the entry should not be added but the block number has to be
++	 * updated to approximate the logical block, where a segment starts.
++	 */
++	list_for_each(l, &discdata->sbid_list) {
++		cur = list_entry(l, struct sbid_entry, list);
 +
-+	switch(action) {
-+		case TAPE_HOTPLUG_CHAR_ADD:
-+		case TAPE_HOTPLUG_BLOCK_ADD:
-+			envp[2] = "ACTION=add";
-+			break;
-+		case TAPE_HOTPLUG_CHAR_REMOVE:
-+		case TAPE_HOTPLUG_BLOCK_REMOVE:
-+			envp[2] = "ACTION=remove";
-+			break;
-+		default:
-+			BUG();
-+	}
-+	switch(action) {
-+		case TAPE_HOTPLUG_CHAR_ADD:
-+		case TAPE_HOTPLUG_CHAR_REMOVE:
-+			envp[3] = "INTERFACE=char";
++		/*
++		 * If the current entry has the same segment and wrap, then
++		 * there is no new entry needed. Only the block number of the
++		 * current entry might be adjusted to reflect an earlier start
++		 * of the segment.
++		 */
++		if(
++			(cur->bid.tbi_segment == new->bid.tbi_segment) &&
++			(cur->bid.tbi_wrap    == new->bid.tbi_wrap)
++		) {
++			if(new->bid.tbi_block < cur->bid.tbi_block) {
++				cur->bid.tbi_block = new->bid.tbi_block;
++			}
++			kfree(new);
 +			break;
-+		case TAPE_HOTPLUG_BLOCK_ADD:
-+		case TAPE_HOTPLUG_BLOCK_REMOVE:
-+			envp[3] = "INTERFACE=block";
++		}
++
++		/*
++		 * Otherwise the list is sorted by block number because it
++		 * is alway ascending while the segment number decreases on
++		 * the second wrap.
++		 */
++		if(cur->bid.tbi_block > new->bid.tbi_block) {
++			list_add_tail(&new->list, l);
 +			break;
++		}
 +	}
-+	envp[4] = devno;
-+	envp[5] = major;
-+	envp[6] = minor;
-+	envp[7] = NULL;
 +
-+	call_usermodehelper(argv[0], argv, envp);
-+#endif
-+}
++	/*
++	 * The loop went through without finding a merge or adding an entry
++	 * add the new entry to the end of the list.
++	 */
++	if(l == &discdata->sbid_list) {
++		list_add_tail(&new->list, &discdata->sbid_list);
++	}
 +
-+MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and "
-+	      "Michael Holzheu (cotte at de.ibm.com,holzheu at de.ibm.com)");
-+MODULE_DESCRIPTION("Linux on zSeries channel attached "
-+		   "tape device driver ($Revision: 1.7.4.8 $)");
++	list_for_each(l, &discdata->sbid_list) {
++		cur = list_entry(l, struct sbid_entry, list);
 +
-+module_init(tape_init);
-+module_exit(tape_exit);
++		DBF_EVENT(3, "sbid_list(%03i:%1i:%08i)\n",
++			cur->bid.tbi_segment, cur->bid.tbi_wrap,
++			cur->bid.tbi_block);
++	}
 +
-+EXPORT_SYMBOL(tape_state_string);
-+EXPORT_SYMBOL(tape_op_verbose);
-+EXPORT_SYMBOL(tape_state_set);
-+EXPORT_SYMBOL(tape_med_state_set);
-+EXPORT_SYMBOL(tape_register_discipline);
-+EXPORT_SYMBOL(tape_unregister_discipline);
-+EXPORT_SYMBOL(tape_alloc_request);
-+EXPORT_SYMBOL(tape_put_request);
-+EXPORT_SYMBOL(tape_clone_request);
-+EXPORT_SYMBOL(tape_dump_sense);
-+EXPORT_SYMBOL(tape_dump_sense_dbf);
-+EXPORT_SYMBOL(tape_do_io);
-+EXPORT_SYMBOL(tape_do_io_free);
-+EXPORT_SYMBOL(tape_do_io_async);
-+EXPORT_SYMBOL(tape_do_io_interruptible);
-+EXPORT_SYMBOL(tape_mtop);
-+EXPORT_SYMBOL(tape_hotplug_event);
++	return;	
++}
 +
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_devmap.c drivers/s390/char/tape_devmap.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_devmap.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/tape_devmap.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,949 @@
 +/*
-+ *  drivers/s390/char/tape_devmap.c
-+ *    device mapping for tape device driver
-+ *
-+ *  S390 and zSeries version
-+ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Carsten Otte <cotte at de.ibm.com>
-+ *               Michael Holzheu <holzheu at de.ibm.com>
-+ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-+ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ *		 Stefan Bader <shbader at de.ibm.com>
++ * Fill hardware positioning information into the given block id. With that
++ * seeks don't have to go back to the beginning of the tape and are done at
++ * faster speed because the vicinity of a segment can be located at faster
++ * speed.
 + *
-+ * Device mapping and tape= parameter parsing functions. All devmap
-+ * functions may not be called from interrupt context. In particular
-+ * tape_get_device is a no-no from interrupt context.
++ * The caller must have set tbi_block.
 + */
++static void
++tape_34xx_merge_sbid(
++	struct tape_device *		device,
++	struct tape_34xx_block_id *	bid
++) {
++	struct tape_34xx_discdata *	discdata = device->discdata;
++	struct sbid_entry *		cur;
++	struct list_head *		l;
 +
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/ctype.h>
-+#include <linux/init.h>
-+
-+#include <asm/debug.h>
-+#include <asm/irq.h>
-+#include <asm/uaccess.h>
++	bid->tbi_wrap    = 0;
++	bid->tbi_segment = 1;
++	bid->tbi_format  = (*device->modeset_byte & 0x08) ?
++				TBI_FORMAT_3480_XF : TBI_FORMAT_3480;
 +
-+/* This is ugly... */
-+#define PRINTK_HEADER "tape_devmap:"
++	if(discdata == NULL)
++		goto tape_34xx_merge_sbid_exit;
++	if(list_empty(&discdata->sbid_list))
++		goto tape_34xx_merge_sbid_exit;
 +
-+#define TAPE_DBF_AREA tape_core_dbf
-+#include "tape.h"
++	list_for_each(l, &discdata->sbid_list) {
++		cur = list_entry(l, struct sbid_entry, list);
 +
-+struct tape_devmap {
-+	struct list_head list;
-+	int devindex;
-+	unsigned short devno;
-+	devreg_t devreg;
-+	struct tape_device *device;
-+};
++		if(cur->bid.tbi_block > bid->tbi_block)
++			break;
++	}
 +
-+struct tape_discmap {
-+	struct list_head list;
-+	devreg_t devreg;
-+	struct tape_discipline *discipline;
-+};
++	/* If block comes before first entries block, use seek from start. */
++	if(l->prev == &discdata->sbid_list)
++		goto tape_34xx_merge_sbid_exit;
 +
-+/*
-+ * List of all registered tapes and disciplines.
-+ */
-+static struct list_head tape_devreg_list = LIST_HEAD_INIT(tape_devreg_list);
-+static struct list_head tape_disc_devreg_list = LIST_HEAD_INIT(tape_disc_devreg_list);
-+int tape_max_devindex = 0;
++	cur = list_entry(l->prev, struct sbid_entry, list);
++	bid->tbi_wrap    = cur->bid.tbi_wrap;
++	bid->tbi_segment = cur->bid.tbi_segment;
 +
-+/*
-+ * Single spinlock to protect devmap structures and lists.
-+ */
-+static spinlock_t tape_devmap_lock = SPIN_LOCK_UNLOCKED;
++tape_34xx_merge_sbid_exit:
++	DBF_EVENT(6, "merged_bid = %08x\n", *((unsigned int *) bid));
++	return;
++}
 +
-+/*
-+ * Module/Kernel Parameter Handling. The syntax of tape= is:
-+ *   <devno>		: (0x)?[0-9a-fA-F]+
-+ *   <range>		: <devno>(-<devno>)?
-+ *   <tape>		: <range>(,<range>)*
-+ */
-+int tape_autodetect = 0;	/* is true, when autodetection is active */
++static void
++tape_34xx_clear_sbid_list(struct tape_device *device)
++{
++	struct list_head *		l;
++	struct list_head *		n;
++	struct tape_34xx_discdata *	discdata;
 +
-+/*
-+ * char *tape[] is intended to hold the ranges supplied by the tape= statement
-+ * it is named 'tape' to directly be filled by insmod with the comma separated
-+ * strings when running as a module.
-+ */
-+static char *tape[256];
-+MODULE_PARM (tape, "1-" __MODULE_STRING (256) "s");
++	if((discdata = device->discdata) == NULL)
++		return;
++
++	list_for_each_safe(l, n, &discdata->sbid_list) {
++		list_del(l);
++		kfree(list_entry(l, struct sbid_entry, list));
++	}
++}
 +
-+#ifndef MODULE
 +/*
-+ * The parameter parsing functions for builtin-drivers are called
-+ * before kmalloc works. Store the pointers to the parameters strings
-+ * into tape[] for later processing.
++ * MTTELL: Tell block. Return the number of block relative to current file.
 + */
-+static int __init
-+tape_call_setup (char *str)
++int
++tape_34xx_mttell(struct tape_device *device, int mt_count)
 +{
-+	static int count = 0;
++	struct tape_34xx_block_id	bid;
++	int				rc;
 +
-+	if (count < 256)
-+		tape[count++] = str;
-+	return 1;
-+}
++	rc = tape_std_read_block_id(device, (unsigned int *) &bid);
++	if (rc)
++		return rc;
 +
-+__setup("tape=", tape_call_setup);
-+#endif	 /* not defined MODULE */
++	/*
++	 * Build up a lookup table. The format id is ingored.
++	 */
++	tape_34xx_add_sbid(device, bid);
++
++	return bid.tbi_block;
++}
 +
 +/*
-+ * Add a range of devices and create the corresponding devreg_t
-+ * structures. The order of the ranges added by this function
-+ * will define the kdevs for the individual devices.
++ * MTSEEK: seek to the specified block.
 + */
 +int
-+tape_add_range(int from, int to)
++tape_34xx_mtseek(struct tape_device *device, int mt_count)
 +{
-+	struct tape_devmap *devmap, *tmp;
-+	struct list_head *l;
-+	int devno;
-+	int rc;
++	struct tape_34xx_block_id	bid;
 +
-+	if (from > to) {
-+		PRINT_ERR("Invalid device range %04x-%04x", from, to);
++	if (mt_count > 0x400000) {
++		DBF_EXCEPTION(6, "xsee parm\n");
 +		return -EINVAL;
 +	}
 +
-+	rc = 0;
-+	spin_lock(&tape_devmap_lock);
-+	for (devno = from; devno <= to; devno++) {
-+		devmap = NULL;
-+		list_for_each(l, &tape_devreg_list) {
-+			tmp = list_entry(l, struct tape_devmap, list);
-+			if (tmp->devno == devno) {
-+				devmap = tmp;
-+				break;
-+			}
-+		}
-+		if (devmap == NULL) {
-+			if(tape_max_devindex >= 256/TAPE_MINORS_PER_DEV) {
-+				PRINT_ERR(" No more device slots available."
-+					" Range %04x-%04x ignored\n",
-+					devno, to);
-+				rc = -E2BIG;
-+				break;
-+			}
-+			/* This devno is new. */
-+			devmap = (struct tape_devmap *)
-+				kmalloc(sizeof(struct tape_devmap),
-+					GFP_KERNEL);
-+			if (devmap == NULL) {
-+				rc = -ENOMEM;
-+				break;
-+			}
-+			memset(devmap, 0, sizeof(struct tape_devmap));
-+			devmap->devno = devno;
-+			devmap->devindex = tape_max_devindex++;
-+			list_add(&devmap->list, &tape_devreg_list);
-+			devmap->devreg.ci.devno = devno;
-+			devmap->devreg.flag = DEVREG_TYPE_DEVNO;
-+			devmap->devreg.oper_func = tape_oper_handler;
-+			s390_device_register(&devmap->devreg);
-+		}
-+	}
-+	spin_unlock(&tape_devmap_lock);
++	bid.tbi_block   = mt_count;
 +
-+	return rc;
++	/*
++	 * Set hardware seek information in the block id.
++	 */
++	tape_34xx_merge_sbid(device, &bid);
++
++	return tape_std_seek_block_id(device, *((unsigned int *) &bid));
 +}
 +
 +/*
-+ * Read device number from string. The number is always is hex,
-+ * a leading 0x is accepted (and has to be removed for simple_stroul
-+ * to work).
++ * Tape block read for 34xx.
 + */
-+static inline int
-+tape_devno(char *str, char **endp)
++#ifdef CONFIG_S390_TAPE_BLOCK
++struct tape_request *
++tape_34xx_bread(struct tape_device *device, struct request *req)
 +{
-+	int devno;
++	struct tape_request  *request;
++	struct buffer_head   *bh;
++	ccw1_t               *ccw;
++	int                   count;
++	int                   size;
 +
-+	/* remove leading '0x' */
-+	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
-+		str += 2;
++	DBF_EVENT(6, "tape_34xx_bread(sector=%u,size=%u)\n",
++		req->sector, req->nr_sectors);
 +
-+	if (!isxdigit(*str))
-+		return -EINVAL;
++	/* Count the number of blocks for the request. */
++	count = 0;
++	size  = 0;
++	for(bh = req->bh; bh; bh = bh->b_reqnext) {
++		for(size = 0; size < bh->b_size; size += TAPEBLOCK_HSEC_SIZE)
++			count++;
++	}
 +
-+	devno = simple_strtoul(str, endp, 16); /* interpret anything as hex */
++	/* Allocate the ccw request. */
++	request = tape_alloc_request(3+count+1, 8);
++	if (IS_ERR(request))
++		return request;
 +
-+	if(devno < 0 || devno > 0xffff) {
-+		PRINT_ERR(" Invalid devno(0x%04x) specified\n", devno);
-+		return -EINVAL;
-+	}
++	/*
++	 * Setup the tape block id to start the read from. The block number
++	 * is later compared to the current position to decide whether a
++	 * locate block is required. If one is needed this block id is used
++	 * to locate it.
++	 */
++	((struct tape_34xx_block_id *) request->cpdata)->tbi_block =
++		req->sector >> TAPEBLOCK_HSEC_S2B;
 +
-+	return devno;
-+}
++	/* Setup ccws. */
++	request->op = TO_BLOCK;
++	ccw = request->cpaddr;
++	ccw = tape_ccw_cc(ccw, MODE_SET_DB, 1, device->modeset_byte);
 +
-+/*
-+ * Parse Kernel/Module Parameters and create devregs for dynamic attach/detach
-+ */
-+static int
-+tape_parm_parse (char *str)
-+{
-+	int from, to, rc;
++	/*
++	 * We always setup a nop after the mode set ccw. This slot is
++	 * used in tape_std_check_locate to insert a locate ccw if the
++	 * current tape position doesn't match the start block to be read.
++	 * The second nop will be filled with a read block id which is in
++	 * turn used by tape_34xx_free_bread to populate the segment bid
++	 * table.
++	 */
++	ccw = tape_ccw_cc(ccw, NOP, 0, NULL);
++	ccw = tape_ccw_cc(ccw, NOP, 0, NULL);
 +
-+	while (1) {
-+		to = from = tape_devno(str, &str);
-+		if (*str == '-') {
-+			str++;
-+			to = tape_devno(str, &str);
-+		}
-+		/* Negative numbers in from/to indicate errors. */
-+		if (from >= 0 && to >= 0) {
-+			rc = tape_add_range(from, to);
-+			if (rc)
-+				return rc;
++	for(bh = req->bh; bh; bh = bh->b_reqnext) {
++		for(size = 0; size < bh->b_size; size += TAPEBLOCK_HSEC_SIZE) {
++			ccw->flags    = CCW_FLAG_CC;
++			ccw->cmd_code = READ_FORWARD;
++			ccw->count    = TAPEBLOCK_HSEC_SIZE;
++			set_normalized_cda(ccw, (void *) __pa(bh->b_data+size));
++			ccw++;
 +		}
-+		if (*str != ',')
-+			break;
-+		str++;
-+	}
-+	if (*str != '\0') {
-+		PRINT_WARN(" Junk at end of tape parameter string: %s\n", str);
-+		return -EINVAL;
 +	}
-+	return 0;
-+}
 +
-+/*
-+ * Parse parameters stored in tape[].
-+ */
-+static int
-+tape_parse(void)
-+{
-+	int rc, i;
++	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
 +
-+	if (*tape == NULL) {
-+		/* No parameters present */
-+		PRINT_INFO ("No parameters supplied, enabling auto detect "
-+			    "mode for all supported devices.\n");
-+		tape_autodetect = 1;
-+		return 0;
-+	}
-+	PRINT_INFO("Using ranges supplied in parameters, "
-+		   "disabling auto detect mode.\n");
-+	rc = 0;
-+	for (i = 0; i < 256; i++) {
-+		if (tape[i] == NULL)
-+			break;
-+		rc = tape_parm_parse(tape[i]);
-+		if (rc) {
-+			PRINT_ERR(" Error while parsing parameters. "
-+				"Setup may be incomplete.\n");
-+			break;
-+		}
-+	}
-+	return rc;
++	return request;
 +}
 +
-+/*
-+ * Create a devreg for a discipline. This is only done if no explicit
-+ * tape range is given. The tape_oper_handler will call tape_add_range
-+ * for each device that appears.
-+ */
-+static int
-+tape_add_disc_devreg(struct tape_discipline *discipline)
++void
++tape_34xx_free_bread (struct tape_request *request)
 +{
-+	struct tape_discmap *discmap;
++	ccw1_t* ccw = request->cpaddr;
 +
-+	discmap = (struct tape_discmap *) kmalloc(sizeof(struct tape_discmap),
-+						  GFP_KERNEL);
-+	if (discmap == NULL) {
-+		PRINT_WARN("Could not alloc devreg: Out of memory\n"
-+			   "Dynamic attach/detach will not work!\n");
-+		return -ENOMEM;
++	if((ccw + 2)->cmd_code == READ_BLOCK_ID) {
++		struct {
++			struct tape_34xx_block_id	channel_block_id;
++			struct tape_34xx_block_id	device_block_id;
++		} __attribute__ ((packed)) *rbi_data;
++
++		rbi_data = request->cpdata;
++
++		if(!request->device)
++			DBF_EVENT(6, "tape_34xx_free_bread: no device!\n");
++		DBF_EVENT(6, "tape_34xx_free_bread: update_sbid\n");
++		tape_34xx_add_sbid(
++			request->device,
++			rbi_data->channel_block_id
++		);
++	} else {
++		DBF_EVENT(3, "tape_34xx_free_bread: no block info\n");
 +	}
-+	spin_lock(&tape_devmap_lock);
-+	discmap->devreg.ci.hc.ctype = discipline->cu_type;
-+	discmap->devreg.flag = DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS;
-+	discmap->devreg.oper_func = tape_oper_handler;
-+	s390_device_register(&discmap->devreg);
-+	list_add(&discmap->list, &tape_disc_devreg_list);
-+	spin_unlock(&tape_devmap_lock);
-+	return 0;
++
++	/* Last ccw is a nop and doesn't need clear_normalized_cda */
++	for (ccw = request->cpaddr; ccw->flags & CCW_FLAG_CC; ccw++)
++		if (ccw->cmd_code == READ_FORWARD)
++			clear_normalized_cda(ccw);
++	tape_put_request(request);
 +}
 +
 +/*
-+ * Free devregs for a discipline.
++ * check_locate is called just before the tape request is passed to
++ * the common io layer for execution. It has to check the current
++ * tape position and insert a locate ccw if it doesn't match the
++ * start block for the request.
 + */
-+static void
-+tape_del_disc_devreg(struct tape_discipline *discipline)
++void
++tape_34xx_check_locate(struct tape_device *device, struct tape_request *request)
 +{
-+	struct list_head *l;
-+	struct tape_discmap *discmap;
++	struct tape_34xx_block_id *id;
++	struct tape_34xx_block_id *start;
 +
-+	spin_lock(&tape_devmap_lock);
-+	list_for_each(l, &tape_disc_devreg_list) {
-+		discmap = list_entry(l, struct tape_discmap, list);
-+		if (discmap->discipline == discipline) {
-+			s390_device_unregister(&discmap->devreg);
-+			list_del(&discmap->list);
-+			kfree(discmap);
-+			break;
-+		}
-+	}
-+	spin_unlock(&tape_devmap_lock);
-+}
++	id = (struct tape_34xx_block_id *) request->cpdata;
 +
++	/*
++	 * The tape is already at the correct position. No seek needed.
++	 */
++	if (id->tbi_block == device->blk_data.block_position)
++		return;
 +
-+/*
-+ * Forget all about device numbers and disciplines.
-+ * This may only be called at module unload or system shutdown.
-+ */
-+static void
-+tape_forget_devregs(void)
-+{
-+	struct list_head *l, *n;
-+	struct tape_devmap *devmap;
-+	struct tape_discmap *discmap;
++	/*
++	 * In case that the block device image doesn't start at the beginning
++	 * of the tape, adjust the blocknumber for the locate request.
++	 */
++	start = (struct tape_34xx_block_id *) &device->blk_data.start_block_id;
++	if(start->tbi_block)
++		id->tbi_block = id->tbi_block + start->tbi_block;
 +
-+	spin_lock(&tape_devmap_lock);
-+        list_for_each_safe(l, n, &tape_devreg_list) {
-+		devmap = list_entry(l, struct tape_devmap, list);
-+		if (devmap->device != NULL)
-+			BUG();
-+		s390_device_unregister(&devmap->devreg);
-+		list_del(&devmap->list);
-+		kfree(devmap);
-+	}
-+	list_for_each_safe(l, n, &tape_disc_devreg_list) {
-+		discmap = list_entry(l, struct tape_discmap, list);
-+		s390_device_unregister(&discmap->devreg);
-+		list_del(&discmap->list);
-+		kfree(discmap);
-+	}
-+	spin_unlock(&tape_devmap_lock);
++	/*
++	 * Merge HW positioning information to the block id. This information
++	 * is used by the device for faster seeks.
++	 */
++	tape_34xx_merge_sbid(device, id);
++
++	/*
++	 * Transform the NOP to a LOCATE entry.
++	 */
++	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
++	tape_ccw_cc(request->cpaddr + 2, READ_BLOCK_ID, 8, request->cpdata);
++
++	return;
++}
++#endif
++
++static int
++tape_34xx_mtweof(struct tape_device *device, int count)
++{
++	tape_34xx_clear_sbid_list(device);
++	return tape_std_mtweof(device, count);
 +}
 +
 +/*
-+ * Allocate memory for a new device structure.
++ * List of 3480/3490 magnetic tape commands.
 + */
-+static struct tape_device *
-+tape_alloc_device(void)
++static tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] =
 +{
-+	struct tape_device *device;
++	[MTRESET]        = tape_std_mtreset,
++	[MTFSF]          = tape_std_mtfsf,
++	[MTBSF]          = tape_std_mtbsf,
++	[MTFSR]          = tape_std_mtfsr,
++	[MTBSR]          = tape_std_mtbsr,
++	[MTWEOF]         = tape_34xx_mtweof,
++	[MTREW]          = tape_std_mtrew,
++	[MTOFFL]         = tape_std_mtoffl,
++	[MTNOP]          = tape_std_mtnop,
++	[MTRETEN]        = tape_std_mtreten,
++	[MTBSFM]         = tape_std_mtbsfm,
++	[MTFSFM]         = tape_std_mtfsfm,
++	[MTEOM]          = tape_std_mteom,
++	[MTERASE]        = tape_std_mterase,
++	[MTRAS1]         = NULL,
++	[MTRAS2]         = NULL,
++	[MTRAS3]         = NULL,
++	[MTSETBLK]       = tape_std_mtsetblk,
++	[MTSETDENSITY]   = NULL,
++	[MTSEEK]         = tape_34xx_mtseek,
++	[MTTELL]         = tape_34xx_mttell,
++	[MTSETDRVBUFFER] = NULL,
++	[MTFSS]          = NULL,
++	[MTBSS]          = NULL,
++	[MTWSM]          = NULL,
++	[MTLOCK]         = NULL,
++	[MTUNLOCK]       = NULL,
++	[MTLOAD]         = tape_std_mtload,
++	[MTUNLOAD]       = tape_std_mtunload,
++	[MTCOMPRESSION]  = tape_std_mtcompression,
++	[MTSETPART]      = NULL,
++	[MTMKPART]       = NULL
++};
 +
-+	device = (struct tape_device *)
-+		kmalloc(sizeof(struct tape_device), GFP_KERNEL);
-+	if (device == NULL) {
-+		DBF_EXCEPTION(2, "ti:no mem\n");
-+		PRINT_INFO ("can't allocate memory for "
-+			    "tape info structure\n");
-+		return ERR_PTR(-ENOMEM);
-+	}
-+	memset(device, 0, sizeof(struct tape_device));
-+	device->modeset_byte = (char *) kmalloc(1, GFP_KERNEL | GFP_DMA);
-+	if (device->modeset_byte == NULL) {
-+		DBF_EXCEPTION(2, "ti:no mem\n");
-+		PRINT_INFO("can't allocate memory for modeset byte\n");
-+		kfree(device);
-+		return ERR_PTR(-ENOMEM);
-+	}
-+	INIT_LIST_HEAD(&device->req_queue);
-+	init_waitqueue_head(&device->state_change_wq);
-+	spin_lock_init(&device->assign_lock);
-+	atomic_set(&device->ref_count, 1);
-+	TAPE_SET_STATE(device, TAPE_STATUS_INIT);
-+	device->medium_state  = MS_UNKNOWN;
-+	*device->modeset_byte = 0;
++/*
++ * Tape discipline structures for 3480 and 3490.
++ */
++static struct tape_discipline tape_discipline_3480 = {
++	.owner = THIS_MODULE,
++	.cu_type = 0x3480,
++	.setup_device = tape_34xx_setup_device,
++	.cleanup_device = tape_34xx_cleanup_device,
++	.process_eov = tape_std_process_eov,
++	.irq = tape_34xx_irq,
++	.read_block = tape_std_read_block,
++	.write_block = tape_std_write_block,
++	.assign = tape_std_assign,
++	.unassign = tape_std_unassign,
++#ifdef TAPE390_FORCE_UNASSIGN
++	.force_unassign = tape_std_force_unassign,
++#endif
++#ifdef CONFIG_S390_TAPE_BLOCK
++	.bread = tape_34xx_bread,
++	.free_bread = tape_34xx_free_bread,
++	.check_locate = tape_34xx_check_locate,
++#endif
++	.ioctl_fn = tape_34xx_ioctl,
++	.mtop_array = tape_34xx_mtop
++};
 +
-+	return device;
-+}
++static struct tape_discipline tape_discipline_3490 = {
++	.owner = THIS_MODULE,
++	.cu_type = 0x3490,
++	.setup_device = tape_34xx_setup_device,
++	.cleanup_device = tape_34xx_cleanup_device,
++	.process_eov = tape_std_process_eov,
++	.irq = tape_34xx_irq,
++	.read_block = tape_std_read_block,
++	.write_block = tape_std_write_block,
++	.assign = tape_std_assign,
++	.unassign = tape_std_unassign,
++#ifdef TAPE390_FORCE_UNASSIGN
++	.force_unassign = tape_std_force_unassign,
++#endif
++#ifdef CONFIG_S390_TAPE_BLOCK
++	.bread = tape_34xx_bread,
++	.free_bread = tape_34xx_free_bread,
++	.check_locate = tape_34xx_check_locate,
++#endif
++	.ioctl_fn = tape_34xx_ioctl,
++	.mtop_array = tape_34xx_mtop
++};
 +
-+/*
-+ * Create a device structure. 
-+ */
-+static struct tape_device *
-+tape_create_device(int devno)
++int
++tape_34xx_init (void)
 +{
-+	struct list_head *l;
-+	struct tape_devmap *devmap, *tmp;
-+	struct tape_device *device;
 +	int rc;
 +
-+	DBF_EVENT(4, "tape_create_device(0x%04x)\n", devno);
-+
-+	device = tape_alloc_device();
-+	if (IS_ERR(device))
-+		return device;
-+	/* Get devinfo from the common io layer. */
-+	rc = get_dev_info_by_devno(devno, &device->devinfo);
-+	if (rc) {
-+		DBF_EVENT(3, "get_dev_info_by_devno returned %d\n", rc);
-+		if (rc == -EUSERS) {
-+			device->devinfo.status |= DEVSTAT_UNFRIENDLY_DEV;
-+		} else {
-+			tape_put_device(device);
-+			return ERR_PTR(rc);
-+		}
-+	}
++	TAPE_DBF_AREA = debug_register ( "tape_34xx", 1, 2, 4*sizeof(long));
++	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
 +
-+	spin_lock(&tape_devmap_lock);
-+	devmap = NULL;
-+	list_for_each(l, &tape_devreg_list) {
-+		tmp = list_entry(l, struct tape_devmap, list);
-+		if (tmp->devno == devno) {
-+			devmap = tmp;
-+			break;
-+		}
-+	}
-+	if (devmap != NULL && devmap->device == NULL) {
-+		devmap->device = tape_clone_device(device);
-+		device->first_minor = devmap->devindex * TAPE_MINORS_PER_DEV;
-+	} else if (devmap == NULL) {
-+		/* devno not in tape range. */
-+		DBF_EVENT(4, "No devmap for entry 0x%04x\n", devno);
-+		tape_put_device(device);
-+		device = ERR_PTR(-ENODEV);
-+	} else {
-+		/* Should not happen. */
-+		DBF_EVENT(4, "A devmap entry for 0x%04x already exists\n",
-+			devno);
-+		tape_put_device(device);
-+		device = ERR_PTR(-EEXIST);
++	DBF_EVENT(3, "34xx init: $Revision: 1.9.4.5 $\n");
++	/* Register discipline. */
++	rc = tape_register_discipline(&tape_discipline_3480);
++	if (rc == 0) {
++		rc = tape_register_discipline(&tape_discipline_3490);
++		if (rc)
++			tape_unregister_discipline(&tape_discipline_3480);
 +	}
-+	spin_unlock(&tape_devmap_lock);
-+
-+	return device;
++	if (rc)
++		DBF_EVENT(3, "34xx init failed\n");
++	else
++		DBF_EVENT(3, "34xx registered\n");
++	return rc;
 +}
 +
-+struct tape_device *
-+tape_clone_device(struct tape_device *device)
++void
++tape_34xx_exit(void)
 +{
-+	DBF_EVENT(4, "tape_clone_device(%p) = %i\n", device,
-+		atomic_inc_return(&device->ref_count));
-+	return device;
++	tape_unregister_discipline(&tape_discipline_3480);
++	tape_unregister_discipline(&tape_discipline_3490);
++	debug_unregister(TAPE_DBF_AREA);
 +}
 +
++MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH");
++MODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape "
++		   "device driver ($Revision: 1.9.4.5 $)");
++MODULE_LICENSE("GPL");
++
++module_init(tape_34xx_init);
++module_exit(tape_34xx_exit);
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape34xx.c drivers/s390/char/tape34xx.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape34xx.c	2002-08-02 18:39:44.000000000 -0600
++++ drivers/s390/char/tape34xx.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1,2389 +0,0 @@
+-/***************************************************************************
+- *
+- *  drivers/s390/char/tape34xx.c
+- *    common tape device discipline for 34xx tapes.
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s): Carsten Otte <cotte at de.ibm.com>
+- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *
+- ****************************************************************************
+- */
+-
+-#include "tapedefs.h"
+-#include <linux/config.h>
+-#include <linux/version.h>
+-#include <linux/stddef.h>
+-#include <linux/kernel.h>
+-#include <asm/types.h>
+-#include <asm/uaccess.h>
+-#include <linux/stat.h>
+-#include <linux/proc_fs.h>
+-#include <asm/ccwcache.h> 
+-#include <asm/idals.h>  
+-#ifdef CONFIG_S390_TAPE_DYNAMIC
+-#include <asm/s390dyn.h>
+-#endif
+-#include <asm/debug.h>
+-#include <linux/compatmac.h>
+-#include "tape.h"
+-#include "tape34xx.h"
+-
+-#define PRINTK_HEADER "T34xx:"
+-
+-tape_event_handler_t tape34xx_event_handler_table[TS_SIZE][TE_SIZE] =
+-{
+-    /* {START , DONE, FAILED, ERROR, OTHER } */
+-	{NULL, tape34xx_unused_done, NULL, NULL, NULL},	/* TS_UNUSED */
+-	{NULL, tape34xx_idle_done, NULL, NULL, NULL},	/* TS_IDLE */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_DONE */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_FAILED */
+-	{NULL, tape34xx_block_done, NULL, NULL, NULL},		/* TS_BLOCK_INIT */
+-	{NULL, tape34xx_bsb_init_done, NULL, NULL, NULL},	/* TS_BSB_INIT */
+-	{NULL, tape34xx_bsf_init_done, NULL, NULL, NULL},	/* TS_BSF_INIT */
+-	{NULL, tape34xx_dse_init_done, NULL, NULL, NULL},	/* TS_DSE_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_EGA_INIT */
+-	{NULL, tape34xx_fsb_init_done, NULL, NULL, NULL},	/* TS_FSB_INIT */
+-	{NULL, tape34xx_fsf_init_done, NULL, NULL, NULL},	/* TS_FSF_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_LDI_INIT */
+-	{NULL, tape34xx_lbl_init_done, NULL, NULL, NULL},	/* TS_LBL_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_MSE_INIT */
+-	{NULL, tape34xx_nop_init_done, NULL, NULL, NULL},	/* TS_NOP_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBA_INIT */
+-	{NULL, tape34xx_rbi_init_done, NULL, NULL, NULL},	/* TS_RBI_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBU_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RBL_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RDC_INIT */
+-	{NULL, tape34xx_rfo_init_done, NULL, NULL, NULL},	/* TS_RFO_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_RSD_INIT */
+-	{NULL, tape34xx_rew_init_done, NULL, NULL, NULL},	/* TS_REW_INIT */
+-	{NULL, tape34xx_rew_release_init_done, NULL, NULL, NULL},	/* TS_REW_RELEASE_IMIT */
+-	{NULL, tape34xx_run_init_done, NULL, NULL, NULL},	/* TS_RUN_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SEN_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SID_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SNP_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SPG_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SWI_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SMR_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_SYN_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_TIO_INIT */
+-	{NULL, NULL, NULL, NULL, NULL},		/* TS_UNA_INIT */
+-	{NULL, tape34xx_wri_init_done, NULL, NULL, NULL},	/* TS_WRI_INIT */
+-	{NULL, tape34xx_wtm_init_done, NULL, NULL, NULL},	/* TS_WTM_INIT */
+-	{NULL, NULL, NULL, NULL, NULL}};        /* TS_NOT_OPER */
+-
+-
+-int
+-tape34xx_ioctl_overload (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+-{
+-	return -EINVAL;		// no additional ioctls
+-
+-}
+-
+-ccw_req_t *
+-tape34xx_write_block (const char *data, size_t count, tape_info_t * ti)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	void *mem;
+-	cqr = tape_alloc_ccw_req (ti, 2, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xwbl nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	mem = kmalloc (count, GFP_KERNEL);
+-	if (!mem) {
+-		tape_free_request (cqr);
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xwbl nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	if (copy_from_user (mem, data, count)) {
+-		kfree (mem);
+-		tape_free_request (cqr);
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xwbl segf.");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-
+-	ccw->cmd_code = WRITE_CMD;
+-	ccw->flags = 0;
+-	ccw->count = count;
+-	set_normalized_cda (ccw, (unsigned long) mem);
+-	if ((ccw->cda) == 0) {
+-		kfree (mem);
+-		tape_free_request (cqr);
+-		return NULL;
+-	}
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = mem;
+-	ti->userbuf = (void *) data;
+-	tapestate_set (ti, TS_WRI_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xwbl ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-void 
+-tape34xx_free_write_block (ccw_req_t * cqr, tape_info_t * ti)
+-{
+-	unsigned long lockflags;
+-	ccw1_t *ccw;
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ccw = cqr->cpaddr;
+-	ccw++;
+-	clear_normalized_cda (ccw);
+-	kfree (ti->kernbuf);
+-	tape_free_request (cqr);
+-	ti->kernbuf = ti->userbuf = NULL;
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xfwb free");
+-#endif /* TAPE_DEBUG */
+-}
+-
+-ccw_req_t *
+-tape34xx_read_block (const char *data, size_t count, tape_info_t * ti)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	void *mem;
+-	cqr = tape_alloc_ccw_req (ti, 2, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xrbl nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	mem = kmalloc (count, GFP_KERNEL);
+-	if (!mem) {
+-		tape_free_request (cqr);
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xrbl nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-
+-	ccw->cmd_code = READ_FORWARD;
+-	ccw->flags = 0;
+-	ccw->count = count;
+-	set_normalized_cda (ccw, (unsigned long) mem);
+-	if ((ccw->cda) == 0) {
+-		kfree (mem);
+-		tape_free_request (cqr);
+-		return NULL;
+-	}
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = mem;
+-	ti->userbuf = (void *) data;
+-	tapestate_set (ti, TS_RFO_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xrbl ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-ccw_req_t *
+-tape34xx_read_opposite (tape_info_t * ti,int novalue)
+-{
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	size_t count;
+-	// first, retrieve the count from the old cqr.
+-	cqr = ti->cqr;
+-	ccw = cqr->cpaddr;
+-	ccw++;
+-	count=ccw->count;
+-	// free old cqr.
+-	clear_normalized_cda (ccw);
+-	tape_free_request (cqr);
+-	// build new cqr
+-	cqr = tape_alloc_ccw_req (ti, 3, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xrop nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-
+-	ccw->cmd_code = READ_BACKWARD;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = count;
+-	set_normalized_cda (ccw, (unsigned long) ti->kernbuf);
+-	if ((ccw->cda) == 0) {
+-		tape_free_request (cqr);
+-		return NULL;
+-	}
+-	ccw++;
+-	ccw->cmd_code = FORSPACEBLOCK;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	ccw->cda = (unsigned long)ccw;
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 1;
+-	ccw->cda = (unsigned long)ccw;
+-	tapestate_set (ti, TS_RBA_INIT);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xrop ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-void 
+-tape34xx_free_read_block (ccw_req_t * cqr, tape_info_t * ti)
+-{
+-	unsigned long lockflags;
+-	size_t cpysize;
+-	ccw1_t *ccw;
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ccw = cqr->cpaddr;
+-	ccw++;
+-	cpysize = ccw->count - ti->devstat.rescnt;
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	if (copy_to_user (ti->userbuf, ti->kernbuf, cpysize)) {
+-#ifdef TAPE_DEBUG
+-	    debug_text_exception (tape_debug_area,6,"xfrb segf.");
+-#endif /* TAPE_DEBUG */
+-	}
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	clear_normalized_cda (ccw);
+-	kfree (ti->kernbuf);
+-	tape_free_request (cqr);
+-	ti->kernbuf = ti->userbuf = NULL;
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xfrb free");
+-#endif /* TAPE_DEBUG */
+-}
+-
+-/*
+- * The IOCTL interface is implemented in the following section,
+- * excepted the MTRESET, MTSETBLK which are handled by tapechar.c
+- */
+-/*
+- * MTFSF: Forward space over 'count' file marks. The tape is positioned
+- * at the EOT (End of Tape) side of the file mark.
+- */
+-ccw_req_t *
+-tape34xx_mtfsf (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	int i;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	if ((count == 0) || (count > 510)) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xfsf parm");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xfsf nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	for (i = 0; i < count; i++) {
+-		ccw->cmd_code = FORSPACEFILE;
+-		ccw->flags = CCW_FLAG_CC;
+-		ccw->count = 0;
+-		ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-		ccw++;
+-	}
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_FSF_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xfsf ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTBSF: Backward space over 'count' file marks. The tape is positioned at
+- * the EOT (End of Tape) side of the last skipped file mark.
+- */
+-ccw_req_t *
+-tape34xx_mtbsf (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	int i;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	if ((count == 0) || (count > 510)) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xbsf parm");
+-#endif /* TAPE_DEBUG */
+-	        return NULL;
+-	}
+-	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xbsf nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	for (i = 0; i < count; i++) {
+-		ccw->cmd_code = BACKSPACEFILE;
+-		ccw->flags = CCW_FLAG_CC;
+-		ccw->count = 0;
+-		ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-		ccw++;
+-	}
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_BSF_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xbsf ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTFSR: Forward space over 'count' tape blocks (blocksize is set
+- * via MTSETBLK.
+- */
+-ccw_req_t *
+-tape34xx_mtfsr (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	int i;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	if ((count == 0) || (count > 510)) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xfsr parm");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xfsr nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	for (i = 0; i < count; i++) {
+-		ccw->cmd_code = FORSPACEBLOCK;
+-		ccw->flags = CCW_FLAG_CC;
+-		ccw->count = 0;
+-		ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-		ccw++;
+-	}
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_FSB_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xfsr ccwgen");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTBSR: Backward space over 'count' tape blocks.
+- * (blocksize is set via MTSETBLK.
+- */
+-ccw_req_t *
+-tape34xx_mtbsr (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	int i;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	if ((count == 0) || (count > 510)) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xbsr parm");
+-#endif /* TAPE_DEBUG */   
+-	        return NULL;
+-	}
+-	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xbsr nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	for (i = 0; i < count; i++) {
+-		ccw->cmd_code = BACKSPACEBLOCK;
+-		ccw->flags = CCW_FLAG_CC;
+-		ccw->count = 0;
+-		ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-		ccw++;
+-	}
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_BSB_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xbsr ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTWEOF: Write 'count' file marks at the current position.
+- */
+-ccw_req_t *
+-tape34xx_mtweof (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	int i;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	if ((count == 0) || (count > 510)) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xweo parm");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xweo nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	for (i = 0; i < count; i++) {
+-		ccw->cmd_code = WRITETAPEMARK;
+-		ccw->flags = CCW_FLAG_CC;
+-		ccw->count = 1;
+-		ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-		ccw++;
+-	}
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	ccw++;
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_WTM_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xweo ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTREW: Rewind the tape.
+- */
+-ccw_req_t *
+-tape34xx_mtrew (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 3, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xrew nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = REWIND;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_REW_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xrew ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTOFFL: Rewind the tape and put the drive off-line.
+- * Implement 'rewind unload'
+- */
+-ccw_req_t *
+-tape34xx_mtoffl (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 3, 32);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xoff nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = REWIND_UNLOAD;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	ccw++;
+-	ccw->cmd_code = SENSE;
+-	ccw->flags = 0;
+-	ccw->count = 32;
+-	ccw->cda = (unsigned long) cqr->cpaddr;
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_RUN_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xoff ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTNOP: 'No operation'.
+- */
+-ccw_req_t *
+-tape34xx_mtnop (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 1, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xnop nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) ccw->cmd_code;
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_NOP_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xnop ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTBSFM: Backward space over 'count' file marks.
+- * The tape is positioned at the BOT (Begin Of Tape) side of the
+- * last skipped file mark.
+- */
+-ccw_req_t *
+-tape34xx_mtbsfm (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	int i;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	if ((count == 0) || (count > 510)) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xbsm parm");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xbsm nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	for (i = 0; i < count; i++) {
+-		ccw->cmd_code = BACKSPACEFILE;
+-		ccw->flags = CCW_FLAG_CC;
+-		ccw->count = 0;
+-		ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-		ccw++;
+-	}
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_BSF_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xbsm ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTFSFM: Forward space over 'count' file marks.
+- * The tape is positioned at the BOT (Begin Of Tape) side
+- * of the last skipped file mark.
+- */
+-ccw_req_t *
+-tape34xx_mtfsfm (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	int i;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	if ((count == 0) || (count > 510)) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xfsm parm");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	cqr = tape_alloc_ccw_req (ti, 2 + count, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xfsm nomem");
+-#endif /* TAPE_DEBUG */	    
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	for (i = 0; i < count; i++) {
+-		ccw->cmd_code = FORSPACEFILE;
+-		ccw->flags = CCW_FLAG_CC;
+-		ccw->count = 0;
+-		ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-		ccw++;
+-	}
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_FSF_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xfsm ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTEOM: positions at the end of the portion of the tape already used
+- * for recordind data. MTEOM positions after the last file mark, ready for
+- * appending another file.
+- * MTRETEN: Retension the tape, i.e. forward space to end of tape and rewind.
+- */
+-ccw_req_t *
+-tape34xx_mteom (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 4, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xeom nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = FORSPACEFILE;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	ccw++;
+-	ccw->cmd_code = CCW_CMD_TIC;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (cqr->cpaddr);
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_FSF_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xeom ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTERASE: erases the tape.
+- */
+-ccw_req_t *
+-tape34xx_mterase (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 5, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xera nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = REWIND;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	ccw++;
+-	ccw->cmd_code = ERASE_GAP;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	ccw++;
+-	ccw->cmd_code = DATA_SEC_ERASE;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_DSE_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xera ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTSETDENSITY: set tape density.
+- */
+-ccw_req_t *
+-tape34xx_mtsetdensity (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 2, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xden nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_NOP_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xden ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTSEEK: seek to the specified block.
+- */
+-ccw_req_t *
+-tape34xx_mtseek (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	__u8 *data;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	if ((data = kmalloc (4 * sizeof (__u8), GFP_KERNEL)) == NULL) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xsee nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	data[0] = 0x01;
+-	data[1] = data[2] = data[3] = 0x00;
+-	if (count >= 4194304) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xsee parm");
+-#endif /* TAPE_DEBUG */
+-		kfree(data);
+-		return NULL;
+-	}
+-	if (((tape34xx_disc_data_t *) ti->discdata)->modeset_byte & 0x08)	// IDRC on
+-
+-		data[1] = data[1] | 0x80;
+-	data[3] += count % 256;
+-	data[2] += (count / 256) % 256;
+-	data[1] += (count / 65536);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xsee id:");
+-	debug_int_event (tape_debug_area,6,count);
+-#endif /* TAPE_DEBUG */
+-	cqr = tape_alloc_ccw_req (ti, 3, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xsee nomem");
+-#endif /* TAPE_DEBUG */
+-		kfree (data);
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = LOCATE;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 4;
+-	set_normalized_cda (ccw, (unsigned long) data);
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = data;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_LBL_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xsee ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTTELL: Tell block. Return the number of block relative to current file.
+- */
+-ccw_req_t *
+-tape34xx_mttell (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	void *mem;
+-	cqr = tape_alloc_ccw_req (ti, 2, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xtel nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	mem = kmalloc (8, GFP_KERNEL);
+-	if (!mem) {
+-		tape_free_request (cqr);
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xtel nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-
+-	ccw->cmd_code = READ_BLOCK_ID;
+-	ccw->flags = 0;
+-	ccw->count = 8;
+-	set_normalized_cda (ccw, (unsigned long) mem);
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = mem;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_RBI_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xtel ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTSETDRVBUFFER: Set the tape drive buffer code to number.
+- * Implement NOP.
+- */
+-ccw_req_t *
+-tape34xx_mtsetdrvbuffer (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 2, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xbuf nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_NOP_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xbuf ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTLOCK: Locks the tape drive door.
+- * Implement NOP CCW command.
+- */
+-ccw_req_t *
+-tape34xx_mtlock (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 2, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xloc nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_NOP_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xloc ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTUNLOCK: Unlocks the tape drive door.
+- * Implement the NOP CCW command.
+- */
+-ccw_req_t *
+-tape34xx_mtunlock (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 2, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xulk nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_NOP_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xulk ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTLOAD: Loads the tape.
+- * This function is not implemented and returns NULL, which causes the Frontend to wait for a medium being loaded.
+- *  The 3480/3490 type Tapes do not support a load command
+- */
+-ccw_req_t *
+-tape34xx_mtload (tape_info_t * ti, int count)
+-{
+-         return NULL;
+-}
+-
+-/*
+- * MTUNLOAD: Rewind the tape and unload it.
+- */
+-ccw_req_t *
+-tape34xx_mtunload (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 3, 32);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xunl nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = REWIND_UNLOAD;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	ccw++;
+-	ccw->cmd_code = SENSE;
+-	ccw->flags = 0;
+-	ccw->count = 32;
+-	ccw->cda = (unsigned long) cqr->cpaddr;
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_RUN_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xunl ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTCOMPRESSION: used to enable compression.
+- * Sets the IDRC on/off.
+- */
+-ccw_req_t *
+-tape34xx_mtcompression (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	if ((count < 0) || (count > 1)) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xcom parm");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	if (count == 0)
+-		((tape34xx_disc_data_t *) ti->discdata)->modeset_byte = 0x00;		// IDRC off
+-
+-	else
+-		((tape34xx_disc_data_t *) ti->discdata)->modeset_byte = 0x08;		// IDRC on
+-
+-	cqr = tape_alloc_ccw_req (ti, 2, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xcom nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_NOP_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xcom ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTSTPART: Move the tape head at the partition with the number 'count'.
+- * Implement the NOP CCW command.
+- */
+-ccw_req_t *
+-tape34xx_mtsetpart (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 2, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xspa nomem");
+-#endif /* TAPE_DEBUG */	    
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_NOP_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xspa ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTMKPART: .... dummy .
+- * Implement the NOP CCW command.
+- */
+-ccw_req_t *
+-tape34xx_mtmkpart (tape_info_t * ti, int count)
+-{
+-	long lockflags;
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	cqr = tape_alloc_ccw_req (ti, 2, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xnpa nomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->kernbuf = NULL;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_NOP_INIT);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xnpa ccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-
+-/*
+- * MTIOCGET: query the tape drive status.
+- */
+-ccw_req_t *
+-tape34xx_mtiocget (tape_info_t * ti, int count)
+-{
+-	return NULL;
+-}
+-
+-/*
+- * MTIOCPOS: query the tape position.
+- */
+-ccw_req_t *
+-tape34xx_mtiocpos (tape_info_t * ti, int count)
+-{
+-	return NULL;
+-}
+-
+-ccw_req_t * tape34xx_bread (struct request *req,tape_info_t* ti,int tapeblock_major) {
+-	ccw_req_t *cqr;
+-	ccw1_t *ccw;
+-	__u8 *data;
+-	int s2b = blksize_size[tapeblock_major][ti->blk_minor]/hardsect_size[tapeblock_major][ti->blk_minor];
+-	int realcount;
+-	int size,bhct = 0;
+-	struct buffer_head* bh;
+-	for (bh = req->bh; bh; bh = bh->b_reqnext) {
+-		if (bh->b_size > blksize_size[tapeblock_major][ti->blk_minor])
+-			for (size = 0; size < bh->b_size; size += blksize_size[tapeblock_major][ti->blk_minor])
+-				bhct++;
+-		else
+-			bhct++;
+-	}
+-	if ((data = kmalloc (4 * sizeof (__u8), GFP_ATOMIC)) == NULL) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,3,"xBREDnomem");
+-#endif /* TAPE_DEBUG */
+-		return NULL;
+-	}
+-	data[0] = 0x01;
+-	data[1] = data[2] = data[3] = 0x00;
+-	realcount=req->sector/s2b;
+-	if (((tape34xx_disc_data_t *) ti->discdata)->modeset_byte & 0x08)	// IDRC on
+-
+-		data[1] = data[1] | 0x80;
+-	data[3] += realcount % 256;
+-	data[2] += (realcount / 256) % 256;
+-	data[1] += (realcount / 65536);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xBREDid:");
+-	debug_int_event (tape_debug_area,6,realcount);
+-#endif /* TAPE_DEBUG */
+-	cqr = tape_alloc_ccw_req (ti, 2+bhct+1, 0);
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_exception (tape_debug_area,6,"xBREDnomem");
+-#endif /* TAPE_DEBUG */
+-		kfree(data);
+-		return NULL;
+-	}
+-	ccw = cqr->cpaddr;
+-	ccw->cmd_code = MODE_SET_DB;
+-	ccw->flags = CCW_FLAG_CC;
+-	ccw->count = 1;
+-	set_normalized_cda (ccw, (unsigned long) (&(((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)));
+-	if (realcount!=ti->position) {
+-	    ccw++;
+-	    ccw->cmd_code = LOCATE;
+-	    ccw->flags = CCW_FLAG_CC;
+-	    ccw->count = 4;
+-	    set_normalized_cda (ccw, (unsigned long) data);
+-	}
+-	ti->position=realcount+req->nr_sectors/s2b;
+-	for (bh=req->bh;bh!=NULL;) {
+-	        ccw->flags = CCW_FLAG_CC;
+-		if (bh->b_size >= blksize_size[tapeblock_major][ti->blk_minor]) {
+-			for (size = 0; size < bh->b_size; size += blksize_size[tapeblock_major][ti->blk_minor]) {
+-			        ccw++;
+-				ccw->flags = CCW_FLAG_CC;
+-				ccw->cmd_code = READ_FORWARD;
+-				ccw->count = blksize_size[tapeblock_major][ti->blk_minor];
+-				set_normalized_cda (ccw, __pa (bh->b_data + size));
+-			}
+-			bh = bh->b_reqnext;
+-		} else {	/* group N bhs to fit into byt_per_blk */
+-			for (size = 0; bh != NULL && size < blksize_size[tapeblock_major][ti->blk_minor];) {
+-				ccw++;
+-				ccw->flags = CCW_FLAG_DC;
+-				ccw->cmd_code = READ_FORWARD;
+-				ccw->count = bh->b_size;
+-				set_normalized_cda (ccw, __pa (bh->b_data));
+-				size += bh->b_size;
+-				bh = bh->b_reqnext;
+-			}
+-			if (size != blksize_size[tapeblock_major][ti->blk_minor]) {
+-				PRINT_WARN ("Cannot fulfill small request %d vs. %d (%ld sects)\n",
+-					    size,
+-					    blksize_size[tapeblock_major][ti->blk_minor],
+-					    req->nr_sectors);
+-				kfree(data);
+-				tape_free_request (cqr);
+-				return NULL;
+-			}
+-		}
+-	}
+-	ccw -> flags &= ~(CCW_FLAG_DC);
+-	ccw -> flags |= (CCW_FLAG_CC);
+-	ccw++;
+-	ccw->cmd_code = NOP;
+-	ccw->flags = 0;
+-	ccw->count = 0;
+-	ccw->cda = (unsigned long) (&(ccw->cmd_code));
+-	ti->kernbuf = data;
+-	ti->userbuf = NULL;
+-	tapestate_set (ti, TS_BLOCK_INIT);
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xBREDccwg");
+-#endif /* TAPE_DEBUG */
+-	return cqr;
+-}
+-void tape34xx_free_bread (ccw_req_t* cqr,struct _tape_info_t* ti) {
+-    ccw1_t* ccw;
+-    for (ccw=(ccw1_t*)cqr->cpaddr;(ccw->flags & CCW_FLAG_CC)||(ccw->flags & CCW_FLAG_DC);ccw++) 
+-	if ((ccw->cmd_code == MODE_SET_DB) ||
+-	    (ccw->cmd_code == LOCATE) ||
+-	    (ccw->cmd_code == READ_FORWARD))
+-	    clear_normalized_cda(ccw);
+-    tape_free_request(cqr);
+-    kfree(ti->kernbuf);
+-    ti->kernbuf=NULL;
+-}
+-
+-/* event handlers */
+-void
+-tape34xx_default_handler (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-    debug_text_event (tape_debug_area,6,"xdefhandle");
+-#endif /* TAPE_DEBUG */
+-	PRINT_ERR ("TAPE34XX: An unexpected Unit Check occurred.\n");
+-	PRINT_ERR ("TAPE34XX: Please read Documentation/s390/TAPE and report it!\n");
+-	PRINT_ERR ("TAPE34XX: Current state is: %s",
+-		   (((tapestate_get (ti) < TS_SIZE) && (tapestate_get (ti) >= 0)) ?
+-		    state_verbose[tapestate_get (ti)] : "->UNKNOWN STATE<-"));
+-	tape_dump_sense (&ti->devstat);
+-	ti->rc = -EIO;
+-	ti->wanna_wakeup=1;
+-        switch (tapestate_get(ti)) {
+-        case TS_REW_RELEASE_INIT:
+-            tapestate_set(ti,TS_FAILED);
+-            wake_up (&ti->wq);
+-            break;
+-        case TS_BLOCK_INIT:
+-            tapestate_set(ti,TS_FAILED);
+-            schedule_tapeblock_exec_IO(ti);
+-            break;
+-        default:
+-            tapestate_set(ti,TS_FAILED);
+-            wake_up_interruptible (&ti->wq);
+-        }      
+-}
+-
+-void
+-tape34xx_unexpect_uchk_handler (tape_info_t * ti)
+-{
+-	if ((ti->devstat.ii.sense.data[0] == 0x40) &&
+-	    (ti->devstat.ii.sense.data[1] == 0x40) &&
+-	    (ti->devstat.ii.sense.data[3] == 0x43)) {
+-		// no tape in the drive
+-	        PRINT_INFO ("Drive %d not ready. No volume loaded.\n", ti->rew_minor / 2);
+-#ifdef TAPE_DEBUG
+-	        debug_text_event (tape_debug_area,3,"xuuh nomed");
+-#endif /* TAPE_DEBUG */
+-		tapestate_set (ti, TS_FAILED);
+-		ti->rc = -ENOMEDIUM;
+-		ti->wanna_wakeup=1;
+-		wake_up_interruptible (&ti->wq);
+-	} else if ((ti->devstat.ii.sense.data[0] == 0x42) &&
+-		   (ti->devstat.ii.sense.data[1] == 0x44) &&
+-		   (ti->devstat.ii.sense.data[3] == 0x3b)) {
+-       	        PRINT_INFO ("Media in drive %d was changed!\n",
+-			    ti->rew_minor / 2);
+-#ifdef TAPE_DEBUG
+-		debug_text_event (tape_debug_area,3,"xuuh medchg");
+-#endif
+-		/* nothing to do. chan end & dev end will be reported when io is finished */
+-	} else {
+-#ifdef TAPE_DEBUG
+-	        debug_text_event (tape_debug_area,3,"xuuh unexp");
+-	        debug_text_event (tape_debug_area,3,"state:");
+-	        debug_text_event (tape_debug_area,3,((tapestate_get (ti) < TS_SIZE) && 
+-						     (tapestate_get (ti) >= 0)) ?
+-				  state_verbose[tapestate_get (ti)] : 
+-				  "TS UNKNOWN");
+-#endif /* TAPE_DEBUG */
+-		tape34xx_default_handler (ti);
+-	}
+-}
+-
+-void
+-tape34xx_unused_done (tape_info_t * ti)
+-{
+-    if (ti->medium_is_unloaded) {
+-	// A medium was inserted in the drive!
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xuui med");
+-#endif /* TAPE_DEBUG */
+-	PRINT_WARN ("A medium was inserted into the tape.\n");
+-	ti->medium_is_unloaded=0;
+-    } else {
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,3,"unsol.irq!");
+-        debug_text_event (tape_debug_area,3,"dev end");
+-        debug_int_exception (tape_debug_area,3,ti->devinfo.irq);
+-#endif /* TAPE_DEBUG */
+-	PRINT_WARN ("Unsolicited IRQ (Device End) caught in unused state.\n");
+-	tape_dump_sense (&ti->devstat);
+-    }
+-}
+-
+-
+-void
+-tape34xx_idle_done (tape_info_t * ti)
+-{
+-    if (ti->medium_is_unloaded) {
+-	// A medium was inserted in the drive!
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"xuud med");
+-#endif /* TAPE_DEBUG */
+-	PRINT_WARN ("A medium was inserted into the tape.\n");
+-	ti->medium_is_unloaded=0;
+-	wake_up_interruptible (&ti->wq);
+-    } else {
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,3,"unsol.irq!");
+-        debug_text_event (tape_debug_area,3,"dev end");
+-        debug_int_exception (tape_debug_area,3,ti->devinfo.irq);
+-#endif /* TAPE_DEBUG */
+-	PRINT_WARN ("Unsolicited IRQ (Device End) caught in idle state.\n");
+-	tape_dump_sense (&ti->devstat);
+-    }
+-}
+-
+-void
+-tape34xx_block_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"x:bREQdone");
+-#endif /* TAPE_DEBUG */
+-	tapestate_set(ti,TS_DONE);
+-	schedule_tapeblock_exec_IO(ti);
+-}
+-
+-void
+-tape34xx_bsf_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"bsf done");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	ti->wanna_wakeup=1;
+-	wake_up_interruptible (&ti->wq);
+-}
+-
+-void
+-tape34xx_dse_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"dse done");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	ti->wanna_wakeup=1;
+-	wake_up_interruptible (&ti->wq);
+-}
+-
+-void
+-tape34xx_fsf_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"fsf done");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	ti->wanna_wakeup=1;
+-	wake_up_interruptible (&ti->wq);
+-}
+-
+-void
+-tape34xx_fsb_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"fsb done");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	ti->wanna_wakeup=1;
+-	wake_up_interruptible (&ti->wq);
+-}
+-
+-void
+-tape34xx_bsb_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"bsb done");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	ti->wanna_wakeup=1;
+-	wake_up (&ti->wq);
+-}
+-
+-void
+-tape34xx_lbl_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"lbl done");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	//s390irq_spin_unlock(tape->devinfo.irq);
+-	ti->wanna_wakeup=1;
+-	wake_up (&ti->wq);
+-}
+-
+-void
+-tape34xx_nop_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"nop done..");
+-        debug_text_exception (tape_debug_area,6,"or rew/rel");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	//s390irq_spin_unlock(tape->devinfo.irq);
+-	ti->wanna_wakeup=1;
+-	wake_up (&ti->wq);
+-}
+-
+-void
+-tape34xx_rfo_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"rfo done");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	ti->wanna_wakeup=1;
+-	wake_up (&ti->wq);
+-}
+-
+-void
+-tape34xx_rbi_init_done (tape_info_t * ti)
+-{
+-	__u8 *data;
+-#ifdef TAPE_DEBUG
+-	int i;
+-#endif
+-	tapestate_set (ti, TS_FAILED);
+-	data = ti->kernbuf;
+-	ti->rc = data[3];
+-	ti->rc += 256 * data[2];
+-	ti->rc += 65536 * (data[1] & 0x3F);
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"rbi done");
+-        debug_text_event (tape_debug_area,6,"data:");
+-	for (i=0;i<8;i++)
+-	    debug_int_event (tape_debug_area,6,data[i]);
+-#endif
+-	ti->wanna_wakeup=1;
+-	wake_up_interruptible (&ti->wq);
+-}
+-
+-void
+-tape34xx_rew_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"rew done");
+-#endif
+-	//BH: use irqsave
+-	//s390irq_spin_lock(tape->devinfo.irq);
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	//s390irq_spin_unlock(tape->devinfo.irq);
+-	ti->wanna_wakeup=1;
+-	wake_up_interruptible (&ti->wq);
+-}
+-
+-void
+-tape34xx_rew_release_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"rewR done");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	//s390irq_spin_unlock(tape->devinfo.irq);
+-	ti->wanna_wakeup=1;
+-	wake_up (&ti->wq);
+-}
+-
+-void
+-tape34xx_run_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"rew done");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	ti->wanna_wakeup=1;
+-	wake_up_interruptible (&ti->wq);
+-}
+-
+-void
+-tape34xx_wri_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"wri done");
+-#endif
+-	//BH: use irqsave
+-	//s390irq_spin_lock(ti->devinfo.irq);
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	//s390irq_spin_unlock(ti->devinfo.irq);
+-	ti->wanna_wakeup=1;
+-	wake_up_interruptible (&ti->wq);
+-}
+-
+-void
+-tape34xx_wtm_init_done (tape_info_t * ti)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,3,"wtm done");
+-#endif
+-	tapestate_set (ti, TS_DONE);
+-	ti->rc = 0;
+-	ti->wanna_wakeup=1;
+-	wake_up_interruptible (&ti->wq);
+-}
+-
+-/* This function analyses the tape's sense-data in case of a unit-check. If possible,
+-   it tries to recover from the error. Else the user is informed about the problem. */
+-void
+-tape34xx_error_recovery (tape_info_t* ti)
+-{
+-    __u8* sense=ti->devstat.ii.sense.data;
+-    int inhibit_cu_recovery=0;
+-    int cu_type=ti->discipline->cu_type;
+-    if ((((tape34xx_disc_data_t *) ti->discdata)->modeset_byte)&0x80) inhibit_cu_recovery=1;
+-    if (tapestate_get(ti)==TS_BLOCK_INIT) {
+-	// no recovery for block device, bottom half will retry...
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    }
+-    if (sense[0]&SENSE_COMMAND_REJECT)
+-	switch (tapestate_get(ti)) {
+-	case TS_BLOCK_INIT:
+-	case TS_DSE_INIT:
+-	case TS_EGA_INIT:
+-	case TS_WRI_INIT:
+-	case TS_WTM_INIT:
+-	    if (sense[1]&SENSE_WRITE_PROTECT) {
+-		// trying to write, but medium is write protected
+-		tape34xx_error_recovery_has_failed(ti,EACCES);
+-		return;
+-	    }
+-	default:
+-	    tape34xx_error_recovery_HWBUG(ti,1);
+-	    return;
+-	}
+-    // special cases for various tape-states when reaching end of recorded area
+-    if (((sense[0]==0x08) || (sense[0]==0x10) || (sense[0]==0x12)) &&
+-	((sense[1]==0x40) || (sense[1]==0x0c)))
+-	switch (tapestate_get(ti)) {
+-	case TS_FSF_INIT:
+-	    // Trying to seek beyond end of recorded area
+-	    tape34xx_error_recovery_has_failed(ti,EIO);
+-	    return;
+-	case TS_LBL_INIT:
+-	    // Block could not be located.
+-	    tape34xx_error_recovery_has_failed(ti,EIO);
+-	    return;
+-	case TS_RFO_INIT:
+-	    // Try to read beyond end of recorded area -> 0 bytes read
+-	    tape34xx_error_recovery_has_failed(ti,0);
+-	    return;
+-	}
+-    // Sensing special bits
+-    if (sense[0]&SENSE_BUS_OUT_CHECK) {
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    }
+-    if (sense[0]&SENSE_DATA_CHECK) {
+-	// hardware failure, damaged tape or improper operating conditions
+-	switch (sense[3]) {
+-	case 0x23:
+-	    // a read data check occurred
+-	    if ((sense[2]&SENSE_TAPE_SYNC_MODE) ||
+-		(inhibit_cu_recovery)) {
+-		// data check is not permanent, may be recovered. 
+-		// We always use async-mode with cu-recovery, so this should *never* happen.
+-		tape34xx_error_recovery_HWBUG(ti,2);
+-		return;
+-	    } else {
+-		// data check is permanent, CU recovery has failed
+-		PRINT_WARN("Permanent read error, recovery failed!\n");
+-		tape34xx_error_recovery_has_failed(ti,EIO);
+-		return;
+-	    }
+-	case 0x25:
+-	    // a write data check occurred
+-	    if ((sense[2]&SENSE_TAPE_SYNC_MODE) ||
+-		(inhibit_cu_recovery)) {
+-		// data check is not permanent, may be recovered.
+-		// We always use async-mode with cu-recovery, so this should *never* happen.
+-		tape34xx_error_recovery_HWBUG(ti,3);
+-		return;
+-	    } else {
+-		// data check is permanent, cu-recovery has failed
+-		PRINT_WARN("Permanent write error, recovery failed!\n");
+-		tape34xx_error_recovery_has_failed(ti,EIO);
+-		return;
+-	    }
+-	case 0x26:
+-	    // Data Check (read opposite) occurred. We'll recover this.
+-	    tape34xx_error_recovery_read_opposite(ti);
+-	    return;
+-	case 0x28:
+-	    // The ID-Mark at the beginning of the tape could not be written. This is fatal, we'll report and exit.
+-	    PRINT_WARN("ID-Mark could not be written. Check your hardware!\n");
+-	    tape34xx_error_recovery_has_failed(ti,EIO);
+-	    return;
+-	case 0x31:
+-	    // Tape void. Tried to read beyond end of device. We'll report and exit.
+-	    PRINT_WARN("Try to read beyond end of recorded area!\n");
+-	    tape34xx_error_recovery_has_failed(ti,ENOSPC);
+-	    return;
+-	case 0x41:
+-	    // Record sequence error. cu detected incorrect block-id sequence on tape. We'll report and exit.
+-	    PRINT_WARN("Illegal block-id sequence found!\n");
+-	    tape34xx_error_recovery_has_failed(ti,EIO);
+-	    return;
+-	    default:
+-	    // well, all data checks for 3480 should result in one of the above erpa-codes. if not -> bug
+-	    // On 3490, other data-check conditions do exist.
+-		if (cu_type==0x3480) {
+-		    tape34xx_error_recovery_HWBUG(ti,4);
+-		    return;
+-		}
+-	}
+-    }
+-    if (sense[0]&SENSE_OVERRUN) {
+-	// A data overrun between cu and drive occurred. The channel speed is to slow! We'll report this and exit!
+-	switch (sense[3]) {
+-	case 0x40: // overrun error
+-	    PRINT_WARN ("Data overrun error between control-unit and drive. Use a faster channel connection, if possible! \n");
+-	    tape34xx_error_recovery_has_failed(ti,EIO);
+-	    return;
+-	default:
+-	    // Overrun bit is set, but erpa does not show overrun error. This is a bug.
+-	    tape34xx_error_recovery_HWBUG(ti,5);
+-	    return;
+-	}
+-    }
+-    if (sense[1]&SENSE_RECORD_SEQUENCE_ERR) {
+-	switch (sense[3]) {
+-	case 0x41:
+-	    // Record sequence error. cu detected incorrect block-id sequence on tape. We'll report and exit.
+-	    PRINT_WARN("Illegal block-id sequence found!\n");
+-	    tape34xx_error_recovery_has_failed(ti,EIO);
+-	    return;
+-	default:
+-	    // Record sequence error bit is set, but erpa does not show record sequence error. This is a bug.
+-	    tape34xx_error_recovery_HWBUG(ti,6);
+-	    return;
+-	}
+-    }
+-    // Sensing erpa codes
+-    switch (sense[3]) {
+-    case 0x00:
+-	// Everything is fine, but we got a unit check. Report and ignore!
+-	PRINT_WARN ("Non-error sense was found. Unit-check will be ignored, expect errors...\n");
+-	return;
+-    case 0x21:
+-	// Data streaming not operational. Cu switches to interlock mode, we reissue the command.
+-	PRINT_WARN ("Data streaming not operational. Switching to interlock-mode! \n");
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    case 0x22:
+-	// Path equipment check. Might be drive adapter error, buffer error on the lower interface, internal path not useable, or error during cartridge load.
+-	// All of the above are not recoverable
+-	PRINT_WARN ("A path equipment check occurred. One of the following conditions occurred:\n");
+-	PRINT_WARN ("drive adapter error,buffer error on the lower interface, internal path not useable, error during cartridge load.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x23:
+-	// Read data check. Should have been be covered earlier -> Bug!
+-	tape34xx_error_recovery_HWBUG(ti,7);
+-	return;
+-    case 0x24:
+-	// Load display check. Load display was command was issued, but the drive is displaying a drive check message. Can be threated as "device end".
+-	tape34xx_error_recovery_succeded(ti);
+-	return;
+-    case 0x25:
+-	// Write data check. Should have been covered earlier -> Bug!
+-	tape34xx_error_recovery_HWBUG(ti,8);
+-	return;
+-    case 0x26:
+-	// Data check (read opposite). Should have been covered earlier -> Bug!
+-	tape34xx_error_recovery_HWBUG(ti,9);
+-	return;
+-    case 0x27:
+-	// Command reject. May indicate illegal channel program or buffer over/underrun. 
+-	// Since all channel programms are issued by this driver and ought be correct,
+-	// we assume a over/underrun situaltion and retry the channel program.
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    case 0x28:
+-	// Write id mark check. Should have beed covered earlier -> bug!
+-	tape34xx_error_recovery_HWBUG(ti,10);
+-	return;
+-    case 0x29:
+-	// Function incompatible. Either idrc is on but hardware not capable doing idrc 
+-	// or a perform subsystem func is issued and the cu is not online. Anyway, this 
+-	// cannot be recovered and is an I/O error.
+-	PRINT_WARN ("Function incompatible. Try to switch off idrc! \n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x2a:
+-	// Unsolicited environmental data. An internal counter overflows, we can ignore
+-	// this and reissue the cmd.
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    case 0x2b:
+-	// Environmental data present. Indicates either unload completed ok or read buffered 
+-	// log command completed ok. 
+-	if (tapestate_get(ti)==TS_RUN_INIT) {
+-	    // Rewind unload completed ok.
+-	    tape34xx_error_recovery_succeded(ti);
+-	    return;
+-	}
+-	// Since we do not issue read buffered log commands, this should never occur -> bug.
+-	tape34xx_error_recovery_HWBUG(ti,11);
+-	return;
+-    case 0x2c:
+-	// Permanent equipment check. cu has tried recovery, but did not succeed. This is an
+-	// I/O error.
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x2d:
+-	// Data security erase failure.
+-	if (tapestate_get(ti)==TS_DSE_INIT) {
+-	    // report an I/O error
+-	    tape34xx_error_recovery_has_failed(ti,EIO);
+-	    return;
+-	}
+-	// Data security erase failure, but no such command issued. This is a bug.
+-	tape34xx_error_recovery_HWBUG(ti,12);
+-	return;
+-    case 0x2e:
+-	// Not capable. This indicates either that the drive fails reading the format id mark
+-	// or that that format specified is not supported by the drive. We write a message and
+-	// return an I/O error.
+-	PRINT_WARN("Drive not capable processing the tape format!");
+-	tape34xx_error_recovery_has_failed(ti,EMEDIUMTYPE);
+-	return;
+-    case 0x2f:
+-	// This erpa is reserved. This is a bug.
+-	tape34xx_error_recovery_HWBUG(ti,13);
+-	return;
+-    case 0x30:
+-	// The medium is write protected, while trying to write on it. We'll report this.
+-	PRINT_WARN("Medium is write protected!\n");
+-	tape34xx_error_recovery_has_failed(ti,EACCES);
+-	return;
+-    case 0x31:
+-	// Tape void. Should have beed covered ealier -> bug
+-	tape34xx_error_recovery_HWBUG(ti,14);
+-	return;
+-    case 0x32:
+-	// Tension loss. We cannot recover this, it's an I/O error.
+-	PRINT_WARN("The drive lost tape tension.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x33:
+-	// Load Failure. The catridge was not inserted correctly or the tape is not threaded
+-	// correctly. We cannot recover this, the user has to reload the catridge.
+-	PRINT_WARN("Cartridge load failure. Reload the cartridge and try again.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x34:
+-	// Unload failure. The drive cannot maintain tape tension and control tape movement 
+-	// during an unload operation. 
+-	PRINT_WARN("Failure during cartridge unload. Please try manually.\n");
+-	if (tapestate_get(ti)!=TS_RUN_INIT) {
+-	    tape34xx_error_recovery_HWBUG(ti,15);
+-	    return;
+-	}
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x35:
+-	// Drive equipment check. One of the following:
+-	// - cu cannot recover from a drive detected error
+-	// - a check code message is displayed on drive message/load displays
+-	// - the cartridge loader does not respond correctly
+-	// - a failure occurs during an index, load, or unload cycle
+-	PRINT_WARN("Equipment check! Please check the drive and the cartridge loader.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x36:
+-	switch (cu_type) {
+-	case 0x3480:
+-	    // This erpa is reserved for 3480 -> BUG
+-	    tape34xx_error_recovery_HWBUG(ti,16);
+-	    return;
+-	case 0x3490:
+-	    // End of data. This is a permanent I/O error, which cannot be recovered.
+-	    // A read-type command has reached the end-of-data mark.
+-	    tape34xx_error_recovery_has_failed(ti,EIO);
+-	    return;
+-	}
+-    case 0x37:
+-	// Tape length error. The tape is shorter than reported in the beginning-of-tape data.
+-	PRINT_WARN("Tape length error.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x38:
+-	// Physical end of tape. A read/write operation reached the physical end of tape.
+-	if (tapestate_get(ti)==TS_WRI_INIT ||
+-	    tapestate_get(ti)==TS_DSE_INIT ||
+-	    tapestate_get(ti)==TS_EGA_INIT ||
+-	    tapestate_get(ti)==TS_WTM_INIT){
+- 	    tape34xx_error_recovery_has_failed(ti,ENOSPC);
+-	} else {
+-	    tape34xx_error_recovery_has_failed(ti,EIO);
+-	}
+-	return;
+-    case 0x39:
+-	// Backward at BOT. The drive is at BOT and is requestet to move backward.
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x3a:
+-	// Drive switched not ready, but the command needs the drive to be ready.
+-	PRINT_WARN("Drive not ready. Turn the ready/not ready switch to ready position and try again.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x3b:
+-	// Manual rewind or unload. This causes an I/O error.
+-	PRINT_WARN("Medium was rewound or unloaded manually. Expect errors! Please do only use the mtoffl and mtrew ioctl to unload tapes or rewind tapes.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x3c:
+-    case 0x3d:
+-    case 0x3e:
+-    case 0x3f:
+-	// These erpas are reserved -> BUG
+-	tape34xx_error_recovery_HWBUG(ti,17);
+-	return;
+-    case 0x40:
+-	// Overrun error. This should have been covered earlier -> bug.
+-	tape34xx_error_recovery_HWBUG(ti,18);
+-	return;
+-    case 0x41:
+-	// Record sequence error. This should have been covered earlier -> bug.
+-	tape34xx_error_recovery_HWBUG(ti,19);
+-	return;
+-    case 0x42:
+-	// Degraded mode. A condition that can cause degraded performace is detected.
+-	PRINT_WARN("Subsystem is running in degraded mode. This may compromise your performace.\n");
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    case 0x43:
+-	// Drive not ready. Probably swith the ready/not ready switch to ready?
+-	PRINT_WARN("The drive is not ready. Maybe no medium in?\n");
+-	tape34xx_error_recovery_has_failed(ti,ENOMEDIUM);
+-	return;
+-    case 0x44:
+-	// Locate Block unsuccessfull. We'll report this.
+-	if ((tapestate_get(ti)!=TS_BLOCK_INIT) &&
+-	    (tapestate_get(ti)!=TS_LBL_INIT)) {
+-	    tape34xx_error_recovery_HWBUG(ti,20); // No locate block was issued...
+-	    return;
+-	}
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x45:
+-	// The drive is assigned elsewhere [to a different channel path/computer].
+-	PRINT_WARN("The drive is assigned elsewhere.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x46:
+-	// Drive not online. Drive may be switched offline, the power supply may be switched off 
+-	// or the drive address may not be set correctly.
+-	PRINT_WARN("The drive is not online.");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x47:
+-	// Volume fenced. cu reports volume integrity is lost! 
+-	PRINT_WARN("Volume fenced. The volume integrity is lost! \n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x48:
+-	// Log sense data and retry request. We'll do so...
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    case 0x49:
+-	// Bus out check. A parity check error on the bus was found.	PRINT_WARN("Bus out check. A data transfer over the bus was corrupted.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x4a:
+-	// Control unit erp failed. We'll report this.
+-	PRINT_WARN("The control unit failed recovering an I/O error.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x4b:
+-	// Cu and drive incompatible. The drive requests micro-program patches, which are not available on the cu.
+-	PRINT_WARN("The drive needs microprogram patches from the control unit, which are not available.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x4c:
+-	// Recovered Check-One failure. Cu develops a hardware error, but is able to recover. We'll reissue the command.
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    case 0x4d:
+-	switch (cu_type) {
+-	case 0x3480:
+-	    // This erpa is reserved for 3480 -> bug
+-      	    tape34xx_error_recovery_HWBUG(ti,21);
+-	    return;
+-	case 0x3490:
+-	    // Resetting event received. Since the driver does not support resetting event recovery
+-	    // (which has to be handled by the I/O Layer), we'll report and retry our command.
+-	    tape34xx_error_recovery_do_retry(ti);
+-	    return;
+-	}
+-    case 0x4e:
+-	switch (cu_type) {
+-	case 0x3480:
+-	    // This erpa is reserved for 3480 -> bug.
+-	    tape34xx_error_recovery_HWBUG(ti,22);
+-	    return;
+-	case 0x3490:
+-	    // Maximum block size exeeded. This indicates, that the block to be written is larger
+-	    // than allowed for buffered mode. We'll report this...
+-	    PRINT_WARN("Maximum block size for buffered mode exceeded.\n");
+-	    tape34xx_error_recovery_has_failed(ti,ENOBUFS);
+-	    return;
+-	}
+-    case 0x4f:
+-	// These erpas are reserved -> bug
+-	tape34xx_error_recovery_HWBUG(ti,23);
+-	return;
+-    case 0x50:
+-	// Read buffered log (Overflow). Cu is running in extended beffered log mode, and a counter overflows.
+-	// This should never happen, since we're never running in extended buffered log mode -> bug.
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    case 0x51:
+-	// Read buffered log (EOV). EOF processing occurs while the cu is in extended buffered log mode.
+-	// This should never happen, since we're never running in extended buffered log mode -> bug.
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    case 0x52:
+-	// End of Volume complete. Rewind unload completed ok. We'll report to the user...
+-	if (tapestate_get(ti)!=TS_RUN_INIT) {
+-	    tape34xx_error_recovery_HWBUG(ti,24);
+-	    return;
+-	}
+-	tape34xx_error_recovery_succeded(ti);
+-	return;
+-    case 0x53:
+-	// Global command intercept. We'll have to reissue our command.
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    case 0x54:
+-	// Channel interface recovery (temporary). This can be recovered by reissuing the command.
+-	tape34xx_error_recovery_do_retry(ti);
+-	return;
+-    case 0x55:
+-	// Channel interface recovery (permanent). This cannot be recovered, we'll inform the user.
+-	PRINT_WARN("A permanent channel interface error occurred.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x56:
+-	// Channel protocol error. This cannot be recovered.
+-	PRINT_WARN("A channel protocol error occurred.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x57:
+-	switch (cu_type) {
+-	case 0x3480:
+-	    // Attention intercept. We have to reissue the command.
+-	    PRINT_WARN("An attention intercept occurred, which will be recovered.\n");
+-	    tape34xx_error_recovery_do_retry(ti);
+-	    return;
+-	case 0x3490:
+-	    // Global status intercept. We have to reissue the command.
+-	    PRINT_WARN("An global status intercept was received, which will be recovered.\n");
+-	    tape34xx_error_recovery_do_retry(ti);
+-	    return;
+-	}
+-    case 0x58:
+-    case 0x59:
+-	// These erpas are reserved -> bug.
+-	tape34xx_error_recovery_HWBUG(ti,25);
+-	return;
+-    case 0x5a:
+-	// Tape length incompatible. The tape inserted is too long, 
+-	// which could cause damage to the tape or the drive.
+-	PRINT_WARN("Tape length incompatible [should be IBM Cartridge System Tape]. May cause damage to drive or tape.n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x5b:
+-	// Format 3480 XF incompatible
+-	if (sense[1]&SENSE_BEGINNING_OF_TAPE) {
+-	    // Everything is fine. The tape will be overwritten in a different format.
+-	    tape34xx_error_recovery_do_retry(ti);
+-	    return;
+-	}
+-	PRINT_WARN("Tape format is incompatible to the drive, which writes 3480-2 XF.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x5c:
+-	// Format 3480-2 XF incompatible
+-	PRINT_WARN("Tape format is incompatible to the drive. The drive cannot access 3480-2 XF volumes.\n");
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	return;
+-    case 0x5d:
+-	// Tape length violation. 
+-	PRINT_WARN("Tape length violation [should be IBM Enhanced Capacity Cartridge System Tape]. May cause damage to drive or tape.\n");
+-	tape34xx_error_recovery_has_failed(ti,EMEDIUMTYPE);
+-	return;
+-    case 0x5e:
+-	// Compaction algorithm incompatible.
+-	PRINT_WARN("The volume is recorded using an incompatible compaction algorith, which is not supported by the control unit.\n");
+-	tape34xx_error_recovery_has_failed(ti,EMEDIUMTYPE);
+-	return;
+-    default:
+-	// Reserved erpas -> bug
+-	tape34xx_error_recovery_HWBUG(ti,26);
+-	return;
+-    }
+-}
+-
+-void tape34xx_error_recovery_has_failed (tape_info_t* ti,int error_id) {
+-#ifdef TAPE_DEBUG
+-    debug_text_event (tape_debug_area,3,"xerp fail");
+-    debug_text_event (tape_debug_area,3,(((tapestate_get (ti) < TS_SIZE) && 
+-		      (tapestate_get (ti) >= 0)) ?
+-	state_verbose[tapestate_get (ti)] : "UNKNOWN"));
+-#endif
+-    if ((tapestate_get(ti)!=TS_UNUSED) && (tapestate_get(ti)!=TS_IDLE)) {
+-	tape_dump_sense(&ti->devstat);
+-	ti->rc = -error_id;
+-	ti->wanna_wakeup=1;
+-	switch (tapestate_get(ti)) {
+-	case TS_REW_RELEASE_INIT:
+-	case TS_RFO_INIT:
+-	case TS_RBA_INIT:
+-	    tapestate_set(ti,TS_FAILED);
+-	    wake_up (&ti->wq);
+-	    break;
+-	case TS_BLOCK_INIT:
+-	    tapestate_set(ti,TS_FAILED);
+-	    schedule_tapeblock_exec_IO(ti);
+-	    break;
+-	default:
+-	    tapestate_set(ti,TS_FAILED);
+-	    wake_up_interruptible (&ti->wq);
+-	}
+-    } else {
+-	PRINT_WARN("Recieved an unsolicited IRQ.\n");
+-	tape_dump_sense(&ti->devstat);
+-    }
+-}    
+-
+-void tape34xx_error_recovery_succeded(tape_info_t* ti) {
+-#ifdef TAPE_DEBUG
+-    debug_text_event (tape_debug_area,3,"xerp done");
+-    debug_text_event (tape_debug_area,3,(((tapestate_get (ti) < TS_SIZE) && 
+-		      (tapestate_get (ti) >= 0)) ?
+-	state_verbose[tapestate_get (ti)] : "UNKNOWN"));
+-#endif
+-    if ((tapestate_get(ti)!=TS_UNUSED) && (tapestate_get(ti)!=TS_DONE)) {
+-	tapestate_event (ti, TE_DONE);
+-    } else {
+-	PRINT_WARN("Recieved an unsolicited IRQ.\n");
+-	tape_dump_sense(&ti->devstat);
+-    }
+-}
+-
+-void tape34xx_error_recovery_do_retry(tape_info_t* ti) {
+-#ifdef TAPE_DEBUG
+-    debug_text_event (tape_debug_area,3,"xerp retr");
+-    debug_text_event (tape_debug_area,3,(((tapestate_get (ti) < TS_SIZE) && 
+-					  (tapestate_get (ti) >= 0)) ?
+-					 state_verbose[tapestate_get (ti)] : "UNKNOWN"));
+-#endif
+-    if ((tapestate_get(ti)!=TS_UNUSED) && (tapestate_get(ti)!=TS_IDLE)) {
+-	tape_dump_sense(&ti->devstat);
+-	while (do_IO (ti->devinfo.irq, ti->cqr->cpaddr, (unsigned long) ti->cqr, 0x00, ti->cqr->options));
+-    } else {
+-	PRINT_WARN("Recieved an unsolicited IRQ.\n");
+-	tape_dump_sense(&ti->devstat);
+-    }
+-}
+-    
+-void 
+-tape34xx_error_recovery_read_opposite (tape_info_t* ti) {
+-    switch (tapestate_get(ti)) {
+-    case TS_RFO_INIT:
+-	// We did read forward, but the data could not be read *correctly*.
+-	// We will read backward and then skip forward again.
+-	ti->cqr=tape34xx_read_opposite(ti,0);
+-	if (ti->cqr==NULL)
+-	    tape34xx_error_recovery_has_failed(ti,EIO);
+-	else
+-	    tape34xx_error_recovery_do_retry(ti);
+-	break;
+-    case TS_RBA_INIT:
+-	// We tried to read forward and backward, but hat no success -> failed.
+-	tape34xx_error_recovery_has_failed(ti,EIO);
+-	break;
+-    case TS_BLOCK_INIT:
+-	tape34xx_error_recovery_do_retry(ti);
+-	break;
+-    default:
+-	PRINT_WARN("read_opposite_recovery_called_with_state:%s\n",
+-		   (((tapestate_get (ti) < TS_SIZE) && 
+-		     (tapestate_get (ti) >= 0)) ?
+-		    state_verbose[tapestate_get (ti)] : "UNKNOWN"));
+-    }
+-}
+-
+-void 
+-tape34xx_error_recovery_HWBUG (tape_info_t* ti,int condno) {
+-    devstat_t* stat=&ti->devstat;
+-    PRINT_WARN("An unexpected condition #%d was caught in tape error recovery.\n",condno);
+-    PRINT_WARN("Please report this incident.\n");
+-    PRINT_WARN("State of the tape:%s\n",
+-	       (((tapestate_get (ti) < TS_SIZE) && 
+-		 (tapestate_get (ti) >= 0)) ?
+-		state_verbose[tapestate_get (ti)] : "UNKNOWN"));
+-    PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X "
+-		" %02X%02X%02X%02X %02X%02X%02X%02X \n",
+-		stat->ii.sense.data[0], stat->ii.sense.data[1],
+-		stat->ii.sense.data[2], stat->ii.sense.data[3],
+-		stat->ii.sense.data[4], stat->ii.sense.data[5],
+-		stat->ii.sense.data[6], stat->ii.sense.data[7],
+-		stat->ii.sense.data[8], stat->ii.sense.data[9],
+-		stat->ii.sense.data[10], stat->ii.sense.data[11],
+-		stat->ii.sense.data[12], stat->ii.sense.data[13],
+-		stat->ii.sense.data[14], stat->ii.sense.data[15]);
+-    PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X "
+-		" %02X%02X%02X%02X %02X%02X%02X%02X \n",
+-		stat->ii.sense.data[16], stat->ii.sense.data[17],
+-		stat->ii.sense.data[18], stat->ii.sense.data[19],
+-		stat->ii.sense.data[20], stat->ii.sense.data[21],
+-		stat->ii.sense.data[22], stat->ii.sense.data[23],
+-		stat->ii.sense.data[24], stat->ii.sense.data[25],
+-		stat->ii.sense.data[26], stat->ii.sense.data[27],
+-		stat->ii.sense.data[28], stat->ii.sense.data[29],
+-		stat->ii.sense.data[30], stat->ii.sense.data[31]);
+-    tape34xx_error_recovery_has_failed(ti,EIO);
+-}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape34xx.h drivers/s390/char/tape34xx.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape34xx.h	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tape34xx.h	2006-02-12 12:47:23.000000000 -0700
+@@ -1,183 +0,0 @@
+-
+-/***************************************************************************
+- *
+- *  drivers/s390/char/tape34xx.h
+- *    common tape device discipline for 34xx tapes.
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s): Carsten Otte <cotte at de.ibm.com>
+- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *
+- ****************************************************************************
+- */
+-
+-#ifndef _TAPE34XX_H
+-
+-#define _TAPE34XX_H
+-
+-/*
+- * The CCW commands for the Tape type of command.
+- */
+-
+-#define         INVALID_00              0x00    /* Invalid cmd      */
+-#define         BACKSPACEBLOCK          0x27    /* Back Space block */
+-#define         BACKSPACEFILE           0x2f    /* Back Space file */
+-#define         DATA_SEC_ERASE          0x97    /* Data security erase */
+-#define         ERASE_GAP               0x17    /* Erase Gap */
+-#define         FORSPACEBLOCK           0x37    /* Forward space block */
+-#define         FORSPACEFILE            0x3F    /* Forward Space file */
+-#define         FORCE_STREAM_CNT        0xEB    /* Forced streaming count #   */
+-#define         NOP                     0x03    /* No operation  */
+-#define         READ_FORWARD            0x02    /* Read forward */
+-#define         REWIND                  0x07    /* Rewind */
+-#define         REWIND_UNLOAD           0x0F    /* Rewind and Unload */
+-#define         SENSE                   0x04    /* Sense */
+-#define         NEW_MODE_SET            0xEB    /* Guess it is Mode set */
+-#define         WRITE_CMD               0x01    /* Write */
+-#define         WRITETAPEMARK           0x1F    /* Write Tape Mark */
+-
+-#define         ASSIGN                  0xB7    /* 3420 REJECT,3480 OK  */
+-#define         CONTROL_ACCESS          0xE3    /* Set high speed */
+-#define         DIAG_MODE_SET           0x0B    /* 3420 NOP, 3480 REJECT*/
+-#define         LOAD_DISPLAY            0x9F    /* 3420 REJECT,3480 OK  */
+-#define         LOCATE                  0x4F    /* 3420 REJ, 3480 NOP   */
+-#define         LOOP_WRITE_TO_READ      0x8B    /* 3480 REJECT        */
+-#define         MODE_SET_DB             0xDB    /* 3420 REJECT,3480 OK  */
+-#define         MODE_SET_C3             0xC3    /* for 3420                */
+-#define         MODE_SET_CB             0xCB    /* for 3420                */
+-#define         MODE_SET_D3             0xD3    /* for 3420                */
+-#define         READ_BACKWARD           0x0C    /*                      */
+-#define         READ_BLOCK_ID           0x22    /* 3420 REJECT,3480 OK  */
+-#define         READ_BUFFER             0x12    /* 3420 REJECT,3480 OK  */
+-#define         READ_BUFF_LOG           0x24    /* 3420 REJECT,3480 OK  */
+-#define         RELEASE                 0xD4    /* 3420 NOP, 3480 REJECT*/
+-#define         REQ_TRK_IN_ERROR        0x1B    /* 3420 NOP, 3480 REJECT*/
+-#define         RESERVE                 0xF4    /* 3420 NOP, 3480 REJECT*/
+-#define         SENSE_GROUP_ID          0x34    /* 3420 REJECT,3480 OK  */
+-#define         SENSE_ID                0xE4    /* 3420 REJECT,3480 OK */
+-#define         READ_DEV_CHAR           0x64    /* Read device characteristics */
+-#define         SET_DIAGNOSE            0x4B    /* 3420 NOP, 3480 REJECT*/
+-#define         SET_GROUP_ID            0xAF    /* 3420 REJECT,3480 OK  */
+-#define         SET_TAPE_WRITE_IMMED    0xC3    /* for 3480                */
+-#define         SUSPEND                 0x5B    /* 3420 REJ, 3480 NOP   */
+-#define         SYNC                    0x43    /* Synchronize (flush buffer) */
+-#define         UNASSIGN                0xC7    /* 3420 REJECT,3480 OK  */
+-#define         PERF_SUBSYS_FUNC        0x77    /* 3490 CMD */
+-#define         READ_CONFIG_DATA        0xFA    /* 3490 CMD */
+-#define         READ_MESSAGE_ID         0x4E    /* 3490 CMD */
+-#define         READ_SUBSYS_DATA        0x3E    /* 3490 CMD */
+-#define         SET_INTERFACE_ID        0x73    /* 3490 CMD */
+-
+-#ifndef MIN
+-#define MIN(a,b)                ( (a) < (b) ? (a) : (b) )
+-#endif
+-
+-
+-#define BLOCKSIZE               4096            /* size of the tape rcds */
+-
+-#define COMMAND_CHAIN    CCW_FLAG_CC      /* redefine from irq.h */
+-#define CHANNEL_END      DEV_STAT_CHN_END /* redefine from irq.h */
+-#define DEVICE_END       DEV_STAT_DEV_END /* redefine from irq.h */
+-#define UNIT_CHECK       DEV_STAT_UNIT_CHECK  /* redefine from irq.h */
+-#define UNIT_EXCEPTION   DEV_STAT_UNIT_EXCEP  /* redefine from irq.h */
+-#define CONTROL_UNIT_END DEV_STAT_CU_END      /* redefine from irq.h */
+-#define INCORR_LEN       SCHN_STAT_INCORR_LEN /* redefine from irq.h */
+-
+-#define SENSE_COMMAND_REJECT        0x80
+-#define SENSE_INTERVENTION_REQUIRED 0x40
+-#define SENSE_BUS_OUT_CHECK         0x20
+-#define SENSE_EQUIPMENT_CHECK       0x10
+-#define SENSE_DATA_CHECK            0x08
+-#define SENSE_OVERRUN               0x04
+-#define SENSE_DEFERRED_UNIT_CHECK   0x02
+-#define SENSE_ASSIGNED_ELSEWHERE    0x01
+-
+-#define SENSE_LOCATE_FAILURE        0x80
+-#define SENSE_DRIVE_ONLINE          0x40
+-#define SENSE_RESERVED              0x20
+-#define SENSE_RECORD_SEQUENCE_ERR   0x10
+-#define SENSE_BEGINNING_OF_TAPE     0x08
+-#define SENSE_WRITE_MODE            0x04
+-#define SENSE_WRITE_PROTECT         0x02
+-#define SENSE_NOT_CAPABLE           0x01
+-
+-#define SENSE_CHANNEL_ADAPTER_CODE  0xE0
+-#define SENSE_CHANNEL_ADAPTER_LOC   0x10
+-#define SENSE_REPORTING_CU          0x08
+-#define SENSE_AUTOMATIC_LOADER      0x04
+-#define SENSE_TAPE_SYNC_MODE        0x02
+-#define SENSE_TAPE_POSITIONING      0x01
+-
+-typedef struct _tape34xx_disc_data_t {
+-    __u8 modeset_byte;
+-} tape34xx_disc_data_t  __attribute__ ((packed, aligned(8)));
+-
+-/* discipline functions */
+-int tape34xx_ioctl_overload (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
+-ccw_req_t * tape34xx_write_block (const char *data, size_t count, tape_info_t * ti);
+-void tape34xx_free_write_block (ccw_req_t * cqr, tape_info_t * ti);
+-ccw_req_t * tape34xx_read_block (const char *data, size_t count, tape_info_t * ti);
+-void  tape34xx_free_read_block (ccw_req_t * cqr, tape_info_t * ti);
+-void  tape34xx_clear_read_block (ccw_req_t * cqr, tape_info_t * ti);
+-ccw_req_t * tape34xx_mtfsf (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtbsf (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtfsr (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtbsr (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtweof (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtrew (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtoffl (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtnop (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtbsfm (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtfsfm (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mteom (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mterase (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtsetdensity (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtseek (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mttell (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtsetdrvbuffer (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtlock (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtunlock (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtload (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtunload (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtcompression (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtsetpart (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtmkpart (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtiocget (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_mtiocpos (tape_info_t * ti, int count);
+-ccw_req_t * tape34xx_bread (struct request *req, tape_info_t* ti,int tapeblock_major);
+-ccw_req_t * tape34xx_bwrite (struct request *req, tape_info_t* ti,int tapeblock_major);
+-void tape34xx_free_bread (ccw_req_t*,struct _tape_info_t*);
+-void tape34xx_free_bwrite (ccw_req_t*,struct _tape_info_t*);
+-
+-/* Event handlers */
+-void tape34xx_default_handler (tape_info_t * ti);
+-void tape34xx_unexpect_uchk_handler (tape_info_t * ti);
+-void tape34xx_unused_done(tape_info_t* ti);
+-void tape34xx_idle_done(tape_info_t* ti);
+-void tape34xx_block_done(tape_info_t* ti);
+-void tape34xx_bsf_init_done(tape_info_t* ti);
+-void tape34xx_dse_init_done(tape_info_t* ti);
+-void tape34xx_fsf_init_done(tape_info_t* ti);
+-void tape34xx_bsb_init_done(tape_info_t* ti);
+-void tape34xx_fsb_init_done(tape_info_t* ti);
+-void tape34xx_lbl_init_done(tape_info_t* ti);
+-void tape34xx_nop_init_done(tape_info_t* ti);
+-void tape34xx_rfo_init_done(tape_info_t* ti);
+-void tape34xx_rbi_init_done(tape_info_t* ti);
+-void tape34xx_rew_init_done(tape_info_t* ti);
+-void tape34xx_rew_release_init_done(tape_info_t* ti);
+-void tape34xx_run_init_done(tape_info_t* ti);
+-void tape34xx_wri_init_done(tape_info_t* ti);
+-void tape34xx_wtm_init_done(tape_info_t* ti);
+-
+-extern void schedule_tapeblock_exec_IO (tape_info_t *ti);
+-
+-// the error recovery stuff:
+-void tape34xx_error_recovery (tape_info_t* ti);
+-void tape34xx_error_recovery_has_failed (tape_info_t* ti,int error_id);
+-void tape34xx_error_recovery_succeded(tape_info_t* ti);
+-void tape34xx_error_recovery_do_retry(tape_info_t* ti);
+-void tape34xx_error_recovery_read_opposite (tape_info_t* ti);
+-void  tape34xx_error_recovery_HWBUG (tape_info_t* ti,int condno);
+-#endif // _TAPE34XX_H
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape3590.c drivers/s390/char/tape3590.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape3590.c	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tape3590.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1 +0,0 @@
+-// tbd
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape3590.h drivers/s390/char/tape3590.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape3590.h	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tape3590.h	2006-02-12 12:47:23.000000000 -0700
+@@ -1 +0,0 @@
+-// tbd
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape_block.c drivers/s390/char/tape_block.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape_block.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/tape_block.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,678 @@
 +/*
-+ * Find tape device by a device index.
++ *  drivers/s390/char/tape_block.c
++ *    block device frontend for tape device driver
++ *
++ *  S390 and zSeries version
++ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Carsten Otte <cotte at de.ibm.com>
++ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
++ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
 + */
-+struct tape_device *
-+tape_get_device(int devindex)
-+{
-+	struct list_head *l;
-+	struct tape_devmap *devmap;
-+	struct tape_device *device;
-+
-+	DBF_EVENT(5, "tape_get_device(%i)\n", devindex);
 +
-+	device = ERR_PTR(-ENODEV);
-+	spin_lock(&tape_devmap_lock);
-+	/* Find devmap for device with device number devno. */
-+	list_for_each(l, &tape_devreg_list) {
-+		devmap = list_entry(l, struct tape_devmap, list);
-+		if (devmap->devindex == devindex) {
-+			if (devmap->device != NULL) {
-+				device = tape_clone_device(devmap->device);
-+			}
-+			break;
-+		}
-+	}
-+	spin_unlock(&tape_devmap_lock);
-+	return device;
-+}
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/blkdev.h>
++#include <linux/blk.h>
++#include <linux/interrupt.h>
++#include <linux/cdrom.h>
 +
-+/*
-+ * Find tape handle by a devno.
-+ */
-+struct tape_device *
-+tape_get_device_by_devno(int devno)
-+{
-+	struct list_head	*l;
-+	struct tape_devmap	*devmap;
-+	struct tape_device	*device;
++#include <asm/debug.h>
++#include <asm/irq.h>
++#include <asm/s390dyn.h>
 +
-+	DBF_EVENT(5, "tape_get_device_by_devno(0x%04x)\n", devno);
++#define TAPE_DBF_AREA	tape_core_dbf
 +
-+	device = ERR_PTR(-ENODEV);
-+	spin_lock(&tape_devmap_lock);
++#include "tape.h"
++#include "tape_std.h"
 +
-+	list_for_each(l, &tape_devreg_list) {
-+		devmap = list_entry(l, struct tape_devmap, list);
-+		if(devmap->device != NULL && devmap->devno == devno) {
-+			device = tape_clone_device(devmap->device);
-+			break;
-+		}
-+	}
-+	spin_unlock(&tape_devmap_lock);
++#define PRINTK_HEADER "TBLOCK:"
 +
-+	return device;
-+}
++#define TAPEBLOCK_DEVFSMODE	0060644	/* brwxrw-rw- */
++#define TAPEBLOCK_MAX_SEC	100
++#define TAPEBLOCK_MIN_REQUEUE   3
 +
 +/*
-+ * Find tape handle by a device irq.
++ * file operation structure for tape block frontend
 + */
-+struct tape_device *
-+tape_get_device_by_irq(int irq)
-+{
-+	struct list_head	*l;
-+	struct tape_devmap	*devmap;
-+	struct tape_device	*device;
++static int tapeblock_open(struct inode *, struct file *);
++static int tapeblock_release(struct inode *, struct file *);
++static int tapeblock_ioctl(
++	struct inode *, struct file *, unsigned int, unsigned long);
 +
-+	DBF_EVENT(5, "tape_get_device_by_irq(0x%02x)\n", irq);
++static struct block_device_operations tapeblock_bdops = {
++	.owner		= THIS_MODULE,
++	.open		= tapeblock_open,
++	.release	= tapeblock_release,
++	.ioctl          = tapeblock_ioctl,
++};
 +
-+	device = ERR_PTR(-ENODEV);
-+	spin_lock(&tape_devmap_lock);
-+	/* Find devmap for device with device number devno. */
-+	list_for_each(l, &tape_devreg_list) {
-+		devmap = list_entry(l, struct tape_devmap, list);
-+		if (devmap->device != NULL && 
-+		    devmap->device->devinfo.irq == irq) {
-+			device = tape_clone_device(devmap->device);
-+			break;
-+		}
-+	}
-+	spin_unlock(&tape_devmap_lock);
-+	return device;
-+}
++int tapeblock_major = 0;
 +
 +/*
-+ * Decrease the reference counter of a devices structure. If the
-+ * reference counter reaches zero free the device structure and
-+ * wake up sleepers.
++ * Some helper inlines
 + */
-+void
-+tape_put_device(struct tape_device *device)
-+{
-+	int remain;
-+
-+	DBF_EVENT(4, "tape_put_device(%p)\n", device);
-+
-+	if ((remain = atomic_dec_return(&device->ref_count)) > 0) {
-+		DBF_EVENT(5, "remaining = %i\n", remain);
-+		return;
-+	}
-+
-+	/*
-+	 * Reference counter dropped to zero. This means
-+	 * that the device is deleted and the last user
-+	 * of the device structure is gone. That is what
-+	 * tape_delete_device is waiting for. Do a wake up.
-+	 */
-+	if(remain < 0) {
-+		PRINT_ERR("put device without reference\n");
-+		return;
-+	}
-+
-+	/*
-+	 * Free memory of a device structure.
-+	 */
-+	kfree(device->modeset_byte);
-+	kfree(device);
-+}
-+
-+void
-+tape_devmap_remove_device(struct tape_device *device) {
-+	struct tape_devmap *	devmap;
-+	struct list_head *	l;
-+	unsigned long		flags;
-+
-+	spin_lock_irqsave(&tape_devmap_lock, flags);
-+	list_for_each(l, &tape_devreg_list) {
-+		devmap = list_entry(l, struct tape_devmap, list);
-+
-+		if(device->devinfo.devno == devmap->devno) {
-+			devmap->device = NULL;
-+			tape_put_device(device);
-+			break;
-+		}
-+	}
-+	spin_unlock_irqrestore(&tape_devmap_lock, flags);
++static inline int tapeblock_size(int minor) {
++	return blk_size[tapeblock_major][minor];
 +}
-+
-+/*
-+ * Scan the device range for devices with matching cu_type, create
-+ * their device structures and enable them.
-+ */
-+void
-+tape_add_devices(struct tape_discipline *discipline)
-+{
-+	struct list_head *l;
-+	struct tape_devmap *devmap;
-+	struct tape_device *device;
-+
-+	/*
-+	 * Scan tape devices for matching cu type.
-+	 */
-+	list_for_each(l, &tape_devreg_list) {
-+		devmap = list_entry(l, struct tape_devmap, list);
-+		device = tape_create_device(devmap->devno);
-+		if (IS_ERR(device))
-+			continue;
-+
-+		if (device->devinfo.sid_data.cu_type == discipline->cu_type) {
-+			DBF_EVENT(4, "tape_add_devices(%p)\n", discipline);
-+			DBF_EVENT(4, "det irq:  %x\n", device->devinfo.irq);
-+			DBF_EVENT(4, "cu     :  %x\n", discipline->cu_type);
-+
-+			if(tape_enable_device(device, discipline) < 0) {
-+				devmap->device = NULL;
-+				tape_put_device(device);
-+			}
-+		} else {
-+			devmap->device = NULL;
-+			tape_put_device(device);
-+		}
-+		tape_put_device(device);
-+	}
-+	if (tape_autodetect)
-+		tape_add_disc_devreg(discipline);
++static inline int tapeblock_ssize(int minor) {
++	return blksize_size[tapeblock_major][minor];
 +}
-+
-+/*
-+ * Scan the device range for devices with matching cu_type, disable them
-+ * and remove their device structures.
-+ */
-+void
-+tape_remove_devices(struct tape_discipline *discipline)
-+{
-+	struct list_head *l;
-+	struct tape_devmap *devmap;
-+	struct tape_device *device;
-+
-+	if (tape_autodetect)
-+		tape_del_disc_devreg(discipline);
-+	/*
-+	 * Go through our tape info list and disable, deq and free
-+	 * all devices with matching discipline
-+	 */
-+	list_for_each(l, &tape_devreg_list) {
-+		devmap = list_entry(l, struct tape_devmap, list);
-+		device = devmap->device;
-+		if (device == NULL)
-+			continue;
-+		if (device->discipline == discipline) {
-+			tape_disable_device(device, 0);
-+			tape_put_device(device);
-+			devmap->device = NULL;
-+		}
-+	}
++static inline int tapeblock_hw_ssize(int minor) {
++	return hardsect_size[tapeblock_major][minor];
 +}
 +
 +/*
-+ * Auto detect tape devices.
++ * Post finished request.
 + */
-+void
-+tape_auto_detect(void)
++static inline void
++tapeblock_end_request(struct request *req, int uptodate)
 +{
-+	struct tape_device *device;
-+	struct tape_discipline *discipline;
-+	s390_dev_info_t dinfo;
-+	int irq, devno;
-+
-+	if (!tape_autodetect)
-+		return;
-+	for (irq = get_irq_first(); irq != -ENODEV; irq = get_irq_next(irq)) {
-+		/* Get device info block. */
-+		devno = get_devno_by_irq(irq);
-+		if (get_dev_info_by_irq(irq, &dinfo) < 0)
-+			continue;
-+		/* Search discipline with matching cu_type */
-+		discipline = tape_get_discipline(dinfo.sid_data.cu_type);
-+		if (discipline == NULL)
-+			continue;
-+		DBF_EVENT(4, "tape_auto_detect()\n");
-+		DBF_EVENT(4, "det irq:  %x\n", irq);
-+		DBF_EVENT(4, "cu     :  %x\n", dinfo.sid_data.cu_type);
-+		if (tape_add_range(dinfo.devno, dinfo.devno) == 0) {
-+			device = tape_create_device(devno);
-+			if (!IS_ERR(device)) {
-+				if(tape_enable_device(device, discipline) < 0)
-+					tape_devmap_remove_device(device);
-+				tape_put_device(device);
-+			}
-+		}
-+		tape_put_discipline(discipline);
-+	}
++	if (end_that_request_first(req, uptodate, "tBLK"))
++		BUG();
++	end_that_request_last(req);
 +}
 +
-+/*
-+ * Private task queue for oper/noper handling...
-+ */
-+static DECLARE_TASK_QUEUE(tape_cio_tasks);
-+
-+/*
-+ * Oper Handler is called from Ingo's I/O layer when a new tape device is
-+ * attached.
-+ */
 +static void
-+do_tape_oper_handler(void *data)
-+{
-+	struct {
-+		int			devno;
-+		int			cu_type;
-+		struct tq_struct	task;
-+	} *p;
-+	struct tape_device       *device;
-+	struct tape_discipline   *discipline;
-+	unsigned long		  flags;
-+
-+	p = (void *) data;
-+
-+	/*
-+	 * Handling the path revalidation scheme or common IO. Devices that
-+	 * were detected before will be reactivated.
-+	 */
-+	if(!IS_ERR(device = tape_get_device_by_devno(p->devno))) {
-+		spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
-+		if (!TAPE_NOACCESS(device)) {
-+			PRINT_ERR(
-+				"Oper handler for irq %d called, "
-+				"which is (still) internally used.\n",
-+				device->devinfo.irq);
-+		} else {
-+			DBF_EVENT(3,
-+				"T390(%04x): resume processing\n",
-+				p->devno);
-+			TAPE_CLEAR_STATE(device, TAPE_STATUS_NOACCESS);
-+			tape_schedule_bh(device);
-+		}
-+		spin_unlock_irqrestore(
-+			get_irq_lock(device->devinfo.irq), flags);
-+
-+		tape_put_device(device);
-+		kfree(p);
-+		return;
-+	}
-+
-+	/* If we get here device is NULL. */	
-+	if (tape_autodetect && tape_add_range(p->devno, p->devno) != 0) {
-+		kfree(p);
-+		return;
-+	}
-+
-+	/* Find discipline for this device. */
-+	discipline = tape_get_discipline(p->cu_type);
-+	if (discipline == NULL) {
-+		/* Strange. Should not happen. */
-+		kfree(p);
-+		return;
-+	}
-+
-+	device = tape_create_device(p->devno);
-+	if (IS_ERR(device)) {
-+		tape_put_discipline(discipline);
-+		kfree(p);
-+		return;
-+	}
-+	if(tape_enable_device(device, discipline) < 0)
-+		tape_devmap_remove_device(device);
-+	tape_put_device(device);
-+	tape_put_discipline(discipline);
-+	kfree(p);
-+}
-+
-+int
-+tape_oper_handler(int irq, devreg_t *devreg)
++__tapeblock_end_request(struct tape_request *ccw_req, void *data)
 +{
-+	struct {
-+		int			devno;
-+		int			cu_type;
-+		struct tq_struct	task;
-+	} *p;
-+	s390_dev_info_t dinfo;
-+	int rc;
-+
-+	rc = get_dev_info_by_irq (irq, &dinfo);
-+	if (rc < 0)
-+		return rc;
-+
-+	/* No memory, we loose. */
-+	if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
-+		return -ENOMEM;
++	struct tape_device *device;
++	struct request     *req;
 +
-+	p->devno   = dinfo.devno;
-+	p->cu_type = dinfo.sid_data.cu_type;
-+	memset(&p->task, 0, sizeof(struct tq_struct));
-+	p->task.routine = do_tape_oper_handler;
-+	p->task.data    = p;
++	device = ccw_req->device;
++	req    = (struct request *) data;
++	if(!device || !req)
++		BUG();
 +
-+	/* queue call to do_oper_handler. */
-+	queue_task(&p->task, &tape_cio_tasks);
-+	run_task_queue(&tape_cio_tasks);
++	tapeblock_end_request(req, ccw_req->rc == 0);
++	if (ccw_req->rc == 0)
++		/* Update position. */
++		device->blk_data.block_position = 
++			(req->sector + req->nr_sectors) >> TAPEBLOCK_HSEC_S2B;
++	else
++		/* We lost the position information due to an error. */
++		device->blk_data.block_position = -1;
 +
-+	return 0;
-+}
++	device->discipline->free_bread(ccw_req);
 +
++	if (!list_empty(&device->req_queue) ||
++	    !list_empty(&device->blk_data.request_queue.queue_head))
++		tasklet_schedule(&device->blk_data.tasklet);
++}
 +
 +/*
-+ * Not Oper Handler is called from Ingo's IO layer, when a tape device
-+ * is detached.
++ * Fetch requests from block device queue.
 + */
-+static void
-+do_tape_noper_handler(void *data)
++static inline void
++__tape_process_blk_queue(struct tape_device *device, struct list_head *new_req)
 +{
-+	struct {
-+		int			irq;
-+		int			status;
-+		struct tq_struct	task;
-+	} *p;
-+	struct tape_device	*device;
-+	struct list_head	*l;
-+	struct tape_devmap	*devmap;
-+	unsigned long		 flags;
-+
-+	p = data;
++	request_queue_t     *queue;
++	struct list_head    *l;
++	struct request      *req;
++	struct tape_request *ccw_req;
++	int                  nr_queued;
 +
-+	/*
-+	 * find out devno of leaving device: CIO has already deleted
-+	 * this information so we need to find it by irq!
-+	 */
-+	device = tape_get_device_by_irq(p->irq);
-+	if (IS_ERR(device)) {
-+		kfree(p);
++	if (!TAPE_BLOCKDEV(device)) {
++		PRINT_WARN("can't process queue. Not a tape blockdevice.\n");
 +		return;
 +	}
 +
-+	/*
-+	 * Handle the new path revalidation scheme of the common IO layer.
-+	 */
-+	switch(p->status) {
-+		case DEVSTAT_DEVICE_GONE:
-+		case DEVSTAT_REVALIDATE: /* FIXME: What to do? */
-+			tape_disable_device(device, 1);
++	nr_queued = 0;
++	queue     = &device->blk_data.request_queue;
 +
-+			/*
-+			 * Remove the device reference from the device map.
-+			 */
-+			spin_lock_irqsave(&tape_devmap_lock, flags);
-+			list_for_each(l, &tape_devreg_list) {
-+				devmap = list_entry(
-+						l, struct tape_devmap, list
-+					);
-+				if (devmap->device == device) {
-+					tape_put_device(device);
-+					devmap->device = NULL;
-+					break;
-+				}
-+			}
-+			spin_unlock_irqrestore(&tape_devmap_lock, flags);
-+			break;
-+		case DEVSTAT_NOT_ACC:
-+			/*
-+			 * Device shouldn't be accessed at the moment. The
-+			 * currently running request will complete.
-+			 */
-+			spin_lock_irqsave(
-+				get_irq_lock(device->devinfo.irq), flags
-+			);
-+			DBF_EVENT(3, "T390(%04x): suspend processing\n",
-+				device->devinfo.devno);
-+			TAPE_SET_STATE(device, TAPE_STATUS_NOACCESS);
-+			spin_unlock_irqrestore(
-+				get_irq_lock(device->devinfo.irq), flags
-+			);
-+			break;
-+		case DEVSTAT_NOT_ACC_ERR: {
-+			struct tape_request *request;
++	/* Count number of requests on ccw queue. */
++	list_for_each(l, &device->req_queue)
++		nr_queued++;
 +
-+			/*
-+			 * Device shouldn't be accessed at the moment. The
-+			 * request that was running is lost.
-+			 */
-+			spin_lock_irqsave(
-+				get_irq_lock(device->devinfo.irq), flags
-+			);
++	while (
++		!queue->plugged &&
++		!list_empty(&queue->queue_head) &&
++		nr_queued < TAPEBLOCK_MIN_REQUEUE
++	) {
++		/* tape_block_next_request(queue); */
++		req = blkdev_entry_next_request(&queue->queue_head);
 +
-+			request = list_entry(device->req_queue.next,
-+					struct tape_request, list);
-+			if(
-+				!list_empty(&device->req_queue)
-+				&&
-+				request->status == TAPE_REQUEST_IN_IO
-+			) {
-+				/* Argh! Might better belong to tape_core.c */
-+				list_del(&request->list);
-+				request->rc     = -EIO;
-+				request->status = TAPE_REQUEST_DONE;
-+				if (request->callback != NULL) {
-+					request->callback(
-+						request,
-+						request->callback_data
-+					);
-+					request->callback = NULL;
-+				}
-+			}
-+			DBF_EVENT(3, "T390(%04x): suspend processing\n",
-+				device->devinfo.devno);
-+			DBF_EVENT(3, "T390(%04x): request lost\n",
-+				device->devinfo.devno);
-+			TAPE_SET_STATE(device, TAPE_STATUS_NOACCESS);
-+			spin_unlock_irqrestore(
-+				get_irq_lock(device->devinfo.irq), flags
-+			);
-+			break;
++		if (req->cmd == WRITE) {
++			DBF_EVENT(1, "TBLOCK: Rejecting write request\n");
++			blkdev_dequeue_request(req);
++			tapeblock_end_request(req, 0);
++			continue;
 +		}
-+		default:
-+			PRINT_WARN("T390(%04x): no operation handler called "
-+				"with unknown status(0x%x)\n",
-+				device->devinfo.devno, p->status);
-+			tape_disable_device(device, 1);
++		ccw_req = device->discipline->bread(device, req);
++		if (IS_ERR(ccw_req)) {
++			if (PTR_ERR(ccw_req) == -ENOMEM)
++				break; /* don't try again */
++			DBF_EVENT(1, "TBLOCK: bread failed\n");
++			blkdev_dequeue_request(req);
++			tapeblock_end_request(req, 0);
++			continue;
++		}
++		blkdev_dequeue_request(req);
++		ccw_req->callback      = __tapeblock_end_request;
++		ccw_req->callback_data = (void *) req;
++		ccw_req->retries       = TAPEBLOCK_RETRIES;
 +
-+			/*
-+			 * Remove the device reference from the device map.
-+			 */
-+			spin_lock_irqsave(&tape_devmap_lock, flags);
-+			list_for_each(l, &tape_devreg_list) {
-+				devmap = list_entry(
-+						l, struct tape_devmap, list
-+					);
-+				if (devmap->device == device) {
-+					tape_put_device(device);
-+					devmap->device = NULL;
-+					break;
-+				}
-+			}
-+			spin_unlock_irqrestore(&tape_devmap_lock, flags);
++		list_add_tail(&ccw_req->list, new_req);
++		nr_queued++;
 +	}
-+
-+	tape_put_device(device);
-+	kfree(p);
 +}
 +
-+void
-+tape_noper_handler(int irq, int status)
++/*
++ * Feed requests to the tape device.
++ */
++static inline int
++tape_queue_requests(struct tape_device *device, struct list_head *new_req)
 +{
-+	struct {
-+		int			irq;
-+		int			status;
-+		struct tq_struct	task;
-+	} *p;
++	struct list_head *l, *n;
++	struct tape_request *ccw_req;
++	struct request *req;
++	int rc, fail;
 +
-+	/* No memory, we loose. */
-+	if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
-+		return;
++	fail = 0;
++	list_for_each_safe(l, n, new_req) {
++		ccw_req = list_entry(l, struct tape_request, list);
++		list_del(&ccw_req->list);
 +
-+	p->irq  = irq;
-+	p->status = status;
-+	memset(&p->task, 0, sizeof(struct tq_struct));
-+	p->task.routine = do_tape_noper_handler;
-+	p->task.data    = p;
-+	
-+	/* queue call to do_oper_handler. */
-+	queue_task(&p->task, &tape_cio_tasks);
-+	run_task_queue(&tape_cio_tasks);
++		rc = tape_do_io_async(device, ccw_req);
++		if (rc) {
++			/*
++			 * Start/enqueueing failed. No retries in
++			 * this case.
++			 */
++			DBF_EVENT(5, "enqueueing failed\n");
++			req = (struct request *) ccw_req->callback_data;
++			tapeblock_end_request(req, 0);
++			device->discipline->free_bread(ccw_req);
++			fail = 1;
++		}
++	}
++	return fail;
 +}
 +
-+
-+int
-+tape_devmap_init(void)
++/*
++ * Tape request queue function. Called from ll_rw_blk.c
++ */
++static void
++tapeblock_request_fn(request_queue_t *queue)
 +{
-+	return tape_parse();
-+}
++	struct list_head    new_req;
++	struct tape_device *device;
 +
-+void
-+tape_devmap_exit(void)
-+{
-+	tape_forget_devregs();
++	device = (struct tape_device *) queue->queuedata;
++	if(device == NULL)
++		BUG();
++
++	while (!list_empty(&queue->queue_head)) {
++		INIT_LIST_HEAD(&new_req);
++		spin_lock(get_irq_lock(device->devinfo.irq));
++		__tape_process_blk_queue(device, &new_req);
++		spin_unlock(get_irq_lock(device->devinfo.irq));
++		/*
++		 * Now queue the new request to the tape. This needs to be
++		 * done without the device lock held.
++		 */
++		if (tape_queue_requests(device, &new_req) == 0)
++			/* All requests queued. Thats enough for now. */
++			break;
++	}
 +}
 +
-+EXPORT_SYMBOL(tape_get_device);
-+EXPORT_SYMBOL(tape_get_device_by_irq);
-+EXPORT_SYMBOL(tape_get_device_by_devno);
-+EXPORT_SYMBOL(tape_put_device);
-+EXPORT_SYMBOL(tape_clone_device);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_proc.c drivers/s390/char/tape_proc.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_proc.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/tape_proc.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,385 @@
 +/*
-+ *  drivers/s390/char/tape.c
-+ *    tape device driver for S/390 and zSeries tapes.
-+ *
-+ *  S390 and zSeries version
-+ *    Copyright (C) 2001 IBM Corporation
-+ *    Author(s): Carsten Otte <cotte at de.ibm.com>
-+ *               Michael Holzheu <holzheu at de.ibm.com>
-+ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-+ *
-+ * PROCFS Functions
++ * Returns block frontend request queue for a tape device.
++ * FIXME: on shutdown make sure ll_rw_blk can put requests on a dead queue.
 + */
++static request_queue_t *
++tapeblock_get_queue(kdev_t kdev)
++{
++	struct tape_device *device;
++	request_queue_t    *queue;
 +
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/module.h>
-+#include <linux/vmalloc.h>
-+#include <linux/seq_file.h>
-+#include <asm/irq.h>
-+#include <asm/s390io.h>
-+
-+#define TAPE_DBF_AREA	tape_core_dbf
++	if (major(kdev) != tapeblock_major)
++		return NULL;
 +
-+#include "tape.h"
++	device = tape_get_device(minor(kdev) >> 1);
++	if (IS_ERR(device))
++		return NULL;
 +
-+#define PRINTK_HEADER "T390:"
++	queue = &device->blk_data.request_queue;
++	tape_put_device(device);
++	return queue;
++}
 +
-+static const char *tape_med_st_verbose[MS_SIZE] =
++/*
++ * Acquire the device lock and process queues for the device.
++ */
++static void
++tapeblock_tasklet(unsigned long data)
 +{
-+	[MS_UNKNOWN]  = "UNKNOWN ",
-+	[MS_LOADED]   = "LOADED  ",
-+	[MS_UNLOADED] = "UNLOADED"
-+};
-+
-+/* our proc tapedevices entry */
-+static struct proc_dir_entry *tape_proc_devices;
-+
-+static int tape_proc_show(struct seq_file *m, void *v) {
-+	struct tape_device  *device;
-+	struct tape_request *request;
-+	unsigned long        n;
-+
-+	n = ((unsigned long) v - 1);
++	struct list_head    new_req;
++	struct tape_device *device;
 +
-+	if(n == 0) {
-+		seq_printf(m,
-+			"TapeNo\tDevNo\tCuType\tCuModel\tDevType\t"
-+			"DevMod\tBlkSize\tState\tOp\tMedState\n"
-+		);
++	device = (struct tape_device *) data;
++	while (!list_empty(&device->blk_data.request_queue.queue_head)) {
++		INIT_LIST_HEAD(&new_req);
++		spin_lock_irq(get_irq_lock(device->devinfo.irq));
++		__tape_process_blk_queue(device, &new_req);
++		spin_unlock_irq(get_irq_lock(device->devinfo.irq));
++		/*
++		 * Now queue the new request to the tape. This needs to be
++		 * done without the device lock held.
++		 */
++		if (tape_queue_requests(device, &new_req) == 0)
++			/* All requests queued. Thats enough for now. */
++			break;
 +	}
++}
 +
-+	device = tape_get_device(n);
-+	if(IS_ERR(device))
-+		return 0;
-+
-+	spin_lock_irq(get_irq_lock(device->devinfo.irq));
-+
-+	seq_printf(m,
-+		"%d\t%04X\t%04X\t%02X\t%04X\t%02X\t",
-+		device->first_minor/TAPE_MINORS_PER_DEV,
-+		device->devinfo.devno,
-+		device->devinfo.sid_data.cu_type,
-+		device->devinfo.sid_data.cu_model,
-+		device->devinfo.sid_data.dev_type,
-+		device->devinfo.sid_data.dev_model
-+	);
-+
-+	/*
-+	 * the blocksize is either 'auto' or the blocksize as a decimal number
-+	 */
-+	if(device->char_data.block_size == 0)
-+		seq_printf(m, "auto\t");
-+	else
-+		seq_printf(m, "%i\t", device->char_data.block_size);
++/*
++ * Create block directory with disc entries
++ */
++static int
++tapeblock_mkdevfstree (struct tape_device *device)
++{
++#ifdef CONFIG_DEVFS_FS
++	device->blk_data.devfs_block_dir = 
++		devfs_mk_dir (device->devfs_dir, "block", device);
++	if (device->blk_data.devfs_block_dir == 0)
++		return -ENOENT;
++	device->blk_data.devfs_disc = 
++		devfs_register(device->blk_data.devfs_block_dir,
++			       "disc", DEVFS_FL_DEFAULT,
++			       tapeblock_major, device->first_minor,
++			       TAPEBLOCK_DEVFSMODE, &tapeblock_bdops, device);
++	if (device->blk_data.devfs_disc == NULL) {
++		devfs_unregister(device->blk_data.devfs_block_dir);
++		return -ENOENT;
++	}
++#endif
++	return 0;
++}
 +
-+	seq_printf(m, "%s\t", tape_state_string(device));
++/*
++ * Remove devfs entries
++ */
++static void
++tapeblock_rmdevfstree (struct tape_device *device)
++{
++#ifdef CONFIG_DEVFS_FS
++	if (device->blk_data.devfs_disc)
++		devfs_unregister(device->blk_data.devfs_disc);
++	if (device->blk_data.devfs_block_dir)
++		devfs_unregister(device->blk_data.devfs_block_dir);
++#endif
++}
 +
-+	/*
-+	 * verbose desciption of current tape operation
-+	 */
-+	if(!list_empty(&device->req_queue)) {
-+		request = list_entry(
-+			device->req_queue.next, struct tape_request, list
-+		);
++/*
++ * This function is called for every new tapedevice
++ */
++int
++tapeblock_setup_device(struct tape_device * device)
++{
++	int rc;
 +
-+		seq_printf(m, "%s\t", tape_op_verbose[request->op]);
-+	} else {
-+		seq_printf(m, "---\t");
-+	}
-+	
-+	seq_printf(m, "%s\n", tape_med_st_verbose[device->medium_state]);
++	/* FIXME: We should be able to sense the sector size */
++	blk_size[tapeblock_major][device->first_minor]      = 0;
++	blksize_size[tapeblock_major][device->first_minor]  =
++	hardsect_size[tapeblock_major][device->first_minor] =
++		TAPEBLOCK_HSEC_SIZE;
 +
-+	spin_unlock_irq(get_irq_lock(device->devinfo.irq));
-+	tape_put_device(device);
++	/* Create devfs entries. */
++	rc = tapeblock_mkdevfstree(device);
++	if (rc)
++		return rc;
 +
-+	return 0;
-+}
++	/* Setup request queue and initialize gendisk for this device. */
++	device->blk_data.request_queue.queuedata = tape_clone_device(device);
 +
-+static void *tape_proc_start(struct seq_file *m, loff_t *pos) {
-+	if(*pos < tape_max_devindex)
-+		return (void *) ((unsigned long) (*pos) + 1);
-+	return NULL;
-+}
 +
-+static void tape_proc_stop(struct seq_file *m, void *v) {
-+}
++	/* As long as the tasklet is running it may access the device */
++	tasklet_init(&device->blk_data.tasklet, tapeblock_tasklet,
++		     (unsigned long) tape_clone_device(device));
 +
-+static void *tape_proc_next(struct seq_file *m, void *v, loff_t *pos) {
-+	(*pos)++;
-+	return tape_proc_start(m, pos);
-+}
++	blk_init_queue(&device->blk_data.request_queue, tapeblock_request_fn);
++	blk_queue_headactive(&device->blk_data.request_queue, 0);
 +
-+static struct seq_operations tape_proc_seq = {
-+	.start  = tape_proc_start,
-+	.next   = tape_proc_next,
-+	.stop   = tape_proc_stop,
-+	.show   = tape_proc_show,
-+};
++	tape_hotplug_event(device, tapeblock_major, TAPE_HOTPLUG_BLOCK_ADD);
 +
-+static int tape_proc_open(struct inode *inode, struct file *file) {
-+	return seq_open(file, &tape_proc_seq);
++	set_device_ro(mk_kdev(tapeblock_major, device->first_minor), 1);
++	return 0;
 +}
 +
-+static int
-+tape_proc_assign(int devno)
++void
++tapeblock_cleanup_device(struct tape_device *device)
 +{
-+	int			 rc;
-+	struct tape_device	*device;
++	/* Prevent further requests to the block request queue. */
++	blk_size[tapeblock_major][device->first_minor] = 0;
 +
-+	if(IS_ERR(device = tape_get_device_by_devno(devno))) {
-+		DBF_EVENT(3, "TAPE(%04x): assign invalid device\n", devno);
-+		PRINT_ERR("TAPE(%04x): assign invalid device\n", devno);
-+		return PTR_ERR(device);
-+	}
++	tapeblock_rmdevfstree(device);
 +
-+	rc = tape_assign(device, TAPE_STATUS_ASSIGN_M);
++	/* With the tasklet gone the reference is gone as well. */
++	tasklet_kill(&device->blk_data.tasklet);
++	tape_put_device(device);
++
++	/* Cleanup the request queue. */
++	blk_cleanup_queue(&device->blk_data.request_queue);
 +
++	/* Remove reference in private data */
++	device->blk_data.request_queue.queuedata = NULL;
 +	tape_put_device(device);
 +
-+	return rc;
++	tape_hotplug_event(device, tapeblock_major, TAPE_HOTPLUG_BLOCK_REMOVE);
 +}
 +
-+static int
-+tape_proc_unassign(int devno)
++/*
++ * Detect number of blocks of the tape.
++ * FIXME: can we extent this to detect the blocks size as well ?
++ * FIXME: (minor) On 34xx the block id also contains a format specification
++ *                which is unknown before the block was skipped or read at
++ *                least once. So detection is sometimes done a second time.
++ */
++int tapeblock_mediumdetect(struct tape_device *device)
 +{
-+	int			 rc;
-+	struct tape_device	*device;
++	unsigned int bid;
++	unsigned int nr_of_blks;
++	int          rc;
++
++	/*
++	 * Identify the first records format
++	 */
++	if((rc = tape_mtop(device, MTFSR, 1)) < 0)
++		return rc;
++	if((rc = tape_mtop(device, MTBSR, 1)) < 0)
++		return rc;
++
++	device->blk_data.block_position = 0;
++	if (tape_std_read_block_id(device, &bid)) {
++		rc = tape_mtop(device, MTREW, 1);
++		if (rc) {
++			device->blk_data.block_position                = -1;
++			blk_size[tapeblock_major][device->first_minor] =  0;
++			return rc;
++		}
++		bid = 0;
++	}
 +
-+	if(IS_ERR(device = tape_get_device_by_devno(devno))) {
-+		DBF_EVENT(3, "TAPE(%04x): unassign invalid device\n", devno);
-+		PRINT_ERR("TAPE(%04x): unassign invalid device\n", devno);
-+		return PTR_ERR(device);
++	if(bid != device->blk_data.start_block_id) {
++		device->blk_data.start_block_id = bid;
++		blk_size[tapeblock_major][device->first_minor] =  0;
 +	}
 +
-+	rc = tape_unassign(device, TAPE_STATUS_ASSIGN_M);
++	if(blk_size[tapeblock_major][device->first_minor] > 0)
++		return 0;
 +
-+	tape_put_device(device);
++	PRINT_INFO("Detecting media size...\n");
++	blk_size[tapeblock_major][device->first_minor] = 0;
 +
-+	return rc;
-+}
++	rc = tape_mtop(device, MTFSF, 1);
++	if (rc)
++		return rc;
 +
-+#ifdef SMB_DEBUG_BOX
-+static int
-+tape_proc_put_into_box(int devno)
-+{
-+	struct tape_device	*device;
++	rc = tape_mtop(device, MTTELL, 1);
++	if (rc < 0)
++		return rc;
++	nr_of_blks = rc - 1; /* don't count FM */
 +
-+	if(IS_ERR(device = tape_get_device_by_devno(devno))) {
-+		DBF_EVENT(3, "TAPE(%04x): invalid device\n", devno);
-+		PRINT_ERR("TAPE(%04x): invalid device\n", devno);
-+		return PTR_ERR(device);
++	if (device->blk_data.start_block_id) {
++		rc = tape_std_seek_block_id(
++			device,
++			device->blk_data.start_block_id);
++	} else {
++		rc = tape_mtop(device, MTREW, 1);
 +	}
++	if (rc)
++		return rc;
 +
-+	TAPE_SET_STATE(device, TAPE_STATUS_BOXED);
++	rc = tape_mtop(device, MTTELL, 1);
++	if (rc < 0)
++		return rc;
 +
-+	tape_put_device(device);
++	/* Don't include start offset */
++	nr_of_blks -= rc;
++
++	PRINT_INFO("Found %i blocks on media\n", nr_of_blks);
++	if (tapeblock_hw_ssize(device->first_minor) > 1024) {
++		nr_of_blks *= tapeblock_hw_ssize(device->first_minor) / 1024;
++	} else {
++		nr_of_blks /= 1024 / tapeblock_hw_ssize(device->first_minor);
++	}
++	PRINT_INFO("Tape block device size is %i KB\n", nr_of_blks);
++	blk_size[tapeblock_major][device->first_minor] = nr_of_blks;
 +
 +	return 0;
 +}
-+#endif
 +
-+#ifdef TAPE390_FORCE_UNASSIGN
-+static int
-+tape_proc_force_unassign(int devno)
-+{
-+	int			 rc;
-+	struct tape_device	*device;
++/*
++ * This function has to be called whenever a new medium has been inserted
++ * into the drive.
++ */
++void
++tapeblock_medium_change(struct tape_device *device) {
++	device->blk_data.start_block_id                = 0;
++	blk_size[tapeblock_major][device->first_minor] = 0;
++}
 +
-+	if(IS_ERR(device = tape_get_device_by_devno(devno))) {
-+		DBF_EVENT(3, "TAPE(%04x): force unassign invalid device\n",
-+			devno);
-+		PRINT_ERR("TAPE(%04x): force unassign invalid device\n",
-+			devno);
++/*
++ * Block frontend tape device open function.
++ */
++int
++tapeblock_open(struct inode *inode, struct file *filp) {
++	struct tape_device *device;
++	int                 rc;
++
++	if (major(filp->f_dentry->d_inode->i_rdev) != tapeblock_major)
++		return -ENODEV;
++
++	MOD_INC_USE_COUNT;
++	device = tape_get_device(minor(filp->f_dentry->d_inode->i_rdev) >> 1);
++	if (IS_ERR(device)) {
++		MOD_DEC_USE_COUNT;
 +		return PTR_ERR(device);
 +	}
 +
-+	if (!TAPE_BOXED(device)) {
-+		DBF_EVENT(3, "TAPE(%04x): forced unassignment only allowed for"
-+			" boxed device\n", devno);
-+		PRINT_ERR("TAPE(%04x): forced unassignment only allowed for"
-+			" boxed device\n", devno);
-+		rc = -EPERM;
-+	} else if(device->discipline->force_unassign == NULL) {
-+		DBF_EVENT(3, "TAPE(%04x: force unassign is not supported on"
-+			" this device\n", devno);
-+		PRINT_ERR("TAPE(%04x: force unassign is not supported on"
-+			" this device\n", devno);
-+		rc = -EPERM;
-+	} else { 
-+		rc = device->discipline->force_unassign(device);
-+		if(rc == 0)
-+			spin_lock_irq(get_irq_lock(device->devinfo.irq));
-+			TAPE_CLEAR_STATE(
-+				device,
-+				TAPE_STATUS_BOXED
-+				| TAPE_STATUS_ASSIGN_A
-+				| TAPE_STATUS_ASSIGN_M
-+			);
-+			spin_unlock_irq(get_irq_lock(device->devinfo.irq));
++	DBF_EVENT(6, "TBLOCK: open:  %x\n", device->first_minor);
++
++	if(device->required_tapemarks) {
++		DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
++		PRINT_ERR("TBLOCK: Refusing to open tape with missing"
++			" end of file marks.\n");
++		tape_put_device(device);
++		MOD_DEC_USE_COUNT;
++		return -EPERM;
 +	}
 +
++	rc = tape_open(device);
++	if (rc == 0) {
++		rc = tape_assign(device, TAPE_STATUS_ASSIGN_A);
++		if (rc == 0) {
++			rc = tapeblock_mediumdetect(device);
++			if (rc == 0) {
++				TAPE_SET_STATE(device, TAPE_STATUS_BLOCKDEV);
++				tape_put_device(device);
++				return 0;
++			}
++			tape_unassign(device, TAPE_STATUS_ASSIGN_A);
++		}
++		tape_release(device);
++	}
 +	tape_put_device(device);
++	MOD_DEC_USE_COUNT;
 +	return rc;
 +}
-+#endif
 +
 +/*
-+ * Skips over all characters to the position after a newline or beyond the
-+ * last character of the string.
-+ * Returns the number of characters skiped.
++ * Block frontend tape device release function.
 + */
-+static size_t
-+tape_proc_skip_eol(const char *buf, size_t len, loff_t *off)
-+{
-+	loff_t start = *off;
++int
++tapeblock_release(struct inode *inode, struct file *filp) {
++	struct tape_device *device;
 +
-+	while((*off - start) < len) {
-+		if(*(buf+*off) == '\n') {
-+			*off += 1;
-+			break;
-+		}
-+		*off += 1;
-+	}
++	device = tape_get_device(minor(inode->i_rdev) >> 1);
 +
-+	return (size_t) (*off - start);
-+}
++	DBF_EVENT(4, "TBLOCK: release %i\n", device->first_minor);
 +
-+/*
-+ * Skips over whitespace characters and returns the number of characters
-+ * that where skiped.
-+ */
-+static size_t
-+tape_proc_skip_ws(const char *buf, size_t len, loff_t *off)
-+{
-+	loff_t start = *off;
++	/* Remove all buffers at device close. */
++	/* FIXME: can we do that a tape unload ? */
++	invalidate_buffers(inode->i_rdev);
 +
-+	while((*off - start) < len) {
-+		if(*(buf + *off) != ' ' && *(buf + *off) != '\t')
-+			break;
-+		*off += 1;
++	if (device->blk_data.start_block_id) {
++		tape_std_seek_block_id(device, device->blk_data.start_block_id);
++	} else {
++		tape_mtop(device, MTREW, 1);
 +	}
++	TAPE_CLEAR_STATE(device, TAPE_STATUS_BLOCKDEV);
++	tape_unassign(device, TAPE_STATUS_ASSIGN_A);
++	tape_release(device);
++	tape_put_device(device);
++	MOD_DEC_USE_COUNT;
 +
-+	return (size_t) (*off - start);
++	return 0;
 +}
 +
-+static size_t
-+tape_proc_get_hexvalue(char *buf, size_t len, loff_t *off, unsigned int *hex)
-+{
-+	int          hexdigit;
-+	loff_t       start    = *off;
-+
-+	/* Skip possible space characters */
-+	tape_proc_skip_ws(buf, len, off);
++int
++tapeblock_ioctl(
++	struct inode	*inode,
++	struct file	*file,
++	unsigned int	 command,
++	unsigned long	 arg
++) {
++	int	 rc     = 0;
++	int	 minor  = minor(inode->i_rdev);
 +
-+	/* The hexvalue might start with '0x' or '0X' */
-+	if((*off - start)+1 < len && *(buf + *off) == '0')
-+		if(*(buf + *off + 1) == 'x' || *(buf + *off + 1) == 'X')
-+			*off += 2;
++	DBF_EVENT(6, "tapeblock_ioctl(%x)\n", command);
 +
-+	*hex = 0;
-+	while((*off - start) < len) {
-+		if(*(buf + *off) >= '0' && *(buf + *off) <= '9') {
-+			hexdigit = *(buf + *off) - '0';
-+		} else if(*(buf + *off) >= 'a' && *(buf + *off) <= 'f') {
-+			hexdigit = *(buf + *off) - 'a' + 10;
-+		} else if(*(buf + *off) >= 'A' && *(buf + *off) <= 'F') {
-+			hexdigit = *(buf + *off) - 'A' + 10;
-+		} else {
++	switch(command) {
++		case BLKSSZGET:
++			if(put_user(tapeblock_ssize(minor), (int *) arg))
++				rc = -EFAULT;
 +			break;
-+		}
-+		*hex  = (*hex << 4) + hexdigit;
-+		*off += 1;
++		case BLKGETSIZE:
++			if(
++				put_user(
++					tapeblock_size(minor),
++					(unsigned long *) arg
++				)
++			)
++				rc = -EFAULT;
++			break;
++#ifdef BLKGETSIZE64
++		case BLKGETSIZE64:
++			if(put_user(tapeblock_size(minor) << 9, (u64 *) arg))
++				rc = -EFAULT;
++			break;
++#endif
++		case CDROMMULTISESSION:
++		case CDROMREADTOCENTRY:
++			/* No message for these... */
++			rc = -EINVAL;
++			break;
++		default:
++			PRINT_WARN("invalid ioctl 0x%x\n", command);
++			rc = -EINVAL;
 +	}
-+
-+	return (size_t) (*off - start);
++	return rc;
 +}
 +
-+static ssize_t tape_proc_write(
-+	struct file *file,
-+	const char  *buf,
-+	size_t       len,
-+	loff_t      *off
-+) {
-+	loff_t  start = *off;
-+	int     devno;
-+	char   *s;
-+
-+	if(PAGE_SIZE < len)
-+		return -EINVAL;
-+
-+	if((s = kmalloc(len, GFP_KERNEL)) == NULL)
-+		return -ENOMEM;
-+
-+	if(copy_from_user(s, buf, len) != 0) {
-+		kfree(s);
-+		return -EFAULT;
-+	}
++/*
++ * Initialize block device frontend.
++ */
++int
++tapeblock_init(void) 
++{
++	int rc;
 +
-+	if(strncmp(s+*off, "assign", 6) == 0) {
-+		(*off) += 6;
-+		tape_proc_get_hexvalue(s, len - 6, off, &devno);
-+		if(devno > 0)
-+			tape_proc_assign(devno);
-+	} else if(strncmp(s+*off, "unassign", 8) == 0) {
-+		(*off) += 8;
-+		tape_proc_get_hexvalue(s, len - (*off - start), off, &devno);
-+		if(devno > 0)
-+			tape_proc_unassign(devno);
-+#ifdef TAPE390_FORCE_UNASSIGN
-+	} else if(strncmp(s+*off, "forceunassign", 13) == 0) {
-+		(*off) += 13;
-+		tape_proc_get_hexvalue(s, len - (*off - start), off, &devno);
-+		if(devno > 0)
-+			tape_proc_force_unassign(devno);
-+#endif
-+#ifdef SMB_DEBUG_BOX
-+	} else if(strncmp(s+*off, "putintobox", 10) == 0) {
-+		(*off) += 10;
-+		tape_proc_get_hexvalue(s, len - (*off - start), off, &devno);
-+		if(devno > 0)
-+			tape_proc_put_into_box(devno);
++	/* Register the tape major number to the kernel */
++#ifdef CONFIG_DEVFS_FS
++	if (tapeblock_major == 0)
++		tapeblock_major = devfs_alloc_major(DEVFS_SPECIAL_BLK);
 +#endif
-+	} else {
-+		DBF_EVENT(3, "tape_proc_write() parse error\n");
-+		PRINT_ERR("Invalid /proc/tapedevices command.\n");
++	rc = register_blkdev(tapeblock_major, "tBLK", &tapeblock_bdops);
++	if (rc < 0) {
++		PRINT_ERR("can't get major %d for block device\n",
++			  tapeblock_major);
++		return rc;
 +	}
-+	tape_proc_skip_eol(s, len - (*off - start), off);
++	if(tapeblock_major == 0)
++		tapeblock_major = rc;
 +
-+	kfree(s);
++	/* Allocate memory for kernel block device tables */
++	rc = -ENOMEM;
++	blk_size[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL);
++	if(blk_size[tapeblock_major] == NULL)
++		goto tapeblock_init_fail;
++	memset(blk_size[tapeblock_major], 0, 256*sizeof(int));
++	blksize_size[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL);
++	if(blksize_size[tapeblock_major] == NULL)
++		goto tapeblock_init_fail;
++	memset(blksize_size[tapeblock_major], 0, 256*sizeof(int));
++	hardsect_size[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL);
++	if(hardsect_size[tapeblock_major] == NULL)
++		goto tapeblock_init_fail;
++	memset(hardsect_size[tapeblock_major], 0, 256*sizeof(int));
++	max_sectors[tapeblock_major] = kmalloc(256*sizeof(int), GFP_KERNEL);
++	if(max_sectors[tapeblock_major] == NULL)
++		goto tapeblock_init_fail;
++	memset(max_sectors[tapeblock_major], 0, 256*sizeof(int));
 +
-+	/* Just pretend to have processed all the stuff */
-+	return len;
-+}
++	blk_dev[tapeblock_major].queue = tapeblock_get_queue;
 +
-+static struct file_operations tape_proc_ops =
-+{
-+	.open    = tape_proc_open,
-+	.read    = seq_read,
-+	.write   = tape_proc_write,
-+	.llseek  = seq_lseek,
-+	.release = seq_release,
-+};
++	PRINT_INFO("tape gets major %d for block device\n", tapeblock_major);
++	DBF_EVENT(3, "TBLOCK: major = %d\n", tapeblock_major);
++	DBF_EVENT(3, "TBLOCK: init ok\n");
 +
-+/* 
-+ * Initialize procfs stuff on startup
-+ */
-+void tape_proc_init(void) {
-+	tape_proc_devices = create_proc_entry(
-+		"tapedevices", S_IFREG | S_IRUGO | S_IWUSR, &proc_root);
++	return 0;
 +
-+	if (tape_proc_devices == NULL) {
-+		PRINT_WARN("tape: Cannot register procfs entry tapedevices\n");
-+		return;
++tapeblock_init_fail:
++	if(tapeblock_major > 0) {
++		if(blk_size[tapeblock_major]) {
++			kfree(blk_size[tapeblock_major]);
++			blk_size[tapeblock_major] = NULL;
++		}
++		if(blksize_size[tapeblock_major]) {
++			kfree(blksize_size[tapeblock_major]);
++			blksize_size[tapeblock_major] = NULL;
++		}
++		if(hardsect_size[tapeblock_major]) {
++			kfree(hardsect_size[tapeblock_major]);
++			hardsect_size[tapeblock_major] = NULL;
++		}
++		if(max_sectors[tapeblock_major]) {
++			kfree(max_sectors[tapeblock_major]);
++			max_sectors[tapeblock_major] = NULL;
++		}
++#ifdef CONFIG_DEVFS_FS
++		devfs_unregister_blkdev(tapeblock_major, "tBLK");
++#else
++		unregister_blkdev(tapeblock_major, "tBLK");
++#endif
++		tapeblock_major = -1;
 +	}
-+	tape_proc_devices->proc_fops = &tape_proc_ops;
-+	tape_proc_devices->owner     = THIS_MODULE;
++
++	DBF_EVENT(3, "TBLOCK: init failed(%d)\n", rc);
++	return rc;
 +}
 +
 +/*
-+ * Cleanup all stuff registered to the procfs
++ * Deregister major for block device frontend
 + */
-+void tape_proc_cleanup(void) {
-+	if(tape_proc_devices != NULL)
-+		remove_proc_entry ("tapedevices", &proc_root);
++void
++tapeblock_exit(void)
++{
++	if(blk_size[tapeblock_major]) {
++		kfree(blk_size[tapeblock_major]);
++		blk_size[tapeblock_major] = NULL;
++	}
++	if(blksize_size[tapeblock_major]) {
++		kfree(blksize_size[tapeblock_major]);
++		blksize_size[tapeblock_major] = NULL;
++	}
++	if(hardsect_size[tapeblock_major]) {
++		kfree(hardsect_size[tapeblock_major]);
++		hardsect_size[tapeblock_major] = NULL;
++	}
++	if(max_sectors[tapeblock_major]) {
++		kfree(max_sectors[tapeblock_major]);
++		max_sectors[tapeblock_major] = NULL;
++	}
++	blk_dev[tapeblock_major].queue = NULL;
++	unregister_blkdev(tapeblock_major, "tBLK");
 +}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_std.c drivers/s390/char/tape_std.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_std.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/tape_std.c	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,779 @@
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tapeblock.c drivers/s390/char/tapeblock.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tapeblock.c	2001-10-25 14:58:35.000000000 -0600
++++ drivers/s390/char/tapeblock.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1,593 +0,0 @@
+-
+-/***************************************************************************
+- *
+- *  drivers/s390/char/tapeblock.c
+- *    block device frontend for tape device driver
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s): Carsten Otte <cotte at de.ibm.com>
+- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *
+- *
+- ****************************************************************************
+- */
+-
+-#include "tapedefs.h"
+-#include <linux/config.h>
+-#include <linux/blkdev.h>
+-#include <linux/blk.h>
+-#include <linux/version.h>
+-#include <linux/interrupt.h>
+-#include <asm/ccwcache.h>	/* CCW allocations      */
+-#include <asm/debug.h>
+-#include <asm/s390dyn.h>
+-#include <linux/compatmac.h>
+-#ifdef MODULE
+-#define __NO_VERSION__
+-#include <linux/module.h>
+-#endif
+-#include "tape.h"
+-#include "tapeblock.h"
+-
+-#define PRINTK_HEADER "TBLOCK:"
+-
+-/*
+- * file operation structure for tape devices
+- */
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+-static struct block_device_operations tapeblock_fops = {
+-#else
+-static struct file_operations tapeblock_fops = {
+-#endif
+-    owner   : THIS_MODULE,
+-    open    : tapeblock_open,      /* open */
+-    release : tapeblock_release,   /* release */
+-        };
+-
+-int    tapeblock_major = 0;
+-
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+-static void tape_request_fn (request_queue_t * queue);
+-#else
+-static void tape_request_fn (void);
+-#endif
+-
+-static request_queue_t* tapeblock_getqueue (kdev_t kdev);
+-
+-#ifdef CONFIG_DEVFS_FS
+-void
+-tapeblock_mkdevfstree (tape_info_t* ti) {
+-    ti->devfs_block_dir=devfs_mk_dir (ti->devfs_dir, "block", ti);
+-    ti->devfs_disc=devfs_register(ti->devfs_block_dir, "disc",DEVFS_FL_DEFAULT,
+-				    tapeblock_major, ti->blk_minor,
+-				    TAPEBLOCK_DEFAULTMODE, &tapeblock_fops, ti);
+-}
+-
+-void
+-tapeblock_rmdevfstree (tape_info_t* ti) {
+-    devfs_unregister(ti->devfs_disc);
+-    devfs_unregister(ti->devfs_block_dir);
+-}
+-#endif
+-
+-void 
+-tapeblock_setup(tape_info_t* ti) {
+-    blk_size[tapeblock_major][ti->blk_minor]=0; // this will be detected
+-    blksize_size[tapeblock_major][ti->blk_minor]=2048; // blocks are 2k by default.
+-    hardsect_size[tapeblock_major][ti->blk_minor]=512;
+-    blk_init_queue (&ti->request_queue, tape_request_fn); 
+-    blk_queue_headactive (&ti->request_queue, 0); 
+-#ifdef CONFIG_DEVFS_FS
+-    tapeblock_mkdevfstree(ti);
+-#endif
+-}
+-
+-int
+-tapeblock_init(void) {
+-    int result;
+-    tape_frontend_t* blkfront,*temp;
+-    tape_info_t* ti;
+-
+-    tape_init();
+-    /* Register the tape major number to the kernel */
+-#ifdef CONFIG_DEVFS_FS
+-    result = devfs_register_blkdev(tapeblock_major, "tBLK", &tapeblock_fops);
+-#else
+-    result = register_blkdev(tapeblock_major, "tBLK", &tapeblock_fops);
+-#endif
+-    if (result < 0) {
+-        PRINT_WARN(KERN_ERR "tape: can't get major %d for block device\n", tapeblock_major);
+-        panic ("cannot get major number for tape block device");
+-    }
+-    if (tapeblock_major == 0) tapeblock_major = result;   /* accept dynamic major number*/
+-    INIT_BLK_DEV(tapeblock_major,tape_request_fn,tapeblock_getqueue,NULL);
+-    read_ahead[tapeblock_major]=TAPEBLOCK_READAHEAD;
+-    PRINT_WARN(KERN_ERR " tape gets major %d for block device\n", result);
+-    blk_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
+-    memset(blk_size[tapeblock_major],0,256*sizeof(int));
+-    blksize_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
+-    memset(blksize_size[tapeblock_major],0,256*sizeof(int));
+-    hardsect_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
+-    memset(hardsect_size[tapeblock_major],0,256*sizeof(int));
+-    max_sectors[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
+-    memset(max_sectors[tapeblock_major],0,256*sizeof(int));
+-    blkfront = kmalloc(sizeof(tape_frontend_t),GFP_KERNEL);
+-    if (blkfront==NULL) panic ("no mem for tape block device structure");
+-    blkfront->device_setup=tapeblock_setup;
+-#ifdef CONFIG_DEVFS_FS
+-    blkfront->mkdevfstree = tapeblock_mkdevfstree;
+-    blkfront->rmdevfstree = tapeblock_rmdevfstree;
+-#endif
+-    blkfront->next=NULL;
+-    if (first_frontend==NULL) {
+-	first_frontend=blkfront;
+-    } else {
+-	temp=first_frontend;
+-	while (temp->next!=NULL)
+-	temp=temp->next;
+-	temp->next=blkfront;
+-    }
+-    ti=first_tape_info;
+-    while (ti!=NULL) {
+-	tapeblock_setup(ti);
+-	ti=ti->next;
+-    }
+-    return 0;
+-}
+-
+-
+-void 
+-tapeblock_uninit(void) {
+-    unregister_blkdev(tapeblock_major, "tBLK");
+-}
+-
+-int
+-tapeblock_open(struct inode *inode, struct file *filp) {
+-    tape_info_t *ti;
+-    kdev_t dev;
+-    int rc;
+-    long lockflags;
+-
+-    inode = filp->f_dentry->d_inode;
+-    ti = first_tape_info;
+-    while ((ti != NULL) && (ti->blk_minor != MINOR (inode->i_rdev)))
+-		ti = (tape_info_t *) ti->next;
+-	if (ti == NULL)
+-		return -ENODEV;
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"b:open:");
+-	debug_int_event (tape_debug_area,6,ti->blk_minor);
+-#endif
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	if (tapestate_get (ti) != TS_UNUSED) {
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-		debug_text_event (tape_debug_area,6,"b:dbusy");
+-#endif
+-		return -EBUSY;
+-	}
+-	tapestate_set (ti, TS_IDLE);
+-        ti->position=-1;
+-        
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-        rc=tapeblock_mediumdetect(ti);
+-        if (rc) {
+-	    s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	    tapestate_set (ti, TS_UNUSED);
+-	    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	    return rc; // in case of errors, we don't have a size of the medium
+-	}
+-	dev = MKDEV (tapeblock_major, MINOR (inode->i_rdev));	/* Get the device */
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->blk_filp = filp;
+-	filp->private_data = ti;	/* save the dev.info for later reference */
+-        ti->cqr=NULL;
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-    
+-	return 0;
+-}
+-
+-int
+-tapeblock_release(struct inode *inode, struct file *filp) {
+-	long lockflags;
+-	tape_info_t *ti,*lastti;
+-	ti = first_tape_info;
+-	while ((ti != NULL) && (ti->blk_minor != MINOR (inode->i_rdev)))
+-		ti = (tape_info_t *) ti->next;
+-	if ((ti != NULL) && (tapestate_get (ti) == TS_NOT_OPER)) {
+-	    if (ti==first_tape_info) {
+-		first_tape_info=ti->next;
+-	    } else {
+-		lastti=first_tape_info;
+-		while (lastti->next!=ti) lastti=lastti->next;
+-		lastti->next=ti->next;
+-	    }
+-	    kfree(ti);    
+-	    return 0;
+-	}
+-	if ((ti == NULL) || (tapestate_get (ti) != TS_IDLE)) {
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,3,"b:notidle!");
+-#endif
+-		return -ENXIO;	/* error in tape_release */
+-	}
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"b:release:");
+-	debug_int_event (tape_debug_area,6,ti->blk_minor);
+-#endif
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	tapestate_set (ti, TS_UNUSED);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	invalidate_buffers(inode->i_rdev);
+-	return 0;
+-}
+-
+-static void
+-tapeblock_end_request(tape_info_t* ti) {
+-    struct buffer_head *bh;
+-    int uptodate;
+-    if ((tapestate_get(ti)!=TS_FAILED) &&
+-	(tapestate_get(ti)!=TS_DONE))
+-       BUG(); // A request has to be completed to end it
+-    uptodate=(tapestate_get(ti)==TS_DONE); // is the buffer up to date?
+-#ifdef TAPE_DEBUG
+-    if (uptodate) {
+-	debug_text_event (tape_debug_area,6,"b:done:");
+-	debug_int_event (tape_debug_area,6,(long)ti->cqr);
+-    } else {
+-	debug_text_event (tape_debug_area,3,"b:failed:");
+-	debug_int_event (tape_debug_area,3,(long)ti->cqr);
+-    }
+-#endif
+-    // now inform ll_rw_block about a request status
+-    while ((bh = ti->current_request->bh) != NULL) {
+-	ti->current_request->bh = bh->b_reqnext;
+-	bh->b_reqnext = NULL;
+-	bh->b_end_io (bh, uptodate);
+-    }
+-    if (!end_that_request_first (ti->current_request, uptodate, "tBLK")) {
+-#ifndef DEVICE_NO_RANDOM
+-	add_blkdev_randomness (MAJOR (ti->current_request->rq_dev));
+-#endif
+-	end_that_request_last (ti->current_request);
+-    }
+-    ti->discipline->free_bread(ti->cqr,ti);
+-    ti->cqr=NULL;
+-    ti->current_request=NULL;
+-    if (tapestate_get(ti)!=TS_NOT_OPER) tapestate_set(ti,TS_IDLE);
+-    return;
+-}
+-
+-static void
+-tapeblock_exec_IO (tape_info_t* ti) {
+-    int rc;
+-    struct request* req;
+-    if (ti->cqr) { // process done/failed request
+-	while ((tapestate_get(ti)==TS_FAILED) &&
+-	    ti->blk_retries>0) {
+-	    ti->blk_retries--;
+-	    ti->position=-1;
+-	    tapestate_set(ti,TS_BLOCK_INIT);
+-#ifdef TAPE_DEBUG
+-	    debug_text_event (tape_debug_area,3,"b:retryreq:");
+-	    debug_int_event (tape_debug_area,3,(long)ti->cqr);
+-#endif
+-	    rc = do_IO (ti->devinfo.irq, ti->cqr->cpaddr, (unsigned long) ti->cqr, 
+-			0x00, ti->cqr->options);
+-	    if (rc) {
+-#ifdef TAPE_DEBUG
+-		debug_text_event (tape_debug_area,3,"b:doIOfail:");
+-		debug_int_event (tape_debug_area,3,(long)ti->cqr);
+-#endif 
+-		continue; // one retry lost 'cause doIO failed
+-	    }
+-	    return;
+-	}
+-	tapeblock_end_request (ti); // check state, inform user, free mem, dev=idl
+-    }
+-    if (ti->cqr!=NULL) BUG(); // tape should be idle now, request should be freed!
+-    if (tapestate_get (ti) == TS_NOT_OPER) {
+-	ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
+-	ti->devinfo.irq=-1;
+-	return;
+-    }
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+-	if (list_empty (&ti->request_queue.queue_head)) {
+-#else
+-	if (ti->request_queue==NULL) {
+-#endif
+-	// nothing more to do or device has dissapeared;)
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"b:Qempty");
+-#endif
+-	tapestate_set(ti,TS_IDLE);
+-	return;
+-    }
+-    // queue is not empty, fetch a request and start IO!
+-    req=ti->current_request=tape_next_request(&ti->request_queue);
+-    if (req==NULL) {
+-	BUG(); // Yo. The queue was not reported empy, but no request found. This is _bad_.
+-    }
+-    if (req->cmd!=READ) { // we only support reading
+-	tapestate_set(ti,TS_FAILED);
+-	tapeblock_end_request (ti); // check state, inform user, free mem, dev=idl
+-	tapestate_set(ti,TS_BLOCK_INIT);
+-	schedule_tapeblock_exec_IO(ti);
+-	return;
+-    }
+-    ti->cqr=ti->discipline->bread(req,ti,tapeblock_major); //build channel program from request
+-    if (!ti->cqr) {
+-	// ccw generation failed. we try again later.
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,3,"b:cqrNULL");
+-#endif
+-	schedule_tapeblock_exec_IO(ti);
+-	ti->current_request=NULL;
+-	return;
+-    }
+-    ti->blk_retries = TAPEBLOCK_RETRIES;
+-    rc= do_IO (ti->devinfo.irq, ti->cqr->cpaddr, 
+-	       (unsigned long) ti->cqr, 0x00, ti->cqr->options);
+-    if (rc) {
+-	// okay. ssch failed. we try later.
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,3,"b:doIOfail");
+-#endif
+-	ti->discipline->free_bread(ti->cqr,ti);
+-	ti->cqr=NULL;
+-	ti->current_request=NULL;
+-	schedule_tapeblock_exec_IO(ti);
+-	return;
+-    }
+-    // our request is in IO. we remove it from the queue and exit
+-    tape_dequeue_request (&ti->request_queue,req);
+-}
+-
+-static void 
+-do_tape_request (request_queue_t * queue) {
+-    tape_info_t* ti;
+-    long lockflags;
+-    for (ti=first_tape_info;
+-	 ((ti!=NULL) && ((&ti->request_queue)!=queue));
+-	 ti=ti->next);
+-    if (ti==NULL) BUG();
+-    s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-    if (tapestate_get(ti)!=TS_IDLE) {
+-	s390irq_spin_unlock_irqrestore(ti->devinfo.irq,lockflags);
+-	return;
+-    }
+-    if (tapestate_get(ti)!=TS_IDLE) BUG();
+-    tapestate_set(ti,TS_BLOCK_INIT);
+-    tapeblock_exec_IO(ti);
+-    s390irq_spin_unlock_irqrestore(ti->devinfo.irq,lockflags);
+-}
+-
+-static void
+-run_tapeblock_exec_IO (tape_info_t* ti) {
+-    long flags_390irq,flags_ior;
+-    spin_lock_irqsave (&io_request_lock, flags_ior);
+-    s390irq_spin_lock_irqsave(ti->devinfo.irq,flags_390irq);
+-    atomic_set(&ti->bh_scheduled,0);
+-    tapeblock_exec_IO(ti);
+-    s390irq_spin_unlock_irqrestore(ti->devinfo.irq,flags_390irq);
+-    spin_unlock_irqrestore (&io_request_lock, flags_ior);
+-}
+-
+-void
+-schedule_tapeblock_exec_IO (tape_info_t *ti)
+-{
+-	/* Protect against rescheduling, when already running */
+-        if (atomic_compare_and_swap(0,1,&ti->bh_scheduled)) {
+-                return;
+-        }
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+-	INIT_LIST_HEAD(&ti->bh_tq.list);
+-#endif
+-	ti->bh_tq.sync = 0;
+-	ti->bh_tq.routine = (void *) (void *) run_tapeblock_exec_IO;
+-	ti->bh_tq.data = ti;
+-
+-	queue_task (&ti->bh_tq, &tq_immediate);
+-	mark_bh (IMMEDIATE_BH);
+-	return;
+-}
+-
+-/* wrappers around do_tape_request for different kernel versions */
+-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98))
+-static void tape_request_fn (void) {
+-    tape_info_t* ti=first_tape_info;
+-    while (ti!=NULL) {
+-	do_tape_request(&ti->request_queue);
+-	ti=ti->next;
+-    }
+-}
+-#else
+-static void  tape_request_fn (request_queue_t* queue) {
+-    do_tape_request(queue);
+-}
+-#endif
+-
+-static request_queue_t* tapeblock_getqueue (kdev_t kdev) {
+-    tape_info_t* ti=first_tape_info;
+-    while ((ti!=NULL) && (MINOR(kdev)!=ti->blk_minor)) 
+-        ti=ti->next;
+-    if (ti!=NULL) return &ti->request_queue;
+-    return NULL;
+-}
+-
+-int tapeblock_mediumdetect(tape_info_t* ti) {
+-        ccw_req_t* cqr;
+-    int losize=1,hisize=1,rc;
+-    long lockflags;
+-#ifdef TAPE_DEBUG
+-    debug_text_event (tape_debug_area,3,"b:medDet");
+-#endif
+-    PRINT_WARN("Detecting media size. This will take _long_, so get yourself a coffee...\n");
+-    while (1) { //is interruped by break
+-	hisize=hisize << 1; // try twice the size tested before 
+-	cqr=ti->discipline->mtseek (ti, hisize);
+-	if (cqr == NULL) {
+-#ifdef TAPE_DEBUG
+-	    debug_text_event (tape_debug_area,6,"b:ccwg fail");
+-#endif
+-	    return -ENOSPC;
+-	}
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->cqr = cqr;
+-	ti->wanna_wakeup=0;
+-	rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	if (rc) return -EIO;
+-	wait_event_interruptible (ti->wq,ti->wanna_wakeup);
+-	ti->cqr = NULL;
+-	tape_free_request (cqr);
+-	if (ti->kernbuf) {
+-	    kfree (ti->kernbuf);
+-	    ti->kernbuf=NULL;
+-	}
+-	if (signal_pending (current)) {
+-		tapestate_set (ti, TS_IDLE);
+-		return -ERESTARTSYS;
+-	}
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	if (tapestate_get (ti) == TS_FAILED) {
+-		tapestate_set (ti, TS_IDLE);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		break;
+-	}
+-	if (tapestate_get (ti) == TS_NOT_OPER) {
+-	    ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
+-	    ti->devinfo.irq=-1;
+-	    s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags);
+-	    return -ENODEV;
+-	}
+-	if (tapestate_get (ti) != TS_DONE) {
+-		tapestate_set (ti, TS_IDLE);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		return -EIO;
+-	}
+-	tapestate_set (ti, TS_IDLE);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	losize=hisize;
+-    }
+-    cqr = ti->discipline->mtrew (ti, 1);
+-    if (cqr == NULL) {
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"b:ccwg fail");
+-#endif
+-	return -ENOSPC;
+-    }
+-    s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-    ti->cqr = cqr;
+-    ti->wanna_wakeup=0;
+-    rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+-    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-    wait_event_interruptible (ti->wq,ti->wanna_wakeup);
+-    ti->cqr = NULL;
+-    tape_free_request (cqr);
+-    if (signal_pending (current)) {
+-	tapestate_set (ti, TS_IDLE);
+-	return -ERESTARTSYS;
+-    }
+-    s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-    if (tapestate_get (ti) == TS_FAILED) {
+-	tapestate_set (ti, TS_IDLE);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	return -EIO;
+-    }
+-    if (tapestate_get (ti) == TS_NOT_OPER) {
+-	ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
+-	ti->devinfo.irq=-1;
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags);
+-	return -ENODEV;
+-    }
+-    if (tapestate_get (ti) != TS_DONE) {
+-	tapestate_set (ti, TS_IDLE);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	return -EIO;
+-    }
+-    tapestate_set (ti, TS_IDLE);
+-    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-    while (losize!=hisize) {
+-	cqr=ti->discipline->mtseek (ti, (hisize+losize)/2+1);
+-	if (cqr == NULL) {
+-#ifdef TAPE_DEBUG
+-	    debug_text_event (tape_debug_area,6,"b:ccwg fail");
+-#endif
+-	    return -ENOSPC;
+-	}
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	ti->cqr = cqr;
+-	ti->wanna_wakeup=0;
+-	rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	if (rc) return -EIO;
+-	wait_event_interruptible (ti->wq,ti->wanna_wakeup);
+-	ti->cqr = NULL;
+-	tape_free_request (cqr);
+-	if (ti->kernbuf) {
+-	    kfree (ti->kernbuf);
+-	    ti->kernbuf=NULL;
+-	}
+-	if (signal_pending (current)) {
+-		tapestate_set (ti, TS_IDLE);
+-		return -ERESTARTSYS;
+-	}
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	if (tapestate_get (ti) == TS_NOT_OPER) {
+-	    ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
+-	    ti->devinfo.irq=-1;
+-	    s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags);
+-	    return -ENODEV;
+-	}
+-	if (tapestate_get (ti) == TS_FAILED) {
+-		tapestate_set (ti, TS_IDLE);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		hisize=(hisize+losize)/2;
+-		cqr = ti->discipline->mtrew (ti, 1);
+-		if (cqr == NULL) {
+-#ifdef TAPE_DEBUG
+-		    debug_text_event (tape_debug_area,6,"b:ccwg fail");
+-#endif
+-		    return -ENOSPC;
+-		}
+-		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-		ti->cqr = cqr;
+-		ti->wanna_wakeup=0;
+-		rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		wait_event_interruptible (ti->wq,ti->wanna_wakeup);
+-		ti->cqr = NULL;
+-		tape_free_request (cqr);
+-		if (signal_pending (current)) {
+-		    tapestate_set (ti, TS_IDLE);
+-		    return -ERESTARTSYS;
+-		}
+-		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-		if (tapestate_get (ti) == TS_FAILED) {
+-		    tapestate_set (ti, TS_IDLE);
+-		    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		    return -EIO;
+-		}
+-		if (tapestate_get (ti) != TS_DONE) {
+-		    tapestate_set (ti, TS_IDLE);
+-		    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		    return -EIO;
+-		}
+-		tapestate_set (ti, TS_IDLE);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		continue;
+-	}
+-	if (tapestate_get (ti) != TS_DONE) {
+-		tapestate_set (ti, TS_IDLE);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		return -EIO;
+-	}
+-	tapestate_set (ti, TS_IDLE);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	losize=(hisize+losize)/2+1;
+-    }
+-    blk_size[tapeblock_major][ti->blk_minor]=(losize)*(blksize_size[tapeblock_major][ti->blk_minor]/1024);
+-    return 0;
+-}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tapeblock.h drivers/s390/char/tapeblock.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/tapeblock.h	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tapeblock.h	2006-02-12 12:47:23.000000000 -0700
+@@ -1,36 +0,0 @@
+-
+-/***************************************************************************
+- *
+- *  drivers/s390/char/tapechar.h
+- *    character device frontend for tape device driver
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s): Carsten Otte <cotte at de.ibm.com>
+- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *
+- *
+- ****************************************************************************
+- */
+-
+-#ifndef TAPEBLOCK_H
+-#define TAPEBLOCK_H
+-#include <linux/config.h>
+-#define PARTN_BITS 0
+-
+-#define TAPEBLOCK_READAHEAD 30
+-#define TAPEBLOCK_MAJOR 0
+-
+-#define TAPEBLOCK_DEFAULTMODE 0060644
+-
+-int tapeblock_open(struct inode *, struct file *);
+-int tapeblock_release(struct inode *, struct file *);
+-void tapeblock_setup(tape_info_t* ti);
+-void schedule_tapeblock_exec_IO (tape_info_t *ti);
+-int tapeblock_mediumdetect(tape_info_t* ti);
+-#ifdef CONFIG_DEVFS_FS
+-void tapeblock_mkdevfstree (tape_info_t* ti);
+-#endif
+-int tapeblock_init (void);
+-void tapeblock_uninit (void);
+-#endif
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape.c drivers/s390/char/tape.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape.c	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tape.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1,1120 +0,0 @@
+-
+-/***********************************************************************
+- *  drivers/s390/char/tape.c
+- *    tape device driver for S/390 and zSeries tapes.
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s): Carsten Otte <cotte at de.ibm.com>
+- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *
+- ***********************************************************************
+- */
+-
+-#include "tapedefs.h"
+-
+-#include <linux/config.h>
+-#include <linux/stddef.h>
+-#include <linux/kernel.h>
+-#include <linux/version.h>
+-#include <linux/proc_fs.h>
+-#include <linux/init.h>
+-#include <asm/types.h>
+-#include <asm/ccwcache.h>
+-#include <asm/idals.h>
+-#include <asm/ebcdic.h>
+-#include <linux/compatmac.h>
+-#ifdef MODULE
+-#include <linux/module.h>
+-#endif   
+-#include <asm/debug.h>
+-#ifdef CONFIG_S390_TAPE_DYNAMIC
+-#include <asm/s390dyn.h>
+-#endif
+-#include "tape.h"
+-#ifdef CONFIG_S390_TAPE_3490
+-#include "tape3490.h"
+-#endif
+-#ifdef CONFIG_S390_TAPE_3480
+-#include "tape3480.h"
+-#endif
+-#ifdef CONFIG_S390_TAPE_BLOCK
+-#include "tapeblock.h"
+-#endif
+-#ifdef CONFIG_S390_TAPE_CHAR
+-#include "tapechar.h"
+-#endif
+-#ifdef CONFIG_PROC_FS
+-#include <linux/vmalloc.h>
+-#endif
+-#define PRINTK_HEADER "T390:"
+-
+-
+-/* state handling routines */
+-inline void tapestate_set (tape_info_t * ti, int newstate);
+-inline int tapestate_get (tape_info_t * ti);
+-void tapestate_event (tape_info_t * ti, int event);
+-
+-/* our globals */
+-tape_info_t *first_tape_info = NULL;
+-tape_discipline_t *first_discipline = NULL;
+-tape_frontend_t *first_frontend = NULL;
+-devreg_t* tape_devreg[128];
+-int devregct=0;
+-
+-#ifdef TAPE_DEBUG
+-debug_info_t *tape_debug_area = NULL;
+-#endif
+-
+-char* state_verbose[TS_SIZE]={
+-    "TS_UNUSED",  "TS_IDLE", "TS_DONE", "TS_FAILED",
+-    "TS_BLOCK_INIT",
+-    "TS_BSB_INIT",
+-    "TS_BSF_INIT",
+-    "TS_DSE_INIT",
+-    "TS_EGA_INIT",
+-    "TS_FSB_INIT",
+-    "TS_FSF_INIT",
+-    "TS_LDI_INIT",
+-    "TS_LBL_INIT",
+-    "TS_MSE_INIT",
+-    "TS_NOP_INIT",
+-    "TS_RBA_INIT",
+-    "TS_RBI_INIT",
+-    "TS_RBU_INIT",
+-    "TS_RBL_INIT",
+-    "TS_RDC_INIT",
+-    "TS_RFO_INIT",
+-    "TS_RSD_INIT",
+-    "TS_REW_INIT",
+-    "TS_REW_RELEASE_INIT",
+-    "TS_RUN_INIT",
+-    "TS_SEN_INIT",
+-    "TS_SID_INIT",
+-    "TS_SNP_INIT",
+-    "TS_SPG_INIT",
+-    "TS_SWI_INIT",
+-    "TS_SMR_INIT",
+-    "TS_SYN_INIT",
+-    "TS_TIO_INIT",
+-    "TS_UNA_INIT",
+-    "TS_WRI_INIT",
+-    "TS_WTM_INIT",
+-    "TS_NOT_OPER"};
+-
+-char* event_verbose[TE_SIZE]= {
+-    "TE_START", "TE_DONE", "TE_FAILED", "TE_ERROR", "TE_OTHER"};
+-
+-/* our root devfs handle */
+-#ifdef CONFIG_DEVFS_FS
+-devfs_handle_t tape_devfs_root_entry;
+-
+-inline void
+-tape_mkdevfsroots (tape_info_t* ti) 
+-{
+-    char devno [5];
+-    sprintf (devno,"%04x",ti->devinfo.devno);
+-    ti->devfs_dir=devfs_mk_dir (tape_devfs_root_entry, devno, ti);
+-}
+-
+-inline void
+-tape_rmdevfsroots (tape_info_t* ti)
+-{
+-    devfs_unregister (ti->devfs_dir);
+-}
+-#endif
+-
+-#ifdef CONFIG_PROC_FS
+-/* our proc tapedevices entry */
+-static struct proc_dir_entry *tape_devices_entry;
+-
+-typedef struct {
+-	char *data;
+-	int len;
+-} tempinfo_t;
+-
+-
+-static int
+-tape_devices_open (struct inode *inode, struct file *file)
+-{
+-    int size=80;
+-    tape_info_t* ti;
+-    tempinfo_t* tempinfo;
+-    char* data;
+-    int pos=0;
+-    tempinfo = kmalloc (sizeof(tempinfo_t),GFP_KERNEL);
+-    if (!tempinfo)
+-        return -ENOMEM;
+-    for (ti=first_tape_info;ti!=NULL;ti=ti->next)
+-        size+=80; // FIXME: Guess better!
+-    data=vmalloc(size);
+-    if (!data) {
+-        kfree (tempinfo);
+-        return -ENOMEM;
+-    }
+-    pos+=sprintf(data+pos,"TapeNo\tDevNo\tCuType\tCuModel\tDevType\tDevModel\tState\n");
+-    for (ti=first_tape_info;ti!=NULL;ti=ti->next) {
+-        pos+=sprintf(data+pos,"%d\t%04X\t%04X\t%02X\t%04X\t%02X\t\t%s\n",ti->rew_minor/2,
+-                     ti->devinfo.devno,ti->devinfo.sid_data.cu_type,
+-                     ti->devinfo.sid_data.cu_model,ti->devinfo.sid_data.dev_type,
+-                     ti->devinfo.sid_data.dev_model,((tapestate_get(ti) >= 0) &&
+-                                                       (tapestate_get(ti) < TS_SIZE)) ?
+-                     state_verbose[tapestate_get (ti)] : "TS UNKNOWN");
+-    }
+-    tempinfo->len=pos;
+-    tempinfo->data=data;
+-    file->private_data= (void*) tempinfo;
+-#ifdef MODULE
+-    MOD_INC_USE_COUNT;
+-#endif
+-    return 0;
+-}
+-
+-static ssize_t
+-tape_devices_read (struct file *file, char *user_buf, size_t user_len, loff_t * offset)
+-{
+-	loff_t len;
+-	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+-
+-	if (*offset >= p_info->len) {
+-		return 0;	/* EOF */
+-	} else {
+-		len =  user_len<(p_info->len - *offset)?user_len:(p_info->len - *offset);
+-		if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
+-			return -EFAULT;
+-		(*offset) += len;
+-		return len;	/* number of bytes "read" */
+-	}
+-}
+-
+-static int
+-tape_devices_release (struct inode *inode, struct file *file)
+-{
+-	int rc = 0;
+-	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+-	if (p_info) {
+-		if (p_info->data)
+-			vfree (p_info->data);
+-		kfree (p_info);
+-	}
+-#ifdef MODULE
+-        MOD_DEC_USE_COUNT;
+-#endif
+-	return rc;
+-}
+-
+-static struct file_operations tape_devices_file_ops =
+-{
+-	read:tape_devices_read,	/* read */
+-	open:tape_devices_open,	/* open */
+-	release:tape_devices_release,	/* close */
+-};
+-
+-static struct inode_operations tape_devices_inode_ops =
+-{
+-#if !(LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+-	default_file_ops:&tape_devices_file_ops		/* file ops */
+-#endif				/* LINUX_IS_24 */
+-};
+-#endif /* CONFIG_PROC_FS */
+-
+-/* SECTION: Parameters for tape */
+-char *tape[256] = { NULL, };
+-
+-#ifndef MODULE
+-static char tape_parm_string[1024] __initdata = { 0, };
+-static void
+-tape_split_parm_string (char *str)
+-{
+-	char *tmp = str;
+-	int count = 0;
+-	while (tmp != NULL && *tmp != '\0') {
+-		char *end;
+-		int len;
+-		end = strchr (tmp, ',');
+-		if (end == NULL) {
+-			len = strlen (tmp) + 1;
+-		} else {
+-			len = (long) end - (long) tmp + 1;
+-			*end = '\0';
+-			end++;
+-		}
+-		tape[count] = kmalloc (len * sizeof (char), GFP_ATOMIC);
+-		if (tape[count] == NULL) {
+-			printk (KERN_WARNING PRINTK_HEADER
+-				"can't store tape= parameter no %d\n",
+-				count + 1);
+-			break;
+-		}
+-		memset (tape[count], 0, len * sizeof (char));
+-		memcpy (tape[count], tmp, len * sizeof (char));
+-		count++;
+-		tmp = end;
+-	};
+-}
+-
+-void __init
+-tape_parm_setup (char *str, int *ints)
+-{
+-	int len = strlen (tape_parm_string);
+-	if (len != 0) {
+-		strcat (tape_parm_string, ",");
+-	}
+-	strcat (tape_parm_string, str);
+-}
+-
+-int __init
+-tape_parm_call_setup (char *str)
+-{
+-	int dummy;
+-	tape_parm_setup (str, &dummy);
+-	return 1;
+-}
+-
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,16))
+-__setup("tape=", tape_parm_call_setup);
+-#endif   /* kernel <2.2.19 */
+-#endif   /* not defined MODULE */
+-
+-static inline int
+-tape_parm_strtoul (char *str, char **stra)
+-{
+-	char *temp = str;
+-	int val;
+-	if (*temp == '0') {
+-		temp++;		/* strip leading zero */
+-		if (*temp == 'x')
+-			temp++;	/* strip leading x */
+-	}
+-	val = simple_strtoul (temp, &temp, 16);	/* interpret anything as hex */
+-	*stra = temp;
+-	return val;
+-}
+-
+-static inline devreg_t *
+-tape_create_devreg (int devno)
+-{
+-	devreg_t *devreg = kmalloc (sizeof (devreg_t), GFP_KERNEL);
+-	if (devreg != NULL) {
+-		memset (devreg, 0, sizeof (devreg_t));
+-		devreg->ci.devno = devno;
+-		devreg->flag = DEVREG_TYPE_DEVNO;
+-		devreg->oper_func = tape_oper_handler;
+-	}
+-	return devreg;
+-}
+-
+-static inline void
+-tape_parm_parse (char **str)
+-{
+-	char *temp;
+-	int from, to,i,irq=0,rc,retries=0,tape_num=0;
+-        s390_dev_info_t dinfo;
+-        tape_info_t* ti,*tempti;
+-        tape_discipline_t* disc;
+-        long lockflags;
+-	if (*str==NULL) {
+-            /* no params present -> leave */
+-            return;
+-	}
+-	while (*str) {
+-		temp = *str;
+-		from = 0;
+-		to = 0;
+-
+-                /* turn off autodetect mode, if any range is present */
+-                from = tape_parm_strtoul (temp, &temp);
+-                to = from;
+-                if (*temp == '-') {
+-                    temp++;
+-                    to = tape_parm_strtoul (temp, &temp);
+-                }
+-                for (i=from;i<=to;i++) {
+-                    retries=0;
+-                    // register for attch/detach of a devno
+-                    tape_devreg[devregct]=tape_create_devreg(i);
+-                    if (tape_devreg[devregct]==NULL) {
+-                        PRINT_WARN ("Could not create devreg for devno %04x, dyn. attach for this devno deactivated.\n",i);
+-                    } else {
+-                        s390_device_register (tape_devreg[devregct++]);
+-                    }
+-                    // we are activating a device if it is present
+-                    for (irq = get_irq_first(); irq!=-ENODEV; irq=get_irq_next(irq)) {
+-                        rc = get_dev_info_by_irq (irq, &dinfo);
+-                     
+-                        disc = first_discipline;
+-                        while ((dinfo.devno == i) && (disc != NULL) && (disc->cu_type != dinfo.sid_data.cu_type))
+-                            disc = (tape_discipline_t *) (disc->next);
+-                        if ((disc == NULL) || (rc == -ENODEV) || (i!=dinfo.devno)) {
+-                            continue;
+-                        }
+-#ifdef TAPE_DEBUG
+-                        debug_text_event (tape_debug_area,3,"det irq:  ");
+-                        debug_int_event (tape_debug_area,3,irq);
+-                        debug_text_event (tape_debug_area,3,"cu:       ");
+-                        debug_int_event (tape_debug_area,3,disc->cu_type);
+-#endif /* TAPE_DEBUG */
+-                        PRINT_INFO ("using devno %04x with discipline %04x on irq %d as tape device %d\n",dinfo.devno,dinfo.sid_data.cu_type,irq,tape_num/2);
+-                        /* Allocate tape structure  */
+-                        ti = kmalloc (sizeof (tape_info_t), GFP_ATOMIC);
+-                        if (ti == NULL) {
+-#ifdef TAPE_DEBUG
+-                            debug_text_exception (tape_debug_area,3,"ti:no mem ");
+-#endif /* TAPE_DEBUG */
+-                            PRINT_INFO ("tape: can't allocate memory for "
+-                                        "tape info structure\n");
+-                            continue;
+-                        }
+-                        memset(ti,0,sizeof(tape_info_t));
+-                        ti->discipline = disc;
+-                        disc->tape = ti;
+-                        rc = tape_setup (ti, irq, tape_num);
+-                        if (rc) {
+-#ifdef TAPE_DEBUG
+-                            debug_text_event (tape_debug_area,3,"tsetup err");
+-                            debug_int_exception (tape_debug_area,3,rc);
+-#endif /* TAPE_DEBUG */
+-                            kfree (ti);
+-                        } else {
+-                            s390irq_spin_lock_irqsave (irq, lockflags);
+-                            if (first_tape_info == NULL) {
+-                                first_tape_info = ti;
+-                            } else {
+-                                tempti = first_tape_info;
+-                                while (tempti->next != NULL)
+-                                    tempti = tempti->next;
+-                                tempti->next = ti;
+-                            }
+-                            s390irq_spin_unlock_irqrestore (irq, lockflags);
+-                        }
+-                    }
+-                    tape_num+=2;
+-                }
+-                str++;
+-        }
+-}
+-
+-
+-/* SECTION: Managing wrappers for ccwcache */
+-
+-#define TAPE_EMERGENCY_REQUESTS 16
+-
+-static ccw_req_t *tape_emergency_req[TAPE_EMERGENCY_REQUESTS] =
+-{NULL,};
+-static spinlock_t tape_emergency_req_lock = SPIN_LOCK_UNLOCKED;
+-
+-static void
+-tape_init_emergency_req (void)
+-{
+-	int i;
+-	for (i = 0; i < TAPE_EMERGENCY_REQUESTS; i++) {
+-		tape_emergency_req[i] = (ccw_req_t *) get_free_page (GFP_KERNEL);
+-	}
+-}
+-
+-#ifdef MODULE // We only cleanup the emergency requests on module unload.
+-static void
+-tape_cleanup_emergency_req (void)
+-{
+-	int i;
+-	for (i = 0; i < TAPE_EMERGENCY_REQUESTS; i++) {
+-		if (tape_emergency_req[i])
+-			free_page ((long) (tape_emergency_req[i]));
+-		else
+-			printk (KERN_WARNING PRINTK_HEADER "losing one page for 'in-use' emergency request\n");
+-	}
+-}
+-#endif
+-
+-ccw_req_t *
+-tape_alloc_request (char *magic, int cplength, int datasize)
+-{
+-	ccw_req_t *rv = NULL;
+-	int i;
+-	if ((rv = ccw_alloc_request (magic, cplength, datasize)) != NULL) {
+-		return rv;
+-	}
+-	if (cplength * sizeof (ccw1_t) + datasize + sizeof (ccw_req_t) > PAGE_SIZE) {
+-		return NULL;
+-	}
+-	spin_lock (&tape_emergency_req_lock);
+-	for (i = 0; i < TAPE_EMERGENCY_REQUESTS; i++) {
+-		if (tape_emergency_req[i] != NULL) {
+-			rv = tape_emergency_req[i];
+-			tape_emergency_req[i] = NULL;
+-		}
+-	}
+-	spin_unlock (&tape_emergency_req_lock);
+-	if (rv) {
+-		memset (rv, 0, PAGE_SIZE);
+-		rv->cache = (kmem_cache_t *) (tape_emergency_req + i);
+-		strncpy ((char *) (&rv->magic), magic, 4);
+-		ASCEBC ((char *) (&rv->magic), 4);
+-		rv->cplength = cplength;
+-		rv->datasize = datasize;
+-		rv->data = (void *) ((long) rv + PAGE_SIZE - datasize);
+-		rv->cpaddr = (ccw1_t *) ((long) rv + sizeof (ccw_req_t));
+-	}
+-	return rv;
+-}
+-
+-void
+-tape_free_request (ccw_req_t * request)
+-{
+-	if (request->cache >= (kmem_cache_t *) tape_emergency_req &&
+-	    request->cache <= (kmem_cache_t *) (tape_emergency_req + TAPE_EMERGENCY_REQUESTS)) {
+-		*((ccw_req_t **) (request->cache)) = request;
+-	} else {
+-		clear_normalized_cda ((ccw1_t *) (request->cpaddr));	// avoid memory leak caused by modeset_byte
+-		ccw_free_request (request);
+-	}
+-}
+-
+-/*
+- * Allocate a ccw request and reserve it for tape driver
+- */
+-inline
+- ccw_req_t *
+-tape_alloc_ccw_req (tape_info_t * ti, int cplength, int datasize)
+-{
+-	char tape_magic_id[] = "tape";
+-	ccw_req_t *cqr = NULL;
+-
+-	if (!ti)
+-		return NULL;
+-	cqr = tape_alloc_request (tape_magic_id, cplength, datasize);
+-
+-	if (!cqr) {
+-#ifdef TAPE_DEBUG
+-		PRINT_WARN ("empty CQR generated\n");
+-#endif
+-	}
+-	cqr->magic = TAPE_MAGIC;	/* sets an identifier for tape driver   */
+-	cqr->device = ti;	/* save pointer to tape info    */
+-	return cqr;
+-}
+-
+-/*
+- * Find the tape_info_t structure associated with irq
+- */
+-static inline tape_info_t *
+-tapedev_find_info (int irq)
+-{
+-	tape_info_t *ti;
+-
+-	ti = first_tape_info;
+-	if (ti != NULL)
+-		do {
+-			if (ti->devinfo.irq == irq)
+-				break;
+-		} while ((ti = (tape_info_t *) ti->next) != NULL);
+-	return ti;
+-}
+-
+-#define QUEUE_THRESHOLD 5
+-
+-/*
+- * Tape interrupt routine, called from Ingo's I/O layer
+- */
+-void
+-tape_irq (int irq, void *int_parm, struct pt_regs *regs)
+-{
+-	tape_info_t *ti = tapedev_find_info (irq);
+-
+-	/* analyse devstat and fire event */
+-	if (ti->devstat.dstat & DEV_STAT_UNIT_CHECK) {
+-		tapestate_event (ti, TE_ERROR);
+-	} else if (ti->devstat.dstat & (DEV_STAT_DEV_END)) {
+-		tapestate_event (ti, TE_DONE);
+-	} else
+-		tapestate_event (ti, TE_OTHER);
+-}
+-
+-int 
+-tape_oper_handler ( int irq, struct _devreg *dreg) {
+-    tape_info_t* ti=first_tape_info;
+-    tape_info_t* newtape;
+-    int rc,tape_num,retries=0,i;
+-    s390_dev_info_t dinfo;
+-    tape_discipline_t* disc;
+-#ifdef CONFIG_DEVFS_FS
+-    tape_frontend_t* frontend;
+-#endif
+-    long lockflags;
+-    while ((ti!=NULL) && (ti->devinfo.irq!=irq)) 
+-        ti=ti->next;
+-    if (ti!=NULL) {
+-        // irq is (still) used by tape. tell ingo to try again later
+-        PRINT_WARN ("Oper handler for irq %d called while irq still (internaly?) used.\n",irq);
+-        return -EAGAIN;
+-    }
+-    // irq is not used by tape
+-    rc = get_dev_info_by_irq (irq, &dinfo);
+-    if (rc == -ENODEV) {
+-        retries++;
+-        rc = get_dev_info_by_irq (irq, &dinfo);
+-        if (retries > 5) {
+-            PRINT_WARN ("No device information for new dev. could be retrieved.\n");
+-            return -ENODEV;
+-        }
+-    }
+-    disc = first_discipline;
+-    while ((disc != NULL) && (disc->cu_type != dinfo.sid_data.cu_type))
+-        disc = (tape_discipline_t *) (disc->next);
+-    if (disc == NULL)
+-        PRINT_WARN ("No matching discipline for cu_type %x found, ignoring device %04x.\n",dinfo.sid_data.cu_type,dinfo.devno);
+-    if (rc == -ENODEV) 
+-        PRINT_WARN ("No device information for new dev. could be retrieved.\n");
+-    if ((disc == NULL) || (rc == -ENODEV))
+-        return -ENODEV;
+-    
+-    /* Allocate tape structure  */
+-    ti = kmalloc (sizeof (tape_info_t), GFP_ATOMIC);
+-    if (ti == NULL) {
+-        PRINT_INFO ( "tape: can't allocate memory for "
+-                    "tape info structure\n");
+-        return -ENOBUFS;
+-    }
+-    memset(ti,0,sizeof(tape_info_t));
+-    ti->discipline = disc;
+-    disc->tape = ti;
+-    tape_num=0;
+-    if (*tape) {
+-        // we have static device ranges, so fingure out the tape_num of the attached tape
+-        for (i=0;i<devregct;i++)
+-            if (tape_devreg[i]->ci.devno==dinfo.devno) {
+-                tape_num=2*i;
+-                break;
+-            }
+-    } else {
+-        // we are running in autoprobe mode, find a free tape_num
+-        newtape=first_tape_info;
+-        while (newtape!=NULL) {
+-            if (newtape->rew_minor==tape_num) {
+-                // tape num in use. try next one
+-                tape_num+=2;
+-                newtape=first_tape_info;
+-            } else {
+-                // tape num not used by newtape. look at next tape info
+-                newtape=newtape->next;
+-            }
+-        }
+-    }
+-    rc = tape_setup (ti, irq, tape_num);
+-    if (rc) {
+-        kfree (ti);
+-        return -ENOBUFS;
+-    }
+-#ifdef CONFIG_DEVFS_FS
+-    for (frontend=first_frontend;frontend!=NULL;frontend=frontend->next) 
+-        frontend->mkdevfstree(ti);
+-#endif
+-    s390irq_spin_lock_irqsave (irq,lockflags);
+-    if (first_tape_info == NULL) {
+-        first_tape_info = ti;
+-    } else {
+-        newtape = first_tape_info;
+-        while (newtape->next != NULL)
+-            newtape = newtape->next;
+-        newtape->next = ti;
+-    }
+-    s390irq_spin_unlock_irqrestore (irq, lockflags);
+-    return 0;
+-}
+-
+-
+-static void
+-tape_noper_handler ( int irq, int status ) {
+-    tape_info_t *ti=first_tape_info;
+-    tape_info_t *lastti;
+-#ifdef CONFIG_DEVFS_FS
+-    tape_frontend_t *frontend;
+-#endif
+-    long lockflags;
+-    s390irq_spin_lock_irqsave(irq,lockflags);
+-    while (ti!=NULL && ti->devinfo.irq!=irq) ti=ti->next;
+-    if (ti==NULL) return;
+-    if (tapestate_get(ti)!=TS_UNUSED) {
+-        // device is in use!
+-        PRINT_WARN ("Tape #%d was detached while it was busy. Expect errors!",ti->blk_minor/2);
+-        tapestate_set(ti,TS_NOT_OPER);
+-        ti->rc=-ENODEV; 
+-	ti->wanna_wakeup=1;
+-	switch (tapestate_get(ti)) {
+-	case TS_REW_RELEASE_INIT:
+-	    tapestate_set(ti,TS_NOT_OPER);
+-	    wake_up (&ti->wq);
+-	    break;
+-#ifdef CONFIG_S390_TAPE_BLOCK
+-	case TS_BLOCK_INIT:
+-	    tapestate_set(ti,TS_NOT_OPER);
+-	    schedule_tapeblock_exec_IO(ti);
+-	    break;
+-#endif
+-	default:
+-	    tapestate_set(ti,TS_NOT_OPER);
+-	    wake_up_interruptible (&ti->wq);
+-	}
+-    } else {
+-        // device is unused!
+-        PRINT_WARN ("Tape #%d was detached.\n",ti->blk_minor/2);
+-        if (ti==first_tape_info) {
+-            first_tape_info=ti->next;
+-        } else {
+-            lastti=first_tape_info;
+-            while (lastti->next!=ti) lastti=lastti->next;
+-            lastti->next=ti->next;
+-        }
+-#ifdef CONFIG_DEVFS_FS
+-        for (frontend=first_frontend;frontend!=NULL;frontend=frontend->next)
+-            frontend->rmdevfstree(ti);
+-        tape_rmdevfsroots(ti);
+-#endif
+-        kfree(ti);
+-    }
+-    s390irq_spin_unlock_irqrestore(irq,lockflags);
+-    return;
+-}
+-
+-
+-void
+-tape_dump_sense (devstat_t * stat)
+-{
+-#ifdef TAPE_DEBUG
+-        int sl;
+-#endif
+-#if 0
+-
+-	PRINT_WARN ("------------I/O resulted in unit check:-----------\n");
+-	for (sl = 0; sl < 4; sl++) {
+-		PRINT_WARN ("Sense:");
+-		for (sct = 0; sct < 8; sct++) {
+-			PRINT_WARN (" %2d:0x%02X", 8 * sl + sct,
+-				    stat->ii.sense.data[8 * sl + sct]);
+-		}
+-		PRINT_WARN ("\n");
+-	}
+-	PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X "
+-		    " %02X%02X%02X%02X %02X%02X%02X%02X \n",
+-		    stat->ii.sense.data[0], stat->ii.sense.data[1],
+-		    stat->ii.sense.data[2], stat->ii.sense.data[3],
+-		    stat->ii.sense.data[4], stat->ii.sense.data[5],
+-		    stat->ii.sense.data[6], stat->ii.sense.data[7],
+-		    stat->ii.sense.data[8], stat->ii.sense.data[9],
+-		    stat->ii.sense.data[10], stat->ii.sense.data[11],
+-		    stat->ii.sense.data[12], stat->ii.sense.data[13],
+-		    stat->ii.sense.data[14], stat->ii.sense.data[15]);
+-	PRINT_INFO ("Sense data: %02X%02X%02X%02X %02X%02X%02X%02X "
+-		    " %02X%02X%02X%02X %02X%02X%02X%02X \n",
+-		    stat->ii.sense.data[16], stat->ii.sense.data[17],
+-		    stat->ii.sense.data[18], stat->ii.sense.data[19],
+-		    stat->ii.sense.data[20], stat->ii.sense.data[21],
+-		    stat->ii.sense.data[22], stat->ii.sense.data[23],
+-		    stat->ii.sense.data[24], stat->ii.sense.data[25],
+-		    stat->ii.sense.data[26], stat->ii.sense.data[27],
+-		    stat->ii.sense.data[28], stat->ii.sense.data[29],
+-		    stat->ii.sense.data[30], stat->ii.sense.data[31]);
+-#endif
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,3,"SENSE:");
+-        for (sl=0;sl<31;sl++) {
+-            debug_int_event (tape_debug_area,3,stat->ii.sense.data[sl]);
+-        }
+-        debug_int_exception (tape_debug_area,3,stat->ii.sense.data[31]);
+-#endif
+-}
+-
+-/*
+- * Setup tape_info_t structure of a tape device
+- */
+-int
+-tape_setup (tape_info_t * ti, int irq, int minor)
+-{
+-	long lockflags;
+-	int rc = 0;
+-
+-        if (minor>254) {
+-            PRINT_WARN ("Device id %d on irq %d will not be accessible since this driver is restricted to 128 devices.\n",minor/2,irq);
+-            return -EINVAL;
+-        }
+-	rc = get_dev_info_by_irq (irq, &(ti->devinfo));
+-	if (rc == -ENODEV) {	/* end of device list */
+-		return rc;
+-	}
+-	ti->rew_minor = minor;
+-	ti->nor_minor = minor + 1;
+-	ti->blk_minor = minor;
+-#ifdef CONFIG_DEVFS_FS
+-        tape_mkdevfsroots(ti);
+-#endif
+-	/* Register IRQ */
+-#ifdef CONFIG_S390_TAPE_DYNAMIC
+-        rc = s390_request_irq_special (irq, tape_irq, tape_noper_handler,0, "tape", &(ti->devstat));
+-#else
+-	rc = s390_request_irq (irq, tape_irq, 0, "tape", &(ti->devstat));
+-#endif
+-	s390irq_spin_lock_irqsave (irq, lockflags);
+-	ti->next = NULL;
+-	if (rc)
+-            PRINT_WARN ("Cannot register irq %d, rc=%d\n", irq, rc);
+-	init_waitqueue_head (&ti->wq);
+-	ti->kernbuf = ti->userbuf = ti->discdata = NULL;
+-	tapestate_set (ti, TS_UNUSED);
+-        ti->discdata=NULL;
+-	ti->discipline->setup_assist (ti);
+-        ti->wanna_wakeup=0;
+-	s390irq_spin_unlock_irqrestore (irq, lockflags);
+-	return rc;
+-}
+-
+-/*
+- *      tape_init will register the driver for each tape.
+- */
+-int
+-tape_init (void)
+-{
+-	long lockflags;
+-	s390_dev_info_t dinfo;
+-	tape_discipline_t *disc;
+-	tape_info_t *ti = NULL, *tempti = NULL;
+-        char *opt_char,*opt_block,*opt_3490,*opt_3480;
+-	int irq = 0, rc, retries = 0, tape_num = 0;
+-        static int initialized=0;
+-
+-        if (initialized) // Only init the devices once
+-            return 0;
+-        initialized=1;
+-
+-#ifdef TAPE_DEBUG
+-        tape_debug_area = debug_register ( "tape", 3, 2, 10);
+-        debug_register_view(tape_debug_area,&debug_hex_ascii_view);
+-        debug_text_event (tape_debug_area,3,"begin init");
+-#endif /* TAPE_DEBUG */
+-
+-        /* print banner */        
+-        PRINT_WARN ("IBM S/390 Tape Device Driver (v1.01).\n");
+-        PRINT_WARN ("(C) IBM Deutschland Entwicklung GmbH, 2000\n");
+-        opt_char=opt_block=opt_3480=opt_3490="not present";
+-#ifdef CONFIG_S390_TAPE_CHAR
+-        opt_char="built in";
+-#endif
+-#ifdef CONFIG_S390_TAPE_BLOCK
+-        opt_block="built in";
+-#endif
+-#ifdef CONFIG_S390_TAPE_3480
+-        opt_3480="built in";
+-#endif
+-#ifdef CONFIG_S390_TAPE_3490
+-        opt_3490="built in";
+-#endif
+-        /* print feature info */
+-        PRINT_WARN ("character device frontend   : %s\n",opt_char);
+-        PRINT_WARN ("block device frontend       : %s\n",opt_block);
+-        PRINT_WARN ("support for 3480 compatible : %s\n",opt_3480);
+-        PRINT_WARN ("support for 3490 compatible : %s\n",opt_3490);
+-        
+-#ifndef MODULE
+-        tape_split_parm_string(tape_parm_string);
+-#endif
+-        if (*tape) 
+-            PRINT_INFO ("Using ranges supplied in parameters, disabling autoprobe mode.\n");
+-        else
+-            PRINT_INFO ("No parameters supplied, enabling autoprobe mode for all supported devices.\n");
+-#ifdef CONFIG_S390_TAPE_3490
+-        if (*tape)
+-            first_discipline = tape3490_init (0); // no autoprobe for devices
+-        else
+-            first_discipline = tape3490_init (1); // do autoprobe since no parm specified
+-	first_discipline->next = NULL;
+-#endif
+-
+-#ifdef CONFIG_S390_TAPE_3480
+-        if (first_discipline == NULL) {
+-            if (*tape)
+-                first_discipline = tape3480_init (0); // no autoprobe for devices
+-            else 
+-                first_discipline = tape3480_init (1); // do autoprobe since no parm specified
+-            first_discipline->next = NULL;
+-        } else {
+-            if (*tape)
+-                first_discipline->next = tape3480_init (0); // no autoprobe for devices
+-            else
+-                first_discipline->next = tape3480_init (1); // do autoprobe since no parm specified
+-            ((tape_discipline_t*) (first_discipline->next))->next=NULL;
+-        }
+-#endif
+-#ifdef CONFIG_DEVFS_FS
+-        tape_devfs_root_entry=devfs_mk_dir (NULL, "tape", NULL);
+-#endif CONFIG_DEVFS_FS
+-
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,3,"dev detect");
+-#endif /* TAPE_DEBUG */
+-	/* Allocate the tape structures */
+-        if (*tape!=NULL) {
+-            // we have parameters, continue with parsing the parameters and set the devices online
+-            tape_parm_parse (tape);
+-        } else {
+-            // we are running in autodetect mode, search all devices for compatibles
+-            for (irq = get_irq_first(); irq!=-ENODEV; irq=get_irq_next(irq)) {
+-		rc = get_dev_info_by_irq (irq, &dinfo);
+-		disc = first_discipline;
+-		while ((disc != NULL) && (disc->cu_type != dinfo.sid_data.cu_type))
+-                    disc = (tape_discipline_t *) (disc->next);
+-		if ((disc == NULL) || (rc == -ENODEV))
+-                    continue;
+-#ifdef TAPE_DEBUG
+-                debug_text_event (tape_debug_area,3,"det irq:  ");
+-                debug_int_event (tape_debug_area,3,irq);
+-                debug_text_event (tape_debug_area,3,"cu:       ");
+-                debug_int_event (tape_debug_area,3,disc->cu_type);
+-#endif /* TAPE_DEBUG */
+-                PRINT_INFO ("using devno %04x with discipline %04x on irq %d as tape device %d\n",dinfo.devno,dinfo.sid_data.cu_type,irq,tape_num/2);
+-		/* Allocate tape structure  */
+-		ti = kmalloc (sizeof (tape_info_t), GFP_ATOMIC);
+-		if (ti == NULL) {
+-#ifdef TAPE_DEBUG
+-                    debug_text_exception (tape_debug_area,3,"ti:no mem ");
+-#endif /* TAPE_DEBUG */
+-                    PRINT_INFO ("tape: can't allocate memory for "
+-				    "tape info structure\n");
+-                    continue;
+-		}
+-		memset(ti,0,sizeof(tape_info_t));
+-		ti->discipline = disc;
+-		disc->tape = ti;
+-		rc = tape_setup (ti, irq, tape_num);
+-		if (rc) {
+-#ifdef TAPE_DEBUG
+-                    debug_text_event (tape_debug_area,3,"tsetup err");
+-                    debug_int_exception (tape_debug_area,3,rc);
+-#endif /* TAPE_DEBUG */
+-                    kfree (ti);
+-		} else {
+-                    s390irq_spin_lock_irqsave (irq, lockflags);
+-                    if (first_tape_info == NULL) {
+-                        first_tape_info = ti;
+-                    } else {
+-                        tempti = first_tape_info;
+-                        while (tempti->next != NULL)
+-                            tempti = tempti->next;
+-                        tempti->next = ti;
+-                    }
+-                    tape_num += 2;
+-                    s390irq_spin_unlock_irqrestore (irq, lockflags);
+-		}
+-            }
+-        }
+-            
+-	/* Allocate local buffer for the ccwcache       */
+-	tape_init_emergency_req ();
+-#ifdef CONFIG_PROC_FS
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+-	tape_devices_entry = create_proc_entry ("tapedevices",
+-						S_IFREG | S_IRUGO | S_IWUSR,
+-						&proc_root);
+-	tape_devices_entry->proc_fops = &tape_devices_file_ops;
+-	tape_devices_entry->proc_iops = &tape_devices_inode_ops;
+-#else
+-	tape_devices_entry = (struct proc_dir_entry *) kmalloc 
+-            (sizeof (struct proc_dir_entry), GFP_ATOMIC);
+-        if (tape_devices_entry) {
+-            memset (tape_devices_entry, 0, sizeof (struct proc_dir_entry));
+-            tape_devices_entry->name = "tapedevices";
+-            tape_devices_entry->namelen = strlen ("tapedevices");
+-            tape_devices_entry->low_ino = 0;
+-            tape_devices_entry->mode = (S_IFREG | S_IRUGO | S_IWUSR);
+-            tape_devices_entry->nlink = 1;
+-            tape_devices_entry->uid = 0;
+-            tape_devices_entry->gid = 0;
+-            tape_devices_entry->size = 0;
+-            tape_devices_entry->get_info = NULL;
+-            tape_devices_entry->ops = &tape_devices_inode_ops;
+-            proc_register (&proc_root, tape_devices_entry);
+-	}
+-#endif
+-#endif /* CONFIG_PROC_FS */
+-
+-	return 0;
+-}
+-
+-#ifdef MODULE
+-MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte (cotte at de.ibm.com)");
+-MODULE_DESCRIPTION("Linux for S/390 channel attached tape device driver");
+-MODULE_PARM (tape, "1-" __MODULE_STRING (256) "s");
+-
+-int
+-init_module (void)
+-{
+-#ifdef CONFIG_S390_TAPE_CHAR
+-	tapechar_init ();
+-#endif
+-#ifdef CONFIG_S390_TAPE_BLOCK
+-	tapeblock_init ();
+-#endif
+-        return 0;
+-}
+-
+-void
+-cleanup_module (void)
+-{
+-        tape_info_t *ti ,*temp;
+-        tape_frontend_t* frontend, *tempfe;
+-        tape_discipline_t* disc ,*tempdi;
+-        int i;
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"cleaup mod");
+-#endif /* TAPE_DEBUG */
+-
+-        if (*tape) {
+-            // we are running with parameters. we'll now deregister from our devno's
+-            for (i=0;i<devregct;i++) {
+-                s390_device_unregister(tape_devreg[devregct]);
+-            }
+-        }
+-	ti = first_tape_info;
+-	while (ti != NULL) {
+-		temp = ti;
+-		ti = ti->next;
+-                //cleanup a device 
+-#ifdef TAPE_DEBUG
+-                debug_text_event (tape_debug_area,6,"free irq:");
+-                debug_int_event (tape_debug_area,6,temp->devinfo.irq);
+-#endif /* TAPE_DEBUG */
+-		free_irq (temp->devinfo.irq, &(temp->devstat));
+-                if (temp->discdata) kfree (temp->discdata);
+-                if (temp->kernbuf) kfree (temp->kernbuf);
+-                if (temp->cqr) tape_free_request(temp->cqr);
+-#ifdef CONFIG_DEVFS_FS
+-                for (frontend=first_frontend;frontend!=NULL;frontend=frontend->next)
+-                    frontend->rmdevfstree(temp);
+-                tape_rmdevfsroots(temp);
+-#endif
+-		kfree (temp);
+-	}
+-#ifdef CONFIG_DEVFS_FS
+-        devfs_unregister (tape_devfs_root_entry);
+-#endif CONFIG_DEVFS_FS
+-#ifdef CONFIG_PROC_FS
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+-	remove_proc_entry ("tapedevices", &proc_root);
+-#else
+-	proc_unregister (&proc_root, tape_devices_entry->low_ino);
+-	kfree (tape_devices_entry);
+-#endif				/* LINUX_IS_24 */
+-#endif 
+-#ifdef CONFIG_S390_TAPE_CHAR
+-	tapechar_uninit();
+-#endif
+-#ifdef CONFIG_S390_TAPE_BLOCK
+-        tapeblock_uninit();
+-#endif
+-        frontend=first_frontend;
+-	while (frontend != NULL) {
+-		tempfe = frontend;
+-		frontend = frontend->next;
+-		kfree (tempfe);
+-	}
+-        disc=first_discipline;
+-	while (disc != NULL) {
+-                if (*tape)
+-                    disc->shutdown(0);
+-                else
+-                    disc->shutdown(1);
+-		tempdi = disc;
+-		disc = disc->next;
+-		kfree (tempdi);
+-	}
+-	/* Deallocate the local buffer for the ccwcache         */
+-	tape_cleanup_emergency_req ();
+-#ifdef TAPE_DEBUG
+-        debug_unregister (tape_debug_area);
+-#endif /* TAPE_DEBUG */
+-}
+-#endif				/* MODULE */
+-
+-inline void
+-tapestate_set (tape_info_t * ti, int newstate)
+-{
+-    if (ti->tape_state == TS_NOT_OPER) {
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,3,"ts_set err");
+-        debug_text_exception (tape_debug_area,3,"dev n.oper");
+-#endif /* TAPE_DEBUG */
+-    } else {
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,4,"ts. dev:  ");
+-        debug_int_event (tape_debug_area,4,ti->blk_minor);
+-        debug_text_event (tape_debug_area,4,"old ts:   ");
+-        debug_text_event (tape_debug_area,4,(((tapestate_get (ti) < TS_SIZE) &&
+-                                             (tapestate_get (ti) >=0 )) ?
+-                                            state_verbose[tapestate_get (ti)] :
+-                                            "UNKNOWN TS"));
+-        debug_text_event (tape_debug_area,4,"new ts:   ");
+-        debug_text_event (tape_debug_area,4,(((newstate < TS_SIZE) &&
+-                                              (newstate >= 0)) ?
+-                                             state_verbose[newstate] :
+-                                             "UNKNOWN TS"));
+-#endif /* TAPE_DEBUG */
+-	ti->tape_state = newstate;
+-    }
+-}
+-
+-inline int
+-tapestate_get (tape_info_t * ti)
+-{
+-	return (ti->tape_state);
+-}
+-
+-void
+-tapestate_event (tape_info_t * ti, int event)
+-{
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"te! dev:  ");
+-        debug_int_event (tape_debug_area,6,ti->blk_minor);
+-        debug_text_event (tape_debug_area,6,"event:");
+-        debug_text_event (tape_debug_area,6,((event >=0) &&
+-                                            (event < TE_SIZE)) ?
+-                         event_verbose[event] : "TE UNKNOWN");
+-        debug_text_event (tape_debug_area,6,"state:");
+-        debug_text_event (tape_debug_area,6,((tapestate_get(ti) >= 0) &&
+-                                            (tapestate_get(ti) < TS_SIZE)) ?
+-                         state_verbose[tapestate_get (ti)] :
+-                         "TS UNKNOWN");
+-#endif /* TAPE_DEBUG */    
+-        if (event == TE_ERROR) { 
+-            ti->discipline->error_recovery(ti);
+-        } else {
+-            if ((event >= 0) &&
+-                (event < TE_SIZE) &&
+-                (tapestate_get (ti) >= 0) &&
+-                (tapestate_get (ti) < TS_SIZE) &&
+-                ((*(ti->discipline->event_table))[tapestate_get (ti)][event] != NULL))
+-		((*(ti->discipline->event_table))[tapestate_get (ti)][event]) (ti);
+-            else {
+-#ifdef TAPE_DEBUG
+-                debug_text_exception (tape_debug_area,3,"TE UNEXPEC");
+-#endif /* TAPE_DEBUG */
+-		ti->discipline->default_handler (ti);
+-            }
+-        }
+-}
+-
+-/*
+- * Overrides for Emacs so that we follow Linus's tabbing style.
+- * Emacs will notice this stuff at the end of the file and automatically
+- * adjust the settings for this buffer only.  This must remain at the end
+- * of the file.
+- * ---------------------------------------------------------------------------
+- * Local variables:
+- * c-indent-level: 4
+- * c-brace-imaginary-offset: 0
+- * c-brace-offset: -4
+- * c-argdecl-indent: 4
+- * c-label-offset: -4
+- * c-continued-statement-offset: 4
+- * c-continued-brace-offset: 0
+- * indent-tabs-mode: nil
+- * tab-width: 8
+- * End:
+- */
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape_char.c drivers/s390/char/tape_char.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape_char.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/tape_char.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,534 @@
 +/*
-+ *  drivers/s390/char/tape_std.c
-+ *    standard tape device functions for ibm tapes.
++ *  drivers/s390/char/tape_char.c
++ *    character device frontend for tape device driver
 + *
 + *  S390 and zSeries version
 + *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
 + *    Author(s): Carsten Otte <cotte at de.ibm.com>
-+ *		 Michael Holzheu <holzheu at de.ibm.com>
-+ *		 Tuan Ngo-Anh <ngoanh at de.ibm.com>
-+ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
++ *               Michael Holzheu <holzheu at de.ibm.com>
++ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
++ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
 + *               Stefan Bader <shbader at de.ibm.com>
 + */
 +
 +#include <linux/config.h>
 +#include <linux/version.h>
-+#include <linux/stddef.h>
-+#include <linux/kernel.h>
-+#include <linux/timer.h>
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+#include <linux/blkdev.h>
-+#endif
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/proc_fs.h>
++#include <linux/mtio.h>
 +
-+#include <asm/types.h>
-+#include <asm/idals.h>
-+#include <asm/ebcdic.h>
-+#include <asm/tape390.h>
++#include <asm/irq.h>
++#include <asm/s390dyn.h>
++#include <asm/uaccess.h>
 +
 +#define TAPE_DBF_AREA	tape_core_dbf
 +
 +#include "tape.h"
 +#include "tape_std.h"
 +
-+#define PRINTK_HEADER "T3xxx:"
-+#define ZLINUX_PASSWD "zLinux PWD"
-+
-+/*
-+ * tape_std_assign
-+ */
-+int
-+tape_std_assign(struct tape_device *device)
-+{
-+	struct tape_request *request;
-+
-+	request = tape_alloc_request(2, 11);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+
-+	request->op = TO_ASSIGN;
-+
-+	/*
-+	 * From the documentation assign requests should fail with the
-+	 * 'assigned elsewhere' bit set if the tape is already assigned
-+	 * to another host. However, it seems, in reality the request
-+	 * hangs forever. Therfor we just set a timeout for this request.
-+	 */
-+	init_timer(&request->timeout);
-+	request->timeout.expires = jiffies + 2 * HZ;
-+
-+	/* Setup the CCWs */
-+	tape_ccw_cc(request->cpaddr, ASSIGN, 11, request->cpdata);
-+	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
-+
-+	return tape_do_io_free(device, request);
-+}
-+
-+/*
-+ * tape_std_unassign
-+ */
-+int
-+tape_std_unassign (struct tape_device *device)
-+{
-+	struct tape_request *request;
-+
-+	request = tape_alloc_request(2, 11);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_UNASSIGN;
-+	tape_ccw_cc(request->cpaddr, UNASSIGN, 11, request->cpdata);
-+	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
-+	return tape_do_io_free(device, request);
-+}
-+
-+#ifdef TAPE390_FORCE_UNASSIGN
-+/*
-+ * tape_std_force_unassign: forces assignment from another host.
-+ * (Since we need a password this works only with other zLinux hosts!)
-+ */
-+int
-+tape_std_force_unassign(struct tape_device *device)
-+{
-+	struct tape_request *request;
-+	struct tape_ca_data *ca_data1;
-+	struct tape_ca_data *ca_data2;
-+
-+	request = tape_alloc_request(2, 24);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+
-+	request->op = TO_BREAKASS;
-+	ca_data1 = (struct tape_ca_data *)
-+			(((char *) request->cpdata));
-+	ca_data2 = (struct tape_ca_data *)
-+			(((char *) request->cpdata) + 12);
-+
-+	ca_data1->function = 0x80; /* Conditional enable */
-+	strcpy(ca_data1->password, ZLINUX_PASSWD);
-+	ASCEBC(ca_data1->password, 11);
-+	ca_data2->function = 0x40; /* Conditional disable */
-+	memcpy(ca_data2->password, ca_data1->password, 11);
-+	
-+	tape_ccw_cc(request->cpaddr, CONTROL_ACCESS, 12, ca_data1);
-+	tape_ccw_end(request->cpaddr + 1, CONTROL_ACCESS, 12, ca_data2);
-+
-+	return tape_do_io_free(device, request);
-+}
-+#endif
-+
-+/*
-+ * TAPE390_DISPLAY: Show a string on the tape display.
-+ */
-+int
-+tape_std_display(struct tape_device *device, struct display_struct *disp)
-+{
-+	struct tape_request *request;
-+	int rc;
-+
-+	request = tape_alloc_request(2, 17);
-+	if (IS_ERR(request)) {
-+		DBF_EVENT(3, "TAPE: load display failed\n");
-+		return PTR_ERR(request);
-+	}
-+
-+	request->op = TO_DIS;
-+	*(unsigned char *) request->cpdata = disp->cntrl;
-+	DBF_EVENT(5, "TAPE: display cntrl=%04x\n", disp->cntrl);
-+	memcpy(((unsigned char *) request->cpdata) + 1, disp->message1, 8);
-+	memcpy(((unsigned char *) request->cpdata) + 9, disp->message2, 8);
-+	ASCEBC(((unsigned char*) request->cpdata) + 1, 16);
-+
-+	tape_ccw_cc(request->cpaddr, LOAD_DISPLAY, 17, request->cpdata);
-+	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
-+
-+	rc = tape_do_io_interruptible(device, request);
-+	tape_put_request(request);
-+	return rc;
-+}
-+
-+/*
-+ * Read block id.
-+ */
-+int
-+tape_std_read_block_id(struct tape_device *device, unsigned int *bid)
-+{
-+	struct tape_request *request;
-+	struct {
-+		unsigned int	channel_block_id;
-+		unsigned int	device_block_id;
-+	} __attribute__ ((packed)) *rbi_data;
-+	int rc;
-+
-+	request = tape_alloc_request(3, 8);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_RBI;
-+
-+	/* setup ccws */
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_cc(request->cpaddr + 1, READ_BLOCK_ID, 8, request->cpdata);
-+	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
-+
-+	/* execute it */
-+	rc = tape_do_io(device, request);
-+	if (rc == 0) {
-+		/* Get result from read buffer. */
-+		DBF_EVENT(6, "rbi_data = 0x%08x%08x\n",
-+			*((unsigned int *) request->cpdata),
-+			*(((unsigned int *) request->cpdata)+1));
-+		rbi_data = (void *) request->cpdata;
-+		*bid = rbi_data->channel_block_id;
-+	}
-+	tape_put_request(request);
-+	return rc;
-+}
-+
-+/* Seek block id */
-+int
-+tape_std_seek_block_id(struct tape_device *device, unsigned int bid)
-+{
-+	struct tape_request	*request;
-+
-+	request = tape_alloc_request(3, 4);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+
-+	request->op                = TO_LBL;
-+	*(__u32 *) request->cpdata = bid;
-+
-+	/* setup ccws */
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
-+	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
-+
-+	/* execute it */
-+	return tape_do_io_free(device, request);
-+}
-+
-+int
-+tape_std_terminate_write(struct tape_device *device)
-+{
-+	int rc;
-+
-+	if(device->required_tapemarks == 0)
-+		return 0;
-+
-+	DBF_EVENT(5, "(%04x): terminate_write %ixEOF\n",
-+		device->devstat.devno, device->required_tapemarks);
-+
-+	rc = tape_mtop(device, MTWEOF, device->required_tapemarks);
-+	if (rc)
-+		return rc;
-+
-+	device->required_tapemarks = 0;
-+	return tape_mtop(device, MTBSR, 1);
-+}
-+
-+/*
-+ * MTLOAD: Loads the tape.
-+ * The default implementation just wait until the tape medium state changes
-+ * to MS_LOADED.
-+ */
-+int
-+tape_std_mtload(struct tape_device *device, int count)
-+{
-+	return wait_event_interruptible(device->state_change_wq,
-+		(device->medium_state == MS_LOADED));
-+}
-+
-+/*
-+ * MTSETBLK: Set block size.
-+ */
-+int
-+tape_std_mtsetblk(struct tape_device *device, int count)
-+{
-+	struct idal_buffer *new;
-+
-+	DBF_EVENT(6, "tape_std_mtsetblk(%d)\n", count);
-+	if (count <= 0) {
-+		/*
-+		 * Just set block_size to 0. tapechar_read/tapechar_write
-+		 * will realloc the idal buffer if a bigger one than the
-+		 * current is needed.
-+		 */
-+		device->char_data.block_size = 0;
-+		return 0;
-+	}
-+	if (device->char_data.idal_buf != NULL &&
-+	    device->char_data.idal_buf->size == count)
-+		/* We already have a idal buffer of that size. */
-+		return 0;
-+
-+	if (count > MAX_BLOCKSIZE) {
-+		DBF_EVENT(3, "Invalid block size (%ld > %ld) given.\n",
-+			count, MAX_BLOCKSIZE);
-+		PRINT_ERR("Invalid block size (%ld > %ld) given.\n",
-+			count, MAX_BLOCKSIZE);
-+		return -EINVAL;
-+	}
-+
-+	/* Allocate a new idal buffer. */
-+	new = idal_buffer_alloc(count, 0);
-+	if (new == NULL)
-+		return -ENOMEM;
-+	if (device->char_data.idal_buf != NULL)
-+		idal_buffer_free(device->char_data.idal_buf);
-+
-+	device->char_data.idal_buf = new;
-+	device->char_data.block_size = count;
-+	DBF_EVENT(6, "new blocksize is %d\n", device->char_data.block_size);
-+	return 0;
-+}
-+
-+/*
-+ * MTRESET: Set block size to 0.
-+ */
-+int
-+tape_std_mtreset(struct tape_device *device, int count)
-+{
-+	DBF_EVENT(6, "TCHAR:devreset:\n");
-+	device->char_data.block_size = 0;
-+	return 0;
-+}
-+
-+/*
-+ * MTFSF: Forward space over 'count' file marks. The tape is positioned
-+ * at the EOT (End of Tape) side of the file mark.
-+ */
-+int
-+tape_std_mtfsf(struct tape_device *device, int mt_count)
-+{
-+	struct tape_request *request;
-+	ccw1_t *ccw;
-+
-+	request = tape_alloc_request(mt_count + 2, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_FSF;
-+	/* setup ccws */
-+	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
-+			  device->modeset_byte);
-+	ccw = tape_ccw_repeat(ccw, FORSPACEFILE, mt_count);
-+	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
-+}
-+
-+/*
-+ * MTFSR: Forward space over 'count' tape blocks (blocksize is set
-+ * via MTSETBLK.
-+ */
-+int
-+tape_std_mtfsr(struct tape_device *device, int mt_count)
-+{
-+	struct tape_request *request;
-+	ccw1_t *ccw;
-+
-+	request = tape_alloc_request(mt_count + 2, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_FSB;
-+	/* setup ccws */
-+	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
-+			  device->modeset_byte);
-+	ccw = tape_ccw_repeat(ccw, FORSPACEBLOCK, mt_count);
-+	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
-+}
-+
-+/*
-+ * MTBSR: Backward space over 'count' tape blocks.
-+ * (blocksize is set via MTSETBLK.
-+ */
-+int
-+tape_std_mtbsr(struct tape_device *device, int mt_count)
-+{
-+	struct tape_request *request;
-+	ccw1_t *ccw;
-+
-+	request = tape_alloc_request(mt_count + 2, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_BSB;
-+	/* setup ccws */
-+	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
-+			  device->modeset_byte);
-+	ccw = tape_ccw_repeat(ccw, BACKSPACEBLOCK, mt_count);
-+	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
-+}
-+
-+/*
-+ * MTWEOF: Write 'count' file marks at the current position.
-+ */
-+int
-+tape_std_mtweof(struct tape_device *device, int mt_count)
-+{
-+	struct tape_request *request;
-+	ccw1_t *ccw;
-+
-+	request = tape_alloc_request(mt_count + 2, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_WTM;
-+	/* setup ccws */
-+	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
-+			  device->modeset_byte);
-+	ccw = tape_ccw_repeat(ccw, WRITETAPEMARK, mt_count);
-+	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
-+}
++#define PRINTK_HEADER "TCHAR:"
 +
-+/*
-+ * MTBSFM: Backward space over 'count' file marks.
-+ * The tape is positioned at the BOT (Begin Of Tape) side of the
-+ * last skipped file mark.
-+ */
-+int
-+tape_std_mtbsfm(struct tape_device *device, int mt_count)
-+{
-+	struct tape_request *request;
-+	ccw1_t *ccw;
++#define TAPECHAR_DEVFSMODE	0020644	/* crwxrw-rw- */
++#define TAPECHAR_MAJOR		0	/* get dynamic major */
 +
-+	request = tape_alloc_request(mt_count + 2, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_BSF;
-+	/* setup ccws */
-+	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
-+			  device->modeset_byte);
-+	ccw = tape_ccw_repeat(ccw, BACKSPACEFILE, mt_count);
-+	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
-+}
++int tapechar_major = TAPECHAR_MAJOR;
 +
 +/*
-+ * MTBSF: Backward space over 'count' file marks. The tape is positioned at
-+ * the EOT (End of Tape) side of the last skipped file mark.
++ * Prototypes for file operation functions
 + */
-+int
-+tape_std_mtbsf(struct tape_device *device, int mt_count)
-+{
-+	struct tape_request *request;
-+	ccw1_t *ccw;
-+	int rc;
-+	
-+	request = tape_alloc_request(mt_count + 2, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_BSF;
-+	/* setup ccws */
-+	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
-+			  device->modeset_byte);
-+	ccw = tape_ccw_repeat(ccw, BACKSPACEFILE, mt_count);
-+	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
-+	/* execute it */
-+	rc = tape_do_io(device, request);
-+	if (rc == 0) {
-+		request->op = TO_FSF;
-+		/* need to skip forward over the filemark. */
-+		tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
-+			    device->modeset_byte);
-+		tape_ccw_cc(request->cpaddr + 1, FORSPACEFILE, 0, NULL);
-+		tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
-+		/* execute it */
-+		rc = tape_do_io(device, request);
-+	}
-+	tape_put_request(request);
-+	return rc;
-+}
++static ssize_t tapechar_read(struct file *, char *, size_t, loff_t *);
++static ssize_t tapechar_write(struct file *, const char *, size_t, loff_t *);
++static int tapechar_open(struct inode *,struct file *);
++static int tapechar_release(struct inode *,struct file *);
++static int tapechar_ioctl(struct inode *, struct file *, unsigned int,
++			  unsigned long);
 +
 +/*
-+ * MTFSFM: Forward space over 'count' file marks.
-+ * The tape is positioned at the BOT (Begin Of Tape) side
-+ * of the last skipped file mark.
++ * File operation structure for tape character frontend
 + */
-+int
-+tape_std_mtfsfm(struct tape_device *device, int mt_count)
++static struct file_operations tape_fops =
 +{
-+	struct tape_request *request;
-+	ccw1_t *ccw;
-+	int rc;
++	.read = tapechar_read,
++	.write = tapechar_write,
++	.ioctl = tapechar_ioctl,
++	.open = tapechar_open,
++	.release = tapechar_release,
++};
 +
-+	request = tape_alloc_request(mt_count + 2, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_FSF;
-+	/* setup ccws */
-+	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, 
-+			  device->modeset_byte);
-+	ccw = tape_ccw_repeat(ccw, FORSPACEFILE, mt_count);
-+	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
-+	/* execute it */
-+	rc = tape_do_io(device, request);
-+	if (rc == 0) {
-+		request->op = TO_BSF;
-+		/* need to skip forward over the filemark. */
-+		tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, 
-+			    device->modeset_byte);
-+		tape_ccw_cc(request->cpaddr + 1, BACKSPACEFILE, 0, NULL);
-+		tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
-+		/* execute it */
-+		rc = tape_do_io(device, request);
++#ifdef CONFIG_DEVFS_FS
++/*
++ * Create Char directory with (non)rewinding entries
++ */
++static int
++tapechar_mkdevfstree(struct tape_device *device)
++{
++	device->char_data.devfs_char_dir =
++		devfs_mk_dir(device->devfs_dir, "char", device);
++	if (device->char_data.devfs_char_dir == NULL)
++		return -ENOENT;
++	device->char_data.devfs_nonrewinding =
++		devfs_register(device->char_data.devfs_char_dir,
++			       "nonrewinding", DEVFS_FL_DEFAULT,
++			       tapechar_major, device->first_minor,
++			       TAPECHAR_DEVFSMODE, &tape_fops, device);
++	if (device->char_data.devfs_nonrewinding == NULL) {
++		devfs_unregister(device->char_data.devfs_char_dir);
++		return -ENOENT;
 +	}
-+	tape_put_request(request);
-+	return rc;
++	device->char_data.devfs_rewinding =
++		devfs_register(device->char_data.devfs_char_dir,
++			       "rewinding", DEVFS_FL_DEFAULT,
++			       tapechar_major, device->first_minor + 1,
++			       TAPECHAR_DEVFSMODE, &tape_fops, device);
++	if (device->char_data.devfs_rewinding == NULL) {
++		devfs_unregister(device->char_data.devfs_nonrewinding);
++		devfs_unregister(device->char_data.devfs_char_dir);
++		return -ENOENT;
++	}
++	return 0;
 +}
 +
 +/*
-+ * MTREW: Rewind the tape.
++ * Remove devfs entries
 + */
-+int
-+tape_std_mtrew(struct tape_device *device, int mt_count)
++static void
++tapechar_rmdevfstree (struct tape_device *device)
 +{
-+	struct tape_request *request;
-+
-+	request = tape_alloc_request(3, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_REW;
-+	/* setup ccws */
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, 
-+		    device->modeset_byte);
-+	tape_ccw_cc(request->cpaddr + 1, REWIND, 0, NULL);
-+	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
++	if (device->char_data.devfs_nonrewinding) 
++		devfs_unregister(device->char_data.devfs_nonrewinding);
++	if (device->char_data.devfs_rewinding)
++		devfs_unregister(device->char_data.devfs_rewinding);
++	if (device->char_data.devfs_char_dir)
++		devfs_unregister(device->char_data.devfs_char_dir);
 +}
++#endif
 +
 +/*
-+ * MTOFFL: Rewind the tape and put the drive off-line.
-+ * Implement 'rewind unload'
++ * This function is called for every new tapedevice
 + */
 +int
-+tape_std_mtoffl(struct tape_device *device, int mt_count)
++tapechar_setup_device(struct tape_device * device)
 +{
-+	struct tape_request *request;
++#ifdef CONFIG_DEVFS_FS
++	int rc;
 +
-+	request = tape_alloc_request(3, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_RUN;
-+	/* setup ccws */
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_cc(request->cpaddr + 1, REWIND_UNLOAD, 0, NULL);
-+	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
++	rc = tapechar_mkdevfstree(device);
++	if (rc)
++		return rc;
++#endif
++
++	tape_hotplug_event(device, tapechar_major, TAPE_HOTPLUG_CHAR_ADD);
++	return 0;
++	
 +}
 +
-+/*
-+ * MTNOP: 'No operation'.
-+ */
-+int
-+tape_std_mtnop(struct tape_device *device, int mt_count)
++void
++tapechar_cleanup_device(struct tape_device* device)
 +{
-+	struct tape_request *request;
-+
-+	request = tape_alloc_request(2, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_NOP;
-+	/* setup ccws */
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
++#ifdef CONFIG_DEVFS_FS
++	tapechar_rmdevfstree(device);
++#endif
++	tape_hotplug_event(device, tapechar_major, TAPE_HOTPLUG_CHAR_REMOVE);
 +}
 +
-+/*
-+ * MTEOM: positions at the end of the portion of the tape already used
-+ * for recordind data. MTEOM positions after the last file mark, ready for
-+ * appending another file.
-+ */
-+int
-+tape_std_mteom(struct tape_device *device, int mt_count)
++static inline int
++tapechar_check_idalbuffer(struct tape_device *device, size_t block_size)
 +{
-+	int                  rc;
++	struct idal_buffer *new;
 +
-+	/*
-+	 * Since there is currently no other way to seek, return to the
-+	 * BOT and start from there.
-+	 */
-+	if((rc = tape_mtop(device, MTREW, 1)) < 0)
-+		return rc;
++	/* Idal buffer must be the same size as the requested block size! */
++	if (device->char_data.idal_buf != NULL &&
++	    device->char_data.idal_buf->size == block_size)
++		return 0;
 +
-+	do {
-+		if((rc = tape_mtop(device, MTFSF, 1)) < 0)
-+			return rc;
-+		if((rc = tape_mtop(device, MTFSR, 1)) < 0)
-+			return rc;
-+	} while((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) == 0);
++	if(block_size > MAX_BLOCKSIZE) {
++		DBF_EVENT(3, "Invalid blocksize (%ld > %ld)\n",
++			block_size, MAX_BLOCKSIZE);
++		PRINT_ERR("Invalid blocksize (%ld > %ld)\n",
++			block_size, MAX_BLOCKSIZE);
++		return -EINVAL;
++	}
 +
-+	return tape_mtop(device, MTBSR, 1);
++	/* The current idal buffer is not big enough. Allocate a new one. */
++	new = idal_buffer_alloc(block_size, 0);
++	if (new == NULL)
++		return -ENOMEM;
++	if (device->char_data.idal_buf != NULL)
++		idal_buffer_free(device->char_data.idal_buf);	
++	device->char_data.idal_buf = new;
++	return 0;
 +}
 +
 +/*
-+ * MTRETEN: Retension the tape, i.e. forward space to end of tape and rewind.
++ * Tape device read function
 + */
-+int
-+tape_std_mtreten(struct tape_device *device, int mt_count)
++ssize_t
++tapechar_read (struct file *filp, char *data, size_t count, loff_t *ppos)
 +{
++	struct tape_device *device;
 +	struct tape_request *request;
++	size_t block_size;
 +	int rc;
 +
-+	request = tape_alloc_request(4, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_FSF;
-+	/* setup ccws */
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_cc(request->cpaddr + 1,FORSPACEFILE, 0, NULL);
-+	tape_ccw_cc(request->cpaddr + 2, NOP, 0, NULL);
-+	tape_ccw_end(request->cpaddr + 3, CCW_CMD_TIC, 0, request->cpaddr);
-+	/* execute it, MTRETEN rc gets ignored */
-+	rc = tape_do_io_interruptible(device, request);
-+	tape_put_request(request);
-+	return tape_std_mtrew(device, 1);
-+}
++        DBF_EVENT(6, "TCHAR:read\n");
++	device = (struct tape_device *) filp->private_data;
 +
-+/*
-+ * MTERASE: erases the tape.
-+ */
-+int
-+tape_std_mterase(struct tape_device *device, int mt_count)
-+{
-+	struct tape_request *request;
++	/* Check position. */
++	if (ppos != &filp->f_pos) {
++		/*
++		 * "A request was outside the capabilities of the device."
++		 * This check uses internal knowledge about how pread and
++		 * read work...
++		 */
++	        DBF_EVENT(6, "TCHAR:ppos wrong\n");
++		return -EOVERFLOW;
++	}
 +
-+	request = tape_alloc_request(5, 0);
-+	if (IS_ERR(request))
-+		return PTR_ERR(request);
-+	request->op = TO_DSE;
-+	/* setup ccws */
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_cc(request->cpaddr + 1, REWIND, 0, NULL);
-+	tape_ccw_cc(request->cpaddr + 2, ERASE_GAP, 0, NULL);
-+	tape_ccw_cc(request->cpaddr + 3, DATA_SEC_ERASE, 0, NULL);
-+	tape_ccw_end(request->cpaddr + 4, NOP, 0, NULL);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
-+}
++	/*
++	 * If the tape isn't terminated yet, do it now. And since we then
++	 * are at the end of the tape there wouldn't be anything to read
++	 * anyways. So we return immediatly.
++	 */
++	if(device->required_tapemarks) {
++		return tape_std_terminate_write(device);
++	}
 +
-+/*
-+ * MTUNLOAD: Rewind the tape and unload it.
-+ */
-+int
-+tape_std_mtunload(struct tape_device *device, int mt_count)
-+{
-+	struct tape_request *request;
++	/* Find out block size to use */
++	if (device->char_data.block_size != 0) {
++		if (count < device->char_data.block_size) {
++			DBF_EVENT(3, "TCHAR:read smaller than block "
++				     "size was requested\n");
++			return -EINVAL;
++		}
++		block_size = device->char_data.block_size;
++	} else {
++		block_size = count;
++	}
 +
-+	request = tape_alloc_request(3, 32);
++	/*
++	 * Set the idal buffer to the correct size. The fixed block size
++	 * could have been set some time ago. And the idal buffer is re-
++	 * leased when the device is closed!
++	 */
++	rc = tapechar_check_idalbuffer(device, block_size);
++	if (rc)
++		return rc;
++
++	DBF_EVENT(6, "TCHAR:nbytes: %lx\n", block_size);
++	/* Let the discipline build the ccw chain. */
++	request = device->discipline->read_block(device, block_size);
 +	if (IS_ERR(request))
 +		return PTR_ERR(request);
-+	request->op = TO_RUN;
-+	/* setup ccws */
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_cc(request->cpaddr + 1, REWIND_UNLOAD, 0, NULL);
-+	tape_ccw_end(request->cpaddr + 2, SENSE, 32, request->cpdata);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
++	/* Execute it. */
++	rc = tape_do_io(device, request);
++	if (rc == 0) {
++		rc = block_size - device->devstat.rescnt;
++		DBF_EVENT(6, "TCHAR:rbytes:  %x\n", rc);
++		filp->f_pos += rc;
++		/* Copy data from idal buffer to user space. */
++		if (idal_buffer_to_user(device->char_data.idal_buf,
++					data, rc) != 0)
++			rc = -EFAULT;
++	}
++	tape_put_request(request);
++	return rc;
 +}
 +
 +/*
-+ * MTCOMPRESSION: used to enable compression.
-+ * Sets the IDRC on/off.
++ * Tape device write function
 + */
-+int
-+tape_std_mtcompression(struct tape_device *device, int mt_count)
++ssize_t
++tapechar_write(struct file *filp, const char *data, size_t count, loff_t *ppos)
 +{
++	struct tape_device *device;
 +	struct tape_request *request;
++	size_t block_size;
++	size_t written;
++	int nblocks;
++	int i, rc;
 +
-+	if (mt_count < 0 || mt_count > 1) {
-+		DBF_EXCEPTION(6, "xcom parm\n");
-+		if (*device->modeset_byte & 0x08)
-+			PRINT_INFO("(%x) Compression is currently on\n",
-+				   device->devstat.devno);
-+		else
-+			PRINT_INFO("(%x) Compression is currently off\n",
-+				   device->devstat.devno);
-+		PRINT_INFO("Use 1 to switch compression on, 0 to "
-+			   "switch it off\n");
-+		return -EINVAL;
++	DBF_EVENT(6, "TCHAR:write\n");
++	device = (struct tape_device *) filp->private_data;
++	/* Check position */
++	if (ppos != &filp->f_pos) {
++		/* "A request was outside the capabilities of the device." */
++	        DBF_EVENT(6, "TCHAR:ppos wrong\n");
++		return -EOVERFLOW;
 +	}
-+	request = tape_alloc_request(2, 0);
++	/* Find out block size and number of blocks */
++	if (device->char_data.block_size != 0) {
++		if (count < device->char_data.block_size) {
++			DBF_EVENT(3, "TCHAR:write smaller than block "
++				    "size was requested\n");
++			return -EINVAL;
++		}
++		block_size = device->char_data.block_size;
++		nblocks = count / block_size;
++	} else {
++		block_size = count;
++		nblocks = 1;
++	}
++
++	/* Set the idal buffer to the correct size. */
++	rc = tapechar_check_idalbuffer(device, block_size);
++	if (rc)
++		return rc;
++
++	DBF_EVENT(6,"TCHAR:nbytes: %lx\n", block_size);
++        DBF_EVENT(6, "TCHAR:nblocks: %x\n", nblocks);
++	/* Let the discipline build the ccw chain. */
++	request = device->discipline->write_block(device, block_size);
 +	if (IS_ERR(request))
 +		return PTR_ERR(request);
-+	request->op = TO_NOP;
-+	/* setup ccws */
-+	*device->modeset_byte = (mt_count == 0) ? 0x00 : 0x08;
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
-+	/* execute it */
-+	return tape_do_io_free(device, request);
-+}
-+
-+/*
-+ * Read Block
-+ */
-+struct tape_request *
-+tape_std_read_block(struct tape_device *device, size_t count)
-+{
-+	struct tape_request *request;
++	rc = 0;
++	written = 0;
++	for (i = 0; i < nblocks; i++) {
++		/* Copy data from user space to idal buffer. */
++		if (idal_buffer_from_user(device->char_data.idal_buf,
++					  data, block_size)) {
++			rc = -EFAULT;
++			break;
++		}
++		rc = tape_do_io(device, request);
++		if (rc)
++			break;
++	        DBF_EVENT(6, "TCHAR:wbytes: %lx\n",
++			  block_size - device->devstat.rescnt); 
++		filp->f_pos += block_size - device->devstat.rescnt;
++		written += block_size - device->devstat.rescnt;
++		if (device->devstat.rescnt != 0)
++			break;
++		data += block_size;
++	}
++	tape_put_request(request);
 +
-+	/*
-+	 * We have to alloc 4 ccws in order to be able to transform request
-+	 * into a read backward request in error case.
-+	 */
-+	request = tape_alloc_request(4, 0);
-+	if (IS_ERR(request)) {
-+		DBF_EXCEPTION(6, "xrbl fail");
-+		return request;
++	if (rc == -ENOSPC) {
++		/*
++		 * Ok, the device has no more space. It has NOT written
++		 * the block.
++		 */
++		if (device->discipline->process_eov)
++			device->discipline->process_eov(device);
++		if (written > 0)
++			rc = 0;
 +	}
-+	request->op = TO_RFO;
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_end_idal(request->cpaddr + 1, READ_FORWARD,
-+			  device->char_data.idal_buf);
-+	DBF_EVENT(6, "xrbl ccwg\n");
-+	return request;
-+}
 +
-+/*
-+ * Read Block backward transformation function.
-+ */
-+void
-+tape_std_read_backward(struct tape_device *device, struct tape_request *request)
-+{
 +	/*
-+	 * We have allocated 4 ccws in tape_std_read, so we can now
-+	 * transform the request to a read backward, followed by a
-+	 * forward space block.
++	 * After doing a write we always need two tapemarks to correctly
++	 * terminate the tape (one to terminate the file, the second to
++	 * flag the end of recorded data.
++	 * Since process_eov positions the tape in front of the written
++	 * tapemark it doesn't hurt to write two marks again.
 +	 */
-+	request->op = TO_RBA;
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_cc_idal(request->cpaddr + 1, READ_BACKWARD,
-+			 device->char_data.idal_buf);
-+	tape_ccw_cc(request->cpaddr + 2, FORSPACEBLOCK, 0, NULL);
-+	tape_ccw_end(request->cpaddr + 3, NOP, 0, NULL);
-+	DBF_EVENT(6, "xrop ccwg");}
++	if(!rc)
++		device->required_tapemarks = 2;
++
++	return rc ? rc : written;
++}
 +
 +/*
-+ * Write Block
++ * Character frontend tape device open function.
 + */
-+struct tape_request *
-+tape_std_write_block(struct tape_device *device, size_t count)
++int
++tapechar_open (struct inode *inode, struct file *filp)
 +{
-+	struct tape_request *request;
++	struct tape_device *device;
++	int minor, rc;
 +
-+	request = tape_alloc_request(2, 0);
-+	if (IS_ERR(request)) {
-+		DBF_EXCEPTION(6, "xwbl fail\n");
-+		return request;
++	MOD_INC_USE_COUNT;
++	if (major(filp->f_dentry->d_inode->i_rdev) != tapechar_major)
++		return -ENODEV;
++	minor = minor(filp->f_dentry->d_inode->i_rdev);
++	device = tape_get_device(minor / TAPE_MINORS_PER_DEV);
++	if (IS_ERR(device)) {
++		MOD_DEC_USE_COUNT;
++		return PTR_ERR(device);
 +	}
-+	request->op = TO_WRI;
-+	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
-+	tape_ccw_end_idal(request->cpaddr + 1, WRITE_CMD,
-+			  device->char_data.idal_buf);
-+	DBF_EVENT(6, "xwbl ccwg\n");
-+	return request;
++	DBF_EVENT(6, "TCHAR:open: %x\n", minor(inode->i_rdev));
++	rc = tape_open(device);
++	if (rc == 0) {
++		rc = tape_assign(device, TAPE_STATUS_ASSIGN_A);
++		if (rc == 0) {
++			filp->private_data = device;
++			return 0;
++		}
++		tape_release(device);
++	}
++	tape_put_device(device);
++	MOD_DEC_USE_COUNT;
++	return rc;
 +}
 +
 +/*
-+ * This routine is called by frontend after an ENOSP on write
++ * Character frontend tape device release function.
 + */
-+void
-+tape_std_process_eov(struct tape_device *device)
++
++int
++tapechar_release(struct inode *inode, struct file *filp)
 +{
++	struct tape_device *device;
++
++	device = (struct tape_device *) filp->private_data;
++	DBF_EVENT(6, "TCHAR:release: %x\n", minor(inode->i_rdev));
++			
 +	/*
-+	 * End of volume: We have to backspace the last written record, then
-+	 * we TRY to write a tapemark and then backspace over the written TM
++	 * If this is the rewinding tape minor then rewind. In that case we
++	 * write all required tapemarks. Otherwise only one to terminate the
++	 * file.
 +	 */
-+	if (tape_mtop(device, MTBSR, 1) < 0)
-+		return;
-+	if (tape_mtop(device, MTWEOF, 1) < 0)
-+		return;
-+	tape_mtop(device, MTBSR, 1);
++	if ((minor(inode->i_rdev) & 1) != 0) {
++		if(device->required_tapemarks)
++			tape_std_terminate_write(device);
++		tape_mtop(device, MTREW, 1);
++	} else {
++		if(device->required_tapemarks > 1) {
++			if(tape_mtop(device, MTWEOF, 1) == 0)
++				device->required_tapemarks--;
++		}
++	}
++
++	if (device->char_data.idal_buf != NULL) {
++		idal_buffer_free(device->char_data.idal_buf);
++		device->char_data.idal_buf = NULL;
++	}
++	tape_unassign(device, TAPE_STATUS_ASSIGN_A);
++	tape_release(device);
++	filp->private_data = NULL; tape_put_device(device);
++	MOD_DEC_USE_COUNT;
++	return 0;
 +}
 +
-+EXPORT_SYMBOL(tape_std_assign);
-+EXPORT_SYMBOL(tape_std_unassign);
-+#ifdef TAPE390_FORCE_UNASSIGN
-+EXPORT_SYMBOL(tape_std_force_unassign);
-+#endif
-+EXPORT_SYMBOL(tape_std_display);
-+EXPORT_SYMBOL(tape_std_read_block_id);
-+EXPORT_SYMBOL(tape_std_seek_block_id);
-+EXPORT_SYMBOL(tape_std_mtload);
-+EXPORT_SYMBOL(tape_std_mtsetblk);
-+EXPORT_SYMBOL(tape_std_mtreset);
-+EXPORT_SYMBOL(tape_std_mtfsf);
-+EXPORT_SYMBOL(tape_std_mtfsr);
-+EXPORT_SYMBOL(tape_std_mtbsr);
-+EXPORT_SYMBOL(tape_std_mtweof);
-+EXPORT_SYMBOL(tape_std_mtbsfm);
-+EXPORT_SYMBOL(tape_std_mtbsf);
-+EXPORT_SYMBOL(tape_std_mtfsfm);
-+EXPORT_SYMBOL(tape_std_mtrew);
-+EXPORT_SYMBOL(tape_std_mtoffl);
-+EXPORT_SYMBOL(tape_std_mtnop);
-+EXPORT_SYMBOL(tape_std_mteom);
-+EXPORT_SYMBOL(tape_std_mtreten);
-+EXPORT_SYMBOL(tape_std_mterase);
-+EXPORT_SYMBOL(tape_std_mtunload);
-+EXPORT_SYMBOL(tape_std_mtcompression);
-+EXPORT_SYMBOL(tape_std_read_block);
-+EXPORT_SYMBOL(tape_std_read_backward);
-+EXPORT_SYMBOL(tape_std_write_block);
-+EXPORT_SYMBOL(tape_std_process_eov);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_std.h drivers/s390/char/tape_std.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_std.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/tape_std.h	2006-01-30 22:25:26.000000000 -0700
-@@ -0,0 +1,161 @@
 +/*
-+ *  drivers/s390/char/tape_std.h
-+ *    standard tape device functions for ibm tapes.
-+ *
-+ *  S390 and zSeries version
-+ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Carsten Otte <cotte at de.ibm.com>
-+ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-+ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ *               Stefan Bader <shbader at de.ibm.com>
++ * Tape device io controls.
 + */
++static int
++tapechar_ioctl(struct inode *inp, struct file *filp,
++	   unsigned int no, unsigned long data)
++{
++	struct tape_device *device;
++	int rc;
 +
-+#ifndef _TAPE_STD_H
-+#define _TAPE_STD_H
++	DBF_EVENT(6, "TCHAR:ioct(%x)\n", no);
 +
-+#include <asm/tape390.h>
++	device = (struct tape_device *) filp->private_data;
 +
-+/*
-+ * Biggest block size to handle. Currently 64K because we only build
-+ * channel programs without data chaining.
-+ */
-+#define MAX_BLOCKSIZE	65535
++	if (no == MTIOCTOP) {
++		struct mtop op;
 +
-+/*
-+ * The CCW commands for the Tape type of command.
-+ */
-+#define INVALID_00		0x00	/* Invalid cmd */
-+#define BACKSPACEBLOCK		0x27	/* Back Space block */
-+#define BACKSPACEFILE		0x2f	/* Back Space file */
-+#define DATA_SEC_ERASE		0x97	/* Data security erase */
-+#define ERASE_GAP		0x17	/* Erase Gap */
-+#define FORSPACEBLOCK		0x37	/* Forward space block */
-+#define FORSPACEFILE		0x3F	/* Forward Space file */
-+#define FORCE_STREAM_CNT	0xEB	/* Forced streaming count # */
-+#define NOP			0x03	/* No operation	*/
-+#define READ_FORWARD		0x02	/* Read forward */
-+#define REWIND			0x07	/* Rewind */
-+#define REWIND_UNLOAD		0x0F	/* Rewind and Unload */
-+#define SENSE			0x04	/* Sense */
-+#define NEW_MODE_SET		0xEB	/* Guess it is Mode set */
-+#define WRITE_CMD		0x01	/* Write */
-+#define WRITETAPEMARK		0x1F	/* Write Tape Mark */
++		if (copy_from_user(&op, (char *) data, sizeof(op)) != 0)
++			return -EFAULT;
++		if (op.mt_count < 0)
++			return -EINVAL;
 +
-+#define ASSIGN			0xB7	/* 3420 REJECT,3480 OK	*/
-+#define CONTROL_ACCESS		0xE3	/* Set high speed */
-+#define DIAG_MODE_SET		0x0B	/* 3420 NOP, 3480 REJECT */
-+#define LOAD_DISPLAY		0x9F	/* 3420 REJECT,3480 OK */
-+#define LOCATE			0x4F	/* 3420 REJ, 3480 NOP */
-+#define LOOP_WRITE_TO_READ	0x8B	/* 3480 REJECT */
-+#define MODE_SET_DB		0xDB	/* 3420 REJECT,3480 OK */
-+#define MODE_SET_C3		0xC3	/* for 3420 */
-+#define MODE_SET_CB		0xCB	/* for 3420 */
-+#define MODE_SET_D3		0xD3	/* for 3420 */
-+#define READ_BACKWARD		0x0C	/* */
-+#define READ_BLOCK_ID		0x22	/* 3420 REJECT,3480 OK */
-+#define READ_BUFFER		0x12	/* 3420 REJECT,3480 OK */
-+#define READ_BUFF_LOG		0x24	/* 3420 REJECT,3480 OK */
-+#define RELEASE			0xD4	/* 3420 NOP, 3480 REJECT */
-+#define REQ_TRK_IN_ERROR	0x1B	/* 3420 NOP, 3480 REJECT */
-+#define RESERVE			0xF4	/* 3420 NOP, 3480 REJECT */
-+#define SENSE_GROUP_ID		0x34	/* 3420 REJECT,3480 OK */
-+#define SENSE_ID		0xE4	/* 3420 REJECT,3480 OK */
-+#define READ_DEV_CHAR		0x64	/* Read device characteristics */
-+#define SET_DIAGNOSE		0x4B	/* 3420 NOP, 3480 REJECT */
-+#define SET_GROUP_ID		0xAF	/* 3420 REJECT,3480 OK */
-+#define SET_TAPE_WRITE_IMMED	0xC3	/* for 3480 */
-+#define SUSPEND			0x5B	/* 3420 REJ, 3480 NOP */
-+#define SYNC			0x43	/* Synchronize (flush buffer) */
-+#define UNASSIGN		0xC7	/* 3420 REJECT,3480 OK */
-+#define PERF_SUBSYS_FUNC	0x77	/* 3490 CMD */
-+#define READ_CONFIG_DATA	0xFA	/* 3490 CMD */
-+#define READ_MESSAGE_ID		0x4E	/* 3490 CMD */
-+#define READ_SUBSYS_DATA	0x3E	/* 3490 CMD */
-+#define SET_INTERFACE_ID	0x73	/* 3490 CMD */
++		/*
++		 * Operations that change tape position should write final
++		 * tapemarks
++		 */
++		switch(op.mt_op) {
++			case MTFSF:
++			case MTBSF:
++			case MTFSR:
++			case MTBSR:
++			case MTREW:
++			case MTOFFL:
++			case MTEOM:
++			case MTRETEN:
++			case MTBSFM:
++			case MTFSFM:
++			case MTSEEK:
++				if(device->required_tapemarks)
++					tape_std_terminate_write(device);
++			default:
++				;
++		}
++		rc = tape_mtop(device, op.mt_op, op.mt_count);
 +
-+#define SENSE_COMMAND_REJECT		0x80
-+#define SENSE_INTERVENTION_REQUIRED	0x40
-+#define SENSE_BUS_OUT_CHECK		0x20
-+#define SENSE_EQUIPMENT_CHECK		0x10
-+#define SENSE_DATA_CHECK		0x08
-+#define SENSE_OVERRUN			0x04
-+#define SENSE_DEFERRED_UNIT_CHECK	0x02
-+#define SENSE_ASSIGNED_ELSEWHERE	0x01
++		if(op.mt_op == MTWEOF && rc == 0) {
++			if(op.mt_count > device->required_tapemarks)
++				device->required_tapemarks = 0;
++			else
++				device->required_tapemarks -= op.mt_count;
++		}
++		return rc;
++	}
++	if (no == MTIOCPOS) {
++		/* MTIOCPOS: query the tape position. */
++		struct mtpos pos;
 +
-+#define SENSE_LOCATE_FAILURE		0x80
-+#define SENSE_DRIVE_ONLINE		0x40
-+#define SENSE_RESERVED			0x20
-+#define SENSE_RECORD_SEQUENCE_ERR	0x10
-+#define SENSE_BEGINNING_OF_TAPE		0x08
-+#define SENSE_WRITE_MODE		0x04
-+#define SENSE_WRITE_PROTECT		0x02
-+#define SENSE_NOT_CAPABLE		0x01
++		rc = tape_mtop(device, MTTELL, 1);
++		if (rc < 0)
++			return rc;
++		pos.mt_blkno = rc;
++		if (copy_to_user((char *) data, &pos, sizeof(pos)) != 0)
++			return -EFAULT;
++		return 0;
++	}
++	if (no == MTIOCGET) {
++		/* MTIOCGET: query the tape drive status. */
++		struct mtget get;
 +
-+#define SENSE_CHANNEL_ADAPTER_CODE	0xE0
-+#define SENSE_CHANNEL_ADAPTER_LOC	0x10
-+#define SENSE_REPORTING_CU		0x08
-+#define SENSE_AUTOMATIC_LOADER		0x04
-+#define SENSE_TAPE_SYNC_MODE		0x02
-+#define SENSE_TAPE_POSITIONING		0x01
++		memset(&get, 0, sizeof(get));
++		get.mt_type = MT_ISUNKNOWN;
++		get.mt_resid = device->devstat.rescnt;
++		get.mt_dsreg = device->tape_status;
++		/* FIXME: mt_erreg, mt_fileno */
++		get.mt_gstat = device->tape_generic_status;
 +
-+/* Data structure for the CONTROL_ACCESS call */
-+struct tape_ca_data {
-+	unsigned char	function;
-+	char		password[11];
-+} __attribute__ ((packed));
++		if(device->medium_state == MS_LOADED) {
++			rc = tape_mtop(device, MTTELL, 1);
 +
-+/* discipline functions */
-+struct tape_request *tape_std_read_block(struct tape_device *, size_t);
-+void tape_std_read_backward(struct tape_device *device,
-+			    struct tape_request *request);
-+struct tape_request *tape_std_write_block(struct tape_device *, size_t);
-+struct tape_request *tape_std_bread(struct tape_device *, struct request *);
-+void tape_std_free_bread(struct tape_request *);
-+void tape_std_check_locate(struct tape_device *, struct tape_request *);
-+struct tape_request *tape_std_bwrite(struct request *,
-+				     struct tape_device *, int);
++			if(rc < 0)
++				return rc;
 +
-+/* Some non-mtop commands. */
-+int tape_std_assign(struct tape_device *);
-+int tape_std_unassign(struct tape_device *);
-+int tape_std_force_unassign(struct tape_device *);
-+int tape_std_read_block_id(struct tape_device *, unsigned int *);
-+int tape_std_seek_block_id(struct tape_device *, unsigned int);
-+int tape_std_display(struct tape_device *, struct display_struct *);
-+int tape_std_terminate_write(struct tape_device *);
++			if(rc == 0)
++				get.mt_gstat |= GMT_BOT(~0);
 +
-+/* Standard magnetic tape commands. */
-+int tape_std_mtbsf(struct tape_device *, int);
-+int tape_std_mtbsfm(struct tape_device *, int);
-+int tape_std_mtbsr(struct tape_device *, int);
-+int tape_std_mtcompression(struct tape_device *, int);
-+int tape_std_mteom(struct tape_device *, int);
-+int tape_std_mterase(struct tape_device *, int);
-+int tape_std_mtfsf(struct tape_device *, int);
-+int tape_std_mtfsfm(struct tape_device *, int);
-+int tape_std_mtfsr(struct tape_device *, int);
-+int tape_std_mtload(struct tape_device *, int);
-+int tape_std_mtnop(struct tape_device *, int);
-+int tape_std_mtoffl(struct tape_device *, int);
-+int tape_std_mtreset(struct tape_device *, int);
-+int tape_std_mtreten(struct tape_device *, int);
-+int tape_std_mtrew(struct tape_device *, int);
-+int tape_std_mtsetblk(struct tape_device *, int);
-+int tape_std_mtunload(struct tape_device *, int);
-+int tape_std_mtweof(struct tape_device *, int);
++			get.mt_blkno = rc;
++		}
++		get.mt_erreg = 0;
++		if (copy_to_user((char *) data, &get, sizeof(get)) != 0)
++			return -EFAULT;
++		return 0;
++	}
++	/* Try the discipline ioctl function. */
++	if (device->discipline->ioctl_fn == NULL)
++		return -EINVAL;
++	return device->discipline->ioctl_fn(device, no, data);
++}
 +
-+/* Event handlers */
-+void tape_std_default_handler(struct tape_device *);
-+void tape_std_unexpect_uchk_handler(struct tape_device *);
-+void tape_std_irq(struct tape_device *);
-+void tape_std_process_eov(struct tape_device *);
++/*
++ * Initialize character device frontend.
++ */
++int
++tapechar_init (void)
++{
++	int rc;
 +
-+// the error recovery stuff:
-+void tape_std_error_recovery(struct tape_device *);
-+void tape_std_error_recovery_has_failed(struct tape_device *,int error_id);
-+void tape_std_error_recovery_succeded(struct tape_device *);
-+void tape_std_error_recovery_do_retry(struct tape_device *);
-+void tape_std_error_recovery_read_opposite(struct tape_device *);
-+void tape_std_error_recovery_HWBUG(struct tape_device *, int condno);
++	/* Register the tape major number to the kernel */
++#ifdef CONFIG_DEVFS_FS
++	if (tapechar_major == 0)
++		tapechar_major = devfs_alloc_major(DEVFS_SPECIAL_CHR);
++#endif
++        rc = register_chrdev(tapechar_major, "tape", &tape_fops);
++	if (rc < 0) {
++		PRINT_ERR("can't get major %d\n", tapechar_major);
++		DBF_EVENT(3, "TCHAR:initfail\n");
++		return rc;
++	}
++	if (tapechar_major == 0)
++		tapechar_major = rc;  /* accept dynamic major number */
++	PRINT_INFO("Tape gets major %d for char device\n", tapechar_major);
++	DBF_EVENT(3, "Tape gets major %d for char device\n", rc);
++        DBF_EVENT(3, "TCHAR:init ok\n");
++	return 0;
++}
 +
-+#endif // _TAPE_STD_H
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapeblock.c drivers/s390/char/tapeblock.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapeblock.c	2001-10-25 14:58:35.000000000 -0600
-+++ drivers/s390/char/tapeblock.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1,593 +0,0 @@
++/*
++ * cleanup
++ */
++void
++tapechar_exit(void)
++{
++	unregister_chrdev (tapechar_major, "tape");
++}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tapechar.c drivers/s390/char/tapechar.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tapechar.c	2004-08-07 17:26:05.000000000 -0600
++++ drivers/s390/char/tapechar.c	2006-02-12 12:47:23.000000000 -0700
+@@ -1,764 +0,0 @@
 -
 -/***************************************************************************
 - *
-- *  drivers/s390/char/tapeblock.c
-- *    block device frontend for tape device driver
+- *  drivers/s390/char/tapechar.c
+- *    character device frontend for tape device driver
 - *
 - *  S390 and zSeries version
 - *    Copyright (C) 2001 IBM Corporation
@@ -28308,446 +24456,495 @@
 -
 -#include "tapedefs.h"
 -#include <linux/config.h>
--#include <linux/blkdev.h>
--#include <linux/blk.h>
 -#include <linux/version.h>
--#include <linux/interrupt.h>
+-#include <linux/types.h>
+-#include <linux/proc_fs.h>
 -#include <asm/ccwcache.h>	/* CCW allocations      */
--#include <asm/debug.h>
 -#include <asm/s390dyn.h>
+-#include <asm/debug.h>
+-#include <linux/mtio.h>
+-#include <asm/uaccess.h>
 -#include <linux/compatmac.h>
 -#ifdef MODULE
 -#define __NO_VERSION__
 -#include <linux/module.h>
 -#endif
 -#include "tape.h"
--#include "tapeblock.h"
+-#include "tapechar.h"
 -
--#define PRINTK_HEADER "TBLOCK:"
+-#define PRINTK_HEADER "TCHAR:"
 -
 -/*
 - * file operation structure for tape devices
 - */
--#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
--static struct block_device_operations tapeblock_fops = {
--#else
--static struct file_operations tapeblock_fops = {
--#endif
--    owner   : THIS_MODULE,
--    open    : tapeblock_open,      /* open */
--    release : tapeblock_release,   /* release */
--        };
--
--int    tapeblock_major = 0;
--
--#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
--static void tape_request_fn (request_queue_t * queue);
--#else
--static void tape_request_fn (void);
--#endif
+-static struct file_operations tape_fops =
+-{
+-    //    owner   : THIS_MODULE,
+-	llseek:NULL,		/* lseek - default */
+-	read:tape_read,		/* read  */
+-	write:tape_write,	/* write */
+-	readdir:NULL,		/* readdir - bad */
+-	poll:NULL,		/* poll */
+-	ioctl:tape_ioctl,	/* ioctl */
+-	mmap:NULL,		/* mmap */
+-	open:tape_open,		/* open */
+-	flush:NULL,		/* flush */
+-	release:tape_release,	/* release */
+-	fsync:NULL,		/* fsync */
+-	fasync:NULL,		/* fasync */
+-	lock:NULL,
+-};
 -
--static request_queue_t* tapeblock_getqueue (kdev_t kdev);
+-int tape_major = TAPE_MAJOR;
 -
 -#ifdef CONFIG_DEVFS_FS
 -void
--tapeblock_mkdevfstree (tape_info_t* ti) {
--    ti->devfs_block_dir=devfs_mk_dir (ti->devfs_dir, "block", ti);
--    ti->devfs_disc=devfs_register(ti->devfs_block_dir, "disc",DEVFS_FL_DEFAULT,
--				    tapeblock_major, ti->blk_minor,
--				    TAPEBLOCK_DEFAULTMODE, &tapeblock_fops, ti);
+-tapechar_mkdevfstree (tape_info_t* ti) {
+-    ti->devfs_char_dir=devfs_mk_dir (ti->devfs_dir, "char", ti);
+-    ti->devfs_nonrewinding=devfs_register(ti->devfs_char_dir, "nonrewinding",
+-					    DEVFS_FL_DEFAULT,tape_major, 
+-					    ti->nor_minor, TAPECHAR_DEFAULTMODE, 
+-					    &tape_fops, ti);
+-    ti->devfs_rewinding=devfs_register(ti->devfs_char_dir, "rewinding",
+-					 DEVFS_FL_DEFAULT, tape_major, ti->rew_minor,
+-					 TAPECHAR_DEFAULTMODE, &tape_fops, ti);
 -}
 -
 -void
--tapeblock_rmdevfstree (tape_info_t* ti) {
--    devfs_unregister(ti->devfs_disc);
--    devfs_unregister(ti->devfs_block_dir);
+-tapechar_rmdevfstree (tape_info_t* ti) {
+-    devfs_unregister(ti->devfs_nonrewinding);
+-    devfs_unregister(ti->devfs_rewinding);
+-    devfs_unregister(ti->devfs_char_dir);
 -}
 -#endif
 -
--void 
--tapeblock_setup(tape_info_t* ti) {
--    blk_size[tapeblock_major][ti->blk_minor]=0; // this will be detected
--    blksize_size[tapeblock_major][ti->blk_minor]=2048; // blocks are 2k by default.
--    hardsect_size[tapeblock_major][ti->blk_minor]=512;
--    blk_init_queue (&ti->request_queue, tape_request_fn); 
--    blk_queue_headactive (&ti->request_queue, 0); 
--#ifdef CONFIG_DEVFS_FS
--    tapeblock_mkdevfstree(ti);
--#endif
--}
--
--int
--tapeblock_init(void) {
--    int result;
--    tape_frontend_t* blkfront,*temp;
--    tape_info_t* ti;
--
--    tape_init();
--    /* Register the tape major number to the kernel */
--#ifdef CONFIG_DEVFS_FS
--    result = devfs_register_blkdev(tapeblock_major, "tBLK", &tapeblock_fops);
--#else
--    result = register_blkdev(tapeblock_major, "tBLK", &tapeblock_fops);
--#endif
--    if (result < 0) {
--        PRINT_WARN(KERN_ERR "tape: can't get major %d for block device\n", tapeblock_major);
--        panic ("cannot get major number for tape block device");
--    }
--    if (tapeblock_major == 0) tapeblock_major = result;   /* accept dynamic major number*/
--    INIT_BLK_DEV(tapeblock_major,tape_request_fn,tapeblock_getqueue,NULL);
--    read_ahead[tapeblock_major]=TAPEBLOCK_READAHEAD;
--    PRINT_WARN(KERN_ERR " tape gets major %d for block device\n", result);
--    blk_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
--    memset(blk_size[tapeblock_major],0,256*sizeof(int));
--    blksize_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
--    memset(blksize_size[tapeblock_major],0,256*sizeof(int));
--    hardsect_size[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
--    memset(hardsect_size[tapeblock_major],0,256*sizeof(int));
--    max_sectors[tapeblock_major] = (int*) kmalloc (256*sizeof(int),GFP_ATOMIC);
--    memset(max_sectors[tapeblock_major],0,256*sizeof(int));
--    blkfront = kmalloc(sizeof(tape_frontend_t),GFP_KERNEL);
--    if (blkfront==NULL) panic ("no mem for tape block device structure");
--    blkfront->device_setup=tapeblock_setup;
+-void
+-tapechar_setup (tape_info_t * ti)
+-{
 -#ifdef CONFIG_DEVFS_FS
--    blkfront->mkdevfstree = tapeblock_mkdevfstree;
--    blkfront->rmdevfstree = tapeblock_rmdevfstree;
+-    tapechar_mkdevfstree(ti);
 -#endif
--    blkfront->next=NULL;
--    if (first_frontend==NULL) {
--	first_frontend=blkfront;
--    } else {
--	temp=first_frontend;
--	while (temp->next!=NULL)
--	temp=temp->next;
--	temp->next=blkfront;
--    }
--    ti=first_tape_info;
--    while (ti!=NULL) {
--	tapeblock_setup(ti);
--	ti=ti->next;
--    }
--    return 0;
 -}
 -
+-void
+-tapechar_init (void)
+-{
+-	int result;
+-	tape_frontend_t *charfront,*temp;
+-	tape_info_t* ti;
 -
--void 
--tapeblock_uninit(void) {
--    unregister_blkdev(tapeblock_major, "tBLK");
--}
--
--int
--tapeblock_open(struct inode *inode, struct file *filp) {
--    tape_info_t *ti;
--    kdev_t dev;
--    int rc;
--    long lockflags;
+-	tape_init();
 -
--    inode = filp->f_dentry->d_inode;
--    ti = first_tape_info;
--    while ((ti != NULL) && (ti->blk_minor != MINOR (inode->i_rdev)))
--		ti = (tape_info_t *) ti->next;
--	if (ti == NULL)
--		return -ENODEV;
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"b:open:");
--	debug_int_event (tape_debug_area,6,ti->blk_minor);
+-	/* Register the tape major number to the kernel */
+-#ifdef CONFIG_DEVFS_FS
+-	result = devfs_register_chrdev (tape_major, "tape", &tape_fops);
+-#else
+-	result = register_chrdev (tape_major, "tape", &tape_fops);
 -#endif
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	if (tapestate_get (ti) != TS_UNUSED) {
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-
+-	if (result < 0) {
+-		PRINT_WARN (KERN_ERR "tape: can't get major %d\n", tape_major);
 -#ifdef TAPE_DEBUG
--		debug_text_event (tape_debug_area,6,"b:dbusy");
+-		debug_text_event (tape_debug_area,3,"c:initfail");
+-		debug_text_event (tape_debug_area,3,"regchrfail");
+-#endif /* TAPE_DEBUG */
+-		panic ("no major number available for tape char device");
+-	}
+-	if (tape_major == 0)
+-		tape_major = result;	/* accept dynamic major number */
+-	PRINT_WARN (KERN_ERR " tape gets major %d for character device\n", result);
+-	charfront = kmalloc (sizeof (tape_frontend_t), GFP_KERNEL);
+-	if (charfront == NULL) {
+-#ifdef TAPE_DEBUG
+-                debug_text_event (tape_debug_area,3,"c:initfail");
+-		debug_text_event (tape_debug_area,3,"no mem");
+-#endif /* TAPE_DEBUG */
+-		panic ("no major number available for tape char device");		
+-	}
+-	charfront->device_setup = tapechar_setup;
+-#ifdef CONFIG_DEVFS_FS
+-	charfront->mkdevfstree = tapechar_mkdevfstree;
+-	charfront->rmdevfstree = tapechar_rmdevfstree;
 -#endif
--		return -EBUSY;
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,3,"c:init ok");
+-#endif /* TAPE_DEBUG */
+-	charfront->next=NULL;
+-	if (first_frontend==NULL) {
+-	    first_frontend=charfront;
+-	} else {
+-	    temp=first_frontend;
+-	    while (temp->next!=NULL)
+-		temp=temp->next;
+-	    temp->next=charfront;
 -	}
--	tapestate_set (ti, TS_IDLE);
--        ti->position=-1;
--        
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--        rc=tapeblock_mediumdetect(ti);
--        if (rc) {
--	    s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	    tapestate_set (ti, TS_UNUSED);
--	    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	    return rc; // in case of errors, we don't have a size of the medium
+-	ti=first_tape_info;
+-	while (ti!=NULL) {
+-	    tapechar_setup(ti);
+-	    ti=ti->next;
 -	}
--	dev = MKDEV (tapeblock_major, MINOR (inode->i_rdev));	/* Get the device */
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->blk_filp = filp;
--	filp->private_data = ti;	/* save the dev.info for later reference */
--        ti->cqr=NULL;
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--    
--	return 0;
 -}
 -
--int
--tapeblock_release(struct inode *inode, struct file *filp) {
+-void
+-tapechar_uninit (void)
+-{
+-	unregister_chrdev (tape_major, "tape");
+-}
+-
+-/*
+- * Tape device read function
+- */
+-ssize_t
+-tape_read (struct file *filp, char *data, size_t count, loff_t * ppos)
+-{
 -	long lockflags;
--	tape_info_t *ti,*lastti;
+-	tape_info_t *ti;
+-	size_t block_size;
+-	ccw_req_t *cqr;
+-	int rc;
+-	loff_t pos = *ppos;
+-#ifdef TAPE_DEBUG
+-        debug_text_event (tape_debug_area,6,"c:read");
+-#endif /* TAPE_DEBUG */
 -	ti = first_tape_info;
--	while ((ti != NULL) && (ti->blk_minor != MINOR (inode->i_rdev)))
+-	while ((ti != NULL) && (ti->rew_filp != filp) && (ti->nor_filp != filp))
 -		ti = (tape_info_t *) ti->next;
--	if ((ti != NULL) && (tapestate_get (ti) == TS_NOT_OPER)) {
--	    if (ti==first_tape_info) {
--		first_tape_info=ti->next;
--	    } else {
--		lastti=first_tape_info;
--		while (lastti->next!=ti) lastti=lastti->next;
--		lastti->next=ti->next;
--	    }
--	    kfree(ti);    
--	    return 0;
+-	if (ti == NULL) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_event (tape_debug_area,6,"c:nodev");
+-#endif /* TAPE_DEBUG */
+-		return -ENODEV;
 -	}
--	if ((ti == NULL) || (tapestate_get (ti) != TS_IDLE)) {
+-	if (ppos != &filp->f_pos) {
+-		/* "A request was outside the capabilities of the device." */
 -#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,3,"b:notidle!");
--#endif
--		return -ENXIO;	/* error in tape_release */
+-	        debug_text_event (tape_debug_area,6,"c:ppos wrong");
+-#endif /* TAPE_DEBUG */
+-		return -EOVERFLOW;	/* errno=75 Value too large for def. data type */
+-	}
+-	if (ti->block_size == 0) {
+-		block_size = count;
+-	} else {
+-		block_size = ti->block_size;
 -	}
 -#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"b:release:");
--	debug_int_event (tape_debug_area,6,ti->blk_minor);
+-	debug_text_event (tape_debug_area,6,"c:nbytes:");
+-	debug_int_event (tape_debug_area,6,block_size);
 -#endif
+-	cqr = ti->discipline->read_block (data, block_size, ti);
+-	if (!cqr) {
+-		return -ENOBUFS;
+-	}
 -	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	tapestate_set (ti, TS_UNUSED);
+-	ti->cqr = cqr;
+-	ti->wanna_wakeup=0;
+-	rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+-	if (rc) {
+-	    tapestate_set(ti,TS_IDLE);
+-	    kfree (cqr);
+-	    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	    return rc;
+-	}
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-	wait_event (ti->wq,ti->wanna_wakeup);
+-	ti->cqr = NULL;
+-	ti->discipline->free_read_block (cqr, ti);
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	if (tapestate_get (ti) == TS_FAILED) {
+-		tapestate_set (ti, TS_IDLE);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		return ti->rc;
+-	}
+-	if (tapestate_get (ti) == TS_NOT_OPER) {
+-	    ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
+-	    ti->devinfo.irq=-1;
+-	    s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags);
+-	    return -ENODEV;
+-	}
+-	if (tapestate_get (ti) != TS_DONE) {
+-		tapestate_set (ti, TS_IDLE);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		return -EIO;
+-	}
+-	tapestate_set (ti, TS_IDLE);
 -	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	invalidate_buffers(inode->i_rdev);
--	return 0;
--}
--
--static void
--tapeblock_end_request(tape_info_t* ti) {
--    struct buffer_head *bh;
--    int uptodate;
--    if ((tapestate_get(ti)!=TS_FAILED) &&
--	(tapestate_get(ti)!=TS_DONE))
--       BUG(); // A request has to be completed to end it
--    uptodate=(tapestate_get(ti)==TS_DONE); // is the buffer up to date?
 -#ifdef TAPE_DEBUG
--    if (uptodate) {
--	debug_text_event (tape_debug_area,6,"b:done:");
--	debug_int_event (tape_debug_area,6,(long)ti->cqr);
--    } else {
--	debug_text_event (tape_debug_area,3,"b:failed:");
--	debug_int_event (tape_debug_area,3,(long)ti->cqr);
--    }
--#endif
--    // now inform ll_rw_block about a request status
--    while ((bh = ti->current_request->bh) != NULL) {
--	ti->current_request->bh = bh->b_reqnext;
--	bh->b_reqnext = NULL;
--	bh->b_end_io (bh, uptodate);
--    }
--    if (!end_that_request_first (ti->current_request, uptodate, "tBLK")) {
--#ifndef DEVICE_NO_RANDOM
--	add_blkdev_randomness (MAJOR (ti->current_request->rq_dev));
--#endif
--	end_that_request_last (ti->current_request);
--    }
--    ti->discipline->free_bread(ti->cqr,ti);
--    ti->cqr=NULL;
--    ti->current_request=NULL;
--    if (tapestate_get(ti)!=TS_NOT_OPER) tapestate_set(ti,TS_IDLE);
--    return;
+-	debug_text_event (tape_debug_area,6,"c:rbytes:");
+-	debug_int_event (tape_debug_area,6,block_size - ti->devstat.rescnt);
+-#endif	/* TAPE_DEBUG */
+-	*ppos = pos + (block_size - ti->devstat.rescnt);
+-	return block_size - ti->devstat.rescnt;
 -}
 -
--static void
--tapeblock_exec_IO (tape_info_t* ti) {
--    int rc;
--    struct request* req;
--    if (ti->cqr) { // process done/failed request
--	while ((tapestate_get(ti)==TS_FAILED) &&
--	    ti->blk_retries>0) {
--	    ti->blk_retries--;
--	    ti->position=-1;
--	    tapestate_set(ti,TS_BLOCK_INIT);
+-/*
+- * Tape device write function
+- */
+-ssize_t
+-tape_write (struct file *filp, const char *data, size_t count, loff_t * ppos)
+-{
+-	long lockflags;
+-	tape_info_t *ti;
+-	size_t block_size;
+-	ccw_req_t *cqr;
+-	int nblocks, i, rc;
+-	size_t written = 0;
+-	loff_t pos = *ppos;
+-
 -#ifdef TAPE_DEBUG
--	    debug_text_event (tape_debug_area,3,"b:retryreq:");
--	    debug_int_event (tape_debug_area,3,(long)ti->cqr);
+-	debug_text_event (tape_debug_area,6,"c:write");
 -#endif
--	    rc = do_IO (ti->devinfo.irq, ti->cqr->cpaddr, (unsigned long) ti->cqr, 
--			0x00, ti->cqr->options);
--	    if (rc) {
+-	ti = first_tape_info;
+-	while ((ti != NULL) && (ti->nor_filp != filp) && (ti->rew_filp != filp))
+-		ti = (tape_info_t *) ti->next;
+-	if (ti == NULL)
+-		return -ENODEV;
+-	if (ppos != &filp->f_pos) {
+-		/* "A request was outside the capabilities of the device." */
 -#ifdef TAPE_DEBUG
--		debug_text_event (tape_debug_area,3,"b:doIOfail:");
--		debug_int_event (tape_debug_area,3,(long)ti->cqr);
--#endif 
--		continue; // one retry lost 'cause doIO failed
--	    }
--	    return;
--	}
--	tapeblock_end_request (ti); // check state, inform user, free mem, dev=idl
--    }
--    if (ti->cqr!=NULL) BUG(); // tape should be idle now, request should be freed!
--    if (tapestate_get (ti) == TS_NOT_OPER) {
--	ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
--	ti->devinfo.irq=-1;
--	return;
--    }
--#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
--	if (list_empty (&ti->request_queue.queue_head)) {
--#else
--	if (ti->request_queue==NULL) {
+-	        debug_text_event (tape_debug_area,6,"c:ppos wrong");
 -#endif
--	// nothing more to do or device has dissapeared;)
+-		return -EOVERFLOW;	/* errno=75 Value too large for def. data type */
+-	}
+-	if ((ti->block_size != 0) && (count % ti->block_size != 0))
+-		return -EIO;
+-	if (ti->block_size == 0) {
+-		block_size = count;
+-		nblocks = 1;
+-	} else {
+-		block_size = ti->block_size;
+-		nblocks = count / (ti->block_size);
+-	}
 -#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"b:Qempty");
+-	        debug_text_event (tape_debug_area,6,"c:nbytes:");
+-		debug_int_event (tape_debug_area,6,block_size);
+-	        debug_text_event (tape_debug_area,6,"c:nblocks:");
+-	        debug_int_event (tape_debug_area,6,nblocks);
 -#endif
--	tapestate_set(ti,TS_IDLE);
--	return;
--    }
--    // queue is not empty, fetch a request and start IO!
--    req=ti->current_request=tape_next_request(&ti->request_queue);
--    if (req==NULL) {
--	BUG(); // Yo. The queue was not reported empy, but no request found. This is _bad_.
--    }
--    if (req->cmd!=READ) { // we only support reading
--	tapestate_set(ti,TS_FAILED);
--	tapeblock_end_request (ti); // check state, inform user, free mem, dev=idl
--	tapestate_set(ti,TS_BLOCK_INIT);
--	schedule_tapeblock_exec_IO(ti);
--	return;
--    }
--    ti->cqr=ti->discipline->bread(req,ti,tapeblock_major); //build channel program from request
--    if (!ti->cqr) {
--	// ccw generation failed. we try again later.
+-	for (i = 0; i < nblocks; i++) {
+-		cqr = ti->discipline->write_block (data + i * block_size, block_size, ti);
+-		if (!cqr) {
+-			return -ENOBUFS;
+-		}
+-		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-		ti->cqr = cqr;
+-		ti->wanna_wakeup=0;
+-		rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		wait_event_interruptible (ti->wq,ti->wanna_wakeup);
+-		ti->cqr = NULL;
+-		ti->discipline->free_write_block (cqr, ti);
+-		if (signal_pending (current)) {
+-			tapestate_set (ti, TS_IDLE);
+-			return -ERESTARTSYS;
+-		}
+-		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-		if (tapestate_get (ti) == TS_FAILED) {
+-			tapestate_set (ti, TS_IDLE);
+-			s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-                        if ((ti->rc==-ENOSPC) && (i!=0))
+-			  return i*block_size;
+-			return ti->rc;
+-		}
+-		if (tapestate_get (ti) == TS_NOT_OPER) {
+-		    ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
+-		    ti->devinfo.irq=-1;
+-		    s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags);
+-		    return -ENODEV;
+-		}
+-		if (tapestate_get (ti) != TS_DONE) {
+-			tapestate_set (ti, TS_IDLE);
+-			s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-			return -EIO;
+-		}
+-		tapestate_set (ti, TS_IDLE);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
 -#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,3,"b:cqrNULL");
+-	        debug_text_event (tape_debug_area,6,"c:wbytes:"); 
+-		debug_int_event (tape_debug_area,6,block_size - ti->devstat.rescnt);
 -#endif
--	schedule_tapeblock_exec_IO(ti);
--	ti->current_request=NULL;
--	return;
--    }
--    ti->blk_retries = TAPEBLOCK_RETRIES;
--    rc= do_IO (ti->devinfo.irq, ti->cqr->cpaddr, 
--	       (unsigned long) ti->cqr, 0x00, ti->cqr->options);
--    if (rc) {
--	// okay. ssch failed. we try later.
+-		written += block_size - ti->devstat.rescnt;
+-		if (ti->devstat.rescnt > 0) {
+-			*ppos = pos + written;
+-			return written;
+-		}
+-	}
 -#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,3,"b:doIOfail");
+-	debug_text_event (tape_debug_area,6,"c:wtotal:");
+-	debug_int_event (tape_debug_area,6,written);
 -#endif
--	ti->discipline->free_bread(ti->cqr,ti);
--	ti->cqr=NULL;
--	ti->current_request=NULL;
--	schedule_tapeblock_exec_IO(ti);
--	return;
--    }
--    // our request is in IO. we remove it from the queue and exit
--    tape_dequeue_request (&ti->request_queue,req);
--}
--
--static void 
--do_tape_request (request_queue_t * queue) {
--    tape_info_t* ti;
--    long lockflags;
--    for (ti=first_tape_info;
--	 ((ti!=NULL) && ((&ti->request_queue)!=queue));
--	 ti=ti->next);
--    if (ti==NULL) BUG();
--    s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--    if (tapestate_get(ti)!=TS_IDLE) {
--	s390irq_spin_unlock_irqrestore(ti->devinfo.irq,lockflags);
--	return;
--    }
--    if (tapestate_get(ti)!=TS_IDLE) BUG();
--    tapestate_set(ti,TS_BLOCK_INIT);
--    tapeblock_exec_IO(ti);
--    s390irq_spin_unlock_irqrestore(ti->devinfo.irq,lockflags);
--}
--
--static void
--run_tapeblock_exec_IO (tape_info_t* ti) {
--    long flags_390irq,flags_ior;
--    spin_lock_irqsave (&io_request_lock, flags_ior);
--    s390irq_spin_lock_irqsave(ti->devinfo.irq,flags_390irq);
--    atomic_set(&ti->bh_scheduled,0);
--    tapeblock_exec_IO(ti);
--    s390irq_spin_unlock_irqrestore(ti->devinfo.irq,flags_390irq);
--    spin_unlock_irqrestore (&io_request_lock, flags_ior);
+-	*ppos = pos + written;
+-	return written;
 -}
 -
--void
--schedule_tapeblock_exec_IO (tape_info_t *ti)
+-static int
+-tape_mtioctop (struct file *filp, short mt_op, int mt_count)
 -{
--	/* Protect against rescheduling, when already running */
--        if (atomic_compare_and_swap(0,1,&ti->bh_scheduled)) {
--                return;
--        }
--#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
--	INIT_LIST_HEAD(&ti->bh_tq.list);
+-	tape_info_t *ti;
+-	ccw_req_t *cqr = NULL;
+-	int rc;
+-	long lockflags;
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"c:mtio");
+-	debug_text_event (tape_debug_area,6,"c:ioop:");
+-	debug_int_event (tape_debug_area,6,mt_op); 
+-	debug_text_event (tape_debug_area,6,"c:arg:");
+-	debug_int_event (tape_debug_area,6,mt_count);
 -#endif
--	ti->bh_tq.sync = 0;
--	ti->bh_tq.routine = (void *) (void *) run_tapeblock_exec_IO;
--	ti->bh_tq.data = ti;
+-	ti = first_tape_info;
+-	while ((ti != NULL) && (ti->rew_filp != filp) && (ti->nor_filp != filp))
+-		ti = (tape_info_t *) ti->next;
+-	if (ti == NULL)
+-		return -ENODEV;
+-	switch (mt_op) {
+-	case MTREW:		// rewind
 -
--	queue_task (&ti->bh_tq, &tq_immediate);
--	mark_bh (IMMEDIATE_BH);
--	return;
--}
+-		cqr = ti->discipline->mtrew (ti, mt_count);
+-		break;
+-	case MTOFFL:		// put drive offline
+-
+-		cqr = ti->discipline->mtoffl (ti, mt_count);
+-		break;
+-	case MTUNLOAD:		// unload the tape
+-
+-		cqr = ti->discipline->mtunload (ti, mt_count);
+-		break;
+-	case MTWEOF:		// write tapemark
+-
+-		cqr = ti->discipline->mtweof (ti, mt_count);
+-		break;
+-	case MTFSF:		// forward space file
+-
+-		cqr = ti->discipline->mtfsf (ti, mt_count);
+-		break;
+-	case MTBSF:		// backward space file
+-
+-		cqr = ti->discipline->mtbsf (ti, mt_count);
+-		break;
+-	case MTFSFM:		// forward space file, stop at BOT side
+-
+-		cqr = ti->discipline->mtfsfm (ti, mt_count);
+-		break;
+-	case MTBSFM:		// backward space file, stop at BOT side
+-
+-		cqr = ti->discipline->mtbsfm (ti, mt_count);
+-		break;
+-	case MTFSR:		// forward space file
+-
+-		cqr = ti->discipline->mtfsr (ti, mt_count);
+-		break;
+-	case MTBSR:		// backward space file
+-
+-		cqr = ti->discipline->mtbsr (ti, mt_count);
+-		break;
+-	case MTNOP:
+-		cqr = ti->discipline->mtnop (ti, mt_count);
+-		break;
+-	case MTEOM:		// postion at the end of portion
+-
+-	case MTRETEN:		// retension the tape
+-
+-		cqr = ti->discipline->mteom (ti, mt_count);
+-		break;
+-	case MTERASE:
+-		cqr = ti->discipline->mterase (ti, mt_count);
+-		break;
+-	case MTSETDENSITY:
+-		cqr = ti->discipline->mtsetdensity (ti, mt_count);
+-		break;
+-	case MTSEEK:
+-		cqr = ti->discipline->mtseek (ti, mt_count);
+-		break;
+-	case MTSETDRVBUFFER:
+-		cqr = ti->discipline->mtsetdrvbuffer (ti, mt_count);
+-		break;
+-	case MTLOCK:
+-		cqr = ti->discipline->mtsetdrvbuffer (ti, mt_count);
+-		break;
+-	case MTUNLOCK:
+-		cqr = ti->discipline->mtsetdrvbuffer (ti, mt_count);
+-		break;
+-	case MTLOAD:
+-		cqr = ti->discipline->mtload (ti, mt_count);
+-		if (cqr!=NULL) break; // if backend driver has an load function ->use it
+-		// if no medium is in, wait until it gets inserted
+-		if (ti->medium_is_unloaded) {
+-		    wait_event_interruptible (ti->wq,ti->medium_is_unloaded==0);
+-		}
+-		return 0;
+-	case MTCOMPRESSION:
+-		cqr = ti->discipline->mtcompression (ti, mt_count);
+-		break;
+-	case MTSETPART:
+-		cqr = ti->discipline->mtsetpart (ti, mt_count);
+-		break;
+-	case MTMKPART:
+-		cqr = ti->discipline->mtmkpart (ti, mt_count);
+-		break;
+-	case MTTELL:		// return number of block relative to current file
 -
--/* wrappers around do_tape_request for different kernel versions */
--#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98))
--static void tape_request_fn (void) {
--    tape_info_t* ti=first_tape_info;
--    while (ti!=NULL) {
--	do_tape_request(&ti->request_queue);
--	ti=ti->next;
--    }
--}
--#else
--static void  tape_request_fn (request_queue_t* queue) {
--    do_tape_request(queue);
--}
+-		cqr = ti->discipline->mttell (ti, mt_count);
+-		break;
+-	case MTSETBLK:
+-		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-		ti->block_size = mt_count;
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-#ifdef TAPE_DEBUG
+-		debug_text_event (tape_debug_area,6,"c:setblk:");
+-	        debug_int_event (tape_debug_area,6,mt_count);
 -#endif
--
--static request_queue_t* tapeblock_getqueue (kdev_t kdev) {
--    tape_info_t* ti=first_tape_info;
--    while ((ti!=NULL) && (MINOR(kdev)!=ti->blk_minor)) 
--        ti=ti->next;
--    if (ti!=NULL) return &ti->request_queue;
--    return NULL;
--}
--
--int tapeblock_mediumdetect(tape_info_t* ti) {
--        ccw_req_t* cqr;
--    int losize=1,hisize=1,rc;
--    long lockflags;
+-		return 0;
+-	case MTRESET:
+-		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-		ti->kernbuf = ti->userbuf = NULL;
+-		tapestate_set (ti, TS_IDLE);
+-		ti->block_size = 0;
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
 -#ifdef TAPE_DEBUG
--    debug_text_event (tape_debug_area,3,"b:medDet");
+-		debug_text_event (tape_debug_area,6,"c:devreset:");
+-		debug_int_event (tape_debug_area,6,ti->blk_minor);
 -#endif
--    PRINT_WARN("Detecting media size. This will take _long_, so get yourself a coffee...\n");
--    while (1) { //is interruped by break
--	hisize=hisize << 1; // try twice the size tested before 
--	cqr=ti->discipline->mtseek (ti, hisize);
+-		return 0;
+-	default:
+-#ifdef TAPE_DEBUG
+-	    debug_text_event (tape_debug_area,6,"c:inv.mtio");
+-#endif
+-		return -EINVAL;
+-	}
 -	if (cqr == NULL) {
 -#ifdef TAPE_DEBUG
--	    debug_text_event (tape_debug_area,6,"b:ccwg fail");
+-	        debug_text_event (tape_debug_area,6,"c:ccwg fail");
 -#endif
--	    return -ENOSPC;
+-		return -ENOSPC;
 -	}
 -	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
 -	ti->cqr = cqr;
 -	ti->wanna_wakeup=0;
 -	rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
 -	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	if (rc) return -EIO;
 -	wait_event_interruptible (ti->wq,ti->wanna_wakeup);
 -	ti->cqr = NULL;
+-	if (ti->kernbuf != NULL) {
+-		kfree (ti->kernbuf);
+-		ti->kernbuf = NULL;
+-	}
 -	tape_free_request (cqr);
--	if (ti->kernbuf) {
--	    kfree (ti->kernbuf);
--	    ti->kernbuf=NULL;
+-	// if medium was unloaded, update the corresponding variable.
+-	switch (mt_op) {
+-	case MTOFFL:
+-	case MTUNLOAD:
+-	    ti->medium_is_unloaded=1;
 -	}
 -	if (signal_pending (current)) {
 -		tapestate_set (ti, TS_IDLE);
 -		return -ERESTARTSYS;
 -	}
 -	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	if (((mt_op == MTEOM) || (mt_op == MTRETEN)) && (tapestate_get (ti) == TS_FAILED))
+-		tapestate_set (ti, TS_DONE);
 -	if (tapestate_get (ti) == TS_FAILED) {
 -		tapestate_set (ti, TS_IDLE);
 -		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		break;
+-		return ti->rc;
 -	}
 -	if (tapestate_get (ti) == TS_NOT_OPER) {
 -	    ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
@@ -28762,1057 +24959,4713 @@
 -	}
 -	tapestate_set (ti, TS_IDLE);
 -	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	losize=hisize;
--    }
--    cqr = ti->discipline->mtrew (ti, 1);
--    if (cqr == NULL) {
+-	switch (mt_op) {
+-	case MTRETEN:		//need to rewind the tape after moving to eom
+-
+-		return tape_mtioctop (filp, MTREW, 1);
+-	case MTFSFM:		//need to skip back over the filemark
+-
+-		return tape_mtioctop (filp, MTBSFM, 1);
+-	case MTBSF:		//need to skip forward over the filemark
+-
+-		return tape_mtioctop (filp, MTFSF, 1);
+-	}
 -#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"b:ccwg fail");
+-	debug_text_event (tape_debug_area,6,"c:mtio done");
 -#endif
--	return -ENOSPC;
--    }
--    s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--    ti->cqr = cqr;
--    ti->wanna_wakeup=0;
--    rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
--    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--    wait_event_interruptible (ti->wq,ti->wanna_wakeup);
--    ti->cqr = NULL;
--    tape_free_request (cqr);
--    if (signal_pending (current)) {
--	tapestate_set (ti, TS_IDLE);
--	return -ERESTARTSYS;
--    }
--    s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--    if (tapestate_get (ti) == TS_FAILED) {
--	tapestate_set (ti, TS_IDLE);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	return -EIO;
--    }
--    if (tapestate_get (ti) == TS_NOT_OPER) {
--	ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
--	ti->devinfo.irq=-1;
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags);
--	return -ENODEV;
--    }
--    if (tapestate_get (ti) != TS_DONE) {
--	tapestate_set (ti, TS_IDLE);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	return -EIO;
--    }
--    tapestate_set (ti, TS_IDLE);
--    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--    while (losize!=hisize) {
--	cqr=ti->discipline->mtseek (ti, (hisize+losize)/2+1);
--	if (cqr == NULL) {
+-	return 0;
+-}
+-
+-/*
+- * Tape device io controls.
+- */
+-int
+-tape_ioctl (struct inode *inode, struct file *filp,
+-	    unsigned int cmd, unsigned long arg)
+-{
+-	long lockflags;
+-	tape_info_t *ti;
+-	ccw_req_t *cqr;
+-	struct mtop op;		/* structure for MTIOCTOP */
+-	struct mtpos pos;	/* structure for MTIOCPOS */
+-	struct mtget get;
+-
+-	int rc;
 -#ifdef TAPE_DEBUG
--	    debug_text_event (tape_debug_area,6,"b:ccwg fail");
+-	debug_text_event (tape_debug_area,6,"c:ioct");
 -#endif
--	    return -ENOSPC;
--	}
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->cqr = cqr;
--	ti->wanna_wakeup=0;
--	rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	if (rc) return -EIO;
--	wait_event_interruptible (ti->wq,ti->wanna_wakeup);
--	ti->cqr = NULL;
--	tape_free_request (cqr);
--	if (ti->kernbuf) {
--	    kfree (ti->kernbuf);
--	    ti->kernbuf=NULL;
--	}
--	if (signal_pending (current)) {
--		tapestate_set (ti, TS_IDLE);
--		return -ERESTARTSYS;
--	}
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	if (tapestate_get (ti) == TS_NOT_OPER) {
--	    ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
--	    ti->devinfo.irq=-1;
--	    s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags);
--	    return -ENODEV;
+-	ti = first_tape_info;
+-	while ((ti != NULL) &&
+-	       (ti->rew_minor != MINOR (inode->i_rdev)) &&
+-	       (ti->nor_minor != MINOR (inode->i_rdev)))
+-		ti = (tape_info_t *) ti->next;
+-	if (ti == NULL) {
+-#ifdef TAPE_DEBUG
+-	        debug_text_event (tape_debug_area,6,"c:nodev");
+-#endif
+-		return -ENODEV;
 -	}
--	if (tapestate_get (ti) == TS_FAILED) {
--		tapestate_set (ti, TS_IDLE);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		hisize=(hisize+losize)/2;
--		cqr = ti->discipline->mtrew (ti, 1);
--		if (cqr == NULL) {
+-	// check for discipline ioctl overloading
+-	if ((rc = ti->discipline->discipline_ioctl_overload (inode, filp, cmd, arg))
+-	    != -EINVAL) {
 -#ifdef TAPE_DEBUG
--		    debug_text_event (tape_debug_area,6,"b:ccwg fail");
+-	    debug_text_event (tape_debug_area,6,"c:ioverloa");
 -#endif
--		    return -ENOSPC;
+-	    return rc;
+-	}
+-
+-	switch (cmd) {
+-	case MTIOCTOP:		/* tape op command */
+-		if (copy_from_user (&op, (char *) arg, sizeof (struct mtop))) {
+-			 return -EFAULT;
 -		}
+-		return (tape_mtioctop (filp, op.mt_op, op.mt_count));
+-	case MTIOCPOS:		/* query tape position */
+-		cqr = ti->discipline->mttell (ti, 0);
 -		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
 -		ti->cqr = cqr;
 -		ti->wanna_wakeup=0;
--		rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+-		do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
 -		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
 -		wait_event_interruptible (ti->wq,ti->wanna_wakeup);
+-		pos.mt_blkno = ti->rc;
 -		ti->cqr = NULL;
+-		if (ti->kernbuf != NULL) {
+-			kfree (ti->kernbuf);
+-			ti->kernbuf = NULL;
+-		}
 -		tape_free_request (cqr);
 -		if (signal_pending (current)) {
--		    tapestate_set (ti, TS_IDLE);
--		    return -ERESTARTSYS;
+-			tapestate_set (ti, TS_IDLE);
+-			return -ERESTARTSYS;
 -		}
 -		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--		if (tapestate_get (ti) == TS_FAILED) {
--		    tapestate_set (ti, TS_IDLE);
--		    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		    return -EIO;
--		}
--		if (tapestate_get (ti) != TS_DONE) {
--		    tapestate_set (ti, TS_IDLE);
--		    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		    return -EIO;
--		}
 -		tapestate_set (ti, TS_IDLE);
 -		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		continue;
--	}
--	if (tapestate_get (ti) != TS_DONE) {
--		tapestate_set (ti, TS_IDLE);
+-		if (copy_to_user ((char *) arg, &pos, sizeof (struct mtpos)))
+-			 return -EFAULT;
+-		return 0;
+-	case MTIOCGET:
+-		get.mt_erreg = ti->rc;
+-		cqr = ti->discipline->mttell (ti, 0);
+-		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-		ti->cqr = cqr;
+-		ti->wanna_wakeup=0;
+-		do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
 -		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		return -EIO;
--	}
--	tapestate_set (ti, TS_IDLE);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	losize=(hisize+losize)/2+1;
--    }
--    blk_size[tapeblock_major][ti->blk_minor]=(losize)*(blksize_size[tapeblock_major][ti->blk_minor]/1024);
--    return 0;
--}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapeblock.h drivers/s390/char/tapeblock.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapeblock.h	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tapeblock.h	2006-01-30 22:25:26.000000000 -0700
-@@ -1,36 +0,0 @@
--
--/***************************************************************************
-- *
-- *  drivers/s390/char/tapechar.h
-- *    character device frontend for tape device driver
-- *
-- *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-- *    Author(s): Carsten Otte <cotte at de.ibm.com>
-- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-- *
-- *
-- ****************************************************************************
-- */
--
--#ifndef TAPEBLOCK_H
--#define TAPEBLOCK_H
--#include <linux/config.h>
--#define PARTN_BITS 0
--
--#define TAPEBLOCK_READAHEAD 30
--#define TAPEBLOCK_MAJOR 0
--
--#define TAPEBLOCK_DEFAULTMODE 0060644
--
--int tapeblock_open(struct inode *, struct file *);
--int tapeblock_release(struct inode *, struct file *);
--void tapeblock_setup(tape_info_t* ti);
--void schedule_tapeblock_exec_IO (tape_info_t *ti);
--int tapeblock_mediumdetect(tape_info_t* ti);
--#ifdef CONFIG_DEVFS_FS
--void tapeblock_mkdevfstree (tape_info_t* ti);
--#endif
--int tapeblock_init (void);
--void tapeblock_uninit (void);
--#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapechar.c drivers/s390/char/tapechar.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapechar.c	2004-08-07 17:26:05.000000000 -0600
-+++ drivers/s390/char/tapechar.c	2006-01-30 22:25:26.000000000 -0700
-@@ -1,764 +0,0 @@
+-		wait_event_interruptible (ti->wq,ti->wanna_wakeup);
+-		get.mt_blkno = ti->rc;
+-		get.mt_fileno = 0;
+-		get.mt_type = MT_ISUNKNOWN;
+-		get.mt_resid = ti->devstat.rescnt;
+-		get.mt_dsreg = ti->devstat.ii.sense.data[3];
+-		get.mt_gstat = 0;
+-		if (ti->devstat.ii.sense.data[1] & 0x08)
+-			get.mt_gstat &= GMT_BOT (1);	// BOT
 -
--/***************************************************************************
-- *
-- *  drivers/s390/char/tapechar.c
-- *    character device frontend for tape device driver
-- *
-- *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-- *    Author(s): Carsten Otte <cotte at de.ibm.com>
-- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
-- *
-- *
-- ****************************************************************************
-- */
+-		if (ti->devstat.ii.sense.data[1] & 0x02)
+-			get.mt_gstat &= GMT_WR_PROT (1);	// write protected
 -
--#include "tapedefs.h"
--#include <linux/config.h>
--#include <linux/version.h>
--#include <linux/types.h>
--#include <linux/proc_fs.h>
--#include <asm/ccwcache.h>	/* CCW allocations      */
--#include <asm/s390dyn.h>
--#include <asm/debug.h>
--#include <linux/mtio.h>
--#include <asm/uaccess.h>
--#include <linux/compatmac.h>
--#ifdef MODULE
--#define __NO_VERSION__
--#include <linux/module.h>
--#endif
--#include "tape.h"
--#include "tapechar.h"
+-		if (ti->devstat.ii.sense.data[1] & 0x40)
+-			get.mt_gstat &= GMT_ONLINE (1);		//drive online
 -
--#define PRINTK_HEADER "TCHAR:"
+-		ti->cqr = NULL;
+-		if (ti->kernbuf != NULL) {
+-			kfree (ti->kernbuf);
+-			ti->kernbuf = NULL;
+-		}
+-		tape_free_request (cqr);
+-		if (signal_pending (current)) {
+-			tapestate_set (ti, TS_IDLE);
+-			return -ERESTARTSYS;
+-		}
+-		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-		tapestate_set (ti, TS_IDLE);
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-		if (copy_to_user ((char *) arg, &get, sizeof (struct mtget)))
+-			 return -EFAULT;
+-		return 0;
+-	default:
+-#ifdef TAPE_DEBUG
+-	        debug_text_event (tape_debug_area,3,"c:ioct inv");
+-#endif	    
+-		return -EINVAL;
+-	}
+-}
 -
 -/*
-- * file operation structure for tape devices
+- * Tape device open function.
 - */
--static struct file_operations tape_fops =
--{
--    //    owner   : THIS_MODULE,
--	llseek:NULL,		/* lseek - default */
--	read:tape_read,		/* read  */
--	write:tape_write,	/* write */
--	readdir:NULL,		/* readdir - bad */
--	poll:NULL,		/* poll */
--	ioctl:tape_ioctl,	/* ioctl */
--	mmap:NULL,		/* mmap */
--	open:tape_open,		/* open */
--	flush:NULL,		/* flush */
--	release:tape_release,	/* release */
--	fsync:NULL,		/* fsync */
--	fasync:NULL,		/* fasync */
--	lock:NULL,
--};
--
--int tape_major = TAPE_MAJOR;
--
--#ifdef CONFIG_DEVFS_FS
--void
--tapechar_mkdevfstree (tape_info_t* ti) {
--    ti->devfs_char_dir=devfs_mk_dir (ti->devfs_dir, "char", ti);
--    ti->devfs_nonrewinding=devfs_register(ti->devfs_char_dir, "nonrewinding",
--					    DEVFS_FL_DEFAULT,tape_major, 
--					    ti->nor_minor, TAPECHAR_DEFAULTMODE, 
--					    &tape_fops, ti);
--    ti->devfs_rewinding=devfs_register(ti->devfs_char_dir, "rewinding",
--					 DEVFS_FL_DEFAULT, tape_major, ti->rew_minor,
--					 TAPECHAR_DEFAULTMODE, &tape_fops, ti);
--}
--
--void
--tapechar_rmdevfstree (tape_info_t* ti) {
--    devfs_unregister(ti->devfs_nonrewinding);
--    devfs_unregister(ti->devfs_rewinding);
--    devfs_unregister(ti->devfs_char_dir);
--}
--#endif
--
--void
--tapechar_setup (tape_info_t * ti)
--{
--#ifdef CONFIG_DEVFS_FS
--    tapechar_mkdevfstree(ti);
--#endif
--}
--
--void
--tapechar_init (void)
+-int
+-tape_open (struct inode *inode, struct file *filp)
 -{
--	int result;
--	tape_frontend_t *charfront,*temp;
--	tape_info_t* ti;
--
--	tape_init();
--
--	/* Register the tape major number to the kernel */
--#ifdef CONFIG_DEVFS_FS
--	result = devfs_register_chrdev (tape_major, "tape", &tape_fops);
--#else
--	result = register_chrdev (tape_major, "tape", &tape_fops);
--#endif
+-	tape_info_t *ti;
+-	kdev_t dev;
+-	long lockflags;
 -
--	if (result < 0) {
--		PRINT_WARN (KERN_ERR "tape: can't get major %d\n", tape_major);
--#ifdef TAPE_DEBUG
--		debug_text_event (tape_debug_area,3,"c:initfail");
--		debug_text_event (tape_debug_area,3,"regchrfail");
--#endif /* TAPE_DEBUG */
--		panic ("no major number available for tape char device");
--	}
--	if (tape_major == 0)
--		tape_major = result;	/* accept dynamic major number */
--	PRINT_WARN (KERN_ERR " tape gets major %d for character device\n", result);
--	charfront = kmalloc (sizeof (tape_frontend_t), GFP_KERNEL);
--	if (charfront == NULL) {
+-	inode = filp->f_dentry->d_inode;
+-	ti = first_tape_info;
+-	while ((ti != NULL) &&
+-	       (ti->rew_minor != MINOR (inode->i_rdev)) &&
+-	       (ti->nor_minor != MINOR (inode->i_rdev)))
+-		ti = (tape_info_t *) ti->next;
+-	if (ti == NULL)
+-		return -ENODEV;
 -#ifdef TAPE_DEBUG
--                debug_text_event (tape_debug_area,3,"c:initfail");
--		debug_text_event (tape_debug_area,3,"no mem");
--#endif /* TAPE_DEBUG */
--		panic ("no major number available for tape char device");		
--	}
--	charfront->device_setup = tapechar_setup;
--#ifdef CONFIG_DEVFS_FS
--	charfront->mkdevfstree = tapechar_mkdevfstree;
--	charfront->rmdevfstree = tapechar_rmdevfstree;
+-	debug_text_event (tape_debug_area,6,"c:open:");
+-	debug_int_event (tape_debug_area,6,ti->blk_minor);
 -#endif
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	if (tapestate_get (ti) != TS_UNUSED) {
+-		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
 -#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,3,"c:init ok");
--#endif /* TAPE_DEBUG */
--	charfront->next=NULL;
--	if (first_frontend==NULL) {
--	    first_frontend=charfront;
--	} else {
--	    temp=first_frontend;
--	    while (temp->next!=NULL)
--		temp=temp->next;
--	    temp->next=charfront;
--	}
--	ti=first_tape_info;
--	while (ti!=NULL) {
--	    tapechar_setup(ti);
--	    ti=ti->next;
+-		debug_text_event (tape_debug_area,6,"c:dbusy");
+-#endif
+-		return -EBUSY;
 -	}
--}
+-	tapestate_set (ti, TS_IDLE);
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
 -
--void
--tapechar_uninit (void)
--{
--	unregister_chrdev (tape_major, "tape");
+-	dev = MKDEV (tape_major, MINOR (inode->i_rdev));	/* Get the device */
+-	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-	if (ti->rew_minor == MINOR (inode->i_rdev))
+-		ti->rew_filp = filp;	/* save for later reference     */
+-	else
+-		ti->nor_filp = filp;
+-	filp->private_data = ti;	/* save the dev.info for later reference */
+-	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-
+-#ifdef MODULE
+-	MOD_INC_USE_COUNT;
+-#endif				/* MODULE */
+-	return 0;
 -}
 -
 -/*
-- * Tape device read function
+- * Tape device release function.
 - */
--ssize_t
--tape_read (struct file *filp, char *data, size_t count, loff_t * ppos)
+-int
+-tape_release (struct inode *inode, struct file *filp)
 -{
 -	long lockflags;
--	tape_info_t *ti;
--	size_t block_size;
--	ccw_req_t *cqr;
--	int rc;
--	loff_t pos = *ppos;
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"c:read");
--#endif /* TAPE_DEBUG */
+-	tape_info_t *ti,*lastti;
+-	ccw_req_t *cqr = NULL;
+-	int rc = 0;
+-
 -	ti = first_tape_info;
--	while ((ti != NULL) && (ti->rew_filp != filp) && (ti->nor_filp != filp))
+-	while ((ti != NULL) && (ti->rew_minor != MINOR (inode->i_rdev)) && (ti->nor_minor != MINOR (inode->i_rdev)))
 -		ti = (tape_info_t *) ti->next;
--	if (ti == NULL) {
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,6,"c:nodev");
--#endif /* TAPE_DEBUG */
--		return -ENODEV;
+-	if ((ti != NULL) && (tapestate_get (ti) == TS_NOT_OPER)) {
+-	    if (ti==first_tape_info) {
+-		first_tape_info=ti->next;
+-	    } else {
+-		lastti=first_tape_info;
+-		while (lastti->next!=ti) lastti=lastti->next;
+-		lastti->next=ti->next;
+-	    }
+-	    kfree(ti);    
+-	    goto out;
 -	}
--	if (ppos != &filp->f_pos) {
--		/* "A request was outside the capabilities of the device." */
+-	if ((ti == NULL) || (tapestate_get (ti) != TS_IDLE)) {
 -#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,6,"c:ppos wrong");
--#endif /* TAPE_DEBUG */
--		return -EOVERFLOW;	/* errno=75 Value too large for def. data type */
--	}
--	if (ti->block_size == 0) {
--		block_size = count;
--	} else {
--		block_size = ti->block_size;
+-	debug_text_event (tape_debug_area,6,"c:notidle!");
+-#endif
+-		rc = -ENXIO;	/* error in tape_release */
+-		goto out;
 -	}
 -#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:nbytes:");
--	debug_int_event (tape_debug_area,6,block_size);
+-	debug_text_event (tape_debug_area,6,"c:release:");
+-	debug_int_event (tape_debug_area,6,ti->blk_minor);
 -#endif
--	cqr = ti->discipline->read_block (data, block_size, ti);
--	if (!cqr) {
--		return -ENOBUFS;
--	}
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->cqr = cqr;
--	ti->wanna_wakeup=0;
--	rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
--	if (rc) {
--	    tapestate_set(ti,TS_IDLE);
--	    kfree (cqr);
--	    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	    return rc;
+-	if (ti->rew_minor == MINOR (inode->i_rdev)) {
+-		cqr = ti->discipline->mtrew (ti, 1);
+-		if (cqr != NULL) {
+-#ifdef TAPE_DEBUG
+-		        debug_text_event (tape_debug_area,6,"c:rewrelea");
+-#endif
+-			s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
+-			tapestate_set (ti, TS_REW_RELEASE_INIT);
+-			ti->cqr = cqr;
+-			ti->wanna_wakeup=0;
+-			rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
+-			s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
+-			wait_event (ti->wq,ti->wanna_wakeup);
+-			ti->cqr = NULL;
+-			tape_free_request (cqr);
+-		}
 -	}
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	wait_event (ti->wq,ti->wanna_wakeup);
--	ti->cqr = NULL;
--	ti->discipline->free_read_block (cqr, ti);
 -	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	if (tapestate_get (ti) == TS_FAILED) {
--		tapestate_set (ti, TS_IDLE);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		return ti->rc;
--	}
--	if (tapestate_get (ti) == TS_NOT_OPER) {
--	    ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
--	    ti->devinfo.irq=-1;
--	    s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags);
--	    return -ENODEV;
--	}
--	if (tapestate_get (ti) != TS_DONE) {
--		tapestate_set (ti, TS_IDLE);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		return -EIO;
--	}
--	tapestate_set (ti, TS_IDLE);
+-	tapestate_set (ti, TS_UNUSED);
 -	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:rbytes:");
--	debug_int_event (tape_debug_area,6,block_size - ti->devstat.rescnt);
--#endif	/* TAPE_DEBUG */
--	*ppos = pos + (block_size - ti->devstat.rescnt);
--	return block_size - ti->devstat.rescnt;
+-out:
+-#ifdef MODULE
+-	MOD_DEC_USE_COUNT;
+-#endif				/* MODULE */
+-	return rc;
 -}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tapechar.h drivers/s390/char/tapechar.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/tapechar.h	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tapechar.h	2006-02-12 12:47:23.000000000 -0700
+@@ -1,34 +0,0 @@
 -
+-/***************************************************************************
+- *
+- *  drivers/s390/char/tapechar.h
+- *    character device frontend for tape device driver
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s): Carsten Otte <cotte at de.ibm.com>
+- *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *
+- *
+- ****************************************************************************
+- */
+-
+-#ifndef TAPECHAR_H
+-#define TAPECHAR_H
+-#include <linux/config.h>
+-#define TAPECHAR_DEFAULTMODE 0020644
+-#define  TAPE_MAJOR                    0        /* get dynamic major since no major officialy defined for tape */
 -/*
-- * Tape device write function
+- * Prototypes for tape_fops
+- */
+-ssize_t tape_read(struct file *, char *, size_t, loff_t *);
+-ssize_t tape_write(struct file *, const char *, size_t, loff_t *);
+-int tape_ioctl(struct inode *,struct file *,unsigned int,unsigned long);
+-int tape_open (struct inode *,struct file *);
+-int tape_release (struct inode *,struct file *);
+-#ifdef CONFIG_DEVFS_FS
+-void tapechar_mkdevfstree (tape_info_t* ti);
+-#endif
+-void tapechar_init (void);
+-void tapechar_uninit (void);
+-#endif /* TAPECHAR_H */
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape_core.c drivers/s390/char/tape_core.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape_core.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/tape_core.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,1434 @@
++/*
++ *  drivers/s390/char/tape_core.c
++ *    basic function of the tape device driver
++ *
++ *  S390 and zSeries version
++ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Carsten Otte <cotte at de.ibm.com>
++ *               Michael Holzheu <holzheu at de.ibm.com>
++ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
++ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
++ *               Stefan Bader <shbader at de.ibm.com>
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>	     // for kernel parameters
++#include <linux/kmod.h>	     // for requesting modules
++#include <linux/spinlock.h>  // for locks
++#include <linux/vmalloc.h>
++
++#include <asm/types.h>	     // for variable types
++#include <asm/irq.h>
++#include <asm/s390io.h>
++#include <asm/s390dyn.h>
++
++#define TAPE_DBF_AREA	tape_core_dbf
++
++#include "tape.h"
++#include "tape_std.h"
++
++#ifdef CONFIG_S390_TAPE_3590
++#include "tape_3590.h"
++#endif
++
++#define PRINTK_HEADER "T390:"
++
++/*
++ * Prototypes for some static functions.
++ */
++static void __tape_do_irq (int, void *, struct pt_regs *);
++static void __tape_remove_request(struct tape_device *, struct tape_request *);
++static void tape_timeout_io (unsigned long);
++
++/*
++ * List of tape disciplines guarded by tape_discipline_lock.
++ */
++static struct list_head	tape_disciplines = LIST_HEAD_INIT(tape_disciplines);
++static spinlock_t tape_discipline_lock   = SPIN_LOCK_UNLOCKED;
++
++/*
++ * Pointer to debug area.
++ */
++debug_info_t *TAPE_DBF_AREA = NULL;
++
++const char *tape_op_verbose[TO_SIZE] =
++{
++	[TO_BLOCK]		= "BLK",
++	[TO_BSB]		= "BSB",
++	[TO_BSF]		= "BSF",
++	[TO_DSE]		= "DSE",
++	[TO_FSB]		= "FSB",
++	[TO_FSF]		= "FSF",
++	[TO_LBL]		= "LBL",
++	[TO_NOP]		= "NOP",
++	[TO_RBA]		= "RBA",
++	[TO_RBI]		= "RBI",
++	[TO_RFO]		= "RFO",
++	[TO_REW]		= "REW",
++	[TO_RUN]		= "RUN",
++	[TO_WRI]		= "WRI",
++	[TO_WTM]		= "WTM",
++	[TO_MSEN]		= "MSN",
++	[TO_LOAD]		= "LOA",
++	[TO_READ_CONFIG]	= "RCF",
++	[TO_READ_ATTMSG]	= "RAT",
++	[TO_DIS]		= "DIS",
++	[TO_ASSIGN]		= "ASS",
++	[TO_UNASSIGN]		= "UAS",
++	[TO_BREAKASS]		= "BRK"
++};
++
++/*
++ * Inline functions, that have to be defined.
++ */
++static inline struct tape_request *
++tape_get_next_request(struct tape_device *device) {
++	if(list_empty(&device->req_queue))
++		return NULL;
++	return list_entry(device->req_queue.next, struct tape_request, list);
++}
++
++/*
++ * I/O helper function. Adds the request to the request queue
++ * and starts it if the tape is idle. Has to be called with
++ * the device lock held.
++ */
++static inline int
++__do_IO(struct tape_device *device, struct tape_request *request)
++{
++	int rc = 0;
++
++	if(request->cpaddr == NULL)
++		BUG();
++
++	if(request->timeout.expires > 0) {
++		/* Init should be done by caller */
++		DBF_EVENT(6, "(%04x): starting timed request\n",
++			device->devstat.devno);
++
++		request->timeout.function = tape_timeout_io;
++		request->timeout.data     = (unsigned long)
++			tape_clone_request(request);
++		add_timer(&request->timeout);
++	}
++
++	rc = do_IO(device->devinfo.irq, request->cpaddr,
++		(unsigned long) request, 0x00, request->options);
++
++	return rc;
++}
++
++static void
++__tape_process_queue(void *data)
++{
++	struct tape_device *device = (struct tape_device *) data;
++	struct list_head *l, *n;
++	struct tape_request *request;
++	int rc;
++
++	DBF_EVENT(6, "tape_process_queue(%p)\n", device);
++
++	/*
++	 * We were told to be quiet. Do nothing for now.
++	 */
++	if (TAPE_NOACCESS(device)) {
++		return;
++	}
++
++	/*
++	 * Try to start each request on request queue until one is
++	 * started successful.
++	 */
++	list_for_each_safe(l, n, &device->req_queue) {
++		request = list_entry(l, struct tape_request, list);
++
++		/* Happens when new request arrive while still doing one. */
++		if (request->status == TAPE_REQUEST_IN_IO)
++			break;
++
++#ifdef CONFIG_S390_TAPE_BLOCK
++		if (request->op == TO_BLOCK)
++			device->discipline->check_locate(device, request);
++#endif
++		switch(request->op) {
++			case TO_MSEN:
++			case TO_ASSIGN:
++			case TO_UNASSIGN:
++			case TO_BREAKASS:
++				break;
++			default:
++				if (TAPE_OPEN(device))
++					break;
++				DBF_EVENT(3,
++					"TAPE(%04x): REQ in UNUSED state\n",
++					device->devstat.devno);
++		}
++
++		rc = __do_IO(device, request);
++		if (rc == 0) {
++			DBF_EVENT(6, "tape: do_IO success\n");
++			request->status = TAPE_REQUEST_IN_IO;
++			break;
++		}
++		/* Start failed. Remove request and indicate failure. */
++		if(rc == -EBUSY) {
++			DBF_EVENT(1, "tape: DOIO request on busy tape\n");
++			break;
++		}
++		DBF_EVENT(1, "tape: DOIO failed with er = %i\n", rc);
++
++		/* Set final status and remove. */
++		request->rc = rc;
++		__tape_remove_request(device, request);
++	}
++}
++
++static void
++tape_process_queue(void *data)
++{
++	unsigned long		flags;
++	struct tape_device *	device;
++
++	device = (struct tape_device *) data;
++	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
++	atomic_set(&device->bh_scheduled, 0);
++	__tape_process_queue(device);
++	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
++}
++
++void
++tape_schedule_bh(struct tape_device *device)
++{
++	/* Protect against rescheduling, when already running. */
++	if (atomic_compare_and_swap(0, 1, &device->bh_scheduled))
++		return;
++
++	INIT_LIST_HEAD(&device->bh_task.list);
++	device->bh_task.sync    = 0;
++	device->bh_task.routine = tape_process_queue;
++	device->bh_task.data    = device;
++
++	queue_task(&device->bh_task, &tq_immediate);
++	mark_bh(IMMEDIATE_BH);
++
++	return;
++}
++
++/*
++ * Stop running ccw. Has to be called with the device lock held.
++ */
++static inline int
++__tape_halt_io(struct tape_device *device, struct tape_request *request)
++{
++	int retries;
++	int rc;
++
++	/* SMB: This should never happen */
++	if(request->cpaddr == NULL)
++		BUG();
++
++	/* Check if interrupt has already been processed */
++	if (request->callback == NULL)
++		return 0;
++
++	/* Stop a possibly running timer */
++	if(request->timeout.expires) {
++		if(del_timer(&request->timeout) > 0) {
++			tape_put_request(request);
++			request->timeout.data = 0L;
++		}
++	}
++
++	rc = 0;
++	for (retries = 0; retries < 5; retries++) {
++		if (retries < 2)
++			rc = halt_IO(device->devinfo.irq,
++				     (long) request, request->options);
++		else
++			rc = clear_IO(device->devinfo.irq,
++				      (long) request, request->options);
++		if (rc == 0)
++			break;		/* termination successful */
++		if (rc == -ENODEV)
++			DBF_EXCEPTION(2, "device gone, retry\n");
++		else if (rc == -EIO)
++			DBF_EXCEPTION(2, "I/O error, retry\n");
++		else if (rc == -EBUSY)
++			DBF_EXCEPTION(2, "device busy, retry later\n");
++		else
++			BUG();
++	}
++	if (rc == 0) {
++		request->rc = -EIO;
++		request->status = TAPE_REQUEST_DONE;
++	}
++	return rc;
++}
++
++static void
++__tape_remove_request(struct tape_device *device, struct tape_request *request)
++{
++	/* First remove the request from the queue. */
++	list_del(&request->list);
++
++	/* This request isn't processed any further. */
++	request->status = TAPE_REQUEST_DONE;
++
++	/* Finally, if the callback hasn't been called, do it now. */
++	if (request->callback != NULL) {
++		request->callback(request, request->callback_data);
++		request->callback = NULL;
++	}
++}
++
++/*
++ * Tape state functions
++ */
++/*
++ * Printable strings for tape enumerations.
++ */
++const char *tape_state_string(struct tape_device *device) {
++	char *s = " ???? ";
++
++	if (TAPE_NOT_OPER(device)) {
++		s = "NOT_OP";
++	} else if (TAPE_NOACCESS(device)) {
++		s = "NO_ACC";
++	} else if (TAPE_BOXED(device)) {
++		s = "BOXED ";
++	} else if (TAPE_OPEN(device)) {
++		s = "IN_USE";
++	} else if (TAPE_ASSIGNED(device)) {
++		s = "ASSIGN";
++	} else if (TAPE_INIT(device)) {
++		s = "INIT  ";
++	} else if (TAPE_UNUSED(device)) {
++		s = "UNUSED";
++	}
++
++	return s;
++}
++
++void
++tape_state_set(struct tape_device *device, unsigned int status)
++{
++	const char *str;
++
++	/* Maybe nothing changed. */
++	if (device->tape_status == status)
++		return;
++
++	DBF_EVENT(4, "ts. dev:  %x\n", device->first_minor);
++	str = tape_state_string(device);
++	DBF_EVENT(4, "old ts:  0x%08x %s\n", device->tape_status, str);
++
++	device->tape_status = status;
++
++	str = tape_state_string(device);
++	DBF_EVENT(4, "new ts:  0x%08x %s\n", status, str);
++
++	wake_up(&device->state_change_wq);
++}
++
++void
++tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate)
++{
++	if (device->medium_state == newstate)
++		return;
++
++	switch(newstate){
++	case MS_UNLOADED:
++		device->tape_generic_status |= GMT_DR_OPEN(~0);
++		PRINT_INFO("(%04x): Tape is unloaded\n",
++			   device->devstat.devno);
++		break;
++	case MS_LOADED:
++		device->tape_generic_status &= ~GMT_DR_OPEN(~0);
++		PRINT_INFO("(%04x): Tape has been mounted\n",
++			   device->devstat.devno);
++		break;
++	default:
++		// print nothing
++		break;
++	}
++#ifdef CONFIG_S390_TAPE_BLOCK
++	tapeblock_medium_change(device);
++#endif
++	device->medium_state = newstate;
++	wake_up(&device->state_change_wq);
++}
++	
++static void
++tape_timeout_io(unsigned long data)
++{
++	struct tape_request	*request;
++	struct tape_device	*device;
++	unsigned long		 flags;
++
++	request = (struct tape_request *) data;
++	device  = request->device;
++
++	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
++	request->timeout.expires = 0;
++
++	if(request->callback != NULL) {
++		DBF_EVENT(3, "TAPE(%04x): %s timeout\n",
++			device->devstat.devno, tape_op_verbose[request->op]);
++		PRINT_ERR("TAPE(%04x): %s timeout\n",
++			device->devstat.devno, tape_op_verbose[request->op]);
++
++		if(__tape_halt_io(device, request) == 0)
++			DBF_EVENT(6, "tape_timeout_io: success\n");
++		else {
++			DBF_EVENT(2, "tape_timeout_io: halt_io failed\n");
++			PRINT_ERR("tape_timeout_io: halt_io failed\n");
++		}
++	}
++	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
++	tape_put_request(request);
++}
++
++/*
++ * DEVFS Functions
++ */
++#ifdef CONFIG_DEVFS_FS
++devfs_handle_t tape_devfs_root_entry;
++
++/*
++ * Create devfs root entry (devno in hex) for device td
++ */
++static int
++tape_mkdevfsroot (struct tape_device* device)
++{
++	char devno [5];
++
++	sprintf(devno, "%04x", device->devinfo.devno);
++	device->devfs_dir = devfs_mk_dir(tape_devfs_root_entry, devno, device);
++	return (device->devfs_dir == NULL) ? -ENOMEM : 0;
++}
++
++/*
++ * Remove devfs root entry for a device
++ */
++static void
++tape_rmdevfsroot (struct tape_device *device)
++{
++	if (device->devfs_dir) {
++		devfs_unregister(device->devfs_dir);
++		device->devfs_dir = NULL;
++	}
++}
++#endif
++
++/*
++ * Enable tape device
++ */
++int
++tape_request_irq(struct tape_device *device)
++{
++	int rc;
++
++	if (device->devinfo.status & DEVINFO_UNFRIENDLY_DEV) {
++		s390_trigger_resense(device->devinfo.irq);
++		rc = get_dev_info_by_devno(
++			device->devinfo.devno,
++			&device->devinfo
++		);
++		if (rc) {
++			DBF_EVENT(3, "get_dev_info_by_devno returned %d\n", rc);
++			if (rc == -EUSERS) {
++				device->devinfo.status |=
++					DEVINFO_UNFRIENDLY_DEV;
++				TAPE_SET_STATE(device, TAPE_STATUS_BOXED);
++			}
++			return rc;
++		}
++	}
++
++	/* Register IRQ. */
++	rc = s390_request_irq_special(
++		device->devinfo.irq,
++		__tape_do_irq,
++		tape_noper_handler,
++		SA_DOPATHGROUP,
++		TAPE_MAGIC,
++		&device->devstat
++	);
++	if (rc) {
++		DBF_EVENT(3, "s390_request_irq_special returned %d\n", rc);
++		if (rc == -EUSERS) {
++			TAPE_SET_STATE(device, TAPE_STATUS_BOXED);
++			device->devinfo.status |= DEVINFO_UNFRIENDLY_DEV;
++		}
++	} else {
++		s390_set_private_data(
++			device->devinfo.irq,
++			tape_clone_device(device)
++		);
++		TAPE_CLEAR_STATE(device, TAPE_STATUS_BOXED);
++	}
++
++	DBF_EVENT(3, "tape_request_irq returns %d\n", rc);
++	return rc;
++}
++
++void
++tape_free_irq(struct tape_device *device)
++{
++	if(!(device->devinfo.status & DEVINFO_UNFRIENDLY_DEV)) {
++		s390_set_private_data(device->devinfo.irq, NULL);
++		tape_put_device(device);
++		free_irq(device->devinfo.irq, &device->devstat);
++	}
++}
++
++int
++tape_enable_device(struct tape_device *device,
++		   struct tape_discipline *discipline)
++{
++	int rc;
++
++	device->discipline = discipline;
++	if (!TAPE_INIT(device))
++		return -EINVAL;
++
++	rc = tape_request_irq(device);
++	if(rc && rc != -EUSERS)
++		return rc;
++
++	/* Let the discipline have a go at the device. */
++	rc = discipline->setup_device(device);
++	if (rc) {
++		DBF_EVENT(3, "discipline->setup_device returned %d\n", rc);
++		tape_free_irq(device);
++		return rc;
++	}
++
++#ifdef CONFIG_DEVFS_FS
++	/* Create devfs entries */
++	rc = tape_mkdevfsroot(device);
++	if (rc){
++		DBF_EVENT(3, "tape_mkdevfsroot returned %d\n", rc);
++		PRINT_WARN ("Cannot create a devfs directory for "
++			    "device %04x\n", device->devinfo.devno);
++		device->discipline->cleanup_device(device);
++		tape_free_irq(device);
++		return rc;
++	}
++#endif
++	rc = tapechar_setup_device(device);
++	if (rc) {
++		DBF_EVENT(3, "tapechar_setup_device returned %d\n", rc);
++#ifdef CONFIG_DEVFS_FS
++		tape_rmdevfsroot(device);
++#endif
++		device->discipline->cleanup_device(device);
++		tape_free_irq(device);
++		return rc;
++	}
++#ifdef CONFIG_S390_TAPE_BLOCK
++	rc = tapeblock_setup_device(device);
++	if (rc) {
++		DBF_EVENT(3, "tapeblock_setup_device returned %d\n", rc);
++		tapechar_cleanup_device(device);
++#ifdef CONFIG_DEVFS_FS
++		tape_rmdevfsroot(device);
++#endif
++		device->discipline->cleanup_device(device);
++		tape_free_irq(device);
++		return rc;
++	}
++#endif
++
++	if(!TAPE_BOXED(device))
++		TAPE_CLEAR_STATE(device, TAPE_STATUS_INIT);
++
++	return 0;
++}
++
++/*
++ * Disable tape device. Check if there is a running request and
++ * terminate it. Post all queued requests with -EIO.
++ */
++void
++tape_disable_device(struct tape_device *device, int gone)
++{
++	struct list_head *	l, *n;
++	struct tape_request *	request;
++	unsigned long		flags;
++
++	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
++	/* Post remaining requests with -EIO */
++	list_for_each_safe(l, n, &device->req_queue) {
++		request = list_entry(l, struct tape_request, list);
++		if (request->status == TAPE_REQUEST_IN_IO && !gone) {
++			__tape_halt_io(device, request);
++		}
++
++		request->rc = -EIO;
++		request->status = TAPE_REQUEST_DONE;
++		__tape_remove_request(device, request);
++	}
++
++	if (TAPE_ASSIGNED(device) && !gone) {
++		spin_unlock(get_irq_lock(device->devinfo.irq));
++		if(
++			tape_unassign(
++				device,
++				TAPE_STATUS_ASSIGN_M|TAPE_STATUS_ASSIGN_A
++			) == 0
++		) {
++			printk(KERN_WARNING "%04x: automatically unassigned\n",
++				device->devinfo.devno);
++		}
++		spin_lock(get_irq_lock(device->devinfo.irq));
++	}
++
++	TAPE_SET_STATE(device, TAPE_STATUS_NOT_OPER);
++	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
++
++#ifdef CONFIG_S390_TAPE_BLOCK
++	tapeblock_cleanup_device(device);
++#endif
++	tapechar_cleanup_device(device);
++#ifdef CONFIG_DEVFS_FS
++	tape_rmdevfsroot(device);
++#endif
++	device->discipline->cleanup_device(device);
++	tape_free_irq(device);
++}
++
++/*
++ * Find discipline by cu_type.
++ */
++struct tape_discipline *
++tape_get_discipline(int cu_type)
++{
++	struct list_head *l;
++	struct tape_discipline *discipline, *tmp;
++
++	discipline = NULL;
++	spin_lock(&tape_discipline_lock);
++	list_for_each(l, &tape_disciplines) {
++		tmp = list_entry(l, struct tape_discipline, list);
++		if (tmp->cu_type == cu_type) {
++			discipline = tmp;
++			break;
++		}
++	}
++	if (discipline->owner != NULL) {
++		if (!try_inc_mod_count(discipline->owner))
++			/* Discipline is currently unloaded! */
++			discipline = NULL;
++	}
++	spin_unlock(&tape_discipline_lock);
++	return discipline;
++}
++
++/*
++ * Decrement usage count for discipline.
++ */
++void
++tape_put_discipline(struct tape_discipline *discipline)
++{
++	spin_lock(&tape_discipline_lock);
++	if (discipline->owner)
++		__MOD_DEC_USE_COUNT(discipline->owner);
++	spin_unlock(&tape_discipline_lock);
++}
++
++/*
++ * Register backend discipline
++ */
++int
++tape_register_discipline(struct tape_discipline *discipline)
++{
++	if (!try_inc_mod_count(THIS_MODULE))
++		/* Tape module is currently unloaded! */
++		return -ENOSYS;
++	spin_lock(&tape_discipline_lock);
++	list_add_tail(&discipline->list, &tape_disciplines);
++	spin_unlock(&tape_discipline_lock);
++	/* Now add the tape devices with matching cu_type. */
++	tape_add_devices(discipline);
++	return 0;
++}
++
++/*
++ * Unregister backend discipline
++ */
++void
++__tape_unregister_discipline(struct tape_discipline *discipline)
++{
++	list_del(&discipline->list);
++	/* Remove tape devices with matching cu_type. */
++	tape_remove_devices(discipline);
++	MOD_DEC_USE_COUNT;
++}
++
++void
++tape_unregister_discipline(struct tape_discipline *discipline)
++{
++	struct list_head *l;
++
++	spin_lock(&tape_discipline_lock);
++	list_for_each(l, &tape_disciplines) {
++		if (list_entry(l, struct tape_discipline, list) == discipline){
++			__tape_unregister_discipline(discipline);
++			break;
++		}
++	}
++	spin_unlock(&tape_discipline_lock);
++}
++
++/*
++ * Allocate a new tape ccw request
++ */
++struct tape_request *
++tape_alloc_request(int cplength, int datasize)
++{
++	struct tape_request *request;
++
++	if (datasize > PAGE_SIZE || (cplength*sizeof(ccw1_t)) > PAGE_SIZE)
++		BUG();
++
++	DBF_EVENT(5, "tape_alloc_request(%d,%d)\n", cplength, datasize);
++
++	request = (struct tape_request *)
++		kmalloc(sizeof(struct tape_request), GFP_KERNEL);
++	if (request == NULL) {
++		DBF_EXCEPTION(1, "cqra nomem\n");
++		return ERR_PTR(-ENOMEM);
++	}
++	memset(request, 0, sizeof(struct tape_request));
++	INIT_LIST_HEAD(&request->list);
++	atomic_set(&request->ref_count, 1);
++
++	/* allocate channel program */
++	if (cplength > 0) {
++		request->cpaddr =
++			kmalloc(cplength*sizeof(ccw1_t), GFP_ATOMIC | GFP_DMA);
++		if (request->cpaddr == NULL) {
++			DBF_EXCEPTION(1, "cqra nomem\n");
++			kfree(request);
++			return ERR_PTR(-ENOMEM);
++		}
++		memset(request->cpaddr, 0, cplength*sizeof(ccw1_t));
++	}
++	/* alloc small kernel buffer */
++	if (datasize > 0) {
++		request->cpdata = kmalloc(datasize, GFP_KERNEL | GFP_DMA);
++		if (request->cpdata == NULL) {
++			DBF_EXCEPTION(1, "cqra nomem\n");
++			if (request->cpaddr != NULL)
++				kfree(request->cpaddr);
++			kfree(request);
++			return ERR_PTR(-ENOMEM);
++		}
++		memset(request->cpdata, 0, datasize);
++	}
++
++	DBF_EVENT(5, "request=%p(%p/%p)\n", request, request->cpaddr,
++		request->cpdata);
++
++	return request;
++}
++
++/*
++ * Free tape ccw request
++ */
++void
++tape_free_request (struct tape_request * request)
++{
++	DBF_EVENT(5, "tape_free_request(%p)\n", request);
++
++	if (request->device != NULL) {
++		tape_put_device(request->device);
++		request->device = NULL;
++	}
++	if (request->cpdata != NULL) {
++		kfree(request->cpdata);
++	}
++	if (request->cpaddr != NULL) {
++		kfree(request->cpaddr);
++	}
++	kfree(request);
++}
++
++struct tape_request *
++tape_clone_request(struct tape_request *request)
++{
++	DBF_EVENT(5, "tape_clone_request(%p) = %i\n", request,
++		atomic_inc_return(&request->ref_count));
++	return request;
++}
++
++struct tape_request *
++tape_put_request(struct tape_request *request)
++{
++	int remain;
++
++	DBF_EVENT(4, "tape_put_request(%p)\n", request);
++	if((remain = atomic_dec_return(&request->ref_count)) > 0) {
++		DBF_EVENT(5, "remaining = %i\n", remain);
++	} else {
++		tape_free_request(request);
++	}
++
++	return NULL;
++}
++
++/*
++ * Write sense data to console/dbf
++ */
++void
++tape_dump_sense(struct tape_device* device, struct tape_request *request)
++{
++	devstat_t *stat;
++	unsigned int *sptr;
++
++	stat = &device->devstat;
++	PRINT_INFO("-------------------------------------------------\n");
++	PRINT_INFO("DSTAT : %02x  CSTAT: %02x	CPA: %04x\n",
++		   stat->dstat, stat->cstat, stat->cpa);
++	PRINT_INFO("DEVICE: %04x\n", device->devinfo.devno);
++	if (request != NULL)
++		PRINT_INFO("OP	  : %s\n", tape_op_verbose[request->op]);
++		
++	sptr = (unsigned int *) stat->ii.sense.data;
++	PRINT_INFO("Sense data: %08X %08X %08X %08X \n",
++		   sptr[0], sptr[1], sptr[2], sptr[3]);
++	PRINT_INFO("Sense data: %08X %08X %08X %08X \n",
++		   sptr[4], sptr[5], sptr[6], sptr[7]);
++	PRINT_INFO("--------------------------------------------------\n");
++}
++
++/*
++ * Write sense data to dbf
++ */
++void
++tape_dump_sense_dbf(struct tape_device *device, struct tape_request *request)
++{
++	devstat_t *stat = &device->devstat;
++	unsigned int *sptr;
++	const char* op;
++
++	if (request != NULL)
++		op = tape_op_verbose[request->op];
++	else
++		op = "---";
++	DBF_EVENT(3, "DSTAT : %02x   CSTAT: %02x\n", stat->dstat,stat->cstat);
++	DBF_EVENT(3, "DEVICE: %04x OP\t: %s\n", device->devinfo.devno,op);
++	sptr = (unsigned int *) stat->ii.sense.data;
++	DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]);
++	DBF_EVENT(3, "%08x %08x\n", sptr[2], sptr[3]);
++	DBF_EVENT(3, "%08x %08x\n", sptr[4], sptr[5]);
++	DBF_EVENT(3, "%08x %08x\n", sptr[6], sptr[7]);
++}
++
++static inline int
++__tape_do_io(struct tape_device *device, struct tape_request *request)
++{
++	if(TAPE_NOT_OPER(device))
++		return -ENODEV;
++
++	/* Some operations may happen even on an unused tape device */
++	switch(request->op) {
++		case TO_MSEN:
++		case TO_ASSIGN:
++		case TO_UNASSIGN:
++		case TO_BREAKASS:
++			break;
++		default:
++			if (!TAPE_OPEN(device))
++				return -ENODEV;
++	}
++
++	/* Add reference to device to the request. This increases the reference
++	   count. */
++	request->device = tape_clone_device(device);
++	request->status = TAPE_REQUEST_QUEUED;
++
++	list_add_tail(&request->list, &device->req_queue);
++	__tape_process_queue(device);
++
++	return 0;
++}
++
++/*
++ * Add the request to the request queue, try to start it if the
++ * tape is idle. Return without waiting for end of i/o.
++ */
++int
++tape_do_io_async(struct tape_device *device, struct tape_request *request)
++{
++	int  rc;
++	long flags;
++
++	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
++	/* Add request to request queue and try to start it. */
++	rc = __tape_do_io(device, request);
++	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
++	return rc;
++}
++
++/*
++ * tape_do_io/__tape_wake_up
++ * Add the request to the request queue, try to start it if the
++ * tape is idle and wait uninterruptible for its completion.
++ */
++static void
++__tape_wake_up(struct tape_request *request, void *data)
++{
++	request->callback = NULL;
++	wake_up((wait_queue_head_t *) data);
++}
++
++int
++tape_do_io(struct tape_device *device, struct tape_request *request)
++{
++	wait_queue_head_t	 wq;
++	long			 flags;
++	int			 rc;
++
++	DBF_EVENT(5, "tape: tape_do_io(%p, %p)\n", device, request);
++
++	init_waitqueue_head(&wq);
++	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
++	/* Setup callback */
++	request->callback = __tape_wake_up;
++	request->callback_data = &wq;
++	/* Add request to request queue and try to start it. */
++	rc = __tape_do_io(device, request);
++	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
++	if (rc)
++		return rc;
++	/* Request added to the queue. Wait for its completion. */
++	wait_event(wq, (request->callback == NULL));
++	/* Get rc from request */
++	return request->rc;
++}
++
++/*
++ * tape_do_io_interruptible/__tape_wake_up_interruptible
++ * Add the request to the request queue, try to start it if the
++ * tape is idle and wait uninterruptible for its completion.
++ */
++static void
++__tape_wake_up_interruptible(struct tape_request *request, void *data)
++{
++	request->callback = NULL;
++	wake_up_interruptible((wait_queue_head_t *) data);
++}
++
++int
++tape_do_io_interruptible(struct tape_device *device,
++			 struct tape_request *request)
++{
++	wait_queue_head_t	 wq;
++	long			 flags;
++	int			 rc;
++
++	DBF_EVENT(5, "tape: tape_do_io_int(%p, %p)\n", device, request);
++
++	init_waitqueue_head(&wq);
++	// debug paranoia
++	if(!device) BUG();
++	if(!request) BUG();
++
++	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
++	/* Setup callback */
++	request->callback = __tape_wake_up_interruptible;
++	request->callback_data = &wq;
++	rc = __tape_do_io(device, request);
++	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
++	if (rc)
++		return rc;
++	/* Request added to the queue. Wait for its completion. */
++	rc = wait_event_interruptible(wq, (request->callback == NULL));
++	if (rc != -ERESTARTSYS)
++		/* Request finished normally. */
++		return request->rc;
++	/* Interrupted by a signal. We have to stop the current request. */
++	spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
++	rc = __tape_halt_io(device, request);
++	if (rc == 0) {
++		DBF_EVENT(3, "IO stopped on irq %d\n", device->devinfo.irq);
++		rc = -ERESTARTSYS;
++	}
++	if(request->callback != NULL)
++		request->callback = __tape_wake_up;
++	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
++	wait_event(wq, (request->callback == NULL));
++
++	return rc;
++}
++
++
++/*
++ * Tape interrupt routine, called from Ingo's I/O layer
++ */
++static void
++__tape_do_irq (int irq, void *ds, struct pt_regs *regs)
++{
++	struct tape_device *device;
++	struct tape_request *request;
++	devstat_t *devstat;
++	int final;
++	int rc;
++
++	devstat = (devstat_t *) ds;
++	device = (struct tape_device *) s390_get_private_data(irq);
++	if (device == NULL) {
++		PRINT_ERR("could not get device structure for irq %d "
++			  "in interrupt\n", irq);
++		return;
++	}
++	request = (struct tape_request *) devstat->intparm;
++
++	DBF_EVENT(5, "tape: __tape_do_irq(%p, %p)\n", device, request);
++
++	if(request != NULL) {
++		if(request->status == TAPE_REQUEST_DONE) {
++			DBF_EVENT(3, "tape: IO stopped successfully\n");
++			__tape_remove_request(device, request);
++
++			/* Start next request. */
++			if (!list_empty(&device->req_queue))
++				tape_schedule_bh(device);
++			return;
++		}
++
++		/* Interrupt on a canceled request */
++		if(request != tape_get_next_request(device)) {
++			DBF_EVENT(3, "tape: late interrupt ingored\n");
++			return;
++		}
++
++		if(request->timeout.expires) {
++			/*
++			 * If the timer was not yet startet the reference to
++			 * the request has to be dropped here. Otherwise it
++			 * will be dropped by the timeout handler.
++			 */
++			if(del_timer(&request->timeout) > 0)
++				request->timeout.data = (unsigned long)
++					tape_put_request(request);
++		}
++	}
++
++	if (device->devstat.cstat & SCHN_STAT_INCORR_LEN)
++		DBF_EVENT(4, "tape: incorrect blocksize\n");
++
++	if (device->devstat.dstat != 0x0c){
++		/*
++		 * Any request that does not come back with channel end
++		 * and device end is unusual. Log the sense data.
++		 */
++		DBF_EVENT(3,"-- Tape Interrupthandler --\n");
++		tape_dump_sense_dbf(device, request);
++	}
++	if (TAPE_NOT_OPER(device)) {
++		DBF_EVENT(6, "tape:device is not operational\n");
++		return;
++	}
++
++	/* Some status handling */
++	if(devstat && devstat->dstat & DEV_STAT_UNIT_CHECK) {
++		unsigned char *sense = devstat->ii.sense.data;
++
++		if(!(sense[1] & SENSE_DRIVE_ONLINE))
++			device->tape_generic_status &= ~GMT_ONLINE(~0);
++	} else {
++		device->tape_generic_status |= GMT_ONLINE(~0);
++	}
++
++	rc = device->discipline->irq(device, request);
++	/*
++	 * rc < 0 : request finished unsuccessfully.
++	 * rc == TAPE_IO_SUCCESS: request finished successfully.
++	 * rc == TAPE_IO_PENDING: request is still running. Ignore rc.
++	 * rc == TAPE_IO_RETRY: request finished but needs another go.
++	 * rc == TAPE_IO_STOP: request needs to get terminated. 
++	 */
++	final = 0;
++	switch (rc) {
++		case TAPE_IO_SUCCESS:
++			final = 1;
++			break;
++		case TAPE_IO_PENDING:
++			break;
++		case TAPE_IO_RETRY:
++#ifdef CONFIG_S390_TAPE_BLOCK
++			if (request->op == TO_BLOCK)
++				device->discipline->check_locate(device, request);
++#endif
++			rc = __do_IO(device, request);
++			if (rc) {
++				DBF_EVENT(1, "tape: DOIO failed with er = %i\n", rc);
++				final = 1;
++			}
++			break;
++		case TAPE_IO_STOP:
++			__tape_halt_io(device, request);
++			rc = -EIO;
++			break;
++		default:
++			if (rc > 0) {
++				DBF_EVENT(6, "xunknownrc\n");
++				PRINT_ERR("Invalid return code from discipline "
++				  "interrupt function.\n");
++				rc = -EIO;
++			}
++			final = 1;
++			break;
++	}
++	if (final) {
++		/* This might be an unsolicited interrupt (no request) */
++		if(request != NULL) {
++			/* Set ending status. */
++			request->rc = rc;
++			__tape_remove_request(device, request);
++		}
++		/* Start next request. */
++		if (!list_empty(&device->req_queue))
++			tape_schedule_bh(device);
++	}
++}
++
++/*
++ * Lock a shared tape for our exclusive use.
++ */
++int
++tape_assign(struct tape_device *device, int type)
++{
++	int rc;
++
++	spin_lock_irq(&device->assign_lock);
++
++	/* The device is already assigned */
++	rc = 0;
++	if (!TAPE_ASSIGNED(device)) {
++		rc = device->discipline->assign(device);
++
++		spin_lock(get_irq_lock(device->devinfo.irq));
++		if (rc) {
++	        	PRINT_WARN(
++				"(%04x): assign failed - "
++				"device might be busy\n",
++				device->devstat.devno);
++	        	DBF_EVENT(3,
++				"(%04x): assign failed "
++				"- device might be busy\n",
++				device->devstat.devno);
++			TAPE_SET_STATE(device, TAPE_STATUS_BOXED);
++		} else {
++			DBF_EVENT(3, "(%04x): assign lpum = %02x\n",
++				device->devstat.devno, device->devstat.lpum);
++			tape_state_set(
++				device,
++				(device->tape_status | type) &
++				(~TAPE_STATUS_BOXED)
++			);
++		}
++	} else {
++		spin_lock(get_irq_lock(device->devinfo.irq));
++		TAPE_SET_STATE(device, type);
++	}
++	spin_unlock(get_irq_lock(device->devinfo.irq));
++	spin_unlock_irq(&device->assign_lock);
++
++	return rc;
++}
++
++/*
++ * Unlock a shared tape.
++ */
++int
++tape_unassign(struct tape_device *device, int type)
++{
++	int	 	rc;
++
++	spin_lock_irq(&device->assign_lock);
++
++	rc = 0;
++	spin_lock(get_irq_lock(device->devinfo.irq));
++	if (!TAPE_ASSIGNED(device)) {
++		spin_unlock(get_irq_lock(device->devinfo.irq));
++		spin_unlock_irq(&device->assign_lock);
++		return 0;
++	}
++	TAPE_CLEAR_STATE(device, type);
++	spin_unlock(get_irq_lock(device->devinfo.irq));
++
++	if (!TAPE_ASSIGNED(device)) {
++		rc = device->discipline->unassign(device);
++		if (rc) {
++			PRINT_WARN("(%04x): unassign failed\n",
++				device->devstat.devno);
++			DBF_EVENT(3, "(%04x): unassign failed\n",
++				device->devstat.devno);
++		} else {
++			DBF_EVENT(3, "(%04x): unassign lpum = %02x\n",
++				device->devstat.devno, device->devstat.lpum);
++		}
++	}
++
++	spin_unlock_irq(&device->assign_lock);
++	return rc;
++}
++
++/*
++ * Tape device open function used by tape_char & tape_block frontends.
++ */
++int
++tape_open(struct tape_device *device)
++{
++	int rc;
++
++	if(TAPE_INIT(device) && TAPE_BOXED(device)) {
++		rc = tape_request_irq(device);
++		if (rc) {
++			if(rc == -EUSERS)
++				return -EPERM;
++			return rc;
++		} else {
++			TAPE_CLEAR_STATE(device, TAPE_STATUS_INIT);
++		}
++	}
++
++	spin_lock_irq(&tape_discipline_lock);
++	spin_lock(get_irq_lock(device->devinfo.irq));
++	if (TAPE_NOT_OPER(device)) {
++		DBF_EVENT(6, "TAPE:nodev\n");
++		rc = -ENODEV;
++	} else if (TAPE_OPEN(device)) {
++		DBF_EVENT(6, "TAPE:dbusy\n");
++		rc = -EBUSY;
++	} else if (device->discipline != NULL &&
++		   !try_inc_mod_count(device->discipline->owner)) {
++		DBF_EVENT(6, "TAPE:nodisc\n");
++		rc = -ENODEV;
++	} else {
++		TAPE_SET_STATE(device, TAPE_STATUS_OPEN);
++		rc = 0;
++	}
++	spin_unlock(get_irq_lock(device->devinfo.irq));
++	spin_unlock_irq(&tape_discipline_lock);
++	return rc;
++}
++
++/*
++ * Tape device release function used by tape_char & tape_block frontends.
++ */
++int
++tape_release(struct tape_device *device)
++{
++	spin_lock_irq(&tape_discipline_lock);
++	spin_lock(get_irq_lock(device->devinfo.irq));
++
++	if (TAPE_OPEN(device)) {
++		TAPE_CLEAR_STATE(device, TAPE_STATUS_OPEN);
++
++		if (device->discipline->owner)
++			__MOD_DEC_USE_COUNT(device->discipline->owner);	
++	}
++	spin_unlock(get_irq_lock(device->devinfo.irq));
++	spin_unlock_irq(&tape_discipline_lock);
++
++	return 0;
++}
++
++/*
++ * Execute a magnetic tape command a number of times.
++ */
++int
++tape_mtop(struct tape_device *device, int mt_op, int mt_count)
++{
++	tape_mtop_fn fn;
++	int rc;
++
++	DBF_EVENT(6, "TAPE:mtio\n");
++	DBF_EVENT(6, "TAPE:ioop: %x\n", mt_op);
++	DBF_EVENT(6, "TAPE:arg:  %x\n", mt_count);
++
++	if (mt_op < 0 || mt_op >= TAPE_NR_MTOPS)
++		return -EINVAL;
++	fn = device->discipline->mtop_array[mt_op];
++	if(fn == NULL)
++		return -EINVAL;
++
++	/* We assume that the backends can handle count up to 500. */
++	if (mt_op == MTBSR  || mt_op == MTFSR  || mt_op == MTFSF  ||
++	    mt_op == MTBSF  || mt_op == MTFSFM || mt_op == MTBSFM) {
++		rc = 0;
++		for (; mt_count > 500; mt_count -= 500)
++			if ((rc = fn(device, 500)) != 0)
++				break;
++		if (rc == 0)
++			rc = fn(device, mt_count);
++	} else
++		rc = fn(device, mt_count);
++	return rc;
++
++}
++
++void
++tape_init_disciplines(void)
++{
++#ifdef	CONFIG_S390_TAPE_34XX
++	tape_34xx_init();
++#endif
++#ifdef CONFIG_S390_TAPE_34XX_MODULE
++	request_module("tape_34xx");
++#endif
++
++#ifdef CONFIG_S390_TAPE_3590
++	tape_3590_init();
++#else
++	request_module("tape_3590");
++#endif
++	tape_auto_detect();
++}
++
++/*
++ * Tape init function.
++ */
++static int
++tape_init (void)
++{
++	TAPE_DBF_AREA = debug_register ( "tape", 1, 2, 4*sizeof(long));
++	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
++	DBF_EVENT(3, "tape init: ($Revision: 1.7.4.8 $)\n");
++#ifdef CONFIG_DEVFS_FS
++	tape_devfs_root_entry = devfs_mk_dir (NULL, "tape", NULL);
++#endif /* CONFIG_DEVFS_FS */
++	DBF_EVENT(3, "dev detect\n");
++	/* Parse the parameters. */
++	tape_devmap_init();
++#ifdef CONFIG_PROC_FS
++	tape_proc_init();
++#endif /* CONFIG_PROC_FS */
++	tapechar_init();
++#ifdef CONFIG_S390_TAPE_BLOCK
++	tapeblock_init();
++#endif
++	tape_init_disciplines();
++	return 0;
++}
++
++/*
++ * Tape exit function.
++ */
++void
++tape_exit(void)
++{
++	struct list_head *l, *n;
++	struct tape_discipline *discipline;
++
++	DBF_EVENT(6, "tape exit\n");
++
++	/* Cleanup registered disciplines. */
++	spin_lock(&tape_discipline_lock);
++	list_for_each_safe(l, n, &tape_disciplines) {
++		discipline = list_entry(l, struct tape_discipline, list);
++	       __tape_unregister_discipline(discipline);
++	}
++	spin_unlock(&tape_discipline_lock);
++
++	/* Get rid of the frontends */
++	tapechar_exit();
++#ifdef CONFIG_S390_TAPE_BLOCK
++	tapeblock_exit();
++#endif
++#ifdef CONFIG_PROC_FS
++	tape_proc_cleanup();
++#endif
++	tape_devmap_exit();
++#ifdef CONFIG_DEVFS_FS
++	devfs_unregister (tape_devfs_root_entry); /* devfs checks for NULL */
++#endif /* CONFIG_DEVFS_FS */
++	debug_unregister (TAPE_DBF_AREA);
++}
++
++/*
++ * Issue an hotplug event
++ */
++void tape_hotplug_event(struct tape_device *device, int devmaj, int action) {
++#ifdef CONFIG_HOTPLUG
++	char *argv[3];
++	char *envp[8];
++	char  devno[20];
++	char  major[20];
++	char  minor[20];
++
++	sprintf(devno, "DEVNO=%04x", device->devinfo.devno);
++	sprintf(major, "MAJOR=%d",   devmaj);
++	sprintf(minor, "MINOR=%d",   device->first_minor);
++
++	argv[0] = hotplug_path;
++	argv[1] = "tape";
++	argv[2] = NULL;
++
++	envp[0] = "HOME=/";
++	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++
++	switch(action) {
++		case TAPE_HOTPLUG_CHAR_ADD:
++		case TAPE_HOTPLUG_BLOCK_ADD:
++			envp[2] = "ACTION=add";
++			break;
++		case TAPE_HOTPLUG_CHAR_REMOVE:
++		case TAPE_HOTPLUG_BLOCK_REMOVE:
++			envp[2] = "ACTION=remove";
++			break;
++		default:
++			BUG();
++	}
++	switch(action) {
++		case TAPE_HOTPLUG_CHAR_ADD:
++		case TAPE_HOTPLUG_CHAR_REMOVE:
++			envp[3] = "INTERFACE=char";
++			break;
++		case TAPE_HOTPLUG_BLOCK_ADD:
++		case TAPE_HOTPLUG_BLOCK_REMOVE:
++			envp[3] = "INTERFACE=block";
++			break;
++	}
++	envp[4] = devno;
++	envp[5] = major;
++	envp[6] = minor;
++	envp[7] = NULL;
++
++	call_usermodehelper(argv[0], argv, envp);
++#endif
++}
++
++MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and "
++	      "Michael Holzheu (cotte at de.ibm.com,holzheu at de.ibm.com)");
++MODULE_DESCRIPTION("Linux on zSeries channel attached "
++		   "tape device driver ($Revision: 1.7.4.8 $)");
++
++module_init(tape_init);
++module_exit(tape_exit);
++
++EXPORT_SYMBOL(tape_state_string);
++EXPORT_SYMBOL(tape_op_verbose);
++EXPORT_SYMBOL(tape_state_set);
++EXPORT_SYMBOL(tape_med_state_set);
++EXPORT_SYMBOL(tape_register_discipline);
++EXPORT_SYMBOL(tape_unregister_discipline);
++EXPORT_SYMBOL(tape_alloc_request);
++EXPORT_SYMBOL(tape_put_request);
++EXPORT_SYMBOL(tape_clone_request);
++EXPORT_SYMBOL(tape_dump_sense);
++EXPORT_SYMBOL(tape_dump_sense_dbf);
++EXPORT_SYMBOL(tape_do_io);
++EXPORT_SYMBOL(tape_do_io_free);
++EXPORT_SYMBOL(tape_do_io_async);
++EXPORT_SYMBOL(tape_do_io_interruptible);
++EXPORT_SYMBOL(tape_mtop);
++EXPORT_SYMBOL(tape_hotplug_event);
++
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tapedefs.h drivers/s390/char/tapedefs.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/tapedefs.h	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tapedefs.h	2006-02-12 12:47:23.000000000 -0700
+@@ -1,76 +0,0 @@
+-/***********************************************************************
+- *  drivers/s390/char/tapedefs.h
+- *    tape device driver for S/390 and zSeries tapes.
+- *
+- *  S390 and zSeries version
+- *    Copyright (C) 2001 IBM Corporation
+- *    Author(s):  Carsten Otte <cotte at de.ibm.com>
+- *                Tuan Ngo-Anh <ngoanh at de.ibm.com>
+- *
+- *
+- ***********************************************************************
 - */
--ssize_t
--tape_write (struct file *filp, const char *data, size_t count, loff_t * ppos)
--{
--	long lockflags;
--	tape_info_t *ti;
--	size_t block_size;
--	ccw_req_t *cqr;
--	int nblocks, i, rc;
--	size_t written = 0;
--	loff_t pos = *ppos;
--
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:write");
--#endif
--	ti = first_tape_info;
--	while ((ti != NULL) && (ti->nor_filp != filp) && (ti->rew_filp != filp))
--		ti = (tape_info_t *) ti->next;
--	if (ti == NULL)
--		return -ENODEV;
--	if (ppos != &filp->f_pos) {
--		/* "A request was outside the capabilities of the device." */
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,6,"c:ppos wrong");
--#endif
--		return -EOVERFLOW;	/* errno=75 Value too large for def. data type */
--	}
--	if ((ti->block_size != 0) && (count % ti->block_size != 0))
--		return -EIO;
--	if (ti->block_size == 0) {
--		block_size = count;
--		nblocks = 1;
--	} else {
--		block_size = ti->block_size;
--		nblocks = count / (ti->block_size);
--	}
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,6,"c:nbytes:");
--		debug_int_event (tape_debug_area,6,block_size);
--	        debug_text_event (tape_debug_area,6,"c:nblocks:");
--	        debug_int_event (tape_debug_area,6,nblocks);
--#endif
--	for (i = 0; i < nblocks; i++) {
--		cqr = ti->discipline->write_block (data + i * block_size, block_size, ti);
--		if (!cqr) {
--			return -ENOBUFS;
--		}
--		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--		ti->cqr = cqr;
--		ti->wanna_wakeup=0;
--		rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		wait_event_interruptible (ti->wq,ti->wanna_wakeup);
--		ti->cqr = NULL;
--		ti->discipline->free_write_block (cqr, ti);
--		if (signal_pending (current)) {
--			tapestate_set (ti, TS_IDLE);
--			return -ERESTARTSYS;
--		}
--		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--		if (tapestate_get (ti) == TS_FAILED) {
--			tapestate_set (ti, TS_IDLE);
--			s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--                        if ((ti->rc==-ENOSPC) && (i!=0))
--			  return i*block_size;
--			return ti->rc;
--		}
--		if (tapestate_get (ti) == TS_NOT_OPER) {
--		    ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
--		    ti->devinfo.irq=-1;
--		    s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags);
--		    return -ENODEV;
--		}
--		if (tapestate_get (ti) != TS_DONE) {
--			tapestate_set (ti, TS_IDLE);
--			s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--			return -EIO;
--		}
--		tapestate_set (ti, TS_IDLE);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,6,"c:wbytes:"); 
--		debug_int_event (tape_debug_area,6,block_size - ti->devstat.rescnt);
--#endif
--		written += block_size - ti->devstat.rescnt;
--		if (ti->devstat.rescnt > 0) {
--			*ppos = pos + written;
--			return written;
--		}
--	}
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:wtotal:");
--	debug_int_event (tape_debug_area,6,written);
--#endif
--	*ppos = pos + written;
--	return written;
--}
--
--static int
--tape_mtioctop (struct file *filp, short mt_op, int mt_count)
--{
--	tape_info_t *ti;
--	ccw_req_t *cqr = NULL;
--	int rc;
--	long lockflags;
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:mtio");
--	debug_text_event (tape_debug_area,6,"c:ioop:");
--	debug_int_event (tape_debug_area,6,mt_op); 
--	debug_text_event (tape_debug_area,6,"c:arg:");
--	debug_int_event (tape_debug_area,6,mt_count);
--#endif
--	ti = first_tape_info;
--	while ((ti != NULL) && (ti->rew_filp != filp) && (ti->nor_filp != filp))
--		ti = (tape_info_t *) ti->next;
--	if (ti == NULL)
--		return -ENODEV;
--	switch (mt_op) {
--	case MTREW:		// rewind
--
--		cqr = ti->discipline->mtrew (ti, mt_count);
--		break;
--	case MTOFFL:		// put drive offline
--
--		cqr = ti->discipline->mtoffl (ti, mt_count);
--		break;
--	case MTUNLOAD:		// unload the tape
--
--		cqr = ti->discipline->mtunload (ti, mt_count);
--		break;
--	case MTWEOF:		// write tapemark
--
--		cqr = ti->discipline->mtweof (ti, mt_count);
--		break;
--	case MTFSF:		// forward space file
--
--		cqr = ti->discipline->mtfsf (ti, mt_count);
--		break;
--	case MTBSF:		// backward space file
--
--		cqr = ti->discipline->mtbsf (ti, mt_count);
--		break;
--	case MTFSFM:		// forward space file, stop at BOT side
--
--		cqr = ti->discipline->mtfsfm (ti, mt_count);
--		break;
--	case MTBSFM:		// backward space file, stop at BOT side
--
--		cqr = ti->discipline->mtbsfm (ti, mt_count);
--		break;
--	case MTFSR:		// forward space file
--
--		cqr = ti->discipline->mtfsr (ti, mt_count);
--		break;
--	case MTBSR:		// backward space file
--
--		cqr = ti->discipline->mtbsr (ti, mt_count);
--		break;
--	case MTNOP:
--		cqr = ti->discipline->mtnop (ti, mt_count);
--		break;
--	case MTEOM:		// postion at the end of portion
--
--	case MTRETEN:		// retension the tape
--
--		cqr = ti->discipline->mteom (ti, mt_count);
--		break;
--	case MTERASE:
--		cqr = ti->discipline->mterase (ti, mt_count);
--		break;
--	case MTSETDENSITY:
--		cqr = ti->discipline->mtsetdensity (ti, mt_count);
--		break;
--	case MTSEEK:
--		cqr = ti->discipline->mtseek (ti, mt_count);
--		break;
--	case MTSETDRVBUFFER:
--		cqr = ti->discipline->mtsetdrvbuffer (ti, mt_count);
--		break;
--	case MTLOCK:
--		cqr = ti->discipline->mtsetdrvbuffer (ti, mt_count);
--		break;
--	case MTUNLOCK:
--		cqr = ti->discipline->mtsetdrvbuffer (ti, mt_count);
--		break;
--	case MTLOAD:
--		cqr = ti->discipline->mtload (ti, mt_count);
--		if (cqr!=NULL) break; // if backend driver has an load function ->use it
--		// if no medium is in, wait until it gets inserted
--		if (ti->medium_is_unloaded) {
--		    wait_event_interruptible (ti->wq,ti->medium_is_unloaded==0);
--		}
--		return 0;
--	case MTCOMPRESSION:
--		cqr = ti->discipline->mtcompression (ti, mt_count);
--		break;
--	case MTSETPART:
--		cqr = ti->discipline->mtsetpart (ti, mt_count);
--		break;
--	case MTMKPART:
--		cqr = ti->discipline->mtmkpart (ti, mt_count);
--		break;
--	case MTTELL:		// return number of block relative to current file
--
--		cqr = ti->discipline->mttell (ti, mt_count);
--		break;
--	case MTSETBLK:
--		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--		ti->block_size = mt_count;
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--		debug_text_event (tape_debug_area,6,"c:setblk:");
--	        debug_int_event (tape_debug_area,6,mt_count);
--#endif
--		return 0;
--	case MTRESET:
--		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--		ti->kernbuf = ti->userbuf = NULL;
--		tapestate_set (ti, TS_IDLE);
--		ti->block_size = 0;
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--		debug_text_event (tape_debug_area,6,"c:devreset:");
--		debug_int_event (tape_debug_area,6,ti->blk_minor);
--#endif
--		return 0;
--	default:
--#ifdef TAPE_DEBUG
--	    debug_text_event (tape_debug_area,6,"c:inv.mtio");
--#endif
--		return -EINVAL;
--	}
--	if (cqr == NULL) {
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,6,"c:ccwg fail");
--#endif
--		return -ENOSPC;
--	}
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	ti->cqr = cqr;
--	ti->wanna_wakeup=0;
--	rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	wait_event_interruptible (ti->wq,ti->wanna_wakeup);
--	ti->cqr = NULL;
--	if (ti->kernbuf != NULL) {
--		kfree (ti->kernbuf);
--		ti->kernbuf = NULL;
--	}
--	tape_free_request (cqr);
--	// if medium was unloaded, update the corresponding variable.
--	switch (mt_op) {
--	case MTOFFL:
--	case MTUNLOAD:
--	    ti->medium_is_unloaded=1;
--	}
--	if (signal_pending (current)) {
--		tapestate_set (ti, TS_IDLE);
--		return -ERESTARTSYS;
--	}
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	if (((mt_op == MTEOM) || (mt_op == MTRETEN)) && (tapestate_get (ti) == TS_FAILED))
--		tapestate_set (ti, TS_DONE);
--	if (tapestate_get (ti) == TS_FAILED) {
--		tapestate_set (ti, TS_IDLE);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		return ti->rc;
--	}
--	if (tapestate_get (ti) == TS_NOT_OPER) {
--	    ti->blk_minor=ti->rew_minor=ti->nor_minor=-1;
--	    ti->devinfo.irq=-1;
--	    s390irq_spin_unlock_irqrestore (ti->devinfo.irq,lockflags);
--	    return -ENODEV;
--	}
--	if (tapestate_get (ti) != TS_DONE) {
--		tapestate_set (ti, TS_IDLE);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		return -EIO;
--	}
--	tapestate_set (ti, TS_IDLE);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--	switch (mt_op) {
--	case MTRETEN:		//need to rewind the tape after moving to eom
--
--		return tape_mtioctop (filp, MTREW, 1);
--	case MTFSFM:		//need to skip back over the filemark
 -
--		return tape_mtioctop (filp, MTBSFM, 1);
--	case MTBSF:		//need to skip forward over the filemark
+-/* Kernel Version Compatibility section */
+-#include <linux/version.h>
+-#include <linux/blkdev.h>
+-#include <linux/blk.h>
+-#include <asm/irq.h>
 -
--		return tape_mtioctop (filp, MTFSF, 1);
--	}
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:mtio done");
--#endif
--	return 0;
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,17))
+-#define TAPE_DEBUG               // use s390 debug feature
+-#else
+-#undef TAPE_DEBUG                // debug feature not supported by our 2.2.16 code
+-static inline void set_normalized_cda ( ccw1_t * cp, unsigned long address ) {
+-    cp -> cda = address;
 -}
--
--/*
-- * Tape device io controls.
-- */
--int
--tape_ioctl (struct inode *inode, struct file *filp,
--	    unsigned int cmd, unsigned long arg)
--{
--	long lockflags;
--	tape_info_t *ti;
--	ccw_req_t *cqr;
--	struct mtop op;		/* structure for MTIOCTOP */
--	struct mtpos pos;	/* structure for MTIOCPOS */
--	struct mtget get;
--
--	int rc;
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:ioct");
--#endif
--	ti = first_tape_info;
--	while ((ti != NULL) &&
--	       (ti->rew_minor != MINOR (inode->i_rdev)) &&
--	       (ti->nor_minor != MINOR (inode->i_rdev)))
--		ti = (tape_info_t *) ti->next;
--	if (ti == NULL) {
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,6,"c:nodev");
--#endif
--		return -ENODEV;
--	}
--	// check for discipline ioctl overloading
--	if ((rc = ti->discipline->discipline_ioctl_overload (inode, filp, cmd, arg))
--	    != -EINVAL) {
--#ifdef TAPE_DEBUG
--	    debug_text_event (tape_debug_area,6,"c:ioverloa");
--#endif
--	    return rc;
--	}
--
--	switch (cmd) {
--	case MTIOCTOP:		/* tape op command */
--		if (copy_from_user (&op, (char *) arg, sizeof (struct mtop))) {
--			 return -EFAULT;
--		}
--		return (tape_mtioctop (filp, op.mt_op, op.mt_count));
--	case MTIOCPOS:		/* query tape position */
--		cqr = ti->discipline->mttell (ti, 0);
--		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--		ti->cqr = cqr;
--		ti->wanna_wakeup=0;
--		do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		wait_event_interruptible (ti->wq,ti->wanna_wakeup);
--		pos.mt_blkno = ti->rc;
--		ti->cqr = NULL;
--		if (ti->kernbuf != NULL) {
--			kfree (ti->kernbuf);
--			ti->kernbuf = NULL;
--		}
--		tape_free_request (cqr);
--		if (signal_pending (current)) {
--			tapestate_set (ti, TS_IDLE);
--			return -ERESTARTSYS;
--		}
--		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--		tapestate_set (ti, TS_IDLE);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		if (copy_to_user ((char *) arg, &pos, sizeof (struct mtpos)))
--			 return -EFAULT;
--		return 0;
--	case MTIOCGET:
--		get.mt_erreg = ti->rc;
--		cqr = ti->discipline->mttell (ti, 0);
--		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--		ti->cqr = cqr;
--		ti->wanna_wakeup=0;
--		do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		wait_event_interruptible (ti->wq,ti->wanna_wakeup);
--		get.mt_blkno = ti->rc;
--		get.mt_fileno = 0;
--		get.mt_type = MT_ISUNKNOWN;
--		get.mt_resid = ti->devstat.rescnt;
--		get.mt_dsreg = ti->devstat.ii.sense.data[3];
--		get.mt_gstat = 0;
--		if (ti->devstat.ii.sense.data[1] & 0x08)
--			get.mt_gstat &= GMT_BOT (1);	// BOT
--
--		if (ti->devstat.ii.sense.data[1] & 0x02)
--			get.mt_gstat &= GMT_WR_PROT (1);	// write protected
--
--		if (ti->devstat.ii.sense.data[1] & 0x40)
--			get.mt_gstat &= GMT_ONLINE (1);		//drive online
--
--		ti->cqr = NULL;
--		if (ti->kernbuf != NULL) {
--			kfree (ti->kernbuf);
--			ti->kernbuf = NULL;
--		}
--		tape_free_request (cqr);
--		if (signal_pending (current)) {
--			tapestate_set (ti, TS_IDLE);
--			return -ERESTARTSYS;
--		}
--		s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--		tapestate_set (ti, TS_IDLE);
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		if (copy_to_user ((char *) arg, &get, sizeof (struct mtget)))
--			 return -EFAULT;
--		return 0;
--	default:
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,3,"c:ioct inv");
--#endif	    
--		return -EINVAL;
--	}
+-static inline void clear_normalized_cda ( ccw1_t * ccw ) {
+-    ccw -> cda = 0;
 -}
+-#define BUG() PRINT_FATAL("tape390: CRITICAL INTERNAL ERROR OCCURED. REPORT THIS BACK TO LINUX390 at DE.IBM.COM\n")
+-#endif
+-#define CONFIG_S390_TAPE_DYNAMIC // allow devices to be attached or detached on the fly
+-#define TAPEBLOCK_RETRIES 20     // number of retries, when a block-dev request fails.
 -
--/*
-- * Tape device open function.
-- */
--int
--tape_open (struct inode *inode, struct file *filp)
--{
--	tape_info_t *ti;
--	kdev_t dev;
--	long lockflags;
--
--	inode = filp->f_dentry->d_inode;
--	ti = first_tape_info;
--	while ((ti != NULL) &&
--	       (ti->rew_minor != MINOR (inode->i_rdev)) &&
--	       (ti->nor_minor != MINOR (inode->i_rdev)))
--		ti = (tape_info_t *) ti->next;
--	if (ti == NULL)
--		return -ENODEV;
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:open:");
--	debug_int_event (tape_debug_area,6,ti->blk_minor);
--#endif
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	if (tapestate_get (ti) != TS_UNUSED) {
--		s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--#ifdef TAPE_DEBUG
--		debug_text_event (tape_debug_area,6,"c:dbusy");
--#endif
--		return -EBUSY;
--	}
--	tapestate_set (ti, TS_IDLE);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--
--	dev = MKDEV (tape_major, MINOR (inode->i_rdev));	/* Get the device */
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	if (ti->rew_minor == MINOR (inode->i_rdev))
--		ti->rew_filp = filp;	/* save for later reference     */
--	else
--		ti->nor_filp = filp;
--	filp->private_data = ti;	/* save the dev.info for later reference */
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
 -
--#ifdef MODULE
--	MOD_INC_USE_COUNT;
--#endif				/* MODULE */
--	return 0;
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
+-#define INIT_BLK_DEV(d_major,d_request_fn,d_queue_fn,d_current) \
+-do { \
+-        blk_dev[d_major].queue = d_queue_fn; \
+-} while(0)
+-static inline struct request * 
+-tape_next_request( request_queue_t *queue ) 
+-{
+-        return blkdev_entry_next_request(&queue->queue_head);
 -}
--
--/*
-- * Tape device release function.
-- */
--int
--tape_release (struct inode *inode, struct file *filp)
+-static inline void 
+-tape_dequeue_request( request_queue_t * q, struct request *req )
 -{
--	long lockflags;
--	tape_info_t *ti,*lastti;
--	ccw_req_t *cqr = NULL;
--	int rc = 0;
--
--	ti = first_tape_info;
--	while ((ti != NULL) && (ti->rew_minor != MINOR (inode->i_rdev)) && (ti->nor_minor != MINOR (inode->i_rdev)))
--		ti = (tape_info_t *) ti->next;
--	if ((ti != NULL) && (tapestate_get (ti) == TS_NOT_OPER)) {
--	    if (ti==first_tape_info) {
--		first_tape_info=ti->next;
--	    } else {
--		lastti=first_tape_info;
--		while (lastti->next!=ti) lastti=lastti->next;
--		lastti->next=ti->next;
--	    }
--	    kfree(ti);    
--	    goto out;
--	}
--	if ((ti == NULL) || (tapestate_get (ti) != TS_IDLE)) {
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:notidle!");
--#endif
--		rc = -ENXIO;	/* error in tape_release */
--		goto out;
--	}
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:release:");
--	debug_int_event (tape_debug_area,6,ti->blk_minor);
--#endif
--	if (ti->rew_minor == MINOR (inode->i_rdev)) {
--		cqr = ti->discipline->mtrew (ti, 1);
--		if (cqr != NULL) {
--#ifdef TAPE_DEBUG
--		        debug_text_event (tape_debug_area,6,"c:rewrelea");
+-        blkdev_dequeue_request (req);
+-}
+-#else 
+-#define s390_dev_info_t dev_info_t
+-typedef struct request *request_queue_t;
+-#ifndef init_waitqueue_head
+-#define init_waitqueue_head(x) do { *x = NULL; } while(0)
 -#endif
--			s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--			tapestate_set (ti, TS_REW_RELEASE_INIT);
--			ti->cqr = cqr;
--			ti->wanna_wakeup=0;
--			rc = do_IO (ti->devinfo.irq, cqr->cpaddr, (unsigned long) cqr, 0x00, cqr->options);
--			s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--			wait_event (ti->wq,ti->wanna_wakeup);
--			ti->cqr = NULL;
--			tape_free_request (cqr);
--		}
--	}
--	s390irq_spin_lock_irqsave (ti->devinfo.irq, lockflags);
--	tapestate_set (ti, TS_UNUSED);
--	s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--out:
--#ifdef MODULE
--	MOD_DEC_USE_COUNT;
--#endif				/* MODULE */
--	return rc;
+-#define blk_init_queue(x,y) do {} while(0)
+-#define blk_queue_headactive(x,y) do {} while(0)
+-#define INIT_BLK_DEV(d_major,d_request_fn,d_queue_fn,d_current) \
+-do { \
+-        blk_dev[d_major].request_fn = d_request_fn; \
+-        blk_dev[d_major].queue = d_queue_fn; \
+-        blk_dev[d_major].current_request = d_current; \
+-} while(0)
+-static inline struct request *
+-tape_next_request( request_queue_t *queue ) 
+-{
+-    return *queue;
 -}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapechar.h drivers/s390/char/tapechar.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapechar.h	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tapechar.h	2006-01-30 22:25:26.000000000 -0700
-@@ -1,34 +0,0 @@
--
+-static inline void 
+-tape_dequeue_request( request_queue_t * q, struct request *req )
+-{
+-        *q = req->next;
+-        req->next = NULL;
+-}
+-#endif 
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape_devmap.c drivers/s390/char/tape_devmap.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape_devmap.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/tape_devmap.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,949 @@
++/*
++ *  drivers/s390/char/tape_devmap.c
++ *    device mapping for tape device driver
++ *
++ *  S390 and zSeries version
++ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Carsten Otte <cotte at de.ibm.com>
++ *               Michael Holzheu <holzheu at de.ibm.com>
++ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
++ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
++ *		 Stefan Bader <shbader at de.ibm.com>
++ *
++ * Device mapping and tape= parameter parsing functions. All devmap
++ * functions may not be called from interrupt context. In particular
++ * tape_get_device is a no-no from interrupt context.
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/ctype.h>
++#include <linux/init.h>
++
++#include <asm/debug.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++
++/* This is ugly... */
++#define PRINTK_HEADER "tape_devmap:"
++
++#define TAPE_DBF_AREA tape_core_dbf
++#include "tape.h"
++
++struct tape_devmap {
++	struct list_head list;
++	int devindex;
++	unsigned short devno;
++	devreg_t devreg;
++	struct tape_device *device;
++};
++
++struct tape_discmap {
++	struct list_head list;
++	devreg_t devreg;
++	struct tape_discipline *discipline;
++};
++
++/*
++ * List of all registered tapes and disciplines.
++ */
++static struct list_head tape_devreg_list = LIST_HEAD_INIT(tape_devreg_list);
++static struct list_head tape_disc_devreg_list = LIST_HEAD_INIT(tape_disc_devreg_list);
++int tape_max_devindex = 0;
++
++/*
++ * Single spinlock to protect devmap structures and lists.
++ */
++static spinlock_t tape_devmap_lock = SPIN_LOCK_UNLOCKED;
++
++/*
++ * Module/Kernel Parameter Handling. The syntax of tape= is:
++ *   <devno>		: (0x)?[0-9a-fA-F]+
++ *   <range>		: <devno>(-<devno>)?
++ *   <tape>		: <range>(,<range>)*
++ */
++int tape_autodetect = 0;	/* is true, when autodetection is active */
++
++/*
++ * char *tape[] is intended to hold the ranges supplied by the tape= statement
++ * it is named 'tape' to directly be filled by insmod with the comma separated
++ * strings when running as a module.
++ */
++static char *tape[256];
++MODULE_PARM (tape, "1-" __MODULE_STRING (256) "s");
++
++#ifndef MODULE
++/*
++ * The parameter parsing functions for builtin-drivers are called
++ * before kmalloc works. Store the pointers to the parameters strings
++ * into tape[] for later processing.
++ */
++static int __init
++tape_call_setup (char *str)
++{
++	static int count = 0;
++
++	if (count < 256)
++		tape[count++] = str;
++	return 1;
++}
++
++__setup("tape=", tape_call_setup);
++#endif	 /* not defined MODULE */
++
++/*
++ * Add a range of devices and create the corresponding devreg_t
++ * structures. The order of the ranges added by this function
++ * will define the kdevs for the individual devices.
++ */
++int
++tape_add_range(int from, int to)
++{
++	struct tape_devmap *devmap, *tmp;
++	struct list_head *l;
++	int devno;
++	int rc;
++
++	if (from > to) {
++		PRINT_ERR("Invalid device range %04x-%04x", from, to);
++		return -EINVAL;
++	}
++
++	rc = 0;
++	spin_lock(&tape_devmap_lock);
++	for (devno = from; devno <= to; devno++) {
++		devmap = NULL;
++		list_for_each(l, &tape_devreg_list) {
++			tmp = list_entry(l, struct tape_devmap, list);
++			if (tmp->devno == devno) {
++				devmap = tmp;
++				break;
++			}
++		}
++		if (devmap == NULL) {
++			if(tape_max_devindex >= 256/TAPE_MINORS_PER_DEV) {
++				PRINT_ERR(" No more device slots available."
++					" Range %04x-%04x ignored\n",
++					devno, to);
++				rc = -E2BIG;
++				break;
++			}
++			/* This devno is new. */
++			devmap = (struct tape_devmap *)
++				kmalloc(sizeof(struct tape_devmap),
++					GFP_KERNEL);
++			if (devmap == NULL) {
++				rc = -ENOMEM;
++				break;
++			}
++			memset(devmap, 0, sizeof(struct tape_devmap));
++			devmap->devno = devno;
++			devmap->devindex = tape_max_devindex++;
++			list_add(&devmap->list, &tape_devreg_list);
++			devmap->devreg.ci.devno = devno;
++			devmap->devreg.flag = DEVREG_TYPE_DEVNO;
++			devmap->devreg.oper_func = tape_oper_handler;
++			s390_device_register(&devmap->devreg);
++		}
++	}
++	spin_unlock(&tape_devmap_lock);
++
++	return rc;
++}
++
++/*
++ * Read device number from string. The number is always is hex,
++ * a leading 0x is accepted (and has to be removed for simple_stroul
++ * to work).
++ */
++static inline int
++tape_devno(char *str, char **endp)
++{
++	int devno;
++
++	/* remove leading '0x' */
++	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
++		str += 2;
++
++	if (!isxdigit(*str))
++		return -EINVAL;
++
++	devno = simple_strtoul(str, endp, 16); /* interpret anything as hex */
++
++	if(devno < 0 || devno > 0xffff) {
++		PRINT_ERR(" Invalid devno(0x%04x) specified\n", devno);
++		return -EINVAL;
++	}
++
++	return devno;
++}
++
++/*
++ * Parse Kernel/Module Parameters and create devregs for dynamic attach/detach
++ */
++static int
++tape_parm_parse (char *str)
++{
++	int from, to, rc;
++
++	while (1) {
++		to = from = tape_devno(str, &str);
++		if (*str == '-') {
++			str++;
++			to = tape_devno(str, &str);
++		}
++		/* Negative numbers in from/to indicate errors. */
++		if (from >= 0 && to >= 0) {
++			rc = tape_add_range(from, to);
++			if (rc)
++				return rc;
++		}
++		if (*str != ',')
++			break;
++		str++;
++	}
++	if (*str != '\0') {
++		PRINT_WARN(" Junk at end of tape parameter string: %s\n", str);
++		return -EINVAL;
++	}
++	return 0;
++}
++
++/*
++ * Parse parameters stored in tape[].
++ */
++static int
++tape_parse(void)
++{
++	int rc, i;
++
++	if (*tape == NULL) {
++		/* No parameters present */
++		PRINT_INFO ("No parameters supplied, enabling auto detect "
++			    "mode for all supported devices.\n");
++		tape_autodetect = 1;
++		return 0;
++	}
++	PRINT_INFO("Using ranges supplied in parameters, "
++		   "disabling auto detect mode.\n");
++	rc = 0;
++	for (i = 0; i < 256; i++) {
++		if (tape[i] == NULL)
++			break;
++		rc = tape_parm_parse(tape[i]);
++		if (rc) {
++			PRINT_ERR(" Error while parsing parameters. "
++				"Setup may be incomplete.\n");
++			break;
++		}
++	}
++	return rc;
++}
++
++/*
++ * Create a devreg for a discipline. This is only done if no explicit
++ * tape range is given. The tape_oper_handler will call tape_add_range
++ * for each device that appears.
++ */
++static int
++tape_add_disc_devreg(struct tape_discipline *discipline)
++{
++	struct tape_discmap *discmap;
++
++	discmap = (struct tape_discmap *) kmalloc(sizeof(struct tape_discmap),
++						  GFP_KERNEL);
++	if (discmap == NULL) {
++		PRINT_WARN("Could not alloc devreg: Out of memory\n"
++			   "Dynamic attach/detach will not work!\n");
++		return -ENOMEM;
++	}
++	spin_lock(&tape_devmap_lock);
++	discmap->devreg.ci.hc.ctype = discipline->cu_type;
++	discmap->devreg.flag = DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS;
++	discmap->devreg.oper_func = tape_oper_handler;
++	s390_device_register(&discmap->devreg);
++	list_add(&discmap->list, &tape_disc_devreg_list);
++	spin_unlock(&tape_devmap_lock);
++	return 0;
++}
++
++/*
++ * Free devregs for a discipline.
++ */
++static void
++tape_del_disc_devreg(struct tape_discipline *discipline)
++{
++	struct list_head *l;
++	struct tape_discmap *discmap;
++
++	spin_lock(&tape_devmap_lock);
++	list_for_each(l, &tape_disc_devreg_list) {
++		discmap = list_entry(l, struct tape_discmap, list);
++		if (discmap->discipline == discipline) {
++			s390_device_unregister(&discmap->devreg);
++			list_del(&discmap->list);
++			kfree(discmap);
++			break;
++		}
++	}
++	spin_unlock(&tape_devmap_lock);
++}
++
++
++/*
++ * Forget all about device numbers and disciplines.
++ * This may only be called at module unload or system shutdown.
++ */
++static void
++tape_forget_devregs(void)
++{
++	struct list_head *l, *n;
++	struct tape_devmap *devmap;
++	struct tape_discmap *discmap;
++
++	spin_lock(&tape_devmap_lock);
++        list_for_each_safe(l, n, &tape_devreg_list) {
++		devmap = list_entry(l, struct tape_devmap, list);
++		if (devmap->device != NULL)
++			BUG();
++		s390_device_unregister(&devmap->devreg);
++		list_del(&devmap->list);
++		kfree(devmap);
++	}
++	list_for_each_safe(l, n, &tape_disc_devreg_list) {
++		discmap = list_entry(l, struct tape_discmap, list);
++		s390_device_unregister(&discmap->devreg);
++		list_del(&discmap->list);
++		kfree(discmap);
++	}
++	spin_unlock(&tape_devmap_lock);
++}
++
++/*
++ * Allocate memory for a new device structure.
++ */
++static struct tape_device *
++tape_alloc_device(void)
++{
++	struct tape_device *device;
++
++	device = (struct tape_device *)
++		kmalloc(sizeof(struct tape_device), GFP_KERNEL);
++	if (device == NULL) {
++		DBF_EXCEPTION(2, "ti:no mem\n");
++		PRINT_INFO ("can't allocate memory for "
++			    "tape info structure\n");
++		return ERR_PTR(-ENOMEM);
++	}
++	memset(device, 0, sizeof(struct tape_device));
++	device->modeset_byte = (char *) kmalloc(1, GFP_KERNEL | GFP_DMA);
++	if (device->modeset_byte == NULL) {
++		DBF_EXCEPTION(2, "ti:no mem\n");
++		PRINT_INFO("can't allocate memory for modeset byte\n");
++		kfree(device);
++		return ERR_PTR(-ENOMEM);
++	}
++	INIT_LIST_HEAD(&device->req_queue);
++	init_waitqueue_head(&device->state_change_wq);
++	spin_lock_init(&device->assign_lock);
++	atomic_set(&device->ref_count, 1);
++	TAPE_SET_STATE(device, TAPE_STATUS_INIT);
++	device->medium_state  = MS_UNKNOWN;
++	*device->modeset_byte = 0;
++
++	return device;
++}
++
++/*
++ * Create a device structure. 
++ */
++static struct tape_device *
++tape_create_device(int devno)
++{
++	struct list_head *l;
++	struct tape_devmap *devmap, *tmp;
++	struct tape_device *device;
++	int rc;
++
++	DBF_EVENT(4, "tape_create_device(0x%04x)\n", devno);
++
++	device = tape_alloc_device();
++	if (IS_ERR(device))
++		return device;
++	/* Get devinfo from the common io layer. */
++	rc = get_dev_info_by_devno(devno, &device->devinfo);
++	if (rc) {
++		DBF_EVENT(3, "get_dev_info_by_devno returned %d\n", rc);
++		if (rc == -EUSERS) {
++			device->devinfo.status |= DEVSTAT_UNFRIENDLY_DEV;
++		} else {
++			tape_put_device(device);
++			return ERR_PTR(rc);
++		}
++	}
++
++	spin_lock(&tape_devmap_lock);
++	devmap = NULL;
++	list_for_each(l, &tape_devreg_list) {
++		tmp = list_entry(l, struct tape_devmap, list);
++		if (tmp->devno == devno) {
++			devmap = tmp;
++			break;
++		}
++	}
++	if (devmap != NULL && devmap->device == NULL) {
++		devmap->device = tape_clone_device(device);
++		device->first_minor = devmap->devindex * TAPE_MINORS_PER_DEV;
++	} else if (devmap == NULL) {
++		/* devno not in tape range. */
++		DBF_EVENT(4, "No devmap for entry 0x%04x\n", devno);
++		tape_put_device(device);
++		device = ERR_PTR(-ENODEV);
++	} else {
++		/* Should not happen. */
++		DBF_EVENT(4, "A devmap entry for 0x%04x already exists\n",
++			devno);
++		tape_put_device(device);
++		device = ERR_PTR(-EEXIST);
++	}
++	spin_unlock(&tape_devmap_lock);
++
++	return device;
++}
++
++struct tape_device *
++tape_clone_device(struct tape_device *device)
++{
++	DBF_EVENT(4, "tape_clone_device(%p) = %i\n", device,
++		atomic_inc_return(&device->ref_count));
++	return device;
++}
++
++/*
++ * Find tape device by a device index.
++ */
++struct tape_device *
++tape_get_device(int devindex)
++{
++	struct list_head *l;
++	struct tape_devmap *devmap;
++	struct tape_device *device;
++
++	DBF_EVENT(5, "tape_get_device(%i)\n", devindex);
++
++	device = ERR_PTR(-ENODEV);
++	spin_lock(&tape_devmap_lock);
++	/* Find devmap for device with device number devno. */
++	list_for_each(l, &tape_devreg_list) {
++		devmap = list_entry(l, struct tape_devmap, list);
++		if (devmap->devindex == devindex) {
++			if (devmap->device != NULL) {
++				device = tape_clone_device(devmap->device);
++			}
++			break;
++		}
++	}
++	spin_unlock(&tape_devmap_lock);
++	return device;
++}
++
++/*
++ * Find tape handle by a devno.
++ */
++struct tape_device *
++tape_get_device_by_devno(int devno)
++{
++	struct list_head	*l;
++	struct tape_devmap	*devmap;
++	struct tape_device	*device;
++
++	DBF_EVENT(5, "tape_get_device_by_devno(0x%04x)\n", devno);
++
++	device = ERR_PTR(-ENODEV);
++	spin_lock(&tape_devmap_lock);
++
++	list_for_each(l, &tape_devreg_list) {
++		devmap = list_entry(l, struct tape_devmap, list);
++		if(devmap->device != NULL && devmap->devno == devno) {
++			device = tape_clone_device(devmap->device);
++			break;
++		}
++	}
++	spin_unlock(&tape_devmap_lock);
++
++	return device;
++}
++
++/*
++ * Find tape handle by a device irq.
++ */
++struct tape_device *
++tape_get_device_by_irq(int irq)
++{
++	struct list_head	*l;
++	struct tape_devmap	*devmap;
++	struct tape_device	*device;
++
++	DBF_EVENT(5, "tape_get_device_by_irq(0x%02x)\n", irq);
++
++	device = ERR_PTR(-ENODEV);
++	spin_lock(&tape_devmap_lock);
++	/* Find devmap for device with device number devno. */
++	list_for_each(l, &tape_devreg_list) {
++		devmap = list_entry(l, struct tape_devmap, list);
++		if (devmap->device != NULL && 
++		    devmap->device->devinfo.irq == irq) {
++			device = tape_clone_device(devmap->device);
++			break;
++		}
++	}
++	spin_unlock(&tape_devmap_lock);
++	return device;
++}
++
++/*
++ * Decrease the reference counter of a devices structure. If the
++ * reference counter reaches zero free the device structure and
++ * wake up sleepers.
++ */
++void
++tape_put_device(struct tape_device *device)
++{
++	int remain;
++
++	DBF_EVENT(4, "tape_put_device(%p)\n", device);
++
++	if ((remain = atomic_dec_return(&device->ref_count)) > 0) {
++		DBF_EVENT(5, "remaining = %i\n", remain);
++		return;
++	}
++
++	/*
++	 * Reference counter dropped to zero. This means
++	 * that the device is deleted and the last user
++	 * of the device structure is gone. That is what
++	 * tape_delete_device is waiting for. Do a wake up.
++	 */
++	if(remain < 0) {
++		PRINT_ERR("put device without reference\n");
++		return;
++	}
++
++	/*
++	 * Free memory of a device structure.
++	 */
++	kfree(device->modeset_byte);
++	kfree(device);
++}
++
++void
++tape_devmap_remove_device(struct tape_device *device) {
++	struct tape_devmap *	devmap;
++	struct list_head *	l;
++	unsigned long		flags;
++
++	spin_lock_irqsave(&tape_devmap_lock, flags);
++	list_for_each(l, &tape_devreg_list) {
++		devmap = list_entry(l, struct tape_devmap, list);
++
++		if(device->devinfo.devno == devmap->devno) {
++			devmap->device = NULL;
++			tape_put_device(device);
++			break;
++		}
++	}
++	spin_unlock_irqrestore(&tape_devmap_lock, flags);
++}
++
++/*
++ * Scan the device range for devices with matching cu_type, create
++ * their device structures and enable them.
++ */
++void
++tape_add_devices(struct tape_discipline *discipline)
++{
++	struct list_head *l;
++	struct tape_devmap *devmap;
++	struct tape_device *device;
++
++	/*
++	 * Scan tape devices for matching cu type.
++	 */
++	list_for_each(l, &tape_devreg_list) {
++		devmap = list_entry(l, struct tape_devmap, list);
++		device = tape_create_device(devmap->devno);
++		if (IS_ERR(device))
++			continue;
++
++		if (device->devinfo.sid_data.cu_type == discipline->cu_type) {
++			DBF_EVENT(4, "tape_add_devices(%p)\n", discipline);
++			DBF_EVENT(4, "det irq:  %x\n", device->devinfo.irq);
++			DBF_EVENT(4, "cu     :  %x\n", discipline->cu_type);
++
++			if(tape_enable_device(device, discipline) < 0) {
++				devmap->device = NULL;
++				tape_put_device(device);
++			}
++		} else {
++			devmap->device = NULL;
++			tape_put_device(device);
++		}
++		tape_put_device(device);
++	}
++	if (tape_autodetect)
++		tape_add_disc_devreg(discipline);
++}
++
++/*
++ * Scan the device range for devices with matching cu_type, disable them
++ * and remove their device structures.
++ */
++void
++tape_remove_devices(struct tape_discipline *discipline)
++{
++	struct list_head *l;
++	struct tape_devmap *devmap;
++	struct tape_device *device;
++
++	if (tape_autodetect)
++		tape_del_disc_devreg(discipline);
++	/*
++	 * Go through our tape info list and disable, deq and free
++	 * all devices with matching discipline
++	 */
++	list_for_each(l, &tape_devreg_list) {
++		devmap = list_entry(l, struct tape_devmap, list);
++		device = devmap->device;
++		if (device == NULL)
++			continue;
++		if (device->discipline == discipline) {
++			tape_disable_device(device, 0);
++			tape_put_device(device);
++			devmap->device = NULL;
++		}
++	}
++}
++
++/*
++ * Auto detect tape devices.
++ */
++void
++tape_auto_detect(void)
++{
++	struct tape_device *device;
++	struct tape_discipline *discipline;
++	s390_dev_info_t dinfo;
++	int irq, devno;
++
++	if (!tape_autodetect)
++		return;
++	for (irq = get_irq_first(); irq != -ENODEV; irq = get_irq_next(irq)) {
++		/* Get device info block. */
++		devno = get_devno_by_irq(irq);
++		if (get_dev_info_by_irq(irq, &dinfo) < 0)
++			continue;
++		/* Search discipline with matching cu_type */
++		discipline = tape_get_discipline(dinfo.sid_data.cu_type);
++		if (discipline == NULL)
++			continue;
++		DBF_EVENT(4, "tape_auto_detect()\n");
++		DBF_EVENT(4, "det irq:  %x\n", irq);
++		DBF_EVENT(4, "cu     :  %x\n", dinfo.sid_data.cu_type);
++		if (tape_add_range(dinfo.devno, dinfo.devno) == 0) {
++			device = tape_create_device(devno);
++			if (!IS_ERR(device)) {
++				if(tape_enable_device(device, discipline) < 0)
++					tape_devmap_remove_device(device);
++				tape_put_device(device);
++			}
++		}
++		tape_put_discipline(discipline);
++	}
++}
++
++/*
++ * Private task queue for oper/noper handling...
++ */
++static DECLARE_TASK_QUEUE(tape_cio_tasks);
++
++/*
++ * Oper Handler is called from Ingo's I/O layer when a new tape device is
++ * attached.
++ */
++static void
++do_tape_oper_handler(void *data)
++{
++	struct {
++		int			devno;
++		int			cu_type;
++		struct tq_struct	task;
++	} *p;
++	struct tape_device       *device;
++	struct tape_discipline   *discipline;
++	unsigned long		  flags;
++
++	p = (void *) data;
++
++	/*
++	 * Handling the path revalidation scheme or common IO. Devices that
++	 * were detected before will be reactivated.
++	 */
++	if(!IS_ERR(device = tape_get_device_by_devno(p->devno))) {
++		spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
++		if (!TAPE_NOACCESS(device)) {
++			PRINT_ERR(
++				"Oper handler for irq %d called, "
++				"which is (still) internally used.\n",
++				device->devinfo.irq);
++		} else {
++			DBF_EVENT(3,
++				"T390(%04x): resume processing\n",
++				p->devno);
++			TAPE_CLEAR_STATE(device, TAPE_STATUS_NOACCESS);
++			tape_schedule_bh(device);
++		}
++		spin_unlock_irqrestore(
++			get_irq_lock(device->devinfo.irq), flags);
++
++		tape_put_device(device);
++		kfree(p);
++		return;
++	}
++
++	/* If we get here device is NULL. */	
++	if (tape_autodetect && tape_add_range(p->devno, p->devno) != 0) {
++		kfree(p);
++		return;
++	}
++
++	/* Find discipline for this device. */
++	discipline = tape_get_discipline(p->cu_type);
++	if (discipline == NULL) {
++		/* Strange. Should not happen. */
++		kfree(p);
++		return;
++	}
++
++	device = tape_create_device(p->devno);
++	if (IS_ERR(device)) {
++		tape_put_discipline(discipline);
++		kfree(p);
++		return;
++	}
++	if(tape_enable_device(device, discipline) < 0)
++		tape_devmap_remove_device(device);
++	tape_put_device(device);
++	tape_put_discipline(discipline);
++	kfree(p);
++}
++
++int
++tape_oper_handler(int irq, devreg_t *devreg)
++{
++	struct {
++		int			devno;
++		int			cu_type;
++		struct tq_struct	task;
++	} *p;
++	s390_dev_info_t dinfo;
++	int rc;
++
++	rc = get_dev_info_by_irq (irq, &dinfo);
++	if (rc < 0)
++		return rc;
++
++	/* No memory, we loose. */
++	if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
++		return -ENOMEM;
++
++	p->devno   = dinfo.devno;
++	p->cu_type = dinfo.sid_data.cu_type;
++	memset(&p->task, 0, sizeof(struct tq_struct));
++	p->task.routine = do_tape_oper_handler;
++	p->task.data    = p;
++
++	/* queue call to do_oper_handler. */
++	queue_task(&p->task, &tape_cio_tasks);
++	run_task_queue(&tape_cio_tasks);
++
++	return 0;
++}
++
++
++/*
++ * Not Oper Handler is called from Ingo's IO layer, when a tape device
++ * is detached.
++ */
++static void
++do_tape_noper_handler(void *data)
++{
++	struct {
++		int			irq;
++		int			status;
++		struct tq_struct	task;
++	} *p;
++	struct tape_device	*device;
++	struct list_head	*l;
++	struct tape_devmap	*devmap;
++	unsigned long		 flags;
++
++	p = data;
++
++	/*
++	 * find out devno of leaving device: CIO has already deleted
++	 * this information so we need to find it by irq!
++	 */
++	device = tape_get_device_by_irq(p->irq);
++	if (IS_ERR(device)) {
++		kfree(p);
++		return;
++	}
++
++	/*
++	 * Handle the new path revalidation scheme of the common IO layer.
++	 */
++	switch(p->status) {
++		case DEVSTAT_DEVICE_GONE:
++		case DEVSTAT_REVALIDATE: /* FIXME: What to do? */
++			tape_disable_device(device, 1);
++
++			/*
++			 * Remove the device reference from the device map.
++			 */
++			spin_lock_irqsave(&tape_devmap_lock, flags);
++			list_for_each(l, &tape_devreg_list) {
++				devmap = list_entry(
++						l, struct tape_devmap, list
++					);
++				if (devmap->device == device) {
++					tape_put_device(device);
++					devmap->device = NULL;
++					break;
++				}
++			}
++			spin_unlock_irqrestore(&tape_devmap_lock, flags);
++			break;
++		case DEVSTAT_NOT_ACC:
++			/*
++			 * Device shouldn't be accessed at the moment. The
++			 * currently running request will complete.
++			 */
++			spin_lock_irqsave(
++				get_irq_lock(device->devinfo.irq), flags
++			);
++			DBF_EVENT(3, "T390(%04x): suspend processing\n",
++				device->devinfo.devno);
++			TAPE_SET_STATE(device, TAPE_STATUS_NOACCESS);
++			spin_unlock_irqrestore(
++				get_irq_lock(device->devinfo.irq), flags
++			);
++			break;
++		case DEVSTAT_NOT_ACC_ERR: {
++			struct tape_request *request;
++
++			/*
++			 * Device shouldn't be accessed at the moment. The
++			 * request that was running is lost.
++			 */
++			spin_lock_irqsave(
++				get_irq_lock(device->devinfo.irq), flags
++			);
++
++			request = list_entry(device->req_queue.next,
++					struct tape_request, list);
++			if(
++				!list_empty(&device->req_queue)
++				&&
++				request->status == TAPE_REQUEST_IN_IO
++			) {
++				/* Argh! Might better belong to tape_core.c */
++				list_del(&request->list);
++				request->rc     = -EIO;
++				request->status = TAPE_REQUEST_DONE;
++				if (request->callback != NULL) {
++					request->callback(
++						request,
++						request->callback_data
++					);
++					request->callback = NULL;
++				}
++			}
++			DBF_EVENT(3, "T390(%04x): suspend processing\n",
++				device->devinfo.devno);
++			DBF_EVENT(3, "T390(%04x): request lost\n",
++				device->devinfo.devno);
++			TAPE_SET_STATE(device, TAPE_STATUS_NOACCESS);
++			spin_unlock_irqrestore(
++				get_irq_lock(device->devinfo.irq), flags
++			);
++			break;
++		}
++		default:
++			PRINT_WARN("T390(%04x): no operation handler called "
++				"with unknown status(0x%x)\n",
++				device->devinfo.devno, p->status);
++			tape_disable_device(device, 1);
++
++			/*
++			 * Remove the device reference from the device map.
++			 */
++			spin_lock_irqsave(&tape_devmap_lock, flags);
++			list_for_each(l, &tape_devreg_list) {
++				devmap = list_entry(
++						l, struct tape_devmap, list
++					);
++				if (devmap->device == device) {
++					tape_put_device(device);
++					devmap->device = NULL;
++					break;
++				}
++			}
++			spin_unlock_irqrestore(&tape_devmap_lock, flags);
++	}
++
++	tape_put_device(device);
++	kfree(p);
++}
++
++void
++tape_noper_handler(int irq, int status)
++{
++	struct {
++		int			irq;
++		int			status;
++		struct tq_struct	task;
++	} *p;
++
++	/* No memory, we loose. */
++	if ((p = kmalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
++		return;
++
++	p->irq  = irq;
++	p->status = status;
++	memset(&p->task, 0, sizeof(struct tq_struct));
++	p->task.routine = do_tape_noper_handler;
++	p->task.data    = p;
++	
++	/* queue call to do_oper_handler. */
++	queue_task(&p->task, &tape_cio_tasks);
++	run_task_queue(&tape_cio_tasks);
++}
++
++
++int
++tape_devmap_init(void)
++{
++	return tape_parse();
++}
++
++void
++tape_devmap_exit(void)
++{
++	tape_forget_devregs();
++}
++
++EXPORT_SYMBOL(tape_get_device);
++EXPORT_SYMBOL(tape_get_device_by_irq);
++EXPORT_SYMBOL(tape_get_device_by_devno);
++EXPORT_SYMBOL(tape_put_device);
++EXPORT_SYMBOL(tape_clone_device);
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape.h drivers/s390/char/tape.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape.h	2001-07-25 15:12:02.000000000 -0600
++++ drivers/s390/char/tape.h	2006-02-12 12:47:23.000000000 -0700
+@@ -1,203 +1,436 @@
 -/***************************************************************************
 - *
-- *  drivers/s390/char/tapechar.h
-- *    character device frontend for tape device driver
-- *
-- *  S390 and zSeries version
++/*
+  *  drivers/s390/char/tape.h
+- *    tape device driver for 3480/3490E tapes.
++ *    tape device driver for 3480/3490E/3590 tapes.
+  *
+  *  S390 and zSeries version
 - *    Copyright (C) 2001 IBM Corporation
-- *    Author(s): Carsten Otte <cotte at de.ibm.com>
++ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
+  *    Author(s): Carsten Otte <cotte at de.ibm.com>
 - *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
 - *
-- *
 - ****************************************************************************
-- */
++ *		 Tuan Ngo-Anh <ngoanh at de.ibm.com>
++ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
++ *		 Stefan Bader <shbader at de.ibm.com>
+  */
+ 
+ #ifndef _TAPE_H
 -
--#ifndef TAPECHAR_H
--#define TAPECHAR_H
--#include <linux/config.h>
--#define TAPECHAR_DEFAULTMODE 0020644
--#define  TAPE_MAJOR                    0        /* get dynamic major since no major officialy defined for tape */
--/*
-- * Prototypes for tape_fops
-- */
--ssize_t tape_read(struct file *, char *, size_t, loff_t *);
--ssize_t tape_write(struct file *, const char *, size_t, loff_t *);
--int tape_ioctl(struct inode *,struct file *,unsigned int,unsigned long);
--int tape_open (struct inode *,struct file *);
--int tape_release (struct inode *,struct file *);
--#ifdef CONFIG_DEVFS_FS
--void tapechar_mkdevfstree (tape_info_t* ti);
--#endif
--void tapechar_init (void);
--void tapechar_uninit (void);
--#endif /* TAPECHAR_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapedefs.h drivers/s390/char/tapedefs.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapedefs.h	2001-07-25 15:12:02.000000000 -0600
-+++ drivers/s390/char/tapedefs.h	2006-01-30 22:25:26.000000000 -0700
-@@ -1,76 +0,0 @@
--/***********************************************************************
-- *  drivers/s390/char/tapedefs.h
-- *    tape device driver for S/390 and zSeries tapes.
-- *
-- *  S390 and zSeries version
-- *    Copyright (C) 2001 IBM Corporation
-- *    Author(s):  Carsten Otte <cotte at de.ibm.com>
-- *                Tuan Ngo-Anh <ngoanh at de.ibm.com>
-- *
-- *
-- ***********************************************************************
-- */
+ #define _TAPE_H
++
+ #include <linux/config.h>
+ #include <linux/blkdev.h>
 -
--/* Kernel Version Compatibility section */
--#include <linux/version.h>
--#include <linux/blkdev.h>
--#include <linux/blk.h>
--#include <asm/irq.h>
+-#define  MAX_TAPES                     7        /* Max tapes supported is 7*/
+-#define TAPE_MAGIC 0xE3C1D7C5       /* is ebcdic-"TAPE" */
+-
+-typedef enum {
+-    TS_UNUSED=0, TS_IDLE, TS_DONE, TS_FAILED,
+-    TS_BLOCK_INIT,
+-    TS_BSB_INIT,
+-    TS_BSF_INIT,
+-    TS_DSE_INIT,
+-    TS_EGA_INIT,
+-    TS_FSB_INIT,
+-    TS_FSF_INIT,
+-    TS_LDI_INIT,
+-    TS_LBL_INIT,
+-    TS_MSE_INIT,
+-    TS_NOP_INIT,
+-    TS_RBA_INIT,
+-    TS_RBI_INIT,
+-    TS_RBU_INIT,
+-    TS_RBL_INIT,
+-    TS_RDC_INIT,
+-    TS_RFO_INIT,
+-    TS_RSD_INIT,
+-    TS_REW_INIT,
+-    TS_REW_RELEASE_INIT,
+-    TS_RUN_INIT,
+-    TS_SEN_INIT,
+-    TS_SID_INIT,
+-    TS_SNP_INIT,
+-    TS_SPG_INIT,
+-    TS_SWI_INIT,
+-    TS_SMR_INIT,
+-    TS_SYN_INIT,
+-    TS_TIO_INIT,
+-    TS_UNA_INIT,
+-    TS_WRI_INIT,
+-    TS_WTM_INIT,
+-    TS_NOT_OPER,
+-    TS_SIZE } tape_stat;
+-
+-struct _tape_info_t; //Forward declaration
+-
+-typedef enum {
+-    TE_START=0, TE_DONE, TE_FAILED, TE_ERROR, TE_OTHER,
+-    TE_SIZE } tape_events;
+-
+-typedef void (*tape_disc_shutdown_t) (int);
+-typedef void (*tape_event_handler_t) (struct _tape_info_t*);
+-typedef ccw_req_t* (*tape_ccwgen_t)(struct _tape_info_t* ti,int count);
+-typedef ccw_req_t* (*tape_reqgen_t)(struct request* req,struct _tape_info_t* ti,int tapeblock_major);
+-typedef ccw_req_t* (*tape_rwblock_t)(const char* data,size_t count,struct _tape_info_t* ti);
+-typedef void (*tape_freeblock_t)(ccw_req_t* cqr,struct _tape_info_t* ti);
+-typedef void (*tape_setup_assist_t) (struct _tape_info_t*);
++#include <linux/kernel.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/mtio.h>
++#include <linux/interrupt.h>
++#include <linux/timer.h>
++#include <asm/debug.h>
++#include <asm/idals.h>
++#include <asm/s390dyn.h>
+ #ifdef CONFIG_DEVFS_FS
+-typedef void (*tape_devfs_handler_t) (struct _tape_info_t*);
++#include <linux/devfs_fs_kernel.h>
+ #endif
+-typedef tape_event_handler_t tape_event_table_t[TS_SIZE][TE_SIZE];
+-typedef struct _tape_discipline_t {
+-    unsigned int cu_type;
+-    tape_setup_assist_t setup_assist;
+-    tape_event_handler_t error_recovery;
+-    tape_reqgen_t bread;
+-    tape_freeblock_t free_bread;
+-    tape_rwblock_t write_block;
+-    tape_freeblock_t free_write_block;
+-    tape_rwblock_t read_block;
+-    tape_freeblock_t free_read_block;
+-    tape_ccwgen_t mtfsf;
+-    tape_ccwgen_t mtbsf;
+-    tape_ccwgen_t mtfsr;
+-    tape_ccwgen_t mtbsr;
+-    tape_ccwgen_t mtweof;
+-    tape_ccwgen_t mtrew;
+-    tape_ccwgen_t mtoffl;
+-    tape_ccwgen_t mtnop;
+-    tape_ccwgen_t mtbsfm;
+-    tape_ccwgen_t mtfsfm;
+-    tape_ccwgen_t mteom;
+-    tape_ccwgen_t mterase;
+-    tape_ccwgen_t mtsetdensity;
+-    tape_ccwgen_t mtseek;
+-    tape_ccwgen_t mttell;
+-    tape_ccwgen_t mtsetdrvbuffer;
+-    tape_ccwgen_t mtlock;
+-    tape_ccwgen_t mtunlock;
+-    tape_ccwgen_t mtload;
+-    tape_ccwgen_t mtunload;
+-    tape_ccwgen_t mtcompression;
+-    tape_ccwgen_t mtsetpart;
+-    tape_ccwgen_t mtmkpart;
+-    tape_ccwgen_t mtiocget;
+-    tape_ccwgen_t mtiocpos;
+-    tape_disc_shutdown_t shutdown;
+-    int (*discipline_ioctl_overload)(struct inode *,struct file*, unsigned int,unsigned long);
+-    tape_event_table_t* event_table;
+-    tape_event_handler_t default_handler;
+-    struct _tape_info_t* tape; /* pointer for backreference */
+-    void* next;
+-} tape_discipline_t  __attribute__ ((aligned(8)));
+ 
+-typedef struct _tape_frontend_t {
+-    tape_setup_assist_t device_setup;
++/*
++ * macros s390 debug feature (dbf)
++ */
++#define DBF_EVENT(d_level, d_str...) \
++do { \
++	debug_sprintf_event(TAPE_DBF_AREA, d_level, d_str); \
++} while (0)
++
++#define DBF_EXCEPTION(d_level, d_str...) \
++do { \
++	debug_sprintf_exception(TAPE_DBF_AREA, d_level, d_str); \
++} while (0)
++
++#define TAPE_VERSION_MAJOR 2
++#define TAPE_VERSION_MINOR 0
++#define TAPE_MAGIC "tape"
++
++#define TAPE_MINORS_PER_DEV 2       /* two minors per device */
++#define TAPEBLOCK_HSEC_SIZE	2048
++#define TAPEBLOCK_HSEC_S2B	2
++#define TAPEBLOCK_RETRIES	5
++
++/* Event types for hotplug */
++#define TAPE_HOTPLUG_CHAR_ADD     1
++#define TAPE_HOTPLUG_BLOCK_ADD    2
++#define TAPE_HOTPLUG_CHAR_REMOVE  3
++#define TAPE_HOTPLUG_BLOCK_REMOVE 4
++
++enum tape_medium_state {
++	MS_UNKNOWN,
++	MS_LOADED,
++	MS_UNLOADED,
++	MS_SIZE
++};
++
++enum tape_op {
++	TO_BLOCK,	/* Block read */
++	TO_BSB,		/* Backward space block */
++	TO_BSF,		/* Backward space filemark */
++	TO_DSE,		/* Data security erase */
++	TO_FSB,		/* Forward space block */
++	TO_FSF,		/* Forward space filemark */
++	TO_LBL,		/* Locate block label */
++	TO_NOP,		/* No operation */
++	TO_RBA,		/* Read backward */
++	TO_RBI,		/* Read block information */
++	TO_RFO,		/* Read forward */
++	TO_REW,		/* Rewind tape */
++	TO_RUN,		/* Rewind and unload tape */
++	TO_WRI,		/* Write block */
++	TO_WTM,		/* Write tape mark */
++	TO_MSEN,	/* Medium sense */
++	TO_LOAD,	/* Load tape */
++	TO_READ_CONFIG, /* Read configuration data */
++	TO_READ_ATTMSG, /* Read attention message */
++	TO_DIS,		/* Tape display */
++	TO_ASSIGN,	/* Assign tape to channel path */
++	TO_UNASSIGN,	/* Unassign tape from channel path */
++	TO_BREAKASS,    /* Break the assignment of another host */
++	TO_SIZE		/* #entries in tape_op_t */
++};
++
++/* Forward declaration */
++struct tape_device;
++
++/* The tape device list lock */
++extern rwlock_t	  tape_dev_lock;
++
++/* Tape CCW request */
++struct tape_request {
++	struct list_head list;		/* list head for request queueing. */
++	struct tape_device *device;	/* tape device of this request */
++	ccw1_t *cpaddr;			/* address of the channel program. */
++	void *cpdata;			/* pointer to ccw data. */
++	char status;			/* status of this request */
++	int options;			/* options for execution. */
++	int retries;			/* retry counter for error recovery. */
++
++	/*
++	 * This timer can be used to automatically cancel a request after
++	 * some time. Specifically the assign request seems to lockup under
++	 * certain circumstances.
++	 */
++	struct timer_list	timeout;
++
++	enum			tape_op op;
++	int			rc;
++	atomic_t		ref_count;
++
++	/* Callback for delivering final status. */
++	void (*callback)(struct tape_request *, void *);
++	void *callback_data;
++};
++
++/* tape_request->status can be: */
++#define TAPE_REQUEST_INIT	0x00	/* request is ready to be processed */
++#define TAPE_REQUEST_QUEUED	0x01	/* request is queued to be processed */
++#define TAPE_REQUEST_IN_IO	0x02	/* request is currently in IO */
++#define TAPE_REQUEST_DONE	0x03	/* request is completed. */
++
++/* Function type for magnetic tape commands */
++typedef int (*tape_mtop_fn)(struct tape_device *, int);
++
++/* Size of the array containing the mtops for a discipline */
++#define TAPE_NR_MTOPS (MTMKPART+1)
++
++/* Tape Discipline */
++struct tape_discipline {
++	struct list_head list;
++	struct module *owner;
++	unsigned int cu_type;
++	int  (*setup_device)(struct tape_device *);
++	void (*cleanup_device)(struct tape_device *);
++	int (*assign)(struct tape_device *);
++	int (*unassign)(struct tape_device *);
++	int (*force_unassign)(struct tape_device *);
++	int (*irq)(struct tape_device *, struct tape_request *);
++	struct tape_request *(*read_block)(struct tape_device *, size_t);
++	struct tape_request *(*write_block)(struct tape_device *, size_t);
++	void (*process_eov)(struct tape_device*);
++	/* Block device stuff. */
++	struct tape_request *(*bread)(struct tape_device *, struct request *);
++	void (*check_locate)(struct tape_device *, struct tape_request *);
++	void (*free_bread)(struct tape_request *);
++	/* ioctl function for additional ioctls. */
++	int (*ioctl_fn)(struct tape_device *, unsigned int, unsigned long);
++	/* Array of tape commands with TAPE_NR_MTOPS entries */
++	tape_mtop_fn *mtop_array;
++};
++
++/*
++ * The discipline irq function either returns an error code (<0) which
++ * means that the request has failed with an error or one of the following:
++ */
++#define TAPE_IO_SUCCESS 0	/* request successful */
++#define TAPE_IO_PENDING 1	/* request still running */
++#define TAPE_IO_RETRY	2	/* retry to current request */
++#define TAPE_IO_STOP	3	/* stop the running request */
++
++/* Char Frontend Data */
++struct tape_char_data {
++	/* Idal buffer to temporaily store character data */
++	struct idal_buffer *	idal_buf;
++	/* Block size (in bytes) of the character device (0=auto) */
++	int			block_size;
++#ifdef CONFIG_DEVFS_FS
++	/* tape/<DEVNO>/char subdirectory in devfs */
++	devfs_handle_t		devfs_char_dir;
++	/* tape/<DEVNO>/char/nonrewinding entry in devfs */
++	devfs_handle_t		devfs_nonrewinding;
++	/* tape/<DEVNO>/char/rewinding entry in devfs */
++	devfs_handle_t		devfs_rewinding;
++#endif /* CONFIG_DEVFS_FS */
++};
++
++#ifdef CONFIG_S390_TAPE_BLOCK
++/* Block Frontend Data */
++struct tape_blk_data
++{
++	/* Block device request queue. */
++	request_queue_t		request_queue;
++	/* Block frontend tasklet */
++	struct tasklet_struct	tasklet;
++	/* Current position on the tape. */
++	unsigned int		block_position;
++	/* The start of the block device image file */
++	unsigned int		start_block_id;
+ #ifdef CONFIG_DEVFS_FS
+-    tape_devfs_handler_t mkdevfstree;
+-    tape_devfs_handler_t rmdevfstree;
++	/* tape/<DEVNO>/block subdirectory in devfs */
++	devfs_handle_t		devfs_block_dir;
++	/* tape/<DEVNO>/block/disc entry in devfs */
++	devfs_handle_t		devfs_disc;
++#endif /* CONFIG_DEVFS_FS */
++};
+ #endif
+-    void* next;
+-} tape_frontend_t  __attribute__ ((aligned(8)));
+ 
++#define TAPE_STATUS_INIT		0x00000001
++#define TAPE_STATUS_ASSIGN_M		0x00000002
++#define TAPE_STATUS_ASSIGN_A		0x00000004
++#define TAPE_STATUS_OPEN		0x00000008
++#define TAPE_STATUS_BLOCKDEV		0x00000010
++#define TAPE_STATUS_BOXED		0x20000000
++#define TAPE_STATUS_NOACCESS		0x40000000
++#define TAPE_STATUS_NOT_OPER		0x80000000
++
++#define TAPE_SET_STATE(td,st) \
++	do { \
++		tape_state_set(td, td->tape_status | (st)); \
++	} while(0)
++#define TAPE_CLEAR_STATE(td,st) \
++	do { \
++		tape_state_set(td, td->tape_status & ~(st)); \
++	} while(0)
++
++#define TAPE_UNUSED(td)		(!TAPE_OPEN(td))
++#define TAPE_INIT(td)		(td->tape_status & TAPE_STATUS_INIT)
++#define TAPE_ASSIGNED(td)	( \
++					td->tape_status & ( \
++						TAPE_STATUS_ASSIGN_M | \
++						TAPE_STATUS_ASSIGN_A \
++					) \
++				)
++#define TAPE_OPEN(td)		(td->tape_status & TAPE_STATUS_OPEN)
++#define TAPE_BLOCKDEV(td)	(td->tape_status & TAPE_STATUS_BLOCKDEV)
++#define TAPE_BOXED(td)		(td->tape_status & TAPE_STATUS_BOXED)
++#define TAPE_NOACCESS(td)	(td->tape_status & TAPE_STATUS_NOACCESS)
++#define TAPE_NOT_OPER(td)	(td->tape_status & TAPE_STATUS_NOT_OPER)
++
++/* Tape Info */
++struct tape_device {
++	/* Device discipline information. */
++	struct tape_discipline *discipline;
++	void *			discdata;
++
++	/* Generic status bits */
++	long			tape_generic_status;
++	unsigned int		tape_status;
++	enum tape_medium_state	medium_state;
++
++	/* Number of tapemarks required for correct termination */
++	int			required_tapemarks;
++
++	/* Waitqueue for state changes and device flags */
++	wait_queue_head_t	state_change_wq;
++	unsigned char *		modeset_byte;
++
++	/* Reference count. */
++	atomic_t		ref_count;
++
++	/* For persistent assign */
++	spinlock_t		assign_lock;
++
++	/* Request queue. */
++	struct list_head	req_queue;
++	atomic_t		bh_scheduled;
++	struct tq_struct	bh_task;
++
++	/* Common i/o stuff. */
++	s390_dev_info_t		devinfo;
++	devstat_t		devstat;
++
++	/* each tape device has two minors */
++	int			first_minor;
+ 
+-typedef struct _tape_info_t {
+-    wait_queue_head_t wq;
+-    s390_dev_info_t devinfo;             /* device info from Common I/O */
+-    int     wanna_wakeup;
+-    int     rew_minor;                  /* minor number for the rewinding tape */
+-    int     nor_minor;                  /* minor number for the nonrewinding tape */
+-    int     blk_minor;                  /* minor number for the block device */
+-    devstat_t devstat;	           /* contains irq, devno, status */
+-    size_t  block_size;             /* block size of tape        */
+-    int    drive_type;              /* Code indicating type of drive */
+-    struct file *rew_filp;	           /* backpointer to file structure */
+-    struct file *nor_filp;
+-    struct file *blk_filp;
+-    int tape_state;  /* State of the device. See tape_stat */
+-    int rc;          /* Return code. */
+-    tape_discipline_t* discipline;
+-    request_queue_t request_queue;
+-    struct request* current_request;
+-    int blk_retries;
+-    long position;
+-    int medium_is_unloaded;  // Becomes true when a unload-type operation was issued, false again when medium-insert was detected
+-    ccw_req_t* cqr;
+-    atomic_t bh_scheduled;
+-    struct tq_struct bh_tq;
+ #ifdef CONFIG_DEVFS_FS
+-    devfs_handle_t devfs_dir;             /* devfs handle for tape/DEVNO directory */
+-    devfs_handle_t devfs_char_dir;        /* devfs handle for tape/DEVNO/char directory */
+-    devfs_handle_t devfs_block_dir;       /* devfs handle for tape/DEVNO/block directory */
+-    devfs_handle_t devfs_nonrewinding;    /* devfs handle for tape/DEVNO/char/nonrewinding device */
+-    devfs_handle_t devfs_rewinding;       /* devfs handle for tape/DEVNO/char/rewinding device */
+-    devfs_handle_t devfs_disc;            /* devfs handle for tape/DEVNO/block/disc device */
++	/* Toplevel devfs directory. */
++	devfs_handle_t		devfs_dir;
++#endif /* CONFIG_DEVFS_FS */
++	/* Character device frontend data */
++	struct tape_char_data	char_data;
++#ifdef CONFIG_S390_TAPE_BLOCK
++	/* Block dev frontend data */
++	struct tape_blk_data	blk_data;
+ #endif
+-    void* discdata;
+-    void* kernbuf;
+-    void* userbuf;
+-    void*  next;
+-} tape_info_t  __attribute__ ((aligned(8)));
++};
+ 
+-/* tape initialisation functions */
+-int tape_init(void);
+-int tape_setup (tape_info_t * ti, int irq, int minor);
++/* Externals from tape_core.c */
++struct tape_request *tape_alloc_request(int cplength, int datasize);
++struct tape_request *tape_put_request(struct tape_request *);
++struct tape_request *tape_clone_request(struct tape_request *);
++int tape_do_io(struct tape_device *, struct tape_request *);
++int tape_do_io_async(struct tape_device *, struct tape_request *);
++int tape_do_io_interruptible(struct tape_device *, struct tape_request *);
++void tape_schedule_bh(struct tape_device *);
++void tape_hotplug_event(struct tape_device *, int, int);
++
++static inline int
++tape_do_io_free(struct tape_device *device, struct tape_request *request)
++{
++	int rc;
++
++	rc = tape_do_io(device, request);
++	tape_put_request(request);
++	return rc;
++}
++
++int tape_oper_handler(int irq, devreg_t *devreg);
++void tape_noper_handler(int irq, int status);
++int tape_open(struct tape_device *);
++int tape_release(struct tape_device *);
++int tape_assign(struct tape_device *, int type);
++int tape_unassign(struct tape_device *, int type);
++int tape_mtop(struct tape_device *, int, int);
++
++/* Externals from tape_devmap.c */
++int tape_devmap_init(void);
++void tape_devmap_exit(void);
++
++struct tape_device *tape_get_device(int devindex);
++struct tape_device *tape_get_device_by_devno(int devno);
++struct tape_device *tape_clone_device(struct tape_device *);
++void tape_put_device(struct tape_device *);
++
++void tape_auto_detect(void);
++void tape_add_devices(struct tape_discipline *);
++void tape_remove_devices(struct tape_discipline *);
++
++extern int tape_max_devindex;
++
++/* Externals from tape_char.c */
++int tapechar_init(void);
++void tapechar_exit(void);
++int  tapechar_setup_device(struct tape_device *);
++void tapechar_cleanup_device(struct tape_device *);
++
++/* Externals from tape_block.c */
++int tapeblock_init (void);
++void tapeblock_exit(void);
++int tapeblock_setup_device(struct tape_device *);
++void tapeblock_cleanup_device(struct tape_device *);
++void tapeblock_medium_change(struct tape_device *);
++
++/* Discipline functions */
++int tape_register_discipline(struct tape_discipline *);
++void tape_unregister_discipline(struct tape_discipline *);
++struct tape_discipline *tape_get_discipline(int cu_type);
++void tape_put_discipline(struct tape_discipline *);
++int tape_enable_device(struct tape_device *, struct tape_discipline *);
++void tape_disable_device(struct tape_device *device, int gone);
+ 
+-/* functoins for alloc'ing ccw stuff */
+-inline  ccw_req_t * tape_alloc_ccw_req (tape_info_t* ti, int cplength, int datasize);
+-void tape_free_request (ccw_req_t * request);
++/* tape initialisation functions */
++void tape_proc_init (void);
++void tape_proc_cleanup (void);
+ 
+ /* a function for dumping device sense info */
+-void tape_dump_sense (devstat_t * stat);
 -
--#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,17))
--#define TAPE_DEBUG               // use s390 debug feature
--#else
--#undef TAPE_DEBUG                // debug feature not supported by our 2.2.16 code
--static inline void set_normalized_cda ( ccw1_t * cp, unsigned long address ) {
--    cp -> cda = address;
--}
--static inline void clear_normalized_cda ( ccw1_t * ccw ) {
--    ccw -> cda = 0;
--}
--#define BUG() PRINT_FATAL("tape390: CRITICAL INTERNAL ERROR OCCURED. REPORT THIS BACK TO LINUX390 at DE.IBM.COM\n")
+-#ifdef CONFIG_S390_TAPE_DYNAMIC
+-/* functions for dyn. dev. attach/detach */
+-int tape_oper_handler ( int irq, struct _devreg *dreg);
 -#endif
--#define CONFIG_S390_TAPE_DYNAMIC // allow devices to be attached or detached on the fly
--#define TAPEBLOCK_RETRIES 20     // number of retries, when a block-dev request fails.
++void tape_dump_sense(struct tape_device *, struct tape_request *);
++void tape_dump_sense_dbf(struct tape_device *, struct tape_request *);
+ 
+ /* functions for handling the status of a device */
+-inline void tapestate_set (tape_info_t * ti, int newstate);
+-inline int tapestate_get (tape_info_t * ti);
+-void tapestate_event (tape_info_t * ti, int event);
+-extern char* state_verbose[TS_SIZE];
+-extern char* event_verbose[TE_SIZE];
 -
+-/****************************************************************************/
 -
--#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
--#define INIT_BLK_DEV(d_major,d_request_fn,d_queue_fn,d_current) \
--do { \
--        blk_dev[d_major].queue = d_queue_fn; \
--} while(0)
--static inline struct request * 
--tape_next_request( request_queue_t *queue ) 
--{
--        return blkdev_entry_next_request(&queue->queue_head);
--}
--static inline void 
--tape_dequeue_request( request_queue_t * q, struct request *req )
--{
--        blkdev_dequeue_request (req);
--}
--#else 
--#define s390_dev_info_t dev_info_t
--typedef struct request *request_queue_t;
--#ifndef init_waitqueue_head
--#define init_waitqueue_head(x) do { *x = NULL; } while(0)
+-/* Some linked lists for storing plugins and devices */
+-extern tape_info_t *first_tape_info;
+-extern tape_discipline_t *first_discipline;
+-extern tape_frontend_t *first_frontend;
++inline void tape_state_set (struct tape_device *, unsigned int status);
++inline void tape_med_state_set(struct tape_device *, enum tape_medium_state);
++const char *tape_state_string(struct tape_device *);
++
++/* Tape 3480/3490 init/exit functions. */
++int tape_34xx_init(void);
++void tape_34xx_exit(void);
+ 
+ /* The debug area */
+-#ifdef TAPE_DEBUG
+-extern debug_info_t *tape_debug_area;
 -#endif
--#define blk_init_queue(x,y) do {} while(0)
--#define blk_queue_headactive(x,y) do {} while(0)
--#define INIT_BLK_DEV(d_major,d_request_fn,d_queue_fn,d_current) \
--do { \
--        blk_dev[d_major].request_fn = d_request_fn; \
--        blk_dev[d_major].queue = d_queue_fn; \
--        blk_dev[d_major].current_request = d_current; \
--} while(0)
--static inline struct request *
--tape_next_request( request_queue_t *queue ) 
--{
--    return *queue;
--}
--static inline void 
--tape_dequeue_request( request_queue_t * q, struct request *req )
--{
--        *q = req->next;
--        req->next = NULL;
--}
--#endif 
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tuball.c drivers/s390/char/tuball.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tuball.c	2003-06-13 08:51:36.000000000 -0600
-+++ drivers/s390/char/tuball.c	2006-01-30 22:25:26.000000000 -0700
++extern debug_info_t *TAPE_DBF_AREA;
++
++/* functions for building ccws */
++static inline ccw1_t *
++tape_ccw_cc(ccw1_t *ccw, __u8 cmd_code, __u16 memsize, void *cda)
++{
++	ccw->cmd_code = cmd_code;
++	ccw->flags = CCW_FLAG_CC;
++	ccw->count = memsize;
++	ccw->cda = (__u32)(addr_t) cda;
++	return ccw + 1;
++}
++
++static inline ccw1_t *
++tape_ccw_end(ccw1_t *ccw, __u8 cmd_code, __u16 memsize, void *cda)
++{
++	ccw->cmd_code = cmd_code;
++	ccw->flags = 0;
++	ccw->count = memsize;
++	ccw->cda = (__u32)(addr_t) cda;
++	return ccw + 1;
++}
++
++static inline ccw1_t *
++tape_ccw_cmd(ccw1_t *ccw, __u8 cmd_code)
++{
++	ccw->cmd_code = cmd_code;
++	ccw->flags = 0;
++	ccw->count = 0;
++	ccw->cda = (__u32)(addr_t) &ccw->cmd_code;
++	return ccw + 1;
++}
++
++static inline ccw1_t *
++tape_ccw_repeat(ccw1_t *ccw, __u8 cmd_code, int count)
++{
++	while (count-- > 0) {
++		ccw->cmd_code = cmd_code;
++		ccw->flags = CCW_FLAG_CC;
++		ccw->count = 0;
++		ccw->cda = (__u32)(addr_t) &ccw->cmd_code;
++		ccw++;
++	}
++	return ccw;
++}
++
++extern inline ccw1_t*
++tape_ccw_cc_idal(ccw1_t *ccw, __u8 cmd_code, struct idal_buffer *idal)
++{
++	ccw->cmd_code = cmd_code;
++	ccw->flags    = CCW_FLAG_CC;
++	idal_buffer_set_cda(idal, ccw);
++	return ccw++;
++}
++
++extern inline ccw1_t*
++tape_ccw_end_idal(ccw1_t *ccw, __u8 cmd_code, struct idal_buffer *idal)
++{
++	ccw->cmd_code = cmd_code;
++	ccw->flags    = 0;
++	idal_buffer_set_cda(idal, ccw);
++	return ccw++;
++}
++
++/* Global vars */
++extern const char *tape_op_verbose[];
+ 
+ #endif /* for ifdef tape.h */
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape_proc.c drivers/s390/char/tape_proc.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape_proc.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/tape_proc.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,385 @@
++/*
++ *  drivers/s390/char/tape.c
++ *    tape device driver for S/390 and zSeries tapes.
++ *
++ *  S390 and zSeries version
++ *    Copyright (C) 2001 IBM Corporation
++ *    Author(s): Carsten Otte <cotte at de.ibm.com>
++ *               Michael Holzheu <holzheu at de.ibm.com>
++ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
++ *
++ * PROCFS Functions
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/vmalloc.h>
++#include <linux/seq_file.h>
++#include <asm/irq.h>
++#include <asm/s390io.h>
++
++#define TAPE_DBF_AREA	tape_core_dbf
++
++#include "tape.h"
++
++#define PRINTK_HEADER "T390:"
++
++static const char *tape_med_st_verbose[MS_SIZE] =
++{
++	[MS_UNKNOWN]  = "UNKNOWN ",
++	[MS_LOADED]   = "LOADED  ",
++	[MS_UNLOADED] = "UNLOADED"
++};
++
++/* our proc tapedevices entry */
++static struct proc_dir_entry *tape_proc_devices;
++
++static int tape_proc_show(struct seq_file *m, void *v) {
++	struct tape_device  *device;
++	struct tape_request *request;
++	unsigned long        n;
++
++	n = ((unsigned long) v - 1);
++
++	if(n == 0) {
++		seq_printf(m,
++			"TapeNo\tDevNo\tCuType\tCuModel\tDevType\t"
++			"DevMod\tBlkSize\tState\tOp\tMedState\n"
++		);
++	}
++
++	device = tape_get_device(n);
++	if(IS_ERR(device))
++		return 0;
++
++	spin_lock_irq(get_irq_lock(device->devinfo.irq));
++
++	seq_printf(m,
++		"%d\t%04X\t%04X\t%02X\t%04X\t%02X\t",
++		device->first_minor/TAPE_MINORS_PER_DEV,
++		device->devinfo.devno,
++		device->devinfo.sid_data.cu_type,
++		device->devinfo.sid_data.cu_model,
++		device->devinfo.sid_data.dev_type,
++		device->devinfo.sid_data.dev_model
++	);
++
++	/*
++	 * the blocksize is either 'auto' or the blocksize as a decimal number
++	 */
++	if(device->char_data.block_size == 0)
++		seq_printf(m, "auto\t");
++	else
++		seq_printf(m, "%i\t", device->char_data.block_size);
++
++	seq_printf(m, "%s\t", tape_state_string(device));
++
++	/*
++	 * verbose desciption of current tape operation
++	 */
++	if(!list_empty(&device->req_queue)) {
++		request = list_entry(
++			device->req_queue.next, struct tape_request, list
++		);
++
++		seq_printf(m, "%s\t", tape_op_verbose[request->op]);
++	} else {
++		seq_printf(m, "---\t");
++	}
++	
++	seq_printf(m, "%s\n", tape_med_st_verbose[device->medium_state]);
++
++	spin_unlock_irq(get_irq_lock(device->devinfo.irq));
++	tape_put_device(device);
++
++	return 0;
++}
++
++static void *tape_proc_start(struct seq_file *m, loff_t *pos) {
++	if(*pos < tape_max_devindex)
++		return (void *) ((unsigned long) (*pos) + 1);
++	return NULL;
++}
++
++static void tape_proc_stop(struct seq_file *m, void *v) {
++}
++
++static void *tape_proc_next(struct seq_file *m, void *v, loff_t *pos) {
++	(*pos)++;
++	return tape_proc_start(m, pos);
++}
++
++static struct seq_operations tape_proc_seq = {
++	.start  = tape_proc_start,
++	.next   = tape_proc_next,
++	.stop   = tape_proc_stop,
++	.show   = tape_proc_show,
++};
++
++static int tape_proc_open(struct inode *inode, struct file *file) {
++	return seq_open(file, &tape_proc_seq);
++}
++
++static int
++tape_proc_assign(int devno)
++{
++	int			 rc;
++	struct tape_device	*device;
++
++	if(IS_ERR(device = tape_get_device_by_devno(devno))) {
++		DBF_EVENT(3, "TAPE(%04x): assign invalid device\n", devno);
++		PRINT_ERR("TAPE(%04x): assign invalid device\n", devno);
++		return PTR_ERR(device);
++	}
++
++	rc = tape_assign(device, TAPE_STATUS_ASSIGN_M);
++
++	tape_put_device(device);
++
++	return rc;
++}
++
++static int
++tape_proc_unassign(int devno)
++{
++	int			 rc;
++	struct tape_device	*device;
++
++	if(IS_ERR(device = tape_get_device_by_devno(devno))) {
++		DBF_EVENT(3, "TAPE(%04x): unassign invalid device\n", devno);
++		PRINT_ERR("TAPE(%04x): unassign invalid device\n", devno);
++		return PTR_ERR(device);
++	}
++
++	rc = tape_unassign(device, TAPE_STATUS_ASSIGN_M);
++
++	tape_put_device(device);
++
++	return rc;
++}
++
++#ifdef SMB_DEBUG_BOX
++static int
++tape_proc_put_into_box(int devno)
++{
++	struct tape_device	*device;
++
++	if(IS_ERR(device = tape_get_device_by_devno(devno))) {
++		DBF_EVENT(3, "TAPE(%04x): invalid device\n", devno);
++		PRINT_ERR("TAPE(%04x): invalid device\n", devno);
++		return PTR_ERR(device);
++	}
++
++	TAPE_SET_STATE(device, TAPE_STATUS_BOXED);
++
++	tape_put_device(device);
++
++	return 0;
++}
++#endif
++
++#ifdef TAPE390_FORCE_UNASSIGN
++static int
++tape_proc_force_unassign(int devno)
++{
++	int			 rc;
++	struct tape_device	*device;
++
++	if(IS_ERR(device = tape_get_device_by_devno(devno))) {
++		DBF_EVENT(3, "TAPE(%04x): force unassign invalid device\n",
++			devno);
++		PRINT_ERR("TAPE(%04x): force unassign invalid device\n",
++			devno);
++		return PTR_ERR(device);
++	}
++
++	if (!TAPE_BOXED(device)) {
++		DBF_EVENT(3, "TAPE(%04x): forced unassignment only allowed for"
++			" boxed device\n", devno);
++		PRINT_ERR("TAPE(%04x): forced unassignment only allowed for"
++			" boxed device\n", devno);
++		rc = -EPERM;
++	} else if(device->discipline->force_unassign == NULL) {
++		DBF_EVENT(3, "TAPE(%04x: force unassign is not supported on"
++			" this device\n", devno);
++		PRINT_ERR("TAPE(%04x: force unassign is not supported on"
++			" this device\n", devno);
++		rc = -EPERM;
++	} else { 
++		rc = device->discipline->force_unassign(device);
++		if(rc == 0)
++			spin_lock_irq(get_irq_lock(device->devinfo.irq));
++			TAPE_CLEAR_STATE(
++				device,
++				TAPE_STATUS_BOXED
++				| TAPE_STATUS_ASSIGN_A
++				| TAPE_STATUS_ASSIGN_M
++			);
++			spin_unlock_irq(get_irq_lock(device->devinfo.irq));
++	}
++
++	tape_put_device(device);
++	return rc;
++}
++#endif
++
++/*
++ * Skips over all characters to the position after a newline or beyond the
++ * last character of the string.
++ * Returns the number of characters skiped.
++ */
++static size_t
++tape_proc_skip_eol(const char *buf, size_t len, loff_t *off)
++{
++	loff_t start = *off;
++
++	while((*off - start) < len) {
++		if(*(buf+*off) == '\n') {
++			*off += 1;
++			break;
++		}
++		*off += 1;
++	}
++
++	return (size_t) (*off - start);
++}
++
++/*
++ * Skips over whitespace characters and returns the number of characters
++ * that where skiped.
++ */
++static size_t
++tape_proc_skip_ws(const char *buf, size_t len, loff_t *off)
++{
++	loff_t start = *off;
++
++	while((*off - start) < len) {
++		if(*(buf + *off) != ' ' && *(buf + *off) != '\t')
++			break;
++		*off += 1;
++	}
++
++	return (size_t) (*off - start);
++}
++
++static size_t
++tape_proc_get_hexvalue(char *buf, size_t len, loff_t *off, unsigned int *hex)
++{
++	int          hexdigit;
++	loff_t       start    = *off;
++
++	/* Skip possible space characters */
++	tape_proc_skip_ws(buf, len, off);
++
++	/* The hexvalue might start with '0x' or '0X' */
++	if((*off - start)+1 < len && *(buf + *off) == '0')
++		if(*(buf + *off + 1) == 'x' || *(buf + *off + 1) == 'X')
++			*off += 2;
++
++	*hex = 0;
++	while((*off - start) < len) {
++		if(*(buf + *off) >= '0' && *(buf + *off) <= '9') {
++			hexdigit = *(buf + *off) - '0';
++		} else if(*(buf + *off) >= 'a' && *(buf + *off) <= 'f') {
++			hexdigit = *(buf + *off) - 'a' + 10;
++		} else if(*(buf + *off) >= 'A' && *(buf + *off) <= 'F') {
++			hexdigit = *(buf + *off) - 'A' + 10;
++		} else {
++			break;
++		}
++		*hex  = (*hex << 4) + hexdigit;
++		*off += 1;
++	}
++
++	return (size_t) (*off - start);
++}
++
++static ssize_t tape_proc_write(
++	struct file *file,
++	const char  *buf,
++	size_t       len,
++	loff_t      *off
++) {
++	loff_t  start = *off;
++	int     devno;
++	char   *s;
++
++	if(PAGE_SIZE < len)
++		return -EINVAL;
++
++	if((s = kmalloc(len, GFP_KERNEL)) == NULL)
++		return -ENOMEM;
++
++	if(copy_from_user(s, buf, len) != 0) {
++		kfree(s);
++		return -EFAULT;
++	}
++
++	if(strncmp(s+*off, "assign", 6) == 0) {
++		(*off) += 6;
++		tape_proc_get_hexvalue(s, len - 6, off, &devno);
++		if(devno > 0)
++			tape_proc_assign(devno);
++	} else if(strncmp(s+*off, "unassign", 8) == 0) {
++		(*off) += 8;
++		tape_proc_get_hexvalue(s, len - (*off - start), off, &devno);
++		if(devno > 0)
++			tape_proc_unassign(devno);
++#ifdef TAPE390_FORCE_UNASSIGN
++	} else if(strncmp(s+*off, "forceunassign", 13) == 0) {
++		(*off) += 13;
++		tape_proc_get_hexvalue(s, len - (*off - start), off, &devno);
++		if(devno > 0)
++			tape_proc_force_unassign(devno);
++#endif
++#ifdef SMB_DEBUG_BOX
++	} else if(strncmp(s+*off, "putintobox", 10) == 0) {
++		(*off) += 10;
++		tape_proc_get_hexvalue(s, len - (*off - start), off, &devno);
++		if(devno > 0)
++			tape_proc_put_into_box(devno);
++#endif
++	} else {
++		DBF_EVENT(3, "tape_proc_write() parse error\n");
++		PRINT_ERR("Invalid /proc/tapedevices command.\n");
++	}
++	tape_proc_skip_eol(s, len - (*off - start), off);
++
++	kfree(s);
++
++	/* Just pretend to have processed all the stuff */
++	return len;
++}
++
++static struct file_operations tape_proc_ops =
++{
++	.open    = tape_proc_open,
++	.read    = seq_read,
++	.write   = tape_proc_write,
++	.llseek  = seq_lseek,
++	.release = seq_release,
++};
++
++/* 
++ * Initialize procfs stuff on startup
++ */
++void tape_proc_init(void) {
++	tape_proc_devices = create_proc_entry(
++		"tapedevices", S_IFREG | S_IRUGO | S_IWUSR, &proc_root);
++
++	if (tape_proc_devices == NULL) {
++		PRINT_WARN("tape: Cannot register procfs entry tapedevices\n");
++		return;
++	}
++	tape_proc_devices->proc_fops = &tape_proc_ops;
++	tape_proc_devices->owner     = THIS_MODULE;
++}
++
++/*
++ * Cleanup all stuff registered to the procfs
++ */
++void tape_proc_cleanup(void) {
++	if(tape_proc_devices != NULL)
++		remove_proc_entry ("tapedevices", &proc_root);
++}
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape_std.c drivers/s390/char/tape_std.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape_std.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/tape_std.c	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,779 @@
++/*
++ *  drivers/s390/char/tape_std.c
++ *    standard tape device functions for ibm tapes.
++ *
++ *  S390 and zSeries version
++ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Carsten Otte <cotte at de.ibm.com>
++ *		 Michael Holzheu <holzheu at de.ibm.com>
++ *		 Tuan Ngo-Anh <ngoanh at de.ibm.com>
++ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
++ *               Stefan Bader <shbader at de.ibm.com>
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/stddef.h>
++#include <linux/kernel.h>
++#include <linux/timer.h>
++#ifdef CONFIG_S390_TAPE_BLOCK
++#include <linux/blkdev.h>
++#endif
++
++#include <asm/types.h>
++#include <asm/idals.h>
++#include <asm/ebcdic.h>
++#include <asm/tape390.h>
++
++#define TAPE_DBF_AREA	tape_core_dbf
++
++#include "tape.h"
++#include "tape_std.h"
++
++#define PRINTK_HEADER "T3xxx:"
++#define ZLINUX_PASSWD "zLinux PWD"
++
++/*
++ * tape_std_assign
++ */
++int
++tape_std_assign(struct tape_device *device)
++{
++	struct tape_request *request;
++
++	request = tape_alloc_request(2, 11);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++
++	request->op = TO_ASSIGN;
++
++	/*
++	 * From the documentation assign requests should fail with the
++	 * 'assigned elsewhere' bit set if the tape is already assigned
++	 * to another host. However, it seems, in reality the request
++	 * hangs forever. Therfor we just set a timeout for this request.
++	 */
++	init_timer(&request->timeout);
++	request->timeout.expires = jiffies + 2 * HZ;
++
++	/* Setup the CCWs */
++	tape_ccw_cc(request->cpaddr, ASSIGN, 11, request->cpdata);
++	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
++
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * tape_std_unassign
++ */
++int
++tape_std_unassign (struct tape_device *device)
++{
++	struct tape_request *request;
++
++	request = tape_alloc_request(2, 11);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_UNASSIGN;
++	tape_ccw_cc(request->cpaddr, UNASSIGN, 11, request->cpdata);
++	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
++	return tape_do_io_free(device, request);
++}
++
++#ifdef TAPE390_FORCE_UNASSIGN
++/*
++ * tape_std_force_unassign: forces assignment from another host.
++ * (Since we need a password this works only with other zLinux hosts!)
++ */
++int
++tape_std_force_unassign(struct tape_device *device)
++{
++	struct tape_request *request;
++	struct tape_ca_data *ca_data1;
++	struct tape_ca_data *ca_data2;
++
++	request = tape_alloc_request(2, 24);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++
++	request->op = TO_BREAKASS;
++	ca_data1 = (struct tape_ca_data *)
++			(((char *) request->cpdata));
++	ca_data2 = (struct tape_ca_data *)
++			(((char *) request->cpdata) + 12);
++
++	ca_data1->function = 0x80; /* Conditional enable */
++	strcpy(ca_data1->password, ZLINUX_PASSWD);
++	ASCEBC(ca_data1->password, 11);
++	ca_data2->function = 0x40; /* Conditional disable */
++	memcpy(ca_data2->password, ca_data1->password, 11);
++	
++	tape_ccw_cc(request->cpaddr, CONTROL_ACCESS, 12, ca_data1);
++	tape_ccw_end(request->cpaddr + 1, CONTROL_ACCESS, 12, ca_data2);
++
++	return tape_do_io_free(device, request);
++}
++#endif
++
++/*
++ * TAPE390_DISPLAY: Show a string on the tape display.
++ */
++int
++tape_std_display(struct tape_device *device, struct display_struct *disp)
++{
++	struct tape_request *request;
++	int rc;
++
++	request = tape_alloc_request(2, 17);
++	if (IS_ERR(request)) {
++		DBF_EVENT(3, "TAPE: load display failed\n");
++		return PTR_ERR(request);
++	}
++
++	request->op = TO_DIS;
++	*(unsigned char *) request->cpdata = disp->cntrl;
++	DBF_EVENT(5, "TAPE: display cntrl=%04x\n", disp->cntrl);
++	memcpy(((unsigned char *) request->cpdata) + 1, disp->message1, 8);
++	memcpy(((unsigned char *) request->cpdata) + 9, disp->message2, 8);
++	ASCEBC(((unsigned char*) request->cpdata) + 1, 16);
++
++	tape_ccw_cc(request->cpaddr, LOAD_DISPLAY, 17, request->cpdata);
++	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
++
++	rc = tape_do_io_interruptible(device, request);
++	tape_put_request(request);
++	return rc;
++}
++
++/*
++ * Read block id.
++ */
++int
++tape_std_read_block_id(struct tape_device *device, unsigned int *bid)
++{
++	struct tape_request *request;
++	struct {
++		unsigned int	channel_block_id;
++		unsigned int	device_block_id;
++	} __attribute__ ((packed)) *rbi_data;
++	int rc;
++
++	request = tape_alloc_request(3, 8);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_RBI;
++
++	/* setup ccws */
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_cc(request->cpaddr + 1, READ_BLOCK_ID, 8, request->cpdata);
++	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
++
++	/* execute it */
++	rc = tape_do_io(device, request);
++	if (rc == 0) {
++		/* Get result from read buffer. */
++		DBF_EVENT(6, "rbi_data = 0x%08x%08x\n",
++			*((unsigned int *) request->cpdata),
++			*(((unsigned int *) request->cpdata)+1));
++		rbi_data = (void *) request->cpdata;
++		*bid = rbi_data->channel_block_id;
++	}
++	tape_put_request(request);
++	return rc;
++}
++
++/* Seek block id */
++int
++tape_std_seek_block_id(struct tape_device *device, unsigned int bid)
++{
++	struct tape_request	*request;
++
++	request = tape_alloc_request(3, 4);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++
++	request->op                = TO_LBL;
++	*(__u32 *) request->cpdata = bid;
++
++	/* setup ccws */
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
++	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
++
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++int
++tape_std_terminate_write(struct tape_device *device)
++{
++	int rc;
++
++	if(device->required_tapemarks == 0)
++		return 0;
++
++	DBF_EVENT(5, "(%04x): terminate_write %ixEOF\n",
++		device->devstat.devno, device->required_tapemarks);
++
++	rc = tape_mtop(device, MTWEOF, device->required_tapemarks);
++	if (rc)
++		return rc;
++
++	device->required_tapemarks = 0;
++	return tape_mtop(device, MTBSR, 1);
++}
++
++/*
++ * MTLOAD: Loads the tape.
++ * The default implementation just wait until the tape medium state changes
++ * to MS_LOADED.
++ */
++int
++tape_std_mtload(struct tape_device *device, int count)
++{
++	return wait_event_interruptible(device->state_change_wq,
++		(device->medium_state == MS_LOADED));
++}
++
++/*
++ * MTSETBLK: Set block size.
++ */
++int
++tape_std_mtsetblk(struct tape_device *device, int count)
++{
++	struct idal_buffer *new;
++
++	DBF_EVENT(6, "tape_std_mtsetblk(%d)\n", count);
++	if (count <= 0) {
++		/*
++		 * Just set block_size to 0. tapechar_read/tapechar_write
++		 * will realloc the idal buffer if a bigger one than the
++		 * current is needed.
++		 */
++		device->char_data.block_size = 0;
++		return 0;
++	}
++	if (device->char_data.idal_buf != NULL &&
++	    device->char_data.idal_buf->size == count)
++		/* We already have a idal buffer of that size. */
++		return 0;
++
++	if (count > MAX_BLOCKSIZE) {
++		DBF_EVENT(3, "Invalid block size (%ld > %ld) given.\n",
++			count, MAX_BLOCKSIZE);
++		PRINT_ERR("Invalid block size (%ld > %ld) given.\n",
++			count, MAX_BLOCKSIZE);
++		return -EINVAL;
++	}
++
++	/* Allocate a new idal buffer. */
++	new = idal_buffer_alloc(count, 0);
++	if (new == NULL)
++		return -ENOMEM;
++	if (device->char_data.idal_buf != NULL)
++		idal_buffer_free(device->char_data.idal_buf);
++
++	device->char_data.idal_buf = new;
++	device->char_data.block_size = count;
++	DBF_EVENT(6, "new blocksize is %d\n", device->char_data.block_size);
++	return 0;
++}
++
++/*
++ * MTRESET: Set block size to 0.
++ */
++int
++tape_std_mtreset(struct tape_device *device, int count)
++{
++	DBF_EVENT(6, "TCHAR:devreset:\n");
++	device->char_data.block_size = 0;
++	return 0;
++}
++
++/*
++ * MTFSF: Forward space over 'count' file marks. The tape is positioned
++ * at the EOT (End of Tape) side of the file mark.
++ */
++int
++tape_std_mtfsf(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++	ccw1_t *ccw;
++
++	request = tape_alloc_request(mt_count + 2, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_FSF;
++	/* setup ccws */
++	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
++			  device->modeset_byte);
++	ccw = tape_ccw_repeat(ccw, FORSPACEFILE, mt_count);
++	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * MTFSR: Forward space over 'count' tape blocks (blocksize is set
++ * via MTSETBLK.
++ */
++int
++tape_std_mtfsr(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++	ccw1_t *ccw;
++
++	request = tape_alloc_request(mt_count + 2, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_FSB;
++	/* setup ccws */
++	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
++			  device->modeset_byte);
++	ccw = tape_ccw_repeat(ccw, FORSPACEBLOCK, mt_count);
++	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * MTBSR: Backward space over 'count' tape blocks.
++ * (blocksize is set via MTSETBLK.
++ */
++int
++tape_std_mtbsr(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++	ccw1_t *ccw;
++
++	request = tape_alloc_request(mt_count + 2, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_BSB;
++	/* setup ccws */
++	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
++			  device->modeset_byte);
++	ccw = tape_ccw_repeat(ccw, BACKSPACEBLOCK, mt_count);
++	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * MTWEOF: Write 'count' file marks at the current position.
++ */
++int
++tape_std_mtweof(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++	ccw1_t *ccw;
++
++	request = tape_alloc_request(mt_count + 2, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_WTM;
++	/* setup ccws */
++	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
++			  device->modeset_byte);
++	ccw = tape_ccw_repeat(ccw, WRITETAPEMARK, mt_count);
++	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * MTBSFM: Backward space over 'count' file marks.
++ * The tape is positioned at the BOT (Begin Of Tape) side of the
++ * last skipped file mark.
++ */
++int
++tape_std_mtbsfm(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++	ccw1_t *ccw;
++
++	request = tape_alloc_request(mt_count + 2, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_BSF;
++	/* setup ccws */
++	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
++			  device->modeset_byte);
++	ccw = tape_ccw_repeat(ccw, BACKSPACEFILE, mt_count);
++	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * MTBSF: Backward space over 'count' file marks. The tape is positioned at
++ * the EOT (End of Tape) side of the last skipped file mark.
++ */
++int
++tape_std_mtbsf(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++	ccw1_t *ccw;
++	int rc;
++	
++	request = tape_alloc_request(mt_count + 2, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_BSF;
++	/* setup ccws */
++	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
++			  device->modeset_byte);
++	ccw = tape_ccw_repeat(ccw, BACKSPACEFILE, mt_count);
++	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
++	/* execute it */
++	rc = tape_do_io(device, request);
++	if (rc == 0) {
++		request->op = TO_FSF;
++		/* need to skip forward over the filemark. */
++		tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1,
++			    device->modeset_byte);
++		tape_ccw_cc(request->cpaddr + 1, FORSPACEFILE, 0, NULL);
++		tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
++		/* execute it */
++		rc = tape_do_io(device, request);
++	}
++	tape_put_request(request);
++	return rc;
++}
++
++/*
++ * MTFSFM: Forward space over 'count' file marks.
++ * The tape is positioned at the BOT (Begin Of Tape) side
++ * of the last skipped file mark.
++ */
++int
++tape_std_mtfsfm(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++	ccw1_t *ccw;
++	int rc;
++
++	request = tape_alloc_request(mt_count + 2, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_FSF;
++	/* setup ccws */
++	ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, 
++			  device->modeset_byte);
++	ccw = tape_ccw_repeat(ccw, FORSPACEFILE, mt_count);
++	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
++	/* execute it */
++	rc = tape_do_io(device, request);
++	if (rc == 0) {
++		request->op = TO_BSF;
++		/* need to skip forward over the filemark. */
++		tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, 
++			    device->modeset_byte);
++		tape_ccw_cc(request->cpaddr + 1, BACKSPACEFILE, 0, NULL);
++		tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
++		/* execute it */
++		rc = tape_do_io(device, request);
++	}
++	tape_put_request(request);
++	return rc;
++}
++
++/*
++ * MTREW: Rewind the tape.
++ */
++int
++tape_std_mtrew(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++
++	request = tape_alloc_request(3, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_REW;
++	/* setup ccws */
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, 
++		    device->modeset_byte);
++	tape_ccw_cc(request->cpaddr + 1, REWIND, 0, NULL);
++	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * MTOFFL: Rewind the tape and put the drive off-line.
++ * Implement 'rewind unload'
++ */
++int
++tape_std_mtoffl(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++
++	request = tape_alloc_request(3, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_RUN;
++	/* setup ccws */
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_cc(request->cpaddr + 1, REWIND_UNLOAD, 0, NULL);
++	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * MTNOP: 'No operation'.
++ */
++int
++tape_std_mtnop(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++
++	request = tape_alloc_request(2, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_NOP;
++	/* setup ccws */
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * MTEOM: positions at the end of the portion of the tape already used
++ * for recordind data. MTEOM positions after the last file mark, ready for
++ * appending another file.
++ */
++int
++tape_std_mteom(struct tape_device *device, int mt_count)
++{
++	int                  rc;
++
++	/*
++	 * Since there is currently no other way to seek, return to the
++	 * BOT and start from there.
++	 */
++	if((rc = tape_mtop(device, MTREW, 1)) < 0)
++		return rc;
++
++	do {
++		if((rc = tape_mtop(device, MTFSF, 1)) < 0)
++			return rc;
++		if((rc = tape_mtop(device, MTFSR, 1)) < 0)
++			return rc;
++	} while((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) == 0);
++
++	return tape_mtop(device, MTBSR, 1);
++}
++
++/*
++ * MTRETEN: Retension the tape, i.e. forward space to end of tape and rewind.
++ */
++int
++tape_std_mtreten(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++	int rc;
++
++	request = tape_alloc_request(4, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_FSF;
++	/* setup ccws */
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_cc(request->cpaddr + 1,FORSPACEFILE, 0, NULL);
++	tape_ccw_cc(request->cpaddr + 2, NOP, 0, NULL);
++	tape_ccw_end(request->cpaddr + 3, CCW_CMD_TIC, 0, request->cpaddr);
++	/* execute it, MTRETEN rc gets ignored */
++	rc = tape_do_io_interruptible(device, request);
++	tape_put_request(request);
++	return tape_std_mtrew(device, 1);
++}
++
++/*
++ * MTERASE: erases the tape.
++ */
++int
++tape_std_mterase(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++
++	request = tape_alloc_request(5, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_DSE;
++	/* setup ccws */
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_cc(request->cpaddr + 1, REWIND, 0, NULL);
++	tape_ccw_cc(request->cpaddr + 2, ERASE_GAP, 0, NULL);
++	tape_ccw_cc(request->cpaddr + 3, DATA_SEC_ERASE, 0, NULL);
++	tape_ccw_end(request->cpaddr + 4, NOP, 0, NULL);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * MTUNLOAD: Rewind the tape and unload it.
++ */
++int
++tape_std_mtunload(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++
++	request = tape_alloc_request(3, 32);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_RUN;
++	/* setup ccws */
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_cc(request->cpaddr + 1, REWIND_UNLOAD, 0, NULL);
++	tape_ccw_end(request->cpaddr + 2, SENSE, 32, request->cpdata);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * MTCOMPRESSION: used to enable compression.
++ * Sets the IDRC on/off.
++ */
++int
++tape_std_mtcompression(struct tape_device *device, int mt_count)
++{
++	struct tape_request *request;
++
++	if (mt_count < 0 || mt_count > 1) {
++		DBF_EXCEPTION(6, "xcom parm\n");
++		if (*device->modeset_byte & 0x08)
++			PRINT_INFO("(%x) Compression is currently on\n",
++				   device->devstat.devno);
++		else
++			PRINT_INFO("(%x) Compression is currently off\n",
++				   device->devstat.devno);
++		PRINT_INFO("Use 1 to switch compression on, 0 to "
++			   "switch it off\n");
++		return -EINVAL;
++	}
++	request = tape_alloc_request(2, 0);
++	if (IS_ERR(request))
++		return PTR_ERR(request);
++	request->op = TO_NOP;
++	/* setup ccws */
++	*device->modeset_byte = (mt_count == 0) ? 0x00 : 0x08;
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL);
++	/* execute it */
++	return tape_do_io_free(device, request);
++}
++
++/*
++ * Read Block
++ */
++struct tape_request *
++tape_std_read_block(struct tape_device *device, size_t count)
++{
++	struct tape_request *request;
++
++	/*
++	 * We have to alloc 4 ccws in order to be able to transform request
++	 * into a read backward request in error case.
++	 */
++	request = tape_alloc_request(4, 0);
++	if (IS_ERR(request)) {
++		DBF_EXCEPTION(6, "xrbl fail");
++		return request;
++	}
++	request->op = TO_RFO;
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_end_idal(request->cpaddr + 1, READ_FORWARD,
++			  device->char_data.idal_buf);
++	DBF_EVENT(6, "xrbl ccwg\n");
++	return request;
++}
++
++/*
++ * Read Block backward transformation function.
++ */
++void
++tape_std_read_backward(struct tape_device *device, struct tape_request *request)
++{
++	/*
++	 * We have allocated 4 ccws in tape_std_read, so we can now
++	 * transform the request to a read backward, followed by a
++	 * forward space block.
++	 */
++	request->op = TO_RBA;
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_cc_idal(request->cpaddr + 1, READ_BACKWARD,
++			 device->char_data.idal_buf);
++	tape_ccw_cc(request->cpaddr + 2, FORSPACEBLOCK, 0, NULL);
++	tape_ccw_end(request->cpaddr + 3, NOP, 0, NULL);
++	DBF_EVENT(6, "xrop ccwg");}
++
++/*
++ * Write Block
++ */
++struct tape_request *
++tape_std_write_block(struct tape_device *device, size_t count)
++{
++	struct tape_request *request;
++
++	request = tape_alloc_request(2, 0);
++	if (IS_ERR(request)) {
++		DBF_EXCEPTION(6, "xwbl fail\n");
++		return request;
++	}
++	request->op = TO_WRI;
++	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
++	tape_ccw_end_idal(request->cpaddr + 1, WRITE_CMD,
++			  device->char_data.idal_buf);
++	DBF_EVENT(6, "xwbl ccwg\n");
++	return request;
++}
++
++/*
++ * This routine is called by frontend after an ENOSP on write
++ */
++void
++tape_std_process_eov(struct tape_device *device)
++{
++	/*
++	 * End of volume: We have to backspace the last written record, then
++	 * we TRY to write a tapemark and then backspace over the written TM
++	 */
++	if (tape_mtop(device, MTBSR, 1) < 0)
++		return;
++	if (tape_mtop(device, MTWEOF, 1) < 0)
++		return;
++	tape_mtop(device, MTBSR, 1);
++}
++
++EXPORT_SYMBOL(tape_std_assign);
++EXPORT_SYMBOL(tape_std_unassign);
++#ifdef TAPE390_FORCE_UNASSIGN
++EXPORT_SYMBOL(tape_std_force_unassign);
++#endif
++EXPORT_SYMBOL(tape_std_display);
++EXPORT_SYMBOL(tape_std_read_block_id);
++EXPORT_SYMBOL(tape_std_seek_block_id);
++EXPORT_SYMBOL(tape_std_mtload);
++EXPORT_SYMBOL(tape_std_mtsetblk);
++EXPORT_SYMBOL(tape_std_mtreset);
++EXPORT_SYMBOL(tape_std_mtfsf);
++EXPORT_SYMBOL(tape_std_mtfsr);
++EXPORT_SYMBOL(tape_std_mtbsr);
++EXPORT_SYMBOL(tape_std_mtweof);
++EXPORT_SYMBOL(tape_std_mtbsfm);
++EXPORT_SYMBOL(tape_std_mtbsf);
++EXPORT_SYMBOL(tape_std_mtfsfm);
++EXPORT_SYMBOL(tape_std_mtrew);
++EXPORT_SYMBOL(tape_std_mtoffl);
++EXPORT_SYMBOL(tape_std_mtnop);
++EXPORT_SYMBOL(tape_std_mteom);
++EXPORT_SYMBOL(tape_std_mtreten);
++EXPORT_SYMBOL(tape_std_mterase);
++EXPORT_SYMBOL(tape_std_mtunload);
++EXPORT_SYMBOL(tape_std_mtcompression);
++EXPORT_SYMBOL(tape_std_read_block);
++EXPORT_SYMBOL(tape_std_read_backward);
++EXPORT_SYMBOL(tape_std_write_block);
++EXPORT_SYMBOL(tape_std_process_eov);
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tape_std.h drivers/s390/char/tape_std.h
+--- kernel-source-2.4.27.orig/drivers/s390/char/tape_std.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/tape_std.h	2006-02-12 12:47:23.000000000 -0700
+@@ -0,0 +1,161 @@
++/*
++ *  drivers/s390/char/tape_std.h
++ *    standard tape device functions for ibm tapes.
++ *
++ *  S390 and zSeries version
++ *    Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Carsten Otte <cotte at de.ibm.com>
++ *               Tuan Ngo-Anh <ngoanh at de.ibm.com>
++ *               Martin Schwidefsky <schwidefsky at de.ibm.com>
++ *               Stefan Bader <shbader at de.ibm.com>
++ */
++
++#ifndef _TAPE_STD_H
++#define _TAPE_STD_H
++
++#include <asm/tape390.h>
++
++/*
++ * Biggest block size to handle. Currently 64K because we only build
++ * channel programs without data chaining.
++ */
++#define MAX_BLOCKSIZE	65535
++
++/*
++ * The CCW commands for the Tape type of command.
++ */
++#define INVALID_00		0x00	/* Invalid cmd */
++#define BACKSPACEBLOCK		0x27	/* Back Space block */
++#define BACKSPACEFILE		0x2f	/* Back Space file */
++#define DATA_SEC_ERASE		0x97	/* Data security erase */
++#define ERASE_GAP		0x17	/* Erase Gap */
++#define FORSPACEBLOCK		0x37	/* Forward space block */
++#define FORSPACEFILE		0x3F	/* Forward Space file */
++#define FORCE_STREAM_CNT	0xEB	/* Forced streaming count # */
++#define NOP			0x03	/* No operation	*/
++#define READ_FORWARD		0x02	/* Read forward */
++#define REWIND			0x07	/* Rewind */
++#define REWIND_UNLOAD		0x0F	/* Rewind and Unload */
++#define SENSE			0x04	/* Sense */
++#define NEW_MODE_SET		0xEB	/* Guess it is Mode set */
++#define WRITE_CMD		0x01	/* Write */
++#define WRITETAPEMARK		0x1F	/* Write Tape Mark */
++
++#define ASSIGN			0xB7	/* 3420 REJECT,3480 OK	*/
++#define CONTROL_ACCESS		0xE3	/* Set high speed */
++#define DIAG_MODE_SET		0x0B	/* 3420 NOP, 3480 REJECT */
++#define LOAD_DISPLAY		0x9F	/* 3420 REJECT,3480 OK */
++#define LOCATE			0x4F	/* 3420 REJ, 3480 NOP */
++#define LOOP_WRITE_TO_READ	0x8B	/* 3480 REJECT */
++#define MODE_SET_DB		0xDB	/* 3420 REJECT,3480 OK */
++#define MODE_SET_C3		0xC3	/* for 3420 */
++#define MODE_SET_CB		0xCB	/* for 3420 */
++#define MODE_SET_D3		0xD3	/* for 3420 */
++#define READ_BACKWARD		0x0C	/* */
++#define READ_BLOCK_ID		0x22	/* 3420 REJECT,3480 OK */
++#define READ_BUFFER		0x12	/* 3420 REJECT,3480 OK */
++#define READ_BUFF_LOG		0x24	/* 3420 REJECT,3480 OK */
++#define RELEASE			0xD4	/* 3420 NOP, 3480 REJECT */
++#define REQ_TRK_IN_ERROR	0x1B	/* 3420 NOP, 3480 REJECT */
++#define RESERVE			0xF4	/* 3420 NOP, 3480 REJECT */
++#define SENSE_GROUP_ID		0x34	/* 3420 REJECT,3480 OK */
++#define SENSE_ID		0xE4	/* 3420 REJECT,3480 OK */
++#define READ_DEV_CHAR		0x64	/* Read device characteristics */
++#define SET_DIAGNOSE		0x4B	/* 3420 NOP, 3480 REJECT */
++#define SET_GROUP_ID		0xAF	/* 3420 REJECT,3480 OK */
++#define SET_TAPE_WRITE_IMMED	0xC3	/* for 3480 */
++#define SUSPEND			0x5B	/* 3420 REJ, 3480 NOP */
++#define SYNC			0x43	/* Synchronize (flush buffer) */
++#define UNASSIGN		0xC7	/* 3420 REJECT,3480 OK */
++#define PERF_SUBSYS_FUNC	0x77	/* 3490 CMD */
++#define READ_CONFIG_DATA	0xFA	/* 3490 CMD */
++#define READ_MESSAGE_ID		0x4E	/* 3490 CMD */
++#define READ_SUBSYS_DATA	0x3E	/* 3490 CMD */
++#define SET_INTERFACE_ID	0x73	/* 3490 CMD */
++
++#define SENSE_COMMAND_REJECT		0x80
++#define SENSE_INTERVENTION_REQUIRED	0x40
++#define SENSE_BUS_OUT_CHECK		0x20
++#define SENSE_EQUIPMENT_CHECK		0x10
++#define SENSE_DATA_CHECK		0x08
++#define SENSE_OVERRUN			0x04
++#define SENSE_DEFERRED_UNIT_CHECK	0x02
++#define SENSE_ASSIGNED_ELSEWHERE	0x01
++
++#define SENSE_LOCATE_FAILURE		0x80
++#define SENSE_DRIVE_ONLINE		0x40
++#define SENSE_RESERVED			0x20
++#define SENSE_RECORD_SEQUENCE_ERR	0x10
++#define SENSE_BEGINNING_OF_TAPE		0x08
++#define SENSE_WRITE_MODE		0x04
++#define SENSE_WRITE_PROTECT		0x02
++#define SENSE_NOT_CAPABLE		0x01
++
++#define SENSE_CHANNEL_ADAPTER_CODE	0xE0
++#define SENSE_CHANNEL_ADAPTER_LOC	0x10
++#define SENSE_REPORTING_CU		0x08
++#define SENSE_AUTOMATIC_LOADER		0x04
++#define SENSE_TAPE_SYNC_MODE		0x02
++#define SENSE_TAPE_POSITIONING		0x01
++
++/* Data structure for the CONTROL_ACCESS call */
++struct tape_ca_data {
++	unsigned char	function;
++	char		password[11];
++} __attribute__ ((packed));
++
++/* discipline functions */
++struct tape_request *tape_std_read_block(struct tape_device *, size_t);
++void tape_std_read_backward(struct tape_device *device,
++			    struct tape_request *request);
++struct tape_request *tape_std_write_block(struct tape_device *, size_t);
++struct tape_request *tape_std_bread(struct tape_device *, struct request *);
++void tape_std_free_bread(struct tape_request *);
++void tape_std_check_locate(struct tape_device *, struct tape_request *);
++struct tape_request *tape_std_bwrite(struct request *,
++				     struct tape_device *, int);
++
++/* Some non-mtop commands. */
++int tape_std_assign(struct tape_device *);
++int tape_std_unassign(struct tape_device *);
++int tape_std_force_unassign(struct tape_device *);
++int tape_std_read_block_id(struct tape_device *, unsigned int *);
++int tape_std_seek_block_id(struct tape_device *, unsigned int);
++int tape_std_display(struct tape_device *, struct display_struct *);
++int tape_std_terminate_write(struct tape_device *);
++
++/* Standard magnetic tape commands. */
++int tape_std_mtbsf(struct tape_device *, int);
++int tape_std_mtbsfm(struct tape_device *, int);
++int tape_std_mtbsr(struct tape_device *, int);
++int tape_std_mtcompression(struct tape_device *, int);
++int tape_std_mteom(struct tape_device *, int);
++int tape_std_mterase(struct tape_device *, int);
++int tape_std_mtfsf(struct tape_device *, int);
++int tape_std_mtfsfm(struct tape_device *, int);
++int tape_std_mtfsr(struct tape_device *, int);
++int tape_std_mtload(struct tape_device *, int);
++int tape_std_mtnop(struct tape_device *, int);
++int tape_std_mtoffl(struct tape_device *, int);
++int tape_std_mtreset(struct tape_device *, int);
++int tape_std_mtreten(struct tape_device *, int);
++int tape_std_mtrew(struct tape_device *, int);
++int tape_std_mtsetblk(struct tape_device *, int);
++int tape_std_mtunload(struct tape_device *, int);
++int tape_std_mtweof(struct tape_device *, int);
++
++/* Event handlers */
++void tape_std_default_handler(struct tape_device *);
++void tape_std_unexpect_uchk_handler(struct tape_device *);
++void tape_std_irq(struct tape_device *);
++void tape_std_process_eov(struct tape_device *);
++
++// the error recovery stuff:
++void tape_std_error_recovery(struct tape_device *);
++void tape_std_error_recovery_has_failed(struct tape_device *,int error_id);
++void tape_std_error_recovery_succeded(struct tape_device *);
++void tape_std_error_recovery_do_retry(struct tape_device *);
++void tape_std_error_recovery_read_opposite(struct tape_device *);
++void tape_std_error_recovery_HWBUG(struct tape_device *, int condno);
++
++#endif // _TAPE_STD_H
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/tuball.c drivers/s390/char/tuball.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/tuball.c	2003-06-13 08:51:36.000000000 -0600
++++ drivers/s390/char/tuball.c	2006-02-12 12:47:23.000000000 -0700
 @@ -29,9 +29,7 @@
  MODULE_PARM(tubdebug, "i");
  MODULE_PARM(tubscrolltime, "i");
@@ -29824,9 +29677,9 @@
  #endif
  /*
   * Values for tubdebug and their effects:
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/vmlogrdr.c drivers/s390/char/vmlogrdr.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/vmlogrdr.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/char/vmlogrdr.c	2006-01-30 22:25:26.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/char/vmlogrdr.c drivers/s390/char/vmlogrdr.c
+--- kernel-source-2.4.27.orig/drivers/s390/char/vmlogrdr.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/char/vmlogrdr.c	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,741 @@
 +/*
 + * drivers/s390/char/vmlogrdr.c
@@ -30569,9 +30422,9 @@
 +
 +
 +
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/cmf.c drivers/s390/cmf.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/cmf.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/cmf.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/cmf.c drivers/s390/cmf.c
+--- kernel-source-2.4.27.orig/drivers/s390/cmf.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/cmf.c	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,701 @@
 +/*
 + * linux/drivers/s390/cmf.c ($Revision: 1.5.6.2 $)
@@ -31274,29 +31127,123 @@
 +EXPORT_SYMBOL_GPL(set_cmf);
 +EXPORT_SYMBOL_GPL(cmf_readall);
 +EXPORT_SYMBOL_GPL(cmf_reset);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/Makefile drivers/s390/misc/Makefile
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/Makefile	2001-04-11 20:02:28.000000000 -0600
-+++ drivers/s390/misc/Makefile	2006-01-30 22:25:25.000000000 -0700
-@@ -4,7 +4,15 @@
+diff -urN kernel-source-2.4.27.orig/drivers/s390/Config.in drivers/s390/Config.in
+--- kernel-source-2.4.27.orig/drivers/s390/Config.in	2003-08-25 05:44:42.000000000 -0600
++++ drivers/s390/Config.in	2006-02-12 12:47:23.000000000 -0700
+@@ -9,6 +9,7 @@
+ fi
+ dep_bool '   Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM
+ tristate 'XPRAM disk support' CONFIG_BLK_DEV_XPRAM
++tristate 'z/VM discontiguos saved segments (DCSS) block device driver' CONFIG_DCSSBLK
  
- O_TARGET := s390-misc.o
+ comment 'S/390 block device drivers'
  
--obj-$(CONFIG_CHANDEV) += chandev.o
- export-objs += chandev.o
+@@ -29,6 +30,7 @@
+       bool     '   Automatic activation of DIAG module' CONFIG_DASD_AUTO_DIAG
+     fi
+   fi
++  dep_tristate '  Support for Channel Measurement on DASD devices' CONFIG_S390_CMF $CONFIG_DASD
+ fi
  
-+list-multi := z90crypt.o
-+z90crypt_mod-objs := z90main.o z90hardware.o
-+obj-$(CONFIG_Z90CRYPT) += z90crypt.o
+ endmenu
+@@ -52,20 +54,26 @@
+ if [ "$CONFIG_TN3215" = "y" ]; then
+   bool 'Support for console on 3215 line mode terminal' CONFIG_TN3215_CONSOLE
+ fi
+-bool 'Support for HWC line mode terminal' CONFIG_HWC
+-if [ "$CONFIG_HWC" = "y" ]; then
+-  bool '   console on HWC line mode terminal' CONFIG_HWC_CONSOLE
+-  tristate '   Control-Program Identification' CONFIG_HWC_CPI
++bool 'Support for SCLP' CONFIG_SCLP
++if [ "$CONFIG_SCLP" = "y" ]; then
++  bool '   Support for SCLP line mode terminal' CONFIG_SCLP_TTY
++  if [ "$CONFIG_SCLP_TTY" = "y" ]; then
++    bool '   Support for console on SCLP line mode terminal' CONFIG_SCLP_CONSOLE
++  fi
++  bool '   Support for SCLP VT220-compatible terminal' CONFIG_SCLP_VT220_TTY
++  if [ "$CONFIG_SCLP_VT220_TTY" = "y" ]; then
++    bool '   Support for console on SCLP VT220-compatible terminal' CONFIG_SCLP_VT220_CONSOLE
++  fi
++  tristate '   Control-Program Identification' CONFIG_SCLP_CPI
+ fi
+ tristate 'S/390 tape device support' CONFIG_S390_TAPE
+ if [ "$CONFIG_S390_TAPE" != "n" ]; then
+   comment 'S/390 tape interface support'
+-  bool '   Support for tape character devices' CONFIG_S390_TAPE_CHAR
+   bool '   Support for tape block devices' CONFIG_S390_TAPE_BLOCK
+   comment 'S/390 tape hardware support'
+-  bool '   Support for 3490 tape hardware' CONFIG_S390_TAPE_3490
+-  bool '   Support for 3480 tape hardware' CONFIG_S390_TAPE_3480
++  dep_tristate '   Support for 3480/3490 tape hardware' CONFIG_S390_TAPE_34XX $CONFIG_S390_TAPE
+ fi
++dep_tristate 'Support for the z/VM recording system services (VM only)' CONFIG_VMLOGRDR $CONFIG_IUCV
+ endmenu
+ 
+ if [ "$CONFIG_NET" = "y" ]; then
+@@ -88,9 +96,38 @@
+ 	define_bool CONFIG_HOTPLUG y
+     fi
+ 
++    if [ "$CONFIG_NET_ETHERNET" != "n" -o "$CONFIG_TR" != "n" ]; then
++      tristate 'Lan Channel Station Interface' CONFIG_LCS
++    fi
 +
-+obj-$(CONFIG_CHANDEV) += chandev.o
++    if [ "$CONFIG_QDIO" != "n" -a "$CONFIG_CHANDEV" = "y" -a "$CONFIG_IP_MULTICAST" = "y" ]; then
++      dep_tristate 'Support for Gigabit Ethernet' CONFIG_QETH $CONFIG_QDIO
++      if [ "$CONFIG_QETH" != "n" ]; then
++        comment 'Gigabit Ethernet default settings'
++	if [ "$CONFIG_IPV6" = "y" -o "$CONFIG_IPV6" = "$CONFIG_QETH" ]; then
++		bool '   IPv6 support for qeth' CONFIG_QETH_IPV6
++	else    
++		define_bool CONFIG_QETH_IPV6 n
++	fi
++	if [ "$CONFIG_VLAN_8021Q" = "y" -o "$CONFIG_VLAN_8021Q" = "$CONFIG_QETH" ]; then
++		bool '   VLAN support for qeth' CONFIG_QETH_VLAN
++	else    
++		define_bool CONFIG_QETH_VLAN n
++	fi
++        bool '   Performance statistics in /proc' CONFIG_QETH_PERF_STATS
++      fi
++    fi
+     tristate 'CTC device support' CONFIG_CTC
+-    tristate 'IUCV device support (VM only)' CONFIG_IUCV
++    tristate 'CTCMPC device support' CONFIG_MPC
++    tristate 'IUCV support (VM only)' CONFIG_IUCV
++    dep_tristate 'IUCV network device support (VM only)' CONFIG_NETIUCV $CONFIG_IUCV
++    dep_tristate 'IUCV special message support (VM only)' CONFIG_SMSGIUCV $CONFIG_IUCV
+   fi
+   endmenu
+ fi
+ 
 +
- include $(TOPDIR)/Rules.make
++mainmenu_option next_comment
++comment 'Miscellaneous'
++  tristate 'Z90CRYPT support' CONFIG_Z90CRYPT
++endmenu
+diff -urN kernel-source-2.4.27.orig/drivers/s390/Makefile drivers/s390/Makefile
+--- kernel-source-2.4.27.orig/drivers/s390/Makefile	2003-06-13 08:51:35.000000000 -0600
++++ drivers/s390/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -4,11 +4,16 @@
+ 
+ O_TARGET := io.o
+ 
+-subdir-y := block char misc net
++subdir-y := block char misc net scsi
+ subdir-m := $(subdir-y)
+ 
+ obj-y := s390io.o s390mach.o s390dyn.o ccwcache.o sysinfo.o
+ export-objs += ccwcache.o s390dyn.o s390io.o
++obj-$(CONFIG_QDIO) += qdio.o
++export-objs += qdio.o
 +
-+z90crypt.o: $(z90crypt_mod-objs)
-+	$(LD) -r -o $@ $(z90crypt_mod-objs)
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/chandev.c drivers/s390/misc/chandev.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/chandev.c	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/s390/misc/chandev.c	2006-01-30 22:25:25.000000000 -0700
++obj-$(CONFIG_S390_CMF) += cmf.o
++export-objs += cmf.o
+ 
+ obj-y += $(foreach dir,$(subdir-y),$(dir)/s390-$(dir).o)
+ 
+diff -urN kernel-source-2.4.27.orig/drivers/s390/misc/chandev.c drivers/s390/misc/chandev.c
+--- kernel-source-2.4.27.orig/drivers/s390/misc/chandev.c	2003-08-25 05:44:42.000000000 -0600
++++ drivers/s390/misc/chandev.c	2006-02-12 12:47:23.000000000 -0700
 @@ -1023,15 +1023,16 @@
  
  	/* 3172/2216 Paralell the 2216 allows 16 ports per card the */
@@ -31374,9 +31321,29 @@
  				case (escon_stridx*stridx_mult):
  					chan_type=chandev_type_escon;
  					break;
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/z90common.h drivers/s390/misc/z90common.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/z90common.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/misc/z90common.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/misc/Makefile drivers/s390/misc/Makefile
+--- kernel-source-2.4.27.orig/drivers/s390/misc/Makefile	2001-04-11 20:02:28.000000000 -0600
++++ drivers/s390/misc/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -4,7 +4,15 @@
+ 
+ O_TARGET := s390-misc.o
+ 
+-obj-$(CONFIG_CHANDEV) += chandev.o
+ export-objs += chandev.o
+ 
++list-multi := z90crypt.o
++z90crypt_mod-objs := z90main.o z90hardware.o
++obj-$(CONFIG_Z90CRYPT) += z90crypt.o
++
++obj-$(CONFIG_CHANDEV) += chandev.o
++
+ include $(TOPDIR)/Rules.make
++
++z90crypt.o: $(z90crypt_mod-objs)
++	$(LD) -r -o $@ $(z90crypt_mod-objs)
+diff -urN kernel-source-2.4.27.orig/drivers/s390/misc/z90common.h drivers/s390/misc/z90common.h
+--- kernel-source-2.4.27.orig/drivers/s390/misc/z90common.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/misc/z90common.h	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,149 @@
 +/*
 + *  linux/drivers/s390/misc/z90common.h
@@ -31527,9 +31494,9 @@
 +#define UMIN(a,b) ((a) < (b) ? (a) : (b))
 +#define IS_EVEN(x) ((x) == (2 * ((x) / 2)))
 +#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/z90crypt.h drivers/s390/misc/z90crypt.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/z90crypt.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/misc/z90crypt.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/misc/z90crypt.h drivers/s390/misc/z90crypt.h
+--- kernel-source-2.4.27.orig/drivers/s390/misc/z90crypt.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/misc/z90crypt.h	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,258 @@
 +/*
 + *  linux/drivers/s390/misc/z90crypt.h
@@ -31789,9 +31756,9 @@
 +};
 +
 +#endif /* _LINUX_Z90CRYPT_H_ */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/z90hardware.c drivers/s390/misc/z90hardware.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/z90hardware.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/misc/z90hardware.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/misc/z90hardware.c drivers/s390/misc/z90hardware.c
+--- kernel-source-2.4.27.orig/drivers/s390/misc/z90hardware.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/misc/z90hardware.c	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,2092 @@
 +/*
 + *  linux/drivers/s390/misc/z90hardware.c
@@ -33885,9 +33852,9 @@
 +		PRINTK("Zero *respbufflen_p\n");
 +	return 0;
 +}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/z90main.c drivers/s390/misc/z90main.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/z90main.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/misc/z90main.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/misc/z90main.c drivers/s390/misc/z90main.c
+--- kernel-source-2.4.27.orig/drivers/s390/misc/z90main.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/misc/z90main.c	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,3588 @@
 +/*
 + *  linux/drivers/s390/misc/z90main.c
@@ -37477,31 +37444,9 @@
 +
 +module_init(z90crypt_init_module);
 +module_exit(z90crypt_cleanup_module);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/Makefile drivers/s390/net/Makefile
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/Makefile	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/s390/net/Makefile	2006-01-30 22:25:25.000000000 -0700
-@@ -9,12 +9,14 @@
- 
- ctc-objs := ctcmain.o ctctty.o
- 
--obj-$(CONFIG_IUCV) += iucv.o fsm.o
-+obj-$(CONFIG_IUCV) += iucv.o
- obj-$(CONFIG_CTC) += ctc.o fsm.o
--obj-$(CONFIG_IUCV) += netiucv.o
--obj-$(CONFIG_C7000) += c7000.o
-+obj-$(CONFIG_MPC) += ctcmpc.o fsm.o
-+obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o
-+obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
-+obj-$(CONFIG_LCS) += lcs.o
- obj-$(CONFIG_QETH) += qeth.o
--export-objs += qeth.o
-+export-objs += qeth.o smsgiucv.o
- 
- include $(TOPDIR)/Rules.make
- 
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/ctcmain.c drivers/s390/net/ctcmain.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/ctcmain.c	2004-08-07 17:26:05.000000000 -0600
-+++ drivers/s390/net/ctcmain.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/ctcmain.c drivers/s390/net/ctcmain.c
+--- kernel-source-2.4.27.orig/drivers/s390/net/ctcmain.c	2004-08-07 17:26:05.000000000 -0600
++++ drivers/s390/net/ctcmain.c	2006-02-12 12:47:23.000000000 -0700
 @@ -1,5 +1,5 @@
  /*
 - * $Id: ctcmain.c,v 1.63 2003/10/22 19:32:57 felfert Exp $
@@ -37642,9 +37587,9 @@
  	return ret;
  }
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/ctcmpc.c drivers/s390/net/ctcmpc.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/ctcmpc.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/net/ctcmpc.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/ctcmpc.c drivers/s390/net/ctcmpc.c
+--- kernel-source-2.4.27.orig/drivers/s390/net/ctcmpc.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/net/ctcmpc.c	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,8876 @@
 +/*  INTERNAL VERSION:  051804b
 + *
@@ -46522,9 +46467,9 @@
 +#endif /* MODULE */
 +
 +/* --- This is the END my friend --- */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/ctcmpc.h drivers/s390/net/ctcmpc.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/ctcmpc.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/net/ctcmpc.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/ctcmpc.h drivers/s390/net/ctcmpc.h
+--- kernel-source-2.4.27.orig/drivers/s390/net/ctcmpc.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/net/ctcmpc.h	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,52 @@
 +/*
 + * $Id: ctcmpc.h,v 1.1.2.1 2004/10/04 13:28:55 ptiedem Exp $ 
@@ -46578,9 +46523,9 @@
 +extern void ctc_mpc_flow_control(int,int);
 +
 +#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/ctctty.c drivers/s390/net/ctctty.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/ctctty.c	2006-01-30 22:23:46.000000000 -0700
-+++ drivers/s390/net/ctctty.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/ctctty.c drivers/s390/net/ctctty.c
+--- kernel-source-2.4.27.orig/drivers/s390/net/ctctty.c	2006-02-08 04:06:25.000000000 -0700
++++ drivers/s390/net/ctctty.c	2006-02-12 12:47:23.000000000 -0700
 @@ -42,7 +42,7 @@
  #define init_waitqueue_head(x) *(x)=NULL
  #define __set_current_state(state_value) \
@@ -46590,9 +46535,9 @@
  #define set_current_state(state_value) \
  	do { __set_current_state(state_value); mb(); } while (0)
  #else
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/iucv.c drivers/s390/net/iucv.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/iucv.c	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/s390/net/iucv.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/iucv.c drivers/s390/net/iucv.c
+--- kernel-source-2.4.27.orig/drivers/s390/net/iucv.c	2003-08-25 05:44:42.000000000 -0600
++++ drivers/s390/net/iucv.c	2006-02-12 12:47:23.000000000 -0700
 @@ -1,5 +1,5 @@
  /* 
 - * $Id: iucv.c,v 1.41 2003/06/24 16:05:32 felfert Exp $
@@ -46778,9 +46723,9 @@
  			}
  			spin_unlock_irqrestore (&iucv_lock, flags);
  			if (h) {
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/iucv.h drivers/s390/net/iucv.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/iucv.h	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/s390/net/iucv.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/iucv.h drivers/s390/net/iucv.h
+--- kernel-source-2.4.27.orig/drivers/s390/net/iucv.h	2003-08-25 05:44:42.000000000 -0600
++++ drivers/s390/net/iucv.h	2006-02-12 12:47:23.000000000 -0700
 @@ -449,7 +449,7 @@
   *        buflen - Length of reply buffer.                              
   * Output: residual_buffer - Address of buffer updated by the number 
@@ -46799,9 +46744,9 @@
   *              If the answer buffer is the same length as the reply, this field
   *               contains zero.
   *              If the answer buffer is longer than the reply, this field contains
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/lcs.c drivers/s390/net/lcs.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/lcs.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/net/lcs.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/lcs.c drivers/s390/net/lcs.c
+--- kernel-source-2.4.27.orig/drivers/s390/net/lcs.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/net/lcs.c	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,2192 @@
 +/*
 + *  linux/drivers/s390/net/lcs.c
@@ -48995,9 +48940,9 @@
 +MODULE_AUTHOR("Frank Pavlic <pavlic at de.ibm.com>");
 +MODULE_LICENSE("GPL");
 +
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/lcs.h drivers/s390/net/lcs.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/lcs.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/net/lcs.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/lcs.h drivers/s390/net/lcs.h
+--- kernel-source-2.4.27.orig/drivers/s390/net/lcs.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/net/lcs.h	2006-02-12 12:47:23.000000000 -0700
 @@ -0,0 +1,312 @@
 +/*lcs.h*/
 +
@@ -49311,9 +49256,31 @@
 +	u8 hint_port_no;
 +	s16 port_protocol_no;
 +}  __attribute__ ((aligned(8)));
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/netiucv.c drivers/s390/net/netiucv.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/netiucv.c	2004-08-07 17:26:05.000000000 -0600
-+++ drivers/s390/net/netiucv.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/Makefile drivers/s390/net/Makefile
+--- kernel-source-2.4.27.orig/drivers/s390/net/Makefile	2003-08-25 05:44:42.000000000 -0600
++++ drivers/s390/net/Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -9,12 +9,14 @@
+ 
+ ctc-objs := ctcmain.o ctctty.o
+ 
+-obj-$(CONFIG_IUCV) += iucv.o fsm.o
++obj-$(CONFIG_IUCV) += iucv.o
+ obj-$(CONFIG_CTC) += ctc.o fsm.o
+-obj-$(CONFIG_IUCV) += netiucv.o
+-obj-$(CONFIG_C7000) += c7000.o
++obj-$(CONFIG_MPC) += ctcmpc.o fsm.o
++obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o
++obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
++obj-$(CONFIG_LCS) += lcs.o
+ obj-$(CONFIG_QETH) += qeth.o
+-export-objs += qeth.o
++export-objs += qeth.o smsgiucv.o
+ 
+ include $(TOPDIR)/Rules.make
+ 
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/netiucv.c drivers/s390/net/netiucv.c
+--- kernel-source-2.4.27.orig/drivers/s390/net/netiucv.c	2004-08-07 17:26:05.000000000 -0600
++++ drivers/s390/net/netiucv.c	2006-02-12 12:47:23.000000000 -0700
 @@ -1,5 +1,5 @@
  /*
 - * $Id: netiucv.c,v 1.23 2003/06/24 16:05:32 felfert Exp $
@@ -49755,9 +49722,9 @@
  	char *version = vbuf;
  
  	if ((version = strchr(version, ':'))) {
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/qeth.c drivers/s390/net/qeth.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/qeth.c	2004-08-07 17:26:05.000000000 -0600
-+++ drivers/s390/net/qeth.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/qeth.c drivers/s390/net/qeth.c
+--- kernel-source-2.4.27.orig/drivers/s390/net/qeth.c	2004-08-07 17:26:05.000000000 -0600
++++ drivers/s390/net/qeth.c	2006-02-12 12:47:24.000000000 -0700
 @@ -1,6 +1,6 @@
  /*
   *
@@ -53092,9 +53059,9 @@
  	return result;
  }
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/qeth.h drivers/s390/net/qeth.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/qeth.h	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/s390/net/qeth.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/qeth.h drivers/s390/net/qeth.h
+--- kernel-source-2.4.27.orig/drivers/s390/net/qeth.h	2003-08-25 05:44:42.000000000 -0600
++++ drivers/s390/net/qeth.h	2006-02-12 12:47:24.000000000 -0700
 @@ -15,7 +15,7 @@
  
  #define QETH_NAME " qeth"
@@ -53452,9 +53419,9 @@
  	default: return " strange";
  	}
  }
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/qeth_mpc.h drivers/s390/net/qeth_mpc.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/qeth_mpc.h	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/s390/net/qeth_mpc.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/qeth_mpc.h drivers/s390/net/qeth_mpc.h
+--- kernel-source-2.4.27.orig/drivers/s390/net/qeth_mpc.h	2003-08-25 05:44:42.000000000 -0600
++++ drivers/s390/net/qeth_mpc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -11,7 +11,7 @@
  #ifndef __QETH_MPC_H__
  #define __QETH_MPC_H__
@@ -53543,9 +53510,9 @@
  #define QETH_IPA_CMD_DEST_ADDR(buffer) (buffer+0x2c)
  
  #define PDU_ENCAPSULATION(buffer) \
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/smsgiucv.c drivers/s390/net/smsgiucv.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/smsgiucv.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/net/smsgiucv.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/smsgiucv.c drivers/s390/net/smsgiucv.c
+--- kernel-source-2.4.27.orig/drivers/s390/net/smsgiucv.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/net/smsgiucv.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,167 @@
 +/*
 + * IUCV special message driver
@@ -53714,9 +53681,9 @@
 +
 +EXPORT_SYMBOL(smsg_register_callback);
 +EXPORT_SYMBOL(smsg_unregister_callback);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/smsgiucv.h drivers/s390/net/smsgiucv.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/smsgiucv.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/net/smsgiucv.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/net/smsgiucv.h drivers/s390/net/smsgiucv.h
+--- kernel-source-2.4.27.orig/drivers/s390/net/smsgiucv.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/net/smsgiucv.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,10 @@
 +/*
 + * IUCV special message driver
@@ -53728,9 +53695,9 @@
 +int  smsg_register_callback(char *, void (*)(char *));
 +void smsg_unregister_callback(char *, void (*)(char *));
 +
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/qdio.c drivers/s390/qdio.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/qdio.c	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/s390/qdio.c	2006-01-30 22:25:26.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/qdio.c drivers/s390/qdio.c
+--- kernel-source-2.4.27.orig/drivers/s390/qdio.c	2003-08-25 05:44:42.000000000 -0600
++++ drivers/s390/qdio.c	2006-02-12 12:47:24.000000000 -0700
 @@ -57,7 +57,7 @@
  
  #include <asm/qdio.h>
@@ -54726,9 +54693,9 @@
  
  	tiqdio_register_thinints();
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/s390io.c drivers/s390/s390io.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/s390io.c	2004-08-07 17:26:05.000000000 -0600
-+++ drivers/s390/s390io.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/s390io.c drivers/s390/s390io.c
+--- kernel-source-2.4.27.orig/drivers/s390/s390io.c	2004-08-07 17:26:05.000000000 -0600
++++ drivers/s390/s390io.c	2006-02-12 12:47:24.000000000 -0700
 @@ -1,7 +1,7 @@
  /*
   *  drivers/s390/s390io.c
@@ -54852,38 +54819,493 @@
  	  return len;
       }
  }
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/Makefile drivers/s390/scsi/Makefile
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/Makefile	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/scsi/Makefile	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/scsi/Makefile drivers/s390/scsi/Makefile
+--- kernel-source-2.4.27.orig/drivers/s390/scsi/Makefile	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/scsi/Makefile	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,25 @@
 +#
 +# Makefile for the S/390 specific device drivers
 +#
 +
-+O_TARGET := s390-scsi.o
++O_TARGET := s390-scsi.o
++
++obj-$(CONFIG_ZFCP) += zfcp.o
++obj-$(CONFIG_ZFCP_HBAAPI) += zfcp_hbaapi.o
++export-objs += zfcp_main.o zfcp_zh.o
++
++zfcp-objs := zfcp_main.o zfcp_zh.o
++hbaapi-objs := zh_main.o
++
++ifeq ($(CONFIG_S390_SUPPORT),y)
++	hbaapi-objs += zh_ioctl32.o
++endif
++
++include $(TOPDIR)/Rules.make
++
++zfcp.o: $(zfcp-objs)
++	$(LD) -r -o $@ $(zfcp-objs)
++
++zfcp_hbaapi.o: $(hbaapi-objs)
++	$(LD) -r -o $@ $(hbaapi-objs)
++
+diff -urN kernel-source-2.4.27.orig/drivers/s390/scsi/zfcp_fsf.h drivers/s390/scsi/zfcp_fsf.h
+--- kernel-source-2.4.27.orig/drivers/s390/scsi/zfcp_fsf.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/scsi/zfcp_fsf.h	2006-02-12 12:47:24.000000000 -0700
+@@ -0,0 +1,451 @@
++/*
++ * $Id: zfcp_fsf.h,v 1.7.2.4 2004/08/13 14:01:13 aherrman Exp $
++ *
++ * header file for FCP adapter driver for IBM eServer zSeries
++ *
++ * (C) Copyright IBM Corp. 2002, 2003
++ *
++ * Authors:
++ *      Martin Peschke <mpeschke at de.ibm.com>
++ *      Raimund Schroeder <raimund.schroeder at de.ibm.com>
++ *      Aron Zeh
++ *      Wolfgang Taphorn
++ *	Andreas Herrmann <aherrman at de.ibm.com>
++ */
++
++#ifndef FSF_H
++#define FSF_H
++
++#define FSF_QTCB_VERSION1			0x00000001
++#define FSF_QTCB_CURRENT_VERSION		FSF_QTCB_VERSION1
++
++/* FSF commands */
++#define	FSF_QTCB_FCP_CMND			0x00000001
++#define	FSF_QTCB_ABORT_FCP_CMND			0x00000002
++#define	FSF_QTCB_OPEN_PORT_WITH_DID		0x00000005
++#define	FSF_QTCB_OPEN_LUN			0x00000006
++#define	FSF_QTCB_CLOSE_LUN			0x00000007
++#define	FSF_QTCB_CLOSE_PORT			0x00000008
++#define	FSF_QTCB_CLOSE_PHYSICAL_PORT		0x00000009
++#define	FSF_QTCB_SEND_ELS			0x0000000B
++#define	FSF_QTCB_SEND_GENERIC			0x0000000C
++#define	FSF_QTCB_EXCHANGE_CONFIG_DATA		0x0000000D
++#define	FSF_QTCB_EXCHANGE_PORT_DATA		0x0000000E
++#define FSF_QTCB_DOWNLOAD_CONTROL_FILE		0x00000012
++#define FSF_QTCB_UPLOAD_CONTROL_FILE		0x00000013
++ 
++/* FSF QTCB types */
++#define FSF_IO_COMMAND				0x00000001
++#define FSF_SUPPORT_COMMAND			0x00000002
++#define FSF_CONFIG_COMMAND			0x00000003
++#define FSF_PORT_COMMAND			0x00000004
++
++/* FSF control file upload/download operations' subtype and options */
++#define FSF_CFDC_OPERATION_SUBTYPE		0x00020001
++#define FSF_CFDC_OPTION_NORMAL_MODE		0x00000000
++#define FSF_CFDC_OPTION_FORCE			0x00000001
++#define FSF_CFDC_OPTION_FULL_ACCESS		0x00000002
++#define FSF_CFDC_OPTION_RESTRICTED_ACCESS	0x00000004
++
++/* FSF protocol stati */
++#define FSF_PROT_GOOD				0x00000001
++#define FSF_PROT_QTCB_VERSION_ERROR		0x00000010
++#define FSF_PROT_SEQ_NUMB_ERROR			0x00000020
++#define FSF_PROT_UNSUPP_QTCB_TYPE		0x00000040
++#define FSF_PROT_HOST_CONNECTION_INITIALIZING	0x00000080
++#define FSF_PROT_FSF_STATUS_PRESENTED		0x00000100
++#define FSF_PROT_DUPLICATE_REQUEST_ID		0x00000200
++#define FSF_PROT_LINK_DOWN                      0x00000400
++#define FSF_PROT_REEST_QUEUE                    0x00000800
++#define FSF_PROT_ERROR_STATE			0x01000000
++
++/* FSF stati */
++#define FSF_GOOD				0x00000000
++#define FSF_PORT_ALREADY_OPEN			0x00000001
++#define FSF_LUN_ALREADY_OPEN			0x00000002
++#define FSF_PORT_HANDLE_NOT_VALID		0x00000003
++#define FSF_LUN_HANDLE_NOT_VALID		0x00000004
++#define FSF_HANDLE_MISMATCH			0x00000005
++#define FSF_SERVICE_CLASS_NOT_SUPPORTED		0x00000006
++#define FSF_FCPLUN_NOT_VALID			0x00000009
++#define FSF_ACCESS_DENIED			0x00000010
++#define FSF_ACCESS_TYPE_NOT_VALID		0x00000011
++#define FSF_LUN_SHARING_VIOLATION		0x00000012
++#define FSF_COMMAND_ABORTED_ULP			0x00000020
++#define FSF_COMMAND_ABORTED_ADAPTER		0x00000021
++#define FSF_FCP_COMMAND_DOES_NOT_EXIST		0x00000022
++#define FSF_DIRECTION_INDICATOR_NOT_VALID	0x00000030
++#define FSF_INBOUND_DATA_LENGTH_NOT_VALID	0x00000031	/* FIXME: obsolete? */
++#define FSF_OUTBOUND_DATA_LENGTH_NOT_VALID	0x00000032	/* FIXME: obsolete? */
++#define FSF_CMND_LENGTH_NOT_VALID		0x00000033
++#define FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED	0x00000040
++#define FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED	0x00000041
++#define FSF_REQUEST_BUF_NOT_VALID		0x00000042
++#define FSF_RESPONSE_BUF_NOT_VALID		0x00000043
++#define FSF_ELS_COMMAND_REJECTED		0x00000050
++#define FSF_GENERIC_COMMAND_REJECTED		0x00000051
++#define FSF_OPERATION_PARTIALLY_SUCCESSFUL	0x00000052
++#define FSF_AUTHORIZATION_FAILURE		0x00000053
++#define FSF_CFDC_ERROR_DETECTED			0x00000054
++#define FSF_CONTROL_FILE_UPDATE_ERROR		0x00000055
++#define FSF_CONTROL_FILE_TOO_LARGE		0x00000056
++#define FSF_ACCESS_CONFLICT_DETECTED		0x00000057
++#define FSF_CONFLICTS_OVERRULED			0x00000058
++#define FSF_PORT_BOXED				0x00000059
++#define FSF_LUN_BOXED				0x0000005A
++#define FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE	0x0000005B
++#define FSF_PAYLOAD_SIZE_MISMATCH		0x00000060
++#define FSF_REQUEST_SIZE_TOO_LARGE		0x00000061
++#define FSF_RESPONSE_SIZE_TOO_LARGE		0x00000062
++#define FSF_ADAPTER_STATUS_AVAILABLE		0x000000AD
++#define FSF_FCP_RSP_AVAILABLE			0x000000AF
++#define FSF_UNKNOWN_COMMAND			0x000000E2
++#define FSF_UNKNOWN_OP_SUBTYPE			0x000000E3
++#define FSF_INVALID_COMMAND_OPTION		0x000000E5
++//#define FSF_ERROR				0x000000FF 
++
++/* FSF status qualifier, recommendations */
++#define FSF_SQ_NO_RECOM				0x00
++#define FSF_SQ_FCP_RSP_AVAILABLE		0x01
++#define FSF_SQ_RETRY_IF_POSSIBLE		0x02
++#define FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED	0x03
++#define FSF_SQ_INVOKE_LINK_TEST_PROCEDURE	0x04
++#define FSF_SQ_ULP_PROGRAMMING_ERROR		0x05
++#define FSF_SQ_COMMAND_ABORTED			0x06
++#define FSF_SQ_NO_RETRY_POSSIBLE		0x07
++
++/* FSF status qualifier for ACT download/upload commands */
++#define FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE	0x00000001
++#define FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2	0x00000002
++/* ACT subtable codes */
++#define FSF_SQ_CFDC_SUBTABLE_OS			0x0001
++#define FSF_SQ_CFDC_SUBTABLE_PORT_WWPN		0x0002
++#define FSF_SQ_CFDC_SUBTABLE_PORT_DID		0x0003
++#define FSF_SQ_CFDC_SUBTABLE_LUN		0x0004
++
++/* FSF status qualifier (most significant 4 bytes), local link down */
++#define FSF_PSQ_LINK_NOLIGHT			0x00000004
++#define FSF_PSQ_LINK_WRAPPLUG			0x00000008
++#define FSF_PSQ_LINK_NOFCP			0x00000010
++
++/* payload size in status read buffer */
++#define FSF_STATUS_READ_PAYLOAD_SIZE		4032
++
++/* number of status read buffers that should be sent by ULP */
++#define FSF_STATUS_READS_RECOM			16
++
++/* status types in status read buffer */
++#define FSF_STATUS_READ_PORT_CLOSED		0x00000001
++#define FSF_STATUS_READ_INCOMING_ELS		0x00000002
++#define FSF_STATUS_READ_BIT_ERROR_THRESHOLD	0x00000004
++#define FSF_STATUS_READ_LINK_DOWN		0x00000005	/* FIXME: really? */
++#define FSF_STATUS_READ_LINK_UP          	0x00000006
++#define FSF_STATUS_READ_CFDC_UPDATED          	0x0000000A
++#define FSF_STATUS_READ_CFDC_HARDENED          	0x0000000B
++
++/* status subtypes in status read buffer */
++#define FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT	0x00000001
++#define FSF_STATUS_READ_SUB_ERROR_PORT		0x00000002
++#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE	0x00000002
++#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2	0x0000000F
++
++#define FSF_OPEN_LUN_SUPPRESS_BOXING		0x00000001
++
++/* topologie that is detected by the adapter */
++#define FSF_TOPO_ERROR				0x00000000
++#define FSF_TOPO_P2P				0x00000001
++#define FSF_TOPO_FABRIC				0x00000002
++#define FSF_TOPO_AL				0x00000003
++#define FSF_TOPO_FABRIC_VIRT			0x00000004
++
++/* data direction for FCP commands */
++#define FSF_DATADIR_WRITE			0x00000001
++#define FSF_DATADIR_READ			0x00000002
++#define FSF_DATADIR_READ_WRITE			0x00000003
++#define FSF_DATADIR_CMND			0x00000004
++
++/* fc service class */
++#define FSF_CLASS_1				0x00000001
++#define FSF_CLASS_2				0x00000002
++#define FSF_CLASS_3				0x00000003
++
++/* SBAL chaining */
++#define FSF_MAX_SBALS_PER_REQ			36
++#define FSF_MAX_SBALS_PER_ELS_REQ		2
++
++/* logging space behind QTCB */
++#define FSF_QTCB_LOG_SIZE			1024
++
++/* channel features */
++#define FSF_FEATURE_QTCB_SUPPRESSION            0x00000001
++#define FSF_FEATURE_CFDC                        0x00000002
++#define FSF_FEATURE_HBAAPI_MANAGEMENT           0x00000010
++#define FSF_FEATURE_ELS_CT_CHAINED_SBALS        0x00000020
++
++/* adapter types */
++#define FSF_ADAPTER_TYPE_FICON                  0x00000001
++#define FSF_ADAPTER_TYPE_FICON_EXPRESS          0x00000002
++
++/* port types */
++#define FSF_HBA_PORTTYPE_UNKNOWN		0x00000001
++#define FSF_HBA_PORTTYPE_NOTPRESENT		0x00000003
++#define FSF_HBA_PORTTYPE_NPORT			0x00000005
++#define FSF_HBA_PORTTYPE_PTP			0x00000021
++/* following are not defined and used by FSF Spec
++   but are additionally defined by FC-HBA */
++#define FSF_HBA_PORTTYPE_OTHER			0x00000002
++#define FSF_HBA_PORTTYPE_NOTPRESENT		0x00000003
++#define FSF_HBA_PORTTYPE_NLPORT			0x00000006
++#define FSF_HBA_PORTTYPE_FLPORT			0x00000007
++#define FSF_HBA_PORTTYPE_FPORT			0x00000008
++#define FSF_HBA_PORTTYPE_LPORT			0x00000020
++
++/* port states */
++#define FSF_HBA_PORTSTATE_UNKNOWN		0x00000001
++#define FSF_HBA_PORTSTATE_ONLINE		0x00000002
++#define FSF_HBA_PORTSTATE_OFFLINE		0x00000003
++#define FSF_HBA_PORTSTATE_LINKDOWN		0x00000006
++#define FSF_HBA_PORTSTATE_ERROR			0x00000007
++
++/* IO states of adapter */
++#define FSF_IOSTAT_NPORT_RJT			0x00000004
++#define FSF_IOSTAT_FABRIC_RJT			0x00000005
++#define FSF_IOSTAT_LS_RJT			0x00000009
++
++
++struct fsf_queue_designator;
++struct fsf_status_read_buffer;
++struct fsf_port_closed_payload;
++struct fsf_bit_error_payload;
++union fsf_prot_status_qual;
++struct fsf_qual_version_error;
++struct fsf_qual_sequence_error;
++struct fsf_qtcb_prefix;
++struct fsf_qtcb_header;
++struct fsf_qtcb_bottom_config;
++struct fsf_qtcb_bottom_support;
++struct fsf_qtcb_bottom_io;
++union fsf_qtcb_bottom;
++
++typedef struct fsf_queue_designator {
++	u8			cssid;
++	u8			chpid;
++	u8			hla;
++	u8			ua;
++	u32			res1;
++} __attribute__ ((packed)) fsf_queue_designator_t;
++
++typedef struct fsf_port_closed_payload {
++	fsf_queue_designator_t	queue_designator;
++	u32			port_handle;
++} __attribute__ ((packed)) fsf_port_closed_payload_t;
++
++typedef struct fsf_bit_error_payload {
++	u32			res1;
++	u32			link_failure_error_count;
++	u32			loss_of_sync_error_count;
++	u32			loss_of_signal_error_count;
++	u32			primitive_sequence_error_count;
++	u32			invalid_transmission_word_error_count;
++	u32			crc_error_count;
++	u32			primitive_sequence_event_timeout_count;
++	u32			elastic_buffer_overrun_error_count;
++	u32			fcal_arbitration_timeout_count;
++	u32			advertised_receive_b2b_credit;
++	u32			current_receive_b2b_credit;
++	u32			advertised_transmit_b2b_credit;
++	u32			current_transmit_b2b_credit;
++} __attribute__ ((packed)) fsf_bit_error_payload_t;
++
++typedef struct fsf_status_read_buffer {
++        u32			status_type;
++        u32			status_subtype;
++        u32			length;
++        u32			res1;
++        fsf_queue_designator_t	queue_designator;
++        u32			d_id;
++        u32			class;
++        u64			fcp_lun;
++        u8			res3[24];
++        u8			payload[FSF_STATUS_READ_PAYLOAD_SIZE];
++} __attribute__ ((packed)) fsf_status_read_buffer_t;
++
++typedef struct fsf_qual_version_error {
++	u32			fsf_version;
++	u32			res1[3];
++} __attribute__ ((packed)) fsf_qual_version_error_t;
++
++typedef struct fsf_qual_sequence_error {
++	u32			exp_req_seq_no;
++	u32			res1[3];
++} __attribute__ ((packed)) fsf_qual_sequence_error_t;
++
++typedef struct fsf_qual_locallink_error {
++	u32			code;
++	u32			res1[3];
++} __attribute__ ((packed)) fsf_qual_locallink_error_t;
++
++typedef union fsf_prot_status_qual {
++	fsf_qual_version_error_t	version_error;
++	fsf_qual_sequence_error_t	sequence_error;
++	fsf_qual_locallink_error_t	locallink_error;
++} __attribute__ ((packed)) fsf_prot_status_qual_t;
++
++typedef struct fsf_qtcb_prefix {
++	u64			req_id;
++	u32			qtcb_version;
++	u32			ulp_info;
++	u32			qtcb_type;
++	u32			req_seq_no;
++	u32			prot_status;
++	fsf_prot_status_qual_t	prot_status_qual;
++	u8			res1[20];
++} __attribute__ ((packed)) fsf_qtcb_prefix_t;
++
++typedef union fsf_status_qual {
++#define FSF_STATUS_QUAL_SIZE	16
++	u8			byte[FSF_STATUS_QUAL_SIZE];
++	u16			halfword[FSF_STATUS_QUAL_SIZE / sizeof(u16)];
++	u32			word[FSF_STATUS_QUAL_SIZE / sizeof(u32)];
++	fsf_queue_designator_t	fsf_queue_designator;
++} __attribute__ ((packed)) fsf_status_qual_t;
++
++typedef struct fsf_qtcb_header {
++	u64			req_handle;
++	u32			fsf_command;
++	u32			res1;
++	u32			port_handle;
++	u32			lun_handle;
++	u32			res2;
++	u32			fsf_status;
++	fsf_status_qual_t	fsf_status_qual;
++	u8			res3[28];
++	u16			log_start;
++	u16			log_length;
++	u8			res4[16];
++} __attribute__ ((packed)) fsf_qtcb_header_t;
 +
-+obj-$(CONFIG_ZFCP) += zfcp.o
-+obj-$(CONFIG_ZFCP_HBAAPI) += zfcp_hbaapi.o
-+export-objs += zfcp_main.o zfcp_zh.o
++typedef u64 fsf_wwn_t;
 +
-+zfcp-objs := zfcp_main.o zfcp_zh.o
-+hbaapi-objs := zh_main.o
++typedef struct fsf_nport_serv_param {
++	u8			common_serv_param[16];
++	fsf_wwn_t		wwpn;
++	fsf_wwn_t		wwnn;
++	u8			class1_serv_param[16];
++	u8			class2_serv_param[16];
++	u8			class3_serv_param[16];
++	u8			class4_serv_param[16];
++	u8			vendor_version_level[16];
++	u8			res1[16];
++} __attribute__ ((packed)) fsf_nport_serv_param_t;
 +
-+ifeq ($(CONFIG_S390_SUPPORT),y)
-+	hbaapi-objs += zh_ioctl32.o
-+endif
++typedef struct fsf_plogi {
++	u32			code;
++	fsf_nport_serv_param_t	serv_param;
++} __attribute__ ((packed)) fsf_plogi_t;
 +
-+include $(TOPDIR)/Rules.make
++#define FSF_FCP_CMND_SIZE	288
++#define FSF_FCP_RSP_SIZE	128
 +
-+zfcp.o: $(zfcp-objs)
-+	$(LD) -r -o $@ $(zfcp-objs)
++typedef struct fsf_qtcb_bottom_io {
++	u32			data_direction;
++	u32			service_class;
++	u8			res1[8];
++	u32			fcp_cmnd_length;
++	u8			res2[12];
++	u8			fcp_cmnd[FSF_FCP_CMND_SIZE];
++	u8			fcp_rsp[FSF_FCP_RSP_SIZE];
++	u8			res3[64];
++} __attribute__ ((packed)) fsf_qtcb_bottom_io_t;
 +
-+zfcp_hbaapi.o: $(hbaapi-objs)
-+	$(LD) -r -o $@ $(hbaapi-objs)
++typedef struct fsf_qtcb_bottom_support {
++	u32			op_subtype;
++	u8			res1[12];
++	u32			d_id;
++	u32			option;
++	u64			fcp_lun;
++	u64			res2;
++	u64			req_handle;
++	u32			service_class;
++	u8			res3[3];
++	u8			timeout;
++	u8			res4[184];
++	u32			els1_length;
++	u32			els2_length;
++	u32			req_buf_length;
++	u32			resp_buf_length;
++	u8			els[256];
++} __attribute__ ((packed)) fsf_qtcb_bottom_support_t;
++
++typedef struct fsf_qtcb_bottom_config {
++	u32			lic_version;
++	u32			feature_selection;
++	u32			high_qtcb_version;
++	u32			low_qtcb_version;
++	u32			max_qtcb_size;
++	u32			max_data_transfer_size;
++	u32			supported_features;
++	u8			res1[4];
++	u32			fc_topology;
++	u32			fc_link_speed;
++	u32			adapter_type;
++	u32			peer_d_id;
++	u8			res2[12];
++	u32			s_id;
++	fsf_nport_serv_param_t	nport_serv_param;
++	u8			res3[8];
++	u32			adapter_ports;
++	u32			hardware_version;
++	u8			serial_number[32];
++	u8			res4[272];
++} __attribute__ ((packed)) fsf_qtcb_bottom_config_t;
++
++typedef struct fsf_qtcb_bottom_port {
++	u8 res1[8];
++	u32 fc_port_id;
++	u32 port_type;
++	u32 port_state;
++	u32 class_of_service; /* should be 0x00000006 for class 2 and 3 */
++	u8 supported_fc4_types[32]; /* should be 0x00000100 for scsi fcp */
++	u8 active_fc4_types[32];
++	u32 supported_speed; /* 0x0001 for 1 GBit/s or 0x0002 for 2 GBit/s */
++	u32 maximum_frame_size; /* fixed value of 2112 */
++	u64 seconds_since_last_reset;
++	u64 tx_frames;
++	u64 tx_words;
++	u64 rx_frames;
++	u64 rx_words;
++	u64 lip; /* 0 */
++	u64 nos; /* currently 0 */
++	u64 error_frames; /* currently 0 */
++	u64 dumped_frames; /* currently 0 */
++	u64 link_failure;
++	u64 loss_of_sync;
++	u64 loss_of_signal;
++	u64 psp_error_counts;
++	u64 invalid_tx_words;
++	u64 invalid_crcs;
++	u64 input_requests;
++	u64 output_requests;
++	u64 control_requests;
++	u64 input_mb; /* where 1 MByte == 1.000.000 Bytes */
++	u64 output_mb; /* where 1 MByte == 1.000.000 Bytes */
++	u8 res2[256];
++} __attribute__ ((packed)) fsf_qtcb_bottom_port_t;
++
++typedef union fsf_qtcb_bottom {
++	fsf_qtcb_bottom_io_t		io;
++	fsf_qtcb_bottom_support_t	support;
++	fsf_qtcb_bottom_config_t	config;
++	fsf_qtcb_bottom_port_t		port;
++} fsf_qtcb_bottom_t;
++
++typedef struct fsf_qtcb {
++	fsf_qtcb_prefix_t	prefix;
++	fsf_qtcb_header_t	header;
++	fsf_qtcb_bottom_t	bottom;
++	u8			log[FSF_QTCB_LOG_SIZE];
++} __attribute__ ((packed)) fsf_qtcb_t;
++
++#endif	/* FSF_H */
 +
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zfcp.h drivers/s390/scsi/zfcp.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zfcp.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/scsi/zfcp.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/scsi/zfcp.h drivers/s390/scsi/zfcp.h
+--- kernel-source-2.4.27.orig/drivers/s390/scsi/zfcp.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/scsi/zfcp.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,1298 @@
 +/* 
 + * $Id$
@@ -55975,672 +56397,217 @@
 +	( \
 +		list_empty(head) ? \
 +		NULL : \
-+		list_entry((head)->next,type,list) \
-+	)
-+
-+#define ZFCP_LAST_ENTITY(head,type) \
-+	( \
-+		list_empty(head) ? \
-+		NULL : \
-+		list_entry((head)->prev,type,list) \
-+	)
-+
-+#define ZFCP_PREV_ENTITY(head,curr,type) \
-+	( \
-+		(curr == ZFCP_FIRST_ENTITY(head,type)) ? \
-+		NULL : \
-+		list_entry(curr->list.prev,type,list) \
-+	)
-+
-+#define ZFCP_NEXT_ENTITY(head,curr,type) \
-+	( \
-+		(curr == ZFCP_LAST_ENTITY(head,type)) ? \
-+		NULL : \
-+		list_entry(curr->list.next,type,list) \
-+	)
-+
-+#define ZFCP_FOR_EACH_ENTITY(head,curr,type) \
-+	for (curr = ZFCP_FIRST_ENTITY(head,type); \
-+	     curr; \
-+	     curr = ZFCP_NEXT_ENTITY(head,curr,type)) 
-+
-+/*
-+ * use these macros if you traverse a list and stop iterations after
-+ * altering the list since changing the list will most likely cause
-+ * next/previous pointers to become unavailable,
-+ * usually: examining some list elements, or removing a single
-+ * element from somewhere in the middle of the list,
-+ * lock the list by means of the associated rwlock before entering
-+ * the loop and thus above the macro,
-+ * unlock the list (the associated rwlock) after leaving the loop
-+ * belonging to the macro,
-+ * use read variant of lock if only looking up something without
-+ * changing the list,
-+ * use write variant of lock if changing the list (in last iteration !),
-+ * attention: "upgrading" read lock to write lock is not supported!
-+ */
-+
-+#define ZFCP_FOR_EACH_ADAPTER(a) \
-+	ZFCP_FOR_EACH_ENTITY(&zfcp_data.adapter_list_head,(a),zfcp_adapter_t)
-+
-+#define ZFCP_FOR_EACH_PORT(a,p) \
-+	ZFCP_FOR_EACH_ENTITY(&(a)->port_list_head,(p),zfcp_port_t)
-+
-+#define ZFCP_FOR_EACH_UNIT(p,u) \
-+	ZFCP_FOR_EACH_ENTITY(&(p)->unit_list_head,(u),zfcp_unit_t)
-+
-+
-+/* Note, the leftmost status byte is common among adapter, port 
-+	 and unit
-+ */
-+#define ZFCP_COMMON_FLAGS                               0xff000000
-+#define ZFCP_SPECIFIC_FLAGS                             0x00ffffff
-+
-+/* common status bits */
-+#define ZFCP_STATUS_COMMON_TO_BE_REMOVED		0x80000000 
-+#define ZFCP_STATUS_COMMON_RUNNING			0x40000000 
-+#define ZFCP_STATUS_COMMON_ERP_FAILED			0x20000000
-+#define ZFCP_STATUS_COMMON_UNBLOCKED			0x10000000
-+#define ZFCP_STATUS_COMMON_OPENING                      0x08000000
-+#define ZFCP_STATUS_COMMON_OPEN                         0x04000000
-+#define ZFCP_STATUS_COMMON_CLOSING                      0x02000000
-+#define ZFCP_STATUS_COMMON_ERP_INUSE			0x01000000
-+
-+/* status of adapter */
-+#define ZFCP_STATUS_ADAPTER_IRQOWNER			0x00000001
-+#define ZFCP_STATUS_ADAPTER_QDIOUP			0x00000002
-+#define ZFCP_STATUS_ADAPTER_REGISTERED			0x00000004
-+#define ZFCP_STATUS_ADAPTER_XCONFIG_OK			0x00000008
-+#define ZFCP_STATUS_ADAPTER_HOST_CON_INIT		0x00000010
-+#define ZFCP_STATUS_ADAPTER_ERP_THREAD_UP		0x00000020
-+#define ZFCP_STATUS_ADAPTER_ERP_THREAD_DONE		0x00000040
-+#define ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL		0x00000080
-+#define ZFCP_STATUS_ADAPTER_ERP_PENDING			0x00000100
-+#define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED		0x00000200
-+
-+#define ZFCP_STATUS_ADAPTER_SCSI_UP			\
-+		(ZFCP_STATUS_COMMON_UNBLOCKED |	\
-+		 ZFCP_STATUS_ADAPTER_REGISTERED)
-+
-+#define ZFCP_DID_NAMESERVER				0xFFFFFC
-+
-+/* status of remote port */
-+#define ZFCP_STATUS_PORT_PHYS_OPEN			0x00000001
-+#define ZFCP_STATUS_PORT_DID_DID			0x00000002
-+#define ZFCP_STATUS_PORT_PHYS_CLOSING			0x00000004
-+#define ZFCP_STATUS_PORT_NO_WWPN			0x00000008
-+#define ZFCP_STATUS_PORT_NO_SCSI_ID			0x00000010
-+#define ZFCP_STATUS_PORT_INVALID_WWPN			0x00000020
-+
-+#define ZFCP_STATUS_PORT_NAMESERVER \
-+		(ZFCP_STATUS_PORT_NO_WWPN | \
-+		 ZFCP_STATUS_PORT_NO_SCSI_ID)
-+
-+/* status of logical unit */
-+#define ZFCP_STATUS_UNIT_NOTSUPPUNITRESET		0x00000001
-+#define ZFCP_STATUS_UNIT_ASSUMETCQ			0x00000002
-+
-+/* no common part here */
-+/* status of FSF request */
-+#define ZFCP_STATUS_FSFREQ_NOT_INIT			0x00000000
-+#define ZFCP_STATUS_FSFREQ_POOL  			0x00000001
-+#define ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT		0x00000002
-+#define ZFCP_STATUS_FSFREQ_COMPLETED			0x00000004
-+#define ZFCP_STATUS_FSFREQ_ERROR			0x00000008
-+#define ZFCP_STATUS_FSFREQ_CLEANUP			0x00000010
-+#define ZFCP_STATUS_FSFREQ_ABORTING			0x00000020
-+#define ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED		0x00000040
-+#define ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED		0x00000080
-+#define ZFCP_STATUS_FSFREQ_ABORTED			0x00000100
-+#define ZFCP_STATUS_FSFREQ_TMFUNCFAILED			0x00000200
-+#define ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP		0x00000400
-+#define ZFCP_STATUS_FSFREQ_RETRY			0x00000800
-+#define ZFCP_STATUS_FSFREQ_DISMISSED			0x00001000
-+#define ZFCP_STATUS_FSFREQ_POOLBUF			0x00002000
-+
-+#define ZFCP_KNOWN                              0x00000001
-+#define ZFCP_REQ_AUTO_CLEANUP			0x00000002
-+#define ZFCP_WAIT_FOR_SBAL			0x00000004
-+#define ZFCP_REQ_USE_MEMPOOL			0x00000008
-+
-+/* Mask parameters */
-+#define ZFCP_SET                                0x00000100
-+#define ZFCP_CLEAR                              0x00000200
-+
-+#define ZFCP_INTERRUPTIBLE			1
-+#define ZFCP_UNINTERRUPTIBLE			0
-+
-+#define ZFCP_MAX_ERPS                           3
-+
-+#define ZFCP_ERP_FSFREQ_TIMEOUT			(100 * HZ)
-+#define ZFCP_ERP_MEMWAIT_TIMEOUT		HZ
-+
-+#define ZFCP_STATUS_ERP_TIMEDOUT		0x10000000
-+#define ZFCP_STATUS_ERP_CLOSE_ONLY		0x01000000
-+#define ZFCP_STATUS_ERP_DISMISSING		0x00100000
-+#define ZFCP_STATUS_ERP_DISMISSED		0x00200000
-+
-+#define ZFCP_ERP_STEP_UNINITIALIZED		0x00000000
-+#define ZFCP_ERP_STEP_FSF_XCONFIG		0x00000001
-+#define ZFCP_ERP_STEP_PHYS_PORT_CLOSING		0x00000010
-+#define ZFCP_ERP_STEP_PORT_CLOSING		0x00000100
-+#define ZFCP_ERP_STEP_NAMESERVER_OPEN		0x00000200
-+#define ZFCP_ERP_STEP_NAMESERVER_LOOKUP		0x00000400
-+#define ZFCP_ERP_STEP_PORT_OPENING		0x00000800
-+#define ZFCP_ERP_STEP_UNIT_CLOSING		0x00001000
-+#define ZFCP_ERP_STEP_UNIT_OPENING		0x00002000
-+
-+/* ordered ! */
-+#define ZFCP_ERP_ACTION_REOPEN_ADAPTER		0x4
-+#define ZFCP_ERP_ACTION_REOPEN_PORT_FORCED	0x3
-+#define ZFCP_ERP_ACTION_REOPEN_PORT		0x2
-+#define ZFCP_ERP_ACTION_REOPEN_UNIT		0x1
-+
-+#define ZFCP_ERP_ACTION_RUNNING			0x1
-+#define ZFCP_ERP_ACTION_READY			0x2
-+
-+#define ZFCP_ERP_SUCCEEDED	0x0
-+#define ZFCP_ERP_FAILED		0x1
-+#define ZFCP_ERP_CONTINUES	0x2
-+#define ZFCP_ERP_EXIT		0x3
-+#define ZFCP_ERP_DISMISSED	0x4
-+#define ZFCP_ERP_NOMEM		0x5
-+
-+/* task attribute values in FCP-2 FCP_CMND IU */
-+#define SIMPLE_Q	0
-+#define HEAD_OF_Q	1
-+#define ORDERED_Q	2
-+#define ACA_Q		4
-+#define UNTAGGED	5
-+
-+/* task management flags in FCP-2 FCP_CMND IU */
-+#define CLEAR_ACA		0x40
-+#define TARGET_RESET		0x20
-+#define LOGICAL_UNIT_RESET	0x10
-+#define CLEAR_TASK_SET		0x04
-+#define ABORT_TASK_SET		0x02
-+
-+#define FCP_CDB_LENGTH		16
-+
-+
-+/* some magics which may be used to authenticate data structures */
-+#define ZFCP_MAGIC		0xFCFCFCFC
-+#define ZFCP_MAGIC_ADAPTER	0xAAAAAAAA
-+#define ZFCP_MAGIC_PORT		0xBBBBBBBB
-+#define ZFCP_MAGIC_UNIT		0xCCCCCCCC
-+#define ZFCP_MAGIC_FSFREQ	0xEEEEEEEE
-+
-+/* function prototypes */
-+int zfcp_erp_wait(zfcp_adapter_t*);
-+int zfcp_fsf_exchange_port_data(zfcp_adapter_t*, fsf_qtcb_bottom_port_t*);
-+int zfcp_fsf_send_els(struct zfcp_send_els *);
-+int zfcp_config_parse_record_add(zfcp_config_record_t*);
-+int zfcp_scsi_command_sync(zfcp_unit_t *, Scsi_Cmnd *);
-+int zfcp_ns_ga_nxt_request(zfcp_port_t *, struct ct_iu_ga_nxt *);
-+int zfcp_fsf_send_ct(struct zfcp_send_ct *, zfcp_mem_pool_t *,
-+		     zfcp_erp_action_t *);
-+extern int  zfcp_check_ct_response(struct ct_hdr *);
-+extern int  zfcp_handle_els_rjt(u32, struct zfcp_ls_rjt_par *);
-+
-+#endif /* _ZFCP_H_ */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zfcp_fsf.h drivers/s390/scsi/zfcp_fsf.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zfcp_fsf.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/scsi/zfcp_fsf.h	2006-01-30 22:25:25.000000000 -0700
-@@ -0,0 +1,451 @@
-+/*
-+ * $Id: zfcp_fsf.h,v 1.7.2.4 2004/08/13 14:01:13 aherrman Exp $
-+ *
-+ * header file for FCP adapter driver for IBM eServer zSeries
-+ *
-+ * (C) Copyright IBM Corp. 2002, 2003
-+ *
-+ * Authors:
-+ *      Martin Peschke <mpeschke at de.ibm.com>
-+ *      Raimund Schroeder <raimund.schroeder at de.ibm.com>
-+ *      Aron Zeh
-+ *      Wolfgang Taphorn
-+ *	Andreas Herrmann <aherrman at de.ibm.com>
-+ */
-+
-+#ifndef FSF_H
-+#define FSF_H
-+
-+#define FSF_QTCB_VERSION1			0x00000001
-+#define FSF_QTCB_CURRENT_VERSION		FSF_QTCB_VERSION1
-+
-+/* FSF commands */
-+#define	FSF_QTCB_FCP_CMND			0x00000001
-+#define	FSF_QTCB_ABORT_FCP_CMND			0x00000002
-+#define	FSF_QTCB_OPEN_PORT_WITH_DID		0x00000005
-+#define	FSF_QTCB_OPEN_LUN			0x00000006
-+#define	FSF_QTCB_CLOSE_LUN			0x00000007
-+#define	FSF_QTCB_CLOSE_PORT			0x00000008
-+#define	FSF_QTCB_CLOSE_PHYSICAL_PORT		0x00000009
-+#define	FSF_QTCB_SEND_ELS			0x0000000B
-+#define	FSF_QTCB_SEND_GENERIC			0x0000000C
-+#define	FSF_QTCB_EXCHANGE_CONFIG_DATA		0x0000000D
-+#define	FSF_QTCB_EXCHANGE_PORT_DATA		0x0000000E
-+#define FSF_QTCB_DOWNLOAD_CONTROL_FILE		0x00000012
-+#define FSF_QTCB_UPLOAD_CONTROL_FILE		0x00000013
-+ 
-+/* FSF QTCB types */
-+#define FSF_IO_COMMAND				0x00000001
-+#define FSF_SUPPORT_COMMAND			0x00000002
-+#define FSF_CONFIG_COMMAND			0x00000003
-+#define FSF_PORT_COMMAND			0x00000004
-+
-+/* FSF control file upload/download operations' subtype and options */
-+#define FSF_CFDC_OPERATION_SUBTYPE		0x00020001
-+#define FSF_CFDC_OPTION_NORMAL_MODE		0x00000000
-+#define FSF_CFDC_OPTION_FORCE			0x00000001
-+#define FSF_CFDC_OPTION_FULL_ACCESS		0x00000002
-+#define FSF_CFDC_OPTION_RESTRICTED_ACCESS	0x00000004
-+
-+/* FSF protocol stati */
-+#define FSF_PROT_GOOD				0x00000001
-+#define FSF_PROT_QTCB_VERSION_ERROR		0x00000010
-+#define FSF_PROT_SEQ_NUMB_ERROR			0x00000020
-+#define FSF_PROT_UNSUPP_QTCB_TYPE		0x00000040
-+#define FSF_PROT_HOST_CONNECTION_INITIALIZING	0x00000080
-+#define FSF_PROT_FSF_STATUS_PRESENTED		0x00000100
-+#define FSF_PROT_DUPLICATE_REQUEST_ID		0x00000200
-+#define FSF_PROT_LINK_DOWN                      0x00000400
-+#define FSF_PROT_REEST_QUEUE                    0x00000800
-+#define FSF_PROT_ERROR_STATE			0x01000000
-+
-+/* FSF stati */
-+#define FSF_GOOD				0x00000000
-+#define FSF_PORT_ALREADY_OPEN			0x00000001
-+#define FSF_LUN_ALREADY_OPEN			0x00000002
-+#define FSF_PORT_HANDLE_NOT_VALID		0x00000003
-+#define FSF_LUN_HANDLE_NOT_VALID		0x00000004
-+#define FSF_HANDLE_MISMATCH			0x00000005
-+#define FSF_SERVICE_CLASS_NOT_SUPPORTED		0x00000006
-+#define FSF_FCPLUN_NOT_VALID			0x00000009
-+#define FSF_ACCESS_DENIED			0x00000010
-+#define FSF_ACCESS_TYPE_NOT_VALID		0x00000011
-+#define FSF_LUN_SHARING_VIOLATION		0x00000012
-+#define FSF_COMMAND_ABORTED_ULP			0x00000020
-+#define FSF_COMMAND_ABORTED_ADAPTER		0x00000021
-+#define FSF_FCP_COMMAND_DOES_NOT_EXIST		0x00000022
-+#define FSF_DIRECTION_INDICATOR_NOT_VALID	0x00000030
-+#define FSF_INBOUND_DATA_LENGTH_NOT_VALID	0x00000031	/* FIXME: obsolete? */
-+#define FSF_OUTBOUND_DATA_LENGTH_NOT_VALID	0x00000032	/* FIXME: obsolete? */
-+#define FSF_CMND_LENGTH_NOT_VALID		0x00000033
-+#define FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED	0x00000040
-+#define FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED	0x00000041
-+#define FSF_REQUEST_BUF_NOT_VALID		0x00000042
-+#define FSF_RESPONSE_BUF_NOT_VALID		0x00000043
-+#define FSF_ELS_COMMAND_REJECTED		0x00000050
-+#define FSF_GENERIC_COMMAND_REJECTED		0x00000051
-+#define FSF_OPERATION_PARTIALLY_SUCCESSFUL	0x00000052
-+#define FSF_AUTHORIZATION_FAILURE		0x00000053
-+#define FSF_CFDC_ERROR_DETECTED			0x00000054
-+#define FSF_CONTROL_FILE_UPDATE_ERROR		0x00000055
-+#define FSF_CONTROL_FILE_TOO_LARGE		0x00000056
-+#define FSF_ACCESS_CONFLICT_DETECTED		0x00000057
-+#define FSF_CONFLICTS_OVERRULED			0x00000058
-+#define FSF_PORT_BOXED				0x00000059
-+#define FSF_LUN_BOXED				0x0000005A
-+#define FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE	0x0000005B
-+#define FSF_PAYLOAD_SIZE_MISMATCH		0x00000060
-+#define FSF_REQUEST_SIZE_TOO_LARGE		0x00000061
-+#define FSF_RESPONSE_SIZE_TOO_LARGE		0x00000062
-+#define FSF_ADAPTER_STATUS_AVAILABLE		0x000000AD
-+#define FSF_FCP_RSP_AVAILABLE			0x000000AF
-+#define FSF_UNKNOWN_COMMAND			0x000000E2
-+#define FSF_UNKNOWN_OP_SUBTYPE			0x000000E3
-+#define FSF_INVALID_COMMAND_OPTION		0x000000E5
-+//#define FSF_ERROR				0x000000FF 
-+
-+/* FSF status qualifier, recommendations */
-+#define FSF_SQ_NO_RECOM				0x00
-+#define FSF_SQ_FCP_RSP_AVAILABLE		0x01
-+#define FSF_SQ_RETRY_IF_POSSIBLE		0x02
-+#define FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED	0x03
-+#define FSF_SQ_INVOKE_LINK_TEST_PROCEDURE	0x04
-+#define FSF_SQ_ULP_PROGRAMMING_ERROR		0x05
-+#define FSF_SQ_COMMAND_ABORTED			0x06
-+#define FSF_SQ_NO_RETRY_POSSIBLE		0x07
-+
-+/* FSF status qualifier for ACT download/upload commands */
-+#define FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE	0x00000001
-+#define FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2	0x00000002
-+/* ACT subtable codes */
-+#define FSF_SQ_CFDC_SUBTABLE_OS			0x0001
-+#define FSF_SQ_CFDC_SUBTABLE_PORT_WWPN		0x0002
-+#define FSF_SQ_CFDC_SUBTABLE_PORT_DID		0x0003
-+#define FSF_SQ_CFDC_SUBTABLE_LUN		0x0004
-+
-+/* FSF status qualifier (most significant 4 bytes), local link down */
-+#define FSF_PSQ_LINK_NOLIGHT			0x00000004
-+#define FSF_PSQ_LINK_WRAPPLUG			0x00000008
-+#define FSF_PSQ_LINK_NOFCP			0x00000010
-+
-+/* payload size in status read buffer */
-+#define FSF_STATUS_READ_PAYLOAD_SIZE		4032
-+
-+/* number of status read buffers that should be sent by ULP */
-+#define FSF_STATUS_READS_RECOM			16
-+
-+/* status types in status read buffer */
-+#define FSF_STATUS_READ_PORT_CLOSED		0x00000001
-+#define FSF_STATUS_READ_INCOMING_ELS		0x00000002
-+#define FSF_STATUS_READ_BIT_ERROR_THRESHOLD	0x00000004
-+#define FSF_STATUS_READ_LINK_DOWN		0x00000005	/* FIXME: really? */
-+#define FSF_STATUS_READ_LINK_UP          	0x00000006
-+#define FSF_STATUS_READ_CFDC_UPDATED          	0x0000000A
-+#define FSF_STATUS_READ_CFDC_HARDENED          	0x0000000B
-+
-+/* status subtypes in status read buffer */
-+#define FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT	0x00000001
-+#define FSF_STATUS_READ_SUB_ERROR_PORT		0x00000002
-+#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE	0x00000002
-+#define FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2	0x0000000F
-+
-+#define FSF_OPEN_LUN_SUPPRESS_BOXING		0x00000001
++		list_entry((head)->next,type,list) \
++	)
 +
-+/* topologie that is detected by the adapter */
-+#define FSF_TOPO_ERROR				0x00000000
-+#define FSF_TOPO_P2P				0x00000001
-+#define FSF_TOPO_FABRIC				0x00000002
-+#define FSF_TOPO_AL				0x00000003
-+#define FSF_TOPO_FABRIC_VIRT			0x00000004
++#define ZFCP_LAST_ENTITY(head,type) \
++	( \
++		list_empty(head) ? \
++		NULL : \
++		list_entry((head)->prev,type,list) \
++	)
 +
-+/* data direction for FCP commands */
-+#define FSF_DATADIR_WRITE			0x00000001
-+#define FSF_DATADIR_READ			0x00000002
-+#define FSF_DATADIR_READ_WRITE			0x00000003
-+#define FSF_DATADIR_CMND			0x00000004
++#define ZFCP_PREV_ENTITY(head,curr,type) \
++	( \
++		(curr == ZFCP_FIRST_ENTITY(head,type)) ? \
++		NULL : \
++		list_entry(curr->list.prev,type,list) \
++	)
 +
-+/* fc service class */
-+#define FSF_CLASS_1				0x00000001
-+#define FSF_CLASS_2				0x00000002
-+#define FSF_CLASS_3				0x00000003
++#define ZFCP_NEXT_ENTITY(head,curr,type) \
++	( \
++		(curr == ZFCP_LAST_ENTITY(head,type)) ? \
++		NULL : \
++		list_entry(curr->list.next,type,list) \
++	)
 +
-+/* SBAL chaining */
-+#define FSF_MAX_SBALS_PER_REQ			36
-+#define FSF_MAX_SBALS_PER_ELS_REQ		2
++#define ZFCP_FOR_EACH_ENTITY(head,curr,type) \
++	for (curr = ZFCP_FIRST_ENTITY(head,type); \
++	     curr; \
++	     curr = ZFCP_NEXT_ENTITY(head,curr,type)) 
 +
-+/* logging space behind QTCB */
-+#define FSF_QTCB_LOG_SIZE			1024
++/*
++ * use these macros if you traverse a list and stop iterations after
++ * altering the list since changing the list will most likely cause
++ * next/previous pointers to become unavailable,
++ * usually: examining some list elements, or removing a single
++ * element from somewhere in the middle of the list,
++ * lock the list by means of the associated rwlock before entering
++ * the loop and thus above the macro,
++ * unlock the list (the associated rwlock) after leaving the loop
++ * belonging to the macro,
++ * use read variant of lock if only looking up something without
++ * changing the list,
++ * use write variant of lock if changing the list (in last iteration !),
++ * attention: "upgrading" read lock to write lock is not supported!
++ */
 +
-+/* channel features */
-+#define FSF_FEATURE_QTCB_SUPPRESSION            0x00000001
-+#define FSF_FEATURE_CFDC                        0x00000002
-+#define FSF_FEATURE_HBAAPI_MANAGEMENT           0x00000010
-+#define FSF_FEATURE_ELS_CT_CHAINED_SBALS        0x00000020
++#define ZFCP_FOR_EACH_ADAPTER(a) \
++	ZFCP_FOR_EACH_ENTITY(&zfcp_data.adapter_list_head,(a),zfcp_adapter_t)
 +
-+/* adapter types */
-+#define FSF_ADAPTER_TYPE_FICON                  0x00000001
-+#define FSF_ADAPTER_TYPE_FICON_EXPRESS          0x00000002
++#define ZFCP_FOR_EACH_PORT(a,p) \
++	ZFCP_FOR_EACH_ENTITY(&(a)->port_list_head,(p),zfcp_port_t)
 +
-+/* port types */
-+#define FSF_HBA_PORTTYPE_UNKNOWN		0x00000001
-+#define FSF_HBA_PORTTYPE_NOTPRESENT		0x00000003
-+#define FSF_HBA_PORTTYPE_NPORT			0x00000005
-+#define FSF_HBA_PORTTYPE_PTP			0x00000021
-+/* following are not defined and used by FSF Spec
-+   but are additionally defined by FC-HBA */
-+#define FSF_HBA_PORTTYPE_OTHER			0x00000002
-+#define FSF_HBA_PORTTYPE_NOTPRESENT		0x00000003
-+#define FSF_HBA_PORTTYPE_NLPORT			0x00000006
-+#define FSF_HBA_PORTTYPE_FLPORT			0x00000007
-+#define FSF_HBA_PORTTYPE_FPORT			0x00000008
-+#define FSF_HBA_PORTTYPE_LPORT			0x00000020
++#define ZFCP_FOR_EACH_UNIT(p,u) \
++	ZFCP_FOR_EACH_ENTITY(&(p)->unit_list_head,(u),zfcp_unit_t)
 +
-+/* port states */
-+#define FSF_HBA_PORTSTATE_UNKNOWN		0x00000001
-+#define FSF_HBA_PORTSTATE_ONLINE		0x00000002
-+#define FSF_HBA_PORTSTATE_OFFLINE		0x00000003
-+#define FSF_HBA_PORTSTATE_LINKDOWN		0x00000006
-+#define FSF_HBA_PORTSTATE_ERROR			0x00000007
 +
-+/* IO states of adapter */
-+#define FSF_IOSTAT_NPORT_RJT			0x00000004
-+#define FSF_IOSTAT_FABRIC_RJT			0x00000005
-+#define FSF_IOSTAT_LS_RJT			0x00000009
++/* Note, the leftmost status byte is common among adapter, port 
++	 and unit
++ */
++#define ZFCP_COMMON_FLAGS                               0xff000000
++#define ZFCP_SPECIFIC_FLAGS                             0x00ffffff
 +
++/* common status bits */
++#define ZFCP_STATUS_COMMON_TO_BE_REMOVED		0x80000000 
++#define ZFCP_STATUS_COMMON_RUNNING			0x40000000 
++#define ZFCP_STATUS_COMMON_ERP_FAILED			0x20000000
++#define ZFCP_STATUS_COMMON_UNBLOCKED			0x10000000
++#define ZFCP_STATUS_COMMON_OPENING                      0x08000000
++#define ZFCP_STATUS_COMMON_OPEN                         0x04000000
++#define ZFCP_STATUS_COMMON_CLOSING                      0x02000000
++#define ZFCP_STATUS_COMMON_ERP_INUSE			0x01000000
 +
-+struct fsf_queue_designator;
-+struct fsf_status_read_buffer;
-+struct fsf_port_closed_payload;
-+struct fsf_bit_error_payload;
-+union fsf_prot_status_qual;
-+struct fsf_qual_version_error;
-+struct fsf_qual_sequence_error;
-+struct fsf_qtcb_prefix;
-+struct fsf_qtcb_header;
-+struct fsf_qtcb_bottom_config;
-+struct fsf_qtcb_bottom_support;
-+struct fsf_qtcb_bottom_io;
-+union fsf_qtcb_bottom;
++/* status of adapter */
++#define ZFCP_STATUS_ADAPTER_IRQOWNER			0x00000001
++#define ZFCP_STATUS_ADAPTER_QDIOUP			0x00000002
++#define ZFCP_STATUS_ADAPTER_REGISTERED			0x00000004
++#define ZFCP_STATUS_ADAPTER_XCONFIG_OK			0x00000008
++#define ZFCP_STATUS_ADAPTER_HOST_CON_INIT		0x00000010
++#define ZFCP_STATUS_ADAPTER_ERP_THREAD_UP		0x00000020
++#define ZFCP_STATUS_ADAPTER_ERP_THREAD_DONE		0x00000040
++#define ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL		0x00000080
++#define ZFCP_STATUS_ADAPTER_ERP_PENDING			0x00000100
++#define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED		0x00000200
 +
-+typedef struct fsf_queue_designator {
-+	u8			cssid;
-+	u8			chpid;
-+	u8			hla;
-+	u8			ua;
-+	u32			res1;
-+} __attribute__ ((packed)) fsf_queue_designator_t;
++#define ZFCP_STATUS_ADAPTER_SCSI_UP			\
++		(ZFCP_STATUS_COMMON_UNBLOCKED |	\
++		 ZFCP_STATUS_ADAPTER_REGISTERED)
 +
-+typedef struct fsf_port_closed_payload {
-+	fsf_queue_designator_t	queue_designator;
-+	u32			port_handle;
-+} __attribute__ ((packed)) fsf_port_closed_payload_t;
++#define ZFCP_DID_NAMESERVER				0xFFFFFC
 +
-+typedef struct fsf_bit_error_payload {
-+	u32			res1;
-+	u32			link_failure_error_count;
-+	u32			loss_of_sync_error_count;
-+	u32			loss_of_signal_error_count;
-+	u32			primitive_sequence_error_count;
-+	u32			invalid_transmission_word_error_count;
-+	u32			crc_error_count;
-+	u32			primitive_sequence_event_timeout_count;
-+	u32			elastic_buffer_overrun_error_count;
-+	u32			fcal_arbitration_timeout_count;
-+	u32			advertised_receive_b2b_credit;
-+	u32			current_receive_b2b_credit;
-+	u32			advertised_transmit_b2b_credit;
-+	u32			current_transmit_b2b_credit;
-+} __attribute__ ((packed)) fsf_bit_error_payload_t;
++/* status of remote port */
++#define ZFCP_STATUS_PORT_PHYS_OPEN			0x00000001
++#define ZFCP_STATUS_PORT_DID_DID			0x00000002
++#define ZFCP_STATUS_PORT_PHYS_CLOSING			0x00000004
++#define ZFCP_STATUS_PORT_NO_WWPN			0x00000008
++#define ZFCP_STATUS_PORT_NO_SCSI_ID			0x00000010
++#define ZFCP_STATUS_PORT_INVALID_WWPN			0x00000020
 +
-+typedef struct fsf_status_read_buffer {
-+        u32			status_type;
-+        u32			status_subtype;
-+        u32			length;
-+        u32			res1;
-+        fsf_queue_designator_t	queue_designator;
-+        u32			d_id;
-+        u32			class;
-+        u64			fcp_lun;
-+        u8			res3[24];
-+        u8			payload[FSF_STATUS_READ_PAYLOAD_SIZE];
-+} __attribute__ ((packed)) fsf_status_read_buffer_t;
++#define ZFCP_STATUS_PORT_NAMESERVER \
++		(ZFCP_STATUS_PORT_NO_WWPN | \
++		 ZFCP_STATUS_PORT_NO_SCSI_ID)
 +
-+typedef struct fsf_qual_version_error {
-+	u32			fsf_version;
-+	u32			res1[3];
-+} __attribute__ ((packed)) fsf_qual_version_error_t;
++/* status of logical unit */
++#define ZFCP_STATUS_UNIT_NOTSUPPUNITRESET		0x00000001
++#define ZFCP_STATUS_UNIT_ASSUMETCQ			0x00000002
 +
-+typedef struct fsf_qual_sequence_error {
-+	u32			exp_req_seq_no;
-+	u32			res1[3];
-+} __attribute__ ((packed)) fsf_qual_sequence_error_t;
++/* no common part here */
++/* status of FSF request */
++#define ZFCP_STATUS_FSFREQ_NOT_INIT			0x00000000
++#define ZFCP_STATUS_FSFREQ_POOL  			0x00000001
++#define ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT		0x00000002
++#define ZFCP_STATUS_FSFREQ_COMPLETED			0x00000004
++#define ZFCP_STATUS_FSFREQ_ERROR			0x00000008
++#define ZFCP_STATUS_FSFREQ_CLEANUP			0x00000010
++#define ZFCP_STATUS_FSFREQ_ABORTING			0x00000020
++#define ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED		0x00000040
++#define ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED		0x00000080
++#define ZFCP_STATUS_FSFREQ_ABORTED			0x00000100
++#define ZFCP_STATUS_FSFREQ_TMFUNCFAILED			0x00000200
++#define ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP		0x00000400
++#define ZFCP_STATUS_FSFREQ_RETRY			0x00000800
++#define ZFCP_STATUS_FSFREQ_DISMISSED			0x00001000
++#define ZFCP_STATUS_FSFREQ_POOLBUF			0x00002000
 +
-+typedef struct fsf_qual_locallink_error {
-+	u32			code;
-+	u32			res1[3];
-+} __attribute__ ((packed)) fsf_qual_locallink_error_t;
++#define ZFCP_KNOWN                              0x00000001
++#define ZFCP_REQ_AUTO_CLEANUP			0x00000002
++#define ZFCP_WAIT_FOR_SBAL			0x00000004
++#define ZFCP_REQ_USE_MEMPOOL			0x00000008
 +
-+typedef union fsf_prot_status_qual {
-+	fsf_qual_version_error_t	version_error;
-+	fsf_qual_sequence_error_t	sequence_error;
-+	fsf_qual_locallink_error_t	locallink_error;
-+} __attribute__ ((packed)) fsf_prot_status_qual_t;
++/* Mask parameters */
++#define ZFCP_SET                                0x00000100
++#define ZFCP_CLEAR                              0x00000200
 +
-+typedef struct fsf_qtcb_prefix {
-+	u64			req_id;
-+	u32			qtcb_version;
-+	u32			ulp_info;
-+	u32			qtcb_type;
-+	u32			req_seq_no;
-+	u32			prot_status;
-+	fsf_prot_status_qual_t	prot_status_qual;
-+	u8			res1[20];
-+} __attribute__ ((packed)) fsf_qtcb_prefix_t;
++#define ZFCP_INTERRUPTIBLE			1
++#define ZFCP_UNINTERRUPTIBLE			0
 +
-+typedef union fsf_status_qual {
-+#define FSF_STATUS_QUAL_SIZE	16
-+	u8			byte[FSF_STATUS_QUAL_SIZE];
-+	u16			halfword[FSF_STATUS_QUAL_SIZE / sizeof(u16)];
-+	u32			word[FSF_STATUS_QUAL_SIZE / sizeof(u32)];
-+	fsf_queue_designator_t	fsf_queue_designator;
-+} __attribute__ ((packed)) fsf_status_qual_t;
++#define ZFCP_MAX_ERPS                           3
 +
-+typedef struct fsf_qtcb_header {
-+	u64			req_handle;
-+	u32			fsf_command;
-+	u32			res1;
-+	u32			port_handle;
-+	u32			lun_handle;
-+	u32			res2;
-+	u32			fsf_status;
-+	fsf_status_qual_t	fsf_status_qual;
-+	u8			res3[28];
-+	u16			log_start;
-+	u16			log_length;
-+	u8			res4[16];
-+} __attribute__ ((packed)) fsf_qtcb_header_t;
++#define ZFCP_ERP_FSFREQ_TIMEOUT			(100 * HZ)
++#define ZFCP_ERP_MEMWAIT_TIMEOUT		HZ
 +
-+typedef u64 fsf_wwn_t;
++#define ZFCP_STATUS_ERP_TIMEDOUT		0x10000000
++#define ZFCP_STATUS_ERP_CLOSE_ONLY		0x01000000
++#define ZFCP_STATUS_ERP_DISMISSING		0x00100000
++#define ZFCP_STATUS_ERP_DISMISSED		0x00200000
 +
-+typedef struct fsf_nport_serv_param {
-+	u8			common_serv_param[16];
-+	fsf_wwn_t		wwpn;
-+	fsf_wwn_t		wwnn;
-+	u8			class1_serv_param[16];
-+	u8			class2_serv_param[16];
-+	u8			class3_serv_param[16];
-+	u8			class4_serv_param[16];
-+	u8			vendor_version_level[16];
-+	u8			res1[16];
-+} __attribute__ ((packed)) fsf_nport_serv_param_t;
++#define ZFCP_ERP_STEP_UNINITIALIZED		0x00000000
++#define ZFCP_ERP_STEP_FSF_XCONFIG		0x00000001
++#define ZFCP_ERP_STEP_PHYS_PORT_CLOSING		0x00000010
++#define ZFCP_ERP_STEP_PORT_CLOSING		0x00000100
++#define ZFCP_ERP_STEP_NAMESERVER_OPEN		0x00000200
++#define ZFCP_ERP_STEP_NAMESERVER_LOOKUP		0x00000400
++#define ZFCP_ERP_STEP_PORT_OPENING		0x00000800
++#define ZFCP_ERP_STEP_UNIT_CLOSING		0x00001000
++#define ZFCP_ERP_STEP_UNIT_OPENING		0x00002000
 +
-+typedef struct fsf_plogi {
-+	u32			code;
-+	fsf_nport_serv_param_t	serv_param;
-+} __attribute__ ((packed)) fsf_plogi_t;
++/* ordered ! */
++#define ZFCP_ERP_ACTION_REOPEN_ADAPTER		0x4
++#define ZFCP_ERP_ACTION_REOPEN_PORT_FORCED	0x3
++#define ZFCP_ERP_ACTION_REOPEN_PORT		0x2
++#define ZFCP_ERP_ACTION_REOPEN_UNIT		0x1
 +
-+#define FSF_FCP_CMND_SIZE	288
-+#define FSF_FCP_RSP_SIZE	128
++#define ZFCP_ERP_ACTION_RUNNING			0x1
++#define ZFCP_ERP_ACTION_READY			0x2
 +
-+typedef struct fsf_qtcb_bottom_io {
-+	u32			data_direction;
-+	u32			service_class;
-+	u8			res1[8];
-+	u32			fcp_cmnd_length;
-+	u8			res2[12];
-+	u8			fcp_cmnd[FSF_FCP_CMND_SIZE];
-+	u8			fcp_rsp[FSF_FCP_RSP_SIZE];
-+	u8			res3[64];
-+} __attribute__ ((packed)) fsf_qtcb_bottom_io_t;
++#define ZFCP_ERP_SUCCEEDED	0x0
++#define ZFCP_ERP_FAILED		0x1
++#define ZFCP_ERP_CONTINUES	0x2
++#define ZFCP_ERP_EXIT		0x3
++#define ZFCP_ERP_DISMISSED	0x4
++#define ZFCP_ERP_NOMEM		0x5
 +
-+typedef struct fsf_qtcb_bottom_support {
-+	u32			op_subtype;
-+	u8			res1[12];
-+	u32			d_id;
-+	u32			option;
-+	u64			fcp_lun;
-+	u64			res2;
-+	u64			req_handle;
-+	u32			service_class;
-+	u8			res3[3];
-+	u8			timeout;
-+	u8			res4[184];
-+	u32			els1_length;
-+	u32			els2_length;
-+	u32			req_buf_length;
-+	u32			resp_buf_length;
-+	u8			els[256];
-+} __attribute__ ((packed)) fsf_qtcb_bottom_support_t;
++/* task attribute values in FCP-2 FCP_CMND IU */
++#define SIMPLE_Q	0
++#define HEAD_OF_Q	1
++#define ORDERED_Q	2
++#define ACA_Q		4
++#define UNTAGGED	5
 +
-+typedef struct fsf_qtcb_bottom_config {
-+	u32			lic_version;
-+	u32			feature_selection;
-+	u32			high_qtcb_version;
-+	u32			low_qtcb_version;
-+	u32			max_qtcb_size;
-+	u32			max_data_transfer_size;
-+	u32			supported_features;
-+	u8			res1[4];
-+	u32			fc_topology;
-+	u32			fc_link_speed;
-+	u32			adapter_type;
-+	u32			peer_d_id;
-+	u8			res2[12];
-+	u32			s_id;
-+	fsf_nport_serv_param_t	nport_serv_param;
-+	u8			res3[8];
-+	u32			adapter_ports;
-+	u32			hardware_version;
-+	u8			serial_number[32];
-+	u8			res4[272];
-+} __attribute__ ((packed)) fsf_qtcb_bottom_config_t;
++/* task management flags in FCP-2 FCP_CMND IU */
++#define CLEAR_ACA		0x40
++#define TARGET_RESET		0x20
++#define LOGICAL_UNIT_RESET	0x10
++#define CLEAR_TASK_SET		0x04
++#define ABORT_TASK_SET		0x02
 +
-+typedef struct fsf_qtcb_bottom_port {
-+	u8 res1[8];
-+	u32 fc_port_id;
-+	u32 port_type;
-+	u32 port_state;
-+	u32 class_of_service; /* should be 0x00000006 for class 2 and 3 */
-+	u8 supported_fc4_types[32]; /* should be 0x00000100 for scsi fcp */
-+	u8 active_fc4_types[32];
-+	u32 supported_speed; /* 0x0001 for 1 GBit/s or 0x0002 for 2 GBit/s */
-+	u32 maximum_frame_size; /* fixed value of 2112 */
-+	u64 seconds_since_last_reset;
-+	u64 tx_frames;
-+	u64 tx_words;
-+	u64 rx_frames;
-+	u64 rx_words;
-+	u64 lip; /* 0 */
-+	u64 nos; /* currently 0 */
-+	u64 error_frames; /* currently 0 */
-+	u64 dumped_frames; /* currently 0 */
-+	u64 link_failure;
-+	u64 loss_of_sync;
-+	u64 loss_of_signal;
-+	u64 psp_error_counts;
-+	u64 invalid_tx_words;
-+	u64 invalid_crcs;
-+	u64 input_requests;
-+	u64 output_requests;
-+	u64 control_requests;
-+	u64 input_mb; /* where 1 MByte == 1.000.000 Bytes */
-+	u64 output_mb; /* where 1 MByte == 1.000.000 Bytes */
-+	u8 res2[256];
-+} __attribute__ ((packed)) fsf_qtcb_bottom_port_t;
++#define FCP_CDB_LENGTH		16
 +
-+typedef union fsf_qtcb_bottom {
-+	fsf_qtcb_bottom_io_t		io;
-+	fsf_qtcb_bottom_support_t	support;
-+	fsf_qtcb_bottom_config_t	config;
-+	fsf_qtcb_bottom_port_t		port;
-+} fsf_qtcb_bottom_t;
 +
-+typedef struct fsf_qtcb {
-+	fsf_qtcb_prefix_t	prefix;
-+	fsf_qtcb_header_t	header;
-+	fsf_qtcb_bottom_t	bottom;
-+	u8			log[FSF_QTCB_LOG_SIZE];
-+} __attribute__ ((packed)) fsf_qtcb_t;
++/* some magics which may be used to authenticate data structures */
++#define ZFCP_MAGIC		0xFCFCFCFC
++#define ZFCP_MAGIC_ADAPTER	0xAAAAAAAA
++#define ZFCP_MAGIC_PORT		0xBBBBBBBB
++#define ZFCP_MAGIC_UNIT		0xCCCCCCCC
++#define ZFCP_MAGIC_FSFREQ	0xEEEEEEEE
 +
-+#endif	/* FSF_H */
++/* function prototypes */
++int zfcp_erp_wait(zfcp_adapter_t*);
++int zfcp_fsf_exchange_port_data(zfcp_adapter_t*, fsf_qtcb_bottom_port_t*);
++int zfcp_fsf_send_els(struct zfcp_send_els *);
++int zfcp_config_parse_record_add(zfcp_config_record_t*);
++int zfcp_scsi_command_sync(zfcp_unit_t *, Scsi_Cmnd *);
++int zfcp_ns_ga_nxt_request(zfcp_port_t *, struct ct_iu_ga_nxt *);
++int zfcp_fsf_send_ct(struct zfcp_send_ct *, zfcp_mem_pool_t *,
++		     zfcp_erp_action_t *);
++extern int  zfcp_check_ct_response(struct ct_hdr *);
++extern int  zfcp_handle_els_rjt(u32, struct zfcp_ls_rjt_par *);
 +
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zfcp_main.c drivers/s390/scsi/zfcp_main.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zfcp_main.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/scsi/zfcp_main.c	2006-01-30 22:25:25.000000000 -0700
++#endif /* _ZFCP_H_ */
+diff -urN kernel-source-2.4.27.orig/drivers/s390/scsi/zfcp_main.c drivers/s390/scsi/zfcp_main.c
+--- kernel-source-2.4.27.orig/drivers/s390/scsi/zfcp_main.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/scsi/zfcp_main.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,21393 @@
 +/*
 + * FCP adapter driver for IBM eServer zSeries
@@ -78035,9 +78002,9 @@
 + * tab-width: 8
 + * End:
 + */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zfcp_zh.c drivers/s390/scsi/zfcp_zh.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zfcp_zh.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/scsi/zfcp_zh.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/scsi/zfcp_zh.c drivers/s390/scsi/zfcp_zh.c
+--- kernel-source-2.4.27.orig/drivers/s390/scsi/zfcp_zh.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/scsi/zfcp_zh.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,951 @@
 +/* 
 + * $Id: zfcp_zh.c,v 1.3.2.4 2004/09/20 16:20:30 aherrman Exp $
@@ -78990,9 +78957,9 @@
 +EXPORT_SYMBOL(zfcp_zh_send_els);
 +EXPORT_SYMBOL(zfcp_zh_send_scsi);
 +EXPORT_SYMBOL(zfcp_zh_assert_fclun_zero);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zfcp_zh.h drivers/s390/scsi/zfcp_zh.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zfcp_zh.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/scsi/zfcp_zh.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/scsi/zfcp_zh.h drivers/s390/scsi/zfcp_zh.h
+--- kernel-source-2.4.27.orig/drivers/s390/scsi/zfcp_zh.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/scsi/zfcp_zh.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,171 @@
 +/* 
 + * $Id: zfcp_zh.h,v 1.3.2.1 2004/01/26 17:26:34 mschwide Exp $
@@ -79165,9 +79132,9 @@
 +void zfcp_callback_do_link_up(const zfcp_adapter_t *);
 +
 +#endif /* _ZFCP_ZH_H_ */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zh.h drivers/s390/scsi/zh.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zh.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/scsi/zh.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/scsi/zh.h drivers/s390/scsi/zh.h
+--- kernel-source-2.4.27.orig/drivers/s390/scsi/zh.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/scsi/zh.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,512 @@
 +/* 
 + * $Id: zh.h,v 1.7.2.2 2004/03/24 11:18:00 aherrman Exp $
@@ -79681,9 +79648,9 @@
 +int zh_report_luns_helper(struct zh_scsi_report_luns *);
 +
 +#endif /* _ZH_H_ */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zh_ioctl32.c drivers/s390/scsi/zh_ioctl32.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zh_ioctl32.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/scsi/zh_ioctl32.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/scsi/zh_ioctl32.c drivers/s390/scsi/zh_ioctl32.c
+--- kernel-source-2.4.27.orig/drivers/s390/scsi/zh_ioctl32.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/scsi/zh_ioctl32.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,248 @@
 +/* 
 + * $Id: zh_ioctl32.c,v 1.4.2.2 2004/03/24 11:18:00 aherrman Exp $
@@ -79933,9 +79900,9 @@
 +{
 +	return do_register();
 +}
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zh_ioctl32.h drivers/s390/scsi/zh_ioctl32.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zh_ioctl32.h	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/scsi/zh_ioctl32.h	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/scsi/zh_ioctl32.h drivers/s390/scsi/zh_ioctl32.h
+--- kernel-source-2.4.27.orig/drivers/s390/scsi/zh_ioctl32.h	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/scsi/zh_ioctl32.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,26 @@
 +/* 
 + * $Id: zh_ioctl32.h,v 1.2.2.1 2004/01/26 17:26:34 mschwide Exp $
@@ -79963,9 +79930,9 @@
 +int zh_unregister_ioctl_conversion(void);
 +
 +#endif /* _ZH_IOCTL32_H_ */
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zh_main.c drivers/s390/scsi/zh_main.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zh_main.c	1969-12-31 17:00:00.000000000 -0700
-+++ drivers/s390/scsi/zh_main.c	2006-01-30 22:25:25.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/s390/scsi/zh_main.c drivers/s390/scsi/zh_main.c
+--- kernel-source-2.4.27.orig/drivers/s390/scsi/zh_main.c	1969-12-31 17:00:00.000000000 -0700
++++ drivers/s390/scsi/zh_main.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,2046 @@
 +/* 
 + * $Id: zh_main.c,v 1.10.2.3 2004/09/17 08:29:41 aherrman Exp $
@@ -82013,9 +81980,9 @@
 +
 +module_init(zh_init);
 +module_exit(zh_exit);
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/Config.in drivers/scsi/Config.in
---- kernel-source-2.4.27-2.4.27.orig/drivers/scsi/Config.in	2006-01-30 22:23:43.000000000 -0700
-+++ drivers/scsi/Config.in	2006-01-30 22:25:26.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/scsi/Config.in drivers/scsi/Config.in
+--- kernel-source-2.4.27.orig/drivers/scsi/Config.in	2006-02-08 04:06:22.000000000 -0700
++++ drivers/scsi/Config.in	2006-02-12 12:47:24.000000000 -0700
 @@ -44,6 +44,8 @@
  if [ "$CONFIG_PCI" = "y" ]; then
     dep_tristate '3ware Hardware ATA-RAID support' CONFIG_BLK_DEV_3W_XXXX_RAID $CONFIG_SCSI
@@ -82039,9 +82006,9 @@
  endmenu
  
  if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/hosts.c drivers/scsi/hosts.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/scsi/hosts.c	2003-06-13 08:51:36.000000000 -0600
-+++ drivers/scsi/hosts.c	2006-01-30 22:25:26.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/scsi/hosts.c drivers/scsi/hosts.c
+--- kernel-source-2.4.27.orig/drivers/scsi/hosts.c	2003-06-13 08:51:36.000000000 -0600
++++ drivers/scsi/hosts.c	2006-02-12 12:47:24.000000000 -0700
 @@ -88,6 +88,24 @@
  scsi_unregister(struct Scsi_Host * sh){
      struct Scsi_Host * shpnt;
@@ -82149,9 +82116,9 @@
      return retval;
  }
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/hosts.h drivers/scsi/hosts.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/scsi/hosts.h	2003-06-13 08:51:36.000000000 -0600
-+++ drivers/scsi/hosts.h	2006-01-30 22:25:26.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/scsi/hosts.h drivers/scsi/hosts.h
+--- kernel-source-2.4.27.orig/drivers/scsi/hosts.h	2003-06-13 08:51:36.000000000 -0600
++++ drivers/scsi/hosts.h	2006-02-12 12:47:24.000000000 -0700
 @@ -471,7 +471,10 @@
  
  extern Scsi_Host_Template * scsi_hosts;
@@ -82164,9 +82131,9 @@
  
  /*
   *  scsi_init initializes the scsi hosts.
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi.c drivers/scsi/scsi.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi.c	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/scsi/scsi.c	2006-01-30 22:25:26.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/scsi/scsi.c drivers/scsi/scsi.c
+--- kernel-source-2.4.27.orig/drivers/scsi/scsi.c	2003-08-25 05:44:42.000000000 -0600
++++ drivers/scsi/scsi.c	2006-02-12 12:47:24.000000000 -0700
 @@ -537,22 +537,10 @@
  				   SCpnt->target,
  				   atomic_read(&SCpnt->host->host_active),
@@ -82371,9 +82338,9 @@
  	}
  
  	/*
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi.h drivers/scsi/scsi.h
---- kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi.h	2003-08-25 05:44:42.000000000 -0600
-+++ drivers/scsi/scsi.h	2006-01-30 22:25:26.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/scsi/scsi.h drivers/scsi/scsi.h
+--- kernel-source-2.4.27.orig/drivers/scsi/scsi.h	2003-08-25 05:44:42.000000000 -0600
++++ drivers/scsi/scsi.h	2006-02-12 12:47:24.000000000 -0700
 @@ -18,6 +18,7 @@
  #include <linux/config.h>	/* for CONFIG_SCSI_LOGGING */
  #include <linux/devfs_fs_kernel.h>
@@ -82382,9 +82349,9 @@
  
  /*
   * Some of the public constants are being moved to this file.
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi_lib.c drivers/scsi/scsi_lib.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi_lib.c	2004-04-14 07:05:31.000000000 -0600
-+++ drivers/scsi/scsi_lib.c	2006-01-30 22:25:26.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/scsi/scsi_lib.c drivers/scsi/scsi_lib.c
+--- kernel-source-2.4.27.orig/drivers/scsi/scsi_lib.c	2004-04-14 07:05:31.000000000 -0600
++++ drivers/scsi/scsi_lib.c	2006-02-12 12:47:24.000000000 -0700
 @@ -256,12 +256,32 @@
  	if (SCpnt != NULL) {
  
@@ -82418,9 +82385,9 @@
  		list_add(&SCpnt->request.queue, &q->queue_head);
  	}
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi_proc.c drivers/scsi/scsi_proc.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi_proc.c	2003-06-13 08:51:36.000000000 -0600
-+++ drivers/scsi/scsi_proc.c	2006-01-30 22:25:26.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/scsi/scsi_proc.c drivers/scsi/scsi_proc.c
+--- kernel-source-2.4.27.orig/drivers/scsi/scsi_proc.c	2003-06-13 08:51:36.000000000 -0600
++++ drivers/scsi/scsi_proc.c	2006-02-12 12:47:24.000000000 -0700
 @@ -120,35 +120,34 @@
  	return(ret);
  }
@@ -82482,9 +82449,9 @@
  }
  
  /*
-diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi_queue.c drivers/scsi/scsi_queue.c
---- kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi_queue.c	2001-02-09 12:30:23.000000000 -0700
-+++ drivers/scsi/scsi_queue.c	2006-01-30 22:25:26.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/drivers/scsi/scsi_queue.c drivers/scsi/scsi_queue.c
+--- kernel-source-2.4.27.orig/drivers/scsi/scsi_queue.c	2001-02-09 12:30:23.000000000 -0700
++++ drivers/scsi/scsi_queue.c	2006-02-12 12:47:24.000000000 -0700
 @@ -103,7 +103,7 @@
  		 * If a host is inactive and cannot queue any commands, I don't see
  		 * how things could possibly work anyways.
@@ -82503,9 +82470,9 @@
  			if (scsi_retry_command(cmd) == 0) {
  				return 0;
  			}
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/Config.in fs/Config.in
---- kernel-source-2.4.27-2.4.27.orig/fs/Config.in	2006-01-30 22:23:45.000000000 -0700
-+++ fs/Config.in	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/Config.in fs/Config.in
+--- kernel-source-2.4.27.orig/fs/Config.in	2006-02-08 04:06:25.000000000 -0700
++++ fs/Config.in	2006-02-12 12:47:24.000000000 -0700
 @@ -100,6 +100,8 @@
  
  tristate 'ROM file system support' CONFIG_ROMFS_FS
@@ -82515,9 +82482,9 @@
  tristate 'Second extended fs support' CONFIG_EXT2_FS
  dep_mbool '  Ext2 extended attributes' CONFIG_EXT2_FS_XATTR $CONFIG_EXT2_FS
  dep_bool '    Ext2 extended attribute block sharing' \
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/partitions/check.c fs/partitions/check.c
---- kernel-source-2.4.27-2.4.27.orig/fs/partitions/check.c	2004-02-18 06:36:31.000000000 -0700
-+++ fs/partitions/check.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/partitions/check.c fs/partitions/check.c
+--- kernel-source-2.4.27.orig/fs/partitions/check.c	2004-02-18 06:36:31.000000000 -0700
++++ fs/partitions/check.c	2006-02-12 12:47:24.000000000 -0700
 @@ -89,7 +89,7 @@
  #ifdef CONFIG_ARCH_S390
  int (*genhd_dasd_name)(char*,int,int,struct gendisk*) = NULL;
@@ -82682,9 +82649,9 @@
  unsigned char *read_dev_sector(struct block_device *bdev, unsigned long n, Sector *p)
  {
  	struct address_space *mapping = bdev->bd_inode->i_mapping;
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/partitions/check.h fs/partitions/check.h
---- kernel-source-2.4.27-2.4.27.orig/fs/partitions/check.h	2001-10-01 21:03:26.000000000 -0600
-+++ fs/partitions/check.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/partitions/check.h fs/partitions/check.h
+--- kernel-source-2.4.27.orig/fs/partitions/check.h	2001-10-01 21:03:26.000000000 -0600
++++ fs/partitions/check.h	2006-02-12 12:47:24.000000000 -0700
 @@ -13,4 +13,6 @@
  	page_cache_release(p.v);
  }
@@ -82692,9 +82659,9 @@
 +void register_disk_label(struct gendisk *hd, int minor, char *label);
 +
  extern int warn_no_part;
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/partitions/ibm.c fs/partitions/ibm.c
---- kernel-source-2.4.27-2.4.27.orig/fs/partitions/ibm.c	2002-08-02 18:39:45.000000000 -0600
-+++ fs/partitions/ibm.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/partitions/ibm.c fs/partitions/ibm.c
+--- kernel-source-2.4.27.orig/fs/partitions/ibm.c	2002-08-02 18:39:45.000000000 -0600
++++ fs/partitions/ibm.c	2006-02-12 12:47:24.000000000 -0700
 @@ -9,6 +9,7 @@
   * 07/10/00 Fixed detection of CMS formatted disks     
   * 02/13/00 VTOC partition support added
@@ -82732,29 +82699,9 @@
  		/*
  		 * New style VOL1 labeled disk
  		 */
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/Makefile fs/xip2fs/Makefile
---- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/Makefile	1969-12-31 17:00:00.000000000 -0700
-+++ fs/xip2fs/Makefile	2006-01-30 22:25:23.000000000 -0700
-@@ -0,0 +1,16 @@
-+#
-+# Makefile for the linux xip2fs-filesystem routines.
-+#
-+# Note! Dependencies are done automagically by 'make dep', which also
-+# removes any old dependencies. DON'T put your own dependencies here
-+# unless it's something special (ie not a .c file).
-+#
-+# Note 2! The CFLAGS definitions are now in the main makefile...
-+
-+O_TARGET := xip2fs.o
-+
-+obj-y    := balloc.o dir.o file.o ialloc.o inode.o \
-+		ioctl.o namei.o super.o symlink.o
-+obj-m    := $(O_TARGET)
-+
-+include $(TOPDIR)/Rules.make
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/balloc.c fs/xip2fs/balloc.c
---- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/balloc.c	1969-12-31 17:00:00.000000000 -0700
-+++ fs/xip2fs/balloc.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/xip2fs/balloc.c fs/xip2fs/balloc.c
+--- kernel-source-2.4.27.orig/fs/xip2fs/balloc.c	1969-12-31 17:00:00.000000000 -0700
++++ fs/xip2fs/balloc.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,83 @@
 +/*
 + *  linux/fs/xip2/balloc.c, Version 1
@@ -82839,9 +82786,9 @@
 +	}
 +	return (void*)(mem_area->start + block*size);
 +}
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/dir.c fs/xip2fs/dir.c
---- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/dir.c	1969-12-31 17:00:00.000000000 -0700
-+++ fs/xip2fs/dir.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/xip2fs/dir.c fs/xip2fs/dir.c
+--- kernel-source-2.4.27.orig/fs/xip2fs/dir.c	1969-12-31 17:00:00.000000000 -0700
++++ fs/xip2fs/dir.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,302 @@
 +/*
 + *  linux/fs/xip2/dir.c, Version 1
@@ -83145,9 +83092,9 @@
 +	readdir:	xip2_readdir,
 +	ioctl:		xip2_ioctl,
 +};
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/file.c fs/xip2fs/file.c
---- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/file.c	1969-12-31 17:00:00.000000000 -0700
-+++ fs/xip2fs/file.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/xip2fs/file.c fs/xip2fs/file.c
+--- kernel-source-2.4.27.orig/fs/xip2fs/file.c	1969-12-31 17:00:00.000000000 -0700
++++ fs/xip2fs/file.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,169 @@
 +/*
 + *  linux/fs/xip2/file.c, Version 1
@@ -83318,9 +83265,9 @@
 +}
 +	return retval;
 +}
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/ialloc.c fs/xip2fs/ialloc.c
---- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/ialloc.c	1969-12-31 17:00:00.000000000 -0700
-+++ fs/xip2fs/ialloc.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/xip2fs/ialloc.c fs/xip2fs/ialloc.c
+--- kernel-source-2.4.27.orig/fs/xip2fs/ialloc.c	1969-12-31 17:00:00.000000000 -0700
++++ fs/xip2fs/ialloc.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,34 @@
 +/*
 + *  linux/fs/xip2/ialloc.c, Version 1
@@ -83356,9 +83303,9 @@
 +{
 +	return le32_to_cpu(sb->u.xip2_sb.s_es->s_free_inodes_count);
 +}
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/inode.c fs/xip2fs/inode.c
---- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/inode.c	1969-12-31 17:00:00.000000000 -0700
-+++ fs/xip2fs/inode.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/xip2fs/inode.c fs/xip2fs/inode.c
+--- kernel-source-2.4.27.orig/fs/xip2fs/inode.c	1969-12-31 17:00:00.000000000 -0700
++++ fs/xip2fs/inode.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,382 @@
 +/*
 + *  linux/fs/xip2/inode.c, Version 1
@@ -83742,9 +83689,9 @@
 +	make_bad_inode(inode);
 +	return;
 +}
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/ioctl.c fs/xip2fs/ioctl.c
---- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/ioctl.c	1969-12-31 17:00:00.000000000 -0700
-+++ fs/xip2fs/ioctl.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/xip2fs/ioctl.c fs/xip2fs/ioctl.c
+--- kernel-source-2.4.27.orig/fs/xip2fs/ioctl.c	1969-12-31 17:00:00.000000000 -0700
++++ fs/xip2fs/ioctl.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,31 @@
 +/*
 + *  linux/fs/xip2/ioctl.c, Version 1
@@ -83777,9 +83724,29 @@
 +		return -ENOTTY;
 +	}
 +}
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/namei.c fs/xip2fs/namei.c
---- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/namei.c	1969-12-31 17:00:00.000000000 -0700
-+++ fs/xip2fs/namei.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/xip2fs/Makefile fs/xip2fs/Makefile
+--- kernel-source-2.4.27.orig/fs/xip2fs/Makefile	1969-12-31 17:00:00.000000000 -0700
++++ fs/xip2fs/Makefile	2006-02-12 12:47:24.000000000 -0700
+@@ -0,0 +1,16 @@
++#
++# Makefile for the linux xip2fs-filesystem routines.
++#
++# Note! Dependencies are done automagically by 'make dep', which also
++# removes any old dependencies. DON'T put your own dependencies here
++# unless it's something special (ie not a .c file).
++#
++# Note 2! The CFLAGS definitions are now in the main makefile...
++
++O_TARGET := xip2fs.o
++
++obj-y    := balloc.o dir.o file.o ialloc.o inode.o \
++		ioctl.o namei.o super.o symlink.o
++obj-m    := $(O_TARGET)
++
++include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27.orig/fs/xip2fs/namei.c fs/xip2fs/namei.c
+--- kernel-source-2.4.27.orig/fs/xip2fs/namei.c	1969-12-31 17:00:00.000000000 -0700
++++ fs/xip2fs/namei.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,38 @@
 +/*
 + *  linux/fs/xip2/namei.c, Version 1
@@ -83819,9 +83786,9 @@
 +struct inode_operations xip2_dir_inode_operations = {
 +	lookup:		xip2_lookup,
 +};
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/super.c fs/xip2fs/super.c
---- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/super.c	1969-12-31 17:00:00.000000000 -0700
-+++ fs/xip2fs/super.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/xip2fs/super.c fs/xip2fs/super.c
+--- kernel-source-2.4.27.orig/fs/xip2fs/super.c	1969-12-31 17:00:00.000000000 -0700
++++ fs/xip2fs/super.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,661 @@
 +/*
 + *  linux/fs/xip2/super.c, Version 1
@@ -84484,9 +84451,9 @@
 +
 +module_init(init_xip2_fs)
 +module_exit(exit_xip2_fs)
-diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/symlink.c fs/xip2fs/symlink.c
---- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/symlink.c	1969-12-31 17:00:00.000000000 -0700
-+++ fs/xip2fs/symlink.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/fs/xip2fs/symlink.c fs/xip2fs/symlink.c
+--- kernel-source-2.4.27.orig/fs/xip2fs/symlink.c	1969-12-31 17:00:00.000000000 -0700
++++ fs/xip2fs/symlink.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,66 @@
 +/*
 + *  linux/fs/xip2/symlink.c, Version 1
@@ -84554,9 +84521,9 @@
 +	readlink:	xip2_readlink,
 +	follow_link:	xip2_follow_link,
 +};
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-alpha/pgalloc.h include/asm-alpha/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-alpha/pgalloc.h	2002-08-02 18:39:45.000000000 -0600
-+++ include/asm-alpha/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-alpha/pgalloc.h include/asm-alpha/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-alpha/pgalloc.h	2002-08-02 18:39:45.000000000 -0600
++++ include/asm-alpha/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -347,4 +347,6 @@
  
  extern int do_check_pgt_cache(int, int);
@@ -84564,9 +84531,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif /* _ALPHA_PGALLOC_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-arm/pgalloc.h include/asm-arm/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-arm/pgalloc.h	2001-08-12 12:14:00.000000000 -0600
-+++ include/asm-arm/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-arm/pgalloc.h include/asm-arm/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-arm/pgalloc.h	2001-08-12 12:14:00.000000000 -0600
++++ include/asm-arm/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -138,4 +138,6 @@
  
  extern int do_check_pgt_cache(int, int);
@@ -84574,9 +84541,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-generic/pgalloc.h include/asm-generic/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-generic/pgalloc.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-generic/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-generic/pgalloc.h include/asm-generic/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-generic/pgalloc.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-generic/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,37 @@
 +#ifndef _ASM_GENERIC_PGALLOC_H
 +#define _ASM_GENERIC_PGALLOC_H
@@ -84615,9 +84582,9 @@
 +}
 +
 +#endif /* _ASM_GENERIC_PGALLOC_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-i386/pgalloc.h include/asm-i386/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-i386/pgalloc.h	2003-08-25 05:44:43.000000000 -0600
-+++ include/asm-i386/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-i386/pgalloc.h include/asm-i386/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-i386/pgalloc.h	2003-08-25 05:44:43.000000000 -0600
++++ include/asm-i386/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -235,4 +235,6 @@
  	flush_tlb_mm(mm);
  }
@@ -84625,9 +84592,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif /* _I386_PGALLOC_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-ia64/pgalloc.h include/asm-ia64/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-ia64/pgalloc.h	2003-06-13 08:51:38.000000000 -0600
-+++ include/asm-ia64/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-ia64/pgalloc.h include/asm-ia64/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-ia64/pgalloc.h	2003-06-13 08:51:38.000000000 -0600
++++ include/asm-ia64/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -297,4 +297,6 @@
  	set_bit(PG_arch_1, &page->flags);	/* mark page as clean */
  }
@@ -84635,9 +84602,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif /* _ASM_IA64_PGALLOC_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-m68k/pgalloc.h include/asm-m68k/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-m68k/pgalloc.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-m68k/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-m68k/pgalloc.h include/asm-m68k/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-m68k/pgalloc.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-m68k/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -163,4 +163,6 @@
  #include <asm/motorola_pgalloc.h>
  #endif
@@ -84645,9 +84612,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif /* M68K_PGALLOC_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-mips/pgalloc.h include/asm-mips/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-mips/pgalloc.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-mips/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-mips/pgalloc.h include/asm-mips/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-mips/pgalloc.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-mips/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -196,4 +196,6 @@
  
  extern int do_check_pgt_cache(int, int);
@@ -84655,9 +84622,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif /* _ASM_PGALLOC_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-mips64/pgalloc.h include/asm-mips64/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-mips64/pgalloc.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-mips64/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-mips64/pgalloc.h include/asm-mips64/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-mips64/pgalloc.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-mips64/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -200,4 +200,6 @@
  
  extern int do_check_pgt_cache(int, int);
@@ -84665,9 +84632,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif /* _ASM_PGALLOC_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-parisc/pgalloc.h include/asm-parisc/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-parisc/pgalloc.h	2003-06-13 08:51:38.000000000 -0600
-+++ include/asm-parisc/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-parisc/pgalloc.h include/asm-parisc/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-parisc/pgalloc.h	2003-06-13 08:51:38.000000000 -0600
++++ include/asm-parisc/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -306,4 +306,6 @@
  
  extern int do_check_pgt_cache(int, int);
@@ -84675,9 +84642,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-ppc/pgalloc.h include/asm-ppc/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-ppc/pgalloc.h	2003-11-28 11:26:21.000000000 -0700
-+++ include/asm-ppc/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-ppc/pgalloc.h include/asm-ppc/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-ppc/pgalloc.h	2003-11-28 11:26:21.000000000 -0700
++++ include/asm-ppc/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -150,5 +150,7 @@
  
  extern int do_check_pgt_cache(int, int);
@@ -84686,9 +84653,9 @@
 +
  #endif /* _PPC_PGALLOC_H */
  #endif /* __KERNEL__ */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/ccwcache.h include/asm-s390/ccwcache.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/ccwcache.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-s390/ccwcache.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/ccwcache.h include/asm-s390/ccwcache.h
+--- kernel-source-2.4.27.orig/include/asm-s390/ccwcache.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-s390/ccwcache.h	2006-02-12 12:47:24.000000000 -0700
 @@ -4,7 +4,7 @@
   * Bugreports.to..: <Linux390 at de.ibm.com>
   * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
@@ -84698,9 +84665,9 @@
   *
   */
  #ifndef CCWCACHE_H
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/chandev.h include/asm-s390/chandev.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/chandev.h	2001-10-11 10:43:38.000000000 -0600
-+++ include/asm-s390/chandev.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/chandev.h include/asm-s390/chandev.h
+--- kernel-source-2.4.27.orig/include/asm-s390/chandev.h	2001-10-11 10:43:38.000000000 -0600
++++ include/asm-s390/chandev.h	2006-02-12 12:47:24.000000000 -0700
 @@ -28,6 +28,7 @@
  	chandev_type_osad=0x8,
  	chandev_type_qeth=0x10,
@@ -84709,9 +84676,9 @@
  } chandev_type;
  
  typedef enum
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/cmb.h include/asm-s390/cmb.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/cmb.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390/cmb.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/cmb.h include/asm-s390/cmb.h
+--- kernel-source-2.4.27.orig/include/asm-s390/cmb.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390/cmb.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,194 @@
 +/*
 + * include/asm-s390/cmb.h
@@ -84907,9 +84874,9 @@
 +extern void cmf_reset(struct cmf_device *cdev);
 +#endif /* __KERNEL__ */
 +#endif /* S390_CMB_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/dasd.h include/asm-s390/dasd.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/dasd.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-s390/dasd.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/dasd.h include/asm-s390/dasd.h
+--- kernel-source-2.4.27.orig/include/asm-s390/dasd.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-s390/dasd.h	2006-02-12 12:47:24.000000000 -0700
 @@ -8,7 +8,7 @@
   * any future changes wrt the API will result in a change of the APIVERSION reported
   * to userspace by the DASDAPIVER-ioctl
@@ -84919,9 +84886,9 @@
   *
   * History of changes (starts July 2000)
   * 05/04/01 created by moving the kernel interface to drivers/s390/block/dasd_int.h
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/dcss.h include/asm-s390/dcss.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/dcss.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390/dcss.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/dcss.h include/asm-s390/dcss.h
+--- kernel-source-2.4.27.orig/include/asm-s390/dcss.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390/dcss.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,19 @@
 +/*
 + *  include/asm-s390/dcss.h
@@ -84942,9 +84909,9 @@
 +extern void segment_replace(char *name);
 +#endif
 +#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/ioctl32.h include/asm-s390/ioctl32.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/ioctl32.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-s390/ioctl32.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/ioctl32.h include/asm-s390/ioctl32.h
+--- kernel-source-2.4.27.orig/include/asm-s390/ioctl32.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-s390/ioctl32.h	2006-02-12 12:47:24.000000000 -0700
 @@ -8,7 +8,7 @@
  #ifndef ASM_IOCTL32_H
  #define ASM_IOCTL32_H
@@ -84954,9 +84921,9 @@
  typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
  
  #ifdef CONFIG_S390_SUPPORT
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/irq.h include/asm-s390/irq.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/irq.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-s390/irq.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/irq.h include/asm-s390/irq.h
+--- kernel-source-2.4.27.orig/include/asm-s390/irq.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-s390/irq.h	2006-02-12 12:47:24.000000000 -0700
 @@ -42,7 +42,9 @@
        __u8  chpid[8];     /* CHPID 0-7 (if available) */
        __u32 unused1 : 8;  /* reserved zeros */
@@ -84978,9 +84945,9 @@
     } __attribute__ ((packed,aligned(4))) schib_t;
  #endif /* __KERNEL__ */
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/kmap_types.h include/asm-s390/kmap_types.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/kmap_types.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390/kmap_types.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/kmap_types.h include/asm-s390/kmap_types.h
+--- kernel-source-2.4.27.orig/include/asm-s390/kmap_types.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390/kmap_types.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,16 @@
 +#ifndef _ASM_KMAP_TYPES_H
 +#define _ASM_KMAP_TYPES_H
@@ -84998,9 +84965,9 @@
 +};
 +
 +#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/lowcore.h include/asm-s390/lowcore.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/lowcore.h	2002-08-02 18:39:45.000000000 -0600
-+++ include/asm-s390/lowcore.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/lowcore.h include/asm-s390/lowcore.h
+--- kernel-source-2.4.27.orig/include/asm-s390/lowcore.h	2002-08-02 18:39:45.000000000 -0600
++++ include/asm-s390/lowcore.h	2006-02-12 12:47:24.000000000 -0700
 @@ -47,6 +47,7 @@
  #define __LC_IPLDEV                     0xC7C
  
@@ -85021,9 +84988,9 @@
  
          /* 0xe00 is used as indicator for dump tools */
          /* whether the kernel died with panic() or not */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/pgtable.h include/asm-s390/pgtable.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/pgtable.h	2003-08-25 05:44:44.000000000 -0600
-+++ include/asm-s390/pgtable.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/pgtable.h include/asm-s390/pgtable.h
+--- kernel-source-2.4.27.orig/include/asm-s390/pgtable.h	2003-08-25 05:44:44.000000000 -0600
++++ include/asm-s390/pgtable.h	2006-02-12 12:47:24.000000000 -0700
 @@ -426,9 +426,12 @@
  	__pte;                                                            \
  })
@@ -85039,9 +85006,9 @@
  			      "a" (__pa((__page-mem_map) << PAGE_SHIFT)));\
  	} while (0)
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/processor.h include/asm-s390/processor.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/processor.h	2003-06-13 08:51:38.000000000 -0600
-+++ include/asm-s390/processor.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/processor.h include/asm-s390/processor.h
+--- kernel-source-2.4.27.orig/include/asm-s390/processor.h	2003-06-13 08:51:38.000000000 -0600
++++ include/asm-s390/processor.h	2006-02-12 12:47:24.000000000 -0700
 @@ -50,6 +50,8 @@
  
  extern void print_cpu_info(struct cpuinfo_S390 *);
@@ -85051,9 +85018,9 @@
  /* Lazy FPU handling on uni-processor */
  extern struct task_struct *last_task_used_math;
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/qdio.h include/asm-s390/qdio.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/qdio.h	2003-08-25 05:44:44.000000000 -0600
-+++ include/asm-s390/qdio.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/qdio.h include/asm-s390/qdio.h
+--- kernel-source-2.4.27.orig/include/asm-s390/qdio.h	2003-08-25 05:44:44.000000000 -0600
++++ include/asm-s390/qdio.h	2006-02-12 12:47:24.000000000 -0700
 @@ -11,7 +11,7 @@
  #ifndef __QDIO_H__
  #define __QDIO_H__
@@ -85124,9 +85091,9 @@
  				/* word 11 */
  				__u32 subsystem_id;
  				/* word 12-1015 */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/qeth.h include/asm-s390/qeth.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/qeth.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390/qeth.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/qeth.h include/asm-s390/qeth.h
+--- kernel-source-2.4.27.orig/include/asm-s390/qeth.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390/qeth.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,26 @@
 +/*
 + * include/asm-s390/qeth.h
@@ -85154,9 +85121,9 @@
 +
 +#endif /* !ASM_QETH_H */
 +
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/setup.h include/asm-s390/setup.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/setup.h	2003-08-25 05:44:44.000000000 -0600
-+++ include/asm-s390/setup.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/setup.h include/asm-s390/setup.h
+--- kernel-source-2.4.27.orig/include/asm-s390/setup.h	2003-08-25 05:44:44.000000000 -0600
++++ include/asm-s390/setup.h	2006-02-12 12:47:24.000000000 -0700
 @@ -25,15 +25,16 @@
   */
  extern unsigned long machine_flags;
@@ -85194,9 +85161,9 @@
  #define SET_CONSOLE_3215	do { console_mode = 2; } while (0)
  #define SET_CONSOLE_3270	do { console_mode = 3; } while (0)
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/sigp.h include/asm-s390/sigp.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/sigp.h	2002-11-28 16:53:15.000000000 -0700
-+++ include/asm-s390/sigp.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/sigp.h include/asm-s390/sigp.h
+--- kernel-source-2.4.27.orig/include/asm-s390/sigp.h	2002-11-28 16:53:15.000000000 -0700
++++ include/asm-s390/sigp.h	2006-02-12 12:47:24.000000000 -0700
 @@ -106,7 +106,7 @@
   * Signal processor with parameter and return status
   */
@@ -85206,9 +85173,9 @@
  		    __u16 cpu_addr, sigp_order_code order_code)
  {
  	sigp_ccode ccode;
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/smp.h include/asm-s390/smp.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/smp.h	2002-11-28 16:53:15.000000000 -0700
-+++ include/asm-s390/smp.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/smp.h include/asm-s390/smp.h
+--- kernel-source-2.4.27.orig/include/asm-s390/smp.h	2002-11-28 16:53:15.000000000 -0700
++++ include/asm-s390/smp.h	2006-02-12 12:47:24.000000000 -0700
 @@ -26,6 +26,9 @@
  	__u16      cpu;
  } sigp_info;
@@ -85253,9 +85220,9 @@
 +#endif
 +
  #endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/tape390.h include/asm-s390/tape390.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/tape390.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390/tape390.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/tape390.h include/asm-s390/tape390.h
+--- kernel-source-2.4.27.orig/include/asm-s390/tape390.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390/tape390.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,39 @@
 +/*************************************************************************
 + *
@@ -85296,9 +85263,9 @@
 +} display_struct;
 +
 +#endif 
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/timer.h include/asm-s390/timer.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390/timer.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390/timer.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390/timer.h include/asm-s390/timer.h
+--- kernel-source-2.4.27.orig/include/asm-s390/timer.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390/timer.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,49 @@
 +/*
 + *  include/asm-s390/timer.h
@@ -85349,9 +85316,9 @@
 +
 +extern atomic_t active_cpu_timer;
 +#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/ccwcache.h include/asm-s390x/ccwcache.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/ccwcache.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-s390x/ccwcache.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/ccwcache.h include/asm-s390x/ccwcache.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/ccwcache.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-s390x/ccwcache.h	2006-02-12 12:47:24.000000000 -0700
 @@ -4,7 +4,7 @@
   * Bugreports.to..: <Linux390 at de.ibm.com>
   * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
@@ -85361,9 +85328,9 @@
   *
   */
  #ifndef CCWCACHE_H
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/chandev.h include/asm-s390x/chandev.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/chandev.h	2001-10-11 10:43:38.000000000 -0600
-+++ include/asm-s390x/chandev.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/chandev.h include/asm-s390x/chandev.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/chandev.h	2001-10-11 10:43:38.000000000 -0600
++++ include/asm-s390x/chandev.h	2006-02-12 12:47:24.000000000 -0700
 @@ -28,6 +28,7 @@
  	chandev_type_osad=0x8,
  	chandev_type_qeth=0x10,
@@ -85372,9 +85339,9 @@
  } chandev_type;
  
  typedef enum
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/cmb.h include/asm-s390x/cmb.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/cmb.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390x/cmb.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/cmb.h include/asm-s390x/cmb.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/cmb.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390x/cmb.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,194 @@
 +/*
 + * include/asm-s390/cmb.h
@@ -85570,9 +85537,9 @@
 +extern void cmf_reset(struct cmf_device *cdev);
 +#endif /* __KERNEL__ */
 +#endif /* S390_CMB_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/dasd.h include/asm-s390x/dasd.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/dasd.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-s390x/dasd.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/dasd.h include/asm-s390x/dasd.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/dasd.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-s390x/dasd.h	2006-02-12 12:47:24.000000000 -0700
 @@ -8,7 +8,7 @@
   * any future changes wrt the API will result in a change of the APIVERSION reported
   * to userspace by the DASDAPIVER-ioctl
@@ -85582,9 +85549,9 @@
   *
   * History of changes (starts July 2000)
   * 05/04/01 created by moving the kernel interface to drivers/s390/block/dasd_int.h
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/dcss.h include/asm-s390x/dcss.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/dcss.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390x/dcss.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/dcss.h include/asm-s390x/dcss.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/dcss.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390x/dcss.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,19 @@
 +/*
 + *  include/asm-s390x/dcss.h
@@ -85605,9 +85572,9 @@
 +extern void segment_replace(char *name);
 +#endif
 +#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/ioctl32.h include/asm-s390x/ioctl32.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/ioctl32.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-s390x/ioctl32.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/ioctl32.h include/asm-s390x/ioctl32.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/ioctl32.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-s390x/ioctl32.h	2006-02-12 12:47:24.000000000 -0700
 @@ -8,7 +8,7 @@
  #ifndef ASM_IOCTL32_H
  #define ASM_IOCTL32_H
@@ -85617,9 +85584,9 @@
  typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
  
  #ifdef CONFIG_S390_SUPPORT
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/irq.h include/asm-s390x/irq.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/irq.h	2004-02-18 06:36:32.000000000 -0700
-+++ include/asm-s390x/irq.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/irq.h include/asm-s390x/irq.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/irq.h	2004-02-18 06:36:32.000000000 -0700
++++ include/asm-s390x/irq.h	2006-02-12 12:47:24.000000000 -0700
 @@ -42,7 +42,9 @@
        __u8  chpid[8];     /* CHPID 0-7 (if available) */
        __u32 unused1 : 8;  /* reserved zeros */
@@ -85641,9 +85608,9 @@
     } __attribute__ ((packed,aligned(4))) schib_t;
  #endif /* __KERNEL__ */
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/kmap_types.h include/asm-s390x/kmap_types.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/kmap_types.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390x/kmap_types.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/kmap_types.h include/asm-s390x/kmap_types.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/kmap_types.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390x/kmap_types.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,16 @@
 +#ifndef _ASM_KMAP_TYPES_H
 +#define _ASM_KMAP_TYPES_H
@@ -85661,9 +85628,9 @@
 +};
 +
 +#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/lowcore.h include/asm-s390x/lowcore.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/lowcore.h	2002-11-28 16:53:15.000000000 -0700
-+++ include/asm-s390x/lowcore.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/lowcore.h include/asm-s390x/lowcore.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/lowcore.h	2002-11-28 16:53:15.000000000 -0700
++++ include/asm-s390x/lowcore.h	2006-02-12 12:47:24.000000000 -0700
 @@ -48,6 +48,7 @@
  #define __LC_IPLDEV                     0xDB8
  
@@ -85684,9 +85651,9 @@
  
          /* 0xe00 is used as indicator for dump tools */
          /* whether the kernel died with panic() or not */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/pgtable.h include/asm-s390x/pgtable.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/pgtable.h	2003-08-25 05:44:44.000000000 -0600
-+++ include/asm-s390x/pgtable.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/pgtable.h include/asm-s390x/pgtable.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/pgtable.h	2003-08-25 05:44:44.000000000 -0600
++++ include/asm-s390x/pgtable.h	2006-02-12 12:47:24.000000000 -0700
 @@ -484,9 +484,11 @@
  	__pte;                                                            \
  })
@@ -85701,9 +85668,9 @@
  			      "a" (__pa((__page-mem_map) << PAGE_SHIFT)));\
  	} while (0)
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/processor.h include/asm-s390x/processor.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/processor.h	2003-06-13 08:51:38.000000000 -0600
-+++ include/asm-s390x/processor.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/processor.h include/asm-s390x/processor.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/processor.h	2003-06-13 08:51:38.000000000 -0600
++++ include/asm-s390x/processor.h	2006-02-12 12:47:24.000000000 -0700
 @@ -52,6 +52,8 @@
  
  extern void print_cpu_info(struct cpuinfo_S390 *);
@@ -85713,9 +85680,9 @@
  /* Lazy FPU handling on uni-processor */
  extern struct task_struct *last_task_used_math;
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/qdio.h include/asm-s390x/qdio.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/qdio.h	2003-08-25 05:44:44.000000000 -0600
-+++ include/asm-s390x/qdio.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/qdio.h include/asm-s390x/qdio.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/qdio.h	2003-08-25 05:44:44.000000000 -0600
++++ include/asm-s390x/qdio.h	2006-02-12 12:47:24.000000000 -0700
 @@ -11,7 +11,7 @@
  #ifndef __QDIO_H__
  #define __QDIO_H__
@@ -85786,9 +85753,9 @@
  				/* word 11 */
  				__u32 subsystem_id;
  				/* word 12-1015 */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/qeth.h include/asm-s390x/qeth.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/qeth.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390x/qeth.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/qeth.h include/asm-s390x/qeth.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/qeth.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390x/qeth.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,26 @@
 +/*
 + * include/asm-s390x/qeth.h
@@ -85816,9 +85783,9 @@
 +
 +#endif /* !ASM_QETH_H */
 +
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/setup.h include/asm-s390x/setup.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/setup.h	2003-08-25 05:44:44.000000000 -0600
-+++ include/asm-s390x/setup.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/setup.h include/asm-s390x/setup.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/setup.h	2003-08-25 05:44:44.000000000 -0600
++++ include/asm-s390x/setup.h	2006-02-12 12:47:24.000000000 -0700
 @@ -32,7 +32,7 @@
  #define MACHINE_NEW_STIDP	(machine_flags & 64)
  #define MACHINE_HAS_PFIX  	(0)
@@ -85841,9 +85808,9 @@
  #define SET_CONSOLE_3215	do { console_mode = 2; } while (0)
  #define SET_CONSOLE_3270	do { console_mode = 3; } while (0)
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/siginfo.h include/asm-s390x/siginfo.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/siginfo.h	2002-08-02 18:39:45.000000000 -0600
-+++ include/asm-s390x/siginfo.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/siginfo.h include/asm-s390x/siginfo.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/siginfo.h	2002-08-02 18:39:45.000000000 -0600
++++ include/asm-s390x/siginfo.h	2006-02-12 12:47:24.000000000 -0700
 @@ -19,7 +19,7 @@
  } sigval_t;
  
@@ -85853,9 +85820,9 @@
  
  typedef struct siginfo {
  	int si_signo;
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/sigp.h include/asm-s390x/sigp.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/sigp.h	2002-11-28 16:53:15.000000000 -0700
-+++ include/asm-s390x/sigp.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/sigp.h include/asm-s390x/sigp.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/sigp.h	2002-11-28 16:53:15.000000000 -0700
++++ include/asm-s390x/sigp.h	2006-02-12 12:47:24.000000000 -0700
 @@ -107,7 +107,7 @@
   * Signal processor with parameter and return status
   */
@@ -85865,9 +85832,9 @@
  		    __u16 cpu_addr, sigp_order_code order_code)
  {
  	sigp_ccode ccode;
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/smp.h include/asm-s390x/smp.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/smp.h	2002-11-28 16:53:15.000000000 -0700
-+++ include/asm-s390x/smp.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/smp.h include/asm-s390x/smp.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/smp.h	2002-11-28 16:53:15.000000000 -0700
++++ include/asm-s390x/smp.h	2006-02-12 12:47:24.000000000 -0700
 @@ -26,6 +26,9 @@
  	__u16      cpu;
  } sigp_info;
@@ -85888,9 +85855,9 @@
 +#endif
 +
  #endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/tape390.h include/asm-s390x/tape390.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/tape390.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390x/tape390.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/tape390.h include/asm-s390x/tape390.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/tape390.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390x/tape390.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,39 @@
 +/*************************************************************************
 + *
@@ -85931,9 +85898,9 @@
 +} display_struct;
 +
 +#endif 
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/timer.h include/asm-s390x/timer.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/timer.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/asm-s390x/timer.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-s390x/timer.h include/asm-s390x/timer.h
+--- kernel-source-2.4.27.orig/include/asm-s390x/timer.h	1969-12-31 17:00:00.000000000 -0700
++++ include/asm-s390x/timer.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,48 @@
 +/*
 + *  include/asm-s390/timer.h
@@ -85983,9 +85950,9 @@
 +extern int del_virt_timer(struct vtimer_list *timer);
 +
 +#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-sh/pgalloc.h include/asm-sh/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-sh/pgalloc.h	2003-08-25 05:44:44.000000000 -0600
-+++ include/asm-sh/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-sh/pgalloc.h include/asm-sh/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-sh/pgalloc.h	2003-08-25 05:44:44.000000000 -0600
++++ include/asm-sh/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -153,4 +153,7 @@
  	pte_t old_pte = *ptep;
  	set_pte(ptep, pte_mkdirty(old_pte));
@@ -85994,9 +85961,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif /* __ASM_SH_PGALLOC_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-sparc/pgalloc.h include/asm-sparc/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-sparc/pgalloc.h	2002-08-02 18:39:45.000000000 -0600
-+++ include/asm-sparc/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-sparc/pgalloc.h include/asm-sparc/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-sparc/pgalloc.h	2002-08-02 18:39:45.000000000 -0600
++++ include/asm-sparc/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -141,4 +141,6 @@
  
  #define pte_free(pte)		free_pte_fast(pte)
@@ -86004,9 +85971,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif /* _SPARC_PGALLOC_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-sparc64/pgalloc.h include/asm-sparc64/pgalloc.h
---- kernel-source-2.4.27-2.4.27.orig/include/asm-sparc64/pgalloc.h	2004-08-07 17:26:06.000000000 -0600
-+++ include/asm-sparc64/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/asm-sparc64/pgalloc.h include/asm-sparc64/pgalloc.h
+--- kernel-source-2.4.27.orig/include/asm-sparc64/pgalloc.h	2004-08-07 17:26:06.000000000 -0600
++++ include/asm-sparc64/pgalloc.h	2006-02-12 12:47:24.000000000 -0700
 @@ -304,4 +304,6 @@
  
  extern int do_check_pgt_cache(int, int);
@@ -86014,9 +85981,9 @@
 +#include <asm-generic/pgalloc.h>
 +
  #endif /* _SPARC64_PGALLOC_H */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/fs.h include/linux/fs.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/fs.h	2006-01-30 22:23:45.000000000 -0700
-+++ include/linux/fs.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/fs.h include/linux/fs.h
+--- kernel-source-2.4.27.orig/include/linux/fs.h	2006-02-08 04:06:25.000000000 -0700
++++ include/linux/fs.h	2006-02-12 12:47:24.000000000 -0700
 @@ -735,6 +735,7 @@
  #include <linux/usbdev_fs_sb.h>
  #include <linux/cramfs_fs_sb.h>
@@ -86033,9 +86000,9 @@
  		void			*generic_sbp;
  	} u;
  	/*
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/genhd.h include/linux/genhd.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/genhd.h	2002-11-28 16:53:15.000000000 -0700
-+++ include/linux/genhd.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/genhd.h include/linux/genhd.h
+--- kernel-source-2.4.27.orig/include/linux/genhd.h	2002-11-28 16:53:15.000000000 -0700
++++ include/linux/genhd.h	2006-02-12 12:47:24.000000000 -0700
 @@ -103,6 +103,7 @@
  
  	devfs_handle_t *de_arr;         /* one per physical disc */
@@ -86044,9 +86011,9 @@
  };
  
  /* drivers/block/genhd.c */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/igmp.h include/linux/igmp.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/igmp.h	2003-08-25 05:44:44.000000000 -0600
-+++ include/linux/igmp.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/igmp.h include/linux/igmp.h
+--- kernel-source-2.4.27.orig/include/linux/igmp.h	2003-08-25 05:44:44.000000000 -0600
++++ include/linux/igmp.h	2006-02-12 12:47:24.000000000 -0700
 @@ -183,6 +183,9 @@
  	unsigned char		crcount;
  };
@@ -86057,9 +86024,9 @@
  /* V3 exponential field decoding */
  #define IGMPV3_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
  #define IGMPV3_EXP(thresh, nbmant, nbexp, value) \
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/inetdevice.h include/linux/inetdevice.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/inetdevice.h	2006-01-30 22:23:44.000000000 -0700
-+++ include/linux/inetdevice.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/inetdevice.h include/linux/inetdevice.h
+--- kernel-source-2.4.27.orig/include/linux/inetdevice.h	2006-02-08 04:06:22.000000000 -0700
++++ include/linux/inetdevice.h	2006-02-12 12:47:24.000000000 -0700
 @@ -28,6 +28,7 @@
  };
  
@@ -86068,9 +86035,9 @@
  
  struct in_device
  {
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/mii.h include/linux/mii.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/mii.h	2002-11-28 16:53:15.000000000 -0700
-+++ include/linux/mii.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/mii.h include/linux/mii.h
+--- kernel-source-2.4.27.orig/include/linux/mii.h	2002-11-28 16:53:15.000000000 -0700
++++ include/linux/mii.h	2006-02-12 12:47:24.000000000 -0700
 @@ -19,6 +19,7 @@
  #define MII_ADVERTISE       0x04        /* Advertisement control reg   */
  #define MII_LPA             0x05        /* Link partner ability reg    */
@@ -86112,9 +86079,9 @@
  /* Advertisement control register. */
  #define ADVERTISE_SLCT          0x001f  /* Selector bits               */
  #define ADVERTISE_CSMA          0x0001  /* Only selector supported     */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/mm.h include/linux/mm.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/mm.h	2006-01-30 22:23:47.000000000 -0700
-+++ include/linux/mm.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/mm.h include/linux/mm.h
+--- kernel-source-2.4.27.orig/include/linux/mm.h	2006-02-08 04:06:26.000000000 -0700
++++ include/linux/mm.h	2006-02-12 12:47:24.000000000 -0700
 @@ -308,11 +308,9 @@
  /* Make it prettier to test the above... */
  #define UnlockPage(page)	unlock_page(page)
@@ -86130,9 +86097,9 @@
  #define ClearPageUptodate(page)	clear_bit(PG_uptodate, &(page)->flags)
  #define PageDirty(page)		test_bit(PG_dirty, &(page)->flags)
  #define SetPageDirty(page)	set_bit(PG_dirty, &(page)->flags)
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/netdevice.h include/linux/netdevice.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/netdevice.h	2006-01-30 22:23:44.000000000 -0700
-+++ include/linux/netdevice.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/netdevice.h include/linux/netdevice.h
+--- kernel-source-2.4.27.orig/include/linux/netdevice.h	2006-02-08 04:06:22.000000000 -0700
++++ include/linux/netdevice.h	2006-02-12 12:47:24.000000000 -0700
 @@ -387,6 +387,8 @@
  #define NETIF_F_HW_VLAN_RX	256	/* Receive VLAN hw acceleration */
  #define NETIF_F_HW_VLAN_FILTER	512	/* Receive filtering on VLAN */
@@ -86152,9 +86119,9 @@
  };
  
  /* 2.6 compatibility */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/s390net.h include/linux/s390net.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/s390net.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/linux/s390net.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/s390net.h include/linux/s390net.h
+--- kernel-source-2.4.27.orig/include/linux/s390net.h	1969-12-31 17:00:00.000000000 -0700
++++ include/linux/s390net.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,17 @@
 +/*
 + * include/linux/s390net.h
@@ -86173,9 +86140,9 @@
 +#endif /* !LINUX_S390NET_H */
 +
 +
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/sched.h include/linux/sched.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/sched.h	2004-08-07 17:26:06.000000000 -0600
-+++ include/linux/sched.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/sched.h include/linux/sched.h
+--- kernel-source-2.4.27.orig/include/linux/sched.h	2004-08-07 17:26:06.000000000 -0600
++++ include/linux/sched.h	2006-02-12 12:47:24.000000000 -0700
 @@ -141,6 +141,9 @@
  extern void cpu_init (void);
  extern void trap_init(void);
@@ -86196,9 +86163,9 @@
  
  extern unsigned int * prof_buffer;
  extern unsigned long prof_len;
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/sysctl.h include/linux/sysctl.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/sysctl.h	2006-01-30 22:23:49.000000000 -0700
-+++ include/linux/sysctl.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/sysctl.h include/linux/sysctl.h
+--- kernel-source-2.4.27.orig/include/linux/sysctl.h	2006-02-08 04:06:26.000000000 -0700
++++ include/linux/sysctl.h	2006-02-12 12:47:24.000000000 -0700
 @@ -125,6 +125,7 @@
  	KERN_CORE_USES_PID=52,		/* int: use core or core.%pid */
  	KERN_TAINTED=53,	/* int: various kernel tainted flags */
@@ -86207,9 +86174,9 @@
   	KERN_CORE_PATTERN=56,	/* string: pattern for core-files */
  	KERN_PPC_L3CR=57,       /* l3cr register on PPC */
  	KERN_EXCEPTION_TRACE=58, /* boolean: exception trace */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/timer.h include/linux/timer.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/timer.h	2006-01-30 22:23:44.000000000 -0700
-+++ include/linux/timer.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/timer.h include/linux/timer.h
+--- kernel-source-2.4.27.orig/include/linux/timer.h	2006-02-08 04:06:22.000000000 -0700
++++ include/linux/timer.h	2006-02-12 12:47:24.000000000 -0700
 @@ -23,6 +23,9 @@
  
  extern void add_timer(struct timer_list * timer);
@@ -86220,9 +86187,9 @@
  
  #ifdef CONFIG_SMP
  extern int del_timer_sync(struct timer_list * timer);
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/tty.h include/linux/tty.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/tty.h	2006-01-30 22:23:46.000000000 -0700
-+++ include/linux/tty.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/tty.h include/linux/tty.h
+--- kernel-source-2.4.27.orig/include/linux/tty.h	2006-02-08 04:06:25.000000000 -0700
++++ include/linux/tty.h	2006-02-12 12:47:24.000000000 -0700
 @@ -383,6 +383,13 @@
  extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags,
  				unsigned minor);
@@ -86237,9 +86204,9 @@
  extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp,
  			     int buflen);
  extern void tty_write_message(struct tty_struct *tty, char *msg);
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/xip2_fs.h include/linux/xip2_fs.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/xip2_fs.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/linux/xip2_fs.h	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/xip2_fs.h include/linux/xip2_fs.h
+--- kernel-source-2.4.27.orig/include/linux/xip2_fs.h	1969-12-31 17:00:00.000000000 -0700
++++ include/linux/xip2_fs.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,117 @@
 +/*
 + *  linux/include/linux/xip2_fs.h, Version 1
@@ -86358,9 +86325,9 @@
 +extern struct inode_operations xip2_fast_symlink_inode_operations;
 +extern struct inode_operations xip2_symlink_inode_operations;
 +#endif
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/xip2_fs_sb.h include/linux/xip2_fs_sb.h
---- kernel-source-2.4.27-2.4.27.orig/include/linux/xip2_fs_sb.h	1969-12-31 17:00:00.000000000 -0700
-+++ include/linux/xip2_fs_sb.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/linux/xip2_fs_sb.h include/linux/xip2_fs_sb.h
+--- kernel-source-2.4.27.orig/include/linux/xip2_fs_sb.h	1969-12-31 17:00:00.000000000 -0700
++++ include/linux/xip2_fs_sb.h	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,47 @@
 +/*
 + *  linux/include/linux/xip2_fs_sb.h, Version 1
@@ -86409,9 +86376,9 @@
 +};
 +
 +#endif	/* _LINUX_XIP2_FS_SB */
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/net/addrconf.h include/net/addrconf.h
---- kernel-source-2.4.27-2.4.27.orig/include/net/addrconf.h	2006-01-30 22:23:44.000000000 -0700
-+++ include/net/addrconf.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/net/addrconf.h include/net/addrconf.h
+--- kernel-source-2.4.27.orig/include/net/addrconf.h	2006-02-08 04:06:22.000000000 -0700
++++ include/net/addrconf.h	2006-02-12 12:47:24.000000000 -0700
 @@ -78,6 +78,9 @@
  /*
   *	multicast prototypes (mcast.c)
@@ -86422,9 +86389,9 @@
  extern int ipv6_sock_mc_join(struct sock *sk, int ifindex, 
  		  struct in6_addr *addr);
  extern int ipv6_sock_mc_drop(struct sock *sk, int ifindex, 
-diff -urN kernel-source-2.4.27-2.4.27.orig/include/net/if_inet6.h include/net/if_inet6.h
---- kernel-source-2.4.27-2.4.27.orig/include/net/if_inet6.h	2006-01-30 22:23:44.000000000 -0700
-+++ include/net/if_inet6.h	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/include/net/if_inet6.h include/net/if_inet6.h
+--- kernel-source-2.4.27.orig/include/net/if_inet6.h	2006-02-08 04:06:22.000000000 -0700
++++ include/net/if_inet6.h	2006-02-12 12:47:24.000000000 -0700
 @@ -134,6 +134,8 @@
  #define	IFA_SITE	IPV6_ADDR_SITELOCAL
  #define	IFA_GLOBAL	0x0000U
@@ -86434,9 +86401,9 @@
  struct inet6_dev 
  {
  	struct net_device		*dev;
-diff -urN kernel-source-2.4.27-2.4.27.orig/init/do_mounts.c init/do_mounts.c
---- kernel-source-2.4.27-2.4.27.orig/init/do_mounts.c	2006-01-30 22:23:44.000000000 -0700
-+++ init/do_mounts.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/init/do_mounts.c init/do_mounts.c
+--- kernel-source-2.4.27.orig/init/do_mounts.c	2006-02-08 04:06:22.000000000 -0700
++++ init/do_mounts.c	2006-02-12 12:47:24.000000000 -0700
 @@ -164,6 +164,27 @@
  	{ "dasdf", (DASD_MAJOR << MINORBITS) + (5 << 2) },
  	{ "dasdg", (DASD_MAJOR << MINORBITS) + (6 << 2) },
@@ -86465,9 +86432,9 @@
  #endif
  	{ "ida/c0d0p",0x4800 },
  	{ "ida/c0d1p",0x4810 },
-diff -urN kernel-source-2.4.27-2.4.27.orig/init/kerntypes.c init/kerntypes.c
---- kernel-source-2.4.27-2.4.27.orig/init/kerntypes.c	1969-12-31 17:00:00.000000000 -0700
-+++ init/kerntypes.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/init/kerntypes.c init/kerntypes.c
+--- kernel-source-2.4.27.orig/init/kerntypes.c	1969-12-31 17:00:00.000000000 -0700
++++ init/kerntypes.c	2006-02-12 12:47:24.000000000 -0700
 @@ -0,0 +1,49 @@
 +/*
 + * kerntypes.c
@@ -86518,9 +86485,9 @@
 +kerntypes_dummy(void)
 +{
 +}
-diff -urN kernel-source-2.4.27-2.4.27.orig/init/version.c init/version.c
---- kernel-source-2.4.27-2.4.27.orig/init/version.c	2001-11-09 15:11:15.000000000 -0700
-+++ init/version.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/init/version.c init/version.c
+--- kernel-source-2.4.27.orig/init/version.c	2001-11-09 15:11:15.000000000 -0700
++++ init/version.c	2006-02-12 12:47:24.000000000 -0700
 @@ -10,6 +10,7 @@
  #include <linux/utsname.h>
  #include <linux/version.h>
@@ -86535,9 +86502,9 @@
  	LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";
 +
 +const char *LINUX_COMPILE_VERSION_ID = __stringify(LINUX_COMPILE_VERSION_ID);
-diff -urN kernel-source-2.4.27-2.4.27.orig/kernel/ksyms.c kernel/ksyms.c
---- kernel-source-2.4.27-2.4.27.orig/kernel/ksyms.c	2006-01-30 22:23:46.000000000 -0700
-+++ kernel/ksyms.c	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/kernel/ksyms.c kernel/ksyms.c
+--- kernel-source-2.4.27.orig/kernel/ksyms.c	2006-02-08 04:06:25.000000000 -0700
++++ kernel/ksyms.c	2006-02-12 12:47:24.000000000 -0700
 @@ -619,6 +619,7 @@
  EXPORT_SYMBOL(do_softirq);
  EXPORT_SYMBOL(raise_softirq);
@@ -86546,9 +86513,9 @@
  EXPORT_SYMBOL(__tasklet_schedule);
  EXPORT_SYMBOL(__tasklet_hi_schedule);
  
-diff -urN kernel-source-2.4.27-2.4.27.orig/kernel/sysctl.c kernel/sysctl.c
---- kernel-source-2.4.27-2.4.27.orig/kernel/sysctl.c	2006-01-30 22:23:49.000000000 -0700
-+++ kernel/sysctl.c	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/kernel/sysctl.c kernel/sysctl.c
+--- kernel-source-2.4.27.orig/kernel/sysctl.c	2006-02-08 04:06:26.000000000 -0700
++++ kernel/sysctl.c	2006-02-12 12:47:24.000000000 -0700
 @@ -92,6 +92,9 @@
  extern int sysctl_ieee_emulation_warnings;
  #endif
@@ -86570,9 +86537,9 @@
  #ifdef __x86_64__
  	{KERN_EXCEPTION_TRACE,"exception-trace",
  	 &exception_trace,sizeof(int),0644,NULL,&proc_dointvec},
-diff -urN kernel-source-2.4.27-2.4.27.orig/kernel/timer.c kernel/timer.c
---- kernel-source-2.4.27-2.4.27.orig/kernel/timer.c	2002-11-28 16:53:15.000000000 -0700
-+++ kernel/timer.c	2006-01-30 22:25:22.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/kernel/timer.c kernel/timer.c
+--- kernel-source-2.4.27.orig/kernel/timer.c	2002-11-28 16:53:15.000000000 -0700
++++ kernel/timer.c	2006-02-12 12:47:24.000000000 -0700
 @@ -339,6 +339,64 @@
  	spin_unlock_irq(&timerlist_lock);
  }
@@ -86703,9 +86670,53 @@
  #if !defined(__alpha__) && !defined(__ia64__)
  
  /*
-diff -urN kernel-source-2.4.27-2.4.27.orig/mm/filemap.c mm/filemap.c
---- kernel-source-2.4.27-2.4.27.orig/mm/filemap.c	2006-01-30 22:23:48.000000000 -0700
-+++ mm/filemap.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/Makefile Makefile
+--- kernel-source-2.4.27.orig/Makefile	2006-02-08 04:06:25.000000000 -0700
++++ Makefile	2006-02-12 12:47:23.000000000 -0700
+@@ -292,7 +292,7 @@
+ boot: vmlinux
+ 	@$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C arch/$(ARCH)/boot
+ 
+-vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o init/do_mounts.o linuxsubdirs
++vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o init/do_mounts.o Kerntypes linuxsubdirs
+ 	$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o init/do_mounts.o \
+ 		--start-group \
+ 		$(CORE_FILES) \
+@@ -303,6 +303,11 @@
+ 		-o vmlinux
+ 	$(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
+ 
++Kerntypes: init/kerntypes.o
++	@if [ -f init/kerntypes.o ]; then \
++		mv init/kerntypes.o Kerntypes; \
++	fi
++
+ symlinks:
+ 	rm -f include/asm
+ 	( cd include ; ln -sf asm-$(ARCH) asm)
+@@ -357,6 +362,9 @@
+ 	 echo > .ver1
+ 	@echo \#define LINUX_COMPILE_DOMAIN \"`cat .ver1 | $(uts_truncate)`\" >> .ver
+ 	@echo \#define LINUX_COMPILER \"`$(CC) $(CFLAGS) -v 2>&1 | tail -n 1`\" >> .ver
++	@echo \__linux_compile_version_id__`echo $(KERNELRELEASE) | tr -c '[0-9A-Za-z\n]' '_'`__`hostname | tr -c '[0-9A-Za-z\n]' '_'`__`LANG=C date | tr -c '[0-9A-Za-z\n]' '_'` > .ver1
++	@echo \#define LINUX_COMPILE_VERSION_ID `cat .ver1`  >> .ver
++	@echo "typedef char* `cat .ver1`_t;"  >> .ver
+ 	@mv -f .ver $@
+ 	@rm -f .ver1
+ 
+@@ -373,6 +381,9 @@
+ init/version.o: init/version.c include/linux/compile.h include/config/MARKER
+ 	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) -DUTS_MACHINE='"$(ARCH)"' -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o init/version.o init/version.c
+ 
++init/kerntypes.o: init/kerntypes.c include/config/MARKER include/linux/compile.h
++	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -gstabs -c -o $*.o $<
++
+ init/main.o: init/main.c include/config/MARKER
+ 	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o $@ $<
+ 
+diff -urN kernel-source-2.4.27.orig/mm/filemap.c mm/filemap.c
+--- kernel-source-2.4.27.orig/mm/filemap.c	2006-02-08 04:06:26.000000000 -0700
++++ mm/filemap.c	2006-02-12 12:47:24.000000000 -0700
 @@ -1388,7 +1388,7 @@
   * If it was already so marked, move it to the active queue and drop
   * the referenced bit.  Otherwise, just mark it for future action..
@@ -86726,9 +86737,9 @@
  			set_page_dirty(page);
  		}
  	}
-diff -urN kernel-source-2.4.27-2.4.27.orig/mm/memory.c mm/memory.c
---- kernel-source-2.4.27-2.4.27.orig/mm/memory.c	2006-01-30 22:23:48.000000000 -0700
-+++ mm/memory.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/mm/memory.c mm/memory.c
+--- kernel-source-2.4.27.orig/mm/memory.c	2006-02-08 04:06:26.000000000 -0700
++++ mm/memory.c	2006-02-12 12:47:24.000000000 -0700
 @@ -163,6 +163,86 @@
  #define PMD_TABLE_MASK	((PTRS_PER_PMD-1) * sizeof(pmd_t))
  
@@ -86964,9 +86975,9 @@
  int make_pages_present(unsigned long addr, unsigned long end)
  {
  	int ret, len, write;
-diff -urN kernel-source-2.4.27-2.4.27.orig/mm/vmscan.c mm/vmscan.c
---- kernel-source-2.4.27-2.4.27.orig/mm/vmscan.c	2006-01-30 22:23:45.000000000 -0700
-+++ mm/vmscan.c	2006-01-30 22:25:24.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/mm/vmscan.c mm/vmscan.c
+--- kernel-source-2.4.27.orig/mm/vmscan.c	2006-02-08 04:06:25.000000000 -0700
++++ mm/vmscan.c	2006-02-12 12:47:24.000000000 -0700
 @@ -145,9 +145,7 @@
  	 * is needed on CPUs which update the accessed and dirty
  	 * bits in hardware.
@@ -86978,9 +86989,9 @@
  
  	if (pte_dirty(pte))
  		set_page_dirty(page);
-diff -urN kernel-source-2.4.27-2.4.27.orig/net/802/tr.c net/802/tr.c
---- kernel-source-2.4.27-2.4.27.orig/net/802/tr.c	2003-06-13 08:51:39.000000000 -0600
-+++ net/802/tr.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/net/802/tr.c net/802/tr.c
+--- kernel-source-2.4.27.orig/net/802/tr.c	2003-06-13 08:51:39.000000000 -0600
++++ net/802/tr.c	2006-02-12 12:47:24.000000000 -0700
 @@ -327,9 +327,9 @@
  	int i;
  	unsigned int hash, rii_p = 0;
@@ -87011,9 +87022,9 @@
  }
  
  /*
-diff -urN kernel-source-2.4.27-2.4.27.orig/net/8021q/vlan.c net/8021q/vlan.c
---- kernel-source-2.4.27-2.4.27.orig/net/8021q/vlan.c	2004-02-18 06:36:32.000000000 -0700
-+++ net/8021q/vlan.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/net/8021q/vlan.c net/8021q/vlan.c
+--- kernel-source-2.4.27.orig/net/8021q/vlan.c	2004-02-18 06:36:32.000000000 -0700
++++ net/8021q/vlan.c	2006-02-12 12:47:24.000000000 -0700
 @@ -444,6 +444,10 @@
  	/* IFF_BROADCAST|IFF_MULTICAST; ??? */
  	new_dev->flags = real_dev->flags;
@@ -87048,9 +87059,9 @@
  	new_dev->set_multicast_list = vlan_dev_set_multicast_list;
  
  	VLAN_DEV_INFO(new_dev)->vlan_id = VLAN_ID; /* 1 through VLAN_VID_MASK */
-diff -urN kernel-source-2.4.27-2.4.27.orig/net/Config.in net/Config.in
---- kernel-source-2.4.27-2.4.27.orig/net/Config.in	2006-01-30 22:23:44.000000000 -0700
-+++ net/Config.in	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/net/Config.in net/Config.in
+--- kernel-source-2.4.27.orig/net/Config.in	2006-02-08 04:06:22.000000000 -0700
++++ net/Config.in	2006-02-12 12:47:24.000000000 -0700
 @@ -26,6 +26,7 @@
        if [ "$CONFIG_IPV6" != "n" ]; then
  	 source net/ipv6/Config.in
@@ -87059,9 +87070,9 @@
     fi
     if [ "$CONFIG_NET_KEY" != "n" -o \
  	"$CONFIG_NET_IPIP" != "n" -o \
-diff -urN kernel-source-2.4.27-2.4.27.orig/net/core/dev.c net/core/dev.c
---- kernel-source-2.4.27-2.4.27.orig/net/core/dev.c	2006-01-30 22:23:44.000000000 -0700
-+++ net/core/dev.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/net/core/dev.c net/core/dev.c
+--- kernel-source-2.4.27.orig/net/core/dev.c	2006-02-08 04:06:22.000000000 -0700
++++ net/core/dev.c	2006-02-12 12:47:24.000000000 -0700
 @@ -104,6 +104,7 @@
  #include <linux/wireless.h>		/* Note : will define WIRELESS_EXT */
  #include <net/iw_handler.h>
@@ -87086,9 +87097,9 @@
  			    (cmd >= SIOCDEVPRIVATE &&
  			     cmd <= SIOCDEVPRIVATE + 15)) {
  				dev_load(ifr.ifr_name);
-diff -urN kernel-source-2.4.27-2.4.27.orig/net/ipv4/igmp.c net/ipv4/igmp.c
---- kernel-source-2.4.27-2.4.27.orig/net/ipv4/igmp.c	2006-01-30 22:23:46.000000000 -0700
-+++ net/ipv4/igmp.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/net/ipv4/igmp.c net/ipv4/igmp.c
+--- kernel-source-2.4.27.orig/net/ipv4/igmp.c	2006-02-08 04:06:25.000000000 -0700
++++ net/ipv4/igmp.c	2006-02-12 12:47:24.000000000 -0700
 @@ -152,6 +152,18 @@
  
  #ifdef CONFIG_IP_MULTICAST
@@ -87126,9 +87137,9 @@
  				ip_ma_put(i);
  				return;
  			}
-diff -urN kernel-source-2.4.27-2.4.27.orig/net/ipv6/addrconf.c net/ipv6/addrconf.c
---- kernel-source-2.4.27-2.4.27.orig/net/ipv6/addrconf.c	2006-01-30 22:23:44.000000000 -0700
-+++ net/ipv6/addrconf.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/net/ipv6/addrconf.c net/ipv6/addrconf.c
+--- kernel-source-2.4.27.orig/net/ipv6/addrconf.c	2006-02-08 04:06:22.000000000 -0700
++++ net/ipv6/addrconf.c	2006-02-12 12:47:24.000000000 -0700
 @@ -1015,9 +1015,20 @@
  			return -1;
  		memcpy(eui, dev->dev_addr, 3);
@@ -87150,9 +87161,9 @@
  		return 0;
  	case ARPHRD_ARCNET:
  		/* XXX: inherit EUI-64 fro mother interface -- yoshfuji */
-diff -urN kernel-source-2.4.27-2.4.27.orig/net/ipv6/ipv6_syms.c net/ipv6/ipv6_syms.c
---- kernel-source-2.4.27-2.4.27.orig/net/ipv6/ipv6_syms.c	2006-01-30 22:23:44.000000000 -0700
-+++ net/ipv6/ipv6_syms.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/net/ipv6/ipv6_syms.c net/ipv6/ipv6_syms.c
+--- kernel-source-2.4.27.orig/net/ipv6/ipv6_syms.c	2006-02-08 04:06:22.000000000 -0700
++++ net/ipv6/ipv6_syms.c	2006-02-12 12:47:24.000000000 -0700
 @@ -15,6 +15,8 @@
  EXPORT_SYMBOL(ndisc_mc_map);
  EXPORT_SYMBOL(register_inet6addr_notifier);
@@ -87162,9 +87173,9 @@
  EXPORT_SYMBOL(ip6_route_output);
  #ifdef CONFIG_NETFILTER
  EXPORT_SYMBOL(ip6_route_me_harder);
-diff -urN kernel-source-2.4.27-2.4.27.orig/net/ipv6/mcast.c net/ipv6/mcast.c
---- kernel-source-2.4.27-2.4.27.orig/net/ipv6/mcast.c	2006-01-30 22:23:46.000000000 -0700
-+++ net/ipv6/mcast.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/net/ipv6/mcast.c net/ipv6/mcast.c
+--- kernel-source-2.4.27.orig/net/ipv6/mcast.c	2006-02-08 04:06:25.000000000 -0700
++++ net/ipv6/mcast.c	2006-02-12 12:47:24.000000000 -0700
 @@ -127,6 +127,18 @@
  
  static struct socket *igmp6_socket;
@@ -87201,9 +87212,9 @@
  				ma_put(ma);
  				return 0;
  			}
-diff -urN kernel-source-2.4.27-2.4.27.orig/net/netsyms.c net/netsyms.c
---- kernel-source-2.4.27-2.4.27.orig/net/netsyms.c	2006-01-30 22:23:49.000000000 -0700
-+++ net/netsyms.c	2006-01-30 22:25:23.000000000 -0700
+diff -urN kernel-source-2.4.27.orig/net/netsyms.c net/netsyms.c
+--- kernel-source-2.4.27.orig/net/netsyms.c	2006-02-08 04:06:26.000000000 -0700
++++ net/netsyms.c	2006-02-12 12:47:24.000000000 -0700
 @@ -297,7 +297,10 @@
  EXPORT_SYMBOL(devinet_ioctl);
  EXPORT_SYMBOL(register_inetaddr_notifier);



More information about the Kernel-svn-changes mailing list