[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ö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ö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(¤t->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ö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ö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(®->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(®->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(®->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(®->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