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

Dann Frazier dannf at costa.debian.org
Tue Jan 31 06:00:16 UTC 2006


Author: dannf
Date: Tue Jan 31 05:58:10 2006
New Revision: 5643

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/control
   dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/linux-2.4.27-s390.diff
Log:
* 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

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	Tue Jan 31 05:58:10 2006
@@ -1,3 +1,11 @@
+kernel-patch-2.4.27-s390 (2.4.27-2sarge1) stable-security; urgency=high
+
+  * 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
+
+ -- dann frazier <dannf at debian.org>  Mon, 30 Jan 2006 22:51:47 -0700
+
 kernel-patch-2.4.27-s390 (2.4.27-2) unstable; urgency=high
 
   * Update patches:

Modified: dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/debian/control
==============================================================================
--- dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/debian/control	(original)
+++ dists/sarge-security/kernel-2.4/s390/kernel-patch-2.4.27-s390/debian/control	Tue Jan 31 05:58:10 2006
@@ -3,7 +3,7 @@
 Priority: extra
 Maintainer: Debian S/390 Team <debian-s390 at lists.debian.org>
 Uploaders: Bastian Blank <waldi at debian.org>, Jochen Röhrig <jr at debian.org>
-Build-Depends-Indep: debhelper (>= 3.0.51), patch (>= 2.5.4-11), dh-kpatches (>= 0.99.10), kernel-tree-2.4.27-8
+Build-Depends-Indep: debhelper (>= 3.0.51), patch (>= 2.5.4-11), dh-kpatches (>= 0.99.10), kernel-tree-2.4.27-10sarge2
 Standards-Version: 3.5.6
 
 Package: kernel-patch-2.4.27-s390

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	Tue Jan 31 05:58:10 2006
@@ -1,6696 +1,5635 @@
-=== kernel/ksyms.c
-==================================================================
---- kernel/ksyms.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ kernel/ksyms.c   (/trunk/2.4.27)   (revision 52)
-@@ -605,6 +605,7 @@
- EXPORT_SYMBOL(do_softirq);
- EXPORT_SYMBOL(raise_softirq);
- EXPORT_SYMBOL(cpu_raise_softirq);
-+EXPORT_SYMBOL(open_softirq);
- EXPORT_SYMBOL(__tasklet_schedule);
- EXPORT_SYMBOL(__tasklet_hi_schedule);
+diff -urN kernel-source-2.4.27-2.4.27.orig/Documentation/Configure.help kernel-source-2.4.27-2.4.27/Documentation/Configure.help
+--- kernel-source-2.4.27-2.4.27.orig/Documentation/Configure.help	2006-01-30 22:23:45.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/Documentation/Configure.help	2006-01-30 22:25:24.000000000 -0700
+@@ -6385,6 +6385,16 @@
  
-=== kernel/timer.c
-==================================================================
---- kernel/timer.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ kernel/timer.c   (/trunk/2.4.27)   (revision 52)
-@@ -339,6 +339,64 @@
- 	spin_unlock_irq(&timerlist_lock);
- }
+   See <file:Documentation/networking/ip-sysctl.txt> for details.
  
-+#ifdef CONFIG_NO_IDLE_HZ
-+/*
-+ * Find out when the next timer event is due to happen. This
-+ * is used on S/390 to stop all activity when all cpus are idle.
-+ * The timerlist_lock must be acquired before calling this function.
-+ */
-+struct timer_list *next_timer_event(void)
-+{
-+	struct timer_list *nte, *tmp;
-+	struct list_head *lst;
-+	int i, j;
-+
-+	/* Look for the next timer event in tv1. */
-+	i = 0;
-+	j = tvecs[0]->index;
-+	do {
-+		struct list_head *head = tvecs[0]->vec + j;
-+		if (!list_empty(head)) {
-+			nte = list_entry(head->next, struct timer_list, list);
-+			goto found;
-+		}
-+		j = (j + 1) & TVR_MASK;
-+	} while (j != tv1.index);
++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).
 +
-+	/* No event found in tv1. Check tv2-tv5. */
-+	for (i = 1; i < NOOF_TVECS; i++) {
-+		j = tvecs[i]->index;
-+		do {
-+			nte = NULL;
-+			list_for_each(lst, tvecs[i]->vec + j) {
-+				tmp = list_entry(lst, struct timer_list, list);
-+				if (nte == NULL ||
-+				    time_before(tmp->expires, nte->expires))
-+					nte = tmp;
-+			}
-+			if (nte)
-+				goto found;
-+			j = (j + 1) & TVN_MASK;
-+		} while (j != tvecs[i]->index);
-+	}
-+	return NULL;
-+found:
-+	/* Found timer event in tvecs[i]->vec[j] */
-+	if (j < tvecs[i]->index && i < NOOF_TVECS-1) {
-+		/* 
-+		 * The search wrapped. We need to look at the next list
-+		 * from tvecs[i+1] that would cascade into tvecs[i].
-+		 */
-+		list_for_each(lst, tvecs[i+1]->vec+tvecs[i+1]->index) {
-+			tmp = list_entry(lst, struct timer_list, list);
-+			if (time_before(tmp->expires, nte->expires))
-+				nte = tmp;
-+		}
-+	}
-+	return nte;
-+}
-+#endif
++  Only say yes on IBM zSeries or S/390 systems.
 +
- spinlock_t tqueue_lock = SPIN_LOCK_UNLOCKED;
+ 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).
  
- void tqueue_bh(void)
-@@ -527,7 +585,7 @@
- 		update_wall_time_one_tick();
- 	} while (ticks);
+   For details please refer to the documentation provided by IBM at
+   <http://www10.software.ibm.com/developerworks/opensource/linux390>
+@@ -8088,6 +8098,61 @@
  
--	if (xtime.tv_usec >= 1000000) {
-+	while (xtime.tv_usec >= 1000000) {
- 	    xtime.tv_usec -= 1000000;
- 	    xtime.tv_sec++;
- 	    second_overflow();
-@@ -620,6 +678,31 @@
- }
+   If unsure, say N.
  
- /*
-+ * Called from the timer interrupt handler to charge a couple of ticks
-+ * to the current process.
-+ */
-+void update_process_times_us(int user_ticks, int system_ticks)
-+{
-+	struct task_struct *p = current;
-+	int cpu = smp_processor_id();
++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.
 +
-+	update_one_process(p, user_ticks, system_ticks, cpu);
-+	if (p->pid) {
-+		p->counter -= user_ticks + system_ticks;
-+		if (p->counter <= 0) {
-+			p->counter = 0;
-+			p->need_resched = 1;
-+		}
-+		if (p->nice > 0)
-+			kstat.per_cpu_nice[cpu] += user_ticks;
-+		else
-+			kstat.per_cpu_user[cpu] += user_ticks;
-+		kstat.per_cpu_system[cpu] += system_ticks;
-+	} else if (local_bh_count(cpu) || local_irq_count(cpu) > 1)
-+		kstat.per_cpu_system[cpu] += system_ticks;
-+}
++  For details please refer to the documentation provided by IBM at
++  <http://www10.software.ibm.com/developerworks/opensource/linux390>
 +
-+/*
-  * Nr of active tasks - counted in fixed-point numbers
-  */
- static unsigned long count_active_tasks(void)
-@@ -651,7 +734,7 @@
- 	static int count = LOAD_FREQ;
- 
- 	count -= ticks;
--	if (count < 0) {
-+	while (count < 0) {
- 		count += LOAD_FREQ;
- 		active_tasks = count_active_tasks();
- 		CALC_LOAD(avenrun[0], EXP_1, active_tasks);
-@@ -709,6 +792,14 @@
- 		mark_bh(TQUEUE_BH);
- }
- 
-+void do_timer_ticks(int ticks)
-+{
-+	(*(unsigned long *)&jiffies) += ticks;
-+	mark_bh(TIMER_BH);
-+	if (TQ_ACTIVE(tq_timer))
-+		mark_bh(TQUEUE_BH);
-+}
++  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.
 +
- #if !defined(__alpha__) && !defined(__ia64__)
- 
- /*
-=== kernel/sysctl.c
-==================================================================
---- kernel/sysctl.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ kernel/sysctl.c   (/trunk/2.4.27)   (revision 52)
-@@ -92,7 +92,10 @@
- extern int sysctl_ieee_emulation_warnings;
- #endif
- extern int sysctl_userprocess_debug;
-+#ifdef CONFIG_NO_IDLE_HZ
-+extern int sysctl_hz_timer;
- #endif
-+#endif
- 
- #ifdef CONFIG_PPC32
- extern unsigned long zero_paged_on, powersave_nap;
-@@ -274,6 +277,10 @@
- 	{KERN_S390_USER_DEBUG_LOGGING,"userprocess_debug",
- 	 &sysctl_userprocess_debug,sizeof(int),0644,NULL,&proc_dointvec},
- #endif
-+#ifdef CONFIG_NO_IDLE_HZ
-+	{KERN_S390_HZ_TIMER,"hz_timer",
-+	 &sysctl_hz_timer,sizeof(int),0644,NULL,&proc_dointvec},
-+#endif
- #ifdef __x86_64__
- 	{KERN_EXCEPTION_TRACE,"exception-trace",
- 	 &exception_trace,sizeof(int),0644,NULL,&proc_dointvec},
-=== include/asm-ia64/pgalloc.h
-==================================================================
---- include/asm-ia64/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-ia64/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -297,4 +297,6 @@
- 	set_bit(PG_arch_1, &page->flags);	/* mark page as clean */
- }
- 
-+#include <asm-generic/pgalloc.h>
++IPv6 support for qeth
++CONFIG_QETH_IPV6
++  If CONFIG_QETH is switched on, this option will include IPv6
++  support in the qeth device driver.
 +
- #endif /* _ASM_IA64_PGALLOC_H */
-=== include/asm-mips/pgalloc.h
-==================================================================
---- include/asm-mips/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-mips/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -196,4 +196,6 @@
- 
- extern int do_check_pgt_cache(int, int);
- 
-+#include <asm-generic/pgalloc.h>
++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.
 +
- #endif /* _ASM_PGALLOC_H */
-=== include/asm-sparc/pgalloc.h
-==================================================================
---- include/asm-sparc/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-sparc/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -141,4 +141,6 @@
- 
- #define pte_free(pte)		free_pte_fast(pte)
- 
-+#include <asm-generic/pgalloc.h>
++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 /* _SPARC_PGALLOC_H */
-=== include/net/if_inet6.h
-==================================================================
---- include/net/if_inet6.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/net/if_inet6.h   (/trunk/2.4.27)   (revision 52)
-@@ -128,6 +128,8 @@
- #define	IFA_SITE	IPV6_ADDR_SITELOCAL
- #define	IFA_GLOBAL	0x0000U
- 
-+extern struct notifier_block *inet6addr_chain;
++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>
 +
- struct inet6_dev 
- {
- 	struct net_device		*dev;
-=== include/net/addrconf.h
-==================================================================
---- include/net/addrconf.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/net/addrconf.h   (/trunk/2.4.27)   (revision 52)
-@@ -71,6 +71,9 @@
- /*
-  *	multicast prototypes (mcast.c)
-  */
-+extern int register_multicast6_notifier(struct notifier_block *nb);
-+extern int unregister_multicast6_notifier(struct notifier_block *nb);
++  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>.
 +
- 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, 
-=== include/asm-sh/pgalloc.h
-==================================================================
---- include/asm-sh/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-sh/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -153,4 +153,7 @@
- 	pte_t old_pte = *ptep;
- 	set_pte(ptep, pte_mkdirty(old_pte));
- }
++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).
 +
-+#include <asm-generic/pgalloc.h>
++  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.
 +
- #endif /* __ASM_SH_PGALLOC_H */
-=== include/asm-generic/pgalloc.h
-==================================================================
---- include/asm-generic/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-generic/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,37 @@
-+#ifndef _ASM_GENERIC_PGALLOC_H
-+#define _ASM_GENERIC_PGALLOC_H
++  If unsure, say N.
 +
-+static inline int ptep_test_and_clear_and_flush_young(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
-+{
-+	if (!ptep_test_and_clear_young(ptep))
-+		return 0;
-+	flush_tlb_page(vma, address);
-+	return 1;
-+}
+ 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. 
 +
-+static inline int ptep_test_and_clear_and_flush_dirty(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
-+{
-+	if (!ptep_test_and_clear_dirty(ptep))
-+		return 0;
-+	flush_tlb_page(vma, address);
-+	return 1;
-+}
++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.
 +
-+static inline void ptep_establish(struct vm_area_struct *vma, unsigned long address, pte_t *ptep, pte_t entry)
-+{
-+	set_pte(ptep, entry);
-+	flush_tlb_page(vma, address);
-+	update_mmu_cache(vma, address, entry);
-+}
++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.
 +
-+static inline pte_t ptep_invalidate(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
-+{
-+	pte_t pte;
++CONFIG_APPLDATA_OS
++  This option provides operating system related data to the Linux -
++  z/VM Monitor Stream, for example, the CPU utilisation.
 +
-+	flush_cache_page(vma, address);
-+        pte = ptep_get_and_clear(ptep);
-+	flush_tlb_page(vma, address);
-+	return pte;
-+}
++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.
 +
-+#endif /* _ASM_GENERIC_PGALLOC_H */
-=== include/asm-arm/pgalloc.h
-==================================================================
---- include/asm-arm/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-arm/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -138,4 +138,6 @@
- 
- extern int do_check_pgt_cache(int, int);
- 
-+#include <asm-generic/pgalloc.h>
++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. 
 +
- #endif
-=== include/asm-parisc/pgalloc.h
-==================================================================
---- include/asm-parisc/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-parisc/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -306,4 +306,6 @@
++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.
  
- extern int do_check_pgt_cache(int, int);
+ 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 <asm-generic/pgalloc.h>
-+
- #endif
-=== include/linux/sysctl.h
-==================================================================
---- include/linux/sysctl.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/sysctl.h   (/trunk/2.4.27)   (revision 52)
-@@ -124,6 +124,7 @@
- 	KERN_CORE_USES_PID=52,		/* int: use core or core.%pid */
- 	KERN_TAINTED=53,	/* int: various kernel tainted flags */
- 	KERN_CADPID=54,		/* int: PID of the process to notify on CAD */
-+	KERN_S390_HZ_TIMER=55,  /* int: hz timer on or off */
-  	KERN_CORE_PATTERN=56,	/* string: pattern for core-files */
- 	KERN_PPC_L3CR=57,       /* l3cr register on PPC */
- 	KERN_EXCEPTION_TRACE=58, /* boolean: exception trace */
-=== include/linux/netdevice.h
-==================================================================
---- include/linux/netdevice.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/netdevice.h   (/trunk/2.4.27)   (revision 52)
-@@ -382,6 +382,8 @@
- #define NETIF_F_HW_VLAN_RX	256	/* Receive VLAN hw acceleration */
- #define NETIF_F_HW_VLAN_FILTER	512	/* Receive filtering on VLAN */
- #define NETIF_F_VLAN_CHALLENGED	1024	/* Device cannot handle VLAN packets */
-+#define NETIF_F_SHARED_IPV6	2048	/* make IPv6 address autogeneration
-+					   network card instance aware */
+ ECKD demand loading
+@@ -25464,6 +25572,14 @@
+ CONFIG_DASD_AUTO_DIAG
+   This option enables demand loading of the DIAG module. 
  
- 	/* Called after device is detached from network. */
- 	void			(*uninit)(struct net_device *dev);
-@@ -453,6 +455,9 @@
- 	/* this will get initialized at each interface type init routine */
- 	struct divert_blk	*divert;
- #endif /* CONFIG_NET_DIVERT */
-+#ifdef CONFIG_SHARED_IPV6_CARDS
-+	unsigned short		dev_id;
-+#endif /* CONFIG_SHARED_IPV6_CARDS */
- };
++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.
  
- /* 2.6 compatibility */
-=== include/linux/xip2_fs_sb.h
-==================================================================
---- include/linux/xip2_fs_sb.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/xip2_fs_sb.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,47 @@
-+/*
-+ *  linux/include/linux/xip2_fs_sb.h, Version 1
-+ *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
-+ */
+-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. 
 +
-+#ifndef _LINUX_XIP2_FS_SB
-+#define _LINUX_XIP2_FS_SB
++Support for SCLP line mode terminal
++CONFIG_SCLP_TTY
++  Include support for IBM SCLP line-mode terminals.
 +
-+/*
-+ * second extended-fs super-block data in memory
-+ */
-+struct xip2_sb_info {
-+	unsigned long s_frag_size;	/* Size of a fragment in bytes */
-+	unsigned long s_frags_per_block;/* Number of fragments per block */
-+	unsigned long s_inodes_per_block;/* Number of inodes per block */
-+	unsigned long s_frags_per_group;/* Number of fragments in a group */
-+	unsigned long s_blocks_per_group;/* Number of blocks in a group */
-+	unsigned long s_inodes_per_group;/* Number of inodes in a group */
-+	unsigned long s_itb_per_group;	/* Number of inode table blocks per group */
-+	unsigned long s_gdb_count;	/* Number of group descriptor blocks */
-+	unsigned long s_desc_per_block;	/* Number of group descriptors per block */
-+	unsigned long s_groups_count;	/* Number of groups in the fs */
-+	void * s_sbp;	                /* Pointer to the super block */
-+	struct ext2_super_block * s_es;	/* Pointer to the super block in buffer */
-+	void ** s_group_desc;
-+	unsigned short s_loaded_inode_bitmaps;
-+	unsigned short s_loaded_block_bitmaps;
-+	unsigned long s_inode_bitmap_number[EXT2_MAX_GROUP_LOADED];
-+	void * s_inode_bitmap[EXT2_MAX_GROUP_LOADED];
-+	unsigned long s_block_bitmap_number[EXT2_MAX_GROUP_LOADED];
-+	void * s_block_bitmap[EXT2_MAX_GROUP_LOADED];
-+	unsigned long  s_mount_opt;
-+	uid_t s_resuid;
-+	gid_t s_resgid;
-+	unsigned short s_mount_state;
-+	unsigned short s_pad;
-+	int s_addr_per_block_bits;
-+	int s_desc_per_block_bits;
-+	int s_inode_size;
-+	int s_first_ino;
-+	void* mem_area;
-+};
++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.
 +
-+#endif	/* _LINUX_XIP2_FS_SB */
-=== include/linux/inetdevice.h
-==================================================================
---- include/linux/inetdevice.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/inetdevice.h   (/trunk/2.4.27)   (revision 52)
-@@ -26,6 +26,7 @@
- };
++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.
  
- extern struct ipv4_devconf ipv4_devconf;
-+extern struct notifier_block *inetaddr_chain;
+ 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".
  
- struct in_device
- {
-=== include/linux/timer.h
-==================================================================
---- include/linux/timer.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/timer.h   (/trunk/2.4.27)   (revision 52)
-@@ -22,6 +22,9 @@
+-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.
  
- extern void add_timer(struct timer_list * timer);
- extern int del_timer(struct timer_list * timer);
-+#ifdef CONFIG_NO_IDLE_HZ
-+extern struct timer_list *next_timer_event(void);
-+#endif
- 
- #ifdef CONFIG_SMP
- extern int del_timer_sync(struct timer_list * timer);
-=== include/linux/igmp.h
-==================================================================
---- include/linux/igmp.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/igmp.h   (/trunk/2.4.27)   (revision 52)
-@@ -183,6 +183,9 @@
- 	unsigned char		crcount;
- };
- 
-+extern int register_multicast_notifier(struct notifier_block *nb);
-+extern int unregister_multicast_notifier(struct notifier_block *nb);
+-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.
 +
- /* V3 exponential field decoding */
- #define IGMPV3_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
- #define IGMPV3_EXP(thresh, nbmant, nbexp, value) \
-=== include/linux/fs.h
-==================================================================
---- include/linux/fs.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/fs.h   (/trunk/2.4.27)   (revision 52)
-@@ -733,6 +733,7 @@
- #include <linux/usbdev_fs_sb.h>
- #include <linux/cramfs_fs_sb.h>
- #include <linux/jffs2_fs_sb.h>
-+#include <linux/xip2_fs_sb.h>
++CONFIG_DCSSBLK
++  A block device driver for DCSS segments. It can be used to create
++  a filesystem in such a segment.
  
- extern struct list_head super_blocks;
- extern spinlock_t sb_lock;
-@@ -792,6 +793,7 @@
- 		struct usbdev_sb_info   usbdevfs_sb;
- 		struct jffs2_sb_info	jffs2_sb;
- 		struct cramfs_sb_info	cramfs_sb;
-+		struct xip2_sb_info	xip2_sb;
- 		void			*generic_sbp;
- 	} u;
- 	/*
-=== include/linux/xip2_fs.h
-==================================================================
---- include/linux/xip2_fs.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/xip2_fs.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,117 @@
-+/*
-+ *  linux/include/linux/xip2_fs.h, Version 1
-+ *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
-+ */
-+
-+#ifndef _LINUX_XIP2_FS_H
-+#define _LINUX_XIP2_FS_H
-+#include <linux/ext2_fs.h>
-+
-+/* bit operations */
-+#define xip2_clear_bit ext2_clear_bit
-+#define xip2_test_bit  ext2_test_bit
-+#define xip2_set_bit   ext2_set_bit
-+#define xip2_find_next_zero_bit ext2_find_next_zero_bit
-+#define xip2_find_first_zero_bit ext2_find_first_zero_bit
-+
-+/* the memory area structure */
-+typedef struct _xip2_mem_area_t {
-+	char* name; 
-+	unsigned long start; 
-+	unsigned long end;
-+} xip2_mem_area_t;
-+
-+/*
-+ * Debug code
-+ */
-+#undef XIP2FS_DEBUG
+ 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.
 +
-+#ifdef XIP2FS_DEBUG
-+#define xip2_debug(f, a...)	{ \
-+				printk ("XIP2-fs DEBUG (%s, %d): %s:", \
-+						__FILE__, __LINE__, __FUNCTION__); \
-+				  	printk (f, ## a); \
-+					}
-+#else
-+#	define xip2_debug(f, a...)	/**/
-+#endif
++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".
 +
-+/*
-+ * Useful macros
-+ */
-+#ifdef __KERNEL__
-+#define XIP2_SB(sb)	(&((sb)->u.xip2_sb))
-+#else
-+/* Assume that user mode programs are passing in an ext2fs superblock, not
-+ * a kernel struct super_block.  This will allow us to call the feature-test
-+ * macros from user land. */
-+#define XIP2_SB(sb)	(sb)
-+#endif
++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.
 +
++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. 
 +
-+/* functions defined in fs/xip2/balloc.c */
-+extern int xip2_bg_has_super(struct super_block *sb, int group);
-+extern unsigned long xip2_bg_num_gdb(struct super_block *sb, int group);
-+extern unsigned long xip2_count_free_blocks (struct super_block *);
-+extern void xip2_check_blocks_bitmap (struct super_block *);
-+extern void* xip2_maread (xip2_mem_area_t* mem_area, int block, int size);
++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".
 +
-+/* dir.c */
-+extern ino_t xip2_inode_by_name(struct inode *, struct dentry *);
-+extern struct ext2_dir_entry_2 * xip2_find_entry (struct inode *,struct dentry *);
++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".
 +
-+/* functions defined in fs/xip2/ialloc.c */
-+extern unsigned long xip2_count_free_inodes (struct super_block *);
+ 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.
 +
-+/* functions defined in fs/xip2/inode.c */
-+extern void xip2_read_inode (struct inode *);
-+extern int xip2_get_block(struct inode *inode, long iblock, 
-+			  unsigned long* blockno_result, int create);
++  For details please refer to the documentation provided by IBM at
++  <http://www10.software.ibm.com/developerworks/opensource/linux390>.
 +
-+/* functions defined in fs/xip2/ioctl.c */
-+extern int xip2_ioctl (struct inode *, struct file *, unsigned int,
-+		       unsigned long);
++  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.
 +
-+/* functions defined in fs/xip2/super.c */
-+extern void xip2_error (struct super_block *, const char *, const char *, ...)
-+	__attribute__ ((format (printf, 3, 4)));
-+extern NORET_TYPE void xip2_panic (struct super_block *, const char *,
-+				   const char *, ...)
-+	__attribute__ ((NORET_AND format (printf, 3, 4)));
-+extern void xip2_warning (struct super_block *, const char *, const char *, ...)
-+	__attribute__ ((format (printf, 3, 4)));
-+extern void xip2_update_dynamic_rev (struct super_block *sb);
-+extern void xip2_put_super (struct super_block *);
-+extern int xip2_remount (struct super_block *, int *, char *);
-+extern struct super_block * xip2_read_super (struct super_block *,void *,int);
-+extern int xip2_statfs (struct super_block *, struct statfs *);
+ 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 kernel-source-2.4.27-2.4.27/Documentation/DocBook/Makefile
+--- kernel-source-2.4.27-2.4.27.orig/Documentation/DocBook/Makefile	2004-08-07 17:26:04.000000000 -0600
++++ kernel-source-2.4.27-2.4.27/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
 +
-+/*
-+ * Inodes and files operations
-+ */
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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"[]>
 +
-+/* dir.c */
-+extern struct file_operations xip2_dir_operations;
++<!--  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". -->
 +
-+/* file.c */
-+extern struct inode_operations xip2_file_inode_operations;
-+extern struct file_operations xip2_file_operations;
-+extern int xip2_file_mmap(struct file * file, struct vm_area_struct * vma);
-+extern struct page * xip2_nopage_in_place(struct vm_area_struct * area, 
-+					  unsigned long address, int unused);
-+extern ssize_t xip2_file_read(struct file * filp, char * buf, size_t count, 
-+			      loff_t *ppos);
++<book id="ZFCPHBAAPI">
 +
-+/* inode.c */
-+extern struct address_space_operations xip2_aops;
++<!-- book header -->
++  <bookinfo>
++    <title>ZFCP HBA API Kernel Interfaces</title>
 +
-+/* namei.c */
-+extern struct inode_operations xip2_dir_inode_operations;
++    <revhistory>
++      <revision>
++	<revnumber>v1.0 </revnumber>
++	<date>2003/09/24</date>
++	<authorinitials>AH</authorinitials>
++	<revremark>
++	  <remark>Initial version.</remark>
++	</revremark>
++      </revision>
++      <revision>
++	<revnumber>v1.1 </revnumber>
++	<date>2003/11/14</date>
++	<authorinitials>AH</authorinitials>
++	<revremark>
++	  <remark>Completed interface description for module
++	    <filename>zfcp_hbaapi</filename>.
++	  </remark>
++<!--     Added Stefan V&ouml;lkel as author. -->
++<!--     Removed improper license statement regarding ZFCP HBA API Library. -->
++<!--     Added Introduction and Bibliography. -->
++<!--     Added section about misc device provided by module zfcp_hbaapi. -->
++<!--     Added section about callback functions of module zfcp_hbaapi. -->
++	</revremark>
++      </revision>
++      <revision>
++	<revnumber>v1.2 </revnumber>
++	<date>2003/11/19</date>
++	<authorinitials>AH</authorinitials>
++	<revremark>
++	  <remark>Completed interface description for module
++	    <filename>zfcp</filename>.
++	  </remark>
++<!--     Added section about intra-kernel interfaces of module zfcp. -->
++<!--     Added section about callbacks and hooks in module zfcp. -->
++	</revremark>
++      </revision>
++    </revhistory>
 +
-+/* symlink.c */
-+extern struct inode_operations xip2_fast_symlink_inode_operations;
-+extern struct inode_operations xip2_symlink_inode_operations;
-+#endif
-=== include/linux/s390net.h
-==================================================================
---- include/linux/s390net.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/s390net.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,17 @@
-+/*
-+ * include/linux/s390net.h
-+ *
-+ * S390 and zSeries version
-+ *         Copyright (C) 2003 IBM Corporation
-+ *         Author(s): Erwin Rol <erwinrol at de.ibm.com>
-+ *
-+ */
++    <authorgroup>
++      <author>
++	<firstname>Andreas</firstname>
++	<surname>Herrman</surname>
++	<affiliation>
++	  <address><email>aherrman at de.ibm.com</email></address>
++	</affiliation>
++      </author>
++      <author>
++	<firstname>Stefan</firstname>
++	<surname>V&ouml;lkel</surname>
++	<affiliation>
++	  <address><email>Stefan.Voelkel at millenux.com</email></address>
++	</affiliation>
++      </author>
++    </authorgroup>
 +
-+#ifndef LINUX_S390NET_H
-+#define LINUX_S390NET_H
++    <copyright>
++      <year>2003</year>
++      <holder>IBM Corp.</holder>
++    </copyright>
 +
-+#define S390NET_IOC_MAGIC 'Z'
++    <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>
 +
-+#endif /* !LINUX_S390NET_H */
++    <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>
 +
-=== include/linux/mm.h
-==================================================================
---- include/linux/mm.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/mm.h   (/trunk/2.4.27)   (revision 52)
-@@ -308,11 +308,9 @@
- /* Make it prettier to test the above... */
- #define UnlockPage(page)	unlock_page(page)
- #define Page_Uptodate(page)	test_bit(PG_uptodate, &(page)->flags)
--#define SetPageUptodate(page) \
--	do {								\
--		arch_set_page_uptodate(page);				\
--		set_bit(PG_uptodate, &(page)->flags);			\
--	} while (0)
-+#ifndef SetPageUptodate
-+#define SetPageUptodate(page)	set_bit(PG_uptodate, &(page)->flags);
-+#endif
- #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)
-=== include/linux/sched.h
-==================================================================
---- include/linux/sched.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/sched.h   (/trunk/2.4.27)   (revision 52)
-@@ -141,6 +141,9 @@
- extern void cpu_init (void);
- extern void trap_init(void);
- extern void update_process_times(int user);
-+#ifdef CONFIG_NO_IDLE_HZ
-+extern void update_process_times_us(int user, int system);
-+#endif
- extern void update_one_process(struct task_struct *p, unsigned long user,
- 			       unsigned long system, int cpu);
- 
-@@ -585,6 +588,9 @@
- extern unsigned long itimer_next;
- extern struct timeval xtime;
- extern void do_timer(struct pt_regs *);
-+#ifdef CONFIG_NO_IDLE_HZ
-+extern void do_timer_ticks(int ticks);
-+#endif
- 
- extern unsigned int * prof_buffer;
- extern unsigned long prof_len;
-=== include/linux/tty.h
-==================================================================
---- include/linux/tty.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/tty.h   (/trunk/2.4.27)   (revision 52)
-@@ -380,6 +380,13 @@
- extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags,
- 				unsigned minor);
- extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor);
-+struct devfs_entry;
-+extern void tty_register_devfs_name (struct tty_driver *driver,
-+				     unsigned int flags, unsigned minor,
-+				     struct devfs_entry *dir, const char *name);
-+extern void tty_unregister_devfs_name (struct tty_driver *driver,
-+				       unsigned minor, struct devfs_entry *dir,
-+				       const char *name);
- 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);
-=== include/linux/mii.h
-==================================================================
---- include/linux/mii.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/mii.h   (/trunk/2.4.27)   (revision 52)
-@@ -19,6 +19,7 @@
- #define MII_ADVERTISE       0x04        /* Advertisement control reg   */
- #define MII_LPA             0x05        /* Link partner ability reg    */
- #define MII_EXPANSION       0x06        /* Expansion register          */
-+#define MII_EXTSTATUS       0x0f        /* Extended status register    */
- #define MII_DCOUNTER        0x12        /* Disconnect counter          */
- #define MII_FCSCOUNTER      0x13        /* False carrier counter       */
- #define MII_NWAYTEST        0x14        /* N-way auto-neg test reg     */
-@@ -32,7 +33,8 @@
- #define MII_NCONFIG         0x1c        /* Network interface config    */
- 
- /* Basic mode control register. */
--#define BMCR_RESV               0x007f  /* Unused...                   */
-+#define BMCR_RESV               0x003f  /* Unused...                   */
-+#define BMCR_SPEED1000          0x0040  /* Select 1Gbps                */
- #define BMCR_CTST               0x0080  /* Collision test              */
- #define BMCR_FULLDPLX           0x0100  /* Full duplex                 */
- #define BMCR_ANRESTART          0x0200  /* Auto negotiation restart    */
-@@ -50,13 +52,21 @@
- #define BMSR_ANEGCAPABLE        0x0008  /* Able to do auto-negotiation */
- #define BMSR_RFAULT             0x0010  /* Remote fault detected       */
- #define BMSR_ANEGCOMPLETE       0x0020  /* Auto-negotiation complete   */
--#define BMSR_RESV               0x07c0  /* Unused...                   */
-+#define BMSR_EXTSTATUS          0x0100  /* extended status             */
-+#define BMSR_RESV               0x06c0  /* Unused...                   */
- #define BMSR_10HALF             0x0800  /* Can do 10mbps, half-duplex  */
- #define BMSR_10FULL             0x1000  /* Can do 10mbps, full-duplex  */
- #define BMSR_100HALF            0x2000  /* Can do 100mbps, half-duplex */
- #define BMSR_100FULL            0x4000  /* Can do 100mbps, full-duplex */
- #define BMSR_100BASE4           0x8000  /* Can do 100mbps, 4k packets  */
- 
-+/* Extended status register */
-+#define EXTSTATUS_RESV          0x0fff  /* Unused..                        */
-+#define EXTSTATUS_1000THALF     0x1000  /* Can do 1000 BASE-T, half-duplex */
-+#define EXTSTATUS_1000TFULL     0x2000  /* Can do 1000 BASE-T, full-duplex */
-+#define EXTSTATUS_1000XHALF     0x4000  /* Can do 1000 BASE-X, half-duplex */
-+#define EXTSTATUS_1000XFULL     0x8000  /* Can do 1000 BASE-X, full-duplex */
++  <!-- 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>
 +
- /* Advertisement control register. */
- #define ADVERTISE_SLCT          0x001f  /* Selector bits               */
- #define ADVERTISE_CSMA          0x0001  /* Only selector supported     */
-=== include/linux/genhd.h
-==================================================================
---- include/linux/genhd.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/linux/genhd.h   (/trunk/2.4.27)   (revision 52)
-@@ -103,6 +103,7 @@
- 
- 	devfs_handle_t *de_arr;         /* one per physical disc */
- 	char *flags;                    /* one per physical disc */
-+	devfs_handle_t *label_arr;      /* one per physical disc */
- };
- 
- /* drivers/block/genhd.c */
-=== include/asm-ppc/pgalloc.h
-==================================================================
---- include/asm-ppc/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-ppc/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -150,5 +150,7 @@
- 
- extern int do_check_pgt_cache(int, int);
- 
-+#include <asm-generic/pgalloc.h>
-+
- #endif /* _PPC_PGALLOC_H */
- #endif /* __KERNEL__ */
-=== include/asm-mips64/pgalloc.h
-==================================================================
---- include/asm-mips64/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-mips64/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -200,4 +200,6 @@
- 
- extern int do_check_pgt_cache(int, int);
- 
-+#include <asm-generic/pgalloc.h>
-+
- #endif /* _ASM_PGALLOC_H */
-=== include/asm-sparc64/pgalloc.h
-==================================================================
---- include/asm-sparc64/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-sparc64/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -304,4 +304,6 @@
- 
- extern int do_check_pgt_cache(int, int);
- 
-+#include <asm-generic/pgalloc.h>
-+
- #endif /* _SPARC64_PGALLOC_H */
-=== include/asm-m68k/pgalloc.h
-==================================================================
---- include/asm-m68k/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-m68k/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -163,4 +163,6 @@
- #include <asm/motorola_pgalloc.h>
- #endif
- 
-+#include <asm-generic/pgalloc.h>
-+
- #endif /* M68K_PGALLOC_H */
-=== include/asm-alpha/pgalloc.h
-==================================================================
---- include/asm-alpha/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-alpha/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -347,4 +347,6 @@
- 
- extern int do_check_pgt_cache(int, int);
- 
-+#include <asm-generic/pgalloc.h>
-+
- #endif /* _ALPHA_PGALLOC_H */
-=== include/asm-s390x/siginfo.h
-==================================================================
---- include/asm-s390x/siginfo.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/siginfo.h   (/trunk/2.4.27)   (revision 52)
-@@ -19,7 +19,7 @@
- } sigval_t;
- 
- #define SI_MAX_SIZE	128
--#define SI_PAD_SIZE	((SI_MAX_SIZE/sizeof(int)) - 3)
-+#define SI_PAD_SIZE	((SI_MAX_SIZE/sizeof(int)) - 4)
- 
- typedef struct siginfo {
- 	int si_signo;
-=== include/asm-s390x/smp.h
-==================================================================
---- include/asm-s390x/smp.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/smp.h   (/trunk/2.4.27)   (revision 52)
-@@ -26,6 +26,9 @@
- 	__u16      cpu;
- } sigp_info;
- 
-+extern int smp_call_function_on(void (*func) (void *info), void *info,
-+				int nonatomic, int wait, int cpu);
-+
- extern unsigned long cpu_online_map;
- 
- #define NO_PROC_ID		0xFF		/* No processor magic marker */
-@@ -65,4 +68,9 @@
- #define cpu_logical_map(cpu) (cpu)
- 
- #endif
-+
-+#ifndef CONFIG_SMP
-+#define smp_call_function_on(func,info,nonatomic,wait,cpu)      ({ 0; })
- #endif
-+
-+#endif
-=== include/asm-s390x/sigp.h
-==================================================================
---- include/asm-s390x/sigp.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/sigp.h   (/trunk/2.4.27)   (revision 52)
-@@ -107,7 +107,7 @@
-  * Signal processor with parameter and return status
-  */
- extern __inline__ sigp_ccode
--signal_processor_ps(__u32 *statusptr, __u64 parameter,
-+signal_processor_ps(unsigned long *statusptr, __u64 parameter,
- 		    __u16 cpu_addr, sigp_order_code order_code)
- {
- 	sigp_ccode ccode;
-=== include/asm-s390x/chandev.h
-==================================================================
---- include/asm-s390x/chandev.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/chandev.h   (/trunk/2.4.27)   (revision 52)
-@@ -28,6 +28,7 @@
- 	chandev_type_osad=0x8,
- 	chandev_type_qeth=0x10,
- 	chandev_type_claw=0x20,
-+       chandev_type_ctcmpc=0x40,      
- } chandev_type;
- 
- typedef enum
-=== include/asm-s390x/lowcore.h
-==================================================================
---- include/asm-s390x/lowcore.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/lowcore.h   (/trunk/2.4.27)   (revision 52)
-@@ -48,6 +48,7 @@
- #define __LC_IPLDEV                     0xDB8
- 
- #define __LC_JIFFY_TIMER		0xDC0
-+#define __LC_INT_CLOCK			0xDC8
- 
- #define __LC_PANIC_MAGIC                0xE00
- 
-@@ -164,8 +165,9 @@
- 
-         /* SMP info area: defined by DJB */
-         __u64        jiffy_timer;              /* 0xdc0 */
--	__u64        ext_call_fast;            /* 0xdc8 */
--        __u8         pad12[0xe00-0xdd0];       /* 0xdd0 */
-+        __u64        int_clock;                /* 0xdc8 */
-+	__u64        ext_call_fast;            /* 0xdd0 */
-+        __u8         pad12[0xe00-0xdd8];       /* 0xdd8 */
- 
-         /* 0xe00 is used as indicator for dump tools */
-         /* whether the kernel died with panic() or not */
-=== include/asm-s390x/dasd.h
-==================================================================
---- include/asm-s390x/dasd.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/dasd.h   (/trunk/2.4.27)   (revision 52)
-@@ -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
-  *
-- * $Revision: 1.53 $
-+ * $Revision: 1.52.6.3 $
-  *
-  * History of changes (starts July 2000)
-  * 05/04/01 created by moving the kernel interface to drivers/s390/block/dasd_int.h
-=== include/asm-s390x/irq.h
-==================================================================
---- include/asm-s390x/irq.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/irq.h   (/trunk/2.4.27)   (revision 52)
-@@ -42,7 +42,9 @@
-       __u8  chpid[8];     /* CHPID 0-7 (if available) */
-       __u32 unused1 : 8;  /* reserved zeros */
-       __u32 st      : 3;  /* subchannel type */
--      __u32 unused2 : 20; /* reserved zeros */
-+      __u32 unused2 : 18; /* reserved zeros */
-+      __u32 mbfc    : 1;  /* measurement block format control */
-+      __u32 xmwme   : 1;  /* extended measurement word mode enable */
-       __u32 csense  : 1;  /* concurrent sense; can be enabled ...*/
-                           /*  ... per MSCH, however, if facility */
-                           /*  ... is not installed, this results */
-@@ -156,7 +158,8 @@
- typedef struct {
-       pmcw_t pmcw;             /* path management control word */
-       scsw_t scsw;             /* subchannel status word */
--      __u8 mda[12];            /* model dependent area */
-+      __u64 mba;               /* measurement block address */
-+      __u8 mda[4];             /* model dependent area */
-    } __attribute__ ((packed,aligned(4))) schib_t;
- #endif /* __KERNEL__ */
- 
-=== include/asm-s390x/ccwcache.h
-==================================================================
---- include/asm-s390x/ccwcache.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/ccwcache.h   (/trunk/2.4.27)   (revision 52)
-@@ -4,7 +4,7 @@
-  * Bugreports.to..: <Linux390 at de.ibm.com>
-  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
-  *
-- * $Revision: 1.11 $
-+ * $Revision: 1.11.4.2 $
-  *
-  */
- #ifndef CCWCACHE_H
-=== include/asm-s390x/qeth.h
-==================================================================
---- include/asm-s390x/qeth.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/qeth.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,26 @@
-+/*
-+ * include/asm-s390x/qeth.h
-+ *
-+ * S390 and zSeries version
-+ *         Copyright (C) 2003 IBM Corporation
-+ *         Author(s): Erwin Rol <erwinrol at de.ibm.com>
-+ *
-+ */
-+
-+#include <linux/s390net.h>
-+
-+#ifndef ASM_QETH_H
-+#define ASM_QETH_H
++  <!-- 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>
 +
-+#define QETH_IOCPROC_REGISTER		_IOW(S390NET_IOC_MAGIC, 1, int)
++  <!-- 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>
 +
-+#define SIOC_QETH_ARP_SET_NO_ENTRIES	_IOWR(S390NET_IOC_MAGIC, 2, int)
-+#define SIOC_QETH_ARP_QUERY_INFO	_IOWR(S390NET_IOC_MAGIC, 3, int)
-+#define SIOC_QETH_ARP_ADD_ENTRY		_IOWR(S390NET_IOC_MAGIC, 4, int)
-+#define SIOC_QETH_ARP_REMOVE_ENTRY	_IOWR(S390NET_IOC_MAGIC, 5, int)
-+#define SIOC_QETH_ARP_FLUSH_CACHE	_IOWR(S390NET_IOC_MAGIC, 6, int)
-+#define SIOC_QETH_ADP_SET_SNMP_CONTROL	_IOWR(S390NET_IOC_MAGIC, 7, int)
-+#define SIOC_QETH_GET_CARD_TYPE		_IOWR(S390NET_IOC_MAGIC, 8, int)
++    <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>
 +
-+#endif /* !ASM_QETH_H */
++  <!-- 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>
 +
-=== include/asm-s390x/pgtable.h
-==================================================================
---- include/asm-s390x/pgtable.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/pgtable.h   (/trunk/2.4.27)   (revision 52)
-@@ -484,9 +484,11 @@
- 	__pte;                                                            \
- })
++</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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/Documentation/s390/TAPE
+--- kernel-source-2.4.27-2.4.27.orig/Documentation/s390/TAPE	2001-07-25 15:12:01.000000000 -0600
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/Makefile
+--- kernel-source-2.4.27-2.4.27.orig/Makefile	2006-01-30 22:23:45.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/Makefile	2006-01-30 22:25:25.000000000 -0700
+@@ -292,7 +292,7 @@
+ boot: vmlinux
+ 	@$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C arch/$(ARCH)/boot
  
--#define arch_set_page_uptodate(__page)					  \
-+#define SetPageUptodate(_page) \
- 	do {								  \
--		asm volatile ("sske %0,%1" : : "d" (0),			  \
-+		struct page *__page = (_page);				  \
-+		if (!test_and_set_bit(PG_uptodate, &__page->flags))	  \
-+			asm volatile ("sske %0,%1" : : "d" (0),		  \
- 			      "a" (__pa((__page-mem_map) << PAGE_SHIFT)));\
- 	} while (0)
+-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
  
-=== include/asm-s390x/qdio.h
-==================================================================
---- include/asm-s390x/qdio.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/qdio.h   (/trunk/2.4.27)   (revision 52)
-@@ -11,7 +11,7 @@
- #ifndef __QDIO_H__
- #define __QDIO_H__
++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
  
--#define VERSION_QDIO_H "$Revision: 1.57 $"
-+#define VERSION_QDIO_H "$Revision: 1.57.4.5 $"
+@@ -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
  
- /* note, that most of the typedef's are from ingo. */
++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 $@ $<
  
-@@ -42,11 +42,18 @@
- #define QDIO_MAX_ELEMENTS_PER_BUFFER 16
- #define SBAL_SIZE 256
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/Makefile kernel-source-2.4.27-2.4.27/arch/s390/Makefile
+--- kernel-source-2.4.27-2.4.27.orig/arch/s390/Makefile	2002-08-02 18:39:43.000000000 -0600
++++ kernel-source-2.4.27-2.4.27/arch/s390/Makefile	2006-01-30 22:25:24.000000000 -0700
+@@ -23,15 +23,18 @@
+ LINKFLAGS =-T $(TOPDIR)/arch/s390/vmlinux.lds $(LDFLAGS)
+ endif
  
--#define IQDIO_FILL_LEVEL_TO_POLL (QDIO_MAX_BUFFERS_PER_Q*4/3)
-+/* unfortunately this can't be (QDIO_MAX_BUFFERS_PER_Q*4/3) or so -- as
-+ * we never know, whether we'll get initiative again, e.g. to give the
-+ * transmit skb's back to the stack, however the stack may be waiting for
-+ * them... therefore we define 4 as threshold to start polling (which
-+ * will stop as soon as the asynchronous queue catches up)
-+ * btw, this only applies to the asynchronous HiperSockets queue */
-+#define IQDIO_FILL_LEVEL_TO_POLL 4
- 
- #define TIQDIO_THININT_ISC 3
- #define TIQDIO_DELAY_TARGET 0
--#define QDIO_BUSY_BIT_PATIENCE 2000 /* in microsecs */
-+#define QDIO_BUSY_BIT_PATIENCE 100 /* in microsecs */
-+#define QDIO_BUSY_BIT_GIVE_UP 10000000 /* 10 seconds */
- #define IQDIO_GLOBAL_LAPS 2 /* GLOBAL_LAPS are not used as we */
- #define IQDIO_GLOBAL_LAPS_INT 1 /* dont global summary */
- #define IQDIO_LOCAL_LAPS 4
-@@ -612,6 +619,8 @@
- typedef struct qdio_q_t {
- 	volatile slsb_t slsb;
- 
-+	char unused[QDIO_MAX_BUFFERS_PER_Q];
-+
- 	__u32 * volatile dev_st_chg_ind;
- 
- 	int is_input_q;
-@@ -697,7 +706,9 @@
- 		int last_transfer_index; */
- 
- 		__u64 last_transfer_time;
-+		__u64 busy_start;
- 	} timing;
-+	atomic_t busy_siga_counter;
-         unsigned int queue_type;
- } __attribute__ ((aligned(256))) qdio_q_t;
- 
-@@ -713,7 +724,7 @@
- 	unsigned int sync_done_on_outb_pcis;
- 
- 	unsigned int state;
--	spinlock_t setting_up_lock;
-+	struct semaphore setting_up_lock;
- 
- 	unsigned int no_input_qs;
- 	unsigned int no_output_qs;
-@@ -783,7 +794,10 @@
- 				int reserved1:21;
- 				int isc:3;
- 				/* word 9&10 */
--				__u32 reserved2[2];
-+                                __u32 word_with_d_bit;
-+                                /* set to 0x10000000 to enable
-+                                 * time delay disablement facility */
-+                                __u32 reserved2;
- 				/* word 11 */
- 				__u32 subsystem_id;
- 				/* word 12-1015 */
-=== include/asm-s390x/setup.h
-==================================================================
---- include/asm-s390x/setup.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/setup.h   (/trunk/2.4.27)   (revision 52)
-@@ -32,7 +32,7 @@
- #define MACHINE_NEW_STIDP	(machine_flags & 64)
- #define MACHINE_HAS_PFIX  	(0)
++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)
  
--#define MACHINE_HAS_HWC		(!MACHINE_IS_P390)
-+#define MACHINE_HAS_SCLP	(!MACHINE_IS_P390)
+ HEAD := arch/s390/kernel/head.o arch/s390/kernel/init_task.o
  
- /*
-  * Console mode. Override with conmode=
-@@ -41,10 +41,10 @@
- extern unsigned int console_device;
+ 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
  
- #define CONSOLE_IS_UNDEFINED	(console_mode == 0)
--#define CONSOLE_IS_HWC		(console_mode == 1)
-+#define CONSOLE_IS_SCLP		(console_mode == 1)
- #define CONSOLE_IS_3215		(console_mode == 2)
- #define CONSOLE_IS_3270		(console_mode == 3)
--#define SET_CONSOLE_HWC		do { console_mode = 1; } while (0)
-+#define SET_CONSOLE_SCLP	do { console_mode = 1; } while (0)
- #define SET_CONSOLE_3215	do { console_mode = 2; } while (0)
- #define SET_CONSOLE_3270	do { console_mode = 3; } while (0)
+@@ -39,7 +42,7 @@
+   CORE_FILES := $(CORE_FILES) arch/s390/math-emu/math-emu.o
+ endif
  
-=== include/asm-s390x/processor.h
-==================================================================
---- include/asm-s390x/processor.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/processor.h   (/trunk/2.4.27)   (revision 52)
-@@ -52,6 +52,8 @@
+-all: image listing
++all: image
  
- extern void print_cpu_info(struct cpuinfo_S390 *);
+ listing: vmlinux
+ 	@$(MAKEBOOT) listing
+@@ -47,6 +50,9 @@
+ arch/s390/kernel: dummy
+ 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/kernel
  
-+extern void show_trace(unsigned long* esp);
++arch/s390/appldata: dummy
++	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/appldata
 +
- /* Lazy FPU handling on uni-processor */
- extern struct task_struct *last_task_used_math;
- 
-=== include/asm-s390x/ioctl32.h
-==================================================================
---- include/asm-s390x/ioctl32.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/ioctl32.h   (/trunk/2.4.27)   (revision 52)
-@@ -8,7 +8,7 @@
- #ifndef ASM_IOCTL32_H
- #define ASM_IOCTL32_H
- 
--extern int sys_ioctl(unsigned int, unsigned int, unsigned long, struct file*);
-+extern int sys_ioctl(unsigned int, unsigned int, unsigned long);
- typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
+ arch/s390/mm: dummy
+ 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/mm
  
- #ifdef CONFIG_S390_SUPPORT
-=== include/asm-s390x/dcss.h
-==================================================================
---- include/asm-s390x/dcss.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/dcss.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,19 @@
-+/*
-+ *  include/asm-s390x/dcss.h
-+ *
-+ *  definitions for discontiguous saved segment support
-+ *  Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ */
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/Makefile kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/appldata/Makefile	2006-01-30 22:25:24.000000000 -0700
+@@ -0,0 +1,13 @@
++#
++# Linux - VM Monitor Stream, Stage 1
++#
 +
-+#ifndef _ASM_S390X_DCSS_H
-+#define _ASM_S390X_DCSS_H
-+#ifndef __ASSEMBLY__
-+#define SEGMENT_SHARED_RW       0
-+#define SEGMENT_SHARED_RO       1
-+#define SEGMENT_EXCLUSIVE_RW    2
-+#define SEGMENT_EXCLUSIVE_RO    3
-+extern int segment_load (char *name,int segtype,unsigned long *addr,unsigned long *length);
-+extern void segment_unload(char *name);
-+extern void segment_replace(char *name);
-+#endif
-+#endif
-=== include/asm-s390x/cmb.h
-==================================================================
---- include/asm-s390x/cmb.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/cmb.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,194 @@
-+/*
-+ * include/asm-s390/cmb.h
-+ * include/asm-s390x/cmb.h
-+ *
-+ *         Copyright (C) 2003 IBM Corporation
-+ *         Author: Arnd Bergmann <arndb at de.ibm.com>
-+ */
++O_TARGET := appldata.o
 +
-+#ifndef S390_CMB_H
-+#define S390_CMB_H
-+/**
-+ * struct cmbdata -- channel measurement block data for user space
++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
++
++include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/appldata/appldata.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/appldata/appldata.h	2006-01-30 22:25:24.000000000 -0700
+@@ -0,0 +1,58 @@
++/*
++ * arch/s390/appldata/appldata.h
 + *
-+ * @size:			size of the stored data
-+ * @elapsed_time:		time since last reset
-+ * @ssch_rsch_count:		number I/O attempts, may overflow at
-+ * 				2^16 or 2^32, depending on machine
-+ * @sample_count:		number of sampled I/O attempts, should
-+ * 				be identical to @ssch_rsch_count
-+ * @device_connect_time:	time that device communicated with
-+ * 				channel
-+ * @function_pending_time:	time between command initiate and
-+ * 				start of execution
-+ * @device_disconnect_time:	time of device-inactive while
-+ * 				subchannel-active
-+ * @control_unit_queuing_time:	time that a device is blocked by
-+ * 				I/O from another system
-+ * @device_active_only_time:	time of device-active while
-+ * 				subchannel-inactive
-+ * @device_busy_time:		time where the device is busy
-+ * 				when attemting to start I/O
-+ * @initial_command_response_time: time between sending and 
-+ * 				the device accepting a command
++ * Definitions for Linux - z/VM Monitor Stream.
 + *
-+ * all values are stored as 64 bit for simplicity, especially
-+ * in 32 bit emulation mode. All time values are normalized to
-+ * nanoseconds.
-+ * Currently, two formats are known, which differ by the size of
-+ * this structure, i.e. the last two members are only set when
-+ * the extended channel measurement facility (first shipped in
-+ * z990 machines) is activated.
-+ * Potentially, more fields could be added, which results in a
-+ * new ioctl number.
-+ **/
-+struct cmbdata {
-+	__u64 size;
-+	__u64 elapsed_time;
-+ /* basic and exended format: */
-+	__u64 ssch_rsch_count;
-+	__u64 sample_count;
-+	__u64 device_connect_time;
-+	__u64 function_pending_time;
-+	__u64 device_disconnect_time;
-+	__u64 control_unit_queuing_time;
-+	__u64 device_active_only_time;
-+ /* extended format only: */
-+	__u64 device_busy_time;
-+	__u64 initial_command_response_time;
-+};
-+
-+/* enable channel measurement */
-+#define BIODASDCMFENABLE	_IO(DASD_IOCTL_LETTER,32)
-+/* enable channel measurement */
-+#define BIODASDCMFDISABLE	_IO(DASD_IOCTL_LETTER,33)
-+/* read channel measurement data */
-+#define BIODASDREADALLCMB	_IOWR(DASD_IOCTL_LETTER,33,struct cmbdata)
-+
-+#ifdef __KERNEL__
-+#include <linux/config.h>
-+#include <asm/irq.h>
-+
-+#if defined(CONFIG_S390_CMF) || defined(CONFIG_S390_CMF_MODULE)
-+/**
-+ * struct cmf_device - basic building block of the CMF code
++ * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH.
 + *
-+ * @cmb_list:	entry in the global list of cmf_devices
-+ * @ccwlock:	a pointer to the subchannel's spinlock
-+ * @cmb_start_time: clock value of previous cmf_reset operation
-+ * @cmb:	pointer to the hardware channel measurement block
-+ * @callback:	function pointer called by cmf_device_callback()
-+ * @irq:	subchannel number of the associated device
++ * Author: Gerald Schaefer <geraldsc at de.ibm.com>
 + */
-+struct cmf_device {
-+	struct list_head cmb_list;
-+	spinlock_t *ccwlock;
-+	u64 cmb_start_time;
-+	void *cmb;
-+	void (*callback)(struct cmf_device *);
-+	u16  irq;
-+};
 +
-+/**
-+ * cmf_device_callback - call the callback function for set_cmf
-+ *
-+ * cmf_device_callback() can be integrated in an appropriate
-+ * point in the device driver where no I/O is ongoing on the
-+ * subchannel and it is safe to call set_cmf.
-+ * This is a nop when CONFIG_S390_CMF is disabled
-+ */
-+static inline void cmf_device_callback(struct cmf_device *cdev)
-+{
-+	if (cdev->callback) {
-+		cdev->callback(cdev);
-+	}
-+}
++//#define APPLDATA_DEBUG		/* Debug messages on/off */
 +
-+/**
-+ * cmf_device_init - initialize the cmf_device structure
-+ *
-+ * cmf_device_init() needs to be called before doing any other operations
-+ * on a cmf_device.
-+ */
-+static inline void cmf_device_init(struct cmf_device *cdev, int devno)
-+{
-+	INIT_LIST_HEAD (&cdev->cmb_list);
-+	cdev->irq = get_irq_by_devno(devno);
-+	cdev->ccwlock = get_irq_lock(cdev->irq);
-+}
++#define APPLDATA_MAX_REC_SIZE	  4024	/* Maximum size of the */
++					/* data buffer */
++					
++#define APPLDATA_PROC_NAME_LENGTH 16	/* Max. length of /proc name */
 +
-+#else /* !CONFIG_S390_CMF */
-+struct cmf_device { };
-+static inline void cmf_device_callback(struct cmf_device *cdev)        { }
-+static inline void cmf_device_init(struct cmf_device *cdev, int devno) { }
-+#endif
++#define APPLDATA_RECORD_MEM_ID		0x01	/* IDs to identify the */
++#define APPLDATA_RECORD_OS_ID		0x02	/* individual records, */
++#define APPLDATA_RECORD_NET_SUM_ID	0x03	/* must be < 256 !     */
 +
-+/**
-+ * enable_cmf() - switch on the channel measurement for a specific device
-+ *  @cdev:	The ccw device to be enabled
-+ *  returns 0 for success or a negative error value.
-+ *
-+ *  This function only allocates memory for the measurement block, the
-+ *  actual activation is done with set_cmf()
-+ *
-+ *  Context:
-+ *    may sleep, device is disabled
-+ **/
-+extern int enable_cmf(struct cmf_device *cdev);
++#define CTL_APPLDATA 		2120	/* sysctl IDs, must be unique */
++#define CTL_APPLDATA_TIMER 	2121
++#define CTL_APPLDATA_INTERVAL 	2122
++#define CTL_APPLDATA_MEM	2123
++#define CTL_APPLDATA_OS		2124
++#define CTL_APPLDATA_NET_SUM	2125
 +
-+/**
-+ * disable_cmf() - switch off the channel measurement for a specific device
-+ *  @cdev:	The ccw device to be disabled
-+ *  returns 0 for success or a negative error value.
-+ *
-+ *  This function only frees the memory allocated with enable_cmf. If
-+ *  measurement has been activated with set_cmf(), it also needs to be 
-+ *  deactivated with that function before calling disable_cmf(). 
-+ *
-+ *  Context:
-+ *    may sleep, device is enabled and inactive
-+ **/
-+extern void disable_cmf(struct cmf_device *cdev);
++#define ULL(var) (unsigned long long) var
 +
-+/**
-+ * cmf_readall() - read one value from the current channel measurement block
-+ * @cmf:	the device to be read
-+ * @data:	a pointer to a data block that will be filled
-+ *
-+ *  Context:
-+ *    device is active
-+ **/
-+extern int cmf_readall(struct cmf_device *cdev, struct cmbdata*data);
++#define P_INFO(x...)	printk(KERN_INFO MY_PRINT_NAME " info: " x)
++#define P_ERROR(x...)   printk(KERN_ERR MY_PRINT_NAME " error: " x)
++#define P_STATUS(x...)  printk(KERN_WARNING MY_PRINT_NAME " status: " x)
 +
-+/**
-+ * set_cmf() - Set the measurement mode for a specific device
-+ * @cmf:	the device to be modified
-+ * @mme:	measurement mode enable value, can be either
-+ *		0 for deactivation or 2 for activation
-+ *
-+ * It is important to call this function only when there is no I/O
-+ * activity on the subchannel. Therefore it may be necessary to call
-+ * it from an interrupt handler at the point where the previous
-+ * request is finished.
-+ * In 2.6.x, set_cmf() is integrated within enable_cmf() and disable_cmf(),
-+ * which makes a lot of sense and life much easier for users.
-+ *
-+ *  Context:
-+ *    device lock held, device is enabled, subchannel is idle
-+ **/
-+extern int set_cmf(struct cmf_device *cdev, u32 mme);
++#ifdef APPLDATA_DEBUG
++#define P_DEBUG(x...)   printk(KERN_DEBUG MY_PRINT_NAME " debug: " x)
++#else
++#define P_DEBUG(x...)   do {} while (0)
++#endif
 +
-+/**
-+ * reset_cmf() - clear a channel measurement block
-+ * @cmf:	the device to be cleared
-+ *
-+ * This function is used to set all values in a channel measurement block
-+ * to sane values. It should be called between enable_cmf() and set_cmf(cdev,2)
-+ * but can be called as well when the device is already active.
-+ *
-+ *  Context:
-+ *    device is enabled
-+ **/
-+extern void cmf_reset(struct cmf_device *cdev);
-+#endif /* __KERNEL__ */
-+#endif /* S390_CMB_H */
-=== include/asm-s390x/kmap_types.h
-==================================================================
---- include/asm-s390x/kmap_types.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/kmap_types.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,16 @@
-+#ifndef _ASM_KMAP_TYPES_H
-+#define _ASM_KMAP_TYPES_H
++struct appldata_ops {
++	struct list_head list;
++	struct ctl_table_header *sysctl_header;
++	struct ctl_table *ctl_table;
++	int    active;				/* monitoring status */
 +
-+enum km_type {
-+	KM_BOUNCE_READ,
-+	KM_SKB_SUNRPC_DATA,
-+	KM_SKB_DATA_SOFTIRQ,
-+	KM_USER0,
-+	KM_USER1,
-+	KM_BH_IRQ,
-+	KM_SOFTIRQ0,
-+	KM_SOFTIRQ1,
-+	KM_TYPE_NR
++	/* fill in from here */
++	unsigned int ctl_nr;			/* sysctl ID */
++	char name[APPLDATA_PROC_NAME_LENGTH];	/* name of /proc fs node */
++	unsigned char record_nr;		/* Record Nr. for Product ID */
++	void (*callback)(void *data);		/* callback function */
++	void *data;				/* record data */
++	unsigned int size;			/* size of record */
++	struct module *owner;			/* THIS_MODULE */
 +};
 +
-+#endif
-=== include/asm-s390x/timer.h
-==================================================================
---- include/asm-s390x/timer.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/timer.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,48 @@
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/appldata/appldata_base.c	2006-01-30 22:25:24.000000000 -0700
+@@ -0,0 +1,650 @@
 +/*
-+ *  include/asm-s390/timer.h
++ * arch/s390/appldata/appldata_base.c
 + *
-+ *  (C) Copyright IBM Corp. 2003
-+ *  Virtual CPU timer
++ * Base infrastructure for Linux-z/VM Monitor Stream, Stage 1.
++ * Exports appldata_register_ops() and appldata_unregister_ops() for the
++ * data gathering modules.
 + *
-+ *  Author: Jan Glauber (jang at de.ibm.com)
++ * 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>
 +
-+#ifndef _ASM_S390_TIMER_H
-+#define _ASM_S390_TIMER_H
++#include "appldata.h"
 +
-+#include <linux/timer.h>
 +
-+#define VTIMER_MAX_SLICE (0x7ffffffffffff000LL)
++#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
 +
-+struct vtimer_list {
-+	struct list_head entry;
++#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
 +
-+	int cpu;
-+	__u64 expires;
-+	__u64 interval;
++#else
 +
-+	spinlock_t lock;
-+	unsigned long magic;
++#define APPLDATA_START_INTERVAL_REC 0x80
++#define APPLDATA_STOP_REC   	    0x81
++#define APPLDATA_GEN_EVENT_RECORD   0x82
++#define APPLDATA_START_CONFIG_REC   0x83
 +
-+	void (*function)(unsigned long, struct pt_regs*);
-+	unsigned long data;
-+};
++#endif /* CONFIG_ARCH_S390X */
 +
-+/* the offset value will wrap after ca. 71 years */
-+struct vtimer_queue {
-+	struct list_head list;
-+	spinlock_t lock;
-+	__u64 to_expire;	  /* current event expire time */
-+	__u64 offset;		  /* list offset to zero */
-+	__u64 idle;		  /* temp var for idle */
++
++/*
++ * 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 */
 +
-+void set_vtimer(__u64 expires);
++/*
++ * /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);
 +
-+extern void init_virt_timer(struct vtimer_list *timer);
-+extern void add_virt_timer(void *new);
-+extern void add_virt_timer_periodic(void *new);
-+extern int mod_virt_timer(struct vtimer_list *timer, __u64 expires);
-+extern int del_virt_timer(struct vtimer_list *timer);
++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 }
++};
 +
-+#endif
-=== include/asm-s390x/tape390.h
-==================================================================
---- include/asm-s390x/tape390.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390x/tape390.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,39 @@
-+/*************************************************************************
-+ *
-+ * tape390.h
-+ *         enables user programs to display messages on the tape device
-+ *
-+ *  S390 and zSeries version
-+ *         Copyright (C) 2001 IBM Corporation
-+ *         Author(s): Despina Papadopoulou <despina_p at de.ibm.com>
-+ *
-+ *************************************************************************/
-+
-+#ifndef _TAPE390_H
-+#define _TAPE390_H
-+
-+#define TAPE390_DISPLAY _IOW('d', 1, struct display_struct)
++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 }
++};
 +
 +/*
-+ * The TAPE390_DISPLAY ioctl calls the Load Display command
-+ * which transfers 17 bytes of data from the channel to the subsystem:
-+ *     - 1 format control byte, and
-+ *     - two 8-byte messages
-+ *
-+ * Format control byte:
-+ *   0-2: New Message Overlay
-+ *     3: Alternate Messages
-+ *     4: Blink Message
-+ *     5: Display Low/High Message
-+ *     6: Reserved
-+ *     7: Automatic Load Request
-+ *
++ * 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;
 +
-+typedef struct display_struct {
-+        char cntrl;
-+        char message1[8];
-+        char message2[8];
-+} display_struct;
-+
-+#endif 
-=== include/asm-i386/pgalloc.h
-==================================================================
---- include/asm-i386/pgalloc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-i386/pgalloc.h   (/trunk/2.4.27)   (revision 52)
-@@ -235,4 +235,6 @@
- 	flush_tlb_mm(mm);
- }
- 
-+#include <asm-generic/pgalloc.h>
-+
- #endif /* _I386_PGALLOC_H */
-=== include/asm-s390/smp.h
-==================================================================
---- include/asm-s390/smp.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/smp.h   (/trunk/2.4.27)   (revision 52)
-@@ -26,6 +26,9 @@
- 	__u16      cpu;
- } sigp_info;
- 
-+extern int smp_call_function_on(void (*func) (void *info), void *info,
-+				int nonatomic, int wait, int cpu);
-+
- extern unsigned long cpu_online_map;
- 
- #define NO_PROC_ID		0xFF		/* No processor magic marker */
-@@ -46,23 +49,28 @@
- 
- extern __inline__ int cpu_logical_map(int cpu)
- {
--        return cpu;
-+	return cpu;
- }
- 
- extern __inline__ int cpu_number_map(int cpu)
- {
--        return cpu;
-+	return cpu;
- }
- 
- extern __inline__ __u16 hard_smp_processor_id(void)
- {
--        __u16 cpu_address;
-+	__u16 cpu_address;
-  
--        __asm__ ("stap %0\n" : "=m" (cpu_address));
--        return cpu_address;
-+	__asm__ ("stap %0\n" : "=m" (cpu_address));
-+	return cpu_address;
- }
- 
- #define cpu_logical_map(cpu) (cpu)
- 
- #endif
-+
-+#ifndef CONFIG_SMP
-+#define smp_call_function_on(func,info,nonatomic,wait,cpu)      ({ 0; })
- #endif
-+
-+#endif
-=== include/asm-s390/sigp.h
-==================================================================
---- include/asm-s390/sigp.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/sigp.h   (/trunk/2.4.27)   (revision 52)
-@@ -106,7 +106,7 @@
-  * Signal processor with parameter and return status
-  */
- extern __inline__ sigp_ccode
--signal_processor_ps(__u32 *statusptr, __u32 parameter,
-+signal_processor_ps(unsigned long *statusptr, __u32 parameter,
- 		    __u16 cpu_addr, sigp_order_code order_code)
- {
- 	sigp_ccode ccode;
-=== include/asm-s390/chandev.h
-==================================================================
---- include/asm-s390/chandev.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/chandev.h   (/trunk/2.4.27)   (revision 52)
-@@ -28,6 +28,7 @@
- 	chandev_type_osad=0x8,
- 	chandev_type_qeth=0x10,
- 	chandev_type_claw=0x20,
-+       chandev_type_ctcmpc=0x40,      
- } chandev_type;
- 
- typedef enum
-=== include/asm-s390/lowcore.h
-==================================================================
---- include/asm-s390/lowcore.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/lowcore.h   (/trunk/2.4.27)   (revision 52)
-@@ -47,6 +47,7 @@
- #define __LC_IPLDEV                     0xC7C
- 
- #define __LC_JIFFY_TIMER		0xC80
-+#define __LC_INT_CLOCK			0xC88
- 
- #define __LC_PANIC_MAGIC                0xE00
- 
-@@ -165,8 +166,9 @@
- 
-         /* SMP info area: defined by DJB */
-         __u64        jiffy_timer;              /* 0xc80 */
--	atomic_t     ext_call_fast;            /* 0xc88 */
--        __u8         pad11[0xe00-0xc8c];       /* 0xc8c */
-+        __u64        int_clock;                /* 0xc88 */
-+	atomic_t     ext_call_fast;            /* 0xc90 */
-+        __u8         pad11[0xe00-0xc94];       /* 0xc94 */
- 
-         /* 0xe00 is used as indicator for dump tools */
-         /* whether the kernel died with panic() or not */
-=== include/asm-s390/dasd.h
-==================================================================
---- include/asm-s390/dasd.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/dasd.h   (/trunk/2.4.27)   (revision 52)
-@@ -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
-  *
-- * $Revision: 1.53 $
-+ * $Revision: 1.52.6.3 $
-  *
-  * History of changes (starts July 2000)
-  * 05/04/01 created by moving the kernel interface to drivers/s390/block/dasd_int.h
-=== include/asm-s390/irq.h
-==================================================================
---- include/asm-s390/irq.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/irq.h   (/trunk/2.4.27)   (revision 52)
-@@ -42,7 +42,9 @@
-       __u8  chpid[8];     /* CHPID 0-7 (if available) */
-       __u32 unused1 : 8;  /* reserved zeros */
-       __u32 st      : 3;  /* subchannel type */
--      __u32 unused2 : 20; /* reserved zeros */
-+      __u32 unused2 : 18; /* reserved zeros */
-+      __u32 mbfc    : 1;  /* measurement block format control */
-+      __u32 xmwme   : 1;  /* extended measurement word mode enable */
-       __u32 csense  : 1;  /* concurrent sense; can be enabled ...*/
-                           /*  ... per MSCH, however, if facility */
-                           /*  ... is not installed, this results */
-@@ -156,7 +158,8 @@
- typedef struct {
-       pmcw_t pmcw;             /* path management control word */
-       scsw_t scsw;             /* subchannel status word */
--      __u8 mda[12];            /* model dependent area */
-+      __u64 mba;               /* measurement block address */
-+      __u8 mda[4];             /* model dependent area */
-    } __attribute__ ((packed,aligned(4))) schib_t;
- #endif /* __KERNEL__ */
- 
-=== include/asm-s390/ccwcache.h
-==================================================================
---- include/asm-s390/ccwcache.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/ccwcache.h   (/trunk/2.4.27)   (revision 52)
-@@ -4,7 +4,7 @@
-  * Bugreports.to..: <Linux390 at de.ibm.com>
-  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
-  *
-- * $Revision: 1.9 $
-+ * $Revision: 1.9.4.2 $
-  *
-  */
- #ifndef CCWCACHE_H
-=== include/asm-s390/qeth.h
-==================================================================
---- include/asm-s390/qeth.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/qeth.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,26 @@
 +/*
-+ * include/asm-s390/qeth.h
-+ *
-+ * S390 and zSeries version
-+ *         Copyright (C) 2003 IBM Corporation
-+ *         Author(s): Erwin Rol <erwinrol at de.ibm.com>
-+ *
++ * Tasklet
 + */
++static struct tasklet_struct appldata_tasklet_struct;
 +
-+#include <linux/s390net.h>
-+
-+#ifndef ASM_QETH_H
-+#define ASM_QETH_H
-+
-+#define QETH_IOCPROC_REGISTER		_IOW(S390NET_IOC_MAGIC, 1, int)
-+
-+#define SIOC_QETH_ARP_SET_NO_ENTRIES	_IOWR(S390NET_IOC_MAGIC, 2, int)
-+#define SIOC_QETH_ARP_QUERY_INFO	_IOWR(S390NET_IOC_MAGIC, 3, int)
-+#define SIOC_QETH_ARP_ADD_ENTRY		_IOWR(S390NET_IOC_MAGIC, 4, int)
-+#define SIOC_QETH_ARP_REMOVE_ENTRY	_IOWR(S390NET_IOC_MAGIC, 5, int)
-+#define SIOC_QETH_ARP_FLUSH_CACHE	_IOWR(S390NET_IOC_MAGIC, 6, int)
-+#define SIOC_QETH_ADP_SET_SNMP_CONTROL	_IOWR(S390NET_IOC_MAGIC, 7, int)
-+#define SIOC_QETH_GET_CARD_TYPE		_IOWR(S390NET_IOC_MAGIC, 8, int)
-+
-+#endif /* !ASM_QETH_H */
-+
-=== include/asm-s390/pgtable.h
-==================================================================
---- include/asm-s390/pgtable.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/pgtable.h   (/trunk/2.4.27)   (revision 52)
-@@ -426,9 +426,12 @@
- 	__pte;                                                            \
- })
- 
--#define arch_set_page_uptodate(__page)					  \
-+#define SetPageUptodate(_page) \
- 	do {								  \
--		asm volatile ("sske %0,%1" : : "d" (get_storage_key()),	  \
-+		struct page *__page = (_page);				  \
-+		if (!test_and_set_bit(PG_uptodate, &__page->flags))	  \
-+			asm volatile ("sske %0,%1" 			  \
-+			      : : "d" (get_storage_key()),		  \
- 			      "a" (__pa((__page-mem_map) << PAGE_SHIFT)));\
- 	} while (0)
- 
-=== include/asm-s390/qdio.h
-==================================================================
---- include/asm-s390/qdio.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/qdio.h   (/trunk/2.4.27)   (revision 52)
-@@ -11,7 +11,7 @@
- #ifndef __QDIO_H__
- #define __QDIO_H__
- 
--#define VERSION_QDIO_H "$Revision: 1.66 $"
-+#define VERSION_QDIO_H "$Revision: 1.66.4.5 $"
- 
- /* note, that most of the typedef's are from ingo. */
- 
-@@ -42,11 +42,18 @@
- #define QDIO_MAX_ELEMENTS_PER_BUFFER 16
- #define SBAL_SIZE 256
- 
--#define IQDIO_FILL_LEVEL_TO_POLL (QDIO_MAX_BUFFERS_PER_Q*4/3)
-+/* unfortunately this can't be (QDIO_MAX_BUFFERS_PER_Q*4/3) or so -- as
-+ * we never know, whether we'll get initiative again, e.g. to give the
-+ * transmit skb's back to the stack, however the stack may be waiting for
-+ * them... therefore we define 4 as threshold to start polling (which
-+ * will stop as soon as the asynchronous queue catches up)
-+ * btw, this only applies to the asynchronous HiperSockets queue */
-+#define IQDIO_FILL_LEVEL_TO_POLL 4
- 
- #define TIQDIO_THININT_ISC 3
- #define TIQDIO_DELAY_TARGET 0
--#define QDIO_BUSY_BIT_PATIENCE 2000 /* in microsecs */
-+#define QDIO_BUSY_BIT_PATIENCE 100 /* in microsecs */
-+#define QDIO_BUSY_BIT_GIVE_UP 10000000 /* 10 seconds */
- #define IQDIO_GLOBAL_LAPS 2 /* GLOBAL_LAPS are not used as we */
- #define IQDIO_GLOBAL_LAPS_INT 1 /* dont global summary */
- #define IQDIO_LOCAL_LAPS 4
-@@ -612,6 +619,8 @@
- typedef struct qdio_q_t {
- 	volatile slsb_t slsb;
- 
-+	char unused[QDIO_MAX_BUFFERS_PER_Q];
++/*
++ * Hook list
++ */
++static spinlock_t appldata_ops_lock = SPIN_LOCK_UNLOCKED;
++static LIST_HEAD(appldata_ops_list);
 +
- 	__u32 * volatile dev_st_chg_ind;
- 
- 	int is_input_q;
-@@ -697,7 +706,9 @@
- 		int last_transfer_index; */
- 
- 		__u64 last_transfer_time;
-+		__u64 busy_start;
- 	} timing;
-+	atomic_t busy_siga_counter;
-         unsigned int queue_type;
- } __attribute__ ((aligned(256))) qdio_q_t;
- 
-@@ -713,7 +724,7 @@
- 	unsigned int sync_done_on_outb_pcis;
- 
- 	unsigned int state;
--	spinlock_t setting_up_lock;
-+	struct semaphore setting_up_lock;
- 
- 	unsigned int no_input_qs;
- 	unsigned int no_output_qs;
-@@ -783,7 +794,10 @@
- 				int reserved1:21;
- 				int isc:3;
- 				/* word 9&10 */
--				__u32 reserved2[2];
-+                                __u32 word_with_d_bit;
-+                                /* set to 0x10000000 to enable
-+                                 * time delay disablement facility */
-+                                __u32 reserved2;
- 				/* word 11 */
- 				__u32 subsystem_id;
- 				/* word 12-1015 */
-=== include/asm-s390/setup.h
-==================================================================
---- include/asm-s390/setup.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/setup.h   (/trunk/2.4.27)   (revision 52)
-@@ -25,15 +25,16 @@
-  */
- extern unsigned long machine_flags;
- 
--#define MACHINE_IS_VM    (machine_flags & 1)
--#define MACHINE_HAS_IEEE (machine_flags & 2)
--#define MACHINE_IS_P390  (machine_flags & 4)
--#define MACHINE_HAS_CSP  (machine_flags & 8)
--#define MACHINE_HAS_MVPG (machine_flags & 16)
-+#define MACHINE_IS_VM		(machine_flags & 1)
-+#define MACHINE_HAS_IEEE	(machine_flags & 2)
-+#define MACHINE_IS_P390		(machine_flags & 4)
-+#define MACHINE_HAS_CSP		(machine_flags & 8)
-+#define MACHINE_HAS_MVPG	(machine_flags & 16)
-+/* 32 is MACHINE_HAS_DIAG44 on s390x */
- #define MACHINE_NEW_STIDP	(machine_flags & 64)
--#define MACHINE_HAS_PFIX (machine_flags & 128)
-+#define MACHINE_HAS_PFIX  	(machine_flags & 128)
- 
--#define MACHINE_HAS_HWC  (!MACHINE_IS_P390)
-+#define MACHINE_HAS_SCLP	(!MACHINE_IS_P390)
- 
- /*
-  * Console mode. Override with conmode=
-@@ -42,10 +43,10 @@
- extern unsigned int console_device;
- 
- #define CONSOLE_IS_UNDEFINED	(console_mode == 0)
--#define CONSOLE_IS_HWC		(console_mode == 1)
-+#define CONSOLE_IS_SCLP		(console_mode == 1)
- #define CONSOLE_IS_3215		(console_mode == 2)
- #define CONSOLE_IS_3270		(console_mode == 3)
--#define SET_CONSOLE_HWC		do { console_mode = 1; } while (0)
-+#define SET_CONSOLE_SCLP	do { console_mode = 1; } while (0)
- #define SET_CONSOLE_3215	do { console_mode = 2; } while (0)
- #define SET_CONSOLE_3270	do { console_mode = 3; } while (0)
- 
-=== include/asm-s390/processor.h
-==================================================================
---- include/asm-s390/processor.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/processor.h   (/trunk/2.4.27)   (revision 52)
-@@ -50,6 +50,8 @@
- 
- extern void print_cpu_info(struct cpuinfo_S390 *);
- 
-+extern void show_trace(unsigned long* esp);
 +
- /* Lazy FPU handling on uni-processor */
- extern struct task_struct *last_task_used_math;
- 
-=== include/asm-s390/ioctl32.h
-==================================================================
---- include/asm-s390/ioctl32.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/ioctl32.h   (/trunk/2.4.27)   (revision 52)
-@@ -8,7 +8,7 @@
- #ifndef ASM_IOCTL32_H
- #define ASM_IOCTL32_H
- 
--extern int sys_ioctl(unsigned int, unsigned int, unsigned long, struct file*);
-+extern int sys_ioctl(unsigned int, unsigned int, unsigned long);
- typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
- 
- #ifdef CONFIG_S390_SUPPORT
-=== include/asm-s390/dcss.h
-==================================================================
---- include/asm-s390/dcss.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/dcss.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,19 @@
++/************************* timer, tasklet, DIAG ******************************/
 +/*
-+ *  include/asm-s390/dcss.h
++ * appldata_timer_function()
 + *
-+ *  definitions for discontiguous saved segment support
-+ *  Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ * 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);
++	}
++}
 +
-+#ifndef _ASM_S390_DCSS_H
-+#define _ASM_S390_DCSS_H
-+#ifndef __ASSEMBLY__
-+#define SEGMENT_SHARED_RW       0
-+#define SEGMENT_SHARED_RO       1
-+#define SEGMENT_EXCLUSIVE_RW    2
-+#define SEGMENT_EXCLUSIVE_RO    3
-+extern int segment_load (char *name,int segtype,unsigned long *addr,unsigned long *length);
-+extern void segment_unload(char *name);
-+extern void segment_replace(char *name);
-+#endif
-+#endif
-=== include/asm-s390/cmb.h
-==================================================================
---- include/asm-s390/cmb.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/cmb.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,194 @@
 +/*
-+ * include/asm-s390/cmb.h
-+ * include/asm-s390x/cmb.h
++ * appldata_tasklet_function()
 + *
-+ *         Copyright (C) 2003 IBM Corporation
-+ *         Author: Arnd Bergmann <arndb at de.ibm.com>
++ * 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;
 +
-+#ifndef S390_CMB_H
-+#define S390_CMB_H
-+/**
-+ * struct cmbdata -- channel measurement block data for user space
-+ *
-+ * @size:			size of the stored data
-+ * @elapsed_time:		time since last reset
-+ * @ssch_rsch_count:		number I/O attempts, may overflow at
-+ * 				2^16 or 2^32, depending on machine
-+ * @sample_count:		number of sampled I/O attempts, should
-+ * 				be identical to @ssch_rsch_count
-+ * @device_connect_time:	time that device communicated with
-+ * 				channel
-+ * @function_pending_time:	time between command initiate and
-+ * 				start of execution
-+ * @device_disconnect_time:	time of device-inactive while
-+ * 				subchannel-active
-+ * @control_unit_queuing_time:	time that a device is blocked by
-+ * 				I/O from another system
-+ * @device_active_only_time:	time of device-active while
-+ * 				subchannel-inactive
-+ * @device_busy_time:		time where the device is busy
-+ * 				when attemting to start I/O
-+ * @initial_command_response_time: time between sending and 
-+ * 				the device accepting a command
-+ *
-+ * all values are stored as 64 bit for simplicity, especially
-+ * in 32 bit emulation mode. All time values are normalized to
-+ * nanoseconds.
-+ * Currently, two formats are known, which differ by the size of
-+ * this structure, i.e. the last two members are only set when
-+ * the extended channel measurement facility (first shipped in
-+ * z990 machines) is activated.
-+ * Potentially, more fields could be added, which results in a
-+ * new ioctl number.
-+ **/
-+struct cmbdata {
-+	__u64 size;
-+	__u64 elapsed_time;
-+ /* basic and exended format: */
-+	__u64 ssch_rsch_count;
-+	__u64 sample_count;
-+	__u64 device_connect_time;
-+	__u64 function_pending_time;
-+	__u64 device_disconnect_time;
-+	__u64 control_unit_queuing_time;
-+	__u64 device_active_only_time;
-+ /* extended format only: */
-+	__u64 device_busy_time;
-+	__u64 initial_command_response_time;
-+};
-+
-+/* enable channel measurement */
-+#define BIODASDCMFENABLE	_IO(DASD_IOCTL_LETTER,32)
-+/* enable channel measurement */
-+#define BIODASDCMFDISABLE	_IO(DASD_IOCTL_LETTER,33)
-+/* read channel measurement data */
-+#define BIODASDREADALLCMB	_IOWR(DASD_IOCTL_LETTER,33,struct cmbdata)
-+
-+#ifdef __KERNEL__
-+#include <linux/config.h>
-+#include <asm/irq.h>
-+
-+#if defined(CONFIG_S390_CMF) || defined(CONFIG_S390_CMF_MODULE)
-+/**
-+ * struct cmf_device - basic building block of the CMF code
-+ *
-+ * @cmb_list:	entry in the global list of cmf_devices
-+ * @ccwlock:	a pointer to the subchannel's spinlock
-+ * @cmb_start_time: clock value of previous cmf_reset operation
-+ * @cmb:	pointer to the hardware channel measurement block
-+ * @callback:	function pointer called by cmf_device_callback()
-+ * @irq:	subchannel number of the associated device
-+ */
-+struct cmf_device {
-+	struct list_head cmb_list;
-+	spinlock_t *ccwlock;
-+	u64 cmb_start_time;
-+	void *cmb;
-+	void (*callback)(struct cmf_device *);
-+	u16  irq;
-+};
++	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);
++}
 +
-+/**
-+ * cmf_device_callback - call the callback function for set_cmf
++/*
++ * appldata_mod_vtimer_wrap()
 + *
-+ * cmf_device_callback() can be integrated in an appropriate
-+ * point in the device driver where no I/O is ongoing on the
-+ * subchannel and it is safe to call set_cmf.
-+ * This is a nop when CONFIG_S390_CMF is disabled
++ * wrapper function for mod_virt_timer(), because smp_call_function_on()
++ * accepts only one parameter.
 + */
-+static inline void cmf_device_callback(struct cmf_device *cdev)
-+{
-+	if (cdev->callback) {
-+		cdev->callback(cdev);
-+	}
++static void appldata_mod_vtimer_wrap(struct appldata_mod_vtimer_args *args) {
++	mod_virt_timer(args->timer, args->expires);
 +}
 +
-+/**
-+ * cmf_device_init - initialize the cmf_device structure
++/*
++ * appldata_diag()
 + *
-+ * cmf_device_init() needs to be called before doing any other operations
-+ * on a cmf_device.
++ * prepare parameter list, issue DIAG 0xDC
 + */
-+static inline void cmf_device_init(struct cmf_device *cdev, int devno)
++static int appldata_diag(char record_nr, u16 function, unsigned long buffer,
++			u16 length)
 +{
-+	INIT_LIST_HEAD (&cdev->cmb_list);
-+	cdev->irq = get_irq_by_devno(devno);
-+	cdev->ccwlock = get_irq_lock(cdev->irq);
-+}
++	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)
++	};
 +
-+#else /* !CONFIG_S390_CMF */
-+struct cmf_device { };
-+static inline void cmf_device_callback(struct cmf_device *cdev)        { }
-+static inline void cmf_device_init(struct cmf_device *cdev, int devno) { }
-+#endif
++        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> ***************************/
 +
-+/**
-+ * enable_cmf() - switch on the channel measurement for a specific device
-+ *  @cdev:	The ccw device to be enabled
-+ *  returns 0 for success or a negative error value.
-+ *
-+ *  This function only allocates memory for the measurement block, the
-+ *  actual activation is done with set_cmf()
-+ *
-+ *  Context:
-+ *    may sleep, device is disabled
-+ **/
-+extern int enable_cmf(struct cmf_device *cdev);
 +
-+/**
-+ * disable_cmf() - switch off the channel measurement for a specific device
-+ *  @cdev:	The ccw device to be disabled
-+ *  returns 0 for success or a negative error value.
-+ *
-+ *  This function only frees the memory allocated with enable_cmf. If
-+ *  measurement has been activated with set_cmf(), it also needs to be 
-+ *  deactivated with that function before calling disable_cmf(). 
-+ *
-+ *  Context:
-+ *    may sleep, device is enabled and inactive
-+ **/
-+extern void disable_cmf(struct cmf_device *cdev);
++/****************************** /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];
 +
-+/**
-+ * cmf_readall() - read one value from the current channel measurement block
-+ * @cmf:	the device to be read
-+ * @data:	a pointer to a data block that will be filled
-+ *
-+ *  Context:
-+ *    device is active
-+ **/
-+extern int cmf_readall(struct cmf_device *cdev, struct cmbdata*data);
++	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;
 +
-+/**
-+ * set_cmf() - Set the measurement mode for a specific device
-+ * @cmf:	the device to be modified
-+ * @mme:	measurement mode enable value, can be either
-+ *		0 for deactivation or 2 for activation
-+ *
-+ * It is important to call this function only when there is no I/O
-+ * activity on the subchannel. Therefore it may be necessary to call
-+ * it from an interrupt handler at the point where the previous
-+ * request is finished.
-+ * In 2.6.x, set_cmf() is integrated within enable_cmf() and disable_cmf(),
-+ * which makes a lot of sense and life much easier for users.
-+ *
-+ *  Context:
-+ *    device lock held, device is enabled, subchannel is idle
-+ **/
-+extern int set_cmf(struct cmf_device *cdev, u32 mme);
++	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;
++}
 +
-+/**
-+ * reset_cmf() - clear a channel measurement block
-+ * @cmf:	the device to be cleared
-+ *
-+ * This function is used to set all values in a channel measurement block
-+ * to sane values. It should be called between enable_cmf() and set_cmf(cdev,2)
-+ * but can be called as well when the device is already active.
-+ *
-+ *  Context:
-+ *    device is enabled
-+ **/
-+extern void cmf_reset(struct cmf_device *cdev);
-+#endif /* __KERNEL__ */
-+#endif /* S390_CMB_H */
-=== include/asm-s390/kmap_types.h
-==================================================================
---- include/asm-s390/kmap_types.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/kmap_types.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,16 @@
-+#ifndef _ASM_KMAP_TYPES_H
-+#define _ASM_KMAP_TYPES_H
++/*
++ * 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;
++	}
 +
-+enum km_type {
-+	KM_BOUNCE_READ,
-+	KM_SKB_SUNRPC_DATA,
-+	KM_SKB_DATA_SOFTIRQ,
-+	KM_USER0,
-+	KM_USER1,
-+	KM_BH_IRQ,
-+	KM_SOFTIRQ0,
-+	KM_SOFTIRQ1,
-+	KM_TYPE_NR
-+};
++	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;
++}
 +
-+#endif
-=== include/asm-s390/timer.h
-==================================================================
---- include/asm-s390/timer.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/timer.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,49 @@
 +/*
-+ *  include/asm-s390/timer.h
-+ *
-+ *  (C) Copyright IBM Corp. 2003
-+ *  Virtual CPU timer
-+ *
-+ *  Author: Jan Glauber (jang at de.ibm.com)
++ * 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];
 +
-+#ifndef _ASM_S390_TIMER_H
-+#define _ASM_S390_TIMER_H
++	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;
++	}
 +
-+#include <linux/timer.h>
++	len = *lenp;
++	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
++		return -EFAULT;
 +
-+#define VTIMER_MAX_SLICE (0x7ffffffffffff000LL)
++	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> *******************************/
 +
-+struct vtimer_list {
-+	struct list_head entry;
 +
-+	int cpu;
-+	__u64 expires;
-+	__u64 interval;
++/************************* 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;
 +
-+	spinlock_t lock;
-+	unsigned long magic;
++	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));
 +
-+	void (*function)(unsigned long, struct pt_regs*);
-+	unsigned long data;
-+};
++	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);
 +
-+/* the offset value will wrap after ca. 71 years */
-+struct vtimer_queue {
-+	struct list_head list;
-+	spinlock_t lock;
-+	__u64 to_expire;	  /* current event expire time */
-+	__u64 offset;		  /* list offset to zero */
-+	__u64 idle;		  /* temp var for idle */
-+};
++	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];
 +
-+void set_vtimer(__u64 expires);
++	ops->ctl_table[1].ctl_name = 0;
 +
-+extern void init_virt_timer(struct vtimer_list *timer);
-+extern void add_virt_timer(void *new);
-+extern void add_virt_timer_periodic(void *new);
-+extern int mod_virt_timer(struct vtimer_list *timer, __u64 expires);
-+extern int del_virt_timer(struct vtimer_list *timer);
++	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;
 +
-+extern atomic_t active_cpu_timer;
-+#endif
-=== include/asm-s390/tape390.h
-==================================================================
---- include/asm-s390/tape390.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ include/asm-s390/tape390.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,39 @@
-+/*************************************************************************
-+ *
-+ * tape390.h
-+ *         enables user programs to display messages on the tape device
-+ *
-+ *  S390 and zSeries version
-+ *         Copyright (C) 2001 IBM Corporation
-+ *         Author(s): Despina Papadopoulou <despina_p at de.ibm.com>
++	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;
 +
-+#ifndef _TAPE390_H
-+#define _TAPE390_H
++	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);
++		}
 +
-+#define TAPE390_DISPLAY _IOW('d', 1, struct display_struct)
++	}
++	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 *********************************/
 +/*
-+ * The TAPE390_DISPLAY ioctl calls the Load Display command
-+ * which transfers 17 bytes of data from the channel to the subsystem:
-+ *     - 1 format control byte, and
-+ *     - two 8-byte messages
-+ *
-+ * Format control byte:
-+ *   0-2: New Message Overlay
-+ *     3: Alternate Messages
-+ *     4: Blink Message
-+ *     5: Display Low/High Message
-+ *     6: Reserved
-+ *     7: Automatic Load Request
++ * 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));
 +
-+typedef struct display_struct {
-+        char cntrl;
-+        char message1[8];
-+        char message2[8];
-+} display_struct;
++	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);
 +
-+#endif 
-=== net/netsyms.c
-==================================================================
---- net/netsyms.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ net/netsyms.c   (/trunk/2.4.27)   (revision 52)
-@@ -289,7 +289,10 @@
- EXPORT_SYMBOL(devinet_ioctl);
- EXPORT_SYMBOL(register_inetaddr_notifier);
- EXPORT_SYMBOL(unregister_inetaddr_notifier);
--
-+#ifdef CONFIG_IP_MULTICAST
-+EXPORT_SYMBOL(register_multicast_notifier);
-+EXPORT_SYMBOL(unregister_multicast_notifier);
++	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
- /* needed for ip_gre -cw */
- EXPORT_SYMBOL(ip_statistics);
- 
-=== net/core/dev.c
-==================================================================
---- net/core/dev.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ net/core/dev.c   (/trunk/2.4.27)   (revision 52)
-@@ -104,6 +104,7 @@
- #include <linux/wireless.h>		/* Note : will define WIRELESS_EXT */
- #include <net/iw_handler.h>
- #endif	/* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
-+#include <linux/s390net.h>
- #ifdef CONFIG_PLIP
- extern int plip_init(void);
- #endif
-@@ -2194,6 +2195,7 @@
- 		default:
- 			if ((cmd >= SIOCDEVPRIVATE &&
- 			    cmd <= SIOCDEVPRIVATE + 15) ||
-+			    _IOC_TYPE(cmd) == S390NET_IOC_MAGIC ||
- 			    cmd == SIOCBONDENSLAVE ||
- 			    cmd == SIOCBONDRELEASE ||
- 			    cmd == SIOCBONDSETHWADDR ||
-@@ -2383,6 +2385,7 @@
- 		 
- 		default:
- 			if (cmd == SIOCWANDEV ||
-+			    _IOC_TYPE(cmd) == S390NET_IOC_MAGIC ||
- 			    (cmd >= SIOCDEVPRIVATE &&
- 			     cmd <= SIOCDEVPRIVATE + 15)) {
- 				dev_load(ifr.ifr_name);
-=== net/802/tr.c
-==================================================================
---- net/802/tr.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ net/802/tr.c   (/trunk/2.4.27)   (revision 52)
-@@ -327,9 +327,9 @@
- 	int i;
- 	unsigned int hash, rii_p = 0;
- 	rif_cache entry;
--	unsigned long flags;
- 
--	spin_lock_irqsave(&rif_lock, flags);
-+
-+	spin_lock_bh(&rif_lock);
- 	
- 	/*
- 	 *	Firstly see if the entry exists
-@@ -368,7 +368,7 @@
- 		if(!entry) 
- 		{
- 			printk(KERN_DEBUG "tr.c: Couldn't malloc rif cache entry !\n");
--			spin_unlock_irqrestore(&rif_lock,flags);
-+			spin_unlock_bh(&rif_lock);
- 			return;
- 		}
- 
-@@ -410,7 +410,7 @@
- 		    }                                         
-            	entry->last_used=jiffies;               
- 	}
--	spin_unlock_irqrestore(&rif_lock,flags);
-+	spin_unlock_bh(&rif_lock);
- }
- 
- /*
-=== net/8021q/vlan.c
-==================================================================
---- net/8021q/vlan.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ net/8021q/vlan.c   (/trunk/2.4.27)   (revision 52)
-@@ -444,6 +444,10 @@
- 	/* IFF_BROADCAST|IFF_MULTICAST; ??? */
- 	new_dev->flags = real_dev->flags;
- 	new_dev->flags &= ~IFF_UP;
-+#ifdef CONFIG_SHARED_IPV6_CARDS
-+	new_dev->features |= (real_dev->features & NETIF_F_SHARED_IPV6);
-+	new_dev->dev_id = real_dev->dev_id;
-+#endif
- 
- 	/* Make this thing known as a VLAN device */
- 	new_dev->priv_flags |= IFF_802_1Q_VLAN;
-@@ -482,16 +486,16 @@
- 	new_dev->stop = vlan_dev_stop;
- 
- 	if (real_dev->features & NETIF_F_HW_VLAN_TX) {
--		new_dev->hard_header = real_dev->hard_header;
-+		new_dev->hard_header     = real_dev->hard_header;
- 		new_dev->hard_start_xmit = vlan_dev_hwaccel_hard_start_xmit;
--		new_dev->rebuild_header = real_dev->rebuild_header;
-+		new_dev->rebuild_header  = real_dev->rebuild_header;
- 	} else {
--		new_dev->hard_header = vlan_dev_hard_header;
-+		new_dev->hard_header     = vlan_dev_hard_header;
- 		new_dev->hard_start_xmit = vlan_dev_hard_start_xmit;
--		new_dev->rebuild_header = vlan_dev_rebuild_header;
-+		new_dev->rebuild_header  = vlan_dev_rebuild_header;
- 	}
--	new_dev->hard_header_parse = real_dev->hard_header_parse;
--	new_dev->set_mac_address = vlan_dev_set_mac_address;
-+	new_dev->hard_header_parse  = real_dev->hard_header_parse;
-+	new_dev->set_mac_address    = vlan_dev_set_mac_address;
- 	new_dev->set_multicast_list = vlan_dev_set_multicast_list;
- 
- 	VLAN_DEV_INFO(new_dev)->vlan_id = VLAN_ID; /* 1 through VLAN_VID_MASK */
-=== net/ipv4/igmp.c
-==================================================================
---- net/ipv4/igmp.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ net/ipv4/igmp.c   (/trunk/2.4.27)   (revision 52)
-@@ -152,6 +152,18 @@
- 
- #ifdef CONFIG_IP_MULTICAST
- 
-+static struct notifier_block *multicast_chain;
++	
++	tasklet_init(&appldata_tasklet_struct, appldata_tasklet_function, 0);
++	P_DEBUG("Base interface initialized.\n");
++	return 0;
++}
 +
-+int register_multicast_notifier(struct notifier_block *nb)
++/*
++ * appldata_exit()
++ * 
++ * stop timer and tasklet, unregister /proc entries
++ */
++static void __exit appldata_exit(void)
 +{
-+	return notifier_chain_register(&multicast_chain, nb);
++	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> ******************************/
 +
-+int unregister_multicast_notifier(struct notifier_block *nb)
++
++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)
 +{
-+	return notifier_chain_unregister(&multicast_chain,nb);
++	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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/appldata/appldata_mem.c	2006-01-30 22:25:24.000000000 -0700
+@@ -0,0 +1,165 @@
++/*
++ * arch/s390/appldata/appldata_mem.c
++ *
++ * 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>
++ */
 +
- /*
-  *	Timer management
-  */
-@@ -1164,6 +1176,7 @@
- 	igmp_group_added(im);
- 	if (!in_dev->dead)
- 		ip_rt_multicast_event(in_dev);
-+	notifier_call_chain(&multicast_chain, NETDEV_REGISTER, im);
- out:
- 	return;
- }
-@@ -1189,6 +1202,9 @@
- 				if (!in_dev->dead)
- 					ip_rt_multicast_event(in_dev);
- 
-+				notifier_call_chain(&multicast_chain,
-+						    NETDEV_UNREGISTER,
-+						    i);
- 				ip_ma_put(i);
- 				return;
- 			}
-=== net/ipv6/ipv6_syms.c
-==================================================================
---- net/ipv6/ipv6_syms.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ net/ipv6/ipv6_syms.c   (/trunk/2.4.27)   (revision 52)
-@@ -14,6 +14,8 @@
- EXPORT_SYMBOL(ndisc_mc_map);
- EXPORT_SYMBOL(register_inet6addr_notifier);
- EXPORT_SYMBOL(unregister_inet6addr_notifier);
-+EXPORT_SYMBOL(register_multicast6_notifier);
-+EXPORT_SYMBOL(unregister_multicast6_notifier);
- EXPORT_SYMBOL(ip6_route_output);
- #ifdef CONFIG_NETFILTER
- EXPORT_SYMBOL(ip6_route_me_harder);
-=== net/ipv6/mcast.c
-==================================================================
---- net/ipv6/mcast.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ net/ipv6/mcast.c   (/trunk/2.4.27)   (revision 52)
-@@ -127,6 +127,18 @@
- 
- static struct socket *igmp6_socket;
- 
-+static struct notifier_block *multicast_chain;
++#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 <asm/io.h>
 +
-+int register_multicast6_notifier(struct notifier_block *nb)
++#include "appldata.h"
++
++
++#define MY_PRINT_NAME "appldata_mem"		/* for debug messages, etc. */
++#define P2K(x) ((x) << (PAGE_SHIFT - 10))	/* Converts #Pages to KB */
++
++/*
++ * 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 */
++
++	u64 pgpgin;		/* pages read from disk  */
++	u64 pgpgout;		/* pages written to disk */
++	u64 pswpin;		/* pages swapped in  */
++	u64 pswpout;		/* pages swapped out */
++
++	u64 sharedram;		/* sharedram is currently set to 0 */
++
++	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;
++
++
++static inline void appldata_debug_print(struct appldata_mem_data *mem_data)
 +{
-+	return notifier_chain_register(&multicast_chain, nb);
++	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 unregister_multicast6_notifier(struct notifier_block *nb)
++/*
++ * appldata_get_mem_data()
++ *
++ * gather memory data
++ */
++static void appldata_get_mem_data(void *data)
 +{
-+	return notifier_chain_unregister(&multicast_chain,nb);
++	struct sysinfo val;
++	struct appldata_mem_data *mem_data;
++	
++	mem_data = data;
++	mem_data->sync_count_1++;
++
++	mem_data->pgpgin  = kstat.pgpgin >> 1;
++	mem_data->pgpgout = kstat.pgpgout >> 1;
++	mem_data->pswpin  = kstat.pswpin;
++	mem_data->pswpout = kstat.pswpout;
++
++	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);
++
++	si_swapinfo(&val);
++
++	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);
 +}
 +
- static void igmp6_join_group(struct ifmcaddr6 *ma);
- static void igmp6_leave_group(struct ifmcaddr6 *ma);
- static void igmp6_timer_handler(unsigned long data);
-@@ -857,6 +869,7 @@
- 
- 	mld_del_delrec(idev, &mc->mca_addr);
- 	igmp6_group_added(mc);
-+	notifier_call_chain(&multicast_chain, NETDEV_REGISTER, mc);
- 	ma_put(mc);
- 	return 0;
- }
-@@ -877,6 +890,8 @@
- 
- 				igmp6_group_dropped(ma);
- 
-+				notifier_call_chain(&multicast_chain,
-+						    NETDEV_UNREGISTER, ma);
- 				ma_put(ma);
- 				return 0;
- 			}
-=== net/ipv6/addrconf.c
-==================================================================
---- net/ipv6/addrconf.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ net/ipv6/addrconf.c   (/trunk/2.4.27)   (revision 52)
-@@ -825,9 +825,20 @@
- 			return -1;
- 		memcpy(eui, dev->dev_addr, 3);
- 		memcpy(eui + 5, dev->dev_addr+3, 3);
-+#ifdef CONFIG_SHARED_IPV6_CARDS
-+		if (dev->features&NETIF_F_SHARED_IPV6) {
-+			eui[3] = (dev->dev_id>>8)&0xff;
-+			eui[4] = dev->dev_id&0xff;
-+		} else {
-+			eui[3] = 0xFF;
-+			eui[4] = 0xFE;
-+			eui[0] ^= 2;
-+		}
-+#else /* CONFIG_SHARED_IPV6_CARDS */
- 		eui[3] = 0xFF;
- 		eui[4] = 0xFE;
- 		eui[0] ^= 2;
-+#endif /* CONFIG_SHARED_IPV6_CARDS */
- 		return 0;
- 	case ARPHRD_ARCNET:
- 		/* XXX: inherit EUI-64 fro mother interface -- yoshfuji */
-=== net/Config.in
-==================================================================
---- net/Config.in   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ net/Config.in   (/trunk/2.4.27)   (revision 52)
-@@ -25,6 +25,7 @@
-       if [ "$CONFIG_IPV6" != "n" ]; then
- 	 source net/ipv6/Config.in
-       fi
-+      bool '  Prepare net_device struct for shared IPv6 cards' CONFIG_SHARED_IPV6_CARDS
-    fi
-    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-       source net/khttpd/Config.in
-=== init/kerntypes.c
-==================================================================
---- init/kerntypes.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ init/kerntypes.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,49 @@
++
++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,
++};
++
++
 +/*
-+ * kerntypes.c
-+ *
-+ * Dummy module that includes headers for all kernel types of interest.
-+ * The kernel type information is used by the lcrash utility when
-+ * analyzing system crash dumps or the live system. Using the type
-+ * information for the running system, rather than kernel header files,
-+ * makes for a more flexible and robust analysis tool.
++ * appldata_mem_init()
 + *
-+ * This source code is released under the GNU GPL.
++ * init_data, register ops
 + */
++static int __init appldata_mem_init(void)
++{
++	int rc;
 +
-+#ifndef __KERNEL__
-+#define __KERNEL__
-+#endif
++	P_DEBUG("sizeof(mem) = %lu\n", sizeof(struct appldata_mem_data));
 +
-+/* General linux types */
++	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_mem_exit()
++ * 
++ * unregister ops
++ */
++static void __exit appldata_mem_exit(void)
++{
++	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-2.4.27.orig/arch/s390/appldata/appldata_net_sum.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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>
++ */
 +
-+#include <linux/autoconf.h>
-+#include <linux/mm.h>
 +#include <linux/config.h>
-+#include <linux/utsname.h>
 +#include <linux/module.h>
-+#include <linux/sched.h>
-+#include <linux/compile.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/errno.h> 
++#include <linux/kernel_stat.h>
++#include <linux/netdevice.h>
 +
-+#ifdef CONFIG_ARCH_S390
++#include "appldata.h"
 +
-+       /* s390 specific types */
 +
-+       #include <asm/lowcore.h>
-+       #include <asm/debug.h>
-+       #include <asm/irq.h>
-+       #include <asm/io.h>
++#define MY_PRINT_NAME	"appldata_net_sum"	/* for debug messages, etc. */
 +
-+       #if defined (CONFIG_DASD) || defined (CONFIG_DASD_MODULE)
-+	       #include "../drivers/s390/block/dasd_int.h"
-+       #endif /* CONFIG_DASD */
++/*
++ * 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 */
 +
-+       #if defined (CONFIG_QDIO) || defined (CONFIG_QDIO_MODULE)
-+	       #include "asm/qdio.h"
-+       #endif /* CONFIG_QDIO */
++	u32 nr_interfaces;	/* nr. of network interfaces being monitored */
 +
-+#endif /* CONFIG_ARCH_S390 */
++	u32 padding;		/* next value is 64-bit aligned, so these */
++				/* 4 byte would be padded out by compiler */
 +
-+void
-+kerntypes_dummy(void)
++	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));
 +}
-=== init/version.c
-==================================================================
---- init/version.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ init/version.c   (/trunk/2.4.27)   (revision 52)
-@@ -10,6 +10,7 @@
- #include <linux/utsname.h>
- #include <linux/version.h>
- #include <linux/compile.h>
-+#include <linux/stringify.h>
- 
- #define version(a) Version_ ## a
- #define version_string(a) version(a)
-@@ -24,3 +25,5 @@
- const char *linux_banner = 
- 	"Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
- 	LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";
 +
-+const char *LINUX_COMPILE_VERSION_ID = __stringify(LINUX_COMPILE_VERSION_ID);
-=== init/do_mounts.c
-==================================================================
---- init/do_mounts.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ init/do_mounts.c   (/trunk/2.4.27)   (revision 52)
-@@ -164,7 +164,28 @@
- 	{ "dasdf", (DASD_MAJOR << MINORBITS) + (5 << 2) },
- 	{ "dasdg", (DASD_MAJOR << MINORBITS) + (6 << 2) },
- 	{ "dasdh", (DASD_MAJOR << MINORBITS) + (7 << 2) },
-+	{ "dasdi", (DASD_MAJOR << MINORBITS) + (8 << 2) },
-+	{ "dasdj", (DASD_MAJOR << MINORBITS) + (9 << 2) },
-+	{ "dasdk", (DASD_MAJOR << MINORBITS) + (10 << 2) },
-+	{ "dasdl", (DASD_MAJOR << MINORBITS) + (11 << 2) },
-+	{ "dasdm", (DASD_MAJOR << MINORBITS) + (12 << 2) },
-+	{ "dasdn", (DASD_MAJOR << MINORBITS) + (13 << 2) },
-+	{ "dasdo", (DASD_MAJOR << MINORBITS) + (14 << 2) },
-+	{ "dasdp", (DASD_MAJOR << MINORBITS) + (15 << 2) },
-+	{ "dasdq", (DASD_MAJOR << MINORBITS) + (16 << 2) },
-+	{ "dasdr", (DASD_MAJOR << MINORBITS) + (17 << 2) },
-+	{ "dasds", (DASD_MAJOR << MINORBITS) + (18 << 2) },
-+	{ "dasdt", (DASD_MAJOR << MINORBITS) + (19 << 2) },
-+	{ "dasdu", (DASD_MAJOR << MINORBITS) + (20 << 2) },
-+	{ "dasdv", (DASD_MAJOR << MINORBITS) + (21 << 2) },
-+	{ "dasdw", (DASD_MAJOR << MINORBITS) + (22 << 2) },
-+	{ "dasdx", (DASD_MAJOR << MINORBITS) + (23 << 2) },
-+	{ "dasdy", (DASD_MAJOR << MINORBITS) + (24 << 2) },
-+	{ "dasdz", (DASD_MAJOR << MINORBITS) + (25 << 2) },
- #endif
-+#ifdef CONFIG_BLK_DEV_XPRAM
-+	{ "xpram", (XPRAM_MAJOR << MINORBITS) },
-+#endif
- 	{ "ida/c0d0p",0x4800 },
- 	{ "ida/c0d1p",0x4810 },
- 	{ "ida/c0d2p",0x4820 },
-=== fs/partitions/ibm.c
-==================================================================
---- fs/partitions/ibm.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/partitions/ibm.c   (/trunk/2.4.27)   (revision 52)
-@@ -9,6 +9,7 @@
-  * 07/10/00 Fixed detection of CMS formatted disks     
-  * 02/13/00 VTOC partition support added
-  * 12/27/01 fixed PL030593 (CMS reserved minidisk not detected on 64 bit)
-+ * 07/24/03 no longer using contents of freed page for CMS label recognition (BZ3611)
-  */
- 
- #include <linux/config.h>
-@@ -134,6 +135,9 @@
- 	EBCASC(type, 4);
- 	EBCASC(name, 6);
- 
-+	if(name[0] != '\0')
-+		register_disk_label(hd, MINOR(to_kdev_t(bdev->bd_dev)), name);
-+
- 	/*
- 	 * Three different types: CMS1, VOL1 and LNX1/unlabeled
- 	 */
-@@ -141,7 +145,7 @@
- 		/*
- 		 * VM style CMS1 labeled disk
- 		 */
--		int *label = (int *) data;
-+		int *label = (int *) vlabel;
- 
- 		if (label[13] != 0) {
- 			printk("CMS1/%8s(MDSK):", name);
-@@ -158,7 +162,8 @@
- 		add_gd_partition(hd, first_part_minor,
- 				 offset*(blocksize >> 9),
- 				 size-offset*(blocksize >> 9));
--	} else if (strncmp(type, "VOL1", 4) == 0) {
-+	} else if ((strncmp(type, "VOL1", 4) == 0) &&
-+		(!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) {
- 		/*
- 		 * New style VOL1 labeled disk
- 		 */
-=== fs/partitions/check.c
-==================================================================
---- fs/partitions/check.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/partitions/check.c   (/trunk/2.4.27)   (revision 52)
-@@ -89,7 +89,7 @@
- #ifdef CONFIG_ARCH_S390
- int (*genhd_dasd_name)(char*,int,int,struct gendisk*) = NULL;
- int (*genhd_dasd_ioctl)(struct inode *inp, struct file *filp,
--			    unsigned int no, unsigned long data);
-+			unsigned int no, unsigned long data);
- EXPORT_SYMBOL(genhd_dasd_name);
- EXPORT_SYMBOL(genhd_dasd_ioctl);
- #endif
-@@ -281,6 +281,7 @@
- 	int devnum = minor >> dev->minor_shift;
- 	devfs_handle_t dir;
- 	unsigned int devfs_flags = DEVFS_FL_DEFAULT;
-+	umode_t devfs_perm  = S_IFBLK | S_IRUSR | S_IWUSR;
- 	char devname[16];
- 
- 	if (dev->part[minor + part].de) return;
-@@ -288,11 +289,14 @@
- 	if (!dir) return;
- 	if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
- 		devfs_flags |= DEVFS_FL_REMOVABLE;
-+	if (is_read_only(MKDEV(dev->major, minor+part))) {
-+	        devfs_perm &= ~(S_IWUSR);
-+	}
- 	sprintf (devname, "part%d", part);
- 	dev->part[minor + part].de =
- 	    devfs_register (dir, devname, devfs_flags,
- 			    dev->major, minor + part,
--			    S_IFBLK | S_IRUSR | S_IWUSR,
-+			    devfs_perm,
- 			    dev->fops, NULL);
- }
- 
-@@ -304,12 +308,16 @@
- 	int devnum = minor >> dev->minor_shift;
- 	devfs_handle_t dir, slave;
- 	unsigned int devfs_flags = DEVFS_FL_DEFAULT;
-+	umode_t devfs_perm  = S_IFBLK | S_IRUSR | S_IWUSR;
- 	char dirname[64], symlink[16];
- 	static devfs_handle_t devfs_handle;
- 
- 	if (dev->part[minor].de) return;
- 	if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
- 		devfs_flags |= DEVFS_FL_REMOVABLE;
-+	if (is_read_only(MKDEV(dev->major, minor))) {
-+	        devfs_perm &= ~(S_IWUSR);
-+	}
- 	if (dev->de_arr) {
- 		dir = dev->de_arr[devnum];
- 		if (!dir)  /*  Aware driver wants to block disc management  */
-@@ -331,7 +339,7 @@
- 			  dirname + pos, &slave, NULL);
- 	dev->part[minor].de =
- 	    devfs_register (dir, "disc", devfs_flags, dev->major, minor,
--			    S_IFBLK | S_IRUSR | S_IWUSR, dev->fops, NULL);
-+			    devfs_perm, dev->fops, NULL);
- 	devfs_auto_unregister (dev->part[minor].de, slave);
- 	if (!dev->de_arr)
- 		devfs_auto_unregister (slave, dir);
-@@ -359,6 +367,10 @@
- 		dev->part[minor].de = NULL;
- 		devfs_dealloc_unique_number (&disc_numspace,
- 					     dev->part[minor].number);
-+		if(dev->label_arr && dev->label_arr[minor >> dev->minor_shift]) {
-+			devfs_unregister(dev->label_arr[minor >> dev->minor_shift]);
-+			dev->label_arr[minor >> dev->minor_shift] = NULL;
-+		}
- 	}
- #endif  /*  CONFIG_DEVFS_FS  */
- }
-@@ -415,6 +427,93 @@
- 	}
- }
- 
 +/*
-+ * This function creates a link from /dev/labels/<labelname> to the devfs
-+ * device directory. The device driver must allocate memory to the label_arr
-+ * for this to work.
-+ * This enables devices/partition tables that support labels to be accessed
-+ * by that name instead of the device name (which can change if devices are
-+ * moved around).
++ * appldata_get_net_sum_data()
 + *
-+ * Current restictions:
-+ *   - Only the first device that uses a certain label creates the link
-+ *     (which can also be good in case there is a backup device)
-+ *   - When removing devices that created labels previously suppressed
-+ *     devices won't show up.
++ * gather accumulated network statistics
 + */
-+void register_disk_label(struct gendisk *hd, int minor, char *label) {
-+#ifdef CONFIG_DEVFS_FS
-+	int                   disknum         = minor >> hd->minor_shift;
-+	static devfs_handle_t devfs_label_dir = NULL;
-+	int                   i;
++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;
 +
-+	/*
-+	 * Check the given label. Trailing whitespaces are removed. Otherwise
-+	 * only alphanumeric characters are allowed. (fstab)
-+	 * Added [$#%@] since these are allowed by fdasd and seem
-+	 * to work in fstab.
-+	 */
-+	for(i=0; label[i] != '\0'; i++);
-+	for(i--; i >= 0; i--) {
-+		if(label[i] == ' ' && label[i+1] == '\0') {
-+			label[i] = '\0';
++	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;
 +		}
-+		if(
-+			label[i] == '$' || label[i] == '#' ||
-+			label[i] == '@' || label[i] == '%'
-+		)
-+			continue;
-+		if(label[i] >= 'a' && label[i] <= 'z')
-+			continue;
-+		if(label[i] >= 'A' && label[i] <= 'Z')
-+			continue;
-+		if(label[i] >= '0' && label[i] <= '9')
-+			continue;
-+
-+		printk(KERN_WARNING "\nregister_disk_label: invalid character(s)"
-+			" in label <%s>\n", label);
-+		printk(KERN_WARNING "register_label: refusing to create devfs entry.\n");
-+		return;
++		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;
 +
-+	if(!hd->label_arr)
-+		return;
++	net_data->timestamp = get_clock();
++	net_data->sync_count_2++;
++	appldata_print_debug(net_data);
++}
 +
-+	if(!devfs_label_dir)
-+		if(!(devfs_label_dir = devfs_mk_dir(NULL, "labels", NULL)))
-+			return;
 +
-+	if(hd->label_arr[disknum]) {
-+		if(strcmp(devfs_get_name(hd->label_arr[disknum], NULL), label) == 0)
-+			return;
++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,
++};
 +
-+		devfs_unregister(hd->label_arr[disknum]);
-+		hd->label_arr[disknum] = NULL;
-+	}
-+	if(!devfs_find_handle(devfs_label_dir, label, 0, 0, 0, 0)) {
-+		int  pos      = 0;
-+		char path[64];
 +
-+		if(hd->de_arr) {
-+			if(!hd->de_arr[disknum])
-+				return;
++/*
++ * appldata_net_init()
++ *
++ * init data, register ops
++ */
++static int __init appldata_net_init(void)
++{
++	int rc;
 +
-+			pos = devfs_generate_path(hd->de_arr[disknum], path+3, sizeof(path)-3);
-+			if(pos < 0)
-+				return;
++	P_DEBUG("sizeof(net) = %lu\n", sizeof(struct appldata_net_sum_data));
 +
-+			strncpy(path+pos, "../", 3);
-+		} else {
-+			sprintf(path, "../%s/disc/%d", hd->major_name, disknum);
-+		}
-+		devfs_mk_symlink(
-+			devfs_label_dir, label, DEVFS_FL_DEFAULT, path+pos,
-+			&hd->label_arr[disknum], NULL);
++	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);
 +	}
-+#endif
++	return rc;
 +}
 +
- unsigned char *read_dev_sector(struct block_device *bdev, unsigned long n, Sector *p)
- {
- 	struct address_space *mapping = bdev->bd_inode->i_mapping;
-=== fs/partitions/check.h
-==================================================================
---- fs/partitions/check.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/partitions/check.h   (/trunk/2.4.27)   (revision 52)
-@@ -13,4 +13,6 @@
- 	page_cache_release(p.v);
- }
- 
-+void register_disk_label(struct gendisk *hd, int minor, char *label);
-+
- extern int warn_no_part;
-=== fs/xip2fs/dir.c
-==================================================================
---- fs/xip2fs/dir.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/xip2fs/dir.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,302 @@
 +/*
-+ *  linux/fs/xip2/dir.c, Version 1
-+ *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
++ * appldata_net_exit()
++ * 
++ * unregister ops
 + */
-+
-+#include <linux/fs.h>
-+#include <linux/xip2_fs.h>
-+#include <linux/pagemap.h>
-+
-+typedef struct ext2_dir_entry_2 ext2_dirent;
-+
-+static inline unsigned xip2_chunk_size(struct inode *inode)
++static void __exit appldata_net_exit(void)
 +{
-+	return inode->i_sb->s_blocksize;
++	appldata_unregister_ops(&ops);
++	P_DEBUG("%s-ops unregistered!\n", ops.name);
 +}
 +
-+static inline unsigned long dir_pages(struct inode *inode)
-+{
-+	return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
-+}
 +
-+static int xip2_check_page(struct inode* dir, unsigned long index, void *kaddr)
-+{
-+	struct super_block *sb = dir->i_sb;
-+	unsigned chunk_size = xip2_chunk_size(dir);
-+	u32 max_inumber = le32_to_cpu(sb->u.xip2_sb.s_es->s_inodes_count);
-+	unsigned offs, rec_len;
-+	unsigned limit = PAGE_CACHE_SIZE;
-+	ext2_dirent *p;
-+	char *error;
++module_init(appldata_net_init);
++module_exit(appldata_net_exit);
 +
-+	if ((dir->i_size >> PAGE_CACHE_SHIFT) == index) {
-+		limit = dir->i_size & ~PAGE_CACHE_MASK;
-+		if (limit & (chunk_size - 1))
-+			goto Ebadsize;
-+		for (offs = limit; offs<PAGE_CACHE_SIZE; offs += chunk_size) {
-+			ext2_dirent *p = (ext2_dirent*)(kaddr + offs);
-+			p->rec_len = cpu_to_le16(chunk_size);
-+		}
-+		if (!limit)
-+			goto out;
-+	}
-+	for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) {
-+		p = (ext2_dirent *)(kaddr + offs);
-+		rec_len = le16_to_cpu(p->rec_len);
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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>
++ */
 +
-+		if (rec_len < EXT2_DIR_REC_LEN(1))
-+			goto Eshort;
-+		if (rec_len & 3)
-+			goto Ealign;
-+		if (rec_len < EXT2_DIR_REC_LEN(p->name_len))
-+			goto Enamelen;
-+		if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1))
-+			goto Espan;
-+		if (le32_to_cpu(p->inode) > max_inumber)
-+			goto Einumber;
-+	}
-+	if (offs != limit)
-+		goto Eend;
-+out:
-+	return 0;
++#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>
 +
-+	/* Too bad, we had an error */
++#include "appldata.h"
 +
-+Ebadsize:
-+	xip2_error(sb, "xip2_check_page",
-+		"size of directory #%lu is not a multiple of chunk size",
-+		dir->i_ino
-+	);
-+	goto fail;
-+Eshort:
-+	error = "rec_len is smaller than minimal";
-+	goto bad_entry;
-+Ealign:
-+	error = "unaligned directory entry";
-+	goto bad_entry;
-+Enamelen:
-+	error = "rec_len is too small for name_len";
-+	goto bad_entry;
-+Espan:
-+	error = "directory entry across blocks";
-+	goto bad_entry;
-+Einumber:
-+	error = "inode out of bounds";
-+bad_entry:
-+	xip2_error (sb, "xip2_check_page", "bad entry in directory #%lu: %s - "
-+		"offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
-+		dir->i_ino, error, (index<<PAGE_CACHE_SHIFT)+offs,
-+		(unsigned long) le32_to_cpu(p->inode),
-+		rec_len, p->name_len);
-+	goto fail;
-+Eend:
-+	p = (ext2_dirent *)(kaddr + offs);
-+	xip2_error (sb, "xip2_check_page",
-+		"entry in directory #%lu spans the page boundary"
-+		"offset=%lu, inode=%lu",
-+		dir->i_ino, (index<<PAGE_CACHE_SHIFT)+offs,
-+		(unsigned long) le32_to_cpu(p->inode));
-+fail:
-+	return -EBADFD;
-+}
 +
++#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)
 +
 +/*
-+ * NOTE! unlike strncmp, xip2_match returns 1 for success, 0 for failure.
-+ *
-+ * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller.
++ * OS data
 + */
-+static inline int xip2_match (int len, const char * const name,
-+					struct ext2_dir_entry_2 * de)
-+{
-+	if (len != de->name_len)
-+		return 0;
-+	if (!de->inode)
-+		return 0;
-+	return !memcmp(name, de->name, len);
-+}
++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           */
++};
 +
-+/*
-+ * p is at least 6 bytes before the end of page
-+ */
-+static inline ext2_dirent *xip2_next_entry(ext2_dirent *p)
-+{
-+	return (ext2_dirent *)((char*)p + le16_to_cpu(p->rec_len));
-+}
++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 */
 +
-+static inline unsigned 
-+xip2_validate_entry(char *base, unsigned offset, unsigned mask)
-+{
-+	ext2_dirent *de = (ext2_dirent*)(base + offset);
-+	ext2_dirent *p = (ext2_dirent*)(base + (offset&mask));
-+	while ((char*)p < (char*)de)
-+		p = xip2_next_entry(p);
-+	return (char *)p - base;
-+}
++	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 */
 +
-+static unsigned char xip2_filetype_table[EXT2_FT_MAX] = {
-+	[EXT2_FT_UNKNOWN]	DT_UNKNOWN,
-+	[EXT2_FT_REG_FILE]	DT_REG,
-+	[EXT2_FT_DIR]		DT_DIR,
-+	[EXT2_FT_CHRDEV]	DT_CHR,
-+	[EXT2_FT_BLKDEV]	DT_BLK,
-+	[EXT2_FT_FIFO]		DT_FIFO,
-+	[EXT2_FT_SOCK]		DT_SOCK,
-+	[EXT2_FT_SYMLINK]	DT_LNK,
-+};
++	u32 nr_running;		/* number of runnable threads       */
++	u32 nr_threads;		/* number of threads                */
 +
-+#define S_SHIFT 12
-+static unsigned char xip2_type_by_mode[S_IFMT >> S_SHIFT] = {
-+	[S_IFREG >> S_SHIFT]	EXT2_FT_REG_FILE,
-+	[S_IFDIR >> S_SHIFT]	EXT2_FT_DIR,
-+	[S_IFCHR >> S_SHIFT]	EXT2_FT_CHRDEV,
-+	[S_IFBLK >> S_SHIFT]	EXT2_FT_BLKDEV,
-+	[S_IFIFO >> S_SHIFT]	EXT2_FT_FIFO,
-+	[S_IFSOCK >> S_SHIFT]	EXT2_FT_SOCK,
-+	[S_IFLNK >> S_SHIFT]	EXT2_FT_SYMLINK,
++	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 inline void xip2_set_de_type(ext2_dirent *de, struct inode *inode)
-+{
-+	mode_t mode = inode->i_mode;
-+	if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, 
-+				      EXT2_FEATURE_INCOMPAT_FILETYPE))
-+		de->file_type = xip2_type_by_mode[(mode & S_IFMT)>>S_SHIFT];
-+	else
-+		de->file_type = 0;
-+}
++static struct appldata_os_data *appldata_os_data;
 +
-+static int
-+xip2_readdir (struct file * filp, void * dirent, filldir_t filldir)
++
++static inline void appldata_print_debug(struct appldata_os_data *os_data)
 +{
-+	int err;
-+	loff_t pos = filp->f_pos;
-+	unsigned long blockno;
-+	struct inode *inode = filp->f_dentry->d_inode;
-+	struct super_block *sb = inode->i_sb;
-+	unsigned offset = pos & ~PAGE_CACHE_MASK;
-+	unsigned long n = pos >> PAGE_CACHE_SHIFT;
-+	unsigned long npages = dir_pages(inode);
-+	unsigned chunk_mask = ~(xip2_chunk_size(inode)-1);
-+	unsigned char *types = NULL;
-+	int need_revalidate = (filp->f_version != inode->i_version);
++	int i;
++	unsigned int a0, a1, a2;
 +
-+	if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
-+		goto done;
-+
-+	if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
-+		types = xip2_filetype_table;
-+
-+	for ( ; n < npages; n++, offset = 0) {
-+		char *kaddr, *limit;
-+		ext2_dirent *de;
-+		err=xip2_get_block(inode, n, &blockno, 0);
-+		if (err) goto done;
-+		kaddr = xip2_maread (inode->i_sb->u.xip2_sb.mem_area, 
-+				     blockno, PAGE_CACHE_SIZE);
-+		if (kaddr==NULL) 
-+			BUG();
-+		xip2_check_page(inode, n, kaddr);
-+		if (need_revalidate) {
-+			offset = xip2_validate_entry(kaddr, offset, 
-+						     chunk_mask);
-+			need_revalidate = 0;
-+		}
-+		de = (ext2_dirent *)(kaddr+offset);
-+		limit = kaddr + PAGE_CACHE_SIZE - EXT2_DIR_REC_LEN(1);
-+		for ( ;(char*)de <= limit; de = xip2_next_entry(de))
-+			if (de->inode) {
-+				int over;
-+				unsigned char d_type = DT_UNKNOWN;
-+
-+				if (types && de->file_type < EXT2_FT_MAX)
-+					d_type = types[de->file_type];
++	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));
 +
-+				offset = (char *)de - kaddr;
-+				over = filldir(dirent, de->name, de->name_len,
-+						(n<<PAGE_CACHE_SHIFT) | offset,
-+						le32_to_cpu(de->inode), d_type);
-+				if (over) {
-+					goto done;
-+				}
-+			}
++	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);
 +	}
 +
-+done:
-+	filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset;
-+	filp->f_version = inode->i_version;
-+	UPDATE_ATIME(inode);
-+	return 0;
++	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));
 +}
 +
 +/*
-+ *	xip2_find_entry()
++ * appldata_get_os_data()
 + *
-+ * finds an entry in the specified directory with the wanted name. It
-+ * returns the page in which the entry was found, and the entry itself
-+ * (as a parameter - res_dir). Page is returned mapped and unlocked.
-+ * Entry is guaranteed to be valid.
++ * gather OS data
 + */
-+struct ext2_dir_entry_2 * xip2_find_entry (struct inode * dir,
-+			struct dentry *dentry)
++static void appldata_get_os_data(void *data)
 +{
-+	unsigned long blockno;
-+	const char *name = dentry->d_name.name;
-+	int err;
-+	int namelen = dentry->d_name.len;
-+	unsigned reclen = EXT2_DIR_REC_LEN(namelen);
-+	unsigned long start, n;
-+	unsigned long npages = dir_pages(dir);
-+	ext2_dirent * de;
++	int i;
++	struct appldata_os_data *os_data;
 +
-+	start = dir->u.ext2_i.i_dir_start_lookup;
-+	if (start >= npages)
-+		start = 0;
-+	n = start;
-+	do {
-+		char *kaddr;
-+		err=xip2_get_block(dir, n, &blockno, 0);
-+		if (err) return NULL;
-+		kaddr = xip2_maread (dir->i_sb->u.xip2_sb.mem_area, blockno, 
-+				     PAGE_CACHE_SIZE);
-+		if (kaddr==NULL) 
-+			BUG();
-+		if (!xip2_check_page(dir, n, kaddr)) {
-+			de = (ext2_dirent *) kaddr;
-+			kaddr += PAGE_CACHE_SIZE - reclen;
-+			while ((char *) de <= kaddr) {
-+				if (xip2_match (namelen, name, de))
-+					goto found;
-+				de = xip2_next_entry(de);
-+			}
-+		}
-+		if (++n >= npages)
-+			n = 0;
-+	} while (n != start);
-+	return NULL;
++	os_data = data;
++	os_data->sync_count_1++;
++	os_data->nr_cpus = smp_num_cpus;
 +
-+found:
-+	dir->u.ext2_i.i_dir_start_lookup = n;
-+	return de;
-+}
++	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);
 +
-+ino_t xip2_inode_by_name(struct inode * dir, struct dentry *dentry)
-+{
-+	ino_t res = 0;
-+	struct ext2_dir_entry_2 * de;
-+	
-+	de = xip2_find_entry (dir, dentry);
-+	if (de) {
-+		res = le32_to_cpu(de->inode);
++	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);
 +	}
-+	return res;
++
++	os_data->timestamp = get_clock();
++	os_data->sync_count_2++;
++	appldata_print_debug(os_data);
 +}
 +
-+struct file_operations xip2_dir_operations = {
-+	read:		generic_read_dir,
-+	readdir:	xip2_readdir,
-+	ioctl:		xip2_ioctl,
++
++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,
 +};
-=== fs/xip2fs/inode.c
-==================================================================
---- fs/xip2fs/inode.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/xip2fs/inode.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,382 @@
++
++
 +/*
-+ *  linux/fs/xip2/inode.c, Version 1
++ * appldata_os_init()
 + *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
++ * init data, register ops
 + */
-+
-+#include <linux/fs.h>
-+#include <linux/xip2_fs.h>
-+#include <linux/iobuf.h>
-+#include <linux/locks.h>
-+#include <linux/smp_lock.h>
-+#include <linux/sched.h>
-+#include <linux/highuid.h>
-+#include <linux/quotaops.h>
-+#include <linux/module.h>
-+
-+#define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512)
-+
-+MODULE_AUTHOR("Carsten Otte, Remy Card and others");
-+MODULE_DESCRIPTION("XIP2 filesystem derived from ext2");
-+MODULE_LICENSE("GPL");
-+
-+typedef struct {
-+	u32	*p;
-+	u32	key;
-+	void *block_ptr;
-+} Indirect;
-+
-+static inline void add_chain(Indirect *p, void *block_ptr, u32 *v)
-+{
-+	p->key = *(p->p = v);
-+	p->block_ptr = block_ptr;
-+}
-+
-+static inline int verify_chain(Indirect *from, Indirect *to)
++static int __init appldata_os_init(void)
 +{
-+	while (from <= to && from->key == *from->p)
-+		from++;
-+	return (from > to);
-+}
++	int rc, size;
 +
-+/**
-+ *	xip2_block_to_path - parse the block number into array of offsets
-+ *	@inode: inode in question (we are only interested in its superblock)
-+ *	@i_block: block number to be parsed
-+ *	@offsets: array to store the offsets in
-+ *
-+ *	To store the locations of file's data ext2 uses a data structure common
-+ *	for UNIX filesystems - tree of pointers anchored in the inode, with
-+ *	data blocks at leaves and indirect blocks in intermediate nodes.
-+ *	This function translates the block number into path in that tree -
-+ *	return value is the path length and @offsets[n] is the offset of
-+ *	pointer to (n+1)th node in the nth one. If @block is out of range
-+ *	(negative or too large) warning is printed and zero returned.
-+ *
-+ *	Note: function doesn't find node addresses, so no IO is needed. All
-+ *	we need to know is the capacity of indirect blocks (taken from the
-+ *	inode->i_sb).
-+ */
++	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));
 +
-+/*
-+ * Portability note: the last comparison (check that we fit into triple
-+ * indirect block) is spelled differently, because otherwise on an
-+ * architecture with 32-bit longs and 8Kb pages we might get into trouble
-+ * if our filesystem had 8Kb blocks. We might use long long, but that would
-+ * kill us on x86. Oh, well, at least the sign propagation does not matter -
-+ * i_block would have to be negative in the very beginning, so we would not
-+ * get there at all.
-+ */
++	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);
 +
-+static int xip2_block_to_path(struct inode *inode, long i_block, int offsets[4])
-+{
-+	int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb);
-+	int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
-+	const long direct_blocks = EXT2_NDIR_BLOCKS,
-+		indirect_blocks = ptrs,
-+		double_blocks = (1 << (ptrs_bits * 2));
-+	int n = 0;
++	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);
 +
-+	if (i_block < 0) {
-+		xip2_warning (inode->i_sb, "xip2_block_to_path", "block < 0");
-+	} else if (i_block < direct_blocks) {
-+		offsets[n++] = i_block;
-+	} else if ( (i_block -= direct_blocks) < indirect_blocks) {
-+		offsets[n++] = EXT2_IND_BLOCK;
-+		offsets[n++] = i_block;
-+	} else if ((i_block -= indirect_blocks) < double_blocks) {
-+		offsets[n++] = EXT2_DIND_BLOCK;
-+		offsets[n++] = i_block >> ptrs_bits;
-+		offsets[n++] = i_block & (ptrs - 1);
-+	} else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
-+		offsets[n++] = EXT2_TIND_BLOCK;
-+		offsets[n++] = i_block >> (ptrs_bits * 2);
-+		offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
-+		offsets[n++] = i_block & (ptrs - 1);
++	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 {
-+		xip2_warning (inode->i_sb, "xip2_block_to_path", 
-+			      "block > big");
++		P_DEBUG("%s-ops registered!\n", ops.name);
 +	}
-+	return n;
++out:
++	return rc;
 +}
 +
-+/**
-+ *	xip2_get_branch - read the chain of indirect blocks leading to data
-+ *	@inode: inode in question
-+ *	@depth: depth of the chain (1 - direct pointer, etc.)
-+ *	@offsets: offsets of pointers in inode/indirect blocks
-+ *	@chain: place to store the result
-+ *	@err: here we store the error value
-+ *
++/*
++ * appldata_os_exit()
++ * 
++ * unregister ops
 + */
-+static Indirect *xip2_get_branch(struct inode *inode,
-+				 int depth,
-+				 int *offsets,
-+				 Indirect chain[4],
-+				 int *err)
++static void __exit appldata_os_exit(void)
 +{
-+	int size = inode->i_sb->s_blocksize;
-+	Indirect *p = chain;
-+	void *block_ptr;
-+
-+	*err = 0;
-+	/* i_data is not going away, no lock needed */
-+	add_chain (chain, NULL, inode->u.ext2_i.i_data + *offsets);
-+	if (!p->key)
-+		goto no_block;
-+	while (--depth) {
-+		block_ptr = xip2_maread(inode->i_sb->u.xip2_sb.mem_area, 
-+					le32_to_cpu(p->key), size);
-+		if (!block_ptr)
-+			goto failure;
-+		/* Reader: pointers */
-+		if (!verify_chain(chain, p))
-+			goto changed;
-+		add_chain(++p, block_ptr, (u32*)block_ptr + *++offsets);
-+		/* Reader: end */
-+		if (!p->key)
-+			goto no_block;
-+	}
-+	return NULL;
-+
-+changed:
-+	*err = -EAGAIN;
-+	goto no_block;
-+failure:
-+	*err = -EIO;
-+no_block:
-+	return p;
++	appldata_unregister_ops(&ops);
++	kfree(appldata_os_data);
++	P_DEBUG("%s-ops unregistered!\n", ops.name);
 +}
 +
-+int xip2_get_block(struct inode *inode, long iblock, unsigned long* blockno_result, 
-+		   int create)
-+{
-+	int err = -EIO;
-+	int offsets[4];
-+	Indirect chain[4];
-+	Indirect *partial;
-+	int depth = xip2_block_to_path(inode, iblock, offsets);
-+
-+	if (depth == 0)
-+		goto out;
-+
-+	partial = xip2_get_branch(inode, depth, offsets, chain, &err);
 +
-+	/* Simplest case - block found, no allocation needed */
-+	if (!partial) {
-+		*blockno_result = le32_to_cpu(chain[depth-1].key);
-+		/* Clean up and exit */
-+		partial = chain+depth-1; /* the whole chain */
-+		goto cleanup;
-+	}
++module_init(appldata_os_init);
++module_exit(appldata_os_exit);
 +
-+	/* Next simple case - plain lookup or failed read of indirect block */
-+	if (!create || err) {
-+cleanup:
-+		while (partial > chain) {
-+			partial--;
-+		}
-+out:
-+		return err;
-+	}
-+	xip2_warning (inode->i_sb, "xip2_get_block", "allocation of a block "
-+		      "would be needed");
-+	return -EROFS;
-+}
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/config.in	2006-01-30 22:25:24.000000000 -0700
+@@ -62,6 +62,33 @@
+ bool 'Show crashed user process info' CONFIG_PROCESS_DEBUG
+ bool 'Pseudo page fault support' CONFIG_PFAULT
+ bool 'VM shared kernel support' CONFIG_SHARED_KERNEL
++bool 'No HZ timer ticks in idle' CONFIG_NO_IDLE_HZ
++if [ "$CONFIG_NO_IDLE_HZ" = "y" ] ; then
++  bool '  Idle HZ timer on by default' CONFIG_NO_IDLE_HZ_INIT
++fi
++bool 'Virtual CPU timer support' CONFIG_VIRT_TIMER 
++dep_bool 'Linux - VM Monitor Stream, base infrastructure' CONFIG_APPLDATA_BASE \
++$CONFIG_PROC_FS $CONFIG_VIRT_TIMER
++dep_tristate '   Monitor memory management statistics' CONFIG_APPLDATA_MEM $CONFIG_APPLDATA_BASE
++dep_tristate '   Monitor OS statistics' CONFIG_APPLDATA_OS $CONFIG_APPLDATA_BASE
++dep_tristate '   Monitor overall network statistics' CONFIG_APPLDATA_NET_SUM $CONFIG_APPLDATA_BASE
++tristate 'Collaborative memory management' CONFIG_CMM
++if [ "$CONFIG_CMM" != "n" ]; then
++  dep_bool '/proc interface to cooperative memory management' CONFIG_CMM_PROC $CONFIG_PROC_FS
++  if [ "$CONFIG_SMSGIUCV" = "y" -o "$CONFIG_SMSGIUCV" = "$CONFIG_CMM" ]; then
++    bool 'IUCV special message interface to cooperative memory management' CONFIG_CMM_IUCV
++  fi
++fi
++endmenu
 +
-+static int xip2_readpage(struct file *file, struct page *page)
-+{
-+	struct inode *inode = page->mapping->host;
-+	unsigned long iblock;
-+	unsigned long blocksize, blocks, blockno;
-+	void* block_ptr;
-+	int err;
++mainmenu_option next_comment
++comment 'SCSI support'
 +
-+	printk ("Whoot whooot! Readpage was called! Trace-me at %p\n",
-+		xip2_readpage);
++tristate 'SCSI support' CONFIG_SCSI
 +
-+	if (!PageLocked(page))
-+		PAGE_BUG(page);
-+	blocksize = 1 << inode->i_blkbits;
-+	blocks = PAGE_CACHE_SIZE >> inode->i_blkbits;
-+	iblock = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
++if [ "$CONFIG_SCSI" != "n" ]; then
++   source drivers/scsi/Config.in
++fi
+ endmenu
+ 
+ source drivers/s390/Config.in
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/defconfig kernel-source-2.4.27-2.4.27/arch/s390/defconfig
+--- kernel-source-2.4.27-2.4.27.orig/arch/s390/defconfig	2006-01-30 22:23:45.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/arch/s390/defconfig	2006-01-30 22:25:24.000000000 -0700
+@@ -48,8 +48,8 @@
+ CONFIG_QDIO=m
+ # CONFIG_QDIO_PERF_STATS is not set
+ CONFIG_IPL=y
+-# CONFIG_IPL_TAPE is not set
+-CONFIG_IPL_VM=y
++CONFIG_IPL_TAPE=y
++# CONFIG_IPL_VM is not set
+ CONFIG_NET=y
+ CONFIG_SYSVIPC=y
+ # CONFIG_BSD_PROCESS_ACCT is not set
+@@ -60,6 +60,50 @@
+ # CONFIG_PROCESS_DEBUG is not set
+ CONFIG_PFAULT=y
+ # CONFIG_SHARED_KERNEL is not set
++# CONFIG_VIRT_TIMER is not set
++# CONFIG_APPLDATA_BASE is not set
++# CONFIG_APPLDATA_MEM is not set
++# CONFIG_APPLDATA_OS is not set
++# CONFIG_APPLDATA_NET_SUM is not set
++CONFIG_CMM=m
++CONFIG_CMM_PROC=y
++# CONFIG_CMM_IUCV is not set
 +
-+	if ((blocksize != PAGE_SIZE) || (blocks != 1))
-+		BUG();
-+	err=xip2_get_block(inode, iblock, &blockno, 0);
-+	if (err)
-+		return err;
-+	block_ptr = xip2_maread(inode->i_sb->u.xip2_sb.mem_area, blockno, 
-+				blocksize);
-+	if (!block_ptr)
-+		return -EIO;
-+	memcpy (page_address(page),block_ptr,blocksize);
-+	SetPageUptodate(page);
-+	UnlockPage(page);
-+	return 0;
-+}
++#
++# SCSI support
++#
++CONFIG_SCSI=m
 +
-+struct address_space_operations xip2_aops = {
-+	readpage: xip2_readpage,
-+};
++#
++# SCSI support type (disk, tape, CD-ROM)
++#
++CONFIG_BLK_DEV_SD=m
++CONFIG_SD_EXTRA_DEVS=1000
++CONFIG_CHR_DEV_ST=m
++# CONFIG_CHR_DEV_OSST is not set
++CONFIG_BLK_DEV_SR=m
++# CONFIG_BLK_DEV_SR_VENDOR is not set
++CONFIG_SR_EXTRA_DEVS=10
++CONFIG_CHR_DEV_SG=m
 +
-+/*
-+ * Probably it should be a library function... search for first non-zero word
-+ * or memcmp with zero_page, whatever is better for particular architecture.
-+ * Linus?
-+ */
-+static inline int all_zeroes(u32 *p, u32 *q)
-+{
-+	while (p < q)
-+		if (*p++)
-+			return 0;
-+	return 1;
-+}
++#
++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
++#
++CONFIG_SCSI_DEBUG_QUEUES=y
++CONFIG_SCSI_MULTI_LUN=y
++CONFIG_SCSI_CONSTANTS=y
++CONFIG_SCSI_LOGGING=y
 +
-+void xip2_read_inode (struct inode * inode)
-+{
-+	void * block_ptr;
-+	struct ext2_inode * raw_inode;
-+	unsigned long block_group;
-+	unsigned long group_desc;
-+	unsigned long desc;
-+	unsigned long block;
-+	unsigned long offset;
-+	struct ext2_group_desc * gdp;
++#
++# SCSI low-level drivers
++#
++CONFIG_ZFCP=m
++CONFIG_ZFCP_HBAAPI=m
 +
-+	if ((inode->i_ino != EXT2_ROOT_INO && 
-+	     inode->i_ino != EXT2_ACL_IDX_INO &&
-+	     inode->i_ino != EXT2_ACL_DATA_INO &&
-+	     inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
-+	    inode->i_ino > 
-+	    le32_to_cpu(inode->i_sb->u.xip2_sb.s_es->s_inodes_count)) {
-+		xip2_error (inode->i_sb, "xip2_read_inode",
-+			    "bad inode number: %lu", inode->i_ino);
-+		goto bad_inode;
-+	}
-+	block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
-+	if (block_group >= inode->i_sb->u.xip2_sb.s_groups_count) {
-+		xip2_error (inode->i_sb, "xip2_read_inode",
-+			    "group >= groups count");
-+		goto bad_inode;
-+	}
-+	group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);
-+	desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);
-+	block_ptr = inode->i_sb->u.xip2_sb.s_group_desc[group_desc];
-+	if (!block_ptr) {
-+		xip2_error (inode->i_sb, "xip2_read_inode",
-+			    "Descriptor not loaded");
-+		goto bad_inode;
-+	}
-+
-+	gdp = (struct ext2_group_desc *) block_ptr;
-+	/*
-+	 * Figure out the offset within the block group inode table
-+	 */
-+	offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
-+		EXT2_INODE_SIZE(inode->i_sb);
-+	block = le32_to_cpu(gdp[desc].bg_inode_table) +
-+		(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
-+	if (!(block_ptr = xip2_maread (inode->i_sb->u.xip2_sb.mem_area, block, 
-+				       inode->i_sb->s_blocksize))) {
-+		xip2_error (inode->i_sb, "xip2_read_inode",
-+			    "unable to read inode block - "
-+			    "inode=%lu, block=%lu", inode->i_ino, block);
-+		goto bad_inode;
-+	}
-+	offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1);
-+	raw_inode = (struct ext2_inode *) (block_ptr + offset);
-+
-+	inode->i_mode = le16_to_cpu(raw_inode->i_mode);
-+	inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
-+	inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
-+	if(!(test_opt (inode->i_sb, NO_UID32))) {
-+		inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
-+		inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
-+	}
-+	inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
-+	inode->i_size = le32_to_cpu(raw_inode->i_size);
-+	inode->i_atime = le32_to_cpu(raw_inode->i_atime);
-+	inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
-+	inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
-+	inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
-+	/* We now have enough fields to check if the inode was active or not.
-+	 * This is needed because nfsd might try to access dead inodes
-+	 * the test is that same one that e2fsck uses
-+	 * NeilBrown 1999oct15
-+	 */
-+	if (inode->i_nlink == 0 && (inode->i_mode == 0 || 
-+				    inode->u.ext2_i.i_dtime)) {
-+		/* this inode is deleted */
-+		goto bad_inode;
-+	}
-+	inode->i_blksize = PAGE_SIZE;
-+	inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
-+	inode->i_version = ++event;
-+	inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags);
-+	inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
-+	inode->u.ext2_i.i_frag_no = raw_inode->i_frag;
-+	inode->u.ext2_i.i_frag_size = raw_inode->i_fsize;
-+	inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
-+	if (S_ISREG(inode->i_mode))
-+		inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) 
-+			<< 32;
-+	else
-+		inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
-+	inode->i_generation = le32_to_cpu(raw_inode->i_generation);
-+	inode->u.ext2_i.i_prealloc_count = 0;
-+	inode->u.ext2_i.i_block_group = block_group;
-+
-+	/*
-+	 * NOTE! The in-memory inode i_data array is in little-endian order
-+	 * even on big-endian machines: we do NOT byteswap the block numbers!
-+	 */
-+	for (block = 0; block < EXT2_N_BLOCKS; block++)
-+		inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
++#
++# PCMCIA SCSI adapter support
++#
++# CONFIG_SCSI_PCMCIA is not set
+ 
+ #
+ # Block device drivers
+@@ -70,6 +114,7 @@
+ CONFIG_BLK_DEV_RAM_SIZE=24576
+ CONFIG_BLK_DEV_INITRD=y
+ CONFIG_BLK_DEV_XPRAM=m
++CONFIG_DCSSBLK=m
+ 
+ #
+ # S/390 block device drivers
+@@ -78,6 +123,7 @@
+ CONFIG_DASD_ECKD=y
+ CONFIG_DASD_FBA=y
+ CONFIG_DASD_DIAG=y
++CONFIG_S390_CMF=m
+ 
+ #
+ # Multi-device support (RAID and LVM)
+@@ -104,22 +150,24 @@
+ CONFIG_TN3270_CONSOLE=y
+ CONFIG_TN3215=y
+ CONFIG_TN3215_CONSOLE=y
+-CONFIG_HWC=y
+-CONFIG_HWC_CONSOLE=y
+-CONFIG_HWC_CPI=m
++CONFIG_SCLP=y
++CONFIG_SCLP_TTY=y
++CONFIG_SCLP_CONSOLE=y
++CONFIG_SCLP_VT220_TTY=y
++CONFIG_SCLP_VT220_CONSOLE=y
++CONFIG_SCLP_CPI=m
+ CONFIG_S390_TAPE=m
+ 
+ #
+ # S/390 tape interface support
+ #
+-CONFIG_S390_TAPE_CHAR=y
+ CONFIG_S390_TAPE_BLOCK=y
+ 
+ #
+ # S/390 tape hardware support
+ #
+-CONFIG_S390_TAPE_3490=y
+-CONFIG_S390_TAPE_3480=y
++CONFIG_S390_TAPE_34XX=m
++CONFIG_VMLOGRDR=m
+ 
+ #
+ # Network device drivers
+@@ -139,8 +187,27 @@
+ #
+ CONFIG_CHANDEV=y
+ CONFIG_HOTPLUG=y
++CONFIG_LCS=m
++CONFIG_QETH=m
 +
-+	if (inode->i_ino == EXT2_ACL_IDX_INO ||
-+	    inode->i_ino == EXT2_ACL_DATA_INO)
-+		/* Nothing to do */ ;
-+	else if (S_ISREG(inode->i_mode)) {
-+		inode->i_op = &xip2_file_inode_operations;
-+		inode->i_fop = &xip2_file_operations;
-+		inode->i_mapping->a_ops = &xip2_aops;
-+	} else if (S_ISDIR(inode->i_mode)) {
-+		inode->i_op = &xip2_dir_inode_operations;
-+		inode->i_fop = &xip2_dir_operations;
-+		inode->i_mapping->a_ops = &xip2_aops;
-+	} else if (S_ISLNK(inode->i_mode)) {
-+		if (!inode->i_blocks)
-+			inode->i_op = &xip2_fast_symlink_inode_operations;
-+		else {
-+			inode->i_op = &xip2_symlink_inode_operations;
-+			inode->i_mapping->a_ops = &xip2_aops;
-+		}
-+	} else 
-+		init_special_inode(inode, inode->i_mode,
-+				   le32_to_cpu(raw_inode->i_block[0]));
-+	inode->i_attr_flags = 0;
-+	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) {
-+		inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS;
-+		inode->i_flags |= S_SYNC;
-+	}
-+	if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL) {
-+		inode->i_attr_flags |= ATTR_FLAG_APPEND;
-+		inode->i_flags |= S_APPEND;
-+	}
-+	if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL) {
-+		inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE;
-+		inode->i_flags |= S_IMMUTABLE;
-+	}
-+	if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL) {
-+		inode->i_attr_flags |= ATTR_FLAG_NOATIME;
-+		inode->i_flags |= S_NOATIME;
-+	}
-+	return;
-+	
-+bad_inode:
-+	make_bad_inode(inode);
-+	return;
-+}
-=== fs/xip2fs/super.c
-==================================================================
---- fs/xip2fs/super.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/xip2fs/super.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,661 @@
-+/*
-+ *  linux/fs/xip2/super.c, Version 1
-+ *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
-+ */
++#
++# Gigabit Ethernet default settings
++#
++CONFIG_QETH_IPV6=y
++CONFIG_QETH_VLAN=y
++# CONFIG_QETH_PERF_STATS is not set
+ CONFIG_CTC=m
++# CONFIG_MPC is not set
+ CONFIG_IUCV=m
++CONFIG_NETIUCV=m
++CONFIG_SMSGIUCV=m
 +
-+#include <linux/config.h>
-+#include <linux/module.h>
-+#include <linux/string.h>
-+#include <linux/fs.h>
-+#include <linux/xip2_fs.h>
-+#include <linux/slab.h>
-+#include <linux/init.h>
-+#include <linux/locks.h>
-+#include <linux/blkdev.h>
-+#include <asm/dcss.h>
-+#include <asm/uaccess.h>
++#
++# Miscellaneous
++#
++CONFIG_Z90CRYPT=m
++# CONFIG_NO_IDLE_HZ is not set
++# CONFIG_NO_IDLE_HZ_INIT is not set
+ 
+ #
+ # Networking options
+@@ -183,7 +250,6 @@
+ CONFIG_IP_NF_MATCH_MARK=m
+ CONFIG_IP_NF_MATCH_MULTIPORT=m
+ CONFIG_IP_NF_MATCH_TOS=m
+-# CONFIG_IP_NF_MATCH_RECENT is not set
+ # CONFIG_IP_NF_MATCH_ECN is not set
+ # CONFIG_IP_NF_MATCH_DSCP is not set
+ CONFIG_IP_NF_MATCH_AH_ESP=m
+@@ -216,14 +282,8 @@
+ CONFIG_IP_NF_TARGET_TCPMSS=m
+ CONFIG_IP_NF_ARPTABLES=m
+ CONFIG_IP_NF_ARPFILTER=m
+-# CONFIG_IP_NF_ARP_MANGLE is not set
+ # CONFIG_IP_NF_COMPAT_IPCHAINS is not set
+ # CONFIG_IP_NF_COMPAT_IPFWADM is not set
+-
+-#
+-#   IP: Virtual Server Configuration
+-#
+-# CONFIG_IP_VS is not set
+ CONFIG_IPV6=m
+ 
+ #
+@@ -248,6 +308,7 @@
+ CONFIG_IP6_NF_TARGET_LOG=m
+ CONFIG_IP6_NF_MANGLE=m
+ CONFIG_IP6_NF_TARGET_MARK=m
++CONFIG_SHARED_IPV6_CARDS=y
+ # CONFIG_KHTTPD is not set
+ 
+ #
+@@ -339,6 +400,7 @@
+ # CONFIG_QNX4FS_FS is not set
+ # CONFIG_QNX4FS_RW is not set
+ # CONFIG_ROMFS_FS is not set
++CONFIG_XIP2FS=m
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/kernel/entry.S	2006-01-30 22:25:24.000000000 -0700
+@@ -690,7 +690,12 @@
+ io_int_handler:
+ 	SAVE_ALL_BASE
+         SAVE_ALL __LC_IO_OLD_PSW,0
++	mc 0,0
+         GET_CURRENT               # load pointer to task_struct to R9
++	stck	__LC_INT_CLOCK
++	clc	__LC_INT_CLOCK(8),__LC_JIFFY_TIMER
++	bhe	BASED(io_handle_tick)
++io_call_handler:
+         l       %r1,BASED(.Ldo_IRQ)        # load address of do_IRQ
+         la      %r2,SP_PTREGS(%r15) # address of register-save area
+         sr      %r3,%r3
+@@ -725,6 +730,15 @@
+         RESTORE_ALL 0
+ 
+ #
++# account tick
++#
++io_handle_tick:
++	l	%r1,BASED(.Laccount_ticks)
++	la	%r2,SP_PTREGS(%r15)    # address of register-save area
++	la	%r14,BASED(io_call_handler)
++	br	%r1
 +
-+static char error_buf[1024];
++#
+ # call do_softirq
+ #
+ io_handle_bottom_half:
+@@ -758,8 +772,13 @@
+ ext_int_handler:
+ 	SAVE_ALL_BASE
+         SAVE_ALL __LC_EXT_OLD_PSW,0
+-        GET_CURRENT                    # load pointer to task_struct to R9
++	mc 0, 0
++	GET_CURRENT                    # load pointer to task_struct to R9
+ 	lh	%r6,__LC_EXT_INT_CODE  # get interruption code
++	stck	__LC_INT_CLOCK
++	clc	__LC_INT_CLOCK(8),__LC_JIFFY_TIMER
++	bhe	BASED(ext_handle_tick)
++ext_call_handler:
+ 	lr	%r1,%r6		       # calculate index = code & 0xff
+ 	n	%r1,BASED(.Lc0xff)
+ 	sll	%r1,2
+@@ -770,7 +789,8 @@
+ ext_int_loop:
+ 	ch	%r6,8(%r7)	       # compare external interrupt code
+ 	bne	BASED(ext_int_next)
+-	l	%r1,4(%r7)	       # get handler address
++	icm	%r1,15,4(%r7)	       # get handler address
++	bz	BASED(ext_int_next)
+ 	la	%r2,SP_PTREGS(%r15)    # address of register-save area
+ 	lr	%r3,%r6		       # interruption code
+ 	basr	%r14,%r1	       # call handler
+@@ -779,6 +799,15 @@
+ 	bnz	BASED(ext_int_loop)
+ 	b	BASED(io_return)
+ 
++#
++# account tick
++#
++ext_handle_tick:
++	l	%r1,BASED(.Laccount_ticks)
++	la	%r2,SP_PTREGS(%r15)    # address of register-save area
++	la	%r14,BASED(ext_call_handler)
++	br	%r1
 +
-+void xip2_error (struct super_block * sb, const char * function,
-+		 const char * fmt, ...)
-+{
-+	va_list args;
+ /*
+  * Machine check handler routines
+  */
+@@ -787,6 +816,7 @@
+ mcck_int_handler:
+ 	SAVE_ALL_BASE
+         SAVE_ALL __LC_MCK_OLD_PSW,0
++	mc 0, 0	
+ 	l       %r1,BASED(.Ls390_mcck)
+ 	basr    %r14,%r1	  # call machine check handler
+ mcck_return:
+@@ -869,4 +899,4 @@
+ .Lvfork:       .long  sys_vfork
+ 
+ .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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/kernel/process.c	2006-01-30 22:25:24.000000000 -0700
+@@ -43,13 +43,27 @@
+ #include <asm/io.h>
+ #include <asm/processor.h>
+ #include <asm/irq.h>
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++#include <asm/timer.h>
++#endif
+ 
+ asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
+ 
++#ifdef CONFIG_VIRT_TIMER
++extern int stop_cpu_timer(void);
++#endif
 +
-+	va_start (args, fmt);
-+	vsprintf (error_buf, fmt, args);
-+	va_end (args);
-+	if (test_opt (sb, ERRORS_PANIC) ||
-+	    (le16_to_cpu(sb->u.xip2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC &&
-+	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO)))
-+		panic ("XIP2-fs panic (device %s): %s: %s\n",
-+		       bdevname(sb->s_dev), function, error_buf);
-+	printk (KERN_CRIT "XIP2-fs error (device %s): %s: %s\n",
-+		bdevname(sb->s_dev), function, error_buf);
-+	if (test_opt (sb, ERRORS_RO) ||
-+	    (le16_to_cpu(sb->u.xip2_sb.s_es->s_errors) == EXT2_ERRORS_RO &&
-+	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) {
-+		printk ("Remounting filesystem read-only\n");
-+		sb->s_flags |= MS_RDONLY;
-+	}
-+}
++#ifdef CONFIG_NO_IDLE_HZ
++extern void stop_hz_timer(void);
++#endif
 +
-+NORET_TYPE void xip2_panic (struct super_block * sb, const char * function,
-+			    const char * fmt, ...)
-+{
-+	va_list args;
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++extern atomic_t active_cpu_timer;
++#endif
 +
-+	if (!(sb->s_flags & MS_RDONLY)) {
-+		sb->u.xip2_sb.s_mount_state |= EXT2_ERROR_FS;
-+		sb->u.xip2_sb.s_es->s_state =
-+			cpu_to_le16(le16_to_cpu(sb->u.xip2_sb.s_es->s_state) 
-+				    | EXT2_ERROR_FS);
-+		sb->s_dirt = 1;
-+	}
-+	va_start (args, fmt);
-+	vsprintf (error_buf, fmt, args);
-+	va_end (args);
-+	sb->s_flags |= MS_RDONLY;
-+	panic ("XIP2-fs panic (device %s): %s: %s\n",
-+	       bdevname(sb->s_dev), function, error_buf);
-+}
+ /*
+  * The idle loop on a S390...
+  */
+-
+ int cpu_idle(void *unused)
+ {
+ 	psw_t wait_psw;
+@@ -68,9 +82,37 @@
+ 			continue;
+ 		}
+ 
++#ifdef CONFIG_VIRT_TIMER
++		/* 
++		 * virtual CPU timer should not progress while its CPU is idle
++		 */
++		if (stop_cpu_timer()) {
++			__sti();
++			continue;
++		}
++#endif
 +
-+void xip2_warning (struct super_block * sb, const char * function,
-+		   const char * fmt, ...)
-+{
-+	va_list args;
++/* 
++ * active_cpu_timer is used by stop_hz_timer to determine if the last
++ * CPU is gone. We have to update this value also if we use the virtual
++ * CPU timer because both use monitor calls.
++ */ 
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++		atomic_dec(&active_cpu_timer);
++#endif
++		
++#ifdef CONFIG_NO_IDLE_HZ
++		stop_hz_timer();
++#endif
 +
-+	va_start (args, fmt);
-+	vsprintf (error_buf, fmt, args);
-+	va_end (args);
-+	printk (KERN_WARNING "XIP2-fs warning (device %s): %s: %s\n",
-+		bdevname(sb->s_dev), function, error_buf);
-+}
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++		/* enable monitor call class 0 */
++		__ctl_set_bit(8, 15);
++#endif
 +
-+void xip2_update_dynamic_rev(struct super_block *sb)
-+{
-+	struct ext2_super_block *es = XIP2_SB(sb)->s_es;
+ 		/* 
+ 		 * Wait for external, I/O or machine check interrupt and
+-		 * switch of machine check bit after the wait has ended.
++		 * switch off machine check bit after the wait has ended.
+ 		 */
+ 		wait_psw.mask = _WAIT_PSW_MASK;
+ 		asm volatile (
+@@ -86,6 +128,10 @@
+ 			"    lpsw 0(%1)\n"
+ 			"2:"
+ 			: "=&a" (reg) : "a" (&wait_psw) : "memory", "cc" );
++		/*
++		 * start_hz_timer is called by monitor call in entry.S
++		 * if stop_hz_timer switched off the regular HZ interrupts
++		 */
+ 	}
+ }
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/s390_ksyms.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/kernel/s390_ksyms.c	2006-01-30 22:25:24.000000000 -0700
+@@ -7,9 +7,13 @@
+ #include <linux/module.h>
+ #include <linux/smp.h>
+ #include <asm/checksum.h>
++#include <asm/cpcmd.h>
+ #include <asm/delay.h>
+ #include <asm/setup.h>
+ #include <asm/softirq.h>
++#ifdef CONFIG_VIRT_TIMER
++#include <asm/timer.h>
++#endif
+ #if CONFIG_IP_MULTICAST
+ #include <net/arp.h>
+ #endif
+@@ -20,6 +24,7 @@
+ EXPORT_SYMBOL_NOVERS(_oi_bitmap);
+ EXPORT_SYMBOL_NOVERS(_ni_bitmap);
+ EXPORT_SYMBOL_NOVERS(_zb_findmap);
++EXPORT_SYMBOL_NOVERS(empty_zero_page);
+ EXPORT_SYMBOL_NOVERS(__copy_from_user_asm);
+ EXPORT_SYMBOL_NOVERS(__copy_to_user_asm);
+ EXPORT_SYMBOL_NOVERS(__clear_user_asm);
+@@ -66,3 +71,22 @@
+ EXPORT_SYMBOL(get_storage_key);
+ EXPORT_SYMBOL_NOVERS(do_call_softirq);
+ EXPORT_SYMBOL(sys_wait4);
++EXPORT_SYMBOL(smp_call_function_on);
++EXPORT_SYMBOL(show_trace);
++EXPORT_SYMBOL(cpcmd);
 +
-+	if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)
-+		return;
 +
-+	xip2_warning(sb, __FUNCTION__,
-+		     "updating to rev %d because of new feature flag, "
-+		     "running e2fsck is recommended",
-+		     EXT2_DYNAMIC_REV);
-+
-+	es->s_first_ino = cpu_to_le32(EXT2_GOOD_OLD_FIRST_INO);
-+	es->s_inode_size = cpu_to_le16(EXT2_GOOD_OLD_INODE_SIZE);
-+	es->s_rev_level = cpu_to_le32(EXT2_DYNAMIC_REV);
-+	/* leave es->s_feature_*compat flags alone */
-+	/* es->s_uuid will be set by e2fsck if empty */
-+
-+	/*
-+	 * The rest of the superblock fields should be zero, and if not it
-+	 * means they are likely already in use, so leave them alone.  We
-+	 * can leave it up to e2fsck to clean up any inconsistencies there.
-+	 */
-+}
-+
-+void xip2_put_super (struct super_block * sb)
-+{
-+	if (sb->u.xip2_sb.mem_area) {
-+		segment_unload (((xip2_mem_area_t*)(sb->u.xip2_sb.mem_area))
-+				->name);
-+		kfree (sb->u.xip2_sb.mem_area);
-+	}
-+	kfree(sb->u.xip2_sb.s_group_desc);
-+	return;
-+}
-+
-+static struct super_operations xip2_sops = {
-+	read_inode:	xip2_read_inode,
-+	put_super:	xip2_put_super,
-+	statfs:		xip2_statfs,
-+	remount_fs:	xip2_remount,
-+};
++/*
++ * virtual CPU timer
++ */
++#ifdef CONFIG_VIRT_TIMER
++EXPORT_SYMBOL(init_virt_timer);
++EXPORT_SYMBOL(add_virt_timer);
++EXPORT_SYMBOL(add_virt_timer_periodic);
++EXPORT_SYMBOL(mod_virt_timer);
++EXPORT_SYMBOL(del_virt_timer);
++#endif
 +
++/* 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/kernel/setup.c	2006-01-30 22:25:24.000000000 -0700
+@@ -276,9 +276,9 @@
+ 
+ static int __init conmode_setup(char *str)
+ {
+-#if defined(CONFIG_HWC_CONSOLE)
+-	if (strncmp(str, "hwc", 4) == 0)
+-                SET_CONSOLE_HWC;
++#if defined(CONFIG_SCLP_CONSOLE)
++	if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
++                SET_CONSOLE_SCLP;
+ #endif
+ #if defined(CONFIG_TN3215_CONSOLE)
+ 	if (strncmp(str, "3215", 5) == 0)
+@@ -310,8 +310,8 @@
+ 		 */
+ 		cpcmd("TERM CONMODE 3215", NULL, 0);
+ 		if (ptr == NULL) {
+-#if defined(CONFIG_HWC_CONSOLE)
+-			SET_CONSOLE_HWC;
++#if defined(CONFIG_SCLP_CONSOLE)
++			SET_CONSOLE_SCLP;
+ #endif
+ 			return;
+ 		}
+@@ -320,16 +320,16 @@
+ 			SET_CONSOLE_3270;
+ #elif defined(CONFIG_TN3215_CONSOLE)
+ 			SET_CONSOLE_3215;
+-#elif defined(CONFIG_HWC_CONSOLE)
+-			SET_CONSOLE_HWC;
++#elif defined(CONFIG_SCLP_CONSOLE)
++			SET_CONSOLE_SCLP;
+ #endif
+ 		} else if (strncmp(ptr + 8, "3215", 4) == 0) {
+ #if defined(CONFIG_TN3215_CONSOLE)
+ 			SET_CONSOLE_3215;
+ #elif defined(CONFIG_TN3270_CONSOLE)
+ 			SET_CONSOLE_3270;
+-#elif defined(CONFIG_HWC_CONSOLE)
+-			SET_CONSOLE_HWC;
++#elif defined(CONFIG_SCLP_CONSOLE)
++			SET_CONSOLE_SCLP;
+ #endif
+ 		}
+         } else if (MACHINE_IS_P390) {
+@@ -339,8 +339,8 @@
+ 		SET_CONSOLE_3270;
+ #endif
+ 	} else {
+-#if defined(CONFIG_HWC_CONSOLE)
+-		SET_CONSOLE_HWC;
++#if defined(CONFIG_SCLP_CONSOLE)
++		SET_CONSOLE_SCLP;
+ #endif
+ 	}
+ }
+@@ -383,21 +383,25 @@
+ 
+ /*
+  * Reboot, halt and power_off stubs. They just call _machine_restart,
+- * _machine_halt or _machine_power_off. 
++ * _machine_halt or _machine_power_off after making sure that all pending
++ * printks reached their destination. 
+  */
+ 
+ void machine_restart(char *command)
+ {
++	console_unblank();
+ 	_machine_restart(command);
+ }
+ 
+ void machine_halt(void)
+ {
++	console_unblank();
+ 	_machine_halt();
+ }
+ 
+ void machine_power_off(void)
+ {
++	console_unblank();
+ 	_machine_power_off();
+ }
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/smp.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/kernel/smp.c	2006-01-30 22:25:24.000000000 -0700
+@@ -92,7 +92,7 @@
+ 
+ extern void reipl(unsigned long devno);
+ 
+-static sigp_ccode smp_ext_bitcall(int, ec_bit_sig);
++static void smp_ext_bitcall(int, ec_bit_sig);
+ static void smp_ext_bitcall_others(ec_bit_sig);
+ 
+ /*
+@@ -131,7 +131,7 @@
+  * in the system.
+  */
+ 
+-int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
++int smp_call_function(void (*func) (void *info), void *info, int nonatomic,
+ 			int wait)
+ /*
+  * [SUMMARY] Run a function on all other CPUs.
+@@ -162,7 +162,7 @@
+ 	spin_lock_bh(&call_lock);
+ 	call_data = &data;
+ 	/* Send a message to all other CPUs and wait for them to respond */
+-        smp_ext_bitcall_others(ec_call_function);
++	smp_ext_bitcall_others(ec_call_function);
+ 
+ 	/* Wait for response */
+ 	while (atomic_read(&data.started) != cpus)
+@@ -176,9 +176,54 @@
+ 	return 0;
+ }
+ 
 +/*
-+ * This function has been shamelessly adapted from the msdos fs
++ * Call a function on one CPU
++ * cpu : the CPU the function should be executed on
++ *
++ * You must not call this function with disabled interrupts or from a
++ * hardware interrupt handler, you may call it from a bottom half handler.
 + */
-+static int parse_options (char * options, unsigned long * sb_block,
-+			  unsigned short *resuid, unsigned short * resgid,
-+			  unsigned long * mount_options,
-+			  xip2_mem_area_t ** mem_area)
-+{
-+	char * this_char;
-+	char * value;
-+
-+	if (!options)
-+		return 1;
-+	for (this_char = strtok (options, ",");
-+	     this_char != NULL;
-+	     this_char = strtok (NULL, ",")) {
-+		if ((value = strchr (this_char, '=')) != NULL)
-+			*value++ = 0;
-+		if (!strcmp (this_char, "bsddf"))
-+			clear_opt (*mount_options, MINIX_DF);
-+		else if (!strcmp (this_char, "nouid32")) {
-+			set_opt (*mount_options, NO_UID32);
-+		}
-+		else if (!strcmp (this_char, "check")) {
-+			if (!value || !*value || !strcmp (value, "none"))
-+				clear_opt (*mount_options, CHECK);
-+			else
-+#ifdef CONFIG_EXT2_CHECK
-+				set_opt (*mount_options, CHECK);
-+#else
-+				printk("XIP2 Check option not supported\n");
-+#endif
-+		}
-+		else if (!strcmp (this_char, "debug"))
-+			set_opt (*mount_options, DEBUG);
-+		else if (!strcmp (this_char, "errors")) {
-+			if (!value || !*value) {
-+				printk ("XIP2-fs: the errors option requires "
-+					"an argument\n");
-+				return 0;
-+			}
-+			if (!strcmp (value, "continue")) {
-+				clear_opt (*mount_options, ERRORS_RO);
-+				clear_opt (*mount_options, ERRORS_PANIC);
-+				set_opt (*mount_options, ERRORS_CONT);
-+			}
-+			else if (!strcmp (value, "remount-ro")) {
-+				clear_opt (*mount_options, ERRORS_CONT);
-+				clear_opt (*mount_options, ERRORS_PANIC);
-+				set_opt (*mount_options, ERRORS_RO);
-+			}
-+			else if (!strcmp (value, "panic")) {
-+				clear_opt (*mount_options, ERRORS_CONT);
-+				clear_opt (*mount_options, ERRORS_RO);
-+				set_opt (*mount_options, ERRORS_PANIC);
-+			}
-+			else {
-+				printk ("XIP2-fs: Invalid errors option: %s\n",
-+					value);
-+				return 0;
-+			}
-+		}
-+		else if (!strcmp (this_char, "grpid") ||
-+			 !strcmp (this_char, "bsdgroups"))
-+			set_opt (*mount_options, GRPID);
-+		else if (!strcmp (this_char, "minixdf"))
-+			set_opt (*mount_options, MINIX_DF);
-+		else if (!strcmp (this_char, "nocheck"))
-+			clear_opt (*mount_options, CHECK);
-+		else if (!strcmp (this_char, "nogrpid") ||
-+			 !strcmp (this_char, "sysvgroups"))
-+			clear_opt (*mount_options, GRPID);
-+		else if (!strcmp (this_char, "resgid")) {
-+			if (!value || !*value) {
-+				printk ("XIP2-fs: the resgid option requires "
-+					"an argument\n");
-+				return 0;
-+			}
-+			*resgid = simple_strtoul (value, &value, 0);
-+			if (*value) {
-+				printk ("XIP2-fs: Invalid resgid option: %s\n",
-+					value);
-+				return 0;
-+			}
-+		}
-+		else if (!strcmp (this_char, "resuid")) {
-+			if (!value || !*value) {
-+				printk ("XIP2-fs: the resuid option requires "
-+					"an argument");
-+				return 0;
-+			}
-+			*resuid = simple_strtoul (value, &value, 0);
-+			if (*value) {
-+				printk ("XIP2-fs: Invalid resuid option: %s\n",
-+					value);
-+				return 0;
-+			}
-+		}
-+		else if (!strcmp (this_char, "sb")) {
-+			if (!value || !*value) {
-+				printk ("XIP2-fs: the sb option requires "
-+					"an argument");
-+				return 0;
-+			}
-+			*sb_block = simple_strtoul (value, &value, 0);
-+			if (*value) {
-+				printk ("XIP2-fs: Invalid sb option: %s\n",
-+					value);
-+				return 0;
-+			}
-+		}
-+		else if (!strcmp (this_char, "memarea")) {
-+			if (!value || !*value) {
-+				printk ("XIP2-fs: the memarea option requires "
-+					"an argument\n");
-+				return 0;
-+			}
-+			*mem_area=kmalloc (sizeof(xip2_mem_area_t)+strlen(value)+1,GFP_ATOMIC);
-+			if (mem_area==NULL) {
-+				printk ("XIP2-fs: out of memory\n");
-+				return 0;
-+			}
-+			(*mem_area)->name=((char*)(*mem_area)) +
-+					sizeof(xip2_mem_area_t);
-+			strcpy ((*mem_area)->name, value);
-+			if (segment_load(value, SEGMENT_SHARED_RO, &(*mem_area)->start, 
-+					 &(*mem_area)->end)<0) {
-+				printk ("XIP2-fs: the memarea specified could "
-+					"not be found\n");
-+				kfree (*mem_area);
-+				*mem_area = NULL;
-+				return 0;
-+			}
-+		}
-+		/* Silently ignore the quota options */
-+		else if (!strcmp (this_char, "grpquota")
-+		         || !strcmp (this_char, "noquota")
-+		         || !strcmp (this_char, "quota")
-+		         || !strcmp (this_char, "usrquota"))
-+			/* Don't do anything ;-) */ ;
-+		else {
-+			printk ("XIP2-fs: Unrecognized mount option %s\n", 
-+				this_char);
-+			return 0;
-+		}
-+	}
-+	return 1;
-+}
-+
-+static int xip2_check_descriptors (struct super_block * sb)
++int smp_call_function_on(void (*func) (void *info), void *info,
++                         int nonatomic, int wait, int cpu)
 +{
-+	int i;
-+	int desc_block = 0;
-+	unsigned long block = le32_to_cpu(sb->u.xip2_sb.s_es->
-+					  s_first_data_block);
-+	struct ext2_group_desc * gdp = NULL;
++	struct call_data_struct data;
 +
-+	xip2_debug ("Checking group descriptors");
++	if (!atomic_read(&smp_commenced))
++		return 0;
 +
-+	for (i = 0; i < sb->u.xip2_sb.s_groups_count; i++)
-+	{
-+		if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
-+			gdp = (struct ext2_group_desc *) 
-+				sb->u.xip2_sb.s_group_desc[desc_block++];
-+		if (le32_to_cpu(gdp->bg_block_bitmap) < block ||
-+		    le32_to_cpu(gdp->bg_block_bitmap) >= 
-+		    block + EXT2_BLOCKS_PER_GROUP(sb))
-+		{
-+			xip2_error (sb, "xip2_check_descriptors",
-+				    "Block bitmap for group %d"
-+				    " not in group (block %lu)!",
-+				    i, (unsigned long) 
-+				    le32_to_cpu(gdp->bg_block_bitmap));
-+			return 0;
-+		}
-+		if (le32_to_cpu(gdp->bg_inode_bitmap) < block ||
-+		    le32_to_cpu(gdp->bg_inode_bitmap) >= block + 
-+		    EXT2_BLOCKS_PER_GROUP(sb))
-+		{
-+			xip2_error (sb, "xip2_check_descriptors",
-+				    "Inode bitmap for group %d"
-+				    " not in group (block %lu)!",
-+				    i, (unsigned long) 
-+				    le32_to_cpu(gdp->bg_inode_bitmap));
-+			return 0;
-+		}
-+		if (le32_to_cpu(gdp->bg_inode_table) < block ||
-+		    le32_to_cpu(gdp->bg_inode_table) + 
-+		    sb->u.xip2_sb.s_itb_per_group >=
-+		    block + EXT2_BLOCKS_PER_GROUP(sb))
-+		{
-+			xip2_error (sb, "xip2_check_descriptors",
-+				    "Inode table for group %d"
-+				    " not in group (block %lu)!",
-+				    i, (unsigned long) 
-+				    le32_to_cpu(gdp->bg_inode_table));
-+			return 0;
-+		}
-+		block += EXT2_BLOCKS_PER_GROUP(sb);
-+		gdp++;
++	if (smp_processor_id() == cpu) {
++		/* direct call to function */
++		func(info);
++		return 0;
 +	}
-+	return 1;
-+}
 +
-+#define log2(n) ffz(~(n))
-+ 
-+/*
-+ * Maximal file size.  There is a direct, and {,double-,triple-}indirect
-+ * block limit, and also a limit of (2^32 - 1) 512-byte sectors in i_blocks.
-+ * We need to be 1 filesystem block less than the 2^32 sector limit.
-+ */
-+static loff_t xip2_max_size(int bits)
-+{
-+	loff_t res = EXT2_NDIR_BLOCKS;
-+	res += 1LL << (bits-2);
-+	res += 1LL << (2*(bits-2));
-+	res += 1LL << (3*(bits-2));
-+	res <<= bits;
-+	if (res > (512LL << 32) - (1 << bits))
-+		res = (512LL << 32) - (1 << bits);
-+	return res;
-+}
++	data.func = func;
++	data.info = info;
 +
-+struct super_block * xip2_read_super (struct super_block * sb, void * data,
-+				      int silent)
-+{
-+	xip2_mem_area_t* mem_area = NULL;
-+	void * block_ptr;
-+	struct ext2_super_block * es;
-+	unsigned long sb_block = 0;
-+	unsigned short resuid = EXT2_DEF_RESUID;
-+	unsigned short resgid = EXT2_DEF_RESGID;
-+	kdev_t dev = sb->s_dev;
-+	int blocksize = BLOCK_SIZE;
-+	int db_count;
-+	int i;
++	atomic_set(&data.started, 0);
++	data.wait = wait;
++	if (wait)
++		atomic_set(&data.finished, 0);
 +
++	spin_lock_bh(&call_lock);
++	call_data = &data;
++	smp_ext_bitcall(cpu, ec_call_function);
 +
-+	blocksize = PAGE_SIZE;
-+	sb->u.xip2_sb.s_mount_opt = 0;
-+	if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
-+	    &sb->u.xip2_sb.s_mount_opt,&mem_area)) {
-+		return NULL;
-+	}
++	/* Wait for response */
++	while (atomic_read(&data.started) != 1)
++		barrier();
 +
-+	if (!mem_area) {
-+		printk ("XIP2-fs: no memarea specified\n");
-+		return NULL;
-+	}
++	if (wait)
++		while (atomic_read(&data.finished) != 1)
++			barrier();
 +
-+	if (!(block_ptr = xip2_maread (mem_area, sb_block, blocksize))) {
-+		printk ("XIP2-fs: unable to read superblock\n");
-+		kfree (mem_area);
-+		return NULL;
-+	}
++	spin_unlock_bh(&call_lock);
++	return 0;
++}
++
+ static inline void do_send_stop(void)
+ {
+-        u32 dummy;
++        unsigned long dummy;
+         int i;
+ 
+         /* stop all processors */
+@@ -199,7 +244,7 @@
+ static inline void do_store_status(void)
+ {
+         unsigned long low_core_addr;
+-        u32 dummy;
++        unsigned long dummy;
+         int i;
+ 
+         /* store status of all processors in their lowcores (real 0) */
+@@ -328,42 +373,41 @@
+ }
+ 
+ /*
+- * Send an external call sigp to another cpu and return without waiting
++ * Send an external call sigp to another cpu and wait
+  * for its completion.
+  */
+-static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig)
++static void smp_ext_bitcall(int cpu, ec_bit_sig sig)
+ {
+-        struct _lowcore *lowcore = get_cpu_lowcore(cpu);
+-        sigp_ccode ccode;
++	struct _lowcore *lowcore = get_cpu_lowcore(cpu);
+ 
+-        /*
+-         * Set signaling bit in lowcore of target cpu and kick it
+-         */
+-        atomic_set_mask(1<<sig, &lowcore->ext_call_fast);
+-        ccode = signal_processor(cpu, sigp_external_call);
+-        return ccode;
 +	/*
-+	 * Note: s_es must be initialized as soon as possible because
-+	 *       some ext2 macro-instructions depend on its value
++	 * Set signaling bit in lowcore of target cpu and kick it
 +	 */
-+	es = (struct ext2_super_block *) (block_ptr + BLOCK_SIZE);
-+	sb->u.xip2_sb.s_es = es;
-+	sb->u.xip2_sb.mem_area = mem_area;
-+	sb->s_magic = le16_to_cpu(es->s_magic);
-+	if (sb->s_magic != EXT2_SUPER_MAGIC) {
-+		if (!silent)
-+			printk ("VFS: Can't find xip2 filesystem on "
-+				"memarea %s.\n", mem_area->name);
-+		goto failed_mount;
++	atomic_set_mask(1<<sig, &lowcore->ext_call_fast);
++	while(signal_processor(cpu, sigp_external_call) == sigp_busy)
++		udelay(10);
+ }
+ 
+ /*
+  * Send an external call sigp to every other cpu in the system and
+- * return without waiting for its completion.
++ * wait for its completion.
+  */
+ static void smp_ext_bitcall_others(ec_bit_sig sig)
+ {
+-        struct _lowcore *lowcore;
+-        int i;
++	struct _lowcore *lowcore;
++	int i;
+ 
+-        for (i = 0; i < smp_num_cpus; i++) {
+-                if (smp_processor_id() == i)
+-                        continue;
+-                lowcore = get_cpu_lowcore(i);
+-                /*
+-                 * Set signaling bit in lowcore of target cpu and kick it
+-                 */
+-                atomic_set_mask(1<<sig, &lowcore->ext_call_fast);
+-                while (signal_processor(i, sigp_external_call) == sigp_busy)
++	for (i = 0; i < smp_num_cpus; i++) {
++		if (smp_processor_id() == i)
++			continue;
++		lowcore = get_cpu_lowcore(i);
++		/*
++		 * Set signaling bit in lowcore of target cpu and kick it
++		 */
++		atomic_set_mask(1<<sig, &lowcore->ext_call_fast);
++		while (signal_processor(i, sigp_external_call) == sigp_busy)
+ 			udelay(10);
+-        }
 +	}
-+	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &&
-+	    (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) ||
-+	     EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
-+	     EXT2_HAS_INCOMPAT_FEATURE(sb, ~0U)))
-+		printk("XIP2-fs warning: feature flags set on rev 0 fs, "
-+		       "running e2fsck is recommended\n");
-+	/*
-+	 * Check if fs should be mounted rdonly. If not let the mount fail.
-+	 */
-+	if (!(sb->s_flags & MS_RDONLY)) {
-+		printk("XIP2-fs: %s: mounting RDWR was requested, "
-+		       "but is not supported. Please mount using"
-+		       "the RO option\n", mem_area->name);
-+		goto failed_mount;
-+	}
-+	
-+	/*
-+	 * Check feature flags regardless of the revision level, since we
-+	 * previously didn't change the revision level when setting the flags,
-+	 * so there is a chance incompat flags are set on a rev 0 filesystem.
-+	 */
-+	if ((i = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP))) {
-+		printk("XIP2-fs: %s: couldn't mount because of "
-+		       "unsupported optional features (%x).\n",
-+		       mem_area->name, i);
-+		goto failed_mount;
-+	}
-+	sb->s_blocksize_bits =
-+		le32_to_cpu(XIP2_SB(sb)->s_es->s_log_block_size) + 10;
-+	sb->s_blocksize = 1 << sb->s_blocksize_bits;
+ }
+ 
+ /*
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/time.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/kernel/time.c	2006-01-30 22:25:24.000000000 -0700
+@@ -4,8 +4,8 @@
+  *  S390 version
+  *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+  *    Author(s): Hartmut Penner (hp at de.ibm.com),
+- *               Martin Schwidefsky (schwidefsky at de.ibm.com),
+- *               Denis Joseph Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
++ *		 Martin Schwidefsky (schwidefsky at de.ibm.com),
++ *		 Denis Joseph Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
+  *
+  *  Derived from "arch/i386/kernel/time.c"
+  *    Copyright (C) 1991, 1992, 1995  Linus Torvalds
+@@ -26,38 +26,67 @@
+ 
+ #include <asm/uaccess.h>
+ #include <asm/delay.h>
+-#include <asm/s390_ext.h>
+-
+ #include <linux/timex.h>
+ #include <linux/config.h>
+-
++#include <asm/s390_ext.h>
+ #include <asm/irq.h>
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++#include <asm/timer.h>
++#endif
+ 
+ /* change this if you have some constant time drift */
+-#define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
++#define USECS_PER_JIFFY	    ((unsigned long) 1000000/HZ)
+ #define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
+ 
++/*
++ * Create a small time difference between the timer interrupts
++ * on the different cpus to avoid lock contention.
++ */
++#define CPU_DEVIATION	    (smp_processor_id() << 12)
 +
-+	sb->s_maxbytes = xip2_max_size(sb->s_blocksize_bits);
+ #define TICK_SIZE tick
+ 
+-static ext_int_info_t ext_int_info_timer;
+-static uint64_t init_timer_cc;
++static ext_int_info_t ext_int_info_cc;
 +
-+	/* If the blocksize doesn't match, tell the user.. */
-+	if (sb->s_blocksize != blocksize) {
-+		printk("XIP2-fs: Blocksize of the filesystem "
-+		       "on %s does not match PAGE_SIZE.\n",
-+		       mem_area->name);
-+			goto failed_mount;
-+		}
++static u64 init_timer_cc;
++static u64 xtime_cc;
+ 
+ extern rwlock_t xtime_lock;
+ extern unsigned long wall_jiffies;
+ 
++#ifdef CONFIG_VIRT_TIMER
++static ext_int_info_t ext_int_info_timer;
++static struct vtimer_queue virt_cpu_timer[NR_CPUS];
++#define VTIMER_MAGIC (0x4b87ad6e + 1)
++#endif
 +
-+	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
-+		sb->u.xip2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
-+		sb->u.xip2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
-+	} else {
-+		sb->u.xip2_sb.s_inode_size = le16_to_cpu(es->s_inode_size);
-+		sb->u.xip2_sb.s_first_ino = le32_to_cpu(es->s_first_ino);
-+		if (sb->u.xip2_sb.s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
-+			printk ("XIP2-fs: unsupported inode size: %d\n",
-+				sb->u.xip2_sb.s_inode_size);
-+			goto failed_mount;
-+		}
-+	}
-+	sb->u.xip2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
-+				   le32_to_cpu(es->s_log_frag_size);
-+	if (sb->u.xip2_sb.s_frag_size)
-+		sb->u.xip2_sb.s_frags_per_block = sb->s_blocksize /
-+						  sb->u.xip2_sb.s_frag_size;
-+	else
-+		sb->s_magic = 0;
-+	sb->u.xip2_sb.s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
-+	sb->u.xip2_sb.s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
-+	sb->u.xip2_sb.s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
-+	sb->u.xip2_sb.s_inodes_per_block = sb->s_blocksize /
-+					   EXT2_INODE_SIZE(sb);
-+	sb->u.xip2_sb.s_itb_per_group = sb->u.xip2_sb.s_inodes_per_group /
-+				        sb->u.xip2_sb.s_inodes_per_block;
-+	sb->u.xip2_sb.s_desc_per_block = sb->s_blocksize /
-+					 sizeof (struct ext2_group_desc);
-+	sb->u.xip2_sb.s_sbp = block_ptr;
-+	if (resuid != EXT2_DEF_RESUID)
-+		sb->u.xip2_sb.s_resuid = resuid;
-+	else
-+		sb->u.xip2_sb.s_resuid = le16_to_cpu(es->s_def_resuid);
-+	if (resgid != EXT2_DEF_RESGID)
-+		sb->u.xip2_sb.s_resgid = resgid;
-+	else
-+		sb->u.xip2_sb.s_resgid = le16_to_cpu(es->s_def_resgid);
-+	sb->u.xip2_sb.s_mount_state = le16_to_cpu(es->s_state);
-+	sb->u.xip2_sb.s_addr_per_block_bits =
-+		log2 (EXT2_ADDR_PER_BLOCK(sb));
-+	sb->u.xip2_sb.s_desc_per_block_bits =
-+		log2 (EXT2_DESC_PER_BLOCK(sb));
-+	if (sb->s_magic != EXT2_SUPER_MAGIC) {
-+		if (!silent)
-+			printk ("VFS: Can't find an xip2 filesystem on dev "
-+				"%s.\n",
-+				bdevname(dev));
-+		goto failed_mount;
-+	}
++#ifdef CONFIG_NO_IDLE_HZ
++ 
++#ifdef CONFIG_NO_IDLE_HZ_INIT
++int sysctl_hz_timer = 0;
++#else
++int sysctl_hz_timer = 1;
++#endif
 +
-+	if (sb->s_blocksize != sb->u.xip2_sb.s_frag_size) {
-+		printk ("XIP2-fs: fragsize %lu != blocksize %lu "
-+			"(not supported yet)\n",
-+			sb->u.xip2_sb.s_frag_size, sb->s_blocksize);
-+		goto failed_mount;
-+	}
++#endif
 +
-+	if (sb->u.xip2_sb.s_blocks_per_group > sb->s_blocksize * 8) {
-+		printk ("XIP2-fs: #blocks per group too big: %lu\n",
-+			sb->u.xip2_sb.s_blocks_per_group);
-+		goto failed_mount;
-+	}
-+	if (sb->u.xip2_sb.s_frags_per_group > sb->s_blocksize * 8) {
-+		printk ("XIP2-fs: #fragments per group too big: %lu\n",
-+			sb->u.xip2_sb.s_frags_per_group);
-+		goto failed_mount;
-+	}
-+	if (sb->u.xip2_sb.s_inodes_per_group > sb->s_blocksize * 8) {
-+		printk ("XIP2-fs: #inodes per group too big: %lu\n",
-+			sb->u.xip2_sb.s_inodes_per_group);
-+		goto failed_mount;
-+	}
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++atomic_t active_cpu_timer = ATOMIC_INIT(0);
++#endif
 +
-+	sb->u.xip2_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) -
-+				        le32_to_cpu(es->s_first_data_block) +
-+				       EXT2_BLOCKS_PER_GROUP(sb) - 1) /
-+				       EXT2_BLOCKS_PER_GROUP(sb);
-+	db_count = (sb->u.xip2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1)
-+		/ EXT2_DESC_PER_BLOCK(sb);
-+	sb->u.xip2_sb.s_group_desc = kmalloc (db_count * sizeof (void *), 
-+					      GFP_KERNEL);
-+	if (sb->u.xip2_sb.s_group_desc == NULL) {
-+		printk ("XIP2-fs: not enough memory\n");
-+		goto failed_mount;
-+	}
-+	for (i = 0; i < db_count; i++) {
-+		sb->u.xip2_sb.s_group_desc[i] = xip2_maread 
-+			(mem_area, sb_block + i + 1, sb->s_blocksize);
-+		if (!sb->u.xip2_sb.s_group_desc[i]) {
-+			kfree(sb->u.xip2_sb.s_group_desc);
-+			printk ("XIP2-fs: unable to read group descriptors\n");
-+			goto failed_mount;
-+		}
-+	}
-+	if (!xip2_check_descriptors (sb)) {
-+		printk ("XIP2-fs: group descriptors corrupted!\n");
-+		db_count = i;
-+		goto failed_mount2;
-+	}
-+	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
-+		sb->u.xip2_sb.s_inode_bitmap_number[i] = 0;
-+		sb->u.xip2_sb.s_inode_bitmap[i] = NULL;
-+		sb->u.xip2_sb.s_block_bitmap_number[i] = 0;
-+		sb->u.xip2_sb.s_block_bitmap[i] = NULL;
-+	}
-+	sb->u.xip2_sb.s_loaded_inode_bitmaps = 0;
-+	sb->u.xip2_sb.s_loaded_block_bitmaps = 0;
-+	sb->u.xip2_sb.s_gdb_count = db_count;
-+	/*
-+	 * set up enough so that it can read an inode
+ void tod_to_timeval(__u64 todval, struct timeval *xtime)
+ {
+-        const int high_bit = 0x80000000L;
+-        const int c_f4240 = 0xf4240L;
+-        const int c_7a120 = 0x7a120;
++	const int high_bit = 0x80000000L;
++	const int c_f4240 = 0xf4240L;
++	const int c_7a120 = 0x7a120;
+ 	/* We have to divide the 64 bit value todval by 4096
+ 	 * (because the 2^12 bit is the one that changes every 
+-         * microsecond) and then split it into seconds and
+-         * microseconds. A value of max (2^52-1) divided by
+-         * the value 0xF4240 can yield a max result of approx
+-         * (2^32.068). Thats to big to fit into a signed int
++	 * microsecond) and then split it into seconds and
++	 * microseconds. A value of max (2^52-1) divided by
++	 * the value 0xF4240 can yield a max result of approx
++	 * (2^32.068). Thats to big to fit into a signed int
+ 	 *   ... hacking time!
+-         */
 +	 */
-+	sb->s_op = &xip2_sops;
-+	sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO));
-+	if (!sb->s_root || !S_ISDIR(sb->s_root->d_inode->i_mode) ||
-+	    !sb->s_root->d_inode->i_blocks || !sb->s_root->d_inode->i_size) {
-+		if (sb->s_root) {
-+			dput(sb->s_root);
-+			sb->s_root = NULL;
-+			printk(KERN_ERR "XIP2-fs: corrupt root inode, run "
-+			       "e2fsck\n");
-+		} else
-+			printk(KERN_ERR "XIP2-fs: get root inode failed\n");
-+		goto failed_mount2;
-+	}
-+	return sb;
-+failed_mount2:
-+	kfree(sb->u.xip2_sb.s_group_desc);
-+failed_mount:
-+	kfree(mem_area);
-+	return NULL;
+ 	asm volatile ("L     2,%1\n\t"
+ 		      "LR    3,2\n\t"
+ 		      "SRL   2,12\n\t"
+@@ -70,12 +99,12 @@
+ 		      "JL    .+12\n\t"
+ 		      "S     2,%2\n\t"
+ 		      "L     4,%3\n\t"
+-                      "D     2,%4\n\t"
++		      "D     2,%4\n\t"
+ 		      "OR    3,4\n\t"
+ 		      "ST    2,%O0+4(%R0)\n\t"
+ 		      "ST    3,%0"
+ 		      : "=m" (*xtime) : "m" (todval),
+-		        "m" (c_7a120), "m" (high_bit), "m" (c_f4240)
++			"m" (c_7a120), "m" (high_bit), "m" (c_f4240)
+ 		      : "cc", "memory", "2", "3", "4" );
+ }
+ 
+@@ -83,8 +112,8 @@
+ {
+ 	__u64 now;
+ 
+-	asm ("STCK 0(%0)" : : "a" (&now) : "memory", "cc");
+-        now = (now - init_timer_cc) >> 12;
++	asm volatile ("STCK 0(%0)" : : "a" (&now) : "memory", "cc");
++	now = (now - init_timer_cc) >> 12;
+ 	/* We require the offset from the latest update of xtime */
+ 	now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
+ 	return (unsigned long) now;
+@@ -114,7 +143,6 @@
+ 
+ void do_settimeofday(struct timeval *tv)
+ {
+-
+ 	write_lock_irq(&xtime_lock);
+ 	/* This is revolting. We need to set the xtime.tv_usec
+ 	 * correctly. However, the value in this location is
+@@ -137,58 +165,280 @@
+ 	write_unlock_irq(&xtime_lock);
+ }
+ 
++static inline __u32 div64_32(__u64 dividend, __u32 divisor)
++{
++	register_pair rp;
++
++	rp.pair = dividend;
++	asm ("dr %0,%1" : "+d" (rp) : "d" (divisor));
++	return rp.subreg.odd;
 +}
 +
-+int xip2_remount (struct super_block * sb, int * flags, char * data)
+ /*
+  * timer_interrupt() needs to keep up the real-time clock,
+  * as well as call the "do_timer()" routine every clocktick
+  */
++void account_ticks(struct pt_regs *regs)
 +{
-+	struct ext2_super_block * es;
-+	xip2_mem_area_t* mem_area;
-+	unsigned short resuid = sb->u.xip2_sb.s_resuid;
-+	unsigned short resgid = sb->u.xip2_sb.s_resgid;
-+	unsigned long new_mount_opt;
-+	unsigned long tmp;
++	int cpu = smp_processor_id();
++	__u64 tmp;
++	__u32 ticks;
 +
-+	/* filesystem is read-only _only_, therefore mounting rdwr is not permitted */
-+	if (!(*flags & MS_RDONLY)) 
-+		return -EINVAL;
++	/* Calculate how many ticks have passed. */
++	tmp = S390_lowcore.int_clock - S390_lowcore.jiffy_timer;
++	if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
++		ticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1) + 1;
++		S390_lowcore.jiffy_timer +=
++			CLK_TICKS_PER_JIFFY * (__u64) ticks;
++	} else if (tmp > CLK_TICKS_PER_JIFFY) {
++		ticks = 2;
++		S390_lowcore.jiffy_timer += 2*CLK_TICKS_PER_JIFFY;
++	} else {
++		ticks = 1;
++		S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
++	}
++
++	/* set clock comparator for next tick */
++	tmp = S390_lowcore.jiffy_timer + CPU_DEVIATION;
++	asm volatile ("SCKC %0" : : "m" (tmp));
 +
++	irq_enter(cpu, 0);
+ 
+ #ifdef CONFIG_SMP
+-extern __u16 boot_cpu_addr;
 +	/*
-+	 * Allow the "check" option to be passed as a remount option.
++	 * Do not rely on the boot cpu to do the calls to do_timer.
++	 * Spread it over all cpus instead.
 +	 */
-+	new_mount_opt = sb->u.xip2_sb.s_mount_opt;
-+	mem_area = sb->u.xip2_sb.mem_area;
-+	if (!parse_options (data, &tmp, &resuid, &resgid,
-+			    &new_mount_opt, &mem_area))
-+		return -EINVAL;
-+
-+	if ((!mem_area) || (mem_area != sb->u.xip2_sb.mem_area))
-+		return -EINVAL;
++	write_lock(&xtime_lock);
++	if (S390_lowcore.jiffy_timer > xtime_cc) {
++		__u32 xticks;
 +
-+	sb->u.xip2_sb.s_mount_opt = new_mount_opt;
-+	sb->u.xip2_sb.s_resuid = resuid;
-+	sb->u.xip2_sb.s_resgid = resgid;
-+	es = sb->u.xip2_sb.s_es;
-+	return 0;
++		tmp = S390_lowcore.jiffy_timer - xtime_cc;
++		if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
++			xticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1);
++			xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY;
++		} else {
++			xticks = 1;
++			xtime_cc += CLK_TICKS_PER_JIFFY;
++		}
++		while (xticks--)
++			do_timer(regs);
++	}
++	write_unlock(&xtime_lock);
++	while (ticks--)
++		update_process_times(user_mode(regs));
++#else
++	while (ticks--)
++		do_timer(regs);
+ #endif
++	irq_exit(cpu, 0);
++}
+ 
+-static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
++#ifdef CONFIG_VIRT_TIMER
++void start_cpu_timer(void)
+ {
+-	int cpu = smp_processor_id();
++	struct vtimer_queue *vt_list;
+ 
+-	irq_enter(cpu, 0);
++	vt_list = &virt_cpu_timer[smp_processor_id()];
++	set_vtimer(vt_list->idle);
 +}
 +
-+int xip2_statfs (struct super_block * sb, struct statfs * buf)
++int stop_cpu_timer(void)
 +{
-+	unsigned long overhead;
-+	int i;
-+
-+	if (test_opt (sb, MINIX_DF))
-+		overhead = 0;
-+	else {
-+		/*
-+		 * Compute the overhead (FS structures)
-+		 */
++	__u64 done;
++	struct vtimer_queue *vt_list;
 +
-+		/*
-+		 * All of the blocks before first_data_block are
-+		 * overhead
-+		 */
-+		overhead = le32_to_cpu(sb->u.xip2_sb.s_es->s_first_data_block);
++	vt_list = &virt_cpu_timer[smp_processor_id()];
 +
-+		/*
-+		 * Add the overhead attributed to the superblock and
-+		 * block group descriptors.  If the sparse superblocks
-+		 * feature is turned on, then not all groups have this.
-+		 */
-+		for (i = 0; i < XIP2_SB(sb)->s_groups_count; i++)
-+			overhead += xip2_bg_has_super(sb, i) +
-+				xip2_bg_num_gdb(sb, i);
++	/* store progress */
++	asm volatile ("STPT %0" : "=m" (done));
+ 
+ 	/*
+-	 * set clock comparator for next tick
++	 * If done is negative we do not stop the CPU timer
++	 * because we will get instantly an interrupt that 
++	 * will start the CPU timer again.
+ 	 */
+-        S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
+-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
++	if (done & 1LL<<63)
++		return 1;
++	else
++		vt_list->offset += vt_list->to_expire - done;
 +
-+		/*
-+		 * Every block group has an inode bitmap, a block
-+		 * bitmap, and an inode table.
-+		 */
-+		overhead += (sb->u.xip2_sb.s_groups_count *
-+			     (2 + sb->u.xip2_sb.s_itb_per_group));
-+	}
++	/* save the actual expire value */
++	vt_list->idle = done;
 +
-+	buf->f_type = EXT2_SUPER_MAGIC;
-+	buf->f_bsize = sb->s_blocksize;
-+	buf->f_blocks = le32_to_cpu(sb->u.xip2_sb.s_es->s_blocks_count) 
-+		- overhead;
-+	buf->f_bfree = xip2_count_free_blocks (sb);
-+	buf->f_bavail = buf->f_bfree - 
-+		le32_to_cpu(sb->u.xip2_sb.s_es->s_r_blocks_count);
-+	if (buf->f_bfree < le32_to_cpu(sb->u.xip2_sb.s_es->s_r_blocks_count))
-+		buf->f_bavail = 0;
-+	buf->f_files = le32_to_cpu(sb->u.xip2_sb.s_es->s_inodes_count);
-+	buf->f_ffree = xip2_count_free_inodes (sb);
-+	buf->f_namelen = EXT2_NAME_LEN;
++	/* 
++	 * We cannot halt the CPU timer, we just write a value that
++	 * nearly never expires (only after 71 years) and re-write
++	 * the stored expire value if we continue the timer 
++	 */
++	set_vtimer(VTIMER_MAX_SLICE);
 +	return 0;
 +}
-+
-+static DECLARE_FSTYPE (xip2_fs_type, "xip2", xip2_read_super, 
-+		       FS_NO_DCACHE|FS_NO_PRELIM);
-+
-+static int __init init_xip2_fs(void)
+ 
+-#ifdef CONFIG_SMP
+-	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr)
+-		write_lock(&xtime_lock);
++void set_vtimer(__u64 expires)
 +{
-+        return register_filesystem(&xip2_fs_type);
++	asm volatile ("SPT %0" : : "m" (expires));
+ 
+-	update_process_times(user_mode(regs));
++	/* store expire time for this CPU timer */
++	virt_cpu_timer[smp_processor_id()].to_expire = expires;
 +}
-+
-+static void __exit exit_xip2_fs(void)
+ 
+-	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) {
+-		do_timer(regs);
+-		write_unlock(&xtime_lock);
++/*
++ * Sorted add to a list. List is linear searched until first bigger 
++ * element is found.
++ */
++void list_add_sorted(struct vtimer_list *timer, struct list_head *head)
 +{
-+	unregister_filesystem(&xip2_fs_type);
-+}
-+
-+EXPORT_NO_SYMBOLS;
++	struct vtimer_list *event;
 +
-+module_init(init_xip2_fs)
-+module_exit(exit_xip2_fs)
-=== fs/xip2fs/file.c
-==================================================================
---- fs/xip2fs/file.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/xip2fs/file.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,169 @@
++	list_for_each_entry(event, head, entry) {
++		if (event->expires > timer->expires) {
++			list_add_tail(&timer->entry, &event->entry);
++			return;
++		}
+ 	}
+-#else
+-	do_timer(regs);
+-#endif
++	list_add_tail(&timer->entry, head);
++}
+ 
+-	irq_exit(cpu, 0);
 +/*
-+ *  linux/fs/xip2/file.c, Version 1
-+ *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
++ * Do the callback functions of expired vtimer events.
++ * Called from within the interrupt handler.
 + */
++static void do_callbacks(struct list_head *cb_list, struct pt_regs *regs)
++{
++	struct vtimer_queue *vt_list;
++	struct vtimer_list *event;
++	struct list_head *ptr, *tmp;
++	void (*fn)(unsigned long, struct pt_regs*);
++	unsigned long data;
 +
-+#include <linux/fs.h>
-+#include <linux/xip2_fs.h>
-+#include <linux/sched.h>
-+#include <linux/mm.h>
-+#include <linux/pagemap.h>
-+#include <asm/uaccess.h>
-+#include <asm/page.h>
-+#include <asm/pgtable.h>
-+
-+/*
-+ * We have mostly NULL's here: the current defaults are ok for
-+ * the xip2 filesystem. for mmap, we use a special implementation
-+ * that provides execute in place functionality
-+ */
-+struct file_operations xip2_file_operations = {
-+	llseek:		generic_file_llseek,
-+	read:		xip2_file_read,
-+	write:		generic_file_write,
-+	ioctl:		xip2_ioctl,
-+	mmap:		xip2_file_mmap,
-+	open:		generic_file_open,
-+};
++	if (list_empty(cb_list))
++		return;
 +
-+struct inode_operations xip2_file_inode_operations = {};
++	vt_list = &virt_cpu_timer[smp_processor_id()];
 +
-+static struct vm_operations_struct xip2_file_vm_ops = {
-+	nopage:		xip2_nopage_in_place,
-+};
++	list_for_each_safe(ptr, tmp, cb_list) {
++		event = list_entry(ptr, struct vtimer_list, entry);
 +
-+/* This is used for a general mmap of a disk file */
++		fn = event->function;
++		data = event->data;
++		fn(data, regs);
 +
-+int xip2_file_mmap(struct file * file, struct vm_area_struct * vma)
++		if (!event->interval)
++			/* delete one shot timer */
++			list_del_init(ptr);
++		else {
++			/* move interval timer back to list */
++			spin_lock(&vt_list->lock);
++			list_del_init(&event->entry);
++			list_add_sorted(event, &vt_list->list);
++			spin_unlock(&vt_list->lock);
++		}
++	}
+ }
+ 
+ /*
+- * Start the clock comparator on the current CPU
++ * Handler for the virtual CPU timer.
++ */
++static void do_cpu_timer_interrupt(struct pt_regs *regs, __u16 error_code)
 +{
-+	struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
-+	struct inode *inode = mapping->host;
++	int cpu;
++	__u64 next, delta;
++	struct list_head *ptr, *tmp;
++	struct vtimer_queue *vt_list;
++	struct vtimer_list *event;
++	/* the callback queue */
++	struct list_head cb_list; 
 +
-+	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
-+		if (!mapping->a_ops->writepage)
-+			return -EINVAL;
-+}
-+	if (!mapping->a_ops->readpage)
-+		return -ENOEXEC;
-+	UPDATE_ATIME(inode);
-+	vma->vm_ops = &xip2_file_vm_ops;
-+	return 0;
-+}
++	INIT_LIST_HEAD(&cb_list);
++	cpu = smp_processor_id();
++	vt_list = &virt_cpu_timer[cpu];
 +
-+struct page * xip2_nopage_in_place(struct vm_area_struct * area, 
-+				   unsigned long address, int unused)
-+{
-+	int error;
-+	unsigned long blockno=ULONG_MAX;
-+	void* block_ptr;
-+	struct file *file = area->vm_file;
-+	struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
-+	struct inode *inode = mapping->host;
-+	unsigned long pgoff;
++	/* walk timer list, fire all expired events */
++	spin_lock(&vt_list->lock);
 +
-+	pgoff = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + 
-+		area->vm_pgoff;
-+	error=xip2_get_block(inode, pgoff, &blockno, 0);
-+	if (error) {
-+		printk ("XIP2-FS: xip2_nopage_in_place could not fullfill "
-+			"page request\n");
-+		return NULL;
++	if (vt_list->to_expire < VTIMER_MAX_SLICE)
++		vt_list->offset += vt_list->to_expire;
++
++	list_for_each_safe(ptr, tmp, &vt_list->list) {
++		event = list_entry(ptr, struct vtimer_list, entry);
++
++		if (event->expires > vt_list->offset)
++			/* found first unexpired event, leave */
++			break;
++
++		/* re-charge interval timer, we have to add the offset */
++		if (event->interval)
++			event->expires = event->interval + vt_list->offset;
++
++		/* move expired timer to the callback queue */
++		list_move_tail(ptr, &cb_list);
 +	}
-+	block_ptr = xip2_maread(inode->i_sb->u.xip2_sb.mem_area, blockno, 
-+				PAGE_SIZE);
-+	if (!block_ptr) 
-+		return virt_to_page(empty_zero_page);
-+	return virt_to_page(block_ptr);
-+}
++	spin_unlock(&vt_list->lock);
++	do_callbacks(&cb_list, regs);
 +
-+void xip2_do_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc)
-+{
-+	struct address_space *mapping = filp->f_dentry->d_inode->i_mapping;
-+	struct inode *inode = mapping->host;
-+	unsigned long block,offset,rdlen,count, iblock, lblock, blockno;
-+	void* block_ptr,* cpystart;
-+	int error,cpycount;
++	/* next event is first in list */
++	spin_lock(&vt_list->lock);
++	if (!list_empty(&vt_list->list)) {
++		ptr = vt_list->list.next;
++		event = list_entry(ptr, struct vtimer_list, entry);
++		next = event->expires - vt_list->offset;
 +
-+	iblock = (*ppos)/PAGE_SIZE;
-+	offset = (*ppos)%PAGE_SIZE;
-+	rdlen = desc->count;
-+	if ((*ppos)+desc->count > inode->i_size)
-+		rdlen = inode->i_size - (*ppos);
-+	lblock = (*ppos + rdlen) / PAGE_SIZE;
-+	count = 0;
-+	for (block = iblock; block <= lblock; block++) {
-+		error=xip2_get_block(inode, block, &blockno, 0);
-+		if (error) {
-+			desc->error = error;
-+			desc->written = count;
-+			return;
-+}
-+		block_ptr = xip2_maread (inode->i_sb->u.xip2_sb.mem_area, 
-+					 blockno, PAGE_SIZE);
-+		if (block_ptr) {
-+			if (block == iblock) {
-+				cpystart = block_ptr + offset;
-+				cpycount = PAGE_SIZE - offset;
++		/* add the expired time from this interrupt handler 
++		 * and the callback functions
++		 */
++		asm volatile ("STPT %0" : "=m" (delta));
++		delta = 0xffffffffffffffffLL - delta + 1;
++		vt_list->offset += delta;
++		next -= delta;
 +	} else {
-+				cpystart = block_ptr;
-+				cpycount = PAGE_SIZE;
-+			}
-+		} else {
-+			// there is no block assigned, copy zeros over
-+			if (block == iblock) {
-+				cpystart = empty_zero_page;
-+				cpycount = PAGE_SIZE - offset;
-+			} else {
-+				cpystart = empty_zero_page;
-+				cpycount = PAGE_SIZE;
-+		}
-+	}
-+		if (cpycount > rdlen-count) {
-+			cpycount = rdlen-count;
-+			if (block!=lblock) BUG();
-+}
-+		if (copy_to_user(desc->buf+count, cpystart, cpycount)) {
-+			desc->error = -EFAULT;
-+			desc->written = count;
-+			return;
-+}
-+		count += cpycount;
++		vt_list->offset = 0;
++		next = VTIMER_MAX_SLICE;
 +	}
-+	if (rdlen-count>0) BUG();
-+	desc->error = 0;
-+	desc->written = count;
-+	*ppos+=count;
-+	return;
++	spin_unlock(&vt_list->lock);
++	set_vtimer(next);
 +}
++#endif
 +
++/*
++ * Start the clock comparator and the virtual CPU timer 
++ * on the current CPU
+  */
+ void init_cpu_timer(void)
+ {
+ 	unsigned long cr0;
++	__u64 timer;
++#ifdef CONFIG_VIRT_TIMER
++	struct vtimer_queue *vt_list;
++#endif
+ 
+-	S390_lowcore.jiffy_timer = (__u64) jiffies * CLK_TICKS_PER_JIFFY;
+-	S390_lowcore.jiffy_timer += init_timer_cc + CLK_TICKS_PER_JIFFY;
+-	asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
+-        /* allow clock comparator timer interrupt */
+-        asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory");
+-        cr0 |= 0x800;
+-        asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory");
++	timer = init_timer_cc + (__u64) jiffies * CLK_TICKS_PER_JIFFY;
++	S390_lowcore.jiffy_timer = timer + CLK_TICKS_PER_JIFFY;
++	timer += CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
++	asm volatile ("SCKC %0" : : "m" (timer));
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ) 
++	atomic_inc(&active_cpu_timer);
++#endif
++	/* allow clock comparator timer interrupt */
++	asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory");
++	cr0 |= 0x800;
++	asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory");
 +
-+ssize_t xip2_file_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
-+{
-+	ssize_t retval;
-+
-+	if ((ssize_t) count < 0)
-+		return -EINVAL;
++#ifdef CONFIG_VIRT_TIMER
++	/* kick the virtual timer */
++	timer = VTIMER_MAX_SLICE;
++	asm volatile ("SPT %0" : : "m" (timer));
++	__ctl_store(cr0, 0, 0);
++	cr0 |= 0x400;
++	__ctl_load(cr0, 0, 0);
 +
-+	retval = -EFAULT;
-+	if (access_ok(VERIFY_WRITE, buf, count)) {
-+		retval = 0;
++	vt_list = &virt_cpu_timer[smp_processor_id()];
++	INIT_LIST_HEAD(&vt_list->list);
++	spin_lock_init(&vt_list->lock);
++	vt_list->to_expire = 0;
++	vt_list->offset = 0;
++	vt_list->idle = 0;
++#endif
+ }
+ 
+ /*
+@@ -196,39 +446,374 @@
+  * the boot cpu.
+  */
+ void __init time_init(void)
+-{
+-        __u64 set_time_cc;
++{	
++	__u64 set_time_cc;
+ 	int cc;
+ 
+-        /* kick the TOD clock */
+-        asm volatile ("STCK 0(%1)\n\t"
+-                      "IPM  %0\n\t"
+-                      "SRL  %0,28" : "=r" (cc) : "a" (&init_timer_cc) 
++	/* kick the TOD clock */
++	asm volatile ("STCK 0(%1)\n\t"
++		      "IPM  %0\n\t"
++		      "SRL  %0,28" : "=r" (cc) : "a" (&init_timer_cc) 
+ 				   : "memory", "cc");
+-        switch (cc) {
+-        case 0: /* clock in set state: all is fine */
+-                break;
+-        case 1: /* clock in non-set state: FIXME */
+-                printk("time_init: TOD clock in non-set state\n");
+-                break;
+-        case 2: /* clock in error state: FIXME */
+-                printk("time_init: TOD clock in error state\n");
+-                break;
+-        case 3: /* clock in stopped or not-operational state: FIXME */
+-                printk("time_init: TOD clock stopped/non-operational\n");
+-                break;
+-        }
++	switch (cc) {
++	case 0: /* clock in set state: all is fine */
++		break;
++	case 1: /* clock in non-set state: FIXME */
++		printk("time_init: TOD clock in non-set state\n");
++		break;
++	case 2: /* clock in error state: FIXME */
++		printk("time_init: TOD clock in error state\n");
++		break;
++	case 3: /* clock in stopped or not-operational state: FIXME */
++		printk("time_init: TOD clock stopped/non-operational\n");
++		break;
++	}
+ 
+ 	/* set xtime */
+-        set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
+-                      (0x3c26700LL*1000000*4096);
+-        tod_to_timeval(set_time_cc, &xtime);
+-
+-        /* request the 0x1004 external interrupt */
+-        if (register_early_external_interrupt(0x1004, do_comparator_interrupt,
++	xtime_cc = init_timer_cc + CLK_TICKS_PER_JIFFY;
++	set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
++		      (0x3c26700LL*1000000*4096);
++	tod_to_timeval(set_time_cc, &xtime);
 +
-+		if (count) {
-+			read_descriptor_t desc;
++	/* request the clock comparator external interrupt */
++	if (register_early_external_interrupt(0x1004, NULL,
++					      &ext_int_info_cc) != 0)
++		panic("Couldn't request external interrupt 0x1004");
 +
-+			desc.written = 0;
-+			desc.count = count;
-+			desc.buf = buf;
-+			desc.error = 0;
-+			xip2_do_file_read(filp, ppos, &desc);
++#ifdef CONFIG_VIRT_TIMER
++	/* request the cpu timer external interrupt */
++	if (register_early_external_interrupt(0x1005, do_cpu_timer_interrupt,
+ 					      &ext_int_info_timer) != 0)
+-                panic("Couldn't request external interrupt 0x1004");
++		panic("Couldn't request external interrupt 0x1005");
++#endif
 +
-+			retval = desc.written;
-+			if (!retval)
-+				retval = desc.error;
-+	}
-+}
-+	return retval;
++	init_cpu_timer();
 +}
-=== fs/xip2fs/ialloc.c
-==================================================================
---- fs/xip2fs/ialloc.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/xip2fs/ialloc.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,34 @@
-+/*
-+ *  linux/fs/xip2/ialloc.c, Version 1
-+ *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/fs.h>
-+#include <linux/xip2_fs.h>
-+#include <linux/locks.h>
-+#include <linux/quotaops.h>
-+
 +
-+/*
-+ * ialloc.c contains the inodes allocation and deallocation routines
-+ */
++#ifdef CONFIG_VIRT_TIMER
++void init_virt_timer(struct vtimer_list *timer)
++{
++	timer->magic = VTIMER_MAGIC;
++	timer->function = NULL;
++	INIT_LIST_HEAD(&timer->entry);
++	spin_lock_init(&timer->lock);
++}
 +
-+/*
-+ * The free inodes are managed by bitmaps.  A file system contains several
-+ * blocks groups.  Each group contains 1 bitmap block for blocks, 1 bitmap
-+ * block for inodes, N blocks for the inode table and data blocks.
-+ *
-+ * The file system contains group descriptors which are located after the
-+ * super block.  Each descriptor contains the number of the bitmap block and
-+ * the free blocks count in the block.  The descriptors are loaded in memory
-+ * when a file system is mounted (see xip2_read_super).
-+ */
++static inline int check_vtimer(struct vtimer_list *timer)
++{
++	if (timer->magic != VTIMER_MAGIC)
++		return -EINVAL;
++	return 0;
++}
 +
-+unsigned long xip2_count_free_inodes (struct super_block * sb)
++static inline int vtimer_pending(struct vtimer_list *timer)
 +{
-+	return le32_to_cpu(sb->u.xip2_sb.s_es->s_free_inodes_count);
++	return (!list_empty(&timer->entry));
 +}
-=== fs/xip2fs/symlink.c
-==================================================================
---- fs/xip2fs/symlink.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/xip2fs/symlink.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,66 @@
+ 
+-        /* init CPU timer */
+-        init_cpu_timer();
 +/*
-+ *  linux/fs/xip2/symlink.c, Version 1
-+ *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
++ * this function should only run on the specified CPU
 + */
++static void internal_add_vtimer(struct vtimer_list *timer)
++{
++	unsigned long flags;
++	__u64 done;
++	struct vtimer_list *event;
++	struct vtimer_queue *vt_list;
 +
-+#include <linux/fs.h>
-+#include <linux/xip2_fs.h>
-+#include <linux/mm.h>
++	vt_list = &virt_cpu_timer[timer->cpu];
++	spin_lock_irqsave(&vt_list->lock, flags);
 +
-+static char *xip2_getlink(struct dentry * dentry)
-+{
-+	char *res;
-+	unsigned long blockno;
++	if (timer->cpu != smp_processor_id())
++		printk("internal_add_vtimer: BUG, running on wrong CPU");
 +
-+	if (xip2_get_block(dentry->d_inode, 0, &blockno, 0)) {
-+		printk ("XIP2-FS: could not resolve symbolic link\n");
-+		return NULL;
++	/* if list is empty we only have to set the timer */
++	if (list_empty(&vt_list->list)) {
++		/* reset the offset, this may happen if the last timer was 
++		 * just deleted by mod_virt_timer and the interrupt
++		 * didn't happen until here
++		 */
++		vt_list->offset = 0;
++		goto fire;
 +	}
-+	res = (char*) xip2_maread (dentry->d_inode->i_sb->u.xip2_sb.mem_area,
-+			blockno, PAGE_SIZE);
-+        if (!res)
-+               return (char*)empty_zero_page;
-+        return (char*)res;
-+}
 +
++	/* save progress */
++	asm volatile ("STPT %0" : "=m" (done));
 +
-+static int xip2_readlink(struct dentry *dentry, char *buffer, int buflen)
-+{
-+        char *s = xip2_getlink(dentry);
-+        int res = vfs_readlink(dentry,buffer,buflen,s);
++	/* calculate completed work */
++	done = vt_list->to_expire - done + vt_list->offset;
++	vt_list->offset = 0;
 +
-+        return res;
-+}
++	list_for_each_entry(event, &vt_list->list, entry)
++		event->expires -= done;
 +
-+static int xip2_follow_link(struct dentry *dentry, struct nameidata *nd)
-+{
-+        char *s = xip2_getlink(dentry);
-+        int res = vfs_follow_link(nd, s);
++ fire:
++	list_add_sorted(timer, &vt_list->list);
 +
-+        return res;
-+}
++	/* get first element, which is the next vtimer slice */
++	event = list_entry(vt_list->list.next, struct vtimer_list, entry);
 +
-+static int xip2_fast_readlink(struct dentry *dentry, char *buffer, int buflen)
-+{
-+	char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
-+	return vfs_readlink(dentry, buffer, buflen, s);
++	set_vtimer(event->expires);
++	spin_unlock_irqrestore(&vt_list->lock, flags);
 +}
 +
-+static int xip2_fast_follow_link(struct dentry *dentry, struct nameidata *nd)
++static inline int prepare_vtimer(struct vtimer_list *timer)
 +{
-+	char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
-+	return vfs_follow_link(nd, s);
-+}
++	if (check_vtimer(timer) || !timer->function) {
++		printk("add_virt_timer: uninitialized timer\n");
++		return -EINVAL;
++	}
 +
-+struct inode_operations xip2_fast_symlink_inode_operations = {
-+	readlink:	xip2_fast_readlink,
-+	follow_link:	xip2_fast_follow_link,
-+};
++	if (!timer->expires || timer->expires > VTIMER_MAX_SLICE) {
++		printk("add_virt_timer: invalid timer expire value!\n");
++		return -EINVAL;
++	}
++
++	if (vtimer_pending(timer)) {
++		printk("add_virt_timer: timer pending\n");
++		return -EBUSY;
++	}
++
++	timer->cpu = smp_processor_id();
++	return 0;
++}
 +
-+struct inode_operations xip2_symlink_inode_operations = {
-+	readlink:	xip2_readlink,
-+	follow_link:	xip2_follow_link,
-+};
-=== fs/xip2fs/namei.c
-==================================================================
---- fs/xip2fs/namei.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/xip2fs/namei.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,38 @@
 +/*
-+ *  linux/fs/xip2/namei.c, Version 1
-+ *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
++ * add_virt_timer - add an oneshot virtual CPU timer
 + */
++void add_virt_timer(void *new)
++{
++	struct vtimer_list *timer;
 +
-+#include <linux/fs.h>
-+#include <linux/xip2_fs.h>
-+#include <linux/pagemap.h>
++	timer = (struct vtimer_list *)new;
++
++	if (prepare_vtimer(timer) < 0)
++		return;
++
++	timer->interval = 0;
++	internal_add_vtimer(timer);
++}
 +
 +/*
-+ * Methods themselves.
++ * add_virt_timer_int - add an interval virtual CPU timer
 + */
-+
-+static struct dentry *xip2_lookup(struct inode * dir, struct dentry *dentry)
++void add_virt_timer_periodic(void *new)
 +{
-+	struct inode * inode;
-+	ino_t ino;
-+	
-+	if (dentry->d_name.len > EXT2_NAME_LEN)
-+		return ERR_PTR(-ENAMETOOLONG);
++	struct vtimer_list *timer;
 +
-+	ino = xip2_inode_by_name(dir, dentry);
-+	inode = NULL;
-+	if (ino) {
-+		inode = iget(dir->i_sb, ino);
-+		if (!inode) 
-+			return ERR_PTR(-EACCES);
++	timer = (struct vtimer_list *)new;
++
++	if (prepare_vtimer(timer) < 0)
++		return;
++
++	timer->interval = timer->expires;
++	internal_add_vtimer(timer);
++}
++
++/* 
++ * If we change a pending timer the function must be called on the CPU 
++ * where the timer is running on, e.g. by smp_call_function_on()
++ *
++ * The original mod_timer adds the timer if it is not pending. For compatibility
++ * we do the same. The timer will be added on the current CPU as a oneshot timer.
++ * 
++ * returns whether it has modified a pending timer (1) or not (0)
++ */
++int mod_virt_timer(struct vtimer_list *timer, __u64 expires)
++{
++	struct vtimer_queue *vt_list;
++	unsigned long flags;
++
++	if (check_vtimer(timer) || !timer->function) {
++		printk("mod_virt_timer: uninitialized timer\n");
++		return	-EINVAL;
 +	}
-+	d_add(dentry, inode);
-+	return NULL;
++
++	if (!expires || expires > VTIMER_MAX_SLICE) {
++		printk("mod_virt_timer: invalid expire range\n");
++		return -EINVAL;
++	}
++
++	/*
++	 * This is a common optimization triggered by the
++	 * networking code - if the timer is re-modified
++	 * to be the same thing then just return:
++	 */
++	if (timer->expires == expires && vtimer_pending(timer)) 
++		return 1;
++
++	/* disable interrupts before test if timer is pending */
++	vt_list = &virt_cpu_timer[smp_processor_id()];
++	spin_lock_irqsave(&vt_list->lock, flags);
++
++	/* if timer isn't pending add it on the current CPU */
++	if (!vtimer_pending(timer)) {
++		spin_unlock_irqrestore(&vt_list->lock, flags);
++		/* we do not activate an interval timer with mod_virt_timer */
++		timer->interval = 0;
++		timer->expires = expires;
++		timer->cpu = smp_processor_id();
++		internal_add_vtimer(timer);
++		return 0;
++	}
++
++	/* check if we run on the right CPU */
++	if (timer->cpu != smp_processor_id()) {
++		printk("mod_virt_timer: running on wrong CPU, check your code\n"); 
++		spin_unlock_irqrestore(&vt_list->lock, flags);
++		return -EINVAL;
++	}
++
++	list_del_init(&timer->entry);
++	timer->expires = expires;
++
++	/* also change the interval if we have an interval timer */
++	if (timer->interval) 
++		timer->interval = expires;
++
++	/* the timer can't expire anymore so we can release the lock */
++	spin_unlock_irqrestore(&vt_list->lock, flags);
++	internal_add_vtimer(timer);
++	return 1;
 +}
 +
-+struct inode_operations xip2_dir_inode_operations = {
-+	lookup:		xip2_lookup,
-+};
-=== fs/xip2fs/ioctl.c
-==================================================================
---- fs/xip2fs/ioctl.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/xip2fs/ioctl.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,31 @@
 +/*
-+ *  linux/fs/xip2/ioctl.c, Version 1
++ * delete a virtual timer
 + *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
++ * returns whether the deleted timer was pending (1) or not (0)
 + */
++int del_virt_timer(struct vtimer_list *timer)
++{
++	unsigned long flags;
++	struct vtimer_queue *vt_list;
 +
-+#include <linux/fs.h>
-+#include <linux/xip2_fs.h>
-+#include <linux/sched.h>
-+#include <asm/uaccess.h>
++	if (check_vtimer(timer)) {
++		printk("del_virt_timer: timer not initialized\n");
++		return -EINVAL;
++	}
 +
++	/* check if timer is pending */
++	if (!vtimer_pending(timer))
++		return 0;
 +
-+int xip2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
-+		unsigned long arg)
++	if (timer->cpu > smp_num_cpus) {
++		printk("del_virt_timer: CPU not present!\n");
++		return -1;
++	}
++
++	vt_list = &virt_cpu_timer[timer->cpu];
++	spin_lock_irqsave(&vt_list->lock, flags);
++
++	/* we don't interrupt a running timer, just let it expire! */
++	list_del_init(&timer->entry);
++
++	/* last timer removed */
++	if (list_empty(&vt_list->list)) {
++		vt_list->to_expire = 0;
++		vt_list->offset = 0;
++	}
++
++	spin_unlock_irqrestore(&vt_list->lock, flags);
++	return 1;
++}
++#endif
++
++#ifdef CONFIG_NO_IDLE_HZ
++
++/*
++ * Start the HZ tick on the current CPU.
++ * Only cpu_idle may call this function.
++ */
++void start_hz_timer(void)
 +{
-+	unsigned int flags;
++	__u64 tmp;
++	__u32 ticks;
 +
-+	xip2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
++	if (sysctl_hz_timer != 0)
++		return;
++	
++	irq_enter(smp_processor_id(), 0);
 +
-+	switch (cmd) {
-+	case EXT2_IOC_GETFLAGS:
-+		flags = inode->u.ext2_i.i_flags & EXT2_FL_USER_VISIBLE;
-+		return put_user(flags, (int *) arg);
-+	case EXT2_IOC_GETVERSION:
-+		return put_user(inode->i_generation, (int *) arg);
-+	default:
-+		return -ENOTTY;
++	/* Calculate how many ticks have passed */
++	asm volatile ("STCK 0(%0)" : : "a" (&tmp) : "memory", "cc");
++	tmp = tmp + CLK_TICKS_PER_JIFFY - S390_lowcore.jiffy_timer;
++	ticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1);
++	S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY * (__u64) ticks;
++
++	/* Set the clock comparator to the next tick. */
++	tmp = S390_lowcore.jiffy_timer + CPU_DEVIATION;
++	asm volatile ("SCKC %0" : : "m" (tmp));
++
++	/* Charge the ticks. */
++	if (ticks > 0) {
++#ifdef CONFIG_SMP
++		write_lock(&xtime_lock);
++		if (S390_lowcore.jiffy_timer > xtime_cc) {
++			__u32 xticks;
++
++			tmp = S390_lowcore.jiffy_timer - xtime_cc;
++			xticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1);
++			xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY;
++			do_timer_ticks(xticks);
++		}
++		write_unlock(&xtime_lock);
++#else
++		do_timer_ticks(0, ticks);
++#endif
++		update_process_times_us(0, ticks);
++	}
++	irq_exit(smp_processor_id(), 0);
++}	
++
++extern spinlock_t timerlist_lock;
++
++/*
++ * Stop the HZ tick on the current CPU.
++ * Only cpu_idle may call this function.
++ */
++void stop_hz_timer(void)
++{
++	__u64 timer;
++
++	if (sysctl_hz_timer != 0)
++		return;
++	
++	if (atomic_read(&active_cpu_timer) == 0) {
++		/* 
++		 * The last active cpu is going to sleep. Setup the clock
++		 * comparator for the next event if nothing on tq_timer
++		 * is pending. If something is pending on tq_timer then
++		 * don't change the clock comparator as it is setup for
++		 * the next timer tick already.
++		 */
++		if (!TQ_ACTIVE(tq_timer)) {
++			spin_lock(&timerlist_lock);
++			timer = (__u64) next_timer_event()->expires;
++			timer *= CLK_TICKS_PER_JIFFY;
++			timer += init_timer_cc;
++			asm volatile ("SCKC %0" : : "m" (timer));
++			spin_unlock(&timerlist_lock);
++		}
++	} else {
++		timer = (__u64) -1;
++		asm volatile ("SCKC %0" : : "m" (timer));
 +	}
 +}
-=== fs/xip2fs/Makefile
-==================================================================
---- fs/xip2fs/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/xip2fs/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -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...
++#endif
 +
-+O_TARGET := xip2fs.o
++void do_monitor_call(struct pt_regs *regs, long interruption_code)
++{
++	/* disable monitor call class 0 */
++	__ctl_clear_bit(8, 15);
 +
-+obj-y    := balloc.o dir.o file.o ialloc.o inode.o \
-+		ioctl.o namei.o super.o symlink.o
-+obj-m    := $(O_TARGET)
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)	
++	atomic_inc(&active_cpu_timer);
++#endif	
 +
-+include $(TOPDIR)/Rules.make
-=== fs/xip2fs/balloc.c
-==================================================================
---- fs/xip2fs/balloc.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/xip2fs/balloc.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,83 @@
++#ifdef CONFIG_VIRT_TIMER
++	start_cpu_timer();
++#endif
++#ifdef CONFIG_NO_IDLE_HZ
++	start_hz_timer();
++#endif
+ }
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/kernel/traps.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/kernel/traps.c	2006-01-30 22:26:06.000000000 -0700
+@@ -54,6 +54,9 @@
+ extern pgm_check_handler_t do_segment_exception;
+ extern pgm_check_handler_t do_page_exception;
+ extern pgm_check_handler_t do_pseudo_page_fault;
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++extern pgm_check_handler_t do_monitor_call;
++#endif
+ #ifdef CONFIG_PFAULT
+ extern int pfault_init(void);
+ extern void pfault_fini(void);
+@@ -642,7 +645,7 @@
+         int i;
+ 
+         for (i = 0; i < 128; i++)
+-          pgm_check_table[i] = &default_trap_handler;
++		pgm_check_table[i] = &default_trap_handler;
+         pgm_check_table[1] = &illegal_op;
+         pgm_check_table[2] = &privileged_op;
+         pgm_check_table[3] = &execute_exception;
+@@ -658,6 +661,9 @@
+  	pgm_check_table[0x14] = &do_pseudo_page_fault;
+         pgm_check_table[0x15] = &operand_exception;
+         pgm_check_table[0x1C] = &space_switch_exception;
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++	pgm_check_table[0x40] = &do_monitor_call;
++#endif	
+ #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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/mm/Makefile	2006-01-30 22:25:24.000000000 -0700
+@@ -9,6 +9,8 @@
+ 
+ O_TARGET := mm.o
+ 
+-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
+ 
+ include $(TOPDIR)/Rules.make
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/mm/cmm.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/mm/cmm.c	2006-01-30 22:25:24.000000000 -0700
+@@ -0,0 +1,448 @@
 +/*
-+ *  linux/fs/xip2/balloc.c, Version 1
++ *  arch/s390/mm/cmm.c
 + *
-+ * (C) Copyright IBM Corp. 2002,2004
-+ * Author(s): Carsten Otte <cotte at de.ibm.com>
-+ * derived from second extended filesystem (ext2)
++ *  S390 version
++ *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Martin Schwidefsky (schwidefsky at de.ibm.com)
++ *
++ *  Collaborative memory management interface.
 + */
 +
 +#include <linux/config.h>
++#include <linux/errno.h>
 +#include <linux/fs.h>
-+#include <linux/xip2_fs.h>
-+#include <linux/locks.h>
-+#include <linux/quotaops.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/sysctl.h>
++#include <linux/ctype.h>
 +
-+/*
-+ * balloc.c contains the blocks allocation and deallocation routines
-+ */
++#include <asm/pgalloc.h>
++#include <asm/uaccess.h>
 +
-+unsigned long xip2_count_free_blocks (struct super_block * sb)
++#include "../../../drivers/s390/net/smsgiucv.h"
++
++#define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2)
++
++struct cmm_page_array {
++	struct cmm_page_array *next;
++	unsigned long index;
++	unsigned long pages[CMM_NR_PAGES];
++};
++
++static long cmm_pages = 0;
++static long cmm_timed_pages = 0;
++static volatile long cmm_pages_target = 0;
++static volatile long cmm_timed_pages_target = 0;
++static long cmm_timeout_pages = 0;
++static long cmm_timeout_seconds = 0;
++
++static struct cmm_page_array *cmm_page_list = 0;
++static struct cmm_page_array *cmm_timed_page_list = 0;
++
++static unsigned long cmm_thread_active = 0;
++static struct tq_struct cmm_thread_starter;
++static wait_queue_head_t cmm_thread_wait;
++static struct timer_list cmm_timer;
++
++static void cmm_timer_fn(unsigned long);
++static void cmm_set_timer(void);
++
++static long
++cmm_strtoul(const char *cp, char **endp)
 +{
-+	return le32_to_cpu(sb->u.xip2_sb.s_es->s_free_blocks_count);
++	unsigned int base = 10;
++
++	if (*cp == '0') {
++		base = 8;
++		cp++;
++		if ((*cp == 'x' || *cp == 'X') && isxdigit(cp[1])) {
++			base = 16;
++			cp++;
++		}
++	}
++	return simple_strtoul(cp, endp, base);
 +}
 +
-+static inline int test_root(int a, int b)
++static long
++cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list)
 +{
-+	if (a == 0)
-+		return 1;
-+	while (1) {
-+		if (a == 1)
-+			return 1;
-+		if (a % b)
-+			return 0;
-+		a = a / b;
++	struct cmm_page_array *pa;
++	unsigned long page;
++	
++	pa = *list;
++	while (pages) {
++		page = __get_free_page(GFP_NOIO);
++		if (!page)
++			break;
++		if (!pa || pa->index >= CMM_NR_PAGES) {
++			/* Need a new page for the page list. */
++			pa = (struct cmm_page_array *)
++				__get_free_page(GFP_NOIO);
++			if (!pa) {
++				free_page(page);
++				break;
++			}
++			pa->next = *list;
++			pa->index = 0;
++			*list = pa;
++		}
++		diag10((unsigned long) page);
++		pa->pages[pa->index++] = page;
++		(*counter)++;
++		pages--;
 +	}
++	return pages;
 +}
 +
-+int xip2_group_sparse(int group)
++static void
++cmm_free_pages(long pages, long *counter, struct cmm_page_array **list)
 +{
-+	return (test_root(group, 3) || test_root(group, 5) ||
-+		test_root(group, 7));
++	struct cmm_page_array *pa;
++	unsigned long page;
++	
++	pa = *list;
++	while (pages) {
++		if (!pa || pa->index <= 0)
++			break;
++		page = pa->pages[--pa->index];
++		if (pa->index == 0) {
++			pa = pa->next;
++			free_page((unsigned long) *list);
++			*list = pa;
++		}
++		free_page(page);
++		(*counter)--;
++		pages--;
++	}
 +}
 +
-+/**
-+ *	xip_bg_has_super - number of blocks used by the superblock in group
-+ *	@sb: superblock for filesystem
-+ *	@group: group number to check
-+ *
-+ *	Return the number of blocks used by the superblock (primary or backup)
-+ *	in this group.  Currently this will be only 0 or 1.
-+ */
-+int xip2_bg_has_super(struct super_block *sb, int group)
++static int
++cmm_thread(void *dummy)
 +{
-+	if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
-+	    && !xip2_group_sparse(group))
-+		return 0;
-+	return 1;
++	int rc;
++	
++	daemonize();
++	reparent_to_init();
++	strcpy(current->comm, "cmmthread");
++	set_cpus_allowed(current, 1);
++	while (1) {
++		rc = wait_event_interruptible(cmm_thread_wait,
++			(cmm_pages != cmm_pages_target ||
++			 cmm_timed_pages != cmm_timed_pages_target));
++		if (rc == -ERESTARTSYS) {
++			/* Got kill signal. End thread. */
++			clear_bit(0, &cmm_thread_active);
++			cmm_pages_target = cmm_pages;
++			cmm_timed_pages_target = cmm_timed_pages;
++			break;
++		}
++		if (cmm_pages_target > cmm_pages) {
++			if (cmm_alloc_pages(1, &cmm_pages, &cmm_page_list))
++				cmm_pages_target = cmm_pages;
++		} else if (cmm_pages_target < cmm_pages) {
++			cmm_free_pages(1, &cmm_pages, &cmm_page_list);
++		}
++		if (cmm_timed_pages_target > cmm_timed_pages) {
++			if (cmm_alloc_pages(1, &cmm_timed_pages,
++					   &cmm_timed_page_list))
++				cmm_timed_pages_target = cmm_timed_pages;
++		} else if (cmm_timed_pages_target < cmm_timed_pages) {
++			cmm_free_pages(1, &cmm_timed_pages,
++			       	       &cmm_timed_page_list);
++		}
++		if (cmm_timed_pages > 0 && !timer_pending(&cmm_timer))
++			cmm_set_timer();
++	}
++	return 0;
 +}
 +
-+/**
-+ *	xip2_bg_num_gdb - number of blocks used by the group table in group
-+ *	@sb: superblock for filesystem
-+ *	@group: group number to check
-+ *
-+ *	Return the number of blocks used by the group descriptor table
-+ *	(primary or backup) in this group.  In the future there may be a
-+ *	different number of descriptor blocks in each group.
-+ */
-+unsigned long xip2_bg_num_gdb(struct super_block *sb, int group)
++static void
++cmm_start_thread(void)
 +{
-+	if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
-+	    && !xip2_group_sparse(group))
-+		return 0;
-+	return XIP2_SB(sb)->s_gdb_count;
++	kernel_thread(cmm_thread, 0, 0);
 +}
 +
++static void
++cmm_kick_thread(void)
++{
++	if (!test_and_set_bit(0, &cmm_thread_active))
++		schedule_task(&cmm_thread_starter);
++	wake_up(&cmm_thread_wait);
++}
 +
-+void* xip2_maread (xip2_mem_area_t* mem_area, int block, int size) {
-+	if ((block+1)*size-1 > (unsigned long)mem_area->end -
-+		(unsigned long)mem_area->start) {
-+		return NULL;
++static void
++cmm_set_timer(void)
++{
++	if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) {
++		if (timer_pending(&cmm_timer))
++			del_timer(&cmm_timer);
++		return;
 +	}
-+	return (void*)(mem_area->start + block*size);
++	if (timer_pending(&cmm_timer)) {
++		if (mod_timer(&cmm_timer, jiffies + cmm_timeout_seconds*HZ))
++			return;
++	}
++	cmm_timer.function = cmm_timer_fn;
++	cmm_timer.data = 0;
++	cmm_timer.expires = jiffies + cmm_timeout_seconds*HZ;
++	add_timer(&cmm_timer);
 +}
-=== fs/Config.in
-==================================================================
---- fs/Config.in   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ fs/Config.in   (/trunk/2.4.27)   (revision 52)
-@@ -91,6 +91,8 @@
- 
- tristate 'ROM file system support' CONFIG_ROMFS_FS
- 
-+tristate 'XIP2 execute-in-place filesystem support' CONFIG_XIP2FS 
 +
- tristate 'Second extended fs support' CONFIG_EXT2_FS
- 
- tristate 'System V/Xenix/V7/Coherent file system support' CONFIG_SYSV_FS
-=== Documentation/s390/xip2.8
-==================================================================
---- Documentation/s390/xip2.8   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ Documentation/s390/xip2.8   (/trunk/2.4.27)   (revision 52)
-@@ -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.
++static void
++cmm_timer_fn(unsigned long ignored)
++{
++	long pages;
 +
-+.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>
-=== Documentation/s390/TAPE
-==================================================================
---- Documentation/s390/TAPE   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ Documentation/s390/TAPE   (/trunk/2.4.27)   (revision 52)
-@@ -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 
--
--
-
-Property changes on: 
-___________________________________________________________________
-Name: svk:merge
- -b0378580-cad3-0310-9fc0-80b21f4f072e:/upstream/ibm/2.4.21-current:50
-
-=== Documentation/ioctl-number.txt
-==================================================================
---- Documentation/ioctl-number.txt   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ Documentation/ioctl-number.txt   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-=== Documentation/DocBook/zfcp-hba-api.tmpl
-==================================================================
---- Documentation/DocBook/zfcp-hba-api.tmpl   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ Documentation/DocBook/zfcp-hba-api.tmpl   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,747 @@
-+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.1//EN"[]>
++	pages = cmm_timed_pages_target - cmm_timeout_pages;
++	if (pages < 0)
++		cmm_timed_pages_target = 0;
++	else
++		cmm_timed_pages_target = pages;
++	cmm_kick_thread();
++	cmm_set_timer();
++}
 +
-+<!--  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". -->
++void
++cmm_set_pages(long pages)
++{
++	cmm_pages_target = pages;
++	cmm_kick_thread();
++}
 +
-+<book id="ZFCPHBAAPI">
++long
++cmm_get_pages(void)
++{
++	return cmm_pages;
++}
++ 
++void
++cmm_add_timed_pages(long pages)
++{
++	cmm_timed_pages_target += pages;
++	cmm_kick_thread();
++}
 +
-+<!-- book header -->
-+  <bookinfo>
-+    <title>ZFCP HBA API Kernel Interfaces</title>
++long
++cmm_get_timed_pages(void)
++{
++	return cmm_timed_pages;
++}
 +
-+    <revhistory>
-+      <revision>
-+	<revnumber>v1.0 </revnumber>
-+	<date>2003/09/24</date>
-+	<authorinitials>AH</authorinitials>
-+	<revremark>
-+	  <remark>Initial version.</remark>
-+	</revremark>
-+      </revision>
-+      <revision>
-+	<revnumber>v1.1 </revnumber>
-+	<date>2003/11/14</date>
-+	<authorinitials>AH</authorinitials>
-+	<revremark>
-+	  <remark>Completed interface description for module
-+	    <filename>zfcp_hbaapi</filename>.
-+	  </remark>
-+<!--     Added Stefan V&ouml;lkel as author. -->
-+<!--     Removed improper license statement regarding ZFCP HBA API Library. -->
-+<!--     Added Introduction and Bibliography. -->
-+<!--     Added section about misc device provided by module zfcp_hbaapi. -->
-+<!--     Added section about callback functions of module zfcp_hbaapi. -->
-+	</revremark>
-+      </revision>
-+      <revision>
-+	<revnumber>v1.2 </revnumber>
-+	<date>2003/11/19</date>
-+	<authorinitials>AH</authorinitials>
-+	<revremark>
-+	  <remark>Completed interface description for module
-+	    <filename>zfcp</filename>.
-+	  </remark>
-+<!--     Added section about intra-kernel interfaces of module zfcp. -->
-+<!--     Added section about callbacks and hooks in module zfcp. -->
-+	</revremark>
-+      </revision>
-+    </revhistory>
++void
++cmm_set_timeout(long pages, long seconds)
++{
++	cmm_timeout_pages = pages;
++	cmm_timeout_seconds = seconds;
++	cmm_set_timer();
++}
 +
-+    <authorgroup>
-+      <author>
-+	<firstname>Andreas</firstname>
-+	<surname>Herrman</surname>
-+	<affiliation>
-+	  <address><email>aherrman at de.ibm.com</email></address>
-+	</affiliation>
-+      </author>
-+      <author>
-+	<firstname>Stefan</firstname>
-+	<surname>V&ouml;lkel</surname>
-+	<affiliation>
-+	  <address><email>Stefan.Voelkel at millenux.com</email></address>
-+	</affiliation>
-+      </author>
-+    </authorgroup>
++static inline int
++cmm_skip_blanks(char *cp, char **endp)
++{
++	char *str;
 +
-+    <copyright>
-+      <year>2003</year>
-+      <holder>IBM Corp.</holder>
-+    </copyright>
++	for (str = cp; *str == ' ' || *str == '\t'; str++);
++	*endp = str;
++	return str != cp;
++}
 +
-+    <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>
++#ifdef CONFIG_CMM_PROC
++/* These will someday get removed. */
++#define VM_CMM_PAGES		1111
++#define VM_CMM_TIMED_PAGES	1112
++#define VM_CMM_TIMEOUT		1113
 +
-+    <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>
++static struct ctl_table cmm_table[];
 +
-+ <toc></toc>
++static int
++cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
++		  void *buffer, size_t *lenp)
++{
++	char buf[16], *p;
++	long pages;
++	int len;
 +
-+  <!-- 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>
++	if (!*lenp || (filp->f_pos && !write)) {
++		*lenp = 0;
++		return 0;
++	}
 +
-+  <!-- 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>
++	if (write) {
++		len = *lenp;
++		if (copy_from_user(buf, buffer,
++				   len > sizeof(buf) ? sizeof(buf) : len))
++			return -EFAULT;
++		buf[sizeof(buf) - 1] = '\0';
++		cmm_skip_blanks(buf, &p);
++		pages = cmm_strtoul(p, &p);
++		if (ctl == &cmm_table[0])
++			cmm_set_pages(pages);
++		else
++			cmm_add_timed_pages(pages);
++	} else {
++		if (ctl == &cmm_table[0])
++			pages = cmm_get_pages();
++		else
++			pages = cmm_get_timed_pages();
++		len = sprintf(buf, "%ld\n", pages);
++		if (len > *lenp)
++			len = *lenp;
++		if (copy_to_user(buffer, buf, len))
++			return -EFAULT;
++	}
++	*lenp = len;
++	filp->f_pos += len;
++	return 0;
++}
 +
-+  <!-- 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>
++static int
++cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
++		    void *buffer, size_t *lenp)
++{
++	char buf[64], *p;
++	long pages, seconds;
++	int len;
 +
-+    <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 (!*lenp || (filp->f_pos && !write)) {
++		*lenp = 0;
++		return 0;
++	}
 +
-+  <!-- 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>
++	if (write) {
++		len = *lenp;
++		if (copy_from_user(buf, buffer,
++				   len > sizeof(buf) ? sizeof(buf) : len))
++			return -EFAULT;
++		buf[sizeof(buf) - 1] = '\0';
++		cmm_skip_blanks(buf, &p);
++		pages = cmm_strtoul(p, &p);
++		cmm_skip_blanks(p, &p);
++		seconds = cmm_strtoul(p, &p);
++		cmm_set_timeout(pages, seconds);
++	} else {
++		len = sprintf(buf, "%ld %ld\n",
++			      cmm_timeout_pages, cmm_timeout_seconds);
++		if (len > *lenp)
++			len = *lenp;
++		if (copy_to_user(buffer, buf, len))
++			return -EFAULT;
++	}
++	*lenp = len;
++	filp->f_pos += len;
++	return 0;
++}
 +
-+</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:
-+-->
-=== Documentation/DocBook/Makefile
-==================================================================
---- Documentation/DocBook/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ Documentation/DocBook/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -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,7 +159,15 @@
- 	$(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
- 
-+zfcp-hba-api.sgml: zfcp-hba-api.tmpl $(ZFCPHBAAPISOURCES)
-+	$(TOPDIR)/scripts/docgen   $(ZFCPHBAAPISOURCES) \
-+		<zfcp-hba-api.tmpl >zfcp-hba-api.sgml
++static struct ctl_table cmm_table[] = {
++	{
++		.ctl_name	= VM_CMM_PAGES,
++		.procname	= "cmm_pages",
++		.mode		= 0600,
++		.proc_handler	= &cmm_pages_handler,
++	},
++	{
++		.ctl_name	= VM_CMM_TIMED_PAGES,
++		.procname	= "cmm_timed_pages",
++		.mode		= 0600,
++		.proc_handler	= &cmm_pages_handler,
++	},
++	{
++		.ctl_name	= VM_CMM_TIMEOUT,
++		.procname	= "cmm_timeout",
++		.mode		= 0600,
++		.proc_handler	= &cmm_timeout_handler,
++	},
++	{ .ctl_name = 0 }
++};
 +
- DVI	:=	$(patsubst %.sgml, %.dvi, $(BOOKS))
- AUX	:=	$(patsubst %.sgml, %.aux, $(BOOKS))
- TEX	:=	$(patsubst %.sgml, %.tex, $(BOOKS))
-=== Documentation/Configure.help
-==================================================================
---- Documentation/Configure.help   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ Documentation/Configure.help   (/trunk/2.4.27)   (revision 52)
-@@ -6283,6 +6283,16 @@
- 
-   It is safe to say N here for now.
- 
-+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).
++static struct ctl_table cmm_dir_table[] = {
++	{
++		.ctl_name	= CTL_VM,
++		.procname	= "vm",
++		.maxlen		= 0,
++		.mode		= 0555,	
++		.child		= cmm_table,
++	},
++	{ .ctl_name = 0 }
++};
++#endif
 +
-+  Only say yes on IBM zSeries or S/390 systems.
++#ifdef CONFIG_CMM_IUCV
++#define SMSG_PREFIX "CMM"
++static void
++cmm_smsg_target(char *msg)
++{
++	long pages, seconds;
 +
- The SCTP Protocol (EXPERIMENTAL)
- CONFIG_IP_SCTP
-   Stream Control Transmission Protocol
-@@ -7953,7 +7963,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>
-@@ -7971,6 +7981,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.
++	if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg))
++		return;
++	if (strncmp(msg, "SHRINK", 6) == 0) {
++		if (!cmm_skip_blanks(msg + 6, &msg))
++			return;
++		pages = cmm_strtoul(msg, &msg);
++		cmm_skip_blanks(msg, &msg);
++		if (*msg == '\0')
++			cmm_set_pages(pages);
++	} else if (strncmp(msg, "RELEASE", 7) == 0) {
++		if (!cmm_skip_blanks(msg + 7, &msg))
++			return;
++		pages = cmm_strtoul(msg, &msg);
++		cmm_skip_blanks(msg, &msg);
++		if (*msg == '\0')
++			cmm_add_timed_pages(pages);
++	} else if (strncmp(msg, "REUSE", 5) == 0) {
++		if (!cmm_skip_blanks(msg + 5, &msg))
++			return;
++		pages = cmm_strtoul(msg, &msg);
++		if (!cmm_skip_blanks(msg, &msg))
++			return;
++		seconds = cmm_strtoul(msg, &msg);
++		cmm_skip_blanks(msg, &msg);
++		if (*msg == '\0')
++			cmm_set_timeout(pages, seconds);
++	}
++}
++#endif
 +
-+  For details please refer to the documentation provided by IBM at
-+  <http://www10.software.ibm.com/developerworks/opensource/linux390>
++struct ctl_table_header *cmm_sysctl_header;
 +
-+  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.
++static int
++cmm_init (void)
++{
++#ifdef CONFIG_CMM_PROC
++	cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1);
++#endif
++#ifdef CONFIG_CMM_IUCV
++	smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
++#endif
++	cmm_thread_starter.routine = (void *) cmm_start_thread;
++	cmm_thread_starter.data = 0;
++	init_waitqueue_head(&cmm_thread_wait);
++	init_timer(&cmm_timer);
++	return 0;
++}
 +
-+IPv6 support for qeth
-+CONFIG_QETH_IPV6
-+  If CONFIG_QETH is switched on, this option will include IPv6
-+  support in the qeth device driver.
++static void
++cmm_exit(void)
++{
++	cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
++	cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
++#ifdef CONFIG_CMM_PROC
++	unregister_sysctl_table(cmm_sysctl_header);
++#endif
++#ifdef CONFIG_CMM_IUCV
++	smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
++#endif
++}
 +
-+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.
++module_init(cmm_init);
++module_exit(cmm_exit);
 +
-+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.
++EXPORT_SYMBOL(cmm_set_pages);
++EXPORT_SYMBOL(cmm_get_pages);
++EXPORT_SYMBOL(cmm_add_timed_pages);
++EXPORT_SYMBOL(cmm_get_timed_pages);
++EXPORT_SYMBOL(cmm_set_timeout);
 +
-+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>
++MODULE_LICENSE("GPL");
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390/mm/dcss.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/mm/dcss.c	2006-01-30 22:25:24.000000000 -0700
+@@ -0,0 +1,504 @@
++/*
++ * File...........: arch/s390/mm/dcss.c
++ * Author(s)......: Steven Shultz <shultzss at us.ibm.com>
++ *                  Carsten Otte <cotte at de.ibm.com>
++ * Bugreports.to..: <Linux390 at de.ibm.com>
++ * thanks to Rob M van der Heij
++ * - he wrote the diag64 function
++ * (C) IBM Corporation 2002
++ */
 +
-+  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>.
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/spinlock.h>
++#include <linux/list.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/bootmem.h>
++#include <asm/page.h>
++#include <asm/ebcdic.h>
++#include <asm/errno.h>
++#include <asm/dcss.h>
++#include <asm/cpcmd.h>
++#include <linux/ctype.h>
 +
-+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).
++#define DCSS_DEBUG	/* Debug messages on/off */
 +
-+  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.
++#define DCSS_NAME "dcss"
++#ifdef DCSS_DEBUG
++#define PRINT_DEBUG(x...)	printk(KERN_DEBUG DCSS_NAME " debug:" x)
++#else
++#define PRINT_DEBUG(x...)   do {} while (0)
++#endif
++#define PRINT_INFO(x...)	printk(KERN_INFO DCSS_NAME " info:" x)
++#define PRINT_WARN(x...)	printk(KERN_WARNING DCSS_NAME " warning:" x)
++#define PRINT_ERR(x...)		printk(KERN_ERR DCSS_NAME " error:" x)
 +
-+  If unsure, say N.
 +
- SGI WD93C93 SCSI Driver
- CONFIG_SCSI_SGIWD93
-   Say Y here to support the on-board WD93C93 SCSI controller found (a)
-@@ -25334,6 +25399,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. 
++#define DCSS_LOADSHR    0x00
++#define DCSS_LOADNSR    0x04
++#define DCSS_PURGESEG   0x08
++#define DCSS_FINDSEG    0x0c
++#define DCSS_LOADNOLY   0x10
++#define DCSS_SEGEXT     0x18
++#define DCSS_QACTV      0x0c
 +
-+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.
++struct dcss_segment {
++        struct list_head list;
++        char dcss_name[8];
++        unsigned long start_addr;
++        unsigned long end;
++        atomic_t ref_count;
++        int dcss_attr;
++	int shared_attr;
++};
 +
-+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.
++static spinlock_t dcss_lock = SPIN_LOCK_UNLOCKED;
++static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list);
++extern struct {unsigned long addr, size, type} memory_chunk[16];
 +
-+CONFIG_APPLDATA_OS
-+  This option provides operating system related data to the Linux -
-+  z/VM Monitor Stream, for example, the CPU utilisation.
++/*
++ * Create the 8 bytes, ebcdic VM segment name from
++ * an ascii name.
++ */
++static void inline dcss_mkname(char *name, char *dcss_name)
++{
++        int i;
 +
-+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.
++        for (i = 0; i <= 8; i++) {
++                if (name[i] == '\0')
++                        break;
++                dcss_name[i] = toupper(name[i]);
++        };
++        for (; i <= 8; i++)
++                dcss_name[i] = ' ';
++        ASCEBC(dcss_name, 8);
++}
 +
-+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. 
++/*
++ * Perform a function on a dcss segment.
++ */
++static inline int
++dcss_diag (__u8 func, void *parameter,
++           unsigned long *ret1, unsigned long *ret2)
++{
++        unsigned long rx, ry;
++        int rc;
 +
-+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. 
++        rx = (unsigned long) parameter;
++        ry = (unsigned long) func;
++        __asm__ __volatile__(
++#ifdef CONFIG_ARCH_S390X
++                             "   sam31\n" // switch to 31 bit
++                             "   diag    %0,%1,0x64\n"
++                             "   sam64\n" // switch back to 64 bit
++#else
++                             "   diag    %0,%1,0x64\n"
++#endif
++                             "   ipm     %2\n"
++                             "   srl     %2,28\n"
++                             : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" );
++        *ret1 = rx;
++        *ret2 = ry;
++        return rc;
++}
 +
- Support for IBM-style disk-labels (S/390)
- CONFIG_S390_PARTITION
-   Enable this option to assure standard IBM labels on the DASDs.
-@@ -25344,13 +25452,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
-@@ -25376,6 +25484,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".
 +
- 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
-@@ -25403,45 +25519,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.
-+Support for SCLP
-+CONFIG_SCLP
-+  Include support for the IBM SCLP interface to the service element. 
- 
--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 line mode terminal
-+CONFIG_SCLP_TTY
-+  Include support for IBM SCLP line-mode terminals.
++/* use to issue "extended" dcss query */
++static inline int
++dcss_diag_query(char *name, int *rwattr, int *shattr, unsigned long *segstart, unsigned long *segend)
++{
++        int i,j,rc;
++        unsigned long  rx, ry;
 +
-+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.
-+Support for SCLP VT220-compatible terminal
-+CONFIG_SCLP_VT220_TTY
-+  Include support for an IBM SCLP VT220-compatible terminal.
- 
--  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 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.
++        typedef struct segentry {
++                char thisseg[8];
++        } segentry;
 +
- 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
-@@ -25451,26 +25571,16 @@
-   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,
-+  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.
- 
--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,
--  this hardware driver cannot be built-in but is only available
--  as a module.
--  It is safe to say "Y" here.
--
- CTC device support
- CONFIG_CTC
-   Select this option if you want to use channel-to-channel networking
-@@ -25486,8 +25596,12 @@
-   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.
++        struct qout64 {
++                int segstart;
++                int segend;
++                int segcnt;
++                int segrcnt;
++                segentry segout[6];
++        };
 +
- Fast IRQ handling
- CONFIG_FAST_IRQ
-   Select this option in order to get the interrupts processed faster
-@@ -25496,12 +25610,38 @@
-   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.
- 
-+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".
++        struct qin64 {
++                char qopcode;
++                char rsrv1[3];
++                char qrcode;
++                char rsrv2[3];
++                char qname[8];
++                unsigned int qoutptr;
++                short int qoutlen;
++        };
 +
-+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.
 +
-+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. 
++        struct qin64  *qinarea;
++        struct qout64 *qoutarea;
 +
- Process warning machine checks
- CONFIG_MACHCHK_WARNING
-   Select this option if you want the machine check handler on IBM S/390 or 
-@@ -25530,6 +25670,20 @@
-   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
-+  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".
++        qinarea = (struct qin64*) get_free_page (GFP_DMA);
++        if (!qinarea) {
++                rc =-ENOMEM;
++                goto out;
++        }
++        qoutarea = (struct qout64*) get_free_page (GFP_DMA);
++        if (!qoutarea) {
++                rc = -ENOMEM;
++                free_page (qinarea);
++                goto out;
++        }
++        memset (qinarea,0,PAGE_SIZE);
++        memset (qoutarea,0,PAGE_SIZE);
 +
-+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".
++        qinarea->qopcode = DCSS_QACTV; /* do a query for active
++                                          segments */
++        qinarea->qoutptr = (unsigned long) qoutarea;
++        qinarea->qoutlen = sizeof(struct qout64);
 +
- Kernel support for 31 bit ELF binaries
- CONFIG_S390_SUPPORT
-   Select this option if you want to enable your system kernel to
-@@ -25537,6 +25691,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".
++        /* Move segment name into double word aligned
++           field and pad with blanks to 8 long.
++         */
 +
- Channel Device Configuration
- CONFIG_CHANDEV
-   The channel device layer is a layer to provide a consistent
-@@ -25573,6 +25736,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.
++        for (i = j = 0 ; i < 8; i++) {
++                qinarea->qname[i] = (name[j] == '\0') ? ' ' : name[j++];
++        }
 +
-+  For details please refer to the documentation provided by IBM at
-+  <http://www10.software.ibm.com/developerworks/opensource/linux390>.
++        /* name already in EBCDIC */
++        /* ASCEBC ((void *)&qinarea.qname, 8); */
 +
-+  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.
++        /* set the assembler variables */
++        rx = (unsigned long) qinarea;
++        ry = DCSS_SEGEXT; /* this is extended function */
 +
- SAB3036 tuner support
- CONFIG_TUNER_3036
-   Say Y here to include support for Philips SAB3036 compatible tuners.
-=== mm/memory.c
-==================================================================
---- mm/memory.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ mm/memory.c   (/trunk/2.4.27)   (revision 52)
-@@ -163,6 +163,86 @@
- #define PMD_TABLE_MASK	((PTRS_PER_PMD-1) * sizeof(pmd_t))
- 
- /*
-+ * Allocate page middle directory.
-+ *
-+ * We've already handled the fast-path in-line, and we own the
-+ * page table lock.
-+ *
-+ * On a two-level page table, this ends up actually being entirely
-+ * optimized away.
++        /* issue diagnose x'64' */
++        __asm__ __volatile__(
++#ifdef CONFIG_ARCH_S390X
++                             "   sam31\n" // switch to 31 bit
++                             "   diag    %0,%1,0x64\n"
++                             "   sam64\n" // switch back to 64 bit
++#else
++                             "   diag    %0,%1,0x64\n"
++#endif
++                             "   ipm     %2\n"
++                             "   srl     %2,28\n"
++                             : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" );
++
++        /* parse the query output area */
++	*segstart=qoutarea->segstart;
++	*segend=qoutarea->segend;
++
++        if (rc > 1)
++                {
++                        *rwattr = 2;
++                        *shattr = 2;
++                        rc = 0;
++                        goto free;
++                }
++
++        if (qoutarea->segcnt > 6)
++                {
++                        *rwattr = 3;
++                        *shattr = 3;
++                        rc = 0;
++                        goto free;
++                }
++
++        *rwattr = 1;
++        *shattr = 1;
++
++        for (i=0; i < qoutarea->segrcnt; i++) {
++                if (qoutarea->segout[i].thisseg[3] == 2 ||
++                    qoutarea->segout[i].thisseg[3] == 3 ||
++                    qoutarea->segout[i].thisseg[3] == 6 )
++                        *rwattr = 0;
++                if (qoutarea->segout[i].thisseg[3] == 1 ||
++                    qoutarea->segout[i].thisseg[3] == 3 ||
++                    qoutarea->segout[i].thisseg[3] == 5 )
++                        *shattr = 0;
++        } /* end of for statement */
++        rc = 0;
++ free:
++        free_page (qoutarea);
++        free_page (qinarea);
++ out:
++        return rc;
++}
++
++/*
++ * Load a DCSS segment via the diag 0x64.
 + */
-+pmd_t *__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
++int segment_load(char *name, int segtype, unsigned long *addr,
++                 unsigned long *end)
 +{
-+	pmd_t *new;
++        char dcss_name[8];
++        struct list_head *l;
++        struct dcss_segment *seg, *tmp;
++	unsigned long dummy;
++	unsigned long segstart, segend;
++        int rc = 0,i;
++        int initrc = 0;
++        int rwattr, shattr;
 +
-+	/* "fast" allocation can happen without dropping the lock.. */
-+	new = pmd_alloc_one_fast(mm, address);
-+	if (!new) {
-+		spin_unlock(&mm->page_table_lock);
-+		new = pmd_alloc_one(mm, address);
-+		spin_lock(&mm->page_table_lock);
-+		if (!new)
-+			return NULL;
++        if (!MACHINE_IS_VM)
++                return -ENOSYS;
++        dcss_mkname(name, dcss_name);
++	/* search for the dcss in list of currently loaded segments */
++        spin_lock(&dcss_lock);
++        seg = NULL;
++        list_for_each(l, &dcss_list) {
++                tmp = list_entry(l, struct dcss_segment, list);
++                if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) {
++                        seg = tmp;
++                        break;
++                }
++        }
 +
-+		/*
-+		 * Because we dropped the lock, we should re-check the
-+		 * entry, as somebody else could have populated it..
-+		 */
-+		if (!pgd_none(*pgd)) {
-+			pmd_free(new);
-+			check_pgt_cache();
++        if (seg == NULL) {
++                /* find out the attributes of this
++                   shared segment */
++                dcss_diag_query(dcss_name, &rwattr, &shattr, &segstart, &segend);
++		/* does segment collide with main memory? */
++		for (i=0; i<16; i++) {
++					if (memory_chunk[i].type != 0)
++						continue;
++					if (memory_chunk[i].addr > segend)
++						continue;
++					if (memory_chunk[i].addr + memory_chunk[i].size <= segstart)
++						continue;
++					spin_unlock(&dcss_lock);
++				        return -ENOENT;
++				}
++		/* or does it collide with other (loaded) segments? */
++        	list_for_each(l, &dcss_list) {
++                	tmp = list_entry(l, struct dcss_segment, list);
++	                if ((segstart <= tmp->end && segstart >= tmp->start_addr) ||
++				(segend <= tmp->end && segend >= tmp->start_addr) ||
++				(segstart <= tmp->start_addr && segend >= tmp->end)) {
++				PRINT_ERR("Segment Overlap!\n");
++			        spin_unlock(&dcss_lock);
++				return -ENOENT;
++	                }
++        	}
++
++                /* do case statement on segtype */
++                /* if asking for shared ro,
++                   shared rw works */
++                /* if asking for exclusive ro,
++                   exclusive rw works */
++
++                switch(segtype) {
++                case SEGMENT_SHARED_RO:
++                        if (shattr > 1 || rwattr > 1) {
++                                spin_unlock(&dcss_lock);
++                                return -ENOENT;
++                        } else {
++                                if (shattr == 0 && rwattr == 0)
++                                        rc = SEGMENT_EXCLUSIVE_RO;
++                                if (shattr == 0 && rwattr == 1)
++                                        rc = SEGMENT_EXCLUSIVE_RW;
++                                if (shattr == 1 && rwattr == 0)
++                                        rc = SEGMENT_SHARED_RO;
++                                if (shattr == 1 && rwattr == 1)
++                                        rc = SEGMENT_SHARED_RW;
++                        }
++                        break;
++                case SEGMENT_SHARED_RW:
++                        if (shattr > 1 || rwattr != 1) {
++                                spin_unlock(&dcss_lock);
++                                return -ENOENT;
++                        } else {
++                                if (shattr == 0)
++                                        rc = SEGMENT_EXCLUSIVE_RW;
++                                if (shattr == 1)
++                                        rc = SEGMENT_SHARED_RW;
++                        }
++                        break;
++
++                case SEGMENT_EXCLUSIVE_RO:
++                        if (shattr > 0 || rwattr > 1) {
++                                spin_unlock(&dcss_lock);
++                                return -ENOENT;
++                        } else {
++                                if (rwattr == 0)
++                                        rc = SEGMENT_EXCLUSIVE_RO;
++                                if (rwattr == 1)
++                                        rc = SEGMENT_EXCLUSIVE_RW;
++                        }
++                        break;
++
++                case SEGMENT_EXCLUSIVE_RW:
++/*                        if (shattr != 0 || rwattr != 1) {
++                                spin_unlock(&dcss_lock);
++                                return -ENOENT;
++                        } else {
++*/
++                                rc = SEGMENT_EXCLUSIVE_RW;
++//                        }
++                        break;
++
++                default:
++                        spin_unlock(&dcss_lock);
++                        return -ENOENT;
++                } /* end switch */
++
++                seg = kmalloc(sizeof(struct dcss_segment), GFP_DMA);
++                if (seg != NULL) {
++                        memcpy(seg->dcss_name, dcss_name, 8);
++			if (rc == SEGMENT_EXCLUSIVE_RW) {
++				if (dcss_diag(DCSS_LOADNSR, seg->dcss_name,
++						&seg->start_addr, &seg->end) == 0) {
++					if (seg->end < max_low_pfn*PAGE_SIZE ) {
++						atomic_set(&seg->ref_count, 1);
++						list_add(&seg->list, &dcss_list);
++						*addr = seg->start_addr;
++						*end = seg->end;
++						seg->dcss_attr = rc;
++						if (shattr == 1 && rwattr == 1)
++							seg->shared_attr = SEGMENT_SHARED_RW;
++						else if (shattr == 1 && rwattr == 0)
++							seg->shared_attr = SEGMENT_SHARED_RO;
++						else
++							seg->shared_attr = SEGMENT_EXCLUSIVE_RW;
++					} else {
++						dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
++						kfree (seg);
++						rc = -ENOENT;
++					}
++				} else {
++					kfree(seg);
++					rc = -ENOENT;
++			        }
++				goto out;
++                        } 
++			if (dcss_diag(DCSS_LOADNOLY, seg->dcss_name,
++                                      &seg->start_addr, &seg->end) == 0) {
++				if (seg->end < max_low_pfn*PAGE_SIZE ) {
++		                        atomic_set(&seg->ref_count, 1);
++					list_add(&seg->list, &dcss_list);
++					*addr = seg->start_addr;
++					*end = seg->end;
++					seg->dcss_attr = rc;
++					seg->shared_attr = rc;
++				} else {
++					dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
++					kfree (seg);
++					rc = -ENOENT;
++				}
++                        } else {
++                                kfree(seg);
++                                rc = -ENOENT;
++                        }
++                } else rc = -ENOMEM;
++        } else {
++		/* found */
++		if ((segtype == SEGMENT_EXCLUSIVE_RW) && (seg->dcss_attr != SEGMENT_EXCLUSIVE_RW)) {
++			PRINT_ERR("Segment already loaded in other mode than EXCLUSIVE_RW!\n");
++			rc = -EPERM;
 +			goto out;
++			/* reload segment in exclusive mode */
++/*			dcss_diag(DCSS_LOADNSR, seg->dcss_name,
++				  &seg->start_addr, &seg->end);
++			seg->dcss_attr = SEGMENT_EXCLUSIVE_RW;*/
 +		}
-+	}
-+#if defined(CONFIG_ARCH_S390X)
-+	new = pgd_populate(mm, pgd, new);
-+	if (!new)
-+		return NULL;
-+#else
-+	pgd_populate(mm, pgd, new);
-+#endif
++		if ((segtype != SEGMENT_EXCLUSIVE_RW) && (seg->dcss_attr == SEGMENT_EXCLUSIVE_RW)) {
++			PRINT_ERR("Segment already loaded in EXCLUSIVE_RW mode!\n");
++			rc = -EPERM;
++			goto out;
++		}
++                atomic_inc(&seg->ref_count);
++                *addr = seg->start_addr;
++                *end = seg->end;
++                rc = seg->dcss_attr;
++        }
 +out:
-+	return pmd_offset(pgd, address);
++        spin_unlock(&dcss_lock);
++        return rc;
 +}
 +
 +/*
-+ * Allocate the page table directory.
-+ *
-+ * We've already handled the fast-path in-line, and we own the
-+ * page table lock.
++ * Decrease the use count of a DCSS segment and remove
++ * it from the address space if nobody is using it
++ * any longer.
 + */
-+inline pte_t *
-+pte_alloc(struct mm_struct *mm, pmd_t *pmd, unsigned long address)
++void segment_unload(char *name)
 +{
-+	if (pmd_none(*pmd)) {
-+		pte_t *new;
-+
-+		/* "fast" allocation can happen without dropping the lock.. */
-+		new = pte_alloc_one_fast(mm, address);
-+		if (!new) {
-+			spin_unlock(&mm->page_table_lock);
-+			new = pte_alloc_one(mm, address);
-+			spin_lock(&mm->page_table_lock);
-+			if (!new)
-+				return NULL;
++        char dcss_name[8];
++        unsigned long dummy;
++        struct list_head *l,*l_tmp;
++        struct dcss_segment *seg;
 +
-+			/*
-+			 * Because we dropped the lock, we should re-check the
-+			 * entry, as somebody else could have populated it..
-+			 */
-+			if (!pmd_none(*pmd)) {
-+				pte_free(new);
-+				check_pgt_cache();
-+				goto out;
-+			}
-+		}
-+		pmd_populate(mm, pmd, new);
-+	}
-+out:
-+	return pte_offset(pmd, address);
++        if (!MACHINE_IS_VM)
++                return;
++        dcss_mkname(name, dcss_name);
++        spin_lock(&dcss_lock);
++        list_for_each_safe(l, l_tmp, &dcss_list) {
++                seg = list_entry(l, struct dcss_segment, list);
++                if (memcmp(seg->dcss_name, dcss_name, 8) == 0) {
++                        if (atomic_dec_return(&seg->ref_count) == 0) {
++                                /* Last user of the segment is
++                                   gone. */
++                                list_del(&seg->list);
++                                dcss_diag(DCSS_PURGESEG, seg->dcss_name,
++                                          &dummy, &dummy);
++				kfree(seg);
++                        }
++                        break;
++                }
++        }
++        spin_unlock(&dcss_lock);
 +}
 +
 +/*
-  * copy one vm_area from one task to the other. Assumes the page tables
-  * already present in the new task to be cleared in the whole range
-  * covered by this vma.
-@@ -422,9 +502,14 @@
- 
- 	pte = *ptep;
- 	if (pte_present(pte)) {
--		if (!write ||
--		    (pte_write(pte) && pte_dirty(pte)))
--			return pte_page(pte);
-+		struct page * page = pte_page(pte);
-+		if (!write)
-+			return page;
-+		if (pte_write(pte)) {
-+			if (!pte_dirty(pte) && !PageDirty(page))
-+				set_page_dirty(page);
-+			return page;
-+		}
- 	}
- 
- out:
-@@ -899,20 +984,6 @@
- 	return error;
- }
- 
--/*
-- * Establish a new mapping:
-- *  - flush the old one
-- *  - update the page tables
-- *  - inform the TLB about the new one
-- *
-- * We hold the mm semaphore for reading and vma->vm_mm->page_table_lock
-- */
--static inline void establish_pte(struct vm_area_struct * vma, unsigned long address, pte_t *page_table, pte_t entry)
--{
--	set_pte(page_table, entry);
--	flush_tlb_page(vma, address);
--	update_mmu_cache(vma, address, entry);
--}
- 
- /*
-  * We hold the mm semaphore for reading and vma->vm_mm->page_table_lock
-@@ -922,7 +993,8 @@
- {
- 	flush_page_to_ram(new_page);
- 	flush_cache_page(vma, address);
--	establish_pte(vma, address, page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
-+	ptep_establish(vma, address, page_table,
-+		pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
- }
- 
- /*
-@@ -959,7 +1031,8 @@
- 		unlock_page(old_page);
- 		if (reuse) {
- 			flush_cache_page(vma, address);
--			establish_pte(vma, address, page_table, pte_mkyoung(pte_mkdirty(pte_mkwrite(pte))));
-+			ptep_establish(vma, address, page_table,
-+				pte_mkyoung(pte_mkdirty(pte_mkwrite(pte))));
- 			spin_unlock(&mm->page_table_lock);
- 			return 1;	/* Minor fault */
- 		}
-@@ -1354,7 +1427,7 @@
- 		entry = pte_mkdirty(entry);
- 	}
- 	entry = pte_mkyoung(entry);
--	establish_pte(vma, address, pte, entry);
-+	ptep_establish(vma, address, pte, entry);
- 	spin_unlock(&mm->page_table_lock);
- 	return 1;
- }
-@@ -1387,79 +1460,6 @@
- 	return -1;
- }
++ * Replace an existing DCSS segment, so that machines
++ * that load it anew will see the new version.
++ */
++void segment_replace(char *name)
++{
++        char dcss_name[8];
++        struct list_head *l;
++        struct dcss_segment *seg;
++        int mybeg = 0;
++        int myend = 0;
++        char mybuff1[80];
++        char mybuff2[80];
++
++        if (!MACHINE_IS_VM)
++                return;
++        dcss_mkname(name, dcss_name);
++
++        memset (mybuff1, 0, sizeof(mybuff1));
++        memset (mybuff2, 0, sizeof(mybuff2));
++
++        spin_lock(&dcss_lock);
++        list_for_each(l, &dcss_list) {
++                seg = list_entry(l, struct dcss_segment, list);
++                if (memcmp(seg->dcss_name, dcss_name, 8) == 0) {
++                        mybeg = seg->start_addr >> 12;
++                        myend = (seg->end) >> 12;
++                        if (seg->shared_attr == SEGMENT_EXCLUSIVE_RW)
++                                sprintf(mybuff1, "DEFSEG %s %X-%X EW",
++                                        name, mybeg, myend);
++                        if (seg->shared_attr == SEGMENT_EXCLUSIVE_RO)
++                                sprintf(mybuff1, "DEFSEG %s %X-%X RO",
++                                        name, mybeg, myend);
++                        if (seg->shared_attr == SEGMENT_SHARED_RW)
++                                sprintf(mybuff1, "DEFSEG %s %X-%X SW",
++                                        name, mybeg, myend);
++                        if (seg->shared_attr == SEGMENT_SHARED_RO)
++                                sprintf(mybuff1, "DEFSEG %s %X-%X SR",
++                                        name, mybeg, myend);
++                        spin_unlock(&dcss_lock);
++                        sprintf(mybuff2, "SAVESEG %s", name);
++                        cpcmd(mybuff1, NULL, 80);
++                        cpcmd(mybuff2, NULL, 80);
++                        break;
++                }
++
++        }
++        if (myend == 0) spin_unlock(&dcss_lock);
++}
++
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390/mm/init.c	2006-01-30 22:25:24.000000000 -0700
+@@ -69,6 +69,8 @@
  
--/*
-- * Allocate page middle directory.
-- *
-- * We've already handled the fast-path in-line, and we own the
-- * page table lock.
-- *
-- * On a two-level page table, this ends up actually being entirely
-- * optimized away.
-- */
--pmd_t *__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
--{
--	pmd_t *new;
--
--	/* "fast" allocation can happen without dropping the lock.. */
--	new = pmd_alloc_one_fast(mm, address);
--	if (!new) {
--		spin_unlock(&mm->page_table_lock);
--		new = pmd_alloc_one(mm, address);
--		spin_lock(&mm->page_table_lock);
--		if (!new)
--			return NULL;
--
--		/*
--		 * Because we dropped the lock, we should re-check the
--		 * entry, as somebody else could have populated it..
--		 */
--		if (!pgd_none(*pgd)) {
--			pmd_free(new);
--			check_pgt_cache();
--			goto out;
--		}
--	}
--	pgd_populate(mm, pgd, new);
--out:
--	return pmd_offset(pgd, address);
--}
--
--/*
-- * Allocate the page table directory.
-- *
-- * We've already handled the fast-path in-line, and we own the
-- * page table lock.
-- */
--pte_t *pte_alloc(struct mm_struct *mm, pmd_t *pmd, unsigned long address)
--{
--	if (pmd_none(*pmd)) {
--		pte_t *new;
--
--		/* "fast" allocation can happen without dropping the lock.. */
--		new = pte_alloc_one_fast(mm, address);
--		if (!new) {
--			spin_unlock(&mm->page_table_lock);
--			new = pte_alloc_one(mm, address);
--			spin_lock(&mm->page_table_lock);
--			if (!new)
--				return NULL;
--
--			/*
--			 * Because we dropped the lock, we should re-check the
--			 * entry, as somebody else could have populated it..
--			 */
--			if (!pmd_none(*pmd)) {
--				pte_free(new);
--				check_pgt_cache();
--				goto out;
--			}
--		}
--		pmd_populate(mm, pmd, new);
--	}
--out:
--	return pte_offset(pmd, address);
--}
--
- int make_pages_present(unsigned long addr, unsigned long end)
- {
- 	int ret, len, write;
-=== mm/filemap.c
-==================================================================
---- mm/filemap.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ mm/filemap.c   (/trunk/2.4.27)   (revision 52)
-@@ -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..
-  */
--void mark_page_accessed(struct page *page)
-+inline void mark_page_accessed(struct page *page)
+ void diag10(unsigned long addr)
  {
- 	if (!PageActive(page) && PageReferenced(page)) {
- 		activate_page(page);
-@@ -2211,8 +2211,8 @@
++        if (addr >= 0x7ff00000)
++                return;
+         asm volatile ("diag %0,%0,0x10" : : "a" (addr));
+ }
  
- 	if (pte_present(pte)) {
- 		struct page *page = pte_page(pte);
--		if (VALID_PAGE(page) && !PageReserved(page) && ptep_test_and_clear_dirty(ptep)) {
--			flush_tlb_page(vma, address);
-+		if (VALID_PAGE(page) && !PageReserved(page) &&
-+                    ptep_test_and_clear_and_flush_dirty(vma, address, ptep)) {
- 			set_page_dirty(page);
- 		}
- 	}
-=== mm/vmscan.c
-==================================================================
---- mm/vmscan.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ mm/vmscan.c   (/trunk/2.4.27)   (revision 52)
-@@ -101,9 +101,7 @@
- 	 * is needed on CPUs which update the accessed and dirty
- 	 * bits in hardware.
- 	 */
--	flush_cache_page(vma, address);
--	pte = ptep_get_and_clear(page_table);
--	flush_tlb_page(vma, address);
-+	pte = ptep_invalidate(vma, address, page_table);
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/Makefile kernel-source-2.4.27-2.4.27/arch/s390x/Makefile
+--- kernel-source-2.4.27-2.4.27.orig/arch/s390x/Makefile	2002-08-02 18:39:43.000000000 -0600
++++ kernel-source-2.4.27-2.4.27/arch/s390x/Makefile	2006-01-30 22:25:24.000000000 -0700
+@@ -24,19 +24,22 @@
+ endif
+ MODFLAGS += -fpic
  
- 	if (pte_dirty(pte))
- 		set_page_dirty(page);
-=== arch/s390x/kernel/smp.c
-==================================================================
---- arch/s390x/kernel/smp.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/kernel/smp.c   (/trunk/2.4.27)   (revision 52)
-@@ -92,7 +92,7 @@
++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)
  
- extern void reipl(unsigned long devno);
+ HEAD := arch/s390x/kernel/head.o arch/s390x/kernel/init_task.o
  
--static sigp_ccode smp_ext_bitcall(int, ec_bit_sig);
-+static void smp_ext_bitcall(int, ec_bit_sig);
- static void smp_ext_bitcall_others(ec_bit_sig);
+ 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
  
- /*
-@@ -131,7 +131,7 @@
-  * in the system.
-  */
+-all: image listing
++all: image
  
--int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
-+int smp_call_function(void (*func) (void *info), void *info, int nonatomic,
- 			int wait)
- /*
-  * [SUMMARY] Run a function on all other CPUs.
-@@ -176,46 +176,91 @@
- 	return 0;
- }
+ listing: vmlinux
+ 	@$(MAKEBOOT) listing
+@@ -44,6 +47,9 @@
+ arch/s390x/kernel: dummy
+ 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390x/kernel
  
-+/*
-+ * Call a function only on one CPU
-+ * cpu : the CPU the function should be executed on
-+ *
-+ * You must not call this function with disabled interrupts or from a
-+ * hardware interrupt handler, you may call it from a bottom half handler.                                                      */
-+int smp_call_function_on(void (*func) (void *info), void *info,
-+			 int nonatomic, int wait, int cpu)
-+{
-+	struct call_data_struct data;
-+
-+	if (!atomic_read(&smp_commenced))
-+		return 0;
-+
-+	if (smp_processor_id() == cpu) {
-+		/* direct call to function */
-+		func(info);
-+		return 0;
-+	}
++arch/s390/appldata: dummy
++	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/appldata
 +
-+	data.func = func;
-+	data.info = info;
+ arch/s390x/mm: dummy
+ 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390x/mm
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/config.in kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/config.in	2006-01-30 22:25:24.000000000 -0700
+@@ -65,8 +65,34 @@
+ bool 'Show crashed user process info' CONFIG_PROCESS_DEBUG
+ bool 'Pseudo page fault support' CONFIG_PFAULT
+ bool 'VM shared kernel support' CONFIG_SHARED_KERNEL
++bool 'No HZ timer ticks in idle' CONFIG_NO_IDLE_HZ
++if [ "$CONFIG_NO_IDLE_HZ" = "y" ] ; then
++  bool '  Idle HZ timer on by default' CONFIG_NO_IDLE_HZ_INIT
++fi
++bool 'Virtual CPU timer support' CONFIG_VIRT_TIMER
++dep_bool 'Linux - VM Monitor Stream, base infrastructure' CONFIG_APPLDATA_BASE \
++$CONFIG_PROC_FS $CONFIG_VIRT_TIMER
++dep_tristate '   Monitor memory management statistics' CONFIG_APPLDATA_MEM $CONFIG_APPLDATA_BASE
++dep_tristate '   Monitor OS statistics' CONFIG_APPLDATA_OS $CONFIG_APPLDATA_BASE
++dep_tristate '   Monitor overall network statistics' CONFIG_APPLDATA_NET_SUM $CONFIG_APPLDATA_BASE
++tristate 'Collaborative memory management' CONFIG_CMM
++if [ "$CONFIG_CMM" != "n" ]; then
++  dep_bool '/proc interface to cooperative memory management' CONFIG_CMM_PROC $CONFIG_PROC_FS
++  if [ "$CONFIG_SMSGIUCV" = "y" -o "$CONFIG_SMSGIUCV" = "$CONFIG_CMM" ]; then
++    bool 'IUCV special message interface to cooperative memory management' CONFIG_CMM_IUCV
++  fi
++fi
+ endmenu
+ 
++mainmenu_option next_comment
++comment 'SCSI support'
 +
-+	atomic_set(&data.started, 0);
-+	data.wait = wait;
-+	if (wait)
-+		atomic_set(&data.finished, 0);
++tristate 'SCSI support' CONFIG_SCSI
 +
-+	spin_lock_bh(&call_lock);
-+	call_data = &data;
-+	smp_ext_bitcall(cpu, ec_call_function);
++if [ "$CONFIG_SCSI" != "n" ]; then
++   source drivers/scsi/Config.in
++fi
++endmenu
+ 
+ source drivers/s390/Config.in
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/defconfig kernel-source-2.4.27-2.4.27/arch/s390x/defconfig
+--- kernel-source-2.4.27-2.4.27.orig/arch/s390x/defconfig	2006-01-30 22:23:45.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/arch/s390x/defconfig	2006-01-30 22:25:24.000000000 -0700
+@@ -49,8 +49,8 @@
+ CONFIG_QDIO=m
+ # CONFIG_QDIO_PERF_STATS is not set
+ CONFIG_IPL=y
+-# CONFIG_IPL_TAPE is not set
+-CONFIG_IPL_VM=y
++CONFIG_IPL_TAPE=y
++# CONFIG_IPL_VM is not set
+ CONFIG_NET=y
+ CONFIG_SYSVIPC=y
+ # CONFIG_BSD_PROCESS_ACCT is not set
+@@ -61,6 +61,50 @@
+ # CONFIG_PROCESS_DEBUG is not set
+ CONFIG_PFAULT=y
+ # CONFIG_SHARED_KERNEL is not set
++# CONFIG_VIRT_TIMER is not set
++# CONFIG_APPLDATA_BASE is not set
++# CONFIG_APPLDATA_MEM is not set
++# CONFIG_APPLDATA_OS is not set
++# CONFIG_APPLDATA_NET_SUM is not set
++CONFIG_CMM=m
++CONFIG_CMM_PROC=y
++# CONFIG_CMM_IUCV is not set
 +
-+	/* Wait for response */
-+	while (atomic_read(&data.started) != 1)
-+		barrier();
++#
++# SCSI support
++#
++CONFIG_SCSI=m
 +
-+	if (wait)
-+		while (atomic_read(&data.finished) != 1)
-+			barrier();
++#
++# SCSI support type (disk, tape, CD-ROM)
++#
++CONFIG_BLK_DEV_SD=m
++CONFIG_SD_EXTRA_DEVS=1000
++CONFIG_CHR_DEV_ST=m
++# CONFIG_CHR_DEV_OSST is not set
++CONFIG_BLK_DEV_SR=m
++# CONFIG_BLK_DEV_SR_VENDOR is not set
++CONFIG_SR_EXTRA_DEVS=10
++CONFIG_CHR_DEV_SG=m
 +
-+	spin_unlock_bh(&call_lock);
-+	return 0;
-+}
++#
++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
++#
++CONFIG_SCSI_DEBUG_QUEUES=y
++CONFIG_SCSI_MULTI_LUN=y
++CONFIG_SCSI_CONSTANTS=y
++CONFIG_SCSI_LOGGING=y
 +
++#
++# SCSI low-level drivers
++#
++CONFIG_ZFCP=m
++CONFIG_ZFCP_HBAAPI=m
 +
- static inline void do_send_stop(void)
- {
--        u32 dummy;
--        int i;
-+	unsigned long dummy;
-+	int i;
- 
--        /* stop all processors */
--        for (i =  0; i < smp_num_cpus; i++) {
--                if (smp_processor_id() != i) {
--                        int ccode;
--                        do {
--                                ccode = signal_processor_ps(
--                                   &dummy,
--                                   0,
--                                   i,
--                                   sigp_stop);
--                        } while(ccode == sigp_busy);
--                }
--        }
-+	/* stop all processors */
-+	for (i =  0; i < smp_num_cpus; i++) {
-+		if (smp_processor_id() != i) {
-+			int ccode;
-+			do {
-+				ccode = signal_processor_ps(
-+				   &dummy,
-+				   0,
-+				   i,
-+				   sigp_stop);
-+			} while(ccode == sigp_busy);
-+		}
-+	}
- }
- 
- static inline void do_store_status(void)
- {
--        unsigned long low_core_addr;
--        u32 dummy;
--        int i;
-+	unsigned long low_core_addr;
-+	unsigned long dummy;
-+	int i;
- 
--        /* store status of all processors in their lowcores (real 0) */
--        for (i =  0; i < smp_num_cpus; i++) {
--                if (smp_processor_id() != i) {
--                        int ccode;
--                        low_core_addr = (unsigned long)get_cpu_lowcore(i);
--                        do {
--                                ccode = signal_processor_ps(
--                                   &dummy,
--                                   low_core_addr,
--                                   i,
--                                   sigp_store_status_at_address);
--                        } while(ccode == sigp_busy);
--                }
--        }
-+	/* store status of all processors in their lowcores (real 0) */
-+	for (i =  0; i < smp_num_cpus; i++) {
-+		if (smp_processor_id() != i) {
-+			int ccode;
-+			low_core_addr = (unsigned long)get_cpu_lowcore(i);
-+			do {
-+				ccode = signal_processor_ps(
-+				   &dummy,
-+				   low_core_addr,
-+				   i,
-+				   sigp_store_status_at_address);
-+			} while(ccode == sigp_busy);
-+		}
-+	}
- }
- 
- /*
-@@ -224,8 +269,8 @@
-  */
- void smp_send_stop(void)
- {
--        /* write magic number to zero page (absolute 0) */
--        get_cpu_lowcore(smp_processor_id())->panic_magic = __PANIC_MAGIC;
-+	/* write magic number to zero page (absolute 0) */
-+	get_cpu_lowcore(smp_processor_id())->panic_magic = __PANIC_MAGIC;
- 
- 	/* stop other processors. */
- 	do_send_stop();
-@@ -263,7 +308,7 @@
- void machine_restart_smp(char * __unused) 
- {
- 	cpu_restart_map = cpu_online_map;
--        smp_call_function(do_machine_restart, NULL, 0, 0);
-+	smp_call_function(do_machine_restart, NULL, 0, 0);
- 	do_machine_restart(NULL);
- }
++#
++# PCMCIA SCSI adapter support
++#
++# CONFIG_SCSI_PCMCIA is not set
  
-@@ -282,7 +327,7 @@
+ #
+ # Block device drivers
+@@ -71,6 +115,7 @@
+ CONFIG_BLK_DEV_RAM_SIZE=24576
+ CONFIG_BLK_DEV_INITRD=y
+ CONFIG_BLK_DEV_XPRAM=m
++CONFIG_DCSSBLK=m
  
- void machine_halt_smp(void)
- {
--        smp_call_function(do_machine_halt, NULL, 0, 0);
-+	smp_call_function(do_machine_halt, NULL, 0, 0);
- 	do_machine_halt(NULL);
- }
+ #
+ # S/390 block device drivers
+@@ -78,6 +123,7 @@
+ CONFIG_DASD=y
+ CONFIG_DASD_ECKD=y
+ CONFIG_DASD_FBA=y
++CONFIG_S390_CMF=m
  
-@@ -301,7 +346,7 @@
+ #
+ # Multi-device support (RAID and LVM)
+@@ -104,22 +150,24 @@
+ CONFIG_TN3270_CONSOLE=y
+ CONFIG_TN3215=y
+ CONFIG_TN3215_CONSOLE=y
+-CONFIG_HWC=y
+-CONFIG_HWC_CONSOLE=y
+-CONFIG_HWC_CPI=m
++CONFIG_SCLP=y
++CONFIG_SCLP_TTY=y
++CONFIG_SCLP_CONSOLE=y
++CONFIG_SCLP_VT220_TTY=y
++CONFIG_SCLP_VT220_CONSOLE=y
++CONFIG_SCLP_CPI=m
+ CONFIG_S390_TAPE=m
  
- void machine_power_off_smp(void)
- {
--        smp_call_function(do_machine_power_off, NULL, 0, 0);
-+	smp_call_function(do_machine_power_off, NULL, 0, 0);
- 	do_machine_power_off(NULL);
- }
+ #
+ # S/390 tape interface support
+ #
+-CONFIG_S390_TAPE_CHAR=y
+ CONFIG_S390_TAPE_BLOCK=y
  
-@@ -312,55 +357,52 @@
+ #
+ # S/390 tape hardware support
+ #
+-CONFIG_S390_TAPE_3490=y
+-CONFIG_S390_TAPE_3480=y
++CONFIG_S390_TAPE_34XX=m
++CONFIG_VMLOGRDR=m
  
- void do_ext_call_interrupt(struct pt_regs *regs, __u16 code)
- {
--        unsigned long bits;
-+	unsigned long bits;
+ #
+ # Network device drivers
+@@ -131,7 +179,6 @@
+ # CONFIG_TUN is not set
+ CONFIG_NET_ETHERNET=y
+ CONFIG_TR=y
+-# CONFIG_C7000 is not set
+ CONFIG_FDDI=y
  
--        /*
--         * handle bit signal external calls
--         *
--         * For the ec_schedule signal we have to do nothing. All the work
--         * is done automatically when we return from the interrupt.
--         */
-+	/*
-+	 * handle bit signal external calls
-+	 *
-+	 * For the ec_schedule signal we have to do nothing. All the work
-+	 * is done automatically when we return from the interrupt.
-+	 */
- 	bits = xchg(&S390_lowcore.ext_call_fast, 0);
- 
--        if (test_bit(ec_call_function, &bits))
-+	if (test_bit(ec_call_function, &bits))
- 		do_call_function();
- }
+ #
+@@ -139,8 +186,27 @@
+ #
+ CONFIG_CHANDEV=y
+ CONFIG_HOTPLUG=y
++CONFIG_LCS=m
++CONFIG_QETH=m
++
++#
++# Gigabit Ethernet default settings
++#
++CONFIG_QETH_IPV6=y
++CONFIG_QETH_VLAN=y
++# CONFIG_QETH_PERF_STATS is not set
+ CONFIG_CTC=m
++# CONFIG_MPC is not set
+ CONFIG_IUCV=m
++CONFIG_NETIUCV=m
++CONFIG_SMSGIUCV=m
++
++#
++# Miscellaneous
++#
++CONFIG_Z90CRYPT=m
++# CONFIG_NO_IDLE_HZ is not set
++# CONFIG_NO_IDLE_HZ_INIT is not set
  
- /*
-- * Send an external call sigp to another cpu and return without waiting
-+ * Send an external call sigp to another cpu and wait
-  * for its completion.
-  */
--static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig)
-+static void smp_ext_bitcall(int cpu, ec_bit_sig sig)
- {
--        sigp_ccode ccode;
+ #
+ # Networking options
+@@ -176,11 +242,6 @@
+ # CONFIG_IP_NF_ARPTABLES is not set
+ # CONFIG_IP_NF_COMPAT_IPCHAINS is not set
+ # CONFIG_IP_NF_COMPAT_IPFWADM is not set
 -
--        /*
--         * Set signaling bit in lowcore of target cpu and kick it
--         */
-+	/*
-+	 * Set signaling bit in lowcore of target cpu and kick it
-+	 */
- 	set_bit(sig, &(get_cpu_lowcore(cpu)->ext_call_fast));
--        ccode = signal_processor(cpu, sigp_external_call);
--        return ccode;
-+	while (signal_processor(cpu, sigp_external_call) == sigp_busy)
-+		udelay(10);
- }
+-#
+-#   IP: Virtual Server Configuration
+-#
+-# CONFIG_IP_VS is not set
+ CONFIG_IPV6=m
  
- /*
-  * Send an external call sigp to every other cpu in the system and
-- * return without waiting for its completion.
-+ * wait for its completion.
-  */
- static void smp_ext_bitcall_others(ec_bit_sig sig)
- {
--        sigp_ccode ccode;
--        int i;
-+	int i;
+ #
+@@ -188,6 +249,7 @@
+ #
+ # CONFIG_IP6_NF_QUEUE is not set
+ # CONFIG_IP6_NF_IPTABLES is not set
++CONFIG_SHARED_IPV6_CARDS=y
+ # CONFIG_KHTTPD is not set
  
--        for (i = 0; i < smp_num_cpus; i++) {
--                if (smp_processor_id() == i)
--                        continue;
--                /*
--                 * Set signaling bit in lowcore of target cpu and kick it
--                 */
-+	for (i = 0; i < smp_num_cpus; i++) {
-+		if (smp_processor_id() == i)
-+			continue;
-+		/*
-+		 * Set signaling bit in lowcore of target cpu and kick it
-+		 */
- 		set_bit(sig, &(get_cpu_lowcore(i)->ext_call_fast));
--                while (signal_processor(i, sigp_external_call) == sigp_busy)
-+		while (signal_processor(i, sigp_external_call) == sigp_busy)
- 			udelay(10);
--        }
-+	}
- }
+ #
+@@ -279,6 +341,7 @@
+ # CONFIG_QNX4FS_FS is not set
+ # CONFIG_QNX4FS_RW is not set
+ # CONFIG_ROMFS_FS is not set
++CONFIG_XIP2FS=m
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/kernel/entry.S	2006-01-30 22:25:24.000000000 -0700
+@@ -722,7 +722,12 @@
+         .globl io_int_handler
+ io_int_handler:
+         SAVE_ALL __LC_IO_OLD_PSW,0
++	mc 0, 0
+         GET_CURRENT                    # load pointer to task_struct to R9
++	stck	__LC_INT_CLOCK
++	clc	__LC_INT_CLOCK(8),__LC_JIFFY_TIMER
++	jhe	io_handle_tick
++io_call_handler:
+         la      %r2,SP_PTREGS(%r15)    # address of register-save area
+ 	llgh    %r3,__LC_SUBCHANNEL_NR # load subchannel number
+         llgf    %r4,__LC_IO_INT_PARM   # load interuption parm
+@@ -780,14 +785,27 @@
+ 	larl    %r14,io_leave
+         jg      do_signal           # return point is io_leave
  
- /*
-@@ -650,3 +692,4 @@
- EXPORT_SYMBOL(smp_ctl_clear_bit);
- EXPORT_SYMBOL(smp_num_cpus);
- EXPORT_SYMBOL(smp_call_function);
++#
++# account tick
++#
++io_handle_tick:
++	la	%r2,SP_PTREGS(%r15)    # address of register-save area
++	larl	%r14,io_call_handler
++	jg	account_ticks
 +
-=== arch/s390x/kernel/setup.c
-==================================================================
---- arch/s390x/kernel/setup.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/kernel/setup.c   (/trunk/2.4.27)   (revision 52)
-@@ -164,9 +164,9 @@
- 
- static int __init conmode_setup(char *str)
- {
--#if defined(CONFIG_HWC_CONSOLE)
--	if (strncmp(str, "hwc", 4) == 0)
--                SET_CONSOLE_HWC;
-+#if defined(CONFIG_SCLP_CONSOLE)
-+	if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
-+                SET_CONSOLE_SCLP;
- #endif
- #if defined(CONFIG_TN3215_CONSOLE)
- 	if (strncmp(str, "3215", 5) == 0)
-@@ -198,8 +198,8 @@
- 		 */
- 		cpcmd("TERM CONMODE 3215", NULL, 0);
- 		if (ptr == NULL) {
--#if defined(CONFIG_HWC_CONSOLE)
--			SET_CONSOLE_HWC;
-+#if defined(CONFIG_SCLP_CONSOLE)
-+			SET_CONSOLE_SCLP;
- #endif
- 			return;
- 		}
-@@ -208,16 +208,16 @@
- 			SET_CONSOLE_3270;
- #elif defined(CONFIG_TN3215_CONSOLE)
- 			SET_CONSOLE_3215;
--#elif defined(CONFIG_HWC_CONSOLE)
--			SET_CONSOLE_HWC;
-+#elif defined(CONFIG_SCLP_CONSOLE)
-+			SET_CONSOLE_SCLP;
- #endif
- 		} else if (strncmp(ptr + 8, "3215", 4) == 0) {
- #if defined(CONFIG_TN3215_CONSOLE)
- 			SET_CONSOLE_3215;
- #elif defined(CONFIG_TN3270_CONSOLE)
- 			SET_CONSOLE_3270;
--#elif defined(CONFIG_HWC_CONSOLE)
--			SET_CONSOLE_HWC;
-+#elif defined(CONFIG_SCLP_CONSOLE)
-+			SET_CONSOLE_SCLP;
- #endif
- 		}
-         } else if (MACHINE_IS_P390) {
-@@ -227,8 +227,8 @@
- 		SET_CONSOLE_3270;
- #endif
- 	} else {
--#if defined(CONFIG_HWC_CONSOLE)
--		SET_CONSOLE_HWC;
-+#if defined(CONFIG_SCLP_CONSOLE)
-+		SET_CONSOLE_SCLP;
- #endif
- 	}
- }
-@@ -271,21 +271,25 @@
- 
  /*
-  * Reboot, halt and power_off stubs. They just call _machine_restart,
-- * _machine_halt or _machine_power_off. 
-+ * _machine_halt or _machine_power_off after making sure that all pending
-+ * printks reached their destination.
+  * External interrupt handler routine
   */
+         .globl  ext_int_handler
+ ext_int_handler:
+         SAVE_ALL __LC_EXT_OLD_PSW,0
++	mc 0, 0
+         GET_CURRENT                    # load pointer to task_struct to R9
+ 	llgh	%r6,__LC_EXT_INT_CODE  # get interruption code
++	stck	__LC_INT_CLOCK
++	clc	__LC_INT_CLOCK(8),__LC_JIFFY_TIMER
++	jhe	ext_handle_tick
++ext_call_handler:
+ 	lgr	%r1,%r6		       # calculate index = code & 0xff
+ 	nill	%r1,0xff
+ 	sll	%r1,3
+@@ -799,6 +817,8 @@
+ 	ch	%r6,16(%r7)	       # compare external interrupt code
+ 	jne	ext_int_next
+ 	lg	%r1,8(%r7)	       # get handler address
++	ltgr	%r1,%r1
++	jz	ext_int_next
+ 	la	%r2,SP_PTREGS(%r15)    # address of register-save area
+ 	lgr	%r3,%r6		       # interruption code
+ 	basr	%r14,%r1	       # call handler
+@@ -808,12 +828,21 @@
+ 	jnz	ext_int_loop
+ 	j	io_return
  
- void machine_restart(char *command)
- {
-+	console_unblank();
- 	_machine_restart(command);
- }
- 
- void machine_halt(void)
- {
-+	console_unblank();
- 	_machine_halt();
- }
- 
- void machine_power_off(void)
- {
-+	console_unblank();
- 	_machine_power_off();
- }
- 
-=== arch/s390x/kernel/linux32.c
-==================================================================
---- arch/s390x/kernel/linux32.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/kernel/linux32.c   (/trunk/2.4.27)   (revision 52)
-@@ -4427,7 +4427,7 @@
-     ret = sys_newstat(tmp, &s);
-     set_fs (old_fs);
-     putname(tmp);
--    if (putstat64 (statbuf, &s)) 
-+    if (!ret && putstat64 (statbuf, &s)) 
- 	    return -EFAULT;
-     return ret;
- }
-@@ -4451,7 +4451,7 @@
-     ret = sys_newlstat(tmp, &s);
-     set_fs (old_fs);
-     putname(tmp);
--    if (putstat64 (statbuf, &s)) 
-+    if (!ret && putstat64 (statbuf, &s)) 
- 	    return -EFAULT;
-     return ret;
- }
-@@ -4467,7 +4467,7 @@
-     set_fs (KERNEL_DS);
-     ret = sys_newfstat(fd, &s);
-     set_fs (old_fs);
--    if (putstat64 (statbuf, &s))
-+    if (!ret && putstat64 (statbuf, &s))
- 	    return -EFAULT;
-     return ret;
- }
-@@ -4507,7 +4507,7 @@
- 	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
- 	if (!IS_ERR((void *) error) && error + len >= 0x80000000ULL) {
- 		/* Result is out of bounds.  */
--		do_munmap(current->mm, addr, len);
-+		do_munmap(current->mm, error, len);
- 		error = -ENOMEM;
- 	}
- 	up_write(&current->mm->mmap_sem);
-=== arch/s390x/kernel/process.c
-==================================================================
---- arch/s390x/kernel/process.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/kernel/process.c   (/trunk/2.4.27)   (revision 52)
-@@ -43,9 +43,24 @@
- #include <asm/io.h>
- #include <asm/processor.h>
- #include <asm/irq.h>
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+#include <asm/timer.h>
-+#endif
- 
- asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
- 
-+#ifdef CONFIG_VIRT_TIMER
-+extern int stop_cpu_timer(void);
-+#endif
-+
-+#ifdef CONFIG_NO_IDLE_HZ
-+extern void stop_hz_timer(void);
-+#endif
-+
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+extern atomic_t active_cpu_timer;
-+#endif
++#
++# account tick
++#
++ext_handle_tick:
++	la	%r2,SP_PTREGS(%r15)    # address of register-save area
++	larl	%r14,ext_call_handler
++	jg	account_ticks
 +
  /*
-  * The idle loop on a S390...
+  * Machine check handler routines
   */
-@@ -68,9 +83,37 @@
- 			continue;
- 		}
- 
-+#ifdef CONFIG_VIRT_TIMER
- 		/* 
-+		 * virtual CPU timer should not progress while its CPU is idle
-+		 */
-+		if (stop_cpu_timer()) {
-+			__sti();
-+			continue;
-+		}
-+#endif
-+
-+/*
-+ * active_cpu_timer is used by stop_hz_timer to determine if the last
-+ * CPU is gone. We have to update this value also if we use the virtual
-+ * CPU timer because both use monitor calls.
-+ */
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+		atomic_dec(&active_cpu_timer);
-+#endif
-+		
-+#ifdef CONFIG_NO_IDLE_HZ
-+		stop_hz_timer();
-+#endif
-+
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+		/* enable monitor call class 0 */
-+		__ctl_set_bit(8, 15);
-+#endif
-+
-+		/* 
- 		 * Wait for external, I/O or machine check interrupt and
--		 * switch of machine check bit after the wait has ended.
-+		 * switch off machine check bit after the wait has ended.
- 		 */
- 		wait_psw.mask = _WAIT_PSW_MASK;
- 		asm volatile (
-@@ -83,6 +126,10 @@
- 			"    lpswe 0(%1)\n"
- 			"1:"
- 			: "=&a" (reg) : "a" (&wait_psw) : "memory", "cc" );
-+		/*
-+		 * start_hz_timer is called by monitor call in entry.S
-+		 * if stop_hz_timer switched off the regular HZ interrupts
-+		 */
- 	}
- }
- 
-=== arch/s390x/kernel/ioctl32.c
-==================================================================
---- arch/s390x/kernel/ioctl32.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/kernel/ioctl32.c   (/trunk/2.4.27)   (revision 52)
-@@ -33,7 +33,9 @@
+         .globl mcck_int_handler
+ mcck_int_handler:
+         SAVE_ALL __LC_MCK_OLD_PSW,0
++	mc 0, 0
+ 	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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/kernel/ioctl32.c	2006-01-30 22:25:24.000000000 -0700
+@@ -34,7 +34,9 @@
  #include <asm/types.h>
  #include <asm/uaccess.h>
  #include <asm/dasd.h>
@@ -6700,7 +5639,7 @@
  
  #include "linux32.h"
  
-@@ -317,6 +319,95 @@
+@@ -318,6 +320,95 @@
  	return err;
  }
  
@@ -6796,7 +5735,7 @@
  struct rtentry32
  {
  	unsigned int	rt_pad1;
-@@ -378,6 +469,7 @@
+@@ -379,6 +470,7 @@
  	return sys_ioctl(fd, cmd, arg);
  }
  
@@ -6804,7 +5743,7 @@
  struct loop_info32 {
  	int			lo_number;      /* ioctl r/o */
  	__kernel_dev_t32	lo_device;      /* ioctl r/o */
-@@ -478,7 +570,6 @@
+@@ -479,7 +571,6 @@
  	return err;
  }
  
@@ -6812,7 +5751,7 @@
  static int w_long(unsigned int fd, unsigned int cmd, unsigned long arg)
  {
  	mm_segment_t old_fs = get_fs();
-@@ -519,6 +610,8 @@
+@@ -520,6 +611,8 @@
  	IOCTL32_DEFAULT(BIODASDINFO),
  	IOCTL32_DEFAULT(BIODASDFMT),
  
@@ -6821,7 +5760,7 @@
  	IOCTL32_DEFAULT(BLKROSET),
  	IOCTL32_DEFAULT(BLKROGET),
  	IOCTL32_DEFAULT(BLKRRPART),
-@@ -686,8 +779,20 @@
+@@ -702,8 +795,20 @@
  	IOCTL32_HANDLER(BLKGETSIZE, w_long),
  	IOCTL32_HANDLER(BLKFRAGET, w_long),
  	IOCTL32_HANDLER(BLKSECTGET, w_long),
@@ -6844,10 +5783,126 @@
  };
  
  #define NR_IOCTL32_HANDLERS	(sizeof(ioctl32_handler_table) /	\
-=== arch/s390x/kernel/s390_ksyms.c
-==================================================================
---- arch/s390x/kernel/s390_ksyms.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/kernel/s390_ksyms.c   (/trunk/2.4.27)   (revision 52)
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/linux32.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/kernel/linux32.c	2006-01-30 22:25:24.000000000 -0700
+@@ -4428,7 +4428,7 @@
+     ret = sys_newstat(tmp, &s);
+     set_fs (old_fs);
+     putname(tmp);
+-    if (putstat64 (statbuf, &s)) 
++    if (!ret && putstat64 (statbuf, &s)) 
+ 	    return -EFAULT;
+     return ret;
+ }
+@@ -4452,7 +4452,7 @@
+     ret = sys_newlstat(tmp, &s);
+     set_fs (old_fs);
+     putname(tmp);
+-    if (putstat64 (statbuf, &s)) 
++    if (!ret && putstat64 (statbuf, &s)) 
+ 	    return -EFAULT;
+     return ret;
+ }
+@@ -4468,7 +4468,7 @@
+     set_fs (KERNEL_DS);
+     ret = sys_newfstat(fd, &s);
+     set_fs (old_fs);
+-    if (putstat64 (statbuf, &s))
++    if (!ret && putstat64 (statbuf, &s))
+ 	    return -EFAULT;
+     return ret;
+ }
+@@ -4508,7 +4508,7 @@
+ 	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+ 	if (!IS_ERR((void *) error) && error + len >= 0x80000000ULL) {
+ 		/* Result is out of bounds.  */
+-		do_munmap(current->mm, addr, len);
++		do_munmap(current->mm, error, len);
+ 		error = -ENOMEM;
+ 	}
+ 	up_write(&current->mm->mmap_sem);
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/process.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/kernel/process.c	2006-01-30 22:25:24.000000000 -0700
+@@ -43,9 +43,24 @@
+ #include <asm/io.h>
+ #include <asm/processor.h>
+ #include <asm/irq.h>
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++#include <asm/timer.h>
++#endif
+ 
+ asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
+ 
++#ifdef CONFIG_VIRT_TIMER
++extern int stop_cpu_timer(void);
++#endif
++
++#ifdef CONFIG_NO_IDLE_HZ
++extern void stop_hz_timer(void);
++#endif
++
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++extern atomic_t active_cpu_timer;
++#endif
++
+ /*
+  * The idle loop on a S390...
+  */
+@@ -68,9 +83,37 @@
+ 			continue;
+ 		}
+ 
++#ifdef CONFIG_VIRT_TIMER
++		/* 
++		 * virtual CPU timer should not progress while its CPU is idle
++		 */
++		if (stop_cpu_timer()) {
++			__sti();
++			continue;
++		}
++#endif
++
++/*
++ * active_cpu_timer is used by stop_hz_timer to determine if the last
++ * CPU is gone. We have to update this value also if we use the virtual
++ * CPU timer because both use monitor calls.
++ */
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++		atomic_dec(&active_cpu_timer);
++#endif
++		
++#ifdef CONFIG_NO_IDLE_HZ
++		stop_hz_timer();
++#endif
++
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++		/* enable monitor call class 0 */
++		__ctl_set_bit(8, 15);
++#endif
++
+ 		/* 
+ 		 * Wait for external, I/O or machine check interrupt and
+-		 * switch of machine check bit after the wait has ended.
++		 * switch off machine check bit after the wait has ended.
+ 		 */
+ 		wait_psw.mask = _WAIT_PSW_MASK;
+ 		asm volatile (
+@@ -83,6 +126,10 @@
+ 			"    lpswe 0(%1)\n"
+ 			"1:"
+ 			: "=&a" (reg) : "a" (&wait_psw) : "memory", "cc" );
++		/*
++		 * start_hz_timer is called by monitor call in entry.S
++		 * if stop_hz_timer switched off the regular HZ interrupts
++		 */
+ 	}
+ }
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/s390_ksyms.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/kernel/s390_ksyms.c	2006-01-30 22:25:24.000000000 -0700
 @@ -9,10 +9,14 @@
  #include <linux/mm.h>
  #include <linux/smp.h>
@@ -6894,46 +5949,400 @@
 +/* urandom read needed for z90crypt */
 +extern struct file_operations urandom_fops;
 +EXPORT_SYMBOL_GPL(urandom_fops);
-=== arch/s390x/kernel/time.c
-==================================================================
---- arch/s390x/kernel/time.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/kernel/time.c   (/trunk/2.4.27)   (revision 52)
-@@ -4,8 +4,8 @@
-  *  S390 version
-  *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-  *    Author(s): Hartmut Penner (hp at de.ibm.com),
-- *               Martin Schwidefsky (schwidefsky at de.ibm.com),
-- *               Denis Joseph Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
-+ *		 Martin Schwidefsky (schwidefsky at de.ibm.com),
-+ *		 Denis Joseph Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
-  *
-  *  Derived from "arch/i386/kernel/time.c"
-  *    Copyright (C) 1991, 1992, 1995  Linus Torvalds
-@@ -26,38 +26,66 @@
- 
- #include <asm/uaccess.h>
- #include <asm/delay.h>
--
- #include <linux/timex.h>
- #include <linux/config.h>
--
-+#include <asm/s390_ext.h>
- #include <asm/irq.h>
--#include <asm/s390_ext.h>
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+#include <asm/timer.h>
-+#endif
- 
- /* change this if you have some constant time drift */
--#define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
-+#define USECS_PER_JIFFY	    ((unsigned long) 1000000/HZ)
- #define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/setup.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/kernel/setup.c	2006-01-30 22:25:24.000000000 -0700
+@@ -164,9 +164,9 @@
  
-+/*
-+ * Create a small time difference between the timer interrupts
-+ * on the different cpus to avoid lock contention.
-+ */
-+#define CPU_DEVIATION	    (smp_processor_id() << 12)
+ static int __init conmode_setup(char *str)
+ {
+-#if defined(CONFIG_HWC_CONSOLE)
+-	if (strncmp(str, "hwc", 4) == 0)
+-                SET_CONSOLE_HWC;
++#if defined(CONFIG_SCLP_CONSOLE)
++	if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
++                SET_CONSOLE_SCLP;
+ #endif
+ #if defined(CONFIG_TN3215_CONSOLE)
+ 	if (strncmp(str, "3215", 5) == 0)
+@@ -198,8 +198,8 @@
+ 		 */
+ 		cpcmd("TERM CONMODE 3215", NULL, 0);
+ 		if (ptr == NULL) {
+-#if defined(CONFIG_HWC_CONSOLE)
+-			SET_CONSOLE_HWC;
++#if defined(CONFIG_SCLP_CONSOLE)
++			SET_CONSOLE_SCLP;
+ #endif
+ 			return;
+ 		}
+@@ -208,16 +208,16 @@
+ 			SET_CONSOLE_3270;
+ #elif defined(CONFIG_TN3215_CONSOLE)
+ 			SET_CONSOLE_3215;
+-#elif defined(CONFIG_HWC_CONSOLE)
+-			SET_CONSOLE_HWC;
++#elif defined(CONFIG_SCLP_CONSOLE)
++			SET_CONSOLE_SCLP;
+ #endif
+ 		} else if (strncmp(ptr + 8, "3215", 4) == 0) {
+ #if defined(CONFIG_TN3215_CONSOLE)
+ 			SET_CONSOLE_3215;
+ #elif defined(CONFIG_TN3270_CONSOLE)
+ 			SET_CONSOLE_3270;
+-#elif defined(CONFIG_HWC_CONSOLE)
+-			SET_CONSOLE_HWC;
++#elif defined(CONFIG_SCLP_CONSOLE)
++			SET_CONSOLE_SCLP;
+ #endif
+ 		}
+         } else if (MACHINE_IS_P390) {
+@@ -227,8 +227,8 @@
+ 		SET_CONSOLE_3270;
+ #endif
+ 	} else {
+-#if defined(CONFIG_HWC_CONSOLE)
+-		SET_CONSOLE_HWC;
++#if defined(CONFIG_SCLP_CONSOLE)
++		SET_CONSOLE_SCLP;
+ #endif
+ 	}
+ }
+@@ -271,21 +271,25 @@
+ 
+ /*
+  * Reboot, halt and power_off stubs. They just call _machine_restart,
+- * _machine_halt or _machine_power_off. 
++ * _machine_halt or _machine_power_off after making sure that all pending
++ * printks reached their destination.
+  */
+ 
+ void machine_restart(char *command)
+ {
++	console_unblank();
+ 	_machine_restart(command);
+ }
+ 
+ void machine_halt(void)
+ {
++	console_unblank();
+ 	_machine_halt();
+ }
+ 
+ void machine_power_off(void)
+ {
++	console_unblank();
+ 	_machine_power_off();
+ }
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/smp.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/kernel/smp.c	2006-01-30 22:25:24.000000000 -0700
+@@ -92,7 +92,7 @@
+ 
+ extern void reipl(unsigned long devno);
+ 
+-static sigp_ccode smp_ext_bitcall(int, ec_bit_sig);
++static void smp_ext_bitcall(int, ec_bit_sig);
+ static void smp_ext_bitcall_others(ec_bit_sig);
+ 
+ /*
+@@ -131,7 +131,7 @@
+  * in the system.
+  */
+ 
+-int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
++int smp_call_function(void (*func) (void *info), void *info, int nonatomic,
+ 			int wait)
+ /*
+  * [SUMMARY] Run a function on all other CPUs.
+@@ -176,46 +176,91 @@
+ 	return 0;
+ }
+ 
++/*
++ * Call a function only on one CPU
++ * cpu : the CPU the function should be executed on
++ *
++ * You must not call this function with disabled interrupts or from a
++ * hardware interrupt handler, you may call it from a bottom half handler.                                                      */
++int smp_call_function_on(void (*func) (void *info), void *info,
++			 int nonatomic, int wait, int cpu)
++{
++	struct call_data_struct data;
++
++	if (!atomic_read(&smp_commenced))
++		return 0;
++
++	if (smp_processor_id() == cpu) {
++		/* direct call to function */
++		func(info);
++		return 0;
++	}
++
++	data.func = func;
++	data.info = info;
++
++	atomic_set(&data.started, 0);
++	data.wait = wait;
++	if (wait)
++		atomic_set(&data.finished, 0);
++
++	spin_lock_bh(&call_lock);
++	call_data = &data;
++	smp_ext_bitcall(cpu, ec_call_function);
++
++	/* Wait for response */
++	while (atomic_read(&data.started) != 1)
++		barrier();
++
++	if (wait)
++		while (atomic_read(&data.finished) != 1)
++			barrier();
++
++	spin_unlock_bh(&call_lock);
++	return 0;
++}
++
++
+ static inline void do_send_stop(void)
+ {
+-        u32 dummy;
+-        int i;
++	unsigned long dummy;
++	int i;
+ 
+-        /* stop all processors */
+-        for (i =  0; i < smp_num_cpus; i++) {
+-                if (smp_processor_id() != i) {
+-                        int ccode;
+-                        do {
+-                                ccode = signal_processor_ps(
+-                                   &dummy,
+-                                   0,
+-                                   i,
+-                                   sigp_stop);
+-                        } while(ccode == sigp_busy);
+-                }
+-        }
++	/* stop all processors */
++	for (i =  0; i < smp_num_cpus; i++) {
++		if (smp_processor_id() != i) {
++			int ccode;
++			do {
++				ccode = signal_processor_ps(
++				   &dummy,
++				   0,
++				   i,
++				   sigp_stop);
++			} while(ccode == sigp_busy);
++		}
++	}
+ }
+ 
+ static inline void do_store_status(void)
+ {
+-        unsigned long low_core_addr;
+-        u32 dummy;
+-        int i;
++	unsigned long low_core_addr;
++	unsigned long dummy;
++	int i;
+ 
+-        /* store status of all processors in their lowcores (real 0) */
+-        for (i =  0; i < smp_num_cpus; i++) {
+-                if (smp_processor_id() != i) {
+-                        int ccode;
+-                        low_core_addr = (unsigned long)get_cpu_lowcore(i);
+-                        do {
+-                                ccode = signal_processor_ps(
+-                                   &dummy,
+-                                   low_core_addr,
+-                                   i,
+-                                   sigp_store_status_at_address);
+-                        } while(ccode == sigp_busy);
+-                }
+-        }
++	/* store status of all processors in their lowcores (real 0) */
++	for (i =  0; i < smp_num_cpus; i++) {
++		if (smp_processor_id() != i) {
++			int ccode;
++			low_core_addr = (unsigned long)get_cpu_lowcore(i);
++			do {
++				ccode = signal_processor_ps(
++				   &dummy,
++				   low_core_addr,
++				   i,
++				   sigp_store_status_at_address);
++			} while(ccode == sigp_busy);
++		}
++	}
+ }
+ 
+ /*
+@@ -224,8 +269,8 @@
+  */
+ void smp_send_stop(void)
+ {
+-        /* write magic number to zero page (absolute 0) */
+-        get_cpu_lowcore(smp_processor_id())->panic_magic = __PANIC_MAGIC;
++	/* write magic number to zero page (absolute 0) */
++	get_cpu_lowcore(smp_processor_id())->panic_magic = __PANIC_MAGIC;
+ 
+ 	/* stop other processors. */
+ 	do_send_stop();
+@@ -263,7 +308,7 @@
+ void machine_restart_smp(char * __unused) 
+ {
+ 	cpu_restart_map = cpu_online_map;
+-        smp_call_function(do_machine_restart, NULL, 0, 0);
++	smp_call_function(do_machine_restart, NULL, 0, 0);
+ 	do_machine_restart(NULL);
+ }
+ 
+@@ -282,7 +327,7 @@
+ 
+ void machine_halt_smp(void)
+ {
+-        smp_call_function(do_machine_halt, NULL, 0, 0);
++	smp_call_function(do_machine_halt, NULL, 0, 0);
+ 	do_machine_halt(NULL);
+ }
+ 
+@@ -301,7 +346,7 @@
+ 
+ void machine_power_off_smp(void)
+ {
+-        smp_call_function(do_machine_power_off, NULL, 0, 0);
++	smp_call_function(do_machine_power_off, NULL, 0, 0);
+ 	do_machine_power_off(NULL);
+ }
+ 
+@@ -312,55 +357,52 @@
+ 
+ void do_ext_call_interrupt(struct pt_regs *regs, __u16 code)
+ {
+-        unsigned long bits;
++	unsigned long bits;
+ 
+-        /*
+-         * handle bit signal external calls
+-         *
+-         * For the ec_schedule signal we have to do nothing. All the work
+-         * is done automatically when we return from the interrupt.
+-         */
++	/*
++	 * handle bit signal external calls
++	 *
++	 * For the ec_schedule signal we have to do nothing. All the work
++	 * is done automatically when we return from the interrupt.
++	 */
+ 	bits = xchg(&S390_lowcore.ext_call_fast, 0);
+ 
+-        if (test_bit(ec_call_function, &bits))
++	if (test_bit(ec_call_function, &bits))
+ 		do_call_function();
+ }
+ 
+ /*
+- * Send an external call sigp to another cpu and return without waiting
++ * Send an external call sigp to another cpu and wait
+  * for its completion.
+  */
+-static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig)
++static void smp_ext_bitcall(int cpu, ec_bit_sig sig)
+ {
+-        sigp_ccode ccode;
+-
+-        /*
+-         * Set signaling bit in lowcore of target cpu and kick it
+-         */
++	/*
++	 * Set signaling bit in lowcore of target cpu and kick it
++	 */
+ 	set_bit(sig, &(get_cpu_lowcore(cpu)->ext_call_fast));
+-        ccode = signal_processor(cpu, sigp_external_call);
+-        return ccode;
++	while (signal_processor(cpu, sigp_external_call) == sigp_busy)
++		udelay(10);
+ }
+ 
+ /*
+  * Send an external call sigp to every other cpu in the system and
+- * return without waiting for its completion.
++ * wait for its completion.
+  */
+ static void smp_ext_bitcall_others(ec_bit_sig sig)
+ {
+-        sigp_ccode ccode;
+-        int i;
++	int i;
+ 
+-        for (i = 0; i < smp_num_cpus; i++) {
+-                if (smp_processor_id() == i)
+-                        continue;
+-                /*
+-                 * Set signaling bit in lowcore of target cpu and kick it
+-                 */
++	for (i = 0; i < smp_num_cpus; i++) {
++		if (smp_processor_id() == i)
++			continue;
++		/*
++		 * Set signaling bit in lowcore of target cpu and kick it
++		 */
+ 		set_bit(sig, &(get_cpu_lowcore(i)->ext_call_fast));
+-                while (signal_processor(i, sigp_external_call) == sigp_busy)
++		while (signal_processor(i, sigp_external_call) == sigp_busy)
+ 			udelay(10);
+-        }
++	}
+ }
+ 
+ /*
+@@ -650,3 +692,4 @@
+ EXPORT_SYMBOL(smp_ctl_clear_bit);
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/kernel/time.c	2006-01-30 22:25:24.000000000 -0700
+@@ -4,8 +4,8 @@
+  *  S390 version
+  *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+  *    Author(s): Hartmut Penner (hp at de.ibm.com),
+- *               Martin Schwidefsky (schwidefsky at de.ibm.com),
+- *               Denis Joseph Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
++ *		 Martin Schwidefsky (schwidefsky at de.ibm.com),
++ *		 Denis Joseph Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
+  *
+  *  Derived from "arch/i386/kernel/time.c"
+  *    Copyright (C) 1991, 1992, 1995  Linus Torvalds
+@@ -26,38 +26,66 @@
+ 
+ #include <asm/uaccess.h>
+ #include <asm/delay.h>
+-
+ #include <linux/timex.h>
+ #include <linux/config.h>
+-
+-#include <asm/irq.h>
+ #include <asm/s390_ext.h>
++#include <asm/irq.h>
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++#include <asm/timer.h>
++#endif
+ 
+ /* change this if you have some constant time drift */
+-#define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
++#define USECS_PER_JIFFY	    ((unsigned long) 1000000/HZ)
+ #define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
+ 
++/*
++ * Create a small time difference between the timer interrupts
++ * on the different cpus to avoid lock contention.
++ */
++#define CPU_DEVIATION	    (smp_processor_id() << 12)
 +
  #define TICK_SIZE tick
  
@@ -6996,7 +6405,7 @@
 +	int cpu = smp_processor_id();
 +	__u64 tmp;
 +	__u32 ticks;
- 
++
 +	/* Calculate how many ticks have passed. */
 +	tmp = S390_lowcore.int_clock - S390_lowcore.jiffy_timer;
 +	if (tmp >= 2*CLK_TICKS_PER_JIFFY) {  /* more than one tick ? */
@@ -7016,7 +6425,7 @@
 +	asm volatile ("SCKC %0" : : "m" (tmp));
 +
 +	irq_enter(cpu, 0);
-+
+ 
  #ifdef CONFIG_SMP
 -extern __u16 boot_cpu_addr;
 +	/*
@@ -7059,7 +6468,7 @@
 +	vt_list = &virt_cpu_timer[smp_processor_id()];
 +	set_vtimer(vt_list->idle);
 +}
- 
++
 +int stop_cpu_timer(void)
 +{
 +	__u64 done;
@@ -7069,47 +6478,47 @@
 +
 +	/* store progress */
 +	asm volatile ("STPT %0" : "=m" (done));
-+
+ 
  	/*
 -	 * set clock comparator for next tick
 +	 * If done is negative we do not stop the CPU timer
 +	 * because we will get instantly an interrupt that 
 +	 * will start the CPU timer again.
- 	 */
--        S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
--        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
++	 */
 +	if (done & 1LL<<63)
 +		return 1;
 +	else
 +		vt_list->offset += vt_list->to_expire - done;
- 
--#ifdef CONFIG_SMP
--	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr)
--		write_lock(&xtime_lock);
++
 +	/* save the actual expire value */
 +	vt_list->idle = done;
- 
--	update_process_times(user_mode(regs));
++
 +	/* 
 +	 * We cannot halt the CPU timer, we just write a value that
 +	 * nearly never expires (only after 71 years) and re-write
 +	 * the stored expire value if we continue the timer 
-+	 */
+ 	 */
+-        S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
+-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
 +	set_vtimer(VTIMER_MAX_SLICE);
 +	return 0;
 +}
  
--	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) {
--		do_timer(regs);
--		write_unlock(&xtime_lock);
+-#ifdef CONFIG_SMP
+-	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr)
+-		write_lock(&xtime_lock);
 +void set_vtimer(__u64 expires)
 +{
 +	asm volatile ("SPT %0" : : "m" (expires));
-+
+ 
+-	update_process_times(user_mode(regs));
 +	/* store expire time for this CPU timer */
 +	virt_cpu_timer[smp_processor_id()].to_expire = expires;
 +}
-+
+ 
+-	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) {
+-		do_timer(regs);
+-		write_unlock(&xtime_lock);
 +/*
 + * Sorted add to a list. List is linear searched until first bigger 
 + * element is found.
@@ -7166,12 +6575,11 @@
 +			spin_unlock(&vt_list->lock);
 +		}
 +	}
- }
- 
- /*
-- * Start the clock comparator on the current CPU
++}
++
++/*
 + * Handler for the virtual CPU timer.
-  */
++ */
 +static void do_cpu_timer_interrupt(struct pt_regs *regs, __u16 error_code)
 +{
 +	int cpu;
@@ -7229,13 +6637,14 @@
 +	}
 +	spin_unlock(&vt_list->lock);
 +	set_vtimer(next);
-+}
+ }
 +#endif
-+
-+/*
+ 
+ /*
+- * Start the clock comparator on the current CPU
 + * Start the clock comparator and the virtual CPU timer 
 + * on the current CPU
-+ */
+  */
  void init_cpu_timer(void)
  {
  	unsigned long cr0;
@@ -7329,13 +6738,14 @@
 -        set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
 -                      (0x3c26700LL*1000000*4096);
 -        tod_to_timeval(set_time_cc, &xtime);
+-
+-        /* request the 0x1004 external interrupt */
+-        if (register_early_external_interrupt(0x1004, do_comparator_interrupt,
 +	xtime_cc = init_timer_cc + CLK_TICKS_PER_JIFFY;
 +	set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
 +		      (0x3c26700LL*1000000*4096);
 +	tod_to_timeval(set_time_cc, &xtime);
- 
--        /* request the 0x1004 external interrupt */
--        if (register_early_external_interrupt(0x1004, do_comparator_interrupt,
++
 +	/* request the clock comparator external interrupt */
 +	if (register_early_external_interrupt(0x1004, NULL,
 +					      &ext_int_info_cc) != 0)
@@ -7348,11 +6758,9 @@
 -                panic("Couldn't request external interrupt 0x1004");
 +		panic("Couldn't request external interrupt 0x1005");
 +#endif
- 
--        /* init CPU timer */
--        init_cpu_timer();
++
 +	init_cpu_timer();
- }
++}
 +
 +#ifdef CONFIG_VIRT_TIMER
 +void init_virt_timer(struct vtimer_list *timer)
@@ -7403,7 +6811,9 @@
 +
 +	/* save progress */
 +	asm volatile ("STPT %0" : "=m" (done));
-+
+ 
+-        /* init CPU timer */
+-        init_cpu_timer();
 +	/* calculate completed work */
 +	done = vt_list->to_expire - done + vt_list->offset;
 +	vt_list->offset = 0;
@@ -7679,306 +7089,34 @@
 +#ifdef CONFIG_NO_IDLE_HZ
 +	start_hz_timer();
 +#endif
-+}
-=== arch/s390x/kernel/entry.S
-==================================================================
---- arch/s390x/kernel/entry.S   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/kernel/entry.S   (/trunk/2.4.27)   (revision 52)
-@@ -722,7 +722,12 @@
-         .globl io_int_handler
- io_int_handler:
-         SAVE_ALL __LC_IO_OLD_PSW,0
-+	mc 0, 0
-         GET_CURRENT                    # load pointer to task_struct to R9
-+	stck	__LC_INT_CLOCK
-+	clc	__LC_INT_CLOCK(8),__LC_JIFFY_TIMER
-+	jhe	io_handle_tick
-+io_call_handler:
-         la      %r2,SP_PTREGS(%r15)    # address of register-save area
- 	llgh    %r3,__LC_SUBCHANNEL_NR # load subchannel number
-         llgf    %r4,__LC_IO_INT_PARM   # load interuption parm
-@@ -780,14 +785,27 @@
- 	larl    %r14,io_leave
-         jg      do_signal           # return point is io_leave
- 
-+#
-+# account tick
-+#
-+io_handle_tick:
-+	la	%r2,SP_PTREGS(%r15)    # address of register-save area
-+	larl	%r14,io_call_handler
-+	jg	account_ticks
-+
- /*
-  * External interrupt handler routine
-  */
-         .globl  ext_int_handler
- ext_int_handler:
-         SAVE_ALL __LC_EXT_OLD_PSW,0
-+	mc 0, 0
-         GET_CURRENT                    # load pointer to task_struct to R9
- 	llgh	%r6,__LC_EXT_INT_CODE  # get interruption code
-+	stck	__LC_INT_CLOCK
-+	clc	__LC_INT_CLOCK(8),__LC_JIFFY_TIMER
-+	jhe	ext_handle_tick
-+ext_call_handler:
- 	lgr	%r1,%r6		       # calculate index = code & 0xff
- 	nill	%r1,0xff
- 	sll	%r1,3
-@@ -799,6 +817,8 @@
- 	ch	%r6,16(%r7)	       # compare external interrupt code
- 	jne	ext_int_next
- 	lg	%r1,8(%r7)	       # get handler address
-+	ltgr	%r1,%r1
-+	jz	ext_int_next
- 	la	%r2,SP_PTREGS(%r15)    # address of register-save area
- 	lgr	%r3,%r6		       # interruption code
- 	basr	%r14,%r1	       # call handler
-@@ -808,12 +828,21 @@
- 	jnz	ext_int_loop
- 	j	io_return
- 
-+#
-+# account tick
-+#
-+ext_handle_tick:
-+	la	%r2,SP_PTREGS(%r15)    # address of register-save area
-+	larl	%r14,ext_call_handler
-+	jg	account_ticks
-+
- /*
-  * Machine check handler routines
-  */
-         .globl mcck_int_handler
- mcck_int_handler:
-         SAVE_ALL __LC_MCK_OLD_PSW,0
-+	mc 0, 0
- 	brasl   %r14,s390_do_machine_check
- mcck_return:
-         RESTORE_ALL 0
-=== arch/s390x/kernel/traps.c
-==================================================================
---- arch/s390x/kernel/traps.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/kernel/traps.c   (/trunk/2.4.27)   (revision 52)
-@@ -56,6 +56,9 @@
- extern pgm_check_handler_t do_segment_exception;
- extern pgm_check_handler_t do_region_exception;
- extern pgm_check_handler_t do_page_exception;
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+extern pgm_check_handler_t do_monitor_call;
-+#endif
- #ifdef CONFIG_PFAULT
- extern int pfault_init(void);
- extern void pfault_fini(void);
-@@ -497,6 +500,9 @@
-         pgm_check_table[0x1C] = &privileged_op;
-         pgm_check_table[0x38] = &addressing_exception;
-         pgm_check_table[0x3B] = &do_region_exception;
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+	pgm_check_table[0x40] = &do_monitor_call;
-+#endif
- #ifdef CONFIG_PFAULT
- 	if (MACHINE_IS_VM) {
- 		/* request the 0x2603 external interrupt */
-=== arch/s390x/defconfig
-==================================================================
---- arch/s390x/defconfig   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/defconfig   (/trunk/2.4.27)   (revision 52)
-@@ -39,8 +39,8 @@
- CONFIG_QDIO=m
- # CONFIG_QDIO_PERF_STATS is not set
- CONFIG_IPL=y
--# CONFIG_IPL_TAPE is not set
--CONFIG_IPL_VM=y
-+CONFIG_IPL_TAPE=y
-+# CONFIG_IPL_VM is not set
- CONFIG_NET=y
- CONFIG_SYSVIPC=y
- # CONFIG_BSD_PROCESS_ACCT is not set
-@@ -51,8 +51,52 @@
- # CONFIG_PROCESS_DEBUG is not set
- CONFIG_PFAULT=y
- # CONFIG_SHARED_KERNEL is not set
-+# CONFIG_VIRT_TIMER is not set
-+# CONFIG_APPLDATA_BASE is not set
-+# CONFIG_APPLDATA_MEM is not set
-+# CONFIG_APPLDATA_OS is not set
-+# CONFIG_APPLDATA_NET_SUM is not set
-+CONFIG_CMM=m
-+CONFIG_CMM_PROC=y
-+# CONFIG_CMM_IUCV is not set
- 
- #
-+# SCSI support
-+#
-+CONFIG_SCSI=m
-+
-+#
-+# SCSI support type (disk, tape, CD-ROM)
-+#
-+CONFIG_BLK_DEV_SD=m
-+CONFIG_SD_EXTRA_DEVS=1000
-+CONFIG_CHR_DEV_ST=m
-+# CONFIG_CHR_DEV_OSST is not set
-+CONFIG_BLK_DEV_SR=m
-+# CONFIG_BLK_DEV_SR_VENDOR is not set
-+CONFIG_SR_EXTRA_DEVS=10
-+CONFIG_CHR_DEV_SG=m
-+
-+#
-+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-+#
-+CONFIG_SCSI_DEBUG_QUEUES=y
-+CONFIG_SCSI_MULTI_LUN=y
-+CONFIG_SCSI_CONSTANTS=y
-+CONFIG_SCSI_LOGGING=y
-+
-+#
-+# SCSI low-level drivers
-+#
-+CONFIG_ZFCP=m
-+CONFIG_ZFCP_HBAAPI=m
-+
-+#
-+# PCMCIA SCSI adapter support
-+#
-+# CONFIG_SCSI_PCMCIA is not set
-+
-+#
- # Block device drivers
- #
- CONFIG_BLK_DEV_LOOP=y
-@@ -61,6 +105,7 @@
- CONFIG_BLK_DEV_RAM_SIZE=24576
- CONFIG_BLK_DEV_INITRD=y
- CONFIG_BLK_DEV_XPRAM=m
-+CONFIG_DCSSBLK=m
- 
- #
- # S/390 block device drivers
-@@ -68,6 +113,7 @@
- CONFIG_DASD=y
- CONFIG_DASD_ECKD=y
- CONFIG_DASD_FBA=y
-+CONFIG_S390_CMF=m
- 
- #
- # Multi-device support (RAID and LVM)
-@@ -94,22 +140,24 @@
- CONFIG_TN3270_CONSOLE=y
- CONFIG_TN3215=y
- CONFIG_TN3215_CONSOLE=y
--CONFIG_HWC=y
--CONFIG_HWC_CONSOLE=y
--CONFIG_HWC_CPI=m
-+CONFIG_SCLP=y
-+CONFIG_SCLP_TTY=y
-+CONFIG_SCLP_CONSOLE=y
-+CONFIG_SCLP_VT220_TTY=y
-+CONFIG_SCLP_VT220_CONSOLE=y
-+CONFIG_SCLP_CPI=m
- CONFIG_S390_TAPE=m
- 
- #
- # S/390 tape interface support
- #
--CONFIG_S390_TAPE_CHAR=y
- CONFIG_S390_TAPE_BLOCK=y
- 
- #
- # S/390 tape hardware support
- #
--CONFIG_S390_TAPE_3490=y
--CONFIG_S390_TAPE_3480=y
-+CONFIG_S390_TAPE_34XX=m
-+CONFIG_VMLOGRDR=m
- 
- #
- # Network device drivers
-@@ -121,7 +169,6 @@
- # CONFIG_TUN is not set
- CONFIG_NET_ETHERNET=y
- CONFIG_TR=y
--# CONFIG_C7000 is not set
- CONFIG_FDDI=y
- 
- #
-@@ -129,10 +176,29 @@
- #
- CONFIG_CHANDEV=y
- CONFIG_HOTPLUG=y
-+CONFIG_LCS=m
-+CONFIG_QETH=m
-+
-+#
-+# Gigabit Ethernet default settings
-+#
-+CONFIG_QETH_IPV6=y
-+CONFIG_QETH_VLAN=y
-+# CONFIG_QETH_PERF_STATS is not set
- CONFIG_CTC=m
-+# CONFIG_MPC is not set
- CONFIG_IUCV=m
-+CONFIG_NETIUCV=m
-+CONFIG_SMSGIUCV=m
- 
- #
-+# Miscellaneous
-+#
-+CONFIG_Z90CRYPT=m
-+# CONFIG_NO_IDLE_HZ is not set
-+# CONFIG_NO_IDLE_HZ_INIT is not set
-+
-+#
- # Networking options
- #
- CONFIG_PACKET=y
-@@ -165,11 +231,6 @@
- # CONFIG_IP_NF_ARPTABLES is not set
- # CONFIG_IP_NF_COMPAT_IPCHAINS is not set
- # CONFIG_IP_NF_COMPAT_IPFWADM is not set
--
--#
--#   IP: Virtual Server Configuration
--#
--# CONFIG_IP_VS is not set
- CONFIG_IPV6=m
- 
- #
-@@ -177,6 +238,7 @@
- #
- # CONFIG_IP6_NF_QUEUE is not set
- # CONFIG_IP6_NF_IPTABLES is not set
-+CONFIG_SHARED_IPV6_CARDS=y
- # CONFIG_KHTTPD is not set
- 
- #
-@@ -268,6 +330,7 @@
- # CONFIG_QNX4FS_FS is not set
- # CONFIG_QNX4FS_RW is not set
- # CONFIG_ROMFS_FS is not set
-+CONFIG_XIP2FS=m
- CONFIG_EXT2_FS=y
- # CONFIG_SYSV_FS is not set
- # CONFIG_UDF_FS is not set
-=== arch/s390x/mm/init.c
-==================================================================
---- arch/s390x/mm/init.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/mm/init.c   (/trunk/2.4.27)   (revision 52)
-@@ -172,7 +172,7 @@
- 
- void diag10(unsigned long addr)
- {
--        if (addr >= 0x80000000)
-+        if (addr >= 0x7ff00000)
-                 return;
-         asm volatile ("sam31\n\t"
-                       "diag %0,%0,0x10\n\t"
-=== arch/s390x/mm/Makefile
-==================================================================
---- arch/s390x/mm/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/mm/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -9,6 +9,8 @@
+ }
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/kernel/traps.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/kernel/traps.c	2006-01-30 22:25:24.000000000 -0700
+@@ -56,6 +56,9 @@
+ extern pgm_check_handler_t do_segment_exception;
+ extern pgm_check_handler_t do_region_exception;
+ extern pgm_check_handler_t do_page_exception;
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++extern pgm_check_handler_t do_monitor_call;
++#endif
+ #ifdef CONFIG_PFAULT
+ extern int pfault_init(void);
+ extern void pfault_fini(void);
+@@ -497,6 +500,9 @@
+         pgm_check_table[0x1C] = &privileged_op;
+         pgm_check_table[0x38] = &addressing_exception;
+         pgm_check_table[0x3B] = &do_region_exception;
++#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
++	pgm_check_table[0x40] = &do_monitor_call;
++#endif
+ #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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/mm/Makefile	2006-01-30 22:25:24.000000000 -0700
+@@ -9,6 +9,8 @@
  
  O_TARGET := mm.o
  
@@ -7988,10 +7126,9 @@
 +export-objs	:= dcss.o cmm.o
  
  include $(TOPDIR)/Rules.make
-=== arch/s390x/mm/cmm.c
-==================================================================
---- arch/s390x/mm/cmm.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/mm/cmm.c   (/trunk/2.4.27)   (revision 52)
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/cmm.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/mm/cmm.c	2006-01-30 22:25:24.000000000 -0700
 @@ -0,0 +1,448 @@
 +/*
 + *  arch/s390/mm/cmm.c
@@ -8441,10 +7578,9 @@
 +EXPORT_SYMBOL(cmm_set_timeout);
 +
 +MODULE_LICENSE("GPL");
-=== arch/s390x/mm/dcss.c
-==================================================================
---- arch/s390x/mm/dcss.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/mm/dcss.c   (/trunk/2.4.27)   (revision 52)
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/dcss.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/mm/dcss.c	2006-01-30 22:25:24.000000000 -0700
 @@ -0,0 +1,504 @@
 +/*
 + * File...........: arch/s390/mm/dcss.c
@@ -8950,78549 +8086,79133 @@
 +EXPORT_SYMBOL(segment_load);
 +EXPORT_SYMBOL(segment_unload);
 +EXPORT_SYMBOL(segment_replace);
-=== arch/s390x/config.in
-==================================================================
---- arch/s390x/config.in   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/config.in   (/trunk/2.4.27)   (revision 52)
-@@ -65,9 +65,35 @@
- bool 'Show crashed user process info' CONFIG_PROCESS_DEBUG
- bool 'Pseudo page fault support' CONFIG_PFAULT
- bool 'VM shared kernel support' CONFIG_SHARED_KERNEL
-+bool 'No HZ timer ticks in idle' CONFIG_NO_IDLE_HZ
-+if [ "$CONFIG_NO_IDLE_HZ" = "y" ] ; then
-+  bool '  Idle HZ timer on by default' CONFIG_NO_IDLE_HZ_INIT
-+fi
-+bool 'Virtual CPU timer support' CONFIG_VIRT_TIMER
-+dep_bool 'Linux - VM Monitor Stream, base infrastructure' CONFIG_APPLDATA_BASE \
-+$CONFIG_PROC_FS $CONFIG_VIRT_TIMER
-+dep_tristate '   Monitor memory management statistics' CONFIG_APPLDATA_MEM $CONFIG_APPLDATA_BASE
-+dep_tristate '   Monitor OS statistics' CONFIG_APPLDATA_OS $CONFIG_APPLDATA_BASE
-+dep_tristate '   Monitor overall network statistics' CONFIG_APPLDATA_NET_SUM $CONFIG_APPLDATA_BASE
-+tristate 'Collaborative memory management' CONFIG_CMM
-+if [ "$CONFIG_CMM" != "n" ]; then
-+  dep_bool '/proc interface to cooperative memory management' CONFIG_CMM_PROC $CONFIG_PROC_FS
-+  if [ "$CONFIG_SMSGIUCV" = "y" -o "$CONFIG_SMSGIUCV" = "$CONFIG_CMM" ]; then
-+    bool 'IUCV special message interface to cooperative memory management' CONFIG_CMM_IUCV
-+  fi
-+fi
- endmenu
- 
-+mainmenu_option next_comment
-+comment 'SCSI support'
- 
-+tristate 'SCSI support' CONFIG_SCSI
-+
-+if [ "$CONFIG_SCSI" != "n" ]; then
-+   source drivers/scsi/Config.in
-+fi
-+endmenu
-+
- source drivers/s390/Config.in
- 
- if [ "$CONFIG_NET" = "y" ]; then
-=== arch/s390x/Makefile
-==================================================================
---- arch/s390x/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390x/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -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)
+diff -urN kernel-source-2.4.27-2.4.27.orig/arch/s390x/mm/init.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/arch/s390x/mm/init.c	2006-01-30 22:25:24.000000000 -0700
+@@ -172,7 +172,7 @@
  
- HEAD := arch/s390x/kernel/head.o arch/s390x/kernel/init_task.o
+ void diag10(unsigned long addr)
+ {
+-        if (addr >= 0x80000000)
++        if (addr >= 0x7ff00000)
+                 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 kernel-source-2.4.27-2.4.27/drivers/block/Makefile
+--- kernel-source-2.4.27-2.4.27.orig/drivers/block/Makefile	2004-08-07 17:26:04.000000000 -0600
++++ kernel-source-2.4.27-2.4.27/drivers/block/Makefile	2006-01-30 22:25:26.000000000 -0700
+@@ -10,7 +10,7 @@
  
- 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
+ O_TARGET := block.o
  
--all: image listing
-+all: image
+-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
  
- listing: vmlinux
- 	@$(MAKEBOOT) listing
-@@ -44,6 +47,9 @@
- arch/s390x/kernel: dummy
- 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390x/kernel
+ obj-y	:= ll_rw_blk.o blkpg.o genhd.o elevator.o
  
-+arch/s390/appldata: dummy
-+	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/appldata
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/block/elevator.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/block/elevator.c	2006-01-30 22:25:26.000000000 -0700
+@@ -219,3 +219,9 @@
+ 	*elevator = type;
+ 	elevator->queue_ID = queue_ID++;
+ }
 +
- arch/s390x/mm: dummy
- 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390x/mm
- 
-=== arch/s390/kernel/time.c
-==================================================================
---- arch/s390/kernel/time.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/kernel/time.c   (/trunk/2.4.27)   (revision 52)
-@@ -4,8 +4,8 @@
-  *  S390 version
-  *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-  *    Author(s): Hartmut Penner (hp at de.ibm.com),
-- *               Martin Schwidefsky (schwidefsky at de.ibm.com),
-- *               Denis Joseph Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
-+ *		 Martin Schwidefsky (schwidefsky at de.ibm.com),
-+ *		 Denis Joseph Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
-  *
-  *  Derived from "arch/i386/kernel/time.c"
-  *    Copyright (C) 1991, 1992, 1995  Linus Torvalds
-@@ -26,38 +26,67 @@
- 
- #include <asm/uaccess.h>
- #include <asm/delay.h>
--#include <asm/s390_ext.h>
--
- #include <linux/timex.h>
- #include <linux/config.h>
--
-+#include <asm/s390_ext.h>
- #include <asm/irq.h>
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+#include <asm/timer.h>
-+#endif
- 
- /* change this if you have some constant time drift */
--#define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
-+#define USECS_PER_JIFFY	    ((unsigned long) 1000000/HZ)
- #define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 */
+ }
  
 +/*
-+ * Create a small time difference between the timer interrupts
-+ * on the different cpus to avoid lock contention.
++ * Register a tty device described by <driver>, with minor number <minor>,
++ * device name <name> and in the /dev directory given by <dir>.
 + */
-+#define CPU_DEVIATION	    (smp_processor_id() << 12)
-+
- #define TICK_SIZE tick
- 
--static ext_int_info_t ext_int_info_timer;
--static uint64_t init_timer_cc;
-+static ext_int_info_t ext_int_info_cc;
- 
-+static u64 init_timer_cc;
-+static u64 xtime_cc;
-+
- extern rwlock_t xtime_lock;
- extern unsigned long wall_jiffies;
- 
-+#ifdef CONFIG_VIRT_TIMER
-+static ext_int_info_t ext_int_info_timer;
-+static struct vtimer_queue virt_cpu_timer[NR_CPUS];
-+#define VTIMER_MAGIC (0x4b87ad6e + 1)
-+#endif
-+
-+#ifdef CONFIG_NO_IDLE_HZ
-+ 
-+#ifdef CONFIG_NO_IDLE_HZ_INIT
-+int sysctl_hz_timer = 0;
-+#else
-+int sysctl_hz_timer = 1;
-+#endif
-+
-+#endif
++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);
 +
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+atomic_t active_cpu_timer = ATOMIC_INIT(0);
-+#endif
++	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 tod_to_timeval(__u64 todval, struct timeval *xtime)
- {
--        const int high_bit = 0x80000000L;
--        const int c_f4240 = 0xf4240L;
--        const int c_7a120 = 0x7a120;
-+	const int high_bit = 0x80000000L;
-+	const int c_f4240 = 0xf4240L;
-+	const int c_7a120 = 0x7a120;
- 	/* We have to divide the 64 bit value todval by 4096
- 	 * (because the 2^12 bit is the one that changes every 
--         * microsecond) and then split it into seconds and
--         * microseconds. A value of max (2^52-1) divided by
--         * the value 0xF4240 can yield a max result of approx
--         * (2^32.068). Thats to big to fit into a signed int
-+	 * microsecond) and then split it into seconds and
-+	 * microseconds. A value of max (2^52-1) divided by
-+	 * the value 0xF4240 can yield a max result of approx
-+	 * (2^32.068). Thats to big to fit into a signed int
- 	 *   ... hacking time!
--         */
-+	 */
- 	asm volatile ("L     2,%1\n\t"
- 		      "LR    3,2\n\t"
- 		      "SRL   2,12\n\t"
-@@ -70,12 +99,12 @@
- 		      "JL    .+12\n\t"
- 		      "S     2,%2\n\t"
- 		      "L     4,%3\n\t"
--                      "D     2,%4\n\t"
-+		      "D     2,%4\n\t"
- 		      "OR    3,4\n\t"
- 		      "ST    2,%O0+4(%R0)\n\t"
- 		      "ST    3,%0"
- 		      : "=m" (*xtime) : "m" (todval),
--		        "m" (c_7a120), "m" (high_bit), "m" (c_f4240)
-+			"m" (c_7a120), "m" (high_bit), "m" (c_f4240)
- 		      : "cc", "memory", "2", "3", "4" );
- }
- 
-@@ -83,8 +112,8 @@
- {
- 	__u64 now;
- 
--	asm ("STCK 0(%0)" : : "a" (&now) : "memory", "cc");
--        now = (now - init_timer_cc) >> 12;
-+	asm volatile ("STCK 0(%0)" : : "a" (&now) : "memory", "cc");
-+	now = (now - init_timer_cc) >> 12;
- 	/* We require the offset from the latest update of xtime */
- 	now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
- 	return (unsigned long) now;
-@@ -114,7 +143,6 @@
- 
- void do_settimeofday(struct timeval *tv)
- {
--
- 	write_lock_irq(&xtime_lock);
- 	/* This is revolting. We need to set the xtime.tv_usec
- 	 * correctly. However, the value in this location is
-@@ -137,58 +165,280 @@
- 	write_unlock_irq(&xtime_lock);
- }
- 
-+static inline __u32 div64_32(__u64 dividend, __u32 divisor)
++void tty_unregister_devfs_name (struct tty_driver *driver, unsigned minor,
++				devfs_handle_t dir, const char *name)
 +{
-+	register_pair rp;
++#ifdef CONFIG_DEVFS_FS
++	void * handle;
 +
-+	rp.pair = dividend;
-+	asm ("dr %0,%1" : "+d" (rp) : "d" (divisor));
-+	return rp.subreg.odd;
++	handle = devfs_find_handle (dir, name, driver->major, minor,
++				    DEVFS_SPECIAL_CHR, 0);
++	devfs_unregister (handle);
++#endif /* CONFIG_DEVFS_FS */
 +}
 +
- /*
-  * timer_interrupt() needs to keep up the real-time clock,
-  * as well as call the "do_timer()" routine every clocktick
-  */
-+void account_ticks(struct pt_regs *regs)
-+{
-+	int cpu = smp_processor_id();
-+	__u64 tmp;
-+	__u32 ticks;
++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);
  
-+	/* Calculate how many ticks have passed. */
-+	tmp = S390_lowcore.int_clock - S390_lowcore.jiffy_timer;
-+	if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
-+		ticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1) + 1;
-+		S390_lowcore.jiffy_timer +=
-+			CLK_TICKS_PER_JIFFY * (__u64) ticks;
-+	} else if (tmp > CLK_TICKS_PER_JIFFY) {
-+		ticks = 2;
-+		S390_lowcore.jiffy_timer += 2*CLK_TICKS_PER_JIFFY;
-+	} else {
-+		ticks = 1;
-+		S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
-+	}
-+
-+	/* set clock comparator for next tick */
-+	tmp = S390_lowcore.jiffy_timer + CPU_DEVIATION;
-+	asm volatile ("SCKC %0" : : "m" (tmp));
-+
-+	irq_enter(cpu, 0);
-+
- #ifdef CONFIG_SMP
--extern __u16 boot_cpu_addr;
-+	/*
-+	 * Do not rely on the boot cpu to do the calls to do_timer.
-+	 * Spread it over all cpus instead.
-+	 */
-+	write_lock(&xtime_lock);
-+	if (S390_lowcore.jiffy_timer > xtime_cc) {
-+		__u32 xticks;
-+
-+		tmp = S390_lowcore.jiffy_timer - xtime_cc;
-+		if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
-+			xticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1);
-+			xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY;
-+		} else {
-+			xticks = 1;
-+			xtime_cc += CLK_TICKS_PER_JIFFY;
-+		}
-+		while (xticks--)
-+			do_timer(regs);
-+	}
-+	write_unlock(&xtime_lock);
-+	while (ticks--)
-+		update_process_times(user_mode(regs));
-+#else
-+	while (ticks--)
-+		do_timer(regs);
+ /*
+  * Called by a tty driver to register itself.
+@@ -2692,8 +2749,11 @@
+ #ifdef CONFIG_TN3215
+ 	con3215_init();
  #endif
-+	irq_exit(cpu, 0);
-+}
+-#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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
  
--static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
-+#ifdef CONFIG_VIRT_TIMER
-+void start_cpu_timer(void)
- {
--	int cpu = smp_processor_id();
-+	struct vtimer_queue *vt_list;
+ comment 'S/390 block device drivers'
  
--	irq_enter(cpu, 0);
-+	vt_list = &virt_cpu_timer[smp_processor_id()];
-+	set_vtimer(vt_list->idle);
-+}
+@@ -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
  
-+int stop_cpu_timer(void)
-+{
-+	__u64 done;
-+	struct vtimer_queue *vt_list;
-+
-+	vt_list = &virt_cpu_timer[smp_processor_id()];
+ 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
 +
-+	/* store progress */
-+	asm volatile ("STPT %0" : "=m" (done));
++    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
+ 
 +
- 	/*
--	 * set clock comparator for next tick
-+	 * If done is negative we do not stop the CPU timer
-+	 * because we will get instantly an interrupt that 
-+	 * will start the CPU timer again.
- 	 */
--        S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
--        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
-+	if (done & 1LL<<63)
-+		return 1;
-+	else
-+		vt_list->offset += vt_list->to_expire - done;
++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 kernel-source-2.4.27-2.4.27/drivers/s390/Makefile
+--- kernel-source-2.4.27-2.4.27.orig/drivers/s390/Makefile	2003-06-13 08:51:35.000000000 -0600
++++ kernel-source-2.4.27-2.4.27/drivers/s390/Makefile	2006-01-30 22:25:25.000000000 -0700
+@@ -4,11 +4,16 @@
  
--#ifdef CONFIG_SMP
--	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr)
--		write_lock(&xtime_lock);
-+	/* save the actual expire value */
-+	vt_list->idle = done;
+ O_TARGET := io.o
  
--	update_process_times(user_mode(regs));
-+	/* 
-+	 * We cannot halt the CPU timer, we just write a value that
-+	 * nearly never expires (only after 71 years) and re-write
-+	 * the stored expire value if we continue the timer 
-+	 */
-+	set_vtimer(VTIMER_MAX_SLICE);
-+	return 0;
-+}
+-subdir-y := block char misc net
++subdir-y := block char misc net scsi
+ subdir-m := $(subdir-y)
  
--	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) {
--		do_timer(regs);
--		write_unlock(&xtime_lock);
-+void set_vtimer(__u64 expires)
-+{
-+	asm volatile ("SPT %0" : : "m" (expires));
-+
-+	/* store expire time for this CPU timer */
-+	virt_cpu_timer[smp_processor_id()].to_expire = expires;
-+}
-+
-+/*
-+ * Sorted add to a list. List is linear searched until first bigger 
-+ * element is found.
-+ */
-+void list_add_sorted(struct vtimer_list *timer, struct list_head *head)
-+{
-+	struct vtimer_list *event;
+ 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
 +
-+	list_for_each_entry(event, head, entry) {
-+		if (event->expires > timer->expires) {
-+			list_add_tail(&timer->entry, &event->entry);
-+			return;
-+		}
- 	}
--#else
--	do_timer(regs);
--#endif
-+	list_add_tail(&timer->entry, head);
-+}
++obj-$(CONFIG_S390_CMF) += cmf.o
++export-objs += cmf.o
  
--	irq_exit(cpu, 0);
-+/*
-+ * Do the callback functions of expired vtimer events.
-+ * Called from within the interrupt handler.
-+ */
-+static void do_callbacks(struct list_head *cb_list, struct pt_regs *regs)
-+{
-+	struct vtimer_queue *vt_list;
-+	struct vtimer_list *event;
-+	struct list_head *ptr, *tmp;
-+	void (*fn)(unsigned long, struct pt_regs*);
-+	unsigned long data;
-+
-+	if (list_empty(cb_list))
-+		return;
-+
-+	vt_list = &virt_cpu_timer[smp_processor_id()];
-+
-+	list_for_each_safe(ptr, tmp, cb_list) {
-+		event = list_entry(ptr, struct vtimer_list, entry);
-+
-+		fn = event->function;
-+		data = event->data;
-+		fn(data, regs);
-+
-+		if (!event->interval)
-+			/* delete one shot timer */
-+			list_del_init(ptr);
-+		else {
-+			/* move interval timer back to list */
-+			spin_lock(&vt_list->lock);
-+			list_del_init(&event->entry);
-+			list_add_sorted(event, &vt_list->list);
-+			spin_unlock(&vt_list->lock);
-+		}
-+	}
- }
+ obj-y += $(foreach dir,$(subdir-y),$(dir)/s390-$(dir).o)
  
- /*
-- * Start the clock comparator on the current CPU
-+ * Handler for the virtual CPU timer.
-  */
-+static void do_cpu_timer_interrupt(struct pt_regs *regs, __u16 error_code)
-+{
-+	int cpu;
-+	__u64 next, delta;
-+	struct list_head *ptr, *tmp;
-+	struct vtimer_queue *vt_list;
-+	struct vtimer_list *event;
-+	/* the callback queue */
-+	struct list_head cb_list; 
-+
-+	INIT_LIST_HEAD(&cb_list);
-+	cpu = smp_processor_id();
-+	vt_list = &virt_cpu_timer[cpu];
-+
-+	/* walk timer list, fire all expired events */
-+	spin_lock(&vt_list->lock);
-+
-+	if (vt_list->to_expire < VTIMER_MAX_SLICE)
-+		vt_list->offset += vt_list->to_expire;
-+
-+	list_for_each_safe(ptr, tmp, &vt_list->list) {
-+		event = list_entry(ptr, struct vtimer_list, entry);
-+
-+		if (event->expires > vt_list->offset)
-+			/* found first unexpired event, leave */
-+			break;
-+
-+		/* re-charge interval timer, we have to add the offset */
-+		if (event->interval)
-+			event->expires = event->interval + vt_list->offset;
-+
-+		/* move expired timer to the callback queue */
-+		list_move_tail(ptr, &cb_list);
-+	}
-+	spin_unlock(&vt_list->lock);
-+	do_callbacks(&cb_list, regs);
-+
-+	/* next event is first in list */
-+	spin_lock(&vt_list->lock);
-+	if (!list_empty(&vt_list->list)) {
-+		ptr = vt_list->list.next;
-+		event = list_entry(ptr, struct vtimer_list, entry);
-+		next = event->expires - vt_list->offset;
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/Makefile kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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
+ 
+ include $(TOPDIR)/Rules.make
+ 
+@@ -31,4 +33,3 @@
+ 
+ dasd_diag_mod.o: $(dasd_diag_mod-objs)
+ 	$(LD) -r -o $@ $(dasd_diag_mod-objs)
+-
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/block/dasd.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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;
 +
-+		/* add the expired time from this interrupt handler 
-+		 * and the callback functions
-+		 */
-+		asm volatile ("STPT %0" : "=m" (delta));
-+		delta = 0xffffffffffffffffLL - delta + 1;
-+		vt_list->offset += delta;
-+		next -= delta;
-+	} else {
-+		vt_list->offset = 0;
-+		next = VTIMER_MAX_SLICE;
-+	}
-+	spin_unlock(&vt_list->lock);
-+	set_vtimer(next);
-+}
-+#endif
++	memset (major_info->gendisk.label_arr, 0,
++		DASD_PER_MAJOR * sizeof(devfs_handle_t));
 +
-+/*
-+ * Start the clock comparator and the virtual CPU timer 
-+ * on the current CPU
-+ */
- void init_cpu_timer(void)
- {
- 	unsigned long cr0;
-+	__u64 timer;
-+#ifdef CONFIG_VIRT_TIMER
-+	struct vtimer_queue *vt_list;
-+#endif
+         /* register blockdevice */
+ 	rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations);
+ 	if (rc < 0) {
+@@ -861,6 +870,9 @@
+ 	}
  
--	S390_lowcore.jiffy_timer = (__u64) jiffies * CLK_TICKS_PER_JIFFY;
--	S390_lowcore.jiffy_timer += init_timer_cc + CLK_TICKS_PER_JIFFY;
--	asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
--        /* allow clock comparator timer interrupt */
--        asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory");
--        cr0 |= 0x800;
--        asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory");
-+	timer = init_timer_cc + (__u64) jiffies * CLK_TICKS_PER_JIFFY;
-+	S390_lowcore.jiffy_timer = timer + CLK_TICKS_PER_JIFFY;
-+	timer += CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
-+	asm volatile ("SCKC %0" : : "m" (timer));
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ) 
-+	atomic_inc(&active_cpu_timer);
-+#endif
-+	/* allow clock comparator timer interrupt */
-+	asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory");
-+	cr0 |= 0x800;
-+	asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory");
-+
-+#ifdef CONFIG_VIRT_TIMER
-+	/* kick the virtual timer */
-+	timer = VTIMER_MAX_SLICE;
-+	asm volatile ("SPT %0" : : "m" (timer));
-+	__ctl_store(cr0, 0, 0);
-+	cr0 |= 0x400;
-+	__ctl_load(cr0, 0, 0);
+ out_reg_blkdev:
++	kfree (major_info->gendisk.label_arr);
 +
-+	vt_list = &virt_cpu_timer[smp_processor_id()];
-+	INIT_LIST_HEAD(&vt_list->list);
-+	spin_lock_init(&vt_list->lock);
-+	vt_list->to_expire = 0;
-+	vt_list->offset = 0;
-+	vt_list->idle = 0;
-+#endif
- }
++out_gd_label_arr:
+ 	kfree (major_info->gendisk.flags);
  
- /*
-@@ -196,39 +446,374 @@
-  * the boot cpu.
-  */
- void __init time_init(void)
--{
--        __u64 set_time_cc;
-+{	
-+	__u64 set_time_cc;
- 	int cc;
+ out_gd_flags:
+@@ -916,6 +928,7 @@
+ 		major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
+ 	}
  
--        /* kick the TOD clock */
--        asm volatile ("STCK 0(%1)\n\t"
--                      "IPM  %0\n\t"
--                      "SRL  %0,28" : "=r" (cc) : "a" (&init_timer_cc) 
-+	/* kick the TOD clock */
-+	asm volatile ("STCK 0(%1)\n\t"
-+		      "IPM  %0\n\t"
-+		      "SRL  %0,28" : "=r" (cc) : "a" (&init_timer_cc) 
- 				   : "memory", "cc");
--        switch (cc) {
--        case 0: /* clock in set state: all is fine */
--                break;
--        case 1: /* clock in non-set state: FIXME */
--                printk("time_init: TOD clock in non-set state\n");
--                break;
--        case 2: /* clock in error state: FIXME */
--                printk("time_init: TOD clock in error state\n");
--                break;
--        case 3: /* clock in stopped or not-operational state: FIXME */
--                printk("time_init: TOD clock stopped/non-operational\n");
--                break;
--        }
-+	switch (cc) {
-+	case 0: /* clock in set state: all is fine */
-+		break;
-+	case 1: /* clock in non-set state: FIXME */
-+		printk("time_init: TOD clock in non-set state\n");
-+		break;
-+	case 2: /* clock in error state: FIXME */
-+		printk("time_init: TOD clock in error state\n");
-+		break;
-+	case 3: /* clock in stopped or not-operational state: FIXME */
-+		printk("time_init: TOD clock stopped/non-operational\n");
-+		break;
-+	}
++	kfree (major_info->gendisk.label_arr);
+ 	kfree (major_info->gendisk.flags);
+ 	kfree (major_info->gendisk.de_arr);
  
- 	/* set xtime */
--        set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
--                      (0x3c26700LL*1000000*4096);
--        tod_to_timeval(set_time_cc, &xtime);
-+	xtime_cc = init_timer_cc + CLK_TICKS_PER_JIFFY;
-+	set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
-+		      (0x3c26700LL*1000000*4096);
-+	tod_to_timeval(set_time_cc, &xtime);
+@@ -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);
++
+         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;
  
--        /* request the 0x1004 external interrupt */
--        if (register_early_external_interrupt(0x1004, do_comparator_interrupt,
-+	/* request the clock comparator external interrupt */
-+	if (register_early_external_interrupt(0x1004, NULL,
-+					      &ext_int_info_cc) != 0)
-+		panic("Couldn't request external interrupt 0x1004");
+-	/* 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);
+-        
 +
-+#ifdef CONFIG_VIRT_TIMER
-+	/* request the cpu timer external interrupt */
-+	if (register_early_external_interrupt(0x1005, do_cpu_timer_interrupt,
- 					      &ext_int_info_timer) != 0)
--                panic("Couldn't request external interrupt 0x1004");
-+		panic("Couldn't request external interrupt 0x1005");
-+#endif
++	cmf_device_init(&device->cdev, devno);
++       
+         /* allocate pages for lowmem pool */
+         for (i = 0; i < DASD_LOWMEM_PAGES; i++) {
+                 
+@@ -3906,7 +3929,8 @@
  
--        /* init CPU timer */
--        init_cpu_timer();
-+	init_cpu_timer();
+         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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 @@
+ 
+                         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-2.4.27.orig/drivers/s390/block/dasd_cmb.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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>
 +
-+#ifdef CONFIG_VIRT_TIMER
-+void init_virt_timer(struct vtimer_list *timer)
++#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)
 +{
-+	timer->magic = VTIMER_MAGIC;
-+	timer->function = NULL;
-+	INIT_LIST_HEAD(&timer->entry);
-+	spin_lock_init(&timer->lock);
++	cdev->callback = NULL;
++	cmf_setup_return = set_cmf(cdev, 2);
++	complete(&cmf_setup_completion);
 +}
 +
-+static inline int check_vtimer(struct vtimer_list *timer)
++static void
++dasd_cmf_disable_callback(struct cmf_device *cdev)
 +{
-+	if (timer->magic != VTIMER_MAGIC)
-+		return -EINVAL;
-+	return 0;
++	cdev->callback = NULL;
++	cmf_setup_return = set_cmf(cdev, 0);
++	complete(&cmf_setup_completion);
 +}
 +
-+static inline int vtimer_pending(struct vtimer_list *timer)
++static inline int
++dasd_cmf_device_busy(struct dasd_device_t *device)
 +{
-+	return (!list_empty(&timer->entry));
++	ccw_req_t *cqr;
++	for (cqr = device->queue.head; cqr; cqr = cqr->next) {
++		if (cqr->status == CQR_STATUS_IN_IO)
++			return 1;
++	}
++	return 0;
 +}
 +
-+/*
-+ * this function should only run on the specified CPU
-+ */
-+static void internal_add_vtimer(struct vtimer_list *timer)
++static int
++dasd_ioctl_cmf_enable(void *inp, int no, long args)
 +{
-+	unsigned long flags;
-+	__u64 done;
-+	struct vtimer_list *event;
-+	struct vtimer_queue *vt_list;
-+
-+	vt_list = &virt_cpu_timer[timer->cpu];
-+	spin_lock_irqsave(&vt_list->lock, flags);
++	struct dasd_device_t *device;
++	int ret;
 +
-+	if (timer->cpu != smp_processor_id())
-+		printk("internal_add_vtimer: BUG, running on wrong CPU");
++	device = dasd_device_from_kdev (((struct inode*)inp)->i_rdev);
++	if (!device)
++		return -EINVAL;
++	
++	if (down_interruptible(&cmf_setup_mutex))
++		return -ERESTARTSYS;
 +
-+	/* if list is empty we only have to set the timer */
-+	if (list_empty(&vt_list->list)) {
-+		/* reset the offset, this may happen if the last timer was 
-+		 * just deleted by mod_virt_timer and the interrupt
-+		 * didn't happen until here
-+		 */
-+		vt_list->offset = 0;
-+		goto fire;
++	/* 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;
 +
-+	/* save progress */
-+	asm volatile ("STPT %0" : "=m" (done));
-+
-+	/* calculate completed work */
-+	done = vt_list->to_expire - done + vt_list->offset;
-+	vt_list->offset = 0;
-+
-+	list_for_each_entry(event, &vt_list->list, entry)
-+		event->expires -= done;
++	MOD_INC_USE_COUNT;
 +
-+ fire:
-+	list_add_sorted(timer, &vt_list->list);
++	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;
++	}
 +
-+	/* get first element, which is the next vtimer slice */
-+	event = list_entry(vt_list->list.next, struct vtimer_list, entry);
++	if (ret) {
++		disable_cmf(&device->cdev);
++		MOD_DEC_USE_COUNT;
++	}
 +
-+	set_vtimer(event->expires);
-+	spin_unlock_irqrestore(&vt_list->lock, flags);
++out_reset:
++	cmf_reset(&device->cdev);
++out:
++	up(&cmf_setup_mutex);
++	return ret;
 +}
 +
-+static inline int prepare_vtimer(struct vtimer_list *timer)
++static int
++dasd_ioctl_cmf_disable(void *inp, int no, long args)
 +{
-+	if (check_vtimer(timer) || !timer->function) {
-+		printk("add_virt_timer: uninitialized timer\n");
-+		return -EINVAL;
-+	}
++	struct dasd_device_t *device;
++	int ret;
 +
-+	if (!timer->expires || timer->expires > VTIMER_MAX_SLICE) {
-+		printk("add_virt_timer: invalid timer expire value!\n");
++	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 (vtimer_pending(timer)) {
-+		printk("add_virt_timer: timer pending\n");
-+		return -EBUSY;
++	if(!ret) {
++		disable_cmf(&device->cdev);
++		MOD_DEC_USE_COUNT;
 +	}
++	up(&cmf_setup_mutex);
++	return ret;
 +
-+	timer->cpu = smp_processor_id();
-+	return 0;
 +}
 +
-+/*
-+ * add_virt_timer - add an oneshot virtual CPU timer
-+ */
-+void add_virt_timer(void *new)
++static int
++dasd_ioctl_readall_cmb(void *inp, int no, long args)
 +{
-+	struct vtimer_list *timer;
-+
-+	timer = (struct vtimer_list *)new;
++	struct dasd_device_t *device;
++	struct cmbdata * udata;
++	struct cmbdata data;
++	size_t size;
++	int ret;
 +
-+	if (prepare_vtimer(timer) < 0)
-+		return;
++	device = dasd_device_from_kdev (((struct inode*)inp)->i_rdev);
++	if (!device)
++		return -EINVAL;
++	udata = (void *) args;
++	size = _IOC_SIZE(no);
 +
-+	timer->interval = 0;
-+	internal_add_vtimer(timer);
++	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;
 +}
 +
-+/*
-+ * add_virt_timer_int - add an interval virtual CPU timer
-+ */
-+void add_virt_timer_periodic(void *new)
++/* 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)
 +{
-+	struct vtimer_list *timer;
++	int ret;
++	ret = dasd_ioctl_no_register(THIS_MODULE, no, handler);
++	if (ret)
++		return ret;
 +
-+	timer = (struct vtimer_list *)new;
++	ret = register_ioctl32_conversion(no, sys_ioctl);
++	if (ret)
++		dasd_ioctl_no_unregister(THIS_MODULE, no, handler);
 +
-+	if (prepare_vtimer(timer) < 0)
-+		return;
++	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);
 +
-+	timer->interval = timer->expires;
-+	internal_add_vtimer(timer);
 +}
 +
-+/* 
-+ * If we change a pending timer the function must be called on the CPU 
-+ * where the timer is running on, e.g. by smp_call_function_on()
-+ *
-+ * The original mod_timer adds the timer if it is not pending. For compatibility
-+ * we do the same. The timer will be added on the current CPU as a oneshot timer.
-+ * 
-+ * returns whether it has modified a pending timer (1) or not (0)
-+ */
-+int mod_virt_timer(struct vtimer_list *timer, __u64 expires)
++static void
++dasd_cmf_exit(void)
 +{
-+	struct vtimer_queue *vt_list;
-+	unsigned long flags;
++	ioctl_unreg(BIODASDCMFENABLE,  dasd_ioctl_cmf_enable);
++	ioctl_unreg(BIODASDCMFDISABLE, dasd_ioctl_cmf_disable);
++	ioctl_unreg(BIODASDREADALLCMB, dasd_ioctl_readall_cmb);
++}
 +
-+	if (check_vtimer(timer) || !timer->function) {
-+		printk("mod_virt_timer: uninitialized timer\n");
-+		return	-EINVAL;
-+	}
++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;
 +
-+	if (!expires || expires > VTIMER_MAX_SLICE) {
-+		printk("mod_virt_timer: invalid expire range\n");
-+		return -EINVAL;
-+	}
++	return 0;
++err:
++	dasd_cmf_exit();
 +
-+	/*
-+	 * This is a common optimization triggered by the
-+	 * networking code - if the timer is re-modified
-+	 * to be the same thing then just return:
-+	 */
-+	if (timer->expires == expires && vtimer_pending(timer)) 
-+		return 1;
++	return ret;
++}
 +
-+	/* disable interrupts before test if timer is pending */
-+	vt_list = &virt_cpu_timer[smp_processor_id()];
-+	spin_lock_irqsave(&vt_list->lock, flags);
++module_init(dasd_cmf_init);
++module_exit(dasd_cmf_exit);
 +
-+	/* if timer isn't pending add it on the current CPU */
-+	if (!vtimer_pending(timer)) {
-+		spin_unlock_irqrestore(&vt_list->lock, flags);
-+		/* we do not activate an interval timer with mod_virt_timer */
-+		timer->interval = 0;
-+		timer->expires = expires;
-+		timer->cpu = smp_processor_id();
-+		internal_add_vtimer(timer);
-+		return 0;
-+	}
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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;
 +
-+	/* check if we run on the right CPU */
-+	if (timer->cpu != smp_processor_id()) {
-+		printk("mod_virt_timer: running on wrong CPU, check your code\n"); 
-+		spin_unlock_irqrestore(&vt_list->lock, flags);
-+		return -EINVAL;
-+	}
+ 	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;
 +
-+	list_del_init(&timer->entry);
-+	timer->expires = expires;
++        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;
 +
-+	/* also change the interval if we have an interval timer */
-+	if (timer->interval) 
-+		timer->interval = expires;
+ 	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;
+-        }
 +
-+	/* the timer can't expire anymore so we can release the lock */
-+	spin_unlock_irqrestore(&vt_list->lock, flags);
-+	internal_add_vtimer(timer);
-+	return 1;
-+}
+ 	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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/block/dcssblk.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,780 @@
 +/*
-+ * delete a virtual timer
++ * dcssblk.c -- the S/390 block driver for dcss memory
 + *
-+ * returns whether the deleted timer was pending (1) or not (0)
++ * Author: Carsten Otte
 + */
-+int del_virt_timer(struct vtimer_list *timer)
-+{
-+	unsigned long flags;
-+	struct vtimer_queue *vt_list;
-+
-+	if (check_vtimer(timer)) {
-+		printk("del_virt_timer: timer not initialized\n");
-+		return -EINVAL;
-+	}
 +
-+	/* check if timer is pending */
-+	if (!vtimer_pending(timer))
-+		return 0;
++#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>
 +
-+	if (timer->cpu > smp_num_cpus) {
-+		printk("del_virt_timer: CPU not present!\n");
-+		return -1;
-+	}
++#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"
 +
-+	vt_list = &virt_cpu_timer[timer->cpu];
-+	spin_lock_irqsave(&vt_list->lock, flags);
++#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];
 +
-+	/* we don't interrupt a running timer, just let it expire! */
-+	list_del_init(&timer->entry);
++static int dcssblk_open (struct inode *inode, struct file *filp);
++static int dcssblk_release (struct inode *inode, struct file *filp);
 +
-+	/* last timer removed */
-+	if (list_empty(&vt_list->list)) {
-+		vt_list->to_expire = 0;
-+		vt_list->offset = 0;
-+	}
++static struct block_device_operations dcssblk_devops =
++	{
++		owner:   THIS_MODULE,
++		open:    dcssblk_open,
++		release: dcssblk_release,
++	};
 +
-+	spin_unlock_irqrestore(&vt_list->lock, flags);
-+	return 1;
-+}
++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;
 +
-+#ifdef CONFIG_NO_IDLE_HZ
-+
-+/*
-+ * Start the HZ tick on the current CPU.
-+ * Only cpu_idle may call this function.
-+ */
-+void start_hz_timer(void)
-+{
-+	__u64 tmp;
-+	__u32 ticks;
-+
-+	if (sysctl_hz_timer != 0)
-+		return;
-+	
-+	irq_enter(smp_processor_id(), 0);
++typedef struct _dcssblk_proc_private_t {
++	char name[8];
++	int bytes_written;
++} dcssblk_proc_private_t;
 +
-+	/* Calculate how many ticks have passed */
-+	asm volatile ("STCK 0(%0)" : : "a" (&tmp) : "memory", "cc");
-+	tmp = tmp + CLK_TICKS_PER_JIFFY - S390_lowcore.jiffy_timer;
-+	ticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1);
-+	S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY * (__u64) ticks;
++static int dcssblk_major;
++static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices);
++static rwlock_t dcssblk_devices_lock = RW_LOCK_UNLOCKED;
 +
-+	/* Set the clock comparator to the next tick. */
-+	tmp = S390_lowcore.jiffy_timer + CPU_DEVIATION;
-+	asm volatile ("SCKC %0" : : "m" (tmp));
 +
-+	/* Charge the ticks. */
-+	if (ticks > 0) {
-+#ifdef CONFIG_SMP
-+		write_lock(&xtime_lock);
-+		if (S390_lowcore.jiffy_timer > xtime_cc) {
-+			__u32 xticks;
++MODULE_LICENSE("GPL");
 +
-+			tmp = S390_lowcore.jiffy_timer - xtime_cc;
-+			xticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1);
-+			xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY;
-+			do_timer_ticks(xticks);
-+		}
-+		write_unlock(&xtime_lock);
-+#else
-+		do_timer_ticks(0, ticks);
-+#endif
-+		update_process_times_us(0, ticks);
++/*
++ * 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
 +	}
-+	irq_exit(smp_processor_id(), 0);
-+}	
-+
-+extern spinlock_t timerlist_lock;
++	if (found)
++		return -EBUSY;
++	device->kdev = MKDEV(dcssblk_major, minor);
++	return 0;
++}
 +
 +/*
-+ * Stop the HZ tick on the current CPU.
-+ * Only cpu_idle may call this function.
++ * get the dcss_device_t from dcssblk_devices 
++ * with the struct device supplied.
++ * needs to be called with read_lock(dcssblk_devices_lock)
 + */
-+void stop_hz_timer(void)
-+{
-+	__u64 timer;
++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;
++}
 +
-+	if (sysctl_hz_timer != 0)
-+		return;
-+	
-+	if (atomic_read(&active_cpu_timer) == 0) {
-+		/* 
-+		 * The last active cpu is going to sleep. Setup the clock
-+		 * comparator for the next event if nothing on tq_timer
-+		 * is pending. If something is pending on tq_timer then
-+		 * don't change the clock comparator as it is setup for
-+		 * the next timer tick already.
-+		 */
-+		if (!TQ_ACTIVE(tq_timer)) {
-+			spin_lock(&timerlist_lock);
-+			timer = (__u64) next_timer_event()->expires;
-+			timer *= CLK_TICKS_PER_JIFFY;
-+			timer += init_timer_cc;
-+			asm volatile ("SCKC %0" : : "m" (timer));
-+			spin_unlock(&timerlist_lock);
++/*
++ * 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);
 +		}
-+	} else {
-+		timer = (__u64) -1;
-+		asm volatile ("SCKC %0" : : "m" (timer));
-+	}
++	return NULL;
 +}
-+#endif
 +
-+void do_monitor_call(struct pt_regs *regs, long interruption_code)
-+{
-+	/* disable monitor call class 0 */
-+	__ctl_clear_bit(8, 15);
++static void dcssblk_do_save (dcss_device_t* device) {
++	segment_replace (device->name);
++}
 +
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)	
-+	atomic_inc(&active_cpu_timer);
-+#endif	
++/*
++ * 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;
 +
-+#ifdef CONFIG_VIRT_TIMER
-+	start_cpu_timer();
-+#endif
-+#ifdef CONFIG_NO_IDLE_HZ
-+	start_hz_timer();
++	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;
++
 +}
-=== arch/s390/kernel/smp.c
-==================================================================
---- arch/s390/kernel/smp.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/kernel/smp.c   (/trunk/2.4.27)   (revision 52)
-@@ -92,7 +92,7 @@
- 
- extern void reipl(unsigned long devno);
- 
--static sigp_ccode smp_ext_bitcall(int, ec_bit_sig);
-+static void smp_ext_bitcall(int, ec_bit_sig);
- static void smp_ext_bitcall_others(ec_bit_sig);
- 
- /*
-@@ -131,7 +131,7 @@
-  * in the system.
-  */
- 
--int smp_call_function (void (*func) (void *info), void *info, int nonatomic,
-+int smp_call_function(void (*func) (void *info), void *info, int nonatomic,
- 			int wait)
- /*
-  * [SUMMARY] Run a function on all other CPUs.
-@@ -162,7 +162,7 @@
- 	spin_lock_bh(&call_lock);
- 	call_data = &data;
- 	/* Send a message to all other CPUs and wait for them to respond */
--        smp_ext_bitcall_others(ec_call_function);
-+	smp_ext_bitcall_others(ec_call_function);
- 
- 	/* Wait for response */
- 	while (atomic_read(&data.started) != cpus)
-@@ -176,9 +176,54 @@
- 	return 0;
- }
- 
++
 +/*
-+ * Call a function on one CPU
-+ * cpu : the CPU the function should be executed on
-+ *
-+ * You must not call this function with disabled interrupts or from a
-+ * hardware interrupt handler, you may call it from a bottom half handler.
++ * device attribute for showing status of "shared / non-shared"
 + */
-+int smp_call_function_on(void (*func) (void *info), void *info,
-+                         int nonatomic, int wait, int cpu)
-+{
-+	struct call_data_struct data;
++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");
++}
 +
-+	if (!atomic_read(&smp_commenced))
-+		return 0;
++/*
++ * 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;
 +
-+	if (smp_processor_id() == cpu) {
-+		/* direct call to function */
-+		func(info);
-+		return 0;
++	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;
++}
 +
-+	data.func = func;
-+	data.info = info;
++/*
++ * 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");
++}
 +
-+	atomic_set(&data.started, 0);
-+	data.wait = wait;
-+	if (wait)
-+		atomic_set(&data.finished, 0);
++/*
++ * 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;
 +
-+	spin_lock_bh(&call_lock);
-+	call_data = &data;
-+	smp_ext_bitcall(cpu, ec_call_function);
++	MOD_INC_USE_COUNT; // released at end of func
 +
-+	/* Wait for response */
-+	while (atomic_read(&data.started) != 1)
-+		barrier();
++	/*
++	 * 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;
++	}
++	
 +
-+	if (wait)
-+		while (atomic_read(&data.finished) != 1)
-+			barrier();
++	/*
++	 * 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
 +
-+	spin_unlock_bh(&call_lock);
-+	return 0;
++	/*
++	 * 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;
 +}
 +
- static inline void do_send_stop(void)
- {
--        u32 dummy;
-+        unsigned long dummy;
-         int i;
- 
-         /* stop all processors */
-@@ -199,7 +244,7 @@
- static inline void do_store_status(void)
- {
-         unsigned long low_core_addr;
--        u32 dummy;
-+        unsigned long dummy;
-         int i;
- 
-         /* store status of all processors in their lowcores (real 0) */
-@@ -328,42 +373,41 @@
- }
- 
- /*
-- * Send an external call sigp to another cpu and return without waiting
-+ * Send an external call sigp to another cpu and wait
-  * for its completion.
-  */
--static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig)
-+static void smp_ext_bitcall(int cpu, ec_bit_sig sig)
- {
--        struct _lowcore *lowcore = get_cpu_lowcore(cpu);
--        sigp_ccode ccode;
-+	struct _lowcore *lowcore = get_cpu_lowcore(cpu);
- 
--        /*
--         * Set signaling bit in lowcore of target cpu and kick it
--         */
--        atomic_set_mask(1<<sig, &lowcore->ext_call_fast);
--        ccode = signal_processor(cpu, sigp_external_call);
--        return ccode;
++/*
++ * 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;
 +	/*
-+	 * Set signaling bit in lowcore of target cpu and kick it
++	 * fetch buffer from userland
 +	 */
-+	atomic_set_mask(1<<sig, &lowcore->ext_call_fast);
-+	while(signal_processor(cpu, sigp_external_call) == sigp_busy)
-+		udelay(10);
- }
- 
- /*
-  * Send an external call sigp to every other cpu in the system and
-- * return without waiting for its completion.
-+ * wait for its completion.
-  */
- static void smp_ext_bitcall_others(ec_bit_sig sig)
- {
--        struct _lowcore *lowcore;
--        int i;
-+	struct _lowcore *lowcore;
-+	int i;
- 
--        for (i = 0; i < smp_num_cpus; i++) {
--                if (smp_processor_id() == i)
--                        continue;
--                lowcore = get_cpu_lowcore(i);
--                /*
--                 * Set signaling bit in lowcore of target cpu and kick it
--                 */
--                atomic_set_mask(1<<sig, &lowcore->ext_call_fast);
--                while (signal_processor(i, sigp_external_call) == sigp_busy)
-+	for (i = 0; i < smp_num_cpus; i++) {
-+		if (smp_processor_id() == i)
-+			continue;
-+		lowcore = get_cpu_lowcore(i);
-+		/*
-+		 * Set signaling bit in lowcore of target cpu and kick it
-+		 */
-+		atomic_set_mask(1<<sig, &lowcore->ext_call_fast);
-+		while (signal_processor(i, sigp_external_call) == sigp_busy)
- 			udelay(10);
--        }
++	buf = kmalloc(count,GFP_KERNEL);
++	if (buf == NULL) {
++		rc = -ENOMEM;
++		goto out_nobuf;
 +	}
- }
- 
- /*
-=== arch/s390/kernel/setup.c
-==================================================================
---- arch/s390/kernel/setup.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/kernel/setup.c   (/trunk/2.4.27)   (revision 52)
-@@ -276,9 +276,9 @@
- 
- static int __init conmode_setup(char *str)
- {
--#if defined(CONFIG_HWC_CONSOLE)
--	if (strncmp(str, "hwc", 4) == 0)
--                SET_CONSOLE_HWC;
-+#if defined(CONFIG_SCLP_CONSOLE)
-+	if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
-+                SET_CONSOLE_SCLP;
- #endif
- #if defined(CONFIG_TN3215_CONSOLE)
- 	if (strncmp(str, "3215", 5) == 0)
-@@ -310,8 +310,8 @@
- 		 */
- 		cpcmd("TERM CONMODE 3215", NULL, 0);
- 		if (ptr == NULL) {
--#if defined(CONFIG_HWC_CONSOLE)
--			SET_CONSOLE_HWC;
-+#if defined(CONFIG_SCLP_CONSOLE)
-+			SET_CONSOLE_SCLP;
- #endif
- 			return;
- 		}
-@@ -320,16 +320,16 @@
- 			SET_CONSOLE_3270;
- #elif defined(CONFIG_TN3215_CONSOLE)
- 			SET_CONSOLE_3215;
--#elif defined(CONFIG_HWC_CONSOLE)
--			SET_CONSOLE_HWC;
-+#elif defined(CONFIG_SCLP_CONSOLE)
-+			SET_CONSOLE_SCLP;
- #endif
- 		} else if (strncmp(ptr + 8, "3215", 4) == 0) {
- #if defined(CONFIG_TN3215_CONSOLE)
- 			SET_CONSOLE_3215;
- #elif defined(CONFIG_TN3270_CONSOLE)
- 			SET_CONSOLE_3270;
--#elif defined(CONFIG_HWC_CONSOLE)
--			SET_CONSOLE_HWC;
-+#elif defined(CONFIG_SCLP_CONSOLE)
-+			SET_CONSOLE_SCLP;
- #endif
- 		}
-         } else if (MACHINE_IS_P390) {
-@@ -339,8 +339,8 @@
- 		SET_CONSOLE_3270;
- #endif
- 	} else {
--#if defined(CONFIG_HWC_CONSOLE)
--		SET_CONSOLE_HWC;
-+#if defined(CONFIG_SCLP_CONSOLE)
-+		SET_CONSOLE_SCLP;
- #endif
- 	}
- }
-@@ -383,21 +383,25 @@
- 
- /*
-  * Reboot, halt and power_off stubs. They just call _machine_restart,
-- * _machine_halt or _machine_power_off. 
-+ * _machine_halt or _machine_power_off after making sure that all pending
-+ * printks reached their destination. 
-  */
- 
- void machine_restart(char *command)
- {
-+	console_unblank();
- 	_machine_restart(command);
- }
- 
- void machine_halt(void)
- {
-+	console_unblank();
- 	_machine_halt();
- }
- 
- void machine_power_off(void)
- {
-+	console_unblank();
- 	_machine_power_off();
- }
- 
-=== arch/s390/kernel/process.c
-==================================================================
---- arch/s390/kernel/process.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/kernel/process.c   (/trunk/2.4.27)   (revision 52)
-@@ -43,13 +43,27 @@
- #include <asm/io.h>
- #include <asm/processor.h>
- #include <asm/irq.h>
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+#include <asm/timer.h>
-+#endif
- 
- asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
- 
-+#ifdef CONFIG_VIRT_TIMER
-+extern int stop_cpu_timer(void);
-+#endif
-+
-+#ifdef CONFIG_NO_IDLE_HZ
-+extern void stop_hz_timer(void);
++	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;
++}
 +
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+extern atomic_t active_cpu_timer;
-+#endif
++/*
++ * device attribute for listing devices
++ */
++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;
 +
- /*
-  * The idle loop on a S390...
-  */
--
- int cpu_idle(void *unused)
- {
- 	psw_t wait_psw;
-@@ -68,9 +82,37 @@
- 			continue;
- 		}
- 
-+#ifdef CONFIG_VIRT_TIMER
- 		/* 
-+		 * virtual CPU timer should not progress while its CPU is idle
-+		 */
-+		if (stop_cpu_timer()) {
-+			__sti();
-+			continue;
-+		}
-+#endif
++	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;
++}
 +
-+/* 
-+ * active_cpu_timer is used by stop_hz_timer to determine if the last
-+ * CPU is gone. We have to update this value also if we use the virtual
-+ * CPU timer because both use monitor calls.
-+ */ 
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+		atomic_dec(&active_cpu_timer);
-+#endif
-+		
-+#ifdef CONFIG_NO_IDLE_HZ
-+		stop_hz_timer();
-+#endif
++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;
++}
 +
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+		/* enable monitor call class 0 */
-+		__ctl_set_bit(8, 15);
-+#endif
++static int dcssblk_release (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 --;
++	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;
++}
 +
-+		/* 
- 		 * Wait for external, I/O or machine check interrupt and
--		 * switch of machine check bit after the wait has ended.
-+		 * switch off machine check bit after the wait has ended.
- 		 */
- 		wait_psw.mask = _WAIT_PSW_MASK;
- 		asm volatile (
-@@ -86,6 +128,10 @@
- 			"    lpsw 0(%1)\n"
- 			"2:"
- 			: "=&a" (reg) : "a" (&wait_psw) : "memory", "cc" );
-+		/*
-+		 * start_hz_timer is called by monitor call in entry.S
-+		 * if stop_hz_timer switched off the regular HZ interrupts
-+		 */
- 	}
- }
- 
-=== arch/s390/kernel/entry.S
-==================================================================
---- arch/s390/kernel/entry.S   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/kernel/entry.S   (/trunk/2.4.27)   (revision 52)
-@@ -690,7 +690,12 @@
- io_int_handler:
- 	SAVE_ALL_BASE
-         SAVE_ALL __LC_IO_OLD_PSW,0
-+	mc 0,0
-         GET_CURRENT               # load pointer to task_struct to R9
-+	stck	__LC_INT_CLOCK
-+	clc	__LC_INT_CLOCK(8),__LC_JIFFY_TIMER
-+	bhe	BASED(io_handle_tick)
-+io_call_handler:
-         l       %r1,BASED(.Ldo_IRQ)        # load address of do_IRQ
-         la      %r2,SP_PTREGS(%r15) # address of register-save area
-         sr      %r3,%r3
-@@ -725,6 +730,15 @@
-         RESTORE_ALL 0
- 
- #
-+# account tick
-+#
-+io_handle_tick:
-+	l	%r1,BASED(.Laccount_ticks)
-+	la	%r2,SP_PTREGS(%r15)    # address of register-save area
-+	la	%r14,BASED(io_call_handler)
-+	br	%r1
++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;
 +
-+#
- # call do_softirq
- #
- io_handle_bottom_half:
-@@ -758,8 +772,13 @@
- ext_int_handler:
- 	SAVE_ALL_BASE
-         SAVE_ALL __LC_EXT_OLD_PSW,0
--        GET_CURRENT                    # load pointer to task_struct to R9
-+	mc 0, 0
-+	GET_CURRENT                    # load pointer to task_struct to R9
- 	lh	%r6,__LC_EXT_INT_CODE  # get interruption code
-+	stck	__LC_INT_CLOCK
-+	clc	__LC_INT_CLOCK(8),__LC_JIFFY_TIMER
-+	bhe	BASED(ext_handle_tick)
-+ext_call_handler:
- 	lr	%r1,%r6		       # calculate index = code & 0xff
- 	n	%r1,BASED(.Lc0xff)
- 	sll	%r1,2
-@@ -770,7 +789,8 @@
- ext_int_loop:
- 	ch	%r6,8(%r7)	       # compare external interrupt code
- 	bne	BASED(ext_int_next)
--	l	%r1,4(%r7)	       # get handler address
-+	icm	%r1,15,4(%r7)	       # get handler address
-+	bz	BASED(ext_int_next)
- 	la	%r2,SP_PTREGS(%r15)    # address of register-save area
- 	lr	%r3,%r6		       # interruption code
- 	basr	%r14,%r1	       # call handler
-@@ -779,6 +799,15 @@
- 	bnz	BASED(ext_int_loop)
- 	b	BASED(io_return)
- 
-+#
-+# account tick
-+#
-+ext_handle_tick:
-+	l	%r1,BASED(.Laccount_ticks)
-+	la	%r2,SP_PTREGS(%r15)    # address of register-save area
-+	la	%r14,BASED(ext_call_handler)
-+	br	%r1
++	read_lock(&dcssblk_devices_lock);
++	dcssdev = dcssblk_get_device_by_minor (MINOR(bh->b_rdev));
++	read_unlock(&dcssblk_devices_lock);
 +
- /*
-  * Machine check handler routines
-  */
-@@ -787,6 +816,7 @@
- mcck_int_handler:
- 	SAVE_ALL_BASE
-         SAVE_ALL __LC_MCK_OLD_PSW,0
-+	mc 0, 0	
- 	l       %r1,BASED(.Ls390_mcck)
- 	basr    %r14,%r1	  # call machine check handler
- mcck_return:
-@@ -869,4 +899,4 @@
- .Lvfork:       .long  sys_vfork
- 
- .Lschedtail:   .long  schedule_tail
--
-+.Laccount_ticks:.long account_ticks
-=== arch/s390/kernel/s390_ksyms.c
-==================================================================
---- arch/s390/kernel/s390_ksyms.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/kernel/s390_ksyms.c   (/trunk/2.4.27)   (revision 52)
-@@ -7,9 +7,13 @@
- #include <linux/module.h>
- #include <linux/smp.h>
- #include <asm/checksum.h>
-+#include <asm/cpcmd.h>
- #include <asm/delay.h>
- #include <asm/setup.h>
- #include <asm/softirq.h>
-+#ifdef CONFIG_VIRT_TIMER
-+#include <asm/timer.h>
-+#endif
- #if CONFIG_IP_MULTICAST
- #include <net/arp.h>
- #endif
-@@ -20,6 +24,7 @@
- EXPORT_SYMBOL_NOVERS(_oi_bitmap);
- EXPORT_SYMBOL_NOVERS(_ni_bitmap);
- EXPORT_SYMBOL_NOVERS(_zb_findmap);
-+EXPORT_SYMBOL_NOVERS(empty_zero_page);
- EXPORT_SYMBOL_NOVERS(__copy_from_user_asm);
- EXPORT_SYMBOL_NOVERS(__copy_to_user_asm);
- EXPORT_SYMBOL_NOVERS(__clear_user_asm);
-@@ -66,3 +71,22 @@
- EXPORT_SYMBOL(get_storage_key);
- EXPORT_SYMBOL_NOVERS(do_call_softirq);
- EXPORT_SYMBOL(sys_wait4);
-+EXPORT_SYMBOL(smp_call_function_on);
-+EXPORT_SYMBOL(show_trace);
-+EXPORT_SYMBOL(cpcmd);
++	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;
++}
 +
 +
 +/*
-+ * virtual CPU timer
++ * setup the block device related things
 + */
-+#ifdef CONFIG_VIRT_TIMER
-+EXPORT_SYMBOL(init_virt_timer);
-+EXPORT_SYMBOL(add_virt_timer);
-+EXPORT_SYMBOL(add_virt_timer_periodic);
-+EXPORT_SYMBOL(mod_virt_timer);
-+EXPORT_SYMBOL(del_virt_timer);
-+#endif
-+
-+/* urandom read needed for z90crypt */
-+extern struct file_operations urandom_fops;
-+EXPORT_SYMBOL_GPL(urandom_fops);
-=== arch/s390/kernel/traps.c
-==================================================================
---- arch/s390/kernel/traps.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/kernel/traps.c   (/trunk/2.4.27)   (revision 52)
-@@ -54,6 +54,9 @@
- extern pgm_check_handler_t do_segment_exception;
- extern pgm_check_handler_t do_page_exception;
- extern pgm_check_handler_t do_pseudo_page_fault;
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+extern pgm_check_handler_t do_monitor_call;
-+#endif
- #ifdef CONFIG_PFAULT
- extern int pfault_init(void);
- extern void pfault_fini(void);
-@@ -628,7 +631,7 @@
-         int i;
- 
-         for (i = 0; i < 128; i++)
--          pgm_check_table[i] = &default_trap_handler;
-+		pgm_check_table[i] = &default_trap_handler;
-         pgm_check_table[1] = &illegal_op;
-         pgm_check_table[2] = &privileged_op;
-         pgm_check_table[3] = &execute_exception;
-@@ -644,6 +647,9 @@
-  	pgm_check_table[0x14] = &do_pseudo_page_fault;
-         pgm_check_table[0x15] = &operand_exception;
-         pgm_check_table[0x1C] = &privileged_op;
-+#if defined (CONFIG_VIRT_TIMER) || defined (CONFIG_NO_IDLE_HZ)
-+	pgm_check_table[0x40] = &do_monitor_call;
-+#endif	
- #ifdef CONFIG_PFAULT
- 	if (MACHINE_IS_VM) {
- 		/* request the 0x2603 external interrupt */
-=== arch/s390/defconfig
-==================================================================
---- arch/s390/defconfig   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/defconfig   (/trunk/2.4.27)   (revision 52)
-@@ -38,8 +38,8 @@
- CONFIG_QDIO=m
- # CONFIG_QDIO_PERF_STATS is not set
- CONFIG_IPL=y
--# CONFIG_IPL_TAPE is not set
--CONFIG_IPL_VM=y
-+CONFIG_IPL_TAPE=y
-+# CONFIG_IPL_VM is not set
- CONFIG_NET=y
- CONFIG_SYSVIPC=y
- # CONFIG_BSD_PROCESS_ACCT is not set
-@@ -50,8 +50,52 @@
- # CONFIG_PROCESS_DEBUG is not set
- CONFIG_PFAULT=y
- # CONFIG_SHARED_KERNEL is not set
-+# CONFIG_VIRT_TIMER is not set
-+# CONFIG_APPLDATA_BASE is not set
-+# CONFIG_APPLDATA_MEM is not set
-+# CONFIG_APPLDATA_OS is not set
-+# CONFIG_APPLDATA_NET_SUM is not set
-+CONFIG_CMM=m
-+CONFIG_CMM_PROC=y
-+# CONFIG_CMM_IUCV is not set
- 
- #
-+# SCSI support
-+#
-+CONFIG_SCSI=m
++static int __init dcssblk_setup_blkdev(void)
++{
++	request_queue_t *q;
++	int i,rc;
 +
-+#
-+# SCSI support type (disk, tape, CD-ROM)
-+#
-+CONFIG_BLK_DEV_SD=m
-+CONFIG_SD_EXTRA_DEVS=1000
-+CONFIG_CHR_DEV_ST=m
-+# CONFIG_CHR_DEV_OSST is not set
-+CONFIG_BLK_DEV_SR=m
-+# CONFIG_BLK_DEV_SR_VENDOR is not set
-+CONFIG_SR_EXTRA_DEVS=10
-+CONFIG_CHR_DEV_SG=m
++	for (i=0; i < (1<<MINORBITS); i++)
++		dcssblk_blksizes[i] = PAGE_SIZE;
 +
-+#
-+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-+#
-+CONFIG_SCSI_DEBUG_QUEUES=y
-+CONFIG_SCSI_MULTI_LUN=y
-+CONFIG_SCSI_CONSTANTS=y
-+CONFIG_SCSI_LOGGING=y
++	/*
++	 * 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;
 +
-+#
-+# SCSI low-level drivers
-+#
-+CONFIG_ZFCP=m
-+CONFIG_ZFCP_HBAAPI=m
++	
++	/*
++	 * Assign the other needed values: make request function, sizes and
++	 * hardsect size. All the minor devices feature the same value.
++	 */
 +
-+#
-+# PCMCIA SCSI adapter support
-+#
-+# CONFIG_SCSI_PCMCIA is not set
++	q = BLK_DEFAULT_QUEUE(dcssblk_major);
++	blk_queue_make_request (q, dcssblk_make_request);
++	return 0;
++}
 +
-+#
- # Block device drivers
- #
- CONFIG_BLK_DEV_LOOP=y
-@@ -60,6 +104,7 @@
- CONFIG_BLK_DEV_RAM_SIZE=24576
- CONFIG_BLK_DEV_INITRD=y
- CONFIG_BLK_DEV_XPRAM=m
-+CONFIG_DCSSBLK=m
- 
- #
- # S/390 block device drivers
-@@ -68,6 +113,7 @@
- CONFIG_DASD_ECKD=y
- CONFIG_DASD_FBA=y
- CONFIG_DASD_DIAG=y
-+CONFIG_S390_CMF=m
- 
- #
- # Multi-device support (RAID and LVM)
-@@ -94,22 +140,24 @@
- CONFIG_TN3270_CONSOLE=y
- CONFIG_TN3215=y
- CONFIG_TN3215_CONSOLE=y
--CONFIG_HWC=y
--CONFIG_HWC_CONSOLE=y
--CONFIG_HWC_CPI=m
-+CONFIG_SCLP=y
-+CONFIG_SCLP_TTY=y
-+CONFIG_SCLP_CONSOLE=y
-+CONFIG_SCLP_VT220_TTY=y
-+CONFIG_SCLP_VT220_CONSOLE=y
-+CONFIG_SCLP_CPI=m
- CONFIG_S390_TAPE=m
- 
- #
- # S/390 tape interface support
- #
--CONFIG_S390_TAPE_CHAR=y
- CONFIG_S390_TAPE_BLOCK=y
- 
- #
- # S/390 tape hardware support
- #
--CONFIG_S390_TAPE_3490=y
--CONFIG_S390_TAPE_3480=y
-+CONFIG_S390_TAPE_34XX=m
-+CONFIG_VMLOGRDR=m
- 
- #
- # Network device drivers
-@@ -129,10 +177,29 @@
- #
- CONFIG_CHANDEV=y
- CONFIG_HOTPLUG=y
-+CONFIG_LCS=m
-+CONFIG_QETH=m
-+
-+#
-+# Gigabit Ethernet default settings
-+#
-+CONFIG_QETH_IPV6=y
-+CONFIG_QETH_VLAN=y
-+# CONFIG_QETH_PERF_STATS is not set
- CONFIG_CTC=m
-+# CONFIG_MPC is not set
- CONFIG_IUCV=m
-+CONFIG_NETIUCV=m
-+CONFIG_SMSGIUCV=m
- 
- #
-+# Miscellaneous
-+#
-+CONFIG_Z90CRYPT=m
-+# CONFIG_NO_IDLE_HZ is not set
-+# CONFIG_NO_IDLE_HZ_INIT is not set
-+
-+#
- # Networking options
- #
- CONFIG_PACKET=y
-@@ -172,7 +239,6 @@
- CONFIG_IP_NF_MATCH_MARK=m
- CONFIG_IP_NF_MATCH_MULTIPORT=m
- CONFIG_IP_NF_MATCH_TOS=m
--# CONFIG_IP_NF_MATCH_RECENT is not set
- # CONFIG_IP_NF_MATCH_ECN is not set
- # CONFIG_IP_NF_MATCH_DSCP is not set
- CONFIG_IP_NF_MATCH_AH_ESP=m
-@@ -205,14 +271,8 @@
- CONFIG_IP_NF_TARGET_TCPMSS=m
- CONFIG_IP_NF_ARPTABLES=m
- CONFIG_IP_NF_ARPFILTER=m
--# CONFIG_IP_NF_ARP_MANGLE is not set
- # CONFIG_IP_NF_COMPAT_IPCHAINS is not set
- # CONFIG_IP_NF_COMPAT_IPFWADM is not set
--
--#
--#   IP: Virtual Server Configuration
--#
--# CONFIG_IP_VS is not set
- CONFIG_IPV6=m
- 
- #
-@@ -237,6 +297,7 @@
- CONFIG_IP6_NF_TARGET_LOG=m
- CONFIG_IP6_NF_MANGLE=m
- CONFIG_IP6_NF_TARGET_MARK=m
-+CONFIG_SHARED_IPV6_CARDS=y
- # CONFIG_KHTTPD is not set
- 
- #
-@@ -328,6 +389,7 @@
- # CONFIG_QNX4FS_FS is not set
- # CONFIG_QNX4FS_RW is not set
- # CONFIG_ROMFS_FS is not set
-+CONFIG_XIP2FS=m
- CONFIG_EXT2_FS=y
- # CONFIG_SYSV_FS is not set
- # CONFIG_UDF_FS is not set
-=== arch/s390/appldata/appldata_base.c
-==================================================================
---- arch/s390/appldata/appldata_base.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/appldata/appldata_base.c   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-+
-+#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 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
-+
-+#else
-+
-+#define APPLDATA_START_INTERVAL_REC 0x80
-+#define APPLDATA_STOP_REC   	    0x81
-+#define APPLDATA_GEN_EVENT_RECORD   0x82
-+#define APPLDATA_START_CONFIG_REC   0x83
-+
-+#endif /* CONFIG_ARCH_S390X */
-+
-+
-+/*
-+ * 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 */
-+
-+/*
-+ * /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);
-+
-+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 }
-+};
-+
-+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 }
-+};
-+
-+/*
-+ * 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;
-+
-+/*
-+ * Tasklet
-+ */
-+static struct tasklet_struct appldata_tasklet_struct;
++static void dcssblk_unregister_blkdev(void) {
++	int rc;
++	rc = unregister_blkdev (dcssblk_major, DCSSBLK_NAME);
++	if (rc) {
++		PRINT_ERR ("Can't unregister blockdev\n");
++	}	
++} 
 +
-+/*
-+ * Hook list
++/* 
++ * Register procfs entries
 + */
-+static spinlock_t appldata_ops_lock = SPIN_LOCK_UNLOCKED;
-+static LIST_HEAD(appldata_ops_list);
++static int dcssblk_register_procfs(void)
++{
++#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);
 +
-+/************************* 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);
-+	}
-+}
++	dcssblk_list_entry = create_proc_entry ("list",
++						  S_IFREG | S_IRUGO,
++						  dcssblk_proc_root_entry);
 +
-+/*
-+ * 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;
++	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;
 +
-+	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_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);
++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
 +}
 +
 +/*
-+ * appldata_diag()
-+ *
-+ * prepare parameter list, issue DIAG 0xDC
++ * Finally, the init/exit functions.
 + */
-+static int appldata_diag(char record_nr, u16 function, unsigned long buffer,
-+			u16 length)
++static void __exit dcssblk_exit(void)
 +{
-+	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)
-+	};
-+
-+        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;
++	dcssblk_unregister_procfs();
++	dcssblk_unregister_blkdev();
 +}
-+/********************** timer, tasklet, DIAG <END> ***************************/
-+
 +
-+/****************************** /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)
++static int __init dcssblk_init(void)
 +{
-+	int len, i;
-+	u64 per_cpu_interval;
-+	char buf[2];
-+
-+	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;
++	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;
 +	}
-+	
-+	len = *lenp;
-+	if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
-+		return -EFAULT;
-+
-+	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;
++ out:
++	return rc;
 +}
 +
-+/*
-+ * 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;
-+	}
-+
-+	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;
-+}
++module_init(dcssblk_init);
++module_exit(dcssblk_exit);
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/Makefile kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/Makefile	2006-01-30 22:25:26.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
 +
-+/*
-+ * 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];
-+
-+	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_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);
-=== arch/s390/appldata/appldata_net_sum.c
-==================================================================
---- arch/s390/appldata/appldata_net_sum.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/appldata/appldata_net_sum.c   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-+ */
-+
-+#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. */
-+
-+/*
-+ * 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");
-=== arch/s390/appldata/appldata_mem.c
-==================================================================
---- arch/s390/appldata/appldata_mem.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/appldata/appldata_mem.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,165 @@
-+/*
-+ * arch/s390/appldata/appldata_mem.c
-+ *
-+ * 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 <linux/kernel_stat.h>
-+#include <asm/io.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 */
-+
-+/*
-+ * 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 */
-+
-+	u64 pgpgin;		/* pages read from disk  */
-+	u64 pgpgout;		/* pages written to disk */
-+	u64 pswpin;		/* pages swapped in  */
-+	u64 pswpout;		/* pages swapped out */
-+
-+	u64 sharedram;		/* sharedram is currently set to 0 */
-+
-+	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;
-+
-+
-+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));
-+}
-+
-+/*
-+ * appldata_get_mem_data()
-+ *
-+ * gather memory data
-+ */
-+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++;
-+
-+	mem_data->pgpgin  = kstat.pgpgin >> 1;
-+	mem_data->pgpgout = kstat.pgpgout >> 1;
-+	mem_data->pswpin  = kstat.pswpin;
-+	mem_data->pswpout = kstat.pswpout;
-+
-+	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);
-+
-+	si_swapinfo(&val);
-+
-+	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);
-+}
-+
-+
-+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,
-+};
-+
-+
-+/*
-+ * appldata_mem_init()
-+ *
-+ * init_data, register ops
-+ */
-+static int __init appldata_mem_init(void)
-+{
-+	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_mem_exit()
-+ * 
-+ * unregister ops
-+ */
-+static void __exit appldata_mem_exit(void)
-+{
-+	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");
-=== arch/s390/appldata/appldata_os.c
-==================================================================
---- arch/s390/appldata/appldata_os.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/appldata/appldata_os.c   (/trunk/2.4.27)   (revision 52)
-@@ -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);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Gerald Schaefer");
-+MODULE_DESCRIPTION("Linux-VM Monitor Stream, OS statistics");
-=== arch/s390/appldata/appldata.h
-==================================================================
---- arch/s390/appldata/appldata.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/appldata/appldata.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,58 @@
-+/*
-+ * arch/s390/appldata/appldata.h
-+ *
-+ * Definitions for Linux - z/VM Monitor Stream.
-+ *
-+ * Copyright (C) 2003 IBM Corporation, IBM Deutschland Entwicklung GmbH.
-+ *
-+ * Author: Gerald Schaefer <geraldsc at de.ibm.com>
-+ */
-+
-+//#define APPLDATA_DEBUG		/* Debug messages on/off */
-+
-+#define APPLDATA_MAX_REC_SIZE	  4024	/* Maximum size of the */
-+					/* data buffer */
-+					
-+#define APPLDATA_PROC_NAME_LENGTH 16	/* Max. length of /proc name */
-+
-+#define APPLDATA_RECORD_MEM_ID		0x01	/* IDs to identify the */
-+#define APPLDATA_RECORD_OS_ID		0x02	/* individual records, */
-+#define APPLDATA_RECORD_NET_SUM_ID	0x03	/* must be < 256 !     */
-+
-+#define CTL_APPLDATA 		2120	/* sysctl IDs, must be unique */
-+#define CTL_APPLDATA_TIMER 	2121
-+#define CTL_APPLDATA_INTERVAL 	2122
-+#define CTL_APPLDATA_MEM	2123
-+#define CTL_APPLDATA_OS		2124
-+#define CTL_APPLDATA_NET_SUM	2125
-+
-+#define ULL(var) (unsigned long long) var
-+
-+#define P_INFO(x...)	printk(KERN_INFO MY_PRINT_NAME " info: " x)
-+#define P_ERROR(x...)   printk(KERN_ERR MY_PRINT_NAME " error: " x)
-+#define P_STATUS(x...)  printk(KERN_WARNING MY_PRINT_NAME " status: " x)
-+
-+#ifdef APPLDATA_DEBUG
-+#define P_DEBUG(x...)   printk(KERN_DEBUG MY_PRINT_NAME " debug: " x)
-+#else
-+#define P_DEBUG(x...)   do {} while (0)
-+#endif
-+
-+struct appldata_ops {
-+	struct list_head list;
-+	struct ctl_table_header *sysctl_header;
-+	struct ctl_table *ctl_table;
-+	int    active;				/* monitoring status */
-+
-+	/* fill in from here */
-+	unsigned int ctl_nr;			/* sysctl ID */
-+	char name[APPLDATA_PROC_NAME_LENGTH];	/* name of /proc fs node */
-+	unsigned char record_nr;		/* Record Nr. for Product ID */
-+	void (*callback)(void *data);		/* callback function */
-+	void *data;				/* record data */
-+	unsigned int size;			/* size of record */
-+	struct module *owner;			/* THIS_MODULE */
-+};
-+
-+extern int appldata_register_ops(struct appldata_ops *ops);
-+extern void appldata_unregister_ops(struct appldata_ops *ops);
-=== arch/s390/appldata/Makefile
-==================================================================
---- arch/s390/appldata/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/appldata/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,13 @@
-+#
-+# Linux - VM Monitor Stream, Stage 1
-+#
-+
-+O_TARGET := appldata.o
-+
-+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
-+
-+include $(TOPDIR)/Rules.make
-=== arch/s390/mm/init.c
-==================================================================
---- arch/s390/mm/init.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/mm/init.c   (/trunk/2.4.27)   (revision 52)
-@@ -69,6 +69,8 @@
- 
- void diag10(unsigned long addr)
- {
-+        if (addr >= 0x7ff00000)
-+                return;
-         asm volatile ("diag %0,%0,0x10" : : "a" (addr));
- }
- 
-=== arch/s390/mm/Makefile
-==================================================================
---- arch/s390/mm/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/mm/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -9,6 +9,8 @@
- 
- O_TARGET := mm.o
- 
--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
- 
- include $(TOPDIR)/Rules.make
-=== arch/s390/mm/cmm.c
-==================================================================
---- arch/s390/mm/cmm.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/mm/cmm.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,448 @@
-+/*
-+ *  arch/s390/mm/cmm.c
-+ *
-+ *  S390 version
-+ *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Martin Schwidefsky (schwidefsky at de.ibm.com)
-+ *
-+ *  Collaborative memory management interface.
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/errno.h>
-+#include <linux/fs.h>
-+#include <linux/init.h>
-+#include <linux/slab.h>
-+#include <linux/module.h>
-+#include <linux/sched.h>
-+#include <linux/sysctl.h>
-+#include <linux/ctype.h>
-+
-+#include <asm/pgalloc.h>
-+#include <asm/uaccess.h>
-+
-+#include "../../../drivers/s390/net/smsgiucv.h"
-+
-+#define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2)
-+
-+struct cmm_page_array {
-+	struct cmm_page_array *next;
-+	unsigned long index;
-+	unsigned long pages[CMM_NR_PAGES];
-+};
-+
-+static long cmm_pages = 0;
-+static long cmm_timed_pages = 0;
-+static volatile long cmm_pages_target = 0;
-+static volatile long cmm_timed_pages_target = 0;
-+static long cmm_timeout_pages = 0;
-+static long cmm_timeout_seconds = 0;
-+
-+static struct cmm_page_array *cmm_page_list = 0;
-+static struct cmm_page_array *cmm_timed_page_list = 0;
-+
-+static unsigned long cmm_thread_active = 0;
-+static struct tq_struct cmm_thread_starter;
-+static wait_queue_head_t cmm_thread_wait;
-+static struct timer_list cmm_timer;
-+
-+static void cmm_timer_fn(unsigned long);
-+static void cmm_set_timer(void);
-+
-+static long
-+cmm_strtoul(const char *cp, char **endp)
-+{
-+	unsigned int base = 10;
-+
-+	if (*cp == '0') {
-+		base = 8;
-+		cp++;
-+		if ((*cp == 'x' || *cp == 'X') && isxdigit(cp[1])) {
-+			base = 16;
-+			cp++;
-+		}
-+	}
-+	return simple_strtoul(cp, endp, base);
-+}
-+
-+static long
-+cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list)
-+{
-+	struct cmm_page_array *pa;
-+	unsigned long page;
-+	
-+	pa = *list;
-+	while (pages) {
-+		page = __get_free_page(GFP_NOIO);
-+		if (!page)
-+			break;
-+		if (!pa || pa->index >= CMM_NR_PAGES) {
-+			/* Need a new page for the page list. */
-+			pa = (struct cmm_page_array *)
-+				__get_free_page(GFP_NOIO);
-+			if (!pa) {
-+				free_page(page);
-+				break;
-+			}
-+			pa->next = *list;
-+			pa->index = 0;
-+			*list = pa;
-+		}
-+		diag10((unsigned long) page);
-+		pa->pages[pa->index++] = page;
-+		(*counter)++;
-+		pages--;
-+	}
-+	return pages;
-+}
-+
-+static void
-+cmm_free_pages(long pages, long *counter, struct cmm_page_array **list)
-+{
-+	struct cmm_page_array *pa;
-+	unsigned long page;
-+	
-+	pa = *list;
-+	while (pages) {
-+		if (!pa || pa->index <= 0)
-+			break;
-+		page = pa->pages[--pa->index];
-+		if (pa->index == 0) {
-+			pa = pa->next;
-+			free_page((unsigned long) *list);
-+			*list = pa;
-+		}
-+		free_page(page);
-+		(*counter)--;
-+		pages--;
-+	}
-+}
-+
-+static int
-+cmm_thread(void *dummy)
-+{
-+	int rc;
-+	
-+	daemonize();
-+	reparent_to_init();
-+	strcpy(current->comm, "cmmthread");
-+	set_cpus_allowed(current, 1);
-+	while (1) {
-+		rc = wait_event_interruptible(cmm_thread_wait,
-+			(cmm_pages != cmm_pages_target ||
-+			 cmm_timed_pages != cmm_timed_pages_target));
-+		if (rc == -ERESTARTSYS) {
-+			/* Got kill signal. End thread. */
-+			clear_bit(0, &cmm_thread_active);
-+			cmm_pages_target = cmm_pages;
-+			cmm_timed_pages_target = cmm_timed_pages;
-+			break;
-+		}
-+		if (cmm_pages_target > cmm_pages) {
-+			if (cmm_alloc_pages(1, &cmm_pages, &cmm_page_list))
-+				cmm_pages_target = cmm_pages;
-+		} else if (cmm_pages_target < cmm_pages) {
-+			cmm_free_pages(1, &cmm_pages, &cmm_page_list);
-+		}
-+		if (cmm_timed_pages_target > cmm_timed_pages) {
-+			if (cmm_alloc_pages(1, &cmm_timed_pages,
-+					   &cmm_timed_page_list))
-+				cmm_timed_pages_target = cmm_timed_pages;
-+		} else if (cmm_timed_pages_target < cmm_timed_pages) {
-+			cmm_free_pages(1, &cmm_timed_pages,
-+			       	       &cmm_timed_page_list);
-+		}
-+		if (cmm_timed_pages > 0 && !timer_pending(&cmm_timer))
-+			cmm_set_timer();
-+	}
-+	return 0;
-+}
-+
-+static void
-+cmm_start_thread(void)
-+{
-+	kernel_thread(cmm_thread, 0, 0);
-+}
-+
-+static void
-+cmm_kick_thread(void)
-+{
-+	if (!test_and_set_bit(0, &cmm_thread_active))
-+		schedule_task(&cmm_thread_starter);
-+	wake_up(&cmm_thread_wait);
-+}
-+
-+static void
-+cmm_set_timer(void)
-+{
-+	if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) {
-+		if (timer_pending(&cmm_timer))
-+			del_timer(&cmm_timer);
-+		return;
-+	}
-+	if (timer_pending(&cmm_timer)) {
-+		if (mod_timer(&cmm_timer, jiffies + cmm_timeout_seconds*HZ))
-+			return;
-+	}
-+	cmm_timer.function = cmm_timer_fn;
-+	cmm_timer.data = 0;
-+	cmm_timer.expires = jiffies + cmm_timeout_seconds*HZ;
-+	add_timer(&cmm_timer);
-+}
-+
-+static void
-+cmm_timer_fn(unsigned long ignored)
-+{
-+	long pages;
-+
-+	pages = cmm_timed_pages_target - cmm_timeout_pages;
-+	if (pages < 0)
-+		cmm_timed_pages_target = 0;
-+	else
-+		cmm_timed_pages_target = pages;
-+	cmm_kick_thread();
-+	cmm_set_timer();
-+}
-+
-+void
-+cmm_set_pages(long pages)
-+{
-+	cmm_pages_target = pages;
-+	cmm_kick_thread();
-+}
-+
-+long
-+cmm_get_pages(void)
-+{
-+	return cmm_pages;
-+}
-+ 
-+void
-+cmm_add_timed_pages(long pages)
-+{
-+	cmm_timed_pages_target += pages;
-+	cmm_kick_thread();
-+}
-+
-+long
-+cmm_get_timed_pages(void)
-+{
-+	return cmm_timed_pages;
-+}
-+
-+void
-+cmm_set_timeout(long pages, long seconds)
-+{
-+	cmm_timeout_pages = pages;
-+	cmm_timeout_seconds = seconds;
-+	cmm_set_timer();
-+}
-+
-+static inline int
-+cmm_skip_blanks(char *cp, char **endp)
-+{
-+	char *str;
-+
-+	for (str = cp; *str == ' ' || *str == '\t'; str++);
-+	*endp = str;
-+	return str != cp;
-+}
-+
-+#ifdef CONFIG_CMM_PROC
-+/* These will someday get removed. */
-+#define VM_CMM_PAGES		1111
-+#define VM_CMM_TIMED_PAGES	1112
-+#define VM_CMM_TIMEOUT		1113
-+
-+static struct ctl_table cmm_table[];
-+
-+static int
-+cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
-+		  void *buffer, size_t *lenp)
-+{
-+	char buf[16], *p;
-+	long pages;
-+	int len;
-+
-+	if (!*lenp || (filp->f_pos && !write)) {
-+		*lenp = 0;
-+		return 0;
-+	}
-+
-+	if (write) {
-+		len = *lenp;
-+		if (copy_from_user(buf, buffer,
-+				   len > sizeof(buf) ? sizeof(buf) : len))
-+			return -EFAULT;
-+		buf[sizeof(buf) - 1] = '\0';
-+		cmm_skip_blanks(buf, &p);
-+		pages = cmm_strtoul(p, &p);
-+		if (ctl == &cmm_table[0])
-+			cmm_set_pages(pages);
-+		else
-+			cmm_add_timed_pages(pages);
-+	} else {
-+		if (ctl == &cmm_table[0])
-+			pages = cmm_get_pages();
-+		else
-+			pages = cmm_get_timed_pages();
-+		len = sprintf(buf, "%ld\n", pages);
-+		if (len > *lenp)
-+			len = *lenp;
-+		if (copy_to_user(buffer, buf, len))
-+			return -EFAULT;
-+	}
-+	*lenp = len;
-+	filp->f_pos += len;
-+	return 0;
-+}
-+
-+static int
-+cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
-+		    void *buffer, size_t *lenp)
-+{
-+	char buf[64], *p;
-+	long pages, seconds;
-+	int len;
-+
-+	if (!*lenp || (filp->f_pos && !write)) {
-+		*lenp = 0;
-+		return 0;
-+	}
-+
-+	if (write) {
-+		len = *lenp;
-+		if (copy_from_user(buf, buffer,
-+				   len > sizeof(buf) ? sizeof(buf) : len))
-+			return -EFAULT;
-+		buf[sizeof(buf) - 1] = '\0';
-+		cmm_skip_blanks(buf, &p);
-+		pages = cmm_strtoul(p, &p);
-+		cmm_skip_blanks(p, &p);
-+		seconds = cmm_strtoul(p, &p);
-+		cmm_set_timeout(pages, seconds);
-+	} else {
-+		len = sprintf(buf, "%ld %ld\n",
-+			      cmm_timeout_pages, cmm_timeout_seconds);
-+		if (len > *lenp)
-+			len = *lenp;
-+		if (copy_to_user(buffer, buf, len))
-+			return -EFAULT;
-+	}
-+	*lenp = len;
-+	filp->f_pos += len;
-+	return 0;
-+}
-+
-+static struct ctl_table cmm_table[] = {
-+	{
-+		.ctl_name	= VM_CMM_PAGES,
-+		.procname	= "cmm_pages",
-+		.mode		= 0600,
-+		.proc_handler	= &cmm_pages_handler,
-+	},
-+	{
-+		.ctl_name	= VM_CMM_TIMED_PAGES,
-+		.procname	= "cmm_timed_pages",
-+		.mode		= 0600,
-+		.proc_handler	= &cmm_pages_handler,
-+	},
-+	{
-+		.ctl_name	= VM_CMM_TIMEOUT,
-+		.procname	= "cmm_timeout",
-+		.mode		= 0600,
-+		.proc_handler	= &cmm_timeout_handler,
-+	},
-+	{ .ctl_name = 0 }
-+};
-+
-+static struct ctl_table cmm_dir_table[] = {
-+	{
-+		.ctl_name	= CTL_VM,
-+		.procname	= "vm",
-+		.maxlen		= 0,
-+		.mode		= 0555,	
-+		.child		= cmm_table,
-+	},
-+	{ .ctl_name = 0 }
-+};
-+#endif
-+
-+#ifdef CONFIG_CMM_IUCV
-+#define SMSG_PREFIX "CMM"
-+static void
-+cmm_smsg_target(char *msg)
-+{
-+	long pages, seconds;
-+
-+	if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg))
-+		return;
-+	if (strncmp(msg, "SHRINK", 6) == 0) {
-+		if (!cmm_skip_blanks(msg + 6, &msg))
-+			return;
-+		pages = cmm_strtoul(msg, &msg);
-+		cmm_skip_blanks(msg, &msg);
-+		if (*msg == '\0')
-+			cmm_set_pages(pages);
-+	} else if (strncmp(msg, "RELEASE", 7) == 0) {
-+		if (!cmm_skip_blanks(msg + 7, &msg))
-+			return;
-+		pages = cmm_strtoul(msg, &msg);
-+		cmm_skip_blanks(msg, &msg);
-+		if (*msg == '\0')
-+			cmm_add_timed_pages(pages);
-+	} else if (strncmp(msg, "REUSE", 5) == 0) {
-+		if (!cmm_skip_blanks(msg + 5, &msg))
-+			return;
-+		pages = cmm_strtoul(msg, &msg);
-+		if (!cmm_skip_blanks(msg, &msg))
-+			return;
-+		seconds = cmm_strtoul(msg, &msg);
-+		cmm_skip_blanks(msg, &msg);
-+		if (*msg == '\0')
-+			cmm_set_timeout(pages, seconds);
-+	}
-+}
-+#endif
-+
-+struct ctl_table_header *cmm_sysctl_header;
-+
-+static int
-+cmm_init (void)
-+{
-+#ifdef CONFIG_CMM_PROC
-+	cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1);
-+#endif
-+#ifdef CONFIG_CMM_IUCV
-+	smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
-+#endif
-+	cmm_thread_starter.routine = (void *) cmm_start_thread;
-+	cmm_thread_starter.data = 0;
-+	init_waitqueue_head(&cmm_thread_wait);
-+	init_timer(&cmm_timer);
-+	return 0;
-+}
-+
-+static void
-+cmm_exit(void)
-+{
-+	cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
-+	cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
-+#ifdef CONFIG_CMM_PROC
-+	unregister_sysctl_table(cmm_sysctl_header);
-+#endif
-+#ifdef CONFIG_CMM_IUCV
-+	smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
-+#endif
-+}
-+
-+module_init(cmm_init);
-+module_exit(cmm_exit);
-+
-+EXPORT_SYMBOL(cmm_set_pages);
-+EXPORT_SYMBOL(cmm_get_pages);
-+EXPORT_SYMBOL(cmm_add_timed_pages);
-+EXPORT_SYMBOL(cmm_get_timed_pages);
-+EXPORT_SYMBOL(cmm_set_timeout);
-+
-+MODULE_LICENSE("GPL");
-=== arch/s390/mm/dcss.c
-==================================================================
---- arch/s390/mm/dcss.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/mm/dcss.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,504 @@
-+/*
-+ * File...........: arch/s390/mm/dcss.c
-+ * Author(s)......: Steven Shultz <shultzss at us.ibm.com>
-+ *                  Carsten Otte <cotte at de.ibm.com>
-+ * Bugreports.to..: <Linux390 at de.ibm.com>
-+ * thanks to Rob M van der Heij
-+ * - he wrote the diag64 function
-+ * (C) IBM Corporation 2002
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/string.h>
-+#include <linux/spinlock.h>
-+#include <linux/list.h>
-+#include <linux/slab.h>
-+#include <linux/module.h>
-+#include <linux/bootmem.h>
-+#include <asm/page.h>
-+#include <asm/ebcdic.h>
-+#include <asm/errno.h>
-+#include <asm/dcss.h>
-+#include <asm/cpcmd.h>
-+#include <linux/ctype.h>
-+
-+#define DCSS_DEBUG	/* Debug messages on/off */
-+
-+#define DCSS_NAME "dcss"
-+#ifdef DCSS_DEBUG
-+#define PRINT_DEBUG(x...)	printk(KERN_DEBUG DCSS_NAME " debug:" x)
-+#else
-+#define PRINT_DEBUG(x...)   do {} while (0)
-+#endif
-+#define PRINT_INFO(x...)	printk(KERN_INFO DCSS_NAME " info:" x)
-+#define PRINT_WARN(x...)	printk(KERN_WARNING DCSS_NAME " warning:" x)
-+#define PRINT_ERR(x...)		printk(KERN_ERR DCSS_NAME " error:" x)
-+
-+
-+#define DCSS_LOADSHR    0x00
-+#define DCSS_LOADNSR    0x04
-+#define DCSS_PURGESEG   0x08
-+#define DCSS_FINDSEG    0x0c
-+#define DCSS_LOADNOLY   0x10
-+#define DCSS_SEGEXT     0x18
-+#define DCSS_QACTV      0x0c
-+
-+struct dcss_segment {
-+        struct list_head list;
-+        char dcss_name[8];
-+        unsigned long start_addr;
-+        unsigned long end;
-+        atomic_t ref_count;
-+        int dcss_attr;
-+	int shared_attr;
-+};
-+
-+static spinlock_t dcss_lock = SPIN_LOCK_UNLOCKED;
-+static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list);
-+extern struct {unsigned long addr, size, type} memory_chunk[16];
-+
-+/*
-+ * Create the 8 bytes, ebcdic VM segment name from
-+ * an ascii name.
-+ */
-+static void inline dcss_mkname(char *name, char *dcss_name)
-+{
-+        int i;
-+
-+        for (i = 0; i <= 8; i++) {
-+                if (name[i] == '\0')
-+                        break;
-+                dcss_name[i] = toupper(name[i]);
-+        };
-+        for (; i <= 8; i++)
-+                dcss_name[i] = ' ';
-+        ASCEBC(dcss_name, 8);
-+}
-+
-+/*
-+ * Perform a function on a dcss segment.
-+ */
-+static inline int
-+dcss_diag (__u8 func, void *parameter,
-+           unsigned long *ret1, unsigned long *ret2)
-+{
-+        unsigned long rx, ry;
-+        int rc;
-+
-+        rx = (unsigned long) parameter;
-+        ry = (unsigned long) func;
-+        __asm__ __volatile__(
-+#ifdef CONFIG_ARCH_S390X
-+                             "   sam31\n" // switch to 31 bit
-+                             "   diag    %0,%1,0x64\n"
-+                             "   sam64\n" // switch back to 64 bit
-+#else
-+                             "   diag    %0,%1,0x64\n"
-+#endif
-+                             "   ipm     %2\n"
-+                             "   srl     %2,28\n"
-+                             : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" );
-+        *ret1 = rx;
-+        *ret2 = ry;
-+        return rc;
-+}
-+
-+
-+/* use to issue "extended" dcss query */
-+static inline int
-+dcss_diag_query(char *name, int *rwattr, int *shattr, unsigned long *segstart, unsigned long *segend)
-+{
-+        int i,j,rc;
-+        unsigned long  rx, ry;
-+
-+        typedef struct segentry {
-+                char thisseg[8];
-+        } segentry;
-+
-+        struct qout64 {
-+                int segstart;
-+                int segend;
-+                int segcnt;
-+                int segrcnt;
-+                segentry segout[6];
-+        };
-+
-+        struct qin64 {
-+                char qopcode;
-+                char rsrv1[3];
-+                char qrcode;
-+                char rsrv2[3];
-+                char qname[8];
-+                unsigned int qoutptr;
-+                short int qoutlen;
-+        };
-+
-+
-+        struct qin64  *qinarea;
-+        struct qout64 *qoutarea;
-+
-+        qinarea = (struct qin64*) get_free_page (GFP_DMA);
-+        if (!qinarea) {
-+                rc =-ENOMEM;
-+                goto out;
-+        }
-+        qoutarea = (struct qout64*) get_free_page (GFP_DMA);
-+        if (!qoutarea) {
-+                rc = -ENOMEM;
-+                free_page (qinarea);
-+                goto out;
-+        }
-+        memset (qinarea,0,PAGE_SIZE);
-+        memset (qoutarea,0,PAGE_SIZE);
-+
-+        qinarea->qopcode = DCSS_QACTV; /* do a query for active
-+                                          segments */
-+        qinarea->qoutptr = (unsigned long) qoutarea;
-+        qinarea->qoutlen = sizeof(struct qout64);
-+
-+        /* Move segment name into double word aligned
-+           field and pad with blanks to 8 long.
-+         */
-+
-+        for (i = j = 0 ; i < 8; i++) {
-+                qinarea->qname[i] = (name[j] == '\0') ? ' ' : name[j++];
-+        }
-+
-+        /* name already in EBCDIC */
-+        /* ASCEBC ((void *)&qinarea.qname, 8); */
-+
-+        /* set the assembler variables */
-+        rx = (unsigned long) qinarea;
-+        ry = DCSS_SEGEXT; /* this is extended function */
-+
-+        /* issue diagnose x'64' */
-+        __asm__ __volatile__(
-+#ifdef CONFIG_ARCH_S390X
-+                             "   sam31\n" // switch to 31 bit
-+                             "   diag    %0,%1,0x64\n"
-+                             "   sam64\n" // switch back to 64 bit
-+#else
-+                             "   diag    %0,%1,0x64\n"
-+#endif
-+                             "   ipm     %2\n"
-+                             "   srl     %2,28\n"
-+                             : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" );
-+
-+        /* parse the query output area */
-+	*segstart=qoutarea->segstart;
-+	*segend=qoutarea->segend;
-+
-+        if (rc > 1)
-+                {
-+                        *rwattr = 2;
-+                        *shattr = 2;
-+                        rc = 0;
-+                        goto free;
-+                }
-+
-+        if (qoutarea->segcnt > 6)
-+                {
-+                        *rwattr = 3;
-+                        *shattr = 3;
-+                        rc = 0;
-+                        goto free;
-+                }
-+
-+        *rwattr = 1;
-+        *shattr = 1;
-+
-+        for (i=0; i < qoutarea->segrcnt; i++) {
-+                if (qoutarea->segout[i].thisseg[3] == 2 ||
-+                    qoutarea->segout[i].thisseg[3] == 3 ||
-+                    qoutarea->segout[i].thisseg[3] == 6 )
-+                        *rwattr = 0;
-+                if (qoutarea->segout[i].thisseg[3] == 1 ||
-+                    qoutarea->segout[i].thisseg[3] == 3 ||
-+                    qoutarea->segout[i].thisseg[3] == 5 )
-+                        *shattr = 0;
-+        } /* end of for statement */
-+        rc = 0;
-+ free:
-+        free_page (qoutarea);
-+        free_page (qinarea);
-+ out:
-+        return rc;
-+}
-+
-+/*
-+ * Load a DCSS segment via the diag 0x64.
-+ */
-+int segment_load(char *name, int segtype, unsigned long *addr,
-+                 unsigned long *end)
-+{
-+        char dcss_name[8];
-+        struct list_head *l;
-+        struct dcss_segment *seg, *tmp;
-+	unsigned long dummy;
-+	unsigned long segstart, segend;
-+        int rc = 0,i;
-+        int initrc = 0;
-+        int rwattr, shattr;
-+
-+        if (!MACHINE_IS_VM)
-+                return -ENOSYS;
-+        dcss_mkname(name, dcss_name);
-+	/* search for the dcss in list of currently loaded segments */
-+        spin_lock(&dcss_lock);
-+        seg = NULL;
-+        list_for_each(l, &dcss_list) {
-+                tmp = list_entry(l, struct dcss_segment, list);
-+                if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) {
-+                        seg = tmp;
-+                        break;
-+                }
-+        }
-+
-+        if (seg == NULL) {
-+                /* find out the attributes of this
-+                   shared segment */
-+                dcss_diag_query(dcss_name, &rwattr, &shattr, &segstart, &segend);
-+		/* does segment collide with main memory? */
-+		for (i=0; i<16; i++) {
-+					if (memory_chunk[i].type != 0)
-+						continue;
-+					if (memory_chunk[i].addr > segend)
-+						continue;
-+					if (memory_chunk[i].addr + memory_chunk[i].size <= segstart)
-+						continue;
-+					spin_unlock(&dcss_lock);
-+				        return -ENOENT;
-+				}
-+		/* or does it collide with other (loaded) segments? */
-+        	list_for_each(l, &dcss_list) {
-+                	tmp = list_entry(l, struct dcss_segment, list);
-+	                if ((segstart <= tmp->end && segstart >= tmp->start_addr) ||
-+				(segend <= tmp->end && segend >= tmp->start_addr) ||
-+				(segstart <= tmp->start_addr && segend >= tmp->end)) {
-+				PRINT_ERR("Segment Overlap!\n");
-+			        spin_unlock(&dcss_lock);
-+				return -ENOENT;
-+	                }
-+        	}
-+
-+                /* do case statement on segtype */
-+                /* if asking for shared ro,
-+                   shared rw works */
-+                /* if asking for exclusive ro,
-+                   exclusive rw works */
-+
-+                switch(segtype) {
-+                case SEGMENT_SHARED_RO:
-+                        if (shattr > 1 || rwattr > 1) {
-+                                spin_unlock(&dcss_lock);
-+                                return -ENOENT;
-+                        } else {
-+                                if (shattr == 0 && rwattr == 0)
-+                                        rc = SEGMENT_EXCLUSIVE_RO;
-+                                if (shattr == 0 && rwattr == 1)
-+                                        rc = SEGMENT_EXCLUSIVE_RW;
-+                                if (shattr == 1 && rwattr == 0)
-+                                        rc = SEGMENT_SHARED_RO;
-+                                if (shattr == 1 && rwattr == 1)
-+                                        rc = SEGMENT_SHARED_RW;
-+                        }
-+                        break;
-+                case SEGMENT_SHARED_RW:
-+                        if (shattr > 1 || rwattr != 1) {
-+                                spin_unlock(&dcss_lock);
-+                                return -ENOENT;
-+                        } else {
-+                                if (shattr == 0)
-+                                        rc = SEGMENT_EXCLUSIVE_RW;
-+                                if (shattr == 1)
-+                                        rc = SEGMENT_SHARED_RW;
-+                        }
-+                        break;
-+
-+                case SEGMENT_EXCLUSIVE_RO:
-+                        if (shattr > 0 || rwattr > 1) {
-+                                spin_unlock(&dcss_lock);
-+                                return -ENOENT;
-+                        } else {
-+                                if (rwattr == 0)
-+                                        rc = SEGMENT_EXCLUSIVE_RO;
-+                                if (rwattr == 1)
-+                                        rc = SEGMENT_EXCLUSIVE_RW;
-+                        }
-+                        break;
-+
-+                case SEGMENT_EXCLUSIVE_RW:
-+/*                        if (shattr != 0 || rwattr != 1) {
-+                                spin_unlock(&dcss_lock);
-+                                return -ENOENT;
-+                        } else {
-+*/
-+                                rc = SEGMENT_EXCLUSIVE_RW;
-+//                        }
-+                        break;
-+
-+                default:
-+                        spin_unlock(&dcss_lock);
-+                        return -ENOENT;
-+                } /* end switch */
-+
-+                seg = kmalloc(sizeof(struct dcss_segment), GFP_DMA);
-+                if (seg != NULL) {
-+                        memcpy(seg->dcss_name, dcss_name, 8);
-+			if (rc == SEGMENT_EXCLUSIVE_RW) {
-+				if (dcss_diag(DCSS_LOADNSR, seg->dcss_name,
-+						&seg->start_addr, &seg->end) == 0) {
-+					if (seg->end < max_low_pfn*PAGE_SIZE ) {
-+						atomic_set(&seg->ref_count, 1);
-+						list_add(&seg->list, &dcss_list);
-+						*addr = seg->start_addr;
-+						*end = seg->end;
-+						seg->dcss_attr = rc;
-+						if (shattr == 1 && rwattr == 1)
-+							seg->shared_attr = SEGMENT_SHARED_RW;
-+						else if (shattr == 1 && rwattr == 0)
-+							seg->shared_attr = SEGMENT_SHARED_RO;
-+						else
-+							seg->shared_attr = SEGMENT_EXCLUSIVE_RW;
-+					} else {
-+						dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
-+						kfree (seg);
-+						rc = -ENOENT;
-+					}
-+				} else {
-+					kfree(seg);
-+					rc = -ENOENT;
-+			        }
-+				goto out;
-+                        } 
-+			if (dcss_diag(DCSS_LOADNOLY, seg->dcss_name,
-+                                      &seg->start_addr, &seg->end) == 0) {
-+				if (seg->end < max_low_pfn*PAGE_SIZE ) {
-+		                        atomic_set(&seg->ref_count, 1);
-+					list_add(&seg->list, &dcss_list);
-+					*addr = seg->start_addr;
-+					*end = seg->end;
-+					seg->dcss_attr = rc;
-+					seg->shared_attr = rc;
-+				} else {
-+					dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
-+					kfree (seg);
-+					rc = -ENOENT;
-+				}
-+                        } else {
-+                                kfree(seg);
-+                                rc = -ENOENT;
-+                        }
-+                } else rc = -ENOMEM;
-+        } else {
-+		/* found */
-+		if ((segtype == SEGMENT_EXCLUSIVE_RW) && (seg->dcss_attr != SEGMENT_EXCLUSIVE_RW)) {
-+			PRINT_ERR("Segment already loaded in other mode than EXCLUSIVE_RW!\n");
-+			rc = -EPERM;
-+			goto out;
-+			/* reload segment in exclusive mode */
-+/*			dcss_diag(DCSS_LOADNSR, seg->dcss_name,
-+				  &seg->start_addr, &seg->end);
-+			seg->dcss_attr = SEGMENT_EXCLUSIVE_RW;*/
-+		}
-+		if ((segtype != SEGMENT_EXCLUSIVE_RW) && (seg->dcss_attr == SEGMENT_EXCLUSIVE_RW)) {
-+			PRINT_ERR("Segment already loaded in EXCLUSIVE_RW mode!\n");
-+			rc = -EPERM;
-+			goto out;
-+		}
-+                atomic_inc(&seg->ref_count);
-+                *addr = seg->start_addr;
-+                *end = seg->end;
-+                rc = seg->dcss_attr;
-+        }
-+out:
-+        spin_unlock(&dcss_lock);
-+        return rc;
-+}
-+
-+/*
-+ * Decrease the use count of a DCSS segment and remove
-+ * it from the address space if nobody is using it
-+ * any longer.
-+ */
-+void segment_unload(char *name)
-+{
-+        char dcss_name[8];
-+        unsigned long dummy;
-+        struct list_head *l,*l_tmp;
-+        struct dcss_segment *seg;
-+
-+        if (!MACHINE_IS_VM)
-+                return;
-+        dcss_mkname(name, dcss_name);
-+        spin_lock(&dcss_lock);
-+        list_for_each_safe(l, l_tmp, &dcss_list) {
-+                seg = list_entry(l, struct dcss_segment, list);
-+                if (memcmp(seg->dcss_name, dcss_name, 8) == 0) {
-+                        if (atomic_dec_return(&seg->ref_count) == 0) {
-+                                /* Last user of the segment is
-+                                   gone. */
-+                                list_del(&seg->list);
-+                                dcss_diag(DCSS_PURGESEG, seg->dcss_name,
-+                                          &dummy, &dummy);
-+				kfree(seg);
-+                        }
-+                        break;
-+                }
-+        }
-+        spin_unlock(&dcss_lock);
-+}
-+
-+/*
-+ * Replace an existing DCSS segment, so that machines
-+ * that load it anew will see the new version.
-+ */
-+void segment_replace(char *name)
-+{
-+        char dcss_name[8];
-+        struct list_head *l;
-+        struct dcss_segment *seg;
-+        int mybeg = 0;
-+        int myend = 0;
-+        char mybuff1[80];
-+        char mybuff2[80];
-+
-+        if (!MACHINE_IS_VM)
-+                return;
-+        dcss_mkname(name, dcss_name);
-+
-+        memset (mybuff1, 0, sizeof(mybuff1));
-+        memset (mybuff2, 0, sizeof(mybuff2));
-+
-+        spin_lock(&dcss_lock);
-+        list_for_each(l, &dcss_list) {
-+                seg = list_entry(l, struct dcss_segment, list);
-+                if (memcmp(seg->dcss_name, dcss_name, 8) == 0) {
-+                        mybeg = seg->start_addr >> 12;
-+                        myend = (seg->end) >> 12;
-+                        if (seg->shared_attr == SEGMENT_EXCLUSIVE_RW)
-+                                sprintf(mybuff1, "DEFSEG %s %X-%X EW",
-+                                        name, mybeg, myend);
-+                        if (seg->shared_attr == SEGMENT_EXCLUSIVE_RO)
-+                                sprintf(mybuff1, "DEFSEG %s %X-%X RO",
-+                                        name, mybeg, myend);
-+                        if (seg->shared_attr == SEGMENT_SHARED_RW)
-+                                sprintf(mybuff1, "DEFSEG %s %X-%X SW",
-+                                        name, mybeg, myend);
-+                        if (seg->shared_attr == SEGMENT_SHARED_RO)
-+                                sprintf(mybuff1, "DEFSEG %s %X-%X SR",
-+                                        name, mybeg, myend);
-+                        spin_unlock(&dcss_lock);
-+                        sprintf(mybuff2, "SAVESEG %s", name);
-+                        cpcmd(mybuff1, NULL, 80);
-+                        cpcmd(mybuff2, NULL, 80);
-+                        break;
-+                }
-+
-+        }
-+        if (myend == 0) spin_unlock(&dcss_lock);
-+}
-+
-+EXPORT_SYMBOL(segment_load);
-+EXPORT_SYMBOL(segment_unload);
-+EXPORT_SYMBOL(segment_replace);
-=== arch/s390/config.in
-==================================================================
---- arch/s390/config.in   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/config.in   (/trunk/2.4.27)   (revision 52)
-@@ -62,8 +62,35 @@
- bool 'Show crashed user process info' CONFIG_PROCESS_DEBUG
- bool 'Pseudo page fault support' CONFIG_PFAULT
- bool 'VM shared kernel support' CONFIG_SHARED_KERNEL
-+bool 'No HZ timer ticks in idle' CONFIG_NO_IDLE_HZ
-+if [ "$CONFIG_NO_IDLE_HZ" = "y" ] ; then
-+  bool '  Idle HZ timer on by default' CONFIG_NO_IDLE_HZ_INIT
-+fi
-+bool 'Virtual CPU timer support' CONFIG_VIRT_TIMER 
-+dep_bool 'Linux - VM Monitor Stream, base infrastructure' CONFIG_APPLDATA_BASE \
-+$CONFIG_PROC_FS $CONFIG_VIRT_TIMER
-+dep_tristate '   Monitor memory management statistics' CONFIG_APPLDATA_MEM $CONFIG_APPLDATA_BASE
-+dep_tristate '   Monitor OS statistics' CONFIG_APPLDATA_OS $CONFIG_APPLDATA_BASE
-+dep_tristate '   Monitor overall network statistics' CONFIG_APPLDATA_NET_SUM $CONFIG_APPLDATA_BASE
-+tristate 'Collaborative memory management' CONFIG_CMM
-+if [ "$CONFIG_CMM" != "n" ]; then
-+  dep_bool '/proc interface to cooperative memory management' CONFIG_CMM_PROC $CONFIG_PROC_FS
-+  if [ "$CONFIG_SMSGIUCV" = "y" -o "$CONFIG_SMSGIUCV" = "$CONFIG_CMM" ]; then
-+    bool 'IUCV special message interface to cooperative memory management' CONFIG_CMM_IUCV
-+  fi
-+fi
- endmenu
- 
-+mainmenu_option next_comment
-+comment 'SCSI support'
-+
-+tristate 'SCSI support' CONFIG_SCSI
-+
-+if [ "$CONFIG_SCSI" != "n" ]; then
-+   source drivers/scsi/Config.in
-+fi
-+endmenu
-+
- source drivers/s390/Config.in
- 
- if [ "$CONFIG_NET" = "y" ]; then
-=== arch/s390/Makefile
-==================================================================
---- arch/s390/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ arch/s390/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -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
-+
- arch/s390/mm: dummy
- 	$(MAKE) linuxsubdirs SUBDIRS=arch/s390/mm
- 
-=== Makefile
-==================================================================
---- Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -285,7 +285,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) \
-@@ -296,6 +296,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)
-@@ -350,6 +355,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
- 
-@@ -366,6 +374,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 $@ $<
- 
-=== drivers/s390/scsi/zh_ioctl32.c
-==================================================================
---- drivers/s390/scsi/zh_ioctl32.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/scsi/zh_ioctl32.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,248 @@
-+/* 
-+ * $Id: zh_ioctl32.c,v 1.4.2.2 2004/03/24 11:18:00 aherrman Exp $
-+ *
-+ * (C) Copyright IBM Corp. 2003
-+ *
-+ * 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 of the License, or
-+ * (at your option) any later version. See the file COPYING for more
-+ * information.
-+ *
-+ * Authors:
-+ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
-+ *       Andreas Herrmann <aherrman at de.ibm.com>
-+ *
-+ * No need for special handler functions here since our ioctl() method is 32 BIT
-+ * aware.
-+ *
-+ * For this trick to work we define a 32bit structure (see "zh.h") and a normal
-+ * one (eg struct zh_scsi_report_luns32 and struct zh_scsi_report_luns). We also
-+ * define an extra 32 BIT ioctl number, ZH_IOC_SCSI_REPORT_LUNS32 using the
-+ * zh_scsi_report_luns32 structure.
-+ *
-+ * This creates an ioctl command that is the same on 32 and 64 BIT. On a 64 BIT
-+ * kernel only 32 BIT applications will call the ioctl() with the 32 BIT ioctl
-+ * command, thus we can react on that and apply our pointer voodoo.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+
-+#include <linux/fs.h>
-+#include <linux/ioctl.h>
-+#include <asm/uaccess.h>
-+#include <asm/ioctl32.h>
-+
-+#include "zh.h"
-+
-+#include "../../../arch/s390x/kernel/linux32.h"
-+
-+/* void* casts needed because of fourth parameter */
-+#define ZH_IOC_DEFAULT(cmd) {cmd, (void*) sys_ioctl}
-+#define ZH_IOC_HANDLER(cmd, handler) {cmd, (void*) handler}
-+
-+/**
-+ * zh_ioctl_entry - one entry in the conversion array
-+ * @cmd: ioctl cmd
-+ * @handler: the corresponding handler
-+ */
-+struct zh_ioctl_entry
-+{
-+	unsigned int cmd;
-+	ioctl_trans_handler_t handler;
-+};
-+
-+/**
-+ * struct zh_send_ct32 - data needed to send out a Generic Service command,
-+ *	32BIT version
-+ * @devid: id of HBA via which to send CT
-+ * @req_length: size the request buffer
-+ * @req: request buffer
-+ * @resp_length: size of response buffer
-+ * @resp: response buffer
-+ */
-+struct zh_send_ct32
-+{
-+	devid_t devid;
-+	u32 req_length;
-+	u32 req;
-+	u32 resp_length;
-+	u32 resp;
-+} __attribute__((packed));
-+
-+#define ZH_IOC_SEND_CT32 _IOWR(ZH_IOC_MAGIC, 7, struct zh_send_ct32)
-+
-+/**
-+ * zh_send_ct32 - ioctl32 conversion function for ZH_IOC_SEND_CT
-+ * @fd: fd of device file
-+ * @cmd: command to execute
-+ * @arg: parameter(s) for the command
-+ * Return:  0 on success, else -E* code
-+ * Context: User
-+ */
-+static inline int zh_send_ct32(unsigned int fd, unsigned int cmd,
-+			       unsigned long arg)
-+{
-+	int ret;
-+	struct zh_send_ct ioc_data;
-+	struct zh_send_ct32 ioc_data32;
-+	struct zh_send_ct32 *u_ptr = (struct zh_send_ct32 *) A(arg);
-+
-+	if (copy_from_user(&ioc_data32, u_ptr, sizeof(ioc_data32)))
-+		return -EFAULT;
-+
-+	ioc_data.devid = ioc_data32.devid;
-+	ioc_data.req_length = ioc_data32.req_length;
-+	ioc_data.resp_length = ioc_data32.resp_length;
-+	ioc_data.req = (void *) A(ioc_data32.req);
-+	ioc_data.resp = (void *) A(ioc_data32.resp);
-+
-+	ret = zh_send_ct_helper(&ioc_data);
-+
-+	return ret;
-+}
-+
-+/**
-+ * struct zh_scsi_report_luns32 - data needed for an REPORT_LUNS, 32BIT version
-+ * @devid: of the adapter
-+ * @wwpn: of the port
-+ * @*rsp_buffer: pointer to response buffer
-+ * @rsp_buffer_size: of the response buffer
-+ * @sense: buffer for sense data
-+ */
-+struct zh_scsi_report_luns32
-+{
-+	devid_t devid;
-+	wwn_t wwpn;
-+	u32 rsp_buffer;
-+	u32 rsp_buffer_size;
-+	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
-+} __attribute__((packed));
-+
-+#define ZH_IOC_SCSI_REPORT_LUNS32 \
-+_IOW(ZH_IOC_MAGIC, 10, struct zh_scsi_report_luns32)
-+
-+/**
-+ * zh_scsi_report_luns32 - ioctl32 conversion function for
-+ *	ZH_SCSI_REPORT_LUNS32
-+ * @fd: fd of device file
-+ * @cmd: command to execute
-+ * @arg: parameter(s) for the command
-+ * Return:  0 on success, else -E* code
-+ * Context: User
-+ */
-+static inline int zh_scsi_report_luns32(unsigned int fd, unsigned int cmd,
-+					unsigned long arg)
-+{
-+	int ret;
-+	struct zh_scsi_report_luns ioc_data = { 0 };
-+	struct zh_scsi_report_luns32 ioc_data32;
-+	struct zh_scsi_report_luns32 *u_ptr;
-+
-+	u_ptr = (struct zh_scsi_report_luns32 *) A(arg);
-+
-+	if (copy_from_user(&ioc_data32, u_ptr, sizeof(ioc_data32)))
-+		return -EFAULT;
-+
-+	ioc_data.devid = ioc_data32.devid;
-+	ioc_data.wwpn = ioc_data32.wwpn;
-+	ioc_data.rsp_buffer = (void *) A(ioc_data32.rsp_buffer);
-+	ioc_data.rsp_buffer_size = ioc_data32.rsp_buffer_size;
-+
-+	ret = zh_report_luns_helper(&ioc_data);
-+
-+	if (ret >= 0) {
-+		memcpy(&ioc_data32.sense, &(ioc_data.sense),
-+		       ZH_SCSI_SENSE_BUFFERSIZE);
-+		if (copy_to_user(u_ptr, &ioc_data32, sizeof(ioc_data32))) {
-+			ret = -EFAULT;
-+		}
-+		if (ret > 0) {
-+			ret = 0;
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+/*
-+ * All the commands we have to register, may be freed if not compiled as module
-+ */
-+static struct zh_ioctl_entry zh_conversion[] __initdata = {
-+	ZH_IOC_DEFAULT(ZH_IOC_GET_ADAPTERATTRIBUTES),
-+	ZH_IOC_DEFAULT(ZH_IOC_GET_PORTATTRIBUTES),
-+	ZH_IOC_DEFAULT(ZH_IOC_GET_PORTSTATISTICS),
-+	ZH_IOC_DEFAULT(ZH_IOC_GET_DPORTATTRIBUTES),
-+	ZH_IOC_DEFAULT(ZH_IOC_GET_RNID),
-+	ZH_IOC_DEFAULT(ZH_IOC_SEND_RNID),
-+	ZH_IOC_HANDLER(ZH_IOC_SEND_CT32, zh_send_ct32),
-+	ZH_IOC_DEFAULT(ZH_IOC_SCSI_INQUIRY),
-+	ZH_IOC_DEFAULT(ZH_IOC_SCSI_READ_CAPACITY),
-+	ZH_IOC_HANDLER(ZH_IOC_SCSI_REPORT_LUNS32, zh_scsi_report_luns32),
-+	ZH_IOC_DEFAULT(ZH_IOC_GET_EVENT_BUFFER),
-+	ZH_IOC_DEFAULT(ZH_IOC_GET_CONFIG),
-+	ZH_IOC_DEFAULT(ZH_IOC_CLEAR_CONFIG),
-+	ZH_IOC_DEFAULT(ZH_IOC_EVENT_START),
-+	ZH_IOC_DEFAULT(ZH_IOC_EVENT_STOP),
-+	ZH_IOC_DEFAULT(ZH_IOC_EVENT),
-+	ZH_IOC_DEFAULT(ZH_IOC_EVENT_INSERT),
-+};
-+
-+/**
-+ * do_unregister - unregister previously registered conversion ioctl()s
-+ * @from: index in the zh_conversion table to start deregistering from
-+ *
-+ * All ioctl()s _before_ from will be deregistered. If from is 0 all will be
-+ * unregistered.
-+ */
-+static int do_unregister(int from)
-+{
-+	int i;
-+
-+	if (0 == from) {
-+		from = sizeof(zh_conversion)/sizeof(zh_conversion[0]);
-+	}
-+	
-+	for (i = from - 1; i >= 0; --i) {
-+		unregister_ioctl32_conversion(zh_conversion[i].cmd);
-+	}
-+
-+	return 0;
-+}
-+
-+/**
-+ * do_register - register ioctl() conversion routines
-+ */
-+static int do_register(void)
-+{
-+	unsigned int i;
-+	int ret;
-+
-+	for (i = 0; i < sizeof(zh_conversion)/sizeof(zh_conversion[0]); ++i) {
-+		ret = register_ioctl32_conversion(zh_conversion[i].cmd,
-+				zh_conversion[i].handler);
-+		if (ret != 0) {
-+			do_unregister(i - 1);
-+			break;
-+		}
-+	}
-+	
-+	return ret;
-+}
-+
-+/**
-+ * zh_unregister_ioctl_conversion - clean up method
-+ */
-+int zh_unregister_ioctl_conversion(void)
-+{
-+	return do_unregister(0);
-+}
-+
-+/**
-+ * zh_register_ioctl_conversion - initialization method
-+ */
-+int zh_register_ioctl_conversion(void)
-+{
-+	return do_register();
-+}
-=== drivers/s390/scsi/zfcp_zh.c
-==================================================================
---- drivers/s390/scsi/zfcp_zh.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/scsi/zfcp_zh.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,951 @@
-+/* 
-+ * $Id: zfcp_zh.c,v 1.3.2.4 2004/09/20 16:20:30 aherrman Exp $
-+ *
-+ * Module providing an interface for HBA API (FC-HBA) implementation
-+ * to the zfcp driver.
-+ *
-+ * (C) Copyright IBM Corp. 2002, 2003
-+ *
-+ * 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 of the License, or
-+ * (at your option) any later version. See the file COPYING for more
-+ * information.
-+ *
-+ * Authors:
-+ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
-+ *       Andreas Herrmann <aherrman at de.ibm.com>
-+ */
-+#include "zfcp.h"
-+#include "zfcp_zh.h"
-+
-+#include <linux/module.h>
-+
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+extern zfcp_data_t zfcp_data;
-+
-+struct zfcp_callbacks zfcp_callback = { };
-+
-+/**
-+ * zfcp_callback_do_adapter_add - callback wrapper
-+ * @*filp: passed through to the callback
-+ * @*a: adapter that was added
-+ *
-+ * We do not need irq safe spinlocks here, since we onyl do a read_lock. All
-+ * writers must use the _irq version though.
-+ */
-+void zfcp_callback_do_adapter_add(struct file *filp, const zfcp_adapter_t *a)
-+{
-+	unsigned long flags;
-+	
-+	read_lock_irqsave(&zfcp_callback.lock, flags);
-+
-+	if ((zfcp_callback.callbacks != NULL) &&
-+	    (zfcp_callback.callbacks->adapter_add != NULL)) {
-+		zfcp_callback.callbacks->adapter_add(filp, a->devno, a->wwnn,
-+						     a->wwpn);
-+	}
-+
-+	read_unlock_irqrestore(&zfcp_callback.lock, flags);
-+}
-+
-+/**
-+ * zfcp_callback_do_port_add - callback wrapper
-+ * @*filp: passed through to the callback
-+ * @*a: adapter the port was added to
-+ * @*p: port that was added
-+ */
-+void zfcp_callback_do_port_add(struct file *filp, const zfcp_adapter_t *a,
-+			       const zfcp_port_t *p)
-+{
-+	unsigned long flags;
-+
-+	read_lock_irqsave(&zfcp_callback.lock, flags);
-+
-+	if ((zfcp_callback.callbacks != NULL) &&
-+	    (zfcp_callback.callbacks->port_add != NULL)) {
-+		zfcp_callback.callbacks->port_add(filp, a->devno, p->wwpn,
-+						  p->wwnn, p->d_id);
-+	}
-+
-+	read_unlock_irqrestore(&zfcp_callback.lock, flags);
-+}
-+
-+/**
-+ * zfcp_callback_do_unit_add - callback wrapper
-+ * @*filp: passed through to the callback
-+ * @*a: Adapter the port belongs to
-+ * @*p: Port the unit belongs to
-+ * @*u: unit that was added
-+ */
-+void zfcp_callback_do_unit_add(struct file *filp, const zfcp_adapter_t *a,
-+			       const zfcp_port_t *p, const zfcp_unit_t *u)
-+{
-+	unsigned long flags;
-+
-+	read_lock_irqsave(&zfcp_callback.lock, flags);
-+
-+	if ((zfcp_callback.callbacks != NULL) &&
-+	    (zfcp_callback.callbacks->unit_add != NULL)) {
-+		zfcp_callback.callbacks->unit_add(filp, a->devno, p->wwpn,
-+				u->fcp_lun, a->scsi_host->host_no,
-+				u->device->channel, u->device->id,
-+				u->device->lun);
-+	}
-+
-+	read_unlock_irqrestore(&zfcp_callback.lock, flags);
-+}
-+
-+/**
-+ * zfcp_callback_do_incomming_els - callback wrapper
-+ * @*a: adapter the ELS was recieved
-+ * @*v: pointer to the ELS payload
-+ */
-+void zfcp_callback_do_incomming_els(const zfcp_adapter_t *a, const void *v)
-+{
-+	unsigned long flags;
-+
-+	read_lock_irqsave(&zfcp_callback.lock, flags);
-+
-+	if ((zfcp_callback.callbacks != NULL) &&
-+	    (zfcp_callback.callbacks->incomming_els != NULL)) {
-+		zfcp_callback.callbacks->incomming_els(a->devno, a->s_id, v);
-+	}
-+
-+	read_unlock_irqrestore(&zfcp_callback.lock, flags);
-+}
-+
-+/**
-+ * zfcp_callback_do_link_down - callback wrapper
-+ * @*a: adapter that lost the link
-+ */
-+void zfcp_callback_do_link_down(const zfcp_adapter_t *a)
-+{
-+	unsigned long flags;
-+
-+	read_lock_irqsave(&zfcp_callback.lock, flags);
-+
-+	if ((zfcp_callback.callbacks != NULL) &&
-+	    (zfcp_callback.callbacks->link_down != NULL)) {
-+		zfcp_callback.callbacks->link_down(a->s_id);
-+	}
-+
-+	read_unlock_irqrestore(&zfcp_callback.lock, flags);
-+}
-+
-+/**
-+ * zfcp_callback_do_link_up - callback wrapper
-+ * @*a: adapter with link up event
-+ */
-+void zfcp_callback_do_link_up(const zfcp_adapter_t *a)
-+{
-+	unsigned long flags;
-+
-+	read_lock_irqsave(&zfcp_callback.lock, flags);
-+
-+	if ((zfcp_callback.callbacks != NULL) &&
-+	    (zfcp_callback.callbacks->link_up != NULL)) {
-+		zfcp_callback.callbacks->link_up(a->s_id);
-+	}
-+
-+	read_unlock_irqrestore(&zfcp_callback.lock, flags);
-+}
-+
-+/**
-+ * zfcp_search_unit - search for a unit
-+ * @*port: pointer to port structure of port where unit is attached to
-+ * @lun: FC LUN of the unit to search for
-+ * @**unit: address to write pointer to found unit structure to
-+ * Return: 0 on success, -E* code else
-+ * Locks: lock/unlock of port->unit_list_lock
-+ *
-+ * Search for an unit and return its address.
-+ * See also zfcp_search_port_and_unit().
-+ */
-+static int zfcp_search_unit(zfcp_port_t *port, fcp_lun_t lun,
-+			    zfcp_unit_t **unit)
-+{
-+	zfcp_unit_t *u;
-+	unsigned long flags;
-+	int ret = 0;
-+
-+	*unit = NULL;
-+
-+	read_lock_irqsave(&port->unit_list_lock, flags);
-+	ZFCP_FOR_EACH_UNIT(port, u)
-+	{
-+		if (u->fcp_lun == lun) {
-+			*unit = u;
-+		}
-+	}
-+	read_unlock_irqrestore(&port->unit_list_lock, flags);
-+	if (NULL == *unit) {
-+		ret = -ENOUNI;
-+	}
-+	
-+	return ret;
-+}
-+
-+/**
-+ * zfcp_search_port_unit - search for a port and unit
-+ * @*adapter: pointer to adapter structure of adapter where port is attached to
-+ * @wwpn: of the port to search for
-+ * @lun: of the unit to search for, ignored if @**unit == NULL
-+ * @**port: address to write pointer to the found port structure to
-+ * @**unit: address to write pointer to the found unit structure to
-+ *	(iff @**unit != NULL)
-+ * Return: 0 on success, -E* code else
-+ * Locks: lock/unlock of adapter->port_list_lock
-+ *
-+ * Search for port and unit and return their addresses.
-+ * If @**unit == NULL search only for port.
-+ * If @**unit != NULL search also for unit.
-+ * See also zfcp_search_adapter_port_unit() and zfcp_search_unit().
-+ */
-+static int zfcp_search_port_unit(zfcp_adapter_t *adapter, wwn_t wwpn,
-+				 fcp_lun_t lun,  zfcp_port_t **port,
-+				 zfcp_unit_t **unit)
-+{
-+	zfcp_port_t *p;
-+	unsigned long flags;
-+	int ret = 0;
-+
-+	*port = NULL;
-+
-+	read_lock_irqsave(&adapter->port_list_lock, flags);
-+	ZFCP_FOR_EACH_PORT(adapter, p)
-+	{
-+		if (p->wwpn == wwpn) {
-+			*port = p;
-+		}
-+	}
-+	read_unlock_irqrestore(&adapter->port_list_lock, flags);
-+
-+	if (NULL == *port) {
-+		ret = -ENOPOR;
-+	} else {
-+		if (NULL != unit) {
-+			ret = zfcp_search_unit(*port, lun, unit);
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+/**
-+ * zfcp_search_adapter_port_unit - search for an adapter, port and unit
-+ * @devno: of the adapter to search for
-+ * @wwpn: of the port to search for, ignored if **port == NULL
-+ * @lun: of the unit to search for, ignored if **port == NULL || **unit == NULL
-+ * @**adapter: address to write pointer to found adapter structure to
-+ * @**port: address to write pointer to found port structure to,
-+ *	(iff **port != NULL)
-+ * @**unit: address to write pointer to found unit structure to,
-+ *	(iff **port != NULL && **unit != NULL)
-+ * Return: 0 on success, -E* code else
-+ * Locks: lock/unlock of zfcp_data.adapter_list_lock
-+ *
-+ * Search for an adapter, port and unit and return their addresses.
-+ * If @**port == NULL, search only for an adapter.
-+ * If @**port != NULL, search also for port.
-+ * If @**port != NULL and @**unit != NULL search also for port and unit.
-+ * See also zfcp_search_port_unit().
-+ */
-+static int zfcp_search_adapter_port_unit(devno_t devno, wwn_t wwpn,
-+					 fcp_lun_t lun,
-+					 zfcp_adapter_t **adapter,
-+					 zfcp_port_t **port, zfcp_unit_t **unit)
-+{
-+	zfcp_adapter_t *a;
-+	unsigned long flags;
-+	int ret = 0;
-+
-+	if (NULL == adapter) {
-+		return -EINVAL;
-+	}
-+
-+	*adapter = NULL;
-+	
-+	read_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
-+	ZFCP_FOR_EACH_ADAPTER(a)
-+	{
-+		if (a->devno == devno) {
-+			if (!atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED,
-+					      &a->status) &&
-+			    atomic_test_mask((ZFCP_STATUS_COMMON_RUNNING |
-+					      ZFCP_STATUS_COMMON_UNBLOCKED),
-+					     &a->status)) {
-+				*adapter = a;
-+			}
-+			break;
-+		}
-+	}
-+	read_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
-+	if (NULL == *adapter) {
-+		ret = -ENOADA;
-+	} else {
-+		if (NULL != port) {
-+			ret = zfcp_search_port_unit(*adapter, wwpn, lun,
-+						    port, unit);
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+/**
-+ * get_config_units - create unit config events
-+ * @*filp: passed through to callback
-+ * @*adapter: the unit belongs to
-+ * @*port: the unit belongs to
-+ *
-+ * generate one unit add event for each unit below the passed port
-+ */
-+static inline int get_config_units(struct file *filp, zfcp_adapter_t *adapter,
-+				   zfcp_port_t *port)
-+{
-+	zfcp_unit_t *unit;
-+	unsigned long flags;
-+	int ret = 0;
-+	
-+	read_lock_irqsave(&port->unit_list_lock, flags);
-+
-+	ZFCP_FOR_EACH_UNIT(port, unit)
-+	{
-+		if (unit->device != NULL) {
-+			zfcp_callback_do_unit_add(filp, adapter, port, unit);
-+			++ret;
-+		}
-+	}
-+
-+	read_unlock_irqrestore(&port->unit_list_lock, flags);
-+
-+	return ret;
-+}
-+
-+/**
-+ * get_config_ports - create port config events, or search for a port.
-+ * @*filp: passed through to callback
-+ * @*adapter: the port belongs to
-+ * @wwpn: generate port add events if 0, or search port and get_config_units()
-+ */
-+static inline int get_config_ports(struct file *filp, zfcp_adapter_t *adapter,
-+				   wwn_t wwpn, unsigned int config_flags)
-+{
-+	zfcp_port_t *port;
-+	unsigned long flags;
-+	int ret = 0;
-+
-+	read_lock_irqsave(&adapter->port_list_lock, flags);
-+
-+	ZFCP_FOR_EACH_PORT(adapter, port)
-+	{
-+		if (ZH_GET_CONFIG_PORTS == config_flags) {
-+			/* ignore name server port */
-+			if (0 != port->wwpn) {
-+				zfcp_callback_do_port_add(filp, adapter, port);
-+				++ret;
-+			}
-+		} else {
-+			if (port->wwpn != wwpn) {
-+				ret = -ENOPOR;
-+			} else {
-+				ret = get_config_units(filp, adapter, port);
-+				break;
-+			}
-+		}
-+	}
-+
-+	read_unlock_irqrestore(&adapter->port_list_lock, flags);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zfcp_zh_get_config - Prepare config for userspace
-+ * @*filp: passed through to callback
-+ * @devno: generate adapter add events if 0, or search and get_config_ports()
-+ * @wwpn: passed to get_config_ports()
-+ *
-+ * TNG get_config method, uses the callback methods and so generates
-+ * events for each configured item. The events are private to the
-+ * passed file descriptor.
-+ */
-+int zfcp_zh_get_config(struct file *filp, devno_t devno, wwn_t wwpn,
-+		unsigned int config_flags)
-+{
-+	zfcp_adapter_t *adapter;
-+	unsigned long flags;
-+	int ret = 0;
-+
-+	read_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
-+
-+	ZFCP_FOR_EACH_ADAPTER(adapter)
-+	{
-+		if (ZH_GET_CONFIG_ADAPTERS == config_flags) {
-+			zfcp_callback_do_adapter_add(filp, adapter);
-+			++ret;
-+		} else {
-+			if (adapter->devno != devno) {
-+				ret = -ENOADA;
-+			} else {
-+				ret = get_config_ports(filp, adapter, wwpn,
-+						       config_flags);
-+				break;
-+			}
-+				
-+		}
-+	}
-+		
-+	read_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zfcp_zh_get_adapter_attributes - provide data for the api call
-+ * @devno: of the adapter 
-+ * @attr: pointer to struct zfcp_adapter_attributes to return attributes
-+ * Return: 0 on success, -E* else
-+ * Context: user
-+ * Locks: lock/unlock zfcp_data.adapter_list_lock
-+ */
-+int zfcp_zh_get_adapter_attributes(devno_t devno,
-+				   struct zfcp_adapter_attributes *attr)
-+{
-+	zfcp_adapter_t *adapter;
-+	int ret = 0;
-+	
-+	memset(attr, 0, sizeof(*attr));
-+
-+	ret = zfcp_search_adapter_port_unit(devno, 0, 0, &adapter, NULL, NULL);
-+	if (ret != 0) {
-+		return ret;
-+	}
-+
-+	strcpy(attr->manufacturer, "IBM");
-+	strncpy(attr->serial_number, adapter->serial_number, 32);
-+	switch (adapter->hydra_version) {
-+	case FSF_ADAPTER_TYPE_FICON:
-+		strcpy(attr->model, "FICON FCP");
-+		break;
-+	case FSF_ADAPTER_TYPE_FICON_EXPRESS:
-+		strcpy(attr->model, "FICON Express FCP");
-+		break;
-+	}
-+	strcpy(attr->model_description, "zSeries Fibre Channel Adapter");
-+	attr->node_wwn = adapter->wwnn;
-+	sprintf(attr->hardware_version, "0x%x",
-+		adapter->hardware_version);
-+	sprintf(attr->driver_version, "0x%x", zfcp_data.driver_version);
-+	sprintf(attr->firmware_version, "0x%x",
-+		adapter->fsf_lic_version);
-+	attr->vendor_specific_id = 42;
-+	attr->number_of_ports = 1;
-+	strcpy(attr->driver_name, "zfcp.o");
-+	/* option_rom_version not used, node_symbolic_name not set */
-+
-+	return ret;
-+}
-+
-+/**
-+ * zfcp_zh_get_port_statistics - Retrieve statistics of adapter port
-+ * @devno: adapter of which port data should be reported
-+ * @stat: pointer to struct zfcp_port_statistics to return statistics
-+ * Return: 0 on success,  -E* else
-+ * Context: user
-+ * Locks: lock/unlock zfcp_data.adapter_list_lock
-+ */
-+int zfcp_zh_get_port_statistics(devno_t devno,
-+				struct zfcp_port_statistics *stat)
-+{
-+	zfcp_adapter_t *adapter;
-+	fsf_qtcb_bottom_port_t data;
-+	int ret = 0;
-+
-+	memset(stat, 0, sizeof(*stat));
-+	
-+	ret = zfcp_search_adapter_port_unit(devno, 0, 0, &adapter, NULL, NULL);
-+	if (ret != 0) {
-+		return ret;
-+	}
-+
-+	if(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT){
-+		ret = zfcp_fsf_exchange_port_data(adapter, &data);
-+		if (0 == ret) {
-+			/* convert fsf_qtcb_bottom_port into
-+			   zfcp_port_statistics */
-+			stat->last_reset = data.seconds_since_last_reset;
-+			stat->tx_frames = data.tx_frames;
-+			stat->tx_words = data.tx_words;
-+			stat->rx_frames = data.rx_words;
-+			stat->lip = data.lip;
-+			stat->nos = data.nos;
-+			stat->error_frames = data.error_frames;
-+			stat->dumped_frames = data.dumped_frames;
-+			stat->link_failure = data.link_failure;
-+			stat->loss_of_sync = data.loss_of_sync;
-+			stat->loss_of_signal = data.loss_of_signal;
-+			stat->prim_seq_prot_error = data.psp_error_counts;
-+			stat->invalid_tx_words = data.invalid_tx_words;
-+			stat->invalid_crc = data.invalid_crcs;
-+			stat->input_requests = data.input_requests;
-+			stat->output_requests = data.output_requests;
-+			stat->control_requests = data.control_requests;
-+			stat->input_megabytes = data.input_mb;
-+			stat->output_megabytes = data.output_mb;
-+		}
-+	} else {
-+		ret = -EOPNOTSUPP;
-+	}
-+
-+	return ret;
-+}
-+
-+
-+/**
-+ * zfcp_zh_get_port_attributes - Retrieve attributes of adapter port
-+ * @devno: adapter of which port data should be reported
-+ * @attr: pointer to struct zfcp_port_attributes to return attributes
-+ * Return: 0 on success,  -E* else
-+ * Context: user
-+ * Locks: lock/unlock zfcp_data.adapter_list_lock, adapter->port_list_lock
-+ */
-+int zfcp_zh_get_port_attributes(devno_t devno,
-+				struct zfcp_port_attributes *attr)
-+{
-+	zfcp_adapter_t *adapter;
-+	zfcp_port_t *port;
-+	fsf_qtcb_bottom_port_t data;
-+	unsigned long flags;
-+	int ret = 0;
-+
-+	memset(attr, 0, sizeof(*attr));
-+	
-+	ret = zfcp_search_adapter_port_unit(devno, 0, 0, &adapter, NULL, NULL);
-+	if (ret != 0) {
-+		return ret;
-+	}
-+
-+	attr->wwnn = adapter->wwnn;
-+	attr->wwpn = adapter->wwpn;
-+	attr->speed = adapter->fc_link_speed;
-+	attr->discovered_ports = adapter->ports;
-+
-+	/* ignore nameserver port */
-+	read_lock_irqsave(&adapter->port_list_lock, flags);
-+	ZFCP_FOR_EACH_PORT(adapter, port)
-+	{
-+		if(port->wwpn == 0){
-+			if(attr->discovered_ports)
-+				--attr->discovered_ports;
-+			break;
-+		}
-+	}
-+	read_unlock_irqrestore(&adapter->port_list_lock, flags);
-+
-+	if(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT){
-+		ret = zfcp_fsf_exchange_port_data(adapter, &data);
-+		if (0 == ret) {
-+			/* convert fsf_qtcb_bottom_port into
-+			   zfcp_port_attributes */
-+			attr->fcid = data.fc_port_id;
-+			attr->type = data.port_type;
-+			attr->state = data.port_state;
-+			attr->supported_class_of_service =
-+				data.class_of_service;
-+			attr->supported_speed = data.supported_speed;
-+			attr->max_frame_size = data.maximum_frame_size;
-+			memcpy(&attr->supported_fc4_types,
-+			       &data.supported_fc4_types, 32);
-+			memcpy(&attr->active_fc4_types,
-+			       &data.active_fc4_types, 32);
-+		}
-+	} else {
-+		ret = -EOPNOTSUPP;
-+	}
-+	/* fabric_name and symbolic_name not set */
-+
-+	return ret;
-+}
-+
-+/**
-+ * zfcp_zh_get_discovered_port_attributes - Retrieve attributes of target port
-+ * @devno: adapter for which port data should be reported
-+ * @wwpn: wwn of discovered port
-+ * @attr: pointer to struct zfcp_port_attributes to return attributes
-+ * Return: 0 on success,  -E* else
-+ * Context: user
-+ * Locks: lock/unlock zfcp_data.adapter_list_lock, adapter->port_list_lock
-+ */
-+int zfcp_zh_get_dport_attributes(devno_t devno, wwn_t wwpn,
-+				 struct zfcp_port_attributes *attr)
-+{
-+	zfcp_adapter_t *adapter;
-+	zfcp_port_t *port;
-+	struct ct_iu_ga_nxt *ct_iu_resp;
-+	int ret;
-+
-+	memset(attr, 0, sizeof(*attr));
-+
-+	ret = zfcp_search_adapter_port_unit(devno, wwpn, 0,
-+					    &adapter, &port, NULL);
-+	if (ret != 0) {
-+		return ret;
-+	}
-+
-+	ct_iu_resp = kmalloc(sizeof(*ct_iu_resp), GFP_KERNEL);
-+	if (0 == ct_iu_resp) {
-+		return -ENOMEM;
-+	}
-+
-+	ret = zfcp_ns_ga_nxt_request(port, ct_iu_resp);
-+	if (0 == ret) {
-+		attr->wwnn = port->wwnn;
-+		attr->wwpn = port->wwpn;
-+		attr->fabric_name = (wwn_t) ct_iu_resp->du.fabric_wwn;
-+		attr->fcid = port->d_id;
-+
-+		/* map FC-GS-2 port types to HBA API
-+		   port types */
-+		switch(ct_iu_resp->du.port_type) {
-+		case 0x01: /* N_Port */
-+			attr->type = FSF_HBA_PORTTYPE_NPORT;
-+		case 0x02: /* NL_Port */
-+			attr->type = FSF_HBA_PORTTYPE_NLPORT;
-+		case 0x81: /* F_Port */
-+			attr->type = FSF_HBA_PORTTYPE_FPORT;
-+		case 0x82: /* FL_Port */
-+			attr->type = FSF_HBA_PORTTYPE_FLPORT;
-+		case 0x03: /* F/NL_Port */
-+		case 0x7f: /* Nx_Port */
-+		case 0x84: /* E_Port */
-+			attr->type = FSF_HBA_PORTTYPE_OTHER;
-+		case 0x00: /* Unidentified */
-+		default: /* reserved */
-+			attr->type = FSF_HBA_PORTTYPE_UNKNOWN;
-+		}
-+
-+		attr->state = FSF_HBA_PORTSTATE_UNKNOWN;
-+		attr->supported_class_of_service = ct_iu_resp->du.cos;
-+		memcpy(&attr->active_fc4_types, &ct_iu_resp->du.fc4_types, 32);
-+		memcpy(&attr->symbolic_name,
-+		       &ct_iu_resp->du.node_symbolic_name,
-+		       ct_iu_resp->du.port_symbolic_name_length);
-+	}
-+	kfree(ct_iu_resp);
-+
-+	/* supported_speed, speed, max_frame_size, supported_fc4_types,
-+	   discovered_ports not set */
-+
-+	return ret;
-+}
-+
-+
-+/**
-+ * zfcp_zh_send_ct_handler() - handler for zfcp_zh_send_ct()
-+ * @data: a pointer to struct zfcp_send_ct, It was set as handler_data
-+ *	in zfcp_zh_send_ct().
-+ * Context: interrupt
-+ *
-+ * This handler is called on completion of a send_ct request. We just wake up
-+ * our own zfcp_zh_send_ct() function.
-+ */
-+static void zfcp_zh_send_ct_handler(unsigned long data)
-+{
-+
-+        struct zfcp_send_ct *ct = (struct zfcp_send_ct *) data;
-+	struct completion *wait = (struct completion*) ct->completion;
-+
-+	complete(wait);
-+	return;
-+}
-+
-+
-+/**
-+ * zfcp_zh_send_ct() - send a CT_IU containing FC-GS-4 command
-+ * @devno: adapter for which port data should be reported
-+ * @req: scatter-gather list with request data
-+ * @req_count: number of elements in @req
-+ * @resp: scatter-gather list for response data
-+ * @resp_count: number of elements in @resp
-+ * Return: 0 on success,  -E* else
-+ * Context: user
-+ * Locks: lock/unlock zfcp_data.adapter_list_lock
-+ *
-+ * Note: Currently only requests to the nameserver port are supported.
-+ *	Other well-known ports currently have no representation in zfcp.
-+ */
-+int zfcp_zh_send_ct(devno_t devno,
-+		    struct scatterlist *req, unsigned int req_count,
-+		    struct scatterlist *resp, unsigned int resp_count)
-+{
-+	struct zfcp_send_ct ct;
-+	zfcp_adapter_t *adapter;
-+	struct ct_hdr *ct_header;
-+	int ret;
-+
-+	DECLARE_COMPLETION(wait);
-+
-+	memset(&ct, 0, sizeof(ct));
-+
-+	ret = zfcp_search_adapter_port_unit(devno, 0, 0, &adapter, NULL, NULL);
-+	if (ret != 0) {
-+		return ret;
-+	}
-+
-+	ct_header = (struct ct_hdr *) req[0].address;
-+	if ((ct_header->gs_type != ZFCP_CT_DIRECTORY_SERVICE) ||
-+	    (ct_header->gs_subtype != ZFCP_CT_NAME_SERVER)) {
-+		/* currently only nameserver requests are supported */
-+		ZFCP_LOG_NORMAL("Tried to send CT IU to other service than "
-+				"Name Server Directory Service. This is "
-+				"currently not supported.\n");
-+		return -ENOPOR;
-+	}
-+
-+	if (!adapter->nameserver_port) {
-+		ZFCP_LOG_NORMAL("Name Server port not available\n.");
-+		return -ENOPOR;
-+	}
-+	ct.port = adapter->nameserver_port;
-+
-+	ct.req = req;
-+	ct.resp = resp;
-+	ct.req_count = req_count;
-+	ct.resp_count = resp_count;
-+	ct.handler = zfcp_zh_send_ct_handler;
-+	ct.handler_data = (unsigned long) &ct;
-+	ct.completion = &wait;
-+	ct.timeout = ZFCP_CT_TIMEOUT;
-+
-+	ret = zfcp_fsf_send_ct(&ct, 0, 0);
-+	if (0 == ret) {
-+		wait_for_completion(&wait);
-+		if (ct.status)
-+			ret = -EIO;
-+		else
-+			zfcp_check_ct_response((void *)resp->address);
-+	}
-+
-+	return ret;
-+}
-+
-+/**
-+ * zfcp_zh_callbacks_register - register callbacks of zfcp_hbaapi
-+ * @*cb: pointer to the callback structure
-+ * Return: 0 on success, else -E* code
-+ * Locks: lock/unlock of zfcp_callback.lock
-+ * Context: user
-+ */
-+int zfcp_zh_callbacks_register(struct zfcp_zh_callbacks *cb)
-+{
-+	unsigned long flags;
-+	int ret = 0;
-+	
-+	write_lock_irqsave(&zfcp_callback.lock, flags);
-+
-+	if (zfcp_callback.callbacks != NULL) {
-+		ret = -EBUSY;
-+	} else {
-+		zfcp_callback.callbacks = cb;
-+	}
-+
-+	write_unlock_irqrestore(&zfcp_callback.lock, flags);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zfcp_zh_callbacks_unregister - unregister callbacks for zfcp_hbaapi
-+ * @*cb: pointer to the callback structure
-+ * Return: 0 on success, -E* code else
-+ * Locks: lock/unlock of zfcp_callback.lock
-+ * Context: user
-+ */
-+int zfcp_zh_callbacks_unregister(struct zfcp_zh_callbacks *cb)
-+{
-+	unsigned long flags;
-+	int ret = 0;
-+
-+	write_lock_irqsave(&zfcp_callback.lock, flags);
-+
-+	if (zfcp_callback.callbacks == NULL) {
-+		ret = -EBUSY;
-+	} else {
-+		if (zfcp_callback.callbacks != cb) {
-+			ZFCP_LOG_DEBUG("Tried to unregister callbacks from "
-+				       "different address.\n");
-+			ret = -EINVAL;
-+		} else {
-+			zfcp_callback.callbacks = NULL;
-+		}
-+	}
-+
-+	write_unlock_irqrestore(&zfcp_callback.lock, flags);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zfcp_zh_assert_fclun_zero - Assert that there is a FC LUN 0
-+ * @devno: devno of the adapter
-+ * @wwpn: wwpn of the discovered port
-+ *
-+ * Look for an unit at the passed adapter:port with FC LUN 0.
-+ * Add it if it does not exist. This unit is needed for
-+ * REPORT_LUNS.
-+ *
-+ * Note: No unit with a FC LUN 0 can be added for the same adpater and port
-+ *	after this call. (We could add an "overwriteable" flag to the
-+ *	zfcp_unit_t structure as a work-around for this.)
-+ */
-+int zfcp_zh_assert_fclun_zero(devno_t devno, wwn_t wwpn)
-+{
-+	int ret;
-+	zfcp_config_record_t cfg;
-+	zfcp_adapter_t *adapter;
-+	zfcp_port_t *port;
-+	zfcp_unit_t *unit;
-+
-+	ret = zfcp_search_adapter_port_unit(devno, wwpn, 0,
-+					    &adapter, &port, &unit);
-+	if (ret != -ENOUNI) {
-+		return ret;
-+	}
-+
-+	memset (&cfg, 0, sizeof(cfg));
-+	cfg.devno = devno;
-+	cfg.scsi_id = port->scsi_id;
-+	cfg.wwpn = port->wwpn;
-+	cfg.scsi_lun = port->max_scsi_lun +1;
-+
-+	ret = zfcp_config_parse_record_add(&cfg);
-+	if (0 != ret) {
-+		return ret;
-+	}
-+
-+	/* wait until the unit is opened */
-+	return zfcp_erp_wait(port->adapter);
-+}
-+
-+/** 
-+ * zfcp_zh_send_scsi - Send a SCSI command to an unit
-+ * @devno: devno of the adapter
-+ * @wwpn: WWPN of the discovered port the unit is attached to
-+ * @lun: FC LUN of the unit to send the command to
-+ * @cmnd: address of the prepared Scsi_Cmnd
-+ * Return: 0 on success, > 0 on SCSI error, -E* code else
-+ *
-+ * FC LUN 0 is handled with extra care, to be able to send REPORT_LUNS.
-+ */
-+int zfcp_zh_send_scsi(devno_t devno, wwn_t wwpn, fcp_lun_t lun,
-+		      Scsi_Cmnd *cmnd)
-+{
-+	int ret;
-+	zfcp_adapter_t *adapter;
-+	zfcp_port_t *port;
-+	zfcp_unit_t *unit;
-+	
-+	ret = zfcp_search_adapter_port_unit(devno, wwpn, lun,
-+					    &adapter, &port, &unit);
-+	if (ret != 0) {
-+		return ret;
-+	}
-+
-+	ret = zfcp_scsi_command_sync(unit, cmnd);
-+	if (ret < 0) {
-+		return ret;
-+	} else {
-+		return cmnd->result;
-+	}
-+}
-+
-+/**
-+ * zfcp_zh_send_els_handler() - handler for zfcp_zh_send_els()
-+ * @data: a pointer to struct zfcp_send_els. It is set as handler_data
-+ *	in zfcp_zh_send_els().
-+ * Context: interrupt
-+ *
-+ * This handler is called on completion of a send_els request. We just wake up
-+ * our own zfcp_zh_send_els() function.
-+ */
-+static void
-+zfcp_zh_send_els_handler(unsigned long data)
-+{
-+  struct zfcp_send_els *els = (struct zfcp_send_els *) data;
-+  complete(els->completion);
-+}
-+
-+/**
-+ * zfcp_zh_send_els - send an els to a port
-+ * @devno: of the adapter to send via
-+ * @wwpn: of the port to send to
-+ * @send: scatterlist describing the els payload to be sent
-+ * @send_count: number of elements in the send scatterlist
-+ * @receive: scatterlist describing buffers for the reply payload
-+ * @receive_count: number of elements in the receive scatterlist
-+ * Return: 0 on success, -E* code else
-+ * Locks: lock/unlock of zfcp_data.adapter_list_lock, adapter->port_list_lock
-+ */
-+int zfcp_zh_send_els(devno_t devno, wwn_t wwpn, struct scatterlist *send,
-+		     unsigned int send_count, struct scatterlist *receive,
-+		     unsigned int receive_count)
-+{
-+	int ret;
-+	struct zfcp_send_els *req;
-+	zfcp_adapter_t *adapter;
-+	zfcp_port_t *port;
-+
-+	DECLARE_COMPLETION(wait);
-+
-+	ret = zfcp_search_adapter_port_unit(devno, wwpn, 0,
-+					    &adapter, &port, NULL);
-+	if (ret != 0) {
-+		return ret;
-+	}
-+
-+	req = kmalloc(sizeof(*req), GFP_KERNEL);
-+	if (NULL == req) {
-+		ret = -ENOMEM;
-+		goto out;
-+	}
-+	memset(req, 0, sizeof(*req));
-+
-+	req->port = port;
-+	req->req = send;
-+	req->req_count = send_count;
-+	req->resp = receive;
-+	req->resp_count = receive_count;
-+	req->handler = zfcp_zh_send_els_handler;
-+	req->handler_data = (unsigned long) req;
-+	req->completion = &wait;
-+
-+	ret = zfcp_fsf_send_els(req);
-+	if (0 == ret) {
-+		wait_for_completion(&wait);
-+		if (req->status)
-+			ret = -EIO;
-+	}
-+
-+	kfree(req);
-+
-+	goto out;
-+
-+out:
-+	return ret;
-+}
-+
-+EXPORT_SYMBOL(zfcp_zh_callbacks_register);
-+EXPORT_SYMBOL(zfcp_zh_callbacks_unregister);
-+EXPORT_SYMBOL(zfcp_zh_get_config);
-+EXPORT_SYMBOL(zfcp_zh_get_adapter_attributes);
-+EXPORT_SYMBOL(zfcp_zh_get_port_attributes);
-+EXPORT_SYMBOL(zfcp_zh_get_port_statistics);
-+EXPORT_SYMBOL(zfcp_zh_get_dport_attributes);
-+EXPORT_SYMBOL(zfcp_zh_send_ct);
-+EXPORT_SYMBOL(zfcp_zh_send_els);
-+EXPORT_SYMBOL(zfcp_zh_send_scsi);
-+EXPORT_SYMBOL(zfcp_zh_assert_fclun_zero);
-=== drivers/s390/scsi/zfcp_fsf.h
-==================================================================
---- drivers/s390/scsi/zfcp_fsf.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/scsi/zfcp_fsf.h   (/trunk/2.4.27)   (revision 52)
-@@ -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;
-+
-+typedef u64 fsf_wwn_t;
-+
-+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;
-+
-+typedef struct fsf_plogi {
-+	u32			code;
-+	fsf_nport_serv_param_t	serv_param;
-+} __attribute__ ((packed)) fsf_plogi_t;
-+
-+#define FSF_FCP_CMND_SIZE	288
-+#define FSF_FCP_RSP_SIZE	128
-+
-+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;
-+
-+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 */
-+
-=== drivers/s390/scsi/zh_ioctl32.h
-==================================================================
---- drivers/s390/scsi/zh_ioctl32.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/scsi/zh_ioctl32.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,26 @@
-+/* 
-+ * $Id: zh_ioctl32.h,v 1.2.2.1 2004/01/26 17:26:34 mschwide Exp $
-+ *
-+ * Module providing an interface for HBA API (FC-HBA) implementation
-+ * to the zfcp driver.
-+ *
-+ * (C) Copyright IBM Corp. 2003
-+ *
-+ * 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 of the License, or
-+ * (at your option) any later version. See the file COPYING for more
-+ * information.
-+ *
-+ * Authors:
-+ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
-+ *       Andreas Herrmann <aherrman at de.ibm.com>
-+ */
-+
-+#ifndef _ZH_IOCTL32_H_
-+#define _ZH_IOCTL32_H_
-+
-+int zh_register_ioctl_conversion(void);
-+int zh_unregister_ioctl_conversion(void);
-+
-+#endif /* _ZH_IOCTL32_H_ */
-=== drivers/s390/scsi/zh_main.c
-==================================================================
---- drivers/s390/scsi/zh_main.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/scsi/zh_main.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,2046 @@
-+/* 
-+ * $Id: zh_main.c,v 1.10.2.3 2004/09/17 08:29:41 aherrman Exp $
-+ *
-+ * Module providing an interface for HBA API (FC-HBA) implementation
-+ * to the zfcp driver.
-+ *
-+ * (C) Copyright IBM Corp. 2003
-+ *
-+ * 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 of the License, or
-+ * (at your option) any later version. See the file COPYING for more
-+ * information.
-+ *
-+ * Authors:
-+ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
-+ *       Andreas Herrmann <aherrman at de.ibm.com>
-+ */
-+
-+/*
-+ * To automatically create the device node (after module loading) use:
-+ *
-+ *	minor=`cat /proc/misc | awk "\\$2==\\"zfcp_hbaapi\\" {print \\$1}"`
-+ *	mknod /dev/zfcp_hbaapi c 10 $minor
-+ */
-+
-+#define HBAAPI_REVISION "$Revision: 1.10.2.3 $"
-+
-+#include <linux/config.h>
-+#include <linux/module.h>
-+#include <linux/init.h>
-+
-+#include <linux/kmod.h>
-+
-+#include <linux/kernel.h>
-+#include <linux/slab.h>
-+#include <linux/types.h>
-+#include <linux/miscdevice.h>
-+#include <linux/stringify.h>
-+
-+#include <asm/uaccess.h>
-+#include <asm/current.h>
-+#include <asm/atomic.h>
-+#include <asm/div64.h>
-+#include <asm/semaphore.h>
-+
-+#include <linux/spinlock.h>
-+
-+#include "zh.h"
-+#include "zfcp.h"
-+#include "zfcp_zh.h"
-+
-+#ifdef CONFIG_S390_SUPPORT
-+# include "zh_ioctl32.h"
-+#endif
-+
-+MODULE_AUTHOR("Stefan Voelkel <Stefan.Voelkel at millenux.com>, "
-+	"Andreas Herrmann  <aherrman at de.ibm.com>, "
-+	"IBM Deutschland Entwicklung GmbH");
-+MODULE_DESCRIPTION("Interface for HBA API to FCP HBA driver for IBM zSeries, "
-+	HBAAPI_REVISION);
-+MODULE_LICENSE("GPL");
-+
-+EXPORT_NO_SYMBOLS;
-+
-+/*
-+ * module and kernel parameters
-+ */
-+int maxshared = ZH_EVENTS_MAX;
-+int maxpolled = ZH_EVENTS_MAX;
-+int minor = MISC_DYNAMIC_MINOR;
-+
-+#ifdef MODULE
-+MODULE_PARM(maxshared,"i");
-+MODULE_PARM_DESC(maxshared, "Maximum number of events in the shared event"
-+		" queue, defaults to "__stringify(ZH_EVENTS_MAX));
-+
-+MODULE_PARM(maxpolled,"i");
-+MODULE_PARM_DESC(maxpolled, "Maximum number of events in the polled event"
-+		" queue, defaults to "__stringify(ZH_EVENTS_MAX));
-+
-+MODULE_PARM(minor, "i");
-+MODULE_PARM_DESC(minor, "Minor of the misc device to register, defaults to"
-+		"dynamic registration");
-+#else
-+static int __init zh_maxshared_setup(char *str)
-+{
-+	maxshared = simple_strtol(str, NULL, 0);
-+	return 1;
-+}
-+__setup("zfcp_hbaapi_maxshared=", zh_maxshared_setup);
-+
-+static int __init zh_maxpolled_setup(char *str)
-+{
-+	maxpolled = simple_strtol(str, NULL, 0);
-+	return 1;
-+}
-+__setup("zfcp_hbaapi_maxpolled=", zh_maxpolled_setup);
-+
-+static int __init zh_minor_setup(char *str)
-+{
-+	minor = simple_strtol(str, NULL, 0);
-+	return 1;
-+}
-+__setup("zfcp_hbaapi_minor=", zh_minor_setup);
-+#endif
-+
-+/**
-+ * struct zh_event_item - An event
-+ * @event: the event itself, as it is passed to userspace
-+ * @count: reference counter
-+ * @list: list handling
-+ *
-+ * An item in the kernel event queue.
-+ */
-+struct zh_event_item
-+{
-+	struct zh_event event;
-+	atomic_t count;
-+	struct list_head list;
-+};
-+
-+/**
-+ * struct zh_config - An config entry
-+ * @event: the event itself
-+ * @list: list handling
-+ *
-+ * A non-counted event. Used for configuration and polled events.
-+ */
-+struct zh_config
-+{
-+	struct zh_event event;
-+	struct list_head list;
-+};
-+
-+/**
-+ * struct zh_client - per client data
-+ * @registered: 1 if the fd is registered for events, else 0
-+ * @lost: 1 if the fd has lost an event, else 0
-+ * @*last: pointer to the last delivered event or NULL
-+ * @config: list of private config events
-+ * @clients: list handling for list of clients
-+ *
-+ * This structure is attached to filp->private_data and used throughout the
-+ * module to save per client data. 
-+ */
-+struct zh_client
-+{
-+	struct semaphore sem;
-+	unsigned int registered:1;
-+	unsigned int lost:1;
-+	struct list_head *last;
-+	struct list_head config;
-+	struct list_head clients;
-+};
-+
-+/* ioctl workers
-+ * We could inline them since only about two are called from more than one
-+ * place. OTOH this is no time critical code */
-+static int zh_ioc_get_adapterattributes(struct zh_get_adapterattributes *);
-+static int zh_ioc_get_portattributes(struct zh_get_portattributes *);
-+static int zh_ioc_get_portstatistics(struct zh_get_portstatistics *);
-+static int zh_ioc_get_dportattributes(struct zh_get_portattributes *);
-+static int zh_ioc_get_config(struct zh_get_config *, struct file *);
-+static int zh_ioc_clear_config(struct file *);
-+static int zh_ioc_get_event_buffer(struct zh_get_event_buffer *);
-+static int zh_ioc_event_start(struct file *);
-+static int zh_ioc_event_stop(struct file *);
-+static int zh_ioc_event(struct zh_event *, struct file *);
-+static int zh_ioc_event_insert(void);
-+static int zh_ioc_scsi_inquiry(struct zh_scsi_inquiry *);
-+static int zh_ioc_scsi_read_capacity(struct zh_scsi_read_capacity *);
-+static int zh_ioc_scsi_report_luns(struct zh_scsi_report_luns *);
-+static int zh_ioc_get_rnid(struct zh_get_rnid *);
-+static int zh_ioc_send_rnid(struct zh_send_rnid *);
-+static int zh_ioc_send_ct(struct zh_send_ct *);
-+
-+/* callbacks for asynchronous event generation. called form zfcp.o */
-+static void zh_cb_adapter_add(struct file *, devno_t, wwn_t, wwn_t);
-+static void zh_cb_port_add(struct file *, devno_t, wwn_t, wwn_t, fc_id_t);
-+static void zh_cb_unit_add(struct file *, devno_t, wwn_t, fcp_lun_t,
-+			   unsigned int, unsigned int, unsigned int,
-+			   unsigned int);
-+static void zh_cb_incomming_els(const devno_t, const fc_id_t, const void *);
-+static void zh_cb_link_down(const fc_id_t);
-+static void zh_cb_link_up(const fc_id_t);
-+
-+/* implemented file operations for our device file */
-+static int zh_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
-+static int zh_open(struct inode *, struct file *);
-+static ssize_t zh_read(struct file *, char *, size_t, loff_t *);
-+static int zh_release(struct inode *, struct file *);
-+
-+/* auxiliary functions */
-+static void zh_map_port_speed(u32 *, int);
-+#define ZH_PORT_OPERATING_SPEED 1
-+#define ZH_PORT_SUPPORTED_SPEED 0
-+
-+/*
-+ * zh_fops - Device file operations
-+ *
-+ * Structure describing the possible operations on our device file
-+ * */
-+static struct file_operations zh_fops = {
-+	.ioctl = zh_ioctl,
-+	.open = zh_open,
-+	.release = zh_release,
-+	.read = zh_read,
-+};
-+
-+/*
-+ * struct zh_misc - misc device description
-+ * @minor: our minor
-+ * @name: that is what we are called
-+ * @fops: file operations
-+ *
-+ * Passed to register_misc
-+ */
-+static struct miscdevice zh_misc = {
-+	.name = "zfcp_hbaapi",
-+	.fops = &zh_fops
-+};
-+
-+/*
-+ * struct zh_callbacks - Callbacks for zfcp events
-+ * @adapter_add: called on adapter add events
-+ *
-+ * This is passed to the zfcp_register_callbacks method, to enable
-+ * zfcp to call our hooks.
-+ */
-+static struct zfcp_zh_callbacks zh_callbacks = {
-+	.adapter_add = zh_cb_adapter_add,
-+	.port_add = zh_cb_port_add,
-+	.unit_add = zh_cb_unit_add,
-+	.incomming_els = zh_cb_incomming_els,
-+	.link_down = zh_cb_link_down,
-+	.link_up = zh_cb_link_up
-+};
-+
-+/**
-+ * struct zh_events
-+ * @lock: spinlock protecting the structure
-+ * @wq: wait queue to wait for events
-+ * @registered: number of processes to notify on events
-+ * @pending: number of events in the queue
-+ * @queue: list of events
-+ * @clients: anchor for list of clients
-+ *
-+ * This structure contains all data needed for asynchronous event handling
-+ */
-+struct zh_shared_events {
-+	spinlock_t lock;
-+	wait_queue_head_t wq;
-+	unsigned short registered;
-+	unsigned int pending;
-+	struct list_head queue;
-+	struct list_head clients;
-+};
-+
-+static struct zh_shared_events zh_shared;
-+
-+/**
-+ * struct zh_polled_events
-+ * @lock: spinlock protecting this structure
-+ * @pending: number of events pending
-+ * @queue: list of events
-+ *
-+ * Polled events must be in an extra queue according to FC-HBA.
-+ */
-+struct zh_polled_events
-+{
-+	spinlock_t lock;
-+	zh_count_t pending;
-+	struct list_head queue;
-+};
-+
-+static struct zh_polled_events zh_polled;
-+
-+/**
-+ * add_event_to_polled - Add an event to the polled queue
-+ * @*c: pointer to an event
-+ *
-+ * We use the zh_config structure here. It distinguishes itself from the
-+ * zh_event only through the missing "left" counter. Since a polled event is
-+ * only delivered once, we do not need a counter here.
-+ */
-+static void add_event_to_polled(struct zh_config *c)
-+{
-+	struct zh_config *last;
-+	unsigned long flags;
-+
-+	spin_lock_irqsave(&zh_polled.lock, flags);
-+
-+	if (zh_polled.pending == maxpolled) {
-+		last = list_entry(zh_polled.queue.next, struct zh_config, list);
-+		list_del(zh_polled.queue.next);
-+		kfree(last);
-+	} else {
-+		++zh_polled.pending;
-+	}
-+
-+	list_add_tail(&c->list, &zh_polled.queue);
-+
-+	spin_unlock_irqrestore(&zh_polled.lock, flags);
-+}
-+
-+/**
-+ * add_event_to_shared - Add an event to the list of pending events
-+ * @e: The event that should be added
-+ * Context: irq/user
-+ *
-+ * Events will be thrown away if nobody is registered for delivery. If there
-+ * are already &maxevents events in the list, the oldest is discarded.
-+ */
-+static void add_event_to_shared(struct zh_event_item *e)
-+{
-+	struct zh_event_item *item;
-+	struct list_head *go, *first;
-+	struct zh_client *c;
-+	unsigned long flags;
-+
-+	/* atm we can be called from user, and from irq context
-+	 * so we need the irqsafe thingie here. */
-+	spin_lock_irqsave(&zh_shared.lock, flags);
-+
-+	/* do not keep events if we have nobody to deliver it to */
-+	if (zh_shared.registered == 0) {
-+		spin_unlock_irqrestore(&zh_shared.lock, flags);
-+		kfree(e);
-+		return;
-+	}
-+
-+	/* is the queue full? */
-+	if (zh_shared.pending == maxshared) {
-+		first = zh_shared.queue.next;
-+
-+		/* check if we have to flag some clients */
-+		list_for_each(go, &zh_shared.clients) {
-+			c = list_entry(go, struct zh_client, clients);
-+
-+			if (NULL == c->last) {
-+				ZH_LOG(KERN_INFO, "lost event for client "
-+				       "with pid %u\n", current->pid);
-+				c->lost = 1;
-+			} else {
-+				if (first == c->last) {
-+					c->last = NULL;
-+				}
-+			}
-+		}
-+
-+		item = list_entry(first, struct zh_event_item, list);
-+		list_del(first);
-+		kfree(item);
-+		
-+		ZH_LOG(KERN_INFO, "event queue full, deleted item %p\n",
-+		       item);
-+
-+	} else {
-+		++zh_shared.pending;
-+	}
-+
-+	/* initialize event, add it to the list */
-+	atomic_set(&e->count, zh_shared.registered);
-+	
-+	list_add_tail(&e->list, &zh_shared.queue);
-+
-+	spin_unlock_irqrestore(&zh_shared.lock, flags);
-+
-+	/* wake up all processes waiting for events */
-+	wake_up_interruptible_all(&zh_shared.wq);
-+}
-+
-+/**
-+ * add_event - Enqueue an event
-+ * @filp: struct file to add the event to, or NULL
-+ * @e: the event to enqueue
-+ * @c: the event to enqueue
-+ *
-+ * Either add the event privately to the fd directly, or to the
-+ * global queue of events.
-+ * FIXME (stage 2) check whether some polled events have to be added to shared
-+ * queueu, too.
-+ */
-+static inline void add_event(struct file *filp, struct zh_event_item *e, struct
-+		zh_config *c)
-+{
-+	if (NULL == filp) {
-+		add_event_to_shared(e);
-+	} else {
-+		struct zh_client *head = (void*) filp->private_data;
-+		
-+		list_add_tail(&c->list, &head->config);
-+	}
-+}
-+
-+/**
-+ * zh_open - Implements the device open call
-+ * @inode: struct inode
-+ * @filp: struct file
-+ * Return: 0 on success, else -ENOMEM 
-+ * Context: user
-+ * 
-+ * Called when the zfcp_hbaapi device file is opened. Iniializes
-+ * filp->private_data.
-+ */
-+static int zh_open(struct inode *inode, struct file *filp)
-+{
-+	struct zh_client *data;
-+
-+	MOD_INC_USE_COUNT;
-+
-+	data = kmalloc(sizeof(*data), GFP_KERNEL);
-+
-+	if (data == NULL) {
-+		MOD_DEC_USE_COUNT;
-+		return -ENOMEM;
-+	}
-+
-+	sema_init(&data->sem, 1);
-+	data->last = NULL;
-+	data->registered = 0;
-+	data->lost = 0;
-+	INIT_LIST_HEAD(&data->config);
-+	INIT_LIST_HEAD(&data->clients);
-+	filp->private_data = data;
-+
-+	return 0;
-+}
-+
-+/**
-+ * zh_release - Called on file release
-+ * @inode: struct inode
-+ * @filp: struct file
-+ * Return: always 0
-+ * Context: user
-+ *
-+ * Called when all copies of a filedescriptor are closed, thus we
-+ * can mess around with private_data, and free it.
-+ */
-+static int zh_release(struct inode *inode, struct file *filp)
-+{
-+	zh_ioc_event_stop(filp);
-+	zh_ioc_clear_config(filp);
-+
-+	kfree(filp->private_data);
-+	filp->private_data = NULL;
-+
-+	MOD_DEC_USE_COUNT;
-+	return 0;
-+}
-+
-+/**
-+ * zh_read - The device's read method
-+ * Context: user
-+ *
-+ * Used to read the whole configuration data, eg adapters, ports or units
-+ * from zfcp.
-+ *
-+ * Access is serialized with a semaphore thus cloned file descriptors may block,
-+ * eg fd = open(); fork(); parent:ioctl(ZH_IOC_EVENT); child:read(); The child
-+ * will be block until the parent returns from the ioctl(), IF they use *
-+ * _exactly the same_ file descriptor. Diffrent file descriptors do _not_ block
-+ * each other.
-+ */
-+static ssize_t zh_read(struct file *filp, char* buf, size_t s, loff_t *off)
-+{
-+	size_t count, i, ret;
-+	struct zh_config *c;
-+	struct list_head *go, *safe;
-+	struct zh_client *client = (struct zh_client*) filp->private_data;
-+
-+	if (down_interruptible(&client->sem))
-+		return -ERESTARTSYS;
-+
-+	if (s < sizeof(c->event)) {
-+		ret = -ENOSPC;
-+		goto up;
-+	}
-+
-+	if (list_empty(&client->config)) {
-+		ret = 0;
-+		goto up;
-+	}
-+
-+	count = s / sizeof(c->event);
-+	i = 0;
-+
-+	list_for_each_safe(go, safe, &client->config)
-+	{
-+		c = list_entry(go, struct zh_config, list);
-+		
-+		if (copy_to_user(buf, &c->event, sizeof(c->event))) {
-+			ret = -EFAULT;
-+			goto up;
-+		}
-+		
-+		list_del(go);
-+		kfree(c);
-+
-+		buf += sizeof(c->event);
-+
-+		if (++i >= count) {
-+			break;
-+		}
-+	}
-+
-+	ret = i * sizeof(c->event);
-+
-+up:
-+	up(&client->sem);
-+
-+	return ret;
-+};
-+
-+/**
-+ * zh_ioctl - The device's ioctl() method
-+ * @inode: struct inode
-+ * @filp: struct file
-+ * @cmd: command to execute
-+ * @arg: parameter(s) for the command
-+ * Return:  0 on success, else -E* code
-+ * Context: User
-+ * 
-+ * This is the main interaction method between the vendor lib and the
-+ * kernel. Here we only determine what we should do, and then call the
-+ * corresponding worker method.
-+ *
-+ * Also read zh_read()'s comment.
-+ */
-+static int zh_ioctl(struct inode *inode, struct file *filp,
-+		    unsigned int cmd, unsigned long arg)
-+{
-+
-+	int ret;
-+	struct zh_client *client = (void*) filp->private_data;
-+
-+	if (down_interruptible(&client->sem))
-+		return -ERESTARTSYS;
-+
-+	switch (cmd)
-+	{
-+	case ZH_IOC_GET_ADAPTERATTRIBUTES:
-+		ret = zh_ioc_get_adapterattributes(
-+			(struct zh_get_adapterattributes *) arg);
-+		break;
-+	case ZH_IOC_GET_PORTATTRIBUTES:
-+		ret = zh_ioc_get_portattributes(
-+			(struct zh_get_portattributes *) arg);
-+		break;
-+	case ZH_IOC_GET_DPORTATTRIBUTES:
-+		ret = zh_ioc_get_dportattributes(
-+			(struct zh_get_portattributes *) arg);
-+		break;
-+	case ZH_IOC_GET_PORTSTATISTICS:
-+		ret = zh_ioc_get_portstatistics(
-+			(struct zh_get_portstatistics *) arg);
-+		break;
-+	case ZH_IOC_GET_EVENT_BUFFER:
-+		ret = zh_ioc_get_event_buffer(
-+			(struct zh_get_event_buffer *) arg);
-+		break;
-+	case ZH_IOC_EVENT_START:
-+		ret = zh_ioc_event_start(filp);
-+		break;
-+	case ZH_IOC_EVENT_STOP:
-+		ret = zh_ioc_event_stop(filp);
-+		break;
-+	case ZH_IOC_EVENT:
-+		ret = zh_ioc_event((struct zh_event*) arg, filp);
-+		break;
-+	case ZH_IOC_EVENT_INSERT: /* DEBUG ONLY */
-+		ret = zh_ioc_event_insert();
-+		break;
-+	case ZH_IOC_SCSI_INQUIRY:
-+		ret = zh_ioc_scsi_inquiry((struct zh_scsi_inquiry*) arg);
-+		break;
-+	case ZH_IOC_SCSI_READ_CAPACITY:
-+		ret = zh_ioc_scsi_read_capacity(
-+			(struct zh_scsi_read_capacity *) arg);
-+		break;
-+	case ZH_IOC_SCSI_REPORT_LUNS:
-+		ret = zh_ioc_scsi_report_luns(
-+			(struct zh_scsi_report_luns *) arg);
-+		break;
-+	case ZH_IOC_GET_CONFIG:
-+		ret = zh_ioc_get_config((struct zh_get_config *) arg, filp);
-+		break;
-+	case ZH_IOC_CLEAR_CONFIG:
-+		ret = zh_ioc_clear_config(filp);
-+		break;
-+	case ZH_IOC_GET_RNID:
-+		ret = zh_ioc_get_rnid((struct zh_get_rnid *) arg);
-+		break;
-+	case ZH_IOC_SEND_RNID:
-+		ret = zh_ioc_send_rnid((struct zh_send_rnid *) arg);
-+		break;
-+	case ZH_IOC_SEND_CT:
-+		ret = zh_ioc_send_ct((struct zh_send_ct *) arg);
-+		break;
-+	default:
-+		ret = -ENOTTY;
-+		break;
-+	}
-+
-+	up(&client->sem);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_init - Module initialisation
-+ * Return: 0 on success, else < 0
-+ * Context: user
-+ * 
-+ * Sets owner, registers with zfcp, registers misc device, initializes
-+ * global events structure.
-+ *
-+ * FIXME Register a misc minor number for zfcp_hbaapi at www.lanana.org.
-+ */
-+static int __init zh_init(void)
-+{
-+	int ret;
-+	
-+	if (0 >= maxshared) {
-+		ZH_LOG(KERN_ERR, "illegal value for maxshared: %d, "
-+		       "minimum is 1\n", maxshared);
-+		return -EINVAL;
-+	}
-+
-+#ifdef CONFIG_ARCH_S390X
-+	ret = zh_register_ioctl_conversion();
-+	if (ret < 0) {
-+		return ret;
-+	}
-+#endif
-+
-+	/* register callbacks with zfcp */
-+	ret = zfcp_zh_callbacks_register(&zh_callbacks);
-+	if (ret < 0) {
-+		return ret;
-+	}
-+
-+	SET_MODULE_OWNER(&zh_fops);
-+
-+	/* register a misc char device */
-+	zh_misc.minor = minor;
-+	ret = misc_register(&zh_misc);
-+	if (ret < 0) {
-+		goto failed_misc;
-+	}
-+
-+	/* initialize shared events */
-+	spin_lock_init(&zh_shared.lock);
-+	init_waitqueue_head(&zh_shared.wq);
-+	zh_shared.registered = 0;
-+	zh_shared.pending = 0;
-+	INIT_LIST_HEAD(&zh_shared.queue);
-+	INIT_LIST_HEAD(&zh_shared.clients);
-+
-+	/* initalize polled events */
-+	spin_lock_init(&zh_polled.lock);
-+	zh_polled.pending = 0;
-+	INIT_LIST_HEAD(&zh_polled.queue);
-+
-+	ZH_LOG(KERN_NOTICE, "loaded hbaapi.o, version %s, maxshared=%d, "
-+			"maxpolled=%d\n", HBAAPI_REVISION, maxshared,
-+			maxpolled);
-+
-+	if (MISC_DYNAMIC_MINOR == minor) {
-+		ZH_LOG(KERN_INFO, "registered dynamic minor with misc "
-+		       "device\n");
-+	} else {
-+		ZH_LOG(KERN_INFO, "registered minor %d with misc device\n",
-+		       minor);
-+	}
-+
-+	return 0;
-+
-+failed_misc:
-+	zfcp_zh_callbacks_unregister(&zh_callbacks);
-+	
-+	return ret;
-+}
-+
-+/**
-+ * zh_exit - Module finalisation
-+ * Context: user
-+ *
-+ * Undoes all work done in zh_init()
-+ */
-+static void __exit zh_exit(void)
-+{
-+	struct list_head *go, *save;
-+	struct zh_event_item *e;
-+	struct zh_config *c;
-+
-+	zfcp_zh_callbacks_unregister(&zh_callbacks);
-+	misc_deregister(&zh_misc);
-+
-+#ifdef CONFIG_ARCH_S390X
-+	/* FIXME return value? log message? */
-+	zh_unregister_ioctl_conversion();
-+#endif
-+
-+	if (!list_empty(&zh_shared.queue)) {
-+		ZH_LOG(KERN_ERR, "event queue not empty while unloading "
-+		       "module\n");
-+
-+		/* the module can only be unloaded when all file descriptors
-+		 * have been closed. If there are events left in the queue, we
-+		 * do have an error in our code. Since nobody else can access
-+		 * these events, we can free them.
-+		 */
-+		list_for_each_safe(go, save, &zh_shared.queue)
-+		{
-+			e = list_entry(go, struct zh_event_item, list);
-+			list_del(go);
-+			kfree(e);
-+			--zh_shared.pending;
-+		}
-+	}
-+
-+	if (zh_shared.pending) {
-+		ZH_LOG(KERN_ERR, "number of pending events not zero but %u\n",
-+		       zh_shared.pending);
-+	}
-+
-+	/* throw away polled events */
-+	list_for_each_safe(go, save, &zh_polled.queue) {
-+		c = list_entry(go, struct zh_config, list);
-+		list_del(go);
-+		kfree(c);
-+	}
-+}
-+
-+/**
-+ * zh_ioc_get_adapterattributes - Retrieve attributes of an adapter
-+ * @u_ptr: userspace pointer to copy data from and to
-+ * Return: 0 on success, else -E* code
-+ * Context: user
-+ */
-+static int zh_ioc_get_adapterattributes(struct zh_get_adapterattributes *u_ptr)
-+{
-+	struct zh_get_adapterattributes ioc_data;
-+	int ret;
-+
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+
-+	ret = zfcp_zh_get_adapter_attributes(DEVID_TO_DEVNO(ioc_data.devid),
-+					     &ioc_data.attributes);
-+	if (0 == ret) {
-+		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
-+			ret = -EFAULT;
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_get_portattributes - Retrieve attributes of an adapter port
-+ * @*u_ptr: userspace pointer to copy the data to
-+ * Return: 0 on success, else -E* code
-+ * Context: user
-+ */
-+static int zh_ioc_get_portattributes(struct zh_get_portattributes *u_ptr)
-+{
-+	struct zh_get_portattributes ioc_data;
-+	int ret;
-+	
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+
-+	ret = zfcp_zh_get_port_attributes(DEVID_TO_DEVNO(ioc_data.devid),
-+					  &ioc_data.attributes);
-+
-+	if ((0 == ret) || (EOPNOTSUPP == ret)) {
-+		zh_map_port_speed(&ioc_data.attributes.supported_speed,
-+				  ZH_PORT_SUPPORTED_SPEED);
-+		zh_map_port_speed(&ioc_data.attributes.speed,
-+				  ZH_PORT_OPERATING_SPEED);
-+		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
-+			ret = -EFAULT;
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_get_portstatistics - Retrieve statistics of an adapter port
-+ * @*u_ptr: userspace pointer to copy data from and to
-+ * Return: 0 on success, else -E* code
-+ * Context: user
-+ */
-+static int zh_ioc_get_portstatistics(struct zh_get_portstatistics *u_ptr)
-+{
-+	struct zh_get_portstatistics ioc_data;
-+	int ret;
-+	
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+
-+	ret = zfcp_zh_get_port_statistics(DEVID_TO_DEVNO(ioc_data.devid),
-+					  &ioc_data.statistics);
-+
-+	if ((0 == ret) || (EOPNOTSUPP == ret)) {
-+		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
-+			ret = -EFAULT;
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_get_dportattributes - Retrieve attributes of an target port
-+ * @*u_ptr: userspace pointer to copy data from and to
-+ * Return: 0 on success, else -E* code
-+ * Context: user
-+ */
-+static int zh_ioc_get_dportattributes(struct zh_get_portattributes *u_ptr)
-+{
-+	struct zh_get_portattributes ioc_data;
-+	int ret;
-+	
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+
-+	ret = zfcp_zh_get_dport_attributes(DEVID_TO_DEVNO(ioc_data.devid),
-+					   ioc_data.wwpn,
-+					   &ioc_data.attributes);
-+
-+	if (0 == ret) {
-+		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
-+			ret = -EFAULT;
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_get_event_buffer - Retrieve events from the polled queue
-+ * @*u_ptr: userspace pointer to copy data from and to
-+ * Return: number of return events on success, else -E* code
-+ * 
-+ * Copy events belonging to an adapter to userspace and delete them.
-+ */
-+static int zh_ioc_get_event_buffer(struct zh_get_event_buffer *u_ptr)
-+{
-+	int ret;
-+	struct zh_get_event_buffer ioc_data;
-+	struct zh_config *c;
-+	struct list_head *go, *safe;
-+	unsigned short i = 0;
-+
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+
-+	if (ioc_data.count > ZH_GET_EVENT_BUFFER_COUNT) {
-+		ioc_data.count = ZH_GET_EVENT_BUFFER_COUNT;
-+	}
-+
-+	spin_lock_irq(&zh_polled.lock);
-+
-+	list_for_each_safe(go, safe, &zh_polled.queue)
-+	{
-+		c = list_entry(go, struct zh_config, list);
-+
-+		if (i >= ioc_data.count) {
-+			break;
-+		}
-+
-+		if (ioc_data.devid == c->event.data.polled.devid){
-+			ioc_data.polled[i] = c->event.data.polled;
-+
-+			list_del(go);
-+			kfree(c);
-+			--zh_polled.pending;
-+
-+			++i;
-+		}
-+	}
-+
-+	spin_unlock_irq(&zh_polled.lock);
-+
-+	if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
-+		ret = -EFAULT;
-+	} else {
-+		ret = i;
-+	}
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_event_start - Called to enable event delivery
-+ * @*filp: file for which event delivery should be enabled
-+ * Return: 0 on success, else -E* code
-+ * Context: user
-+ *
-+ * Mark the fd as target for events, increase each events
-+ * "to-be-delivered-to" counter by 1.
-+ */
-+static int zh_ioc_event_start(struct file *filp)
-+{
-+	struct zh_client *client = (struct zh_client*) filp->private_data;
-+
-+	/* registration is only allowed once */
-+	if (client->registered) {
-+		return -EINVAL;
-+	}
-+
-+	spin_lock_irq(&zh_shared.lock);
-+
-+	/* remember that there is one more fd events have to be delivered to */
-+	++zh_shared.registered;
-+
-+	client->registered = 1;
-+
-+	/* set number of the next event, 0 means no events available ATM */
-+	if (list_empty(&zh_shared.queue)) {
-+		client->last = NULL;
-+	} else {
-+		client->last = zh_shared.queue.prev;
-+	}
-+
-+	list_add_tail(&client->clients, &zh_shared.clients);
-+
-+	spin_unlock_irq(&zh_shared.lock);
-+
-+	return 0;
-+}
-+
-+/**
-+ * count_down_event - Refcount event
-+ * @list: containded list structure of the event
-+ * Context: user
-+ * Return: 1 of the event was deleted, 0 else
-+ * Locks: zh_shared.lock must be held
-+ * 
-+ * Removes an event from the list, if it was delivered to all fd's. Otherwise
-+ * just refcount it.
-+ */
-+static inline int count_down_event(struct list_head *list)
-+{
-+	struct zh_event_item *e = list_entry(list, struct zh_event_item, list);
-+
-+	if (atomic_dec_and_test(&e->count)) {
-+		--zh_shared.pending;
-+
-+		list_del(list);
-+		kfree(e);
-+			
-+		return 1;
-+	} else {
-+		return 0;
-+	}
-+}
-+
-+/**
-+ * zh_ioc_event_stop - Stop event delivery
-+ * @*filp: file for which event delivery should be disabled
-+ * Return: 0 on success, else -E* code
-+ * Context: user
-+ *
-+ * Decrease total number of fd's which get events, count down all events
-+ * _after_ the event delivered to this fd.
-+ */
-+static int zh_ioc_event_stop(struct file *filp)
-+{
-+	struct list_head *go, *safe;
-+	struct zh_client *client = (struct zh_client*) filp->private_data;
-+
-+	/* deregistration is only allowed once */
-+	if (!client->registered) {
-+		return -EINVAL;
-+	}
-+
-+	spin_lock_irq(&zh_shared.lock);
-+
-+	--zh_shared.registered;
-+
-+	list_del(&client->clients);
-+
-+	/* count down all not yet delivered events for this fd */
-+	list_for_each_safe(go, safe, &zh_shared.queue)
-+	{
-+		if (NULL == client->last) {
-+			count_down_event(go);
-+		} else {
-+			if (go == client->last) {
-+				client->last = NULL;
-+			}
-+		}
-+	}
-+	
-+	spin_unlock_irq(&zh_shared.lock);
-+
-+	client->registered = 0;
-+	client->lost = 0;
-+	client->last = NULL;
-+
-+	return 0;
-+}
-+
-+/**
-+ * has_next_event - Condition for wait_event_interruptible
-+ * @head: private data of the fd
-+ * Return: 1 if there is an event waiting, 0 else
-+ * Locks: lock/unlock of zh_shared.lock
-+ *
-+ * This is used as the condition for the wait_event_interruptible()
-+ * call. It avoids a wakeup, statechange race.
-+ */
-+static inline int has_next_event(struct zh_client *client)
-+{
-+	int ret = 0;
-+	unsigned long flags;
-+
-+	spin_lock_irqsave(&zh_shared.lock, flags);
-+
-+	if (NULL == client->last) {
-+		ret = !list_empty(&zh_shared.queue);
-+	} else {
-+		ret = (zh_shared.queue.prev != client->last);
-+	}
-+	
-+	spin_unlock_irqrestore(&zh_shared.lock, flags);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_event - Wait for an event
-+ * @*u_ptr: userspace pointer to copy data to
-+ * @filp: descriptor receiving events
-+ * Return: 0 on success, -E* code else
-+ * Context: user
-+ *
-+ * The heart of the event delivery. Waits for events and delivers the next one
-+ */
-+static int zh_ioc_event(struct zh_event *u_ptr, struct file *filp)
-+{
-+	struct zh_event_item *event;
-+	struct list_head *entry;
-+	int ret;
-+	struct zh_client *client = (struct zh_client*) filp->private_data;
-+
-+	if (!client->registered) {
-+		return -EINVAL;
-+	}
-+
-+	/* wait for events */
-+	ret = wait_event_interruptible(zh_shared.wq, has_next_event(client));
-+	if (-ERESTARTSYS == ret) {
-+		/* ERESTARTSYS should never be seen by user programs */
-+		return -ERESTART;
-+	}
-+
-+	spin_lock_irq(&zh_shared.lock);
-+
-+	/* need to check it lock protected */
-+	if (client->lost) {
-+		client->last = NULL;
-+		client->lost = 0;
-+		ret = -ENXIO;
-+		goto release;
-+	}
-+
-+	if (NULL == client->last) {
-+		entry = zh_shared.queue.next;
-+	} else {
-+		entry = client->last->next;
-+	}
-+	
-+	event = list_entry(entry, struct zh_event_item, list);
-+
-+	if (copy_to_user(u_ptr, &event->event, sizeof(*u_ptr))) {
-+		ret = -EFAULT;
-+		goto release; /* keep the event in the queue */
-+	}
-+
-+	if (count_down_event(entry)) {
-+		client->last = NULL;
-+	} else {
-+		client->last = entry;
-+	}
-+
-+	ret = 0;
-+
-+release:
-+	spin_unlock_irq(&zh_shared.lock);
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_event_insert - Insert an event into the list
-+ * @*u_ptr: userspace pointer to copy data from
-+ * Return: 0 on success, else -E* code
-+ * Debug: DEBUG ONLY
-+ *
-+ * Insert a dummy event into the list of events, used to determine if
-+ * the event handling code is working. Insert a dummy event into the 
-+ * polled event buffer, used to test the polled event buffer code.
-+ */
-+static int zh_ioc_event_insert(void)
-+{
-+	struct zh_event_item *e;
-+	
-+	e = (struct zh_event_item*) kmalloc(sizeof(*e), GFP_KERNEL);
-+	if (e == NULL) {
-+		return -ENOMEM;
-+	}
-+
-+	memset(e, 0, sizeof(*e));
-+	e->event.event = ZH_EVENT_DUMMY;
-+
-+	add_event_to_shared(e);
-+
-+	return 0;
-+}
-+
-+/**
-+ * zh_sg_free - Free an allocated scatterlist
-+ * @sg: the scatterlist
-+ */
-+static void zh_sg_free(struct sg_list *sg)
-+{
-+	int i;
-+	
-+	for (i = sg->count - 1; i >= 0; --i) {
-+		free_page((unsigned long) sg->sg[i].address);
-+	}
-+
-+	kfree(sg->sg);
-+	memset(sg, 0, sizeof(*sg));
-+}
-+
-+/**
-+ * zh_sg_alloc - Allocate a scatterlist
-+ * @*sg: scatterlist
-+ * Return: 0 on success, else -E* code
-+ * Context: User
-+ *
-+ * Either all pages can be allocated, or none. On error *sg is invalid and
-+ * has to be reinitialized.
-+ */
-+static int zh_sg_alloc(struct sg_list *sg, size_t size)
-+{
-+	unsigned int i;
-+	size_t lastsize, array;
-+
-+	if (0 == size) {
-+		return -EINVAL;
-+	}
-+
-+	sg->count = size / PAGE_SIZE;
-+	lastsize = size - (sg->count * PAGE_SIZE);
-+	if (0 != lastsize) {
-+		++sg->count;
-+	} else {
-+		lastsize = PAGE_SIZE;
-+	}
-+
-+	/* allocate the array */
-+	array = sizeof(struct scatterlist) * sg->count;
-+	sg->sg = kmalloc(array, GFP_KERNEL);
-+	if (NULL == sg->sg) {
-+		return -ENOMEM;
-+	}
-+
-+	memset(sg->sg, 0, array);
-+
-+	/* allocate needed pages */
-+	for (i = 0; i < sg->count; ++i) {
-+		sg->sg[i].address = (void*) get_zeroed_page(GFP_KERNEL);
-+		if (NULL == sg->sg[i].address) {
-+			sg->count = i;
-+			zh_sg_free(sg);
-+			return -ENOMEM;
-+		}
-+
-+		sg->sg[i].length = PAGE_SIZE;
-+	}
-+
-+	sg->sg[i-1].length = lastsize;
-+
-+	return 0;
-+}
-+
-+/**
-+ * zh_sg_get_size - calculate size in bytes of a sg
-+ * @*sg: pointer to a sg structure
-+ */
-+static inline size_t zh_sg_get_size(struct sg_list *sg)
-+{
-+	return (((sg->count - 1) * PAGE_SIZE) + sg->sg[sg->count - 1].length);
-+}
-+
-+/**
-+ * zh_sg_copy_from_user - copy data from user
-+ * @*u_ptr: address in userspace to copy from
-+ * @size: bytes to copy
-+ * @*sg: describing space to copy to
-+ * Return: 0 on success, -E* code else
-+ * 
-+ * This method refuses to copy data if size is too big (-ENOSPC)
-+ */
-+static int zh_sg_copy_from_user(char *u_ptr, size_t size, struct sg_list *sg)
-+{
-+	unsigned int i;
-+
-+	if (size > zh_sg_get_size(sg)) {
-+		return -ENOSPC;
-+	}
-+
-+	for (i = 0; i < sg->count - 1; ++i) {
-+		if (copy_from_user(sg->sg[i].address, u_ptr, PAGE_SIZE)) {
-+			return -EFAULT;
-+		}
-+
-+		u_ptr += PAGE_SIZE;
-+	}
-+
-+	if (copy_from_user(sg->sg[i].address, u_ptr,
-+			   sg->sg[sg->count - 1].length)) {
-+		return -EFAULT;
-+	}
-+
-+	return 0;
-+}
-+
-+/**
-+ * zh_sg_copy_from_user_alloc - Allocate a scatterlist and copy data from user
-+ */
-+static inline int zh_sg_copy_from_user_alloc(char *u_ptr, size_t size,
-+					     struct sg_list *sg)
-+{
-+	int ret;
-+
-+	ret = zh_sg_alloc(sg, size);
-+	if (ret < 0) {
-+		return ret;
-+	}
-+
-+	return zh_sg_copy_from_user(u_ptr, size, sg);
-+}
-+
-+/**
-+ * zh_sg_copy_to_user - Copy sg to userspace
-+ * @*u_ptr: address in userspace to copy to
-+ * @*size: bytes to copy, contains number of copied bytes on return
-+ * @*sg: describing data to copy
-+ * Return: copied bytes on success, -E* code else
-+ */
-+static int zh_sg_copy_to_user(char *u_ptr, size_t *size, struct sg_list *sg)
-+{
-+	unsigned int i;
-+	unsigned int copy;
-+	size_t left;
-+	char *begin;
-+
-+	begin = u_ptr;
-+	left = *size;
-+	*size = 0;
-+
-+	for (i = 0; (i < sg->count) && (left > 0); ++i)	{
-+		/* FIXME: What to check here? I think one assumption is
-+		   (sg[i].address == 0) => (sg[i].offset == 0) which becomes
-+		   (sg[i].address != 0) ||
-+		   ((sg[i].address == 0) && sg[i].offset == 0) */
-+		if ((NULL == sg->sg[i].address) && (0 != sg->sg[i].offset)) {
-+			return -EINVAL;
-+		}
-+
-+		copy = min(PAGE_SIZE, left);
-+		copy = min(copy, sg->sg[i].length);
-+
-+		if (copy_to_user(u_ptr, sg->sg[i].address, copy)) {
-+			return -EFAULT;
-+		}
-+
-+		u_ptr += copy;
-+		left -= copy;
-+		*size += copy;
-+	}
-+
-+	if ((u_ptr - begin) != zh_sg_get_size(sg)) {
-+		return -ENOSPC;
-+	} else {
-+		return 0;
-+	}
-+}
-+
-+/**
-+ * zh_sg_copy_to_user_truncate - Copy as much from sg to user as fits
-+ * @*u_ptr: address in userspace to copy to
-+ * @*size: bytes to copy, contains number of copied bytes on return
-+ * @*sg: describing data to copy
-+ * Return: 0 on success, -E* code else
-+ *
-+ * This wrapper to zh_sg_copy_to_user() simply ignores -ENOSPC errors, which is
-+ * what we want when we use this function.
-+ */
-+static inline int zh_sg_copy_to_user_truncate(char *u_ptr, size_t *size,
-+					      struct sg_list *sg)
-+{
-+	long long ret;
-+	
-+	ret = zh_sg_copy_to_user(u_ptr, size, sg);
-+
-+	if (-ENOSPC == ret) {
-+		ret = 0;
-+	}
-+	
-+	return ret;
-+}
-+
-+/**
-+ * zh_alloc_scsi_cmnd - Allocate and fill a Scsi_Cmnd
-+ * @cmd: The scsi command as specified in the SCSI standards
-+ * @cmd_size: size of cmd in bytes
-+ * @*sg: pointer to scatterlist to retrieve response
-+ * Return: The created Scsi_Cmnd on succes, else NULL
-+ */
-+static Scsi_Cmnd* zh_alloc_scsi_cmnd(void *cmd, size_t cmd_size,
-+				     struct sg_list *sg)
-+{
-+	Scsi_Cmnd *sc;
-+
-+	sc = kmalloc(sizeof(*sc), GFP_KERNEL);
-+	if (NULL == sc) {
-+		return NULL;
-+	}
-+	
-+	memset(sc, 0, sizeof(*sc));
-+	/* zfcp uses host_scribble, bh_next and scsi_done, don't touch em */
-+
-+	sc->sc_data_direction = SCSI_DATA_READ;
-+
-+	sc->use_sg = sg->count;
-+	sc->sglist_len = sg->count;
-+	sc->buffer = sg->sg;
-+	sc->bufflen = zh_sg_get_size(sg);
-+	sc->request_buffer = sc->buffer;
-+	sc->request_bufflen = sc->bufflen;
-+
-+	sc->cmd_len = cmd_size;
-+
-+	memcpy(sc->cmnd, cmd, cmd_size);
-+	memcpy(sc->data_cmnd, cmd, cmd_size);
-+
-+	return sc;
-+}
-+
-+/**
-+ * zh_do_scsi_command - worker for sending scsi commands
-+ * @devid: of the adpapter to send via
-+ * @wwpn: of the port to send the command to
-+ * @lun: to send the command to
-+ * @cmd: SCSI command to send
-+ * @cmd_size: size of the command in bytes
-+ * @rsp: userspace pointer to copy the response to
-+ * @rsp_size: size of the userspace buffer
-+ * @sense: userspace pointer to copy sense data to
-+ */
-+static int zh_do_scsi_command(devid_t devid, wwn_t wwpn, fcp_lun_t lun,
-+			      void *cmd, size_t cmd_size, struct sg_list *rsp,
-+			      void *sense)
-+{
-+	Scsi_Cmnd *sc;
-+	int ret;
-+
-+	sc = zh_alloc_scsi_cmnd(cmd, cmd_size, rsp);
-+	if (NULL == sc) {
-+		return -ENOMEM;
-+	}
-+
-+	ret = zfcp_zh_send_scsi(DEVID_TO_DEVNO(devid), wwpn, lun, sc);
-+
-+	/* the scsi stack sets this, if there was a scsi error */
-+	if (ret > 0) {
-+		memcpy(sense, sc->sense_buffer, SCSI_SENSE_BUFFERSIZE);
-+	}
-+
-+	kfree(sc);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_scsi_report_luns - Send SCSI REPORT LUNS
-+ * @*u_ptr: userspace pointer to copy data from and to
-+ * Return: 0 on success, < 0 -E* code, > 0 SCSI ERROR
-+ * Context: user
-+ */
-+static int zh_ioc_scsi_report_luns(struct zh_scsi_report_luns *u_ptr)
-+{
-+	int ret;
-+	struct zh_scsi_report_luns ioc_data;
-+
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data)))
-+		return -EFAULT;
-+
-+	ret = zh_report_luns_helper(&ioc_data);
-+
-+	if (ret >= 0) {
-+		if (copy_to_user(u_ptr, &ioc_data, sizeof(ioc_data))) {
-+			return -EFAULT;
-+		}
-+		if (ret > 0) {
-+			ret = 0;
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_report_luns_helper - Send SCSI REPORT LUNS
-+ * @*u_ptr: userspace pointer to copy data from and to
-+ * Return: 0 on success, < 0 -E* code, > 0 SCSI ERROR
-+ * Context: user
-+ */
-+int zh_report_luns_helper(struct zh_scsi_report_luns *ioc_data)
-+{
-+	int ret;
-+	size_t copy;
-+	struct sg_list sg;
-+	struct scsi_report_luns_cmd cmd = { 0 };
-+
-+	if (ioc_data->rsp_buffer_size < SCSI_REPORT_LUNS_SIZE_MIN) {
-+		return -EINVAL;
-+	}
-+
-+	ret = zfcp_zh_assert_fclun_zero(DEVID_TO_DEVNO(ioc_data->devid),
-+					ioc_data->wwpn);
-+	if (0 != ret) {
-+		return ret;
-+	}
-+
-+	ret = zh_sg_alloc(&sg, ioc_data->rsp_buffer_size);
-+	if (ret < 0) {
-+		return ret;
-+	}
-+	
-+	cmd.op = REPORT_LUNS;
-+	cmd.alloc_length = ioc_data->rsp_buffer_size;
-+
-+	ret = zh_do_scsi_command(ioc_data->devid, ioc_data->wwpn, 0, &cmd,
-+				 sizeof(cmd), &sg, ioc_data->sense);
-+
-+	if (ret >= 0) {
-+		copy = ioc_data->rsp_buffer_size;
-+		ret =  zh_sg_copy_to_user_truncate(ioc_data->rsp_buffer, &copy,
-+						   &sg);
-+	}
-+
-+	zh_sg_free(&sg);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_scsi_read_capacity - send SCSI READ CAPACITY
-+ * @*u_ptr: userspace pointer to copy data from and to
-+ * Return: 0 on success, < 0 -E* code, > 0 SCSI ERROR
-+ * Context: user
-+ */
-+static int zh_ioc_scsi_read_capacity(struct zh_scsi_read_capacity *u_ptr)
-+{
-+	int ret;
-+	struct sg_list sg;
-+	struct zh_scsi_read_capacity ioc_data;
-+	struct scsi_read_capacity_cmd cmd = { 0 };
-+
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+
-+	ret = zh_sg_alloc(&sg, ZH_SCSI_READ_CAPACITY_SIZE);
-+	if (ret < 0) {
-+		return ret;
-+	}
-+
-+	cmd.op = READ_CAPACITY;
-+
-+	ret = zh_do_scsi_command(ioc_data.devid, ioc_data.wwpn,
-+				 ioc_data.fclun, &cmd, sizeof(cmd), &sg,
-+				 ioc_data.sense);
-+
-+	if (ret >= 0) {
-+		memcpy(ioc_data.read_capacity, sg.sg[0].address,
-+		       sg.sg[0].length);
-+
-+		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
-+			ret = -EFAULT;
-+		}
-+	}
-+
-+	zh_sg_free(&sg);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_scsi_inquiry - send SCSI INQUIRY
-+ * @*u_ptr: userspace pointer to copy data from and to
-+ * Return: 0 on success, < 0 -E* code, > 0 SCSI ERROR
-+ * Context: user
-+ */
-+static int zh_ioc_scsi_inquiry(struct zh_scsi_inquiry *u_ptr)
-+{
-+	int ret;
-+	struct sg_list sg;
-+	struct zh_scsi_inquiry ioc_data;
-+	struct scsi_inquiry_cmd cmd = { 0 };
-+
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+
-+	ret = zh_sg_alloc(&sg, ZH_SCSI_INQUIRY_SIZE);
-+	if (ret < 0) {
-+		return ret;
-+	}
-+
-+	/* alloc_lentgh is of type u8, thus can only range to 255 */
-+	cmd.op  = INQUIRY;
-+	cmd.alloc_length = 255;
-+
-+	if (ioc_data.evpd)	{
-+		cmd.evpd = 1;
-+		cmd.page_code = ioc_data.page_code;
-+	}
-+
-+	ret = zh_do_scsi_command(ioc_data.devid, ioc_data.wwpn,
-+				 ioc_data.fclun, &cmd, sizeof(cmd), &sg,
-+				 ioc_data.sense);
-+
-+	if (ret >= 0) {
-+		memcpy(ioc_data.inquiry, sg.sg[0].address, sg.sg[0].length);
-+
-+		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
-+			ret = -EFAULT;
-+		}
-+	}
-+
-+	zh_sg_free(&sg);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_get_config - create private events for a specfific struct
-+ * @*u_ptr: userspace pointer to copy data from
-+ * @*filp: requesting fd
-+ * Return: no of created events, else -E* code
-+ * Context: user
-+ *
-+ * With this ioctl events are generated and attached to the fd only.  Used to
-+ * enumerate currently configured adapters/ports/units. Subsequent calls
-+ * discard prior created events.
-+ */
-+static int zh_ioc_get_config(struct zh_get_config *u_ptr, struct file *filp)
-+{
-+	struct zh_get_config ioc_data;
-+	struct zh_client *head = (void*) filp->private_data;
-+
-+	if (!list_empty(&head->config)) {
-+		zh_ioc_clear_config(filp);
-+	}
-+	
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+
-+	return zfcp_zh_get_config(filp, DEVID_TO_DEVNO(ioc_data.devid),
-+				  ioc_data.wwpn, ioc_data.flags);
-+}
-+
-+/**
-+ * zh_ioc_clear_config - remove config events from fd
-+ * @filp: fd requesting to clear its config
-+ * Return: always 0
-+ * Context: user
-+ */
-+static int zh_ioc_clear_config(struct file *filp)
-+{
-+	struct zh_config *c;
-+	struct list_head *go, *safe;
-+	struct zh_client *client = (struct zh_client*) filp->private_data;
-+
-+	list_for_each_safe(go, safe, &client->config)
-+	{
-+		c = list_entry(go, struct zh_config, list);
-+		list_del(go);
-+		kfree(c);
-+	}
-+
-+	INIT_LIST_HEAD(&client->config);
-+
-+	return 0;
-+}
-+
-+/**
-+ * zh_ioc_get_rnid() - get RNID from adapter
-+ * @u_ptr: userspace pointer to copy data from and to
-+ *
-+ * Note: We set data in zfcp_hbaapi since we can not access the data
-+ *	the adapter sends out.
-+ * Note: wwpn and wwnn not set because not used in ZFCP HBA API Library
-+ */
-+static int zh_ioc_get_rnid(struct zh_get_rnid *u_ptr)
-+{
-+	struct zh_get_rnid ioc_data;
-+
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+	
-+	memset(&ioc_data.payload, 0, sizeof(ioc_data.payload));
-+
-+	ioc_data.payload.code = ZFCP_LS_RNID;
-+	ioc_data.payload.node_id_format = 0xDF;
-+	ioc_data.payload.common_id_length =
-+		sizeof(struct zfcp_ls_rnid_common_id);
-+	ioc_data.payload.specific_id_length =
-+		sizeof(struct zfcp_ls_rnid_general_topology_id);
-+
-+	/* all other fields not set */
-+	ioc_data.payload.specific_id.associated_type = 0x000000a;
-+	ioc_data.payload.specific_id.physical_port_number = 1;
-+	ioc_data.payload.specific_id.nr_attached_nodes = 1;
-+
-+	if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
-+		return -EFAULT;
-+	}
-+
-+	return 0;
-+}
-+
-+/**
-+ * zh_ioc_send_rnid - send an rnid to a port
-+ * @*u_ptr: userspace pointer to copy data from and to
-+ *
-+ * Send a FC-FS ELS RNID to a discovered port.
-+ */
-+static int zh_ioc_send_rnid(struct zh_send_rnid *u_ptr)
-+{
-+	int ret;
-+	struct sg_list sg_send, sg_receive;
-+	struct zh_send_rnid ioc_data;
-+	struct zfcp_ls_rnid *rnid;
-+
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+
-+	ret = zh_sg_alloc(&sg_send, sizeof(struct zfcp_ls_rnid));
-+	if (ret < 0) {
-+		return ret;
-+	}
-+
-+	ret = zh_sg_alloc(&sg_receive, sizeof(struct zfcp_ls_rnid_acc));
-+	if (ret < 0) {
-+		goto free_send;
-+	}
-+
-+	rnid = (void*) sg_send.sg[0].address;
-+	rnid->code = ZFCP_LS_RNID;
-+	rnid->node_id_format = 0xDF;
-+
-+	ret = zfcp_zh_send_els(DEVID_TO_DEVNO(ioc_data.devid), ioc_data.wwpn,
-+			       sg_send.sg, sg_send.count, sg_receive.sg,
-+			       sg_receive.count);
-+	if (0 == ret) {
-+		ioc_data.size = sg_receive.sg[0].length;
-+		memcpy(&ioc_data.payload, sg_receive.sg[0].address,
-+		       ioc_data.size);
-+		
-+		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
-+			ret = -EFAULT;
-+		}
-+	}
-+
-+	zh_sg_free(&sg_receive);
-+free_send:
-+	zh_sg_free(&sg_send);
-+	
-+	return ret;
-+}
-+
-+/**
-+ * zh_ioc_send_ct - send a Generic Service command
-+ * @u_ptr: userspace pointer to parameter structure
-+ * Return: 0 on success, -E* code else
-+ *
-+ * Send a FC-GS CT IU
-+ */
-+static int zh_ioc_send_ct(struct zh_send_ct *u_ptr)
-+{
-+	int ret;
-+	struct zh_send_ct ioc_data;
-+
-+	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
-+		return -EFAULT;
-+	}
-+
-+	ret = zh_send_ct_helper(&ioc_data);
-+
-+	return ret;
-+}
-+
-+/**
-+ * zh_send_ct_helper - send a Generic Service command
-+ * @send_ct: userspace pointer to parameter structure
-+ * Return: 0 on success, -E* code else
-+ *
-+ * Send a FC-GS CT IU
-+ */
-+int zh_send_ct_helper(struct zh_send_ct *send_ct)
-+{
-+	int ret;
-+	struct sg_list req, resp;
-+
-+	ret = zh_sg_alloc(&req, send_ct->req_length);
-+	if (ret < 0) {
-+		return ret;
-+	}
-+
-+	if (zh_sg_copy_from_user(send_ct->req, send_ct->req_length, &req)) {
-+		ret = -EFAULT;
-+		goto free_req;
-+	}
-+
-+	ret = zh_sg_alloc(&resp, send_ct->resp_length);
-+	if (ret < 0) {
-+		goto free_req;
-+	}
-+
-+	ret = zfcp_zh_send_ct(DEVID_TO_DEVNO(send_ct->devid),
-+			      req.sg, req.count, resp.sg, resp.count);
-+
-+	if (0 == ret) {
-+		size_t size = send_ct->resp_length;
-+		ret = zh_sg_copy_to_user_truncate(send_ct->resp, &size, &resp);
-+	}
-+
-+	zh_sg_free(&resp);
-+free_req:
-+	zh_sg_free(&req);
-+	
-+	return ret;
-+}
-+
-+/**
-+ * prepare_event - prepare event structure
-+ * @filp: fd event to attach
-+ * @e: countable event
-+ * @c: non-countable event
-+ * Return: created event on success, else NULL
-+ * Context: irq/user
-+ *
-+ * To be able to generate private and shared events with the same callbacks,
-+ * this function creates a private or shared event depending on filp beeing NULL
-+ * or not.
-+ */
-+static inline struct zh_event*
-+prepare_event(struct file *filp, struct zh_event_item **e, struct zh_config **c)
-+{
-+	if (NULL != filp) {
-+		*c = (struct zh_config*) kmalloc(sizeof(**c), GFP_ATOMIC);
-+		if (NULL == *c) {
-+			return NULL;
-+		}
-+
-+		*e = NULL;
-+		return &(*c)->event;
-+	} else {
-+		*e = (struct zh_event_item*) kmalloc(sizeof(**e), GFP_ATOMIC);
-+		if (NULL == *e) {
-+			return NULL;
-+		}
-+
-+		*c = NULL;
-+		return &(*e)->event;
-+	}
-+}
-+
-+/**
-+ * zh_cb_adapter_add - Callback for adapter add events
-+ * @filp: Address of struct file (see below)
-+ * @devno: of the adapter
-+ * @wwnn: of the adapter
-+ * @wwpn: of the adapter
-+ * Context: irq/user
-+ * FIXME: check context
-+ *
-+ * Called in two situations:
-+ *   - initial config creation (filp != NULL) (USER-ctx)
-+ *   - adapter added to running system (filp == NULL) (IRQ-ctx)
-+ *
-+ * When called with (filp == NULL) the created event is added to the
-+ * global list of events, and thus delivered to all registered fd's.
-+ * When called with (filp != NULL) the created event is private to the
-+ * specific filp, and is appended only there.
-+ * */
-+static void zh_cb_adapter_add(struct file *filp, devno_t devno, wwn_t wwnn,
-+			      wwn_t wwpn)
-+{
-+	struct zh_event *event;
-+	struct zh_event_item *e = NULL;
-+	struct zh_config *c = NULL;
-+	
-+	ZH_LOG(KERN_DEBUG, "wwpn: 0x%llx\n", (long long) wwpn);
-+
-+	event = prepare_event(filp, &e, &c);
-+	if (NULL == event) {
-+		return;
-+	}
-+
-+	event->event = ZH_EVENT_ADAPTER_ADD;
-+	event->data.adapter_add.devid = ZH_DEVID(0, 0, devno);
-+	event->data.adapter_add.wwnn = wwnn;
-+	event->data.adapter_add.wwpn = wwpn;
-+
-+	add_event(filp, e, c);
-+}
-+
-+/**
-+ * zh_cb_port_add() - Called on port add events
-+ * @filp: Address of struct file (see zh_cb_adapter_add)
-+ * @devno: of the adapter
-+ * @wwnn: of the port
-+ * @wwpn: of the port
-+ * @did: of the port
-+ * Context: irq/user
-+ * FIXME: check context
-+ */
-+static void zh_cb_port_add(struct file *filp, devno_t devno, wwn_t wwpn,
-+			   wwn_t wwnn, fc_id_t did)
-+{
-+	struct zh_event *event;
-+	struct zh_event_item *e;
-+	struct zh_config *c;
-+
-+	ZH_LOG(KERN_DEBUG, "wwpn: 0x%llx\n", (long long) wwpn);
-+
-+	event = prepare_event(filp, &e, &c);
-+	if (NULL == event) {
-+		return;
-+	}
-+
-+	event->event = ZH_EVENT_PORT_ADD;
-+	event->data.port_add.devid = ZH_DEVID(0, 0, devno);
-+	event->data.port_add.wwpn = wwpn;
-+	event->data.port_add.wwnn = wwnn;
-+	event->data.port_add.did = did;
-+
-+	add_event(filp, e, c);
-+}
-+
-+/**
-+ * zh_cb_unit_add() - Called on unit add events
-+ * @filp: Address of struct file (see zh_cb_adapter_add)
-+ * @devno: of the adapter
-+ * @wwnn: of the port
-+ * @wwpn: of the port
-+ * @fclun: of the unit
-+ * @host: SCSI host id
-+ * @channel: SCSI channel
-+ * @target: SCSI target
-+ * @lun: SCSI lun
-+ * Context: irq/user
-+ * FIXME: check context
-+ */
-+static void zh_cb_unit_add(struct file *filp, devno_t devno, wwn_t wwpn,
-+			   fcp_lun_t fclun, unsigned int host,
-+			   unsigned int channel, unsigned int target,
-+			   unsigned int lun)
-+{
-+	struct zh_event *event;
-+	struct zh_event_item *e;
-+	struct zh_config *c;
-+
-+	ZH_LOG(KERN_DEBUG, "wwpn: 0x%llx lun: 0x%llx\n", (long long) wwpn,
-+	       (long long) fclun);
-+
-+	event = prepare_event(filp, &e, &c);
-+	if (NULL == event) {
-+		return;
-+	}
-+
-+	event->event = ZH_EVENT_UNIT_ADD;
-+	event->data.unit_add.devid = ZH_DEVID(0, 0, devno);
-+	event->data.unit_add.wwpn = wwpn;
-+	event->data.unit_add.fclun = fclun;
-+	event->data.unit_add.host = host;
-+	event->data.unit_add.channel = channel;
-+	event->data.unit_add.id = target;
-+	event->data.unit_add.lun = lun;
-+
-+	add_event(filp, e, c);
-+}
-+
-+/**
-+ * zh_cb_incomming_els_rscn - RSCN ELS worker
-+ * @devno: of the adapter
-+ * @s_id: sid of the sending port
-+ * @v: pointer to rscn payload
-+ * Context: irq
-+ */
-+static inline void zh_cb_incomming_els_rscn(const devno_t devno,
-+					    const fc_id_t s_id, const void *v)
-+{
-+	int count, i;
-+	struct zh_config *c;
-+	const struct zh_els_rscn *rscn = v;
-+	struct zh_els_rscn_payload *payload = rscn->payload;
-+
-+	ZH_LOG(KERN_INFO, "incoming RSCN\n");
-+
-+	count = rscn->payload_length / sizeof(struct zh_els_rscn_payload);
-+	
-+	for (i = 0; i < count; ++i, ++payload) {
-+		c = kmalloc(sizeof(*c), GFP_ATOMIC);
-+		if (NULL == c) {
-+			return;
-+		}
-+
-+		c->event.event = ZH_EVENT_POLLED;
-+		c->event.data.polled.event = ZH_EVENT_POLLED_RSCN;
-+		c->event.data.polled.devid = ZH_DEVID(0, 0, devno);
-+		c->event.data.polled.data.rscn.port_fc_id = s_id;
-+		memcpy(&c->event.data.polled.data.rscn.port_page, payload,
-+		       sizeof(*payload));
-+
-+		add_event_to_polled(c);
-+	}
-+}
-+
-+/**
-+ * zh_cb_incomming_els - hook for incomming els'
-+ * @devno: of the adapter the els came in
-+ * @s_id: sid of the port the els came in
-+ * @*v: pointer to the els payload
-+ * Context: irq
-+ */
-+static void zh_cb_incomming_els(const devno_t devno, const fc_id_t s_id,
-+				const void *v)
-+{
-+	const u8 *op = v;
-+
-+	switch (*op)
-+	{
-+	case ZFCP_LS_RSCN:
-+		zh_cb_incomming_els_rscn(devno, s_id, v);
-+		break;
-+	}
-+}
-+
-+/**
-+ * zh_cb_link_down - hook for link down events
-+ * @id: s_id this event occured on
-+ * Context: irq
-+ *
-+ * Atm this hook is only called for local link down events.
-+ */
-+static void zh_cb_link_down(const fc_id_t id)
-+{
-+	struct zh_config *c;
-+
-+	c = kmalloc(sizeof(*c), GFP_ATOMIC);
-+	if (NULL == c) {
-+		return;
-+	}
-+
-+	c->event.event = ZH_EVENT_POLLED_LINK_DOWN;
-+	c->event.data.polled.data.link.port_fc_id = id;
-+
-+	add_event_to_polled(c);
-+}
-+
-+/**
-+ * zh_cb_link_up - hook for link up events
-+ * @id: s_id this event occured on
-+ * Context: irq
-+ *
-+ * Atm this hook is only called for local link up events.
-+ */
-+static void zh_cb_link_up(const fc_id_t id)
-+{
-+	struct zh_config *c;
-+
-+	c = kmalloc(sizeof(*c), GFP_ATOMIC);
-+	if (NULL == c) {
-+		return;
-+	}
-+
-+	c->event.event = ZH_EVENT_POLLED_LINK_UP;
-+	c->event.data.polled.data.link.port_fc_id = id;
-+
-+	add_event_to_polled(c);
-+}
-+
-+/**
-+ * zh_map_port_speed - maps port speed between FC-FS/FC-GS4 and FC-HBA
-+ * @speed: address of u32 to be mapped
-+ */
-+static void zh_map_port_speed(u32 *speed, int flag_operating_speed)
-+{
-+	if (flag_operating_speed == ZH_PORT_OPERATING_SPEED) {
-+		switch(*speed) {
-+		case 4:
-+			*speed = 8;
-+		case 8:	
-+			*speed = 4;
-+		case (1<<14):
-+			*speed = 0;
-+		}
-+	} else {		/* ZH_PORT_SUPPORTED_SPEED */
-+		switch(*speed) {
-+		case 4:
-+			*speed = 8;
-+		case 8:
-+			*speed = 4;
-+		case (1<<15):
-+			*speed = 0;
-+		}
-+	}
-+
-+	return;
-+}
-+
-+module_init(zh_init);
-+module_exit(zh_exit);
-=== drivers/s390/scsi/zfcp_main.c
-==================================================================
---- drivers/s390/scsi/zfcp_main.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/scsi/zfcp_main.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,21393 @@
-+/*
-+ * 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
-+ *      Stefan Bader <stefan.bader at de.ibm.com>
-+ *      Andreas Herrmann <aherrman at de.ibm.com>
-+ *      Stefan Voelkel <Stefan.Voelkel at millenux.com>
-+ */
-+
-+/* this drivers version (do not edit !!! generated and updated by cvs) */
-+#define ZFCP_REVISION		"$Revision: 1.31.2.25 $"
-+
-+#define ZFCP_QTCB_VERSION	FSF_QTCB_CURRENT_VERSION
-+
-+#define ZFCP_PRINT_FLAGS
-+
-+#undef ZFCP_CAUSE_ERRORS
-+
-+#undef ZFCP_MEMORY_DEBUG
-+
-+#undef ZFCP_MEM_POOL_ONLY
-+
-+#define ZFCP_DEBUG_REQUESTS	/* fsf_req tracing */
-+
-+#define ZFCP_DEBUG_COMMANDS	/* host_byte tracing */
-+
-+#define ZFCP_DEBUG_ABORTS	/* scsi_cmnd abort tracing */
-+
-+#define ZFCP_DEBUG_INCOMING_ELS	/* incoming ELS tracing */
-+
-+#undef ZFCP_RESID
-+
-+#define ZFCP_STAT_REQSIZES
-+#define ZFCP_STAT_QUEUES
-+
-+// current implementation does not work due to proc_sema
-+#undef ZFCP_ERP_DEBUG_SINGLE_STEP
-+
-+#ifdef ZFCP_CAUSE_ERRORS
-+struct timer_list zfcp_force_error_timer;
-+#endif
-+
-+/* ATTENTION: value must not be used by hardware */
-+#define FSF_QTCB_UNSOLICITED_STATUS		0x6305
-+
-+#define ZFCP_FAKE_SCSI_COMPLETION_TIME	        (HZ / 3)
-+
-+#define ZFCP_SCSI_LOW_MEM_TIMEOUT               (100*HZ)
-+
-+#define ZFCP_SCSI_ER_TIMEOUT                    (100*HZ)
-+
-+#define ZFCP_SCSI_RETRY_TIMEOUT			(120*HZ)
-+
-+/********************* QDIO SPECIFIC DEFINES *********************************/
-+
-+/* allow as much chained SBALs as supported by hardware */
-+#define ZFCP_MAX_SBALS_PER_REQ		FSF_MAX_SBALS_PER_REQ
-+#define ZFCP_MAX_SBALS_PER_CT_REQ	FSF_MAX_SBALS_PER_REQ
-+#define ZFCP_MAX_SBALS_PER_ELS_REQ	FSF_MAX_SBALS_PER_ELS_REQ
-+/* DMQ bug workaround: don't use last SBALE */
-+#define ZFCP_MAX_SBALES_PER_SBAL	(QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
-+/* index of last SBALE (with respect to DMQ bug workaround) */
-+#define ZFCP_LAST_SBALE_PER_SBAL	(ZFCP_MAX_SBALES_PER_SBAL - 1)
-+/* max. number of (data buffer) SBALEs in largest SBAL chain */
-+#define ZFCP_MAX_SBALES_PER_REQ		\
-+	(ZFCP_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL \
-+	 - 2)	/* request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */
-+
-+/* FIXME(tune): free space should be one max. SBAL chain plus what? */
-+#define ZFCP_QDIO_PCI_INTERVAL		(QDIO_MAX_BUFFERS_PER_Q - (ZFCP_MAX_SBALS_PER_REQ + 4))
-+
-+#define ZFCP_SBAL_TIMEOUT (5 * HZ)
-+
-+#define ZFCP_STATUS_READ_FAILED_THRESHOLD	3
-+
-+/* parsing stuff */
-+#define ZFCP_PARSE_SPACE_CHARS		" \t"
-+#define ZFCP_PARSE_RECORD_DELIM_CHARS	";\n"
-+#define ZFCP_PARSE_DELIM_CHARS		":"
-+#define ZFCP_PARSE_COMMENT_CHARS	"#"
-+#define ZFCP_PARSE_ADD		1
-+#define ZFCP_PARSE_DEL		0
-+
-+#include <linux/config.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/delay.h>
-+#include <linux/slab.h>
-+#include <linux/version.h>
-+#include <linux/interrupt.h>
-+#include <linux/init.h>
-+#include <linux/proc_fs.h>
-+#include <linux/notifier.h>
-+#include <linux/reboot.h>
-+#include <linux/time.h>
-+
-+#include <linux/ioctl.h>
-+#include <linux/major.h>
-+#include <linux/miscdevice.h>
-+
-+#include "../../fc4/fc.h"
-+
-+#include <linux/module.h>
-+
-+#include <asm/semaphore.h>
-+#include <asm/io.h>
-+#include <asm/uaccess.h>
-+
-+#include <asm/ebcdic.h>
-+#include <asm/cpcmd.h>               /* Debugging only */
-+#include <asm/processor.h>           /* Debugging only */
-+#include <asm/div64.h>
-+#include <asm/ebcdic.h>
-+
-+#include "zfcp.h"
-+#include "zfcp_zh.h"
-+
-+#define ZFCP_MAX_SBALES_PER_CONTROL_FILE \
-+	(ZFCP_CFDC_MAX_CONTROL_FILE_SIZE + PAGE_SIZE - 1) >> PAGE_SHIFT
-+
-+/* Cosmetics */
-+#define ZFCP_FSFREQ_CLEANUP_TIMEOUT	HZ/10
-+
-+#define ZFCP_TYPE2_RECOVERY_TIME        8*HZ
-+
-+#ifdef ZFCP_STAT_REQSIZES
-+#define ZFCP_MAX_PROC_SIZE              3 * PAGE_SIZE
-+#else
-+#define ZFCP_MAX_PROC_SIZE              PAGE_SIZE
-+#endif
-+
-+#define ZFCP_64BIT			(BITS_PER_LONG == 64)
-+#define ZFCP_31BIT			(!ZFCP_64BIT)
-+
-+#define QDIO_SCSI_QFMT			1	/* 1 for FSF */
-+
-+/* queue polling (values in microseconds) */
-+#define ZFCP_MAX_INPUT_THRESHOLD 	5000	/* FIXME: tune */
-+#define ZFCP_MAX_OUTPUT_THRESHOLD 	1000	/* FIXME: tune */
-+#define ZFCP_MIN_INPUT_THRESHOLD 	1	/* ignored by QDIO layer */
-+#define ZFCP_MIN_OUTPUT_THRESHOLD 	1	/* ignored by QDIO layer */
-+
-+#define ZFCP_PARM_FILE                  "mod_parm"
-+#define ZFCP_MAP_FILE                   "map"
-+#define ZFCP_ADD_MAP_FILE               "add_map"
-+#define ZFCP_STATUS_FILE                "status"
-+#define ZFCP_MAX_PROC_LINE              1024
-+
-+#define ZFCP_CFDC_DEV_NAME		"zfcp_cfdc"
-+#define ZFCP_CFDC_DEV_MAJOR		MISC_MAJOR
-+#define ZFCP_CFDC_DEV_MINOR		MISC_DYNAMIC_MINOR
-+
-+#define ZFCP_CFDC_IOC_MAGIC		0xDD
-+#define ZFCP_CFDC_IOC \
-+	_IOWR(ZFCP_CFDC_IOC_MAGIC, 0, zfcp_cfdc_sense_data_t)
-+
-+#ifdef CONFIG_S390_SUPPORT
-+
-+#define ZFCP_IOC_DEFAULT(cmd)		{cmd, (void*)sys_ioctl}
-+
-+typedef struct _zfcp_ioctl_entry {
-+	unsigned int cmd;
-+	ioctl_trans_handler_t handler;
-+} zfcp_ioctl_entry_t;
-+
-+static zfcp_ioctl_entry_t zfcp_cfdc_ioc __initdata =
-+	ZFCP_IOC_DEFAULT(ZFCP_CFDC_IOC);
-+
-+#endif
-+
-+#define ZFCP_RESET_ERP			"reset erp"
-+#define ZFCP_SET_OFFLINE		"set offline"
-+#define ZFCP_SET_ONLINE			"set online"
-+#define ZFCP_RTV			"rtv"
-+#define ZFCP_RLS			"rls"
-+#define ZFCP_PDISC			"pdisc"
-+#define ZFCP_ADISC			"adisc"
-+#define ZFCP_STAT_ON			"stat on"
-+#define ZFCP_STAT_OFF			"stat off"
-+#define ZFCP_STAT_RESET			"stat reset"
-+
-+#define ZFCP_DID_MASK                   0x00ffffff
-+
-+/* Adapter Identification Parameters */
-+#define ZFCP_CONTROL_UNIT_TYPE	0x1731
-+#define ZFCP_CONTROL_UNIT_MODEL	0x03
-+#define ZFCP_DEVICE_TYPE	0x1732
-+#define ZFCP_DEVICE_MODEL	0x03
-+#define ZFCP_DEVICE_MODEL_PRIV	0x04
-+ 
-+#define ZFCP_FC_SERVICE_CLASS_DEFAULT	FSF_CLASS_3
-+
-+/* timeout for name-server lookup (in seconds) */
-+/* FIXME(tune) */
-+#define ZFCP_NS_GID_PN_TIMEOUT		10
-+#define ZFCP_NS_GA_NXT_TIMEOUT		120
-+
-+#define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES	6
-+#define ZFCP_EXCHANGE_CONFIG_DATA_SLEEP		50
-+
-+#define ZFCP_STATUS_READS_RECOM		FSF_STATUS_READS_RECOM
-+
-+/* largest SCSI command we can process */
-+/* FCP-2 (FCP_CMND IU) allows up to (255-3+16) */
-+#define ZFCP_MAX_SCSI_CMND_LENGTH	255
-+/* maximum number of commands in LUN queue */
-+#define ZFCP_CMND_PER_LUN               32
-+
-+/* debug feature entries per adapter */
-+#define ZFCP_ERP_DBF_INDEX	1 
-+#define ZFCP_ERP_DBF_AREAS	2
-+#define ZFCP_ERP_DBF_LENGTH	16
-+#define ZFCP_ERP_DBF_LEVEL	3
-+#define ZFCP_ERP_DBF_NAME	"zfcp_erp"
-+
-+#define ZFCP_REQ_DBF_INDEX	1
-+#define ZFCP_REQ_DBF_AREAS	1
-+#define ZFCP_REQ_DBF_LENGTH	8
-+#define ZFCP_REQ_DBF_LEVEL	DEBUG_OFF_LEVEL
-+#define ZFCP_REQ_DBF_NAME	"zfcp_req"
-+
-+#define ZFCP_CMD_DBF_INDEX	2
-+#define ZFCP_CMD_DBF_AREAS	1
-+#define ZFCP_CMD_DBF_LENGTH	8
-+#define ZFCP_CMD_DBF_LEVEL	3
-+#define ZFCP_CMD_DBF_NAME	"zfcp_cmd"
-+
-+#define ZFCP_ABORT_DBF_INDEX	2
-+#define ZFCP_ABORT_DBF_AREAS	1
-+#define ZFCP_ABORT_DBF_LENGTH	8
-+#define ZFCP_ABORT_DBF_LEVEL	6
-+#define ZFCP_ABORT_DBF_NAME	"zfcp_abt"
-+
-+#define ZFCP_IN_ELS_DBF_INDEX	2
-+#define ZFCP_IN_ELS_DBF_AREAS	1
-+#define ZFCP_IN_ELS_DBF_LENGTH	8
-+#define ZFCP_IN_ELS_DBF_LEVEL	6
-+#define ZFCP_IN_ELS_DBF_NAME	"zfcp_els"
-+
-+/*
-+ * paranoia: some extra checks ensuring driver consistency and probably
-+ * reducing performance,
-+ * should be compiled in and defined per default during development
-+ * should be compiled in and disabled per default in beta program
-+ * should not be compiled in field
-+ */
-+
-+/* do (not) compile in paranoia code (by means of "dead code") */
-+#undef ZFCP_PARANOIA_DEAD_CODE 
-+
-+/* enable/disable paranoia checks if compiled in */
-+#define ZFCP_PARANOIA_PER_DEFAULT
-+
-+/* paranoia status (override by means of module parameter allowed) */
-+#ifdef ZFCP_PARANOIA_PER_DEFAULT
-+unsigned char zfcp_paranoia = 1;
-+#else
-+unsigned char zfcp_paranoia = 0;
-+#endif
-+
-+/*
-+ * decide whether paranoia checks are (1) dead code,
-+ * (2) active code + disabled, or (3) active code + enabled
-+ */
-+#ifdef ZFCP_PARANOIA_DEAD_CODE
-+#define ZFCP_PARANOIA		if (0)
-+#else
-+#define ZFCP_PARANOIA		if (zfcp_paranoia)
-+#endif
-+
-+/* association between FSF command and FSF QTCB type */
-+static u32 fsf_qtcb_type[] = {
-+  [ FSF_QTCB_FCP_CMND ]			= FSF_IO_COMMAND,
-+  [ FSF_QTCB_ABORT_FCP_CMND ]		= FSF_SUPPORT_COMMAND,
-+  [ FSF_QTCB_OPEN_PORT_WITH_DID ]	= FSF_SUPPORT_COMMAND,
-+  [ FSF_QTCB_OPEN_LUN ]			= FSF_SUPPORT_COMMAND,
-+  [ FSF_QTCB_CLOSE_LUN ]		= FSF_SUPPORT_COMMAND,
-+  [ FSF_QTCB_CLOSE_PORT ]		= FSF_SUPPORT_COMMAND,
-+  [ FSF_QTCB_CLOSE_PHYSICAL_PORT ]	= FSF_SUPPORT_COMMAND,
-+  [ FSF_QTCB_SEND_ELS ]			= FSF_SUPPORT_COMMAND,
-+  [ FSF_QTCB_SEND_GENERIC ]		= FSF_SUPPORT_COMMAND,
-+  [ FSF_QTCB_EXCHANGE_CONFIG_DATA ]	= FSF_CONFIG_COMMAND,
-+  [ FSF_QTCB_EXCHANGE_PORT_DATA ]       = FSF_PORT_COMMAND,
-+  [ FSF_QTCB_DOWNLOAD_CONTROL_FILE ]	= FSF_SUPPORT_COMMAND,
-+  [ FSF_QTCB_UPLOAD_CONTROL_FILE ]	= FSF_SUPPORT_COMMAND
-+};
-+
-+/* accumulated log level (module parameter) */
-+static u32 loglevel = ZFCP_LOG_LEVEL_DEFAULTS;
-+
-+unsigned long debug_addr;
-+unsigned long debug_len;
-+
-+const char zfcp_topologies[5][25] = {
-+	{"<error>"},
-+	{"point-to-point"},
-+	{"fabric"}, 
-+	{"arbitrated loop"},
-+	{"fabric (virt. adapter)"}
-+};
-+
-+const char zfcp_act_subtable_type[5][8] = {
-+	{"unknown"}, {"OS"}, {"WWPN"}, {"DID"}, {"LUN"}
-+};
-+
-+inline void _zfcp_hex_dump(char *addr, int count)
-+{
-+	int i;
-+       	for (i = 0; i < count; i++) {
-+       	        printk("%02x", addr[i]);
-+        	if ((i % 4) == 3)
-+       	        	printk(" ");
-+               	if ((i % 32) == 31)
-+                	printk("\n");
-+	}
-+	if ((i % 32) != 31)
-+		printk("\n");
-+}
-+ 
-+#define ZFCP_HEX_DUMP(level, addr, count) \
-+		if (ZFCP_LOG_CHECK(level)) { \
-+			_zfcp_hex_dump(addr, count); \
-+		}
-+
-+static int proc_debug=1;
-+
-+/*
-+ * buffer struct used for private_data entry (proc interface)
-+ */
-+
-+typedef struct { 
-+     int len;
-+     char *buf;
-+} procbuf_t;
-+
-+
-+/*
-+ * not yet optimal but useful:
-+ * waits until condition is met or timeout is expired,
-+ * condition might be a function call which allows to
-+ * execute some additional instructions aside from check
-+ * (e.g. get a lock without race if condition is met),
-+ * timeout is modified and holds the remaining time,
-+ * thus timeout is zero if timeout is expired,
-+ * result value zero indicates that condition has not been met
-+ */
-+#define __ZFCP_WAIT_EVENT_TIMEOUT(timeout, condition) \
-+do { \
-+	set_current_state(TASK_UNINTERRUPTIBLE); \
-+	while (!(condition) && timeout) \
-+		timeout = schedule_timeout(timeout); \
-+	current->state = TASK_RUNNING; \
-+} while (0);
-+
-+#define ZFCP_WAIT_EVENT_TIMEOUT(waitqueue, timeout, condition) \
-+do { \
-+	wait_queue_t entry; \
-+	init_waitqueue_entry(&entry, current); \
-+	add_wait_queue(&waitqueue, &entry); \
-+	__ZFCP_WAIT_EVENT_TIMEOUT(timeout, condition) \
-+	remove_wait_queue(&waitqueue, &entry); \
-+} while (0);
-+
-+
-+/* General type defines */
-+
-+typedef long long unsigned int  llui_t;
-+
-+/* QDIO request identifier */
-+typedef u64	qdio_reqid_t;
-+
-+
-+/* FCP(-2) FCP_CMND IU */
-+typedef struct _fcp_cmnd_iu {
-+	/* FCP logical unit number */
-+	fcp_lun_t	fcp_lun;
-+	/* command reference number */
-+	u8		crn;
-+	/* reserved */
-+	u8		reserved0:5;
-+	/* task attribute */
-+	u8		task_attribute:3;
-+	/* task management flags */
-+	u8		task_management_flags;
-+	/* additional FCP_CDB length */
-+	u8		add_fcp_cdb_length:6;
-+	/* read data */
-+	u8		rddata:1;
-+	/* write data */
-+	u8		wddata:1;
-+	/* */
-+	u8		fcp_cdb[FCP_CDB_LENGTH];
-+	/* variable length fields (additional FCP_CDB, FCP_DL) */
-+} __attribute__((packed)) fcp_cmnd_iu_t;
-+
-+/* data length field may be at variable position in FCP-2 FCP_CMND IU */
-+typedef u32	fcp_dl_t;
-+
-+
-+#define RSP_CODE_GOOD			0
-+#define RSP_CODE_LENGTH_MISMATCH	1
-+#define RSP_CODE_FIELD_INVALID		2
-+#define RSP_CODE_RO_MISMATCH		3
-+#define RSP_CODE_TASKMAN_UNSUPP		4
-+#define RSP_CODE_TASKMAN_FAILED		5
-+
-+/* see fc-fs */
-+#define LS_FAN 0x60000000
-+#define LS_RSCN 0x61040000
-+
-+typedef struct _fcp_rscn_head {
-+        u8  command;
-+        u8  page_length; /* always 0x04 */
-+        u16 payload_len;
-+} __attribute__((packed)) fcp_rscn_head_t;
-+
-+typedef struct _fcp_rscn_element {
-+        u8  reserved:2;
-+        u8  event_qual:4;
-+        u8  addr_format:2;
-+        u32 nport_did:24;
-+} __attribute__((packed)) fcp_rscn_element_t;
-+
-+#define ZFCP_PORT_ADDRESS   0x0
-+#define ZFCP_AREA_ADDRESS   0x1
-+#define ZFCP_DOMAIN_ADDRESS 0x2
-+#define ZFCP_FABRIC_ADDRESS 0x3
-+
-+#define ZFCP_PORTS_RANGE_PORT   0xFFFFFF
-+#define ZFCP_PORTS_RANGE_AREA   0xFFFF00
-+#define ZFCP_PORTS_RANGE_DOMAIN 0xFF0000
-+#define ZFCP_PORTS_RANGE_FABRIC 0x000000
-+
-+#define ZFCP_NO_PORTS_PER_AREA    0x100
-+#define ZFCP_NO_PORTS_PER_DOMAIN  0x10000
-+#define ZFCP_NO_PORTS_PER_FABRIC  0x1000000
-+
-+typedef struct _fcp_fan {
-+        u32 command;
-+        u32 fport_did;
-+        wwn_t fport_wwpn;
-+        wwn_t fport_wwname;
-+} __attribute__((packed)) fcp_fan_t;
-+
-+/* see fc-ph */
-+typedef struct _fcp_logo {
-+        u32 command;
-+        u32 nport_did;
-+        wwn_t nport_wwpn;
-+} __attribute__((packed)) fcp_logo_t;
-+
-+
-+/* FCP(-2) FCP_RSP IU */
-+typedef struct _fcp_rsp_iu {
-+	/* reserved */
-+	u8		reserved0[10];
-+	union {
-+		struct {
-+			/* reserved */
-+			u8		reserved1:3;
-+			/* */
-+			u8		fcp_conf_req:1;
-+			/* */
-+			u8		fcp_resid_under:1;
-+			/* */
-+			u8		fcp_resid_over:1;
-+			/* */
-+			u8		fcp_sns_len_valid:1;
-+			/* */
-+			u8		fcp_rsp_len_valid:1;
-+		} bits;
-+		u8		value;
-+	} validity;
-+	/* */
-+	u8		scsi_status;
-+	/* */
-+	u32		fcp_resid;
-+	/* */
-+	u32		fcp_sns_len;
-+	/* */
-+	u32		fcp_rsp_len;
-+	/* variable length fields: FCP_RSP_INFO, FCP_SNS_INFO */
-+} __attribute__((packed)) fcp_rsp_iu_t;
-+
-+
-+inline char *zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu_t *fcp_rsp_iu)
-+{
-+        char *fcp_rsp_info_ptr = NULL;
-+        fcp_rsp_info_ptr=
-+                (unsigned char*)fcp_rsp_iu + (sizeof(fcp_rsp_iu_t));
-+
-+        return fcp_rsp_info_ptr;
-+}
-+
-+
-+inline char *zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu_t *fcp_rsp_iu)
-+{
-+	char *fcp_sns_info_ptr = NULL;
-+        fcp_sns_info_ptr =
-+                (unsigned char*)fcp_rsp_iu + (sizeof(fcp_rsp_iu_t));
-+          // NOTE:fcp_rsp_info is really only a part of the whole as 
-+          // defined in FCP-2 documentation
-+        if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid) 
-+                fcp_sns_info_ptr = (char *)fcp_sns_info_ptr +
-+                        fcp_rsp_iu->fcp_rsp_len;
-+
-+	return fcp_sns_info_ptr;
-+}
-+
-+#ifdef ZFCP_STAT_REQSIZES
-+typedef struct _zfcp_statistics {
-+        struct list_head list;
-+        u32 num;
-+        u32 hits;
-+} zfcp_statistics_t;
-+#endif
-+
-+
-+/*********************** LIST DEFINES ****************************************/
-+
-+#define ZFCP_FIRST_ADAPTER \
-+	ZFCP_FIRST_ENTITY(&zfcp_data.adapter_list_head,zfcp_adapter_t)
-+
-+#define ZFCP_FIRST_PORT(a) \
-+	ZFCP_FIRST_ENTITY(&(a)->port_list_head,zfcp_port_t)
-+
-+#define ZFCP_FIRST_UNIT(p) \
-+	ZFCP_FIRST_ENTITY(&(p)->unit_list_head,zfcp_unit_t)
-+
-+#define ZFCP_FIRST_SCSIREQ(a) \
-+	ZFCP_FIRST_ENTITY(&(a)->scsi_req_list_head,zfcp_scsi_req_t)
-+
-+#define ZFCP_FIRST_FSFREQ(a) \
-+	ZFCP_FIRST_ENTITY(&(a)->fsf_req_list_head,zfcp_fsf_req_t)
-+
-+#define ZFCP_LAST_ADAPTER \
-+	ZFCP_LAST_ENTITY(&zfcp_data.adapter_list_head,zfcp_adapter_t)
-+
-+#define ZFCP_LAST_PORT(a) \
-+	ZFCP_LAST_ENTITY(&(a)->port_list_head,zfcp_port_t)
-+
-+#define ZFCP_LAST_UNIT(p) \
-+	ZFCP_LAST_ENTITY(&(p)->unit_list_head,zfcp_unit_t)
-+
-+#define ZFCP_LAST_SCSIREQ(a) \
-+	ZFCP_LAST_ENTITY(&(a)->scsi_req_list_head,zfcp_scsi_req_t)
-+
-+#define ZFCP_LAST_FSFREQ(a) \
-+	ZFCP_LAST_ENTITY(&(a)->fsf_req_list_head,zfcp_fsf_req_t)
-+
-+#define ZFCP_PREV_ADAPTER(a) \
-+	ZFCP_PREV_ENTITY(&zfcp_data.adapter_list_head,(a),zfcp_adapter_t)
-+
-+#define ZFCP_PREV_PORT(p) \
-+	ZFCP_PREV_ENTITY(&(p)->adapter->port_list_head,(p),zfcp_port_t)
-+
-+#define ZFCP_PREV_UNIT(u) \
-+	ZFCP_PREV_ENTITY(&(u)->port->unit_list_head,(u),zfcp_unit_t)
-+
-+#define ZFCP_PREV_SCSIREQ(s) \
-+	ZFCP_PREV_ENTITY(&(s)->adapter->scsi_req_list_head,(s),zfcp_scsi_req_t)
-+
-+#define ZFCP_PREV_FSFREQ(o) \
-+	ZFCP_PREV_ENTITY(&(o)->adapter->fsf_req_list_head,(o), \
-+				zfcp_fsf_req_t)
-+
-+#define ZFCP_NEXT_ADAPTER(a) \
-+	ZFCP_NEXT_ENTITY(&zfcp_data.adapter_list_head,(a),zfcp_adapter_t)
-+
-+#define ZFCP_NEXT_PORT(p) \
-+	ZFCP_NEXT_ENTITY(&(p)->adapter->port_list_head,(p),zfcp_port_t)
-+
-+#define ZFCP_NEXT_UNIT(u) \
-+	ZFCP_NEXT_ENTITY(&(u)->port->unit_list_head,(u),zfcp_unit_t)
-+
-+#define ZFCP_NEXT_SCSIREQ(s) \
-+	ZFCP_NEXT_ENTITY(&(s)->adapter->scsi_req_list_head,(s),zfcp_scsi_req_t)
-+
-+#define ZFCP_NEXT_FSFREQ(o) \
-+	ZFCP_NEXT_ENTITY(&(o)->adapter->fsf_req_list_head,(o), \
-+				zfcp_fsf_req_t)
-+
-+#define ZFCP_FOR_EACH_FSFREQ(a,o) \
-+	ZFCP_FOR_EACH_ENTITY(&(a)->fsf_req_list_head,(o),zfcp_fsf_req_t)
-+
-+/*
-+ * use these macros if you traverse a list and do not stop after
-+ * altering the list,
-+ * attention: do not modify "tmp" (last) arg during iterations,
-+ * usually: removing several elements 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 write variant of lock
-+ */
-+
-+#define ZFCP_FOR_NEXT_ENTITY(head,curr,type,tmp) \
-+	for (curr = ZFCP_FIRST_ENTITY(head,type), \
-+	     tmp = ZFCP_NEXT_ENTITY(head,curr,type); \
-+	     curr; \
-+	     curr = tmp, \
-+	     tmp = ZFCP_NEXT_ENTITY(head,curr,type))
-+
-+#define ZFCP_FOR_NEXT_ADAPTER(a,n) \
-+	ZFCP_FOR_NEXT_ENTITY(&zfcp_data.adapter_list_head,(a), \
-+				zfcp_adapter_t,(n))
-+
-+#define ZFCP_FOR_NEXT_PORT(a,p,n) \
-+	ZFCP_FOR_NEXT_ENTITY(&(a)->port_list_head,(p),zfcp_port_t,(n))
-+
-+#define ZFCP_FOR_NEXT_UNIT(p,u,n) \
-+	ZFCP_FOR_NEXT_ENTITY(&(p)->unit_list_head,(u),zfcp_unit_t,(n))
-+
-+#define ZFCP_FOR_NEXT_SCSIREQ(a,s,n) \
-+	ZFCP_FOR_NEXT_ENTITY(&(a)->scsi_req_list_head,(s),zfcp_scsi_req_t,(n))
-+
-+#define ZFCP_FOR_NEXT_FSFREQ(a,o,n) \
-+	ZFCP_FOR_NEXT_ENTITY(&(a)->fsf_req_list_head,(o), \
-+				zfcp_fsf_req_t,(n))
-+
-+/*
-+ * use these macros for loops do not walking through lists but*
-+ * changing the list at their heads,
-+ * attention: without changing the head of the list or any other
-+ * break condition this will become an endless loop,
-+ * next and previous pointers may become invalid !!!
-+ * usually: removing all elements in lists
-+ * 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 write variant of lock
-+ */
-+#define ZFCP_WHILE_ENTITY(head,curr,type) \
-+	while (((curr) = ZFCP_FIRST_ENTITY(head,type)))
-+
-+#define ZFCP_WHILE_ADAPTER(a) \
-+	ZFCP_WHILE_ENTITY(&zfcp_data.adapter_list_head,(a),zfcp_adapter_t)
-+
-+#define ZFCP_WHILE_PORT(a,p) \
-+	ZFCP_WHILE_ENTITY(&(a)->port_list_head,(p),zfcp_port_t)
-+
-+#define ZFCP_WHILE_UNIT(p,u) \
-+	ZFCP_WHILE_ENTITY(&(p)->unit_list_head,(u),zfcp_unit_t)
-+
-+#define ZFCP_WHILE_SCSIREQ(a,s) \
-+	ZFCP_WHILE_ENTITY(&(a)->scsi_req_list_head,(s),zfcp_scsi_req_t)
-+
-+#define ZFCP_WHILE_FSFREQ(a,o) \
-+	ZFCP_WHILE_ENTITY(&(a)->fsf_req_list_head,(o),zfcp_fsf_req_t)
-+
-+/* prototypes for functions which could kernel lib functions (but aren't) */
-+static size_t	strnspn(const char*, const char*, size_t);
-+char*		strnpbrk(const char*, const char*, size_t);
-+char*		strnchr(const char*, int, size_t);
-+char*		strnrchr(const char*, int, size_t);
-+
-+/* prototypes for functions written against the modul interface */
-+static int __init	zfcp_module_init(void);
-+static void __exit	zfcp_module_exit(void);
-+
-+int zfcp_reboot_handler (struct notifier_block*, unsigned long, void*);
-+
-+/* prototypes for functions written against the SCSI stack HBA driver interface */
-+int		zfcp_scsi_detect (Scsi_Host_Template*);
-+int		zfcp_scsi_revoke (Scsi_Device*);
-+int		zfcp_scsi_release (struct Scsi_Host*);
-+int		zfcp_scsi_queuecommand (Scsi_Cmnd*, void (*done)(Scsi_Cmnd*));
-+int		zfcp_scsi_eh_abort_handler (Scsi_Cmnd*);
-+int		zfcp_scsi_eh_device_reset_handler (Scsi_Cmnd*);
-+int		zfcp_scsi_eh_bus_reset_handler (Scsi_Cmnd*);
-+int		zfcp_scsi_eh_host_reset_handler (Scsi_Cmnd*);
-+void            zfcp_scsi_select_queue_depth(struct Scsi_Host*, Scsi_Device*);
-+
-+static inline int zfcp_sg_list_alloc(zfcp_sg_list_t*, size_t);
-+static inline int zfcp_sg_list_free(zfcp_sg_list_t*);
-+static inline int zfcp_sg_list_copy_from_user(zfcp_sg_list_t*, void*, size_t);
-+static inline int zfcp_sg_list_copy_to_user(void*, zfcp_sg_list_t*, size_t);
-+
-+/* prototypes for functions written against the FSF interface */
-+static int zfcp_fsf_req_send(zfcp_fsf_req_t*, struct timer_list*);
-+static int zfcp_fsf_req_create(zfcp_adapter_t *, u32, int, zfcp_mem_pool_t *,
-+                               unsigned long *, zfcp_fsf_req_t **);
-+static int zfcp_fsf_req_free(zfcp_fsf_req_t*);
-+static int	zfcp_fsf_exchange_config_data (zfcp_erp_action_t*);
-+static int	zfcp_fsf_open_port (zfcp_erp_action_t*);
-+static int	zfcp_fsf_close_port (zfcp_erp_action_t*);
-+static int	zfcp_fsf_open_unit (zfcp_erp_action_t*);
-+static int	zfcp_fsf_close_unit (zfcp_erp_action_t*);
-+static int	zfcp_fsf_close_physical_port (zfcp_erp_action_t*);
-+static int	zfcp_fsf_send_fcp_command_task (zfcp_unit_t*, Scsi_Cmnd *);
-+static zfcp_fsf_req_t*	zfcp_fsf_send_fcp_command_task_management
-+			(zfcp_adapter_t*, zfcp_unit_t*, u8, int);
-+static zfcp_fsf_req_t*	zfcp_fsf_abort_fcp_command
-+			(unsigned long, zfcp_adapter_t*, zfcp_unit_t*, int);
-+static void zfcp_fsf_start_scsi_er_timer(zfcp_adapter_t*);
-+static void zfcp_fsf_scsi_er_timeout_handler(unsigned long);
-+static int	zfcp_fsf_status_read (zfcp_adapter_t*, int);
-+static int	zfcp_fsf_control_file(
-+	zfcp_adapter_t*, zfcp_fsf_req_t**, u32, u32, zfcp_sg_list_t*);
-+
-+static int	zfcp_fsf_exchange_config_data_handler
-+			(zfcp_fsf_req_t*);
-+static int	zfcp_fsf_exchange_port_data_handler (zfcp_fsf_req_t*);
-+static int	zfcp_fsf_open_port_handler (zfcp_fsf_req_t*);
-+static int      zfcp_fsf_close_port_handler (zfcp_fsf_req_t*);
-+static int      zfcp_fsf_close_physical_port_handler (zfcp_fsf_req_t*);
-+static int	zfcp_fsf_open_unit_handler (zfcp_fsf_req_t*);
-+static int      zfcp_fsf_close_unit_handler (zfcp_fsf_req_t*);
-+static int	zfcp_fsf_send_fcp_command_handler (zfcp_fsf_req_t*);
-+static int	zfcp_fsf_send_fcp_command_task_handler
-+			(zfcp_fsf_req_t*);
-+static int	zfcp_fsf_send_fcp_command_task_management_handler
-+			(zfcp_fsf_req_t*);
-+static int	zfcp_fsf_abort_fcp_command_handler (zfcp_fsf_req_t*);
-+static int	zfcp_fsf_send_ct_handler (zfcp_fsf_req_t*);
-+static int      zfcp_fsf_status_read_handler (zfcp_fsf_req_t*);
-+void		zfcp_fsf_incoming_els (zfcp_fsf_req_t *); 
-+static int	zfcp_fsf_control_file_handler(zfcp_fsf_req_t*);
-+
-+static inline int
-+		zfcp_fsf_req_create_sbal_check
-+			(unsigned long*, zfcp_qdio_queue_t*, int);
-+
-+static int	zfcp_fsf_send_els_handler(zfcp_fsf_req_t *);
-+
-+static inline int
-+		zfcp_mem_pool_element_alloc (zfcp_mem_pool_t*, int);
-+static inline int
-+		zfcp_mem_pool_element_free (zfcp_mem_pool_t*, int);
-+static inline void*
-+		zfcp_mem_pool_element_get (zfcp_mem_pool_t*, int);
-+static inline int
-+		zfcp_mem_pool_element_put (zfcp_mem_pool_t*, int);
-+
-+static inline int
-+		zfcp_mem_pool_create (zfcp_mem_pool_t*, int, int,
-+                                      void (*function)(unsigned long),
-+                                      unsigned long);
-+static inline int
-+		zfcp_mem_pool_destroy (zfcp_mem_pool_t*);
-+static inline void*
-+		zfcp_mem_pool_find (zfcp_mem_pool_t*);
-+static inline int
-+		zfcp_mem_pool_return (void*, zfcp_mem_pool_t*);
-+
-+
-+
-+static void	zfcp_scsi_low_mem_buffer_timeout_handler
-+			(unsigned long);
-+
-+static zfcp_fsf_req_t* zfcp_fsf_req_alloc(zfcp_mem_pool_t *, int, int);
-+static int	zfcp_fsf_req_cleanup (zfcp_fsf_req_t*);
-+static int	zfcp_fsf_req_wait_and_cleanup
-+			(zfcp_fsf_req_t*, int, u32*);
-+static int	zfcp_fsf_req_complete (zfcp_fsf_req_t*);
-+static int	zfcp_fsf_protstatus_eval (zfcp_fsf_req_t*);
-+static int	zfcp_fsf_fsfstatus_eval (zfcp_fsf_req_t*);
-+static int      zfcp_fsf_fsfstatus_qual_eval(zfcp_fsf_req_t *);
-+static int 	zfcp_fsf_req_dispatch (zfcp_fsf_req_t*);
-+static int	zfcp_fsf_req_dismiss (zfcp_fsf_req_t*);
-+static int	zfcp_fsf_req_dismiss_all (zfcp_adapter_t*);
-+
-+/* prototypes for FCP related functions */
-+static int	zfcp_nameserver_enqueue (zfcp_adapter_t*);
-+static int zfcp_ns_gid_pn_request(zfcp_erp_action_t*);
-+static void zfcp_ns_gid_pn_handler(unsigned long);
-+static void zfcp_ns_ga_nxt_handler(unsigned long);
-+
-+/* prototypes for functions written against the QDIO layer interface */
-+qdio_handler_t	zfcp_qdio_request_handler;
-+qdio_handler_t	zfcp_qdio_response_handler;
-+
-+int             zfcp_qdio_handler_error_check
-+			(zfcp_adapter_t*, unsigned int, unsigned int,
-+			 unsigned int);
-+
-+/* prototypes for functions written against the Dynamic I/O layer interface */
-+static int	zfcp_dio_oper_handler (int, devreg_t *);
-+static void	zfcp_dio_not_oper_handler (int, int);
-+
-+/* prototypes for functions written against the Common I/O layer interface */
-+static void	zfcp_cio_handler (int, void *, struct pt_regs *);
-+
-+/* prototypes for other functions */
-+static int	zfcp_task_management_function (zfcp_unit_t*, u8);
-+
-+static void	zfcp_config_parse_error(
-+			unsigned char *, unsigned char *, const char *, ...)
-+			__attribute__ ((format (printf, 3, 4)));
-+static int	zfcp_config_parse_record(
-+			unsigned char*, int, zfcp_config_record_t*);
-+static int	zfcp_config_parse_record_list (unsigned char*, int, int);
-+//static int	zfcp_config_parse_record_del (zfcp_config_record_t*);
-+static int	zfcp_config_cleanup (void);
-+
-+static int      zfcp_adapter_enqueue (devno_t, zfcp_adapter_t**);
-+static int      zfcp_port_enqueue (zfcp_adapter_t*, scsi_id_t, wwn_t, u32, 
-+                                   zfcp_port_t**);
-+static int      zfcp_unit_enqueue (zfcp_port_t*, scsi_lun_t, fcp_lun_t,
-+                                   zfcp_unit_t**);
-+
-+static int	zfcp_adapter_dequeue (zfcp_adapter_t*);
-+static int	zfcp_port_dequeue (zfcp_port_t*);
-+static int	zfcp_unit_dequeue (zfcp_unit_t*);
-+
-+static int	zfcp_adapter_detect(zfcp_adapter_t*);
-+static int	zfcp_adapter_irq_register(zfcp_adapter_t*);
-+static int	zfcp_adapter_irq_unregister(zfcp_adapter_t*);
-+static int	zfcp_adapter_scsi_register(zfcp_adapter_t*);
-+static int	zfcp_adapter_scsi_register_all (void);
-+static int	zfcp_adapter_shutdown_all (void);
-+
-+static u32	zfcp_derive_driver_version (void);
-+
-+static inline int
-+		zfcp_qdio_reqid_check(zfcp_adapter_t*, void*);
-+
-+static inline void zfcp_qdio_sbal_limit(zfcp_fsf_req_t*, int);
-+static inline volatile qdio_buffer_element_t*
-+	zfcp_qdio_sbale_get
-+		(zfcp_qdio_queue_t*, int, int);
-+static inline volatile qdio_buffer_element_t*
-+	zfcp_qdio_sbale_req
-+		(zfcp_fsf_req_t*, int, int);
-+static inline volatile qdio_buffer_element_t*
-+	zfcp_qdio_sbale_resp
-+		(zfcp_fsf_req_t*, int, int);
-+static inline volatile qdio_buffer_element_t*
-+	zfcp_qdio_sbale_curr
-+		(zfcp_fsf_req_t*);
-+static inline volatile qdio_buffer_element_t*
-+	zfcp_qdio_sbal_chain
-+		(zfcp_fsf_req_t*, unsigned long);
-+static inline volatile qdio_buffer_element_t*
-+	zfcp_qdio_sbale_next
-+		(zfcp_fsf_req_t*, unsigned long);
-+static inline int
-+	zfcp_qdio_sbals_zero
-+		(zfcp_qdio_queue_t*, int, int);
-+static inline int
-+	zfcp_qdio_sbals_wipe
-+		(zfcp_fsf_req_t*);
-+static inline void
-+	zfcp_qdio_sbale_fill
-+		(zfcp_fsf_req_t*, unsigned long, void*, int);
-+static inline int
-+	zfcp_qdio_sbals_from_segment
-+		(zfcp_fsf_req_t*, unsigned long, void*, unsigned long);
-+static inline int zfcp_qdio_sbals_from_buffer(zfcp_fsf_req_t *, unsigned long,
-+                                              void *, unsigned long, int);
-+static inline int zfcp_qdio_sbals_from_sg(zfcp_fsf_req_t*, unsigned long,
-+                                          struct scatterlist*, int, int);
-+static inline int
-+	zfcp_qdio_sbals_from_scsicmnd
-+		(zfcp_fsf_req_t*, unsigned long, struct scsi_cmnd*);
-+static inline void
-+		zfcp_zero_sbals(qdio_buffer_t**, int, int);
-+
-+static zfcp_unit_t*
-+		zfcp_unit_lookup (zfcp_adapter_t*, int, int, int);
-+
-+/* prototypes for functions faking callbacks of lower layers */
-+inline void     zfcp_scsi_process_and_clear_fake_queue(unsigned long);
-+inline void     zfcp_scsi_insert_into_fake_queue(zfcp_adapter_t *,
-+                                                 Scsi_Cmnd *);
-+void		zfcp_fake_outbound_callback (unsigned long);
-+void		zfcp_fake_inbound_callback (unsigned long);
-+
-+/* prototypes for proc-file interfacing stuff */
-+unsigned long zfcp_find_forward(char**, 
-+                                unsigned long*,
-+                                char**,
-+                                unsigned long*);
-+unsigned long zfcp_find_backward(char**, 
-+                                 unsigned long*,
-+                                 char**,
-+                                 unsigned long*);
-+int zfcp_create_root_proc(void);
-+int zfcp_delete_root_proc(void);
-+int zfcp_create_data_procs(void);
-+int zfcp_delete_data_procs(void);
-+int zfcp_proc_map_open(struct inode*, struct file*);
-+int zfcp_proc_map_close(struct inode*, struct file*);
-+int zfcp_open_parm_proc(struct inode*, struct file*);
-+int zfcp_close_parm_proc(struct inode*, struct file*);
-+int zfcp_open_add_map_proc(struct inode*, struct file*);
-+int zfcp_close_add_map_proc(struct inode*, struct file*);
-+int zfcp_adapter_proc_open(struct inode*, struct file*);
-+int zfcp_adapter_proc_close(struct inode*, struct file*);
-+int zfcp_port_proc_open(struct inode*, struct file*);
-+int zfcp_port_proc_close(struct inode*, struct file*);
-+int zfcp_unit_proc_open(struct inode*, struct file*);
-+int zfcp_unit_proc_close(struct inode*, struct file*);
-+int zfcp_cfdc_dev_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
-+ssize_t zfcp_parm_proc_read(struct file*, 
-+                             char*, 
-+                             size_t, 
-+                             loff_t*);
-+ssize_t zfcp_parm_proc_write(struct file*, 
-+                             const char*, 
-+                             size_t, 
-+                             loff_t*);
-+ssize_t zfcp_add_map_proc_write(struct file*, 
-+                             const char*, 
-+                             size_t, 
-+                             loff_t*);
-+ssize_t zfcp_proc_map_read(struct file*, 
-+                             char*, 
-+                             size_t, 
-+                             loff_t*);
-+ssize_t zfcp_adapter_proc_read(struct file*,
-+                             char*,
-+                             size_t,
-+                             loff_t*);
-+ssize_t zfcp_adapter_proc_write(struct file*,
-+                             const char*,
-+                             size_t,
-+                             loff_t*);
-+ssize_t zfcp_port_proc_read(struct file*,
-+                             char*,
-+                             size_t,
-+                             loff_t*);
-+ssize_t zfcp_port_proc_write(struct file*,
-+                             const char*,
-+                             size_t,
-+                             loff_t*);
-+ssize_t zfcp_unit_proc_read(struct file*,
-+                             char*,
-+                             size_t,
-+                             loff_t*);
-+ssize_t zfcp_unit_proc_write(struct file*,
-+                             const char*,
-+                             size_t,
-+                             loff_t*);
-+int zfcp_create_adapter_proc(zfcp_adapter_t*);
-+int zfcp_delete_adapter_proc(zfcp_adapter_t*);
-+int zfcp_create_port_proc(zfcp_port_t*);
-+int zfcp_delete_port_proc(zfcp_port_t*);
-+int zfcp_create_unit_proc(zfcp_unit_t*);
-+int zfcp_delete_unit_proc(zfcp_unit_t*);
-+
-+/* prototypes for initialisation functions */
-+static int zfcp_dio_register (void); 
-+
-+/* prototypes for extended link services functions */
-+static int zfcp_els(zfcp_port_t*, u8);
-+static void zfcp_els_handler(unsigned long);
-+static int zfcp_test_link(zfcp_port_t*);
-+
-+/* prototypes for error recovery functions */
-+static int zfcp_erp_adapter_reopen (zfcp_adapter_t*, int);
-+static int zfcp_erp_port_forced_reopen (zfcp_port_t*, int);
-+static int zfcp_erp_port_reopen (zfcp_port_t*, int);
-+static int zfcp_erp_unit_reopen (zfcp_unit_t*, int);
-+
-+static int zfcp_erp_adapter_reopen_internal (zfcp_adapter_t*, int);
-+static int zfcp_erp_port_forced_reopen_internal (zfcp_port_t*, int);
-+static int zfcp_erp_port_reopen_internal (zfcp_port_t*, int);
-+static int zfcp_erp_unit_reopen_internal (zfcp_unit_t*, int);
-+
-+static int zfcp_erp_port_reopen_all (zfcp_adapter_t*, int);
-+static int zfcp_erp_port_reopen_all_internal (zfcp_adapter_t*, int);
-+static int zfcp_erp_unit_reopen_all_internal (zfcp_port_t*, int);
-+
-+static inline int zfcp_erp_adapter_shutdown (zfcp_adapter_t*, int);
-+static inline int zfcp_erp_port_shutdown (zfcp_port_t*, int);
-+static inline int zfcp_erp_port_shutdown_all (zfcp_adapter_t*, int);
-+static inline int zfcp_erp_unit_shutdown (zfcp_unit_t*, int);
-+
-+static int zfcp_erp_adapter_block (zfcp_adapter_t*, int);
-+static int zfcp_erp_adapter_unblock (zfcp_adapter_t*);
-+static int zfcp_erp_port_block (zfcp_port_t*, int);
-+static int zfcp_erp_port_unblock (zfcp_port_t*);
-+static int zfcp_erp_unit_block (zfcp_unit_t*, int);
-+static int zfcp_erp_unit_unblock (zfcp_unit_t*);
-+
-+static int  zfcp_erp_thread (void*);
-+static int  zfcp_erp_thread_setup (zfcp_adapter_t*);
-+static void zfcp_erp_thread_setup_task (void*);
-+static int  zfcp_erp_thread_kill (zfcp_adapter_t*);
-+
-+static int zfcp_erp_strategy (zfcp_erp_action_t*);
-+
-+static int zfcp_erp_strategy_do_action (zfcp_erp_action_t*);
-+static int zfcp_erp_strategy_memwait (zfcp_erp_action_t*);
-+static int zfcp_erp_strategy_check_target (zfcp_erp_action_t*, int);
-+static int zfcp_erp_strategy_check_unit (zfcp_unit_t*, int);
-+static int zfcp_erp_strategy_check_port (zfcp_port_t*, int);
-+static int zfcp_erp_strategy_check_adapter (zfcp_adapter_t*, int);
-+static int zfcp_erp_strategy_statechange
-+		(int, u32, zfcp_adapter_t*, zfcp_port_t*, zfcp_unit_t*, int);
-+static inline int zfcp_erp_strategy_statechange_detected (atomic_t*, u32);
-+static int zfcp_erp_strategy_followup_actions
-+		(int, zfcp_adapter_t*, zfcp_port_t*, zfcp_unit_t*, int);
-+static int zfcp_erp_strategy_check_queues (zfcp_adapter_t*);
-+static int zfcp_erp_strategy_check_action(zfcp_erp_action_t *, int);
-+
-+static int zfcp_erp_adapter_strategy (zfcp_erp_action_t*);
-+static int zfcp_erp_adapter_strategy_generic (zfcp_erp_action_t*, int);
-+static int zfcp_erp_adapter_strategy_close (zfcp_erp_action_t*);
-+static int zfcp_erp_adapter_strategy_close_irq (zfcp_erp_action_t*);
-+static int zfcp_erp_adapter_strategy_close_qdio (zfcp_erp_action_t*);
-+static int zfcp_erp_adapter_strategy_close_fsf (zfcp_erp_action_t*);
-+static int zfcp_erp_adapter_strategy_open (zfcp_erp_action_t*);
-+static int zfcp_erp_adapter_strategy_open_irq (zfcp_erp_action_t*);
-+static int zfcp_erp_adapter_strategy_open_qdio (zfcp_erp_action_t*);
-+static int zfcp_erp_adapter_strategy_open_fsf (zfcp_erp_action_t*);
-+static int zfcp_erp_adapter_strategy_open_fsf_xconfig (zfcp_erp_action_t*);
-+static int zfcp_erp_adapter_strategy_open_fsf_statusread (zfcp_erp_action_t*);
-+
-+static int zfcp_erp_port_forced_strategy (zfcp_erp_action_t*);
-+static int zfcp_erp_port_forced_strategy_close (zfcp_erp_action_t*);
-+
-+static int zfcp_erp_port_strategy (zfcp_erp_action_t*);
-+static int zfcp_erp_port_strategy_clearstati (zfcp_port_t*);
-+static int zfcp_erp_port_strategy_close (zfcp_erp_action_t*);
-+static int zfcp_erp_port_strategy_open (zfcp_erp_action_t*);
-+static int zfcp_erp_port_strategy_open_nameserver (zfcp_erp_action_t*);
-+static int zfcp_erp_port_strategy_open_nameserver_wakeup (zfcp_erp_action_t*);
-+static int zfcp_erp_port_strategy_open_common (zfcp_erp_action_t*);
-+static int zfcp_erp_port_strategy_open_common_lookup (zfcp_erp_action_t*);
-+static int zfcp_erp_port_strategy_open_port (zfcp_erp_action_t*);
-+
-+static int zfcp_erp_unit_strategy (zfcp_erp_action_t*);
-+static int zfcp_erp_unit_strategy_clearstati (zfcp_unit_t*);
-+static int zfcp_erp_unit_strategy_close (zfcp_erp_action_t*);
-+static int zfcp_erp_unit_strategy_open (zfcp_erp_action_t*);
-+
-+static void zfcp_erp_modify_adapter_status(zfcp_adapter_t*, u32, int);
-+static void zfcp_erp_modify_port_status(zfcp_port_t*, u32, int);
-+static void zfcp_erp_modify_unit_status(zfcp_unit_t*, u32, int);
-+static void zfcp_erp_adapter_failed(zfcp_adapter_t*);
-+static void zfcp_erp_port_failed(zfcp_port_t*);
-+static void zfcp_erp_unit_failed(zfcp_unit_t*);
-+
-+static int zfcp_erp_action_dismiss_adapter (zfcp_adapter_t*);
-+static int zfcp_erp_action_dismiss_port(zfcp_port_t*);
-+/* zfcp_erp_action_dismiss_unit not needed */
-+static int zfcp_erp_action_dismiss (zfcp_erp_action_t*);
-+
-+static int zfcp_erp_action_enqueue
-+	(int, zfcp_adapter_t*, zfcp_port_t*, zfcp_unit_t*);
-+static int zfcp_erp_action_dequeue (zfcp_erp_action_t*);
-+
-+static int zfcp_erp_action_ready (zfcp_erp_action_t*);
-+static int zfcp_erp_action_exists (zfcp_erp_action_t*);
-+
-+static inline void zfcp_erp_action_to_ready (zfcp_erp_action_t*);
-+static inline void zfcp_erp_action_to_running (zfcp_erp_action_t*);
-+static inline void zfcp_erp_from_one_to_other
-+	(struct list_head*, struct list_head*);
-+
-+static int  zfcp_erp_async_handler (zfcp_erp_action_t*, unsigned long);
-+static void zfcp_erp_memwait_handler (unsigned long);
-+static void zfcp_erp_timeout_handler (unsigned long);
-+static int zfcp_erp_timeout_init (zfcp_erp_action_t*);
-+
-+int zfcp_erp_wait (zfcp_adapter_t*);
-+
-+
-+#ifdef ZFCP_STAT_REQSIZES
-+static int zfcp_statistics_clear
-+	(zfcp_adapter_t*, struct list_head*);
-+static int zfcp_statistics_print
-+	(zfcp_adapter_t*, struct list_head*, char*, char*, int, int);
-+static void zfcp_statistics_inc
-+	(zfcp_adapter_t*, struct list_head*, u32);
-+static inline void zfcp_statistics_new
-+	(zfcp_adapter_t*, struct list_head*, u32);
-+static inline void zfcp_statistics_sort
-+	(struct list_head*, struct list_head*, zfcp_statistics_t*);
-+#endif
-+
-+
-+/* driver data */
-+static struct file_operations zfcp_parm_fops =
-+{
-+     open: zfcp_open_parm_proc, 
-+     read: zfcp_parm_proc_read,
-+     write: zfcp_parm_proc_write, 
-+     release: zfcp_close_parm_proc, 
-+};
-+
-+static struct file_operations zfcp_map_fops =
-+{
-+     open:	zfcp_proc_map_open,
-+     read:	zfcp_proc_map_read,
-+     release:	zfcp_proc_map_close,
-+};
-+
-+static struct file_operations zfcp_add_map_fops =
-+{
-+     open: zfcp_open_add_map_proc,
-+     write: zfcp_add_map_proc_write,
-+     release: zfcp_close_add_map_proc,
-+};
-+
-+static struct file_operations zfcp_adapter_fops =
-+{
-+     open: zfcp_adapter_proc_open,
-+     read: zfcp_adapter_proc_read,
-+     write: zfcp_adapter_proc_write,
-+     release: zfcp_adapter_proc_close,
-+};
-+
-+static struct file_operations zfcp_port_fops =
-+{
-+     open: zfcp_port_proc_open,
-+     read: zfcp_port_proc_read,
-+     write: zfcp_port_proc_write,
-+     release: zfcp_port_proc_close,
-+};
-+
-+static struct file_operations zfcp_unit_fops =
-+{
-+     open: zfcp_unit_proc_open,
-+     read: zfcp_unit_proc_read,
-+     write: zfcp_unit_proc_write,
-+     release: zfcp_unit_proc_close,
-+};
-+
-+static struct file_operations zfcp_cfdc_fops =
-+{
-+	ioctl: zfcp_cfdc_dev_ioctl
-+};
-+
-+static struct miscdevice zfcp_cfdc_misc = {
-+	minor: ZFCP_CFDC_DEV_MINOR,
-+	name: ZFCP_CFDC_DEV_NAME,
-+	fops: &zfcp_cfdc_fops
-+};
-+
-+zfcp_data_t zfcp_data = {
-+	{	/* Scsi Host Template */
-+                name:                   ZFCP_NAME,
-+                proc_name:              "dummy",
-+                proc_info:              NULL, /* we don't need scsi proc info */
-+		detect:			zfcp_scsi_detect,
-+		revoke:			zfcp_scsi_revoke,
-+		release:		zfcp_scsi_release,
-+		queuecommand:		zfcp_scsi_queuecommand,
-+		eh_abort_handler:	zfcp_scsi_eh_abort_handler,
-+		eh_device_reset_handler:zfcp_scsi_eh_device_reset_handler,
-+		eh_bus_reset_handler:	zfcp_scsi_eh_bus_reset_handler,
-+		eh_host_reset_handler:	zfcp_scsi_eh_host_reset_handler,
-+		/* FIXME(openfcp): Tune */
-+		can_queue:	   	4096,
-+		this_id:		0,
-+		/*
-+		 * FIXME:
-+		 * one less? can zfcp_create_sbale cope with it?
-+		 */
-+                sg_tablesize:	        ZFCP_MAX_SBALES_PER_REQ,
-+		/* some moderate value for the moment */
-+		cmd_per_lun:		ZFCP_CMND_PER_LUN,
-+		/* no requirement on the addresses of data buffers */
-+		unchecked_isa_dma:	0,
-+		/* maybe try it later */
-+		use_clustering:		1,
-+		/* we are straight forward */
-+		use_new_eh_code:	1
-+	}
-+	/* rest initialized with zeros */
-+};
-+
-+
-+inline fcp_dl_t *zfcp_get_fcp_dl_ptr(fcp_cmnd_iu_t *fcp_cmd)
-+{
-+	int additional_length = fcp_cmd->add_fcp_cdb_length << 2;
-+	fcp_dl_t *fcp_dl_addr=
-+			(fcp_dl_t *)(
-+				(unsigned char*)fcp_cmd +				
-+                                sizeof(fcp_cmnd_iu_t) +
-+				additional_length);    
-+	/*
-+	 * fcp_dl_addr = start address of fcp_cmnd structure + 
-+	 * size of fixed part + size of dynamically sized add_dcp_cdb field
-+	 * SEE FCP-2 documentation
-+	 */
-+	return fcp_dl_addr;
-+}
-+
-+
-+inline fcp_dl_t zfcp_get_fcp_dl(fcp_cmnd_iu_t *fcp_cmd)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+     
-+	ZFCP_LOG_TRACE("enter (fcp_cmd=0x%lx)\n",
-+                       (unsigned long) fcp_cmd);
-+	ZFCP_LOG_TRACE("exit 0x%lx\n",
-+                       (unsigned long)*zfcp_get_fcp_dl_ptr(fcp_cmd));
-+	return *zfcp_get_fcp_dl_ptr(fcp_cmd);
-+#undef ZFCP_LOG_AREA       
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+inline void zfcp_set_fcp_dl(fcp_cmnd_iu_t *fcp_cmd, fcp_dl_t fcp_dl)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+     ZFCP_LOG_TRACE("enter (fcp_cmd=0x%lx), (fcp_dl=0x%x)\n",
-+                    (unsigned long)fcp_cmd,
-+                    fcp_dl);
-+     *zfcp_get_fcp_dl_ptr(fcp_cmd)=fcp_dl;
-+     ZFCP_LOG_TRACE("exit\n");
-+#undef ZFCP_LOG_AREA       
-+#undef ZFCP_LOG_AREA_PREFIX
-+}  
-+
-+
-+#ifdef MODULE
-+/* declare driver module init/cleanup functions */
-+module_init(zfcp_module_init);
-+module_exit(zfcp_module_exit);
-+
-+MODULE_AUTHOR(
-+	"Martin Peschke <mpeschke at de.ibm.com>, "
-+        "Raimund Schroeder <raimund.schroeder at de.ibm.com>, "
-+	"Aron Zeh <arzeh at de.ibm.com>, "
-+	"IBM Deutschland Entwicklung GmbH");
-+/* what this driver module is about */
-+MODULE_DESCRIPTION(
-+	"FCP (SCSI over Fibre Channel) HBA driver for IBM eServer zSeries, " ZFCP_REVISION);
-+MODULE_LICENSE("GPL");
-+/* log level may be provided as a module parameter */
-+MODULE_PARM(loglevel, "i");
-+/* short explaination of the previous module parameter */
-+MODULE_PARM_DESC(loglevel,
-+	"log levels, 8 nibbles: "
-+	"(unassigned) ERP QDIO DIO Config FSF SCSI Other, "
-+	"levels: 0=none 1=normal 2=devel 3=trace");
-+#endif /* MODULE */
-+
-+#ifdef ZFCP_PRINT_FLAGS
-+static u32 flags_dump=0;
-+MODULE_PARM(flags_dump, "i");
-+#define ZFCP_LOG_FLAGS(ll, m...) \
-+		if (ll<=flags_dump) \
-+			_ZFCP_LOG(m)
-+#else
-+#define ZFCP_LOG_FLAGS(ll, m...)
-+#endif
-+
-+static char *map = NULL;
-+#ifdef MODULE
-+MODULE_PARM(map, "s");
-+MODULE_PARM_DESC(map,
-+	"Initial FC to SCSI mapping table");
-+
-+/* enable/disable paranoia (extra checks to ensure driver consistency) */
-+MODULE_PARM(zfcp_paranoia, "b");
-+/* short explaination of the previous module parameter */
-+MODULE_PARM_DESC(zfcp_paranoia,
-+	"extra checks to ensure driver consistency, "
-+	"0=disabled other !0=enabled");
-+#else
-+
-+/* zfcp_map boot parameter */
-+static int __init zfcp_map_setup(char *str)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	/* don't parse trailing " */
-+	map = str + 1;		
-+	/* don't parse final " */
-+	map[strlen(map) - 1] = ZFCP_PARSE_SPACE_CHARS[0];
-+	ZFCP_LOG_INFO("map is %s\n", map);
-+	return 1;	/* why just 1? */
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+__setup("zfcp_map=", zfcp_map_setup);
-+
-+/* zfcp_loglevel boot_parameter */
-+static int __init zfcp_loglevel_setup(char *str)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	loglevel = simple_strtoul(str, NULL, 0);
-+	//ZFCP_LOG_NORMAL("loglevel is 0x%x\n", loglevel);
-+	return 1;	/* why just 1? */
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+__setup("zfcp_loglevel=", zfcp_loglevel_setup);
-+
-+#endif /* MODULE */
-+
-+#ifdef ZFCP_CAUSE_ERRORS
-+void zfcp_force_error(unsigned long data)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
-+        
-+        zfcp_adapter_t *adapter;
-+
-+        ZFCP_LOG_NORMAL("Cause error....\n");
-+        adapter = ZFCP_FIRST_ADAPTER;
-+        printk("adater reopen\n");
-+        zfcp_erp_adapter_reopen(adapter, 0);
-+        printk("adater close\n");
-+	zfcp_erp_adapter_shutdown(adapter, 0);
-+        /*
-+	zfcp_force_error_timer.function = zfcp_force_error;
-+        zfcp_force_error_timer.data = 0;
-+        zfcp_force_error_timer.expires = jiffies + 60*HZ;
-+	add_timer(&zfcp_force_error_timer);
-+        */
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+#endif //ZFCP_CAUSE_ERRORS
-+
-+
-+static inline int zfcp_fsf_req_is_scsi_cmnd(zfcp_fsf_req_t *fsf_req)
-+{
-+	return ((fsf_req->fsf_command == FSF_QTCB_FCP_CMND) &&
-+		!(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT));
-+}
-+
-+
-+static inline void zfcp_cmd_dbf_event_fsf(
-+		const char *text,
-+		zfcp_fsf_req_t *fsf_req,
-+		void *add_data,
-+		int add_length)
-+{
-+#ifdef ZFCP_DEBUG_COMMANDS
-+	zfcp_adapter_t *adapter = fsf_req->adapter;
-+	Scsi_Cmnd *scsi_cmnd;
-+	int level = 3;
-+	int i;
-+	unsigned long flags;
-+
-+	write_lock_irqsave(&adapter->cmd_dbf_lock, flags);
-+	if (zfcp_fsf_req_is_scsi_cmnd(fsf_req)) {
-+		scsi_cmnd = fsf_req->data.send_fcp_command_task.scsi_cmnd;
-+		debug_text_event(adapter->cmd_dbf, level, "fsferror");
-+		debug_text_event(adapter->cmd_dbf, level, text);
-+		debug_event(adapter->cmd_dbf, level, &fsf_req, sizeof(unsigned long));
-+		debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no, sizeof(u32));
-+		debug_event(adapter->cmd_dbf, level, &scsi_cmnd, sizeof(unsigned long));
-+		debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd,
-+			    min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len));
-+		for (i = 0; i < add_length; i += ZFCP_CMD_DBF_LENGTH)
-+			debug_event(
-+				adapter->cmd_dbf,
-+				level,
-+				(char*)add_data + i,
-+				min(ZFCP_CMD_DBF_LENGTH, add_length - i));
-+	}
-+	write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags);
-+#endif
-+}
-+
-+
-+static inline void zfcp_cmd_dbf_event_scsi(
-+		const char *text,
-+		zfcp_adapter_t *adapter,
-+		Scsi_Cmnd *scsi_cmnd)
-+{
-+#ifdef ZFCP_DEBUG_COMMANDS
-+	zfcp_fsf_req_t *fsf_req = (zfcp_fsf_req_t*) scsi_cmnd->host_scribble;
-+	int level = ((host_byte(scsi_cmnd->result) != 0) ? 1 : 5);
-+	unsigned long flags;
-+
-+	write_lock_irqsave(&adapter->cmd_dbf_lock, flags);	
-+	debug_text_event(adapter->cmd_dbf, level, "hostbyte");
-+	debug_text_event(adapter->cmd_dbf, level, text);
-+	debug_event(adapter->cmd_dbf, level, &scsi_cmnd->result, sizeof(u32));
-+	debug_event(adapter->cmd_dbf, level, &scsi_cmnd, sizeof(unsigned long));
-+	debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd,
-+		    min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len));
-+	if (fsf_req) {
-+		debug_event(adapter->cmd_dbf, level, &fsf_req, sizeof(unsigned long));
-+		debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no, sizeof(u32));
-+	} else	{
-+		debug_text_event(adapter->cmd_dbf, level, "");
-+		debug_text_event(adapter->cmd_dbf, level, "");
-+	}
-+	write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags);
-+#endif
-+}
-+
-+
-+static inline void zfcp_in_els_dbf_event(
-+		zfcp_adapter_t *adapter,
-+		const char *text,
-+		fsf_status_read_buffer_t *status_buffer,
-+		int length)
-+{
-+#ifdef ZFCP_DEBUG_INCOMING_ELS
-+	int level = 1;
-+	int i;
-+
-+	debug_text_event(adapter->in_els_dbf, level, text);
-+	debug_event(adapter->in_els_dbf, level, &status_buffer->d_id, 8);
-+	for (i = 0; i < length; i += ZFCP_IN_ELS_DBF_LENGTH)
-+		debug_event(
-+			adapter->in_els_dbf,
-+			level,
-+			(char*)status_buffer->payload + i,
-+			min(ZFCP_IN_ELS_DBF_LENGTH, length - i));
-+#endif
-+}
-+
-+
-+/****************************************************************/
-+
-+
-+/*
-+ * function:	zfcp_module_init
-+ *
-+ * purpose:	driver module initialization routine
-+ *
-+ * locks:       initialises zfcp_data.proc_sema, zfcp_data.adapter_list_lock
-+ *              zfcp_data.proc_sema is taken and released within this 
-+ *              function
-+ *
-+ * returns:	0	success
-+ *		!0	failure
-+ */
-+static int __init zfcp_module_init(void)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+	int retval = 0;
-+
-+	atomic_set(&zfcp_data.loglevel, loglevel);
-+
-+#ifdef ZFCP_LOW_MEM_CREDITS
-+	atomic_set(&zfcp_data.lowmem_credit, 0);
-+#endif
-+	
-+	ZFCP_LOG_DEBUG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); 
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        ZFCP_LOG_TRACE(
-+		"Start Address of module: 0x%lx\n",
-+		(unsigned long) &zfcp_module_init);
-+
-+	/* derive driver version number from cvs revision string */
-+	zfcp_data.driver_version = zfcp_derive_driver_version();
-+	ZFCP_LOG_NORMAL(
-+		"driver version 0x%x\n",
-+		zfcp_data.driver_version);
-+
-+	/* initialize adapter list */
-+	rwlock_init(&zfcp_data.adapter_list_lock);
-+	INIT_LIST_HEAD(&zfcp_data.adapter_list_head);
-+
-+        /* initialize lock for callback handling */
-+        rwlock_init(&zfcp_callback.lock);
-+
-+	/* initialize map list */
-+	INIT_LIST_HEAD(&zfcp_data.map_list_head);
-+
-+#ifdef CONFIG_S390_SUPPORT
-+	retval = register_ioctl32_conversion(
-+		zfcp_cfdc_ioc.cmd, zfcp_cfdc_ioc.handler);
-+	if (retval != 0) {
-+		ZFCP_LOG_INFO(
-+			"Cannot register a 32-bit support of the IOC handler\n");
-+		goto failed_ioctl32;
-+	}
-+#endif
-+	retval = misc_register(&zfcp_cfdc_misc);
-+	if (retval != 0) {
-+		ZFCP_LOG_INFO(
-+			"Special file of the control file data channel "
-+			"cannot be registered\n");
-+		goto failed_misc_register;
-+	} else {
-+		ZFCP_LOG_INFO(
-+			"Special file of the control file data channel "
-+			"has become MAJOR/MINOR numbers %d/%d\n",
-+			ZFCP_CFDC_DEV_MAJOR,
-+			zfcp_cfdc_misc.minor);
-+	}
-+
-+        /* Initialise proc semaphores */
-+        sema_init(&zfcp_data.proc_sema,1);
-+	down(&zfcp_data.proc_sema); /* config changes protected by proc_sema */
-+
-+#ifdef CONFIG_PROC_FS
-+	retval = zfcp_create_root_proc();
-+	if (retval) {
-+		ZFCP_LOG_NORMAL(
-+			"Error: Proc fs startup failed\n");
-+		goto failed_root_proc;
-+	}
-+
-+	retval = zfcp_create_data_procs();
-+	if (retval) {
-+                ZFCP_LOG_NORMAL(
-+                        "Error: Proc fs startup failed\n");
-+		goto failed_data_procs;
-+        }
-+#endif
-+
-+	/* always succeeds for now */
-+	/* FIXME: set priority? */
-+	zfcp_data.reboot_notifier.notifier_call = zfcp_reboot_handler;
-+	register_reboot_notifier(&zfcp_data.reboot_notifier);
-+
-+	/*
-+	 * parse module parameter string for valid configurations and create
-+	 * entries for configured adapters, remote ports and logical units
-+	 */
-+	if (map) {
-+		retval = zfcp_config_parse_record_list(
-+				map,
-+				strlen(map),
-+				ZFCP_PARSE_ADD);
-+
-+		if (retval < 0)
-+			goto failed_parse;	/* some entries may have been created */
-+	}
-+
-+	/* save address of data structure managing the driver module */
-+	zfcp_data.scsi_host_template.module = THIS_MODULE;
-+
-+	/*
-+	 * register driver module with SCSI stack
-+	 * we do this last to avoid the need to revert this step
-+	 * if other init stuff goes wrong
-+	 * (scsi_unregister_module() does not work here!)
-+	 */
-+        retval = scsi_register_module(
-+			MODULE_SCSI_HA,
-+			&zfcp_data.scsi_host_template);
-+	if (retval) {
-+		ZFCP_LOG_NORMAL(
-+			"error: Registration of the driver module "
-+			"with the Linux SCSI stack failed.\n");
-+		goto failed_scsi;
-+	}
-+
-+        /* setup dynamic I/O */
-+        retval = zfcp_dio_register();
-+        if (retval) {
-+                ZFCP_LOG_NORMAL(
-+                        "warning: Dynamic attach/detach facilities for "
-+                        "the adapter(s) could not be started. \n");
-+		retval = 0;
-+        }
-+
-+	up(&zfcp_data.proc_sema); /* release procfs */
-+
-+#ifdef ZFCP_CAUSE_ERRORS
-+	init_timer(&zfcp_force_error_timer);
-+	zfcp_force_error_timer.function = zfcp_force_error;
-+        zfcp_force_error_timer.data = 0;
-+        zfcp_force_error_timer.expires = jiffies + 60*HZ;
-+	add_timer(&zfcp_force_error_timer);
-+#endif
-+
-+	/* we did it, skip all cleanups related to failures */
-+        goto out;
-+
-+ failed_scsi:
-+ failed_parse:
-+	/* FIXME: might there be a race between module unload and shutdown? */
-+	unregister_reboot_notifier(&zfcp_data.reboot_notifier);
-+	zfcp_adapter_shutdown_all();
-+	zfcp_config_cleanup();
-+	/*
-+	 * FIXME(design):
-+	 * We need a way to cancel all proc usage at this point.
-+	 * Just having a semaphore is not sufficient since this
-+	 * semaphore makes exploiters sleep in our proc code.
-+	 * If we wake them then we do not know when they actually
-+	 * left the proc path. They must left the proc path before
-+	 * we are allowed to delete proc entries. We need a kind of
-+	 * handshaking to ensure that all proc-users are really
-+	 * gone. Even if we have this then we can't ensure
-+	 * that another proc-user enters the proc-path before
-+	 * we delete proc-entries.
-+	 */
-+	zfcp_delete_data_procs();
-+
-+ failed_data_procs:
-+	zfcp_delete_root_proc();
-+        
-+ failed_root_proc:
-+	misc_deregister(&zfcp_cfdc_misc);
-+ failed_misc_register:
-+#ifdef CONFIG_S390_SUPPORT
-+	unregister_ioctl32_conversion(zfcp_cfdc_ioc.cmd);
-+ failed_ioctl32:
-+#endif
-+
-+	 ZFCP_LOG_NORMAL("error: Module could not be loaded.\n");
-+
-+ out:
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+	return retval;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+__initcall(zfcp_module_init);
-+
-+/*
-+ * function:	zfcp_module_exit
-+ *
-+ * purpose:	driver module cleanup routine
-+ *
-+ * locks:       zfcp_data.proc_sema is acquired prior to calling 
-+ *                zfcp_config_cleanup and released afterwards
-+ *
-+ * returns:	void
-+ */
-+static void __exit zfcp_module_exit(void)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+        int temp_ret=0;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	/* FIXME: might there be a race between module unload and shutdown? */
-+	unregister_reboot_notifier(&zfcp_data.reboot_notifier);
-+
-+	/* unregister driver module from SCSI stack */
-+	scsi_unregister_module(MODULE_SCSI_HA, &zfcp_data.scsi_host_template);
-+
-+	/* shutdown all adapters (incl. those not registered in SCSI stack) */
-+	zfcp_adapter_shutdown_all();
-+
-+	zfcp_delete_data_procs();
-+	/* unregister from Dynamic I/O */
-+	temp_ret = s390_device_unregister(&zfcp_data.devreg);
-+        ZFCP_LOG_TRACE(
-+		"s390_device_unregister returned %i\n", 
-+		temp_ret);
-+	temp_ret = s390_device_unregister(&zfcp_data.devreg_priv);
-+        ZFCP_LOG_TRACE(
-+		"s390_device_unregister returned %i (privileged subchannel)\n",
-+		temp_ret);
-+        ZFCP_LOG_TRACE("Before zfcp_config_cleanup\n");
-+
-+	/* free all resources dynamically allocated */
-+
-+	/* block proc access to config */
-+        down(&zfcp_data.proc_sema);
-+	temp_ret=zfcp_config_cleanup();
-+        up(&zfcp_data.proc_sema);
-+
-+        if (temp_ret) {
-+                ZFCP_LOG_NORMAL("bug: Could not free all memory "
-+                               "(debug info %d)\n",
-+                               temp_ret);
-+	}
-+
-+        zfcp_delete_root_proc();
-+
-+	misc_deregister(&zfcp_cfdc_misc);
-+#ifdef CONFIG_S390_SUPPORT
-+	unregister_ioctl32_conversion(zfcp_cfdc_ioc.cmd);
-+#endif
-+
-+#ifdef ZFCP_CAUSE_ERRORS
-+        del_timer(&zfcp_force_error_timer);
-+#endif	
-+
-+        ZFCP_LOG_TRACE("exit\n");
-+	ZFCP_LOG_DEBUG("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_reboot_handler
-+ *
-+ * purpose:	This function is called automatically by the kernel whenever
-+ *              a reboot or a shut-down is initiated and zfcp is still
-+ *              loaded
-+ *
-+ * locks:       zfcp_data.proc_sema is taken prior to shutting down the module
-+ *              and removing all structures
-+ *
-+ * returns:     NOTIFY_DONE in all cases
-+ */
-+int zfcp_reboot_handler(struct notifier_block *notifier, unsigned long code, void *ptr)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+	int retval = NOTIFY_DONE;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	/* block proc access to config (for rest of lifetime of this Linux) */
-+        down(&zfcp_data.proc_sema);
-+	zfcp_adapter_shutdown_all();
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_adapter_shutdown_all
-+ *
-+ * purpose:	recursively calls zfcp_erp_adapter_shutdown to stop all
-+ *              IO on each adapter, return all outstanding packets and 
-+ *              relinquish all IRQs
-+ *              Note: This function waits for completion of all shutdowns
-+ *
-+ * returns:     0 in all cases
-+ */
-+static int zfcp_adapter_shutdown_all(void)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+	int retval = 0;
-+	zfcp_adapter_t *adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	/*
-+	 * no adapter list lock since list won't change (proc is blocked),
-+	 * this allows sleeping while iterating the list
-+	 */
-+	ZFCP_FOR_EACH_ADAPTER(adapter)
-+		zfcp_erp_adapter_shutdown(adapter, 0);
-+	/* start all shutdowns first before any waiting to allow for concurreny */
-+	ZFCP_FOR_EACH_ADAPTER(adapter)
-+		zfcp_erp_wait(adapter);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_erp_port_shutdown_all
-+ *
-+ * purpose:	wrapper around zfcp_erp_port_reopen_all setting all the 
-+ *              required parameters to close a port
-+ *
-+ * returns:     0 in all cases
-+ */
-+static int zfcp_erp_port_shutdown_all(zfcp_adapter_t *adapter, int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n",
-+                       (unsigned long)adapter);
-+
-+        zfcp_erp_port_reopen_all(adapter, 
-+                                 ZFCP_STATUS_COMMON_RUNNING |
-+                                 ZFCP_STATUS_COMMON_ERP_FAILED |
-+                                 clear_mask);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+
-+/*
-+ * function:  zfcp_dio_register
-+ *
-+ * purpose:   Registers the FCP-adapter specific device number with the common
-+ *            io layer. All oper/not_oper calls will only be presented for 
-+ *            devices that match the below criteria.	
-+ *
-+ * returns:   0 on success
-+ *            -error code on failure
-+ */
-+static int zfcp_dio_register()
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_DIO
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_DIO
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	/* register handler for Dynamic I/O */
-+        zfcp_data.devreg.ci.hc.ctype = ZFCP_CONTROL_UNIT_TYPE;
-+        zfcp_data.devreg.ci.hc.cmode = ZFCP_CONTROL_UNIT_MODEL;
-+        zfcp_data.devreg.ci.hc.dtype = ZFCP_DEVICE_TYPE;
-+        zfcp_data.devreg.ci.hc.dmode = ZFCP_DEVICE_MODEL;
-+        zfcp_data.devreg.flag = DEVREG_TYPE_DEVCHARS | DEVREG_EXACT_MATCH;
-+        zfcp_data.devreg.oper_func = &zfcp_dio_oper_handler;
-+
-+	retval = s390_device_register(&zfcp_data.devreg);
-+        if (retval < 0) {
-+                ZFCP_LOG_NORMAL(
-+			"bug: The FSF device type could not "
-+			"be registered with the S/390 i/o layer "
-+			"(debug info %d)",
-+			retval);
-+        }
-+
-+        zfcp_data.devreg_priv.ci.hc.ctype = ZFCP_CONTROL_UNIT_TYPE;
-+        zfcp_data.devreg_priv.ci.hc.cmode = ZFCP_CONTROL_UNIT_MODEL;
-+        zfcp_data.devreg_priv.ci.hc.dtype = ZFCP_DEVICE_TYPE;
-+        zfcp_data.devreg_priv.ci.hc.dmode = ZFCP_DEVICE_MODEL_PRIV;
-+        zfcp_data.devreg_priv.flag = DEVREG_TYPE_DEVCHARS | DEVREG_EXACT_MATCH;
-+        zfcp_data.devreg_priv.oper_func = &zfcp_dio_oper_handler;
-+
-+	retval = s390_device_register(&zfcp_data.devreg_priv);
-+        if (retval < 0) {
-+                ZFCP_LOG_NORMAL(
-+			"bug: The FSF privileged device type could not "
-+			"be registered with the S/390 i/o layer "
-+			"(debug info %d)",
-+			retval);
-+         }
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_config_cleanup
-+ *
-+ * purpose:	must only be called after all adapters are properly shut down
-+ *              Frees all device structs (unit, port, adapter) and removes them 
-+ *              from the lists
-+ *
-+ * returns:	0 - 	no error occured during cleanup
-+ *		!0 - 	one or more errors occured during cleanup
-+ *			(retval is not guaranteed to be a valid -E*)
-+ *
-+ * context:	called on failure of module_init and from module_exit
-+ *
-+ * locks:       zfcp_data.proc_sema needs to be held on function entry
-+ *              adapter->port_list_lock,
-+ *                port->unit_list_lock are held when walking the 
-+ *                respective lists
-+ */
-+static int zfcp_config_cleanup(void)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_OTHER
-+ 
-+	int retval = 0;
-+ 	zfcp_adapter_t *adapter, *tmp_adapter;
-+	zfcp_port_t *port, *tmp_port;
-+	zfcp_unit_t *unit, *tmp_unit;
-+	unsigned long flags = 0;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        /* Note: no adapter_list_lock is needed as we have the proc_sema */ 
-+	ZFCP_FOR_NEXT_ADAPTER (adapter, tmp_adapter) {
-+		write_lock_irqsave(&adapter->port_list_lock, flags);
-+		ZFCP_FOR_NEXT_PORT (adapter, port, tmp_port) {
-+			write_lock(&port->unit_list_lock);
-+			ZFCP_FOR_NEXT_UNIT (port, unit, tmp_unit){ 
-+				retval |= zfcp_unit_dequeue(unit);
-+                        }
-+			write_unlock(&port->unit_list_lock);
-+			retval |= zfcp_port_dequeue(port);
-+ 		}
-+		write_unlock_irqrestore(&adapter->port_list_lock, flags);
-+		retval |= zfcp_adapter_dequeue(adapter);
-+	}
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_derive_driver_version
-+ *
-+ * purpose:	generates a 32 bit value from the cvs mantained revision,
-+ *
-+ * returns:	!0 - driver version
-+ *			format:	0 .. 7		8 .. 15		16 .. 31
-+ *				(reserved)	major	.	minor
-+ *		0 - if no version string could be assembled
-+ */
-+static u32 zfcp_derive_driver_version(void)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+	char *revision = ZFCP_REVISION;
-+	u32 version = 0;
-+	char *d;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	/* major */
-+	for (d = revision; !isdigit(d[0]); d++) {}
-+	version |= (simple_strtoul(d, &d, 10) & 0xFF) << 16; 
-+
-+	/* dot */
-+	if (d[0] != '.') {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Revision number generation from string "
-+                        "unsuccesfull. Setting revision number to 0 and "
-+                        "continuing (debug info %s).\n",
-+			revision);
-+		version = 0;
-+		goto out;
-+	}
-+	d++;
-+
-+	/* minor */
-+	version |= simple_strtoul(d, NULL, 10) & 0xFFFF;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (0x%x)\n", version);
-+
-+	return version;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_buffers_enqueue
-+ *
-+ * purpose:	allocates BUFFER memory to each of the pointers of
-+ *              the qdio_buffer_t array in the adapter struct
-+ *
-+ * returns:	number of buffers allocated
-+ *
-+ * comments:    cur_buf is the pointer array and count can be any
-+ *              number of required buffers, the page-fitting arithmetic is
-+ *              done entirely within this funciton
-+ *
-+ * locks:       must only be called with zfcp_data.proc_sema taken
-+ */
-+int zfcp_buffers_enqueue(qdio_buffer_t **cur_buf, int count)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+        int buf_pos;
-+        int qdio_buffers_per_page;
-+        int page_pos = 0;
-+        qdio_buffer_t *first_in_page = NULL;
-+        
-+	ZFCP_LOG_TRACE(
-+		"enter cur_buf 0x%lx\n",
-+                (unsigned long)cur_buf);
-+
-+        qdio_buffers_per_page = PAGE_SIZE / sizeof(qdio_buffer_t);
-+        ZFCP_LOG_TRACE(
-+		"Buffers per page %d.\n",
-+                qdio_buffers_per_page);
-+
-+        for (buf_pos = 0; buf_pos < count; buf_pos++) {
-+                if (page_pos == 0) {
-+                        cur_buf[buf_pos] = (qdio_buffer_t*) ZFCP_GET_ZEROED_PAGE(GFP_KERNEL);
-+                        if (cur_buf[buf_pos] == NULL) {
-+                                ZFCP_LOG_INFO(
-+					"error: Could not allocate "
-+                                        "memory for qdio transfer structures.\n");
-+                                goto out;
-+                        }
-+                        first_in_page = cur_buf[buf_pos];
-+                } else {
-+                        cur_buf[buf_pos] = first_in_page + page_pos;
-+
-+                }
-+                /* was initialised to zero */
-+                page_pos++;
-+                page_pos %= qdio_buffers_per_page;
-+        } // for (buf_pos = 0; buf_pos < count; buf_pos++)
-+ out:
-+	ZFCP_LOG_TRACE("exit (%d)\n", buf_pos);
-+        return buf_pos; 
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_buffers_dequeue
-+ *
-+ * purpose:	frees BUFFER memory for each of the pointers of
-+ *              the qdio_buffer_t array in the adapter struct
-+ *
-+ * returns:	sod all
-+ *
-+ * comments:    cur_buf is the pointer array and count can be any
-+ *              number of buffers in the array that should be freed
-+ *              starting from buffer 0
-+ *
-+ * locks:       must only be called with zfcp_data.proc_sema taken
-+ */
-+void zfcp_buffers_dequeue(qdio_buffer_t **cur_buf, int count)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+        int buf_pos;
-+        int qdio_buffers_per_page;
-+
-+	ZFCP_LOG_TRACE("enter cur_buf 0x%lx count %d\n",
-+                       (unsigned long)cur_buf,
-+                       count);
-+        
-+        qdio_buffers_per_page = PAGE_SIZE / sizeof(qdio_buffer_t);
-+        ZFCP_LOG_TRACE(
-+		"Buffers per page %d.\n",
-+                qdio_buffers_per_page);
-+
-+        for (buf_pos = 0; buf_pos < count; buf_pos += qdio_buffers_per_page) {
-+                ZFCP_FREE_PAGE((unsigned long)cur_buf[buf_pos]);
-+        }
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_allocate_qdio_queues
-+ *
-+ * purpose:	wrapper around zfcp_buffers_enqueue with possible calls
-+ *              to zfcp_buffers_dequeue in the error case. Deals with
-+ *              request and response queues
-+ *
-+ * returns:	0 on success
-+ *              -EIO if allocation of buffers failed
-+ *               (all buffers are guarranteed to be un-allocated in this case)
-+ *
-+ * comments:    called only from adapter_enqueue
-+ *
-+ * locks:       must only be called with zfcp_data.proc_sema taken
-+ */
-+int zfcp_allocate_qdio_queues(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+        int buffer_count;
-+        int retval=0;
-+
-+        ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n",
-+                       (unsigned long)adapter);
-+
-+        buffer_count = zfcp_buffers_enqueue(
-+                            &(adapter->request_queue.buffer[0]),
-+                            QDIO_MAX_BUFFERS_PER_Q);
-+        if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) {
-+                ZFCP_LOG_DEBUG("error: Out of memory allocating "
-+                               "request queue, only %d buffers got. "
-+                               "Binning them.\n",
-+                                buffer_count);
-+                zfcp_buffers_dequeue(
-+                     &(adapter->request_queue.buffer[0]),
-+                     buffer_count);
-+                retval = -ENOMEM;
-+                goto out;
-+        }
-+
-+        buffer_count = zfcp_buffers_enqueue(
-+                            &(adapter->response_queue.buffer[0]),
-+                            QDIO_MAX_BUFFERS_PER_Q);
-+        if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) {
-+                ZFCP_LOG_DEBUG("error: Out of memory allocating "
-+                               "response queue, only %d buffers got. "
-+                               "Binning them.\n",
-+                               buffer_count);
-+                zfcp_buffers_dequeue(
-+                     &(adapter->response_queue.buffer[0]),
-+                     buffer_count);
-+                ZFCP_LOG_TRACE("Deallocating request_queue Buffers.\n");
-+                zfcp_buffers_dequeue(
-+                     &(adapter->request_queue.buffer[0]),
-+                     QDIO_MAX_BUFFERS_PER_Q);
-+                retval = -ENOMEM;
-+                goto out;
-+        }
-+ out:
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_free_qdio_queues
-+ *
-+ * purpose:	wrapper around zfcp_buffers_dequeue for request and response
-+ *              queues
-+ *
-+ * returns:	sod all
-+ *
-+ * comments:    called only from adapter_dequeue
-+ *
-+ * locks:       must only be called with zfcp_data.proc_sema taken
-+ */
-+void zfcp_free_qdio_queues(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n", 
-+                       (unsigned long)adapter);
-+        
-+        ZFCP_LOG_TRACE("Deallocating request_queue Buffers.\n");
-+        zfcp_buffers_dequeue(
-+             &(adapter->request_queue.buffer[0]),
-+             QDIO_MAX_BUFFERS_PER_Q);
-+
-+        ZFCP_LOG_TRACE("Deallocating response_queue Buffers.\n");
-+        zfcp_buffers_dequeue(
-+             &(adapter->response_queue.buffer[0]),
-+             QDIO_MAX_BUFFERS_PER_Q);
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_free_low_mem_buffers
-+ *
-+ * purpose:	frees all static memory in the pools previously allocated by
-+ *              zfcp_allocate_low_mem buffers
-+ *
-+ * returns:     sod all
-+ *
-+ * locks:       must only be called with zfcp_data.proc_sema taken
-+ */
-+static void zfcp_free_low_mem_buffers(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	ZFCP_LOG_TRACE("enter (adapter 0x%lx)\n",
-+                       (unsigned long)adapter);
-+
-+	zfcp_mem_pool_destroy(&adapter->pool.fsf_req_status_read);
-+	zfcp_mem_pool_destroy(&adapter->pool.data_status_read);
-+	zfcp_mem_pool_destroy(&adapter->pool.data_gid_pn);
-+	zfcp_mem_pool_destroy(&adapter->pool.fsf_req_erp);
-+	zfcp_mem_pool_destroy(&adapter->pool.fsf_req_scsi);
-+        
-+        ZFCP_LOG_TRACE("exit\n");
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_allocate_low_mem_buffers
-+ *
-+ * purpose:	The pivot for the static memory buffer pool generation.
-+ *              Called only from zfcp_adapter_enqueue in order to allocate
-+ *              a combined QTCB/fsf_req buffer for erp actions and fcp/SCSI
-+ *              commands.
-+ *              It also genrates fcp-nameserver request/response buffer pairs
-+ *              and unsolicited status read fsf_req buffers by means of 
-+ *              function calls to the apropriate handlers.
-+ *
-+ * returns:     0 on success
-+ *              -ENOMEM on failure (some buffers might be allocated)
-+ *
-+ * locks:       must only be called with zfcp_data.proc_sema taken
-+ */
-+static int zfcp_allocate_low_mem_buffers(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter (adapter 0x%lx)\n",
-+                       (unsigned long)adapter);
-+
-+	retval = zfcp_mem_pool_create(&adapter->pool.fsf_req_erp, 1,
-+                                      sizeof(struct zfcp_fsf_req_pool_buffer),
-+                                      0, 0);
-+	if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: FCP command buffer pool allocation failed\n");
-+		goto out;
-+	}
-+
-+	retval = zfcp_mem_pool_create(&adapter->pool.data_gid_pn, 1,
-+                                      sizeof(struct zfcp_gid_pn_data), 0, 0);
-+
-+	if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: Nameserver buffer pool allocation failed\n");
-+		goto out;
-+	}
-+
-+	retval = zfcp_mem_pool_create(&adapter->pool.fsf_req_status_read,
-+                                      ZFCP_STATUS_READS_RECOM,
-+                                      sizeof(zfcp_fsf_req_t), 0, 0);
-+	if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: Status read request pool allocation failed\n");
-+		goto out;
-+	}
-+
-+	retval = zfcp_mem_pool_create(&adapter->pool.data_status_read,
-+                                      ZFCP_STATUS_READS_RECOM,
-+                                      sizeof(fsf_status_read_buffer_t), 0, 0);
-+	if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: Status read buffer pool allocation failed\n");
-+		goto out;
-+	}
-+
-+	retval = zfcp_mem_pool_create(&adapter->pool.fsf_req_scsi,
-+                                      1, sizeof(struct zfcp_fsf_req_pool_buffer),
-+                                      zfcp_scsi_low_mem_buffer_timeout_handler,
-+                                      (unsigned long) adapter);
-+	if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: FCP command buffer pool allocation failed\n");
-+		goto out;
-+	}
-+
-+out:
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_adapter_enqueue
-+ *
-+ * purpose:	enqueues an adapter at the end of the adapter list
-+ *		in the driver data
-+ *              all adapter internal structures are set up
-+ *              proc-fs entries are also created
-+ *
-+ * returns:	0 if a new adapter was successfully enqueued
-+ *              ZFCP_KNOWN if an adapter with this devno was already present
-+ *		-ENOMEM if alloc failed
-+ *	   
-+ * locks:	proc_sema must be held to serialise chnages to the adapter list
-+ *              zfcp_data.adapter_list_lock is taken and released several times
-+ *              within the function (must not be held on entry)
-+ */
-+static int zfcp_adapter_enqueue(devno_t devno, zfcp_adapter_t **adapter_p)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	int retval = 0;
-+        zfcp_adapter_t *adapter;
-+	unsigned long flags;
-+        char dbf_name[20];
-+        
-+        ZFCP_LOG_TRACE(
-+		"enter (devno=0x%04x ,adapter_p=0x%lx)\n", 
-+		devno,
-+		(unsigned long)adapter_p);
-+
-+	read_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
-+	ZFCP_FOR_EACH_ADAPTER(adapter) {
-+		if (adapter->devno == devno) {
-+			ZFCP_LOG_TRACE(
-+				"Adapter with devno 0x%04x "
-+				"already exists.\n",
-+				 devno);
-+                        retval = ZFCP_KNOWN;
-+                        break;
-+		}
-+        }
-+        read_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
-+        if (retval == ZFCP_KNOWN)
-+                goto known_adapter;
-+
-+        /*
-+	 * Note: It is safe to release the list_lock, as any list changes 
-+         * are protected by the proc_sema, which must be held to get here
-+         */
-+
-+	/* try to allocate new adapter data structure (zeroed) */
-+	adapter = ZFCP_KMALLOC(sizeof(zfcp_adapter_t), GFP_KERNEL);
-+	if (!adapter) {
-+		ZFCP_LOG_INFO(
-+			"error: Allocation of base adapter "
-+			"structure failed\n");
-+                retval = -ENOMEM;
-+                goto adapter_alloc_failed;
-+	}
-+
-+	retval = zfcp_allocate_qdio_queues(adapter);        
-+        if (retval)
-+                goto queues_alloc_failed;
-+ 
-+        retval = zfcp_allocate_low_mem_buffers(adapter);
-+        if (retval)
-+                goto failed_low_mem_buffers;
-+
-+        /* initialise list of ports */
-+        rwlock_init(&adapter->port_list_lock);
-+        INIT_LIST_HEAD(&adapter->port_list_head);
-+        
-+        /* initialize list of fsf requests */
-+        rwlock_init(&adapter->fsf_req_list_lock);
-+	INIT_LIST_HEAD(&adapter->fsf_req_list_head);
-+        
-+        /* initialize abort lock */
-+        rwlock_init(&adapter->abort_lock);
-+
-+        /* initialise scsi faking structures */
-+        rwlock_init(&adapter->fake_list_lock);
-+        init_timer(&adapter->fake_scsi_timer);
-+
-+	/* initialise some erp stuff */
-+        init_waitqueue_head(&adapter->erp_thread_wqh);
-+        init_waitqueue_head(&adapter->erp_done_wqh);
-+        
-+        /* initialize lock of associated request queue */
-+        rwlock_init(&adapter->request_queue.queue_lock);
-+
-+        /* intitialise SCSI ER timer */
-+	init_timer(&adapter->scsi_er_timer);
-+
-+        /* save devno */
-+        adapter->devno = devno;
-+        
-+        /* set FC service class used per default */
-+        adapter->fc_service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT; 
-+
-+#ifdef ZFCP_DEBUG_REQUESTS
-+	/* debug feature area which records fsf request sequence numbers */
-+	sprintf(dbf_name, ZFCP_REQ_DBF_NAME"0x%04x",adapter->devno);
-+	adapter->req_dbf = debug_register(
-+				dbf_name,
-+				ZFCP_REQ_DBF_INDEX,
-+				ZFCP_REQ_DBF_AREAS,
-+				ZFCP_REQ_DBF_LENGTH);
-+	if (!adapter->req_dbf) {
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. Request debug feature for "
-+			"adapter with devno 0x%04x could not be generated.\n",
-+			adapter->devno);
-+		retval = -ENOMEM;
-+		goto failed_req_dbf;
-+	}
-+	debug_register_view(adapter->req_dbf, &debug_hex_ascii_view);
-+	debug_set_level(adapter->req_dbf, ZFCP_REQ_DBF_LEVEL);
-+	debug_text_event(adapter->req_dbf, 1, "zzz");
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+
-+#ifdef ZFCP_DEBUG_COMMANDS
-+	/* debug feature area which records SCSI command failures (hostbyte) */
-+	rwlock_init(&adapter->cmd_dbf_lock);
-+	sprintf(dbf_name, ZFCP_CMD_DBF_NAME"0x%04x", adapter->devno);
-+	adapter->cmd_dbf = debug_register(
-+				dbf_name, 
-+				ZFCP_CMD_DBF_INDEX,
-+				ZFCP_CMD_DBF_AREAS,
-+				ZFCP_CMD_DBF_LENGTH);
-+	if (!adapter->cmd_dbf) {
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. Command debug feature for "
-+			"adapter with devno 0x%04x could not be generated.\n",
-+			adapter->devno);
-+		retval = -ENOMEM;
-+		goto failed_cmd_dbf;
-+	}
-+	debug_register_view(adapter->cmd_dbf, &debug_hex_ascii_view);
-+	debug_set_level(adapter->cmd_dbf, ZFCP_CMD_DBF_LEVEL);
-+#endif /* ZFCP_DEBUG_COMMANDS */
-+
-+#ifdef ZFCP_DEBUG_ABORTS
-+	/* debug feature area which records SCSI command aborts */
-+	sprintf(dbf_name, ZFCP_ABORT_DBF_NAME"0x%04x", adapter->devno);
-+	adapter->abort_dbf = debug_register(
-+				dbf_name, 
-+				ZFCP_ABORT_DBF_INDEX,
-+				ZFCP_ABORT_DBF_AREAS,
-+				ZFCP_ABORT_DBF_LENGTH);
-+	if (!adapter->abort_dbf) {
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. Abort debug feature for "
-+			"adapter with devno 0x%04x could not be generated.\n",
-+			adapter->devno);
-+		retval = -ENOMEM;
-+		goto failed_abort_dbf;
-+	}
-+	debug_register_view(adapter->abort_dbf, &debug_hex_ascii_view);
-+	debug_set_level(adapter->abort_dbf, ZFCP_ABORT_DBF_LEVEL);
-+#endif /* ZFCP_DEBUG_ABORTS */
-+
-+#ifdef ZFCP_DEBUG_INCOMING_ELS
-+	/* debug feature area which records SCSI command aborts */
-+	sprintf(dbf_name, ZFCP_IN_ELS_DBF_NAME"0x%04x", adapter->devno);
-+	adapter->in_els_dbf = debug_register(
-+				dbf_name,
-+				ZFCP_IN_ELS_DBF_INDEX,
-+				ZFCP_IN_ELS_DBF_AREAS,
-+				ZFCP_IN_ELS_DBF_LENGTH);
-+	if (!adapter->in_els_dbf) {
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. ELS debug feature for "
-+			"adapter with devno 0x%04x could not be generated.\n",
-+			adapter->devno);
-+		retval = -ENOMEM;
-+		goto failed_in_els_dbf;
-+	}
-+	debug_register_view(adapter->in_els_dbf, &debug_hex_ascii_view);
-+	debug_set_level(adapter->in_els_dbf, ZFCP_IN_ELS_DBF_LEVEL);
-+#endif /* ZFCP_DEBUG_INCOMING_ELS */
-+
-+	sprintf(dbf_name, ZFCP_ERP_DBF_NAME"0x%04x", adapter->devno);
-+	adapter->erp_dbf = debug_register(
-+				dbf_name, 
-+				ZFCP_ERP_DBF_INDEX, 
-+				ZFCP_ERP_DBF_AREAS,
-+				ZFCP_ERP_DBF_LENGTH);
-+	if (!adapter->erp_dbf) { 
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. ERP debug feature for "
-+			"adapter with devno 0x%04x could not be generated.\n",
-+			adapter->devno);
-+		retval = -ENOMEM;
-+		goto failed_erp_dbf;
-+	}
-+	debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view);
-+	debug_set_level(adapter->erp_dbf, ZFCP_ERP_DBF_LEVEL);
-+
-+        /* Init proc structures */
-+#ifdef CONFIG_PROC_FS
-+        ZFCP_LOG_TRACE("Generating proc entry....\n");
-+	retval = zfcp_create_adapter_proc(adapter);
-+        if (retval) {
-+                ZFCP_LOG_INFO(
-+			"error: Out of resources. "
-+			"proc-file entries for adapter with "
-+			"devno 0x%04x could not be generated\n",
-+			adapter->devno);
-+                goto proc_failed;
-+        }
-+        ZFCP_LOG_TRACE("Proc entry created.\n");
-+#endif
-+
-+	retval = zfcp_erp_thread_setup(adapter);
-+        if (retval) {
-+                ZFCP_LOG_INFO(
-+			"error: out of resources. "
-+			"error recovery thread for the adapter with "
-+			"devno 0x%04x could not be started\n",
-+			adapter->devno);
-+                goto thread_failed;
-+        }
-+
-+#ifndef ZFCP_PARANOIA_DEAD_CODE
-+        /* set magics */
-+        adapter->common_magic = ZFCP_MAGIC;
-+        adapter->specific_magic = ZFCP_MAGIC_ADAPTER;
-+#endif
-+
-+#ifdef ZFCP_STAT_REQSIZES
-+	rwlock_init(&adapter->stat_lock);
-+	atomic_set(&adapter->stat_on, 0);
-+	atomic_set(&adapter->stat_errors, 0);
-+	INIT_LIST_HEAD(&adapter->read_req_head);
-+	INIT_LIST_HEAD(&adapter->write_req_head);
-+#endif
-+
-+        /* put allocated adapter at list tail */
-+	write_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
-+        list_add_tail(&adapter->list, &zfcp_data.adapter_list_head);
-+	zfcp_data.adapters++;
-+	write_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
-+
-+	sprintf(adapter->name, "0x%04x", adapter->devno);
-+	ASCEBC(adapter->name, strlen(adapter->name));
-+
-+	atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &adapter->status);
-+
-+	ZFCP_LOG_TRACE(
-+		"adapter allocated at 0x%lx, %i adapters in list\n",
-+		(unsigned long)adapter,
-+		zfcp_data.adapters);
-+        goto out;
-+
-+thread_failed:
-+	zfcp_delete_adapter_proc(adapter);
-+
-+proc_failed:
-+	debug_unregister(adapter->erp_dbf);
-+
-+failed_erp_dbf:
-+#ifdef ZFCP_DEBUG_INCOMING_ELS
-+	debug_unregister(adapter->in_els_dbf);
-+failed_in_els_dbf:
-+#endif
-+
-+#ifdef ZFCP_DEBUG_ABORTS
-+	debug_unregister(adapter->abort_dbf);
-+failed_abort_dbf:
-+#endif
-+
-+#ifdef ZFCP_DEBUG_COMMANDS
-+	debug_unregister(adapter->cmd_dbf);
-+failed_cmd_dbf:
-+#endif
-+
-+#ifdef ZFCP_DEBUG_REQUESTS
-+        debug_unregister(adapter->req_dbf);
-+failed_req_dbf:
-+#endif
-+
-+failed_low_mem_buffers:
-+        zfcp_free_low_mem_buffers(adapter);
-+        zfcp_free_qdio_queues(adapter);
-+
-+queues_alloc_failed:
-+        ZFCP_LOG_TRACE(
-+		"freeing adapter struct 0x%lx\n",
-+		(unsigned long) adapter);
-+	/* 'typeof' works as well */
-+        ZFCP_KFREE(adapter, sizeof(typeof(adapter)));
-+
-+adapter_alloc_failed:
-+	adapter = NULL;
-+
-+known_adapter:
-+out:
-+	*adapter_p = adapter;
-+        ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+        
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_adapter_dequeue
-+ *
-+ * purpose:	dequeues the specified adapter from the list in the driver data
-+ *
-+ * returns:	0 - zfcp_adapter_t data structure successfully removed
-+ *		!0 - zfcp_adapter_t data structure could not be removed
-+ *			(e.g. still used)
-+ *
-+ * locks:	adapter list write lock is assumed to be held by caller
-+ *              adapter->fsf_req_list_lock is taken and released within this 
-+ *              function and must not be held on entry
-+ */
-+static int zfcp_adapter_dequeue(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	int retval = 0;
-+	unsigned long flags;
-+
-+	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n", (unsigned long)adapter);
-+
-+	/*
-+	 * sanity check:
-+	 * I/O interrupt should be disabled, leave if not
-+	 */
-+
-+        /* Note: no adapter_list_lock is needed as we have the proc_sema */ 
-+
-+	/* sanity check: valid adapter data structure address */
-+	if (!adapter) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Pointer to an adapter struct is a null "
-+                        "pointer\n");
-+		retval = -EINVAL;
-+		goto out;
-+	}
-+
-+	/* sanity check: no remote ports pending */
-+	if (adapter->ports) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Adapter with devno 0x%04x is still in use, "
-+			"%i remote ports are still existing "
-+                        "(debug info 0x%lx)\n",
-+			adapter->devno, 
-+                        adapter->ports,
-+                        (unsigned long)adapter);
-+		retval = -EBUSY;
-+		goto out;
-+	}
-+
-+	/* sanity check: no pending FSF requests */
-+	read_lock_irqsave(&adapter->fsf_req_list_lock, flags);
-+
-+	retval = !list_empty(&adapter->fsf_req_list_head);
-+
-+	read_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
-+	if (retval) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Adapter with devno 0x%04x is still in use, "
-+			"%i requests are still outstanding "
-+                        "(debug info 0x%lx)\n",
-+			adapter->devno, 
-+                        atomic_read(&adapter->fsf_reqs_active),
-+                        (unsigned long)adapter);
-+		retval = -EBUSY;
-+		goto out;
-+	}
-+
-+	/* remove specified adapter data structure from list */
-+	list_del(&adapter->list);
-+
-+	/* decrease number of adapters in list */
-+	zfcp_data.adapters--;
-+
-+	ZFCP_LOG_TRACE(
-+		"adapter 0x%lx removed from list, "
-+		"%i adapters still in list\n",
-+		(unsigned long)adapter,
-+		zfcp_data.adapters);
-+
-+        retval = zfcp_erp_thread_kill(adapter);
-+
-+#ifdef ZFCP_STAT_REQSIZES
-+	zfcp_statistics_clear(adapter, &adapter->read_req_head);
-+	zfcp_statistics_clear(adapter, &adapter->write_req_head);
-+#endif
-+
-+        zfcp_delete_adapter_proc(adapter);
-+        ZFCP_LOG_TRACE("Proc entry removed.\n");
-+
-+        debug_unregister(adapter->erp_dbf);
-+
-+#ifdef ZFCP_DEBUG_REQUESTS
-+        debug_unregister(adapter->req_dbf);
-+#endif
-+
-+#ifdef ZFCP_DEBUG_COMMANDS
-+	debug_unregister(adapter->cmd_dbf);
-+#endif
-+
-+#ifdef ZFCP_DEBUG_ABORTS
-+	debug_unregister(adapter->abort_dbf);
-+#endif
-+
-+#ifdef ZFCP_DEBUG_INCOMING_ELS
-+	debug_unregister(adapter->in_els_dbf);
-+#endif
-+
-+
-+        zfcp_free_low_mem_buffers(adapter);
-+	/* free memory of adapter data structure and queues */
-+        zfcp_free_qdio_queues(adapter);
-+        ZFCP_LOG_TRACE("Freeing adapter structure.\n");
-+        ZFCP_KFREE(adapter, sizeof(zfcp_adapter_t));
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;	/* succeed */
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_port_enqueue
-+ *
-+ * purpose:	enqueues an remote port at the end of the port list
-+ *		associated with the specified adapter
-+ *              all port internal structures are set-up and the proc-fs
-+ *              entry is also allocated
-+ *              some SCSI-stack structures are modified for the port
-+ *
-+ * returns:	0 if a new port was successfully enqueued
-+ *              ZFCP_KNOWN if a port with the requested wwpn already exists
-+ *              -ENOMEM if allocation failed
-+ *              -EINVAL if at least one of the specified parameters was wrong
-+ *
-+ * locks:       proc_sema must be held to serialise changes to the port list
-+ *              adapter->port_list_lock is taken and released several times
-+ *              within this function (must not be held on entry)
-+ */
-+static int 
-+	zfcp_port_enqueue(
-+		zfcp_adapter_t *adapter,
-+		scsi_id_t scsi_id,
-+		wwn_t wwpn,
-+		u32 status,
-+                zfcp_port_t **port_p)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	zfcp_port_t *port = NULL;
-+	int check_scsi_id, check_wwpn;
-+        unsigned long flags;
-+        int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx scsi_id=%i wwpn=0x%Lx status=0x%x)\n",
-+		(unsigned long)adapter,
-+		scsi_id,
-+		(llui_t)wwpn,
-+		status);
-+
-+        /* to check that there is not a port with either this 
-+	 * SCSI ID or WWPN already in list
-+	 */
-+	check_scsi_id = !(status & ZFCP_STATUS_PORT_NO_SCSI_ID);
-+	check_wwpn = !(status & ZFCP_STATUS_PORT_NO_WWPN);
-+
-+	if (check_scsi_id && check_wwpn) {
-+		read_lock_irqsave(&adapter->port_list_lock, flags);
-+		ZFCP_FOR_EACH_PORT(adapter, port) {
-+			if ((port->scsi_id != scsi_id) && (port->wwpn != wwpn))
-+				continue;
-+			if ((port->scsi_id == scsi_id) && (port->wwpn == wwpn)) {
-+				ZFCP_LOG_TRACE(
-+					"Port with SCSI ID 0x%x and WWPN 0x%016Lx already in list\n",
-+					scsi_id, (llui_t)wwpn);
-+				retval = ZFCP_KNOWN;
-+				read_unlock_irqrestore(&adapter->port_list_lock, flags);
-+				goto known_port;
-+			}
-+			ZFCP_LOG_NORMAL(
-+				"user error: new mapping 0x%x:0x%016Lx "
-+				"does not match existing mapping 0x%x:0x%016Lx "
-+				"(adapter devno 0x%04x)\n",
-+				scsi_id,
-+				(llui_t)wwpn,
-+				port->scsi_id,
-+				(llui_t)port->wwpn,
-+				port->adapter->devno);
-+			retval = -EINVAL;
-+			read_unlock_irqrestore(&adapter->port_list_lock, flags);
-+			goto match_failed;
-+		}
-+		read_unlock_irqrestore(&adapter->port_list_lock, flags);
-+	}
-+
-+        /*
-+	 * Note: It is safe to release the list_lock, as any list changes 
-+         * are protected by the proc_sema, which must be held to get here
-+         */
-+
-+	/* try to allocate new port data structure (zeroed) */
-+	port = ZFCP_KMALLOC(sizeof(zfcp_port_t), GFP_KERNEL);
-+	if (!port) {
-+		ZFCP_LOG_INFO(
-+			"error: Allocation of port struct failed. "
-+			"Out of memory.\n");
-+                retval = -ENOMEM;
-+                goto port_alloc_failed;
-+        }
-+
-+        /* initialize unit list */
-+        rwlock_init(&port->unit_list_lock);
-+        INIT_LIST_HEAD(&port->unit_list_head);
-+
-+        /* save pointer to "parent" adapter */
-+        port->adapter = adapter;
-+
-+        /* save SCSI ID */
-+	if (check_scsi_id)
-+		port->scsi_id = scsi_id;
-+
-+        /* save WWPN */
-+	if (check_wwpn)
-+		port->wwpn = wwpn;
-+
-+        /* save initial status */
-+        atomic_set_mask(status, &port->status);
-+ 
-+#ifndef ZFCP_PARANOIA_DEAD_CODE
-+        /* set magics */
-+        port->common_magic = ZFCP_MAGIC;
-+        port->specific_magic = ZFCP_MAGIC_PORT;
-+#endif
-+
-+	/* Init proc structures */
-+#ifdef CONFIG_PROC_FS
-+        ZFCP_LOG_TRACE("Generating proc entry....\n");
-+	retval = zfcp_create_port_proc(port);
-+        if (retval)
-+                goto proc_failed;
-+        ZFCP_LOG_TRACE("Proc entry created.\n");
-+#endif
-+      
-+        if (check_scsi_id) {
-+	        /*
-+        	 * update max. SCSI ID of remote ports attached to
-+	         * "parent" adapter if necessary
-+        	 * (do not care about the adapters own SCSI ID)
-+	         */
-+		if (adapter->max_scsi_id < scsi_id) {
-+			adapter->max_scsi_id = scsi_id;
-+			ZFCP_LOG_TRACE(
-+				"max. SCSI ID of adapter 0x%lx now %i\n",
-+				(unsigned long)adapter,
-+				scsi_id);
-+		}
-+		/*
-+		 * update max. SCSI ID of remote ports attached to
-+		 * "parent" host (SCSI stack) if necessary
-+		 */
-+		if (adapter->scsi_host &&
-+		    (adapter->scsi_host->max_id < (scsi_id + 1))) {
-+			adapter->scsi_host->max_id = scsi_id + 1;
-+			ZFCP_LOG_TRACE(
-+				"max. SCSI ID of ports attached "
-+				"via host # %d now %i\n",
-+				adapter->scsi_host->host_no,
-+				adapter->scsi_host->max_id);
-+        	}
-+        }
-+
-+        /* Port is allocated, enqueue it*/
-+        write_lock_irqsave(&adapter->port_list_lock,flags);
-+        list_add_tail(&port->list, &adapter->port_list_head);
-+	adapter->ports++;
-+        write_unlock_irqrestore(&adapter->port_list_lock,flags);
-+        
-+	atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status);
-+
-+	/* ignore nameserver port */
-+	if (port->wwpn != 0)
-+		zfcp_callback_do_port_add(NULL, adapter, port);
-+
-+        ZFCP_LOG_TRACE(
-+		"port allocated at 0x%lx, %i ports in list "
-+		"of adapter 0x%lx\n",
-+		(unsigned long)port,
-+		adapter->ports,
-+		(unsigned long)adapter);
-+        goto out;
-+
-+proc_failed:
-+        ZFCP_KFREE(port, sizeof(zfcp_port_t));
-+        ZFCP_LOG_TRACE(
-+		"freeing port struct 0x%lx\n",
-+		(unsigned long) port);
-+
-+port_alloc_failed:
-+match_failed:
-+	port = NULL;
-+
-+known_port:
-+out:
-+	*port_p = port;
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_port_dequeue
-+ *
-+ * purpose:	dequeues the specified port from the list of the
-+ *		 "parent" adapter
-+ *
-+ * returns:	0 - zfcp_port_t data structure successfully removed
-+ *		!0 - zfcp_port_t data structure could not be removed
-+ *			(e.g. still used)
-+ *
-+ * locks :	port list write lock is assumed to be held by caller
-+ */
-+static int zfcp_port_dequeue(zfcp_port_t *port)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter (port=0x%lx)\n", (unsigned long)port);
-+
-+	/* sanity check: valid port data structure address (simple check) */
-+	if (!port) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Pointer to a port struct is a null "
-+                        "pointer\n");
-+		retval = -EINVAL;
-+		goto out;
-+	}
-+
-+	/*
-+	 * sanity check:
-+	 * leave if required list lock is not held,
-+	 * do not know whether it is held by the calling routine (required!)
-+	 * protecting this critical area or someone else (must not occur!),
-+	 * but a lock not held by anyone is definetely wrong
-+	 */
-+	if (!spin_is_locked(&port->adapter->port_list_lock)) {
-+		ZFCP_LOG_NORMAL("bug: Port list lock not held "
-+                               "(debug info 0x%lx)\n",
-+                               (unsigned long) port);
-+		retval = -EPERM;
-+		goto out;
-+	}
-+
-+	/* sanity check: no logical units pending */
-+	if (port->units) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Port with SCSI-id 0x%x is still in use, "
-+			"%i units (LUNs) are still existing "
-+                        "(debug info 0x%lx)\n",
-+			port->scsi_id, 
-+                        port->units,
-+                        (unsigned long)port);
-+		retval = -EBUSY;
-+		goto out;
-+	}
-+
-+	/* remove specified port data structure from list */
-+	list_del(&port->list);
-+
-+	/* decrease number of ports in list */
-+	port->adapter->ports--;
-+
-+	ZFCP_LOG_TRACE(
-+		"port 0x%lx removed from list of adapter 0x%lx, "
-+		"%i ports still in list\n",
-+		(unsigned long)port,
-+		(unsigned long)port->adapter,
-+		port->adapter->ports);
-+
-+	/* free memory of port data structure */
-+        ZFCP_LOG_TRACE("Deleting proc entry......\n");
-+        zfcp_delete_port_proc(port);
-+        ZFCP_LOG_TRACE("Proc entry removed.\n");
-+	ZFCP_KFREE(port, sizeof(zfcp_port_t));
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;	/* succeed */
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:     zfcp_scsi_low_mem_buffer_timeout_handler
-+ *
-+ * purpose:      This function needs to be called whenever the SCSI command
-+ *               in the low memory buffer does not return.
-+ *               Re-opening the adapter means that the command can be returned
-+ *               by zfcp (it is guarranteed that it does not return via the
-+ *               adapter anymore). The buffer can then be used again.
-+ *    
-+ * returns:      sod all
-+ */
-+static void zfcp_scsi_low_mem_buffer_timeout_handler(unsigned long data)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	zfcp_adapter_t *adapter = (zfcp_adapter_t *)data   ;
-+
-+	ZFCP_LOG_TRACE("enter (data=0x%lx)\n", 
-+                       (unsigned long) data);
-+        /*DEBUG*/
-+        ZFCP_LOG_INFO("*****************************mem_timeout******************************\n");
-+        zfcp_erp_adapter_reopen(adapter, 0);
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+	return;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_fsf_scsi_er_timeout_handler
-+ *
-+ * purpose:     This function needs to be called whenever a SCSI error recovery
-+ *              action (abort/reset) does not return.
-+ *              Re-opening the adapter means that the command can be returned
-+ *              by zfcp (it is guarranteed that it does not return via the
-+ *              adapter anymore). The buffer can then be used again.
-+ *    
-+ * returns:     sod all
-+ */
-+static void zfcp_fsf_scsi_er_timeout_handler(unsigned long data)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	zfcp_adapter_t *adapter = (zfcp_adapter_t *)data;
-+
-+	ZFCP_LOG_TRACE("enter (data=0x%lx)\n", 
-+                       (unsigned long) data);
-+        /*DEBUG*/
-+        ZFCP_LOG_INFO("*****************************er_timeout******************************\n");
-+        zfcp_erp_adapter_reopen(adapter, 0);
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+	return;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * memory pool implementation
-+ * the first four functions (element_alloc, element_release, element_get, element_put)
-+ * are for internal use,
-+ * the other four functions (create, destroy, find, free) are the external interface
-+ * which should be used by exploiter of the memory pool
-+ */
-+
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+/* associate a buffer with the specified memory pool element */
-+static inline int zfcp_mem_pool_element_alloc(
-+		zfcp_mem_pool_t *pool,
-+		int index)
-+{
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (pool=0x%lx, index=%i)\n",
-+		(unsigned long)pool,
-+		index);
-+
-+	pool->element[index].buffer = ZFCP_KMALLOC(pool->size, GFP_KERNEL);
-+	if (!pool->element[index].buffer) {
-+		retval = -ENOMEM;
-+        };
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+}
-+
-+
-+/* release the buffer associated with the specified memory pool element */
-+static inline int zfcp_mem_pool_element_free(
-+		zfcp_mem_pool_t *pool,
-+		int index)
-+{
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (pool=0x%lx, index=%i)\n",
-+		(unsigned long)pool,
-+		index);
-+
-+	if (atomic_read(&pool->element[index].use) != 0) {
-+		ZFCP_LOG_NORMAL("bug: memory pool is in use\n");
-+		retval = -EINVAL;
-+	} else if (pool->element[index].buffer)
-+		ZFCP_KFREE(pool->element[index].buffer, pool->size);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+}
-+
-+
-+/* try to get hold of buffer associated with the specified memory pool element */
-+static inline void *zfcp_mem_pool_element_get(
-+		zfcp_mem_pool_t *pool,
-+		int index)
-+{
-+	void *buffer;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (pool=0x%lx, index=%i)\n",
-+		(unsigned long)pool,
-+		index);
-+
-+	ZFCP_LOG_DEBUG("buffer=0x%lx\n",
-+                       (unsigned long)pool->element[index].buffer);
-+	ZFCP_HEX_DUMP(
-+		ZFCP_LOG_LEVEL_DEBUG,
-+		(char*)pool->element,
-+		pool->entries * sizeof(zfcp_mem_pool_element_t));
-+
-+	if (atomic_compare_and_swap(0, 1, &pool->element[index].use))
-+		buffer = NULL;
-+	else {
-+                memset(pool->element[index].buffer, 0, pool->size);
-+                buffer = pool->element[index].buffer;
-+        }
-+
-+
-+	ZFCP_LOG_TRACE("exit (0x%lx)\n", (unsigned long)buffer);
-+
-+	return buffer;
-+}
-+
-+
-+/* mark buffer associated with the specified memory pool element as available */
-+static inline int zfcp_mem_pool_element_put(
-+		zfcp_mem_pool_t *pool,
-+		int index)
-+{
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (pool=0x%lx, index=%i)\n",
-+		(unsigned long)pool,
-+		index);
-+
-+	if (atomic_compare_and_swap(1, 0, &pool->element[index].use)) {
-+		ZFCP_LOG_NORMAL("bug: memory pool is broken (element not in use)\n");
-+		retval = -EINVAL;
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+}
-+
-+
-+/*
-+ * creation of a new memory pool including setup of management data structures
-+ * as well as allocation of memory pool elements
-+ * (this routine does not cleanup partially set up pools, instead the corresponding
-+ * destroy routine should be called)
-+ */
-+static inline int zfcp_mem_pool_create(zfcp_mem_pool_t *pool,
-+                                       int entries, int size,
-+                                       void (*function) (unsigned long),
-+                                       unsigned long data)
-+{
-+	int retval = 0;
-+	int i;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (pool=0x%lx, entries=%i, size=%i)\n",
-+		(unsigned long)pool,
-+		entries,
-+		size);
-+
-+	if (pool->element || pool->entries) {
-+		ZFCP_LOG_NORMAL("bug: memory pool is broken (pool is in use)\n");
-+		retval = -EINVAL;
-+		goto out;
-+	}
-+
-+	pool->element = ZFCP_KMALLOC(entries * sizeof(zfcp_mem_pool_element_t),
-+                                     GFP_KERNEL);
-+	if (!pool->element) {
-+		ZFCP_LOG_NORMAL("warning: memory pool not avalaible\n");
-+		retval = -ENOMEM;
-+		goto out;
-+	}
-+        /* Ensure that the use flag is 0. */
-+
-+        memset(pool->element, 0, entries * sizeof(zfcp_mem_pool_element_t)); 
-+	pool->entries = entries;
-+	pool->size = size;
-+
-+	for (i = 0; i < entries; i++) {
-+		retval = zfcp_mem_pool_element_alloc(pool, i);
-+		if (retval) {
-+			ZFCP_LOG_NORMAL("warning: memory pool not avalaible\n");
-+                        retval = -ENOMEM;
-+			goto out;
-+		}
-+	}
-+	ZFCP_HEX_DUMP(
-+		ZFCP_LOG_LEVEL_DEBUG,
-+		(char*)pool->element,
-+		entries * sizeof(zfcp_mem_pool_element_t));
-+
-+	init_timer(&pool->timer);
-+	pool->timer.function = function;
-+	pool->timer.data = data;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+}
-+
-+
-+/*
-+ * give up memory pool with all its memory pool elements as well as
-+ * data structures used for management purposes
-+ * (this routine is able to handle partially alloacted memory pools)
-+ */
-+static inline int zfcp_mem_pool_destroy(
-+		zfcp_mem_pool_t *pool)
-+{
-+	int retval = 0;
-+	int i;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (pool=0x%lx)\n",
-+		(unsigned long)pool);
-+
-+	for (i = 0; i < pool->entries; i++)
-+		retval |= zfcp_mem_pool_element_free(pool, i);
-+
-+	if (pool->element)
-+		ZFCP_KFREE(pool->element, pool->entries);
-+
-+	pool->element = NULL;
-+	pool->entries = 0;
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+}
-+
-+
-+/*
-+ * try to find next available element in the specified memory pool,
-+ * on success get hold of buffer associated with the selected element
-+ */
-+static inline void* zfcp_mem_pool_find(
-+		zfcp_mem_pool_t *pool)
-+{
-+	void *buffer = NULL;
-+	int i;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (pool=0x%lx)\n",
-+		(unsigned long)pool);
-+
-+	for (i = 0; i < pool->entries; i++) {
-+		buffer = zfcp_mem_pool_element_get(pool, i);
-+		if (buffer)
-+			break;
-+	}
-+
-+        if ((buffer != 0) && (pool->timer.function != 0)) {
-+                /*
-+                 * watch low mem buffer
-+                 * Note: Take care if more than 1 timer is active.
-+                 * The first expired timer has to delete all other
-+                 * timers. (See ZFCP_SCSI_LOW_MEM_TIMEOUT and
-+                 * ZFCP_SCSI_ER_TIMEOUT)
-+                 */
-+                pool->timer.expires = jiffies + ZFCP_SCSI_LOW_MEM_TIMEOUT;
-+                add_timer(&pool->timer);
-+        }
-+
-+	ZFCP_LOG_TRACE("exit (0x%lx)\n", (unsigned long)buffer);
-+
-+	return buffer;
-+}
-+
-+
-+/*
-+ * make buffer available to memory pool again,
-+ * (since buffers are specified by their own address instead of the
-+ * memory pool element they are associated with a search for the
-+ * right element of the given memory pool)
-+ */ 
-+static inline int zfcp_mem_pool_return(void *buffer, zfcp_mem_pool_t *pool)
-+{
-+	int retval = 0;
-+	int i;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (buffer=0x%lx, pool=0x%lx)\n",
-+		(unsigned long)buffer,
-+		(unsigned long)pool);
-+
-+        if (pool->timer.function) {
-+                del_timer(&pool->timer);
-+        }
-+
-+	for (i = 0; i < pool->entries; i++) {
-+		if (buffer == pool->element[i].buffer) {
-+			retval = zfcp_mem_pool_element_put(pool, i);
-+			goto out;
-+		}
-+	}
-+
-+	if (i == pool->entries) {
-+		ZFCP_LOG_NORMAL("bug: memory pool is broken (buffer not found)\n");
-+		retval = -EINVAL;
-+	}
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+}
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+
-+/* end of memory pool implementation */
-+
-+/*
-+ * function:	zfcp_fsf_req_alloc
-+ *
-+ * purpose:     Obtains an fsf_req and potentially a qtcb (for all but 
-+ *              unsolicited requests) via helper functions
-+ *              Does some initial fsf request set-up.
-+ *              
-+ * returns:	pointer to allocated fsf_req if successfull
-+ *              NULL otherwise
-+ *
-+ * locks:       none
-+ *
-+ */
-+static zfcp_fsf_req_t *zfcp_fsf_req_alloc(zfcp_mem_pool_t *pool, int flags,
-+                                          int kmalloc_flags)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+        
-+        zfcp_fsf_req_t *fsf_req = NULL;
-+
-+        if (!(flags & ZFCP_REQ_USE_MEMPOOL)) {
-+                fsf_req = ZFCP_KMALLOC(sizeof(struct zfcp_fsf_req_pool_buffer),
-+                                       kmalloc_flags);
-+        }
-+
-+	if ((fsf_req == 0) && (pool != 0)) {
-+		fsf_req = zfcp_mem_pool_find(pool);
-+		if (fsf_req){
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_POOL;
-+                        fsf_req->pool = pool;
-+                }
-+	}
-+
-+	if (fsf_req == 0) {
-+		ZFCP_LOG_DEBUG("error: Out of memory. Allocation of FSF "
-+                              "request structure failed\n");
-+	}
-+
-+	return fsf_req;	
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_fsf_req_free
-+ *
-+ * purpose:     Frees the memory of an fsf_req (and potentially a qtcb) or
-+ *              returns it into the pool via helper functions.
-+ *
-+ * returns:     sod all
-+ *
-+ * locks:       none
-+ */
-+static inline int zfcp_fsf_req_free(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+        if (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL) {
-+                retval = zfcp_mem_pool_return(fsf_req, fsf_req->pool);
-+        } else {
-+                ZFCP_KFREE(fsf_req, sizeof(struct zfcp_fsf_req_pool_buffer));
-+        }
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_unit_enqueue
-+ *
-+ * purpose:	enqueues a logical unit at the end of the unit list
-+ *		associated with the specified port
-+ *              also sets up unit internal structures
-+ *
-+ * returns:	0 if a new unit was successfully enqueued
-+ *              -ENOMEM if the allocation failed
-+ *              -EINVAL if at least one specified parameter was faulty    
-+ *
-+ * locks:	proc_sema must be held to serialise changes to the unit list
-+ *              port->unit_list_lock is taken and released several times
-+ */
-+static int
-+	zfcp_unit_enqueue(
-+		zfcp_port_t *port,
-+		scsi_lun_t scsi_lun,
-+		fcp_lun_t fcp_lun,
-+                zfcp_unit_t **unit_p)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	zfcp_unit_t *unit;
-+        int retval = 0;
-+        unsigned long flags;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx scsi_lun=%i fcp_lun=0x%Lx)\n",
-+		(unsigned long)port, scsi_lun, (llui_t)fcp_lun);
-+
-+	/*
-+	 * check that there is no unit with either this
-+	 * SCSI LUN or FCP_LUN already in list
-+         * Note: Unlike for the adapter and the port, this is an error
-+	 */
-+	read_lock_irqsave(&port->unit_list_lock, flags);
-+	ZFCP_FOR_EACH_UNIT(port, unit) {
-+		if (unit->scsi_lun == scsi_lun) {
-+			ZFCP_LOG_NORMAL(
-+				"Warning: A Unit with SCSI LUN 0x%x already "
-+				"exists. Skipping record.\n",
-+				scsi_lun);
-+                        retval = -EINVAL;
-+                        break;
-+                } else if (unit->fcp_lun == fcp_lun) {
-+                        ZFCP_LOG_NORMAL(
-+                              "Warning: A Unit with FCP_LUN 0x%016Lx is already present. "
-+                              "Record was ignored\n",
-+                              (llui_t)fcp_lun);
-+                        retval = -EINVAL;
-+                        break;
-+		}
-+	}
-+        read_unlock_irqrestore(&port->unit_list_lock, flags);
-+        if (retval == -EINVAL)
-+                goto known_unit;
-+
-+	/* try to allocate new unit data structure (zeroed) */
-+	unit = ZFCP_KMALLOC(sizeof(zfcp_unit_t), GFP_KERNEL);
-+	if (!unit) {
-+		ZFCP_LOG_INFO("error: Allocation of unit struct failed. "
-+                                "Out of memory.\n");
-+                retval = -ENOMEM;
-+                goto unit_alloc_failed;
-+        }
-+        
-+        /* save pointer to "parent" port */
-+        unit->port = port;
-+        
-+        /* save SCSI LUN */
-+        unit->scsi_lun = scsi_lun;
-+        
-+        /* save FCP_LUN */
-+        unit->fcp_lun = fcp_lun;
-+ 
-+#ifndef ZFCP_PARANOIA_DEAD_CODE
-+        /* set magics */
-+        unit->common_magic = ZFCP_MAGIC;
-+        unit->specific_magic = ZFCP_MAGIC_UNIT;
-+#endif
-+
-+        /* Init proc structures */
-+#ifdef CONFIG_PROC_FS
-+        ZFCP_LOG_TRACE("Generating proc entry....\n");
-+	retval = zfcp_create_unit_proc(unit);
-+        if (retval) {
-+                ZFCP_LOG_TRACE(
-+			"freeing unit struct 0x%lx\n",
-+			(unsigned long) unit);
-+                goto proc_failed;
-+        }
-+        ZFCP_LOG_TRACE("Proc entry created.\n");
-+#endif
-+       
-+        /*
-+         * update max. SCSI LUN of logical units attached to 
-+         * "parent" remote port if necessary
-+         */
-+        if (port->max_scsi_lun < scsi_lun) {
-+                port->max_scsi_lun = scsi_lun;
-+                ZFCP_LOG_TRACE(
-+			"max. SCSI LUN of units of remote "
-+			"port 0x%lx now %i\n",
-+			(unsigned long)port,
-+			scsi_lun);
-+        }
-+        
-+        /*
-+         * update max. SCSI LUN of logical units attached to
-+         * "parent" adapter if necessary
-+         */
-+        if (port->adapter->max_scsi_lun < scsi_lun) {
-+                port->adapter->max_scsi_lun = scsi_lun;
-+                ZFCP_LOG_TRACE(
-+			"max. SCSI LUN of units attached "
-+			"via adapter with devno 0x%04x now %i\n",
-+			port->adapter->devno,
-+			scsi_lun);
-+        }
-+
-+        /*
-+         * update max. SCSI LUN of logical units attached to
-+         * "parent" host (SCSI stack) if necessary
-+         */
-+        if (port->adapter->scsi_host &&
-+            (port->adapter->scsi_host->max_lun < (scsi_lun + 1))) {
-+                port->adapter->scsi_host->max_lun = scsi_lun + 1;
-+                ZFCP_LOG_TRACE(
-+			"max. SCSI LUN of units attached "
-+			"via host # %d now %i\n",
-+			port->adapter->scsi_host->host_no,
-+			port->adapter->scsi_host->max_lun);
-+        }
-+
-+        /* Unit is new and needs to be added to list */
-+        write_lock_irqsave(&port->unit_list_lock, flags);
-+        list_add_tail(&unit->list, &port->unit_list_head);
-+	port->units++;
-+        write_unlock_irqrestore(&port->unit_list_lock, flags);
-+        
-+	/* also add unit to map list to get them in order of addition */
-+	list_add_tail(&unit->map_list, &zfcp_data.map_list_head);
-+         
-+	atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
-+
-+	zfcp_callback_do_unit_add(NULL, port->adapter, port, unit);
-+
-+        ZFCP_LOG_TRACE(
-+		"unit allocated at 0x%lx, %i units in "
-+		"list of port 0x%lx\n",
-+		(unsigned long)unit,
-+		port->units,
-+		(unsigned long)port);
-+        goto out;                
-+
-+proc_failed:
-+        ZFCP_KFREE(unit, sizeof(zfcp_unit_t));
-+
-+unit_alloc_failed:
-+	unit = NULL;
-+
-+known_unit:
-+out:
-+	*unit_p = unit;
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_unit_dequeue
-+ *
-+ * purpose:	dequeues the specified logical unit from the list of
-+ *		the "parent" port
-+ *
-+ * returns:	0 - zfcp_unit_t data structure successfully removed
-+ *		!0 - zfcp_unit_t data structure could not be removed
-+ *			(e.g. still used)
-+ *
-+ * locks :	unit list write lock is assumed to be held by caller
-+ */
-+static int zfcp_unit_dequeue(zfcp_unit_t *unit)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter (unit=0x%lx)\n", (unsigned long)unit);
-+
-+	/* sanity check: valid unit data structure address (simple check) */
-+	if (!unit) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Pointer to a unit struct is a null "
-+                        "pointer\n");
-+                retval = -EINVAL;
-+		goto out;
-+	}
-+
-+	/*
-+	 * sanity check:
-+	 * leave if required list lock is not held,
-+	 * do not know whether it is held by the calling routine (required!)
-+	 * protecting this critical area or someone else (must not occur!),
-+	 * but a lock not held by anyone is definetely wrong
-+	 */
-+	if (!spin_is_locked(&unit->port->unit_list_lock)) {
-+		ZFCP_LOG_NORMAL("bug: Unit list lock not held "
-+                               "(debug info 0x%lx)\n",
-+                               (unsigned long) unit);
-+		retval = -EPERM;
-+		goto out;
-+	}
-+
-+	/* remove specified unit data structure from list */
-+	list_del(&unit->list);
-+
-+	/* decrease number of units in list */
-+	unit->port->units--;
-+
-+	ZFCP_LOG_TRACE(
-+		"unit 0x%lx removed, %i units still in list of port 0x%lx\n",
-+		(unsigned long)unit,
-+		unit->port->units,
-+		(unsigned long)unit->port);
-+
-+        ZFCP_LOG_TRACE("Deleting proc entry......\n");
-+        zfcp_delete_unit_proc(unit);
-+        ZFCP_LOG_TRACE("Proc entry removed.\n");
-+
-+	/* free memory of unit data structure */
-+	ZFCP_KFREE(unit, sizeof(zfcp_unit_t));
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;	/* succeed */
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:   zfcp_create_unit_proc
-+ *
-+ * purpose:    creates proc-dir and status file for the unit passed in
-+ *
-+ * returns:    0      if all entries could be created properly
-+ *             -EPERM if at least one entry could not be created
-+ *                    (all entries are guarranteed to be freed in this 
-+ *                     case)
-+ *
-+ * locks:      proc_sema must be held on call and throughout the function  
-+ */
-+int zfcp_create_unit_proc(zfcp_unit_t *unit)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+     char unit_scsi_lun[20];
-+     int length = 0;
-+     int retval = 0;
-+     
-+     ZFCP_LOG_TRACE("enter (unit=0x%lx)\n",
-+                    (unsigned long)unit);
-+
-+     length += sprintf(&unit_scsi_lun[length],"lun0x%x", unit->scsi_lun);
-+     unit_scsi_lun[length]='\0';
-+     unit->proc_dir = proc_mkdir (unit_scsi_lun, 
-+                                  unit->port->proc_dir);
-+     if (!unit->proc_dir) {
-+          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the unit "
-+                          "with SCSI LUN 0x%x failed. Out of resources.\n",
-+                          unit_scsi_lun,
-+                          unit->scsi_lun);
-+          retval=-EPERM;
-+          goto out;
-+     }
-+     unit->proc_file=create_proc_entry(ZFCP_STATUS_FILE, 
-+                                       S_IFREG|S_IRUGO|S_IWUSR,
-+                                       unit->proc_dir);
-+     if (!unit->proc_file) {
-+          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the unit "
-+                          "with SCSI LUN 0x%x failed. Out of resources.\n",
-+                          ZFCP_STATUS_FILE,
-+                          unit->scsi_lun);
-+          remove_proc_entry (unit_scsi_lun, unit->port->proc_dir);          
-+          retval=-EPERM;
-+          goto out;
-+     }
-+
-+     unit->proc_file->proc_fops = &zfcp_unit_fops;
-+     unit->proc_file->data=(void *)unit;
-+     
-+     ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+out:
-+     return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:   zfcp_delete_unit_proc
-+ *
-+ * purpose:    deletes proc-dir and status file for the unit passed in
-+ *
-+ * returns:    0 in all cases
-+ *
-+ * locks:      proc_sema must be held on call and throughout the function  
-+ */
-+int zfcp_delete_unit_proc(zfcp_unit_t *unit)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+     char unit_scsi_lun[20];
-+     int length = 0;     
-+     int retval = 0;
-+
-+     ZFCP_LOG_TRACE("enter (unit=0x%lx)\n",
-+                    (unsigned long)unit);
-+     
-+     remove_proc_entry (ZFCP_STATUS_FILE, 
-+                        unit->proc_dir);
-+     length += sprintf(&unit_scsi_lun[length],"lun0x%x", unit->scsi_lun);
-+     unit_scsi_lun[length]='\0';
-+     remove_proc_entry (unit_scsi_lun, unit->port->proc_dir);
-+
-+     ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+     return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:   zfcp_create_port_proc
-+ *
-+ * purpose:    creates proc-dir and status file for the port passed in
-+ *
-+ * returns:    0      if all entries could be created properly
-+ *             -EPERM if at least one entry could not be created
-+ *                    (all entries are guarranteed to be freed in this 
-+ *                     case)
-+ *
-+ * locks:      proc_sema must be held on call and throughout the function  
-+ */
-+int zfcp_create_port_proc(zfcp_port_t *port)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+     char port_scsi_id[20];
-+     int length = 0;
-+     int retval = 0;
-+     
-+     length +=sprintf(&port_scsi_id[length],"id0x%x", port->scsi_id);
-+     port_scsi_id[length]='\0';
-+     port->proc_dir = proc_mkdir (port_scsi_id, 
-+                                  port->adapter->proc_dir);
-+     if (!port->proc_dir) {
-+          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the port "
-+                          "with SCSI-id 0x%x failed. Out of resources.\n",
-+                          port_scsi_id,
-+                          port->scsi_id);
-+          retval=-EPERM;
-+          goto out;
-+     }
-+     ZFCP_LOG_TRACE("enter (port=0x%lx)\n",
-+                    (unsigned long)port);
-+     port->proc_file=create_proc_entry(ZFCP_STATUS_FILE, 
-+                                       S_IFREG|S_IRUGO|S_IWUSR,
-+                                       port->proc_dir);
-+     if (!port->proc_file) {
-+          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the port "
-+                          "with SCSI-id 0x%x failed. Out of resources.\n",
-+                          ZFCP_STATUS_FILE,
-+                          port->scsi_id);
-+          remove_proc_entry (port_scsi_id, port->adapter->proc_dir);
-+          retval=-EPERM;
-+          goto out;
-+     }
-+
-+     port->proc_file->proc_fops = &zfcp_port_fops;
-+     port->proc_file->data=(void *)port;
-+     
-+out:
-+     ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+     return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:   zfcp_delete_port_proc
-+ *
-+ * purpose:    deletes proc-dir and status file for the port passed in
-+ *
-+ * returns:    0 in all cases
-+ *
-+ * locks:      proc_sema must be held on call and throughout the function  
-+ */
-+int zfcp_delete_port_proc(zfcp_port_t *port)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+     char port_scsi_id[20];
-+     int length = 0;     
-+     int retval = 0;
-+
-+     ZFCP_LOG_TRACE("enter (port=0x%lx)\n",
-+                    (unsigned long)port);
-+     
-+     remove_proc_entry (ZFCP_STATUS_FILE, port->proc_dir);
-+     length = 0;
-+     length +=sprintf(&port_scsi_id[length],"id0x%x", port->scsi_id);
-+     port_scsi_id[length]='\0';
-+     remove_proc_entry (port_scsi_id, port->adapter->proc_dir);
-+     
-+     ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+     return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:   zfcp_create_adapter_proc
-+ *
-+ * purpose:    creates proc-dir and status file for the adapter passed in
-+ *
-+ * returns:    0      if all entries could be created properly
-+ *             -EPERM if at least one entry could not be created
-+ *                    (all entries are guarranteed to be freed in this 
-+ *                     case)
-+ *
-+ * locks:      proc_sema must be held on call and throughout the function  
-+ */
-+int zfcp_create_adapter_proc(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+     char devno[20];
-+     int length = 0;
-+     int retval = 0;
-+
-+     ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n",
-+                    (unsigned long)adapter);
-+
-+     length +=sprintf(&devno[length],"devno0x%04x", adapter->devno);
-+     devno[length]='\0';
-+     adapter->proc_dir = proc_mkdir (devno, zfcp_data.proc_dir);
-+     if (!adapter->proc_dir) {
-+          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the adapter "
-+                          "with devno 0x%04x failed. Out of resources.\n",
-+                          devno,
-+                          adapter->devno);
-+          retval=-EPERM;
-+          goto out;
-+     }
-+     adapter->proc_file=create_proc_entry(ZFCP_STATUS_FILE, 
-+                                          S_IFREG|S_IRUGO|S_IWUSR,
-+                                          adapter->proc_dir);
-+     if (!adapter->proc_file) {
-+          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the adapter "
-+                          "with devno 0x%04x failed. Out of resources.\n",
-+                          ZFCP_STATUS_FILE,
-+                          adapter->devno);
-+          remove_proc_entry (devno, zfcp_data.proc_dir);
-+          retval=-EPERM;
-+          goto out;
-+     }
-+
-+     adapter->proc_file->proc_fops = &zfcp_adapter_fops;
-+
-+     adapter->proc_file->data=(void *)adapter;
-+
-+     out:
-+
-+     ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+     return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:   zfcp_delete_adapter_proc
-+ *
-+ * purpose:    deletes proc-dir and status file for the adapter passed in
-+ *
-+ * returns:    0 in all cases
-+ *
-+ * locks:      proc_sema must be held on call and throughout the function  
-+ */
-+int zfcp_delete_adapter_proc(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+        char devno[20];
-+        int length = 0;
-+        int retval = 0;
-+        
-+        ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n",
-+                       (unsigned long)adapter);
-+        
-+        remove_proc_entry (ZFCP_STATUS_FILE, adapter->proc_dir);
-+        length += sprintf(&devno[length],"devno0x%04x", adapter->devno);
-+        devno[length]='\0';
-+        remove_proc_entry (devno, zfcp_data.proc_dir);
-+        
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        return retval;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/* 
-+ * function:   zfcp_open_parm_proc
-+ *
-+ * purpose:    sets-up and fills the contents of the parm proc_entry
-+ *             during a read access
-+ *
-+ * retval:     0 if successfull
-+ *             -ENOMEM if at least one buffer could not be allocated
-+ *              (all buffers will be freed on exit)
-+ */
-+int zfcp_open_parm_proc(struct inode *inode, struct file *file)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+	
-+/*
-+ * Note: modified proc fs utilization (instead of using ..._generic):
-+ *
-+ *  - to avoid (SMP) races, allocate buffers for output using
-+ *    the private_data member in the respective file struct
-+ *    such that read() just has to copy out of this buffer
-+ *
-+ */
-+	int len = 0;
-+	procbuf_t *pbuf;
-+        int retval=0;
-+
-+	ZFCP_LOG_TRACE("enter (inode=0x%lx, file=0x%lx)\n",
-+		(unsigned long)inode,
-+		(unsigned long) file);
-+
-+#if 0
-+	/* DEBUG: force an abort which is being hung than, usage of mod_parm dismisses pending fsf_req */
-+	ZFCP_LOG_NORMAL("try to recover forced and hung abort\n");
-+	zfcp_erp_adapter_reopen(ZFCP_FIRST_ADAPTER, 0);
-+#endif
-+
-+	pbuf = ZFCP_KMALLOC(sizeof(procbuf_t), GFP_KERNEL);
-+	if (pbuf == NULL) {
-+		ZFCP_LOG_NORMAL("error: Not enough memory available for "
-+                               "proc-fs action. Action will be ignored.\n");
-+		retval = -ENOMEM;
-+                goto out;
-+	} else {
-+		file->private_data = ( void * ) pbuf;
-+	}
-+
-+	pbuf->buf = ZFCP_KMALLOC(ZFCP_MAX_PROC_SIZE, GFP_KERNEL);
-+	if (pbuf->buf == NULL) {
-+		ZFCP_LOG_NORMAL("error: Not enough memory available for "
-+                               "proc-fs action. Action will be ignored.\n");
-+		ZFCP_KFREE(pbuf, sizeof(*pbuf));
-+		retval = -ENOMEM;
-+                goto out;
-+	}
-+
-+	ZFCP_LOG_TRACE("Memory for proc parm output allocated.\n");
-+
-+	MOD_INC_USE_COUNT;
-+
-+	len += sprintf(pbuf->buf+len,"\n");
-+
-+	len += sprintf(pbuf->buf+len,"Module Information: \n");
-+
-+	len += sprintf(pbuf->buf+len,"\n");
-+
-+	len += sprintf(pbuf->buf+len,"Module Version %s running in mode: ",
-+                                ZFCP_REVISION);
-+
-+	len += sprintf(pbuf->buf+len,"FULL FEATURED\n");
-+
-+	len += sprintf(pbuf->buf+len,"Debug proc output enabled: %s\n",
-+                                     proc_debug ? "  YES" : "   NO");
-+
-+	len += sprintf(pbuf->buf+len,"\n");
-+
-+	len += sprintf(pbuf->buf+len,
-+                                "Full log-level is:    0x%08x which means:\n",
-+                                atomic_read(&zfcp_data.loglevel));
-+
-+        len += sprintf(pbuf->buf+len,
-+                       "ERP log-level:                 %01x\n",
-+                       (atomic_read(&zfcp_data.loglevel) >> 6*4) & 0xf);
-+        len += sprintf(pbuf->buf+len,
-+                                "QDIO log-level:                %01x     "
-+                                "Dynamic IO log-level:               %01x\n",
-+                                (atomic_read(&zfcp_data.loglevel) >> 5*4) & 0xf,
-+                                (atomic_read(&zfcp_data.loglevel) >> 4*4) & 0xf);
-+        len += sprintf(pbuf->buf+len,
-+                                "Configuration log-level:       %01x     "
-+                                "FSF log-level:                      %01x\n",
-+                                (atomic_read(&zfcp_data.loglevel) >> 3*4) & 0xf,
-+                                (atomic_read(&zfcp_data.loglevel) >> 2*4) & 0xf);
-+        len += sprintf(pbuf->buf+len,
-+                                "SCSI log-level:                %01x     "
-+                                "Other log-level:                    %01x\n",
-+                                (atomic_read(&zfcp_data.loglevel) >> 1*4) & 0xf,
-+                                atomic_read(&zfcp_data.loglevel) & 0xf);
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        len += sprintf(pbuf->buf+len,
-+                       "Registered Adapters:       %5d\n",
-+                       zfcp_data.adapters);
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+	if (proc_debug != 0) {
-+	        len += sprintf(pbuf->buf+len,
-+                                        "Data Structure information:\n");
-+	        len += sprintf(pbuf->buf+len,
-+                               "Data struct at:       0x%08lx\n",
-+                               (unsigned long) &zfcp_data);
-+	       	len += sprintf(pbuf->buf+len,"\n");
-+
-+		len += sprintf(pbuf->buf+len,
-+                                        "Adapter list head at: 0x%08lx\n",
-+                                        (unsigned long) &(zfcp_data.adapter_list_head));
-+		len += sprintf(pbuf->buf+len,
-+                                        "Next list head:       0x%08lx     "
-+                                        "Previous list head:        0x%08lx\n",
-+                                        (unsigned long) zfcp_data.adapter_list_head.next,
-+                                        (unsigned long) zfcp_data.adapter_list_head.prev);
-+                len += sprintf(pbuf->buf+len,
-+                                        "List lock:            0x%08lx     "
-+                                        "List lock owner PC:        0x%08lx\n",
-+                                        zfcp_data.adapter_list_lock.lock,
-+                                        zfcp_data.adapter_list_lock.owner_pc);
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+		len += sprintf(pbuf->buf+len,
-+                                        "Total memory used(bytes): 0x%08x\n",
-+                                        atomic_read(&zfcp_data.mem_count));
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+                len += sprintf(pbuf->buf+len,
-+                                        "DEVICE REGISTRATION INFO (devreg):\n");
-+                len += sprintf(pbuf->buf+len,
-+                                        "Control Unit Type:        0x%04x     "
-+                                        "Control Unit Mode:               0x%02x\n",
-+                                        zfcp_data.devreg.ci.hc.ctype,
-+                                        zfcp_data.devreg.ci.hc.cmode);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Channel Status:           0x%04x     "
-+                                        "Device Status:                   0x%02x\n",
-+                                        zfcp_data.devreg.ci.hc.dtype,
-+                                        zfcp_data.devreg.ci.hc.dmode);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Flags:                0x%08x\n",
-+                                        zfcp_data.devreg.flag);
-+                len += sprintf(pbuf->buf+len,"\n");
-+                len += sprintf(pbuf->buf+len,
-+                                        "PRIVILEGED DEVICE REGISTRATION INFO (devreg):\n");
-+                len += sprintf(pbuf->buf+len,
-+                                        "Control Unit Type:        0x%04x     "
-+                                        "Control Unit Model:              0x%02x\n",
-+                                        zfcp_data.devreg_priv.ci.hc.ctype,
-+                                        zfcp_data.devreg_priv.ci.hc.cmode);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Device Type:              0x%04x     "
-+                                        "Device Model:                    0x%02x\n",
-+                                        zfcp_data.devreg_priv.ci.hc.dtype,
-+                                        zfcp_data.devreg_priv.ci.hc.dmode);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Flags:                0x%08x\n",
-+                                        zfcp_data.devreg_priv.flag);
-+                len += sprintf(pbuf->buf+len,"\n");
-+	}// if (proc_debug != 0)
-+
-+        pbuf->len = len;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/* 
-+ * function:   zfcp_close_parm_proc
-+ *
-+ * purpose:    releases the memory allocated by zfcp_open_parm_proc
-+ *
-+ * retval:     0 in all cases
-+ *
-+ */
-+int zfcp_close_parm_proc(struct inode *inode, struct file *file)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int rc=0;
-+	procbuf_t *pbuf = (procbuf_t *) file->private_data;
-+
-+        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
-+                       (unsigned long)inode,
-+                       (unsigned long) file);
-+
-+	if (pbuf) {
-+		if (pbuf->buf) {
-+                        ZFCP_LOG_TRACE("Freeing pbuf->buf\n");
-+			ZFCP_KFREE(pbuf->buf, ZFCP_MAX_PROC_SIZE);
-+                } else {
-+                        ZFCP_LOG_DEBUG("No procfile buffer found to be freed\n");
-+                }
-+                ZFCP_LOG_TRACE("Freeing pbuf\n");
-+		ZFCP_KFREE(pbuf, sizeof(*pbuf));
-+	} else {
-+                ZFCP_LOG_DEBUG("No procfile buffer found to be freed.\n");
-+        }
-+
-+        ZFCP_LOG_TRACE("exit (%i)\n", rc);
-+
-+        MOD_DEC_USE_COUNT;
-+
-+        return rc;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/* 
-+ * function:   zfcp_open_add_map_proc
-+ *
-+ * purpose:    allocates memory for proc_line, intitalises count
-+ *
-+ * retval:     0 if successfull
-+ *             -ENOMEM if memory coud not be obtained
-+ *
-+ * locks:     grabs the zfcp_data.sema_map semaphore
-+ *            it is released upon exit of zfcp_close_add_map_proc
-+ */
-+int zfcp_open_add_map_proc(struct inode *inode, struct file *buffer)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int retval=0;
-+        
-+        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
-+                       (unsigned long)inode,
-+                       (unsigned long) buffer);
-+
-+	down(&zfcp_data.proc_sema);
-+
-+	zfcp_data.proc_line = ZFCP_KMALLOC(ZFCP_MAX_PROC_LINE, GFP_KERNEL);
-+        if (zfcp_data.proc_line == NULL) {
-+		/* release semaphore on memory shortage */
-+		up(&zfcp_data.proc_sema);
-+
-+                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
-+                               " input. Input will be ignored.\n");
-+
-+                retval = -ENOMEM;
-+                goto out;
-+        }
-+
-+        /* This holds the length of the part acutally containing data, not the
-+           size of the buffer */
-+        zfcp_data.proc_line_length=0;
-+        
-+        MOD_INC_USE_COUNT;
-+
-+        ZFCP_LOG_TRACE("proc_line buffer allocated...\n");
-+out:
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+
-+/* 
-+ * function:   zfcp_close_add_map_proc
-+ *
-+ * purpose:    parses any remaining string in proc_line, then
-+ *             releases memory for proc_line, then calls
-+ *             zfcp_adapter_scsi_register_all to tell the SCSI stack about
-+ *             possible new devices
-+ *
-+ * retval:     0 in all cases
-+ *
-+ * locks:      upon exit of zfcp_close_add_map_proc, releases the proc_sema
-+ */
-+int zfcp_close_add_map_proc(struct inode *inode, struct file *buffer)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int retval=0;
-+
-+        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
-+                       (unsigned long)inode,
-+                       (unsigned long) buffer);
-+
-+	if (zfcp_data.proc_line == NULL)
-+		goto out;
-+
-+        if (zfcp_data.proc_line_length > 0) {
-+                ZFCP_LOG_TRACE("Passing leftover line to parser\n");
-+                retval=zfcp_config_parse_record_list(
-+                             zfcp_data.proc_line,
-+                             zfcp_data.proc_line_length,
-+                             ZFCP_PARSE_ADD);
-+                if(retval<0) {
-+                        ZFCP_LOG_NORMAL("Warning: One or several mapping "
-+                                        "entries were not added to the "
-+                                        "module configuration.\n");
-+                }
-+        }
-+        ZFCP_KFREE(zfcp_data.proc_line, ZFCP_MAX_PROC_LINE);
-+        ZFCP_LOG_TRACE("proc_line buffer released...\n");
-+        zfcp_data.proc_line=NULL;
-+        zfcp_data.proc_line_length=0;
-+
-+	zfcp_adapter_scsi_register_all();
-+
-+	/* release semaphore */
-+	up(&zfcp_data.proc_sema);
-+
-+        MOD_DEC_USE_COUNT;
-+out:
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+
-+/* 
-+ * function:   zfcp_create_root_proc
-+ *
-+ * purpose:    creates the main proc-directory for the zfcp driver
-+ *
-+ * retval:     0 if successfull
-+ *             -EPERM if the proc-directory could not be created
-+ *
-+ * locks:      the proc_sema is held on entry and throughout this function
-+ */
-+int zfcp_create_root_proc()
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+     int retval = 0;
-+
-+     ZFCP_LOG_TRACE("enter\n");
-+
-+     zfcp_data.proc_dir = proc_mkdir (ZFCP_NAME, proc_scsi);
-+     if (!zfcp_data.proc_dir) {
-+          ZFCP_LOG_INFO("error: Allocation of proc-fs directory %s for the  "
-+                        "zfcp-driver failed.\n",
-+                        ZFCP_NAME);
-+          retval = -EPERM;
-+     }
-+     ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+     return retval;
-+     
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+
-+/* 
-+ * function:   zfcp_create_data_procs
-+ *
-+ * purpose:    creates the module-centric proc-entries
-+ *
-+ * retval:     0 if successfull
-+ *             -EPERM if the proc-entries could not be created
-+ *              (all entries are removed on exit)
-+ *
-+ * locks:      the proc_sema is held on entry and throughout this function
-+ */
-+int zfcp_create_data_procs()
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+     int retval = 0;
-+
-+     ZFCP_LOG_TRACE("enter\n");
-+     /* parm_file */
-+     zfcp_data.parm_proc_file=create_proc_entry(ZFCP_PARM_FILE, 
-+                                            S_IFREG|S_IRUGO|S_IWUSR,
-+                                            zfcp_data.proc_dir);
-+     if (!zfcp_data.parm_proc_file) {
-+             ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for module "
-+                             "configuration failed. Out of resources.\n",
-+                             ZFCP_PARM_FILE);
-+             retval = -EPERM;
-+             goto out;
-+     }
-+     zfcp_data.parm_proc_file->proc_fops=&zfcp_parm_fops;
-+
-+     /* map file */
-+     zfcp_data.map_proc_file=create_proc_entry(ZFCP_MAP_FILE, 
-+                                            S_IFREG|S_IRUGO,
-+                                            zfcp_data.proc_dir);
-+     if (!zfcp_data.map_proc_file) {
-+             ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for module "
-+                             "configuration failed. Out of resources.\n",
-+                             ZFCP_MAP_FILE);
-+          retval = -EPERM;
-+          goto fail_map_proc_file;
-+     }
-+     zfcp_data.map_proc_file->proc_fops=&zfcp_map_fops;
-+
-+     /* add_map file */
-+     zfcp_data.add_map_proc_file=create_proc_entry(ZFCP_ADD_MAP_FILE, 
-+                                            S_IFREG|S_IWUSR,
-+                                            zfcp_data.proc_dir);
-+     if (!zfcp_data.map_proc_file) {
-+             ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for module "
-+                             "configuration failed. Out of resources.\n",
-+                             ZFCP_ADD_MAP_FILE);
-+             retval = -EPERM;
-+             goto fail_add_map_proc_file;
-+     }
-+     zfcp_data.add_map_proc_file->proc_fops=&zfcp_add_map_fops;
-+     goto out;
-+
-+ fail_add_map_proc_file:
-+     remove_proc_entry (ZFCP_MAP_FILE, zfcp_data.proc_dir);
-+ fail_map_proc_file:
-+     remove_proc_entry (ZFCP_PARM_FILE, zfcp_data.proc_dir);
-+     
-+ out:
-+
-+     ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+     return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+
-+/* 
-+ * function:   zfcp_delete_root_proc
-+ *
-+ * purpose:    deletes the main proc-directory for the zfcp driver
-+ *
-+ * retval:     0 in all cases
-+ */
-+int zfcp_delete_root_proc()
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+        int retval = 0;
-+        
-+        ZFCP_LOG_TRACE("enter\n");
-+        
-+        remove_proc_entry (ZFCP_NAME, proc_scsi);
-+        
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+};
-+
-+
-+
-+/* 
-+ * function:   zfcp_delete_data_proc
-+ *
-+ * purpose:    deletes the module-specific proc-entries for the zfcp driver
-+ *
-+ * retval:     0 in all cases
-+ */
-+int zfcp_delete_data_procs()
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
-+        int retval = 0;
-+        
-+        ZFCP_LOG_TRACE("enter\n");
-+        
-+        remove_proc_entry (ZFCP_MAP_FILE, zfcp_data.proc_dir);
-+        remove_proc_entry (ZFCP_ADD_MAP_FILE, zfcp_data.proc_dir);
-+        remove_proc_entry (ZFCP_PARM_FILE, zfcp_data.proc_dir);
-+        
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+};
-+
-+
-+
-+/*
-+ * function:	zfcp_parm_proc_read
-+ *
-+ * purpose:	Provides information about module settings as proc-output
-+ *
-+ * returns:     number of characters copied to user-space
-+ *              - <error-type> otherwise
-+ */
-+ssize_t zfcp_parm_proc_read(struct file *file, 
-+                            char *user_buf, 
-+                            size_t user_len, 
-+                            loff_t *offset)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+        
-+	loff_t len;
-+	procbuf_t *pbuf = (procbuf_t *) file->private_data;
-+
-+	ZFCP_LOG_TRACE(
-+          "enter (file=0x%lx  user_buf=0x%lx "
-+          "user_length=%li *offset=0x%lx)\n",
-+          (unsigned long)file,
-+          (unsigned long)user_buf,
-+          user_len,
-+          (unsigned long)*offset);
-+
-+	if ( *offset>=pbuf->len) {
-+		return 0;
-+	} else {
-+		len = min(user_len, (unsigned long)(pbuf->len - *offset));
-+		if (copy_to_user( user_buf, &(pbuf->buf[*offset]), len))
-+			return -EFAULT;
-+		(* offset) += len;
-+		return len;
-+	}
-+
-+	/* FIXME: the following code is never reached */
-+
-+        ZFCP_LOG_TRACE("Size-offset is %ld, user_len is %ld\n",
-+                       ((unsigned long)(pbuf->len  - *offset)),
-+                       user_len);
-+
-+        ZFCP_LOG_TRACE("exit (%Li)\n", len);
-+        
-+        return len;
-+     
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:   zfcp_parm_proc_write
-+ *
-+ * purpose:    parses write requests to parm procfile
-+ *
-+ * returns:    number of characters passed into function
-+ *             -<error code> on failure
-+ *
-+ * known bugs: does not work when small buffers are used
-+ */
-+
-+ssize_t zfcp_parm_proc_write(struct file *file,
-+			const char *user_buf,
-+			size_t user_len,
-+			loff_t *offset)
-+
-+{
-+
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	char *buffer, *tmp = NULL;
-+	char *buffer_start = NULL;
-+	char *pos;
-+	size_t my_count = user_len;
-+	u32 value;
-+	int retval = user_len;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (file=0x%lx  user_buf=0x%lx "
-+		"user_length=%li *offset=0x%lx)\n",
-+		(unsigned long)file,
-+		(unsigned long)user_buf,
-+		user_len,
-+		(unsigned long)*offset);
-+
-+	buffer = ZFCP_KMALLOC(my_count + 1, GFP_KERNEL);
-+	if (!buffer) {
-+                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
-+                                " input. Input will be ignored.\n");
-+		retval = -ENOMEM;
-+		goto out;
-+	}
-+	buffer_start=buffer;
-+	ZFCP_LOG_TRACE("buffer allocated...\n");
-+
-+	copy_from_user(buffer, user_buf, my_count);
-+
-+	buffer[my_count] = '\0';
-+
-+	ZFCP_LOG_TRACE("user_len= %ld, strlen= %ld, buffer=%s<\n", 
-+		user_len, strlen("loglevel=0x00000000"), buffer);
-+
-+	/* look for new loglevel */
-+	pos = strstr(buffer, "loglevel=");
-+	if (pos) {
-+		tmp = pos + strlen("loglevel=");
-+		value = simple_strtoul(tmp, &pos, 0);
-+		if (pos == tmp) {
-+			ZFCP_LOG_INFO(
-+				"warning: Log-level could not be changed, syntax faulty."
-+				"\nSyntax is loglevel=0xueqdcfso, see device driver "
-+				"documentation for details.\n");
-+			retval = -EFAULT;
-+		} else	{
-+			ZFCP_LOG_TRACE(
-+				"setting new loglevel (old is 0x%x, new is 0x%x)\n",
-+				atomic_read(&zfcp_data.loglevel), value);
-+			atomic_set(&zfcp_data.loglevel, value);
-+		}
-+	}
-+
-+#ifdef ZFCP_LOW_MEM_CREDITS
-+	/* look for low mem trigger/credit */
-+	pos = strstr(buffer, "lowmem=");
-+	if (pos) {
-+		tmp = pos + strlen("lowmem=");
-+		value = simple_strtoul(tmp, &pos, 0);
-+		if (pos == tmp) {
-+			ZFCP_LOG_INFO("warning: lowmem credit faulty.");
-+			retval = -EFAULT;
-+		} else	{
-+			ZFCP_LOG_INFO("setting lowmem credit to %d\n", value);
-+			atomic_set(&zfcp_data.lowmem_credit, value);
-+		}
-+	}
-+#endif
-+
-+	ZFCP_LOG_TRACE("freeing buffer..\n");
-+	ZFCP_KFREE(buffer_start, my_count + 1);
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+/* 
-+ * function:   zfcp_open_proc_map
-+ *
-+ * purpose:    allocates memory for proc_buffer_map
-+ *
-+ * retval:     0 if successfull
-+ *             -ENOMEM if memory coud not be obtained
-+ *
-+ * locks:     grabs the zfcp_data.sema_map semaphore 
-+ *            it is released upon exit of zfcp_close_proc_map
-+ */
-+int zfcp_proc_map_open(struct inode *inode, struct file *buffer)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int retval = 0;
-+
-+        ZFCP_LOG_TRACE(
-+                "enter (inode=0x%lx, buffer=0x%lx)\n",
-+                (unsigned long)inode,
-+                (unsigned long) buffer);
-+
-+        /* block access */
-+        down(&zfcp_data.proc_sema);
-+
-+        zfcp_data.proc_buffer_map = ZFCP_KMALLOC(
-+                                        ZFCP_MAX_PROC_SIZE,
-+                                        GFP_KERNEL);
-+        if (!zfcp_data.proc_buffer_map) {
-+                /* release semaphore on memory shortage */
-+                up(&zfcp_data.proc_sema);
-+                ZFCP_LOG_NORMAL(
-+                        "error: Not enough free memory for procfile"
-+                        " output. No output will be given.\n");
-+                retval = -ENOMEM;
-+        } else  MOD_INC_USE_COUNT;
-+
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/* 
-+ * function:   zfcp_close_proc_map
-+ *
-+ * purpose:    releases memory for proc_buffer_map
-+ *
-+ * retval:     0 in all cases
-+ *
-+ * locks:      upon exit releases zfcp_close_proc_map
-+ */
-+int zfcp_proc_map_close(struct inode *inode, struct file *buffer)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int retval=0;
-+
-+        ZFCP_LOG_TRACE(
-+                "enter (inode=0x%lx, buffer=0x%lx)\n",
-+                (unsigned long)inode,
-+                (unsigned long) buffer);
-+
-+        if (zfcp_data.proc_buffer_map) {
-+                ZFCP_LOG_TRACE("Freeing zfcp_data.proc_buffer_map.\n");
-+                ZFCP_KFREE(zfcp_data.proc_buffer_map, ZFCP_MAX_PROC_SIZE);
-+                up(&zfcp_data.proc_sema);
-+                MOD_DEC_USE_COUNT;
-+        }
-+
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_proc_map_read
-+ *
-+ * purpose:	Provides a list of all configured devices in identical format
-+ *		to expected configuration input as proc-output
-+ *
-+ * returns:     number of characters copied to user-space
-+ *              - <error-type> otherwise
-+ *
-+ * locks:       proc_sema must be held on entry and throughout function
-+ */
-+ssize_t zfcp_proc_map_read(
-+		struct file *file, 
-+		char *user_buf, 
-+		size_t user_len, 
-+		loff_t *offset)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	size_t real_len = 0;
-+	size_t print_len = 0;
-+	loff_t line_offset = 0;
-+	u64 current_unit = 0;
-+	zfcp_unit_t *unit;
-+	int i = 0;
-+	static size_t item_size = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (file=0x%lx  user_buf=0x%lx "
-+		"user_length=%li, *offset=%Ld)\n",
-+		(unsigned long)file,
-+		(unsigned long)user_buf,
-+		user_len,
-+		*offset);
-+
-+	if (*offset) {
-+		/*
-+		 * current_unit: unit that needs to be printed (might be remainder)
-+		 * line_offset: bytes of current_unit that have already been printed
-+		 */
-+		current_unit = (*offset);
-+		line_offset = do_div(current_unit, item_size);
-+		ZFCP_LOG_TRACE(
-+			"item_size %ld, current_unit %Ld, line_offset %Ld\n",
-+			item_size,
-+			(llui_t)current_unit,
-+			line_offset);
-+	}
-+
-+	list_for_each_entry(unit, &zfcp_data.map_list_head, map_list) {
-+		/* skip all units that have already been completely printed */
-+		if (i < current_unit) {
-+			i++;
-+			continue;
-+		}
-+		/* a unit to be printed (at least partially) */
-+		ZFCP_LOG_TRACE("unit=0x%lx\n", (unsigned long)unit);
-+		/* assumption: item_size <= ZFCP_MAX_PROC_SIZE */
-+		item_size = sprintf(
-+				&zfcp_data.proc_buffer_map[real_len],
-+				"0x%04x 0x%08x:0x%016Lx 0x%08x:0x%016Lx\n",
-+				unit->port->adapter->devno,
-+				unit->port->scsi_id,
-+				(llui_t)(unit->port->wwpn),
-+				unit->scsi_lun,
-+				(llui_t)(unit->fcp_lun));
-+		/* re-calculate used bytes in kernel buffer */
-+		real_len += item_size;
-+		/* re-calculate bytes to be printed */
-+		print_len = real_len - line_offset;
-+		/* stop if there is not enough user buffer space left */
-+		if (print_len > user_len) {
-+			/* adjust number of bytes to be printed */
-+			print_len = user_len;
-+			break;
-+		}
-+		/* stop if there is not enough kernel buffer space left */
-+		if (real_len + item_size > ZFCP_MAX_PROC_SIZE)
-+			break;
-+	}
-+
-+	/* print if there is something in buffer */
-+	if (print_len) {
-+		ZFCP_LOG_TRACE(
-+			"Trying to do output (line_offset=%Ld, print_len=%ld, "
-+			"real_len=%ld, user_len=%ld).\n",
-+			line_offset, print_len, real_len, user_len);
-+		if (copy_to_user(
-+				user_buf,
-+				&zfcp_data.proc_buffer_map[line_offset],
-+				print_len)) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: Copying proc-file output to user space "
-+				"failed (debug info %ld)",
-+				print_len);
-+			print_len = -EFAULT;
-+		} else	/* re-calculate offset in proc-output for next call */
-+			(*offset) += print_len;
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%li)\n", print_len);
-+
-+	return print_len;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/* why is such a function not provided by the kernel? */
-+static size_t strnspn(const char *string, const char *chars, size_t limit)
-+{
-+	size_t pos = 0;
-+	const char *s = string, *c;
-+
-+	while ((*s != '\0') && (pos < limit)) {
-+		c = chars;
-+		do {
-+			if (*c == '\0')
-+				goto out;
-+		} while (*c++ != *s);
-+		s++;
-+		pos++;
-+	}
-+
-+out:
-+	return pos;
-+}
-+
-+
-+/* why is such a function not provided by the kernel? */
-+char* strnchr(const char *string, int character, size_t count)
-+{
-+	char *s = (char*) string;
-+
-+	for (;; s++, count--) {
-+		if (!count)
-+			return NULL;
-+		if (*s == character)
-+			return s;
-+		if (*s == '\0')
-+			return NULL;
-+	}
-+}
-+
-+
-+/* why is such a function not provided by the kernel? */
-+char* strnpbrk(const char *string, const char *chars, size_t count)
-+{
-+	char *s = (char*) string;
-+
-+	for (;; s++, count--) {
-+		if (!count)
-+			return NULL;
-+		if (strnspn(s, chars, 1))
-+			return s;
-+		if (*s == '\0')
-+			return NULL;
-+	}
-+}
-+
-+
-+/*
-+ * function:	zfcp_find_forward
-+ *
-+ * purpose:	Scans buffer for '\n' to a max length of *buffer_length
-+ *		buffer is incremented to after the first occurance of
-+ *              '\n' and *buffer_length decremented to reflect the new
-+ *              buffer length.
-+ *              fragment is a pointer to the original buffer start address
-+ *              and contains the initial fragment string of length 
-+ *              *fragment_length
-+ *
-+ * returns:     0 if found
-+ *              -1 otherwise
-+ */
-+unsigned long zfcp_find_forward(char **buffer, 
-+                      unsigned long *buffer_length,
-+                      char **fragment,
-+                      unsigned long *fragment_length)
-+{
-+                        
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+        
-+        unsigned long retval=0;
-+        
-+	ZFCP_LOG_TRACE(
-+                       "enter (*buffer=0x%lx, *buffer_length=%ld, "
-+                       "*fragment=0x%lx, *fragment_length=%ld)\n",
-+                       (unsigned long)*buffer,
-+                       *buffer_length,
-+                       (unsigned long)*fragment,
-+                       *fragment_length);
-+
-+        *fragment = *buffer;
-+        for(;*buffer < (*fragment + *buffer_length);){
-+                if (**buffer=='\n') break;
-+                (*buffer)++;
-+        }
-+        if(*buffer >= (*fragment + *buffer_length)){
-+                *fragment_length = *buffer_length;
-+                *buffer_length = 0;
-+                retval = -1;
-+                goto out;
-+        }
-+        (*buffer)++;
-+        *fragment_length = *buffer - *fragment;
-+        *buffer_length -= *fragment_length;
-+
-+ out:
-+        ZFCP_LOG_TRACE("exit (%li)\n", retval);
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:	zfcp_find_backward
-+ *
-+ * purpose:	Scans buffer for '\n' backwards to a max length of 
-+ *              *buffer_length. Buffer is left unchanged, but 
-+ *              *buffer_length is decremented to reflect the new
-+ *              buffer length.
-+ *              rest points to the part of the string past the last
-+ *              occurrence of '\n' in the original buffer contents
-+ *              rest_length is the length of this part
-+ *
-+ * returns:     0 if found
-+ *              -1 otherwise
-+ */
-+unsigned long zfcp_find_backward(char **buffer, 
-+                      unsigned long *buffer_length,
-+                      char **rest,
-+                      unsigned long *rest_length)
-+{
-+                        
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+        
-+        unsigned long retval=0;
-+        
-+	ZFCP_LOG_TRACE(
-+                       "enter (*buffer=0x%lx, *buffer_length=%ld, "
-+                       "*rest=0x%lx, *rest_length=%ld)\n",
-+                       (unsigned long)*buffer,
-+                       *buffer_length,
-+                       (unsigned long)*rest,
-+                       *rest_length);
-+
-+        *rest = *buffer + *buffer_length - 1;
-+        /*
-+          n    n+1    n+2    n+3    n+4    n+5     n+6   n+7    n+8
-+          ^                                               ^     ^(*buffer+*buffer_length)
-+          *buffer                                 *rest (buffer end)        
-+        */
-+        for(;*rest!=*buffer;){
-+                if (**rest=='\n') break;
-+                (*rest)--;
-+        }
-+        if (*rest <= *buffer) {
-+                *rest_length = *buffer_length;
-+                *buffer_length = 0;
-+                retval = -1;
-+                goto out;
-+        }
-+        (*rest)++;
-+        /*
-+          n    n+1    n+2    n+3     n+4    n+5    n+6   n+7   n+8
-+          ^                           ^     ^             ^    ^(*buffer+*buffer_length)
-+          *buffer                     '\n'  *rest      (buffer end)        
-+        */
-+        *rest_length = (*buffer + *buffer_length) - *rest;
-+        *buffer_length -= *rest_length;
-+
-+ out:
-+        ZFCP_LOG_TRACE("*rest= 0x%lx\n",
-+                       (unsigned long)*rest);
-+
-+        ZFCP_LOG_TRACE("exit (%li)\n", retval);
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_add_map_proc_write
-+ *
-+ * purpose:	Breaks down the input map entries in user_buf into lines
-+ *              to be parsed by zfcp_config_parse_record_list.
-+ *              Also takes care of recombinations, multiple calls, etc.
-+ *
-+ * returns:     user_len as passed in
-+ *
-+ * locks:       proc_sema must be held on entry and throughout function
-+ */
-+ssize_t zfcp_add_map_proc_write(struct file *file, 
-+                             const char *user_buf, 
-+                             size_t user_len, 
-+                             loff_t *offset)
-+{
-+
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        char *buffer = NULL;
-+        char *buffer_start = NULL; /* buffer is modified, this isn't (see kfree) */
-+        char *frag = NULL;
-+        size_t frag_length = 0;
-+        size_t my_count = user_len;
-+        int temp_ret = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (file=0x%lx  user_buf=0x%lx "
-+		"user_length=%li *offset=0x%lx)\n",
-+		(unsigned long)file,
-+		(unsigned long)user_buf,
-+		user_len,
-+		(unsigned long)*offset);
-+
-+
-+        buffer = ZFCP_KMALLOC(my_count, GFP_KERNEL);
-+	if (!buffer) {
-+                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
-+                                " input. Input will be ignored.\n");
-+		user_len = -ENOMEM;
-+		goto out;
-+	}
-+        buffer_start=buffer;
-+	ZFCP_LOG_TRACE("buffer allocated...\n");
-+     
-+	copy_from_user(buffer, user_buf, my_count);
-+
-+	if (zfcp_data.proc_line_length > 0) {
-+		ZFCP_LOG_TRACE(
-+			"Remnants were present...(%ld)\n",
-+			zfcp_data.proc_line_length);
-+		temp_ret = zfcp_find_forward(
-+				&buffer, &my_count, &frag, &frag_length);
-+		ZFCP_LOG_TRACE(
-+			"fragment = 0x%lx, length= %ld\n",
-+			(unsigned long) frag,
-+			frag_length);
-+
-+		if ((zfcp_data.proc_line_length + frag_length) >
-+		    (ZFCP_MAX_PROC_LINE - 1)) {
-+			ZFCP_LOG_INFO(
-+				"Maximum line length exceeded while parsing (%ld)\n",
-+				zfcp_data.proc_line_length + frag_length);
-+			zfcp_data.proc_line_length = 0;
-+                        user_len= -EINVAL;
-+			goto free_buffer;
-+		} 
-+
-+		if (frag_length > 0) {
-+			memcpy(	zfcp_data.proc_line + zfcp_data.proc_line_length,
-+				frag, 
-+				frag_length);
-+			zfcp_data.proc_line_length += frag_length;
-+		}
-+
-+		if(temp_ret) {
-+			ZFCP_LOG_TRACE("\"\\n\" was not found \n");
-+			goto free_buffer;
-+		}
-+
-+		ZFCP_LOG_TRACE(
-+			"my_count= %ld, buffer=0x%lx text: \"%s\"\n",
-+			my_count,
-+			(unsigned long) buffer,
-+			buffer);
-+
-+		/* process line combined from several buffers */
-+                if (zfcp_config_parse_record_list(
-+			zfcp_data.proc_line,
-+			zfcp_data.proc_line_length,
-+                        ZFCP_PARSE_ADD) < 0) {
-+                        user_len=-EINVAL;
-+                        /* Do not try another parse in close_proc */
-+                        zfcp_data.proc_line_length = 0;
-+                        ZFCP_LOG_NORMAL("Warning: One or several mapping "
-+                                        "entries were not added to the "
-+                                        "module configuration.\n");
-+                }
-+		zfcp_data.proc_line_length = 0;
-+	}// if(zfcp_data.proc_line_length > 0)
-+
-+	temp_ret = zfcp_find_backward(&buffer, &my_count, &frag, &frag_length);
-+	ZFCP_LOG_TRACE(
-+		"fragment length = %ld\n",
-+		frag_length);
-+	if (frag_length > (ZFCP_MAX_PROC_LINE - 1)) {
-+                ZFCP_LOG_NORMAL(
-+                       "warning: Maximum line length exceeded while parsing "
-+                       "input. Length is already %ld. Some part of the input "
-+                       "will be ignored.\n",
-+			frag_length);
-+		zfcp_data.proc_line_length = 0;
-+                user_len = -EINVAL;
-+		goto free_buffer;
-+	}
-+
-+	if (frag_length > 0) {
-+		memcpy(zfcp_data.proc_line, frag, frag_length);
-+		zfcp_data.proc_line_length += frag_length;
-+	}
-+
-+	if (temp_ret) {
-+		ZFCP_LOG_TRACE("\"\\n\" was not found \n");
-+		goto free_buffer;
-+	}
-+
-+	ZFCP_LOG_TRACE(
-+		"my_count= %ld, buffer=0x%lx text: \"%s\"\n",
-+		my_count,
-+		(unsigned long) buffer,
-+		buffer);
-+	if (zfcp_config_parse_record_list(
-+                   buffer,
-+                   my_count,
-+                   ZFCP_PARSE_ADD) < 0) {
-+                user_len=-EINVAL;
-+                /* Do not try another parse in close_proc */
-+                zfcp_data.proc_line_length = 0;
-+                ZFCP_LOG_NORMAL("Warning: One or several mapping "
-+                                "entries were not added to the "
-+                                "module configuration.\n");
-+        }
-+free_buffer:
-+	ZFCP_LOG_TRACE("freeing buffer..\n");
-+	ZFCP_KFREE(buffer_start, my_count + 1);
-+out:
-+	ZFCP_LOG_TRACE("exit (%li)\n", user_len);
-+	return (user_len);
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * zfcp_adapter_proc_open
-+ *
-+ * modified proc fs utilization (instead of using ..._generic):
-+ *
-+ *  - to avoid (SMP) races, allocate buffers for output using
-+ *    the private_data member in the respective file struct
-+ *    such that read() just has to copy out of this buffer
-+ *
-+ */
-+
-+int zfcp_adapter_proc_open(struct inode *inode, struct file *file)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int len = 0;
-+        procbuf_t *pbuf;
-+        int retval=0;
-+        const struct inode *ino = file->f_dentry->d_inode;
-+        const struct proc_dir_entry *dp = ino->u.generic_ip;
-+	zfcp_adapter_t *adapter = dp->data;
-+	int i;
-+
-+        ZFCP_LOG_TRACE("enter (inode=0x%lx, file=0x%lx)\n",
-+                (unsigned long)inode,
-+                (unsigned long) file);
-+
-+#if 0
-+        /* DEBUG: force an abort which is being hung than, usage of mod_parm dismisses pending fsf_req */
-+        ZFCP_LOG_NORMAL("try to recover forced and hung abort\n");
-+        zfcp_erp_adapter_reopen(ZFCP_FIRST_ADAPTER, 0);
-+#endif
-+
-+        pbuf = ZFCP_KMALLOC(sizeof(procbuf_t), GFP_KERNEL);
-+        if (pbuf == NULL) {
-+                ZFCP_LOG_NORMAL("error: Not enough memory available for "
-+                               "proc-fs action. Action will be ignored.\n");
-+                retval = -ENOMEM;
-+                goto out;
-+        } else {
-+                file->private_data = ( void * ) pbuf;
-+        }
-+
-+        pbuf->buf = ZFCP_KMALLOC(ZFCP_MAX_PROC_SIZE, GFP_KERNEL);
-+        if (pbuf->buf == NULL) {
-+                ZFCP_LOG_NORMAL("error: Not enough memory available for "
-+                               "proc-fs action. Action will be ignored.\n");
-+                ZFCP_KFREE(pbuf, sizeof(*pbuf));
-+                retval = -ENOMEM;
-+                goto out;
-+        }
-+
-+        ZFCP_LOG_TRACE("Memory for adapter proc output allocated.\n");
-+
-+        MOD_INC_USE_COUNT;
-+
-+        len += sprintf(pbuf->buf+len,
-+                                "\nFCP adapter\n\n");
-+
-+        len += sprintf(pbuf->buf+len,
-+                                "FCP driver %s "
-+                                "(or for cryptography's sake 0x%08x)\n\n",
-+                                ZFCP_REVISION,
-+                                zfcp_data.driver_version);
-+
-+        len += sprintf(pbuf->buf+len,
-+				"device number:            0x%04x     "
-+                                "registered on irq:             0x%04x\n",
-+                                adapter->devno,
-+                                adapter->irq);
-+        len += sprintf(pbuf->buf+len,
-+                                "WWNN:         0x%016Lx\n",
-+                                (llui_t)adapter->wwnn);
-+        len += sprintf(pbuf->buf+len,
-+				"WWPN:         0x%016Lx     "
-+				"S_ID:                        0x%06x\n",
-+                                (llui_t)adapter->wwpn,
-+				adapter->s_id);
-+        len += sprintf(pbuf->buf+len,
-+                                "HW version:               0x%04x     "
-+                                "LIC version:               0x%08x\n",
-+                                adapter->hydra_version,
-+                                adapter->fsf_lic_version);
-+        len += sprintf(pbuf->buf+len,
-+                                "FC link speed:            %d Gb/s     "
-+                                "FC service class:                   %d\n",
-+				adapter->fc_link_speed,
-+                                adapter->fc_service_class);
-+        len += sprintf(pbuf->buf+len,
-+                       "Hardware Version:     0x%08x\n"
-+                       "Serial Number: %17s\n",
-+                       adapter->hardware_version,
-+                       adapter->serial_number);
-+        len += sprintf(pbuf->buf+len,
-+                                "FC topology:   %s\n",
-+                                zfcp_topologies[adapter->fc_topology]);
-+#if 0
-+	if (adapter->fc_topology == FSF_TOPO_P2P)
-+        len += sprintf(pbuf->buf+len,
-+				"D_ID of peer:         0x%06x\n",
-+				adapter->peer_d_id);
-+#endif
-+        len += sprintf(pbuf->buf+len,
-+                                "SCSI host number:     0x%08x\n",
-+                                adapter->scsi_host->host_no);
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        len += sprintf(pbuf->buf+len,
-+                                "Attached ports:       %10d     "
-+                                "QTCB size (bytes):         %10ld\n",
-+                                adapter->ports,
-+                                sizeof(fsf_qtcb_t));
-+        len += sprintf(pbuf->buf+len,
-+                                "Max SCSI ID of ports: 0x%08x     "
-+                                "Max SCSI LUN of ports:     0x%08x\n",
-+                                adapter->max_scsi_id,
-+                                adapter->max_scsi_lun);
-+        len += sprintf(pbuf->buf+len,
-+                                "FSF req seq. no:      0x%08x     "
-+                                "FSF reqs active:           %10d\n",
-+                                adapter->fsf_req_seq_no,
-+                                atomic_read(&adapter->fsf_reqs_active));
-+        len += sprintf(pbuf->buf+len,
-+                                "Scatter-gather table-size: %5d     "
-+                                "Max no of queued commands: %10d\n",
-+                                zfcp_data.scsi_host_template.sg_tablesize,
-+                                zfcp_data.scsi_host_template.can_queue);
-+        len += sprintf(pbuf->buf+len,
-+                                "Uses clustering:               %1d     "
-+                                "Uses New Error-Handling Code:       %1d\n",
-+                                zfcp_data.scsi_host_template.use_clustering,
-+                                zfcp_data.scsi_host_template.use_new_eh_code);
-+        len += sprintf(pbuf->buf+len,
-+                                "ERP counter:          0x%08x     ",
-+                                atomic_read(&adapter->erp_counter));
-+        len += sprintf(pbuf->buf+len,
-+                                "Adapter Status:            0x%08x\n",
-+                                atomic_read(&adapter->status));
-+	len += sprintf(pbuf->buf+len,
-+				"SCSI commands delayed: %10d\n",
-+				atomic_read(&adapter->fake_scsi_reqs_active));
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        if (proc_debug != 0) {
-+		len += sprintf(pbuf->buf+len,
-+                                        "Adapter Structure information:\n");
-+                len += sprintf(pbuf->buf+len,
-+                                        "Common Magic:         0x%08x     "
-+                                        "Specific Magic:            0x%08x\n",
-+                                        adapter->common_magic,
-+                                        adapter->specific_magic);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Adapter struct at:    0x%08lx     "
-+                                        "List head at:              0x%08lx\n",
-+                                        (unsigned long) adapter,
-+                                        (unsigned long) &(adapter->list));
-+                len += sprintf(pbuf->buf+len,
-+                                        "Next list head:       0x%08lx     "
-+                                        "Previous list head:        0x%08lx\n",
-+                                        (unsigned long) adapter->list.next,
-+                                        (unsigned long) adapter->list.prev);
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+                len += sprintf(pbuf->buf+len,
-+                                        "Scsi_Host struct at:  0x%08lx\n",
-+                                        (unsigned long) adapter->scsi_host);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Port list head at:    0x%08lx\n",
-+                                        (unsigned long) &(adapter->port_list_head));
-+                len += sprintf(pbuf->buf+len,
-+                                        "Next list head:       0x%08lx     "
-+                                        "Previous list head:        0x%08lx\n",
-+                                        (unsigned long) adapter->port_list_head.next,
-+                                        (unsigned long) adapter->port_list_head.prev);
-+                len += sprintf(pbuf->buf+len,
-+                                        "List lock:            0x%08lx     "
-+                                        "List lock owner PC:        0x%08lx\n",
-+                                        adapter->port_list_lock.lock,
-+                                        adapter->port_list_lock.owner_pc);
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+                len += sprintf(pbuf->buf+len,
-+                                        "O-FCP req list head:  0x%08lx\n",
-+                                        (unsigned long) &(adapter->fsf_req_list_head));
-+                len += sprintf(pbuf->buf+len,
-+                                        "Next list head:       0x%08lx     "
-+                                        "Previous list head:        0x%08lx\n",
-+                                        (unsigned long) adapter->fsf_req_list_head.next,
-+                                        (unsigned long) adapter->fsf_req_list_head.prev);
-+                len += sprintf(pbuf->buf+len,
-+                                        "List lock:            0x%08lx     "
-+                                        "List lock owner PC:        0x%08lx\n",
-+                                        adapter->fsf_req_list_lock.lock,
-+                                        adapter->fsf_req_list_lock.owner_pc);
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+                len += sprintf(pbuf->buf+len,
-+                                        "Request queue at:     0x%08lx\n",
-+                                        (unsigned long)&(adapter->request_queue));
-+                len += sprintf(pbuf->buf+len,
-+                                        "Free index:                  %03d     "
-+                                        "Free count:                       %03d\n",
-+                                        adapter->request_queue.free_index,
-+                                        atomic_read(&adapter->request_queue.free_count));
-+                len += sprintf(pbuf->buf+len,
-+                                        "List lock:            0x%08lx     "
-+                                        "List lock owner PC:        0x%08lx\n",
-+                                        adapter->request_queue.queue_lock.lock,
-+                                        adapter->request_queue.queue_lock.owner_pc);
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+                len += sprintf(pbuf->buf+len,
-+                                        "Response queue at:    0x%08lx\n",
-+                                        (unsigned long)&(adapter->response_queue));
-+                len += sprintf(pbuf->buf+len,
-+                                        "Free index:                  %03d     "
-+                                        "Free count:                       %03d\n",
-+                                        adapter->response_queue.free_index,
-+                                        atomic_read(&adapter->response_queue.free_count));
-+                len += sprintf(pbuf->buf+len,
-+                                        "List lock:            0x%08lx     "
-+                                        "List lock owner PC:        0x%08lx\n",
-+                                        adapter->response_queue.queue_lock.lock,
-+                                        adapter->response_queue.queue_lock.owner_pc);
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+                len += sprintf(pbuf->buf+len,"DEVICE INFORMATION (devinfo):\n");
-+                len += sprintf(pbuf->buf+len,"Status: ");
-+                switch(adapter->devinfo.status) {
-+                        case 0:
-+                                len += sprintf(pbuf->buf+len,
-+                                                "\"OK\"\n");
-+                        break;
-+                        case DEVSTAT_NOT_OPER:
-+                                len += sprintf(pbuf->buf+len,
-+                                                "\"DEVSTAT_NOT_OPER\"\n");
-+                                break;
-+                        case DEVSTAT_DEVICE_OWNED:
-+                                len += sprintf(pbuf->buf+len,
-+                                                "\"DEVSTAT_DEVICE_OWNED\"\n");
-+                                break;
-+                        case DEVSTAT_UNKNOWN_DEV:
-+                                len += sprintf(pbuf->buf+len,
-+                                                "\"DEVSTAT_UNKNOWN_DEV\"\n");
-+                                break;
-+                        default:
-+                                len += sprintf(pbuf->buf+len,
-+                                                "UNSPECIFIED STATE (value is 0x%x)\n",
-+                                        adapter->devinfo.status);
-+                                break;
-+                }
-+                len += sprintf(pbuf->buf+len,
-+                                        "Control Unit Type:        0x%04x     "
-+                                        "Control Unit Model:              0x%02x\n",
-+                                        adapter->devinfo.sid_data.cu_type,
-+                                        adapter->devinfo.sid_data.cu_model);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Device Type:              0x%04x     "
-+                                        "Device Model:                    0x%02x\n",
-+                                        adapter->devinfo.sid_data.dev_type,
-+                                        adapter->devinfo.sid_data.dev_model);
-+                len += sprintf(pbuf->buf+len,
-+                                        "CIWs:       ");
-+                for(i=0;i<4;i++){
-+                                len += sprintf(pbuf->buf+len,
-+                                                "0x%08x ",
-+                                                *(unsigned int *)(&adapter->devinfo.sid_data.ciw[i]));
-+                }
-+                len += sprintf(pbuf->buf+len,"\n            ");
-+                for(i=4;i<8;i++){
-+                                len += sprintf(pbuf->buf+len,
-+                                                "0x%08x ",
-+                                                *(unsigned int *)(&adapter->devinfo.sid_data.ciw[i]));
-+                }
-+                len += sprintf(pbuf->buf+len,"\n");
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+                len += sprintf(pbuf->buf+len,"DEVICE INFORMATION (devstat):\n");
-+                len += sprintf(pbuf->buf+len,
-+                                        "Interrupt Parameter:  0x%08lx     "
-+                                        "Last path used mask:             0x%02x\n",
-+                                        adapter->devstat.intparm,
-+                                        adapter->devstat.lpum);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Channel Status:             0x%02x     "
-+                                        "Device Status:                   0x%02x\n",
-+                                        adapter->devstat.cstat,
-+                                        adapter->devstat.dstat);
-+		len += sprintf(pbuf->buf+len,
-+                                        "Flag:                 0x%08x     "
-+                                        "CCW address (from irb):    0x%08lx\n",
-+                                        adapter->devstat.flag,
-+                                        (unsigned long)adapter->devstat.cpa);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Response count:       0x%08x     "
-+                                        "Sense Count:               0x%08x\n",
-+                                        adapter->devstat.rescnt,
-+                                        adapter->devstat.scnt);
-+                len += sprintf(pbuf->buf+len,
-+                                "IRB:        ");
-+                for(i=0;i<4;i++){
-+                                len += sprintf(pbuf->buf+len,
-+                                                "0x%08x ",
-+                                                *((unsigned int *)(&adapter->devstat.ii.irb)+i));
-+                }
-+                len += sprintf(pbuf->buf+len,"\n");
-+                len += sprintf(pbuf->buf+len,
-+                                        "Sense Data: ");
-+                for(i=0;i<4;i++){
-+                                len += sprintf(pbuf->buf+len,
-+                                                "0x%08x ",
-+                                                *((unsigned int *)(&adapter->devstat.ii.sense.data)+i));
-+                }
-+                        len += sprintf(pbuf->buf+len,"\n");
-+        }
-+
-+#ifdef ZFCP_STAT_QUEUES
-+        len += sprintf(pbuf->buf + len, "\nOutbound queue full:  0x%08x     ",
-+                       atomic_read(&adapter->outbound_queue_full));
-+	len += sprintf(pbuf->buf + len, "Outbound requests:    0x%08x\n\n",
-+			atomic_read(&adapter->outbound_total));
-+#endif
-+#ifdef ZFCP_STAT_REQSIZES
-+	len += sprintf(pbuf->buf + len, "missed stats 0x%x\n",
-+			atomic_read(&adapter->stat_errors));
-+	len = zfcp_statistics_print(
-+			adapter, &adapter->read_req_head,
-+			"rr", pbuf->buf, len, ZFCP_MAX_PROC_SIZE);
-+	len = zfcp_statistics_print(
-+			adapter, &adapter->write_req_head,
-+			"wr", pbuf->buf, len, ZFCP_MAX_PROC_SIZE);
-+#endif
-+
-+	ZFCP_LOG_TRACE("stored %d bytes in proc buffer\n", len);
-+
-+        pbuf->len = len;
-+
-+out:
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+int zfcp_adapter_proc_close(struct inode *inode, struct file *file)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int rc=0;
-+        procbuf_t *pbuf = (procbuf_t *) file->private_data;
-+
-+        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
-+                       (unsigned long)inode,
-+                       (unsigned long) file);
-+
-+        if (pbuf) {
-+                if (pbuf->buf) {
-+                        ZFCP_LOG_TRACE("Freeing pbuf->buf\n");
-+                        ZFCP_KFREE(pbuf->buf, ZFCP_MAX_PROC_SIZE);
-+                } else {
-+                        ZFCP_LOG_DEBUG("No procfile buffer found to be freed\n");
-+                }
-+                ZFCP_LOG_TRACE("Freeing pbuf\n");
-+                ZFCP_KFREE(pbuf, sizeof(*pbuf));
-+        } else {
-+                ZFCP_LOG_DEBUG("No procfile buffer found to be freed.\n");
-+        }
-+
-+        ZFCP_LOG_TRACE("exit (%i)\n", rc);
-+
-+        MOD_DEC_USE_COUNT;
-+
-+        return rc;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:    zfcp_adapter_proc_read
-+ *
-+ * returns:     number of characters copied to user-space
-+ *              - <error-type> otherwise
-+ */
-+ssize_t zfcp_adapter_proc_read(struct file *file,
-+                            char *user_buf,
-+                            size_t user_len,
-+                            loff_t *offset)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        loff_t len;
-+        procbuf_t *pbuf = (procbuf_t *) file->private_data;
-+
-+        ZFCP_LOG_TRACE(
-+          "enter (file=0x%lx  user_buf=0x%lx "
-+          "user_length=%li *offset=0x%lx)\n",
-+          (unsigned long)file,
-+          (unsigned long)user_buf,
-+          user_len,
-+          (unsigned long)*offset);
-+
-+        if ( *offset>=pbuf->len) {
-+                return 0;
-+        } else {
-+                len = min(user_len, (unsigned long)(pbuf->len - *offset));
-+                if (copy_to_user( user_buf, &(pbuf->buf[*offset]), len))
-+                        return -EFAULT;
-+                (* offset) += len;
-+                return len;
-+        }
-+
-+        ZFCP_LOG_TRACE("Size-offset is %ld, user_len is %ld\n",
-+                       ((unsigned long)(pbuf->len  - *offset)),
-+                       user_len);
-+
-+        ZFCP_LOG_TRACE("exit (%Li)\n", len);
-+
-+        return len;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function: zfcp_adapter_proc_write
-+ *
-+ * known bugs: does not work when small buffers are used
-+ *
-+ */
-+
-+ssize_t zfcp_adapter_proc_write(struct file *file,
-+                        const char *user_buf,
-+                        size_t user_len,
-+                        loff_t *offset)
-+
-+{
-+
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        char *buffer = NULL;
-+        size_t my_count = user_len;
-+        const struct inode *ino = file->f_dentry->d_inode;
-+        const struct proc_dir_entry *dp = ino->u.generic_ip;
-+	zfcp_adapter_t *adapter = dp->data;
-+
-+        ZFCP_LOG_TRACE(
-+                "enter (file=0x%lx  user_buf=0x%lx "
-+                "user_length=%li *offset=0x%lx)\n",
-+                (unsigned long)file,
-+                (unsigned long)user_buf,
-+                user_len,
-+                (unsigned long)*offset);
-+
-+        buffer = ZFCP_KMALLOC(my_count + 1, GFP_KERNEL);
-+        if (!buffer) {
-+                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
-+                                " input. Input will be ignored.\n");
-+                user_len = -ENOMEM;
-+                goto out;
-+        }
-+        ZFCP_LOG_TRACE("buffer allocated...\n");
-+
-+        copy_from_user(buffer, user_buf, my_count);
-+
-+        buffer[my_count] = '\0'; /* for debugging */
-+
-+        ZFCP_LOG_TRACE("user_len= %ld, buffer=>%s<\n",
-+                user_len, buffer);
-+
-+	if ((strncmp(ZFCP_RESET_ERP, buffer, strlen(ZFCP_RESET_ERP)) == 0) ||
-+	    (strncmp(ZFCP_SET_ONLINE, buffer, strlen(ZFCP_SET_ONLINE)) == 0)) {
-+		ZFCP_LOG_NORMAL(
-+			"user triggered (re)start of all operations on the "
-+			"adapter with devno 0x%04x\n",
-+			adapter->devno);
-+		zfcp_erp_modify_adapter_status(
-+			adapter,
-+			ZFCP_STATUS_COMMON_RUNNING,
-+			ZFCP_SET);
-+		zfcp_erp_adapter_reopen(
-+			adapter,
-+			ZFCP_STATUS_COMMON_ERP_FAILED);
-+		zfcp_erp_wait(adapter);
-+		user_len = strlen(buffer);
-+	} else  if (strncmp(ZFCP_SET_OFFLINE, buffer, strlen(ZFCP_SET_OFFLINE)) == 0) {
-+                ZFCP_LOG_NORMAL(
-+			"user triggered shutdown of all operations on the "
-+			"adapter with devno 0x%04x\n",
-+			adapter->devno);
-+		zfcp_erp_adapter_shutdown(adapter, 0);
-+		zfcp_erp_wait(adapter);
-+		user_len = strlen(buffer);
-+	} else	if (strncmp(ZFCP_STAT_RESET, buffer, strlen(ZFCP_STAT_RESET)) == 0) {
-+#ifdef ZFCP_STAT_REQSIZES
-+		ZFCP_LOG_NORMAL(
-+			"user triggered reset of all statisticss for the "
-+			"adapter with devno 0x%04x\n",
-+			adapter->devno);
-+		atomic_compare_and_swap(1, 0, &adapter->stat_on);
-+		zfcp_statistics_clear(adapter, &adapter->read_req_head);
-+		zfcp_statistics_clear(adapter, &adapter->write_req_head);
-+		atomic_set(&adapter->stat_errors, 0);
-+		atomic_compare_and_swap(0, 1, &adapter->stat_on);
-+#endif
-+		user_len = strlen(buffer);
-+	} else	if (strncmp(ZFCP_STAT_OFF, buffer, strlen(ZFCP_STAT_OFF)) == 0) {
-+#ifdef ZFCP_STAT_REQSIZES
-+		if (atomic_compare_and_swap(1, 0, &adapter->stat_on)) {
-+			ZFCP_LOG_NORMAL(
-+				"warning: all statistics for the adapter "
-+				"with devno 0x%04x already off\n ",
-+			adapter->devno);
-+		} else	{
-+			ZFCP_LOG_NORMAL(
-+				"user triggered shutdown of all statistics for the "
-+				"adapter with devno 0x%04x\n",
-+			adapter->devno);
-+			zfcp_statistics_clear(adapter, &adapter->read_req_head);
-+			zfcp_statistics_clear(adapter, &adapter->write_req_head);
-+		}
-+#endif
-+		user_len = strlen(buffer);
-+	} else	if (strncmp(ZFCP_STAT_ON, buffer, strlen(ZFCP_STAT_ON)) == 0) {
-+#ifdef ZFCP_STAT_REQSIZES
-+		if (atomic_compare_and_swap(0, 1, &adapter->stat_on)) {
-+			ZFCP_LOG_NORMAL(
-+				"warning: all statistics for the adapter "
-+				"with devno 0x%04x already on\n ",
-+			adapter->devno);
-+		} else	{
-+			ZFCP_LOG_NORMAL(
-+				"user triggered (re)start of all statistics for the "
-+				"adapter with devno 0x%04x\n",
-+			adapter->devno);
-+		}
-+#endif
-+		user_len = strlen(buffer);
-+	} else	{
-+		ZFCP_LOG_INFO("error: unknown procfs command\n");
-+		user_len = -EINVAL;
-+	}
-+
-+        ZFCP_LOG_TRACE("freeing buffer..\n");
-+        ZFCP_KFREE(buffer, my_count + 1);
-+out:
-+        ZFCP_LOG_TRACE("exit (%li)\n", user_len);
-+        return (user_len);
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+int zfcp_port_proc_close(struct inode *inode, struct file *file)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int rc=0;
-+        procbuf_t *pbuf = (procbuf_t *) file->private_data;
-+
-+        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
-+                       (unsigned long)inode,
-+                       (unsigned long) file);
-+
-+        if (pbuf) {
-+                if (pbuf->buf) {
-+                        ZFCP_LOG_TRACE("Freeing pbuf->buf\n");
-+                        ZFCP_KFREE(pbuf->buf, ZFCP_MAX_PROC_SIZE);
-+                } else {
-+                        ZFCP_LOG_DEBUG("No procfile buffer found to be freed\n");
-+                }
-+                ZFCP_LOG_TRACE("Freeing pbuf\n");
-+                ZFCP_KFREE(pbuf, sizeof(*pbuf));
-+        } else {
-+                ZFCP_LOG_DEBUG("No procfile buffer found to be freed.\n");
-+        }
-+
-+        ZFCP_LOG_TRACE("exit (%i)\n", rc);
-+
-+        MOD_DEC_USE_COUNT;
-+
-+        return rc;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:    zfcp_port_proc_read
-+ *
-+ * returns:     number of characters copied to user-space
-+ *              - <error-type> otherwise
-+ */
-+ssize_t zfcp_port_proc_read(struct file *file,
-+                            char *user_buf,
-+                            size_t user_len,
-+                            loff_t *offset)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        loff_t len;
-+        procbuf_t *pbuf = (procbuf_t *) file->private_data;
-+
-+        ZFCP_LOG_TRACE(
-+          "enter (file=0x%lx  user_buf=0x%lx "
-+          "user_length=%li *offset=0x%lx)\n",
-+          (unsigned long)file,
-+          (unsigned long)user_buf,
-+          user_len,
-+          (unsigned long)*offset);
-+
-+        if ( *offset>=pbuf->len) {
-+                return 0;
-+        } else {
-+                len = min(user_len, (unsigned long)(pbuf->len - *offset));
-+                if (copy_to_user( user_buf, &(pbuf->buf[*offset]), len))
-+                        return -EFAULT;
-+                (* offset) += len;
-+                return len;
-+        }
-+
-+        ZFCP_LOG_TRACE("Size-offset is %ld, user_len is %ld\n",
-+                       ((unsigned long)(pbuf->len  - *offset)),
-+                       user_len);
-+
-+        ZFCP_LOG_TRACE("exit (%Li)\n", len);
-+
-+        return len;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function: zfcp_port_proc_write
-+ *
-+ * known bugs: does not work when small buffers are used
-+ *
-+ */
-+
-+ssize_t zfcp_port_proc_write(struct file *file,
-+                        const char *user_buf,
-+                        size_t user_len,
-+                        loff_t *offset)
-+
-+{
-+
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        char *buffer = NULL;
-+        size_t my_count = user_len;
-+        const struct inode *ino = file->f_dentry->d_inode;
-+        const struct proc_dir_entry *dp = ino->u.generic_ip;
-+        zfcp_port_t *port = dp->data;
-+	zfcp_adapter_t *adapter = port->adapter;
-+
-+        ZFCP_LOG_TRACE(
-+                "enter (file=0x%lx  user_buf=0x%lx "
-+                "user_length=%li *offset=0x%lx)\n",
-+                (unsigned long)file,
-+                (unsigned long)user_buf,
-+                user_len,
-+                (unsigned long)*offset);
-+
-+        buffer = ZFCP_KMALLOC(my_count + 1, GFP_KERNEL);
-+        if (!buffer) {
-+                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
-+                                " input. Input will be ignored.\n");
-+                user_len = -ENOMEM;
-+                goto out;
-+        }
-+        ZFCP_LOG_TRACE("buffer allocated...\n");
-+
-+        copy_from_user(buffer, user_buf, my_count);
-+
-+        buffer[my_count] = '\0'; /* for debugging */
-+
-+        ZFCP_LOG_TRACE("user_len= %ld, buffer=>%s<\n",
-+                user_len, buffer);
-+
-+	if ((strncmp(ZFCP_RESET_ERP, buffer, strlen(ZFCP_RESET_ERP)) == 0) ||
-+	    (strncmp(ZFCP_SET_ONLINE, buffer, strlen(ZFCP_SET_ONLINE)) == 0)) {
-+		ZFCP_LOG_NORMAL(
-+			"user triggered (re)start of all operations on the "
-+			"port with WWPN 0x%016Lx on the adapter with devno "
-+			"0x%04x\n",
-+			(llui_t)port->wwpn,
-+			adapter->devno);
-+		zfcp_erp_modify_port_status(
-+			port,
-+			ZFCP_STATUS_COMMON_RUNNING,
-+			ZFCP_SET);
-+		zfcp_erp_port_reopen(
-+			port,
-+			ZFCP_STATUS_COMMON_ERP_FAILED);
-+		zfcp_erp_wait(adapter);
-+		user_len = strlen(buffer);
-+	} else  if (strncmp(ZFCP_SET_OFFLINE, buffer, strlen(ZFCP_SET_OFFLINE)) == 0) {
-+		ZFCP_LOG_NORMAL(
-+			"user triggered shutdown of all operations on the "
-+			"port with WWPN 0x%016Lx on the adapter with devno "
-+			"0x%04x\n",
-+			(llui_t)port->wwpn,
-+			adapter->devno);
-+		zfcp_erp_port_shutdown(port, 0);
-+		zfcp_erp_wait(adapter);
-+		user_len = strlen(buffer);
-+	} else  if (strncmp(ZFCP_RTV, buffer, strlen(ZFCP_RTV)) == 0) {
-+		ZFCP_LOG_NORMAL(
-+			"Read timeout value (RTV) ELS "
-+			"(wwpn=0x%016Lx devno=0x%04x)\n",
-+			(llui_t)port->wwpn,
-+			adapter->devno);
-+		zfcp_els(port, ZFCP_LS_RTV);
-+		user_len = strlen(buffer);
-+	} else  if (strncmp(ZFCP_RLS, buffer, strlen(ZFCP_RLS)) == 0) {
-+		ZFCP_LOG_NORMAL(
-+			"Read link status (RLS) ELS "
-+			"(wwpn=0x%016Lx devno=0x%04x)\n",
-+			(llui_t)port->wwpn,
-+			adapter->devno);
-+		zfcp_els(port, ZFCP_LS_RLS);
-+		user_len = strlen(buffer);
-+	} else  if (strncmp(ZFCP_PDISC, buffer, strlen(ZFCP_PDISC)) == 0) {
-+		ZFCP_LOG_NORMAL(
-+			"Port discovery (PDISC) ELS "
-+			"(wwpn=0x%016Lx devno=0x%04x)\n",
-+			(llui_t)port->wwpn,
-+			adapter->devno);
-+		zfcp_els(port, ZFCP_LS_PDISC);
-+		user_len = strlen(buffer);
-+	} else  if (strncmp(ZFCP_ADISC, buffer, strlen(ZFCP_ADISC)) == 0) {
-+		ZFCP_LOG_NORMAL(
-+			"Address discovery (ADISC) ELS "
-+			"(wwpn=0x%016Lx devno=0x%04x)\n",
-+			(llui_t)port->wwpn,
-+			adapter->devno);
-+		zfcp_els(port, ZFCP_LS_ADISC);
-+	} else	{
-+		ZFCP_LOG_INFO("error: unknown procfs command\n");
-+		user_len = -EINVAL;
-+	}
-+
-+        ZFCP_LOG_TRACE("freeing buffer..\n");
-+        ZFCP_KFREE(buffer, my_count + 1);
-+out:
-+        ZFCP_LOG_TRACE("exit (%li)\n", user_len);
-+        return (user_len);
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * zfcp_port_proc_open
-+ *
-+ * modified proc fs utilization (instead of using ..._generic):
-+ *
-+ *  - to avoid (SMP) races, allocate buffers for output using
-+ *    the private_data member in the respective file struct
-+ *    such that read() just has to copy out of this buffer
-+ *
-+ */
-+
-+int zfcp_port_proc_open(struct inode *inode, struct file *file)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int len = 0;
-+        procbuf_t *pbuf;
-+        int retval=0;
-+        const struct inode *ino = file->f_dentry->d_inode;
-+        const struct proc_dir_entry *dp = ino->u.generic_ip;
-+        zfcp_port_t *port = dp->data;
-+
-+        ZFCP_LOG_TRACE("enter (inode=0x%lx, file=0x%lx)\n",
-+                (unsigned long)inode,
-+                (unsigned long) file);
-+
-+        pbuf = ZFCP_KMALLOC(sizeof(procbuf_t), GFP_KERNEL);
-+        if (pbuf == NULL) {
-+                ZFCP_LOG_NORMAL("error: Not enough memory available for "
-+                               "proc-fs action. Action will be ignored.\n");
-+                retval = -ENOMEM;
-+                goto out;
-+        } else {
-+                file->private_data = ( void * ) pbuf;
-+        }
-+
-+        pbuf->buf = ZFCP_KMALLOC(ZFCP_MAX_PROC_SIZE, GFP_KERNEL);
-+        if (pbuf->buf == NULL) {
-+                ZFCP_LOG_NORMAL("error: Not enough memory available for "
-+                               "proc-fs action. Action will be ignored.\n");
-+                ZFCP_KFREE(pbuf, sizeof(*pbuf));
-+                retval = -ENOMEM;
-+                goto out;
-+        }
-+
-+        ZFCP_LOG_TRACE("Memory for port proc output allocated.\n");
-+
-+        MOD_INC_USE_COUNT;
-+
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        len += sprintf(pbuf->buf+len,
-+                               "Port Information: \n");
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        len += sprintf(pbuf->buf+len,
-+                                "WWNN:         0x%016Lx     "
-+                                "WWPN:              0x%016Lx\n",
-+                                (llui_t)port->wwnn,
-+                                (llui_t)port->wwpn);
-+        len += sprintf(pbuf->buf+len,
-+                                "SCSI ID:              0x%08x     "
-+                                "Max SCSI LUN:              0x%08x\n",
-+                                port->scsi_id,
-+                                port->max_scsi_lun);
-+        len += sprintf(pbuf->buf+len,
-+                                "D_ID:                   0x%06x\n",
-+                                port->d_id);
-+        len += sprintf(pbuf->buf+len,
-+                                "Handle:               0x%08x\n",
-+                                port->handle);
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        len += sprintf(pbuf->buf+len,
-+                                "Attached units:       %10d\n",
-+                                port->units);
-+        len += sprintf(pbuf->buf+len,
-+                                "ERP counter:          0x%08x\n",
-+                                atomic_read(&port->erp_counter));
-+        len += sprintf(pbuf->buf+len,
-+                                "Port Status:          0x%08x\n",
-+                                atomic_read(&port->status));
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        if (proc_debug != 0) {
-+		len += sprintf(pbuf->buf+len,
-+                                        "Port Structure information:\n");
-+                len += sprintf(pbuf->buf+len,
-+                                        "Common Magic:         0x%08x     "
-+                                        "Specific Magic:            0x%08x\n",
-+                                        port->common_magic,
-+                                        port->specific_magic);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Port struct at:       0x%08lx     "
-+                                        "List head at:              0x%08lx\n",
-+                                        (unsigned long) port,
-+                                        (unsigned long) &(port->list));
-+                len += sprintf(pbuf->buf+len,
-+                                        "Next list head:       0x%08lx     "
-+                                        "Previous list head:        0x%08lx\n",
-+                                        (unsigned long) port->list.next,
-+                                        (unsigned long) port->list.prev);
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+                len += sprintf(pbuf->buf+len,
-+                                        "Unit list head at:    0x%08lx\n",
-+                                        (unsigned long) &(port->unit_list_head));
-+                len += sprintf(pbuf->buf+len,
-+                                        "Next list head:       0x%08lx     "
-+                                        "Previous list head:        0x%08lx\n",
-+                                        (unsigned long) port->unit_list_head.next,
-+                                        (unsigned long) port->unit_list_head.prev);
-+                len += sprintf(pbuf->buf+len,
-+                                        "List lock:            0x%08lx     "
-+                                        "List lock owner PC:        0x%08lx\n",
-+                                        port->unit_list_lock.lock,
-+                                        port->unit_list_lock.owner_pc);
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+                len += sprintf(pbuf->buf+len,
-+                                        "Parent adapter at:    0x%08lx\n",
-+                                        (unsigned long) port->adapter);
-+        }
-+
-+        ZFCP_LOG_TRACE("stored %d bytes in proc buffer\n", len);
-+
-+        pbuf->len = len;
-+
-+out:
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * zfcp_unit_proc_open
-+ *
-+ *  - to avoid (SMP) races, allocate buffers for output using
-+ *    the private_data member in the respective file struct
-+ *    such that read() just has to copy out of this buffer
-+ *
-+ */
-+
-+int zfcp_unit_proc_open(struct inode *inode, struct file *file)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int len = 0;
-+        procbuf_t *pbuf;
-+        int retval=0;
-+        const struct inode *ino = file->f_dentry->d_inode;
-+        const struct proc_dir_entry *dp = ino->u.generic_ip;
-+        zfcp_unit_t *unit = dp->data;
-+
-+        ZFCP_LOG_TRACE("enter (inode=0x%lx, file=0x%lx)\n",
-+                (unsigned long)inode,
-+                (unsigned long) file);
-+
-+        pbuf = ZFCP_KMALLOC(sizeof(procbuf_t), GFP_KERNEL);
-+        if (pbuf == NULL) {
-+                ZFCP_LOG_NORMAL("error: Not enough memory available for "
-+                               "proc-fs action. Action will be ignored.\n");
-+                retval = -ENOMEM;
-+                goto out;
-+        } else {
-+                file->private_data = ( void * ) pbuf;
-+        }
-+
-+        pbuf->buf = ZFCP_KMALLOC(ZFCP_MAX_PROC_SIZE, GFP_KERNEL);
-+        if (pbuf->buf == NULL) {
-+                ZFCP_LOG_NORMAL("error: Not enough memory available for "
-+                               "proc-fs action. Action will be ignored.\n");
-+                ZFCP_KFREE(pbuf, sizeof(*pbuf));
-+                retval = -ENOMEM;
-+                goto out;
-+        }
-+
-+        ZFCP_LOG_TRACE("Memory for unit proc output allocated.\n");
-+
-+        MOD_INC_USE_COUNT;
-+
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        len += sprintf(pbuf->buf+len,
-+                                "Unit Information: \n");
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        len += sprintf(pbuf->buf+len,
-+                                "SCSI LUN:             0x%08x     "
-+                                "FCP_LUN:           0x%016Lx\n",
-+                                unit->scsi_lun,
-+                                (llui_t)unit->fcp_lun);
-+        len += sprintf(pbuf->buf+len,
-+                                "Handle:               0x%08x\n",
-+                                unit->handle);
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        len += sprintf(pbuf->buf+len,
-+                                "ERP counter:          0x%08x\n",
-+                                atomic_read(&unit->erp_counter));
-+        len += sprintf(pbuf->buf+len,
-+                                "Unit Status:          0x%08x\n",
-+                                atomic_read(&unit->status));
-+        len += sprintf(pbuf->buf+len,"\n");
-+
-+        if (proc_debug != 0) {
-+        	len += sprintf(pbuf->buf+len,
-+                                        "Unit Structure information:\n");
-+                len += sprintf(pbuf->buf+len,
-+                                        "Common Magic:         0x%08x     "
-+                                        "Specific Magic:            0x%08x\n",
-+                                        unit->common_magic,
-+                                        unit->specific_magic);
-+                len += sprintf(pbuf->buf+len,
-+                                        "Unit struct at:       0x%08lx     "
-+                                        "List head at:              0x%08lx\n",
-+                                        (unsigned long) unit,
-+                                        (unsigned long) &(unit->list));
-+                len += sprintf(pbuf->buf+len,
-+                                        "Next list head:       0x%08lx     "
-+                                        "Previous list head:        0x%08lx\n",
-+                                        (unsigned long) unit->list.next,
-+                                        (unsigned long) unit->list.prev);
-+                len += sprintf(pbuf->buf+len,"\n");
-+
-+                len += sprintf(pbuf->buf+len,
-+                                        "Parent port at:       0x%08lx     "
-+                                        "SCSI dev struct at:        0x%08lx\n",
-+                                        (unsigned long) unit->port,
-+                                        (unsigned long) unit->device);
-+	}
-+
-+        ZFCP_LOG_TRACE("stored %d bytes in proc buffer\n", len);
-+
-+        pbuf->len = len;
-+
-+out:
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+int zfcp_unit_proc_close(struct inode *inode, struct file *file)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int rc=0;
-+        procbuf_t *pbuf = (procbuf_t *) file->private_data;
-+
-+        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
-+                       (unsigned long)inode,
-+                       (unsigned long) file);
-+
-+        if (pbuf) {
-+                if (pbuf->buf) {
-+                        ZFCP_LOG_TRACE("Freeing pbuf->buf\n");
-+                        ZFCP_KFREE(pbuf->buf, ZFCP_MAX_PROC_SIZE);
-+                } else {
-+                        ZFCP_LOG_DEBUG("No procfile buffer found to be freed\n");
-+                }
-+                ZFCP_LOG_TRACE("Freeing pbuf\n");
-+                ZFCP_KFREE(pbuf, sizeof(*pbuf));
-+        } else {
-+                ZFCP_LOG_DEBUG("No procfile buffer found to be freed.\n");
-+        }
-+
-+        ZFCP_LOG_TRACE("exit (%i)\n", rc);
-+
-+        MOD_DEC_USE_COUNT;
-+
-+        return rc;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:    zfcp_unit_proc_read
-+ *
-+ * returns:     number of characters copied to user-space
-+ *              - <error-type> otherwise
-+ */
-+ssize_t zfcp_unit_proc_read(struct file *file,
-+                            char *user_buf,
-+                            size_t user_len,
-+                            loff_t *offset)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        loff_t len;
-+        procbuf_t *pbuf = (procbuf_t *) file->private_data;
-+
-+        ZFCP_LOG_TRACE(
-+          "enter (file=0x%lx  user_buf=0x%lx "
-+          "user_length=%li *offset=0x%lx)\n",
-+          (unsigned long)file,
-+          (unsigned long)user_buf,
-+          user_len,
-+          (unsigned long)*offset);
-+
-+        if ( *offset>=pbuf->len) {
-+                return 0;
-+        } else {
-+                len = min(user_len, (unsigned long)(pbuf->len - *offset));
-+                if (copy_to_user( user_buf, &(pbuf->buf[*offset]), len))
-+                        return -EFAULT;
-+                (* offset) += len;
-+                return len;
-+        }
-+
-+        ZFCP_LOG_TRACE("Size-offset is %ld, user_len is %ld\n",
-+                       ((unsigned long)(pbuf->len  - *offset)),
-+                       user_len);
-+
-+        ZFCP_LOG_TRACE("exit (%Li)\n", len);
-+
-+        return len;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function: zfcp_unit_proc_write
-+ *
-+ */
-+
-+ssize_t zfcp_unit_proc_write(struct file *file,
-+                        const char *user_buf,
-+                        size_t user_len,
-+                        loff_t *offset)
-+
-+{
-+
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        char *buffer = NULL;
-+        size_t my_count = user_len;
-+        const struct inode *ino = file->f_dentry->d_inode;
-+        const struct proc_dir_entry *dp = ino->u.generic_ip;
-+        zfcp_unit_t *unit = dp->data;
-+
-+        ZFCP_LOG_TRACE(
-+                "enter (file=0x%lx  user_buf=0x%lx "
-+                "user_length=%li *offset=0x%lx)\n",
-+                (unsigned long)file,
-+                (unsigned long)user_buf,
-+                user_len,
-+                (unsigned long)*offset);
-+
-+        buffer = ZFCP_KMALLOC(my_count + 1, GFP_KERNEL);
-+        if (!buffer) {
-+                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
-+                                " input. Input will be ignored.\n");
-+                user_len = -ENOMEM;
-+                goto out;
-+        }
-+        ZFCP_LOG_TRACE("buffer allocated...\n");
-+
-+        copy_from_user(buffer, user_buf, my_count);
-+
-+        buffer[my_count] = '\0'; /* for debugging */
-+
-+        ZFCP_LOG_TRACE("user_len= %ld, buffer=>%s<\n",
-+                user_len, buffer);
-+
-+	if ((strncmp(ZFCP_RESET_ERP, buffer, strlen(ZFCP_RESET_ERP)) == 0) ||
-+	    (strncmp(ZFCP_SET_ONLINE, buffer, strlen(ZFCP_SET_ONLINE)) == 0)) {
-+		ZFCP_LOG_NORMAL(
-+			"user triggered (re)start of all operations on the "
-+			"unit with FCP_LUN 0x%016Lx on the port with WWPN 0x%016Lx "
-+			"on the adapter with devno 0x%04x\n",
-+			(llui_t)unit->fcp_lun,
-+			(llui_t)unit->port->wwpn,
-+			unit->port->adapter->devno);
-+		zfcp_erp_modify_unit_status(
-+			unit,
-+			ZFCP_STATUS_COMMON_RUNNING,
-+			ZFCP_SET);
-+		zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED);
-+		zfcp_erp_wait(unit->port->adapter);
-+		user_len = strlen(buffer);
-+	} else	if (strncmp(ZFCP_SET_OFFLINE, buffer, strlen(ZFCP_SET_OFFLINE)) == 0) {
-+		ZFCP_LOG_NORMAL(
-+			"user triggered shutdown of all operations on the "
-+			"unit with FCP_LUN 0x%016Lx on the port with WWPN 0x%016Lx "
-+			"on the adapter with devno 0x%04x\n",
-+			(llui_t)unit->fcp_lun,
-+			(llui_t)unit->port->wwpn,
-+			unit->port->adapter->devno);
-+		zfcp_erp_unit_shutdown(unit, 0);
-+		zfcp_erp_wait(unit->port->adapter);
-+		user_len = strlen(buffer);
-+	} else  {
-+		ZFCP_LOG_INFO("error: unknown procfs command\n");
-+		user_len = -EINVAL;
-+	}
-+
-+        ZFCP_LOG_TRACE("freeing buffer..\n");
-+        ZFCP_KFREE(buffer, my_count + 1);
-+out:
-+        ZFCP_LOG_TRACE("exit (%li)\n", user_len);
-+        return (user_len);
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:	zfcp_scsi_detect
-+ *
-+ * purpose:	This routine is called by the SCSI stack mid layer
-+ *		to query detected host bus adapters.
-+ *
-+ * returns:	number of detcted HBAs (0, if no HBAs detected)
-+ */
-+int zfcp_scsi_detect(Scsi_Host_Template *shtpnt)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+     
-+	int adapters = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (shtpnt =0x%lx)\n",
-+		(unsigned long) shtpnt);
-+
-+	spin_unlock_irq(&io_request_lock);
-+	adapters = zfcp_adapter_scsi_register_all();
-+	spin_lock_irq(&io_request_lock);
-+
-+	ZFCP_LOG_TRACE(
-+		"exit (adapters =%d)\n",
-+		adapters);
-+
-+	return adapters;
-+     
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	for all adapters which are not yet registered with SCSI stack:
-+ *		wait for finish of erp and register adapter with SCSI stack then 
-+ *
-+ * returns:	number of adapters registered with SCSI stack
-+ *
-+ * FIXME(design):	/proc/scsi/zfcp/add-del_map must be locked as long as we
-+ *			are in such a loop as implemented here.
-+ *			We need a guarantee that no adapter will (dis)sappear.
-+ *			Otherwise list corruption may be caused.
-+ *			(We can't hold the lock all the time due to possible
-+ *			 calls to schedule())
-+ */
-+static int zfcp_adapter_scsi_register_all()
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	int retval = 0;
-+	unsigned long flags;
-+	zfcp_adapter_t *adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	read_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
-+	adapter = ZFCP_FIRST_ADAPTER;
-+	while (adapter) {
-+		read_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
-+		if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status)) {
-+			ZFCP_LOG_DEBUG(
-+				"adapter with devno 0x%04x needs "
-+				"to be registered with SCSI stack, "
-+				"waiting for erp to settle\n",
-+				adapter->devno);
-+			zfcp_erp_wait(adapter);
-+			if (zfcp_adapter_scsi_register(adapter) == 0);
-+				retval++;
-+		}
-+		read_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
-+		adapter = ZFCP_NEXT_ADAPTER(adapter);
-+	}
-+	read_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+void zfcp_scsi_select_queue_depth(struct Scsi_Host *host, Scsi_Device *dev_list)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        zfcp_adapter_t *adapter = (zfcp_adapter_t *)host->hostdata[0];
-+        zfcp_port_t *port = NULL;
-+        zfcp_unit_t *unit = NULL;
-+        unsigned long flags=0;
-+        
-+        ZFCP_LOG_TRACE("enter (host =0x%lx, dev_list=0x%lx)\n",
-+                       (unsigned long) host,
-+                       (unsigned long) dev_list);
-+        
-+        read_lock_irqsave(&adapter->port_list_lock, flags);
-+        ZFCP_FOR_EACH_PORT(adapter, port) {
-+                read_lock(&port->unit_list_lock);
-+                ZFCP_FOR_EACH_UNIT(port, unit) {
-+                        ZFCP_LOG_DEBUG("Determinig if unit 0x%lx"
-+                                       " supports tagging\n",
-+                                       (unsigned long) unit);
-+                        if (!unit->device) 
-+                                continue; 
-+
-+                        if (unit->device->tagged_supported) {
-+                                ZFCP_LOG_DEBUG("Enabling tagging for "
-+                                               "unit 0x%lx \n",
-+                                               (unsigned long) unit);
-+                                unit->device->tagged_queue = 1;
-+                                unit->device->current_tag = 0;
-+                                unit->device->queue_depth = ZFCP_CMND_PER_LUN;
-+				atomic_set_mask(ZFCP_STATUS_UNIT_ASSUMETCQ, &unit->status);
-+                        } else {
-+                                ZFCP_LOG_DEBUG("Disabling tagging for "
-+                                               "unit 0x%lx \n",
-+                                               (unsigned long) unit);
-+                                unit->device->tagged_queue = 0;
-+                                unit->device->current_tag = 0;
-+                                unit->device->queue_depth = 1;
-+				atomic_clear_mask(ZFCP_STATUS_UNIT_ASSUMETCQ, &unit->status);
-+                        }
-+		}
-+		read_unlock(&port->unit_list_lock);
-+	}
-+	read_unlock_irqrestore(&adapter->port_list_lock, flags);
-+
-+        ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_scsi_revoke
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ */
-+int zfcp_scsi_revoke(Scsi_Device *sdpnt)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	int retval = 0;
-+	zfcp_unit_t *unit = (zfcp_unit_t*) sdpnt->hostdata;
-+#if 0
-+	zfcp_port_t *port = unit->port;
-+#endif
-+
-+	ZFCP_LOG_TRACE("enter (sdpnt=0x%lx)\n", (unsigned long)sdpnt);
-+
-+	if (!unit) {
-+		ZFCP_LOG_INFO(
-+			"no unit associated with SCSI device at "
-+			"address 0x%lx\n",
-+			(unsigned long)sdpnt);
-+		goto out;
-+	}
-+
-+#if 0
-+	/* Shutdown entire port if we are going to shutdown the last unit. */
-+	if (port->units == 1) {
-+                zfcp_erp_port_shutdown(port, 0);
-+                zfcp_erp_wait(port->adapter);
-+        } else {
-+                zfcp_erp_unit_shutdown(unit, 0);
-+                zfcp_erp_wait(port->adapter);
-+        }
-+#endif
-+	sdpnt->hostdata = NULL;
-+	unit->device = NULL;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_scsi_release
-+ *
-+ * purpose:	called from SCSI stack mid layer to make this driver
-+ *		cleanup I/O and resources for this adapter
-+ *
-+ * returns:
-+ */
-+int zfcp_scsi_release(struct Scsi_Host *shpnt)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter (shpnt=0x%lx)\n", (unsigned long)shpnt);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_scsi_insert_into_fake_queue
-+ *
-+ * purpose:
-+ *		
-+ *
-+ * returns:
-+ */
-+inline void zfcp_scsi_insert_into_fake_queue(zfcp_adapter_t *adapter, Scsi_Cmnd *new_cmnd)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        unsigned long flags;
-+        Scsi_Cmnd *current_cmnd;
-+
-+	ZFCP_LOG_TRACE("enter (adapter=0x%lx, cmnd=0x%lx)\n", 
-+                       (unsigned long)adapter,
-+                       (unsigned long)new_cmnd);
-+
-+        ZFCP_LOG_DEBUG("Faking SCSI command:\n");
-+        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-+                      (char*)new_cmnd->cmnd,
-+                      new_cmnd->cmd_len);
-+        
-+        new_cmnd->host_scribble = NULL;
-+
-+        write_lock_irqsave(&adapter->fake_list_lock,flags);
-+        if(adapter->first_fake_cmnd==NULL) {
-+                adapter->first_fake_cmnd = new_cmnd;
-+                adapter->fake_scsi_timer.function = 
-+                        zfcp_scsi_process_and_clear_fake_queue;
-+                adapter->fake_scsi_timer.data = 
-+                        (unsigned long)adapter;
-+                adapter->fake_scsi_timer.expires = 
-+                        jiffies + ZFCP_FAKE_SCSI_COMPLETION_TIME;
-+                add_timer(&adapter->fake_scsi_timer);
-+        } else {        
-+                for(current_cmnd=adapter->first_fake_cmnd;
-+                    current_cmnd->host_scribble != NULL;
-+                    current_cmnd = (Scsi_Cmnd *)(current_cmnd->host_scribble));
-+                current_cmnd->host_scribble = (char *)new_cmnd;
-+        }
-+	atomic_inc(&adapter->fake_scsi_reqs_active);
-+        write_unlock_irqrestore(&adapter->fake_list_lock,flags);
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+ 
-+	return;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_scsi_process_and_clear_fake_queue
-+ *
-+ * purpose:
-+ *		
-+ *
-+ * returns:
-+ */
-+inline void zfcp_scsi_process_and_clear_fake_queue(unsigned long data)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        unsigned long flags;
-+        Scsi_Cmnd *current_cmnd;
-+        Scsi_Cmnd *next_cmnd;
-+        zfcp_adapter_t *adapter=(zfcp_adapter_t *)data;
-+
-+	ZFCP_LOG_TRACE("enter (data=0x%lx)\n", data);
-+
-+        /*
-+         * We need a common lock for scsi_req on command completion
-+         * as well as on command abort to avoid race conditions
-+         * during completions and aborts taking place at the same time.
-+         * It needs to be the outer lock as in the eh_abort_handler.
-+         */
-+        read_lock_irqsave(&adapter->abort_lock, flags);
-+        write_lock(&adapter->fake_list_lock);
-+        if(adapter->first_fake_cmnd == NULL) {
-+                ZFCP_LOG_DEBUG("Processing of fake-queue called "
-+                               "for an empty queue.\n");
-+        } else {        
-+                current_cmnd=adapter->first_fake_cmnd;
-+                do {
-+                        next_cmnd=(Scsi_Cmnd *)(current_cmnd->host_scribble);
-+                        current_cmnd->host_scribble = NULL;
-+#if 0
-+			zfcp_cmd_dbf_event_scsi("clrfake", adapter, current_cmnd);
-+#endif
-+                        current_cmnd->scsi_done(current_cmnd);
-+#ifdef ZFCP_DEBUG_REQUESTS
-+                        debug_text_event(adapter->req_dbf, 2, "fk_done:");
-+                        debug_event(adapter->req_dbf, 2, &current_cmnd, sizeof(unsigned long));
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+			atomic_dec(&adapter->fake_scsi_reqs_active);
-+                        current_cmnd=next_cmnd;
-+                } while (next_cmnd != NULL);
-+                /* Set list to empty */
-+                adapter->first_fake_cmnd = NULL;
-+        }
-+        write_unlock(&adapter->fake_list_lock);
-+        read_unlock_irqrestore(&adapter->abort_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+ 
-+	return;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+static void zfcp_scsi_command_fail(
-+		zfcp_unit_t *unit,
-+		Scsi_Cmnd *scsi_cmnd,
-+		int result)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	zfcp_adapter_t *adapter = unit->port->adapter;
-+
-+#ifdef ZFCP_DEBUG_REQUESTS
-+	debug_text_event(adapter->req_dbf, 2, "de_done:");
-+	debug_event(adapter->req_dbf, 2, &scsi_cmnd, sizeof(unsigned long));
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+
-+	scsi_cmnd->SCp.ptr = (char*)0;
-+	scsi_cmnd->result = result;
-+
-+	zfcp_cmd_dbf_event_scsi("failing", adapter, scsi_cmnd);
-+
-+	scsi_cmnd->scsi_done(scsi_cmnd);
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+ 
-+static void zfcp_scsi_command_fake(
-+		zfcp_unit_t *unit,
-+		Scsi_Cmnd *scsi_cmnd)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	if (scsi_cmnd->SCp.ptr) {
-+		if (((unsigned long)scsi_cmnd->SCp.ptr + ZFCP_SCSI_RETRY_TIMEOUT)
-+		    < jiffies) {
-+			/* leave it to the SCSI stack eh */
-+			zfcp_scsi_command_fail(unit, scsi_cmnd, DID_TIME_OUT << 16);
-+			return;
-+		}
-+	} else	scsi_cmnd->SCp.ptr = (char*)jiffies;
-+	scsi_cmnd->retries--;	/* -1 is ok */
-+	scsi_cmnd->result |= DID_SOFT_ERROR << 16
-+			     | SUGGEST_RETRY << 24;
-+	zfcp_scsi_insert_into_fake_queue(unit->port->adapter, scsi_cmnd);
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/**
-+ * zfcp_scsi_command_async - worker for zfcp_scsi_queuecommand and
-+ * zfcp_scsi_command_sync
-+ */
-+int zfcp_scsi_command_async(
-+		zfcp_unit_t *unit,
-+		Scsi_Cmnd *scsi_cmnd,
-+		void (* done)(Scsi_Cmnd *))
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	scsi_cmnd->scsi_done = done;
-+	scsi_cmnd->result = 0;
-+
-+	if (!unit) {
-+		zfcp_scsi_command_fail(unit, scsi_cmnd, DID_NO_CONNECT << 16);
-+		goto out;
-+	}
-+
-+	if (atomic_test_mask(
-+			ZFCP_STATUS_COMMON_ERP_FAILED,
-+			&unit->status)) {
-+		zfcp_scsi_command_fail(unit, scsi_cmnd, DID_ERROR << 16);
-+		goto out;
-+	}
-+
-+	if (!atomic_test_mask(
-+			ZFCP_STATUS_COMMON_RUNNING,
-+			&unit->status)) {
-+		zfcp_scsi_command_fail(unit, scsi_cmnd, DID_ERROR << 16);
-+		goto out;
-+	}
-+
-+	if (!atomic_test_mask(
-+			ZFCP_STATUS_COMMON_UNBLOCKED,
-+			&unit->status))  {
-+		zfcp_scsi_command_fake(unit, scsi_cmnd);
-+		goto out;
-+	}
-+
-+	if (zfcp_fsf_send_fcp_command_task(unit, scsi_cmnd) < 0)
-+		zfcp_scsi_command_fake(unit, scsi_cmnd);
-+
-+out:
-+	return 0;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+void zfcp_scsi_command_sync_handler(Scsi_Cmnd *scsi_cmnd)
-+{
-+	struct completion *wait = (struct completion*) scsi_cmnd->bh_next;
-+	complete(wait);
-+}
-+
-+
-+/**
-+ * zfcp_scsi_command_sync - send a SCSI command and wait for completion
-+ * returns 0, errors are indicated by scsi_cmnd->result
-+ */
-+int zfcp_scsi_command_sync(
-+		zfcp_unit_t *unit,
-+		Scsi_Cmnd *scsi_cmnd)
-+{
-+	DECLARE_COMPLETION(wait);
-+
-+	scsi_cmnd->bh_next = (void*) &wait;  /* silent re-use */
-+        zfcp_scsi_command_async(
-+			unit,
-+			scsi_cmnd,
-+			zfcp_scsi_command_sync_handler);
-+	wait_for_completion(&wait);
-+
-+	return 0;
-+}
-+
-+
-+ 
-+/*
-+ * function:	zfcp_scsi_queuecommand
-+ *
-+ * purpose:	enqueues a SCSI command to the specified target device
-+ *
-+ * note:        The scsi_done midlayer function may be called directly from
-+ *              within queuecommand provided queuecommand returns with success (0)
-+ *              If it fails, it is expected that the command could not be sent
-+ *              and is still available for processing.
-+ *              As we ensure that queuecommand never fails, we have the choice 
-+ *              to call done directly wherever we please.
-+ *              Thus, any kind of send errors other than those indicating
-+ *              'infinite' retries will be reported directly.
-+ *              Retry requests are put into a list to be processed under timer 
-+ *              control once in a while to allow for other operations to
-+ *              complete in the meantime.
-+ *
-+ * returns:	0 - success, SCSI command enqueued
-+ *		!0 - failure, note that we never allow this to happen as the 
-+ *              SCSI stack would block indefinitely should a non-zero return
-+ *              value be reported if there are no outstanding commands
-+ *              (as in when the queues are down)
-+ */
-+int zfcp_scsi_queuecommand(
-+		Scsi_Cmnd *scsi_cmnd,
-+		void (* done)(Scsi_Cmnd *))
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	zfcp_unit_t *unit;
-+	zfcp_adapter_t *adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (scsi_cmnd=0x%lx done=0x%lx)\n",
-+		(unsigned long)scsi_cmnd,
-+		(unsigned long)done);
-+
-+	spin_unlock_irq(&io_request_lock);
-+
-+	/*
-+	 * figure out adapter
-+	 * (previously stored there by the driver when
-+	 * the adapter was registered)
-+	 */
-+	adapter = (zfcp_adapter_t*) scsi_cmnd->host->hostdata[0];
-+
-+	/*
-+	 * figure out target device
-+	 * (stored there by the driver when the first command
-+	 * is sent to this target device)
-+	 * ATTENTION: assumes hostdata initialized to NULL by
-+	 * mid layer (see scsi_scan.c)
-+	 */
-+	if (!scsi_cmnd->device->hostdata) {
-+		unit = zfcp_unit_lookup(
-+				adapter,
-+				scsi_cmnd->device->channel,
-+				scsi_cmnd->device->id,
-+				scsi_cmnd->device->lun);
-+		/* Is specified unit configured? */
-+		if (unit) {
-+			scsi_cmnd->device->hostdata = unit;
-+			unit->device = scsi_cmnd->device;
-+			ZFCP_LOG_DEBUG(
-+				"logical unit address (0x%lx) saved "
-+				"for direct lookup and scsi_stack "
-+				"pointer 0x%lx saved in unit structure\n",
-+				(unsigned long)unit,
-+				(unsigned long)unit->device);
-+		}
-+	} else	unit = (zfcp_unit_t*) scsi_cmnd->device->hostdata;
-+
-+	zfcp_scsi_command_async(unit, scsi_cmnd, done);
-+
-+	spin_lock_irq(&io_request_lock);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", 0);
-+
-+	return 0;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_unit_lookup
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ *
-+ * context:	
-+ */
-+static zfcp_unit_t* zfcp_unit_lookup(
-+	zfcp_adapter_t *adapter,
-+	int channel,
-+	int id,
-+	int lun)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+	zfcp_port_t *port; 
-+	zfcp_unit_t *unit = NULL;
-+	unsigned long flags;
-+ 
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter devno=0x%04x, channel=%i, id=%i, lun=%i)\n",
-+		adapter->devno,
-+		channel,
-+		id,
-+		lun);
-+
-+	read_lock_irqsave(&adapter->port_list_lock, flags);
-+	ZFCP_FOR_EACH_PORT(adapter, port) {
-+		if ((scsi_id_t)id != port->scsi_id)
-+			continue;
-+		read_lock(&port->unit_list_lock);
-+		ZFCP_FOR_EACH_UNIT(port, unit) {
-+			if ((scsi_lun_t)lun == unit->scsi_lun) {
-+				ZFCP_LOG_TRACE("found unit\n");
-+				break;
-+			}
-+		}
-+		read_unlock(&port->unit_list_lock);
-+		if (unit)
-+			break;
-+	}
-+	read_unlock_irqrestore(&adapter->port_list_lock, flags);
-+ 
-+	ZFCP_LOG_TRACE("exit (0x%lx)\n", (unsigned long)unit);
-+ 
-+	return unit;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_scsi_potential_abort_on_fake
-+ *
-+ * purpose:
-+ *
-+ * returns:     0 - no fake request aborted
-+ *              1 - fake request was aborted
-+ *
-+ * context:	both the adapter->abort_lock and the 
-+ *              adapter->fake_list_lock are assumed to be held write lock
-+ *              irqsave
-+ */
-+inline int zfcp_scsi_potential_abort_on_fake(zfcp_adapter_t *adapter, Scsi_Cmnd *cmnd)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        Scsi_Cmnd *current_cmnd, *prev_cmnd;
-+	unsigned long flags;
-+        int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter (adapter=0x%lx, cmnd=0x%lx)\n",
-+                       (unsigned long)adapter,
-+                       (unsigned long)cmnd);
-+
-+	write_lock_irqsave(&adapter->fake_list_lock, flags);
-+
-+        current_cmnd=adapter->first_fake_cmnd;
-+
-+	if (!current_cmnd)
-+		goto out;
-+
-+        if(current_cmnd==cmnd) {
-+                adapter->first_fake_cmnd=(Scsi_Cmnd *)cmnd->host_scribble;
-+                cmnd->host_scribble=NULL;
-+                if(adapter->first_fake_cmnd==NULL) {
-+                        /* No need to wake anymore  */
-+                        /* Note: It does not matter if the timer has already
-+                         * expired, the fake_list_lock takes care of 
-+                         * potential races 
-+                         */
-+                        del_timer(&adapter->fake_scsi_timer);
-+                }
-+		atomic_dec(&adapter->fake_scsi_reqs_active);
-+                retval=1;
-+                goto out;
-+        } 
-+        do {
-+                prev_cmnd = current_cmnd;
-+                current_cmnd = (Scsi_Cmnd *)(current_cmnd->host_scribble);
-+                if (current_cmnd==cmnd) {
-+                        prev_cmnd->host_scribble=current_cmnd->host_scribble;
-+                        current_cmnd->host_scribble=NULL;
-+			atomic_dec(&adapter->fake_scsi_reqs_active);
-+                        retval=1;
-+                        goto out;
-+                }
-+        } while (current_cmnd->host_scribble != NULL);         
-+
-+out:
-+	write_unlock_irqrestore(&adapter->fake_list_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+                }        
-+
-+
-+/*
-+ * function:	zfcp_scsi_eh_abort_handler
-+ *
-+ * purpose:	tries to abort the specified (timed out) SCSI command
-+ *
-+ * note: 	We do not need to care for a SCSI command which completes
-+ *		normally but late during this abort routine runs.
-+ *		We are allowed to return late commands to the SCSI stack.
-+ *		It tracks the state of commands and will handle late commands.
-+ *		(Usually, the normal completion of late commands is ignored with
-+ *		respect to the running abort operation. Grep for 'done_late'
-+ *		in the SCSI stacks sources.)
-+ *
-+ * returns:	SUCCESS	- command has been aborted and cleaned up in internal
-+ *			  bookkeeping,
-+ *			  SCSI stack won't be called for aborted command
-+ *		FAILED	- otherwise
-+ */
-+int zfcp_scsi_eh_abort_handler(Scsi_Cmnd *scpnt)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	int retval = SUCCESS;
-+	zfcp_fsf_req_t *new_fsf_req, *old_fsf_req;
-+	zfcp_adapter_t *adapter = (zfcp_adapter_t*) scpnt->host->hostdata[0];
-+	zfcp_unit_t *unit = (zfcp_unit_t*) scpnt->device->hostdata;
-+	zfcp_port_t *port = unit->port;
-+	unsigned long flags;
-+	u32 status = 0;
-+#ifdef ZFCP_DEBUG_ABORTS
-+	/* the components of a abort_dbf record (fixed size record) */
-+	u64		dbf_scsi_cmnd	= (unsigned long)scpnt;
-+	char		dbf_opcode[ZFCP_ABORT_DBF_LENGTH];
-+	wwn_t		dbf_wwn		= port->wwpn;
-+	fcp_lun_t	dbf_fcp_lun	= unit->fcp_lun;
-+	u64		dbf_retries	= scpnt->retries;
-+	u64		dbf_allowed	= scpnt->allowed;
-+	u64		dbf_timeout	= 0;
-+	u64		dbf_fsf_req	= 0;
-+	u64		dbf_fsf_status	= 0;
-+	u64		dbf_fsf_qual[2]	= { 0, 0 };
-+	char		dbf_result[ZFCP_ABORT_DBF_LENGTH]
-+					= { "##undef" };
-+
-+	memset(dbf_opcode, 0, ZFCP_ABORT_DBF_LENGTH);
-+	memcpy(	dbf_opcode,
-+		scpnt->cmnd,
-+		min(scpnt->cmd_len, (unsigned char)ZFCP_ABORT_DBF_LENGTH));
-+#endif
-+
-+        /*TRACE*/
-+	ZFCP_LOG_TRACE("enter (scpnt=0x%lx)\n", (unsigned long)scpnt);
-+
-+	ZFCP_LOG_INFO(
-+		"Aborting for adapter=0x%lx, devno=0x%04x, scsi_cmnd=0x%lx\n",
-+		(unsigned long)adapter,
-+		adapter->devno,
-+		(unsigned long)scpnt);
-+
-+	spin_unlock_irq(&io_request_lock);
-+#if 0
-+               /* DEBUG */
-+        retval=FAILED;
-+        goto out;
-+#endif
-+
-+	/*
-+	 * Race condition between normal (late) completion and abort has
-+	 * to be avoided.
-+	 * The entirity of all accesses to scsi_req have to be atomic.
-+	 * scsi_req is usually part of the fsf_req (for requests which
-+	 * are not faked) and thus we block the release of fsf_req
-+	 * as long as we need to access scsi_req.
-+	 * For faked commands we use the same lock even if they are not
-+	 * put into the fsf_req queue. This makes implementation
-+	 * easier. 
-+	 */
-+	write_lock_irqsave(&adapter->abort_lock, flags);
-+
-+	/*
-+	 * Check if we deal with a faked command, which we may just forget
-+	 * about from now on
-+	 */
-+	if (zfcp_scsi_potential_abort_on_fake(adapter, scpnt)) {
-+		write_unlock_irqrestore(&adapter->abort_lock, flags);
-+#ifdef ZFCP_DEBUG_ABORTS
-+		strncpy(dbf_result, "##faked", ZFCP_ABORT_DBF_LENGTH);
-+#endif
-+		retval = SUCCESS;
-+		goto out;
-+	}
-+
-+	/*
-+	 * Check whether command has just completed and can not be aborted.
-+	 * Even if the command has just been completed late, we can access
-+	 * scpnt since the SCSI stack does not release it at least until
-+	 * this routine returns. (scpnt is parameter passed to this routine
-+	 * and must not disappear during abort even on late completion.)
-+	 */
-+	old_fsf_req = (zfcp_fsf_req_t*) scpnt->host_scribble;
-+	if (!old_fsf_req) {
-+		ZFCP_LOG_DEBUG("late command completion overtook abort\n");
-+		/*
-+		 * That's it.
-+		 * Do not initiate abort but return SUCCESS.
-+		 */
-+		write_unlock_irqrestore(&adapter->abort_lock, flags);
-+		retval = SUCCESS;
-+#ifdef ZFCP_DEBUG_ABORTS
-+		strncpy(dbf_result, "##late1", ZFCP_ABORT_DBF_LENGTH);
-+#endif
-+		goto out;
-+	}
-+#ifdef ZFCP_DEBUG_ABORTS
-+	dbf_fsf_req = (unsigned long)old_fsf_req;
-+	dbf_timeout = (jiffies - old_fsf_req->data.send_fcp_command_task.start_jiffies) / HZ;
-+#endif
-+
-+        old_fsf_req->data.send_fcp_command_task.scsi_cmnd = NULL;
-+	/* mark old request as being aborted */
-+	old_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTING;
-+	/*
-+	 * We have to collect all information (e.g. unit) needed by 
-+	 * zfcp_fsf_abort_fcp_command before calling that routine
-+	 * since that routine is not allowed to access
-+	 * fsf_req which it is going to abort.
-+	 * This is because of we need to release fsf_req_list_lock
-+	 * before calling zfcp_fsf_abort_fcp_command.
-+	 * Since this lock will not be held, fsf_req may complete
-+	 * late and may be released meanwhile.
-+	 */
-+	ZFCP_LOG_DEBUG(
-+		"unit=0x%lx, unit_fcp_lun=0x%Lx\n",
-+		(unsigned long)unit,
-+		(llui_t)unit->fcp_lun);
-+
-+	/*
-+	 * We block (call schedule)
-+	 * That's why we must release the lock and enable the
-+	 * interrupts before.
-+	 * On the other hand we do not need the lock anymore since
-+	 * all critical accesses to scsi_req are done.
-+	 */
-+	write_unlock_irqrestore(&adapter->abort_lock, flags);
-+	/* call FSF routine which does the abort */
-+	new_fsf_req = zfcp_fsf_abort_fcp_command(
-+				(unsigned long)old_fsf_req, adapter, unit, 0);
-+	ZFCP_LOG_DEBUG(
-+		"new_fsf_req=0x%lx\n",
-+		(unsigned long) new_fsf_req);
-+	if (!new_fsf_req) {
-+		retval = FAILED;
-+		ZFCP_LOG_DEBUG(
-+			"warning: Could not abort SCSI command "
-+			"at 0x%lx\n",
-+			(unsigned long)scpnt);
-+#ifdef ZFCP_DEBUG_ABORTS
-+		strncpy(dbf_result, "##nores", ZFCP_ABORT_DBF_LENGTH);
-+#endif
-+		goto out;
-+	}
-+
-+	/* wait for completion of abort */
-+	ZFCP_LOG_DEBUG("Waiting for cleanup....\n");
-+#ifdef ZFCP_DEBUG_ABORTS
-+	/* FIXME: copying zfcp_fsf_req_wait_and_cleanup code is not really nice */
-+	__wait_event(
-+		new_fsf_req->completion_wq,
-+		new_fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
-+	status = new_fsf_req->status;
-+	dbf_fsf_status = new_fsf_req->qtcb->header.fsf_status;
-+	/*
-+	 * Ralphs special debug load provides timestamps in the FSF
-+	 * status qualifier. This might be specified later if being
-+	 * useful for debugging aborts.
-+	 */
-+	dbf_fsf_qual[0] = *(u64*)&new_fsf_req->qtcb->header.fsf_status_qual.word[0];
-+	dbf_fsf_qual[1] = *(u64*)&new_fsf_req->qtcb->header.fsf_status_qual.word[2];
-+	retval = zfcp_fsf_req_cleanup(new_fsf_req);
-+#else
-+	retval = zfcp_fsf_req_wait_and_cleanup(
-+			new_fsf_req,
-+			ZFCP_UNINTERRUPTIBLE,
-+			&status);
-+#endif
-+	ZFCP_LOG_DEBUG(
-+		"Waiting for cleanup complete, status=0x%x\n",
-+		status);
-+	/* status should be valid since signals were not permitted */
-+	if (status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) {
-+		retval = SUCCESS;
-+#ifdef ZFCP_DEBUG_ABORTS
-+		strncpy(dbf_result, "##succ", ZFCP_ABORT_DBF_LENGTH);
-+#endif
-+	} else	if (status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) {
-+		retval = SUCCESS;
-+#ifdef ZFCP_DEBUG_ABORTS
-+		strncpy(dbf_result, "##late2", ZFCP_ABORT_DBF_LENGTH);
-+#endif
-+	} else	{
-+		retval = FAILED;
-+#ifdef ZFCP_DEBUG_ABORTS
-+		strncpy(dbf_result, "##fail", ZFCP_ABORT_DBF_LENGTH);
-+#endif
-+	}
-+
-+out:
-+#ifdef ZFCP_DEBUG_ABORTS
-+	debug_event(adapter->abort_dbf, 1, &dbf_scsi_cmnd, sizeof(u64));
-+	debug_event(adapter->abort_dbf, 1, &dbf_opcode, ZFCP_ABORT_DBF_LENGTH);
-+	debug_event(adapter->abort_dbf, 1, &dbf_wwn, sizeof(wwn_t));
-+	debug_event(adapter->abort_dbf, 1, &dbf_fcp_lun, sizeof(fcp_lun_t));
-+	debug_event(adapter->abort_dbf, 1, &dbf_retries, sizeof(u64));
-+	debug_event(adapter->abort_dbf, 1, &dbf_allowed, sizeof(u64));
-+	debug_event(adapter->abort_dbf, 1, &dbf_timeout, sizeof(u64));
-+	debug_event(adapter->abort_dbf, 1, &dbf_fsf_req, sizeof(u64));
-+	debug_event(adapter->abort_dbf, 1, &dbf_fsf_status, sizeof(u64));
-+	debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[0], sizeof(u64));
-+	debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[1], sizeof(u64));
-+	debug_text_event(adapter->abort_dbf, 1, dbf_result);
-+#endif
-+
-+	spin_lock_irq(&io_request_lock);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_scsi_eh_device_reset_handler
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ */
-+int zfcp_scsi_eh_device_reset_handler(Scsi_Cmnd *scpnt)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	int retval;
-+	zfcp_unit_t *unit = (zfcp_unit_t*) scpnt->device->hostdata;
-+        /*TRACE*/
-+	ZFCP_LOG_TRACE("enter (scpnt=0x%lx)\n", (unsigned long)scpnt);
-+
-+        spin_unlock_irq(&io_request_lock);
-+	/*
-+	 * We should not be called to reset a target which we 'sent' faked SCSI
-+	 * commands since the abort of faked SCSI commands should always
-+	 * succeed (simply delete timer). 
-+	 */
-+	if (!unit) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Tried to reset a non existant unit.\n");
-+		retval = SUCCESS;
-+		goto out;
-+	}
-+	ZFCP_LOG_NORMAL(
-+		"Resetting SCSI device "
-+		"(unit with FCP_LUN 0x%016Lx on the port with WWPN 0x%016Lx "
-+		"on the adapter with devno 0x%04x)\n",
-+		(llui_t)unit->fcp_lun,
-+		(llui_t)unit->port->wwpn,
-+		unit->port->adapter->devno);
-+
-+	/*
-+	 * If we do not know whether the unit supports 'logical unit reset'
-+	 * then try 'logical unit reset' and proceed with 'target reset'
-+	 * if 'logical unit reset' fails.
-+	 * If the unit is known not to support 'logical unit reset' then
-+	 * skip 'logical unit reset' and try 'target reset' immediately.
-+	 */
-+	if (!atomic_test_mask(ZFCP_STATUS_UNIT_NOTSUPPUNITRESET, &unit->status)) { 
-+		retval = zfcp_task_management_function(unit, LOGICAL_UNIT_RESET);
-+		if (retval) {
-+			ZFCP_LOG_DEBUG(
-+				"logical unit reset failed (unit=0x%lx)\n",
-+				(unsigned long)unit);
-+			if (retval == -ENOTSUPP)
-+				atomic_set_mask(ZFCP_STATUS_UNIT_NOTSUPPUNITRESET, 
-+                                                &unit->status); 
-+			/* fall through and try 'target reset' next */
-+		} else	{
-+			ZFCP_LOG_DEBUG(
-+				"logical unit reset succeeded (unit=0x%lx)\n",
-+				(unsigned long)unit);
-+			/* avoid 'target reset' */
-+			retval = SUCCESS;
-+			goto out;
-+		}
-+	}
-+	retval = zfcp_task_management_function(unit, TARGET_RESET);
-+	if (retval) {
-+		ZFCP_LOG_DEBUG(
-+			"target reset failed (unit=0x%lx)\n",
-+			(unsigned long)unit);
-+		retval = FAILED;
-+	} else	{
-+		ZFCP_LOG_DEBUG(
-+			"target reset succeeded (unit=0x%lx)\n",
-+			(unsigned long)unit);
-+		retval = SUCCESS;
-+	}
-+
-+out:
-+	spin_lock_irq(&io_request_lock);
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+static int zfcp_task_management_function(zfcp_unit_t *unit, u8 tm_flags)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	zfcp_adapter_t *adapter = unit->port->adapter;
-+	int retval;
-+	int status;
-+	zfcp_fsf_req_t *fsf_req;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (unit=0x%lx tm_flags=0x%x)\n",
-+		(unsigned long)unit,
-+		tm_flags);
-+
-+	/* issue task management function */	
-+	fsf_req = zfcp_fsf_send_fcp_command_task_management
-+	 		(adapter, unit, tm_flags, 0);
-+	if (!fsf_req) {
-+                ZFCP_LOG_INFO(
-+			"error: Out of resources. Could not create a "
-+			"task management (abort, reset, etc) request "
-+			"for the unit with FCP_LUN 0x%016Lx connected to "
-+			"the port with WWPN 0x%016Lx connected to "
-+			"the adapter with devno 0x%04x.\n",
-+			(llui_t)unit->fcp_lun,
-+			(llui_t)unit->port->wwpn,
-+			adapter->devno);
-+		retval = -ENOMEM;
-+		goto out;
-+	}
-+
-+	retval = zfcp_fsf_req_wait_and_cleanup(
-+			fsf_req,
-+                        ZFCP_UNINTERRUPTIBLE,
-+			&status);
-+	/*
-+	 * check completion status of task management function
-+	 * (status should always be valid since no signals permitted)
-+	 */
-+        if (status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED)
-+		retval = -EIO;
-+	else	if (status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP)
-+			retval = -ENOTSUPP;
-+		else	retval = 0;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_scsi_eh_bus_reset_handler
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ */
-+int zfcp_scsi_eh_bus_reset_handler(Scsi_Cmnd *scpnt)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	int retval = 0;
-+        zfcp_unit_t *unit;
-+
-+	ZFCP_LOG_TRACE("enter (scpnt=0x%lx)\n", (unsigned long)scpnt);
-+        spin_unlock_irq(&io_request_lock);
-+
-+        unit = (zfcp_unit_t *)scpnt->device->hostdata; 
-+        /*DEBUG*/
-+	ZFCP_LOG_NORMAL(
-+		"Resetting SCSI bus "
-+		"(unit with FCP_LUN 0x%016Lx on the port with WWPN 0x%016Lx "
-+		"on the adapter with devno 0x%04x)\n",
-+		(llui_t)unit->fcp_lun,
-+		(llui_t)unit->port->wwpn,
-+		unit->port->adapter->devno);
-+        zfcp_erp_adapter_reopen(unit->port->adapter, 0);
-+	zfcp_erp_wait(unit->port->adapter);
-+        retval = SUCCESS;
-+
-+	spin_lock_irq(&io_request_lock);
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_scsi_eh_host_reset_handler
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ */
-+int zfcp_scsi_eh_host_reset_handler(Scsi_Cmnd *scpnt)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	int retval = 0;
-+        zfcp_unit_t *unit;
-+
-+	ZFCP_LOG_TRACE("enter (scpnt=0x%lx)\n", (unsigned long)scpnt);
-+        spin_unlock_irq(&io_request_lock);
-+
-+        unit = (zfcp_unit_t *)scpnt->device->hostdata; 
-+        /*DEBUG*/
-+	ZFCP_LOG_NORMAL(
-+		"Resetting SCSI host "
-+		"(unit with FCP_LUN 0x%016Lx on the port with WWPN 0x%016Lx "
-+		"on the adapter with devno 0x%04x)\n",
-+		(llui_t)unit->fcp_lun,
-+		(llui_t)unit->port->wwpn,
-+		unit->port->adapter->devno);
-+        zfcp_erp_adapter_reopen(unit->port->adapter, 0);
-+	zfcp_erp_wait(unit->port->adapter);
-+        retval=SUCCESS;
-+
-+	spin_lock_irq(&io_request_lock);
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_adapter_detect
-+ *
-+ * purpose:	checks whether the specified zSeries device is
-+ *		a supported adapter
-+ *
-+  * returns:	0 - for supported adapter
-+ *		!0 - for unsupported devices
-+ */
-+int zfcp_adapter_detect(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_OTHER
-+     
-+        int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter: (adapter=0x%lx)\n", (unsigned long)adapter);
-+        retval = get_dev_info_by_devno(adapter->devno, &adapter->devinfo);
-+        if (retval) {
-+		ZFCP_LOG_INFO(
-+			"warning: Device information for the adapter "
-+			"with devno 0x%04x could not be determined. "
-+			"The attempt returned %d. It is probable that "
-+			"no device with this devno exists.\n",
-+			adapter->devno,
-+			retval);
-+		goto out;
-+        }
-+        
-+        if (adapter->devinfo.status == 0){
-+		ZFCP_LOG_TRACE(
-+			"Adapter returned \"OK\", " 
-+			"devno is 0x%04x.\n", 
-+			(unsigned int) adapter->devno);
-+		goto ok;
-+        }
-+        if (adapter->devinfo.status & DEVSTAT_NOT_OPER) {
-+                ZFCP_LOG_INFO(
-+			"error: Adapter with devno 0x%04x is not "
-+			"operational.\n",
-+			(unsigned int) adapter->devno);
-+		retval = -EBUSY;
-+        }
-+        if (adapter->devinfo.status & DEVSTAT_DEVICE_OWNED) {
-+		ZFCP_LOG_INFO(
-+			"error: Adapter with devno 0x%04x is already "
-+			"owned by another driver.\n",
-+			(unsigned int) adapter->devno);
-+		retval = -EACCES;
-+        }
-+        if (adapter->devinfo.status & DEVSTAT_UNKNOWN_DEV) {
-+		ZFCP_LOG_INFO(
-+			"error: Adapter with devno 0x%04x is not "
-+			"an FCP card.\n",
-+			(unsigned int) adapter->devno);
-+		retval = -EACCES;
-+        }
-+        if (adapter->devinfo.status & (~(DEVSTAT_NOT_OPER |
-+                                        DEVSTAT_DEVICE_OWNED |
-+                                        DEVSTAT_UNKNOWN_DEV))){
-+		ZFCP_LOG_NORMAL(
-+			"bug: Adapter with devno 0x%04x returned an "
-+			"unexpected condition during the identification "
-+			"phase. (debug info %d)\n",
-+			(unsigned int) adapter->devno,
-+			adapter->devinfo.status);
-+		retval = -ENODEV;
-+        }
-+        if (retval < 0)
-+		goto out;
-+ok:
-+        if ((adapter->devinfo.sid_data.cu_type != ZFCP_CONTROL_UNIT_TYPE) ||
-+            (adapter->devinfo.sid_data.cu_model != ZFCP_CONTROL_UNIT_MODEL) ||
-+            (adapter->devinfo.sid_data.dev_type != ZFCP_DEVICE_TYPE) ||
-+           ((adapter->devinfo.sid_data.dev_model != ZFCP_DEVICE_MODEL) &&
-+            (adapter->devinfo.sid_data.dev_model != ZFCP_DEVICE_MODEL_PRIV))) {
-+		ZFCP_LOG_NORMAL(
-+			"error: Adapter with devno 0x%04x is not "
-+			"an FCP card.\n",
-+			(unsigned int) adapter->devno);
-+		retval = -ENODEV;
-+        }
-+        
-+out:
-+        ZFCP_LOG_TRACE(
-+		"CU type, model, dev type, model" 
-+		" 0x%x, 0x%x, 0x%x, 0x%x.\n", 
-+		adapter->devinfo.sid_data.cu_type,
-+		adapter->devinfo.sid_data.cu_model,
-+		adapter->devinfo.sid_data.dev_type,
-+		adapter->devinfo.sid_data.dev_model);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_adapter_irq_register(zfcp_adapter_t* adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+	int retval = 0;
-+	signed int tmp_irq;     /* adapter->irq is unsigned 16 bit! */
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	/* find out IRQ */
-+	tmp_irq = get_irq_by_devno(adapter->devno);
-+
-+        if (tmp_irq < 0 || tmp_irq > 0x0FFFF) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: The attempt to identify the irq for the "
-+			"adapter with devno 0x%04x failed. All map entries "
-+			"containing this devno are ignored. "
-+			"(debug info 0x%x)\n",
-+			adapter->devno,
-+			tmp_irq);
-+		retval = -ENXIO;
-+		goto out;
-+        }
-+	ZFCP_LOG_TRACE(
-+		"get_irq_by_devno returned irq=0x%x.\n",
-+		tmp_irq);
-+	adapter->irq = tmp_irq;
-+
-+	/* request IRQ */
-+	retval = s390_request_irq_special(
-+			adapter->irq,
-+			(void *)zfcp_cio_handler,
-+			zfcp_dio_not_oper_handler,
-+			0,
-+			zfcp_data.scsi_host_template.name,
-+			(void *)&adapter->devstat);
-+	if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not allocate irq %i to the adapter "
-+                        "with devno 0x%04x (debug info %i).\n",
-+			adapter->irq,
-+                        adapter->devno,
-+			retval);
-+		goto out;
-+	}
-+	atomic_set_mask(ZFCP_STATUS_ADAPTER_IRQOWNER, &adapter->status);
-+        ZFCP_LOG_DEBUG("request irq %i successfull\n", adapter->irq);
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_adapter_irq_unregister(zfcp_adapter_t* adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n", (unsigned long)adapter);
-+
-+        if(!atomic_test_mask(ZFCP_STATUS_ADAPTER_IRQOWNER, &adapter->status)) {
-+                ZFCP_LOG_DEBUG("Adapter with devno 0x%04x does not own "
-+                               "an irq, skipping over freeing attempt.\n",
-+                               adapter->devno);
-+                goto out;    
-+        }
-+        /* Note: There exists no race condition when the irq is given up by some
-+           other agency while at this point. The CIO layer will still handle the
-+           subsequent free_irq correctly.
-+        */
-+	free_irq(adapter->irq, (void *) &adapter->devstat);
-+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_IRQOWNER, &adapter->status);
-+	ZFCP_LOG_DEBUG("gave up irq=%i\n", adapter->irq);
-+ out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_adapter_scsi_register(zfcp_adapter_t* adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx)\n",
-+		(unsigned long)adapter);
-+
-+	/* register adapter as SCSI host with mid layer of SCSI stack */
-+	adapter->scsi_host = scsi_register(
-+				&zfcp_data.scsi_host_template,
-+				sizeof(zfcp_adapter_t*));
-+	if (!adapter->scsi_host) {
-+		ZFCP_LOG_NORMAL(
-+			"error: Not enough free memory. "
-+			"Could not register host-adapter with "
-+			"devno 0x%04x with the SCSI-stack.\n",
-+			adapter->devno);
-+		retval = -EIO;
-+		goto out;
-+	} 
-+	atomic_set_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);
-+	ZFCP_LOG_DEBUG(
-+		"host registered, scsi_host at 0x%lx\n",
-+		(unsigned long)adapter->scsi_host);
-+
-+	/* tell the SCSI stack some characteristics of this adapter */
-+	adapter->scsi_host->max_id = adapter->max_scsi_id + 1;
-+	adapter->scsi_host->max_lun = adapter->max_scsi_lun + 1;
-+	adapter->scsi_host->max_channel = 0;
-+	adapter->scsi_host->irq = adapter->irq;
-+	adapter->scsi_host->unique_id = adapter->devno;
-+	adapter->scsi_host->max_cmd_len = ZFCP_MAX_SCSI_CMND_LENGTH;
-+	adapter->scsi_host->loaded_as_module
-+		= (zfcp_data.scsi_host_template.module ? 1 : 0);
-+	adapter->scsi_host->select_queue_depths
-+		= zfcp_scsi_select_queue_depth;
-+
-+	/*
-+	 * save a pointer to our own adapter data structure within
-+	 * hostdata field of SCSI host data structure
-+	 */
-+	adapter->scsi_host->hostdata[0] = (unsigned long)adapter;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+int zfcp_initialize_with_0copy(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
-+
-+        int retval = 0;
-+        qdio_initialize_t init_data;
-+
-+	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n", (unsigned long)adapter);
-+                
-+        init_data.irq = adapter->irq;
-+        init_data.q_format = QDIO_SCSI_QFMT;
-+        memcpy(init_data.adapter_name,&adapter->name,8);
-+        init_data.qib_param_field_format = 0;
-+        init_data.qib_param_field = NULL;
-+        init_data.input_slib_elements = NULL;
-+        init_data.output_slib_elements = NULL;
-+        init_data.min_input_threshold = ZFCP_MIN_INPUT_THRESHOLD;
-+        init_data.max_input_threshold = ZFCP_MAX_INPUT_THRESHOLD;
-+        init_data.min_output_threshold = ZFCP_MIN_OUTPUT_THRESHOLD;
-+        init_data.max_output_threshold = ZFCP_MAX_OUTPUT_THRESHOLD;
-+        init_data.no_input_qs = 1;
-+        init_data.no_output_qs = 1;
-+        init_data.input_handler = zfcp_qdio_response_handler;
-+        init_data.output_handler = zfcp_qdio_request_handler;
-+        init_data.int_parm = (unsigned long)adapter;
-+        init_data.flags = QDIO_INBOUND_0COPY_SBALS|
-+                QDIO_OUTBOUND_0COPY_SBALS|
-+                QDIO_USE_OUTBOUND_PCIS;
-+        init_data.input_sbal_addr_array = 
-+                (void **)(adapter->response_queue.buffer);
-+        init_data.output_sbal_addr_array = 
-+                (void **)(adapter->request_queue.buffer);
-+        ZFCP_LOG_TRACE("Before qdio_initialise\n");
-+	retval = qdio_initialize(&init_data);
-+        ZFCP_LOG_TRACE("After qdio_initialise\n");
-+        
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ *
-+ * note:	qdio queues shall be down (no ongoing inbound processing)
-+ */
-+static int zfcp_fsf_req_dismiss_all(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	int retval = 0;
-+	zfcp_fsf_req_t *fsf_req, *next_fsf_req;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx)\n",
-+		(unsigned long)adapter);
-+
-+	ZFCP_FOR_NEXT_FSFREQ(adapter, fsf_req, next_fsf_req)
-+		zfcp_fsf_req_dismiss(fsf_req);
-+	while (!list_empty(&adapter->fsf_req_list_head)) {
-+		ZFCP_LOG_DEBUG(
-+			"fsf req list of adapter with "
-+			"devno 0x%04x not yet empty\n",
-+			adapter->devno);
-+		/* wait for woken intiators to clean up their requests */
-+		set_current_state(TASK_UNINTERRUPTIBLE);
-+		schedule_timeout(ZFCP_FSFREQ_CLEANUP_TIMEOUT);
-+	}
-+
-+	/* consistency check */
-+        if (atomic_read(&adapter->fsf_reqs_active)) {
-+                ZFCP_LOG_NORMAL(
-+			"bug: There are still %d FSF requests pending "
-+			"on the adapter with devno 0x%04x after "
-+			"cleanup.\n",
-+			atomic_read(&adapter->fsf_reqs_active),
-+			adapter->devno);
-+		atomic_set(&adapter->fsf_reqs_active, 0);
-+        }
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_fsf_req_dismiss(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+	fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
-+	zfcp_fsf_req_complete(fsf_req);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:   	zfcp_qdio_handler_error_check
-+ *
-+ * purpose:     called by the response handler to determine error condition
-+ *
-+ * returns:	error flag
-+ *
-+ */
-+inline int zfcp_qdio_handler_error_check(
-+        zfcp_adapter_t *adapter,
-+	unsigned int status,
-+	unsigned int qdio_error,
-+	unsigned int siga_error)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
-+
-+     int retval=0;
-+
-+     ZFCP_LOG_TRACE(
-+          "enter (adapter=0x%lx, status=%i qdio_error=%i siga_error=%i\n",
-+          (unsigned long) adapter,
-+          status,
-+          qdio_error,
-+          siga_error);
-+
-+     if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_TRACE)){     
-+             if (status &  QDIO_STATUS_INBOUND_INT){
-+                     ZFCP_LOG_TRACE("status is"
-+                                    " QDIO_STATUS_INBOUND_INT \n");
-+             }
-+             if (status & QDIO_STATUS_OUTBOUND_INT){
-+                     ZFCP_LOG_TRACE("status is"
-+                                    " QDIO_STATUS_OUTBOUND_INT \n");
-+             }
-+     }// if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_TRACE))
-+     if (status & QDIO_STATUS_LOOK_FOR_ERROR){
-+             retval=-EIO;
-+             
-+             ZFCP_LOG_FLAGS(1,"QDIO_STATUS_LOOK_FOR_ERROR \n");
-+
-+             ZFCP_LOG_INFO("A qdio problem occured. The status, qdio_error and "
-+                           "siga_error are 0x%x, 0x%x and 0x%x\n", 
-+                           status,
-+                           qdio_error,
-+                           siga_error); 
-+
-+             if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION){
-+                     ZFCP_LOG_FLAGS(2, "QDIO_STATUS_ACTIVATE_CHECK_CONDITION\n");
-+             }
-+             if (status & QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR){
-+                     ZFCP_LOG_FLAGS(2, "QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR\n");
-+             }
-+             if (status & QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR){
-+                     ZFCP_LOG_FLAGS(2, "QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR\n");
-+             }
-+             
-+             if (siga_error & QDIO_SIGA_ERROR_ACCESS_EXCEPTION) {
-+                     ZFCP_LOG_FLAGS(2, "QDIO_SIGA_ERROR_ACCESS_EXCEPTION\n"); 
-+             }
-+             
-+             if (siga_error & QDIO_SIGA_ERROR_B_BIT_SET) {
-+                     ZFCP_LOG_FLAGS(2, "QDIO_SIGA_ERROR_B_BIT_SET\n"); 
-+             }
-+
-+             switch (qdio_error) {
-+             case 0:
-+                     ZFCP_LOG_FLAGS(3, "QDIO_OK");
-+                     break;
-+             case SLSB_P_INPUT_ERROR :
-+                     ZFCP_LOG_FLAGS(1, "SLSB_P_INPUT_ERROR\n");
-+                     break;
-+             case SLSB_P_OUTPUT_ERROR :
-+                     ZFCP_LOG_FLAGS(1, "SLSB_P_OUTPUT_ERROR\n"); 
-+                     break;
-+             default :
-+                     ZFCP_LOG_NORMAL("bug: Unknown qdio error reported "
-+                                     "(debug info 0x%x)\n",
-+                                     qdio_error);
-+                     break;
-+             }
-+             /* Restarting IO on the failed adapter from scratch */
-+             debug_text_event(adapter->erp_dbf,1,"qdio_err");
-+		/*
-+		 * Since we have been using this adapter, it is save to assume
-+		 * that it is not failed but recoverable. The card seems to
-+		 * report link-up events by self-initiated queue shutdown.
-+		 * That is why we need to clear the the link-down flag
-+		 * which is set again in case we have missed by a mile.
-+		 */
-+		zfcp_erp_adapter_reopen(
-+			adapter, 
-+			ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
-+			ZFCP_STATUS_COMMON_ERP_FAILED);
-+     } // if(status & QDIO_STATUS_LOOK_FOR_ERROR)
-+
-+     ZFCP_LOG_TRACE("exit (%i)\n", retval);             
-+     return retval;
-+     
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_qdio_request_handler
-+ *
-+ * purpose:	is called by QDIO layer for completed SBALs in request queue
-+ *
-+ * returns:	(void)
-+ */
-+void zfcp_qdio_request_handler(
-+	int irq,
-+	unsigned int status,
-+	unsigned int qdio_error,
-+	unsigned int siga_error,
-+	unsigned int queue_number,
-+	int first_element,
-+	int elements_processed,
-+	unsigned long int_parm)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
-+
-+	zfcp_adapter_t *adapter;
-+	zfcp_qdio_queue_t *queue;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (irq=%i status=%i qdio_error=%i siga_error=%i "
-+		"queue_number=%i first_element=%i elements_processed=%i "
-+		"int_parm=0x%lx)\n",
-+		irq,
-+		status,
-+		qdio_error,
-+		siga_error,
-+		queue_number,
-+		first_element,
-+		elements_processed,
-+		int_parm);
-+
-+	adapter = (zfcp_adapter_t*)int_parm;
-+      	queue = &adapter->request_queue;
-+
-+        ZFCP_LOG_DEBUG("devno=0x%04x, first=%d, count=%d\n",
-+                        adapter->devno,
-+                        first_element,
-+                        elements_processed);
-+
-+	if (zfcp_qdio_handler_error_check(adapter, status, qdio_error, siga_error))
-+		goto out;        
-+
-+	/* cleanup all SBALs being program-owned now */
-+	zfcp_zero_sbals(
-+	        queue->buffer, 
-+		first_element, 
-+		elements_processed);
-+
-+	/* increase free space in outbound queue */
-+	atomic_add(elements_processed, &queue->free_count);
-+        ZFCP_LOG_DEBUG("free_count=%d\n",
-+                       atomic_read(&queue->free_count));
-+        wake_up(&adapter->request_wq);
-+	ZFCP_LOG_DEBUG(
-+		"Elements_processed = %d, free count=%d \n",
-+		elements_processed,
-+		atomic_read(&queue->free_count));
-+
-+out: 
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+	return;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:   	zfcp_qdio_response_handler
-+ *
-+ * purpose:	is called by QDIO layer for completed SBALs in response queue
-+ *
-+ * returns:	(void)
-+ */
-+void zfcp_qdio_response_handler(
-+	int irq,
-+	unsigned int status,
-+	unsigned int qdio_error,
-+	unsigned int siga_error,
-+	unsigned int queue_number,
-+	int first_element,
-+	int elements_processed,
-+	unsigned long int_parm)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
-+
-+	zfcp_adapter_t *adapter;
-+	zfcp_qdio_queue_t *queue;
-+	int buffer_index;
-+	int i;
-+	qdio_buffer_t *buffer;
-+	int retval = 0;
-+	u8 count;
-+	u8 start;
-+        volatile qdio_buffer_element_t *buffere=NULL;
-+	int buffere_index;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (irq=0x%x status=0x%x qdio_error=0x%x siga_error=0x%x "
-+		"queue_number=%i first_element=%i elements_processed=%i "
-+		"int_parm=0x%lx)\n",
-+		irq,
-+		status,
-+		qdio_error,
-+		siga_error,
-+		queue_number,
-+		first_element,
-+		elements_processed,
-+		int_parm);
-+        
-+	adapter = (zfcp_adapter_t*)int_parm;
-+      	queue = &adapter->response_queue;
-+
-+        if (zfcp_qdio_handler_error_check(adapter, status, qdio_error, siga_error))
-+                goto out;        
-+
-+        buffere = &(queue->buffer[first_element]->element[0]);
-+        ZFCP_LOG_DEBUG("first BUFFERE flags=0x%x \n",
-+                       buffere->flags);
-+	/*
-+	 * go through all SBALs from input queue currently
-+	 * returned by QDIO layer
-+         */ 
-+
-+	for (i = 0; i < elements_processed; i++) {
-+
-+		buffer_index = first_element + i;
-+		buffer_index %= QDIO_MAX_BUFFERS_PER_Q;
-+		buffer = queue->buffer[buffer_index];
-+
-+		/* go through all SBALEs of SBAL */
-+                for(buffere_index = 0;
-+                    buffere_index < QDIO_MAX_ELEMENTS_PER_BUFFER;
-+                    buffere_index++) {
-+
-+			/* look for QDIO request identifiers in SB */
-+			buffere = &buffer->element[buffere_index];
-+			retval = zfcp_qdio_reqid_check(adapter, 
-+                                                       (void *)buffere->addr);
-+
-+			if (retval) {
-+				ZFCP_LOG_NORMAL(
-+					"bug: Inbound packet seems not to have "
-+					"been sent at all. It will be ignored."
-+					"(debug info 0x%lx, 0x%lx, %d, %d, 0x%x)\n",
-+					(unsigned long)buffere->addr,
-+					(unsigned long)&(buffere->addr),
-+                                        first_element,
-+                                        elements_processed,
-+					adapter->devno);
-+
-+				ZFCP_LOG_NORMAL(
-+					"Dump of inbound BUFFER %d BUFFERE %d "
-+					"at address 0x%lx\n",
-+					buffer_index,
-+                                        buffere_index, 
-+					(unsigned long)buffer);
-+				ZFCP_HEX_DUMP(
-+					ZFCP_LOG_LEVEL_NORMAL,
-+					(char*)buffer,
-+					SBAL_SIZE);
-+			}
-+                        if (buffere->flags & SBAL_FLAGS_LAST_ENTRY)
-+				break;                        
-+		};
-+
-+                if (!(buffere->flags & SBAL_FLAGS_LAST_ENTRY)) {
-+			ZFCP_LOG_NORMAL("bug: End of inbound data not marked!\n");
-+                } 
-+	}
-+
-+	/*
-+	 * put range of SBALs back to response queue
-+	 * (including SBALs which have already been free before)
-+	 */
-+	count = atomic_read(&queue->free_count) + elements_processed;
-+	start = queue->free_index;
-+
-+        ZFCP_LOG_TRACE(
-+		"Calling do QDIO irq=0x%x,flags=0x%x, queue_no=%i, "
-+		"index_in_queue=%i, count=%i, buffers=0x%lx\n",
-+		irq,
-+		QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
-+		0,
-+		start,
-+		count,
-+		(unsigned long)&queue->buffer[start]);	
-+
-+        retval = do_QDIO(
-+                        irq,
-+                        QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
-+                        0,
-+                        start,
-+                        count,
-+                        NULL);
-+        if (retval) {
-+		atomic_set(&queue->free_count, count);
-+                ZFCP_LOG_DEBUG(
-+			"Inbound data regions could not be cleared "
-+                        "Transfer queues may be down. "
-+                        "(info %d, %d, %d)\n",
-+			count,
-+			start,
-+			retval);
-+        } else  {
-+		queue->free_index += count;
-+		queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;
-+		atomic_set(&queue->free_count, 0);
-+                ZFCP_LOG_TRACE(
-+			"%i buffers successfully enqueued to response queue "
-+			"starting at position %i\n",
-+			count,
-+			start);
-+        }
-+
-+out:
-+        /*
-+          ZFCP_LOG_DEBUG("response_queue->free_count=%i,response_queue->free_index=%i\n",
-+          atomic_read(&queue->free_count),
-+          queue->free_index) ;
-+        */
-+	ZFCP_LOG_TRACE("exit\n");
-+ 
-+	return;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_qdio_reqid_check
-+ *
-+ * purpose:	checks for valid reqids or unsolicited status
-+ *
-+ * returns:	0 - valid request id or unsolicited status
-+ *		!0 - otherwise
-+ */
-+static inline int zfcp_qdio_reqid_check(zfcp_adapter_t *adapter, void *sbale_addr)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_QDIO
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_QDIO
-+
-+	zfcp_fsf_req_t *fsf_req;
-+        int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (sbale_addr=0x%lx)\n",
-+		(unsigned long)sbale_addr);
-+
-+#ifdef ZFCP_DEBUG_REQUESTS
-+        /* Note: seq is entered later */
-+        debug_text_event(adapter->req_dbf, 1, "i:a/seq");
-+        debug_event(adapter->req_dbf, 1, &sbale_addr, sizeof(unsigned long));
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+
-+	/* invalid (per convention used in this driver) */
-+	if (!sbale_addr) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Inbound data faulty, contains null-pointer!\n");
-+		retval = -EINVAL;
-+		goto out;
-+        }
-+
-+	/* valid request id and thus (hopefully :) valid fsf_req address */
-+	fsf_req = (zfcp_fsf_req_t*)sbale_addr;
-+
-+	ZFCP_PARANOIA {
-+		if ((fsf_req->common_magic != ZFCP_MAGIC)
-+                    ||(fsf_req->specific_magic != ZFCP_MAGIC_FSFREQ)) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: An inbound FSF acknowledgement was "
-+				"faulty (debug info 0x%x, 0x%x, 0x%lx)\n",
-+				fsf_req->common_magic,
-+				fsf_req->specific_magic,
-+				(unsigned long)fsf_req);
-+			retval = -EINVAL;
-+                        //                        panic("void of grace");
-+			goto out;
-+		}
-+
-+		if (adapter != fsf_req->adapter) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: An inbound FSF acknowledgement was not "
-+				"correct (debug info 0x%lx, 0x%lx, 0%lx) \n",
-+				(unsigned long)fsf_req,
-+				(unsigned long)fsf_req->adapter,
-+                                (unsigned long)adapter);
-+			retval = -EINVAL;
-+			goto out;
-+		}
-+	}
-+
-+#ifdef ZFCP_DEBUG_REQUESTS
-+	/* debug feature stuff (test for QTCB: remember new unsol. status!) */
-+	if (fsf_req->qtcb) {
-+                debug_event(adapter->req_dbf, 1, &fsf_req->qtcb->prefix.req_seq_no,
-+                            sizeof(u32));
-+	}
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+
-+	ZFCP_LOG_TRACE(
-+		"fsf_req at 0x%lx, QTCB at 0x%lx\n",
-+		(unsigned long)fsf_req,
-+		(unsigned long)fsf_req->qtcb);
-+	if (fsf_req->qtcb) {
-+		ZFCP_LOG_TRACE("HEX DUMP OF 1ST BUFFERE PAYLOAD (QTCB):\n");
-+		ZFCP_HEX_DUMP(
-+			ZFCP_LOG_LEVEL_TRACE,
-+			(char*)fsf_req->qtcb,
-+			sizeof(fsf_qtcb_t));
-+	}
-+
-+	/* finish the FSF request */
-+	zfcp_fsf_req_complete(fsf_req);
-+          
-+out:
-+	ZFCP_LOG_TRACE("exit \n");
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_activate_adapter
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ */
-+inline static void zfcp_activate_adapter(int irq)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_DIO
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_DIO
-+
-+	int devno;
-+	zfcp_adapter_t *adapter;
-+
-+	ZFCP_LOG_TRACE("enter (irq=%i)\n", irq);
-+
-+	devno = get_devno_by_irq(irq);
-+	ZFCP_LOG_TRACE("devno is 0x%04x\n",devno);
-+
-+	/* Find the new adapter and open it */
-+	ZFCP_FOR_EACH_ADAPTER(adapter) {
-+		if (adapter->devno == devno) {
-+			ZFCP_LOG_INFO(
-+				"The adapter with devno 0x%04x "
-+				"will now be activated.\n",
-+				devno);
-+			debug_text_event(adapter->erp_dbf,1,"activate");
-+			zfcp_erp_modify_adapter_status(
-+				adapter,
-+				ZFCP_STATUS_COMMON_RUNNING,
-+				ZFCP_SET);
-+			zfcp_erp_adapter_reopen(
-+				adapter,
-+				ZFCP_STATUS_COMMON_ERP_FAILED);
-+		}
-+	}
-+	if (!adapter)
-+		ZFCP_LOG_DEBUG(
-+			"An unconfigured adapter has become "
-+			"active, it's devno 0x%04x.\n",
-+			devno);
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_dio_oper_handler
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ */
-+static int zfcp_dio_oper_handler(int irq, devreg_t *dreg)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_DIO
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_DIO
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (irq=%i, dreg=0x%lx)\n",
-+		irq, (unsigned long)dreg);
-+
-+        zfcp_activate_adapter(irq);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_dio_not_oper_handler
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ */
-+static void zfcp_dio_not_oper_handler(int irq,  int status)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_DIO
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_DIO
-+        
-+        zfcp_adapter_t *adapter;
-+        int known=0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (irq=%i, status=%i)\n",
-+		irq, status);
-+
-+        ZFCP_FOR_EACH_ADAPTER(adapter) {
-+                if(atomic_test_mask(ZFCP_STATUS_ADAPTER_IRQOWNER, &adapter->status) &&
-+                   (adapter->irq==irq)) {
-+                        known=1;
-+                        break;
-+                }
-+        }
-+
-+        switch (status) {
-+        case DEVSTAT_DEVICE_GONE:
-+                ZFCP_LOG_FLAGS(1,"DEVSTAT_DEVICE_GONE\n");
-+        case DEVSTAT_NOT_OPER:
-+                ZFCP_LOG_FLAGS(1,"DEVSTAT_NOT_OPER\n");
-+                if (!known) {
-+                        ZFCP_LOG_DEBUG("An unconfigured or an already "
-+                                       "disabled adapter became "
-+                                       "unoperational on irq 0x%x.\n",
-+                                       irq);
-+                        goto out;
-+                }
-+                ZFCP_LOG_INFO("The adapter with devno 0x%04x became "
-+                              "unoperational.\n",
-+                              adapter->devno);
-+                /* shut-down the adapter and wait for completion */
-+                debug_text_event(adapter->erp_dbf,1,"not_oper");
-+                zfcp_erp_adapter_shutdown(adapter, ZFCP_STATUS_ADAPTER_IRQOWNER);
-+                zfcp_erp_wait(adapter);
-+                break;
-+        case DEVSTAT_REVALIDATE:
-+                ZFCP_LOG_FLAGS(1,"DEVSTAT_REVALIDATE\n");
-+                /* The irq should still be that of the old adapter */
-+                if(known) {
-+                        ZFCP_LOG_INFO("The adapter with devno 0x%04x became "
-+                                      "unoperational.\n",
-+                                      adapter->devno);
-+                        /* shut-down the adapter and wait for completion */
-+                        /* Note: This adapter is not the real IRQ-owner anymore 
-+                         * The ERP strategy requires the IRQ to be freed somehow
-+                         * though 
-+                         */
-+                        debug_text_event(adapter->erp_dbf,1,"reval");
-+                        zfcp_erp_adapter_shutdown(adapter, 0);
-+                        zfcp_erp_wait(adapter);
-+                } else {
-+                        ZFCP_LOG_DEBUG("An unconfigured adapter was the "
-+                                       "origin of a VM define, it's irq 0x%x.\n",
-+                                       irq);
-+                }
-+                /* The new adapter already owns the irq and needs to be activated  */
-+                zfcp_activate_adapter(irq);
-+                break;
-+        default:
-+                ZFCP_LOG_NORMAL("bug: Common I/O layer presented information  "
-+                               "unknown to the zfcp module (debug info "
-+                               "0x%x, 0x%x)\n",
-+                               irq,
-+                               status);
-+        }      
-+ out:
-+        ZFCP_LOG_TRACE("exit\n");
-+	
-+        return;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_cio_handler
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ */
-+static void zfcp_cio_handler(int irq, void *devstat, struct pt_regs *rgs)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_DIO
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_DIO
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (irq=%i, devstat=0%lx, pt_regs=0%lx)\n",
-+		irq, (unsigned long)devstat,
-+		(unsigned long)rgs);
-+        ZFCP_LOG_DEBUG("Normally, this function would never be called. "
-+                       "(info 0x%x, 0x%lx, 0x%lx)\n",
-+                       irq,
-+                       (unsigned long)devstat,
-+                       (unsigned long)rgs);
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+	return;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_req_complete
-+ *
-+ * purpose:	Updates active counts and timers for openfcp-reqs
-+ *              May cleanup request after req_eval returns
-+ *
-+ * returns:	0 - success
-+ *		!0 - failure
-+ *
-+ * context:	
-+ */
-+static int zfcp_fsf_req_complete(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = 0;
-+        int cleanup;
-+        zfcp_adapter_t *adapter = fsf_req->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+	/* do some statistics */
-+	atomic_dec(&adapter->fsf_reqs_active);
-+
-+	if (fsf_req->fsf_command == FSF_QTCB_UNSOLICITED_STATUS) {
-+                ZFCP_LOG_DEBUG("Status read response received\n");
-+                /* Note: all cleanup handling is done in the callchain of 
-+                   the function call-chain below.
-+                */
-+		zfcp_fsf_status_read_handler(fsf_req);
-+                goto out;
-+	} else	zfcp_fsf_protstatus_eval(fsf_req);
-+
-+        /*
-+	 * fsf_req may be deleted due to waking up functions, so 
-+         * cleanup is saved here and used later 
-+         */
-+        if (fsf_req->status & ZFCP_STATUS_FSFREQ_CLEANUP) 
-+                cleanup = 1;
-+        else    cleanup = 0;        
-+
-+        fsf_req->status |= ZFCP_STATUS_FSFREQ_COMPLETED;
-+        
-+        /* cleanup request if requested by initiator */
-+        if (cleanup) {
-+                ZFCP_LOG_TRACE(
-+			"removing FSF request 0x%lx\n",
-+			(unsigned long)fsf_req);
-+		/*
-+		 * lock must not be held here since it will be
-+		 * grabed by the called routine, too
-+		 */
-+		if (zfcp_fsf_req_cleanup(fsf_req)) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: Could not remove one FSF "
-+				"request. Memory leakage possible. "
-+				"(debug info 0x%lx).\n",
-+				(unsigned long)fsf_req);
-+		}
-+        } else {
-+                /* notify initiator waiting for the requests completion */
-+                ZFCP_LOG_TRACE(
-+                    "waking initiator of FSF request 0x%lx\n",
-+                    (unsigned long)fsf_req);
-+                wake_up(&fsf_req->completion_wq);
-+        }
-+        
-+ out:
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+        return retval;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_protstatus_eval
-+ *
-+ * purpose:	evaluates the QTCB of the finished FSF request
-+ *		and initiates appropriate actions
-+ *		(usually calling FSF command specific handlers)
-+ *
-+ * returns:	
-+ *
-+ * context:	
-+ *
-+ * locks:
-+ */
-+static int zfcp_fsf_protstatus_eval(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+     
-+	int retval = 0;
-+	zfcp_adapter_t *adapter = fsf_req->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+	ZFCP_LOG_DEBUG(
-+		"QTCB is at 0x%lx\n",
-+		(unsigned long)fsf_req->qtcb);
-+
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
-+		ZFCP_LOG_DEBUG(
-+			"fsf_req 0x%lx has been dismissed\n",
-+			(unsigned long)fsf_req);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-+				   ZFCP_STATUS_FSFREQ_RETRY;	/* only for SCSI cmnds. */
-+		zfcp_cmd_dbf_event_fsf("dismiss", fsf_req, NULL, 0);
-+		goto skip_protstatus;
-+	}
-+
-+	/* log additional information provided by FSF (if any) */
-+	if (fsf_req->qtcb->header.log_length) {
-+		/* do not trust them ;-) */
-+		if (fsf_req->qtcb->header.log_start > sizeof(fsf_qtcb_t)) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: ULP (FSF logging) log data starts "
-+                                "beyond end of packet header. Ignored. "
-+				"(start=%i, size=%li)\n",
-+				fsf_req->qtcb->header.log_start,
-+				sizeof(fsf_qtcb_t));
-+			goto forget_log;
-+		}
-+		if ((size_t)(fsf_req->qtcb->header.log_start +
-+		     fsf_req->qtcb->header.log_length)
-+		    > sizeof(fsf_qtcb_t)) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: ULP (FSF logging) log data ends "
-+                                "beyond end of packet header. Ignored. "
-+				"(start=%i, length=%i, size=%li)\n",
-+				fsf_req->qtcb->header.log_start,
-+				fsf_req->qtcb->header.log_length,
-+				sizeof(fsf_qtcb_t));
-+			goto forget_log;
-+		}
-+		ZFCP_LOG_TRACE("ULP log data: \n");
-+		ZFCP_HEX_DUMP(
-+			ZFCP_LOG_LEVEL_TRACE,
-+			(char*)fsf_req->qtcb + fsf_req->qtcb->header.log_start,
-+			fsf_req->qtcb->header.log_length);
-+	}
-+forget_log:
-+
-+	/* evaluate FSF Protocol Status */
-+	switch (fsf_req->qtcb->prefix.prot_status) {
-+
-+		case FSF_PROT_GOOD :
-+			ZFCP_LOG_TRACE("FSF_PROT_GOOD\n");
-+			break;
-+
-+		case FSF_PROT_FSF_STATUS_PRESENTED :
-+			ZFCP_LOG_TRACE("FSF_PROT_FSF_STATUS_PRESENTED\n");
-+			break;
-+
-+		case FSF_PROT_QTCB_VERSION_ERROR :
-+			ZFCP_LOG_FLAGS(0, "FSF_PROT_QTCB_VERSION_ERROR\n");
-+			/* DEBUG */
-+			ZFCP_LOG_NORMAL(
-+				"fsf_req=0x%lx, qtcb=0x%lx\n",
-+				(unsigned long)fsf_req,
-+				(unsigned long)fsf_req->qtcb);
-+			ZFCP_HEX_DUMP(
-+				ZFCP_LOG_LEVEL_NORMAL,
-+				(char*)fsf_req,
-+				sizeof(zfcp_fsf_req_t));
-+			ZFCP_LOG_NORMAL(
-+				"error: The adapter with devno 0x%04x contains "
-+                                "microcode of version 0x%x, the device driver "
-+                                "only supports 0x%x. Aborting.\n",
-+				adapter->devno,
-+				fsf_req->qtcb->prefix.prot_status_qual.version_error.fsf_version,
-+				ZFCP_QTCB_VERSION);
-+			/* stop operation for this adapter */
-+                        debug_text_exception(adapter->erp_dbf,0,"prot_ver_err");
-+                        zfcp_erp_adapter_shutdown(adapter, 0);
-+			zfcp_cmd_dbf_event_fsf(
-+				"qverserr", fsf_req,
-+				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			break;
-+
-+		case FSF_PROT_SEQ_NUMB_ERROR :
-+			ZFCP_LOG_FLAGS(0, "FSF_PROT_SEQ_NUMB_ERROR\n");
-+			ZFCP_LOG_NORMAL(
-+				"bug: Sequence number mismatch between "
-+				"driver (0x%x) and adapter of devno 0x%04x "
-+				"(0x%x). Restarting all operations on this "
-+                                "adapter.\n",
-+				fsf_req->qtcb->prefix.req_seq_no,
-+                                adapter->devno,
-+				fsf_req->qtcb->prefix.prot_status_qual.sequence_error.exp_req_seq_no);
-+#ifdef ZFCP_DEBUG_REQUESTS
-+                        debug_text_event(adapter->req_dbf, 1, "exp_seq!");
-+                        debug_event(adapter->req_dbf, 1, &fsf_req->qtcb->prefix.prot_status_qual.sequence_error.exp_req_seq_no, 4);
-+                        debug_text_event(adapter->req_dbf, 1, "qtcb_seq!");
-+                        debug_exception(adapter->req_dbf, 1, &fsf_req->qtcb->prefix.req_seq_no, 4);
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+                        debug_text_exception(adapter->erp_dbf,0,"prot_seq_err");
-+			/* restart operation on this adapter */
-+                        zfcp_erp_adapter_reopen(adapter,0);
-+			zfcp_cmd_dbf_event_fsf(
-+				"seqnoerr", fsf_req,
-+				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY;
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			break;
-+
-+		case FSF_PROT_UNSUPP_QTCB_TYPE :
-+			ZFCP_LOG_FLAGS(0, "FSF_PROT_UNSUP_QTCB_TYPE\n");
-+			ZFCP_LOG_NORMAL("error: Packet header type used by the "
-+                                        "device driver is incompatible with "
-+                                        "that used on the adapter with devno "
-+                                        "0x%04x. "
-+                                        "Stopping all operations on this adapter.\n", 
-+                                        adapter->devno);
-+			ZFCP_LOG_NORMAL(
-+				"fsf_req=0x%lx, qtcb=0x%lx\n",
-+				(unsigned long)fsf_req,
-+				(unsigned long)fsf_req->qtcb);
-+			ZFCP_HEX_DUMP(
-+				ZFCP_LOG_LEVEL_NORMAL,
-+				(char*)fsf_req,
-+				sizeof(zfcp_fsf_req_t));
-+                        debug_text_exception(adapter->erp_dbf,0,"prot_unsup_qtcb");
-+                        zfcp_erp_adapter_shutdown(adapter, 0);
-+			zfcp_cmd_dbf_event_fsf(
-+				"unsqtcbt", fsf_req,
-+				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			break;
-+
-+		case FSF_PROT_HOST_CONNECTION_INITIALIZING :
-+			ZFCP_LOG_FLAGS(1, "FSF_PROT_HOST_CONNECTION_INITIALIZING\n");
-+			zfcp_cmd_dbf_event_fsf(
-+				"hconinit", fsf_req,
-+				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, 
-+                                        &(adapter->status));
-+                        debug_text_event(adapter->erp_dbf,4,"prot_con_init");
-+			break;
-+
-+		case FSF_PROT_DUPLICATE_REQUEST_ID :
-+			ZFCP_LOG_FLAGS(0, "FSF_PROT_DUPLICATE_REQUEST_IDS\n");
-+			if (fsf_req->qtcb) {
-+				ZFCP_LOG_NORMAL(
-+					"bug: The request identifier 0x%Lx "
-+                                	"to the adapter with devno 0x%04x is " 
-+					"ambiguous. "
-+                                        "Stopping all operations on this adapter.\n",
-+        	                        *(llui_t*)(&fsf_req->qtcb->bottom.support.req_handle),
-+					adapter->devno);
-+			} else	{
-+				ZFCP_LOG_NORMAL(
-+					"bug: The request identifier 0x%lx "
-+                                	"to the adapter with devno 0x%04x is " 
-+					"ambiguous. "
-+                                        "Stopping all operations on this adapter. "
-+                                        "(bug: got this for an unsolicited "
-+					"status read request)\n",
-+					(unsigned long)fsf_req,
-+					adapter->devno);
-+			}
-+                        debug_text_exception(adapter->erp_dbf,0,"prot_dup_id");
-+                        zfcp_erp_adapter_shutdown(adapter, 0);
-+			zfcp_cmd_dbf_event_fsf(
-+				"dupreqid", fsf_req,
-+				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			break;
-+                        
-+		case FSF_PROT_LINK_DOWN :
-+			ZFCP_LOG_FLAGS(1, "FSF_PROT_LINK_DOWN\n");
-+			/*
-+			 * 'test and set' is not atomic here -
-+			 * it's ok as long as calls to our response queue handler
-+			 * (and thus execution of this code here) are serialized
-+			 * by the qdio module
-+			 */
-+			if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, 
-+                                               &adapter->status)) {
-+				switch (fsf_req->qtcb->prefix.prot_status_qual.locallink_error.code) {
-+				case FSF_PSQ_LINK_NOLIGHT :
-+					ZFCP_LOG_INFO(
-+						"The local link to the adapter with "
-+						"devno 0x%04x is down"
-+						"(no light detected).\n",
-+						adapter->devno);
-+					break;
-+				case FSF_PSQ_LINK_WRAPPLUG :
-+					ZFCP_LOG_INFO(
-+						"The local link to the adapter with "
-+						"devno 0x%04x is down"
-+						"(wrap plug detected).\n",
-+						adapter->devno);
-+					break;
-+				case FSF_PSQ_LINK_NOFCP :
-+					ZFCP_LOG_INFO(
-+						"The local link to the adapter with "
-+						"devno 0x%04x is down"
-+						"(the adjacent node on the link "
-+						"does not support FCP).\n",
-+						adapter->devno);
-+					break;
-+				default :
-+					ZFCP_LOG_INFO(
-+						"The local link to the adapter with "
-+						"devno 0x%04x is down"
-+						"(warning: unknown reason code).\n",
-+						adapter->devno);
-+					break;
-+
-+				}
-+				/*
-+				 * Due to the 'erp failed' flag the adapter won't
-+				 * be recovered but will be just set to 'blocked'
-+				 * state. All subordinary devices will have state
-+				 * 'blocked' and 'erp failed', too.
-+				 * Thus the adapter is still able to provide
-+				 * 'link up' status without being flooded with
-+				 * requests.
-+				 * (note: even 'close port' is not permitted)
-+				 */
-+				ZFCP_LOG_INFO(
-+					"Stopping all operations for the adapter "
-+					"with devno 0x%04x.\n",
-+					adapter->devno);
-+				atomic_set_mask(
-+					ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
-+					ZFCP_STATUS_COMMON_ERP_FAILED,
-+					&adapter->status);
-+				zfcp_erp_adapter_reopen(adapter, 0);
-+                                debug_text_event(adapter->erp_dbf,1,"prot_link_down");
-+                        }
-+			zfcp_cmd_dbf_event_fsf(
-+				"linkdown", fsf_req,
-+				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                        zfcp_callback_do_link_down(adapter);
-+			break;
-+
-+		case FSF_PROT_REEST_QUEUE :
-+			ZFCP_LOG_FLAGS(1, "FSF_PROT_REEST_QUEUE\n"); 
-+                        debug_text_event(adapter->erp_dbf,1,"prot_reest_queue");
-+                        ZFCP_LOG_INFO("The local link to the adapter with "
-+                                      "devno 0x%04x was re-plugged. "
-+                                      "Re-starting operations on this adapter.\n", 
-+                                      adapter->devno);
-+                        /* All ports should be marked as ready to run again */
-+                        zfcp_erp_modify_adapter_status(
-+                                       adapter,
-+                                       ZFCP_STATUS_COMMON_RUNNING,
-+                                       ZFCP_SET);
-+                        zfcp_erp_adapter_reopen(
-+                                       adapter, 
-+                                       ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED
-+                                       | ZFCP_STATUS_COMMON_ERP_FAILED);
-+			zfcp_cmd_dbf_event_fsf(
-+				"reestque", fsf_req,
-+				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                        break;
-+			
-+		case FSF_PROT_ERROR_STATE :
-+			ZFCP_LOG_FLAGS(0, "FSF_PROT_ERROR_STATE\n");
-+			ZFCP_LOG_NORMAL(
-+				"error: The adapter with devno 0x%04x "
-+				"has entered the error state. "
-+                                "Restarting all operations on this "
-+                                "adapter.\n",
-+                                adapter->devno);
-+                        debug_text_event(adapter->erp_dbf,0,"prot_err_sta");
-+			/* restart operation on this adapter */
-+                        zfcp_erp_adapter_reopen(adapter,0);
-+			zfcp_cmd_dbf_event_fsf(
-+				"proterrs", fsf_req,
-+				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY;
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			break;
-+
-+		default :
-+			ZFCP_LOG_NORMAL(
-+				"bug: Transfer protocol status information "
-+				"provided by the adapter with devno 0x%04x "
-+                                "is not compatible with the device driver. "
-+                                "Stopping all operations on this adapter. "
-+                                "(debug info 0x%x).\n",
-+                                adapter->devno,
-+				fsf_req->qtcb->prefix.prot_status);
-+			ZFCP_LOG_NORMAL(
-+				"fsf_req=0x%lx, qtcb=0x%lx\n",
-+				(unsigned long)fsf_req,
-+				(unsigned long)fsf_req->qtcb);
-+			ZFCP_HEX_DUMP(
-+				ZFCP_LOG_LEVEL_NORMAL,
-+				(char*)fsf_req,
-+				sizeof(zfcp_fsf_req_t));
-+                        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, (char *)fsf_req->qtcb, sizeof(fsf_qtcb_t));
-+                        debug_text_event(adapter->erp_dbf,0,"prot_inval:");
-+                        debug_exception(adapter->erp_dbf,0,
-+                                        &fsf_req->qtcb->prefix.prot_status,
-+                                        sizeof(u32));
-+                        //                        panic("it was pity");
-+                        zfcp_erp_adapter_shutdown(adapter, 0);
-+			zfcp_cmd_dbf_event_fsf(
-+				"undefps", fsf_req,
-+				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+	}
-+        
-+ skip_protstatus:
-+	/*
-+	 * always call specific handlers to give them a chance to do
-+	 * something meaningful even in error cases
-+	 */
-+	zfcp_fsf_fsfstatus_eval(fsf_req);
-+        
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+	return retval;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_fsf_fsfstatus_eval
-+ *
-+ * purpose:	evaluates FSF status of completed FSF request
-+ *		and acts accordingly
-+ *
-+ * returns:
-+ */
-+static int zfcp_fsf_fsfstatus_eval(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		goto skip_fsfstatus;
-+	}
-+
-+	/* evaluate FSF Status */
-+	switch (fsf_req->qtcb->header.fsf_status) {
-+		case FSF_UNKNOWN_COMMAND :
-+			ZFCP_LOG_FLAGS(0, "FSF_UNKNOWN_COMMAND\n");
-+			ZFCP_LOG_NORMAL("bug: Command issued by the device driver is "
-+                                        "not known by the adapter with devno 0x%04x "
-+                                        "Stopping all operations on this adapter. "
-+                                        "(debug info 0x%x).\n",
-+                                        fsf_req->adapter->devno,
-+                                        fsf_req->qtcb->header.fsf_command);
-+                        debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_s_unknown");
-+                        zfcp_erp_adapter_shutdown(fsf_req->adapter, 0);
-+			zfcp_cmd_dbf_event_fsf(
-+				"unknownc", fsf_req,
-+				&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			break;
-+                        
-+                case FSF_FCP_RSP_AVAILABLE :
-+                        ZFCP_LOG_FLAGS(2, "FSF_FCP_RSP_AVAILABLE\n");
-+                        ZFCP_LOG_DEBUG("FCP Sense data will be presented to the "
-+                                      "SCSI stack.\n");
-+                        debug_text_event(fsf_req->adapter->erp_dbf,4,"fsf_s_rsp");
-+                        break;
-+
-+		case FSF_ADAPTER_STATUS_AVAILABLE :
-+			ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
-+                        debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_s_astatus");
-+                        zfcp_fsf_fsfstatus_qual_eval(fsf_req);
-+                        break;
-+
-+		default :
-+			break;
-+	}
-+
-+skip_fsfstatus:
-+	/*
-+         * always call specific handlers to give them a chance to do
-+         * something meaningful even in error cases
-+         */ 
-+	zfcp_fsf_req_dispatch(fsf_req);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+
-+/*
-+ * function:	zfcp_fsf_fsfstatus_qual_eval
-+ *
-+ * purpose:	evaluates FSF status-qualifier of completed FSF request
-+ *		and acts accordingly
-+ *
-+ * returns:
-+ */
-+static int zfcp_fsf_fsfstatus_qual_eval(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+        switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
-+        case FSF_SQ_FCP_RSP_AVAILABLE :
-+                ZFCP_LOG_FLAGS(2, "FSF_SQ_FCP_RSP_AVAILABLE\n");
-+                debug_text_event(fsf_req->adapter->erp_dbf,4,"fsf_sq_rsp");
-+                break;
-+        case FSF_SQ_RETRY_IF_POSSIBLE :
-+                ZFCP_LOG_FLAGS(2, "FSF_SQ_RETRY_IF_POSSIBLE\n");
-+                /* The SCSI-stack may now issue retries or escalate */
-+                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_sq_retry");
-+		zfcp_cmd_dbf_event_fsf(
-+			"sqretry", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+        case FSF_SQ_COMMAND_ABORTED :
-+                ZFCP_LOG_FLAGS(2, "FSF_SQ_COMMAND_ABORTED\n");
-+		/* Carry the aborted state on to upper layer */
-+                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_sq_abort");
-+		zfcp_cmd_dbf_event_fsf(
-+			"sqabort", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTED;
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+        case FSF_SQ_NO_RECOM :
-+                ZFCP_LOG_FLAGS(0, "FSF_SQ_NO_RECOM\n");
-+                debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_sq_no_rec");
-+                ZFCP_LOG_NORMAL("bug: No recommendation could be given for a"
-+                                "problem on the adapter with devno 0x%04x "
-+                                "Stopping all operations on this adapter. ",
-+                                fsf_req->adapter->devno);
-+                zfcp_erp_adapter_shutdown(fsf_req->adapter, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"sqnrecom", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+        case FSF_SQ_ULP_PROGRAMMING_ERROR :
-+                ZFCP_LOG_FLAGS(0, "FSF_SQ_ULP_PROGRAMMING_ERROR\n");
-+		ZFCP_LOG_NORMAL("error: not enough SBALs for data transfer "
-+				"(adapter devno=0x%04x)\n",
-+                                fsf_req->adapter->devno);
-+                debug_text_exception(fsf_req->adapter->erp_dbf, 0,
-+                                     "fsf_sq_ulp_err");
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+        case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
-+        case FSF_SQ_NO_RETRY_POSSIBLE :
-+        case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
-+                /* dealt with in the respective functions */
-+                break;
-+        default:
-+                ZFCP_LOG_NORMAL("bug: Additional status info could "
-+                                "not be interpreted properly.\n");
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
-+                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
-+                              16);
-+                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
-+                debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                &fsf_req->qtcb->header.fsf_status_qual.word[0],
-+                                sizeof(u32));
-+		zfcp_cmd_dbf_event_fsf(
-+			"squndef", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+        }
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_fsf_req_dispatch
-+ *
-+ * purpose:	calls the appropriate command specific handler
-+ *
-+ * returns:	
-+ */
-+static int zfcp_fsf_req_dispatch(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	zfcp_erp_action_t *erp_action = fsf_req->erp_action;
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		ZFCP_LOG_TRACE(
-+			"fsf_req=0x%lx, QTCB=0x%lx\n",
-+			(unsigned long)fsf_req,
-+			(unsigned long)(fsf_req->qtcb));
-+		ZFCP_HEX_DUMP(
-+			ZFCP_LOG_LEVEL_TRACE,
-+			(char *)fsf_req->qtcb,
-+			sizeof(fsf_qtcb_t));
-+	}
-+
-+	switch (fsf_req->fsf_command) {
-+
-+		case FSF_QTCB_FCP_CMND :
-+			ZFCP_LOG_FLAGS(3, "FSF_QTCB_FCP_CMND\n");
-+			zfcp_fsf_send_fcp_command_handler(fsf_req);
-+			break;
-+
-+		case FSF_QTCB_ABORT_FCP_CMND :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_ABORT_FCP_CMND\n");
-+			zfcp_fsf_abort_fcp_command_handler(fsf_req);
-+			break;
-+
-+		case FSF_QTCB_SEND_GENERIC :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_SEND_GENERIC\n");
-+			zfcp_fsf_send_ct_handler(fsf_req);
-+			break;
-+
-+		case FSF_QTCB_OPEN_PORT_WITH_DID :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_OPEN_PORT_WITH_DID\n");
-+			zfcp_fsf_open_port_handler(fsf_req);
-+			break;
-+
-+		case FSF_QTCB_OPEN_LUN :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_OPEN_LUN\n");
-+			zfcp_fsf_open_unit_handler(fsf_req);
-+			break;
-+
-+		case FSF_QTCB_CLOSE_LUN :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_LUN\n");
-+			zfcp_fsf_close_unit_handler(fsf_req);
-+			break;
-+
-+		case FSF_QTCB_CLOSE_PORT :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_PORT\n");
-+			zfcp_fsf_close_port_handler(fsf_req);
-+			break;
-+
-+                case FSF_QTCB_CLOSE_PHYSICAL_PORT :
-+                        ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_PHYSICAL_PORT\n");
-+                        zfcp_fsf_close_physical_port_handler(fsf_req);
-+                        break;
-+
-+		case FSF_QTCB_EXCHANGE_CONFIG_DATA :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_EXCHANGE_CONFIG_DATA\n");
-+			zfcp_fsf_exchange_config_data_handler(fsf_req);
-+                        break;
-+
-+		case FSF_QTCB_EXCHANGE_PORT_DATA :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_EXCHANGE_PORT_DATA\n");
-+			zfcp_fsf_exchange_port_data_handler(fsf_req);
-+			break;
-+
-+		case FSF_QTCB_SEND_ELS :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_SEND_ELS\n");
-+			zfcp_fsf_send_els_handler(fsf_req);
-+			break;
-+
-+		case FSF_QTCB_DOWNLOAD_CONTROL_FILE :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_DOWNLOAD_CONTROL_FILE\n");
-+			zfcp_fsf_control_file_handler(fsf_req);
-+			break;
-+
-+		case FSF_QTCB_UPLOAD_CONTROL_FILE :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_UPLOAD_CONTROL_FILE\n");
-+			zfcp_fsf_control_file_handler(fsf_req);
-+			break;
-+
-+		default :
-+			ZFCP_LOG_FLAGS(2, "FSF_QTCB_UNKNOWN\n");
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			ZFCP_LOG_NORMAL(
-+				"bug: Command issued by the device driver is "
-+                                "not supported by the adapter with devno 0x%04x "
-+                                "(debug info 0x%lx 0x%x).\n",
-+                                fsf_req->adapter->devno,
-+				(unsigned long)fsf_req,
-+				fsf_req->fsf_command);
-+			if (fsf_req->fsf_command !=
-+			    fsf_req->qtcb->header.fsf_command)
-+                                ZFCP_LOG_NORMAL(
-+                                     "bug: Command issued by the device driver differs "
-+                                     "from the command returned by the adapter with devno "
-+                                     "0x%04x (debug info 0x%x, 0x%x).\n",
-+                                     fsf_req->adapter->devno,
-+                                     fsf_req->fsf_command,
-+                                     fsf_req->qtcb->header.fsf_command);
-+	}
-+
-+	if (erp_action)
-+	        zfcp_erp_async_handler(erp_action, 0);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_status_read
-+ *
-+ * purpose:	initiates a Status Read command at the specified adapter
-+ *
-+ * returns:
-+ */
-+static int zfcp_fsf_status_read(
-+	zfcp_adapter_t *adapter,
-+	int req_flags)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	zfcp_fsf_req_t *fsf_req;
-+	fsf_status_read_buffer_t *status_buffer;
-+	unsigned long lock_flags;
-+	volatile qdio_buffer_element_t *sbale;
-+        int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx, req_flags=0x%x)\n", 
-+		(unsigned long)adapter,
-+		req_flags);
-+
-+        /* setup new FSF request */
-+        retval = zfcp_fsf_req_create(
-+			adapter,
-+			FSF_QTCB_UNSOLICITED_STATUS,
-+			req_flags | ZFCP_REQ_USE_MEMPOOL,
-+                        &adapter->pool.fsf_req_status_read,
-+			&lock_flags,
-+			&fsf_req);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. Could not create an "
-+			"unsolicited status buffer for "
-+			"the adapter with devno 0x%04x.\n",
-+			adapter->devno);
-+                goto failed_req_create;
-+        }
-+
-+	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-+        sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
-+        sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
-+        fsf_req->sbale_curr = 2;
-+
-+	status_buffer = zfcp_mem_pool_find(&adapter->pool.data_status_read);
-+	if (!status_buffer) {
-+		ZFCP_LOG_NORMAL("bug: could not get some buffer\n");
-+		goto failed_buf;
-+	}
-+	fsf_req->data.status_read.buffer = status_buffer;
-+
-+	/* insert pointer to respective buffer */
-+	sbale = zfcp_qdio_sbale_curr(fsf_req);
-+	sbale->addr = (void *)status_buffer;
-+	sbale->length = sizeof(fsf_status_read_buffer_t);
-+
-+	/* start QDIO request for this FSF request */
-+        retval = zfcp_fsf_req_send(fsf_req, NULL);
-+        if (retval) {
-+                ZFCP_LOG_DEBUG(
-+                        "error: Could not set-up unsolicited status "
-+                        "environment.\n");
-+                goto failed_req_send;
-+        }
-+
-+        ZFCP_LOG_TRACE(
-+                "Status Read request initiated "
-+                "(adapter devno=0x%04x)\n",
-+                adapter->devno);
-+#ifdef ZFCP_DEBUG_REQUESTS
-+        debug_text_event(adapter->req_dbf, 1, "unso");
-+#endif
-+
-+	goto out;
-+
-+failed_req_send:
-+	zfcp_mem_pool_return(status_buffer, &adapter->pool.data_status_read);
-+
-+failed_buf:
-+	if (zfcp_fsf_req_free(fsf_req)) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Could not remove one FSF "
-+			"request. Memory leakage possible. "
-+			"(debug info 0x%lx).\n",
-+			(unsigned long)fsf_req);
-+	};
-+
-+failed_req_create:
-+out:
-+        write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-+        
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+static int zfcp_fsf_status_read_port_closed(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	fsf_status_read_buffer_t *status_buffer = fsf_req->data.status_read.buffer;
-+	zfcp_adapter_t *adapter = fsf_req->adapter;
-+	unsigned long flags;
-+	zfcp_port_t *port;
-+
-+	write_lock_irqsave(&adapter->port_list_lock, flags);
-+	ZFCP_FOR_EACH_PORT (adapter, port)
-+		if (port->d_id == (status_buffer->d_id & ZFCP_DID_MASK))
-+			break;
-+	write_unlock_irqrestore(&adapter->port_list_lock, flags);
-+
-+	if (!port) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Re-open port indication received for the "
-+			"non-existing port with DID 0x%06x, on the adapter "
-+			"with devno 0x%04x. Ignored.\n",
-+			status_buffer->d_id & ZFCP_DID_MASK,
-+			adapter->devno);
-+		goto out;
-+	}
-+
-+	switch (status_buffer->status_subtype) {
-+
-+		case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT:
-+			ZFCP_LOG_FLAGS(2, "FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT\n");
-+			debug_text_event(adapter->erp_dbf, 3, "unsol_pc_phys:");
-+			zfcp_erp_port_reopen(port, 0);
-+			break;
-+
-+		case FSF_STATUS_READ_SUB_ERROR_PORT:
-+			ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_SUB_ERROR_PORT\n");
-+			debug_text_event(adapter->erp_dbf, 1, "unsol_pc_err:");
-+			zfcp_erp_port_shutdown(port, 0);
-+			break;
-+
-+		default:
-+			debug_text_event(adapter->erp_dbf, 0, "unsol_unk_sub:");
-+			debug_exception(
-+				adapter->erp_dbf, 0,
-+				&status_buffer->status_subtype, sizeof(u32));
-+			ZFCP_LOG_NORMAL(
-+				"bug: Undefined status subtype received "
-+				"for a re-open indication on the port with "
-+				"DID 0x%06x, on the adapter with devno "
-+				"0x%04x. Ignored. (debug info 0x%x)\n",
-+				status_buffer->d_id,
-+				adapter->devno,
-+				status_buffer->status_subtype);
-+	}
-+
-+out:
-+	return 0;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_status_read_handler
-+ *
-+ * purpose:	is called for finished Open Port command
-+ *
-+ * returns:	
-+ */
-+static int zfcp_fsf_status_read_handler(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = 0;
-+        zfcp_adapter_t *adapter = fsf_req->adapter;
-+        fsf_status_read_buffer_t *status_buffer = fsf_req->data.status_read.buffer; 
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
-+		zfcp_mem_pool_return(status_buffer, &adapter->pool.data_status_read);
-+                if (zfcp_fsf_req_cleanup(fsf_req)) {
-+                        ZFCP_LOG_NORMAL("bug: Could not remove one FSF "
-+                                        "request. Memory leakage possible. "
-+                                        "(debug info 0x%lx).\n",
-+                                        (unsigned long)fsf_req);
-+                }
-+		goto out;
-+        }
-+
-+	switch (status_buffer->status_type) {
-+
-+	case FSF_STATUS_READ_PORT_CLOSED:
-+                ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_PORT_CLOSED\n");
-+                debug_text_event(adapter->erp_dbf,3,"unsol_pclosed:");
-+                debug_event(adapter->erp_dbf,3,
-+                            &status_buffer->d_id,
-+                            sizeof(u32));
-+		zfcp_fsf_status_read_port_closed(fsf_req);
-+		break;
-+
-+	case FSF_STATUS_READ_INCOMING_ELS:
-+                ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_INCOMING_ELS\n");
-+                debug_text_event(adapter->erp_dbf,3,"unsol_els:");
-+                zfcp_fsf_incoming_els(fsf_req);
-+		break;
-+
-+        case FSF_STATUS_READ_BIT_ERROR_THRESHOLD:
-+                ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_BIT_ERROR_THRESHOLD\n");
-+                debug_text_event(adapter->erp_dbf,3,"unsol_bit_err:");
-+                ZFCP_LOG_NORMAL("Bit error threshold data received:\n");
-+                ZFCP_HEX_DUMP(
-+                              ZFCP_LOG_LEVEL_NORMAL, 
-+                              (char*)status_buffer,
-+                              sizeof(fsf_status_read_buffer_t));
-+		break;
-+                
-+        case FSF_STATUS_READ_LINK_DOWN:
-+                ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_LINK_DOWN\n");
-+		debug_text_event(adapter->erp_dbf, 0, "unsol_link_down:");
-+		ZFCP_LOG_INFO(
-+			"Local link to adapter with devno 0x%04x is down\n",
-+			adapter->devno);
-+		atomic_set_mask(
-+			ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
-+			&adapter->status);
-+		zfcp_erp_adapter_failed(adapter);
-+                zfcp_callback_do_link_down(adapter);
-+                break;
-+                
-+
-+	case FSF_STATUS_READ_CFDC_UPDATED:
-+		ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_CFDC_UPDATED\n");
-+		debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_upd:");
-+		ZFCP_LOG_NORMAL(
-+			"CFDC has been updated on the FCP adapter "
-+			"(devno=0x%04x)\n",
-+			adapter->devno);
-+		break;
-+                 
-+	case FSF_STATUS_READ_CFDC_HARDENED:
-+		ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_CFDC_HARDENED\n");
-+		debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_harden:");
-+		switch (status_buffer->status_subtype) {
-+		case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE:
-+			ZFCP_LOG_NORMAL(
-+				"CFDC has been saved on the SE "
-+				"(devno=0x%04x)\n",
-+				adapter->devno);
-+			break;
-+		case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2:
-+			ZFCP_LOG_NORMAL(
-+				"CFDC has been copied to the secondary SE "
-+				"(devno=0x%04x)\n",
-+				adapter->devno);
-+			break;
-+		default:
-+			ZFCP_LOG_NORMAL(
-+				"CFDC has been hardened on the FCP adapter "
-+				"(devno=0x%04x)\n",
-+				adapter->devno);
-+		}
-+		break;
-+
-+        case FSF_STATUS_READ_LINK_UP:
-+                ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_LINK_UP\n");
-+                debug_text_event(adapter->erp_dbf,2,"unsol_link_up:");
-+                ZFCP_LOG_INFO("The local link to the adapter with "
-+                              "devno 0x%04x was re-plugged. "
-+                              "Re-starting operations on this adapter..\n", 
-+                              adapter->devno);
-+                /* All ports should be marked as ready to run again */
-+                zfcp_erp_modify_adapter_status(
-+                                  adapter,
-+                                  ZFCP_STATUS_COMMON_RUNNING,
-+                                  ZFCP_SET);
-+                zfcp_erp_adapter_reopen(
-+                                  adapter, 
-+                                  ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED
-+                                  | ZFCP_STATUS_COMMON_ERP_FAILED);
-+
-+                zfcp_callback_do_link_up(adapter);
-+                break;
-+                
-+	default:
-+                debug_text_event(adapter->erp_dbf,0,"unsol_unknown:");
-+                debug_exception(adapter->erp_dbf,0,
-+                                &status_buffer->status_type,
-+                                sizeof(u32));
-+		ZFCP_LOG_NORMAL("bug: An unsolicited status packet of unknown "
-+                               "type was received by the zfcp-driver "
-+                               "(debug info 0x%x)\n",
-+                                status_buffer->status_type);
-+                ZFCP_LOG_DEBUG("Dump of status_read_buffer 0x%lx:\n", 
-+                              (unsigned long)status_buffer);
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, 
-+                              (char*)status_buffer,
-+                              sizeof(fsf_status_read_buffer_t));
-+                break;
-+	}
-+
-+	zfcp_mem_pool_return(status_buffer, &adapter->pool.data_status_read);
-+        if (zfcp_fsf_req_cleanup(fsf_req)) {
-+                ZFCP_LOG_NORMAL("bug: Could not remove one FSF "
-+                                "request. Memory leakage possible. "
-+                                "(debug info 0x%lx).\n",
-+                                (unsigned long)fsf_req);
-+        }
-+        /* recycle buffer and start new request 
-+         * repeat until outbound queue is empty or adapter shutdown is requested*/
-+
-+        /* FIXME(qdio) - we may wait in the req_create for 5s during shutdown, so 
-+           qdio_cleanup will have to wait at least that long before returning with
-+           failure to allow us a proper cleanup under all circumstances
-+        */
-+	/* FIXME: allocation failure possible? (Is this code needed?) */
-+	retval = zfcp_fsf_status_read(adapter, 0);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO(
-+			"Outbound queue busy. "
-+			"Could not create use an "
-+			"unsolicited status read request for "
-+			"the adapter with devno 0x%04x.\n",
-+			adapter->devno);
-+		/* temporary fix to avoid status read buffer shortage */
-+		adapter->status_read_failed++;
-+		if ((ZFCP_STATUS_READS_RECOM - adapter->status_read_failed)
-+		    < ZFCP_STATUS_READ_FAILED_THRESHOLD) {
-+			ZFCP_LOG_INFO(
-+				"restart adapter due to status read "
-+				"buffer shortage (devno 0x%04x)\n",
-+				adapter->devno);
-+				zfcp_erp_adapter_reopen(adapter, 0);
-+		}
-+	}
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+void zfcp_fsf_incoming_els_rscn(
-+		zfcp_adapter_t *adapter,
-+		fsf_status_read_buffer_t *status_buffer) 
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+	fcp_rscn_head_t *fcp_rscn_head
-+		= (fcp_rscn_head_t *) status_buffer->payload;
-+        fcp_rscn_element_t *fcp_rscn_element
-+		= (fcp_rscn_element_t *) status_buffer->payload;
-+
-+        unsigned long flags;
-+        zfcp_port_t *port;
-+        int i;
-+        int known=0;
-+        int no_notifications=0;
-+        int range_mask=0;
-+        int reopen_unknown=0;
-+        /* see FC-FS */
-+        int no_entries=(fcp_rscn_head->payload_len / 4);
-+
-+	zfcp_in_els_dbf_event(adapter, "##rscn", status_buffer, fcp_rscn_head->payload_len);
-+
-+        for (i=1; i < no_entries; i++) {
-+                /* skip head and start with 1st element */
-+                fcp_rscn_element++;
-+                switch (fcp_rscn_element->addr_format) {
-+                case ZFCP_PORT_ADDRESS:
-+                        ZFCP_LOG_FLAGS(1,"ZFCP_PORT_ADDRESS\n");
-+                        range_mask=ZFCP_PORTS_RANGE_PORT;
-+                        no_notifications=1;
-+                        break;
-+                case ZFCP_AREA_ADDRESS:
-+                        ZFCP_LOG_FLAGS(1,"ZFCP_AREA_ADDRESS\n");
-+			/* skip head and start with 1st element */
-+                        range_mask=ZFCP_PORTS_RANGE_AREA;
-+                        no_notifications = ZFCP_NO_PORTS_PER_AREA;
-+                        break;
-+                case ZFCP_DOMAIN_ADDRESS:
-+                        ZFCP_LOG_FLAGS(1,"ZFCP_DOMAIN_ADDRESS\n");
-+                        range_mask=ZFCP_PORTS_RANGE_DOMAIN;
-+                        no_notifications = ZFCP_NO_PORTS_PER_DOMAIN;
-+                        break;
-+                case ZFCP_FABRIC_ADDRESS:
-+                        ZFCP_LOG_FLAGS(1,"ZFCP_FABRIC_ADDRESS\n");
-+                        range_mask=ZFCP_PORTS_RANGE_FABRIC;
-+                        no_notifications = ZFCP_NO_PORTS_PER_FABRIC;
-+                        break;
-+                }
-+                known=0;
-+                write_lock_irqsave(&adapter->port_list_lock, flags);
-+                ZFCP_FOR_EACH_PORT (adapter, port) {
-+                        if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status))
-+                                continue;
-+                        if(((u32)port->d_id & range_mask) 
-+                           == (u32)(fcp_rscn_element->nport_did & range_mask)) {
-+                                known++;
-+#if 0
-+                                printk("known=%d, reopen did 0x%x\n",
-+                                       known,
-+                                       fcp_rscn_element->nport_did);
-+#endif
-+				debug_text_event(adapter->erp_dbf,1,"unsol_els_rscnk:");
-+				zfcp_test_link(port);
-+                        }
-+                }
-+                write_unlock_irqrestore(&adapter->port_list_lock, flags);
-+#if 0
-+                printk("known %d, no_notifications %d\n",
-+                       known, no_notifications);
-+#endif
-+                if(known<no_notifications) {
-+                        ZFCP_LOG_DEBUG("At least one unknown port changed state. "
-+                                      "Unknown ports need to be reopened.\n");
-+                        reopen_unknown=1;
-+                }
-+        } // for (i=1; i < no_entries; i++)
-+        
-+        if(reopen_unknown) {
-+                ZFCP_LOG_DEBUG("At least one unknown did "
-+                              "underwent a state change.\n");
-+                write_lock_irqsave(&adapter->port_list_lock, flags);
-+                ZFCP_FOR_EACH_PORT (adapter, port) {
-+			if (atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER, &port->status))
-+				continue;
-+			if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status)) {
-+                                ZFCP_LOG_INFO("Received state change notification."
-+                                                "Trying to open the port with WWPN "
-+                                                "0x%016Lx. Hope it's there now.\n",
-+                                                (llui_t)port->wwpn);
-+				debug_text_event(adapter->erp_dbf,1,"unsol_els_rscnu:");
-+                                zfcp_erp_port_reopen(port, 
-+                                                     ZFCP_STATUS_COMMON_ERP_FAILED);
-+                        }
-+                }
-+                write_unlock_irqrestore(&adapter->port_list_lock, flags);
-+        }
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+void zfcp_fsf_incoming_els_plogi(
-+		zfcp_adapter_t *adapter,
-+		fsf_status_read_buffer_t *status_buffer)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	logi *els_logi = (logi*) status_buffer->payload;
-+	zfcp_port_t *port;
-+	unsigned long flags;
-+
-+	zfcp_in_els_dbf_event(adapter, "##plogi", status_buffer, 28);
-+
-+	write_lock_irqsave(&adapter->port_list_lock, flags);
-+	ZFCP_FOR_EACH_PORT(adapter, port) {
-+		if (port->wwpn == (*(wwn_t *)&els_logi->nport_wwn))
-+			break;
-+	}
-+	write_unlock_irqrestore(&adapter->port_list_lock, flags);
-+
-+	if (!port) {
-+		ZFCP_LOG_DEBUG(
-+			"Re-open port indication received "
-+			"for the non-existing port with D_ID "
-+			"0x%06x, on the adapter with devno "
-+			"0x%04x. Ignored.\n",
-+			status_buffer->d_id,
-+			adapter->devno);
-+	} else	{
-+		debug_text_event(adapter->erp_dbf, 1, "unsol_els_plogi:");
-+		debug_event(adapter->erp_dbf, 1, &els_logi->nport_wwn, 8);
-+		zfcp_erp_port_forced_reopen(port, 0);
-+	}
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+void zfcp_fsf_incoming_els_logo(
-+		zfcp_adapter_t *adapter,
-+		fsf_status_read_buffer_t *status_buffer)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	fcp_logo_t *els_logo = (fcp_logo_t*) status_buffer->payload;
-+	zfcp_port_t *port;
-+	unsigned long flags;
-+
-+	zfcp_in_els_dbf_event(adapter, "##logo", status_buffer, 16);
-+
-+	write_lock_irqsave(&adapter->port_list_lock, flags);
-+	ZFCP_FOR_EACH_PORT(adapter, port) {
-+		if (port->wwpn == els_logo->nport_wwpn)
-+			break;
-+	}
-+	write_unlock_irqrestore(&adapter->port_list_lock, flags);
-+
-+	if (!port) {
-+		ZFCP_LOG_DEBUG(
-+			"Re-open port indication received "
-+			"for the non-existing port with D_ID "
-+			"0x%06x, on the adapter with devno "
-+			"0x%04x. Ignored.\n",
-+			status_buffer->d_id,
-+			adapter->devno);
-+	} else	{
-+		debug_text_event(adapter->erp_dbf, 1, "unsol_els_logo:");
-+		debug_event(adapter->erp_dbf, 1, &els_logo->nport_wwpn, 8);
-+		zfcp_erp_port_forced_reopen(port, 0);
-+	}
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+void zfcp_fsf_incoming_els_unknown(
-+		zfcp_adapter_t *adapter,
-+		fsf_status_read_buffer_t *status_buffer)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	zfcp_in_els_dbf_event(adapter, "##undef", status_buffer, 24);
-+	ZFCP_LOG_NORMAL(
-+		"warning: Unknown incoming ELS (0x%x) received "
-+		"for the adapter with devno 0x%04x\n",
-+		*(u32*)(status_buffer->payload),
-+		adapter->devno);
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+void zfcp_fsf_incoming_els(zfcp_fsf_req_t *fsf_req) 
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	fsf_status_read_buffer_t *status_buffer = fsf_req->data.status_read.buffer; 
-+	u32 els_type = *(u32*)(status_buffer->payload);
-+	zfcp_adapter_t *adapter = fsf_req->adapter;
-+
-+	if (els_type == LS_PLOGI)
-+        	zfcp_fsf_incoming_els_plogi(adapter, status_buffer);
-+	else if (els_type == LS_LOGO)
-+		zfcp_fsf_incoming_els_logo(adapter, status_buffer);
-+	else if ((els_type & 0xffff0000) == LS_RSCN)
-+		/* we are only concerned with the command, not the length */
-+		zfcp_fsf_incoming_els_rscn(adapter, status_buffer);
-+	else	zfcp_fsf_incoming_els_unknown(adapter, status_buffer);
-+
-+	zfcp_callback_do_incomming_els(adapter, status_buffer->payload);
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_start_scsi_er_timer
-+ *
-+ * purpose:     sets up the timer to watch over SCSI error recovery
-+ *              actions and starts it
-+ *
-+ */
-+static void zfcp_fsf_start_scsi_er_timer(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+	ZFCP_LOG_TRACE("enter (adapter=0x%lx\n",
-+                       (unsigned long)adapter);
-+        adapter->scsi_er_timer.function = 
-+                zfcp_fsf_scsi_er_timeout_handler;
-+        adapter->scsi_er_timer.data = 
-+                (unsigned long)adapter;
-+        adapter->scsi_er_timer.expires = 
-+                jiffies + ZFCP_SCSI_ER_TIMEOUT;
-+        add_timer(&adapter->scsi_er_timer);
-+
-+        ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+
-+/*
-+ * function:    zfcp_fsf_abort_fcp_command
-+ *
-+ * purpose:	tells FSF to abort a running SCSI command
-+ *
-+ * returns:	address of initiated FSF request
-+ *		NULL - request could not be initiated
-+ *
-+ * FIXME(design) shouldn't this be modified to return an int
-+ *               also...don't know how though
-+ */
-+static zfcp_fsf_req_t * zfcp_fsf_abort_fcp_command(
-+		unsigned long old_req_id,
-+		zfcp_adapter_t *adapter,
-+		zfcp_unit_t *unit,
-+		int req_flags)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	volatile qdio_buffer_element_t *sbale;
-+	zfcp_fsf_req_t *fsf_req = NULL;
-+	int retval = 0;
-+	unsigned long lock_flags;
-+ 
-+	ZFCP_LOG_TRACE(
-+		"enter (old_req_id=0x%lx, adapter=0x%lx, "
-+		"unit=0x%lx, req_flags=0x%x)\n",
-+		old_req_id,
-+		(unsigned long)adapter,
-+		(unsigned long)unit,
-+		req_flags);
-+
-+	/* setup new FSF request */
-+	retval = zfcp_fsf_req_create(
-+			adapter,
-+			FSF_QTCB_ABORT_FCP_CMND,
-+			req_flags,
-+                        &adapter->pool.fsf_req_scsi,
-+			&lock_flags,
-+			&fsf_req);
-+	if (retval < 0) {
-+                ZFCP_LOG_INFO(
-+			"error: Out of resources. Could not create an "
-+                        "abort command request on the device with "
-+                        "the FCP_LUN 0x%016Lx connected to "
-+                        "the port with WWPN 0x%016Lx connected to "
-+                        "the adapter with devno 0x%04x.\n",
-+                        (llui_t)unit->fcp_lun,
-+                        (llui_t)unit->port->wwpn,
-+                        adapter->devno);
-+		goto out;
-+	}
-+        
-+	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-+        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-+        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-+
-+	fsf_req->data.abort_fcp_command.unit = unit;
-+
-+	/* set handles of unit and its parent port in QTCB */
-+	fsf_req->qtcb->header.lun_handle = unit->handle;
-+	fsf_req->qtcb->header.port_handle = unit->port->handle;
-+
-+	/* set handle of request which should be aborted */
-+        fsf_req->qtcb->bottom.support.req_handle = (u64)old_req_id;
-+
-+#if 0
-+	/* DEBUG */
-+	goto out;
-+#endif
-+
-+	/* start QDIO request for this FSF request */
-+        
-+        zfcp_fsf_start_scsi_er_timer(adapter);
-+        retval = zfcp_fsf_req_send(fsf_req, NULL);
-+	if (retval) {
-+                del_timer(&adapter->scsi_er_timer);
-+		ZFCP_LOG_INFO(
-+			"error: Could not send an abort command request "
-+			"for a command on the adapter with devno 0x%04x, "
-+                        "port WWPN 0x%016Lx and unit FCP_LUN 0x%016Lx\n",
-+			adapter->devno,
-+			(llui_t)unit->port->wwpn,
-+			(llui_t)unit->fcp_lun);
-+		if (zfcp_fsf_req_free(fsf_req)) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: Could not remove one FSF "
-+				"request. Memory leakage possible. "
-+                                "(debug info 0x%lx).\n",
-+                                (unsigned long)fsf_req);
-+                };
-+		fsf_req = NULL;
-+		goto out;
-+	}
-+
-+	ZFCP_LOG_DEBUG(
-+		"Abort FCP Command request initiated "
-+		"(adapter devno=0x%04x, port D_ID=0x%06x, "
-+		"unit FCP_LUN=0x%016Lx, old_req_id=0x%lx)\n",
-+		adapter->devno,
-+		unit->port->d_id,
-+		(llui_t)unit->fcp_lun,
-+		old_req_id);
-+	
-+out:
-+        write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-+
-+	ZFCP_LOG_DEBUG("exit (0x%lx)\n", (unsigned long)fsf_req);
-+ 
-+	return fsf_req;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_abort_fcp_command_handler
-+ *
-+ * purpose:	is called for finished Abort FCP Command request
-+ *
-+ * returns:	
-+ */
-+static int zfcp_fsf_abort_fcp_command_handler(
-+		zfcp_fsf_req_t *new_fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = -EINVAL;
-+	zfcp_unit_t *unit = new_fsf_req->data.abort_fcp_command.unit;
-+        unsigned char status_qual = new_fsf_req->qtcb->header.fsf_status_qual.word[0];
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (new_fsf_req=0x%lx)\n",
-+		(unsigned long)new_fsf_req);
-+
-+        del_timer(&new_fsf_req->adapter->scsi_er_timer);
-+
-+	if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		/* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */
-+		goto skip_fsfstatus;
-+	}
-+     
-+	/* evaluate FSF status in QTCB */
-+	switch (new_fsf_req->qtcb->header.fsf_status) {
-+                
-+        case FSF_PORT_HANDLE_NOT_VALID :
-+                if(status_qual>>4 != status_qual%0xf) {
-+                        ZFCP_LOG_FLAGS(2, "FSF_PORT_HANDLE_NOT_VALID\n");
-+                        debug_text_event(new_fsf_req->adapter->erp_dbf,3,"fsf_s_phand_nv0");
-+                        /* In this case a command that was sent prior to a port
-+                         * reopen was aborted (handles are different). This is fine.
-+                         */
-+                } else {
-+                        ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
-+                        ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
-+                                        "for the port with WWPN 0x%016Lx connected to "
-+                                        "the adapter of devno 0x%04x is "
-+                                        "not valid. This may happen occasionally.\n",
-+                                        unit->port->handle,
-+                                        (llui_t)unit->port->wwpn,
-+                                        unit->port->adapter->devno);
-+                        ZFCP_LOG_INFO("status qualifier:\n");
-+                        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
-+                                      (char*)&new_fsf_req->qtcb->header.fsf_status_qual,
-+                                      16);
-+                        /* Let's hope this sorts out the mess */
-+                        debug_text_event(new_fsf_req->adapter->erp_dbf,1,"fsf_s_phand_nv1");
-+                        zfcp_erp_adapter_reopen(unit->port->adapter, 0);
-+                        new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                }
-+                break;
-+                
-+        case FSF_LUN_HANDLE_NOT_VALID :
-+                if(status_qual>>4 != status_qual%0xf) {
-+                        /* 2 */
-+                        ZFCP_LOG_FLAGS(0, "FSF_LUN_HANDLE_NOT_VALID\n");
-+                        debug_text_event(new_fsf_req->adapter->erp_dbf,3,"fsf_s_lhand_nv0");
-+                        /* In this case a command that was sent prior to a unit
-+                         * reopen was aborted (handles are different). This is fine.
-+                         */
-+                } else {
-+                        ZFCP_LOG_FLAGS(1, "FSF_LUN_HANDLE_NOT_VALID\n");
-+                        ZFCP_LOG_INFO("Warning: Temporary LUN identifier (handle) 0x%x "
-+                                      "of the logical unit with FCP_LUN 0x%016Lx at "
-+                                      "the remote port with WWPN 0x%016Lx connected "
-+                                      "to the adapter with devno 0x%04x is " 
-+                                      "not valid. This may happen in rare cases."
-+                                      "Trying to re-establish link.\n",
-+                                      unit->handle,
-+                                      (llui_t)unit->fcp_lun,
-+                                      (llui_t)unit->port->wwpn,
-+                                      unit->port->adapter->devno);
-+                        ZFCP_LOG_DEBUG("Status qualifier data:\n");
-+                        ZFCP_HEX_DUMP(
-+                                      ZFCP_LOG_LEVEL_DEBUG,
-+                                      (char*)&new_fsf_req->qtcb->header.fsf_status_qual,
-+                                      16);
-+                        /* Let's hope this sorts out the mess */
-+                        debug_text_event(new_fsf_req->adapter->erp_dbf,1,"fsf_s_lhand_nv1");
-+                        zfcp_erp_port_reopen(unit->port, 0);
-+                        new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                }
-+                break;
-+
-+        case FSF_FCP_COMMAND_DOES_NOT_EXIST :
-+                ZFCP_LOG_FLAGS(2, "FSF_FCP_COMMAND_DOES_NOT_EXIST\n");
-+                retval = 0;
-+#ifdef ZFCP_DEBUG_REQUESTS
-+                  /* debug feature area which records fsf request sequence numbers */
-+	        debug_text_event(new_fsf_req->adapter->req_dbf, 3, "no_exist");
-+	        debug_event(new_fsf_req->adapter->req_dbf, 3, 
-+        	            &new_fsf_req->qtcb->bottom.support.req_handle,
-+	                    sizeof(unsigned long));
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+	        debug_text_event(new_fsf_req->adapter->erp_dbf,3,"fsf_s_no_exist");
-+                new_fsf_req->status
-+                        |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED;
-+                break;
-+                
-+        case FSF_PORT_BOXED :
-+                /* 2 */
-+                ZFCP_LOG_FLAGS(0, "FSF_PORT_BOXED\n");
-+                ZFCP_LOG_DEBUG("The remote port "
-+                               "with WWPN 0x%016Lx on the adapter with "
-+                               "devno 0x%04x needs to be reopened\n",
-+                               (llui_t)unit->port->wwpn,
-+                               unit->port->adapter->devno);
-+                debug_text_event(new_fsf_req->adapter->erp_dbf,2,"fsf_s_pboxed");
-+                zfcp_erp_port_reopen(unit->port, 0);
-+                new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-+                        | ZFCP_STATUS_FSFREQ_RETRY;
-+                break;
-+                        
-+        case FSF_ADAPTER_STATUS_AVAILABLE :
-+                /* 2 */
-+                ZFCP_LOG_FLAGS(0, "FSF_ADAPTER_STATUS_AVAILABLE\n");
-+                switch (new_fsf_req->qtcb->header.fsf_status_qual.word[0]){
-+                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
-+                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
-+                        debug_text_event(new_fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
-+                        /* reopening link to port */
-+                        zfcp_erp_port_reopen(unit->port, 0);
-+                        new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;
-+                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
-+                        ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
-+                        /* SCSI stack will escalate */
-+                        debug_text_event(new_fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
-+                        new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;
-+                default:
-+                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
-+                                        new_fsf_req->qtcb->header.fsf_status_qual.word[0]);
-+                        debug_text_event(new_fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
-+                        debug_exception(new_fsf_req->adapter->erp_dbf,0,
-+                                        &new_fsf_req->qtcb->header.fsf_status_qual.word[0],
-+                                        sizeof(u32));
-+                        break;
-+                }
-+                break;
-+                
-+        case FSF_GOOD :
-+                /* 3 */
-+                ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
-+                retval = 0;
-+                new_fsf_req->status
-+                        |= ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED;
-+                break;
-+                
-+        default :
-+                ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-+                                "(debug info 0x%x)\n",
-+                                new_fsf_req->qtcb->header.fsf_status);
-+                debug_text_event(new_fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
-+                debug_exception(new_fsf_req->adapter->erp_dbf,0,
-+                                &new_fsf_req->qtcb->header.fsf_status,
-+                                sizeof(u32));
-+                break;
-+	}
-+        
-+ skip_fsfstatus:
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+     
-+	return retval;
-+     
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_nameserver_enqueue
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_nameserver_enqueue(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+	int retval = 0;
-+	zfcp_port_t *port;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	/* generate port structure */
-+	retval = zfcp_port_enqueue(
-+			adapter,
-+			0,
-+			0,
-+			ZFCP_STATUS_PORT_NAMESERVER,
-+                        &port);
-+	if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not establish a connection to the "
-+                        "fabric name server connected to the "
-+			"adapter with devno 0x%04x\n",
-+			adapter->devno);
-+		goto out;
-+	}
-+	/* set special D_ID */
-+	port->d_id = ZFCP_DID_NAMESERVER;
-+        /* enter nameserver port into adapter struct */
-+        adapter->nameserver_port=port;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ *
-+ */
-+static void zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+        if ((gid_pn->ct.pool != 0)) {
-+		zfcp_mem_pool_return(gid_pn, gid_pn->ct.pool);
-+        } else {
-+                ZFCP_KFREE(gid_pn, sizeof(struct zfcp_gid_pn_data));
-+        }
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+	return;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ *
-+ */
-+static int zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn,
-+                                     zfcp_mem_pool_t *pool)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+#ifdef ZFCP_MEM_POOL_ONLY
-+	*gid_pn = NULL;
-+#else
-+	*gid_pn = ZFCP_KMALLOC(sizeof(struct zfcp_gid_pn_data), GFP_KERNEL);
-+#endif
-+	if ((*gid_pn == 0) && (pool != 0))
-+		*gid_pn = zfcp_mem_pool_find(pool);
-+
-+        if (*gid_pn == 0)
-+                return -ENOMEM;
-+
-+        (*gid_pn)->ct.req = &(*gid_pn)->req;
-+        (*gid_pn)->ct.resp = &(*gid_pn)->resp;
-+	(*gid_pn)->ct.req_count = (*gid_pn)->ct.resp_count = 1;
-+        (*gid_pn)->req.address = (char *) &(*gid_pn)->ct_iu_req;
-+        (*gid_pn)->resp.address = (char *) &(*gid_pn)->ct_iu_resp;
-+        (*gid_pn)->req.length = sizeof(struct ct_iu_ns_req);
-+        (*gid_pn)->resp.length = sizeof(struct ct_iu_gid_pn);
-+
-+	return 0;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ *
-+ */
-+static int zfcp_ns_gid_pn_request(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+        struct zfcp_gid_pn_data *gid_pn = 0;
-+        struct ct_iu_ns_req *ct_iu_req;
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
-+	if (!adapter->nameserver_port) {
-+		ZFCP_LOG_NORMAL("bug: no nameserver available\n");
-+		retval = -EINVAL;
-+		goto out;
-+	}
-+
-+        retval = zfcp_gid_pn_buffers_alloc(&gid_pn, &adapter->pool.data_gid_pn);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO("error: Out of memory. Could not allocate "
-+                              "buffers for nameserver request GID_PN. "
-+                              "(adapter: 0x%04x)\n", adapter->devno);
-+		goto out;
-+	}
-+
-+	/* setup nameserver request */
-+        ct_iu_req = (struct ct_iu_ns_req *) gid_pn->ct.req->address;
-+        ct_iu_req->header.revision = ZFCP_CT_REVISION;
-+        ct_iu_req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
-+        ct_iu_req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
-+        ct_iu_req->header.options = ZFCP_CT_SYNCHRONOUS;
-+        ct_iu_req->header.cmd_rsp_code = ZFCP_CT_GID_PN;
-+        ct_iu_req->header.max_res_size = ZFCP_CT_MAX_SIZE;
-+	ct_iu_req->data.wwpn = erp_action->port->wwpn;
-+
-+        /* setup parameters for send generic command */
-+        gid_pn->ct.port = adapter->nameserver_port;
-+	gid_pn->ct.handler = zfcp_ns_gid_pn_handler;
-+	gid_pn->ct.handler_data = (unsigned long) erp_action;
-+        gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
-+        gid_pn->ct.timer = &erp_action->timer;
-+        erp_action->data.gid_pn = gid_pn;
-+
-+	retval = zfcp_fsf_send_ct(&gid_pn->ct,
-+                                  &erp_action->adapter->pool.fsf_req_erp,
-+                                  erp_action);
-+	if (retval) {
-+		ZFCP_LOG_INFO("error: Could not send nameserver request GID_PN "
-+                              "via adapter with devno 0x%04x\n",
-+                              adapter->devno);
-+                zfcp_gid_pn_buffers_free(gid_pn);
-+                erp_action->data.gid_pn = 0;
-+	}
-+
-+ out:
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/**
-+ * 
-+ */
-+static void zfcp_ns_gid_pn_handler(unsigned long data)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+        zfcp_erp_action_t *erp_action = (zfcp_erp_action_t *) data;
-+	zfcp_port_t *port = erp_action->port;
-+        struct zfcp_send_ct *ct = &erp_action->data.gid_pn->ct;
-+	struct ct_iu_ns_req *ct_iu_req =
-+                (struct ct_iu_ns_req *) ct->req->address;
-+	struct ct_iu_gid_pn *ct_iu_resp =
-+                (struct ct_iu_gid_pn *) ct->resp->address;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        if (ct->status)
-+                goto failed;
-+
-+	if (zfcp_check_ct_response(&ct_iu_resp->header)) {
-+		/* FIXME: do we need some specific erp entry points */
-+		atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
-+		goto failed;
-+	}
-+	/* paranoia */
-+	if (ct_iu_req->data.wwpn != port->wwpn) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: Port WWPN returned by nameserver lookup "
-+                        "does not correspond to "
-+                        "the expected value on the adapter with devno 0x%04x. "
-+			"(debug info 0x%016Lx, 0x%016Lx)\n",
-+                        port->adapter->devno,
-+			(llui_t)port->wwpn,
-+                        (llui_t)ct_iu_req->data.wwpn);
-+		goto failed;
-+	}
-+
-+	/* looks like a valid d_id */
-+        port->d_id = ZFCP_DID_MASK & ct_iu_resp->d_id;
-+	atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
-+	ZFCP_LOG_DEBUG(
-+		"devno 0x%04x:  WWPN=0x%016Lx ---> D_ID=0x%06x\n",
-+		port->adapter->devno,
-+		(llui_t)port->wwpn,
-+		port->d_id);
-+	goto out;
-+
-+ failed:
-+	ZFCP_LOG_NORMAL(
-+		"warning: WWPN 0x%016Lx not found by nameserver lookup "
-+		"using the adapter with devno 0x%04x\n", 
-+		(llui_t)port->wwpn,
-+		port->adapter->devno);
-+	ZFCP_LOG_DEBUG("CT IUs do not match:\n");
-+	ZFCP_HEX_DUMP(
-+		ZFCP_LOG_LEVEL_DEBUG,
-+		(char*)ct_iu_req,
-+		sizeof(struct ct_iu_ns_req));
-+	ZFCP_HEX_DUMP(
-+		ZFCP_LOG_LEVEL_DEBUG,
-+		(char*)ct_iu_resp,
-+		sizeof(struct ct_iu_gid_pn));
-+
-+ out:
-+        zfcp_gid_pn_buffers_free(erp_action->data.gid_pn);
-+        erp_action->data.gid_pn = 0;
-+	ZFCP_LOG_TRACE("exit\n");
-+	return;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/**
-+ * FIXME: document
-+ * FIXME: check for FS_RJT IU and set appropriate return code
-+ */
-+int zfcp_ns_ga_nxt_request(zfcp_port_t *port, struct ct_iu_ga_nxt *ct_iu_resp)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+        struct ct_iu_ns_req *ct_iu_req;
-+        struct zfcp_send_ct *ct;
-+        zfcp_adapter_t *adapter = port->adapter;
-+	int ret = 0;
-+
-+	DECLARE_COMPLETION(wait);
-+
-+        memset(ct_iu_resp, 0, sizeof(*ct_iu_resp));
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	if (!adapter->nameserver_port) {
-+		ZFCP_LOG_NORMAL("bug: no nameserver available\n");
-+		ret = -EINVAL;
-+		goto out;
-+	}
-+
-+        if ((ct_iu_req =
-+             ZFCP_KMALLOC(sizeof(struct ct_iu_ns_req), GFP_KERNEL)) == 0) {
-+                ZFCP_LOG_INFO("error: Out of memory. Unable to create "
-+                              "CT request (FC-GS), adapter devno 0x%04x.\n",
-+                              adapter->devno);
-+                ret = -ENOMEM;
-+                goto out;
-+        }
-+
-+        if ((ct =
-+             ZFCP_KMALLOC(sizeof(struct zfcp_send_ct), GFP_KERNEL)) == 0) {
-+                ZFCP_LOG_INFO("error: Out of memory. Unable to create "
-+                              "CT request (FC-GS), adapter devno 0x%04x.\n",
-+                              adapter->devno);
-+                ret = -ENOMEM;
-+                goto free_ct_iu_req;
-+        }
-+
-+        if ((ct->req =
-+             ZFCP_KMALLOC(sizeof(struct scatterlist), GFP_KERNEL)) == 0) {
-+                ZFCP_LOG_INFO("error: Out of memory. Unable to create "
-+                              "CT request (FC-GS), adapter devno 0x%04x.\n",
-+                              adapter->devno);
-+                ret = -ENOMEM;
-+                goto free_ct;
-+        }
-+
-+        if ((ct->resp =
-+             ZFCP_KMALLOC(sizeof(struct scatterlist), GFP_KERNEL)) == 0) {
-+                ZFCP_LOG_INFO("error: Out of memory. Unable to create "
-+                              "CT request (FC-GS), adapter devno 0x%04x.\n",
-+                              adapter->devno);
-+                ret = -ENOMEM;
-+                goto free_req;
-+        }
-+
-+	/* setup nameserver request */
-+        ct_iu_req->header.revision = ZFCP_CT_REVISION;
-+        ct_iu_req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
-+        ct_iu_req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
-+        ct_iu_req->header.options = ZFCP_CT_SYNCHRONOUS;
-+        ct_iu_req->header.cmd_rsp_code = ZFCP_CT_GA_NXT;
-+        ct_iu_req->header.max_res_size = ZFCP_CT_MAX_SIZE;
-+	ct_iu_req->data.d_id = ZFCP_DID_MASK & (port->d_id - 1);
-+
-+	ct->completion = &wait;
-+        ct->req->address = (char *) ct_iu_req;
-+        ct->resp->address = (char *) ct_iu_resp;
-+        ct->req->length = sizeof(*ct_iu_req);
-+        ct->resp->length = sizeof(*ct_iu_resp);
-+        ct->req_count = ct->resp_count = 1;
-+
-+        /* setup parameters for send generic command */
-+        ct->port = adapter->nameserver_port;
-+	ct->handler = zfcp_ns_ga_nxt_handler;
-+	ct->handler_data = (unsigned long) ct;
-+
-+        ct->timeout = ZFCP_NS_GA_NXT_TIMEOUT;
-+
-+	ret = zfcp_fsf_send_ct(ct, NULL, NULL);
-+	if (ret) {
-+		ZFCP_LOG_INFO("error: Could not send nameserver request GA_NXT "
-+                              "via adapter with devno 0x%04x\n",
-+                              adapter->devno);
-+                goto free_resp;
-+	}
-+        wait_for_completion(&wait);
-+        ret = ct->status;
-+
-+ free_resp:
-+        ZFCP_KFREE(ct->resp, sizeof(struct scatterlist));
-+ free_req:
-+        ZFCP_KFREE(ct->req, sizeof(struct scatterlist));
-+ free_ct:
-+        ZFCP_KFREE(ct, sizeof(struct zfcp_send_ct));
-+ free_ct_iu_req:
-+        ZFCP_KFREE(ct_iu_req, sizeof(struct ct_iu_ns_req));
-+ out:
-+	ZFCP_LOG_TRACE("exit (%d)\n", ret);
-+	return ret;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * FIXME: document
-+ * FIXME: check for FS_RJT IU and return appropriate status
-+ */
-+static void zfcp_ns_ga_nxt_handler(unsigned long data)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+        struct zfcp_send_ct *ct = (struct zfcp_send_ct *) data;
-+	struct ct_iu_ns_req *ct_iu_req =
-+                (struct ct_iu_ns_req *) ct->req[0].address;
-+	struct ct_iu_ga_nxt *ct_iu_resp =
-+                (struct ct_iu_ga_nxt *) ct->resp[0].address;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	if (zfcp_check_ct_response(&ct_iu_resp->header))
-+		goto failed;
-+
-+	goto out;
-+
-+ failed:
-+        ct->status = -EIO;
-+	ZFCP_LOG_DEBUG("CT IU headers do not match:\n");
-+	ZFCP_HEX_DUMP(
-+		ZFCP_LOG_LEVEL_DEBUG,
-+		(char*)ct_iu_req,
-+		sizeof(struct ct_iu_ns_req));
-+	ZFCP_HEX_DUMP(
-+		ZFCP_LOG_LEVEL_DEBUG,
-+		(char*)ct_iu_resp,
-+		sizeof(struct ct_iu_gid_pn));
-+out:
-+	if (ct->completion != NULL) {
-+                complete(ct->completion);
-+        }
-+	ZFCP_LOG_TRACE("exit\n");
-+	return;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/* reject CT_IU reason codes acc. to FC-GS-4 */
-+static const struct zfcp_rc_entry zfcp_ct_rc[] = {
-+	{0x01, "invalid command code"},
-+	{0x02, "invalid version level"},
-+	{0x03, "logical error"},
-+	{0x04, "invalid CT_IU size"},
-+	{0x05, "logical busy"},
-+	{0x07, "protocol error"},
-+	{0x09, "unable to perform command request"},
-+	{0x0b, "command not supported"},
-+	{0x0d, "server not available"},
-+	{0x0e, "session could not be established"},
-+	{0xff, "vendor specific error"},
-+	{0, NULL},
-+};
-+
-+/* LS_RJT reason codes acc. to FC-FS */
-+static const struct zfcp_rc_entry zfcp_ls_rjt_rc[] = {
-+	{0x01, "invalid LS_Command code"},
-+	{0x03, "logical error"},
-+	{0x05, "logical busy"},
-+	{0x07, "protocol error"},
-+	{0x09, "unable to perform command request"},
-+	{0x0b, "command not supported"},
-+	{0x0e, "command already in progress"},
-+	{0xff, "vendor specific error"},
-+	{0, NULL},
-+};
-+
-+/* reject reason codes according to FC-PH/FC-FS */
-+static const struct zfcp_rc_entry zfcp_p_rjt_rc[] = {
-+	{0x01, "invalid D_ID"},
-+	{0x02, "invalid S_ID"},
-+	{0x03, "Nx_Port not available, temporary"},
-+	{0x04, "Nx_Port not available, permament"},
-+	{0x05, "class not supported"},
-+	{0x06, "delimiter usage error"},
-+	{0x07, "TYPE not supported"},
-+	{0x08, "invalid Link_Control"},
-+	{0x09, "invalid R_CTL field"},
-+	{0x0a, "invalid F_CTL field"},
-+	{0x0b, "invalid OX_ID"},
-+	{0x0c, "invalid RX_ID"},
-+	{0x0d, "invalid SEQ_ID"},
-+	{0x0e, "invalid DF_CTL"},
-+	{0x0f, "invalid SEQ_CNT"},
-+	{0x10, "invalid parameter field"},
-+	{0x11, "exchange error"},
-+	{0x12, "protocol error"},
-+	{0x13, "incorrect length"},
-+	{0x14, "unsupported ACK"},
-+	{0x15, "class of service not supported by entity at FFFFFE"},
-+	{0x16, "login required"},
-+	{0x17, "excessive sequences attempted"},
-+	{0x18, "unable to establish exchange"},
-+	{0x1a, "fabric path not available"},
-+	{0x1b, "invalid VC_ID (class 4)"},
-+	{0x1c, "invalid CS_CTL field"},
-+	{0x1d, "insufficient resources for VC (class 4)"},
-+	{0x1f, "invalid class of service"},
-+	{0x20, "preemption request rejected"},
-+	{0x21, "preemption not enabled"},
-+	{0x22, "multicast error"},
-+	{0x23, "multicast error terminate"},
-+	{0x24, "process login required"},
-+	{0xff, "vendor specific reject"},
-+	{0, NULL},
-+};
-+
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+/**
-+ * zfcp_rc_description - return description for given reaon code
-+ * @code: reason code
-+ * @rc_table: table of reason codes and descriptions
-+ */
-+static inline const char *
-+zfcp_rc_description(u8 code, const struct zfcp_rc_entry *rc_table)
-+{
-+	const char *descr = "unknown reason code";
-+
-+	do {
-+		if (code == rc_table->code) {
-+			descr = rc_table->description;
-+			break;
-+		}
-+		rc_table++;
-+	} while (rc_table->code && rc_table->description);
-+
-+	return descr;
-+}
-+		     
-+/**
-+ * zfcp_check_ct_response - evaluate reason code for CT_IU
-+ * @rjt: response payload to an CT_IU request
-+ * Return: 0 for accept CT_IU, 1 for reject CT_IU or invlid response code
-+ */
-+int
-+zfcp_check_ct_response(struct ct_hdr *rjt)
-+{
-+	if (rjt->cmd_rsp_code == ZFCP_CT_ACCEPT)
-+		return 0;
-+
-+	if (rjt->cmd_rsp_code != ZFCP_CT_REJECT) {
-+		ZFCP_LOG_NORMAL("error: invalid Generic Service command/"
-+				"response code (0x%04hx)\n",
-+				rjt->cmd_rsp_code);
-+		return 1;
-+	}
-+
-+	ZFCP_LOG_INFO("Generic Service command rejected\n");
-+	ZFCP_LOG_INFO("%s (0x%02x, 0x%02x, 0x%02x)\n",
-+		      zfcp_rc_description(rjt->reason_code, zfcp_ct_rc),
-+		      (u32) rjt->reason_code, (u32) rjt->reason_code_expl,
-+		      (u32) rjt->vendor_unique);
-+
-+	return 1;
-+}
-+
-+/**
-+ * zfcp_print_els_rjt - print reject parameter and description for ELS reject
-+ * @rjt_par: reject parameter acc. to FC-PH/FC-FS
-+ * @rc_table: table of reason codes and descriptions
-+ */
-+static inline void
-+zfcp_print_els_rjt(struct zfcp_ls_rjt_par *rjt_par,
-+		   const struct zfcp_rc_entry *rc_table)
-+{
-+	ZFCP_LOG_INFO("%s (%02x %02x %02x %02x)\n",
-+		      zfcp_rc_description(rjt_par->reason_code, rc_table),
-+		      (u32) rjt_par->action, (u32) rjt_par->reason_code,
-+		      (u32) rjt_par->reason_expl, (u32) rjt_par->vendor_unique);
-+}
-+
-+/**
-+ * zfcp_fsf_handle_els_rjt - evaluate status qualifier/reason code on ELS reject
-+ * @sq: status qualifier word
-+ * @rjt_par: reject parameter as described in FC-PH and FC-FS
-+ * Return: -EROMTEIO for LS_RJT, -EREMCHG for invalid D_ID, -EIO else
-+ */
-+int
-+zfcp_handle_els_rjt(u32 sq, struct zfcp_ls_rjt_par *rjt_par)
-+{
-+	int ret = -EIO;
-+
-+	if (sq == FSF_IOSTAT_NPORT_RJT) {
-+		ZFCP_LOG_INFO("ELS rejected (P_RJT)\n");
-+		zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc);
-+		/* invalid d_id */
-+		if (rjt_par->reason_code == 0x01)
-+			ret = -EREMCHG;
-+	} else if (sq == FSF_IOSTAT_FABRIC_RJT) {
-+		ZFCP_LOG_INFO("ELS rejected (F_RJT)\n");
-+		zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc);
-+		/* invalid d_id */
-+		if (rjt_par->reason_code == 0x01)
-+			ret = -EREMCHG; 
-+	} else if (sq == FSF_IOSTAT_LS_RJT) {
-+		ZFCP_LOG_INFO("ELS rejected (LS_RJT)\n");
-+		zfcp_print_els_rjt(rjt_par, zfcp_ls_rjt_rc);
-+		ret = -EREMOTEIO;
-+	} else
-+		ZFCP_LOG_INFO("unexpected SQ: 0x%02x\n", sq);
-+
-+	return ret;
-+}
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+
-+
-+/*
-+ * checks whether req buffer and resp bother fit into one SBALE each
-+ */
-+static inline int
-+zfcp_use_one_sbal(struct scatterlist *req, int req_count,
-+                  struct scatterlist *resp, int resp_count)
-+{
-+        return ((req_count == 1) && (resp_count == 1) &&
-+                (((unsigned long) req[0].address & PAGE_MASK) ==
-+                 ((unsigned long) (req[0].address +
-+                                   req[0].length - 1) & PAGE_MASK)) &&
-+                (((unsigned long) resp[0].address & PAGE_MASK) ==
-+                 ((unsigned long) (resp[0].address +
-+                                  resp[0].length - 1) & PAGE_MASK)));
-+}
-+
-+/**
-+ * FIXME: doc
-+ */
-+int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, zfcp_mem_pool_t *pool,
-+                     zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = 0;
-+	volatile qdio_buffer_element_t *sbale;
-+	zfcp_port_t *port = ct->port;
-+	zfcp_adapter_t *adapter = port->adapter;
-+        zfcp_fsf_req_t *fsf_req;
-+        unsigned long lock_flags;
-+        int bytes;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	/* setup new FSF request */
-+	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC,
-+                                     ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-+                                     pool, &lock_flags, &fsf_req);
-+	if (retval < 0) {
-+                ZFCP_LOG_INFO("error: Out of resources. "
-+                              "Could not create a CT request (FC-GS), "
-+                              "destination port D_ID is 0x%06x "
-+                              "at the adapter with devno 0x%04x.\n",
-+                              ct->port->d_id, adapter->devno);
-+		goto failed_req;
-+	}
-+
-+        if (erp_action != 0) {
-+                erp_action->fsf_req = fsf_req;
-+                fsf_req->erp_action = erp_action;
-+        }
-+                
-+	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-+        if (zfcp_use_one_sbal(ct->req, ct->req_count,
-+                              ct->resp, ct->resp_count)){
-+                /* both request buffer and response buffer
-+                   fit into one sbale each */
-+                sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
-+                sbale[2].addr = ct->req[0].address;
-+                sbale[2].length = ct->req[0].length;
-+                sbale[3].addr = ct->resp[0].address;
-+                sbale[3].length = ct->resp[0].length;
-+                sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
-+        } else if (adapter->supported_features &
-+                   FSF_FEATURE_ELS_CT_CHAINED_SBALS) {
-+                /* try to use chained SBALs */
-+                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
-+                                                SBAL_FLAGS0_TYPE_WRITE_READ,
-+                                                ct->req, ct->req_count,
-+                                                ZFCP_MAX_SBALS_PER_CT_REQ);
-+                if (bytes <= 0) {
-+                        ZFCP_LOG_INFO("error: Out of resources (outbuf). "
-+                                      "Could not create a CT request (FC-GS), "
-+                                      "destination port D_ID is 0x%06x "
-+                                      "at the adapter with devno 0x%04x.\n",
-+                                      ct->port->d_id, adapter->devno);
-+                        if (bytes == 0) {
-+                                retval = -ENOMEM;
-+                        } else {
-+                                retval = bytes;
-+                        }
-+                        goto failed_send;
-+                }
-+                fsf_req->qtcb->bottom.support.req_buf_length = bytes;
-+                fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
-+                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
-+                                                SBAL_FLAGS0_TYPE_WRITE_READ,
-+                                                ct->resp, ct->resp_count,
-+                                                ZFCP_MAX_SBALS_PER_CT_REQ);
-+                if (bytes <= 0) {
-+                        ZFCP_LOG_INFO("error: Out of resources (inbuf). "
-+                                      "Could not create a CT request (FC-GS), "
-+                                      "destination port D_ID is 0x%06x "
-+                                      "at the adapter with devno 0x%04x.\n",
-+                                      ct->port->d_id, adapter->devno);
-+                        if (bytes == 0) {
-+                                retval = -ENOMEM;
-+                        } else {
-+                                retval = bytes;
-+                        }
-+                        goto failed_send;
-+                }
-+                fsf_req->qtcb->bottom.support.resp_buf_length = bytes;
-+        } else {
-+                /* reject send generic request */
-+		ZFCP_LOG_INFO(
-+			"error: microcode does not support chained SBALs."
-+                        "CT request (FC-GS) too big."
-+                        "Destination port D_ID is 0x%06x "
-+			"at the adapter with devno 0x%04x.\n",
-+			port->d_id, adapter->devno);
-+                retval = -EOPNOTSUPP;
-+                goto failed_send;
-+        }
-+
-+	/* settings in QTCB */
-+	fsf_req->qtcb->header.port_handle = port->handle;
-+	fsf_req->qtcb->bottom.support.service_class = adapter->fc_service_class;
-+	fsf_req->qtcb->bottom.support.timeout = ct->timeout;
-+        fsf_req->data.send_ct = ct;
-+
-+	/* start QDIO request for this FSF request */
-+	retval = zfcp_fsf_req_send(fsf_req, ct->timer);
-+	if (retval) {
-+		ZFCP_LOG_DEBUG("error: Out of resources. Could not send a "
-+                               "generic services command via adapter with "
-+                               "devno 0x%04x, port WWPN 0x%016Lx\n",
-+                               adapter->devno,	(llui_t) port->wwpn);
-+		goto failed_send;
-+	} else {
-+                ZFCP_LOG_DEBUG("Send Generic request initiated "
-+                               "(adapter devno=0x%04x, port D_ID=0x%06x)\n",
-+                               adapter->devno, port->d_id);
-+                goto out;
-+        }
-+
-+ failed_send:
-+	if (zfcp_fsf_req_free(fsf_req)) {
-+                ZFCP_LOG_NORMAL("bug: Could not remove one FSF request. Memory "
-+                                "leakage possible. (debug info 0x%lx).\n",
-+                                (unsigned long)fsf_req);
-+                retval = -EINVAL;
-+	};
-+        if (erp_action != 0) {
-+                erp_action->fsf_req = NULL;
-+        }
-+ failed_req:
-+ out:
-+        write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-+                                     lock_flags);
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_send_ct_handler
-+ *
-+ * purpose:	is called for finished Send Generic request
-+ *
-+ * returns:	
-+ */
-+static int zfcp_fsf_send_ct_handler(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = -EINVAL;
-+	zfcp_port_t *port = fsf_req->data.send_ct->port;
-+	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
-+	u16 subtable, rule, counter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		/* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */
-+		goto skip_fsfstatus;
-+	}
-+
-+	/* evaluate FSF status in QTCB */
-+	switch (fsf_req->qtcb->header.fsf_status) {
-+
-+        case FSF_PORT_HANDLE_NOT_VALID :
-+                ZFCP_LOG_FLAGS(1,"FSF_PORT_HANDLE_NOT_VALID\n");
-+                ZFCP_LOG_DEBUG("Temporary port identifier (handle) 0x%x "
-+				"for the port with WWPN 0x%016Lx connected to "
-+                                "the adapter of devno 0x%04x is "
-+				"not valid. This may happen occasionally.\n",
-+				port->handle,
-+				(llui_t)port->wwpn,
-+                                port->adapter->devno);
-+                ZFCP_LOG_INFO("status qualifier:\n");
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
-+                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
-+                              16);
-+                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_phandle_nv");
-+                zfcp_erp_adapter_reopen(port->adapter, 0);
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+                
-+        case FSF_SERVICE_CLASS_NOT_SUPPORTED :
-+                ZFCP_LOG_FLAGS(0, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n");
-+                if(fsf_req->adapter->fc_service_class <= 3) {
-+                        ZFCP_LOG_NORMAL("error: The adapter with devno=0x%04x does "
-+                                        "not support fibre-channel class %d.\n",
-+                                        port->adapter->devno,
-+                                        fsf_req->adapter->fc_service_class);
-+                } else {
-+                        ZFCP_LOG_NORMAL( "bug: The fibre channel class at the adapter "
-+                                        "with devno 0x%04x is invalid. "
-+                                        "(debug info %d)\n",
-+                                        port->adapter->devno,
-+                                        fsf_req->adapter->fc_service_class);
-+                }
-+                /* stop operation for this adapter */
-+                debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_s_class_nsup");
-+                zfcp_erp_adapter_shutdown(port->adapter, 0);
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+
-+	case FSF_ACCESS_DENIED :
-+		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
-+		ZFCP_LOG_NORMAL("Access denied, cannot send generic command "
-+				"(devno=0x%04x wwpn=0x%016Lx)\n",
-+				port->adapter->devno,
-+				(llui_t)port->wwpn);
-+		for (counter = 0; counter < 2; counter++) {
-+			subtable = header->fsf_status_qual.halfword[counter * 2];
-+			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-+			switch (subtable) {
-+			case FSF_SQ_CFDC_SUBTABLE_OS:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-+			case FSF_SQ_CFDC_SUBTABLE_LUN:
-+				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-+					zfcp_act_subtable_type[subtable], rule);
-+				break;
-+			}
-+		}
-+		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+        case FSF_GENERIC_COMMAND_REJECTED :
-+                ZFCP_LOG_FLAGS(1,"FSF_GENERIC_COMMAND_REJECTED\n");
-+                ZFCP_LOG_INFO("warning: The port with WWPN 0x%016Lx connected to "
-+                              "the adapter of devno 0x%04x has "
-+                              "rejected a generic services command.\n",
-+                              (llui_t)port->wwpn,
-+                              port->adapter->devno);
-+                ZFCP_LOG_INFO("status qualifier:\n");
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
-+                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
-+                              16);
-+                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_gcom_rej");
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+
-+	case FSF_REQUEST_BUF_NOT_VALID :
-+		ZFCP_LOG_FLAGS(1, "FSF_REQUEST_BUF_NOT_VALID\n");
-+		ZFCP_LOG_NORMAL(
-+			"error: The port with WWPN 0x%016Lx connected to "
-+			"the adapter of devno 0x%04x has "
-+			"rejected a generic services command "
-+			"due to invalid request buffer.\n",
-+			(llui_t)port->wwpn,
-+			port->adapter->devno);
-+		debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_reqiv");
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+	case FSF_RESPONSE_BUF_NOT_VALID :
-+		ZFCP_LOG_FLAGS(1, "FSF_RESPONSE_BUF_NOT_VALID\n");
-+		ZFCP_LOG_NORMAL(
-+			"error: The port with WWPN 0x%016Lx connected to "
-+			"the adapter of devno 0x%04x has "
-+			"rejected a generic services command "
-+			"due to invalid response buffer.\n",
-+			(llui_t)port->wwpn,
-+			port->adapter->devno);
-+		debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_resiv");
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+        case FSF_PORT_BOXED :
-+                ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
-+                ZFCP_LOG_DEBUG("The remote port "
-+                               "with WWPN 0x%016Lx on the adapter with "
-+                               "devno 0x%04x needs to be reopened\n",
-+                               (llui_t)port->wwpn,
-+                               port->adapter->devno);
-+                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_s_pboxed");
-+                zfcp_erp_port_reopen(port, 0);
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-+                        | ZFCP_STATUS_FSFREQ_RETRY;
-+                break;
-+                        
-+        case FSF_ADAPTER_STATUS_AVAILABLE :
-+                ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
-+                switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
-+                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
-+                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
-+                        /* reopening link to port */
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
-+			zfcp_test_link(port);
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;
-+                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
-+                        /* ERP strategy will escalate */
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;
-+                
-+                default:
-+                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
-+                                        fsf_req->qtcb->header.fsf_status_qual.word[0]);
-+                        break;
-+                }
-+                break;
-+                
-+        case FSF_GOOD :
-+                ZFCP_LOG_FLAGS(2,"FSF_GOOD\n");
-+                retval = 0;
-+                break;
-+
-+        default :
-+                ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-+                                "(debug info 0x%x)\n",
-+                                fsf_req->qtcb->header.fsf_status);          
-+                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
-+                debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                &fsf_req->qtcb->header.fsf_status_qual.word[0],
-+                                sizeof(u32));
-+                break;
-+	}
-+
-+skip_fsfstatus:
-+	fsf_req->data.send_ct->status = retval;
-+
-+        /* callback */
-+	if (fsf_req->data.send_ct->handler != 0) {
-+                (fsf_req->data.send_ct->handler)
-+                        (fsf_req->data.send_ct->handler_data);
-+        }
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+     
-+	return retval;
-+     
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_send_els_handler
-+ *
-+ * purpose:     Handler for the Send ELS FSF requests
-+ *
-+ * returns:     0       - FSF request processed successfuly
-+ *              -EINVAL - FSF status is not 0
-+ */
-+static int zfcp_fsf_send_els_handler(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	zfcp_adapter_t *adapter = fsf_req->adapter;
-+	zfcp_port_t *port = fsf_req->data.send_els->port;
-+	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
-+	fsf_qtcb_bottom_support_t *bottom = &fsf_req->qtcb->bottom.support;
-+	struct zfcp_send_els *send_els = fsf_req->data.send_els;
-+	u16 subtable, rule, counter;
-+	int retval = -EINVAL;
-+
-+	ZFCP_LOG_TRACE("enter (fsf_req=0x%lx)\n", (unsigned long)fsf_req);
-+
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
-+		goto skip_fsfstatus;
-+
-+	switch (header->fsf_status) {
-+
-+	case FSF_GOOD:
-+		ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
-+		ZFCP_LOG_INFO(
-+			"The FSF request has been successfully completed "
-+			"(devno=0x%04x fsf_req.seq_no=%d)\n",
-+			adapter->devno,
-+			fsf_req->seq_no);
-+		retval = 0;
-+		break;
-+
-+	case FSF_SERVICE_CLASS_NOT_SUPPORTED:
-+		ZFCP_LOG_FLAGS(2, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n");
-+		if (adapter->fc_service_class <= 3) {
-+			ZFCP_LOG_INFO(
-+				"error: The adapter with devno=0x%04x does "
-+				"not support fibre-channel class %d\n",
-+				adapter->devno,
-+				adapter->fc_service_class);
-+		} else {
-+			ZFCP_LOG_INFO(
-+				"bug: The fibre channel class at the adapter "
-+				"with devno 0x%04x is invalid "
-+				"(debug info %d)\n",
-+				adapter->devno,
-+				adapter->fc_service_class);
-+		}
-+		debug_text_exception(adapter->erp_dbf, 0, "fsf_s_class_nsup");
-+		zfcp_erp_adapter_shutdown(port->adapter, 0);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+	case FSF_ACCESS_DENIED:
-+		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
-+		ZFCP_LOG_NORMAL("Access denied, cannot send ELS "
-+				"(devno=0x%04x wwpn=0x%016Lx)\n",
-+				adapter->devno,
-+				(llui_t)port->wwpn);
-+		for (counter = 0; counter < 2; counter++) {
-+			subtable = header->fsf_status_qual.halfword[counter * 2];
-+			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-+			switch (subtable) {
-+			case FSF_SQ_CFDC_SUBTABLE_OS:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-+			case FSF_SQ_CFDC_SUBTABLE_LUN:
-+				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-+					zfcp_act_subtable_type[subtable], rule);
-+				break;
-+			}
-+		}
-+		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+	case FSF_ELS_COMMAND_REJECTED:
-+		ZFCP_LOG_FLAGS(2, "FSF_ELS_COMMAND_REJECTED\n");
-+		ZFCP_LOG_INFO(
-+			"The ELS command has been rejected because "
-+			"a command filter in the FCP channel prohibited "
-+			"sending of the ELS to the SAN "
-+			"(devno=0x%04x wwpn=0x%016Lx)\n",
-+			adapter->devno,
-+			(llui_t)port->wwpn);
-+		break;
-+
-+	case FSF_PAYLOAD_SIZE_MISMATCH:
-+		ZFCP_LOG_FLAGS(2, "FSF_PAYLOAD_SIZE_MISMATCH\n");
-+		ZFCP_LOG_INFO(
-+			"ELS request size and ELS response size must be either "
-+			"both 0, or both greater than 0 "
-+			"(devno=0x%04x req_buf_length=%d resp_buf_length=%d)\n",
-+			adapter->devno,
-+			bottom->req_buf_length,
-+			bottom->resp_buf_length);
-+		break;
-+
-+	case FSF_REQUEST_SIZE_TOO_LARGE:
-+		ZFCP_LOG_FLAGS(2, "FSF_REQUEST_SIZE_TOO_LARGE\n");
-+		ZFCP_LOG_INFO(
-+			"Length of the ELS request buffer, "
-+			"specified in QTCB bottom, "
-+			"exceeds the size of the buffers "
-+			"that have been allocated for ELS request data "
-+			"(devno=0x%04x req_buf_length=%d)\n",
-+			adapter->devno,
-+			bottom->req_buf_length);
-+		break;
-+
-+	case FSF_RESPONSE_SIZE_TOO_LARGE:
-+		ZFCP_LOG_FLAGS(2, "FSF_RESPONSE_SIZE_TOO_LARGE\n");
-+		ZFCP_LOG_INFO(
-+			"Length of the ELS response buffer, "
-+			"specified in QTCB bottom, "
-+			"exceeds the size of the buffers "
-+			"that have been allocated for ELS response data "
-+			"(devno=0x%04x resp_buf_length=%d)\n",
-+			adapter->devno,
-+			bottom->resp_buf_length);
-+		break;
-+
-+	case FSF_ADAPTER_STATUS_AVAILABLE:
-+		ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
-+		switch (header->fsf_status_qual.word[0]){
-+
-+		case FSF_SQ_RETRY_IF_POSSIBLE:
-+			ZFCP_LOG_FLAGS(2, "FSF_SQ_RETRY_IF_POSSIBLE\n");
-+			debug_text_event(adapter->erp_dbf, 1, "fsf_sq_retry");
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			break;
-+
-+		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
-+			ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
-+			debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ulp");
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			retval =
-+			  zfcp_handle_els_rjt(header->fsf_status_qual.word[1],
-+					      (struct zfcp_ls_rjt_par *)
-+					      &header->fsf_status_qual.word[2]);
-+			break;
-+
-+		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-+			ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
-+			debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ltest");
-+			if (send_els->ls_code != ZFCP_LS_ADISC)
-+				zfcp_test_link(port);
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			break;
-+
-+		default:
-+			ZFCP_LOG_INFO(
-+				"bug: Wrong status qualifier 0x%x arrived.\n",
-+				header->fsf_status_qual.word[0]);
-+			ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
-+				(char*)header->fsf_status_qual.word, 16);
-+		}
-+		break;
-+
-+	case FSF_UNKNOWN_COMMAND:
-+		ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_COMMAND\n");
-+		ZFCP_LOG_INFO(
-+			"FSF command 0x%x is not supported by FCP adapter "
-+			"(devno=0x%04x)\n",
-+			fsf_req->fsf_command,
-+			adapter->devno);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+	default:
-+		ZFCP_LOG_NORMAL(
-+			"bug: An unknown FSF Status was presented "
-+			"(devno=0x%04x fsf_status=0x%08x)\n",
-+			adapter->devno,
-+			header->fsf_status);
-+		debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval");
-+		debug_exception(fsf_req->adapter->erp_dbf, 0,
-+			&header->fsf_status_qual.word[0], sizeof(u32));
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+	}
-+
-+skip_fsfstatus:
-+	send_els->status = retval;
-+
-+	if (send_els->handler != 0)
-+		send_els->handler(send_els->handler_data);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/**
-+ * zfcp_fsf_send_els - Send an ELS
-+ * @*els: to send
-+ * Returns: 0 on success, -E* code else
-+ *
-+ * Create a FSF request from an ELS and queue it for sending. Chaining is used
-+ * if needed and supported (in that order).
-+ */
-+int zfcp_fsf_send_els(struct zfcp_send_els *els)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	unsigned long lock_flags;
-+	int retval;
-+	zfcp_fsf_req_t *fsf_req;
-+	zfcp_port_t *port = els->port;
-+	zfcp_adapter_t *adapter = port->adapter;
-+	volatile struct qdio_buffer_element_t *sbale;
-+        int bytes;
-+
-+        retval = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS,
-+                                     ZFCP_REQ_AUTO_CLEANUP,
-+                                     NULL, &lock_flags, &fsf_req);
-+	if (retval < 0) {
-+                ZFCP_LOG_INFO("error: Out of resources. "
-+                              "Could not create an ELS request, "
-+                              "destination port D_ID is 0x%06x "
-+                              "at the adapter with devno 0x%04x.\n",
-+                              port->d_id, adapter->devno);
-+                goto failed_req;
-+	}
-+
-+	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-+        if (zfcp_use_one_sbal(els->req, els->req_count,
-+                              els->resp, els->resp_count)){
-+                /* both request buffer and response buffer
-+                   fit into one sbale each */
-+                sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
-+                sbale[2].addr = els->req[0].address;
-+                sbale[2].length = els->req[0].length;
-+                sbale[3].addr = els->resp[0].address;
-+                sbale[3].length = els->resp[0].length;
-+                sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
-+        } else if (adapter->supported_features &
-+                   FSF_FEATURE_ELS_CT_CHAINED_SBALS) {
-+                /* try to use chained SBALs */
-+                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
-+                                                SBAL_FLAGS0_TYPE_WRITE_READ,
-+                                                els->req, els->req_count,
-+                                                ZFCP_MAX_SBALS_PER_ELS_REQ);
-+                if (bytes <= 0) {
-+                        ZFCP_LOG_INFO("error: Out of resources (outbuf). "
-+                                      "Could not create an ELS request, "
-+                                      "destination port D_ID is 0x%06x "
-+                                      "at the adapter with devno 0x%04x.\n",
-+                                      port->d_id, adapter->devno);
-+                        if (bytes == 0) {
-+                                retval = -ENOMEM;
-+                        } else {
-+                                retval = bytes;
-+                        }
-+                        goto failed_send;
-+                }
-+                fsf_req->qtcb->bottom.support.req_buf_length = bytes;
-+                fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
-+                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
-+                                                SBAL_FLAGS0_TYPE_WRITE_READ,
-+                                                els->resp, els->resp_count,
-+                                                ZFCP_MAX_SBALS_PER_ELS_REQ);
-+                if (bytes <= 0) {
-+                        ZFCP_LOG_INFO("error: Out of resources (inbuf). "
-+                                      "Could not create an ELS request, "
-+                                      "destination port D_ID is 0x%06x "
-+                                      "at the adapter with devno 0x%04x.\n",
-+                                      port->d_id, adapter->devno);
-+                        if (bytes == 0) {
-+                                retval = -ENOMEM;
-+                        } else {
-+                                retval = bytes;
-+                        }
-+                        goto failed_send;
-+                }
-+                fsf_req->qtcb->bottom.support.resp_buf_length = bytes;
-+        } else {
-+                /* reject request */
-+		ZFCP_LOG_INFO("error: microcode does not support chained SBALs."
-+                              "ELS request too big."
-+                              "Destination port D_ID is 0x%06x "
-+                              "at the adapter with devno 0x%04x.\n",
-+                              port->d_id, adapter->devno);
-+                retval = -EOPNOTSUPP;
-+                goto failed_send;
-+        }
-+
-+	/* settings in QTCB */
-+	fsf_req->qtcb->bottom.support.d_id = port->d_id;
-+	fsf_req->qtcb->bottom.support.service_class = adapter->fc_service_class;
-+	fsf_req->qtcb->bottom.support.timeout = ZFCP_ELS_TIMEOUT;
-+	fsf_req->data.send_els = els;
-+
-+	/* start QDIO request for this FSF request */
-+	retval = zfcp_fsf_req_send(fsf_req, NULL);
-+	if (retval) {
-+		ZFCP_LOG_DEBUG("error: Out of resources. Could not send an "
-+                               "ELS command via adapter with "
-+                               "devno 0x%04x, port WWPN 0x%016Lx\n",
-+                               adapter->devno,	(llui_t) port->wwpn);
-+		goto failed_send;
-+	} else {
-+                ZFCP_LOG_DEBUG("ELS request initiated "
-+                               "(adapter devno=0x%04x, port D_ID=0x%06x)\n",
-+                               adapter->devno, port->d_id);
-+                goto out;
-+        }
-+
-+ failed_send:
-+	if (zfcp_fsf_req_free(fsf_req)) {
-+                ZFCP_LOG_NORMAL("bug: Could not remove one FSF request. Memory "
-+                                "leakage possible. (debug info 0x%lx).\n",
-+                                (unsigned long)fsf_req);
-+                retval = -EINVAL;
-+	};
-+ failed_req:
-+ out:
-+	write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-+                                     lock_flags);
-+
-+        return retval;
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+static inline volatile qdio_buffer_element_t * zfcp_qdio_sbale_get(
-+		zfcp_qdio_queue_t *queue,
-+		int sbal,
-+		int sbale)
-+{
-+	return &queue->buffer[sbal]->element[sbale];
-+}
-+
-+
-+static inline volatile qdio_buffer_element_t * zfcp_qdio_sbale_req(
-+		zfcp_fsf_req_t *fsf_req,
-+		int sbal,
-+		int sbale)
-+{
-+	return zfcp_qdio_sbale_get(
-+			&fsf_req->adapter->request_queue,
-+			sbal,
-+			sbale);
-+}
-+
-+
-+static inline volatile qdio_buffer_element_t * zfcp_qdio_sbale_resp(
-+		zfcp_fsf_req_t *fsf_req,
-+		int sbal,
-+		int sbale)
-+{
-+	return zfcp_qdio_sbale_get(
-+			&fsf_req->adapter->response_queue,
-+			sbal,
-+			sbale);
-+}
-+
-+
-+/* the following routines work on outbound queues */
-+static inline volatile qdio_buffer_element_t * zfcp_qdio_sbale_curr(
-+		zfcp_fsf_req_t *fsf_req)
-+{
-+	return zfcp_qdio_sbale_req(
-+			fsf_req,
-+			fsf_req->sbal_curr,
-+			fsf_req->sbale_curr);
-+}
-+
-+
-+/* can assume at least one free SBAL in outbound queue when called */
-+static inline void zfcp_qdio_sbal_limit(zfcp_fsf_req_t *fsf_req, int max_sbals)
-+{
-+	int count = atomic_read(&fsf_req->adapter->request_queue.free_count);
-+	count = min(count, max_sbals);
-+	fsf_req->sbal_last  = fsf_req->sbal_first;
-+	fsf_req->sbal_last += (count - 1);
-+	fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
-+}
-+
-+
-+static inline volatile qdio_buffer_element_t * zfcp_qdio_sbal_chain(
-+		zfcp_fsf_req_t *fsf_req,
-+		unsigned long sbtype)
-+{
-+	volatile qdio_buffer_element_t *sbale;
-+
-+	/* set last entry flag in current SBALE of current SBAL */
-+	sbale = zfcp_qdio_sbale_curr(fsf_req);
-+	sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
-+
-+	/* don't exceed last allowed SBAL */
-+	if (fsf_req->sbal_curr == fsf_req->sbal_last)
-+		return NULL;
-+
-+	/* set chaining flag in first SBALE of current SBAL */
-+	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-+	sbale->flags |= SBAL_FLAGS0_MORE_SBALS;
-+
-+	/* calculate index of next SBAL */
-+	fsf_req->sbal_curr++;
-+	fsf_req->sbal_curr %= QDIO_MAX_BUFFERS_PER_Q;
-+
-+	/* keep this requests number of SBALs up-to-date */
-+	fsf_req->sbal_number++;
-+
-+	/* start at first SBALE of new SBAL */
-+	fsf_req->sbale_curr = 0;
-+
-+	/* set storage-block type for new SBAL */
-+	sbale = zfcp_qdio_sbale_curr(fsf_req);
-+	sbale->flags |= sbtype;
-+
-+	return sbale;
-+}
-+
-+
-+static inline volatile qdio_buffer_element_t * zfcp_qdio_sbale_next(
-+		zfcp_fsf_req_t *fsf_req, unsigned long sbtype)
-+{
-+	if (fsf_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL)
-+		return zfcp_qdio_sbal_chain(fsf_req, sbtype);
-+
-+	fsf_req->sbale_curr++;
-+
-+	return zfcp_qdio_sbale_curr(fsf_req);
-+}
-+
-+
-+static inline int zfcp_qdio_sbals_zero(
-+		zfcp_qdio_queue_t *queue,
-+		int first,
-+		int last)
-+{
-+	qdio_buffer_t **buf = queue->buffer;
-+	int curr = first;
-+	int count = 0;
-+
-+	for(;;) {
-+		curr %= QDIO_MAX_BUFFERS_PER_Q;
-+		count++;
-+		memset(buf[curr], 0, sizeof(qdio_buffer_t));
-+		if (curr == last)
-+			break;
-+		curr++;
-+	}
-+	return count;
-+}
-+
-+
-+static inline int zfcp_qdio_sbals_wipe(
-+		zfcp_fsf_req_t *fsf_req)
-+{
-+	return zfcp_qdio_sbals_zero(
-+			&fsf_req->adapter->request_queue,
-+			fsf_req->sbal_first,
-+			fsf_req->sbal_curr);
-+}
-+
-+
-+static inline void zfcp_qdio_sbale_fill(
-+		zfcp_fsf_req_t *fsf_req,
-+		unsigned long sbtype,
-+		void *addr,
-+		int length)
-+{
-+	volatile qdio_buffer_element_t *sbale = zfcp_qdio_sbale_curr(fsf_req);
-+
-+	sbale->addr = addr;
-+	sbale->length = length;
-+}
-+
-+
-+static inline int zfcp_qdio_sbals_from_segment(
-+		zfcp_fsf_req_t *fsf_req,
-+		unsigned long sbtype,
-+		void* start_addr,
-+		unsigned long total_length)
-+{
-+	unsigned long remaining, length;
-+	void *addr;
-+
-+	/* split segment up heeding page boundaries */
-+	for (addr = start_addr,
-+	     remaining = total_length;
-+	     remaining;
-+	     addr += length,
-+	     remaining -= length) {
-+		/* get next free SBALE for new piece */
-+		if (!zfcp_qdio_sbale_next(fsf_req, sbtype)) {
-+			/* no SBALE left, clean up and leave */
-+			zfcp_qdio_sbals_wipe(fsf_req);
-+			return -EINVAL;
-+		}
-+		/* calculate length of new piece */
-+		length = min(remaining,
-+			     (PAGE_SIZE - ((unsigned long)addr & (PAGE_SIZE - 1))));
-+		/* fill current SBALE with calculated piece */
-+		zfcp_qdio_sbale_fill(fsf_req, sbtype, addr, length);
-+	}
-+	return total_length;
-+}
-+
-+
-+/* for exploiters with a scatter-gather list ready at hand */
-+static inline int
-+zfcp_qdio_sbals_from_sg(zfcp_fsf_req_t *fsf_req, unsigned long sbtype,
-+                        struct scatterlist *sg,	int sg_count, int max_sbals)
-+{
-+	int sg_index;
-+	struct scatterlist *sg_segment;
-+	int bytes, retval;
-+	volatile qdio_buffer_element_t *sbale;
-+	zfcp_adapter_t *adapter;
-+
-+	/* figure out last allowed SBAL */
-+	zfcp_qdio_sbal_limit(fsf_req, max_sbals);
-+
-+	/* set storage-block type for current SBAL */
-+	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-+	sbale->flags |= sbtype;
-+
-+	/* process all segements of scatter-gather list */
-+	for (sg_index = 0, sg_segment = sg, bytes = 0;
-+	     sg_index < sg_count;
-+	     sg_index++, sg_segment++) {
-+		retval = zfcp_qdio_sbals_from_segment(
-+				fsf_req,
-+				sbtype,
-+				sg_segment->address,
-+				sg_segment->length);
-+		if (retval < 0)
-+			return retval;
-+		bytes += retval;
-+	}
-+	/* assume that no other SBALEs are to follow in the same SBAL */
-+	sbale = zfcp_qdio_sbale_curr(fsf_req);
-+	sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
-+
-+#ifdef ZFCP_STAT_REQSIZES
-+	adapter = fsf_req->adapter;
-+	if (sbtype == SBAL_FLAGS0_TYPE_READ)
-+		zfcp_statistics_inc(adapter, &adapter->read_req_head, bytes);
-+	else	zfcp_statistics_inc(adapter, &adapter->write_req_head, bytes);
-+#endif
-+	return bytes;
-+}
-+
-+
-+/* for exploiters with just a buffer ready at hand */
-+static inline int zfcp_qdio_sbals_from_buffer(
-+		zfcp_fsf_req_t *fsf_req,
-+		unsigned long sbtype,
-+		void *buffer,
-+		unsigned long length,
-+		int max_sbals)
-+{
-+	struct scatterlist sg_segment;
-+
-+	sg_segment.address = buffer;
-+	sg_segment.length = length;
-+
-+	return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, &sg_segment, 1,
-+                                       max_sbals);
-+}
-+
-+
-+/* for exploiters with a SCSI command ready at hand */
-+static inline int zfcp_qdio_sbals_from_scsicmnd(
-+		zfcp_fsf_req_t *fsf_req,
-+		unsigned long sbtype,
-+		struct scsi_cmnd *scsi_cmnd)
-+{
-+	if (scsi_cmnd->use_sg)
-+		return zfcp_qdio_sbals_from_sg(fsf_req,	sbtype,
-+                                               (struct scatterlist *)
-+                                               scsi_cmnd->request_buffer,
-+                                               scsi_cmnd->use_sg,
-+                                               ZFCP_MAX_SBALS_PER_REQ);
-+	else
-+                return zfcp_qdio_sbals_from_buffer(fsf_req, sbtype,
-+                                                   scsi_cmnd->request_buffer,
-+                                                   scsi_cmnd->request_bufflen,
-+                                                   ZFCP_MAX_SBALS_PER_REQ);
-+}
-+
-+/*
-+ * function:
-+ *
-+ * purpose:
-+ *
-+ * returns:	address of initiated FSF request
-+ *		NULL - request could not be initiated
-+ */
-+static int zfcp_fsf_exchange_config_data(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	volatile qdio_buffer_element_t *sbale;
-+	int retval = 0;
-+	unsigned long lock_flags;
-+
-+	ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
-+
-+	/* setup new FSF request */
-+	retval = zfcp_fsf_req_create(
-+			erp_action->adapter,
-+			FSF_QTCB_EXCHANGE_CONFIG_DATA,
-+			ZFCP_REQ_AUTO_CLEANUP,
-+                        &erp_action->adapter->pool.fsf_req_erp,
-+			&lock_flags,
-+			&erp_action->fsf_req);
-+	if (retval < 0) {
-+                ZFCP_LOG_INFO(
-+			 "error: Out of resources. Could not create an "
-+                         "exchange configuration data request for"
-+                         "the adapter with devno 0x%04x.\n",
-+                         erp_action->adapter->devno);
-+		goto out;
-+	}
-+
-+
-+	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
-+                                    erp_action->fsf_req->sbal_curr, 0);
-+        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-+        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-+
-+	erp_action->fsf_req->erp_action = erp_action;
-+	erp_action->fsf_req->qtcb->bottom.config.feature_selection =
-+		FSF_FEATURE_CFDC;
-+
-+	/* start QDIO request for this FSF request */
-+        retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
-+        if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not send an exchange configuration data "
-+			"command on the adapter with devno 0x%04x\n",
-+			erp_action->adapter->devno);
-+                if (zfcp_fsf_req_free(erp_action->fsf_req)) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: Could not remove one FSF "
-+				"request. Memory leakage possible. "
-+                                "(debug info 0x%lx).\n",
-+                                (unsigned long)erp_action->fsf_req);
-+                }
-+		erp_action->fsf_req = NULL;
-+                goto out;
-+        }
-+ 
-+        ZFCP_LOG_DEBUG(
-+                "Exchange Configuration Data request initiated "
-+                "(adapter devno=0x%04x)\n",
-+                erp_action->adapter->devno);
-+
-+out:
-+        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
-+
-+        ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+ 
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/**
-+ * zfcp_fsf_exchange_config_evaluate
-+ * @fsf_req: fsf_req which belongs to xchg config data request
-+ * @xchg_ok: specifies if xchg config data was incomplete or complete (0/1)
-+ *
-+ * returns: -EIO on error, 0 otherwise
-+ */
-+static int
-+zfcp_fsf_exchange_config_evaluate(zfcp_fsf_req_t *fsf_req, int xchg_ok)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	fsf_qtcb_bottom_config_t *bottom;
-+	zfcp_adapter_t *adapter = fsf_req->adapter;
-+
-+	bottom = &fsf_req->qtcb->bottom.config;
-+	ZFCP_LOG_DEBUG(
-+		"low/high QTCB version 0x%x/0x%x of FSF\n",
-+		bottom->low_qtcb_version, bottom->high_qtcb_version);
-+	adapter->fsf_lic_version = bottom->lic_version;
-+	adapter->supported_features = bottom->supported_features;
-+
-+	if (xchg_ok) {
-+		adapter->wwnn = bottom->nport_serv_param.wwnn;
-+		adapter->wwpn = bottom->nport_serv_param.wwpn;
-+		adapter->s_id = bottom->s_id & ZFCP_DID_MASK;
-+		adapter->fc_topology = bottom->fc_topology;
-+		adapter->fc_link_speed = bottom->fc_link_speed;
-+		adapter->hydra_version = bottom->adapter_type;
-+	} else {
-+		adapter->wwnn = 0;
-+		adapter->wwpn = 0;
-+		adapter->s_id = 0;
-+		adapter->fc_topology = 0;
-+		adapter->fc_link_speed = 0;
-+		adapter->hydra_version = 0;
-+	}
-+
-+	if (adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT) {
-+		adapter->hardware_version = bottom->hardware_version;
-+		memcpy(adapter->serial_number, bottom->serial_number, 17);
-+		EBCASC(adapter->serial_number, sizeof(adapter->serial_number));
-+	}
-+
-+	ZFCP_LOG_INFO(
-+		"The adapter with devno=0x%04x reported "
-+		"the following characteristics:\n"
-+		"WWNN 0x%016Lx, WWPN 0x%016Lx, S_ID 0x%08x,\n"
-+		"adapter version 0x%x, LIC version 0x%x, "
-+		"FC link speed %d Gb/s\n",
-+		adapter->devno,
-+		(llui_t) adapter->wwnn, (llui_t) adapter->wwpn,
-+		(unsigned int) adapter->s_id,
-+		adapter->hydra_version,
-+		adapter->fsf_lic_version,
-+		adapter->fc_link_speed);
-+	if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) {
-+		ZFCP_LOG_NORMAL(
-+			"error: the adapter with devno 0x%04x "
-+			"only supports newer control block "
-+			"versions in comparison to this device "
-+			"driver (try updated device driver)\n",
-+			adapter->devno);
-+		debug_text_event(adapter->erp_dbf, 0, "low_qtcb_ver");
-+		zfcp_erp_adapter_shutdown(adapter, 0);
-+		return -EIO;
-+	}
-+	if (ZFCP_QTCB_VERSION > bottom->high_qtcb_version) {
-+		ZFCP_LOG_NORMAL(
-+			"error: the adapter with devno 0x%04x "
-+			"only supports older control block "
-+			"versions than this device driver uses"
-+			"(consider a microcode upgrade)\n",
-+			adapter->devno);
-+		debug_text_event(adapter->erp_dbf, 0, "high_qtcb_ver");
-+		zfcp_erp_adapter_shutdown(adapter, 0);
-+		return -EIO;
-+	}
-+	return 0;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_exchange_config_data_handler
-+ *
-+ * purpose:     is called for finished Exchange Configuration Data command
-+ *
-+ * returns:
-+ */
-+int zfcp_fsf_exchange_config_data_handler
-+	(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	int retval = -EIO;
-+	fsf_qtcb_bottom_config_t *bottom;
-+	zfcp_adapter_t *adapter = fsf_req->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+                (unsigned long)fsf_req);
-+
-+        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		/* don't set any value, stay with the old (unitialized) ones */ 
-+                goto skip_fsfstatus;
-+        }
-+
-+	switch (fsf_req->qtcb->header.fsf_status) {
-+
-+        case FSF_GOOD :
-+                ZFCP_LOG_FLAGS(2,"FSF_GOOD\n");
-+		if (zfcp_fsf_exchange_config_evaluate(fsf_req, 1))
-+			goto skip_fsfstatus;
-+                switch (adapter->fc_topology) {
-+                case FSF_TOPO_P2P:
-+                        ZFCP_LOG_FLAGS(1,"FSF_TOPO_P2P\n");
-+                        ZFCP_LOG_NORMAL("error: Point-to-point fibre-channel "
-+                                      "configuration detected "
-+                                      "at the adapter with devno "
-+                                      "0x%04x, not supported, shutting down adapter\n",
-+                                      adapter->devno);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,0,"top-p-to-p");
-+			zfcp_erp_adapter_shutdown(adapter, 0);
-+			goto skip_fsfstatus;
-+                case FSF_TOPO_AL:
-+                        ZFCP_LOG_FLAGS(1,"FSF_TOPO_AL\n");
-+                        ZFCP_LOG_NORMAL("error: Arbitrated loop fibre-channel "
-+                                      "topology detected "
-+                                      "at the adapter with devno "
-+                                      "0x%04x, not supported, shutting down adapter\n",
-+                                      adapter->devno);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,0,"top-al");
-+			zfcp_erp_adapter_shutdown(adapter, 0);
-+                        goto skip_fsfstatus;
-+                case FSF_TOPO_FABRIC:
-+                        ZFCP_LOG_FLAGS(1,"FSF_TOPO_FABRIC\n");
-+                        ZFCP_LOG_INFO("Switched fabric fibre-channel "
-+                                      "network detected "
-+                                      "at the adapter with devno "
-+                                      "0x%04x\n",
-+                                      adapter->devno);
-+                        break;
-+                default:
-+                        ZFCP_LOG_NORMAL("bug: The fibre-channel topology "
-+                                        "reported by the exchange "
-+                                        "configuration command for "
-+                                        "the adapter with devno "
-+                                        "0x%04x is not "
-+                                        "of a type known to the zfcp "
-+                                        "driver, shutting down adapter\n",
-+                                        adapter->devno);
-+                        debug_text_exception(fsf_req->adapter->erp_dbf,0,
-+                                             "unknown-topo");
-+			zfcp_erp_adapter_shutdown(adapter, 0);
-+                        goto skip_fsfstatus;
-+                }
-+		bottom = &fsf_req->qtcb->bottom.config;
-+                if (bottom->max_qtcb_size < sizeof(fsf_qtcb_t)) {
-+                        ZFCP_LOG_NORMAL("bug: Maximum QTCB size (%d bytes) "
-+                                        "allowed by the adapter with devno "
-+                                        "0x%04x is lower than the minimum "
-+                                        "required by the driver (%ld bytes).\n",
-+                                        bottom->max_qtcb_size,
-+                                        adapter->devno,
-+                                        sizeof(fsf_qtcb_t));
-+                        debug_text_event(fsf_req->adapter->erp_dbf,0,"qtcb-size");
-+                        debug_event(fsf_req->adapter->erp_dbf,0,&bottom->max_qtcb_size,
-+                                    sizeof(u32)); 
-+			zfcp_erp_adapter_shutdown(adapter, 0);
-+			goto skip_fsfstatus;
-+                }
-+                atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
-+                retval = 0;
-+
-+                zfcp_callback_do_adapter_add(NULL, adapter);
-+
-+                break;
-+
-+	case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
-+		debug_text_event(adapter->erp_dbf, 0, "xchg-inco");
-+
-+		if (zfcp_fsf_exchange_config_evaluate(fsf_req, 0))
-+			goto skip_fsfstatus;
-+
-+		ZFCP_LOG_INFO(
-+			"Local link to adapter with devno 0x%04x is down\n",
-+			adapter->devno);
-+		atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
-+				ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
-+				&adapter->status);
-+		zfcp_erp_adapter_failed(adapter);
-+		break;
-+
-+        default:
-+                /* retval is -EIO by default */
-+                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf-stat-ng");
-+                debug_event(fsf_req->adapter->erp_dbf,0,
-+                            &fsf_req->qtcb->header.fsf_status,
-+                            sizeof(u32)); 
-+                zfcp_erp_adapter_shutdown(adapter, 0);
-+	}
-+        
-+ skip_fsfstatus:
-+        
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+int zfcp_fsf_exchange_port_data(zfcp_adapter_t *adapter,
-+                                fsf_qtcb_bottom_port_t *data)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	volatile qdio_buffer_element_t *sbale;
-+	int retval = 0;
-+        int status;
-+	unsigned long lock_flags;
-+        zfcp_fsf_req_t *fsf_req;
-+
-+        if(!(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT)){
-+		ZFCP_LOG_INFO("error: exchange port data "
-+                              "command not supported by adapter  0x%4.4x\n",
-+                              adapter->devno);
-+                retval = ENOTSUPP;
-+                goto out;
-+        }
-+
-+	/* setup new FSF request */
-+	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
-+                                     0, 0, &lock_flags, &fsf_req);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO("error: Out of resources. Could not create an "
-+                              "exchange port data request for"
-+                              "the adapter with devno 0x%4.4x.\n",
-+                              adapter->devno);
-+		write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-+                                             lock_flags);
-+		goto out;
-+	}
-+
-+	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-+        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-+        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-+
-+        fsf_req->data.port_data = data;
-+
-+	/* start QDIO request for this FSF request */
-+	retval = zfcp_fsf_req_send(fsf_req, NULL);
-+	if (retval) {
-+		ZFCP_LOG_INFO("error: Could not send an exchange port data "
-+                              "command on the adapter with devno 0x%4.4x\n",
-+                              adapter->devno);
-+		if (zfcp_fsf_req_free(fsf_req)) {
-+			ZFCP_LOG_NORMAL("bug: Could not remove one FSF "
-+					"request. Memory leakage possible. "
-+					"(debug info 0x%lx).\n",
-+					(unsigned long)fsf_req);
-+		}
-+		fsf_req = NULL;
-+		write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-+                                             lock_flags);
-+		goto out;
-+	}
-+
-+	ZFCP_LOG_DEBUG("Exchange Port Data request initiated "
-+                       "(adapter devno=0x%x)\n", adapter->devno);
-+
-+
-+	write_unlock_irqrestore(&adapter->request_queue.queue_lock,
-+                                     lock_flags);
-+
-+        /* FIXME: could we wait interruptible here ? */
-+	retval = zfcp_fsf_req_wait_and_cleanup(fsf_req, ZFCP_UNINTERRUPTIBLE,
-+                                               &status);
-+
-+ out:
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+static int zfcp_fsf_exchange_port_data_handler(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	int retval = -EIO;
-+	fsf_qtcb_bottom_port_t *bottom;
-+	fsf_qtcb_bottom_port_t *data = fsf_req->data.port_data;
-+
-+	ZFCP_LOG_TRACE("enter (fsf_req=0x%lx)\n",
-+                       (unsigned long)fsf_req);
-+
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		/* don't set any value, stay with the old (unitialized) ones */ 
-+		goto skip_fsfstatus;
-+	}
-+
-+	/* evaluate FSF status in QTCB */
-+	switch (fsf_req->qtcb->header.fsf_status) {
-+        case FSF_GOOD :
-+                ZFCP_LOG_FLAGS(2,"FSF_GOOD\n");
-+                bottom = &fsf_req->qtcb->bottom.port;
-+                memcpy(data, bottom, sizeof(fsf_qtcb_bottom_port_t));
-+                retval = 0;
-+                break;
-+        default:
-+                /* retval is -EIO by default */
-+                debug_text_event(fsf_req->adapter->erp_dbf, 0,
-+                                 "fsf-stat-ng");
-+                debug_event(fsf_req->adapter->erp_dbf,0,
-+                            &fsf_req->qtcb->header.fsf_status,
-+                            sizeof(u32)); 
-+	}
-+
-+ skip_fsfstatus:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:    zfcp_fsf_open_port
-+ *
-+ * purpose:	
-+ *
-+ * returns:	address of initiated FSF request
-+ *		NULL - request could not be initiated 
-+ */
-+static int zfcp_fsf_open_port(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	volatile qdio_buffer_element_t *sbale;
-+	int retval = 0;
-+	unsigned long lock_flags;
-+ 
-+	ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
-+
-+	/* setup new FSF request */
-+	retval = zfcp_fsf_req_create(
-+			erp_action->adapter,
-+			FSF_QTCB_OPEN_PORT_WITH_DID,
-+			ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-+                        &erp_action->adapter->pool.fsf_req_erp,
-+			&lock_flags,
-+			&erp_action->fsf_req);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. Could not create an "
-+			"open port request for "
-+			"the port with WWPN 0x%016Lx connected to "
-+			"the adapter with devno 0x%04x.\n",
-+			(llui_t)erp_action->port->wwpn,
-+			erp_action->adapter->devno);
-+		goto out;
-+	}
-+
-+	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
-+                                    erp_action->fsf_req->sbal_curr, 0);
-+        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-+        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-+
-+	erp_action->fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id;
-+	atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status);
-+	erp_action->fsf_req->data.open_port.port = erp_action->port;
-+	erp_action->fsf_req->erp_action = erp_action;
-+
-+	/* start QDIO request for this FSF request */
-+	retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
-+	if (retval) {
-+                ZFCP_LOG_INFO(
-+			"error: Could not send an "
-+                        "open port request for "
-+                        "the port with WWPN 0x%016Lx connected to "
-+                        "the adapter with devno 0x%04x.\n",
-+                        (llui_t)erp_action->port->wwpn,
-+                        erp_action->adapter->devno);
-+		if (zfcp_fsf_req_free(erp_action->fsf_req)) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: Could not remove one FSF "
-+				"request. Memory leakage possible. "
-+                                "(debug info 0x%lx).\n",
-+                                (unsigned long)erp_action->fsf_req);
-+                        retval=-EINVAL;
-+                };
-+		erp_action->fsf_req = NULL;
-+		goto out;
-+	}
-+
-+	ZFCP_LOG_DEBUG(
-+		"Open Port request initiated "
-+		"(adapter devno=0x%04x, port WWPN=0x%016Lx)\n",
-+		erp_action->adapter->devno,
-+		(llui_t)erp_action->port->wwpn);
-+	
-+out:
-+        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
-+
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_open_port_handler
-+ *
-+ * purpose:	is called for finished Open Port command
-+ *
-+ * returns:	
-+ */
-+static int zfcp_fsf_open_port_handler(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = -EINVAL;
-+	zfcp_port_t *port;
-+	fsf_plogi_t *plogi;
-+	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
-+	u16 subtable, rule, counter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+     
-+	port = fsf_req->data.open_port.port;
-+
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		/* don't change port status in our bookkeeping */
-+		goto skip_fsfstatus;
-+	}
-+     
-+	/* evaluate FSF status in QTCB */
-+	switch (fsf_req->qtcb->header.fsf_status) {
-+          
-+        case FSF_PORT_ALREADY_OPEN :
-+                ZFCP_LOG_FLAGS(0, "FSF_PORT_ALREADY_OPEN\n");
-+                ZFCP_LOG_NORMAL("bug: The remote port with WWPN=0x%016Lx "
-+                                "connected to the adapter with "
-+                                "devno=0x%04x is already open.\n",
-+                                (llui_t)port->wwpn,
-+                                port->adapter->devno);
-+                debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_s_popen");
-+                /* This is a bug, however operation should continue normally
-+                 * if it is simply ignored */
-+                break;
-+                
-+	case FSF_ACCESS_DENIED :
-+		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
-+		ZFCP_LOG_NORMAL("Access denied, cannot open port "
-+				"(devno=0x%04x wwpn=0x%016Lx)\n",
-+				port->adapter->devno,
-+				(llui_t)port->wwpn);
-+		for (counter = 0; counter < 2; counter++) {
-+			subtable = header->fsf_status_qual.halfword[counter * 2];
-+			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-+			switch (subtable) {
-+			case FSF_SQ_CFDC_SUBTABLE_OS:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-+			case FSF_SQ_CFDC_SUBTABLE_LUN:
-+				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-+					zfcp_act_subtable_type[subtable], rule);
-+				break;
-+			}
-+		}
-+		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
-+		zfcp_erp_port_failed(port);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+        case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED :
-+                ZFCP_LOG_FLAGS(1, "FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED\n");
-+                ZFCP_LOG_INFO("error: The FSF adapter is out of resources. "
-+                              "The remote port with WWPN=0x%016Lx "
-+                              "connected to the adapter with "
-+                              "devno=0x%04x could not be opened. "
-+                              "Disabling it.\n",
-+                              (llui_t)port->wwpn,
-+                              port->adapter->devno);
-+                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_max_ports");
-+                zfcp_erp_port_failed(port);
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+
-+        case FSF_ADAPTER_STATUS_AVAILABLE :
-+                ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
-+                switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
-+                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
-+                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
-+                        /* ERP strategy will escalate */
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;
-+                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
-+                        /* ERP strategy will escalate */
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;  
-+                case FSF_SQ_NO_RETRY_POSSIBLE :
-+                        ZFCP_LOG_FLAGS(0, "FSF_SQ_NO_RETRY_POSSIBLE\n");
-+                        ZFCP_LOG_NORMAL("The remote port with WWPN=0x%016Lx "
-+                                        "connected to the adapter with "
-+                                        "devno=0x%04x could not be opened. "
-+                                        "Disabling it.\n",
-+                                        (llui_t)port->wwpn,
-+                                        port->adapter->devno);
-+                        debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_sq_no_retry");
-+                        zfcp_erp_port_failed(port);
-+                        zfcp_cmd_dbf_event_fsf("sqnretry", fsf_req,
-+                                               &fsf_req->qtcb->header.fsf_status_qual,
-+                                               sizeof(fsf_status_qual_t));
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                        break;
-+                default:
-+                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
-+                                        fsf_req->qtcb->header.fsf_status_qual.word[0]);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
-+                        debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                        &fsf_req->qtcb->header.fsf_status_qual.word[0],
-+                                        sizeof(u32));
-+                        break;
-+                }
-+                break;
-+
-+        case FSF_GOOD :
-+                ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
-+                /* save port handle assigned by FSF */
-+                port->handle = fsf_req->qtcb->header.port_handle;
-+                ZFCP_LOG_INFO("The remote port (WWPN=0x%016Lx) via adapter "
-+                              "(devno=0x%04x) was opened, it's "
-+                              "port handle is 0x%x\n",
-+                              (llui_t)port->wwpn,
-+                              port->adapter->devno,
-+                              port->handle);
-+                /* mark port as open */
-+                atomic_set_mask(
-+			ZFCP_STATUS_COMMON_OPEN |
-+			ZFCP_STATUS_PORT_PHYS_OPEN,
-+			&port->status);
-+                retval = 0;
-+		/* check whether D_ID has changed during open */
-+		/*
-+		 * FIXME: This check is not airtight, as the FCP channel does
-+		 * not monitor closures of target port connections caused on
-+		 * the remote side. Thus, they might miss out on invalidating
-+		 * locally cached WWPNs (and other N_Port parameters) of gone
-+		 * target ports. So, our heroic attempt to make things safe
-+		 * could be undermined by 'open port' response data tagged with
-+		 * obsolete WWPNs. Another reason to monitor potential
-+		 * connection closures ourself at least (by interpreting
-+		 * incoming ELS' and unsolicited status). It just crosses my
-+		 * mind that one should be able to cross-check by means of
-+		 * another GID_PN straight after a port has been opened.
-+		 * Alternately, an ADISC/PDISC ELS should suffice, as well.
-+		 */
-+		plogi = (fsf_plogi_t*) fsf_req->qtcb->bottom.support.els;
-+		if (!atomic_test_mask(ZFCP_STATUS_PORT_NO_WWPN, &port->status)) {
-+			if (fsf_req->qtcb->bottom.support.els1_length <
-+			    ((((unsigned long)&plogi->serv_param.wwpn) -
-+			      ((unsigned long)plogi)) +
-+			     sizeof(fsf_wwn_t))) {
-+				ZFCP_LOG_INFO(
-+					"warning: insufficient length of PLOGI payload (%i)\n",
-+					fsf_req->qtcb->bottom.support.els1_length);
-+				debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_short_plogi:");
-+				/* skip sanity check and assume wwpn is ok */
-+			} else	{
-+				if (plogi->serv_param.wwpn != port->wwpn) {
-+					ZFCP_LOG_INFO(
-+						"warning: D_ID of port with WWPN 0x%016Lx changed "
-+						"during open\n",
-+						(llui_t)port->wwpn);
-+					debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_did_change:");
-+					atomic_clear_mask(
-+						ZFCP_STATUS_PORT_DID_DID,
-+						&port->status);
-+				}
-+			}
-+		}
-+                break;
-+                
-+	default :
-+		ZFCP_LOG_NORMAL(
-+			"bug: An unknown FSF Status was presented "
-+			"(debug info 0x%x)\n",
-+			fsf_req->qtcb->header.fsf_status);
-+		debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
-+		debug_exception(fsf_req->adapter->erp_dbf,0,
-+				&fsf_req->qtcb->header.fsf_status,
-+				sizeof(u32));
-+		break;
-+	}
-+
-+skip_fsfstatus:
-+
-+	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &port->status);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_close_port
-+ *
-+ * purpose:     submit FSF command "close port"
-+ *
-+ * returns:     address of initiated FSF request
-+ *              NULL - request could not be initiated
-+ */
-+static int zfcp_fsf_close_port(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	volatile qdio_buffer_element_t *sbale;
-+        int retval = 0;
-+	unsigned long lock_flags;
-+
-+        ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
-+
-+        /* setup new FSF request */
-+        retval = zfcp_fsf_req_create(
-+			erp_action->adapter,
-+			FSF_QTCB_CLOSE_PORT,
-+			ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-+                        &erp_action->adapter->pool.fsf_req_erp,
-+			&lock_flags,
-+			&erp_action->fsf_req);
-+        if (retval < 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. Could not create a "
-+			"close port request for WWPN 0x%016Lx connected to "
-+			"the adapter with devno 0x%04x.\n",
-+			(llui_t)erp_action->port->wwpn,
-+			erp_action->adapter->devno);
-+		goto out;
-+        }
-+
-+	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
-+                                    erp_action->fsf_req->sbal_curr, 0);
-+        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-+        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-+
-+        atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status);
-+        erp_action->fsf_req->data.close_port.port = erp_action->port;
-+	erp_action->fsf_req->erp_action = erp_action;
-+        erp_action->fsf_req->qtcb->header.port_handle = erp_action->port->handle;
-+
-+        /* start QDIO request for this FSF request */
-+        retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
-+        if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not send a "
-+			"close port request for WWPN 0x%016Lx connected to "
-+			"the adapter with devno 0x%04x.\n",
-+			(llui_t)erp_action->port->wwpn,
-+			erp_action->adapter->devno);
-+		if (zfcp_fsf_req_free(erp_action->fsf_req)) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: Could not remove one FSF "
-+				"request. Memory leakage possible. "
-+				"(debug info 0x%lx).\n",
-+				(unsigned long)erp_action->fsf_req);
-+                        retval=-EINVAL;
-+		};
-+		erp_action->fsf_req = NULL;
-+		goto out;
-+        }
-+
-+        ZFCP_LOG_TRACE(
-+                "Close Port request initiated "
-+                "(adapter devno=0x%04x, port WWPN=0x%016Lx)\n",
-+                erp_action->adapter->devno,
-+                (llui_t)erp_action->port->wwpn);
-+
-+out:
-+        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
-+
-+        ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_close_port_handler
-+ *
-+ * purpose:     is called for finished Close Port FSF command
-+ *
-+ * returns:
-+ */
-+static int zfcp_fsf_close_port_handler(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+        int retval = -EINVAL;
-+        zfcp_port_t *port;
-+
-+        ZFCP_LOG_TRACE(
-+                "enter (fsf_req=0x%lx)\n",
-+                (unsigned long)fsf_req);
-+
-+        port = fsf_req->data.close_port.port; 
-+
-+        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+                /* don't change port status in our bookkeeping */
-+                goto skip_fsfstatus;
-+        }
-+
-+        /* evaluate FSF status in QTCB */
-+        switch (fsf_req->qtcb->header.fsf_status) {
-+
-+		case FSF_PORT_HANDLE_NOT_VALID :
-+			ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
-+			ZFCP_LOG_INFO(
-+				"Temporary port identifier (handle) 0x%x "
-+				"for the port with WWPN 0x%016Lx connected to "
-+				"the adapter of devno 0x%04x is "
-+				"not valid. This may happen occasionally.\n",
-+				port->handle,
-+				(llui_t)port->wwpn,
-+				port->adapter->devno);
-+			ZFCP_LOG_DEBUG("status qualifier:\n");
-+			ZFCP_HEX_DUMP(
-+				ZFCP_LOG_LEVEL_DEBUG,
-+				(char*)&fsf_req->qtcb->header.fsf_status_qual,
-+				16);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_phand_nv");
-+			zfcp_erp_adapter_reopen(port->adapter, 0);
-+			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+			break;
-+
-+		case FSF_ADAPTER_STATUS_AVAILABLE :
-+			ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
-+                        /* Note: FSF has actually closed the port in this case.
-+                         * The status code is just daft. Fingers crossed for a change
-+                         */
-+                        retval=0;
-+                        break;
-+#if 0
-+			switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
-+                        case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
-+                                ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
-+                                /* This will now be escalated by ERP */
-+                                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
-+                                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                                break;
-+                        case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
-+                                ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
-+                                /* ERP strategy will escalate */
-+                                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
-+                                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                                break;
-+                        default:
-+                                ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
-+						fsf_req->qtcb->header.fsf_status_qual.word[0]);
-+                                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
-+                                debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                                &fsf_req->qtcb->header.fsf_status_qual.word[0],
-+                                                sizeof(u32));
-+                                break;
-+			}
-+			break;
-+#endif
-+
-+		case FSF_GOOD :
-+			ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
-+			ZFCP_LOG_TRACE(
-+				"remote port (WWPN=0x%016Lx) via adapter "
-+				"(devno=0x%04x) closed, "
-+				"port handle 0x%x\n",
-+				(llui_t)port->wwpn,
-+				port->adapter->devno,
-+				port->handle);
-+			zfcp_erp_modify_port_status(
-+				port,
-+				ZFCP_STATUS_COMMON_OPEN,
-+				ZFCP_CLEAR);
-+			retval = 0;
-+			break;
-+
-+		default :
-+			ZFCP_LOG_NORMAL(
-+				"bug: An unknown FSF Status was presented "
-+				"(debug info 0x%x)\n",
-+				fsf_req->qtcb->header.fsf_status);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
-+                        debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                        &fsf_req->qtcb->header.fsf_status,
-+                                        sizeof(u32));
-+                        break;
-+	}
-+
-+skip_fsfstatus:
-+        atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &port->status);
-+
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_close_physical_port
-+ *
-+ * purpose:     submit FSF command "close physical port"
-+ *
-+ * returns:     address of initiated FSF request
-+ *              NULL - request could not be initiated
-+ */
-+static int zfcp_fsf_close_physical_port(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	volatile qdio_buffer_element_t *sbale;
-+        int retval = 0;
-+	unsigned long lock_flags;
-+
-+        ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
-+
-+        /* setup new FSF request */
-+        retval = zfcp_fsf_req_create(
-+			erp_action->adapter,
-+			FSF_QTCB_CLOSE_PHYSICAL_PORT,
-+			ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-+                        &erp_action->adapter->pool.fsf_req_erp,
-+			&lock_flags,
-+			&erp_action->fsf_req);
-+        if (retval < 0) {
-+                ZFCP_LOG_INFO(
-+			"error: Out of resources. Could not create a "
-+                        "close physical port request for "
-+                        "the port with WWPN 0x%016Lx connected to "
-+                        "the adapter with devno 0x%04x.\n",
-+                        (llui_t)erp_action->port->wwpn,
-+                        erp_action->adapter->devno);
-+                goto out;
-+        }
-+
-+	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
-+                                    erp_action->fsf_req->sbal_curr, 0);
-+        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-+        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-+
-+        /* mark port as being closed */
-+        atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, &erp_action->port->status);
-+        /* save a pointer to this port */
-+        erp_action->fsf_req->data.close_physical_port.port = erp_action->port;
-+        /* port to be closeed */
-+        erp_action->fsf_req->qtcb->header.port_handle = erp_action->port->handle;
-+	erp_action->fsf_req->erp_action = erp_action;
-+
-+        /* start QDIO request for this FSF request */
-+        retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
-+        if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not send an "
-+			"close physical port request for "
-+			"the port with WWPN 0x%016Lx connected to "
-+			"the adapter with devno 0x%04x.\n",
-+			(llui_t)erp_action->port->wwpn,
-+			erp_action->adapter->devno);
-+		if (zfcp_fsf_req_free(erp_action->fsf_req)){
-+			ZFCP_LOG_NORMAL(
-+				"bug: Could not remove one FSF "
-+				"request. Memory leakage possible. "
-+				"(debug info 0x%lx).\n",
-+				(unsigned long)erp_action->fsf_req);
-+                        retval=-EINVAL;
-+		};
-+		erp_action->fsf_req = NULL;
-+		goto out;
-+        }
-+
-+        ZFCP_LOG_TRACE(
-+                "Close Physical Port request initiated "
-+                "(adapter devno=0x%04x, port WWPN=0x%016Lx)\n",
-+                erp_action->adapter->devno,
-+                (llui_t)erp_action->port->wwpn);
-+
-+out:
-+        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
-+
-+        ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_close_physical_port_handler
-+ *
-+ * purpose:     is called for finished Close Physical Port FSF command
-+ *
-+ * returns:
-+ */
-+static int zfcp_fsf_close_physical_port_handler(zfcp_fsf_req_t *fsf_req){
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
-+
-+        int retval = -EINVAL;
-+        zfcp_port_t *port;
-+        zfcp_unit_t *unit;
-+        unsigned long flags;
-+	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
-+	u16 subtable, rule, counter;
-+
-+        ZFCP_LOG_TRACE(
-+                "enter (fsf_req=0x%lx)\n",
-+                (unsigned long)fsf_req);
-+
-+        port = fsf_req->data.close_physical_port.port;
-+
-+        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+                /* don't change port status in our bookkeeping */
-+                goto skip_fsfstatus;
-+        }
-+
-+        /* evaluate FSF status in QTCB */
-+        switch (fsf_req->qtcb->header.fsf_status) {
-+
-+		case FSF_PORT_HANDLE_NOT_VALID :
-+			ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
-+			ZFCP_LOG_INFO(
-+				"Temporary port identifier (handle) 0x%x "
-+				"for the port with WWPN 0x%016Lx connected to "
-+				"the adapter of devno 0x%04x is "
-+				"not valid. This may happen occasionally.\n",
-+				port->handle,
-+				(llui_t)port->wwpn,
-+				port->adapter->devno);
-+			ZFCP_LOG_DEBUG("status qualifier:\n");
-+			ZFCP_HEX_DUMP(
-+				ZFCP_LOG_LEVEL_DEBUG,
-+				(char*)&fsf_req->qtcb->header.fsf_status_qual,
-+				16);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_phand_nv");
-+                        zfcp_erp_adapter_reopen(port->adapter, 0);
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                        //                        panic("for ralph");
-+			break;
-+
-+	case FSF_ACCESS_DENIED :
-+		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
-+		ZFCP_LOG_NORMAL("Access denied, cannot close physical port "
-+				"(devno=0x%04x wwpn=0x%016Lx)\n",
-+				port->adapter->devno,
-+				(llui_t)port->wwpn);
-+		for (counter = 0; counter < 2; counter++) {
-+			subtable = header->fsf_status_qual.halfword[counter * 2];
-+			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-+			switch (subtable) {
-+			case FSF_SQ_CFDC_SUBTABLE_OS:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-+			case FSF_SQ_CFDC_SUBTABLE_LUN:
-+				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-+					zfcp_act_subtable_type[subtable], rule);
-+				break;
-+			}
-+		}
-+		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+        case FSF_PORT_BOXED :
-+                ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
-+                ZFCP_LOG_DEBUG("The remote port "
-+                               "with WWPN 0x%016Lx on the adapter with "
-+                               "devno 0x%04x needs to be reopened but "
-+                               "it was attempted to close it physically.\n",
-+                               (llui_t)port->wwpn,
-+                               port->adapter->devno);
-+                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_pboxed");
-+                zfcp_erp_port_reopen(port, 0);
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-+                        | ZFCP_STATUS_FSFREQ_RETRY;
-+                break;
-+
-+
-+		case FSF_ADAPTER_STATUS_AVAILABLE :
-+			ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
-+			switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
-+                        case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
-+                                ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
-+                                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
-+                                /* This will now be escalated by ERP */
-+                                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                                break;
-+                        case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
-+                                ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
-+                                /* ERP strategy will escalate */
-+                                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
-+                                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                                break;
-+                        default:
-+                                ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
-+						fsf_req->qtcb->header.fsf_status_qual.word[0]);
-+                                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
-+                                debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                                &fsf_req->qtcb->header.fsf_status_qual.word[0],
-+                                                sizeof(u32));
-+                                break;
-+			}
-+			break;
-+	
-+		case FSF_GOOD :
-+			ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
-+			ZFCP_LOG_DEBUG(
-+				"Remote port (WWPN=0x%016Lx) via adapter "
-+				"(devno=0x%04x) physically closed, "
-+				"port handle 0x%x\n",
-+				(llui_t)port->wwpn,
-+				port->adapter->devno,
-+				port->handle);
-+                        /* can't use generic zfcp_erp_modify_port_status because
-+                         * ZFCP_STATUS_COMMON_OPEN must not be reset for the port
-+                         */
-+			atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, 
-+                                          &port->status);
-+                        read_lock_irqsave(&port->unit_list_lock, flags);
-+                        ZFCP_FOR_EACH_UNIT(port, unit) {
-+                                atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, 
-+                                                  &unit->status);
-+                        }
-+                        read_unlock_irqrestore(&port->unit_list_lock, flags);
-+			retval = 0;
-+			break;
-+
-+		default :
-+			ZFCP_LOG_NORMAL(
-+				"bug: An unknown FSF Status was presented "
-+				"(debug info 0x%x)\n",
-+				fsf_req->qtcb->header.fsf_status);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
-+                        debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                        &fsf_req->qtcb->header.fsf_status,
-+                                        sizeof(u32));
-+                        break;
-+        }
-+
-+skip_fsfstatus:
-+        atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, &port->status);
-+        
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_open_unit
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ *
-+ * assumptions:	This routine does not check whether the associated
-+ *		remote port has already been opened. This should be
-+ *		done by calling routines. Otherwise some status
-+ *		may be presented by FSF
-+ */
-+static int zfcp_fsf_open_unit(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	volatile qdio_buffer_element_t *sbale;
-+	int retval = 0;
-+	unsigned long lock_flags;
-+
-+	ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
-+
-+	/* setup new FSF request */
-+	retval = zfcp_fsf_req_create(
-+			erp_action->adapter,
-+			FSF_QTCB_OPEN_LUN,
-+			ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-+                        &erp_action->adapter->pool.fsf_req_erp,
-+			&lock_flags,
-+			&erp_action->fsf_req);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. Could not create an "
-+			"open unit request for FCP_LUN 0x%016Lx connected to "
-+			"the port with WWPN 0x%016Lx connected to "
-+			"the adapter with devno 0x%04x.\n",
-+			(llui_t)erp_action->unit->fcp_lun,
-+			(llui_t)erp_action->unit->port->wwpn,
-+			erp_action->adapter->devno);
-+		goto out;
-+	}
-+
-+	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
-+                                    erp_action->fsf_req->sbal_curr, 0);
-+        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-+        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-+
-+	erp_action->fsf_req->qtcb->header.port_handle =
-+                erp_action->port->handle;
-+	erp_action->fsf_req->qtcb->bottom.support.fcp_lun =
-+                erp_action->unit->fcp_lun;
-+	atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status);
-+	erp_action->fsf_req->data.open_unit.unit = erp_action->unit;
-+	erp_action->fsf_req->erp_action = erp_action;
-+
-+	/* start QDIO request for this FSF request */
-+	retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
-+	if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not send an open unit request "
-+			"on the adapter with devno 0x%04x, "
-+			"port WWPN 0x%016Lx for unit FCP_LUN 0x%016Lx\n",
-+			erp_action->adapter->devno,
-+			(llui_t)erp_action->port->wwpn,
-+			(llui_t)erp_action->unit->fcp_lun);
-+		if (zfcp_fsf_req_free(erp_action->fsf_req)) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: Could not remove one FSF "
-+				"request. Memory leakage possible. "
-+				"(debug info 0x%lx).\n",
-+				(unsigned long)erp_action->fsf_req);
-+                        retval=-EINVAL;
-+		};
-+		erp_action->fsf_req = NULL;
-+		goto out;
-+	}
-+
-+	ZFCP_LOG_TRACE(
-+		"Open LUN request initiated "
-+		"(adapter devno=0x%04x, port WWPN=0x%016Lx, unit FCP_LUN=0x%016Lx)\n",
-+		erp_action->adapter->devno,
-+		(llui_t)erp_action->port->wwpn,
-+		(llui_t)erp_action->unit->fcp_lun);
-+
-+out:
-+        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
-+
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+
-+/*
-+ * function:    zfcp_fsf_open_unit_handler
-+ *
-+ * purpose:	is called for finished Open LUN command
-+ *
-+ * returns:	
-+ */
-+static int zfcp_fsf_open_unit_handler(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = -EINVAL;
-+	zfcp_adapter_t *adapter;
-+	zfcp_unit_t *unit;
-+	fsf_qtcb_header_t *header;
-+	fsf_queue_designator_t *queue_designator;
-+	u16 subtable, rule, counter;
-+ 
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+	adapter = fsf_req->adapter;
-+	unit = fsf_req->data.open_unit.unit;
-+	header = &fsf_req->qtcb->header;
-+	queue_designator = &header->fsf_status_qual.fsf_queue_designator;
-+
-+        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		/* don't change unit status in our bookkeeping */ 
-+                goto skip_fsfstatus;
-+        }
-+
-+	/* evaluate FSF status in QTCB */
-+	switch (fsf_req->qtcb->header.fsf_status) {
-+
-+        case FSF_PORT_HANDLE_NOT_VALID :
-+                ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
-+                ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
-+                              "for the port with WWPN 0x%016Lx connected to "
-+                              "the adapter of devno 0x%04x is "
-+                              "not valid. This may happen occasionally.\n",
-+                              unit->port->handle,
-+                              (llui_t)unit->port->wwpn,
-+                              unit->port->adapter->devno);
-+                ZFCP_LOG_DEBUG("status qualifier:\n");
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-+                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
-+                              16);
-+                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_ph_nv");
-+                zfcp_erp_adapter_reopen(unit->port->adapter, 0);
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+                
-+        case FSF_LUN_ALREADY_OPEN :
-+                ZFCP_LOG_FLAGS(0, "FSF_LUN_ALREADY_OPEN\n");
-+                ZFCP_LOG_NORMAL("bug: Attempted to open the logical unit "
-+				"with FCP_LUN 0x%016Lx at "
-+				"the remote port with WWPN 0x%016Lx connected "
-+				"to the adapter with devno 0x%04x twice.\n", 
-+				(llui_t)unit->fcp_lun,
-+				(llui_t)unit->port->wwpn,
-+				unit->port->adapter->devno);
-+                debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_s_uopen");
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+
-+	case FSF_ACCESS_DENIED :
-+		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
-+		ZFCP_LOG_NORMAL("Access denied, cannot open unit 0x%016Lx "
-+				"on the remote port 0x%016Lx "
-+				"on adapter with devno 0x%04x\n",
-+				(llui_t)unit->fcp_lun,
-+				(llui_t)unit->port->wwpn,
-+				adapter->devno);
-+		for (counter = 0; counter < 2; counter++) {
-+			subtable = header->fsf_status_qual.halfword[counter * 2];
-+			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-+			switch (subtable) {
-+			case FSF_SQ_CFDC_SUBTABLE_OS:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-+			case FSF_SQ_CFDC_SUBTABLE_LUN:
-+				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-+					zfcp_act_subtable_type[subtable], rule);
-+				break;
-+			}
-+		}
-+		debug_text_event(adapter->erp_dbf, 1, "fsf_s_access");
-+		zfcp_erp_unit_failed(unit);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+        case FSF_PORT_BOXED :
-+                ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
-+                ZFCP_LOG_DEBUG("The remote port "
-+                               "with WWPN 0x%016Lx on the adapter with "
-+                               "devno 0x%04x needs to be reopened\n",
-+                               (llui_t)unit->port->wwpn,
-+                               unit->port->adapter->devno);
-+                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_s_pboxed");
-+                zfcp_erp_port_reopen(unit->port, 0);
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-+                        | ZFCP_STATUS_FSFREQ_RETRY;
-+                break;
-+                        
-+	case FSF_LUN_SHARING_VIOLATION :
-+		ZFCP_LOG_FLAGS(2, "FSF_LUN_SHARING_VIOLATION\n");
-+		if (header->fsf_status_qual.word[0] != 0) {
-+			ZFCP_LOG_NORMAL("FCP-LUN 0x%Lx at the remote port with "
-+					"WWPN 0x%Lx connected to the adapter "
-+					"with devno 0x%04x is already in use "
-+					"in LPAR%d, CSS%d\n",
-+					(llui_t)unit->fcp_lun,
-+					(llui_t)unit->port->wwpn,
-+					adapter->devno,
-+					queue_designator->hla,
-+					queue_designator->cssid);
-+		} else {
-+			subtable = header->fsf_status_qual.halfword[4];
-+			rule = header->fsf_status_qual.halfword[5];
-+			switch (subtable) {
-+			case FSF_SQ_CFDC_SUBTABLE_OS:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-+			case FSF_SQ_CFDC_SUBTABLE_LUN:
-+				ZFCP_LOG_NORMAL("Access to FCP-LUN 0x%Lx at the "
-+						"remote port with WWPN 0x%Lx "
-+						"connected to the adapter "
-+						"with devno 0x%04x "
-+						"is denied (%s rule %d)\n",
-+						(llui_t)unit->fcp_lun,
-+						(llui_t)unit->port->wwpn,
-+						adapter->devno,
-+						zfcp_act_subtable_type[subtable],
-+						rule);
-+				break;
-+			}
-+		}
-+		ZFCP_LOG_DEBUG("Additional sense data is presented:\n");
-+		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-+			(char*)&header->fsf_status_qual,
-+			sizeof(fsf_status_qual_t));
-+		debug_text_event(adapter->erp_dbf,2,"fsf_s_l_sh_vio");
-+		zfcp_erp_unit_failed(unit);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+        case FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED :
-+                ZFCP_LOG_FLAGS(1, "FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED\n");
-+                ZFCP_LOG_INFO("error: The adapter ran out of resources. "
-+                              "There is no handle (temporary port identifier) "
-+                              "available for the unit with "
-+                              "FCP_LUN 0x%016Lx at the remote port with WWPN 0x%016Lx "
-+                              "connected to the adapter with devno 0x%04x\n",
-+                              (llui_t)unit->fcp_lun,
-+                              (llui_t)unit->port->wwpn,
-+                              unit->port->adapter->devno);
-+                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_max_units");
-+                zfcp_erp_unit_failed(unit);
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+                
-+        case FSF_ADAPTER_STATUS_AVAILABLE :
-+                ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
-+                switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
-+                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
-+                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
-+                        /* Re-establish link to port */
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
-+                        zfcp_erp_port_reopen(unit->port, 0);
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;
-+                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
-+                        ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
-+                        /* ERP strategy will escalate */
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;
-+                default:
-+                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
-+					fsf_req->qtcb->header.fsf_status_qual.word[0]);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
-+                        debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                        &fsf_req->qtcb->header.fsf_status_qual.word[0],
-+                                        sizeof(u32));
-+                }
-+                break;
-+
-+        case FSF_GOOD :
-+                ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
-+                /* save LUN handle assigned by FSF */
-+                unit->handle = fsf_req->qtcb->header.lun_handle;
-+			ZFCP_LOG_TRACE("unit (FCP_LUN=0x%016Lx) of remote port "
-+                                       "(WWPN=0x%016Lx) via adapter (devno=0x%04x) opened, "
-+                                       "port handle 0x%x \n",
-+                                       (llui_t)unit->fcp_lun,
-+                                       (llui_t)unit->port->wwpn,
-+                                       unit->port->adapter->devno,
-+                                       unit->handle);
-+			/* mark unit as open */
-+			atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
-+			retval = 0;
-+			break;
-+                        
-+        default :
-+                ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-+				"(debug info 0x%x)\n",
-+				fsf_req->qtcb->header.fsf_status);
-+                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
-+                debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                &fsf_req->qtcb->header.fsf_status,
-+                                sizeof(u32));
-+                break;
-+	}
-+        
-+skip_fsfstatus:
-+	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &unit->status); 
-+        
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+	return retval;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_close_unit
-+ *
-+ * purpose:
-+ *
-+ * returns:	address of fsf_req - request successfully initiated
-+ *		NULL - 
-+ *
-+ * assumptions: This routine does not check whether the associated
-+ *              remote port/lun has already been opened. This should be
-+ *              done by calling routines. Otherwise some status
-+ *              may be presented by FSF
-+ */
-+static int zfcp_fsf_close_unit(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	volatile qdio_buffer_element_t *sbale;
-+        int retval = 0;
-+	unsigned long lock_flags;
-+
-+        ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
-+
-+        /* setup new FSF request */
-+        retval = zfcp_fsf_req_create(
-+			erp_action->adapter,
-+			FSF_QTCB_CLOSE_LUN,
-+			ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
-+                        &erp_action->adapter->pool.fsf_req_erp,
-+			&lock_flags,
-+			&erp_action->fsf_req);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. Could not create a "
-+			"close unit request for FCP_LUN 0x%016Lx connected to "
-+			"the port with WWPN 0x%016Lx connected to "
-+			"the adapter with devno 0x%04x.\n",
-+			(llui_t)erp_action->unit->fcp_lun,
-+			(llui_t)erp_action->port->wwpn,
-+			erp_action->adapter->devno);
-+		goto out;
-+        }
-+
-+	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
-+                                    erp_action->fsf_req->sbal_curr, 0);
-+        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
-+        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-+
-+        erp_action->fsf_req->qtcb->header.port_handle = erp_action->port->handle;
-+	erp_action->fsf_req->qtcb->header.lun_handle = erp_action->unit->handle;
-+        atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->unit->status);
-+        erp_action->fsf_req->data.close_unit.unit = erp_action->unit;
-+	erp_action->fsf_req->erp_action = erp_action;
-+
-+        /* start QDIO request for this FSF request */
-+        retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
-+	if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not send a "
-+			"close unit request for FCP_LUN 0x%016Lx connected to "
-+			"the port with WWPN 0x%016Lx connected to "
-+			"the adapter with devno 0x%04x.\n",
-+			(llui_t)erp_action->unit->fcp_lun,
-+			(llui_t)erp_action->port->wwpn,
-+			erp_action->adapter->devno);
-+		if (zfcp_fsf_req_free(erp_action->fsf_req)){
-+			ZFCP_LOG_NORMAL(
-+				"bug: Could not remove one FSF "
-+				"request. Memory leakage possible. "
-+				"(debug info 0x%lx).\n",
-+				(unsigned long)erp_action->fsf_req);
-+                        retval = -EINVAL;
-+		};
-+		erp_action->fsf_req = NULL;
-+		goto out;
-+	}
-+
-+        ZFCP_LOG_TRACE(
-+                "Close LUN request initiated "
-+                "(adapter devno=0x%04x, port WWPN=0x%016Lx, unit FCP_LUN=0x%016Lx)\n",
-+                erp_action->adapter->devno,
-+                (llui_t)erp_action->port->wwpn,
-+                (llui_t)erp_action->unit->fcp_lun);
-+
-+out:
-+        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
-+
-+        ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_close_unit_handler
-+ *
-+ * purpose:     is called for finished Close LUN FSF command
-+ *
-+ * returns:
-+ */
-+static int zfcp_fsf_close_unit_handler(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+        int retval = -EINVAL;
-+        zfcp_unit_t *unit;
-+
-+        ZFCP_LOG_TRACE(
-+                "enter (fsf_req=0x%lx)\n",
-+                (unsigned long)fsf_req);
-+
-+        unit = fsf_req->data.close_unit.unit; /* restore unit */
-+
-+        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+                /* don't change unit status in our bookkeeping */
-+                goto skip_fsfstatus;
-+        }
-+
-+        /* evaluate FSF status in QTCB */
-+        switch (fsf_req->qtcb->header.fsf_status) {
-+
-+                case FSF_PORT_HANDLE_NOT_VALID :
-+			ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
-+			ZFCP_LOG_INFO(
-+				"Temporary port identifier (handle) 0x%x "
-+				"for the port with WWPN 0x%016Lx connected to "
-+                                "the adapter of devno 0x%04x is "
-+				"not valid. This may happen in rare "
-+                                "circumstances\n",
-+				unit->port->handle,
-+				(llui_t)unit->port->wwpn,
-+                                unit->port->adapter->devno);
-+			ZFCP_LOG_DEBUG("status qualifier:\n");
-+                        ZFCP_HEX_DUMP(
-+				ZFCP_LOG_LEVEL_DEBUG,
-+				(char*)&fsf_req->qtcb->header.fsf_status_qual,
-+				16);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_phand_nv");
-+                        zfcp_erp_adapter_reopen(unit->port->adapter, 0);
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                        break;
-+
-+                case FSF_LUN_HANDLE_NOT_VALID :
-+			ZFCP_LOG_FLAGS(1, "FSF_LUN_HANDLE_NOT_VALID\n");
-+			ZFCP_LOG_INFO(
-+                                "Temporary LUN identifier (handle) 0x%x "
-+				"of the logical unit with FCP_LUN 0x%016Lx at "
-+				"the remote port with WWPN 0x%016Lx connected "
-+                                "to the adapter with devno 0x%04x is " 
-+				"not valid. This may happen occasionally.\n",
-+				unit->handle,
-+				(llui_t)unit->fcp_lun,
-+				(llui_t)unit->port->wwpn,
-+				unit->port->adapter->devno);
-+			ZFCP_LOG_DEBUG("Status qualifier data:\n");
-+                        ZFCP_HEX_DUMP(
-+				ZFCP_LOG_LEVEL_DEBUG,
-+				(char*)&fsf_req->qtcb->header.fsf_status_qual,
-+				16);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_lhand_nv");
-+                        zfcp_erp_port_reopen(unit->port, 0);
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                        break;
-+
-+        case FSF_PORT_BOXED :
-+                ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
-+                ZFCP_LOG_DEBUG("The remote port "
-+                               "with WWPN 0x%016Lx on the adapter with "
-+                               "devno 0x%04x needs to be reopened\n",
-+                               (llui_t)unit->port->wwpn,
-+                               unit->port->adapter->devno);
-+                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_s_pboxed");
-+                zfcp_erp_port_reopen(unit->port, 0);
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-+                        | ZFCP_STATUS_FSFREQ_RETRY;
-+                break;
-+
-+        case FSF_ADAPTER_STATUS_AVAILABLE :
-+                ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
-+                switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
-+                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
-+                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
-+                        /* re-establish link to port */
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
-+                        zfcp_erp_port_reopen(unit->port, 0);
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;
-+                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
-+                        ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
-+                        /* ERP strategy will escalate */
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;
-+                default:
-+                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
-+                                        fsf_req->qtcb->header.fsf_status_qual.word[0]);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
-+                        debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                        &fsf_req->qtcb->header.fsf_status_qual.word[0],
-+                                        sizeof(u32));
-+                        break;
-+                }
-+                break;
-+                
-+        case FSF_GOOD :
-+                ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
-+                ZFCP_LOG_TRACE("unit (FCP_LUN=0x%016Lx) of remote port "
-+                               "(WWPN=0x%016Lx) via adapter (devno=0x%04x) closed, "
-+                               "port handle 0x%x \n",
-+                                (llui_t)unit->fcp_lun,
-+                               (llui_t)unit->port->wwpn,
-+                                unit->port->adapter->devno,
-+                               unit->handle);
-+                /* mark unit as closed */
-+                atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
-+                retval = 0;
-+                break;
-+                
-+        default :
-+                ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
-+                                "(debug info 0x%x)\n",
-+                                fsf_req->qtcb->header.fsf_status);
-+                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
-+                debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                &fsf_req->qtcb->header.fsf_status,
-+                                sizeof(u32));
-+                break;
-+        }
-+
-+skip_fsfstatus:
-+
-+        atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &unit->status);
-+
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_control_file
-+ *
-+ * purpose:     Initiator of the control file upload/download FSF requests
-+ *
-+ * returns:     0           - FSF request is successfuly created and queued
-+ *              -EOPNOTSUPP - The FCP adapter does not have Control File support
-+ *              -EINVAL     - Invalid direction specified
-+ *              -ENOMEM     - Insufficient memory
-+ *              -EPERM      - Cannot create FSF request or or place it in QDIO queue
-+ */
-+static int zfcp_fsf_control_file(
-+	zfcp_adapter_t *adapter,
-+	zfcp_fsf_req_t **fsf_req_ptr,
-+	u32 fsf_command,
-+	u32 option,
-+	zfcp_sg_list_t *sg_list)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	zfcp_fsf_req_t *fsf_req;
-+	fsf_qtcb_bottom_support_t *bottom;
-+	unsigned long lock_flags;
-+	int req_flags = 0;
-+	int direction;
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx fsf_req_ptr=0x%lx "
-+		"fsf_command=0x%x option=0x%x sg_list=0x%lx)\n",
-+		(unsigned long)adapter,
-+		(unsigned long)fsf_req_ptr,
-+		fsf_command,
-+		option,
-+		(unsigned long)sg_list);
-+
-+	if (!(adapter->supported_features & FSF_FEATURE_CFDC)) {
-+		ZFCP_LOG_INFO(
-+			"Adapter does not support control file "
-+			"(devno=0x%04x)\n",
-+			adapter->devno);
-+		retval = -EOPNOTSUPP;
-+		goto no_act_support;
-+	}
-+
-+	switch (fsf_command) {
-+
-+	case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
-+		direction = SBAL_FLAGS0_TYPE_WRITE;
-+		if ((option != FSF_CFDC_OPTION_FULL_ACCESS) &&
-+		    (option != FSF_CFDC_OPTION_RESTRICTED_ACCESS))
-+			req_flags |= ZFCP_WAIT_FOR_SBAL;
-+		break;
-+
-+	case FSF_QTCB_UPLOAD_CONTROL_FILE:
-+		direction = SBAL_FLAGS0_TYPE_READ;
-+		break;
-+
-+	default:
-+		ZFCP_LOG_INFO("Invalid FSF command code 0x%08x\n", fsf_command);
-+		goto invalid_command;
-+	}
-+
-+	retval = zfcp_fsf_req_create(
-+		adapter, fsf_command, req_flags, NULL, &lock_flags, &fsf_req);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not create FSF request (devno=0x%04x)\n",
-+			adapter->devno);
-+		retval = -EPERM;
-+		goto out;
-+	}
-+
-+	bottom = &fsf_req->qtcb->bottom.support;
-+	bottom->op_subtype = FSF_CFDC_OPERATION_SUBTYPE;
-+	bottom->option = option;
-+
-+	if (sg_list->count > 0) {
-+		int bytes = zfcp_qdio_sbals_from_sg(
-+			fsf_req, direction, sg_list->sg,
-+			ZFCP_MAX_SBALES_PER_CONTROL_FILE,
-+			ZFCP_MAX_SBALS_PER_REQ);
-+		
-+		if (bytes != ZFCP_CFDC_MAX_CONTROL_FILE_SIZE) {
-+			ZFCP_LOG_INFO(
-+				"error: Could not create sufficient number "
-+				"of SBALS (devno=0x%04x)\n",
-+				adapter->devno);
-+			retval = -ENOMEM;
-+			goto sbals_failed;
-+		}
-+	} else {
-+		volatile qdio_buffer_element_t *sbale =
-+			zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-+		sbale[0].flags |= direction;
-+		sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
-+	}
-+
-+	retval = zfcp_fsf_req_send(fsf_req, NULL);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not send FSF request (devno=0x%04x)\n",
-+			adapter->devno);
-+		retval = -EPERM;
-+		goto queue_failed;
-+	}
-+
-+	ZFCP_LOG_NORMAL(
-+		"Control file %s FSF request initiated (devno=0x%04x)\n",
-+		fsf_command == FSF_QTCB_DOWNLOAD_CONTROL_FILE ?
-+			"download" : "upload",
-+		adapter->devno);
-+
-+	*fsf_req_ptr = fsf_req;
-+
-+	goto out;
-+
-+sbals_failed:
-+queue_failed:
-+	if (zfcp_fsf_req_free(fsf_req)) {
-+		ZFCP_LOG_INFO(
-+			"bug: Could not remove the FSF request "
-+			"(devno=0x%04x fsf_req=0x%lx)\n",
-+			adapter->devno,
-+			(unsigned long)fsf_req);
-+	}
-+
-+out:
-+	write_unlock_irqrestore(
-+		&adapter->request_queue.queue_lock, lock_flags);
-+
-+no_act_support:
-+invalid_command:
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_control_file_handler
-+ *
-+ * purpose:     Handler of the control file upload/download FSF requests
-+ *
-+ * returns:     0       - FSF request successfuly processed
-+ *              -EAGAIN - Operation has to be repeated because of a temporary problem
-+ *              -EACCES - There is no permission to execute an operation
-+ *              -EPERM  - The control file is not in a right format
-+ *              -EIO    - There is a problem with the FCP adapter
-+ *              -EINVAL - Invalid operation
-+ *              -EFAULT - User space memory I/O operation fault
-+ */
-+static int zfcp_fsf_control_file_handler(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	zfcp_adapter_t *adapter = fsf_req->adapter;
-+	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
-+	fsf_qtcb_bottom_support_t *bottom = &fsf_req->qtcb->bottom.support;
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter (fsf_req=0x%lx)\n", (unsigned long)fsf_req);
-+
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		retval = -EINVAL;
-+		goto skip_fsfstatus;
-+	}
-+
-+	switch (header->fsf_status) {
-+
-+	case FSF_GOOD:
-+		ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
-+		ZFCP_LOG_NORMAL(
-+			"The FSF request has been successfully completed "
-+			"(devno=0x%04x fsf_req.seq_no=%d)\n",
-+			adapter->devno,
-+			fsf_req->seq_no);
-+		break;
-+
-+	case FSF_OPERATION_PARTIALLY_SUCCESSFUL:
-+		ZFCP_LOG_FLAGS(2, "FSF_OPERATION_PARTIALLY_SUCCESSFUL\n");
-+		if (bottom->op_subtype == FSF_CFDC_OPERATION_SUBTYPE) {
-+			switch (header->fsf_status_qual.word[0]) {
-+
-+			case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE:
-+				ZFCP_LOG_NORMAL(
-+					"CDFC could not be saved "
-+					"on the SE (devno=0x%04x)\n",
-+					adapter->devno);
-+				break;
-+
-+			case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2:
-+				ZFCP_LOG_NORMAL(
-+					"CDFC could not be copied "
-+					"to the secondary SE (devno=0x%04x)\n",
-+					adapter->devno);
-+				break;
-+
-+			default:
-+				ZFCP_LOG_NORMAL(
-+					"CDFC could not be hardened "
-+					"on the FCP adapter (devno=0x%04x)\n",
-+					adapter->devno);
-+			}
-+		}
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EAGAIN;
-+		break;
-+
-+	case FSF_AUTHORIZATION_FAILURE:
-+		ZFCP_LOG_FLAGS(2, "FSF_AUTHORIZATION_FAILURE\n");
-+		ZFCP_LOG_NORMAL(
-+			"Subchannel does not accept privileged commands "
-+			"(devno=0x%04x)\n",
-+			adapter->devno);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EACCES;
-+		break;
-+
-+	case FSF_CFDC_ERROR_DETECTED:
-+		ZFCP_LOG_FLAGS(2, "FSF_CFDC_ERROR_DETECTED\n");
-+		ZFCP_LOG_NORMAL(
-+			"Error at position %d in the CDFC, "
-+			"CDFC is discarded by the FCP adapter (devno=0x%04x)\n",
-+			header->fsf_status_qual.word[0],
-+			adapter->devno);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EPERM;
-+		break;
-+
-+	case FSF_CONTROL_FILE_UPDATE_ERROR:
-+		ZFCP_LOG_FLAGS(2, "FSF_CONTROL_FILE_UPDATE_ERROR\n");
-+		ZFCP_LOG_NORMAL(
-+			"FCP adapter cannot harden the control file, "
-+			"file is discarded (devno=0x%04x)\n",
-+			adapter->devno);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EIO;
-+		break;
-+
-+	case FSF_CONTROL_FILE_TOO_LARGE:
-+		ZFCP_LOG_FLAGS(2, "FSF_CONTROL_FILE_TOO_LARGE\n");
-+		ZFCP_LOG_NORMAL(
-+			"Control file is too large, file is discarded "
-+			"by the FCP adapter (devno=0x%04x)\n",
-+			adapter->devno);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EIO;
-+		break;
-+
-+	case FSF_ACCESS_CONFLICT_DETECTED:
-+		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_CONFLICT_DETECTED\n");
-+		if (bottom->op_subtype == FSF_CFDC_OPERATION_SUBTYPE)
-+			ZFCP_LOG_NORMAL(
-+				"CDFC has been discarded, because activation "
-+				"would impact %d active connection(s) "
-+				"(devno=0x%04x)\n",
-+				header->fsf_status_qual.word[0],
-+				adapter->devno);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EIO;
-+		break;
-+
-+	case FSF_CONFLICTS_OVERRULED:
-+		ZFCP_LOG_FLAGS(2, "FSF_CONFLICTS_OVERRULED\n");
-+		if (bottom->op_subtype == FSF_CFDC_OPERATION_SUBTYPE)
-+			ZFCP_LOG_NORMAL(
-+				"CDFC has been activated, but activation "
-+				"has impacted %d active connection(s) "
-+				"(devno=0x%04x)\n",
-+				header->fsf_status_qual.word[0],
-+				adapter->devno);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EIO;
-+		break;
-+
-+	case FSF_UNKNOWN_COMMAND:
-+		ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_COMMAND\n");
-+		ZFCP_LOG_NORMAL(
-+			"FSF command 0x%x is not supported by FCP adapter "
-+			"(devno=0x%04x)\n",
-+			fsf_req->fsf_command,
-+			adapter->devno);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EINVAL;
-+		break;
-+
-+	case FSF_UNKNOWN_OP_SUBTYPE:
-+		ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_OP_SUBTYPE\n");
-+		ZFCP_LOG_NORMAL(
-+			"Invalid operation subtype 0x%x has been specified "
-+			"in QTCB bottom (devno=0x%04x)\n",
-+			bottom->op_subtype,
-+			adapter->devno);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EINVAL;
-+		break;
-+
-+	case FSF_INVALID_COMMAND_OPTION:
-+		ZFCP_LOG_FLAGS(2, "FSF_INVALID_COMMAND_OPTION\n");
-+		ZFCP_LOG_NORMAL(
-+			"Invalid option 0x%x has been specified in QTCB bottom "
-+			"(devno=0x%04x)\n",
-+			bottom->option,
-+			adapter->devno);
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EINVAL;
-+		break;
-+
-+	default:
-+		ZFCP_LOG_NORMAL(
-+			"bug: An unknown FSF Status was presented "
-+			"(devno=0x%04x fsf_status=0x%08x)\n",
-+			adapter->devno,
-+			header->fsf_status);
-+		debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval");
-+		debug_exception(fsf_req->adapter->erp_dbf, 0,
-+			&header->fsf_status_qual.word[0], sizeof(u32));
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		retval = -EINVAL;
-+		break;
-+	}
-+
-+skip_fsfstatus:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+#ifdef ZFCP_RESID
-+/*
-+ * function:    zfcp_scsi_truncte_command
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ */
-+inline int zfcp_scsi_truncate_command(unsigned char *command_struct,
-+                                      unsigned long original_byte_length,
-+                                      unsigned long new_byte_length)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+        int retval=0;
-+        unsigned long factor, new_block_size;
-+        u8 *len_6;
-+        u16 *len_10;
-+        u32 *len_12;
-+        /* trace */
-+        ZFCP_LOG_NORMAL(
-+                 "enter command_struct = 0x%lx, "
-+                 "original_byte_length = %ld "
-+                 "new_byte_length = %ld\n",
-+                 (unsigned long)command_struct,
-+                 original_byte_length,
-+                 new_byte_length);
-+        
-+        /*trace*/
-+        ZFCP_LOG_NORMAL("original SCSI command:\n");
-+        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
-+                      command_struct,
-+                      12);
-+
-+        switch(command_struct[0]) {
-+        case WRITE_6:
-+        case READ_6:
-+                len_6 = &command_struct[4];
-+                factor = (unsigned long)(original_byte_length
-+                                       / *len_6);
-+                new_block_size = new_byte_length / factor;
-+                if(new_byte_length % factor) {
-+                        ZFCP_LOG_NORMAL("bug: Recalculation of command size "
-+                                        "failed. "
-+                                        "(debug info %d, %ld, %d, %ld, %ld)\n",
-+                                        6,
-+                                        original_byte_length,
-+                                        *len_6,
-+                                        new_byte_length,
-+                                        factor);
-+                        goto error;
-+                }
-+                /* trace */
-+                ZFCP_LOG_NORMAL("*len_6=%d, factor= %ld, new_byte_length= %ld\n",
-+                               *len_6, factor, new_byte_length);
-+                *len_6=(u8)new_block_size;
-+                /* trace */
-+                ZFCP_LOG_NORMAL("new *len_6=%d\n",
-+                                *len_6);
-+                break;
-+        case WRITE_10:
-+        case READ_10:
-+        case WRITE_VERIFY:
-+                len_10= (u16 *)&command_struct[7];
-+                factor = (unsigned long)(original_byte_length
-+                                       / *len_10);
-+                new_block_size = new_byte_length / factor;
-+                if(new_byte_length % factor) {
-+                        ZFCP_LOG_NORMAL("bug: Recalculation of command size "
-+                                        "failed. "
-+                                        "(debug info %d, %ld, %d, %ld, %ld)\n",
-+                                        10,
-+                                        original_byte_length,
-+                                        *len_10,
-+                                        new_byte_length,
-+                                        factor);
-+                        goto error;
-+                }
-+                /* TRACE */
-+                ZFCP_LOG_NORMAL("*len_10 = %d, factor = %ld, new_byte_length = %ld\n",
-+                               *len_10, factor, new_byte_length);
-+                *len_10=(u16)new_block_size;
-+                /* trace */
-+                ZFCP_LOG_NORMAL("new *len_10=%d\n",
-+                                *len_10);
-+                break;
-+        case WRITE_12:
-+        case READ_12:
-+        case WRITE_VERIFY_12:
-+                len_12= (u32 *)&command_struct[7];
-+                factor = (unsigned long)(original_byte_length
-+                                       / *len_12);
-+                new_block_size = new_byte_length / factor;
-+                if(new_byte_length % factor) {
-+                        ZFCP_LOG_NORMAL("bug: Recalculation of command size "
-+                                        "failed. "
-+                                        "(debug info %d, %ld, %d, %ld, %ld)\n",
-+                                        12,
-+                                        original_byte_length,
-+                                        *len_12,
-+                                        new_byte_length,
-+                                        factor);
-+                        goto error;
-+                }
-+                /* TRACE */
-+                ZFCP_LOG_NORMAL("*len_12 = %d, factor = %ld, new_byte_length = %ld\n",
-+                               *len_12, factor, new_byte_length);
-+                *len_12=(u32)new_block_size;
-+                /* trace */
-+                ZFCP_LOG_NORMAL("new *len_12=%d\n",
-+                                *len_12);
-+                break;
-+        default:
-+                /* INFO */
-+                ZFCP_LOG_NORMAL("Command to be truncated is not in the list of "
-+                              "known objects.\n");
-+                goto error;
-+                break;
-+        }
-+                goto out;
-+                
-+        error:
-+                retval=1;
-+        out:                
-+        /*trace*/
-+        ZFCP_LOG_NORMAL("truncated SCSI command:\n");
-+        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
-+                      command_struct,
-+                      12);
-+
-+        /* TRACE */
-+        ZFCP_LOG_NORMAL("exit (%i)\n", retval);
-+        
-+        return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+#endif // ZFCP_RESID
-+
-+/*
-+ * function:    zfcp_fsf_send_fcp_command_task
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ *
-+ * note: we do not employ linked commands (not supported by HBA anyway)
-+ */
-+static int
-+	zfcp_fsf_send_fcp_command_task(
-+		zfcp_unit_t *unit,
-+                Scsi_Cmnd *scsi_cmnd)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+  
-+	zfcp_fsf_req_t *fsf_req = NULL;
-+	fcp_cmnd_iu_t *fcp_cmnd_iu;
-+	zfcp_adapter_t *adapter = unit->port->adapter;
-+	unsigned int sbtype;
-+	unsigned long lock_flags;
-+	int real_bytes = 0;
-+        int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter devno=0x%04x, unit=0x%lx)\n",
-+		adapter->devno,
-+		(unsigned long)unit);
-+
-+	/* setup new FSF request */
-+	retval = zfcp_fsf_req_create(
-+			adapter,
-+			FSF_QTCB_FCP_CMND,
-+			ZFCP_REQ_AUTO_CLEANUP,
-+                        &adapter->pool.fsf_req_scsi,
-+			&lock_flags,
-+			&fsf_req);
-+	if (retval < 0) {
-+		ZFCP_LOG_DEBUG(
-+			"error: Out of resources. Could not create an "
-+			"FCP command request for FCP_LUN 0x%016Lx connected to "
-+			"the port with WWPN 0x%016Lx connected to "
-+			"the adapter with devno 0x%04x.\n",
-+			(llui_t)unit->fcp_lun,
-+			(llui_t)unit->port->wwpn,
-+			adapter->devno);
-+		goto failed_req_create;
-+	}
-+
-+	/*
-+	 * associate FSF request with SCSI request
-+	 * (need this for look up on abort)
-+	 */
-+	scsi_cmnd->host_scribble = (char*) fsf_req;
-+
-+	/*
-+	 * associate SCSI command with FSF request
-+	 * (need this for look up on normal command completion)
-+	 */
-+	fsf_req->data.send_fcp_command_task.scsi_cmnd = scsi_cmnd;
-+#ifdef ZFCP_DEBUG_REQUESTS
-+	debug_text_event(adapter->req_dbf, 3, "fsf/sc");
-+	debug_event(adapter->req_dbf, 3, &fsf_req, sizeof(unsigned long));
-+	debug_event(adapter->req_dbf, 3, &scsi_cmnd, sizeof(unsigned long));
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+#ifdef ZFCP_DEBUG_ABORTS
-+	fsf_req->data.send_fcp_command_task.start_jiffies = jiffies;
-+#endif
-+
-+	fsf_req->data.send_fcp_command_task.unit = unit;
-+        ZFCP_LOG_DEBUG("unit=0x%lx, unit_fcp_lun=0x%Lx\n",
-+                       (unsigned long)unit,
-+                       (llui_t)unit->fcp_lun);
-+
-+	/* set handles of unit and its parent port in QTCB */
-+	fsf_req->qtcb->header.lun_handle = unit->handle;
-+	fsf_req->qtcb->header.port_handle = unit->port->handle;
-+
-+	/* FSF does not define the structure of the FCP_CMND IU */
-+	fcp_cmnd_iu = (fcp_cmnd_iu_t*)
-+			&(fsf_req->qtcb->bottom.io.fcp_cmnd);
-+
-+	/*
-+	 * set depending on data direction:
-+	 *	data direction bits in SBALE (SB Type)
-+	 * 	data direction bits in QTCB
-+	 *	data direction bits in FCP_CMND IU
-+         */
-+	switch (scsi_cmnd->sc_data_direction) {
-+		case SCSI_DATA_NONE:
-+                        ZFCP_LOG_FLAGS(3, "SCSI_DATA_NONE\n");
-+			fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
-+			/*
-+			 * FIXME(qdio):
-+			 * what is the correct type for commands
-+			 * without 'real' data buffers?
-+			 */
-+			sbtype = SBAL_FLAGS0_TYPE_READ;
-+			break;
-+		case SCSI_DATA_READ:
-+                        ZFCP_LOG_FLAGS(3, "SCSI_DATA_READ\n");
-+			fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
-+			sbtype = SBAL_FLAGS0_TYPE_READ;
-+			fcp_cmnd_iu->rddata = 1;
-+			break;
-+		case SCSI_DATA_WRITE:
-+                        ZFCP_LOG_FLAGS(3, "SCSI_DATA_WRITE\n");
-+			fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
-+			sbtype = SBAL_FLAGS0_TYPE_WRITE;
-+			fcp_cmnd_iu->wddata = 1;
-+			break;
-+		case SCSI_DATA_UNKNOWN:
-+			ZFCP_LOG_FLAGS(0, "SCSI_DATA_UNKNOWN not supported\n");
-+		default:
-+			/*
-+			 * dummy, catch this condition earlier
-+			 * in zfcp_scsi_queuecommand
-+			 */
-+			goto failed_scsi_cmnd;
-+	}
-+
-+	/* set FC service class in QTCB (3 per default) */
-+	fsf_req->qtcb->bottom.io.service_class = adapter->fc_service_class;
-+
-+	/* set FCP_LUN in FCP_CMND IU in QTCB */
-+	fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
-+
-+	/* set task attributes in FCP_CMND IU in QTCB */
-+	if ((scsi_cmnd->device && scsi_cmnd->device->tagged_queue) ||
-+	    atomic_test_mask(ZFCP_STATUS_UNIT_ASSUMETCQ, &unit->status)) {
-+		fcp_cmnd_iu->task_attribute = SIMPLE_Q;
-+		ZFCP_LOG_TRACE("setting SIMPLE_Q task attribute\n");
-+	} else	{
-+		fcp_cmnd_iu->task_attribute = UNTAGGED;
-+		ZFCP_LOG_TRACE("setting UNTAGGED task attribute\n");
-+	}
-+
-+	/* set additional length of FCP_CDB in FCP_CMND IU in QTCB, if needed */
-+	if (scsi_cmnd->cmd_len > FCP_CDB_LENGTH) {
-+		fcp_cmnd_iu->add_fcp_cdb_length
-+			= (scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2;
-+		ZFCP_LOG_TRACE("SCSI CDB length is 0x%x, "
-+				"additional FCP_CDB length is 0x%x "
-+				"(shifted right 2 bits)\n",
-+				scsi_cmnd->cmd_len,
-+				fcp_cmnd_iu->add_fcp_cdb_length);
-+	}
-+	/*
-+	 * copy SCSI CDB (including additional length, if any) to
-+	 * FCP_CDB in FCP_CMND IU in QTCB
-+	 */ 
-+	memcpy( fcp_cmnd_iu->fcp_cdb,
-+		scsi_cmnd->cmnd,
-+		scsi_cmnd->cmd_len);
-+
-+	/* FCP CMND IU length in QTCB */
-+	fsf_req->qtcb->bottom.io.fcp_cmnd_length 
-+		= sizeof(fcp_cmnd_iu_t) +
-+		  fcp_cmnd_iu->add_fcp_cdb_length +
-+		  sizeof(fcp_dl_t);
-+
-+	/* generate SBALEs from data buffer */
-+	real_bytes = zfcp_qdio_sbals_from_scsicmnd(
-+			fsf_req,
-+			sbtype,
-+			scsi_cmnd);
-+	if (real_bytes < 0) {
-+		if (fsf_req->sbal_number < ZFCP_MAX_SBALS_PER_REQ) {
-+			ZFCP_LOG_DEBUG(
-+				"Data did not fit into available buffer(s), "
-+				"waiting for more...\n");
-+			retval = -EIO;
-+        	} else	{
-+                	ZFCP_LOG_NORMAL(
-+				"error: Too large SCSI data buffer. "
-+				"Shutting down unit "
-+				"(devno=0x%04x, WWPN=0x%016Lx, FCP_LUN=0x%016Lx)\n",
-+				unit->port->adapter->devno,
-+				(llui_t)unit->port->wwpn,
-+				(llui_t)unit->fcp_lun);
-+			zfcp_erp_unit_shutdown(unit, 0);
-+			retval = -EINVAL;
-+		}
-+		goto no_fit;
-+        }
-+
-+        /* set length of FCP data length in FCP_CMND IU in QTCB */
-+	zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes);
-+
-+        ZFCP_LOG_DEBUG("Sending SCSI command:\n");
-+        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-+                      (char *)scsi_cmnd->cmnd,
-+                      scsi_cmnd->cmd_len);
-+
-+	/*
-+	 * start QDIO request for this FSF request
-+	 *  covered by an SBALE)
-+	 */
-+	{
-+		int i, pos;
-+		ZFCP_LOG_DEBUG(
-+			"opcode=0x%x, sbal_first=%d, "
-+			"sbal_curr=%d, sbal_last=%d, "
-+			"sbal_number=%d, sbale_curr=%d\n",
-+			scsi_cmnd->cmnd[0],
-+			fsf_req->sbal_first,
-+			fsf_req->sbal_curr,
-+			fsf_req->sbal_last,
-+			fsf_req->sbal_number,
-+			fsf_req->sbale_curr);
-+		for (i = 0; i < fsf_req->sbal_number; i++) {
-+			pos = (fsf_req->sbal_first + i) % QDIO_MAX_BUFFERS_PER_Q;
-+			ZFCP_HEX_DUMP(
-+				ZFCP_LOG_LEVEL_DEBUG,
-+				(char*)adapter->request_queue.buffer[pos],
-+				sizeof(qdio_buffer_t));
-+		}
-+	}
-+        retval = zfcp_fsf_req_send(fsf_req, NULL);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not send an FCP command request "
-+			"for a command on the adapter with devno 0x%04x, "
-+                        "port WWPN 0x%016Lx and unit FCP_LUN 0x%016Lx\n",
-+			adapter->devno,
-+			(llui_t)unit->port->wwpn,
-+			(llui_t)unit->fcp_lun);
-+                goto send_failed;
-+	}
-+
-+        ZFCP_LOG_TRACE(
-+		"Send FCP Command initiated "
-+		"(adapter devno=0x%04x, port WWPN=0x%016Lx, unit FCP_LUN=0x%016Lx)\n",
-+		adapter->devno,
-+		(llui_t)unit->port->wwpn,
-+		(llui_t)unit->fcp_lun);
-+        goto success;
-+
-+send_failed:
-+no_fit:
-+failed_scsi_cmnd:
-+	/* dequeue new FSF request previously enqueued */
-+#ifdef ZFCP_DEBUG_REQUESTS
-+	debug_text_event(adapter->req_dbf, 3, "fail_sc");
-+	debug_event(adapter->req_dbf, 3, &scsi_cmnd, sizeof(unsigned long));
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+         
-+        if (zfcp_fsf_req_free(fsf_req)) {
-+                ZFCP_LOG_INFO(
-+			"error: Could not remove an FSF request from "
-+			"the otubound (send) list (debug info 0x%lx)\n",
-+			(unsigned long)fsf_req);
-+        }
-+        fsf_req = NULL;
-+
-+success:
-+failed_req_create:
-+        write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-+
-+	ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_send_fcp_command_task_management
-+ *
-+ * purpose:
-+ *
-+ * returns:
-+ *
-+ * FIXME(design) shouldn't this be modified to return an int
-+ *               also...don't know how though
-+
-+ */
-+static zfcp_fsf_req_t*
-+	zfcp_fsf_send_fcp_command_task_management(
-+		zfcp_adapter_t *adapter,
-+		zfcp_unit_t *unit,
-+                u8 tm_flags,
-+		int req_flags)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+  
-+	zfcp_fsf_req_t *fsf_req = NULL;
-+	int retval = 0;
-+	fcp_cmnd_iu_t *fcp_cmnd_iu;
-+	unsigned long lock_flags;
-+
-+	volatile qdio_buffer_element_t *sbale;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter devno=0x%04x, unit=0x%lx, tm_flags=0x%x, "
-+		"req_flags=0x%x)\n",
-+		adapter->devno,
-+		(unsigned long)unit,
-+		tm_flags,
-+		req_flags);
-+
-+
-+	/* setup new FSF request */
-+	retval = zfcp_fsf_req_create(
-+			adapter,
-+			FSF_QTCB_FCP_CMND,
-+			req_flags,
-+                        &adapter->pool.fsf_req_scsi,
-+			&lock_flags,
-+			&fsf_req);
-+	if (retval < 0) {
-+                ZFCP_LOG_INFO("error: Out of resources. Could not create an "
-+                              "FCP command (task management) request for "
-+                              "the adapter with devno 0x%04x, port with "
-+                              "WWPN 0x%016Lx and FCP_LUN 0x%016Lx.\n",
-+                              adapter->devno,
-+                              (llui_t)unit->port->wwpn,
-+                              (llui_t)unit->fcp_lun);
-+                goto out;
-+	}
-+
-+        /* Used to decide on proper handler in the return path,
-+         * could be either zfcp_fsf_send_fcp_command_task_handler or
-+         * zfcp_fsf_send_fcp_command_task_management_handler */
-+        fsf_req->status|=ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT;
-+	/*
-+	 * hold a pointer to the unit being target of this
-+	 * task management request
-+	 */
-+	fsf_req->data.send_fcp_command_task_management.unit = unit;
-+
-+	/* set FSF related fields in QTCB */
-+	fsf_req->qtcb->header.lun_handle = unit->handle;
-+	fsf_req->qtcb->header.port_handle = unit->port->handle;
-+	fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
-+	fsf_req->qtcb->bottom.io.service_class
-+		= adapter->fc_service_class;
-+	fsf_req->qtcb->bottom.io.fcp_cmnd_length
-+		= sizeof(fcp_cmnd_iu_t) + sizeof(fcp_dl_t);
-+
-+	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-+	sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
-+        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; 
-+
-+	/* set FCP related fields in FCP_CMND IU in QTCB */
-+	fcp_cmnd_iu = (fcp_cmnd_iu_t*)
-+			&(fsf_req->qtcb->bottom.io.fcp_cmnd);
-+	fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
-+	fcp_cmnd_iu->task_management_flags = tm_flags;
-+
-+	/* start QDIO request for this FSF request */
-+        zfcp_fsf_start_scsi_er_timer(adapter);
-+	retval = zfcp_fsf_req_send(fsf_req, NULL);
-+	if (retval) {
-+                del_timer(&adapter->scsi_er_timer);
-+		ZFCP_LOG_INFO(
-+                        "error: Could not send an FCP-command (task management) "
-+                        "on the adapter with devno 0x%04x, "
-+                        "port WWPN 0x%016Lx for unit FCP_LUN 0x%016Lx\n",
-+			adapter->devno,
-+			(llui_t)unit->port->wwpn,
-+			(llui_t)unit->fcp_lun);
-+                if (zfcp_fsf_req_free(fsf_req)){
-+			ZFCP_LOG_NORMAL(
-+				"bug: Could not remove one FSF "
-+				"request. Memory leakage possible. "
-+                                "(debug info 0x%lx).\n",
-+                                (unsigned long)fsf_req);
-+                        retval=-EINVAL;
-+                };
-+		fsf_req = NULL;
-+		goto out;
-+	}
-+
-+	ZFCP_LOG_TRACE(
-+		"Send FCP Command (task management function) initiated "
-+		"(adapter devno=0x%04x, port WWPN=0x%016Lx, unit FCP_LUN=0x%016Lx, "
-+		"tm_flags=0x%x)\n",
-+		adapter->devno,
-+		(llui_t)unit->port->wwpn,
-+		(llui_t)unit->fcp_lun,
-+		tm_flags);
-+
-+out:
-+        write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
-+
-+	ZFCP_LOG_TRACE("exit (0x%lx)\n", (unsigned long)fsf_req);
-+ 
-+	return fsf_req;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_send_fcp_command_handler
-+ *
-+ * purpose:	is called for finished Send FCP Command
-+ *
-+ * returns:	
-+ */
-+static int
-+	zfcp_fsf_send_fcp_command_handler(
-+		zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = -EINVAL;
-+        zfcp_unit_t *unit;
-+	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
-+	u16 subtable, rule, counter;
-+
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) 
-+		unit = fsf_req->data.send_fcp_command_task_management.unit;
-+	else	unit = fsf_req->data.send_fcp_command_task.unit;
-+        
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		/* go directly to calls of special handlers */ 
-+                goto skip_fsfstatus;
-+        }
-+
-+	/* evaluate FSF status in QTCB */
-+	switch (fsf_req->qtcb->header.fsf_status) {
-+
-+        case FSF_PORT_HANDLE_NOT_VALID:
-+                ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
-+                ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
-+				"for the port with WWPN 0x%016Lx connected to "
-+                                "the adapter of devno 0x%04x is not valid.\n",
-+				unit->port->handle,
-+				(llui_t)unit->port->wwpn,
-+                                unit->port->adapter->devno);
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-+                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
-+                              16);
-+                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_phand_nv");
-+                zfcp_erp_adapter_reopen(unit->port->adapter, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"porthinv", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+                
-+        case FSF_LUN_HANDLE_NOT_VALID:
-+                ZFCP_LOG_FLAGS(1, "FSF_LUN_HANDLE_NOT_VALID\n");
-+                ZFCP_LOG_INFO("Temporary LUN identifier (handle) 0x%x "
-+                              "of the logical unit with FCP_LUN 0x%016Lx at "
-+                              "the remote port with WWPN 0x%016Lx connected "
-+                              "to the adapter with devno 0x%04x is " 
-+                              "not valid. This may happen occasionally.\n",
-+                              unit->handle,
-+                              (llui_t)unit->fcp_lun,
-+                              (llui_t)unit->port->wwpn,
-+                              unit->port->adapter->devno);
-+                ZFCP_LOG_NORMAL("Status qualifier data:\n");
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
-+                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
-+                              16);
-+                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_uhand_nv");
-+                zfcp_erp_port_reopen(unit->port, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"lunhinv", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+
-+        case FSF_HANDLE_MISMATCH:
-+                ZFCP_LOG_FLAGS(0, "FSF_HANDLE_MISMATCH\n");
-+                ZFCP_LOG_NORMAL("bug: The port handle (temporary port "
-+                                "identifier) 0x%x has changed unexpectedly. " 
-+				"This was detected upon receiveing the response "
-+                                "of a command send to the unit with FCP_LUN "
-+                                "0x%016Lx at the remote port with WWPN 0x%016Lx "
-+                                "connected to the adapter with devno 0x%04x.\n",
-+				unit->port->handle,
-+				(llui_t)unit->fcp_lun,
-+				(llui_t)unit->port->wwpn,
-+				unit->port->adapter->devno);
-+                ZFCP_LOG_NORMAL("status qualifier:\n");
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
-+                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
-+                              16);
-+                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_hand_mis");
-+                zfcp_erp_adapter_reopen(unit->port->adapter, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"handmism", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+
-+        case FSF_SERVICE_CLASS_NOT_SUPPORTED :
-+                ZFCP_LOG_FLAGS(0, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n");
-+                if(fsf_req->adapter->fc_service_class <= 3) {
-+                        ZFCP_LOG_NORMAL( "error: The adapter with devno=0x%04x does "
-+                                         "not support fibre-channel class %d.\n",
-+                                         unit->port->adapter->devno,
-+                                         fsf_req->adapter->fc_service_class);
-+                } else {
-+                        ZFCP_LOG_NORMAL(
-+                                        "bug: The fibre channel class at the adapter "
-+                                        "with devno 0x%04x is invalid. "
-+                                        "(debug info %d)\n",
-+                                        unit->port->adapter->devno,
-+                                        fsf_req->adapter->fc_service_class);
-+                }
-+                /* stop operation for this adapter */
-+                debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_s_class_nsup");
-+                zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"unsclass", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+
-+        case FSF_FCPLUN_NOT_VALID:
-+                ZFCP_LOG_FLAGS(0, "FSF_FCPLUN_NOT_VALID\n");
-+                ZFCP_LOG_NORMAL("bug: The FCP_LUN 0x%016Lx behind the remote port "
-+				"of WWPN 0x%016Lx via the adapter with "
-+                                "devno 0x%04x does not have the correct unit "
-+                                "handle (temporary unit identifier) 0x%x\n",
-+				(llui_t)unit->fcp_lun,
-+				(llui_t)unit->port->wwpn,
-+				unit->port->adapter->devno,
-+				unit->handle);
-+                ZFCP_LOG_DEBUG("status qualifier:\n");
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-+                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
-+                              16);
-+                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_fcp_lun_nv");
-+                zfcp_erp_port_reopen(unit->port, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"fluninv", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+                
-+
-+	case FSF_ACCESS_DENIED :
-+		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
-+		ZFCP_LOG_NORMAL("Access denied, cannot send FCP command "
-+				"(devno=0x%04x wwpn=0x%016Lx lun=0x%016Lx)\n",
-+				unit->port->adapter->devno,
-+				(llui_t)unit->port->wwpn,
-+				(llui_t)unit->fcp_lun);
-+		for (counter = 0; counter < 2; counter++) {
-+			subtable = header->fsf_status_qual.halfword[counter * 2];
-+			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
-+			switch (subtable) {
-+			case FSF_SQ_CFDC_SUBTABLE_OS:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
-+			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
-+			case FSF_SQ_CFDC_SUBTABLE_LUN:
-+				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
-+					zfcp_act_subtable_type[subtable], rule);
-+				break;
-+			}
-+		}
-+		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+		break;
-+
-+        case FSF_DIRECTION_INDICATOR_NOT_VALID:
-+                ZFCP_LOG_FLAGS(0, "FSF_DIRECTION_INDICATOR_NOT_VALID\n");
-+                ZFCP_LOG_INFO("bug: Invalid data direction given for the unit "
-+                              "with FCP_LUN 0x%016Lx at the remote port with "
-+                              "WWPN 0x%016Lx via the adapter with devno 0x%04x "
-+                              "(debug info %d)\n",
-+                              (llui_t)unit->fcp_lun,
-+                              (llui_t)unit->port->wwpn,
-+                              unit->port->adapter->devno,
-+                              fsf_req->qtcb->bottom.io.data_direction);
-+                /* stop operation for this adapter */
-+                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_dir_ind_nv");
-+                zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"dirinv", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+
-+	/* FIXME: this should be obsolete, isn' it? */
-+        case FSF_INBOUND_DATA_LENGTH_NOT_VALID:
-+                ZFCP_LOG_FLAGS(0, "FSF_INBOUND_DATA_LENGTH_NOT_VALID\n");
-+                ZFCP_LOG_NORMAL("bug: An invalid inbound data length field "
-+				"was found in a command for the unit with "
-+                                "FCP_LUN 0x%016Lx of the remote port "
-+				"with WWPN 0x%016Lx via the adapter with "
-+                                "devno 0x%04x\n",
-+				(llui_t)unit->fcp_lun,
-+				(llui_t)unit->port->wwpn,
-+				unit->port->adapter->devno);
-+                /* stop operation for this adapter */
-+                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_in_dl_nv");
-+                zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"odleninv", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+
-+	/* FIXME: this should be obsolete, isn' it? */
-+        case FSF_OUTBOUND_DATA_LENGTH_NOT_VALID:
-+                ZFCP_LOG_FLAGS(0, "FSF_OUTBOUND_DATA_LENGTH_NOT_VALID\n");
-+                ZFCP_LOG_NORMAL("bug: An invalid outbound data length field "
-+				"was found in a command for the unit with "
-+                                "FCP_LUN 0x%016Lx of the remote port "
-+				"with WWPN 0x%016Lx via the adapter with "
-+                                "devno 0x%04x\n",
-+				(llui_t)unit->fcp_lun,
-+				(llui_t)unit->port->wwpn,
-+				unit->port->adapter->devno);
-+                /* stop operation for this adapter */
-+                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_out_dl_nv");
-+                zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"idleninv", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+
-+        case FSF_CMND_LENGTH_NOT_VALID:
-+                ZFCP_LOG_FLAGS(0, "FSF_CMND_LENGTH_NOT_VALID\n");
-+                ZFCP_LOG_NORMAL("bug: An invalid control-data-block length field "
-+				"was found in a command for the unit with "
-+                                "FCP_LUN 0x%016Lx of the remote port "
-+				"with WWPN 0x%016Lx via the adapter with "
-+                                "devno 0x%04x (debug info %d)\n",
-+				(llui_t)unit->fcp_lun,
-+				(llui_t)unit->port->wwpn,
-+				unit->port->adapter->devno,
-+				fsf_req->qtcb->bottom.io.fcp_cmnd_length);
-+                /* stop operation for this adapter */
-+                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_cmd_len_nv");
-+                zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"cleninv", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                break;
-+                
-+        case FSF_PORT_BOXED :
-+                ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
-+                ZFCP_LOG_DEBUG("The remote port "
-+                               "with WWPN 0x%016Lx on the adapter with "
-+                               "devno 0x%04x needs to be reopened\n",
-+                               (llui_t)unit->port->wwpn,
-+                               unit->port->adapter->devno);
-+                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_s_pboxed");
-+                zfcp_erp_port_reopen(unit->port, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"portbox", fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-+                        | ZFCP_STATUS_FSFREQ_RETRY;
-+                break;
-+                        
-+
-+	case FSF_LUN_BOXED :
-+		ZFCP_LOG_FLAGS(0, "FSF_LUN_BOXED\n");
-+		ZFCP_LOG_NORMAL(
-+			"The remote unit needs to be reopened "
-+			"(devno=0x%04x wwpn=0x%016Lx lun=0x%016Lx)\n",
-+			unit->port->adapter->devno,
-+			(llui_t)unit->port->wwpn,
-+			(llui_t)unit->fcp_lun);
-+		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_lboxed");
-+		zfcp_erp_unit_reopen(unit, 0);
-+		zfcp_cmd_dbf_event_fsf(
-+			"unitbox",
-+			fsf_req,
-+			&fsf_req->qtcb->header.fsf_status_qual,
-+			sizeof(fsf_status_qual_t));
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
-+			| ZFCP_STATUS_FSFREQ_RETRY;
-+		break;
-+
-+        case FSF_ADAPTER_STATUS_AVAILABLE :
-+                ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
-+                switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
-+                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
-+                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
-+                        /* re-establish link to port */
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
-+                        zfcp_erp_port_reopen(unit->port, 0);
-+			zfcp_cmd_dbf_event_fsf(
-+				"sqltest", fsf_req,
-+				&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
-+                        break;
-+                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
-+                        ZFCP_LOG_FLAGS(3, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
-+                        /* FIXME(hw) need proper specs for proper action */
-+                        /* let scsi stack deal with retries and escalation */
-+                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
-+			zfcp_cmd_dbf_event_fsf(
-+				"sqdeperp", fsf_req,
-+				&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
-+                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
-+                        break;
-+                default:
-+			/* FIXME: shall we consider this a successful transfer? */
-+                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
-+                                        fsf_req->qtcb->header.fsf_status_qual.word[0]);
-+                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
-+                        debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                        &fsf_req->qtcb->header.fsf_status_qual.word[0],
-+                                        sizeof(u32));
-+                        break;
-+                }
-+                break;
-+                
-+	case FSF_GOOD:
-+                ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
-+		break;
-+
-+        case FSF_FCP_RSP_AVAILABLE:
-+                ZFCP_LOG_FLAGS(2, "FSF_FCP_RSP_AVAILABLE\n");
-+                break;
-+
-+	default :
-+                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
-+                debug_exception(fsf_req->adapter->erp_dbf,0,
-+                                &fsf_req->qtcb->header.fsf_status,
-+                                sizeof(u32));
-+                break;
-+	}
-+
-+skip_fsfstatus:
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) {
-+		retval = zfcp_fsf_send_fcp_command_task_management_handler(
-+				fsf_req);
-+	} else	{
-+		retval = zfcp_fsf_send_fcp_command_task_handler(
-+				fsf_req);
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_send_fcp_command_task_handler
-+ *
-+ * purpose:	evaluates FCP_RSP IU
-+ *
-+ * returns:	
-+ */
-+static int zfcp_fsf_send_fcp_command_task_handler(
-+		zfcp_fsf_req_t *fsf_req)
-+
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	int retval = 0;
-+
-+	Scsi_Cmnd *scpnt;
-+	fcp_rsp_iu_t *fcp_rsp_iu =
-+		(fcp_rsp_iu_t*)
-+		&(fsf_req->qtcb->bottom.io.fcp_rsp);
-+	fcp_cmnd_iu_t *fcp_cmnd_iu =
-+		(fcp_cmnd_iu_t*)
-+		&(fsf_req->qtcb->bottom.io.fcp_cmnd);
-+	u32 sns_len;
-+        char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
-+	unsigned long flags;
-+        zfcp_unit_t *unit = fsf_req->data.send_fcp_command_task.unit;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+	read_lock_irqsave(&fsf_req->adapter->abort_lock, flags);
-+        scpnt = fsf_req->data.send_fcp_command_task.scsi_cmnd;
-+        if (!scpnt) {
-+                ZFCP_LOG_DEBUG("Command with fsf_req 0x%lx is not associated to "
-+                               "a scsi command anymore. Aborted?\n",
-+                               (unsigned long)fsf_req);
-+                goto out;
-+        }
-+
-+        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTED) {
-+		/* FIXME: (design) mid-layer should handle DID_ABORT like
-+		 *        DID_SOFT_ERROR by retrying the request for devices
-+		 *        that allow retries.
-+		 */
-+                ZFCP_LOG_DEBUG("Setting DID_SOFT_ERROR and SUGGEST_RETRY\n");
-+		scpnt->result |= DID_SOFT_ERROR << 16 |
-+				 SUGGEST_RETRY << 24;
-+		goto skip_fsfstatus;
-+        }
-+
-+        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+                ZFCP_LOG_DEBUG("Setting DID_ERROR\n");
-+		scpnt->result |= DID_ERROR << 16;
-+                goto skip_fsfstatus;
-+        }
-+
-+	/* set message byte of result in SCSI command */
-+	scpnt->result |= COMMAND_COMPLETE << 8;
-+
-+	/*
-+	 * copy SCSI status code of FCP_STATUS of FCP_RSP IU to status byte
-+	 * of result in SCSI command
-+	 */
-+	scpnt->result |= fcp_rsp_iu->scsi_status;
-+        if(fcp_rsp_iu->scsi_status) {
-+                /* DEBUG */
-+                ZFCP_LOG_DEBUG("status for SCSI Command:\n");
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-+                              scpnt->cmnd,
-+                              scpnt->cmd_len);
-+                ZFCP_LOG_DEBUG("SCSI status code 0x%x\n",
-+                               fcp_rsp_iu->scsi_status);
-+                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
-+                              (void *)fcp_rsp_iu,
-+                              sizeof(fcp_rsp_iu_t));
-+                ZFCP_HEX_DUMP(
-+                              ZFCP_LOG_LEVEL_DEBUG,
-+                              zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu),
-+                              fcp_rsp_iu->fcp_sns_len);
-+        }
-+
-+	/* check FCP_RSP_INFO */
-+	if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid) {
-+		ZFCP_LOG_DEBUG("rsp_len is valid\n");
-+		switch (fcp_rsp_info[3]) {
-+			case RSP_CODE_GOOD:
-+                                ZFCP_LOG_FLAGS(3, "RSP_CODE_GOOD\n");
-+				/* ok, continue */
-+				ZFCP_LOG_TRACE(
-+					"no failure or Task Management "
-+					"Function complete\n");
-+				scpnt->result |= DID_OK << 16;
-+				break;
-+			case RSP_CODE_LENGTH_MISMATCH:
-+                                ZFCP_LOG_FLAGS(0, "RSP_CODE_LENGTH_MISMATCH\n");
-+				/* hardware bug */
-+				ZFCP_LOG_NORMAL(
-+					"bug: FCP response code indictates "
-+                                        " that the fibre-channel protocol data "
-+                                        "length differs from the burst "
-+                                        "length. The problem occured on the unit "
-+                                        "with FCP_LUN 0x%016Lx connected to the "
-+                                        "port with WWPN 0x%016Lx at the adapter with "
-+                                        "devno 0x%04x\n",
-+                                        (llui_t)unit->fcp_lun,
-+                                        (llui_t)unit->port->wwpn,
-+                                        unit->port->adapter->devno);
-+				/* dump SCSI CDB as prepared by zfcp */
-+                                ZFCP_HEX_DUMP(
-+					ZFCP_LOG_LEVEL_DEBUG,
-+					(char*)&fsf_req->qtcb->
-+					bottom.io.fcp_cmnd,
-+					FSF_FCP_CMND_SIZE);
-+				zfcp_cmd_dbf_event_fsf("clenmism", fsf_req, NULL, 0);
-+				scpnt->result |= DID_ERROR << 16;
-+                                goto skip_fsfstatus;
-+			case RSP_CODE_FIELD_INVALID:
-+                                ZFCP_LOG_FLAGS(0, "RSP_CODE_FIELD_INVALID\n");
-+				/* driver or hardware bug */
-+				ZFCP_LOG_NORMAL(
-+					"bug: FCP response code indictates "
-+                                        "that the fibre-channel protocol data "
-+                                        "fields were incorrectly set-up. "
-+                                        "The problem occured on the unit "
-+                                        "with FCP_LUN 0x%016Lx connected to the "
-+                                        "port with WWPN 0x%016Lx at the adapter with "
-+                                        "devno 0x%04x\n",
-+                                        (llui_t)unit->fcp_lun,
-+                                        (llui_t)unit->port->wwpn,
-+                                        unit->port->adapter->devno);
-+				/* dump SCSI CDB as prepared by zfcp */
-+                                ZFCP_HEX_DUMP(
-+					ZFCP_LOG_LEVEL_DEBUG,
-+					(char*)&fsf_req->qtcb->
-+					bottom.io.fcp_cmnd,
-+					FSF_FCP_CMND_SIZE);
-+				zfcp_cmd_dbf_event_fsf("codeinv", fsf_req, NULL, 0);
-+				scpnt->result |= DID_ERROR << 16;
-+                                goto skip_fsfstatus;
-+			case RSP_CODE_RO_MISMATCH:
-+                                ZFCP_LOG_FLAGS(0, "RSP_CODE_RO_MISMATCH\n");
-+				/* hardware bug */
-+				ZFCP_LOG_NORMAL(
-+					"bug: The FCP response code indicates "
-+                                        "that conflicting  values for the "
-+                                        "fibre-channel payload offset from the "
-+                                        "header were found. "
-+                                        "The problem occured on the unit "
-+                                        "with FCP_LUN 0x%016Lx connected to the "
-+                                        "port with WWPN 0x%016Lx at the adapter with "
-+                                        "devno 0x%04x\n",
-+                                        (llui_t)unit->fcp_lun,
-+                                        (llui_t)unit->port->wwpn,
-+                                        unit->port->adapter->devno);
-+				/* dump SCSI CDB as prepared by zfcp */
-+                                ZFCP_HEX_DUMP(
-+					ZFCP_LOG_LEVEL_DEBUG,
-+					(char*)&fsf_req->qtcb->
-+					bottom.io.fcp_cmnd,
-+					FSF_FCP_CMND_SIZE);
-+				zfcp_cmd_dbf_event_fsf("codemism", fsf_req, NULL, 0);
-+				scpnt->result |= DID_ERROR << 16;
-+                                goto skip_fsfstatus;
-+			default :
-+				ZFCP_LOG_NORMAL(
-+                                      "bug: An invalid FCP response "
-+                                      "code was detected for a command. "
-+                                      "The problem occured on the unit "
-+                                      "with FCP_LUN 0x%016Lx connected to the "
-+                                      "port with WWPN 0x%016Lx at the adapter with "
-+                                      "devno 0x%04x (debug info 0x%x)\n",
-+                                      (llui_t)unit->fcp_lun,
-+                                      (llui_t)unit->port->wwpn,
-+                                      unit->port->adapter->devno,
-+                                      fcp_rsp_info[3]);
-+				/* dump SCSI CDB as prepared by zfcp */
-+                                ZFCP_HEX_DUMP(
-+					ZFCP_LOG_LEVEL_DEBUG,
-+					(char*)&fsf_req->qtcb->
-+					bottom.io.fcp_cmnd,
-+					FSF_FCP_CMND_SIZE);
-+				zfcp_cmd_dbf_event_fsf("undeffcp", fsf_req, NULL, 0);
-+				scpnt->result |= DID_ERROR << 16;
-+		}
-+	}
-+
-+	/* check for sense data */
-+	if (fcp_rsp_iu->validity.bits.fcp_sns_len_valid) {
-+		sns_len = FSF_FCP_RSP_SIZE -
-+			  sizeof(fcp_rsp_iu_t) +
-+			  fcp_rsp_iu->fcp_rsp_len;
-+		ZFCP_LOG_TRACE(
-+			"room for %i bytes sense data in QTCB\n",
-+			sns_len);
-+		sns_len = min(sns_len, (u32)SCSI_SENSE_BUFFERSIZE);
-+		ZFCP_LOG_TRACE(
-+			"room for %i bytes sense data in SCSI command\n",
-+			SCSI_SENSE_BUFFERSIZE);
-+		sns_len = min(sns_len, fcp_rsp_iu->fcp_sns_len);
-+                ZFCP_LOG_TRACE("scpnt->result =0x%x, command was:\n",
-+                               scpnt->result);
-+                ZFCP_HEX_DUMP(
-+			ZFCP_LOG_LEVEL_TRACE,
-+			(void *)&scpnt->cmnd,
-+                        scpnt->cmd_len);
-+
-+		ZFCP_LOG_TRACE(
-+			"%i bytes sense data provided by FCP\n",
-+			fcp_rsp_iu->fcp_sns_len);
-+		memcpy(	&scpnt->sense_buffer,
-+			zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu),
-+			sns_len);
-+                ZFCP_HEX_DUMP(
-+			ZFCP_LOG_LEVEL_TRACE,
-+			(void *)&scpnt->sense_buffer,
-+                        sns_len);
-+	}
-+
-+	/* check for overrun */
-+	if (fcp_rsp_iu->validity.bits.fcp_resid_over) {
-+		ZFCP_LOG_INFO(
-+                        "A data overrun was detected for a command. "
-+                        "This happened for a command to the unit "
-+                        "with FCP_LUN 0x%016Lx connected to the "
-+                        "port with WWPN 0x%016Lx at the adapter with "
-+                        "devno 0x%04x. The response data length is "
-+                        "%d, the original length was %d.\n",
-+                        (llui_t)unit->fcp_lun,
-+                        (llui_t)unit->port->wwpn,
-+                        unit->port->adapter->devno,
-+			fcp_rsp_iu->fcp_resid,
-+                        zfcp_get_fcp_dl(fcp_cmnd_iu));
-+	}
-+
-+	/* check for underrun */ 
-+	if (fcp_rsp_iu->validity.bits.fcp_resid_under) {
-+		ZFCP_LOG_DEBUG(
-+                        "A data underrun was detected for a command. "
-+                        "This happened for a command to the unit "
-+                        "with FCP_LUN 0x%016Lx connected to the "
-+                        "port with WWPN 0x%016Lx at the adapter with "
-+                        "devno 0x%04x. The response data length is "
-+                        "%d, the original length was %d.\n",
-+                        (llui_t)unit->fcp_lun,
-+                        (llui_t)unit->port->wwpn,
-+                        unit->port->adapter->devno,
-+			fcp_rsp_iu->fcp_resid,
-+                        zfcp_get_fcp_dl(fcp_cmnd_iu));
-+                /*
-+                 * It may not have been possible to send all data and the
-+                 * underrun on send may already be in scpnt->resid, so it's add
-+                 * not equals in the below statement.
-+                 */
-+		scpnt->resid += fcp_rsp_iu->fcp_resid;
-+                ZFCP_LOG_TRACE("scpnt->resid=0x%x\n",
-+                                scpnt->resid);
-+	}
-+
-+skip_fsfstatus:
-+#if 0
-+	/*
-+	 * This nasty chop at the problem is not working anymore
-+	 * as we do not adjust the retry count anylonger in order
-+	 * to have a number of retries that avoids I/O errors.
-+	 * The manipulation of the retry count has been removed
-+	 * in favour of a safe tape device handling. We must not
-+	 * sent SCSI commands more than once to a device if no
-+	 * retries are permitted by the high level driver. Generally
-+	 * speaking, it was a mess to change retry counts. So it is
-+	 * fine that this sort of workaround is gone.
-+	 * Then, we had to face a certain number of immediate retries in case of
-+	 * busy and queue full conditions (see below).
-+	 * This is not acceptable
-+	 * for the latter. Queue full conditions are used
-+	 * by devices to indicate to a host that the host can rely
-+	 * on the completion (or timeout) of at least one outstanding
-+	 * command as a suggested trigger for command retries.
-+	 * Busy conditions require a different trigger since
-+	 * no commands are outstanding for that initiator from the
-+	 * devices perspective.
-+	 * The drawback of mapping a queue full condition to a
-+	 * busy condition is the chance of wasting all retries prior
-+	 * to the time when the device indicates that a command
-+	 * rejected due to a queue full condition should be re-driven.
-+	 * This case would lead to unnecessary I/O errors that
-+	 * have to be considered fatal if for example ext3's
-+	 * journaling would be torpedoed by such an avoidable
-+	 * I/O error.
-+	 * So, what issues are there with not mapping a queue-full
-+	 * condition to a busy condition?
-+	 * Due to the 'exclusive LUN'
-+	 * policy enforced by the zSeries FCP channel, this 
-+	 * Linux instance is the only initiator with regard to
-+	 * this adapter. It is safe to rely on the information
-+	 * 'don't disturb me now ... and btw. no other commands
-+	 * pending for you' (= queue full) sent by the LU,
-+	 * since no other Linux can use this LUN via this adapter
-+	 * at the same time. If there is a potential race
-+	 * introduced by the FCP channel by not inhibiting Linux A
-+	 * to give up a LU with commands pending while Linux B
-+	 * grabs this LU and sends commands  - thus providing
-+	 * an exploit at the 'exclusive LUN' policy - then this
-+	 * issue has to be considered a hardware problem. It should
-+	 * be tracked as such if it really occurs. Even if the
-+	 * FCP Channel spec. begs exploiters to wait for the
-+	 * completion of all request sent to a LU prior to
-+	 * closing this LU connection.
-+	 * This spec. statement in conjunction with
-+	 * the 'exclusive LUN' policy is not consistent design.
-+	 * Another issue is how resource constraints for SCSI commands
-+	 * might be handled by the FCP channel (just guessing for now).
-+	 * If the FCP channel would always map resource constraints,
-+	 * e.g. no free FC exchange ID due to I/O stress caused by
-+	 * other sharing Linux instances, to faked queue-full
-+	 * conditions then this would be a misinterpretation and
-+	 * violation of SCSI standards.
-+	 * If there are SCSI stack races as indicated below
-+	 * then they need to be fixed just there.
-+	 * Providing all issue above are not applicable or will
-+	 * be fixed appropriately, removing the following hack
-+	 * is the right thing to do.
-+	 */
-+
-+	/*
-+	 * Note: This is a rather nasty chop at the problem. We cannot 
-+	 * risk adding to the mlqueue however as this will block the 
-+	 * device. If it is the last outstanding command for this host
-+	 * it will remain blocked indefinitely. This would be quite possible
-+	 * on the zSeries FCP adapter.
-+	 * Also, there exists a race with scsi_insert_special relying on 
-+	 * scsi_request_fn to recalculate some command data which may not 
-+	 * happen when q->plugged is true in scsi_request_fn
-+	 */
-+	if (status_byte(scpnt->result) == QUEUE_FULL) {
-+		ZFCP_LOG_DEBUG("Changing QUEUE_FULL to BUSY....\n");
-+		scpnt->result &= ~(QUEUE_FULL << 1);
-+		scpnt->result |= (BUSY << 1);
-+	}
-+#endif
-+
-+        ZFCP_LOG_DEBUG("scpnt->result =0x%x\n",
-+                       scpnt->result);
-+
-+	zfcp_cmd_dbf_event_scsi("response", fsf_req->adapter, scpnt);
-+
-+	/* cleanup pointer (need this especially for abort) */
-+	scpnt->host_scribble = NULL;
-+	scpnt->SCp.ptr = (char*)0;
-+
-+	/*
-+	 * NOTE:
-+	 * according to the outcome of a discussion on linux-scsi we
-+	 * don't need to grab the io_request_lock here since we use
-+	 * the new eh
-+	 */
-+	/* always call back */
-+#ifdef ZFCP_DEBUG_REQUESTS
-+        debug_text_event(fsf_req->adapter->req_dbf, 2, "ok_done:");
-+        debug_event(fsf_req->adapter->req_dbf, 2, &scpnt, sizeof(unsigned long));
-+        debug_event(fsf_req->adapter->req_dbf, 2, &scpnt->scsi_done, 
-+                      sizeof(unsigned long));
-+        debug_event(fsf_req->adapter->req_dbf, 2, &fsf_req, sizeof(unsigned long));
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+        (scpnt->scsi_done)(scpnt);
-+ 	/*
-+	 * We must hold this lock until scsi_done has been called.
-+	 * Otherwise we may call scsi_done after abort regarding this
-+	 * command has completed.
-+	 * Note: scsi_done must not block!
-+	 */
-+out:
-+	read_unlock_irqrestore(&fsf_req->adapter->abort_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_send_fcp_command_task_management_handler
-+ *
-+ * purpose:	evaluates FCP_RSP IU
-+ *
-+ * returns:	
-+ */
-+static int zfcp_fsf_send_fcp_command_task_management_handler(
-+     zfcp_fsf_req_t *fsf_req)
-+
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = 0;
-+	fcp_rsp_iu_t *fcp_rsp_iu =
-+		(fcp_rsp_iu_t*)
-+		&(fsf_req->qtcb->bottom.io.fcp_rsp);
-+        char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
-+        zfcp_unit_t *unit = fsf_req->data.send_fcp_command_task_management.unit;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx)\n",
-+		(unsigned long)fsf_req);
-+
-+        del_timer(&fsf_req->adapter->scsi_er_timer);
-+	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED; 
-+                goto skip_fsfstatus;
-+        }
-+
-+	/* check FCP_RSP_INFO */
-+	switch (fcp_rsp_info[3]) {
-+		case RSP_CODE_GOOD:
-+                        ZFCP_LOG_FLAGS(3, "RSP_CODE_GOOD\n");
-+			/* ok, continue */
-+			ZFCP_LOG_DEBUG(
-+				"no failure or Task Management "
-+				"Function complete\n");
-+			break;
-+		case RSP_CODE_TASKMAN_UNSUPP:
-+                        ZFCP_LOG_FLAGS(0, "RSP_CODE_TASKMAN_UNSUPP\n");
-+			ZFCP_LOG_NORMAL(
-+				"bug: A reuested task management function "
-+                                "is not supported on the target device "
-+                                "The corresponding device is the unit with "
-+                                "FCP_LUN 0x%016Lx at the port "
-+                                "with WWPN 0x%016Lx at the adapter with devno "
-+                                "0x%04x\n",
-+                                (llui_t)unit->fcp_lun,
-+                                (llui_t)unit->port->wwpn,
-+                                unit->port->adapter->devno);
-+                        fsf_req->status
-+				|= ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP;
-+			break;
-+		case RSP_CODE_TASKMAN_FAILED:
-+                        ZFCP_LOG_FLAGS(0, "RSP_CODE_TASKMAN_FAILED\n");
-+			ZFCP_LOG_NORMAL(
-+				"bug: A reuested task management function "
-+                                "failed to complete successfully. "
-+                                "The corresponding device is the unit with "
-+                                "FCP_LUN 0x%016Lx at the port "
-+                                "with WWPN 0x%016Lx at the adapter with devno "
-+                                "0x%04x\n",
-+                                (llui_t)unit->fcp_lun,
-+                                (llui_t)unit->port->wwpn,
-+                                unit->port->adapter->devno);
-+			fsf_req->status
-+				|= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
-+			break;
-+		default :
-+                        ZFCP_LOG_NORMAL(
-+                               "bug: An invalid FCP response "
-+                               "code was detected for a command. "
-+                               "The problem occured on the unit "
-+                               "with FCP_LUN 0x%016Lx connected to the "
-+                               "port with WWPN 0x%016Lx at the adapter with "
-+                               "devno 0x%04x (debug info 0x%x)\n",
-+                               (llui_t)unit->fcp_lun,
-+                               (llui_t)unit->port->wwpn,
-+                               unit->port->adapter->devno,
-+                               fcp_rsp_info[3]);
-+			fsf_req->status
-+                                |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
-+	}
-+
-+skip_fsfstatus:
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_req_wait_and_cleanup
-+ *
-+ * purpose:
-+ *
-+ * FIXME(design): signal seems to be <0 !!!
-+ * returns:	0	- request completed (*status is valid), cleanup succeeded
-+ *		<0	- request completed (*status is valid), cleanup failed
-+ *		>0	- signal which interrupted waiting (*status is not valid),
-+ *			  request not completed, no cleanup
-+ *
-+ *		*status is a copy of status of completed fsf_req
-+ */
-+static int zfcp_fsf_req_wait_and_cleanup(
-+		zfcp_fsf_req_t *fsf_req,
-+		int interruptible,
-+		u32 *status)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = 0;
-+	int signal = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx,"
-+		"interruptible=%d, *status=0x%x\n",
-+		(unsigned long)fsf_req,
-+		interruptible,
-+		*status);
-+
-+	if (interruptible) {
-+		__wait_event_interruptible(
-+			fsf_req->completion_wq,
-+			fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED,
-+			signal);
-+		if (signal) {
-+			ZFCP_LOG_DEBUG(
-+				"Caught signal %i while waiting for the "
-+                                "completion of the request at 0x%lx\n",
-+				signal,
-+				(unsigned long)fsf_req);
-+			retval = signal;
-+			goto out;
-+		}
-+	} else	{
-+		__wait_event(
-+			fsf_req->completion_wq,
-+			fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
-+	}
-+
-+	*status = fsf_req->status;
-+
-+	/* cleanup request */
-+	retval = zfcp_fsf_req_cleanup(fsf_req);
-+out: 
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+static inline int zfcp_fsf_req_create_sbal_check(
-+		unsigned long *flags,
-+		zfcp_qdio_queue_t *queue,
-+		int needed)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+
-+        write_lock_irqsave(&queue->queue_lock, *flags);
-+	if (atomic_read(&queue->free_count) >= needed) 
-+		return 1;
-+        write_unlock_irqrestore(&queue->queue_lock, *flags);
-+	return 0;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * set qtcb pointer in fsf_req and initialize QTCB
-+ */
-+static inline void zfcp_fsf_req_qtcb_init(zfcp_fsf_req_t *fsf_req, u32 fsf_cmd)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+	if (fsf_cmd != FSF_QTCB_UNSOLICITED_STATUS) {
-+                struct zfcp_fsf_req_pool_buffer *data =
-+                        (struct zfcp_fsf_req_pool_buffer *) fsf_req;
-+                fsf_req->qtcb = &data->qtcb;
-+        }
-+
-+	if (fsf_req->qtcb) {
-+	        ZFCP_LOG_TRACE("fsf_req->qtcb=0x%lx\n",
-+                               (unsigned long ) fsf_req->qtcb);
-+		fsf_req->qtcb->prefix.req_id = (unsigned long)fsf_req;
-+		fsf_req->qtcb->prefix.ulp_info = zfcp_data.driver_version;
-+		fsf_req->qtcb->prefix.qtcb_type = fsf_qtcb_type[fsf_cmd];
-+		fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION;
-+		fsf_req->qtcb->header.req_handle = (unsigned long)fsf_req;
-+		fsf_req->qtcb->header.fsf_command = fsf_cmd;
-+		/* Request Sequence Number is set later when the request is
-+                   actually sent. */
-+	}
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * try to get needed SBALs in request queue
-+ * (get queue lock on success)
-+ */
-+static int zfcp_fsf_req_sbal_get(zfcp_adapter_t *adapter, int req_flags,
-+                                 unsigned long *lock_flags)
-+{
-+        int condition;
-+        unsigned long timeout = ZFCP_SBAL_TIMEOUT;
-+        zfcp_qdio_queue_t *req_queue = &adapter->request_queue;
-+
-+        if (req_flags & ZFCP_WAIT_FOR_SBAL) {
-+                ZFCP_WAIT_EVENT_TIMEOUT(adapter->request_wq, timeout,
-+                                        (condition =
-+                                         (zfcp_fsf_req_create_sbal_check)
-+                                         (lock_flags, req_queue, 1)));
-+                if (!condition)
-+                        return -EIO;
-+        } else if (!zfcp_fsf_req_create_sbal_check(lock_flags, req_queue, 1))
-+                return -EIO;
-+
-+        return 0;
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_req_create
-+ *
-+ * purpose:	create an FSF request at the specified adapter and
-+ *		setup common fields
-+ *
-+ * returns:	-ENOMEM if there was insufficient memory for a request
-+ *              -EIO if no qdio buffers could be allocate to the request
-+ *              -EINVAL/-EPERM on bug conditions in req_dequeue
-+ *              0 in success
-+ *
-+ * note:        The created request is returned by reference.
-+ *
-+ * locks:	lock of concerned request queue must not be held,
-+ *		but is held on completion (write, irqsave)
-+ */
-+static int zfcp_fsf_req_create(zfcp_adapter_t *adapter, u32 fsf_cmd,
-+                               int req_flags, zfcp_mem_pool_t *mem_pool,
-+                               unsigned long *lock_flags,
-+                               zfcp_fsf_req_t **fsf_req_p)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+	zfcp_fsf_req_t *fsf_req = NULL;
-+       	int retval=0;
-+        zfcp_qdio_queue_t *req_queue = &adapter->request_queue;
-+        volatile qdio_buffer_element_t *sbale;
-+
-+        ZFCP_LOG_TRACE("enter (adapter=0x%lx fsf_cmd=0x%x *lock_flags=0x%lx "
-+                       "req_flags=0x%x)\n", (unsigned long)adapter,
-+                       fsf_cmd, *lock_flags, req_flags);
-+
-+        atomic_inc(&adapter->reqs_in_progress);
-+
-+	/* allocate new FSF request */
-+	fsf_req = zfcp_fsf_req_alloc(mem_pool, req_flags, GFP_ATOMIC);
-+	if (!fsf_req) {
-+                ZFCP_LOG_DEBUG(
-+			"error: Could not put an FSF request into"
-+                        "the outbound (send) queue.\n");
-+                retval=-ENOMEM;
-+		goto failed_fsf_req;
-+	}
-+
-+        zfcp_fsf_req_qtcb_init(fsf_req, fsf_cmd);
-+
-+	/* initialize waitqueue which may be used to wait on 
-+	   this request completion */
-+	init_waitqueue_head(&fsf_req->completion_wq);
-+
-+        retval = zfcp_fsf_req_sbal_get(adapter, req_flags, lock_flags);
-+        if(retval < 0)
-+                goto failed_sbals;
-+
-+        /*
-+         * We hold queue_lock here. Check if QDIOUP is set and let request fail
-+         * if it is not set (see also *_open_qdio and *_close_qdio).
-+         */
-+
-+        if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
-+                write_unlock_irqrestore(&req_queue->queue_lock, *lock_flags);
-+                retval = -EIO;
-+                goto failed_sbals;
-+        }
-+
-+#ifndef ZFCP_PARANOIA_DEAD_CODE
-+	/* set magics */
-+	fsf_req->common_magic = ZFCP_MAGIC;
-+	fsf_req->specific_magic = ZFCP_MAGIC_FSFREQ;
-+#endif
-+	fsf_req->adapter = adapter;
-+	fsf_req->fsf_command = fsf_cmd;
-+	fsf_req->sbal_number = 1;
-+	fsf_req->sbal_first = req_queue->free_index;
-+	fsf_req->sbal_curr = req_queue->free_index;
-+        fsf_req->sbale_curr = 1;
-+
-+	if (req_flags & ZFCP_REQ_AUTO_CLEANUP)
-+		fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
-+
-+	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
-+
-+	/* setup common SBALE fields */
-+	sbale[0].addr = fsf_req;
-+	sbale[0].flags |= SBAL_FLAGS0_COMMAND;
-+	if (fsf_req->qtcb != 0) {
-+		sbale[1].addr = (void *)fsf_req->qtcb;
-+		sbale[1].length = sizeof(fsf_qtcb_t);
-+	}
-+
-+	ZFCP_LOG_TRACE("got %i free BUFFERs starting at index %i\n",
-+                       fsf_req->sbal_number, fsf_req->sbal_first);
-+
-+	goto success;
-+
-+ failed_sbals:
-+#ifdef ZFCP_STAT_QUEUES
-+        atomic_inc(&adapter->outbound_queue_full);
-+#endif
-+        /* dequeue new FSF request previously enqueued */
-+        zfcp_fsf_req_free(fsf_req);
-+        fsf_req = NULL;
-+        
-+ failed_fsf_req:
-+        //failed_running:
-+        write_lock_irqsave(&req_queue->queue_lock, *lock_flags);
-+
-+ success: 
-+        *fsf_req_p = fsf_req;
-+        ZFCP_LOG_TRACE("exit (%d)\n", retval);
-+	return retval;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+static inline int zfcp_qdio_determine_pci(zfcp_qdio_queue_t *req_queue, 
-+                                          zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
-+        int new_distance_from_int;
-+        int pci_pos;
-+	volatile qdio_buffer_element_t *sbale;
-+
-+        ZFCP_LOG_TRACE("enter (0x%lx, 0x%lx)\n", 
-+                       (unsigned long)req_queue,
-+                       (unsigned long)fsf_req);
-+
-+        new_distance_from_int = req_queue->distance_from_int +
-+                fsf_req->sbal_number;
-+
-+        if (new_distance_from_int >= ZFCP_QDIO_PCI_INTERVAL) {
-+                new_distance_from_int %= ZFCP_QDIO_PCI_INTERVAL;
-+                pci_pos  = fsf_req->sbal_first;
-+		pci_pos += fsf_req->sbal_number;
-+		pci_pos -= new_distance_from_int;
-+		pci_pos -= 1;
-+		pci_pos %= QDIO_MAX_BUFFERS_PER_Q;
-+		sbale = zfcp_qdio_sbale_req(fsf_req, pci_pos, 0);
-+		sbale->flags |= SBAL_FLAGS0_PCI;
-+                ZFCP_LOG_DEBUG(
-+			"Setting PCI flag at pos %d (0x%lx)\n",
-+			pci_pos,
-+			(unsigned long)sbale);
-+		ZFCP_HEX_DUMP(
-+			ZFCP_LOG_LEVEL_TRACE,
-+			(char*)sbale,
-+			sizeof(qdio_buffer_t));
-+        }
-+
-+        ZFCP_LOG_TRACE("exit (%d)\n", new_distance_from_int);
-+	return new_distance_from_int;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+
-+/*
-+ * function:    zfcp_fsf_req_send
-+ *
-+ * purpose:	start transfer of FSF request via QDIO
-+ *
-+ * returns:	0 - request transfer succesfully started
-+ *		!0 - start of request transfer failed
-+ */
-+static int zfcp_fsf_req_send(zfcp_fsf_req_t *fsf_req, struct timer_list *timer)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval = 0;
-+        zfcp_adapter_t *adapter = fsf_req->adapter;
-+        zfcp_qdio_queue_t *req_queue = &adapter->request_queue;
-+        volatile qdio_buffer_element_t* sbale;
-+	int inc_seq_no = 1;
-+        int new_distance_from_int;
-+	unsigned long flags;
-+	int test_count;
-+
-+	u8 sbal_index = fsf_req->sbal_first;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (fsf_req=0x%lx timer=0x%lx)\n",
-+		(unsigned long)fsf_req,
-+		(unsigned long)timer);
-+        
-+	/* FIXME(debug): remove it later */
-+	sbale = zfcp_qdio_sbale_req(fsf_req, sbal_index, 0);
-+	ZFCP_LOG_DEBUG(
-+		"SBALE0 flags=0x%x\n",
-+		sbale[0].flags);
-+	ZFCP_LOG_TRACE("HEX DUMP OF SBALE1 PAYLOAD:\n");
-+	ZFCP_HEX_DUMP(
-+		ZFCP_LOG_LEVEL_TRACE,
-+		(char*)sbale[1].addr,
-+		sbale[1].length);
-+
-+	test_count = (fsf_req->sbal_curr - fsf_req->sbal_first) + 1;
-+	test_count += QDIO_MAX_BUFFERS_PER_Q; /* no module of <0 */
-+	test_count %= QDIO_MAX_BUFFERS_PER_Q;
-+	if (fsf_req->sbal_number != test_count)
-+		ZFCP_LOG_NORMAL(
-+			"error: inconsistent SBAL count in request "
-+			"(%d, %d, %d, %d, %d)\n",
-+			fsf_req->sbal_first,
-+			fsf_req->sbal_curr,
-+			fsf_req->sbal_last,
-+			fsf_req->sbal_number,
-+			test_count);
-+        
-+	/* set sequence counter in QTCB */
-+	if (fsf_req->qtcb) {
-+		fsf_req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no;
-+		fsf_req->seq_no = adapter->fsf_req_seq_no;
-+		ZFCP_LOG_TRACE(
-+			"FSF request 0x%lx of adapter 0x%lx gets "
-+			"FSF sequence counter value of %i\n",
-+			(unsigned long)fsf_req,
-+			(unsigned long)adapter,
-+			fsf_req->qtcb->prefix.req_seq_no);
-+	} else
-+                inc_seq_no = 0;
-+
-+        /* put allocated FSF request at list tail */
-+	write_lock_irqsave(&adapter->fsf_req_list_lock, flags);
-+        list_add_tail(&fsf_req->list,
-+                      &adapter->fsf_req_list_head);
-+	write_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
-+
-+	/* figure out expiration time of timeout and start timeout */
-+	if (timer) {
-+		timer->expires += jiffies;
-+		add_timer(timer);
-+	}
-+
-+	ZFCP_LOG_TRACE(
-+		"request queue of adapter with devno=0x%04x: "
-+		"next free SBAL is %i, %i free SBALs\n",
-+		adapter->devno,
-+		req_queue->free_index,
-+		atomic_read(&req_queue->free_count));
-+
-+        ZFCP_LOG_DEBUG(
-+		"Calling do QDIO irq=0x%x, flags=0x%x, queue_no=%i, "
-+		"index_in_queue=%i, count=%i, buffers=0x%lx\n",
-+		adapter->irq,
-+		QDIO_FLAG_SYNC_OUTPUT,
-+		0,
-+		fsf_req->sbal_first,
-+		fsf_req->sbal_number,
-+		(unsigned long)&req_queue->buffer[sbal_index]);	
-+
-+        /*
-+         * adjust the number of free SBALs in request queue as well as
-+         * position of first one
-+         */
-+        atomic_sub(fsf_req->sbal_number, &req_queue->free_count);
-+        ZFCP_LOG_TRACE("free_count=%d\n",
-+                       atomic_read(&req_queue->free_count));
-+        req_queue->free_index += fsf_req->sbal_number;	/* increase */
-+        req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;    /* wrap if needed */
-+        new_distance_from_int = zfcp_qdio_determine_pci(req_queue, fsf_req);
-+	retval = do_QDIO(
-+			adapter->irq,
-+			QDIO_FLAG_SYNC_OUTPUT,
-+                        0,
-+			fsf_req->sbal_first,
-+                        fsf_req->sbal_number,
-+                        NULL);
-+
-+	if (retval) {
-+                /* Queues are down..... */
-+                retval=-EIO;
-+		/* FIXME(potential race): timer might be expired (absolutely unlikely) */
-+		if (timer)
-+			del_timer_sync(timer);
-+		write_lock_irqsave(&adapter->fsf_req_list_lock, flags);
-+        	list_del(&fsf_req->list);
-+		write_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
-+                /*
-+                 * adjust the number of free SBALs in request queue as well as
-+                 * position of first one
-+                 */
-+                zfcp_zero_sbals(
-+			req_queue->buffer,
-+			fsf_req->sbal_first,
-+			fsf_req->sbal_number);
-+                atomic_add(fsf_req->sbal_number, &req_queue->free_count);
-+                req_queue->free_index += QDIO_MAX_BUFFERS_PER_Q;
-+                req_queue->free_index -= fsf_req->sbal_number;
-+                req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;
-+
-+		ZFCP_LOG_DEBUG(
-+			"error: do_QDIO failed. Buffers could not be enqueued "
-+                        "to request queue.\n");
-+        } else {
-+                req_queue->distance_from_int = new_distance_from_int;
-+#ifdef ZFCP_DEBUG_REQUESTS
-+                debug_text_event(adapter->req_dbf, 1, "o:a/seq");
-+                debug_event(adapter->req_dbf, 1, &fsf_req,
-+                            sizeof(unsigned long));
-+                if (inc_seq_no)
-+                        debug_event(adapter->req_dbf, 1,
-+                                    &adapter->fsf_req_seq_no, sizeof(u32));
-+                else
-+                        debug_text_event(adapter->req_dbf, 1, "nocb");
-+                debug_event(adapter->req_dbf, 4, &fsf_req->fsf_command,
-+                            sizeof(fsf_req->fsf_command));
-+                if (fsf_req->qtcb)
-+                        debug_event(adapter->req_dbf, 5, &fsf_req->qtcb,
-+                                    sizeof(unsigned long));
-+                if (fsf_req && (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL))
-+                        debug_text_event(adapter->req_dbf, 5, "fsfa_pl");
-+#endif /* ZFCP_DEBUG_REQUESTS */
-+		/*
-+		 * increase FSF sequence counter -
-+		 * this must only be done for request successfully enqueued to QDIO
-+		 * this rejected requests may be cleaned up by calling routines
-+		 * resulting in missing sequence counter values otherwise,
-+		 */
-+                /* Don't increase for unsolicited status */ 
-+                if (inc_seq_no) {
-+                        adapter->fsf_req_seq_no++;
-+			ZFCP_LOG_TRACE(
-+				"FSF sequence counter value of adapter 0x%lx "
-+				"increased to %i\n",
-+				(unsigned long)adapter,
-+				adapter->fsf_req_seq_no);
-+		}
-+		/* count FSF requests pending */
-+		atomic_inc(&adapter->fsf_reqs_active);
-+#ifdef ZFCP_STAT_QUEUES
-+		atomic_inc(&adapter->outbound_total);
-+#endif
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_req_cleanup
-+ *
-+ * purpose:	cleans up an FSF request and removes it from the specified list
-+ *
-+ * returns:
-+ *
-+ * assumption:	no pending SB in SBALEs other than QTCB
-+ */
-+static int zfcp_fsf_req_cleanup(zfcp_fsf_req_t *fsf_req)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
-+ 
-+	int retval;
-+        zfcp_adapter_t *adapter = fsf_req->adapter;
-+	unsigned long flags;
-+
-+	ZFCP_LOG_TRACE("enter (fsf_req=0x%lx)\n", (unsigned long)fsf_req);
-+
-+	write_lock_irqsave(&adapter->fsf_req_list_lock, flags);
-+        list_del(&fsf_req->list);
-+	write_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
-+        retval = zfcp_fsf_req_free(fsf_req);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:	zfcp_zero_sbals
-+ *
-+ * purpose:	zeros specified range of SBALs
-+ *
-+ * returns:
-+ */
-+static inline void zfcp_zero_sbals(qdio_buffer_t *buf[], int first, int clean_count)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_QDIO
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_QDIO
-+
-+        int cur_pos;
-+        int index;
-+        
-+        
-+	ZFCP_LOG_TRACE(
-+		"enter (buf=0x%lx, first=%i, clean_count=%i\n",
-+		(unsigned long)buf, first, clean_count);
-+        
-+        for (cur_pos = first; cur_pos < (first + clean_count); cur_pos++){
-+                index = cur_pos % QDIO_MAX_BUFFERS_PER_Q;
-+                memset(buf[index], 0, sizeof(qdio_buffer_t));
-+		ZFCP_LOG_TRACE(
-+			"zeroing BUFFER %d at address 0x%lx\n",
-+                        index,
-+			(unsigned long) buf[index]);
-+        }
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+static void zfcp_config_parse_error(
-+	unsigned char	*s,		/* complete mapping string */
-+	unsigned char	*err_pos,	/* position of error in mapping string */
-+	const char	*err_msg,	/* error message */
-+	...)				/* additional arguments to be integrated into error message */
-+{
-+#define ZFCP_LOG_AREA		ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX	ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	int buf_l;
-+	va_list args;
-+	unsigned char *pos;
-+	unsigned char c;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (s=0x%lx, err_pos=0x%lx, err_msg=0x%lx\n",
-+		(unsigned long)s, 
-+                (unsigned long)err_pos, 
-+                (unsigned long)err_msg);
-+
-+	/* integrate additional arguments into error message */
-+	va_start(args, err_msg);
-+	buf_l = vsprintf(zfcp_data.perrbuf, err_msg, args);
-+	va_end(args);
-+	if (buf_l > ZFCP_PARSE_ERR_BUF_SIZE) {
-+		ZFCP_LOG_NORMAL("Buffer overflow while parsing error message\n");
-+		/* truncate error message */
-+		zfcp_data.perrbuf[ZFCP_PARSE_ERR_BUF_SIZE - 1] = '\0';
-+		buf_l = ZFCP_PARSE_ERR_BUF_SIZE;
-+	}
-+
-+	/* calculate and print substring of mapping followed by error info */
-+	pos = min((s + strlen(s) - 1), (err_pos + 1));
-+	c = *pos;
-+	*pos = '\0';
-+	ZFCP_LOG_NORMAL("\"%s\" <- %s\n", s, zfcp_data.perrbuf);
-+	*pos = c;
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/* these macros implement the logic of the following 3 functions */
-+#define ZFCP_PARSE_CHECK(condition, err_msg...) \
-+		if (condition) { \
-+			zfcp_config_parse_error(s, s + s_l - ts_l, err_msg); \
-+			retval = -EINVAL; \
-+			goto out; \
-+		}
-+
-+#define ZFCP_PARSE_CHECKEND \
-+		ZFCP_PARSE_CHECK(!ts_l, "syntax error: unexpected end of record")
-+
-+#define ZFCP_PARSE_TRUNCATE \
-+		ts += count; ts_l -= count;
-+
-+#define ZFCP_PARSE_SKIP_CHARS(characters, min, max) \
-+		count = strnspn(ts, characters, ts_l); \
-+		ZFCP_PARSE_CHECK((size_t)count < (size_t)min, "syntax error: missing \"%c\" or equivalent character", *characters) \
-+		ZFCP_PARSE_CHECK((size_t)count > (size_t)max, "syntax error: extranous \"%c\" or equivalent character", *characters) \
-+		ZFCP_PARSE_TRUNCATE
-+
-+#define ZFCP_PARSE_SKIP_COMMENT \
-+		count = strnspn(ts, ZFCP_PARSE_COMMENT_CHARS, ts_l); \
-+		if (count) { \
-+			char *tmp; \
-+			ZFCP_PARSE_TRUNCATE \
-+			tmp = strnpbrk(ts, ZFCP_PARSE_RECORD_DELIM_CHARS, ts_l); \
-+			if (tmp) \
-+				count = (unsigned long)tmp - (unsigned long)ts; \
-+			else	count = ts_l; \
-+			ZFCP_PARSE_TRUNCATE \
-+		}
-+
-+#define ZFCP_PARSE_NUMBER(func, value, add_cond, msg...) \
-+		value = func(ts, &endp, 0); \
-+		count = (unsigned long)endp - (unsigned long)ts; \
-+		ZFCP_PARSE_CHECK(!count || (add_cond), msg) \
-+		ZFCP_PARSE_TRUNCATE
-+		
-+#define ZFCP_PARSE_UL(value, cond, msg...) \
-+		ZFCP_PARSE_NUMBER(simple_strtoul, value, cond, msg)
-+
-+#define ZFCP_PARSE_ULL(value, cond, msg...) \
-+		ZFCP_PARSE_NUMBER(simple_strtoull, value, cond, msg)
-+
-+
-+static int zfcp_config_parse_record_list(unsigned char *s, int s_l, int flags)
-+{
-+#define ZFCP_LOG_AREA           ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX    ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	int retval;
-+	int count;
-+	zfcp_config_record_t rec;
-+	int ts_l = s_l;
-+	unsigned char *ts = s;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (s=0x%lx, s_l=%i, flags=%i)\n",
-+		(unsigned long)s, s_l, flags);
-+
-+	while (ts_l) {
-+		/* parse single line */
-+		count = zfcp_config_parse_record(ts, ts_l, &rec);
-+		if (count < 0) {
-+			retval = count;
-+			goto out;
-+		}
-+		ZFCP_PARSE_TRUNCATE;
-+
-+		/* create configuration according to parsed line */
-+		if (rec.valid) {
-+			if (flags & ZFCP_PARSE_ADD) {
-+				retval = zfcp_config_parse_record_add(&rec);
-+                        } else {	
-+                                /* FIXME (implement) switch in when record_del works again */
-+#if 0
-+                                retval = zfcp_config_parse_record_del(&rec);
-+#endif
-+                                ZFCP_LOG_TRACE("DEL\n");
-+                                retval = -1;
-+                        }
-+			if (retval < 0)
-+				goto out;
-+		} /* else we parsed an empty line or a comment */
-+                if (ts_l > 0) {
-+                        /* skip expected 'new line' */
-+			ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_RECORD_DELIM_CHARS, 1, ts_l);
-+                }
-+	}
-+	retval = s_l;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+static int zfcp_config_parse_record(
-+	unsigned char	*s,
-+	int		s_l,
-+	zfcp_config_record_t *rec)
-+{
-+#define ZFCP_LOG_AREA           ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX    ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	int retval;
-+	int count = 0;
-+	char *endp;
-+	unsigned char *ts = s;
-+	int ts_l = s_l;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (s=0x%lx, s_l=%i, rec=0x%lx)\n",
-+		(unsigned long)s, 
-+                s_l,
-+                (unsigned long)rec);
-+
-+	rec->valid = 0;
-+
-+	/* skip any leading spaces + tabs */
-+	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_SPACE_CHARS, 0, -1UL);
-+
-+	/* allow for comments */
-+	ZFCP_PARSE_SKIP_COMMENT;
-+
-+	/* allow 'empty' line */
-+	if (strnspn(ts, ZFCP_PARSE_RECORD_DELIM_CHARS, 1))
-+		goto calculate;
-+
-+	/* parse device number of host */
-+	ZFCP_PARSE_UL(rec->devno, rec->devno > 0xFFFF, "no valid device number");
-+	ZFCP_LOG_TRACE("devno \"0x%lx\"\n", rec->devno);
-+	ZFCP_PARSE_CHECKEND;
-+
-+	/* skip delimiting spaces + tabs (at least 1 character is mandatory */
-+	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_SPACE_CHARS, 1, -1UL);
-+	ZFCP_PARSE_CHECKEND;
-+
-+	/* parse scsi id of remote port */
-+	ZFCP_PARSE_UL(rec->scsi_id, 0, "no valid SCSI ID");
-+	ZFCP_LOG_TRACE("SCSI ID \"0x%lx\"\n", rec->scsi_id);
-+	ZFCP_PARSE_CHECKEND;
-+
-+	/* skip delimiting character */
-+	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_DELIM_CHARS, 1, 1);
-+	ZFCP_PARSE_CHECKEND;
-+
-+	/* parse wwpn of remote port */
-+	ZFCP_PARSE_ULL(rec->wwpn, 0, "no valid WWPN");
-+	ZFCP_LOG_TRACE("WWPN \"0x%016Lx\"\n", rec->wwpn);
-+	ZFCP_PARSE_CHECKEND;
-+
-+	/* skip delimiting spaces + tabs (at least 1 character is mandatory */
-+	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_SPACE_CHARS, 1, -1UL);
-+	ZFCP_PARSE_CHECKEND;
-+
-+	/* parse scsi lun of logical unit */
-+	ZFCP_PARSE_UL(rec->scsi_lun, 0, "no valid SCSI LUN");
-+	ZFCP_LOG_TRACE("SCSI LUN \"0x%lx\"\n", rec->scsi_lun);
-+	ZFCP_PARSE_CHECKEND;
-+
-+	/* skip delimiting character */
-+	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_DELIM_CHARS, 1, 1);
-+	ZFCP_PARSE_CHECKEND;
-+
-+	/* parse fcp_lun of logical unit */
-+	ZFCP_PARSE_ULL(rec->fcp_lun, 0, "no valid FCP_LUN");
-+	ZFCP_LOG_TRACE("FCP_LUN \"0x%016Lx\"\n", rec->fcp_lun);
-+
-+        /* skip any ending spaces + tabs */
-+	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_SPACE_CHARS, 0, -1UL);
-+
-+	/* allow for comments */
-+	ZFCP_PARSE_SKIP_COMMENT;
-+
-+	/* this is something valid */
-+	rec->valid = 1;
-+
-+calculate:
-+	/* length of string which has been parsed */
-+	retval = s_l - ts_l;
-+
-+out:
-+        ZFCP_LOG_TRACE("exit %d\n",
-+                       retval);
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+#define ZFCP_PRINT_FAILED_RECORD(rec, log_func) \
-+	log_func( \
-+		"warning: unable to add record: " \
-+		"0x%04lx %li:0x%016Lx %li:0x%016Lx\n", \
-+		rec->devno, \
-+		rec->scsi_id, rec->wwpn, \
-+		rec->scsi_lun, rec->fcp_lun);
-+
-+
-+/*
-+ * function:	zfcp_config_parse_record_add
-+ *
-+ * purpose:	Alloctes the required adapter, port and unit structs
-+ *              and puts them into their respective lists
-+ *
-+ * returns:	0 on success
-+ *              -E* on failure (depends on called routines)
-+ */
-+int zfcp_config_parse_record_add(zfcp_config_record_t *rec)
-+{
-+#define ZFCP_LOG_AREA		ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX	ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	int retval;
-+	zfcp_adapter_t *adapter;
-+	zfcp_port_t *port;
-+	zfcp_unit_t *unit;
-+
-+	ZFCP_LOG_TRACE("enter (rec=0x%lx)\n", (unsigned long)rec);
-+
-+        /* don't allow SCSI ID 0 for any port since it is reserved for adapters */
-+        if (rec->scsi_id == 0) {
-+                ZFCP_LOG_NORMAL(
-+                        "warning: SCSI ID 0 is not allowed for ports as it is "
-+                        "reserved for adapters\n");
-+		retval = -EINVAL;
-+                goto failed_record;
-+        }
-+	/* check for adapter and configure it if needed */
-+        retval = zfcp_adapter_enqueue(rec->devno, &adapter);
-+        if (retval < 0)
-+                goto failed_record;
-+
-+	/*
-+	 * no explicit adapter reopen necessary,
-+	 * will be escalated by unit reopen if required
-+	 */
-+
-+        retval = zfcp_port_enqueue(
-+			adapter,
-+			rec->scsi_id,
-+			rec->wwpn,
-+			0,
-+			&port);
-+        if (retval < 0)
-+                goto failed_record;
-+
-+	/*
-+	 * no explicit port reopen necessary,
-+	 * will be escalated by unit reopen if required
-+	 */
-+
-+        retval = zfcp_unit_enqueue(
-+			port,
-+			rec->scsi_lun,
-+			rec->fcp_lun,
-+			&unit);
-+        if (retval < 0)
-+                goto failed_record;
-+
-+	zfcp_erp_unit_reopen(unit, 0);
-+
-+	/* processed record successfully */
-+	goto out;
-+
-+failed_record:
-+	ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_NORMAL);
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+#if 0
-+/* FIXME(design): rewrite necessary */
-+static int zfcp_config_parse_record_del(zfcp_config_record_t *rec)
-+{
-+#define ZFCP_LOG_AREA		ZFCP_LOG_AREA_CONFIG
-+#define ZFCP_LOG_AREA_PREFIX	ZFCP_LOG_AREA_PREFIX_CONFIG
-+
-+	int retval = 0;
-+	unsigned long flags;
-+	zfcp_adapter_t *adapter;
-+	zfcp_port_t *port;
-+	zfcp_unit_t *unit;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (rec=0x%lx)\n",
-+		(unsigned long)rec);
-+
-+	/* check for adapter */
-+	write_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
-+	ZFCP_FOR_EACH_ADAPTER(adapter) {
-+                if (adapter->devno == rec->devno)
-+			break;
-+	}
-+	if (!adapter) {
-+		ZFCP_LOG_NORMAL(
-+			"warning: Could not delete a record. "
-+                        "The adapter with devno 0x%04x does not exist.\n",
-+			adapter->devno);
-+		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_DEBUG);
-+		goto unlock_adapter;
-+	}
-+
-+	/* check for remote port */
-+	write_lock(&adapter->port_list_lock);
-+	ZFCP_FOR_EACH_PORT(adapter, port) {
-+		if (port->scsi_id == rec->scsi_id)
-+			break;
-+	}
-+	if (!port) {
-+		ZFCP_LOG_NORMAL(
-+                               "warning: Could not delete a record. "
-+                               "The port with SCSI ID %i does not exist.\n",
-+                               port->scsi_id);
-+		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_DEBUG);
-+		goto unlock_port;
-+	}
-+	if (port->wwpn != rec->wwpn) {
-+		ZFCP_LOG_NORMAL(
-+			"error: The port WWPN 0x%016Lx "
-+			"does not match the already configured WWPN 0x%016Lx\n",
-+			rec->wwpn,
-+			(llui_t)port->wwpn);
-+		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_INFO);
-+		goto unlock_port;
-+	}
-+
-+	/* check for logical unit */
-+	write_lock(&port->unit_list_lock);
-+	ZFCP_FOR_EACH_UNIT(port, unit) {
-+		if (unit->scsi_lun == rec->scsi_lun)
-+			break;
-+	}
-+	if (!unit) {
-+		ZFCP_LOG_NORMAL(
-+			"warning: Could not delete a record. "
-+                        "The unit with SCSI LUN %i does not exist\n",
-+			unit->scsi_lun);
-+		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_DEBUG);
-+		goto unlock_unit;
-+	}
-+	if (unit->fcp_lun != rec->fcp_lun) {
-+		ZFCP_LOG_NORMAL(
-+			"error: The record for the FCP_LUN 0x%016Lx "
-+			"does not match that of the already "
-+                        "configured FCP_LUN 0x%016Lx\n",
-+			rec->fcp_lun,
-+			(llui_t)unit->fcp_lun);
-+		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_INFO);
-+		goto unlock_unit;
-+	}
-+
-+	/* FIXME: do more work here: CLOSE UNIT */
-+	retval = zfcp_unit_dequeue(unit);
-+	if (retval == -EBUSY) {
-+		ZFCP_LOG_NORMAL("warning: Attempt to remove active unit with "
-+                               "FCP_LUN 0x%016Lx, at the port with WWPN 0x%016Lx of the "
-+                               "adapter with devno 0x%04x was ignored. Unit "
-+                               "is still in use.\n",
-+                               (llui_t)unit->fcp_lun,
-+                               (llui_t)unit->port->wwpn,
-+                               unit->port->adapter->devno);
-+		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_INFO);
-+		goto unlock_unit;
-+	}
-+
-+	/* FIXME: do more work here: CLOSE PORT */
-+	retval = zfcp_port_dequeue(port);
-+	if (retval == -EBUSY) {
-+		retval = 0;
-+		goto unlock_unit;
-+	}
-+
-+	/* FIXME: do more work here: shutdown adapter */
-+	retval = zfcp_adapter_dequeue(adapter);
-+	if (retval == -EBUSY)
-+		retval = 0;
-+
-+unlock_unit:
-+	write_unlock(&port->unit_list_lock);
-+unlock_port:
-+	write_unlock(&adapter->port_list_lock);
-+unlock_adapter:
-+	write_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+#endif
-+
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	called if an adapter failed,
-+ *		initiates adapter recovery which is done
-+ *		asynchronously
-+ *
-+ * returns:	0	- initiated action succesfully
-+ *		<0	- failed to initiate action
-+ */
-+static int zfcp_erp_adapter_reopen_internal(
-+		zfcp_adapter_t *adapter,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)adapter,
-+		clear_mask);
-+
-+        debug_text_event(adapter->erp_dbf,5,"a_ro");
-+        ZFCP_LOG_DEBUG(
-+		"Reopen on the adapter with devno 0x%04x\n",
-+		adapter->devno);
-+
-+	zfcp_erp_adapter_block(adapter, clear_mask);
-+
-+	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
-+		ZFCP_LOG_DEBUG(
-+			"skipped reopen on the failed adapter with devno 0x%04x\n",
-+			adapter->devno);
-+                debug_text_event(adapter->erp_dbf,5,"a_ro_f");
-+		/* ensure propagation of failed status to new devices */
-+		zfcp_erp_adapter_failed(adapter);
-+		retval = -EIO;
-+		goto out;
-+	}
-+	retval = zfcp_erp_action_enqueue(
-+			ZFCP_ERP_ACTION_REOPEN_ADAPTER,
-+			adapter,
-+			NULL,
-+			NULL);
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	Wrappper for zfcp_erp_adapter_reopen_internal
-+ *              used to ensure the correct locking
-+ *
-+ * returns:	0	- initiated action succesfully
-+ *		<0	- failed to initiate action
-+ */
-+static int zfcp_erp_adapter_reopen(
-+		zfcp_adapter_t *adapter,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        unsigned long flags;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)adapter,
-+		clear_mask);
-+
-+        write_lock_irqsave(&adapter->erp_lock, flags);
-+        retval = zfcp_erp_adapter_reopen_internal(adapter, clear_mask);
-+        write_unlock_irqrestore(&adapter->erp_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static inline int zfcp_erp_adapter_shutdown(zfcp_adapter_t* adapter, int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)adapter,
-+		clear_mask);
-+
-+        retval=zfcp_erp_adapter_reopen(
-+			adapter,
-+			ZFCP_STATUS_COMMON_RUNNING |
-+			ZFCP_STATUS_COMMON_ERP_FAILED |
-+			clear_mask);	
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static inline int zfcp_erp_port_shutdown(zfcp_port_t* port, int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)port,
-+		clear_mask);
-+
-+	retval = zfcp_erp_port_reopen(
-+			port,
-+			ZFCP_STATUS_COMMON_RUNNING |
-+			ZFCP_STATUS_COMMON_ERP_FAILED |
-+			clear_mask);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static inline int zfcp_erp_unit_shutdown(zfcp_unit_t* unit, int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (unit=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)unit,
-+		clear_mask);
-+
-+	retval = zfcp_erp_unit_reopen(
-+			unit,
-+			ZFCP_STATUS_COMMON_RUNNING |
-+			ZFCP_STATUS_COMMON_ERP_FAILED |
-+			clear_mask);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_els
-+ *
-+ * purpose:     Originator of the ELS commands
-+ *
-+ * returns:     0       - Operation completed successfuly
-+ *              -EINVAL - Unknown IOCTL command or invalid sense data record
-+ *              -ENOMEM - Insufficient memory
-+ *              -EPERM  - Cannot create or queue FSF request
-+ */
-+static int zfcp_els(zfcp_port_t *port, u8 ls_code)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	struct zfcp_send_els *send_els;
-+	struct zfcp_ls_rls *rls;
-+	struct zfcp_ls_pdisc *pdisc;
-+	struct zfcp_ls_adisc *adisc;
-+	void *page = NULL;
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx ls_code=0x%02x)\n",
-+		(unsigned long)port, ls_code);
-+
-+	send_els = (struct zfcp_send_els*)ZFCP_KMALLOC(
-+		sizeof(struct zfcp_send_els), GFP_ATOMIC);
-+	if (send_els == NULL)
-+		goto nomem;
-+
-+	send_els->req = (struct scatterlist*)ZFCP_KMALLOC(
-+		sizeof(struct scatterlist), GFP_ATOMIC);
-+	if (send_els->req == NULL)
-+		goto nomem;
-+	send_els->req_count = 1;
-+
-+	send_els->resp = (struct scatterlist*)ZFCP_KMALLOC(
-+		sizeof(struct scatterlist), GFP_ATOMIC);
-+	if (send_els->resp == NULL)
-+		goto nomem;
-+	send_els->resp_count = 1;
-+
-+	page = (void*)ZFCP_GET_ZEROED_PAGE(GFP_ATOMIC);
-+	if (page == NULL)
-+		goto nomem;
-+	send_els->req->address = (char*)page;
-+	send_els->resp->address = (char*)(page + (PAGE_SIZE >> 1));
-+
-+	send_els->port = port;
-+	send_els->ls_code = ls_code;
-+	send_els->handler = zfcp_els_handler;
-+	send_els->handler_data = (unsigned long)send_els;
-+
-+	*(u32*)page = 0;
-+	*(u8*)page = ls_code;
-+
-+	switch (ls_code) {
-+
-+	case ZFCP_LS_RTV:
-+		send_els->req->length = sizeof(struct zfcp_ls_rtv);
-+		send_els->resp->length = sizeof(struct zfcp_ls_rtv_acc);
-+		ZFCP_LOG_NORMAL(
-+			"RTV request from sid 0x%06x to did 0x%06x\n",
-+			port->adapter->s_id, port->d_id);
-+		break;
-+
-+	case ZFCP_LS_RLS:
-+		send_els->req->length = sizeof(struct zfcp_ls_rls);
-+		send_els->resp->length = sizeof(struct zfcp_ls_rls_acc);
-+		rls = (struct zfcp_ls_rls*)send_els->req->address;
-+		rls->port_id = port->adapter->s_id;
-+		ZFCP_LOG_NORMAL(
-+			"RLS request from sid 0x%06x to did 0x%06x "
-+			"payload(port_id=0x%06x)\n",
-+			port->adapter->s_id, port->d_id, rls->port_id);
-+		break;
-+
-+	case ZFCP_LS_PDISC:
-+		send_els->req->length = sizeof(struct zfcp_ls_pdisc);
-+		send_els->resp->length = sizeof(struct zfcp_ls_pdisc_acc);
-+		pdisc = (struct zfcp_ls_pdisc*)send_els->req->address;
-+		pdisc->wwpn = port->adapter->wwpn;
-+		pdisc->wwnn = port->adapter->wwnn;
-+		ZFCP_LOG_NORMAL(
-+			"PDISC request from sid 0x%06x to did 0x%06x "
-+			"payload(wwpn=0x%016Lx wwnn=0x%016Lx)\n",
-+			port->adapter->s_id, port->d_id,
-+			(unsigned long long)pdisc->wwpn,
-+			(unsigned long long)pdisc->wwnn);
-+		break;
-+
-+	case ZFCP_LS_ADISC:
-+		send_els->req->length = sizeof(struct zfcp_ls_adisc);
-+		send_els->resp->length = sizeof(struct zfcp_ls_adisc_acc);
-+		adisc = (struct zfcp_ls_adisc*)send_els->req->address;
-+		adisc->hard_nport_id = port->adapter->s_id;
-+		adisc->wwpn = port->adapter->wwpn;
-+		adisc->wwnn = port->adapter->wwnn;
-+		adisc->nport_id = port->adapter->s_id;
-+		ZFCP_LOG_NORMAL(
-+			"ADISC request from sid 0x%06x to did 0x%06x "
-+			"payload(wwpn=0x%016Lx wwnn=0x%016Lx "
-+			"hard_nport_id=0x%06x nport_id=0x%06x)\n",
-+			port->adapter->s_id, port->d_id,
-+			(unsigned long long)adisc->wwpn,
-+			(unsigned long long)adisc->wwnn,
-+			adisc->hard_nport_id, adisc->nport_id);
-+		break;
-+
-+	default:
-+		ZFCP_LOG_NORMAL(
-+			"ELS command code 0x%02x is not supported\n", ls_code);
-+		retval = -EINVAL;
-+		goto invalid_ls_code;
-+	}
-+
-+	retval = zfcp_fsf_send_els(send_els);
-+	if (retval != 0) {
-+		ZFCP_LOG_NORMAL(
-+			"ELS request could not be processed "
-+			"(sid=0x%06x did=0x%06x)\n",
-+			port->adapter->s_id, port->d_id);
-+		retval = -EPERM;
-+	}
-+
-+	goto out;
-+
-+nomem:
-+	ZFCP_LOG_INFO("Out of memory!\n");
-+	retval = -ENOMEM;
-+
-+invalid_ls_code:
-+	if (page != NULL)
-+		ZFCP_FREE_PAGE((unsigned long)page);
-+	if (send_els != NULL) {
-+		if (send_els->req != NULL)
-+			ZFCP_KFREE(send_els->req, sizeof(struct scatterlist));
-+		if (send_els->resp != NULL)
-+			ZFCP_KFREE(send_els->resp, sizeof(struct scatterlist));
-+		ZFCP_KFREE(send_els, sizeof(struct zfcp_send_els));
-+	}
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/**
-+ * zfcp_els_handler - handler for ELS commands
-+ * @data: pointer to struct zfcp_send_els
-+ * If ELS failed (LS_RJT or timed out) forced reopen of the port is triggered.
-+ */
-+static void zfcp_els_handler(unsigned long data)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	struct zfcp_send_els *send_els = (struct zfcp_send_els*)data;
-+	zfcp_port_t *port = send_els->port;
-+	zfcp_adapter_t *adapter = port->adapter;
-+	u8 req_code = *(u8*)send_els->req->address;
-+	struct zfcp_ls_rtv_acc *rtv;
-+	struct zfcp_ls_rls_acc *rls;
-+	struct zfcp_ls_pdisc_acc *pdisc;
-+	struct zfcp_ls_adisc_acc *adisc;
-+
-+	ZFCP_LOG_TRACE("enter (data=0x%lx)\n", data);
-+
-+	/* request rejected or timed out */
-+	if (send_els->status != 0) {
-+		ZFCP_LOG_NORMAL("ELS request failed, force physical port "
-+			"reopen (wwpn=0x%016Lx devno=0x%04x)\n",
-+			(unsigned long long)port->wwpn, adapter->devno);
-+		debug_text_event(adapter->erp_dbf, 3, "forcreop");
-+		if (zfcp_erp_port_forced_reopen(port, 0))
-+			ZFCP_LOG_NORMAL(
-+				"Cannot reopen a remote port "
-+				"(wwpn=0x%016Lx devno=0x%04x)\n",
-+				(unsigned long long)port->wwpn,
-+				adapter->devno);
-+		goto out;
-+	}
-+
-+        switch (req_code) {
-+
-+        case ZFCP_LS_RTV:
-+                rtv = (struct zfcp_ls_rtv_acc*)send_els->resp->address;
-+                ZFCP_LOG_NORMAL(
-+				"RTV response from did 0x%06x to sid 0x%06x "
-+				"with payload(R_A_TOV=%ds E_D_TOV=%d%cs)\n",
-+				port->d_id, port->adapter->s_id,
-+				rtv->r_a_tov, rtv->e_d_tov,
-+				rtv->qualifier & ZFCP_LS_RTV_E_D_TOV_FLAG ?
-+                                'n' : 'm');
-+                break;
-+
-+        case ZFCP_LS_RLS:
-+                rls = (struct zfcp_ls_rls_acc*)send_els->resp->address;
-+                ZFCP_LOG_NORMAL(
-+				"RLS response from did 0x%06x to sid 0x%06x "
-+				"with payload(link_failure_count=%u "
-+				"loss_of_sync_count=%u "
-+				"loss_of_signal_count=%u "
-+				"primitive_sequence_protocol_error=%u "
-+				"invalid_transmition_word=%u "
-+				"invalid_crc_count=%u)\n",
-+				port->d_id, port->adapter->s_id,
-+				rls->link_failure_count,
-+				rls->loss_of_sync_count,
-+				rls->loss_of_signal_count,
-+				rls->prim_seq_prot_error,
-+				rls->invalid_transmition_word,
-+				rls->invalid_crc_count);
-+                break;
-+
-+        case ZFCP_LS_PDISC:
-+                pdisc = (struct zfcp_ls_pdisc_acc*)send_els->resp->address;
-+                ZFCP_LOG_NORMAL(
-+				"PDISC response from did 0x%06x to sid 0x%06x "
-+				"with payload(wwpn=0x%016Lx wwnn=0x%016Lx "
-+				"vendor='%-16s')\n",
-+				port->d_id, port->adapter->s_id,
-+				(unsigned long long)pdisc->wwpn,
-+				(unsigned long long)pdisc->wwnn,
-+				pdisc->vendor_version);
-+                break;
-+
-+        case ZFCP_LS_ADISC:
-+                adisc = (struct zfcp_ls_adisc_acc*)send_els->resp->address;
-+                ZFCP_LOG_NORMAL(
-+				"ADISC response from did 0x%06x to sid 0x%06x "
-+				"with payload(wwpn=0x%016Lx wwnn=0x%016Lx "
-+				"hard_nport_id=0x%06x nport_id=0x%06x)\n",
-+				port->d_id, port->adapter->s_id,
-+				(unsigned long long)adisc->wwpn,
-+				(unsigned long long)adisc->wwnn,
-+				adisc->hard_nport_id, adisc->nport_id);
-+		if (port->wwpn != adisc->wwpn) {
-+			ZFCP_LOG_NORMAL("d_id assignment changed, reopening "
-+					"port (devno=0x%04x, wwpn=0x%016Lx, "
-+					"adisc_resp_wwpn=0x%016Lx)\n",
-+					adapter->devno,
-+                                        (unsigned long long) port->wwpn,
-+                                        (unsigned long long) adisc->wwpn);
-+			if (zfcp_erp_port_reopen(port, 0))
-+				ZFCP_LOG_NORMAL("failed reopen of port "
-+						"(devno=0x%04x, "
-+                                                "wwpn=0x%016Lx)\n",
-+						adapter->devno,
-+                                                (long long) port->wwpn);
-+                } else
-+			if (port->wwnn == 0)
-+				port->wwnn = adisc->wwnn;
-+
-+                break;
-+        }
-+
-+ out:
-+	ZFCP_FREE_PAGE((unsigned long)send_els->req->address);
-+	ZFCP_KFREE(send_els->req, sizeof(struct scatterlist));
-+	ZFCP_KFREE(send_els->resp, sizeof(struct scatterlist));
-+	ZFCP_KFREE(send_els, sizeof(struct zfcp_send_els));
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_test_link
-+ *
-+ * purpose:     Test a status of a link to a remote port using the ELS command ADISC
-+ *
-+ * returns:     0       - Link is OK
-+ *              -EPERM  - Port forced reopen failed
-+ */
-+static int zfcp_test_link(zfcp_port_t *port)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
-+
-+	int retval;
-+
-+	ZFCP_LOG_TRACE("enter (port=0x%lx)\n", (unsigned long)port);
-+
-+	retval = zfcp_els(port, ZFCP_LS_ADISC);
-+	if (retval != 0) {
-+		ZFCP_LOG_NORMAL(
-+			"Port needs to be reopened "
-+			"(wwpn=0x%016Lx devno=0x%04x)\n",
-+			(unsigned long long)port->wwpn,
-+			port->adapter->devno);
-+		retval = zfcp_erp_port_forced_reopen(port, 0);
-+		if (retval != 0) {
-+			ZFCP_LOG_NORMAL(
-+				"Cannot reopen a remote port "
-+				"(wwpn=0x%016Lx devno=0x%04x)\n",
-+				(unsigned long long)port->wwpn,
-+				port->adapter->devno);
-+			retval = -EPERM;
-+		}
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	called if a port failed to be opened normally
-+ *		initiates Forced Reopen recovery which is done
-+ *		asynchronously
-+ *
-+ * returns:	0	- initiated action succesfully
-+ *		<0	- failed to initiate action
-+ */
-+static int zfcp_erp_port_forced_reopen_internal(
-+		zfcp_port_t *port,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+	zfcp_adapter_t *adapter = port->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)port,
-+		clear_mask);
-+
-+        debug_text_event(adapter->erp_dbf,5,"pf_ro");
-+        debug_event(adapter->erp_dbf,5,&port->wwpn,
-+                    sizeof(wwn_t));
-+
-+        ZFCP_LOG_DEBUG(
-+		"Forced reopen of the port with WWPN 0x%016Lx "
-+		"on the adapter with devno 0x%04x\n",
-+		(llui_t)port->wwpn,
-+		adapter->devno);
-+
-+	zfcp_erp_port_block(port, clear_mask);
-+
-+	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
-+		ZFCP_LOG_DEBUG(
-+			"skipped forced reopen on the failed port "
-+			"with WWPN 0x%016Lx on the adapter with devno 0x%04x\n",
-+			(llui_t)port->wwpn,
-+			adapter->devno);
-+                debug_text_event(adapter->erp_dbf,5,"pf_ro_f");
-+                debug_event(adapter->erp_dbf,5,&port->wwpn,
-+                            sizeof(wwn_t));
-+		retval = -EIO;
-+		goto out;
-+	}
-+
-+	retval = zfcp_erp_action_enqueue(
-+			ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
-+			adapter,
-+			port,
-+			NULL);
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	Wrappper for zfcp_erp_port_forced_reopen_internal
-+ *              used to ensure the correct locking
-+ *
-+ * returns:	0	- initiated action succesfully
-+ *		<0	- failed to initiate action
-+ */
-+static int zfcp_erp_port_forced_reopen(
-+		zfcp_port_t *port,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        unsigned long flags;
-+        zfcp_adapter_t *adapter = port->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx clear_mask=x0%x)\n",
-+		(unsigned long)port,
-+		clear_mask);
-+
-+        write_lock_irqsave(&adapter->erp_lock, flags);
-+        retval = zfcp_erp_port_forced_reopen_internal(port, clear_mask);
-+        write_unlock_irqrestore(&adapter->erp_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	called if a port is to be opened
-+ *		initiates Reopen recovery which is done
-+ *		asynchronously
-+ *
-+ * returns:	0	- initiated action succesfully
-+ *		<0	- failed to initiate action
-+ */
-+static int zfcp_erp_port_reopen_internal(
-+		zfcp_port_t *port,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+	zfcp_adapter_t *adapter = port->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)port,
-+		clear_mask);
-+
-+        debug_text_event(adapter->erp_dbf, 5, "p_ro");
-+        debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+
-+        ZFCP_LOG_DEBUG(
-+		"Reopen of the port with WWPN 0x%016Lx "
-+		"on the adapter with devno 0x%04x\n",
-+		(llui_t)port->wwpn,
-+		adapter->devno);
-+
-+	zfcp_erp_port_block(port, clear_mask);
-+
-+	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
-+		ZFCP_LOG_DEBUG(
-+			"skipped reopen on the failed port with WWPN 0x%016Lx "
-+			"on the adapter with devno 0x%04x\n",
-+			(llui_t)port->wwpn,
-+			adapter->devno);
-+                debug_text_event(adapter->erp_dbf, 5, "p_ro_f");
-+                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+		/* ensure propagation of failed status to new devices */
-+		zfcp_erp_port_failed(port);
-+		retval = -EIO;
-+		goto out;
-+	}
-+
-+	retval = zfcp_erp_action_enqueue(
-+			ZFCP_ERP_ACTION_REOPEN_PORT,
-+			adapter,
-+			port,
-+			NULL);
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	Wrappper for zfcp_erp_port_reopen_internal
-+ *              used to ensure the correct locking
-+ *
-+ * returns:	0	- initiated action succesfully
-+ *		<0	- failed to initiate action
-+ */
-+static int zfcp_erp_port_reopen(
-+		zfcp_port_t *port,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        unsigned long flags;
-+        zfcp_adapter_t *adapter = port->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)port,
-+		clear_mask);
-+
-+        write_lock_irqsave(&adapter->erp_lock, flags);
-+        retval = zfcp_erp_port_reopen_internal(port, clear_mask);
-+        write_unlock_irqrestore(&adapter->erp_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	called if a unit is to be opened
-+ *		initiates Reopen recovery which is done
-+ *		asynchronously
-+ *
-+ * returns:	0	- initiated action succesfully
-+ *		<0	- failed to initiate action
-+ */
-+static int zfcp_erp_unit_reopen_internal(
-+		zfcp_unit_t *unit,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+	zfcp_adapter_t *adapter = unit->port->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (unit=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)unit,
-+		clear_mask);
-+
-+        debug_text_event(adapter->erp_dbf,5,"u_ro");
-+        debug_event(adapter->erp_dbf,5,&unit->fcp_lun,
-+                    sizeof(fcp_lun_t));
-+        ZFCP_LOG_DEBUG(
-+		"Reopen of the unit with FCP_LUN 0x%016Lx on the "
-+		"port with WWPN 0x%016Lx "
-+		"on the adapter with devno 0x%04x\n",
-+		(llui_t)unit->fcp_lun,
-+		(llui_t)unit->port->wwpn,
-+		adapter->devno);
-+
-+	zfcp_erp_unit_block(unit, clear_mask);
-+
-+	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
-+		ZFCP_LOG_DEBUG(
-+			"skipped reopen on the failed unit with FCP_LUN 0x%016Lx on the "
-+			"port with WWPN 0x%016Lx "
-+			"on the adapter with devno 0x%04x\n",
-+			(llui_t)unit->fcp_lun,
-+			(llui_t)unit->port->wwpn,
-+			adapter->devno);
-+                debug_text_event(adapter->erp_dbf,5,"u_ro_f");
-+                debug_event(adapter->erp_dbf,5,&unit->fcp_lun,
-+                            sizeof(fcp_lun_t));
-+		retval = -EIO;
-+		goto out;
-+	}
-+
-+	retval = zfcp_erp_action_enqueue(
-+			ZFCP_ERP_ACTION_REOPEN_UNIT,
-+			unit->port->adapter,
-+			unit->port,
-+			unit);
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	Wrappper for zfcp_erp_unit_reopen_internal
-+ *              used to ensure the correct locking
-+ *
-+ * returns:	0	- initiated action succesfully
-+ *		<0	- failed to initiate action
-+ */
-+static int zfcp_erp_unit_reopen(
-+		zfcp_unit_t *unit,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        unsigned long flags;
-+        zfcp_adapter_t *adapter = unit->port->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (unit=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)unit,
-+		clear_mask);
-+
-+        write_lock_irqsave(&adapter->erp_lock, flags);
-+        retval = zfcp_erp_unit_reopen_internal(unit, clear_mask);
-+        write_unlock_irqrestore(&adapter->erp_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	disable I/O,
-+ *		return any open requests and clean them up,
-+ *		aim: no pending and incoming I/O
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_adapter_block(zfcp_adapter_t *adapter, int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)adapter,
-+		clear_mask);
-+
-+        debug_text_event(adapter->erp_dbf,6,"a_bl");
-+
-+	zfcp_erp_modify_adapter_status(
-+                   adapter,
-+                   ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
-+                   ZFCP_CLEAR);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	enable I/O
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_adapter_unblock(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        debug_text_event(adapter->erp_dbf,6,"a_ubl");
-+	atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	disable I/O,
-+ *		return any open requests and clean them up,
-+ *		aim: no pending and incoming I/O
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_port_block(zfcp_port_t *port, int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+        zfcp_adapter_t *adapter=port->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)port,
-+		clear_mask);
-+
-+        debug_text_event(adapter->erp_dbf,6,"p_bl");
-+        debug_event(adapter->erp_dbf,6,&port->wwpn,
-+                    sizeof(wwn_t));
-+
-+	zfcp_erp_modify_port_status(
-+                   port,
-+                   ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
-+                   ZFCP_CLEAR);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	enable I/O
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_port_unblock(zfcp_port_t *port)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+        zfcp_adapter_t *adapter=port->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        debug_text_event(adapter->erp_dbf,6,"p_ubl");
-+        debug_event(adapter->erp_dbf,6,&port->wwpn,
-+                    sizeof(wwn_t));
-+	atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	disable I/O,
-+ *		return any open requests and clean them up,
-+ *		aim: no pending and incoming I/O
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_unit_block(zfcp_unit_t *unit, int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+        zfcp_adapter_t *adapter=unit->port->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (unit=0x%lx clear_mask=0x%x\n",
-+		(unsigned long)unit,
-+		clear_mask);
-+
-+        debug_text_event(adapter->erp_dbf,6,"u_bl");
-+        debug_event(adapter->erp_dbf,6,&unit->fcp_lun,
-+                    sizeof(fcp_lun_t));
-+
-+	zfcp_erp_modify_unit_status(
-+                   unit,
-+                   ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
-+                   ZFCP_CLEAR);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	enable I/O
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_unit_unblock(zfcp_unit_t *unit)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+        zfcp_adapter_t *adapter=unit->port->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        debug_text_event(adapter->erp_dbf,6,"u_ubl");
-+        debug_event(adapter->erp_dbf,6,&unit->fcp_lun,
-+                    sizeof(fcp_lun_t));
-+	atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_action_ready(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (erp_action=0x%lx)\n",
-+		(unsigned long)erp_action);
-+
-+        debug_text_event(adapter->erp_dbf, 4, "a_ar");
-+        debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof(int));
-+
-+	zfcp_erp_action_to_ready(erp_action);
-+	ZFCP_LOG_DEBUG(
-+		"Waking erp_thread of adapter with devno 0x%04x\n",
-+		adapter->devno);
-+	up(&adapter->erp_ready_sem);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:
-+ *
-+ * returns:	<0			erp_action not found in any list
-+ *		ZFCP_ERP_ACTION_READY	erp_action is in ready list
-+ *		ZFCP_ERP_ACTION_RUNNING	erp_action is in running list
-+ *
-+ * locks:	erp_lock must be held
-+ */
-+static int zfcp_erp_action_exists(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = -EINVAL;
-+	struct list_head *entry;
-+	zfcp_erp_action_t *entry_erp_action;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (erp_action=0x%lx)\n",
-+		(unsigned long)erp_action);
-+
-+	/* search in running list */
-+	list_for_each(entry, &adapter->erp_running_head) {
-+		entry_erp_action = list_entry(entry, zfcp_erp_action_t, list);
-+		if (entry_erp_action == erp_action) {
-+			retval = ZFCP_ERP_ACTION_RUNNING;
-+			goto out;
-+		}
-+	}
-+	/* search in ready list */
-+	list_for_each(entry, &adapter->erp_ready_head) {
-+		entry_erp_action = list_entry(entry, zfcp_erp_action_t, list);
-+		if (entry_erp_action == erp_action) {
-+			retval = ZFCP_ERP_ACTION_READY;
-+			goto out;
-+		}
-+	}
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * purpose:	checks current status of action (timed out, dismissed, ...)
-+ *		and does appropriate preparations (dismiss fsf request, ...)
-+ *
-+ * locks:	called under erp_lock (disabled interrupts)
-+ *
-+ * returns:	0
-+ */
-+static int
-+zfcp_erp_strategy_check_fsfreq(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	zfcp_fsf_req_t *fsf_req;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	if (erp_action->fsf_req) {
-+		/* take lock to ensure that request is not being deleted meanwhile */
-+		write_lock(&adapter->fsf_req_list_lock);
-+		/* check whether fsf req does still exist */
-+		list_for_each_entry(fsf_req, &adapter->fsf_req_list_head, list)
-+		    if (fsf_req == erp_action->fsf_req)
-+			break;
-+		if (fsf_req == erp_action->fsf_req) {
-+			/* fsf_req still exists */
-+			debug_text_event(adapter->erp_dbf, 3, "a_ca_req");
-+			debug_event(adapter->erp_dbf, 3, &fsf_req,
-+				    sizeof (unsigned long));
-+			/* dismiss fsf_req of timed out or dismissed erp_action */
-+			if (erp_action->status & (ZFCP_STATUS_ERP_DISMISSED |
-+						  ZFCP_STATUS_ERP_TIMEDOUT)) {
-+				debug_text_event(adapter->erp_dbf, 3,
-+						 "a_ca_disreq");
-+				fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
-+			}
-+			if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
-+				ZFCP_LOG_NORMAL("error: erp step timed out "
-+						"(action=%d, fsf_req=%p)\n ",
-+						erp_action->action,
-+						erp_action->fsf_req);
-+			}
-+			/*
-+			 * If fsf_req is neither dismissed nor completed
-+			 * then keep it running asynchronously and don't mess
-+			 * with the association of erp_action and fsf_req.
-+			 */
-+			if (fsf_req->status & (ZFCP_STATUS_FSFREQ_COMPLETED |
-+					       ZFCP_STATUS_FSFREQ_DISMISSED)) {
-+				/* forget about association between fsf_req
-+				   and erp_action */
-+				fsf_req->erp_action = NULL;
-+				erp_action->fsf_req = NULL;
-+			}
-+		} else {
-+			debug_text_event(adapter->erp_dbf, 3, "a_ca_gonereq");
-+			/*
-+			 * even if this fsf_req has gone, forget about
-+			 * association between erp_action and fsf_req
-+			 */
-+			erp_action->fsf_req = NULL;
-+		}
-+		write_unlock(&adapter->fsf_req_list_lock);
-+	} else
-+		debug_text_event(adapter->erp_dbf, 3, "a_ca_noreq");
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * purpose:	generic handler for asynchronous events related to erp_action events
-+ *		(normal completion, time-out, dismissing, retry after
-+ *		low memory condition)
-+ *
-+ * note:	deletion of timer is not required (e.g. in case of a time-out),
-+ *		but a second try does no harm,
-+ *		we leave it in here to allow for greater simplification
-+ *
-+ * returns:	0 - there was an action to handle
-+ *		!0 - otherwise
-+ */
-+static int
-+zfcp_erp_async_handler_nolock(zfcp_erp_action_t *erp_action,
-+			      unsigned long set_mask)
-+{
-+	int retval;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
-+		debug_text_event(adapter->erp_dbf, 2, "a_asyh_ex");
-+		debug_event(adapter->erp_dbf, 2, &erp_action->action,
-+			    sizeof (int));
-+		if (!(set_mask & ZFCP_STATUS_ERP_TIMEDOUT))
-+			del_timer_sync(&erp_action->timer);
-+		erp_action->status |= set_mask;
-+		zfcp_erp_action_ready(erp_action);
-+		retval = 0;
-+	} else {
-+		/* action is ready or gone - nothing to do */
-+		debug_text_event(adapter->erp_dbf, 3, "a_asyh_gone");
-+		debug_event(adapter->erp_dbf, 3, &erp_action->action,
-+			    sizeof (int));
-+		retval = 1;
-+	}
-+
-+	return retval;
-+}
-+
-+/*
-+ * purpose:	generic handler for asynchronous events related to erp_action
-+ *               events	(normal completion, time-out, dismissing, retry after
-+ *		low memory condition)
-+ *
-+ * note:	deletion of timer is not required (e.g. in case of a time-out),
-+ *		but a second try does no harm,
-+ *		we leave it in here to allow for greater simplification
-+ *
-+ * returns:	0 - there was an action to handle
-+ *		!0 - otherwise
-+ */
-+static int
-+zfcp_erp_async_handler(zfcp_erp_action_t *erp_action,
-+		       unsigned long set_mask)
-+{
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+	unsigned long flags;
-+	int retval;
-+
-+	write_lock_irqsave(&adapter->erp_lock, flags);
-+	retval = zfcp_erp_async_handler_nolock(erp_action, set_mask);
-+	write_unlock_irqrestore(&adapter->erp_lock, flags);
-+
-+	return retval;
-+}
-+
-+/*
-+ * purpose:	is called for erp_action which was slept waiting for
-+ *		memory becoming avaliable,
-+ *		will trigger that this action will be continued
-+ */
-+static void
-+zfcp_erp_memwait_handler(unsigned long data)
-+{
-+	zfcp_erp_action_t *erp_action = (zfcp_erp_action_t *) data;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	debug_text_event(adapter->erp_dbf, 2, "a_mwh");
-+	debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
-+
-+	zfcp_erp_async_handler(erp_action, 0);
-+}
-+
-+/*
-+ * purpose:	is called if an asynchronous erp step timed out,
-+ *		action gets an appropriate flag and will be processed
-+ *		accordingly
-+ */
-+static void
-+zfcp_erp_timeout_handler(unsigned long data)
-+{
-+	zfcp_erp_action_t *erp_action = (zfcp_erp_action_t *) data;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	debug_text_event(adapter->erp_dbf, 2, "a_th");
-+	debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
-+
-+	zfcp_erp_async_handler(erp_action, ZFCP_STATUS_ERP_TIMEDOUT);
-+}
-+
-+/*
-+ * purpose:	is called for an erp_action which needs to be ended
-+ *		though not being done,
-+ *		this is usually required if an higher is generated,
-+ *		action gets an appropriate flag and will be processed
-+ *		accordingly
-+ *
-+ * locks:	erp_lock held (thus we need to call another handler variant)
-+ */
-+static int
-+zfcp_erp_action_dismiss(zfcp_erp_action_t *erp_action)
-+{
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	debug_text_event(adapter->erp_dbf, 2, "a_adis");
-+	debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
-+
-+	zfcp_erp_async_handler_nolock(erp_action, ZFCP_STATUS_ERP_DISMISSED);
-+
-+	return 0;
-+}
-+
-+
-+static int zfcp_erp_thread_setup(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+        int retval = 0;
-+        struct tq_struct *erp_task = NULL;
-+
-+	ZFCP_LOG_TRACE(
-+               "enter (adapter=0x%lx)\n",
-+               (unsigned long)adapter);
-+
-+        erp_task = ZFCP_KMALLOC(sizeof(struct tq_struct), GFP_KERNEL); 
-+        if (!erp_task) {
-+                ZFCP_LOG_INFO(
-+			"error: Not enough memory for the error handler "
-+			"of the adapter with devno 0x%04x, leaving.\n",
-+			adapter->devno);
-+                retval = -ENOMEM;
-+                goto out;
-+        }
-+
-+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_DONE, &adapter->status);
-+
-+	rwlock_init(&adapter->erp_lock);
-+	INIT_LIST_HEAD(&adapter->erp_ready_head);
-+	INIT_LIST_HEAD(&adapter->erp_running_head);
-+	sema_init(&adapter->erp_ready_sem, 0);
-+#ifdef ZFCP_ERP_DEBUG_SINGLE_STEP
-+	sema_init(&adapter->erp_continue_sem, 0);
-+#endif // ZFCP_ERP_DEBUG_SINGLE_STEP
-+
-+        INIT_TQUEUE(erp_task, 
-+                    zfcp_erp_thread_setup_task, 
-+                    adapter); 
-+        schedule_task(erp_task);
-+
-+	__wait_event_interruptible(
-+		adapter->erp_thread_wqh,
-+		atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_DONE, &adapter->status),
-+		retval);
-+
-+	if (retval) {
-+		ZFCP_LOG_INFO(
-+			"error: The error recovery procedure thread creation "
-+			"for the adapter with devno 0x%04x was aborted. An "
-+                        "OS signal was receiveved.\n",
-+			adapter->devno);
-+		if (atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, 
-+                                     &adapter->status)) {
-+			zfcp_erp_thread_kill(adapter);
-+                }
-+	}
-+
-+	ZFCP_KFREE(erp_task, sizeof(*erp_task));
-+ 
-+        debug_text_event(adapter->erp_dbf, 5, "a_thset_ok");
-+out:
-+	ZFCP_LOG_TRACE("exit %d\n", retval);
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+static void zfcp_erp_thread_setup_task(void *data)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	zfcp_adapter_t *adapter = (zfcp_adapter_t *)data;
-+	int retval;
-+
-+	ZFCP_LOG_TRACE(
-+               "enter (data=0x%lx)\n",
-+               (unsigned long)data);
-+
-+	retval = kernel_thread(zfcp_erp_thread, adapter, SIGCHLD);
-+	if (retval < 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Out of resources. Could not create an "
-+                        "error recovery procedure  thread "
-+			"for the adapter with devno 0x%04x\n",
-+			adapter->devno);
-+		atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_DONE, &adapter->status);
-+		wake_up_interruptible(&adapter->erp_thread_wqh);
-+	} else	{
-+		ZFCP_LOG_DEBUG(
-+			"created erp thread "
-+			"for the adapter with devno 0x%04x\n",
-+			adapter->devno);
-+                debug_text_event(adapter->erp_dbf,5,"a_thset_t_ok");
-+	}
-+		
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+	return;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ *
-+ * context:	process (i.e. proc-fs or rmmod/insmod)
-+ *
-+ * note:	The caller of this routine ensures that the specified
-+ *		adapter has been shut down and that this operation
-+ *		has been completed. Thus, there are no pending erp_actions
-+ *		which would need to be handled here.
-+ */
-+static int zfcp_erp_thread_kill(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+               "enter (adapter=0x%lx)\n",
-+               (unsigned long)adapter);
-+
-+	ZFCP_LOG_DEBUG(
-+		"Killing erp_thread for the adapter with devno 0x%04x\n",
-+		adapter->devno);
-+	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
-+	up(&adapter->erp_ready_sem);
-+	wait_event(
-+		adapter->erp_thread_wqh,
-+		!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status));
-+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
-+
-+        debug_text_event(adapter->erp_dbf,5,"a_thki_ok");
-+
-+	ZFCP_LOG_DEBUG(
-+		"Killed erp_thread for the adapter with devno 0x%04x\n",
-+		adapter->devno);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	should be run as a thread,
-+ *		goes through list of error recovery actions of associated adapter
-+ *		and delegates single action to execution
-+ *
-+ * returns:
-+ */
-+/* FIXME(design): static or not? */
-+static int zfcp_erp_thread(void *data)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	zfcp_adapter_t *adapter = (zfcp_adapter_t*)data;
-+	struct list_head *next;
-+	zfcp_erp_action_t *erp_action;
-+	unsigned long flags;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (data=0x%lx)\n",
-+		(unsigned long)data);
-+
-+	__sti();
-+	daemonize();
-+        /* disable all signals */
-+	siginitsetinv(&current->blocked, 0);
-+
-+	sprintf(current->comm, "zfcp_erp_0x%04x", adapter->devno);
-+
-+	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
-+	ZFCP_LOG_DEBUG(
-+		"erp thread for adapter with devno 0x%04x is up.\n",
-+		adapter->devno);
-+        debug_text_event(adapter->erp_dbf, 5, "a_th_run");
-+	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_DONE, &adapter->status);
-+	wake_up_interruptible(&adapter->erp_thread_wqh);
-+
-+	/* (nearly) infinite loop */
-+	for (;;) {
-+		/* sleep as long as there is no action in 'ready' queue */
-+		down_interruptible(&adapter->erp_ready_sem);
-+#ifdef ZFCP_ERP_DEBUG_SINGLE_STEP
-+		down(&adapter->erp_continue_sem);
-+#endif // ZFCP_ERP_DEBUG_SINGLE_STEP
-+		ZFCP_LOG_TRACE(
-+			"erp thread woken on adapter with devno 0x%04x\n",
-+			adapter->devno);
-+
-+		/* killing this thread */
-+		if (atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status)) {
-+                        debug_text_event(adapter->erp_dbf,5,"a_th_kill");
-+			ZFCP_LOG_DEBUG(
-+				"Recognized kill flag for the erp_thread of "
-+				"the adapter with devno 0x%04x\n",
-+				adapter->devno);
-+			read_lock_irqsave(&adapter->erp_lock, flags);
-+			retval = !list_empty(&adapter->erp_ready_head) ||
-+				 !list_empty(&adapter->erp_running_head);
-+			read_unlock_irqrestore(&adapter->erp_lock, flags);
-+			if (retval) {
-+				debug_text_exception(adapter->erp_dbf, 1, "a_th_bkill");
-+				ZFCP_LOG_NORMAL(
-+					"bug: error recovery thread is "
-+					"shutting down although there are "
-+					"error recovery actions pending at "
-+					"adapter with devno 0x%04x\n",
-+					adapter->devno);
-+				/* don't exit erp to avoid potential system crash */
-+			} else	break;
-+		}
-+
-+		ZFCP_PARANOIA {
-+			/* there should be something in 'ready' queue */
-+			/*
-+			 * need lock since list_empty checks for entry at
-+			 * lists head while lists head is subject to
-+			 * modification when another action is put to this
-+			 * queue (only list tail won't be modified then)
-+			 */
-+			read_lock_irqsave(&adapter->erp_lock, flags);
-+			retval = list_empty(&adapter->erp_ready_head);
-+			read_unlock_irqrestore(&adapter->erp_lock, flags);
-+			if (retval) {
-+                                debug_text_exception(adapter->erp_dbf, 1, "a_th_empt");
-+				ZFCP_LOG_NORMAL(
-+					"bug: Error recovery procedure thread "
-+                                        "woken for empty action list on the "
-+					"adapter with devno 0x%04x.\n",
-+					adapter->devno);
-+				/* sleep until next try */
-+				continue;
-+			}
-+		}
-+
-+		/*
-+		 * get next action to be executed; FIFO -> head.prev
-+		 * (don't need lock since there is an action at lists tail and
-+		 * lists tail won't be modified concurrently; only lists head
-+		 * would be modified if another action is put to this queue)
-+		 */
-+		next = adapter->erp_ready_head.prev;
-+		erp_action = list_entry(next, zfcp_erp_action_t, list);
-+		/* process action (incl. [re]moving it from 'ready' queue) */
-+		retval = zfcp_erp_strategy(erp_action);
-+	}
-+
-+	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
-+	ZFCP_LOG_DEBUG(
-+		"erp thread for adapter with devno 0x%04x is down.\n",
-+		adapter->devno);
-+
-+	wake_up(&adapter->erp_thread_wqh);
-+
-+        debug_text_event(adapter->erp_dbf, 5, "a_th_stop");
-+
-+	ZFCP_LOG_TRACE("exit %d\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	drives single error recovery action and schedules higher and
-+ *		subordinate actions, if necessary
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
-+ *		ZFCP_ERP_SUCCEEDED	- action finished successfully (action dequeued)
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully (action dequeued)
-+ *		ZFCP_ERP_EXIT		- action finished (action dequeued), target offline
-+ *		ZFCP_ERP_DISMISSED	- action canceled (action dequeued)
-+ */
-+static int zfcp_erp_strategy(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+	zfcp_port_t *port = erp_action->port;
-+	zfcp_unit_t *unit = erp_action->unit;
-+	int action = erp_action->action;
-+	u32 status = erp_action->status;
-+	unsigned long flags;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (erp_action=0x%lx)\n",
-+		(unsigned long)erp_action);
-+
-+	write_lock_irqsave(&adapter->erp_lock, flags);
-+	/* dequeue dismissed action and leave, if required */
-+	retval = zfcp_erp_strategy_check_action(erp_action, retval);
-+	if (retval == ZFCP_ERP_DISMISSED) {
-+                debug_text_event(adapter->erp_dbf, 4, "a_st_dis1");
-+		goto unlock;
-+        }
-+
-+	/*
-+	 * move action to 'running' queue before processing it
-+	 * (to avoid a race condition regarding moving the
-+	 * action to the 'running' queue and back)
-+	 */
-+	zfcp_erp_action_to_running(erp_action);
-+
-+	write_unlock_irqrestore(&adapter->erp_lock, flags);
-+	retval = zfcp_erp_strategy_do_action(erp_action);
-+	write_lock_irqsave(&adapter->erp_lock, flags);
-+
-+	/*
-+	 * check for dismissed status again to avoid follow-up actions,
-+	 * failing of targets and so on for dismissed actions
-+	 */
-+	retval = zfcp_erp_strategy_check_action(erp_action, retval);
-+
-+	switch (retval) {
-+	case ZFCP_ERP_DISMISSED:
-+		/* leave since this action has ridden to its ancestors */
-+		debug_text_event(adapter->erp_dbf, 6, "a_st_dis2");
-+		goto unlock;
-+	case ZFCP_ERP_NOMEM :
-+		/* no memory to continue immediately, let it sleep */
-+		debug_text_event(adapter->erp_dbf, 2, "a_st_memw");
-+		retval = zfcp_erp_strategy_memwait(erp_action);
-+		goto unlock;
-+	case ZFCP_ERP_CONTINUES :
-+		/* leave since this action runs asynchronously */
-+		debug_text_event(adapter->erp_dbf, 6, "a_st_cont");
-+		goto unlock;
-+        }
-+
-+	/* ok, finished action (whatever its result is) */
-+
-+	/* check for unrecoverable targets */
-+	retval = zfcp_erp_strategy_check_target(erp_action, retval);
-+
-+	/* action must be dequeued (here to allow for further ones) */
-+	zfcp_erp_action_dequeue(erp_action);
-+
-+	/*
-+	 * put this target through the erp mill again if someone has
-+	 * requested to change the status of a target being online 
-+	 * to offline or the other way around
-+	 * (old retval is preserved if nothing has to be done here)
-+	 */
-+	retval = zfcp_erp_strategy_statechange(
-+			action, status, adapter, port, unit, retval);
-+
-+	/*
-+	 * leave if target is in permanent error state or if
-+	 * action is repeated in order to process state change
-+	 */
-+	if (retval == ZFCP_ERP_EXIT) {
-+                debug_text_event(adapter->erp_dbf, 2, "a_st_exit");
-+                goto unlock;
-+        }
-+
-+	/* trigger follow up actions */
-+	zfcp_erp_strategy_followup_actions(
-+		action, adapter, port, unit, retval);
-+
-+unlock:
-+	write_unlock_irqrestore(&adapter->erp_lock, flags);
-+
-+	/*
-+	 * 2 things we want to check finally if the erp queues will be empty
-+	 * (don't do that if the last action evaluated was dismissed
-+	 * since this clearly indicates that there is more to come) :
-+	 * - close the name server port if it is open yet (enqueues another final action)
-+	 * - otherwise, wake up whoever wants to be woken when we are done with erp
-+	 */
-+	if (retval != ZFCP_ERP_DISMISSED)
-+		zfcp_erp_strategy_check_queues(adapter);
-+
-+        debug_text_event(adapter->erp_dbf, 6, "a_st_done");
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:	ZFCP_ERP_DISMISSED	- if action has been dismissed
-+ *		retval			- otherwise
-+ */
-+static int
-+zfcp_erp_strategy_check_action(zfcp_erp_action_t *erp_action, int retval)
-+{
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	zfcp_erp_strategy_check_fsfreq(erp_action);
-+
-+	debug_event(adapter->erp_dbf, 5, &erp_action->action, sizeof (int));
-+	if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
-+		debug_text_event(adapter->erp_dbf, 3, "a_stcd_dis");
-+		zfcp_erp_action_dequeue(erp_action);
-+		retval = ZFCP_ERP_DISMISSED;
-+	} else
-+		debug_text_event(adapter->erp_dbf, 5, "a_stcd_nodis");
-+
-+	return retval;
-+}
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_strategy_do_action(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = ZFCP_ERP_FAILED;
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (erp_action=0x%lx)\n",
-+		(unsigned long)erp_action);
-+	/*
-+	 * no lock in subsequent stratetgy routines
-+	 * (this allows these routine to call schedule, e.g.
-+	 * kmalloc with such flags or qdio_initialize & friends)
-+	 */
-+
-+	if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
-+		/* DEBUG */
-+		//unsigned long timeout = 1000 * HZ;
-+       
-+		debug_text_event(adapter->erp_dbf, 3, "a_stda_tim");
-+		debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof(int));
-+	
-+		/* DEBUG */
-+		//__ZFCP_WAIT_EVENT_TIMEOUT(timeout, 0);
-+        }
-+        /* Note: in case of timeout, the seperate strategies will fail
-+           anyhow. No need for a special action. Even worse, a nameserver
-+           failure would not wake up waiting ports without the call.
-+        */ 
-+        /* try to execute/continue action as far as possible */
-+        switch (erp_action->action) {
-+        case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
-+                retval = zfcp_erp_adapter_strategy(erp_action);
-+                break;
-+        case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
-+                retval = zfcp_erp_port_forced_strategy(erp_action);
-+                break;
-+        case ZFCP_ERP_ACTION_REOPEN_PORT :
-+                retval = zfcp_erp_port_strategy(erp_action);
-+                break;
-+        case ZFCP_ERP_ACTION_REOPEN_UNIT :
-+                retval = zfcp_erp_unit_strategy(erp_action);
-+                break;
-+        default :
-+                debug_text_exception(adapter->erp_dbf, 1, "a_stda_bug");
-+                debug_event(adapter->erp_dbf, 1, &erp_action->action, sizeof(int));
-+                ZFCP_LOG_NORMAL("bug: Unknown error recovery procedure "
-+                                "action requested on the adapter with "
-+                                "devno 0x%04x (debug info %d)\n",
-+                                erp_action->adapter->devno,
-+                                erp_action->action);
-+        }
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	triggers retry of this action after a certain amount of time
-+ *		by means of timer provided by erp_action
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES - erp_action sleeps in erp running queue
-+ */
-+static int zfcp_erp_strategy_memwait(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = ZFCP_ERP_CONTINUES;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (erp_action=0x%lx)\n",
-+		(unsigned long)erp_action);
-+
-+        debug_text_event(adapter->erp_dbf, 6, "a_mwinit");
-+        debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof(int));
-+	init_timer(&erp_action->timer);
-+	erp_action->timer.function = zfcp_erp_memwait_handler;
-+	erp_action->timer.data = (unsigned long)erp_action;
-+	erp_action->timer.expires = jiffies + ZFCP_ERP_MEMWAIT_TIMEOUT;
-+	add_timer(&erp_action->timer);
-+	
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/* 
-+ * function:    zfcp_erp_adapter_failed
-+ *
-+ * purpose:     sets the adapter and all underlying devices to ERP_FAILED
-+ *
-+ */
-+static void zfcp_erp_adapter_failed(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
-+
-+
-+        ZFCP_LOG_TRACE(
-+                "enter (adapter=0x%lx)\n",
-+                (unsigned long)adapter);
-+
-+        zfcp_erp_modify_adapter_status(adapter,
-+                                       ZFCP_STATUS_COMMON_ERP_FAILED,
-+                                       ZFCP_SET);
-+        ZFCP_LOG_NORMAL(
-+                "Adapter recovery failed on the "
-+                "adapter with devno 0x%04x.\n",
-+                adapter->devno);
-+        debug_text_event(adapter->erp_dbf, 2, "a_afail");
-+
-+
-+        ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/* 
-+ * function:    zfcp_erp_port_failed
-+ *
-+ * purpose:     sets the port and all underlying devices to ERP_FAILED
-+ *
-+ */
-+static void zfcp_erp_port_failed(zfcp_port_t *port)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
-+
-+
-+        ZFCP_LOG_TRACE("enter (port=0x%lx)\n",
-+                       (unsigned long)port);
-+        
-+        zfcp_erp_modify_port_status(port,
-+                                    ZFCP_STATUS_COMMON_ERP_FAILED,
-+                                    ZFCP_SET);
-+        ZFCP_LOG_NORMAL("Port recovery failed on the "
-+                        "port with WWPN 0x%016Lx at the "
-+                        "adapter with devno 0x%04x.\n",
-+                        (llui_t)port->wwpn,
-+                        port->adapter->devno);
-+        debug_text_event(port->adapter->erp_dbf, 2, "p_pfail");
-+        debug_event(port->adapter->erp_dbf, 2, &port->wwpn, sizeof(wwn_t));
-+        
-+        ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/* 
-+ * function:    zfcp_erp_unit_failed
-+ *
-+ * purpose:     sets the unit to ERP_FAILED
-+ *
-+ */
-+static void zfcp_erp_unit_failed(zfcp_unit_t *unit)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
-+
-+
-+        ZFCP_LOG_TRACE(
-+                "enter (unit=0x%lx)\n",
-+                (unsigned long)unit);
-+
-+        zfcp_erp_modify_unit_status(unit,
-+                                    ZFCP_STATUS_COMMON_ERP_FAILED,
-+                                    ZFCP_SET);
-+        ZFCP_LOG_NORMAL(
-+                "Unit recovery failed on the unit with FCP_LUN 0x%016Lx "
-+                "connected to the port with WWPN 0x%016Lx at the "
-+                "adapter with devno 0x%04x.\n",
-+                (llui_t)unit->fcp_lun,
-+                (llui_t)unit->port->wwpn,
-+                unit->port->adapter->devno);
-+        debug_text_event(unit->port->adapter->erp_dbf, 2, "u_ufail");
-+        debug_event(unit->port->adapter->erp_dbf, 2, 
-+                    &unit->fcp_lun, sizeof(fcp_lun_t));
-+
-+        ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_erp_strategy_check_target
-+ *
-+ * purpose:	increments the erp action count on the device currently in recovery if
-+ *              the action failed or resets the count in case of success. If a maximum
-+ *              count is exceeded the device is marked as ERP_FAILED.
-+ *		The 'blocked' state of a target which has been recovered successfully is reset.
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (not considered)
-+ *		ZFCP_ERP_SUCCEEDED	- action finished successfully 
-+ *		ZFCP_ERP_EXIT		- action failed and will not continue
-+ */
-+static int zfcp_erp_strategy_check_target(
-+		zfcp_erp_action_t *erp_action, 
-+		int result)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+	zfcp_port_t *port = erp_action->port;
-+	zfcp_unit_t *unit = erp_action->unit;
-+
-+        ZFCP_LOG_TRACE(
-+		"enter (erp_action=0x%lx, result=%d)\n",
-+		(unsigned long)erp_action,
-+		result);
-+        
-+        debug_text_event(adapter->erp_dbf, 5, "a_stct_norm");
-+        debug_event(adapter->erp_dbf, 5, &erp_action->action, sizeof(int));
-+        debug_event(adapter->erp_dbf, 5, &result, sizeof(int));
-+
-+	switch (erp_action->action) {
-+
-+		case ZFCP_ERP_ACTION_REOPEN_UNIT :
-+			result = zfcp_erp_strategy_check_unit(unit, result);
-+			break;
-+
-+		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
-+		case ZFCP_ERP_ACTION_REOPEN_PORT :
-+			result = zfcp_erp_strategy_check_port(port, result);
-+			break;
-+
-+		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
-+			result = zfcp_erp_strategy_check_adapter(adapter, result);
-+			break;
-+	}
-+        
-+	ZFCP_LOG_TRACE("exit (%d)\n", result);
-+        
-+	return result;
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_strategy_statechange(
-+		int action,
-+		u32 status,
-+                zfcp_adapter_t *adapter,
-+                zfcp_port_t *port,
-+                zfcp_unit_t *unit,
-+                int retval)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (action=%d status=0x%x adapter=0x%lx port=0x%lx "
-+		"unit=0x%lx retval=0x%x)\n",
-+		action,
-+		status,
-+		(unsigned long)adapter,
-+		(unsigned long)port,
-+		(unsigned long)unit,
-+		retval);
-+        debug_text_event(adapter->erp_dbf, 5, "a_stsc");
-+        debug_event(adapter->erp_dbf, 5, &action, sizeof(int));
-+
-+	switch (action) {
-+
-+		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
-+			if (zfcp_erp_strategy_statechange_detected(&adapter->status, status)) {
-+				zfcp_erp_adapter_reopen_internal(
-+					adapter,
-+					ZFCP_STATUS_COMMON_ERP_FAILED);
-+				retval = ZFCP_ERP_EXIT;
-+			}
-+			break;
-+
-+		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
-+		case ZFCP_ERP_ACTION_REOPEN_PORT :
-+			if (zfcp_erp_strategy_statechange_detected(&port->status, status)) {
-+				zfcp_erp_port_reopen_internal(
-+					port,
-+					ZFCP_STATUS_COMMON_ERP_FAILED);
-+				retval = ZFCP_ERP_EXIT;
-+			}
-+			break;
-+
-+		case ZFCP_ERP_ACTION_REOPEN_UNIT :
-+			if (zfcp_erp_strategy_statechange_detected(&unit->status, status)) {
-+				zfcp_erp_unit_reopen_internal(
-+					unit,
-+					ZFCP_STATUS_COMMON_ERP_FAILED);
-+				retval = ZFCP_ERP_EXIT;
-+			}
-+			break;
-+
-+		default :
-+			debug_text_exception(adapter->erp_dbf, 1, "a_stsc_bug");
-+			debug_event(adapter->erp_dbf, 1, &action, sizeof(int));
-+			ZFCP_LOG_NORMAL(
-+				"bug: Unknown error recovery procedure "
-+				"action requested on the adapter with "
-+				"devno 0x%04x (debug info %d)\n",
-+				adapter->devno,
-+				action);
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static inline int zfcp_erp_strategy_statechange_detected(atomic_t *target_status, u32 erp_status)
-+{
-+	return
-+		/* take it online */
-+		(atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
-+		 (ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status)) ||
-+		/* take it offline */
-+		(!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
-+		 !(ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status));
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_strategy_check_unit(zfcp_unit_t *unit, int result)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (unit=0x%lx result=%d)\n",
-+		(unsigned long)unit,
-+		result);
-+
-+	debug_text_event(unit->port->adapter->erp_dbf, 5, "u_stct");
-+	debug_event(unit->port->adapter->erp_dbf, 5, &unit->fcp_lun, sizeof(fcp_lun_t));
-+
-+	switch (result) {
-+	case ZFCP_ERP_SUCCEEDED :
-+		atomic_set(&unit->erp_counter, 0);
-+		zfcp_erp_unit_unblock(unit);
-+		break;
-+	case ZFCP_ERP_FAILED :
-+		atomic_inc(&unit->erp_counter);
-+		if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS)
-+			zfcp_erp_unit_failed(unit);
-+		break;
-+	case ZFCP_ERP_EXIT :
-+		/* nothing */
-+		break;
-+	}
-+
-+	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
-+		zfcp_erp_unit_block(unit, 0); /* for ZFCP_ERP_SUCCEEDED */
-+		result = ZFCP_ERP_EXIT;
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", result);
-+
-+	return result;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_strategy_check_port(zfcp_port_t *port, int result)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx result=%d\n",
-+		(unsigned long)port,
-+		result);
-+
-+	debug_text_event(port->adapter->erp_dbf, 5, "p_stct");
-+	debug_event(port->adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+
-+	switch (result) {
-+	case ZFCP_ERP_SUCCEEDED :
-+		atomic_set(&port->erp_counter, 0);
-+		zfcp_erp_port_unblock(port);
-+		break;
-+	case ZFCP_ERP_FAILED :
-+		atomic_inc(&port->erp_counter);
-+		if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS)
-+			zfcp_erp_port_failed(port);
-+		break;
-+	case ZFCP_ERP_EXIT :
-+		/* nothing */
-+		break;
-+	}
-+
-+	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
-+		zfcp_erp_port_block(port, 0); /* for ZFCP_ERP_SUCCEEDED */
-+		result = ZFCP_ERP_EXIT;
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", result);
-+
-+	return result;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_strategy_check_adapter(zfcp_adapter_t *adapter, int result)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx result=%d)\n",
-+		(unsigned long)adapter,
-+		result);
-+
-+	debug_text_event(adapter->erp_dbf, 5, "a_stct");
-+
-+	switch (result) {
-+	case ZFCP_ERP_SUCCEEDED :
-+		atomic_set(&adapter->erp_counter, 0);
-+		zfcp_erp_adapter_unblock(adapter);
-+		break;
-+	case ZFCP_ERP_FAILED :
-+		atomic_inc(&adapter->erp_counter);
-+		if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS)
-+			zfcp_erp_adapter_failed(adapter);
-+		break;
-+	case ZFCP_ERP_EXIT :
-+		/* nothing */
-+		break;
-+	}
-+
-+	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
-+		zfcp_erp_adapter_block(adapter, 0); /* for ZFCP_ERP_SUCCEEDED */
-+		result = ZFCP_ERP_EXIT;
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", result);
-+
-+	return result;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	remaining things in good cases,
-+ *		escalation in bad cases
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_strategy_followup_actions(
-+		int action,
-+		zfcp_adapter_t *adapter,
-+		zfcp_port_t *port,
-+		zfcp_unit_t *unit,
-+		int status)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (action=%d adapter=0x%lx port=0x%lx "
-+		"unit=0x%lx status=0x%x)\n",
-+		action,
-+		(unsigned long)adapter,
-+		(unsigned long)port,
-+		(unsigned long)unit,
-+		status);
-+        debug_text_event(adapter->erp_dbf, 5, "a_stfol");
-+        debug_event(adapter->erp_dbf, 5, &action, sizeof(int));
-+
-+	/* initiate follow-up actions depending on success of finished action */
-+	switch (action) {
-+
-+		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
-+			if (status == ZFCP_ERP_SUCCEEDED)
-+				zfcp_erp_port_reopen_all_internal(adapter, 0);
-+			else	zfcp_erp_adapter_reopen_internal(adapter, 0);
-+			break;
-+
-+		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
-+			if (status == ZFCP_ERP_SUCCEEDED)
-+				zfcp_erp_port_reopen_internal(port, 0);
-+			else	zfcp_erp_adapter_reopen_internal(adapter, 0);
-+			break;
-+
-+		case ZFCP_ERP_ACTION_REOPEN_PORT :
-+			if (status == ZFCP_ERP_SUCCEEDED)
-+				zfcp_erp_unit_reopen_all_internal(port, 0);
-+			else	zfcp_erp_port_forced_reopen_internal(port, 0);
-+			break;
-+		
-+		case ZFCP_ERP_ACTION_REOPEN_UNIT :
-+			if (status == ZFCP_ERP_SUCCEEDED)
-+				;/* no further action */
-+			else	zfcp_erp_port_reopen_internal(unit->port, 0);
-+			break;
-+
-+		default :
-+			debug_text_exception(adapter->erp_dbf, 1, "a_stda_bug");
-+			debug_event(adapter->erp_dbf, 1, &action, sizeof(int));
-+			ZFCP_LOG_NORMAL(
-+				"bug: Unknown error recovery procedure "
-+				"action requested on the adapter with "
-+				"devno 0x%04x (debug info %d)\n",
-+				adapter->devno,
-+				action);
-+	}
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+	return 0;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/**
-+ * FIXME: document
-+ */
-+static int zfcp_erp_strategy_check_queues(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	unsigned long flags;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	read_lock_irqsave(&adapter->erp_lock, flags);
-+	if (list_empty(&adapter->erp_ready_head) &&
-+	    list_empty(&adapter->erp_running_head)) {
-+                debug_text_event(adapter->erp_dbf, 4, "a_cq_wake");
-+                atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
-+                                  &adapter->status);
-+                wake_up(&adapter->erp_done_wqh);
-+	} else	debug_text_event(adapter->erp_dbf, 5, "a_cq_notempty");
-+	read_unlock_irqrestore(&adapter->erp_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+int zfcp_erp_wait(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	wait_event(
-+		adapter->erp_done_wqh,
-+		!atomic_test_mask(
-+			ZFCP_STATUS_ADAPTER_ERP_PENDING,
-+			&adapter->status));
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_erp_modify_adapter_status
-+ *
-+ * purpose:	
-+ *
-+ */
-+static void zfcp_erp_modify_adapter_status(zfcp_adapter_t *adapter, 
-+                                           u32 mask,
-+                                           int set_or_clear)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+        zfcp_port_t *port;
-+        unsigned long flags;
-+        u32 common_mask=mask & ZFCP_COMMON_FLAGS;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx)\n",
-+		(unsigned long)adapter);
-+
-+        if(set_or_clear==ZFCP_SET) {
-+                atomic_set_mask(mask, &adapter->status);
-+                debug_text_event(adapter->erp_dbf, 3, "a_mod_as_s");
-+        } else {
-+                atomic_clear_mask(mask, &adapter->status);
-+                if(mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
-+                        atomic_set(&adapter->erp_counter, 0);
-+                }
-+                debug_text_event(adapter->erp_dbf, 3, "a_mod_as_c");
-+        }
-+	debug_event(adapter->erp_dbf, 3, &mask, sizeof(u32));
-+
-+        if(!common_mask) goto out;
-+        /* Deal with all underlying devices, only pass common_mask */
-+	read_lock_irqsave(&adapter->port_list_lock, flags);
-+	ZFCP_FOR_EACH_PORT(adapter, port) {
-+                zfcp_erp_modify_port_status(port,
-+                                            common_mask,
-+                                            set_or_clear);
-+        }
-+	read_unlock_irqrestore(&adapter->port_list_lock, flags);
-+ out:
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_erp_modify_port_status
-+ *
-+ * purpose:	sets the port and all underlying devices to ERP_FAILED
-+ *
-+ */
-+static void zfcp_erp_modify_port_status(zfcp_port_t *port,
-+                                        u32 mask,
-+                                        int set_or_clear)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+        zfcp_unit_t *unit;
-+        unsigned long flags;
-+        u32 common_mask=mask & ZFCP_COMMON_FLAGS;
-+ 
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx)\n",
-+		(unsigned long)port);
-+
-+        if(set_or_clear==ZFCP_SET) {
-+                atomic_set_mask(mask, &port->status);
-+                debug_text_event(port->adapter->erp_dbf, 3, 
-+                                 "p_mod_ps_s");
-+        } else {
-+                atomic_clear_mask(mask, &port->status);
-+                if(mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
-+                        atomic_set(&port->erp_counter, 0);
-+                }
-+                debug_text_event(port->adapter->erp_dbf, 3, 
-+                                 "p_mod_ps_c");
-+        }
-+        debug_event(port->adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t));
-+	debug_event(port->adapter->erp_dbf, 3, &mask, sizeof(u32));
-+       
-+        if(!common_mask) goto out;
-+        /* Modify status of all underlying devices, only pass common mask */
-+	read_lock_irqsave(&port->unit_list_lock, flags);
-+	ZFCP_FOR_EACH_UNIT(port, unit) {
-+                zfcp_erp_modify_unit_status(unit,
-+                                            common_mask,
-+                                            set_or_clear);
-+        }
-+	read_unlock_irqrestore(&port->unit_list_lock, flags);
-+ out:
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	zfcp_erp_modify_unit_status
-+ *
-+ * purpose:	sets the unit to ERP_FAILED
-+ *
-+ */
-+static void zfcp_erp_modify_unit_status(zfcp_unit_t *unit,
-+                                        u32 mask,
-+                                        int set_or_clear)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (unit=0x%lx)\n",
-+		(unsigned long)unit);
-+
-+        if(set_or_clear==ZFCP_SET) {
-+                atomic_set_mask(mask, &unit->status);
-+                debug_text_event(unit->port->adapter->erp_dbf, 3, "u_mod_us_s");
-+        } else {
-+                atomic_clear_mask(mask, &unit->status);
-+                if(mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
-+                        atomic_set(&unit->erp_counter, 0);
-+                }
-+                debug_text_event(unit->port->adapter->erp_dbf, 3, "u_mod_us_c");
-+        }
-+        debug_event(unit->port->adapter->erp_dbf, 3, &unit->fcp_lun, sizeof(fcp_lun_t));
-+	debug_event(unit->port->adapter->erp_dbf, 3, &mask, sizeof(u32));
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	Wrappper for zfcp_erp_port_reopen_all_internal
-+ *              used to ensure the correct locking
-+ *
-+ * returns:	0	- initiated action succesfully
-+ *		<0	- failed to initiate action
-+ */
-+static int zfcp_erp_port_reopen_all(
-+		zfcp_adapter_t *adapter,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        unsigned long flags;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)adapter,
-+		clear_mask);
-+
-+        write_lock_irqsave(&adapter->erp_lock, flags);
-+        retval = zfcp_erp_port_reopen_all_internal(adapter, clear_mask);
-+        write_unlock_irqrestore(&adapter->erp_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_port_reopen_all_internal(
-+		zfcp_adapter_t *adapter,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	unsigned long flags;
-+	zfcp_port_t *port;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (adapter=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)adapter,
-+		clear_mask);
-+
-+	read_lock_irqsave(&adapter->port_list_lock, flags);
-+	ZFCP_FOR_EACH_PORT(adapter, port) {
-+		if (!atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER, &port->status)) 
-+			zfcp_erp_port_reopen_internal(port, clear_mask);
-+	}
-+	read_unlock_irqrestore(&adapter->port_list_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_unit_reopen_all_internal(
-+		zfcp_port_t *port,
-+		int clear_mask)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	unsigned long flags;
-+	zfcp_unit_t *unit;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (port=0x%lx clear_mask=0x%x)\n",
-+		(unsigned long)port,
-+		clear_mask);
-+
-+	read_lock_irqsave(&port->unit_list_lock, flags);
-+	ZFCP_FOR_EACH_UNIT(port, unit)
-+		zfcp_erp_unit_reopen_internal(unit, clear_mask);
-+	read_unlock_irqrestore(&port->unit_list_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	this routine executes the 'Reopen Adapter' action
-+ *		(the entire action is processed synchronously, since
-+ *		there are no actions which might be run concurrently
-+ *		per definition)
-+ *
-+ * returns:	ZFCP_ERP_SUCCEEDED	- action finished successfully
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
-+ */
-+static int zfcp_erp_adapter_strategy(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        unsigned long timeout;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+        
-+	retval = zfcp_erp_adapter_strategy_close(erp_action);
-+	if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
-+		retval = ZFCP_ERP_EXIT;
-+        else	retval = zfcp_erp_adapter_strategy_open(erp_action);
-+
-+        debug_text_event(adapter->erp_dbf, 3, "a_ast/ret");
-+        debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof(int));
-+        debug_event(adapter->erp_dbf, 3, &retval, sizeof(int));
-+
-+        if(retval==ZFCP_ERP_FAILED) {
-+                /*INFO*/
-+                ZFCP_LOG_NORMAL("Waiting to allow the adapter with devno "
-+                              "0x%04x to recover itself\n",
-+                              adapter->devno);
-+		/*
-+		 * SUGGESTION: substitute by
-+		 * timeout = ZFCP_TYPE2_RECOVERY_TIME;
-+		 * __ZFCP_WAIT_EVENT_TIMEOUT(timeout, 0);
-+		 */
-+                timeout=ZFCP_TYPE2_RECOVERY_TIME;
-+                set_current_state(TASK_UNINTERRUPTIBLE);
-+                do { 
-+                        timeout=schedule_timeout(timeout);
-+                } while (timeout);
-+		/* FIXME: why no  current->state = TASK_RUNNING ? */
-+        }
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:	ZFCP_ERP_SUCCEEDED      - action finished successfully
-+ *              ZFCP_ERP_FAILED         - action finished unsuccessfully
-+ */
-+static int zfcp_erp_adapter_strategy_close(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->adapter->status);
-+	retval = zfcp_erp_adapter_strategy_generic(erp_action, 1);
-+        atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->adapter->status);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:	ZFCP_ERP_SUCCEEDED      - action finished successfully
-+ *              ZFCP_ERP_FAILED         - action finished unsuccessfully
-+ */
-+static int zfcp_erp_adapter_strategy_open(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->adapter->status);
-+	retval = zfcp_erp_adapter_strategy_generic(erp_action, 0);
-+        atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->adapter->status);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_register_adapter
-+ *
-+ * purpose:	allocate the irq associated with this devno and register
-+ *		the FSF adapter with the SCSI stack
-+ *
-+ * returns:	
-+ */
-+static int zfcp_erp_adapter_strategy_generic(zfcp_erp_action_t *erp_action, int close)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
-+ 
-+	int retval = ZFCP_ERP_SUCCEEDED;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	if (close)
-+		goto close_only;
-+
-+	retval = zfcp_erp_adapter_strategy_open_irq(erp_action);
-+	if (retval != ZFCP_ERP_SUCCEEDED) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not setup irq, "
-+			"adapter with devno 0x%04x. ",
-+			adapter->devno);
-+		goto failed_irq;
-+	}
-+	ZFCP_LOG_DEBUG(
-+		"got irq %d (adapter devno=0x%04x)\n",
-+		adapter->irq,
-+		adapter->devno);
-+
-+	/* setup QDIO for this adapter */
-+	retval = zfcp_erp_adapter_strategy_open_qdio(erp_action);
-+	if (retval != ZFCP_ERP_SUCCEEDED) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not start QDIO (data transfer mechanism) "
-+                        "adapter with devno 0x%04x.\n",
-+			adapter->devno);
-+		goto failed_qdio;
-+	}
-+	ZFCP_LOG_DEBUG("QDIO started (adapter devno=0x%04x)\n", adapter->devno);
-+
-+	/* setup FSF for this adapter */
-+	retval = zfcp_erp_adapter_strategy_open_fsf(erp_action);
-+	if (retval != ZFCP_ERP_SUCCEEDED) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not communicate with the adapter with "
-+                        "devno 0x%04x. Card may be busy.\n",
-+			adapter->devno);
-+		goto failed_openfcp;
-+	}
-+	ZFCP_LOG_DEBUG("FSF started (adapter devno=0x%04x)\n", adapter->devno);
-+
-+        /* Success */
-+        atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &erp_action->adapter->status);
-+        goto out;
-+
-+close_only:
-+        atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, 
-+                          &erp_action->adapter->status); 
-+failed_openfcp:
-+	zfcp_erp_adapter_strategy_close_qdio(erp_action);
-+	zfcp_erp_adapter_strategy_close_fsf(erp_action);
-+        
-+failed_qdio:
-+	zfcp_erp_adapter_strategy_close_irq(erp_action);
-+        
-+failed_irq:
-+                
-+        /* NOP */
-+out:
-+                
-+        ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+        
-+ return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	gets irq associated with devno and requests irq
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_adapter_strategy_open_irq(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = ZFCP_ERP_FAILED;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+#if 0
-+        retval = zfcp_adapter_detect(adapter)
-+        if (retval == -ENOMEM) {
-+                retval = ZFCP_ERP_NOMEM;
-+                goto out;
-+        }
-+        if (retval != 0) {
-+                retval = ZFCP_ERP_FAILED;
-+                goto out;
-+        }
-+
-+        retval = zfcp_adapter_detect(adapter)
-+        if (retval == -ENOMEM) {
-+                retval = ZFCP_ERP_NOMEM;
-+                goto out;
-+        }
-+        if (retval != 0) {
-+                retval = ZFCP_ERP_FAILED;
-+                goto out;
-+        }
-+        retval = ZFCP_ERP_SUCCEEDED;
-+#endif
-+
-+	if (zfcp_adapter_detect(adapter) == 0)
-+		if (zfcp_adapter_irq_register(adapter) == 0)
-+			retval = ZFCP_ERP_SUCCEEDED;
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	releases owned irq
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_adapter_strategy_close_irq(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = ZFCP_ERP_SUCCEEDED;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	zfcp_adapter_irq_unregister(erp_action->adapter);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_qdio_init
-+ *
-+ * purpose:	setup QDIO operation for specified adapter
-+ *
-+ * returns:	0 - successful setup
-+ *		!0 - failed setup
-+ */
-+int zfcp_erp_adapter_strategy_open_qdio(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+	int i;
-+	volatile qdio_buffer_element_t *buffere;
-+	
-+        ZFCP_LOG_TRACE("enter\n");
-+
-+	if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: QDIO (data transfer mechanism) start-up on "
-+                        "adapter with devno 0x%04x attempted twice. "
-+                        "Second attempt ignored.",
-+			adapter->devno);
-+			goto failed_sanity;
-+	}
-+
-+	if (zfcp_initialize_with_0copy(adapter) != 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not establish queues for QDIO (data "
-+                        "transfer mechanism) operation on adapter with devno "
-+                        "0x%04x.\n",
-+                        adapter->devno);
-+		goto failed_qdio_initialize;
-+	}
-+	ZFCP_LOG_DEBUG("queues established\n");
-+
-+        /* activate QDIO request and response queue */
-+	if (qdio_activate(adapter->irq, 0) != 0) {
-+		ZFCP_LOG_INFO(
-+			"error: Could not activate queues for QDIO (data "
-+                        "transfer mechanism) operation on adapter with devno "
-+                        "0x%04x.\n",
-+                        adapter->devno);
-+		goto failed_qdio_activate;
-+	}
-+	ZFCP_LOG_DEBUG("queues activated\n");
-+
-+	/*
-+	 * put buffers into response queue,
-+	 */
-+	for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
-+		buffere =  &(adapter->response_queue.buffer[i]->element[0]);
-+		buffere->length = 0;
-+        	buffere->flags = SBAL_FLAGS_LAST_ENTRY;
-+		buffere->addr = 0;
-+	}
-+
-+        ZFCP_LOG_TRACE(
-+		"Calling do QDIO irq=0x%x,flags=0x%x, queue_no=%i, "
-+		"index_in_queue=%i, count=%i\n",
-+		adapter->irq,
-+		QDIO_FLAG_SYNC_INPUT,
-+		0,
-+		0,
-+		QDIO_MAX_BUFFERS_PER_Q);	
-+
-+	retval = do_QDIO(
-+			adapter->irq,
-+			QDIO_FLAG_SYNC_INPUT,
-+			0,
-+			0,
-+			QDIO_MAX_BUFFERS_PER_Q,
-+			NULL);
-+
-+	if (retval) {
-+		ZFCP_LOG_NORMAL(
-+			"bug: QDIO (data transfer mechanism) inobund transfer "
-+                        "structures could not be set-up (debug info %d)\n",
-+			retval);
-+		goto failed_do_qdio;
-+	} else	{
-+		adapter->response_queue.free_index = 0;
-+		atomic_set(&adapter->response_queue.free_count, 0);
-+		ZFCP_LOG_DEBUG(
-+			"%i buffers successfully enqueued to response queue\n",
-+			QDIO_MAX_BUFFERS_PER_Q);
-+	}
-+
-+	/* set index of first avalable SBALS / number of available SBALS */
-+	adapter->request_queue.free_index = 0;
-+	atomic_set(&adapter->request_queue.free_count, QDIO_MAX_BUFFERS_PER_Q);
-+        adapter->request_queue.distance_from_int = 0;
-+
-+	/* initialize waitqueue used to wait for free SBALs in requests queue */
-+	init_waitqueue_head(&adapter->request_wq);
-+
-+	/* ok, we did it - skip all cleanups for different failures */
-+	atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
-+	retval = ZFCP_ERP_SUCCEEDED;
-+
-+	goto out;
-+
-+failed_do_qdio:
-+	/* NOP */
-+
-+failed_qdio_activate:
-+        debug_text_event(adapter->erp_dbf, 3, "qdio_down1a");
-+        while (qdio_cleanup(adapter->irq,
-+                            QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS) {
-+                set_current_state(TASK_UNINTERRUPTIBLE);
-+                schedule_timeout(HZ);
-+        }
-+        debug_text_event(adapter->erp_dbf, 3, "qdio_down1b");
-+
-+	/*
-+	 * First we had to stop QDIO operation.
-+	 * Now it is safe to take the following actions.
-+	 */
-+
-+failed_qdio_initialize:
-+failed_sanity:
-+	retval = ZFCP_ERP_FAILED;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_qdio_cleanup
-+ *
-+ * purpose:	cleans up QDIO operation for the specified adapter
-+ *
-+ * returns:	0 - successful cleanup
-+ *		!0 - failed cleanup
-+ */
-+int zfcp_erp_adapter_strategy_close_qdio(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
-+ 
-+	int retval = ZFCP_ERP_SUCCEEDED;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
-+		ZFCP_LOG_DEBUG(
-+			"Termination of QDIO (data transfer operation) "
-+                        "attempted for an inactive qdio on the "
-+                        "adapter with devno 0x%04x....ignored.\n",
-+			adapter->devno);
-+		retval = ZFCP_ERP_FAILED;
-+		goto out;
-+	}
-+
-+        /*
-+         * Get queue_lock and clear QDIOUP flag. Thus it's guaranteed that
-+         * do_QDIO won't be called while qdio_shutdown is in progress.
-+         */
-+
-+        write_lock_irq(&adapter->request_queue.queue_lock);
-+        atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
-+        write_unlock_irq(&adapter->request_queue.queue_lock);
-+
-+        debug_text_event(adapter->erp_dbf, 3, "qdio_down2a");
-+        while (qdio_cleanup(adapter->irq,
-+                            QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS) {
-+                set_current_state(TASK_UNINTERRUPTIBLE);
-+                schedule_timeout(HZ);
-+        }
-+        debug_text_event(adapter->erp_dbf, 3, "qdio_down2b");
-+
-+	/*
-+	 * First we had to stop QDIO operation.
-+	 * Now it is safe to take the following actions.
-+	 */
-+
-+	zfcp_zero_sbals(
-+		adapter->request_queue.buffer,
-+		0,
-+		QDIO_MAX_BUFFERS_PER_Q);
-+        adapter->response_queue.free_index = 0;
-+        atomic_set(&adapter->response_queue.free_count, 0);
-+        adapter->request_queue.free_index = 0;
-+        atomic_set(&adapter->request_queue.free_count, 0);
-+        adapter->request_queue.distance_from_int = 0;
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_init
-+ *
-+ * purpose:	initializes FSF operation for the specified adapter
-+ *
-+ * returns:	0 - succesful initialization of FSF operation
-+ *		!0 - failed to initialize FSF operation
-+ */
-+static int zfcp_erp_adapter_strategy_open_fsf(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
-+ 
-+	int retval;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	/* do 'exchange configuration data' */
-+	retval = zfcp_erp_adapter_strategy_open_fsf_xconfig(erp_action);
-+	if (retval == ZFCP_ERP_FAILED)
-+		goto out;
-+
-+	/* start the desired number of Status Reads */
-+	retval = zfcp_erp_adapter_strategy_open_fsf_statusread(erp_action);
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+ 
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_adapter_strategy_open_fsf_xconfig(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = ZFCP_ERP_SUCCEEDED;
-+	int retries;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
-+	retries = ZFCP_EXCHANGE_CONFIG_DATA_RETRIES;
-+
-+	do {
-+		atomic_clear_mask(
-+			ZFCP_STATUS_ADAPTER_HOST_CON_INIT, 
-+			&adapter->status);
-+	        ZFCP_LOG_DEBUG("Doing exchange config data\n");
-+		zfcp_erp_action_to_running(erp_action);
-+		zfcp_erp_timeout_init(erp_action);
-+		if (zfcp_fsf_exchange_config_data(erp_action)) {
-+			retval = ZFCP_ERP_FAILED;
-+        	        debug_text_event(adapter->erp_dbf, 5, "a_fstx_xf");
-+			ZFCP_LOG_INFO(
-+				"error: Out of resources. Could not "
-+				"start exchange of configuration data "
-+				"between the adapter with devno "
-+				"0x%04x and the device driver.\n",
-+				adapter->devno);
-+			break;
-+		}
-+		debug_text_event(adapter->erp_dbf, 6, "a_fstx_xok");
-+		ZFCP_LOG_DEBUG("Xchange underway\n");
-+
-+		/*
-+		 * Why this works:
-+		 * Both the normal completion handler as well as the timeout
-+		 * handler will do an 'up' when the 'exchange config data'
-+		 * request completes or times out. Thus, the signal to go on
-+		 * won't be lost utilizing this semaphore.
-+		 * Furthermore, this 'adapter_reopen' action is
-+		 * guaranteed to be the only action being there (highest action
-+		 * which prevents other actions from being created).
-+		 * Resulting from that, the wake signal recognized here
-+		 * _must_ be the one belonging to the 'exchange config
-+		 * data' request.
-+		 */
-+		down(&adapter->erp_ready_sem);
-+		if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
-+			ZFCP_LOG_INFO(
-+				"error: Exchange of configuration data between "
-+				"the adapter with devno 0x%04x and the device "
-+				"driver timed out\n",
-+				adapter->devno);
-+			break;
-+		}
-+		if (atomic_test_mask(
-+				ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
-+				&adapter->status)) {
-+                        ZFCP_LOG_DEBUG(
-+				"Host connection still initialising... "
-+				"waiting and retrying....\n");
-+			/* sleep a little bit before retry */
-+			set_current_state(TASK_INTERRUPTIBLE);
-+			schedule_timeout(ZFCP_EXCHANGE_CONFIG_DATA_SLEEP);
-+		}
-+	} while ((retries--) &&
-+		 atomic_test_mask(
-+			ZFCP_STATUS_ADAPTER_HOST_CON_INIT, 
-+			&adapter->status));
-+
-+	if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status)) {
-+                ZFCP_LOG_INFO(
-+			"error: Exchange of configuration data between "
-+			"the adapter with devno 0x%04x and the device "
-+			"driver failed.\n",
-+			adapter->devno);
-+		retval = ZFCP_ERP_FAILED;;
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_adapter_strategy_open_fsf_statusread(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = ZFCP_ERP_SUCCEEDED;
-+        int temp_ret;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+	int i;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	adapter->status_read_failed = 0;
-+	for (i = 0; i < ZFCP_STATUS_READS_RECOM; i++) {
-+		ZFCP_LOG_TRACE("issuing status read request #%d...\n", i);
-+		temp_ret = zfcp_fsf_status_read(
-+				adapter,
-+				ZFCP_WAIT_FOR_SBAL);
-+		if (temp_ret < 0) {
-+        	        ZFCP_LOG_INFO(
-+				"error: Out of resources. Could not "
-+				"set-up the infrastructure for "
-+				"unsolicited status presentation "
-+				"for the adapter with devno "
-+				"0x%04x.\n",
-+				adapter->devno);
-+			retval = ZFCP_ERP_FAILED;
-+			i--;
-+			break;
-+		}
-+	}
-+	ZFCP_LOG_DEBUG("started %i status reads\n", i);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_fsf_cleanup
-+ *
-+ * purpose:	cleanup FSF operation for specified adapter
-+ *
-+ * returns:	0 - FSF operation successfully cleaned up
-+ *		!0 - failed to cleanup FSF operation for this adapter
-+ */
-+static int zfcp_erp_adapter_strategy_close_fsf(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
-+ 
-+	int retval = ZFCP_ERP_SUCCEEDED;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n", (unsigned long)adapter);
-+
-+	/*
-+	 * wake waiting initiators of requests,
-+	 * return SCSI commands (with error status),
-+	 * clean up all requests (synchronously)
-+	 */
-+        zfcp_fsf_req_dismiss_all(adapter);
-+       	/* reset FSF request sequence number */
-+	adapter->fsf_req_seq_no = 0;
-+	/* all ports and units are closed */
-+	zfcp_erp_modify_adapter_status(
-+		adapter,
-+		ZFCP_STATUS_COMMON_OPEN,
-+		ZFCP_CLEAR);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+ 
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	this routine executes the 'Reopen Physical Port' action
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
-+ *		ZFCP_ERP_SUCCEEDED	- action finished successfully
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
-+ */
-+static int zfcp_erp_port_forced_strategy(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = ZFCP_ERP_FAILED;
-+	zfcp_port_t *port = erp_action->port;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	switch (erp_action->step) {
-+
-+		/* FIXME: the ULP spec. begs for waiting for oustanding commands */
-+		case ZFCP_ERP_STEP_UNINITIALIZED :
-+			zfcp_erp_port_strategy_clearstati(port);
-+			/*
-+			 * it would be sufficient to test only the normal open flag
-+			 * since the phys. open flag cannot be set if the normal
-+			 * open flag is unset - however, this is for readabilty ...
-+			 */
-+			if (atomic_test_mask(
-+					(ZFCP_STATUS_PORT_PHYS_OPEN |
-+					 ZFCP_STATUS_COMMON_OPEN),
-+				 	&port->status)) {
-+				ZFCP_LOG_DEBUG(
-+					"Port WWPN=0x%016Lx is open -> trying close physical\n",
-+					(llui_t)port->wwpn);
-+				retval = zfcp_erp_port_forced_strategy_close(erp_action);
-+			} else	retval = ZFCP_ERP_FAILED;
-+			break;
-+
-+		case ZFCP_ERP_STEP_PHYS_PORT_CLOSING :
-+			if (atomic_test_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status)) { 
-+				ZFCP_LOG_DEBUG(
-+					"failed to close physical port WWPN=0x%016Lx\n",
-+					(llui_t)port->wwpn);
-+				retval = ZFCP_ERP_FAILED;
-+			} else	retval = ZFCP_ERP_SUCCEEDED;
-+			break;
-+	}
-+
-+        debug_text_event(adapter->erp_dbf, 3, "p_pfst/ret");
-+        debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t));
-+        debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof(int));
-+        debug_event(adapter->erp_dbf, 3, &retval, sizeof(int));
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	this routine executes the 'Reopen Port' action
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
-+ *		ZFCP_ERP_SUCCEEDED	- action finished successfully
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
-+ */
-+static int zfcp_erp_port_strategy(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = ZFCP_ERP_FAILED;
-+	zfcp_port_t *port = erp_action->port;
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	switch (erp_action->step) {
-+
-+		/* FIXME: the ULP spec. begs for waiting for oustanding commands */
-+		case ZFCP_ERP_STEP_UNINITIALIZED :
-+			zfcp_erp_port_strategy_clearstati(port);
-+			if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
-+				ZFCP_LOG_DEBUG(
-+					"port WWPN=0x%016Lx is open -> trying close\n",
-+					(llui_t)port->wwpn);
-+				retval = zfcp_erp_port_strategy_close(erp_action);
-+				goto out;
-+			} /* else it's already closed, open it */
-+			break;
-+
-+		case ZFCP_ERP_STEP_PORT_CLOSING :
-+                        if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) { 
-+				ZFCP_LOG_DEBUG(
-+					"failed to close port WWPN=0x%016Lx\n",
-+					(llui_t)port->wwpn);
-+				retval = ZFCP_ERP_FAILED;
-+				goto out;
-+			} /* else it's closed now, open it */
-+			break;
-+	}
-+        if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
-+		retval = ZFCP_ERP_EXIT;
-+	else	retval = zfcp_erp_port_strategy_open(erp_action);
-+
-+out:
-+        debug_text_event(adapter->erp_dbf, 3, "p_pst/ret");
-+        debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t));
-+        debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof(int));
-+        debug_event(adapter->erp_dbf, 3, &retval, sizeof(int));
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_port_strategy_open(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	if (atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER, &erp_action->port->status))
-+		retval = zfcp_erp_port_strategy_open_nameserver(erp_action);
-+	else	retval = zfcp_erp_port_strategy_open_common(erp_action);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ *
-+ * FIXME(design):	currently only prepared for fabric (nameserver!)
-+ */
-+static int zfcp_erp_port_strategy_open_common(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+	zfcp_port_t *port = erp_action->port;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	switch (erp_action->step) {
-+
-+		case ZFCP_ERP_STEP_UNINITIALIZED :
-+		case ZFCP_ERP_STEP_PHYS_PORT_CLOSING :
-+		case ZFCP_ERP_STEP_PORT_CLOSING :
-+			if (!(adapter->nameserver_port)) {
-+				retval = zfcp_nameserver_enqueue(adapter);
-+				if (retval == -ENOMEM) {
-+					retval = ZFCP_ERP_NOMEM;
-+					break;
-+				}
-+				if (retval != 0) {
-+					ZFCP_LOG_NORMAL(
-+						"error: nameserver port not available "
-+						"(adapter with devno 0x%04x)\n",
-+						adapter->devno);
-+					retval = ZFCP_ERP_FAILED;
-+					break;
-+				}
-+			}
-+			if (!atomic_test_mask(
-+					ZFCP_STATUS_COMMON_UNBLOCKED, 
-+					&adapter->nameserver_port->status)) {
-+				ZFCP_LOG_DEBUG(
-+					"nameserver port is not open -> open nameserver port\n");
-+                                /* nameserver port may live again */
-+				atomic_set_mask(
-+					ZFCP_STATUS_COMMON_RUNNING,
-+					&adapter->nameserver_port->status); 
-+				if (zfcp_erp_port_reopen(adapter->nameserver_port, 0) >= 0) {
-+					erp_action->step = ZFCP_ERP_STEP_NAMESERVER_OPEN;
-+					retval = ZFCP_ERP_CONTINUES;
-+				} else	retval = ZFCP_ERP_FAILED;
-+				break;
-+			} /* else nameserver port is already open, fall through */
-+
-+		case ZFCP_ERP_STEP_NAMESERVER_OPEN :
-+			if (!atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, 
-+                                              &adapter->nameserver_port->status)) {
-+				ZFCP_LOG_DEBUG("failed to open nameserver port\n");
-+				retval = ZFCP_ERP_FAILED;
-+			} else	{
-+				ZFCP_LOG_DEBUG(
-+					"nameserver port is open -> "
-+					"ask nameserver for current D_ID of port with WWPN 0x%016Lx\n",
-+					(llui_t)port->wwpn);
-+				retval = zfcp_erp_port_strategy_open_common_lookup(erp_action);
-+			}
-+			break;
-+
-+		case ZFCP_ERP_STEP_NAMESERVER_LOOKUP :
-+			if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status)) { 
-+				if (atomic_test_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status)) {
-+					ZFCP_LOG_DEBUG(
-+						"failed to look up the D_ID of the port WWPN=0x%016Lx "
-+						"(misconfigured WWPN?)\n",
-+						(llui_t)port->wwpn);
-+                                        zfcp_erp_port_failed(port);
-+					retval = ZFCP_ERP_EXIT;
-+				} else	{
-+					ZFCP_LOG_DEBUG(
-+						"failed to look up the D_ID of the port WWPN=0x%016Lx\n",
-+						(llui_t)port->wwpn);
-+					retval = ZFCP_ERP_FAILED;
-+				}
-+			} else	{
-+				ZFCP_LOG_DEBUG(
-+					"port WWPN=0x%016Lx has D_ID=0x%06x -> trying open\n",
-+					(llui_t)port->wwpn,
-+					port->d_id);
-+				retval = zfcp_erp_port_strategy_open_port(erp_action);
-+			}
-+			break;
-+
-+		case ZFCP_ERP_STEP_PORT_OPENING :
-+			if (atomic_test_mask(
-+					(ZFCP_STATUS_COMMON_OPEN |
-+					 ZFCP_STATUS_PORT_DID_DID),/* D_ID might have changed during open */
-+					&port->status)) {
-+				ZFCP_LOG_DEBUG(
-+					"port WWPN=0x%016Lx is open ",
-+					(llui_t)port->wwpn);
-+				retval = ZFCP_ERP_SUCCEEDED;
-+			} else	{
-+				ZFCP_LOG_DEBUG(
-+					"failed to open port WWPN=0x%016Lx\n",
-+					(llui_t)port->wwpn);
-+				retval = ZFCP_ERP_FAILED;
-+			}
-+			break;
-+
-+		default :
-+			ZFCP_LOG_NORMAL(
-+				"bug: unkown erp step 0x%x\n",
-+				erp_action->step);
-+			retval = ZFCP_ERP_FAILED;
-+	}
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_port_strategy_open_nameserver(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+	zfcp_port_t *port = erp_action->port;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	switch (erp_action->step) {
-+
-+		case ZFCP_ERP_STEP_UNINITIALIZED :
-+		case ZFCP_ERP_STEP_PHYS_PORT_CLOSING :
-+		case ZFCP_ERP_STEP_PORT_CLOSING :
-+			ZFCP_LOG_DEBUG(
-+				"port WWPN=0x%016Lx has D_ID=0x%06x -> trying open\n",
-+				(llui_t)port->wwpn,
-+				port->d_id);
-+			retval = zfcp_erp_port_strategy_open_port(erp_action);
-+			break;
-+
-+		case ZFCP_ERP_STEP_PORT_OPENING :
-+			if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
-+				ZFCP_LOG_DEBUG("nameserver port is open\n");
-+				retval = ZFCP_ERP_SUCCEEDED;
-+			} else	{
-+				ZFCP_LOG_DEBUG("failed to open nameserver port\n");
-+				retval = ZFCP_ERP_FAILED;
-+			}
-+			/* this is needed anyway (dont care for retval of wakeup) */
-+			ZFCP_LOG_DEBUG("continue other open port operations\n");
-+			zfcp_erp_port_strategy_open_nameserver_wakeup(erp_action);
-+			break;
-+
-+		default :
-+			ZFCP_LOG_NORMAL(
-+				"bug: unkown erp step 0x%x\n",
-+				erp_action->step);
-+			retval = ZFCP_ERP_FAILED;
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	makes the erp thread continue with reopen (physical) port
-+ *		actions which have been paused until the name server port
-+ *		is opened (or failed)
-+ *
-+ * returns:	0	(a kind of void retval, its not used)
-+ */
-+static int zfcp_erp_port_strategy_open_nameserver_wakeup(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	unsigned long flags;
-+	zfcp_adapter_t *adapter = erp_action->adapter;
-+	struct list_head *entry, *temp_entry;
-+	zfcp_erp_action_t *tmp_erp_action;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	write_lock_irqsave(&adapter->erp_lock, flags);
-+	list_for_each_safe(entry, temp_entry, &adapter->erp_running_head) {
-+		tmp_erp_action = list_entry(entry, zfcp_erp_action_t, list);
-+		debug_text_event(adapter->erp_dbf, 3, "p_pstnsw_n");
-+		debug_event(adapter->erp_dbf, 3, &tmp_erp_action->port->wwpn, sizeof(wwn_t));
-+		if (tmp_erp_action->step == ZFCP_ERP_STEP_NAMESERVER_OPEN) {
-+			debug_text_event(adapter->erp_dbf, 3, "p_pstnsw_w");
-+			debug_event(adapter->erp_dbf, 3, &tmp_erp_action->port->wwpn, sizeof(wwn_t));
-+			if (atomic_test_mask(
-+					ZFCP_STATUS_COMMON_ERP_FAILED,
-+					&adapter->nameserver_port->status))
-+				zfcp_erp_port_failed(tmp_erp_action->port);
-+			zfcp_erp_action_ready(tmp_erp_action);
-+		}
-+	}
-+	write_unlock_irqrestore(&adapter->erp_lock, flags);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
-+ */
-+static int zfcp_erp_port_forced_strategy_close(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+        zfcp_port_t *port = erp_action->port;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	zfcp_erp_timeout_init(erp_action);
-+	retval = zfcp_fsf_close_physical_port(erp_action);
-+	if (retval == -ENOMEM) {
-+                debug_text_event(adapter->erp_dbf, 5, "o_pfstc_nomem");
-+                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+		retval = ZFCP_ERP_NOMEM;
-+		goto out;
-+	}
-+	erp_action->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING;
-+	if (retval != 0) {
-+                debug_text_event(adapter->erp_dbf, 5, "o_pfstc_cpf");
-+                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+		/* could not send 'open', fail */
-+		retval = ZFCP_ERP_FAILED;
-+		goto out;
-+	}
-+	debug_text_event(adapter->erp_dbf, 6, "o_pfstc_cpok");
-+	debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof(wwn_t));
-+	retval = ZFCP_ERP_CONTINUES;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_port_strategy_clearstati(zfcp_port_t *port)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+        zfcp_adapter_t *adapter = port->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        debug_text_event(adapter->erp_dbf, 5, "p_pstclst");
-+        debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+
-+	atomic_clear_mask(
-+		ZFCP_STATUS_COMMON_OPENING |
-+		ZFCP_STATUS_COMMON_CLOSING |
-+		ZFCP_STATUS_PORT_DID_DID |
-+		ZFCP_STATUS_PORT_PHYS_CLOSING |
-+		ZFCP_STATUS_PORT_INVALID_WWPN,
-+		&port->status);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
-+ */
-+static int zfcp_erp_port_strategy_close(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+        zfcp_port_t *port = erp_action->port;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	zfcp_erp_timeout_init(erp_action);
-+	retval = zfcp_fsf_close_port(erp_action);
-+	if (retval == -ENOMEM) {
-+                debug_text_event(adapter->erp_dbf, 5, "p_pstc_nomem");
-+                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+		retval = ZFCP_ERP_NOMEM;
-+		goto out;
-+	}
-+	erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING;
-+	if (retval != 0) {
-+		debug_text_event(adapter->erp_dbf, 5, "p_pstc_cpf");
-+		debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+		/* could not send 'close', fail */
-+		retval = ZFCP_ERP_FAILED;
-+		goto out;
-+	}
-+	debug_text_event(adapter->erp_dbf, 6, "p_pstc_cpok");
-+	debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof(wwn_t));
-+	retval = ZFCP_ERP_CONTINUES;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
-+ */
-+static int zfcp_erp_port_strategy_open_port(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+        zfcp_port_t *port = erp_action->port;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	zfcp_erp_timeout_init(erp_action);
-+	retval = zfcp_fsf_open_port(erp_action);
-+	if (retval == -ENOMEM) {
-+                debug_text_event(adapter->erp_dbf, 5, "p_psto_nomem");
-+                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+		retval = ZFCP_ERP_NOMEM;
-+		goto out;
-+	}
-+	erp_action->step = ZFCP_ERP_STEP_PORT_OPENING;
-+	if (retval != 0) {
-+                debug_text_event(adapter->erp_dbf, 5, "p_psto_opf");
-+                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+		/* could not send 'open', fail */
-+		retval = ZFCP_ERP_FAILED;
-+		goto out;
-+	}
-+	debug_text_event(adapter->erp_dbf, 6, "p_psto_opok");
-+	debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof(wwn_t));
-+	retval = ZFCP_ERP_CONTINUES;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
-+ */
-+static int zfcp_erp_port_strategy_open_common_lookup(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+        zfcp_port_t *port = erp_action->port;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	zfcp_erp_timeout_init(erp_action);
-+	retval = zfcp_ns_gid_pn_request(erp_action);
-+	if (retval == -ENOMEM) {
-+                debug_text_event(adapter->erp_dbf, 5, "p_pstn_nomem");
-+                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+		retval = ZFCP_ERP_NOMEM;
-+		goto out;
-+	}
-+	erp_action->step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
-+	if (retval != 0) {
-+                debug_text_event(adapter->erp_dbf, 5, "p_pstn_ref");
-+                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+		/* could not send nameserver request, fail */
-+		retval = ZFCP_ERP_FAILED;
-+		goto out;
-+	}
-+	debug_text_event(adapter->erp_dbf, 6, "p_pstn_reok");
-+	debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof(wwn_t));
-+	retval = ZFCP_ERP_CONTINUES;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	this routine executes the 'Reopen Unit' action
-+ *		currently no retries
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
-+ *		ZFCP_ERP_SUCCEEDED	- action finished successfully
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
-+ */
-+static int zfcp_erp_unit_strategy(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = ZFCP_ERP_FAILED;
-+	zfcp_unit_t *unit = erp_action->unit;
-+        zfcp_adapter_t *adapter=erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	switch (erp_action->step) {
-+
-+		/* FIXME: the ULP spec. begs for waiting for oustanding commands */
-+		case ZFCP_ERP_STEP_UNINITIALIZED :
-+			zfcp_erp_unit_strategy_clearstati(unit);
-+			if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
-+				ZFCP_LOG_DEBUG(
-+					"unit FCP_LUN=0x%016Lx is open -> trying close\n",
-+					(llui_t)unit->fcp_lun);
-+				retval = zfcp_erp_unit_strategy_close(erp_action);
-+				break;
-+			} /* else it's already closed, fall through */
-+
-+		case ZFCP_ERP_STEP_UNIT_CLOSING :
-+			if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
-+				ZFCP_LOG_DEBUG(
-+					"failed to close unit FCP_LUN=0x%016Lx\n",
-+					(llui_t)unit->fcp_lun);
-+				retval = ZFCP_ERP_FAILED;
-+			} else	{
-+				if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
-+			                retval = ZFCP_ERP_EXIT;
-+				else	{
-+					ZFCP_LOG_DEBUG(
-+						"unit FCP_LUN=0x%016Lx is not open -> trying open\n",
-+						(llui_t)unit->fcp_lun);
-+					retval = zfcp_erp_unit_strategy_open(erp_action);
-+				}
-+			}
-+			break;
-+
-+		case ZFCP_ERP_STEP_UNIT_OPENING :
-+			if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) { 
-+				ZFCP_LOG_DEBUG(
-+					"unit FCP_LUN=0x%016Lx is open\n",
-+					(llui_t)unit->fcp_lun);
-+				retval = ZFCP_ERP_SUCCEEDED;
-+			} else	{
-+				ZFCP_LOG_DEBUG(
-+					"failed to open unit FCP_LUN=0x%016Lx\n",
-+					(llui_t)unit->fcp_lun);
-+				retval = ZFCP_ERP_FAILED;
-+			}
-+			break;
-+	}
-+
-+        debug_text_event(adapter->erp_dbf, 3, "u_ust/ret");
-+        debug_event(adapter->erp_dbf, 3, &unit->fcp_lun, sizeof(fcp_lun_t));
-+        debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof(int));
-+        debug_event(adapter->erp_dbf, 3, &retval, sizeof(int));
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_unit_strategy_clearstati(zfcp_unit_t *unit)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+        zfcp_adapter_t *adapter=unit->port->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+       
-+        debug_text_event(adapter->erp_dbf,5,"u_ustclst");
-+        debug_event(adapter->erp_dbf,5,&unit->fcp_lun,
-+                    sizeof(fcp_lun_t));
-+
-+	atomic_clear_mask(
-+		ZFCP_STATUS_COMMON_OPENING |
-+		ZFCP_STATUS_COMMON_CLOSING,
-+		&unit->status);
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
-+ */
-+static int zfcp_erp_unit_strategy_close(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+        zfcp_unit_t *unit = erp_action->unit;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	zfcp_erp_timeout_init(erp_action);
-+	retval = zfcp_fsf_close_unit(erp_action);
-+	if (retval == -ENOMEM) {
-+                debug_text_event(adapter->erp_dbf, 5, "u_ustc_nomem");
-+                debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof(fcp_lun_t));
-+		retval = ZFCP_ERP_NOMEM;
-+		goto out;
-+	}
-+	erp_action->step = ZFCP_ERP_STEP_UNIT_CLOSING;
-+	if (retval != 0) {
-+                debug_text_event(adapter->erp_dbf, 5, "u_ustc_cuf");
-+                debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof(fcp_lun_t));
-+		/* could not send 'close', fail */
-+		retval = ZFCP_ERP_FAILED;
-+		goto out;
-+	}
-+	debug_text_event(adapter->erp_dbf, 6, "u_ustc_cuok");
-+	debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof(fcp_lun_t));
-+	retval = ZFCP_ERP_CONTINUES;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
-+ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
-+ */
-+static int zfcp_erp_unit_strategy_open(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval;
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+        zfcp_unit_t *unit = erp_action->unit;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+	zfcp_erp_timeout_init(erp_action);
-+	retval = zfcp_fsf_open_unit(erp_action);
-+	if (retval == -ENOMEM) {
-+                debug_text_event(adapter->erp_dbf, 5, "u_usto_nomem");
-+                debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof(fcp_lun_t));
-+		retval = ZFCP_ERP_NOMEM;
-+		goto out;
-+	}
-+	erp_action->step = ZFCP_ERP_STEP_UNIT_OPENING;
-+	if (retval != 0) {
-+                debug_text_event(adapter->erp_dbf, 5, "u_usto_ouf");
-+                debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof(fcp_lun_t));
-+		/* could not send 'open', fail */
-+		retval = ZFCP_ERP_FAILED;
-+		goto out;
-+	}
-+	debug_text_event(adapter->erp_dbf, 6, "u_usto_ouok");
-+	debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof(fcp_lun_t));
-+	retval = ZFCP_ERP_CONTINUES;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static /*inline*/ int zfcp_erp_timeout_init(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+        zfcp_adapter_t *adapter=erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+        
-+        debug_text_event(adapter->erp_dbf, 6, "a_timinit");
-+        debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof(int));
-+	init_timer(&erp_action->timer);
-+	erp_action->timer.function = zfcp_erp_timeout_handler;
-+	erp_action->timer.data = (unsigned long)erp_action;
-+        /* jiffies will be added in zfcp_fsf_req_send */
-+	erp_action->timer.expires = ZFCP_ERP_FSFREQ_TIMEOUT;
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	enqueue the specified error recovery action, if needed
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_action_enqueue(
-+	int action,
-+	zfcp_adapter_t *adapter,
-+	zfcp_port_t *port,
-+	zfcp_unit_t *unit)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 1;
-+	zfcp_erp_action_t *erp_action = NULL;
-+	int stronger_action = 0;
-+	u32 status = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (action=%d adapter=0x%lx "
-+		"port=0x%lx unit=0x%lx)\n",
-+		action,
-+		(unsigned long)adapter,
-+		(unsigned long)port,
-+		(unsigned long)unit);
-+
-+	/*
-+	 * We need some rules here which check whether we really need
-+	 * this action or whether we should just drop it.
-+	 * E.g. if there is a unfinished 'Reopen Port' request then we drop a
-+	 * 'Reopen Unit' request for an associated unit since we can't
-+	 * satisfy this request now. A 'Reopen Port' action will trigger
-+	 * 'Reopen Unit' actions when it completes.
-+	 * Thus, there are only actions in the queue which can immediately be
-+	 * executed. This makes the processing of the action queue more
-+	 * efficient.
-+	 */
-+
-+        debug_text_event(adapter->erp_dbf, 4, "a_actenq");
-+        debug_event(adapter->erp_dbf, 4, &action, sizeof(int));
-+	/* check whether we really need this */
-+	switch (action) {
-+		case ZFCP_ERP_ACTION_REOPEN_UNIT :
-+			if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status)) {
-+                                debug_text_event(adapter->erp_dbf, 4, "u_actenq_drp");
-+                                debug_event(adapter->erp_dbf, 4, &unit->fcp_lun, sizeof(fcp_lun_t));
-+				ZFCP_LOG_DEBUG(
-+					"drop: erp action %i on unit "
-+					"FCP_LUN=0x%016Lx "
-+					"(erp action %i already exists)\n",
-+					action,
-+					(llui_t)unit->fcp_lun,
-+					unit->erp_action.action);
-+				goto out;
-+			}
-+			if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status)) { 
-+				stronger_action = ZFCP_ERP_ACTION_REOPEN_PORT;
-+				unit = NULL;
-+			}
-+			/* fall through !!! */
-+
-+		case ZFCP_ERP_ACTION_REOPEN_PORT :
-+			if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)) {
-+                                debug_text_event(adapter->erp_dbf, 4, "p_actenq_drp");
-+                                debug_event(adapter->erp_dbf, 4, &port->wwpn, sizeof(wwn_t));
-+				ZFCP_LOG_DEBUG(
-+					"drop: erp action %i on port "
-+					"WWPN=0x%016Lx "
-+					"(erp action %i already exists)\n",
-+					action,
-+					(llui_t)port->wwpn,
-+					port->erp_action.action);
-+				goto out;
-+			}
-+			/* fall through !!! */
-+
-+		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
-+                        /*
-+                         * FIXME(erpmod):
-+                         * Can't override normal port_reopen in the usual
-+                         * manner. Trying adapter_reopen as a workaround
-+                         * won't do it. The old code indifferently overwrote
-+                         * the normal port_reopen action. That was not a valid
-+                         * approach. The old implementation left the erp-action
-+                         * list corrupted which had held the former normal
-+                         * port_reopen action, since the latter had not been
-+                         * removed from the list, just added in another place.
-+                         * The hapless endeavour continued with the immediate
-+                         * dismissal of the shiny new port_forced_reopen,
-+                         * a doom that was meant to be met by the just wiped out
-+                         * normal port_reopen action. Not to mention the stray
-+                         * fsf_req that might still have felt attached to the
-+                         * converted action.   Lots of accidents .... who knows
-+                         * what else was about to go awry in the wake of them.
-+                         * As a (permanent) hack we are going to use a flag to
-+			 * get this action scheduled as a retry after completion
-+			 * of the normal port_reopen.
-+			 * But til then, just gracefully exit here (and warn).
-+                         */
-+			if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)) {
-+				if (port->erp_action.action == ZFCP_ERP_ACTION_REOPEN_PORT_FORCED) {
-+	                                debug_text_event(adapter->erp_dbf, 4, "pf_actenq_drp");
-+        	                        debug_event(adapter->erp_dbf, 4, &port->wwpn, sizeof(wwn_t));
-+					ZFCP_LOG_DEBUG(
-+						"drop: erp action %i on port "
-+						"WWPN=0x%016Lx "
-+						"(erp action %i already exists)\n",
-+						action,
-+						(llui_t)port->wwpn,
-+						port->erp_action.action);
-+				} else	{
-+	                                debug_text_event(adapter->erp_dbf, 0, "pf_actenq_drpcp");
-+        	                        debug_event(adapter->erp_dbf, 0, &port->wwpn, sizeof(wwn_t));
-+					ZFCP_LOG_NORMAL(
-+						"drop: erp action %i on port "
-+						"WWPN=0x%016Lx "
-+						"(erp action %i already exists)\n",
-+						action,
-+						(llui_t)port->wwpn,
-+						port->erp_action.action);
-+
-+				}
-+				goto out;
-+			}
-+			if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status)) {
-+				stronger_action = ZFCP_ERP_ACTION_REOPEN_ADAPTER;
-+				port = NULL;
-+			}
-+			/* fall through !!! */
-+
-+		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
-+			if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status)) {
-+                                debug_text_event(adapter->erp_dbf, 4, "a_actenq_drp");
-+				ZFCP_LOG_DEBUG(
-+					"drop: erp action %i on adapter "
-+					"devno=0x%04x "
-+					"(erp action %i already exists)\n",
-+					action,
-+					adapter->devno,
-+					adapter->erp_action.action);
-+				goto out;
-+			}
-+			break;
-+
-+		default :
-+                        debug_text_exception(adapter->erp_dbf, 1, "a_actenq_bug");
-+                        debug_event(adapter->erp_dbf, 1, &action, sizeof(int));
-+                        ZFCP_LOG_NORMAL(
-+				"bug: Unknown error recovery procedure "
-+				"action requested on the adapter with "
-+				"devno 0x%04x "
-+				"(debug info %d)\n",
-+				adapter->devno,
-+				action);
-+			goto out;
-+	}
-+
-+	/* check whether we need something stronger first */
-+	if (stronger_action) {
-+                debug_text_event(adapter->erp_dbf, 4, "a_actenq_str");
-+                debug_event(adapter->erp_dbf, 4, &stronger_action, sizeof(int));
-+		ZFCP_LOG_DEBUG(
-+			"shortcut: need erp action %i before "
-+			"erp action %i (adapter devno=0x%04x)\n",
-+			stronger_action,
-+			action,
-+			adapter->devno);
-+		action = stronger_action;
-+	}
-+
-+	/* mark adapter to have some error recovery pending */
-+	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
-+
-+	/* setup error recovery action */
-+	switch (action) {
-+
-+		case ZFCP_ERP_ACTION_REOPEN_UNIT :
-+			atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
-+			erp_action = &unit->erp_action;
-+			if (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status))
-+				status = ZFCP_STATUS_ERP_CLOSE_ONLY;
-+			break;
-+
-+		case ZFCP_ERP_ACTION_REOPEN_PORT :
-+		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
-+			zfcp_erp_action_dismiss_port(port);
-+			atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
-+			erp_action = &port->erp_action;
-+			if (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status))
-+				status = ZFCP_STATUS_ERP_CLOSE_ONLY;
-+			break;
-+
-+		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
-+			zfcp_erp_action_dismiss_adapter(adapter);
-+			atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
-+			erp_action = &adapter->erp_action;
-+			if (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &adapter->status))
-+				status = ZFCP_STATUS_ERP_CLOSE_ONLY;
-+			break;
-+	}
-+
-+	memset(erp_action, 0, sizeof(zfcp_erp_action_t));
-+	erp_action->adapter = adapter;
-+	erp_action->port = port;
-+	erp_action->unit = unit;
-+	erp_action->action = action;
-+	erp_action->status = status;
-+
-+	/* finally put it into 'ready' queue and kick erp thread */
-+	list_add(&erp_action->list, &adapter->erp_ready_head);
-+	ZFCP_LOG_DEBUG(
-+		"waking erp_thread of the adapter with devno=0x%04x\n",
-+		adapter->devno);
-+	up(&adapter->erp_ready_sem);
-+	retval = 0;
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_action_dequeue(
-+                zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (erp_action=0x%lx)\n",
-+		(unsigned long)erp_action);
-+
-+        debug_text_event(adapter->erp_dbf, 4, "a_actdeq");
-+        debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof(int));
-+        list_del(&erp_action->list);
-+	switch (erp_action->action) {
-+		case ZFCP_ERP_ACTION_REOPEN_UNIT :
-+			atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &erp_action->unit->status);
-+			break;
-+		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
-+		case ZFCP_ERP_ACTION_REOPEN_PORT :
-+			atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &erp_action->port->status);
-+			break;
-+		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
-+			atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &erp_action->adapter->status);
-+			break;
-+		default :
-+			/* bug */
-+			break;
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_action_dismiss_adapter(zfcp_adapter_t *adapter)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	zfcp_port_t *port;
-+	unsigned long flags;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        debug_text_event(adapter->erp_dbf, 5, "a_actab");
-+	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status))
-+		/* that's really all in this case */
-+		zfcp_erp_action_dismiss(&adapter->erp_action);
-+	else	{
-+		/* have a deeper look */
-+		read_lock_irqsave(&adapter->port_list_lock, flags);
-+		ZFCP_FOR_EACH_PORT(adapter, port) {
-+			zfcp_erp_action_dismiss_port(port);
-+		}
-+		read_unlock_irqrestore(&adapter->port_list_lock, flags);	
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	
-+ *
-+ * returns:
-+ */
-+static int zfcp_erp_action_dismiss_port(zfcp_port_t *port)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	int retval = 0;
-+	zfcp_unit_t *unit;
-+        zfcp_adapter_t *adapter = port->adapter;
-+	unsigned long flags;
-+
-+	ZFCP_LOG_TRACE("enter\n");
-+
-+        debug_text_event(adapter->erp_dbf, 5, "p_actab");
-+        debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
-+	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status))
-+		/* that's really all in this case */
-+		zfcp_erp_action_dismiss(&port->erp_action);
-+	else	{
-+		/* have a deeper look */
-+		read_lock_irqsave(&port->unit_list_lock, flags);
-+		ZFCP_FOR_EACH_UNIT(port, unit) {
-+                        if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status)) {
-+                                zfcp_erp_action_dismiss(&unit->erp_action);
-+                        }
-+		}
-+		read_unlock_irqrestore(&port->unit_list_lock, flags);	
-+	}
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	moves erp_action to 'erp running list'
-+ *
-+ * returns:
-+ */
-+static inline void zfcp_erp_action_to_running(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (erp_action=0x%lx\n",
-+		(unsigned long)erp_action);
-+
-+        debug_text_event(adapter->erp_dbf, 6, "a_toru");
-+        debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof(int));
-+        zfcp_erp_from_one_to_other(
-+		&erp_action->list,
-+		&erp_action->adapter->erp_running_head);
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	moves erp_action to 'erp ready list'
-+ *
-+ * returns:
-+ */
-+static inline void zfcp_erp_action_to_ready(zfcp_erp_action_t *erp_action)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+        zfcp_adapter_t *adapter = erp_action->adapter;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (erp_action=0x%lx\n",
-+		(unsigned long)erp_action);
-+
-+        debug_text_event(adapter->erp_dbf, 6, "a_tore");
-+        debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof(int));
-+	zfcp_erp_from_one_to_other(
-+		&erp_action->list,
-+		&erp_action->adapter->erp_ready_head);
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:	
-+ *
-+ * purpose:	moves a request from one erp_action list to the other
-+ *
-+ * returns:
-+ */
-+static inline void zfcp_erp_from_one_to_other(
-+	struct list_head *entry,
-+	struct list_head *head)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
-+
-+	ZFCP_LOG_TRACE(
-+		"enter entry=0x%lx, head=0x%lx\n",
-+		(unsigned long)entry,
-+                (unsigned long)head);
-+
-+	list_del(entry);
-+	list_add(entry, head);
-+
-+	ZFCP_LOG_TRACE("exit\n");
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+#ifdef ZFCP_STAT_REQSIZES
-+
-+static int zfcp_statistics_clear(
-+		zfcp_adapter_t *adapter,
-+		struct list_head *head)
-+{
-+	int retval = 0;
-+	unsigned long flags;
-+	struct list_head *entry, *next_entry;
-+	zfcp_statistics_t *stat;
-+
-+	write_lock_irqsave(&adapter->stat_lock, flags);
-+	list_for_each_safe(entry, next_entry, head) {
-+		stat = list_entry(entry, zfcp_statistics_t, list);
-+		list_del(entry);
-+		kfree(stat);
-+	}
-+	write_unlock_irqrestore(&adapter->stat_lock, flags);
-+
-+	return retval;
-+}
-+
-+
-+static inline void zfcp_statistics_new(
-+		zfcp_adapter_t *adapter,
-+		struct list_head *head,
-+		u32 num)
-+{
-+	zfcp_statistics_t *stat;
-+
-+	stat = ZFCP_KMALLOC(sizeof(zfcp_statistics_t), GFP_ATOMIC);
-+	if (stat) {
-+		stat->num = num;
-+		stat->hits = 1;
-+		list_add_tail(&stat->list, head);
-+	} else	atomic_inc(&adapter->stat_errors);
-+}
-+
-+/**
-+ * list_for_some_prev   -       iterate over a list backwards
-+ * 				starting somewhere in the middle
-+ *				of the list
-+ * @pos:        the &list_t to use as a loop counter.
-+ * @middle:	the &list_t pointing to the antecessor to start at
-+ * @head:       the head for your list.
-+ */
-+#define list_for_some_prev(pos, middle, head) \
-+	for (pos = (middle)->prev, prefetch(pos->prev); pos != (head); \
-+		pos = pos->prev, prefetch(pos->prev))
-+
-+/*
-+ * Sort list if necessary to find frequently used entries quicker.
-+ * Since counters are only increased by one, sorting can be implemented
-+ * in a quite efficient way. It usually comprimises swapping positions
-+ * of the given entry with its antecessor, if at all. In rare cases
-+ * (= if there is a series of antecessors with identical counter values
-+ * which are in turn less than the value hold by the current entry)
-+ * searching for the position where we want to move the current entry to
-+ * takes more than one hop back through the list. As to the overall
-+ * performance of our statistics this is not a big deal.
-+ * As a side-effect, we provide statistics sorted by hits to the user.
-+ */
-+static inline void zfcp_statistics_sort(
-+		struct list_head *head,
-+		struct list_head *entry,
-+		zfcp_statistics_t *stat)
-+{
-+	zfcp_statistics_t *stat_sort = NULL;
-+	struct list_head *entry_sort = NULL;
-+
-+	list_for_some_prev(entry_sort, entry, head) {
-+		stat_sort = list_entry(entry_sort, zfcp_statistics_t, list);
-+		if (stat_sort->hits >= stat->hits)
-+			break;
-+	}
-+	if (stat_sort &&
-+	    entry->prev != entry_sort)
-+		list_move(entry, entry_sort);
-+}
-+
-+
-+static void zfcp_statistics_inc(
-+		zfcp_adapter_t *adapter,
-+                struct list_head *head,
-+		u32 num)
-+{
-+        unsigned long flags;
-+        zfcp_statistics_t *stat;
-+        struct list_head *entry;
-+
-+	if (atomic_read(&adapter->stat_on) == 0)
-+		return;
-+
-+        write_lock_irqsave(&adapter->stat_lock, flags);
-+        list_for_each(entry, head) {
-+                stat = list_entry(entry, zfcp_statistics_t, list);
-+                if (stat->num == num) {
-+                        stat->hits++;
-+			zfcp_statistics_sort(head, entry, stat);
-+                        goto unlock;
-+                }
-+        }
-+        /* hits is initialized to 1 */
-+        zfcp_statistics_new(adapter, head, num);
-+unlock:
-+        write_unlock_irqrestore(&adapter->stat_lock, flags);
-+}
-+
-+
-+static int zfcp_statistics_print(
-+		zfcp_adapter_t *adapter,
-+                struct list_head *head,
-+		char *prefix,
-+		char *buf,
-+		int len,
-+		int max)
-+{
-+        unsigned long flags;
-+        zfcp_statistics_t *stat;
-+        struct list_head *entry;
-+
-+        write_lock_irqsave(&adapter->stat_lock, flags);
-+	list_for_each(entry, head) {
-+		if (len > max - 26)
-+			break;
-+		stat = list_entry(entry, zfcp_statistics_t, list);
-+		len += sprintf(buf + len, "%s 0x%08x: 0x%08x\n",
-+			       prefix, stat->num, stat->hits);
-+        }
-+        write_unlock_irqrestore(&adapter->stat_lock, flags);
-+
-+        return len;
-+}
-+
-+#endif // ZFCP_STAT_REQSIZES
-+
-+/*
-+ * function:    zfcp_cfdc_dev_ioctl
-+ *
-+ * purpose:     Handle control file upload/download transaction via IOCTL interface
-+ *
-+ * returns:     0           - Operation completed successfuly
-+ *              -ENOTTY     - Unknown IOCTL command
-+ *              -EINVAL     - Invalid sense data record
-+ *              -ENXIO      - The FCP adapter is not available
-+ *              -EOPNOTSUPP - The FCP adapter does not have Control File support
-+ *              -ENOMEM     - Insufficient memory
-+ *              -EFAULT     - User space memory I/O operation fault
-+ *              -EPERM      - Cannot create or queue FSF request or create SBALs
-+ */
-+int zfcp_cfdc_dev_ioctl(
-+	struct inode *inode,
-+	struct file *file,
-+	unsigned int command,
-+	unsigned long buffer)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	zfcp_cfdc_sense_data_t *sense_data, *sense_data_user;
-+	zfcp_adapter_t *adapter;
-+	zfcp_fsf_req_t *fsf_req = NULL;
-+	zfcp_sg_list_t *sg_list = NULL;
-+	u32 fsf_command, option;
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (inode=0x%lx file=0x%lx command=0x%x buffer=0x%lx)\n",
-+		(unsigned long)inode, (unsigned long)file, command, buffer);
-+
-+	ZFCP_LOG_NORMAL(
-+		"Control file data channel transaction opened\n");
-+
-+	sense_data = (zfcp_cfdc_sense_data_t*)ZFCP_KMALLOC(
-+		sizeof(zfcp_cfdc_sense_data_t), GFP_KERNEL);
-+	if (sense_data == NULL) {
-+		ZFCP_LOG_NORMAL(
-+			"Not enough memory for the sense data record\n");
-+		retval = -ENOMEM;
-+		goto out;
-+	}
-+
-+	sg_list = (zfcp_sg_list_t*)ZFCP_KMALLOC(
-+		sizeof(zfcp_sg_list_t), GFP_KERNEL);
-+	if (sg_list == NULL) {
-+		ZFCP_LOG_NORMAL(
-+			"Not enough memory for the scatter-gather list\n");
-+		retval = -ENOMEM;
-+		goto out;
-+	}
-+
-+	if (command != ZFCP_CFDC_IOC) {
-+		ZFCP_LOG_NORMAL(
-+			"IOC request code 0x%x is not valid\n",
-+			command);
-+		retval = -ENOTTY;
-+		goto out;
-+	}
-+
-+	if ((sense_data_user = (zfcp_cfdc_sense_data_t*)buffer) == NULL) {
-+		ZFCP_LOG_NORMAL(
-+			"Sense data record is required\n");
-+		retval = -EINVAL;
-+		goto out;
-+	}
-+
-+	retval = copy_from_user(
-+		sense_data, sense_data_user, sizeof(zfcp_cfdc_sense_data_t));
-+	if (retval) {
-+		ZFCP_LOG_NORMAL(
-+			"Cannot copy sense data record from user space memory\n");
-+		retval = -EFAULT;
-+		goto out;
-+	}
-+
-+	if (sense_data->signature != ZFCP_CFDC_SIGNATURE) {
-+		ZFCP_LOG_NORMAL(
-+			"No valid sense data request signature 0x%08x found\n",
-+			ZFCP_CFDC_SIGNATURE);
-+		retval = -EINVAL;
-+		goto out;
-+	}
-+
-+	switch (sense_data->command) {
-+
-+	case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
-+		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
-+		option = FSF_CFDC_OPTION_NORMAL_MODE;
-+		break;
-+
-+	case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
-+		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
-+		option = FSF_CFDC_OPTION_FORCE;
-+		break;
-+
-+	case ZFCP_CFDC_CMND_FULL_ACCESS:
-+		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
-+		option = FSF_CFDC_OPTION_FULL_ACCESS;
-+		break;
-+
-+	case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
-+		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
-+		option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
-+		break;
-+
-+	case ZFCP_CFDC_CMND_UPLOAD:
-+		fsf_command = FSF_QTCB_UPLOAD_CONTROL_FILE;
-+		option = 0;
-+		break;
-+
-+	default:
-+		ZFCP_LOG_NORMAL(
-+			"Command code 0x%08x is not valid\n",
-+			sense_data->command);
-+		retval = -EINVAL;
-+		goto out;
-+	}
-+
-+	retval = zfcp_adapter_enqueue(sense_data->devno, &adapter);
-+	if (retval != 0) {
-+		if (retval != ZFCP_KNOWN) {
-+			ZFCP_LOG_NORMAL(
-+				"Cannot enqueue the FCP adapter (devno=0x%04x)\n",
-+				sense_data->devno);
-+			retval = -ENXIO;
-+			goto out;
-+		}
-+	} else {
-+		retval = zfcp_erp_adapter_reopen(adapter, 0);
-+		if (retval < 0) {
-+			ZFCP_LOG_NORMAL(
-+				"Cannot reopen the FCP adapter (devno=0x%04x)\n",
-+				adapter->devno);
-+			retval = -ENXIO;
-+			goto out;
-+		}
-+		zfcp_erp_wait(adapter);
-+	}
-+
-+	if (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &adapter->status) ||
-+	    atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
-+		ZFCP_LOG_NORMAL(
-+			"The FCP adapter is not available (devno=0x%04x)\n",
-+			adapter->devno);
-+		retval = -ENXIO;
-+		goto out;
-+	}
-+
-+	if (adapter->devinfo.sid_data.dev_model != ZFCP_DEVICE_MODEL_PRIV) {
-+		ZFCP_LOG_NORMAL(
-+			"Control file upload/download is accepted "
-+			"only on privileged subchannels (devno=0x%04x)\n",
-+			adapter->devno);
-+		retval = -ENXIO;
-+		goto out;
-+	}
-+
-+	if (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE) {
-+		retval = zfcp_sg_list_alloc(sg_list, ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
-+		if (retval) {
-+			ZFCP_LOG_NORMAL(
-+				"Not enough memory for the scatter-gather list\n");
-+			retval = -ENOMEM;
-+			goto out;
-+		}
-+	}
-+
-+	if ((sense_data->command & ZFCP_CFDC_DOWNLOAD) &&
-+	    (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE)) {
-+		retval = zfcp_sg_list_copy_from_user(
-+			sg_list, &sense_data_user->control_file,
-+			ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
-+		if (retval) {
-+			ZFCP_LOG_NORMAL(
-+				"Cannot copy control file from user space memory\n");
-+			retval = -EFAULT;
-+			goto out;
-+		}
-+	}
-+
-+	retval = zfcp_fsf_control_file(
-+		adapter, &fsf_req, fsf_command, option, sg_list);
-+	if (retval == -EOPNOTSUPP) {
-+		ZFCP_LOG_NORMAL(
-+			"Specified adapter does not support control file\n");
-+		goto out;
-+	} else if (retval != 0) {
-+		ZFCP_LOG_NORMAL(
-+			"Cannot create or queue FSF request or create SBALs\n");
-+		retval = -EPERM;
-+		goto out;
-+	}
-+
-+	wait_event(fsf_req->completion_wq,
-+		   fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
-+
-+	if ((fsf_req->qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
-+	    (fsf_req->qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) {
-+		retval = -ENXIO;
-+		goto out;
-+	}
-+
-+	sense_data->fsf_status = fsf_req->qtcb->header.fsf_status;
-+	memcpy(&sense_data->fsf_status_qual,
-+	       &fsf_req->qtcb->header.fsf_status_qual,
-+	       sizeof(fsf_status_qual_t));
-+	memcpy(&sense_data->payloads, &fsf_req->qtcb->bottom.support.els, 256);
-+
-+	retval = copy_to_user(
-+		sense_data_user, sense_data, sizeof(zfcp_cfdc_sense_data_t));
-+	if (retval) {
-+		ZFCP_LOG_NORMAL(
-+			"Cannot copy sense data record to user space memory\n");
-+		retval = -EFAULT;
-+		goto out;
-+	}
-+
-+	if (sense_data->command & ZFCP_CFDC_UPLOAD) {
-+		retval = zfcp_sg_list_copy_to_user(
-+			&sense_data_user->control_file, sg_list,
-+			ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
-+		if (retval) {
-+			ZFCP_LOG_NORMAL(
-+				"Cannot copy control file to user space memory\n");
-+			retval = -EFAULT;
-+			goto out;
-+		}
-+	}
-+
-+out:
-+	if (fsf_req) {
-+		retval = zfcp_fsf_req_cleanup(fsf_req);
-+		if (retval) {
-+			ZFCP_LOG_NORMAL(
-+				"bug: Could not remove the FSF request "
-+				"(devno=0x%04x fsf_req=0x%lx)\n",
-+				adapter->devno,
-+				(unsigned long)fsf_req);
-+			retval = -EPERM;
-+		}
-+	}
-+
-+	if (sg_list != NULL) {
-+		zfcp_sg_list_free(sg_list);
-+		ZFCP_KFREE(sg_list, sizeof(zfcp_sg_list_t));
-+	}
-+
-+	if (sense_data != NULL)
-+		ZFCP_KFREE(sense_data, sizeof(zfcp_cfdc_sense_data_t));
-+
-+	ZFCP_LOG_NORMAL(
-+		"Control file data channel transaction closed\n");
-+
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_sg_list_alloc
-+ *
-+ * purpose:     Create a scatter-gather list of the specified size
-+ *
-+ * returns:     0       - Scatter gather list is created
-+ *              -ENOMEM - Insufficient memory (*list_ptr is then set to NULL)
-+ */
-+static inline int zfcp_sg_list_alloc(zfcp_sg_list_t *sg_list, size_t size)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	struct scatterlist *sg;
-+	int i;
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (sg_list=0x%lx size=%ld)\n",
-+		(unsigned long)sg_list, size);
-+
-+	sg_list->count = size >> PAGE_SHIFT;
-+	if (size & ~PAGE_MASK)
-+		sg_list->count++;
-+	sg_list->sg = (struct scatterlist*)ZFCP_KMALLOC(
-+		sg_list->count * sizeof(struct scatterlist), GFP_KERNEL);
-+	if (sg_list->sg == NULL) {
-+		ZFCP_LOG_INFO("Out of memory!\n");
-+		retval = -ENOMEM;
-+		goto out;
-+	}
-+
-+	for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) {
-+		if (i < sg_list->count - 1) {
-+			sg->length = PAGE_SIZE;
-+			sg->address = (char*)ZFCP_GET_ZEROED_PAGE(GFP_KERNEL);
-+		} else {
-+			sg->length = size & ~PAGE_MASK;
-+			sg->address = (char*)ZFCP_KMALLOC(sg->length, GFP_KERNEL);
-+		}
-+		if (sg->address == NULL) {
-+			while (sg-- != sg_list->sg)
-+				ZFCP_FREE_PAGE((unsigned long)sg->address);
-+			ZFCP_KFREE(sg_list->sg,
-+				sg_list->count * sizeof(struct scatterlist));
-+			ZFCP_LOG_INFO("Out of memory!\n");
-+			retval = -ENOMEM;
-+			goto out;
-+		}
-+	}
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_sg_list_free
-+ *
-+ * purpose:     Destroy a scatter-gather list and release memory
-+ *
-+ * returns:     Always 0
-+ */
-+static inline int zfcp_sg_list_free(zfcp_sg_list_t *sg_list)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	struct scatterlist *sg;
-+	int i;
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE("enter (sg_list=0x%lx)\n", (unsigned long)sg_list);
-+
-+	if ((sg_list->sg == NULL) || (sg_list->count == 0))
-+		goto out;
-+
-+	for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++)
-+		ZFCP_FREE_PAGE((unsigned long)sg->address);
-+	ZFCP_KFREE(sg_list->sg, sg_list->count * sizeof(struct scatterlist));
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_sg_list_copy_from_user
-+ *
-+ * purpose:     Copy data from user space memory to the scatter-gather list
-+ *
-+ * returns:     0       - The data has been copied from user
-+ *              -EFAULT - Memory I/O operation fault
-+ */
-+static inline int zfcp_sg_list_copy_from_user(
-+	zfcp_sg_list_t *sg_list, void *buffer, size_t size)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	struct scatterlist *sg;
-+	unsigned int length;
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (sg_list=0x%lx buffer=0x%lx size=%ld)\n",
-+		(unsigned long)sg_list, (unsigned long)buffer, size);
-+
-+	for (sg = sg_list->sg; size > 0; sg++) {
-+		length = min((unsigned int)size, sg->length);
-+		if (copy_from_user(sg->address, buffer, length)) {
-+			ZFCP_LOG_INFO("Memory error (copy_from_user)\n");
-+			retval = -EFAULT;
-+			goto out;
-+		}
-+		buffer += length;
-+		size -= length;
-+	}
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+/*
-+ * function:    zfcp_sg_list_copy_to_user
-+ *
-+ * purpose:     Copy data from the scatter-gather list to user space memory
-+ *
-+ * returns:     0       - The data has been copied to user
-+ *              -EFAULT - Memory I/O operation fault
-+ */
-+static inline int zfcp_sg_list_copy_to_user(
-+	void *buffer, zfcp_sg_list_t *sg_list, size_t size)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
-+
-+	struct scatterlist *sg;
-+	unsigned int length;
-+	int retval = 0;
-+
-+	ZFCP_LOG_TRACE(
-+		"enter (buffer=0x%lx sg_list=0x%lx size=%ld)\n",
-+		(unsigned long)buffer, (unsigned long)sg_list, size);
-+
-+	for (sg = sg_list->sg; size > 0; sg++) {
-+		length = min((unsigned int)size, sg->length);
-+		if (copy_to_user(buffer, sg->address, length)) {
-+			ZFCP_LOG_INFO("Memory error (copy_to_user)\n");
-+			retval = -EFAULT;
-+			goto out;
-+		}
-+		buffer += length;
-+		size -= length;
-+	}
-+
-+out:
-+	ZFCP_LOG_TRACE("exit (%i)\n", retval);
-+
-+	return retval;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+EXPORT_SYMBOL(zfcp_data);
-+
-+/*
-+ * Overrides for Emacs so that we get a uniform 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:
-+ */
-=== drivers/s390/scsi/zh.h
-==================================================================
---- drivers/s390/scsi/zh.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/scsi/zh.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,512 @@
-+/* 
-+ * $Id: zh.h,v 1.7.2.2 2004/03/24 11:18:00 aherrman Exp $
-+ *
-+ * Module providing an interface for HBA API (FC-HBA) implementation
-+ * to the zfcp driver.
-+ *
-+ * (C) Copyright IBM Corp. 2003
-+ *
-+ * 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 of the License, or
-+ * (at your option) any later version. See the file COPYING for more
-+ * information.
-+ *
-+ * Authors:
-+ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
-+ *       Andreas Herrmann <aherrman at de.ibm.com>
-+ */
-+
-+/*
-+ * We expect wwn to be a 64 bit type and not to be an array of 8 characters.
-+ * Conversion of wwn thus has to be done in vendor/os specific library.
-+ */
-+
-+#ifndef _ZH_H_
-+#define _ZH_H_
-+
-+#include <linux/ioctl.h>
-+#include <asm/string.h>
-+
-+#include "zfcp_zh.h"
-+
-+typedef u64 devid_t;
-+
-+#define DEVID_TO_DEVNO(devid) (devno_t)(devid & 0xffff)
-+#define ZH_DEVID(scsid, cssid, devno) \
-+ ((devid_t) (scsid << 24) | (cssid << 16) | devno)
-+
-+/* Maximum number of events in the queues (shared and polled) */
-+#define ZH_EVENTS_MAX 20
-+#define ZH_GET_EVENT_BUFFER_COUNT 10
-+
-+/* SPC-2 defines the addional_length field in the sense data format as a byte,
-+ * thus only 255 bytes of additional data may be returned. Size of header in
-+ * sense data format is 8 bytes.
-+ */
-+#define ZH_SCSI_SENSE_BUFFERSIZE   263
-+
-+typedef unsigned short zh_count_t;
-+
-+/**
-+ * struct sg_list - ng scatterlist
-+ * @*sg: pointer to the real scatterlist
-+ * @count: number of allocated pages
-+ *
-+ * Even if this struct is small, it groups logical depended information. And it
-+ * reduces parameter list length.
-+ */
-+struct sg_list
-+{
-+	struct scatterlist *sg;
-+	unsigned int count;
-+};
-+
-+/**
-+ * struct scsi_inquiry_cmd
-+ * FIXME: to be documented
-+ * The following are defined by the SCSI-2 specification.
-+ */
-+struct scsi_inquiry_cmd
-+{
-+	u8 op;
-+	u8 reserved1:6;
-+	u8 cmdt:1;
-+	u8 evpd:1;
-+	u8 page_code;
-+	u8 reserved2;
-+	u8 alloc_length;
-+	u8 control;
-+} __attribute__((packed));
-+
-+/**
-+ * struct scsi_read_capacity_cmd
-+ * FIXME: to be documented
-+ * the 10 byte version of the command, see SBC-2
-+ */
-+struct scsi_read_capacity_cmd
-+{
-+	u8 op;
-+	u8 reserved1:7;
-+	u8 reladdr:1;
-+	u32 lba;
-+	u16 reserved2;
-+	u8 reserved3:7;
-+	u8 pmi:1;
-+	u8 control;
-+} __attribute__((packed));
-+
-+/**
-+ * struct scsi_report_luns_cmd
-+ * FIXME: to be documented
-+ */
-+struct scsi_report_luns_cmd
-+{
-+	u8 op;
-+	u8 reserved1[5];
-+	u32 alloc_length;
-+	u8 reserved2;
-+	u8 control;
-+} __attribute__((packed));
-+
-+/* minimum size of the response data for REPORT LUNS */ 
-+#define SCSI_REPORT_LUNS_SIZE_MIN 8
-+
-+#ifndef REPORT_LUNS
-+#define REPORT_LUNS 0xA0
-+#endif
-+
-+/**
-+ * struct zh_get_config
-+ * @devid: of the adapter to generate config events for
-+ * @wwpn: of the port to generate config events for
-+ * @size: must be set to sizeof(struct zh_event)
-+ *
-+ * The size member is used to assert that the events structure in user- and
-+ * kernelspace are of the same size.
-+ * If devid is 0, adapter_add events are generated for all configured adapters.
-+ * If devid is != 0 and wwpn is zero, port_add events are generated for all
-+ * ports of the specified adapter.
-+ * if devid != 0 and wwpn != 0, unit_add events are generated for all units
-+ * configured on the specified port;
-+ */
-+struct zh_get_config
-+{
-+	devid_t devid;
-+	wwn_t wwpn;
-+	unsigned int flags:2;
-+};
-+
-+/**
-+ * struct zh_get_adapterattributes
-+ * @devid: unique id for adapter device
-+ * @attributes: adapter attributes
-+ *
-+ * structure passed by ioctl ZH_IOC_GET_ADAPTERATTRIBUTES
-+ */
-+struct zh_get_adapterattributes
-+{
-+	devid_t devid;
-+	struct zfcp_adapter_attributes attributes;
-+};
-+
-+/**
-+ * struct zh_get_portattributes
-+ * @devid: unique id for adapter device
-+ * @wwpn: wwn of discovered or local port (optional)
-+ * @attributes: port attributes
-+ *
-+ * structure passed by ioctls ZH_IOC_GET_PORTATTRIBUTES and
-+ * ZH_IOC_GET_DPORTATTRIBUTES
-+ */
-+struct zh_get_portattributes
-+{
-+	devid_t devid;
-+	wwn_t wwpn;
-+	struct zfcp_port_attributes attributes;
-+};
-+
-+/**
-+ * struct zh_get_portstatistics
-+ * @devid: unique id for adapter device
-+ * @portwwn: wwn local port (optional)
-+ * @stat: port statistics
-+ *
-+ * structure passed by ioctl ZH_IOC_GET_PORTSTATISTICS
-+ */
-+struct zh_get_portstatistics
-+{
-+	devid_t devid;
-+	wwn_t portwwn;
-+	struct zfcp_port_statistics statistics;
-+};
-+
-+/**
-+ * struct zh_event_polled_link
-+ * @event: subtype of link event, see @zh_event_polled_link_e
-+ * @port_fc_id: port FC id, where this event occured
-+ * 
-+ * The standard defines an array of 3 u32 as reserved
-+ * in its structure. We do not need this here since no data
-+ * is passed with this member from kernel, to userspace
-+ */
-+struct zh_event_polled_link
-+{
-+  u32 port_fc_id;
-+};
-+
-+/**
-+ * struct zh_event_polled_rscn
-+ * @port_fc_id: port FC id, where this event occured
-+ * @port_page: affected port_id pages
-+ * 
-+ * The standard defines an array of 2 u32 as reserved
-+ * in its structure. We do not need this here since no data
-+ * is passed with this member from kernel, to userspace
-+ */
-+struct zh_event_polled_rscn
-+{
-+  u32 port_fc_id;
-+  u32 port_page;
-+};
-+
-+/**
-+ * struct zh_event_polled_pty
-+ * @pty_data: proprietary data
-+ */
-+struct zh_event_polled_pty
-+{
-+  u32 pty_data[4];
-+};
-+
-+/**
-+ * struct zh_event_polled
-+ * @event: type of occured event
-+ * @data: union of diffrent events
-+ * @link: link event, @see zh_event_polled_link
-+ * @rscn: rscn event, @see zh_event_polled_rscn
-+ * @pty: pty event, @see zh_event_polled_pty
-+ */
-+struct zh_event_polled
-+{
-+  u32 event;
-+  devid_t devid;
-+  union
-+  {
-+    struct zh_event_polled_link link;
-+    struct zh_event_polled_rscn rscn;
-+    struct zh_event_polled_pty pty;
-+  } data;
-+};
-+
-+/**
-+ * struct zh_get_event_buffer
-+ * @devid: of the adapter to poll events
-+ * @polled: array of events
-+ */
-+struct zh_get_event_buffer
-+{
-+	devid_t devid;
-+	unsigned int count;
-+	struct zh_event_polled polled[ZH_GET_EVENT_BUFFER_COUNT];
-+};
-+
-+/**
-+ * struct zh_event_adapter_add
-+ * @devid: unique id of adapter device
-+ * @wwnn: wwn of adapter node
-+ * @wwpn: wwn of adapter port
-+ *
-+ * structure used for adapter add events
-+ */
-+struct zh_event_adapter_add
-+{
-+	devid_t	devid;
-+	wwn_t	wwnn;
-+	wwn_t	wwpn;
-+};
-+
-+/**
-+ * struct zh_event_port_edd
-+ * @devid: of the adapter the port was added
-+ * @wwpn: of the added port
-+ * @wwnn: of the added port
-+ * @did: of the added port
-+ */
-+struct zh_event_port_add
-+{
-+	devid_t	devid;
-+	wwn_t wwpn;
-+	wwn_t wwnn;
-+	fc_id_t did;
-+};
-+
-+/**
-+ * struct zh_event_unit_add
-+ * @devid: of the adapter the unit was added
-+ * @wwpn: of the adapter the unit was added
-+ * @fclun: of the added unit
-+ * @host: SCSI host id
-+ * @channel: SCSI channel
-+ * @id: SCSI id
-+ * @lun: SCSI lun
-+ */
-+struct zh_event_unit_add
-+{
-+	devid_t	devid;
-+	wwn_t wwpn;
-+	fcp_lun_t fclun;
-+	unsigned int host;
-+	unsigned int channel;
-+	unsigned int id;
-+	unsigned int lun;
-+};
-+
-+/**
-+ * struct zh_event
-+ * @event: type of event
-+ * @data: union of event specific structures
-+ *
-+ * structure passed by ioctl ZH_IOC_EVENT (and others?)
-+ */
-+struct zh_event
-+{
-+	u8 event;
-+	union
-+	{
-+		struct zh_event_polled polled;
-+		struct zh_event_adapter_add adapter_add;
-+		struct zh_event_port_add port_add;
-+		struct zh_event_unit_add unit_add;
-+	} data;
-+};
-+
-+/* SPC-2 defines the addional_length field of the inquiry reply as a byte, thus
-+ * only 255 bytes of additional data may be returned. Size of header for
-+ * standard INQUIRY data is 5 bytes.
-+ */
-+#define ZH_SCSI_INQUIRY_SIZE 260
-+
-+/**
-+ * struct zh_scsi_inquiry - data needed for an INQUIRY
-+ * @devid: of the adapter
-+ * @wwpn: of the port
-+ * @fclun: of the unit to send the command to
-+ * @evpd: request EVPD?
-+ * @page_code: of the EVPD to request
-+ * @inquiry: payload of the response
-+ * @sense: buffer for sense data
-+ */
-+struct zh_scsi_inquiry
-+{
-+	devid_t devid;
-+	wwn_t wwpn;
-+	u64 fclun;
-+	u8 evpd;
-+	u32 page_code;
-+	u8 inquiry[ZH_SCSI_INQUIRY_SIZE];
-+	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
-+};
-+
-+/* SBC-2 defines the READ CAPACITY data */
-+#define ZH_SCSI_READ_CAPACITY_SIZE 8
-+
-+/**
-+ * struct zh_scsi_read_capacity - data needed for a READ_CAPACITY
-+ * @devid: of the adapter
-+ * @wwpn: of the port
-+ * @fclun: of the unit to send the command to
-+ * @read_capacity: payload of the response
-+ * @sense: buffer for sense data
-+ */
-+struct zh_scsi_read_capacity
-+{
-+	devid_t devid;
-+	wwn_t wwpn;
-+	u64 fclun;
-+	u8 read_capacity[ZH_SCSI_READ_CAPACITY_SIZE];
-+	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
-+};
-+
-+/**
-+ * struct zh_scsi_report_luns - data needed for an REPORT_LUNS
-+ * @devid: of the adapter
-+ * @wwpn: of the port
-+ * @*rsp_buffer: pointer to response buffer
-+ * @rsp_buffer_size: of the response buffer
-+ * @sense: buffer for sense data
-+ */
-+struct zh_scsi_report_luns
-+{
-+	devid_t devid;
-+	wwn_t wwpn;
-+	void *rsp_buffer;
-+	u32 rsp_buffer_size;
-+	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
-+} __attribute__((packed));
-+
-+/**
-+ * struct zh_els_rscn_payload - RSCN ELS payload as of FC-FS
-+ * @qualifier: event qualifier
-+ * @address_format: format of the address
-+ * @domain:
-+ * @area:
-+ * @sequence:
-+ */
-+struct zh_els_rscn_payload
-+{
-+	u8 reserved1:2;
-+	u8 qualifier:4;
-+	u8 address_format:2;
-+	u8 domain;
-+	u8 area;
-+	u8 sequence;
-+} __attribute__((packed));
-+
-+/**
-+ * struct zh_els_rscn - RSCN ELS as of FC-FS
-+ * FIXME: to be documented
-+ */
-+struct zh_els_rscn
-+{
-+	u8 op;
-+	u8 page_length;
-+	u16 payload_length;
-+	struct zh_els_rscn_payload payload[0];
-+} __attribute__((packed));
-+
-+/**
-+ * struct zh_get_rnid - retrieve RNID from adapter
-+ * @devid: to send the rnid via
-+ * @payload: payload for RNID ELS
-+ */
-+struct zh_get_rnid
-+{
-+	devid_t devid;
-+	struct zfcp_ls_rnid_acc payload;
-+};
-+
-+/**
-+ * struct zh_send_rnid - send out an RNID ELS
-+ * @devid: to send the rnid via
-+ * @wwpn: to send it to
-+ * @size: of the buffer
-+ * @payload: payload buffer
-+ */
-+struct zh_send_rnid
-+{
-+	devid_t devid;
-+	wwn_t wwpn;
-+	u32 size;
-+	struct zfcp_ls_rnid_acc payload;
-+};
-+
-+/**
-+ * struct zh_send_ct - data needed to send out a Generic Service command
-+ * struct zh_send_ct - send out a Generic Service command 
-+ * @devid: id of HBA via which to send CT
-+ * @req_length: size the request buffer
-+ * @req: request buffer
-+ * @resp_length: size of response buffer
-+ * @resp: response buffer
-+ */
-+struct zh_send_ct
-+{
-+	devid_t devid;
-+	u32 req_length;
-+	void *req;
-+	u32 resp_length;
-+	void *resp;
-+} __attribute__((packed));
-+
-+/* IOCTL's */
-+#define ZH_IOC_MAGIC 0xDD
-+
-+#define ZH_IOC_GET_ADAPTERATTRIBUTES \
-+ _IOWR(ZH_IOC_MAGIC, 1, struct zh_get_adapterattributes)
-+#define ZH_IOC_GET_PORTATTRIBUTES \
-+ _IOWR(ZH_IOC_MAGIC, 2, struct zh_get_portattributes)
-+#define ZH_IOC_GET_PORTSTATISTICS \
-+ _IOWR(ZH_IOC_MAGIC, 3, struct zh_get_portstatistics)
-+#define ZH_IOC_GET_DPORTATTRIBUTES \
-+ _IOWR(ZH_IOC_MAGIC, 4, struct zh_get_portattributes)
-+#define ZH_IOC_GET_RNID _IOWR(ZH_IOC_MAGIC, 5, struct zh_get_rnid)
-+#define ZH_IOC_SEND_RNID _IOWR(ZH_IOC_MAGIC, 6, struct zh_send_rnid)
-+#define ZH_IOC_SEND_CT _IOWR(ZH_IOC_MAGIC, 7, struct zh_send_ct)
-+#define ZH_IOC_SCSI_INQUIRY _IOW(ZH_IOC_MAGIC, 8, struct zh_scsi_inquiry)
-+#define ZH_IOC_SCSI_READ_CAPACITY \
-+ _IOW(ZH_IOC_MAGIC, 9, struct zh_scsi_read_capacity)
-+#define ZH_IOC_SCSI_REPORT_LUNS \
-+ _IOW(ZH_IOC_MAGIC, 10, struct zh_scsi_report_luns)
-+#define ZH_IOC_GET_EVENT_BUFFER \
-+ _IOWR(ZH_IOC_MAGIC, 11, struct zh_get_event_buffer)
-+#define ZH_IOC_GET_CONFIG _IOW(ZH_IOC_MAGIC, 12, struct zh_get_config)
-+#define ZH_IOC_CLEAR_CONFIG _IO(ZH_IOC_MAGIC, 13)
-+#define ZH_IOC_EVENT_START _IO(ZH_IOC_MAGIC, 14)
-+#define ZH_IOC_EVENT_STOP _IO(ZH_IOC_MAGIC, 15)
-+#define ZH_IOC_EVENT _IOR(ZH_IOC_MAGIC, 16, struct zh_event)
-+#define ZH_IOC_EVENT_INSERT _IO(ZH_IOC_MAGIC, 17)
-+
-+enum zh_event_e {
-+	ZH_EVENT_DUMMY,
-+	ZH_EVENT_ADAPTER_ADD,
-+	ZH_EVENT_ADAPTER_DEL,
-+	ZH_EVENT_PORT_ADD,
-+	ZH_EVENT_UNIT_ADD,
-+	ZH_EVENT_POLLED
-+};
-+
-+enum zh_event_polled_e {
-+	ZH_EVENT_POLLED_LINK_UP,
-+	ZH_EVENT_POLLED_LINK_DOWN,
-+	ZH_EVENT_POLLED_RSCN,
-+	ZH_EVENT_POLLED_PTY
-+};
-+
-+#define ZH_LOG(level, fmt, args...) \
-+printk(level"%s:%d, "fmt, __FUNCTION__, __LINE__, ##args)
-+
-+int zh_send_ct_helper(struct zh_send_ct *);
-+int zh_report_luns_helper(struct zh_scsi_report_luns *);
-+
-+#endif /* _ZH_H_ */
-=== drivers/s390/scsi/zfcp.h
-==================================================================
---- drivers/s390/scsi/zfcp.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/scsi/zfcp.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,1298 @@
-+/* 
-+ * $Id$
-+ *
-+ * FCP adapter driver for IBM eServer zSeries
-+ *
-+ * (C) Copyright IBM Corp. 2002, 2003
-+ *
-+ * 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 of the License, or
-+ * (at your option) any later version. See the file COPYING for more
-+ * information.
-+ *
-+ * Authors:
-+ *      Martin Peschke <mpeschke at de.ibm.com>
-+ *      Raimund Schroeder <raimund.schroeder at de.ibm.com>
-+ *      Aron Zeh
-+ *      Wolfgang Taphorn
-+ *      Stefan Bader <stefan.bader at de.ibm.com>
-+ *      Andreas Herrmann <aherrman at de.ibm.com>
-+ *      Stefan Voelkel <Stefan.Voelkel at millenux.com>
-+ */
-+
-+#ifndef _ZFCP_H_
-+#define _ZFCP_H_
-+
-+#define ZFCP_LOW_MEM_CREDITS
-+#define ZFCP_STAT_REQSIZES
-+#define ZFCP_STAT_QUEUES
-+
-+#define ZFCP_PARSE_ERR_BUF_SIZE 100
-+
-+#include <linux/config.h>
-+#include <linux/notifier.h>
-+#include <linux/types.h>
-+#include <linux/list.h>
-+#include <linux/completion.h>
-+
-+#include <asm/types.h>
-+#include <asm/irq.h>
-+#include <asm/s390io.h>
-+#include <asm/s390dyn.h>	/* devreg_t */
-+#include <asm/debug.h>		/* debug_info_t */
-+#include <asm/qdio.h>		/* qdio_buffer_t */
-+#ifdef CONFIG_S390_SUPPORT
-+#include <asm/ioctl32.h>
-+#endif
-+
-+#include <linux/blk.h>
-+#include <../drivers/scsi/scsi.h>
-+#include <../drivers/scsi/hosts.h>
-+
-+#include "zfcp_fsf.h"
-+
-+/* 32 bit for SCSI ID and LUN as long as the SCSI stack uses this type */
-+typedef u32	scsi_id_t;
-+typedef u32	scsi_lun_t;
-+
-+typedef u16	devno_t;
-+typedef u16	irq_t;
-+
-+typedef u64	wwn_t;
-+typedef u32	fc_id_t;
-+typedef u64	fcp_lun_t;
-+
-+
-+struct _zfcp_adapter;
-+struct _zfcp_fsf_req;
-+
-+/*
-+ * very simple implementation of an emergency pool:
-+ * a pool consists of a fixed number of equal elements,
-+ * for each purpose a different pool should be created
-+ */
-+typedef struct _zfcp_mem_pool_element {
-+	atomic_t use;
-+	void *buffer;
-+} zfcp_mem_pool_element_t;
-+
-+typedef struct _zfcp_mem_pool {
-+	int entries;
-+	int size;
-+	zfcp_mem_pool_element_t *element;
-+        struct timer_list timer;
-+} zfcp_mem_pool_t;
-+
-+typedef struct _zfcp_adapter_mem_pool {
-+        zfcp_mem_pool_t fsf_req_status_read;
-+	zfcp_mem_pool_t data_status_read;
-+        zfcp_mem_pool_t data_gid_pn;
-+        zfcp_mem_pool_t fsf_req_erp;
-+        zfcp_mem_pool_t fsf_req_scsi;
-+} zfcp_adapter_mem_pool_t;
-+
-+typedef void zfcp_fsf_req_handler_t(struct _zfcp_fsf_req*);
-+
-+typedef struct {
-+} zfcp_exchange_config_data_t;
-+
-+typedef struct {
-+	struct _zfcp_port *port;
-+} zfcp_open_port_t;
-+
-+typedef struct {
-+	struct _zfcp_port *port;
-+} zfcp_close_port_t;
-+
-+typedef struct {
-+	struct _zfcp_unit *unit;
-+} zfcp_open_unit_t;
-+
-+typedef struct {
-+	struct _zfcp_unit *unit;
-+} zfcp_close_unit_t;
-+
-+typedef struct {
-+	struct _zfcp_port *port;
-+} zfcp_close_physical_port_t;
-+
-+typedef struct {
-+	struct _zfcp_unit *unit;
-+	Scsi_Cmnd *scsi_cmnd;
-+	unsigned long start_jiffies;
-+} zfcp_send_fcp_command_task_t;
-+
-+
-+typedef struct {
-+	struct _zfcp_unit *unit;
-+} zfcp_send_fcp_command_task_management_t;
-+
-+typedef struct {
-+	struct _zfcp_fsf_req_t *fsf_req;
-+	struct _zfcp_unit *unit;
-+} zfcp_abort_fcp_command_t;
-+
-+/*
-+ * FC-GS-2 stuff
-+ */
-+#define ZFCP_CT_REVISION		0x01
-+#define ZFCP_CT_DIRECTORY_SERVICE	0xFC
-+#define ZFCP_CT_NAME_SERVER		0x02
-+#define ZFCP_CT_SYNCHRONOUS		0x00
-+#define ZFCP_CT_GID_PN			0x0121
-+#define ZFCP_CT_GA_NXT			0x0100
-+#define ZFCP_CT_MAX_SIZE		0x1020
-+#define ZFCP_CT_ACCEPT			0x8002
-+#define ZFCP_CT_REJECT			0x8001
-+
-+/*
-+ * FC-FS stuff
-+ */
-+#define R_A_TOV				10 /* seconds */
-+#define ZFCP_ELS_TIMEOUT		(2 * R_A_TOV)
-+
-+#define ZFCP_LS_RJT			0x01
-+#define ZFCP_LS_ACC			0x02
-+#define ZFCP_LS_RTV			0x0E
-+#define ZFCP_LS_RLS			0x0F
-+#define ZFCP_LS_PDISC			0x50
-+#define ZFCP_LS_ADISC			0x52
-+#define ZFCP_LS_RSCN			0x61
-+#define ZFCP_LS_RNID			0x78
-+#define ZFCP_LS_RLIR			0x7A
-+#define ZFCP_LS_RTV_E_D_TOV_FLAG	0x04000000
-+
-+/* LS_ACC Reason Codes */
-+#define ZFCP_LS_RJT_INVALID_COMMAND_CODE	0x01
-+#define ZFCP_LS_RJT_LOGICAL_ERROR		0x03
-+#define ZFCP_LS_RJT_LOGICAL_BUSY		0x05
-+#define ZFCP_LS_RJT_PROTOCOL_ERROR		0x07
-+#define ZFCP_LS_RJT_UNABLE_TO_PERFORM		0x09
-+#define ZFCP_LS_RJT_COMMAND_NOT_SUPPORTED	0x0B
-+#define ZFCP_LS_RJT_VENDOR_UNIQUE_ERROR		0xFF
-+
-+struct zfcp_ls_rjt_par {
-+	u8 action;
-+ 	u8 reason_code;
-+ 	u8 reason_expl;
-+ 	u8 vendor_unique;
-+} __attribute__ ((packed));
-+
-+struct zfcp_ls_rtv {
-+	u8		code;
-+	u8		field[3];
-+} __attribute__ ((packed));
-+
-+struct zfcp_ls_rtv_acc {
-+	u8		code;
-+	u8		field[3];
-+	u32		r_a_tov;
-+	u32		e_d_tov;
-+	u32		qualifier;
-+} __attribute__ ((packed));
-+
-+struct zfcp_ls_rls {
-+	u8		code;
-+	u8		field[3];
-+	fc_id_t		port_id;
-+} __attribute__ ((packed));
-+
-+struct zfcp_ls_rls_acc {
-+	u8		code;
-+	u8		field[3];
-+	u32		link_failure_count;
-+	u32		loss_of_sync_count;
-+	u32		loss_of_signal_count;
-+	u32		prim_seq_prot_error;
-+	u32		invalid_transmition_word;
-+	u32		invalid_crc_count;
-+} __attribute__ ((packed));
-+
-+struct zfcp_ls_pdisc {
-+	u8		code;
-+	u8		field[3];
-+	u8		common_svc_parm[16];
-+	wwn_t		wwpn;
-+	wwn_t		wwnn;
-+	struct {
-+		u8	class1[16];
-+		u8	class2[16];
-+		u8	class3[16];
-+	} svc_parm;
-+	u8		reserved[16];
-+	u8		vendor_version[16];
-+} __attribute__ ((packed));
-+
-+struct zfcp_ls_pdisc_acc {
-+	u8		code;
-+	u8		field[3];
-+	u8		common_svc_parm[16];
-+	wwn_t		wwpn;
-+	wwn_t		wwnn;
-+	struct {
-+		u8	class1[16];
-+		u8	class2[16];
-+		u8	class3[16];
-+	} svc_parm;
-+	u8		reserved[16];
-+	u8		vendor_version[16];
-+} __attribute__ ((packed));
-+
-+struct zfcp_ls_adisc {
-+	u8		code;
-+	u8		field[3];
-+	fc_id_t		hard_nport_id;
-+	wwn_t		wwpn;
-+	wwn_t		wwnn;
-+	fc_id_t		nport_id;
-+} __attribute__ ((packed));
-+
-+struct zfcp_ls_adisc_acc {
-+	u8		code;
-+	u8		field[3];
-+	fc_id_t		hard_nport_id;
-+	wwn_t		wwpn;
-+	wwn_t		wwnn;
-+	fc_id_t		nport_id;
-+} __attribute__ ((packed));
-+
-+struct zfcp_ls_rnid {
-+	u8		code;
-+	u8		field[3];
-+	u8		node_id_format;
-+	u8		reserved[3];
-+} __attribute__((packed));
-+
-+/* common identification data */
-+struct zfcp_ls_rnid_common_id {
-+	u64		n_port_name;
-+	u64		node_name;
-+} __attribute__((packed));
-+
-+/* general topology specific identification data */
-+struct zfcp_ls_rnid_general_topology_id {
-+	u8		vendor_unique[16];
-+	u32		associated_type;
-+	u32		physical_port_number;
-+	u32		nr_attached_nodes;
-+	u8		node_management;
-+	u8		ip_version;
-+	u16		port_number;
-+	u8		ip_address[16];
-+	u8		reserved[2];
-+	u16		vendor_specific;
-+} __attribute__((packed));
-+
-+struct zfcp_ls_rnid_acc {
-+	u8		code;
-+	u8		field[3];
-+	u8		node_id_format;
-+	u8		common_id_length;
-+	u8		reserved;
-+	u8		specific_id_length;
-+	struct zfcp_ls_rnid_common_id
-+			common_id;
-+	struct zfcp_ls_rnid_general_topology_id
-+			specific_id;
-+} __attribute__((packed));
-+
-+struct zfcp_rc_entry {
-+	u8 code;
-+	const char *description;
-+};
-+
-+
-+/*
-+ * FC-GS-4 stuff
-+ */
-+#define ZFCP_CT_TIMEOUT			(3 * R_A_TOV)
-+
-+
-+/*
-+ * header for CT_IU
-+ */
-+struct ct_hdr {
-+	u8 revision;		// 0x01
-+	u8 in_id[3];		// 0x00
-+	u8 gs_type;		// 0xFC	Directory Service
-+	u8 gs_subtype;		// 0x02	Name Server
-+	u8 options;		// 0x00 single bidirectional exchange
-+	u8 reserved0;
-+	u16 cmd_rsp_code;	// 0x0121 GID_PN, or 0x0100 GA_NXT
-+	u16 max_res_size;	// <= (4096 - 16) / 4
-+	u8 reserved1;
-+	u8 reason_code;
-+	u8 reason_code_expl;
-+	u8 vendor_unique;
-+} __attribute__ ((packed));
-+
-+/*
-+ * nameserver request CT_IU -- for requests where
-+ * a port identifier or a port name is required
-+ */
-+struct ct_iu_ns_req {
-+	struct ct_hdr header;
-+	union {
-+		wwn_t wwpn;	/* e.g .for GID_PN */
-+		fc_id_t d_id;	/* e.g. for GA_NXT */
-+	} data;
-+} __attribute__ ((packed));
-+
-+/* FS_ACC IU and data unit for GID_PN nameserver request */
-+struct ct_iu_gid_pn {
-+	struct ct_hdr header;
-+	fc_id_t d_id;
-+} __attribute__ ((packed));
-+
-+/* data unit for GA_NXT nameserver request */
-+struct ns_ga_nxt {
-+        u8 port_type;
-+        u8 port_id[3];
-+        u64 port_wwn;
-+        u8 port_symbolic_name_length;
-+        u8 port_symbolic_name[255];
-+        u64 node_wwn;
-+        u8 node_symbolic_name_length;
-+        u8 node_symbolic_name[255];
-+        u64 initial_process_associator;
-+        u8 node_ip[16];
-+        u32 cos;
-+        u8 fc4_types[32];
-+        u8 port_ip[16];
-+        u64 fabric_wwn;
-+        u8 reserved;
-+        u8 hard_address[3];
-+} __attribute__ ((packed));
-+
-+/* FS_ACC IU and data unit for GA_NXT nameserver request */
-+struct ct_iu_ga_nxt {
-+	struct ct_hdr header;
-+	struct ns_ga_nxt du;
-+} __attribute__ ((packed));
-+
-+
-+typedef void (*zfcp_send_ct_handler_t)(unsigned long);
-+
-+/* used to pass parameters to zfcp_send_ct() */
-+struct zfcp_send_ct {
-+	struct _zfcp_port *port;
-+	struct scatterlist *req;
-+	struct scatterlist *resp;
-+	unsigned int req_count;
-+	unsigned int resp_count;
-+	zfcp_send_ct_handler_t handler;
-+	unsigned long handler_data;
-+	struct _zfcp_mem_pool *pool;
-+	int timeout;
-+	struct timer_list *timer;
-+	struct completion *completion;
-+	int status;
-+};
-+
-+/* used for name server requests in error recovery */
-+struct zfcp_gid_pn_data {
-+	struct zfcp_send_ct ct;
-+	struct scatterlist req;
-+	struct scatterlist resp;
-+	struct ct_iu_ns_req ct_iu_req;
-+	struct ct_iu_gid_pn ct_iu_resp;
-+};
-+
-+typedef void (*zfcp_send_els_handler_t)(unsigned long);
-+
-+/* used to pass parameters to zfcp_send_els() */
-+struct zfcp_send_els {
-+	struct _zfcp_port *port;
-+	struct scatterlist *req;
-+	struct scatterlist *resp;
-+	unsigned int req_count;
-+	unsigned int resp_count;
-+	zfcp_send_els_handler_t handler;
-+	unsigned long handler_data;
-+	struct completion *completion;
-+	int ls_code;
-+	int status;
-+};
-+
-+typedef struct {
-+	fsf_status_read_buffer_t *buffer;
-+} zfcp_status_read_t;
-+
-+/* request specific data */
-+typedef union _zfcp_req_data {
-+	zfcp_exchange_config_data_t	exchange_config_data;
-+	zfcp_open_port_t		open_port;
-+	zfcp_close_port_t		close_port;
-+	zfcp_open_unit_t		open_unit;
-+	zfcp_close_unit_t		close_unit;
-+	zfcp_close_physical_port_t	close_physical_port;
-+	zfcp_send_fcp_command_task_t	send_fcp_command_task;
-+	zfcp_send_fcp_command_task_management_t
-+		send_fcp_command_task_management;
-+	zfcp_abort_fcp_command_t	abort_fcp_command;
-+	struct zfcp_send_ct		*send_ct;
-+	struct zfcp_send_els		*send_els;
-+	zfcp_status_read_t		status_read;
-+	fsf_qtcb_bottom_port_t		*port_data;
-+} zfcp_req_data_t;
-+
-+/* FSF request */
-+typedef struct _zfcp_fsf_req {
-+	/* driver wide common magic */
-+	u32				common_magic;
-+	/* data structure specific magic */
-+	u32				specific_magic;
-+	/* list of FSF requests */
-+	struct list_head		list;
-+	/* adapter this request belongs to */
-+	struct _zfcp_adapter		*adapter;
-+	/* number of SBALs that can be used */
-+	u8				sbal_number;
-+	/* first SBAL for this request */
-+	u8				sbal_first;
-+	/* last possible SBAL for this request */
-+	u8				sbal_last;
-+	/* current SBAL during creation of request */
-+	u8				sbal_curr;
-+	/* current SBALE during creation of request */
-+	u8				sbale_curr;
-+
-+	/* can be used by routine to wait for request completion */
-+	wait_queue_head_t completion_wq;
-+	/* status of this request */
-+	volatile u32			status;
-+	/* copy of FSF Command (avoid to touch SBAL when it is QDIO owned) */
-+	u32				fsf_command;
-+	/* address of QTCB*/
-+	fsf_qtcb_t			*qtcb;
-+	/* Sequence number used with this request */
-+	u32				seq_no;
-+	/* Information fields corresponding to the various types of request */ 
-+	zfcp_req_data_t			data;
-+	/* used if this request is issued on behalf of erp */
-+	struct _zfcp_erp_action		*erp_action;
-+	/* used if this request is alloacted from emergency pool */
-+	struct _zfcp_mem_pool		*pool;
-+} zfcp_fsf_req_t;
-+
-+typedef struct _zfcp_erp_action {
-+	struct list_head list;
-+	/* requested action */
-+	int action;
-+	/* thing which should be recovered */
-+	struct _zfcp_adapter *adapter;
-+	struct _zfcp_port *port;
-+	struct _zfcp_unit *unit;
-+	/* status of recovery */
-+	volatile u32 status;
-+	/* step which is currently taken */
-+	u32 step;
-+	/* fsf_req which is currently pending for this action */
-+	struct _zfcp_fsf_req *fsf_req;
-+	struct timer_list timer;
-+	/* retry counter, ... ? */
-+	union {
-+		/* used for nameserver requests (GID_PN) in error recovery */
-+		struct zfcp_gid_pn_data *gid_pn;
-+	} data;
-+} zfcp_erp_action_t;
-+
-+/* logical unit */
-+typedef struct _zfcp_unit {
-+	/* driver wide common magic */
-+	u32				common_magic;
-+	/* data structure specific magic */
-+	u32				specific_magic;
-+	/* list of logical units */
-+	struct list_head		list;
-+	/* remote port this logical unit belongs to */
-+	struct _zfcp_port		*port;
-+	/* status of this logical unit */
-+	atomic_t			status;
-+	/* own SCSI LUN */
-+	scsi_lun_t			scsi_lun;
-+	/* own FCP_LUN */
-+	fcp_lun_t			fcp_lun;
-+	/* handle assigned by FSF */
-+	u32				handle;
-+	/* save scsi device struct pointer locally */
-+	Scsi_Device                     *device;
-+	/* used for proc_fs support */
-+	char                            *proc_buffer;
-+	struct proc_dir_entry		*proc_file;
-+	struct proc_dir_entry		*proc_dir;
-+	/* error recovery action pending for this unit (if any) */
-+	struct _zfcp_erp_action		erp_action;
-+	atomic_t                        erp_counter;
-+	/* list of units in order of configuration via mapping */
-+	struct list_head		map_list;
-+} zfcp_unit_t;
-+
-+/* remote port */
-+typedef struct _zfcp_port {
-+	/* driver wide common magic */
-+	u32				common_magic;
-+	/* data structure specific magic */
-+	u32				specific_magic;
-+	/* list of remote ports */
-+	struct list_head		list;
-+	/* adapter this remote port accessed */
-+	struct _zfcp_adapter		*adapter;
-+	/* head of logical unit list */
-+	struct list_head		unit_list_head;
-+	/* lock for critical operations on list of logical units */
-+	rwlock_t			unit_list_lock;
-+	/* number of logical units in list */
-+	u32				units;
-+	/* status of this remote port */
-+	atomic_t			status;
-+	/* own SCSI ID */
-+	scsi_id_t			scsi_id;
-+	/* WWNN of node this remote port belongs to (if known) */
-+	wwn_t				wwnn;
-+	/* own WWPN */
-+	wwn_t				wwpn;
-+	/* D_ID */
-+	fc_id_t				d_id;
-+	/* largest SCSI LUN of units attached to this port */
-+	scsi_lun_t			max_scsi_lun;
-+	/* handle assigned by FSF */
-+	u32				handle;
-+	/* used for proc_fs support */
-+        char                            *proc_buffer;
-+	struct proc_dir_entry		*proc_file;
-+	struct proc_dir_entry		*proc_dir;
-+	/* error recovery action pending for this port (if any) */
-+	struct _zfcp_erp_action		erp_action;
-+        atomic_t                        erp_counter;
-+} zfcp_port_t;
-+
-+
-+/* QDIO request/response queue */
-+typedef struct _zfcp_qdio_queue {
-+	/* SBALs */
-+	qdio_buffer_t			*buffer[QDIO_MAX_BUFFERS_PER_Q];
-+	/* index of next free buffer in queue (only valid if free_count>0) */
-+	u8				free_index;
-+	/* number of free buffers in queue */
-+	atomic_t			free_count;
-+	/* lock for critical operations on queue */
-+	rwlock_t			queue_lock;
-+        /* outbound queue only, SBALs since PCI indication */
-+        int                             distance_from_int;
-+} zfcp_qdio_queue_t;
-+
-+
-+/* Control file data channel sense data record */
-+typedef struct _zfcp_cfdc_sense_data {
-+	/* Request signature */
-+	u32				signature;
-+	/* FCP adapter device number */
-+	u32				devno;
-+	/* Command code */
-+	u32				command;
-+	/* FSF request status */
-+	u32				fsf_status;
-+	/* FSF request status qualifier */
-+	u32				fsf_status_qual[4];
-+	/* Access conflicts list */
-+	u8				payloads[256];
-+	/* Access control table */
-+	u8				control_file[0];
-+} zfcp_cfdc_sense_data_t;
-+
-+#define ZFCP_CFDC_SIGNATURE			0xCFDCACDF
-+
-+#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL		0x00010001
-+#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE		0x00010101
-+#define ZFCP_CFDC_CMND_FULL_ACCESS		0x00000201
-+#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS	0x00000401
-+#define ZFCP_CFDC_CMND_UPLOAD			0x00010002
-+
-+#define ZFCP_CFDC_DOWNLOAD			0x00000001
-+#define ZFCP_CFDC_UPLOAD			0x00000002
-+#define ZFCP_CFDC_WITH_CONTROL_FILE		0x00010000
-+
-+#define ZFCP_CFDC_MAX_CONTROL_FILE_SIZE		127 * 1024
-+
-+
-+#define ZFCP_PARSE_ERR_BUF_SIZE 100
-+
-+/* adapter */
-+typedef struct _zfcp_adapter {
-+/* elements protected by zfcp_data.adapter_list_lock */
-+	/* driver wide common magic */
-+	u32				common_magic;
-+	/* data structure specific magic */
-+	u32				specific_magic;
-+	struct list_head		list;
-+	/* WWNN */
-+	wwn_t				wwnn;
-+	/* WWPN */
-+	wwn_t				wwpn;
-+	/* N_Port ID */
-+	fc_id_t				s_id;
-+	/* irq (subchannel) */
-+	irq_t				irq;
-+	/* device number */
-+	devno_t				devno;
-+	/* default FC service class */
-+	u8				fc_service_class;
-+	/* topology which this adapter is attached to */
-+	u32				fc_topology;
-+	/* FC interface speed */
-+	u32				fc_link_speed;
-+	/* Hydra version */
-+	u32				hydra_version;
-+	/* Licensed Internal Code version of FSF in adapter */
-+	u32				fsf_lic_version;
-+	/* supported features of FCP channel */
-+	u32				supported_features;
-+	/* hardware version of FCP channel */
-+	u32				hardware_version;
-+	/* serial number of hardware */
-+	u8				serial_number[32];
-+	/* SCSI host structure of the mid layer of the SCSI stack */
-+	struct Scsi_Host		*scsi_host;
-+	/* Start of packets in flight list */
-+	Scsi_Cmnd                       *first_fake_cmnd;
-+	/* lock for the above */
-+	rwlock_t			fake_list_lock;
-+	/* starts processing of faked commands */
-+	struct timer_list               fake_scsi_timer;
-+	atomic_t			fake_scsi_reqs_active;
-+	/* name */
-+	unsigned char			name[9];
-+	/* elements protected by port_list_lock */
-+	/* head of remote port list */
-+	struct list_head		port_list_head;
-+	/* lock for critical operations on list of remote ports */
-+	rwlock_t			port_list_lock;
-+	/* number of remote currently configured */
-+	u32				ports;
-+	/* largest SCSI ID of ports attached to this adapter */
-+	scsi_id_t			max_scsi_id;
-+	/* largest SCSI LUN of units of ports attached to this adapter */
-+	scsi_lun_t			max_scsi_lun;
-+	/* elements protected by fsf_req_list_lock */
-+	/* head of FSF request list */
-+	struct list_head		fsf_req_list_head;
-+	/* lock for critical operations on list of FSF requests */
-+	rwlock_t			fsf_req_list_lock;
-+	/* number of existing FSF requests pending */
-+	atomic_t       			fsf_reqs_active;
-+	/* elements partially protected by request_queue.lock */
-+	/* request queue */
-+	struct _zfcp_qdio_queue		request_queue;
-+	/* FSF command sequence number */
-+	u32				fsf_req_seq_no;
-+	/* can be used to wait for avaliable SBALs in request queue */
-+	wait_queue_head_t		request_wq;
-+	/* elements partially protected by response_queue.lock */
-+	/* response queue */
-+	struct _zfcp_qdio_queue		response_queue;
-+	devstat_t			devstat;
-+	s390_dev_info_t                 devinfo;
-+	rwlock_t			abort_lock;
-+	/* number of status reads failed */
-+	u16				status_read_failed;
-+	/* elements which various bits are protected by several locks */
-+	/* status of this adapter */
-+	atomic_t			status;
-+	/* for proc_info */
-+	char                            *proc_buffer;
-+	/* and here for the extra proc_dir */
-+	struct proc_dir_entry 		*proc_dir;
-+	struct proc_dir_entry		*proc_file;
-+	/* nameserver avaliable via this adapter */
-+	struct _zfcp_port		*nameserver_port;
-+	/* error recovery for this adapter and associated devices */
-+	struct list_head		erp_ready_head;
-+	struct list_head		erp_running_head;
-+	rwlock_t			erp_lock;
-+	struct semaphore		erp_ready_sem;
-+	wait_queue_head_t		erp_thread_wqh;
-+	wait_queue_head_t		erp_done_wqh;
-+	/* error recovery action pending for this adapter (if any) */
-+	struct _zfcp_erp_action		erp_action;
-+	atomic_t                        erp_counter;
-+	debug_info_t                    *erp_dbf;
-+	debug_info_t			*abort_dbf;
-+	debug_info_t                    *req_dbf;
-+	debug_info_t			*in_els_dbf;
-+	debug_info_t			*cmd_dbf;
-+	rwlock_t			cmd_dbf_lock;
-+	zfcp_adapter_mem_pool_t		pool;
-+	/* SCSI error recovery watch */
-+	struct timer_list               scsi_er_timer;
-+	/* Used to handle buffer positioning when reopening queues*/
-+	atomic_t                        reqs_in_progress;
-+#ifdef ZFCP_ERP_DEBUG_SINGLE_STEP
-+	struct				semaphore erp_continue_sem;
-+#endif /* ZFCP_ERP_DEBUG_SINGLE_STEP */
-+#ifdef ZFCP_STAT_REQSIZES
-+	struct list_head		read_req_head;
-+	struct list_head		write_req_head;
-+	rwlock_t			stat_lock;
-+	atomic_t			stat_errors;
-+	atomic_t			stat_on;
-+#endif
-+#ifdef ZFCP_STAT_QUEUES
-+	atomic_t                        outbound_queue_full;
-+	atomic_t			outbound_total;
-+#endif
-+} zfcp_adapter_t;
-+
-+/* driver data */
-+typedef struct _zfcp_data {
-+	/* SCSI stack data structure storing information about this driver */
-+	Scsi_Host_Template		scsi_host_template;
-+	/* head of adapter list */
-+	struct list_head		adapter_list_head;
-+	/* lock for critical operations on list of adapters */
-+	rwlock_t			adapter_list_lock;
-+	/* number of adapters in list */
-+	u16				adapters;
-+	/* data used for dynamic I/O */
-+	devreg_t			devreg;
-+	devreg_t			devreg_priv;
-+	/* driver version number derived from cvs revision */
-+	u32				driver_version;
-+	/* serialises proc-fs/configuration changes */
-+	struct semaphore                proc_sema;
-+	/* for proc_info */
-+	char                            *proc_buffer_parm;
-+	char                            *proc_buffer_map;
-+	char                            *proc_line;
-+	unsigned long                   proc_line_length;
-+	/* and here for the extra proc_dir */
-+	struct proc_dir_entry		*proc_dir;
-+	struct proc_dir_entry 		*parm_proc_file;
-+	struct proc_dir_entry 		*map_proc_file;
-+	struct proc_dir_entry 		*add_map_proc_file;
-+	/* buffer for parse error messages (don't want to put it on stack) */
-+	unsigned char 			perrbuf[ZFCP_PARSE_ERR_BUF_SIZE];
-+	atomic_t                        mem_count;
-+	struct notifier_block		reboot_notifier;
-+	atomic_t			loglevel;
-+#ifdef ZFCP_LOW_MEM_CREDITS
-+	atomic_t			lowmem_credit;
-+#endif
-+	/* no extra lock here, we have the proc_sema */
-+	struct list_head		map_list_head;
-+} zfcp_data_t;
-+
-+/* struct used by memory pools for fsf_requests */
-+struct zfcp_fsf_req_pool_buffer {
-+	struct _zfcp_fsf_req fsf_req;
-+	struct fsf_qtcb qtcb;
-+};
-+
-+/* record generated from parsed conf. lines */
-+typedef struct _zfcp_config_record {
-+	int			valid;
-+	unsigned long		devno;
-+	unsigned long		scsi_id;
-+	unsigned long long	wwpn;
-+	unsigned long		scsi_lun;
-+	unsigned long long	fcp_lun;
-+} zfcp_config_record_t;
-+
-+/* for use by zfcp_sg_list_...() */
-+typedef struct _zfcp_sg_list {
-+	struct scatterlist *sg;
-+	unsigned int count;
-+} zfcp_sg_list_t;
-+
-+extern zfcp_data_t zfcp_data;
-+
-+#ifdef ZFCP_LOW_MEM_CREDITS
-+/* only substract i from v if v is not equal to no_sub; returns 0 then, 1 otherwise */
-+static __inline__ int atomic_test_and_sub(int no_sub, int i, atomic_t *v)
-+{
-+        int old_val, new_val;
-+	do {
-+		old_val = atomic_read(v);
-+		if (old_val == no_sub)
-+			return 1;
-+		new_val = old_val - i;
-+	} while (atomic_compare_and_swap(old_val, new_val, v));
-+        return 0;
-+}
-+
-+/* only decrement v if v is not equal to no_dec; returns 0 then, 1 otherwise */
-+static __inline__ int atomic_test_and_dec(int no_dec, atomic_t *v)
-+{
-+	return atomic_test_and_sub(no_dec, 1, v);
-+}
-+#endif
-+
-+#ifndef atomic_test_mask
-+#define atomic_test_mask(mask, target) \
-+           ((atomic_read(target) & mask) == mask)
-+#endif
-+
-+/*
-+ * Macros used for logging etc.
-+ */
-+
-+#define ZFCP_NAME			"zfcp"
-+
-+/*
-+ * Logging may be applied on certain kinds of driver operations
-+ * independently. Besides different log levels are supported for
-+ * each of these areas.
-+ */
-+
-+/* independent areas being subject of logging */
-+#define ZFCP_LOG_AREA_OTHER	0
-+#define ZFCP_LOG_AREA_SCSI	1
-+#define ZFCP_LOG_AREA_FSF	2
-+#define ZFCP_LOG_AREA_CONFIG	3
-+#define ZFCP_LOG_AREA_DIO	4
-+#define ZFCP_LOG_AREA_QDIO	5
-+#define ZFCP_LOG_AREA_ERP	6
-+
-+/* values for log level - keep it simple for now */
-+#define ZFCP_LOG_LEVEL_NORMAL	0
-+#define ZFCP_LOG_LEVEL_INFO	1
-+#define ZFCP_LOG_LEVEL_DEBUG	2
-+#define ZFCP_LOG_LEVEL_TRACE	3
-+
-+/* default log levels for different log areas */
-+#define ZFCP_LOG_LEVEL_DEFAULT_OTHER	ZFCP_LOG_LEVEL_NORMAL
-+#define ZFCP_LOG_LEVEL_DEFAULT_SCSI	ZFCP_LOG_LEVEL_NORMAL
-+#define ZFCP_LOG_LEVEL_DEFAULT_FSF	ZFCP_LOG_LEVEL_NORMAL
-+#define ZFCP_LOG_LEVEL_DEFAULT_CONFIG	ZFCP_LOG_LEVEL_NORMAL
-+#define ZFCP_LOG_LEVEL_DEFAULT_DIO	ZFCP_LOG_LEVEL_NORMAL
-+#define ZFCP_LOG_LEVEL_DEFAULT_QDIO	ZFCP_LOG_LEVEL_NORMAL
-+#define ZFCP_LOG_LEVEL_DEFAULT_ERP	ZFCP_LOG_LEVEL_NORMAL
-+
-+/*
-+ * this allows to remove excluded logs from the code by the preprocessor
-+ * (this is the last log level compiled in, higher log levels are removed)
-+ */
-+#define ZFCP_LOG_LEVEL_LIMIT	ZFCP_LOG_LEVEL_DEBUG
-+
-+/* nibbles of "loglevel" are used for particular purposes */
-+#define ZFCP_LOG_VALUE(zfcp_lognibble) \
-+		((atomic_read(&zfcp_data.loglevel) >> (zfcp_lognibble<<2)) & 0xF)
-+
-+#define ZFCP_LOG_VALUE_OTHER	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_OTHER)
-+#define ZFCP_LOG_VALUE_SCSI	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_SCSI)
-+#define ZFCP_LOG_VALUE_FSF	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_FSF)
-+#define ZFCP_LOG_VALUE_CONFIG	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_CONFIG)
-+#define ZFCP_LOG_VALUE_DIO	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_DIO)
-+#define ZFCP_LOG_VALUE_QDIO	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_QDIO)
-+#define ZFCP_LOG_VALUE_ERP	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_ERP)
-+
-+/* all log level defaults put together into log level word */
-+#define ZFCP_LOG_LEVEL_DEFAULTS \
-+	((ZFCP_LOG_LEVEL_DEFAULT_OTHER	<< (ZFCP_LOG_AREA_OTHER<<2))	| \
-+	 (ZFCP_LOG_LEVEL_DEFAULT_SCSI	<< (ZFCP_LOG_AREA_SCSI<<2))	| \
-+	 (ZFCP_LOG_LEVEL_DEFAULT_FSF	<< (ZFCP_LOG_AREA_FSF<<2))	| \
-+	 (ZFCP_LOG_LEVEL_DEFAULT_CONFIG	<< (ZFCP_LOG_AREA_CONFIG<<2))	| \
-+	 (ZFCP_LOG_LEVEL_DEFAULT_DIO	<< (ZFCP_LOG_AREA_DIO<<2))	| \
-+	 (ZFCP_LOG_LEVEL_DEFAULT_QDIO	<< (ZFCP_LOG_AREA_QDIO<<2))	| \
-+	 (ZFCP_LOG_LEVEL_DEFAULT_ERP	<< (ZFCP_LOG_AREA_ERP<<2)))
-+
-+/* that's the prefix placed at the beginning of each driver message */
-+#define ZFCP_LOG_PREFIX ZFCP_NAME": "
-+
-+/* log area specific log prefixes */
-+#define ZFCP_LOG_AREA_PREFIX_OTHER	""
-+#define ZFCP_LOG_AREA_PREFIX_SCSI	"SCSI: "
-+#define ZFCP_LOG_AREA_PREFIX_FSF	"FSF: "
-+#define ZFCP_LOG_AREA_PREFIX_CONFIG	"config: "
-+#define ZFCP_LOG_AREA_PREFIX_DIO	"dynamic I/O: "
-+#define ZFCP_LOG_AREA_PREFIX_QDIO	"QDIO: "
-+#define ZFCP_LOG_AREA_PREFIX_ERP	"ERP: "
-+
-+/* check whether we have the right level for logging */
-+#define ZFCP_LOG_CHECK(ll)	(ZFCP_LOG_VALUE(ZFCP_LOG_AREA)) >= ll
-+
-+/* As we have two printks it is possible for them to be seperated by another
-+ * message. This holds true even for printks from within this module.
-+ * In any case there should only be a small readability hit, however.
-+ */
-+#define _ZFCP_LOG(m...) \
-+		{ \
-+			printk( "%s%s: ", \
-+				ZFCP_LOG_PREFIX ZFCP_LOG_AREA_PREFIX, \
-+				__FUNCTION__); \
-+			printk(m); \
-+		}
-+
-+#define ZFCP_LOG(ll, m...) \
-+		if (ZFCP_LOG_CHECK(ll)) \
-+			_ZFCP_LOG(m)
-+	
-+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL
-+#define ZFCP_LOG_NORMAL(m...)
-+#else	/* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_NORMAL */
-+#define ZFCP_LOG_NORMAL(m...)		ZFCP_LOG(ZFCP_LOG_LEVEL_NORMAL, m)
-+#endif
-+
-+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO
-+#define ZFCP_LOG_INFO(m...)
-+#else	/* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_INFO */
-+#define ZFCP_LOG_INFO(m...)		ZFCP_LOG(ZFCP_LOG_LEVEL_INFO, m)
-+#endif
-+
-+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG
-+#define ZFCP_LOG_DEBUG(m...)
-+#else	/* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_DEBUG */
-+#define ZFCP_LOG_DEBUG(m...)		ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, m)
-+#endif
-+
-+#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE
-+#define ZFCP_LOG_TRACE(m...)
-+#else	/* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_TRACE */
-+#define ZFCP_LOG_TRACE(m...)		ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, m)
-+#endif
-+
-+/**************** memory management wrappers ************************/
-+
-+#define ZFCP_KMALLOC(params...)		zfcp_kmalloc(params, __FUNCTION__)
-+#define ZFCP_KFREE(params...)		zfcp_kfree(params, __FUNCTION__)
-+#define ZFCP_GET_ZEROED_PAGE(params...)	zfcp_get_zeroed_page(params, __FUNCTION__)
-+#define ZFCP_FREE_PAGE(params...)	zfcp_free_page(params, __FUNCTION__)
-+
-+static inline void *zfcp_kmalloc(size_t size, int type, char *origin)
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+        void *ret = NULL;
-+#if 0
-+        if (error_counter>=10000) {
-+                if(error_counter==10000) {
-+                        printk("********LOW MEMORY********\n");
-+                }
-+                error_counter=10001;
-+                goto out;
-+        }
-+#endif
-+
-+#ifdef ZFCP_LOW_MEM_CREDITS
-+	if (!atomic_test_and_dec(0, &zfcp_data.lowmem_credit))
-+		return NULL;
-+#endif
-+
-+        ret = kmalloc(size, type);
-+        if (ret) {
-+                atomic_add(size, &zfcp_data.mem_count);
-+		memset(ret, 0, size);
-+        }
-+#ifdef ZFCP_MEMORY_DEBUG
-+	/* FIXME(design): shouldn't this rather be a dbf entry? */
-+        ZFCP_LOG_NORMAL(
-+		"origin: %s, addr=0x%lx, size=%li, type=%d\n",
-+		origin,
-+		(unsigned long)ret,
-+		size,
-+		type);
-+#endif
-+        /* out: */
-+        return ret;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+static inline unsigned long zfcp_get_zeroed_page(int flags, char *origin) 
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+        unsigned long ret = 0;
-+#if 0
-+        if (error_counter>=10000) {
-+                if(error_counter==10000) {
-+                        printk("********LOW MEMORY********\n");
-+                }
-+                error_counter=10001;
-+                goto out;
-+        }
-+#endif
-+        ret = get_zeroed_page(flags);
-+        if (ret) {
-+                atomic_add(PAGE_SIZE, &zfcp_data.mem_count);
-+        }
-+
-+#ifdef ZFCP_MEMORY_DEBUG
-+	/* FIXME(design): shouldn't this rather be a dbf entry? */
-+        ZFCP_LOG_NORMAL(
-+		"origin=%s, addr=0x%lx, type=%d\n",
-+		origin,
-+		ret,
-+		flags);
-+#endif
-+        /* out :*/
-+        return ret;
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+/*
-+ * Note:
-+ * 'kfree' may free a different amount of storage than specified here by
-+ * 'size' since 'kfree' has its own means to figure this number out.
-+ * Thus, an arbitrary value assigned to 'size' (usage error) will
-+ * mess up our storage accounting even in cases of no memory leaks.
-+ */
-+static inline void zfcp_kfree(void *addr, size_t size, char *origin) 
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+        atomic_sub(size, &zfcp_data.mem_count);
-+#ifdef ZFCP_MEMORY_DEBUG
-+	/* FIXME(design): shouldn't this rather be a dbf entry? */
-+        ZFCP_LOG_NORMAL(
-+		"origin: %s, addr=0x%lx, count=%ld \n",
-+		origin,
-+		(unsigned long)addr,
-+                size);
-+#endif
-+        kfree(addr);
-+
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+
-+static inline void zfcp_free_page(unsigned long addr, char *origin) 
-+{
-+#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
-+#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
-+
-+        atomic_sub(PAGE_SIZE, &zfcp_data.mem_count);
-+#ifdef ZFCP_MEMORY_DEBUG
-+        ZFCP_LOG_NORMAL("origin: %s, addr=0x%lx\n",
-+                        origin,
-+                        addr);
-+#endif
-+        free_page(addr);
-+        
-+#undef ZFCP_LOG_AREA
-+#undef ZFCP_LOG_AREA_PREFIX
-+}
-+
-+int	zfcp_config_parse_record_add (zfcp_config_record_t*);
-+
-+#define ZFCP_FIRST_ENTITY(head,type) \
-+	( \
-+		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_ */
-=== drivers/s390/scsi/zfcp_zh.h
-==================================================================
---- drivers/s390/scsi/zfcp_zh.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/scsi/zfcp_zh.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,171 @@
-+/* 
-+ * $Id: zfcp_zh.h,v 1.3.2.1 2004/01/26 17:26:34 mschwide Exp $
-+ *
-+ * Module providing an interface for HBA API (FC-HBA) implementation
-+ * to the zfcp driver.
-+ *
-+ * (C) Copyright IBM Corp. 2002, 2003
-+ *
-+ * 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 of the License, or
-+ * (at your option) any later version. See the file COPYING for more
-+ * information.
-+ *
-+ * Authors:
-+ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
-+ *       Andreas Herrmann <aherrman at de.ibm.com>
-+ */
-+
-+
-+#ifndef _ZFCP_ZH_H_
-+#define _ZFCP_ZH_H_
-+
-+#include "zfcp.h"
-+#include <scsi/scsi_ioctl.h>
-+#include <asm/scatterlist.h>
-+
-+/*
-+ * Besides a punch of standard error codes we use some newly defined error
-+ * codes.
-+ */
-+#define ENOADA 200 /* no such adapter */
-+#define ENOPOR 201 /* no such port */
-+#define ENOUNI 202 /* no such unit */
-+
-+/*
-+ * flags for get_config
-+ * */
-+#define ZH_GET_CONFIG_ADAPTERS 0
-+#define ZH_GET_CONFIG_PORTS 1
-+#define ZH_GET_CONFIG_UNITS 2
-+
-+/**
-+ * struct zfcp_zh_callbacks
-+ * @adapter_add: callback for adapter add events
-+ *
-+ * structure containing all the callbacks
-+ */
-+struct zfcp_zh_callbacks
-+{
-+	void (*adapter_add) (struct file *, devno_t, wwn_t, wwn_t);
-+	void (*port_add) (struct file *, devno_t, wwn_t, wwn_t, fc_id_t);
-+	void (*unit_add) (struct file *, devno_t, wwn_t, fcp_lun_t,
-+			  unsigned int, unsigned int,
-+			  unsigned int, unsigned int);
-+	void (*incomming_els) (const devno_t, const fc_id_t, const void *);
-+	void (*link_down) (const fc_id_t);
-+	void (*link_up) (const fc_id_t);
-+};
-+
-+/**
-+ * struct zfcp_callbacks
-+ * @lock: rw-lock
-+ * @callbacks: relevant callbacks into zh module
-+ *
-+ * callbacks and according lock
-+ */
-+struct zfcp_callbacks
-+{
-+	rwlock_t lock;
-+	struct zfcp_zh_callbacks *callbacks;
-+};
-+
-+extern struct zfcp_callbacks zfcp_callback;
-+
-+/**
-+ * struct zfcp_adapter_attributes
-+ */
-+struct zfcp_adapter_attributes
-+{
-+	char		manufacturer[64];
-+	char		serial_number[64];
-+	char		model[256];
-+	char		model_description[256];
-+	wwn_t		node_wwn;
-+	char		node_symbolic_name[256];
-+	char		hardware_version[256];
-+	char		driver_version[256];
-+	char		option_rom_version[256];
-+	char		firmware_version[256];
-+	u32		vendor_specific_id;
-+	u32		number_of_ports;
-+	char		driver_name[256];
-+};
-+
-+/**
-+ * struct zfcp_port_attributes
-+ */
-+struct zfcp_port_attributes {
-+	wwn_t wwnn;
-+	wwn_t wwpn;
-+	wwn_t fabric_name;
-+	u32 fcid;
-+	u32 type;
-+	u32 state;
-+	u32 supported_class_of_service;
-+	u32 supported_speed;
-+	u32 speed;
-+	u32 max_frame_size;
-+	u32 discovered_ports;
-+	u8 supported_fc4_types[32];
-+	u8 active_fc4_types[32];
-+	char symbolic_name[256];
-+};
-+
-+/**
-+ * struct zfcp_port_statistics
-+ */
-+struct zfcp_port_statistics {
-+	u64 last_reset;
-+	u64 tx_frames;
-+	u64 tx_words;
-+	u64 rx_frames;
-+	u64 rx_words;
-+	u64 lip;
-+	u64 nos;
-+	u64 error_frames;
-+	u64 dumped_frames;
-+	u64 link_failure;
-+	u64 loss_of_sync;
-+	u64 loss_of_signal;
-+	u64 prim_seq_prot_error;
-+	u64 invalid_tx_words;
-+	u64 invalid_crc;
-+	u64 input_requests;
-+	u64 output_requests;
-+	u64 control_requests;
-+	u64 input_megabytes;
-+	u64 output_megabytes;
-+};
-+
-+/*
-+ * functions needed by zfcp_hbaapi in zfcp
-+ */
-+int zfcp_zh_callbacks_register(struct zfcp_zh_callbacks *);
-+int zfcp_zh_callbacks_unregister(struct zfcp_zh_callbacks *);
-+int zfcp_zh_get_config(struct file *, devno_t, wwn_t, unsigned int);
-+int zfcp_zh_get_adapter_attributes(devno_t, struct zfcp_adapter_attributes *);
-+int zfcp_zh_get_port_attributes(devno_t, struct zfcp_port_attributes *);
-+int zfcp_zh_get_port_statistics(devno_t, struct zfcp_port_statistics *);
-+int zfcp_zh_get_dport_attributes(devno_t, wwn_t, struct zfcp_port_attributes *);
-+int zfcp_zh_send_ct(devno_t, struct scatterlist *, unsigned int,
-+		    struct scatterlist *, unsigned int);
-+int zfcp_zh_send_els(devno_t, wwn_t, struct scatterlist*, unsigned int,
-+		     struct scatterlist*, unsigned int);
-+int zfcp_zh_send_scsi(devno_t, wwn_t, fcp_lun_t, Scsi_Cmnd *);
-+int zfcp_zh_assert_fclun_zero(devno_t, wwn_t); /* needed for ReportLUNs */
-+
-+/*
-+ * functions needed for callbacks into zfcp_hbaapi
-+ */
-+void zfcp_callback_do_adapter_add(struct file *, const zfcp_adapter_t *);
-+void zfcp_callback_do_port_add(struct file *, const zfcp_adapter_t *,
-+			       const zfcp_port_t *);
-+void zfcp_callback_do_unit_add(struct file *, const zfcp_adapter_t *,
-+			       const zfcp_port_t *, const zfcp_unit_t *);
-+void zfcp_callback_do_incomming_els(const zfcp_adapter_t *, const void *);
-+void zfcp_callback_do_link_down(const zfcp_adapter_t *);
-+void zfcp_callback_do_link_up(const zfcp_adapter_t *);
-+
-+#endif /* _ZFCP_ZH_H_ */
-=== drivers/s390/scsi/Makefile
-==================================================================
---- drivers/s390/scsi/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/scsi/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,25 @@
-+#
-+# Makefile for the S/390 specific device drivers
-+#
-+
-+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)
-+
-=== drivers/s390/cmf.c
-==================================================================
---- drivers/s390/cmf.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/cmf.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,701 @@
-+/*
-+ * linux/drivers/s390/cmf.c ($Revision: 1.5.6.2 $)
-+ *
-+ * Linux on zSeries Channel Measurement Facility support
-+ *
-+ * Copyright 2000,2003 IBM Corporation
-+ *
-+ * Author: Arnd Bergmann <arndb at de.ibm.com>
-+ *
-+ * original idea from Natarajan Krishnaswami <nkrishna at us.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/init.h>
-+#include <linux/list.h>
-+#include <linux/module.h>
-+#include <linux/slab.h>
-+
-+#include <asm/cmb.h>
-+#include <asm/div64.h>
-+#include <asm/irq.h>
-+
-+/**
-+ * enum cmb_format - types of supported measurement block formats
-+ *
-+ * @CMF_BASIC:      traditional channel measurement blocks supported
-+ * 		    by all machines that we run on
-+ * @CMF_EXTENDED:   improved format that was introduced with the z990
-+ * 		    machine
-+ * @CMF_AUTODETECT: default: use extended format when running on a z990
-+ *                  or later machine, otherwise fall back to basic format
-+ **/
-+enum cmb_format {
-+	CMF_BASIC,
-+	CMF_EXTENDED,
-+	CMF_AUTODETECT = -1,
-+};
-+/**
-+ * format - actual format for all measurement blocks
-+ * 
-+ * The format module parameter can be set to a value of 0 (zero)
-+ * or 1, indicating basic or extended format as described for
-+ * enum cmb_format.
-+ */
-+static int format = CMF_AUTODETECT;
-+MODULE_PARM(format, "i");
-+
-+/**
-+ * struct cmb_operations - functions to use depending on cmb_format
-+ *
-+ * all these functions operate on a struct cmf_device. There is only
-+ * one instance of struct cmb_operations because all cmf_device 
-+ * objects are guaranteed to be of the same type.
-+ *
-+ * @alloc:	allocate memory for a channel measurement block,
-+ *		either with the help of a special pool or with kmalloc
-+ * @free:	free memory allocated with @alloc
-+ * @set:	enable or disable measurement
-+ * @readall:	read a measurement block in a common format
-+ * @reset:	clear the data in the associated measurement block and
-+ *		reset its time stamp
-+ */
-+struct cmb_operations {
-+	int (*alloc)  (struct cmf_device*);
-+	void(*free)   (struct cmf_device*);
-+	int (*set)    (struct cmf_device*, u32);
-+	int (*readall)(struct cmf_device*, struct cmbdata *);
-+	void (*reset) (struct cmf_device*);
-+};
-+static struct cmb_operations *cmbops;
-+
-+/* our user interface is designed in terms of nanoseconds,
-+ * while the hardware measures total times in its own
-+ * unit.*/
-+static inline u64 time_to_nsec(u32 value)
-+{
-+	return ((u64)value) * 128000ull;
-+}
-+
-+/*
-+ * Users are usually interested in average times,
-+ * not accumulated time.
-+ * This also helps us with atomicity problems
-+ * when reading sinlge values.
-+ */
-+static inline u64 time_to_avg_nsec(u32 value, u32 count)
-+{
-+	u64 ret;
-+
-+	/* no samples yet, avoid division by 0 */
-+	if (count == 0)
-+		return 0;
-+
-+	/* value comes in units of 128 µsec */
-+	ret = time_to_nsec(value);
-+	do_div(ret, count);
-+
-+	return ret;
-+}
-+
-+/* activate or deactivate the channel monitor. When area is NULL,
-+ * the monitor is deactivated. The channel monitor needs to
-+ * be active in order to measure subchannels, which also need
-+ * to be enabled. */
-+static inline void
-+cmf_activate(void *area, unsigned int onoff)
-+{
-+	register void * __gpr2 asm("2");
-+	register long __gpr1 asm("1");
-+
-+	__gpr2 = area;
-+	__gpr1 = onoff ? 2 : 0;
-+	/* activate channel measurement */
-+	asm("schm" : : "d" (__gpr2), "d" (__gpr1) );
-+}
-+
-+static int
-+set_schib(int sch, u32 mme, int mbfc, unsigned long address)
-+{
-+	int ret;
-+	int retry;
-+	schib_t *schib;
-+	unsigned long mba, mbi;
-+
-+	/* address can be either a block address or a block index */
-+	mba = mbi = 0;
-+	(mbfc ? mba : mbi) = address;
-+
-+	/* msch can silently fail, so do it again if necessary */
-+	for (retry = 0; retry < 3; retry++) {
-+		/* prepare schib */
-+		schib = &ioinfo[sch]->schib;
-+		stsch(sch, schib);
-+		schib->pmcw.mme  = mme;
-+		schib->pmcw.mbfc = mbfc;
-+		schib->pmcw.mbi  = mbi;
-+		if (mbfc)
-+			schib->mba	 = mba;
-+
-+		/* try to submit it */
-+		switch(ret = msch_err(sch, schib)) {
-+			case 0:
-+				break;
-+			case 1:
-+			case 2: /* in I/O or status pending */
-+				ret = -EBUSY;
-+				break;
-+			case 3: /* subchannel is no longer valid */
-+				ret = -ENODEV;
-+				break;
-+			default: /* msch caught an exception */
-+				ret = -EINVAL;
-+				break;
-+		}
-+		stsch(sch, schib); /* restore the schib */
-+		
-+		if (ret)
-+			break;
-+		
-+		/* check if it worked */
-+		if (schib->pmcw.mme  == mme &&
-+		    schib->pmcw.mbfc == mbfc &&
-+		    schib->pmcw.mbi == mbi &&
-+		    (!mbfc || schib->mba == mba)) 
-+			return 0;
-+
-+		ret = -EINVAL;
-+	}
-+
-+	return ret;
-+}
-+
-+/**
-+ * struct cmb_area - container for global cmb data
-+ *
-+ * @mem:	pointer to CMBs (only in basic measurement mode)
-+ * @list:	contains a linked list of all subchannels
-+ * @lock:	protect concurrent access to @mem and @list
-+ */
-+struct cmb_area {
-+	struct cmb *mem;
-+	struct list_head list;
-+	spinlock_t lock;
-+};
-+static struct cmb_area cmb_area = {
-+	.lock = SPIN_LOCK_UNLOCKED,
-+	.list = LIST_HEAD_INIT(cmb_area.list),
-+};
-+
-+
-+/* ****** old style CMB handling ********/
-+
-+/** int maxchannels
-+ *
-+ * Basic channel measurement blocks are allocated in one contiguous
-+ * block of memory, which can not be moved as long as any channel
-+ * is active. Therefore, a maximum number of subchannels needs to
-+ * be defined somewhere. This is a module parameter, defaulting to
-+ * a resonable value of 1024, or 32 kb of memory.
-+ * Current kernels don't allow kmalloc with more than 128kb, so the
-+ * maximum is 4096
-+ */
-+static int maxchannels = 1024;
-+MODULE_PARM(maxchannels,"i");
-+
-+/**
-+ * struct cmb - basic channel measurement block
-+ *
-+ * cmb as used by the hardware the fields are described in z/Architecture
-+ * Principles of Operation, chapter 17.
-+ * The area to be a contiguous array and may not be reallocated or freed.
-+ * Only one cmb area can be present in the system.
-+ */
-+struct cmb {
-+	u16 ssch_rsch_count;
-+	u16 sample_count;
-+	u32 device_connect_time;
-+	u32 function_pending_time;
-+	u32 device_disconnect_time;
-+	u32 control_unit_queuing_time;
-+	u32 device_active_only_time;
-+	u32 reserved[2];
-+};
-+
-+/* insert a single device into the cmb_area list
-+ * called with cmb_area.lock held from alloc_cmb
-+ */
-+static inline int
-+alloc_cmb_single (struct cmf_device *cdev)
-+{
-+	struct cmb *cmb;
-+	struct cmf_device *node;
-+	int ret;
-+
-+	spin_lock_irq(cdev->ccwlock);
-+	if (!list_empty(&cdev->cmb_list)) {
-+		ret = -EBUSY;
-+		goto out;
-+	}
-+
-+	/* find first unused cmb in cmb_area.mem.
-+	 * this is a little tricky: cmb_area.list
-+	 * remains sorted by ->cmb pointers */
-+	cmb = cmb_area.mem;
-+	list_for_each_entry(node, &cmb_area.list, cmb_list) {
-+		if ((struct cmb*)node->cmb > cmb)
-+			break;
-+		cmb++;
-+	}
-+	if (cmb - cmb_area.mem >= maxchannels) {
-+		ret = -ENOMEM;
-+		goto out;
-+	}
-+
-+	/* insert new cmb */
-+	list_add_tail(&cdev->cmb_list, &node->cmb_list);
-+	cdev->cmb = cmb;
-+	ret = 0;
-+out:
-+	spin_unlock_irq(cdev->ccwlock);
-+	return ret;
-+}
-+
-+static int
-+alloc_cmb (struct cmf_device *cdev)
-+{
-+	int ret;
-+	struct cmb *mem;
-+	ssize_t size;
-+
-+	spin_lock(&cmb_area.lock);
-+
-+	if (!cmb_area.mem) {
-+		/* there is no user yet, so we need a new area */
-+		size = sizeof(struct cmb) * maxchannels;
-+		BUG_ON(!list_empty(&cmb_area.list));
-+
-+		spin_unlock(&cmb_area.lock);
-+		mem = kmalloc(size, GFP_KERNEL | GFP_DMA);
-+		spin_lock(&cmb_area.lock);
-+
-+		if (cmb_area.mem) {
-+			/* ok, another thread was faster */
-+			kfree(mem);
-+		} else if (!mem) {
-+			/* no luck */
-+			ret = -ENOMEM;
-+			goto out;
-+		} else {
-+			/* everything ok */
-+			memset(mem, 0, size);
-+			cmb_area.mem = mem;
-+			cmf_activate(cmb_area.mem, 1);
-+		}
-+	}
-+
-+	/* do the actual allocation */
-+	ret = alloc_cmb_single(cdev);
-+out:
-+	spin_unlock(&cmb_area.lock);
-+
-+	return ret;
-+}
-+
-+static void
-+free_cmb(struct cmf_device *cdev)
-+{
-+	spin_lock(&cmb_area.lock);
-+	spin_lock_irq(cdev->ccwlock);
-+
-+	if (list_empty(&cdev->cmb_list)) {
-+		/* already freed */
-+		goto out;
-+	}
-+
-+	cdev->cmb = NULL;
-+	list_del_init(&cdev->cmb_list);
-+
-+	if (list_empty(&cmb_area.list)) {
-+		cmf_activate(NULL, 0);
-+		kfree(cmb_area.mem);
-+		cmb_area.mem = NULL;
-+	}
-+out:
-+	spin_unlock_irq(cdev->ccwlock);
-+	spin_unlock(&cmb_area.lock);
-+}
-+
-+static int
-+set_cmb(struct cmf_device *cdev, u32 mme)
-+{
-+	u16 offset;
-+
-+	if (!cdev->cmb)
-+		return -EINVAL;
-+
-+	offset = mme ? (struct cmb *)cdev->cmb - cmb_area.mem : 0;
-+
-+	return set_schib(cdev->irq, mme, 0, offset);
-+}
-+
-+static int
-+readall_cmb (struct cmf_device *cdev, struct cmbdata *data)
-+{
-+	/* yes, we have to put it on the stack
-+	 * because the cmb must only be accessed
-+	 * atomically, e.g. with mvc */
-+	struct cmb cmb;
-+	unsigned long flags;
-+	u64 time;
-+
-+	spin_lock_irqsave(cdev->ccwlock, flags);
-+	if (!cdev->cmb) {
-+		spin_unlock_irqrestore(cdev->ccwlock, flags);
-+		return -ENODEV;
-+	}
-+
-+	cmb = *(struct cmb*)cdev->cmb;
-+	time = get_clock() - cdev->cmb_start_time;
-+	spin_unlock_irqrestore(cdev->ccwlock, flags);
-+
-+	*data = (struct cmbdata) {
-+		/* we only know values before device_busy_time */
-+		.size = offsetof(struct cmbdata, device_busy_time),
-+
-+		/* conver to nanoseconds */
-+		.elapsed_time = (time * 1000) >> 12,
-+
-+		/* copy data to new structure */
-+		.ssch_rsch_count		= cmb.ssch_rsch_count,
-+		.sample_count			= cmb.sample_count,
-+
-+		/* time fields are converted to nanoseconds while copying */
-+		.device_connect_time
-+			= time_to_nsec(cmb.device_connect_time),
-+		.function_pending_time
-+			= time_to_nsec(cmb.function_pending_time),
-+		.device_disconnect_time
-+			= time_to_nsec(cmb.device_disconnect_time),
-+		.control_unit_queuing_time
-+		 	= time_to_nsec(cmb.control_unit_queuing_time),
-+		.device_active_only_time
-+			= time_to_nsec(cmb.device_active_only_time),
-+	};
-+
-+	return 0;
-+}
-+
-+static void
-+reset_cmb(struct cmf_device *cdev)
-+{
-+	struct cmb *cmb;
-+	spin_lock_irq(cdev->ccwlock);
-+	cmb = cdev->cmb;
-+	if (cmb)
-+		memset (cmb, 0, sizeof (*cmb));
-+	cdev->cmb_start_time = get_clock();
-+	spin_unlock_irq(cdev->ccwlock);
-+}
-+
-+static struct cmb_operations cmbops_basic = {
-+	.alloc	= alloc_cmb,
-+	.free	= free_cmb,
-+	.set	= set_cmb,
-+	.readall= readall_cmb,
-+	.reset	= reset_cmb,
-+};
-+
-+/* ******** extended cmb handling ********/
-+
-+/**
-+ * struct cmbe - extended channel measurement block
-+ *
-+ * cmb as used by the hardware, may be in any 64 bit physical location,
-+ * the fields are described in z/Architecture Principles of Operation,
-+ * third edition, chapter 17.
-+ */
-+struct cmbe {
-+	u32 ssch_rsch_count;
-+	u32 sample_count;
-+	u32 device_connect_time;
-+	u32 function_pending_time;
-+	u32 device_disconnect_time;
-+	u32 control_unit_queuing_time;
-+	u32 device_active_only_time;
-+	u32 device_busy_time;
-+	u32 initial_command_response_time;
-+	u32 reserved[7];
-+};
-+
-+static int
-+alloc_cmbe (struct cmf_device *cdev)
-+{
-+	struct cmbe *cmbe;
-+	cmbe = kmalloc (sizeof (*cmbe), GFP_KERNEL /* | GFP_DMA ? */);
-+	if (!cmbe)
-+		return -ENOMEM;
-+
-+	spin_lock_irq(cdev->ccwlock);
-+	if (cdev->cmb)
-+		kfree(cmbe);
-+	else
-+		cdev->cmb = cmbe;
-+	spin_unlock_irq(cdev->ccwlock);
-+
-+	/* activate global measurement if this is the first channel */
-+	spin_lock(&cmb_area.lock);
-+	if (list_empty(&cmb_area.list))
-+		cmf_activate(NULL, 1);
-+	list_add_tail(&cdev->cmb_list, &cmb_area.list);
-+	spin_unlock(&cmb_area.lock);
-+
-+	return 0;
-+}
-+
-+static void
-+free_cmbe (struct cmf_device *cdev)
-+{
-+	spin_lock_irq(cdev->ccwlock);
-+	if (cdev->cmb)
-+		kfree(cdev->cmb);
-+	cdev->cmb = NULL;
-+	spin_unlock_irq(cdev->ccwlock);
-+
-+	/* deactivate global measurement if this is the last channel */
-+	spin_lock(&cmb_area.lock);
-+	list_del_init(&cdev->cmb_list);
-+	if (list_empty(&cmb_area.list))
-+		cmf_activate(NULL, 0);
-+	spin_unlock(&cmb_area.lock);
-+}
-+
-+static int
-+set_cmbe(struct cmf_device *cdev, u32 mme)
-+{
-+	unsigned long mba;
-+
-+	if (!cdev->cmb)
-+		return -EINVAL;
-+
-+	mba = mme ? (unsigned long)cdev->cmb : 0;
-+
-+	return set_schib(cdev->irq, mme, 1, mba);
-+}
-+
-+static int
-+readall_cmbe (struct cmf_device *cdev, struct cmbdata *data)
-+{
-+	/* yes, we have to put it on the stack
-+	 * because the cmb must only be accessed
-+	 * atomically, e.g. with mvc */
-+	struct cmbe cmb;
-+	unsigned long flags;
-+	u64 time;
-+
-+	spin_lock_irqsave(cdev->ccwlock, flags);
-+	if (!cdev->cmb) {
-+		spin_unlock_irqrestore(cdev->ccwlock, flags);
-+		return -ENODEV;
-+	}
-+
-+	cmb = *(struct cmbe*)cdev->cmb;
-+	time = get_clock() - cdev->cmb_start_time;
-+	spin_unlock_irqrestore(cdev->ccwlock, flags);
-+	
-+	*data = (struct cmbdata) {
-+		/* we only know values before device_busy_time */
-+		.size = offsetof(struct cmbdata, device_busy_time),
-+
-+		/* conver to nanoseconds */
-+		.elapsed_time = (time * 1000) >> 12,
-+
-+		/* copy data to new structure */
-+		.ssch_rsch_count		= cmb.ssch_rsch_count,
-+		.sample_count			= cmb.sample_count,
-+
-+		/* time fields are converted to nanoseconds while copying */
-+		.device_connect_time
-+			= time_to_nsec(cmb.device_connect_time),
-+		.function_pending_time
-+			= time_to_nsec(cmb.function_pending_time),
-+		.device_disconnect_time
-+			= time_to_nsec(cmb.device_disconnect_time),
-+		.control_unit_queuing_time
-+		 	= time_to_nsec(cmb.control_unit_queuing_time),
-+		.device_active_only_time
-+			= time_to_nsec(cmb.device_active_only_time),
-+		.device_busy_time
-+			= time_to_nsec(cmb.device_busy_time),
-+		.initial_command_response_time
-+			= time_to_nsec(cmb.initial_command_response_time),
-+	};
-+
-+	return 0;
-+}
-+
-+static void
-+reset_cmbe(struct cmf_device *cdev)
-+{
-+	struct cmbe *cmb;
-+	spin_lock_irq(cdev->ccwlock);
-+	cmb = cdev->cmb;
-+	if (cmb)
-+		memset (cmb, 0, sizeof (*cmb));
-+	cdev->cmb_start_time = get_clock();
-+	spin_unlock_irq(cdev->ccwlock);
-+}
-+
-+static struct cmb_operations cmbops_extended = {
-+	.alloc	= alloc_cmbe,
-+	.free	= free_cmbe,
-+	.set	= set_cmbe,
-+	.readall= readall_cmbe,
-+	.reset	= reset_cmbe,
-+};
-+
-+/******* external interface to kernel *******/
-+
-+/**
-+ * enable_cmf, disable_cmf, set_cmf, cmf_readall, cmf_reset:
-+ *  simple wrappers around the cmb_operations.
-+ */
-+int
-+enable_cmf(struct cmf_device *cdev)
-+{
-+	return cmbops->alloc(cdev);
-+}
-+
-+void
-+disable_cmf(struct cmf_device *cdev)
-+{
-+	cmbops->free(cdev);
-+}
-+
-+int
-+set_cmf(struct cmf_device *cdev, u32 mme)
-+{
-+	return cmbops->set(cdev, mme);
-+}
-+
-+int
-+cmf_readall(struct cmf_device *cdev, struct cmbdata *data)
-+{
-+	return cmbops->readall(cdev, data);
-+}
-+
-+void
-+cmf_reset(struct cmf_device *cdev)
-+{
-+	return cmbops->reset(cdev);
-+}
-+
-+/* set up maxchannels and format parameter when the module is built-in */
-+#ifndef MODULE
-+static int __init
-+setup_cmf_maxchannels(char *arg)
-+{
-+	int arglen = sizeof("cmf_maxchannels=") - 1;
-+	int c;
-+	
-+	c = simple_strtoul(arg + arglen + 1, 0, 0);
-+	if (c <= 0 || c > 65536) {
-+		printk(KERN_WARNING "Invalid parameter %s, using "
-+			"default (%d)\n", arg, maxchannels);
-+	} else {
-+		maxchannels = c;
-+	}
-+
-+	return 0;
-+}
-+__setup("cmf_maxchannels=", setup_cmf_maxchannels);
-+
-+static int __init
-+setup_cmf_format(char *arg)
-+{
-+	int arglen = sizeof("cmf_format") - 1;
-+	
-+	format = simple_strtoul(arg + arglen, 0, 0);
-+	if (format < CMF_BASIC || format > CMF_EXTENDED) {
-+		printk(KERN_WARNING "Invalid parameter %s\n", arg);
-+		format = -1;
-+	}
-+
-+	return 0;
-+}
-+__setup("cmf_format=", setup_cmf_format);
-+#endif
-+
-+static int __init
-+init_cmf(void)
-+{
-+	char *format_string;
-+	char *detect_string = "parameter";
-+
-+	/* We cannot really autoprobe this. If the user did not give a parameter,
-+	   see if we are running on z990 or up, otherwise fall back to basic mode. */
-+
-+	if (format == CMF_AUTODETECT) {
-+		if (!MACHINE_NEW_STIDP) {
-+			format = CMF_BASIC;
-+		} else {
-+			format = CMF_EXTENDED;
-+		}
-+		detect_string = "autodetected";
-+	} else {
-+		detect_string = "parameter";
-+	}
-+
-+	switch (format) {
-+	case CMF_BASIC:
-+		format_string = "basic";
-+		cmbops = &cmbops_basic;
-+		if (maxchannels > 4096 || maxchannels < 1) {
-+			printk(KERN_ERR "Basic channel measurement facility"
-+					" can only use 1 to 4096 devices\n"
-+			       KERN_ERR "when the cmf driver is built"
-+					" as a loadable module\n");
-+			return 1;
-+		}
-+		break;
-+	case CMF_EXTENDED:
-+ 		format_string = "extended";
-+		cmbops = &cmbops_extended;
-+		break;
-+	default:
-+		printk(KERN_ERR "Invalid format %d for channel "
-+			"measurement facility\n", format);
-+		return 1;
-+	}
-+
-+	printk(KERN_INFO "Channel measurement facility using %s format (%s)\n",
-+		format_string, detect_string);
-+	return 0;
-+}
-+
-+module_init(init_cmf);
-+
-+
-+MODULE_AUTHOR("Arnd Bergmann <arndb at de.ibm.com>");
-+MODULE_LICENSE("GPL");
-+MODULE_DESCRIPTION("channel measurement facility base driver\n"
-+		   "Copyright 2003 IBM Corporation\n");
-+
-+EXPORT_SYMBOL_GPL(enable_cmf);
-+EXPORT_SYMBOL_GPL(disable_cmf);
-+EXPORT_SYMBOL_GPL(set_cmf);
-+EXPORT_SYMBOL_GPL(cmf_readall);
-+EXPORT_SYMBOL_GPL(cmf_reset);
-=== drivers/s390/net/smsgiucv.c
-==================================================================
---- drivers/s390/net/smsgiucv.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/smsgiucv.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,167 @@
-+/*
-+ * IUCV special message driver
-+ *
-+ * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ * Author(s): Martin Schwidefsky (schwidefsky 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 <linux/errno.h>
-+#include <linux/slab.h>
-+#include <asm/cpcmd.h>
-+#include <asm/ebcdic.h>
-+
-+#include "iucv.h"
-+
-+struct smsg_callback {
-+	struct list_head list;
-+	char *prefix;
-+	int len;
-+	void (*callback)(char *str);
-+};
-+
-+MODULE_AUTHOR
-+   ("(C) 2003 IBM Corporation by Martin Schwidefsky (schwidefsky at de.ibm.com)");
-+MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
-+
-+static iucv_handle_t smsg_handle;
-+static unsigned short smsg_pathid;
-+static spinlock_t smsg_list_lock = SPIN_LOCK_UNLOCKED;
-+static struct list_head smsg_list = LIST_HEAD_INIT(smsg_list);
-+
-+static void
-+smsg_connection_complete(iucv_ConnectionComplete *eib, void *pgm_data)
-+{
-+}
-+
-+
-+static void
-+smsg_message_pending(iucv_MessagePending *eib, void *pgm_data)
-+{
-+	struct smsg_callback *cb;
-+        unsigned char *msg;
-+	unsigned short len;
-+	int rc;
-+
-+	len = eib->ln1msg2.ipbfln1f;
-+	msg = kmalloc(len + 1, GFP_ATOMIC|GFP_DMA);
-+	if (!msg) {
-+		iucv_reject(eib->ippathid, eib->ipmsgid, eib->iptrgcls);
-+		return;
-+	}
-+	rc = iucv_receive(eib->ippathid, eib->ipmsgid, eib->iptrgcls,
-+			  msg, len, 0, 0, 0);
-+	if (rc == 0) {
-+		msg[len] = 0;
-+		EBCASC(msg, len);
-+		spin_lock(&smsg_list_lock);
-+		list_for_each_entry(cb, &smsg_list, list)
-+			if (strncmp(msg + 8, cb->prefix, cb->len) == 0) {
-+				cb->callback(msg + 8);
-+				break;
-+			}
-+		spin_unlock(&smsg_list_lock);
-+	}
-+	kfree(msg);
-+}
-+
-+static iucv_interrupt_ops_t smsg_ops = {
-+	.ConnectionComplete = smsg_connection_complete,
-+	.MessagePending     = smsg_message_pending,
-+};
-+
-+int
-+smsg_register_callback(char *prefix, void (*callback)(char *str))
-+{
-+	struct smsg_callback *cb;
-+
-+	cb = kmalloc(sizeof(struct smsg_callback), GFP_KERNEL);
-+	if (!cb)
-+		return -ENOMEM;
-+	cb->prefix = prefix;
-+	cb->len = strlen(prefix);
-+	cb->callback = callback;
-+	spin_lock(&smsg_list_lock);
-+	list_add_tail(&cb->list, &smsg_list);
-+	spin_unlock(&smsg_list_lock);
-+	return 0;
-+}
-+
-+void
-+smsg_unregister_callback(char *prefix, void (*callback)(char *str))
-+{
-+	struct smsg_callback *cb, *tmp;
-+
-+	spin_lock(&smsg_list_lock);
-+	cb = 0;
-+	list_for_each_entry(tmp, &smsg_list, list)
-+		if (tmp->callback == callback &&
-+		    strcmp(tmp->prefix, prefix) == 0) {
-+			cb = tmp;
-+			list_del(&cb->list);
-+			break;
-+		}
-+	spin_unlock(&smsg_list_lock);
-+	kfree(cb);
-+}
-+
-+static void __exit
-+smsg_exit(void)
-+{
-+	if (smsg_handle > 0) {
-+		cpcmd("SET SMSG OFF", 0, 0);
-+		iucv_sever(smsg_pathid, 0);
-+		iucv_unregister_program(smsg_handle);
-+	}
-+	return;
-+}
-+
-+static int __init
-+smsg_init(void)
-+{
-+	static unsigned char pgmmask[24] = {
-+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-+	};
-+	int rc;
-+	
-+	smsg_handle = iucv_register_program("SMSGIUCV        ", "*MSG    ",
-+					    pgmmask, &smsg_ops, 0);
-+	if (!smsg_handle) {
-+		printk(KERN_ERR "SMSGIUCV: failed to register to iucv");
-+		return -EIO;	/* better errno ? */
-+	}
-+	rc = iucv_connect (&smsg_pathid, 1, 0, "*MSG    ", 0, 0, 0, 0,
-+			   smsg_handle, 0);
-+	if (rc) {
-+		printk(KERN_ERR "SMSGIUCV: failed to connect to *MSG");
-+		iucv_unregister_program(smsg_handle);
-+		smsg_handle = 0;
-+		return -EIO;
-+	}
-+	cpcmd("SET SMSG IUCV", 0, 0);
-+	return 0;
-+}
-+	
-+module_init(smsg_init);
-+module_exit(smsg_exit);
-+MODULE_LICENSE("GPL");
-+
-+EXPORT_SYMBOL(smsg_register_callback);
-+EXPORT_SYMBOL(smsg_unregister_callback);
-=== drivers/s390/net/qeth.c
-==================================================================
---- drivers/s390/net/qeth.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/qeth.c   (/trunk/2.4.27)   (revision 52)
-@@ -1,6 +1,6 @@
- /*
-  *
-- * linux/drivers/s390/net/qeth.c ($Revision: 1.337 $)
-+ * linux/drivers/s390/net/qeth.c ($Revision: 1.337.4.32 $)
-  *
-  * Linux on zSeries OSA Express and HiperSockets support
-  *
-@@ -28,9 +28,6 @@
-  */
- 
- /*
-- * The driver supports in general all QDIO driven network devices on the
-- * Hydra card.
-- *
-  * For all devices, three channels must be available to the driver. One
-  * channel is the read channel, one is the write channel and the third
-  * one is the channel used to control QDIO.
-@@ -149,6 +146,7 @@
- #include <linux/trdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/reboot.h>
-+#include <linux/mii.h>
- 
- #include <linux/if_vlan.h>
- #include <asm/chandev.h>
-@@ -156,8 +154,10 @@
- #include <asm/irq.h>
- #include <asm/s390dyn.h>
- #include <asm/debug.h>
-+#include <asm/processor.h>
- 
- #include <asm/qdio.h>
-+#include <asm/qeth.h>
- 
- #include "qeth_mpc.h"
- #include "qeth.h"
-@@ -171,7 +171,7 @@
- static int global_stay_in_mem=0;
- 
- /****************** MODULE STUFF **********************************/
--#define VERSION_QETH_C "$Revision: 1.337 $"
-+#define VERSION_QETH_C "$Revision: 1.337.4.32 $"
- static const char *version="qeth S/390 OSA-Express driver (" \
- 	VERSION_QETH_C "/" VERSION_QETH_H "/" VERSION_QETH_MPC_H
- 	QETH_VERSION_IPV6 QETH_VERSION_VLAN ")";
-@@ -183,10 +183,8 @@
- 
- /******************** HERE WE GO ***********************************/
- 
--#define PROCFILE_SLEEP_SEM_MAX_VALUE 0
--#define PROCFILE_IOCTL_SEM_MAX_VALUE 3
--static struct semaphore qeth_procfile_ioctl_lock;
--static struct semaphore qeth_procfile_ioctl_sem;
-+
-+
- static qeth_card_t *firstcard=NULL;
- 
- static sparebufs_t sparebufs[MAX_SPARE_BUFFERS];
-@@ -224,9 +222,12 @@
- /* thought I could get along without forward declarations...
-  * just lazyness here */
- static int qeth_reinit_thread(void*);
--static void qeth_schedule_recovery(qeth_card_t *card);
-+static inline void qeth_schedule_recovery(qeth_card_t *card);
-+static int qeth_fake_header(struct sk_buff *skb, struct net_device *dev,
-+       			    unsigned short type, void *daddr, void *saddr,
-+       			    unsigned len);
- 
--inline static int QETH_IP_VERSION(struct sk_buff *skb)
-+static inline int QETH_IP_VERSION(struct sk_buff *skb)
- {
-         switch (skb->protocol) {
-         case ETH_P_IPV6: return 6;
-@@ -243,6 +244,21 @@
- 		return b;
- }
- 
-+/*                                                                             
-+ * This is our local skb_unshare, only with pskb_copy instead of skb_copy.
-+ * We place our headers whare Ethernet MAC was, so we do not need
-+ * full skb_copy.
-+ */
-+static inline struct sk_buff *qeth_pskb_unshare(struct sk_buff *skb, int pri)
-+{
-+	struct sk_buff *nskb;
-+	if (!skb_cloned(skb))
-+		return skb;
-+	nskb = skb_copy(skb, pri);
-+	kfree_skb(skb); /* free our shared copy */
-+	return nskb;
-+}
-+
- static inline unsigned int qeth_get_millis(void)
- {
- 	__u64 time;
-@@ -291,7 +307,8 @@
- 	set_task_state(current,TASK_RUNNING);
- }
- 
--static void qeth_get_mac_for_ipm(__u32 ipm,char *mac,struct net_device *dev) {
-+static inline void qeth_get_mac_for_ipm(__u32 ipm,char *mac,
-+					struct net_device *dev) {
- 	if (dev->type==ARPHRD_IEEE802_TR)
- 		ip_tr_mc_map(ipm,mac);
- 	else
-@@ -403,7 +420,7 @@
- #define QETH_GET_ADDR(x) ((__u32)x)
- #endif /* CONFIG_ARCH_S390X */
- 
--static int qeth_does_card_exist(qeth_card_t *card)
-+static inline int qeth_does_card_exist(qeth_card_t *card)
- {
- 	qeth_card_t *c=firstcard;
- 	int rc=0;
-@@ -696,6 +713,12 @@
- 	QETH_DBF_TEXT2(0,trace,dbf_text);
- 	QETH_DBF_TEXT2(0,setup,dbf_text);
- 
-+	if ((card->options.layer2 == DO_LAYER2) && 
-+	    (!atomic_read(&card->mac_registered))) {
-+		QETH_DBF_TEXT2(0,trace,"nomacaddr");
-+		return -EPERM;
-+	}
-+		
- 	qeth_save_dev_flag_state(card);
- 
- 	netif_start_queue(dev);
-@@ -718,7 +741,9 @@
- 
- static int qeth_is_multicast_skb_at_all(struct sk_buff *skb,int version)
- {
--	int i;
-+	int i=RTN_UNSPEC;
-+	qeth_card_t *card = (qeth_card_t *)skb->dev->priv;
-+	
- 	if (skb->dst && skb->dst->neighbour) {
- 		i=skb->dst->neighbour->type;
- 		return ((i==RTN_BROADCAST)||
-@@ -731,13 +756,38 @@
- 	} else if (version==6) {
- 		return (skb->nh.raw[24]==0xff)?RTN_MULTICAST:0;
- 	}
--	return 0;
-+
-+	if (!memcmp(skb->data,skb->dev->broadcast,6)) {
-+		i=RTN_BROADCAST;
-+	} else {
-+	        __u16 hdr_mac;
-+	        hdr_mac=*((__u16*)skb->data);
-+	        /* tr multicast? */
-+	        switch (card->link_type) {
-+	        case QETH_MPC_LINK_TYPE_HSTR:
-+	        case QETH_MPC_LINK_TYPE_LANE_TR:
-+	        	if ( (hdr_mac==QETH_TR_MC_MAC_NC) ||
-+			     (hdr_mac==QETH_TR_MC_MAC_C) )
-+				i = RTN_MULTICAST;
-+			break;
-+	        /* eth or so multicast? */
-+                default:
-+                      	if ( (hdr_mac==QETH_ETH_MC_MAC_V4) ||
-+			     (hdr_mac==QETH_ETH_MC_MAC_V6) )
-+			        i = RTN_MULTICAST;
-+	        }
-+        }
-+	return ((i==RTN_BROADCAST)||
-+	        (i==RTN_MULTICAST)||
-+	        (i==RTN_ANYCAST))?i:0;
- }
- 
- static int qeth_get_prioqueue(qeth_card_t *card,struct sk_buff *skb,
- 			      int multicast,int version)
- {
--	if (!version) return QETH_DEFAULT_QUEUE;
-+	if (!version &&
-+	    (card->type==QETH_CARD_TYPE_OSAE)) 
-+		return QETH_DEFAULT_QUEUE;
-         switch (card->no_queues) {
-         case 1:
-                 return 0;
-@@ -901,6 +951,7 @@
- 			sprintf(dbf_text,"CBOT%04x",card->irq0);
- 			QETH_DBF_TEXT1(0,trace,dbf_text);
- 			qeth_set_dev_flag_norunning(card);
-+			netif_carrier_off(card->dev);
-                         problem=0;
- 			goto out;
-                 }
-@@ -910,6 +961,7 @@
- 				atomic_set(&card->is_startlaned,1);
- 				problem=PROBLEM_CARD_HAS_STARTLANED;
- 			}
-+			netif_carrier_on(card->dev);
- 			goto out;
-                 }
- 		if ( *(PDU_ENCAPSULATION(buffer))==
-@@ -1046,7 +1098,7 @@
- 	return retval;
- }
- 
--static int qeth_get_spare_buf(void)
-+static inline int qeth_get_spare_buf(void)
- {
- 	int i=0;
- 	char dbf_text[15];
-@@ -1131,8 +1183,8 @@
- 	}
- }
- 
--static void qeth_queue_input_buffer(qeth_card_t *card,int bufno,
--				    unsigned int under_int)
-+static inline void qeth_queue_input_buffer(qeth_card_t *card,int bufno,
-+	       				   unsigned int under_int)
- {
- 	int count=0,start=0,stop=0,pos;
- 	int result;
-@@ -1283,10 +1335,11 @@
- 	return skb;
- }
- 
--static struct sk_buff *qeth_get_next_skb(qeth_card_t *card,
--	       				 int *element_ptr,int *pos_in_el_ptr,
--			       		 void **hdr_ptr,
--					 qdio_buffer_t *buffer)
-+static inline struct sk_buff *qeth_get_next_skb(qeth_card_t *card,
-+		     				int *element_ptr,
-+						int *pos_in_el_ptr,
-+		       				void **hdr_ptr,
-+		       				qdio_buffer_t *buffer)
- {
- 	int length;
- 	char *data_ptr;
-@@ -1357,7 +1410,9 @@
- 	}
- 
- 	*hdr_ptr=SBALE_ADDR(element)+pos_in_el;
--
-+        if (card->options.layer2 == DO_LAYER2)
-+		length=*(__u16*)((char*)(*hdr_ptr)+QETH_HEADER2_LEN_POS);
-+        else
- 	length=*(__u16*)((char*)(*hdr_ptr)+QETH_HEADER_LEN_POS);
- 
- #ifdef QETH_DBF_LIKE_HELL
-@@ -1394,10 +1449,6 @@
- 		skb=qeth_get_skb(length+QETH_FAKE_LL_LEN);
- 		if (!skb) goto nomem;
- 		skb_pull(skb,QETH_FAKE_LL_LEN);
--		if (!skb) {
--			dev_kfree_skb_irq(skb);
--			goto nomem;
--		}
- 	} else {
- 		skb=qeth_get_skb(length);
- 		if (!skb) goto nomem;
-@@ -1472,8 +1523,8 @@
- 	return NULL;
- }
- 
--static void qeth_transform_outbound_addrs(qeth_card_t *card,
--					  qdio_buffer_t *buffer)
-+static inline void qeth_transform_outbound_addrs(qeth_card_t *card,
-+	       					 qdio_buffer_t *buffer)
- {
- 	int i;
- 	void *ptr;
-@@ -1485,7 +1536,8 @@
- 		}
- 	}
- }
--static void qeth_get_linux_addrs_for_buffer(qeth_card_t *card,int buffer_no)
-+static inline void qeth_get_linux_addrs_for_buffer(qeth_card_t *card,
-+						   int buffer_no)
- {
- 	int i;
- 	void *ptr;
-@@ -1501,7 +1553,7 @@
- 	}
- }
- 
--static void qeth_read_in_buffer(qeth_card_t *card,int buffer_no)
-+static inline void qeth_read_in_buffer(qeth_card_t *card,int buffer_no)
- {
-         struct sk_buff *skb;
-         void *hdr_ptr;
-@@ -1511,8 +1563,10 @@
- 	unsigned short cast_type;
- #ifdef QETH_VLAN
- 	__u16 *vlan_tag;
-+	__u16 l2_vlan_tag=0;
- #endif
- 	int i;
-+	__u32 l2_cast_type=0;
- 	int max_elements;
- 	char dbf_text[15];
- 	struct net_device *dev;
-@@ -1559,12 +1613,41 @@
- 		if (skb) {
- 			skb->dev=dev;
- 
--#ifdef QETH_IPV6
-+			/* QDIO header type 2 -> layer 2 layout */
-+			if ( (*(__u8 *)(hdr_ptr))==2) {
-+		        	l2_cast_type = (*(__u8*)(hdr_ptr+3));
-+				/* unicast is probably most of the traffic,
-+				 * so we don't use the default branch down at
-+				 * the bottom for this */
-+		                if (l2_cast_type &
-+		                    QETH_QDIO_HEADER2_FLAG_UNICAST_FRAME)
-+		                	skb->pkt_type = PACKET_HOST;
-+		                else if (l2_cast_type & 
-+					 QETH_QDIO_HEADER2_FLAG_MULTICAST_FRAME)
-+		                	skb->pkt_type = PACKET_MULTICAST;
-+		                else if (l2_cast_type &
-+		                         QETH_QDIO_HEADER2_FLAG_BROADCAST_FRAME)
-+		                        skb->pkt_type = PACKET_BROADCAST;
-+				else /* default: unicast */
-+					skb->pkt_type = PACKET_HOST;
-+				version=0;
-+
-+#ifdef QETH_VLAN
-+ 				if (l2_cast_type==
-+  				     QETH_QDIO_HEADER2_FLAG_VLAN_FRAME)
-+  					l2_vlan_tag=
-+						*(__u16*)(((__u8*)hdr_ptr)+10);
-+#endif
-+	                       skb->protocol=card->type_trans(skb,dev);
-+			       if(card->options.checksum_type==NO_CHECKSUMMING)
-+					skb->ip_summed = CHECKSUM_UNNECESSARY;
-+			       else
-+					skb->ip_summed = CHECKSUM_NONE;
-+                               goto jump_layer2;
-+			}
- 			if ( (*(__u16 *)(hdr_ptr))&(QETH_HEADER_PASSTHRU) ) {
- 				skb->protocol=card->type_trans(skb,dev);
--			} else
--#endif /* QETH_IPV6 */
--			{
-+			} else {
- 				version=((*(__u16 *)(hdr_ptr))&
- 					 (QETH_HEADER_IPV6))?6:4;
- 				skb->protocol=htons((version==4)?ETH_P_IP:
-@@ -1597,7 +1680,27 @@
- 					sprintf(dbf_text,"castun%2x",cast_type);
- 					QETH_DBF_TEXT2(1,trace,dbf_text);
- 				}
--
-+#ifdef QETH_VLAN
-+				if (*(__u8*)(hdr_ptr+11)&
-+				    QETH_EXT_HEADER_VLAN_FRAME) {
-+					vlan_tag=(__u16 *)skb_push(skb,
-+								   VLAN_HLEN);
-+					/*
-+					 if (*(__u8*)(hdr_ptr+11) & 
-+					     QETH_EXT_HEADER_INCLUDE_VLAN_TAG) {
-+				   	   *vlan_tag = *(__u16*)(hdr_ptr+28);
-+			  		   *(vlan_tag+1)= *(__u16*)(hdr_ptr+30);
-+			 		 } else {
-+					 */
-+				    	*vlan_tag = *(__u16*)(hdr_ptr+12);
-+			 		*(vlan_tag+1) = skb->protocol;
-+					/*
-+					   }
-+					 */
-+					skb->protocol=
-+						__constant_htons(ETH_P_8021Q);
-+				}
-+#endif				
- 		if (card->options.fake_ll==FAKE_LL) {
- 			skb->mac.raw=skb->data-QETH_FAKE_LL_LEN;
- 			if (skb->pkt_type==PACKET_MULTICAST) {
-@@ -1643,7 +1746,7 @@
- 			} else {
- 				/* clear source MAC for security reasons */
- 				memset(skb->mac.raw+
--				       QETH_FAKE_LL_DEST_MAC_POS,0,
-+				       QETH_FAKE_LL_SRC_MAC_POS,0,
- 				       QETH_FAKE_LL_ADDR_LEN);
- 			}
- 			memcpy(skb->mac.raw+
-@@ -1654,58 +1757,51 @@
- 			skb->mac.raw=skb->data;
- 		}
- 
--		skb->ip_summed=card->options.checksum_type;
- 		if (card->options.checksum_type==HW_CHECKSUMMING) {
- 			/* do we have a checksummed packet? */
--			if (*(__u8*)(hdr_ptr+11)&
--			    QETH_EXT_HEADER_CSUM_TRANSP_REQ) {
--				/* skb->ip_summed is set already */
- 
--				/* vlan is not an issue here, it's still in
-+			/* we only check for TCP/UDP checksums when the
-+			 * pseudo header was also checked sucessfully -- for
-+			 * the rest of the packets, it's not clear, whether
-+			 * the upper layer csum is alright. And they
-+			 * shouldn't occur too often anyway in real life */
-+			if ( (*(__u8*)(hdr_ptr+11)&
-+			      (QETH_EXT_HEADER_CSUM_HDR_REQ|
-+			       QETH_EXT_HEADER_CSUM_TRANSP_REQ)) ==
-+			      (QETH_EXT_HEADER_CSUM_HDR_REQ|
-+			       QETH_EXT_HEADER_CSUM_TRANSP_REQ) ) {
-+				/* csum does not need to be set
-+				 * inbound anyway
-+				 *
-+				 * vlan is not an issue here, it's still in
- 				 * the QDIO header, not pushed in the
--				 * skb yet */
-+				 * skb yet *
- 				int ip_len=(skb->data[0]&0x0f)<<2;
- 
- 				if (*(__u8*)(hdr_ptr+11)&
- 				    QETH_EXT_HEADER_CSUM_TRANSP_FRAME_TYPE) {
--					/* get the UDP checksum */
-+					* get the UDP checksum *
- 					skb->csum=*(__u16*)
- 						(&skb->data[ip_len+
- 						 QETH_UDP_CSUM_OFFSET]);
- 				} else {
--					/* get the TCP checksum */
-+					* get the TCP checksum *
- 					skb->csum=*(__u16*)
- 						(&skb->data[ip_len+
- 						 QETH_TCP_CSUM_OFFSET]);
- 				}
-+				 */
-+				skb->ip_summed=CHECKSUM_UNNECESSARY;
- 			} else {
- 				/* make the stack check it */
--				skb->ip_summed=SW_CHECKSUMMING;
-+				skb->ip_summed=CHECKSUM_NONE;
- 			}
-+		} else {
-+			skb->ip_summed=card->options.checksum_type;
- 		}
- 
--#ifdef QETH_VLAN
--				if (*(__u8*)(hdr_ptr+11)&
--				    QETH_EXT_HEADER_VLAN_FRAME) {
--					vlan_tag=(__u16 *)skb_push(skb,
--								   VLAN_HLEN);
--					/*
--					 if (*(__u8*)(hdr_ptr+11) & 
--					     QETH_EXT_HEADER_INCLUDE_VLAN_TAG) {
--				   	   *vlan_tag = *(__u16*)(hdr_ptr+28);
--			  		   *(vlan_tag+1)= *(__u16*)(hdr_ptr+30);
--			 		 } else {
--					 */
--				    	*vlan_tag = *(__u16*)(hdr_ptr+12);
--			 		*(vlan_tag+1) = skb->protocol;
--					/*
--					   }
--					 */
--					skb->protocol=
--						__constant_htons(ETH_P_8021Q);
--				}
--#endif				
- 			}
-+jump_layer2:
- 
- #ifdef QETH_PERFORMANCE_STATS
- 			card->perf_stats.inbound_time+=
-@@ -1718,7 +1814,19 @@
- 			QETH_DBF_TEXT6(0,trace,dbf_text);
- #endif /* QETH_DBF_LIKE_HELL */
- 
-+#ifdef QETH_VLAN
-+			if (l2_vlan_tag) {
-+				/* the tag was in the VLAN information has
-+				 * been in the QDIO header, therefore remove
-+				 * the tag for the hw acceleration function */
-+				skb_pull(skb,VLAN_HLEN);
-+				vlan_hwaccel_rx(skb,card->vlangrp,l2_vlan_tag);
-+			} else {
-+				netif_rx(skb);
-+			}
-+#else
- 			netif_rx(skb);
-+#endif /* QETH_VLAN */
- 
- 			card->stats->rx_packets++;
- 			card->stats->rx_bytes+=skb->len;
-@@ -1735,39 +1843,95 @@
- 				   buffer_no]);
- }
- 
--static void qeth_fill_header(qeth_hdr_t *hdr,struct sk_buff *skb,
--	       		     int version,int multicast)
-+static inline void qeth_fill_header(qeth_hdr_t *hdr,struct sk_buff *skb,
-+		     		    int version,int multicast)
- {
- #ifdef QETH_DBF_LIKE_HELL
- 	char dbf_text[15];
- #endif /* QETH_DBF_LIKE_HELL */
--#ifdef QETH_VLAN
- 	qeth_card_t *card;
--#endif
-+	int is_l2=0;
- 
-+	card = (qeth_card_t *)skb->dev->priv;
-+
-+	if (card->options.layer2 == DO_LAYER2) {
-+		is_l2=1;
-+	} else {
- 	hdr->id=1;
- 	hdr->ext_flags=0;
- 
-+		/* as skb->len includes the header now */
-+		hdr->length=skb->len-QETH_HEADER_SIZE; 
- #ifdef QETH_VLAN
-         /* before we're going to overwrite 
--	   this location with next hop ip 
--	*/
--	card = (qeth_card_t *)skb->dev->priv;
-+	 * this location with next hop ip.
-+	 * v6 uses passthrough, v4 sets the tag in the QDIO header */
- 	if ((card->vlangrp != NULL) && 
--	    (version == 4)          &&
--	    vlan_tx_tag_present(skb)) 
--	{
--		hdr->ext_flags = QETH_EXT_HEADER_VLAN_FRAME;
-+		    vlan_tx_tag_present(skb)) {
-+		if (version == 4) {
-+			hdr->ext_flags = QETH_EXT_HEADER_VLAN_FRAME;
-+		} else {
-+				hdr->ext_flags =
-+					QETH_EXT_HEADER_INCLUDE_VLAN_TAG;
-+		}
- 		hdr->vlan_id  = vlan_tx_tag_get(skb);
- 	}
- #endif
-+	}
- 
--	hdr->length=skb->len-QETH_HEADER_SIZE; /* as skb->len includes
--						  the header now */
-+	if (is_l2) {
-+		/* set byte 0 to "0x02" and byte 3 to casting flags */
-+		if (multicast==RTN_MULTICAST) {
-+			*(__u32*)hdr=(2<<24)+
-+				QETH_QDIO_HEADER2_FLAG_MULTICAST_FRAME;
-+		} else if (multicast==RTN_BROADCAST) {
-+			*(__u32*)hdr=(2<<24)+
-+				QETH_QDIO_HEADER2_FLAG_BROADCAST_FRAME;
-+ 		} else {
-+ 			/* do it on our own :-( */
-+ 			if (!memcmp(skb->data+QETH_HEADER_SIZE,
-+ 				    skb->dev->broadcast,6)) { /* broadcast? */
-+ 				*(__u32*)hdr=(2<<24)+
-+ 					QETH_QDIO_HEADER2_FLAG_BROADCAST_FRAME;
-+ 			} else {
-+ 				__u16 hdr_mac;
-+ 				hdr_mac=*((__u16*)skb->data);
-+ 				/* tr multicast? */
-+ 				switch (card->link_type) {
-+ 				case QETH_MPC_LINK_TYPE_HSTR:
-+ 				case QETH_MPC_LINK_TYPE_LANE_TR:
-+ 					if ( (hdr_mac==QETH_TR_MC_MAC_NC) ||
-+ 					     (hdr_mac==QETH_TR_MC_MAC_C) )
-+						*(__u32*)hdr=(2<<24)+
-+ 					QETH_QDIO_HEADER2_FLAG_MULTICAST_FRAME;
-+					else
-+						*(__u32*)hdr=(2<<24)+
-+ 					QETH_QDIO_HEADER2_FLAG_UNICAST_FRAME;
-+ 					break;
-+ 				/* eth or so multicast? */
-+ 				default:
-+ 					if ( (hdr_mac==QETH_ETH_MC_MAC_V4) ||
-+ 					     (hdr_mac==QETH_ETH_MC_MAC_V6) )
-+						*(__u32*)hdr=(2<<24)+
-+ 					QETH_QDIO_HEADER2_FLAG_MULTICAST_FRAME;
-+					else
-+						*(__u32*)hdr=(2<<24)+
-+ 					QETH_QDIO_HEADER2_FLAG_UNICAST_FRAME;
-+ 				}
-+ 			}
-+ 		}
- 
--	/* yes, I know this is doubled code, but a small little bit
--	   faster maybe */
--	if (version==4) { /* IPv4 */
-+ 		*(__u16*)(((__u8*)hdr)+6)=skb->len-QETH_HEADER_SIZE;
-+ #ifdef QETH_VLAN
-+ 		/* VSWITCH relies on the VLAN information to be present
-+ 		 * in the QDIO header */
-+ 		if ((card->vlangrp != NULL) && 
-+ 		    vlan_tx_tag_present(skb)) {
-+ 			*(__u32*)hdr|=QETH_QDIO_HEADER2_FLAG_VLAN_FRAME;
-+ 			*(__u16*)(((__u8*)hdr)+10)=vlan_tx_tag_get(skb);
-+ 		}
-+ #endif
-+ 	} else if (version==4) { /* IPv4 */
- 		if (multicast==RTN_MULTICAST) {
- 			hdr->flags=QETH_CAST_MULTICAST;
- 		} else if (multicast==RTN_BROADCAST) {
-@@ -1820,7 +1984,13 @@
- 			    skb->dev->broadcast,6)) { /* broadcast? */
- 			hdr->flags=QETH_CAST_BROADCAST|QETH_HEADER_PASSTHRU;
- 		} else {
--			hdr->flags=QETH_CAST_UNICAST|QETH_HEADER_PASSTHRU;
-+			if (multicast==RTN_MULTICAST) {
-+				hdr->flags=QETH_CAST_MULTICAST|
-+					QETH_HEADER_PASSTHRU;
-+			} else {
-+				hdr->flags=QETH_CAST_UNICAST|
-+					QETH_HEADER_PASSTHRU;
-+			}
- 		}
- 	}
- #ifdef QETH_DBF_LIKE_HELL
-@@ -1836,7 +2006,7 @@
- #endif /* QETH_DBF_LIKE_HELL */
- }
- 
--static int inline qeth_fill_buffer(qdio_buffer_t *buffer,char *dataptr,
-+static inline int qeth_fill_buffer(qdio_buffer_t *buffer,char *dataptr,
- 		       		   int length,int element)
- {
- 	int length_here;
-@@ -1891,8 +2061,8 @@
- 	return element;
- }
- 
--static void qeth_flush_packed_packets(qeth_card_t *card,int queue,
--				      int under_int)
-+static inline void qeth_flush_packed_packets(qeth_card_t *card,int queue,
-+	       				     int under_int)
- {
- 	qdio_buffer_t *buffer;
- 	int result;
-@@ -1951,8 +2121,23 @@
- 	 * adapter honors it or not */
- 	switch (card->send_state[queue]) {
-  	case SEND_STATE_DONT_PACK:
-+		/* only request a PCI, if the fill level of the queue
-+		 * is close to the high watermark, so that we don't
-+		 * loose initiative during packing */
- 		if (atomic_read(&card->outbound_used_buffers[queue])
- 		    <HIGH_WATERMARK_PACK-WATERMARK_FUZZ) break;
-+
-+		last_pci=atomic_read(&card->last_pci_pos[queue]);
-+		/* compensate queues that wrapped around */
-+		if (position_for_do_qdio<last_pci)
-+			last_pci-=QDIO_MAX_BUFFERS_PER_Q;
-+
-+		/* reduce the number of PCIs in cases where we are always
-+		 * a little below the high watermark for packing -- request
-+		 * PCIs less frequently */
-+		if (position_for_do_qdio-last_pci<
-+		    HIGH_WATERMARK_PACK-WATERMARK_FUZZ) break;
-+
- 		/* set the PCI bit */
- 		card->outbound_ringbuffer[queue]->
- 			buffer[position_for_do_qdio].element[0].flags|=0x40;
-@@ -1966,13 +2151,13 @@
- 		 * last_pci is the position of the last pci we've set
- 		 * position_for_do_qdio is the position we will send out now
- 		 * outbound_used_buffers is the number of buffers used (means
--		 *   all buffers hydra has, inclusive position_for_do_qdio)
-+		 *   all buffers OSA has, inclusive position_for_do_qdio)
- 		 *
- 		 * we have to request a pci, if we have got the buffer of the
- 		 * last_pci position back.
- 		 *
- 		 * position_for_do_qdio-outbound_used_buffers is the newest
--		 *   buffer that we got back from hydra
-+		 *   buffer that we got back from OSA
- 		 *
- 		 * if this is greater or equal than the last_pci position,
- 		 * we should request a pci, as no pci request is
-@@ -2055,8 +2240,8 @@
- 	return ERROR_LINK_FAILURE; /* should never happen */
- }
- 
--static void qeth_free_buffer(qeth_card_t *card,int queue,int bufno,
--	       		     int qdio_error,int siga_error)
-+static inline void qeth_free_buffer(qeth_card_t *card,int queue,int bufno,
-+		     		    int qdio_error,int siga_error)
- {
- 	struct sk_buff *skb;
- 	int error;
-@@ -2139,7 +2324,8 @@
- 			case ERROR_LINK_FAILURE:
- 			case ERROR_KICK_THAT_PUPPY:
- 				QETH_DBF_TEXT4(0,trace,"endeglnd");
--				dst_link_failure(skb);
-+				card->stats->tx_dropped++;
-+				card->stats->tx_errors++;
- 				atomic_dec(&skb->users);
- 				dev_kfree_skb_irq(skb);
- 				break;
-@@ -2164,7 +2350,7 @@
- 	card->send_retries[queue][bufno]=0;
- }
- 
--static void qeth_free_all_skbs(qeth_card_t *card)
-+static inline void qeth_free_all_skbs(qeth_card_t *card)
- {
- 	int q,b;
- 
-@@ -2199,7 +2385,7 @@
- }
- #ifdef QETH_VLAN
- 
--void qeth_insert_ipv6_vlan_tag(struct sk_buff *__skb)
-+static inline void qeth_insert_ipv6_vlan_tag(struct sk_buff *__skb)
- {
- 
- 	/* Move the mac addresses to the beginning of the new header.
-@@ -2230,9 +2416,9 @@
- 
- 
- 
--static void qeth_send_packet_fast(qeth_card_t *card,struct sk_buff *skb,
--				  struct net_device *dev,
--				  int queue,int version,int multicast)
-+static inline void qeth_send_packet_fast(qeth_card_t *card,struct sk_buff *skb,
-+	       				 struct net_device *dev,
-+	       				 int queue,int version,int multicast)
- {
- 	qeth_ringbuffer_element_t *mybuffer;
- 	int position;
-@@ -2250,8 +2436,8 @@
- 		if ((version)&&(!card->realloc_message)) {
- 			card->realloc_message=1;
- 			PRINT_WARN("%s: not enough headroom in skb. " \
--				   "Try increasing the " \
--				   "add_hhlen parameter by %i.\n",
-+				   "Increasing the " \
-+				   "add_hhlen parameter by %i may help.\n",
- 				   card->dev_name,
- 				   QETH_HEADER_SIZE-skb_headroom(skb));
- 		}
-@@ -2277,9 +2463,11 @@
- 		skb=nskb;
- 	}
- #ifdef QETH_VLAN
-+	/* ATT: this assumes, L2 does want the VLAN tag in the payload,
-+	 * otherwise remove (version==0) */
- 	if ( (card->vlangrp != NULL) &&
-              vlan_tx_tag_present(skb) &&
--             (version==6)) {
-+             ((version==6)||(version==0))) {
-                 qeth_insert_ipv6_vlan_tag(skb);
-         }
- #endif
-@@ -2327,9 +2515,11 @@
- 
- /* no checks, if all elements are used, as then we would not be here (at most
-    127 buffers are enqueued) */
--static void qeth_send_packet_packed(qeth_card_t *card,struct sk_buff *skb,
--       				    struct net_device *dev,
--       				    int queue,int version,int multicast)
-+static inline void qeth_send_packet_packed(qeth_card_t *card,
-+					   struct sk_buff *skb,
-+					   struct net_device *dev,
-+					   int queue,int version,
-+					   int multicast)
- {
- 	qeth_ringbuffer_element_t *mybuffer;
- 	int elements_needed;
-@@ -2373,9 +2563,11 @@
- 		skb=nskb;
- 	}
- #ifdef QETH_VLAN
-+	/* ATT: this assumes, L2 does want the VLAN tag in the payload,
-+	 * otherwise remove (version==0) */
-         if ( (card->vlangrp != NULL) &&
-              vlan_tx_tag_present(skb) &&
--             (version==6)) {
-+             ((version==6)||(version==0))) {
-                 qeth_insert_ipv6_vlan_tag(skb);
-         }
- 
-@@ -2490,16 +2682,25 @@
- 	return old_val;
- }
- 
--static int qeth_do_send_packet(qeth_card_t *card,struct sk_buff *skb,
--       			       struct net_device *dev)
-+static inline int qeth_do_send_packet(qeth_card_t *card,struct sk_buff *skb,
-+	       			      struct net_device *dev)
- {
-         int queue,result=0;
- 	int multicast,version;
- 	char dbf_text[15];
- 	char dbf_text2[15]="stchupXX";
- 
-+	if (card->options.layer2 == DO_LAYER2)
-+		version=0;
-+	else
- 	version=QETH_IP_VERSION(skb);
- 	multicast=qeth_is_multicast_skb_at_all(skb,version);
-+	if ((multicast == RTN_BROADCAST) && (card->broadcast_capable == 0)) {
-+		card->stats->tx_dropped++;
-+		card->stats->tx_errors++;
-+		dev_kfree_skb_irq(skb);
-+		return 0;
-+	}
-         queue=qeth_get_prioqueue(card,skb,multicast,version);
- 
- #ifdef QETH_DBF_LIKE_HELL
-@@ -2610,21 +2811,37 @@
-         qeth_card_t *card;
- 	char dbf_text[15];
- 	int result;
-+	unsigned long stackptr;
- 
-         card=(qeth_card_t*)(dev->priv);
- 
--        if (skb==NULL)
--		return 0;
-+#ifdef CONFIG_ARCH_S390X
-+	asm volatile ("lgr %0,15" : "=d" (stackptr));
-+#else /* CONFIG_ARCH_S390X */
-+	asm volatile ("lr %0,15" : "=d" (stackptr));
-+#endif /* CONFIG_ARCH_S390X */
-+	/* prevent stack overflows */
-+	/* normal and async stack is both 8k on s390 and 16k on s390x,
-+	 * so it doesn't matter whether we're in an interrupt */
-+	if ( (stackptr & STACK_PTR_MASK)<
-+	     (sizeof(struct task_struct) + WORST_CASE_STACK_USAGE) ) {
-+		PRINT_ERR("delaying packet transmission " \
-+		       	  "due to potential stack overflow\n");
-+		sprintf(dbf_text,"STOF%4x",card->irq0);
-+		QETH_DBF_TEXT1(1,trace,dbf_text);
-+		PRINT_ERR("Backtrace follows:\n");
-+		show_trace((unsigned long *)stackptr);
-+		return -EBUSY;
-+	}
- 
- #ifdef QETH_DBF_LIKE_HELL
- 	QETH_DBF_HEX4(0,data,skb->data,__max(QETH_DBF_DATA_LEN,skb->len));
- #endif /* QETH_DBF_LIKE_HELL */
- 
--	netif_stop_queue(dev);
--
- 	if (!card) {
- 		QETH_DBF_TEXT2(0,trace,"XMNSNOCD");
--		dst_link_failure(skb);
-+		card->stats->tx_dropped++;
-+		card->stats->tx_errors++;
- 		dev_kfree_skb_irq(skb);
- 		return 0;
- 	}
-@@ -2637,11 +2854,31 @@
- 		card->stats->tx_carrier_errors++;
- 		sprintf(dbf_text,"XMNS%4x",card->irq0);
- 		QETH_DBF_TEXT2(0,trace,dbf_text);
--		dst_link_failure(skb);
-+		card->stats->tx_dropped++;
-+		card->stats->tx_errors++;
- 		dev_kfree_skb_irq(skb);
- 		return 0;
- 	}
- 
-+	if (dev->hard_header == qeth_fake_header) {
-+		/*
-+		 * in theory, if we run in undef-ed QETH_IPV6, we should
-+		 * always unshare, because we do skb_push, then overwrite
-+		 * that place with OSA header in qeth_send_packet_fast().
-+		 * But it is only visible to one application - tcpdump.
-+		 * Nobody else cares if (fake) MAC header gets smashed.
-+		 * So, we only do it if fake_ll is in effect.
-+		 */
-+		if ((skb = qeth_pskb_unshare(skb, GFP_ATOMIC)) == NULL) {
-+			card->stats->tx_dropped++;
-+			dev_kfree_skb_irq(skb);
-+			return 0;
-+		}
-+		skb_pull(skb, QETH_FAKE_LL_LEN);
-+	}
-+
-+	netif_stop_queue(dev);
-+
- 	result=qeth_do_send_packet(card,skb,dev);
- 
- 	if (!result)
-@@ -2650,6 +2887,36 @@
- 	return result;
- }
- 
-+/*
-+ * This function is needed to tell af_packet.c to process headers.
-+ * It is not called from there, but only from the transmit path,
-+ * when we do not need any actual header.
-+ *
-+ * N.B. Why do we insist on kludging here instead of fixing tcpdump?
-+ * Because tcpdump is shared among gazillions of platforms, and
-+ * there is a) no reliable way to identify qeth or its packets
-+ * in pcap-linux.c (sll->sll_halen is the only hope); b) no easy
-+ * way to pass this information from libpcap to tcpdump proper.
-+ *
-+ * XXX This fails with TR: traffic flows ok, but tcpdump remains confused.
-+ */
-+int qeth_fake_header(struct sk_buff *skb, struct net_device *dev,
-+		     unsigned short type, void *daddr, void *saddr,
-+		     unsigned len)
-+{
-+	unsigned char *hdr;
-+
-+	hdr = skb_push(skb, QETH_FAKE_LL_LEN);
-+	memcpy(hdr, "FAKELLFAKELL", ETH_ALEN*2);
-+	if (type != ETH_P_802_3)
-+		*(u16 *)(hdr + ETH_ALEN*2) = htons(type);
-+	else
-+		*(u16 *)(hdr + ETH_ALEN*2) = htons(len);
-+
-+	/* XXX Maybe dev->hard_header_len here? Then skb_pull by same size. */
-+	return QETH_FAKE_LL_LEN;
-+}
-+
- static struct net_device_stats* qeth_get_stats(struct net_device *dev)
- {
-         qeth_card_t *card;
-@@ -2803,23 +3070,19 @@
- 	return retval;
- }
- 
--static void qeth_wakeup_procfile(void) 
-+static void qeth_snmp_notify(void) 
- {
--	QETH_DBF_TEXT5(0,trace,"procwkup");
--	if (atomic_read(&qeth_procfile_ioctl_sem.count)<
--	    PROCFILE_SLEEP_SEM_MAX_VALUE)
--  		up(&qeth_procfile_ioctl_sem);
--}
--
--
--static int qeth_sleepon_procfile(void)
--{
--	QETH_DBF_TEXT5(0,trace,"procslp");
--	if (down_interruptible(&qeth_procfile_ioctl_sem)) {
--      		up(&qeth_procfile_ioctl_sem);
--      		return -ERESTARTSYS;
-+	/*notify all  registered processes */
-+	struct list_head *l;
-+	struct qeth_notify_list *n_entry;
-+	
-+	QETH_DBF_TEXT5(0,trace,"snmpnoti");
-+	spin_lock(&notify_lock);
-+	list_for_each(l, &notify_list) {
-+		n_entry = list_entry(l, struct qeth_notify_list, list);
-+		send_sig(n_entry->signum, n_entry->task, 1);
- 	}
--	return 0;
-+	spin_unlock(&notify_lock);
- }
- 
- static char* qeth_send_control_data(qeth_card_t *card,unsigned char *buffer,
-@@ -2889,17 +3152,19 @@
- 		QETH_DBF_TEXT2(0,trace,"scd:doio");
- 		sprintf(dbf_text,"%4x",(__s16)result);
- 		QETH_DBF_TEXT2(0,trace,dbf_text);
-+		/* re-enable qeth_send_control_data again */
-+		atomic_set(&card->write_busy,0);
- 		return NULL;
- 	}
- 
- 	if (intparam==IPA_IOCTL_STATE) {
- 		if (qeth_sleepon_ioctl(card,QETH_IPA_TIMEOUT)) {
--			QETH_DBF_TEXT2(0,trace,"scd:ioctime");
-+			QETH_DBF_TEXT2(0,trace,"scd:ioct");
- 			/* re-enable qeth_send_control_data again */
- 			atomic_set(&card->write_busy,0);
- 			return NULL;
- 		}
--		rec_buf=card->ipa_buf;
-+		rec_buf=card->dma_stuff->recbuf;
- 		sprintf(dbf_text,"scro%4x",card->irq0);
- 	} else {
- 		if (qeth_sleepon(card,(setip)?QETH_IPA_TIMEOUT:
-@@ -2929,6 +3194,7 @@
-         ipa_cmd_t *reply;
-         int ipa_cmd;
-         int result;
-+	unsigned char prot;
- 
- 	/* don't muck around with ipv6 if there's no use to do so */
- 	if ( (cmd->prot_version==6) &&
-@@ -2938,7 +3204,12 @@
- 
-         memcpy(card->send_buf,IPA_PDU_HEADER,
- 	       IPA_PDU_HEADER_SIZE);
--
-+	if (card->options.layer2 == DO_LAYER2) {
-+		prot=QETH_IPA_CMD_PROT_LAYER2;
-+	} else {
-+		prot=QETH_IPA_CMD_PROT_TCPIP;
-+	}
-+	memcpy(QETH_IPA_CMD_PROT_TYPE(card->send_buf),&prot,1);
-         memcpy(QETH_IPA_CMD_DEST_ADDR(card->send_buf),
-                &card->token.ulp_connection_r,QETH_MPC_TOKEN_LENGTH);
- 
-@@ -2963,6 +3234,15 @@
- 		if ((ipa_cmd==IPA_CMD_SETADAPTERPARMS)&&(result==0)) {
- 			result=reply->data.setadapterparms.return_code;
- 		}
-+		if ( (ipa_cmd==IPA_CMD_SETASSPARMS) &&
-+		     (result==0) &&
-+		     (reply->data.setassparms.assist_no==
-+		      IPA_INBOUND_CHECKSUM) &&
-+		     (reply->data.setassparms.command_code==
-+		      IPA_CMD_ASS_START) ) {
-+			card->csum_enable_mask=
-+				reply->data.setassparms.data.flags_32bit;
-+		}
- 	}
-         return result;
- }
-@@ -2976,7 +3256,11 @@
-         cmd->seq_no=card->seqno.ipa++;
-         cmd->adapter_type=qeth_get_adapter_type_for_ipa(card->link_type);
-         cmd->rel_adapter_no=(__u8)card->options.portno;
-+	if (card->options.layer2 == DO_LAYER2) {
-+		cmd->prim_version_no=2;
-+	} else {
-         cmd->prim_version_no=1;
-+	}
-         cmd->param_count=1;
-         cmd->prot_version=ip_vers;
-         cmd->ipa_supported=0;
-@@ -3072,6 +3356,7 @@
-         int ipa_cmd;
-         int result;
- 	__u16 s1,s2;
-+	unsigned char prot;
- 
- 	/* don't muck around with ipv6 if there's no use to do so */
- 	if ( (cmd->prot_version==6) &&
-@@ -3081,6 +3366,12 @@
- 
-         memcpy(card->send_buf,IPA_PDU_HEADER,
- 	       IPA_PDU_HEADER_SIZE);
-+	if (card->options.layer2 == DO_LAYER2) {
-+		prot=QETH_IPA_CMD_PROT_LAYER2;
-+	} else {
-+		prot=QETH_IPA_CMD_PROT_TCPIP;
-+	}
-+	memcpy(QETH_IPA_CMD_PROT_TYPE(card->send_buf),&prot,1);
-         memcpy(QETH_IPA_CMD_DEST_ADDR(card->send_buf),
-                &card->token.ulp_connection_r,QETH_MPC_TOKEN_LENGTH);
-         memcpy(card->send_buf+IPA_PDU_HEADER_SIZE,
-@@ -3183,17 +3474,29 @@
- 
- static int qeth_ioctl_handle_arp_data(qeth_card_t *card, arp_cmd_t *reply) 
- {
-+	if ( (reply->data.setassparms.command_code==
-+	      IPA_CMD_ASS_ARP_SET_NO_ENTRIES) ||
-+	     (reply->data.setassparms.command_code==
-+	      IPA_CMD_ASS_ARP_ADD_ENTRY) ||
-+	     (reply->data.setassparms.command_code==
-+	      IPA_CMD_ASS_ARP_REMOVE_ENTRY) ) {
-+		if (reply->data.setassparms.return_code) {
-+			return ARP_RETURNCODE_ERROR;
-+		} else {
-+			return ARP_RETURNCODE_LASTREPLY;
-+		}
-+	}
-+	/* check for corrupt data */
-+	if (reply->data.setassparms.seq_no <= 0)
-+		return ARP_RETURNCODE_ERROR;
-+	
- 	if (reply->data.setassparms.seq_no == 1) {
- 		if (card->ioctl_buffersize <= 
- 		    (sizeof(__u16) + sizeof(int) + reply->data.
--		     setassparms.number_of_replies * ARP_DATA_SIZE)) {
-+		     setassparms.number_of_replies * ARP_DATA_SIZE))
- 			card->ioctl_returncode = ARP_RETURNCODE_ERROR;
--		} else {
-+		else
- 			card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
--			card->number_of_entries = 0;
--			card->ioctl_buffer_pointer = card->ioctl_data_buffer+
--				sizeof(__u16) + sizeof(int);
--		}
- 	}
- 
- 	if (card->ioctl_returncode != ARP_RETURNCODE_ERROR &&
-@@ -3229,6 +3532,21 @@
- 	}
- 	return card->ioctl_returncode;
- }
-+static int qeth_is_arp_command(int cmd)
-+{
-+	switch (cmd) {
-+		case IPA_CMD_ASS_ARP_SET_NO_ENTRIES:
-+		case IPA_CMD_ASS_ARP_QUERY_CACHE:
-+		case IPA_CMD_ASS_ARP_ADD_ENTRY:
-+		case IPA_CMD_ASS_ARP_REMOVE_ENTRY:
-+		case IPA_CMD_ASS_ARP_FLUSH_CACHE:
-+		case IPA_CMD_ASS_ARP_QUERY_INFO:
-+		case IPA_CMD_ASS_ARP_QUERY_STATS:
-+			return 1;
-+		default:
-+			return 0;
-+	}
-+}
- 
- static int qeth_look_for_arp_data(qeth_card_t *card) 
- {
-@@ -3245,9 +3563,7 @@
- 		result=ARP_FLUSH;
- 	} else if ( (reply->command == IPA_CMD_SETASSPARMS) &&
- 	     (reply->data.setassparms.assist_no == IPA_ARP_PROCESSING) &&
--	     (reply->data.setassparms.command_code == 
--	      IPA_CMD_ASS_ARP_QUERY_INFO) &&
--	     (card->ioctl_returncode == ARP_RETURNCODE_SUCCESS)) {
-+	     (qeth_is_arp_command(reply->data.setassparms.command_code)) ) {
- 		result = qeth_ioctl_handle_arp_data(card,reply);
- 	} else if ( (reply->command == IPA_CMD_SETADAPTERPARMS)	&&
- 	            (reply->data.setadapterparms.command_code == 
-@@ -3286,8 +3602,13 @@
-         cmd->data.setassparms.return_code=0;
- 	cmd->data.setassparms.seq_no=0;
- 
-+	/* initialize ioctl pointers and buffer sizes */
- 	card->ioctl_buffersize = data_size;
- 	card->ioctl_data_buffer = (char *) vmalloc(data_size);
-+	memset(card->ioctl_data_buffer, 0, data_size);
-+	card->ioctl_buffer_pointer = card->ioctl_data_buffer +
-+		sizeof(__u16) + sizeof(int); /* flags and # of entries */
-+	card->number_of_entries = 0;
- 	if (!card->ioctl_data_buffer) {
- 		kfree(cmd);
- 		return IPA_REPLY_FAILED;
-@@ -3305,8 +3626,9 @@
- 		result = IPA_REPLY_SUCCESS;
- 		memcpy(((char *)(card->ioctl_data_buffer)) + sizeof(__u16),
- 		       &(card->number_of_entries),sizeof(int));
--		copy_to_user(req->ifr_ifru.ifru_data,
--			     card->ioctl_data_buffer,data_size);		
-+		if (copy_to_user(req->ifr_ifru.ifru_data,
-+			         card->ioctl_data_buffer,data_size))
-+			result =-EFAULT;		
- 	}
- 	card->ioctl_buffer_pointer = NULL;
- 	vfree(card->ioctl_data_buffer);
-@@ -3373,16 +3695,14 @@
- 		result = IPA_REPLY_FAILED;
- 		goto snmp_out;
- 	}
--	if (result == ARP_RETURNCODE_ERROR ) {
--		copy_to_user(req->ifr_ifru.ifru_data+SNMP_REQUEST_DATA_OFFSET,
--			     card->ioctl_data_buffer,card->ioctl_buffersize);
-+	if (result == ARP_RETURNCODE_ERROR ) 
- 		result = IPA_REPLY_FAILED;
--	}
--	else {
--		copy_to_user(req->ifr_ifru.ifru_data+SNMP_REQUEST_DATA_OFFSET,
--			     card->ioctl_data_buffer,card->ioctl_buffersize);
-+	else 
- 		result = IPA_REPLY_SUCCESS;
--	}
-+
-+	if (copy_to_user(req->ifr_ifru.ifru_data + SNMP_REQUEST_DATA_OFFSET, 
-+			 card->ioctl_data_buffer, card->ioctl_buffersize))
-+		result = -EFAULT;
- snmp_out:
- 	card->number_of_entries = 0;
- 	card->ioctl_buffersize = 0;
-@@ -3556,6 +3876,43 @@
- 				 ((ipacmd==IPA_CMD_SETIPM)?IPA_SETIP_FLAG:0));
- }
- 
-+static int qeth_send_setdelmac(qeth_card_t *card,__u8 *mac, int ipacmd)
-+{
-+	ipa_cmd_t cmd;
-+
-+	qeth_fill_ipa_cmd(card,&cmd,ipacmd,4);
-+	cmd.data.setdelmac.mac_length = 6;
-+	memcpy(&cmd.data.setdelmac.mac,mac,6);
-+	return qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
-+}
-+#ifdef QETH_VLAN
-+static void qeth_send_setdel_vlan(qeth_card_t *card,int i,int ipacmd)
-+{
-+	int result;
-+	ipa_cmd_t cmd;
-+	char dbf_text[15];
-+
-+	qeth_fill_ipa_cmd(card,&cmd,ipacmd,4);
-+	cmd.data.setdelvlan.vlan_id = i;
-+	result=qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
-+
-+	if (result) {
-+		PRINT_ERR("Could not %s VLAN %i on %s: 0x%x. Continuing\n",
-+			  (ipacmd==IPA_CMD_SETVLAN)?"set":"delete",i,
-+			  card->dev_name,result);
-+		if (ipacmd==IPA_CMD_SETVLAN) {
-+			sprintf(dbf_text,"STVLF%3x",i);
-+			QETH_DBF_TEXT2(0,trace,dbf_text);
-+		} else {
-+			sprintf(dbf_text,"DLVLF%3x",i);
-+			QETH_DBF_TEXT2(0,trace,dbf_text);
-+		}
-+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
-+		QETH_DBF_TEXT2(0,trace,dbf_text);
-+	}
-+}
-+#endif
-+
- #define PRINT_SETIP_ERROR(x) \
- 	if (result) \
- 		PRINT_ERR("setip%c: return code 0x%x (%s)\n",x,result, \
-@@ -3571,6 +3928,11 @@
- 			  (result==0xe00e)?"unsupported arp assist cmd": \
- 			  (result==0xe00f)?"arp assist not enabled": \
- 			  (result==0xe080)?"startlan disabled": \
-+			  (result==0xf012)?"unicast IP address invalid": \
-+			  (result==0xf013)?"multicast router limit reached": \
-+			  (result==0xf014)?"stop assist not supported": \
-+			  (result==0xf015)?"multicast assist not set": \
-+			  (result==0xf080)?"VM: startlan disabled": \
- 			  (result==-1)?"IPA communication timeout": \
- 			  "unknown return code")
- 
-@@ -3612,7 +3974,8 @@
- 		QETH_DBF_TEXT2(0,trace,dbf_text);
- 	}
- 
--	if (((result==-1)||(result==0xe080))&&(retries--)) {
-+	if ( ((result==-1)||(result==0xe080)||(result==0xf080))&&
-+	     (retries--) ) {
- 		sprintf(dbf_text,"sipr%4x",card->irq0);
- 		QETH_DBF_TEXT2(0,trace,dbf_text);
- 		if (ip_vers==4) {
-@@ -3648,6 +4011,29 @@
- 	int retries;
- 	char dbf_text[15];
- 	
-+	if (card->options.layer2 == DO_LAYER2) {
-+		result=qeth_send_setdelmac(card,mac,IPA_CMD_SETGMAC);
-+			return 0;
-+		if (result) {
-+			QETH_DBF_TEXT2(0,trace,"SETMCFLD");
-+			QETH_DBF_HEX2(0,trace,mac,QETH_DBF_TRACE_LEN);
-+			sprintf(dbf_text,"%4x%4x",card->irq0,result);
-+			QETH_DBF_TEXT2(0,trace,dbf_text);
-+			if (result==0x2005) {
-+				PRINT_WARN("Group MAC " \
-+					  "%02x:%02x:%02x:%02x:%02x:%02x already " \
-+					  "existing on %s !\n",
-+					  mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],
-+					  card->dev_name);
-+				result = 0;
-+			} else
-+				PRINT_ERR("Could not set group MAC " \
-+					  "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
-+					  mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],
-+					  card->dev_name,result);
-+		}
-+		return result;
-+	}
- 	retries=(use_retries)?QETH_SETIP_RETRIES:1;
- 	if (qeth_is_ipa_covered_by_ipato_entries(ip_vers,ip,card)) {
- 		sprintf(dbf_text,"imto%4x",card->irq0);
-@@ -3694,6 +4080,23 @@
- static inline int qeth_send_delipm(qeth_card_t *card,__u8 *ip,
- 				   __u8 *mac,short ip_vers)
- {
-+	int result;
-+	char dbf_text[15];
-+ 
-+	if (card->options.layer2 == DO_LAYER2) {
-+		result=qeth_send_setdelmac(card,mac,IPA_CMD_DELGMAC);
-+		if (result) {
-+			QETH_DBF_TEXT2(0,trace,"DELMCFLD");
-+			QETH_DBF_HEX2(0,trace,mac,QETH_DBF_TRACE_LEN);
-+			sprintf(dbf_text,"%4x%4x",card->irq0,result);
-+			QETH_DBF_TEXT2(0,trace,dbf_text);
-+			PRINT_ERR("Could not delete group MAC " \
-+				  "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
-+				  mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],
-+				  card->dev_name,result);
-+		}
-+		return result;
-+	} else
-         return qeth_send_setdelipm(card,ip,mac,IPA_CMD_DELIPM,ip_vers);
- }
- 
-@@ -3771,8 +4174,8 @@
-      					      le is last entry */
- 	char dbf_text[15];
- 	int result;
--	__u8 netmask[16]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
--		0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
-+	__u8 netmask[16]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
- 	qeth_vipa_entry_t *priv_add_list=NULL;
- 	qeth_vipa_entry_t *priv_del_list=NULL;
- 
-@@ -3790,6 +4193,7 @@
- 					GFP_KERNEL);
- 			if (ne) {
- 				ne->version=e->version;
-+				ne->flag=e->flag;
- 				memcpy(ne->ip,e->ip,16);
- 				ne->next=priv_add_list;
- 				priv_add_list=ne;
-@@ -3821,6 +4225,7 @@
- 					GFP_KERNEL);
- 			if (ne) {
- 				ne->version=e->version;
-+				ne->flag=e->flag;
- 				memcpy(ne->ip,e->ip,16);
- 				ne->next=priv_del_list;
- 				priv_del_list=ne;
-@@ -3852,7 +4257,7 @@
- 			sprintf(dbf_text,"%4x%4x",card->irq0,result);
- 			QETH_DBF_TEXT2(0,trace,dbf_text);
- 			if (priv_add_list->version==4) {
--				PRINT_ERR("going to leave vipa/rxip %08x" \
-+				PRINT_ERR("going to leave vipa/rxip x%08x " \
- 					  "unset...\n",
- 					  *((__u32*)&priv_add_list->ip[0]));
- 				sprintf(dbf_text,"%08x",
-@@ -4214,7 +4619,8 @@
- 	sprintf(dbf_text,"stim%4x",card->irq0);
- 	QETH_DBF_TEXT3(0,trace,dbf_text);
- 
--        if (qeth_is_supported(IPA_MULTICASTING)) {
-+        if (qeth_is_supported(IPA_MULTICASTING) ||
-+	   (card->options.layer2 == DO_LAYER2)) {
- 		addr=card->ip_mc_current_state.ipm_ifa;
- 		while (addr) {
- 			if (!qeth_is_ipma_in_list(addr,card->
-@@ -4404,20 +4810,127 @@
- }
- #endif /* QETH_IPV6 */
- 
-+/* FIXME: new values for 10gig */
-+static int mdio_read(struct net_device *dev, int phy_id, int regnum)
-+{
-+	int ret_val=0;
-+	qeth_card_t *card = (qeth_card_t*)dev->priv;
-+
-+	switch(regnum){
-+		case MII_BMCR: /* Basic mode control register */
-+			/* XXX Get real values from card */
-+			ret_val = BMCR_FULLDPLX;
-+
-+			if ( ( card->link_type == 
-+			       QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET ) ||
-+			     ( card->link_type ==
-+			       QETH_MPC_LINK_TYPE_10GIG_ETHERNET ) )
-+				ret_val |= BMCR_SPEED1000;
-+			else
-+				ret_val |= BMCR_SPEED100;
-+			break;
-+
-+		case MII_BMSR: /* Basic mode status register */
-+			/* XXX Get real values from card */
-+			ret_val = BMSR_ERCAP | BMSR_ANEGCOMPLETE | 
-+				  BMSR_LSTATUS | BMSR_10HALF | BMSR_10FULL | 
-+				  BMSR_100HALF | BMSR_100FULL | BMSR_100BASE4;
-+			if ( ( card->link_type == 
-+			       QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET ) ||
-+			     ( card->link_type ==
-+			       QETH_MPC_LINK_TYPE_10GIG_ETHERNET ) )
-+				ret_val |= BMSR_EXTSTATUS;
-+			
-+			break;
-+
-+		case MII_EXTSTATUS: /* Extended status register */
-+			if ( ( card->link_type == 
-+			       QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET ) ||
-+			     ( card->link_type ==
-+			       QETH_MPC_LINK_TYPE_10GIG_ETHERNET ) )
-+				ret_val = EXTSTATUS_1000TFULL | 
-+					  EXTSTATUS_1000THALF | 
-+					  EXTSTATUS_1000XFULL | 
-+					  EXTSTATUS_1000XHALF;
-+			else
-+				ret_val = 0;
-+			break;
-+
-+		case MII_PHYSID1: /* PHYS ID 1 */
-+			ret_val = ( dev->dev_addr[0] << 16 ) | 
-+				  ( dev->dev_addr[1] << 8) | dev->dev_addr[2];
-+			ret_val = (ret_val >> 5) & 0xFFFF;
-+			break;
-+
-+		case MII_PHYSID2: /* PHYS ID 2 */
-+			ret_val = (dev->dev_addr[2] << 10) & 0xFFFF;
-+			break;
-+
-+		case MII_ADVERTISE: /* Advertisement control reg */
-+			ret_val = ADVERTISE_ALL;
-+			break;
-+
-+		case MII_LPA: /* Link partner ability reg */
-+			ret_val = LPA_10HALF | LPA_10FULL | LPA_100HALF | 
-+				  LPA_100FULL | LPA_100BASE4 | LPA_LPACK;
-+			break;
-+
-+		case MII_EXPANSION: /* Expansion register */
-+			ret_val = 0;
-+			break;
-+		
-+		default:
-+			ret_val = 0;
-+			break;
-+	}
-+
-+	return ret_val;
-+}
-+                                                                                                                                                                             
-+static void mdio_write(struct net_device *dev,int phy_id,int regnum,int value)
-+{
-+	/* writing MII registers is not yet implmented */
-+
-+	switch(regnum){
-+		case MII_BMCR:          /* Basic mode control register */
-+		case MII_BMSR:          /* Basic mode status register  */
-+		case MII_ADVERTISE:     /* Advertisement control reg   */
-+		case MII_LPA:           /* Link partner ability reg    */
-+		case MII_EXPANSION:     /* Expansion register          */
-+		case MII_DCOUNTER:      /* Disconnect counter          */
-+		case MII_FCSCOUNTER:    /* False carrier counter       */
-+		case MII_NWAYTEST:      /* N-way auto-neg test reg     */
-+		case MII_RERRCOUNTER:   /* Receive error counter       */
-+		case MII_SREVISION:     /* Silicon revision            */
-+		case MII_LBRERROR:      /* Lpback, rx, bypass error    */
-+		case MII_PHYADDR:       /* PHY address                 */
-+		case MII_TPISTATUS:     /* TPI status for 10mbps       */
-+		case MII_NCONFIG:       /* Network interface config    */
-+		case MII_PHYSID1:       /* PHYS ID 1                   */
-+		case MII_PHYSID2:       /* PHYS ID 2                   */
-+		case MII_RESV1:         /* Reserved...                 */
-+		case MII_RESV2:         /* Reserved...                 */
-+		default:
-+			break;
-+	}
-+}
-+
- #define QETH_STANDARD_RETVALS \
- 		ret_val=-EIO; \
- 		if (result==IPA_REPLY_SUCCESS) ret_val=0; \
-+		if (result==-EFAULT) ret_val=-EFAULT; \
- 		if (result==IPA_REPLY_FAILED) ret_val=-EIO; \
- 		if (result==IPA_REPLY_OPNOTSUPP) ret_val=-EOPNOTSUPP
- 
- static int qeth_do_ioctl(struct net_device *dev,struct ifreq *rq,int cmd)
- {
--	char *data;
- 	int result,i,ret_val;
- 	int version=4;
- 	qeth_card_t *card;
- 	char dbf_text[15];
--	char buff[100];
-+	char data[100];
-+        struct mii_ioctl_data* mii_data = 
-+			(struct mii_ioctl_data*)&(rq->ifr_ifru.ifru_data);
- 
- 	card=(qeth_card_t*)dev->priv;
- 
-@@ -4427,20 +4940,20 @@
- 	QETH_DBF_TEXT2(0,trace,dbf_text);
- 	QETH_DBF_HEX2(0,trace,&rq,sizeof(void*));
- 
--	if ((cmd<SIOCDEVPRIVATE) || (cmd>SIOCDEVPRIVATE+5))
--		return -EOPNOTSUPP;
--	copy_from_user(buff,rq->ifr_ifru.ifru_data,sizeof(buff));
--	data=buff;
--
- 	if ( (!atomic_read(&card->is_registered))||
- 	     (!atomic_read(&card->is_hardsetup))||
- 	     (atomic_read(&card->is_gone)) ) return -ENODEV;
- 
- 	if (atomic_read(&card->shutdown_phase)) return -ENODEV;
- 
--	my_spin_lock(&card->ioctl_lock);
-+	if (down_interruptible ( &card->ioctl_sem ) )
-+		return -ERESTARTSYS;
- 
--	if (atomic_read(&card->shutdown_phase)) return -ENODEV;
-+	if (atomic_read(&card->shutdown_phase)) {
-+		ret_val=-ENODEV;
-+		goto out;
-+	}
-+
- 	if ( (!atomic_read(&card->is_registered))||
- 	     (!atomic_read(&card->is_hardsetup))||
- 	     (atomic_read(&card->is_gone)) ) {
-@@ -4449,71 +4962,154 @@
- 	}
- 
- 	switch (cmd) {
--	case SIOCDEVPRIVATE+0:
--		if (!capable(CAP_NET_ADMIN)) {
-+	case SIOCDEVPRIVATE+0:		
-+	case SIOC_QETH_ARP_SET_NO_ENTRIES:
- 			ret_val=-EPERM;
-+		if (!capable(CAP_NET_ADMIN) ||
-+		     (card->options.layer2 == DO_LAYER2)) {
- 			break;
- 		}
--		result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
-+		result=qeth_send_setassparms(card,version,
-+					     IPA_ARP_PROCESSING,
- 					     IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
- 					     rq->ifr_ifru.ifru_ivalue,4);
- 		QETH_STANDARD_RETVALS;
- 		if (result==3) ret_val=-EINVAL;
- 		break;
- 	case SIOCDEVPRIVATE+1:
--		if (!capable(CAP_NET_ADMIN)) {
-+	case SIOC_QETH_ARP_QUERY_INFO:
-+		if (!capable(CAP_NET_ADMIN) ||
-+		    (card->options.layer2 == DO_LAYER2)) {
- 			ret_val=-EPERM;
- 			break;
- 		}
--		result = qeth_queryarp(card,rq,version,IPA_ARP_PROCESSING,
-+
-+		if (copy_from_user(data,rq->ifr_ifru.ifru_data,
-+				  sizeof(data))) {
-+			ret_val = -EFAULT;
-+			break;
-+		}
-+
-+		result = qeth_queryarp(card,rq,version,
-+				       IPA_ARP_PROCESSING,
- 				       IPA_CMD_ASS_ARP_QUERY_INFO,data,4);
- 
- 		QETH_STANDARD_RETVALS;
- 		break;
- 	case SIOCDEVPRIVATE+2:
--		if (!capable(CAP_NET_ADMIN)) {
-+	case SIOC_QETH_ARP_ADD_ENTRY:
-+		if (!capable(CAP_NET_ADMIN) ||
-+		    (card->options.layer2 == DO_LAYER2)) {
- 			ret_val=-EPERM;
- 			break;
- 		}
-+
-+		if (copy_from_user(data,rq->ifr_ifru.ifru_data,
-+		 		  sizeof(data))) {
-+			ret_val = -EFAULT;
-+			break;
-+		}
-+
- 		for (i=12;i<24;i++) if (data[i]) version=6;
--		result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
-+		result=qeth_send_setassparms(card,version,
-+					     IPA_ARP_PROCESSING,
- 					     IPA_CMD_ASS_ARP_ADD_ENTRY,
- 					     (long)data,56);
- 		QETH_STANDARD_RETVALS;
- 		break;
- 	case SIOCDEVPRIVATE+3:
--		if (!capable(CAP_NET_ADMIN)) {
-+	case SIOC_QETH_ARP_REMOVE_ENTRY:
-+		if (!capable(CAP_NET_ADMIN) ||
-+		    (card->options.layer2 == DO_LAYER2)) {
- 			ret_val=-EPERM;
- 			break;
- 		}
--		for (i=4;i<12;i++) if (data[i]) version=6;
--		result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
-+
-+		if (copy_from_user(data,rq->ifr_ifru.ifru_data,
-+				   sizeof(data))) {
-+			ret_val = -EFAULT;
-+			break;
-+		}
-+
-+		for (i=12;i<24;i++) if (data[i]) version=6;
-+		result=qeth_send_setassparms(card,version,
-+					     IPA_ARP_PROCESSING,
- 					     IPA_CMD_ASS_ARP_REMOVE_ENTRY,
- 					     (long)data,16);
- 		QETH_STANDARD_RETVALS;
- 		break;
- 	case SIOCDEVPRIVATE+4:
--		if (!capable(CAP_NET_ADMIN)) {
-+	case SIOC_QETH_ARP_FLUSH_CACHE:
-+		if (!capable(CAP_NET_ADMIN) ||
-+			(card->options.layer2 == DO_LAYER2)) {
- 			ret_val=-EPERM;
- 			break;
- 		}
--		result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
-+
-+		result=qeth_send_setassparms(card,version,
-+					     IPA_ARP_PROCESSING,
- 					     IPA_CMD_ASS_ARP_FLUSH_CACHE,
- 					     0,0);
- 		QETH_STANDARD_RETVALS;
- 		break;
- 	case SIOCDEVPRIVATE+5:
--		result=qeth_send_snmp_control(card,rq,IPA_CMD_SETADAPTERPARMS,
-+	case SIOC_QETH_ADP_SET_SNMP_CONTROL:
-+		if (copy_from_user(data,rq->ifr_ifru.ifru_data,
-+				   sizeof(data))) {
-+			ret_val = -EFAULT;
-+			break;
-+		}
-+
-+		result=qeth_send_snmp_control(card,rq,
-+					      IPA_CMD_SETADAPTERPARMS,
- 					      IPA_SETADP_SET_SNMP_CONTROL,
- 					      data,4);
- 		QETH_STANDARD_RETVALS;
- 		break;
-+	case SIOCDEVPRIVATE+6:
-+	case SIOC_QETH_GET_CARD_TYPE:
-+		if (!card->is_guest_lan &&
-+		    (card->type == QETH_CARD_TYPE_OSAE))
-+			ret_val = 1;
-+		else
-+			ret_val = 0;
-+		break;
-+	case SIOCGMIIPHY:
-+		mii_data->phy_id = 0; /* for now we set this 
-+					 fixed to one phy with ID 0 */
-+		ret_val = 0;
-+		break;
- 
-+	case SIOCGMIIREG:
-+		if(mii_data->phy_id != 0) {
-+			ret_val = -EINVAL;
-+			break;
-+		}
-+		mii_data->val_out = mdio_read(dev, mii_data->phy_id, 
-+					      mii_data->reg_num);
-+		ret_val = 0;
-+		break;
-+
-+	case SIOCSMIIREG:
-+		if (!capable(CAP_NET_ADMIN)) {
-+			ret_val=-EPERM;
-+			break;
-+		}
-+		if(mii_data->phy_id != 0) {
-+			ret_val = -EINVAL;
-+			break;
-+		}
-+		mdio_write(dev, mii_data->phy_id, 
-+			   mii_data->reg_num, mii_data->val_in );
-+		ret_val = 0;
-+		break;
-+
- 	default:
--		return -EOPNOTSUPP;
-+		ret_val=-EOPNOTSUPP;
-+		goto out;
- 	}
- out:
--	my_spin_unlock(&card->ioctl_lock);
-+	up (&card->ioctl_sem);
- 
- 	sprintf(dbf_text,"ret=%4x",ret_val);
- 	QETH_DBF_TEXT2(0,trace,dbf_text);
-@@ -4710,7 +5306,8 @@
- 	}
- #ifdef QETH_VLAN
- 	QETH_DBF_TEXT4(0,trace,"tovipm6s");
--	if ( (qeth_is_supported(IPA_FULL_VLAN)) &&
-+	if ( ((card->options.layer2 == DO_LAYER2) ||
-+              (qeth_is_supported(IPA_FULL_VLAN))) &&
- 	     (atomic_read(&card->is_open)) ) {
- 		card_group = (struct vlan_group *) card->vlangrp;
- 		if (card_group) for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
-@@ -4785,6 +5382,53 @@
- }
- #endif /* QETH_IPV6 */
- 
-+#ifdef QETH_VLAN
-+/* ATT: not a very readable order: bytes count from lower numbers, bits
-+   count from lsb */
-+static void qeth_set_bit(__u8 *ptr,int i)
-+{
-+	ptr[i/8]|=0x80>>(i%8);
-+}
-+
-+static int qeth_get_bit(__u8 *ptr,int i)
-+{
-+	return (ptr[i/8]&(0x80>>(i%8)))?1:0;
-+}
-+
-+static void qeth_takeover_vlans(qeth_card_t *card)
-+{
-+	int i;
-+
-+	/* copy new to current */
-+	memcpy(&card->vlans_current[0],
-+	       &card->vlans_new[0],
-+	       VLAN_GROUP_ARRAY_LEN/(8*sizeof(__u8)));
-+
-+	/* clear new vector */
-+	memset(&card->vlans_new[0],0,VLAN_GROUP_ARRAY_LEN/(8*sizeof(__u8)));
-+
-+	for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
-+		if ( (card->vlangrp) &&
-+		     (card->vlangrp->vlan_devices[i]) )
-+			qeth_set_bit(&card->vlans_new[0],i);
-+	}
-+}
-+
-+static void qeth_set_vlans(qeth_card_t *card)
-+{
-+	int i;
-+
-+	for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
-+		if ( (qeth_get_bit(&card->vlans_current[0],i)) &&
-+		     (!qeth_get_bit(&card->vlans_new[0],i)) )
-+			qeth_send_setdel_vlan(card,i,IPA_CMD_DELVLAN);
-+		if ( (!qeth_get_bit(&card->vlans_current[0],i)) &&
-+		     (qeth_get_bit(&card->vlans_new[0],i)) )
-+			qeth_send_setdel_vlan(card,i,IPA_CMD_SETVLAN);
-+	}
-+}
-+#endif
-+
- static void qeth_clear_ifa4_list(struct in_ifaddr **ifa_list)
- {
- 	struct in_ifaddr *ifa;
-@@ -4962,7 +5606,8 @@
- 
- #ifdef QETH_VLAN
- 	QETH_DBF_TEXT4(0,trace,"to-vipms");
--	if ( (qeth_is_supported(IPA_FULL_VLAN)) &&
-+	if ( ((card->options.layer2 == DO_LAYER2) ||
-+              (qeth_is_supported(IPA_FULL_VLAN))) &&
- 	     (atomic_read(&card->is_open)) ) {
- 		card_group = (struct vlan_group *) card->vlangrp;
-                 if (card_group) for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
-@@ -5192,7 +5837,51 @@
- 		}
- 	}
- }
-+static int qeth_do_layer2_mac_stuff(qeth_card_t *card) 
-+{
-+	int result;
-+	char dbf_text[15];
- 
-+	sprintf(dbf_text,"dols%4x",card->irq0);
-+	QETH_DBF_TEXT4(0,trace,dbf_text);
-+
-+	if (atomic_read(&card->mac_registered))  
-+	    return 0;
-+	/* get mac addr */
-+	/* we read the mac regardless of the return code */
-+	result=qeth_send_setadapterparms_change_addr(card,
-+			     IPA_SETADP_ALTER_MAC_ADDRESS,
-+			     CHANGE_ADDR_READ_MAC,card->dev->dev_addr,
-+			     OSA_ADDR_LEN);
-+	if (result) {
-+		PRINT_WARN("couldn't get Layer 2 MAC address on " \
-+			   "irq 0x%x: x%x\n",card->irq0,result);
-+		QETH_DBF_TEXT1(0,trace,"L2NMCADD");
-+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
-+		QETH_DBF_TEXT1(1,trace,dbf_text);
-+		return result;
-+	} else {
-+		QETH_DBF_HEX2(0,setup,card->dev->dev_addr,
-+			      __max(OSA_ADDR_LEN,QETH_DBF_SETUP_LEN));
-+		QETH_DBF_HEX3(0,trace,card->dev->dev_addr,
-+			      __max(OSA_ADDR_LEN,QETH_DBF_TRACE_LEN));
-+	}
-+	result=qeth_send_setdelmac(card,&card->dev->dev_addr[0],
-+				   IPA_CMD_SETVMAC);
-+	if (result) {
-+		PRINT_WARN("couldn't register MAC address on " \
-+			   "irq 0x%x: x%x\n",card->irq0,result);
-+		QETH_DBF_TEXT1(0,trace,"NOREGMAC");
-+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
-+		QETH_DBF_TEXT1(1,trace,dbf_text);
-+		atomic_set(&card->mac_registered,0);
-+	} else {
-+		atomic_set(&card->mac_registered,1);
-+	}
-+	return 0; /* it's ok if SETVMAC fails -- we'll track that in
-+		     mac_registered */
-+}
-+
- static int qeth_softsetup_card(qeth_card_t *card,int wait_for_lock)
- {
-         int result;
-@@ -5240,13 +5929,17 @@
- 					   "failure -- please check the " \
- 					   "network, plug in the cable or " \
- 					   "enable the OSA port":
-+					   (result==0xf080)?
-+					   "startlan disabled (VM: LAN " \
-+					   "is offline for functions " \
-+					   "requiring LAN access.":
- 					   "unknown return code");
- 				sprintf(dbf_text,"stln%4x",result);
- 				QETH_DBF_TEXT2(0,trace,dbf_text);
- 				atomic_set(&card->is_softsetup,0);
- 				atomic_set(&card->is_startlaned,0);
- 				/* do not return an error */
--				if (result==0xe080) {
-+				if ((result==0xe080)||(result==0xf080)) {
- 					result=0;
- 				}
- 				goto out;
-@@ -5255,6 +5948,20 @@
- 		}
- 		netif_wake_queue(card->dev);
- 
-+		if (card->options.layer2 == DO_LAYER2) {
-+			card->dev->features |=
-+				NETIF_F_HW_VLAN_TX |
-+				NETIF_F_HW_VLAN_RX;
-+			card->dev->flags|=IFF_MULTICAST|IFF_BROADCAST;
-+			card->broadcast_capable=1;
-+			result=qeth_do_layer2_mac_stuff(card);
-+			if (result) {
-+				atomic_set(&card->is_softsetup,0);
-+				return result;
-+			}
-+			atomic_set(&card->is_softsetup,1);
-+			goto layer2_1;
-+		} else
- 		qeth_do_setadapterparms_stuff(card);
- 
-                 if (!qeth_is_supported(IPA_ARP_PROCESSING)) {
-@@ -5372,7 +6079,8 @@
- 					QETH_DBF_TEXT2(0,trace,dbf_text);
- 					atomic_set(&card->is_softsetup,0);
- 					/* do not return an error */
--					if (result==0xe080) {
-+					if ((result==0xe080)||
-+					    (result==0xf080)) {
- 						result=0;
- 					}
- 					goto out;
-@@ -5390,9 +6098,10 @@
- 				goto out;
- 			}
- 
--			sprintf(dbf_text,"%4x%4x",card->ipa6_supported,
--				card->ipa6_enabled);
-+			sprintf(dbf_text,"%8x",card->ipa6_supported);
- 			QETH_DBF_TEXT2(0,trace,dbf_text);
-+			sprintf(dbf_text,"%8x",card->ipa6_enabled);
-+			QETH_DBF_TEXT2(0,trace,dbf_text);
- 			QETH_DBF_TEXT2(0,trace,"enaipv46");
-                         result=qeth_send_setassparms_simple_with_data(
-                                 card,IPA_IPv6,IPA_CMD_ASS_START,3);
-@@ -5461,8 +6170,19 @@
- 				goto go_on_filt;
- 			}
- 			card->dev->flags|=IFF_BROADCAST;
--			card->broadcast_capable=1;
-+			card->broadcast_capable=BROADCAST_WITH_ECHO;
- 
-+			result=qeth_send_setassparms_simple_with_data(
-+                                card,IPA_FILTERING,IPA_CMD_ASS_ENABLE,1);
-+			sprintf(dbf_text,"Flt3%4x",result);
-+			QETH_DBF_TEXT2(0,trace,dbf_text);
-+			QETH_DBF_TEXT2(0,setup,dbf_text);
-+			if (!result) {
-+				PRINT_INFO("Broadcast packets will not be " \
-+					   "echoed back on %s.\n",
-+					   card->dev_name);
-+				card->broadcast_capable=BROADCAST_WITHOUT_ECHO;
-+			}
- 		}
- go_on_filt:
- 		if (card->options.checksum_type==HW_CHECKSUMMING) {
-@@ -5494,7 +6214,7 @@
- 				result=qeth_send_setassparms_simple_with_data(
- 					card,IPA_INBOUND_CHECKSUM,
- 					IPA_CMD_ASS_ENABLE,
--					IPA_CHECKSUM_ENABLE_MASK);
-+					card->csum_enable_mask);
- 				if (result) {
- 					PRINT_WARN("Could not enable inbound " \
- 						   "checksumming on %s: " \
-@@ -5571,7 +6291,14 @@
- 
- #ifdef QETH_IPV6
- 	if (atomic_read(&card->enable_routing_attempts6)) {
--		if (card->options.routing_type6) {
-+		/* for OSAs that can't do v6 multicast routing, we don't try */
-+		if ( (card->type==QETH_CARD_TYPE_OSAE) &&
-+		     ( (card->options.routing_type6&ROUTER_MASK) ==
-+		       MULTICAST_ROUTER) &&
-+		     (!qeth_is_supported6(IPA_OSA_MC_ROUTER_AVAIL)) ) {
-+			atomic_set(&card->enable_routing_attempts6,0);
-+			atomic_set(&card->rt6fld,0);
-+		} else if (card->options.routing_type6) {
- 			sprintf(dbf_text,"strtg6%2x",
- 				card->options.routing_type6);
- 			QETH_DBF_TEXT2(0,trace,dbf_text);
-@@ -5625,13 +6352,22 @@
- 	}
- #endif /* QETH_IPV6 */
- 
--	QETH_DBF_TEXT2(0,trace,"delvipa");
--	qeth_set_vipas(card,0);
-+	if (card->options.layer2 == DONT_LAYER2) {
-+		QETH_DBF_TEXT2(0,trace,"delvipa");
-+		qeth_set_vipas(card,0);
-+	}
-+layer2_1:
- 	QETH_DBF_TEXT2(0,trace,"toip/ms");
- 	qeth_takeover_ip_ipms(card);
- #ifdef QETH_IPV6
- 	qeth_takeover_ip_ipms6(card);
- #endif /* QETH_IPV6 */
-+	if (card->options.layer2 == DO_LAYER2) {
-+#ifdef QETH_VLAN
-+		qeth_takeover_vlans(card);
-+#endif
-+		goto layer2_2;
-+	}
- 	QETH_DBF_TEXT2(0,trace,"setvipa");
- 	qeth_set_vipas(card,1);
- 
-@@ -5644,7 +6380,12 @@
- 		atomic_set(&card->is_softsetup,0);
-                 goto out;
-         }
--
-+layer2_2:
-+#ifdef QETH_VLAN
-+ 	if (card->options.layer2 == DO_LAYER2) {
-+		qeth_set_vlans(card);
-+	}
-+#endif
-         result=qeth_setipms(card,use_setip_retries);
-         if (result) { /* by now, qeth_setipms does not return errors */
-                 PRINT_WARN("couldn't set up multicast IPs on %s: 0x%x\n",
-@@ -5753,9 +6494,12 @@
- 	sprintf(dbf_text,"PROB%4x",i);
- 	QETH_DBF_TEXT2(0,trace,dbf_text);
- 
--        PRINT_WARN("recovery was scheduled on irq 0x%x (%s) with " \
--		   "problem 0x%x\n",
--                   card->irq0,card->dev_name,i);
-+	if (i!=PROBLEM_TX_TIMEOUT) {
-+		PRINT_WARN("recovery was scheduled on irq 0x%x (%s) with " \
-+			   "problem 0x%x\n",
-+			   card->irq0,card->dev_name,i);
-+	}
-+
-         switch (i) {
-         case PROBLEM_RECEIVED_IDX_TERMINATE:
- 		if (atomic_read(&card->in_recovery))
-@@ -5802,7 +6546,7 @@
-         }
- }
- 
--static void qeth_schedule_recovery(qeth_card_t *card)
-+static inline void qeth_schedule_recovery(qeth_card_t *card)
- {
- 	if (card) {
-    		INIT_LIST_HEAD(&card->tqueue.list);
-@@ -5858,7 +6602,7 @@
- 			return;
- 		}
- 		sbalf15=(card->inbound_qdio_buffers[(first_element+count-1)&
--			 QDIO_MAX_BUFFERS_PER_Q].
-+			 (QDIO_MAX_BUFFERS_PER_Q-1)].
- 			 element[15].flags)&&0xff;
- 		PRINT_STUPID("inbound qdio transfer error on irq 0x%04x. " \
- 			     "qdio_error=0x%x (more than one: %c), " \
-@@ -5920,6 +6664,9 @@
- 	card=(qeth_card_t *)card_ptr;
- 
- 	if (status&QDIO_STATUS_LOOK_FOR_ERROR) {
-+		sbalf15=(card->outbound_ringbuffer[queue]->buffer[
-+			 (first_element+count-1)&
-+			 (QDIO_MAX_BUFFERS_PER_Q-1)].element[15].flags)&0xff;
- 		if (status&QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
- 			problem=PROBLEM_ACTIVATE_CHECK_CONDITION;
- 			PRINT_WARN("activate queues on irq 0x%x: " \
-@@ -5937,9 +6684,29 @@
- 			qeth_schedule_recovery(card);
- 			goto out;
- 		}
--		sbalf15=(card->outbound_ringbuffer[queue]->buffer[
--			 (first_element+count-1)&
--			 QDIO_MAX_BUFFERS_PER_Q].element[15].flags)&0xff;
-+		if ( (siga_error==0x01) ||
-+		     (siga_error==(0x02|QDIO_SIGA_ERROR_B_BIT_SET)) ||
-+		     (siga_error==0x03) ) {
-+			sprintf(dbf_text,"BS%4x%2x",card->irq0,queue);
-+			QETH_DBF_TEXT2(0,trace,dbf_text);
-+			QETH_DBF_TEXT2(0,qerr,dbf_text);
-+			QETH_DBF_TEXT2(1,setup,dbf_text);
-+			sprintf(dbf_text,"%2x%2x%2x%2x",
-+				first_element+count-1,
-+				siga_error,qdio_error,sbalf15);
-+			QETH_DBF_TEXT2(1,trace,dbf_text);
-+			QETH_DBF_TEXT2(1,qerr,dbf_text);
-+			PRINT_ERR("Outbound queue x%x on irq x%x (%s); " \
-+				  "errs: siga: x%x, qdio: x%x, flags15: " \
-+				  "x%x. The device will be taken down.\n",
-+				  queue,card->irq0,card->dev_name,
-+				  siga_error,qdio_error,sbalf15);
-+			netif_stop_queue(card->dev);
-+			qeth_set_dev_flag_norunning(card);
-+			atomic_set(&card->problem,PROBLEM_BAD_SIGA_RESULT);
-+			qeth_schedule_recovery(card);
-+			goto out;
-+		}
- 		PRINT_STUPID("outbound qdio transfer error on irq %04x, " \
- 			     "queue=%i. qdio_error=0x%x (more than one: %c)," \
- 			     " siga_error=0x%x (more than one: %c), " \
-@@ -5988,7 +6755,7 @@
- 			      (&card->outbound_used_buffers[queue])<=
- 			      LOW_WATERMARK_PACK);
- 		/* first_element is the last buffer that we got back
--		 * from hydra */
-+		 * from OSA */
- 		if (switch_state||last_pci_hit) {
- 			*((__u16*)(&dbf_text2[6]))=card->irq0;
- 			QETH_DBF_HEX3(0,trace,dbf_text2,QETH_DBF_TRACE_LEN);
-@@ -6010,6 +6777,14 @@
- 				if (switch_state)
- 					card->send_state[queue]=
- 						SEND_STATE_DONT_PACK;
-+
-+				/* reset the last_pci position to avoid
-+				 * races, when we get close to packing again
-+				 * immediately (in order to never loose
-+				 * a PCI) */
-+				atomic_set(&card->last_pci_pos[queue],
-+					   (-2*QDIO_MAX_BUFFERS_PER_Q));
-+
- 				netif_wake_queue(card->dev);
- 				atomic_set(&card->outbound_ringbuffer_lock[
- 					   queue],QETH_LOCK_UNLOCKED);
-@@ -6137,8 +6912,9 @@
-                 goto wakeup_out;
-         }
- 
--        if (!IS_IPA(card->dma_stuff->recbuf)||
--            IS_IPA_REPLY(card->dma_stuff->recbuf)) {
-+        if ( (!IS_IPA(card->dma_stuff->recbuf))||
-+	     (IS_IPA(card->dma_stuff->recbuf)&&
-+	      IS_IPA_REPLY(card->dma_stuff->recbuf)) ) {
-                 /* setup or unknown data */
- 		result = qeth_look_for_arp_data(card);
- 		switch (result) {
-@@ -6387,10 +7163,21 @@
- static void qeth_softshutdown(qeth_card_t *card)
- {
- 	char dbf_text[15];
-+	int result;
- 
- 	sprintf(dbf_text,"ssht%4x",card->irq0);
- 	QETH_DBF_TEXT3(0,trace,dbf_text);
--
-+	if (card->options.layer2 == DO_LAYER2) {
-+		result=qeth_send_setdelmac(card,&card->dev->dev_addr[0],
-+					   IPA_CMD_DELVMAC);
-+		if (result) {
-+			PRINT_WARN("couldn't de-register MAC address on " \
-+				   "irq 0x%x: x%x\n",card->irq0,result);
-+			QETH_DBF_TEXT1(0,trace,"NODRGMAC");
-+			sprintf(dbf_text,"%4x%4x",card->irq0,result);
-+			QETH_DBF_TEXT1(1,trace,dbf_text);
-+		}
-+	}
-         qeth_send_stoplan(card);
- }
- 
-@@ -6669,6 +7456,7 @@
- 		atomic_set(&card->is_registered,0);
- 	}
- 
-+	if (card->options.layer2 == DONT_LAYER2)
- 	qeth_put_unique_id(card);
- 
- 	QETH_DBF_TEXT2(0,trace,"clrcard");
-@@ -6716,15 +7504,6 @@
- 	qeth_start_softsetup_thread(card);
- }
- 
--static int qeth_set_mac_address(struct net_device *dev,void *addr)
--{
--	char dbf_text[15];
--
--	sprintf(dbf_text,"stmc%4x",((qeth_card_t *)dev->priv)->irq0);
--	QETH_DBF_TEXT2(0,trace,dbf_text);
--	return -EOPNOTSUPP;
--}
--
- static int qeth_neigh_setup(struct net_device *dev,struct neigh_parms *np)
- {
- 	char dbf_text[15];
-@@ -6870,6 +7649,10 @@
- 	card->portname_required=
- 		((!QETH_IDX_NO_PORTNAME_REQUIRED(card->dma_stuff->recbuf))&&
- 		 (card->type==QETH_CARD_TYPE_OSAE));
-+	/* however, as the portname indication of OSA is wrong, we have to
-+         * do this: */
-+	card->portname_required=(card->type==QETH_CARD_TYPE_OSAE);
-+		
- 
-         memcpy(&temp,QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->recbuf),2);
-         if (temp!=qeth_peer_func_level(card->func_level)) {
-@@ -7111,11 +7894,18 @@
- 	__u16 len;
- 	__u8 link_type;
- 	int result;
-+	char prot_type;
- 	char dbf_text[15];
- 
-         memcpy(card->send_buf,ULP_ENABLE,ULP_ENABLE_SIZE);
- 
- 	*(QETH_ULP_ENABLE_LINKNUM(card->send_buf))=(__u8)card->options.portno;
-+	/*is it layer2 or tcpip*/
-+	if (card->options.layer2 == DO_LAYER2)
-+		prot_type = QETH_ULP_ENABLE_PROT_LAYER2;
-+	else
-+		prot_type = QETH_ULP_ENABLE_PROT_TCPIP;
-+ 	memcpy(QETH_ULP_ENABLE_PROT_TYPE(card->send_buf),&prot_type,1);
- 
-         memcpy(QETH_ULP_ENABLE_DEST_ADDR(card->send_buf),
-                &card->token.cm_connection_r,QETH_MPC_TOKEN_LENGTH);
-@@ -7220,12 +8010,17 @@
- static int qeth_qdio_establish(qeth_card_t *card)
- {
- 	int result;
--	char adapter_area[15];
-+	char *adapter_area;
- 	char dbf_text[15];
- 	void **input_array,**output_array,**ptr;
- 	int i,j;
- 	qdio_initialize_t init_data;
- 
-+	adapter_area=vmalloc(QDIO_MAX_BUFFERS_PER_Q*sizeof(char));
-+	if (!adapter_area) return -ENOMEM;
-+
-+	memset(adapter_area,0,QDIO_MAX_BUFFERS_PER_Q*sizeof(char));
-+
- 	adapter_area[0]=_ascebc['P'];
- 	adapter_area[1]=_ascebc['C'];
- 	adapter_area[2]=_ascebc['I'];
-@@ -7235,7 +8030,10 @@
- 	*((unsigned int*)(&adapter_area[12]))=PCI_TIMER_VALUE;
- 
- 	input_array=vmalloc(QDIO_MAX_BUFFERS_PER_Q*sizeof(void*));
--	if (!input_array) return -ENOMEM;
-+	if (!input_array) {
-+		vfree(adapter_area);
-+		return -ENOMEM;
-+	}
- 	ptr=input_array;
- 	for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
- 		*ptr=(void*)virt_to_phys
-@@ -7247,6 +8045,7 @@
- 			     card->no_queues);
- 	if (!output_array) {
- 		vfree(input_array);
-+		vfree(adapter_area);
- 		return -ENOMEM;
- 	}
- 	ptr=output_array;
-@@ -7285,6 +8084,7 @@
- 	
- 	vfree(input_array);
- 	vfree(output_array);
-+	vfree(adapter_area);
- 
- 	sprintf(dbf_text,"qde=%4i",result);
- 	QETH_DBF_TEXT3(0,trace,dbf_text);
-@@ -7349,19 +8149,17 @@
- 	for (;tmp&&(!result);tmp=tmp->next) {
- 		if (atomic_read(&tmp->shutdown_phase))
- 			continue;
--		if (dev==tmp->dev) {
-+		if (dev==tmp->dev)
- 			result=QETH_VERIFY_IS_REAL_DEV;
--		}
- #ifdef QETH_VLAN
- 		/* check all vlan devices */
- 		vlan_grp = (struct vlan_group *) tmp->vlangrp;
- 		if (vlan_grp) {
- 			for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
--				if (vlan_grp->vlan_devices[i]==dev) {
-+				if (vlan_grp->vlan_devices[i]==dev)
- 					result=QETH_VERIFY_IS_VLAN_DEV;
- 				}
- 			}
--		}
- 
- #endif
- 	}
-@@ -7388,7 +8186,111 @@
- 	return result;
- }
- 
-+static int qeth_set_mac_address(struct net_device *dev,void *p)
-+{
-+	char dbf_text[15];
-+	struct sockaddr *addr=p;
-+	qeth_card_t *card;
-+	int result;
-+#ifdef QETH_VLAN
-+	if (qeth_verify_dev(dev)!=QETH_VERIFY_IS_REAL_DEV) {
-+		QETH_DBF_TEXT2(0,trace,"setmcINV");
-+		return -EOPNOTSUPP;
-+	}
-+#endif
-+	card=(qeth_card_t *)dev->priv;
-+	if (card->options.layer2 != DO_LAYER2) {
-+		QETH_DBF_TEXT2(0,trace,"setmc_l3");
-+		PRINT_WARN("Setting MAC address on %s is not supported "
-+			   "in Layer 3 mode\n",dev->name);
-+		return -EOPNOTSUPP;
-+	}
-+
-+	sprintf(dbf_text,"stmc%4x",card->irq0);
-+	QETH_DBF_TEXT2(0,trace,dbf_text);
-+	QETH_DBF_TEXT2(0,setup,dbf_text);
-+	QETH_DBF_HEX2(0,trace,addr->sa_data,6);
-+	QETH_DBF_HEX2(0,setup,addr->sa_data,6);
-+
-+	if (atomic_read(&card->mac_registered)) {
-+		result=qeth_send_setdelmac(card,&card->dev->dev_addr[0],
-+					   IPA_CMD_DELVMAC);
-+		if (result) {
-+			PRINT_WARN("couldn't de-register MAC address on " \
-+				   "irq 0x%x: x%x\n",card->irq0,result);
-+			QETH_DBF_TEXT1(0,trace,"STMCFLDd");
-+			sprintf(dbf_text,"%4x%4x",card->irq0,result);
-+			QETH_DBF_TEXT1(1,trace,dbf_text);
-+			return -EIO;
-+		}
-+		atomic_set(&card->mac_registered,0);
-+	}
-+
-+	result=qeth_send_setdelmac(card,addr->sa_data,IPA_CMD_SETVMAC);
-+	if (result) {
-+		PRINT_WARN("couldn't register MAC address on " \
-+			   "irq 0x%x: x%x\n",card->irq0,result);
-+		QETH_DBF_TEXT1(0,trace,"STMCFLDr");
-+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
-+		QETH_DBF_TEXT1(1,trace,dbf_text);
-+		return -EIO;
-+	}
-+	atomic_set(&card->mac_registered, 1);
-+	memcpy(dev->dev_addr,addr->sa_data,ETH_ALEN);
-+	PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
-+		   "successfully registered on device %s\n",
-+		   dev->dev_addr[0],dev->dev_addr[1],dev->dev_addr[2],
-+		   dev->dev_addr[3],dev->dev_addr[4],dev->dev_addr[5],
-+		   dev->name);
-+	return 0;
-+}
-+
-+static void qeth_correct_routing_status(qeth_card_t *card)
-+{
-+	if (card->type==QETH_CARD_TYPE_IQD) {
-+		/* if it's not a mc router, it's no router */
-+		if ( (card->options.routing_type4 == PRIMARY_ROUTER) ||
-+		     (card->options.routing_type4 == SECONDARY_ROUTER) 
- #ifdef QETH_IPV6
-+		     ||
-+		     (card->options.routing_type6 == PRIMARY_ROUTER) ||
-+		     (card->options.routing_type6 == SECONDARY_ROUTER) 
-+#endif /* QETH_IPV6 */
-+		     ) {
-+			PRINT_WARN("routing not applicable, reset " \
-+				   "routing status.\n");
-+			card->options.routing_type4=NO_ROUTER;
-+#ifdef QETH_IPV6
-+			card->options.routing_type6=NO_ROUTER;
-+#endif /* QETH_IPV6 */
-+		}
-+		card->options.do_prio_queueing=NO_PRIO_QUEUEING;
-+	} else {
-+		/* if it's a mc router, it's no router */
-+		if ( (((!qeth_is_supported(IPA_OSA_MC_ROUTER_AVAIL))&&
-+		       card->options.routing_type4 == MULTICAST_ROUTER)) ||
-+		     (card->options.routing_type4 == PRIMARY_CONNECTOR) ||
-+		     (card->options.routing_type4 == SECONDARY_CONNECTOR) 
-+#ifdef QETH_IPV6
-+		     ||
-+		     (((!qeth_is_supported(IPA_OSA_MC_ROUTER_AVAIL))&&
-+		       card->options.routing_type6 == MULTICAST_ROUTER)) ||
-+		     (card->options.routing_type6 == PRIMARY_CONNECTOR) ||
-+		     (card->options.routing_type6 == SECONDARY_CONNECTOR) 
-+#endif /* QETH_IPV6 */
-+		     ) {
-+			PRINT_WARN("routing not applicable, reset " \
-+				   "routing status. (Did you mean " \
-+				   "primary_router or secondary_router?)\n");
-+			card->options.routing_type4=NO_ROUTER;
-+#ifdef QETH_IPV6
-+			card->options.routing_type6=NO_ROUTER;
-+#endif /* QETH_IPV6 */
-+		}
-+	}
-+}
-+
-+#ifdef QETH_IPV6
- extern struct neigh_table arp_tbl;
- int (*qeth_old_arp_constructor)(struct neighbour *);
- static struct neigh_ops arp_direct_ops_template =
-@@ -7411,13 +8313,31 @@
-         char dbf_text[15];
-         struct net_device *dev = neigh->dev;
-         struct in_device *in_dev = in_dev_get(dev);
-+	int is_qeth_device;
-+	qeth_card_t *card;
-+	int tweak_neighbour=1;
- 
-         if (in_dev == NULL)
-                 return -EINVAL;
- 
-         QETH_DBF_TEXT4(0,trace,"arpconst");
--        if (!qeth_verify_dev(dev)) {
- 	    	
-+        is_qeth_device=qeth_verify_dev(dev);
-+	/* so if we're dealing wiht a qeth device, we've got to
-+	 * check, whether we're in layer 2 mode. if yes, don't
-+	 * mind fiddling with the neighbours */
-+	if (is_qeth_device) {
-+		if (is_qeth_device==QETH_VERIFY_IS_VLAN_DEV)
-+			card=(qeth_card_t *)VLAN_DEV_INFO(dev)->
-+				real_dev->priv;
-+		else
-+			card=(qeth_card_t*)dev->priv;
-+	       	if (card->options.layer2 == DO_LAYER2)
-+			tweak_neighbour=0;
-+	} else
-+		tweak_neighbour=0;
-+
-+        if (!tweak_neighbour) {
-                 in_dev_put(in_dev);
-                 return qeth_old_arp_constructor(neigh);
-         }
-@@ -7490,10 +8410,14 @@
- 	card->hard_header_cache=qeth_get_hard_header_cache(card->link_type);
- 	card->header_cache_update=
- 		qeth_get_header_cache_update(card->link_type);
--	card->type_trans=qeth_get_type_trans(card->link_type);
- }
- #endif /* QETH_IPV6 */
- 
-+static void qeth_tt_init_card(qeth_card_t *card)
-+{
-+	card->type_trans=qeth_get_type_trans(card->link_type);
-+}
-+
- #ifdef QETH_VLAN
- static void qeth_vlan_rx_register(struct net_device *dev, 
- 				  struct vlan_group *grp)
-@@ -7503,6 +8427,7 @@
-         spin_lock_irq(&card->vlan_lock);
-         card->vlangrp = grp;
-         spin_unlock_irq(&card->vlan_lock);
-+	qeth_start_softsetup_thread(card);
- }
- static void qeth_vlan_rx_kill_vid(struct net_device *dev, 
- 				  unsigned short vid)
-@@ -7513,6 +8438,7 @@
- 	if (card->vlangrp)
-                 card->vlangrp->vlan_devices[vid] = NULL;
-         spin_unlock_irq(&card->vlan_lock);
-+	qeth_start_softsetup_thread(card);
- }
- 
- #endif /*QETH_VLAN*/
-@@ -7557,39 +8483,43 @@
- 
- 	dev->rebuild_header=
- #ifdef QETH_IPV6
--		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
-+		(!(qeth_get_additional_dev_flags(card)&IFF_NOARP))?
- 		(qeth_get_rebuild_header(card->link_type)?
- 		 qeth_rebuild_header:NULL):
- #endif /* QETH_IPV6 */
--		NULL;
-+		(card->options.layer2 == DO_LAYER2) ? 
-+		qeth_get_rebuild_header(card->link_type) : NULL;
- 	dev->hard_header=
- #ifdef QETH_IPV6
--		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
-+		(!(qeth_get_additional_dev_flags(card)&IFF_NOARP))?
- 		(qeth_get_hard_header(card->link_type)?
- 		 qeth_hard_header:NULL):
-+#else /* QETH_IPV6 */
-+		(card->options.fake_ll==FAKE_LL)?qeth_fake_header:
- #endif /* QETH_IPV6 */
--		NULL;
-+		(card->options.layer2 == DO_LAYER2) ? 
-+		qeth_get_hard_header(card->link_type) : NULL;
- 	dev->header_cache_update=
- #ifdef QETH_IPV6
--		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
-+		(!(qeth_get_additional_dev_flags(card)&IFF_NOARP))?
- 		(qeth_get_header_cache_update(card->link_type)?
- 		 qeth_header_cache_update:NULL):
- #endif /* QETH_IPV6 */
--		NULL;
-+		(card->options.layer2 == DO_LAYER2) ? 
-+		qeth_get_header_cache_update(card->link_type) : NULL;
- 	dev->hard_header_cache=
- #ifdef QETH_IPV6
--		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
-+		(!(qeth_get_additional_dev_flags(card)&IFF_NOARP))?
- 		qeth_get_hard_header_cache(card->link_type):
- #endif /* QETH_IPV6 */
--		NULL;
-+		(card->options.layer2 == DO_LAYER2) ? 
-+		qeth_get_hard_header_cache(card->link_type) : NULL;
-         dev->hard_header_parse=NULL;
-         dev->destructor=qeth_destructor;
-         dev->set_multicast_list=qeth_set_multicast_list;
-         dev->set_mac_address=qeth_set_mac_address;
-         dev->neigh_setup=qeth_neigh_setup;
--
--	dev->flags|=qeth_get_additional_dev_flags(card->type);
--
-+	dev->flags|=qeth_get_additional_dev_flags(card);
- 	dev->flags|=(
- 		     (card->options.fake_broadcast==FAKE_BROADCAST)||
- 		     (card->broadcast_capable)
-@@ -7600,11 +8530,12 @@
- 	qeth_send_qipassist(card,4);*/
- 
- 	/* that was the old place. one id. we need to make sure, that
--	 * hydra knows about us going to use the same id again, so we
-+	 * OSA knows about us going to use the same id again, so we
- 	 * do that in hardsetup_card every time
- 	qeth_get_unique_id(card);*/
- 
- #ifdef CONFIG_SHARED_IPV6_CARDS
-+/*in Layer2 mode unique id is zero*/
- 	dev->features=(card->unique_id&UNIQUE_ID_NOT_BY_CARD)?
- 		0:NETIF_F_SHARED_IPV6;
- 	dev->dev_id=card->unique_id&0xffff;
-@@ -7621,6 +8552,7 @@
- #ifdef QETH_IPV6
- 	qeth_ipv6_init_card(card);
- #endif /* QETH_IPV6 */
-+	qeth_tt_init_card(card);
- 
- 	dev_init_buffers(dev);
-  
-@@ -7650,8 +8582,9 @@
- 	card->unit_addr2 = prcd[31];
- 	card->cula = prcd[63];
- 	/* Don't build queues with diag98 for VM guest lan. */
--	card->do_pfix = (MACHINE_HAS_PFIX) ? ((prcd[0x10]!=_ascebc['V']) ||
--					      (prcd[0x11]!=_ascebc['M'])):0;
-+	card->is_guest_lan= ((prcd[0x10]==_ascebc['V']) &&
-+			     (prcd[0x11]==_ascebc['M']));
-+	card->do_pfix = (MACHINE_HAS_PFIX) ? (!(card->is_guest_lan)):0;
- 
- 	sprintf(dbf_text,"chpid:%02x",card->chpid);
- 	QETH_DBF_TEXT2(0,trace,dbf_text);
-@@ -8032,6 +8965,7 @@
- 
- 	/* here we need to know, whether we should include a value
- 	 * into eui-64 address generation */
-+	if (card->options.layer2 == DONT_LAYER2) {
- 	QETH_DBF_TEXT2(0,trace,"qipassi4");
- 	r=qeth_send_qipassist(card,4);
- 	if (r) {
-@@ -8040,10 +8974,16 @@
- 		sprintf(dbf_text,"QIP4%4x",r);
- 		QETH_DBF_TEXT2(0,trace,dbf_text);
- 	}
-+	}
- 
--	sprintf(dbf_text,"%4x%4x",card->ipa_supported,card->ipa_enabled);
-+	sprintf(dbf_text,"%8x",card->ipa_supported);
- 	QETH_DBF_TEXT2(0,trace,dbf_text);
-+	sprintf(dbf_text,"%8x",card->ipa_enabled);
-+	QETH_DBF_TEXT2(0,trace,dbf_text);
- 
-+	qeth_correct_routing_status(card);
-+
-+	if (card->options.layer2 == DONT_LAYER2) 
- 	qeth_get_unique_id(card);
- 
- 	/* print out status */
-@@ -8094,7 +9034,8 @@
- 			       "card%s%s%s\n" \
- 			       "with link type %s (portname: %s)\n",
- 			       card->devno0,card->devno1,card->devno2,
--			       qeth_get_cardname(card->type),
-+			       qeth_get_cardname(card->type,
-+						 card->is_guest_lan),
- 			       (card->level[0])?" (level: ":"",
- 			       (card->level[0])?card->level:"",
- 			       (card->level[0])?")":"",
-@@ -8102,16 +9043,32 @@
- 						       card->link_type),
- 			       dbf_text);
- 		} else {
--			printk("qeth: Device 0x%X/0x%X/0x%X is a%s " \
--			       "card%s%s%s\nwith link type %s " \
--			       "(no portname needed by interface)\n",
--			       card->devno0,card->devno1,card->devno2,
--			       qeth_get_cardname(card->type),
--			       (card->level[0])?" (level: ":"",
--			       (card->level[0])?card->level:"",
--			       (card->level[0])?")":"",
--			       qeth_get_link_type_name(card->type,
--						       card->link_type));
-+			if (card->options.portname[0]) {
-+				printk("qeth: Device 0x%X/0x%X/0x%X is a%s " \
-+				       "card%s%s%s\nwith link type %s " \
-+				       "(no portname needed by interface)\n",
-+				       card->devno0,card->devno1,card->devno2,
-+				       qeth_get_cardname(card->type,
-+							 card->is_guest_lan),
-+				       (card->level[0])?" (level: ":"",
-+				       (card->level[0])?card->level:"",
-+				       (card->level[0])?")":"",
-+				       qeth_get_link_type_name(card->type,
-+							       card->
-+							       link_type));
-+			} else {
-+				printk("qeth: Device 0x%X/0x%X/0x%X is a%s " \
-+				       "card%s%s%s\nwith link type %s\n",
-+				       card->devno0,card->devno1,card->devno2,
-+				       qeth_get_cardname(card->type,
-+							 card->is_guest_lan),
-+				       (card->level[0])?" (level: ":"",
-+				       (card->level[0])?card->level:"",
-+				       (card->level[0])?")":"",
-+				       qeth_get_link_type_name(card->type,
-+							       card->
-+							       link_type));
-+			}
- 		}
- 	}
-         
-@@ -8210,7 +9167,7 @@
- 			atomic_set(&card->is_startlaned,0);
- 			/* show status in /proc/qeth */
- 			atomic_set(&card->is_gone,1);
--			qeth_wakeup_procfile();
-+			qeth_snmp_notify();
- 		} else {
- 			QETH_DBF_TEXT1(0,trace,"ri-sftst");
- 			qeth_softsetup_card(card,QETH_LOCK_ALREADY_HELD);
-@@ -8223,7 +9180,7 @@
- 			qeth_restore_dev_flag_state(card);
- 			atomic_set(&card->is_gone,0);
- 			netif_wake_queue(card->dev);
--			qeth_wakeup_procfile();
-+			qeth_snmp_notify();
- 		}
- 		my_spin_unlock(&setup_lock);
- 	}
-@@ -8264,6 +9221,7 @@
- 	card->options.add_hhlen=DEFAULT_ADD_HHLEN;
- 	card->options.fake_ll=DONT_FAKE_LL;
- 	card->options.async_iqd=SYNC_IQD;
-+	card->options.layer2=QETH_DEFAULT_LAYER2;
- }
- 
- static qeth_card_t *qeth_alloc_card(void)
-@@ -8324,7 +9282,8 @@
- 	spin_lock_init(&card->wait_q_lock);
- 	spin_lock_init(&card->softsetup_lock);
- 	spin_lock_init(&card->hardsetup_lock);
--	spin_lock_init(&card->ioctl_lock);
-+	sema_init(&card->ioctl_sem, 1);
-+
- #ifdef QETH_VLAN
- 	spin_lock_init(&card->vlan_lock);
-         card->vlangrp = NULL;
-@@ -8346,6 +9305,8 @@
- 	card->ip_mc_new_state.ipm6_ifa=NULL;
- #endif /* QETH_IPV6 */
- 
-+	card->csum_enable_mask=IPA_CHECKSUM_DEFAULT_ENABLE_MASK;
-+
-         /* setup net_device stuff */
- 	card->dev->priv=card;
- 
-@@ -8697,6 +9658,11 @@
- 	doit1("dont_fake_broadcast",PARSE_FAKE_BROADCAST,
- 	      card->options.fake_broadcast,DONT_FAKE_BROADCAST);
- 	
-+	doit1("layer2",PARSE_LAYER2,
-+	      card->options.layer2,DO_LAYER2);
-+	doit1("no_layer2",PARSE_LAYER2,
-+	      card->options.layer2,DONT_LAYER2);
-+	
- 	doit1("fake_ll",PARSE_FAKE_LL,
- 	      card->options.fake_ll,FAKE_LL);
- 	doit1("dont_fake_ll",PARSE_FAKE_LL,
-@@ -8795,6 +9761,59 @@
-         return 0;
- }
- 
-+#define IGNORE_PARAM_EQ(option,value,reset_value,msg) \
-+        if (card->options.option == value) { \
-+                PRINT_ERR("%s not supported with layer 2 " \
-+                          "functionality, ignoring option on irq " \
-+                          "0x%x.\n",msg,card->irq0); \
-+                card->options.option = reset_value; \
-+        }
-+#define IGNORE_PARAM_NEQ(option,value,reset_value,msg) \
-+        if (card->options.option != value) { \
-+                PRINT_ERR("%s not supported with layer 2 " \
-+                          "functionality, ignoring option on irq " \
-+                          "0x%x.\n",msg,card->irq0); \
-+                card->options.option = reset_value; \
-+        }
-+
-+
-+static void qeth_make_parameters_consistent(qeth_card_t *card) 
-+{
-+
-+	if (card->options.layer2 == DO_LAYER2) {
-+		if (card->type == QETH_CARD_TYPE_IQD) {
-+			PRINT_ERR("Device on irq 0x%x does not support " \
-+				  "layer 2 functionality. "  \
-+				  "Ignoring layer2 option.\n",card->irq0 );
-+		}
-+		IGNORE_PARAM_NEQ(routing_type4,NO_ROUTER,NO_ROUTER,
-+				 "Routing options are");
-+#ifdef QETH_IPV6
-+		IGNORE_PARAM_NEQ(routing_type6,NO_ROUTER,NO_ROUTER,
-+				 "Routing options are");
-+#endif /* QETH_IPV6 */
-+		IGNORE_PARAM_EQ(checksum_type,HW_CHECKSUMMING,
-+				QETH_CHECKSUM_DEFAULT,
-+				"Checksumming options are");
-+		IGNORE_PARAM_NEQ(broadcast_mode,BROADCAST_ALLRINGS,
-+				 BROADCAST_ALLRINGS,
-+				 "Broadcast mode options are");
-+		IGNORE_PARAM_NEQ(macaddr_mode,MACADDR_NONCANONICAL,
-+				 MACADDR_NONCANONICAL,
-+				 "Canonical MAC addr options are");
-+		IGNORE_PARAM_EQ(ena_ipat,ENABLE_TAKEOVER,
-+				 DISABLE_TAKEOVER,
-+				 "Takeover options are");
-+		IGNORE_PARAM_NEQ(fake_broadcast,DONT_FAKE_BROADCAST,
-+				 DONT_FAKE_BROADCAST,
-+				 "Broadcast faking options are");
-+		IGNORE_PARAM_NEQ(add_hhlen,DEFAULT_ADD_HHLEN,
-+				 DEFAULT_ADD_HHLEN,"Option add_hhlen is");
-+		IGNORE_PARAM_NEQ(fake_ll,DONT_FAKE_LL,
-+				 DONT_FAKE_LL,"Option fake_ll is");
-+	}
-+}
-+
- static void qeth_detach_handler(int irq,int status)
- {
-         qeth_card_t *card;
-@@ -8819,7 +9838,7 @@
- 	if ((card=qeth_get_card_by_irq(irq))) {
- 		qeth_remove_card(card,remove_method);
-         }
--	qeth_wakeup_procfile();
-+	qeth_snmp_notify();
-         my_spin_unlock(&setup_lock);
- }
- 
-@@ -8905,49 +9924,6 @@
- 	return cnt;
- }
- 
--static void qeth_correct_routing_status(qeth_card_t *card)
--{
--	if (card->type==QETH_CARD_TYPE_IQD) {
--		/* if it's not a mc router, it's no router */
--		if ( (card->options.routing_type4 == PRIMARY_ROUTER) ||
--		     (card->options.routing_type4 == SECONDARY_ROUTER) 
--#ifdef QETH_IPV6
--		     ||
--		     (card->options.routing_type6 == PRIMARY_ROUTER) ||
--		     (card->options.routing_type6 == SECONDARY_ROUTER) 
--#endif /* QETH_IPV6 */
--		     ) {
--			PRINT_WARN("routing not applicable, reset " \
--				   "routing status.\n");
--			card->options.routing_type4=NO_ROUTER;
--#ifdef QETH_IPV6
--			card->options.routing_type6=NO_ROUTER;
--#endif /* QETH_IPV6 */
--		}
--		card->options.do_prio_queueing=NO_PRIO_QUEUEING;
--	} else {
--		/* if it's a mc router, it's no router */
--		if ( (card->options.routing_type4 == MULTICAST_ROUTER) ||
--		     (card->options.routing_type4 == PRIMARY_CONNECTOR) ||
--		     (card->options.routing_type4 == SECONDARY_CONNECTOR) 
--#ifdef QETH_IPV6
--		     ||
--		     (card->options.routing_type6 == MULTICAST_ROUTER) ||
--		     (card->options.routing_type6 == PRIMARY_CONNECTOR) ||
--		     (card->options.routing_type6 == SECONDARY_CONNECTOR) 
--#endif /* QETH_IPV6 */
--		     ) {
--			PRINT_WARN("routing not applicable, reset " \
--				   "routing status. (Did you mean " \
--				   "primary_router or secondary_router?)\n");
--			card->options.routing_type4=NO_ROUTER;
--#ifdef QETH_IPV6
--			card->options.routing_type6=NO_ROUTER;
--#endif /* QETH_IPV6 */
--		}
--	}
--}
--
- static int qeth_attach_handler(int irq_to_scan,chandev_probeinfo *probeinfo)
- {
- 	int result = 0;
-@@ -9070,6 +10046,7 @@
- 		card->options.portno = 0;
- 
- 	qeth_chandev_parse_options(card,probeinfo->parmstr);
-+	qeth_make_parameters_consistent(card);
- 
- 	card->has_irq=0;
- 	card->irq0=irq;
-@@ -9102,7 +10079,6 @@
- 		goto endloop;
- 	}
- 	
--	qeth_correct_routing_status(card);
- 	qeth_insert_card_into_list(card);
- 	
- 	QETH_DBF_TEXT3(0,trace,"request0");
-@@ -9247,7 +10223,7 @@
- 			/* means, we prevent looping in
- 			 * qeth_send_control_data */
- 			atomic_set(&card->write_busy,0);
--			qeth_wakeup_procfile();
-+			qeth_snmp_notify();
- 		}
- 		my_read_unlock(&list_lock);
- 	}
-@@ -9297,7 +10273,7 @@
- 	card->tqueue_sst.sync=0;
- 	schedule_task(&card->tqueue_sst);
-  out:
--	qeth_wakeup_procfile();
-+	qeth_snmp_notify();
- 	return dev;
- 
- }
-@@ -9385,11 +10361,10 @@
- 	sprintf(dbf_text,"ipevent");
- 	QETH_DBF_TEXT3(0,trace,dbf_text);
- 	QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
--	QETH_DBF_HEX3(0,trace,&dev,sizeof(void*));
--	sprintf(dbf_text,"%08x",ifa->ifa_address);
--	QETH_DBF_TEXT3(0,trace,dbf_text);
--	sprintf(dbf_text,"%08x",ifa->ifa_mask);
--	QETH_DBF_TEXT3(0,trace,dbf_text);
-+		sprintf(dbf_text,"%08x",ifa->ifa_address);
-+		QETH_DBF_TEXT3(0,trace,dbf_text);
-+		sprintf(dbf_text,"%08x",ifa->ifa_mask);
-+		QETH_DBF_TEXT3(0,trace,dbf_text);
- 
- #ifdef QETH_VLAN
- 	if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
-@@ -9418,10 +10393,9 @@
- 	sprintf(dbf_text,"ip6event");
- 	QETH_DBF_TEXT3(0,trace,dbf_text);
- 	QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
--	QETH_DBF_HEX3(0,trace,&dev,sizeof(void*));
--	QETH_DBF_HEX3(0,trace,ifa->addr.s6_addr,QETH_DBF_TRACE_LEN);
--	QETH_DBF_HEX3(0,trace,ifa->addr.s6_addr+QETH_DBF_TRACE_LEN,
--		      QETH_DBF_TRACE_LEN);
-+		QETH_DBF_HEX3(0,trace,ifa->addr.s6_addr,QETH_DBF_TRACE_LEN);
-+		QETH_DBF_HEX3(0,trace,ifa->addr.s6_addr+QETH_DBF_TRACE_LEN,
-+			      QETH_DBF_TRACE_LEN);
- 
- #ifdef QETH_VLAN
- 	if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
-@@ -9438,8 +10412,63 @@
- 
- 	return NOTIFY_DONE;
- }
-+
-+static int 
-+qeth_multicast6_event(struct notifier_block *this,
-+		     unsigned long event, void *ptr)
-+{
-+	qeth_card_t *card;
-+	struct ifmcaddr6 *mc  = (struct ifmcaddr6 *) ptr; 
-+	struct net_device *dev = mc->idev->dev;
-+	char dbf_text[15];
-+
-+	sprintf(dbf_text,"mc6event");
-+	QETH_DBF_TEXT3(0,trace,dbf_text);
-+	QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
-+#ifdef QETH_VLAN
-+	if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
-+		card = (qeth_card_t *)VLAN_DEV_INFO(dev)->real_dev->priv;
-+        else
-+#endif
-+	card=(qeth_card_t *)dev->priv;
-+        if (qeth_does_card_exist(card)) {
-+		QETH_DBF_HEX3(0,trace,&card,sizeof(void*));
-+		qeth_save_dev_flag_state(card);
-+		qeth_start_softsetup_thread(card);
-+	}
-+
-+	return NOTIFY_DONE;
-+}
-+
- #endif /* QETH_IPV6 */
- 
-+static int 
-+qeth_multicast_event(struct notifier_block *this,
-+		     unsigned long event, void *ptr)
-+{
-+	qeth_card_t *card;
-+	struct ip_mc_list *mc  = (struct ip_mc_list *) ptr; 
-+	struct net_device *dev = mc->interface->dev;
-+	char dbf_text[15];
-+
-+	sprintf(dbf_text,"mc4event");
-+	QETH_DBF_TEXT3(0,trace,dbf_text);
-+	QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
-+#ifdef QETH_VLAN
-+	if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
-+		card = (qeth_card_t *)VLAN_DEV_INFO(dev)->real_dev->priv;
-+        else
-+#endif
-+	card=(qeth_card_t *)dev->priv;
-+        if (qeth_does_card_exist(card)) {
-+		QETH_DBF_HEX3(0,trace,&card,sizeof(void*));
-+		qeth_save_dev_flag_state(card);
-+		qeth_start_softsetup_thread(card);
-+	}
-+
-+	return NOTIFY_DONE;
-+}
-+
- static int qeth_reboot_event(struct notifier_block *this,
-    			     unsigned long event,void *ptr)
- {
-@@ -9481,11 +10510,22 @@
-         0
- };
- 
-+static struct notifier_block qeth_mc_notifier = {
-+        qeth_multicast_event,
-+        0
-+};
-+
- #ifdef QETH_IPV6
- static struct notifier_block qeth_ip6_notifier = {
-         qeth_ip6_event,
-         0
- };
-+
-+static struct notifier_block qeth_mc6_notifier = {
-+        qeth_multicast6_event,
-+        0
-+};
-+
- #endif /* QETH_IPV6 */
- 
- static struct notifier_block qeth_reboot_notifier = {
-@@ -9500,10 +10540,11 @@
- 	QETH_DBF_TEXT5(0,trace,"regnotif");
-         /* register to be notified on events */
- 	r=register_netdevice_notifier(&qeth_dev_notifier);
--        
- 	r=register_inetaddr_notifier(&qeth_ip_notifier);
-+	r=register_multicast_notifier(&qeth_mc_notifier);
- #ifdef QETH_IPV6
- 	r=register_inet6addr_notifier(&qeth_ip6_notifier);
-+	r=register_multicast6_notifier(&qeth_mc6_notifier);
- #endif /* QETH_IPV6 */
- 	r=register_reboot_notifier(&qeth_reboot_notifier);
- }
-@@ -9516,8 +10557,10 @@
- 	QETH_DBF_TEXT5(0,trace,"unregnot");
- 	r=unregister_netdevice_notifier(&qeth_dev_notifier);
- 	r=unregister_inetaddr_notifier(&qeth_ip_notifier);
-+	r=unregister_multicast_notifier(&qeth_mc_notifier);
- #ifdef QETH_IPV6
- 	r=unregister_inet6addr_notifier(&qeth_ip6_notifier);
-+	r=unregister_multicast6_notifier(&qeth_mc6_notifier);
- #endif /* QETH_IPV6 */
- 	r=unregister_reboot_notifier(&qeth_reboot_notifier);
- }
-@@ -9533,9 +10576,11 @@
- 	int size;
- 	tempinfo_t *info;
- 
-+        MOD_INC_USE_COUNT;
- 	info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
- 	if (info == NULL) {
- 		PRINT_WARN("No memory available for data\n");
-+		MOD_DEC_USE_COUNT;
- 		return -ENOMEM;
- 	} else {
- 		file->private_data = (void *) info;
-@@ -9556,6 +10601,7 @@
- 		PRINT_WARN("No memory available for data\n");
- 		vfree (info);
- 		rc=-ENOMEM;
-+		MOD_DEC_USE_COUNT;
- 		goto out;
- 	}
- 	
-@@ -9583,23 +10629,23 @@
- 			       "by_ToS");
- 		}
- 
-+		/* a '+' in the routing indicator means, that broadcast
-+		 * packets are not echoed back to the sender */
- #ifdef QETH_IPV6
--		if (atomic_read(&card->rt4fld) &&
--		    atomic_read(&card->rt6fld))
--			strcpy(router_str, "no");
--		else if (atomic_read(&card->rt4fld) ||
--			 atomic_read(&card->rt6fld))
--			strcpy(router_str, "mix");
-+		if (atomic_read(&card->rt4fld) ||
-+	   	    atomic_read(&card->rt6fld))
-+			strcpy(router_str, "FLD");
- #else /* QETH_IPV6 */
- 		if (atomic_read(&card->rt4fld))
--			strcpy(router_str, "no");
-+			strcpy(router_str, "FLD");
- #endif /* QETH_IPV6 */		
- 		else if ( ((card->options.routing_type4&ROUTER_MASK)==
- 		      PRIMARY_ROUTER) 
- #ifdef QETH_IPV6
- 		     &&
--		     ((card->options.routing_type6&ROUTER_MASK)==
--		      PRIMARY_ROUTER) 
-+		     ( ((card->options.routing_type6&ROUTER_MASK)==
-+			PRIMARY_ROUTER)||
-+		       (!qeth_is_supported(IPA_IPv6)) )
- #endif /* QETH_IPV6 */
- 		     ) {
- 			strcpy(router_str,"pri");
-@@ -9608,8 +10654,9 @@
- 		      SECONDARY_ROUTER) 
- #ifdef QETH_IPV6
- 		     &&
--		     ((card->options.routing_type6&ROUTER_MASK)==
--		      SECONDARY_ROUTER) 
-+		     ( ((card->options.routing_type6&ROUTER_MASK)==
-+			SECONDARY_ROUTER)||
-+		       (!qeth_is_supported(IPA_IPv6)) )
- #endif /* QETH_IPV6 */
- 		     ) {
- 			strcpy(router_str,"sec");
-@@ -9618,38 +10665,51 @@
- 		      MULTICAST_ROUTER) 
- #ifdef QETH_IPV6
- 		     &&
--		     ((card->options.routing_type6&ROUTER_MASK)==
--		      MULTICAST_ROUTER) 
-+		     ( ((card->options.routing_type6&ROUTER_MASK)==
-+			MULTICAST_ROUTER)||
-+		       (!qeth_is_supported(IPA_IPv6)) )
- #endif /* QETH_IPV6 */
- 		     ) {
--			strcpy(router_str,"mc");
-+			if (card->broadcast_capable==BROADCAST_WITHOUT_ECHO)
-+				strcpy(router_str,"mc+");
-+			else
-+				strcpy(router_str,"mc");
- 		} else
- 		if ( ((card->options.routing_type4&ROUTER_MASK)==
- 		      PRIMARY_CONNECTOR) 
- #ifdef QETH_IPV6
- 		     &&
--		     ((card->options.routing_type6&ROUTER_MASK)==
--		      PRIMARY_CONNECTOR) 
-+		     ( ((card->options.routing_type6&ROUTER_MASK)==
-+			PRIMARY_CONNECTOR)||
-+		       (!qeth_is_supported(IPA_IPv6)) )
- #endif /* QETH_IPV6 */
- 		     ) {
--			strcpy(router_str,"p.c");
-+			if (card->broadcast_capable==BROADCAST_WITHOUT_ECHO)
-+				strcpy(router_str,"p+c");
-+			else
-+				strcpy(router_str,"p.c");
- 		} else
- 		if ( ((card->options.routing_type4&ROUTER_MASK)==
- 		      SECONDARY_CONNECTOR)
- #ifdef QETH_IPV6
- 		     &&
--		     ((card->options.routing_type6&ROUTER_MASK)==
--		      SECONDARY_CONNECTOR) 
-+		     ( ((card->options.routing_type6&ROUTER_MASK)==
-+			SECONDARY_CONNECTOR)||
-+		       (!qeth_is_supported(IPA_IPv6)) )
- #endif /* QETH_IPV6 */
- 		     ) {
--			strcpy(router_str,"s.c");
-+			if (card->broadcast_capable==BROADCAST_WITHOUT_ECHO)
-+				strcpy(router_str,"s+c");
-+			else
-+				strcpy(router_str,"s.c");
- 		} else
- 		if ( ((card->options.routing_type4&ROUTER_MASK)==
- 		      NO_ROUTER) 
- #ifdef QETH_IPV6
- 		     &&
--		     ((card->options.routing_type6&ROUTER_MASK)==
--		      NO_ROUTER) 
-+		     ( ((card->options.routing_type6&ROUTER_MASK)==
-+			NO_ROUTER)||
-+		       (!qeth_is_supported(IPA_IPv6)) )
- #endif /* QETH_IPV6 */
- 		     ) {
- 			strcpy(router_str,"no");
-@@ -9670,7 +10730,8 @@
- 					card->chpid,
- 					card->dev_name,
- 					qeth_get_cardname_short
--					(card->type,card->link_type),
-+					(card->type,card->link_type,
-+					 card->is_guest_lan),
- 					card->options.portno);
- 		} else if (!atomic_read(&card->is_startlaned)) {
- 			length+=sprintf(buffer+length,
-@@ -9680,8 +10741,20 @@
- 					card->chpid,
- 					card->dev_name,
- 					qeth_get_cardname_short
--					(card->type,card->link_type),
-+					(card->type,card->link_type,
-+					 card->is_guest_lan),
- 					card->options.portno);
-+		} else if (card->options.layer2 == DO_LAYER2) {
-+			length+=sprintf(buffer+length,
-+					"%04X/%04X/%04X x%02X %10s %14s %2i" 
-+					"  +++ LAYER 2 +++\n",
-+					card->devno0,card->devno1,card->devno2,
-+					card->chpid,
-+					card->dev_name,
-+					qeth_get_cardname_short
-+					(card->type,card->link_type,
-+					 card->is_guest_lan),
-+					card->options.portno);
- 		} else {
- 			length+=sprintf(buffer+length,
- 					"%04X/%04X/%04X x%02X %10s %14s %2i" \
-@@ -9689,7 +10762,8 @@
- 					card->devno0,card->devno1,card->devno2,
- 					card->chpid,card->dev_name,
- 					qeth_get_cardname_short
--					(card->type,card->link_type),
-+					(card->type,card->link_type,
-+					 card->is_guest_lan),
- 					card->options.portno,
- 					checksum_str,
- 					queueing_str,router_str,bufsize_str,
-@@ -9792,7 +10866,7 @@
- 	int pos=0,end_pos;
- 	char dbf_text[15];
- 
--	if (*offset) return user_len;
-+	if (*offset>0) return user_len;
- 	buffer=vmalloc(__max(user_len+1,QETH_DBF_MISC_LEN));
- 	if (buffer == NULL)
- 		return -ENOMEM;
-@@ -10174,6 +11248,7 @@
- 	int size;
- 	char entry_type[5];
- 
-+        MOD_INC_USE_COUNT;
- 	info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
- 	if (info == NULL) {
- 		PRINT_WARN("No memory available for data\n");
-@@ -10308,16 +11383,14 @@
- {
- 	loff_t len;
- 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
--	loff_t n = *offset;
--	unsigned long pos = n;
- 	
--	if (pos != n || pos >= p_info->len) {
-+	if (*offset >= p_info->len) {
- 		return 0;
- 	} 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;
- 	}
- }
-@@ -10327,13 +11400,25 @@
- static int qeth_procfile_release(struct inode *inode,struct file *file)
- {
-    	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
-+	struct list_head *l,*n;
-+  	struct qeth_notify_list *n_entry;
- 	
- 	if (p_info) {
- 		if (p_info->data)
- 			vfree (p_info->data);
- 		vfree (p_info);
- 	}
--	
-+/*remove task-entry to notify from list */
-+	spin_lock(&notify_lock);
-+	list_for_each_safe(l, n, &notify_list) {
-+		n_entry = list_entry(l, struct qeth_notify_list, list);
-+		if (n_entry->task == current) {
-+			list_del(&n_entry->list);
-+			kfree(n_entry);
-+		}
-+	}
-+	spin_unlock(&notify_lock);
-+	MOD_DEC_USE_COUNT;
- 	return 0;
- }
- 
-@@ -10352,7 +11437,7 @@
- 	qeth_card_t *card;
- #define BUFFER_LEN (10+32+1+5+1+DEV_NAME_LEN+1)
- 
--	if (*offset) return user_len;
-+	if (*offset>0) return user_len;
- 	buffer=vmalloc(__max(__max(user_len+1,BUFFER_LEN),QETH_DBF_MISC_LEN));
- 
- 	if (buffer == NULL)
-@@ -10476,123 +11561,40 @@
- 	PRINT_ERR("unknown ipato information command\n");
- out:
- 	vfree(buffer);
--	*offset = user_len;
-+	*offset = *offset + user_len;
- #undef BUFFER_LEN
- 	return user_len;
- }
- 
--static int qeth_procfile_getinterfaces(unsigned long arg) 
-+static int qeth_snmp_register(struct task_struct *p, unsigned long arg)
- {
--	qeth_card_t *card;
-+	struct qeth_notify_list *n_entry;
-+	struct list_head *l;	
-+	QETH_DBF_TEXT5(0,trace,"snmpreg");
- 
--	char parms[16];
--	char *buffer;
--	char *buffer_pointer;
--	__u32 version,valid_fields,qeth_version,number_of_devices,if_index;
--	__u32 data_size,data_len;
--	unsigned long ioctl_flags;
--	int result=0;
--
--	/* the struct of version 0 is:
--typedef struct dev_list
--{
--  char device_name[IFNAME_MAXLEN]; // OSA-Exp device name (e.g. eth0)
--  __u32 if_index;                  // interface index from kernel
--  __u32 flags;             	   // device charateristics
--} __attribute__((packed)) DEV_LIST;
--
--typedef struct osaexp_dev_ver0
--{
--  __u32 version;                // structure version
--  __u32 valid_fields;           // bitmask of fields that are really filled
--  __u32 qeth_version;           // qeth driver version
--  __u32 number_of_devices;      // number of OSA Express devices
--  struct dev_list devices[0]; // list of OSA Express devices
--} __attribute__((packed)) OSAEXP_DEV_VER0;
--	*/
--
--	version = 0;
--	valid_fields = 0;
--	qeth_version = 0;
--	number_of_devices= 0;
--
--	copy_from_user((void*)parms,(void*)arg,sizeof(parms));
--	memcpy(&data_size,parms,sizeof(__u32));
--
--	if ( !(data_size > 0) ) 
--	     return -EFAULT;
--	if ( data_size > IOCTL_MAX_TRANSFER_SIZE ) 
--	     return -EFAULT;
--	if ( !access_ok(VERIFY_WRITE, (void *)arg, data_size) )
--	     return -EFAULT;
--
--	my_read_lock(&list_lock);
--	card = firstcard;
--#define IOCTL_USER_STRUCT_SIZE (DEV_NAME_LEN*sizeof(char)) + \
--	sizeof(__u32) + sizeof(__u32)
--	while (card) {
--	     if (card->type == QETH_CARD_TYPE_OSAE)
--		  number_of_devices=number_of_devices + IOCTL_USER_STRUCT_SIZE;
--	     card = card->next;
-+	/*check first if entry already exists*/
-+	
-+	spin_lock(&notify_lock);
-+	list_for_each(l, &notify_list) {
-+		n_entry = list_entry(l, struct qeth_notify_list, list);
-+		if (n_entry->task == p) {
-+			n_entry->signum = (int) arg;
-+			goto reg_out;
-+		}
-+			
- 	}
--#undef IOCTL_USER_STRUCT_SIZE
--	if ((number_of_devices + 4*sizeof(__u32)) >= data_size) {
--		result=-ENOMEM;
--		goto out;
--	}
--
--	number_of_devices=0;
--	card = firstcard;
--	buffer = (char *)vmalloc(data_size);
--	if (!buffer) {
--		result=-EFAULT;
--		goto out;
--	}
--	buffer_pointer = ((char *)(buffer)) + (4*sizeof(__u32)) ; 
--	while (card) {
--	     if ((card->type == QETH_CARD_TYPE_OSAE)&&
--		 (!atomic_read(&card->is_gone))&&
--		 (atomic_read(&card->is_hardsetup))&&
--		 (atomic_read(&card->is_registered))) {
--
--		  memcpy(buffer_pointer,card->dev_name,DEV_NAME_LEN);
--		  buffer_pointer = buffer_pointer + DEV_NAME_LEN;
--		  if_index=card->dev->ifindex;
--		  memcpy(buffer_pointer,&if_index,sizeof(__u32));
--		  buffer_pointer = buffer_pointer + sizeof(__u32);
--		  memcpy(buffer_pointer,&ioctl_flags,sizeof(__u32));
--		  buffer_pointer = buffer_pointer + sizeof(__u32);
--		  number_of_devices=number_of_devices+1;
--	     }
--	     card = card->next;
--	}
--
--	/* we copy the real size */
--	data_len=buffer_pointer-buffer;
--
--	buffer_pointer = buffer;
--	/* copy the header information at the beginning of the buffer */
--	memcpy(buffer_pointer,&version,sizeof(__u32));
--	memcpy(((char *)buffer_pointer)+sizeof(__u32),&valid_fields,
--	       sizeof(__u32));
--	memcpy(((char *)buffer_pointer)+(2*sizeof(__u32)),&qeth_version,
--	       sizeof(__u32));
--	memcpy(((char *)buffer_pointer)+(3*sizeof(__u32)),&number_of_devices,
--	       sizeof(__u32));
--	copy_to_user((char *)arg,buffer,data_len);
--	vfree(buffer);
--out:
--	my_read_unlock(&list_lock);
--	return result;
--
--#undef PARMS_BUFFERLENGTH
--
--};
--
--static int qeth_procfile_interfacechanges(unsigned long arg) 
--{
--	return qeth_sleepon_procfile();
--
-+	spin_unlock(&notify_lock);
-+	n_entry = (struct qeth_notify_list *)
-+			kmalloc(sizeof(struct qeth_notify_list),GFP_KERNEL);
-+	if (!n_entry)
-+		return -ENOMEM;
-+	n_entry->task = p;
-+	n_entry->signum = (int) arg;
-+	spin_lock(&notify_lock);
-+	list_add(&n_entry->list,&notify_list);
-+reg_out:
-+	spin_unlock(&notify_lock);
-+	return 0;
- } 
- 
- static int qeth_procfile_ioctl(struct inode *inode, struct file *file,
-@@ -10600,19 +11602,17 @@
- {
- 
-      int result;
--     down_interruptible(&qeth_procfile_ioctl_lock);
-+     
-      switch (cmd) {
--
--     case QETH_IOCPROC_OSAEINTERFACES:
--	     result = qeth_procfile_getinterfaces(arg);
-+     case QETH_IOCPROC_REGISTER:
-+	     if ( (arg > 0) && (arg < 32) ) 
-+		     result = qeth_snmp_register(current,arg);
-+	     else 
-+		     result = -EINVAL;
- 	     break;
--     case QETH_IOCPROC_INTERFACECHANGES:
--	     result = qeth_procfile_interfacechanges(arg);
--	     break;
-      default:
- 	     result = -EOPNOTSUPP;	     
-      }
--     up(&qeth_procfile_ioctl_lock);
-      return result;
- };
- 
-@@ -10644,10 +11644,6 @@
- 					 S_IFREG|0644,&proc_root);
- 	if (qeth_proc_file) {
- 		qeth_proc_file->proc_fops = &qeth_procfile_fops;
--		sema_init(&qeth_procfile_ioctl_sem,
--			  PROCFILE_SLEEP_SEM_MAX_VALUE);
--		sema_init(&qeth_procfile_ioctl_lock,
--			  PROCFILE_IOCTL_SEM_MAX_VALUE);
- 	} else proc_file_registration=-1;
- 
- 	if (proc_file_registration)
-@@ -10796,8 +11792,12 @@
- 	global_stay_in_mem = chandev_persist(chandev_type_qeth);
- #endif /* MODULE */
- 
--        spin_lock_init(&setup_lock);
-+/*SNMP init stuff*/
-+	spin_lock_init(&notify_lock);
-+	INIT_LIST_HEAD(&notify_list);
- 
-+	spin_lock_init(&setup_lock);
-+
- 	spin_lock_init(&ipato_list_lock);
- 
- 	qeth_get_internal_functions();
-@@ -10918,6 +11918,7 @@
- #endif /* QETH_IPV6 */
- 	qeth_unregister_dbf_views();
- 	qeth_free_all_spare_bufs();
-+
- 	return result;
- }
- 
-=== drivers/s390/net/qeth_mpc.h
-==================================================================
---- drivers/s390/net/qeth_mpc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/qeth_mpc.h   (/trunk/2.4.27)   (revision 52)
-@@ -11,7 +11,7 @@
- #ifndef __QETH_MPC_H__
- #define __QETH_MPC_H__
- 
--#define VERSION_QETH_MPC_H "$Revision: 1.42 $"
-+#define VERSION_QETH_MPC_H "$Revision: 1.42.4.5 $"
- 
- #define QETH_IPA_TIMEOUT (card->ipa_timeout)
- #define QETH_MPC_TIMEOUT 2000
-@@ -164,6 +164,9 @@
- 
- #define QETH_ULP_ENABLE_LINKNUM(buffer) (buffer+0x61)
- #define QETH_ULP_ENABLE_DEST_ADDR(buffer) (buffer+0x2c)
-+#define QETH_ULP_ENABLE_PROT_TYPE(buffer) (buffer+0x50)
-+#define QETH_ULP_ENABLE_PROT_TCPIP  0x03
-+#define QETH_ULP_ENABLE_PROT_LAYER2 0x08
- #define QETH_ULP_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53)
- #define QETH_ULP_ENABLE_PORTNAME_AND_LL(buffer) (buffer+0x62)
- 
-@@ -245,6 +248,12 @@
- 
- #define IPA_CMD_STARTLAN 0x01
- #define IPA_CMD_STOPLAN 0x02
-+#define IPA_CMD_SETVMAC 0x21
-+#define IPA_CMD_DELVMAC 0x22
-+#define IPA_CMD_SETGMAC 0x23
-+#define IPA_CMD_DELGMAC 0x24
-+#define IPA_CMD_SETVLAN 0x25
-+#define IPA_CMD_DELVLAN 0x26
- #define IPA_CMD_SETIP 0xb1
- #define IPA_CMD_DELIP 0xb7
- #define IPA_CMD_QIPASSIST 0xb2
-@@ -288,6 +297,7 @@
- #define IPA_PASSTHRU 0x00001000L
- #define IPA_FULL_VLAN 0x00004000L
- #define IPA_SOURCE_MAC_AVAIL 0x00010000L
-+#define IPA_OSA_MC_ROUTER_AVAIL 0x00020000L
- 
- #define IPA_SETADP_QUERY_COMMANDS_SUPPORTED 0x01
- #define IPA_SETADP_ALTER_MAC_ADDRESS 0x02
-@@ -331,7 +341,7 @@
- #define IPA_CMD_ASS_ARP_QUERY_INFO 0x0104
- #define IPA_CMD_ASS_ARP_QUERY_STATS 0x0204
- 
--#define IPA_CHECKSUM_ENABLE_MASK 0x001f
-+#define IPA_CHECKSUM_DEFAULT_ENABLE_MASK 0x001a
- 
- #define IPA_CMD_ASS_FILTER_SET_TYPES 0x0003
- 
-@@ -434,7 +444,15 @@
- 			__u8 type;
- 		} setrtg;
- 		struct ipa_setadp_cmd setadapterparms;
-+/*set/del Vmacs and Gmacs*/
- 		struct {
-+			__u32 mac_length;
-+			__u8 mac[6];
-+		} setdelmac;
-+		struct {
-+			__u16  vlan_id;
-+		} setdelvlan;
-+		struct {
- 			__u32 command;
- #define ADDR_FRAME_TYPE_DIX 1
- #define ADDR_FRAME_TYPE_802_3 2
-@@ -461,12 +479,8 @@
- 	} data;
- } ipa_cmd_t __attribute__ ((packed));
- 
--#define QETH_IOC_MAGIC 0x22
--#define QETH_IOCPROC_OSAEINTERFACES _IOWR(QETH_IOC_MAGIC, 1, arg)
--#define QETH_IOCPROC_INTERFACECHANGES _IOWR(QETH_IOC_MAGIC, 2, arg)
--
- #define SNMP_QUERY_CARD_INFO 0x00000002L
--#define SNMP_REGISETER_MIB   0x00000004L
-+#define SNMP_REGISTER_MIB    0x00000004L
- #define SNMP_GET_OID         0x00000010L
- #define SNMP_SET_OID         0x00000011L
- #define SNMP_GET_NEXT_OID    0x00000012L
-@@ -565,6 +579,9 @@
- 				0x00,0x00,0x00,0x40,
- };
- 
-+#define QETH_IPA_CMD_PROT_TYPE(buffer) (buffer+0x19)
-+#define QETH_IPA_CMD_PROT_TCPIP  0x03
-+#define QETH_IPA_CMD_PROT_LAYER2 0x08
- #define QETH_IPA_CMD_DEST_ADDR(buffer) (buffer+0x2c)
- 
- #define PDU_ENCAPSULATION(buffer) \
-=== drivers/s390/net/smsgiucv.h
-==================================================================
---- drivers/s390/net/smsgiucv.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/smsgiucv.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,10 @@
-+/*
-+ * IUCV special message driver
-+ *
-+ * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ * Author(s): Martin Schwidefsky (schwidefsky at de.ibm.com)
-+ */
-+
-+int  smsg_register_callback(char *, void (*)(char *));
-+void smsg_unregister_callback(char *, void (*)(char *));
-+
-=== drivers/s390/net/qeth.h
-==================================================================
---- drivers/s390/net/qeth.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/qeth.h   (/trunk/2.4.27)   (revision 52)
-@@ -15,7 +15,7 @@
- 
- #define QETH_NAME " qeth"
- 
--#define VERSION_QETH_H "$Revision: 1.113 $"
-+#define VERSION_QETH_H "$Revision: 1.113.4.9 $"
- 
- /******************** CONFIG STUFF ***********************/
- //#define QETH_DBF_LIKE_HELL
-@@ -47,7 +47,7 @@
- /********************* TUNING STUFF **************************/
- #define HIGH_WATERMARK_PACK		5
- #define LOW_WATERMARK_PACK		2
--#define WATERMARK_FUZZ			2
-+#define WATERMARK_FUZZ			1
- 
- #define QETH_MAX_INPUT_THRESHOLD 500
- #define QETH_MAX_OUTPUT_THRESHOLD 300 /* ? */
-@@ -85,6 +85,22 @@
- #define QETH_HARDSETUP_CLEAR_LAPS 3
- #define QETH_RECOVERY_HARDSETUP_RETRY 2
- 
-+/* the worst case stack usage is:
-+ * qeth_hard_start_xmit
-+ * do_QDIO
-+ * qeth_qdio_output_handler
-+ * do_QDIO
-+ * qeth_qdio_output_handler
-+ * (no more recursion as we have called netif_stop_queue)
-+ */
-+#ifdef CONFIG_ARCH_S390X
-+#define STACK_PTR_MASK 0x3fff
-+#define WORST_CASE_STACK_USAGE 1100
-+#else /* CONFIG_ARCH_S390X */
-+#define STACK_PTR_MASK 0x1fff
-+#define WORST_CASE_STACK_USAGE 800
-+#endif /* CONFIG_ARCH_S390X */
-+
- /************************* DEBUG FACILITY STUFF *********************/
- 
- #define QETH_DBF_HEX(ex,name,level,addr,len) \
-@@ -238,6 +254,7 @@
- #define QETH_MPC_LINK_TYPE_FAST_ETHERNET 0x01
- #define QETH_MPC_LINK_TYPE_HSTR 0x02
- #define QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET 0x03
-+#define QETH_MPC_LINK_TYPE_10GIG_ETHERNET 0x10
- #define QETH_MPC_LINK_TYPE_LANE_ETH100 0x81
- #define QETH_MPC_LINK_TYPE_LANE_TR 0x82
- #define QETH_MPC_LINK_TYPE_LANE_ETH1000 0x83
-@@ -249,10 +266,18 @@
- #define QETH_HEADER_SIZE	32
- #define QETH_IP_HEADER_SIZE	40
- #define QETH_HEADER_LEN_POS	8
-+/*ATT: packet length in LAYER 2 mode is at offset 0x06*/	
-+#define QETH_HEADER2_LEN_POS 	6	 
- /* flags for the header: */
- #define QETH_HEADER_PASSTHRU	0x10
- #define QETH_HEADER_IPV6	0x80
- 
-+#define QETH_ETH_MC_MAC_V4      0x0100 /* like v4 */
-+#define QETH_ETH_MC_MAC_V6      0x3333 /* like v6 */
-+/* tr mc mac is longer, but that will be enough to detect mc frames */
-+#define QETH_TR_MC_MAC_NC       0xc000 /* non-canonical */
-+#define QETH_TR_MC_MAC_C        0x0300 /* canonical */
-+
- #define QETH_CAST_FLAGS		0x07
- #define QETH_CAST_UNICAST	6
- #define QETH_CAST_MULTICAST	4
-@@ -260,6 +285,11 @@
- #define QETH_CAST_ANYCAST	7
- #define QETH_CAST_NOCAST	0
- 
-+#define QETH_QDIO_HEADER2_FLAG_MULTICAST_FRAME     0x01
-+#define QETH_QDIO_HEADER2_FLAG_BROADCAST_FRAME     0x02
-+#define QETH_QDIO_HEADER2_FLAG_UNICAST_FRAME       0x04
-+#define QETH_QDIO_HEADER2_FLAG_VLAN_FRAME          0x10
-+
- /* VLAN defines */
- #define QETH_EXT_HEADER_VLAN_FRAME	  0x01
- #define QETH_EXT_HEADER_TOKEN_ID	  0x02
-@@ -284,18 +314,6 @@
- 	}
- }
- 
--inline static unsigned short qeth_get_additional_dev_flags(int cardtype)
--{
--	switch (cardtype) {
--	case QETH_CARD_TYPE_IQD: return IFF_NOARP;
--#ifdef QETH_IPV6
--	default: return 0;
--#else /* QETH_IPV6 */
--	default: return IFF_NOARP;
--#endif /* QETH_IPV6 */
--	}
--}
--
- inline static int qeth_get_hlen(__u8 link_type)
- {
- #ifdef QETH_IPV6
-@@ -330,7 +348,6 @@
- void (*qeth_my_eth_header_cache_update)(struct hh_cache *,struct net_device *,
- 	 unsigned char *);
- 
--#ifdef QETH_IPV6
- typedef int (*__qeth_temp1)(struct sk_buff *,struct net_device *,
- 	unsigned short,void *,void *,unsigned);
- inline static __qeth_temp1 qeth_get_hard_header(__u8 link_type)
-@@ -391,7 +408,7 @@
- 	struct ethhdr *eth;
- 	
- 	skb->mac.raw=skb->data;
--	skb_pull(skb,ETH_ALEN*2+sizeof(short));
-+	skb_pull(skb,ETH_ALEN*2+2); /* dest, src, type */
- 	eth=skb->mac.ethernet;
- 	
- 	if(*eth->h_dest&1) {
-@@ -400,7 +417,10 @@
- 		else
- 			skb->pkt_type=PACKET_MULTICAST;
-    	} else {
-+		if (memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
- 		skb->pkt_type=PACKET_OTHERHOST;
-+		else
-+			skb->pkt_type=PACKET_HOST;
- 	}
- 	if (ntohs(eth->h_proto)>=1536) return eth->h_proto;
- 	if (*(unsigned short *)(skb->data) == 0xFFFF)
-@@ -417,9 +437,9 @@
- 		return tr_type_trans;
- 	default:
- 		return qeth_eth_type_trans;
-+		
- 	}
- }
--#endif /* QETH_IPV6 */
- 
- inline static const char *qeth_get_link_type_name(int cardtype,__u8 linktype)
- {
-@@ -430,6 +450,7 @@
- 		case QETH_MPC_LINK_TYPE_FAST_ETHERNET: return "Fast Eth";
- 		case QETH_MPC_LINK_TYPE_HSTR: return "HSTR";
- 		case QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET: return "Gigabit Eth";
-+		case QETH_MPC_LINK_TYPE_10GIG_ETHERNET: return "10Gig Eth";
- 		case QETH_MPC_LINK_TYPE_LANE_ETH100: return "LANE Eth100";
- 		case QETH_MPC_LINK_TYPE_LANE_TR: return "LANE TR";
- 		case QETH_MPC_LINK_TYPE_LANE_ETH1000: return "LANE Eth1000";
-@@ -529,7 +550,7 @@
- #define QETH_FAKE_LL_ADDR_LEN ETH_ALEN /* 6 */
- #define QETH_FAKE_LL_DEST_MAC_POS 0
- #define QETH_FAKE_LL_SRC_MAC_POS 6
--#define QETH_FAKE_LL_SRC_MAC_POS_IN_QDIO_HDR 6
-+#define QETH_FAKE_LL_SRC_MAC_POS_IN_QDIO_HDR 18
- #define QETH_FAKE_LL_PROT_POS 12 
- #define QETH_FAKE_LL_V4_ADDR_POS 16
- #define QETH_FAKE_LL_V6_ADDR_POS 24
-@@ -591,8 +612,10 @@
- #define PARSE_ROUTING_TYPE6 17
- #define PARSE_FAKE_LL 18
- #define PARSE_ASYNC_IQD 19
-+#define PARSE_LAYER2 20
-+#define PARSE_COUNT 21
-+#define QETH_DEFAULT_LAYER2 0
- 
--#define PARSE_COUNT 20
- 
- #define NO_PRIO_QUEUEING 0
- #define PRIO_QUEUEING_PREC 1
-@@ -603,7 +626,7 @@
- #define MULTICAST_ROUTER 3
- #define PRIMARY_CONNECTOR 4
- #define SECONDARY_CONNECTOR 5
--#define ROUTER_MASK 0xf /* used to remove SET_ROUTING_FLAG
-+#define ROUTER_MASK 0xf /* used to remove RESET_ROUTING_FLAG
- 			   from routing_type */
- #define RESET_ROUTING_FLAG 0x10 /* used to indicate, that setting
- 				   the routing type is desired */
-@@ -621,7 +644,8 @@
- #define DONT_FAKE_LL 1
- #define SYNC_IQD 0
- #define ASYNC_IQD 1
--
-+#define DONT_LAYER2 0
-+#define DO_LAYER2 1
- #define QETH_BREAKOUT_LEAVE 1
- #define QETH_BREAKOUT_AGAIN 2
- 
-@@ -629,6 +653,9 @@
- #define QETH_DONT_WAIT_FOR_LOCK 1
- #define QETH_LOCK_ALREADY_HELD 2
- 
-+#define BROADCAST_WITH_ECHO 1
-+#define BROADCAST_WITHOUT_ECHO 2
-+
- #define PROBLEM_CARD_HAS_STARTLANED 1
- #define PROBLEM_RECEIVED_IDX_TERMINATE 2
- #define PROBLEM_ACTIVATE_CHECK_CONDITION 3
-@@ -712,6 +739,7 @@
- 	int add_hhlen;
- 	int fake_ll;
- 	int async_iqd;
-+	int layer2;
- };
- 
- typedef struct qeth_hdr_t {
-@@ -782,6 +810,7 @@
- 
- 	__u8 link_type;
- 
-+	int is_guest_lan;
- 	int do_pfix; /* to avoid doing diag98 for vm guest lan devices */
- 
- 	/* inbound buffer management */
-@@ -820,15 +849,16 @@
- 	int (*hard_header_cache)(struct neighbour *,struct hh_cache *);
- 	void (*header_cache_update)(struct hh_cache *,struct net_device *,
- 				    unsigned char *);
-+#endif /* QETH_IPV6 */
- 	unsigned short (*type_trans)(struct sk_buff *,struct net_device *);
--	int type_trans_correction;
--#endif /* QETH_IPV6 */
- 
- #ifdef QETH_VLAN
-         struct vlan_group *vlangrp;
-         spinlock_t vlan_lock;
-+#endif
-+	__u8 vlans_current[VLAN_GROUP_ARRAY_LEN/(8*sizeof(__u8))];
-+	__u8 vlans_new[VLAN_GROUP_ARRAY_LEN/(8*sizeof(__u8))];
- 
--#endif
- 	char dev_name[DEV_NAME_LEN];		/* pointed to by dev->name */
- 	char dev_basename[DEV_NAME_LEN];
- 	struct net_device *dev;
-@@ -846,13 +876,14 @@
- 	atomic_t is_softsetup;		/* card is setup by softsetup */
- 	atomic_t is_open;		/* card is in use */
- 	atomic_t is_gone;               /* after a msck */
-+	atomic_t mac_registered;
- 
- 	int has_irq;			/* once a request_irq was successful */
- 
- 	/* prevents deadlocks :-O */
- 	spinlock_t softsetup_lock;
- 	spinlock_t hardsetup_lock;
--	spinlock_t ioctl_lock;
-+	struct semaphore ioctl_sem;
- 	atomic_t softsetup_thread_is_running;
- 	struct semaphore softsetup_thread_sem;
- 	struct tq_struct tqueue_sst;
-@@ -907,6 +938,8 @@
- 	__u32 ipa6_enabled;
- 	__u32 adp_supported;
- 
-+	__u32 csum_enable_mask;
-+
- 	atomic_t startlan_attempts;
- 	atomic_t enable_routing_attempts4;
- 	atomic_t rt4fld;
-@@ -1004,6 +1037,30 @@
- 	struct mydevreg_t *prev;
- } mydevreg_t;
- 
-+/*user process notification stuff */
-+spinlock_t notify_lock;
-+struct list_head notify_list;
-+struct qeth_notify_list {
-+	struct list_head list;
-+	struct task_struct *task;
-+	int signum;
-+};
-+
-+inline static unsigned short 
-+qeth_get_additional_dev_flags(qeth_card_t *card)
-+{
-+	if (card->options.layer2 == DO_LAYER2)
-+		return 0;
-+	switch (card->type) {
-+	case QETH_CARD_TYPE_IQD: return IFF_NOARP;
-+#ifdef QETH_IPV6
-+	default: return 0;
-+#else /* QETH_IPV6 */
-+	default: return IFF_NOARP;
-+#endif /* QETH_IPV6 */
-+	}
-+}
-+
- inline static int qeth_get_arphrd_type(int cardtype,int linktype)
- {
- 	switch (cardtype) {
-@@ -1011,7 +1068,7 @@
- 				  case QETH_MPC_LINK_TYPE_LANE_TR:
- 					  /* fallthrough */
- 				  case QETH_MPC_LINK_TYPE_HSTR:
--					  return ARPHRD_IEEE802;
-+					  return ARPHRD_IEEE802_TR;
- 				  default: return ARPHRD_ETHER;
- 				  }
- 	case QETH_CARD_TYPE_IQD: return ARPHRD_ETHER;
-@@ -1036,28 +1093,42 @@
- 	}
- }
- 
--inline static const char *qeth_get_cardname(int cardtype)
-+inline static const char *qeth_get_cardname(int cardtype,int is_guest_lan)
- {
--	switch (cardtype) {
--	case QETH_CARD_TYPE_UNKNOWN: return "n unknown";
--	case QETH_CARD_TYPE_OSAE: return "n OSD Express";
--	case QETH_CARD_TYPE_IQD: return " HiperSockets";
--	default: return " strange";
-+	if (is_guest_lan) {
-+		switch (cardtype) {
-+		case QETH_CARD_TYPE_UNKNOWN: return "n unknown";
-+		case QETH_CARD_TYPE_OSAE: return " Guest LAN QDIO";
-+		case QETH_CARD_TYPE_IQD: return " Guest LAN Hiper";
-+		default: return " strange";
-+		}
-+	} else {
-+		switch (cardtype) {
-+		case QETH_CARD_TYPE_UNKNOWN: return "n unknown";
-+		case QETH_CARD_TYPE_OSAE: return "n OSD Express";
-+		case QETH_CARD_TYPE_IQD: return " HiperSockets";
-+		default: return " strange";
-+		}
- 	}
- }
- 
- /* max length to be returned: 14 */
--inline static const char *qeth_get_cardname_short(int cardtype,__u8 link_type)
-+inline static const char *qeth_get_cardname_short(int cardtype,__u8 link_type,
-+						  int is_guest_lan)
- {
- 	switch (cardtype) {
- 	case QETH_CARD_TYPE_UNKNOWN: return "unknown";
--	case QETH_CARD_TYPE_OSAE: switch (link_type) {
-+	case QETH_CARD_TYPE_OSAE: if (is_guest_lan)
-+					  return "GuestLAN QDIO";
-+				  switch (link_type) {
- 			      	  case QETH_MPC_LINK_TYPE_FAST_ETHERNET:
- 		     			  return "OSD_100";
- 			      	  case QETH_MPC_LINK_TYPE_HSTR:
- 		     			  return "HSTR";
- 	     			  case QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET:
-      					  return "OSD_1000";
-+				  case QETH_MPC_LINK_TYPE_10GIG_ETHERNET:
-+					  return "OSD_10GIG";
- 				  case QETH_MPC_LINK_TYPE_LANE_ETH100:
-      					  return "OSD_FE_LANE";
- 				  case QETH_MPC_LINK_TYPE_LANE_TR:
-@@ -1068,7 +1139,7 @@
-      					  return "OSD_ATM_LANE";
- 				  default: return "OSD_Express";
- 				  }
--	case QETH_CARD_TYPE_IQD: return "HiperSockets";
-+	case QETH_CARD_TYPE_IQD: return (is_guest_lan)?"GuestLAN Hiper":"HiperSockets";
- 	default: return " strange";
- 	}
- }
-=== drivers/s390/net/iucv.c
-==================================================================
---- drivers/s390/net/iucv.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/iucv.c   (/trunk/2.4.27)   (revision 52)
-@@ -1,5 +1,5 @@
- /* 
-- * $Id: iucv.c,v 1.41 2003/06/24 16:05:32 felfert Exp $
-+ * $Id: iucv.c,v 1.40.2.5 2004/06/29 07:37:33 braunu Exp $
-  *
-  * IUCV network driver
-  *
-@@ -29,7 +29,7 @@
-  * along with this program; if not, write to the Free Software
-  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-  *
-- * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.41 $
-+ * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.40.2.5 $
-  *
-  */
- 
-@@ -320,7 +320,7 @@
- #define iucv_debug(lvl, fmt, args...) \
- do { \
- 	if (debuglevel >= lvl) \
--		printk(KERN_DEBUG __FUNCTION__ ": " fmt "\n", ## args); \
-+		printk(KERN_DEBUG "%s: " fmt "\n", __FUNCTION__, ## args); \
- } while (0)
- 
- #else
-@@ -334,13 +334,15 @@
-  * Internal functions
-  *******************************************************************************/
- 
-+static int iucv_retrieve_buffer(void);
-+
- /**
-  * print start banner
-  */
- static void
- iucv_banner(void)
- {
--	char vbuf[] = "$Revision: 1.41 $";
-+	char vbuf[] = "$Revision: 1.40.2.5 $";
- 	char *version = vbuf;
- 
- 	if ((version = strchr(version, ':'))) {
-@@ -418,6 +420,7 @@
- static void
- iucv_exit(void)
- {
-+	iucv_retrieve_buffer();
- 	if (iucv_external_int_buffer)
- 		kfree(iucv_external_int_buffer);
- 	if (iucv_param_pool)
-@@ -438,17 +441,19 @@
- static __inline__ iucv_param *
- grab_param(void)
- {
--	iucv_param *ret;
--	int i = 0;
-+	iucv_param *ptr;
-+        static int hint = 0;
- 
--	while (atomic_compare_and_swap(0, 1, &iucv_param_pool[i].in_use)) {
--		i++;
--		if (i >= PARAM_POOL_SIZE)
--			i = 0;
--	}
--	ret = &iucv_param_pool[i];
--	memset(&ret->param, 0, sizeof(ret->param));
--	return ret;
-+	ptr = iucv_param_pool + hint;
-+	do {
-+		ptr++;
-+		if (ptr >= iucv_param_pool + PARAM_POOL_SIZE)
-+			ptr = iucv_param_pool;
-+	} while (atomic_compare_and_swap(0, 1, &ptr->in_use));
-+	hint = ptr - iucv_param_pool;
-+
-+	memset(&ptr->param, 0, sizeof(ptr->param));
-+	return ptr;
- }
- 
- /**
-@@ -549,10 +554,8 @@
-  *	   - ENOMEM - storage allocation for a new pathid table failed
- */
- static int
--iucv_add_pathid(__u16 pathid, handler *handler)
-+__iucv_add_pathid(__u16 pathid, handler *handler)
- {
--	ulong flags;
--
- 	iucv_debug(1, "entering");
- 
- 	iucv_debug(1, "handler is pointing to %p", handler);
-@@ -560,21 +563,30 @@
- 	if (pathid > (max_connections - 1))
- 		return -EINVAL;
- 
--	spin_lock_irqsave (&iucv_lock, flags);
- 	if (iucv_pathid_table[pathid]) {
--		spin_unlock_irqrestore (&iucv_lock, flags);
- 		iucv_debug(1, "pathid entry is %p", iucv_pathid_table[pathid]);
- 		printk(KERN_WARNING
- 		       "%s: Pathid being used, error.\n", __FUNCTION__);
- 		return -EINVAL;
- 	}
- 	iucv_pathid_table[pathid] = handler;
--	spin_unlock_irqrestore (&iucv_lock, flags);
- 
- 	iucv_debug(1, "exiting");
- 	return 0;
- }				/* end of add_pathid function */
- 
-+static int
-+iucv_add_pathid(__u16 pathid, handler *handler)
-+{
-+	ulong flags;
-+	int rc;
-+
-+	spin_lock_irqsave (&iucv_lock, flags);
-+	rc = __iucv_add_pathid(pathid, handler);
-+	spin_unlock_irqrestore (&iucv_lock, flags);
-+	return rc;
-+}
-+
- static void
- iucv_remove_pathid(__u16 pathid)
- {
-@@ -688,7 +700,6 @@
- 	spin_lock_irqsave (&iucv_lock, flags);
- 	list_del(&handler->list);
- 	if (list_empty(&iucv_handler_table)) {
--		iucv_retrieve_buffer();
- 		if (register_flag) {
- 			unregister_external_interrupt(0x4000, iucv_irq_handler);
- 			register_flag = 0;
-@@ -764,6 +775,7 @@
- 		if (iucv_pathid_table == NULL) {
- 			printk(KERN_WARNING "%s: iucv_pathid_table storage "
- 			       "allocation failed\n", __FUNCTION__);
-+			kfree(new_handler);
- 			return NULL;
- 		}
- 		memset (iucv_pathid_table, 0, max_connections * sizeof(handler *));
-@@ -1002,6 +1014,8 @@
- 	b2f0_result = b2f0(ACCEPT, parm);
- 
- 	if (b2f0_result == 0) {
-+		if (msglim)
-+			*msglim = parm->ipmsglim;
- 		if (pgm_data)
- 			h->pgm_data = pgm_data;
- 		if (flags1_out)
-@@ -1133,11 +1147,15 @@
- 	iucv_setmask(~(AllInterrupts));
- 	messagesDisabled = 1;
- 
-+	spin_lock_irqsave (&iucv_lock, flags);
- 	parm->ipflags1 = (__u8)flags1;
- 	b2f0_result = b2f0(CONNECT, parm);
- 	memcpy(&local_parm, parm, sizeof(local_parm));
- 	release_param(parm);
- 	parm = &local_parm;
-+	if (b2f0_result == 0)
-+		add_pathid_result = __iucv_add_pathid(parm->ippathid, h);
-+	spin_unlock_irqrestore (&iucv_lock, flags);
- 
- 	if (b2f0_result) {
- 		iucv_setmask(~0);
-@@ -1145,7 +1163,6 @@
- 		return b2f0_result;
- 	}
- 
--	add_pathid_result = iucv_add_pathid(parm->ippathid, h);
- 	*pathid = parm->ippathid;
- 
- 	/* Enable everything again */
-@@ -2333,7 +2350,8 @@
- 					iucv_debug(2,
- 						   "found a matching handler");
- 					break;
--				}
-+				} else
-+					h = NULL;
- 			}
- 			spin_unlock_irqrestore (&iucv_lock, flags);
- 			if (h) {
-=== drivers/s390/net/ctcmpc.c
-==================================================================
---- drivers/s390/net/ctcmpc.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/ctcmpc.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,8876 @@
-+/*  INTERNAL VERSION:  051804b
-+ *
-+ * CTC / SNA/MPC network driver
-+ *
-+ * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ * Author(s): Fritz Elfert (elfert at de.ibm.com, felfert at millenux.com)
-+ * Fixes by : Jochen Röhrig (roehrig at de.ibm.com)
-+ *            Arnaldo Carvalho de Melo <acme at conectiva.com.br>
-+ * MPC additions: Belinda Thompson  (belindat at us.ibm.com)
-+ *		  Andy Richter  (richtera at us.ibm.com)
-+ *
-+ * Documentation used:
-+ *  - Principles of Operation (IBM doc#: SA22-7201-06)	   
-+ *  - Common IO/-Device Commands and Self Description (IBM doc#: SA22-7204-02)
-+ *  - Common IO/-Device Commands and Self Description (IBM doc#: SN22-5535)
-+ *  - ESCON Channel-to-Channel Adapter (IBM doc#: SA22-7203-00)
-+ *  - ESCON I/O Interface (IBM doc#: SA22-7202-029
-+ *
-+ * and the source of the original CTC driver by:
-+ *  Dieter Wellerdiek (wel at de.ibm.com)
-+ *  Martin Schwidefsky (schwidefsky at de.ibm.com)
-+ *  Denis Joseph Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
-+ *  Jochen Röhrig (roehrig 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.
-+ *
-+ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.2.2.2 $
-+ *
-+ */
-+
-+#include <linux/version.h>
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/kernel.h>
-+#include <linux/slab.h>
-+#include <linux/errno.h>
-+#include <linux/types.h>
-+#include <linux/interrupt.h>
-+#include <linux/timer.h>
-+#include <linux/sched.h>
-+
-+#include <linux/signal.h>
-+#include <linux/string.h>
-+#include <linux/proc_fs.h>
-+
-+#include <linux/ip.h>
-+#include <linux/if_arp.h>
-+#include <linux/tcp.h>
-+#include <linux/skbuff.h>
-+#include <linux/ctype.h>
-+#include <linux/netdevice.h>
-+#include <net/dst.h>
-+
-+#include <asm/io.h>
-+#include <asm/bitops.h>
-+#include <asm/uaccess.h>
-+#include <linux/wait.h>
-+
-+DECLARE_WAIT_QUEUE_HEAD(my_queue);
-+
-+#ifdef CONFIG_CHANDEV
-+        #define CTC_CHANDEV
-+#endif
-+
-+#ifdef CTC_CHANDEV
-+        #include <asm/chandev.h>
-+        #define REQUEST_IRQ chandev_request_irq
-+        #define FREE_IRQ chandev_free_irq
-+#else
-+        #define REQUEST_IRQ request_irq
-+        #define FREE_IRQ free_irq
-+#endif
-+
-+#include <asm/idals.h>
-+#include <asm/irq.h>
-+
-+#include "ctcmpc.h"
-+#include "fsm.h"
-+
-+#ifdef MODULE
-+MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert at millenux.com),"
-+              "Belinda Thompson (belindat at us.ibm.com)");
-+MODULE_DESCRIPTION("Linux for S/390 CTC/SNA MPC Driver");
-+        #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
-+MODULE_LICENSE("GPL");
-+        #endif
-+        #ifndef CTC_CHANDEV
-+MODULE_PARM(mpc, "s");
-+MODULE_PARM_DESC(mpc,
-+                 "One or more definitions in the same format like the "
-+                 "kernel param for mpc.\n"
-+                 "E.g.: mpc0:0x700:0x701:4:mpc1:0x702:0x703:4\n");
-+
-+char *mpc = NULL;
-+        #endif
-+#else
-+/**
-+ * Number of devices in monolithic (not module) driver version.
-+ */
-+        #define MAX_STATIC_DEVICES 16
-+#endif /* MODULE */
-+
-+//#define DEBUG2 1
-+//#define DEBUGCCW 1
-+//#define DEBUGXID 1
-+//#define DEBUGDATA 1
-+//#define DEBUGSEQ 1
-+//#define DEBUG 1
-+//#undef DEBUG
-+
-+#define ETH_P_SNA_DIX	       0x80D5
-+
-+/**
-+ * CCW commands, used in this driver.
-+ */
-+#define CCW_CMD_WRITE		0x01
-+#define CCW_CMD_READ		0x02
-+#define CCW_CMD_NOOP		0x03
-+#define CCW_CMD_TIC            0x08
-+#define CCW_CMD_SENSE_CMD	0x14
-+#define CCW_CMD_WRITE_CTL	0x17
-+#define CCW_CMD_SET_EXTENDED	0xc3  /* Disable Compatibility Mode */
-+#define CCW_CMD_PREPARE		0xe3
-+
-+#define CTC_PROTO_S390          0
-+#define CTC_PROTO_LINUX         1
-+#define CTC_PROTO_LINUX_TTY     2
-+#define CTC_PROTO_OS390         3
-+#define CTC_PROTO_MPC           4
-+#define CTC_PROTO_MAX           4
-+
-+#define CTC_BUFSIZE_LIMIT      65535
-+#define CTC_BUFSIZE_DEFAULT    32768
-+#define MPC_BUFSIZE_DEFAULT	65535
-+
-+#define CTC_TIMEOUT_5SEC        5000
-+#define CTC_TIMEOUT_1SEC        1000
-+#define CTC_BUSYWAIT_10SEC      10000
-+
-+#define CTC_INITIAL_BLOCKLEN    2
-+
-+#define READ 0
-+#define WRITE 1
-+
-+/**
-+ * Enum for classifying detected devices.
-+ */
-+enum channel_types
-+{
-+        /**
-+         * Device is not a channel.
-+         */
-+        channel_type_none,
-+
-+        /**
-+         * Device is a channel, but we don't know
-+         * anything about it.
-+         */
-+        channel_type_unknown,
-+        /**
-+         * Device is an mpc capable channel.
-+         */
-+        channel_type_mpc,
-+
-+        /**
-+         * Device is a CTC/A.
-+         */
-+        channel_type_ctca,
-+
-+        /**
-+         * Device is a ESCON channel.
-+         */
-+        channel_type_escon,
-+        /**
-+         * Device is an unsupported model.
-+         */
-+        channel_type_unsupported
-+};
-+
-+typedef enum channel_types channel_type_t;
-+
-+#ifndef CTC_CHANDEV
-+static int ctc_no_auto = 0;
-+#endif
-+
-+/**
-+ * If running on 64 bit, this must be changed. XXX Why? (bird)
-+ */
-+typedef unsigned long intparm_t;
-+
-+#ifndef CTC_CHANDEV
-+/**
-+ * Definition of a per device parameter block
-+ */
-+        #define MAX_PARAM_NAME_LEN 11
-+typedef struct param_t
-+{
-+        struct param_t *next;
-+        int            read_dev;
-+        int            write_dev;
-+        __u16          proto;
-+        char           name[MAX_PARAM_NAME_LEN];
-+} param;
-+
-+static param *params = NULL;
-+#endif
-+
-+typedef struct
-+{
-+        unsigned long maxmulti;
-+        unsigned long maxcqueue;
-+        unsigned long doios_single;
-+        unsigned long doios_multi;
-+        unsigned long txlen;
-+        unsigned long tx_time;
-+        struct timeval send_stamp;
-+} ctc_profile;
-+
-+/**
-+ * Definition of an XID2 
-+ *
-+ */
-+#define ALLZEROS 0x0000000000000000
-+
-+#define XID_FM2      0x20
-+#define XID2_0	      0x00	
-+#define XID2_7	      0x07
-+#define XID2_MAX_READ   (2**16-1)
-+#define XID2_WRITE_SIDE 0x04
-+#define XID2_READ_SIDE	0x05
-+
-+struct xid2_t
-+{
-+        __u8    xid2_type_id;
-+        __u8    xid2_len;
-+        __u32   xid2_adj_id;
-+        __u8    xid2_rlen;
-+        __u8    xid2_resv1;
-+        __u8    xid2_flag1;
-+        __u8    xid2_fmtt;
-+        __u8    xid2_flag4;
-+        __u16   xid2_resv2;
-+        __u8    xid2_tgnum;
-+        __u32   xid2_sender_id;
-+        __u8    xid2_flag2;
-+        __u8    xid2_option;
-+        char    xid2_resv3[8];
-+        __u16   xid2_resv4;
-+        __u8    xid2_dlc_type;
-+        __u16   xid2_resv5;
-+        __u8    xid2_mpc_flag;
-+        __u8    xid2_resv6;
-+        __u16   xid2_buf_len;
-+        char xid2_buffer[255-(sizeof(__u8)*13)-(sizeof(__u32)*2)-
-+                (sizeof(__u16)*4)-(sizeof(char)*8)];
-+}__attribute__ ((packed)); 
-+typedef struct xid2_t xid2;
-+
-+#define XID2_LENGTH  (sizeof(xid2))
-+
-+static const xid2 init_xid = {
-+        xid2_type_id:   XID_FM2,
-+        xid2_len:       0x45,
-+        xid2_adj_id:    0,
-+        xid2_rlen:      0x31,
-+        xid2_resv1:     0,
-+        xid2_flag1:     0,
-+        xid2_fmtt:      0,
-+        xid2_flag4:     0x80,
-+        xid2_resv2:     0,
-+        xid2_tgnum:     0,
-+        xid2_sender_id: 0,
-+        xid2_flag2:     0,
-+        xid2_option:    XID2_0,
-+        xid2_resv3:     "\x00",
-+        xid2_resv4:     0,
-+        xid2_dlc_type:  XID2_READ_SIDE,
-+        xid2_resv5:     0,
-+        xid2_mpc_flag:  0,
-+        xid2_resv6:     0,
-+        xid2_buf_len:   (MPC_BUFSIZE_DEFAULT - 35),
-+};
-+
-+struct th_header_t
-+{
-+        __u8    th_seg;
-+        __u8    th_ch_flag;
-+#define TH_HAS_PDU	0xf0
-+#define TH_IS_XID	0x01
-+#define TH_SWEEP_REQ	0xfe
-+#define TH_SWEEP_RESP	0xff
-+        __u8    th_blk_flag;
-+#define TH_DATA_IS_XID	0x80
-+#define TH_RETRY	0x40
-+#define TH_DISCONTACT	0xc0
-+#define TH_SEG_BLK	0x20
-+#define TH_LAST_SEG	0x10
-+#define TH_PDU_PART	0x08
-+        __u8    th_is_xid;      /* is 0x01 if this is XID  */
-+        __u32   th_seq_num;
-+}__attribute__ ((packed)); 
-+typedef struct th_header_t th_header;
-+
-+static const th_header thnorm = {
-+        th_seg:     0x00,
-+        th_ch_flag: TH_IS_XID,
-+        th_blk_flag:TH_DATA_IS_XID,
-+        th_is_xid:  0x01,
-+        th_seq_num: 0x00000000,
-+};
-+
-+static const th_header thdummy = {
-+        th_seg:     0x00,
-+        th_ch_flag: 0x00,
-+        th_blk_flag:TH_DATA_IS_XID,
-+        th_is_xid:  0x01,
-+        th_seq_num: 0x00000000,
-+};
-+
-+
-+struct th_addon_t
-+{
-+        __u32   th_last_seq;    
-+        __u32   th_resvd;       
-+}__attribute__ ((packed));
-+typedef struct th_addon_t th_addon;
-+
-+struct th_sweep_t
-+{
-+        th_header th;
-+        th_addon  sw;
-+}__attribute__ ((packed));
-+typedef struct th_sweep_t th_sweep;
-+
-+#define TH_HEADER_LENGTH (sizeof(th_header)) 
-+#define TH_SWEEP_LENGTH (sizeof(th_sweep)) 
-+
-+#define PDU_LAST	0x80
-+#define PDU_CNTL	0x40
-+#define PDU_FIRST	0x20
-+
-+struct pdu_t
-+{
-+        __u32   pdu_offset;
-+        __u8    pdu_flag;
-+        __u8    pdu_proto;   /*  0x01 is APPN SNA  */ 
-+        __u16   pdu_seq;
-+}__attribute__ ((packed));
-+typedef struct pdu_t pdu;
-+#define PDU_HEADER_LENGTH  (sizeof(pdu))
-+
-+struct qllc_t
-+{
-+        __u8    qllc_address;
-+#define QLLC_REQ        0xFF
-+#define QLLC_RESP       0x00
-+        __u8    qllc_commands;
-+#define QLLC_DISCONNECT 0x53
-+#define QLLC_UNSEQACK   0x73
-+#define QLLC_SETMODE	 0x93
-+#define QLLC_EXCHID	 0xBF
-+}__attribute__ ((packed));
-+typedef struct qllc_t qllc;
-+
-+
-+static void ctcmpc_bh(unsigned long);
-+
-+/**
-+ * Definition of one channel
-+ */
-+struct channel_t
-+{
-+
-+        /**
-+         * Pointer to next channel in list.
-+         */
-+        struct channel_t *next;
-+        __u16 devno;
-+        int irq;
-+        /**
-+         * Type of this channel.
-+         * CTC/A or Escon for valid channels.
-+         */
-+        channel_type_t type;
-+        /**
-+         * Misc. flags. See CHANNEL_FLAGS_... below
-+         */
-+        __u32 flags;
-+        /**
-+         * The protocol of this channel
-+         */
-+        __u16 protocol;
-+        /**
-+         * I/O and irq related stuff
-+         */
-+        ccw1_t *ccw;
-+        devstat_t *devstat;
-+        /**
-+         * RX/TX buffer size
-+         */
-+        __u32 max_bufsize;
-+        /**
-+         * Transmit/Receive buffer.
-+         */
-+        struct sk_buff *trans_skb;
-+        /**
-+         * Universal I/O queue.
-+         */
-+        struct sk_buff_head io_queue;
-+        struct tasklet_struct ch_tasklet; 
-+        /**
-+         * TX queue for collecting skb's during busy.
-+         */
-+        struct sk_buff_head collect_queue;
-+        /**
-+         * Amount of data in collect_queue.
-+         */
-+        int collect_len;
-+        /**
-+         * spinlock for collect_queue and collect_len
-+         */
-+        spinlock_t collect_lock;
-+        /**
-+         * Timer for detecting unresposive
-+         * I/O operations.
-+         */
-+        fsm_timer timer;
-+        /**
-+         * Retry counter for misc. operations.
-+         */
-+        int retry;
-+        /**
-+         * spinlock for serializing inbound SNA Segments
-+         */
-+        spinlock_t segment_lock;
-+        /**
-+          * SNA TH Seq Number
-+          */
-+        __u32           th_seq_num;
-+        __u8            th_seg;
-+        __u32           pdu_seq;
-+        sk_buff         *xid_skb;
-+        char            *xid_skb_data;
-+        th_header       *xid_th;
-+        xid2            *xid;
-+        char            *xid_id;
-+        th_header       *rcvd_xid_th;
-+        xid2            *rcvd_xid;
-+        char            *rcvd_xid_id;
-+        __u8            in_mpcgroup;
-+        fsm_timer       sweep_timer;
-+        struct sk_buff_head     sweep_queue;
-+        th_header       *discontact_th;
-+        struct tasklet_struct ch_disc_tasklet; 
-+        /**
-+         * The finite state machine of this channel
-+         */
-+        fsm_instance     *fsm;
-+        /**
-+         * The corresponding net_device this channel
-+         * belongs to.
-+         */
-+        struct net_device   *netdev;
-+        ctc_profile         prof;
-+        unsigned char       *trans_skb_data;
-+};
-+typedef struct channel_t channel;
-+
-+#define CHANNEL_FLAGS_READ                0
-+#define CHANNEL_FLAGS_WRITE               1
-+#define CHANNEL_FLAGS_INUSE               2
-+#define CHANNEL_FLAGS_BUFSIZE_CHANGED     4
-+#define CHANNEL_FLAGS_RWMASK 	           1
-+#define CHANNEL_PRIMARY		   0x20   /* we are the x side */
-+#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK)
-+
-+/**
-+ * Linked list of all detected channels.
-+ */
-+static channel *channels = NULL;
-+
-+#ifdef CTC_CHANDEV
-+static int activated;
-+#endif
-+
-+
-+/***
-+  * Definition of one MPC group
-+  */
-+
-+#define MAX_MPCGCHAN                10
-+#define MPC_XID_TIMEOUT_VALUE       10000
-+#define MPC_CHANNEL_TIMEOUT_1SEC    1000
-+#define MPC_CHANNEL_ADD 0
-+#define MPC_CHANNEL_REMOVE 1
-+#define MPC_CHANNEL_ATTN 2
-+#define XSIDE 1
-+#define YSIDE 0
-+
-+
-+
-+struct mpcg_info_t
-+{
-+        struct sk_buff        *skb;
-+        struct channel_t      *ch;
-+        struct xid2_t         *xid;
-+        struct th_sweep_t     *sweep;
-+        struct th_header_t    *th;
-+};
-+typedef struct mpcg_info_t mpcg_info;
-+
-+struct mpc_group_t
-+{
-+        struct tasklet_struct mpc_tasklet; 
-+        struct tasklet_struct mpc_tasklet2; 
-+        int     changed_side;
-+        int     saved_state;
-+        int     channels_terminating;
-+        int     out_of_sequence;
-+        int     flow_off_called;
-+        int     port_num;
-+        int     port_persist;
-+        int     alloc_called;
-+        __u32   xid2_adj_id;
-+        __u8    xid2_tgnum;
-+        __u32   xid2_sender_id;
-+        int     num_channel_paths;
-+        int     active_channels[2];
-+        __u16   group_max_buflen;
-+        int     outstanding_xid2;
-+        int     outstanding_xid7;
-+        int     outstanding_xid7_p2;
-+        int     sweep_req_pend_num;
-+        int     sweep_rsp_pend_num;
-+        sk_buff *xid_skb;
-+        char    *xid_skb_data;
-+        th_header *xid_th;
-+        xid2    *xid;
-+        char    *xid_id;
-+        th_header *rcvd_xid_th;
-+        sk_buff *rcvd_xid_skb;
-+        char    *rcvd_xid_data;
-+        __u8    in_sweep;
-+        __u8    roll;
-+        xid2    *saved_xid2;   
-+        callbacktypei2  allochanfunc;  
-+        int     allocchan_callback_retries;
-+        callbacktypei3  estconnfunc;
-+        int     estconn_callback_retries;
-+        int     estconn_called;
-+        int     xidnogood;
-+        int     send_qllc_disc;
-+        fsm_timer timer; 
-+        fsm_instance    *fsm; /* group xid fsm */
-+};
-+typedef struct mpc_group_t mpc_group;
-+
-+typedef struct ctc_priv_t
-+{
-+        struct net_device_stats stats;
-+        unsigned long           tbusy;
-+
-+        /**The MPC group struct of this interface
-+        */
-+        mpc_group               *mpcg;
-+        /**
-+         * The finite state machine of this interface.
-+         */
-+        fsm_instance            *fsm;
-+        /**
-+         * The protocol of this device
-+         */
-+        __u16                   protocol;
-+        channel                 *channel[2];
-+        xid2                    *xid;
-+        struct proc_dir_entry   *proc_dentry;
-+        struct proc_dir_entry   *proc_stat_entry;
-+        struct proc_dir_entry   *proc_ctrl_entry;
-+        int                     proc_registered;
-+        /**
-+         * Timer for restarting after I/O Errors
-+         */
-+        fsm_timer               restart_timer;
-+
-+} ctc_priv;
-+
-+
-+static void ctcmpc_action_send_discontact(unsigned long);
-+
-+
-+/**
-+ * Compatibility macros for busy handling
-+ * of network devices.
-+ */
-+static __inline__ void 
-+ctcmpc_clear_busy(struct net_device *dev)
-+{
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s enter:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+        if(((ctc_priv *)dev->priv)->mpcg->in_sweep == 0)
-+        {
-+                clear_bit(0, &(((ctc_priv *)dev->priv)->tbusy));
-+                netif_wake_queue(dev);
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+
-+
-+}
-+
-+static __inline__ int 
-+ctcmpc_test_and_set_busy(struct net_device *dev)
-+{
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s enter:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+        netif_stop_queue(dev);
-+        return test_and_set_bit(0, &((ctc_priv *)dev->priv)->tbusy);
-+}
-+
-+#define SET_DEVICE_START(device, value)
-+
-+static __inline__ int ctcmpc_checkalloc_buffer(channel *,int);
-+static void ctcmpc_purge_skb_queue(struct sk_buff_head *);
-+
-+
-+/**
-+ * Print Banner.
-+ */
-+static void 
-+print_banner(void)
-+{
-+        static int printed = 0;
-+        char vbuf[] = "$Revision: 1.2.2.2 $";
-+        char *version = vbuf;
-+
-+        if(printed)
-+                return;
-+        if((version = strchr(version, ':')))
-+        {
-+                char *p = strchr(version + 1, '$');
-+                if(p)
-+                        *p = '\0';
-+        } else
-+                version = " ??? ";
-+        printk(KERN_INFO
-+               "CTC MPC driver Version%swith"
-+#ifndef CTC_CHANDEV
-+               "out"
-+#endif
-+               " CHANDEV support"
-+#ifdef DEBUG
-+               " (DEBUG-VERSION, " __DATE__ __TIME__ ")"
-+#endif
-+               " initialized\n", version);
-+        printed = 1;
-+}
-+
-+static inline int 
-+gfp_type(void)
-+{
-+        return in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
-+}
-+
-+
-+/**
-+  * MPC Group Station FSM States
-+
-+State Name		When In This State
-+======================	=======================================	
-+MPCG_STATE_RESET	Initial State When Driver Loaded
-+                        We receive and send NOTHING
-+
-+MPCG_STATE_INOP         INOP Received. 
-+                        Group level non-recoverable error
-+
-+MPCG_STATE_READY	XID exchanges for at least 1 write and
-+                        1 read channel have completed.
-+                        Group is ready for data transfer.
-+
-+States from ctc_mpc_alloc_channel
-+==============================================================
-+MPCG_STATE_XID2INITW	Awaiting XID2(0) Initiation
-+                              ATTN from other side will start
-+                              XID negotiations.
-+                              Y-side protocol only.
-+
-+MPCG_STATE_XID2INITX	XID2(0) negotiations are in progress.
-+                              At least 1, but not all, XID2(0)'s
-+                              have been received from partner.
-+
-+MPCG_STATE_XID7INITW	XID2(0) complete 
-+                              No XID2(7)'s have yet been received.
-+                              XID2(7) negotiations pending.
-+
-+MPCG_STATE_XID7INITX	XID2(7) negotiations in progress.
-+                              At least 1, but not all, XID2(7)'s 
-+                              have been received from partner.
-+
-+MPCG_STATE_XID7INITF	XID2(7) negotiations complete.
-+                              Transitioning to READY.
-+
-+MPCG_STATE_READY	      Ready for Data Transfer.
-+                                                                        
-+
-+States from ctc_mpc_establish_connectivity call
-+==============================================================
-+MPCG_STATE_XID0IOWAIT	Initiating XID2(0) negotiations.
-+                              X-side protocol only.
-+                              ATTN-BUSY from other side will convert
-+                              this to Y-side protocol and the
-+                              ctc_mpc_alloc_channel flow will begin.
-+
-+MPCG_STATE_XID0IOWAIX	XID2(0) negotiations are in progress.
-+                              At least 1, but not all, XID2(0)'s
-+                              have been received from partner.
-+
-+MPCG_STATE_XID7INITI	XID2(0) complete 
-+                              No XID2(7)'s have yet been received.
-+                              XID2(7) negotiations pending.
-+
-+MPCG_STATE_XID7INITZ	XID2(7) negotiations in progress.
-+                              At least 1, but not all, XID2(7)'s 
-+                              have been received from partner.
-+
-+MPCG_STATE_XID7INITF	XID2(7) negotiations complete.
-+                              Transitioning to READY.
-+
-+MPCG_STATE_READY	      Ready for Data Transfer.
-+                                                                        
-+*/
-+
-+enum mpcg_events
-+{
-+        MPCG_EVENT_INOP,
-+        MPCG_EVENT_DISCONC,
-+        MPCG_EVENT_XID0DO,
-+        MPCG_EVENT_XID2,
-+        MPCG_EVENT_XID2DONE,
-+        MPCG_EVENT_XID7DONE,
-+        MPCG_EVENT_TIMER,
-+        MPCG_EVENT_DOIO,
-+        NR_MPCG_EVENTS,  
-+};
-+
-+static const char *mpcg_event_names[]  = {
-+        "INOP Condition",
-+        "Discontact Received",
-+        "Channel Active - Start XID",
-+        "XID2 Received",
-+        "XID0 Complete",
-+        "XID7 Complete",
-+        "XID Setup Timer",
-+        "XID DoIO",
-+};
-+
-+
-+enum mpcg_states
-+{
-+        MPCG_STATE_RESET,
-+        MPCG_STATE_INOP,
-+        MPCG_STATE_XID2INITW,
-+        MPCG_STATE_XID2INITX,
-+        MPCG_STATE_XID7INITW,
-+        MPCG_STATE_XID7INITX,
-+        MPCG_STATE_XID0IOWAIT,
-+        MPCG_STATE_XID0IOWAIX,
-+        MPCG_STATE_XID7INITI,
-+        MPCG_STATE_XID7INITZ,
-+        MPCG_STATE_XID7INITF,
-+        MPCG_STATE_FLOWC,
-+        MPCG_STATE_READY,
-+        NR_MPCG_STATES,
-+};
-+
-+
-+static const char *mpcg_state_names[] =  {
-+        "Reset",
-+        "INOP",
-+        "Passive XID- XID0 Pending Start",
-+        "Passive XID- XID0 Pending Complete",
-+        "Passive XID- XID7 Pending P1 Start",
-+        "Passive XID- XID7 Pending P2 Complete",
-+        "Active  XID- XID0 Pending Start",
-+        "Active  XID- XID0 Pending Complete",
-+        "Active  XID- XID7 Pending Start",
-+        "Active  XID- XID7 Pending Complete ",
-+        "XID        - XID7 Complete ",
-+        "FLOW CONTROL ON",
-+        "READY",
-+};
-+
-+#ifndef CTC_CHANDEV
-+/**
-+ * Return type of a detected device.
-+ */
-+static channel_type_t channel_type (senseid_t *id)
-+{
-+        channel_type_t type = channel_type_none;
-+
-+        switch(id->cu_type)
-+        {
-+                case 0x3088:
-+                        switch(id->cu_model)
-+                        {
-+                                case 0x1E:
-+                                        /**
-+                                         * 3088-1E = FICON channel
-+                                         */
-+                                case 0x08:
-+                                        /**
-+                                         * 3088-08 = CTCA
-+                                         */
-+                                case 0x1F:
-+                                        /**
-+                                         * 3088-1F = ESCON channel
-+                                         */
-+                                        type = channel_type_mpc;
-+                                        break;
-+
-+                                        /**
-+                                         * 3088-01 = P390 OSA emulation
-+                                         */
-+                                case 0x01:
-+                                        /* fall thru */
-+
-+                                        /**
-+                                         * 3088-60 = OSA/2 adapter
-+                                         */
-+                                case 0x60:
-+                                        /* fall thru */
-+
-+                                        /**
-+                                         * 3088-61 = CISCO 7206 CLAW proto
-+                                         * on ESCON
-+                                         */
-+                                case 0x61:
-+                                        /* fall thru */
-+
-+                                        /**
-+                                         * 3088-62 = OSA/D device
-+                                         */
-+                                case 0x62:
-+                                        type = channel_type_unsupported;
-+                                        break;
-+
-+                                default:
-+                                        type = channel_type_unknown;
-+                                        printk(KERN_INFO
-+                                               "channel: Unknown model found "
-+                                               "3088-%02x\n", id->cu_model);
-+                        }
-+                        break;
-+
-+                default:
-+                        type = channel_type_none;
-+        }
-+        return type;
-+}
-+#endif
-+
-+
-+/**
-+ * States of the interface statemachine.
-+ */
-+enum dev_states
-+{
-+        DEV_STATE_STOPPED,
-+        DEV_STATE_STARTWAIT_RXTX,
-+        DEV_STATE_STARTWAIT_RX,
-+        DEV_STATE_STARTWAIT_TX,
-+        DEV_STATE_STOPWAIT_RXTX,
-+        DEV_STATE_STOPWAIT_RX,
-+        DEV_STATE_STOPWAIT_TX,
-+        DEV_STATE_RUNNING,
-+        /**
-+         * MUST be always the last element!!
-+         */
-+        NR_DEV_STATES
-+};
-+
-+static const char *dev_state_names[] = {
-+        "Stopped",
-+        "StartWait RXTX",
-+        "StartWait RX",
-+        "StartWait TX",
-+        "StopWait RXTX",
-+        "StopWait RX",
-+        "StopWait TX",
-+        "Running",
-+};
-+
-+/**
-+ * Events of the interface statemachine.
-+ */
-+enum dev_events
-+{
-+        DEV_EVENT_START,
-+        DEV_EVENT_STOP,
-+        DEV_EVENT_RXUP,
-+        DEV_EVENT_TXUP,
-+        DEV_EVENT_RXDOWN,
-+        DEV_EVENT_TXDOWN,
-+        DEV_EVENT_RESTART,
-+        /**
-+         * MUST be always the last element!!
-+         */
-+        NR_DEV_EVENTS
-+};
-+
-+static const char *dev_event_names[] = {
-+        "Start",
-+        "Stop",
-+        "RX up",
-+        "TX up",
-+        "RX down",
-+        "TX down",
-+        "Restart",
-+};
-+
-+/**
-+ * Events of the channel statemachine
-+ */
-+enum ch_events
-+{
-+        /**
-+         * Events, representing return code of
-+         * I/O operations (do_IO, halt_IO et al.)
-+         */
-+        CH_EVENT_IO_SUCCESS,
-+        CH_EVENT_IO_EBUSY,
-+        CH_EVENT_IO_ENODEV,
-+        CH_EVENT_IO_EIO,
-+        CH_EVENT_IO_UNKNOWN,
-+
-+        CH_EVENT_ATTNBUSY,
-+        CH_EVENT_ATTN,
-+        CH_EVENT_BUSY,
-+
-+        /**
-+         * Events, representing unit-check
-+         */
-+        CH_EVENT_UC_RCRESET,
-+        CH_EVENT_UC_RSRESET,
-+        CH_EVENT_UC_TXTIMEOUT,
-+        CH_EVENT_UC_TXPARITY,
-+        CH_EVENT_UC_HWFAIL,
-+        CH_EVENT_UC_RXPARITY,
-+        CH_EVENT_UC_ZERO,
-+        CH_EVENT_UC_UNKNOWN,
-+
-+        /**
-+         * Events, representing subchannel-check
-+         */
-+        CH_EVENT_SC_UNKNOWN,
-+
-+        /**
-+         * Events, representing machine checks
-+         */
-+        CH_EVENT_MC_FAIL,
-+        CH_EVENT_MC_GOOD,
-+
-+        /**
-+         * Event, representing normal IRQ
-+         */
-+        CH_EVENT_IRQ,
-+        CH_EVENT_FINSTAT,
-+
-+        /**
-+         * Event, representing timer expiry.
-+         */
-+        CH_EVENT_TIMER,
-+
-+        /**
-+         * Events, representing commands from upper levels.
-+         */
-+        CH_EVENT_START,
-+        CH_EVENT_STOP,
-+        CH_EVENT_SEND_XID,
-+
-+        /** 
-+         * Events, representing TX MPC buffer states
-+         */
-+        CH_EVENT_TX_LOMEM,
-+        CH_EVENT_TX_MEM_OK,
-+        CH_EVENT_RSWEEP1_TIMER,
-+
-+        /**
-+         * MUST be always the last element!!
-+         */
-+        NR_CH_EVENTS,
-+};
-+
-+static const char *ch_event_names[] = {
-+        "do_IO success",
-+        "do_IO busy",
-+        "do_IO enodev",
-+        "do_IO ioerr",
-+        "do_IO unknown",
-+
-+        "Status ATTN & BUSY",
-+        "Status ATTN",
-+        "Status BUSY",
-+
-+        "Unit check remote reset",
-+        "Unit check remote system reset",
-+        "Unit check TX timeout",
-+        "Unit check TX parity",
-+        "Unit check Hardware failure",
-+        "Unit check RX parity",
-+        "Unit check ZERO",
-+        "Unit check Unknown",
-+
-+        "SubChannel check Unknown",
-+
-+        "Machine check failure",
-+        "Machine check operational",
-+
-+        "IRQ normal",
-+        "IRQ final",
-+
-+        "Timer",
-+
-+        "Start",
-+        "Stop",
-+        "XID Exchange",
-+
-+        "TX buffer shortage",
-+        "TX buffer shortage relieved",
-+        "MPC Group Sweep Timer",
-+};
-+
-+/**
-+ * States of the channel statemachine.
-+ */
-+enum ch_states
-+{
-+        /**
-+         * Channel not assigned to any device,
-+         * initial state, direction invalid
-+         */
-+        CH_STATE_IDLE,
-+
-+        /**
-+         * Channel assigned but not operating
-+         */
-+        CH_STATE_STOPPED,
-+        CH_STATE_STARTWAIT,
-+        CH_STATE_STARTRETRY,
-+        CH_STATE_SETUPWAIT,
-+        CH_STATE_RXINIT,
-+        CH_STATE_TXINIT,
-+        CH_STATE_RX,
-+        CH_STATE_TX,
-+        CH_STATE_RXIDLE,
-+        CH_STATE_TXIDLE,
-+        CH_STATE_RXERR,
-+        CH_STATE_TXERR,
-+        CH_STATE_TERM,
-+        CH_STATE_DTERM,
-+        CH_STATE_NOTOP,
-+        CH_STATE_TXLOMEM,
-+        CH_XID0_PENDING,
-+        CH_XID0_INPROGRESS,
-+        CH_XID7_PENDING,
-+        CH_XID7_PENDING1,
-+        CH_XID7_PENDING2,
-+        CH_XID7_PENDING3,
-+        CH_XID7_PENDING4,
-+
-+        /**
-+         * MUST be always the last element!!
-+         */
-+        NR_CH_STATES,
-+};
-+
-+static const char *ch_state_names[] = {
-+        "Idle",
-+        "Stopped",
-+        "StartWait",
-+        "StartRetry",
-+        "SetupWait",
-+        "RX init",
-+        "TX init",
-+        "RX",
-+        "TX",
-+        "RX idle",
-+        "TX idle",
-+        "RX error",
-+        "TX error",
-+        "Terminating",
-+        "Restarting",
-+        "Not operational",
-+        "TX Buffers low",
-+        "Pending XID0 Start",
-+        "In XID0 Negotiations ",
-+        "Pending XID7 P1 Start",
-+        "Active XID7 P1 Exchange ",
-+        "Pending XID7 P2 Start ",
-+        "Active XID7 P2 Exchange ",
-+        "XID7 Complete - Pending READY ",
-+};
-+
-+static int transmit_skb(channel *, struct sk_buff *);
-+
-+#if defined(DEBUGDATA) || defined(DEBUGXID)\
-+  || defined(DEBUGCCW) || defined(DEBUGSEQ)
-+/*-------------------------------------------------------------------*
-+* Dump buffer format                                                 *
-+*                                                                    *
-+*--------------------------------------------------------------------*/
-+static void 
-+dumpit(char* buf, int len)
-+{
-+
-+        __u32      ct, sw, rm, dup;
-+        char       *ptr, *rptr;
-+        char       tbuf[82], tdup[82];
-+#if (UTS_MACHINE == s390x)
-+        char       addr[22];
-+#else
-+        char       addr[12];
-+#endif
-+        char       boff[12];
-+        char       bhex[82], duphex[82];
-+        char       basc[40];
-+
-+        sw  = 0;
-+        rptr =ptr=buf;
-+        rm  = 16;
-+        duphex[0]  = 0x00;
-+        dup = 0;
-+
-+        for(ct=0; ct < len; ct++, ptr++, rptr++)
-+        {
-+                if(sw == 0)
-+                {
-+#if (UTS_MACHINE == s390x)
-+                        sprintf(addr, "%16.16lx",(unsigned long)rptr);
-+#else
-+                        sprintf(addr, "%8.8X",(__u32)rptr);
-+#endif
-+                        sprintf(boff, "%4.4X", (__u32)ct);
-+                        bhex[0] = '\0';
-+                        basc[0] = '\0';
-+                }
-+                if((sw == 4) || (sw == 12))
-+                {
-+                        strcat(bhex, " ");
-+                }
-+                if(sw == 8)
-+                {
-+                        strcat(bhex, "  ");
-+                }
-+#if (UTS_MACHINE == s390x)
-+                sprintf(tbuf,"%2.2lX", (unsigned long)*ptr);
-+#else
-+                sprintf(tbuf,"%2.2X", (__u32)*ptr);
-+#endif
-+                tbuf[2] = '\0'; 
-+                strcat(bhex, tbuf);
-+                if((0!=isprint(*ptr)) && (*ptr >= 0x20))
-+                {
-+                        basc[sw] = *ptr; 
-+                } else
-+                {
-+                        basc[sw] = '.';
-+                }
-+                basc[sw+1] = '\0';
-+                sw++;
-+                rm--;
-+                if(sw==16)
-+                {
-+                        if((strcmp(duphex, bhex)) !=0)
-+                        {
-+                                if(dup !=0)
-+                                {
-+                                        sprintf(tdup,"Duplicate as above "
-+                                                "to %s", addr);
-+                                        printk( KERN_INFO "               "
-+                                                "     --- %s ---\n",tdup);
-+                                }
-+                                printk( KERN_INFO "   %s (+%s) : %s  [%s]\n",
-+                                        addr, boff, bhex, basc);
-+                                dup = 0;
-+                                strcpy(duphex, bhex);
-+                        } else
-+                        {
-+                                dup++;
-+                        }
-+                        sw = 0;
-+                        rm = 16;
-+                }
-+        }  /* endfor */
-+
-+        if(sw != 0)
-+        {
-+                for(; rm > 0; rm--, sw++)
-+                {
-+                        if((sw==4) || (sw==12)) strcat(bhex, " ");
-+                        if(sw==8)               strcat(bhex, "  ");
-+                        strcat(bhex, "  ");
-+                        strcat(basc, " ");
-+                }
-+                if(dup !=0)
-+                {
-+                        sprintf(tdup,"Duplicate as above to %s", addr);
-+                        printk( KERN_INFO "               "
-+                                "     --- %s ---\n",tdup);
-+                }
-+                printk( KERN_INFO "   %s (+%s) : %s  [%s]\n",
-+                        addr, boff, bhex, basc);
-+        } else
-+        {
-+                if(dup >=1)
-+                {
-+                        sprintf(tdup,"Duplicate as above to %s", addr);
-+                        printk( KERN_INFO "               "
-+                                "     --- %s ---\n",tdup);
-+                }
-+                if(dup !=0)
-+                {
-+                        printk( KERN_INFO "   %s (+%s) : %s  [%s]\n",
-+                                addr, boff, bhex, basc);
-+                }
-+        }
-+
-+        return;
-+
-+}   /*   end of dumpit  */
-+
-+#endif
-+
-+#ifdef DEBUGDATA
-+/**
-+ * Dump header and first 16 bytes of an sk_buff for debugging purposes.
-+ *
-+ * @param skb    The sk_buff to dump.
-+ * @param offset Offset relative to skb-data, where to start the dump.
-+ */
-+static void 
-+ctcmpc_dump_skb(struct sk_buff *skb, int offset)
-+{
-+        unsigned char *p = skb->data;
-+        th_header *header;
-+        pdu *pheader;
-+        int bl = skb->len;
-+        int i;
-+
-+        if(p == NULL) return;
-+        p += offset;
-+        header = (th_header *)p;
-+
-+        printk(KERN_INFO "dump:\n");
-+        printk(KERN_INFO "skb len=%d \n", skb->len);
-+        if(skb->len > 2)
-+        {
-+                switch(header->th_ch_flag)
-+                {
-+                        case TH_HAS_PDU:
-+                                break;
-+                        case 0x00:
-+                        case TH_IS_XID:
-+                                if((header->th_blk_flag == TH_DATA_IS_XID) &&
-+                                   (header->th_is_xid == 0x01))
-+                                        goto dumpth;
-+                        case TH_SWEEP_REQ:
-+                                goto dumpth;
-+                        case TH_SWEEP_RESP:
-+                                goto dumpth;
-+                        default:
-+                                break;
-+
-+                }
-+
-+                pheader = (pdu *)p;
-+                printk(KERN_INFO "pdu->offset: %d hex: %04x\n",
-+                       pheader->pdu_offset,pheader->pdu_offset);
-+                printk(KERN_INFO "pdu->flag  : %02x\n",pheader->pdu_flag);
-+                printk(KERN_INFO "pdu->proto : %02x\n",pheader->pdu_proto);
-+                printk(KERN_INFO "pdu->seq   : %02x\n",pheader->pdu_seq);
-+                goto dumpdata;
-+
-+                dumpth:
-+                printk(KERN_INFO "th->seg     : %02x\n", header->th_seg);
-+                printk(KERN_INFO "th->ch      : %02x\n", header->th_ch_flag);
-+                printk(KERN_INFO "th->blk_flag: %02x\n", header->th_blk_flag);
-+                printk(KERN_INFO "th->type    : %s\n",
-+                       (header->th_is_xid) ? "DATA" : "XID"); 
-+                printk(KERN_INFO "th->seqnum  : %04x\n", header->th_seq_num);
-+
-+        }  /* only dump the data if the length is not greater than 2 */
-+        dumpdata:
-+
-+        if(bl > 32)
-+                bl = 32;
-+        printk(KERN_INFO "data: ");
-+        for(i = 0; i < bl; i++)
-+                printk("%02x%s", *p++, (i % 16) ? " " : "\n<7>");
-+        printk("\n");
-+}
-+
-+#endif
-+
-+/**
-+ * Dummy NOP action for statemachines
-+ */
-+static void 
-+fsm_action_nop(fsm_instance *fi, int event, void *arg)
-+{
-+}
-+
-+static int ctcmpc_open(net_device *);
-+static void ctcmpc_ch_action_rxidle(fsm_instance *fi, int event, void *arg);
-+static void ctcmpc_ch_action_txidle(fsm_instance *fi, int event, void *arg);
-+static void inline ccw_check_return_code (channel *,int);
-+
-+
-+/*
-+      ctc_mpc_alloc_channel
-+      Device Initialization :  
-+            ACTPATH  driven IO operations
-+*/
-+int 
-+ctc_mpc_alloc_channel(int port_num,callbacktypei2  callback)
-+{
-+        char device[20];
-+        char *devnam = "mpc";
-+        net_device *dev = NULL;
-+        mpc_group *grpptr;
-+        ctc_priv *privptr;
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        sprintf(device, "%s%i",devnam,port_num);
-+        dev = __dev_get_by_name(device);
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "ctc_mpc_alloc_channel %s dev=NULL\n",device);
-+                return(1);
-+        }
-+
-+
-+        privptr = (ctc_priv *)dev->priv;
-+        grpptr = privptr->mpcg;
-+        if(!grpptr)
-+                return(1);
-+
-+        grpptr->allochanfunc = callback;
-+        grpptr->port_num = port_num;
-+        grpptr->port_persist = 1;
-+
-+        printk(KERN_INFO "%s: %s called for device %s refcount=%d state=%s\n", 
-+               dev->name,
-+               __FUNCTION__,
-+               dev->name,
-+               atomic_read(&dev->refcnt),
-+               fsm_getstate_str(grpptr->fsm));
-+
-+        switch(fsm_getstate(grpptr->fsm))
-+        {
-+                case MPCG_STATE_INOP:
-+                        /* Group is in the process of terminating */
-+                        grpptr->alloc_called = 1;
-+                        break;
-+                case MPCG_STATE_RESET:
-+                        /* MPC Group will transition to state             */
-+                        /* MPCG_STATE_XID2INITW iff the minimum number    */
-+                        /* of 1 read and 1 write channel have successfully*/
-+                        /* activated                                      */
-+                        /*fsm_newstate(grpptr->fsm, MPCG_STATE_XID2INITW);*/
-+                        if(callback)
-+                                grpptr->send_qllc_disc = 1;
-+                case MPCG_STATE_XID0IOWAIT:
-+                        fsm_deltimer(&grpptr->timer);
-+                        grpptr->outstanding_xid2 = 0;
-+                        grpptr->outstanding_xid7 = 0;
-+                        grpptr->outstanding_xid7_p2 = 0;
-+                        grpptr->saved_xid2 = NULL;
-+                        if(callback)
-+                                ctcmpc_open(dev);
-+                        fsm_event(((ctc_priv *)dev->priv)->fsm, 
-+                                  DEV_EVENT_START, dev);
-+                        break;;
-+                case MPCG_STATE_READY:
-+                        /* XID exchanges completed after PORT was activated */
-+                        /* Link station already active                      */
-+                        /* Maybe timing issue...retry callback              */
-+                        grpptr->allocchan_callback_retries++;
-+                        if(grpptr->allocchan_callback_retries < 4)
-+                        {
-+                                if(grpptr->allochanfunc)
-+                                        grpptr->allochanfunc(grpptr->port_num, 
-+                                                      grpptr->group_max_buflen);
-+                        } else
-+                        {
-+                                /* there are problems...bail out            */
-+                                /* there may be a state mismatch so restart */
-+                                grpptr->port_persist = 1;
-+                                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+                                grpptr->allocchan_callback_retries = 0;
-+                        }
-+                        break;
-+                default:
-+                        return(0);
-+
-+        }
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+        return(0);
-+}
-+
-+
-+void 
-+ctc_mpc_establish_connectivity(int port_num, callbacktypei3 callback)
-+{
-+
-+        char device[20];
-+        char *devnam = "mpc";
-+        net_device *dev = NULL;
-+        mpc_group *grpptr;
-+        ctc_priv *privptr;
-+        channel *rch,*wch;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+
-+#endif
-+
-+        sprintf(device,"%s%i",devnam,port_num);
-+        dev = __dev_get_by_name(device);
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "ctc_mpc_establish_connectivity %s dev=NULL\n"
-+                       ,device);
-+                return;
-+        }
-+        privptr = (ctc_priv *)dev->priv;
-+        rch = privptr->channel[READ];
-+        wch = privptr->channel[WRITE];
-+
-+        grpptr = privptr->mpcg;
-+
-+        printk(KERN_INFO "%s: %s called for device %s refcount=%d state=%s\n", 
-+               dev->name,
-+               __FUNCTION__,
-+               dev->name,
-+               atomic_read(&dev->refcnt),
-+               fsm_getstate_str(grpptr->fsm));
-+
-+        grpptr->estconnfunc = callback;
-+        grpptr->port_num = port_num;
-+
-+        switch(fsm_getstate(grpptr->fsm))
-+        {
-+                case MPCG_STATE_READY:
-+                        /* XID exchanges completed after PORT was activated */
-+                        /* Link station already active                      */
-+                        /* Maybe timing issue...retry callback              */
-+                        fsm_deltimer(&grpptr->timer);
-+                        grpptr->estconn_callback_retries++;
-+                        if(grpptr->estconn_callback_retries < 4)
-+                        {
-+                                if(grpptr->estconnfunc)
-+                                {
-+                                        grpptr->estconnfunc(grpptr->port_num,0,
-+                                                      grpptr->group_max_buflen);
-+                                        grpptr->estconnfunc = NULL;
-+                                }
-+                        } else
-+                        {
-+                                /* there are problems...bail out         */
-+                                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+                                grpptr->estconn_callback_retries = 0;
-+                        }
-+                        break;
-+                case MPCG_STATE_INOP:
-+                case MPCG_STATE_RESET:
-+                        /* MPC Group is not ready to start XID - min number of*/
-+                        /* 1 read and 1 write channel have not been acquired  */
-+                        printk(KERN_WARNING "ctcmpc: %s() REJECTED ACTIVE XID"
-+                               " Request - Channel Pair is not Active\n",
-+                               __FUNCTION__);
-+                        if(grpptr->estconnfunc)
-+                        {
-+                                grpptr->estconnfunc(grpptr->port_num,-1,0);
-+                                grpptr->estconnfunc = NULL;
-+                        }
-+                        break;
-+                case MPCG_STATE_XID2INITW:
-+                        /* alloc channel was called but no XID exchange    */
-+                        /* has occurred. initiate xside XID exchange       */
-+                        /* make sure yside XID0 processing has not started */
-+                        if((fsm_getstate(rch->fsm) > CH_XID0_PENDING) ||
-+                           (fsm_getstate(wch->fsm) > CH_XID0_PENDING))
-+                        {
-+                                printk(KERN_WARNING "ctcmpc: %s() ABORT ACTIVE"
-+                                       " XID Request - PASSIVE XID already in "
-+                                       "process\n",
-+                                       __FUNCTION__);
-+                                break;
-+                        }
-+                        grpptr->send_qllc_disc = 1;
-+                        fsm_newstate(grpptr->fsm, MPCG_STATE_XID0IOWAIT);
-+                        fsm_deltimer(&grpptr->timer);
-+                        fsm_addtimer(&grpptr->timer, 
-+                                     MPC_XID_TIMEOUT_VALUE, 
-+                                     MPCG_EVENT_TIMER, 
-+                                     dev);
-+                        grpptr->outstanding_xid7 = 0;
-+                        grpptr->outstanding_xid7_p2 = 0;
-+                        grpptr->saved_xid2 = NULL;
-+                        if((rch->in_mpcgroup) && 
-+                           (fsm_getstate(rch->fsm) == CH_XID0_PENDING))
-+                                fsm_event(grpptr->fsm, MPCG_EVENT_XID0DO, rch);
-+                        else
-+                        {
-+                                printk(KERN_WARNING "ctcmpc: %s() Unable to"
-+                                       " start ACTIVE XID0 on read channel\n",
-+                                       __FUNCTION__);
-+                                if(grpptr->estconnfunc)
-+                                {
-+                                        grpptr->estconnfunc(grpptr->port_num,
-+                                                            -1,
-+                                                            0);
-+                                        grpptr->estconnfunc = NULL;
-+                                }
-+                                fsm_deltimer(&grpptr->timer);
-+                                goto done;
-+                        }
-+                        if((wch->in_mpcgroup) && 
-+                           (fsm_getstate(wch->fsm) == CH_XID0_PENDING))
-+                                fsm_event(grpptr->fsm, MPCG_EVENT_XID0DO, wch);
-+                        else
-+                        {
-+                                printk(KERN_WARNING "ctcmpc: %s() Unable to "
-+                                       "start ACTIVE XID0 on write channel\n",
-+                                       __FUNCTION__);
-+                                if(grpptr->estconnfunc)
-+                                {
-+                                        grpptr->estconnfunc(grpptr->port_num,
-+                                                            -1,0);
-+                                        grpptr->estconnfunc = NULL;
-+                                }
-+                                fsm_deltimer(&grpptr->timer);
-+                                goto done;
-+
-+                        }
-+                        break;
-+                case MPCG_STATE_XID0IOWAIT:
-+                        /* already in active XID negotiations */
-+                default:
-+                        break;
-+        }
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return;
-+}
-+
-+static int ctcmpc_close(net_device *);
-+
-+void 
-+ctc_mpc_dealloc_ch(int port_num)
-+{
-+        net_device *dev;
-+        char device[20];
-+        char *devnam = "mpc";
-+        ctc_priv *privptr;
-+        mpc_group *grpptr;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        sprintf(device,"%s%i",devnam,port_num);
-+        dev = __dev_get_by_name(device);
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "%s() %s dev=NULL\n",__FUNCTION__,device);
-+                goto done;
-+        }
-+
-+        printk(KERN_INFO "%s: %s called for device %s refcount=%d\n", 
-+               dev->name,__FUNCTION__,dev->name,atomic_read(&dev->refcnt));
-+
-+        privptr  = (ctc_priv *)dev->priv;
-+        if(privptr == NULL)
-+        {
-+                printk(KERN_INFO "%s() %s privptr=NULL\n",__FUNCTION__,device);
-+                goto done;
-+        }
-+        fsm_deltimer(&privptr->restart_timer);
-+
-+        grpptr = privptr->mpcg;
-+        if(grpptr == NULL)
-+        {
-+                printk(KERN_INFO "%s() %s dev=NULL\n",__FUNCTION__,device);
-+                goto done;
-+        }
-+        grpptr->channels_terminating = 0;
-+
-+        fsm_deltimer(&grpptr->timer);
-+
-+        grpptr->allochanfunc = NULL;
-+        grpptr->estconnfunc = NULL; 
-+
-+        grpptr->port_persist = 0;
-+
-+        grpptr->send_qllc_disc = 0;
-+
-+        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+
-+        ctcmpc_close(dev);
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+        return;
-+}
-+
-+void 
-+ctc_mpc_flow_control(int port_num,int flowc)
-+{
-+        char device[20];
-+        char *devnam = "mpc";
-+        ctc_priv *privptr;
-+        mpc_group *grpptr;
-+        net_device *dev;
-+        channel *rch = NULL;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %i\n", __FUNCTION__,flowc);
-+#endif
-+
-+        sprintf(device,"%s%i",devnam,port_num);
-+        dev = __dev_get_by_name(device);
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "ctc_mpc_flow_control %s dev=NULL\n",device);
-+                return;
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s: %s called \n", 
-+               dev->name,__FUNCTION__);
-+#endif
-+
-+        privptr  = (ctc_priv *)dev->priv;
-+        if(privptr == NULL)
-+        {
-+                printk(KERN_INFO "ctc_mpc_flow_control %s privptr=NULL\n",
-+                       device);
-+                return;
-+        }
-+        grpptr = privptr->mpcg;
-+        rch = privptr->channel[READ];
-+
-+        switch(flowc)
-+        {
-+                case 1: 
-+                        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_FLOWC)
-+                                break;
-+                        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_READY)
-+                        {
-+                                if(grpptr->flow_off_called == 1)
-+                                        grpptr->flow_off_called = 0;
-+                                else
-+                                        fsm_newstate(grpptr->fsm, 
-+                                                     MPCG_STATE_FLOWC); 
-+                                break;
-+                        }
-+                        break;
-+                case 0:
-+                        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_FLOWC)
-+                        {
-+                                fsm_newstate(grpptr->fsm, MPCG_STATE_READY); 
-+                                /* ensure any data that has accumulated */
-+                                /* on the io_queue will now be sent     */
-+                                tasklet_schedule(&rch->ch_tasklet);
-+                        }
-+                        /* possible race condition                      */
-+                        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_READY)
-+                        {
-+                                grpptr->flow_off_called = 1;
-+                                break;
-+                        }
-+                        break;
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %i\n", __FUNCTION__,flowc);
-+#endif
-+
-+
-+}
-+
-+static int ctcmpc_send_qllc_discontact(net_device *);
-+/*********************************************************************/
-+/*
-+        invoked when the device transitions to dev_stopped
-+        MPC will stop each individual channel if a single XID failure
-+        occurs, or will intitiate all channels be stopped if a GROUP
-+        level failure occurs.
-+*/
-+/*********************************************************************/
-+
-+static void 
-+ctcmpc_action_go_inop(fsm_instance *fi, int event, void *arg)
-+{
-+        net_device  *dev = (net_device *)arg;
-+        ctc_priv    *privptr;
-+        mpc_group   *grpptr;
-+        int rc = 0;
-+        channel *wch,*rch;
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "%s() dev=NULL\n",__FUNCTION__);
-+                return;
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s enter:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+
-+        privptr  = (ctc_priv *)dev->priv;
-+        grpptr =  privptr->mpcg;
-+        grpptr->flow_off_called = 0;
-+
-+        fsm_deltimer(&grpptr->timer);
-+
-+        if(grpptr->channels_terminating)
-+                goto done;
-+
-+        grpptr->channels_terminating = 1;
-+
-+        grpptr->saved_state = fsm_getstate(grpptr->fsm);
-+        fsm_newstate(grpptr->fsm,MPCG_STATE_INOP);
-+        if(grpptr->saved_state > MPCG_STATE_XID7INITF)
-+                printk(KERN_NOTICE "%s:MPC GROUP INOPERATIVE\n", dev->name);
-+        if((grpptr->saved_state != MPCG_STATE_RESET) ||
-+           /* dealloc_channel has been called */
-+           ((grpptr->saved_state == MPCG_STATE_RESET) &&
-+            (grpptr->port_persist == 0)))
-+                fsm_deltimer(&privptr->restart_timer);
-+
-+        wch = privptr->channel[WRITE];   
-+        rch = privptr->channel[READ];    
-+
-+        switch(grpptr->saved_state)
-+        {
-+                case MPCG_STATE_RESET:
-+                case MPCG_STATE_INOP:
-+                case MPCG_STATE_XID2INITW:
-+                case MPCG_STATE_XID0IOWAIT:
-+                case MPCG_STATE_XID2INITX:
-+                case MPCG_STATE_XID7INITW:
-+                case MPCG_STATE_XID7INITX:
-+                case MPCG_STATE_XID0IOWAIX:
-+                case MPCG_STATE_XID7INITI:
-+                case MPCG_STATE_XID7INITZ:
-+                case MPCG_STATE_XID7INITF:
-+                        break;
-+                case MPCG_STATE_FLOWC:
-+                case MPCG_STATE_READY:
-+                default:
-+                        tasklet_hi_schedule(&wch->ch_disc_tasklet); 
-+        }
-+
-+        grpptr->xid2_tgnum = 0;
-+        grpptr->group_max_buflen = 0;  /*min of all received */
-+        grpptr->outstanding_xid2 = 0;
-+        grpptr->outstanding_xid7 = 0;
-+        grpptr->outstanding_xid7_p2 = 0;
-+        grpptr->saved_xid2 = NULL;
-+        grpptr->xidnogood = 0;
-+        grpptr->changed_side = 0;
-+
-+        grpptr->rcvd_xid_skb->data = 
-+                grpptr->rcvd_xid_skb->tail = grpptr->rcvd_xid_data;
-+        grpptr->rcvd_xid_skb->len = 0;
-+        grpptr->rcvd_xid_th = (th_header *)grpptr->rcvd_xid_skb->data;
-+        memcpy(skb_put(grpptr->rcvd_xid_skb,TH_HEADER_LENGTH),
-+               &thnorm,
-+               TH_HEADER_LENGTH);
-+
-+        if(grpptr->send_qllc_disc == 1)
-+        {
-+                grpptr->send_qllc_disc = 0;
-+                rc = ctcmpc_send_qllc_discontact(dev);
-+        }
-+
-+        /* DO NOT issue DEV_EVENT_STOP directly out of this code */
-+        /* This can result in INOP of VTAM PU due to halting of  */
-+        /* outstanding IO which causes a sense to be returned    */
-+        /* Only about 3 senses are allowed and then IOS/VTAM will*/
-+        /* ebcome unreachable without manual intervention        */
-+        if((grpptr->port_persist == 1)  || (grpptr->alloc_called))
-+        {
-+                grpptr->alloc_called = 0;
-+                fsm_deltimer(&privptr->restart_timer);
-+                fsm_addtimer(&privptr->restart_timer, 
-+                             500, 
-+                             DEV_EVENT_RESTART, 
-+                             dev);
-+                fsm_newstate(grpptr->fsm, MPCG_STATE_RESET);
-+                if(grpptr->saved_state > MPCG_STATE_XID7INITF)
-+                        printk(KERN_NOTICE "%s:MPC GROUP RECOVERY SCHEDULED\n", 
-+                               dev->name);
-+        } else
-+        {
-+                fsm_deltimer(&privptr->restart_timer);
-+                fsm_addtimer(&privptr->restart_timer, 500, DEV_EVENT_STOP, dev);
-+                fsm_newstate(grpptr->fsm, MPCG_STATE_RESET);
-+                printk(KERN_NOTICE "%s:MPC GROUP RECOVERY NOT ATTEMPTED\n", 
-+                       dev->name);
-+        }
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+
-+        return;
-+}
-+
-+
-+
-+static void 
-+ctcmpc_action_timeout(fsm_instance *fi, int event, void *arg)
-+{
-+        net_device *dev = (net_device *)arg;
-+        ctc_priv *privptr;
-+        mpc_group *grpptr;
-+        channel *wch;
-+        channel *rch;
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "%s() dev=NULL\n",__FUNCTION__);
-+                return;
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s enter:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+
-+        privptr = (ctc_priv *)dev->priv;
-+        grpptr = privptr->mpcg;
-+        wch = privptr->channel[WRITE];   
-+        rch = privptr->channel[READ];    
-+
-+
-+        switch(fsm_getstate(grpptr->fsm))
-+        {
-+                case MPCG_STATE_XID2INITW:
-+                        /* Unless there is outstanding IO on the  */
-+                        /* channel just return and wait for ATTN  */
-+                        /* interrupt to begin XID negotiations    */
-+                        if((fsm_getstate(rch->fsm) == CH_XID0_PENDING) &&
-+                           (fsm_getstate(wch->fsm) == CH_XID0_PENDING))
-+                                break;
-+                default:
-+                        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+
-+
-+        return;
-+}
-+
-+
-+static void 
-+ctcmpc_action_discontact(fsm_instance *fi, int event, void *arg)
-+{
-+        mpcg_info   *mpcginfo   = (mpcg_info *)arg;
-+        channel     *ch         = mpcginfo->ch;
-+        net_device  *dev        = ch->netdev;
-+        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group   *grpptr     = privptr->mpcg;
-+
-+
-+        if(ch == NULL)
-+        {
-+                printk(KERN_INFO "%s() ch=NULL\n",__FUNCTION__);
-+                return;
-+        }
-+        if(ch->netdev == NULL)
-+        {
-+                printk(KERN_INFO "%s() dev=NULL, irq=%d\n",__FUNCTION__,
-+                       ch->irq);
-+                return;
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s enter:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+
-+        grpptr->send_qllc_disc = 1;
-+        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+
-+        return;
-+}
-+
-+static void 
-+ctcmpc_action_send_discontact(unsigned long thischan)
-+{
-+        channel *ch = (channel *)thischan;  
-+        int rc = 0;
-+        unsigned long saveflags;
-+#ifdef DEBUG
-+	net_device *dev = ch->netdev;
-+	ctc_priv *privptr = (ctc_priv *)dev->priv;
-+	mpc_group = *grpptr = privptr->mpcg;
-+#endif
-+
-+
-+#ifdef DEBUG
-+
-+        printk(KERN_INFO "%s cp:%i enter: %s() irq=%d GrpState:%s ChState:%s\n", 
-+               dev->name,
-+               smp_processor_id(),
-+               __FUNCTION__,
-+               ch->irq,
-+               fsm_getstate_str(grpptr->fsm),
-+               fsm_getstate_str(ch->fsm));
-+#endif
-+
-+        s390irq_spin_lock_irqsave(ch->irq,saveflags);
-+        rc = do_IO(ch->irq, &ch->ccw[15], (intparm_t)ch, 0xff, 0);
-+        s390irq_spin_unlock_irqrestore(ch->irq,saveflags);
-+
-+        if(rc != 0)
-+        {
-+#ifdef DEBUG
-+                printk(KERN_INFO "%s() %04x do_IO failed \n", 
-+                       __FUNCTION__,ch->devno);
-+                ccw_check_return_code(ch, rc);
-+#endif
-+                /* Not checking return code value here */
-+                /* Making best effort to notify partner*/
-+                /* that MPC Group is going down        */
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+
-+        return;
-+}
-+
-+
-+
-+static int 
-+ctcmpc_validate_xid(mpcg_info *mpcginfo)
-+{
-+        channel     *ch         = mpcginfo->ch;
-+        net_device  *dev        = ch->netdev;
-+        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group   *grpptr     = privptr->mpcg;
-+        xid2        *xid        = mpcginfo->xid;
-+        int         failed      = 0;
-+        int         rc          = 0;
-+        __u64       our_id,their_id = 0;
-+        int         len;
-+
-+        len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+
-+        if(mpcginfo->xid == NULL)
-+        {
-+                printk(KERN_INFO "%s() xid=NULL, irq=%d\n",__FUNCTION__,
-+                       ch->irq);
-+                rc = 1;
-+                goto done;
-+        }
-+
-+#ifdef DEBUGXID
-+        printk(KERN_INFO "ctcmpc :  %s  xid received()\n", __FUNCTION__);
-+        dumpit((char *)mpcginfo->xid,XID2_LENGTH);
-+#endif
-+        /*the received direction should be the opposite of ours            */
-+        if(((CHANNEL_DIRECTION(ch->flags) == READ) ? 
-+            XID2_WRITE_SIDE : XID2_READ_SIDE )
-+           != xid->xid2_dlc_type)
-+        {
-+                failed = 1;
-+                printk(KERN_INFO "%s XID REJECTED - READ-WRITE CH "
-+                       "Pairing Invalid \n",
-+                       __FUNCTION__);
-+        }
-+
-+        if(xid->xid2_dlc_type == XID2_READ_SIDE)
-+        {
-+#ifdef DEBUGDATA
-+                printk(KERN_INFO "%s(): grpmaxbuf:%d xid2buflen:%d\n", 
-+                       __FUNCTION__,
-+                       grpptr->group_max_buflen,
-+                       xid->xid2_buf_len);
-+#endif
-+
-+                if(grpptr->group_max_buflen == 0)
-+                        grpptr->group_max_buflen = xid->xid2_buf_len - len;
-+                else
-+                {
-+                        if((xid->xid2_buf_len - len) < grpptr->group_max_buflen)
-+                        {
-+                                grpptr->group_max_buflen = 
-+                                        xid->xid2_buf_len - len;
-+                        }
-+                }
-+
-+        }
-+
-+        if(grpptr->saved_xid2 == NULL)
-+        {
-+                grpptr->saved_xid2 = (xid2 *)grpptr->rcvd_xid_skb->tail;
-+                memcpy(skb_put(grpptr->rcvd_xid_skb,XID2_LENGTH), 
-+                       xid, 
-+                       XID2_LENGTH);
-+                grpptr->rcvd_xid_skb->data = 
-+                        grpptr->rcvd_xid_skb->tail = 
-+                        grpptr->rcvd_xid_data;
-+                grpptr->rcvd_xid_skb->len = 0;
-+
-+                /* convert two 32 bit numbers into 1 64 bit for id compare */
-+                our_id = (__u64)privptr->xid->xid2_adj_id;
-+                our_id = our_id << 32;
-+                our_id = our_id + privptr->xid->xid2_sender_id;
-+                their_id = (__u64)xid->xid2_adj_id;
-+                their_id = their_id << 32;
-+                their_id = their_id + xid->xid2_sender_id;
-+                /* lower id assume the xside role */
-+                if(our_id < their_id)
-+                {
-+                        grpptr->roll = XSIDE;
-+#ifdef DEBUGXID
-+                        printk(KERN_INFO "ctcmpc :%s() WE HAVE LOW "
-+                               "ID-TAKE XSIDE\n", __FUNCTION__);
-+#endif
-+                } else
-+                {
-+                        grpptr->roll = YSIDE;
-+#ifdef DEBUGXID
-+                        printk(KERN_INFO "ctcmpc :%s() WE HAVE HIGH "
-+                               "ID-TAKE YSIDE\n", __FUNCTION__);
-+#endif
-+                } 
-+
-+        } else
-+        {
-+                if(xid->xid2_flag4 != grpptr->saved_xid2->xid2_flag4)
-+                {
-+                        failed = 1;
-+                        printk(KERN_INFO "%s XID REJECTED - XID Flag Byte4\n",
-+                               __FUNCTION__);
-+
-+                }
-+                if(xid->xid2_flag2 == 0x40)
-+                {
-+                        failed = 1;
-+                        printk(KERN_INFO "%s XID REJECTED - XID NOGOOD\n",
-+                               __FUNCTION__);
-+
-+                }
-+                if(xid->xid2_adj_id != grpptr->saved_xid2->xid2_adj_id)
-+                {
-+                        failed = 1;
-+                        printk(KERN_INFO "%s XID REJECTED - "
-+                               "Adjacent Station ID Mismatch\n",
-+                               __FUNCTION__);
-+
-+                }
-+                if(xid->xid2_sender_id != grpptr->saved_xid2->xid2_sender_id)
-+                {
-+                        failed = 1;
-+                        printk(KERN_INFO "%s XID REJECTED - "
-+                               "Sender Address Mismatch\n",
-+                               __FUNCTION__);
-+
-+                }
-+        }
-+
-+        if(failed)
-+        {
-+#ifdef DEBUG
-+                printk(KERN_INFO "ctcmpc     :  %s() failed\n", __FUNCTION__);
-+#endif
-+
-+                privptr->xid->xid2_flag2 = 0x40;
-+                grpptr->saved_xid2->xid2_flag2 = 0x40;
-+                rc = 1;
-+                goto done;
-+        }
-+
-+        done:
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return(rc);
-+}
-+
-+
-+static void 
-+ctcmpc_action_yside_xid(fsm_instance *fsm,int event, void *arg) 
-+{
-+        channel           *ch         = (channel *)arg;
-+        ctc_priv          *privptr;
-+        mpc_group         *grpptr     = NULL;
-+        int               rc          = 0;
-+        unsigned long     saveflags;
-+        int               gotlock     = 0;
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc cp:%i enter:  %s()  %04x\n", 
-+               smp_processor_id(),__FUNCTION__,ch->devno);
-+#endif
-+
-+        if(ch == NULL)
-+        {
-+                printk(KERN_INFO "%s ch=NULL\n",__FUNCTION__);
-+                goto done;
-+        }
-+
-+        net_device *dev = ch->netdev;
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "%s dev=NULL, irq=%d\n",
-+                       __FUNCTION__,
-+                       ch->irq);
-+                goto done;
-+        }
-+
-+        privptr = (ctc_priv *)dev->priv;
-+        if(privptr == NULL)
-+        {
-+                printk(KERN_INFO "%s privptr=NULL, irq=%d\n",
-+                       __FUNCTION__,
-+                       ch->irq);
-+                goto done;
-+        }
-+
-+        grpptr = privptr->mpcg;
-+        if(grpptr == NULL)
-+        {
-+                printk(KERN_INFO "%s grpptr=NULL, irq=%d\n",
-+                       __FUNCTION__,
-+                       ch->irq);
-+                goto done;
-+        }
-+
-+        if(ctcmpc_checkalloc_buffer(ch, 0))
-+        {
-+                rc = -ENOMEM;
-+                goto done;
-+        }
-+
-+
-+        ch->trans_skb->data =  ch->trans_skb->tail = ch->trans_skb_data;
-+        ch->trans_skb->len = 0;
-+        memset(ch->trans_skb->data, 0, 16);
-+        ch->rcvd_xid_th =  (th_header *)ch->trans_skb->data;
-+        skb_put(ch->trans_skb,TH_HEADER_LENGTH);
-+        ch->rcvd_xid = (xid2 *)ch->trans_skb->tail;
-+        skb_put(ch->trans_skb,XID2_LENGTH);
-+        ch->rcvd_xid_id = ch->trans_skb->tail;
-+        ch->trans_skb->data =  ch->trans_skb->tail = ch->trans_skb_data;
-+        ch->trans_skb->len = 0;
-+
-+        ch->ccw[8].flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[8].count        = 0;
-+        ch->ccw[8].cda          = 0x00;  
-+
-+        if(ch->rcvd_xid_th == NULL)
-+        {
-+                printk(KERN_INFO "%s ch->rcvd_xid_th=NULL, irq=%d\n",
-+                       __FUNCTION__,
-+                       ch->irq);
-+                goto done;
-+        }
-+        ch->ccw[9].cmd_code     = CCW_CMD_READ;
-+        ch->ccw[9].flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[9].count        = TH_HEADER_LENGTH;
-+        ch->ccw[9].cda          = virt_to_phys(ch->rcvd_xid_th);
-+
-+        if(ch->rcvd_xid == NULL)
-+        {
-+                printk(KERN_INFO "%s ch->rcvd_xid=NULL, irq=%d\n",
-+                       __FUNCTION__,
-+                       ch->irq);
-+                goto done;
-+        }
-+        ch->ccw[10].cmd_code    = CCW_CMD_READ;
-+        ch->ccw[10].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[10].count       = XID2_LENGTH;
-+        ch->ccw[10].cda         = virt_to_phys(ch->rcvd_xid);
-+
-+        if(ch->xid_th == NULL)
-+        {
-+                printk(KERN_INFO "%s ch->xid_th=NULL, irq=%d\n",
-+                       __FUNCTION__,
-+                       ch->irq);
-+                goto done;
-+        }
-+        ch->ccw[11].cmd_code    = CCW_CMD_WRITE;
-+        ch->ccw[11].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[11].count       = TH_HEADER_LENGTH;
-+        ch->ccw[11].cda         = virt_to_phys(ch->xid_th);
-+
-+        if(ch->xid == NULL)
-+        {
-+                printk(KERN_INFO "%s ch->xid=NULL, irq=%d\n",
-+                       __FUNCTION__,
-+                       ch->irq);
-+                goto done;
-+        }
-+
-+        ch->ccw[12].cmd_code    = CCW_CMD_WRITE;
-+        ch->ccw[12].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[12].count       = XID2_LENGTH;
-+        ch->ccw[12].cda         = virt_to_phys(ch->xid);
-+
-+        if(ch->xid_id == NULL)
-+        {
-+                printk(KERN_INFO "%s ch->xid_id=NULL, irq=%d\n",
-+                       __FUNCTION__,
-+                       ch->irq);
-+                goto done;
-+        }
-+        ch->ccw[13].cmd_code    = CCW_CMD_WRITE;
-+        ch->ccw[13].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[13].count       = 4;
-+        ch->ccw[13].cda         = virt_to_phys(ch->xid_id);
-+
-+        ch->ccw[14].cmd_code    = CCW_CMD_NOOP;
-+        ch->ccw[14].flags       = CCW_FLAG_SLI;
-+        ch->ccw[14].count       = 0;
-+        ch->ccw[14].cda         = 0;
-+
-+
-+#ifdef DEBUGCCW
-+        dumpit((char *)&ch->ccw[8],sizeof(ccw1_t) * 7);
-+#endif
-+#ifdef DEBUGXID
-+        dumpit((char *)ch->xid_th,TH_HEADER_LENGTH);
-+        dumpit((char *)ch->xid,XID2_LENGTH);
-+        dumpit((char *)ch->xid_id,4);
-+#endif
-+
-+
-+        if(!in_irq())
-+        {
-+                s390irq_spin_lock_irqsave(ch->irq,saveflags);
-+                gotlock = 1;
-+        }
-+
-+        fsm_addtimer(&ch->timer, 5000 , CH_EVENT_TIMER, ch);
-+        rc = do_IO(ch->irq, &ch->ccw[8], (intparm_t)ch, 0xff, 0);
-+
-+        if(gotlock)
-+                s390irq_spin_unlock_irqrestore(ch->irq,saveflags);
-+
-+        if(rc != 0)
-+        {
-+#ifdef DEBUG
-+                printk(KERN_INFO "ctcmpc: %s() %04x do_IO failed \n", 
-+                       __FUNCTION__,ch->devno);
-+#endif
-+                ccw_check_return_code(ch, rc);
-+                goto done;
-+        }
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()  %04x\n", 
-+               __FUNCTION__, ch->devno);
-+#endif
-+
-+        return;
-+
-+}       
-+
-+static void 
-+ctcmpc_action_doxid0(fsm_instance *fsm,int event, void *arg)    
-+{
-+        channel     *ch         = (channel *)arg;
-+        ctc_priv    *privptr;
-+        mpc_group   *grpptr     = NULL;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()  %04x\n", 
-+               __FUNCTION__, ch->devno);
-+#endif
-+
-+        if(ch == NULL)
-+        {
-+                printk(KERN_WARNING "%s ch=NULL\n",__FUNCTION__);
-+                goto done;
-+        }
-+
-+        net_device *dev = ch->netdev;
-+        if(dev == NULL)
-+        {
-+                printk(KERN_WARNING "%s dev=NULL, irq=%d\n",
-+                       __FUNCTION__, ch->irq);
-+                goto done;
-+        }
-+
-+        privptr = (ctc_priv *)dev->priv;
-+        if(privptr == NULL)
-+        {
-+                printk(KERN_WARNING "%s privptr=NULL, irq=%d\n",
-+                       __FUNCTION__, ch->irq);
-+                goto done;
-+        }
-+
-+        grpptr = privptr->mpcg;
-+        if(grpptr == NULL)
-+        {
-+                printk(KERN_WARNING "%s grpptr=NULL, irq=%d\n",
-+                       __FUNCTION__, ch->irq);
-+                goto done;
-+        }
-+
-+
-+        if(ch->xid == NULL)
-+        {
-+                printk(KERN_WARNING "%s ch-xid=NULL, irq=%d\n",
-+                       __FUNCTION__,ch->irq);
-+                goto done;
-+        }
-+
-+        fsm_newstate(ch->fsm, CH_XID0_INPROGRESS);
-+
-+        ch->xid->xid2_option =  XID2_0;
-+
-+        switch(fsm_getstate(grpptr->fsm))
-+        {
-+                case MPCG_STATE_XID2INITW:
-+                case MPCG_STATE_XID2INITX:
-+                        ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
-+                        break;
-+                case MPCG_STATE_XID0IOWAIT:
-+                case MPCG_STATE_XID0IOWAIX:
-+                        ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
-+                        break;
-+        }
-+
-+        fsm_event(grpptr->fsm,MPCG_EVENT_DOIO,ch);
-+
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()  %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+        return;
-+
-+}       
-+
-+
-+
-+static void 
-+ctcmpc_action_doxid7(fsm_instance *fsm,int event, void *arg)
-+{
-+        net_device  *dev        = (net_device *)arg;
-+        int         direction;
-+        int         rc          = 0;
-+        int         send        = 0;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() \n", __FUNCTION__);
-+#endif
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "%s dev=NULL \n",__FUNCTION__);
-+                rc = 1;
-+                goto done;
-+        }
-+
-+        ctc_priv   *privptr = (ctc_priv *)dev->priv;
-+        if(privptr == NULL)
-+        {
-+                printk(KERN_INFO "%s privptr=NULL \n",__FUNCTION__);
-+                rc = 1;
-+                goto done;
-+        }
-+
-+        mpc_group  *grpptr = privptr->mpcg;
-+        if(grpptr == NULL)
-+        {
-+                printk(KERN_INFO "%s grpptr=NULL \n",__FUNCTION__);
-+                rc = 1;
-+                goto done;
-+        }
-+
-+        for(direction = READ; direction <= WRITE; direction++)
-+        {
-+                channel *ch = privptr->channel[direction];
-+                xid2 *thisxid = ch->xid;
-+                ch->xid_skb->data = ch->xid_skb->tail = ch->xid_skb_data;
-+                ch->xid_skb->len = 0;
-+                thisxid->xid2_option = XID2_7;
-+                send = 0;
-+
-+                /* xid7 phase 1 */
-+                if(grpptr->outstanding_xid7_p2 > 0)
-+                {
-+                        if(grpptr->roll == YSIDE)
-+                        {
-+                                if(fsm_getstate(ch->fsm) == CH_XID7_PENDING1)
-+                                {
-+                                        fsm_newstate(ch->fsm,CH_XID7_PENDING2);
-+                                        ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
-+                                        memcpy(skb_put(ch->xid_skb,
-+                                                       TH_HEADER_LENGTH),
-+                                               &thdummy,TH_HEADER_LENGTH);
-+                                        send = 1;
-+                                }
-+                        } else
-+                        {
-+                                if(fsm_getstate(ch->fsm) < CH_XID7_PENDING2)
-+                                {
-+                                        fsm_newstate(ch->fsm,CH_XID7_PENDING2);
-+                                        ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
-+                                        memcpy(skb_put(ch->xid_skb,
-+                                                       TH_HEADER_LENGTH),
-+                                               &thnorm,TH_HEADER_LENGTH);
-+                                        send = 1;
-+                                }
-+                        }
-+                }
-+                /* xid7 phase 2 */
-+                else
-+                {
-+                        if(grpptr->roll == YSIDE)
-+                        {
-+                                if(fsm_getstate(ch->fsm) < CH_XID7_PENDING4)
-+                                {
-+                                        fsm_newstate(ch->fsm,CH_XID7_PENDING4);
-+                                        memcpy(skb_put(ch->xid_skb,
-+                                                       TH_HEADER_LENGTH),
-+                                               &thnorm,TH_HEADER_LENGTH);
-+                                        ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
-+                                        send = 1;
-+                                }
-+                        } else
-+                        {
-+                                if(fsm_getstate(ch->fsm) == CH_XID7_PENDING3)
-+                                {
-+                                        fsm_newstate(ch->fsm,CH_XID7_PENDING4);
-+                                        ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
-+                                        memcpy(skb_put(ch->xid_skb,
-+                                                       TH_HEADER_LENGTH),
-+                                               &thdummy,TH_HEADER_LENGTH);
-+                                        send = 1;
-+                                }
-+                        } 
-+                }
-+
-+                if(send)
-+                        fsm_event(grpptr->fsm,MPCG_EVENT_DOIO,ch);
-+        }
-+
-+        done:
-+
-+        if(rc != 0)
-+                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return;
-+}
-+
-+
-+
-+static void 
-+ctcmpc_action_xside_xid(fsm_instance *fsm,int event, void *arg)
-+{
-+        channel           *ch         = (channel *)arg;
-+        ctc_priv          *privptr;
-+        mpc_group         *grpptr     = NULL;
-+        int               rc          = 0;
-+        unsigned long     saveflags;
-+        int               gotlock     = 0;
-+
-+        if(ch == NULL)
-+        {
-+                printk(KERN_INFO "%s ch=NULL\n",__FUNCTION__);
-+                goto done;
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc cp:%i enter:  %s()  %04x\n", 
-+               smp_processor_id(),__FUNCTION__,ch->devno);
-+#endif
-+
-+
-+        net_device *dev = ch->netdev;
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "%s dev=NULL, irq=%d\n",__FUNCTION__,ch->irq);
-+                goto done;
-+        }
-+
-+        privptr = (ctc_priv *)dev->priv;
-+        if(privptr == NULL)
-+        {
-+                printk(KERN_INFO "%s privptr=NULL, irq=%d\n",
-+                       __FUNCTION__,ch->irq);
-+                goto done;
-+        }
-+
-+        grpptr = privptr->mpcg;
-+        if(grpptr == NULL)
-+        {
-+                printk(KERN_INFO "%s grpptr=NULL, irq=%d\n",
-+                       __FUNCTION__,ch->irq);
-+                goto done;
-+        }
-+
-+        if(ctcmpc_checkalloc_buffer(ch, 0))
-+        {
-+                rc = -ENOMEM;
-+                goto done;
-+        }
-+
-+        ch->trans_skb->data =  ch->trans_skb->tail = ch->trans_skb_data;
-+        ch->trans_skb->len = 0;
-+        memset(ch->trans_skb->data, 0, 16);
-+        ch->rcvd_xid_th =  (th_header *)ch->trans_skb->data;
-+        skb_put(ch->trans_skb,TH_HEADER_LENGTH);
-+        ch->rcvd_xid = (xid2 *)ch->trans_skb->tail;
-+        skb_put(ch->trans_skb,XID2_LENGTH);
-+        ch->rcvd_xid_id = ch->trans_skb->tail;
-+        ch->trans_skb->data =  ch->trans_skb->tail = ch->trans_skb_data;
-+        ch->trans_skb->len = 0;
-+
-+        ch->ccw[8].flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[8].count        = 0;
-+        ch->ccw[8].cda        = 0x00;  /* null   */
-+
-+        if(ch->xid_th == NULL)
-+        {
-+                printk(KERN_INFO "%s ch->xid_th=NULL, irq=%d\n",
-+                       __FUNCTION__,ch->irq);
-+                goto done;
-+        }
-+        ch->ccw[9].cmd_code     = CCW_CMD_WRITE;
-+        ch->ccw[9].flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[9].count        = TH_HEADER_LENGTH;
-+        ch->ccw[9].cda          = virt_to_phys(ch->xid_th);
-+
-+        if(ch->xid == NULL)
-+        {
-+                printk(KERN_INFO "%s ch->xid=NULL, irq=%d\n",
-+                       __FUNCTION__,ch->irq);
-+                goto done;
-+        }
-+
-+        ch->ccw[10].cmd_code    = CCW_CMD_WRITE;
-+        ch->ccw[10].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[10].count       = XID2_LENGTH;
-+        ch->ccw[10].cda         = virt_to_phys(ch->xid);
-+
-+        if(ch->rcvd_xid_th == NULL)
-+        {
-+                printk(KERN_INFO "%s ch->rcvd_xid_th=NULL, irq=%d\n",
-+                       __FUNCTION__,ch->irq);
-+                goto done;
-+        }
-+        ch->ccw[11].cmd_code    = CCW_CMD_READ;
-+        ch->ccw[11].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[11].count       = TH_HEADER_LENGTH;
-+        ch->ccw[11].cda         = virt_to_phys(ch->rcvd_xid_th);
-+
-+        if(ch->rcvd_xid == NULL)
-+        {
-+                printk(KERN_INFO "%s ch->rcvd_xid=NULL, irq=%d\n",
-+                       __FUNCTION__,ch->irq);
-+                goto done;
-+        }
-+        ch->ccw[12].cmd_code    = CCW_CMD_READ;
-+        ch->ccw[12].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[12].count       = XID2_LENGTH;
-+        ch->ccw[12].cda         = virt_to_phys(ch->rcvd_xid);
-+
-+        if(ch->xid_id == NULL)
-+        {
-+                printk(KERN_INFO "%s ch->xid_id=NULL, irq=%d\n",
-+                       __FUNCTION__,ch->irq);
-+                goto done;
-+        }
-+        ch->ccw[13].cmd_code    = CCW_CMD_READ;
-+        ch->ccw[13].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[13].count       = 4;
-+        ch->ccw[13].cda         = virt_to_phys(ch->rcvd_xid_id);
-+
-+        ch->ccw[14].cmd_code    = CCW_CMD_NOOP;
-+        ch->ccw[14].flags       = CCW_FLAG_SLI;
-+        ch->ccw[14].count       = 0;
-+        ch->ccw[14].cda         = 0;
-+
-+#ifdef DEBUGCCW
-+        dumpit((char *)&ch->ccw[8],sizeof(ccw1_t) * 7);
-+#endif
-+#ifdef DEBUGXID
-+        dumpit((char *)ch->xid_th,TH_HEADER_LENGTH);
-+        dumpit((char *)ch->xid,XID2_LENGTH);
-+#endif
-+
-+        if(!in_irq())
-+        {
-+                s390irq_spin_lock_irqsave(ch->irq,saveflags);
-+                gotlock = 1;
-+        }
-+
-+        fsm_addtimer(&ch->timer, 5000 , CH_EVENT_TIMER, ch);
-+        rc = do_IO(ch->irq, &ch->ccw[8], (intparm_t)ch, 0xff, 0);
-+
-+        if(gotlock)
-+                s390irq_spin_unlock_irqrestore(ch->irq,saveflags);
-+
-+        if(rc != 0)
-+        {
-+
-+#ifdef DEBUG
-+                printk(KERN_INFO "ctcmpc: %s() %04x do_IO failed \n", 
-+                       __FUNCTION__,ch->devno);
-+#endif
-+                ccw_check_return_code(ch, rc);
-+                goto done;
-+        }
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+        return;
-+}
-+
-+
-+
-+static void 
-+ctcmpc_action_rcvd_xid0(fsm_instance *fsm,int event, void *arg)
-+{
-+
-+        mpcg_info   *mpcginfo   = (mpcg_info *)arg;
-+        channel     *ch         = mpcginfo->ch;
-+        net_device  *dev        = ch->netdev;
-+        ctc_priv    *privptr;
-+        mpc_group   *grpptr;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+        privptr = (ctc_priv *)dev->priv;
-+        grpptr = privptr->mpcg;
-+
-+#ifdef DEBUGXID
-+        printk(KERN_INFO "ctcmpc in:%s() %04x xid2:%i xid7:%i xidt_p2:%i \n", 
-+               __FUNCTION__,ch->devno,
-+               grpptr->outstanding_xid2,
-+               grpptr->outstanding_xid7,
-+               grpptr->outstanding_xid7_p2); 
-+#endif
-+
-+        if(fsm_getstate(ch->fsm) < CH_XID7_PENDING)
-+                fsm_newstate(ch->fsm,CH_XID7_PENDING);
-+
-+        grpptr->outstanding_xid2--;
-+        grpptr->outstanding_xid7++;
-+        grpptr->outstanding_xid7_p2++;
-+
-+        /* must change state before validating xid to */
-+        /* properly handle interim interrupts received*/
-+        switch(fsm_getstate(grpptr->fsm))
-+        {
-+                case MPCG_STATE_XID2INITW:
-+                        fsm_newstate(grpptr->fsm,MPCG_STATE_XID2INITX);
-+                        ctcmpc_validate_xid(mpcginfo);
-+                        break;
-+                case MPCG_STATE_XID0IOWAIT:
-+                        fsm_newstate(grpptr->fsm,MPCG_STATE_XID0IOWAIX);
-+                        ctcmpc_validate_xid(mpcginfo);
-+                        break;
-+                case MPCG_STATE_XID2INITX:       
-+                        if(grpptr->outstanding_xid2 == 0)
-+                        {
-+                                fsm_newstate(grpptr->fsm,MPCG_STATE_XID7INITW);
-+                                ctcmpc_validate_xid(mpcginfo);
-+                                fsm_event(grpptr->fsm,MPCG_EVENT_XID2DONE,dev);
-+                        }
-+                        break;
-+                case MPCG_STATE_XID0IOWAIX:
-+                        if(grpptr->outstanding_xid2 == 0)
-+                        {
-+                                fsm_newstate(grpptr->fsm,MPCG_STATE_XID7INITI);
-+                                ctcmpc_validate_xid(mpcginfo);
-+                                fsm_event(grpptr->fsm,MPCG_EVENT_XID2DONE,dev);
-+                        }
-+                        break;
-+        }
-+        kfree(mpcginfo);
-+
-+#ifdef DEBUGXID
-+        printk(KERN_INFO "ctcmpc out:%s() %04x xid2:%i xid7:%i xidt_p2:%i \n", 
-+               __FUNCTION__,ch->devno,
-+               grpptr->outstanding_xid2,
-+               grpptr->outstanding_xid7,
-+               grpptr->outstanding_xid7_p2); 
-+        printk(KERN_INFO "ctcmpc out:%s() %04x groupstate: %s chanstate: %s \n", 
-+               __FUNCTION__,ch->devno,
-+               fsm_getstate_str(grpptr->fsm), 
-+               fsm_getstate_str(ch->fsm)); 
-+#endif
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", __FUNCTION__,ch->devno);
-+#endif
-+
-+        return;
-+
-+}
-+
-+
-+static void 
-+ctcmpc_action_rcvd_xid7(fsm_instance *fsm,int event, void *arg)
-+{
-+
-+        mpcg_info   *mpcginfo   = (mpcg_info *)arg;
-+        channel     *ch         = mpcginfo->ch;
-+        net_device  *dev        = ch->netdev;
-+        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group   *grpptr     = privptr->mpcg;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+#ifdef DEBUGXID
-+        printk(KERN_INFO "ctcmpc:  outstanding_xid7: %i, "
-+               "outstanding_xid7_p2: %i\n",
-+               grpptr->outstanding_xid7,
-+               grpptr->outstanding_xid7_p2);
-+#endif
-+
-+
-+        grpptr->outstanding_xid7--;
-+
-+        ch->xid_skb->data = ch->xid_skb->tail = ch->xid_skb_data;
-+        ch->xid_skb->len = 0;
-+
-+        switch(fsm_getstate(grpptr->fsm))
-+        {
-+                case MPCG_STATE_XID7INITI:
-+                        fsm_newstate(grpptr->fsm,MPCG_STATE_XID7INITZ);
-+                        ctcmpc_validate_xid(mpcginfo);
-+                        break;
-+                case MPCG_STATE_XID7INITW:
-+                        fsm_newstate(grpptr->fsm,MPCG_STATE_XID7INITX);
-+                        ctcmpc_validate_xid(mpcginfo);
-+                        break;
-+                case MPCG_STATE_XID7INITZ: 
-+                case MPCG_STATE_XID7INITX:
-+                        if(grpptr->outstanding_xid7 == 0)
-+                        {
-+                                if(grpptr->outstanding_xid7_p2 > 0)
-+                                {
-+                                        grpptr->outstanding_xid7 = 
-+                                                grpptr->outstanding_xid7_p2;
-+                                        grpptr->outstanding_xid7_p2 = 0;
-+                                } else
-+                                        fsm_newstate(grpptr->fsm,
-+                                                     MPCG_STATE_XID7INITF);
-+                                ctcmpc_validate_xid(mpcginfo);
-+                                fsm_event(grpptr->fsm,MPCG_EVENT_XID7DONE,dev);
-+                                break;
-+                        }
-+                        ctcmpc_validate_xid(mpcginfo);
-+                        break;
-+        }
-+
-+        kfree(mpcginfo);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+        return;
-+
-+}
-+
-+static void 
-+ctcmpc_action_attn(fsm_instance *fsm,int event, void *arg)
-+{
-+        channel     *ch         = (channel *)arg;
-+        net_device  *dev        = ch->netdev;
-+        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group   *grpptr     = privptr->mpcg;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %04x  \nGrpState:%s ChState:%s\n", 
-+               __FUNCTION__,ch->devno,
-+               fsm_getstate_str(grpptr->fsm),
-+               fsm_getstate_str(ch->fsm));
-+#endif
-+
-+        switch(fsm_getstate(grpptr->fsm))
-+        {
-+                case MPCG_STATE_XID2INITW:
-+                        /* ok..start yside xid exchanges */
-+                        if(ch->in_mpcgroup)
-+                        {
-+                                if(fsm_getstate(ch->fsm) ==  CH_XID0_PENDING)
-+                                {
-+                                        fsm_deltimer(&grpptr->timer);
-+                                        fsm_addtimer(&grpptr->timer, 
-+                                                     MPC_XID_TIMEOUT_VALUE, 
-+                                                     MPCG_EVENT_TIMER, 
-+                                                     dev);
-+                                        fsm_event(grpptr->fsm, 
-+                                                  MPCG_EVENT_XID0DO, 
-+                                                  ch);
-+                                } else
-+                                {/* attn rcvd before xid0 processed via bh */
-+                                        if(fsm_getstate(ch->fsm) < 
-+                                           CH_XID7_PENDING1)
-+                                                fsm_newstate(ch->fsm,
-+                                                             CH_XID7_PENDING1);
-+                                }
-+                        }
-+                        break;
-+                case MPCG_STATE_XID2INITX:
-+                case MPCG_STATE_XID0IOWAIT:
-+                case MPCG_STATE_XID0IOWAIX:
-+                        /* attn rcvd before xid0 processed on ch  
-+                        but mid-xid0 processing for group    */
-+                        if(fsm_getstate(ch->fsm) < CH_XID7_PENDING1)
-+                                fsm_newstate(ch->fsm,CH_XID7_PENDING1);
-+                        break;
-+                case MPCG_STATE_XID7INITW:
-+                case MPCG_STATE_XID7INITX:
-+                case MPCG_STATE_XID7INITI:
-+                case MPCG_STATE_XID7INITZ:
-+                        switch(fsm_getstate(ch->fsm))
-+                        {
-+                                case CH_XID7_PENDING:
-+                                        fsm_newstate(ch->fsm,CH_XID7_PENDING1);
-+                                        break;
-+                                case CH_XID7_PENDING2:
-+                                        fsm_newstate(ch->fsm,CH_XID7_PENDING3);
-+                                        break;
-+                        }
-+                        fsm_event(grpptr->fsm,MPCG_EVENT_XID7DONE,dev);
-+                        break;
-+        }
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", __FUNCTION__,ch->devno);
-+#endif
-+        return;
-+
-+}
-+
-+static void 
-+ctcmpc_action_attnbusy(fsm_instance *fsm,int event, void *arg)
-+{
-+        channel     *ch         = (channel *)arg;
-+        net_device  *dev        = ch->netdev;
-+        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group   *grpptr     = privptr->mpcg;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s enter:  %s() %04x  \nGrpState:%s ChState:%s\n", 
-+               dev->name,
-+               __FUNCTION__,ch->devno,
-+               fsm_getstate_str(grpptr->fsm),
-+               fsm_getstate_str(ch->fsm));
-+#endif
-+
-+
-+        fsm_deltimer(&ch->timer);
-+
-+        switch(fsm_getstate(grpptr->fsm))
-+        {
-+                case MPCG_STATE_XID0IOWAIT:
-+                        /* vtam wants to be primary..start yside xid exchanges*/
-+                        /* will only rcv  one attn-busy at a time so must not */
-+                        /* change state each time                             */
-+                        grpptr->changed_side = 1;
-+                        fsm_newstate(grpptr->fsm,MPCG_STATE_XID2INITW);
-+                        break;
-+                case MPCG_STATE_XID2INITW:
-+                        if(grpptr->changed_side == 1)
-+                        {
-+                                grpptr->changed_side = 2;
-+                                break;
-+                        }
-+                        /* process began via call to establish_conn      */
-+                        /* so must report failure instead of reverting   */
-+                        /* back to ready-for-xid passive state           */
-+                        if(grpptr->estconnfunc)
-+                                goto done;
-+                        /* this attnbusy is NOT the result of xside xid  */
-+                        /* collisions so yside must have been triggered  */
-+                        /* by an ATTN that was not intended to start XID */
-+                        /* processing. Revert back to ready-for-xid and  */
-+                        /* wait for ATTN interrupt to signal xid start   */
-+                        if(fsm_getstate(ch->fsm) == CH_XID0_INPROGRESS)
-+                        {
-+                                fsm_newstate(ch->fsm,CH_XID0_PENDING) ;
-+                                fsm_deltimer(&grpptr->timer);
-+                                goto done;
-+                        }
-+                        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+                        goto done;
-+                case MPCG_STATE_XID2INITX:
-+                        /* XID2 was received before ATTN Busy for second
-+                           channel.Send yside xid for second channel.
-+                        */
-+                        if(grpptr->changed_side == 1)
-+                        {
-+                                grpptr->changed_side = 2;
-+                                break;
-+                        }
-+                case MPCG_STATE_XID0IOWAIX:
-+                case MPCG_STATE_XID7INITW:
-+                case MPCG_STATE_XID7INITX:
-+                case MPCG_STATE_XID7INITI:
-+                case MPCG_STATE_XID7INITZ:
-+                default:
-+                        /* multiple attn-busy indicates too out-of-sync      */
-+                        /* and they are certainly not being received as part */
-+                        /* of valid mpc group negotiations..                 */
-+                        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+                        goto done;
-+        }
-+
-+        if(grpptr->changed_side == 1)
-+        {
-+                fsm_deltimer(&grpptr->timer);
-+                fsm_addtimer(&grpptr->timer, MPC_XID_TIMEOUT_VALUE, 
-+                             MPCG_EVENT_TIMER, dev);
-+        }
-+        if(ch->in_mpcgroup)
-+                fsm_event(grpptr->fsm, MPCG_EVENT_XID0DO, ch);
-+        else
-+                printk( KERN_WARNING "ctcmpc: %s() Not all channels have "
-+                        "been added to group\n",
-+                        __FUNCTION__);
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s() %04x\n", 
-+               dev->name,__FUNCTION__,ch->devno);
-+#endif
-+        return;
-+
-+}
-+
-+static void 
-+ctcmpc_action_resend(fsm_instance *fsm,int event, void *arg)
-+{
-+        channel     *ch         = (channel *)arg;
-+        net_device  *dev        = ch->netdev;
-+        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group   *grpptr     = privptr->mpcg;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s enter:  %s() %04x  \nGrpState:%s ChState:%s\n", 
-+               dev->name,__FUNCTION__,ch->devno,
-+               fsm_getstate_str(grpptr->fsm),
-+               fsm_getstate_str(ch->fsm));
-+#endif
-+        fsm_event(grpptr->fsm, MPCG_EVENT_XID0DO, ch);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s() %04x\n", 
-+               dev->name,__FUNCTION__,ch->devno);
-+#endif
-+        return;
-+
-+}
-+
-+
-+static int 
-+ctcmpc_send_qllc_discontact(net_device *dev)
-+{
-+        int               rc = 0, space = 0;
-+        __u32         new_len = 0;
-+        struct sk_buff    *skb;
-+        qllc              *qllcptr;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n",
-+               __FUNCTION__);
-+#endif
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "%s() dev=NULL\n",
-+                       __FUNCTION__);
-+                rc = 1;
-+                goto done;
-+        }
-+
-+        ctc_priv *privptr = (ctc_priv *)dev->priv;
-+        if(privptr == NULL)
-+        {
-+                printk(KERN_INFO "%s() privptr=NULL\n",
-+                       __FUNCTION__);
-+                rc = 1;
-+                goto done;
-+        }
-+
-+        mpc_group       *grpptr = privptr->mpcg;
-+        if(grpptr == NULL)
-+        {
-+                printk(KERN_INFO "%s() grpptr=NULL\n",
-+                       __FUNCTION__);
-+                rc = 1;
-+                goto done;
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc: %s() GROUP STATE: %s\n", 
-+               __FUNCTION__,mpcg_state_names[grpptr->saved_state]);
-+#endif
-+
-+
-+        switch(grpptr->saved_state)
-+        {       /* establish conn callback function is */
-+                /* preferred method to report failure  */
-+                case MPCG_STATE_XID0IOWAIT:
-+                case MPCG_STATE_XID0IOWAIX:
-+                case MPCG_STATE_XID7INITI:
-+                case MPCG_STATE_XID7INITZ:
-+                case MPCG_STATE_XID2INITW:
-+                case MPCG_STATE_XID2INITX:
-+                case MPCG_STATE_XID7INITW:
-+                case MPCG_STATE_XID7INITX:
-+                        if(grpptr->estconnfunc)
-+                        {
-+                                grpptr->estconnfunc(grpptr->port_num,-1,0);
-+                                grpptr->estconnfunc = NULL;
-+                                break;
-+                        }
-+                case MPCG_STATE_FLOWC:
-+                case MPCG_STATE_READY:
-+                        grpptr->send_qllc_disc = 2;
-+                        new_len = sizeof(qllc);
-+                        if((qllcptr = (qllc *)kmalloc(sizeof(qllc), 
-+                                                      gfp_type() | GFP_DMA)) 
-+                           == NULL)
-+                        {
-+                                printk(KERN_INFO "qllc: Out of memory"
-+                                       " in send_qllc\n");
-+                                rc = 1;
-+                                goto done;
-+                        }
-+
-+                        memset(qllcptr, 0, new_len);
-+                        qllcptr->qllc_address = 0xcc;
-+                        qllcptr->qllc_commands = 0x03;
-+
-+                        skb = __dev_alloc_skb(new_len,GFP_ATOMIC);
-+
-+                        if(skb == NULL)
-+                        {
-+                                printk(KERN_INFO
-+                                       "%s Out of memory in ctcmpc_send_qllc\n",
-+                                       dev->name);
-+                                privptr->stats.rx_dropped++;
-+                                rc = 1;
-+                                kfree(qllcptr);
-+                                goto done;
-+                        }
-+
-+                        memcpy(skb_put(skb, new_len), qllcptr, new_len);
-+                        kfree(qllcptr);
-+
-+                        space = skb_headroom(skb);
-+                        if(space < 4)
-+                        {
-+                                printk(KERN_INFO "%s Unable to build "
-+                                       "discontact for %s\n",
-+                                       __FUNCTION__,dev->name);
-+                                rc = 1;
-+                                dev_kfree_skb_any(skb);
-+                                goto done;
-+                        }
-+
-+                        *((__u32 *) skb_push(skb, 4)) = 
-+                                privptr->channel[READ]->pdu_seq;
-+                        privptr->channel[READ]->pdu_seq++;
-+#ifdef DEBUGSEQ
-+                        printk(KERN_INFO "%s: ToDCM_pdu_seq= %08x\n" ,
-+                               __FUNCTION__,privptr->channel[READ]->pdu_seq);
-+#endif
-+                        /* receipt of CC03 resets anticipated sequence "
-+                        "number on receiving side */
-+                        privptr->channel[READ]->pdu_seq = 0x00;
-+
-+                        skb->mac.raw = skb->data;
-+                        skb->dev = dev;
-+                        skb->protocol = htons(ETH_P_SNAP);
-+                        skb->ip_summed = CHECKSUM_UNNECESSARY;
-+
-+#ifdef DEBUGDATA
-+                        dumpit((char *)skb->data,(sizeof(qllc)+4));
-+#endif
-+                        netif_rx(skb);
-+                        break;
-+                default: break;
-+
-+        }
-+
-+        done:
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return(rc);
-+
-+}
-+
-+static void 
-+ctcmpc_send_sweep(fsm_instance *fsm,int event, void *arg)
-+{
-+        channel           *ach        = (channel *)arg;
-+        net_device        *dev        = ach->netdev;
-+        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group         *grpptr     = privptr->mpcg;
-+        int               rc          = 0;
-+        sk_buff           *skb;
-+        unsigned long     saveflags;
-+        channel           *wch        = privptr->channel[WRITE];        
-+        channel           *rch        = privptr->channel[READ];  
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc cp:%i enter:  %s() %04x\n", 
-+               smp_processor_id(),__FUNCTION__,ach->devno);
-+#endif
-+
-+        if(grpptr->in_sweep == 0)
-+                goto done;
-+
-+#ifdef DEBUGSEQ
-+        printk(KERN_INFO "%s 1: ToVTAM_th_seq= %08x\n" ,
-+               __FUNCTION__,wch->th_seq_num);
-+        printk(KERN_INFO "%s 1: FromVTAM_th_seq= %08x\n" ,
-+               __FUNCTION__,rch->th_seq_num);
-+#endif
-+
-+
-+        if(fsm_getstate(wch->fsm) != CH_STATE_TXIDLE)
-+        {
-+                /* give the previous do_IO time to complete */
-+                fsm_addtimer(&wch->sweep_timer,200,CH_EVENT_RSWEEP1_TIMER,wch);
-+                goto done;
-+        }
-+
-+        skb = skb_dequeue(&wch->sweep_queue);
-+        if(!skb)
-+                goto done;
-+
-+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21))
-+        if(set_normalized_cda(&wch->ccw[4], virt_to_phys(skb->data)))
-+        {
-+#else
-+        if(set_normalized_cda(&wch->ccw[4], skb->data))
-+        {
-+#endif
-+                grpptr->in_sweep = 0;
-+                ctcmpc_clear_busy(dev);
-+                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);     
-+                goto done;
-+        } else
-+                skb_queue_tail(&wch->io_queue, skb);
-+
-+        /* send out the sweep */
-+        wch->ccw[4].count = skb->len;
-+
-+        th_sweep *header = (th_sweep *)skb->data;
-+        switch(header->th.th_ch_flag)
-+        {
-+                case TH_SWEEP_REQ:      
-+                        grpptr->sweep_req_pend_num--;
-+                        break;
-+                case TH_SWEEP_RESP:     
-+                        grpptr->sweep_rsp_pend_num--;
-+                        break;
-+        }
-+
-+        header->sw.th_last_seq  = wch->th_seq_num;
-+
-+#ifdef DEBUGCCW
-+        dumpit((char *)&wch->ccw[3],sizeof(ccw1_t) * 3);
-+#endif
-+
-+#ifdef DEBUGSEQ
-+        printk(KERN_INFO "%s(): sweep packet\n", __FUNCTION__);
-+        dumpit((char *)header,TH_SWEEP_LENGTH);
-+#endif
-+
-+
-+        fsm_addtimer(&wch->timer,CTC_TIMEOUT_5SEC,CH_EVENT_TIMER, wch);
-+        fsm_newstate(wch->fsm, CH_STATE_TX);
-+
-+        s390irq_spin_lock_irqsave(wch->irq, saveflags);
-+        wch->prof.send_stamp = xtime;
-+        rc = do_IO(wch->irq, &wch->ccw[3], (intparm_t)wch, 0xff, 0);
-+        s390irq_spin_unlock_irqrestore(wch->irq, saveflags);
-+
-+        if((grpptr->sweep_req_pend_num == 0) &&
-+           (grpptr->sweep_rsp_pend_num == 0))
-+        {
-+                grpptr->in_sweep = 0;
-+                rch->th_seq_num = 0x00;
-+                wch->th_seq_num = 0x00;
-+                ctcmpc_clear_busy(dev);
-+        }
-+
-+#ifdef DEBUGSEQ
-+        printk(KERN_INFO "%s 2: ToVTAM_th_seq= %08x\n" ,
-+               __FUNCTION__,wch->th_seq_num);
-+        printk(KERN_INFO "%s 2: FromVTAM_th_seq= %08x\n" ,
-+               __FUNCTION__,rch->th_seq_num);
-+#endif
-+
-+        if(rc != 0)
-+                ccw_check_return_code(wch, rc);
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", __FUNCTION__,ach->devno);
-+#endif
-+        return;
-+
-+}
-+
-+
-+static void 
-+ctcmpc_rcvd_sweep_resp(mpcg_info *mpcginfo)
-+{
-+        channel           *rch        = mpcginfo->ch;
-+        net_device        *dev        = rch->netdev;
-+        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group         *grpptr     = privptr->mpcg;
-+        channel           *ch         = privptr->channel[WRITE];        
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+#ifdef DEBUGSEQ
-+        dumpit((char *)mpcginfo->sweep,TH_SWEEP_LENGTH);
-+#endif
-+
-+
-+        grpptr->sweep_rsp_pend_num--;
-+
-+        if((grpptr->sweep_req_pend_num == 0) &&
-+           (grpptr->sweep_rsp_pend_num == 0))
-+        {
-+                fsm_deltimer(&ch->sweep_timer);
-+                grpptr->in_sweep = 0;
-+                rch->th_seq_num = 0x00;
-+                ch->th_seq_num = 0x00;
-+                ctcmpc_clear_busy(dev);
-+        }
-+
-+        kfree(mpcginfo);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+        return;
-+
-+}
-+
-+
-+static void 
-+ctcmpc_send_sweep_req(channel *rch)
-+{
-+        net_device        *dev        = rch->netdev;
-+        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group         *grpptr     = privptr->mpcg;
-+        th_sweep          *header;
-+        struct sk_buff    *sweep_skb;
-+        int               rc          = 0;
-+        channel           *ch         = privptr->channel[WRITE];        
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+        /* sweep processing is not complete until response and request */
-+        /* has completed for all read channels in group                */
-+        if(grpptr->in_sweep == 0)
-+        {
-+                grpptr->in_sweep = 1;
-+                grpptr->sweep_rsp_pend_num = grpptr->active_channels[READ];
-+                grpptr->sweep_req_pend_num = grpptr->active_channels[READ];
-+        }
-+
-+
-+        sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
-+                                    GFP_ATOMIC|GFP_DMA);
-+
-+        if(sweep_skb == NULL)
-+        {
-+                printk(KERN_INFO "Couldn't alloc sweep_skb\n");
-+                rc = -ENOMEM;
-+                goto done;
-+        }
-+
-+        header = (th_sweep *)kmalloc(TH_SWEEP_LENGTH, gfp_type());
-+
-+        if(!header)
-+        {
-+                dev_kfree_skb_any(sweep_skb);
-+                rc = -ENOMEM;
-+                goto done;
-+        }
-+
-+        header->th.th_seg       = 0x00 ;
-+        header->th.th_ch_flag   = TH_SWEEP_REQ;  /* 0x0f */
-+        header->th.th_blk_flag  = 0x00;
-+        header->th.th_is_xid    = 0x00; 
-+        header->th.th_seq_num   = 0x00;
-+        header->sw.th_last_seq  = ch->th_seq_num;
-+
-+        memcpy(skb_put(sweep_skb,TH_SWEEP_LENGTH),header,TH_SWEEP_LENGTH);
-+
-+        kfree(header);
-+
-+        dev->trans_start = jiffies;
-+        skb_queue_tail(&ch->sweep_queue,sweep_skb);
-+
-+        fsm_addtimer(&ch->sweep_timer,100,CH_EVENT_RSWEEP1_TIMER,ch);
-+
-+        return;
-+        done:
-+        if(rc != 0)
-+        {
-+                grpptr->in_sweep = 0;
-+                ctcmpc_clear_busy(dev);
-+                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+        return;
-+}
-+
-+static void 
-+ctcmpc_send_sweep_resp(channel *rch)
-+{
-+        net_device        *dev        = rch->netdev;
-+        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group         *grpptr     = privptr->mpcg;
-+        int               rc          = 0;
-+        th_sweep          *header;
-+        struct sk_buff    *sweep_skb;
-+        channel           *ch         = privptr->channel[WRITE];
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
-+               __FUNCTION__,rch->devno);
-+#endif
-+
-+        sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
-+                                    GFP_ATOMIC|GFP_DMA);
-+        if(sweep_skb == NULL)
-+        {
-+                printk(KERN_INFO
-+                       "Couldn't alloc sweep_skb\n");
-+                rc = -ENOMEM;
-+                goto done;
-+        }
-+
-+        header = (th_sweep *)kmalloc(sizeof(struct th_sweep_t), gfp_type());
-+
-+        if(!header)
-+        {
-+                dev_kfree_skb_any(sweep_skb);
-+                rc = -ENOMEM;
-+                goto done;
-+        }
-+
-+        header->th.th_seg       = 0x00 ;
-+        header->th.th_ch_flag   = TH_SWEEP_RESP;
-+        header->th.th_blk_flag  = 0x00;
-+        header->th.th_is_xid    = 0x00; 
-+        header->th.th_seq_num   = 0x00;
-+        header->sw.th_last_seq  = ch->th_seq_num;
-+
-+        memcpy(skb_put(sweep_skb,TH_SWEEP_LENGTH),header,TH_SWEEP_LENGTH);
-+
-+        kfree(header);
-+
-+        dev->trans_start = jiffies;
-+        skb_queue_tail(&ch->sweep_queue,sweep_skb);
-+
-+        fsm_addtimer(&ch->sweep_timer,100,CH_EVENT_RSWEEP1_TIMER,ch);
-+
-+        return;
-+
-+        done:
-+        if(rc != 0)
-+        {
-+                grpptr->in_sweep = 0;
-+                ctcmpc_clear_busy(dev);
-+                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+        return;
-+
-+}
-+
-+static void 
-+ctcmpc_rcvd_sweep_req(mpcg_info *mpcginfo)
-+{
-+        channel     *rch        = mpcginfo->ch;
-+        net_device  *dev        = rch->netdev;
-+        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group   *grpptr     = privptr->mpcg;
-+        channel     *ch         = privptr->channel[WRITE];      
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+        if(grpptr->in_sweep == 0)
-+        {
-+                grpptr->in_sweep = 1;
-+                ctcmpc_test_and_set_busy(dev);
-+                grpptr->sweep_req_pend_num = grpptr->active_channels[READ];
-+                grpptr->sweep_rsp_pend_num = grpptr->active_channels[READ];
-+        }
-+
-+#ifdef DEBUGSEQ
-+        dumpit((char *)mpcginfo->sweep,TH_SWEEP_LENGTH);
-+#endif
-+
-+        grpptr->sweep_req_pend_num --;
-+
-+        ctcmpc_send_sweep_resp(ch);
-+
-+        kfree(mpcginfo);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+        return;
-+}
-+
-+static void 
-+ctcmpc_action_go_ready(fsm_instance *fsm,int event, void *arg)
-+{
-+        net_device     *dev = (net_device *)arg;
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "%s() dev=NULL\n",
-+                       __FUNCTION__);
-+                return;
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s enter:  %s()\n", 
-+               dev->name,__FUNCTION__);
-+#endif
-+
-+        ctc_priv *privptr = (ctc_priv *)dev->priv;
-+        if(privptr == NULL)
-+        {
-+                printk(KERN_INFO "%s() privptr=NULL\n",
-+                       __FUNCTION__);
-+                return;
-+        }
-+
-+        mpc_group       *grpptr = privptr->mpcg;
-+        if(grpptr == NULL)
-+        {
-+                printk(KERN_INFO "%s() grpptr=NULL\n",
-+                       __FUNCTION__);
-+                return;
-+        }
-+
-+        fsm_deltimer(&grpptr->timer);
-+
-+        if(grpptr->saved_xid2->xid2_flag2 == 0x40)
-+        {
-+                privptr->xid->xid2_flag2 = 0x00;
-+                if(grpptr->estconnfunc)
-+                {
-+                        grpptr->estconnfunc(grpptr->port_num,1,
-+                                            grpptr->group_max_buflen);
-+                        grpptr->estconnfunc = NULL;
-+                } else
-+                        if(grpptr->allochanfunc)
-+                        grpptr->send_qllc_disc = 1;
-+                goto done;
-+        }
-+
-+        grpptr->port_persist = 1;
-+        grpptr->out_of_sequence = 0;
-+        grpptr->estconn_called = 0;
-+
-+        tasklet_hi_schedule(&grpptr->mpc_tasklet2);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+        return;
-+
-+        done:
-+        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit: failure occurred: %s()\n", 
-+               __FUNCTION__);
-+#endif
-+
-+}
-+
-+
-+static void 
-+ctcmpc_group_ready(unsigned long adev)
-+{
-+        net_device *dev = (net_device *)adev;
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "%s() dev=NULL\n",__FUNCTION__);
-+                return;
-+        }
-+
-+        ctc_priv *privptr = (ctc_priv *)dev->priv;
-+        if(privptr == NULL)
-+        {
-+                printk(KERN_INFO "%s() privptr=NULL\n",__FUNCTION__);
-+                return;
-+        }
-+
-+        mpc_group       *grpptr = privptr->mpcg;
-+        if(grpptr == NULL)
-+        {
-+                printk(KERN_INFO "%s() grpptr=NULL\n",__FUNCTION__);
-+                return;
-+        }
-+
-+        printk(KERN_NOTICE "%s:GROUP TRANSITIONED TO READY maxbuf:%d\n", 
-+               dev->name,grpptr->group_max_buflen);
-+
-+        fsm_newstate(grpptr->fsm, MPCG_STATE_READY);
-+
-+        /* Put up a read on the channel */
-+        channel *ch = privptr->channel[READ];
-+        ch->pdu_seq = 0;
-+#ifdef DEBUGSEQ
-+        printk(KERN_INFO "%s: ToDCM_pdu_seq= %08x\n" ,
-+               __FUNCTION__,ch->pdu_seq);
-+#endif
-+
-+        ctcmpc_ch_action_rxidle(ch->fsm, CH_EVENT_START, ch);
-+        /* Put the write channel in idle state */
-+        ch = privptr->channel[WRITE];
-+        if(ch->collect_len > 0)
-+        {
-+                spin_lock(&ch->collect_lock);
-+                ctcmpc_purge_skb_queue(&ch->collect_queue);
-+                ch->collect_len = 0;
-+                spin_unlock(&ch->collect_lock);
-+        }
-+        ctcmpc_ch_action_txidle(ch->fsm, CH_EVENT_START, ch);
-+
-+        ctcmpc_clear_busy(dev);
-+
-+        if(grpptr->estconnfunc)
-+        {
-+                grpptr->estconnfunc(grpptr->port_num,0,
-+                                    grpptr->group_max_buflen);
-+                grpptr->estconnfunc = NULL;
-+        } else
-+                if(grpptr->allochanfunc)
-+                grpptr->allochanfunc(grpptr->port_num,
-+                                     grpptr->group_max_buflen);
-+
-+        grpptr->send_qllc_disc = 1;
-+        grpptr->changed_side = 0;
-+
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return;
-+
-+}
-+
-+/****************************************************************/
-+/* Increment the MPC Group Active Channel Counts                */
-+/****************************************************************/
-+static int 
-+ctcmpc_channel_action(channel *ch, int direction, int action)
-+{
-+        net_device  *dev     = ch->netdev;
-+        ctc_priv    *privptr;
-+        mpc_group   *grpptr  = NULL;
-+        int         rc = 0;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+
-+        if(dev == NULL)
-+        {
-+                printk(KERN_INFO "mpc_channel_action %i dev=NULL, irq=%d\n",
-+                       action,ch->irq);
-+                rc = 1;
-+                goto done;
-+        }
-+
-+        privptr = (ctc_priv *)dev->priv;
-+        if(privptr == NULL)
-+        {
-+                printk(KERN_INFO "mpc_channel_action%i privptr=NULL, dev=%s\n",
-+                       action,dev->name);
-+                rc = 2;
-+                goto done;
-+        }
-+
-+        grpptr = privptr->mpcg;
-+
-+        if(grpptr == NULL)
-+        {
-+                printk(KERN_INFO "mpc_channel_action%i mpcgroup=NULL, dev=%s\n",
-+                       action,dev->name);
-+                rc = 3;
-+                goto done;
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO
-+               "ctcmpc enter : %s %i(): Grp:%s total_channel_paths=%i "
-+               "active_channels read=%i,write=%i\n", 
-+               __FUNCTION__,
-+               action,
-+               fsm_getstate_str(grpptr->fsm),
-+               grpptr->num_channel_paths, 
-+               grpptr->active_channels[READ],
-+               grpptr->active_channels[WRITE]);
-+#endif
-+
-+        switch(action)
-+        {
-+                case MPC_CHANNEL_ADD:
-+                        if(ch->in_mpcgroup == 0)
-+                        {
-+                                grpptr->num_channel_paths++;
-+                                grpptr->active_channels[direction]++;
-+                                grpptr->outstanding_xid2++;
-+                                ch->in_mpcgroup = 1;
-+
-+                                if(ch->xid_skb != NULL)
-+                                        dev_kfree_skb_any(ch->xid_skb);
-+                                ch->xid_skb = 
-+                                        __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
-+                                                        GFP_ATOMIC|GFP_DMA);
-+                                if(ch->xid_skb == NULL)
-+                                {
-+                                        printk(KERN_INFO 
-+                                               "Couldn't alloc ch xid_skb\n");
-+                                        fsm_event(grpptr->fsm,
-+                                                  MPCG_EVENT_INOP,dev);
-+                                        return 1;
-+                                }
-+
-+                                ch->xid_skb_data = ch->xid_skb->data;
-+                                ch->xid_th = (th_header *)ch->xid_skb->data;
-+                                skb_put(ch->xid_skb,TH_HEADER_LENGTH);
-+                                ch->xid = (xid2 *)ch->xid_skb->tail;
-+                                skb_put(ch->xid_skb,XID2_LENGTH);
-+                                ch->xid_id = ch->xid_skb->tail;
-+                                ch->xid_skb->data =  
-+                                        ch->xid_skb->tail = 
-+                                        ch->xid_skb_data;
-+                                ch->xid_skb->len = 0;
-+
-+
-+                                memcpy(skb_put(ch->xid_skb,
-+                                               grpptr->xid_skb->len),
-+                                       grpptr->xid_skb->data,
-+                                       grpptr->xid_skb->len);
-+
-+                                ch->xid->xid2_dlc_type = 
-+                                ((CHANNEL_DIRECTION(ch->flags) == READ)
-+                                 ? XID2_READ_SIDE : XID2_WRITE_SIDE );
-+
-+                                if(CHANNEL_DIRECTION(ch->flags) == WRITE)
-+                                        ch->xid->xid2_buf_len = 0x00;
-+
-+
-+                                ch->xid_skb->data = 
-+                                        ch->xid_skb->tail = 
-+                                        ch->xid_skb_data;
-+                                ch->xid_skb->len = 0;
-+
-+                                fsm_newstate(ch->fsm,CH_XID0_PENDING);
-+                                if((grpptr->active_channels[READ]  > 0) && 
-+                                   (grpptr->active_channels[WRITE] > 0) &&
-+                                   (fsm_getstate(grpptr->fsm) < 
-+                                    MPCG_STATE_XID2INITW))
-+                                {
-+                                        fsm_newstate(grpptr->fsm, 
-+                                                     MPCG_STATE_XID2INITW);
-+                                        printk(KERN_NOTICE 
-+                                               "%s MPC GROUP CHANNELS ACTIVE\n",
-+                                               dev->name);
-+                                }
-+
-+
-+                        }
-+                        break; 
-+                case MPC_CHANNEL_REMOVE:
-+                        if(ch->in_mpcgroup == 1)
-+                        {
-+                                ch->in_mpcgroup = 0;
-+                                grpptr->num_channel_paths--;
-+                                grpptr->active_channels[direction]--;
-+
-+                                if(ch->xid_skb != NULL)
-+                                        dev_kfree_skb_any(ch->xid_skb);
-+                                ch->xid_skb = NULL;
-+
-+                                if(grpptr->channels_terminating)
-+                                        break;
-+
-+                                if(((grpptr->active_channels[READ]  == 0) && 
-+                                    (grpptr->active_channels[WRITE] > 0)) || 
-+                                   ((grpptr->active_channels[WRITE] == 0) && 
-+                                    (grpptr->active_channels[READ]  > 0)))
-+                                        fsm_event(grpptr->fsm,
-+                                                  MPCG_EVENT_INOP,
-+                                                  dev);
-+                        }
-+                        break;
-+        }
-+
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO
-+               "ctcmpc leave: %s %i(): Grp:%s total_channel_paths=%i "
-+               "active_channels read=%i,write=%i\n", 
-+               __FUNCTION__,
-+               action,
-+               fsm_getstate_str(grpptr->fsm),
-+               grpptr->num_channel_paths, 
-+               grpptr->active_channels[READ],
-+               grpptr->active_channels[WRITE]);
-+#endif
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
-+               __FUNCTION__,ch->devno);
-+#endif
-+
-+        return(rc);
-+
-+}
-+
-+/**
-+ * The minimum required active read or write channels 
-+ * are no longer available.  Game over.
-+ */
-+
-+/**
-+ * The MPC Group Station FSM
-+ *   22 events
-+ */
-+static const fsm_node mpcg_fsm[] = {
-+        {  MPCG_STATE_RESET,     MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
-+        {  MPCG_STATE_INOP,      MPCG_EVENT_INOP,     fsm_action_nop},
-+
-+        {  MPCG_STATE_FLOWC,     MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
-+
-+        {  MPCG_STATE_READY,     MPCG_EVENT_DISCONC,  ctcmpc_action_discontact},
-+        {  MPCG_STATE_READY,     MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
-+
-+        {  MPCG_STATE_XID2INITW, MPCG_EVENT_XID0DO,   ctcmpc_action_doxid0},
-+        {  MPCG_STATE_XID2INITW, MPCG_EVENT_XID2,     ctcmpc_action_rcvd_xid0},
-+        {  MPCG_STATE_XID2INITW, MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
-+        {  MPCG_STATE_XID2INITW, MPCG_EVENT_TIMER,    ctcmpc_action_timeout},
-+        {  MPCG_STATE_XID2INITW, MPCG_EVENT_DOIO,     ctcmpc_action_yside_xid},
-+
-+        {  MPCG_STATE_XID2INITX, MPCG_EVENT_XID0DO,   ctcmpc_action_doxid0},
-+        {  MPCG_STATE_XID2INITX, MPCG_EVENT_XID2,     ctcmpc_action_rcvd_xid0},
-+        {  MPCG_STATE_XID2INITX, MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
-+        {  MPCG_STATE_XID2INITX, MPCG_EVENT_TIMER,    ctcmpc_action_timeout},
-+        {  MPCG_STATE_XID2INITX, MPCG_EVENT_DOIO,     ctcmpc_action_yside_xid},
-+
-+        {  MPCG_STATE_XID7INITW, MPCG_EVENT_XID2DONE, ctcmpc_action_doxid7},
-+        {  MPCG_STATE_XID7INITW, MPCG_EVENT_DISCONC,  ctcmpc_action_discontact},
-+        {  MPCG_STATE_XID7INITW, MPCG_EVENT_XID2,     ctcmpc_action_rcvd_xid7},
-+        {  MPCG_STATE_XID7INITW, MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
-+        {  MPCG_STATE_XID7INITW, MPCG_EVENT_TIMER,    ctcmpc_action_timeout},
-+        {  MPCG_STATE_XID7INITW, MPCG_EVENT_XID7DONE, ctcmpc_action_doxid7},
-+        {  MPCG_STATE_XID7INITW, MPCG_EVENT_DOIO,     ctcmpc_action_yside_xid},
-+
-+        {  MPCG_STATE_XID7INITX, MPCG_EVENT_DISCONC,  ctcmpc_action_discontact},
-+        {  MPCG_STATE_XID7INITX, MPCG_EVENT_XID2,     ctcmpc_action_rcvd_xid7},
-+        {  MPCG_STATE_XID7INITX, MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
-+        {  MPCG_STATE_XID7INITX, MPCG_EVENT_XID7DONE, ctcmpc_action_doxid7},
-+        {  MPCG_STATE_XID7INITX, MPCG_EVENT_TIMER,    ctcmpc_action_timeout},
-+        {  MPCG_STATE_XID7INITX, MPCG_EVENT_DOIO,     ctcmpc_action_yside_xid},
-+
-+        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID0DO,  ctcmpc_action_doxid0},
-+        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DISCONC, ctcmpc_action_discontact},
-+        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID2,    ctcmpc_action_rcvd_xid0},
-+        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_INOP,    ctcmpc_action_go_inop},
-+        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_TIMER,   ctcmpc_action_timeout},
-+        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DOIO,    ctcmpc_action_xside_xid},
-+
-+        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID0DO,  ctcmpc_action_doxid0},
-+        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DISCONC, ctcmpc_action_discontact},
-+        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID2,    ctcmpc_action_rcvd_xid0},
-+        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_INOP,    ctcmpc_action_go_inop},
-+        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_TIMER,   ctcmpc_action_timeout},
-+        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DOIO,    ctcmpc_action_xside_xid},
-+
-+        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_XID2DONE,ctcmpc_action_doxid7},
-+        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_XID2,    ctcmpc_action_rcvd_xid7},
-+        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_DISCONC, ctcmpc_action_discontact},
-+        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_INOP,    ctcmpc_action_go_inop},
-+        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_TIMER,   ctcmpc_action_timeout},
-+        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_XID7DONE,ctcmpc_action_doxid7},
-+        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_DOIO,    ctcmpc_action_xside_xid},
-+
-+        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_XID2,    ctcmpc_action_rcvd_xid7},
-+        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_XID7DONE,ctcmpc_action_doxid7},
-+        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_DISCONC, ctcmpc_action_discontact},
-+        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_INOP,    ctcmpc_action_go_inop},
-+        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_TIMER,   ctcmpc_action_timeout},
-+        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_DOIO,    ctcmpc_action_xside_xid},
-+
-+        {  MPCG_STATE_XID7INITF,  MPCG_EVENT_INOP,    ctcmpc_action_go_inop},
-+        {  MPCG_STATE_XID7INITF,  MPCG_EVENT_XID7DONE,ctcmpc_action_go_ready},
-+
-+};
-+
-+static const int MPCG_FSM_LEN = sizeof(mpcg_fsm) / sizeof(fsm_node);
-+
-+
-+/**
-+ * Unpack a just received skb and hand it over to
-+ * upper layers.
-+ *
-+ * @param ch The channel where this skb has been received.
-+ * @param pskb The received packed skb.
-+ */
-+static __inline__ void 
-+ctcmpc_unpack_skb(channel *ch, struct sk_buff *pskb)
-+{
-+        net_device *dev  = ch->netdev;
-+        ctc_priv *privptr = (ctc_priv *)dev->priv;
-+        mpc_group *grpptr = privptr->mpcg;
-+        pdu *curr_pdu;
-+        mpcg_info *mpcginfo;
-+        int pdu_last_seen = 0;
-+        __u32 new_len;
-+        struct sk_buff *skb;
-+        int sendrc = 0;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s cp:%i enter:%s() %04x\n", 
-+               dev->name,smp_processor_id(),__FUNCTION__,ch->devno);
-+#endif
-+
-+        th_header *header = (th_header *)pskb->data;
-+        if((header->th_seg == 0) && 
-+           (header->th_ch_flag == 0) &&
-+           (header->th_blk_flag == 0) &&
-+           (header->th_seq_num == 0))
-+                goto done;  /* nothing for us */
-+
-+#ifdef DEBUGDATA
-+        printk(KERN_INFO "%s(): th_header\n", __FUNCTION__);
-+        dumpit((char *)header,TH_HEADER_LENGTH);
-+        printk(KERN_INFO "%s(): pskb len: %04x \n", __FUNCTION__,pskb->len);
-+#endif
-+
-+        pskb->dev = dev;        
-+        pskb->ip_summed = CHECKSUM_UNNECESSARY;
-+        spin_lock(&ch->segment_lock);  /* make sure we are alone here */
-+
-+        skb_pull(pskb,TH_HEADER_LENGTH);
-+
-+        if(likely(header->th_ch_flag == TH_HAS_PDU))
-+        {
-+//               #ifdef DEBUGDATA
-+//                        printk(KERN_INFO "%s(): came into th_has_pdu\n", 
-+//                        __FUNCTION__);
-+//               #endif
-+
-+
-+                if((fsm_getstate(grpptr->fsm) == MPCG_STATE_FLOWC) ||
-+                   ((fsm_getstate(grpptr->fsm) == MPCG_STATE_READY) &&
-+                    (header->th_seq_num != ch->th_seq_num + 1) &&
-+                    (ch->th_seq_num != 0)))
-+                {
-+                        /* This isn't the next segment          *
-+                        * we are not the correct race winner   *  	
-+                        * go away and let someone else win     *
-+                        * BUT..this only applies if xid negot  *
-+                        * is done                              *
-+                       */
-+                        grpptr->out_of_sequence +=1;
-+                        __skb_push(pskb,TH_HEADER_LENGTH);
-+                        spin_unlock(&ch->segment_lock);
-+                        skb_queue_tail(&ch->io_queue, pskb);
-+#ifdef DEBUGSEQ
-+                        printk(KERN_INFO "%s() th_seq_num expect:%08x "
-+                               "got:%08x\n",
-+                               __FUNCTION__,
-+                               ch->th_seq_num + 1,
-+                               header->th_seq_num);
-+#endif
-+                        return;
-+                }
-+                grpptr->out_of_sequence = 0;
-+                ch->th_seq_num = header->th_seq_num;
-+
-+#ifdef DEBUGSEQ
-+                printk(KERN_INFO "%s: FromVTAM_th_seq= %08x\n" ,
-+                       __FUNCTION__,ch->th_seq_num);
-+#endif
-+                pdu_last_seen = 0;
-+                if(fsm_getstate(grpptr->fsm) == MPCG_STATE_READY)
-+                        while((pskb->len > 0) && !pdu_last_seen)
-+                        {
-+                                curr_pdu = (pdu *)pskb->data;
-+#ifdef DEBUGDATA
-+                                printk(KERN_INFO "%s(): pdu_header\n", 
-+                                       __FUNCTION__);
-+                                dumpit((char *)pskb->data,PDU_HEADER_LENGTH);
-+                                printk(KERN_INFO "%s(): pskb len: %04x \n", 
-+                                       __FUNCTION__,pskb->len);
-+#endif
-+                                skb_pull(pskb,PDU_HEADER_LENGTH);
-+                                if(curr_pdu->pdu_flag & PDU_LAST)
-+                                        pdu_last_seen = 1;
-+                                if(curr_pdu->pdu_flag & PDU_CNTL)
-+                                        pskb->protocol = htons(ETH_P_SNAP);
-+                                else
-+                                        pskb->protocol = htons(ETH_P_SNA_DIX);
-+                                if((pskb->len <= 0) ||
-+                                   (pskb->len > ch->max_bufsize))
-+                                {
-+                                        printk(KERN_INFO
-+                                               "%s Illegal packet size %d "
-+                                               "received "
-+                                               "dropping\n", dev->name, 
-+                                               pskb->len);
-+                                        privptr->stats.rx_dropped++;
-+                                        privptr->stats.rx_length_errors++;
-+                                        spin_unlock(&ch->segment_lock);
-+                                        goto done;
-+                                }
-+                                pskb->mac.raw = pskb->data;
-+                                new_len = curr_pdu->pdu_offset;
-+//			  #ifdef DEBUGDATA
-+//				printk(KERN_INFO "%s(): new_len: %04x \n", 
-+//                                __FUNCTION__,new_len);
-+//			  #endif
-+                                if((new_len == 0) ||
-+                                   (new_len > pskb->len))
-+                                {
-+                                        /* should never happen              */
-+                                        /* pskb len must be hosed...bail out */
-+                                        printk(KERN_INFO 
-+                                               "%s(): invalid pdu offset "
-+                                               "of %04x - data may be lost\n", 
-+                                               __FUNCTION__,new_len);
-+                                        spin_unlock(&ch->segment_lock);
-+                                        goto done;
-+                                }
-+                                skb = __dev_alloc_skb(new_len+4,GFP_ATOMIC);
-+
-+                                if(!skb)
-+                                {
-+                                        printk(KERN_INFO
-+                                               "%s Out of memory in %s- "
-+                                               "request-len:%04x \n",
-+                                               dev->name,
-+                                               __FUNCTION__,
-+                                               new_len+4);                                                                              
-+                                        privptr->stats.rx_dropped++;
-+                                        spin_unlock(&ch->segment_lock);
-+                                        fsm_event(grpptr->fsm,
-+                                                  MPCG_EVENT_INOP,
-+                                                  dev);
-+                                        goto done;
-+                                }
-+
-+                                memcpy(skb_put(skb, new_len), 
-+                                       pskb->data, 
-+                                       new_len);
-+
-+                                skb->mac.raw = skb->data;
-+                                skb->dev = pskb->dev;
-+                                skb->protocol = pskb->protocol;
-+                                skb->ip_summed = CHECKSUM_UNNECESSARY;
-+                                *((__u32 *) skb_push(skb, 4)) = ch->pdu_seq;  
-+                                ch->pdu_seq++;
-+
-+#ifdef DEBUGSEQ
-+                                printk(KERN_INFO "%s: ToDCM_pdu_seq= %08x\n" ,
-+                                       __FUNCTION__,ch->pdu_seq);
-+#endif
-+
-+#ifdef DEBUGDATA
-+                                __u32 out_len;
-+                                if(skb->len > 32) out_len = 32;
-+                                else out_len = skb->len;
-+                                printk(KERN_INFO "%s(): skb:%0lx skb len:%d \n",
-+                                        __FUNCTION__,
-+                                       (unsigned long)skb,
-+                                       skb->len);
-+                                printk(KERN_INFO "%s(): up to 32 bytes "
-+                                       "of pdu_data sent\n", 
-+                                       __FUNCTION__);
-+                                dumpit((char *)skb->data,out_len);
-+#endif
-+
-+                                sendrc = netif_rx(skb);
-+                                privptr->stats.rx_packets++;
-+                                privptr->stats.rx_bytes += skb->len;
-+                                skb_pull(pskb, new_len); /* point to next PDU */
-+                        }
-+        } else
-+        {
-+                if((mpcginfo = (mpcg_info *)kmalloc(sizeof(mpcg_info), 
-+                                                    gfp_type())) == NULL)
-+                {
-+                        spin_unlock(&ch->segment_lock);
-+                        goto done;
-+                }
-+
-+                mpcginfo->ch = ch;
-+                mpcginfo->th = header;
-+                mpcginfo->skb = pskb;
-+
-+#ifdef DEBUG
-+                printk(KERN_INFO "%s(): It's not PDU it may be control pkt\n", 
-+                       __FUNCTION__);
-+#endif
-+                /*  it's a sweep?   */
-+                th_sweep *sweep = (th_sweep *) pskb->data;
-+                mpcginfo->sweep = sweep;
-+                if(header->th_ch_flag == TH_SWEEP_REQ)
-+                        ctcmpc_rcvd_sweep_req(mpcginfo);
-+                else
-+                        if(header->th_ch_flag == TH_SWEEP_RESP)
-+                        ctcmpc_rcvd_sweep_resp(mpcginfo);
-+                else
-+                {
-+                        if(header->th_blk_flag == TH_DATA_IS_XID)
-+                        {
-+                                xid2 *thisxid = (xid2 *)pskb->data;
-+                                skb_pull(pskb,XID2_LENGTH);                                               
-+                                mpcginfo->xid = thisxid;
-+                                fsm_event(grpptr->fsm,MPCG_EVENT_XID2,mpcginfo);
-+                        } else
-+                        {
-+                                if(header->th_blk_flag == TH_DISCONTACT)
-+                                {
-+                                        fsm_event(grpptr->fsm,
-+                                                  MPCG_EVENT_DISCONC,mpcginfo);
-+                                } else
-+                                        if(header->th_seq_num != 0)
-+                                {
-+                                        printk(KERN_INFO
-+                                               "%s unexpected packet expected"
-+                                               " control pkt\n",
-+                                               dev->name);
-+                                        privptr->stats.rx_dropped++;
-+#ifdef DEBUGDATA
-+                                        ctcmpc_dump_skb(pskb, -8);
-+#endif
-+                                        kfree(mpcginfo);
-+                                }
-+                        }
-+                }
-+        }
-+        spin_unlock(&ch->segment_lock);
-+        done:
-+
-+        dev_kfree_skb_any(pskb);  
-+        switch(sendrc)
-+        {
-+                case NET_RX_DROP:
-+                        printk(KERN_WARNING "%s %s() NETWORK BACKLOG "
-+                               "EXCEEDED - PACKET DROPPED\n",
-+                               dev->name,
-+                               __FUNCTION__);
-+                        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+                        break;
-+                case NET_RX_SUCCESS:
-+                case NET_RX_CN_LOW:
-+                case NET_RX_CN_MOD:
-+                case NET_RX_CN_HIGH:
-+                default:
-+                        break;
-+        }
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s() %04x\n", 
-+               dev->name,__FUNCTION__,ch->devno);
-+#endif
-+
-+}
-+
-+
-+
-+/**
-+ * Bottom half routine.
-+ *
-+ * @param ch The channel to work on.
-+ * Allow flow control back pressure to occur here.
-+ * Throttling back channel can result in excessive
-+ * channel inactivity and system deact of channel
-+ */
-+static void 
-+ctcmpc_bh(unsigned long thischan)
-+{
-+        channel           *ch         = (channel *)thischan;
-+        struct sk_buff    *peek_skb   = NULL;
-+        struct sk_buff    *skb;
-+        struct sk_buff    *same_skb   = NULL;
-+        net_device        *dev        = ch->netdev;
-+        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group         *grpptr     = privptr->mpcg;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s cp:%i enter:  %s()  %04x\n", 
-+               dev->name,smp_processor_id(),__FUNCTION__,ch->devno);
-+#endif
-+        /* caller has requested driver to throttle back */
-+        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_FLOWC)
-+        {
-+                goto done;
-+        } else
-+        {
-+                while((skb = skb_dequeue(&ch->io_queue)))
-+                {
-+                        same_skb = skb;
-+                        ctcmpc_unpack_skb(ch, skb);
-+                        if(grpptr->out_of_sequence > 20)
-+                        {
-+                                /* assume data loss has occurred if */
-+                                /* missing seq_num for extended     */
-+                                /* period of time                   */
-+                                grpptr->out_of_sequence = 0;
-+                                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+                                goto done;
-+                        }
-+                        peek_skb = skb_peek(&ch->io_queue);
-+                        if(peek_skb == same_skb)
-+                                goto done;
-+                        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_FLOWC)
-+                                goto done;
-+                }
-+        }
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()  %04x\n", 
-+               dev->name,__FUNCTION__,ch->devno);
-+#endif
-+
-+        return;
-+}
-+
-+/**
-+ * Check return code of a preceeding do_IO, halt_IO etc...
-+ *
-+ * @param ch          The channel, the error belongs to.
-+ * @param return_code The error code to inspect.
-+ */
-+static void inline 
-+ccw_check_return_code (channel *ch, int return_code)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter: cp:%i %s()\n", 
-+               smp_processor_id(),__FUNCTION__);
-+#endif
-+
-+
-+        switch(return_code)
-+        {
-+                case 0:
-+                        fsm_event(ch->fsm, CH_EVENT_IO_SUCCESS, ch);
-+                        break;
-+                case -EBUSY:
-+                        printk(KERN_INFO "ch-%04x: Busy !\n", ch->devno);
-+                        fsm_event(ch->fsm, CH_EVENT_IO_EBUSY, ch);
-+                        break;
-+                case -ENODEV:
-+                        printk(KERN_EMERG
-+                               "ch-%04x: Invalid device called for IO\n",
-+                               ch->devno);
-+                        fsm_event(ch->fsm, CH_EVENT_IO_ENODEV, ch);
-+                        break;
-+                case -EIO:
-+                        printk(KERN_EMERG
-+                               "ch-%04x: Status pending... \n", ch->devno);
-+                        fsm_event(ch->fsm, CH_EVENT_IO_EIO, ch);
-+                        break;
-+                default:
-+                        printk(KERN_EMERG
-+                               "ch-%04x: Unknown error in do_IO %04x\n",
-+                               ch->devno, return_code);
-+                        fsm_event(ch->fsm, CH_EVENT_IO_UNKNOWN, ch);
-+        }
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+
-+}
-+
-+/**
-+ * Check sense of a unit check.
-+ *
-+ * @param ch    The channel, the sense code belongs to.
-+ * @param sense The sense code to inspect.
-+ */
-+static void inline 
-+ccw_unit_check (channel *ch, unsigned char sense)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        if(sense & SNS0_INTERVENTION_REQ)
-+        {
-+                if(sense & 0x01)
-+                {
-+                        printk(KERN_INFO
-+                               "ch-%04x: Interface disc. or Sel. reset "
-+                               "(remote)\n", ch->devno);
-+                        fsm_event(ch->fsm, CH_EVENT_UC_RCRESET, ch);
-+                } else
-+                {
-+                        printk(KERN_INFO "ch-%04x: System reset (remote)\n",
-+                               ch->devno);
-+                        fsm_event(ch->fsm, CH_EVENT_UC_RSRESET, ch);
-+                }
-+        } else if(sense & SNS0_EQUIPMENT_CHECK)
-+        {
-+                if(sense & SNS0_BUS_OUT_CHECK)
-+                {
-+                        printk(KERN_INFO
-+                               "ch-%04x: Hardware malfunction (remote)\n",
-+                               ch->devno);
-+                        fsm_event(ch->fsm, CH_EVENT_UC_HWFAIL, ch);
-+                } else
-+                {
-+                        printk(KERN_INFO
-+                               "ch-%04x: Read-data parity error (remote)\n",
-+                               ch->devno);
-+                        fsm_event(ch->fsm, CH_EVENT_UC_RXPARITY, ch);
-+                }
-+        } else if(sense & SNS0_BUS_OUT_CHECK)
-+        {
-+                if(sense & 0x04)
-+                {
-+                        printk(KERN_INFO
-+                               "ch-%04x: Data-streaming timeout)\n",
-+                               ch->devno);
-+                        fsm_event(ch->fsm, CH_EVENT_UC_TXTIMEOUT, ch);
-+                } else
-+                {
-+                        printk(KERN_INFO
-+                               "ch-%04x: Data-transfer parity error\n",
-+                               ch->devno);
-+                        fsm_event(ch->fsm, CH_EVENT_UC_TXPARITY, ch);
-+                }
-+        } else if(sense & SNS0_CMD_REJECT)
-+        {
-+                printk(KERN_INFO "ch-%04x: Command reject\n",
-+                       ch->devno);
-+        } else if(sense == 0)
-+        {
-+                printk(KERN_INFO "ch-%04x: Unit check ZERO\n", ch->devno);
-+                fsm_event(ch->fsm, CH_EVENT_UC_ZERO, ch);
-+        } else
-+        {
-+                printk(KERN_INFO
-+                       "ch-%04x: Unit Check with sense code: %02x\n",
-+                       ch->devno, sense);
-+                fsm_event(ch->fsm, CH_EVENT_UC_UNKNOWN, ch);
-+        }
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+}
-+
-+static void 
-+ctcmpc_purge_skb_queue(struct sk_buff_head *q)
-+{
-+        struct sk_buff *skb;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        while((skb = skb_dequeue(q)))
-+        {
-+                atomic_dec(&skb->users);
-+
-+                dev_kfree_skb_any(skb);
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+static __inline__ int 
-+ctcmpc_checkalloc_buffer(channel *ch, int warn)
-+{
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        if((ch->trans_skb == NULL) ||
-+           (ch->flags & CHANNEL_FLAGS_BUFSIZE_CHANGED))
-+        {
-+                if(ch->trans_skb != NULL)
-+                        dev_kfree_skb_any(ch->trans_skb);
-+                clear_normalized_cda(&ch->ccw[1]);
-+                ch->trans_skb = __dev_alloc_skb(ch->max_bufsize,
-+                                                GFP_ATOMIC|GFP_DMA);
-+                if(ch->trans_skb == NULL)
-+                {
-+                        if(warn)
-+                                printk(KERN_INFO
-+                                       "ch-%04x: Couldn't alloc %s trans_skb\n",
-+                                       ch->devno,
-+                                       (CHANNEL_DIRECTION(ch->flags) == READ) ?
-+                                       "RX" : "TX");
-+                        return -ENOMEM;
-+                }
-+                ch->ccw[1].count = ch->max_bufsize;
-+
-+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21))
-+                if(set_normalized_cda(&ch->ccw[1],
-+                                      virt_to_phys(ch->trans_skb->data)))
-+                {
-+#else 
-+                if(set_normalized_cda(&ch->ccw[1],ch->trans_skb->data))
-+                {
-+#endif
-+                        dev_kfree_skb_any(ch->trans_skb);
-+                        ch->trans_skb = NULL;
-+                        if(warn)
-+                                printk(KERN_INFO
-+                                       "ch-%04x: set_normalized_cda for %s "
-+                                       "trans_skb failed, dropping packets\n",
-+                                       ch->devno,
-+                                       (CHANNEL_DIRECTION(ch->flags) == READ) ?
-+                                       "RX" : "TX");
-+                        return -ENOMEM;
-+                }
-+                ch->ccw[1].count = 0;
-+                ch->trans_skb_data = ch->trans_skb->data;
-+                ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED;
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return 0;
-+}
-+
-+/**
-+ * Actions for channel - statemachines.
-+ *****************************************************************************/
-+
-+/**
-+ * Normal data has been sent. Free the corresponding
-+ * skb (it's in io_queue), reset dev->tbusy and
-+ * revert to idle state.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_txdone(fsm_instance *fi, int event, void *arg)
-+{
-+        channel        *ch = (channel *)arg;
-+        net_device     *dev = ch->netdev;
-+        ctc_priv       *privptr = (ctc_priv *)dev->priv;
-+        mpc_group      *grpptr  = privptr->mpcg;
-+        struct sk_buff *skb;
-+        int            first = 1;
-+        int            i;
-+#ifdef DEBUGDATA
-+        __u32                out_len = 0;
-+#endif
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s cp:%i enter:  %s()\n", 
-+               dev->name,smp_processor_id(),__FUNCTION__);
-+#endif
-+
-+
-+        struct timeval done_stamp = xtime;
-+        unsigned long duration = 
-+        (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
-+        done_stamp.tv_usec - ch->prof.send_stamp.tv_usec;
-+        if(duration > ch->prof.tx_time)
-+                ch->prof.tx_time = duration;
-+
-+        if(ch->devstat->rescnt != 0)
-+                printk(KERN_INFO "%s: TX not complete, remaining %d bytes\n",
-+                       dev->name, ch->devstat->rescnt);
-+
-+        fsm_deltimer(&ch->timer);
-+        while((skb = skb_dequeue(&ch->io_queue)))
-+        {
-+                privptr->stats.tx_packets++;
-+                privptr->stats.tx_bytes += skb->len - TH_HEADER_LENGTH;
-+                if(first)
-+                {
-+                        privptr->stats.tx_bytes += 2;
-+                        first = 0;
-+                }
-+                atomic_dec(&skb->users);
-+                dev_kfree_skb_irq(skb);
-+        }
-+        spin_lock(&ch->collect_lock);
-+        clear_normalized_cda(&ch->ccw[4]);
-+        if((ch->collect_len > 0) && (grpptr->in_sweep == 0))
-+        {
-+                int rc;
-+                th_header       *header;
-+                pdu     *p_header = NULL;
-+
-+                if(ctcmpc_checkalloc_buffer(ch, 1))
-+                {
-+                        spin_unlock(&ch->collect_lock);
-+                        goto done;
-+                }
-+                ch->trans_skb->tail = ch->trans_skb->data = ch->trans_skb_data;
-+                ch->trans_skb->len = 0;
-+                if(ch->prof.maxmulti < (ch->collect_len + TH_HEADER_LENGTH))
-+                        ch->prof.maxmulti = ch->collect_len + TH_HEADER_LENGTH;
-+                if(ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
-+                        ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
-+                i = 0;
-+#ifdef DEBUGDATA
-+                printk(KERN_INFO "%s(): building trans_skb from collect_q \n", 
-+                       __FUNCTION__);
-+#endif
-+
-+                __u32 data_space = grpptr->group_max_buflen - TH_HEADER_LENGTH;
-+
-+#ifdef DEBUGDATA
-+                printk(KERN_INFO "%s(): building trans_skb from "
-+                       "collect_q data_space:%04x\n", 
-+                       __FUNCTION__,data_space);
-+#endif
-+
-+
-+                while((skb = skb_dequeue(&ch->collect_queue)))
-+                {
-+                        memcpy(skb_put(ch->trans_skb, skb->len), skb->data,
-+                               skb->len);
-+                        p_header = (pdu *)(ch->trans_skb->tail - skb->len);  
-+                        p_header->pdu_flag = 0x00;
-+                        if(skb->protocol == ntohs(ETH_P_SNAP))
-+                        {
-+                                p_header->pdu_flag |= 0x60;
-+                        } else
-+                        {
-+                                p_header->pdu_flag |= 0x20;
-+                        }
-+#ifdef DEBUGDATA
-+                        __u32            out_len = 0;
-+                        printk(KERN_INFO "%s():trans_skb len:%04x \n", 
-+                               __FUNCTION__,ch->trans_skb->len);
-+                        if(skb->len > 32) out_len = 32;
-+                        else out_len = skb->len;
-+                        printk(KERN_INFO "%s(): pdu header and data for "
-+                               "up to 32 bytes sent to vtam\n", 
-+                               __FUNCTION__);
-+                        dumpit((char *)p_header,out_len);
-+#endif
-+                        ch->collect_len -= skb->len;
-+                        data_space -= skb->len;
-+                        privptr->stats.tx_packets++;
-+                        privptr->stats.tx_bytes += skb->len;
-+                        atomic_dec(&skb->users);
-+                        dev_kfree_skb_any(skb);
-+                        sk_buff *peekskb = skb_peek(&ch->collect_queue);
-+                        if(peekskb->len > data_space)
-+                                break;
-+                        i++;
-+                }
-+                /* p_header points to the last one we handled */
-+                if(p_header)
-+                        p_header->pdu_flag |= PDU_LAST; 
-+                header = (th_header *)kmalloc(TH_HEADER_LENGTH, gfp_type());
-+
-+                if(!header)
-+                {
-+                        printk(KERN_WARNING ": OUT OF MEMORY IN %s(): "
-+                               "Data Lost \n",
-+                               __FUNCTION__);
-+                        spin_unlock(&ch->collect_lock);
-+                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
-+                        goto done;
-+                }
-+                header->th_seg = 0x00;
-+                header->th_ch_flag = TH_HAS_PDU;  /* Normal data */
-+                header->th_blk_flag = 0x00;
-+                header->th_is_xid = 0x00; 
-+                ch->th_seq_num++;
-+                header->th_seq_num = ch->th_seq_num;
-+
-+#ifdef DEBUGSEQ
-+                printk(KERN_INFO "%s: ToVTAM_th_seq= %08x\n" ,
-+                       __FUNCTION__,ch->th_seq_num);
-+#endif
-+                memcpy(skb_push(ch->trans_skb, TH_HEADER_LENGTH), header,
-+                       TH_HEADER_LENGTH);       /*  put the TH on the packet */
-+
-+                kfree(header);
-+
-+#ifdef DEBUGDATA
-+                printk(KERN_INFO "%s():trans_skb len:%04x \n", 
-+                       __FUNCTION__,ch->trans_skb->len);
-+                if(ch->trans_skb->len > 50) out_len = 50;
-+                else out_len = ch->trans_skb->len;
-+                printk(KERN_INFO "%s(): up-to-50 bytes of trans_skb "
-+                       "data to vtam from collect_q\n", 
-+                       __FUNCTION__);
-+                dumpit((char *)ch->trans_skb->data,out_len);
-+#endif
-+
-+                spin_unlock(&ch->collect_lock);
-+                clear_normalized_cda(&ch->ccw[1]);
-+
-+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21))
-+                if(set_normalized_cda(&ch->ccw[1],
-+                                      virt_to_phys(ch->trans_skb->data)))
-+                {
-+#else
-+                if(set_normalized_cda(&ch->ccw[1],ch->trans_skb->data))
-+                {
-+#endif
-+                        dev_kfree_skb_any(ch->trans_skb);
-+                        ch->trans_skb = NULL;
-+                        printk(KERN_WARNING "%s():CCW failure - data lost\n", 
-+                               __FUNCTION__);
-+                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
-+                        return;
-+                }
-+                ch->ccw[1].count = ch->trans_skb->len;
-+                fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
-+                ch->prof.send_stamp = xtime;
-+
-+#ifdef DEBUGCCW
-+                dumpit((char *)&ch->ccw[0],sizeof(ccw1_t) * 3);
-+#endif
-+
-+                rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
-+                ch->prof.doios_multi++;
-+                if(rc != 0)
-+                {
-+                        ccw_check_return_code(ch, rc);
-+                }
-+        } else
-+        {
-+                spin_unlock(&ch->collect_lock);
-+                fsm_newstate(fi, CH_STATE_TXIDLE);
-+        }
-+
-+        done:
-+        ctcmpc_clear_busy(dev);
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+        return;
-+
-+}
-+
-+/**
-+ * Initial data is sent.
-+ * Notify device statemachine that we are up and
-+ * running.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_txidle(fsm_instance *fi, int event, void *arg)
-+{
-+        channel *ch = (channel *)arg;
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+        fsm_deltimer(&ch->timer);
-+        fsm_newstate(fi, CH_STATE_TXIDLE);
-+        fsm_event(((ctc_priv *)ch->netdev->priv)->fsm, DEV_EVENT_TXUP,
-+                  ch->netdev);
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Got normal data, check for sanity, queue it up, allocate new buffer
-+ * trigger bottom half, and initiate next read.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_rx(fsm_instance *fi, int event, void *arg)
-+{
-+        channel           *ch         = (channel *)arg;
-+        net_device        *dev        = ch->netdev;
-+        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group         *grpptr     = privptr->mpcg;
-+        __u32             len         = ch->max_bufsize - ch->devstat->rescnt;
-+        struct sk_buff    *skb        = ch->trans_skb;
-+        struct sk_buff    *new_skb;
-+        int               rc          = 0;
-+        __u32               block_len;
-+        unsigned long     saveflags;
-+        int               gotlock     = 0;
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s cp:%i enter:%s() %04x\n", 
-+               dev->name,smp_processor_id(),__FUNCTION__,ch->devno);
-+#endif
-+#ifdef DEBUGDATA
-+        printk(KERN_INFO "ctcmpc:%s() max_bufsize:%04x rescnt:%04x len:%04x\n", 
-+               __FUNCTION__,ch->max_bufsize,ch->devstat->rescnt,len);
-+#endif
-+
-+        fsm_deltimer(&ch->timer);
-+
-+        if(skb == NULL)
-+        {
-+#ifdef DEBUG
-+                printk(KERN_INFO "ctcmpc exit:  %s() TRANS_SKB = NULL \n", 
-+                       __FUNCTION__);
-+#endif	
-+                goto again;
-+        }
-+
-+        if(len < TH_HEADER_LENGTH)
-+        {
-+                printk(KERN_INFO "%s: got packet with invalid length %d\n",
-+                       dev->name, len);
-+                privptr->stats.rx_dropped++;
-+                privptr->stats.rx_length_errors++;
-+                goto again;
-+        } else
-+        {
-+                /* must have valid th header or game over */
-+                block_len = len;
-+                len = TH_HEADER_LENGTH + XID2_LENGTH + 4;
-+                new_skb = __dev_alloc_skb(ch->max_bufsize,GFP_ATOMIC);
-+
-+                if(new_skb == NULL)
-+                {
-+                        printk(KERN_INFO "ctcmpc exit:%s() NEW_SKB = NULL \n", 
-+                               __FUNCTION__);
-+                        printk(KERN_WARNING "%s() MEMORY ALLOC FAILED - "
-+                               "DATA LOST - MPC FAILED\n", 
-+                               __FUNCTION__);
-+                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
-+                        goto again;
-+                }
-+                switch(fsm_getstate(grpptr->fsm))
-+                {
-+                        case MPCG_STATE_RESET:
-+                        case MPCG_STATE_INOP:
-+                                goto again;
-+                        case MPCG_STATE_FLOWC:
-+                        case MPCG_STATE_READY:
-+                                memcpy(skb_put(new_skb, block_len), 
-+                                       skb->data,
-+                                       block_len);
-+                                skb_queue_tail(&ch->io_queue, new_skb);
-+                                tasklet_schedule(&ch->ch_tasklet); 
-+                                goto again;
-+                        default:
-+                                memcpy(skb_put(new_skb, len), skb->data,len);
-+                                skb_queue_tail(&ch->io_queue, new_skb);
-+                                tasklet_hi_schedule(&ch->ch_tasklet); 
-+                                goto again;
-+                }
-+
-+        }
-+
-+        again:
-+        switch(fsm_getstate(grpptr->fsm))
-+        {
-+                case MPCG_STATE_FLOWC:
-+                case MPCG_STATE_READY:
-+                        if(ctcmpc_checkalloc_buffer(ch, 1))
-+                                break;
-+                        ch->trans_skb->data = 
-+                                ch->trans_skb->tail = 
-+                                ch->trans_skb_data;
-+                        ch->trans_skb->len = 0;
-+                        ch->ccw[1].count = ch->max_bufsize;
-+#ifdef DEBUGCCW
-+                        dumpit((char *)&ch->ccw[0],sizeof(ccw1_t) * 3);
-+#endif
-+                        if(!in_irq())
-+                        {
-+                                s390irq_spin_lock_irqsave(ch->irq,saveflags);
-+                                gotlock = 1;
-+                        }
-+                        rc = do_IO(ch->irq, &ch->ccw[0], 
-+                                   (intparm_t)ch, 0xff, 0);
-+                        if(gotlock)
-+                                s390irq_spin_unlock_irqrestore(ch->irq,
-+                                                               saveflags);
-+                        if(rc != 0)
-+                                ccw_check_return_code(ch, rc);
-+                        break;
-+                default: 
-+                        break;
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()  %04x\n", 
-+               dev->name,__FUNCTION__,ch->devno);
-+#endif
-+
-+}
-+
-+/**
-+ * Initialize connection by sending a __u16 of value 0.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_firstio(fsm_instance *fi, int event, void *arg)
-+{
-+        channel *ch = (channel *)arg;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", __FUNCTION__,ch->devno);
-+#endif
-+
-+#ifdef DEBUG
-+        net_device     *dev = ch->netdev;
-+        mpc_group          *grpptr = ((ctc_priv *)dev->priv)->mpcg;
-+        printk(KERN_INFO "%s() %04x chstate:%i grpstate:%i chprotocol:%i\n", 
-+               __FUNCTION__, ch->devno,
-+               fsm_getstate(fi),
-+               fsm_getstate(grpptr->fsm),
-+               ch->protocol);
-+#endif
-+
-+        if(fsm_getstate(fi) == CH_STATE_TXIDLE)
-+                printk(KERN_INFO "ch-%04x: remote side issued READ?, "
-+                       "init ...\n", ch->devno);
-+        fsm_deltimer(&ch->timer);
-+        if(ctcmpc_checkalloc_buffer(ch, 1))
-+        {
-+                goto done;
-+        }
-+
-+        if(ch->protocol == CTC_PROTO_MPC)
-+                switch(fsm_getstate(fi))
-+                {
-+                        case CH_STATE_STARTRETRY:
-+                        case CH_STATE_SETUPWAIT:
-+                                if(CHANNEL_DIRECTION(ch->flags) == READ)
-+                                {
-+                                        ctcmpc_ch_action_rxidle(fi, event, arg);
-+                                } else
-+                                {
-+                                        net_device *dev = ch->netdev;
-+                                        fsm_newstate(fi, CH_STATE_TXIDLE);
-+                                        fsm_event(((ctc_priv *)dev->priv)->fsm,
-+                                                  DEV_EVENT_TXUP, dev);
-+                                }
-+                                goto done;
-+                        default: 
-+                                break;
-+
-+                };
-+
-+        fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ)
-+                     ? CH_STATE_RXINIT : CH_STATE_TXINIT);
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", __FUNCTION__,ch->devno);
-+#endif
-+        return;
-+}
-+
-+/**
-+ * Got initial data, check it. If OK,
-+ * notify device statemachine that we are up and
-+ * running.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_rxidle(fsm_instance *fi, int event, void *arg)
-+{
-+        channel    *ch = (channel *)arg;
-+        net_device *dev = ch->netdev;
-+        ctc_priv   *privptr = (ctc_priv *)dev->priv;
-+        mpc_group  *grpptr = privptr->mpcg;
-+        int        rc;
-+        unsigned long saveflags;
-+
-+
-+        fsm_deltimer(&ch->timer);
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s cp:%i enter:  %s()\n", 
-+               dev->name,smp_processor_id(),__FUNCTION__);
-+#endif
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s() %04x chstate:%i grpstate:%i\n", 
-+               __FUNCTION__, ch->devno,
-+               fsm_getstate(fi),
-+               fsm_getstate(grpptr->fsm));
-+#endif
-+
-+
-+        fsm_newstate(fi, CH_STATE_RXIDLE);
-+        /* XID processing complete */
-+        switch(fsm_getstate(grpptr->fsm))
-+        {
-+                case MPCG_STATE_FLOWC:
-+                case MPCG_STATE_READY:
-+                        if(ctcmpc_checkalloc_buffer(ch, 1)) goto done;
-+                        ch->trans_skb->data =  
-+                                ch->trans_skb->tail = 
-+                                ch->trans_skb_data;
-+                        ch->trans_skb->len = 0;
-+                        ch->ccw[1].count = ch->max_bufsize;
-+#ifdef DEBUGCCW
-+                        dumpit((char *)&ch->ccw[0],sizeof(ccw1_t) * 3);
-+#endif
-+                        if(event == CH_EVENT_START)
-+                                s390irq_spin_lock_irqsave(ch->irq, saveflags);
-+                        rc = do_IO(ch->irq, &ch->ccw[0], 
-+                                   (intparm_t)ch, 0xff, 0);
-+                        if(event == CH_EVENT_START)
-+                                s390irq_spin_unlock_irqrestore(ch->irq, 
-+                                                               saveflags);
-+                        if(rc != 0)
-+                        {
-+                                fsm_newstate(fi, CH_STATE_RXINIT);
-+                                ccw_check_return_code(ch, rc);
-+                                goto done;
-+                        }
-+                        break;
-+                default:
-+                        break;
-+        }
-+
-+        fsm_event(((ctc_priv *)dev->priv)->fsm,
-+                  DEV_EVENT_RXUP, dev);
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+        return;
-+}
-+
-+/**
-+ * Set channel into extended mode.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_setmode(fsm_instance *fi, int event, void *arg)
-+{
-+        channel *ch = (channel *)arg;
-+        int     rc;
-+        unsigned long saveflags;
-+
-+        fsm_deltimer(&ch->timer);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc cp:%i enter:  %s()\n", 
-+               smp_processor_id(),__FUNCTION__);
-+#endif
-+
-+        fsm_addtimer(&ch->timer, 1500, CH_EVENT_TIMER, ch);
-+        fsm_newstate(fi, CH_STATE_SETUPWAIT);
-+
-+#ifdef DEBUGCCW
-+        dumpit((char *)&ch->ccw[6],sizeof(ccw1_t) * 2);
-+#endif
-+
-+        if(event == CH_EVENT_TIMER)
-+                s390irq_spin_lock_irqsave(ch->irq, saveflags);
-+        rc = do_IO(ch->irq, &ch->ccw[6], (intparm_t)ch, 0xff, 0);
-+        if(event == CH_EVENT_TIMER)
-+                s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
-+        if(rc != 0)
-+        {
-+                fsm_deltimer(&ch->timer);
-+                fsm_newstate(fi, CH_STATE_STARTWAIT);
-+                ccw_check_return_code(ch, rc);
-+        } else
-+                ch->retry = 0;
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Setup channel.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_start(fsm_instance *fi, int event, void *arg)
-+{
-+        channel *ch = (channel *)arg;
-+        unsigned long saveflags;
-+        int     rc;
-+        net_device *dev;
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        if(ch == NULL)
-+        {
-+                printk(KERN_INFO "ctcmpc_ch_action_start ch=NULL\n");
-+                goto done;
-+        }
-+        if(ch->netdev == NULL)
-+        {
-+                printk(KERN_INFO "ctcmpc_ch_action_start dev=NULL, irq=%d\n",
-+                       ch->irq);
-+                goto done;
-+        }
-+        dev = ch->netdev;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s: %s channel start\n", dev->name,
-+               (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
-+#endif
-+
-+        if(ch->trans_skb != NULL)
-+        {
-+                clear_normalized_cda(&ch->ccw[1]);
-+                dev_kfree_skb(ch->trans_skb);
-+                ch->trans_skb = NULL;
-+        }
-+        if(CHANNEL_DIRECTION(ch->flags) == READ)
-+        {
-+                ch->ccw[1].cmd_code = CCW_CMD_READ;
-+                ch->ccw[1].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
-+                ch->ccw[1].count    = 0;
-+        } else
-+        {
-+                ch->ccw[1].cmd_code = CCW_CMD_WRITE;
-+                ch->ccw[1].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
-+                ch->ccw[1].count    = 0;
-+        }
-+        if(ctcmpc_checkalloc_buffer(ch, 0))
-+                printk(KERN_NOTICE
-+                       "%s: Could not allocate %s trans_skb, delaying "
-+                       "allocation until first transfer\n",
-+                       dev->name, 
-+                       (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
-+
-+
-+        ch->ccw[0].cmd_code = CCW_CMD_PREPARE;
-+        ch->ccw[0].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[0].count    = 0;
-+        ch->ccw[0].cda      = 0;
-+        ch->ccw[2].cmd_code = CCW_CMD_NOOP;      
-+        ch->ccw[2].flags    = CCW_FLAG_SLI;
-+        ch->ccw[2].count    = 0;
-+        ch->ccw[2].cda      = 0;
-+        /*************************************************
-+        ch->ccw[2].cmd_code = CCW_CMD_TIC;	 
-+        ch->ccw[2].flags    = 0;
-+        ch->ccw[2].count    = 0;
-+        ch->ccw[2].cda	    = virt_to_phys(&ch->ccw[15]);
-+        **************************************************/
-+        memcpy(&ch->ccw[3], &ch->ccw[0], sizeof(ccw1_t) * 3);
-+        ch->ccw[4].cda      = 0;
-+        ch->ccw[4].flags    &= ~CCW_FLAG_IDA;
-+
-+
-+        fsm_newstate(fi, CH_STATE_STARTWAIT);
-+        fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
-+        s390irq_spin_lock_irqsave(ch->irq, saveflags);
-+        rc = halt_IO(ch->irq, (intparm_t)ch, 0);
-+        s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
-+        if(rc != 0)
-+        {
-+                fsm_deltimer(&ch->timer);
-+                ccw_check_return_code(ch, rc);
-+        }
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+        return;
-+}
-+
-+
-+
-+/**
-+ * Shutdown a channel.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_haltio(fsm_instance *fi, int event, void *arg)
-+{
-+        channel *ch = (channel *)arg;
-+        unsigned long saveflags;
-+        int     rc;
-+        int     oldstate;
-+        int gotlock = 0;
-+
-+        fsm_deltimer(&ch->timer);
-+        fsm_deltimer(&ch->sweep_timer);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
-+        if(event == CH_EVENT_STOP)
-+        {
-+                s390irq_spin_lock_irqsave(ch->irq, saveflags);
-+                gotlock = 1;
-+        }
-+        oldstate = fsm_getstate(fi);
-+        fsm_newstate(fi, CH_STATE_TERM);
-+        rc = halt_IO (ch->irq, (intparm_t)ch, 0);
-+        if(gotlock)
-+                s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
-+        if(rc != 0)
-+        {
-+                fsm_deltimer(&ch->timer);
-+                /* When I say stop..that means STOP */
-+                if(event != CH_EVENT_STOP)
-+                {
-+                        fsm_newstate(fi, oldstate);
-+                        ccw_check_return_code(ch, rc);
-+                }
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * A channel has successfully been halted.
-+ * Cleanup it's queue and notify interface statemachine.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_stopped(fsm_instance *fi, int event, void *arg)
-+{
-+        channel *ch = (channel *)arg;
-+        net_device *dev = ch->netdev;
-+
-+        fsm_deltimer(&ch->timer);
-+        fsm_deltimer(&ch->sweep_timer);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        fsm_newstate(fi, CH_STATE_STOPPED);
-+        if(ch->trans_skb != NULL)
-+        {
-+                clear_normalized_cda(&ch->ccw[1]);
-+                dev_kfree_skb_any(ch->trans_skb);
-+                ch->trans_skb = NULL;
-+        }
-+
-+        ch->th_seg = 0x00;
-+        ch->th_seq_num = 0x00;
-+
-+#ifdef DEBUGSEQ
-+        printk(KERN_INFO "%s: CH_th_seq= %08x\n" ,__FUNCTION__,ch->th_seq_num);
-+#endif
-+
-+        if(CHANNEL_DIRECTION(ch->flags) == READ)
-+        {
-+                skb_queue_purge(&ch->io_queue);
-+                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
-+        } else
-+        {
-+                ctcmpc_purge_skb_queue(&ch->io_queue);
-+                ctcmpc_purge_skb_queue(&ch->sweep_queue);
-+                spin_lock(&ch->collect_lock);
-+                ctcmpc_purge_skb_queue(&ch->collect_queue);
-+                ch->collect_len = 0;
-+                spin_unlock(&ch->collect_lock);
-+                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * A stop command from device statemachine arrived and we are in
-+ * not operational mode. Set state to stopped.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_stop(fsm_instance *fi, int event, void *arg)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        fsm_newstate(fi, CH_STATE_STOPPED);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * A machine check for no path, not operational status or gone device has
-+ * happened.
-+ * Cleanup queue and notify interface statemachine.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_fail(fsm_instance *fi, int event, void *arg)
-+{
-+        channel *ch = (channel *)arg;
-+        net_device *dev = ch->netdev;
-+
-+        fsm_deltimer(&ch->timer);
-+        fsm_deltimer(&ch->sweep_timer);
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        fsm_newstate(fi, CH_STATE_NOTOP);
-+
-+        ch->th_seg = 0x00;
-+        ch->th_seq_num = 0x00;
-+
-+#ifdef DEBUGSEQ
-+        printk(KERN_INFO "%s: CH_th_seq= %08x\n" ,__FUNCTION__,ch->th_seq_num);
-+#endif
-+        if(CHANNEL_DIRECTION(ch->flags) == READ)
-+        {
-+                skb_queue_purge(&ch->io_queue);
-+                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
-+        } else
-+        {
-+                ctcmpc_purge_skb_queue(&ch->io_queue);
-+                ctcmpc_purge_skb_queue(&ch->sweep_queue);
-+                spin_lock(&ch->collect_lock);
-+                ctcmpc_purge_skb_queue(&ch->collect_queue);
-+                ch->collect_len = 0;
-+                spin_unlock(&ch->collect_lock);
-+                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Handle error during setup of channel.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_setuperr(fsm_instance *fi, int event, void *arg)
-+{
-+        channel *ch = (channel *)arg;
-+        net_device *dev = ch->netdev;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        /**
-+         * Special case: Got UC_RCRESET on setmode.
-+         * This means that remote side isn't setup. In this case
-+         * simply retry after some secs...
-+         */
-+        if((fsm_getstate(fi) == CH_STATE_SETUPWAIT) &&
-+           ((event == CH_EVENT_UC_RCRESET) ||
-+            (event == CH_EVENT_UC_RSRESET)   ))
-+        {
-+                fsm_newstate(fi, CH_STATE_STARTRETRY);
-+                fsm_deltimer(&ch->timer);
-+                fsm_addtimer(&ch->timer, CTC_TIMEOUT_1SEC, CH_EVENT_TIMER, ch);
-+//		if (CHANNEL_DIRECTION(ch->flags) == READ) {
-+//			int rc = halt_IO (ch->irq, (intparm_t)ch, 0);
-+//			if (rc != 0)
-+//				ccw_check_return_code(ch, rc);
-+//		}
-+                goto done;
-+        }
-+
-+        printk(KERN_INFO "%s: Error %s during %s channel setup state=%s\n",
-+               dev->name, ch_event_names[event],
-+               (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX",
-+               fsm_getstate_str(fi));
-+        if(CHANNEL_DIRECTION(ch->flags) == READ)
-+        {
-+                fsm_newstate(fi, CH_STATE_RXERR);
-+                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
-+        } else
-+        {
-+                fsm_newstate(fi, CH_STATE_TXERR);
-+                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
-+        }
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+        return;
-+}
-+
-+/**
-+ * Restart a channel after an error.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_restart(fsm_instance *fi, int event, void *arg)
-+{
-+        unsigned long saveflags;
-+        int   oldstate;
-+        int   rc;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        channel *ch = (channel *)arg;
-+        net_device *dev = ch->netdev;
-+
-+        fsm_deltimer(&ch->timer);
-+        printk(KERN_INFO "%s: %s channel restart\n", dev->name,
-+               (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
-+        fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
-+        oldstate = fsm_getstate(fi);
-+        fsm_newstate(fi, CH_STATE_STARTWAIT);
-+        if(event == CH_EVENT_TIMER)
-+                s390irq_spin_lock_irqsave(ch->irq, saveflags);
-+        rc = halt_IO (ch->irq, (intparm_t)ch, 0);
-+        if(event == CH_EVENT_TIMER)
-+                s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
-+        if(rc != 0)
-+        {
-+                fsm_deltimer(&ch->timer);
-+                fsm_newstate(fi, oldstate);
-+                ccw_check_return_code(ch, rc);
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Handle error during RX initial handshake (exchange of
-+ * 0-length block header)
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_rxiniterr(fsm_instance *fi, int event, void *arg)
-+{
-+        channel *ch = (channel *)arg;
-+        net_device *dev = ch->netdev;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        if(event == CH_EVENT_TIMER)
-+        {
-+                printk(KERN_INFO "%s: Timeout during RX init handshake\n",
-+                       dev->name);
-+                if(ch->retry++ < 3)
-+                        ctcmpc_ch_action_restart(fi, event, arg);
-+                else
-+                {
-+                        fsm_newstate(fi, CH_STATE_RXERR);
-+                        fsm_event(((ctc_priv *)dev->priv)->fsm,
-+                                  DEV_EVENT_RXDOWN, dev);
-+                }
-+        } else
-+                printk(KERN_INFO "%s: Error during RX init handshake\n",
-+                       dev->name);
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Notify device statemachine if we gave up initialization
-+ * of RX channel.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_rxinitfail(fsm_instance *fi, int event, void *arg)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        channel *ch = (channel *)arg;
-+        net_device *dev = ch->netdev;
-+
-+        fsm_newstate(fi, CH_STATE_RXERR);
-+        printk(KERN_INFO "%s: RX initialization failed\n", dev->name);
-+        printk(KERN_INFO "%s: RX <-> RX connection detected\n", dev->name);
-+        fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Handle error during TX channel initialization.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_txiniterr(fsm_instance *fi, int event, void *arg)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        channel *ch = (channel *)arg;
-+        net_device *dev = ch->netdev;
-+
-+        if(event == CH_EVENT_TIMER)
-+        {
-+                fsm_deltimer(&ch->timer);
-+                printk(KERN_INFO "%s: Timeout during TX init handshake\n",
-+                       dev->name);
-+                if(ch->retry++ < 3)
-+                        ctcmpc_ch_action_restart(fi, event, arg);
-+                else
-+                {
-+                        fsm_newstate(fi, CH_STATE_TXERR);
-+                        fsm_event(((ctc_priv *)dev->priv)->fsm,
-+                                  DEV_EVENT_TXDOWN, dev);
-+                }
-+        } else
-+                printk(KERN_INFO "%s: Error during TX init handshake\n",
-+                       dev->name);
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Handle TX timeout by retrying operation.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_txretry(fsm_instance *fi, int event, void *arg)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc cp:%i enter:  %s()\n", 
-+               smp_processor_id(),__FUNCTION__);
-+#endif
-+
-+        channel    *ch = (channel *)arg;
-+        net_device *dev = ch->netdev;
-+        unsigned long saveflags;
-+
-+        ctc_priv *privptr = (ctc_priv *)dev->priv;
-+        mpc_group *grpptr = privptr->mpcg;
-+
-+        fsm_deltimer(&ch->timer);
-+
-+
-+        if(ch->retry++ > 3)
-+        {
-+                printk(KERN_INFO "%s: TX retry limit reached\n",
-+                       dev->name);
-+                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
-+                if((grpptr) && (fsm_getstate(grpptr->fsm) == MPCG_STATE_READY))
-+                        ctcmpc_ch_action_restart(fi, event, arg);
-+        } else
-+        {
-+                struct sk_buff *skb;
-+
-+                printk(KERN_INFO "%s: TX retry %d\n", dev->name, ch->retry);
-+                if((skb = skb_peek(&ch->io_queue)))
-+                {
-+                        int rc = 0;
-+
-+                        clear_normalized_cda(&ch->ccw[4]);
-+                        ch->ccw[4].count = skb->len;
-+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21))
-+                        if(set_normalized_cda(&ch->ccw[4],
-+                                              virt_to_phys(skb->data)))
-+                        {
-+#else
-+                        if(set_normalized_cda(&ch->ccw[4],skb->data))
-+                        {
-+#endif
-+                                printk(KERN_INFO "%s: IDAL alloc failed, "
-+                                       "restarting channel\n", dev->name);
-+                                fsm_event(((ctc_priv *)dev->priv)->fsm,
-+                                          DEV_EVENT_TXDOWN, dev);
-+                                ctcmpc_ch_action_restart(fi, event, arg);
-+                                goto done;
-+                        }
-+                        fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
-+                        if(event == CH_EVENT_TIMER)
-+                                s390irq_spin_lock_irqsave(ch->irq, saveflags);
-+
-+#ifdef DEBUGCCW
-+                        dumpit((char *)&ch->ccw[3],sizeof(ccw1_t) * 3);
-+#endif
-+
-+                        rc = do_IO(ch->irq, &ch->ccw[3], 
-+                                   (intparm_t)ch, 0xff, 0);
-+                        if(event == CH_EVENT_TIMER)
-+                                s390irq_spin_unlock_irqrestore(ch->irq,
-+                                                               saveflags);
-+                        if(rc != 0)
-+                        {
-+                                /*not all return codes are bad */
-+                                /* allow retries to occur      */
-+                                /*ccw_check will result in ch  */
-+                                /* down if rc was serious error*/
-+                                ccw_check_return_code(ch, rc);
-+                        }
-+                }
-+        }
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+        return;
-+}
-+/**
-+ * Handle TX busy by setting timer to wait for completion
-+ * then normal retry logic will occur
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_txbusy(fsm_instance *fi, int event, void *arg)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        channel    *ch = (channel *)arg;
-+        if(ch->retry == 0)
-+        {
-+                fsm_deltimer(&ch->timer);
-+                fsm_addtimer(&ch->timer, 
-+                             CTC_BUSYWAIT_10SEC, 
-+                             CH_EVENT_TIMER, 
-+                             ch);
-+        }
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+        return;
-+}
-+
-+/**
-+ * Handle fatal errors during an I/O command.
-+ *
-+ * @param fi    An instance of a channel statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from channel * upon call.
-+ */
-+static void 
-+ctcmpc_ch_action_iofatal(fsm_instance *fi, int event, void *arg)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        channel *ch = (channel *)arg;
-+        net_device *dev = ch->netdev;
-+        ctc_priv   *privptr = (ctc_priv *)dev->priv;
-+
-+        fsm_deltimer(&ch->timer);
-+
-+        printk(KERN_WARNING "%s(): UNRECOVERABLE CHANNEL ERR - "
-+               "CHANNEL REMOVED FROM MPC GROUP\n",
-+               __FUNCTION__);
-+        privptr->stats.tx_dropped++;
-+        privptr->stats.tx_errors++;
-+
-+        if(CHANNEL_DIRECTION(ch->flags) == READ)
-+        {
-+                printk(KERN_INFO "%s: RX I/O error\n", dev->name);
-+                fsm_newstate(fi, CH_STATE_RXERR);
-+                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
-+        } else
-+        {
-+                printk(KERN_INFO "%s: TX I/O error\n", dev->name);
-+                fsm_newstate(fi, CH_STATE_TXERR);
-+                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * The statemachine for a channel.
-+ */
-+static const fsm_node ch_fsm[] = {
-+        { CH_STATE_STOPPED,    CH_EVENT_STOP,        fsm_action_nop},
-+        { CH_STATE_STOPPED,    CH_EVENT_START,       ctcmpc_ch_action_start},
-+        { CH_STATE_STOPPED,    CH_EVENT_IO_ENODEV,   ctcmpc_ch_action_iofatal},
-+
-+        { CH_STATE_STOPPED,    CH_EVENT_FINSTAT,     fsm_action_nop},
-+        { CH_STATE_STOPPED,    CH_EVENT_MC_FAIL,     fsm_action_nop},
-+
-+        { CH_STATE_NOTOP,      CH_EVENT_STOP,        ctcmpc_ch_action_stop},
-+        { CH_STATE_NOTOP,      CH_EVENT_START,       fsm_action_nop},
-+        { CH_STATE_NOTOP,      CH_EVENT_FINSTAT,     fsm_action_nop},
-+        { CH_STATE_NOTOP,      CH_EVENT_MC_FAIL,     fsm_action_nop},
-+        { CH_STATE_NOTOP,      CH_EVENT_MC_GOOD,     ctcmpc_ch_action_start},
-+        { CH_STATE_NOTOP,      CH_EVENT_UC_RCRESET,  ctcmpc_ch_action_stop},
-+        { CH_STATE_NOTOP,      CH_EVENT_UC_RSRESET,  ctcmpc_ch_action_stop},
-+        { CH_STATE_NOTOP,      CH_EVENT_IO_ENODEV,   ctcmpc_ch_action_iofatal},
-+
-+        { CH_STATE_STARTWAIT,  CH_EVENT_STOP,        ctcmpc_ch_action_haltio},
-+        { CH_STATE_STARTWAIT,  CH_EVENT_START,       fsm_action_nop},
-+        { CH_STATE_STARTWAIT,  CH_EVENT_FINSTAT,     ctcmpc_ch_action_setmode},
-+        { CH_STATE_STARTWAIT,  CH_EVENT_TIMER,       ctcmpc_ch_action_setuperr},
-+        { CH_STATE_STARTWAIT,  CH_EVENT_IO_ENODEV,   ctcmpc_ch_action_iofatal},
-+        { CH_STATE_STARTWAIT,  CH_EVENT_IO_EIO,      ctcmpc_ch_action_iofatal},
-+        { CH_STATE_STARTWAIT,  CH_EVENT_MC_FAIL,     ctcmpc_ch_action_fail},
-+
-+        { CH_STATE_STARTRETRY, CH_EVENT_STOP,        ctcmpc_ch_action_haltio},
-+        { CH_STATE_STARTRETRY, CH_EVENT_TIMER,       ctcmpc_ch_action_setmode},
-+        { CH_STATE_STARTRETRY, CH_EVENT_FINSTAT,     ctcmpc_ch_action_setmode},
-+//	{ CH_STATE_STARTRETRY, CH_EVENT_FINSTAT,     fsm_action_nop       },
-+        { CH_STATE_STARTRETRY, CH_EVENT_MC_FAIL,     ctcmpc_ch_action_fail},
-+        { CH_STATE_STARTRETRY, CH_EVENT_IO_ENODEV,   ctcmpc_ch_action_iofatal},
-+
-+
-+        { CH_STATE_SETUPWAIT,  CH_EVENT_STOP,        ctcmpc_ch_action_haltio},
-+        { CH_STATE_SETUPWAIT,  CH_EVENT_START,       fsm_action_nop},
-+        { CH_STATE_SETUPWAIT,  CH_EVENT_FINSTAT,     ctcmpc_ch_action_firstio},
-+        { CH_STATE_SETUPWAIT,  CH_EVENT_UC_RCRESET,  ctcmpc_ch_action_setuperr},
-+        { CH_STATE_SETUPWAIT,  CH_EVENT_UC_RSRESET,  ctcmpc_ch_action_setuperr},
-+        { CH_STATE_SETUPWAIT,  CH_EVENT_TIMER,       ctcmpc_ch_action_setmode},
-+        { CH_STATE_SETUPWAIT,  CH_EVENT_IO_ENODEV,   ctcmpc_ch_action_iofatal},
-+        { CH_STATE_SETUPWAIT,  CH_EVENT_IO_EIO,      ctcmpc_ch_action_iofatal},
-+        { CH_STATE_SETUPWAIT,  CH_EVENT_MC_FAIL,     ctcmpc_ch_action_fail},
-+
-+        { CH_STATE_RXINIT,     CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
-+        { CH_STATE_RXINIT,     CH_EVENT_START,      fsm_action_nop},
-+        { CH_STATE_RXINIT,     CH_EVENT_FINSTAT,    ctcmpc_ch_action_rxidle},
-+        { CH_STATE_RXINIT,     CH_EVENT_UC_RCRESET, ctcmpc_ch_action_rxiniterr},
-+        { CH_STATE_RXINIT,     CH_EVENT_UC_RSRESET, ctcmpc_ch_action_rxiniterr},
-+        { CH_STATE_RXINIT,     CH_EVENT_TIMER,      ctcmpc_ch_action_rxiniterr},
-+        { CH_STATE_RXINIT,     CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_rxinitfail},
-+        { CH_STATE_RXINIT,     CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
-+        { CH_STATE_RXINIT,     CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
-+        { CH_STATE_RXINIT,     CH_EVENT_UC_ZERO,    ctcmpc_ch_action_firstio},
-+        { CH_STATE_RXINIT,     CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
-+
-+        { CH_XID0_PENDING,      CH_EVENT_FINSTAT,   fsm_action_nop},
-+        { CH_XID0_PENDING,      CH_EVENT_ATTN,      ctcmpc_action_attn},
-+        { CH_XID0_PENDING,      CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
-+        { CH_XID0_PENDING,      CH_EVENT_START,     fsm_action_nop},
-+        { CH_XID0_PENDING,      CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
-+        { CH_XID0_PENDING,      CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
-+        { CH_XID0_PENDING,      CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
-+        { CH_XID0_PENDING,      CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID0_PENDING,      CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID0_PENDING,      CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID0_PENDING,      CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
-+
-+        { CH_XID0_INPROGRESS,  CH_EVENT_FINSTAT,    ctcmpc_ch_action_rx},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_ATTN,       ctcmpc_action_attn},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_START,      fsm_action_nop},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_UC_ZERO,    ctcmpc_ch_action_rx},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_UC_RCRESET, ctcmpc_ch_action_setuperr},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_ATTNBUSY,   ctcmpc_action_attnbusy},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_TIMER,      ctcmpc_action_resend},
-+        { CH_XID0_INPROGRESS,  CH_EVENT_IO_EBUSY,   ctcmpc_ch_action_fail},
-+
-+        { CH_XID7_PENDING,      CH_EVENT_FINSTAT,   ctcmpc_ch_action_rx},
-+        { CH_XID7_PENDING,      CH_EVENT_ATTN,      ctcmpc_action_attn},
-+        { CH_XID7_PENDING,      CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
-+        { CH_XID7_PENDING,      CH_EVENT_START,     fsm_action_nop},
-+        { CH_XID7_PENDING,      CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING,      CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING,      CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
-+        { CH_XID7_PENDING,      CH_EVENT_UC_ZERO,   ctcmpc_ch_action_rx},
-+        { CH_XID7_PENDING,      CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING,      CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING,      CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING,      CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING,      CH_EVENT_TIMER,     ctcmpc_action_resend},
-+        { CH_XID7_PENDING,      CH_EVENT_IO_EBUSY,  ctcmpc_ch_action_fail},
-+
-+
-+        { CH_XID7_PENDING1,     CH_EVENT_FINSTAT,   ctcmpc_ch_action_rx},
-+        { CH_XID7_PENDING1,     CH_EVENT_ATTN,      ctcmpc_action_attn},
-+        { CH_XID7_PENDING1,     CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
-+        { CH_XID7_PENDING1,     CH_EVENT_START,     fsm_action_nop},
-+        { CH_XID7_PENDING1,     CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING1,     CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING1,     CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
-+        { CH_XID7_PENDING1,     CH_EVENT_UC_ZERO,   ctcmpc_ch_action_rx},
-+        { CH_XID7_PENDING1,     CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING1,     CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING1,     CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING1,     CH_EVENT_TIMER,     ctcmpc_action_resend},
-+        { CH_XID7_PENDING1,     CH_EVENT_IO_EBUSY,  ctcmpc_ch_action_fail},
-+
-+        { CH_XID7_PENDING2,     CH_EVENT_FINSTAT,   ctcmpc_ch_action_rx},
-+        { CH_XID7_PENDING2,     CH_EVENT_ATTN,      ctcmpc_action_attn},
-+        { CH_XID7_PENDING2,     CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
-+        { CH_XID7_PENDING2,     CH_EVENT_START,     fsm_action_nop},
-+        { CH_XID7_PENDING2,     CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING2,     CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING2,     CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
-+        { CH_XID7_PENDING2,     CH_EVENT_UC_ZERO,   ctcmpc_ch_action_rx},
-+        { CH_XID7_PENDING2,     CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING2,     CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING2,     CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING2,     CH_EVENT_TIMER,     ctcmpc_action_resend},
-+        { CH_XID7_PENDING2,     CH_EVENT_IO_EBUSY,  ctcmpc_ch_action_fail},
-+
-+
-+        { CH_XID7_PENDING3,     CH_EVENT_FINSTAT,   ctcmpc_ch_action_rx},
-+        { CH_XID7_PENDING3,     CH_EVENT_ATTN,      ctcmpc_action_attn},
-+        { CH_XID7_PENDING3,     CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
-+        { CH_XID7_PENDING3,     CH_EVENT_START,     fsm_action_nop},
-+        { CH_XID7_PENDING3,     CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING3,     CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING3,     CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
-+        { CH_XID7_PENDING3,     CH_EVENT_UC_ZERO,   ctcmpc_ch_action_rx},
-+        { CH_XID7_PENDING3,     CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING3,     CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING3,     CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING3,     CH_EVENT_TIMER,     ctcmpc_action_resend},
-+        { CH_XID7_PENDING3,     CH_EVENT_IO_EBUSY,  ctcmpc_ch_action_fail},
-+
-+
-+        { CH_XID7_PENDING4,     CH_EVENT_FINSTAT,   ctcmpc_ch_action_rx},
-+        { CH_XID7_PENDING4,     CH_EVENT_ATTN,      ctcmpc_action_attn},
-+        { CH_XID7_PENDING4,     CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
-+        { CH_XID7_PENDING4,     CH_EVENT_START,     fsm_action_nop},
-+        { CH_XID7_PENDING4,     CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING4,     CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING4,     CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
-+        { CH_XID7_PENDING4,     CH_EVENT_UC_ZERO,   ctcmpc_ch_action_rx},
-+        { CH_XID7_PENDING4,     CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING4,     CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
-+        { CH_XID7_PENDING4,     CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
-+        { CH_XID7_PENDING4,     CH_EVENT_TIMER,     ctcmpc_action_resend},
-+        { CH_XID7_PENDING4,     CH_EVENT_IO_EBUSY,  ctcmpc_ch_action_fail},
-+
-+        { CH_STATE_RXIDLE,     CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
-+        { CH_STATE_RXIDLE,     CH_EVENT_START,      fsm_action_nop},
-+        { CH_STATE_RXIDLE,     CH_EVENT_FINSTAT,    ctcmpc_ch_action_rx},
-+        { CH_STATE_RXIDLE,     CH_EVENT_UC_RCRESET, ctcmpc_ch_action_fail},
-+        { CH_STATE_RXIDLE,     CH_EVENT_UC_RSRESET, ctcmpc_ch_action_fail},
-+        { CH_STATE_RXIDLE,     CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
-+        { CH_STATE_RXIDLE,     CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
-+        { CH_STATE_RXIDLE,     CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
-+        { CH_STATE_RXIDLE,     CH_EVENT_UC_ZERO,    ctcmpc_ch_action_rx},
-+
-+        { CH_STATE_TXINIT,     CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
-+        { CH_STATE_TXINIT,     CH_EVENT_START,      fsm_action_nop},
-+        { CH_STATE_TXINIT,     CH_EVENT_FINSTAT,    ctcmpc_ch_action_txidle},
-+        { CH_STATE_TXINIT,     CH_EVENT_UC_RCRESET, ctcmpc_ch_action_txiniterr},
-+        { CH_STATE_TXINIT,     CH_EVENT_UC_RSRESET, ctcmpc_ch_action_txiniterr},
-+        { CH_STATE_TXINIT,     CH_EVENT_TIMER,      ctcmpc_ch_action_txiniterr},
-+        { CH_STATE_TXINIT,     CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
-+        { CH_STATE_TXINIT,     CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
-+        { CH_STATE_TXINIT,     CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
-+        { CH_STATE_TXINIT,     CH_EVENT_RSWEEP1_TIMER,  ctcmpc_send_sweep},
-+
-+        { CH_STATE_TXIDLE,     CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
-+        { CH_STATE_TXIDLE,     CH_EVENT_START,      fsm_action_nop},
-+        { CH_STATE_TXIDLE,     CH_EVENT_FINSTAT,    ctcmpc_ch_action_firstio},
-+        { CH_STATE_TXIDLE,     CH_EVENT_UC_RCRESET, ctcmpc_ch_action_fail},
-+        { CH_STATE_TXIDLE,     CH_EVENT_UC_RSRESET, ctcmpc_ch_action_fail},
-+        { CH_STATE_TXIDLE,     CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
-+        { CH_STATE_TXIDLE,     CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
-+        { CH_STATE_TXIDLE,     CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
-+        { CH_STATE_TXIDLE,     CH_EVENT_RSWEEP1_TIMER,  ctcmpc_send_sweep},
-+
-+        { CH_STATE_TERM,       CH_EVENT_STOP,       fsm_action_nop},
-+        { CH_STATE_TERM,       CH_EVENT_START,      ctcmpc_ch_action_restart},
-+        { CH_STATE_TERM,       CH_EVENT_FINSTAT,    ctcmpc_ch_action_stopped},
-+        { CH_STATE_TERM,       CH_EVENT_UC_RCRESET, fsm_action_nop},
-+        { CH_STATE_TERM,       CH_EVENT_UC_RSRESET, fsm_action_nop},
-+        { CH_STATE_TERM,       CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
-+        { CH_STATE_TERM,       CH_EVENT_IO_EBUSY,   ctcmpc_ch_action_fail},
-+        { CH_STATE_TERM,       CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
-+                                                    
-+        { CH_STATE_DTERM,      CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
-+        { CH_STATE_DTERM,      CH_EVENT_START,      ctcmpc_ch_action_restart},
-+        { CH_STATE_DTERM,      CH_EVENT_FINSTAT,    ctcmpc_ch_action_setmode},
-+        { CH_STATE_DTERM,      CH_EVENT_UC_RCRESET, fsm_action_nop},
-+        { CH_STATE_DTERM,      CH_EVENT_UC_RSRESET, fsm_action_nop},
-+        { CH_STATE_DTERM,      CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
-+        { CH_STATE_DTERM,      CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
-+                                                    
-+        { CH_STATE_TX,         CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
-+        { CH_STATE_TX,         CH_EVENT_START,      fsm_action_nop},
-+        { CH_STATE_TX,         CH_EVENT_FINSTAT,    ctcmpc_ch_action_txdone},
-+        { CH_STATE_TX,         CH_EVENT_UC_RCRESET, ctcmpc_ch_action_fail},
-+        { CH_STATE_TX,         CH_EVENT_UC_RSRESET, ctcmpc_ch_action_fail},
-+        { CH_STATE_TX,         CH_EVENT_TIMER,      ctcmpc_ch_action_txretry},  
-+        { CH_STATE_TX,         CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
-+        { CH_STATE_TX,         CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
-+        { CH_STATE_TX,         CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
-+        { CH_STATE_TX,         CH_EVENT_RSWEEP1_TIMER,  ctcmpc_send_sweep},
-+        { CH_STATE_TX,         CH_EVENT_IO_EBUSY,   ctcmpc_ch_action_txbusy},
-+
-+        { CH_STATE_RXERR,      CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
-+        { CH_STATE_TXERR,      CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
-+        { CH_STATE_TXERR,      CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
-+
-+        { CH_STATE_TXERR,      CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
-+        { CH_STATE_RXERR,      CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
-+};
-+
-+static const int CH_FSM_LEN = sizeof(ch_fsm) / sizeof(fsm_node);
-+
-+/**
-+ * Functions related to setup and device detection.
-+ *****************************************************************************/
-+
-+/**
-+ * Add a new channel to the list of channels.
-+ * Keeps the channel list sorted.
-+ *
-+ * @param irq   The IRQ to be used by the new channel.
-+ * @param devno The device number of the new channel.
-+ * @param type  The type class of the new channel.
-+ *
-+ * @return 0 on success, !0 on error.
-+ */
-+static int 
-+ctcmpc_add_channel(int irq, __u16 devno, channel_type_t type)
-+{
-+        channel **c = &channels;
-+        channel *ch;
-+        char name[10];
-+        int rc = 0;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+
-+        if((ch = (channel *)kmalloc(sizeof(channel), gfp_type())) == NULL)
-+        {
-+                printk(KERN_INFO "ctcmpc: Out of memory in %s\n",__FUNCTION__);
-+                rc = -1;
-+                goto done;
-+        }
-+        memset(ch, 0, sizeof(channel));
-+
-+        ch->discontact_th = (th_header *)kmalloc(TH_HEADER_LENGTH,gfp_type());
-+        if(ch->discontact_th == NULL)
-+        {
-+                kfree(ch);
-+                printk(KERN_INFO "ctcmpc: Out of memory in %s\n",__FUNCTION__);
-+                rc = -1;
-+                goto done;
-+        }
-+        memset(ch->discontact_th, 0, TH_HEADER_LENGTH);
-+        ch->discontact_th->th_blk_flag = TH_DISCONTACT;
-+        tasklet_init(&ch->ch_disc_tasklet,
-+                     ctcmpc_action_send_discontact,
-+                     (unsigned long)ch);
-+
-+
-+        tasklet_init(&ch->ch_tasklet,ctcmpc_bh,(unsigned long)ch);
-+        if((ch->ccw = (ccw1_t *)kmalloc(sizeof(ccw1_t) * 17,
-+                                        GFP_KERNEL|GFP_DMA)) == NULL)
-+        {
-+                kfree(ch);
-+                printk(KERN_INFO "ctcmpc: Out of memory in %s\n",__FUNCTION__);
-+                rc = -1;
-+                goto done;
-+        }
-+
-+        ch->max_bufsize = (MPC_BUFSIZE_DEFAULT - 35);
-+        /**
-+         * "static" ccws are used in the following way:
-+         *
-+         * ccw[0..2] (Channel program for generic I/O):
-+         *           0: prepare
-+         *           1: read or write (depending on direction) with fixed
-+         *              buffer (idal allocated once when buffer is allocated)
-+         *           2: tic  (to double noops)
-+         * ccw[3..5] (Channel program for direct write of packets)
-+         *           3: prepare
-+         *           4: write (idal allocated on every write).
-+         *           5: tic  (to double noops)
-+         * ccw[6..7] (Channel program for initial channel setup):
-+         *           6: set extended mode
-+         *           7: nop
-+         *
-+         * ch->ccw[0..5] are initialized in ctcmpc_ch_action_start because
-+         * the channel's direction is yet unknown here.
-+         *
-+         * ccws used for xid2 negotiations
-+         *  ch-ccw[8-14] need to be used for the XID exchange either 
-+         *    X side XID2 Processing
-+         *       8:  write control
-+         *       9:  write th 
-+         *	     10: write XID
-+         *	     11: read th from secondary
-+         *	     12: read XID   from secondary
-+         *	     13: read 4 byte ID
-+         *	     14: nop
-+         *    Y side XID Processing
-+         *	     8:  sense
-+         *       9:  read th 
-+         *	     10: read XID
-+         *	     11: write th 
-+         *	     12: write XID   
-+         *	     13: write 4 byte ID
-+         *	     14: nop
-+         *
-+         *  ccws used for double noop due to VM timing issues
-+         *  which result in unrecoverable Busy on channel
-+         *       15: nop
-+         *       16: nop
-+         */
-+
-+        ch->ccw[6].cmd_code = CCW_CMD_SET_EXTENDED;
-+        ch->ccw[6].flags    = CCW_FLAG_SLI;
-+        ch->ccw[6].count    = 0;
-+        ch->ccw[6].cda      = 0;
-+
-+        ch->ccw[7].cmd_code = CCW_CMD_NOOP;
-+        ch->ccw[7].flags    = CCW_FLAG_SLI;
-+        ch->ccw[7].count    = 0;
-+        ch->ccw[7].cda      = 0;
-+
-+        ch->ccw[15].cmd_code = CCW_CMD_WRITE;
-+        ch->ccw[15].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
-+        ch->ccw[15].count    = TH_HEADER_LENGTH;
-+        ch->ccw[15].cda      = virt_to_phys(ch->discontact_th);
-+
-+        ch->ccw[16].cmd_code = CCW_CMD_NOOP;
-+        ch->ccw[16].flags    = CCW_FLAG_SLI;
-+        ch->ccw[16].count    = 0;
-+        ch->ccw[16].cda      = 0;
-+
-+
-+        ch->irq = irq;
-+        ch->devno = devno;
-+        ch->type = type;
-+        ch->in_mpcgroup = 0;
-+        sprintf(name, "ch-%04x", devno);
-+        ch->fsm = init_fsm(name, ch_state_names,
-+                           ch_event_names, NR_CH_STATES, NR_CH_EVENTS,
-+                           ch_fsm, CH_FSM_LEN, GFP_KERNEL);
-+        if(ch->fsm == NULL)
-+        {
-+                printk(KERN_INFO
-+                       "ctcmpc: Could not create FSM in ctcmpc_add_channel\n");
-+                kfree(ch);
-+                rc = -1;
-+                goto done;
-+        }
-+        fsm_newstate(ch->fsm, CH_STATE_IDLE);
-+        if((ch->devstat = (devstat_t*)kmalloc(sizeof(devstat_t), gfp_type()))
-+           == NULL)
-+        {
-+                printk(KERN_INFO "ctcmpc: Out of memory in %s\n",__FUNCTION__);
-+                kfree_fsm(ch->fsm);
-+                kfree(ch);
-+                rc = -1;
-+                goto done;
-+        }
-+        memset(ch->devstat, 0, sizeof(devstat_t));
-+        while(*c && ((*c)->devno < devno))
-+                c = &(*c)->next;
-+        if((*c)->devno == devno)
-+        {
-+                printk(KERN_INFO
-+                       "ctcmpc: %s: device %04x already in list, "
-+                       "using old entry\n", __FUNCTION__,(*c)->devno);
-+                kfree(ch->devstat);
-+                kfree_fsm(ch->fsm);
-+                kfree(ch);
-+                rc = 0;
-+                goto done;
-+        }
-+        fsm_settimer(ch->fsm, &ch->timer);
-+        fsm_settimer(ch->fsm, &ch->sweep_timer);
-+        skb_queue_head_init(&ch->io_queue);
-+        skb_queue_head_init(&ch->collect_queue);
-+        skb_queue_head_init(&ch->sweep_queue);
-+        ch->next = *c;
-+        *c = ch;
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return(rc);
-+}
-+
-+#ifndef CTC_CHANDEV
-+/**
-+ * scan for all channels and create an entry in the channels list
-+ * for every supported channel.
-+ */
-+static void 
-+channel_scan(void)
-+{
-+        static int      print_result = 1;
-+        int             irq;
-+        int             nr_mpc = 0;
-+        s390_dev_info_t di;
-+
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+
-+        for(irq = 0; irq < NR_IRQS; irq++)
-+        {
-+                if(get_dev_info_by_irq(irq, &di) == 0)
-+                {
-+                        if((di.status == DEVSTAT_NOT_OPER) ||
-+                           (di.status == DEVSTAT_DEVICE_OWNED))
-+                                continue;
-+                        switch(channel_type(&di.sid_data))
-+                        {
-+                                case channel_type_mpc:
-+                                        /* CTC/A  or ESCON*/
-+                                        if(!ctcmpc_add_channel(irq, di.devno,
-+                                                             channel_type_mpc))
-+                                                nr_mpc++;
-+                                        break;
-+                                default: break;
-+                        }
-+                }
-+        }
-+        if(print_result)
-+        {
-+                if(nr_mpc)
-+                        printk(KERN_INFO
-+                               "ctcmpc: %d channel%s found.\n",
-+                               nr_mpc, (nr_mpc == 1) ? "s" : "");
-+                else
-+                        printk(KERN_INFO "ctcmpc: No channel devices found.\n");
-+        }
-+        print_result = 0;
-+
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+}
-+#endif
-+
-+/**
-+ * Release a specific channel in the channel list.
-+ *
-+ * @param ch Pointer to channel struct to be released.
-+ */
-+static void 
-+channel_free(channel *ch)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        ch->flags &= ~CHANNEL_FLAGS_INUSE;
-+        fsm_newstate(ch->fsm, CH_STATE_IDLE);
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Remove a specific channel in the channel list.
-+ *
-+ * @param ch Pointer to channel struct to be released.
-+ */
-+static void 
-+channel_remove(channel *ch)
-+{
-+        channel **c = &channels;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        if(ch == NULL)
-+                goto done;
-+
-+#ifndef CTC_CHANDEV
-+        if(ch->flags & CHANNEL_FLAGS_INUSE)
-+                FREE_IRQ(ch->irq, ch->devstat);
-+#endif
-+        channel_free(ch);
-+        while(*c)
-+        {
-+                if(*c == ch)
-+                {
-+                        *c = ch->next;
-+                        fsm_deltimer(&ch->timer);
-+                        fsm_deltimer(&ch->sweep_timer);
-+                        kfree_fsm(ch->fsm);
-+                        clear_normalized_cda(&ch->ccw[4]);
-+                        if(ch->trans_skb != NULL)
-+                        {
-+                                clear_normalized_cda(&ch->ccw[1]);
-+                                dev_kfree_skb_any(ch->trans_skb);
-+                        }
-+                        tasklet_kill(&ch->ch_tasklet);
-+                        tasklet_kill(&ch->ch_disc_tasklet);
-+                        kfree(ch->discontact_th);
-+                        kfree(ch->ccw);
-+                        goto done;
-+                }
-+                c = &((*c)->next);
-+        }
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+        return;
-+}
-+
-+
-+/**
-+ * Get a specific channel from the channel list.
-+ *
-+ * @param type Type of channel we are interested in.
-+ * @param devno Device number of channel we are interested in.
-+ * @param direction Direction we want to use this channel for.
-+ *
-+ * @return Pointer to a channel or NULL if no matching channel available.
-+ */
-+static channel 
-+*channel_get(channel_type_t type, int devno, int direction)
-+{
-+        channel *ch = channels;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO
-+               "ctcmpc: %s(): searching for ch with devno %d and type %d\n",
-+               __FUNCTION__, devno, type);
-+#endif
-+
-+        while(ch && ((ch->devno != devno) || (ch->type != type)))
-+        {
-+#ifdef DEBUG
-+                printk(KERN_INFO
-+                       "ctcmpc: %s(): ch=0x%p (devno=%d, type=%d\n",
-+                       __FUNCTION__, ch, ch->devno, ch->type);
-+#endif
-+                ch = ch->next;
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO
-+               "ctcmpc: %s(): ch=0x%pq (devno=%d, type=%d\n",
-+               __FUNCTION__, ch, ch->devno, ch->type);
-+#endif
-+        if(!ch)
-+        {
-+                printk(KERN_INFO "ctcmpc: %s(): channel with devno %d "
-+                       "and type %d not found in channel list\n",
-+                       __FUNCTION__, devno, type);
-+        } else
-+        {
-+                if(ch->flags & CHANNEL_FLAGS_INUSE)
-+                        ch = NULL;
-+                else
-+                {
-+                        ch->flags |= CHANNEL_FLAGS_INUSE;
-+                        ch->flags &= ~CHANNEL_FLAGS_RWMASK;
-+                        ch->flags |= (direction == WRITE)
-+                                     ? CHANNEL_FLAGS_WRITE:CHANNEL_FLAGS_READ;
-+                        fsm_newstate(ch->fsm, CH_STATE_STOPPED);
-+                }
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return ch;
-+}
-+
-+#ifndef CTC_CHANDEV
-+/**
-+ * Get the next free channel from the channel list
-+ *
-+ * @param type Type of channel we are interested in.
-+ * @param direction Direction we want to use this channel for.
-+ *
-+ * @return Pointer to a channel or NULL if no matching channel available.
-+ */
-+static channel 
-+*channel_get_next(channel_type_t type, int direction)
-+{
-+        channel *ch = channels;
-+
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+        while(ch && (ch->type != type || (ch->flags & CHANNEL_FLAGS_INUSE)))
-+                ch = ch->next;
-+        if(ch)
-+        {
-+                ch->flags |= CHANNEL_FLAGS_INUSE;
-+                ch->flags &= ~CHANNEL_FLAGS_RWMASK;
-+                ch->flags |= (direction == WRITE)
-+                             ? CHANNEL_FLAGS_WRITE:CHANNEL_FLAGS_READ;
-+                fsm_newstate(ch->fsm, CH_STATE_STOPPED);
-+        }
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+        return ch;
-+}
-+#endif
-+
-+/**
-+ * Return the channel type by name.
-+ *
-+ * @param name Name of network interface.
-+ *
-+ * @return Type class of channel to be used for that interface.
-+ */
-+static channel_type_t inline 
-+extract_channel_media(char *name)
-+{
-+        channel_type_t ret = channel_type_unknown;
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+
-+        if(name != NULL)
-+        {
-+                if(strncmp(name, "mpc", 3) == 0)
-+                        ret = channel_type_mpc;
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return ret;
-+}
-+
-+/**
-+ * Find a channel in the list by its IRQ.
-+ *
-+ * @param irq IRQ to search for.
-+ *
-+ * @return Pointer to channel or NULL if no matching channel found.
-+ */
-+static channel 
-+*find_channel_by_irq(int irq)
-+{
-+        channel *ch = channels;
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        while(ch && (ch->irq != irq))
-+                ch = ch->next;
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return ch;
-+}
-+
-+/**
-+ * Main IRQ handler.
-+ *
-+ * @param irq     The IRQ to handle.
-+ * @param intparm IRQ params.
-+ * @param regs    CPU registers.
-+ */
-+static void 
-+ctcmpc_irq_handler (int irq, void *intparm, struct pt_regs *regs)
-+{
-+
-+        devstat_t  *devstat = (devstat_t *)intparm;
-+        channel    *ach = (channel *)devstat->intparm;
-+        channel    *ch  = NULL;
-+        net_device *dev;
-+
-+        /**
-+         * Check for unsolicited interrupts.
-+         * If intparm is NULL, then loop over all our known
-+         * channels and try matching the irq number.
-+         */
-+        if(ach == NULL)
-+        {
-+                if((ch = find_channel_by_irq(irq)) == NULL)
-+                {
-+                        printk(KERN_INFO
-+                               "ctcmpc: Got unsolicited irq: %04x c-%02x d-%02x"
-+                               "f-%02x\n", devstat->devno, devstat->cstat,
-+                               devstat->dstat, devstat->flag);
-+                        goto done;
-+                }
-+        } else
-+                ch = ach;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", __FUNCTION__, ch->devno);
-+#endif
-+
-+
-+        dev = (net_device *)(ch->netdev);
-+        if(dev == NULL)
-+        {
-+                printk(KERN_CRIT
-+                       "ctcmpc: %s dev = NULL irq=%d, ch=0x%p\n",
-+                       __FUNCTION__,irq, ch);
-+                goto done;
-+        }
-+
-+
-+        if(intparm == NULL)
-+                printk(KERN_INFO "%s: Channel %04x found by IRQ %d\n",
-+                       dev->name, ch->devno, irq);
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO
-+               "%s: cp:%i interrupt for device: %04x received "
-+               "in state:%s c-%02x d-%02x "
-+               "f-%02x\n", 
-+               dev->name, 
-+               smp_processor_id(),
-+               devstat->devno, 
-+               fsm_getstate_str(ch->fsm),
-+               devstat->cstat,
-+               devstat->dstat, 
-+               devstat->flag);
-+#endif
-+
-+        /* Check for good subchannel return code, otherwise error message */
-+        if(devstat->cstat)
-+        {
-+                fsm_event(ch->fsm, CH_EVENT_SC_UNKNOWN, ch);
-+                printk(KERN_INFO
-+                       "%s: subchannel check for device: %04x - %02x %02x "
-+                       "%02x\n", dev->name, ch->devno, devstat->cstat,
-+                       devstat->dstat, devstat->flag);
-+                goto done;
-+        }
-+
-+        /* Check the reason-code of a unit check */
-+        if(devstat->dstat & DEV_STAT_UNIT_CHECK)
-+        {
-+                ccw_unit_check(ch, devstat->ii.sense.data[0]);
-+                goto done;
-+        }
-+        if(devstat->dstat & DEV_STAT_BUSY)
-+        {
-+                if(devstat->dstat & DEV_STAT_ATTENTION)
-+                        fsm_event(ch->fsm, CH_EVENT_ATTNBUSY, ch);
-+                else
-+                        fsm_event(ch->fsm, CH_EVENT_BUSY, ch);
-+                goto done;
-+        }
-+
-+        if(devstat->dstat & DEV_STAT_ATTENTION)
-+        {
-+                fsm_event(ch->fsm, CH_EVENT_ATTN, ch);
-+                goto done;
-+        }
-+
-+        /* NOT unsolicited irq */
-+        if(ach)
-+        {
-+                if(devstat->dstat & DEV_STAT_DEV_END)
-+                {
-+                        if((devstat->dstat & DEV_STAT_CHN_END) ||
-+                           (devstat->flag & DEVSTAT_HALT_FUNCTION))
-+                        {
-+                                fsm_event(ch->fsm, CH_EVENT_FINSTAT, ch);
-+                                goto done;
-+                        } else
-+                        {
-+                                /* do_IO has not really completed */
-+                                fsm_event(ch->fsm, CH_EVENT_IRQ, ch);
-+                                goto done;
-+                        }
-+                }
-+        }
-+
-+        if(devstat->flag & DEVSTAT_FINAL_STATUS)
-+                fsm_event(ch->fsm, CH_EVENT_FINSTAT, ch);
-+        else
-+                fsm_event(ch->fsm, CH_EVENT_IRQ, ch);
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", __FUNCTION__, ch->devno);
-+#endif
-+
-+        return;
-+
-+}
-+
-+/**
-+ * Actions for interface - statemachine.
-+ *****************************************************************************/
-+
-+/**
-+ * Startup channels by sending CH_EVENT_START to each channel.
-+ *
-+ * @param fi    An instance of an interface statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from net_device * upon call.
-+ */
-+static void 
-+dev_action_start(fsm_instance *fi, int event, void *arg)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        net_device *dev = (net_device *)arg;
-+        ctc_priv   *privptr = (ctc_priv *)dev->priv;
-+        mpc_group  *grpptr = privptr->mpcg;
-+        int        direction;
-+
-+        fsm_deltimer(&privptr->restart_timer);
-+        fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
-+        grpptr->channels_terminating = 0;
-+        for(direction = READ; direction <= WRITE; direction++)
-+        {
-+                channel *ch = privptr->channel[direction];
-+                fsm_event(ch->fsm, CH_EVENT_START, ch);
-+        }
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Shutdown channels by sending CH_EVENT_STOP to each channel.
-+ *
-+ * @param fi    An instance of an interface statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from net_device * upon call.
-+ */
-+static void 
-+dev_action_stop(fsm_instance *fi, int event, void *arg)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        net_device *dev = (net_device *)arg;
-+        ctc_priv   *privptr = (ctc_priv *)dev->priv;
-+        mpc_group  * grpptr = privptr->mpcg;
-+        int        direction;
-+
-+        fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
-+        for(direction = READ; direction <= WRITE; direction++)
-+        {
-+                channel *ch = privptr->channel[direction];
-+                fsm_event(ch->fsm, CH_EVENT_STOP, ch);
-+                ch->th_seq_num = 0x00;
-+
-+#ifdef DEBUGSEQ
-+                printk(KERN_INFO "%s: CH_th_seq= %08x\n" ,
-+                       __FUNCTION__,ch->th_seq_num);
-+#endif
-+        }
-+        fsm_newstate(grpptr->fsm, MPCG_STATE_RESET);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+static void 
-+dev_action_restart(fsm_instance *fi, int event, void *arg)
-+{
-+        net_device *dev = (net_device *)arg;
-+        ctc_priv   *privptr = dev->priv;
-+        mpc_group  * grpptr = privptr->mpcg;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        printk(KERN_DEBUG "ctcmpc: Restarting %s Device and MPC Group "
-+               "in 5 seconds\n", 
-+               dev->name);
-+        dev_action_stop(fi, event, arg);
-+        fsm_event(privptr->fsm, DEV_EVENT_STOP, dev);
-+        fsm_newstate(grpptr->fsm, MPCG_STATE_RESET);
-+        /* going back into start sequence too quickly can         */
-+        /* result in the other side becoming unreachable   due    */
-+        /* to sense reported when IO is aborted                   */
-+        fsm_addtimer(&privptr->restart_timer, 1000, DEV_EVENT_START, dev);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Called from channel statemachine
-+ * when a channel is up and running.
-+ *
-+ * @param fi    An instance of an interface statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from net_device * upon call.
-+ */
-+static void 
-+dev_action_chup(fsm_instance *fi, int event, void *arg)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        net_device *dev = (net_device *)arg;
-+        ctc_priv  *privptr = (ctc_priv *)dev->priv;
-+
-+        switch(fsm_getstate(fi))
-+        {
-+                case DEV_STATE_STARTWAIT_RXTX:
-+                        if(event == DEV_EVENT_RXUP)
-+                                fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
-+                        else
-+                                fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
-+                        break;
-+                case DEV_STATE_STARTWAIT_RX:
-+                        if(event == DEV_EVENT_RXUP)
-+                        {
-+                                fsm_newstate(fi, DEV_STATE_RUNNING);
-+                                printk(KERN_INFO
-+                                       "%s: connected with remote side\n",
-+                                       dev->name);
-+                                ctcmpc_clear_busy(dev);
-+                        }
-+                        break;
-+                case DEV_STATE_STARTWAIT_TX:
-+                        if(event == DEV_EVENT_TXUP)
-+                        {
-+                                fsm_newstate(fi, DEV_STATE_RUNNING);
-+                                printk(KERN_INFO
-+                                       "%s: connected with remote side\n",
-+                                       dev->name);
-+                                ctcmpc_clear_busy(dev);
-+                        }
-+                        break;
-+                case DEV_STATE_STOPWAIT_TX:
-+                        if(event == DEV_EVENT_RXUP)
-+                                fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
-+                        break;
-+                case DEV_STATE_STOPWAIT_RX:
-+                        if(event == DEV_EVENT_TXUP)
-+                                fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
-+                        break;
-+        }
-+
-+        if(event == DEV_EVENT_RXUP)
-+                ctcmpc_channel_action(privptr->channel[READ],
-+                                      READ,MPC_CHANNEL_ADD);
-+        else
-+                ctcmpc_channel_action(privptr->channel[WRITE],
-+                                      WRITE,MPC_CHANNEL_ADD);
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+/**
-+ * Called from channel statemachine
-+ * when a channel has been shutdown.
-+ *
-+ * @param fi    An instance of an interface statemachine.
-+ * @param event The event, just happened.
-+ * @param arg   Generic pointer, casted from net_device * upon call.
-+ */
-+static void 
-+dev_action_chdown(fsm_instance *fi, int event, void *arg)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        net_device *dev = (net_device *)arg;
-+        ctc_priv   *privptr = (ctc_priv *)dev->priv;
-+
-+        switch(fsm_getstate(fi))
-+        {
-+                case DEV_STATE_RUNNING:
-+                        if(event == DEV_EVENT_TXDOWN)
-+                                fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
-+                        else
-+                                fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
-+                        break;
-+                case DEV_STATE_STARTWAIT_RX:
-+                        if(event == DEV_EVENT_TXDOWN)
-+                                fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
-+                        break;
-+                case DEV_STATE_STARTWAIT_TX:
-+                        if(event == DEV_EVENT_RXDOWN)
-+                                fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
-+                        break;
-+                case DEV_STATE_STOPWAIT_RXTX:
-+                        if(event == DEV_EVENT_TXDOWN)
-+                                fsm_newstate(fi, DEV_STATE_STOPWAIT_RX);
-+                        else
-+                                fsm_newstate(fi, DEV_STATE_STOPWAIT_TX);
-+                        break;
-+                case DEV_STATE_STOPWAIT_RX:
-+                        if(event == DEV_EVENT_RXDOWN)
-+                                fsm_newstate(fi, DEV_STATE_STOPPED);
-+                        break;
-+                case DEV_STATE_STOPWAIT_TX:
-+                        if(event == DEV_EVENT_TXDOWN)
-+                                fsm_newstate(fi, DEV_STATE_STOPPED);
-+                        break;
-+        }
-+
-+
-+        if(event == DEV_EVENT_RXDOWN)
-+                ctcmpc_channel_action(privptr->channel[READ],
-+                                      READ,MPC_CHANNEL_REMOVE);
-+        else
-+                ctcmpc_channel_action(privptr->channel[WRITE],
-+                                      WRITE,MPC_CHANNEL_REMOVE);
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+}
-+
-+static const fsm_node dev_fsm[] = {
-+        { DEV_STATE_STOPPED,        DEV_EVENT_START,   dev_action_start},
-+
-+        { DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_START,   dev_action_start},
-+        { DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_RXDOWN,  dev_action_chdown},
-+        { DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_TXDOWN,  dev_action_chdown},
-+        { DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_RESTART, dev_action_restart},
-+
-+        { DEV_STATE_STOPWAIT_RX,    DEV_EVENT_START,   dev_action_start},
-+        { DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RXUP,    dev_action_chup},
-+        { DEV_STATE_STOPWAIT_RX,    DEV_EVENT_TXUP,    dev_action_chup},
-+        { DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RXDOWN,  dev_action_chdown},
-+        { DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RESTART, dev_action_restart},
-+
-+        { DEV_STATE_STOPWAIT_TX,    DEV_EVENT_START,   dev_action_start},
-+        { DEV_STATE_STOPWAIT_TX,    DEV_EVENT_RXUP,    dev_action_chup},
-+        { DEV_STATE_STOPWAIT_TX,    DEV_EVENT_TXUP,    dev_action_chup},
-+        { DEV_STATE_STOPWAIT_TX,    DEV_EVENT_TXDOWN,  dev_action_chdown},
-+        { DEV_STATE_STOPWAIT_TX,    DEV_EVENT_RESTART, dev_action_restart},
-+
-+        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP,    dev_action_stop},
-+        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP,    dev_action_chup},
-+        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP,    dev_action_chup},
-+        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN,  dev_action_chdown},
-+        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN,  dev_action_chdown},
-+        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart},
-+
-+        { DEV_STATE_STARTWAIT_TX,   DEV_EVENT_STOP,    dev_action_stop},
-+        { DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RXUP,    dev_action_chup},
-+        { DEV_STATE_STARTWAIT_TX,   DEV_EVENT_TXUP,    dev_action_chup},
-+        { DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RXDOWN,  dev_action_chdown},
-+        { DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RESTART, dev_action_restart},
-+
-+        { DEV_STATE_STARTWAIT_RX,   DEV_EVENT_STOP,    dev_action_stop},
-+        { DEV_STATE_STARTWAIT_RX,   DEV_EVENT_RXUP,    dev_action_chup},
-+        { DEV_STATE_STARTWAIT_RX,   DEV_EVENT_TXUP,    dev_action_chup},
-+        { DEV_STATE_STARTWAIT_RX,   DEV_EVENT_TXDOWN,  dev_action_chdown},
-+        { DEV_STATE_STARTWAIT_RX,   DEV_EVENT_RESTART, dev_action_restart},
-+
-+        { DEV_STATE_RUNNING,        DEV_EVENT_STOP,    dev_action_stop},
-+        { DEV_STATE_RUNNING,        DEV_EVENT_RXDOWN,  dev_action_chdown},
-+        { DEV_STATE_RUNNING,        DEV_EVENT_TXDOWN,  dev_action_chdown},
-+        { DEV_STATE_RUNNING,        DEV_EVENT_TXUP,    fsm_action_nop},
-+        { DEV_STATE_RUNNING,        DEV_EVENT_RXUP,    fsm_action_nop},
-+        { DEV_STATE_RUNNING,        DEV_EVENT_RESTART, dev_action_restart},
-+};
-+
-+static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
-+
-+/**
-+ * Transmit a packet.
-+ * This is a helper function for ctcmpc_tx().
-+ *
-+ * @param ch Channel to be used for sending.
-+ * @param skb Pointer to struct sk_buff of packet to send.
-+ *            The linklevel header has already been set up
-+ *            by ctcmpc_tx().
-+ *
-+ * @return 0 on success, -ERRNO on failure. (Never fails.)
-+ */
-+static int 
-+transmit_skb(channel *ch, struct sk_buff *skb)
-+{
-+
-+        unsigned long saveflags;
-+        pdu      *p_header;
-+        int       rc = 0;
-+        net_device  *dev        = ch->netdev;
-+        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
-+        mpc_group   *grpptr     = privptr->mpcg;
-+#ifdef DEBUGDATA
-+        __u32                out_len = 0;
-+#endif
-+
-+
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s cp:%i enter:  %s() state: %s \n", 
-+               dev->name,
-+               smp_processor_id(),
-+               __FUNCTION__,
-+               fsm_getstate_str(ch->fsm));
-+#endif
-+
-+        if((fsm_getstate(ch->fsm) != CH_STATE_TXIDLE) || 
-+           (grpptr->in_sweep))
-+        {
-+                spin_lock_irqsave(&ch->collect_lock, saveflags);
-+                atomic_inc(&skb->users);
-+                p_header = (pdu *)kmalloc(PDU_HEADER_LENGTH, gfp_type());
-+
-+                if(!p_header)
-+                {
-+                        printk(KERN_WARNING ": OUT OF MEMORY IN %s(): "
-+                               "Data Lost \n",
-+                               __FUNCTION__);
-+                        atomic_dec(&skb->users);
-+                        dev_kfree_skb_any(skb);
-+                        spin_unlock_irqrestore(&ch->collect_lock, saveflags);
-+                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
-+                        goto done;
-+                }
-+
-+                p_header->pdu_offset = skb->len;
-+                p_header->pdu_proto = 0x01;
-+                p_header->pdu_flag = 0x00;
-+                if(skb->protocol == ntohs(ETH_P_SNAP))
-+                {
-+                        p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;   
-+                } else
-+                {
-+                        p_header->pdu_flag |= PDU_FIRST; 
-+                }
-+                p_header->pdu_seq = 0;
-+                memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header,
-+                       PDU_HEADER_LENGTH);
-+#ifdef DEBUGDATA
-+                __u32 out_len;
-+                if(skb->len > 32) out_len = 32;
-+                else out_len = skb->len;
-+                printk(KERN_INFO 
-+                       "%s(): Putting on collect_q - skb len:%04x \n", 
-+                       __FUNCTION__,skb->len);
-+                printk(KERN_INFO 
-+                       "%s(): pdu header and data for up to 32 bytes\n", 
-+                       __FUNCTION__);
-+                dumpit((char *)skb->data,out_len);
-+#endif
-+                skb_queue_tail(&ch->collect_queue, skb);
-+                ch->collect_len += skb->len;
-+                kfree(p_header);
-+
-+                spin_unlock_irqrestore(&ch->collect_lock, saveflags);
-+        } else
-+        {
-+                __u16 block_len;
-+                int ccw_idx;
-+                struct sk_buff *nskb;
-+                unsigned long hi;
-+
-+                /**
-+                 * Protect skb against beeing free'd by upper
-+                 * layers.
-+                 */
-+                atomic_inc(&skb->users);
-+
-+                block_len = skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
-+
-+                /**
-+                 * IDAL support in CTC is broken, so we have to
-+                 * care about skb's above 2G ourselves.
-+                 */
-+                hi = ((unsigned long)skb->tail + TH_HEADER_LENGTH) >> 31;
-+                if(hi)
-+                {
-+                        nskb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
-+
-+                        if(!nskb)
-+                        {
-+                                printk(KERN_WARNING 
-+                                       ": OUT OF MEMORY IN %s(): Data Lost \n",
-+                                       __FUNCTION__);
-+                                atomic_dec(&skb->users);
-+                                dev_kfree_skb_any(skb);
-+                                fsm_event(privptr->mpcg->fsm,
-+                                          MPCG_EVENT_INOP,dev);
-+                                goto done;
-+                        } else
-+                        {
-+                                memcpy(skb_put(nskb, skb->len),
-+                                       skb->data, skb->len);
-+                                atomic_inc(&nskb->users);
-+                                atomic_dec(&skb->users);
-+                                dev_kfree_skb_any(skb);
-+                                skb = nskb;
-+                        }
-+                }
-+
-+                p_header = (pdu *)kmalloc(PDU_HEADER_LENGTH, gfp_type());
-+
-+                if(!p_header)
-+                {
-+                        printk(KERN_WARNING 
-+                               "ctcmpc: OUT OF MEMORY IN %s(): Data Lost \n",
-+                               __FUNCTION__);
-+                        atomic_dec(&skb->users);
-+                        dev_kfree_skb_any(skb);
-+                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
-+                        goto done;
-+                }
-+
-+                p_header->pdu_offset = skb->len;
-+                p_header->pdu_proto = 0x01;
-+                p_header->pdu_flag = 0x00;
-+                p_header->pdu_seq = 0;
-+                if(skb->protocol == ntohs(ETH_P_SNAP))
-+                {
-+                        p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;   
-+                } else
-+                {
-+                        p_header->pdu_flag |= PDU_FIRST; 
-+                }
-+                memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header,
-+                       PDU_HEADER_LENGTH);
-+
-+                kfree(p_header);
-+
-+                if(ch->collect_len > 0)
-+                {
-+                        spin_lock_irqsave(&ch->collect_lock, saveflags);
-+                        skb_queue_tail(&ch->collect_queue, skb);
-+                        ch->collect_len += skb->len;
-+                        skb = skb_dequeue(&ch->collect_queue);
-+                        ch->collect_len -= skb->len;
-+                        spin_unlock_irqrestore(&ch->collect_lock, saveflags);
-+                }
-+
-+                p_header = (pdu *)skb->data;  
-+                p_header->pdu_flag |= PDU_LAST; 
-+
-+                ch->prof.txlen += skb->len - PDU_HEADER_LENGTH;
-+
-+                th_header *header;
-+                header = (th_header *)kmalloc(TH_HEADER_LENGTH, gfp_type());
-+
-+                if(!header)
-+                {
-+                        printk(KERN_WARNING 
-+                               "ctcmpc: OUT OF MEMORY IN %s(): Data Lost \n",
-+                               __FUNCTION__);
-+                        atomic_dec(&skb->users);
-+                        dev_kfree_skb_any(skb);
-+                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
-+                        goto done;
-+                }
-+
-+                header->th_seg = 0x00;
-+                header->th_ch_flag = TH_HAS_PDU;  /* Normal data */
-+                header->th_blk_flag = 0x00;
-+                header->th_is_xid = 0x00;          /* Just data here */
-+                ch->th_seq_num++;
-+                header->th_seq_num = ch->th_seq_num;
-+
-+#ifdef DEBUGSEQ
-+                printk(KERN_INFO "%s: ToVTAM_th_seq= %08x\n" ,
-+                       __FUNCTION__,ch->th_seq_num);
-+#endif
-+
-+                memcpy(skb_push(skb, TH_HEADER_LENGTH), header,
-+                       TH_HEADER_LENGTH);       /*  put the TH on the packet */
-+
-+                kfree(header);
-+
-+#ifdef DEBUGDATA
-+                if(skb->len > 32) out_len = 32;
-+                else out_len = skb->len;
-+                printk(KERN_INFO "%s(): skb len: %04x \n", 
-+                       __FUNCTION__,skb->len);
-+                printk(KERN_INFO 
-+                       "%s(): pdu header and data for up to "
-+                       "32 bytes sent to vtam\n", 
-+                       __FUNCTION__);
-+                dumpit((char *)skb->data,out_len);
-+#endif
-+
-+                ch->ccw[4].count = skb->len;
-+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21))
-+                if(set_normalized_cda(&ch->ccw[4], virt_to_phys(skb->data)))
-+                {
-+#else
-+                if(set_normalized_cda(&ch->ccw[4], skb->data))
-+                {
-+#endif
-+                        /**
-+                         * idal allocation failed, try via copying to
-+                         * trans_skb. trans_skb usually has a pre-allocated
-+                         * idal.
-+                         */
-+                        if(ctcmpc_checkalloc_buffer(ch, 1))
-+                        {
-+                                /**
-+                                 * We are toast.  Data lost.
-+                                 */
-+                                atomic_dec(&skb->users);
-+                                dev_kfree_skb_any(skb);
-+                                printk(KERN_WARNING 
-+                                       "ctcmpc: OUT OF MEMORY IN %s():"
-+                                       " Data Lost \n",
-+                                       __FUNCTION__);
-+                                fsm_event(privptr->mpcg->fsm,
-+                                          MPCG_EVENT_INOP,dev);
-+                                goto done;
-+                        }
-+
-+                        ch->trans_skb->tail = ch->trans_skb->data;
-+                        ch->trans_skb->len = 0;
-+                        ch->ccw[1].count = skb->len;
-+                        memcpy(skb_put(ch->trans_skb, skb->len), skb->data,
-+                               skb->len);
-+                        atomic_dec(&skb->users);
-+                        dev_kfree_skb_any(skb);
-+                        ccw_idx = 0;
-+#ifdef DEBUGDATA
-+                        if(ch->trans_skb->len > 32) out_len = 32;
-+                        else out_len = ch->trans_skb->len;
-+                        printk(KERN_INFO 
-+                               "%s(): TRANS skb len: %d \n", 
-+                               __FUNCTION__,ch->trans_skb->len);
-+                        printk(KERN_INFO 
-+                               "%s(): up to 32 bytes of data sent to vtam\n", 
-+                               __FUNCTION__);
-+                        dumpit((char *)ch->trans_skb->data,out_len);
-+#endif
-+
-+
-+                } else
-+                {
-+                        skb_queue_tail(&ch->io_queue, skb);
-+                        ccw_idx = 3;
-+                }
-+
-+                ch->retry = 0;
-+                fsm_newstate(ch->fsm, CH_STATE_TX);
-+                fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC,
-+                             CH_EVENT_TIMER, ch);
-+
-+#ifdef DEBUGCCW
-+                dumpit((char *)&ch->ccw[ccw_idx],sizeof(ccw1_t) * 3);
-+#endif
-+
-+                s390irq_spin_lock_irqsave(ch->irq, saveflags);
-+                ch->prof.send_stamp = xtime;
-+
-+
-+                rc = do_IO(ch->irq, &ch->ccw[ccw_idx], (intparm_t)ch, 0xff, 0);
-+
-+                s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
-+
-+                if(ccw_idx == 3)
-+                        ch->prof.doios_single++;
-+                if(rc != 0)
-+                {
-+                        /* Not all rc from do_IO are bad.  ccw_check will     */
-+                        /* handle cases in which this data will not be retried*/
-+                        ccw_check_return_code(ch, rc);
-+                } else
-+                {
-+                        if(ccw_idx == 0)
-+                        {
-+                                privptr->stats.tx_packets++;
-+                                privptr->stats.tx_bytes +=
-+                                skb->len - TH_HEADER_LENGTH;
-+                        }
-+                }
-+                if(ch->th_seq_num > 0xf0000000)
-+                {   /* Chose 4Billion at random.  */ 
-+                        ctcmpc_send_sweep_req(ch);
-+                }
-+        }
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
-+#endif
-+        return(0);
-+}
-+
-+/**
-+ * Interface API for upper network layers
-+ *****************************************************************************/
-+
-+/**
-+ * Open an interface.
-+ * Called from generic network layer when ifconfig up is run.
-+ *
-+ * @param dev Pointer to interface struct.
-+ *
-+ * @return 0 on success, -ERRNO on failure. (Never fails.)
-+ */
-+static int 
-+ctcmpc_open(net_device *dev)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        MOD_INC_USE_COUNT;
-+        /* for MPC this is called from ctc_mpc_alloc_channel */
-+        /*fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_START, dev);*/
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return 0;
-+}
-+
-+/**
-+ * Close an interface.
-+ * Called from generic network layer when ifconfig down is run.
-+ *
-+ * @param dev Pointer to interface struct.
-+ *
-+ * @return 0 on success, -ERRNO on failure. (Never fails.)
-+ */
-+static int 
-+ctcmpc_close(net_device *dev)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        SET_DEVICE_START(dev, 0);
-+        /*Now called from mpc close only         */
-+        /*fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_STOP, dev);*/
-+        MOD_DEC_USE_COUNT;
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return 0;
-+}
-+
-+/**
-+ * Start transmission of a packet.
-+ * Called from generic network device layer.
-+ *
-+ * @param skb Pointer to buffer containing the packet.
-+ * @param dev Pointer to interface struct.
-+ *
-+ * @return 0 if packet consumed, !0 if packet rejected.
-+ *         Note: If we return !0, then the packet is free'd by
-+ *               the generic network layer.
-+ */
-+static int 
-+ctcmpc_tx(struct sk_buff *skb, net_device *dev)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:%s() skb:%0lx\n", 
-+               __FUNCTION__,(unsigned long)skb);
-+#endif
-+
-+        int len;
-+        ctc_priv  *privptr = (ctc_priv *)dev->priv;
-+        mpc_group *grpptr  = privptr->mpcg;
-+
-+        /**
-+         * Some sanity checks ...
-+         */
-+        if(skb == NULL)
-+        {
-+                printk(KERN_INFO 
-+                       "%s: NULL sk_buff passed - EMPTY PACKET DROPPED\n", 
-+                       dev->name);
-+                privptr->stats.tx_dropped++;
-+                goto done;
-+        }
-+        if(skb_headroom(skb) < (TH_HEADER_LENGTH + PDU_HEADER_LENGTH))
-+        {
-+                printk(KERN_INFO
-+                       "%s: Got sk_buff with head room < %ld bytes\n",
-+                       dev->name, TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
-+                if(skb->len > 32) len = 32;
-+                else len = skb->len;
-+#ifdef DEBUGDATA
-+                dumpit((char *)skb->data,len);
-+#endif
-+                len =  skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
-+                sk_buff *newskb = __dev_alloc_skb(len,gfp_type() | GFP_DMA);
-+
-+                if(!newskb)
-+                {
-+                        printk(KERN_WARNING 
-+                               "OUT OF MEMORY - in %s Data Lost\n",
-+                               __FUNCTION__);
-+                        printk(KERN_WARNING 
-+                               "%s: DEVICE ERROR - UNRECOVERABLE DATA LOSS\n",
-+                               __FUNCTION__);
-+                        dev_kfree_skb_any(skb);
-+                        privptr->stats.tx_dropped++;
-+                        privptr->stats.tx_errors++;
-+                        privptr->stats.tx_carrier_errors++;
-+                        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+                        goto done;
-+                }
-+                newskb->protocol = skb->protocol;
-+                skb_reserve(newskb,TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
-+                memcpy(skb_put(newskb,skb->len),skb->data,skb->len);
-+                dev_kfree_skb_any(skb);
-+                skb = newskb;
-+        }
-+
-+        /**
-+         * If channels are not running, 
-+         * notify anybody about a link failure and throw
-+         * away packet. 
-+         */
-+        if((fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) || 
-+           (fsm_getstate(grpptr->fsm) <  MPCG_STATE_XID2INITW))
-+        {
-+                dev_kfree_skb_any(skb);
-+                printk(KERN_INFO 
-+                       "%s(): DATA RCVD - MPC GROUP NOT ACTIVE - DROPPED\n",
-+                       __FUNCTION__);
-+                privptr->stats.tx_dropped++;
-+                privptr->stats.tx_errors++;
-+                privptr->stats.tx_carrier_errors++;
-+                goto done;
-+        }
-+
-+        if(ctcmpc_test_and_set_busy(dev))
-+        {
-+                printk(KERN_WARNING 
-+                       "%s: DEVICE ERROR - UNRECOVERABLE DATA LOSS\n",
-+                       __FUNCTION__);
-+                dev_kfree_skb_any(skb);
-+                privptr->stats.tx_dropped++;
-+                privptr->stats.tx_errors++;
-+                privptr->stats.tx_carrier_errors++;
-+                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+                goto done;
-+        }
-+
-+        dev->trans_start = jiffies;
-+        if(transmit_skb(privptr->channel[WRITE], skb) != 0)
-+        {
-+                printk(KERN_WARNING 
-+                       "ctcmpc: DEVICE ERROR in %s(): Data Lost \n",
-+                       __FUNCTION__);
-+                printk(KERN_WARNING 
-+                       "%s: DEVICE ERROR - UNRECOVERABLE DATA LOSS\n",
-+                       __FUNCTION__);
-+                dev_kfree_skb_any(skb);
-+                privptr->stats.tx_dropped++;
-+                privptr->stats.tx_errors++;
-+                privptr->stats.tx_carrier_errors++;
-+                ctcmpc_clear_busy(dev);
-+                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
-+                goto done;
-+        }
-+        ctcmpc_clear_busy(dev);
-+
-+        done:
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return(0);      /*handle freeing of skb here */
-+}
-+
-+
-+/**
-+ * Sets MTU of an interface.
-+ *
-+ * @param dev     Pointer to interface struct.
-+ * @param new_mtu The new MTU to use for this interface.
-+ *
-+ * @return 0 on success, -EINVAL if MTU is out of valid range.
-+ *         (valid range is 576 .. 65527). If VM is on the
-+ *         remote side, maximum MTU is 32760, however this is
-+ *         <em>not</em> checked here.
-+ */
-+static int 
-+ctcmpc_change_mtu(net_device *dev, int new_mtu)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        ctc_priv  *privptr = (ctc_priv *)dev->priv;
-+
-+        if((new_mtu < 576) || (new_mtu > 65527) ||
-+           (new_mtu > (privptr->channel[READ]->max_bufsize -
-+                       TH_HEADER_LENGTH )))
-+                return -EINVAL;
-+        dev->mtu = new_mtu;
-+        /* TH plus 4 byte sequence number on outbound path */
-+        dev->hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return 0;
-+}
-+
-+
-+/**
-+ * Returns interface statistics of a device.
-+ *
-+ * @param dev Pointer to interface struct.
-+ *
-+ * @return Pointer to stats struct of this interface.
-+ */
-+static struct net_device_stats 
-+*ctcmpc_stats(net_device *dev)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return &((ctc_priv *)dev->priv)->stats;
-+}
-+
-+/**
-+ * procfs related structures and routines
-+ *****************************************************************************/
-+
-+static net_device 
-+*find_netdev_by_ino(unsigned long ino)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        channel *ch = channels;
-+        net_device *dev = NULL;
-+        ctc_priv *privptr;
-+
-+        while(ch)
-+        {
-+                if(ch->netdev != dev)
-+                {
-+                        dev = ch->netdev;
-+                        privptr = (ctc_priv *)dev->priv;
-+
-+                        if((privptr->proc_ctrl_entry->low_ino == ino) ||
-+                           (privptr->proc_stat_entry->low_ino == ino))
-+                                return dev;
-+                }
-+                ch = ch->next;
-+        }
-+        return NULL;
-+}
-+
-+#define CTRL_BUFSIZE 40
-+
-+static int 
-+ctcmpc_ctrl_open(struct inode *inode, struct file *file)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL);
-+        if(file->private_data == NULL)
-+                return -ENOMEM;
-+        MOD_INC_USE_COUNT;
-+        return 0;
-+}
-+
-+static int 
-+ctcmpc_ctrl_close(struct inode *inode, struct file *file)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        kfree(file->private_data);
-+        MOD_DEC_USE_COUNT;
-+        return 0;
-+}
-+
-+static ssize_t 
-+ctcmpc_ctrl_write(struct file *file, const char *buf, size_t count,
-+                  loff_t *off)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
-+        net_device   *dev;
-+        ctc_priv     *privptr;
-+        char         *e;
-+        int          bs1;
-+        char         tmp[40];
-+
-+        return 0;
-+        /* This function is currently disabled in this MPC environment */
-+        if(!(dev = find_netdev_by_ino(ino)))
-+                return -ENODEV;
-+        if(off != &file->f_pos)
-+                return -ESPIPE;
-+
-+        privptr = (ctc_priv *)dev->priv;
-+
-+        if(count >= 39)
-+                return -EINVAL;
-+
-+        if(copy_from_user(tmp, buf, count))
-+                return -EFAULT;
-+        tmp[count+1] = '\0';
-+        bs1 = simple_strtoul(tmp, &e, 0);
-+
-+        if((bs1 > CTC_BUFSIZE_LIMIT) ||
-+           (e && (!isspace(*e))))
-+                return -EINVAL;
-+        if((dev->flags & IFF_RUNNING) &&
-+           (bs1 < (dev->mtu + TH_HEADER_LENGTH )))
-+                return -EINVAL;
-+        if(bs1 < (576 + TH_HEADER_LENGTH ))
-+                return -EINVAL;
-+
-+
-+        privptr->channel[READ]->max_bufsize =
-+        privptr->channel[WRITE]->max_bufsize = bs1;
-+        if(!(dev->flags & IFF_RUNNING))
-+                dev->mtu = bs1 - TH_HEADER_LENGTH ;
-+        privptr->channel[READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
-+        privptr->channel[WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
-+
-+        return count;
-+}
-+
-+static ssize_t 
-+ctcmpc_ctrl_read(struct file *file, char *buf, size_t count,
-+                 loff_t *off)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
-+        char *sbuf = (char *)file->private_data;
-+        net_device *dev;
-+        ctc_priv *privptr;
-+        ssize_t ret = 0;
-+        char *p = sbuf;
-+        int l;
-+
-+        if(!(dev = find_netdev_by_ino(ino)))
-+                return -ENODEV;
-+        if(off != &file->f_pos)
-+                return -ESPIPE;
-+
-+        privptr = (ctc_priv *)dev->priv;
-+
-+        if(file->f_pos == 0)
-+                sprintf(sbuf, "%d\n", privptr->channel[READ]->max_bufsize);
-+
-+        l = strlen(sbuf);
-+        p = sbuf;
-+        if(file->f_pos < l)
-+        {
-+                p += file->f_pos;
-+                l = strlen(p);
-+                ret = (count > l) ? l : count;
-+                if(copy_to_user(buf, p, ret))
-+                        return -EFAULT;
-+        }
-+        file->f_pos += ret;
-+        return ret;
-+}
-+
-+#define STATS_BUFSIZE 2048
-+
-+static int 
-+ctcmpc_stat_open(struct inode *inode, struct file *file)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL);
-+        if(file->private_data == NULL)
-+                return -ENOMEM;
-+        MOD_INC_USE_COUNT;
-+        return 0;
-+}
-+
-+static int 
-+ctcmpc_stat_close(struct inode *inode, struct file *file)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        kfree(file->private_data);
-+        MOD_DEC_USE_COUNT;
-+        return 0;
-+}
-+
-+static ssize_t 
-+ctcmpc_stat_write(struct file *file, const char *buf, size_t count,
-+                  loff_t *off)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
-+        net_device *dev;
-+        ctc_priv *privptr;
-+
-+        if(!(dev = find_netdev_by_ino(ino)))
-+                return -ENODEV;
-+        privptr = (ctc_priv *)dev->priv;
-+        privptr->channel[WRITE]->prof.maxmulti = 0;
-+        privptr->channel[WRITE]->prof.maxcqueue = 0;
-+        privptr->channel[WRITE]->prof.doios_single = 0;
-+        privptr->channel[WRITE]->prof.doios_multi = 0;
-+        privptr->channel[WRITE]->prof.txlen = 0;
-+        privptr->channel[WRITE]->prof.tx_time = 0;
-+        return count;
-+}
-+
-+static ssize_t 
-+ctcmpc_stat_read(struct file *file, char *buf, size_t count,
-+                 loff_t *off)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
-+        char *sbuf = (char *)file->private_data;
-+        net_device *dev;
-+        ctc_priv *privptr;
-+        ssize_t ret = 0;
-+        char *p = sbuf;
-+        int l;
-+
-+        if(!(dev = find_netdev_by_ino(ino)))
-+                return -ENODEV;
-+        if(off != &file->f_pos)
-+                return -ESPIPE;
-+
-+        privptr = (ctc_priv *)dev->priv;
-+
-+        if(file->f_pos == 0)
-+        {
-+                p += sprintf(p, "Device FSM state: %s\n",
-+                             fsm_getstate_str(privptr->fsm));
-+                p += sprintf(p, "RX channel FSM state: %s\n",
-+                             fsm_getstate_str(privptr->channel[READ]->fsm));
-+                p += sprintf(p, "TX channel FSM state: %s\n",
-+                             fsm_getstate_str(privptr->channel[WRITE]->fsm));
-+                p += sprintf(p, "Max. TX buffer used: %ld\n",
-+                             privptr->channel[WRITE]->prof.maxmulti);
-+                p += sprintf(p, "Max. chained SKBs: %ld\n",
-+                             privptr->channel[WRITE]->prof.maxcqueue);
-+                p += sprintf(p, "TX single write ops: %ld\n",
-+                             privptr->channel[WRITE]->prof.doios_single);
-+                p += sprintf(p, "TX multi write ops: %ld\n",
-+                             privptr->channel[WRITE]->prof.doios_multi);
-+                p += sprintf(p, "Netto bytes written: %ld\n",
-+                             privptr->channel[WRITE]->prof.txlen);
-+                p += sprintf(p, "Max. TX IO-time: %ld\n",
-+                             privptr->channel[WRITE]->prof.tx_time);
-+        }
-+        l = strlen(sbuf);
-+        p = sbuf;
-+        if(file->f_pos < l)
-+        {
-+                p += file->f_pos;
-+                l = strlen(p);
-+                ret = (count > l) ? l : count;
-+                if(copy_to_user(buf, p, ret))
-+                        return -EFAULT;
-+        }
-+        file->f_pos += ret;
-+        return ret;
-+}
-+
-+static struct file_operations ctcmpc_stat_fops = {
-+        read:    ctcmpc_stat_read,
-+        write:   ctcmpc_stat_write,
-+        open:    ctcmpc_stat_open,
-+        release: ctcmpc_stat_close,
-+};
-+
-+static struct file_operations ctcmpc_ctrl_fops = {
-+        read:    ctcmpc_ctrl_read,
-+        write:   ctcmpc_ctrl_write,
-+        open:    ctcmpc_ctrl_open,
-+        release: ctcmpc_ctrl_close,
-+};
-+
-+static struct inode_operations ctcmpc_stat_iops = {
-+};
-+static struct inode_operations ctcmpc_ctrl_iops = {
-+};
-+
-+static struct proc_dir_entry stat_entry = {
-+        0,                           /* low_ino */
-+        10,                          /* namelen */
-+        "statistics",                /* name    */
-+        S_IFREG | S_IRUGO | S_IWUSR, /* mode    */
-+        1,                           /* nlink   */
-+        0,                           /* uid     */
-+        0,                           /* gid     */
-+        0,                           /* size    */
-+        &ctcmpc_stat_iops               /* ops     */
-+};
-+
-+static struct proc_dir_entry ctrl_entry = {
-+        0,                           /* low_ino */
-+        10,                          /* namelen */
-+        "buffersize",                /* name    */
-+        S_IFREG | S_IRUSR | S_IWUSR, /* mode    */
-+        1,                           /* nlink   */
-+        0,                           /* uid     */
-+        0,                           /* gid     */
-+        0,                           /* size    */
-+        &ctcmpc_ctrl_iops               /* ops     */
-+};
-+
-+static struct proc_dir_entry *ctcmpc_dir = NULL;
-+static struct proc_dir_entry *ctcmpc_template = NULL;
-+
-+/**
-+ * Create the driver's main directory /proc/net/ctc
-+ */
-+static void 
-+ctcmpc_proc_create_main(void)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        /**
-+         * If not registered, register main proc dir-entry now
-+         */
-+        if(!ctcmpc_dir)
-+                ctcmpc_dir = proc_mkdir("mpc", proc_net);
-+
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc exit:%s() ctcmpc_dir:%lx\n", 
-+               __FUNCTION__,(unsigned long)ctcmpc_dir);
-+#endif
-+
-+}
-+
-+#ifdef MODULE
-+/**
-+ * Destroy /proc/net/ctc
-+ */
-+static void 
-+ctcmpc_proc_destroy_main(void)
-+{
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+        if(ctcmpc_dir)
-+                remove_proc_entry("mpc", proc_net);
-+}
-+#endif /*MODULE*/
-+
-+/**
-+ * Create a device specific subdirectory in /proc/net/ctc/ with the
-+ * same name like the device. In that directory, create 2 entries
-+ * "statistics" and "buffersize".
-+ *
-+ * @param dev The device for which the subdirectory should be created.
-+ *
-+ */
-+static void 
-+ctcmpc_proc_create_sub(net_device *dev)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        ctc_priv *privptr = (ctc_priv *)dev->priv;
-+
-+        privptr->proc_dentry = proc_mkdir(dev->name, ctcmpc_dir);
-+        privptr->proc_stat_entry =
-+        create_proc_entry("statistics",
-+                          S_IFREG | S_IRUSR | S_IWUSR,
-+                          privptr->proc_dentry);
-+        privptr->proc_stat_entry->proc_fops = &ctcmpc_stat_fops;
-+        privptr->proc_stat_entry->proc_iops = &ctcmpc_stat_iops;
-+        privptr->proc_ctrl_entry =
-+        create_proc_entry("buffersize",
-+                          S_IFREG | S_IRUSR | S_IWUSR,
-+                          privptr->proc_dentry);
-+        privptr->proc_ctrl_entry->proc_fops = &ctcmpc_ctrl_fops;
-+        privptr->proc_ctrl_entry->proc_iops = &ctcmpc_ctrl_iops;
-+        privptr->proc_registered = 1;
-+}
-+
-+
-+/**
-+ * Destroy a device specific subdirectory.
-+ *
-+ * @param privptr Pointer to device private data.
-+ */
-+static void 
-+ctcmpc_proc_destroy_sub(ctc_priv *privptr)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        if(!privptr->proc_registered)
-+                return;
-+        remove_proc_entry("statistics", privptr->proc_dentry);
-+        remove_proc_entry("buffersize", privptr->proc_dentry);
-+        remove_proc_entry(privptr->proc_dentry->name, ctcmpc_dir);
-+        privptr->proc_registered = 0;
-+}
-+
-+
-+
-+#ifndef CTC_CHANDEV
-+/**
-+ * Setup related routines
-+ *****************************************************************************/
-+
-+/**
-+ * Parse a portion of the setup string describing a single device or option
-+ * providing the following syntax:
-+ *
-+ * [Device/OptionName[:int1][:int2][:int3]]
-+ *
-+ *
-+ * @param setup    Pointer to a pointer to the remainder of the parameter
-+ *                 string to be parsed. On return, the content of this
-+ *                 pointer is updated to point to the first character after
-+ *                 the parsed portion (e.g. possible start of next portion)
-+ *                 NOTE: The string pointed to must be writeable, since a
-+ *                 \0 is written for termination of the device/option name.
-+ *
-+ * @param dev_name Pointer to a pointer to the name of the device whose
-+ *                 parameters are parsed. On return, this is set to the
-+ *                 name of the device/option.
-+ *
-+ * @param ints     Pointer to an array of integer parameters. On return,
-+ *                 element 0 is set to the number of parameters found.
-+ *
-+ * @param maxip    Maximum number of ints to parse.
-+ *                 (ints[] must have size maxip+1)
-+ *
-+ * @return     0 if string "setup" was empty, !=0 otherwise
-+ */
-+static int 
-+parse_opts(char **setup, char **dev_name, int *ints, int maxip)
-+{
-+        char *cur = *setup;
-+        int i = 1;
-+        int rc = 0;
-+        int in_name = 1;
-+        int noauto = 0;
-+
-+        #ifdef DEBUG
-+        printk(KERN_INFO
-+               "ctcmpc: parse_opts(): *setup='%s', maxip=%d\n", *setup, maxip);
-+        #endif
-+        if(*setup)
-+        {
-+                *dev_name = *setup;
-+
-+                if(strncmp(cur, "noauto", 6) && strncmp(cur, "mpc", 3))
-+                {
-+                        if((*setup = strchr(cur, ':')))
-+                                *(*setup)++ = '\0';
-+                        printk(KERN_INFO
-+                               "ctcmpc: Invalid device name or option '%s'\n",
-+                               cur);
-+                        return 1;
-+                }
-+                switch(*cur)
-+                {
-+                        case 'c':
-+                                cur += 3;
-+                                break;
-+                        case 'm':
-+                                cur += 3;
-+                                break;
-+                        case 'e':
-+                                cur += 5;
-+                                break;
-+                        case 'n':
-+                                cur += 6;
-+                                *cur++ = '\0';
-+                                noauto = 1;
-+                }
-+                if(!noauto)
-+                {
-+                        while(cur &&
-+                              (*cur == '-' || isdigit(*cur)) &&
-+                              i <= maxip)
-+                        {
-+                                if(in_name)
-+                                {
-+                                        cur++;
-+                                        if(*cur == ':')
-+                                        {
-+                                                *cur++ = '\0';
-+                                                in_name = 0;
-+                                        }
-+                                } else
-+                                {
-+                                        ints[i++] =
-+                                        simple_strtoul(cur, NULL, 0);
-+        #ifdef DEBUG
-+                                        printk(KERN_INFO
-+                                               "ctcmpc: %s: ints[%d]=%d\n",
-+                                               __FUNCTION__,
-+                                               i-1, ints[i-1]);
-+        #endif
-+                                        if((cur = strchr(cur, ':')) != NULL)
-+                                                cur++;
-+                                }
-+                        }
-+                }
-+                ints[0] = i - 1;
-+                *setup = cur;
-+                if(cur && (*cur == ':'))
-+                        (*setup)++;
-+                rc = 1;
-+        }
-+        return rc;
-+}
-+
-+/**
-+ *
-+ * Allocate one param struct
-+ *
-+ * If the driver is loaded as a module this functions is called during
-+ *   module set up and we can allocate the struct by using kmalloc()
-+ *
-+ * If the driver is statically linked into the kernel this function is called
-+ * when kmalloc() is not yet available so we must allocate from a static array
-+ *
-+ */
-+        #ifdef MODULE
-+                #define alloc_param() ((param *)kmalloc(sizeof(param), 
-+                GFP_KERNEL));
-+        #else
-+static param parms_array[MAX_STATIC_DEVICES];
-+static param *next_param = parms_array;
-+                #define alloc_param() \
-+        ((next_param<parms_array+MAX_STATIC_DEVICES)?next_param++:NULL)
-+        #endif /*MODULE*/
-+
-+/**
-+ * Returns commandline parameter using device name as key.
-+ *
-+ * @param name Name of interface to get parameters from.
-+ *
-+ * @return Pointer to corresponting param struct, NULL if not found.
-+ */
-+static param 
-+*find_param(char *name)
-+{
-+        param *p = params;
-+
-+        while(p && strcmp(p->name, name))
-+                p = p->next;
-+        return p;
-+}
-+
-+/**
-+ * maximum number of integer parametes that may be specified
-+ * for one device in the setup string
-+ */
-+        #define CTC_MAX_INTPARMS 3
-+
-+/**
-+ * Parse configuration options for all interfaces.
-+ *
-+ * This function is called from two possible locations:
-+ *  - If built as module, this function is called from init_module().
-+ *  - If built in monolithic kernel, this function is called from within
-+ *    init/main.c.
-+ * Parsing is always done here.
-+ *
-+ * Valid parameters are:
-+ *
-+ *
-+ *   [NAME[:0xRRRR[:0xWWWW[:P]]]]
-+ *
-+ *     where P       is the channel protocol (always 0)
-+ *	      0xRRRR is the cu number for the read channel
-+ *	      0xWWWW is the cu number for the write channel
-+ *	      NAME   is either mpc0 ... mpcN  for sna MPC
-+ *                 or     noauto
-+ *                 which switches off auto-detection of channels.
-+ *
-+ * @param setup    The parameter string to parse. MUST be writeable!
-+ * @param ints     Pointer to an array of ints. 
-+ */
-+        #ifdef MODULE
-+static void ctcmpc_setup(char *setup)
-+                #define ctcmpc_setup_return return
-+        #else /*MODULE*/
-+static int __init ctcmpc_setup(char *setup)
-+                #define ctcmpc_setup_return return(1)
-+        #endif /*MODULE*/
-+{
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+        int write_dev;
-+        int read_dev;
-+        int proto;
-+        param *par;
-+        char *dev_name;
-+        int ints[CTC_MAX_INTPARMS+1];
-+
-+        while(parse_opts(&setup, &dev_name, ints, CTC_MAX_INTPARMS))
-+        {
-+                write_dev = -1;
-+                read_dev = -1;
-+                proto = CTC_PROTO_MPC;
-+        #ifdef DEBUG
-+                printk(KERN_INFO
-+                       "ctcmpc: ctcmpc_setup(): setup='%s' dev_name='%s',"
-+                       " ints[0]=%d)\n",
-+                       setup, dev_name, ints[0]);
-+        #endif /*DEBUG*/
-+                if(dev_name == NULL)
-+                {
-+                        /**
-+                         * happens if device name is not specified in
-+                         * parameter line (cf. init/main.c:get_options()
-+                         */
-+                        printk(KERN_INFO
-+                               "ctcmpc: %s(): Device name not specified\n",
-+                               __FUNCTION__);
-+                        ctcmpc_setup_return;
-+                }
-+
-+        #ifdef DEBUG
-+                printk(KERN_INFO "name=´%s´ argc=%d\n", dev_name, ints[0]);
-+        #endif
-+
-+                if(strcmp(dev_name, "noauto") == 0)
-+                {
-+                        printk(KERN_INFO "ctcmpc: autoprobing disabled\n");
-+                        ctc_no_auto = 1;
-+                        continue;
-+                }
-+
-+                if(find_param(dev_name) != NULL)
-+                {
-+                        printk(KERN_INFO
-+                               "ctcmpc: Definition for device %s already set. "
-+                               "Ignoring second definition\n", dev_name);
-+                        continue;
-+                }
-+
-+                switch(ints[0])
-+                {
-+                        case 3: /* protocol type passed */
-+                                proto = ints[3];
-+                                if(proto != CTC_PROTO_MPC)
-+                                {
-+                                        printk(KERN_INFO
-+                                               "%s: wrong protocol type "
-+                                               "passed\n", dev_name);
-+                                        ctcmpc_setup_return;
-+                                }
-+                        case 2: /* write channel passed */
-+                                write_dev = ints[2];
-+                        case 1: /* read channel passed */
-+                                read_dev = ints[1];
-+                                if(write_dev == -1)
-+                                        write_dev = read_dev + 1;
-+                                break;
-+                        default:
-+                                printk(KERN_INFO
-+                                       "ctcmpc: wrong number of parameter "
-+                                       "passed (is: %d, expected: [1..3]\n",
-+                                       ints[0]);
-+                                ctcmpc_setup_return;
-+                }
-+                par = alloc_param();
-+                if(!par)
-+                {
-+        #ifdef MODULE
-+                        printk(KERN_INFO
-+                               "ctcmpc: Couldn't allocate setup param block\n");
-+        #else
-+                        printk(KERN_INFO
-+                               "ctcmpc: Number of device definitions in "
-+                               " kernel commandline exceeds builtin limit "
-+                               " of %d devices.\n", MAX_STATIC_DEVICES);
-+        #endif
-+                        ctcmpc_setup_return;
-+                }
-+                par->read_dev = read_dev;
-+                par->write_dev = write_dev;
-+                par->proto = proto;
-+                strncpy(par->name, dev_name, MAX_PARAM_NAME_LEN);
-+                par->next = params;
-+                params = par;
-+        #ifdef DEBUG
-+                printk(KERN_INFO "%s: protocol=%x read=%04x write=%04x\n",
-+                       dev_name, proto, read_dev, write_dev);
-+        #endif
-+        }
-+        ctcmpc_setup_return;
-+}
-+
-+__setup("mpc=", ctcmpc_setup);
-+#endif /* !CTC_CHANDEV */
-+
-+
-+static void
-+ctcmpc_netdev_unregister(net_device *dev)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        ctc_priv *privptr;
-+
-+        if(!dev)
-+                return;
-+        privptr = (ctc_priv *)dev->priv;
-+        unregister_netdev(dev);
-+}
-+
-+static int
-+ctcmpc_netdev_register(net_device *dev)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        return register_netdev(dev);
-+}
-+
-+static void
-+ctcmpc_free_netdevice(net_device *dev, int free_dev)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        ctc_priv *privptr;
-+        mpc_group *grpptr;
-+
-+        if(!dev)
-+                return;
-+        privptr = (ctc_priv *)dev->priv;
-+        if(privptr)
-+        {
-+                grpptr = privptr->mpcg;
-+                if(privptr->fsm)
-+                        kfree_fsm(privptr->fsm);
-+                ctcmpc_proc_destroy_sub(privptr);
-+                if(grpptr)
-+                {
-+                        if(grpptr->fsm)
-+                                kfree_fsm(grpptr->fsm);
-+                        if(grpptr->xid_skb)
-+                                dev_kfree_skb(grpptr->xid_skb);
-+                        if(grpptr->rcvd_xid_skb)
-+                                dev_kfree_skb(grpptr->rcvd_xid_skb);
-+                        tasklet_kill(&grpptr->mpc_tasklet2);
-+                        kfree(grpptr);
-+                }
-+                kfree(privptr);
-+        }
-+#ifdef MODULE
-+        if(free_dev)
-+                kfree(dev);
-+#endif
-+}
-+
-+#ifdef CTC_CHANDEV
-+static int
-+ctcmpc_shutdown(net_device *dev)
-+{
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+        ctc_priv *privptr;
-+
-+        if(!dev)
-+                return 0;
-+        privptr = (ctc_priv *)dev->priv;
-+        channel_remove(privptr->channel[READ]);
-+        channel_remove(privptr->channel[WRITE]);
-+        ctcmpc_free_netdevice(dev, 0);
-+        return 0;
-+}
-+#endif
-+
-+/**
-+ * Initialize everything of the net device except the name and the
-+ * channel structs.
-+ */
-+static net_device *
-+ctcmpc_init_netdevice(net_device *dev, int alloc_device)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+        ctc_priv *privptr;
-+        mpc_group *grpptr;
-+        int      priv_size;
-+        if(alloc_device)
-+        {
-+                dev = kmalloc(sizeof(net_device)
-+                              , GFP_KERNEL);
-+                if(!dev)
-+                        return NULL;
-+#ifdef DEBUG
-+                printk(KERN_INFO "kmalloc dev:  %s()\n", __FUNCTION__);
-+#endif
-+
-+                memset(dev, 0, sizeof(net_device));
-+        }
-+        priv_size = sizeof(ctc_priv) + sizeof(ctcmpc_template) +
-+                    sizeof(stat_entry) + sizeof(ctrl_entry);
-+        dev->priv = kmalloc(priv_size, GFP_KERNEL);
-+        if(dev->priv == NULL)
-+        {
-+                if(alloc_device)
-+                        kfree(dev);
-+                return NULL;
-+        }
-+
-+        memset(dev->priv, 0, priv_size);
-+        privptr = (ctc_priv *)dev->priv;
-+
-+        privptr->proc_dentry = (struct proc_dir_entry *)
-+                               (((char *)privptr) + sizeof(ctc_priv));
-+        privptr->proc_stat_entry = (struct proc_dir_entry *)
-+                                   (((char *)privptr) + sizeof(ctc_priv) +
-+                                    sizeof(ctcmpc_template));
-+        privptr->proc_ctrl_entry = (struct proc_dir_entry *)
-+                                   (((char *)privptr) + sizeof(ctc_priv) +
-+                                    sizeof(ctcmpc_template) + 
-+                                    sizeof(stat_entry));
-+        memcpy(privptr->proc_dentry, &ctcmpc_template, sizeof(ctcmpc_template));
-+        memcpy(privptr->proc_stat_entry, &stat_entry, sizeof(stat_entry));
-+        memcpy(privptr->proc_ctrl_entry, &ctrl_entry, sizeof(ctrl_entry));
-+
-+        privptr->fsm = init_fsm("ctcdev", dev_state_names,
-+                                dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
-+                                dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
-+        if(privptr->fsm == NULL)
-+        {
-+                kfree(privptr);
-+                privptr = NULL;
-+                if(alloc_device)
-+                        kfree(dev);
-+                return NULL;
-+        }
-+
-+        fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
-+        fsm_settimer(privptr->fsm, &privptr->restart_timer);
-+        /********************************************************/
-+        /*  MPC Group Initializations                           */
-+        /********************************************************/
-+        privptr->mpcg = kmalloc(sizeof(mpc_group),GFP_KERNEL);
-+        if(privptr->mpcg == NULL)
-+        {
-+                kfree(privptr);
-+                privptr = NULL;
-+                if(alloc_device)
-+                        kfree(dev);
-+                return NULL;
-+        }
-+        grpptr = privptr->mpcg;
-+        memset(grpptr, 0, sizeof(mpc_group));
-+
-+        grpptr->fsm = init_fsm("mpcg", mpcg_state_names,
-+                               mpcg_event_names, 
-+                               NR_MPCG_STATES, 
-+                               NR_MPCG_EVENTS,
-+                               mpcg_fsm, 
-+                               MPCG_FSM_LEN, 
-+                               GFP_KERNEL);
-+        if(grpptr->fsm == NULL)
-+        {
-+                kfree(grpptr);
-+                grpptr = NULL;
-+                kfree(privptr);
-+                privptr = NULL;
-+                if(alloc_device)
-+                        kfree(dev);
-+                return NULL;
-+        }
-+
-+        fsm_newstate(grpptr->fsm, MPCG_STATE_RESET);
-+        fsm_settimer(grpptr->fsm,&grpptr->timer);
-+
-+
-+        grpptr->xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
-+                                          GFP_ATOMIC|GFP_DMA);
-+        if(grpptr->xid_skb == NULL)
-+        {
-+                printk(KERN_INFO
-+                       "Couldn't alloc MPCgroup xid_skb\n");
-+                kfree_fsm(grpptr->fsm);
-+                grpptr->fsm = NULL;
-+                kfree(grpptr);
-+                grpptr = NULL;
-+                kfree(privptr);
-+                privptr = NULL;
-+                if(alloc_device)
-+                        kfree(dev);
-+                return NULL;
-+        }
-+        /*  base xid for all channels in group  */
-+        grpptr->xid_skb_data = grpptr->xid_skb->data;
-+        grpptr->xid_th = (th_header *)grpptr->xid_skb->data;
-+        memcpy(skb_put(grpptr->xid_skb,
-+                       TH_HEADER_LENGTH),
-+               &thnorm,
-+               TH_HEADER_LENGTH);
-+
-+        privptr->xid = grpptr->xid = (xid2 *)grpptr->xid_skb->tail;
-+        memcpy(skb_put(grpptr->xid_skb,XID2_LENGTH),&init_xid,XID2_LENGTH);
-+        privptr->xid->xid2_adj_id = jiffies | 0xfff00000;
-+        privptr->xid->xid2_sender_id = jiffies;
-+
-+        grpptr->xid_id = (char *)grpptr->xid_skb->tail;
-+        memcpy(skb_put(grpptr->xid_skb,4),"VTAM",4);
-+
-+
-+        grpptr->rcvd_xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
-+                                               GFP_ATOMIC|GFP_DMA);
-+        if(grpptr->rcvd_xid_skb == NULL)
-+        {
-+                printk(KERN_INFO
-+                       "Couldn't alloc MPCgroup rcvd_xid_skb\n");
-+                kfree_fsm(grpptr->fsm);
-+                grpptr->fsm = NULL;
-+                dev_kfree_skb(grpptr->xid_skb);
-+                grpptr->xid_skb = NULL;
-+                grpptr->xid_id = NULL;
-+                grpptr->xid_skb_data = NULL;
-+                grpptr->xid_th = NULL;
-+                kfree(grpptr);
-+                grpptr = NULL;
-+                privptr->xid = NULL;
-+                kfree(privptr);
-+                privptr = NULL;
-+                if(alloc_device)
-+                        kfree(dev);
-+                return NULL;
-+        }
-+
-+        grpptr->rcvd_xid_data =  grpptr->rcvd_xid_skb->data;
-+        grpptr->rcvd_xid_th =   (th_header *)grpptr->rcvd_xid_skb->data;
-+        memcpy(skb_put(grpptr->rcvd_xid_skb,TH_HEADER_LENGTH),
-+               &thnorm,
-+               TH_HEADER_LENGTH);
-+        grpptr->saved_xid2 = NULL;
-+
-+        tasklet_init(&grpptr->mpc_tasklet2,
-+                     ctcmpc_group_ready,
-+                     (unsigned long)dev);
-+        /********************************************************/
-+        /*  MPC Group Initializations							 */
-+        /********************************************************/
-+
-+
-+        dev->mtu                 = 
-+                MPC_BUFSIZE_DEFAULT - 
-+                TH_HEADER_LENGTH - 
-+                PDU_HEADER_LENGTH;
-+        dev->hard_start_xmit     = ctcmpc_tx;
-+        dev->open                = ctcmpc_open;
-+        dev->stop                = ctcmpc_close;
-+        dev->get_stats           = ctcmpc_stats;
-+        dev->change_mtu          = ctcmpc_change_mtu;
-+        dev->hard_header_len     = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
-+        dev->addr_len            = 0;
-+        dev->type                = ARPHRD_SLIP;
-+        dev->tx_queue_len        = 100;
-+        SET_DEVICE_START(dev, 1);
-+        dev_init_buffers(dev);
-+        dev->flags               = IFF_POINTOPOINT | IFF_NOARP;
-+        return dev;
-+}
-+
-+#ifdef CTC_CHANDEV
-+static void
-+ctcmpc_chandev_msck_notify(void *dev, int msck_irq,
-+                           chandev_msck_status prevstatus,
-+                           chandev_msck_status newstatus)
-+{
-+
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+        net_device *device = (net_device *)dev;
-+        ctc_priv *privptr;
-+        int direction;
-+
-+        if(!dev)
-+                return;
-+
-+        privptr = device->priv;
-+        if(prevstatus == chandev_status_revalidate)
-+                for(direction = READ; direction <= WRITE; direction++)
-+                {
-+                        channel *ch = privptr->channel[direction];
-+                        if(ch->irq == msck_irq)
-+                        {
-+                                s390_dev_info_t devinfo;
-+
-+                                if(get_dev_info_by_irq(ch->irq, &devinfo))
-+                                        ch->devno = devinfo.devno;
-+                                else
-+                                        printk(KERN_INFO
-+                                               "ctcmpc_chandev_msck_notify: "
-+                                               "get_dev_info_by_irq failed for "
-+                                               "irq %d\n", ch->irq);
-+                        }
-+                }
-+        switch(newstatus)
-+        {
-+                case chandev_status_not_oper:
-+                case chandev_status_no_path:
-+                case chandev_status_gone:
-+                        for(direction = READ; direction <= WRITE; direction++)
-+                        {
-+                                channel *ch = privptr->channel[direction];
-+                                fsm_event(ch->fsm, CH_EVENT_MC_FAIL, ch);
-+                        }
-+                        printk(KERN_INFO
-+                               "ctcmpc: %s channel deactivated\n", 
-+                               device->name);
-+                        break;
-+                case chandev_status_all_chans_good:
-+                        for(direction = READ; direction <= WRITE; direction++)
-+                        {
-+                                channel *ch = privptr->channel[direction];
-+                                fsm_event(ch->fsm, CH_EVENT_MC_GOOD, ch);
-+                        }
-+                        printk(KERN_INFO
-+                               "ctcmpc: %s channel activated\n", device->name);
-+                        break;
-+                default:
-+                        break;
-+        }
-+}
-+
-+/**
-+ *
-+ * Setup an interface.
-+ *
-+ * Like ctcmpc_setup(),ctcmpc_probe()can be called from two different locations:
-+ *  - If built as module, it is called from within init_module().
-+ *  - If built in monolithic kernel, it is called from within generic network
-+ *    layer during initialization for every corresponding device, declared in
-+ *    drivers/net/Space.c
-+ *
-+ * @param dev Pointer to net_device to be initialized.
-+ *
-+ * @returns 0 on success, !0 on failure.
-+ */
-+static int 
-+ctcmpc_chandev_probe(chandev_probeinfo *info)
-+{
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+        int               devno[2];
-+        __u16             proto;
-+        int               rc;
-+        int               direction;
-+        channel_type_t    type;
-+        ctc_priv          *privptr;
-+        net_device        *dev;
-+
-+        ctcmpc_proc_create_main();
-+
-+
-+        switch(info->chan_type)
-+        {
-+                case chandev_type_ctcmpc:
-+                        type = channel_type_mpc;
-+                        break;
-+                case chandev_type_escon:
-+                        type = channel_type_escon;
-+                        break;
-+                default:
-+                        printk(KERN_INFO "ctcmpc_chandev_probe called with "
-+                               "unsupported channel type %d\n", 
-+                               info->chan_type);
-+                        return -ENODEV;
-+        }
-+        devno[READ]  = info->read.devno;
-+        devno[WRITE] = info->write.devno;
-+        proto        = info->port_protocol_no;
-+
-+        if(ctcmpc_add_channel(info->read.irq, info->read.devno, type))
-+                return -ENOMEM;
-+        if(ctcmpc_add_channel(info->write.irq, info->write.devno, type))
-+                return -ENOMEM;
-+
-+        dev = ctcmpc_init_netdevice(NULL, 1);
-+
-+
-+        if(!dev)
-+        {
-+                printk(KERN_INFO "ctcmpc_init_netdevice failed\n");
-+                return -ENODEV;
-+        }
-+
-+        chandev_build_device_name(info, dev->name, "mpc", 1);
-+
-+        privptr = (ctc_priv *)dev->priv;
-+        privptr->protocol = proto;
-+        privptr->xid = (xid2 *)kmalloc(sizeof(xid2), GFP_KERNEL);
-+        if(privptr->xid == NULL)
-+        {
-+                printk(KERN_INFO "ctcmpc: Out of memory in chandev_probe\n");
-+                return -1;
-+        }
-+        for(direction = READ; direction <= WRITE; direction++)
-+        {
-+                privptr->channel[direction] =
-+                channel_get(type, devno[direction], direction);
-+                if(privptr->channel[direction] == NULL)
-+                {
-+                        if(direction == WRITE)
-+                        {
-+                                FREE_IRQ(privptr->channel[READ]->irq,
-+                                         privptr->channel[READ]->devstat);
-+                                channel_free(privptr->channel[READ]);
-+                        }
-+                        ctcmpc_free_netdevice(dev, 1);
-+                        return -ENODEV;
-+                }
-+                privptr->channel[direction]->netdev = dev;
-+                privptr->channel[direction]->protocol = proto;
-+                /*todo...who put this 35 here....make it a define*/
-+                privptr->channel[direction]->max_bufsize = 
-+                        (MPC_BUFSIZE_DEFAULT - 35);
-+                rc = REQUEST_IRQ(privptr->channel[direction]->irq,
-+                                 (void *)ctcmpc_irq_handler, SA_INTERRUPT,
-+                                 dev->name,
-+                                 privptr->channel[direction]->devstat);
-+                if(rc)
-+                {
-+                        printk(KERN_INFO
-+                               "%s: requested irq %d is busy rc=%02x\n",
-+                               dev->name, privptr->channel[direction]->irq,
-+                               rc);
-+                        if(direction == WRITE)
-+                        {
-+                                FREE_IRQ(privptr->channel[READ]->irq,
-+                                         privptr->channel[READ]->devstat);
-+                                channel_free(privptr->channel[READ]);
-+                        }
-+                        channel_free(privptr->channel[direction]);
-+                        ctcmpc_free_netdevice(dev, 1);
-+                        return -EBUSY;
-+                }
-+        }
-+        if(ctcmpc_netdev_register(dev) != 0)
-+        {
-+                ctcmpc_free_netdevice(dev, 1);
-+                return -ENODEV;
-+        }
-+
-+        /**
-+         * register subdir in /proc/net/mpc
-+         */
-+        ctcmpc_proc_create_sub(dev);
-+        strncpy(privptr->fsm->name, dev->name, sizeof(privptr->fsm->name));
-+        activated++;
-+
-+        print_banner();
-+
-+        printk(KERN_INFO
-+               "%s: read: ch %04x (irq %04x), "
-+               "write: ch %04x (irq %04x) proto: %d\n",
-+               dev->name, privptr->channel[READ]->devno,
-+               privptr->channel[READ]->irq, privptr->channel[WRITE]->devno,
-+               privptr->channel[WRITE]->irq, proto);
-+
-+        chandev_initdevice(info, dev, 0, dev->name,
-+                           (proto == CTC_PROTO_MPC)
-+                           ? chandev_category_serial_device :
-+                           chandev_category_network_device,
-+                           (chandev_unregfunc)ctcmpc_netdev_unregister);
-+        return 0;
-+}
-+#else /* ! CHANDEV */
-+/**
-+ *
-+ * Setup an interface.
-+ *
-+ * Like ctcmpc_setup(),ctcmpc_probe()can be called from two different locations:
-+ *  - If built as module, it is called from within init_module().
-+ *  - If built in monolithic kernel, it is called from within generic network
-+ *    layer during initialization for every corresponding device, declared in
-+ *    drivers/net/Space.c
-+ *
-+ * @param dev Pointer to net_device to be initialized.
-+ *
-+ * @returns 0 on success, !0 on failure.
-+ */
-+int 
-+ctcmpc_probe(net_device *dev)
-+{
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+        int            devno[2];
-+        __u16          proto;
-+        int            rc;
-+        int            direction;
-+        channel_type_t type;
-+        ctc_priv       *privptr;
-+        param          *par;
-+
-+        ctcmpc_proc_create_main();
-+
-+        /**
-+         * Scan for available channels only the first time,
-+         * ctcmpc_probe gets control.
-+         */
-+        if(channels == NULL)
-+                channel_scan();
-+
-+        type = extract_channel_media(dev->name);
-+        if(type == channel_type_unknown)
-+                return -ENODEV;
-+
-+        par = find_param(dev->name);
-+        if(par)
-+        {
-+                devno[READ] = par->read_dev;
-+                devno[WRITE] = par->write_dev;
-+                proto = par->proto;
-+        } else
-+        {
-+                if(ctc_no_auto)
-+                        return -ENODEV;
-+                else
-+                {
-+                        devno[READ] = -1;
-+                        devno[WRITE] = -1;
-+                        proto = CTC_PROTO_MPC;    
-+                }
-+        }
-+
-+        #ifndef MODULE
-+        if(ctcmpc_init_netdevice(dev, 0) == NULL)
-+                return -ENODEV;
-+        #endif
-+        privptr = (ctc_priv *)dev->priv;
-+        privptr->protocol = proto;
-+
-+        for(direction = READ; direction <= WRITE; direction++)
-+        {
-+                if((ctc_no_auto == 0) || (devno[direction] == -1))
-+                        privptr->channel[direction] =
-+                        channel_get_next(type, direction);
-+                else
-+                        privptr->channel[direction] =
-+                        channel_get(type, devno[direction], direction);
-+                if(privptr->channel[direction] == NULL)
-+                {
-+                        if(direction == WRITE)
-+                        {
-+                                FREE_IRQ(privptr->channel[READ]->irq,
-+                                         privptr->channel[READ]->devstat);
-+                                channel_free(privptr->channel[READ]);
-+                        }
-+                        ctcmpc_free_netdevice(dev, 1);
-+                        return -ENODEV;
-+                }
-+                privptr->channel[direction]->netdev = dev;
-+                privptr->channel[direction]->protocol = proto;
-+                privptr->channel[direction]->max_bufsize = 
-+                        (MPC_BUFSIZE_DEFAULT - 35);
-+                rc = REQUEST_IRQ(privptr->channel[direction]->irq,
-+                                 (void *)ctcmpc_irq_handler, SA_INTERRUPT,
-+                                 dev->name,
-+                                 privptr->channel[direction]->devstat);
-+                if(rc)
-+                {
-+                        printk(KERN_INFO
-+                               "%s: requested irq %d is busy rc=%02x\n",
-+                               dev->name, privptr->channel[direction]->irq,
-+                               rc);
-+                        if(direction == WRITE)
-+                        {
-+                                FREE_IRQ(privptr->channel[READ]->irq,
-+                                         privptr->channel[READ]->devstat);
-+                                channel_free(privptr->channel[READ]);
-+                        }
-+                        channel_free(privptr->channel[direction]);
-+                        ctcmpc_free_netdevice(dev, 1);
-+                        return -EBUSY;
-+                }
-+        }
-+
-+        /**
-+         * register subdir in /proc/net/ctc
-+         */
-+        ctcmpc_proc_create_sub(dev);
-+
-+        print_banner();
-+
-+        printk(KERN_INFO
-+               "%s: read: ch %04x (irq %04x), "
-+               "write: ch %04x (irq %04x) proto: %d\n",
-+               dev->name, privptr->channel[READ]->devno,
-+               privptr->channel[READ]->irq, privptr->channel[WRITE]->devno,
-+               privptr->channel[WRITE]->irq, proto);
-+
-+        return 0;
-+}
-+#endif
-+
-+/**
-+ * Module related routines
-+ *****************************************************************************/
-+
-+#ifdef MODULE
-+/**
-+ * Prepare to be unloaded. Free IRQ's and release all resources.
-+ * This is called just before this module is unloaded. It is
-+ * <em>not</em> called, if the usage count is !0, so we don't need to check
-+ * for that.
-+ */
-+void 
-+cleanup_module(void)
-+{
-+        #ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+        #endif
-+
-+
-+        /* we are called if all interfaces are down only, so no need
-+         * to bother around with locking stuff
-+         */
-+        #ifndef CTC_CHANDEV
-+        while(channels)
-+        {
-+                if((channels->flags & CHANNEL_FLAGS_INUSE) &&
-+                   (channels->netdev != NULL))
-+                {
-+                        net_device *dev = channels->netdev;
-+                        ctc_priv *privptr = (ctc_priv *)dev->priv;
-+
-+                        if(privptr)
-+                        {
-+                                privptr->channel[READ]->netdev = NULL;
-+                                privptr->channel[WRITE]->netdev = NULL;
-+                        }
-+                        channels->netdev = NULL;
-+                        ctcmpc_netdev_unregister(dev);
-+                        ctcmpc_free_netdevice(dev, 1);
-+                }
-+                channel_remove(channels);
-+        }
-+        channels = NULL;
-+        #endif
-+
-+        ctcmpc_proc_destroy_main();
-+        #ifdef CTC_CHANDEV
-+        chandev_unregister(ctcmpc_chandev_probe, 1);
-+        #endif
-+        printk(KERN_INFO "MPC driver unloaded\n");
-+}
-+
-+        #define ctcmpc_init init_module
-+#endif /*MODULE*/
-+
-+/**
-+ * Initialize module.
-+ * This is called just after the module is loaded.
-+ *
-+ * @return 0 on success, !0 on error.
-+ */
-+int 
-+ctcmpc_init(void)
-+{
-+#ifdef DEBUG
-+        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
-+#endif
-+
-+#ifndef CTC_CHANDEV
-+        int   cnt;
-+        int   activated;
-+        param *par;
-+#endif
-+        int   ret = 0;
-+        int   probed = 0;
-+
-+        print_banner();
-+
-+#if defined(DEBUG) && !defined(CTC_CHANDEV)
-+        printk(KERN_INFO
-+               "ctcmpc: init_module(): got string '%s'\n", mpc);
-+#endif
-+
-+#ifndef CTC_CHANDEV
-+        #ifdef MODULE
-+        ctcmpc_setup(mpc);
-+        #endif
-+        par = params;
-+#endif
-+
-+        activated = 0;
-+
-+#ifdef CTC_CHANDEV
-+        chandev_register_and_probe(ctcmpc_chandev_probe,
-+                                   (chandev_shutdownfunc)ctcmpc_shutdown,
-+                                   ctcmpc_chandev_msck_notify,
-+                                   chandev_type_ctcmpc|chandev_type_escon);
-+#else /* CTC_CHANDEV */
-+        net_device *dev = NULL;
-+        char       *bname = "mpc";
-+
-+        cnt = 0;
-+        do
-+        {
-+                dev = ctcmpc_init_netdevice(NULL, 1);
-+                if(!dev)
-+                {
-+                        ret = -ENOMEM;
-+                        break;
-+                }
-+                if(par && par->name)
-+                {
-+                        char *p;
-+                        int  n;
-+
-+                        sprintf(dev->name, "%s", par->name);
-+                        par = par->next;
-+                        for(p = dev->name; p && *p; p++)
-+                                if(isdigit(*p))
-+                                        break;
-+                        if(p && *p)
-+                        {
-+                                n = simple_strtoul(p, NULL, 0);
-+                                if(n >= cnt)
-+                                        cnt = n + 1;
-+                        }
-+                } else
-+                {
-+                        if(ctc_no_auto)
-+                        {
-+                                ctcmpc_free_netdevice(dev, 1);
-+                                dev = NULL;
-+                                break;
-+                        }
-+                        sprintf(dev->name, "%s%d", bname,
-+                                cnt++);
-+                }
-+        #ifdef DEBUG
-+                printk(KERN_INFO "ctcmpc: %s(): probing for device %s\n",
-+                       __FUNCTION__, dev->name);
-+        #endif			
-+                probed = 1;
-+                if(ctcmpc_probe(dev) == 0)
-+                {
-+                        ctc_priv *privptr = (ctc_priv *)dev->priv;
-+        #ifdef DEBUG
-+                        printk(KERN_INFO
-+                               "ctcmpc: %s(): probing succeeded\n",
-+                               __FUNCTION__);
-+                        printk(KERN_INFO
-+                               "ctcmpc: %s(): registering device %s\n",
-+                               __FUNCTION__, dev->name);
-+        #endif
-+                        if(ctcmpc_netdev_register(dev) != 0)
-+                        {
-+                                printk(KERN_INFO
-+                                       "ctcmpc: Couldn't register %s\n",
-+                                       dev->name);
-+                                FREE_IRQ(
-+                                        privptr->channel[READ]->irq,
-+                                        privptr->channel[READ]->devstat);
-+                                FREE_IRQ(
-+                                        privptr->channel[WRITE]->irq,
-+                                        privptr->channel[WRITE]->devstat);
-+                                channel_free(privptr->channel[READ]);
-+                                channel_free(privptr->channel[WRITE]);
-+                                ctcmpc_free_netdevice(dev, 1);
-+                                dev = NULL;
-+                        } else
-+                        {
-+        #ifdef DEBUG
-+                                printk(KERN_INFO
-+                                       "ctcmpc: %s(): register succeed\n",
-+                                       __FUNCTION__);
-+        #endif			
-+                                activated++;
-+                        }
-+                } else
-+                {
-+        #ifdef DEBUG
-+                        printk(KERN_INFO
-+                               "ctcmpc: %s(): probing failed\n",
-+                               __FUNCTION__);
-+        #endif			
-+                        dev = NULL;
-+                }
-+        } while(dev && (ret == 0));
-+#endif /* CHANDEV */
-+#if !defined(CTC_CHANDEV) && defined(MODULE)
-+        if(!activated)
-+        {
-+                printk(KERN_INFO "ctcmpc: No devices registered\n");
-+                ret = -ENODEV;
-+        }
-+#endif
-+        if(ret)
-+        {
-+#if defined(CTC_CHANDEV) && defined(MODULE)
-+                chandev_unregister(ctcmpc_chandev_probe, 0);
-+#endif
-+#ifdef MODULE
-+                if(probed)
-+                        ctcmpc_proc_destroy_main();
-+#endif
-+        }
-+        return ret;
-+}
-+
-+#ifndef MODULE
-+__initcall(ctcmpc_init);
-+#endif /* MODULE */
-+
-+/* --- This is the END my friend --- */
-=== drivers/s390/net/ctctty.c
-==================================================================
---- drivers/s390/net/ctctty.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/ctctty.c   (/trunk/2.4.27)   (revision 52)
-@@ -42,7 +42,7 @@
- #define init_waitqueue_head(x) *(x)=NULL
- #define __set_current_state(state_value) \
- 	do { current->state = state_value; } while (0)
--#ifdef CONFIG_SMP
-+#ifdef __SMP__
- #define set_current_state(state_value) \
- 	do { __set_current_state(state_value); mb(); } while (0)
- #else
-=== drivers/s390/net/iucv.h
-==================================================================
---- drivers/s390/net/iucv.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/iucv.h   (/trunk/2.4.27)   (revision 52)
-@@ -449,7 +449,7 @@
-  *        buflen - Length of reply buffer.                              
-  * Output: residual_buffer - Address of buffer updated by the number 
-  *                    of bytes you have moved.              
-- *         residual_length - Contains one of the following values:
-+ *         residual_length - Contains on the the following values
-  *		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
-@@ -483,7 +483,7 @@
-  *        buffer - Address of array of reply buffers.                     
-  *        buflen - Total length of reply buffers.                         
-  * Output: residual_buffer - Address of buffer which IUCV is currently working on.
-- *         residual_length - Contains one of the following values:
-+ *         residual_length - Contains on the the following values
-  *              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
-=== drivers/s390/net/netiucv.c
-==================================================================
---- drivers/s390/net/netiucv.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/netiucv.c   (/trunk/2.4.27)   (revision 52)
-@@ -1,5 +1,5 @@
- /*
-- * $Id: netiucv.c,v 1.23 2003/06/24 16:05:32 felfert Exp $
-+ * $Id: netiucv.c,v 1.21.8.6 2004/06/29 07:37:33 braunu Exp $
-  *
-  * IUCV network driver
-  *
-@@ -28,7 +28,7 @@
-  * along with this program; if not, write to the Free Software
-  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-  *
-- * RELEASE-TAG: IUCV network driver $Revision: 1.23 $
-+ * RELEASE-TAG: IUCV network driver $Revision: 1.21.8.6 $
-  *
-  */
- 
-@@ -114,17 +114,13 @@
- 	spinlock_t               collect_lock;
- 	int                      collect_len;
- 	int                      max_buffsize;
--	int                      flags;
- 	fsm_timer                timer;
--	int                      retry;
- 	fsm_instance             *fsm;
- 	net_device               *netdev;
- 	connection_profile       prof;
- 	char                     userid[9];
- } iucv_connection;
- 
--#define CONN_FLAGS_BUFSIZE_CHANGED 1
--
- /**
-  * Linked list of all connection structs.
-  */
-@@ -590,7 +586,7 @@
- 	iucv_MessagePending *eib = (iucv_MessagePending *)ev->data;
- 	netiucv_priv *privptr = (netiucv_priv *)conn->netdev->priv;
- 
--	__u16 msglen = eib->ln1msg2.ipbfln1f;
-+	__u32 msglen = eib->ln1msg2.ipbfln1f;
- 	int rc;
- 
- #ifdef DEBUG
-@@ -613,6 +609,7 @@
- 			  conn->rx_buff->data, msglen, NULL, NULL, NULL);
- 	if (rc != 0 || msglen < 5) {
- 		privptr->stats.rx_errors++;
-+		printk(KERN_INFO "iucv_receive returned %08x\n", rc);
- 		return;
- 	}
- 	netiucv_unpack_skb(conn, conn->rx_buff);
-@@ -637,7 +634,6 @@
- #ifdef DEBUG
- 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
- #endif
--	fsm_deltimer(&conn->timer);
- 	if (conn && conn->netdev && conn->netdev->priv)
- 		privptr = (netiucv_priv *)conn->netdev->priv;
- 	conn->prof.tx_pending--;
-@@ -645,12 +641,13 @@
- 		if ((skb = skb_dequeue(&conn->commit_queue))) {
- 			atomic_dec(&skb->users);
- 			dev_kfree_skb_any(skb);
-+			if (privptr) {
-+				privptr->stats.tx_packets++;
-+				privptr->stats.tx_bytes +=
-+					(skb->len - NETIUCV_HDRLEN 
-+						  - NETIUCV_HDRLEN);
-+			}
- 		}
--		if (privptr) {
--			privptr->stats.tx_packets++;
--			privptr->stats.tx_bytes +=
--				(skb->len - NETIUCV_HDRLEN - NETIUCV_HDRLEN);
--		}
- 	}
- 	conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head;
- 	conn->tx_buff->len = 0;
-@@ -677,8 +674,6 @@
- 		memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
- 		       NETIUCV_HDRLEN);
- 
--		fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
--			     CONN_EVENT_TIMER, conn);
- 		conn->prof.send_stamp = xtime;
- 		rc = iucv_send(conn->pathid, NULL, 0, 0, 0, 0,
- 			       conn->tx_buff->data, conn->tx_buff->len);
-@@ -688,12 +683,11 @@
- 		if (conn->prof.tx_pending > conn->prof.tx_max_pending)
- 			conn->prof.tx_max_pending = conn->prof.tx_pending;
- 		if (rc != 0) {
--			fsm_deltimer(&conn->timer);
- 			conn->prof.tx_pending--;
- 			fsm_newstate(fi, CONN_STATE_IDLE);
- 			if (privptr)
- 				privptr->stats.tx_errors += txpackets;
--			printk(KERN_DEBUG "iucv_send returned %08x\n",
-+			printk(KERN_INFO "iucv_send returned %08x\n",
- 			       rc);
- 		} else {
- 			if (privptr) {
-@@ -762,6 +756,7 @@
- #ifdef DEBUG
- 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
- #endif
-+	fsm_deltimer(&conn->timer);
- 	fsm_newstate(fi, CONN_STATE_IDLE);
- 	conn->pathid = eib->ippathid;
- 	netdev->tx_queue_len = eib->ipmsglim;
-@@ -769,6 +764,19 @@
- }
- 
- static void
-+conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
-+{
-+	iucv_connection *conn = (iucv_connection *)arg;
-+	__u8 udata[16];
-+
-+	pr_debug("%s() called\n", __FUNCTION__);
-+	
-+	fsm_deltimer(&conn->timer);
-+	iucv_sever(conn->pathid, udata);
-+	fsm_newstate(fi, CONN_STATE_STARTWAIT);
-+}
-+
-+static void
- conn_action_connsever(fsm_instance *fi, int event, void *arg)
- {
- 	iucv_event *ev = (iucv_event *)arg;
-@@ -776,30 +784,17 @@
- 	// iucv_ConnectionSevered *eib = (iucv_ConnectionSevered *)ev->data;
- 	net_device *netdev = conn->netdev;
- 	netiucv_priv *privptr = (netiucv_priv *)netdev->priv;
--	int state = fsm_getstate(fi);
-+	__u8 udata[16];
- 
- #ifdef DEBUG
- 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
- #endif
--	switch (state) {
--		case CONN_STATE_SETUPWAIT:
--			printk(KERN_INFO "%s: Remote dropped connection\n",
--			       netdev->name);
--			conn->handle = 0;
--			fsm_newstate(fi, CONN_STATE_STOPPED);
--			fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
--			break;
--		case CONN_STATE_IDLE:
--		case CONN_STATE_TX:
--			printk(KERN_INFO "%s: Remote dropped connection\n",
--			       netdev->name);
--			if (conn->handle)
--				iucv_unregister_program(conn->handle);
--			conn->handle = 0;
--			fsm_newstate(fi, CONN_STATE_STOPPED);
--			fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
--			break;
--	}
-+	fsm_deltimer(&conn->timer);
-+	iucv_sever(conn->pathid, udata);
-+	printk(KERN_INFO "%s: Remote dropped connection\n",
-+	       netdev->name);
-+	fsm_newstate(fi, CONN_STATE_STARTWAIT);
-+	fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
- }
- 
- static void
-@@ -807,7 +802,7 @@
- {
- 	iucv_event *ev = (iucv_event *)arg;
- 	iucv_connection *conn = ev->conn;
--
-+	__u16 msglimit;
- 	int rc;
- 
- #ifdef DEBUG
-@@ -839,10 +834,13 @@
- 
- 	fsm_newstate(fi, CONN_STATE_SETUPWAIT);
- 	rc = iucv_connect(&(conn->pathid), NETIUCV_QUEUELEN_DEFAULT, iucvMagic,
--			  conn->userid, iucv_host, 0, NULL, NULL, conn->handle,
-+			  conn->userid, iucv_host, 0, NULL, &msglimit, conn->handle,
- 			  conn);
- 	switch (rc) {
- 		case 0:
-+			conn->netdev->tx_queue_len = msglimit;
-+			fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
-+				CONN_EVENT_TIMER, conn);
- 			return;
- 		case 11:
- 			printk(KERN_NOTICE
-@@ -910,6 +908,7 @@
- #ifdef DEBUG
- 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
- #endif
-+	fsm_deltimer(&conn->timer);
- 	fsm_newstate(fi, CONN_STATE_STOPPED);
- 	netiucv_purge_skb_queue(&conn->collect_queue);
- 	if (conn->handle)
-@@ -934,8 +933,8 @@
- static const fsm_node conn_fsm[] = {
- 	{ CONN_STATE_INVALID,   CONN_EVENT_START,    conn_action_inval      },
- 	{ CONN_STATE_STOPPED,   CONN_EVENT_START,    conn_action_start      },
--	{ CONN_STATE_STARTWAIT, CONN_EVENT_START,    conn_action_start      },
- 
-+	{ CONN_STATE_STOPPED,   CONN_EVENT_STOP,     conn_action_stop       },
- 	{ CONN_STATE_STARTWAIT, CONN_EVENT_STOP,     conn_action_stop       },
- 	{ CONN_STATE_SETUPWAIT, CONN_EVENT_STOP,     conn_action_stop       },
- 	{ CONN_STATE_IDLE,      CONN_EVENT_STOP,     conn_action_stop       },
-@@ -950,6 +949,7 @@
- 	{ CONN_STATE_TX,        CONN_EVENT_CONN_REQ, conn_action_connreject },
- 
- 	{ CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack    },
-+	{ CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER,    conn_action_conntimsev },
- 
- 	{ CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever  },
- 	{ CONN_STATE_IDLE,      CONN_EVENT_CONN_REJ, conn_action_connsever  },
-@@ -1026,6 +1026,7 @@
- dev_action_connup(fsm_instance *fi, int event, void *arg)
- {
- 	net_device   *dev = (net_device *)arg;
-+	netiucv_priv *privptr = (netiucv_priv *)dev->priv;
- 
- #ifdef DEBUG
- 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
-@@ -1034,8 +1035,8 @@
- 		case DEV_STATE_STARTWAIT:
- 			fsm_newstate(fi, DEV_STATE_RUNNING);
- 			printk(KERN_INFO
--			       "%s: connected with remote side\n",
--			       dev->name);
-+			       "%s: connected with remote side %s\n",
-+			       dev->name, privptr->conn->userid);
- 			break;
- 		case DEV_STATE_STOPWAIT:
- 			printk(KERN_INFO
-@@ -1056,9 +1057,6 @@
- static void
- dev_action_conndown(fsm_instance *fi, int event, void *arg)
- {
--	net_device   *dev = (net_device *)arg;
--	netiucv_priv *privptr = dev->priv;
--	iucv_event   ev;
- 
- #ifdef DEBUG
- 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
-@@ -1066,11 +1064,7 @@
- 	switch (fsm_getstate(fi)) {
- 		case DEV_STATE_RUNNING:
- 			fsm_newstate(fi, DEV_STATE_STARTWAIT);
--			ev.conn = privptr->conn;
--			fsm_event(privptr->conn->fsm, CONN_EVENT_START, &ev);
- 			break;
--		case DEV_STATE_STARTWAIT:
--			break;
- 		case DEV_STATE_STOPWAIT:
- 			fsm_newstate(fi, DEV_STATE_STOPPED);
- 			break;
-@@ -1085,7 +1079,6 @@
- 
- 	{ DEV_STATE_STARTWAIT, DEV_EVENT_STOP,    dev_action_stop     },
- 	{ DEV_STATE_STARTWAIT, DEV_EVENT_CONUP,   dev_action_connup   },
--	{ DEV_STATE_STARTWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
- 
- 	{ DEV_STATE_RUNNING,   DEV_EVENT_STOP,    dev_action_stop     },
- 	{ DEV_STATE_RUNNING,   DEV_EVENT_CONDOWN, dev_action_conndown },
-@@ -1141,6 +1134,7 @@
- 				       "%s: Could not allocate tx_skb\n",
- 				       conn->netdev->name);
- 				rc = -ENOMEM;
-+				return rc;
- 			} else {
- 				skb_reserve(nskb, NETIUCV_HDRLEN);
- 				memcpy(skb_put(nskb, skb->len),
-@@ -1156,10 +1150,7 @@
- 		header.next = 0;
- 		memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header,  NETIUCV_HDRLEN);
- 
--		conn->retry = 0;
- 		fsm_newstate(conn->fsm, CONN_STATE_TX);
--		fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
--			     CONN_EVENT_TIMER, conn);
- 		conn->prof.send_stamp = xtime;
- 		
- 		rc = iucv_send(conn->pathid, NULL, 0, 0, 1 /* single_flag */,
-@@ -1171,7 +1162,6 @@
- 			conn->prof.tx_max_pending = conn->prof.tx_pending;
- 		if (rc != 0) {
- 			netiucv_priv *privptr;
--			fsm_deltimer(&conn->timer);
- 			fsm_newstate(conn->fsm, CONN_STATE_IDLE);
- 			conn->prof.tx_pending--;
- 			privptr = (netiucv_priv *)conn->netdev->priv;
-@@ -1276,7 +1266,6 @@
- 	 */
- 	if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
- 		fsm_event(privptr->fsm, DEV_EVENT_START, dev);
--		dst_link_failure(skb);
- 		dev_kfree_skb(skb);
- 		privptr->stats.tx_dropped++;
- 		privptr->stats.tx_errors++;
-@@ -1375,7 +1364,6 @@
- 	file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL);
- 	if (file->private_data == NULL)
- 		return -ENOMEM;
--	*(char *)file->private_data = '\0';
- 	MOD_INC_USE_COUNT;
- 	return 0;
- }
-@@ -1427,7 +1415,6 @@
- 	privptr->conn->max_buffsize = bs1;
- 	if (!(dev->flags & IFF_RUNNING))
- 		dev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
--	privptr->conn->flags |= CONN_FLAGS_BUFSIZE_CHANGED;
- 
- 	return count;
- }
-@@ -1441,7 +1428,6 @@
- 	netiucv_priv *privptr;
- 	ssize_t ret = 0;
- 	char *p = sbuf;
--	loff_t pos = *ppos;
- 	int l;
- 
- 	if (!(dev = find_netdev_by_ino(ino)))
-@@ -1451,20 +1437,19 @@
- 
- 	privptr = (netiucv_priv *)dev->priv;
- 
--	if (!*sbuf || pos == 0)
-+	if (file->f_pos == 0)
- 		sprintf(sbuf, "%d\n", privptr->conn->max_buffsize);
- 
- 	l = strlen(sbuf);
- 	p = sbuf;
--	if (pos == (unsigned)pos && pos < l) {
--		p += pos;
-+	if (file->f_pos < l) {
-+		p += file->f_pos;
- 		l = strlen(p);
- 		ret = (count > l) ? l : count;
- 		if (copy_to_user(buf, p, ret))
- 			return -EFAULT;
- 	}
--	pos += ret;
--	*ppos = pos;
-+	file->f_pos += ret;
- 	return ret;
- }
- 
-@@ -1474,7 +1459,6 @@
- 	file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL);
- 	if (file->private_data == NULL)
- 		return -ENOMEM;
--	*(char *)file->private_data = '\0';
- 	MOD_INC_USE_COUNT;
- 	return 0;
- }
-@@ -1539,7 +1523,6 @@
- 	netiucv_priv *privptr;
- 	ssize_t ret = 0;
- 	char *p = sbuf;
--	loff_t pos = *ppos;
- 	int l;
- 
- 	if (!(dev = find_netdev_by_ino(ino)))
-@@ -1550,20 +1533,20 @@
- 	privptr = (netiucv_priv *)dev->priv;
- 
- 
--	if (!*sbuf || pos == 0)
-+	if (file->f_pos == 0)
- 		sprintf(sbuf, "%s\n",
- 			netiucv_printname(privptr->conn->userid));
- 
- 	l = strlen(sbuf);
- 	p = sbuf;
--	if (pos == (unsigned)pos && pos < l) {
--		p += pos;
-+	if (file->f_pos < l) {
-+		p += file->f_pos;
- 		l = strlen(p);
- 		ret = (count > l) ? l : count;
- 		if (copy_to_user(buf, p, ret))
- 			return -EFAULT;
--		*ppos = pos + ret;
- 	}
-+	file->f_pos += ret;
- 	return ret;
- }
- 
-@@ -1575,7 +1558,6 @@
- 	file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL);
- 	if (file->private_data == NULL)
- 		return -ENOMEM;
--	*(char *)file->private_data = '\0';
- 	MOD_INC_USE_COUNT;
- 	return 0;
- }
-@@ -1606,7 +1588,6 @@
- netiucv_stat_read(struct file *file, char *buf, size_t count, loff_t *off)
- {
- 	unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
--	loff_t pos = *ppos;
- 	char *sbuf = (char *)file->private_data;
- 	net_device *dev;
- 	netiucv_priv *privptr;
-@@ -1621,7 +1602,7 @@
- 
- 	privptr = (netiucv_priv *)dev->priv;
- 
--	if (!*sbuf || pos == 0) {
-+	if (file->f_pos == 0) {
- 		p += sprintf(p, "Device FSM state: %s\n",
- 			     fsm_getstate_str(privptr->fsm));
- 		p += sprintf(p, "Connection FSM state: %s\n",
-@@ -1645,14 +1626,14 @@
- 	}
- 	l = strlen(sbuf);
- 	p = sbuf;
--	if (pos == (unsigned)pos && pos < l) {
--		p += pos;
-+	if (file->f_pos < l) {
-+		p += file->f_pos;
- 		l = strlen(p);
- 		ret = (count > l) ? l : count;
- 		if (copy_to_user(buf, p, ret))
- 			return -EFAULT;
--		*ppos = pos + ret;
- 	}
-+	file->f_pos += ret;
- 	return ret;
- }
- 
-@@ -2059,7 +2040,7 @@
- static void
- netiucv_banner(void)
- {
--	char vbuf[] = "$Revision: 1.23 $";
-+	char vbuf[] = "$Revision: 1.21.8.6 $";
- 	char *version = vbuf;
- 
- 	if ((version = strchr(version, ':'))) {
-=== drivers/s390/net/ctcmpc.h
-==================================================================
---- drivers/s390/net/ctcmpc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/ctcmpc.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,52 @@
-+/*
-+ * $Id: ctcmpc.h,v 1.1.2.1 2004/10/04 13:28:55 ptiedem Exp $ 
-+ *
-+ * CTC / ESCON network driver, mpc interface.
-+ *
-+ * Copyright (C) 2003 IBM United States, IBM Corporation
-+ * Author(s): Belinda Thompson (belindat at us.ibm.com)
-+ *            Andy Richter (richtera at us.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.
-+ *
-+ * RELEASE-TAG: CTCMPC/ESCON network driver $Revision: 1.1.2.1 $
-+ */
-+
-+#ifndef _CTCMPC_H_
-+#define _CTCMPC_H_
-+
-+#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,3,0)
-+typedef struct net_device  net_device;
-+#else
-+typedef struct device  net_device;
-+#endif
-+
-+typedef struct sk_buff sk_buff;
-+typedef void (*callbacktypei2)(int,int);	      /* void (*void)(int,int) */
-+typedef void (*callbacktypei3)(int,int,int);	  /* void (*void)(int,int,int) */
-+
-+/*  port_number is the mpc device 0,1,2 etc mpc2 is port_number 2 */
-+/*  passive open  Just wait for XID2 exchange */
-+/*  ctc_mpc_alloc channel(port_number,
-+                                 void(*callback)(port_number,max_write_size)) */
-+extern  int ctc_mpc_alloc_channel(int,callbacktypei2);
-+/* active open  Alloc then send XID2 */
-+/*  ctc_mpc_establish_connectivity(port_number ,
-+                              void(callback*)(port_number,rc,max_write_size)) */
-+extern void ctc_mpc_establish_connectivity(int,callbacktypei3);
-+extern void ctc_mpc_dealloc_ch(int);
-+extern void ctc_mpc_flow_control(int,int);
-+
-+#endif
-=== drivers/s390/net/ctcmain.c
-==================================================================
---- drivers/s390/net/ctcmain.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/ctcmain.c   (/trunk/2.4.27)   (revision 52)
-@@ -1,5 +1,5 @@
- /*
-- * $Id: ctcmain.c,v 1.63 2003/10/22 19:32:57 felfert Exp $
-+ * $Id: ctcmain.c,v 1.59.4.3 2003/10/22 20:14:47 felfert Exp $
-  *
-  * CTC / ESCON network driver
-  *
-@@ -35,7 +35,7 @@
-  * along with this program; if not, write to the Free Software
-  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-  *
-- * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.63 $
-+ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.59.4.3 $
-  *
-  */
- 
-@@ -419,7 +419,7 @@
-  */
- static void print_banner(void) {
- 	static int printed = 0;
--	char vbuf[] = "$Revision: 1.63 $";
-+	char vbuf[] = "$Revision: 1.59.4.3 $";
- 	char *version = vbuf;
- 
- 	if (printed)
-@@ -2934,7 +2934,6 @@
- 	file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL);
- 	if (file->private_data == NULL)
- 		return -ENOMEM;
--	*(char *)file->private_data = '\0';
- 	MOD_INC_USE_COUNT;
- 	return 0;
- }
-@@ -3000,7 +2999,6 @@
- 	ctc_priv *privptr;
- 	ssize_t ret = 0;
- 	char *p = sbuf;
--	loff_t pos = *off;
- 	int l;
- 
- 	if (!(dev = find_netdev_by_ino(ino)))
-@@ -3010,19 +3008,19 @@
- 
- 	privptr = (ctc_priv *)dev->priv;
- 
--	if (!*sbuf || pos == 0)
-+	if (file->f_pos == 0)
- 		sprintf(sbuf, "%d\n", privptr->channel[READ]->max_bufsize);
- 
- 	l = strlen(sbuf);
- 	p = sbuf;
--	if (pos == (unsigned)pos && pos < l) {
--		p += pos;
-+	if (file->f_pos < l) {
-+		p += file->f_pos;
- 		l = strlen(p);
- 		ret = (count > l) ? l : count;
- 		if (copy_to_user(buf, p, ret))
- 			return -EFAULT;
--		*off = pos + ret;
- 	}
-+	file->f_pos += ret;
- 	return ret;
- }
- 
-@@ -3086,7 +3084,6 @@
- 	ctc_priv *privptr;
- 	ssize_t ret = 0;
- 	char *p = sbuf;
--	loff_t pos = *off;
- 	int l;
- 
- 	if (!(dev = find_netdev_by_ino(ino)))
-@@ -3096,19 +3093,19 @@
- 
- 	privptr = (ctc_priv *)dev->priv;
- 
--	if (!*sbus || pos == 0)
-+	if (file->f_pos == 0)
- 		sprintf(sbuf, "0x%02x\n", loglevel);
- 
- 	l = strlen(sbuf);
- 	p = sbuf;
--	if (pos == (unsigned)pos && pos < l) {
--		p += pos;
-+	if (file->f_pos < l) {
-+		p += file->f_pos;
- 		l = strlen(p);
- 		ret = (count > l) ? l : count;
- 		if (copy_to_user(buf, p, ret))
- 			return -EFAULT;
--		*off = pos + ret;
- 	}
-+	file->f_pos += ret;
- 	return ret;
- }
- 
-@@ -3119,7 +3116,6 @@
- 	file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL);
- 	if (file->private_data == NULL)
- 		return -ENOMEM;
--	*(char *)file->private_data = '\0';
- 	MOD_INC_USE_COUNT;
- 	return 0;
- }
-@@ -3159,7 +3155,6 @@
- 	ctc_priv *privptr;
- 	ssize_t ret = 0;
- 	char *p = sbuf;
--	loff_t pos = *off;
- 	int l;
- 
- 	if (!(dev = find_netdev_by_ino(ino)))
-@@ -3169,7 +3164,7 @@
- 
- 	privptr = (ctc_priv *)dev->priv;
- 
--	if (!*sbus || pos == 0) {
-+	if (file->f_pos == 0) {
- 		p += sprintf(p, "Device FSM state: %s\n",
- 			     fsm_getstate_str(privptr->fsm));
- 		p += sprintf(p, "RX channel FSM state: %s\n",
-@@ -3191,14 +3186,14 @@
- 	}
- 	l = strlen(sbuf);
- 	p = sbuf;
--	if (pos == (unsigned)pos && pos < l) {
--		p += pos;
-+	if (file->f_pos < l) {
-+		p += file->f_pos;
- 		l = strlen(p);
- 		ret = (count > l) ? l : count;
- 		if (copy_to_user(buf, p, ret))
- 			return -EFAULT;
--		*off = pos + ret;
- 	}
-+	file->f_pos += ret;
- 	return ret;
- }
- 
-=== drivers/s390/net/lcs.c
-==================================================================
---- drivers/s390/net/lcs.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/lcs.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,2192 @@
-+/*
-+ *  linux/drivers/s390/net/lcs.c
-+ *
-+ *  Linux for S/390 Lan Channel Station Network Driver
-+ *
-+ *  Copyright (C)  1999-2001 IBM Deutschland Entwicklung GmbH,
-+ *			     IBM Corporation
-+ *    Author(s): Original Code written by
-+ *			  DJ Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
-+ *		 Rewritten by
-+ *			  Frank Pavlic (pavlic at de.ibm.com) and
-+ *		 	  Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ *
-+ *    $Revision: 1.132.20.6 $	 $Date: 2004/11/24 10:17:56 $
-+ *
-+ * 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/version.h>
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/if.h>
-+#include <linux/netdevice.h>
-+#include <linux/etherdevice.h>
-+#include <linux/trdevice.h>
-+#include <linux/fddidevice.h>
-+#include <linux/inetdevice.h>
-+#include <linux/in.h>
-+#include <linux/igmp.h>
-+#include <net/arp.h>
-+#include <net/ip.h>
-+
-+#include <asm/debug.h>
-+#include <asm/idals.h>
-+#include <asm/timex.h>
-+
-+#include "lcs.h"
-+
-+#if !defined(CONFIG_CHANDEV)
-+#error Cannot compile lcs.c without chandev support.
-+#endif
-+
-+#if !defined(CONFIG_NET_ETHERNET) && \
-+    !defined(CONFIG_TR) && !defined(CONFIG_FDDI)
-+#error Cannot compile lcs.c without some net devices switched on.
-+#endif
-+
-+/**
-+ * initialization string for output
-+ */
-+#define VERSION_LCS_C  "$Revision: 1.132.20.6 $"
-+
-+static const char *version="LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")";
-+static const char *cardname = "S390 Lan Channel Station Interface";
-+static char debug_buffer[255];
-+
-+/**
-+ * Some prototypes.
-+ */
-+static void lcs_irq(int, void *, struct pt_regs *);
-+static void lcs_tasklet(unsigned long);
-+static void lcs_start_kernel_thread(struct lcs_card *card);
-+static void lcs_get_frames_cb(struct lcs_channel *, struct lcs_buffer *);
-+static int lcs_send_delipm(struct lcs_card *, struct lcs_ipm_list *);
-+
-+/**
-+ * Debug Facility Stuff
-+ */
-+static debug_info_t *lcs_dbf_setup;
-+static debug_info_t *lcs_dbf_trace;
-+
-+/**
-+ *  LCS Debug Facility functions
-+ */
-+static void
-+lcs_unregister_debug_facility(void)
-+{
-+	if (lcs_dbf_setup)
-+		debug_unregister(lcs_dbf_setup);
-+	if (lcs_dbf_trace)
-+		debug_unregister(lcs_dbf_trace);
-+}
-+
-+static int
-+lcs_register_debug_facility(void)
-+{
-+	lcs_dbf_setup = debug_register("lcs_setup", 1, 1, 8);
-+	lcs_dbf_trace = debug_register("lcs_trace", 1, 2, 8);
-+	if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) {
-+		PRINT_ERR("Not enough memory for debug facility.\n");
-+		lcs_unregister_debug_facility();
-+		return -ENOMEM;
-+	}
-+	debug_register_view(lcs_dbf_setup, &debug_hex_ascii_view);
-+	debug_set_level(lcs_dbf_setup, 5);
-+	debug_register_view(lcs_dbf_trace, &debug_hex_ascii_view);
-+	debug_set_level(lcs_dbf_trace, 3);
-+	return 0;
-+}
-+
-+/**
-+ * Allocate io buffers.
-+ */
-+static int
-+lcs_alloc_channel(struct lcs_channel *channel)
-+{
-+	int cnt;
-+
-+	LCS_DBF_TEXT(2, setup, "ichalloc");
-+	memset(channel, 0, sizeof(struct lcs_channel));
-+	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
-+		/* alloc memory fo iobuffer */
-+		channel->iob[cnt].data = (void *)
-+			kmalloc(LCS_IOBUFFERSIZE, GFP_DMA | GFP_KERNEL);
-+		if (channel->iob[cnt].data == NULL)
-+			break;
-+		memset(channel->iob[cnt].data, 0, LCS_IOBUFFERSIZE);
-+		channel->iob[cnt].state = BUF_STATE_EMPTY;
-+	}
-+	if (cnt < LCS_NUM_BUFFS) {
-+		/* Not all io buffers could be allocated. */
-+		LCS_DBF_TEXT(3, setup, "echalloc");
-+		while (cnt-- > 0)
-+			kfree(channel->iob[cnt].data);
-+		return -ENOMEM;
-+	}
-+	return 0;
-+}
-+
-+/**
-+ * Free io buffers.
-+ */
-+static void
-+lcs_free_channel(struct lcs_channel *channel)
-+{
-+	int cnt;
-+
-+	LCS_DBF_TEXT(2, setup, "ichfree");
-+	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++)
-+		kfree(channel->iob[cnt].data);
-+}
-+
-+/**
-+ * LCS alloc memory for card and channels
-+ */
-+static struct lcs_card *
-+lcs_alloc_card(void)
-+{
-+	struct lcs_card *card;
-+	int rc;
-+
-+	LCS_DBF_TEXT(2, setup, "alloclcs");
-+	card = kmalloc(sizeof(struct lcs_card), GFP_KERNEL | GFP_DMA);
-+	if (card == NULL)
-+		return NULL;
-+	memset(card, 0, sizeof(struct lcs_card));
-+	card->lan_type = LCS_FRAME_TYPE_AUTO;
-+	card->pkt_seq = 0;
-+	/* Allocate io buffers for the read channel. */
-+	rc = lcs_alloc_channel(&card->read);
-+	if (rc){
-+		LCS_DBF_TEXT(2, setup, "iccwerr");
-+		kfree(card);
-+		return NULL;
-+	}
-+	/* Allocate io buffers for the write channel. */
-+	rc = lcs_alloc_channel(&card->write);
-+	if (rc) {
-+		LCS_DBF_TEXT(2, setup, "iccwerr");
-+		lcs_free_channel(&card->read);
-+		kfree(card);
-+		return NULL;
-+	}
-+	LCS_DBF_HEX(2, setup, &card, sizeof(void*));
-+	return card;
-+}
-+
-+/**
-+ * LCS free memory for card and channels.
-+ */
-+static void
-+lcs_free_card(struct lcs_card *card)
-+{
-+	LCS_DBF_TEXT(2, setup, "remcard");
-+	/* Free write channel buffers. */
-+	lcs_free_channel(&card->write);
-+	/* Free read channel buffers. */
-+	lcs_free_channel(&card->read);
-+	kfree(card);
-+}
-+
-+/*
-+ * Setup read channel.
-+ */
-+static void
-+lcs_setup_read_ccws(struct lcs_card *card)
-+{
-+	int cnt;
-+
-+	LCS_DBF_TEXT(2, setup, "ireadccw");
-+	/* Setup read ccws. */
-+	memset(card->read.ccws, 0, sizeof (ccw1_t) * (LCS_NUM_BUFFS + 1));
-+	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
-+		card->read.ccws[cnt].cmd_code = LCS_CCW_READ;
-+		card->read.ccws[cnt].count = LCS_IOBUFFERSIZE;
-+		card->read.ccws[cnt].flags =
-+			CCW_FLAG_CC | CCW_FLAG_SLI | CCW_FLAG_PCI;
-+		/*
-+		 * Note: we have allocated the buffer with GFP_DMA, so
-+		 * we do not need to do set_normalized_cda.
-+		 */
-+		card->read.ccws[cnt].cda =
-+			(__u32) __pa(card->read.iob[cnt].data);
-+		((struct lcs_header *)
-+		 card->read.iob[cnt].data)->offset = LCS_ILLEGAL_OFFSET;
-+		card->read.iob[cnt].callback = lcs_get_frames_cb;
-+		card->read.iob[cnt].state = BUF_STATE_READY;
-+		card->read.iob[cnt].count = LCS_IOBUFFERSIZE;
-+	}
-+	card->read.ccws[0].flags &= ~CCW_FLAG_PCI;
-+	card->read.ccws[LCS_NUM_BUFFS - 1].flags &= ~CCW_FLAG_PCI;
-+	card->read.ccws[LCS_NUM_BUFFS - 1].flags |= CCW_FLAG_SUSPEND;
-+	/* Last ccw is a tic (transfer in channel). */
-+	card->read.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER;
-+	card->read.ccws[LCS_NUM_BUFFS].cda =
-+		(__u32) __pa(card->read.ccws);
-+	/* Set initial state of the read channel. */
-+	card->read.state = CH_STATE_INIT;
-+
-+	card->read.io_idx = 0;
-+	card->read.buf_idx = 0;
-+}
-+
-+static int
-+lcs_setup_read(struct lcs_card *card)
-+{
-+	char dbf_text[15];
-+	int rc;
-+
-+	LCS_DBF_TEXT(2, setup, "setpread");
-+	/* Request irq for read channel. */
-+	rc = chandev_request_irq(card->read.irq, (void *) lcs_irq,
-+				 0, cardname, &card->read.devstat);
-+	if (rc) {
-+		sprintf(dbf_text, "chre%4x", card->read.irq);
-+		LCS_DBF_TEXT(3, setup, dbf_text);
-+		return rc;
-+	}
-+	lcs_setup_read_ccws(card);
-+	/* Initialize read channel tasklet. */
-+	card->read.irq_tasklet.data = (unsigned long) &card->read;
-+	card->read.irq_tasklet.func = lcs_tasklet;
-+	/* Initialize waitqueue. */
-+	init_waitqueue_head(&card->read.wait_q);
-+	return 0;
-+}
-+
-+/*
-+ * Setup write channel.
-+ */
-+static void
-+lcs_setup_write_ccws(struct lcs_card *card)
-+{
-+	int cnt;
-+
-+	LCS_DBF_TEXT(2, setup, "iwritccw");
-+	/* Setup write ccws. */
-+	memset(card->write.ccws, 0, sizeof(ccw1_t) * LCS_NUM_BUFFS + 1);
-+	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
-+		card->write.ccws[cnt].cmd_code = LCS_CCW_WRITE;
-+		card->write.ccws[cnt].count = 0;
-+		card->write.ccws[cnt].flags =
-+			CCW_FLAG_SUSPEND | CCW_FLAG_CC | CCW_FLAG_SLI;
-+		/*
-+		 * Note: we have allocated the buffer with GFP_DMA, so
-+		 * we do not need to do set_normalized_cda.
-+		 */
-+		card->write.ccws[cnt].cda =
-+			(__u32) __pa(card->write.iob[cnt].data);
-+	}
-+	/* Last ccw is a tic (transfer in channel). */
-+	card->write.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER;
-+	card->write.ccws[LCS_NUM_BUFFS].cda =
-+		(__u32) __pa(card->write.ccws);
-+	/* Set initial state of the write channel. */
-+	card->read.state = CH_STATE_INIT;
-+
-+	card->write.io_idx = 0;
-+	card->write.buf_idx = 0;
-+}
-+
-+static int
-+lcs_setup_write(struct lcs_card *card)
-+{
-+	char dbf_text[15];
-+	int rc;
-+
-+	LCS_DBF_TEXT(2, setup, "setpwrit");
-+	/* Request irq for write channel. */
-+	rc = chandev_request_irq(card->write.irq, (void *) lcs_irq,
-+				 0, cardname, &card->write.devstat);
-+	if (rc) {
-+		sprintf(dbf_text,"chwr%4x", card->write.irq);
-+		LCS_DBF_TEXT(3, setup, dbf_text);
-+		return rc;
-+	}
-+	lcs_setup_write_ccws(card);
-+	/* Initialize write channel tasklet. */
-+	card->write.irq_tasklet.data = (unsigned long) &card->write;
-+	card->write.irq_tasklet.func = lcs_tasklet;
-+	/* Initialize waitqueue. */
-+	init_waitqueue_head(&card->write.wait_q);
-+	return 0;
-+}
-+
-+static void
-+lcs_set_allowed_threads(struct lcs_card *card, unsigned long threads)
-+{
-+        unsigned long flags;
-+
-+        spin_lock_irqsave(&card->mask_lock, flags);
-+        card->thread_allowed_mask = threads;
-+        spin_unlock_irqrestore(&card->mask_lock, flags);
-+        wake_up(&card->wait_q);
-+}
-+
-+static inline int
-+lcs_threads_running(struct lcs_card *card, unsigned long threads)
-+{
-+        unsigned long flags;
-+        int rc = 0;
-+
-+        spin_lock_irqsave(&card->mask_lock, flags);
-+        rc = (card->thread_running_mask & threads);
-+        spin_unlock_irqrestore(&card->mask_lock, flags);
-+        return rc;
-+}
-+
-+static int
-+lcs_wait_for_threads(struct lcs_card *card, unsigned long threads)
-+{
-+        return wait_event_interruptible(card->wait_q,
-+                        lcs_threads_running(card, threads) == 0);
-+}
-+
-+static inline int
-+lcs_set_thread_start_bit(struct lcs_card *card, unsigned long thread)
-+{
-+        unsigned long flags;
-+
-+        spin_lock_irqsave(&card->mask_lock, flags);
-+        if ( !(card->thread_allowed_mask & thread) ||
-+              (card->thread_start_mask & thread) ) {
-+                spin_unlock_irqrestore(&card->mask_lock, flags);
-+                return -EPERM;
-+        }
-+        card->thread_start_mask |= thread;
-+        spin_unlock_irqrestore(&card->mask_lock, flags);
-+        return 0;
-+}
-+
-+static void
-+lcs_clear_thread_running_bit(struct lcs_card *card, unsigned long thread)
-+{
-+        unsigned long flags;
-+
-+        spin_lock_irqsave(&card->mask_lock, flags);
-+        card->thread_running_mask &= ~thread;
-+        spin_unlock_irqrestore(&card->mask_lock, flags);
-+        wake_up(&card->wait_q);
-+}
-+
-+static inline int
-+__lcs_do_run_thread(struct lcs_card *card, unsigned long thread)
-+{
-+        unsigned long flags;
-+        int rc = 0;
-+
-+        spin_lock_irqsave(&card->mask_lock, flags);
-+        if (card->thread_start_mask & thread){
-+                if ((card->thread_allowed_mask & thread) &&
-+                    !(card->thread_running_mask & thread)){
-+                        rc = 1;
-+                        card->thread_start_mask &= ~thread;
-+                        card->thread_running_mask |= thread;
-+                } else
-+                        rc = -EPERM;
-+        }
-+        spin_unlock_irqrestore(&card->mask_lock, flags);
-+        return rc;
-+}
-+
-+static int
-+lcs_do_run_thread(struct lcs_card *card, unsigned long thread)
-+{
-+        int rc = 0;
-+        wait_event(card->wait_q,
-+                   (rc = __lcs_do_run_thread(card, thread)) >= 0);
-+        return rc;
-+}
-+
-+static int
-+lcs_do_start_thread(struct lcs_card *card, unsigned long thread)
-+{
-+        unsigned long flags;
-+        int rc = 0;
-+
-+        spin_lock_irqsave(&card->mask_lock, flags);
-+        LCS_DBF_TEXT_(4, trace, "  %02x%02x%02x",
-+                        (u8) card->thread_start_mask,
-+                        (u8) card->thread_allowed_mask,
-+                        (u8) card->thread_running_mask);
-+        rc = (card->thread_start_mask & thread);
-+        spin_unlock_irqrestore(&card->mask_lock, flags);
-+        return rc;
-+}
-+
-+/*
-+ * Cleanup channel.
-+ */
-+static void
-+lcs_cleanup_channel(struct lcs_channel *channel)
-+{
-+	LCS_DBF_TEXT(2, setup, "cleanch");
-+	/* Kill write channel tasklets. */
-+	tasklet_kill(&channel->irq_tasklet);
-+	/* Free irq. */
-+	chandev_free_irq(channel->irq, &channel->devstat);
-+}
-+
-+/**
-+ * Initialize channels,card and state machines.
-+ */
-+static int
-+lcs_setup_card(struct lcs_card *card)
-+{
-+	int rc;
-+
-+	LCS_DBF_TEXT(2, setup, "initcard");
-+
-+	rc = lcs_setup_read(card);
-+	if (rc) {
-+		PRINT_ERR("Could not initialize read channel\n");
-+		return rc;
-+	}
-+	rc = lcs_setup_write(card);
-+	if (rc) {
-+		PRINT_ERR("Could not initialize write channel\n");
-+		lcs_cleanup_channel(&card->read);
-+		return rc;
-+	}
-+	/* Set cards initial state. */
-+	card->state = DEV_STATE_DOWN;
-+	if (card->port_protocol_no != LCS_INVALID_PORT_NO )
-+		card->portno = card->port_protocol_no;
-+	else
-+		card->portno = 0;
-+	card->tx_buffer = NULL;
-+	card->tx_emitted = 0;
-+
-+	/* Initialize kernel thread task used for LGW commands. */
-+	card->kernel_thread_starter.routine = (void *) lcs_start_kernel_thread;
-+	card->kernel_thread_starter.data = (void *) card;
-+	card->thread_start_mask = 0;
-+        card->thread_allowed_mask = 0;
-+        card->thread_running_mask = 0;
-+	init_waitqueue_head(&card->wait_q);
-+	spin_lock_init(&card->lock);
-+	spin_lock_init(&card->ipm_lock);
-+	INIT_LIST_HEAD(&card->ipm_list);
-+	INIT_LIST_HEAD(&card->kernel_thread_starter.list);
-+	INIT_LIST_HEAD(&card->lancmd_waiters);
-+	return 0;
-+}
-+
-+static inline void
-+lcs_clear_multicast_list(struct lcs_card *card)
-+{
-+#ifdef  CONFIG_IP_MULTICAST
-+        struct lcs_ipm_list *ipm;
-+        unsigned long flags;
-+
-+        /* Free multicast list. */
-+        LCS_DBF_TEXT(3, setup, "clmclist");
-+        spin_lock_irqsave(&card->ipm_lock, flags);
-+        while (!list_empty(&card->ipm_list)){
-+                ipm = list_entry(card->ipm_list.next,
-+                                 struct lcs_ipm_list, list);
-+                list_del(&ipm->list);
-+                if (ipm->ipm_state != LCS_IPM_STATE_SET_REQUIRED){
-+                        spin_unlock_irqrestore(&card->ipm_lock, flags);
-+                        lcs_send_delipm(card, ipm);
-+                        spin_lock_irqsave(&card->ipm_lock, flags);
-+                }
-+                kfree(ipm);
-+        }
-+        spin_unlock_irqrestore(&card->ipm_lock, flags);
-+#endif
-+}
-+
-+/**
-+ * Cleanup channels,card and state machines.
-+ */
-+static void
-+lcs_cleanup_card(struct lcs_card *card)
-+{
-+	LCS_DBF_TEXT(2, setup, "cleancrd");
-+	kfree(card->dev);
-+	/* Cleanup channels. */
-+	lcs_cleanup_channel(&card->write);
-+	lcs_cleanup_channel(&card->read);
-+}
-+
-+/**
-+ * Start channel.
-+ */
-+static int
-+lcs_start_channel(struct lcs_channel *channel)
-+{
-+	char dbf_text[15];
-+	unsigned long flags;
-+	int rc;
-+
-+	sprintf(dbf_text,"ssch%4x", channel->irq);
-+	LCS_DBF_TEXT(3, trace, dbf_text);
-+	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
-+	rc = do_IO(channel->irq, channel->ccws + channel->io_idx, 0,
-+		   0, DOIO_DENY_PREFETCH | DOIO_ALLOW_SUSPEND);
-+	if (rc == 0)
-+		channel->state = CH_STATE_RUNNING;
-+	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
-+	if (rc) {
-+		sprintf(dbf_text,"essc%4x", channel->irq);
-+		LCS_DBF_TEXT(3, trace, dbf_text);
-+		PRINT_ERR("Error in starting channel!\n");
-+	}
-+	return rc;
-+}
-+
-+/**
-+ * Stop channel.
-+ */
-+static int
-+lcs_stop_channel(struct lcs_channel *channel)
-+{
-+	char dbf_text[15];
-+	unsigned long flags;
-+	int rc;
-+
-+	if (channel->state == CH_STATE_STOPPED)
-+		return 0;
-+	sprintf(dbf_text,"hsch%4x", channel->irq);
-+	LCS_DBF_TEXT(3, trace, dbf_text);
-+	channel->state = CH_STATE_INIT;
-+	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
-+	rc = halt_IO(channel->irq, (addr_t) channel, 0);
-+	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
-+	if (rc) {
-+		sprintf(dbf_text,"ehsc%4x", channel->irq);
-+		LCS_DBF_TEXT(3, trace, dbf_text);
-+		return rc;
-+	}
-+	/* Asynchronous halt initialted. Wait for its completion. */
-+	wait_event(channel->wait_q, (channel->state == CH_STATE_HALTED));
-+	return 0;
-+}
-+
-+/**
-+ * start read and write channel
-+ */
-+static int
-+lcs_start_channels(struct lcs_card *card)
-+{
-+	int rc;
-+
-+	LCS_DBF_TEXT(3, trace, "chstart");
-+	/* start read channel */
-+	rc = lcs_start_channel(&card->read);
-+	if (rc)
-+		return rc;
-+	/* start write channel */
-+	rc = lcs_start_channel(&card->write);
-+	if (rc)
-+		lcs_stop_channel(&card->read);
-+	return rc;
-+}
-+
-+/**
-+ * stop read and write channel
-+ */
-+static int
-+lcs_stop_channels(struct lcs_card *card)
-+{
-+	LCS_DBF_TEXT(3, trace, "chhalt");
-+	lcs_stop_channel(&card->read);
-+	lcs_stop_channel(&card->write);
-+	return 0;
-+}
-+
-+/**
-+ * Get empty buffer.
-+ */
-+static struct lcs_buffer *
-+__lcs_get_buffer(struct lcs_channel *channel)
-+{
-+	int index;
-+
-+	LCS_DBF_TEXT(5, trace, "_getbuff");
-+	index = channel->io_idx;
-+	do {
-+		if (channel->iob[index].state == BUF_STATE_EMPTY) {
-+			channel->iob[index].state = BUF_STATE_LOCKED;
-+			return channel->iob + index;
-+		}
-+		index = (index + 1) & (LCS_NUM_BUFFS - 1);
-+	} while (index != channel->io_idx);
-+	return NULL;
-+}
-+
-+static struct lcs_buffer *
-+lcs_get_buffer(struct lcs_channel *channel)
-+{
-+	struct lcs_buffer *buffer;
-+	unsigned long flags;
-+
-+	LCS_DBF_TEXT(5, trace, "getbuff");
-+	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
-+	buffer = __lcs_get_buffer(channel);
-+	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
-+	return buffer;
-+}
-+
-+/**
-+ * Resume channel program if the channel is suspended.
-+ */
-+static int
-+__lcs_resume_channel(struct lcs_channel *channel)
-+{
-+	int rc;
-+
-+	if (channel->state != CH_STATE_SUSPENDED)
-+		return 0;
-+	if (channel->ccws[channel->io_idx].flags & CCW_FLAG_SUSPEND)
-+		return 0;
-+	LCS_DBF_TEXT_(5, trace, "rsch%4x",channel->irq);
-+	rc = resume_IO(channel->irq);
-+	if (rc) {
-+		LCS_DBF_TEXT_(4, trace, "ersc%4x",channel->irq);
-+		PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc);
-+	} else
-+		channel->state = CH_STATE_RUNNING;
-+	return rc;
-+
-+}
-+
-+/**
-+ * Make a buffer ready for processing.
-+ */
-+static inline void
-+__lcs_ready_buffer_bits(struct lcs_channel *channel, int index)
-+{
-+	int prev, next;
-+
-+	LCS_DBF_TEXT(5, trace, "rdybits");
-+	prev = (index - 1) & (LCS_NUM_BUFFS - 1);
-+	next = (index + 1) & (LCS_NUM_BUFFS - 1);
-+	/* Check if we may clear the suspend bit of this buffer. */
-+	if (channel->ccws[next].flags & CCW_FLAG_SUSPEND) {
-+		/* Check if we have to set the PCI bit. */
-+		if (!(channel->ccws[prev].flags & CCW_FLAG_SUSPEND))
-+			/* Suspend bit of the previous buffer is not set. */
-+			channel->ccws[index].flags |= CCW_FLAG_PCI;
-+		/* Suspend bit of the next buffer is set. */
-+		channel->ccws[index].flags &= ~CCW_FLAG_SUSPEND;
-+	}
-+}
-+
-+static int
-+lcs_ready_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
-+{
-+	unsigned long flags;
-+	int index, rc;
-+
-+	LCS_DBF_TEXT(5, trace, "rdybuff");
-+	if (buffer->state != BUF_STATE_LOCKED &&
-+	    buffer->state != BUF_STATE_PROCESSED)
-+		BUG();
-+	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
-+	buffer->state = BUF_STATE_READY;
-+	index = buffer - channel->iob;
-+	/* Set length. */
-+	channel->ccws[index].count = buffer->count;
-+	/* Check relevant PCI/suspend bits. */
-+	__lcs_ready_buffer_bits(channel, index);
-+	rc = __lcs_resume_channel(channel);
-+	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
-+	return rc;
-+}
-+
-+/**
-+ * Mark the buffer as processed. Take care of the suspend bit
-+ * of the previous buffer. This function is called from
-+ * interrupt context, so the lock must not be taken.
-+ */
-+static int
-+__lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
-+{
-+	int index, prev, next;
-+
-+	LCS_DBF_TEXT(5, trace, "prcsbuff");
-+	if (buffer->state != BUF_STATE_READY)
-+		BUG();
-+	buffer->state = BUF_STATE_PROCESSED;
-+	index = buffer - channel->iob;
-+	prev = (index - 1) & (LCS_NUM_BUFFS - 1);
-+	next = (index + 1) & (LCS_NUM_BUFFS - 1);
-+	/* Set the suspend bit and clear the PCI bit of this buffer. */
-+	channel->ccws[index].flags |= CCW_FLAG_SUSPEND;
-+	channel->ccws[index].flags &= ~CCW_FLAG_PCI;
-+	/* Check the suspend bit of the previous buffer. */
-+	if (channel->iob[prev].state == BUF_STATE_READY) {
-+		/*
-+		 * Previous buffer is in state ready. It might have
-+		 * happened in lcs_ready_buffer that the suspend bit
-+		 * has not been cleared to avoid an endless loop.
-+		 * Do it now.
-+		 */
-+		__lcs_ready_buffer_bits(channel, prev);
-+	}
-+	/* Clear PCI bit of next buffer. */
-+	channel->ccws[next].flags &= ~CCW_FLAG_PCI;
-+	return __lcs_resume_channel(channel);
-+}
-+
-+/**
-+ * Put a processed buffer back to state empty.
-+ */
-+static void
-+lcs_release_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
-+{
-+	unsigned long flags;
-+
-+	LCS_DBF_TEXT(5, trace, "relbuff");
-+	if (buffer->state != BUF_STATE_LOCKED &&
-+	    buffer->state != BUF_STATE_PROCESSED)
-+		BUG();
-+	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
-+	buffer->state = BUF_STATE_EMPTY;
-+	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
-+}
-+
-+/**
-+ * Get buffer for a lan command.
-+ */
-+static struct lcs_buffer *
-+lcs_get_lancmd(struct lcs_card *card, int count)
-+{
-+	struct lcs_buffer *buffer;
-+	struct lcs_cmd *cmd;
-+	
-+	LCS_DBF_TEXT(4, trace, "getlncmd");
-+	
-+	/* Get buffer and wait if none is available. */
-+	wait_event(card->write.wait_q,
-+		   ((buffer = lcs_get_buffer(&card->write)) != NULL));
-+	count += sizeof(struct lcs_header);
-+	*(__u16 *)(buffer->data + count) = 0;
-+	buffer->count = count + sizeof(__u16);
-+	buffer->callback = lcs_release_buffer;
-+	cmd = (struct lcs_cmd *) buffer->data;
-+	cmd->offset = count;
-+	cmd->type = LCS_FRAME_TYPE_CONTROL;
-+	cmd->slot = 0;
-+	return buffer;
-+}
-+
-+static void
-+lcs_get_reply(struct lcs_reply *reply)
-+{
-+        atomic_inc(&reply->refcnt);
-+}
-+
-+static void
-+lcs_put_reply(struct lcs_reply *reply)
-+{
-+        if (atomic_dec_and_test(&reply->refcnt)) {
-+                kfree(reply);
-+        }
-+
-+}
-+
-+static struct lcs_reply *
-+lcs_alloc_reply(struct lcs_cmd *cmd)
-+{
-+        struct lcs_reply *reply;
-+
-+        LCS_DBF_TEXT(4, trace, "getreply");
-+
-+        reply = kmalloc(sizeof(struct lcs_reply), GFP_ATOMIC);
-+        if (!reply)
-+                return NULL;
-+        memset(reply,0,sizeof(struct lcs_reply));
-+        atomic_set(&reply->refcnt,1);
-+        reply->sequence_no = cmd->sequence_no;
-+        reply->received = 0;
-+        reply->rc = 0;
-+        init_waitqueue_head(&reply->wait_q);
-+
-+        return reply;
-+}
-+
-+
-+/**
-+ * Notifier function for lancmd replies. Called from read irq.
-+ */
-+static void
-+lcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd)
-+{
-+	struct list_head *l, *n;
-+	struct lcs_reply *reply;
-+
-+	LCS_DBF_TEXT(4, trace, "notiwait");
-+	spin_lock(&card->lock);
-+	list_for_each_safe(l, n, &card->lancmd_waiters) {
-+		reply = list_entry(l, struct lcs_reply, list);
-+		if (reply->sequence_no == cmd->sequence_no) {
-+			lcs_get_reply(reply);
-+			list_del_init(&reply->list);
-+			if (reply->callback != NULL)
-+				reply->callback(card, cmd);
-+			reply->received = 1;
-+			reply->rc = cmd->return_code;
-+			wake_up(&reply->wait_q);
-+			lcs_put_reply(reply);
-+			break;
-+		}
-+	}
-+	spin_unlock(&card->lock);
-+}
-+
-+/**
-+ * Emit buffer of a lan comand.
-+ */
-+void
-+lcs_lancmd_timeout(unsigned long data)
-+{
-+	struct lcs_reply *reply, *list_reply;
-+	struct list_head *l, *n;
-+	unsigned long flags;
-+
-+	LCS_DBF_TEXT(4, trace, "timeout");
-+	list_reply = (struct lcs_reply *) data;
-+	spin_lock_irqsave(&list_reply->card->lock, flags);
-+	list_for_each_safe(l, n, &list_reply->card->lancmd_waiters) {
-+		reply = list_entry(l, struct lcs_reply, list);
-+                if (reply == list_reply) {
-+                        lcs_get_reply(reply);
-+                        list_del_init(&reply->list);
-+                        spin_unlock_irqrestore(&list_reply->card->lock, flags);
-+			reply->received = 1;
-+			reply->rc = -ETIME;
-+			wake_up(&reply->wait_q);
-+			lcs_put_reply(reply);
-+			return;
-+		}
-+	}
-+        spin_unlock_irqrestore(&list_reply->card->lock, flags);
-+}
-+
-+static int
-+lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer,
-+		void (*reply_callback)(struct lcs_card *, struct lcs_cmd *))
-+{
-+	struct lcs_reply *reply;
-+	struct lcs_cmd *cmd;
-+	struct timer_list timer;
-+	unsigned long flags;
-+	int rc;
-+
-+	LCS_DBF_TEXT(4, trace, "sendcmd");
-+	cmd = (struct lcs_cmd *) buffer->data;
-+	cmd->sequence_no = ++card->sequence_no;
-+	cmd->return_code = 0;
-+	reply = lcs_alloc_reply(cmd);
-+	if (!reply)
-+		return -ENOMEM;
-+	reply->callback = reply_callback;
-+	reply->card = card;
-+        spin_lock_irqsave(&card->lock, flags);
-+        list_add_tail(&reply->list, &card->lancmd_waiters);
-+        spin_unlock_irqrestore(&card->lock, flags);
-+
-+	buffer->callback = lcs_release_buffer;
-+	rc = lcs_ready_buffer(&card->write, buffer);
-+	if (rc)
-+		return rc;
-+	init_timer(&timer);
-+	timer.function = lcs_lancmd_timeout;
-+	timer.data = (unsigned long) reply;
-+	timer.expires = jiffies + HZ*5;
-+	add_timer(&timer);
-+	wait_event(reply->wait_q, reply->received);
-+	lcs_put_reply(reply);
-+	del_timer_sync(&timer);
-+	return reply->rc ? -EIO : 0;
-+}
-+
-+/**
-+ * LCS startup command
-+ */
-+static int
-+lcs_send_startup(struct lcs_card *card, __u8 initiator)
-+{
-+	struct lcs_buffer *buffer;
-+	struct lcs_cmd *cmd;
-+
-+	LCS_DBF_TEXT(2, trace, "startup");
-+	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
-+	cmd = (struct lcs_cmd *) buffer->data;
-+	cmd->cmd_code = LCS_CMD_STARTUP;
-+	cmd->initiator = initiator;
-+	cmd->cmd.lcs_startup.buff_size = LCS_IOBUFFERSIZE;
-+	return lcs_send_lancmd(card, buffer, NULL);
-+}
-+
-+/**
-+ * LCS shutdown command
-+ */
-+static int
-+lcs_send_shutdown(struct lcs_card *card)
-+{
-+	struct lcs_buffer *buffer;
-+	struct lcs_cmd *cmd;
-+
-+	LCS_DBF_TEXT(2, trace, "shutdown");
-+	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
-+	cmd = (struct lcs_cmd *) buffer->data;
-+	cmd->cmd_code = LCS_CMD_SHUTDOWN;
-+	cmd->initiator = LCS_INITIATOR_TCPIP;
-+	return lcs_send_lancmd(card, buffer, NULL);
-+}
-+
-+/**
-+ * LCS lanstat command
-+ */
-+static void
-+__lcs_lanstat_cb(struct lcs_card *card, struct lcs_cmd *cmd)
-+{
-+	LCS_DBF_TEXT(2, trace, "statcb");
-+	memcpy(card->mac, cmd->cmd.lcs_lanstat_cmd.mac_addr,
-+	       LCS_MAC_LENGTH);
-+}
-+
-+static int
-+lcs_send_lanstat(struct lcs_card *card)
-+{
-+	struct lcs_buffer *buffer;
-+	struct lcs_cmd *cmd;
-+
-+	LCS_DBF_TEXT(2, trace, "cmdstat");
-+	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
-+	cmd = (struct lcs_cmd *) buffer->data;
-+	/* Setup lanstat command. */
-+	cmd->cmd_code = LCS_CMD_LANSTAT;
-+	cmd->initiator = LCS_INITIATOR_TCPIP;
-+	cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
-+	cmd->cmd.lcs_std_cmd.portno = card->portno;
-+	return lcs_send_lancmd(card, buffer, __lcs_lanstat_cb);
-+}
-+
-+/**
-+ * send stoplan command
-+ */
-+static int
-+lcs_send_stoplan(struct lcs_card *card, __u8 initiator)
-+{
-+	struct lcs_buffer *buffer;
-+	struct lcs_cmd *cmd;
-+
-+	LCS_DBF_TEXT(2, trace, "cmdstpln");
-+	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
-+	cmd = (struct lcs_cmd *) buffer->data;
-+	cmd->cmd_code = LCS_CMD_STOPLAN;
-+	cmd->initiator = initiator;
-+	cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
-+	cmd->cmd.lcs_std_cmd.portno = card->portno;
-+	return lcs_send_lancmd(card, buffer, NULL);
-+}
-+
-+/**
-+ * send startlan command
-+ */
-+static void
-+__lcs_send_startlan_cb(struct lcs_card *card, struct lcs_cmd *cmd)
-+{
-+	LCS_DBF_TEXT(2, trace, "srtlancb");
-+	card->lan_type = cmd->cmd.lcs_std_cmd.lan_type;
-+	card->portno = cmd->cmd.lcs_std_cmd.portno;
-+}
-+
-+static int
-+lcs_send_startlan(struct lcs_card *card, __u8 initiator)
-+{
-+	struct lcs_buffer *buffer;
-+	struct lcs_cmd *cmd;
-+
-+	LCS_DBF_TEXT(2, trace, "cmdstaln");
-+	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
-+	cmd = (struct lcs_cmd *) buffer->data;
-+	cmd->cmd_code = LCS_CMD_STARTLAN;
-+	cmd->initiator = initiator;
-+	cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
-+	cmd->cmd.lcs_std_cmd.portno = card->portno;
-+	return lcs_send_lancmd(card, buffer, __lcs_send_startlan_cb);
-+}
-+
-+#ifdef CONFIG_IP_MULTICAST
-+/**
-+ * send setipm command (Multicast)
-+ */
-+static int
-+lcs_send_setipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list)
-+{
-+	struct lcs_buffer *buffer;
-+	struct lcs_cmd *cmd;
-+
-+	LCS_DBF_TEXT(2, trace, "cmdsetim");
-+	buffer = lcs_get_lancmd(card, LCS_MULTICAST_CMD_SIZE);
-+	cmd = (struct lcs_cmd *) buffer->data;
-+	cmd->cmd_code = LCS_CMD_SETIPM;
-+	cmd->initiator = LCS_INITIATOR_TCPIP;
-+	cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
-+	cmd->cmd.lcs_qipassist.portno = card->portno;
-+	cmd->cmd.lcs_qipassist.version = 4;
-+	cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
-+	memcpy(cmd->cmd.lcs_qipassist.lcs_ipass_ctlmsg.ip_mac_pair,
-+	       &ipm_list->ipm, sizeof (struct lcs_ip_mac_pair));
-+	return lcs_send_lancmd(card, buffer, NULL);
-+}
-+
-+/**
-+ * send delipm command (Multicast)
-+ */
-+static int
-+lcs_send_delipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list)
-+{
-+	struct lcs_buffer *buffer;
-+	struct lcs_cmd *cmd;
-+
-+	LCS_DBF_TEXT(2, trace, "cmddelim");
-+	buffer = lcs_get_lancmd(card, LCS_MULTICAST_CMD_SIZE);
-+	cmd = (struct lcs_cmd *) buffer->data;
-+	cmd->cmd_code = LCS_CMD_DELIPM;
-+	cmd->initiator = LCS_INITIATOR_TCPIP;
-+	cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
-+	cmd->cmd.lcs_qipassist.portno = card->portno;
-+	cmd->cmd.lcs_qipassist.version = 4;
-+	cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
-+	memcpy(cmd->cmd.lcs_qipassist.lcs_ipass_ctlmsg.ip_mac_pair,
-+	       &ipm_list->ipm, sizeof (struct lcs_ip_mac_pair));
-+	return lcs_send_lancmd(card, buffer, NULL);
-+}
-+
-+/**
-+ * check if multicast is supported by LCS
-+ */
-+static void
-+__lcs_check_multicast_cb(struct lcs_card *card, struct lcs_cmd *cmd)
-+{
-+	LCS_DBF_TEXT(2, trace, "chkmccb");
-+	card->ip_assists_supported =
-+		cmd->cmd.lcs_qipassist.ip_assists_supported;
-+	card->ip_assists_enabled =
-+		cmd->cmd.lcs_qipassist.ip_assists_enabled;
-+}
-+
-+static int
-+lcs_check_multicast_support(struct lcs_card *card)
-+{
-+	struct lcs_buffer *buffer;
-+	struct lcs_cmd *cmd;
-+	int rc;
-+
-+	LCS_DBF_TEXT(2, trace, "cmdqipa");
-+	/* Send query ipassist. */
-+	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
-+	cmd = (struct lcs_cmd *) buffer->data;
-+	cmd->cmd_code = LCS_CMD_QIPASSIST;
-+	cmd->initiator = LCS_INITIATOR_TCPIP;
-+	cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
-+	cmd->cmd.lcs_qipassist.portno = card->portno;
-+	cmd->cmd.lcs_qipassist.version = 4;
-+	cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
-+	rc = lcs_send_lancmd(card, buffer, __lcs_check_multicast_cb);
-+	if (rc != 0) {
-+		PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n");
-+		return -EOPNOTSUPP;
-+	}
-+	/* Print out supported assists: IPv6 */
-+	PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name,
-+		   (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
-+		   "with" : "without");
-+	/* Print out supported assist: Multicast */
-+	PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name,
-+		   (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
-+		   "with" : "without");
-+	if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT)
-+		return 0;
-+	return -EOPNOTSUPP;
-+}
-+
-+/**
-+ * set or del multicast address on LCS card
-+ */
-+static void
-+lcs_fix_multicast_list(struct lcs_card *card)
-+{
-+	struct list_head failed_list;
-+	struct list_head *l, *n;
-+	struct lcs_ipm_list *ipm;
-+	unsigned long flags;
-+	int rc;
-+	
-+	LCS_DBF_TEXT(4, trace, "fixipm");
-+	INIT_LIST_HEAD(&failed_list);
-+	spin_lock_irqsave(&card->ipm_lock, flags);
-+list_modified:
-+	list_for_each_safe(l, n, &card->ipm_list) {
-+		ipm = list_entry(l, struct lcs_ipm_list, list);
-+		switch (ipm->ipm_state) {
-+		case LCS_IPM_STATE_SET_REQUIRED:
-+			list_del_init(&ipm->list);
-+                        spin_unlock_irqrestore(&card->ipm_lock, flags);
-+			rc = lcs_send_setipm(card, ipm);
-+			spin_lock_irqsave(&card->ipm_lock, flags);
-+			if (rc) {
-+				PRINT_INFO("Adding multicast address failed."
-+					   "Table possibly full!\n");
-+                                list_add_tail(&ipm->list, &failed_list);
-+			} else {
-+				ipm->ipm_state = LCS_IPM_STATE_ON_CARD;
-+				list_add_tail(&ipm->list, &card->ipm_list);
-+			}
-+			goto list_modified;
-+		case LCS_IPM_STATE_DEL_REQUIRED:
-+			list_del(&ipm->list);
-+			spin_unlock_irqrestore(&card->ipm_lock, flags);
-+			lcs_send_delipm(card, ipm);
-+			spin_lock_irqsave(&card->ipm_lock, flags);
-+			kfree(ipm);
-+			goto list_modified;
-+		case LCS_IPM_STATE_ON_CARD:
-+			break;
-+		}
-+	}
-+	list_for_each_entry(ipm, &failed_list, list) {
-+		list_del_init(&ipm->list);
-+		list_add_tail(&ipm->list, &card->ipm_list);
-+	}
-+        spin_unlock_irqrestore(&card->ipm_lock, flags);
-+	if (card->state == DEV_STATE_UP)
-+		netif_wake_queue(card->dev);
-+}
-+
-+/**
-+ * get mac address for the relevant Multicast address
-+ */
-+static void
-+lcs_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev)
-+{
-+	LCS_DBF_TEXT(4, trace, "getmac");
-+	if (dev->type == ARPHRD_IEEE802_TR)
-+		ip_tr_mc_map(ipm, mac);
-+	else
-+		ip_eth_mc_map(ipm, mac);
-+}
-+
-+static inline void
-+lcs_remove_mc_addresses(struct lcs_card *card, struct in_device *in4_dev)
-+{
-+        struct ip_mc_list *im4;
-+        struct list_head *l;
-+        struct lcs_ipm_list *ipm;
-+        unsigned long flags;
-+        char buf[MAX_ADDR_LEN];
-+
-+        LCS_DBF_TEXT(4, trace, "remmclst");
-+        spin_lock_irqsave(&card->ipm_lock, flags);
-+        list_for_each(l, &card->ipm_list) {
-+                ipm = list_entry(l, struct lcs_ipm_list, list);
-+                for (im4 = in4_dev->mc_list; im4 != NULL; im4 = im4->next) {
-+                        lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev);
-+                        if ( (ipm->ipm.ip_addr == im4->multiaddr) &&
-+                             (memcmp(buf, &ipm->ipm.mac_addr,
-+                                     LCS_MAC_LENGTH) == 0) )
-+                                break;
-+                }
-+                if (im4 == NULL)
-+                        ipm->ipm_state = LCS_IPM_STATE_DEL_REQUIRED;
-+        }
-+        spin_unlock_irqrestore(&card->ipm_lock, flags);
-+}
-+
-+static inline struct lcs_ipm_list *
-+lcs_check_addr_entry(struct lcs_card *card, struct ip_mc_list *im4, char *buf)
-+{
-+        struct lcs_ipm_list *tmp, *ipm = NULL;
-+        struct list_head *l;
-+        unsigned long flags;
-+
-+        LCS_DBF_TEXT(4, trace, "chkmcent");
-+        spin_lock_irqsave(&card->ipm_lock, flags);
-+        list_for_each(l, &card->ipm_list) {
-+                tmp = list_entry(l, struct lcs_ipm_list, list);
-+                if ( (tmp->ipm.ip_addr == im4->multiaddr) &&
-+                     (memcmp(buf, &tmp->ipm.mac_addr,
-+                             LCS_MAC_LENGTH) == 0) ) {
-+                        ipm = tmp;
-+                        break;
-+                }
-+        }
-+        spin_unlock_irqrestore(&card->ipm_lock, flags);
-+        return ipm;
-+}
-+
-+static inline void
-+lcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev)
-+{
-+
-+        struct ip_mc_list *im4;
-+        struct lcs_ipm_list *ipm;
-+        char buf[MAX_ADDR_LEN];
-+        unsigned long flags;
-+
-+        LCS_DBF_TEXT(4, trace, "setmclst");
-+        for (im4 = in4_dev->mc_list; im4; im4 = im4->next) {
-+                lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev);
-+                ipm = lcs_check_addr_entry(card, im4, buf);
-+                if (ipm != NULL)
-+                        continue;       /* Address already in list. */
-+                ipm = (struct lcs_ipm_list *)
-+                        kmalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC);
-+                if (ipm == NULL) {
-+                        PRINT_INFO("Not enough memory to add "
-+                                   "new multicast entry!\n");
-+                        break;
-+                }
-+                memset(ipm, 0, sizeof(struct lcs_ipm_list));
-+                memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH);
-+                ipm->ipm.ip_addr = im4->multiaddr;
-+                ipm->ipm_state = LCS_IPM_STATE_SET_REQUIRED;
-+                spin_lock_irqsave(&card->ipm_lock, flags);
-+                list_add(&ipm->list, &card->ipm_list);
-+                spin_unlock_irqrestore(&card->ipm_lock, flags);
-+        }
-+}
-+
-+/**
-+ * register multicast addresses
-+ */ 
-+static int
-+lcs_register_mc_addresses(void *data)
-+{
-+	struct lcs_card *card;
-+	struct in_device *in4_dev;
-+
-+	card = (struct lcs_card *) data;
-+	daemonize();
-+
-+	if (!lcs_do_run_thread(card, LCS_SET_MC_THREAD))
-+                return 0;
-+	LCS_DBF_TEXT(4, trace, "regmulti");
-+	
-+	in4_dev = in_dev_get(card->dev);
-+	if (in4_dev == NULL)
-+		goto out;
-+	read_lock(&in4_dev->lock);
-+	lcs_remove_mc_addresses(card, in4_dev);
-+        lcs_set_mc_addresses(card, in4_dev);
-+	read_unlock(&in4_dev->lock);
-+	in_dev_put(in4_dev);
-+
-+	lcs_fix_multicast_list(card);
-+out:
-+	lcs_clear_thread_running_bit(card, LCS_SET_MC_THREAD);
-+	return 0;
-+}
-+
-+/**
-+ * function called by net device to handle multicast address relevant things
-+ */
-+static void
-+lcs_set_multicast_list(struct net_device *dev)
-+{
-+	struct lcs_card *card;
-+
-+	LCS_DBF_TEXT(4, trace, "setmulti");
-+	card = (struct lcs_card *) dev->priv;
-+
-+	if (!lcs_set_thread_start_bit(card, LCS_SET_MC_THREAD))
-+		schedule_task(&card->kernel_thread_starter);
-+}
-+
-+#endif /* CONFIG_IP_MULTICAST */
-+
-+/**
-+ * IRQ Handler for LCS channels
-+ */
-+static void
-+lcs_irq(int irq, void *devstat, struct pt_regs *p)
-+{
-+	struct lcs_channel *channel;
-+	devstat_t *stat;
-+	int index;
-+
-+	stat  = (devstat_t *) devstat;
-+	channel = (struct lcs_channel *)
-+		((char *) devstat - offsetof(struct lcs_channel, devstat));
-+	LCS_DBF_TEXT_(5, trace, "Rint%4x",irq);
-+	LCS_DBF_TEXT_(5, trace, "%4x%4x",stat->cstat, stat->dstat);
-+
-+	/* How far in the ccw chain have we processed? */
-+	if (channel->state != CH_STATE_INIT) {
-+		index = (ccw1_t *) __va((addr_t) stat->cpa) - channel->ccws;
-+		if ((stat->ii.irb.scsw.actl & SCSW_ACTL_SUSPENDED) ||
-+		    (stat->cstat | SCHN_STAT_PCI))
-+			/* Bloody io subsystem tells us lies about cpa... */
-+			index = (index - 1) & (LCS_NUM_BUFFS - 1);
-+		while (channel->io_idx != index) {
-+			__lcs_processed_buffer(channel,
-+					       channel->iob + channel->io_idx);
-+			channel->io_idx =
-+				(channel->io_idx + 1) & (LCS_NUM_BUFFS - 1);
-+		}
-+	}
-+
-+	if ((stat->dstat & DEV_STAT_DEV_END) ||
-+	    (stat->dstat & DEV_STAT_CHN_END) ||
-+	    (stat->dstat & DEV_STAT_UNIT_CHECK))
-+		/* Mark channel as stopped. */
-+		channel->state = CH_STATE_STOPPED;
-+	else if (stat->ii.irb.scsw.actl & SCSW_ACTL_SUSPENDED)
-+		/* CCW execution stopped on a suspend bit. */
-+		channel->state = CH_STATE_SUSPENDED;
-+
-+	if (stat->ii.irb.scsw.fctl & SCSW_FCTL_HALT_FUNC)
-+		/* The channel has been stopped by halt_IO. */
-+		channel->state = CH_STATE_HALTED;
-+
-+	/* Do the rest in the tasklet. */
-+	tasklet_schedule(&channel->irq_tasklet);
-+}
-+
-+/**
-+ * Tasklet for IRQ handler
-+ */
-+static void
-+lcs_tasklet(unsigned long data)
-+{
-+	unsigned long flags;
-+	struct lcs_channel *channel;
-+	struct lcs_buffer *iob;
-+	int buf_idx;
-+	int rc;
-+
-+	channel = (struct lcs_channel *) data;
-+
-+	LCS_DBF_TEXT_(5, trace, "tlet%4x",channel->irq);
-+	/* Check for processed buffers. */
-+	iob = channel->iob;
-+	buf_idx = channel->buf_idx;
-+	while (iob[buf_idx].state == BUF_STATE_PROCESSED) {
-+		/* Do the callback thing. */
-+		if (iob[buf_idx].callback != NULL)
-+			iob[buf_idx].callback(channel, iob + buf_idx);
-+		buf_idx = (buf_idx + 1) & (LCS_NUM_BUFFS - 1);
-+	}
-+	channel->buf_idx = buf_idx;
-+
-+	if (channel->state == CH_STATE_STOPPED)
-+		// FIXME: what if rc != 0 ??
-+		rc = lcs_start_channel(channel);
-+	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
-+	if (channel->state == CH_STATE_SUSPENDED &&
-+	    channel->iob[channel->io_idx].state == BUF_STATE_READY) {
-+		// FIXME: what if rc != 0 ??
-+		rc = __lcs_resume_channel(channel);
-+	}
-+	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
-+
-+	/* Something happened on the channel. Wake up waiters. */
-+	wake_up(&channel->wait_q);
-+}
-+
-+/**
-+ * Finish current tx buffer and make it ready for transmit.
-+ */
-+static void
-+__lcs_emit_txbuffer(struct lcs_card *card)
-+{
-+	LCS_DBF_TEXT(5, trace,"emittx");
-+	*(__u16 *)(card->tx_buffer->data + card->tx_buffer->count) = 0;
-+	card->tx_buffer->count += 2;
-+	lcs_ready_buffer(&card->write, card->tx_buffer);
-+	card->tx_buffer = NULL;
-+	card->tx_emitted++;
-+}
-+
-+/**
-+ * Callback for finished tx buffers.
-+ */
-+static void
-+lcs_txbuffer_cb(struct lcs_channel *channel, struct lcs_buffer *buffer)
-+{
-+	struct lcs_card *card;
-+
-+	LCS_DBF_TEXT(5, trace,"txbuffcb");
-+	/* Put buffer back to pool. */
-+	lcs_release_buffer(channel, buffer);
-+	card = (struct lcs_card *)
-+		((char *) channel - offsetof(struct lcs_card, write));
-+	spin_lock(&card->lock);
-+	card->tx_emitted--;
-+	if (card->tx_emitted <= 0 && card->tx_buffer != NULL)
-+		/*
-+		 * Last running tx buffer has finished. Submit partially
-+		 * filled current buffer.
-+		 */
-+		__lcs_emit_txbuffer(card);
-+	spin_unlock(&card->lock);
-+}
-+
-+/**
-+ * Packet transmit function called by network stack
-+ */
-+static int
-+__lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb,
-+		 struct net_device *dev)
-+{
-+	struct lcs_header *header;
-+
-+	LCS_DBF_TEXT(5, trace,"hardxmit");
-+	if (skb == NULL) {
-+		card->stats.tx_dropped++;
-+		card->stats.tx_errors++;
-+		return -EIO;
-+	}
-+	if (card->state != DEV_STATE_UP) {
-+		dst_link_failure(skb);
-+		dev_kfree_skb(skb);
-+		card->stats.tx_dropped++;
-+		card->stats.tx_errors++;
-+		card->stats.tx_carrier_errors++;
-+		return 0;
-+	}
-+	if (netif_queue_stopped(dev) ) {
-+		card->stats.tx_dropped++;
-+		return -EBUSY;
-+	}
-+	if (card->tx_buffer != NULL &&
-+	    card->tx_buffer->count + sizeof(struct lcs_header) +
-+	    skb->len + sizeof(u16) > LCS_IOBUFFERSIZE)
-+		/* skb too big for current tx buffer. */
-+		__lcs_emit_txbuffer(card);
-+	if (card->tx_buffer == NULL) {
-+		/* Get new tx buffer */
-+		card->tx_buffer = lcs_get_buffer(&card->write);
-+		if (card->tx_buffer == NULL) {
-+			card->stats.tx_dropped++;
-+			return -EBUSY;
-+		}
-+		card->tx_buffer->callback = lcs_txbuffer_cb;
-+		card->tx_buffer->count = 0;
-+	}
-+	header = (struct lcs_header *)
-+		(card->tx_buffer->data + card->tx_buffer->count);
-+	card->tx_buffer->count += skb->len + sizeof(struct lcs_header);
-+	header->offset = card->tx_buffer->count;
-+	header->type = card->lan_type;
-+	header->slot = card->portno;
-+	memcpy(header + 1, skb->data, skb->len);
-+	card->stats.tx_bytes += skb->len;
-+	card->stats.tx_packets++;
-+	dev_kfree_skb(skb);
-+	if (card->tx_emitted <= 0)
-+		/* If this is the first tx buffer emit it immediatly. */
-+		__lcs_emit_txbuffer(card);
-+	return 0;
-+}
-+
-+static int
-+lcs_start_xmit(struct sk_buff *skb, struct net_device *dev)
-+{
-+	struct lcs_card *card;
-+	int rc;
-+
-+	LCS_DBF_TEXT(5, trace, "pktxmit");
-+	card = (struct lcs_card *) dev->priv;
-+	spin_lock(&card->lock);
-+	rc = __lcs_start_xmit(card, skb, dev);
-+	spin_unlock(&card->lock);
-+	return rc;
-+}
-+
-+/**
-+ * send startlan and lanstat command to make LCS device ready
-+ */
-+static int
-+lcs_startlan_auto(struct lcs_card *card)
-+{
-+	int rc;
-+
-+	LCS_DBF_TEXT(2, trace,"strtauto");
-+#ifdef CONFIG_NET_ETHERNET
-+	card->lan_type = LCS_FRAME_TYPE_ENET;
-+	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
-+	if (rc == 0)
-+		return 0;
-+
-+#endif
-+#ifdef CONFIG_TR
-+	card->lan_type = LCS_FRAME_TYPE_TR;
-+	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
-+	if (rc == 0)
-+		return 0;
-+#endif
-+#ifdef CONFIG_FDDI
-+	card->lan_type = LCS_FRAME_TYPE_FDDI;
-+	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
-+	if (rc == 0)
-+		return 0;
-+#endif
-+	return -EIO;
-+}
-+
-+static int
-+lcs_startlan(struct lcs_card *card)
-+{
-+	int rc, i;
-+
-+	LCS_DBF_TEXT(2, trace, "startlan");
-+	rc = 0;
-+	if (card->device_forced) {
-+		card->portno = card->port_protocol_no;
-+		if (card->lan_type == LCS_FRAME_TYPE_AUTO)
-+			rc = lcs_startlan_auto(card);
-+		else
-+			rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
-+	} else {
-+		for (i = 0; i <= card->max_port_no; i++) {
-+			card->portno = i;
-+			if (i == 0)
-+				card->portno = card->hint_port_no;
-+			else if (i == card->hint_port_no)
-+				card->portno = 0;
-+
-+			if (card->lan_type != LCS_FRAME_TYPE_AUTO)
-+				rc = lcs_send_startlan(card,
-+						       LCS_INITIATOR_TCPIP);
-+			else
-+				/* autodetecting lan type */
-+				rc = lcs_startlan_auto(card);
-+			if (rc == 0)
-+				break;
-+		}
-+	}
-+	if (rc == 0)
-+		return lcs_send_lanstat(card);
-+	return rc;
-+}
-+
-+/**
-+ * LCS detect function
-+ * setup channels and make them I/O ready
-+ */
-+static int
-+lcs_detect(struct lcs_card *card)
-+{
-+	int rc;
-+
-+	LCS_DBF_TEXT(2, setup,"lcsdetct");
-+
-+	/* start/reset card */
-+	if (card->dev)
-+		netif_stop_queue(card->dev);
-+	rc = lcs_stop_channels(card);
-+	if (rc == 0) {
-+		rc = lcs_start_channels(card);
-+		if (rc == 0) {
-+			rc = lcs_send_startup(card, LCS_INITIATOR_TCPIP);
-+			if (rc == 0)
-+				rc = lcs_startlan(card);
-+		}
-+	}
-+	if (rc == 0) {
-+		card->state = DEV_STATE_UP;
-+	} else {
-+		card->state = DEV_STATE_DOWN;
-+		card->write.state = CH_STATE_INIT;
-+		card->read.state =  CH_STATE_INIT;
-+	}
-+	return rc;
-+}
-+
-+/**
-+ * reset card
-+ */
-+static int
-+lcs_resetcard(struct lcs_card *card)
-+{
-+	int retries;
-+
-+	LCS_DBF_TEXT(2, trace, "rescard");
-+	for (retries = 0; retries < 10; retries++) {
-+		if (lcs_detect(card) == 0) {
-+			netif_wake_queue(card->dev);
-+			card->state = DEV_STATE_UP;
-+			PRINT_INFO("LCS device %s successfully restarted!\n",
-+				   card->dev->name);
-+			return 0;
-+		}
-+		schedule_timeout(3 * HZ);
-+	}
-+	PRINT_ERR("Error in Reseting LCS card!\n");
-+	return -EIO;
-+}
-+
-+/**
-+ * LCS Stop card
-+ */
-+static int
-+lcs_stopcard(struct lcs_card *card)
-+{
-+	int rc;
-+
-+	LCS_DBF_TEXT(2, setup, "stopcard");
-+	if (card->read.state != CH_STATE_STOPPED &&
-+	    card->write.state != CH_STATE_STOPPED &&
-+	    card->state == DEV_STATE_UP) {
-+		lcs_clear_multicast_list(card);
-+		rc = lcs_send_stoplan(card,LCS_INITIATOR_TCPIP);
-+		rc = lcs_send_shutdown(card);
-+	}
-+	rc = lcs_stop_channels(card);
-+	card->state = DEV_STATE_DOWN;
-+	return rc;
-+}
-+
-+/**
-+ * LGW initiated commands
-+ */
-+static int
-+lcs_lgw_startlan_thread(void *data)
-+{
-+	struct lcs_card *card;
-+
-+	card = (struct lcs_card *) data;
-+	daemonize();
-+
-+	if (!lcs_do_run_thread(card, LCS_STARTLAN_THREAD))
-+		return 0;
-+	LCS_DBF_TEXT(2, trace, "lgwstpln");
-+	if (card->dev)
-+		netif_stop_queue(card->dev);
-+	if (lcs_startlan(card) == 0) {
-+		netif_wake_queue(card->dev);
-+		card->state = DEV_STATE_UP;
-+		PRINT_INFO("LCS Startlan for device %s succeeded!\n",
-+			   card->dev->name);
-+
-+	} else
-+		PRINT_ERR("LCS Startlan for device %s failed!\n",
-+			  card->dev->name);
-+	lcs_clear_thread_running_bit(card, LCS_STARTLAN_THREAD);
-+	return 0;
-+}
-+
-+/**
-+ * Send startup command initiated by Lan Gateway
-+ */
-+static int
-+lcs_lgw_startup_thread(void *data)
-+{
-+	int rc;
-+
-+	struct lcs_card *card;
-+
-+	card = (struct lcs_card *) data;
-+	daemonize();
-+
-+	if (!lcs_do_run_thread(card, LCS_STARTUP_THREAD))
-+                return 0;
-+	LCS_DBF_TEXT(2, trace, "lgwstaln");
-+	if (card->dev)
-+		netif_stop_queue(card->dev);
-+	rc = lcs_send_startup(card, LCS_INITIATOR_LGW);
-+	if (rc != 0) {
-+		PRINT_ERR("Startup for LCS device %s initiated " \
-+			  "by LGW failed!\nReseting card ...\n",
-+			  card->dev->name);
-+		/* do a card reset */
-+		rc = lcs_resetcard(card);
-+		if (rc == 0)
-+			goto Done;
-+	}
-+	rc = lcs_startlan(card);
-+	if (rc == 0) {
-+		netif_wake_queue(card->dev);
-+		card->state = DEV_STATE_UP;
-+	}
-+Done:
-+	if (rc == 0)
-+		PRINT_INFO("LCS Startup for device %s succeeded!\n",
-+			   card->dev->name);
-+	else
-+		PRINT_ERR("LCS Startup for device %s failed!\n",
-+			  card->dev->name);
-+	lcs_clear_thread_running_bit(card, LCS_STARTUP_THREAD);
-+	return 0;
-+}
-+
-+
-+/**
-+ * send stoplan command initiated by Lan Gateway
-+ */
-+static int
-+lcs_lgw_stoplan_thread(void *data)
-+{
-+	struct lcs_card *card;
-+	int rc;
-+
-+	card = (struct lcs_card *) data;
-+	daemonize();
-+	
-+	if (!lcs_do_run_thread(card, LCS_STOPLAN_THREAD))
-+                return 0;
-+	LCS_DBF_TEXT(2, trace, "lgwstop");
-+	if (card->dev)
-+		netif_stop_queue(card->dev);
-+	if (lcs_send_stoplan(card, LCS_INITIATOR_LGW) == 0)
-+		PRINT_INFO("Stoplan for %s initiated by LGW succeeded!\n",
-+			   card->dev->name);
-+	else
-+		PRINT_ERR("Stoplan %s initiated by LGW failed!\n",
-+			  card->dev->name);
-+	/*Try to reset the card, stop it on failure */
-+	rc = lcs_resetcard(card);
-+	if (rc != 0)
-+		rc = lcs_stopcard(card);
-+	lcs_clear_thread_running_bit(card, LCS_STOPLAN_THREAD);
-+	return rc;
-+}
-+
-+/**
-+ * Kernel Thread helper functions for LGW initiated commands
-+ */
-+static void
-+lcs_start_kernel_thread(struct lcs_card *card)
-+{
-+	LCS_DBF_TEXT(5, trace, "krnthrd");
-+	if (lcs_do_start_thread(card, LCS_STARTUP_THREAD))
-+                kernel_thread(lcs_lgw_startup_thread, (void *) card, SIGCHLD);
-+        if (lcs_do_start_thread(card, LCS_STARTLAN_THREAD))
-+                kernel_thread(lcs_lgw_startlan_thread, (void *) card, SIGCHLD);
-+        if (lcs_do_start_thread(card, LCS_STOPLAN_THREAD))
-+                kernel_thread(lcs_lgw_stoplan_thread, (void *) card, SIGCHLD);
-+#ifdef CONFIG_IP_MULTICAST
-+        if (lcs_do_start_thread(card, LCS_SET_MC_THREAD))
-+                kernel_thread(lcs_register_mc_addresses, (void *) card, SIGCHLD);
-+#endif
-+}
-+
-+/**
-+ * Process control frames.
-+ */
-+static void
-+lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd)
-+{
-+	LCS_DBF_TEXT(5, trace, "getctrl");
-+	if (cmd->initiator == LCS_INITIATOR_LGW) {
-+		switch(cmd->cmd_code) {
-+		case LCS_CMD_STARTUP:
-+			if (!lcs_set_thread_start_bit(card,
-+                                                      LCS_STARTUP_THREAD))
-+				schedule_task(&card->kernel_thread_starter);
-+			break;
-+		case LCS_CMD_STARTLAN:
-+			if (!lcs_set_thread_start_bit(card,
-+                                                      LCS_STARTLAN_THREAD))
-+			schedule_task(&card->kernel_thread_starter);
-+			break;
-+		case LCS_CMD_STOPLAN:
-+			if (!lcs_set_thread_start_bit(card,
-+                                                      LCS_STOPLAN_THREAD))
-+			schedule_task(&card->kernel_thread_starter);
-+			break;
-+		default:
-+			PRINT_INFO("UNRECOGNIZED LGW COMMAND\n");
-+			break;
-+		}
-+	} else
-+		lcs_notify_lancmd_waiters(card, cmd);
-+}
-+
-+/**
-+ * Unpack network packet.
-+ */
-+static void
-+lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
-+{
-+	struct sk_buff *skb;
-+
-+	LCS_DBF_TEXT(5, trace, "getskb");
-+	if (card->dev == NULL ||
-+	    card->state != DEV_STATE_UP)
-+		/* The card isn't up. Ignore the packet. */
-+		return;
-+
-+	skb = dev_alloc_skb(skb_len);
-+	if (skb == NULL) {
-+		PRINT_ERR("LCS: alloc_skb failed for device=%s\n",
-+			  card->dev->name);
-+		card->stats.rx_dropped++;
-+		return;
-+	}
-+	skb->dev = card->dev;
-+	memcpy(skb_put(skb, skb_len), skb_data, skb_len);
-+	skb->protocol =	card->lan_type_trans(skb, card->dev);
-+	card->stats.rx_bytes += skb_len;
-+	card->stats.rx_packets++;
-+	*((__u32 *)skb->cb) = ++card->pkt_seq;
-+	netif_rx(skb);
-+}
-+
-+/**
-+ * LCS main routine to get packets and lancmd replies from the buffers
-+ */
-+static void
-+lcs_get_frames_cb(struct lcs_channel *channel, struct lcs_buffer *buffer)
-+{
-+	struct lcs_card *card;
-+	struct lcs_header *lcs_hdr;
-+	__u16 offset;
-+
-+	LCS_DBF_TEXT(5, trace, "lcsgtpkt");
-+	lcs_hdr = (struct lcs_header *) buffer->data;
-+	if (lcs_hdr->offset == LCS_ILLEGAL_OFFSET) {
-+		LCS_DBF_TEXT(4, trace, "-eiogpkt");
-+		return;
-+	}
-+	card = (struct lcs_card *)
-+		((char *) channel - offsetof(struct lcs_card, read));
-+	offset = 0;
-+	while (lcs_hdr->offset != 0) {
-+		if (lcs_hdr->offset <= 0 ||
-+		    lcs_hdr->offset > LCS_IOBUFFERSIZE ||
-+		    lcs_hdr->offset < offset) {
-+			/* Offset invalid. */
-+			card->stats.rx_length_errors++;
-+			card->stats.rx_errors++;
-+			return;
-+		}
-+		/* What kind of frame is it? */
-+		if (lcs_hdr->type == LCS_FRAME_TYPE_CONTROL)
-+			/* Control frame. */
-+			lcs_get_control(card, (struct lcs_cmd *) lcs_hdr);
-+		else if (lcs_hdr->type == LCS_FRAME_TYPE_ENET ||
-+			 lcs_hdr->type == LCS_FRAME_TYPE_TR ||
-+			 lcs_hdr->type == LCS_FRAME_TYPE_FDDI)
-+			/* Normal network packet. */
-+			lcs_get_skb(card, (char *)(lcs_hdr + 1),
-+				    lcs_hdr->offset - offset -
-+				    sizeof(struct lcs_header));
-+		else
-+			/* Unknown frame type. */
-+			; // FIXME: error message ?
-+		/* Proceed to next frame. */
-+		offset = lcs_hdr->offset;
-+		lcs_hdr->offset = LCS_ILLEGAL_OFFSET;
-+		lcs_hdr = (struct lcs_header *) (buffer->data + offset);
-+	}
-+	/* The buffer is now empty. Make it ready again. */
-+	lcs_ready_buffer(&card->read, buffer);
-+}
-+
-+/**
-+ * get network statistics for ifconfig and other user programs
-+ */
-+struct net_device_stats *
-+lcs_getstats(struct net_device *dev)
-+{
-+	struct lcs_card *card;
-+
-+	LCS_DBF_TEXT(4, trace, "netstats");
-+	card = (struct lcs_card *) dev->priv;
-+	return &card->stats;
-+}
-+
-+/**
-+ * stop lcs device
-+ * This function will be called by user doing ifconfig xxx down
-+ */
-+int
-+lcs_stop_device(struct net_device *dev)
-+{
-+	struct lcs_card *card;
-+
-+	LCS_DBF_TEXT(2, trace, "stopdev");
-+	card = (struct lcs_card *) dev->priv;
-+	netif_stop_queue(dev);
-+	MOD_DEC_USE_COUNT;
-+	return 0;
-+}
-+
-+/**
-+ * start lcs device and make it runnable
-+ * This function will be called by user doing ifconfig xxx up
-+ */
-+int
-+lcs_open_device(struct net_device *dev)
-+{
-+	struct lcs_card *card;
-+
-+	LCS_DBF_TEXT(2, trace, "opendev");
-+	LCS_DBF_TEXT_(3,trace,"%s",dev->name);
-+	card = (struct lcs_card *) dev->priv;
-+	LCS_DBF_HEX(2, trace, &card, sizeof(void*));
-+	/* initialize statistics */
-+	MOD_INC_USE_COUNT;
-+	netif_wake_queue(dev);
-+	card->state = DEV_STATE_UP;
-+	return 0; 
-+}
-+
-+/**
-+ * LCS probe function
-+ * Main device detection routine called whenever lcs will be started
-+ * either as module or on bootup
-+ */
-+static int
-+lcs_probe(chandev_probeinfo *info)
-+{
-+	struct lcs_card *card;
-+	struct net_device *dev;
-+	int rc;
-+
-+	LCS_DBF_TEXT(2, setup, "lcsprobe");
-+	card = lcs_alloc_card();
-+	if (card == NULL) {
-+		PRINT_ERR("Allocation of lcs card failed\n");
-+		return -ENOMEM;
-+	}
-+	card->read.irq = info->read.irq;
-+	card->write.irq = info->write.irq;
-+	card->device_forced = info->device_forced;
-+	card->max_port_no = info->max_port_no;
-+	card->hint_port_no = info->hint_port_no;
-+	card->port_protocol_no = info->port_protocol_no;
-+	rc = lcs_setup_card(card);
-+	if (rc) {
-+		LCS_DBF_TEXT(3, setup, "errinit");
-+		PRINT_ERR("LCS card Initialization failed\n");
-+		lcs_free_card(card);
-+		return rc;
-+	}
-+
-+	/* Now let's detect and start LCS. */
-+	rc = lcs_detect(card);
-+	if (rc) {
-+		LCS_DBF_TEXT(2, setup, "dtctfail");
-+		lcs_stopcard(card);
-+		lcs_cleanup_card(card);
-+		lcs_free_card(card);
-+		return -ENODEV;
-+	}
-+	info->memory_usage_in_k =
-+		-((LCS_IOBUFFERSIZE * LCS_NUM_BUFFS +
-+		   LCS_NUM_BUFFS * LCS_IOBUFFERSIZE) / 1024);
-+	switch (card->lan_type) {
-+#ifdef CONFIG_NET_ETHERNET
-+	case LCS_FRAME_TYPE_ENET:
-+		card->lan_type_trans = eth_type_trans;
-+		dev = chandev_initnetdevice(info, card->portno,
-+					    NULL, 0, "eth",
-+					    init_etherdev,
-+					    unregister_netdev);
-+		break;
-+#endif
-+#ifdef CONFIG_TR
-+	case LCS_FRAME_TYPE_TR:
-+		card->lan_type_trans = tr_type_trans;
-+		dev = chandev_initnetdevice(info, card->portno,
-+					    NULL, 0, "tr",
-+					    init_trdev,
-+					    unregister_netdev);
-+		break;
-+#endif
-+#ifdef CONFIG_FDDI
-+	case LCS_FRAME_TYPE_FDDI:
-+		card->lan_type_trans = fddi_type_trans;
-+		dev = chandev_initnetdevice(info, card->portno,
-+					    NULL, 0, "fddi",
-+					    init_fddidev,
-+					    unregister_netdev);
-+		break;
-+#endif
-+	default:
-+		LCS_DBF_TEXT(2, setup, "errinit");
-+		PRINT_ERR("LCS: Initialization failed\n");
-+		PRINT_ERR("LCS: No device found!\n");
-+		lcs_cleanup_channel(&card->read);
-+		lcs_cleanup_channel(&card->write);
-+		lcs_free_card(card);
-+		return -ENODEV;
-+	}
-+	memcpy(dev->dev_addr, card->mac, LCS_MAC_LENGTH);
-+	card->dev = dev;
-+	dev->priv = card;
-+	dev->open = lcs_open_device;
-+	dev->stop = lcs_stop_device;
-+	dev->hard_start_xmit = lcs_start_xmit;
-+#ifdef CONFIG_IP_MULTICAST
-+	if (lcs_check_multicast_support(card))
-+		dev->set_multicast_list = lcs_set_multicast_list;
-+#endif
-+	dev->get_stats = lcs_getstats;
-+	netif_stop_queue(dev);
-+	lcs_set_allowed_threads(card, 0xffffffff);
-+	return 0;
-+}
-+
-+/**
-+ * shutdown function called by chandev
-+ */
-+static int
-+lcs_shutdown(struct net_device *dev)
-+{
-+	struct lcs_card *card;
-+
-+	LCS_DBF_TEXT(2, setup, "shtdndev");
-+	card = dev->priv;
-+	lcs_set_allowed_threads(card, 0);
-+	if (lcs_wait_for_threads(card, LCS_SET_MC_THREAD))
-+                return -ERESTARTSYS;
-+	if (card != NULL) {
-+		lcs_stopcard(card);
-+		lcs_cleanup_card(card);
-+		lcs_free_card(card);
-+	}
-+	return 0;
-+}
-+
-+/**
-+ * chandev notification function,only used by 2.4 kernel
-+ */
-+static void
-+lcs_msck_notify(struct net_device *device, int msck_irq,
-+		chandev_msck_status prevstatus,
-+		chandev_msck_status newstatus)
-+{
-+	struct lcs_card *card;
-+
-+	if (device->priv == NULL)
-+		return;
-+	LCS_DBF_TEXT(2, trace, "mscknot");
-+	card = (struct lcs_card *) device->priv;
-+
-+	if ((prevstatus != chandev_status_good) ||
-+	    (prevstatus != chandev_status_all_chans_good))
-+		if ((newstatus == chandev_status_good) ||
-+		    (newstatus == chandev_status_all_chans_good))
-+			lcs_resetcard(card);
-+	if ((newstatus == chandev_status_gone) ||
-+	    (newstatus == chandev_status_no_path) ||
-+	    (newstatus == chandev_status_not_oper))
-+		lcs_stopcard(card);
-+}
-+
-+static int 
-+lcs_verify_dev(struct net_device *dev)
-+{
-+	return (dev->hard_start_xmit==lcs_start_xmit);
-+}
-+
-+/**
-+ * multicast notifier structures
-+ */
-+#ifdef CONFIG_IP_MULTICAST
-+static int lcs_mc_event(struct notifier_block *this,
-+                       unsigned long event,void *ptr)
-+{
-+	struct ip_mc_list *mc  = (struct ip_mc_list *) ptr;
-+	struct net_device *dev = mc->interface->dev;
-+	struct lcs_card *card; 
-+
-+	LCS_DBF_TEXT(3,trace,"mcevent");
-+
-+	if (!lcs_verify_dev(dev)) 
-+		return NOTIFY_DONE;
-+	card  = (struct lcs_card *) dev->priv;
-+	if (!card)
-+		return NOTIFY_DONE;
-+	lcs_set_multicast_list(dev);
-+	return NOTIFY_DONE;
-+}
-+
-+static struct notifier_block lcs_mc_notifier = {
-+       lcs_mc_event,
-+       0
-+};
-+#endif
-+
-+/**
-+ *  LCS Module/Kernel initialization function
-+ */
-+static int
-+__init lcs_init_module(void)
-+{
-+	int cardsfound;
-+	int rc;
-+
-+	LCS_DBF_TEXT(0, setup, "lcsinit");
-+	PRINT_INFO("Loading %s\n",version);
-+	rc = lcs_register_debug_facility();
-+	if (rc) {
-+		PRINT_ERR("Initialization failed\n");
-+		return rc;
-+	}
-+
-+	cardsfound =
-+		chandev_register_and_probe(lcs_probe,
-+					   (chandev_shutdownfunc)
-+					   lcs_shutdown,
-+					   (chandev_msck_notification_func)
-+					   lcs_msck_notify, chandev_type_lcs);
-+#ifdef MODULE
-+	if (cardsfound <= 0 &&
-+	    !chandev_persist(chandev_type_lcs)) {
-+		chandev_unregister(lcs_probe,0);
-+		return -ENODEV;
-+	}
-+#else
-+	if (cardsfound <= 0)
-+		return -ENODEV;
-+#endif
-+
-+#ifdef CONFIG_IP_MULTICAST
-+	if (register_multicast_notifier(&lcs_mc_notifier)) {
-+		PRINT_ERR("register_multicast_notifier failed, maybe not " \
-+			  "all multicast addresses will be registered\n");
-+	}
-+#endif
-+
-+	return 0;
-+}
-+
-+
-+/**
-+ *  LCS module cleanup function
-+ */
-+static void
-+__exit lcs_cleanup_module(void)
-+{
-+	PRINT_INFO("Terminating lcs module.\n");
-+	LCS_DBF_TEXT(0, trace, "cleanup");
-+	chandev_unregister(lcs_probe, 1);
-+
-+#ifdef CONFIG_IP_MULTICAST
-+	unregister_multicast_notifier(&lcs_mc_notifier);
-+#endif
-+
-+	lcs_unregister_debug_facility();
-+}
-+
-+module_init(lcs_init_module);
-+module_exit(lcs_cleanup_module);
-+
-+MODULE_AUTHOR("Frank Pavlic <pavlic at de.ibm.com>");
-+MODULE_LICENSE("GPL");
-+
-=== drivers/s390/net/lcs.h
-==================================================================
---- drivers/s390/net/lcs.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/lcs.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,312 @@
-+/*lcs.h*/
-+
-+#include <linux/interrupt.h>
-+#include <linux/netdevice.h>
-+#include <linux/skbuff.h>
-+#include <asm/chandev.h>
-+#include <asm/irq.h>
-+
-+#define VERSION_LCS_H "$Revision: 1.1.4.1 $"
-+
-+#define LCS_DBF_TEXT(level, name, text) \
-+	do { \
-+		debug_text_event(lcs_dbf_##name, level, text); \
-+	} while (0)
-+
-+#define LCS_DBF_HEX(level,name,addr,len) \
-+do { \
-+	        debug_event(lcs_dbf_##name,level,(void*)(addr),len); \
-+} while (0)
-+
-+
-+#define LCS_DBF_TEXT_(level,name,text...) \
-+do {                                       \
-+	sprintf(debug_buffer, text);  \
-+		debug_text_event(lcs_dbf_##name,level, debug_buffer);\
-+} while (0)
-+
-+/**
-+ * some more definitions for debug or output stuff
-+ */
-+#define PRINTK_HEADER		" lcs: "
-+
-+/**
-+ * CCW commands used in this driver
-+ */
-+#define LCS_CCW_WRITE		0x01
-+#define LCS_CCW_READ		0x02
-+#define LCS_CCW_TRANSFER	0x08
-+
-+/**
-+ * LCS device status primitives
-+ */
-+#define LCS_CMD_STARTLAN	0x01
-+#define LCS_CMD_STOPLAN		0x02
-+#define LCS_CMD_LANSTAT		0x04
-+#define LCS_CMD_STARTUP		0x07
-+#define LCS_CMD_SHUTDOWN	0x08
-+#define LCS_CMD_QIPASSIST	0xb2
-+#define LCS_CMD_SETIPM		0xb4
-+#define LCS_CMD_DELIPM		0xb5
-+
-+#define LCS_INITIATOR_TCPIP	0x00
-+#define LCS_INITIATOR_LGW	0x01
-+#define LCS_STD_CMD_SIZE	16
-+#define LCS_MULTICAST_CMD_SIZE	404
-+
-+/**
-+ * LCS IPASSIST MASKS,only used when multicast is switched on
-+ */
-+/* Not supported by LCS */
-+#define LCS_IPASS_ARP_PROCESSING	0x0001
-+#define LCS_IPASS_IN_CHECKSUM_SUPPORT	0x0002
-+#define LCS_IPASS_OUT_CHECKSUM_SUPPORT	0x0004
-+#define LCS_IPASS_IP_FRAG_REASSEMBLY	0x0008
-+#define LCS_IPASS_IP_FILTERING		0x0010
-+/* Supported by lcs 3172 */
-+#define LCS_IPASS_IPV6_SUPPORT		0x0020
-+#define LCS_IPASS_MULTICAST_SUPPORT	0x0040
-+
-+/**
-+ * LCS sense byte definitions
-+ */
-+#define LCS_SENSE_INTERFACE_DISCONNECT	0x01
-+#define LCS_SENSE_EQUIPMENT_CHECK	0x10
-+#define LCS_SENSE_BUS_OUT_CHECK		0x20
-+#define LCS_SENSE_INTERVENTION_REQUIRED 0x40
-+#define LCS_SENSE_CMD_REJECT		0x80
-+#define LCS_SENSE_RESETTING_EVENT	0x0080
-+#define LCS_SENSE_DEVICE_ONLINE		0x0020
-+
-+/**
-+ * LCS packet type definitions
-+ */
-+#define LCS_FRAME_TYPE_CONTROL		0
-+#define LCS_FRAME_TYPE_ENET		1
-+#define LCS_FRAME_TYPE_TR		2
-+#define LCS_FRAME_TYPE_FDDI		7
-+#define LCS_FRAME_TYPE_AUTO		-1
-+
-+/**
-+ * some more definitions,we will sort them later
-+ */
-+#define LCS_ILLEGAL_OFFSET		0xffff
-+#define LCS_IOBUFFERSIZE		0x5000
-+#define LCS_NUM_BUFFS			8	/* needs to be power of 2 */
-+#define LCS_MAC_LENGTH			6
-+#define LCS_INVALID_PORT_NO		-1
-+
-+/**
-+ * Multicast state
-+ */
-+#define	 LCS_IPM_STATE_SET_REQUIRED	0
-+#define	 LCS_IPM_STATE_DEL_REQUIRED	1
-+#define	 LCS_IPM_STATE_ON_CARD		2
-+
-+/**
-+ * LCS IP Assist declarations
-+ * seems to be only used for multicast
-+ */
-+#define	 LCS_IPASS_ARP_PROCESSING	0x0001
-+#define	 LCS_IPASS_INBOUND_CSUM_SUPP	0x0002
-+#define	 LCS_IPASS_OUTBOUND_CSUM_SUPP	0x0004
-+#define	 LCS_IPASS_IP_FRAG_REASSEMBLY	0x0008
-+#define	 LCS_IPASS_IP_FILTERING		0x0010
-+#define	 LCS_IPASS_IPV6_SUPPORT		0x0020
-+#define	 LCS_IPASS_MULTICAST_SUPPORT	0x0040
-+
-+/**
-+ * LCS Buffer states
-+ */
-+enum lcs_buffer_states {
-+	BUF_STATE_EMPTY,	/* buffer is empty */
-+	BUF_STATE_LOCKED,	/* buffer is locked, don't touch */
-+	BUF_STATE_READY,	/* buffer is ready for read/write */
-+	BUF_STATE_PROCESSED,
-+};
-+
-+/**
-+ * LCS Channel State Machine declarations
-+ */
-+enum lcs_channel_states {
-+	CH_STATE_INIT,
-+	CH_STATE_HALTED,
-+	CH_STATE_STOPPED,
-+	CH_STATE_RUNNING,
-+	CH_STATE_SUSPENDED,
-+};
-+
-+/**
-+ * LCS device state machine
-+ */
-+enum lcs_dev_states {
-+	DEV_STATE_DOWN,
-+	DEV_STATE_UP,
-+};
-+
-+enum lcs_threads {
-+	LCS_SET_MC_THREAD       = 1,
-+	LCS_STARTLAN_THREAD     = 2,
-+	LCS_STOPLAN_THREAD      = 4,
-+	LCS_STARTUP_THREAD      = 8,
-+};
-+
-+/**
-+ * LCS struct declarations
-+ */
-+struct lcs_header {
-+	__u16  offset;
-+	__u8   type;
-+	__u8   slot;
-+}  __attribute__ ((packed));
-+
-+struct lcs_ip_mac_pair {
-+	__u32  ip_addr;
-+	__u8   mac_addr[LCS_MAC_LENGTH];
-+	__u8   reserved[2];
-+}  __attribute__ ((packed));
-+
-+struct lcs_ipm_list {
-+	struct list_head list;
-+	struct lcs_ip_mac_pair ipm;
-+	__u8 ipm_state;
-+};
-+
-+struct lcs_cmd {
-+	__u16  offset;
-+	__u8   type;
-+	__u8   slot;
-+	__u8   cmd_code;
-+	__u8   initiator;
-+	__u16  sequence_no;
-+	__u16  return_code;
-+	union {
-+		struct {
-+			__u8   lan_type;
-+			__u8   portno;
-+			__u16  parameter_count;
-+			__u8   operator_flags[3];
-+			__u8   reserved[3];
-+		} lcs_std_cmd;
-+		struct {
-+			__u16  unused1;
-+			__u16  buff_size;
-+			__u8   unused2[6];
-+		} lcs_startup;
-+		struct {
-+			__u8   lan_type;
-+			__u8   portno;
-+			__u8   unused[10];
-+			__u8   mac_addr[LCS_MAC_LENGTH];
-+			__u32  num_packets_deblocked;
-+			__u32  num_packets_blocked;
-+			__u32  num_packets_tx_on_lan;
-+			__u32  num_tx_errors_detected;
-+			__u32  num_tx_packets_disgarded;
-+			__u32  num_packets_rx_from_lan;
-+			__u32  num_rx_errors_detected;
-+			__u32  num_rx_discarded_nobuffs_avail;
-+			__u32  num_rx_packets_too_large;
-+		} lcs_lanstat_cmd;
-+#ifdef CONFIG_IP_MULTICAST
-+		struct {
-+			__u8   lan_type;
-+			__u8   portno;
-+			__u16  num_ip_pairs;
-+			__u16  ip_assists_supported;
-+			__u16  ip_assists_enabled;
-+			__u16  version;
-+			struct {
-+				struct lcs_ip_mac_pair
-+				ip_mac_pair[32];
-+				__u32	  response_data;
-+			} lcs_ipass_ctlmsg __attribute__ ((packed));
-+		} lcs_qipassist __attribute__ ((packed));
-+#endif /*CONFIG_IP_MULTICAST */
-+	} cmd __attribute__ ((packed));
-+}  __attribute__ ((packed));
-+
-+/**
-+ * Forward declarations.
-+ */
-+struct lcs_card;
-+struct lcs_channel;
-+
-+/**
-+ * Definition of an lcs buffer.
-+ */
-+struct lcs_buffer {
-+	enum lcs_buffer_states state;
-+	void *data;
-+	int count;
-+	/* Callback for completion notification. */
-+	void (*callback)(struct lcs_channel *, struct lcs_buffer *);
-+};
-+
-+struct lcs_reply {
-+	struct list_head list;
-+	__u16 sequence_no;
-+	 atomic_t refcnt;
-+	/* Callback for completion notification. */
-+	void (*callback)(struct lcs_card *, struct lcs_cmd *);
-+	wait_queue_head_t wait_q;
-+	struct lcs_card *card;
-+	int received;
-+	int rc;
-+};
-+
-+/**
-+ * Definition of an lcs channel
-+ */
-+struct lcs_channel {
-+	enum lcs_channel_states state;
-+	__u16 irq;
-+	ccw1_t ccws[LCS_NUM_BUFFS + 1];
-+	devstat_t devstat;
-+	wait_queue_head_t wait_q;
-+	struct tasklet_struct irq_tasklet;
-+	struct lcs_buffer iob[LCS_NUM_BUFFS];
-+	int io_idx;
-+	int buf_idx;
-+};
-+
-+/**
-+ * definition of the lcs card
-+ */
-+struct lcs_card {
-+	spinlock_t lock;
-+	spinlock_t ipm_lock;
-+	enum lcs_dev_states state;
-+	struct net_device *dev;
-+	struct net_device_stats stats;
-+	unsigned short (*lan_type_trans)(struct sk_buff *skb,
-+					 struct net_device *dev);
-+	struct lcs_channel read;
-+	struct lcs_channel write;
-+	struct lcs_buffer *tx_buffer;
-+	int tx_emitted;
-+	struct list_head lancmd_waiters;
-+
-+	struct tq_struct kernel_thread_starter;
-+	spinlock_t mask_lock;
-+	unsigned long thread_start_mask;
-+	unsigned long thread_running_mask;
-+	unsigned long thread_allowed_mask;
-+	wait_queue_head_t wait_q;
-+	
-+#ifdef CONFIG_IP_MULTICAST
-+	struct list_head ipm_list;
-+#endif
-+	__u8 mac[LCS_MAC_LENGTH];
-+	__u16 ip_assists_supported;
-+	__u16 ip_assists_enabled;
-+	__s8 lan_type;
-+	__u32 pkt_seq;
-+	__u16 sequence_no;
-+	__u16 portno;
-+	/* Some info copied from probeinfo */
-+	u8 device_forced;
-+	u8 max_port_no;
-+	u8 hint_port_no;
-+	s16 port_protocol_no;
-+}  __attribute__ ((aligned(8)));
-=== drivers/s390/net/Makefile
-==================================================================
---- drivers/s390/net/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/net/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -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
- 
-=== drivers/s390/s390io.c
-==================================================================
---- drivers/s390/s390io.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/s390io.c   (/trunk/2.4.27)   (revision 52)
-@@ -1,7 +1,7 @@
- /*
-  *  drivers/s390/s390io.c
-  *   S/390 common I/O routines
-- *   $Revision: 1.258 $
-+ *   $Revision: 1.247.4.4 $
-  *
-  *  S390 version
-  *    Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH,
-@@ -3043,7 +3043,16 @@
- 		if (!ioinfo[irq]->ui.flags.ready)
- 			return (ending_status);
- 
--		memcpy (udp, &(ioinfo[irq]->devstat), sdevstat);
-+		/*
-+		 * Special case: We got a deferred cc 3 on a basic sense.
-+		 * We have to notify the device driver of the former unit
-+		 * check, but must not confuse it by calling it with the status
-+		 * for the failed basic sense.
-+		 */
-+		if (ioinfo[irq]->ui.flags.w4sense)
-+			ioinfo[irq]->ui.flags.w4sense = 0;
-+		else
-+			memcpy (udp, &(ioinfo[irq]->devstat), sdevstat);
- 
- 		ioinfo[irq]->devstat.intparm = 0;
- 
-@@ -8328,15 +8337,14 @@
- {
- 	loff_t len;
- 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
--	loff_t pos = *offset;
- 
--	if (pos < 0 || pos >= p_info->len) {
-+	if (*offset >= p_info->len) {
- 		return 0;
- 	} 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;
- 	}
- }
-@@ -8411,15 +8419,14 @@
- {
- 	loff_t len;
- 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
--	loff_t pos = *offset;
- 
--	if (pos < 0 || pos >= p_info->len) {
-+	if (*offset >= p_info->len) {
- 		return 0;
- 	} 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;
- 	}
- }
-@@ -8876,15 +8883,14 @@
- {
- 	loff_t len;
- 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
--	loff_t pos = *offset;
- 
--	if (pos < 0 || pos >= p_info->len) {
-+	if (*offset >= p_info->len) {
- 		return 0;
- 	} else {
- 		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;
- 	}
- }
-@@ -8997,15 +9003,14 @@
- {
- 	loff_t len;
- 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
--	loff_t pos = *offset;
- 
--	if (pos < 0 || pos >= p_info->len) {
-+	if (*offset >= p_info->len) {
- 		return 0;
- 	} else {
- 		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;
- 	}
- }
-@@ -9127,15 +9132,14 @@
- {
-      loff_t len;
-      tempinfo_t *p_info = (tempinfo_t *) file->private_data;
--     loff_t pos = *offset;
-      
--     if (pos < 0 || pos >= p_info->len) {
-+     if ( *offset>=p_info->len) {
- 	  return 0;
-      } 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;
-      }
- }
-=== drivers/s390/Config.in
-==================================================================
---- drivers/s390/Config.in   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/Config.in   (/trunk/2.4.27)   (revision 52)
-@@ -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
- 
- 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
-+
-+    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
- 
-+
-+mainmenu_option next_comment
-+comment 'Miscellaneous'
-+  tristate 'Z90CRYPT support' CONFIG_Z90CRYPT
-+endmenu
-=== drivers/s390/block/dasd_diag.c
-==================================================================
---- drivers/s390/block/dasd_diag.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/block/dasd_diag.c   (/trunk/2.4.27)   (revision 52)
-@@ -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
-=== drivers/s390/block/dasd_3990_erp.c
-==================================================================
---- drivers/s390/block/dasd_3990_erp.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/block/dasd_3990_erp.c   (/trunk/2.4.27)   (revision 52)
-@@ -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);
--
-+		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");
-                 }
- 
-         }
-=== drivers/s390/block/dasd.c
-==================================================================
---- drivers/s390/block/dasd.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/block/dasd.c   (/trunk/2.4.27)   (revision 52)
-@@ -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;
-+
-+	memset (major_info->gendisk.label_arr, 0,
-+		DASD_PER_MAJOR * sizeof(devfs_handle_t));
-+
-         /* register blockdevice */
- 	rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations);
- 	if (rc < 0) {
-@@ -861,6 +870,9 @@
- 	}
- 
- out_reg_blkdev:
-+	kfree (major_info->gendisk.label_arr);
-+
-+out_gd_label_arr:
- 	kfree (major_info->gendisk.flags);
- 
- 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);
-+
-         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;
- }
- 
-=== drivers/s390/block/dasd_fba.c
-==================================================================
---- drivers/s390/block/dasd_fba.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/block/dasd_fba.c   (/trunk/2.4.27)   (revision 52)
-@@ -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;
- }
-=== drivers/s390/block/dcssblk.c
-==================================================================
---- drivers/s390/block/dcssblk.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/block/dcssblk.c   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-+
-+#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"
-+
-+#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];
-+
-+static int dcssblk_open (struct inode *inode, struct file *filp);
-+static int dcssblk_release (struct inode *inode, struct file *filp);
-+
-+static struct block_device_operations dcssblk_devops =
-+	{
-+		owner:   THIS_MODULE,
-+		open:    dcssblk_open,
-+		release: dcssblk_release,
-+	};
-+
-+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;
-+
-+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");
-+
-+/*
-+ * 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;
-+
-+}
-+
-+/*
-+ * 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;
-+
-+	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;
-+}
-+
-+/*
-+ * 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");
-+}
-+
-+/*
-+ * 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;
-+}
-+
-+/*
-+ * device attribute for listing devices
-+ */
-+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)
-+{
-+	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);
-+
-+	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;
-+}
-+
-+
-+/*
-+ * 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;
-+	}
-+	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;
-+}
-+
-+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)
-+{
-+#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 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
-+}
-+
-+/*
-+ * Finally, the init/exit functions.
-+ */
-+static void __exit dcssblk_exit(void)
-+{
-+	dcssblk_unregister_procfs();
-+	dcssblk_unregister_blkdev();
-+}
-+
-+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;
-+}
-+
-+module_init(dcssblk_init);
-+module_exit(dcssblk_exit);
-=== drivers/s390/block/dasd_int.h
-==================================================================
---- drivers/s390/block/dasd_int.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/block/dasd_int.h   (/trunk/2.4.27)   (revision 52)
-@@ -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 */
-=== drivers/s390/block/dasd_cmb.c
-==================================================================
---- drivers/s390/block/dasd_cmb.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/block/dasd_cmb.c   (/trunk/2.4.27)   (revision 52)
-@@ -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");
-=== drivers/s390/block/Makefile
-==================================================================
---- drivers/s390/block/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/block/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -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)
--
-=== drivers/s390/misc/z90common.h
-==================================================================
---- drivers/s390/misc/z90common.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/misc/z90common.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,149 @@
-+/*
-+ *  linux/drivers/s390/misc/z90common.h
-+ *
-+ *  z90crypt 1.3.2
-+ *
-+ *  Copyright (C)  2001, 2004 IBM Corporation
-+ *  Author(s): Robert Burroughs (burrough at us.ibm.com)
-+ *             Eric Rossman (edrossma at us.ibm.com)
-+ *
-+ *  Hotplug & misc device support: Jochen Roehrig (roehrig 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.
-+ */
-+#ifndef _Z90COMMON_
-+#define _Z90COMMON_
-+#define VERSION_Z90COMMON_H "$Revision: 1.4.6.9 $"
-+#define RESPBUFFSIZE 256
-+#define PCI_FUNC_KEY_DECRYPT 0x5044
-+#define PCI_FUNC_KEY_ENCRYPT 0x504B
-+extern int ext_bitlens; 
-+enum devstat {
-+	DEV_GONE,		
-+	DEV_ONLINE,		
-+	DEV_QUEUE_FULL,		
-+	DEV_EMPTY,		
-+	DEV_NO_WORK,		
-+	DEV_BAD_MESSAGE,	
-+	DEV_TSQ_EXCEPTION,	
-+	DEV_RSQ_EXCEPTION,	
-+	DEV_SEN_EXCEPTION,	
-+	DEV_REC_EXCEPTION	
-+};
-+enum hdstat {
-+	HD_NOT_THERE,		
-+	HD_BUSY,		
-+	HD_DECONFIGURED,	
-+	HD_CHECKSTOPPED,	
-+	HD_ONLINE,		
-+	HD_TSQ_EXCEPTION	
-+};
-+#define Z90C_NO_DEVICES		1
-+#define Z90C_AMBIGUOUS_DOMAIN	2
-+#define Z90C_INCORRECT_DOMAIN	3
-+#define ENOTINIT		4
-+#define SEN_BUSY	 7	
-+#define SEN_USER_ERROR	 8	
-+#define SEN_QUEUE_FULL	11	
-+#define SEN_NOT_AVAIL	16	
-+#define SEN_PAD_ERROR	17	
-+#define SEN_RETRY	18	
-+#define SEN_RELEASED	24	
-+#define REC_EMPTY	 4	
-+#define REC_BUSY	 6	
-+#define REC_OPERAND_INV	 8	
-+#define REC_OPERAND_SIZE 9	
-+#define REC_EVEN_MOD	10	
-+#define REC_NO_WORK	11	
-+#define REC_HARDWAR_ERR	12	
-+#define REC_NO_RESPONSE	13	
-+#define REC_RETRY_DEV	14	
-+#define REC_USER_GONE	15	
-+#define REC_BAD_MESSAGE	16	
-+#define REC_INVALID_PAD	17	
-+#define REC_USE_PCICA	18	
-+#define WRONG_DEVICE_TYPE 20	
-+#define REC_FATAL_ERROR 32	
-+#define SEN_FATAL_ERROR 33	
-+#define TSQ_FATAL_ERROR 34
-+#define RSQ_FATAL_ERROR 35
-+#define Z90CRYPT_NUM_TYPES	5
-+#define PCICA		0
-+#define PCICC		1
-+#define PCIXCC_MCL2	2
-+#define PCIXCC_MCL3	3
-+#define CEX2C		4
-+#define NILDEV		-1
-+#define ANYDEV		-1
-+#define PCIXCC_UNK	-2	
-+enum hdevice_type {
-+	PCICC_HW  = 3,
-+	PCICA_HW  = 4,
-+	PCIXCC_HW = 5,
-+	OTHER_HW  = 6,	
-+	CEX2C_HW  = 7
-+};
-+struct CPRBX {
-+	unsigned short cprb_len;	
-+	unsigned char  cprb_ver_id;	
-+	unsigned char  pad_000[3];	
-+	unsigned char  func_id[2];	
-+	unsigned char  cprb_flags[4];	
-+	unsigned int   req_parml;	
-+	unsigned int   req_datal;	
-+	unsigned int   rpl_msgbl;	
-+	unsigned int   rpld_parml;	
-+	unsigned int   rpl_datal;	
-+	unsigned int   rpld_datal;	
-+	unsigned int   req_extbl;	
-+	unsigned char  pad_001[4];	
-+	unsigned int   rpld_extbl;	
-+	unsigned char  req_parmb[16];	
-+	unsigned char  req_datab[16];	
-+	unsigned char  rpl_parmb[16];	
-+	unsigned char  rpl_datab[16];	
-+	unsigned char  req_extb[16];	
-+	unsigned char  rpl_extb[16];	
-+	unsigned short ccp_rtcode;	
-+	unsigned short ccp_rscode;	
-+	unsigned int   mac_data_len;	
-+	unsigned char  logon_id[8];	
-+	unsigned char  mac_value[8];	
-+	unsigned char  mac_content_flgs;
-+	unsigned char  pad_002;		
-+	unsigned short domain;		
-+	unsigned char  pad_003[12];	
-+	unsigned char  pad_004[36];	
-+};
-+#ifndef DEV_NAME
-+#define DEV_NAME	"z90crypt"
-+#endif
-+#define PRINTK(fmt, args...) \
-+	printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-+#define PRINTKN(fmt, args...) \
-+	printk(KERN_DEBUG DEV_NAME ": " fmt, ## args)
-+#define PRINTKW(fmt, args...) \
-+	printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-+#define PRINTKC(fmt, args...) \
-+	printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-+#ifdef Z90CRYPT_DEBUG
-+#define PDEBUG(fmt, args...) \
-+	printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-+#else
-+#define PDEBUG(fmt, args...) do {} while (0)
-+#endif
-+#define UMIN(a,b) ((a) < (b) ? (a) : (b))
-+#define IS_EVEN(x) ((x) == (2 * ((x) / 2)))
-+#endif
-=== drivers/s390/misc/z90hardware.c
-==================================================================
---- drivers/s390/misc/z90hardware.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/misc/z90hardware.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,2092 @@
-+/*
-+ *  linux/drivers/s390/misc/z90hardware.c
-+ *
-+ *  z90crypt 1.3.2
-+ *
-+ *  Copyright (C)  2001, 2004 IBM Corporation
-+ *  Author(s): Robert Burroughs (burrough at us.ibm.com)
-+ *             Eric Rossman (edrossma at us.ibm.com)
-+ *
-+ *  Hotplug & misc device support: Jochen Roehrig (roehrig 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 <asm/uaccess.h>	
-+#include <linux/compiler.h>
-+#include <linux/delay.h>	
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include "z90crypt.h"
-+#include "z90common.h"
-+#define VERSION_Z90HARDWARE_C "$Revision: 1.7.6.10 $"
-+char z90chardware_version[] __initdata =
-+	"z90hardware.o (" VERSION_Z90HARDWARE_C "/"
-+	                  VERSION_Z90COMMON_H "/" VERSION_Z90CRYPT_H ")";
-+struct cca_token_hdr {
-+	unsigned char  token_identifier;
-+	unsigned char  version;
-+	unsigned short token_length;
-+	unsigned char  reserved[4];
-+};
-+#define CCA_TKN_HDR_ID_EXT 0x1E
-+struct cca_private_ext_ME_sec {
-+	unsigned char  section_identifier;
-+	unsigned char  version;
-+	unsigned short section_length;
-+	unsigned char  private_key_hash[20];
-+	unsigned char  reserved1[4];
-+	unsigned char  key_format;
-+	unsigned char  reserved2;
-+	unsigned char  key_name_hash[20];
-+	unsigned char  key_use_flags[4];
-+	unsigned char  reserved3[6];
-+	unsigned char  reserved4[24];
-+	unsigned char  confounder[24];
-+	unsigned char  exponent[128];
-+	unsigned char  modulus[128];
-+};
-+#define CCA_PVT_USAGE_ALL 0x80
-+struct cca_public_sec {
-+	unsigned char  section_identifier;
-+	unsigned char  version;
-+	unsigned short section_length;
-+	unsigned char  reserved[2];
-+	unsigned short exponent_len;
-+	unsigned short modulus_bit_len;
-+	unsigned short modulus_byte_len;    
-+	unsigned char  exponent[3];
-+};
-+struct cca_private_ext_ME {
-+	struct cca_token_hdr	      pvtMEHdr;
-+	struct cca_private_ext_ME_sec pvtMESec;
-+	struct cca_public_sec	      pubMESec;
-+};
-+struct cca_public_key {
-+	struct cca_token_hdr  pubHdr;
-+	struct cca_public_sec pubSec;
-+};
-+struct cca_pvt_ext_CRT_sec {
-+	unsigned char  section_identifier;
-+	unsigned char  version;
-+	unsigned short section_length;
-+	unsigned char  private_key_hash[20];
-+	unsigned char  reserved1[4];
-+	unsigned char  key_format;
-+	unsigned char  reserved2;
-+	unsigned char  key_name_hash[20];
-+	unsigned char  key_use_flags[4];
-+	unsigned short p_len;
-+	unsigned short q_len;
-+	unsigned short dp_len;
-+	unsigned short dq_len;
-+	unsigned short u_len;
-+	unsigned short mod_len;
-+	unsigned char  reserved3[4];
-+	unsigned short pad_len;
-+	unsigned char  reserved4[52];
-+	unsigned char  confounder[8];
-+};
-+#define CCA_PVT_EXT_CRT_SEC_ID_PVT 0x08
-+#define CCA_PVT_EXT_CRT_SEC_FMT_CL 0x40
-+struct cca_private_ext_CRT {
-+	struct cca_token_hdr	   pvtCrtHdr;
-+	struct cca_pvt_ext_CRT_sec pvtCrtSec;
-+	struct cca_public_sec	   pubCrtSec;
-+};
-+struct ap_status_word {
-+	unsigned char q_stat_flags;
-+	unsigned char response_code;
-+	unsigned char reserved[2];
-+};
-+#define AP_Q_STATUS_EMPTY		0x80
-+#define AP_Q_STATUS_REPLIES_WAITING	0x40
-+#define AP_Q_STATUS_ARRAY_FULL		0x20
-+#define AP_RESPONSE_NORMAL		0x00
-+#define AP_RESPONSE_Q_NOT_AVAIL		0x01
-+#define AP_RESPONSE_RESET_IN_PROGRESS	0x02
-+#define AP_RESPONSE_DECONFIGURED	0x03
-+#define AP_RESPONSE_CHECKSTOPPED	0x04
-+#define AP_RESPONSE_BUSY		0x05
-+#define AP_RESPONSE_Q_FULL		0x10
-+#define AP_RESPONSE_NO_PENDING_REPLY	0x10   
-+#define AP_RESPONSE_INDEX_TOO_BIG	0x11
-+#define AP_RESPONSE_NO_FIRST_PART	0x13
-+#define AP_RESPONSE_MESSAGE_TOO_BIG	0x15
-+#define AP_MAX_CDX_BITL		4
-+#define AP_RQID_RESERVED_BITL	4
-+#define SKIP_BITL		(AP_MAX_CDX_BITL + AP_RQID_RESERVED_BITL)
-+struct type4_hdr {
-+	unsigned char  reserved1;
-+	unsigned char  msg_type_code;	
-+	unsigned short msg_len;
-+	unsigned char  request_code;	
-+	unsigned char  msg_fmt;
-+	unsigned short reserved2;
-+};
-+#define TYPE4_TYPE_CODE 0x04
-+#define TYPE4_REQU_CODE 0x40
-+#define TYPE4_SME_LEN 0x0188
-+#define TYPE4_LME_LEN 0x0308
-+#define TYPE4_SCR_LEN 0x01E0
-+#define TYPE4_LCR_LEN 0x03A0
-+#define TYPE4_SME_FMT 0x00
-+#define TYPE4_LME_FMT 0x10
-+#define TYPE4_SCR_FMT 0x40
-+#define TYPE4_LCR_FMT 0x50
-+struct type4_sme {
-+	struct type4_hdr header;
-+	unsigned char	 message[128];
-+	unsigned char	 exponent[128];
-+	unsigned char	 modulus[128];
-+};
-+struct type4_lme {
-+	struct type4_hdr header;
-+	unsigned char	 message[256];
-+	unsigned char	 exponent[256];
-+	unsigned char	 modulus[256];
-+};
-+struct type4_scr {
-+	struct type4_hdr header;
-+	unsigned char	 message[128];
-+	unsigned char	 dp[72];
-+	unsigned char	 dq[64];
-+	unsigned char	 p[72];
-+	unsigned char	 q[64];
-+	unsigned char	 u[72];
-+};
-+struct type4_lcr {
-+	struct type4_hdr header;
-+	unsigned char	 message[256];
-+	unsigned char	 dp[136];
-+	unsigned char	 dq[128];
-+	unsigned char	 p[136];
-+	unsigned char	 q[128];
-+	unsigned char	 u[136];
-+};
-+union type4_msg {
-+	struct type4_sme sme;
-+	struct type4_lme lme;
-+	struct type4_scr scr;
-+	struct type4_lcr lcr;
-+};
-+struct type84_hdr {
-+	unsigned char  reserved1;
-+	unsigned char  code;
-+	unsigned short len;
-+	unsigned char  reserved2[4];
-+};
-+#define TYPE84_RSP_CODE 0x84
-+struct type6_hdr {
-+	unsigned char reserved1;	
-+	unsigned char type;		
-+	unsigned char reserved2[2];	
-+	unsigned char right[4];		
-+	unsigned char reserved3[2];	
-+	unsigned char reserved4[2];	
-+	unsigned char apfs[4];		
-+	unsigned int  offset1;		
-+	unsigned int  offset2;		
-+	unsigned int  offset3;		
-+	unsigned int  offset4;		
-+	unsigned char agent_id[16];	
-+					
-+					
-+					
-+					
-+					
-+					
-+	unsigned char rqid[2];		
-+	unsigned char reserved5[2];	
-+	unsigned char function_code[2];	
-+	unsigned char reserved6[2];	
-+	unsigned int  ToCardLen1;	
-+	unsigned int  ToCardLen2;	
-+	unsigned int  ToCardLen3;	
-+	unsigned int  ToCardLen4;	
-+	unsigned int  FromCardLen1;	
-+	unsigned int  FromCardLen2;	
-+	unsigned int  FromCardLen3;	
-+	unsigned int  FromCardLen4;	
-+};
-+struct CPRB {
-+	unsigned char cprb_len[2];	
-+	unsigned char cprb_ver_id;	
-+	unsigned char pad_000;		
-+	unsigned char srpi_rtcode[4];	
-+	unsigned char srpi_verb;	
-+	unsigned char flags;		
-+	unsigned char func_id[2];	
-+	unsigned char checkpoint_flag;	
-+	unsigned char resv2;		
-+	unsigned char req_parml[2];	
-+					
-+	unsigned char req_parmp[4];	
-+	unsigned char req_datal[4];	
-+					
-+	unsigned char req_datap[4];	
-+					
-+	unsigned char rpl_parml[2];	
-+					
-+	unsigned char pad_001[2];	
-+	unsigned char rpl_parmp[4];	
-+	unsigned char rpl_datal[4];	
-+	unsigned char rpl_datap[4];	
-+					
-+	unsigned char ccp_rscode[2];	
-+	unsigned char ccp_rtcode[2];	
-+	unsigned char repd_parml[2];	
-+	unsigned char mac_data_len[2];	
-+	unsigned char repd_datal[4];	
-+	unsigned char req_pc[2];	
-+	unsigned char res_origin[8];	
-+	unsigned char mac_value[8];	
-+	unsigned char logon_id[8];	
-+	unsigned char usage_domain[2];	
-+	unsigned char resv3[18];	
-+	unsigned char svr_namel[2];	
-+	unsigned char svr_name[8];	
-+};
-+struct type6_msg {
-+	struct type6_hdr header;
-+	struct CPRB	 CPRB;
-+};
-+union request_msg {
-+	union  type4_msg t4msg;
-+	struct type6_msg t6msg;
-+};
-+struct request_msg_ext {
-+	int		  q_nr;
-+	unsigned char	  *psmid;
-+	union request_msg reqMsg;
-+};
-+struct type82_hdr {
-+	unsigned char reserved1;	
-+	unsigned char type;		
-+	unsigned char reserved2[2];	
-+	unsigned char reply_code;	
-+	unsigned char reserved3[3];	
-+};
-+#define TYPE82_RSP_CODE 0x82
-+#define REPLY_ERROR_MACHINE_FAILURE  0x10
-+#define REPLY_ERROR_PREEMPT_FAILURE  0x12
-+#define REPLY_ERROR_CHECKPT_FAILURE  0x14
-+#define REPLY_ERROR_MESSAGE_TYPE     0x20
-+#define REPLY_ERROR_INVALID_COMM_CD  0x21 
-+#define REPLY_ERROR_INVALID_MSG_LEN  0x23
-+#define REPLY_ERROR_RESERVD_FIELD    0x24 
-+#define REPLY_ERROR_FORMAT_FIELD     0x29
-+#define REPLY_ERROR_INVALID_COMMAND  0x30
-+#define REPLY_ERROR_MALFORMED_MSG    0x40
-+#define REPLY_ERROR_RESERVED_FIELDO  0x50 
-+#define REPLY_ERROR_WORD_ALIGNMENT   0x60
-+#define REPLY_ERROR_MESSAGE_LENGTH   0x80
-+#define REPLY_ERROR_OPERAND_INVALID  0x82
-+#define REPLY_ERROR_OPERAND_SIZE     0x84
-+#define REPLY_ERROR_EVEN_MOD_IN_OPND 0x85
-+#define REPLY_ERROR_RESERVED_FIELD   0x88
-+#define REPLY_ERROR_TRANSPORT_FAIL   0x90
-+#define REPLY_ERROR_PACKET_TRUNCATED 0xA0
-+#define REPLY_ERROR_ZERO_BUFFER_LEN  0xB0
-+struct type86_hdr {
-+	unsigned char reserved1;	
-+	unsigned char type;		
-+	unsigned char format;		
-+	unsigned char reserved2;	
-+	unsigned char reply_code;	
-+	unsigned char reserved3[3];	
-+};
-+#define TYPE86_RSP_CODE 0x86
-+#define TYPE86_FMT2	0x02
-+struct type86_fmt2_msg {
-+	struct type86_hdr hdr;
-+	unsigned char	  reserved[4];	
-+	unsigned char	  apfs[4];	
-+	unsigned int	  count1;	
-+	unsigned int	  offset1;	
-+	unsigned int	  count2;	
-+	unsigned int	  offset2;	
-+	unsigned int	  count3;	
-+	unsigned int	  offset3;	
-+	unsigned int	  count4;	
-+	unsigned int	  offset4;	
-+};
-+static struct type6_hdr static_type6_hdr = {
-+	0x00,
-+	0x06,
-+	{0x00,0x00},
-+	{0x00,0x00,0x00,0x00},
-+	{0x00,0x00},
-+	{0x00,0x00},
-+	{0x00,0x00,0x00,0x00},
-+	0x00000058,
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,
-+	{0x01,0x00,0x43,0x43,0x41,0x2D,0x41,0x50,
-+	 0x50,0x4C,0x20,0x20,0x20,0x01,0x01,0x01},
-+	{0x00,0x00},
-+	{0x00,0x00},
-+	{0x50,0x44},
-+	{0x00,0x00},
-+	0x00000000,	
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,	
-+	0x00000000,
-+	0x00000000,
-+	0x00000000
-+};
-+static struct type6_hdr static_type6_hdrX = {
-+	0x00,
-+	0x06,
-+	{0x00,0x00},
-+	{0x00,0x00,0x00,0x00},
-+	{0x00,0x00},
-+	{0x00,0x00},
-+	{0x00,0x00,0x00,0x00},
-+	0x00000058,
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,
-+	{0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00},
-+	{0x00,0x00},
-+	{0x50,0x44},
-+	{0x00,0x00},
-+	0x00000000,	
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,	
-+	0x00000000,
-+	0x00000000,
-+	0x00000000
-+};
-+static struct CPRB static_cprb = {
-+	{0x70,0x00},
-+	0x41,
-+	0x00,
-+	{0x00,0x00,0x00,0x00},
-+	0x00,
-+	0x00,
-+	{0x54,0x32},
-+	0x01,
-+	0x00,
-+	{0x00,0x00},	
-+	{0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00},
-+	{0x00,0x00},	
-+	{0x00,0x00},
-+	{0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00},
-+	{0x00,0x00},
-+	{0x00,0x00},
-+	{0x00,0x00},
-+	{0x00,0x00},
-+	{0x00,0x00,0x00,0x00},
-+	{0x00,0x00},
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00},	
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00},
-+	{0x08,0x00},
-+	{0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20}
-+};
-+struct function_and_rules_block {
-+	unsigned char function_code[2];
-+	unsigned char ulen[2];
-+	unsigned char only_rule[8];
-+};
-+static struct function_and_rules_block static_pkd_function_and_rules = {
-+	{0x50,0x44},			   
-+	{0x0A,0x00},			   
-+	{'P','K','C','S','-','1','.','2'}
-+};
-+static struct function_and_rules_block static_pke_function_and_rules = {
-+	{0x50,0x4B},			   
-+	{0x0A,0x00},			   
-+	{'P','K','C','S','-','1','.','2'}
-+};
-+struct T6_keyBlock_hdr {
-+	unsigned char blen[2];
-+	unsigned char ulen[2];
-+	unsigned char flags[2];
-+};
-+static struct T6_keyBlock_hdr static_T6_keyBlock_hdr = {
-+	{0x89,0x01},	   
-+	{0x87,0x01},	   
-+	{0x00}
-+};
-+static struct CPRBX static_cprbx = {
-+	0x00DC,
-+	0x02,
-+	{0x00,0x00,0x00},
-+	{0x54,0x32},
-+	{0x00,0x00,0x00,0x00},
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,
-+	0x00000000,
-+	{0x00,0x00,0x00,0x00},
-+	0x00000000,
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	0x0000,
-+	0x0000,
-+	0x00000000,
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	0x00,
-+	0x00,
-+	0x0000,
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
-+};
-+static struct function_and_rules_block static_pkd_function_and_rulesX_MCL2 = {
-+	{0x50,0x44},	
-+	{0x00,0x0A},	
-+	{'P','K','C','S','-','1','.','2'}
-+};
-+static struct function_and_rules_block static_pke_function_and_rulesX_MCL2 = {
-+	{0x50,0x4B},	
-+	{0x00,0x0A},	
-+	{'Z','E','R','O','-','P','A','D'}
-+};
-+static struct function_and_rules_block static_pkd_function_and_rulesX = {
-+	{0x50,0x44},	
-+	{0x00,0x0A},	
-+	{'Z','E','R','O','-','P','A','D'}
-+};
-+static struct function_and_rules_block static_pke_function_and_rulesX = {
-+	{0x50,0x4B},	
-+	{0x00,0x0A},	
-+	{'M','R','P',' ',' ',' ',' ',' '}
-+};
-+struct T6_keyBlock_hdrX {
-+	unsigned short blen;
-+	unsigned short ulen;
-+	unsigned char flags[2];
-+};
-+static unsigned char static_pad[256] = {
-+0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57,
-+0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39,
-+0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5,0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D,
-+0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB,0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F,
-+0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9,0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45,
-+0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9,0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F,
-+0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD,0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D,
-+0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD,0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9,
-+0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B,0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B,
-+0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B,0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD,
-+0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7,0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1,
-+0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3,0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23,
-+0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55,0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43,
-+0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F,0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F,
-+0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD,
-+0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09
-+};
-+static struct cca_private_ext_ME static_pvt_me_key = {
-+	{
-+		0x1E,
-+		0x00,
-+		0x0183,
-+		{0x00,0x00,0x00,0x00}
-+	},
-+	{
-+		0x02,
-+		0x00,
-+		0x016C,
-+		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00},
-+		{0x00,0x00,0x00,0x00},
-+		0x00,
-+		0x00,
-+		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00},
-+		{0x80,0x00,0x00,0x00},
-+		{0x00,0x00,0x00,0x00,0x00,0x00},
-+		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
-+		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
-+	},
-+	{
-+		0x04,
-+		0x00,
-+		0x000F,
-+		{0x00,0x00},
-+		0x0003,
-+		0x0000,
-+		0x0000,
-+		{0x01,0x00,0x01}
-+	}
-+};
-+static struct cca_public_key static_public_key = {
-+	{
-+		0x1E,
-+		0x00,
-+		0x0000,			
-+		{0x00,0x00,0x00,0x00}
-+	},
-+	{
-+		0x04,
-+		0x00,
-+		0x0000,			
-+		{0x00,0x00},
-+		0x0000,			
-+		0x0000,			
-+		0x0000,			
-+		{0x01,0x00,0x01}
-+	}
-+};
-+#define FIXED_TYPE6_ME_LEN 0x0000025F
-+#define FIXED_TYPE6_ME_EN_LEN 0x000000F0
-+#define FIXED_TYPE6_ME_LENX 0x000002CB
-+#define FIXED_TYPE6_ME_EN_LENX 0x0000015C
-+static struct cca_public_sec static_cca_pub_sec = {
-+	0x04,
-+	0x00,
-+	0x000f,
-+	{0x00,0x00},
-+	0x0003,
-+	0x0000,	      
-+	0x0000,
-+	{0x01,0x00,0x01}
-+};
-+#define FIXED_TYPE6_CR_LEN 0x00000177
-+#define FIXED_TYPE6_CR_LENX 0x000001E3
-+#define MAX_RESPONSE_SIZE 0x00000710
-+#define MAX_RESPONSEX_SIZE 0x0000077C
-+#define RESPONSE_CPRB_SIZE  0x000006B8 
-+#define RESPONSE_CPRBX_SIZE 0x00000724 
-+#define CALLER_HEADER 12	
-+static unsigned char static_PKE_function_code[2] = {0x50, 0x4B};
-+static inline int
-+testq(int q_nr, int *q_depth, int *dev_type, struct ap_status_word *stat)
-+{
-+	int ccode;
-+	asm volatile
-+#ifdef __s390x__
-+	("	llgfr	0,%4		\n"	
-+	 "	slgr	1,1		\n"	
-+	 "	lgr	2,1		\n"	
-+	 "0:	.long	0xb2af0000	\n"	
-+	 "1:	ipm	%0		\n"	
-+	 "	srl	%0,28		\n"	
-+	 "	iihh	%0,0		\n"	
-+	 "	iihl	%0,0		\n"	
-+	 "	lgr	%1,1		\n"	
-+	 "	lgr	%3,2		\n"
-+	 "	srl	%3,24		\n"	
-+	 "	sll	2,24		\n"
-+	 "	srl	2,24		\n"
-+	 "	lgr	%2,2		\n"	
-+	 "2:				\n"	
-+	 ".section .fixup,\"ax\"	\n"
-+	 "3:				\n"	
-+	 "	lhi	%0,%h5		\n"
-+	 "	jg	2b		\n"
-+	 ".previous			\n"
-+	 ".section __ex_table,\"a\"	\n"
-+	 "	.align	8		\n"
-+	 "	.quad	0b,3b		\n"
-+	 "	.quad	1b,3b		\n"
-+	 ".previous"
-+	 :"=d" (ccode),"=d" (*stat),"=d" (*q_depth), "=d" (*dev_type)
-+	 :"d" (q_nr), "K" (DEV_TSQ_EXCEPTION)
-+	 :"cc","0","1","2","memory");
-+#else
-+	("	lr	0,%4		\n"	
-+	 "	slr	1,1		\n"	
-+	 "	lr	2,1		\n"	
-+	 "0:	.long	0xb2af0000	\n"	
-+	 "1:	ipm	%0		\n"	
-+	 "	srl	%0,28		\n"	
-+	 "	lr	%1,1		\n"	
-+	 "	lr	%3,2		\n"
-+	 "	srl	%3,24		\n"	
-+	 "	sll	2,24		\n"
-+	 "	srl	2,24		\n"
-+	 "	lr	%2,2		\n"	
-+	 "2:				\n"	
-+	 ".section .fixup,\"ax\"	\n"
-+	 "3:				\n"	
-+	 "	lhi	%0,%h5		\n"
-+	 "	bras	1,4f		\n"
-+	 "	.long	2b		\n"
-+	 "4:				\n"
-+	 "	l	1,0(1)		\n"
-+	 "	br	1		\n"
-+	 ".previous			\n"
-+	 ".section __ex_table,\"a\"	\n"
-+	 "	.align	4		\n"
-+	 "	.long	0b,3b		\n"
-+	 "	.long	1b,3b		\n"
-+	 ".previous"
-+	 :"=d" (ccode),"=d" (*stat),"=d" (*q_depth), "=d" (*dev_type)
-+	 :"d" (q_nr), "K" (DEV_TSQ_EXCEPTION)
-+	 :"cc","0","1","2","memory");
-+#endif
-+	return ccode;
-+}
-+static inline int
-+resetq(int q_nr, struct ap_status_word *stat_p)
-+{
-+	int ccode;
-+	asm volatile
-+#ifdef __s390x__
-+	("	llgfr	0,%2		\n"	
-+	 "	lghi	1,1		\n"	
-+	 "	sll	1,24		\n"	
-+	 "	or	0,1		\n"	
-+	 "	slgr	1,1		\n"	
-+	 "	lgr	2,1		\n"	
-+	 "0:	.long	0xb2af0000	\n"	
-+	 "1:	ipm	%0		\n"	
-+	 "	srl	%0,28		\n"	
-+	 "	iihh	%0,0		\n"	
-+	 "	iihl	%0,0		\n"	
-+	 "	lgr	%1,1		\n"	
-+	 "2:				\n"	
-+	 ".section .fixup,\"ax\"	\n"
-+	 "3:				\n"	
-+	 "	lhi	%0,%h3		\n"
-+	 "	jg	2b		\n"
-+	 ".previous			\n"
-+	 ".section __ex_table,\"a\"	\n"
-+	 "	.align	8		\n"
-+	 "	.quad	0b,3b		\n"
-+	 "	.quad	1b,3b		\n"
-+	 ".previous"
-+	 :"=d" (ccode),"=d" (*stat_p)
-+	 :"d" (q_nr), "K" (DEV_RSQ_EXCEPTION)
-+	 :"cc","0","1","2","memory");
-+#else
-+	("	lr	0,%2		\n"	
-+	 "	lhi	1,1		\n"	
-+	 "	sll	1,24		\n"	
-+	 "	or	0,1		\n"	
-+	 "	slr	1,1		\n"	
-+	 "	lr	2,1		\n"	
-+	 "0:	.long	0xb2af0000	\n"	
-+	 "1:	ipm	%0		\n"	
-+	 "	srl	%0,28		\n"	
-+	 "	lr	%1,1		\n"	
-+	 "2:				\n"	
-+	 ".section .fixup,\"ax\"	\n"
-+	 "3:				\n"	
-+	 "	lhi	%0,%h3		\n"
-+	 "	bras	1,4f		\n"
-+	 "	.long	2b		\n"
-+	 "4:				\n"
-+	 "	l	1,0(1)		\n"
-+	 "	br	1		\n"
-+	 ".previous			\n"
-+	 ".section __ex_table,\"a\"	\n"
-+	 "	.align	4		\n"
-+	 "	.long	0b,3b		\n"
-+	 "	.long	1b,3b		\n"
-+	 ".previous"
-+	 :"=d" (ccode),"=d" (*stat_p)
-+	 :"d" (q_nr), "K" (DEV_RSQ_EXCEPTION)
-+	 :"cc","0","1","2","memory");
-+#endif
-+	return ccode;
-+}
-+static inline int
-+sen(int msg_len, unsigned char *msg_ext, struct ap_status_word *stat)
-+{
-+	int ccode;
-+	asm volatile
-+#ifdef __s390x__
-+	("	lgr	6,%3		\n"	
-+	 "	llgfr	7,%2		\n"	
-+	 "	llgt	0,0(6)		\n"	
-+	 "	lghi	1,64		\n"	
-+	 "	sll	1,24		\n"	
-+	 "	or	0,1		\n"	
-+	 "	la	6,4(6)		\n"	
-+	 "	llgt	2,0(6)		\n"	
-+	 "	llgt	3,4(6)		\n"	
-+	 "	la	6,8(6)		\n"	
-+	 "	slr	1,1		\n"	
-+	 "0:	.long	0xb2ad0026	\n"	
-+	 "1:	brc	2,0b		\n"	
-+	 "	ipm	%0		\n"	
-+	 "	srl	%0,28		\n"	
-+	 "	iihh	%0,0		\n"	
-+	 "	iihl	%0,0		\n"	
-+	 "	lgr	%1,1		\n"	
-+	 "2:				\n"	
-+	 ".section .fixup,\"ax\"	\n"
-+	 "3:				\n"	
-+	 "	lhi	%0,%h4		\n"
-+	 "	jg	2b		\n"
-+	 ".previous			\n"
-+	 ".section __ex_table,\"a\"	\n"
-+	 "	.align	8		\n"
-+	 "	.quad	0b,3b		\n"
-+	 "	.quad	1b,3b		\n"
-+	 ".previous"
-+	 :"=d" (ccode),"=d" (*stat)
-+	 :"d" (msg_len),"a" (msg_ext), "K" (DEV_SEN_EXCEPTION)
-+	 :"cc","0","1","2","3","6","7","memory");
-+#else
-+	("	lr	6,%3		\n"	
-+	 "	lr	7,%2		\n"	
-+	 "	l	0,0(6)		\n"	
-+	 "	lhi	1,64		\n"	
-+	 "	sll	1,24		\n"	
-+	 "	or	0,1		\n"	
-+	 "	la	6,4(6)		\n"	
-+	 "	l	2,0(6)		\n"	
-+	 "	l	3,4(6)		\n"	
-+	 "	la	6,8(6)		\n"	
-+	 "	slr	1,1		\n"	
-+	 "0:	.long	0xb2ad0026	\n"	
-+	 "1:	brc	2,0b		\n"	
-+	 "	ipm	%0		\n"	
-+	 "	srl	%0,28		\n"	
-+	 "	lr	%1,1		\n"	
-+	 "2:				\n"	
-+	 ".section .fixup,\"ax\"	\n"
-+	 "3:				\n"	
-+	 "	lhi	%0,%h4		\n"
-+	 "	bras	1,4f		\n"
-+	 "	.long	2b		\n"
-+	 "4:				\n"
-+	 "	l	1,0(1)		\n"
-+	 "	br	1		\n"
-+	 ".previous			\n"
-+	 ".section __ex_table,\"a\"	\n"
-+	 "	.align	4		\n"
-+	 "	.long	0b,3b		\n"
-+	 "	.long	1b,3b		\n"
-+	 ".previous"
-+	 :"=d" (ccode),"=d" (*stat)
-+	 :"d" (msg_len),"a" (msg_ext), "K" (DEV_SEN_EXCEPTION)
-+	 :"cc","0","1","2","3","6","7","memory");
-+#endif
-+	return ccode;
-+}
-+static inline int
-+rec(int q_nr, int buff_l, unsigned char *rsp, unsigned char *id,
-+    struct ap_status_word *st)
-+{
-+	int ccode;
-+	asm volatile
-+#ifdef __s390x__
-+	("	llgfr	0,%2		\n"	
-+	 "	lgr	3,%4		\n"	
-+	 "	lgr	6,%3		\n"	
-+	 "	llgfr	7,%5		\n"	
-+	 "	lghi	1,128		\n"	
-+	 "	sll	1,24		\n"	
-+	 "	or	0,1		\n"	
-+	 "	slgr	1,1		\n"	
-+	 "	lgr	2,1		\n"	
-+	 "	lgr	4,1		\n"	
-+	 "	lgr	5,1		\n"	
-+	 "0:	.long	0xb2ae0046	\n"	
-+	 "1:	brc	2,0b		\n"	
-+	 "	brc	4,0b		\n"	
-+	 "	ipm	%0		\n"	
-+	 "	srl	%0,28		\n"	
-+	 "	iihh	%0,0		\n"	
-+	 "	iihl	%0,0		\n"	
-+	 "	lgr	%1,1		\n"	
-+	 "	st	4,0(3)		\n"	
-+	 "	st	5,4(3)		\n"	
-+	 "2:				\n"	
-+	 ".section .fixup,\"ax\"	\n"
-+	 "3:				\n"	
-+	 "	lhi   %0,%h6		\n"
-+	 "	jg    2b		\n"
-+	 ".previous			\n"
-+	 ".section __ex_table,\"a\"	\n"
-+	 "   .align	8		\n"
-+	 "   .quad	0b,3b		\n"
-+	 "   .quad	1b,3b		\n"
-+	 ".previous"
-+	 :"=d"(ccode),"=d"(*st)
-+	 :"d" (q_nr), "d" (rsp), "d" (id), "d" (buff_l), "K" (DEV_REC_EXCEPTION)
-+	 :"cc","0","1","2","3","4","5","6","7","memory");
-+#else
-+	("	lr	0,%2		\n"	
-+	 "	lr	3,%4		\n"	
-+	 "	lr	6,%3		\n"	
-+	 "	lr	7,%5		\n"	
-+	 "	lhi	1,128		\n"	
-+	 "	sll	1,24		\n"	
-+	 "	or	0,1		\n"	
-+	 "	slr	1,1		\n"	
-+	 "	lr	2,1		\n"	
-+	 "	lr	4,1		\n"	
-+	 "	lr	5,1		\n"	
-+	 "0:	.long	0xb2ae0046	\n"	
-+	 "1:	brc	2,0b		\n"	
-+	 "	brc	4,0b		\n"	
-+	 "	ipm	%0		\n"	
-+	 "	srl	%0,28		\n"	
-+	 "	lr	%1,1		\n"	
-+	 "	st	4,0(3)		\n"	
-+	 "	st	5,4(3)		\n"	
-+	 "2:				\n"	
-+	 ".section .fixup,\"ax\"	\n"
-+	 "3:				\n"	
-+	 "	lhi   %0,%h6		\n"
-+	 "	bras  1,4f		\n"
-+	 "	.long 2b		\n"
-+	 "4:				\n"
-+	 "	l     1,0(1)		\n"
-+	 "	br    1			\n"
-+	 ".previous			\n"
-+	 ".section __ex_table,\"a\"	\n"
-+	 "   .align	4		\n"
-+	 "   .long	0b,3b		\n"
-+	 "   .long	1b,3b		\n"
-+	 ".previous"
-+	 :"=d"(ccode),"=d"(*st)
-+	 :"d" (q_nr), "d" (rsp), "d" (id), "d" (buff_l), "K" (DEV_REC_EXCEPTION)
-+	 :"cc","0","1","2","3","4","5","6","7","memory");
-+#endif
-+	return ccode;
-+}
-+static inline void
-+itoLe2(int *i_p, unsigned char *lechars)
-+{
-+	*lechars       = *((unsigned char *) i_p + sizeof(int) - 1);
-+	*(lechars + 1) = *((unsigned char *) i_p + sizeof(int) - 2);
-+}
-+static inline void
-+le2toI(unsigned char *lechars, int *i_p)
-+{
-+	unsigned char *ic_p;
-+	*i_p = 0;
-+	ic_p = (unsigned char *) i_p;
-+	*(ic_p + 2) = *(lechars + 1);
-+	*(ic_p + 3) = *(lechars);
-+}
-+static inline int
-+is_empty(unsigned char *ptr, int len)
-+{
-+	return !memcmp(ptr, (unsigned char *) &static_pvt_me_key+60, len);
-+}
-+enum hdstat
-+query_online(int deviceNr, int cdx, int resetNr, int *q_depth, int *dev_type)
-+{
-+	int q_nr, i, t_depth, t_dev_type;
-+	enum devstat ccode;
-+	struct ap_status_word stat_word;
-+	enum hdstat stat;
-+	int break_out;
-+	q_nr = (deviceNr << SKIP_BITL) + cdx;
-+	stat = HD_BUSY;
-+	ccode = testq(q_nr, &t_depth, &t_dev_type, &stat_word);
-+	PDEBUG("ccode %d response_code %02X\n", ccode, stat_word.response_code);
-+	break_out = 0;
-+	for (i = 0; i < resetNr; i++) {
-+		if (ccode > 3) {
-+			PRINTKC("Exception testing device %d\n", i);
-+			return HD_TSQ_EXCEPTION;
-+		}
-+		switch (ccode) {
-+		case 0:
-+			PDEBUG("t_dev_type %d\n", t_dev_type);
-+			break_out = 1;
-+			stat = HD_ONLINE;
-+			*q_depth = t_depth + 1;
-+			switch (t_dev_type) {
-+			case OTHER_HW:
-+				stat = HD_NOT_THERE;
-+				*dev_type = NILDEV;
-+				break;
-+			case PCICA_HW:
-+				*dev_type = PCICA;
-+				break;
-+			case PCICC_HW:
-+				*dev_type = PCICC;
-+				break;
-+			case PCIXCC_HW:
-+				*dev_type = PCIXCC_UNK;
-+				break;
-+			case CEX2C_HW:
-+				*dev_type = CEX2C;
-+				break;
-+			default:
-+				*dev_type = NILDEV;
-+				break;
-+			}
-+			PDEBUG("available device %d: Q depth = %d, dev "
-+			       "type = %d, stat = %02X%02X%02X%02X\n",
-+			       deviceNr, *q_depth, *dev_type,
-+			       stat_word.q_stat_flags,
-+			       stat_word.response_code,
-+			       stat_word.reserved[0],
-+			       stat_word.reserved[1]);
-+			break;
-+		case 3:
-+			switch (stat_word.response_code) {
-+			case AP_RESPONSE_NORMAL:
-+				stat = HD_ONLINE;
-+				break_out = 1;
-+				*q_depth = t_depth + 1;
-+				*dev_type = t_dev_type;
-+				PDEBUG("cc3, available device "
-+				       "%d: Q depth = %d, dev "
-+				       "type = %d, stat = "
-+				       "%02X%02X%02X%02X\n",
-+				       deviceNr, *q_depth,
-+				       *dev_type,
-+				       stat_word.q_stat_flags,
-+				       stat_word.response_code,
-+				       stat_word.reserved[0],
-+				       stat_word.reserved[1]);
-+				break;
-+			case AP_RESPONSE_Q_NOT_AVAIL:
-+				stat = HD_NOT_THERE;
-+				break_out = 1;
-+				break;
-+			case AP_RESPONSE_RESET_IN_PROGRESS:
-+				PDEBUG("device %d in reset\n",
-+				       deviceNr);
-+				break;
-+			case AP_RESPONSE_DECONFIGURED:
-+				stat = HD_DECONFIGURED;
-+				break_out = 1;
-+				break;
-+			case AP_RESPONSE_CHECKSTOPPED:
-+				stat = HD_CHECKSTOPPED;
-+				break_out = 1;
-+				break;
-+			case AP_RESPONSE_BUSY:
-+				PDEBUG("device %d busy\n",
-+				       deviceNr);
-+				break;
-+			default:
-+				break;
-+			}
-+			break;
-+		default:
-+			stat = HD_NOT_THERE;
-+			break_out = 1;
-+			break;
-+		}
-+		if (break_out)
-+			break;
-+		udelay(5);
-+		ccode = testq(q_nr, &t_depth, &t_dev_type, &stat_word);
-+	}
-+	return stat;
-+}
-+enum devstat
-+reset_device(int deviceNr, int cdx, int resetNr)
-+{
-+	int q_nr, ccode = 0, dummy_qdepth, dummy_devType, i;
-+	struct ap_status_word stat_word;
-+	enum devstat stat;
-+	int break_out;
-+	q_nr = (deviceNr << SKIP_BITL) + cdx;
-+	stat = DEV_GONE;
-+	ccode = resetq(q_nr, &stat_word);
-+	if (ccode > 3)
-+		return DEV_RSQ_EXCEPTION;
-+	break_out = 0;
-+	for (i = 0; i < resetNr; i++) {
-+		switch (ccode) {
-+		case 0:
-+			stat = DEV_ONLINE;
-+			if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY)
-+				break_out = 1;
-+			break;
-+		case 3:
-+			switch (stat_word.response_code) {
-+			case AP_RESPONSE_NORMAL:
-+				stat = DEV_ONLINE;
-+				if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY)
-+					break_out = 1;
-+				break;
-+			case AP_RESPONSE_Q_NOT_AVAIL:
-+			case AP_RESPONSE_DECONFIGURED:
-+			case AP_RESPONSE_CHECKSTOPPED:
-+				stat = DEV_GONE;
-+				break_out = 1;
-+				break;
-+			case AP_RESPONSE_RESET_IN_PROGRESS:
-+			case AP_RESPONSE_BUSY:
-+			default:
-+				break;
-+			}
-+			break;
-+		default:
-+			stat = DEV_GONE;
-+			break_out = 1;
-+			break;
-+		}
-+		if (break_out == 1)
-+			break;
-+		udelay(5);
-+		ccode = testq(q_nr, &dummy_qdepth, &dummy_devType, &stat_word);
-+		if (ccode > 3) {
-+			stat = DEV_TSQ_EXCEPTION;
-+			break;
-+		}
-+	}
-+	PDEBUG("Number of testq's needed for reset: %d\n", i);
-+	if (i >= resetNr) {
-+	  stat = DEV_GONE;
-+	}
-+	return stat;
-+}
-+#ifdef DEBUG_HYDRA_MSGS
-+static inline void
-+print_buffer(unsigned char *buffer, int bufflen)
-+{
-+	int i;
-+	for (i = 0; i < bufflen; i += 16) {
-+		PRINTK("%04X: %02X%02X%02X%02X %02X%02X%02X%02X "
-+		       "%02X%02X%02X%02X %02X%02X%02X%02X\n", i,
-+		       buffer[i+0], buffer[i+1], buffer[i+2], buffer[i+3],
-+		       buffer[i+4], buffer[i+5], buffer[i+6], buffer[i+7],
-+		       buffer[i+8], buffer[i+9], buffer[i+10], buffer[i+11],
-+		       buffer[i+12], buffer[i+13], buffer[i+14], buffer[i+15]);
-+	}
-+}
-+#endif
-+enum devstat
-+send_to_AP(int dev_nr, int cdx, int msg_len, unsigned char *msg_ext)
-+{
-+	struct ap_status_word stat_word;
-+	enum devstat stat;
-+	int ccode;
-+	((struct request_msg_ext *) msg_ext)->q_nr =
-+		(dev_nr << SKIP_BITL) + cdx;
-+	PDEBUG("msg_len passed to sen: %d\n", msg_len);
-+	PDEBUG("q number passed to sen: %02x%02x%02x%02x\n",
-+	       msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3]);
-+	stat = DEV_GONE;
-+#ifdef DEBUG_HYDRA_MSGS
-+	PRINTK("Request header: %02X%02X%02X%02X %02X%02X%02X%02X "
-+	       "%02X%02X%02X%02X\n",
-+	       msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3],
-+	       msg_ext[4], msg_ext[5], msg_ext[6], msg_ext[7],
-+	       msg_ext[8], msg_ext[9], msg_ext[10], msg_ext[11]);
-+	
-+	print_buffer(msg_ext+CALLER_HEADER, msg_len);
-+#endif
-+	ccode = sen(msg_len, msg_ext, &stat_word);
-+	if (ccode > 3)
-+		return DEV_SEN_EXCEPTION;
-+	PDEBUG("nq cc: %u, st: %02x%02x%02x%02x\n",
-+	       ccode, stat_word.q_stat_flags, stat_word.response_code,
-+	       stat_word.reserved[0], stat_word.reserved[1]);
-+	switch (ccode) {
-+	case 0:
-+		stat = DEV_ONLINE;
-+		break;
-+	case 1:
-+		stat = DEV_GONE;
-+		break;
-+	case 3:
-+		switch (stat_word.response_code) {
-+		case AP_RESPONSE_NORMAL:
-+			stat = DEV_ONLINE;
-+			break;
-+		case AP_RESPONSE_Q_FULL:
-+			stat = DEV_QUEUE_FULL;
-+			break;
-+		default:
-+			stat = DEV_GONE;
-+			break;
-+		}
-+		break;
-+	default:
-+		stat = DEV_GONE;
-+		break;
-+	}
-+	return stat;
-+}
-+enum devstat
-+receive_from_AP(int dev_nr, int cdx, int resplen, unsigned char *resp,
-+		unsigned char *psmid)
-+{
-+	int ccode;
-+	struct ap_status_word stat_word;
-+	enum devstat stat;
-+	memset(resp, 0x00, 8);
-+	ccode = rec((dev_nr << SKIP_BITL) + cdx, resplen, resp, psmid,
-+		    &stat_word);
-+	if (ccode > 3)
-+		return DEV_REC_EXCEPTION;
-+	PDEBUG("dq cc: %u, st: %02x%02x%02x%02x\n",
-+	       ccode, stat_word.q_stat_flags, stat_word.response_code,
-+	       stat_word.reserved[0], stat_word.reserved[1]);
-+	stat = DEV_GONE;
-+	switch (ccode) {
-+	case 0:
-+		stat = DEV_ONLINE;
-+#ifdef DEBUG_HYDRA_MSGS
-+		print_buffer(resp, resplen);
-+#endif
-+		break;
-+	case 3:
-+		switch (stat_word.response_code) {
-+		case AP_RESPONSE_NORMAL:
-+			stat = DEV_ONLINE;
-+			break;
-+		case AP_RESPONSE_NO_PENDING_REPLY:
-+			if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY)
-+				stat = DEV_EMPTY;
-+			else
-+				stat = DEV_NO_WORK;
-+			break;
-+		case AP_RESPONSE_INDEX_TOO_BIG:
-+		case AP_RESPONSE_NO_FIRST_PART:
-+		case AP_RESPONSE_MESSAGE_TOO_BIG:
-+			stat = DEV_BAD_MESSAGE;
-+			break;
-+		default:
-+			break;
-+		}
-+		break;
-+	default:
-+		break;
-+	}
-+	return stat;
-+}
-+static inline int
-+pad_msg(unsigned char *buffer, int  totalLength, int msgLength)
-+{
-+	int pad_len;
-+	for (pad_len = 0; pad_len < (totalLength - msgLength); pad_len++)
-+		if (buffer[pad_len] != 0x00)
-+			break;
-+	pad_len -= 3; 
-+	if (pad_len < 8)
-+		return SEN_PAD_ERROR;
-+	buffer[0] = 0x00;
-+	buffer[1] = 0x02;
-+	memcpy(buffer+2, static_pad, pad_len);
-+	buffer[pad_len + 2] = 0x00;
-+	return 0;
-+}
-+static inline int
-+is_common_public_key(unsigned char *key, int len)
-+{
-+	int i;
-+	for (i = 0; i < len; i++)
-+		if (key[i])
-+			break;
-+	key += i;
-+	len -= i;
-+	if (((len == 1) && (key[0] == 3)) ||
-+	    ((len == 3) && (key[0] == 1) && (key[1] == 0) && (key[2] == 1)))
-+		return 1;
-+	return 0;
-+}
-+static int
-+ICAMEX_msg_to_type4MEX_msg(struct ica_rsa_modexpo *icaMex_p, int *z90cMsg_l_p,
-+			   union type4_msg *z90cMsg_p)
-+{
-+	int mod_len, msg_size, mod_tgt_len, exp_tgt_len, inp_tgt_len;
-+	unsigned char *mod_tgt, *exp_tgt, *inp_tgt;
-+	union type4_msg *tmp_type4_msg;
-+	mod_len = icaMex_p->inputdatalength;
-+	msg_size = ((mod_len <= 128) ? TYPE4_SME_LEN : TYPE4_LME_LEN) +
-+		    CALLER_HEADER;
-+	memset(z90cMsg_p, 0, msg_size);
-+	tmp_type4_msg = (union type4_msg *)
-+		((unsigned char *) z90cMsg_p + CALLER_HEADER);
-+	tmp_type4_msg->sme.header.msg_type_code = TYPE4_TYPE_CODE;
-+	tmp_type4_msg->sme.header.request_code = TYPE4_REQU_CODE;
-+	if (mod_len <= 128) {
-+		tmp_type4_msg->sme.header.msg_fmt = TYPE4_SME_FMT;
-+		tmp_type4_msg->sme.header.msg_len = TYPE4_SME_LEN;
-+		mod_tgt = tmp_type4_msg->sme.modulus;
-+		mod_tgt_len = sizeof(tmp_type4_msg->sme.modulus);
-+		exp_tgt = tmp_type4_msg->sme.exponent;
-+		exp_tgt_len = sizeof(tmp_type4_msg->sme.exponent);
-+		inp_tgt = tmp_type4_msg->sme.message;
-+		inp_tgt_len = sizeof(tmp_type4_msg->sme.message);
-+	} else {
-+		tmp_type4_msg->lme.header.msg_fmt = TYPE4_LME_FMT;
-+		tmp_type4_msg->lme.header.msg_len = TYPE4_LME_LEN;
-+		mod_tgt = tmp_type4_msg->lme.modulus;
-+		mod_tgt_len = sizeof(tmp_type4_msg->lme.modulus);
-+		exp_tgt = tmp_type4_msg->lme.exponent;
-+		exp_tgt_len = sizeof(tmp_type4_msg->lme.exponent);
-+		inp_tgt = tmp_type4_msg->lme.message;
-+		inp_tgt_len = sizeof(tmp_type4_msg->lme.message);
-+	}
-+	mod_tgt += (mod_tgt_len - mod_len);
-+	if (copy_from_user(mod_tgt, icaMex_p->n_modulus, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(mod_tgt, mod_len))
-+		return SEN_USER_ERROR;
-+	exp_tgt += (exp_tgt_len - mod_len);
-+	if (copy_from_user(exp_tgt, icaMex_p->b_key, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(exp_tgt, mod_len))
-+		return SEN_USER_ERROR;
-+	inp_tgt += (inp_tgt_len - mod_len);
-+	if (copy_from_user(inp_tgt, icaMex_p->inputdata, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(inp_tgt, mod_len))
-+		return SEN_USER_ERROR;
-+	*z90cMsg_l_p = msg_size - CALLER_HEADER;
-+	return 0;
-+}
-+static int
-+ICACRT_msg_to_type4CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p,
-+			   int *z90cMsg_l_p, union type4_msg *z90cMsg_p)
-+{
-+	int mod_len, short_len, long_len, tmp_size, p_tgt_len, q_tgt_len,
-+	    dp_tgt_len, dq_tgt_len, u_tgt_len, inp_tgt_len;
-+	unsigned char *p_tgt, *q_tgt, *dp_tgt, *dq_tgt, *u_tgt, *inp_tgt;
-+	union type4_msg *tmp_type4_msg;
-+	mod_len = icaMsg_p->inputdatalength;
-+	short_len = mod_len / 2;
-+	long_len = mod_len / 2 + 8;
-+	tmp_size = ((mod_len <= 128) ? TYPE4_SCR_LEN : TYPE4_LCR_LEN) +
-+		    CALLER_HEADER;
-+	memset(z90cMsg_p, 0, tmp_size);
-+	tmp_type4_msg = (union type4_msg *)
-+		((unsigned char *) z90cMsg_p + CALLER_HEADER);
-+	tmp_type4_msg->scr.header.msg_type_code = TYPE4_TYPE_CODE;
-+	tmp_type4_msg->scr.header.request_code = TYPE4_REQU_CODE;
-+	if (mod_len <= 128) {
-+		tmp_type4_msg->scr.header.msg_fmt = TYPE4_SCR_FMT;
-+		tmp_type4_msg->scr.header.msg_len = TYPE4_SCR_LEN;
-+		p_tgt = tmp_type4_msg->scr.p;
-+		p_tgt_len = sizeof(tmp_type4_msg->scr.p);
-+		q_tgt = tmp_type4_msg->scr.q;
-+		q_tgt_len = sizeof(tmp_type4_msg->scr.q);
-+		dp_tgt = tmp_type4_msg->scr.dp;
-+		dp_tgt_len = sizeof(tmp_type4_msg->scr.dp);
-+		dq_tgt = tmp_type4_msg->scr.dq;
-+		dq_tgt_len = sizeof(tmp_type4_msg->scr.dq);
-+		u_tgt = tmp_type4_msg->scr.u;
-+		u_tgt_len = sizeof(tmp_type4_msg->scr.u);
-+		inp_tgt = tmp_type4_msg->scr.message;
-+		inp_tgt_len = sizeof(tmp_type4_msg->scr.message);
-+	} else {
-+		tmp_type4_msg->lcr.header.msg_fmt = TYPE4_LCR_FMT;
-+		tmp_type4_msg->lcr.header.msg_len = TYPE4_LCR_LEN;
-+		p_tgt = tmp_type4_msg->lcr.p;
-+		p_tgt_len = sizeof(tmp_type4_msg->lcr.p);
-+		q_tgt = tmp_type4_msg->lcr.q;
-+		q_tgt_len = sizeof(tmp_type4_msg->lcr.q);
-+		dp_tgt = tmp_type4_msg->lcr.dp;
-+		dp_tgt_len = sizeof(tmp_type4_msg->lcr.dp);
-+		dq_tgt = tmp_type4_msg->lcr.dq;
-+		dq_tgt_len = sizeof(tmp_type4_msg->lcr.dq);
-+		u_tgt = tmp_type4_msg->lcr.u;
-+		u_tgt_len = sizeof(tmp_type4_msg->lcr.u);
-+		inp_tgt = tmp_type4_msg->lcr.message;
-+		inp_tgt_len = sizeof(tmp_type4_msg->lcr.message);
-+	}
-+	p_tgt += (p_tgt_len - long_len);
-+	if (copy_from_user(p_tgt, icaMsg_p->np_prime, long_len))
-+		return SEN_RELEASED;
-+	if (is_empty(p_tgt, long_len))
-+		return SEN_USER_ERROR;
-+	q_tgt += (q_tgt_len - short_len);
-+	if (copy_from_user(q_tgt, icaMsg_p->nq_prime, short_len))
-+		return SEN_RELEASED;
-+	if (is_empty(q_tgt, short_len))
-+		return SEN_USER_ERROR;
-+	dp_tgt += (dp_tgt_len - long_len);
-+	if (copy_from_user(dp_tgt, icaMsg_p->bp_key, long_len))
-+		return SEN_RELEASED;
-+	if (is_empty(dp_tgt, long_len))
-+		return SEN_USER_ERROR;
-+	dq_tgt += (dq_tgt_len - short_len);
-+	if (copy_from_user(dq_tgt, icaMsg_p->bq_key, short_len))
-+		return SEN_RELEASED;
-+	if (is_empty(dq_tgt, short_len))
-+		return SEN_USER_ERROR;
-+	u_tgt += (u_tgt_len - long_len);
-+	if (copy_from_user(u_tgt, icaMsg_p->u_mult_inv, long_len))
-+		return SEN_RELEASED;
-+	if (is_empty(u_tgt, long_len))
-+		return SEN_USER_ERROR;
-+	inp_tgt += (inp_tgt_len - mod_len);
-+	if (copy_from_user(inp_tgt, icaMsg_p->inputdata, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(inp_tgt, mod_len))
-+		return SEN_USER_ERROR;
-+	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
-+	return 0;
-+}
-+static int
-+ICAMEX_msg_to_type6MEX_de_msg(struct ica_rsa_modexpo *icaMsg_p, int cdx,
-+			      int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
-+{
-+	int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l;
-+	unsigned char *temp;
-+	struct type6_hdr *tp6Hdr_p;
-+	struct CPRB *cprb_p;
-+	struct cca_private_ext_ME *key_p;
-+	static int deprecated_msg_count = 0;
-+	mod_len = icaMsg_p->inputdatalength;
-+	tmp_size = FIXED_TYPE6_ME_LEN + mod_len;
-+	total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
-+	parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
-+	tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
-+	memset(z90cMsg_p, 0, tmp_size);
-+	
-+	temp = (unsigned char *)z90cMsg_p + CALLER_HEADER;
-+	memcpy(temp, &static_type6_hdr, sizeof(struct type6_hdr));
-+	tp6Hdr_p = (struct type6_hdr *)temp;
-+	tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
-+	tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
-+	
-+	temp += sizeof(struct type6_hdr);
-+	memcpy(temp, &static_cprb, sizeof(struct CPRB));
-+	cprb_p = (struct CPRB *) temp;
-+	cprb_p->usage_domain[0]= (unsigned char)cdx;
-+	itoLe2(&parmBlock_l, cprb_p->req_parml);
-+	itoLe2((int *)&(tp6Hdr_p->FromCardLen1), cprb_p->rpl_parml);
-+	
-+	temp += sizeof(struct CPRB);
-+	memcpy(temp, &static_pkd_function_and_rules,
-+	       sizeof(struct function_and_rules_block));
-+	
-+	temp += sizeof(struct function_and_rules_block);
-+	vud_len = 2 + icaMsg_p->inputdatalength;
-+	itoLe2(&vud_len, temp); 
-+	
-+	temp += 2;
-+	if (copy_from_user(temp, icaMsg_p->inputdata, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(temp, mod_len))
-+		return SEN_USER_ERROR;
-+	
-+	temp += mod_len;
-+	memcpy(temp, &static_T6_keyBlock_hdr, sizeof(struct T6_keyBlock_hdr));
-+	
-+	temp += sizeof(struct T6_keyBlock_hdr);
-+	memcpy(temp, &static_pvt_me_key, sizeof(struct cca_private_ext_ME));
-+	key_p = (struct cca_private_ext_ME *)temp;
-+	temp = key_p->pvtMESec.exponent + sizeof(key_p->pvtMESec.exponent)
-+	       - mod_len;
-+	if (copy_from_user(temp, icaMsg_p->b_key, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(temp, mod_len))
-+		return SEN_USER_ERROR;
-+	if (is_common_public_key(temp, mod_len)) {
-+		if (deprecated_msg_count < 20) {
-+			PRINTK("Common public key used for modex decrypt\n");
-+			deprecated_msg_count++;
-+			if (deprecated_msg_count == 20)
-+				PRINTK("No longer issuing messages about common"
-+				       " public key for modex decrypt.\n");
-+		}
-+		return SEN_NOT_AVAIL;
-+	}
-+	temp = key_p->pvtMESec.modulus + sizeof(key_p->pvtMESec.modulus)
-+	       - mod_len;
-+	if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(temp, mod_len))
-+		return SEN_USER_ERROR;
-+	
-+	key_p->pubMESec.modulus_bit_len = 8 * mod_len;
-+	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
-+	return 0;
-+}
-+static int
-+ICAMEX_msg_to_type6MEX_en_msg(struct ica_rsa_modexpo *icaMsg_p, int cdx,
-+			      int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
-+{
-+	int mod_len, vud_len, exp_len, key_len;
-+	int pad_len, tmp_size, total_CPRB_len, parmBlock_l, i;
-+	unsigned char temp_exp[256], *exp_p, *temp;
-+	struct type6_hdr *tp6Hdr_p;
-+	struct CPRB *cprb_p;
-+	struct cca_public_key *key_p;
-+	struct T6_keyBlock_hdr *keyb_p;
-+	mod_len = icaMsg_p->inputdatalength;
-+	if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(temp_exp, mod_len))
-+		return SEN_USER_ERROR;
-+	exp_p = temp_exp;
-+	for (i = 0; i < mod_len; i++)
-+		if (exp_p[i])
-+			break;
-+	if (i >= mod_len)
-+		return SEN_USER_ERROR;
-+	exp_len = mod_len - i;
-+	exp_p += i;
-+	PDEBUG("exp_len after computation: %08x\n", exp_len);
-+	tmp_size = FIXED_TYPE6_ME_EN_LEN + 2 * mod_len + exp_len;
-+	total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
-+	parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
-+	tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
-+	vud_len = 2 + mod_len;
-+	memset(z90cMsg_p, 0, tmp_size);
-+	
-+	temp = (unsigned char *)z90cMsg_p + CALLER_HEADER;
-+	memcpy(temp, &static_type6_hdr, sizeof(struct type6_hdr));
-+	tp6Hdr_p = (struct type6_hdr *)temp;
-+	tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
-+	tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
-+	memcpy(tp6Hdr_p->function_code, static_PKE_function_code,
-+	       sizeof(static_PKE_function_code));
-+	
-+	temp += sizeof(struct type6_hdr);
-+	memcpy(temp, &static_cprb, sizeof(struct CPRB));
-+	cprb_p = (struct CPRB *) temp;
-+	cprb_p->usage_domain[0]= (unsigned char)cdx;
-+	itoLe2((int *)&(tp6Hdr_p->FromCardLen1), cprb_p->rpl_parml);
-+	
-+	temp += sizeof(struct CPRB);
-+	memcpy(temp, &static_pke_function_and_rules,
-+		 sizeof(struct function_and_rules_block));
-+	
-+	temp += sizeof(struct function_and_rules_block);
-+	temp += 2;
-+	if (copy_from_user(temp, icaMsg_p->inputdata, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(temp, mod_len))
-+		return SEN_USER_ERROR;
-+	
-+	if ((temp[0] != 0x00) || (temp[1] != 0x02))
-+		return SEN_NOT_AVAIL;
-+	for (i = 2; i < mod_len; i++)
-+		if (temp[i] == 0x00)
-+			break;
-+	if ((i < 9) || (i > (mod_len - 2)))
-+		return SEN_NOT_AVAIL;
-+	pad_len = i + 1;
-+	vud_len = mod_len - pad_len;
-+	memmove(temp, temp+pad_len, vud_len);
-+	
-+	temp -= 2;
-+	vud_len += 2;
-+	itoLe2(&vud_len, temp);
-+	
-+	temp += (vud_len);
-+	keyb_p = (struct T6_keyBlock_hdr *)temp;
-+	
-+	temp += sizeof(struct T6_keyBlock_hdr);
-+	memcpy(temp, &static_public_key, sizeof(static_public_key));
-+	key_p = (struct cca_public_key *)temp;
-+	temp = key_p->pubSec.exponent;
-+	memcpy(temp, exp_p, exp_len);
-+	temp += exp_len;
-+	if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(temp, mod_len))
-+		return SEN_USER_ERROR;
-+	key_p->pubSec.modulus_bit_len = 8 * mod_len;
-+	key_p->pubSec.modulus_byte_len = mod_len;
-+	key_p->pubSec.exponent_len = exp_len;
-+	key_p->pubSec.section_length = CALLER_HEADER + mod_len + exp_len;
-+	key_len = key_p->pubSec.section_length + sizeof(struct cca_token_hdr);
-+	key_p->pubHdr.token_length = key_len;
-+	key_len += 4;
-+	itoLe2(&key_len, keyb_p->ulen);
-+	key_len += 2;
-+	itoLe2(&key_len, keyb_p->blen);
-+	parmBlock_l -= pad_len;
-+	itoLe2(&parmBlock_l, cprb_p->req_parml);
-+	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
-+	return 0;
-+}
-+static int
-+ICACRT_msg_to_type6CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx,
-+			   int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
-+{
-+	int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l, short_len;
-+	int long_len, pad_len, keyPartsLen, tmp_l;
-+	unsigned char *tgt_p, *temp;
-+	struct type6_hdr *tp6Hdr_p;
-+	struct CPRB *cprb_p;
-+	struct cca_token_hdr *keyHdr_p;
-+	struct cca_pvt_ext_CRT_sec *pvtSec_p;
-+	struct cca_public_sec *pubSec_p;
-+	mod_len = icaMsg_p->inputdatalength;
-+	short_len = mod_len / 2;
-+	long_len = 8 + short_len;
-+	keyPartsLen = 3 * long_len + 2 * short_len;
-+	pad_len = (8 - (keyPartsLen % 8)) % 8;
-+	keyPartsLen += pad_len + mod_len;
-+	tmp_size = FIXED_TYPE6_CR_LEN + keyPartsLen + mod_len;
-+	total_CPRB_len = tmp_size -  sizeof(struct type6_hdr);
-+	parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
-+	vud_len = 2 + mod_len;	   
-+	tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
-+	memset(z90cMsg_p, 0, tmp_size);
-+	
-+	tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
-+	memcpy(tgt_p, &static_type6_hdr, sizeof(struct type6_hdr));
-+	tp6Hdr_p = (struct type6_hdr *)tgt_p;
-+	tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
-+	tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
-+	
-+	tgt_p += sizeof(struct type6_hdr);
-+	cprb_p = (struct CPRB *) tgt_p;
-+	memcpy(tgt_p, &static_cprb, sizeof(struct CPRB));
-+	cprb_p->usage_domain[0]= *((unsigned char *)(&(cdx))+3);
-+	itoLe2(&parmBlock_l, cprb_p->req_parml);
-+	memcpy(cprb_p->rpl_parml, cprb_p->req_parml,
-+	       sizeof(cprb_p->req_parml));
-+	
-+	tgt_p += sizeof(struct CPRB);
-+	memcpy(tgt_p, &static_pkd_function_and_rules,
-+	       sizeof(struct function_and_rules_block));
-+	
-+	tgt_p += sizeof(struct function_and_rules_block);
-+	itoLe2(&vud_len, tgt_p);
-+	
-+	tgt_p += 2;
-+	if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, mod_len))
-+		return SEN_USER_ERROR;
-+	
-+	tgt_p += mod_len;
-+	tmp_l = sizeof(struct T6_keyBlock_hdr) + sizeof(struct cca_token_hdr) +
-+		sizeof(struct cca_pvt_ext_CRT_sec) + 0x0F + keyPartsLen;
-+	itoLe2(&tmp_l, tgt_p);
-+	
-+	temp = tgt_p + 2;
-+	tmp_l -= 2;
-+	itoLe2(&tmp_l, temp);
-+	
-+	tgt_p += sizeof(struct T6_keyBlock_hdr);
-+	keyHdr_p = (struct cca_token_hdr *)tgt_p;
-+	keyHdr_p->token_identifier = CCA_TKN_HDR_ID_EXT;
-+	tmp_l -= 4;
-+	keyHdr_p->token_length = tmp_l;
-+	
-+	tgt_p += sizeof(struct cca_token_hdr);
-+	pvtSec_p = (struct cca_pvt_ext_CRT_sec *)tgt_p;
-+	pvtSec_p->section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT;
-+	pvtSec_p->section_length =
-+		sizeof(struct cca_pvt_ext_CRT_sec) + keyPartsLen;
-+	pvtSec_p->key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL;
-+	pvtSec_p->key_use_flags[0] = CCA_PVT_USAGE_ALL;
-+	pvtSec_p->p_len = long_len;
-+	pvtSec_p->q_len = short_len;
-+	pvtSec_p->dp_len = long_len;
-+	pvtSec_p->dq_len = short_len;
-+	pvtSec_p->u_len = long_len;
-+	pvtSec_p->mod_len = mod_len;
-+	pvtSec_p->pad_len = pad_len;
-+	
-+	tgt_p += sizeof(struct cca_pvt_ext_CRT_sec);
-+	if (copy_from_user(tgt_p, icaMsg_p->np_prime, long_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, long_len))
-+		return SEN_USER_ERROR;
-+	tgt_p += long_len;
-+	if (copy_from_user(tgt_p, icaMsg_p->nq_prime, short_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, short_len))
-+		return SEN_USER_ERROR;
-+	tgt_p += short_len;
-+	if (copy_from_user(tgt_p, icaMsg_p->bp_key, long_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, long_len))
-+		return SEN_USER_ERROR;
-+	tgt_p += long_len;
-+	if (copy_from_user(tgt_p, icaMsg_p->bq_key, short_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, short_len))
-+		return SEN_USER_ERROR;
-+	tgt_p += short_len;
-+	if (copy_from_user(tgt_p, icaMsg_p->u_mult_inv, long_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, long_len))
-+		return SEN_USER_ERROR;
-+	tgt_p += long_len;
-+	tgt_p += pad_len;
-+	memset(tgt_p, 0xFF, mod_len);
-+	
-+	tgt_p += mod_len;
-+	memcpy(tgt_p, &static_cca_pub_sec, sizeof(struct cca_public_sec));
-+	pubSec_p = (struct cca_public_sec *) tgt_p;
-+	pubSec_p->modulus_bit_len = 8 * mod_len;
-+	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
-+	return 0;
-+}
-+static int
-+ICAMEX_msg_to_type6MEX_msgX(struct ica_rsa_modexpo *icaMsg_p, int cdx,
-+			    int *z90cMsg_l_p, struct type6_msg *z90cMsg_p,
-+			    int dev_type)
-+{
-+	int mod_len, exp_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l;
-+	int key_len, i;
-+	unsigned char temp_exp[256], *tgt_p, *temp, *exp_p;
-+	struct type6_hdr *tp6Hdr_p;
-+	struct CPRBX *cprbx_p;
-+	struct cca_public_key *key_p;
-+	struct T6_keyBlock_hdrX *keyb_p;
-+	mod_len = icaMsg_p->inputdatalength;
-+	if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(temp_exp, mod_len))
-+		return SEN_USER_ERROR;
-+	exp_p = temp_exp;
-+	for (i = 0; i < mod_len; i++)
-+		if (exp_p[i])
-+			break;
-+	if (i >= mod_len)
-+		return SEN_USER_ERROR;
-+	exp_len = mod_len - i;
-+	exp_p += i;
-+	PDEBUG("exp_len after computation: %08x\n", exp_len);
-+	tmp_size = FIXED_TYPE6_ME_EN_LENX + 2 * mod_len + exp_len;
-+	total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
-+	parmBlock_l = total_CPRB_len - sizeof(struct CPRBX);
-+	tmp_size = tmp_size + CALLER_HEADER;
-+	vud_len = 2 + mod_len;
-+	memset(z90cMsg_p, 0, tmp_size);
-+	
-+	tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
-+	memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr));
-+	tp6Hdr_p = (struct type6_hdr *)tgt_p;
-+	tp6Hdr_p->ToCardLen1 = total_CPRB_len;
-+	tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE;
-+	memcpy(tp6Hdr_p->function_code, static_PKE_function_code,
-+	       sizeof(static_PKE_function_code));
-+	
-+	tgt_p += sizeof(struct type6_hdr);
-+	memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX));
-+	cprbx_p = (struct CPRBX *) tgt_p;
-+	cprbx_p->domain = (unsigned short)cdx;
-+	cprbx_p->rpl_msgbl = RESPONSE_CPRBX_SIZE;
-+	
-+	tgt_p += sizeof(struct CPRBX);
-+	if (dev_type == PCIXCC_MCL2)
-+		memcpy(tgt_p, &static_pke_function_and_rulesX_MCL2,
-+		       sizeof(struct function_and_rules_block));
-+	else
-+		memcpy(tgt_p, &static_pke_function_and_rulesX,
-+		       sizeof(struct function_and_rules_block));
-+	
-+	tgt_p += sizeof(struct function_and_rules_block);
-+	
-+	tgt_p += 2;
-+	if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
-+	      return SEN_RELEASED;
-+	if (is_empty(tgt_p, mod_len))
-+	      return SEN_USER_ERROR;
-+	
-+	tgt_p -= 2;
-+	*((short *)tgt_p) = (short) vud_len;
-+	tgt_p += vud_len;
-+	keyb_p = (struct T6_keyBlock_hdrX *)tgt_p;
-+	
-+	tgt_p += sizeof(struct T6_keyBlock_hdrX);
-+	memcpy(tgt_p, &static_public_key, sizeof(static_public_key));
-+	key_p = (struct cca_public_key *)tgt_p;
-+	temp = key_p->pubSec.exponent;
-+	memcpy(temp, exp_p, exp_len);
-+	temp += exp_len;
-+	if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
-+	      return SEN_RELEASED;
-+	if (is_empty(temp, mod_len))
-+	      return SEN_USER_ERROR;
-+	key_p->pubSec.modulus_bit_len = 8 * mod_len;
-+	key_p->pubSec.modulus_byte_len = mod_len;
-+	key_p->pubSec.exponent_len = exp_len;
-+	key_p->pubSec.section_length = CALLER_HEADER + mod_len + exp_len;
-+	key_len = key_p->pubSec.section_length + sizeof(struct cca_token_hdr);
-+	key_p->pubHdr.token_length = key_len;
-+	key_len += 4;
-+	keyb_p->ulen = (unsigned short)key_len;
-+	key_len += 2;
-+	keyb_p->blen = (unsigned short)key_len;
-+	cprbx_p->req_parml = parmBlock_l;
-+	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
-+	return 0;
-+}
-+static int
-+ICACRT_msg_to_type6CRT_msgX(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx,
-+			    int *z90cMsg_l_p, struct type6_msg *z90cMsg_p,
-+			    int dev_type)
-+{
-+	int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l, short_len;
-+	int long_len, pad_len, keyPartsLen, tmp_l;
-+	unsigned char *tgt_p, *temp;
-+	struct type6_hdr *tp6Hdr_p;
-+	struct CPRBX *cprbx_p;
-+	struct cca_token_hdr *keyHdr_p;
-+	struct cca_pvt_ext_CRT_sec *pvtSec_p;
-+	struct cca_public_sec *pubSec_p;
-+	mod_len = icaMsg_p->inputdatalength;
-+	short_len = mod_len / 2;
-+	long_len = 8 + short_len;
-+	keyPartsLen = 3 * long_len + 2 * short_len;
-+	pad_len = (8 - (keyPartsLen % 8)) % 8;
-+	keyPartsLen += pad_len + mod_len;
-+	tmp_size = FIXED_TYPE6_CR_LENX + keyPartsLen + mod_len;
-+	total_CPRB_len = tmp_size -  sizeof(struct type6_hdr);
-+	parmBlock_l = total_CPRB_len - sizeof(struct CPRBX);
-+	vud_len = 2 + mod_len;
-+	tmp_size = tmp_size + CALLER_HEADER;
-+	memset(z90cMsg_p, 0, tmp_size);
-+	
-+	tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
-+	memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr));
-+	tp6Hdr_p = (struct type6_hdr *)tgt_p;
-+	tp6Hdr_p->ToCardLen1 = total_CPRB_len;
-+	tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE;
-+	
-+	tgt_p += sizeof(struct type6_hdr);
-+	cprbx_p = (struct CPRBX *) tgt_p;
-+	memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX));
-+	cprbx_p->domain = (unsigned short)cdx;
-+	cprbx_p->req_parml = parmBlock_l;
-+	cprbx_p->rpl_msgbl = parmBlock_l;
-+	
-+	tgt_p += sizeof(struct CPRBX);
-+	if (dev_type == PCIXCC_MCL2)
-+		memcpy(tgt_p, &static_pkd_function_and_rulesX_MCL2,
-+		       sizeof(struct function_and_rules_block));
-+	else
-+		memcpy(tgt_p, &static_pkd_function_and_rulesX,
-+		       sizeof(struct function_and_rules_block));
-+	
-+	tgt_p += sizeof(struct function_and_rules_block);
-+	*((short *)tgt_p) = (short) vud_len;
-+	
-+	tgt_p += 2;
-+	if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, mod_len))
-+		return SEN_USER_ERROR;
-+	
-+	tgt_p += mod_len;
-+	tmp_l = sizeof(struct T6_keyBlock_hdr) + sizeof(struct cca_token_hdr) +
-+		sizeof(struct cca_pvt_ext_CRT_sec) + 0x0F + keyPartsLen;
-+	*((short *)tgt_p) = (short) tmp_l;
-+	temp = tgt_p + 2;
-+	tmp_l -= 2;
-+	*((short *)temp) = (short) tmp_l;
-+	
-+	tgt_p += sizeof(struct T6_keyBlock_hdr);
-+	keyHdr_p = (struct cca_token_hdr *)tgt_p;
-+	keyHdr_p->token_identifier = CCA_TKN_HDR_ID_EXT;
-+	tmp_l -= 4;
-+	keyHdr_p->token_length = tmp_l;
-+	
-+	tgt_p += sizeof(struct cca_token_hdr);
-+	pvtSec_p = (struct cca_pvt_ext_CRT_sec *)tgt_p;
-+	pvtSec_p->section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT;
-+	pvtSec_p->section_length =
-+		sizeof(struct cca_pvt_ext_CRT_sec) + keyPartsLen;
-+	pvtSec_p->key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL;
-+	pvtSec_p->key_use_flags[0] = CCA_PVT_USAGE_ALL;
-+	pvtSec_p->p_len = long_len;
-+	pvtSec_p->q_len = short_len;
-+	pvtSec_p->dp_len = long_len;
-+	pvtSec_p->dq_len = short_len;
-+	pvtSec_p->u_len = long_len;
-+	pvtSec_p->mod_len = mod_len;
-+	pvtSec_p->pad_len = pad_len;
-+	
-+	tgt_p += sizeof(struct cca_pvt_ext_CRT_sec);
-+	if (copy_from_user(tgt_p, icaMsg_p->np_prime, long_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, long_len))
-+		return SEN_USER_ERROR;
-+	tgt_p += long_len;
-+	if (copy_from_user(tgt_p, icaMsg_p->nq_prime, short_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, short_len))
-+		return SEN_USER_ERROR;
-+	tgt_p += short_len;
-+	if (copy_from_user(tgt_p, icaMsg_p->bp_key, long_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, long_len))
-+		return SEN_USER_ERROR;
-+	tgt_p += long_len;
-+	if (copy_from_user(tgt_p, icaMsg_p->bq_key, short_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, short_len))
-+		return SEN_USER_ERROR;
-+	tgt_p += short_len;
-+	if (copy_from_user(tgt_p, icaMsg_p->u_mult_inv, long_len))
-+		return SEN_RELEASED;
-+	if (is_empty(tgt_p, long_len))
-+		return SEN_USER_ERROR;
-+	tgt_p += long_len;
-+	tgt_p += pad_len;
-+	memset(tgt_p, 0xFF, mod_len);
-+	
-+	tgt_p += mod_len;
-+	memcpy(tgt_p, &static_cca_pub_sec, sizeof(struct cca_public_sec));
-+	pubSec_p = (struct cca_public_sec *) tgt_p;
-+	pubSec_p->modulus_bit_len = 8 * mod_len;
-+	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
-+	return 0;
-+}
-+int
-+convert_request(unsigned char *buffer, int func, unsigned short function,
-+		int cdx, int dev_type, int *msg_l_p, unsigned char *msg_p)
-+{
-+	if (dev_type == PCICA) {
-+		if (func == ICARSACRT)
-+			return ICACRT_msg_to_type4CRT_msg(
-+				(struct ica_rsa_modexpo_crt *) buffer,
-+				msg_l_p, (union type4_msg *) msg_p);
-+		else
-+			return ICAMEX_msg_to_type4MEX_msg(
-+				(struct ica_rsa_modexpo *) buffer,
-+				msg_l_p, (union type4_msg *) msg_p);
-+	}
-+	if (dev_type == PCICC) {
-+		if (func == ICARSACRT)
-+			return ICACRT_msg_to_type6CRT_msg(
-+				(struct ica_rsa_modexpo_crt *) buffer,
-+				cdx, msg_l_p, (struct type6_msg *)msg_p);
-+		if (function == PCI_FUNC_KEY_ENCRYPT)
-+			return ICAMEX_msg_to_type6MEX_en_msg(
-+				(struct ica_rsa_modexpo *) buffer,
-+				cdx, msg_l_p, (struct type6_msg *) msg_p);
-+		else
-+			return ICAMEX_msg_to_type6MEX_de_msg(
-+				(struct ica_rsa_modexpo *) buffer,
-+				cdx, msg_l_p, (struct type6_msg *) msg_p);
-+	}
-+	if ((dev_type == PCIXCC_MCL2) ||
-+	    (dev_type == PCIXCC_MCL3) ||
-+	    (dev_type == CEX2C)) {
-+		if (func == ICARSACRT)
-+			return ICACRT_msg_to_type6CRT_msgX(
-+				(struct ica_rsa_modexpo_crt *) buffer,
-+				cdx, msg_l_p, (struct type6_msg *) msg_p,
-+				dev_type);
-+		else
-+			return ICAMEX_msg_to_type6MEX_msgX(
-+				(struct ica_rsa_modexpo *) buffer,
-+				cdx, msg_l_p, (struct type6_msg *) msg_p,
-+				dev_type);
-+	}
-+	return 0;
-+}
-+int ext_bitlens_msg_count = 0; 
-+static inline void
-+unset_ext_bitlens(void)
-+{
-+	if (!ext_bitlens_msg_count) {
-+		PRINTK("Unable to use coprocessors for extended bitlengths. "
-+		       "Using PCICAs (if present) for extended bitlengths. "
-+		       "This is not an error.\n");
-+		ext_bitlens_msg_count++;
-+	}
-+	ext_bitlens = 0;
-+}
-+int
-+convert_response(unsigned char *response, unsigned char *buffer,
-+		 int *respbufflen_p, unsigned char *resp_buff)
-+{
-+	struct ica_rsa_modexpo *icaMsg_p = (struct ica_rsa_modexpo *) buffer;
-+	struct type82_hdr *t82h_p = (struct type82_hdr *) response;
-+	struct type84_hdr *t84h_p = (struct type84_hdr *) response;
-+	struct type86_fmt2_msg *t86m_p =  (struct type86_fmt2_msg *) response;
-+	int reply_code, service_rc, service_rs, src_l;
-+	unsigned char *src_p, *tgt_p;
-+	struct CPRB *cprb_p;
-+	struct CPRBX *cprbx_p;
-+	src_p = 0;
-+	reply_code = 0;
-+	service_rc = 0;
-+	service_rs = 0;
-+	src_l = 0;
-+	switch (t82h_p->type) {
-+	case TYPE82_RSP_CODE:
-+		reply_code = t82h_p->reply_code;
-+		src_p = (unsigned char *)t82h_p;
-+		PRINTK("Hardware error: Type 82 Message Header: "
-+		       "%02x%02x%02x%02x%02x%02x%02x%02x\n",
-+		       src_p[0], src_p[1], src_p[2], src_p[3],
-+		       src_p[4], src_p[5], src_p[6], src_p[7]);
-+		break;
-+	case TYPE84_RSP_CODE:
-+		src_l = icaMsg_p->outputdatalength;
-+		src_p = response + (int)t84h_p->len - src_l;
-+		break;
-+	case TYPE86_RSP_CODE:
-+		reply_code = t86m_p->hdr.reply_code;
-+		if (reply_code != 0)
-+			break;
-+		cprb_p = (struct CPRB *)
-+			(response + sizeof(struct type86_fmt2_msg));
-+		cprbx_p = (struct CPRBX *) cprb_p;
-+		if (cprb_p->cprb_ver_id != 0x02) {
-+			le2toI(cprb_p->ccp_rtcode, &service_rc);
-+			if (service_rc != 0) {
-+				le2toI(cprb_p->ccp_rscode, &service_rs);
-+				if ((service_rc == 8) && (service_rs == 66))
-+					PDEBUG("Bad block format on PCICC\n");
-+				else if ((service_rc == 8) && (service_rs == 770)) {
-+					PDEBUG("Invalid key length on PCICC\n");
-+					unset_ext_bitlens();
-+					return REC_USE_PCICA;
-+				}
-+				else if ((service_rc == 8) && (service_rs == 783)) {
-+					PDEBUG("Extended bitlengths not enabled"
-+					       "on PCICC\n");
-+					unset_ext_bitlens();
-+					return REC_USE_PCICA;
-+				}
-+				else
-+					PRINTK("service rc/rs: %d/%d\n",
-+					       service_rc, service_rs);
-+				return REC_OPERAND_INV;
-+			}
-+			src_p = (unsigned char *)cprb_p + sizeof(struct CPRB);
-+			src_p += 4; 
-+			le2toI(src_p, &src_l);
-+			src_l -= 2;	
-+			src_p += 2;	
-+		} else {
-+			service_rc = (int)cprbx_p->ccp_rtcode;
-+			if (service_rc != 0) {
-+				service_rs = (int) cprbx_p->ccp_rscode;
-+				if ((service_rc == 8) && (service_rs == 66))
-+					PDEBUG("Bad block format on PCXICC\n");
-+				else if ((service_rc == 8) && (service_rs == 770)) {
-+					PDEBUG("Invalid key length on PCIXCC\n");
-+					unset_ext_bitlens();
-+					return REC_USE_PCICA;
-+				}
-+				else if ((service_rc == 8) && (service_rs == 783)) {
-+					PDEBUG("Extended bitlengths not enabled"
-+					       "on PCIXCC\n");
-+					unset_ext_bitlens();
-+					return REC_USE_PCICA;
-+				}
-+				else
-+					PRINTK("service rc/rs: %d/%d\n",
-+					       service_rc, service_rs);
-+				return REC_OPERAND_INV;
-+			}
-+			src_p = (unsigned char *)
-+				cprbx_p + sizeof(struct CPRBX);
-+			src_p += 4; 
-+			src_l = (int)(*((short *) src_p));
-+			src_l -= 2;	
-+			src_p += 2;	
-+		}
-+		break;
-+	default:
-+		return REC_BAD_MESSAGE; 
-+	}
-+	
-+	if (reply_code)
-+		switch (reply_code) {
-+		case REPLY_ERROR_OPERAND_INVALID:	
-+			return REC_OPERAND_INV;
-+		case REPLY_ERROR_OPERAND_SIZE:		
-+			return REC_OPERAND_SIZE;
-+		case REPLY_ERROR_EVEN_MOD_IN_OPND:	
-+			return REC_EVEN_MOD;
-+		case REPLY_ERROR_MESSAGE_TYPE:
-+			return WRONG_DEVICE_TYPE;
-+		case REPLY_ERROR_TRANSPORT_FAIL: 
-+			PRINTKW("Transport failed (APFS = %02X%02X%02X%02X)\n",
-+				t86m_p->apfs[0], t86m_p->apfs[1],
-+				t86m_p->apfs[2], t86m_p->apfs[3]);
-+			return REC_HARDWAR_ERR;
-+		default:
-+			PRINTKW("reply code = %d\n", reply_code);
-+			return REC_HARDWAR_ERR;
-+		}
-+	if (service_rc != 0)
-+		return REC_OPERAND_INV;
-+	if ((src_l > icaMsg_p->outputdatalength) ||
-+	    (src_l > RESPBUFFSIZE) ||
-+	    (src_l <= 0))
-+		return REC_OPERAND_SIZE;
-+	PDEBUG("Length returned = %d\n", src_l);
-+	
-+	tgt_p = resp_buff + icaMsg_p->outputdatalength - src_l;
-+	memcpy(tgt_p, src_p, src_l);
-+	
-+	if ((t82h_p->type == TYPE86_RSP_CODE) && (resp_buff < tgt_p)) {
-+		memset(resp_buff, 0, icaMsg_p->outputdatalength - src_l);
-+		if (pad_msg(resp_buff, icaMsg_p->outputdatalength, src_l))
-+			return REC_INVALID_PAD;
-+	}
-+	*respbufflen_p = icaMsg_p->outputdatalength;
-+	if (*respbufflen_p == 0)
-+		PRINTK("Zero *respbufflen_p\n");
-+	return 0;
-+}
-=== drivers/s390/misc/z90main.c
-==================================================================
---- drivers/s390/misc/z90main.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/misc/z90main.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,3588 @@
-+/*
-+ *  linux/drivers/s390/misc/z90main.c
-+ *
-+ *  z90crypt 1.3.2
-+ *
-+ *  Copyright (C)  2001, 2004 IBM Corporation
-+ *  Author(s): Robert Burroughs (burrough at us.ibm.com)
-+ *             Eric Rossman (edrossma at us.ibm.com)
-+ *
-+ *  Hotplug & misc device support: Jochen Roehrig (roehrig 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 <asm/uaccess.h>       // copy_(from|to)_user
-+#include <linux/compiler.h>
-+#include <linux/delay.h>       // mdelay
-+#include <linux/init.h>
-+#include <linux/interrupt.h>   // for tasklets
-+#include <asm/ioctl32.h>
-+#include <linux/kmod.h>
-+#include <linux/module.h>
-+#include <linux/proc_fs.h>
-+#include <linux/version.h>
-+#include "z90crypt.h"
-+#include "z90common.h"
-+#ifndef Z90CRYPT_USE_HOTPLUG
-+#include <linux/miscdevice.h>
-+#endif
-+
-+#define VERSION_CODE(vers, rel, seq) (((vers)<<16) | ((rel)<<8) | (seq))
-+#if LINUX_VERSION_CODE < VERSION_CODE(2,4,0) /* version < 2.4 */
-+#  error "This kernel is too old: not supported"
-+#endif
-+#if LINUX_VERSION_CODE > VERSION_CODE(2,7,0) /* version > 2.6 */
-+#  error "This kernel is too recent: not supported by this file"
-+#endif
-+
-+#define VERSION_Z90MAIN_C "$Revision: 1.9.4.13 $"
-+
-+static char z90cmain_version[] __initdata =
-+	"z90main.o (" VERSION_Z90MAIN_C "/"
-+                      VERSION_Z90COMMON_H "/" VERSION_Z90CRYPT_H ")";
-+
-+extern char z90chardware_version[];
-+
-+/**
-+ * Defaults that may be modified.
-+ */
-+
-+#ifndef Z90CRYPT_USE_HOTPLUG
-+/**
-+ * You can specify a different minor at compile time.
-+ */
-+#ifndef Z90CRYPT_MINOR
-+#define Z90CRYPT_MINOR	MISC_DYNAMIC_MINOR
-+#endif
-+#else
-+/**
-+ * You can specify a different major at compile time.
-+ */
-+#ifndef Z90CRYPT_MAJOR
-+#define Z90CRYPT_MAJOR	0
-+#endif
-+#endif
-+
-+/**
-+ * You can specify a different domain at compile time or on the insmod
-+ * command line.
-+ */
-+#ifndef DOMAIN_INDEX
-+#define DOMAIN_INDEX	-1
-+#endif
-+
-+/**
-+ * This is the name under which the device is registered in /proc/modules.
-+ */
-+#define REG_NAME	"z90crypt"
-+
-+/**
-+ * Cleanup should run every CLEANUPTIME seconds and should clean up requests
-+ * older than CLEANUPTIME seconds in the past.
-+ */
-+#ifndef CLEANUPTIME
-+#define CLEANUPTIME 15
-+#endif
-+
-+/**
-+ * Config should run every CONFIGTIME seconds
-+ */
-+#ifndef CONFIGTIME
-+#define CONFIGTIME 30
-+#endif
-+
-+/**
-+ * The first execution of the config task should take place
-+ * immediately after initialization
-+ */
-+#ifndef INITIAL_CONFIGTIME
-+#define INITIAL_CONFIGTIME 1
-+#endif
-+
-+/**
-+ * Reader should run every READERTIME milliseconds
-+ * With the 100Hz patch for s390, z90crypt can lock the system solid while
-+ * under heavy load. We'll try to avoid that.
-+ */
-+#ifndef READERTIME
-+#if HZ > 1000
-+#define READERTIME 2
-+#else
-+#define READERTIME 10
-+#endif
-+#endif
-+
-+/**
-+ * turn long device array index into device pointer
-+ */
-+#define LONG2DEVPTR(ndx) (z90crypt.device_p[(ndx)])
-+
-+/**
-+ * turn short device array index into long device array index
-+ */
-+#define SHRT2LONG(ndx) (z90crypt.overall_device_x.device_index[(ndx)])
-+
-+/**
-+ * turn short device array index into device pointer
-+ */
-+#define SHRT2DEVPTR(ndx) LONG2DEVPTR(SHRT2LONG(ndx))
-+
-+/**
-+ * Status for a work-element
-+ */
-+#define STAT_DEFAULT	0x00 // request has not been processed
-+
-+#define STAT_ROUTED	0x80 // bit 7: requests get routed to specific device
-+			     //	       else, device is determined each write
-+#define STAT_FAILED	0x40 // bit 6: this bit is set if the request failed
-+			     //	       before being sent to the hardware.
-+#define STAT_WRITTEN	0x30 // bits 5-4: work to be done, not sent to device
-+//			0x20 // UNUSED state
-+#define STAT_READPEND	0x10 // bits 5-4: work done, we're returning data now
-+#define STAT_NOWORK	0x00 // bits off: no work on any queue
-+#define STAT_RDWRMASK	0x30 // mask for bits 5-4
-+
-+/**
-+ * Macros to check the status RDWRMASK
-+ */
-+#define CHK_RDWRMASK(statbyte) ((statbyte) & STAT_RDWRMASK)
-+#define SET_RDWRMASK(statbyte, newval) \
-+	{(statbyte) &= ~STAT_RDWRMASK; (statbyte) |= newval;}
-+
-+/**
-+ * Audit Trail.	 Progress of a Work element
-+ * audit[0]: Unless noted otherwise, these bits are all set by the process
-+ */
-+#define FP_COPYFROM	0x80 // Caller's buffer has been copied to work element
-+#define FP_BUFFREQ	0x40 // Low Level buffer requested
-+#define FP_BUFFGOT	0x20 // Low Level buffer obtained
-+#define FP_SENT		0x10 // Work element sent to a crypto device
-+			     // (may be set by process or by reader task)
-+#define FP_PENDING	0x08 // Work element placed on pending queue
-+			     // (may be set by process or by reader task)
-+#define FP_REQUEST	0x04 // Work element placed on request queue
-+#define FP_ASLEEP	0x02 // Work element about to sleep
-+#define FP_AWAKE	0x01 // Work element has been awakened
-+
-+/**
-+ * audit[1]: These bits are set by the reader task and/or the cleanup task
-+ */
-+#define FP_NOTPENDING	  0x80 // Work element removed from pending queue
-+#define FP_AWAKENING	  0x40 // Caller about to be awakened
-+#define FP_TIMEDOUT	  0x20 // Caller timed out
-+#define FP_RESPSIZESET	  0x10 // Response size copied to work element
-+#define FP_RESPADDRCOPIED 0x08 // Response address copied to work element
-+#define FP_RESPBUFFCOPIED 0x04 // Response buffer copied to work element
-+#define FP_REMREQUEST	  0x02 // Work element removed from request queue
-+#define FP_SIGNALED	  0x01 // Work element was awakened by a signal
-+
-+/**
-+ * audit[2]: unused
-+ */
-+
-+/**
-+ * state of the file handle in private_data.status
-+ */
-+#define STAT_OPEN 0
-+#define STAT_CLOSED 1
-+
-+/**
-+ * PID() expands to the process ID of the current process
-+ */
-+#define PID() (current->pid)
-+
-+/**
-+ * Selected Constants.	The number of APs and the number of devices
-+ */
-+#ifndef Z90CRYPT_NUM_APS
-+#define Z90CRYPT_NUM_APS 64
-+#endif
-+#ifndef Z90CRYPT_NUM_DEVS
-+#define Z90CRYPT_NUM_DEVS Z90CRYPT_NUM_APS
-+#endif
-+
-+/**
-+ * Buffer size for receiving responses. The maximum Response Size
-+ * is actually the maximum request size, since in an error condition
-+ * the request itself may be returned unchanged.
-+ */
-+#define MAX_RESPONSE_SIZE 0x0000077C
-+
-+/**
-+ * A count and status-byte mask
-+ */
-+struct status {
-+	int	      st_count;		    // # of enabled devices
-+	int	      disabled_count;	    // # of disabled devices
-+	int	      user_disabled_count;  // # of devices disabled via proc fs
-+	unsigned char st_mask[Z90CRYPT_NUM_APS]; // current status mask
-+};
-+
-+/**
-+ * The array of device indexes is a mechanism for fast indexing into
-+ * a long (and sparse) array.  For instance, if APs 3, 9 and 47 are
-+ * installed, z90CDeviceIndex[0] is 3, z90CDeviceIndex[1] is 9, and
-+ * z90CDeviceIndex[2] is 47.
-+ */
-+struct device_x {
-+	int device_index[Z90CRYPT_NUM_DEVS];
-+};
-+
-+/**
-+ * All devices are arranged in a single array: 64 APs
-+ */
-+struct device {
-+	int		 dev_type;	    // PCICA, PCICC, PCIXCC_MCL2,
-+					    // PCIXCC_MCL3, CEX2C
-+	enum devstat	 dev_stat;	    // current device status
-+	int		 dev_self_x;	    // Index in array
-+	int		 disabled;	    // Set when device is in error
-+	int		 user_disabled;	    // Set when device is disabled by user
-+	int		 dev_q_depth;	    // q depth
-+	unsigned char *	 dev_resp_p;	    // Response buffer address
-+	int		 dev_resp_l;	    // Response Buffer length
-+	int		 dev_caller_count;  // Number of callers
-+	int		 dev_total_req_cnt; // # requests for device since load
-+	struct list_head dev_caller_list;   // List of callers
-+};
-+
-+/**
-+ * There's a struct status and a struct device_x for each device type.
-+ */
-+struct hdware_block {
-+	struct status	hdware_mask;
-+	struct status	type_mask[Z90CRYPT_NUM_TYPES];
-+	struct device_x type_x_addr[Z90CRYPT_NUM_TYPES];
-+	unsigned char	device_type_array[Z90CRYPT_NUM_APS];
-+};
-+
-+/**
-+ * z90crypt is the topmost data structure in the hierarchy.
-+ */
-+struct z90crypt {
-+	int		     max_count;		// Nr of possible crypto devices
-+	struct status	     mask;
-+	int		     q_depth_array[Z90CRYPT_NUM_DEVS];
-+	int		     dev_type_array[Z90CRYPT_NUM_DEVS];
-+	struct device_x	     overall_device_x;	// array device indexes
-+	struct device *	     device_p[Z90CRYPT_NUM_DEVS];
-+	int		     terminating;
-+	int		     domain_established;// TRUE:  domain has been found
-+	int		     cdx;		// Crypto Domain Index
-+	int		     len;		// Length of this data structure
-+	struct hdware_block *hdware_info;
-+};
-+
-+/**
-+ * An array of these structures is pointed to from dev_caller
-+ * The length of the array depends on the device type. For APs,
-+ * there are 8.
-+ *
-+ * The caller buffer is allocated to the user at OPEN. At WRITE,
-+ * it contains the request; at READ, the response. The function
-+ * send_to_crypto_device converts the request to device-dependent
-+ * form and use the caller's OPEN-allocated buffer for the response.
-+ */
-+struct caller {
-+	int		 caller_buf_l;		 // length of original request
-+	unsigned char *	 caller_buf_p;		 // Original request on WRITE
-+	int		 caller_dev_dep_req_l;	 // len device dependent request
-+	unsigned char *	 caller_dev_dep_req_p;	 // Device dependent form
-+	unsigned char	 caller_id[8];		 // caller-supplied message id
-+	struct list_head caller_liste;
-+	unsigned char	 caller_dev_dep_req[MAX_RESPONSE_SIZE];
-+};
-+
-+/**
-+ * Function prototypes from z90hardware.c
-+ */
-+enum hdstat query_online(int, int, int, int *, int *);
-+enum devstat reset_device(int, int, int);
-+enum devstat send_to_AP(int, int, int, unsigned char *);
-+enum devstat receive_from_AP(int, int, int, unsigned char *, unsigned char *);
-+int convert_request(unsigned char *, int, short, int, int, int *,
-+		    unsigned char *);
-+int convert_response(unsigned char *, unsigned char *, int *, unsigned char *);
-+
-+/**
-+ * Low level function prototypes
-+ */
-+static int create_z90crypt(int *);
-+static int refresh_z90crypt(int *);
-+static int find_crypto_devices(struct status *);
-+static int create_crypto_device(int);
-+static int destroy_crypto_device(int);
-+static void destroy_z90crypt(void);
-+static int refresh_index_array(struct status *, struct device_x *);
-+static int probe_device_type(struct device *);
-+static int probe_PCIXCC_type(struct device *);
-+
-+/**
-+ * proc fs definitions
-+ */
-+static struct proc_dir_entry *z90crypt_entry;
-+
-+/**
-+ * data structures
-+ */
-+
-+/**
-+ * work_element.opener points back to this structure
-+ */
-+struct priv_data {
-+	pid_t	opener_pid;
-+	unsigned char	status;		// 0: open  1: closed
-+};
-+
-+/**
-+ * A work element is allocated for each request
-+ */
-+struct work_element {
-+	struct priv_data *priv_data;
-+	pid_t		  pid;
-+	int		  devindex;	  // index of device processing this w_e
-+					  // (If request did not specify device,
-+					  // -1 until placed onto a queue)
-+	int		  devtype;
-+	struct list_head  liste;	  // used for requestq and pendingq
-+	char		  buffer[128];	  // local copy of user request
-+	int		  buff_size;	  // size of the buffer for the request
-+	char		  resp_buff[RESPBUFFSIZE];
-+	int		  resp_buff_size;
-+	char __user *	  resp_addr;	  // address of response in user space
-+	unsigned int	  funccode;	  // function code of request
-+	wait_queue_head_t waitq;
-+	unsigned long	  requestsent;	  // time at which the request was sent
-+	atomic_t	  alarmrung;	  // wake-up signal
-+	unsigned char	  caller_id[8];	  // pid + counter, for this w_e
-+	unsigned char	  status[1];	  // bits to mark status of the request
-+	unsigned char	  audit[3];	  // record of work element's progress
-+	unsigned char *	  requestptr;	  // address of request buffer
-+	int		  retcode;	  // return code of request
-+};
-+
-+/**
-+ * High level function prototypes
-+ */
-+static int z90crypt_open(struct inode *, struct file *);
-+static int z90crypt_release(struct inode *, struct file *);
-+static ssize_t z90crypt_read(struct file *, char __user *, size_t, loff_t *);
-+static ssize_t z90crypt_write(struct file *, const char __user *,
-+							size_t, loff_t *);
-+static int z90crypt_ioctl(struct inode *, struct file *,
-+			  unsigned int, unsigned long);
-+
-+static void z90crypt_reader_task(unsigned long);
-+static void z90crypt_schedule_reader_task(unsigned long);
-+static void z90crypt_config_task(unsigned long);
-+static void z90crypt_cleanup_task(unsigned long);
-+
-+static int z90crypt_status(char *, char **, off_t, int, int *, void *);
-+static int z90crypt_status_write(struct file *, const char __user *,
-+				 unsigned long, void *);
-+
-+/**
-+ * Hotplug support
-+ */
-+
-+#ifdef Z90CRYPT_USE_HOTPLUG
-+#define Z90CRYPT_HOTPLUG_ADD	 1
-+#define Z90CRYPT_HOTPLUG_REMOVE	 2
-+
-+static void z90crypt_hotplug_event(int, int, int);
-+#endif
-+
-+/**
-+ * Storage allocated at initialization and used throughout the life of
-+ * this insmod
-+ */
-+#ifdef Z90CRYPT_USE_HOTPLUG
-+static int z90crypt_major = Z90CRYPT_MAJOR;
-+#endif
-+
-+static int domain = DOMAIN_INDEX;
-+static struct z90crypt z90crypt;
-+static int quiesce_z90crypt;
-+static spinlock_t queuespinlock;
-+static struct list_head request_list;
-+static int requestq_count;
-+static struct list_head pending_list;
-+static int pendingq_count;
-+
-+static struct tasklet_struct reader_tasklet;
-+static struct timer_list reader_timer;
-+static struct timer_list config_timer;
-+static struct timer_list cleanup_timer;
-+static atomic_t total_open;
-+static atomic_t z90crypt_step;
-+
-+static struct file_operations z90crypt_fops = {
-+	.owner	 = THIS_MODULE,
-+	.read	 = z90crypt_read,
-+	.write	 = z90crypt_write,
-+	.ioctl	 = z90crypt_ioctl,
-+	.open	 = z90crypt_open,
-+	.release = z90crypt_release
-+};
-+
-+#ifndef Z90CRYPT_USE_HOTPLUG
-+static struct miscdevice z90crypt_misc_device = {
-+	.minor	    = Z90CRYPT_MINOR,
-+	.name	    = DEV_NAME,
-+	.fops	    = &z90crypt_fops,
-+};
-+#endif
-+
-+/**
-+ * Documentation values.
-+ */
-+MODULE_AUTHOR("zSeries Linux Crypto Team: Robert H. Burroughs, Eric D. Rossman"
-+	      "and Jochen Roehrig");
-+MODULE_DESCRIPTION("zSeries Linux Cryptographic Coprocessor device driver, "
-+		   "Copyright 2001, 2004 IBM Corporation");
-+MODULE_LICENSE("GPL");
-+MODULE_PARM(domain, "i");
-+MODULE_PARM_DESC(domain, "domain index for device");
-+
-+#ifdef CONFIG_S390_SUPPORT
-+/**
-+ * Borrowed from 2.6 kernel
-+ * - compat_uptr_t
-+ * - compat_ptr()
-+ * - compat_alloc_user_space()
-+ */
-+/**
-+ * A pointer passed in from user mode. This should not
-+ * be used for syscall parameters, just declare them
-+ * as pointers because the syscall entry code will have
-+ * appropriately comverted them already.
-+ */
-+typedef	u32	compat_uptr_t;
-+
-+static inline void __user *compat_ptr(compat_uptr_t uptr)
-+{
-+	return (void __user *)(unsigned long)(uptr & 0x7fffffffUL);
-+}
-+
-+static inline void __user *compat_alloc_user_space(long len)
-+{
-+	unsigned long stack;
-+
-+	stack = KSTK_ESP(current);
-+	stack &= 0x7fffffffUL;
-+	return (void __user *) (stack - len);
-+}
-+
-+/**
-+ * ioctl32 conversion routines
-+ */
-+struct ica_rsa_modexpo_32 { // For 32-bit callers
-+	compat_uptr_t	inputdata;
-+	unsigned int	inputdatalength;
-+	compat_uptr_t	outputdata;
-+	unsigned int	outputdatalength;
-+	compat_uptr_t	b_key;
-+	compat_uptr_t	n_modulus;
-+};
-+
-+static int
-+trans_modexpo32(unsigned int fd, unsigned int cmd, unsigned long arg,
-+		struct file *file)
-+{
-+	struct ica_rsa_modexpo_32 __user *mex32u = compat_ptr(arg);
-+	struct ica_rsa_modexpo_32  mex32k;
-+	struct ica_rsa_modexpo __user *mex64;
-+	int ret = 0;
-+	unsigned int i;
-+
-+	if (!access_ok(VERIFY_WRITE, mex32u, sizeof(struct ica_rsa_modexpo_32)))
-+		return -EFAULT;
-+	mex64 = compat_alloc_user_space(sizeof(struct ica_rsa_modexpo));
-+	if (!access_ok(VERIFY_WRITE, mex64, sizeof(struct ica_rsa_modexpo)))
-+		return -EFAULT;
-+	if (copy_from_user(&mex32k, mex32u, sizeof(struct ica_rsa_modexpo_32)))
-+		return -EFAULT;
-+	if (__put_user(compat_ptr(mex32k.inputdata), &mex64->inputdata)   ||
-+	    __put_user(mex32k.inputdatalength, &mex64->inputdatalength)   ||
-+	    __put_user(compat_ptr(mex32k.outputdata), &mex64->outputdata) ||
-+	    __put_user(mex32k.outputdatalength, &mex64->outputdatalength) ||
-+	    __put_user(compat_ptr(mex32k.b_key), &mex64->b_key)           ||
-+	    __put_user(compat_ptr(mex32k.n_modulus), &mex64->n_modulus))
-+		return -EFAULT;
-+	ret = sys_ioctl(fd, cmd, (unsigned long)mex64);
-+	if (!ret)
-+		if (__get_user(i, &mex64->outputdatalength) ||
-+		    __put_user(i, &mex32u->outputdatalength))
-+			ret = -EFAULT;
-+	return ret;
-+}
-+
-+struct ica_rsa_modexpo_crt_32 { // For 32-bit callers
-+	compat_uptr_t	inputdata;
-+	unsigned int	inputdatalength;
-+	compat_uptr_t	outputdata;
-+	unsigned int	outputdatalength;
-+	compat_uptr_t	bp_key;
-+	compat_uptr_t	bq_key;
-+	compat_uptr_t	np_prime;
-+	compat_uptr_t	nq_prime;
-+	compat_uptr_t	u_mult_inv;
-+};
-+
-+static int
-+trans_modexpo_crt32(unsigned int fd, unsigned int cmd, unsigned long arg,
-+		    struct file *file)
-+{
-+	struct ica_rsa_modexpo_crt_32 __user *crt32u = compat_ptr(arg);
-+	struct ica_rsa_modexpo_crt_32  crt32k;
-+	struct ica_rsa_modexpo_crt __user *crt64;
-+	int ret = 0;
-+	unsigned int i;
-+
-+	if (!access_ok(VERIFY_WRITE, crt32u,
-+		       sizeof(struct ica_rsa_modexpo_crt_32)))
-+		return -EFAULT;
-+	crt64 = compat_alloc_user_space(sizeof(struct ica_rsa_modexpo_crt));
-+	if (!access_ok(VERIFY_WRITE, crt64, sizeof(struct ica_rsa_modexpo_crt)))
-+		return -EFAULT;
-+	if (copy_from_user(&crt32k, crt32u,
-+			   sizeof(struct ica_rsa_modexpo_crt_32)))
-+		return -EFAULT;
-+	if (__put_user(compat_ptr(crt32k.inputdata), &crt64->inputdata)   ||
-+	    __put_user(crt32k.inputdatalength, &crt64->inputdatalength)   ||
-+	    __put_user(compat_ptr(crt32k.outputdata), &crt64->outputdata) ||
-+	    __put_user(crt32k.outputdatalength, &crt64->outputdatalength) ||
-+	    __put_user(compat_ptr(crt32k.bp_key), &crt64->bp_key)         ||
-+	    __put_user(compat_ptr(crt32k.bq_key), &crt64->bq_key)         ||
-+	    __put_user(compat_ptr(crt32k.np_prime), &crt64->np_prime)     ||
-+	    __put_user(compat_ptr(crt32k.nq_prime), &crt64->nq_prime)     ||
-+	    __put_user(compat_ptr(crt32k.u_mult_inv), &crt64->u_mult_inv))
-+		ret = -EFAULT;
-+	if (!ret)
-+		ret = sys_ioctl(fd, cmd, (unsigned long)crt64);
-+	if (!ret)
-+		if (__get_user(i, &crt64->outputdatalength) ||
-+		    __put_user(i, &crt32u->outputdatalength))
-+			ret = -EFAULT;
-+	return ret;
-+}
-+
-+static int compatible_ioctls[] = {
-+	ICAZ90STATUS, Z90QUIESCE, Z90STAT_TOTALCOUNT, Z90STAT_PCICACOUNT,
-+	Z90STAT_PCICCCOUNT, Z90STAT_PCIXCCCOUNT, Z90STAT_PCIXCCMCL2COUNT,
-+	Z90STAT_PCIXCCMCL3COUNT, Z90STAT_CEX2CCOUNT, Z90STAT_REQUESTQ_COUNT,
-+	Z90STAT_PENDINGQ_COUNT, Z90STAT_TOTALOPEN_COUNT, Z90STAT_DOMAIN_INDEX,
-+	Z90STAT_STATUS_MASK, Z90STAT_QDEPTH_MASK, Z90STAT_PERDEV_REQCNT,
-+};
-+
-+static void z90_unregister_ioctl32s(void)
-+{
-+	int i;
-+
-+	unregister_ioctl32_conversion(ICARSAMODEXPO);
-+	unregister_ioctl32_conversion(ICARSACRT);
-+
-+	for(i = 0; i < ARRAY_SIZE(compatible_ioctls); i++)
-+		unregister_ioctl32_conversion(compatible_ioctls[i]);
-+}
-+
-+static int z90_register_ioctl32s(void)
-+{
-+	int result, i;
-+
-+	result = register_ioctl32_conversion(ICARSAMODEXPO, trans_modexpo32);
-+	if (result == -EBUSY) {
-+		unregister_ioctl32_conversion(ICARSAMODEXPO);
-+		result = register_ioctl32_conversion(ICARSAMODEXPO,
-+						     trans_modexpo32);
-+	}
-+	if (result)
-+		return result;
-+	result = register_ioctl32_conversion(ICARSACRT, trans_modexpo_crt32);
-+	if (result == -EBUSY) {
-+		unregister_ioctl32_conversion(ICARSACRT);
-+		result = register_ioctl32_conversion(ICARSACRT,
-+						     trans_modexpo_crt32);
-+	}
-+	if (result)
-+		return result;
-+
-+	for(i = 0; i < ARRAY_SIZE(compatible_ioctls); i++) {
-+		result = register_ioctl32_conversion(compatible_ioctls[i],
-+						     (void *) sys_ioctl);
-+		if (result == -EBUSY) {
-+			unregister_ioctl32_conversion(compatible_ioctls[i]);
-+			result = register_ioctl32_conversion(
-+							compatible_ioctls[i],
-+							(void *) sys_ioctl);
-+		}
-+		if (result)
-+			return result;
-+	}
-+	return 0;
-+}
-+#else // !CONFIG_COMPAT
-+static inline void z90_unregister_ioctl32s(void)
-+{
-+}
-+
-+static inline int z90_register_ioctl32s(void)
-+{
-+	return 0;
-+}
-+#endif
-+
-+/**
-+ * The module initialization code.
-+ */
-+static int __init
-+z90crypt_init_module(void)
-+{
-+	int result, nresult;
-+	struct proc_dir_entry *entry;
-+
-+	PDEBUG("PID %d\n", PID());
-+
-+	if ((domain < -1) || (domain > 15)) {
-+		PRINTKW("Invalid param: domain = %d.  Not loading.\n", domain);
-+		return -EINVAL;
-+	}
-+
-+#ifndef Z90CRYPT_USE_HOTPLUG
-+	/* Register as misc device with given minor (or get a dynamic one). */
-+	result = misc_register(&z90crypt_misc_device);
-+	if (result < 0) {
-+		PRINTKW(KERN_ERR "misc_register (minor %d) failed with %d\n",
-+			z90crypt_misc_device.minor, result);
-+		return result;
-+	}
-+#else
-+	/* Register the major (or get a dynamic one). */
-+	result = register_chrdev(z90crypt_major, REG_NAME, &z90crypt_fops);
-+	if (result < 0) {
-+		PRINTKW("register_chrdev (major %d) failed with %d.\n",
-+			z90crypt_major, result);
-+		return result;
-+	}
-+
-+	if (z90crypt_major == 0)
-+		z90crypt_major = result;
-+#endif
-+
-+	PDEBUG("Registered " DEV_NAME " with result %d\n", result);
-+
-+	result = create_z90crypt(&domain);
-+	if (result != 0) {
-+		PRINTKW("create_z90crypt (domain index %d) failed with %d.\n",
-+			domain, result);
-+		result = -ENOMEM;
-+		goto init_module_cleanup;
-+	}
-+
-+	if (result == 0) {
-+		PRINTKN("Version %d.%d.%d loaded, built on %s %s\n",
-+			z90crypt_VERSION, z90crypt_RELEASE, z90crypt_VARIANT,
-+			__DATE__, __TIME__);
-+		PRINTKN("%s\n", z90cmain_version);
-+		PRINTKN("%s\n", z90chardware_version);
-+		PDEBUG("create_z90crypt (domain index %d) successful.\n",
-+		       domain);
-+	} else
-+		PRINTK("No devices at startup\n");
-+
-+#ifdef Z90CRYPT_USE_HOTPLUG
-+	/* generate hotplug event for device node generation */
-+	z90crypt_hotplug_event(z90crypt_major, 0, Z90CRYPT_HOTPLUG_ADD);
-+#endif
-+
-+	/* Initialize globals. */
-+	spin_lock_init(&queuespinlock);
-+
-+	INIT_LIST_HEAD(&pending_list);
-+	pendingq_count = 0;
-+
-+	INIT_LIST_HEAD(&request_list);
-+	requestq_count = 0;
-+
-+	quiesce_z90crypt = 0;
-+
-+	atomic_set(&total_open, 0);
-+	atomic_set(&z90crypt_step, 0);
-+
-+	/* Set up the cleanup task. */
-+	init_timer(&cleanup_timer);
-+	cleanup_timer.function = z90crypt_cleanup_task;
-+	cleanup_timer.data = 0;
-+	cleanup_timer.expires = jiffies + (CLEANUPTIME * HZ);
-+	add_timer(&cleanup_timer);
-+
-+	/* Set up the proc file system */
-+	entry = create_proc_entry("driver/z90crypt", 0644, 0);
-+	if (entry) {
-+		entry->nlink = 1;
-+		entry->data = 0;
-+		entry->read_proc = z90crypt_status;
-+		entry->write_proc = z90crypt_status_write;
-+	}
-+	else
-+		PRINTK("Couldn't create z90crypt proc entry\n");
-+	z90crypt_entry = entry;
-+
-+	/* Set up the configuration task. */
-+	init_timer(&config_timer);
-+	config_timer.function = z90crypt_config_task;
-+	config_timer.data = 0;
-+	config_timer.expires = jiffies + (INITIAL_CONFIGTIME * HZ);
-+	add_timer(&config_timer);
-+
-+	/* Set up the reader task */
-+	tasklet_init(&reader_tasklet, z90crypt_reader_task, 0);
-+	init_timer(&reader_timer);
-+	reader_timer.function = z90crypt_schedule_reader_task;
-+	reader_timer.data = 0;
-+	reader_timer.expires = jiffies + (READERTIME * HZ / 1000);
-+	add_timer(&reader_timer);
-+
-+	if ((result = z90_register_ioctl32s()))
-+		goto init_module_cleanup;
-+
-+	return 0; // success
-+
-+init_module_cleanup:
-+	z90_unregister_ioctl32s();
-+
-+#ifndef Z90CRYPT_USE_HOTPLUG
-+	if ((nresult = misc_deregister(&z90crypt_misc_device)))
-+		PRINTK("misc_deregister failed with %d.\n", nresult);
-+	else
-+		PDEBUG("misc_deregister successful.\n");
-+#else
-+	if ((nresult = unregister_chrdev(z90crypt_major, REG_NAME)))
-+		PRINTK("unregister_chrdev failed with %d.\n", nresult);
-+	else
-+		PDEBUG("unregister_chrdev successful.\n");
-+#endif
-+
-+	return result; // failure
-+}
-+
-+/**
-+ * The module termination code
-+ */
-+static void __exit
-+z90crypt_cleanup_module(void)
-+{
-+	int nresult;
-+
-+	PDEBUG("PID %d\n", PID());
-+
-+	z90_unregister_ioctl32s();
-+
-+	remove_proc_entry("driver/z90crypt", 0);
-+
-+#ifndef Z90CRYPT_USE_HOTPLUG
-+	if ((nresult = misc_deregister(&z90crypt_misc_device)))
-+		PRINTK("misc_deregister failed with %d.\n", nresult);
-+	else
-+		PDEBUG("misc_deregister successful.\n");
-+#else
-+	z90crypt_hotplug_event(z90crypt_major, 0, Z90CRYPT_HOTPLUG_REMOVE);
-+
-+	if ((nresult = unregister_chrdev(z90crypt_major, REG_NAME)))
-+		PRINTK("unregister_chrdev failed with %d.\n", nresult);
-+	else
-+		PDEBUG("unregister_chrdev successful.\n");
-+#endif
-+
-+	/* Remove the tasks */
-+	tasklet_kill(&reader_tasklet);
-+	del_timer(&reader_timer);
-+	del_timer(&config_timer);
-+	del_timer(&cleanup_timer);
-+
-+	destroy_z90crypt();
-+
-+	PRINTKN("Unloaded.\n");
-+}
-+
-+/**
-+ * Functions running under a process id
-+ *
-+ * The I/O functions:
-+ *     z90crypt_open
-+ *     z90crypt_release
-+ *     z90crypt_read
-+ *     z90crypt_write
-+ *     z90crypt_ioctl
-+ *     z90crypt_status
-+ *     z90crypt_status_write
-+ *	 disable_card
-+ *	 enable_card
-+ *	 scan_char
-+ *	 scan_string
-+ *
-+ * Helper functions:
-+ *     z90crypt_rsa
-+ *	 z90crypt_prepare
-+ *	 z90crypt_send
-+ *	 z90crypt_process_results
-+ *
-+ */
-+static int
-+z90crypt_open(struct inode *inode, struct file *filp)
-+{
-+	struct priv_data *private_data_p;
-+
-+	if (quiesce_z90crypt)
-+		return -EQUIESCE;
-+
-+	private_data_p = kmalloc(sizeof(struct priv_data), GFP_KERNEL);
-+	if (!private_data_p) {
-+		PRINTK("Memory allocate failed\n");
-+		return -ENOMEM;
-+	}
-+
-+	memset((void *)private_data_p, 0, sizeof(struct priv_data));
-+	private_data_p->status = STAT_OPEN;
-+	private_data_p->opener_pid = PID();
-+	filp->private_data = private_data_p;
-+	atomic_inc(&total_open);
-+
-+	return 0;
-+}
-+
-+static int
-+z90crypt_release(struct inode *inode, struct file *filp)
-+{
-+	struct priv_data *private_data_p = filp->private_data;
-+
-+	PDEBUG("PID %d (filp %p)\n", PID(), filp);
-+
-+	private_data_p->status = STAT_CLOSED;
-+	memset(private_data_p, 0, sizeof(struct priv_data));
-+	kfree(private_data_p);
-+	atomic_dec(&total_open);
-+
-+	return 0;
-+}
-+
-+/*
-+ * there are two read functions, of which compile options will choose one
-+ * without USE_GET_RANDOM_BYTES
-+ *   => read() always returns -EPERM;
-+ * otherwise
-+ *   => read() uses get_random_bytes() kernel function
-+ */
-+#ifndef USE_GET_RANDOM_BYTES
-+/**
-+ * z90crypt_read will not be supported beyond z90crypt 1.3.1
-+ */
-+static ssize_t
-+z90crypt_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
-+{
-+	PDEBUG("filp %p (PID %d)\n", filp, PID());
-+	return -EPERM;
-+}
-+#else // we want to use get_random_bytes
-+/**
-+ * read() just returns a string of random bytes.  Since we have no way
-+ * to generate these cryptographically, we just execute get_random_bytes
-+ * for the length specified.
-+ */
-+#include <linux/random.h>
-+static ssize_t
-+z90crypt_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
-+{
-+	unsigned char *temp_buff;
-+
-+	PDEBUG("filp %p (PID %d)\n", filp, PID());
-+
-+	if (quiesce_z90crypt)
-+		return -EQUIESCE;
-+	if (count < 0) {
-+		PRINTK("Requested random byte count negative: %ld\n", count);
-+		return -EINVAL;
-+	}
-+	if (count > RESPBUFFSIZE) {
-+		PDEBUG("count[%d] > RESPBUFFSIZE", count);
-+		return -EINVAL;
-+	}
-+	if (count == 0)
-+		return 0;
-+	temp_buff = kmalloc(RESPBUFFSIZE, GFP_KERNEL);
-+	if (!temp_buff) {
-+		PRINTK("Memory allocate failed\n");
-+		return -ENOMEM;
-+	}
-+	get_random_bytes(temp_buff, count);
-+
-+	if (copy_to_user(buf, temp_buff, count) != 0) {
-+		kfree(temp_buff);
-+		return -EFAULT;
-+	}
-+	kfree(temp_buff);
-+	return count;
-+}
-+#endif
-+
-+/**
-+ * Write is is not allowed
-+ */
-+static ssize_t
-+z90crypt_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
-+{
-+	PDEBUG("filp %p (PID %d)\n", filp, PID());
-+	return -EPERM;
-+}
-+
-+/**
-+ * New status functions
-+ */
-+static inline int
-+get_status_totalcount(void)
-+{
-+	return z90crypt.hdware_info->hdware_mask.st_count;
-+}
-+
-+static inline int
-+get_status_PCICAcount(void)
-+{
-+	return z90crypt.hdware_info->type_mask[PCICA].st_count;
-+}
-+
-+static inline int
-+get_status_PCICCcount(void)
-+{
-+	return z90crypt.hdware_info->type_mask[PCICC].st_count;
-+}
-+
-+static inline int
-+get_status_PCIXCCcount(void)
-+{
-+	return z90crypt.hdware_info->type_mask[PCIXCC_MCL2].st_count +
-+	       z90crypt.hdware_info->type_mask[PCIXCC_MCL3].st_count;
-+}
-+
-+static inline int
-+get_status_PCIXCCMCL2count(void)
-+{
-+	return z90crypt.hdware_info->type_mask[PCIXCC_MCL2].st_count;
-+}
-+
-+static inline int
-+get_status_PCIXCCMCL3count(void)
-+{
-+	return z90crypt.hdware_info->type_mask[PCIXCC_MCL3].st_count;
-+}
-+
-+static inline int
-+get_status_CEX2Ccount(void)
-+{
-+	return z90crypt.hdware_info->type_mask[CEX2C].st_count;
-+}
-+
-+static inline int
-+get_status_requestq_count(void)
-+{
-+	return requestq_count;
-+}
-+
-+static inline int
-+get_status_pendingq_count(void)
-+{
-+	return pendingq_count;
-+}
-+
-+static inline int
-+get_status_totalopen_count(void)
-+{
-+	return atomic_read(&total_open);
-+}
-+
-+static inline int
-+get_status_domain_index(void)
-+{
-+	return z90crypt.cdx;
-+}
-+
-+static inline unsigned char *
-+get_status_status_mask(unsigned char status[Z90CRYPT_NUM_APS])
-+{
-+	int i, ix;
-+
-+	memcpy(status, z90crypt.hdware_info->device_type_array,
-+	       Z90CRYPT_NUM_APS);
-+
-+	for (i = 0; i < get_status_totalcount(); i++) {
-+		ix = SHRT2LONG(i);
-+		if (LONG2DEVPTR(ix)->user_disabled)
-+			status[ix] = 0x0d;
-+	}
-+
-+	return status;
-+}
-+
-+static inline unsigned char *
-+get_status_qdepth_mask(unsigned char qdepth[Z90CRYPT_NUM_APS])
-+{
-+	int i, ix;
-+
-+	memset(qdepth, 0, Z90CRYPT_NUM_APS);
-+
-+	for (i = 0; i < get_status_totalcount(); i++) {
-+		ix = SHRT2LONG(i);
-+		qdepth[ix] = LONG2DEVPTR(ix)->dev_caller_count;
-+	}
-+
-+	return qdepth;
-+}
-+
-+static inline unsigned int *
-+get_status_perdevice_reqcnt(unsigned int reqcnt[Z90CRYPT_NUM_APS])
-+{
-+	int i, ix;
-+
-+	memset(reqcnt, 0, Z90CRYPT_NUM_APS * sizeof(int));
-+
-+	for (i = 0; i < get_status_totalcount(); i++) {
-+		ix = SHRT2LONG(i);
-+		reqcnt[ix] = LONG2DEVPTR(ix)->dev_total_req_cnt;
-+	}
-+
-+	return reqcnt;
-+}
-+
-+static inline void
-+init_work_element(struct work_element *we_p,
-+		  struct priv_data *priv_data, pid_t pid)
-+{
-+	int step;
-+
-+	we_p->requestptr = (unsigned char *)we_p + sizeof(struct work_element);
-+	/* Come up with a unique id for this caller. */
-+	step = atomic_inc_return(&z90crypt_step);
-+	memcpy(we_p->caller_id+0, (void *) &pid, sizeof(pid));
-+	memcpy(we_p->caller_id+4, (void *) &step, sizeof(step));
-+	we_p->pid = pid;
-+	we_p->priv_data = priv_data;
-+	we_p->status[0] = STAT_DEFAULT;
-+	we_p->audit[0] = 0x00;
-+	we_p->audit[1] = 0x00;
-+	we_p->audit[2] = 0x00;
-+	we_p->resp_buff_size = 0;
-+	we_p->retcode = 0;
-+	we_p->devindex = -1;
-+	we_p->devtype = -1;
-+	atomic_set(&we_p->alarmrung, 0);
-+	init_waitqueue_head(&we_p->waitq);
-+	INIT_LIST_HEAD(&(we_p->liste));
-+}
-+
-+static inline int
-+allocate_work_element(struct work_element **we_pp,
-+		      struct priv_data *priv_data_p, pid_t pid)
-+{
-+	struct work_element *we_p;
-+
-+	we_p = (struct work_element *) get_zeroed_page(GFP_KERNEL);
-+	if (!we_p)
-+		return -ENOMEM;
-+	init_work_element(we_p, priv_data_p, pid);
-+	*we_pp = we_p;
-+	return 0;
-+}
-+
-+static inline void
-+remove_device(struct device *device_p)
-+{
-+	if (!device_p || (device_p->disabled != 0))
-+		return;
-+	device_p->disabled = 1;
-+	z90crypt.hdware_info->type_mask[device_p->dev_type].disabled_count++;
-+	z90crypt.hdware_info->hdware_mask.disabled_count++;
-+}
-+
-+/**
-+ * Bitlength limits for each card
-+ *
-+ * There are new MCLs which allow more bitlengths. See the table for details.
-+ * The MCL must be applied and the newer bitlengths enabled for these to work.
-+ *
-+ * Card Type    Old limit    New limit
-+ * PCICC         512-1024     512-2048
-+ * PCIXCC_MCL2   512-2048     no change (applying this MCL == card is MCL3+)
-+ * PCIXCC_MCL3   512-2048     128-2048
-+ * CEX2C         512-2048     128-2048
-+ *
-+ * ext_bitlens (extended bitlengths) is a global, since you should not apply an
-+ * MCL to just one card in a machine. We assume, at first, that all cards have
-+ * these capabilities.
-+ */
-+int ext_bitlens = 1; // This is global
-+#define PCIXCC_MIN_MOD_SIZE	 16	//  128 bits
-+#define OLD_PCIXCC_MIN_MOD_SIZE	 64	//  512 bits
-+#define PCICC_MIN_MOD_SIZE	 64	//  512 bits
-+#define OLD_PCICC_MAX_MOD_SIZE	128	// 1024 bits
-+#define MAX_MOD_SIZE		256	// 2048 bits
-+
-+static inline int
-+select_device_type(int *dev_type_p, int bytelength)
-+{
-+	static int count = 0;
-+	int PCICA_avail, PCIXCC_MCL3_avail, CEX2C_avail, index_to_use;
-+	struct status *stat;
-+	if ((*dev_type_p != PCICC) && (*dev_type_p != PCICA) &&
-+	    (*dev_type_p != PCIXCC_MCL2) && (*dev_type_p != PCIXCC_MCL3) &&
-+	    (*dev_type_p != CEX2C) && (*dev_type_p != ANYDEV))
-+		return -1;
-+	if (*dev_type_p != ANYDEV) {
-+		stat = &z90crypt.hdware_info->type_mask[*dev_type_p];
-+		if (stat->st_count >
-+		    (stat->disabled_count + stat->user_disabled_count))
-+			return 0;
-+		return -1;
-+	}
-+
-+	/* Assumption: PCICA, PCIXCC_MCL3, and CEX2C are all similar in speed */
-+	stat = &z90crypt.hdware_info->type_mask[PCICA];
-+	PCICA_avail = stat->st_count -
-+			(stat->disabled_count + stat->user_disabled_count);
-+	stat = &z90crypt.hdware_info->type_mask[PCIXCC_MCL3];
-+	PCIXCC_MCL3_avail = stat->st_count -
-+			(stat->disabled_count + stat->user_disabled_count);
-+	stat = &z90crypt.hdware_info->type_mask[CEX2C];
-+	CEX2C_avail = stat->st_count -
-+			(stat->disabled_count + stat->user_disabled_count);
-+	if (PCICA_avail || PCIXCC_MCL3_avail || CEX2C_avail) {
-+		/**
-+		 * bitlength is a factor, PCICA is the most capable, even with
-+		 * the new MCL.
-+		 */
-+		if ((bytelength < PCIXCC_MIN_MOD_SIZE) ||
-+		    (!ext_bitlens && (bytelength < OLD_PCIXCC_MIN_MOD_SIZE))) {
-+			if (!PCICA_avail)
-+				return -1;
-+			else {
-+				*dev_type_p = PCICA;
-+				return 0;
-+			}
-+		}
-+
-+		index_to_use = count % (PCICA_avail + PCIXCC_MCL3_avail +
-+					CEX2C_avail);
-+		if (index_to_use < PCICA_avail)
-+			*dev_type_p = PCICA;
-+		else if (index_to_use < (PCICA_avail + PCIXCC_MCL3_avail))
-+			*dev_type_p = PCIXCC_MCL3;
-+		else
-+			*dev_type_p = CEX2C;
-+		count++;
-+		return 0;
-+	}
-+
-+	/* Less than OLD_PCIXCC_MIN_MOD_SIZE cannot go to a PCIXCC_MCL2 */
-+	if (bytelength < OLD_PCIXCC_MIN_MOD_SIZE)
-+		return -1;
-+	stat = &z90crypt.hdware_info->type_mask[PCIXCC_MCL2];
-+	if (stat->st_count >
-+	    (stat->disabled_count + stat->user_disabled_count)) {
-+		*dev_type_p = PCIXCC_MCL2;
-+		return 0;
-+	}
-+
-+	/**
-+	 * Less than PCICC_MIN_MOD_SIZE or more than OLD_PCICC_MAX_MOD_SIZE
-+	 * (if we don't have the MCL applied and the newer bitlengths enabled)
-+	 * cannot go to a PCICC
-+	 */
-+	if ((bytelength < PCICC_MIN_MOD_SIZE) ||
-+	    (!ext_bitlens && (bytelength > OLD_PCICC_MAX_MOD_SIZE))) {
-+		return -1;
-+	}
-+	stat = &z90crypt.hdware_info->type_mask[PCICC];
-+	if (stat->st_count >
-+	    (stat->disabled_count + stat->user_disabled_count)) {
-+		*dev_type_p = PCICC;
-+		return 0;
-+	}
-+
-+	return -1;
-+}
-+
-+/**
-+ * Try the selected number, then the selected type (can be ANYDEV)
-+ */
-+static inline int
-+select_device(int *dev_type_p, int *device_nr_p, int bytelength)
-+{
-+	int i, indx, devTp, low_count, low_indx;
-+	struct device_x *index_p;
-+	struct device *dev_ptr;
-+
-+	PDEBUG("device type = %d, index = %d\n", *dev_type_p, *device_nr_p);
-+	if ((*device_nr_p >= 0) && (*device_nr_p < Z90CRYPT_NUM_DEVS)) {
-+		PDEBUG("trying index = %d\n", *device_nr_p);
-+		dev_ptr = z90crypt.device_p[*device_nr_p];
-+
-+		if (dev_ptr &&
-+		    (dev_ptr->dev_stat != DEV_GONE) &&
-+		    (dev_ptr->disabled == 0) &&
-+		    (dev_ptr->user_disabled == 0)) {
-+			PDEBUG("selected by number, index = %d\n",
-+			       *device_nr_p);
-+			*dev_type_p = dev_ptr->dev_type;
-+			return *device_nr_p;
-+		}
-+	}
-+	*device_nr_p = -1;
-+	PDEBUG("trying type = %d\n", *dev_type_p);
-+	devTp = *dev_type_p;
-+	if (select_device_type(&devTp, bytelength) == -1) {
-+		PDEBUG("failed to select by type\n");
-+		return -1;
-+	}
-+	PDEBUG("selected type = %d\n", devTp);
-+	index_p = &z90crypt.hdware_info->type_x_addr[devTp];
-+	low_count = 0x0000FFFF;
-+	low_indx = -1;
-+	for (i = 0; i < z90crypt.hdware_info->type_mask[devTp].st_count; i++) {
-+		indx = index_p->device_index[i];
-+		dev_ptr = z90crypt.device_p[indx];
-+		if (dev_ptr &&
-+		    (dev_ptr->dev_stat != DEV_GONE) &&
-+		    (dev_ptr->disabled == 0) &&
-+		    (dev_ptr->user_disabled == 0) &&
-+		    (devTp == dev_ptr->dev_type) &&
-+		    (low_count > dev_ptr->dev_caller_count)) {
-+			low_count = dev_ptr->dev_caller_count;
-+			low_indx = indx;
-+		}
-+	}
-+	*device_nr_p = low_indx;
-+	return low_indx;
-+}
-+
-+static inline int
-+send_to_crypto_device(struct work_element *we_p)
-+{
-+	struct caller *caller_p;
-+	struct device *device_p;
-+	int dev_nr;
-+	int bytelen = ((struct ica_rsa_modexpo *)we_p->buffer)->inputdatalength;
-+
-+	if (!we_p->requestptr)
-+		return SEN_FATAL_ERROR;
-+	caller_p = (struct caller *)we_p->requestptr;
-+	dev_nr = we_p->devindex;
-+	if (select_device(&we_p->devtype, &dev_nr, bytelen) == -1) {
-+		if (z90crypt.hdware_info->hdware_mask.st_count != 0)
-+			return SEN_RETRY;
-+		else
-+			return SEN_NOT_AVAIL;
-+	}
-+	we_p->devindex = dev_nr;
-+	device_p = z90crypt.device_p[dev_nr];
-+	if (!device_p)
-+		return SEN_NOT_AVAIL;
-+	if (device_p->dev_type != we_p->devtype)
-+		return SEN_RETRY;
-+	if (device_p->dev_caller_count >= device_p->dev_q_depth)
-+		return SEN_QUEUE_FULL;
-+	PDEBUG("device number prior to send: %d\n", dev_nr);
-+	switch (send_to_AP(dev_nr, z90crypt.cdx,
-+			   caller_p->caller_dev_dep_req_l,
-+			   caller_p->caller_dev_dep_req_p)) {
-+	case DEV_SEN_EXCEPTION:
-+		PRINTKC("Exception during send to device %d\n", dev_nr);
-+		z90crypt.terminating = 1;
-+		return SEN_FATAL_ERROR;
-+	case DEV_GONE:
-+		PRINTK("Device %d not available\n", dev_nr);
-+		remove_device(device_p);
-+		return SEN_NOT_AVAIL;
-+	case DEV_EMPTY:
-+		return SEN_NOT_AVAIL;
-+	case DEV_NO_WORK:
-+		return SEN_FATAL_ERROR;
-+	case DEV_BAD_MESSAGE:
-+		return SEN_USER_ERROR;
-+	case DEV_QUEUE_FULL:
-+		return SEN_QUEUE_FULL;
-+	default:
-+	case DEV_ONLINE:
-+		break;
-+	}
-+	list_add_tail(&(caller_p->caller_liste), &(device_p->dev_caller_list));
-+	device_p->dev_caller_count++;
-+	return 0;
-+}
-+
-+/**
-+ * Send puts the user's work on one of two queues:
-+ *   the pending queue if the send was successful
-+ *   the request queue if the send failed because device full or busy
-+ */
-+static inline int
-+z90crypt_send(struct work_element *we_p, const char *buf)
-+{
-+	int rv;
-+
-+	PDEBUG("PID %d\n", PID());
-+
-+	if (CHK_RDWRMASK(we_p->status[0]) != STAT_NOWORK) {
-+		PDEBUG("PID %d tried to send more work but has outstanding "
-+		       "work.\n", PID());
-+		return -EWORKPEND;
-+	}
-+	we_p->devindex = -1; // Reset device number
-+	spin_lock_irq(&queuespinlock);
-+	rv = send_to_crypto_device(we_p);
-+	switch (rv) {
-+	case 0:
-+		we_p->requestsent = jiffies;
-+		we_p->audit[0] |= FP_SENT;
-+		list_add_tail(&we_p->liste, &pending_list);
-+		++pendingq_count;
-+		we_p->audit[0] |= FP_PENDING;
-+		break;
-+	case SEN_BUSY:
-+	case SEN_QUEUE_FULL:
-+		rv = 0;
-+		we_p->devindex = -1; // any device will do
-+		we_p->requestsent = jiffies;
-+		list_add_tail(&we_p->liste, &request_list);
-+		++requestq_count;
-+		we_p->audit[0] |= FP_REQUEST;
-+		break;
-+	case SEN_RETRY:
-+		rv = -ERESTARTSYS;
-+		break;
-+	case SEN_NOT_AVAIL:
-+		PRINTK("*** No devices available.\n");
-+		rv = we_p->retcode = -ENODEV;
-+		we_p->status[0] |= STAT_FAILED;
-+		break;
-+	case REC_OPERAND_INV:
-+	case REC_OPERAND_SIZE:
-+	case REC_EVEN_MOD:
-+	case REC_INVALID_PAD:
-+		rv = we_p->retcode = -EINVAL;
-+		we_p->status[0] |= STAT_FAILED;
-+		break;
-+	default:
-+		we_p->retcode = rv;
-+		we_p->status[0] |= STAT_FAILED;
-+		break;
-+	}
-+	if (rv != -ERESTARTSYS)
-+		SET_RDWRMASK(we_p->status[0], STAT_WRITTEN);
-+	spin_unlock_irq(&queuespinlock);
-+	if (rv == 0)
-+		tasklet_schedule(&reader_tasklet);
-+	return rv;
-+}
-+
-+/**
-+ * process_results copies the user's work from kernel space.
-+ */
-+static inline int
-+z90crypt_process_results(struct work_element *we_p, char __user *buf)
-+{
-+	int rv;
-+
-+	PDEBUG("we_p %p (PID %d)\n", we_p, PID());
-+
-+	LONG2DEVPTR(we_p->devindex)->dev_total_req_cnt++;
-+	SET_RDWRMASK(we_p->status[0], STAT_READPEND);
-+
-+	rv = 0;
-+	if (!we_p->buffer) {
-+		PRINTK("we_p %p PID %d in STAT_READPEND: buffer NULL.\n",
-+			we_p, PID());
-+		rv = -ENOBUFF;
-+	}
-+
-+	if (!rv)
-+		if ((rv = copy_to_user(buf, we_p->buffer, we_p->buff_size))) {
-+			PDEBUG("copy_to_user failed: rv = %d\n", rv);
-+			rv = -EFAULT;
-+		}
-+
-+	if (!rv)
-+		rv = we_p->retcode;
-+	if (!rv)
-+		if (we_p->resp_buff_size
-+		    &&	copy_to_user(we_p->resp_addr, we_p->resp_buff,
-+				     we_p->resp_buff_size))
-+			rv = -EFAULT;
-+
-+	SET_RDWRMASK(we_p->status[0], STAT_NOWORK);
-+	return rv;
-+}
-+
-+static unsigned char NULL_psmid[8] =
-+{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-+
-+/**
-+ * Used in device configuration functions
-+ */
-+#define MAX_RESET 90
-+
-+/**
-+ * This is used only for PCICC support
-+ */
-+static inline int
-+is_PKCS11_padded(unsigned char *buffer, int length)
-+{
-+	int i;
-+	if ((buffer[0] != 0x00) || (buffer[1] != 0x01))
-+		return 0;
-+	for (i = 2; i < length; i++)
-+		if (buffer[i] != 0xFF)
-+			break;
-+	if ((i < 10) || (i == length))
-+		return 0;
-+	if (buffer[i] != 0x00)
-+		return 0;
-+	return 1;
-+}
-+
-+/**
-+ * This is used only for PCICC support
-+ */
-+static inline int
-+is_PKCS12_padded(unsigned char *buffer, int length)
-+{
-+	int i;
-+	if ((buffer[0] != 0x00) || (buffer[1] != 0x02))
-+		return 0;
-+	for (i = 2; i < length; i++)
-+		if (buffer[i] == 0x00)
-+			break;
-+	if ((i < 10) || (i == length))
-+		return 0;
-+	if (buffer[i] != 0x00)
-+		return 0;
-+	return 1;
-+}
-+
-+/**
-+ * builds struct caller and converts message from generic format to
-+ * device-dependent format
-+ * func is ICARSAMODEXPO or ICARSACRT
-+ * function is PCI_FUNC_KEY_ENCRYPT or PCI_FUNC_KEY_DECRYPT
-+ */
-+static inline int
-+build_caller(struct work_element *we_p, short function)
-+{
-+	int rv;
-+	struct caller *caller_p = (struct caller *)we_p->requestptr;
-+
-+	if ((we_p->devtype != PCICC) && (we_p->devtype != PCICA) &&
-+	    (we_p->devtype != PCIXCC_MCL2) && (we_p->devtype != PCIXCC_MCL3) &&
-+	    (we_p->devtype != CEX2C))
-+		return SEN_NOT_AVAIL;
-+
-+	memcpy(caller_p->caller_id, we_p->caller_id,
-+	       sizeof(caller_p->caller_id));
-+	caller_p->caller_dev_dep_req_p = caller_p->caller_dev_dep_req;
-+	caller_p->caller_dev_dep_req_l = MAX_RESPONSE_SIZE;
-+	caller_p->caller_buf_p = we_p->buffer;
-+	INIT_LIST_HEAD(&(caller_p->caller_liste));
-+
-+	rv = convert_request(we_p->buffer, we_p->funccode, function,
-+			     z90crypt.cdx, we_p->devtype,
-+			     &caller_p->caller_dev_dep_req_l,
-+			     caller_p->caller_dev_dep_req_p);
-+	if (rv) {
-+		if (rv == SEN_NOT_AVAIL)
-+			PDEBUG("request can't be processed on hdwr avail\n");
-+		else
-+			PRINTK("Error from convert_request: %d\n", rv);
-+	}
-+	else
-+		memcpy(&(caller_p->caller_dev_dep_req_p[4]), we_p->caller_id,8);
-+	return rv;
-+}
-+
-+static inline void
-+unbuild_caller(struct device *device_p, struct caller *caller_p)
-+{
-+	if (!caller_p)
-+		return;
-+	if (caller_p->caller_liste.next && caller_p->caller_liste.prev)
-+		if (!list_empty(&caller_p->caller_liste)) {
-+			list_del_init(&caller_p->caller_liste);
-+			device_p->dev_caller_count--;
-+		}
-+	memset(caller_p->caller_id, 0, sizeof(caller_p->caller_id));
-+}
-+
-+static inline int
-+get_crypto_request_buffer(struct work_element *we_p)
-+{
-+	struct ica_rsa_modexpo *mex_p;
-+	struct ica_rsa_modexpo_crt *crt_p;
-+	unsigned char *temp_buffer;
-+	short function;
-+	int rv;
-+
-+	mex_p =	(struct ica_rsa_modexpo *) we_p->buffer;
-+	crt_p = (struct ica_rsa_modexpo_crt *) we_p->buffer;
-+
-+	PDEBUG("device type input = %d\n", we_p->devtype);
-+
-+	if (z90crypt.terminating)
-+		return REC_NO_RESPONSE;
-+	if (memcmp(we_p->caller_id, NULL_psmid, 8) == 0) {
-+		PRINTK("psmid zeroes\n");
-+		return SEN_FATAL_ERROR;
-+	}
-+	if (!we_p->buffer) {
-+		PRINTK("buffer pointer NULL\n");
-+		return SEN_USER_ERROR;
-+	}
-+	if (!we_p->requestptr) {
-+		PRINTK("caller pointer NULL\n");
-+		return SEN_USER_ERROR;
-+	}
-+
-+	if ((we_p->devtype != PCICA) && (we_p->devtype != PCICC) &&
-+	    (we_p->devtype != PCIXCC_MCL2) && (we_p->devtype != PCIXCC_MCL3) &&
-+	    (we_p->devtype != CEX2C) && (we_p->devtype != ANYDEV)) {
-+		PRINTK("invalid device type\n");
-+		return SEN_USER_ERROR;
-+	}
-+
-+	if ((mex_p->inputdatalength < 1) ||
-+	    (mex_p->inputdatalength > MAX_MOD_SIZE)) {
-+		PRINTK("inputdatalength[%d] is not valid\n",
-+		       mex_p->inputdatalength);
-+		return SEN_USER_ERROR;
-+	}
-+
-+	if (mex_p->outputdatalength < mex_p->inputdatalength) {
-+		PRINTK("outputdatalength[%d] < inputdatalength[%d]\n",
-+		       mex_p->outputdatalength, mex_p->inputdatalength);
-+		return SEN_USER_ERROR;
-+	}
-+
-+	if (!mex_p->inputdata || !mex_p->outputdata) {
-+		PRINTK("inputdata[%p] or outputdata[%p] is NULL\n",
-+		       mex_p->outputdata, mex_p->inputdata);
-+		return SEN_USER_ERROR;
-+	}
-+
-+	/**
-+	 * As long as outputdatalength is big enough, we can set the
-+	 * outputdatalength equal to the inputdatalength, since that is the
-+	 * number of bytes we will copy in any case
-+	 */
-+	mex_p->outputdatalength = mex_p->inputdatalength;
-+
-+	rv = 0;
-+	switch (we_p->funccode) {
-+	case ICARSAMODEXPO:
-+		if (!mex_p->b_key || !mex_p->n_modulus)
-+			rv = SEN_USER_ERROR;
-+		break;
-+	case ICARSACRT:
-+		if (!IS_EVEN(crt_p->inputdatalength)) {
-+			PRINTK("inputdatalength[%d] is odd, CRT form\n",
-+			       crt_p->inputdatalength);
-+			rv = SEN_USER_ERROR;
-+			break;
-+		}
-+		if (!crt_p->bp_key ||
-+		    !crt_p->bq_key ||
-+		    !crt_p->np_prime ||
-+		    !crt_p->nq_prime ||
-+		    !crt_p->u_mult_inv) {
-+			PRINTK("CRT form, bad data: %p/%p/%p/%p/%p\n",
-+			       crt_p->bp_key, crt_p->bq_key,
-+			       crt_p->np_prime, crt_p->nq_prime,
-+			       crt_p->u_mult_inv);
-+			rv = SEN_USER_ERROR;
-+		}
-+		break;
-+	default:
-+		PRINTK("bad func = %d\n", we_p->funccode);
-+		rv = SEN_USER_ERROR;
-+		break;
-+	}
-+	if (rv != 0)
-+		return rv;
-+
-+	if (select_device_type(&we_p->devtype, mex_p->inputdatalength) < 0)
-+		return SEN_NOT_AVAIL;
-+
-+	temp_buffer = (unsigned char *)we_p + sizeof(struct work_element) +
-+		      sizeof(struct caller);
-+	if (copy_from_user(temp_buffer, mex_p->inputdata,
-+			   mex_p->inputdatalength) != 0)
-+		return SEN_RELEASED;
-+
-+	function = PCI_FUNC_KEY_ENCRYPT;
-+	switch (we_p->devtype) {
-+	/* PCICA does everything with a simple RSA mod-expo operation */
-+	case PCICA:
-+		function = PCI_FUNC_KEY_ENCRYPT;
-+		break;
-+	/**
-+	 * PCIXCC_MCL2 does all Mod-Expo form with a simple RSA mod-expo
-+	 * operation, and all CRT forms with a PKCS-1.2 format decrypt.
-+	 * PCIXCC_MCL3 and CEX2C do all Mod-Expo and CRT forms with a simple RSA
-+	 * mod-expo operation
-+	 */
-+	case PCIXCC_MCL2:
-+		if (we_p->funccode == ICARSAMODEXPO)
-+			function = PCI_FUNC_KEY_ENCRYPT;
-+		else
-+			function = PCI_FUNC_KEY_DECRYPT;
-+		break;
-+	case PCIXCC_MCL3:
-+	case CEX2C:
-+		if (we_p->funccode == ICARSAMODEXPO)
-+			function = PCI_FUNC_KEY_ENCRYPT;
-+		else
-+			function = PCI_FUNC_KEY_DECRYPT;
-+		break;
-+	/**
-+	 * PCICC does everything as a PKCS-1.2 format request
-+	 */
-+	case PCICC:
-+		/* PCICC cannot handle input that is is PKCS#1.1 padded */
-+		if (is_PKCS11_padded(temp_buffer, mex_p->inputdatalength)) {
-+			return SEN_NOT_AVAIL;
-+		}
-+		if (we_p->funccode == ICARSAMODEXPO) {
-+			if (is_PKCS12_padded(temp_buffer,
-+					     mex_p->inputdatalength))
-+				function = PCI_FUNC_KEY_ENCRYPT;
-+			else
-+				function = PCI_FUNC_KEY_DECRYPT;
-+		} else
-+			/* all CRT forms are decrypts */
-+			function = PCI_FUNC_KEY_DECRYPT;
-+		break;
-+	}
-+	PDEBUG("function: %04x\n", function);
-+	rv = build_caller(we_p, function);
-+	PDEBUG("rv from build_caller = %d\n", rv);
-+	return rv;
-+}
-+
-+static inline int
-+z90crypt_prepare(struct work_element *we_p, unsigned int funccode,
-+		 const char __user *buffer)
-+{
-+	int rv;
-+
-+	we_p->devindex = -1;
-+	if (funccode == ICARSAMODEXPO)
-+		we_p->buff_size = sizeof(struct ica_rsa_modexpo);
-+	else
-+		we_p->buff_size = sizeof(struct ica_rsa_modexpo_crt);
-+
-+	if (copy_from_user(we_p->buffer, buffer, we_p->buff_size))
-+		return -EFAULT;
-+
-+	we_p->audit[0] |= FP_COPYFROM;
-+	SET_RDWRMASK(we_p->status[0], STAT_WRITTEN);
-+	we_p->funccode = funccode;
-+	we_p->devtype = -1;
-+	we_p->audit[0] |= FP_BUFFREQ;
-+	rv = get_crypto_request_buffer(we_p);
-+	switch (rv) {
-+	case 0:
-+		we_p->audit[0] |= FP_BUFFGOT;
-+		break;
-+	case SEN_USER_ERROR:
-+		rv = -EINVAL;
-+		break;
-+	case SEN_QUEUE_FULL:
-+		rv = 0;
-+		break;
-+	case SEN_RELEASED:
-+		rv = -EFAULT;
-+		break;
-+	case REC_NO_RESPONSE:
-+		rv = -ENODEV;
-+		break;
-+	case SEN_NOT_AVAIL:
-+		rv = -EGETBUFF;
-+		break;
-+	default:
-+		PRINTK("rv = %d\n", rv);
-+		rv = -EGETBUFF;
-+		break;
-+	}
-+	if (CHK_RDWRMASK(we_p->status[0]) == STAT_WRITTEN)
-+		SET_RDWRMASK(we_p->status[0], STAT_DEFAULT);
-+	return rv;
-+}
-+
-+static inline void
-+purge_work_element(struct work_element *we_p)
-+{
-+	struct list_head *lptr;
-+
-+	spin_lock_irq(&queuespinlock);
-+	list_for_each(lptr, &request_list) {
-+		if (lptr == &we_p->liste) {
-+			list_del_init(lptr);
-+			requestq_count--;
-+			break;
-+		}
-+	}
-+	list_for_each(lptr, &pending_list) {
-+		if (lptr == &we_p->liste) {
-+			list_del_init(lptr);
-+			pendingq_count--;
-+			break;
-+		}
-+	}
-+	spin_unlock_irq(&queuespinlock);
-+}
-+
-+/**
-+ * Build the request and send it.
-+ */
-+static inline int
-+z90crypt_rsa(struct priv_data *private_data_p, pid_t pid,
-+	     unsigned int cmd, unsigned long arg)
-+{
-+	struct work_element *we_p;
-+	int rv;
-+
-+	if ((rv = allocate_work_element(&we_p, private_data_p, pid))) {
-+		PDEBUG("PID %d: allocate_work_element returned ENOMEM\n", pid);
-+		return rv;
-+	}
-+	if ((rv = z90crypt_prepare(we_p, cmd, (const char __user *)arg)))
-+		PDEBUG("PID %d: rv = %d from z90crypt_prepare\n", pid, rv);
-+	if (!rv)
-+		if ((rv = z90crypt_send(we_p, (const char *)arg)))
-+			PDEBUG("PID %d: rv %d from z90crypt_send.\n", pid, rv);
-+	if (!rv) {
-+		we_p->audit[0] |= FP_ASLEEP;
-+		wait_event(we_p->waitq, atomic_read(&we_p->alarmrung));
-+		we_p->audit[0] |= FP_AWAKE;
-+		rv = we_p->retcode;
-+	}
-+	if (!rv)
-+		rv = z90crypt_process_results(we_p, (char __user *)arg);
-+
-+	if ((we_p->status[0] & STAT_FAILED)) {
-+		switch (rv) {
-+		/**
-+		 * EINVAL *after* receive is almost always a padding error or
-+		 * length error issued by a coprocessor (not an accelerator).
-+		 * We convert this return value to -EGETBUFF which should
-+		 * trigger a fallback to software.
-+		 */
-+		case -EINVAL:
-+			if (we_p->devtype != PCICA)
-+				rv = -EGETBUFF;
-+			break;
-+		case -ETIMEOUT:
-+			if (z90crypt.mask.st_count > 0)
-+				rv = -ERESTARTSYS; // retry with another
-+			else
-+				rv = -ENODEV; // no cards left
-+		/* fall through to clean up request queue */
-+		case -ERESTARTSYS:
-+		case -ERELEASED:
-+			switch (CHK_RDWRMASK(we_p->status[0])) {
-+			case STAT_WRITTEN:
-+				purge_work_element(we_p);
-+				break;
-+			case STAT_READPEND:
-+			case STAT_NOWORK:
-+			default:
-+				break;
-+			}
-+			break;
-+		default:
-+			we_p->status[0] ^= STAT_FAILED;
-+			break;
-+		}
-+	}
-+	free_page((long)we_p);
-+	return rv;
-+}
-+
-+/**
-+ * This function is a little long, but it's really just one large switch
-+ * statement.
-+ */
-+static int
-+z90crypt_ioctl(struct inode *inode, struct file *filp,
-+	       unsigned int cmd, unsigned long arg)
-+{
-+	struct priv_data *private_data_p = filp->private_data;
-+	unsigned char *status;
-+	unsigned char *qdepth;
-+	unsigned int *reqcnt;
-+	struct ica_z90_status *pstat;
-+	int ret, i, loopLim, tempstat;
-+	static int deprecated_msg_count1 = 0;
-+	static int deprecated_msg_count2 = 0;
-+
-+	PDEBUG("filp %p (PID %d), cmd 0x%08X\n", filp, PID(), cmd);
-+	PDEBUG("cmd 0x%08X: dir %s, size 0x%04X, type 0x%02X, nr 0x%02X\n",
-+		cmd,
-+		!_IOC_DIR(cmd) ? "NO"
-+		: ((_IOC_DIR(cmd) == (_IOC_READ|_IOC_WRITE)) ? "RW"
-+		: ((_IOC_DIR(cmd) == _IOC_READ) ? "RD"
-+		: "WR")),
-+		_IOC_SIZE(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd));
-+
-+	if (_IOC_TYPE(cmd) != Z90_IOCTL_MAGIC) {
-+		PRINTK("cmd 0x%08X contains bad magic\n", cmd);
-+		return -ENOTTY;
-+	}
-+
-+	ret = 0;
-+	switch (cmd) {
-+	case ICARSAMODEXPO:
-+	case ICARSACRT:
-+		if (quiesce_z90crypt) {
-+			ret = -EQUIESCE;
-+			break;
-+		}
-+		ret = -ENODEV; // Default if no devices
-+		loopLim = z90crypt.hdware_info->hdware_mask.st_count -
-+			(z90crypt.hdware_info->hdware_mask.disabled_count +
-+			 z90crypt.hdware_info->hdware_mask.user_disabled_count);
-+		for (i = 0; i < loopLim; i++) {
-+			ret = z90crypt_rsa(private_data_p, PID(), cmd, arg);
-+			if (ret != -ERESTARTSYS)
-+				break;
-+		}
-+		if (ret == -ERESTARTSYS)
-+			ret = -ENODEV;
-+		break;
-+
-+	case Z90STAT_TOTALCOUNT:
-+		tempstat = get_status_totalcount();
-+		if (copy_to_user((int __user *)arg, &tempstat,sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90STAT_PCICACOUNT:
-+		tempstat = get_status_PCICAcount();
-+		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90STAT_PCICCCOUNT:
-+		tempstat = get_status_PCICCcount();
-+		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90STAT_PCIXCCMCL2COUNT:
-+		tempstat = get_status_PCIXCCMCL2count();
-+		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90STAT_PCIXCCMCL3COUNT:
-+		tempstat = get_status_PCIXCCMCL3count();
-+		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90STAT_CEX2CCOUNT:
-+		tempstat = get_status_CEX2Ccount();
-+		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90STAT_REQUESTQ_COUNT:
-+		tempstat = get_status_requestq_count();
-+		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90STAT_PENDINGQ_COUNT:
-+		tempstat = get_status_pendingq_count();
-+		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90STAT_TOTALOPEN_COUNT:
-+		tempstat = get_status_totalopen_count();
-+		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90STAT_DOMAIN_INDEX:
-+		tempstat = get_status_domain_index();
-+		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90STAT_STATUS_MASK:
-+		status = kmalloc(Z90CRYPT_NUM_APS, GFP_KERNEL);
-+		if (!status) {
-+			PRINTK("kmalloc for status failed!\n");
-+			ret = -ENOMEM;
-+			break;
-+		}
-+		get_status_status_mask(status);
-+		if (copy_to_user((char __user *) arg, status, Z90CRYPT_NUM_APS)
-+									!= 0)
-+			ret = -EFAULT;
-+		kfree(status);
-+		break;
-+
-+	case Z90STAT_QDEPTH_MASK:
-+		qdepth = kmalloc(Z90CRYPT_NUM_APS, GFP_KERNEL);
-+		if (!qdepth) {
-+			PRINTK("kmalloc for qdepth failed!\n");
-+			ret = -ENOMEM;
-+			break;
-+		}
-+		get_status_qdepth_mask(qdepth);
-+		if (copy_to_user((char __user *) arg, qdepth, Z90CRYPT_NUM_APS) != 0)
-+			ret = -EFAULT;
-+		kfree(qdepth);
-+		break;
-+
-+	case Z90STAT_PERDEV_REQCNT:
-+		reqcnt = kmalloc(sizeof(int) * Z90CRYPT_NUM_APS, GFP_KERNEL);
-+		if (!reqcnt) {
-+			PRINTK("kmalloc for reqcnt failed!\n");
-+			ret = -ENOMEM;
-+			break;
-+		}
-+		get_status_perdevice_reqcnt(reqcnt);
-+		if (copy_to_user((char __user *) arg, reqcnt,
-+				 Z90CRYPT_NUM_APS * sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		kfree(reqcnt);
-+		break;
-+
-+		/* THIS IS DEPRECATED.	USE THE NEW STATUS CALLS */
-+	case ICAZ90STATUS:
-+		if (deprecated_msg_count1 < 20) {
-+			PRINTK("deprecated call to ioctl (ICAZ90STATUS)!\n");
-+			deprecated_msg_count1++;
-+			if (deprecated_msg_count1 == 20)
-+				PRINTK("No longer issuing messages related to "
-+				       "deprecated call to ICAZ90STATUS.\n");
-+		}
-+
-+		pstat = kmalloc(sizeof(struct ica_z90_status), GFP_KERNEL);
-+		if (!pstat) {
-+			PRINTK("kmalloc for pstat failed!\n");
-+			ret = -ENOMEM;
-+			break;
-+		}
-+
-+		pstat->totalcount	 = get_status_totalcount();
-+		pstat->leedslitecount	 = get_status_PCICAcount();
-+		pstat->leeds2count	 = get_status_PCICCcount();
-+		pstat->requestqWaitCount = get_status_requestq_count();
-+		pstat->pendingqWaitCount = get_status_pendingq_count();
-+		pstat->totalOpenCount	 = get_status_totalopen_count();
-+		pstat->cryptoDomain	 = get_status_domain_index();
-+		get_status_status_mask(pstat->status);
-+		get_status_qdepth_mask(pstat->qdepth);
-+
-+		if (copy_to_user((struct ica_z90_status __user *) arg, pstat,
-+				 sizeof(struct ica_z90_status)) != 0)
-+			ret = -EFAULT;
-+		kfree(pstat);
-+		break;
-+
-+		/* THIS IS DEPRECATED.	USE THE NEW STATUS CALLS */
-+	case Z90STAT_PCIXCCCOUNT:
-+		if (deprecated_msg_count2 < 20) {
-+			PRINTK("deprecated ioctl (Z90STAT_PCIXCCCOUNT)!\n");
-+			deprecated_msg_count2++;
-+			if (deprecated_msg_count2 == 20)
-+				PRINTK("No longer issuing messages about depre"
-+				       "cated ioctl Z90STAT_PCIXCCCOUNT.\n");
-+		}
-+
-+		tempstat = get_status_PCIXCCcount();
-+		if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
-+			ret = -EFAULT;
-+		break;
-+
-+	case Z90QUIESCE:
-+		if (current->euid != 0) {
-+			PRINTK("QUIESCE fails: euid %d\n",
-+			       current->euid);
-+			ret = -EACCES;
-+		} else {
-+			PRINTK("QUIESCE device from PID %d\n", PID());
-+			quiesce_z90crypt = 1;
-+		}
-+		break;
-+
-+	default:
-+		/* user passed an invalid IOCTL number */
-+		PDEBUG("cmd 0x%08X contains invalid ioctl code\n", cmd);
-+		ret = -ENOTTY;
-+		break;
-+	}
-+
-+	return ret;
-+}
-+
-+static inline int
-+sprintcl(unsigned char *outaddr, unsigned char *addr, unsigned int len)
-+{
-+	int hl, i;
-+
-+	hl = 0;
-+	for (i = 0; i < len; i++)
-+		hl += sprintf(outaddr+hl, "%01x", (unsigned int) addr[i]);
-+	hl += sprintf(outaddr+hl, " ");
-+
-+	return hl;
-+}
-+
-+static inline int
-+sprintrw(unsigned char *outaddr, unsigned char *addr, unsigned int len)
-+{
-+	int hl, inl, c, cx;
-+
-+	hl = sprintf(outaddr, "	   ");
-+	inl = 0;
-+	for (c = 0; c < (len / 16); c++) {
-+		hl += sprintcl(outaddr+hl, addr+inl, 16);
-+		inl += 16;
-+	}
-+
-+	cx = len%16;
-+	if (cx) {
-+		hl += sprintcl(outaddr+hl, addr+inl, cx);
-+		inl += cx;
-+	}
-+
-+	hl += sprintf(outaddr+hl, "\n");
-+
-+	return hl;
-+}
-+
-+static inline int
-+sprinthx(unsigned char *title, unsigned char *outaddr,
-+	 unsigned char *addr, unsigned int len)
-+{
-+	int hl, inl, r, rx;
-+
-+	hl = sprintf(outaddr, "\n%s\n", title);
-+	inl = 0;
-+	for (r = 0; r < (len / 64); r++) {
-+		hl += sprintrw(outaddr+hl, addr+inl, 64);
-+		inl += 64;
-+	}
-+	rx = len % 64;
-+	if (rx) {
-+		hl += sprintrw(outaddr+hl, addr+inl, rx);
-+		inl += rx;
-+	}
-+
-+	hl += sprintf(outaddr+hl, "\n");
-+
-+	return hl;
-+}
-+
-+static inline int
-+sprinthx4(unsigned char *title, unsigned char *outaddr,
-+	  unsigned int *array, unsigned int len)
-+{
-+	int hl, r;
-+
-+	hl = sprintf(outaddr, "\n%s\n", title);
-+
-+	for (r = 0; r < len; r++) {
-+		if ((r % 8) == 0)
-+			hl += sprintf(outaddr+hl, "    ");
-+		hl += sprintf(outaddr+hl, "%08X ", array[r]);
-+		if ((r % 8) == 7)
-+			hl += sprintf(outaddr+hl, "\n");
-+	}
-+
-+	hl += sprintf(outaddr+hl, "\n");
-+
-+	return hl;
-+}
-+
-+static int
-+z90crypt_status(char *resp_buff, char **start, off_t offset,
-+		int count, int *eof, void *data)
-+{
-+	unsigned char *workarea;
-+	int len;
-+
-+	/* resp_buff is a page. Use the right half for a work area */
-+	workarea = resp_buff+2000;
-+	len = 0;
-+	len += sprintf(resp_buff+len, "\nz90crypt version: %d.%d.%d\n",
-+		z90crypt_VERSION, z90crypt_RELEASE, z90crypt_VARIANT);
-+	len += sprintf(resp_buff+len, "Cryptographic domain: %d\n",
-+		get_status_domain_index());
-+	len += sprintf(resp_buff+len, "Total device count: %d\n",
-+		get_status_totalcount());
-+	len += sprintf(resp_buff+len, "PCICA count: %d\n",
-+		get_status_PCICAcount());
-+	len += sprintf(resp_buff+len, "PCICC count: %d\n",
-+		get_status_PCICCcount());
-+	len += sprintf(resp_buff+len, "PCIXCC MCL2 count: %d\n",
-+		get_status_PCIXCCMCL2count());
-+	len += sprintf(resp_buff+len, "PCIXCC MCL3 count: %d\n",
-+		get_status_PCIXCCMCL3count());
-+	len += sprintf(resp_buff+len, "CEX2C count: %d\n",
-+		get_status_CEX2Ccount());
-+	len += sprintf(resp_buff+len, "requestq count: %d\n",
-+		get_status_requestq_count());
-+	len += sprintf(resp_buff+len, "pendingq count: %d\n",
-+		get_status_pendingq_count());
-+	len += sprintf(resp_buff+len, "Total open handles: %d\n\n",
-+		get_status_totalopen_count());
-+	len += sprinthx(
-+		"Online devices: 1: PCICA, 2: PCICC, 3: PCIXCC (MCL2), "
-+		"4: PCIXCC (MCL3), 5: CEX2C",
-+		resp_buff+len,
-+		get_status_status_mask(workarea),
-+		Z90CRYPT_NUM_APS);
-+	len += sprinthx("Waiting work element counts",
-+		resp_buff+len,
-+		get_status_qdepth_mask(workarea),
-+		Z90CRYPT_NUM_APS);
-+	len += sprinthx4(
-+		"Per-device successfully completed request counts",
-+		resp_buff+len,
-+		get_status_perdevice_reqcnt((unsigned int *)workarea),
-+		Z90CRYPT_NUM_APS);
-+	*eof = 1;
-+	memset(workarea, 0, Z90CRYPT_NUM_APS * sizeof(unsigned int));
-+	return len;
-+}
-+
-+static inline void
-+disable_card(int card_index)
-+{
-+	struct device *devp;
-+
-+	devp = LONG2DEVPTR(card_index);
-+	if (!devp || devp->user_disabled)
-+		return;
-+	devp->user_disabled = 1;
-+	z90crypt.hdware_info->hdware_mask.user_disabled_count++;
-+	if (devp->dev_type == -1)
-+		return;
-+	z90crypt.hdware_info->type_mask[devp->dev_type].user_disabled_count++;
-+}
-+
-+static inline void
-+enable_card(int card_index)
-+{
-+	struct device *devp;
-+
-+	devp = LONG2DEVPTR(card_index);
-+	if (!devp || !devp->user_disabled)
-+		return;
-+	devp->user_disabled = 0;
-+	z90crypt.hdware_info->hdware_mask.user_disabled_count--;
-+	if (devp->dev_type == -1)
-+		return;
-+	z90crypt.hdware_info->type_mask[devp->dev_type].user_disabled_count--;
-+}
-+
-+static inline int
-+scan_char(unsigned char *bf, unsigned int len,
-+	  unsigned int *offs, unsigned int *p_eof, unsigned char c)
-+{
-+	unsigned int i, found;
-+
-+	found = 0;
-+	for (i = 0; i < len; i++) {
-+		if (bf[i] == c) {
-+			found = 1;
-+			break;
-+		}
-+		if (bf[i] == '\0') {
-+			*p_eof = 1;
-+			break;
-+		}
-+		if (bf[i] == '\n') {
-+			break;
-+		}
-+	}
-+	*offs = i+1;
-+	return found;
-+}
-+
-+static inline int
-+scan_string(unsigned char *bf, unsigned int len,
-+	    unsigned int *offs, unsigned int *p_eof, unsigned char *s)
-+{
-+	unsigned int temp_len, temp_offs, found, eof;
-+
-+	temp_len = temp_offs = found = eof = 0;
-+	while (!eof && !found) {
-+		found = scan_char(bf+temp_len, len-temp_len,
-+				  &temp_offs, &eof, *s);
-+
-+		temp_len += temp_offs;
-+		if (eof) {
-+			found = 0;
-+			break;
-+		}
-+
-+		if (found) {
-+			if (len >= temp_offs+strlen(s)) {
-+				found = !strncmp(bf+temp_len-1, s, strlen(s));
-+				if (found) {
-+					*offs = temp_len+strlen(s)-1;
-+					break;
-+				}
-+			} else {
-+				found = 0;
-+				*p_eof = 1;
-+				break;
-+			}
-+		}
-+	}
-+	return found;
-+}
-+
-+static int
-+z90crypt_status_write(struct file *file, const char __user *buffer,
-+		      unsigned long count, void *data)
-+{
-+	int i, j, len, offs, found, eof;
-+	unsigned char *lbuf;
-+	unsigned int local_count;
-+
-+#define LBUFSIZE 600
-+	lbuf = kmalloc(LBUFSIZE, GFP_KERNEL);
-+	if (!lbuf) {
-+		PRINTK("kmalloc failed!\n");
-+		return 0;
-+	}
-+
-+	if (count <= 0)
-+		return 0;
-+
-+	local_count = UMIN((unsigned int)count, LBUFSIZE-1);
-+
-+	if (copy_from_user(lbuf, buffer, local_count) != 0) {
-+		kfree(lbuf);
-+		return -EFAULT;
-+	}
-+
-+	lbuf[local_count-1] = '\0';
-+
-+	len = 0;
-+	eof = 0;
-+	found = 0;
-+	while (!eof) {
-+		found = scan_string(lbuf+len, local_count-len, &offs, &eof,
-+				    "Online devices");
-+		len += offs;
-+		if (found == 1)
-+			break;
-+	}
-+
-+	if (eof) {
-+		kfree(lbuf);
-+		return count;
-+	}
-+
-+	if (found)
-+		found = scan_char(lbuf+len, local_count-len, &offs, &eof, '\n');
-+
-+	if (!found || eof) {
-+		kfree(lbuf);
-+		return count;
-+	}
-+
-+	len += offs;
-+	j = 0;
-+	for (i = 0; i < 80; i++) {
-+		switch (*(lbuf+len+i)) {
-+		case '\t':
-+		case ' ':
-+			break;
-+		case '\n':
-+		default:
-+			eof = 1;
-+			break;
-+		case '0':
-+		case '1':
-+		case '2':
-+		case '3':
-+		case '4':
-+		case '5':
-+			j++;
-+			break;
-+		case 'd':
-+		case 'D':
-+			disable_card(j);
-+			j++;
-+			break;
-+		case 'e':
-+		case 'E':
-+			enable_card(j);
-+			j++;
-+			break;
-+		}
-+		if (eof)
-+			break;
-+	}
-+
-+	kfree(lbuf);
-+	return count;
-+}
-+
-+/**
-+ * Functions that run under a timer, with no process id
-+ *
-+ * The task functions:
-+ *     z90crypt_reader_task
-+ *	 helper_send_work
-+ *	 helper_handle_work_element
-+ *	 helper_receive_rc
-+ *     z90crypt_config_task
-+ *     z90crypt_cleanup_task
-+ *
-+ * Helper functions:
-+ *     z90crypt_schedule_reader_timer
-+ *     z90crypt_schedule_reader_task
-+ *     z90crypt_schedule_config_task
-+ *     z90crypt_schedule_cleanup_task
-+ */
-+static inline int
-+receive_from_crypto_device(int index, unsigned char *psmid, int *buff_len_p,
-+			   unsigned char *buff, unsigned char __user **dest_p_p)
-+{
-+	int dv, rv;
-+	struct device *dev_ptr;
-+	struct caller *caller_p;
-+	struct ica_rsa_modexpo *icaMsg_p;
-+	struct list_head *ptr, *tptr;
-+
-+	memcpy(psmid, NULL_psmid, sizeof(NULL_psmid));
-+
-+	if (z90crypt.terminating)
-+		return REC_FATAL_ERROR;
-+
-+	caller_p = 0;
-+	dev_ptr = z90crypt.device_p[index];
-+	rv = 0;
-+	do {
-+		if (!dev_ptr || dev_ptr->disabled) {
-+			rv = REC_NO_WORK; // a disabled device can't return work
-+			break;
-+		}
-+		if (dev_ptr->dev_self_x != index) {
-+			PRINTK("Corrupt dev ptr in receive_from_AP\n");
-+			z90crypt.terminating = 1;
-+			rv = REC_FATAL_ERROR;
-+			break;
-+		}
-+		if (!dev_ptr->dev_resp_l || !dev_ptr->dev_resp_p) {
-+			dv = DEV_REC_EXCEPTION;
-+			PRINTK("dev_resp_l = %d, dev_resp_p = %p\n",
-+			       dev_ptr->dev_resp_l, dev_ptr->dev_resp_p);
-+		} else {
-+			PDEBUG("Dequeue called for device %d\n", index);
-+			dv = receive_from_AP(index, z90crypt.cdx,
-+					     dev_ptr->dev_resp_l,
-+					     dev_ptr->dev_resp_p, psmid);
-+		}
-+		switch (dv) {
-+		case DEV_REC_EXCEPTION:
-+			rv = REC_FATAL_ERROR;
-+			z90crypt.terminating = 1;
-+			PRINTKC("Exception in receive from device %d\n",
-+				index);
-+			break;
-+		case DEV_ONLINE:
-+			rv = 0;
-+			break;
-+		case DEV_EMPTY:
-+			rv = REC_EMPTY;
-+			break;
-+		case DEV_NO_WORK:
-+			rv = REC_NO_WORK;
-+			break;
-+		case DEV_BAD_MESSAGE:
-+		case DEV_GONE:
-+		case REC_HARDWAR_ERR:
-+		default:
-+			rv = REC_NO_RESPONSE;
-+			break;
-+		}
-+		if (rv)
-+			break;
-+		if (dev_ptr->dev_caller_count <= 0) {
-+			rv = REC_USER_GONE;
-+			break;
-+	        }
-+
-+		list_for_each_safe(ptr, tptr, &dev_ptr->dev_caller_list) {
-+			caller_p = list_entry(ptr, struct caller, caller_liste);
-+			if (!memcmp(caller_p->caller_id, psmid,
-+				    sizeof(caller_p->caller_id))) {
-+				if (!list_empty(&caller_p->caller_liste)) {
-+					list_del_init(ptr);
-+					dev_ptr->dev_caller_count--;
-+					break;
-+				}
-+			}
-+			caller_p = 0;
-+		}
-+		if (!caller_p) {
-+			PRINTKW("Unable to locate PSMID %02X%02X%02X%02X%02X"
-+				"%02X%02X%02X in device list\n",
-+				psmid[0], psmid[1], psmid[2], psmid[3],
-+				psmid[4], psmid[5], psmid[6], psmid[7]);
-+			rv = REC_USER_GONE;
-+			break;
-+		}
-+
-+		PDEBUG("caller_p after successful receive: %p\n", caller_p);
-+		rv = convert_response(dev_ptr->dev_resp_p,
-+				      caller_p->caller_buf_p, buff_len_p, buff);
-+		switch (rv) {
-+		case REC_USE_PCICA:
-+			break;
-+		case REC_OPERAND_INV:
-+		case REC_OPERAND_SIZE:
-+		case REC_EVEN_MOD:
-+		case REC_INVALID_PAD:
-+			PDEBUG("device %d: 'user error' %d\n", index, rv);
-+			break;
-+		case WRONG_DEVICE_TYPE:
-+		case REC_HARDWAR_ERR:
-+		case REC_BAD_MESSAGE:
-+			PRINTKW("device %d: hardware error %d\n", index, rv);
-+			rv = REC_NO_RESPONSE;
-+			break;
-+		default:
-+			PDEBUG("device %d: rv = %d\n", index, rv);
-+			break;
-+		}
-+	} while (0);
-+
-+	switch (rv) {
-+	case 0:
-+		PDEBUG("Successful receive from device %d\n", index);
-+		icaMsg_p = (struct ica_rsa_modexpo *)caller_p->caller_buf_p;
-+		*dest_p_p = icaMsg_p->outputdata;
-+		if (*buff_len_p == 0)
-+			PRINTK("Zero *buff_len_p\n");
-+		break;
-+	case REC_NO_RESPONSE:
-+		PRINTKW("Removing device %d from availability\n", index);
-+		remove_device(dev_ptr);
-+		break;
-+	}
-+
-+	if (caller_p)
-+		unbuild_caller(dev_ptr, caller_p);
-+
-+	return rv;
-+}
-+
-+static inline void
-+helper_send_work(int index)
-+{
-+	struct work_element *rq_p;
-+	int rv;
-+
-+	if (list_empty(&request_list))
-+		return;
-+	requestq_count--;
-+	rq_p = list_entry(request_list.next, struct work_element, liste);
-+	list_del_init(&rq_p->liste);
-+	rq_p->audit[1] |= FP_REMREQUEST;
-+	if (rq_p->devtype == SHRT2DEVPTR(index)->dev_type) {
-+		rq_p->devindex = SHRT2LONG(index);
-+		rv = send_to_crypto_device(rq_p);
-+		if (rv == 0) {
-+			rq_p->requestsent = jiffies;
-+			rq_p->audit[0] |= FP_SENT;
-+			list_add_tail(&rq_p->liste, &pending_list);
-+			++pendingq_count;
-+			rq_p->audit[0] |= FP_PENDING;
-+		} else {
-+			switch (rv) {
-+			case REC_OPERAND_INV:
-+			case REC_OPERAND_SIZE:
-+			case REC_EVEN_MOD:
-+			case REC_INVALID_PAD:
-+				rq_p->retcode = -EINVAL;
-+				break;
-+			case SEN_NOT_AVAIL:
-+			case SEN_RETRY:
-+			case REC_NO_RESPONSE:
-+			default:
-+				if (z90crypt.mask.st_count > 1)
-+					rq_p->retcode =
-+						-ERESTARTSYS;
-+				else
-+					rq_p->retcode = -ENODEV;
-+				break;
-+			}
-+			rq_p->status[0] |= STAT_FAILED;
-+			rq_p->audit[1] |= FP_AWAKENING;
-+			atomic_set(&rq_p->alarmrung, 1);
-+			wake_up(&rq_p->waitq);
-+		}
-+	} else {
-+		if (z90crypt.mask.st_count > 1)
-+			rq_p->retcode = -ERESTARTSYS;
-+		else
-+			rq_p->retcode = -ENODEV;
-+		rq_p->status[0] |= STAT_FAILED;
-+		rq_p->audit[1] |= FP_AWAKENING;
-+		atomic_set(&rq_p->alarmrung, 1);
-+		wake_up(&rq_p->waitq);
-+	}
-+}
-+
-+static inline void
-+helper_handle_work_element(int index, unsigned char psmid[8], int rc,
-+			   int buff_len, unsigned char *buff,
-+			   unsigned char __user *resp_addr)
-+{
-+	struct work_element *pq_p;
-+	struct list_head *lptr, *tptr;
-+
-+	pq_p = 0;
-+	list_for_each_safe(lptr, tptr, &pending_list) {
-+		pq_p = list_entry(lptr, struct work_element, liste);
-+		if (!memcmp(pq_p->caller_id, psmid, sizeof(pq_p->caller_id))) {
-+			list_del_init(lptr);
-+			pendingq_count--;
-+			pq_p->audit[1] |= FP_NOTPENDING;
-+			break;
-+		}
-+		pq_p = 0;
-+	}
-+
-+	if (!pq_p) {
-+		PRINTK("device %d has work but no caller exists on pending Q\n",
-+		       SHRT2LONG(index));
-+		return;
-+	}
-+
-+	switch (rc) {
-+		case 0:
-+			pq_p->resp_buff_size = buff_len;
-+			pq_p->audit[1] |= FP_RESPSIZESET;
-+			if (buff_len) {
-+				pq_p->resp_addr = resp_addr;
-+				pq_p->audit[1] |= FP_RESPADDRCOPIED;
-+				memcpy(pq_p->resp_buff, buff, buff_len);
-+				pq_p->audit[1] |= FP_RESPBUFFCOPIED;
-+			}
-+			break;
-+		case REC_OPERAND_INV:
-+		case REC_OPERAND_SIZE:
-+		case REC_EVEN_MOD:
-+		case REC_INVALID_PAD:
-+			PDEBUG("-EINVAL after application error %d\n", rc);
-+			pq_p->retcode = -EINVAL;
-+			pq_p->status[0] |= STAT_FAILED;
-+			break;
-+		case REC_USE_PCICA:
-+			pq_p->retcode = -ERESTARTSYS;
-+			pq_p->status[0] |= STAT_FAILED;
-+			break;
-+		case REC_NO_RESPONSE:
-+		default:
-+			if (z90crypt.mask.st_count > 1)
-+				pq_p->retcode = -ERESTARTSYS;
-+			else
-+				pq_p->retcode = -ENODEV;
-+			pq_p->status[0] |= STAT_FAILED;
-+			break;
-+	}
-+	if ((pq_p->status[0] != STAT_FAILED) || (pq_p->retcode != -ERELEASED)) {
-+		pq_p->audit[1] |= FP_AWAKENING;
-+		atomic_set(&pq_p->alarmrung, 1);
-+		wake_up(&pq_p->waitq);
-+	}
-+}
-+
-+/**
-+ * return TRUE if the work element should be removed from the queue
-+ */
-+static inline int
-+helper_receive_rc(int index, int *rc_p)
-+{
-+	switch (*rc_p) {
-+	case 0:
-+	case REC_OPERAND_INV:
-+	case REC_OPERAND_SIZE:
-+	case REC_EVEN_MOD:
-+	case REC_INVALID_PAD:
-+	case REC_USE_PCICA:
-+		break;
-+
-+	case REC_BUSY:
-+	case REC_NO_WORK:
-+	case REC_EMPTY:
-+	case REC_RETRY_DEV:
-+	case REC_FATAL_ERROR:
-+		return 0;
-+
-+	case REC_NO_RESPONSE:
-+		break;
-+
-+	default:
-+		PRINTK("rc %d, device %d converted to REC_NO_RESPONSE\n",
-+		       *rc_p, SHRT2LONG(index));
-+		*rc_p = REC_NO_RESPONSE;
-+		break;
-+	}
-+	return 1;
-+}
-+
-+static inline void
-+z90crypt_schedule_reader_timer(void)
-+{
-+	if (timer_pending(&reader_timer))
-+		return;
-+	if (mod_timer(&reader_timer, jiffies+(READERTIME*HZ/1000)) != 0)
-+		PRINTK("Timer pending while modifying reader timer\n");
-+}
-+
-+static void
-+z90crypt_reader_task(unsigned long ptr)
-+{
-+	int workavail, index, rc, buff_len;
-+	unsigned char	psmid[8];
-+	unsigned char __user *resp_addr;
-+	static unsigned char buff[1024];
-+
-+	/**
-+	 * we use workavail = 2 to ensure 2 passes with nothing dequeued before
-+	 * exiting the loop. If (pendingq_count+requestq_count) == 0 after the
-+	 * loop, there is no work remaining on the queues.
-+	 */
-+	resp_addr = 0;
-+	workavail = 2;
-+	buff_len = 0;
-+	while (workavail) {
-+		workavail--;
-+		rc = 0;
-+		spin_lock_irq(&queuespinlock);
-+		memset(buff, 0x00, sizeof(buff));
-+
-+		/* Dequeue once from each device in round robin. */
-+		for (index = 0; index < z90crypt.mask.st_count; index++) {
-+			PDEBUG("About to receive.\n");
-+			rc = receive_from_crypto_device(SHRT2LONG(index),
-+							psmid,
-+							&buff_len,
-+							buff,
-+							&resp_addr);
-+			PDEBUG("Dequeued: rc = %d.\n", rc);
-+
-+			if (helper_receive_rc(index, &rc)) {
-+				if (rc != REC_NO_RESPONSE) {
-+					helper_send_work(index);
-+					workavail = 2;
-+				}
-+
-+				helper_handle_work_element(index, psmid, rc,
-+							   buff_len, buff,
-+							   resp_addr);
-+			}
-+
-+			if (rc == REC_FATAL_ERROR)
-+				PRINTKW("REC_FATAL_ERROR from device %d!\n",
-+					SHRT2LONG(index));
-+		}
-+		spin_unlock_irq(&queuespinlock);
-+	}
-+
-+	if (pendingq_count + requestq_count)
-+		z90crypt_schedule_reader_timer();
-+}
-+
-+static inline void
-+z90crypt_schedule_config_task(unsigned int expiration)
-+{
-+	if (timer_pending(&config_timer))
-+		return;
-+	if (mod_timer(&config_timer, jiffies+(expiration*HZ)) != 0)
-+		PRINTK("Timer pending while modifying config timer\n");
-+}
-+
-+static void
-+z90crypt_config_task(unsigned long ptr)
-+{
-+	int rc;
-+
-+	PDEBUG("jiffies %ld\n", jiffies);
-+
-+	if ((rc = refresh_z90crypt(&z90crypt.cdx)))
-+		PRINTK("Error %d detected in refresh_z90crypt.\n", rc);
-+	/* If return was fatal, don't bother reconfiguring */
-+	if ((rc != TSQ_FATAL_ERROR) && (rc != RSQ_FATAL_ERROR))
-+		z90crypt_schedule_config_task(CONFIGTIME);
-+}
-+
-+static inline void
-+z90crypt_schedule_cleanup_task(void)
-+{
-+	if (timer_pending(&cleanup_timer))
-+		return;
-+	if (mod_timer(&cleanup_timer, jiffies+(CLEANUPTIME*HZ)) != 0)
-+		PRINTK("Timer pending while modifying cleanup timer\n");
-+}
-+
-+static inline void
-+helper_drain_queues(void)
-+{
-+	struct work_element *pq_p;
-+	struct list_head *lptr, *tptr;
-+
-+	list_for_each_safe(lptr, tptr, &pending_list) {
-+		pq_p = list_entry(lptr, struct work_element, liste);
-+		pq_p->retcode = -ENODEV;
-+		pq_p->status[0] |= STAT_FAILED;
-+		unbuild_caller(LONG2DEVPTR(pq_p->devindex),
-+			       (struct caller *)pq_p->requestptr);
-+		list_del_init(lptr);
-+		pendingq_count--;
-+		pq_p->audit[1] |= FP_NOTPENDING;
-+		pq_p->audit[1] |= FP_AWAKENING;
-+		atomic_set(&pq_p->alarmrung, 1);
-+		wake_up(&pq_p->waitq);
-+	}
-+
-+	list_for_each_safe(lptr, tptr, &request_list) {
-+		pq_p = list_entry(lptr, struct work_element, liste);
-+		pq_p->retcode = -ENODEV;
-+		pq_p->status[0] |= STAT_FAILED;
-+		list_del_init(lptr);
-+		requestq_count--;
-+		pq_p->audit[1] |= FP_REMREQUEST;
-+		pq_p->audit[1] |= FP_AWAKENING;
-+		atomic_set(&pq_p->alarmrung, 1);
-+		wake_up(&pq_p->waitq);
-+	}
-+}
-+
-+static inline void
-+helper_timeout_requests(void)
-+{
-+	struct work_element *pq_p;
-+	struct list_head *lptr, *tptr;
-+	long timelimit;
-+
-+	timelimit = jiffies - (CLEANUPTIME * HZ);
-+	/* The list is in strict chronological order */
-+	list_for_each_safe(lptr, tptr, &pending_list) {
-+		pq_p = list_entry(lptr, struct work_element, liste);
-+		if (pq_p->requestsent >= timelimit)
-+			break;
-+		PRINTKW("Purging(PQ) PSMID %02X%02X%02X%02X%02X%02X%02X%02X\n",
-+		       ((struct caller *)pq_p->requestptr)->caller_id[0],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[1],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[2],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[3],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[4],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[5],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[6],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[7]);
-+		pq_p->retcode = -ETIMEOUT;
-+		pq_p->status[0] |= STAT_FAILED;
-+		/* get this off any caller queue it may be on */
-+		unbuild_caller(LONG2DEVPTR(pq_p->devindex),
-+			       (struct caller *) pq_p->requestptr);
-+		list_del_init(lptr);
-+		pendingq_count--;
-+		pq_p->audit[1] |= FP_TIMEDOUT;
-+		pq_p->audit[1] |= FP_NOTPENDING;
-+		pq_p->audit[1] |= FP_AWAKENING;
-+		atomic_set(&pq_p->alarmrung, 1);
-+		wake_up(&pq_p->waitq);
-+	}
-+
-+	/**
-+	 * If pending count is zero, items left on the request queue may
-+	 * never be processed.
-+	 */
-+	if (pendingq_count <= 0) {
-+		list_for_each_safe(lptr, tptr, &request_list) {
-+			pq_p = list_entry(lptr, struct work_element, liste);
-+			if (pq_p->requestsent >= timelimit)
-+				break;
-+		PRINTKW("Purging(RQ) PSMID %02X%02X%02X%02X%02X%02X%02X%02X\n",
-+		       ((struct caller *)pq_p->requestptr)->caller_id[0],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[1],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[2],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[3],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[4],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[5],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[6],
-+		       ((struct caller *)pq_p->requestptr)->caller_id[7]);
-+			pq_p->retcode = -ETIMEOUT;
-+			pq_p->status[0] |= STAT_FAILED;
-+			list_del_init(lptr);
-+			requestq_count--;
-+			pq_p->audit[1] |= FP_TIMEDOUT;
-+			pq_p->audit[1] |= FP_REMREQUEST;
-+			pq_p->audit[1] |= FP_AWAKENING;
-+			atomic_set(&pq_p->alarmrung, 1);
-+			wake_up(&pq_p->waitq);
-+		}
-+	}
-+}
-+
-+static void
-+z90crypt_cleanup_task(unsigned long ptr)
-+{
-+	PDEBUG("jiffies %ld\n", jiffies);
-+	spin_lock_irq(&queuespinlock);
-+	if (z90crypt.mask.st_count <= 0) // no devices!
-+		helper_drain_queues();
-+	else
-+		helper_timeout_requests();
-+	spin_unlock_irq(&queuespinlock);
-+	z90crypt_schedule_cleanup_task();
-+}
-+
-+static void
-+z90crypt_schedule_reader_task(unsigned long ptr)
-+{
-+	tasklet_schedule(&reader_tasklet);
-+}
-+
-+/**
-+ * Lowlevel Functions:
-+ *
-+ *   create_z90crypt:  creates and initializes basic data structures
-+ *   refresh_z90crypt:	re-initializes basic data structures
-+ *   find_crypto_devices: returns a count and mask of hardware status
-+ *   create_crypto_device:  builds the descriptor for a device
-+ *   destroy_crypto_device:  unallocates the descriptor for a device
-+ *   destroy_z90crypt:	drains all work, unallocates structs
-+ */
-+
-+/**
-+ * build the z90crypt root structure using the given domain index
-+ */
-+static int
-+create_z90crypt(int *cdx_p)
-+{
-+	struct hdware_block *hdware_blk_p;
-+
-+	memset(&z90crypt, 0x00, sizeof(struct z90crypt));
-+	z90crypt.domain_established = 0;
-+	z90crypt.len = sizeof(struct z90crypt);
-+	z90crypt.max_count = Z90CRYPT_NUM_DEVS;
-+	z90crypt.cdx = *cdx_p;
-+
-+	hdware_blk_p = (struct hdware_block *)
-+		kmalloc(sizeof(struct hdware_block), GFP_ATOMIC);
-+	if (!hdware_blk_p) {
-+		PDEBUG("kmalloc for hardware block failed\n");
-+		return ENOMEM;
-+	}
-+	memset(hdware_blk_p, 0x00, sizeof(struct hdware_block));
-+	z90crypt.hdware_info = hdware_blk_p;
-+
-+	return 0;
-+}
-+
-+static inline int
-+helper_scan_devices(int cdx_array[16], int *cdx_p, int *correct_cdx_found)
-+{
-+	enum hdstat hd_stat;
-+	int q_depth, dev_type;
-+	int indx, chkdom, numdomains;
-+
-+	q_depth = dev_type = numdomains = 0;
-+	for (chkdom = 0; chkdom <= 15; cdx_array[chkdom++] = -1);
-+	for (indx = 0; indx < z90crypt.max_count; indx++) {
-+		hd_stat = HD_NOT_THERE;
-+		numdomains = 0;
-+		for (chkdom = 0; chkdom <= 15; chkdom++) {
-+			hd_stat = query_online(indx, chkdom, MAX_RESET,
-+					       &q_depth, &dev_type);
-+			if (hd_stat == HD_TSQ_EXCEPTION) {
-+				z90crypt.terminating = 1;
-+				PRINTKC("exception taken!\n");
-+				break;
-+			}
-+			if (hd_stat == HD_ONLINE) {
-+				cdx_array[numdomains++] = chkdom;
-+				if (*cdx_p == chkdom) {
-+					*correct_cdx_found  = 1;
-+					break;
-+				}
-+			}
-+		}
-+		if ((*correct_cdx_found == 1) || (numdomains != 0))
-+			break;
-+		if (z90crypt.terminating)
-+			break;
-+	}
-+	return numdomains;
-+}
-+
-+static inline int
-+probe_crypto_domain(int *cdx_p)
-+{
-+	int cdx_array[16];
-+	char cdx_array_text[53], temp[5];
-+	int correct_cdx_found, numdomains;
-+
-+	correct_cdx_found = 0;
-+	numdomains = helper_scan_devices(cdx_array, cdx_p, &correct_cdx_found);
-+
-+	if (z90crypt.terminating)
-+		return TSQ_FATAL_ERROR;
-+
-+	if (correct_cdx_found)
-+		return 0;
-+
-+	if (numdomains == 0) {
-+		PRINTKW("Unable to find crypto domain: No devices found\n");
-+		return Z90C_NO_DEVICES;
-+	}
-+
-+	if (numdomains == 1) {
-+		if (*cdx_p == -1) {
-+			*cdx_p = cdx_array[0];
-+			return 0;
-+		}
-+		PRINTKW("incorrect domain: specified = %d, found = %d\n",
-+		       *cdx_p, cdx_array[0]);
-+		return Z90C_INCORRECT_DOMAIN;
-+	}
-+
-+	numdomains--;
-+	sprintf(cdx_array_text, "%d", cdx_array[numdomains]);
-+	while (numdomains) {
-+		numdomains--;
-+		sprintf(temp, ", %d", cdx_array[numdomains]);
-+		strcat(cdx_array_text, temp);
-+	}
-+
-+	PRINTKW("ambiguous domain detected: specified = %d, found array = %s\n",
-+		*cdx_p, cdx_array_text);
-+	return Z90C_AMBIGUOUS_DOMAIN;
-+}
-+
-+static int
-+refresh_z90crypt(int *cdx_p)
-+{
-+	int i, j, indx, rv;
-+	struct status local_mask;
-+	struct device *devPtr;
-+	unsigned char oldStat, newStat;
-+	int return_unchanged;
-+
-+	if (z90crypt.len != sizeof(z90crypt))
-+		return ENOTINIT;
-+	if (z90crypt.terminating)
-+		return TSQ_FATAL_ERROR;
-+	rv = 0;
-+	if (!z90crypt.hdware_info->hdware_mask.st_count &&
-+	    !z90crypt.domain_established) {
-+		rv = probe_crypto_domain(cdx_p);
-+		if (z90crypt.terminating)
-+			return TSQ_FATAL_ERROR;
-+		if (rv == Z90C_NO_DEVICES)
-+			return 0; // try later
-+		if (rv)
-+			return rv;
-+		z90crypt.cdx = *cdx_p;
-+		z90crypt.domain_established = 1;
-+	}
-+	rv = find_crypto_devices(&local_mask);
-+	if (rv) {
-+		PRINTK("find crypto devices returned %d\n", rv);
-+		return rv;
-+	}
-+	if (!memcmp(&local_mask, &z90crypt.hdware_info->hdware_mask,
-+		    sizeof(struct status))) {
-+		return_unchanged = 1;
-+		for (i = 0; i < Z90CRYPT_NUM_TYPES; i++) {
-+			/**
-+			 * Check for disabled cards.  If any device is marked
-+			 * disabled, destroy it.
-+			 */
-+			for (j = 0;
-+			     j < z90crypt.hdware_info->type_mask[i].st_count;
-+			     j++) {
-+				indx = z90crypt.hdware_info->type_x_addr[i].
-+								device_index[j];
-+				devPtr = z90crypt.device_p[indx];
-+				if (devPtr && devPtr->disabled) {
-+					local_mask.st_mask[indx] = HD_NOT_THERE;
-+					return_unchanged = 0;
-+				}
-+			}
-+		}
-+		if (return_unchanged == 1)
-+			return 0;
-+	}
-+
-+	spin_lock_irq(&queuespinlock);
-+	for (i = 0; i < z90crypt.max_count; i++) {
-+		oldStat = z90crypt.hdware_info->hdware_mask.st_mask[i];
-+		newStat = local_mask.st_mask[i];
-+		if ((oldStat == HD_ONLINE) && (newStat != HD_ONLINE))
-+			destroy_crypto_device(i);
-+		else if ((oldStat != HD_ONLINE) && (newStat == HD_ONLINE)) {
-+			rv = create_crypto_device(i);
-+			if (rv >= REC_FATAL_ERROR)
-+				return rv;
-+			if (rv != 0) {
-+				local_mask.st_mask[i] = HD_NOT_THERE;
-+				local_mask.st_count--;
-+			}
-+		}
-+	}
-+	memcpy(z90crypt.hdware_info->hdware_mask.st_mask, local_mask.st_mask,
-+	       sizeof(local_mask.st_mask));
-+	z90crypt.hdware_info->hdware_mask.st_count = local_mask.st_count;
-+	z90crypt.hdware_info->hdware_mask.disabled_count =
-+						      local_mask.disabled_count;
-+	refresh_index_array(&z90crypt.mask, &z90crypt.overall_device_x);
-+	for (i = 0; i < Z90CRYPT_NUM_TYPES; i++)
-+		refresh_index_array(&(z90crypt.hdware_info->type_mask[i]),
-+				    &(z90crypt.hdware_info->type_x_addr[i]));
-+	spin_unlock_irq(&queuespinlock);
-+
-+	return rv;
-+}
-+
-+static int
-+find_crypto_devices(struct status *deviceMask)
-+{
-+	int i, q_depth, dev_type;
-+	enum hdstat hd_stat;
-+
-+	deviceMask->st_count = 0;
-+	deviceMask->disabled_count = 0;
-+	deviceMask->user_disabled_count = 0;
-+
-+	for (i = 0; i < z90crypt.max_count; i++) {
-+		hd_stat = query_online(i, z90crypt.cdx, MAX_RESET, &q_depth,
-+				       &dev_type);
-+		if (hd_stat == HD_TSQ_EXCEPTION) {
-+			z90crypt.terminating = 1;
-+			PRINTKC("Exception during probe for crypto devices\n");
-+			return TSQ_FATAL_ERROR;
-+		}
-+		deviceMask->st_mask[i] = hd_stat;
-+		if (hd_stat == HD_ONLINE) {
-+			PDEBUG("Got an online crypto!: %d\n", i);
-+			PDEBUG("Got a queue depth of %d\n", q_depth);
-+			PDEBUG("Got a device type of %d\n", dev_type);
-+			if (q_depth <= 0)
-+				return TSQ_FATAL_ERROR;
-+			deviceMask->st_count++;
-+			z90crypt.q_depth_array[i] = q_depth;
-+			z90crypt.dev_type_array[i] = dev_type;
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+refresh_index_array(struct status *status_str, struct device_x *index_array)
-+{
-+	int i, count;
-+	enum devstat stat;
-+
-+	i = -1;
-+	count = 0;
-+	do {
-+		stat = status_str->st_mask[++i];
-+		if (stat == DEV_ONLINE)
-+			index_array->device_index[count++] = i;
-+	} while ((i < Z90CRYPT_NUM_DEVS) && (count < status_str->st_count));
-+
-+	return count;
-+}
-+
-+static int
-+create_crypto_device(int index)
-+{
-+	int rv, devstat, total_size;
-+	struct device *dev_ptr;
-+	struct status *type_str_p;
-+	int deviceType;
-+
-+	dev_ptr = z90crypt.device_p[index];
-+	if (!dev_ptr) {
-+		total_size = sizeof(struct device) +
-+			     z90crypt.q_depth_array[index] * sizeof(int);
-+
-+		dev_ptr = (struct device *) kmalloc(total_size, GFP_ATOMIC);
-+		if (!dev_ptr) {
-+			PRINTK("kmalloc device %d failed\n", index);
-+			return ENOMEM;
-+		}
-+		memset(dev_ptr, 0, total_size);
-+		dev_ptr->dev_resp_p = kmalloc(MAX_RESPONSE_SIZE, GFP_ATOMIC);
-+		if (!dev_ptr->dev_resp_p) {
-+			kfree(dev_ptr);
-+			PRINTK("kmalloc device %d rec buffer failed\n", index);
-+			return ENOMEM;
-+		}
-+		dev_ptr->dev_resp_l = MAX_RESPONSE_SIZE;
-+		INIT_LIST_HEAD(&(dev_ptr->dev_caller_list));
-+	}
-+
-+	devstat = reset_device(index, z90crypt.cdx, MAX_RESET);
-+	if (devstat == DEV_RSQ_EXCEPTION) {
-+		PRINTK("exception during reset device %d\n", index);
-+		kfree(dev_ptr->dev_resp_p);
-+		kfree(dev_ptr);
-+		return RSQ_FATAL_ERROR;
-+	}
-+	if (devstat == DEV_ONLINE) {
-+		dev_ptr->dev_self_x = index;
-+		dev_ptr->dev_type = z90crypt.dev_type_array[index];
-+		if (dev_ptr->dev_type == NILDEV) {
-+			rv = probe_device_type(dev_ptr);
-+			if (rv) {
-+				PRINTK("rv = %d from probe_device_type %d\n",
-+				       rv, index);
-+				kfree(dev_ptr->dev_resp_p);
-+				kfree(dev_ptr);
-+				return rv;
-+			}
-+		}
-+		if (dev_ptr->dev_type == PCIXCC_UNK) {
-+			rv = probe_PCIXCC_type(dev_ptr);
-+			if (rv) {
-+				PRINTK("rv = %d from probe_PCIXCC_type %d\n",
-+				       rv, index);
-+				kfree(dev_ptr->dev_resp_p);
-+				kfree(dev_ptr);
-+				return rv;
-+			}
-+		}
-+		deviceType = dev_ptr->dev_type;
-+		z90crypt.dev_type_array[index] = deviceType;
-+		if (deviceType == PCICA)
-+			z90crypt.hdware_info->device_type_array[index] = 1;
-+		else if (deviceType == PCICC)
-+			z90crypt.hdware_info->device_type_array[index] = 2;
-+		else if (deviceType == PCIXCC_MCL2)
-+			z90crypt.hdware_info->device_type_array[index] = 3;
-+		else if (deviceType == PCIXCC_MCL3)
-+			z90crypt.hdware_info->device_type_array[index] = 4;
-+		else if (deviceType == CEX2C)
-+			z90crypt.hdware_info->device_type_array[index] = 5;
-+		else
-+			z90crypt.hdware_info->device_type_array[index] = -1;
-+	}
-+
-+	/**
-+	 * 'q_depth' returned by the hardware is one less than
-+	 * the actual depth
-+	 */
-+	dev_ptr->dev_q_depth = z90crypt.q_depth_array[index];
-+	dev_ptr->dev_type = z90crypt.dev_type_array[index];
-+	dev_ptr->dev_stat = devstat;
-+	dev_ptr->disabled = 0;
-+	z90crypt.device_p[index] = dev_ptr;
-+
-+	if (devstat == DEV_ONLINE) {
-+		if (z90crypt.mask.st_mask[index] != DEV_ONLINE) {
-+			z90crypt.mask.st_mask[index] = DEV_ONLINE;
-+			z90crypt.mask.st_count++;
-+		}
-+		deviceType = dev_ptr->dev_type;
-+		type_str_p = &z90crypt.hdware_info->type_mask[deviceType];
-+		if (type_str_p->st_mask[index] != DEV_ONLINE) {
-+			type_str_p->st_mask[index] = DEV_ONLINE;
-+			type_str_p->st_count++;
-+		}
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+destroy_crypto_device(int index)
-+{
-+	struct device *dev_ptr;
-+	int t, disabledFlag;
-+
-+	dev_ptr = z90crypt.device_p[index];
-+
-+	/* remember device type; get rid of device struct */
-+	if (dev_ptr) {
-+		disabledFlag = dev_ptr->disabled;
-+		t = dev_ptr->dev_type;
-+		if (dev_ptr->dev_resp_p)
-+			kfree(dev_ptr->dev_resp_p);
-+		kfree(dev_ptr);
-+	} else {
-+		disabledFlag = 0;
-+		t = -1;
-+	}
-+	z90crypt.device_p[index] = 0;
-+
-+	/* if the type is valid, remove the device from the type_mask */
-+	if ((t != -1) && z90crypt.hdware_info->type_mask[t].st_mask[index]) {
-+		  z90crypt.hdware_info->type_mask[t].st_mask[index] = 0x00;
-+		  z90crypt.hdware_info->type_mask[t].st_count--;
-+		  if (disabledFlag == 1)
-+			z90crypt.hdware_info->type_mask[t].disabled_count--;
-+	}
-+	if (z90crypt.mask.st_mask[index] != DEV_GONE) {
-+		z90crypt.mask.st_mask[index] = DEV_GONE;
-+		z90crypt.mask.st_count--;
-+	}
-+	z90crypt.hdware_info->device_type_array[index] = 0;
-+
-+	return 0;
-+}
-+
-+static void
-+destroy_z90crypt(void)
-+{
-+	int i;
-+	for (i = 0; i < z90crypt.max_count; i++)
-+		if (z90crypt.device_p[i])
-+			destroy_crypto_device(i);
-+	if (z90crypt.hdware_info)
-+		kfree((void *)z90crypt.hdware_info);
-+	memset((void *)&z90crypt, 0, sizeof(z90crypt));
-+}
-+
-+static unsigned char static_testmsg[384] = {
-+0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x06,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x58,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x43,
-+0x41,0x2d,0x41,0x50,0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01,0x00,0x00,0x00,0x00,
-+0x50,0x4b,0x00,0x00,0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x32,
-+0x01,0x00,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0xb8,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x49,0x43,0x53,0x46,
-+0x20,0x20,0x20,0x20,0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53,0x2d,0x31,0x2e,0x32,
-+0x37,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,
-+0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
-+0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,
-+0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77,0x88,0x1e,0x00,0x00,
-+0x57,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00,0x03,0x02,0x00,0x00,
-+0x40,0x01,0x00,0x01,0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c,0xf6,0xd2,0x7b,0x58,
-+0x4b,0xf9,0x28,0x68,0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66,0x63,0x42,0xef,0xf8,
-+0xfd,0xa4,0xf8,0xb0,0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8,0x53,0x8c,0x6f,0x4e,
-+0x72,0x8f,0x6c,0x04,0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57,0xf7,0xdd,0xfd,0x4f,
-+0x11,0x36,0x95,0x5d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
-+};
-+
-+static int
-+probe_device_type(struct device *devPtr)
-+{
-+	int rv, dv, i, index, length;
-+	unsigned char psmid[8];
-+	static unsigned char loc_testmsg[sizeof(static_testmsg)];
-+
-+	index = devPtr->dev_self_x;
-+	rv = 0;
-+	do {
-+		memcpy(loc_testmsg, static_testmsg, sizeof(static_testmsg));
-+		length = sizeof(static_testmsg) - 24;
-+		/* the -24 allows for the header */
-+		dv = send_to_AP(index, z90crypt.cdx, length, loc_testmsg);
-+		if (dv) {
-+			PDEBUG("dv returned by send during probe: %d\n", dv);
-+			if (dv == DEV_SEN_EXCEPTION) {
-+				rv = SEN_FATAL_ERROR;
-+				PRINTKC("exception in send to AP %d\n", index);
-+				break;
-+			}
-+			PDEBUG("return value from send_to_AP: %d\n", rv);
-+			switch (dv) {
-+			case DEV_GONE:
-+				PDEBUG("dev %d not available\n", index);
-+				rv = SEN_NOT_AVAIL;
-+				break;
-+			case DEV_ONLINE:
-+				rv = 0;
-+				break;
-+			case DEV_EMPTY:
-+				rv = SEN_NOT_AVAIL;
-+				break;
-+			case DEV_NO_WORK:
-+				rv = SEN_FATAL_ERROR;
-+				break;
-+			case DEV_BAD_MESSAGE:
-+				rv = SEN_USER_ERROR;
-+				break;
-+			case DEV_QUEUE_FULL:
-+				rv = SEN_QUEUE_FULL;
-+				break;
-+			default:
-+				PRINTK("unknown dv=%d for dev %d\n", dv, index);
-+				rv = SEN_NOT_AVAIL;
-+				break;
-+			}
-+		}
-+
-+		if (rv)
-+			break;
-+
-+		for (i = 0; i < 6; i++) {
-+			mdelay(300);
-+			dv = receive_from_AP(index, z90crypt.cdx,
-+					     devPtr->dev_resp_l,
-+					     devPtr->dev_resp_p, psmid);
-+			PDEBUG("dv returned by DQ = %d\n", dv);
-+			if (dv == DEV_REC_EXCEPTION) {
-+				rv = REC_FATAL_ERROR;
-+				PRINTKC("exception in dequeue %d\n",
-+					index);
-+				break;
-+			}
-+			switch (dv) {
-+			case DEV_ONLINE:
-+				rv = 0;
-+				break;
-+			case DEV_EMPTY:
-+				rv = REC_EMPTY;
-+				break;
-+			case DEV_NO_WORK:
-+				rv = REC_NO_WORK;
-+				break;
-+			case DEV_BAD_MESSAGE:
-+			case DEV_GONE:
-+			default:
-+				rv = REC_NO_RESPONSE;
-+				break;
-+			}
-+			if ((rv != 0) && (rv != REC_NO_WORK))
-+				break;
-+			if (rv == 0)
-+				break;
-+		}
-+		if (rv)
-+			break;
-+		rv = (devPtr->dev_resp_p[0] == 0x00) &&
-+		     (devPtr->dev_resp_p[1] == 0x86);
-+		if (rv)
-+			devPtr->dev_type = PCICC;
-+		else
-+			devPtr->dev_type = PCICA;
-+		rv = 0;
-+	} while (0);
-+	/* In a general error case, the card is not marked online */
-+	return rv;
-+}
-+
-+static unsigned char MCL3_testmsg[] = {
-+0x00,0x00,0x00,0x00,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,
-+0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x00,0x00,0x00,0x01,0xC4,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x02,0x00,0x00,0x00,0x54,0x32,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE8,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
-+0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x0A,0x4D,0x52,0x50,0x20,0x20,0x20,0x20,0x20,
-+0x00,0x42,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,
-+0x0E,0x0F,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,
-+0xEE,0xFF,0xFF,0xEE,0xDD,0xCC,0xBB,0xAA,0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22,
-+0x11,0x00,0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,0xFE,0xDC,0xBA,0x98,0x76,0x54,
-+0x32,0x10,0x00,0x9A,0x00,0x98,0x00,0x00,0x1E,0x00,0x00,0x94,0x00,0x00,0x00,0x00,
-+0x04,0x00,0x00,0x8C,0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x40,0xBA,0xE8,0x23,0x3C,
-+0x75,0xF3,0x91,0x61,0xD6,0x73,0x39,0xCF,0x7B,0x6D,0x8E,0x61,0x97,0x63,0x9E,0xD9,
-+0x60,0x55,0xD6,0xC7,0xEF,0xF8,0x1E,0x63,0x95,0x17,0xCC,0x28,0x45,0x60,0x11,0xC5,
-+0xC4,0x4E,0x66,0xC6,0xE6,0xC3,0xDE,0x8A,0x19,0x30,0xCF,0x0E,0xD7,0xAA,0xDB,0x01,
-+0xD8,0x00,0xBB,0x8F,0x39,0x9F,0x64,0x28,0xF5,0x7A,0x77,0x49,0xCC,0x6B,0xA3,0x91,
-+0x97,0x70,0xE7,0x60,0x1E,0x39,0xE1,0xE5,0x33,0xE1,0x15,0x63,0x69,0x08,0x80,0x4C,
-+0x67,0xC4,0x41,0x8F,0x48,0xDF,0x26,0x98,0xF1,0xD5,0x8D,0x88,0xD9,0x6A,0xA4,0x96,
-+0xC5,0x84,0xD9,0x30,0x49,0x67,0x7D,0x19,0xB1,0xB3,0x45,0x4D,0xB2,0x53,0x9A,0x47,
-+0x3C,0x7C,0x55,0xBF,0xCC,0x85,0x00,0x36,0xF1,0x3D,0x93,0x53
-+};
-+
-+static int
-+probe_PCIXCC_type(struct device *devPtr)
-+{
-+	int rv, dv, i, index, length;
-+	unsigned char psmid[8];
-+	static unsigned char loc_testmsg[548];
-+	struct CPRBX *cprbx_p;
-+
-+	index = devPtr->dev_self_x;
-+	rv = 0;
-+	do {
-+		memcpy(loc_testmsg, MCL3_testmsg, sizeof(MCL3_testmsg));
-+		length = sizeof(MCL3_testmsg) - 0x0C;
-+		dv = send_to_AP(index, z90crypt.cdx, length, loc_testmsg);
-+		if (dv) {
-+			PDEBUG("dv returned = %d\n", dv);
-+			if (dv == DEV_SEN_EXCEPTION) {
-+				rv = SEN_FATAL_ERROR;
-+				PRINTKC("exception in send to AP %d\n", index);
-+				break;
-+			}
-+			PDEBUG("return value from send_to_AP: %d\n", rv);
-+			switch (dv) {
-+			case DEV_GONE:
-+				PDEBUG("dev %d not available\n", index);
-+				rv = SEN_NOT_AVAIL;
-+				break;
-+			case DEV_ONLINE:
-+				rv = 0;
-+				break;
-+			case DEV_EMPTY:
-+				rv = SEN_NOT_AVAIL;
-+				break;
-+			case DEV_NO_WORK:
-+				rv = SEN_FATAL_ERROR;
-+				break;
-+			case DEV_BAD_MESSAGE:
-+				rv = SEN_USER_ERROR;
-+				break;
-+			case DEV_QUEUE_FULL:
-+				rv = SEN_QUEUE_FULL;
-+				break;
-+			default:
-+				PRINTK("unknown dv=%d for dev %d\n", dv, index);
-+				rv = SEN_NOT_AVAIL;
-+				break;
-+			}
-+		}
-+
-+		if (rv)
-+			break;
-+
-+		for (i = 0; i < 6; i++) {
-+			mdelay(300);
-+			dv = receive_from_AP(index, z90crypt.cdx,
-+					     devPtr->dev_resp_l,
-+					     devPtr->dev_resp_p, psmid);
-+			PDEBUG("dv returned by DQ = %d\n", dv);
-+			if (dv == DEV_REC_EXCEPTION) {
-+				rv = REC_FATAL_ERROR;
-+				PRINTKC("exception in dequeue %d\n",
-+					index);
-+				break;
-+			}
-+			switch (dv) {
-+			case DEV_ONLINE:
-+				rv = 0;
-+				break;
-+			case DEV_EMPTY:
-+				rv = REC_EMPTY;
-+				break;
-+			case DEV_NO_WORK:
-+				rv = REC_NO_WORK;
-+				break;
-+			case DEV_BAD_MESSAGE:
-+			case DEV_GONE:
-+			default:
-+				rv = REC_NO_RESPONSE;
-+				break;
-+			}
-+			if ((rv != 0) && (rv != REC_NO_WORK))
-+				break;
-+			if (rv == 0)
-+				break;
-+		}
-+		if (rv)
-+			break;
-+		cprbx_p = (struct CPRBX *) (devPtr->dev_resp_p + 48);
-+		if ((cprbx_p->ccp_rtcode == 8) && (cprbx_p->ccp_rscode == 33)) {
-+			devPtr->dev_type = PCIXCC_MCL2;
-+			PDEBUG("device %d is MCL2\n", index);
-+		} else {
-+			devPtr->dev_type = PCIXCC_MCL3;
-+			PDEBUG("device %d is MCL3\n", index);
-+		}
-+	} while (0);
-+	/* In a general error case, the card is not marked online */
-+	return rv;
-+}
-+
-+#ifdef Z90CRYPT_USE_HOTPLUG
-+static void
-+z90crypt_hotplug_event(int dev_major, int dev_minor, int action)
-+{
-+#ifdef CONFIG_HOTPLUG
-+	char *argv[3];
-+	char *envp[6];
-+	char  major[20];
-+	char  minor[20];
-+
-+	sprintf(major, "MAJOR=%d", dev_major);
-+	sprintf(minor, "MINOR=%d", dev_minor);
-+
-+	argv[0] = hotplug_path;
-+	argv[1] = "z90crypt";
-+	argv[2] = 0;
-+
-+	envp[0] = "HOME=/";
-+	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
-+
-+	switch (action) {
-+	case Z90CRYPT_HOTPLUG_ADD:
-+		envp[2] = "ACTION=add";
-+		break;
-+	case Z90CRYPT_HOTPLUG_REMOVE:
-+		envp[2] = "ACTION=remove";
-+		break;
-+	default:
-+		BUG();
-+		break;
-+	}
-+	envp[3] = major;
-+	envp[4] = minor;
-+	envp[5] = 0;
-+
-+	call_usermodehelper(argv[0], argv, envp);
-+#endif
-+}
-+#endif
-+
-+module_init(z90crypt_init_module);
-+module_exit(z90crypt_cleanup_module);
-=== drivers/s390/misc/chandev.c
-==================================================================
---- drivers/s390/misc/chandev.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/misc/chandev.c   (/trunk/2.4.27)   (revision 52)
-@@ -1023,15 +1023,16 @@
- 
- 	/* 3172/2216 Paralell the 2216 allows 16 ports per card the */
- 	/* the original 3172 only allows 4 we will assume the max of 16 */
--	chandev_add_model(chandev_type_lcs|chandev_type_ctc,0x3088,0x8,-1,-1,15,default_msck_bits,FALSE,FALSE);
-+	chandev_add_model(chandev_type_lcs|chandev_type_ctc|chandev_type_ctcmpc,0x3088,0x8,-1,-1,15,default_msck_bits,FALSE,FALSE);
- 
- 	/* 3172/2216 Escon serial the 2216 allows 16 ports per card the */
- 	/* the original 3172 only allows 4 we will assume the max of 16 */
--	chandev_add_model(chandev_type_lcs|chandev_type_escon,0x3088,0x1F,-1,-1,15,default_msck_bits,FALSE,FALSE);
-+	chandev_add_model(chandev_type_lcs|chandev_type_escon|chandev_type_ctcmpc,0x3088,0x1F,-1,-1,15,default_msck_bits,FALSE,FALSE);
- 
- 	/* Only 2 ports allowed on OSA2 cards model 0x60 */
- 	chandev_add_model(chandev_type_lcs,0x3088,0x60,-1,-1,1,default_msck_bits,FALSE,FALSE);
--	/* qeth gigabit ethernet */
-+	chandev_add_model(chandev_type_claw,0x3088,0x61,-1,-1,0,default_msck_bits,FALSE,FALSE);
-+       /* qeth gigabit ethernet */
- 	chandev_add_model(chandev_type_qeth,0x1731,0x1,0x1732,0x1,0,default_msck_bits,FALSE,FALSE);
- 	chandev_add_model(chandev_type_qeth,0x1731,0x5,0x1732,0x5,0,default_msck_bits,FALSE,FALSE);
- 	/* Osa-D we currently aren't too emotionally involved with this */
-@@ -1040,7 +1041,7 @@
- 	chandev_add_model(chandev_type_claw,0x3088,0x61,-1,-1,0,default_msck_bits,FALSE,FALSE);
- 
- 	/* ficon attached ctc */
--	chandev_add_model(chandev_type_escon,0x3088,0x1E,-1,-1,0,default_msck_bits,FALSE,FALSE);
-+	chandev_add_model(chandev_type_escon|chandev_type_ctcmpc,0x3088,0x1E,-1,-1,0,default_msck_bits,FALSE,FALSE);
- }
- 
- 
-@@ -2187,6 +2188,7 @@
- {
- 	"noauto",
- 	"del_noauto",
-+	"ctcmpc",
- 	"ctc",
- 	"escon",
- 	"lcs",
-@@ -2224,6 +2226,7 @@
- 	first_stridx=0,
- 	noauto_stridx=first_stridx,
- 	del_noauto_stridx,
-+	ctcmpc_stridx,
- 	ctc_stridx,
- 	escon_stridx,
- 	lcs_stridx,
-@@ -2285,7 +2288,7 @@
- 
- 
- static char chandev_keydescript[]=
--"\nchan_type key bitfield ctc=0x1,escon=0x2,lcs=0x4,osad=0x8,qeth=0x10,claw=0x20\n";
-+"\nchan_type key bitfield ctc=0x1,escon=0x2,lcs=0x4,osad=0x8,qeth=0x10,claw=0x20,ctcmpc=0x40\n";
- 
- 
- #if  CONFIG_ARCH_S390X
-@@ -2500,10 +2503,12 @@
- 						  ints[3],ints[4],ints[5],ints[6],ints[7],
- 						  NULL,NULL,NULL);
- 				break;
-+			case (ctcmpc_stridx*stridx_mult)|isnum|iscomma:
- 			case (ctc_stridx*stridx_mult)|isnum|iscomma:
- 			case (escon_stridx*stridx_mult)|isnum|iscomma:
- 			case (lcs_stridx*stridx_mult)|isnum|iscomma:
- 			case (osad_stridx*stridx_mult)|isnum|iscomma:
-+			case (ctcmpc_stridx*stridx_mult)|iscomma:
- 			case (ctc_stridx*stridx_mult)|iscomma:
- 			case (escon_stridx*stridx_mult)|iscomma:
- 			case (lcs_stridx*stridx_mult)|iscomma:
-@@ -2513,6 +2518,9 @@
- 				case (ctc_stridx*stridx_mult):
- 					chan_type=chandev_type_ctc;
- 					break;
-+				case (ctcmpc_stridx*stridx_mult):
-+					chan_type=chandev_type_ctcmpc;
-+					break;
- 				case (escon_stridx*stridx_mult):
- 					chan_type=chandev_type_escon;
- 					break;
-=== drivers/s390/misc/z90crypt.h
-==================================================================
---- drivers/s390/misc/z90crypt.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/misc/z90crypt.h   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,258 @@
-+/*
-+ *  linux/drivers/s390/misc/z90crypt.h
-+ *
-+ *  z90crypt 1.3.2
-+ *
-+ *  Copyright (C)  2001, 2004 IBM Corporation
-+ *  Author(s): Robert Burroughs (burrough at us.ibm.com)
-+ *             Eric Rossman (edrossma at us.ibm.com)
-+ *
-+ *  Hotplug & misc device support: Jochen Roehrig (roehrig 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.
-+ */
-+
-+#ifndef _LINUX_Z90CRYPT_H_
-+#define _LINUX_Z90CRYPT_H_
-+
-+#include <linux/ioctl.h>
-+
-+#define VERSION_Z90CRYPT_H "$Revision: 1.3.4.7 $"
-+
-+#define z90crypt_VERSION 1
-+#define z90crypt_RELEASE 3	// 2 = PCIXCC, 3 = rewrite for coding standards
-+#define z90crypt_VARIANT 2	// 2 = added PCIXCC MCL3 and CEX2C support
-+
-+/**
-+ * If we are not using the sparse checker, __user has no use.
-+ */
-+#ifdef __CHECKER__
-+# define __user		__attribute__((noderef, address_space(1)))
-+#else
-+# define __user
-+#endif
-+
-+/**
-+ * struct ica_rsa_modexpo
-+ *
-+ * Requirements:
-+ * - outputdatalength is at least as large as inputdatalength.
-+ * - All key parts are right justified in their fields, padded on
-+ *   the left with zeroes.
-+ * - length(b_key) = inputdatalength
-+ * - length(n_modulus) = inputdatalength
-+ */
-+struct ica_rsa_modexpo {
-+	char __user *	inputdata;
-+	unsigned int	inputdatalength;
-+	char __user *	outputdata;
-+	unsigned int	outputdatalength;
-+	char __user *	b_key;
-+	char __user *	n_modulus;
-+};
-+
-+/**
-+ * struct ica_rsa_modexpo_crt
-+ *
-+ * Requirements:
-+ * - inputdatalength is even.
-+ * - outputdatalength is at least as large as inputdatalength.
-+ * - All key parts are right justified in their fields, padded on
-+ *   the left with zeroes.
-+ * - length(bp_key)	= inputdatalength/2 + 8
-+ * - length(bq_key)	= inputdatalength/2
-+ * - length(np_key)	= inputdatalength/2 + 8
-+ * - length(nq_key)	= inputdatalength/2
-+ * - length(u_mult_inv) = inputdatalength/2 + 8
-+ */
-+struct ica_rsa_modexpo_crt {
-+	char __user *	inputdata;
-+	unsigned int	inputdatalength;
-+	char __user *	outputdata;
-+	unsigned int	outputdatalength;
-+	char __user *	bp_key;
-+	char __user *	bq_key;
-+	char __user *	np_prime;
-+	char __user *	nq_prime;
-+	char __user *	u_mult_inv;
-+};
-+
-+#define Z90_IOCTL_MAGIC 'z'  // NOTE:  Need to allocate from linux folks
-+
-+/**
-+ * Interface notes:
-+ *
-+ * The ioctl()s which are implemented (along with relevant details)
-+ * are:
-+ *
-+ *   ICARSAMODEXPO
-+ *     Perform an RSA operation using a Modulus-Exponent pair
-+ *     This takes an ica_rsa_modexpo struct as its arg.
-+ *
-+ *     NOTE: please refer to the comments preceding this structure
-+ *           for the implementation details for the contents of the
-+ *           block
-+ *
-+ *   ICARSACRT
-+ *     Perform an RSA operation using a Chinese-Remainder Theorem key
-+ *     This takes an ica_rsa_modexpo_crt struct as its arg.
-+ *
-+ *     NOTE: please refer to the comments preceding this structure
-+ *           for the implementation details for the contents of the
-+ *           block
-+ *
-+ *   Z90STAT_TOTALCOUNT
-+ *     Return an integer count of all device types together.
-+ *
-+ *   Z90STAT_PCICACOUNT
-+ *     Return an integer count of all PCICAs.
-+ *
-+ *   Z90STAT_PCICCCOUNT
-+ *     Return an integer count of all PCICCs.
-+ *
-+ *   Z90STAT_PCIXCCMCL2COUNT
-+ *     Return an integer count of all MCL2 PCIXCCs.
-+ *
-+ *   Z90STAT_PCIXCCMCL3COUNT
-+ *     Return an integer count of all MCL3 PCIXCCs.
-+ *
-+ *   Z90STAT_CEX2CCOUNT
-+ *     Return an integer count of all CEX2Cs.
-+ *
-+ *   Z90STAT_REQUESTQ_COUNT
-+ *     Return an integer count of the number of entries waiting to be
-+ *     sent to a device.
-+ *
-+ *   Z90STAT_PENDINGQ_COUNT
-+ *     Return an integer count of the number of entries sent to a
-+ *     device awaiting the reply.
-+ *
-+ *   Z90STAT_TOTALOPEN_COUNT
-+ *     Return an integer count of the number of open file handles.
-+ *
-+ *   Z90STAT_DOMAIN_INDEX
-+ *     Return the integer value of the Cryptographic Domain.
-+ *
-+ *   Z90STAT_STATUS_MASK
-+ *     Return an 64 element array of unsigned chars for the status of
-+ *     all devices.
-+ *       0x01: PCICA
-+ *       0x02: PCICC
-+ *       0x03: PCIXCC_MCL2
-+ *       0x04: PCIXCC_MCL3
-+ *       0x05: CEX2C
-+ *       0x0d: device is disabled via the proc filesystem
-+ *
-+ *   Z90STAT_QDEPTH_MASK
-+ *     Return an 64 element array of unsigned chars for the queue
-+ *     depth of all devices.
-+ *
-+ *   Z90STAT_PERDEV_REQCNT
-+ *     Return an 64 element array of unsigned integers for the number
-+ *     of successfully completed requests per device since the device
-+ *     was detected and made available.
-+ *
-+ *   ICAZ90STATUS (deprecated)
-+ *     Return some device driver status in a ica_z90_status struct
-+ *     This takes an ica_z90_status struct as its arg.
-+ *
-+ *     NOTE: this ioctl() is deprecated, and has been replaced with
-+ *           single ioctl()s for each type of status being requested
-+ *
-+ *   Z90STAT_PCIXCCCOUNT (deprecated)
-+ *     Return an integer count of all PCIXCCs (MCL2 + MCL3).
-+ *     This is DEPRECATED now that MCL3 PCIXCCs are treated differently from
-+ *     MCL2 PCIXCCs.
-+ *
-+ *   Z90QUIESCE (not recommended)
-+ *     Quiesce the driver.  This is intended to stop all new
-+ *     requests from being processed.  Its use is NOT recommended,
-+ *     except in circumstances where there is no other way to stop
-+ *     callers from accessing the driver.  Its original use was to
-+ *     allow the driver to be "drained" of work in preparation for
-+ *     a system shutdown.
-+ *
-+ *     NOTE: once issued, this ban on new work cannot be undone
-+ *           except by unloading and reloading the driver.
-+ */
-+
-+/**
-+ * Supported ioctl calls
-+ */
-+#define ICARSAMODEXPO	_IOC(_IOC_READ|_IOC_WRITE, Z90_IOCTL_MAGIC, 0x05, 0)
-+#define ICARSACRT	_IOC(_IOC_READ|_IOC_WRITE, Z90_IOCTL_MAGIC, 0x06, 0)
-+
-+/* DEPRECATED status calls (bound for removal at some point) */
-+#define ICAZ90STATUS	_IOR(Z90_IOCTL_MAGIC, 0x10, struct ica_z90_status)
-+#define Z90STAT_PCIXCCCOUNT	_IOR(Z90_IOCTL_MAGIC, 0x43, int)
-+
-+/* unrelated to ICA callers */
-+#define Z90QUIESCE	_IO(Z90_IOCTL_MAGIC, 0x11)
-+
-+/* New status calls */
-+#define Z90STAT_TOTALCOUNT	_IOR(Z90_IOCTL_MAGIC, 0x40, int)
-+#define Z90STAT_PCICACOUNT	_IOR(Z90_IOCTL_MAGIC, 0x41, int)
-+#define Z90STAT_PCICCCOUNT	_IOR(Z90_IOCTL_MAGIC, 0x42, int)
-+#define Z90STAT_PCIXCCMCL2COUNT	_IOR(Z90_IOCTL_MAGIC, 0x4b, int)
-+#define Z90STAT_PCIXCCMCL3COUNT	_IOR(Z90_IOCTL_MAGIC, 0x4c, int)
-+#define Z90STAT_CEX2CCOUNT	_IOR(Z90_IOCTL_MAGIC, 0x4d, int)
-+#define Z90STAT_REQUESTQ_COUNT	_IOR(Z90_IOCTL_MAGIC, 0x44, int)
-+#define Z90STAT_PENDINGQ_COUNT	_IOR(Z90_IOCTL_MAGIC, 0x45, int)
-+#define Z90STAT_TOTALOPEN_COUNT _IOR(Z90_IOCTL_MAGIC, 0x46, int)
-+#define Z90STAT_DOMAIN_INDEX	_IOR(Z90_IOCTL_MAGIC, 0x47, int)
-+#define Z90STAT_STATUS_MASK	_IOR(Z90_IOCTL_MAGIC, 0x48, char[64])
-+#define Z90STAT_QDEPTH_MASK	_IOR(Z90_IOCTL_MAGIC, 0x49, char[64])
-+#define Z90STAT_PERDEV_REQCNT	_IOR(Z90_IOCTL_MAGIC, 0x4a, int[64])
-+
-+/**
-+ * local errno definitions
-+ */
-+#define ENOBUFF	  129	// filp->private_data->...>work_elem_p->buffer is NULL
-+#define EWORKPEND 130	// user issues ioctl while another pending
-+#define ERELEASED 131	// user released while ioctl pending
-+#define EQUIESCE  132	// z90crypt quiescing (no more work allowed)
-+#define ETIMEOUT  133	// request timed out
-+#define EUNKNOWN  134	// some unrecognized error occured (retry may succeed)
-+#define EGETBUFF  135	// Error getting buffer or hardware lacks capability
-+			// (retry in software)
-+
-+/**
-+ * DEPRECATED STRUCTURES
-+ */
-+
-+/**
-+ * This structure is DEPRECATED and the corresponding ioctl() has been
-+ * replaced with individual ioctl()s for each piece of data!
-+ * This structure will NOT survive past version 1.3.1, so switch to the
-+ * new ioctl()s.
-+ */
-+#define MASK_LENGTH 64 // mask length
-+struct ica_z90_status {
-+	int totalcount;
-+	int leedslitecount; // PCICA
-+	int leeds2count;    // PCICC
-+	// int PCIXCCCount; is not in struct for backward compatibility
-+	int requestqWaitCount;
-+	int pendingqWaitCount;
-+	int totalOpenCount;
-+	int cryptoDomain;
-+	// status: 0=not there, 1=PCICA, 2=PCICC, 3=PCIXCC_MCL2, 4=PCIXCC_MCL3,
-+	//         5=CEX2C
-+	unsigned char status[MASK_LENGTH];
-+	// qdepth: # work elements waiting for each device
-+	unsigned char qdepth[MASK_LENGTH];
-+};
-+
-+#endif /* _LINUX_Z90CRYPT_H_ */
-=== drivers/s390/misc/Makefile
-==================================================================
---- drivers/s390/misc/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/misc/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -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)
-=== drivers/s390/Makefile
-==================================================================
---- drivers/s390/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -4,12 +4,17 @@
- 
- 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
- 
-+obj-$(CONFIG_S390_CMF) += cmf.o
-+export-objs += cmf.o
-+
- obj-y += $(foreach dir,$(subdir-y),$(dir)/s390-$(dir).o)
- 
- include $(TOPDIR)/Rules.make
-=== drivers/s390/char/tape.h
-==================================================================
---- drivers/s390/char/tape.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape.h   (/trunk/2.4.27)   (revision 52)
-@@ -1,203 +1,436 @@
--/***************************************************************************
-- *
-+/*
-  *  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>
-- *
-- ****************************************************************************
-+ *		 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
-+#define _TAPE_H
- 
--#define _TAPE_H
- #include <linux/config.h>
- #include <linux/blkdev.h>
-+#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
-+#include <linux/devfs_fs_kernel.h>
-+#endif
- 
--#define  MAX_TAPES                     7        /* Max tapes supported is 7*/
--#define TAPE_MAGIC 0xE3C1D7C5       /* is ebcdic-"TAPE" */
-+/*
-+ * 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)
- 
--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;
-+#define DBF_EXCEPTION(d_level, d_str...) \
-+do { \
-+	debug_sprintf_exception(TAPE_DBF_AREA, d_level, d_str); \
-+} while (0)
- 
--struct _tape_info_t; //Forward declaration
-+#define TAPE_VERSION_MAJOR 2
-+#define TAPE_VERSION_MINOR 0
-+#define TAPE_MAGIC "tape"
- 
--typedef enum {
--    TE_START=0, TE_DONE, TE_FAILED, TE_ERROR, TE_OTHER,
--    TE_SIZE } tape_events;
-+#define TAPE_MINORS_PER_DEV 2       /* two minors per device */
-+#define TAPEBLOCK_HSEC_SIZE	2048
-+#define TAPEBLOCK_HSEC_S2B	2
-+#define TAPEBLOCK_RETRIES	5
- 
--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*);
-+/* 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
--typedef void (*tape_devfs_handler_t) (struct _tape_info_t*);
--#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)));
-+	/* 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 */
-+};
- 
--typedef struct _tape_frontend_t {
--    tape_setup_assist_t device_setup;
-+#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
- 
--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;
-+#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;
-+
- #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)));
-+};
- 
-+/* 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);
-+
- /* tape initialisation functions */
--int tape_init(void);
--int tape_setup (tape_info_t * ti, int irq, int minor);
-+void tape_proc_init (void);
-+void tape_proc_cleanup (void);
- 
--/* 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);
--
- /* a function for dumping device sense info */
--void tape_dump_sense (devstat_t * stat);
-+void tape_dump_sense(struct tape_device *, struct tape_request *);
-+void tape_dump_sense_dbf(struct tape_device *, struct tape_request *);
- 
--#ifdef CONFIG_S390_TAPE_DYNAMIC
--/* functions for dyn. dev. attach/detach */
--int tape_oper_handler ( int irq, struct _devreg *dreg);
--#endif
--
- /* 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];
-+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);
- 
--/* 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;
--
- /* The debug area */
--#ifdef TAPE_DEBUG
--extern debug_info_t *tape_debug_area;
--#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 */
-=== drivers/s390/char/sclp_con.c
-==================================================================
---- drivers/s390/char/sclp_con.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/sclp_con.c   (/trunk/2.4.27)   (revision 52)
-@@ -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);
-+}
-=== drivers/s390/char/sclp_tty.c
-==================================================================
---- drivers/s390/char/sclp_tty.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/sclp_tty.c   (/trunk/2.4.27)   (revision 52)
-@@ -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);
-+}
-=== drivers/s390/char/ctrlchar.c
-==================================================================
---- drivers/s390/char/ctrlchar.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/ctrlchar.c   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-=== drivers/s390/char/tape_std.c
-==================================================================
---- drivers/s390/char/tape_std.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape_std.c   (/trunk/2.4.27)   (revision 52)
-@@ -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);
-=== drivers/s390/char/sclp_tty.h
-==================================================================
---- drivers/s390/char/sclp_tty.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/sclp_tty.h   (/trunk/2.4.27)   (revision 52)
-@@ -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__ */
-=== drivers/s390/char/tape_std.h
-==================================================================
---- drivers/s390/char/tape_std.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape_std.h   (/trunk/2.4.27)   (revision 52)
-@@ -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
-=== drivers/s390/char/tape_34xx.c
-==================================================================
---- drivers/s390/char/tape_34xx.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape_34xx.c   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-+ */
-+
-+#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
-+
-+#include "tape.h"
-+#include "tape_std.h"
-+
-+#define PRINTK_HEADER "T34xx:"
-+
-+/*
-+ * Pointer to debug area.
-+ */
-+debug_info_t *TAPE_DBF_AREA = NULL;
-+
-+/*
-+ * 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.
-+ */
-+#define TBI_FORMAT_3480			0x00
-+#define TBI_FORMAT_3480_2_XF		0x01
-+#define TBI_FORMAT_3480_XF		0x02
-+#define TBI_FORMAT_RESERVED		0x03
-+
-+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));
-+
-+struct sbid_entry {
-+	struct list_head		list;
-+	struct tape_34xx_block_id	bid;
-+};
-+
-+struct tape_34xx_discdata {
-+	/* A list of block id's of the tape segments (for faster seek) */
-+	struct list_head		sbid_list;
-+};
-+
-+/* 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)
-+{
-+	unsigned char *sense = request->cpdata;
-+
-+	request->callback = NULL;
-+
-+	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));
-+
-+	if(sense[0] & SENSE_INTERVENTION_REQUIRED) {
-+		tape_med_state_set(request->device, MS_UNLOADED);
-+	} else {
-+		tape_med_state_set(request->device, MS_LOADED);
-+	}
-+
-+	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);
-+	}
-+
-+	tape_put_request(request);
-+}
-+
-+static int
-+tape_34xx_medium_sense(struct tape_device *device)
-+{
-+	struct tape_request *	request;
-+	int			rc;
-+
-+	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);
-+	}
-+
-+	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;
-+}
-+
-+static void
-+tape_34xx_work_handler(void *data)
-+{
-+	struct {
-+		struct tape_device	*device;
-+		enum tape_op		 op;
-+		struct tq_struct	 task;
-+	} *p = data;
-+
-+	switch(p->op) {
-+		case TO_MSEN:
-+			tape_34xx_medium_sense(p->device);
-+			break;
-+		default:
-+			DBF_EVENT(3, "T34XX: internal error: unknown work\n");
-+	}
-+
-+	tape_put_device(p->device);
-+	kfree(p);
-+}
-+
-+/*
-+ * 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.
-+ */
-+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;
-+
-+	p->device = tape_clone_device(device);
-+	p->op     = op;
-+
-+	schedule_task(&p->task);
-+
-+	return 0;
-+}
-+
-+/*
-+ * 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);
-+
-+	return TAPE_IO_SUCCESS;
-+}
-+
-+static inline int
-+tape_34xx_erp_failed(struct tape_device *device,
-+		     struct tape_request *request, int rc)
-+{
-+	DBF_EVENT(3, "Error recovery failed for %s\n",
-+		  tape_op_verbose[request->op]);
-+	return rc;
-+}
-+
-+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);
-+}
-+
-+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;
-+}
-+
-+/*
-+ * This function is called, when no request is outstanding and we get an
-+ * interrupt
-+ */
-+static int
-+tape_34xx_unsolicited_irq(struct tape_device *device)
-+{
-+	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;
-+}
-+
-+/*
-+ * Read Opposite Error Recovery Function:
-+ * Used, when Read Forward does not work
-+ */
-+static int
-+tape_34xx_erp_read_opposite(struct tape_device *device,
-+			    struct tape_request *request)
-+{
-+	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);
-+}
-+
-+static int
-+tape_34xx_erp_bug(struct tape_device *device,
-+		  struct tape_request *request, int no)
-+{
-+	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);
-+}
-+
-+/*
-+ * Handle data overrun between cu and drive. The channel speed might
-+ * be too slow.
-+ */
-+static int
-+tape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request)
-+{
-+	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);
-+	}
-+	return tape_34xx_erp_bug(device, request, -1);
-+}
-+	
-+/*
-+ * Handle record sequence error.
-+ */
-+static int
-+tape_34xx_erp_sequence(struct tape_device *device,
-+		       struct tape_request *request)
-+{
-+	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);
-+}
-+
-+/*
-+ * 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;
-+
-+	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
-+
-+	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);
-+		}
-+	}
-+
-+	/*
-+	 * 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);
-+		}
-+	}
-+
-+	/* Sensing special bits */
-+	if (sense[0] & SENSE_BUS_OUT_CHECK)
-+		return tape_34xx_erp_retry(device, request);
-+
-+	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);
-+
-+			/* 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);
-+
-+			// 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);
-+		}
-+	}
-+
-+	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;
-+			}
-+		}
-+		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);
-+
-+		/* 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]);
-+	}
-+}
-+
-+/*
-+ * 3480/3490 interrupt handler
-+ */
-+static int
-+tape_34xx_irq(struct tape_device *device, struct tape_request *request)
-+{
-+	if (request == NULL)
-+		return tape_34xx_unsolicited_irq(device);
-+
-+	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);
-+	}
-+
-+	if ((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) &&
-+	    (request->op == TO_BSB || request->op == TO_FSB))
-+		DBF_EVENT(5, "Skipped over tapemark\n");
-+
-+	if (device->devstat.dstat & DEV_STAT_UNIT_CHECK)
-+		return tape_34xx_unit_check(device, request);
-+
-+	if (device->devstat.dstat & DEV_STAT_DEV_END)
-+		return tape_34xx_done(device, request);
-+
-+	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;
-+}
-+
-+/*
-+ * ioctl_overload
-+ */
-+static int
-+tape_34xx_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)
-+{
-+        if (cmd == TAPE390_DISPLAY) {
-+		struct display_struct disp;
-+
-+		if(copy_from_user(&disp, (char *) arg, sizeof(disp)) != 0)
-+			return -EFAULT;
-+
-+	        return tape_std_display(device, &disp);
-+	} else
-+                return -EINVAL;
-+}
-+
-+static int
-+tape_34xx_setup_device(struct tape_device * device)
-+{
-+	struct tape_34xx_discdata *discdata;
-+
-+	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;
-+	}
-+
-+	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...
-+ */
-+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;
-+
-+	if(discdata == NULL)
-+		return;
-+	if((new = kmalloc(sizeof(struct sbid_entry), GFP_ATOMIC)) == NULL)
-+		return;
-+
-+	new->bid = bid;
-+	new->bid.tbi_format = 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);
-+
-+		/*
-+		 * 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;
-+		}
-+
-+		/*
-+		 * 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;
-+		}
-+	}
-+
-+	/*
-+	 * 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);
-+	}
-+
-+	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);
-+	}
-+
-+	return;	
-+}
-+
-+/*
-+ * 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.
-+ */
-+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;
-+
-+	list_for_each(l, &discdata->sbid_list) {
-+		cur = list_entry(l, struct sbid_entry, list);
-+
-+		if(cur->bid.tbi_block > bid->tbi_block)
-+			break;
-+	}
-+
-+	/* 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;
-+}
-+
-+static void
-+tape_34xx_clear_sbid_list(struct tape_device *device)
-+{
-+	struct list_head *		l;
-+	struct list_head *		n;
-+	struct tape_34xx_discdata *	discdata;
-+
-+	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));
-+	}
-+}
-+
-+/*
-+ * MTTELL: Tell block. Return the number of block relative to current file.
-+ */
-+int
-+tape_34xx_mttell(struct tape_device *device, int mt_count)
-+{
-+	struct tape_34xx_block_id	bid;
-+	int				rc;
-+
-+	rc = tape_std_read_block_id(device, (unsigned int *) &bid);
-+	if (rc)
-+		return rc;
-+
-+	/*
-+	 * Build up a lookup table. The format id is ingored.
-+	 */
-+	tape_34xx_add_sbid(device, bid);
-+
-+	return bid.tbi_block;
-+}
-+
-+/*
-+ * MTSEEK: seek to the specified block.
-+ */
-+int
-+tape_34xx_mtseek(struct tape_device *device, int mt_count)
-+{
-+	struct tape_34xx_block_id	bid;
-+
-+	if (mt_count > 0x400000) {
-+		DBF_EXCEPTION(6, "xsee parm\n");
-+		return -EINVAL;
-+	}
-+
-+	bid.tbi_block   = mt_count;
-+
-+	/*
-+	 * Set hardware seek information in the block id.
-+	 */
-+	tape_34xx_merge_sbid(device, &bid);
-+
-+	return tape_std_seek_block_id(device, *((unsigned int *) &bid));
-+}
-+
-+/*
-+ * Tape block read for 34xx.
-+ */
-+#ifdef CONFIG_S390_TAPE_BLOCK
-+struct tape_request *
-+tape_34xx_bread(struct tape_device *device, struct request *req)
-+{
-+	struct tape_request  *request;
-+	struct buffer_head   *bh;
-+	ccw1_t               *ccw;
-+	int                   count;
-+	int                   size;
-+
-+	DBF_EVENT(6, "tape_34xx_bread(sector=%u,size=%u)\n",
-+		req->sector, req->nr_sectors);
-+
-+	/* 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++;
-+	}
-+
-+	/* 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++;
-+		}
-+	}
-+
-+	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
-+
-+	return request;
-+}
-+
-+void
-+tape_34xx_free_bread (struct tape_request *request)
-+{
-+	ccw1_t* ccw = request->cpaddr;
-+
-+	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");
-+	}
-+
-+	/* 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);
-+}
-+
-+/*
-+ * 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.
-+ */
-+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;
-+
-+	/*
-+	 * The tape is already at the correct position. No seek needed.
-+	 */
-+	if (id->tbi_block == device->blk_data.block_position)
-+		return;
-+
-+	/*
-+	 * 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;
-+
-+	/*
-+	 * 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);
-+}
-+
-+/*
-+ * 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
-+};
-+
-+/*
-+ * 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
-+};
-+
-+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
-+};
-+
-+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");
-+
-+module_init(tape_34xx_init);
-+module_exit(tape_34xx_exit);
-=== drivers/s390/char/tuball.c
-==================================================================
---- drivers/s390/char/tuball.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tuball.c   (/trunk/2.4.27)   (revision 52)
-@@ -29,10 +29,8 @@
- MODULE_PARM(tubdebug, "i");
- MODULE_PARM(tubscrolltime, "i");
- MODULE_PARM(tubxcorrect, "i");
--#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
--MODULE_LICENSE ("GPL");
-+MODULE_LICENSE("GPL");
- #endif
--#endif
- /*
-  * Values for tubdebug and their effects:
-  * 1 - print in hex on console the first 16 bytes received
-=== drivers/s390/char/vmlogrdr.c
-==================================================================
---- drivers/s390/char/vmlogrdr.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/vmlogrdr.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,741 @@
-+/*
-+ * drivers/s390/char/vmlogrdr.c
-+ *	character device driver for reading z/VM system service records
-+ *
-+ *
-+ *	Copyright (C) 2004 IBM Corporation
-+ *	character device driver for reading z/VM system service records,
-+ *	Version 1.0
-+ *	Author(s): Xenia Tkatschow <xenia at us.ibm.com>
-+ *		   Stefan Weinhuber <wein at de.ibm.com>
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/errno.h>
-+#include <linux/types.h>
-+#include <linux/interrupt.h>
-+#include <linux/spinlock.h>
-+#include <asm/atomic.h>
-+#include <asm/uaccess.h>
-+#include <asm/cpcmd.h>
-+#include <asm/debug.h>
-+#include <asm/ebcdic.h>
-+#include "../net/iucv.h"
-+#include <linux/kmod.h>
-+
-+
-+
-+#define MAXSERVICES 1
-+
-+enum vmlogrdr_hotplug_action { 
-+	VMLOGRDR_HOTPLUG_ADD=0,
-+	VMLOGRDR_HOTPLUG_REMOVE=1
-+};
-+
-+MODULE_AUTHOR
-+	("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia at us.ibm.com)\n"
-+	 "                            Stefan Weinhuber (wein at de.ibm.com)");
-+MODULE_DESCRIPTION ("Character device driver for reading z/VM "
-+		    "system service records.");
-+MODULE_PARM (services, "1-"__MODULE_STRING(MAXSERVICES)"s");
-+MODULE_PARM_DESC (services,
-+		"Specify the system services\n"
-+		"services=system_service0,system_service1,..,"
-+		"system_serviceN\n");
-+MODULE_LICENSE("GPL");
-+
-+static char *services[MAXSERVICES];
-+
-+char userid[9];
-+
-+static char FENCE[] = {"EOR"};
-+static int logreader_major = 0;
-+
-+static int logreader_open(struct inode *, struct file *);
-+static int logreader_release(struct inode *, struct file *);
-+static ssize_t logreader_read (struct file *filp, char *data, size_t count,
-+			       loff_t * ppos);
-+
-+
-+
-+/*
-+ * File operation structures for logreader devices
-+ */
-+static struct file_operations logreader_fops = {
-+	.owner=THIS_MODULE,
-+	.open =	logreader_open,
-+	.release = logreader_release,
-+	.read =	logreader_read,
-+};
-+
-+
-+static __u8 iucvMagic[16] = {
-+	0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
-+	0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
-+};
-+
-+static __u8 mask[] = {
-+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-+};
-+
-+static __u8 iucv_host[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-+
-+
-+static void
-+logreader_iucv_ConnectionComplete(iucv_ConnectionComplete *eib,
-+			void *pgm_data);
-+
-+static void
-+logreader_iucv_ConnectionSevered(iucv_ConnectionSevered *eib,
-+			void *pgm_data);
-+
-+static void
-+logreader_iucv_MessagePending(iucv_MessagePending *eib,
-+			void *pgm_data);
-+
-+/*
-+ * Name: iucv_interrupt_ops_t
-+ * Descriptor: Defines the iucv interrupt routines that the IUCV driver
-+ *	will call when there is an interrupt for the logreader
-+ *	character device driver
-+ * Members:
-+ *	ConnectionSevered: This routine will be called by the IUCV driver
-+ *		if the system service severes the iucv connection.
-+ *	MessagePending: This routine will be called be the IUCV driver
-+ *		when it receives a message pending interrupt.
-+ * Note: The remaining routines are not defined as we do not expect to
-+ *	handle these situations.
-+ */
-+static iucv_interrupt_ops_t logreader_iucvops = {
-+	.ConnectionComplete=	logreader_iucv_ConnectionComplete,
-+	.ConnectionSevered=	logreader_iucv_ConnectionSevered,
-+	.MessagePending=	logreader_iucv_MessagePending,
-+};
-+
-+
-+
-+#define buflen	4088
-+
-+struct logreader_priv_t {
-+	char system_service[8];
-+	u16 pathid;
-+	int connection_established;
-+	int iucv_path_severed;
-+	iucv_MessagePending local_interrupt_buffer;
-+	atomic_t receive_ready;
-+	iucv_handle_t iucv_handle;
-+	int minor_num;
-+	char * buffer;
-+	char * current_position;
-+	int remaining;
-+	ulong residual_length;
-+	int buffer_free;
-+	int dev_in_use; /* 1: already opened, 0: not opened*/
-+	spinlock_t priv_lock;
-+};
-+
-+DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue);
-+DECLARE_WAIT_QUEUE_HEAD(read_wait_queue);
-+
-+/*
-+ * pointer to system service private structure
-+ * minor number 0 --> logrec
-+ * minor number 1 --> account	 -> not yet implemented
-+ * minor number 2 --> symptom    -> not yet implemented
-+ */
-+#define MAXMINOR	1
-+static struct logreader_priv_t * sys_ser[MAXMINOR];
-+
-+
-+static void
-+logreader_iucv_ConnectionComplete (iucv_ConnectionComplete * eib,
-+				   void * pgm_data)
-+{
-+	struct logreader_priv_t * logptr =
-+		(struct logreader_priv_t *) pgm_data;
-+	spin_lock(&logptr->priv_lock);
-+	logptr->connection_established = 1;
-+	spin_unlock(&logptr->priv_lock);
-+	wake_up(&conn_wait_queue);
-+	return;
-+}
-+
-+
-+static void
-+logreader_iucv_ConnectionSevered (iucv_ConnectionSevered * eib, void * pgm_data)
-+{
-+	
-+	u8 reason = (u8) eib->ipuser[8];
-+	struct logreader_priv_t * logptr = (struct logreader_priv_t *) pgm_data;
-+
-+	printk (KERN_ERR "vmlogrdr: connection severed with"
-+		" reason %i/n", reason);
-+
-+	spin_lock(&logptr->priv_lock);	
-+	logptr->connection_established = 0;
-+	logptr->iucv_path_severed = 1;
-+	spin_unlock(&logptr->priv_lock);
-+
-+	wake_up(&conn_wait_queue);
-+	/* just in case we're sleeping waiting for a record */
-+	wake_up_interruptible(&read_wait_queue);
-+	
-+	return;
-+}
-+
-+
-+static void
-+logreader_iucv_MessagePending (iucv_MessagePending * eib, void * pgm_data)
-+{
-+	struct logreader_priv_t * logptr = (struct logreader_priv_t *) pgm_data;
-+			
-+	/*
-+	 * This function is the bottom half so it should be quick.
-+	 * Copy the external interrupt data into our local eib and increment
-+	 * the usage count
-+	 */
-+	spin_lock(&logptr->priv_lock);
-+	memcpy(&(logptr->local_interrupt_buffer), eib,
-+	       sizeof(iucv_MessagePending));	
-+	atomic_inc(&logptr->receive_ready);
-+	spin_unlock(&logptr->priv_lock);
-+	
-+	wake_up_interruptible(&read_wait_queue);
-+
-+	return;
-+}
-+
-+static int
-+logreader_open (struct inode *inode, struct file *filp)
-+{
-+	int dev_num = 0;
-+	struct logreader_priv_t * logptr = NULL;
-+	int connect_rc = 0;
-+
-+	char cp_command[80];
-+	char cp_response[80];
-+	unsigned long flags;
-+
-+	/* extract the minor number */
-+	dev_num = MINOR(inode->i_rdev);
-+
-+
-+	/* located the private structure of dev_num */
-+	if (dev_num > MAXMINOR)
-+		return -ENODEV;
-+
-+	logptr = sys_ser[dev_num];
-+	if (logptr == NULL)
-+		return -ENODEV;
-+
-+	/*
-+	 * only allow for blocking reads to be open
-+	 */
-+	if (filp->f_flags & O_NONBLOCK)
-+		return -ENOSYS;
-+
-+	/* Besure this device hasn't already been opened */
-+	spin_lock_irqsave(&logptr->priv_lock, flags);
-+	if (logptr->dev_in_use)	{
-+		spin_unlock_irqrestore(&logptr->priv_lock, flags);
-+		return -EBUSY;
-+	} else {
-+		logptr->dev_in_use = 1;
-+		spin_unlock_irqrestore(&logptr->priv_lock, flags);
-+	}
-+
-+	/* set the file options */
-+	filp->private_data = logptr;
-+	filp->f_op = &logreader_fops;
-+
-+
-+	/*
-+	 * Purge "old" CP records that have been collected but not retrieved and
-+	 * request CP to begin collecting records on behalf of this virtual
-+	 * machine.
-+	 * "RECORDING EREP ON QID userid PURGE"
-+	 */
-+	memset(cp_command, 0x00, sizeof(cp_command));
-+	memset(cp_response, 0x00, sizeof(cp_response));
-+
-+	/* Set up CP command depending on the system service */
-+	switch(dev_num) {
-+		case 0:
-+			sprintf(cp_command, "RECORDING EREP ON QID %s PURGE",
-+				userid);
-+			break;
-+		default:
-+			return -ENODEV;
-+	}
-+	
-+	cpcmd(cp_command, cp_response, 80);
-+	
-+	/* Register with iucv driver */
-+	logptr->iucv_handle = iucv_register_program(iucvMagic,
-+			logptr->system_service, mask, &logreader_iucvops,
-+			logptr);
-+
-+	if (logptr->iucv_handle == NULL) {
-+		printk (KERN_ERR "vmlogrdr: failed to register with"
-+			"iucv driver\n");
-+		
-+		/* Set up CP command depending on the system service */
-+		memset(cp_command, 0x00, sizeof(cp_command));
-+		memset(cp_response, 0x00, sizeof(cp_response));
-+		switch(dev_num) {
-+			case 0:
-+				sprintf(cp_command,"RECORDING EREP OFF QID %s ",
-+					userid);
-+				break;
-+			default:
-+				return -ENODEV;
-+		}
-+		cpcmd(cp_command, cp_response, 80);
-+		logptr->dev_in_use = 0;
-+		return -EIO;
-+	}
-+
-+	/* create connection to the system service */
-+	spin_lock_irqsave(&logptr->priv_lock, flags);
-+	logptr->connection_established = 0;
-+	logptr->iucv_path_severed = 0;
-+	spin_unlock_irqrestore(&logptr->priv_lock, flags);
-+	
-+	connect_rc = iucv_connect (&(logptr->pathid), 10, iucvMagic,
-+					logptr->system_service, iucv_host, 0,
-+					NULL, NULL,
-+					logptr->iucv_handle, NULL);
-+	if (connect_rc) {
-+		printk (KERN_ERR "vmlogrdr: iucv connection to %s "
-+			"failed with rc %i \n", logptr->system_service,
-+			connect_rc);
-+
-+		/* Set up CP command depending on the system service */
-+		memset(cp_command, 0x00, sizeof(cp_command));
-+		memset(cp_response, 0x00, sizeof(cp_response));
-+		switch(dev_num) {
-+			case 0:
-+				sprintf(cp_command, "RECORDING EREP OFF QID %s ",
-+					userid);
-+				break;
-+			default:
-+				return -ENODEV;
-+		}
-+		cpcmd(cp_command, cp_response, 80);
-+
-+		iucv_unregister_program(logptr->iucv_handle);
-+		logptr->iucv_handle = NULL;
-+		logptr->dev_in_use = 0;
-+		return -EIO;
-+	}
-+
-+	/* We've issued the connect and now we must wait for a
-+	 * ConnectionComplete or ConnectinSevered Interrupt
-+	 * before we can continue to process.
-+	 * note: When the condition in the second parameter is true,
-+	 *       we'll wake up
-+	 */
-+	wait_event(conn_wait_queue, (logptr->connection_established)
-+		|| (logptr->iucv_path_severed));
-+
-+	
-+	if (logptr->iucv_path_severed) {
-+		iucv_unregister_program(logptr->iucv_handle);
-+		logptr->iucv_handle = NULL;
-+
-+		memset(cp_command, 0x00, sizeof(cp_command));
-+		memset(cp_response, 0x00, sizeof(cp_response));
-+
-+		switch(dev_num) {
-+			case 0:
-+				sprintf(cp_command, "RECORDING EREP OFF QID %s ",
-+					userid);
-+				break;
-+			default:
-+				return -ENODEV;
-+		}
-+
-+		cpcmd(cp_command, cp_response, 80);
-+
-+		logptr->dev_in_use = 0;
-+		return -EIO;
-+	}
-+
-+	 	
-+	return 0;
-+}
-+
-+static int
-+logreader_release (struct inode *inode, struct file *filp)
-+{
-+	int dev_num = 0;
-+	struct logreader_priv_t * logptr =
-+		(struct logreader_priv_t *) filp->private_data;
-+
-+	char cp_command[80];
-+	char cp_response[80];
-+
-+	if (logptr == NULL )
-+		return -ENODEV;
-+	
-+	if (logptr->iucv_handle == NULL)
-+		return 0;
-+
-+
-+	 /* extract the minor number */
-+	dev_num = MINOR(inode->i_rdev);
-+
-+	iucv_unregister_program(logptr->iucv_handle);
-+	
-+	/* Set data structures back to original state
-+	 * 1. private struct
-+	 * 2. buffer pointer structure (binary, hex buffers)
-+	 * 3. buffer
-+	 */
-+	logptr->iucv_handle = NULL;
-+	
-+	memset(&(logptr->local_interrupt_buffer), 0x00,
-+		sizeof(iucv_MessagePending));
-+	atomic_set(&logptr->receive_ready, 0);
-+	logptr->current_position = logptr->buffer;
-+	logptr->remaining = 0;
-+	logptr->residual_length = 0;
-+	logptr->buffer_free = 1;	
-+
-+
-+	/* Purge any records remaining in CP storage and turn recording off
-+	 * "RECORDING record_type OFF QID userid PURGE"
-+	 */
-+	memset(cp_command, 0x00, sizeof(cp_command));
-+	memset(cp_response, 0x00, sizeof(cp_response));
-+
-+	/* Set up the CP command according to system service */
-+	switch(dev_num) {
-+		case 0:
-+			sprintf(cp_command, "RECORDING EREP OFF QID %s PURGE",
-+				userid);
-+			break;
-+		default:
-+			return -ENODEV;
-+	}
-+	cpcmd(cp_command, cp_response, 80);
-+
-+	logptr->dev_in_use = 0;
-+
-+	return 0;
-+}
-+
-+
-+static ssize_t
-+logreader_read (struct file *filp, char *data, size_t count, loff_t * ppos)
-+{
-+	int rc = 0;
-+	struct logreader_priv_t * logptr =
-+		(struct logreader_priv_t *) filp->private_data;
-+	int new_record = 0;
-+	int total_record_length = 0;
-+	unsigned long	flags;
-+	while (logptr->buffer_free) {
-+
-+		/* assume we're not going to receive a a record by
-+		 * setting rc != 0
-+		 */
-+		rc = 1;
-+		spin_lock_irqsave(&logptr->priv_lock, flags);
-+		if (atomic_read(&logptr->receive_ready)) {
-+			/*
-+		 	 * first check whether we need to receive the second
-+			 * half of a record (residual_length != 0).
-+		 	 */
-+		
-+			if (logptr->residual_length){
-+				/* collecting part of the record */
-+				logptr->remaining = logptr->residual_length;
-+				logptr->current_position = logptr->buffer;
-+			} else {
-+				/* beginning a new record */
-+				logptr->remaining = (int)
-+					logptr->local_interrupt_buffer.ln1msg2.ipbfln1f;
-+				logptr->current_position = logptr->buffer + 4;
-+				total_record_length = (int)
-+					logptr->local_interrupt_buffer.ln1msg2.ipbfln1f;
-+				new_record = 1;
-+			}
-+			if (logptr->remaining > buflen)
-+				logptr->remaining = buflen;
-+
-+			
-+			rc = iucv_receive(logptr->pathid,
-+					  logptr->local_interrupt_buffer.ipmsgid,
-+					  logptr->local_interrupt_buffer.iptrgcls,
-+					  logptr->current_position,
-+					  logptr->remaining,
-+					  NULL,
-+					  NULL,
-+					  &logptr->residual_length);
-+
-+			if (!logptr->residual_length)
-+				atomic_dec(&logptr->receive_ready);
-+		}
-+		spin_unlock_irqrestore(&logptr->priv_lock, flags);
-+
-+		if (rc) {
-+			wait_event_interruptible(read_wait_queue,
-+					 atomic_read(&logptr->receive_ready));
-+			if (atomic_read(&logptr->receive_ready) == 0)
-+				return -ERESTARTSYS; /* woken up by signal */
-+
-+		} else {
-+			/*
-+			 * just received some data so we must mark the
-+			 * buffer busy
-+			 */
-+			logptr->buffer_free = 0;
-+			if (new_record) {
-+				/*
-+				 * if we just received a new record then we
-+				 * need to add the header length.
-+				 * To the first 4 bytes of the buffer we add
-+				 * the length field, which is record + fence
-+				 */
-+				int * total_record_length_ptr =
-+					(int *)logptr->buffer;
-+	
-+				*total_record_length_ptr =
-+					total_record_length + 4;
-+	
-+				logptr->remaining += 4;
-+				logptr->current_position = logptr->buffer;
-+				
-+				new_record = 0;
-+			}
-+
-+			if (logptr->residual_length == 0){
-+				/* the whole record has been captured,
-+				 * now add the fence */
-+				char * temp_position = logptr->current_position
-+						       + logptr->remaining;
-+				memcpy(temp_position, FENCE, sizeof(FENCE));
-+				logptr->remaining += 4;
-+			}
-+		}				
-+
-+	}
-+	/* copy only up to end of record */
-+	if (count > logptr->remaining)
-+		count = logptr->remaining;
-+	
-+	if (copy_to_user(data, logptr->current_position, count))
-+		return -EFAULT;
-+
-+	*ppos += count;
-+	logptr->current_position += count;
-+	logptr->remaining -= count;
-+
-+	/* if all data has been transferred, set buffer free */
-+	if (logptr->remaining == 0)
-+		logptr->buffer_free = 1;
-+
-+	return count;
-+
-+
-+}
-+
-+
-+static void
-+get_vm_usrid (char * userid)
-+{
-+	char cp_response[81];
-+	cpcmd("Q USERID", cp_response, 80);
-+	cp_response[80]=0;
-+	memcpy(userid, cp_response, 8);
-+	printk (KERN_DEBUG "vmlogrdr: VM guest id: %s\n", userid);
-+	return;
-+}
-+
-+
-+static void
-+logreader_hotplug_event(int devmaj, struct logreader_priv_t *logptr, 
-+			enum vmlogrdr_hotplug_action action) {
-+#ifdef CONFIG_HOTPLUG
-+	char *argv[3];
-+	char *envp[8];
-+	char  major[20];
-+	char  minor[20];
-+	char  service[20];
-+	char *firstblank;	
-+				 
-+	sprintf(major, "MAJOR=%d",   devmaj);
-+	sprintf(minor, "MINOR=%d",   logptr->minor_num);
-+	// remember: system_service starts with an * and padded with blanks
-+	sprintf(service, "SERVICE=%.7s", logptr->system_service+1);
-+	firstblank = strchr(service, ' ');
-+	if (firstblank != NULL) {
-+		*firstblank=0;
-+	}
-+
-+	argv[0] = hotplug_path;
-+	argv[1] = "vmlogrdr";
-+	argv[2] = NULL;
-+
-+	envp[0] = "HOME=/";
-+	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
-+
-+	switch(action) {
-+	case VMLOGRDR_HOTPLUG_ADD: 
-+		envp[2] = "ACTION=add";
-+		break;
-+	case VMLOGRDR_HOTPLUG_REMOVE: 
-+		envp[2] = "ACTION=remove";
-+		break;
-+	default:
-+		BUG();
-+	}
-+	envp[3] = major;
-+	envp[4] = minor;
-+	envp[5] = service;
-+	envp[6] = NULL;
-+
-+	call_usermodehelper(argv[0], argv, envp);
-+#endif
-+}
-+
-+
-+
-+static int
-+logreader_init(void)
-+{
-+	char temp_system[9];
-+	struct logreader_priv_t * logptr = NULL;
-+	int rc=0;
-+	int i;
-+
-+	if (! MACHINE_IS_VM) {
-+		printk (KERN_ERR "vmlogrdr: not running under VM, "
-+				"driver not loaded.\n");
-+		return -ENODEV;
-+	}
-+	
-+	get_vm_usrid(userid);
-+	userid[8] = '\0';
-+
-+	logreader_major = register_chrdev(logreader_major,"vmlogrdr",
-+					  &logreader_fops);
-+	if (logreader_major < 0 ) {
-+		printk (KERN_ERR "vmlogrdr: can't get major %d\n",
-+		logreader_major);	
-+		return -EIO;			
-+	}
-+
-+	int parm_no;
-+	int minor;
-+	for (parm_no=0; parm_no < MAXSERVICES; ++parm_no ) {
-+		printk (KERN_DEBUG "vmlogrdr: services[%d] == %s \n",parm_no,
-+			services[parm_no]);
-+		// compare to the know system services (excluding the leading *)
-+		// important: the system name needs to be 8 characters,
-+		//            padded with blanks and not 0.
-+		if (memcmp (services[parm_no], "LOGREC", 7) == 0) {
-+			memcpy(temp_system, "*LOGREC ", 8);
-+			minor = 0;
-+		} else if (services[parm_no] == 0) {
-+			break;
-+		} else {
-+			printk (KERN_ERR "vmlogrdr: unknown service: %s \n",
-+				services[parm_no] );
-+			rc=-EINVAL;
-+			goto free_sys_ser;
-+		}
-+	
-+		if (sys_ser[minor] == NULL) {	
-+			
-+			/* Allocate memory for:
-+			 * 1 logreader_priv_t
-+			 * 1 buffer (4096 bytes)
-+			 */
-+			logptr = kmalloc (sizeof(struct logreader_priv_t),
-+					  GFP_KERNEL);
-+			if (!logptr) {
-+				rc=-ENOMEM;
-+				goto free_sys_ser;
-+			}
-+			memset(logptr, 0x00, sizeof(struct logreader_priv_t));
-+			sys_ser[minor] = logptr;
-+
-+			/* set all structures */
-+			memcpy(logptr->system_service, temp_system, 8);	
-+			logptr->minor_num = minor;
-+			logptr->priv_lock = SPIN_LOCK_UNLOCKED;
-+			logptr->buffer_free = 1;
-+			logptr->buffer = kmalloc (4096, GFP_KERNEL);
-+			if (!logptr->buffer) {
-+				kfree(logptr);
-+				sys_ser[minor]=0;
-+				rc=-ENOMEM;
-+				goto free_sys_ser;
-+			}
-+			memset(logptr->buffer, 0x00, buflen);
-+			logptr->current_position = logptr->buffer;
-+			
-+			logreader_hotplug_event(logreader_major, logptr, 
-+						VMLOGRDR_HOTPLUG_ADD);
-+		
-+		} else {
-+			printk (KERN_WARNING "vmlogrdr: Service %s defined more"
-+				" then once -> ignore \n", services[parm_no]);
-+		}
-+	}
-+	printk (KERN_INFO "vmlogrdr: driver loaded\n");
-+
-+	return 0;
-+free_sys_ser:
-+	for (i=0; i < MAXMINOR; ++i ) {
-+		if ( sys_ser[i] != 0 ) {
-+			logreader_hotplug_event(logreader_major, sys_ser[i], 
-+						VMLOGRDR_HOTPLUG_REMOVE);			
-+			kfree (sys_ser[i]->buffer);
-+			kfree (sys_ser[i]);
-+		}
-+	}
-+	
-+	unregister_chrdev(logreader_major, "vmlogrdr");
-+	printk (KERN_ERR "vmlogrdr: driver not loaded.\n");
-+
-+	return rc;	
-+}
-+
-+
-+static void
-+logreader_exit(void)
-+{
-+	/* return all storage and unregister driver */
-+	int index = 0;
-+	struct logreader_priv_t * logptr = NULL;
-+
-+	for (index = 0; index < MAXMINOR; index++) {
-+		logptr = sys_ser[index];
-+		if (logptr) {
-+			kfree(logptr->buffer);
-+			kfree(logptr);
-+			sys_ser[index] = NULL;
-+			logreader_hotplug_event(logreader_major, logptr,
-+						VMLOGRDR_HOTPLUG_REMOVE);
-+		}
-+	}
-+	unregister_chrdev(logreader_major, "vmlogrdr");
-+	printk (KERN_INFO "vmlogrdr: driver unloaded\n");
-+	return;
-+}
-+
-+module_init(logreader_init);
-+module_exit(logreader_exit);
-+
-+
-+
-+
-+
-=== drivers/s390/char/Makefile
-==================================================================
---- drivers/s390/char/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -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)
- 
-=== drivers/s390/char/sclp_cpi.c
-==================================================================
---- drivers/s390/char/sclp_cpi.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/sclp_cpi.c   (/trunk/2.4.27)   (revision 52)
-@@ -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);
-+
-=== drivers/s390/char/sclp_vt220.c
-==================================================================
---- drivers/s390/char/sclp_vt220.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/sclp_vt220.c   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-+ */
-+
-+#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"
-+
-+#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;
-+}
-+
-+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);
-+}
-+
-+/*
-+ * 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;
-+
-+	if (count > sclp_vt220_space_left(request))
-+		count = sclp_vt220_space_left(request);
-+	if (count <= 0)
-+		return 0;
-+
-+	sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
-+	buffer = (void *) ((addr_t) sccb + sccb->header.length);
-+
-+	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;
-+
-+			} 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
-+sclp_vt220_timeout(unsigned long data)
-+{
-+	sclp_vt220_emit_current();
-+}
-+
-+#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.
-+ */
-+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);
-+		}
-+		/* 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;
-+}
-+
-+/*
-+ * 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)
-+{
-+	return __sclp_vt220_write(from_user, buf, count, 1, 0);
-+}
-+
-+#define SCLP_VT220_SESSION_ENDED	0x01
-+#define	SCLP_VT220_SESSION_STARTED	0x80
-+#define SCLP_VT220_SESSION_DATA		0x00
-+
-+/*
-+ * Called by the SCLP to report incoming event buffers.
-+ */
-+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;
-+
-+	buffer = (char *) ((addr_t) evbuf + sizeof(struct evbuf_header));
-+	count = evbuf->length - sizeof(struct evbuf_header);
-+
-+	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;
-+	}
-+}
-+
-+/*
-+ * 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
-+sclp_vt220_close(struct tty_struct * tty, struct file * filp)
-+{
-+	if (tty->count > 1)
-+		return;
-+	sclp_vt220_tty = NULL;
-+}
-+
-+/*
-+ * 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);
-+}
-+
-+/*
-+ * 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_vt220_flush_chars(struct tty_struct *tty)
-+{
-+	if (sclp_vt220_outqueue_count == 0)
-+		sclp_vt220_emit_current();
-+	else
-+		sclp_vt220_flush_later = 1;
-+}
-+
-+/*
-+ * 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)
-+{
-+	unsigned long flags;
-+	struct list_head *l;
-+	int count;
-+
-+	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;
-+}
-+
-+/*
-+ * Return number of buffered chars.
-+ */
-+static int
-+sclp_vt220_chars_in_buffer(struct tty_struct *tty)
-+{
-+	unsigned long flags;
-+	struct list_head *l;
-+	struct sclp_vt220_request *r;
-+	int count;
-+
-+	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);
-+	}
-+	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-+	return count;
-+}
-+
-+static void
-+__sclp_vt220_flush_buffer(void)
-+{
-+	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);
-+	}
-+	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-+}
-+
-+/*
-+ * 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)
-+{
-+	sclp_vt220_emit_current();
-+}
-+
-+/*
-+ * Initialize all relevant components and register driver with system.
-+ */
-+static void
-+__sclp_vt220_init(int early)
-+{
-+	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);
-+	}
-+}
-+
-+/*
-+ * Register driver with SCLP and Linux and initialize internal tty structures.
-+ */
-+void __init
-+sclp_vt220_tty_init(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;
-+	}
-+
-+	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;
-+
-+	/* 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;
-+
-+	/* 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;
-+
-+	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_SCLP_VT220_CONSOLE
-+
-+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);
-+}
-+
-+static kdev_t
-+sclp_vt220_con_device(struct console *c)
-+{
-+	return mk_kdev(SCLP_VT220_MAJOR, SCLP_VT220_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.
-+ * Note that this function may be called from within an interrupt context.
-+ */
-+static void
-+sclp_vt220_con_unblank(void)
-+{
-+	__sclp_vt220_flush_buffer();
-+}
-+
-+/* 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)
-+{
-+        if (!CONSOLE_IS_SCLP)
-+		return;
-+	__sclp_vt220_init(1);
-+	/* Attach linux console */
-+	register_console(&sclp_vt220_console);
-+}
-+
-+#endif /* CONFIG_SCLP_VT220_CONSOLE */
-+
-=== drivers/s390/char/tape_core.c
-==================================================================
---- drivers/s390/char/tape_core.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape_core.c   (/trunk/2.4.27)   (revision 52)
-@@ -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);
-+
-=== drivers/s390/char/sclp.c
-==================================================================
---- drivers/s390/char/sclp.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/sclp.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,854 @@
-+/*
-+ *  drivers/s390/char/sclp.c
-+ *     core function to access sclp interface
-+ *
-+ *  S390 version
-+ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-+ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
-+ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
-+ */
-+
-+#include <linux/config.h>
-+#include <linux/version.h>
-+#include <linux/module.h>
-+#include <linux/kmod.h>
-+#include <linux/bootmem.h>
-+#include <linux/errno.h>
-+#include <linux/ptrace.h>
-+#include <linux/slab.h>
-+#include <linux/spinlock.h>
-+#include <linux/interrupt.h>
-+#include <linux/timer.h>
-+#include <linux/init.h>
-+#include <linux/reboot.h>
-+#include <asm/irq.h>
-+#include <asm/s390_ext.h>
-+#include <asm/processor.h>
-+
-+#include "sclp.h"
-+
-+#define SCLP_CORE_PRINT_HEADER "sclp low level driver: "
-+
-+/* Structure for register_early_external_interrupt. */
-+static ext_int_info_t ext_int_info_hwc;
-+
-+/* spinlock to protect global variables of sclp_core */
-+static spinlock_t sclp_lock;
-+
-+/* Mask of valid sclp events */
-+static sccb_mask_t sclp_receive_mask;
-+static sccb_mask_t sclp_send_mask;
-+
-+/* List of registered event types */
-+static struct list_head sclp_reg_list;
-+
-+/* sccb queue */
-+static struct list_head sclp_req_queue;
-+
-+/* sccb for unconditional read */
-+static struct sclp_req sclp_read_req;
-+static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
-+/* sccb for write mask sccb */
-+static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
-+
-+/* Timer for init mask retries. */
-+static struct timer_list retry_timer;
-+
-+/* Timer for busy retries. */
-+static struct timer_list sclp_busy_timer;
-+
-+static volatile unsigned long sclp_status = 0;
-+/* some status flags */
-+#define SCLP_INIT		0
-+#define SCLP_RUNNING		1
-+#define SCLP_READING		2
-+#define SCLP_SHUTDOWN		3
-+
-+#define SCLP_INIT_POLL_INTERVAL	1
-+#define SCLP_BUSY_POLL_INTERVAL	1
-+
-+#define SCLP_COMMAND_INITIATED	0
-+#define SCLP_BUSY		2
-+#define SCLP_NOT_OPERATIONAL	3
-+
-+/*
-+ * assembler instruction for Service Call
-+ */
-+static int
-+__service_call(sclp_cmdw_t command, void *sccb)
-+{
-+	int cc;
-+
-+	/*
-+	 *  Mnemonic:	SERVC	Rx, Ry	[RRE]
-+	 *
-+	 *  Rx: SCLP command word
-+	 *  Ry: address of SCCB
-+	 */
-+	__asm__ __volatile__(
-+		"   .insn rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */
-+		"   ipm	  %0\n"
-+		"   srl	  %0,28"
-+		: "=&d" (cc)
-+		: "d" (command), "a" (__pa(sccb))
-+		: "cc", "memory" );
-+	/*
-+	 * cc == 0:   Service Call succesful initiated
-+	 * cc == 2:   SCLP busy, new Service Call not initiated,
-+	 *	      new SCCB unchanged
-+	 * cc == 3:   SCLP function not operational
-+	 */
-+	if (cc == SCLP_NOT_OPERATIONAL)
-+		return -EIO;
-+	if (cc == SCLP_BUSY)
-+		return -EBUSY;
-+	return 0;
-+}
-+
-+static void
-+sclp_start_request(void)
-+{
-+	struct sclp_req *req;
-+	int rc;
-+	unsigned long flags;
-+
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	/* quick exit if sclp is already in use */
-+	if (test_bit(SCLP_RUNNING, &sclp_status)) {
-+		spin_unlock_irqrestore(&sclp_lock, flags);
-+		return;
-+	}
-+	/* Try to start requests from the request queue. */
-+	while (!list_empty(&sclp_req_queue)) {
-+		req = list_entry(sclp_req_queue.next, struct sclp_req, list);
-+		rc = __service_call(req->command, req->sccb);
-+		if (rc == 0) {
-+			/* Sucessfully started request. */
-+			req->status = SCLP_REQ_RUNNING;
-+			/* Request active. Set running indication. */
-+			set_bit(SCLP_RUNNING, &sclp_status);
-+			break;
-+		}
-+		if (rc == -EBUSY) {
-+			/**
-+			 * SCLP is busy but no request is running.
-+			 * Try again later.
-+			 */
-+			if (!timer_pending(&sclp_busy_timer) ||
-+			    !mod_timer(&sclp_busy_timer,
-+				       jiffies + SCLP_BUSY_POLL_INTERVAL*HZ)) {
-+				sclp_busy_timer.function =
-+					(void *) sclp_start_request;
-+				sclp_busy_timer.expires =
-+					jiffies + SCLP_BUSY_POLL_INTERVAL*HZ;
-+				add_timer(&sclp_busy_timer);
-+			}
-+			break;
-+		}
-+		/* Request failed. */
-+		req->status = SCLP_REQ_FAILED;
-+		list_del(&req->list);
-+		if (req->callback) {
-+			spin_unlock_irqrestore(&sclp_lock, flags);
-+			req->callback(req, req->callback_data);
-+			spin_lock_irqsave(&sclp_lock, flags);
-+		}
-+	}
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+}
-+
-+static int
-+sclp_process_evbufs(struct sccb_header *sccb)
-+{
-+	int result;
-+	unsigned long flags;
-+	struct evbuf_header *evbuf;
-+	struct list_head *l;
-+	struct sclp_register *t;
-+
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	evbuf = (struct evbuf_header *) (sccb + 1);
-+	result = 0;
-+	while ((addr_t) evbuf < (addr_t) sccb + sccb->length) {
-+		/* check registered event */
-+		t = NULL;
-+		list_for_each(l, &sclp_reg_list) {
-+			t = list_entry(l, struct sclp_register, list);
-+			if (t->receive_mask & (1 << (32 - evbuf->type))) {
-+				if (t->receiver_fn != NULL) {
-+					spin_unlock_irqrestore(&sclp_lock,
-+							       flags);
-+					t->receiver_fn(evbuf);
-+					spin_lock_irqsave(&sclp_lock, flags);
-+				}
-+				break;
-+			}
-+			else
-+				t = NULL;
-+		}
-+		/* Check for unrequested event buffer */
-+		if (t == NULL)
-+			result = -ENOSYS;
-+		evbuf = (struct evbuf_header *)
-+				((addr_t) evbuf + evbuf->length);
-+	}
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	return result;
-+}
-+
-+char *
-+sclp_error_message(u16 rc)
-+{
-+	static struct {
-+		u16 code; char *msg;
-+	} sclp_errors[] = {
-+		{ 0x0000, "No response code stored (machine malfunction)" },
-+		{ 0x0020, "Normal Completion" },
-+		{ 0x0040, "SCLP equipment check" },
-+		{ 0x0100, "SCCB boundary violation" },
-+		{ 0x01f0, "Invalid command" },
-+		{ 0x0220, "Normal Completion; suppressed buffers pending" },
-+		{ 0x0300, "Insufficient SCCB length" },
-+		{ 0x0340, "Contained SCLP equipment check" },
-+		{ 0x05f0, "Target resource in improper state" },
-+		{ 0x40f0, "Invalid function code/not installed" },
-+		{ 0x60f0, "No buffers stored" },
-+		{ 0x62f0, "No buffers stored; suppressed buffers pending" },
-+		{ 0x70f0, "Invalid selection mask" },
-+		{ 0x71f0, "Event buffer exceeds available space" },
-+		{ 0x72f0, "Inconsistent lengths" },
-+		{ 0x73f0, "Event buffer syntax error" }
-+	};
-+	int i;
-+	for (i = 0; i < sizeof(sclp_errors)/sizeof(sclp_errors[0]); i++)
-+		if (rc == sclp_errors[i].code)
-+			return sclp_errors[i].msg;
-+	return "Invalid response code";
-+}
-+
-+/*
-+ * postprocessing of unconditional read service call
-+ */
-+static void
-+sclp_unconditional_read_cb(struct sclp_req *read_req, void *data)
-+{
-+	struct sccb_header *sccb;
-+
-+	sccb = read_req->sccb;
-+	if (sccb->response_code == 0x0020 ||
-+	    sccb->response_code == 0x0220) {
-+		if (sclp_process_evbufs(sccb) != 0)
-+			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
-+			       "unconditional read: "
-+			       "unrequested event buffer received.\n");
-+	}
-+
-+	if (sccb->response_code != 0x0020)
-+		printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
-+		       "unconditional read: %s (response code=0x%x).\n",
-+		       sclp_error_message(sccb->response_code),
-+		       sccb->response_code);
-+
-+	clear_bit(SCLP_READING, &sclp_status);
-+}
-+
-+/*
-+ * Function to queue Read Event Data/Unconditional Read
-+ */
-+static void
-+__sclp_unconditional_read(void)
-+{
-+	struct sccb_header *sccb;
-+	struct sclp_req *read_req;
-+
-+	/*
-+	 * Don't try to initiate Unconditional Read if we are not able to
-+	 * receive anything
-+	 */
-+	if (sclp_receive_mask == 0)
-+		return;
-+	/* Don't try reading if a read is already outstanding */
-+	if (test_and_set_bit(SCLP_READING, &sclp_status))
-+		return;
-+	/* Initialize read sccb */
-+	sccb = (struct sccb_header *) sclp_read_sccb;
-+	clear_page(sccb);
-+	sccb->length = PAGE_SIZE;
-+	sccb->function_code = 0;	/* unconditional read */
-+	sccb->control_mask[2] = 0x80;	/* variable length response */
-+	/* Initialize request structure */
-+	read_req = &sclp_read_req;
-+	read_req->command = SCLP_CMDW_READDATA;
-+	read_req->status = SCLP_REQ_QUEUED;
-+	read_req->callback = sclp_unconditional_read_cb;
-+	read_req->sccb = sccb;
-+	/* Add read request to the head of queue */
-+	list_add(&read_req->list, &sclp_req_queue);
-+}
-+
-+/* Bit masks to interpret external interruption parameter contents. */
-+#define EXT_INT_SCCB_MASK		0xfffffff8
-+#define EXT_INT_STATECHANGE_PENDING	0x00000002
-+#define EXT_INT_EVBUF_PENDING		0x00000001
-+
-+/*
-+ * Handler for service-signal external interruptions
-+ */
-+static void
-+sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
-+{
-+	u32 ext_int_param, finished_sccb, evbuf_pending;
-+	struct list_head *l;
-+	struct sclp_req *req, *tmp;
-+	int cpu;
-+
-+	spin_lock(&sclp_lock);
-+	/*
-+	 * Only process interrupt if sclp is initialized.
-+	 * This avoids strange effects for a pending request
-+	 * from before the last re-ipl.
-+	 */
-+	if (!test_bit(SCLP_INIT, &sclp_status)) {
-+		/* Now clear the running bit */
-+		clear_bit(SCLP_RUNNING, &sclp_status);
-+		spin_unlock(&sclp_lock);
-+		return;
-+	}
-+	ext_int_param = S390_lowcore.ext_params;
-+	finished_sccb = ext_int_param & EXT_INT_SCCB_MASK;
-+	evbuf_pending = ext_int_param & (EXT_INT_EVBUF_PENDING |
-+					 EXT_INT_STATECHANGE_PENDING);
-+	cpu = smp_processor_id();
-+	irq_enter(cpu, 0x2401);
-+	req = NULL;
-+	if (finished_sccb != 0U) {
-+		list_for_each(l, &sclp_req_queue) {
-+			tmp = list_entry(l, struct sclp_req, list);
-+			if (finished_sccb == (u32)(addr_t) tmp->sccb) {
-+				list_del(&tmp->list);
-+				req = tmp;
-+				break;
-+			}
-+		}
-+	}
-+	spin_unlock(&sclp_lock);
-+	/* Perform callback */
-+	if (req != NULL) {
-+		req->status = SCLP_REQ_DONE;
-+		if (req->callback != NULL)
-+			req->callback(req, req->callback_data);
-+	}
-+	spin_lock(&sclp_lock);
-+	/* Head queue a read sccb if an event buffer is pending */
-+	if (evbuf_pending)
-+		__sclp_unconditional_read();
-+	/* Now clear the running bit if SCLP indicated a finished SCCB */
-+	if (finished_sccb != 0U)
-+		clear_bit(SCLP_RUNNING, &sclp_status);
-+	spin_unlock(&sclp_lock);
-+	/* and start next request on the queue */
-+	sclp_start_request();
-+	irq_exit(cpu, 0x2401);
-+}
-+
-+/*
-+ * Wait synchronously for external interrupt of sclp. We may not receive
-+ * any other external interrupt, so we disable all other external interrupts
-+ * in control register 0.
-+ */
-+void
-+sclp_sync_wait(void)
-+{
-+	unsigned long psw_mask;
-+	unsigned long cr0, cr0_sync;
-+
-+	/* Prevent BH from executing. */
-+	local_bh_disable();
-+	/*
-+	 * save cr0
-+	 * enable service signal external interruption (cr0.22)
-+	 * disable cr0.20-21, cr0.25, cr0.27, cr0.30-31
-+	 * don't touch any other bit in cr0
-+	 */
-+	__ctl_store(cr0, 0, 0);
-+	cr0_sync = cr0;
-+	cr0_sync |= 0x00000200;
-+	cr0_sync &= 0xFFFFF3AC;
-+	__ctl_load(cr0_sync, 0, 0);
-+
-+	/* enable external interruptions (PSW-mask.7) */
-+	asm volatile ("STOSM 0(%1),0x01"
-+		      : "=m" (psw_mask) : "a" (&psw_mask) : "memory");
-+
-+	/* wait until ISR signals receipt of interrupt */
-+	while (test_bit(SCLP_RUNNING, &sclp_status)) {
-+		barrier();
-+		cpu_relax();
-+	}
-+
-+	/* disable external interruptions */
-+	asm volatile ("SSM 0(%0)"
-+		      : : "a" (&psw_mask) : "memory");
-+
-+	/* restore cr0 */
-+	__ctl_load(cr0, 0, 0);
-+	__local_bh_enable();
-+}
-+
-+/*
-+ * Queue an SCLP request. Request will immediately be processed if queue is
-+ * empty.
-+ */
-+void
-+sclp_add_request(struct sclp_req *req)
-+{
-+	unsigned long flags;
-+
-+	if (!test_bit(SCLP_INIT, &sclp_status)) {
-+		req->status = SCLP_REQ_FAILED;
-+		if (req->callback != NULL)
-+			req->callback(req, req->callback_data);
-+		return;
-+	}
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	/* queue the request */
-+	req->status = SCLP_REQ_QUEUED;
-+	list_add_tail(&req->list, &sclp_req_queue);
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	/* try to start the first request on the queue */
-+	sclp_start_request();
-+}
-+
-+/* state change notification */
-+struct sclp_statechangebuf {
-+	struct evbuf_header	header;
-+	u8		validity_sclp_active_facility_mask : 1;
-+	u8		validity_sclp_receive_mask : 1;
-+	u8		validity_sclp_send_mask : 1;
-+	u8		validity_read_data_function_mask : 1;
-+	u16		_zeros : 12;
-+	u16		mask_length;
-+	u64		sclp_active_facility_mask;
-+	sccb_mask_t	sclp_receive_mask;
-+	sccb_mask_t	sclp_send_mask;
-+	u32		read_data_function_mask;
-+} __attribute__((packed));
-+
-+static inline void
-+__sclp_notify_state_change(void)
-+{
-+	struct list_head *l;
-+	struct sclp_register *t;
-+	sccb_mask_t receive_mask, send_mask;
-+
-+	list_for_each(l, &sclp_reg_list) {
-+		t = list_entry(l, struct sclp_register, list);
-+		receive_mask = t->receive_mask & sclp_receive_mask;
-+		send_mask = t->send_mask & sclp_send_mask;
-+		if (t->sclp_receive_mask != receive_mask ||
-+		    t->sclp_send_mask != send_mask) {
-+			t->sclp_receive_mask = receive_mask;
-+			t->sclp_send_mask = send_mask;
-+			if (t->state_change_fn != NULL)
-+				t->state_change_fn(t);
-+		}
-+	}
-+}
-+
-+static void
-+sclp_state_change(struct evbuf_header *evbuf)
-+{
-+	unsigned long flags;
-+	struct sclp_statechangebuf *scbuf;
-+
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	scbuf = (struct sclp_statechangebuf *) evbuf;
-+
-+	if (scbuf->validity_sclp_receive_mask) {
-+		if (scbuf->mask_length != sizeof(sccb_mask_t))
-+			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
-+			       "state change event with mask length %i\n",
-+			       scbuf->mask_length);
-+		else
-+			/* set new receive mask */
-+			sclp_receive_mask = scbuf->sclp_receive_mask;
-+	}
-+
-+	if (scbuf->validity_sclp_send_mask) {
-+		if (scbuf->mask_length != sizeof(sccb_mask_t))
-+			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
-+			       "state change event with mask length %i\n",
-+			       scbuf->mask_length);
-+		else
-+			/* set new send mask */
-+			sclp_send_mask = scbuf->sclp_send_mask;
-+	}
-+
-+	__sclp_notify_state_change();
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+}
-+
-+static struct sclp_register sclp_state_change_event = {
-+	.receive_mask = EvTyp_StateChange_Mask,
-+	.receiver_fn = sclp_state_change
-+};
-+
-+
-+/*
-+ * SCLP quiesce event handler
-+ */
-+#ifdef CONFIG_SMP
-+static void
-+do_load_quiesce_psw(void * __unused)
-+{
-+	psw_t quiesce_psw;
-+	unsigned long status;
-+	int i;
-+
-+	if (smp_processor_id() != 0)
-+		signal_processor(smp_processor_id(), sigp_stop);
-+	/* Wait for all other cpus to enter stopped state */
-+	i = 1;
-+	while (i < smp_num_cpus) {
-+		switch (signal_processor_ps(&status, 0, i, sigp_sense)) {
-+		case sigp_order_code_accepted:
-+		case sigp_status_stored:
-+			/* Check for stopped and check stop state */
-+			if (test_bit(6, &status) || test_bit(4, &status))
-+				i++;
-+			break;
-+		case sigp_busy:
-+			break;
-+		case sigp_not_operational:
-+			i++;
-+			break;
-+		}
-+	}
-+	/* Quiesce the last cpu with the special psw */
-+	quiesce_psw.mask = _DW_PSW_MASK;
-+	quiesce_psw.addr = 0xfff;
-+	__load_psw(quiesce_psw);
-+}
-+
-+static void
-+do_machine_quiesce(void)
-+{
-+	smp_call_function(do_load_quiesce_psw, NULL, 0, 0);
-+	do_load_quiesce_psw(NULL);
-+}
-+#else
-+static void
-+do_machine_quiesce(void)
-+{
-+	psw_t quiesce_psw;
-+
-+	quiesce_psw.mask = _DW_PSW_MASK;
-+	quiesce_psw.addr = 0xfff;
-+	__load_psw(quiesce_psw);
-+}
-+#endif
-+
-+extern void ctrl_alt_del(void);
-+
-+static void
-+sclp_quiesce(struct evbuf_header *evbuf)
-+{
-+	/*
-+	 * We got a "shutdown" request.
-+	 * Add a call to an appropriate "shutdown" routine here. This
-+	 * routine should set all PSWs to 'disabled-wait', 'stopped'
-+	 * or 'check-stopped' - except 1 PSW which needs to carry a
-+	 * special bit pattern called 'quiesce PSW'.
-+	 */
-+	_machine_restart = (void *) do_machine_quiesce;
-+	_machine_halt = do_machine_quiesce;
-+	_machine_power_off = do_machine_quiesce;
-+	ctrl_alt_del();
-+}
-+
-+static struct sclp_register sclp_quiesce_event = {
-+	.receive_mask = EvTyp_SigQuiesce_Mask,
-+	.receiver_fn = sclp_quiesce
-+};
-+
-+/* initialisation of SCLP */
-+struct init_sccb {
-+	struct sccb_header header;
-+	u16 _reserved;
-+	u16 mask_length;
-+	sccb_mask_t receive_mask;
-+	sccb_mask_t send_mask;
-+	sccb_mask_t sclp_send_mask;
-+	sccb_mask_t sclp_receive_mask;
-+} __attribute__((packed));
-+
-+static void sclp_init_mask_retry(unsigned long);
-+
-+static int
-+sclp_init_mask(void)
-+{
-+	unsigned long flags;
-+	struct init_sccb *sccb;
-+	struct sclp_req *req;
-+	struct list_head *l;
-+	struct sclp_register *t;
-+	int rc;
-+
-+	sccb = (struct init_sccb *) sclp_init_sccb;
-+	/* stick the request structure to the end of the init sccb page */
-+	req = (struct sclp_req *) ((addr_t) sccb + PAGE_SIZE) - 1;
-+
-+	/* SCLP setup concerning receiving and sending Event Buffers */
-+	req->command = SCLP_CMDW_WRITEMASK;
-+	req->status = SCLP_REQ_QUEUED;
-+	req->callback = NULL;
-+	req->sccb = sccb;
-+	/* setup sccb for writemask command */
-+	memset(sccb, 0, sizeof(struct init_sccb));
-+	sccb->header.length = sizeof(struct init_sccb);
-+	sccb->mask_length = sizeof(sccb_mask_t);
-+	/* copy in the sccb mask of the registered event types */
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	if (!test_bit(SCLP_SHUTDOWN, &sclp_status)) {
-+		list_for_each(l, &sclp_reg_list) {
-+			t = list_entry(l, struct sclp_register, list);
-+			sccb->receive_mask |= t->receive_mask;
-+			sccb->send_mask |= t->send_mask;
-+		}
-+	}
-+	sccb->sclp_receive_mask = 0;
-+	sccb->sclp_send_mask = 0;
-+	if (test_bit(SCLP_INIT, &sclp_status)) {
-+		/* add request to sclp queue */
-+		list_add_tail(&req->list, &sclp_req_queue);
-+		spin_unlock_irqrestore(&sclp_lock, flags);
-+		/* and start if SCLP is idle */
-+		sclp_start_request();
-+		/* now wait for completion */
-+		while (req->status != SCLP_REQ_DONE &&
-+		       req->status != SCLP_REQ_FAILED)
-+			sclp_sync_wait();
-+		spin_lock_irqsave(&sclp_lock, flags);
-+	} else {
-+		/*
-+		 * Special case for the very first write mask command.
-+		 * The interrupt handler is not removing request from
-+		 * the request queue and doesn't call callbacks yet
-+		 * because there might be an pending old interrupt
-+		 * after a Re-IPL. We have to receive and ignore it.
-+		 */
-+		do {
-+			rc = __service_call(req->command, req->sccb);
-+			if (rc == 0)
-+				set_bit(SCLP_RUNNING, &sclp_status);
-+			spin_unlock_irqrestore(&sclp_lock, flags);
-+			if (rc == -EIO)
-+				return -ENOSYS;
-+			sclp_sync_wait();
-+			spin_lock_irqsave(&sclp_lock, flags);
-+		} while (rc == -EBUSY);
-+	}
-+	if (sccb->header.response_code != 0x0020) {
-+		/* WRITEMASK failed - we cannot rely on receiving a state
-+		   change event, so initially, polling is the only alternative
-+		   for us to ever become operational. */
-+		if (!test_bit(SCLP_SHUTDOWN, &sclp_status) &&
-+		    (!timer_pending(&retry_timer) ||
-+		     !mod_timer(&retry_timer,
-+			       jiffies + SCLP_INIT_POLL_INTERVAL*HZ))) {
-+			retry_timer.function = sclp_init_mask_retry;
-+			retry_timer.data = 0;
-+			retry_timer.expires = jiffies +
-+				SCLP_INIT_POLL_INTERVAL*HZ;
-+			add_timer(&retry_timer);
-+		}
-+	} else {
-+		sclp_receive_mask = sccb->sclp_receive_mask;
-+		sclp_send_mask = sccb->sclp_send_mask;
-+		__sclp_notify_state_change();
-+	}
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	return 0;
-+}
-+
-+static void
-+sclp_init_mask_retry(unsigned long data) 
-+{
-+	sclp_init_mask();
-+}
-+
-+/* Reboot event handler - reset send and receive mask to prevent pending SCLP
-+ * events from interfering with rebooted system. */
-+static int
-+sclp_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
-+{
-+	unsigned long flags;
-+
-+	/* Note: need spinlock to maintain atomicity when accessing global
-+         * variables. */
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	set_bit(SCLP_SHUTDOWN, &sclp_status);
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	sclp_init_mask();
-+	return NOTIFY_DONE;
-+}
-+
-+static struct notifier_block sclp_reboot_notifier = {
-+	.notifier_call = sclp_reboot_event
-+};
-+
-+/*
-+ * sclp setup function. Called early (no kmalloc!) from sclp_console_init().
-+ */
-+static int
-+sclp_init(void)
-+{
-+	int rc;
-+
-+	if (test_bit(SCLP_INIT, &sclp_status))
-+		/* Already initialized. */
-+		return 0;
-+
-+	spin_lock_init(&sclp_lock);
-+	INIT_LIST_HEAD(&sclp_req_queue);
-+
-+	/* init event list */
-+	INIT_LIST_HEAD(&sclp_reg_list);
-+	list_add(&sclp_state_change_event.list, &sclp_reg_list);
-+	list_add(&sclp_quiesce_event.list, &sclp_reg_list);
-+
-+	rc = register_reboot_notifier(&sclp_reboot_notifier);
-+	if (rc)
-+		return rc;
-+
-+	/*
-+	 * request the 0x2401 external interrupt
-+	 * The sclp driver is initialized early (before kmalloc works). We
-+	 * need to use register_early_external_interrupt.
-+	 */
-+	if (register_early_external_interrupt(0x2401, sclp_interrupt_handler,
-+					      &ext_int_info_hwc) != 0)
-+		return -EBUSY;
-+
-+	/* enable service-signal external interruptions,
-+	 * Control Register 0 bit 22 := 1
-+	 * (besides PSW bit 7 must be set to 1 sometimes for external
-+	 * interruptions)
-+	 */
-+	ctl_set_bit(0, 9);
-+
-+	init_timer(&retry_timer);
-+	init_timer(&sclp_busy_timer);
-+	/* do the initial write event mask */
-+	rc = sclp_init_mask();
-+	if (rc == 0) {
-+		/* Ok, now everything is setup right. */
-+		set_bit(SCLP_INIT, &sclp_status);
-+		return 0;
-+	}
-+
-+	/* The sclp_init_mask failed. SCLP is broken, unregister and exit. */
-+	ctl_clear_bit(0,9);
-+	unregister_early_external_interrupt(0x2401, sclp_interrupt_handler,
-+					    &ext_int_info_hwc);
-+
-+	return rc;
-+}
-+
-+/*
-+ * Register the SCLP event listener identified by REG. Return 0 on success.
-+ * Some error codes and their meaning:
-+ *
-+ *  -ENODEV = SCLP interface is not supported on this machine
-+ *   -EBUSY = there is already a listener registered for the requested
-+ *            event type
-+ *     -EIO = SCLP interface is currently not operational
-+ */
-+int
-+sclp_register(struct sclp_register *reg)
-+{
-+	unsigned long flags;
-+	struct list_head *l;
-+	struct sclp_register *t;
-+
-+	if (!MACHINE_HAS_SCLP)
-+		return -ENODEV;
-+
-+	if (!test_bit(SCLP_INIT, &sclp_status))
-+		sclp_init();
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	/* check already registered event masks for collisions */
-+	list_for_each(l, &sclp_reg_list) {
-+		t = list_entry(l, struct sclp_register, list);
-+		if (t->receive_mask & reg->receive_mask ||
-+		    t->send_mask & reg->send_mask) {
-+			spin_unlock_irqrestore(&sclp_lock, flags);
-+			return -EBUSY;
-+		}
-+	}
-+	/*
-+	 * set present mask to 0 to trigger state change
-+	 * callback in sclp_init_mask
-+	 */
-+	reg->sclp_receive_mask = 0;
-+	reg->sclp_send_mask = 0;
-+	list_add(&reg->list, &sclp_reg_list);
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	sclp_init_mask();
-+	return 0;
-+}
-+
-+/*
-+ * Unregister the SCLP event listener identified by REG.
-+ */
-+void
-+sclp_unregister(struct sclp_register *reg)
-+{
-+	unsigned long flags;
-+
-+	spin_lock_irqsave(&sclp_lock, flags);
-+	list_del(&reg->list);
-+	spin_unlock_irqrestore(&sclp_lock, flags);
-+	sclp_init_mask();
-+}
-+
-+#define	SCLP_EVBUF_PROCESSED	0x80
-+
-+/*
-+ * Traverse array of event buffers contained in SCCB and remove all buffers
-+ * with a set "processed" flag. Return the number of unprocessed buffers.
-+ */
-+int
-+sclp_remove_processed(struct sccb_header *sccb)
-+{
-+	struct evbuf_header *evbuf;
-+	int unprocessed;
-+	u16 remaining;
-+
-+	evbuf = (struct evbuf_header *) (sccb + 1);
-+	unprocessed = 0;
-+	remaining = sccb->length - sizeof(struct sccb_header);
-+	while (remaining > 0) {
-+		remaining -= evbuf->length;
-+		if (evbuf->flags & SCLP_EVBUF_PROCESSED) {
-+			sccb->length -= evbuf->length;
-+			memcpy((void *) evbuf,
-+			       (void *) ((addr_t) evbuf + evbuf->length),
-+			       remaining);
-+		} else {
-+			unprocessed++;
-+			evbuf = (struct evbuf_header *)
-+					((addr_t) evbuf + evbuf->length);
-+		}
-+	}
-+
-+	return unprocessed;
-+}
-+
-+module_init(sclp_init);
-+
-+EXPORT_SYMBOL(sclp_add_request);
-+EXPORT_SYMBOL(sclp_sync_wait);
-+EXPORT_SYMBOL(sclp_register);
-+EXPORT_SYMBOL(sclp_unregister);
-+EXPORT_SYMBOL(sclp_error_message);
-=== drivers/s390/char/tape_block.c
-==================================================================
---- drivers/s390/char/tape_block.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape_block.c   (/trunk/2.4.27)   (revision 52)
-@@ -0,0 +1,678 @@
-+/*
-+ *  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>
-+ */
-+
-+#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>
-+
-+#include <asm/debug.h>
-+#include <asm/irq.h>
-+#include <asm/s390dyn.h>
-+
-+#define TAPE_DBF_AREA	tape_core_dbf
-+
-+#include "tape.h"
-+#include "tape_std.h"
-+
-+#define PRINTK_HEADER "TBLOCK:"
-+
-+#define TAPEBLOCK_DEVFSMODE	0060644	/* brwxrw-rw- */
-+#define TAPEBLOCK_MAX_SEC	100
-+#define TAPEBLOCK_MIN_REQUEUE   3
-+
-+/*
-+ * file operation structure for tape block frontend
-+ */
-+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,
-+};
-+
-+int tapeblock_major = 0;
-+
-+/*
-+ * Some helper inlines
-+ */
-+static inline int tapeblock_size(int minor) {
-+	return blk_size[tapeblock_major][minor];
-+}
-+static inline int tapeblock_ssize(int minor) {
-+	return blksize_size[tapeblock_major][minor];
-+}
-+static inline int tapeblock_hw_ssize(int minor) {
-+	return hardsect_size[tapeblock_major][minor];
-+}
-+
-+/*
-+ * Post finished request.
-+ */
-+static inline void
-+tapeblock_end_request(struct request *req, int uptodate)
-+{
-+	if (end_that_request_first(req, uptodate, "tBLK"))
-+		BUG();
-+	end_that_request_last(req);
-+}
-+
-+static void
-+__tapeblock_end_request(struct tape_request *ccw_req, void *data)
-+{
-+	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);
-+
-+	if (!list_empty(&device->req_queue) ||
-+	    !list_empty(&device->blk_data.request_queue.queue_head))
-+		tasklet_schedule(&device->blk_data.tasklet);
-+}
-+
-+/*
-+ * Fetch requests from block device queue.
-+ */
-+static inline void
-+__tape_process_blk_queue(struct tape_device *device, struct list_head *new_req)
-+{
-+	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);
-+
-+		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;
-+
-+		list_add_tail(&ccw_req->list, new_req);
-+		nr_queued++;
-+	}
-+}
-+
-+/*
-+ * Feed requests to the tape device.
-+ */
-+static inline int
-+tape_queue_requests(struct tape_device *device, struct list_head *new_req)
-+{
-+	struct list_head *l, *n;
-+	struct tape_request *ccw_req;
-+	struct request *req;
-+	int rc, fail;
-+
-+	fail = 0;
-+	list_for_each_safe(l, n, new_req) {
-+		ccw_req = list_entry(l, struct tape_request, list);
-+		list_del(&ccw_req->list);
-+
-+		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;
-+}
-+
-+/*
-+ * Tape request queue function. Called from ll_rw_blk.c
-+ */
-+static void
-+tapeblock_request_fn(request_queue_t *queue)
-+{
-+	struct list_head    new_req;
-+	struct tape_device *device;
-+
-+	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;
-+	}
-+}
-+
-+/*
-+ * 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.
-+ */
-+static void
-+tapeblock_tasklet(unsigned long data)
-+{
-+	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;
-+	}
-+}
-+
-+/*
-+ * 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;
-+}
-+
-+/*
-+ * 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
-+}
-+
-+/*
-+ * This function is called for every new tapedevice
-+ */
-+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)
-+{
-+	/* 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);
-+
-+	tape_hotplug_event(device, tapeblock_major, TAPE_HOTPLUG_BLOCK_REMOVE);
-+}
-+
-+/*
-+ * 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)
-+{
-+	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);
-+	}
-+	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.
-+ */
-+void
-+tapeblock_medium_change(struct tape_device *device) {
-+	device->blk_data.start_block_id                = 0;
-+	blk_size[tapeblock_major][device->first_minor] = 0;
-+}
-+
-+/*
-+ * 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);
-+	}
-+
-+	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;
-+}
-+
-+/*
-+ * Block frontend tape device release function.
-+ */
-+int
-+tapeblock_release(struct inode *inode, struct file *filp) {
-+	struct tape_device *device;
-+
-+	device = tape_get_device(minor(inode->i_rdev) >> 1);
-+
-+	DBF_EVENT(4, "TBLOCK: release %i\n", device->first_minor);
-+
-+	/* Remove all buffers at device close. */
-+	/* FIXME: can we do that a tape unload ? */
-+	invalidate_buffers(inode->i_rdev);
-+
-+	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 0;
-+}
-+
-+int
-+tapeblock_ioctl(
-+	struct inode	*inode,
-+	struct file	*file,
-+	unsigned int	 command,
-+	unsigned long	 arg
-+) {
-+	int	 rc     = 0;
-+	int	 minor  = minor(inode->i_rdev);
-+
-+	DBF_EVENT(6, "tapeblock_ioctl(%x)\n", command);
-+
-+	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;
-+			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 rc;
-+}
-+
-+/*
-+ * Initialize block device frontend.
-+ */
-+int
-+tapeblock_init(void) 
-+{
-+	int rc;
-+
-+	/* 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;
-+	}
-+	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;
-+
-+	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");
-+
-+	return 0;
-+
-+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;
-+	}
-+
-+	DBF_EVENT(3, "TBLOCK: init failed(%d)\n", rc);
-+	return rc;
-+}
-+
-+/*
-+ * Deregister major for block device frontend
-+ */
-+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");
-+}
-=== drivers/s390/char/sclp.h
-==================================================================
---- drivers/s390/char/sclp.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/sclp.h   (/trunk/2.4.27)   (revision 52)
-@@ -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__ */
-=== drivers/s390/char/sclp_rw.c
-==================================================================
---- drivers/s390/char/sclp_rw.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/sclp_rw.c   (/trunk/2.4.27)   (revision 52)
-@@ -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);
-+}
-=== drivers/s390/char/tape_proc.c
-==================================================================
---- drivers/s390/char/tape_proc.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape_proc.c   (/trunk/2.4.27)   (revision 52)
-@@ -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);
-+}
-=== drivers/s390/char/sclp_rw.h
-==================================================================
---- drivers/s390/char/sclp_rw.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/sclp_rw.h   (/trunk/2.4.27)   (revision 52)
-@@ -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__ */
-=== drivers/s390/char/tape_devmap.c
-==================================================================
---- drivers/s390/char/tape_devmap.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape_devmap.c   (/trunk/2.4.27)   (revision 52)
-@@ -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);
-=== drivers/s390/char/tape_char.c
-==================================================================
---- drivers/s390/char/tape_char.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape_char.c   (/trunk/2.4.27)   (revision 52)
-@@ -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"
-+
-+#define PRINTK_HEADER "TCHAR:"
-+
-+#define TAPECHAR_DEVFSMODE	0020644	/* crwxrw-rw- */
-+#define TAPECHAR_MAJOR		0	/* get dynamic major */
-+
-+int tapechar_major = TAPECHAR_MAJOR;
-+
-+/*
-+ * Prototypes for file operation functions
-+ */
-+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,
-+};
-+
-+#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;
-+}
-+
-+/*
-+ * 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 function is called for every new tapedevice
-+ */
-+int
-+tapechar_setup_device(struct tape_device * device)
-+{
-+#ifdef CONFIG_DEVFS_FS
-+	int rc;
-+
-+	rc = tapechar_mkdevfstree(device);
-+	if (rc)
-+		return rc;
-+#endif
-+
-+	tape_hotplug_event(device, tapechar_major, TAPE_HOTPLUG_CHAR_ADD);
-+	return 0;
-+	
-+}
-+
-+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);
-+}
-+
-+static inline int
-+tapechar_check_idalbuffer(struct tape_device *device, size_t block_size)
-+{
-+	struct idal_buffer *new;
-+
-+	/* 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;
-+
-+	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;
-+	}
-+
-+	/* 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;
-+}
-+
-+/*
-+ * 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;
-+
-+        DBF_EVENT(6, "TCHAR:read\n");
-+	device = (struct tape_device *) filp->private_data;
-+
-+	/* 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;
-+	}
-+
-+	/*
-+	 * 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);
-+	}
-+
-+	/* 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;
-+	}
-+
-+	/*
-+	 * 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);
-+	/* 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;
-+}
-+
-+/*
-+ * 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;
-+
-+	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;
-+	}
-+
-+	/* 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);
-+	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);
-+
-+	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;
-+	}
-+
-+	/*
-+	 * 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;
-+
-+	return rc ? rc : written;
-+}
-+
-+/*
-+ * Character frontend tape device open function.
-+ */
-+int
-+tapechar_open (struct inode *inode, struct file *filp)
-+{
-+	struct tape_device *device;
-+	int minor, rc;
-+
-+	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.
-+ */
-+
-+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));
-+			
-+	/*
-+	 * 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--;
-+		}
-+	}
-+
-+	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;
-+}
-+
-+/*
-+ * 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;
-+
-+	DBF_EVENT(6, "TCHAR:ioct(%x)\n", no);
-+
-+	device = (struct tape_device *) filp->private_data;
-+
-+	if (no == MTIOCTOP) {
-+		struct mtop op;
-+
-+		if (copy_from_user(&op, (char *) data, sizeof(op)) != 0)
-+			return -EFAULT;
-+		if (op.mt_count < 0)
-+			return -EINVAL;
-+
-+		/*
-+		 * 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);
-+
-+		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;
-+
-+		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;
-+
-+		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;
-+
-+		if(device->medium_state == MS_LOADED) {
-+			rc = tape_mtop(device, MTTELL, 1);
-+
-+			if(rc < 0)
-+				return rc;
-+
-+			if(rc == 0)
-+				get.mt_gstat |= GMT_BOT(~0);
-+
-+			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);
-+}
-+
-+/*
-+ * Initialize character device frontend.
-+ */
-+int
-+tapechar_init (void)
-+{
-+	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 (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;
-+}
-+
-+/*
-+ * cleanup
-+ */
-+void
-+tapechar_exit(void)
-+{
-+	unregister_chrdev (tapechar_major, "tape");
-+}
-=== drivers/s390/char/hwc_rw.h
-==================================================================
---- drivers/s390/char/hwc_rw.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/hwc_rw.h   (/trunk/2.4.27)   (revision 52)
-@@ -1,132 +0,0 @@
++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-2.4.27.orig/drivers/s390/char/ctrlchar.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/ctrlchar.c	2006-01-30 22:25:26.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-2.4.27.orig/drivers/s390/char/hwc.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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;
+-
+-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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/hwc_rw.c	2006-01-30 22:25:26.000000000 -0700
+@@ -1,2458 +0,0 @@
 -/*
-- *  drivers/s390/char/hwc_rw.h
-- *    interface to the HWC-read/write driver 
+- *  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>
+- *
+- * 
+- *
+- * 
+- * 
+- * 
 - */
 -
--#ifndef __HWC_RW_H__
--#define __HWC_RW_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 '%'
+-
+-#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;
+-}
 -
--#include <linux/ioctl.h>
+-static int 
+-allocate_write_hwcb (void)
+-{
+-	unsigned char *page;
+-	int page_nr;
 -
--typedef struct {
+-	if (hwc_data.hwcb_count == hwc_data.ioctls.max_hwcb)
+-		return -ENOMEM;
 -
--	void (*move_input) (unsigned char *, unsigned int);
+-	page_nr = find_first_zero_bit (&hwc_data.kmem_pages, MAX_KMEM_PAGES);
+-	if (page_nr < hwc_data.ioctls.kmem_hwcb) {
 -
--	void (*wake_up) (void);
--} hwc_high_level_calls_t;
+-		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);
 -
--struct _hwc_request;
+-	if (!page)
+-		return -ENOMEM;
 -
--typedef void hwc_callback_t (struct _hwc_request *);
+-	if (!OUT_HWCB)
+-		OUT_HWCB = page;
+-	else
+-		BUF_HWCB_NEXT = page;
 -
--typedef struct _hwc_request {
--	void *block;
--	u32 word;
--	hwc_callback_t *callback;
--	void *data;
--} __attribute__ ((packed)) 
+-	BUF_HWCB = page;
 -
--hwc_request_t;
+-	BUF_HWCB_NEXT = NULL;
 -
--#define HWC_ASCEBC(x) ((MACHINE_IS_VM ? _ascebc[x] : _ascebc_500[x]))
+-	hwc_data.hwcb_count++;
 -
--#define HWC_EBCASC_STR(s,c) ((MACHINE_IS_VM ? EBCASC(s,c) : EBCASC_500(s,c)))
+-	prepare_write_hwcb ();
 -
--#define HWC_ASCEBC_STR(s,c) ((MACHINE_IS_VM ? ASCEBC(s,c) : ASCEBC_500(s,c)))
+-	BUF_HWCB_TIMES_LOST = 0;
+-	BUF_HWCB_MTO_LOST = 0;
+-	BUF_HWCB_CHAR_LOST = 0;
 -
--#define IN_HWCB      1
--#define IN_WRITE_BUF 2
--#define IN_BUFS_TOTAL        (IN_HWCB | IN_WRITE_BUF)
+-#ifdef BUFFER_STRESS_TEST
 -
--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;
+-	internal_print (
+-			       DELAYED_WRITE,
+-			       "*** " HWC_RW_PRINT_HEADER
+-			    "page #%i at 0x%x for buffering allocated. ***\n",
+-			       hwc_data.hwcb_count, page);
 -
--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;
+-#endif
 -
--static hwc_ioctls_t _hwc_ioctls;
+-	return 0;
+-}
 -
--#define HWC_IOCTL_LETTER 'B'
+-static int 
+-release_write_hwcb (void)
+-{
+-	unsigned long page;
+-	int page_nr;
 -
--#define TIOCHWCSHTAB	_IOW(HWC_IOCTL_LETTER, 0, _hwc_ioctls.width_htab)
+-	if (!hwc_data.hwcb_count)
+-		return -ENODATA;
 -
--#define TIOCHWCSECHO	_IOW(HWC_IOCTL_LETTER, 1, _hwc_ioctls.echo)
+-	if (hwc_data.hwcb_count == 1) {
 -
--#define TIOCHWCSCOLS	_IOW(HWC_IOCTL_LETTER, 2, _hwc_ioctls.columns)
+-		prepare_write_hwcb ();
 -
--#define TIOCHWCSNL	_IOW(HWC_IOCTL_LETTER, 4, _hwc_ioctls.final_nl)
+-		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;
 -
--#define TIOCHWCSOBUF	_IOW(HWC_IOCTL_LETTER, 5, _hwc_ioctls.max_hwcb)
+-		ALL_HWCB_MTO -= OUT_HWCB_MTO;
+-		ALL_HWCB_CHAR -= OUT_HWCB_CHAR;
+-		hwc_data.hwcb_count--;
 -
--#define TIOCHWCSINIT	_IO(HWC_IOCTL_LETTER, 6)
+-		OUT_HWCB = OUT_HWCB_NEXT;
 -
--#define TIOCHWCSCASE	_IOW(HWC_IOCTL_LETTER, 7, _hwc_ioctls.tolower)
+-		if (page >= hwc_data.kmem_start &&
+-		    page <= hwc_data.kmem_end) {
+-			/*memset((void *) page, 0, PAGE_SIZE); */
 -
--#define TIOCHWCSDELIM	_IOW(HWC_IOCTL_LETTER, 9, _hwc_ioctls.delim)
+-			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
 -
--#define TIOCHWCGHTAB	_IOR(HWC_IOCTL_LETTER, 10, _hwc_ioctls.width_htab)
+-		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 TIOCHWCGECHO	_IOR(HWC_IOCTL_LETTER, 11, _hwc_ioctls.echo)
+-#endif
+-	}
+-	return 0;
+-}
 -
--#define TIOCHWCGCOLS	_IOR(HWC_IOCTL_LETTER, 12, _hwc_ioctls.columns)
+-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;
 -
--#define TIOCHWCGNL	_IOR(HWC_IOCTL_LETTER, 14, _hwc_ioctls.final_nl)
+-	if (!BUF_HWCB)
+-		return -ENOMEM;
 -
--#define TIOCHWCGOBUF	_IOR(HWC_IOCTL_LETTER, 15, _hwc_ioctls.max_hwcb)
+-	if (BUF_HWCB == hwc_data.current_hwcb)
+-		return -ENOMEM;
 -
--#define TIOCHWCGINIT	_IOR(HWC_IOCTL_LETTER, 16, _hwc_ioctls)
+-	mto_size = sizeof (mto_t) + count;
 -
--#define TIOCHWCGCASE	_IOR(HWC_IOCTL_LETTER, 17, _hwc_ioctls.tolower)
+-	hwcb = (write_hwcb_t *) BUF_HWCB;
 -
--#define TIOCHWCGDELIM	_IOR(HWC_IOCTL_LETTER, 19, _hwc_ioctls.delim)
+-	if ((MAX_HWCB_ROOM - hwcb->length) < mto_size)
+-		return -ENOMEM;
 -
--#define TIOCHWCGKBUF	_IOR(HWC_IOCTL_LETTER, 20, _hwc_ioctls.max_hwcb)
+-	mto = (mto_t *) (((unsigned long) hwcb) + hwcb->length);
 -
--#define TIOCHWCGCURR	_IOR(HWC_IOCTL_LETTER, 21, _hwc_ioctls)
+-	memcpy (mto, &mto_template, sizeof (mto_t));
 -
--#ifndef __HWC_RW_C__
+-	dest = (void *) (((unsigned long) mto) + sizeof (mto_t));
 -
--extern int hwc_init (void);
+-	memcpy (dest, message, count);
 -
--extern int hwc_write (int from_user, const unsigned char *, unsigned int);
+-	mto->length += count;
 -
--extern unsigned int hwc_chars_in_buffer (unsigned char);
+-	hwcb->length += mto_size;
+-	hwcb->msgbuf.length += mto_size;
+-	hwcb->msgbuf.mdb.length += mto_size;
 -
--extern unsigned int hwc_write_room (unsigned char);
+-	BUF_HWCB_MTO++;
+-	ALL_HWCB_MTO++;
+-	BUF_HWCB_CHAR += count;
+-	ALL_HWCB_CHAR += count;
 -
--extern void hwc_flush_buffer (unsigned char);
+-	return count;
+-}
 -
--extern void hwc_unblank (void);
+-static int write_event_data_1 (void);
 -
--extern signed int hwc_ioctl (unsigned int, unsigned long);
+-static void 
+-do_poll_hwc (unsigned long data)
+-{
+-	unsigned long flags;
 -
--extern void do_hwc_interrupt (void);
+-	spin_lock_irqsave (&hwc_data.lock, flags);
 -
--extern int hwc_printk (const char *,...);
+-	write_event_data_1 ();
 -
--extern signed int hwc_register_calls (hwc_high_level_calls_t *);
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-}
 -
--extern signed int hwc_unregister_calls (hwc_high_level_calls_t *);
+-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;
+-}
 -
--extern int hwc_send (hwc_request_t *);
+-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
-=== drivers/s390/char/tape3490.c
-==================================================================
---- drivers/s390/char/tape3490.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape3490.c   (/trunk/2.4.27)   (revision 52)
-@@ -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"
+-	switch (hwcb->response_code) {
+-	case 0x0020:
 -
--tape_event_handler_t tape3490_event_handler_table[TS_SIZE][TE_SIZE] =
+-		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)
 -{
--    /* {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
--};
+-	if (add_mto (message, count) != count) {
 -
--void
--tape3490_setup_assist (tape_info_t * ti)
+-		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)
 -{
--	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;
+-
+-	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;
 -
--void
--tape3490_shutdown (int autoprobe) {
--    if (autoprobe)
--	s390_device_unregister(&tape3490_devreg);
--}
+-	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;
 -
--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;
+-	hwc_data.obuf_cursor = 0;
+-	hwc_data.obuf_count = 0;
+-
+-	write_event_data_1 ();
+-
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
 -}
-=== drivers/s390/char/tape3590.c
-==================================================================
---- drivers/s390/char/tape3590.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape3590.c   (/trunk/2.4.27)   (revision 52)
-@@ -1 +0,0 @@
--// tbd
-=== drivers/s390/char/tape3490.h
-==================================================================
---- drivers/s390/char/tape3490.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape3490.h   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-- *
-- ****************************************************************************
-- */
+-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;
 -
--#ifndef _TAPE3490_H
+-	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;
+-	}
 -
--#define _TAPE3490_H
+-	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++;
 -
--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
-=== drivers/s390/char/tape3590.h
-==================================================================
---- drivers/s390/char/tape3590.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape3590.h   (/trunk/2.4.27)   (revision 52)
-@@ -1 +0,0 @@
--// tbd
-=== drivers/s390/char/hwc_con.c
-==================================================================
---- drivers/s390/char/hwc_con.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/hwc_con.c   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-- */
+-		if ((obuf_cursor == obuf_columns) &&
 -
--#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>
+-		    (ch != '\n') &&
 -
--#include "hwc_rw.h"
+-		    (ch != '\t')) {
+-			put_line (&hwc_data.obuf[hwc_data.obuf_start],
+-				  obuf_columns);
+-			obuf_cursor = 0;
+-			obuf_count = 0;
+-		}
+-		switch (ch) {
 -
--#ifdef CONFIG_HWC_CONSOLE
+-		case '\n':
 -
--#define  hwc_console_major 4
--#define  hwc_console_minor 64
--#define  hwc_console_name  "console"
+-			put_line (&hwc_data.obuf[hwc_data.obuf_start],
+-				  obuf_count);
+-			obuf_cursor = 0;
+-			obuf_count = 0;
+-			break;
 -
--void hwc_console_write (struct console *, const char *, unsigned int);
--kdev_t hwc_console_device (struct console *);
--void hwc_console_unblank (void);
+-		case '\a':
 -
--#define  HWC_CON_PRINT_HEADER "hwc console driver: "
+-			hwc_data.obuf_start += obuf_count;
+-			set_alarm ();
+-			hwc_data.obuf_start -= obuf_count;
 -
--struct console hwc_console = {
--	name:	hwc_console_name,
--	write:	hwc_console_write,
--	device:	hwc_console_device,
--	unblank:hwc_console_unblank,
--	flags:	CON_PRINTBUFFER,
--};
+-			break;
 -
--void 
--hwc_console_write (
--			  struct console *console,
--			  const char *message,
--			  unsigned int count)
--{
+-		case '\t':
 -
--	if (console->device (console) != hwc_console.device (&hwc_console)) {
+-			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);
 -
--		hwc_printk (KERN_WARNING HWC_CON_PRINT_HEADER
--			    "hwc_console_write() called with wrong "
--			    "device number");
--		return;
--	}
--	hwc_write (0, message, count);
--}
+-			break;
 -
--kdev_t 
--hwc_console_device (struct console * c)
--{
--	return MKDEV (hwc_console_major, hwc_console_minor);
--}
+-		case '\f':
+-		case '\v':
 -
--void 
--hwc_console_unblank (void)
--{
--	hwc_unblank ();
--}
+-			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--;
+-			}
 -
--#endif
+-			break;
 -
--void __init 
--hwc_console_init (void)
--{
--	if (!MACHINE_HAS_HWC)
--		return;
+-		case '\b':
 -
--	if (hwc_init () == 0) {
--#ifdef CONFIG_HWC_CONSOLE
+-			if (obuf_cursor)
+-				obuf_cursor--;
+-			break;
 -
--		if (CONSOLE_IS_HWC)
--			register_console (&hwc_console);
--#endif
--	} else
--		panic (HWC_CON_PRINT_HEADER "hwc initialisation failed !");
+-		case '\r':
 -
--	return;
--}
-=== drivers/s390/char/hwc_tty.c
-==================================================================
---- drivers/s390/char/hwc_tty.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/hwc_tty.c   (/trunk/2.4.27)   (revision 52)
-@@ -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.
-- */
+-			obuf_cursor = 0;
+-			break;
 -
--#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>
+-		case 0x00:
 -
--#include <asm/uaccess.h>
+-			put_line (&hwc_data.obuf[hwc_data.obuf_start],
+-				  obuf_count);
+-			obuf_cursor = 0;
+-			obuf_count = 0;
+-			goto out;
 -
--#include "hwc_rw.h"
--#include "ctrlchar.h"
+-		default:
 -
--#define HWC_TTY_PRINT_HEADER "hwc tty driver: "
+-			if (isprint (ch))
+-				hwc_data.obuf[hwc_data.obuf_start +
+-					      obuf_cursor++]
+-				    = HWC_ASCEBC (ch);
+-		}
+-		if (obuf_cursor > obuf_count)
+-			obuf_count = obuf_cursor;
+-	}
 -
--#define HWC_TTY_BUF_SIZE 512
+-	if (obuf_cursor) {
 -
--typedef struct {
+-		if (hwc_data.obuf_start ||
+-		    (hwc_data.ioctls.final_nl == 0)) {
 -
--	struct tty_struct *tty;
+-			put_line (&hwc_data.obuf[hwc_data.obuf_start],
+-				  obuf_count);
+-			obuf_cursor = 0;
+-			obuf_count = 0;
+-		} else {
 -
--	unsigned char buf[HWC_TTY_BUF_SIZE];
+-			if (hwc_data.ioctls.final_nl > 0) {
 -
--	unsigned short int buf_count;
+-				if (hwc_data.flags & HWC_WTIMER_RUNS) {
 -
--	spinlock_t lock;
+-					mod_timer (&hwc_data.write_timer,
+-						   jiffies + hwc_data.ioctls.final_nl * HZ / 10);
+-				} else {
 -
--	hwc_high_level_calls_t calls;
--} hwc_tty_data_struct;
+-					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;
 -
--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;
+-		}
+-	} else;
 -
--extern struct termios tty_std_termios;
+-      out:
 -
--void hwc_tty_wake_up (void);
--void hwc_tty_input (unsigned char *, unsigned int);
+-	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 ();
 -
--static int 
--hwc_tty_open (struct tty_struct *tty,
--	      struct file *filp)
+-	return processed_characters;
+-}
+-
+-signed int 
+-hwc_write (int from_user, const unsigned char *msg, unsigned int count)
 -{
+-	unsigned long flags;
+-	int retval;
 -
--	if (MINOR (tty->device) - tty->driver.minor_start)
--		return -ENODEV;
+-	spin_lock_irqsave (&hwc_data.lock, flags);
 -
--	tty->driver_data = &hwc_tty_data;
--	hwc_tty_data.buf_count = 0;
--	hwc_tty_data.tty = tty;
--	tty->low_latency = 0;
+-	retval = do_hwc_write (from_user, (unsigned char *) msg,
+-			       count, IMMEDIATE_WRITE);
 -
--	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));
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
 -
--	return 0;
+-	return retval;
 -}
 -
--static void 
--hwc_tty_close (struct tty_struct *tty,
--	       struct file *filp)
+-unsigned int 
+-hwc_chars_in_buffer (unsigned char flag)
 -{
--	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;
+-	unsigned short int number = 0;
+-	unsigned long flags;
 -
--	hwc_tty_data.tty = NULL;
+-	spin_lock_irqsave (&hwc_data.lock, flags);
 -
--	hwc_unregister_calls (&(hwc_tty_data.calls));
+-	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 int 
--hwc_tty_write_room (struct tty_struct *tty)
+-static inline int 
+-nr_setbits (kmem_pages_t arg)
 -{
--	int retval;
+-	int i;
+-	int nr = 0;
 -
--	retval = hwc_write_room (IN_BUFS_TOTAL);
--	return retval;
+-	for (i = 0; i < (sizeof (arg) << 3); i++) {
+-		if (arg & 1)
+-			nr++;
+-		arg >>= 1;
+-	}
+-
+-	return nr;
 -}
 -
--static int 
--hwc_tty_write (struct tty_struct *tty,
--	       int from_user,
--	       const unsigned char *buf,
--	       int count)
+-unsigned int 
+-hwc_write_room (unsigned char flag)
 -{
--	int retval;
+-	unsigned int number = 0;
+-	unsigned long flags;
+-	write_hwcb_t *hwcb;
 -
--	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;
+-	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)));
 -	}
--	retval = hwc_write (from_user, buf, count);
--	return retval;
+-	if (flag & IN_WRITE_BUF)
+-		number += MAX_HWCB_ROOM - hwc_data.obuf_cursor;
+-
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-
+-	return number;
 -}
 -
--static void 
--hwc_tty_put_char (struct tty_struct *tty,
--		  unsigned char ch)
+-void 
+-hwc_flush_buffer (unsigned char flag)
 -{
 -	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;
+-	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;
 -	}
--	hwc_tty_data.buf[hwc_tty_data.buf_count] = ch;
--	hwc_tty_data.buf_count++;
--	spin_unlock_irqrestore (&hwc_tty_data.lock, flags);
+-	if (flag & IN_WRITE_BUF) {
+-		hwc_data.obuf_cursor = 0;
+-		hwc_data.obuf_count = 0;
+-	}
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
 -}
 -
--static void 
--hwc_tty_flush_chars (struct tty_struct *tty)
--{
--	unsigned long 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];
 -
--	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);
+-			i_out++;
+-		}
+-	}
+-
+-	return i_out;
 -}
 -
+-#ifdef DUMP_HWCB_INPUT
+-
 -static int 
--hwc_tty_chars_in_buffer (struct tty_struct *tty)
+-gds_vector_name (u16 id, unsigned char name[])
 -{
--	int retval;
+-	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;
+-	}
 -
--	retval = hwc_chars_in_buffer (IN_BUFS_TOTAL);
 -	return retval;
 -}
+-#endif
 -
--static void 
--hwc_tty_flush_buffer (struct tty_struct *tty)
+-inline static gds_vector_t *
+-find_gds_vector (
+-			gds_vector_t * start, void *end, u16 id)
 -{
--	hwc_tty_wake_up ();
--}
+-	gds_vector_t *vec;
+-	gds_vector_t *retval = NULL;
 -
--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;
+-	vec = start;
 -
--	return hwc_ioctl (cmd, arg);
--}
+-	while (((void *) vec) < end) {
+-		if (vec->gds_id == id) {
 -
--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);
+-#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;
 -}
 -
--void 
--hwc_tty_input (unsigned char *buf, unsigned int count)
+-inline static gds_subvector_t *
+-find_gds_subvector (
+-			   gds_subvector_t * start, void *end, u8 key)
 -{
--	struct tty_struct *tty = hwc_tty_data.tty;
+-	gds_subvector_t *subvec;
+-	gds_subvector_t *retval = NULL;
 -
--	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 {
+-	subvec = start;
 -
--			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;
+-	while (((void *) subvec) < end) {
+-		if (subvec->key == key) {
+-			retval = subvec;
+-			break;
 -		}
--		tty_flip_buffer_push (tty);
--		hwc_tty_wake_up ();
+-		subvec = (gds_subvector_t *)
+-		    (((unsigned long) subvec) + subvec->length);
 -	}
+-
+-	return retval;
 -}
 -
--void 
--hwc_tty_init (void)
+-inline static int 
+-get_input (void *start, void *end)
 -{
--	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;
+-	int count;
 -
--	hwc_tty_driver.table = hwc_tty_table;
--	hwc_tty_driver.termios = hwc_tty_termios;
--	hwc_tty_driver.termios_locked = hwc_tty_termios_locked;
+-	count = ((unsigned long) end) - ((unsigned long) start);
 -
--	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;
+-	if (hwc_data.ioctls.tolower)
+-		EBC_TOLOWER (start, count);
 -
--	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 (hwc_data.ioctls.delim)
+-		count = seperate_cases (start, count);
 -
--	if (tty_register_driver (&hwc_tty_driver))
--		panic ("Couldn't register hwc_tty driver\n");
--}
-=== drivers/s390/char/tapechar.c
-==================================================================
---- drivers/s390/char/tapechar.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tapechar.c   (/trunk/2.4.27)   (revision 52)
-@@ -1,764 +0,0 @@
+-	HWC_EBCASC_STR (start, count);
 -
--/***************************************************************************
-- *
-- *  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 (hwc_data.ioctls.echo)
+-		do_hwc_write (0, start, count, IMMEDIATE_WRITE);
 -
--#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 (hwc_data.calls != NULL)
+-		if (hwc_data.calls->move_input != NULL)
+-			(hwc_data.calls->move_input) (start, count);
 -
--#define PRINTK_HEADER "TCHAR:"
+-	return count;
+-}
 -
--/*
-- * file operation structure for tape devices
-- */
--static struct file_operations tape_fops =
+-inline static int 
+-eval_selfdeftextmsg (gds_subvector_t * start, void *end)
 -{
--    //    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,
--};
+-	gds_subvector_t *subvec;
+-	void *subvec_data;
+-	void *subvec_end;
+-	int retval = 0;
 -
--int tape_major = TAPE_MAJOR;
+-	subvec = start;
 -
--#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);
--}
+-	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;
+-	}
 -
--void
--tapechar_rmdevfstree (tape_info_t* ti) {
--    devfs_unregister(ti->devfs_nonrewinding);
--    devfs_unregister(ti->devfs_rewinding);
--    devfs_unregister(ti->devfs_char_dir);
+-	return retval;
 -}
--#endif
 -
--void
--tapechar_setup (tape_info_t * ti)
+-inline static int 
+-eval_textcmd (gds_subvector_t * start, void *end)
 -{
--#ifdef CONFIG_DEVFS_FS
--    tapechar_mkdevfstree(ti);
--#endif
+-	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;
 -}
 -
--void
--tapechar_init (void)
+-inline static int 
+-eval_cpmsu (gds_vector_t * start, void *end)
 -{
--	int result;
--	tape_frontend_t *charfront,*temp;
--	tape_info_t* ti;
--
--	tape_init();
+-	gds_vector_t *vec;
+-	gds_subvector_t *vec_data;
+-	void *vec_end;
+-	int retval = 0;
 -
--	/* 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
+-	vec = start;
 -
--	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) {
--#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
--#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;
+-	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;
 -}
 -
--void
--tapechar_uninit (void)
+-inline static int 
+-eval_mdsmu (gds_vector_t * start, void *end)
 -{
--	unregister_chrdev (tape_major, "tape");
+-	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;
 -}
 -
--/*
-- * Tape device read function
-- */
--ssize_t
--tape_read (struct file *filp, char *data, size_t count, loff_t * ppos)
+-static int 
+-eval_evbuf (gds_vector_t * start, void *end)
 -{
--	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 */
--	ti = first_tape_info;
--	while ((ti != NULL) && (ti->rew_filp != filp) && (ti->nor_filp != filp))
--		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 (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 /* 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,"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);
--	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;
+-	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);
 -	}
--	tapestate_set (ti, TS_IDLE);
--	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;
+-	return retval;
 -}
 -
--/*
-- * Tape device write function
-- */
--ssize_t
--tape_write (struct file *filp, const char *data, size_t count, loff_t * ppos)
+-static inline int 
+-eval_hwc_receive_mask (_hwcb_mask_t mask)
 -{
--	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 */
+-	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;
 -	}
--	if ((ti->block_size != 0) && (count % ti->block_size != 0))
--		return -EIO;
--	if (ti->block_size == 0) {
--		block_size = count;
--		nblocks = 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 {
--		block_size = ti->block_size;
--		nblocks = count / (ti->block_size);
+-		internal_print (
+-				       DELAYED_WRITE,
+-				       HWC_RW_PRINT_HEADER
+-				     "can not read commands from operator\n");
+-		return -1;
 -	}
--#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);
+-}
+-
+-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
--	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;
+-		} else {
+-
+-			retval += eval_hwc_receive_mask
+-			    (scbuf->hwc_receive_mask);
 -		}
--		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);
+-	}
+-	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
--		written += block_size - ti->devstat.rescnt;
--		if (ti->devstat.rescnt > 0) {
--			*ppos = pos + written;
--			return written;
+-		} else {
+-
+-			retval += eval_hwc_send_mask
+-			    (scbuf->hwc_send_mask);
 -		}
 -	}
--#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;
+-	if (scbuf->validity_read_data_function_mask) {
+-
+-	}
+-	return retval;
 -}
 -
--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
+-#ifdef CONFIG_SMP
+-extern unsigned long cpu_online_map;
+-static volatile unsigned long cpu_quiesce_map;
 -
--		cqr = ti->discipline->mtrew (ti, mt_count);
--		break;
--	case MTOFFL:		// put drive offline
+-static void 
+-do_load_quiesce_psw (void)
+-{
+-	psw_t quiesce_psw;
 -
--		cqr = ti->discipline->mtoffl (ti, mt_count);
--		break;
--	case MTUNLOAD:		// unload the tape
+-	clear_bit (smp_processor_id (), &cpu_quiesce_map);
+-	if (smp_processor_id () == 0) {
 -
--		cqr = ti->discipline->mtunload (ti, mt_count);
--		break;
--	case MTWEOF:		// write tapemark
+-		while (cpu_quiesce_map != 0) ;
 -
--		cqr = ti->discipline->mtweof (ti, mt_count);
--		break;
--	case MTFSF:		// forward space file
+-		quiesce_psw.mask = _DW_PSW_MASK;
+-		quiesce_psw.addr = 0xfff;
+-		__load_psw (quiesce_psw);
+-	}
+-	signal_processor (smp_processor_id (), sigp_stop);
+-}
 -
--		cqr = ti->discipline->mtfsf (ti, mt_count);
--		break;
--	case MTBSF:		// backward space file
+-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 ();
+-}
 -
--		cqr = ti->discipline->mtbsf (ti, mt_count);
--		break;
--	case MTFSFM:		// forward space file, stop at BOT side
+-#else
+-static void 
+-do_machine_quiesce (void)
+-{
+-	psw_t quiesce_psw;
 -
--		cqr = ti->discipline->mtfsfm (ti, mt_count);
--		break;
--	case MTBSFM:		// backward space file, stop at BOT side
+-	quiesce_psw.mask = _DW_PSW_MASK;
+-	queisce_psw.addr = 0xfff;
+-	__load_psw (quiesce_psw);
+-}
 -
--		cqr = ti->discipline->mtbsfm (ti, mt_count);
--		break;
--	case MTFSR:		// forward space file
+-#endif
 -
--		cqr = ti->discipline->mtfsr (ti, mt_count);
--		break;
--	case MTBSR:		// backward space file
+-static int 
+-process_evbufs (void *start, void *end)
+-{
+-	int retval = 0;
+-	evbuf_t *evbuf;
+-	void *evbuf_end;
+-	gds_vector_t *evbuf_data;
 -
--		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
+-	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
 -
--	case MTRETEN:		// retension the tape
+-			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:
 -
--		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);
+-			_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;
 -		}
--		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;
+-		evbuf = (evbuf_t *) evbuf_end;
 -	}
--	if (cqr == NULL) {
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,6,"c:ccwg fail");
+-	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
--		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
+-	memset (hwcb, 0x00, PAGE_SIZE);
+-	memcpy (hwcb, &read_hwcb_template, sizeof (read_hwcb_t));
 -
--		return tape_mtioctop (filp, MTBSFM, 1);
--	case MTBSF:		//need to skip forward over the filemark
+-	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
 -
--		return tape_mtioctop (filp, MTFSF, 1);
+-	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;
 -	}
--#ifdef TAPE_DEBUG
--	debug_text_event (tape_debug_area,6,"c:mtio done");
--#endif
--	return 0;
+-
+-	return retval;
 -}
 -
--/*
-- * Tape device io controls.
-- */
--int
--tape_ioctl (struct inode *inode, struct file *filp,
--	    unsigned int cmd, unsigned long arg)
+-static int 
+-unconditional_read_2 (u32 ext_int_param)
 -{
--	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;
+-	read_hwcb_t *hwcb = (read_hwcb_t *) hwc_data.page;
 -
--	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");
+-#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
--	    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
+-	hwc_data.current_servc = 0;
+-	hwc_data.current_hwcb = NULL;
 -
--		if (ti->devstat.ii.sense.data[1] & 0x02)
--			get.mt_gstat &= GMT_WR_PROT (1);	// write protected
+-	switch (hwcb->response_code) {
 -
--		if (ti->devstat.ii.sense.data[1] & 0x40)
--			get.mt_gstat &= GMT_ONLINE (1);		//drive online
+-	case 0x0020:
+-	case 0x0220:
+-		return process_evbufs (
+-		     (void *) (((unsigned long) hwcb) + sizeof (read_hwcb_t)),
+-			    (void *) (((unsigned long) hwcb) + hwcb->length));
 -
--		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;
+-	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:
--#ifdef TAPE_DEBUG
--	        debug_text_event (tape_debug_area,3,"c:ioct inv");
--#endif	    
--		return -EINVAL;
+-		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;
 -	}
 -}
 -
--/*
-- * Tape device open function.
-- */
--int
--tape_open (struct inode *inode, struct file *filp)
+-static int 
+-write_event_mask_1 (void)
 -{
--	tape_info_t *ti;
--	kdev_t dev;
--	long lockflags;
+-	unsigned int condition_code;
+-	int retval;
 -
--	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");
+-	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
--		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);
+-	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;
+-	}
 -
--#ifdef MODULE
--	MOD_INC_USE_COUNT;
--#endif				/* MODULE */
--	return 0;
+-	return retval;
 -}
 -
--/*
-- * Tape device release function.
-- */
--int
--tape_release (struct inode *inode, struct file *filp)
+-static int 
+-write_event_mask_2 (u32 ext_int_param)
 -{
--	long lockflags;
--	tape_info_t *ti,*lastti;
--	ccw_req_t *cqr = NULL;
--	int rc = 0;
+-	init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page;
+-	int retval = 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);
+-	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
--	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");
+-	} 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
--			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);
+-		} else {
+-			retval += eval_hwc_receive_mask
+-			    (hwcb->hwc_receive_mask);
+-			retval += eval_hwc_send_mask (hwcb->hwc_send_mask);
 -		}
 -	}
--	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;
+-
+-	hwc_data.current_servc = 0;
+-	hwc_data.current_hwcb = NULL;
+-
+-	return retval;
 -}
-=== drivers/s390/char/tapechar.h
-==================================================================
---- drivers/s390/char/tapechar.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tapechar.h   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-- *
-- *
-- ****************************************************************************
-- */
+-static int 
+-set_hwc_ioctls (hwc_ioctls_t * ioctls, char correct)
+-{
+-	int retval = 0;
+-	hwc_ioctls_t tmp;
 -
--#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 */
-=== drivers/s390/char/tapedefs.h
-==================================================================
---- drivers/s390/char/tapedefs.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tapedefs.h   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-- *
-- *
-- ***********************************************************************
-- */
+-	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;
 -
--/* Kernel Version Compatibility section */
--#include <linux/version.h>
--#include <linux/blkdev.h>
--#include <linux/blk.h>
--#include <asm/irq.h>
+-	tmp.echo = ioctls->echo;
 -
--#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")
--#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.
+-	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 (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)
--#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;
+-	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;
 -}
--static inline void 
--tape_dequeue_request( request_queue_t * q, struct request *req )
+-
+-int 
+-do_hwc_init (void)
 -{
--        *q = req->next;
--        req->next = NULL;
--}
--#endif 
-=== drivers/s390/char/tapeblock.c
-==================================================================
---- drivers/s390/char/tapeblock.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tapeblock.c   (/trunk/2.4.27)   (revision 52)
-@@ -1,593 +0,0 @@
+-	int retval;
 -
--/***************************************************************************
-- *
-- *  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>
-- *
-- *
-- ****************************************************************************
-- */
+-	memcpy (hwc_data.page, &init_hwcb_template, sizeof (init_hwcb_t));
 -
--#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"
+-	do {
 -
--#define PRINTK_HEADER "TBLOCK:"
+-		retval = write_event_mask_1 ();
 -
--/*
-- * 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 */
--        };
+-		if (retval == -EBUSY) {
 -
--int    tapeblock_major = 0;
+-			hwc_data.flags |= HWC_INIT;
 -
--#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
+-			__ctl_store (cr0, 0, 0);
+-			cr0_save = cr0;
+-			cr0 |= 0x00000200;
+-			cr0 &= 0xFFFFF3AC;
+-			__ctl_load (cr0, 0, 0);
 -
--static request_queue_t* tapeblock_getqueue (kdev_t kdev);
+-			asm volatile ("STOSM %0,0x01"
+-				      :"=m" (psw_mask)::"memory");
 -
--#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);
--}
+-			while (!(hwc_data.flags & HWC_INTERRUPT))
+-				barrier ();
 -
--void
--tapeblock_rmdevfstree (tape_info_t* ti) {
--    devfs_unregister(ti->devfs_disc);
--    devfs_unregister(ti->devfs_block_dir);
--}
--#endif
+-			asm volatile ("STNSM %0,0xFE"
+-				      :"=m" (psw_mask)::"memory");
 -
--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
--}
+-			__ctl_load (cr0_save, 0, 0);
 -
--int
--tapeblock_init(void) {
--    int result;
--    tape_frontend_t* blkfront,*temp;
--    tape_info_t* ti;
+-			hwc_data.flags &= ~HWC_INIT;
+-		}
+-	} while (retval == -EBUSY);
 -
--    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;
+-	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);
 -
--void 
--tapeblock_uninit(void) {
--    unregister_blkdev(tapeblock_major, "tBLK");
--}
+-int 
+-hwc_init (void)
+-{
+-	int retval;
 -
--int
--tapeblock_open(struct inode *inode, struct file *filp) {
--    tape_info_t *ti;
--    kdev_t dev;
--    int rc;
--    long lockflags;
+-#ifdef BUFFER_STRESS_TEST
 -
--    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;
--}
+-	init_hwcb_t *hwcb;
+-	int i;
 -
--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 */
+-
+-	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;
 -	}
--#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;
--}
+-	retval = set_hwc_ioctls (&hwc_data.init_ioctls, 1);
 -
--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;
--}
+-	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;
 -
--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;
+-	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)));
 -	}
--	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);
+-
+-	return /*retval */ 0;
 -}
 -
--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);
+-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;
 -}
 -
--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);
+-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;
 -}
 -
--void
--schedule_tapeblock_exec_IO (tape_info_t *ti)
+-int 
+-hwc_send (hwc_request_t * req)
 -{
--	/* 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;
+-	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;
 -
--	queue_task (&ti->bh_tq, &tq_immediate);
--	mark_bh (IMMEDIATE_BH);
--	return;
+-	}
+-      unlock:
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-	return retval;
 -}
 -
--/* 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
+-EXPORT_SYMBOL (hwc_send);
 -
--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;
+-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;
 -}
 -
--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;
+-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 (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 (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:
+-				}
+-			}
 -		}
--		if (tapestate_get (ti) != TS_DONE) {
--		    tapestate_set (ti, TS_IDLE);
--		    s390irq_spin_unlock_irqrestore (ti->devinfo.irq, lockflags);
--		    return -EIO;
+-	} 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);
 -		}
--		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;
+-
+-	if (evbuf_pending) {
+-
+-		unconditional_read_1 ();
+-	} else {
+-
+-		write_event_data_1 ();
 -	}
--	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;
--}
-=== drivers/s390/char/tape3480.c
-==================================================================
---- drivers/s390/char/tape3480.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape3480.c   (/trunk/2.4.27)   (revision 52)
-@@ -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"
+-	if (!hwc_data.calls || !hwc_data.calls->wake_up)
+-		return;
+-	(hwc_data.calls->wake_up) ();
+-}
 -
--tape_event_handler_t tape3480_event_handler_table[TS_SIZE][TE_SIZE] =
+-void 
+-hwc_interrupt_handler (struct pt_regs *regs, __u16 code)
 -{
--    /* {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 */
+-	int cpu = smp_processor_id ();
 -
--devreg_t tape3480_devreg = {
--    ci:
--    {hc:
--     {ctype:0x3480}},
--    flag:DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS,
--    oper_func:tape_oper_handler
--};
+-	u32 ext_int_param = hwc_ext_int_param ();
 -
+-	irq_enter (cpu, 0x2401);
 -
--void
--tape3480_setup_assist (tape_info_t * ti)
+-	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)
 -{
--	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;
+-
+-	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);
 -
--void
--tape3480_shutdown (int autoprobe) {
--    if (autoprobe)
--	s390_device_unregister(&tape3480_devreg);
--}
+-	goto out;
 -
--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;
+-      fault:
+-	retval = -EFAULT;
+-	goto out;
+-      noioctlcmd:
+-	retval = -ENOIOCTLCMD;
+-      out:
+-	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-	return retval;
 -}
-=== drivers/s390/char/tapeblock.h
-==================================================================
---- drivers/s390/char/tapeblock.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tapeblock.h   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-- *
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_rw.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 TAPEBLOCK_H
--#define TAPEBLOCK_H
--#include <linux/config.h>
--#define PARTN_BITS 0
+-#ifndef __HWC_RW_H__
+-#define __HWC_RW_H__
 -
--#define TAPEBLOCK_READAHEAD 30
--#define TAPEBLOCK_MAJOR 0
+-#include <linux/ioctl.h>
 -
--#define TAPEBLOCK_DEFAULTMODE 0060644
+-typedef struct {
 -
--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
-=== drivers/s390/char/hwc_cpi.c
-==================================================================
---- drivers/s390/char/hwc_cpi.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/hwc_cpi.c   (/trunk/2.4.27)   (revision 52)
-@@ -1,211 +0,0 @@
+-	void (*move_input) (unsigned char *, unsigned int);
 -
--/*
-- * Author: Martin Peschke <mpeschke at de.ibm.com>
-- * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation
-- */
+-	void (*wake_up) (void);
+-} hwc_high_level_calls_t;
 -
--#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"
+-struct _hwc_request;
 -
--#define CPI_RETRIES		3
--#define CPI_SLEEP_TICKS		50
+-typedef void hwc_callback_t (struct _hwc_request *);
 -
--#define CPI_LENGTH_SYSTEM_TYPE	8
--#define CPI_LENGTH_SYSTEM_NAME	8
--#define CPI_LENGTH_SYSPLEX_NAME	8
+-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 {
--	_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)) 
+-	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;
 -
--cpi_evbuf_t;
+-static hwc_ioctls_t _hwc_ioctls;
 -
--typedef struct _cpi_hwcb_t {
--	_HWCB_HEADER
--	cpi_evbuf_t cpi_evbuf;
--} __attribute__ ((packed)) 
+-#define HWC_IOCTL_LETTER 'B'
 -
--cpi_hwcb_t;
+-#define TIOCHWCSHTAB	_IOW(HWC_IOCTL_LETTER, 0, _hwc_ioctls.width_htab)
 -
--cpi_hwcb_t *cpi_hwcb;
+-#define TIOCHWCSECHO	_IOW(HWC_IOCTL_LETTER, 1, _hwc_ioctls.echo)
 -
--static int __init cpi_module_init (void);
--static void __exit cpi_module_exit (void);
+-#define TIOCHWCSCOLS	_IOW(HWC_IOCTL_LETTER, 2, _hwc_ioctls.columns)
 -
--module_init (cpi_module_init);
--module_exit (cpi_module_exit);
+-#define TIOCHWCSNL	_IOW(HWC_IOCTL_LETTER, 4, _hwc_ioctls.final_nl)
 -
--MODULE_AUTHOR (
--		      "Martin Peschke, IBM Deutschland Entwicklung GmbH "
--		      "<mpeschke at de.ibm.com>");
+-#define TIOCHWCSOBUF	_IOW(HWC_IOCTL_LETTER, 5, _hwc_ioctls.max_hwcb)
 -
--MODULE_DESCRIPTION (
--  "identify this operating system instance to the S/390 or zSeries hardware");
+-#define TIOCHWCSINIT	_IO(HWC_IOCTL_LETTER, 6)
 -
--static char *system_name = NULL;
--MODULE_PARM (system_name, "s");
--MODULE_PARM_DESC (system_name, "e.g. hostname - max. 8 characters");
+-#define TIOCHWCSCASE	_IOW(HWC_IOCTL_LETTER, 7, _hwc_ioctls.tolower)
 -
--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
+-#define TIOCHWCSDELIM	_IOW(HWC_IOCTL_LETTER, 9, _hwc_ioctls.delim)
 -
--static char *system_type = "LINUX";
+-#define TIOCHWCGHTAB	_IOR(HWC_IOCTL_LETTER, 10, _hwc_ioctls.width_htab)
 -
--hwc_request_t cpi_request =
--{};
+-#define TIOCHWCGECHO	_IOR(HWC_IOCTL_LETTER, 11, _hwc_ioctls.echo)
 -
--hwc_callback_t cpi_callback;
+-#define TIOCHWCGCOLS	_IOR(HWC_IOCTL_LETTER, 12, _hwc_ioctls.columns)
 -
--static DECLARE_MUTEX_LOCKED (sem);
+-#define TIOCHWCGNL	_IOR(HWC_IOCTL_LETTER, 14, _hwc_ioctls.final_nl)
 -
--static int __init 
--cpi_module_init (void)
--{
--	int retval;
--	int system_type_length;
--	int system_name_length;
--	int sysplex_name_length = 0;
--	int retries;
+-#define TIOCHWCGOBUF	_IOR(HWC_IOCTL_LETTER, 15, _hwc_ioctls.max_hwcb)
 -
--	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));
+-#define TIOCHWCGINIT	_IOR(HWC_IOCTL_LETTER, 16, _hwc_ioctls)
 -
--	cpi_hwcb->length = sizeof (cpi_hwcb_t);
--	cpi_hwcb->cpi_evbuf.length = sizeof (cpi_evbuf_t);
--	cpi_hwcb->cpi_evbuf.type = 0x0B;
+-#define TIOCHWCGCASE	_IOR(HWC_IOCTL_LETTER, 17, _hwc_ioctls.tolower)
 -
--	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 TIOCHWCGDELIM	_IOR(HWC_IOCTL_LETTER, 19, _hwc_ioctls.delim)
 -
--	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);
+-#define TIOCHWCGKBUF	_IOR(HWC_IOCTL_LETTER, 20, _hwc_ioctls.max_hwcb)
 -
--	cpi_hwcb->cpi_evbuf.system_level = LINUX_VERSION_CODE;
+-#define TIOCHWCGCURR	_IOR(HWC_IOCTL_LETTER, 21, _hwc_ioctls)
 -
--	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;
+-#ifndef __HWC_RW_C__
 -
--	for (retries = CPI_RETRIES; retries; retries--) {
--		retval = hwc_send (&cpi_request);
--		if (retval) {
+-extern int hwc_init (void);
 -
--			set_current_state (TASK_INTERRUPTIBLE);
--			schedule_timeout (CPI_SLEEP_TICKS);
--		} else {
+-extern int hwc_write (int from_user, const unsigned char *, unsigned int);
 -
--			down (&sem);
+-extern unsigned int hwc_chars_in_buffer (unsigned char);
 -
--			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;
--		}
--	}
+-extern unsigned int hwc_write_room (unsigned char);
 -
--	printk ("cpi: failed (%i)\n", retval);
+-extern void hwc_flush_buffer (unsigned char);
 -
--      free:
--	kfree (cpi_hwcb);
+-extern void hwc_unblank (void);
 -
--      out:
--	return retval;
--}
+-extern signed int hwc_ioctl (unsigned int, unsigned long);
 -
--static void __exit 
--cpi_module_exit (void)
--{
--	printk ("cpi: exit\n");
--}
+-extern void do_hwc_interrupt (void);
 -
--void 
--cpi_callback (hwc_request_t * req)
--{
--	up (&sem);
--}
-=== drivers/s390/char/tape3480.h
-==================================================================
---- drivers/s390/char/tape3480.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape3480.h   (/trunk/2.4.27)   (revision 52)
-@@ -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>
-- *
-- ****************************************************************************
-- */
+-extern int hwc_printk (const char *,...);
 -
--#ifndef _TAPE3480_H
+-extern signed int hwc_register_calls (hwc_high_level_calls_t *);
 -
--#define _TAPE3480_H
+-extern signed int hwc_unregister_calls (hwc_high_level_calls_t *);
 -
+-extern int hwc_send (hwc_request_t *);
 -
--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
-=== drivers/s390/char/tape34xx.c
-==================================================================
---- drivers/s390/char/tape34xx.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape34xx.c   (/trunk/2.4.27)   (revision 52)
-@@ -1,2389 +0,0 @@
--/***************************************************************************
-- *
-- *  drivers/s390/char/tape34xx.c
-- *    common tape device discipline for 34xx tapes.
+-#endif
+-
+-#endif
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/hwc_tty.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 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>
+- *  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 "tapedefs.h"
 -#include <linux/config.h>
--#include <linux/version.h>
--#include <linux/stddef.h>
--#include <linux/kernel.h>
--#include <asm/types.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 <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:"
+-#include "hwc_rw.h"
+-#include "ctrlchar.h"
 -
--tape_event_handler_t tape34xx_event_handler_table[TS_SIZE][TE_SIZE] =
+-#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)
 -{
--    /* {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 */
 -
+-	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;
 -
--int
--tape34xx_ioctl_overload (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
--{
--	return -EINVAL;		// no additional ioctls
+-	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;
 -}
 -
--ccw_req_t *
--tape34xx_write_block (const char *data, size_t count, tape_info_t * ti)
+-static void 
+-hwc_tty_close (struct tty_struct *tty,
+-	       struct file *filp)
 -{
--	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;
+-	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;
 -	}
--	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++;
+-	if (tty->count > 1)
+-		return;
 -
--	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;
--}
+-	hwc_tty_data.tty = NULL;
 -
--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 */
+-	hwc_unregister_calls (&(hwc_tty_data.calls));
 -}
 -
--ccw_req_t *
--tape34xx_read_block (const char *data, size_t count, tape_info_t * ti)
+-static int 
+-hwc_tty_write_room (struct tty_struct *tty)
 -{
--	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++;
+-	int retval;
 -
--	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;
+-	retval = hwc_write_room (IN_BUFS_TOTAL);
+-	return retval;
 -}
 -
--ccw_req_t *
--tape34xx_read_opposite (tape_info_t * ti,int novalue)
+-static int 
+-hwc_tty_write (struct tty_struct *tty,
+-	       int from_user,
+-	       const unsigned char *buf,
+-	       int count)
 -{
--	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++;
+-	int retval;
 -
--	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;
+-	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;
 -	}
--	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;
+-	retval = hwc_write (from_user, buf, count);
+-	return retval;
 -}
 -
--void 
--tape34xx_free_read_block (ccw_req_t * cqr, tape_info_t * ti)
+-static void 
+-hwc_tty_put_char (struct tty_struct *tty,
+-		  unsigned char ch)
 -{
--	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 */
--}
+-	unsigned long flags;
 -
--/*
-- * 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++;
+-	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;
 -	}
--	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;
+-	hwc_tty_data.buf[hwc_tty_data.buf_count] = ch;
+-	hwc_tty_data.buf_count++;
+-	spin_unlock_irqrestore (&hwc_tty_data.lock, flags);
 -}
 -
--/*
-- * 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 
+-hwc_tty_flush_chars (struct tty_struct *tty)
 -{
--	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;
--}
+-	unsigned long flags;
 -
--/*
-- * 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;
+-	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);
 -}
 -
--/*
-- * MTBSR: Backward space over 'count' tape blocks.
-- * (blocksize is set via MTSETBLK.
-- */
--ccw_req_t *
--tape34xx_mtbsr (tape_info_t * ti, int count)
+-static int 
+-hwc_tty_chars_in_buffer (struct tty_struct *tty)
 -{
--	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;
--}
+-	int retval;
 -
--/*
-- * 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;
+-	retval = hwc_chars_in_buffer (IN_BUFS_TOTAL);
+-	return retval;
 -}
 -
--/*
-- * MTREW: Rewind the tape.
-- */
--ccw_req_t *
--tape34xx_mtrew (tape_info_t * ti, int count)
+-static void 
+-hwc_tty_flush_buffer (struct tty_struct *tty)
 -{
--	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;
+-	hwc_tty_wake_up ();
 -}
 -
--/*
-- * MTOFFL: Rewind the tape and put the drive off-line.
-- * Implement 'rewind unload'
-- */
--ccw_req_t *
--tape34xx_mtoffl (tape_info_t * ti, int count)
+-static int 
+-hwc_tty_ioctl (
+-		      struct tty_struct *tty,
+-		      struct file *file,
+-		      unsigned int cmd,
+-		      unsigned long arg)
 -{
--	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;
+-	if (tty->flags & (1 << TTY_IO_ERROR))
+-		return -EIO;
+-
+-	return hwc_ioctl (cmd, arg);
 -}
 -
--/*
-- * MTNOP: 'No operation'.
-- */
--ccw_req_t *
--tape34xx_mtnop (tape_info_t * ti, int count)
+-void 
+-hwc_tty_wake_up (void)
 -{
--	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;
+-	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);
 -}
 -
--/*
-- * 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_tty_input (unsigned char *buf, unsigned 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++;
+-	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 ();
 -	}
--	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)
+-void 
+-hwc_tty_init (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,"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;
+-	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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/sclp.c	2006-01-30 22:25:26.000000000 -0700
+@@ -0,0 +1,854 @@
++/*
++ *  drivers/s390/char/sclp.c
++ *     core function to access sclp interface
++ *
++ *  S390 version
++ *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
++ *		 Martin Schwidefsky <schwidefsky at de.ibm.com>
++ */
++
++#include <linux/config.h>
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/kmod.h>
++#include <linux/bootmem.h>
++#include <linux/errno.h>
++#include <linux/ptrace.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/interrupt.h>
++#include <linux/timer.h>
++#include <linux/init.h>
++#include <linux/reboot.h>
++#include <asm/irq.h>
++#include <asm/s390_ext.h>
++#include <asm/processor.h>
++
++#include "sclp.h"
++
++#define SCLP_CORE_PRINT_HEADER "sclp low level driver: "
++
++/* Structure for register_early_external_interrupt. */
++static ext_int_info_t ext_int_info_hwc;
++
++/* spinlock to protect global variables of sclp_core */
++static spinlock_t sclp_lock;
++
++/* Mask of valid sclp events */
++static sccb_mask_t sclp_receive_mask;
++static sccb_mask_t sclp_send_mask;
++
++/* List of registered event types */
++static struct list_head sclp_reg_list;
++
++/* sccb queue */
++static struct list_head sclp_req_queue;
++
++/* sccb for unconditional read */
++static struct sclp_req sclp_read_req;
++static char sclp_read_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
++/* sccb for write mask sccb */
++static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
++
++/* Timer for init mask retries. */
++static struct timer_list retry_timer;
++
++/* Timer for busy retries. */
++static struct timer_list sclp_busy_timer;
++
++static volatile unsigned long sclp_status = 0;
++/* some status flags */
++#define SCLP_INIT		0
++#define SCLP_RUNNING		1
++#define SCLP_READING		2
++#define SCLP_SHUTDOWN		3
++
++#define SCLP_INIT_POLL_INTERVAL	1
++#define SCLP_BUSY_POLL_INTERVAL	1
++
++#define SCLP_COMMAND_INITIATED	0
++#define SCLP_BUSY		2
++#define SCLP_NOT_OPERATIONAL	3
++
++/*
++ * assembler instruction for Service Call
++ */
++static int
++__service_call(sclp_cmdw_t command, void *sccb)
++{
++	int cc;
++
++	/*
++	 *  Mnemonic:	SERVC	Rx, Ry	[RRE]
++	 *
++	 *  Rx: SCLP command word
++	 *  Ry: address of SCCB
++	 */
++	__asm__ __volatile__(
++		"   .insn rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */
++		"   ipm	  %0\n"
++		"   srl	  %0,28"
++		: "=&d" (cc)
++		: "d" (command), "a" (__pa(sccb))
++		: "cc", "memory" );
++	/*
++	 * cc == 0:   Service Call succesful initiated
++	 * cc == 2:   SCLP busy, new Service Call not initiated,
++	 *	      new SCCB unchanged
++	 * cc == 3:   SCLP function not operational
++	 */
++	if (cc == SCLP_NOT_OPERATIONAL)
++		return -EIO;
++	if (cc == SCLP_BUSY)
++		return -EBUSY;
++	return 0;
++}
++
++static void
++sclp_start_request(void)
++{
++	struct sclp_req *req;
++	int rc;
++	unsigned long flags;
++
++	spin_lock_irqsave(&sclp_lock, flags);
++	/* quick exit if sclp is already in use */
++	if (test_bit(SCLP_RUNNING, &sclp_status)) {
++		spin_unlock_irqrestore(&sclp_lock, flags);
++		return;
++	}
++	/* Try to start requests from the request queue. */
++	while (!list_empty(&sclp_req_queue)) {
++		req = list_entry(sclp_req_queue.next, struct sclp_req, list);
++		rc = __service_call(req->command, req->sccb);
++		if (rc == 0) {
++			/* Sucessfully started request. */
++			req->status = SCLP_REQ_RUNNING;
++			/* Request active. Set running indication. */
++			set_bit(SCLP_RUNNING, &sclp_status);
++			break;
++		}
++		if (rc == -EBUSY) {
++			/**
++			 * SCLP is busy but no request is running.
++			 * Try again later.
++			 */
++			if (!timer_pending(&sclp_busy_timer) ||
++			    !mod_timer(&sclp_busy_timer,
++				       jiffies + SCLP_BUSY_POLL_INTERVAL*HZ)) {
++				sclp_busy_timer.function =
++					(void *) sclp_start_request;
++				sclp_busy_timer.expires =
++					jiffies + SCLP_BUSY_POLL_INTERVAL*HZ;
++				add_timer(&sclp_busy_timer);
++			}
++			break;
++		}
++		/* Request failed. */
++		req->status = SCLP_REQ_FAILED;
++		list_del(&req->list);
++		if (req->callback) {
++			spin_unlock_irqrestore(&sclp_lock, flags);
++			req->callback(req, req->callback_data);
++			spin_lock_irqsave(&sclp_lock, flags);
++		}
++	}
++	spin_unlock_irqrestore(&sclp_lock, flags);
++}
++
++static int
++sclp_process_evbufs(struct sccb_header *sccb)
++{
++	int result;
++	unsigned long flags;
++	struct evbuf_header *evbuf;
++	struct list_head *l;
++	struct sclp_register *t;
++
++	spin_lock_irqsave(&sclp_lock, flags);
++	evbuf = (struct evbuf_header *) (sccb + 1);
++	result = 0;
++	while ((addr_t) evbuf < (addr_t) sccb + sccb->length) {
++		/* check registered event */
++		t = NULL;
++		list_for_each(l, &sclp_reg_list) {
++			t = list_entry(l, struct sclp_register, list);
++			if (t->receive_mask & (1 << (32 - evbuf->type))) {
++				if (t->receiver_fn != NULL) {
++					spin_unlock_irqrestore(&sclp_lock,
++							       flags);
++					t->receiver_fn(evbuf);
++					spin_lock_irqsave(&sclp_lock, flags);
++				}
++				break;
++			}
++			else
++				t = NULL;
++		}
++		/* Check for unrequested event buffer */
++		if (t == NULL)
++			result = -ENOSYS;
++		evbuf = (struct evbuf_header *)
++				((addr_t) evbuf + evbuf->length);
++	}
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	return result;
++}
++
++char *
++sclp_error_message(u16 rc)
++{
++	static struct {
++		u16 code; char *msg;
++	} sclp_errors[] = {
++		{ 0x0000, "No response code stored (machine malfunction)" },
++		{ 0x0020, "Normal Completion" },
++		{ 0x0040, "SCLP equipment check" },
++		{ 0x0100, "SCCB boundary violation" },
++		{ 0x01f0, "Invalid command" },
++		{ 0x0220, "Normal Completion; suppressed buffers pending" },
++		{ 0x0300, "Insufficient SCCB length" },
++		{ 0x0340, "Contained SCLP equipment check" },
++		{ 0x05f0, "Target resource in improper state" },
++		{ 0x40f0, "Invalid function code/not installed" },
++		{ 0x60f0, "No buffers stored" },
++		{ 0x62f0, "No buffers stored; suppressed buffers pending" },
++		{ 0x70f0, "Invalid selection mask" },
++		{ 0x71f0, "Event buffer exceeds available space" },
++		{ 0x72f0, "Inconsistent lengths" },
++		{ 0x73f0, "Event buffer syntax error" }
++	};
++	int i;
++	for (i = 0; i < sizeof(sclp_errors)/sizeof(sclp_errors[0]); i++)
++		if (rc == sclp_errors[i].code)
++			return sclp_errors[i].msg;
++	return "Invalid response code";
++}
++
++/*
++ * postprocessing of unconditional read service call
++ */
++static void
++sclp_unconditional_read_cb(struct sclp_req *read_req, void *data)
++{
++	struct sccb_header *sccb;
++
++	sccb = read_req->sccb;
++	if (sccb->response_code == 0x0020 ||
++	    sccb->response_code == 0x0220) {
++		if (sclp_process_evbufs(sccb) != 0)
++			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
++			       "unconditional read: "
++			       "unrequested event buffer received.\n");
++	}
++
++	if (sccb->response_code != 0x0020)
++		printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
++		       "unconditional read: %s (response code=0x%x).\n",
++		       sclp_error_message(sccb->response_code),
++		       sccb->response_code);
++
++	clear_bit(SCLP_READING, &sclp_status);
++}
++
++/*
++ * Function to queue Read Event Data/Unconditional Read
++ */
++static void
++__sclp_unconditional_read(void)
++{
++	struct sccb_header *sccb;
++	struct sclp_req *read_req;
++
++	/*
++	 * Don't try to initiate Unconditional Read if we are not able to
++	 * receive anything
++	 */
++	if (sclp_receive_mask == 0)
++		return;
++	/* Don't try reading if a read is already outstanding */
++	if (test_and_set_bit(SCLP_READING, &sclp_status))
++		return;
++	/* Initialize read sccb */
++	sccb = (struct sccb_header *) sclp_read_sccb;
++	clear_page(sccb);
++	sccb->length = PAGE_SIZE;
++	sccb->function_code = 0;	/* unconditional read */
++	sccb->control_mask[2] = 0x80;	/* variable length response */
++	/* Initialize request structure */
++	read_req = &sclp_read_req;
++	read_req->command = SCLP_CMDW_READDATA;
++	read_req->status = SCLP_REQ_QUEUED;
++	read_req->callback = sclp_unconditional_read_cb;
++	read_req->sccb = sccb;
++	/* Add read request to the head of queue */
++	list_add(&read_req->list, &sclp_req_queue);
++}
++
++/* Bit masks to interpret external interruption parameter contents. */
++#define EXT_INT_SCCB_MASK		0xfffffff8
++#define EXT_INT_STATECHANGE_PENDING	0x00000002
++#define EXT_INT_EVBUF_PENDING		0x00000001
++
++/*
++ * Handler for service-signal external interruptions
++ */
++static void
++sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
++{
++	u32 ext_int_param, finished_sccb, evbuf_pending;
++	struct list_head *l;
++	struct sclp_req *req, *tmp;
++	int cpu;
++
++	spin_lock(&sclp_lock);
++	/*
++	 * Only process interrupt if sclp is initialized.
++	 * This avoids strange effects for a pending request
++	 * from before the last re-ipl.
++	 */
++	if (!test_bit(SCLP_INIT, &sclp_status)) {
++		/* Now clear the running bit */
++		clear_bit(SCLP_RUNNING, &sclp_status);
++		spin_unlock(&sclp_lock);
++		return;
++	}
++	ext_int_param = S390_lowcore.ext_params;
++	finished_sccb = ext_int_param & EXT_INT_SCCB_MASK;
++	evbuf_pending = ext_int_param & (EXT_INT_EVBUF_PENDING |
++					 EXT_INT_STATECHANGE_PENDING);
++	cpu = smp_processor_id();
++	irq_enter(cpu, 0x2401);
++	req = NULL;
++	if (finished_sccb != 0U) {
++		list_for_each(l, &sclp_req_queue) {
++			tmp = list_entry(l, struct sclp_req, list);
++			if (finished_sccb == (u32)(addr_t) tmp->sccb) {
++				list_del(&tmp->list);
++				req = tmp;
++				break;
++			}
++		}
++	}
++	spin_unlock(&sclp_lock);
++	/* Perform callback */
++	if (req != NULL) {
++		req->status = SCLP_REQ_DONE;
++		if (req->callback != NULL)
++			req->callback(req, req->callback_data);
++	}
++	spin_lock(&sclp_lock);
++	/* Head queue a read sccb if an event buffer is pending */
++	if (evbuf_pending)
++		__sclp_unconditional_read();
++	/* Now clear the running bit if SCLP indicated a finished SCCB */
++	if (finished_sccb != 0U)
++		clear_bit(SCLP_RUNNING, &sclp_status);
++	spin_unlock(&sclp_lock);
++	/* and start next request on the queue */
++	sclp_start_request();
++	irq_exit(cpu, 0x2401);
++}
++
++/*
++ * Wait synchronously for external interrupt of sclp. We may not receive
++ * any other external interrupt, so we disable all other external interrupts
++ * in control register 0.
++ */
++void
++sclp_sync_wait(void)
++{
++	unsigned long psw_mask;
++	unsigned long cr0, cr0_sync;
++
++	/* Prevent BH from executing. */
++	local_bh_disable();
++	/*
++	 * save cr0
++	 * enable service signal external interruption (cr0.22)
++	 * disable cr0.20-21, cr0.25, cr0.27, cr0.30-31
++	 * don't touch any other bit in cr0
++	 */
++	__ctl_store(cr0, 0, 0);
++	cr0_sync = cr0;
++	cr0_sync |= 0x00000200;
++	cr0_sync &= 0xFFFFF3AC;
++	__ctl_load(cr0_sync, 0, 0);
++
++	/* enable external interruptions (PSW-mask.7) */
++	asm volatile ("STOSM 0(%1),0x01"
++		      : "=m" (psw_mask) : "a" (&psw_mask) : "memory");
++
++	/* wait until ISR signals receipt of interrupt */
++	while (test_bit(SCLP_RUNNING, &sclp_status)) {
++		barrier();
++		cpu_relax();
++	}
++
++	/* disable external interruptions */
++	asm volatile ("SSM 0(%0)"
++		      : : "a" (&psw_mask) : "memory");
++
++	/* restore cr0 */
++	__ctl_load(cr0, 0, 0);
++	__local_bh_enable();
++}
++
++/*
++ * Queue an SCLP request. Request will immediately be processed if queue is
++ * empty.
++ */
++void
++sclp_add_request(struct sclp_req *req)
++{
++	unsigned long flags;
++
++	if (!test_bit(SCLP_INIT, &sclp_status)) {
++		req->status = SCLP_REQ_FAILED;
++		if (req->callback != NULL)
++			req->callback(req, req->callback_data);
++		return;
++	}
++	spin_lock_irqsave(&sclp_lock, flags);
++	/* queue the request */
++	req->status = SCLP_REQ_QUEUED;
++	list_add_tail(&req->list, &sclp_req_queue);
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	/* try to start the first request on the queue */
++	sclp_start_request();
++}
++
++/* state change notification */
++struct sclp_statechangebuf {
++	struct evbuf_header	header;
++	u8		validity_sclp_active_facility_mask : 1;
++	u8		validity_sclp_receive_mask : 1;
++	u8		validity_sclp_send_mask : 1;
++	u8		validity_read_data_function_mask : 1;
++	u16		_zeros : 12;
++	u16		mask_length;
++	u64		sclp_active_facility_mask;
++	sccb_mask_t	sclp_receive_mask;
++	sccb_mask_t	sclp_send_mask;
++	u32		read_data_function_mask;
++} __attribute__((packed));
++
++static inline void
++__sclp_notify_state_change(void)
++{
++	struct list_head *l;
++	struct sclp_register *t;
++	sccb_mask_t receive_mask, send_mask;
++
++	list_for_each(l, &sclp_reg_list) {
++		t = list_entry(l, struct sclp_register, list);
++		receive_mask = t->receive_mask & sclp_receive_mask;
++		send_mask = t->send_mask & sclp_send_mask;
++		if (t->sclp_receive_mask != receive_mask ||
++		    t->sclp_send_mask != send_mask) {
++			t->sclp_receive_mask = receive_mask;
++			t->sclp_send_mask = send_mask;
++			if (t->state_change_fn != NULL)
++				t->state_change_fn(t);
++		}
++	}
++}
++
++static void
++sclp_state_change(struct evbuf_header *evbuf)
++{
++	unsigned long flags;
++	struct sclp_statechangebuf *scbuf;
++
++	spin_lock_irqsave(&sclp_lock, flags);
++	scbuf = (struct sclp_statechangebuf *) evbuf;
++
++	if (scbuf->validity_sclp_receive_mask) {
++		if (scbuf->mask_length != sizeof(sccb_mask_t))
++			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
++			       "state change event with mask length %i\n",
++			       scbuf->mask_length);
++		else
++			/* set new receive mask */
++			sclp_receive_mask = scbuf->sclp_receive_mask;
++	}
++
++	if (scbuf->validity_sclp_send_mask) {
++		if (scbuf->mask_length != sizeof(sccb_mask_t))
++			printk(KERN_WARNING SCLP_CORE_PRINT_HEADER
++			       "state change event with mask length %i\n",
++			       scbuf->mask_length);
++		else
++			/* set new send mask */
++			sclp_send_mask = scbuf->sclp_send_mask;
++	}
++
++	__sclp_notify_state_change();
++	spin_unlock_irqrestore(&sclp_lock, flags);
++}
++
++static struct sclp_register sclp_state_change_event = {
++	.receive_mask = EvTyp_StateChange_Mask,
++	.receiver_fn = sclp_state_change
++};
++
++
++/*
++ * SCLP quiesce event handler
++ */
++#ifdef CONFIG_SMP
++static void
++do_load_quiesce_psw(void * __unused)
++{
++	psw_t quiesce_psw;
++	unsigned long status;
++	int i;
++
++	if (smp_processor_id() != 0)
++		signal_processor(smp_processor_id(), sigp_stop);
++	/* Wait for all other cpus to enter stopped state */
++	i = 1;
++	while (i < smp_num_cpus) {
++		switch (signal_processor_ps(&status, 0, i, sigp_sense)) {
++		case sigp_order_code_accepted:
++		case sigp_status_stored:
++			/* Check for stopped and check stop state */
++			if (test_bit(6, &status) || test_bit(4, &status))
++				i++;
++			break;
++		case sigp_busy:
++			break;
++		case sigp_not_operational:
++			i++;
++			break;
++		}
++	}
++	/* Quiesce the last cpu with the special psw */
++	quiesce_psw.mask = _DW_PSW_MASK;
++	quiesce_psw.addr = 0xfff;
++	__load_psw(quiesce_psw);
++}
++
++static void
++do_machine_quiesce(void)
++{
++	smp_call_function(do_load_quiesce_psw, NULL, 0, 0);
++	do_load_quiesce_psw(NULL);
++}
++#else
++static void
++do_machine_quiesce(void)
++{
++	psw_t quiesce_psw;
++
++	quiesce_psw.mask = _DW_PSW_MASK;
++	quiesce_psw.addr = 0xfff;
++	__load_psw(quiesce_psw);
++}
++#endif
++
++extern void ctrl_alt_del(void);
++
++static void
++sclp_quiesce(struct evbuf_header *evbuf)
++{
++	/*
++	 * We got a "shutdown" request.
++	 * Add a call to an appropriate "shutdown" routine here. This
++	 * routine should set all PSWs to 'disabled-wait', 'stopped'
++	 * or 'check-stopped' - except 1 PSW which needs to carry a
++	 * special bit pattern called 'quiesce PSW'.
++	 */
++	_machine_restart = (void *) do_machine_quiesce;
++	_machine_halt = do_machine_quiesce;
++	_machine_power_off = do_machine_quiesce;
++	ctrl_alt_del();
++}
++
++static struct sclp_register sclp_quiesce_event = {
++	.receive_mask = EvTyp_SigQuiesce_Mask,
++	.receiver_fn = sclp_quiesce
++};
++
++/* initialisation of SCLP */
++struct init_sccb {
++	struct sccb_header header;
++	u16 _reserved;
++	u16 mask_length;
++	sccb_mask_t receive_mask;
++	sccb_mask_t send_mask;
++	sccb_mask_t sclp_send_mask;
++	sccb_mask_t sclp_receive_mask;
++} __attribute__((packed));
++
++static void sclp_init_mask_retry(unsigned long);
++
++static int
++sclp_init_mask(void)
++{
++	unsigned long flags;
++	struct init_sccb *sccb;
++	struct sclp_req *req;
++	struct list_head *l;
++	struct sclp_register *t;
++	int rc;
++
++	sccb = (struct init_sccb *) sclp_init_sccb;
++	/* stick the request structure to the end of the init sccb page */
++	req = (struct sclp_req *) ((addr_t) sccb + PAGE_SIZE) - 1;
++
++	/* SCLP setup concerning receiving and sending Event Buffers */
++	req->command = SCLP_CMDW_WRITEMASK;
++	req->status = SCLP_REQ_QUEUED;
++	req->callback = NULL;
++	req->sccb = sccb;
++	/* setup sccb for writemask command */
++	memset(sccb, 0, sizeof(struct init_sccb));
++	sccb->header.length = sizeof(struct init_sccb);
++	sccb->mask_length = sizeof(sccb_mask_t);
++	/* copy in the sccb mask of the registered event types */
++	spin_lock_irqsave(&sclp_lock, flags);
++	if (!test_bit(SCLP_SHUTDOWN, &sclp_status)) {
++		list_for_each(l, &sclp_reg_list) {
++			t = list_entry(l, struct sclp_register, list);
++			sccb->receive_mask |= t->receive_mask;
++			sccb->send_mask |= t->send_mask;
++		}
++	}
++	sccb->sclp_receive_mask = 0;
++	sccb->sclp_send_mask = 0;
++	if (test_bit(SCLP_INIT, &sclp_status)) {
++		/* add request to sclp queue */
++		list_add_tail(&req->list, &sclp_req_queue);
++		spin_unlock_irqrestore(&sclp_lock, flags);
++		/* and start if SCLP is idle */
++		sclp_start_request();
++		/* now wait for completion */
++		while (req->status != SCLP_REQ_DONE &&
++		       req->status != SCLP_REQ_FAILED)
++			sclp_sync_wait();
++		spin_lock_irqsave(&sclp_lock, flags);
++	} else {
++		/*
++		 * Special case for the very first write mask command.
++		 * The interrupt handler is not removing request from
++		 * the request queue and doesn't call callbacks yet
++		 * because there might be an pending old interrupt
++		 * after a Re-IPL. We have to receive and ignore it.
++		 */
++		do {
++			rc = __service_call(req->command, req->sccb);
++			if (rc == 0)
++				set_bit(SCLP_RUNNING, &sclp_status);
++			spin_unlock_irqrestore(&sclp_lock, flags);
++			if (rc == -EIO)
++				return -ENOSYS;
++			sclp_sync_wait();
++			spin_lock_irqsave(&sclp_lock, flags);
++		} while (rc == -EBUSY);
++	}
++	if (sccb->header.response_code != 0x0020) {
++		/* WRITEMASK failed - we cannot rely on receiving a state
++		   change event, so initially, polling is the only alternative
++		   for us to ever become operational. */
++		if (!test_bit(SCLP_SHUTDOWN, &sclp_status) &&
++		    (!timer_pending(&retry_timer) ||
++		     !mod_timer(&retry_timer,
++			       jiffies + SCLP_INIT_POLL_INTERVAL*HZ))) {
++			retry_timer.function = sclp_init_mask_retry;
++			retry_timer.data = 0;
++			retry_timer.expires = jiffies +
++				SCLP_INIT_POLL_INTERVAL*HZ;
++			add_timer(&retry_timer);
++		}
++	} else {
++		sclp_receive_mask = sccb->sclp_receive_mask;
++		sclp_send_mask = sccb->sclp_send_mask;
++		__sclp_notify_state_change();
++	}
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	return 0;
++}
++
++static void
++sclp_init_mask_retry(unsigned long data) 
++{
++	sclp_init_mask();
++}
++
++/* Reboot event handler - reset send and receive mask to prevent pending SCLP
++ * events from interfering with rebooted system. */
++static int
++sclp_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
++{
++	unsigned long flags;
++
++	/* Note: need spinlock to maintain atomicity when accessing global
++         * variables. */
++	spin_lock_irqsave(&sclp_lock, flags);
++	set_bit(SCLP_SHUTDOWN, &sclp_status);
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	sclp_init_mask();
++	return NOTIFY_DONE;
++}
++
++static struct notifier_block sclp_reboot_notifier = {
++	.notifier_call = sclp_reboot_event
++};
++
++/*
++ * sclp setup function. Called early (no kmalloc!) from sclp_console_init().
++ */
++static int
++sclp_init(void)
++{
++	int rc;
++
++	if (test_bit(SCLP_INIT, &sclp_status))
++		/* Already initialized. */
++		return 0;
++
++	spin_lock_init(&sclp_lock);
++	INIT_LIST_HEAD(&sclp_req_queue);
++
++	/* init event list */
++	INIT_LIST_HEAD(&sclp_reg_list);
++	list_add(&sclp_state_change_event.list, &sclp_reg_list);
++	list_add(&sclp_quiesce_event.list, &sclp_reg_list);
++
++	rc = register_reboot_notifier(&sclp_reboot_notifier);
++	if (rc)
++		return rc;
++
++	/*
++	 * request the 0x2401 external interrupt
++	 * The sclp driver is initialized early (before kmalloc works). We
++	 * need to use register_early_external_interrupt.
++	 */
++	if (register_early_external_interrupt(0x2401, sclp_interrupt_handler,
++					      &ext_int_info_hwc) != 0)
++		return -EBUSY;
++
++	/* enable service-signal external interruptions,
++	 * Control Register 0 bit 22 := 1
++	 * (besides PSW bit 7 must be set to 1 sometimes for external
++	 * interruptions)
++	 */
++	ctl_set_bit(0, 9);
++
++	init_timer(&retry_timer);
++	init_timer(&sclp_busy_timer);
++	/* do the initial write event mask */
++	rc = sclp_init_mask();
++	if (rc == 0) {
++		/* Ok, now everything is setup right. */
++		set_bit(SCLP_INIT, &sclp_status);
++		return 0;
++	}
++
++	/* The sclp_init_mask failed. SCLP is broken, unregister and exit. */
++	ctl_clear_bit(0,9);
++	unregister_early_external_interrupt(0x2401, sclp_interrupt_handler,
++					    &ext_int_info_hwc);
++
++	return rc;
++}
++
++/*
++ * Register the SCLP event listener identified by REG. Return 0 on success.
++ * Some error codes and their meaning:
++ *
++ *  -ENODEV = SCLP interface is not supported on this machine
++ *   -EBUSY = there is already a listener registered for the requested
++ *            event type
++ *     -EIO = SCLP interface is currently not operational
++ */
++int
++sclp_register(struct sclp_register *reg)
++{
++	unsigned long flags;
++	struct list_head *l;
++	struct sclp_register *t;
++
++	if (!MACHINE_HAS_SCLP)
++		return -ENODEV;
++
++	if (!test_bit(SCLP_INIT, &sclp_status))
++		sclp_init();
++	spin_lock_irqsave(&sclp_lock, flags);
++	/* check already registered event masks for collisions */
++	list_for_each(l, &sclp_reg_list) {
++		t = list_entry(l, struct sclp_register, list);
++		if (t->receive_mask & reg->receive_mask ||
++		    t->send_mask & reg->send_mask) {
++			spin_unlock_irqrestore(&sclp_lock, flags);
++			return -EBUSY;
++		}
++	}
++	/*
++	 * set present mask to 0 to trigger state change
++	 * callback in sclp_init_mask
++	 */
++	reg->sclp_receive_mask = 0;
++	reg->sclp_send_mask = 0;
++	list_add(&reg->list, &sclp_reg_list);
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	sclp_init_mask();
++	return 0;
++}
++
++/*
++ * Unregister the SCLP event listener identified by REG.
++ */
++void
++sclp_unregister(struct sclp_register *reg)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&sclp_lock, flags);
++	list_del(&reg->list);
++	spin_unlock_irqrestore(&sclp_lock, flags);
++	sclp_init_mask();
++}
++
++#define	SCLP_EVBUF_PROCESSED	0x80
++
++/*
++ * Traverse array of event buffers contained in SCCB and remove all buffers
++ * with a set "processed" flag. Return the number of unprocessed buffers.
++ */
++int
++sclp_remove_processed(struct sccb_header *sccb)
++{
++	struct evbuf_header *evbuf;
++	int unprocessed;
++	u16 remaining;
++
++	evbuf = (struct evbuf_header *) (sccb + 1);
++	unprocessed = 0;
++	remaining = sccb->length - sizeof(struct sccb_header);
++	while (remaining > 0) {
++		remaining -= evbuf->length;
++		if (evbuf->flags & SCLP_EVBUF_PROCESSED) {
++			sccb->length -= evbuf->length;
++			memcpy((void *) evbuf,
++			       (void *) ((addr_t) evbuf + evbuf->length),
++			       remaining);
++		} else {
++			unprocessed++;
++			evbuf = (struct evbuf_header *)
++					((addr_t) evbuf + evbuf->length);
++		}
++	}
++
++	return unprocessed;
++}
++
++module_init(sclp_init);
++
++EXPORT_SYMBOL(sclp_add_request);
++EXPORT_SYMBOL(sclp_sync_wait);
++EXPORT_SYMBOL(sclp_register);
++EXPORT_SYMBOL(sclp_unregister);
++EXPORT_SYMBOL(sclp_error_message);
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/sclp.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/sclp_vt220.c	2006-01-30 22:25:26.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>
++ */
++
++#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"
++
++#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;
++}
++
++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);
++}
++
++/*
++ * 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;
++
++	if (count > sclp_vt220_space_left(request))
++		count = sclp_vt220_space_left(request);
++	if (count <= 0)
++		return 0;
++
++	sccb = (struct sclp_vt220_sccb *) request->sclp_req.sccb;
++	buffer = (void *) ((addr_t) sccb + sccb->header.length);
++
++	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;
++
++			} 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
++sclp_vt220_timeout(unsigned long data)
++{
++	sclp_vt220_emit_current();
++}
++
++#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.
++ */
++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);
++		}
++		/* 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;
++}
++
++/*
++ * 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)
++{
++	return __sclp_vt220_write(from_user, buf, count, 1, 0);
++}
++
++#define SCLP_VT220_SESSION_ENDED	0x01
++#define	SCLP_VT220_SESSION_STARTED	0x80
++#define SCLP_VT220_SESSION_DATA		0x00
++
++/*
++ * Called by the SCLP to report incoming event buffers.
++ */
++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;
++
++	buffer = (char *) ((addr_t) evbuf + sizeof(struct evbuf_header));
++	count = evbuf->length - sizeof(struct evbuf_header);
++
++	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;
++	}
++}
++
++/*
++ * 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
++sclp_vt220_close(struct tty_struct * tty, struct file * filp)
++{
++	if (tty->count > 1)
++		return;
++	sclp_vt220_tty = NULL;
++}
++
++/*
++ * 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);
++}
++
++/*
++ * 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_vt220_flush_chars(struct tty_struct *tty)
++{
++	if (sclp_vt220_outqueue_count == 0)
++		sclp_vt220_emit_current();
++	else
++		sclp_vt220_flush_later = 1;
++}
++
++/*
++ * 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)
++{
++	unsigned long flags;
++	struct list_head *l;
++	int count;
++
++	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;
++}
++
++/*
++ * Return number of buffered chars.
++ */
++static int
++sclp_vt220_chars_in_buffer(struct tty_struct *tty)
++{
++	unsigned long flags;
++	struct list_head *l;
++	struct sclp_vt220_request *r;
++	int count;
++
++	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);
++	}
++	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++	return count;
++}
++
++static void
++__sclp_vt220_flush_buffer(void)
++{
++	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);
++	}
++	spin_unlock_irqrestore(&sclp_vt220_lock, flags);
++}
++
++/*
++ * 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)
++{
++	sclp_vt220_emit_current();
++}
++
++/*
++ * Initialize all relevant components and register driver with system.
++ */
++static void
++__sclp_vt220_init(int early)
++{
++	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);
++	}
++}
++
++/*
++ * Register driver with SCLP and Linux and initialize internal tty structures.
++ */
++void __init
++sclp_vt220_tty_init(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;
++	}
++
++	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;
++
++	/* 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;
++
++	/* 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;
++
++	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_SCLP_VT220_CONSOLE
++
++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);
++}
++
++static kdev_t
++sclp_vt220_con_device(struct console *c)
++{
++	return mk_kdev(SCLP_VT220_MAJOR, SCLP_VT220_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.
++ * Note that this function may be called from within an interrupt context.
++ */
++static void
++sclp_vt220_con_unblank(void)
++{
++	__sclp_vt220_flush_buffer();
++}
++
++/* 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)
++{
++        if (!CONSOLE_IS_SCLP)
++		return;
++	__sclp_vt220_init(1);
++	/* Attach linux console */
++	register_console(&sclp_vt220_console);
++}
++
++#endif /* CONFIG_SCLP_VT220_CONSOLE */
++
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tape.c	2006-01-30 22:25:26.000000000 -0700
+@@ -1,1120 +0,0 @@
 -
--/*
-- * 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.
+-/***********************************************************************
+- *  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>
+- *
+- ***********************************************************************
 - */
--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;
--}
+-#include "tapedefs.h"
 -
--/*
-- * 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;
--}
+-#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:"
 -
--/*
-- * 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;
--}
+-/* 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);
 -
--/*
-- * 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++;
+-/* 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;
 -
--	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;
--}
+-debug_info_t *tape_debug_area = NULL;
+-#endif
 -
--/*
-- * MTSETDRVBUFFER: Set the tape drive buffer code to number.
-- * Implement NOP.
-- */
--ccw_req_t *
--tape34xx_mtsetdrvbuffer (tape_info_t * ti, int count)
+-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) 
 -{
--	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;
+-    char devno [5];
+-    sprintf (devno,"%04x",ti->devinfo.devno);
+-    ti->devfs_dir=devfs_mk_dir (tape_devfs_root_entry, devno, ti);
 -}
 -
--/*
-- * MTLOCK: Locks the tape drive door.
-- * Implement NOP CCW command.
-- */
--ccw_req_t *
--tape34xx_mtlock (tape_info_t * ti, int count)
+-inline void
+-tape_rmdevfsroots (tape_info_t* ti)
 -{
--	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;
+-    devfs_unregister (ti->devfs_dir);
 -}
+-#endif
 -
--/*
-- * MTUNLOCK: Unlocks the tape drive door.
-- * Implement the NOP CCW command.
-- */
--ccw_req_t *
--tape34xx_mtunlock (tape_info_t * ti, int count)
+-#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)
 -{
--	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;
+-    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;
 -}
 -
--/*
-- * 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 ssize_t
+-tape_devices_read (struct file *file, char *user_buf, size_t user_len, loff_t * offset)
 -{
--         return NULL;
+-	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" */
+-	}
 -}
 -
--/*
-- * MTUNLOAD: Rewind the tape and unload it.
-- */
--ccw_req_t *
--tape34xx_mtunload (tape_info_t * ti, int count)
+-static int
+-tape_devices_release (struct inode *inode, struct file *file)
 -{
--	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 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);
 -	}
--	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;
+-#ifdef MODULE
+-        MOD_DEC_USE_COUNT;
+-#endif
+-	return rc;
 -}
 -
--/*
-- * MTCOMPRESSION: used to enable compression.
-- * Sets the IDRC on/off.
-- */
--ccw_req_t *
--tape34xx_mtcompression (tape_info_t * ti, int count)
+-static struct file_operations tape_devices_file_ops =
 -{
--	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
+-	read:tape_devices_read,	/* read */
+-	open:tape_devices_open,	/* open */
+-	release:tape_devices_release,	/* close */
+-};
 -
--	else
--		((tape34xx_disc_data_t *) ti->discdata)->modeset_byte = 0x08;		// IDRC on
+-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 */
 -
--	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;
--}
+-/* SECTION: Parameters for tape */
+-char *tape[256] = { NULL, };
 -
--/*
-- * 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)
+-#ifndef MODULE
+-static char tape_parm_string[1024] __initdata = { 0, };
+-static void
+-tape_split_parm_string (char *str)
 -{
--	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;
+-	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;
+-	};
 -}
 -
--/*
-- * MTMKPART: .... dummy .
-- * Implement the NOP CCW command.
-- */
--ccw_req_t *
--tape34xx_mtmkpart (tape_info_t * ti, int count)
+-void __init
+-tape_parm_setup (char *str, int *ints)
 -{
--	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;
+-	int len = strlen (tape_parm_string);
+-	if (len != 0) {
+-		strcat (tape_parm_string, ",");
 -	}
--	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;
+-	strcat (tape_parm_string, str);
 -}
 -
--/*
-- * MTIOCPOS: query the tape position.
-- */
--ccw_req_t *
--tape34xx_mtiocpos (tape_info_t * ti, int count)
+-int __init
+-tape_parm_call_setup (char *str)
 -{
--	return NULL;
+-	int dummy;
+-	tape_parm_setup (str, &dummy);
+-	return 1;
 -}
 -
--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
+-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,16))
+-__setup("tape=", tape_parm_call_setup);
+-#endif   /* kernel <2.2.19 */
+-#endif   /* not defined MODULE */
 -
--		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;
--			}
--		}
+-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 */
 -	}
--	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;
+-	val = simple_strtoul (temp, &temp, 16);	/* interpret anything as hex */
+-	*stra = temp;
+-	return val;
 -}
 -
--/* event handlers */
--void
--tape34xx_default_handler (tape_info_t * ti)
+-static inline devreg_t *
+-tape_create_devreg (int devno)
 -{
--#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);
--        }      
+-	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;
 -}
 -
--void
--tape34xx_unexpect_uchk_handler (tape_info_t * ti)
+-static inline void
+-tape_parm_parse (char **str)
 -{
--	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 {
+-	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,"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");
+-                        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 */
--		tape34xx_default_handler (ti);
--	}
--}
--
--void
--tape34xx_unused_done (tape_info_t * ti)
--{
--    if (ti->medium_is_unloaded) {
--	// A medium was inserted in the drive!
+-                        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_event (tape_debug_area,6,"xuui med");
+-                            debug_text_exception (tape_debug_area,3,"ti:no mem ");
 -#endif /* TAPE_DEBUG */
--	PRINT_WARN ("A medium was inserted into the tape.\n");
--	ti->medium_is_unloaded=0;
--    } else {
+-                            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,"unsol.irq!");
--        debug_text_event (tape_debug_area,3,"dev end");
--        debug_int_exception (tape_debug_area,3,ti->devinfo.irq);
+-                            debug_text_event (tape_debug_area,3,"tsetup err");
+-                            debug_int_exception (tape_debug_area,3,rc);
 -#endif /* TAPE_DEBUG */
--	PRINT_WARN ("Unsolicited IRQ (Device End) caught in unused state.\n");
--	tape_dump_sense (&ti->devstat);
--    }
+-                            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++;
+-        }
 -}
 -
 -
--void
--tape34xx_idle_done (tape_info_t * ti)
+-/* 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)
 -{
--    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);
--    }
+-	int i;
+-	for (i = 0; i < TAPE_EMERGENCY_REQUESTS; i++) {
+-		tape_emergency_req[i] = (ccw_req_t *) get_free_page (GFP_KERNEL);
+-	}
 -}
 -
--void
--tape34xx_block_done (tape_info_t * ti)
+-#ifdef MODULE // We only cleanup the emergency requests on module unload.
+-static void
+-tape_cleanup_emergency_req (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 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
 -
--void
--tape34xx_bsf_init_done (tape_info_t * ti)
+-ccw_req_t *
+-tape_alloc_request (char *magic, int cplength, int datasize)
 -{
--#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);
+-	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
--tape34xx_dse_init_done (tape_info_t * ti)
+-tape_free_request (ccw_req_t * request)
 -{
--#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);
+-	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);
+-	}
 -}
 -
--void
--tape34xx_fsf_init_done (tape_info_t * ti)
+-/*
+- * 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
--        debug_text_event (tape_debug_area,6,"fsf done");
+-		PRINT_WARN ("empty CQR generated\n");
 -#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
+-	}
+-	cqr->magic = TAPE_MAGIC;	/* sets an identifier for tape driver   */
+-	cqr->device = ti;	/* save pointer to tape info    */
+-	return cqr;
 -}
 -
--void
--tape34xx_fsb_init_done (tape_info_t * ti)
+-/*
+- * Find the tape_info_t structure associated with irq
+- */
+-static inline tape_info_t *
+-tapedev_find_info (int irq)
 -{
--#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);
+-	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
--tape34xx_bsb_init_done (tape_info_t * ti)
+-tape_irq (int irq, void *int_parm, struct pt_regs *regs)
 -{
--#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);
+-	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);
 -}
 -
--void
--tape34xx_lbl_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"lbl done");
+-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
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	//s390irq_spin_unlock(tape->devinfo.irq);
--	ti->wanna_wakeup=1;
--	wake_up (&ti->wq);
+-    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;
 -}
 -
--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");
+-
+-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
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	//s390irq_spin_unlock(tape->devinfo.irq);
+-    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;
--	wake_up (&ti->wq);
+-	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
--tape34xx_rfo_init_done (tape_info_t * ti)
+-tape_dump_sense (devstat_t * stat)
 -{
 -#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"rfo done");
+-        int sl;
 -#endif
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	ti->wanna_wakeup=1;
--	wake_up (&ti->wq);
--}
+-#if 0
 -
--void
--tape34xx_rbi_init_done (tape_info_t * ti)
--{
--	__u8 *data;
--#ifdef TAPE_DEBUG
--	int i;
+-	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
--	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]);
+-        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
--	ti->wanna_wakeup=1;
--	wake_up_interruptible (&ti->wq);
 -}
 -
--void
--tape34xx_rew_init_done (tape_info_t * ti)
+-/*
+- * Setup tape_info_t structure of a tape device
+- */
+-int
+-tape_setup (tape_info_t * ti, int irq, int minor)
 -{
--#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);
--}
+-	long lockflags;
+-	int rc = 0;
 -
--void
--tape34xx_rew_release_init_done (tape_info_t * ti)
--{
--#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,6,"rewR done");
+-        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
--	tapestate_set (ti, TS_DONE);
--	ti->rc = 0;
--	//s390irq_spin_unlock(tape->devinfo.irq);
--	ti->wanna_wakeup=1;
--	wake_up (&ti->wq);
+-	/* 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;
 -}
 -
--void
--tape34xx_run_init_done (tape_info_t * ti)
+-/*
+- *      tape_init will register the driver for each tape.
+- */
+-int
+-tape_init (void)
 -{
--#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);
--}
+-	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;
 -
--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);
--}
+-        if (initialized) // Only init the devices once
+-            return 0;
+-        initialized=1;
 -
--void
--tape34xx_wtm_init_done (tape_info_t * ti)
--{
 -#ifdef TAPE_DEBUG
--        debug_text_event (tape_debug_area,3,"wtm done");
+-        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
--	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;
+-#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);
 -	}
--    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;
+-#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);
 -	}
--	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;
+-#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);
 -	}
--    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;
+-        disc=first_discipline;
+-	while (disc != NULL) {
+-                if (*tape)
+-                    disc->shutdown(0);
+-                else
+-                    disc->shutdown(1);
+-		tempdi = disc;
+-		disc = disc->next;
+-		kfree (tempdi);
 -	}
--	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;
+-	/* 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;
 -    }
 -}
 -
--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"));
+-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-2.4.27.orig/drivers/s390/char/tape.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tape.h	2006-01-30 22:25:26.000000000 -0700
+@@ -1,203 +1,436 @@
+-/***************************************************************************
+- *
++/*
+  *  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>
+- *
+- ****************************************************************************
++ *		 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
+-
+ #define _TAPE_H
++
+ #include <linux/config.h>
+ #include <linux/blkdev.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);
+-
+-#ifdef CONFIG_S390_TAPE_DYNAMIC
+-/* functions for dyn. dev. attach/detach */
+-int tape_oper_handler ( int irq, struct _devreg *dreg);
 -#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 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];
 -
--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) {
+-/* 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
--    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"));
+-extern debug_info_t *tape_debug_area;
 -#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);
--}
-=== drivers/s390/char/tape34xx.h
-==================================================================
---- drivers/s390/char/tape34xx.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape34xx.h   (/trunk/2.4.27)   (revision 52)
-@@ -1,183 +0,0 @@
--
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tape3480.c	2006-01-30 22:25:26.000000000 -0700
+@@ -1,156 +0,0 @@
 -/***************************************************************************
 - *
-- *  drivers/s390/char/tape34xx.h
-- *    common tape device discipline for 34xx tapes.
+- *  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>
-- *
-- ****************************************************************************
-- */
--
--#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
-=== drivers/s390/char/hwc.h
-==================================================================
---- drivers/s390/char/hwc.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/hwc.h   (/trunk/2.4.27)   (revision 52)
-@@ -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;
+-#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"
 -
--static init_hwcb_t init_hwcb_template =
+-tape_event_handler_t tape3480_event_handler_table[TS_SIZE][TE_SIZE] =
 -{
--	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;
--
--typedef struct {
--	_GDS_VECTOR_HEADER
--} __attribute__ ((packed)) 
+-    /* {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 */
 -
--gds_vector_t;
+-devreg_t tape3480_devreg = {
+-    ci:
+-    {hc:
+-     {ctype:0x3480}},
+-    flag:DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS,
+-    oper_func:tape_oper_handler
+-};
 -
--typedef struct {
--	_GDS_SUBVECTOR_HEADER
--} __attribute__ ((packed)) 
 -
--gds_subvector_t;
+-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;
+-}
 -
--typedef struct {
--	_HWCB_HEADER
--} __attribute__ ((packed)) 
 -
--read_hwcb_t;
+-void
+-tape3480_shutdown (int autoprobe) {
+-    if (autoprobe)
+-	s390_device_unregister(&tape3480_devreg);
+-}
 -
--static read_hwcb_t read_hwcb_template =
+-tape_discipline_t *
+-tape3480_init (int autoprobe)
 -{
--	PAGE_SIZE,
--	0x00,
--	{
--		0x00,
--		0x00,
--		0x80
+-	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;
 -	}
--};
--
--#endif				/* __HWC_H__ */
-=== drivers/s390/char/hwc_rw.c
-==================================================================
---- drivers/s390/char/hwc_rw.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/hwc_rw.c   (/trunk/2.4.27)   (revision 52)
-@@ -1,2458 +0,0 @@
--/*
-- *  drivers/s390/char/hwc_rw.c
-- *     driver: reading from and writing to system console on S/390 via HWC
+-	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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tape3480.h	2006-01-30 22:25:26.000000000 -0700
+@@ -1,23 +0,0 @@
+-/***************************************************************************
 - *
-- *  S390 version
-- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
-- *    Author(s): Martin Peschke <mpeschke at de.ibm.com>
+- *  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>
 - *
-- * 
-- * 
-- * 
+- ****************************************************************************
 - */
 -
--#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__
+-#ifndef _TAPE3480_H
 -
--static unsigned char _obuf[MAX_HWCB_ROOM];
+-#define _TAPE3480_H
 -
--static unsigned char
-- _page[PAGE_SIZE] __attribute__ ((aligned (PAGE_SIZE)));
 -
--typedef unsigned long kmem_pages_t;
+-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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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>
+- *
+- ****************************************************************************
+- */
 -
--#define MAX_KMEM_PAGES (sizeof(kmem_pages_t) << 3)
+-#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"
 -
--#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
+-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 */
 -
--static struct {
+-devreg_t tape3490_devreg = {
+-    ci:
+-    {hc:
+-     {ctype:0x3490}},
+-    flag:DEVREG_MATCH_CU_TYPE | DEVREG_TYPE_DEVCHARS,
+-    oper_func:tape_oper_handler
+-};
 -
--	hwc_ioctls_t ioctls;
+-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;
+-}
 -
--	hwc_ioctls_t init_ioctls;
 -
--	unsigned char *hwcb_list_head;
+-void
+-tape3490_shutdown (int autoprobe) {
+-    if (autoprobe)
+-	s390_device_unregister(&tape3490_devreg);
+-}
 -
--	unsigned char *hwcb_list_tail;
 -
--	unsigned short int mto_number;
+-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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tape3490.h	2006-01-30 22:25:26.000000000 -0700
+@@ -1,24 +0,0 @@
 -
--	unsigned int mto_char_sum;
+-/***************************************************************************
+- *
+- *  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>
+- *
+- ****************************************************************************
+- */
 -
--	unsigned char hwcb_count;
+-#ifndef _TAPE3490_H
 -
--	unsigned long kmem_start;
+-#define _TAPE3490_H
 -
--	unsigned long kmem_end;
 -
--	kmem_pages_t kmem_pages;
+-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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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>
+- *
+- ****************************************************************************
+- */
 -
--	unsigned char *obuf;
+-#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"
 -
--	unsigned short int obuf_cursor;
+-#define PRINTK_HEADER "T34xx:"
 -
--	unsigned short int obuf_count;
+-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 */
 -
--	unsigned short int obuf_start;
 -
--	unsigned char *page;
+-int
+-tape34xx_ioctl_overload (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+-{
+-	return -EINVAL;		// no additional ioctls
 -
--	u32 current_servc;
+-}
 -
--	unsigned char *current_hwcb;
+-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++;
 -
--	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;
+-	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;
+-}
 -
--	unsigned char flags;
+-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 */
+-}
 -
--	hwc_high_level_calls_t *calls;
+-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++;
 -
--	hwc_request_t *request;
+-	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;
+-}
 -
--	spinlock_t lock;
+-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++;
 -
--	struct timer_list write_timer;
+-	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;
+-}
 -
--	struct timer_list poll_timer;
--} hwc_data =
+-void 
+-tape34xx_free_read_block (ccw_req_t * cqr, tape_info_t * ti)
 -{
--	{
--	},
--	{
--		8,
--		    0,
--		    80,
--		    1,
--		    MAX_KMEM_PAGES,
--		    MAX_KMEM_PAGES,
--
--		    0,
+-	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 */
+-}
 -
--		    0x6c
+-/*
+- * 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;
+-}
 -
--	},
--	    NULL,
--	    NULL,
--	    0,
--	    0,
--	    0,
--	    0,
--	    0,
--	    0,
--	    _obuf,
--	    0,
--	    0,
--	    0,
--	    _page,
--	    0,
--	    NULL,
--	    0,
--	    0,
--	    0,
--	    0,
--	    0,
--	    0,
--	    0,
--	    NULL,
--	    NULL
+-/*
+- * 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;
+-}
 -
--static unsigned long cr0 __attribute__ ((aligned (8)));
--static unsigned long cr0_save __attribute__ ((aligned (8)));
--static unsigned char psw_mask __attribute__ ((aligned (8)));
+-/*
+- * 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;
+-}
 -
--static ext_int_info_t ext_int_info_hwc;
+-/*
+- * 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;
+-}
 -
--#define DELAYED_WRITE 0
--#define IMMEDIATE_WRITE 1
+-/*
+- * 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;
+-}
 -
--static signed int do_hwc_write (int from_user, unsigned char *,
--				unsigned int,
--				unsigned char);
+-/*
+- * 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;
+-}
 -
--unsigned char hwc_ip_buf[512];
+-/*
+- * 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;
+-}
 -
--static asmlinkage int 
--internal_print (char write_time, char *fmt,...)
+-/*
+- * 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)
 -{
--	va_list args;
+-	long lockflags;
 -	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);
+-	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;
 -}
 -
--int 
--hwc_printk (const char *fmt,...)
+-/*
+- * 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)
 -{
--	va_list args;
+-	long lockflags;
 -	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;
+-	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;
 -}
 -
--#ifdef DUMP_HWCB_INPUT
--
--static void 
--dump_storage_area (unsigned char *area, unsigned short int count)
+-/*
+- * 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)
 -{
--	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, " ");
+-	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;
+-}
 -
--	internal_print (IMMEDIATE_WRITE, "\n");
+-/*
+- * 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;
+-}
 -
--	hwc_data.ioctls.final_nl = old_final_nl;
+-/*
+- * 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;
 -}
--#endif
 -
--static inline u32 
--service_call (
--		     u32 hwc_command_word,
--		     unsigned char hwcb[])
+-/*
+- * MTSEEK: seek to the specified block.
+- */
+-ccw_req_t *
+-tape34xx_mtseek (tape_info_t * ti, int count)
 -{
--	unsigned int condition_code = 1;
+-	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
 -
--	__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");
+-		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;
+-}
 -
--	__asm__ __volatile__ ("IPM %0 \n\t"
--			      "SRL %0, 28 \n\t"
--			      :"=r" (condition_code));
+-/*
+- * 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++;
 -
--	return condition_code;
+-	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;
 -}
 -
--static inline unsigned long 
--hwc_ext_int_param (void)
+-/*
+- * MTSETDRVBUFFER: Set the tape drive buffer code to number.
+- * Implement NOP.
+- */
+-ccw_req_t *
+-tape34xx_mtsetdrvbuffer (tape_info_t * ti, int count)
 -{
--	u32 param;
--
--	__asm__ __volatile__ ("L %0,128\n\t"
--			      :"=r" (param));
--
--	return (unsigned long) param;
+-	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;
 -}
 -
--static int 
--prepare_write_hwcb (void)
+-/*
+- * MTLOCK: Locks the tape drive door.
+- * Implement NOP CCW command.
+- */
+-ccw_req_t *
+-tape34xx_mtlock (tape_info_t * ti, int count)
 -{
--	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;
+-	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;
 -}
 -
--static int 
--sane_write_hwcb (void)
+-/*
+- * MTUNLOCK: Unlocks the tape drive door.
+- * Implement the NOP CCW command.
+- */
+-ccw_req_t *
+-tape34xx_mtunlock (tape_info_t * ti, int count)
 -{
--	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);
+-	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;
 -	}
--	return 0;
+-	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;
 -}
 -
--static int 
--reuse_write_hwcb (void)
+-/*
+- * 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)
 -{
--	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;
+-         return NULL;
+-}
 -
--		BUF_HWCB_NEXT = 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;
 -	}
--
--	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;
+-	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;
 -}
 -
--static int 
--allocate_write_hwcb (void)
+-/*
+- * MTCOMPRESSION: used to enable compression.
+- * Sets the IDRC on/off.
+- */
+-ccw_req_t *
+-tape34xx_mtcompression (tape_info_t * ti, int count)
 -{
--	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;
+-	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
 -
--	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
+-		((tape34xx_disc_data_t *) ti->discdata)->modeset_byte = 0x08;		// IDRC on
 -
--	return 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;
+-	}
+-	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;
 -}
 -
--static int 
--release_write_hwcb (void)
+-/*
+- * 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)
 -{
--	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);
+-	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;
+-}
 -
--#endif
+-/*
+- * 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;
 -	}
--	return 0;
+-	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;
 -}
 -
--static int 
--add_mto (
--		unsigned char *message,
--		unsigned short int count)
+-/*
+- * MTIOCGET: query the tape drive status.
+- */
+-ccw_req_t *
+-tape34xx_mtiocget (tape_info_t * ti, 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;
+-	return NULL;
 -}
 -
--static int write_event_data_1 (void);
--
--static void 
--do_poll_hwc (unsigned long data)
+-/*
+- * MTIOCPOS: query the tape position.
+- */
+-ccw_req_t *
+-tape34xx_mtiocpos (tape_info_t * ti, int count)
 -{
--	unsigned long flags;
--
--	spin_lock_irqsave (&hwc_data.lock, flags);
+-	return NULL;
+-}
 -
--	write_event_data_1 ();
+-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
 -
--	spin_unlock_irqrestore (&hwc_data.lock, flags);
+-		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;
 -}
 -
--void 
--start_poll_hwc (void)
+-/* event handlers */
+-void
+-tape34xx_default_handler (tape_info_t * ti)
 -{
--	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;
+-#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);
+-        }      
 -}
 -
--static int 
--write_event_data_1 (void)
+-void
+-tape34xx_unexpect_uchk_handler (tape_info_t * ti)
 -{
--	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");
+-	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
--
--	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;
+-		/* 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);
 -	}
--
--	return retval;
 -}
 -
--static void 
--flush_hwcbs (void)
+-void
+-tape34xx_unused_done (tape_info_t * ti)
 -{
--	while (hwc_data.hwcb_count > 1)
--		release_write_hwcb ();
+-    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);
+-    }
+-}
 -
--	release_write_hwcb ();
 -
--	hwc_data.flags &= ~HWC_FLUSH;
+-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);
+-    }
 -}
 -
--static int 
--write_event_data_2 (u32 ext_int_param)
+-void
+-tape34xx_block_done (tape_info_t * ti)
 -{
--	write_hwcb_t *hwcb;
--	int retval = 0;
+-#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);
+-}
 -
--#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;
--	}
+-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);
+-}
 -
--	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");
--	}
+-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);
+-}
 -
--#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");
--	}
+-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);
+-}
 -
--	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;
+-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);
 -}
 -
--static void 
--do_put_line (
--		    unsigned char *message,
--		    unsigned short count)
+-void
+-tape34xx_bsb_init_done (tape_info_t * ti)
 -{
--
--	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);
+-#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);
 -}
 -
--static void 
--put_line (
--		 unsigned char *message,
--		 unsigned short count)
+-void
+-tape34xx_lbl_init_done (tape_info_t * ti)
 -{
--
--	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;
+-#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);
 -}
 -
--static void 
--set_alarm (void)
+-void
+-tape34xx_nop_init_done (tape_info_t * ti)
 -{
--	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;
+-#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);
 -}
 -
--static void 
--hwc_write_timeout (unsigned long data)
+-void
+-tape34xx_rfo_init_done (tape_info_t * ti)
 -{
--	unsigned long flags;
+-#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);
+-}
 -
--	spin_lock_irqsave (&hwc_data.lock, flags);
+-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);
+-}
 -
--	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;
+-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);
+-}
 -
--	hwc_data.obuf_cursor = 0;
--	hwc_data.obuf_count = 0;
+-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);
+-}
 -
--	write_event_data_1 ();
+-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_unlock_irqrestore (&hwc_data.lock, flags);
+-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);
 -}
 -
--static int 
--do_hwc_write (
--		     int from_user,
--		     unsigned char *msg,
--		     unsigned int count,
--		     unsigned char write_time)
+-void
+-tape34xx_wtm_init_done (tape_info_t * ti)
 -{
--	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;
+-#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 (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;
+-/* 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;
 -	}
--
--	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);
+-    // 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 (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 (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;
 -	}
--	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;
+-    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 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)));
+-	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 (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;
+-	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;
 -	}
--	if (flag & IN_WRITE_BUF) {
--		hwc_data.obuf_cursor = 0;
--		hwc_data.obuf_count = 0;
+-    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;
 -	}
--	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++;
--		}
+-    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;
 -	}
--
--	return i_out;
+-	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;
+-    }
 -}
 -
--#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;
--
+-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:
--		name = "unknown GDS variable";
--		retval = -EINVAL;
+-	    tapestate_set(ti,TS_FAILED);
+-	    wake_up_interruptible (&ti->wq);
 -	}
+-    } else {
+-	PRINT_WARN("Recieved an unsolicited IRQ.\n");
+-	tape_dump_sense(&ti->devstat);
+-    }
+-}    
 -
--	return retval;
--}
+-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);
+-    }
+-}
 -
--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");
+-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"));
+-    }
+-}
 -
--			retval = vec;
--			break;
--		}
--		vec = (gds_vector_t *) (((unsigned long) vec) + vec->length);
--	}
--
--	return retval;
+-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-2.4.27.orig/drivers/s390/char/tape34xx.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tape34xx.h	2006-01-30 22:25:26.000000000 -0700
+@@ -1,183 +0,0 @@
 -
--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;
+-/***************************************************************************
+- *
+- *  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>
+- *
+- ****************************************************************************
+- */
 -
--	subvec = start;
+-#ifndef _TAPE34XX_H
 -
--	while (((void *) subvec) < end) {
--		if (subvec->key == key) {
--			retval = subvec;
--			break;
--		}
--		subvec = (gds_subvector_t *)
--		    (((unsigned long) subvec) + subvec->length);
--	}
+-#define _TAPE34XX_H
 -
--	return retval;
--}
+-/*
+- * The CCW commands for the Tape type of command.
+- */
 -
--inline static int 
--get_input (void *start, void *end)
--{
--	int count;
+-#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 */
 -
--	count = ((unsigned long) end) - ((unsigned long) start);
+-#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 */
 -
--	if (hwc_data.ioctls.tolower)
--		EBC_TOLOWER (start, count);
+-#ifndef MIN
+-#define MIN(a,b)                ( (a) < (b) ? (a) : (b) )
+-#endif
 -
--	if (hwc_data.ioctls.delim)
--		count = seperate_cases (start, count);
 -
--	HWC_EBCASC_STR (start, count);
+-#define BLOCKSIZE               4096            /* size of the tape rcds */
 -
--	if (hwc_data.ioctls.echo)
--		do_hwc_write (0, start, count, IMMEDIATE_WRITE);
+-#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 */
 -
--	if (hwc_data.calls != NULL)
--		if (hwc_data.calls->move_input != NULL)
--			(hwc_data.calls->move_input) (start, 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
 -
--	return count;
--}
+-#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
 -
--inline static int 
--eval_selfdeftextmsg (gds_subvector_t * start, void *end)
--{
--	gds_subvector_t *subvec;
--	void *subvec_data;
--	void *subvec_end;
--	int retval = 0;
+-#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
 -
--	subvec = start;
+-typedef struct _tape34xx_disc_data_t {
+-    __u8 modeset_byte;
+-} tape34xx_disc_data_t  __attribute__ ((packed, aligned(8)));
 -
--	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;
--	}
+-/* 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*);
 -
--	return retval;
--}
+-/* 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);
 -
--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;
+-extern void schedule_tapeblock_exec_IO (tape_info_t *ti);
 -
--	subvec = start;
+-// 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tape_34xx.c	2006-01-30 22:25:26.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>
++ */
++
++#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
++
++#include "tape.h"
++#include "tape_std.h"
++
++#define PRINTK_HEADER "T34xx:"
++
++/*
++ * Pointer to debug area.
++ */
++debug_info_t *TAPE_DBF_AREA = NULL;
++
++/*
++ * 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.
++ */
++#define TBI_FORMAT_3480			0x00
++#define TBI_FORMAT_3480_2_XF		0x01
++#define TBI_FORMAT_3480_XF		0x02
++#define TBI_FORMAT_RESERVED		0x03
++
++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));
++
++struct sbid_entry {
++	struct list_head		list;
++	struct tape_34xx_block_id	bid;
++};
++
++struct tape_34xx_discdata {
++	/* A list of block id's of the tape segments (for faster seek) */
++	struct list_head		sbid_list;
++};
++
++/* 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)
++{
++	unsigned char *sense = request->cpdata;
++
++	request->callback = NULL;
++
++	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));
++
++	if(sense[0] & SENSE_INTERVENTION_REQUIRED) {
++		tape_med_state_set(request->device, MS_UNLOADED);
++	} else {
++		tape_med_state_set(request->device, MS_LOADED);
++	}
++
++	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);
++	}
++
++	tape_put_request(request);
++}
++
++static int
++tape_34xx_medium_sense(struct tape_device *device)
++{
++	struct tape_request *	request;
++	int			rc;
++
++	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);
++	}
++
++	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;
++}
++
++static void
++tape_34xx_work_handler(void *data)
++{
++	struct {
++		struct tape_device	*device;
++		enum tape_op		 op;
++		struct tq_struct	 task;
++	} *p = data;
++
++	switch(p->op) {
++		case TO_MSEN:
++			tape_34xx_medium_sense(p->device);
++			break;
++		default:
++			DBF_EVENT(3, "T34XX: internal error: unknown work\n");
++	}
++
++	tape_put_device(p->device);
++	kfree(p);
++}
++
++/*
++ * 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.
++ */
++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;
++
++	p->device = tape_clone_device(device);
++	p->op     = op;
++
++	schedule_task(&p->task);
++
++	return 0;
++}
++
++/*
++ * 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);
++
++	return TAPE_IO_SUCCESS;
++}
++
++static inline int
++tape_34xx_erp_failed(struct tape_device *device,
++		     struct tape_request *request, int rc)
++{
++	DBF_EVENT(3, "Error recovery failed for %s\n",
++		  tape_op_verbose[request->op]);
++	return rc;
++}
++
++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);
++}
++
++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;
++}
++
++/*
++ * This function is called, when no request is outstanding and we get an
++ * interrupt
++ */
++static int
++tape_34xx_unsolicited_irq(struct tape_device *device)
++{
++	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;
++}
++
++/*
++ * Read Opposite Error Recovery Function:
++ * Used, when Read Forward does not work
++ */
++static int
++tape_34xx_erp_read_opposite(struct tape_device *device,
++			    struct tape_request *request)
++{
++	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);
++}
++
++static int
++tape_34xx_erp_bug(struct tape_device *device,
++		  struct tape_request *request, int no)
++{
++	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);
++}
++
++/*
++ * Handle data overrun between cu and drive. The channel speed might
++ * be too slow.
++ */
++static int
++tape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request)
++{
++	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);
++	}
++	return tape_34xx_erp_bug(device, request, -1);
++}
++	
++/*
++ * Handle record sequence error.
++ */
++static int
++tape_34xx_erp_sequence(struct tape_device *device,
++		       struct tape_request *request)
++{
++	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);
++}
++
++/*
++ * 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;
++
++	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
++
++	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);
++		}
++	}
++
++	/*
++	 * 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);
++		}
++	}
++
++	/* Sensing special bits */
++	if (sense[0] & SENSE_BUS_OUT_CHECK)
++		return tape_34xx_erp_retry(device, request);
++
++	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);
++
++			/* 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);
++
++			// 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);
++		}
++	}
++
++	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;
++			}
++		}
++		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);
++
++		/* 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]);
++	}
++}
++
++/*
++ * 3480/3490 interrupt handler
++ */
++static int
++tape_34xx_irq(struct tape_device *device, struct tape_request *request)
++{
++	if (request == NULL)
++		return tape_34xx_unsolicited_irq(device);
++
++	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);
++	}
++
++	if ((device->devstat.dstat & DEV_STAT_UNIT_EXCEP) &&
++	    (request->op == TO_BSB || request->op == TO_FSB))
++		DBF_EVENT(5, "Skipped over tapemark\n");
++
++	if (device->devstat.dstat & DEV_STAT_UNIT_CHECK)
++		return tape_34xx_unit_check(device, request);
++
++	if (device->devstat.dstat & DEV_STAT_DEV_END)
++		return tape_34xx_done(device, request);
++
++	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;
++}
++
++/*
++ * ioctl_overload
++ */
++static int
++tape_34xx_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)
++{
++        if (cmd == TAPE390_DISPLAY) {
++		struct display_struct disp;
++
++		if(copy_from_user(&disp, (char *) arg, sizeof(disp)) != 0)
++			return -EFAULT;
++
++	        return tape_std_display(device, &disp);
++	} else
++                return -EINVAL;
++}
++
++static int
++tape_34xx_setup_device(struct tape_device * device)
++{
++	struct tape_34xx_discdata *discdata;
++
++	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;
++	}
++
++	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...
++ */
++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;
++
++	if(discdata == NULL)
++		return;
++	if((new = kmalloc(sizeof(struct sbid_entry), GFP_ATOMIC)) == NULL)
++		return;
++
++	new->bid = bid;
++	new->bid.tbi_format = 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);
++
++		/*
++		 * 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;
++		}
++
++		/*
++		 * 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;
++		}
++	}
++
++	/*
++	 * 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);
++	}
++
++	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);
++	}
++
++	return;	
++}
++
++/*
++ * 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.
++ */
++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;
++
++	list_for_each(l, &discdata->sbid_list) {
++		cur = list_entry(l, struct sbid_entry, list);
++
++		if(cur->bid.tbi_block > bid->tbi_block)
++			break;
++	}
++
++	/* 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;
++}
++
++static void
++tape_34xx_clear_sbid_list(struct tape_device *device)
++{
++	struct list_head *		l;
++	struct list_head *		n;
++	struct tape_34xx_discdata *	discdata;
++
++	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));
++	}
++}
++
++/*
++ * MTTELL: Tell block. Return the number of block relative to current file.
++ */
++int
++tape_34xx_mttell(struct tape_device *device, int mt_count)
++{
++	struct tape_34xx_block_id	bid;
++	int				rc;
++
++	rc = tape_std_read_block_id(device, (unsigned int *) &bid);
++	if (rc)
++		return rc;
++
++	/*
++	 * Build up a lookup table. The format id is ingored.
++	 */
++	tape_34xx_add_sbid(device, bid);
++
++	return bid.tbi_block;
++}
++
++/*
++ * MTSEEK: seek to the specified block.
++ */
++int
++tape_34xx_mtseek(struct tape_device *device, int mt_count)
++{
++	struct tape_34xx_block_id	bid;
++
++	if (mt_count > 0x400000) {
++		DBF_EXCEPTION(6, "xsee parm\n");
++		return -EINVAL;
++	}
++
++	bid.tbi_block   = mt_count;
++
++	/*
++	 * Set hardware seek information in the block id.
++	 */
++	tape_34xx_merge_sbid(device, &bid);
++
++	return tape_std_seek_block_id(device, *((unsigned int *) &bid));
++}
++
++/*
++ * Tape block read for 34xx.
++ */
++#ifdef CONFIG_S390_TAPE_BLOCK
++struct tape_request *
++tape_34xx_bread(struct tape_device *device, struct request *req)
++{
++	struct tape_request  *request;
++	struct buffer_head   *bh;
++	ccw1_t               *ccw;
++	int                   count;
++	int                   size;
++
++	DBF_EVENT(6, "tape_34xx_bread(sector=%u,size=%u)\n",
++		req->sector, req->nr_sectors);
++
++	/* 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++;
++	}
++
++	/* 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++;
++		}
++	}
++
++	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
++
++	return request;
++}
++
++void
++tape_34xx_free_bread (struct tape_request *request)
++{
++	ccw1_t* ccw = request->cpaddr;
++
++	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");
++	}
++
++	/* 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);
++}
++
++/*
++ * 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.
++ */
++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;
++
++	/*
++	 * The tape is already at the correct position. No seek needed.
++	 */
++	if (id->tbi_block == device->blk_data.block_position)
++		return;
++
++	/*
++	 * 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;
++
++	/*
++	 * 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);
++}
++
++/*
++ * 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
++};
++
++/*
++ * 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
++};
++
++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
++};
++
++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");
++
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tape_block.c	2006-01-30 22:25:26.000000000 -0700
+@@ -0,0 +1,678 @@
++/*
++ *  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>
++ */
++
++#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>
++
++#include <asm/debug.h>
++#include <asm/irq.h>
++#include <asm/s390dyn.h>
++
++#define TAPE_DBF_AREA	tape_core_dbf
++
++#include "tape.h"
++#include "tape_std.h"
++
++#define PRINTK_HEADER "TBLOCK:"
++
++#define TAPEBLOCK_DEVFSMODE	0060644	/* brwxrw-rw- */
++#define TAPEBLOCK_MAX_SEC	100
++#define TAPEBLOCK_MIN_REQUEUE   3
++
++/*
++ * file operation structure for tape block frontend
++ */
++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,
++};
++
++int tapeblock_major = 0;
++
++/*
++ * Some helper inlines
++ */
++static inline int tapeblock_size(int minor) {
++	return blk_size[tapeblock_major][minor];
++}
++static inline int tapeblock_ssize(int minor) {
++	return blksize_size[tapeblock_major][minor];
++}
++static inline int tapeblock_hw_ssize(int minor) {
++	return hardsect_size[tapeblock_major][minor];
++}
++
++/*
++ * Post finished request.
++ */
++static inline void
++tapeblock_end_request(struct request *req, int uptodate)
++{
++	if (end_that_request_first(req, uptodate, "tBLK"))
++		BUG();
++	end_that_request_last(req);
++}
++
++static void
++__tapeblock_end_request(struct tape_request *ccw_req, void *data)
++{
++	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);
++
++	if (!list_empty(&device->req_queue) ||
++	    !list_empty(&device->blk_data.request_queue.queue_head))
++		tasklet_schedule(&device->blk_data.tasklet);
++}
++
++/*
++ * Fetch requests from block device queue.
++ */
++static inline void
++__tape_process_blk_queue(struct tape_device *device, struct list_head *new_req)
++{
++	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);
++
++		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;
++
++		list_add_tail(&ccw_req->list, new_req);
++		nr_queued++;
++	}
++}
++
++/*
++ * Feed requests to the tape device.
++ */
++static inline int
++tape_queue_requests(struct tape_device *device, struct list_head *new_req)
++{
++	struct list_head *l, *n;
++	struct tape_request *ccw_req;
++	struct request *req;
++	int rc, fail;
++
++	fail = 0;
++	list_for_each_safe(l, n, new_req) {
++		ccw_req = list_entry(l, struct tape_request, list);
++		list_del(&ccw_req->list);
++
++		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;
++}
++
++/*
++ * Tape request queue function. Called from ll_rw_blk.c
++ */
++static void
++tapeblock_request_fn(request_queue_t *queue)
++{
++	struct list_head    new_req;
++	struct tape_device *device;
++
++	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;
++	}
++}
++
++/*
++ * 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.
++ */
++static void
++tapeblock_tasklet(unsigned long data)
++{
++	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;
++	}
++}
++
++/*
++ * 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;
++}
++
++/*
++ * 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
++}
++
++/*
++ * This function is called for every new tapedevice
++ */
++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)
++{
++	/* 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);
++
++	tape_hotplug_event(device, tapeblock_major, TAPE_HOTPLUG_BLOCK_REMOVE);
++}
++
++/*
++ * 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)
++{
++	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);
++	}
++	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.
++ */
++void
++tapeblock_medium_change(struct tape_device *device) {
++	device->blk_data.start_block_id                = 0;
++	blk_size[tapeblock_major][device->first_minor] = 0;
++}
++
++/*
++ * 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);
++	}
++
++	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;
++}
++
++/*
++ * Block frontend tape device release function.
++ */
++int
++tapeblock_release(struct inode *inode, struct file *filp) {
++	struct tape_device *device;
++
++	device = tape_get_device(minor(inode->i_rdev) >> 1);
++
++	DBF_EVENT(4, "TBLOCK: release %i\n", device->first_minor);
++
++	/* Remove all buffers at device close. */
++	/* FIXME: can we do that a tape unload ? */
++	invalidate_buffers(inode->i_rdev);
++
++	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 0;
++}
++
++int
++tapeblock_ioctl(
++	struct inode	*inode,
++	struct file	*file,
++	unsigned int	 command,
++	unsigned long	 arg
++) {
++	int	 rc     = 0;
++	int	 minor  = minor(inode->i_rdev);
++
++	DBF_EVENT(6, "tapeblock_ioctl(%x)\n", command);
++
++	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;
++			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 rc;
++}
++
++/*
++ * Initialize block device frontend.
++ */
++int
++tapeblock_init(void) 
++{
++	int rc;
++
++	/* 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;
++	}
++	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;
++
++	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");
++
++	return 0;
++
++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;
++	}
++
++	DBF_EVENT(3, "TBLOCK: init failed(%d)\n", rc);
++	return rc;
++}
++
++/*
++ * Deregister major for block device frontend
++ */
++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_char.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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"
++
++#define PRINTK_HEADER "TCHAR:"
++
++#define TAPECHAR_DEVFSMODE	0020644	/* crwxrw-rw- */
++#define TAPECHAR_MAJOR		0	/* get dynamic major */
++
++int tapechar_major = TAPECHAR_MAJOR;
++
++/*
++ * Prototypes for file operation functions
++ */
++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,
++};
++
++#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;
++}
++
++/*
++ * 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 function is called for every new tapedevice
++ */
++int
++tapechar_setup_device(struct tape_device * device)
++{
++#ifdef CONFIG_DEVFS_FS
++	int rc;
++
++	rc = tapechar_mkdevfstree(device);
++	if (rc)
++		return rc;
++#endif
++
++	tape_hotplug_event(device, tapechar_major, TAPE_HOTPLUG_CHAR_ADD);
++	return 0;
++	
++}
++
++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);
++}
++
++static inline int
++tapechar_check_idalbuffer(struct tape_device *device, size_t block_size)
++{
++	struct idal_buffer *new;
++
++	/* 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;
++
++	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;
++	}
++
++	/* 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;
++}
++
++/*
++ * 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;
++
++        DBF_EVENT(6, "TCHAR:read\n");
++	device = (struct tape_device *) filp->private_data;
++
++	/* 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;
++	}
++
++	/*
++	 * 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);
++	}
++
++	/* 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;
++	}
++
++	/*
++	 * 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);
++	/* 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;
++}
++
++/*
++ * 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;
++
++	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;
++	}
++
++	/* 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);
++	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);
++
++	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;
++	}
++
++	/*
++	 * 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;
++
++	return rc ? rc : written;
++}
++
++/*
++ * Character frontend tape device open function.
++ */
++int
++tapechar_open (struct inode *inode, struct file *filp)
++{
++	struct tape_device *device;
++	int minor, rc;
++
++	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.
++ */
++
++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));
++			
++	/*
++	 * 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--;
++		}
++	}
++
++	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;
++}
++
++/*
++ * 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;
++
++	DBF_EVENT(6, "TCHAR:ioct(%x)\n", no);
++
++	device = (struct tape_device *) filp->private_data;
++
++	if (no == MTIOCTOP) {
++		struct mtop op;
++
++		if (copy_from_user(&op, (char *) data, sizeof(op)) != 0)
++			return -EFAULT;
++		if (op.mt_count < 0)
++			return -EINVAL;
++
++		/*
++		 * 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);
++
++		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;
++
++		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;
++
++		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;
++
++		if(device->medium_state == MS_LOADED) {
++			rc = tape_mtop(device, MTTELL, 1);
++
++			if(rc < 0)
++				return rc;
++
++			if(rc == 0)
++				get.mt_gstat |= GMT_BOT(~0);
++
++			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);
++}
++
++/*
++ * Initialize character device frontend.
++ */
++int
++tapechar_init (void)
++{
++	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 (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;
++}
++
++/*
++ * cleanup
++ */
++void
++tapechar_exit(void)
++{
++	unregister_chrdev (tapechar_major, "tape");
++}
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tape_core.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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>
++ */
++
++#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-2.4.27.orig/drivers/s390/char/tape_devmap.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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>
++ *
++ * 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-2.4.27.orig/drivers/s390/char/tape_proc.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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
++ */
++
++#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-2.4.27.orig/drivers/s390/char/tape_std.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tape_std.c	2006-01-30 22:25:26.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-2.4.27.orig/drivers/s390/char/tape_std.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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>
++ */
++
++#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-2.4.27.orig/drivers/s390/char/tapeblock.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tapeblock.c	2006-01-30 22:25:26.000000000 -0700
+@@ -1,593 +0,0 @@
 -
--	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;
--	}
+-/***************************************************************************
+- *
+- *  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>
+- *
+- *
+- ****************************************************************************
+- */
 -
--	return retval;
--}
+-#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"
 -
--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;
+-#define PRINTK_HEADER "TBLOCK:"
 -
--	vec = start;
+-/*
+- * 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 */
+-        };
 -
--	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;
--	}
+-int    tapeblock_major = 0;
 -
--	return retval;
--}
+-#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
 -
--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;
+-static request_queue_t* tapeblock_getqueue (kdev_t kdev);
 -
--	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;
+-#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);
 -}
 -
--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;
+-void
+-tapeblock_rmdevfstree (tape_info_t* ti) {
+-    devfs_unregister(ti->devfs_disc);
+-    devfs_unregister(ti->devfs_block_dir);
 -}
+-#endif
 -
--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;
--	}
+-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
 -}
 -
--static inline int 
--eval_hwc_send_mask (_hwcb_mask_t mask)
--{
+-int
+-tapeblock_init(void) {
+-    int result;
+-    tape_frontend_t* blkfront,*temp;
+-    tape_info_t* ti;
 -
--	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");
+-    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;
+-}
 -
--	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");
+-void 
+-tapeblock_uninit(void) {
+-    unregister_blkdev(tapeblock_major, "tBLK");
+-}
 -
--	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");
+-int
+-tapeblock_open(struct inode *inode, struct file *filp) {
+-    tape_info_t *ti;
+-    kdev_t dev;
+-    int rc;
+-    long lockflags;
 -
--	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;
+-    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;
 -}
 -
--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) {
--
+-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 (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");
+-	if ((ti == NULL) || (tapestate_get (ti) != TS_IDLE)) {
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,3,"b:notidle!");
 -#endif
--		} else {
--
--			retval += eval_hwc_receive_mask
--			    (scbuf->hwc_receive_mask);
--		}
+-		return -ENXIO;	/* error in tape_release */
 -	}
--	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");
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"b:release:");
+-	debug_int_event (tape_debug_area,6,ti->blk_minor);
 -#endif
--		} else {
--
--			retval += eval_hwc_send_mask
--			    (scbuf->hwc_send_mask);
--		}
--	}
--	if (scbuf->validity_read_data_function_mask) {
--
--	}
--	return retval;
+-	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;
 -}
 -
--#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) ;
+-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;
+-}
 -
--		quiesce_psw.mask = _DW_PSW_MASK;
--		quiesce_psw.addr = 0xfff;
--		__load_psw (quiesce_psw);
+-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;
 -	}
--	signal_processor (smp_processor_id (), sigp_stop);
+-	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_machine_quiesce (void)
--{
--	cpu_quiesce_map = cpu_online_map;
--	smp_call_function (do_load_quiesce_psw, NULL, 0, 0);
--	do_load_quiesce_psw ();
+-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);
 -}
 -
--#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);
+-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);
 -}
 -
--#endif
--
--static int 
--process_evbufs (void *start, void *end)
+-void
+-schedule_tapeblock_exec_IO (tape_info_t *ti)
 -{
--	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);
+-	/* 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
--			retval += eval_evbuf (evbuf_data, evbuf_end);
--			break;
--		case ET_StateChange:
--			retval += eval_statechangebuf
--			    ((statechangebuf_t *) evbuf);
--			break;
--		case ET_SigQuiesce:
+-	ti->bh_tq.sync = 0;
+-	ti->bh_tq.routine = (void *) (void *) run_tapeblock_exec_IO;
+-	ti->bh_tq.data = ti;
 -
--			_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;
+-	queue_task (&ti->bh_tq, &tq_immediate);
+-	mark_bh (IMMEDIATE_BH);
+-	return;
 -}
 -
--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;
+-/* 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
 -
--	memset (hwcb, 0x00, PAGE_SIZE);
--	memcpy (hwcb, &read_hwcb_template, sizeof (read_hwcb_t));
--
--	condition_code = service_call (HWC_CMDW_READDATA, hwc_data.page);
+-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;
+-}
 -
--#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");
+-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
--
--	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;
+-    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;
--	default:
--		retval = -EIO;
 -	}
--
--	return retval;
+-	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-2.4.27.orig/drivers/s390/char/tapeblock.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tapeblock.h	2006-01-30 22:25:26.000000000 -0700
+@@ -1,36 +0,0 @@
 -
--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
+-/***************************************************************************
+- *
+- *  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>
+- *
+- *
+- ****************************************************************************
+- */
 -
--	hwc_data.current_servc = 0;
--	hwc_data.current_hwcb = NULL;
+-#ifndef TAPEBLOCK_H
+-#define TAPEBLOCK_H
+-#include <linux/config.h>
+-#define PARTN_BITS 0
 -
--	switch (hwcb->response_code) {
+-#define TAPEBLOCK_READAHEAD 30
+-#define TAPEBLOCK_MAJOR 0
 -
--	case 0x0020:
--	case 0x0220:
--		return process_evbufs (
--		     (void *) (((unsigned long) hwcb) + sizeof (read_hwcb_t)),
--			    (void *) (((unsigned long) hwcb) + hwcb->length));
+-#define TAPEBLOCK_DEFAULTMODE 0060644
 -
--	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;
+-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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tapechar.c	2006-01-30 22:25:26.000000000 -0700
+@@ -1,764 +0,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;
+-/***************************************************************************
+- *
+- *  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>
+- *
+- *
+- ****************************************************************************
+- */
 -
--	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;
+-#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"
 -
--	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;
+-#define PRINTK_HEADER "TCHAR:"
 -
--	case 0x40F0:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--			       "unconditional read: invalid function code\n");
--		return -EIO;
+-/*
+- * file operation structure for tape devices
+- */
+-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,
+-};
 -
--	case 0x70F0:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--			      "unconditional read: invalid selection mask\n");
--		return -EIO;
+-int tape_major = TAPE_MAJOR;
 -
--	case 0x0040:
--		internal_print (
--				       IMMEDIATE_WRITE,
--				       HWC_RW_PRINT_HEADER
--				 "unconditional read: HWC equipment check\n");
--		return -EIO;
+-#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);
+-}
 -
--	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;
--	}
+-void
+-tapechar_rmdevfstree (tape_info_t* ti) {
+-    devfs_unregister(ti->devfs_nonrewinding);
+-    devfs_unregister(ti->devfs_rewinding);
+-    devfs_unregister(ti->devfs_char_dir);
 -}
+-#endif
 -
--static int 
--write_event_mask_1 (void)
+-void
+-tapechar_setup (tape_info_t * ti)
 -{
--	unsigned int condition_code;
--	int retval;
+-#ifdef CONFIG_DEVFS_FS
+-    tapechar_mkdevfstree(ti);
+-#endif
+-}
 -
--	condition_code = service_call (HWC_CMDW_WRITEMASK, hwc_data.page);
+-void
+-tapechar_init (void)
+-{
+-	int result;
+-	tape_frontend_t *charfront,*temp;
+-	tape_info_t* ti;
 -
--#ifdef DUMP_HWC_INIT_ERROR
+-	tape_init();
 -
--	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");
+-	/* 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
 -
--	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;
+-	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) {
+-#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
+-#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;
 -	}
+-}
 -
--	return retval;
+-void
+-tapechar_uninit (void)
+-{
+-	unregister_chrdev (tape_major, "tape");
 -}
 -
--static int 
--write_event_mask_2 (u32 ext_int_param)
+-/*
+- * Tape device read function
+- */
+-ssize_t
+-tape_read (struct file *filp, char *data, size_t count, loff_t * ppos)
 -{
--	init_hwcb_t *hwcb = (init_hwcb_t *) hwc_data.page;
--	int retval = 0;
+-	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 */
+-	ti = first_tape_info;
+-	while ((ti != NULL) && (ti->rew_filp != filp) && (ti->nor_filp != filp))
+-		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 (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 /* 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,"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);
+-	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);
+-#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;
+-}
 -
--	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;
+-/*
+- * 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,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 {
--		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");
+-		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
--		} else {
--			retval += eval_hwc_receive_mask
--			    (hwcb->hwc_receive_mask);
--			retval += eval_hwc_send_mask (hwcb->hwc_send_mask);
+-	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;
 -		}
 -	}
--
--	hwc_data.current_servc = 0;
--	hwc_data.current_hwcb = NULL;
--
--	return retval;
+-#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 
--set_hwc_ioctls (hwc_ioctls_t * ioctls, char correct)
+-static int
+-tape_mtioctop (struct file *filp, short mt_op, int mt_count)
 -{
--	int retval = 0;
--	hwc_ioctls_t tmp;
+-	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
 -
--	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;
+-		cqr = ti->discipline->mtrew (ti, mt_count);
+-		break;
+-	case MTOFFL:		// put drive offline
 -
--	tmp.echo = ioctls->echo;
+-		cqr = ti->discipline->mtoffl (ti, mt_count);
+-		break;
+-	case MTUNLOAD:		// unload the tape
 -
--	if (ioctls->columns > MAX_MESSAGE_SIZE) {
--		if (correct)
--			tmp.columns = MAX_MESSAGE_SIZE;
--		else
--			retval = -EINVAL;
--	} else
--		tmp.columns = ioctls->columns;
+-		cqr = ti->discipline->mtunload (ti, mt_count);
+-		break;
+-	case MTWEOF:		// write tapemark
 -
--	tmp.final_nl = ioctls->final_nl;
+-		cqr = ti->discipline->mtweof (ti, mt_count);
+-		break;
+-	case MTFSF:		// forward space file
 -
--	if (ioctls->max_hwcb < 2) {
--		if (correct)
--			tmp.max_hwcb = 2;
--		else
--			retval = -EINVAL;
--	} else
--		tmp.max_hwcb = ioctls->max_hwcb;
+-		cqr = ti->discipline->mtfsf (ti, mt_count);
+-		break;
+-	case MTBSF:		// backward space file
 -
--	tmp.tolower = ioctls->tolower;
+-		cqr = ti->discipline->mtbsf (ti, mt_count);
+-		break;
+-	case MTFSFM:		// forward space file, stop at BOT side
 -
--	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;
+-		cqr = ti->discipline->mtfsfm (ti, mt_count);
+-		break;
+-	case MTBSFM:		// backward space file, stop at BOT side
 -
--	if (ioctls->kmem_hwcb > MAX_KMEM_PAGES) {
--		if (correct)
--			ioctls->kmem_hwcb = MAX_KMEM_PAGES;
--		else
--			retval = -EINVAL;
+-		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;
 -	}
--	if (ioctls->kmem_hwcb < 2) {
--		if (correct)
--			ioctls->kmem_hwcb = 2;
--		else
--			retval = -EINVAL;
+-	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;
 -	}
--	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");
+-	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
 -
--			__ctl_load (cr0_save, 0, 0);
+-		return tape_mtioctop (filp, MTREW, 1);
+-	case MTFSFM:		//need to skip back over the filemark
 -
--			hwc_data.flags &= ~HWC_INIT;
--		}
--	} while (retval == -EBUSY);
+-		return tape_mtioctop (filp, MTBSFM, 1);
+-	case MTBSF:		//need to skip forward over the filemark
 -
--	if (retval == -EIO) {
--		hwc_data.flags |= HWC_BROKEN;
--		printk (HWC_RW_PRINT_HEADER "HWC not operational\n");
+-		return tape_mtioctop (filp, MTFSF, 1);
 -	}
--	return retval;
+-#ifdef TAPE_DEBUG
+-	debug_text_event (tape_debug_area,6,"c:mtio done");
+-#endif
+-	return 0;
 -}
 -
--void hwc_interrupt_handler (struct pt_regs *regs, __u16 code);
--
--int 
--hwc_init (void)
+-/*
+- * Tape device io controls.
+- */
+-int
+-tape_ioctl (struct inode *inode, struct file *filp,
+-	    unsigned int cmd, unsigned long arg)
 -{
--	int retval;
--
--#ifdef BUFFER_STRESS_TEST
--
--	init_hwcb_t *hwcb;
--	int i;
+-	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
--
--	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;
+-	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
--	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;
+-	    return rc;
+-	}
 -
--	retval = do_hwc_init ();
+-	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
 -
--	ctl_set_bit (0, 9);
+-		if (ti->devstat.ii.sense.data[1] & 0x02)
+-			get.mt_gstat &= GMT_WR_PROT (1);	// write protected
 -
--#ifdef BUFFER_STRESS_TEST
+-		if (ti->devstat.ii.sense.data[1] & 0x40)
+-			get.mt_gstat &= GMT_ONLINE (1);		//drive online
 -
--	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)));
+-		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;
 -	}
--
--#endif
--
--	return /*retval */ 0;
 -}
 -
--signed int 
--hwc_register_calls (hwc_high_level_calls_t * calls)
+-/*
+- * Tape device open function.
+- */
+-int
+-tape_open (struct inode *inode, struct file *filp)
 -{
--	if (calls == NULL)
--		return -EINVAL;
+-	tape_info_t *ti;
+-	kdev_t dev;
+-	long lockflags;
 -
--	if (hwc_data.calls != 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,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);
 -
--	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;
+-	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);
 -
--	hwc_data.calls = NULL;
+-#ifdef MODULE
+-	MOD_INC_USE_COUNT;
+-#endif				/* MODULE */
 -	return 0;
 -}
 -
--int 
--hwc_send (hwc_request_t * req)
+-/*
+- * Tape device release function.
+- */
+-int
+-tape_release (struct inode *inode, struct file *filp)
 -{
--	unsigned long flags;
--	int retval;
--	int cc;
+-	long lockflags;
+-	tape_info_t *ti,*lastti;
+-	ccw_req_t *cqr = NULL;
+-	int rc = 0;
 -
--	spin_lock_irqsave (&hwc_data.lock, flags);
--	if (!req || !req->callback || !req->block) {
--		retval = -EINVAL;
--		goto unlock;
+-	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 (hwc_data.request) {
--		retval = -ENOTSUPP;
--		goto unlock;
+-	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;
 -	}
--	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;
--
+-#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");
+-#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);
+-		}
 -	}
--      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;
+-	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;
 -}
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/tapechar.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tapechar.h	2006-01-30 22:25:26.000000000 -0700
+@@ -1,34 +0,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:
+-/***************************************************************************
+- *
+- *  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>
+- *
+- *
+- ****************************************************************************
+- */
 -
--					unconditional_read_2 (ext_int_param);
--					break;
--				default:
--				}
--			}
--		}
--	} else {
+-#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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>
+- *
+- *
+- ***********************************************************************
+- */
 -
--		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);
--		}
--	}
+-/* Kernel Version Compatibility section */
+-#include <linux/version.h>
+-#include <linux/blkdev.h>
+-#include <linux/blk.h>
+-#include <asm/irq.h>
 -
--	if (evbuf_pending) {
+-#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")
+-#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.
 -
--		unconditional_read_1 ();
--	} else {
 -
--		write_event_data_1 ();
+-#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)
+-#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/tuball.c	2006-01-30 22:25:26.000000000 -0700
+@@ -29,9 +29,7 @@
+ MODULE_PARM(tubdebug, "i");
+ MODULE_PARM(tubscrolltime, "i");
+ MODULE_PARM(tubxcorrect, "i");
+-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
+-MODULE_LICENSE ("GPL");
+-#endif
++MODULE_LICENSE("GPL");
+ #endif
+ /*
+  * Values for tubdebug and their effects:
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/char/vmlogrdr.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/char/vmlogrdr.c	2006-01-30 22:25:26.000000000 -0700
+@@ -0,0 +1,741 @@
++/*
++ * drivers/s390/char/vmlogrdr.c
++ *	character device driver for reading z/VM system service records
++ *
++ *
++ *	Copyright (C) 2004 IBM Corporation
++ *	character device driver for reading z/VM system service records,
++ *	Version 1.0
++ *	Author(s): Xenia Tkatschow <xenia at us.ibm.com>
++ *		   Stefan Weinhuber <wein at de.ibm.com>
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/spinlock.h>
++#include <asm/atomic.h>
++#include <asm/uaccess.h>
++#include <asm/cpcmd.h>
++#include <asm/debug.h>
++#include <asm/ebcdic.h>
++#include "../net/iucv.h"
++#include <linux/kmod.h>
++
++
++
++#define MAXSERVICES 1
++
++enum vmlogrdr_hotplug_action { 
++	VMLOGRDR_HOTPLUG_ADD=0,
++	VMLOGRDR_HOTPLUG_REMOVE=1
++};
++
++MODULE_AUTHOR
++	("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia at us.ibm.com)\n"
++	 "                            Stefan Weinhuber (wein at de.ibm.com)");
++MODULE_DESCRIPTION ("Character device driver for reading z/VM "
++		    "system service records.");
++MODULE_PARM (services, "1-"__MODULE_STRING(MAXSERVICES)"s");
++MODULE_PARM_DESC (services,
++		"Specify the system services\n"
++		"services=system_service0,system_service1,..,"
++		"system_serviceN\n");
++MODULE_LICENSE("GPL");
++
++static char *services[MAXSERVICES];
++
++char userid[9];
++
++static char FENCE[] = {"EOR"};
++static int logreader_major = 0;
++
++static int logreader_open(struct inode *, struct file *);
++static int logreader_release(struct inode *, struct file *);
++static ssize_t logreader_read (struct file *filp, char *data, size_t count,
++			       loff_t * ppos);
++
++
++
++/*
++ * File operation structures for logreader devices
++ */
++static struct file_operations logreader_fops = {
++	.owner=THIS_MODULE,
++	.open =	logreader_open,
++	.release = logreader_release,
++	.read =	logreader_read,
++};
++
++
++static __u8 iucvMagic[16] = {
++	0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
++	0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
++};
++
++static __u8 mask[] = {
++	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
++	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
++	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
++	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
++};
++
++static __u8 iucv_host[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
++
++
++static void
++logreader_iucv_ConnectionComplete(iucv_ConnectionComplete *eib,
++			void *pgm_data);
++
++static void
++logreader_iucv_ConnectionSevered(iucv_ConnectionSevered *eib,
++			void *pgm_data);
++
++static void
++logreader_iucv_MessagePending(iucv_MessagePending *eib,
++			void *pgm_data);
++
++/*
++ * Name: iucv_interrupt_ops_t
++ * Descriptor: Defines the iucv interrupt routines that the IUCV driver
++ *	will call when there is an interrupt for the logreader
++ *	character device driver
++ * Members:
++ *	ConnectionSevered: This routine will be called by the IUCV driver
++ *		if the system service severes the iucv connection.
++ *	MessagePending: This routine will be called be the IUCV driver
++ *		when it receives a message pending interrupt.
++ * Note: The remaining routines are not defined as we do not expect to
++ *	handle these situations.
++ */
++static iucv_interrupt_ops_t logreader_iucvops = {
++	.ConnectionComplete=	logreader_iucv_ConnectionComplete,
++	.ConnectionSevered=	logreader_iucv_ConnectionSevered,
++	.MessagePending=	logreader_iucv_MessagePending,
++};
++
++
++
++#define buflen	4088
++
++struct logreader_priv_t {
++	char system_service[8];
++	u16 pathid;
++	int connection_established;
++	int iucv_path_severed;
++	iucv_MessagePending local_interrupt_buffer;
++	atomic_t receive_ready;
++	iucv_handle_t iucv_handle;
++	int minor_num;
++	char * buffer;
++	char * current_position;
++	int remaining;
++	ulong residual_length;
++	int buffer_free;
++	int dev_in_use; /* 1: already opened, 0: not opened*/
++	spinlock_t priv_lock;
++};
++
++DECLARE_WAIT_QUEUE_HEAD(conn_wait_queue);
++DECLARE_WAIT_QUEUE_HEAD(read_wait_queue);
++
++/*
++ * pointer to system service private structure
++ * minor number 0 --> logrec
++ * minor number 1 --> account	 -> not yet implemented
++ * minor number 2 --> symptom    -> not yet implemented
++ */
++#define MAXMINOR	1
++static struct logreader_priv_t * sys_ser[MAXMINOR];
++
++
++static void
++logreader_iucv_ConnectionComplete (iucv_ConnectionComplete * eib,
++				   void * pgm_data)
++{
++	struct logreader_priv_t * logptr =
++		(struct logreader_priv_t *) pgm_data;
++	spin_lock(&logptr->priv_lock);
++	logptr->connection_established = 1;
++	spin_unlock(&logptr->priv_lock);
++	wake_up(&conn_wait_queue);
++	return;
++}
++
++
++static void
++logreader_iucv_ConnectionSevered (iucv_ConnectionSevered * eib, void * pgm_data)
++{
++	
++	u8 reason = (u8) eib->ipuser[8];
++	struct logreader_priv_t * logptr = (struct logreader_priv_t *) pgm_data;
++
++	printk (KERN_ERR "vmlogrdr: connection severed with"
++		" reason %i/n", reason);
++
++	spin_lock(&logptr->priv_lock);	
++	logptr->connection_established = 0;
++	logptr->iucv_path_severed = 1;
++	spin_unlock(&logptr->priv_lock);
++
++	wake_up(&conn_wait_queue);
++	/* just in case we're sleeping waiting for a record */
++	wake_up_interruptible(&read_wait_queue);
++	
++	return;
++}
++
++
++static void
++logreader_iucv_MessagePending (iucv_MessagePending * eib, void * pgm_data)
++{
++	struct logreader_priv_t * logptr = (struct logreader_priv_t *) pgm_data;
++			
++	/*
++	 * This function is the bottom half so it should be quick.
++	 * Copy the external interrupt data into our local eib and increment
++	 * the usage count
++	 */
++	spin_lock(&logptr->priv_lock);
++	memcpy(&(logptr->local_interrupt_buffer), eib,
++	       sizeof(iucv_MessagePending));	
++	atomic_inc(&logptr->receive_ready);
++	spin_unlock(&logptr->priv_lock);
++	
++	wake_up_interruptible(&read_wait_queue);
++
++	return;
++}
++
++static int
++logreader_open (struct inode *inode, struct file *filp)
++{
++	int dev_num = 0;
++	struct logreader_priv_t * logptr = NULL;
++	int connect_rc = 0;
++
++	char cp_command[80];
++	char cp_response[80];
++	unsigned long flags;
++
++	/* extract the minor number */
++	dev_num = MINOR(inode->i_rdev);
++
++
++	/* located the private structure of dev_num */
++	if (dev_num > MAXMINOR)
++		return -ENODEV;
++
++	logptr = sys_ser[dev_num];
++	if (logptr == NULL)
++		return -ENODEV;
++
++	/*
++	 * only allow for blocking reads to be open
++	 */
++	if (filp->f_flags & O_NONBLOCK)
++		return -ENOSYS;
++
++	/* Besure this device hasn't already been opened */
++	spin_lock_irqsave(&logptr->priv_lock, flags);
++	if (logptr->dev_in_use)	{
++		spin_unlock_irqrestore(&logptr->priv_lock, flags);
++		return -EBUSY;
++	} else {
++		logptr->dev_in_use = 1;
++		spin_unlock_irqrestore(&logptr->priv_lock, flags);
++	}
++
++	/* set the file options */
++	filp->private_data = logptr;
++	filp->f_op = &logreader_fops;
++
++
++	/*
++	 * Purge "old" CP records that have been collected but not retrieved and
++	 * request CP to begin collecting records on behalf of this virtual
++	 * machine.
++	 * "RECORDING EREP ON QID userid PURGE"
++	 */
++	memset(cp_command, 0x00, sizeof(cp_command));
++	memset(cp_response, 0x00, sizeof(cp_response));
++
++	/* Set up CP command depending on the system service */
++	switch(dev_num) {
++		case 0:
++			sprintf(cp_command, "RECORDING EREP ON QID %s PURGE",
++				userid);
++			break;
++		default:
++			return -ENODEV;
++	}
++	
++	cpcmd(cp_command, cp_response, 80);
++	
++	/* Register with iucv driver */
++	logptr->iucv_handle = iucv_register_program(iucvMagic,
++			logptr->system_service, mask, &logreader_iucvops,
++			logptr);
++
++	if (logptr->iucv_handle == NULL) {
++		printk (KERN_ERR "vmlogrdr: failed to register with"
++			"iucv driver\n");
++		
++		/* Set up CP command depending on the system service */
++		memset(cp_command, 0x00, sizeof(cp_command));
++		memset(cp_response, 0x00, sizeof(cp_response));
++		switch(dev_num) {
++			case 0:
++				sprintf(cp_command,"RECORDING EREP OFF QID %s ",
++					userid);
++				break;
++			default:
++				return -ENODEV;
++		}
++		cpcmd(cp_command, cp_response, 80);
++		logptr->dev_in_use = 0;
++		return -EIO;
++	}
++
++	/* create connection to the system service */
++	spin_lock_irqsave(&logptr->priv_lock, flags);
++	logptr->connection_established = 0;
++	logptr->iucv_path_severed = 0;
++	spin_unlock_irqrestore(&logptr->priv_lock, flags);
++	
++	connect_rc = iucv_connect (&(logptr->pathid), 10, iucvMagic,
++					logptr->system_service, iucv_host, 0,
++					NULL, NULL,
++					logptr->iucv_handle, NULL);
++	if (connect_rc) {
++		printk (KERN_ERR "vmlogrdr: iucv connection to %s "
++			"failed with rc %i \n", logptr->system_service,
++			connect_rc);
++
++		/* Set up CP command depending on the system service */
++		memset(cp_command, 0x00, sizeof(cp_command));
++		memset(cp_response, 0x00, sizeof(cp_response));
++		switch(dev_num) {
++			case 0:
++				sprintf(cp_command, "RECORDING EREP OFF QID %s ",
++					userid);
++				break;
++			default:
++				return -ENODEV;
++		}
++		cpcmd(cp_command, cp_response, 80);
++
++		iucv_unregister_program(logptr->iucv_handle);
++		logptr->iucv_handle = NULL;
++		logptr->dev_in_use = 0;
++		return -EIO;
++	}
++
++	/* We've issued the connect and now we must wait for a
++	 * ConnectionComplete or ConnectinSevered Interrupt
++	 * before we can continue to process.
++	 * note: When the condition in the second parameter is true,
++	 *       we'll wake up
++	 */
++	wait_event(conn_wait_queue, (logptr->connection_established)
++		|| (logptr->iucv_path_severed));
++
++	
++	if (logptr->iucv_path_severed) {
++		iucv_unregister_program(logptr->iucv_handle);
++		logptr->iucv_handle = NULL;
++
++		memset(cp_command, 0x00, sizeof(cp_command));
++		memset(cp_response, 0x00, sizeof(cp_response));
++
++		switch(dev_num) {
++			case 0:
++				sprintf(cp_command, "RECORDING EREP OFF QID %s ",
++					userid);
++				break;
++			default:
++				return -ENODEV;
++		}
++
++		cpcmd(cp_command, cp_response, 80);
++
++		logptr->dev_in_use = 0;
++		return -EIO;
++	}
++
++	 	
++	return 0;
++}
++
++static int
++logreader_release (struct inode *inode, struct file *filp)
++{
++	int dev_num = 0;
++	struct logreader_priv_t * logptr =
++		(struct logreader_priv_t *) filp->private_data;
++
++	char cp_command[80];
++	char cp_response[80];
++
++	if (logptr == NULL )
++		return -ENODEV;
++	
++	if (logptr->iucv_handle == NULL)
++		return 0;
++
++
++	 /* extract the minor number */
++	dev_num = MINOR(inode->i_rdev);
++
++	iucv_unregister_program(logptr->iucv_handle);
++	
++	/* Set data structures back to original state
++	 * 1. private struct
++	 * 2. buffer pointer structure (binary, hex buffers)
++	 * 3. buffer
++	 */
++	logptr->iucv_handle = NULL;
++	
++	memset(&(logptr->local_interrupt_buffer), 0x00,
++		sizeof(iucv_MessagePending));
++	atomic_set(&logptr->receive_ready, 0);
++	logptr->current_position = logptr->buffer;
++	logptr->remaining = 0;
++	logptr->residual_length = 0;
++	logptr->buffer_free = 1;	
++
++
++	/* Purge any records remaining in CP storage and turn recording off
++	 * "RECORDING record_type OFF QID userid PURGE"
++	 */
++	memset(cp_command, 0x00, sizeof(cp_command));
++	memset(cp_response, 0x00, sizeof(cp_response));
++
++	/* Set up the CP command according to system service */
++	switch(dev_num) {
++		case 0:
++			sprintf(cp_command, "RECORDING EREP OFF QID %s PURGE",
++				userid);
++			break;
++		default:
++			return -ENODEV;
++	}
++	cpcmd(cp_command, cp_response, 80);
++
++	logptr->dev_in_use = 0;
++
++	return 0;
++}
++
++
++static ssize_t
++logreader_read (struct file *filp, char *data, size_t count, loff_t * ppos)
++{
++	int rc = 0;
++	struct logreader_priv_t * logptr =
++		(struct logreader_priv_t *) filp->private_data;
++	int new_record = 0;
++	int total_record_length = 0;
++	unsigned long	flags;
++	while (logptr->buffer_free) {
++
++		/* assume we're not going to receive a a record by
++		 * setting rc != 0
++		 */
++		rc = 1;
++		spin_lock_irqsave(&logptr->priv_lock, flags);
++		if (atomic_read(&logptr->receive_ready)) {
++			/*
++		 	 * first check whether we need to receive the second
++			 * half of a record (residual_length != 0).
++		 	 */
++		
++			if (logptr->residual_length){
++				/* collecting part of the record */
++				logptr->remaining = logptr->residual_length;
++				logptr->current_position = logptr->buffer;
++			} else {
++				/* beginning a new record */
++				logptr->remaining = (int)
++					logptr->local_interrupt_buffer.ln1msg2.ipbfln1f;
++				logptr->current_position = logptr->buffer + 4;
++				total_record_length = (int)
++					logptr->local_interrupt_buffer.ln1msg2.ipbfln1f;
++				new_record = 1;
++			}
++			if (logptr->remaining > buflen)
++				logptr->remaining = buflen;
++
++			
++			rc = iucv_receive(logptr->pathid,
++					  logptr->local_interrupt_buffer.ipmsgid,
++					  logptr->local_interrupt_buffer.iptrgcls,
++					  logptr->current_position,
++					  logptr->remaining,
++					  NULL,
++					  NULL,
++					  &logptr->residual_length);
++
++			if (!logptr->residual_length)
++				atomic_dec(&logptr->receive_ready);
++		}
++		spin_unlock_irqrestore(&logptr->priv_lock, flags);
++
++		if (rc) {
++			wait_event_interruptible(read_wait_queue,
++					 atomic_read(&logptr->receive_ready));
++			if (atomic_read(&logptr->receive_ready) == 0)
++				return -ERESTARTSYS; /* woken up by signal */
++
++		} else {
++			/*
++			 * just received some data so we must mark the
++			 * buffer busy
++			 */
++			logptr->buffer_free = 0;
++			if (new_record) {
++				/*
++				 * if we just received a new record then we
++				 * need to add the header length.
++				 * To the first 4 bytes of the buffer we add
++				 * the length field, which is record + fence
++				 */
++				int * total_record_length_ptr =
++					(int *)logptr->buffer;
++	
++				*total_record_length_ptr =
++					total_record_length + 4;
++	
++				logptr->remaining += 4;
++				logptr->current_position = logptr->buffer;
++				
++				new_record = 0;
++			}
++
++			if (logptr->residual_length == 0){
++				/* the whole record has been captured,
++				 * now add the fence */
++				char * temp_position = logptr->current_position
++						       + logptr->remaining;
++				memcpy(temp_position, FENCE, sizeof(FENCE));
++				logptr->remaining += 4;
++			}
++		}				
++
++	}
++	/* copy only up to end of record */
++	if (count > logptr->remaining)
++		count = logptr->remaining;
++	
++	if (copy_to_user(data, logptr->current_position, count))
++		return -EFAULT;
++
++	*ppos += count;
++	logptr->current_position += count;
++	logptr->remaining -= count;
++
++	/* if all data has been transferred, set buffer free */
++	if (logptr->remaining == 0)
++		logptr->buffer_free = 1;
++
++	return count;
++
++
++}
++
++
++static void
++get_vm_usrid (char * userid)
++{
++	char cp_response[81];
++	cpcmd("Q USERID", cp_response, 80);
++	cp_response[80]=0;
++	memcpy(userid, cp_response, 8);
++	printk (KERN_DEBUG "vmlogrdr: VM guest id: %s\n", userid);
++	return;
++}
++
++
++static void
++logreader_hotplug_event(int devmaj, struct logreader_priv_t *logptr, 
++			enum vmlogrdr_hotplug_action action) {
++#ifdef CONFIG_HOTPLUG
++	char *argv[3];
++	char *envp[8];
++	char  major[20];
++	char  minor[20];
++	char  service[20];
++	char *firstblank;	
++				 
++	sprintf(major, "MAJOR=%d",   devmaj);
++	sprintf(minor, "MINOR=%d",   logptr->minor_num);
++	// remember: system_service starts with an * and padded with blanks
++	sprintf(service, "SERVICE=%.7s", logptr->system_service+1);
++	firstblank = strchr(service, ' ');
++	if (firstblank != NULL) {
++		*firstblank=0;
++	}
++
++	argv[0] = hotplug_path;
++	argv[1] = "vmlogrdr";
++	argv[2] = NULL;
++
++	envp[0] = "HOME=/";
++	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++
++	switch(action) {
++	case VMLOGRDR_HOTPLUG_ADD: 
++		envp[2] = "ACTION=add";
++		break;
++	case VMLOGRDR_HOTPLUG_REMOVE: 
++		envp[2] = "ACTION=remove";
++		break;
++	default:
++		BUG();
++	}
++	envp[3] = major;
++	envp[4] = minor;
++	envp[5] = service;
++	envp[6] = NULL;
++
++	call_usermodehelper(argv[0], argv, envp);
++#endif
++}
++
++
++
++static int
++logreader_init(void)
++{
++	char temp_system[9];
++	struct logreader_priv_t * logptr = NULL;
++	int rc=0;
++	int i;
++
++	if (! MACHINE_IS_VM) {
++		printk (KERN_ERR "vmlogrdr: not running under VM, "
++				"driver not loaded.\n");
++		return -ENODEV;
++	}
++	
++	get_vm_usrid(userid);
++	userid[8] = '\0';
++
++	logreader_major = register_chrdev(logreader_major,"vmlogrdr",
++					  &logreader_fops);
++	if (logreader_major < 0 ) {
++		printk (KERN_ERR "vmlogrdr: can't get major %d\n",
++		logreader_major);	
++		return -EIO;			
++	}
++
++	int parm_no;
++	int minor;
++	for (parm_no=0; parm_no < MAXSERVICES; ++parm_no ) {
++		printk (KERN_DEBUG "vmlogrdr: services[%d] == %s \n",parm_no,
++			services[parm_no]);
++		// compare to the know system services (excluding the leading *)
++		// important: the system name needs to be 8 characters,
++		//            padded with blanks and not 0.
++		if (memcmp (services[parm_no], "LOGREC", 7) == 0) {
++			memcpy(temp_system, "*LOGREC ", 8);
++			minor = 0;
++		} else if (services[parm_no] == 0) {
++			break;
++		} else {
++			printk (KERN_ERR "vmlogrdr: unknown service: %s \n",
++				services[parm_no] );
++			rc=-EINVAL;
++			goto free_sys_ser;
++		}
++	
++		if (sys_ser[minor] == NULL) {	
++			
++			/* Allocate memory for:
++			 * 1 logreader_priv_t
++			 * 1 buffer (4096 bytes)
++			 */
++			logptr = kmalloc (sizeof(struct logreader_priv_t),
++					  GFP_KERNEL);
++			if (!logptr) {
++				rc=-ENOMEM;
++				goto free_sys_ser;
++			}
++			memset(logptr, 0x00, sizeof(struct logreader_priv_t));
++			sys_ser[minor] = logptr;
++
++			/* set all structures */
++			memcpy(logptr->system_service, temp_system, 8);	
++			logptr->minor_num = minor;
++			logptr->priv_lock = SPIN_LOCK_UNLOCKED;
++			logptr->buffer_free = 1;
++			logptr->buffer = kmalloc (4096, GFP_KERNEL);
++			if (!logptr->buffer) {
++				kfree(logptr);
++				sys_ser[minor]=0;
++				rc=-ENOMEM;
++				goto free_sys_ser;
++			}
++			memset(logptr->buffer, 0x00, buflen);
++			logptr->current_position = logptr->buffer;
++			
++			logreader_hotplug_event(logreader_major, logptr, 
++						VMLOGRDR_HOTPLUG_ADD);
++		
++		} else {
++			printk (KERN_WARNING "vmlogrdr: Service %s defined more"
++				" then once -> ignore \n", services[parm_no]);
++		}
++	}
++	printk (KERN_INFO "vmlogrdr: driver loaded\n");
++
++	return 0;
++free_sys_ser:
++	for (i=0; i < MAXMINOR; ++i ) {
++		if ( sys_ser[i] != 0 ) {
++			logreader_hotplug_event(logreader_major, sys_ser[i], 
++						VMLOGRDR_HOTPLUG_REMOVE);			
++			kfree (sys_ser[i]->buffer);
++			kfree (sys_ser[i]);
++		}
++	}
++	
++	unregister_chrdev(logreader_major, "vmlogrdr");
++	printk (KERN_ERR "vmlogrdr: driver not loaded.\n");
++
++	return rc;	
++}
++
++
++static void
++logreader_exit(void)
++{
++	/* return all storage and unregister driver */
++	int index = 0;
++	struct logreader_priv_t * logptr = NULL;
++
++	for (index = 0; index < MAXMINOR; index++) {
++		logptr = sys_ser[index];
++		if (logptr) {
++			kfree(logptr->buffer);
++			kfree(logptr);
++			sys_ser[index] = NULL;
++			logreader_hotplug_event(logreader_major, logptr,
++						VMLOGRDR_HOTPLUG_REMOVE);
++		}
++	}
++	unregister_chrdev(logreader_major, "vmlogrdr");
++	printk (KERN_INFO "vmlogrdr: driver unloaded\n");
++	return;
++}
++
++module_init(logreader_init);
++module_exit(logreader_exit);
++
++
++
++
++
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/cmf.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/cmf.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,701 @@
++/*
++ * linux/drivers/s390/cmf.c ($Revision: 1.5.6.2 $)
++ *
++ * Linux on zSeries Channel Measurement Facility support
++ *
++ * Copyright 2000,2003 IBM Corporation
++ *
++ * Author: Arnd Bergmann <arndb at de.ibm.com>
++ *
++ * original idea from Natarajan Krishnaswami <nkrishna at us.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/init.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++
++#include <asm/cmb.h>
++#include <asm/div64.h>
++#include <asm/irq.h>
++
++/**
++ * enum cmb_format - types of supported measurement block formats
++ *
++ * @CMF_BASIC:      traditional channel measurement blocks supported
++ * 		    by all machines that we run on
++ * @CMF_EXTENDED:   improved format that was introduced with the z990
++ * 		    machine
++ * @CMF_AUTODETECT: default: use extended format when running on a z990
++ *                  or later machine, otherwise fall back to basic format
++ **/
++enum cmb_format {
++	CMF_BASIC,
++	CMF_EXTENDED,
++	CMF_AUTODETECT = -1,
++};
++/**
++ * format - actual format for all measurement blocks
++ * 
++ * The format module parameter can be set to a value of 0 (zero)
++ * or 1, indicating basic or extended format as described for
++ * enum cmb_format.
++ */
++static int format = CMF_AUTODETECT;
++MODULE_PARM(format, "i");
++
++/**
++ * struct cmb_operations - functions to use depending on cmb_format
++ *
++ * all these functions operate on a struct cmf_device. There is only
++ * one instance of struct cmb_operations because all cmf_device 
++ * objects are guaranteed to be of the same type.
++ *
++ * @alloc:	allocate memory for a channel measurement block,
++ *		either with the help of a special pool or with kmalloc
++ * @free:	free memory allocated with @alloc
++ * @set:	enable or disable measurement
++ * @readall:	read a measurement block in a common format
++ * @reset:	clear the data in the associated measurement block and
++ *		reset its time stamp
++ */
++struct cmb_operations {
++	int (*alloc)  (struct cmf_device*);
++	void(*free)   (struct cmf_device*);
++	int (*set)    (struct cmf_device*, u32);
++	int (*readall)(struct cmf_device*, struct cmbdata *);
++	void (*reset) (struct cmf_device*);
++};
++static struct cmb_operations *cmbops;
++
++/* our user interface is designed in terms of nanoseconds,
++ * while the hardware measures total times in its own
++ * unit.*/
++static inline u64 time_to_nsec(u32 value)
++{
++	return ((u64)value) * 128000ull;
++}
++
++/*
++ * Users are usually interested in average times,
++ * not accumulated time.
++ * This also helps us with atomicity problems
++ * when reading sinlge values.
++ */
++static inline u64 time_to_avg_nsec(u32 value, u32 count)
++{
++	u64 ret;
++
++	/* no samples yet, avoid division by 0 */
++	if (count == 0)
++		return 0;
++
++	/* value comes in units of 128 µsec */
++	ret = time_to_nsec(value);
++	do_div(ret, count);
++
++	return ret;
++}
++
++/* activate or deactivate the channel monitor. When area is NULL,
++ * the monitor is deactivated. The channel monitor needs to
++ * be active in order to measure subchannels, which also need
++ * to be enabled. */
++static inline void
++cmf_activate(void *area, unsigned int onoff)
++{
++	register void * __gpr2 asm("2");
++	register long __gpr1 asm("1");
++
++	__gpr2 = area;
++	__gpr1 = onoff ? 2 : 0;
++	/* activate channel measurement */
++	asm("schm" : : "d" (__gpr2), "d" (__gpr1) );
++}
++
++static int
++set_schib(int sch, u32 mme, int mbfc, unsigned long address)
++{
++	int ret;
++	int retry;
++	schib_t *schib;
++	unsigned long mba, mbi;
++
++	/* address can be either a block address or a block index */
++	mba = mbi = 0;
++	(mbfc ? mba : mbi) = address;
++
++	/* msch can silently fail, so do it again if necessary */
++	for (retry = 0; retry < 3; retry++) {
++		/* prepare schib */
++		schib = &ioinfo[sch]->schib;
++		stsch(sch, schib);
++		schib->pmcw.mme  = mme;
++		schib->pmcw.mbfc = mbfc;
++		schib->pmcw.mbi  = mbi;
++		if (mbfc)
++			schib->mba	 = mba;
++
++		/* try to submit it */
++		switch(ret = msch_err(sch, schib)) {
++			case 0:
++				break;
++			case 1:
++			case 2: /* in I/O or status pending */
++				ret = -EBUSY;
++				break;
++			case 3: /* subchannel is no longer valid */
++				ret = -ENODEV;
++				break;
++			default: /* msch caught an exception */
++				ret = -EINVAL;
++				break;
++		}
++		stsch(sch, schib); /* restore the schib */
++		
++		if (ret)
++			break;
++		
++		/* check if it worked */
++		if (schib->pmcw.mme  == mme &&
++		    schib->pmcw.mbfc == mbfc &&
++		    schib->pmcw.mbi == mbi &&
++		    (!mbfc || schib->mba == mba)) 
++			return 0;
++
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++/**
++ * struct cmb_area - container for global cmb data
++ *
++ * @mem:	pointer to CMBs (only in basic measurement mode)
++ * @list:	contains a linked list of all subchannels
++ * @lock:	protect concurrent access to @mem and @list
++ */
++struct cmb_area {
++	struct cmb *mem;
++	struct list_head list;
++	spinlock_t lock;
++};
++static struct cmb_area cmb_area = {
++	.lock = SPIN_LOCK_UNLOCKED,
++	.list = LIST_HEAD_INIT(cmb_area.list),
++};
++
++
++/* ****** old style CMB handling ********/
++
++/** int maxchannels
++ *
++ * Basic channel measurement blocks are allocated in one contiguous
++ * block of memory, which can not be moved as long as any channel
++ * is active. Therefore, a maximum number of subchannels needs to
++ * be defined somewhere. This is a module parameter, defaulting to
++ * a resonable value of 1024, or 32 kb of memory.
++ * Current kernels don't allow kmalloc with more than 128kb, so the
++ * maximum is 4096
++ */
++static int maxchannels = 1024;
++MODULE_PARM(maxchannels,"i");
++
++/**
++ * struct cmb - basic channel measurement block
++ *
++ * cmb as used by the hardware the fields are described in z/Architecture
++ * Principles of Operation, chapter 17.
++ * The area to be a contiguous array and may not be reallocated or freed.
++ * Only one cmb area can be present in the system.
++ */
++struct cmb {
++	u16 ssch_rsch_count;
++	u16 sample_count;
++	u32 device_connect_time;
++	u32 function_pending_time;
++	u32 device_disconnect_time;
++	u32 control_unit_queuing_time;
++	u32 device_active_only_time;
++	u32 reserved[2];
++};
++
++/* insert a single device into the cmb_area list
++ * called with cmb_area.lock held from alloc_cmb
++ */
++static inline int
++alloc_cmb_single (struct cmf_device *cdev)
++{
++	struct cmb *cmb;
++	struct cmf_device *node;
++	int ret;
++
++	spin_lock_irq(cdev->ccwlock);
++	if (!list_empty(&cdev->cmb_list)) {
++		ret = -EBUSY;
++		goto out;
++	}
++
++	/* find first unused cmb in cmb_area.mem.
++	 * this is a little tricky: cmb_area.list
++	 * remains sorted by ->cmb pointers */
++	cmb = cmb_area.mem;
++	list_for_each_entry(node, &cmb_area.list, cmb_list) {
++		if ((struct cmb*)node->cmb > cmb)
++			break;
++		cmb++;
++	}
++	if (cmb - cmb_area.mem >= maxchannels) {
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	/* insert new cmb */
++	list_add_tail(&cdev->cmb_list, &node->cmb_list);
++	cdev->cmb = cmb;
++	ret = 0;
++out:
++	spin_unlock_irq(cdev->ccwlock);
++	return ret;
++}
++
++static int
++alloc_cmb (struct cmf_device *cdev)
++{
++	int ret;
++	struct cmb *mem;
++	ssize_t size;
++
++	spin_lock(&cmb_area.lock);
++
++	if (!cmb_area.mem) {
++		/* there is no user yet, so we need a new area */
++		size = sizeof(struct cmb) * maxchannels;
++		BUG_ON(!list_empty(&cmb_area.list));
++
++		spin_unlock(&cmb_area.lock);
++		mem = kmalloc(size, GFP_KERNEL | GFP_DMA);
++		spin_lock(&cmb_area.lock);
++
++		if (cmb_area.mem) {
++			/* ok, another thread was faster */
++			kfree(mem);
++		} else if (!mem) {
++			/* no luck */
++			ret = -ENOMEM;
++			goto out;
++		} else {
++			/* everything ok */
++			memset(mem, 0, size);
++			cmb_area.mem = mem;
++			cmf_activate(cmb_area.mem, 1);
++		}
++	}
++
++	/* do the actual allocation */
++	ret = alloc_cmb_single(cdev);
++out:
++	spin_unlock(&cmb_area.lock);
++
++	return ret;
++}
++
++static void
++free_cmb(struct cmf_device *cdev)
++{
++	spin_lock(&cmb_area.lock);
++	spin_lock_irq(cdev->ccwlock);
++
++	if (list_empty(&cdev->cmb_list)) {
++		/* already freed */
++		goto out;
++	}
++
++	cdev->cmb = NULL;
++	list_del_init(&cdev->cmb_list);
++
++	if (list_empty(&cmb_area.list)) {
++		cmf_activate(NULL, 0);
++		kfree(cmb_area.mem);
++		cmb_area.mem = NULL;
++	}
++out:
++	spin_unlock_irq(cdev->ccwlock);
++	spin_unlock(&cmb_area.lock);
++}
++
++static int
++set_cmb(struct cmf_device *cdev, u32 mme)
++{
++	u16 offset;
++
++	if (!cdev->cmb)
++		return -EINVAL;
++
++	offset = mme ? (struct cmb *)cdev->cmb - cmb_area.mem : 0;
++
++	return set_schib(cdev->irq, mme, 0, offset);
++}
++
++static int
++readall_cmb (struct cmf_device *cdev, struct cmbdata *data)
++{
++	/* yes, we have to put it on the stack
++	 * because the cmb must only be accessed
++	 * atomically, e.g. with mvc */
++	struct cmb cmb;
++	unsigned long flags;
++	u64 time;
++
++	spin_lock_irqsave(cdev->ccwlock, flags);
++	if (!cdev->cmb) {
++		spin_unlock_irqrestore(cdev->ccwlock, flags);
++		return -ENODEV;
++	}
++
++	cmb = *(struct cmb*)cdev->cmb;
++	time = get_clock() - cdev->cmb_start_time;
++	spin_unlock_irqrestore(cdev->ccwlock, flags);
++
++	*data = (struct cmbdata) {
++		/* we only know values before device_busy_time */
++		.size = offsetof(struct cmbdata, device_busy_time),
++
++		/* conver to nanoseconds */
++		.elapsed_time = (time * 1000) >> 12,
++
++		/* copy data to new structure */
++		.ssch_rsch_count		= cmb.ssch_rsch_count,
++		.sample_count			= cmb.sample_count,
++
++		/* time fields are converted to nanoseconds while copying */
++		.device_connect_time
++			= time_to_nsec(cmb.device_connect_time),
++		.function_pending_time
++			= time_to_nsec(cmb.function_pending_time),
++		.device_disconnect_time
++			= time_to_nsec(cmb.device_disconnect_time),
++		.control_unit_queuing_time
++		 	= time_to_nsec(cmb.control_unit_queuing_time),
++		.device_active_only_time
++			= time_to_nsec(cmb.device_active_only_time),
++	};
++
++	return 0;
++}
++
++static void
++reset_cmb(struct cmf_device *cdev)
++{
++	struct cmb *cmb;
++	spin_lock_irq(cdev->ccwlock);
++	cmb = cdev->cmb;
++	if (cmb)
++		memset (cmb, 0, sizeof (*cmb));
++	cdev->cmb_start_time = get_clock();
++	spin_unlock_irq(cdev->ccwlock);
++}
++
++static struct cmb_operations cmbops_basic = {
++	.alloc	= alloc_cmb,
++	.free	= free_cmb,
++	.set	= set_cmb,
++	.readall= readall_cmb,
++	.reset	= reset_cmb,
++};
++
++/* ******** extended cmb handling ********/
++
++/**
++ * struct cmbe - extended channel measurement block
++ *
++ * cmb as used by the hardware, may be in any 64 bit physical location,
++ * the fields are described in z/Architecture Principles of Operation,
++ * third edition, chapter 17.
++ */
++struct cmbe {
++	u32 ssch_rsch_count;
++	u32 sample_count;
++	u32 device_connect_time;
++	u32 function_pending_time;
++	u32 device_disconnect_time;
++	u32 control_unit_queuing_time;
++	u32 device_active_only_time;
++	u32 device_busy_time;
++	u32 initial_command_response_time;
++	u32 reserved[7];
++};
++
++static int
++alloc_cmbe (struct cmf_device *cdev)
++{
++	struct cmbe *cmbe;
++	cmbe = kmalloc (sizeof (*cmbe), GFP_KERNEL /* | GFP_DMA ? */);
++	if (!cmbe)
++		return -ENOMEM;
++
++	spin_lock_irq(cdev->ccwlock);
++	if (cdev->cmb)
++		kfree(cmbe);
++	else
++		cdev->cmb = cmbe;
++	spin_unlock_irq(cdev->ccwlock);
++
++	/* activate global measurement if this is the first channel */
++	spin_lock(&cmb_area.lock);
++	if (list_empty(&cmb_area.list))
++		cmf_activate(NULL, 1);
++	list_add_tail(&cdev->cmb_list, &cmb_area.list);
++	spin_unlock(&cmb_area.lock);
++
++	return 0;
++}
++
++static void
++free_cmbe (struct cmf_device *cdev)
++{
++	spin_lock_irq(cdev->ccwlock);
++	if (cdev->cmb)
++		kfree(cdev->cmb);
++	cdev->cmb = NULL;
++	spin_unlock_irq(cdev->ccwlock);
++
++	/* deactivate global measurement if this is the last channel */
++	spin_lock(&cmb_area.lock);
++	list_del_init(&cdev->cmb_list);
++	if (list_empty(&cmb_area.list))
++		cmf_activate(NULL, 0);
++	spin_unlock(&cmb_area.lock);
++}
++
++static int
++set_cmbe(struct cmf_device *cdev, u32 mme)
++{
++	unsigned long mba;
++
++	if (!cdev->cmb)
++		return -EINVAL;
++
++	mba = mme ? (unsigned long)cdev->cmb : 0;
++
++	return set_schib(cdev->irq, mme, 1, mba);
++}
++
++static int
++readall_cmbe (struct cmf_device *cdev, struct cmbdata *data)
++{
++	/* yes, we have to put it on the stack
++	 * because the cmb must only be accessed
++	 * atomically, e.g. with mvc */
++	struct cmbe cmb;
++	unsigned long flags;
++	u64 time;
++
++	spin_lock_irqsave(cdev->ccwlock, flags);
++	if (!cdev->cmb) {
++		spin_unlock_irqrestore(cdev->ccwlock, flags);
++		return -ENODEV;
++	}
++
++	cmb = *(struct cmbe*)cdev->cmb;
++	time = get_clock() - cdev->cmb_start_time;
++	spin_unlock_irqrestore(cdev->ccwlock, flags);
++	
++	*data = (struct cmbdata) {
++		/* we only know values before device_busy_time */
++		.size = offsetof(struct cmbdata, device_busy_time),
++
++		/* conver to nanoseconds */
++		.elapsed_time = (time * 1000) >> 12,
++
++		/* copy data to new structure */
++		.ssch_rsch_count		= cmb.ssch_rsch_count,
++		.sample_count			= cmb.sample_count,
++
++		/* time fields are converted to nanoseconds while copying */
++		.device_connect_time
++			= time_to_nsec(cmb.device_connect_time),
++		.function_pending_time
++			= time_to_nsec(cmb.function_pending_time),
++		.device_disconnect_time
++			= time_to_nsec(cmb.device_disconnect_time),
++		.control_unit_queuing_time
++		 	= time_to_nsec(cmb.control_unit_queuing_time),
++		.device_active_only_time
++			= time_to_nsec(cmb.device_active_only_time),
++		.device_busy_time
++			= time_to_nsec(cmb.device_busy_time),
++		.initial_command_response_time
++			= time_to_nsec(cmb.initial_command_response_time),
++	};
++
++	return 0;
++}
++
++static void
++reset_cmbe(struct cmf_device *cdev)
++{
++	struct cmbe *cmb;
++	spin_lock_irq(cdev->ccwlock);
++	cmb = cdev->cmb;
++	if (cmb)
++		memset (cmb, 0, sizeof (*cmb));
++	cdev->cmb_start_time = get_clock();
++	spin_unlock_irq(cdev->ccwlock);
++}
++
++static struct cmb_operations cmbops_extended = {
++	.alloc	= alloc_cmbe,
++	.free	= free_cmbe,
++	.set	= set_cmbe,
++	.readall= readall_cmbe,
++	.reset	= reset_cmbe,
++};
++
++/******* external interface to kernel *******/
++
++/**
++ * enable_cmf, disable_cmf, set_cmf, cmf_readall, cmf_reset:
++ *  simple wrappers around the cmb_operations.
++ */
++int
++enable_cmf(struct cmf_device *cdev)
++{
++	return cmbops->alloc(cdev);
++}
++
++void
++disable_cmf(struct cmf_device *cdev)
++{
++	cmbops->free(cdev);
++}
++
++int
++set_cmf(struct cmf_device *cdev, u32 mme)
++{
++	return cmbops->set(cdev, mme);
++}
++
++int
++cmf_readall(struct cmf_device *cdev, struct cmbdata *data)
++{
++	return cmbops->readall(cdev, data);
++}
++
++void
++cmf_reset(struct cmf_device *cdev)
++{
++	return cmbops->reset(cdev);
++}
++
++/* set up maxchannels and format parameter when the module is built-in */
++#ifndef MODULE
++static int __init
++setup_cmf_maxchannels(char *arg)
++{
++	int arglen = sizeof("cmf_maxchannels=") - 1;
++	int c;
++	
++	c = simple_strtoul(arg + arglen + 1, 0, 0);
++	if (c <= 0 || c > 65536) {
++		printk(KERN_WARNING "Invalid parameter %s, using "
++			"default (%d)\n", arg, maxchannels);
++	} else {
++		maxchannels = c;
++	}
++
++	return 0;
++}
++__setup("cmf_maxchannels=", setup_cmf_maxchannels);
++
++static int __init
++setup_cmf_format(char *arg)
++{
++	int arglen = sizeof("cmf_format") - 1;
++	
++	format = simple_strtoul(arg + arglen, 0, 0);
++	if (format < CMF_BASIC || format > CMF_EXTENDED) {
++		printk(KERN_WARNING "Invalid parameter %s\n", arg);
++		format = -1;
++	}
++
++	return 0;
++}
++__setup("cmf_format=", setup_cmf_format);
++#endif
++
++static int __init
++init_cmf(void)
++{
++	char *format_string;
++	char *detect_string = "parameter";
++
++	/* We cannot really autoprobe this. If the user did not give a parameter,
++	   see if we are running on z990 or up, otherwise fall back to basic mode. */
++
++	if (format == CMF_AUTODETECT) {
++		if (!MACHINE_NEW_STIDP) {
++			format = CMF_BASIC;
++		} else {
++			format = CMF_EXTENDED;
++		}
++		detect_string = "autodetected";
++	} else {
++		detect_string = "parameter";
++	}
++
++	switch (format) {
++	case CMF_BASIC:
++		format_string = "basic";
++		cmbops = &cmbops_basic;
++		if (maxchannels > 4096 || maxchannels < 1) {
++			printk(KERN_ERR "Basic channel measurement facility"
++					" can only use 1 to 4096 devices\n"
++			       KERN_ERR "when the cmf driver is built"
++					" as a loadable module\n");
++			return 1;
++		}
++		break;
++	case CMF_EXTENDED:
++ 		format_string = "extended";
++		cmbops = &cmbops_extended;
++		break;
++	default:
++		printk(KERN_ERR "Invalid format %d for channel "
++			"measurement facility\n", format);
++		return 1;
++	}
++
++	printk(KERN_INFO "Channel measurement facility using %s format (%s)\n",
++		format_string, detect_string);
++	return 0;
++}
++
++module_init(init_cmf);
++
++
++MODULE_AUTHOR("Arnd Bergmann <arndb at de.ibm.com>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("channel measurement facility base driver\n"
++		   "Copyright 2003 IBM Corporation\n");
++
++EXPORT_SYMBOL_GPL(enable_cmf);
++EXPORT_SYMBOL_GPL(disable_cmf);
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/misc/Makefile	2006-01-30 22:25:25.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-2.4.27.orig/drivers/s390/misc/chandev.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/misc/chandev.c	2006-01-30 22:25:25.000000000 -0700
+@@ -1023,15 +1023,16 @@
+ 
+ 	/* 3172/2216 Paralell the 2216 allows 16 ports per card the */
+ 	/* the original 3172 only allows 4 we will assume the max of 16 */
+-	chandev_add_model(chandev_type_lcs|chandev_type_ctc,0x3088,0x8,-1,-1,15,default_msck_bits,FALSE,FALSE);
++	chandev_add_model(chandev_type_lcs|chandev_type_ctc|chandev_type_ctcmpc,0x3088,0x8,-1,-1,15,default_msck_bits,FALSE,FALSE);
+ 
+ 	/* 3172/2216 Escon serial the 2216 allows 16 ports per card the */
+ 	/* the original 3172 only allows 4 we will assume the max of 16 */
+-	chandev_add_model(chandev_type_lcs|chandev_type_escon,0x3088,0x1F,-1,-1,15,default_msck_bits,FALSE,FALSE);
++	chandev_add_model(chandev_type_lcs|chandev_type_escon|chandev_type_ctcmpc,0x3088,0x1F,-1,-1,15,default_msck_bits,FALSE,FALSE);
+ 
+ 	/* Only 2 ports allowed on OSA2 cards model 0x60 */
+ 	chandev_add_model(chandev_type_lcs,0x3088,0x60,-1,-1,1,default_msck_bits,FALSE,FALSE);
+-	/* qeth gigabit ethernet */
++	chandev_add_model(chandev_type_claw,0x3088,0x61,-1,-1,0,default_msck_bits,FALSE,FALSE);
++       /* qeth gigabit ethernet */
+ 	chandev_add_model(chandev_type_qeth,0x1731,0x1,0x1732,0x1,0,default_msck_bits,FALSE,FALSE);
+ 	chandev_add_model(chandev_type_qeth,0x1731,0x5,0x1732,0x5,0,default_msck_bits,FALSE,FALSE);
+ 	/* Osa-D we currently aren't too emotionally involved with this */
+@@ -1040,7 +1041,7 @@
+ 	chandev_add_model(chandev_type_claw,0x3088,0x61,-1,-1,0,default_msck_bits,FALSE,FALSE);
+ 
+ 	/* ficon attached ctc */
+-	chandev_add_model(chandev_type_escon,0x3088,0x1E,-1,-1,0,default_msck_bits,FALSE,FALSE);
++	chandev_add_model(chandev_type_escon|chandev_type_ctcmpc,0x3088,0x1E,-1,-1,0,default_msck_bits,FALSE,FALSE);
+ }
+ 
+ 
+@@ -2187,6 +2188,7 @@
+ {
+ 	"noauto",
+ 	"del_noauto",
++	"ctcmpc",
+ 	"ctc",
+ 	"escon",
+ 	"lcs",
+@@ -2224,6 +2226,7 @@
+ 	first_stridx=0,
+ 	noauto_stridx=first_stridx,
+ 	del_noauto_stridx,
++	ctcmpc_stridx,
+ 	ctc_stridx,
+ 	escon_stridx,
+ 	lcs_stridx,
+@@ -2285,7 +2288,7 @@
+ 
+ 
+ static char chandev_keydescript[]=
+-"\nchan_type key bitfield ctc=0x1,escon=0x2,lcs=0x4,osad=0x8,qeth=0x10,claw=0x20\n";
++"\nchan_type key bitfield ctc=0x1,escon=0x2,lcs=0x4,osad=0x8,qeth=0x10,claw=0x20,ctcmpc=0x40\n";
+ 
+ 
+ #if  CONFIG_ARCH_S390X
+@@ -2500,10 +2503,12 @@
+ 						  ints[3],ints[4],ints[5],ints[6],ints[7],
+ 						  NULL,NULL,NULL);
+ 				break;
++			case (ctcmpc_stridx*stridx_mult)|isnum|iscomma:
+ 			case (ctc_stridx*stridx_mult)|isnum|iscomma:
+ 			case (escon_stridx*stridx_mult)|isnum|iscomma:
+ 			case (lcs_stridx*stridx_mult)|isnum|iscomma:
+ 			case (osad_stridx*stridx_mult)|isnum|iscomma:
++			case (ctcmpc_stridx*stridx_mult)|iscomma:
+ 			case (ctc_stridx*stridx_mult)|iscomma:
+ 			case (escon_stridx*stridx_mult)|iscomma:
+ 			case (lcs_stridx*stridx_mult)|iscomma:
+@@ -2513,6 +2518,9 @@
+ 				case (ctc_stridx*stridx_mult):
+ 					chan_type=chandev_type_ctc;
+ 					break;
++				case (ctcmpc_stridx*stridx_mult):
++					chan_type=chandev_type_ctcmpc;
++					break;
+ 				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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/misc/z90common.h	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,149 @@
++/*
++ *  linux/drivers/s390/misc/z90common.h
++ *
++ *  z90crypt 1.3.2
++ *
++ *  Copyright (C)  2001, 2004 IBM Corporation
++ *  Author(s): Robert Burroughs (burrough at us.ibm.com)
++ *             Eric Rossman (edrossma at us.ibm.com)
++ *
++ *  Hotplug & misc device support: Jochen Roehrig (roehrig 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.
++ */
++#ifndef _Z90COMMON_
++#define _Z90COMMON_
++#define VERSION_Z90COMMON_H "$Revision: 1.4.6.9 $"
++#define RESPBUFFSIZE 256
++#define PCI_FUNC_KEY_DECRYPT 0x5044
++#define PCI_FUNC_KEY_ENCRYPT 0x504B
++extern int ext_bitlens; 
++enum devstat {
++	DEV_GONE,		
++	DEV_ONLINE,		
++	DEV_QUEUE_FULL,		
++	DEV_EMPTY,		
++	DEV_NO_WORK,		
++	DEV_BAD_MESSAGE,	
++	DEV_TSQ_EXCEPTION,	
++	DEV_RSQ_EXCEPTION,	
++	DEV_SEN_EXCEPTION,	
++	DEV_REC_EXCEPTION	
++};
++enum hdstat {
++	HD_NOT_THERE,		
++	HD_BUSY,		
++	HD_DECONFIGURED,	
++	HD_CHECKSTOPPED,	
++	HD_ONLINE,		
++	HD_TSQ_EXCEPTION	
++};
++#define Z90C_NO_DEVICES		1
++#define Z90C_AMBIGUOUS_DOMAIN	2
++#define Z90C_INCORRECT_DOMAIN	3
++#define ENOTINIT		4
++#define SEN_BUSY	 7	
++#define SEN_USER_ERROR	 8	
++#define SEN_QUEUE_FULL	11	
++#define SEN_NOT_AVAIL	16	
++#define SEN_PAD_ERROR	17	
++#define SEN_RETRY	18	
++#define SEN_RELEASED	24	
++#define REC_EMPTY	 4	
++#define REC_BUSY	 6	
++#define REC_OPERAND_INV	 8	
++#define REC_OPERAND_SIZE 9	
++#define REC_EVEN_MOD	10	
++#define REC_NO_WORK	11	
++#define REC_HARDWAR_ERR	12	
++#define REC_NO_RESPONSE	13	
++#define REC_RETRY_DEV	14	
++#define REC_USER_GONE	15	
++#define REC_BAD_MESSAGE	16	
++#define REC_INVALID_PAD	17	
++#define REC_USE_PCICA	18	
++#define WRONG_DEVICE_TYPE 20	
++#define REC_FATAL_ERROR 32	
++#define SEN_FATAL_ERROR 33	
++#define TSQ_FATAL_ERROR 34
++#define RSQ_FATAL_ERROR 35
++#define Z90CRYPT_NUM_TYPES	5
++#define PCICA		0
++#define PCICC		1
++#define PCIXCC_MCL2	2
++#define PCIXCC_MCL3	3
++#define CEX2C		4
++#define NILDEV		-1
++#define ANYDEV		-1
++#define PCIXCC_UNK	-2	
++enum hdevice_type {
++	PCICC_HW  = 3,
++	PCICA_HW  = 4,
++	PCIXCC_HW = 5,
++	OTHER_HW  = 6,	
++	CEX2C_HW  = 7
++};
++struct CPRBX {
++	unsigned short cprb_len;	
++	unsigned char  cprb_ver_id;	
++	unsigned char  pad_000[3];	
++	unsigned char  func_id[2];	
++	unsigned char  cprb_flags[4];	
++	unsigned int   req_parml;	
++	unsigned int   req_datal;	
++	unsigned int   rpl_msgbl;	
++	unsigned int   rpld_parml;	
++	unsigned int   rpl_datal;	
++	unsigned int   rpld_datal;	
++	unsigned int   req_extbl;	
++	unsigned char  pad_001[4];	
++	unsigned int   rpld_extbl;	
++	unsigned char  req_parmb[16];	
++	unsigned char  req_datab[16];	
++	unsigned char  rpl_parmb[16];	
++	unsigned char  rpl_datab[16];	
++	unsigned char  req_extb[16];	
++	unsigned char  rpl_extb[16];	
++	unsigned short ccp_rtcode;	
++	unsigned short ccp_rscode;	
++	unsigned int   mac_data_len;	
++	unsigned char  logon_id[8];	
++	unsigned char  mac_value[8];	
++	unsigned char  mac_content_flgs;
++	unsigned char  pad_002;		
++	unsigned short domain;		
++	unsigned char  pad_003[12];	
++	unsigned char  pad_004[36];	
++};
++#ifndef DEV_NAME
++#define DEV_NAME	"z90crypt"
++#endif
++#define PRINTK(fmt, args...) \
++	printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
++#define PRINTKN(fmt, args...) \
++	printk(KERN_DEBUG DEV_NAME ": " fmt, ## args)
++#define PRINTKW(fmt, args...) \
++	printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
++#define PRINTKC(fmt, args...) \
++	printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
++#ifdef Z90CRYPT_DEBUG
++#define PDEBUG(fmt, args...) \
++	printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
++#else
++#define PDEBUG(fmt, args...) do {} while (0)
++#endif
++#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/misc/z90crypt.h	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,258 @@
++/*
++ *  linux/drivers/s390/misc/z90crypt.h
++ *
++ *  z90crypt 1.3.2
++ *
++ *  Copyright (C)  2001, 2004 IBM Corporation
++ *  Author(s): Robert Burroughs (burrough at us.ibm.com)
++ *             Eric Rossman (edrossma at us.ibm.com)
++ *
++ *  Hotplug & misc device support: Jochen Roehrig (roehrig 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.
++ */
++
++#ifndef _LINUX_Z90CRYPT_H_
++#define _LINUX_Z90CRYPT_H_
++
++#include <linux/ioctl.h>
++
++#define VERSION_Z90CRYPT_H "$Revision: 1.3.4.7 $"
++
++#define z90crypt_VERSION 1
++#define z90crypt_RELEASE 3	// 2 = PCIXCC, 3 = rewrite for coding standards
++#define z90crypt_VARIANT 2	// 2 = added PCIXCC MCL3 and CEX2C support
++
++/**
++ * If we are not using the sparse checker, __user has no use.
++ */
++#ifdef __CHECKER__
++# define __user		__attribute__((noderef, address_space(1)))
++#else
++# define __user
++#endif
++
++/**
++ * struct ica_rsa_modexpo
++ *
++ * Requirements:
++ * - outputdatalength is at least as large as inputdatalength.
++ * - All key parts are right justified in their fields, padded on
++ *   the left with zeroes.
++ * - length(b_key) = inputdatalength
++ * - length(n_modulus) = inputdatalength
++ */
++struct ica_rsa_modexpo {
++	char __user *	inputdata;
++	unsigned int	inputdatalength;
++	char __user *	outputdata;
++	unsigned int	outputdatalength;
++	char __user *	b_key;
++	char __user *	n_modulus;
++};
++
++/**
++ * struct ica_rsa_modexpo_crt
++ *
++ * Requirements:
++ * - inputdatalength is even.
++ * - outputdatalength is at least as large as inputdatalength.
++ * - All key parts are right justified in their fields, padded on
++ *   the left with zeroes.
++ * - length(bp_key)	= inputdatalength/2 + 8
++ * - length(bq_key)	= inputdatalength/2
++ * - length(np_key)	= inputdatalength/2 + 8
++ * - length(nq_key)	= inputdatalength/2
++ * - length(u_mult_inv) = inputdatalength/2 + 8
++ */
++struct ica_rsa_modexpo_crt {
++	char __user *	inputdata;
++	unsigned int	inputdatalength;
++	char __user *	outputdata;
++	unsigned int	outputdatalength;
++	char __user *	bp_key;
++	char __user *	bq_key;
++	char __user *	np_prime;
++	char __user *	nq_prime;
++	char __user *	u_mult_inv;
++};
++
++#define Z90_IOCTL_MAGIC 'z'  // NOTE:  Need to allocate from linux folks
++
++/**
++ * Interface notes:
++ *
++ * The ioctl()s which are implemented (along with relevant details)
++ * are:
++ *
++ *   ICARSAMODEXPO
++ *     Perform an RSA operation using a Modulus-Exponent pair
++ *     This takes an ica_rsa_modexpo struct as its arg.
++ *
++ *     NOTE: please refer to the comments preceding this structure
++ *           for the implementation details for the contents of the
++ *           block
++ *
++ *   ICARSACRT
++ *     Perform an RSA operation using a Chinese-Remainder Theorem key
++ *     This takes an ica_rsa_modexpo_crt struct as its arg.
++ *
++ *     NOTE: please refer to the comments preceding this structure
++ *           for the implementation details for the contents of the
++ *           block
++ *
++ *   Z90STAT_TOTALCOUNT
++ *     Return an integer count of all device types together.
++ *
++ *   Z90STAT_PCICACOUNT
++ *     Return an integer count of all PCICAs.
++ *
++ *   Z90STAT_PCICCCOUNT
++ *     Return an integer count of all PCICCs.
++ *
++ *   Z90STAT_PCIXCCMCL2COUNT
++ *     Return an integer count of all MCL2 PCIXCCs.
++ *
++ *   Z90STAT_PCIXCCMCL3COUNT
++ *     Return an integer count of all MCL3 PCIXCCs.
++ *
++ *   Z90STAT_CEX2CCOUNT
++ *     Return an integer count of all CEX2Cs.
++ *
++ *   Z90STAT_REQUESTQ_COUNT
++ *     Return an integer count of the number of entries waiting to be
++ *     sent to a device.
++ *
++ *   Z90STAT_PENDINGQ_COUNT
++ *     Return an integer count of the number of entries sent to a
++ *     device awaiting the reply.
++ *
++ *   Z90STAT_TOTALOPEN_COUNT
++ *     Return an integer count of the number of open file handles.
++ *
++ *   Z90STAT_DOMAIN_INDEX
++ *     Return the integer value of the Cryptographic Domain.
++ *
++ *   Z90STAT_STATUS_MASK
++ *     Return an 64 element array of unsigned chars for the status of
++ *     all devices.
++ *       0x01: PCICA
++ *       0x02: PCICC
++ *       0x03: PCIXCC_MCL2
++ *       0x04: PCIXCC_MCL3
++ *       0x05: CEX2C
++ *       0x0d: device is disabled via the proc filesystem
++ *
++ *   Z90STAT_QDEPTH_MASK
++ *     Return an 64 element array of unsigned chars for the queue
++ *     depth of all devices.
++ *
++ *   Z90STAT_PERDEV_REQCNT
++ *     Return an 64 element array of unsigned integers for the number
++ *     of successfully completed requests per device since the device
++ *     was detected and made available.
++ *
++ *   ICAZ90STATUS (deprecated)
++ *     Return some device driver status in a ica_z90_status struct
++ *     This takes an ica_z90_status struct as its arg.
++ *
++ *     NOTE: this ioctl() is deprecated, and has been replaced with
++ *           single ioctl()s for each type of status being requested
++ *
++ *   Z90STAT_PCIXCCCOUNT (deprecated)
++ *     Return an integer count of all PCIXCCs (MCL2 + MCL3).
++ *     This is DEPRECATED now that MCL3 PCIXCCs are treated differently from
++ *     MCL2 PCIXCCs.
++ *
++ *   Z90QUIESCE (not recommended)
++ *     Quiesce the driver.  This is intended to stop all new
++ *     requests from being processed.  Its use is NOT recommended,
++ *     except in circumstances where there is no other way to stop
++ *     callers from accessing the driver.  Its original use was to
++ *     allow the driver to be "drained" of work in preparation for
++ *     a system shutdown.
++ *
++ *     NOTE: once issued, this ban on new work cannot be undone
++ *           except by unloading and reloading the driver.
++ */
++
++/**
++ * Supported ioctl calls
++ */
++#define ICARSAMODEXPO	_IOC(_IOC_READ|_IOC_WRITE, Z90_IOCTL_MAGIC, 0x05, 0)
++#define ICARSACRT	_IOC(_IOC_READ|_IOC_WRITE, Z90_IOCTL_MAGIC, 0x06, 0)
++
++/* DEPRECATED status calls (bound for removal at some point) */
++#define ICAZ90STATUS	_IOR(Z90_IOCTL_MAGIC, 0x10, struct ica_z90_status)
++#define Z90STAT_PCIXCCCOUNT	_IOR(Z90_IOCTL_MAGIC, 0x43, int)
++
++/* unrelated to ICA callers */
++#define Z90QUIESCE	_IO(Z90_IOCTL_MAGIC, 0x11)
++
++/* New status calls */
++#define Z90STAT_TOTALCOUNT	_IOR(Z90_IOCTL_MAGIC, 0x40, int)
++#define Z90STAT_PCICACOUNT	_IOR(Z90_IOCTL_MAGIC, 0x41, int)
++#define Z90STAT_PCICCCOUNT	_IOR(Z90_IOCTL_MAGIC, 0x42, int)
++#define Z90STAT_PCIXCCMCL2COUNT	_IOR(Z90_IOCTL_MAGIC, 0x4b, int)
++#define Z90STAT_PCIXCCMCL3COUNT	_IOR(Z90_IOCTL_MAGIC, 0x4c, int)
++#define Z90STAT_CEX2CCOUNT	_IOR(Z90_IOCTL_MAGIC, 0x4d, int)
++#define Z90STAT_REQUESTQ_COUNT	_IOR(Z90_IOCTL_MAGIC, 0x44, int)
++#define Z90STAT_PENDINGQ_COUNT	_IOR(Z90_IOCTL_MAGIC, 0x45, int)
++#define Z90STAT_TOTALOPEN_COUNT _IOR(Z90_IOCTL_MAGIC, 0x46, int)
++#define Z90STAT_DOMAIN_INDEX	_IOR(Z90_IOCTL_MAGIC, 0x47, int)
++#define Z90STAT_STATUS_MASK	_IOR(Z90_IOCTL_MAGIC, 0x48, char[64])
++#define Z90STAT_QDEPTH_MASK	_IOR(Z90_IOCTL_MAGIC, 0x49, char[64])
++#define Z90STAT_PERDEV_REQCNT	_IOR(Z90_IOCTL_MAGIC, 0x4a, int[64])
++
++/**
++ * local errno definitions
++ */
++#define ENOBUFF	  129	// filp->private_data->...>work_elem_p->buffer is NULL
++#define EWORKPEND 130	// user issues ioctl while another pending
++#define ERELEASED 131	// user released while ioctl pending
++#define EQUIESCE  132	// z90crypt quiescing (no more work allowed)
++#define ETIMEOUT  133	// request timed out
++#define EUNKNOWN  134	// some unrecognized error occured (retry may succeed)
++#define EGETBUFF  135	// Error getting buffer or hardware lacks capability
++			// (retry in software)
++
++/**
++ * DEPRECATED STRUCTURES
++ */
++
++/**
++ * This structure is DEPRECATED and the corresponding ioctl() has been
++ * replaced with individual ioctl()s for each piece of data!
++ * This structure will NOT survive past version 1.3.1, so switch to the
++ * new ioctl()s.
++ */
++#define MASK_LENGTH 64 // mask length
++struct ica_z90_status {
++	int totalcount;
++	int leedslitecount; // PCICA
++	int leeds2count;    // PCICC
++	// int PCIXCCCount; is not in struct for backward compatibility
++	int requestqWaitCount;
++	int pendingqWaitCount;
++	int totalOpenCount;
++	int cryptoDomain;
++	// status: 0=not there, 1=PCICA, 2=PCICC, 3=PCIXCC_MCL2, 4=PCIXCC_MCL3,
++	//         5=CEX2C
++	unsigned char status[MASK_LENGTH];
++	// qdepth: # work elements waiting for each device
++	unsigned char qdepth[MASK_LENGTH];
++};
++
++#endif /* _LINUX_Z90CRYPT_H_ */
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/z90hardware.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/misc/z90hardware.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,2092 @@
++/*
++ *  linux/drivers/s390/misc/z90hardware.c
++ *
++ *  z90crypt 1.3.2
++ *
++ *  Copyright (C)  2001, 2004 IBM Corporation
++ *  Author(s): Robert Burroughs (burrough at us.ibm.com)
++ *             Eric Rossman (edrossma at us.ibm.com)
++ *
++ *  Hotplug & misc device support: Jochen Roehrig (roehrig 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 <asm/uaccess.h>	
++#include <linux/compiler.h>
++#include <linux/delay.h>	
++#include <linux/init.h>
++#include <linux/module.h>
++#include "z90crypt.h"
++#include "z90common.h"
++#define VERSION_Z90HARDWARE_C "$Revision: 1.7.6.10 $"
++char z90chardware_version[] __initdata =
++	"z90hardware.o (" VERSION_Z90HARDWARE_C "/"
++	                  VERSION_Z90COMMON_H "/" VERSION_Z90CRYPT_H ")";
++struct cca_token_hdr {
++	unsigned char  token_identifier;
++	unsigned char  version;
++	unsigned short token_length;
++	unsigned char  reserved[4];
++};
++#define CCA_TKN_HDR_ID_EXT 0x1E
++struct cca_private_ext_ME_sec {
++	unsigned char  section_identifier;
++	unsigned char  version;
++	unsigned short section_length;
++	unsigned char  private_key_hash[20];
++	unsigned char  reserved1[4];
++	unsigned char  key_format;
++	unsigned char  reserved2;
++	unsigned char  key_name_hash[20];
++	unsigned char  key_use_flags[4];
++	unsigned char  reserved3[6];
++	unsigned char  reserved4[24];
++	unsigned char  confounder[24];
++	unsigned char  exponent[128];
++	unsigned char  modulus[128];
++};
++#define CCA_PVT_USAGE_ALL 0x80
++struct cca_public_sec {
++	unsigned char  section_identifier;
++	unsigned char  version;
++	unsigned short section_length;
++	unsigned char  reserved[2];
++	unsigned short exponent_len;
++	unsigned short modulus_bit_len;
++	unsigned short modulus_byte_len;    
++	unsigned char  exponent[3];
++};
++struct cca_private_ext_ME {
++	struct cca_token_hdr	      pvtMEHdr;
++	struct cca_private_ext_ME_sec pvtMESec;
++	struct cca_public_sec	      pubMESec;
++};
++struct cca_public_key {
++	struct cca_token_hdr  pubHdr;
++	struct cca_public_sec pubSec;
++};
++struct cca_pvt_ext_CRT_sec {
++	unsigned char  section_identifier;
++	unsigned char  version;
++	unsigned short section_length;
++	unsigned char  private_key_hash[20];
++	unsigned char  reserved1[4];
++	unsigned char  key_format;
++	unsigned char  reserved2;
++	unsigned char  key_name_hash[20];
++	unsigned char  key_use_flags[4];
++	unsigned short p_len;
++	unsigned short q_len;
++	unsigned short dp_len;
++	unsigned short dq_len;
++	unsigned short u_len;
++	unsigned short mod_len;
++	unsigned char  reserved3[4];
++	unsigned short pad_len;
++	unsigned char  reserved4[52];
++	unsigned char  confounder[8];
++};
++#define CCA_PVT_EXT_CRT_SEC_ID_PVT 0x08
++#define CCA_PVT_EXT_CRT_SEC_FMT_CL 0x40
++struct cca_private_ext_CRT {
++	struct cca_token_hdr	   pvtCrtHdr;
++	struct cca_pvt_ext_CRT_sec pvtCrtSec;
++	struct cca_public_sec	   pubCrtSec;
++};
++struct ap_status_word {
++	unsigned char q_stat_flags;
++	unsigned char response_code;
++	unsigned char reserved[2];
++};
++#define AP_Q_STATUS_EMPTY		0x80
++#define AP_Q_STATUS_REPLIES_WAITING	0x40
++#define AP_Q_STATUS_ARRAY_FULL		0x20
++#define AP_RESPONSE_NORMAL		0x00
++#define AP_RESPONSE_Q_NOT_AVAIL		0x01
++#define AP_RESPONSE_RESET_IN_PROGRESS	0x02
++#define AP_RESPONSE_DECONFIGURED	0x03
++#define AP_RESPONSE_CHECKSTOPPED	0x04
++#define AP_RESPONSE_BUSY		0x05
++#define AP_RESPONSE_Q_FULL		0x10
++#define AP_RESPONSE_NO_PENDING_REPLY	0x10   
++#define AP_RESPONSE_INDEX_TOO_BIG	0x11
++#define AP_RESPONSE_NO_FIRST_PART	0x13
++#define AP_RESPONSE_MESSAGE_TOO_BIG	0x15
++#define AP_MAX_CDX_BITL		4
++#define AP_RQID_RESERVED_BITL	4
++#define SKIP_BITL		(AP_MAX_CDX_BITL + AP_RQID_RESERVED_BITL)
++struct type4_hdr {
++	unsigned char  reserved1;
++	unsigned char  msg_type_code;	
++	unsigned short msg_len;
++	unsigned char  request_code;	
++	unsigned char  msg_fmt;
++	unsigned short reserved2;
++};
++#define TYPE4_TYPE_CODE 0x04
++#define TYPE4_REQU_CODE 0x40
++#define TYPE4_SME_LEN 0x0188
++#define TYPE4_LME_LEN 0x0308
++#define TYPE4_SCR_LEN 0x01E0
++#define TYPE4_LCR_LEN 0x03A0
++#define TYPE4_SME_FMT 0x00
++#define TYPE4_LME_FMT 0x10
++#define TYPE4_SCR_FMT 0x40
++#define TYPE4_LCR_FMT 0x50
++struct type4_sme {
++	struct type4_hdr header;
++	unsigned char	 message[128];
++	unsigned char	 exponent[128];
++	unsigned char	 modulus[128];
++};
++struct type4_lme {
++	struct type4_hdr header;
++	unsigned char	 message[256];
++	unsigned char	 exponent[256];
++	unsigned char	 modulus[256];
++};
++struct type4_scr {
++	struct type4_hdr header;
++	unsigned char	 message[128];
++	unsigned char	 dp[72];
++	unsigned char	 dq[64];
++	unsigned char	 p[72];
++	unsigned char	 q[64];
++	unsigned char	 u[72];
++};
++struct type4_lcr {
++	struct type4_hdr header;
++	unsigned char	 message[256];
++	unsigned char	 dp[136];
++	unsigned char	 dq[128];
++	unsigned char	 p[136];
++	unsigned char	 q[128];
++	unsigned char	 u[136];
++};
++union type4_msg {
++	struct type4_sme sme;
++	struct type4_lme lme;
++	struct type4_scr scr;
++	struct type4_lcr lcr;
++};
++struct type84_hdr {
++	unsigned char  reserved1;
++	unsigned char  code;
++	unsigned short len;
++	unsigned char  reserved2[4];
++};
++#define TYPE84_RSP_CODE 0x84
++struct type6_hdr {
++	unsigned char reserved1;	
++	unsigned char type;		
++	unsigned char reserved2[2];	
++	unsigned char right[4];		
++	unsigned char reserved3[2];	
++	unsigned char reserved4[2];	
++	unsigned char apfs[4];		
++	unsigned int  offset1;		
++	unsigned int  offset2;		
++	unsigned int  offset3;		
++	unsigned int  offset4;		
++	unsigned char agent_id[16];	
++					
++					
++					
++					
++					
++					
++	unsigned char rqid[2];		
++	unsigned char reserved5[2];	
++	unsigned char function_code[2];	
++	unsigned char reserved6[2];	
++	unsigned int  ToCardLen1;	
++	unsigned int  ToCardLen2;	
++	unsigned int  ToCardLen3;	
++	unsigned int  ToCardLen4;	
++	unsigned int  FromCardLen1;	
++	unsigned int  FromCardLen2;	
++	unsigned int  FromCardLen3;	
++	unsigned int  FromCardLen4;	
++};
++struct CPRB {
++	unsigned char cprb_len[2];	
++	unsigned char cprb_ver_id;	
++	unsigned char pad_000;		
++	unsigned char srpi_rtcode[4];	
++	unsigned char srpi_verb;	
++	unsigned char flags;		
++	unsigned char func_id[2];	
++	unsigned char checkpoint_flag;	
++	unsigned char resv2;		
++	unsigned char req_parml[2];	
++					
++	unsigned char req_parmp[4];	
++	unsigned char req_datal[4];	
++					
++	unsigned char req_datap[4];	
++					
++	unsigned char rpl_parml[2];	
++					
++	unsigned char pad_001[2];	
++	unsigned char rpl_parmp[4];	
++	unsigned char rpl_datal[4];	
++	unsigned char rpl_datap[4];	
++					
++	unsigned char ccp_rscode[2];	
++	unsigned char ccp_rtcode[2];	
++	unsigned char repd_parml[2];	
++	unsigned char mac_data_len[2];	
++	unsigned char repd_datal[4];	
++	unsigned char req_pc[2];	
++	unsigned char res_origin[8];	
++	unsigned char mac_value[8];	
++	unsigned char logon_id[8];	
++	unsigned char usage_domain[2];	
++	unsigned char resv3[18];	
++	unsigned char svr_namel[2];	
++	unsigned char svr_name[8];	
++};
++struct type6_msg {
++	struct type6_hdr header;
++	struct CPRB	 CPRB;
++};
++union request_msg {
++	union  type4_msg t4msg;
++	struct type6_msg t6msg;
++};
++struct request_msg_ext {
++	int		  q_nr;
++	unsigned char	  *psmid;
++	union request_msg reqMsg;
++};
++struct type82_hdr {
++	unsigned char reserved1;	
++	unsigned char type;		
++	unsigned char reserved2[2];	
++	unsigned char reply_code;	
++	unsigned char reserved3[3];	
++};
++#define TYPE82_RSP_CODE 0x82
++#define REPLY_ERROR_MACHINE_FAILURE  0x10
++#define REPLY_ERROR_PREEMPT_FAILURE  0x12
++#define REPLY_ERROR_CHECKPT_FAILURE  0x14
++#define REPLY_ERROR_MESSAGE_TYPE     0x20
++#define REPLY_ERROR_INVALID_COMM_CD  0x21 
++#define REPLY_ERROR_INVALID_MSG_LEN  0x23
++#define REPLY_ERROR_RESERVD_FIELD    0x24 
++#define REPLY_ERROR_FORMAT_FIELD     0x29
++#define REPLY_ERROR_INVALID_COMMAND  0x30
++#define REPLY_ERROR_MALFORMED_MSG    0x40
++#define REPLY_ERROR_RESERVED_FIELDO  0x50 
++#define REPLY_ERROR_WORD_ALIGNMENT   0x60
++#define REPLY_ERROR_MESSAGE_LENGTH   0x80
++#define REPLY_ERROR_OPERAND_INVALID  0x82
++#define REPLY_ERROR_OPERAND_SIZE     0x84
++#define REPLY_ERROR_EVEN_MOD_IN_OPND 0x85
++#define REPLY_ERROR_RESERVED_FIELD   0x88
++#define REPLY_ERROR_TRANSPORT_FAIL   0x90
++#define REPLY_ERROR_PACKET_TRUNCATED 0xA0
++#define REPLY_ERROR_ZERO_BUFFER_LEN  0xB0
++struct type86_hdr {
++	unsigned char reserved1;	
++	unsigned char type;		
++	unsigned char format;		
++	unsigned char reserved2;	
++	unsigned char reply_code;	
++	unsigned char reserved3[3];	
++};
++#define TYPE86_RSP_CODE 0x86
++#define TYPE86_FMT2	0x02
++struct type86_fmt2_msg {
++	struct type86_hdr hdr;
++	unsigned char	  reserved[4];	
++	unsigned char	  apfs[4];	
++	unsigned int	  count1;	
++	unsigned int	  offset1;	
++	unsigned int	  count2;	
++	unsigned int	  offset2;	
++	unsigned int	  count3;	
++	unsigned int	  offset3;	
++	unsigned int	  count4;	
++	unsigned int	  offset4;	
++};
++static struct type6_hdr static_type6_hdr = {
++	0x00,
++	0x06,
++	{0x00,0x00},
++	{0x00,0x00,0x00,0x00},
++	{0x00,0x00},
++	{0x00,0x00},
++	{0x00,0x00,0x00,0x00},
++	0x00000058,
++	0x00000000,
++	0x00000000,
++	0x00000000,
++	{0x01,0x00,0x43,0x43,0x41,0x2D,0x41,0x50,
++	 0x50,0x4C,0x20,0x20,0x20,0x01,0x01,0x01},
++	{0x00,0x00},
++	{0x00,0x00},
++	{0x50,0x44},
++	{0x00,0x00},
++	0x00000000,	
++	0x00000000,
++	0x00000000,
++	0x00000000,
++	0x00000000,	
++	0x00000000,
++	0x00000000,
++	0x00000000
++};
++static struct type6_hdr static_type6_hdrX = {
++	0x00,
++	0x06,
++	{0x00,0x00},
++	{0x00,0x00,0x00,0x00},
++	{0x00,0x00},
++	{0x00,0x00},
++	{0x00,0x00,0x00,0x00},
++	0x00000058,
++	0x00000000,
++	0x00000000,
++	0x00000000,
++	{0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00},
++	{0x00,0x00},
++	{0x50,0x44},
++	{0x00,0x00},
++	0x00000000,	
++	0x00000000,
++	0x00000000,
++	0x00000000,
++	0x00000000,	
++	0x00000000,
++	0x00000000,
++	0x00000000
++};
++static struct CPRB static_cprb = {
++	{0x70,0x00},
++	0x41,
++	0x00,
++	{0x00,0x00,0x00,0x00},
++	0x00,
++	0x00,
++	{0x54,0x32},
++	0x01,
++	0x00,
++	{0x00,0x00},	
++	{0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00},
++	{0x00,0x00},	
++	{0x00,0x00},
++	{0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00},
++	{0x00,0x00},
++	{0x00,0x00},
++	{0x00,0x00},
++	{0x00,0x00},
++	{0x00,0x00,0x00,0x00},
++	{0x00,0x00},
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00},	
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00},
++	{0x08,0x00},
++	{0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20}
++};
++struct function_and_rules_block {
++	unsigned char function_code[2];
++	unsigned char ulen[2];
++	unsigned char only_rule[8];
++};
++static struct function_and_rules_block static_pkd_function_and_rules = {
++	{0x50,0x44},			   
++	{0x0A,0x00},			   
++	{'P','K','C','S','-','1','.','2'}
++};
++static struct function_and_rules_block static_pke_function_and_rules = {
++	{0x50,0x4B},			   
++	{0x0A,0x00},			   
++	{'P','K','C','S','-','1','.','2'}
++};
++struct T6_keyBlock_hdr {
++	unsigned char blen[2];
++	unsigned char ulen[2];
++	unsigned char flags[2];
++};
++static struct T6_keyBlock_hdr static_T6_keyBlock_hdr = {
++	{0x89,0x01},	   
++	{0x87,0x01},	   
++	{0x00}
++};
++static struct CPRBX static_cprbx = {
++	0x00DC,
++	0x02,
++	{0x00,0x00,0x00},
++	{0x54,0x32},
++	{0x00,0x00,0x00,0x00},
++	0x00000000,
++	0x00000000,
++	0x00000000,
++	0x00000000,
++	0x00000000,
++	0x00000000,
++	0x00000000,
++	{0x00,0x00,0x00,0x00},
++	0x00000000,
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	0x0000,
++	0x0000,
++	0x00000000,
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	0x00,
++	0x00,
++	0x0000,
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++	{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++	 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
++};
++static struct function_and_rules_block static_pkd_function_and_rulesX_MCL2 = {
++	{0x50,0x44},	
++	{0x00,0x0A},	
++	{'P','K','C','S','-','1','.','2'}
++};
++static struct function_and_rules_block static_pke_function_and_rulesX_MCL2 = {
++	{0x50,0x4B},	
++	{0x00,0x0A},	
++	{'Z','E','R','O','-','P','A','D'}
++};
++static struct function_and_rules_block static_pkd_function_and_rulesX = {
++	{0x50,0x44},	
++	{0x00,0x0A},	
++	{'Z','E','R','O','-','P','A','D'}
++};
++static struct function_and_rules_block static_pke_function_and_rulesX = {
++	{0x50,0x4B},	
++	{0x00,0x0A},	
++	{'M','R','P',' ',' ',' ',' ',' '}
++};
++struct T6_keyBlock_hdrX {
++	unsigned short blen;
++	unsigned short ulen;
++	unsigned char flags[2];
++};
++static unsigned char static_pad[256] = {
++0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57,
++0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39,
++0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5,0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D,
++0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB,0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F,
++0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9,0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45,
++0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9,0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F,
++0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD,0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D,
++0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD,0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9,
++0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B,0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B,
++0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B,0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD,
++0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7,0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1,
++0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3,0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23,
++0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55,0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43,
++0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F,0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F,
++0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD,
++0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09
++};
++static struct cca_private_ext_ME static_pvt_me_key = {
++	{
++		0x1E,
++		0x00,
++		0x0183,
++		{0x00,0x00,0x00,0x00}
++	},
++	{
++		0x02,
++		0x00,
++		0x016C,
++		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00},
++		{0x00,0x00,0x00,0x00},
++		0x00,
++		0x00,
++		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00},
++		{0x80,0x00,0x00,0x00},
++		{0x00,0x00,0x00,0x00,0x00,0x00},
++		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
++		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
++	},
++	{
++		0x04,
++		0x00,
++		0x000F,
++		{0x00,0x00},
++		0x0003,
++		0x0000,
++		0x0000,
++		{0x01,0x00,0x01}
++	}
++};
++static struct cca_public_key static_public_key = {
++	{
++		0x1E,
++		0x00,
++		0x0000,			
++		{0x00,0x00,0x00,0x00}
++	},
++	{
++		0x04,
++		0x00,
++		0x0000,			
++		{0x00,0x00},
++		0x0000,			
++		0x0000,			
++		0x0000,			
++		{0x01,0x00,0x01}
++	}
++};
++#define FIXED_TYPE6_ME_LEN 0x0000025F
++#define FIXED_TYPE6_ME_EN_LEN 0x000000F0
++#define FIXED_TYPE6_ME_LENX 0x000002CB
++#define FIXED_TYPE6_ME_EN_LENX 0x0000015C
++static struct cca_public_sec static_cca_pub_sec = {
++	0x04,
++	0x00,
++	0x000f,
++	{0x00,0x00},
++	0x0003,
++	0x0000,	      
++	0x0000,
++	{0x01,0x00,0x01}
++};
++#define FIXED_TYPE6_CR_LEN 0x00000177
++#define FIXED_TYPE6_CR_LENX 0x000001E3
++#define MAX_RESPONSE_SIZE 0x00000710
++#define MAX_RESPONSEX_SIZE 0x0000077C
++#define RESPONSE_CPRB_SIZE  0x000006B8 
++#define RESPONSE_CPRBX_SIZE 0x00000724 
++#define CALLER_HEADER 12	
++static unsigned char static_PKE_function_code[2] = {0x50, 0x4B};
++static inline int
++testq(int q_nr, int *q_depth, int *dev_type, struct ap_status_word *stat)
++{
++	int ccode;
++	asm volatile
++#ifdef __s390x__
++	("	llgfr	0,%4		\n"	
++	 "	slgr	1,1		\n"	
++	 "	lgr	2,1		\n"	
++	 "0:	.long	0xb2af0000	\n"	
++	 "1:	ipm	%0		\n"	
++	 "	srl	%0,28		\n"	
++	 "	iihh	%0,0		\n"	
++	 "	iihl	%0,0		\n"	
++	 "	lgr	%1,1		\n"	
++	 "	lgr	%3,2		\n"
++	 "	srl	%3,24		\n"	
++	 "	sll	2,24		\n"
++	 "	srl	2,24		\n"
++	 "	lgr	%2,2		\n"	
++	 "2:				\n"	
++	 ".section .fixup,\"ax\"	\n"
++	 "3:				\n"	
++	 "	lhi	%0,%h5		\n"
++	 "	jg	2b		\n"
++	 ".previous			\n"
++	 ".section __ex_table,\"a\"	\n"
++	 "	.align	8		\n"
++	 "	.quad	0b,3b		\n"
++	 "	.quad	1b,3b		\n"
++	 ".previous"
++	 :"=d" (ccode),"=d" (*stat),"=d" (*q_depth), "=d" (*dev_type)
++	 :"d" (q_nr), "K" (DEV_TSQ_EXCEPTION)
++	 :"cc","0","1","2","memory");
++#else
++	("	lr	0,%4		\n"	
++	 "	slr	1,1		\n"	
++	 "	lr	2,1		\n"	
++	 "0:	.long	0xb2af0000	\n"	
++	 "1:	ipm	%0		\n"	
++	 "	srl	%0,28		\n"	
++	 "	lr	%1,1		\n"	
++	 "	lr	%3,2		\n"
++	 "	srl	%3,24		\n"	
++	 "	sll	2,24		\n"
++	 "	srl	2,24		\n"
++	 "	lr	%2,2		\n"	
++	 "2:				\n"	
++	 ".section .fixup,\"ax\"	\n"
++	 "3:				\n"	
++	 "	lhi	%0,%h5		\n"
++	 "	bras	1,4f		\n"
++	 "	.long	2b		\n"
++	 "4:				\n"
++	 "	l	1,0(1)		\n"
++	 "	br	1		\n"
++	 ".previous			\n"
++	 ".section __ex_table,\"a\"	\n"
++	 "	.align	4		\n"
++	 "	.long	0b,3b		\n"
++	 "	.long	1b,3b		\n"
++	 ".previous"
++	 :"=d" (ccode),"=d" (*stat),"=d" (*q_depth), "=d" (*dev_type)
++	 :"d" (q_nr), "K" (DEV_TSQ_EXCEPTION)
++	 :"cc","0","1","2","memory");
++#endif
++	return ccode;
++}
++static inline int
++resetq(int q_nr, struct ap_status_word *stat_p)
++{
++	int ccode;
++	asm volatile
++#ifdef __s390x__
++	("	llgfr	0,%2		\n"	
++	 "	lghi	1,1		\n"	
++	 "	sll	1,24		\n"	
++	 "	or	0,1		\n"	
++	 "	slgr	1,1		\n"	
++	 "	lgr	2,1		\n"	
++	 "0:	.long	0xb2af0000	\n"	
++	 "1:	ipm	%0		\n"	
++	 "	srl	%0,28		\n"	
++	 "	iihh	%0,0		\n"	
++	 "	iihl	%0,0		\n"	
++	 "	lgr	%1,1		\n"	
++	 "2:				\n"	
++	 ".section .fixup,\"ax\"	\n"
++	 "3:				\n"	
++	 "	lhi	%0,%h3		\n"
++	 "	jg	2b		\n"
++	 ".previous			\n"
++	 ".section __ex_table,\"a\"	\n"
++	 "	.align	8		\n"
++	 "	.quad	0b,3b		\n"
++	 "	.quad	1b,3b		\n"
++	 ".previous"
++	 :"=d" (ccode),"=d" (*stat_p)
++	 :"d" (q_nr), "K" (DEV_RSQ_EXCEPTION)
++	 :"cc","0","1","2","memory");
++#else
++	("	lr	0,%2		\n"	
++	 "	lhi	1,1		\n"	
++	 "	sll	1,24		\n"	
++	 "	or	0,1		\n"	
++	 "	slr	1,1		\n"	
++	 "	lr	2,1		\n"	
++	 "0:	.long	0xb2af0000	\n"	
++	 "1:	ipm	%0		\n"	
++	 "	srl	%0,28		\n"	
++	 "	lr	%1,1		\n"	
++	 "2:				\n"	
++	 ".section .fixup,\"ax\"	\n"
++	 "3:				\n"	
++	 "	lhi	%0,%h3		\n"
++	 "	bras	1,4f		\n"
++	 "	.long	2b		\n"
++	 "4:				\n"
++	 "	l	1,0(1)		\n"
++	 "	br	1		\n"
++	 ".previous			\n"
++	 ".section __ex_table,\"a\"	\n"
++	 "	.align	4		\n"
++	 "	.long	0b,3b		\n"
++	 "	.long	1b,3b		\n"
++	 ".previous"
++	 :"=d" (ccode),"=d" (*stat_p)
++	 :"d" (q_nr), "K" (DEV_RSQ_EXCEPTION)
++	 :"cc","0","1","2","memory");
++#endif
++	return ccode;
++}
++static inline int
++sen(int msg_len, unsigned char *msg_ext, struct ap_status_word *stat)
++{
++	int ccode;
++	asm volatile
++#ifdef __s390x__
++	("	lgr	6,%3		\n"	
++	 "	llgfr	7,%2		\n"	
++	 "	llgt	0,0(6)		\n"	
++	 "	lghi	1,64		\n"	
++	 "	sll	1,24		\n"	
++	 "	or	0,1		\n"	
++	 "	la	6,4(6)		\n"	
++	 "	llgt	2,0(6)		\n"	
++	 "	llgt	3,4(6)		\n"	
++	 "	la	6,8(6)		\n"	
++	 "	slr	1,1		\n"	
++	 "0:	.long	0xb2ad0026	\n"	
++	 "1:	brc	2,0b		\n"	
++	 "	ipm	%0		\n"	
++	 "	srl	%0,28		\n"	
++	 "	iihh	%0,0		\n"	
++	 "	iihl	%0,0		\n"	
++	 "	lgr	%1,1		\n"	
++	 "2:				\n"	
++	 ".section .fixup,\"ax\"	\n"
++	 "3:				\n"	
++	 "	lhi	%0,%h4		\n"
++	 "	jg	2b		\n"
++	 ".previous			\n"
++	 ".section __ex_table,\"a\"	\n"
++	 "	.align	8		\n"
++	 "	.quad	0b,3b		\n"
++	 "	.quad	1b,3b		\n"
++	 ".previous"
++	 :"=d" (ccode),"=d" (*stat)
++	 :"d" (msg_len),"a" (msg_ext), "K" (DEV_SEN_EXCEPTION)
++	 :"cc","0","1","2","3","6","7","memory");
++#else
++	("	lr	6,%3		\n"	
++	 "	lr	7,%2		\n"	
++	 "	l	0,0(6)		\n"	
++	 "	lhi	1,64		\n"	
++	 "	sll	1,24		\n"	
++	 "	or	0,1		\n"	
++	 "	la	6,4(6)		\n"	
++	 "	l	2,0(6)		\n"	
++	 "	l	3,4(6)		\n"	
++	 "	la	6,8(6)		\n"	
++	 "	slr	1,1		\n"	
++	 "0:	.long	0xb2ad0026	\n"	
++	 "1:	brc	2,0b		\n"	
++	 "	ipm	%0		\n"	
++	 "	srl	%0,28		\n"	
++	 "	lr	%1,1		\n"	
++	 "2:				\n"	
++	 ".section .fixup,\"ax\"	\n"
++	 "3:				\n"	
++	 "	lhi	%0,%h4		\n"
++	 "	bras	1,4f		\n"
++	 "	.long	2b		\n"
++	 "4:				\n"
++	 "	l	1,0(1)		\n"
++	 "	br	1		\n"
++	 ".previous			\n"
++	 ".section __ex_table,\"a\"	\n"
++	 "	.align	4		\n"
++	 "	.long	0b,3b		\n"
++	 "	.long	1b,3b		\n"
++	 ".previous"
++	 :"=d" (ccode),"=d" (*stat)
++	 :"d" (msg_len),"a" (msg_ext), "K" (DEV_SEN_EXCEPTION)
++	 :"cc","0","1","2","3","6","7","memory");
++#endif
++	return ccode;
++}
++static inline int
++rec(int q_nr, int buff_l, unsigned char *rsp, unsigned char *id,
++    struct ap_status_word *st)
++{
++	int ccode;
++	asm volatile
++#ifdef __s390x__
++	("	llgfr	0,%2		\n"	
++	 "	lgr	3,%4		\n"	
++	 "	lgr	6,%3		\n"	
++	 "	llgfr	7,%5		\n"	
++	 "	lghi	1,128		\n"	
++	 "	sll	1,24		\n"	
++	 "	or	0,1		\n"	
++	 "	slgr	1,1		\n"	
++	 "	lgr	2,1		\n"	
++	 "	lgr	4,1		\n"	
++	 "	lgr	5,1		\n"	
++	 "0:	.long	0xb2ae0046	\n"	
++	 "1:	brc	2,0b		\n"	
++	 "	brc	4,0b		\n"	
++	 "	ipm	%0		\n"	
++	 "	srl	%0,28		\n"	
++	 "	iihh	%0,0		\n"	
++	 "	iihl	%0,0		\n"	
++	 "	lgr	%1,1		\n"	
++	 "	st	4,0(3)		\n"	
++	 "	st	5,4(3)		\n"	
++	 "2:				\n"	
++	 ".section .fixup,\"ax\"	\n"
++	 "3:				\n"	
++	 "	lhi   %0,%h6		\n"
++	 "	jg    2b		\n"
++	 ".previous			\n"
++	 ".section __ex_table,\"a\"	\n"
++	 "   .align	8		\n"
++	 "   .quad	0b,3b		\n"
++	 "   .quad	1b,3b		\n"
++	 ".previous"
++	 :"=d"(ccode),"=d"(*st)
++	 :"d" (q_nr), "d" (rsp), "d" (id), "d" (buff_l), "K" (DEV_REC_EXCEPTION)
++	 :"cc","0","1","2","3","4","5","6","7","memory");
++#else
++	("	lr	0,%2		\n"	
++	 "	lr	3,%4		\n"	
++	 "	lr	6,%3		\n"	
++	 "	lr	7,%5		\n"	
++	 "	lhi	1,128		\n"	
++	 "	sll	1,24		\n"	
++	 "	or	0,1		\n"	
++	 "	slr	1,1		\n"	
++	 "	lr	2,1		\n"	
++	 "	lr	4,1		\n"	
++	 "	lr	5,1		\n"	
++	 "0:	.long	0xb2ae0046	\n"	
++	 "1:	brc	2,0b		\n"	
++	 "	brc	4,0b		\n"	
++	 "	ipm	%0		\n"	
++	 "	srl	%0,28		\n"	
++	 "	lr	%1,1		\n"	
++	 "	st	4,0(3)		\n"	
++	 "	st	5,4(3)		\n"	
++	 "2:				\n"	
++	 ".section .fixup,\"ax\"	\n"
++	 "3:				\n"	
++	 "	lhi   %0,%h6		\n"
++	 "	bras  1,4f		\n"
++	 "	.long 2b		\n"
++	 "4:				\n"
++	 "	l     1,0(1)		\n"
++	 "	br    1			\n"
++	 ".previous			\n"
++	 ".section __ex_table,\"a\"	\n"
++	 "   .align	4		\n"
++	 "   .long	0b,3b		\n"
++	 "   .long	1b,3b		\n"
++	 ".previous"
++	 :"=d"(ccode),"=d"(*st)
++	 :"d" (q_nr), "d" (rsp), "d" (id), "d" (buff_l), "K" (DEV_REC_EXCEPTION)
++	 :"cc","0","1","2","3","4","5","6","7","memory");
++#endif
++	return ccode;
++}
++static inline void
++itoLe2(int *i_p, unsigned char *lechars)
++{
++	*lechars       = *((unsigned char *) i_p + sizeof(int) - 1);
++	*(lechars + 1) = *((unsigned char *) i_p + sizeof(int) - 2);
++}
++static inline void
++le2toI(unsigned char *lechars, int *i_p)
++{
++	unsigned char *ic_p;
++	*i_p = 0;
++	ic_p = (unsigned char *) i_p;
++	*(ic_p + 2) = *(lechars + 1);
++	*(ic_p + 3) = *(lechars);
++}
++static inline int
++is_empty(unsigned char *ptr, int len)
++{
++	return !memcmp(ptr, (unsigned char *) &static_pvt_me_key+60, len);
++}
++enum hdstat
++query_online(int deviceNr, int cdx, int resetNr, int *q_depth, int *dev_type)
++{
++	int q_nr, i, t_depth, t_dev_type;
++	enum devstat ccode;
++	struct ap_status_word stat_word;
++	enum hdstat stat;
++	int break_out;
++	q_nr = (deviceNr << SKIP_BITL) + cdx;
++	stat = HD_BUSY;
++	ccode = testq(q_nr, &t_depth, &t_dev_type, &stat_word);
++	PDEBUG("ccode %d response_code %02X\n", ccode, stat_word.response_code);
++	break_out = 0;
++	for (i = 0; i < resetNr; i++) {
++		if (ccode > 3) {
++			PRINTKC("Exception testing device %d\n", i);
++			return HD_TSQ_EXCEPTION;
++		}
++		switch (ccode) {
++		case 0:
++			PDEBUG("t_dev_type %d\n", t_dev_type);
++			break_out = 1;
++			stat = HD_ONLINE;
++			*q_depth = t_depth + 1;
++			switch (t_dev_type) {
++			case OTHER_HW:
++				stat = HD_NOT_THERE;
++				*dev_type = NILDEV;
++				break;
++			case PCICA_HW:
++				*dev_type = PCICA;
++				break;
++			case PCICC_HW:
++				*dev_type = PCICC;
++				break;
++			case PCIXCC_HW:
++				*dev_type = PCIXCC_UNK;
++				break;
++			case CEX2C_HW:
++				*dev_type = CEX2C;
++				break;
++			default:
++				*dev_type = NILDEV;
++				break;
++			}
++			PDEBUG("available device %d: Q depth = %d, dev "
++			       "type = %d, stat = %02X%02X%02X%02X\n",
++			       deviceNr, *q_depth, *dev_type,
++			       stat_word.q_stat_flags,
++			       stat_word.response_code,
++			       stat_word.reserved[0],
++			       stat_word.reserved[1]);
++			break;
++		case 3:
++			switch (stat_word.response_code) {
++			case AP_RESPONSE_NORMAL:
++				stat = HD_ONLINE;
++				break_out = 1;
++				*q_depth = t_depth + 1;
++				*dev_type = t_dev_type;
++				PDEBUG("cc3, available device "
++				       "%d: Q depth = %d, dev "
++				       "type = %d, stat = "
++				       "%02X%02X%02X%02X\n",
++				       deviceNr, *q_depth,
++				       *dev_type,
++				       stat_word.q_stat_flags,
++				       stat_word.response_code,
++				       stat_word.reserved[0],
++				       stat_word.reserved[1]);
++				break;
++			case AP_RESPONSE_Q_NOT_AVAIL:
++				stat = HD_NOT_THERE;
++				break_out = 1;
++				break;
++			case AP_RESPONSE_RESET_IN_PROGRESS:
++				PDEBUG("device %d in reset\n",
++				       deviceNr);
++				break;
++			case AP_RESPONSE_DECONFIGURED:
++				stat = HD_DECONFIGURED;
++				break_out = 1;
++				break;
++			case AP_RESPONSE_CHECKSTOPPED:
++				stat = HD_CHECKSTOPPED;
++				break_out = 1;
++				break;
++			case AP_RESPONSE_BUSY:
++				PDEBUG("device %d busy\n",
++				       deviceNr);
++				break;
++			default:
++				break;
++			}
++			break;
++		default:
++			stat = HD_NOT_THERE;
++			break_out = 1;
++			break;
++		}
++		if (break_out)
++			break;
++		udelay(5);
++		ccode = testq(q_nr, &t_depth, &t_dev_type, &stat_word);
++	}
++	return stat;
++}
++enum devstat
++reset_device(int deviceNr, int cdx, int resetNr)
++{
++	int q_nr, ccode = 0, dummy_qdepth, dummy_devType, i;
++	struct ap_status_word stat_word;
++	enum devstat stat;
++	int break_out;
++	q_nr = (deviceNr << SKIP_BITL) + cdx;
++	stat = DEV_GONE;
++	ccode = resetq(q_nr, &stat_word);
++	if (ccode > 3)
++		return DEV_RSQ_EXCEPTION;
++	break_out = 0;
++	for (i = 0; i < resetNr; i++) {
++		switch (ccode) {
++		case 0:
++			stat = DEV_ONLINE;
++			if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY)
++				break_out = 1;
++			break;
++		case 3:
++			switch (stat_word.response_code) {
++			case AP_RESPONSE_NORMAL:
++				stat = DEV_ONLINE;
++				if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY)
++					break_out = 1;
++				break;
++			case AP_RESPONSE_Q_NOT_AVAIL:
++			case AP_RESPONSE_DECONFIGURED:
++			case AP_RESPONSE_CHECKSTOPPED:
++				stat = DEV_GONE;
++				break_out = 1;
++				break;
++			case AP_RESPONSE_RESET_IN_PROGRESS:
++			case AP_RESPONSE_BUSY:
++			default:
++				break;
++			}
++			break;
++		default:
++			stat = DEV_GONE;
++			break_out = 1;
++			break;
++		}
++		if (break_out == 1)
++			break;
++		udelay(5);
++		ccode = testq(q_nr, &dummy_qdepth, &dummy_devType, &stat_word);
++		if (ccode > 3) {
++			stat = DEV_TSQ_EXCEPTION;
++			break;
++		}
++	}
++	PDEBUG("Number of testq's needed for reset: %d\n", i);
++	if (i >= resetNr) {
++	  stat = DEV_GONE;
++	}
++	return stat;
++}
++#ifdef DEBUG_HYDRA_MSGS
++static inline void
++print_buffer(unsigned char *buffer, int bufflen)
++{
++	int i;
++	for (i = 0; i < bufflen; i += 16) {
++		PRINTK("%04X: %02X%02X%02X%02X %02X%02X%02X%02X "
++		       "%02X%02X%02X%02X %02X%02X%02X%02X\n", i,
++		       buffer[i+0], buffer[i+1], buffer[i+2], buffer[i+3],
++		       buffer[i+4], buffer[i+5], buffer[i+6], buffer[i+7],
++		       buffer[i+8], buffer[i+9], buffer[i+10], buffer[i+11],
++		       buffer[i+12], buffer[i+13], buffer[i+14], buffer[i+15]);
++	}
++}
++#endif
++enum devstat
++send_to_AP(int dev_nr, int cdx, int msg_len, unsigned char *msg_ext)
++{
++	struct ap_status_word stat_word;
++	enum devstat stat;
++	int ccode;
++	((struct request_msg_ext *) msg_ext)->q_nr =
++		(dev_nr << SKIP_BITL) + cdx;
++	PDEBUG("msg_len passed to sen: %d\n", msg_len);
++	PDEBUG("q number passed to sen: %02x%02x%02x%02x\n",
++	       msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3]);
++	stat = DEV_GONE;
++#ifdef DEBUG_HYDRA_MSGS
++	PRINTK("Request header: %02X%02X%02X%02X %02X%02X%02X%02X "
++	       "%02X%02X%02X%02X\n",
++	       msg_ext[0], msg_ext[1], msg_ext[2], msg_ext[3],
++	       msg_ext[4], msg_ext[5], msg_ext[6], msg_ext[7],
++	       msg_ext[8], msg_ext[9], msg_ext[10], msg_ext[11]);
++	
++	print_buffer(msg_ext+CALLER_HEADER, msg_len);
++#endif
++	ccode = sen(msg_len, msg_ext, &stat_word);
++	if (ccode > 3)
++		return DEV_SEN_EXCEPTION;
++	PDEBUG("nq cc: %u, st: %02x%02x%02x%02x\n",
++	       ccode, stat_word.q_stat_flags, stat_word.response_code,
++	       stat_word.reserved[0], stat_word.reserved[1]);
++	switch (ccode) {
++	case 0:
++		stat = DEV_ONLINE;
++		break;
++	case 1:
++		stat = DEV_GONE;
++		break;
++	case 3:
++		switch (stat_word.response_code) {
++		case AP_RESPONSE_NORMAL:
++			stat = DEV_ONLINE;
++			break;
++		case AP_RESPONSE_Q_FULL:
++			stat = DEV_QUEUE_FULL;
++			break;
++		default:
++			stat = DEV_GONE;
++			break;
++		}
++		break;
++	default:
++		stat = DEV_GONE;
++		break;
++	}
++	return stat;
++}
++enum devstat
++receive_from_AP(int dev_nr, int cdx, int resplen, unsigned char *resp,
++		unsigned char *psmid)
++{
++	int ccode;
++	struct ap_status_word stat_word;
++	enum devstat stat;
++	memset(resp, 0x00, 8);
++	ccode = rec((dev_nr << SKIP_BITL) + cdx, resplen, resp, psmid,
++		    &stat_word);
++	if (ccode > 3)
++		return DEV_REC_EXCEPTION;
++	PDEBUG("dq cc: %u, st: %02x%02x%02x%02x\n",
++	       ccode, stat_word.q_stat_flags, stat_word.response_code,
++	       stat_word.reserved[0], stat_word.reserved[1]);
++	stat = DEV_GONE;
++	switch (ccode) {
++	case 0:
++		stat = DEV_ONLINE;
++#ifdef DEBUG_HYDRA_MSGS
++		print_buffer(resp, resplen);
++#endif
++		break;
++	case 3:
++		switch (stat_word.response_code) {
++		case AP_RESPONSE_NORMAL:
++			stat = DEV_ONLINE;
++			break;
++		case AP_RESPONSE_NO_PENDING_REPLY:
++			if (stat_word.q_stat_flags & AP_Q_STATUS_EMPTY)
++				stat = DEV_EMPTY;
++			else
++				stat = DEV_NO_WORK;
++			break;
++		case AP_RESPONSE_INDEX_TOO_BIG:
++		case AP_RESPONSE_NO_FIRST_PART:
++		case AP_RESPONSE_MESSAGE_TOO_BIG:
++			stat = DEV_BAD_MESSAGE;
++			break;
++		default:
++			break;
++		}
++		break;
++	default:
++		break;
++	}
++	return stat;
++}
++static inline int
++pad_msg(unsigned char *buffer, int  totalLength, int msgLength)
++{
++	int pad_len;
++	for (pad_len = 0; pad_len < (totalLength - msgLength); pad_len++)
++		if (buffer[pad_len] != 0x00)
++			break;
++	pad_len -= 3; 
++	if (pad_len < 8)
++		return SEN_PAD_ERROR;
++	buffer[0] = 0x00;
++	buffer[1] = 0x02;
++	memcpy(buffer+2, static_pad, pad_len);
++	buffer[pad_len + 2] = 0x00;
++	return 0;
++}
++static inline int
++is_common_public_key(unsigned char *key, int len)
++{
++	int i;
++	for (i = 0; i < len; i++)
++		if (key[i])
++			break;
++	key += i;
++	len -= i;
++	if (((len == 1) && (key[0] == 3)) ||
++	    ((len == 3) && (key[0] == 1) && (key[1] == 0) && (key[2] == 1)))
++		return 1;
++	return 0;
++}
++static int
++ICAMEX_msg_to_type4MEX_msg(struct ica_rsa_modexpo *icaMex_p, int *z90cMsg_l_p,
++			   union type4_msg *z90cMsg_p)
++{
++	int mod_len, msg_size, mod_tgt_len, exp_tgt_len, inp_tgt_len;
++	unsigned char *mod_tgt, *exp_tgt, *inp_tgt;
++	union type4_msg *tmp_type4_msg;
++	mod_len = icaMex_p->inputdatalength;
++	msg_size = ((mod_len <= 128) ? TYPE4_SME_LEN : TYPE4_LME_LEN) +
++		    CALLER_HEADER;
++	memset(z90cMsg_p, 0, msg_size);
++	tmp_type4_msg = (union type4_msg *)
++		((unsigned char *) z90cMsg_p + CALLER_HEADER);
++	tmp_type4_msg->sme.header.msg_type_code = TYPE4_TYPE_CODE;
++	tmp_type4_msg->sme.header.request_code = TYPE4_REQU_CODE;
++	if (mod_len <= 128) {
++		tmp_type4_msg->sme.header.msg_fmt = TYPE4_SME_FMT;
++		tmp_type4_msg->sme.header.msg_len = TYPE4_SME_LEN;
++		mod_tgt = tmp_type4_msg->sme.modulus;
++		mod_tgt_len = sizeof(tmp_type4_msg->sme.modulus);
++		exp_tgt = tmp_type4_msg->sme.exponent;
++		exp_tgt_len = sizeof(tmp_type4_msg->sme.exponent);
++		inp_tgt = tmp_type4_msg->sme.message;
++		inp_tgt_len = sizeof(tmp_type4_msg->sme.message);
++	} else {
++		tmp_type4_msg->lme.header.msg_fmt = TYPE4_LME_FMT;
++		tmp_type4_msg->lme.header.msg_len = TYPE4_LME_LEN;
++		mod_tgt = tmp_type4_msg->lme.modulus;
++		mod_tgt_len = sizeof(tmp_type4_msg->lme.modulus);
++		exp_tgt = tmp_type4_msg->lme.exponent;
++		exp_tgt_len = sizeof(tmp_type4_msg->lme.exponent);
++		inp_tgt = tmp_type4_msg->lme.message;
++		inp_tgt_len = sizeof(tmp_type4_msg->lme.message);
++	}
++	mod_tgt += (mod_tgt_len - mod_len);
++	if (copy_from_user(mod_tgt, icaMex_p->n_modulus, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(mod_tgt, mod_len))
++		return SEN_USER_ERROR;
++	exp_tgt += (exp_tgt_len - mod_len);
++	if (copy_from_user(exp_tgt, icaMex_p->b_key, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(exp_tgt, mod_len))
++		return SEN_USER_ERROR;
++	inp_tgt += (inp_tgt_len - mod_len);
++	if (copy_from_user(inp_tgt, icaMex_p->inputdata, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(inp_tgt, mod_len))
++		return SEN_USER_ERROR;
++	*z90cMsg_l_p = msg_size - CALLER_HEADER;
++	return 0;
++}
++static int
++ICACRT_msg_to_type4CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p,
++			   int *z90cMsg_l_p, union type4_msg *z90cMsg_p)
++{
++	int mod_len, short_len, long_len, tmp_size, p_tgt_len, q_tgt_len,
++	    dp_tgt_len, dq_tgt_len, u_tgt_len, inp_tgt_len;
++	unsigned char *p_tgt, *q_tgt, *dp_tgt, *dq_tgt, *u_tgt, *inp_tgt;
++	union type4_msg *tmp_type4_msg;
++	mod_len = icaMsg_p->inputdatalength;
++	short_len = mod_len / 2;
++	long_len = mod_len / 2 + 8;
++	tmp_size = ((mod_len <= 128) ? TYPE4_SCR_LEN : TYPE4_LCR_LEN) +
++		    CALLER_HEADER;
++	memset(z90cMsg_p, 0, tmp_size);
++	tmp_type4_msg = (union type4_msg *)
++		((unsigned char *) z90cMsg_p + CALLER_HEADER);
++	tmp_type4_msg->scr.header.msg_type_code = TYPE4_TYPE_CODE;
++	tmp_type4_msg->scr.header.request_code = TYPE4_REQU_CODE;
++	if (mod_len <= 128) {
++		tmp_type4_msg->scr.header.msg_fmt = TYPE4_SCR_FMT;
++		tmp_type4_msg->scr.header.msg_len = TYPE4_SCR_LEN;
++		p_tgt = tmp_type4_msg->scr.p;
++		p_tgt_len = sizeof(tmp_type4_msg->scr.p);
++		q_tgt = tmp_type4_msg->scr.q;
++		q_tgt_len = sizeof(tmp_type4_msg->scr.q);
++		dp_tgt = tmp_type4_msg->scr.dp;
++		dp_tgt_len = sizeof(tmp_type4_msg->scr.dp);
++		dq_tgt = tmp_type4_msg->scr.dq;
++		dq_tgt_len = sizeof(tmp_type4_msg->scr.dq);
++		u_tgt = tmp_type4_msg->scr.u;
++		u_tgt_len = sizeof(tmp_type4_msg->scr.u);
++		inp_tgt = tmp_type4_msg->scr.message;
++		inp_tgt_len = sizeof(tmp_type4_msg->scr.message);
++	} else {
++		tmp_type4_msg->lcr.header.msg_fmt = TYPE4_LCR_FMT;
++		tmp_type4_msg->lcr.header.msg_len = TYPE4_LCR_LEN;
++		p_tgt = tmp_type4_msg->lcr.p;
++		p_tgt_len = sizeof(tmp_type4_msg->lcr.p);
++		q_tgt = tmp_type4_msg->lcr.q;
++		q_tgt_len = sizeof(tmp_type4_msg->lcr.q);
++		dp_tgt = tmp_type4_msg->lcr.dp;
++		dp_tgt_len = sizeof(tmp_type4_msg->lcr.dp);
++		dq_tgt = tmp_type4_msg->lcr.dq;
++		dq_tgt_len = sizeof(tmp_type4_msg->lcr.dq);
++		u_tgt = tmp_type4_msg->lcr.u;
++		u_tgt_len = sizeof(tmp_type4_msg->lcr.u);
++		inp_tgt = tmp_type4_msg->lcr.message;
++		inp_tgt_len = sizeof(tmp_type4_msg->lcr.message);
++	}
++	p_tgt += (p_tgt_len - long_len);
++	if (copy_from_user(p_tgt, icaMsg_p->np_prime, long_len))
++		return SEN_RELEASED;
++	if (is_empty(p_tgt, long_len))
++		return SEN_USER_ERROR;
++	q_tgt += (q_tgt_len - short_len);
++	if (copy_from_user(q_tgt, icaMsg_p->nq_prime, short_len))
++		return SEN_RELEASED;
++	if (is_empty(q_tgt, short_len))
++		return SEN_USER_ERROR;
++	dp_tgt += (dp_tgt_len - long_len);
++	if (copy_from_user(dp_tgt, icaMsg_p->bp_key, long_len))
++		return SEN_RELEASED;
++	if (is_empty(dp_tgt, long_len))
++		return SEN_USER_ERROR;
++	dq_tgt += (dq_tgt_len - short_len);
++	if (copy_from_user(dq_tgt, icaMsg_p->bq_key, short_len))
++		return SEN_RELEASED;
++	if (is_empty(dq_tgt, short_len))
++		return SEN_USER_ERROR;
++	u_tgt += (u_tgt_len - long_len);
++	if (copy_from_user(u_tgt, icaMsg_p->u_mult_inv, long_len))
++		return SEN_RELEASED;
++	if (is_empty(u_tgt, long_len))
++		return SEN_USER_ERROR;
++	inp_tgt += (inp_tgt_len - mod_len);
++	if (copy_from_user(inp_tgt, icaMsg_p->inputdata, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(inp_tgt, mod_len))
++		return SEN_USER_ERROR;
++	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
++	return 0;
++}
++static int
++ICAMEX_msg_to_type6MEX_de_msg(struct ica_rsa_modexpo *icaMsg_p, int cdx,
++			      int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
++{
++	int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l;
++	unsigned char *temp;
++	struct type6_hdr *tp6Hdr_p;
++	struct CPRB *cprb_p;
++	struct cca_private_ext_ME *key_p;
++	static int deprecated_msg_count = 0;
++	mod_len = icaMsg_p->inputdatalength;
++	tmp_size = FIXED_TYPE6_ME_LEN + mod_len;
++	total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
++	parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
++	tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
++	memset(z90cMsg_p, 0, tmp_size);
++	
++	temp = (unsigned char *)z90cMsg_p + CALLER_HEADER;
++	memcpy(temp, &static_type6_hdr, sizeof(struct type6_hdr));
++	tp6Hdr_p = (struct type6_hdr *)temp;
++	tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
++	tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
++	
++	temp += sizeof(struct type6_hdr);
++	memcpy(temp, &static_cprb, sizeof(struct CPRB));
++	cprb_p = (struct CPRB *) temp;
++	cprb_p->usage_domain[0]= (unsigned char)cdx;
++	itoLe2(&parmBlock_l, cprb_p->req_parml);
++	itoLe2((int *)&(tp6Hdr_p->FromCardLen1), cprb_p->rpl_parml);
++	
++	temp += sizeof(struct CPRB);
++	memcpy(temp, &static_pkd_function_and_rules,
++	       sizeof(struct function_and_rules_block));
++	
++	temp += sizeof(struct function_and_rules_block);
++	vud_len = 2 + icaMsg_p->inputdatalength;
++	itoLe2(&vud_len, temp); 
++	
++	temp += 2;
++	if (copy_from_user(temp, icaMsg_p->inputdata, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(temp, mod_len))
++		return SEN_USER_ERROR;
++	
++	temp += mod_len;
++	memcpy(temp, &static_T6_keyBlock_hdr, sizeof(struct T6_keyBlock_hdr));
++	
++	temp += sizeof(struct T6_keyBlock_hdr);
++	memcpy(temp, &static_pvt_me_key, sizeof(struct cca_private_ext_ME));
++	key_p = (struct cca_private_ext_ME *)temp;
++	temp = key_p->pvtMESec.exponent + sizeof(key_p->pvtMESec.exponent)
++	       - mod_len;
++	if (copy_from_user(temp, icaMsg_p->b_key, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(temp, mod_len))
++		return SEN_USER_ERROR;
++	if (is_common_public_key(temp, mod_len)) {
++		if (deprecated_msg_count < 20) {
++			PRINTK("Common public key used for modex decrypt\n");
++			deprecated_msg_count++;
++			if (deprecated_msg_count == 20)
++				PRINTK("No longer issuing messages about common"
++				       " public key for modex decrypt.\n");
++		}
++		return SEN_NOT_AVAIL;
++	}
++	temp = key_p->pvtMESec.modulus + sizeof(key_p->pvtMESec.modulus)
++	       - mod_len;
++	if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(temp, mod_len))
++		return SEN_USER_ERROR;
++	
++	key_p->pubMESec.modulus_bit_len = 8 * mod_len;
++	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
++	return 0;
++}
++static int
++ICAMEX_msg_to_type6MEX_en_msg(struct ica_rsa_modexpo *icaMsg_p, int cdx,
++			      int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
++{
++	int mod_len, vud_len, exp_len, key_len;
++	int pad_len, tmp_size, total_CPRB_len, parmBlock_l, i;
++	unsigned char temp_exp[256], *exp_p, *temp;
++	struct type6_hdr *tp6Hdr_p;
++	struct CPRB *cprb_p;
++	struct cca_public_key *key_p;
++	struct T6_keyBlock_hdr *keyb_p;
++	mod_len = icaMsg_p->inputdatalength;
++	if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(temp_exp, mod_len))
++		return SEN_USER_ERROR;
++	exp_p = temp_exp;
++	for (i = 0; i < mod_len; i++)
++		if (exp_p[i])
++			break;
++	if (i >= mod_len)
++		return SEN_USER_ERROR;
++	exp_len = mod_len - i;
++	exp_p += i;
++	PDEBUG("exp_len after computation: %08x\n", exp_len);
++	tmp_size = FIXED_TYPE6_ME_EN_LEN + 2 * mod_len + exp_len;
++	total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
++	parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
++	tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
++	vud_len = 2 + mod_len;
++	memset(z90cMsg_p, 0, tmp_size);
++	
++	temp = (unsigned char *)z90cMsg_p + CALLER_HEADER;
++	memcpy(temp, &static_type6_hdr, sizeof(struct type6_hdr));
++	tp6Hdr_p = (struct type6_hdr *)temp;
++	tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
++	tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
++	memcpy(tp6Hdr_p->function_code, static_PKE_function_code,
++	       sizeof(static_PKE_function_code));
++	
++	temp += sizeof(struct type6_hdr);
++	memcpy(temp, &static_cprb, sizeof(struct CPRB));
++	cprb_p = (struct CPRB *) temp;
++	cprb_p->usage_domain[0]= (unsigned char)cdx;
++	itoLe2((int *)&(tp6Hdr_p->FromCardLen1), cprb_p->rpl_parml);
++	
++	temp += sizeof(struct CPRB);
++	memcpy(temp, &static_pke_function_and_rules,
++		 sizeof(struct function_and_rules_block));
++	
++	temp += sizeof(struct function_and_rules_block);
++	temp += 2;
++	if (copy_from_user(temp, icaMsg_p->inputdata, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(temp, mod_len))
++		return SEN_USER_ERROR;
++	
++	if ((temp[0] != 0x00) || (temp[1] != 0x02))
++		return SEN_NOT_AVAIL;
++	for (i = 2; i < mod_len; i++)
++		if (temp[i] == 0x00)
++			break;
++	if ((i < 9) || (i > (mod_len - 2)))
++		return SEN_NOT_AVAIL;
++	pad_len = i + 1;
++	vud_len = mod_len - pad_len;
++	memmove(temp, temp+pad_len, vud_len);
++	
++	temp -= 2;
++	vud_len += 2;
++	itoLe2(&vud_len, temp);
++	
++	temp += (vud_len);
++	keyb_p = (struct T6_keyBlock_hdr *)temp;
++	
++	temp += sizeof(struct T6_keyBlock_hdr);
++	memcpy(temp, &static_public_key, sizeof(static_public_key));
++	key_p = (struct cca_public_key *)temp;
++	temp = key_p->pubSec.exponent;
++	memcpy(temp, exp_p, exp_len);
++	temp += exp_len;
++	if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(temp, mod_len))
++		return SEN_USER_ERROR;
++	key_p->pubSec.modulus_bit_len = 8 * mod_len;
++	key_p->pubSec.modulus_byte_len = mod_len;
++	key_p->pubSec.exponent_len = exp_len;
++	key_p->pubSec.section_length = CALLER_HEADER + mod_len + exp_len;
++	key_len = key_p->pubSec.section_length + sizeof(struct cca_token_hdr);
++	key_p->pubHdr.token_length = key_len;
++	key_len += 4;
++	itoLe2(&key_len, keyb_p->ulen);
++	key_len += 2;
++	itoLe2(&key_len, keyb_p->blen);
++	parmBlock_l -= pad_len;
++	itoLe2(&parmBlock_l, cprb_p->req_parml);
++	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
++	return 0;
++}
++static int
++ICACRT_msg_to_type6CRT_msg(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx,
++			   int *z90cMsg_l_p, struct type6_msg *z90cMsg_p)
++{
++	int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l, short_len;
++	int long_len, pad_len, keyPartsLen, tmp_l;
++	unsigned char *tgt_p, *temp;
++	struct type6_hdr *tp6Hdr_p;
++	struct CPRB *cprb_p;
++	struct cca_token_hdr *keyHdr_p;
++	struct cca_pvt_ext_CRT_sec *pvtSec_p;
++	struct cca_public_sec *pubSec_p;
++	mod_len = icaMsg_p->inputdatalength;
++	short_len = mod_len / 2;
++	long_len = 8 + short_len;
++	keyPartsLen = 3 * long_len + 2 * short_len;
++	pad_len = (8 - (keyPartsLen % 8)) % 8;
++	keyPartsLen += pad_len + mod_len;
++	tmp_size = FIXED_TYPE6_CR_LEN + keyPartsLen + mod_len;
++	total_CPRB_len = tmp_size -  sizeof(struct type6_hdr);
++	parmBlock_l = total_CPRB_len - sizeof(struct CPRB);
++	vud_len = 2 + mod_len;	   
++	tmp_size = 4*((tmp_size + 3)/4) + CALLER_HEADER;
++	memset(z90cMsg_p, 0, tmp_size);
++	
++	tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
++	memcpy(tgt_p, &static_type6_hdr, sizeof(struct type6_hdr));
++	tp6Hdr_p = (struct type6_hdr *)tgt_p;
++	tp6Hdr_p->ToCardLen1 = 4*((total_CPRB_len+3)/4);
++	tp6Hdr_p->FromCardLen1 = RESPONSE_CPRB_SIZE;
++	
++	tgt_p += sizeof(struct type6_hdr);
++	cprb_p = (struct CPRB *) tgt_p;
++	memcpy(tgt_p, &static_cprb, sizeof(struct CPRB));
++	cprb_p->usage_domain[0]= *((unsigned char *)(&(cdx))+3);
++	itoLe2(&parmBlock_l, cprb_p->req_parml);
++	memcpy(cprb_p->rpl_parml, cprb_p->req_parml,
++	       sizeof(cprb_p->req_parml));
++	
++	tgt_p += sizeof(struct CPRB);
++	memcpy(tgt_p, &static_pkd_function_and_rules,
++	       sizeof(struct function_and_rules_block));
++	
++	tgt_p += sizeof(struct function_and_rules_block);
++	itoLe2(&vud_len, tgt_p);
++	
++	tgt_p += 2;
++	if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, mod_len))
++		return SEN_USER_ERROR;
++	
++	tgt_p += mod_len;
++	tmp_l = sizeof(struct T6_keyBlock_hdr) + sizeof(struct cca_token_hdr) +
++		sizeof(struct cca_pvt_ext_CRT_sec) + 0x0F + keyPartsLen;
++	itoLe2(&tmp_l, tgt_p);
++	
++	temp = tgt_p + 2;
++	tmp_l -= 2;
++	itoLe2(&tmp_l, temp);
++	
++	tgt_p += sizeof(struct T6_keyBlock_hdr);
++	keyHdr_p = (struct cca_token_hdr *)tgt_p;
++	keyHdr_p->token_identifier = CCA_TKN_HDR_ID_EXT;
++	tmp_l -= 4;
++	keyHdr_p->token_length = tmp_l;
++	
++	tgt_p += sizeof(struct cca_token_hdr);
++	pvtSec_p = (struct cca_pvt_ext_CRT_sec *)tgt_p;
++	pvtSec_p->section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT;
++	pvtSec_p->section_length =
++		sizeof(struct cca_pvt_ext_CRT_sec) + keyPartsLen;
++	pvtSec_p->key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL;
++	pvtSec_p->key_use_flags[0] = CCA_PVT_USAGE_ALL;
++	pvtSec_p->p_len = long_len;
++	pvtSec_p->q_len = short_len;
++	pvtSec_p->dp_len = long_len;
++	pvtSec_p->dq_len = short_len;
++	pvtSec_p->u_len = long_len;
++	pvtSec_p->mod_len = mod_len;
++	pvtSec_p->pad_len = pad_len;
++	
++	tgt_p += sizeof(struct cca_pvt_ext_CRT_sec);
++	if (copy_from_user(tgt_p, icaMsg_p->np_prime, long_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, long_len))
++		return SEN_USER_ERROR;
++	tgt_p += long_len;
++	if (copy_from_user(tgt_p, icaMsg_p->nq_prime, short_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, short_len))
++		return SEN_USER_ERROR;
++	tgt_p += short_len;
++	if (copy_from_user(tgt_p, icaMsg_p->bp_key, long_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, long_len))
++		return SEN_USER_ERROR;
++	tgt_p += long_len;
++	if (copy_from_user(tgt_p, icaMsg_p->bq_key, short_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, short_len))
++		return SEN_USER_ERROR;
++	tgt_p += short_len;
++	if (copy_from_user(tgt_p, icaMsg_p->u_mult_inv, long_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, long_len))
++		return SEN_USER_ERROR;
++	tgt_p += long_len;
++	tgt_p += pad_len;
++	memset(tgt_p, 0xFF, mod_len);
++	
++	tgt_p += mod_len;
++	memcpy(tgt_p, &static_cca_pub_sec, sizeof(struct cca_public_sec));
++	pubSec_p = (struct cca_public_sec *) tgt_p;
++	pubSec_p->modulus_bit_len = 8 * mod_len;
++	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
++	return 0;
++}
++static int
++ICAMEX_msg_to_type6MEX_msgX(struct ica_rsa_modexpo *icaMsg_p, int cdx,
++			    int *z90cMsg_l_p, struct type6_msg *z90cMsg_p,
++			    int dev_type)
++{
++	int mod_len, exp_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l;
++	int key_len, i;
++	unsigned char temp_exp[256], *tgt_p, *temp, *exp_p;
++	struct type6_hdr *tp6Hdr_p;
++	struct CPRBX *cprbx_p;
++	struct cca_public_key *key_p;
++	struct T6_keyBlock_hdrX *keyb_p;
++	mod_len = icaMsg_p->inputdatalength;
++	if (copy_from_user(temp_exp, icaMsg_p->b_key, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(temp_exp, mod_len))
++		return SEN_USER_ERROR;
++	exp_p = temp_exp;
++	for (i = 0; i < mod_len; i++)
++		if (exp_p[i])
++			break;
++	if (i >= mod_len)
++		return SEN_USER_ERROR;
++	exp_len = mod_len - i;
++	exp_p += i;
++	PDEBUG("exp_len after computation: %08x\n", exp_len);
++	tmp_size = FIXED_TYPE6_ME_EN_LENX + 2 * mod_len + exp_len;
++	total_CPRB_len = tmp_size - sizeof(struct type6_hdr);
++	parmBlock_l = total_CPRB_len - sizeof(struct CPRBX);
++	tmp_size = tmp_size + CALLER_HEADER;
++	vud_len = 2 + mod_len;
++	memset(z90cMsg_p, 0, tmp_size);
++	
++	tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
++	memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr));
++	tp6Hdr_p = (struct type6_hdr *)tgt_p;
++	tp6Hdr_p->ToCardLen1 = total_CPRB_len;
++	tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE;
++	memcpy(tp6Hdr_p->function_code, static_PKE_function_code,
++	       sizeof(static_PKE_function_code));
++	
++	tgt_p += sizeof(struct type6_hdr);
++	memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX));
++	cprbx_p = (struct CPRBX *) tgt_p;
++	cprbx_p->domain = (unsigned short)cdx;
++	cprbx_p->rpl_msgbl = RESPONSE_CPRBX_SIZE;
++	
++	tgt_p += sizeof(struct CPRBX);
++	if (dev_type == PCIXCC_MCL2)
++		memcpy(tgt_p, &static_pke_function_and_rulesX_MCL2,
++		       sizeof(struct function_and_rules_block));
++	else
++		memcpy(tgt_p, &static_pke_function_and_rulesX,
++		       sizeof(struct function_and_rules_block));
++	
++	tgt_p += sizeof(struct function_and_rules_block);
++	
++	tgt_p += 2;
++	if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
++	      return SEN_RELEASED;
++	if (is_empty(tgt_p, mod_len))
++	      return SEN_USER_ERROR;
++	
++	tgt_p -= 2;
++	*((short *)tgt_p) = (short) vud_len;
++	tgt_p += vud_len;
++	keyb_p = (struct T6_keyBlock_hdrX *)tgt_p;
++	
++	tgt_p += sizeof(struct T6_keyBlock_hdrX);
++	memcpy(tgt_p, &static_public_key, sizeof(static_public_key));
++	key_p = (struct cca_public_key *)tgt_p;
++	temp = key_p->pubSec.exponent;
++	memcpy(temp, exp_p, exp_len);
++	temp += exp_len;
++	if (copy_from_user(temp, icaMsg_p->n_modulus, mod_len))
++	      return SEN_RELEASED;
++	if (is_empty(temp, mod_len))
++	      return SEN_USER_ERROR;
++	key_p->pubSec.modulus_bit_len = 8 * mod_len;
++	key_p->pubSec.modulus_byte_len = mod_len;
++	key_p->pubSec.exponent_len = exp_len;
++	key_p->pubSec.section_length = CALLER_HEADER + mod_len + exp_len;
++	key_len = key_p->pubSec.section_length + sizeof(struct cca_token_hdr);
++	key_p->pubHdr.token_length = key_len;
++	key_len += 4;
++	keyb_p->ulen = (unsigned short)key_len;
++	key_len += 2;
++	keyb_p->blen = (unsigned short)key_len;
++	cprbx_p->req_parml = parmBlock_l;
++	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
++	return 0;
++}
++static int
++ICACRT_msg_to_type6CRT_msgX(struct ica_rsa_modexpo_crt *icaMsg_p, int cdx,
++			    int *z90cMsg_l_p, struct type6_msg *z90cMsg_p,
++			    int dev_type)
++{
++	int mod_len, vud_len, tmp_size, total_CPRB_len, parmBlock_l, short_len;
++	int long_len, pad_len, keyPartsLen, tmp_l;
++	unsigned char *tgt_p, *temp;
++	struct type6_hdr *tp6Hdr_p;
++	struct CPRBX *cprbx_p;
++	struct cca_token_hdr *keyHdr_p;
++	struct cca_pvt_ext_CRT_sec *pvtSec_p;
++	struct cca_public_sec *pubSec_p;
++	mod_len = icaMsg_p->inputdatalength;
++	short_len = mod_len / 2;
++	long_len = 8 + short_len;
++	keyPartsLen = 3 * long_len + 2 * short_len;
++	pad_len = (8 - (keyPartsLen % 8)) % 8;
++	keyPartsLen += pad_len + mod_len;
++	tmp_size = FIXED_TYPE6_CR_LENX + keyPartsLen + mod_len;
++	total_CPRB_len = tmp_size -  sizeof(struct type6_hdr);
++	parmBlock_l = total_CPRB_len - sizeof(struct CPRBX);
++	vud_len = 2 + mod_len;
++	tmp_size = tmp_size + CALLER_HEADER;
++	memset(z90cMsg_p, 0, tmp_size);
++	
++	tgt_p = (unsigned char *)z90cMsg_p + CALLER_HEADER;
++	memcpy(tgt_p, &static_type6_hdrX, sizeof(struct type6_hdr));
++	tp6Hdr_p = (struct type6_hdr *)tgt_p;
++	tp6Hdr_p->ToCardLen1 = total_CPRB_len;
++	tp6Hdr_p->FromCardLen1 = RESPONSE_CPRBX_SIZE;
++	
++	tgt_p += sizeof(struct type6_hdr);
++	cprbx_p = (struct CPRBX *) tgt_p;
++	memcpy(tgt_p, &static_cprbx, sizeof(struct CPRBX));
++	cprbx_p->domain = (unsigned short)cdx;
++	cprbx_p->req_parml = parmBlock_l;
++	cprbx_p->rpl_msgbl = parmBlock_l;
++	
++	tgt_p += sizeof(struct CPRBX);
++	if (dev_type == PCIXCC_MCL2)
++		memcpy(tgt_p, &static_pkd_function_and_rulesX_MCL2,
++		       sizeof(struct function_and_rules_block));
++	else
++		memcpy(tgt_p, &static_pkd_function_and_rulesX,
++		       sizeof(struct function_and_rules_block));
++	
++	tgt_p += sizeof(struct function_and_rules_block);
++	*((short *)tgt_p) = (short) vud_len;
++	
++	tgt_p += 2;
++	if (copy_from_user(tgt_p, icaMsg_p->inputdata, mod_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, mod_len))
++		return SEN_USER_ERROR;
++	
++	tgt_p += mod_len;
++	tmp_l = sizeof(struct T6_keyBlock_hdr) + sizeof(struct cca_token_hdr) +
++		sizeof(struct cca_pvt_ext_CRT_sec) + 0x0F + keyPartsLen;
++	*((short *)tgt_p) = (short) tmp_l;
++	temp = tgt_p + 2;
++	tmp_l -= 2;
++	*((short *)temp) = (short) tmp_l;
++	
++	tgt_p += sizeof(struct T6_keyBlock_hdr);
++	keyHdr_p = (struct cca_token_hdr *)tgt_p;
++	keyHdr_p->token_identifier = CCA_TKN_HDR_ID_EXT;
++	tmp_l -= 4;
++	keyHdr_p->token_length = tmp_l;
++	
++	tgt_p += sizeof(struct cca_token_hdr);
++	pvtSec_p = (struct cca_pvt_ext_CRT_sec *)tgt_p;
++	pvtSec_p->section_identifier = CCA_PVT_EXT_CRT_SEC_ID_PVT;
++	pvtSec_p->section_length =
++		sizeof(struct cca_pvt_ext_CRT_sec) + keyPartsLen;
++	pvtSec_p->key_format = CCA_PVT_EXT_CRT_SEC_FMT_CL;
++	pvtSec_p->key_use_flags[0] = CCA_PVT_USAGE_ALL;
++	pvtSec_p->p_len = long_len;
++	pvtSec_p->q_len = short_len;
++	pvtSec_p->dp_len = long_len;
++	pvtSec_p->dq_len = short_len;
++	pvtSec_p->u_len = long_len;
++	pvtSec_p->mod_len = mod_len;
++	pvtSec_p->pad_len = pad_len;
++	
++	tgt_p += sizeof(struct cca_pvt_ext_CRT_sec);
++	if (copy_from_user(tgt_p, icaMsg_p->np_prime, long_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, long_len))
++		return SEN_USER_ERROR;
++	tgt_p += long_len;
++	if (copy_from_user(tgt_p, icaMsg_p->nq_prime, short_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, short_len))
++		return SEN_USER_ERROR;
++	tgt_p += short_len;
++	if (copy_from_user(tgt_p, icaMsg_p->bp_key, long_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, long_len))
++		return SEN_USER_ERROR;
++	tgt_p += long_len;
++	if (copy_from_user(tgt_p, icaMsg_p->bq_key, short_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, short_len))
++		return SEN_USER_ERROR;
++	tgt_p += short_len;
++	if (copy_from_user(tgt_p, icaMsg_p->u_mult_inv, long_len))
++		return SEN_RELEASED;
++	if (is_empty(tgt_p, long_len))
++		return SEN_USER_ERROR;
++	tgt_p += long_len;
++	tgt_p += pad_len;
++	memset(tgt_p, 0xFF, mod_len);
++	
++	tgt_p += mod_len;
++	memcpy(tgt_p, &static_cca_pub_sec, sizeof(struct cca_public_sec));
++	pubSec_p = (struct cca_public_sec *) tgt_p;
++	pubSec_p->modulus_bit_len = 8 * mod_len;
++	*z90cMsg_l_p = tmp_size - CALLER_HEADER;
++	return 0;
++}
++int
++convert_request(unsigned char *buffer, int func, unsigned short function,
++		int cdx, int dev_type, int *msg_l_p, unsigned char *msg_p)
++{
++	if (dev_type == PCICA) {
++		if (func == ICARSACRT)
++			return ICACRT_msg_to_type4CRT_msg(
++				(struct ica_rsa_modexpo_crt *) buffer,
++				msg_l_p, (union type4_msg *) msg_p);
++		else
++			return ICAMEX_msg_to_type4MEX_msg(
++				(struct ica_rsa_modexpo *) buffer,
++				msg_l_p, (union type4_msg *) msg_p);
++	}
++	if (dev_type == PCICC) {
++		if (func == ICARSACRT)
++			return ICACRT_msg_to_type6CRT_msg(
++				(struct ica_rsa_modexpo_crt *) buffer,
++				cdx, msg_l_p, (struct type6_msg *)msg_p);
++		if (function == PCI_FUNC_KEY_ENCRYPT)
++			return ICAMEX_msg_to_type6MEX_en_msg(
++				(struct ica_rsa_modexpo *) buffer,
++				cdx, msg_l_p, (struct type6_msg *) msg_p);
++		else
++			return ICAMEX_msg_to_type6MEX_de_msg(
++				(struct ica_rsa_modexpo *) buffer,
++				cdx, msg_l_p, (struct type6_msg *) msg_p);
++	}
++	if ((dev_type == PCIXCC_MCL2) ||
++	    (dev_type == PCIXCC_MCL3) ||
++	    (dev_type == CEX2C)) {
++		if (func == ICARSACRT)
++			return ICACRT_msg_to_type6CRT_msgX(
++				(struct ica_rsa_modexpo_crt *) buffer,
++				cdx, msg_l_p, (struct type6_msg *) msg_p,
++				dev_type);
++		else
++			return ICAMEX_msg_to_type6MEX_msgX(
++				(struct ica_rsa_modexpo *) buffer,
++				cdx, msg_l_p, (struct type6_msg *) msg_p,
++				dev_type);
++	}
++	return 0;
++}
++int ext_bitlens_msg_count = 0; 
++static inline void
++unset_ext_bitlens(void)
++{
++	if (!ext_bitlens_msg_count) {
++		PRINTK("Unable to use coprocessors for extended bitlengths. "
++		       "Using PCICAs (if present) for extended bitlengths. "
++		       "This is not an error.\n");
++		ext_bitlens_msg_count++;
++	}
++	ext_bitlens = 0;
++}
++int
++convert_response(unsigned char *response, unsigned char *buffer,
++		 int *respbufflen_p, unsigned char *resp_buff)
++{
++	struct ica_rsa_modexpo *icaMsg_p = (struct ica_rsa_modexpo *) buffer;
++	struct type82_hdr *t82h_p = (struct type82_hdr *) response;
++	struct type84_hdr *t84h_p = (struct type84_hdr *) response;
++	struct type86_fmt2_msg *t86m_p =  (struct type86_fmt2_msg *) response;
++	int reply_code, service_rc, service_rs, src_l;
++	unsigned char *src_p, *tgt_p;
++	struct CPRB *cprb_p;
++	struct CPRBX *cprbx_p;
++	src_p = 0;
++	reply_code = 0;
++	service_rc = 0;
++	service_rs = 0;
++	src_l = 0;
++	switch (t82h_p->type) {
++	case TYPE82_RSP_CODE:
++		reply_code = t82h_p->reply_code;
++		src_p = (unsigned char *)t82h_p;
++		PRINTK("Hardware error: Type 82 Message Header: "
++		       "%02x%02x%02x%02x%02x%02x%02x%02x\n",
++		       src_p[0], src_p[1], src_p[2], src_p[3],
++		       src_p[4], src_p[5], src_p[6], src_p[7]);
++		break;
++	case TYPE84_RSP_CODE:
++		src_l = icaMsg_p->outputdatalength;
++		src_p = response + (int)t84h_p->len - src_l;
++		break;
++	case TYPE86_RSP_CODE:
++		reply_code = t86m_p->hdr.reply_code;
++		if (reply_code != 0)
++			break;
++		cprb_p = (struct CPRB *)
++			(response + sizeof(struct type86_fmt2_msg));
++		cprbx_p = (struct CPRBX *) cprb_p;
++		if (cprb_p->cprb_ver_id != 0x02) {
++			le2toI(cprb_p->ccp_rtcode, &service_rc);
++			if (service_rc != 0) {
++				le2toI(cprb_p->ccp_rscode, &service_rs);
++				if ((service_rc == 8) && (service_rs == 66))
++					PDEBUG("Bad block format on PCICC\n");
++				else if ((service_rc == 8) && (service_rs == 770)) {
++					PDEBUG("Invalid key length on PCICC\n");
++					unset_ext_bitlens();
++					return REC_USE_PCICA;
++				}
++				else if ((service_rc == 8) && (service_rs == 783)) {
++					PDEBUG("Extended bitlengths not enabled"
++					       "on PCICC\n");
++					unset_ext_bitlens();
++					return REC_USE_PCICA;
++				}
++				else
++					PRINTK("service rc/rs: %d/%d\n",
++					       service_rc, service_rs);
++				return REC_OPERAND_INV;
++			}
++			src_p = (unsigned char *)cprb_p + sizeof(struct CPRB);
++			src_p += 4; 
++			le2toI(src_p, &src_l);
++			src_l -= 2;	
++			src_p += 2;	
++		} else {
++			service_rc = (int)cprbx_p->ccp_rtcode;
++			if (service_rc != 0) {
++				service_rs = (int) cprbx_p->ccp_rscode;
++				if ((service_rc == 8) && (service_rs == 66))
++					PDEBUG("Bad block format on PCXICC\n");
++				else if ((service_rc == 8) && (service_rs == 770)) {
++					PDEBUG("Invalid key length on PCIXCC\n");
++					unset_ext_bitlens();
++					return REC_USE_PCICA;
++				}
++				else if ((service_rc == 8) && (service_rs == 783)) {
++					PDEBUG("Extended bitlengths not enabled"
++					       "on PCIXCC\n");
++					unset_ext_bitlens();
++					return REC_USE_PCICA;
++				}
++				else
++					PRINTK("service rc/rs: %d/%d\n",
++					       service_rc, service_rs);
++				return REC_OPERAND_INV;
++			}
++			src_p = (unsigned char *)
++				cprbx_p + sizeof(struct CPRBX);
++			src_p += 4; 
++			src_l = (int)(*((short *) src_p));
++			src_l -= 2;	
++			src_p += 2;	
++		}
++		break;
++	default:
++		return REC_BAD_MESSAGE; 
++	}
++	
++	if (reply_code)
++		switch (reply_code) {
++		case REPLY_ERROR_OPERAND_INVALID:	
++			return REC_OPERAND_INV;
++		case REPLY_ERROR_OPERAND_SIZE:		
++			return REC_OPERAND_SIZE;
++		case REPLY_ERROR_EVEN_MOD_IN_OPND:	
++			return REC_EVEN_MOD;
++		case REPLY_ERROR_MESSAGE_TYPE:
++			return WRONG_DEVICE_TYPE;
++		case REPLY_ERROR_TRANSPORT_FAIL: 
++			PRINTKW("Transport failed (APFS = %02X%02X%02X%02X)\n",
++				t86m_p->apfs[0], t86m_p->apfs[1],
++				t86m_p->apfs[2], t86m_p->apfs[3]);
++			return REC_HARDWAR_ERR;
++		default:
++			PRINTKW("reply code = %d\n", reply_code);
++			return REC_HARDWAR_ERR;
++		}
++	if (service_rc != 0)
++		return REC_OPERAND_INV;
++	if ((src_l > icaMsg_p->outputdatalength) ||
++	    (src_l > RESPBUFFSIZE) ||
++	    (src_l <= 0))
++		return REC_OPERAND_SIZE;
++	PDEBUG("Length returned = %d\n", src_l);
++	
++	tgt_p = resp_buff + icaMsg_p->outputdatalength - src_l;
++	memcpy(tgt_p, src_p, src_l);
++	
++	if ((t82h_p->type == TYPE86_RSP_CODE) && (resp_buff < tgt_p)) {
++		memset(resp_buff, 0, icaMsg_p->outputdatalength - src_l);
++		if (pad_msg(resp_buff, icaMsg_p->outputdatalength, src_l))
++			return REC_INVALID_PAD;
++	}
++	*respbufflen_p = icaMsg_p->outputdatalength;
++	if (*respbufflen_p == 0)
++		PRINTK("Zero *respbufflen_p\n");
++	return 0;
++}
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/misc/z90main.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/misc/z90main.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,3588 @@
++/*
++ *  linux/drivers/s390/misc/z90main.c
++ *
++ *  z90crypt 1.3.2
++ *
++ *  Copyright (C)  2001, 2004 IBM Corporation
++ *  Author(s): Robert Burroughs (burrough at us.ibm.com)
++ *             Eric Rossman (edrossma at us.ibm.com)
++ *
++ *  Hotplug & misc device support: Jochen Roehrig (roehrig 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 <asm/uaccess.h>       // copy_(from|to)_user
++#include <linux/compiler.h>
++#include <linux/delay.h>       // mdelay
++#include <linux/init.h>
++#include <linux/interrupt.h>   // for tasklets
++#include <asm/ioctl32.h>
++#include <linux/kmod.h>
++#include <linux/module.h>
++#include <linux/proc_fs.h>
++#include <linux/version.h>
++#include "z90crypt.h"
++#include "z90common.h"
++#ifndef Z90CRYPT_USE_HOTPLUG
++#include <linux/miscdevice.h>
++#endif
++
++#define VERSION_CODE(vers, rel, seq) (((vers)<<16) | ((rel)<<8) | (seq))
++#if LINUX_VERSION_CODE < VERSION_CODE(2,4,0) /* version < 2.4 */
++#  error "This kernel is too old: not supported"
++#endif
++#if LINUX_VERSION_CODE > VERSION_CODE(2,7,0) /* version > 2.6 */
++#  error "This kernel is too recent: not supported by this file"
++#endif
++
++#define VERSION_Z90MAIN_C "$Revision: 1.9.4.13 $"
++
++static char z90cmain_version[] __initdata =
++	"z90main.o (" VERSION_Z90MAIN_C "/"
++                      VERSION_Z90COMMON_H "/" VERSION_Z90CRYPT_H ")";
++
++extern char z90chardware_version[];
++
++/**
++ * Defaults that may be modified.
++ */
++
++#ifndef Z90CRYPT_USE_HOTPLUG
++/**
++ * You can specify a different minor at compile time.
++ */
++#ifndef Z90CRYPT_MINOR
++#define Z90CRYPT_MINOR	MISC_DYNAMIC_MINOR
++#endif
++#else
++/**
++ * You can specify a different major at compile time.
++ */
++#ifndef Z90CRYPT_MAJOR
++#define Z90CRYPT_MAJOR	0
++#endif
++#endif
++
++/**
++ * You can specify a different domain at compile time or on the insmod
++ * command line.
++ */
++#ifndef DOMAIN_INDEX
++#define DOMAIN_INDEX	-1
++#endif
++
++/**
++ * This is the name under which the device is registered in /proc/modules.
++ */
++#define REG_NAME	"z90crypt"
++
++/**
++ * Cleanup should run every CLEANUPTIME seconds and should clean up requests
++ * older than CLEANUPTIME seconds in the past.
++ */
++#ifndef CLEANUPTIME
++#define CLEANUPTIME 15
++#endif
++
++/**
++ * Config should run every CONFIGTIME seconds
++ */
++#ifndef CONFIGTIME
++#define CONFIGTIME 30
++#endif
++
++/**
++ * The first execution of the config task should take place
++ * immediately after initialization
++ */
++#ifndef INITIAL_CONFIGTIME
++#define INITIAL_CONFIGTIME 1
++#endif
++
++/**
++ * Reader should run every READERTIME milliseconds
++ * With the 100Hz patch for s390, z90crypt can lock the system solid while
++ * under heavy load. We'll try to avoid that.
++ */
++#ifndef READERTIME
++#if HZ > 1000
++#define READERTIME 2
++#else
++#define READERTIME 10
++#endif
++#endif
++
++/**
++ * turn long device array index into device pointer
++ */
++#define LONG2DEVPTR(ndx) (z90crypt.device_p[(ndx)])
++
++/**
++ * turn short device array index into long device array index
++ */
++#define SHRT2LONG(ndx) (z90crypt.overall_device_x.device_index[(ndx)])
++
++/**
++ * turn short device array index into device pointer
++ */
++#define SHRT2DEVPTR(ndx) LONG2DEVPTR(SHRT2LONG(ndx))
++
++/**
++ * Status for a work-element
++ */
++#define STAT_DEFAULT	0x00 // request has not been processed
++
++#define STAT_ROUTED	0x80 // bit 7: requests get routed to specific device
++			     //	       else, device is determined each write
++#define STAT_FAILED	0x40 // bit 6: this bit is set if the request failed
++			     //	       before being sent to the hardware.
++#define STAT_WRITTEN	0x30 // bits 5-4: work to be done, not sent to device
++//			0x20 // UNUSED state
++#define STAT_READPEND	0x10 // bits 5-4: work done, we're returning data now
++#define STAT_NOWORK	0x00 // bits off: no work on any queue
++#define STAT_RDWRMASK	0x30 // mask for bits 5-4
++
++/**
++ * Macros to check the status RDWRMASK
++ */
++#define CHK_RDWRMASK(statbyte) ((statbyte) & STAT_RDWRMASK)
++#define SET_RDWRMASK(statbyte, newval) \
++	{(statbyte) &= ~STAT_RDWRMASK; (statbyte) |= newval;}
++
++/**
++ * Audit Trail.	 Progress of a Work element
++ * audit[0]: Unless noted otherwise, these bits are all set by the process
++ */
++#define FP_COPYFROM	0x80 // Caller's buffer has been copied to work element
++#define FP_BUFFREQ	0x40 // Low Level buffer requested
++#define FP_BUFFGOT	0x20 // Low Level buffer obtained
++#define FP_SENT		0x10 // Work element sent to a crypto device
++			     // (may be set by process or by reader task)
++#define FP_PENDING	0x08 // Work element placed on pending queue
++			     // (may be set by process or by reader task)
++#define FP_REQUEST	0x04 // Work element placed on request queue
++#define FP_ASLEEP	0x02 // Work element about to sleep
++#define FP_AWAKE	0x01 // Work element has been awakened
++
++/**
++ * audit[1]: These bits are set by the reader task and/or the cleanup task
++ */
++#define FP_NOTPENDING	  0x80 // Work element removed from pending queue
++#define FP_AWAKENING	  0x40 // Caller about to be awakened
++#define FP_TIMEDOUT	  0x20 // Caller timed out
++#define FP_RESPSIZESET	  0x10 // Response size copied to work element
++#define FP_RESPADDRCOPIED 0x08 // Response address copied to work element
++#define FP_RESPBUFFCOPIED 0x04 // Response buffer copied to work element
++#define FP_REMREQUEST	  0x02 // Work element removed from request queue
++#define FP_SIGNALED	  0x01 // Work element was awakened by a signal
++
++/**
++ * audit[2]: unused
++ */
++
++/**
++ * state of the file handle in private_data.status
++ */
++#define STAT_OPEN 0
++#define STAT_CLOSED 1
++
++/**
++ * PID() expands to the process ID of the current process
++ */
++#define PID() (current->pid)
++
++/**
++ * Selected Constants.	The number of APs and the number of devices
++ */
++#ifndef Z90CRYPT_NUM_APS
++#define Z90CRYPT_NUM_APS 64
++#endif
++#ifndef Z90CRYPT_NUM_DEVS
++#define Z90CRYPT_NUM_DEVS Z90CRYPT_NUM_APS
++#endif
++
++/**
++ * Buffer size for receiving responses. The maximum Response Size
++ * is actually the maximum request size, since in an error condition
++ * the request itself may be returned unchanged.
++ */
++#define MAX_RESPONSE_SIZE 0x0000077C
++
++/**
++ * A count and status-byte mask
++ */
++struct status {
++	int	      st_count;		    // # of enabled devices
++	int	      disabled_count;	    // # of disabled devices
++	int	      user_disabled_count;  // # of devices disabled via proc fs
++	unsigned char st_mask[Z90CRYPT_NUM_APS]; // current status mask
++};
++
++/**
++ * The array of device indexes is a mechanism for fast indexing into
++ * a long (and sparse) array.  For instance, if APs 3, 9 and 47 are
++ * installed, z90CDeviceIndex[0] is 3, z90CDeviceIndex[1] is 9, and
++ * z90CDeviceIndex[2] is 47.
++ */
++struct device_x {
++	int device_index[Z90CRYPT_NUM_DEVS];
++};
++
++/**
++ * All devices are arranged in a single array: 64 APs
++ */
++struct device {
++	int		 dev_type;	    // PCICA, PCICC, PCIXCC_MCL2,
++					    // PCIXCC_MCL3, CEX2C
++	enum devstat	 dev_stat;	    // current device status
++	int		 dev_self_x;	    // Index in array
++	int		 disabled;	    // Set when device is in error
++	int		 user_disabled;	    // Set when device is disabled by user
++	int		 dev_q_depth;	    // q depth
++	unsigned char *	 dev_resp_p;	    // Response buffer address
++	int		 dev_resp_l;	    // Response Buffer length
++	int		 dev_caller_count;  // Number of callers
++	int		 dev_total_req_cnt; // # requests for device since load
++	struct list_head dev_caller_list;   // List of callers
++};
++
++/**
++ * There's a struct status and a struct device_x for each device type.
++ */
++struct hdware_block {
++	struct status	hdware_mask;
++	struct status	type_mask[Z90CRYPT_NUM_TYPES];
++	struct device_x type_x_addr[Z90CRYPT_NUM_TYPES];
++	unsigned char	device_type_array[Z90CRYPT_NUM_APS];
++};
++
++/**
++ * z90crypt is the topmost data structure in the hierarchy.
++ */
++struct z90crypt {
++	int		     max_count;		// Nr of possible crypto devices
++	struct status	     mask;
++	int		     q_depth_array[Z90CRYPT_NUM_DEVS];
++	int		     dev_type_array[Z90CRYPT_NUM_DEVS];
++	struct device_x	     overall_device_x;	// array device indexes
++	struct device *	     device_p[Z90CRYPT_NUM_DEVS];
++	int		     terminating;
++	int		     domain_established;// TRUE:  domain has been found
++	int		     cdx;		// Crypto Domain Index
++	int		     len;		// Length of this data structure
++	struct hdware_block *hdware_info;
++};
++
++/**
++ * An array of these structures is pointed to from dev_caller
++ * The length of the array depends on the device type. For APs,
++ * there are 8.
++ *
++ * The caller buffer is allocated to the user at OPEN. At WRITE,
++ * it contains the request; at READ, the response. The function
++ * send_to_crypto_device converts the request to device-dependent
++ * form and use the caller's OPEN-allocated buffer for the response.
++ */
++struct caller {
++	int		 caller_buf_l;		 // length of original request
++	unsigned char *	 caller_buf_p;		 // Original request on WRITE
++	int		 caller_dev_dep_req_l;	 // len device dependent request
++	unsigned char *	 caller_dev_dep_req_p;	 // Device dependent form
++	unsigned char	 caller_id[8];		 // caller-supplied message id
++	struct list_head caller_liste;
++	unsigned char	 caller_dev_dep_req[MAX_RESPONSE_SIZE];
++};
++
++/**
++ * Function prototypes from z90hardware.c
++ */
++enum hdstat query_online(int, int, int, int *, int *);
++enum devstat reset_device(int, int, int);
++enum devstat send_to_AP(int, int, int, unsigned char *);
++enum devstat receive_from_AP(int, int, int, unsigned char *, unsigned char *);
++int convert_request(unsigned char *, int, short, int, int, int *,
++		    unsigned char *);
++int convert_response(unsigned char *, unsigned char *, int *, unsigned char *);
++
++/**
++ * Low level function prototypes
++ */
++static int create_z90crypt(int *);
++static int refresh_z90crypt(int *);
++static int find_crypto_devices(struct status *);
++static int create_crypto_device(int);
++static int destroy_crypto_device(int);
++static void destroy_z90crypt(void);
++static int refresh_index_array(struct status *, struct device_x *);
++static int probe_device_type(struct device *);
++static int probe_PCIXCC_type(struct device *);
++
++/**
++ * proc fs definitions
++ */
++static struct proc_dir_entry *z90crypt_entry;
++
++/**
++ * data structures
++ */
++
++/**
++ * work_element.opener points back to this structure
++ */
++struct priv_data {
++	pid_t	opener_pid;
++	unsigned char	status;		// 0: open  1: closed
++};
++
++/**
++ * A work element is allocated for each request
++ */
++struct work_element {
++	struct priv_data *priv_data;
++	pid_t		  pid;
++	int		  devindex;	  // index of device processing this w_e
++					  // (If request did not specify device,
++					  // -1 until placed onto a queue)
++	int		  devtype;
++	struct list_head  liste;	  // used for requestq and pendingq
++	char		  buffer[128];	  // local copy of user request
++	int		  buff_size;	  // size of the buffer for the request
++	char		  resp_buff[RESPBUFFSIZE];
++	int		  resp_buff_size;
++	char __user *	  resp_addr;	  // address of response in user space
++	unsigned int	  funccode;	  // function code of request
++	wait_queue_head_t waitq;
++	unsigned long	  requestsent;	  // time at which the request was sent
++	atomic_t	  alarmrung;	  // wake-up signal
++	unsigned char	  caller_id[8];	  // pid + counter, for this w_e
++	unsigned char	  status[1];	  // bits to mark status of the request
++	unsigned char	  audit[3];	  // record of work element's progress
++	unsigned char *	  requestptr;	  // address of request buffer
++	int		  retcode;	  // return code of request
++};
++
++/**
++ * High level function prototypes
++ */
++static int z90crypt_open(struct inode *, struct file *);
++static int z90crypt_release(struct inode *, struct file *);
++static ssize_t z90crypt_read(struct file *, char __user *, size_t, loff_t *);
++static ssize_t z90crypt_write(struct file *, const char __user *,
++							size_t, loff_t *);
++static int z90crypt_ioctl(struct inode *, struct file *,
++			  unsigned int, unsigned long);
++
++static void z90crypt_reader_task(unsigned long);
++static void z90crypt_schedule_reader_task(unsigned long);
++static void z90crypt_config_task(unsigned long);
++static void z90crypt_cleanup_task(unsigned long);
++
++static int z90crypt_status(char *, char **, off_t, int, int *, void *);
++static int z90crypt_status_write(struct file *, const char __user *,
++				 unsigned long, void *);
++
++/**
++ * Hotplug support
++ */
++
++#ifdef Z90CRYPT_USE_HOTPLUG
++#define Z90CRYPT_HOTPLUG_ADD	 1
++#define Z90CRYPT_HOTPLUG_REMOVE	 2
++
++static void z90crypt_hotplug_event(int, int, int);
++#endif
++
++/**
++ * Storage allocated at initialization and used throughout the life of
++ * this insmod
++ */
++#ifdef Z90CRYPT_USE_HOTPLUG
++static int z90crypt_major = Z90CRYPT_MAJOR;
++#endif
++
++static int domain = DOMAIN_INDEX;
++static struct z90crypt z90crypt;
++static int quiesce_z90crypt;
++static spinlock_t queuespinlock;
++static struct list_head request_list;
++static int requestq_count;
++static struct list_head pending_list;
++static int pendingq_count;
++
++static struct tasklet_struct reader_tasklet;
++static struct timer_list reader_timer;
++static struct timer_list config_timer;
++static struct timer_list cleanup_timer;
++static atomic_t total_open;
++static atomic_t z90crypt_step;
++
++static struct file_operations z90crypt_fops = {
++	.owner	 = THIS_MODULE,
++	.read	 = z90crypt_read,
++	.write	 = z90crypt_write,
++	.ioctl	 = z90crypt_ioctl,
++	.open	 = z90crypt_open,
++	.release = z90crypt_release
++};
++
++#ifndef Z90CRYPT_USE_HOTPLUG
++static struct miscdevice z90crypt_misc_device = {
++	.minor	    = Z90CRYPT_MINOR,
++	.name	    = DEV_NAME,
++	.fops	    = &z90crypt_fops,
++};
++#endif
++
++/**
++ * Documentation values.
++ */
++MODULE_AUTHOR("zSeries Linux Crypto Team: Robert H. Burroughs, Eric D. Rossman"
++	      "and Jochen Roehrig");
++MODULE_DESCRIPTION("zSeries Linux Cryptographic Coprocessor device driver, "
++		   "Copyright 2001, 2004 IBM Corporation");
++MODULE_LICENSE("GPL");
++MODULE_PARM(domain, "i");
++MODULE_PARM_DESC(domain, "domain index for device");
++
++#ifdef CONFIG_S390_SUPPORT
++/**
++ * Borrowed from 2.6 kernel
++ * - compat_uptr_t
++ * - compat_ptr()
++ * - compat_alloc_user_space()
++ */
++/**
++ * A pointer passed in from user mode. This should not
++ * be used for syscall parameters, just declare them
++ * as pointers because the syscall entry code will have
++ * appropriately comverted them already.
++ */
++typedef	u32	compat_uptr_t;
++
++static inline void __user *compat_ptr(compat_uptr_t uptr)
++{
++	return (void __user *)(unsigned long)(uptr & 0x7fffffffUL);
++}
++
++static inline void __user *compat_alloc_user_space(long len)
++{
++	unsigned long stack;
++
++	stack = KSTK_ESP(current);
++	stack &= 0x7fffffffUL;
++	return (void __user *) (stack - len);
++}
++
++/**
++ * ioctl32 conversion routines
++ */
++struct ica_rsa_modexpo_32 { // For 32-bit callers
++	compat_uptr_t	inputdata;
++	unsigned int	inputdatalength;
++	compat_uptr_t	outputdata;
++	unsigned int	outputdatalength;
++	compat_uptr_t	b_key;
++	compat_uptr_t	n_modulus;
++};
++
++static int
++trans_modexpo32(unsigned int fd, unsigned int cmd, unsigned long arg,
++		struct file *file)
++{
++	struct ica_rsa_modexpo_32 __user *mex32u = compat_ptr(arg);
++	struct ica_rsa_modexpo_32  mex32k;
++	struct ica_rsa_modexpo __user *mex64;
++	int ret = 0;
++	unsigned int i;
++
++	if (!access_ok(VERIFY_WRITE, mex32u, sizeof(struct ica_rsa_modexpo_32)))
++		return -EFAULT;
++	mex64 = compat_alloc_user_space(sizeof(struct ica_rsa_modexpo));
++	if (!access_ok(VERIFY_WRITE, mex64, sizeof(struct ica_rsa_modexpo)))
++		return -EFAULT;
++	if (copy_from_user(&mex32k, mex32u, sizeof(struct ica_rsa_modexpo_32)))
++		return -EFAULT;
++	if (__put_user(compat_ptr(mex32k.inputdata), &mex64->inputdata)   ||
++	    __put_user(mex32k.inputdatalength, &mex64->inputdatalength)   ||
++	    __put_user(compat_ptr(mex32k.outputdata), &mex64->outputdata) ||
++	    __put_user(mex32k.outputdatalength, &mex64->outputdatalength) ||
++	    __put_user(compat_ptr(mex32k.b_key), &mex64->b_key)           ||
++	    __put_user(compat_ptr(mex32k.n_modulus), &mex64->n_modulus))
++		return -EFAULT;
++	ret = sys_ioctl(fd, cmd, (unsigned long)mex64);
++	if (!ret)
++		if (__get_user(i, &mex64->outputdatalength) ||
++		    __put_user(i, &mex32u->outputdatalength))
++			ret = -EFAULT;
++	return ret;
++}
++
++struct ica_rsa_modexpo_crt_32 { // For 32-bit callers
++	compat_uptr_t	inputdata;
++	unsigned int	inputdatalength;
++	compat_uptr_t	outputdata;
++	unsigned int	outputdatalength;
++	compat_uptr_t	bp_key;
++	compat_uptr_t	bq_key;
++	compat_uptr_t	np_prime;
++	compat_uptr_t	nq_prime;
++	compat_uptr_t	u_mult_inv;
++};
++
++static int
++trans_modexpo_crt32(unsigned int fd, unsigned int cmd, unsigned long arg,
++		    struct file *file)
++{
++	struct ica_rsa_modexpo_crt_32 __user *crt32u = compat_ptr(arg);
++	struct ica_rsa_modexpo_crt_32  crt32k;
++	struct ica_rsa_modexpo_crt __user *crt64;
++	int ret = 0;
++	unsigned int i;
++
++	if (!access_ok(VERIFY_WRITE, crt32u,
++		       sizeof(struct ica_rsa_modexpo_crt_32)))
++		return -EFAULT;
++	crt64 = compat_alloc_user_space(sizeof(struct ica_rsa_modexpo_crt));
++	if (!access_ok(VERIFY_WRITE, crt64, sizeof(struct ica_rsa_modexpo_crt)))
++		return -EFAULT;
++	if (copy_from_user(&crt32k, crt32u,
++			   sizeof(struct ica_rsa_modexpo_crt_32)))
++		return -EFAULT;
++	if (__put_user(compat_ptr(crt32k.inputdata), &crt64->inputdata)   ||
++	    __put_user(crt32k.inputdatalength, &crt64->inputdatalength)   ||
++	    __put_user(compat_ptr(crt32k.outputdata), &crt64->outputdata) ||
++	    __put_user(crt32k.outputdatalength, &crt64->outputdatalength) ||
++	    __put_user(compat_ptr(crt32k.bp_key), &crt64->bp_key)         ||
++	    __put_user(compat_ptr(crt32k.bq_key), &crt64->bq_key)         ||
++	    __put_user(compat_ptr(crt32k.np_prime), &crt64->np_prime)     ||
++	    __put_user(compat_ptr(crt32k.nq_prime), &crt64->nq_prime)     ||
++	    __put_user(compat_ptr(crt32k.u_mult_inv), &crt64->u_mult_inv))
++		ret = -EFAULT;
++	if (!ret)
++		ret = sys_ioctl(fd, cmd, (unsigned long)crt64);
++	if (!ret)
++		if (__get_user(i, &crt64->outputdatalength) ||
++		    __put_user(i, &crt32u->outputdatalength))
++			ret = -EFAULT;
++	return ret;
++}
++
++static int compatible_ioctls[] = {
++	ICAZ90STATUS, Z90QUIESCE, Z90STAT_TOTALCOUNT, Z90STAT_PCICACOUNT,
++	Z90STAT_PCICCCOUNT, Z90STAT_PCIXCCCOUNT, Z90STAT_PCIXCCMCL2COUNT,
++	Z90STAT_PCIXCCMCL3COUNT, Z90STAT_CEX2CCOUNT, Z90STAT_REQUESTQ_COUNT,
++	Z90STAT_PENDINGQ_COUNT, Z90STAT_TOTALOPEN_COUNT, Z90STAT_DOMAIN_INDEX,
++	Z90STAT_STATUS_MASK, Z90STAT_QDEPTH_MASK, Z90STAT_PERDEV_REQCNT,
++};
++
++static void z90_unregister_ioctl32s(void)
++{
++	int i;
++
++	unregister_ioctl32_conversion(ICARSAMODEXPO);
++	unregister_ioctl32_conversion(ICARSACRT);
++
++	for(i = 0; i < ARRAY_SIZE(compatible_ioctls); i++)
++		unregister_ioctl32_conversion(compatible_ioctls[i]);
++}
++
++static int z90_register_ioctl32s(void)
++{
++	int result, i;
++
++	result = register_ioctl32_conversion(ICARSAMODEXPO, trans_modexpo32);
++	if (result == -EBUSY) {
++		unregister_ioctl32_conversion(ICARSAMODEXPO);
++		result = register_ioctl32_conversion(ICARSAMODEXPO,
++						     trans_modexpo32);
++	}
++	if (result)
++		return result;
++	result = register_ioctl32_conversion(ICARSACRT, trans_modexpo_crt32);
++	if (result == -EBUSY) {
++		unregister_ioctl32_conversion(ICARSACRT);
++		result = register_ioctl32_conversion(ICARSACRT,
++						     trans_modexpo_crt32);
++	}
++	if (result)
++		return result;
++
++	for(i = 0; i < ARRAY_SIZE(compatible_ioctls); i++) {
++		result = register_ioctl32_conversion(compatible_ioctls[i],
++						     (void *) sys_ioctl);
++		if (result == -EBUSY) {
++			unregister_ioctl32_conversion(compatible_ioctls[i]);
++			result = register_ioctl32_conversion(
++							compatible_ioctls[i],
++							(void *) sys_ioctl);
++		}
++		if (result)
++			return result;
++	}
++	return 0;
++}
++#else // !CONFIG_COMPAT
++static inline void z90_unregister_ioctl32s(void)
++{
++}
++
++static inline int z90_register_ioctl32s(void)
++{
++	return 0;
++}
++#endif
++
++/**
++ * The module initialization code.
++ */
++static int __init
++z90crypt_init_module(void)
++{
++	int result, nresult;
++	struct proc_dir_entry *entry;
++
++	PDEBUG("PID %d\n", PID());
++
++	if ((domain < -1) || (domain > 15)) {
++		PRINTKW("Invalid param: domain = %d.  Not loading.\n", domain);
++		return -EINVAL;
++	}
++
++#ifndef Z90CRYPT_USE_HOTPLUG
++	/* Register as misc device with given minor (or get a dynamic one). */
++	result = misc_register(&z90crypt_misc_device);
++	if (result < 0) {
++		PRINTKW(KERN_ERR "misc_register (minor %d) failed with %d\n",
++			z90crypt_misc_device.minor, result);
++		return result;
++	}
++#else
++	/* Register the major (or get a dynamic one). */
++	result = register_chrdev(z90crypt_major, REG_NAME, &z90crypt_fops);
++	if (result < 0) {
++		PRINTKW("register_chrdev (major %d) failed with %d.\n",
++			z90crypt_major, result);
++		return result;
++	}
++
++	if (z90crypt_major == 0)
++		z90crypt_major = result;
++#endif
++
++	PDEBUG("Registered " DEV_NAME " with result %d\n", result);
++
++	result = create_z90crypt(&domain);
++	if (result != 0) {
++		PRINTKW("create_z90crypt (domain index %d) failed with %d.\n",
++			domain, result);
++		result = -ENOMEM;
++		goto init_module_cleanup;
++	}
++
++	if (result == 0) {
++		PRINTKN("Version %d.%d.%d loaded, built on %s %s\n",
++			z90crypt_VERSION, z90crypt_RELEASE, z90crypt_VARIANT,
++			__DATE__, __TIME__);
++		PRINTKN("%s\n", z90cmain_version);
++		PRINTKN("%s\n", z90chardware_version);
++		PDEBUG("create_z90crypt (domain index %d) successful.\n",
++		       domain);
++	} else
++		PRINTK("No devices at startup\n");
++
++#ifdef Z90CRYPT_USE_HOTPLUG
++	/* generate hotplug event for device node generation */
++	z90crypt_hotplug_event(z90crypt_major, 0, Z90CRYPT_HOTPLUG_ADD);
++#endif
++
++	/* Initialize globals. */
++	spin_lock_init(&queuespinlock);
++
++	INIT_LIST_HEAD(&pending_list);
++	pendingq_count = 0;
++
++	INIT_LIST_HEAD(&request_list);
++	requestq_count = 0;
++
++	quiesce_z90crypt = 0;
++
++	atomic_set(&total_open, 0);
++	atomic_set(&z90crypt_step, 0);
++
++	/* Set up the cleanup task. */
++	init_timer(&cleanup_timer);
++	cleanup_timer.function = z90crypt_cleanup_task;
++	cleanup_timer.data = 0;
++	cleanup_timer.expires = jiffies + (CLEANUPTIME * HZ);
++	add_timer(&cleanup_timer);
++
++	/* Set up the proc file system */
++	entry = create_proc_entry("driver/z90crypt", 0644, 0);
++	if (entry) {
++		entry->nlink = 1;
++		entry->data = 0;
++		entry->read_proc = z90crypt_status;
++		entry->write_proc = z90crypt_status_write;
++	}
++	else
++		PRINTK("Couldn't create z90crypt proc entry\n");
++	z90crypt_entry = entry;
++
++	/* Set up the configuration task. */
++	init_timer(&config_timer);
++	config_timer.function = z90crypt_config_task;
++	config_timer.data = 0;
++	config_timer.expires = jiffies + (INITIAL_CONFIGTIME * HZ);
++	add_timer(&config_timer);
++
++	/* Set up the reader task */
++	tasklet_init(&reader_tasklet, z90crypt_reader_task, 0);
++	init_timer(&reader_timer);
++	reader_timer.function = z90crypt_schedule_reader_task;
++	reader_timer.data = 0;
++	reader_timer.expires = jiffies + (READERTIME * HZ / 1000);
++	add_timer(&reader_timer);
++
++	if ((result = z90_register_ioctl32s()))
++		goto init_module_cleanup;
++
++	return 0; // success
++
++init_module_cleanup:
++	z90_unregister_ioctl32s();
++
++#ifndef Z90CRYPT_USE_HOTPLUG
++	if ((nresult = misc_deregister(&z90crypt_misc_device)))
++		PRINTK("misc_deregister failed with %d.\n", nresult);
++	else
++		PDEBUG("misc_deregister successful.\n");
++#else
++	if ((nresult = unregister_chrdev(z90crypt_major, REG_NAME)))
++		PRINTK("unregister_chrdev failed with %d.\n", nresult);
++	else
++		PDEBUG("unregister_chrdev successful.\n");
++#endif
++
++	return result; // failure
++}
++
++/**
++ * The module termination code
++ */
++static void __exit
++z90crypt_cleanup_module(void)
++{
++	int nresult;
++
++	PDEBUG("PID %d\n", PID());
++
++	z90_unregister_ioctl32s();
++
++	remove_proc_entry("driver/z90crypt", 0);
++
++#ifndef Z90CRYPT_USE_HOTPLUG
++	if ((nresult = misc_deregister(&z90crypt_misc_device)))
++		PRINTK("misc_deregister failed with %d.\n", nresult);
++	else
++		PDEBUG("misc_deregister successful.\n");
++#else
++	z90crypt_hotplug_event(z90crypt_major, 0, Z90CRYPT_HOTPLUG_REMOVE);
++
++	if ((nresult = unregister_chrdev(z90crypt_major, REG_NAME)))
++		PRINTK("unregister_chrdev failed with %d.\n", nresult);
++	else
++		PDEBUG("unregister_chrdev successful.\n");
++#endif
++
++	/* Remove the tasks */
++	tasklet_kill(&reader_tasklet);
++	del_timer(&reader_timer);
++	del_timer(&config_timer);
++	del_timer(&cleanup_timer);
++
++	destroy_z90crypt();
++
++	PRINTKN("Unloaded.\n");
++}
++
++/**
++ * Functions running under a process id
++ *
++ * The I/O functions:
++ *     z90crypt_open
++ *     z90crypt_release
++ *     z90crypt_read
++ *     z90crypt_write
++ *     z90crypt_ioctl
++ *     z90crypt_status
++ *     z90crypt_status_write
++ *	 disable_card
++ *	 enable_card
++ *	 scan_char
++ *	 scan_string
++ *
++ * Helper functions:
++ *     z90crypt_rsa
++ *	 z90crypt_prepare
++ *	 z90crypt_send
++ *	 z90crypt_process_results
++ *
++ */
++static int
++z90crypt_open(struct inode *inode, struct file *filp)
++{
++	struct priv_data *private_data_p;
++
++	if (quiesce_z90crypt)
++		return -EQUIESCE;
++
++	private_data_p = kmalloc(sizeof(struct priv_data), GFP_KERNEL);
++	if (!private_data_p) {
++		PRINTK("Memory allocate failed\n");
++		return -ENOMEM;
++	}
++
++	memset((void *)private_data_p, 0, sizeof(struct priv_data));
++	private_data_p->status = STAT_OPEN;
++	private_data_p->opener_pid = PID();
++	filp->private_data = private_data_p;
++	atomic_inc(&total_open);
++
++	return 0;
++}
++
++static int
++z90crypt_release(struct inode *inode, struct file *filp)
++{
++	struct priv_data *private_data_p = filp->private_data;
++
++	PDEBUG("PID %d (filp %p)\n", PID(), filp);
++
++	private_data_p->status = STAT_CLOSED;
++	memset(private_data_p, 0, sizeof(struct priv_data));
++	kfree(private_data_p);
++	atomic_dec(&total_open);
++
++	return 0;
++}
++
++/*
++ * there are two read functions, of which compile options will choose one
++ * without USE_GET_RANDOM_BYTES
++ *   => read() always returns -EPERM;
++ * otherwise
++ *   => read() uses get_random_bytes() kernel function
++ */
++#ifndef USE_GET_RANDOM_BYTES
++/**
++ * z90crypt_read will not be supported beyond z90crypt 1.3.1
++ */
++static ssize_t
++z90crypt_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
++{
++	PDEBUG("filp %p (PID %d)\n", filp, PID());
++	return -EPERM;
++}
++#else // we want to use get_random_bytes
++/**
++ * read() just returns a string of random bytes.  Since we have no way
++ * to generate these cryptographically, we just execute get_random_bytes
++ * for the length specified.
++ */
++#include <linux/random.h>
++static ssize_t
++z90crypt_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
++{
++	unsigned char *temp_buff;
++
++	PDEBUG("filp %p (PID %d)\n", filp, PID());
++
++	if (quiesce_z90crypt)
++		return -EQUIESCE;
++	if (count < 0) {
++		PRINTK("Requested random byte count negative: %ld\n", count);
++		return -EINVAL;
++	}
++	if (count > RESPBUFFSIZE) {
++		PDEBUG("count[%d] > RESPBUFFSIZE", count);
++		return -EINVAL;
++	}
++	if (count == 0)
++		return 0;
++	temp_buff = kmalloc(RESPBUFFSIZE, GFP_KERNEL);
++	if (!temp_buff) {
++		PRINTK("Memory allocate failed\n");
++		return -ENOMEM;
++	}
++	get_random_bytes(temp_buff, count);
++
++	if (copy_to_user(buf, temp_buff, count) != 0) {
++		kfree(temp_buff);
++		return -EFAULT;
++	}
++	kfree(temp_buff);
++	return count;
++}
++#endif
++
++/**
++ * Write is is not allowed
++ */
++static ssize_t
++z90crypt_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
++{
++	PDEBUG("filp %p (PID %d)\n", filp, PID());
++	return -EPERM;
++}
++
++/**
++ * New status functions
++ */
++static inline int
++get_status_totalcount(void)
++{
++	return z90crypt.hdware_info->hdware_mask.st_count;
++}
++
++static inline int
++get_status_PCICAcount(void)
++{
++	return z90crypt.hdware_info->type_mask[PCICA].st_count;
++}
++
++static inline int
++get_status_PCICCcount(void)
++{
++	return z90crypt.hdware_info->type_mask[PCICC].st_count;
++}
++
++static inline int
++get_status_PCIXCCcount(void)
++{
++	return z90crypt.hdware_info->type_mask[PCIXCC_MCL2].st_count +
++	       z90crypt.hdware_info->type_mask[PCIXCC_MCL3].st_count;
++}
++
++static inline int
++get_status_PCIXCCMCL2count(void)
++{
++	return z90crypt.hdware_info->type_mask[PCIXCC_MCL2].st_count;
++}
++
++static inline int
++get_status_PCIXCCMCL3count(void)
++{
++	return z90crypt.hdware_info->type_mask[PCIXCC_MCL3].st_count;
++}
++
++static inline int
++get_status_CEX2Ccount(void)
++{
++	return z90crypt.hdware_info->type_mask[CEX2C].st_count;
++}
++
++static inline int
++get_status_requestq_count(void)
++{
++	return requestq_count;
++}
++
++static inline int
++get_status_pendingq_count(void)
++{
++	return pendingq_count;
++}
++
++static inline int
++get_status_totalopen_count(void)
++{
++	return atomic_read(&total_open);
++}
++
++static inline int
++get_status_domain_index(void)
++{
++	return z90crypt.cdx;
++}
++
++static inline unsigned char *
++get_status_status_mask(unsigned char status[Z90CRYPT_NUM_APS])
++{
++	int i, ix;
++
++	memcpy(status, z90crypt.hdware_info->device_type_array,
++	       Z90CRYPT_NUM_APS);
++
++	for (i = 0; i < get_status_totalcount(); i++) {
++		ix = SHRT2LONG(i);
++		if (LONG2DEVPTR(ix)->user_disabled)
++			status[ix] = 0x0d;
++	}
++
++	return status;
++}
++
++static inline unsigned char *
++get_status_qdepth_mask(unsigned char qdepth[Z90CRYPT_NUM_APS])
++{
++	int i, ix;
++
++	memset(qdepth, 0, Z90CRYPT_NUM_APS);
++
++	for (i = 0; i < get_status_totalcount(); i++) {
++		ix = SHRT2LONG(i);
++		qdepth[ix] = LONG2DEVPTR(ix)->dev_caller_count;
++	}
++
++	return qdepth;
++}
++
++static inline unsigned int *
++get_status_perdevice_reqcnt(unsigned int reqcnt[Z90CRYPT_NUM_APS])
++{
++	int i, ix;
++
++	memset(reqcnt, 0, Z90CRYPT_NUM_APS * sizeof(int));
++
++	for (i = 0; i < get_status_totalcount(); i++) {
++		ix = SHRT2LONG(i);
++		reqcnt[ix] = LONG2DEVPTR(ix)->dev_total_req_cnt;
++	}
++
++	return reqcnt;
++}
++
++static inline void
++init_work_element(struct work_element *we_p,
++		  struct priv_data *priv_data, pid_t pid)
++{
++	int step;
++
++	we_p->requestptr = (unsigned char *)we_p + sizeof(struct work_element);
++	/* Come up with a unique id for this caller. */
++	step = atomic_inc_return(&z90crypt_step);
++	memcpy(we_p->caller_id+0, (void *) &pid, sizeof(pid));
++	memcpy(we_p->caller_id+4, (void *) &step, sizeof(step));
++	we_p->pid = pid;
++	we_p->priv_data = priv_data;
++	we_p->status[0] = STAT_DEFAULT;
++	we_p->audit[0] = 0x00;
++	we_p->audit[1] = 0x00;
++	we_p->audit[2] = 0x00;
++	we_p->resp_buff_size = 0;
++	we_p->retcode = 0;
++	we_p->devindex = -1;
++	we_p->devtype = -1;
++	atomic_set(&we_p->alarmrung, 0);
++	init_waitqueue_head(&we_p->waitq);
++	INIT_LIST_HEAD(&(we_p->liste));
++}
++
++static inline int
++allocate_work_element(struct work_element **we_pp,
++		      struct priv_data *priv_data_p, pid_t pid)
++{
++	struct work_element *we_p;
++
++	we_p = (struct work_element *) get_zeroed_page(GFP_KERNEL);
++	if (!we_p)
++		return -ENOMEM;
++	init_work_element(we_p, priv_data_p, pid);
++	*we_pp = we_p;
++	return 0;
++}
++
++static inline void
++remove_device(struct device *device_p)
++{
++	if (!device_p || (device_p->disabled != 0))
++		return;
++	device_p->disabled = 1;
++	z90crypt.hdware_info->type_mask[device_p->dev_type].disabled_count++;
++	z90crypt.hdware_info->hdware_mask.disabled_count++;
++}
++
++/**
++ * Bitlength limits for each card
++ *
++ * There are new MCLs which allow more bitlengths. See the table for details.
++ * The MCL must be applied and the newer bitlengths enabled for these to work.
++ *
++ * Card Type    Old limit    New limit
++ * PCICC         512-1024     512-2048
++ * PCIXCC_MCL2   512-2048     no change (applying this MCL == card is MCL3+)
++ * PCIXCC_MCL3   512-2048     128-2048
++ * CEX2C         512-2048     128-2048
++ *
++ * ext_bitlens (extended bitlengths) is a global, since you should not apply an
++ * MCL to just one card in a machine. We assume, at first, that all cards have
++ * these capabilities.
++ */
++int ext_bitlens = 1; // This is global
++#define PCIXCC_MIN_MOD_SIZE	 16	//  128 bits
++#define OLD_PCIXCC_MIN_MOD_SIZE	 64	//  512 bits
++#define PCICC_MIN_MOD_SIZE	 64	//  512 bits
++#define OLD_PCICC_MAX_MOD_SIZE	128	// 1024 bits
++#define MAX_MOD_SIZE		256	// 2048 bits
++
++static inline int
++select_device_type(int *dev_type_p, int bytelength)
++{
++	static int count = 0;
++	int PCICA_avail, PCIXCC_MCL3_avail, CEX2C_avail, index_to_use;
++	struct status *stat;
++	if ((*dev_type_p != PCICC) && (*dev_type_p != PCICA) &&
++	    (*dev_type_p != PCIXCC_MCL2) && (*dev_type_p != PCIXCC_MCL3) &&
++	    (*dev_type_p != CEX2C) && (*dev_type_p != ANYDEV))
++		return -1;
++	if (*dev_type_p != ANYDEV) {
++		stat = &z90crypt.hdware_info->type_mask[*dev_type_p];
++		if (stat->st_count >
++		    (stat->disabled_count + stat->user_disabled_count))
++			return 0;
++		return -1;
++	}
++
++	/* Assumption: PCICA, PCIXCC_MCL3, and CEX2C are all similar in speed */
++	stat = &z90crypt.hdware_info->type_mask[PCICA];
++	PCICA_avail = stat->st_count -
++			(stat->disabled_count + stat->user_disabled_count);
++	stat = &z90crypt.hdware_info->type_mask[PCIXCC_MCL3];
++	PCIXCC_MCL3_avail = stat->st_count -
++			(stat->disabled_count + stat->user_disabled_count);
++	stat = &z90crypt.hdware_info->type_mask[CEX2C];
++	CEX2C_avail = stat->st_count -
++			(stat->disabled_count + stat->user_disabled_count);
++	if (PCICA_avail || PCIXCC_MCL3_avail || CEX2C_avail) {
++		/**
++		 * bitlength is a factor, PCICA is the most capable, even with
++		 * the new MCL.
++		 */
++		if ((bytelength < PCIXCC_MIN_MOD_SIZE) ||
++		    (!ext_bitlens && (bytelength < OLD_PCIXCC_MIN_MOD_SIZE))) {
++			if (!PCICA_avail)
++				return -1;
++			else {
++				*dev_type_p = PCICA;
++				return 0;
++			}
++		}
++
++		index_to_use = count % (PCICA_avail + PCIXCC_MCL3_avail +
++					CEX2C_avail);
++		if (index_to_use < PCICA_avail)
++			*dev_type_p = PCICA;
++		else if (index_to_use < (PCICA_avail + PCIXCC_MCL3_avail))
++			*dev_type_p = PCIXCC_MCL3;
++		else
++			*dev_type_p = CEX2C;
++		count++;
++		return 0;
++	}
++
++	/* Less than OLD_PCIXCC_MIN_MOD_SIZE cannot go to a PCIXCC_MCL2 */
++	if (bytelength < OLD_PCIXCC_MIN_MOD_SIZE)
++		return -1;
++	stat = &z90crypt.hdware_info->type_mask[PCIXCC_MCL2];
++	if (stat->st_count >
++	    (stat->disabled_count + stat->user_disabled_count)) {
++		*dev_type_p = PCIXCC_MCL2;
++		return 0;
++	}
++
++	/**
++	 * Less than PCICC_MIN_MOD_SIZE or more than OLD_PCICC_MAX_MOD_SIZE
++	 * (if we don't have the MCL applied and the newer bitlengths enabled)
++	 * cannot go to a PCICC
++	 */
++	if ((bytelength < PCICC_MIN_MOD_SIZE) ||
++	    (!ext_bitlens && (bytelength > OLD_PCICC_MAX_MOD_SIZE))) {
++		return -1;
++	}
++	stat = &z90crypt.hdware_info->type_mask[PCICC];
++	if (stat->st_count >
++	    (stat->disabled_count + stat->user_disabled_count)) {
++		*dev_type_p = PCICC;
++		return 0;
++	}
++
++	return -1;
++}
++
++/**
++ * Try the selected number, then the selected type (can be ANYDEV)
++ */
++static inline int
++select_device(int *dev_type_p, int *device_nr_p, int bytelength)
++{
++	int i, indx, devTp, low_count, low_indx;
++	struct device_x *index_p;
++	struct device *dev_ptr;
++
++	PDEBUG("device type = %d, index = %d\n", *dev_type_p, *device_nr_p);
++	if ((*device_nr_p >= 0) && (*device_nr_p < Z90CRYPT_NUM_DEVS)) {
++		PDEBUG("trying index = %d\n", *device_nr_p);
++		dev_ptr = z90crypt.device_p[*device_nr_p];
++
++		if (dev_ptr &&
++		    (dev_ptr->dev_stat != DEV_GONE) &&
++		    (dev_ptr->disabled == 0) &&
++		    (dev_ptr->user_disabled == 0)) {
++			PDEBUG("selected by number, index = %d\n",
++			       *device_nr_p);
++			*dev_type_p = dev_ptr->dev_type;
++			return *device_nr_p;
++		}
++	}
++	*device_nr_p = -1;
++	PDEBUG("trying type = %d\n", *dev_type_p);
++	devTp = *dev_type_p;
++	if (select_device_type(&devTp, bytelength) == -1) {
++		PDEBUG("failed to select by type\n");
++		return -1;
++	}
++	PDEBUG("selected type = %d\n", devTp);
++	index_p = &z90crypt.hdware_info->type_x_addr[devTp];
++	low_count = 0x0000FFFF;
++	low_indx = -1;
++	for (i = 0; i < z90crypt.hdware_info->type_mask[devTp].st_count; i++) {
++		indx = index_p->device_index[i];
++		dev_ptr = z90crypt.device_p[indx];
++		if (dev_ptr &&
++		    (dev_ptr->dev_stat != DEV_GONE) &&
++		    (dev_ptr->disabled == 0) &&
++		    (dev_ptr->user_disabled == 0) &&
++		    (devTp == dev_ptr->dev_type) &&
++		    (low_count > dev_ptr->dev_caller_count)) {
++			low_count = dev_ptr->dev_caller_count;
++			low_indx = indx;
++		}
++	}
++	*device_nr_p = low_indx;
++	return low_indx;
++}
++
++static inline int
++send_to_crypto_device(struct work_element *we_p)
++{
++	struct caller *caller_p;
++	struct device *device_p;
++	int dev_nr;
++	int bytelen = ((struct ica_rsa_modexpo *)we_p->buffer)->inputdatalength;
++
++	if (!we_p->requestptr)
++		return SEN_FATAL_ERROR;
++	caller_p = (struct caller *)we_p->requestptr;
++	dev_nr = we_p->devindex;
++	if (select_device(&we_p->devtype, &dev_nr, bytelen) == -1) {
++		if (z90crypt.hdware_info->hdware_mask.st_count != 0)
++			return SEN_RETRY;
++		else
++			return SEN_NOT_AVAIL;
++	}
++	we_p->devindex = dev_nr;
++	device_p = z90crypt.device_p[dev_nr];
++	if (!device_p)
++		return SEN_NOT_AVAIL;
++	if (device_p->dev_type != we_p->devtype)
++		return SEN_RETRY;
++	if (device_p->dev_caller_count >= device_p->dev_q_depth)
++		return SEN_QUEUE_FULL;
++	PDEBUG("device number prior to send: %d\n", dev_nr);
++	switch (send_to_AP(dev_nr, z90crypt.cdx,
++			   caller_p->caller_dev_dep_req_l,
++			   caller_p->caller_dev_dep_req_p)) {
++	case DEV_SEN_EXCEPTION:
++		PRINTKC("Exception during send to device %d\n", dev_nr);
++		z90crypt.terminating = 1;
++		return SEN_FATAL_ERROR;
++	case DEV_GONE:
++		PRINTK("Device %d not available\n", dev_nr);
++		remove_device(device_p);
++		return SEN_NOT_AVAIL;
++	case DEV_EMPTY:
++		return SEN_NOT_AVAIL;
++	case DEV_NO_WORK:
++		return SEN_FATAL_ERROR;
++	case DEV_BAD_MESSAGE:
++		return SEN_USER_ERROR;
++	case DEV_QUEUE_FULL:
++		return SEN_QUEUE_FULL;
++	default:
++	case DEV_ONLINE:
++		break;
++	}
++	list_add_tail(&(caller_p->caller_liste), &(device_p->dev_caller_list));
++	device_p->dev_caller_count++;
++	return 0;
++}
++
++/**
++ * Send puts the user's work on one of two queues:
++ *   the pending queue if the send was successful
++ *   the request queue if the send failed because device full or busy
++ */
++static inline int
++z90crypt_send(struct work_element *we_p, const char *buf)
++{
++	int rv;
++
++	PDEBUG("PID %d\n", PID());
++
++	if (CHK_RDWRMASK(we_p->status[0]) != STAT_NOWORK) {
++		PDEBUG("PID %d tried to send more work but has outstanding "
++		       "work.\n", PID());
++		return -EWORKPEND;
++	}
++	we_p->devindex = -1; // Reset device number
++	spin_lock_irq(&queuespinlock);
++	rv = send_to_crypto_device(we_p);
++	switch (rv) {
++	case 0:
++		we_p->requestsent = jiffies;
++		we_p->audit[0] |= FP_SENT;
++		list_add_tail(&we_p->liste, &pending_list);
++		++pendingq_count;
++		we_p->audit[0] |= FP_PENDING;
++		break;
++	case SEN_BUSY:
++	case SEN_QUEUE_FULL:
++		rv = 0;
++		we_p->devindex = -1; // any device will do
++		we_p->requestsent = jiffies;
++		list_add_tail(&we_p->liste, &request_list);
++		++requestq_count;
++		we_p->audit[0] |= FP_REQUEST;
++		break;
++	case SEN_RETRY:
++		rv = -ERESTARTSYS;
++		break;
++	case SEN_NOT_AVAIL:
++		PRINTK("*** No devices available.\n");
++		rv = we_p->retcode = -ENODEV;
++		we_p->status[0] |= STAT_FAILED;
++		break;
++	case REC_OPERAND_INV:
++	case REC_OPERAND_SIZE:
++	case REC_EVEN_MOD:
++	case REC_INVALID_PAD:
++		rv = we_p->retcode = -EINVAL;
++		we_p->status[0] |= STAT_FAILED;
++		break;
++	default:
++		we_p->retcode = rv;
++		we_p->status[0] |= STAT_FAILED;
++		break;
++	}
++	if (rv != -ERESTARTSYS)
++		SET_RDWRMASK(we_p->status[0], STAT_WRITTEN);
++	spin_unlock_irq(&queuespinlock);
++	if (rv == 0)
++		tasklet_schedule(&reader_tasklet);
++	return rv;
++}
++
++/**
++ * process_results copies the user's work from kernel space.
++ */
++static inline int
++z90crypt_process_results(struct work_element *we_p, char __user *buf)
++{
++	int rv;
++
++	PDEBUG("we_p %p (PID %d)\n", we_p, PID());
++
++	LONG2DEVPTR(we_p->devindex)->dev_total_req_cnt++;
++	SET_RDWRMASK(we_p->status[0], STAT_READPEND);
++
++	rv = 0;
++	if (!we_p->buffer) {
++		PRINTK("we_p %p PID %d in STAT_READPEND: buffer NULL.\n",
++			we_p, PID());
++		rv = -ENOBUFF;
++	}
++
++	if (!rv)
++		if ((rv = copy_to_user(buf, we_p->buffer, we_p->buff_size))) {
++			PDEBUG("copy_to_user failed: rv = %d\n", rv);
++			rv = -EFAULT;
++		}
++
++	if (!rv)
++		rv = we_p->retcode;
++	if (!rv)
++		if (we_p->resp_buff_size
++		    &&	copy_to_user(we_p->resp_addr, we_p->resp_buff,
++				     we_p->resp_buff_size))
++			rv = -EFAULT;
++
++	SET_RDWRMASK(we_p->status[0], STAT_NOWORK);
++	return rv;
++}
++
++static unsigned char NULL_psmid[8] =
++{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
++
++/**
++ * Used in device configuration functions
++ */
++#define MAX_RESET 90
++
++/**
++ * This is used only for PCICC support
++ */
++static inline int
++is_PKCS11_padded(unsigned char *buffer, int length)
++{
++	int i;
++	if ((buffer[0] != 0x00) || (buffer[1] != 0x01))
++		return 0;
++	for (i = 2; i < length; i++)
++		if (buffer[i] != 0xFF)
++			break;
++	if ((i < 10) || (i == length))
++		return 0;
++	if (buffer[i] != 0x00)
++		return 0;
++	return 1;
++}
++
++/**
++ * This is used only for PCICC support
++ */
++static inline int
++is_PKCS12_padded(unsigned char *buffer, int length)
++{
++	int i;
++	if ((buffer[0] != 0x00) || (buffer[1] != 0x02))
++		return 0;
++	for (i = 2; i < length; i++)
++		if (buffer[i] == 0x00)
++			break;
++	if ((i < 10) || (i == length))
++		return 0;
++	if (buffer[i] != 0x00)
++		return 0;
++	return 1;
++}
++
++/**
++ * builds struct caller and converts message from generic format to
++ * device-dependent format
++ * func is ICARSAMODEXPO or ICARSACRT
++ * function is PCI_FUNC_KEY_ENCRYPT or PCI_FUNC_KEY_DECRYPT
++ */
++static inline int
++build_caller(struct work_element *we_p, short function)
++{
++	int rv;
++	struct caller *caller_p = (struct caller *)we_p->requestptr;
++
++	if ((we_p->devtype != PCICC) && (we_p->devtype != PCICA) &&
++	    (we_p->devtype != PCIXCC_MCL2) && (we_p->devtype != PCIXCC_MCL3) &&
++	    (we_p->devtype != CEX2C))
++		return SEN_NOT_AVAIL;
++
++	memcpy(caller_p->caller_id, we_p->caller_id,
++	       sizeof(caller_p->caller_id));
++	caller_p->caller_dev_dep_req_p = caller_p->caller_dev_dep_req;
++	caller_p->caller_dev_dep_req_l = MAX_RESPONSE_SIZE;
++	caller_p->caller_buf_p = we_p->buffer;
++	INIT_LIST_HEAD(&(caller_p->caller_liste));
++
++	rv = convert_request(we_p->buffer, we_p->funccode, function,
++			     z90crypt.cdx, we_p->devtype,
++			     &caller_p->caller_dev_dep_req_l,
++			     caller_p->caller_dev_dep_req_p);
++	if (rv) {
++		if (rv == SEN_NOT_AVAIL)
++			PDEBUG("request can't be processed on hdwr avail\n");
++		else
++			PRINTK("Error from convert_request: %d\n", rv);
++	}
++	else
++		memcpy(&(caller_p->caller_dev_dep_req_p[4]), we_p->caller_id,8);
++	return rv;
++}
++
++static inline void
++unbuild_caller(struct device *device_p, struct caller *caller_p)
++{
++	if (!caller_p)
++		return;
++	if (caller_p->caller_liste.next && caller_p->caller_liste.prev)
++		if (!list_empty(&caller_p->caller_liste)) {
++			list_del_init(&caller_p->caller_liste);
++			device_p->dev_caller_count--;
++		}
++	memset(caller_p->caller_id, 0, sizeof(caller_p->caller_id));
++}
++
++static inline int
++get_crypto_request_buffer(struct work_element *we_p)
++{
++	struct ica_rsa_modexpo *mex_p;
++	struct ica_rsa_modexpo_crt *crt_p;
++	unsigned char *temp_buffer;
++	short function;
++	int rv;
++
++	mex_p =	(struct ica_rsa_modexpo *) we_p->buffer;
++	crt_p = (struct ica_rsa_modexpo_crt *) we_p->buffer;
++
++	PDEBUG("device type input = %d\n", we_p->devtype);
++
++	if (z90crypt.terminating)
++		return REC_NO_RESPONSE;
++	if (memcmp(we_p->caller_id, NULL_psmid, 8) == 0) {
++		PRINTK("psmid zeroes\n");
++		return SEN_FATAL_ERROR;
++	}
++	if (!we_p->buffer) {
++		PRINTK("buffer pointer NULL\n");
++		return SEN_USER_ERROR;
++	}
++	if (!we_p->requestptr) {
++		PRINTK("caller pointer NULL\n");
++		return SEN_USER_ERROR;
++	}
++
++	if ((we_p->devtype != PCICA) && (we_p->devtype != PCICC) &&
++	    (we_p->devtype != PCIXCC_MCL2) && (we_p->devtype != PCIXCC_MCL3) &&
++	    (we_p->devtype != CEX2C) && (we_p->devtype != ANYDEV)) {
++		PRINTK("invalid device type\n");
++		return SEN_USER_ERROR;
++	}
++
++	if ((mex_p->inputdatalength < 1) ||
++	    (mex_p->inputdatalength > MAX_MOD_SIZE)) {
++		PRINTK("inputdatalength[%d] is not valid\n",
++		       mex_p->inputdatalength);
++		return SEN_USER_ERROR;
++	}
++
++	if (mex_p->outputdatalength < mex_p->inputdatalength) {
++		PRINTK("outputdatalength[%d] < inputdatalength[%d]\n",
++		       mex_p->outputdatalength, mex_p->inputdatalength);
++		return SEN_USER_ERROR;
++	}
++
++	if (!mex_p->inputdata || !mex_p->outputdata) {
++		PRINTK("inputdata[%p] or outputdata[%p] is NULL\n",
++		       mex_p->outputdata, mex_p->inputdata);
++		return SEN_USER_ERROR;
++	}
++
++	/**
++	 * As long as outputdatalength is big enough, we can set the
++	 * outputdatalength equal to the inputdatalength, since that is the
++	 * number of bytes we will copy in any case
++	 */
++	mex_p->outputdatalength = mex_p->inputdatalength;
++
++	rv = 0;
++	switch (we_p->funccode) {
++	case ICARSAMODEXPO:
++		if (!mex_p->b_key || !mex_p->n_modulus)
++			rv = SEN_USER_ERROR;
++		break;
++	case ICARSACRT:
++		if (!IS_EVEN(crt_p->inputdatalength)) {
++			PRINTK("inputdatalength[%d] is odd, CRT form\n",
++			       crt_p->inputdatalength);
++			rv = SEN_USER_ERROR;
++			break;
++		}
++		if (!crt_p->bp_key ||
++		    !crt_p->bq_key ||
++		    !crt_p->np_prime ||
++		    !crt_p->nq_prime ||
++		    !crt_p->u_mult_inv) {
++			PRINTK("CRT form, bad data: %p/%p/%p/%p/%p\n",
++			       crt_p->bp_key, crt_p->bq_key,
++			       crt_p->np_prime, crt_p->nq_prime,
++			       crt_p->u_mult_inv);
++			rv = SEN_USER_ERROR;
++		}
++		break;
++	default:
++		PRINTK("bad func = %d\n", we_p->funccode);
++		rv = SEN_USER_ERROR;
++		break;
++	}
++	if (rv != 0)
++		return rv;
++
++	if (select_device_type(&we_p->devtype, mex_p->inputdatalength) < 0)
++		return SEN_NOT_AVAIL;
++
++	temp_buffer = (unsigned char *)we_p + sizeof(struct work_element) +
++		      sizeof(struct caller);
++	if (copy_from_user(temp_buffer, mex_p->inputdata,
++			   mex_p->inputdatalength) != 0)
++		return SEN_RELEASED;
++
++	function = PCI_FUNC_KEY_ENCRYPT;
++	switch (we_p->devtype) {
++	/* PCICA does everything with a simple RSA mod-expo operation */
++	case PCICA:
++		function = PCI_FUNC_KEY_ENCRYPT;
++		break;
++	/**
++	 * PCIXCC_MCL2 does all Mod-Expo form with a simple RSA mod-expo
++	 * operation, and all CRT forms with a PKCS-1.2 format decrypt.
++	 * PCIXCC_MCL3 and CEX2C do all Mod-Expo and CRT forms with a simple RSA
++	 * mod-expo operation
++	 */
++	case PCIXCC_MCL2:
++		if (we_p->funccode == ICARSAMODEXPO)
++			function = PCI_FUNC_KEY_ENCRYPT;
++		else
++			function = PCI_FUNC_KEY_DECRYPT;
++		break;
++	case PCIXCC_MCL3:
++	case CEX2C:
++		if (we_p->funccode == ICARSAMODEXPO)
++			function = PCI_FUNC_KEY_ENCRYPT;
++		else
++			function = PCI_FUNC_KEY_DECRYPT;
++		break;
++	/**
++	 * PCICC does everything as a PKCS-1.2 format request
++	 */
++	case PCICC:
++		/* PCICC cannot handle input that is is PKCS#1.1 padded */
++		if (is_PKCS11_padded(temp_buffer, mex_p->inputdatalength)) {
++			return SEN_NOT_AVAIL;
++		}
++		if (we_p->funccode == ICARSAMODEXPO) {
++			if (is_PKCS12_padded(temp_buffer,
++					     mex_p->inputdatalength))
++				function = PCI_FUNC_KEY_ENCRYPT;
++			else
++				function = PCI_FUNC_KEY_DECRYPT;
++		} else
++			/* all CRT forms are decrypts */
++			function = PCI_FUNC_KEY_DECRYPT;
++		break;
++	}
++	PDEBUG("function: %04x\n", function);
++	rv = build_caller(we_p, function);
++	PDEBUG("rv from build_caller = %d\n", rv);
++	return rv;
++}
++
++static inline int
++z90crypt_prepare(struct work_element *we_p, unsigned int funccode,
++		 const char __user *buffer)
++{
++	int rv;
++
++	we_p->devindex = -1;
++	if (funccode == ICARSAMODEXPO)
++		we_p->buff_size = sizeof(struct ica_rsa_modexpo);
++	else
++		we_p->buff_size = sizeof(struct ica_rsa_modexpo_crt);
++
++	if (copy_from_user(we_p->buffer, buffer, we_p->buff_size))
++		return -EFAULT;
++
++	we_p->audit[0] |= FP_COPYFROM;
++	SET_RDWRMASK(we_p->status[0], STAT_WRITTEN);
++	we_p->funccode = funccode;
++	we_p->devtype = -1;
++	we_p->audit[0] |= FP_BUFFREQ;
++	rv = get_crypto_request_buffer(we_p);
++	switch (rv) {
++	case 0:
++		we_p->audit[0] |= FP_BUFFGOT;
++		break;
++	case SEN_USER_ERROR:
++		rv = -EINVAL;
++		break;
++	case SEN_QUEUE_FULL:
++		rv = 0;
++		break;
++	case SEN_RELEASED:
++		rv = -EFAULT;
++		break;
++	case REC_NO_RESPONSE:
++		rv = -ENODEV;
++		break;
++	case SEN_NOT_AVAIL:
++		rv = -EGETBUFF;
++		break;
++	default:
++		PRINTK("rv = %d\n", rv);
++		rv = -EGETBUFF;
++		break;
++	}
++	if (CHK_RDWRMASK(we_p->status[0]) == STAT_WRITTEN)
++		SET_RDWRMASK(we_p->status[0], STAT_DEFAULT);
++	return rv;
++}
++
++static inline void
++purge_work_element(struct work_element *we_p)
++{
++	struct list_head *lptr;
++
++	spin_lock_irq(&queuespinlock);
++	list_for_each(lptr, &request_list) {
++		if (lptr == &we_p->liste) {
++			list_del_init(lptr);
++			requestq_count--;
++			break;
++		}
++	}
++	list_for_each(lptr, &pending_list) {
++		if (lptr == &we_p->liste) {
++			list_del_init(lptr);
++			pendingq_count--;
++			break;
++		}
++	}
++	spin_unlock_irq(&queuespinlock);
++}
++
++/**
++ * Build the request and send it.
++ */
++static inline int
++z90crypt_rsa(struct priv_data *private_data_p, pid_t pid,
++	     unsigned int cmd, unsigned long arg)
++{
++	struct work_element *we_p;
++	int rv;
++
++	if ((rv = allocate_work_element(&we_p, private_data_p, pid))) {
++		PDEBUG("PID %d: allocate_work_element returned ENOMEM\n", pid);
++		return rv;
++	}
++	if ((rv = z90crypt_prepare(we_p, cmd, (const char __user *)arg)))
++		PDEBUG("PID %d: rv = %d from z90crypt_prepare\n", pid, rv);
++	if (!rv)
++		if ((rv = z90crypt_send(we_p, (const char *)arg)))
++			PDEBUG("PID %d: rv %d from z90crypt_send.\n", pid, rv);
++	if (!rv) {
++		we_p->audit[0] |= FP_ASLEEP;
++		wait_event(we_p->waitq, atomic_read(&we_p->alarmrung));
++		we_p->audit[0] |= FP_AWAKE;
++		rv = we_p->retcode;
++	}
++	if (!rv)
++		rv = z90crypt_process_results(we_p, (char __user *)arg);
++
++	if ((we_p->status[0] & STAT_FAILED)) {
++		switch (rv) {
++		/**
++		 * EINVAL *after* receive is almost always a padding error or
++		 * length error issued by a coprocessor (not an accelerator).
++		 * We convert this return value to -EGETBUFF which should
++		 * trigger a fallback to software.
++		 */
++		case -EINVAL:
++			if (we_p->devtype != PCICA)
++				rv = -EGETBUFF;
++			break;
++		case -ETIMEOUT:
++			if (z90crypt.mask.st_count > 0)
++				rv = -ERESTARTSYS; // retry with another
++			else
++				rv = -ENODEV; // no cards left
++		/* fall through to clean up request queue */
++		case -ERESTARTSYS:
++		case -ERELEASED:
++			switch (CHK_RDWRMASK(we_p->status[0])) {
++			case STAT_WRITTEN:
++				purge_work_element(we_p);
++				break;
++			case STAT_READPEND:
++			case STAT_NOWORK:
++			default:
++				break;
++			}
++			break;
++		default:
++			we_p->status[0] ^= STAT_FAILED;
++			break;
++		}
++	}
++	free_page((long)we_p);
++	return rv;
++}
++
++/**
++ * This function is a little long, but it's really just one large switch
++ * statement.
++ */
++static int
++z90crypt_ioctl(struct inode *inode, struct file *filp,
++	       unsigned int cmd, unsigned long arg)
++{
++	struct priv_data *private_data_p = filp->private_data;
++	unsigned char *status;
++	unsigned char *qdepth;
++	unsigned int *reqcnt;
++	struct ica_z90_status *pstat;
++	int ret, i, loopLim, tempstat;
++	static int deprecated_msg_count1 = 0;
++	static int deprecated_msg_count2 = 0;
++
++	PDEBUG("filp %p (PID %d), cmd 0x%08X\n", filp, PID(), cmd);
++	PDEBUG("cmd 0x%08X: dir %s, size 0x%04X, type 0x%02X, nr 0x%02X\n",
++		cmd,
++		!_IOC_DIR(cmd) ? "NO"
++		: ((_IOC_DIR(cmd) == (_IOC_READ|_IOC_WRITE)) ? "RW"
++		: ((_IOC_DIR(cmd) == _IOC_READ) ? "RD"
++		: "WR")),
++		_IOC_SIZE(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd));
++
++	if (_IOC_TYPE(cmd) != Z90_IOCTL_MAGIC) {
++		PRINTK("cmd 0x%08X contains bad magic\n", cmd);
++		return -ENOTTY;
++	}
++
++	ret = 0;
++	switch (cmd) {
++	case ICARSAMODEXPO:
++	case ICARSACRT:
++		if (quiesce_z90crypt) {
++			ret = -EQUIESCE;
++			break;
++		}
++		ret = -ENODEV; // Default if no devices
++		loopLim = z90crypt.hdware_info->hdware_mask.st_count -
++			(z90crypt.hdware_info->hdware_mask.disabled_count +
++			 z90crypt.hdware_info->hdware_mask.user_disabled_count);
++		for (i = 0; i < loopLim; i++) {
++			ret = z90crypt_rsa(private_data_p, PID(), cmd, arg);
++			if (ret != -ERESTARTSYS)
++				break;
++		}
++		if (ret == -ERESTARTSYS)
++			ret = -ENODEV;
++		break;
++
++	case Z90STAT_TOTALCOUNT:
++		tempstat = get_status_totalcount();
++		if (copy_to_user((int __user *)arg, &tempstat,sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90STAT_PCICACOUNT:
++		tempstat = get_status_PCICAcount();
++		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90STAT_PCICCCOUNT:
++		tempstat = get_status_PCICCcount();
++		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90STAT_PCIXCCMCL2COUNT:
++		tempstat = get_status_PCIXCCMCL2count();
++		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90STAT_PCIXCCMCL3COUNT:
++		tempstat = get_status_PCIXCCMCL3count();
++		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90STAT_CEX2CCOUNT:
++		tempstat = get_status_CEX2Ccount();
++		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90STAT_REQUESTQ_COUNT:
++		tempstat = get_status_requestq_count();
++		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90STAT_PENDINGQ_COUNT:
++		tempstat = get_status_pendingq_count();
++		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90STAT_TOTALOPEN_COUNT:
++		tempstat = get_status_totalopen_count();
++		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90STAT_DOMAIN_INDEX:
++		tempstat = get_status_domain_index();
++		if (copy_to_user((int __user *)arg, &tempstat, sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90STAT_STATUS_MASK:
++		status = kmalloc(Z90CRYPT_NUM_APS, GFP_KERNEL);
++		if (!status) {
++			PRINTK("kmalloc for status failed!\n");
++			ret = -ENOMEM;
++			break;
++		}
++		get_status_status_mask(status);
++		if (copy_to_user((char __user *) arg, status, Z90CRYPT_NUM_APS)
++									!= 0)
++			ret = -EFAULT;
++		kfree(status);
++		break;
++
++	case Z90STAT_QDEPTH_MASK:
++		qdepth = kmalloc(Z90CRYPT_NUM_APS, GFP_KERNEL);
++		if (!qdepth) {
++			PRINTK("kmalloc for qdepth failed!\n");
++			ret = -ENOMEM;
++			break;
++		}
++		get_status_qdepth_mask(qdepth);
++		if (copy_to_user((char __user *) arg, qdepth, Z90CRYPT_NUM_APS) != 0)
++			ret = -EFAULT;
++		kfree(qdepth);
++		break;
++
++	case Z90STAT_PERDEV_REQCNT:
++		reqcnt = kmalloc(sizeof(int) * Z90CRYPT_NUM_APS, GFP_KERNEL);
++		if (!reqcnt) {
++			PRINTK("kmalloc for reqcnt failed!\n");
++			ret = -ENOMEM;
++			break;
++		}
++		get_status_perdevice_reqcnt(reqcnt);
++		if (copy_to_user((char __user *) arg, reqcnt,
++				 Z90CRYPT_NUM_APS * sizeof(int)) != 0)
++			ret = -EFAULT;
++		kfree(reqcnt);
++		break;
++
++		/* THIS IS DEPRECATED.	USE THE NEW STATUS CALLS */
++	case ICAZ90STATUS:
++		if (deprecated_msg_count1 < 20) {
++			PRINTK("deprecated call to ioctl (ICAZ90STATUS)!\n");
++			deprecated_msg_count1++;
++			if (deprecated_msg_count1 == 20)
++				PRINTK("No longer issuing messages related to "
++				       "deprecated call to ICAZ90STATUS.\n");
++		}
++
++		pstat = kmalloc(sizeof(struct ica_z90_status), GFP_KERNEL);
++		if (!pstat) {
++			PRINTK("kmalloc for pstat failed!\n");
++			ret = -ENOMEM;
++			break;
++		}
++
++		pstat->totalcount	 = get_status_totalcount();
++		pstat->leedslitecount	 = get_status_PCICAcount();
++		pstat->leeds2count	 = get_status_PCICCcount();
++		pstat->requestqWaitCount = get_status_requestq_count();
++		pstat->pendingqWaitCount = get_status_pendingq_count();
++		pstat->totalOpenCount	 = get_status_totalopen_count();
++		pstat->cryptoDomain	 = get_status_domain_index();
++		get_status_status_mask(pstat->status);
++		get_status_qdepth_mask(pstat->qdepth);
++
++		if (copy_to_user((struct ica_z90_status __user *) arg, pstat,
++				 sizeof(struct ica_z90_status)) != 0)
++			ret = -EFAULT;
++		kfree(pstat);
++		break;
++
++		/* THIS IS DEPRECATED.	USE THE NEW STATUS CALLS */
++	case Z90STAT_PCIXCCCOUNT:
++		if (deprecated_msg_count2 < 20) {
++			PRINTK("deprecated ioctl (Z90STAT_PCIXCCCOUNT)!\n");
++			deprecated_msg_count2++;
++			if (deprecated_msg_count2 == 20)
++				PRINTK("No longer issuing messages about depre"
++				       "cated ioctl Z90STAT_PCIXCCCOUNT.\n");
++		}
++
++		tempstat = get_status_PCIXCCcount();
++		if (copy_to_user((int *)arg, &tempstat, sizeof(int)) != 0)
++			ret = -EFAULT;
++		break;
++
++	case Z90QUIESCE:
++		if (current->euid != 0) {
++			PRINTK("QUIESCE fails: euid %d\n",
++			       current->euid);
++			ret = -EACCES;
++		} else {
++			PRINTK("QUIESCE device from PID %d\n", PID());
++			quiesce_z90crypt = 1;
++		}
++		break;
++
++	default:
++		/* user passed an invalid IOCTL number */
++		PDEBUG("cmd 0x%08X contains invalid ioctl code\n", cmd);
++		ret = -ENOTTY;
++		break;
++	}
++
++	return ret;
++}
++
++static inline int
++sprintcl(unsigned char *outaddr, unsigned char *addr, unsigned int len)
++{
++	int hl, i;
++
++	hl = 0;
++	for (i = 0; i < len; i++)
++		hl += sprintf(outaddr+hl, "%01x", (unsigned int) addr[i]);
++	hl += sprintf(outaddr+hl, " ");
++
++	return hl;
++}
++
++static inline int
++sprintrw(unsigned char *outaddr, unsigned char *addr, unsigned int len)
++{
++	int hl, inl, c, cx;
++
++	hl = sprintf(outaddr, "	   ");
++	inl = 0;
++	for (c = 0; c < (len / 16); c++) {
++		hl += sprintcl(outaddr+hl, addr+inl, 16);
++		inl += 16;
++	}
++
++	cx = len%16;
++	if (cx) {
++		hl += sprintcl(outaddr+hl, addr+inl, cx);
++		inl += cx;
++	}
++
++	hl += sprintf(outaddr+hl, "\n");
++
++	return hl;
++}
++
++static inline int
++sprinthx(unsigned char *title, unsigned char *outaddr,
++	 unsigned char *addr, unsigned int len)
++{
++	int hl, inl, r, rx;
++
++	hl = sprintf(outaddr, "\n%s\n", title);
++	inl = 0;
++	for (r = 0; r < (len / 64); r++) {
++		hl += sprintrw(outaddr+hl, addr+inl, 64);
++		inl += 64;
++	}
++	rx = len % 64;
++	if (rx) {
++		hl += sprintrw(outaddr+hl, addr+inl, rx);
++		inl += rx;
++	}
++
++	hl += sprintf(outaddr+hl, "\n");
++
++	return hl;
++}
++
++static inline int
++sprinthx4(unsigned char *title, unsigned char *outaddr,
++	  unsigned int *array, unsigned int len)
++{
++	int hl, r;
++
++	hl = sprintf(outaddr, "\n%s\n", title);
++
++	for (r = 0; r < len; r++) {
++		if ((r % 8) == 0)
++			hl += sprintf(outaddr+hl, "    ");
++		hl += sprintf(outaddr+hl, "%08X ", array[r]);
++		if ((r % 8) == 7)
++			hl += sprintf(outaddr+hl, "\n");
++	}
++
++	hl += sprintf(outaddr+hl, "\n");
++
++	return hl;
++}
++
++static int
++z90crypt_status(char *resp_buff, char **start, off_t offset,
++		int count, int *eof, void *data)
++{
++	unsigned char *workarea;
++	int len;
++
++	/* resp_buff is a page. Use the right half for a work area */
++	workarea = resp_buff+2000;
++	len = 0;
++	len += sprintf(resp_buff+len, "\nz90crypt version: %d.%d.%d\n",
++		z90crypt_VERSION, z90crypt_RELEASE, z90crypt_VARIANT);
++	len += sprintf(resp_buff+len, "Cryptographic domain: %d\n",
++		get_status_domain_index());
++	len += sprintf(resp_buff+len, "Total device count: %d\n",
++		get_status_totalcount());
++	len += sprintf(resp_buff+len, "PCICA count: %d\n",
++		get_status_PCICAcount());
++	len += sprintf(resp_buff+len, "PCICC count: %d\n",
++		get_status_PCICCcount());
++	len += sprintf(resp_buff+len, "PCIXCC MCL2 count: %d\n",
++		get_status_PCIXCCMCL2count());
++	len += sprintf(resp_buff+len, "PCIXCC MCL3 count: %d\n",
++		get_status_PCIXCCMCL3count());
++	len += sprintf(resp_buff+len, "CEX2C count: %d\n",
++		get_status_CEX2Ccount());
++	len += sprintf(resp_buff+len, "requestq count: %d\n",
++		get_status_requestq_count());
++	len += sprintf(resp_buff+len, "pendingq count: %d\n",
++		get_status_pendingq_count());
++	len += sprintf(resp_buff+len, "Total open handles: %d\n\n",
++		get_status_totalopen_count());
++	len += sprinthx(
++		"Online devices: 1: PCICA, 2: PCICC, 3: PCIXCC (MCL2), "
++		"4: PCIXCC (MCL3), 5: CEX2C",
++		resp_buff+len,
++		get_status_status_mask(workarea),
++		Z90CRYPT_NUM_APS);
++	len += sprinthx("Waiting work element counts",
++		resp_buff+len,
++		get_status_qdepth_mask(workarea),
++		Z90CRYPT_NUM_APS);
++	len += sprinthx4(
++		"Per-device successfully completed request counts",
++		resp_buff+len,
++		get_status_perdevice_reqcnt((unsigned int *)workarea),
++		Z90CRYPT_NUM_APS);
++	*eof = 1;
++	memset(workarea, 0, Z90CRYPT_NUM_APS * sizeof(unsigned int));
++	return len;
++}
++
++static inline void
++disable_card(int card_index)
++{
++	struct device *devp;
++
++	devp = LONG2DEVPTR(card_index);
++	if (!devp || devp->user_disabled)
++		return;
++	devp->user_disabled = 1;
++	z90crypt.hdware_info->hdware_mask.user_disabled_count++;
++	if (devp->dev_type == -1)
++		return;
++	z90crypt.hdware_info->type_mask[devp->dev_type].user_disabled_count++;
++}
++
++static inline void
++enable_card(int card_index)
++{
++	struct device *devp;
++
++	devp = LONG2DEVPTR(card_index);
++	if (!devp || !devp->user_disabled)
++		return;
++	devp->user_disabled = 0;
++	z90crypt.hdware_info->hdware_mask.user_disabled_count--;
++	if (devp->dev_type == -1)
++		return;
++	z90crypt.hdware_info->type_mask[devp->dev_type].user_disabled_count--;
++}
++
++static inline int
++scan_char(unsigned char *bf, unsigned int len,
++	  unsigned int *offs, unsigned int *p_eof, unsigned char c)
++{
++	unsigned int i, found;
++
++	found = 0;
++	for (i = 0; i < len; i++) {
++		if (bf[i] == c) {
++			found = 1;
++			break;
++		}
++		if (bf[i] == '\0') {
++			*p_eof = 1;
++			break;
++		}
++		if (bf[i] == '\n') {
++			break;
++		}
++	}
++	*offs = i+1;
++	return found;
++}
++
++static inline int
++scan_string(unsigned char *bf, unsigned int len,
++	    unsigned int *offs, unsigned int *p_eof, unsigned char *s)
++{
++	unsigned int temp_len, temp_offs, found, eof;
++
++	temp_len = temp_offs = found = eof = 0;
++	while (!eof && !found) {
++		found = scan_char(bf+temp_len, len-temp_len,
++				  &temp_offs, &eof, *s);
++
++		temp_len += temp_offs;
++		if (eof) {
++			found = 0;
++			break;
++		}
++
++		if (found) {
++			if (len >= temp_offs+strlen(s)) {
++				found = !strncmp(bf+temp_len-1, s, strlen(s));
++				if (found) {
++					*offs = temp_len+strlen(s)-1;
++					break;
++				}
++			} else {
++				found = 0;
++				*p_eof = 1;
++				break;
++			}
++		}
++	}
++	return found;
++}
++
++static int
++z90crypt_status_write(struct file *file, const char __user *buffer,
++		      unsigned long count, void *data)
++{
++	int i, j, len, offs, found, eof;
++	unsigned char *lbuf;
++	unsigned int local_count;
++
++#define LBUFSIZE 600
++	lbuf = kmalloc(LBUFSIZE, GFP_KERNEL);
++	if (!lbuf) {
++		PRINTK("kmalloc failed!\n");
++		return 0;
++	}
++
++	if (count <= 0)
++		return 0;
++
++	local_count = UMIN((unsigned int)count, LBUFSIZE-1);
++
++	if (copy_from_user(lbuf, buffer, local_count) != 0) {
++		kfree(lbuf);
++		return -EFAULT;
++	}
++
++	lbuf[local_count-1] = '\0';
++
++	len = 0;
++	eof = 0;
++	found = 0;
++	while (!eof) {
++		found = scan_string(lbuf+len, local_count-len, &offs, &eof,
++				    "Online devices");
++		len += offs;
++		if (found == 1)
++			break;
++	}
++
++	if (eof) {
++		kfree(lbuf);
++		return count;
++	}
++
++	if (found)
++		found = scan_char(lbuf+len, local_count-len, &offs, &eof, '\n');
++
++	if (!found || eof) {
++		kfree(lbuf);
++		return count;
++	}
++
++	len += offs;
++	j = 0;
++	for (i = 0; i < 80; i++) {
++		switch (*(lbuf+len+i)) {
++		case '\t':
++		case ' ':
++			break;
++		case '\n':
++		default:
++			eof = 1;
++			break;
++		case '0':
++		case '1':
++		case '2':
++		case '3':
++		case '4':
++		case '5':
++			j++;
++			break;
++		case 'd':
++		case 'D':
++			disable_card(j);
++			j++;
++			break;
++		case 'e':
++		case 'E':
++			enable_card(j);
++			j++;
++			break;
++		}
++		if (eof)
++			break;
++	}
++
++	kfree(lbuf);
++	return count;
++}
++
++/**
++ * Functions that run under a timer, with no process id
++ *
++ * The task functions:
++ *     z90crypt_reader_task
++ *	 helper_send_work
++ *	 helper_handle_work_element
++ *	 helper_receive_rc
++ *     z90crypt_config_task
++ *     z90crypt_cleanup_task
++ *
++ * Helper functions:
++ *     z90crypt_schedule_reader_timer
++ *     z90crypt_schedule_reader_task
++ *     z90crypt_schedule_config_task
++ *     z90crypt_schedule_cleanup_task
++ */
++static inline int
++receive_from_crypto_device(int index, unsigned char *psmid, int *buff_len_p,
++			   unsigned char *buff, unsigned char __user **dest_p_p)
++{
++	int dv, rv;
++	struct device *dev_ptr;
++	struct caller *caller_p;
++	struct ica_rsa_modexpo *icaMsg_p;
++	struct list_head *ptr, *tptr;
++
++	memcpy(psmid, NULL_psmid, sizeof(NULL_psmid));
++
++	if (z90crypt.terminating)
++		return REC_FATAL_ERROR;
++
++	caller_p = 0;
++	dev_ptr = z90crypt.device_p[index];
++	rv = 0;
++	do {
++		if (!dev_ptr || dev_ptr->disabled) {
++			rv = REC_NO_WORK; // a disabled device can't return work
++			break;
++		}
++		if (dev_ptr->dev_self_x != index) {
++			PRINTK("Corrupt dev ptr in receive_from_AP\n");
++			z90crypt.terminating = 1;
++			rv = REC_FATAL_ERROR;
++			break;
++		}
++		if (!dev_ptr->dev_resp_l || !dev_ptr->dev_resp_p) {
++			dv = DEV_REC_EXCEPTION;
++			PRINTK("dev_resp_l = %d, dev_resp_p = %p\n",
++			       dev_ptr->dev_resp_l, dev_ptr->dev_resp_p);
++		} else {
++			PDEBUG("Dequeue called for device %d\n", index);
++			dv = receive_from_AP(index, z90crypt.cdx,
++					     dev_ptr->dev_resp_l,
++					     dev_ptr->dev_resp_p, psmid);
++		}
++		switch (dv) {
++		case DEV_REC_EXCEPTION:
++			rv = REC_FATAL_ERROR;
++			z90crypt.terminating = 1;
++			PRINTKC("Exception in receive from device %d\n",
++				index);
++			break;
++		case DEV_ONLINE:
++			rv = 0;
++			break;
++		case DEV_EMPTY:
++			rv = REC_EMPTY;
++			break;
++		case DEV_NO_WORK:
++			rv = REC_NO_WORK;
++			break;
++		case DEV_BAD_MESSAGE:
++		case DEV_GONE:
++		case REC_HARDWAR_ERR:
++		default:
++			rv = REC_NO_RESPONSE;
++			break;
++		}
++		if (rv)
++			break;
++		if (dev_ptr->dev_caller_count <= 0) {
++			rv = REC_USER_GONE;
++			break;
++	        }
++
++		list_for_each_safe(ptr, tptr, &dev_ptr->dev_caller_list) {
++			caller_p = list_entry(ptr, struct caller, caller_liste);
++			if (!memcmp(caller_p->caller_id, psmid,
++				    sizeof(caller_p->caller_id))) {
++				if (!list_empty(&caller_p->caller_liste)) {
++					list_del_init(ptr);
++					dev_ptr->dev_caller_count--;
++					break;
++				}
++			}
++			caller_p = 0;
++		}
++		if (!caller_p) {
++			PRINTKW("Unable to locate PSMID %02X%02X%02X%02X%02X"
++				"%02X%02X%02X in device list\n",
++				psmid[0], psmid[1], psmid[2], psmid[3],
++				psmid[4], psmid[5], psmid[6], psmid[7]);
++			rv = REC_USER_GONE;
++			break;
++		}
++
++		PDEBUG("caller_p after successful receive: %p\n", caller_p);
++		rv = convert_response(dev_ptr->dev_resp_p,
++				      caller_p->caller_buf_p, buff_len_p, buff);
++		switch (rv) {
++		case REC_USE_PCICA:
++			break;
++		case REC_OPERAND_INV:
++		case REC_OPERAND_SIZE:
++		case REC_EVEN_MOD:
++		case REC_INVALID_PAD:
++			PDEBUG("device %d: 'user error' %d\n", index, rv);
++			break;
++		case WRONG_DEVICE_TYPE:
++		case REC_HARDWAR_ERR:
++		case REC_BAD_MESSAGE:
++			PRINTKW("device %d: hardware error %d\n", index, rv);
++			rv = REC_NO_RESPONSE;
++			break;
++		default:
++			PDEBUG("device %d: rv = %d\n", index, rv);
++			break;
++		}
++	} while (0);
++
++	switch (rv) {
++	case 0:
++		PDEBUG("Successful receive from device %d\n", index);
++		icaMsg_p = (struct ica_rsa_modexpo *)caller_p->caller_buf_p;
++		*dest_p_p = icaMsg_p->outputdata;
++		if (*buff_len_p == 0)
++			PRINTK("Zero *buff_len_p\n");
++		break;
++	case REC_NO_RESPONSE:
++		PRINTKW("Removing device %d from availability\n", index);
++		remove_device(dev_ptr);
++		break;
++	}
++
++	if (caller_p)
++		unbuild_caller(dev_ptr, caller_p);
++
++	return rv;
++}
++
++static inline void
++helper_send_work(int index)
++{
++	struct work_element *rq_p;
++	int rv;
++
++	if (list_empty(&request_list))
++		return;
++	requestq_count--;
++	rq_p = list_entry(request_list.next, struct work_element, liste);
++	list_del_init(&rq_p->liste);
++	rq_p->audit[1] |= FP_REMREQUEST;
++	if (rq_p->devtype == SHRT2DEVPTR(index)->dev_type) {
++		rq_p->devindex = SHRT2LONG(index);
++		rv = send_to_crypto_device(rq_p);
++		if (rv == 0) {
++			rq_p->requestsent = jiffies;
++			rq_p->audit[0] |= FP_SENT;
++			list_add_tail(&rq_p->liste, &pending_list);
++			++pendingq_count;
++			rq_p->audit[0] |= FP_PENDING;
++		} else {
++			switch (rv) {
++			case REC_OPERAND_INV:
++			case REC_OPERAND_SIZE:
++			case REC_EVEN_MOD:
++			case REC_INVALID_PAD:
++				rq_p->retcode = -EINVAL;
++				break;
++			case SEN_NOT_AVAIL:
++			case SEN_RETRY:
++			case REC_NO_RESPONSE:
++			default:
++				if (z90crypt.mask.st_count > 1)
++					rq_p->retcode =
++						-ERESTARTSYS;
++				else
++					rq_p->retcode = -ENODEV;
++				break;
++			}
++			rq_p->status[0] |= STAT_FAILED;
++			rq_p->audit[1] |= FP_AWAKENING;
++			atomic_set(&rq_p->alarmrung, 1);
++			wake_up(&rq_p->waitq);
++		}
++	} else {
++		if (z90crypt.mask.st_count > 1)
++			rq_p->retcode = -ERESTARTSYS;
++		else
++			rq_p->retcode = -ENODEV;
++		rq_p->status[0] |= STAT_FAILED;
++		rq_p->audit[1] |= FP_AWAKENING;
++		atomic_set(&rq_p->alarmrung, 1);
++		wake_up(&rq_p->waitq);
++	}
++}
++
++static inline void
++helper_handle_work_element(int index, unsigned char psmid[8], int rc,
++			   int buff_len, unsigned char *buff,
++			   unsigned char __user *resp_addr)
++{
++	struct work_element *pq_p;
++	struct list_head *lptr, *tptr;
++
++	pq_p = 0;
++	list_for_each_safe(lptr, tptr, &pending_list) {
++		pq_p = list_entry(lptr, struct work_element, liste);
++		if (!memcmp(pq_p->caller_id, psmid, sizeof(pq_p->caller_id))) {
++			list_del_init(lptr);
++			pendingq_count--;
++			pq_p->audit[1] |= FP_NOTPENDING;
++			break;
++		}
++		pq_p = 0;
++	}
++
++	if (!pq_p) {
++		PRINTK("device %d has work but no caller exists on pending Q\n",
++		       SHRT2LONG(index));
++		return;
++	}
++
++	switch (rc) {
++		case 0:
++			pq_p->resp_buff_size = buff_len;
++			pq_p->audit[1] |= FP_RESPSIZESET;
++			if (buff_len) {
++				pq_p->resp_addr = resp_addr;
++				pq_p->audit[1] |= FP_RESPADDRCOPIED;
++				memcpy(pq_p->resp_buff, buff, buff_len);
++				pq_p->audit[1] |= FP_RESPBUFFCOPIED;
++			}
++			break;
++		case REC_OPERAND_INV:
++		case REC_OPERAND_SIZE:
++		case REC_EVEN_MOD:
++		case REC_INVALID_PAD:
++			PDEBUG("-EINVAL after application error %d\n", rc);
++			pq_p->retcode = -EINVAL;
++			pq_p->status[0] |= STAT_FAILED;
++			break;
++		case REC_USE_PCICA:
++			pq_p->retcode = -ERESTARTSYS;
++			pq_p->status[0] |= STAT_FAILED;
++			break;
++		case REC_NO_RESPONSE:
++		default:
++			if (z90crypt.mask.st_count > 1)
++				pq_p->retcode = -ERESTARTSYS;
++			else
++				pq_p->retcode = -ENODEV;
++			pq_p->status[0] |= STAT_FAILED;
++			break;
++	}
++	if ((pq_p->status[0] != STAT_FAILED) || (pq_p->retcode != -ERELEASED)) {
++		pq_p->audit[1] |= FP_AWAKENING;
++		atomic_set(&pq_p->alarmrung, 1);
++		wake_up(&pq_p->waitq);
++	}
++}
++
++/**
++ * return TRUE if the work element should be removed from the queue
++ */
++static inline int
++helper_receive_rc(int index, int *rc_p)
++{
++	switch (*rc_p) {
++	case 0:
++	case REC_OPERAND_INV:
++	case REC_OPERAND_SIZE:
++	case REC_EVEN_MOD:
++	case REC_INVALID_PAD:
++	case REC_USE_PCICA:
++		break;
++
++	case REC_BUSY:
++	case REC_NO_WORK:
++	case REC_EMPTY:
++	case REC_RETRY_DEV:
++	case REC_FATAL_ERROR:
++		return 0;
++
++	case REC_NO_RESPONSE:
++		break;
++
++	default:
++		PRINTK("rc %d, device %d converted to REC_NO_RESPONSE\n",
++		       *rc_p, SHRT2LONG(index));
++		*rc_p = REC_NO_RESPONSE;
++		break;
++	}
++	return 1;
++}
++
++static inline void
++z90crypt_schedule_reader_timer(void)
++{
++	if (timer_pending(&reader_timer))
++		return;
++	if (mod_timer(&reader_timer, jiffies+(READERTIME*HZ/1000)) != 0)
++		PRINTK("Timer pending while modifying reader timer\n");
++}
++
++static void
++z90crypt_reader_task(unsigned long ptr)
++{
++	int workavail, index, rc, buff_len;
++	unsigned char	psmid[8];
++	unsigned char __user *resp_addr;
++	static unsigned char buff[1024];
++
++	/**
++	 * we use workavail = 2 to ensure 2 passes with nothing dequeued before
++	 * exiting the loop. If (pendingq_count+requestq_count) == 0 after the
++	 * loop, there is no work remaining on the queues.
++	 */
++	resp_addr = 0;
++	workavail = 2;
++	buff_len = 0;
++	while (workavail) {
++		workavail--;
++		rc = 0;
++		spin_lock_irq(&queuespinlock);
++		memset(buff, 0x00, sizeof(buff));
++
++		/* Dequeue once from each device in round robin. */
++		for (index = 0; index < z90crypt.mask.st_count; index++) {
++			PDEBUG("About to receive.\n");
++			rc = receive_from_crypto_device(SHRT2LONG(index),
++							psmid,
++							&buff_len,
++							buff,
++							&resp_addr);
++			PDEBUG("Dequeued: rc = %d.\n", rc);
++
++			if (helper_receive_rc(index, &rc)) {
++				if (rc != REC_NO_RESPONSE) {
++					helper_send_work(index);
++					workavail = 2;
++				}
++
++				helper_handle_work_element(index, psmid, rc,
++							   buff_len, buff,
++							   resp_addr);
++			}
++
++			if (rc == REC_FATAL_ERROR)
++				PRINTKW("REC_FATAL_ERROR from device %d!\n",
++					SHRT2LONG(index));
++		}
++		spin_unlock_irq(&queuespinlock);
++	}
++
++	if (pendingq_count + requestq_count)
++		z90crypt_schedule_reader_timer();
++}
++
++static inline void
++z90crypt_schedule_config_task(unsigned int expiration)
++{
++	if (timer_pending(&config_timer))
++		return;
++	if (mod_timer(&config_timer, jiffies+(expiration*HZ)) != 0)
++		PRINTK("Timer pending while modifying config timer\n");
++}
++
++static void
++z90crypt_config_task(unsigned long ptr)
++{
++	int rc;
++
++	PDEBUG("jiffies %ld\n", jiffies);
++
++	if ((rc = refresh_z90crypt(&z90crypt.cdx)))
++		PRINTK("Error %d detected in refresh_z90crypt.\n", rc);
++	/* If return was fatal, don't bother reconfiguring */
++	if ((rc != TSQ_FATAL_ERROR) && (rc != RSQ_FATAL_ERROR))
++		z90crypt_schedule_config_task(CONFIGTIME);
++}
++
++static inline void
++z90crypt_schedule_cleanup_task(void)
++{
++	if (timer_pending(&cleanup_timer))
++		return;
++	if (mod_timer(&cleanup_timer, jiffies+(CLEANUPTIME*HZ)) != 0)
++		PRINTK("Timer pending while modifying cleanup timer\n");
++}
++
++static inline void
++helper_drain_queues(void)
++{
++	struct work_element *pq_p;
++	struct list_head *lptr, *tptr;
++
++	list_for_each_safe(lptr, tptr, &pending_list) {
++		pq_p = list_entry(lptr, struct work_element, liste);
++		pq_p->retcode = -ENODEV;
++		pq_p->status[0] |= STAT_FAILED;
++		unbuild_caller(LONG2DEVPTR(pq_p->devindex),
++			       (struct caller *)pq_p->requestptr);
++		list_del_init(lptr);
++		pendingq_count--;
++		pq_p->audit[1] |= FP_NOTPENDING;
++		pq_p->audit[1] |= FP_AWAKENING;
++		atomic_set(&pq_p->alarmrung, 1);
++		wake_up(&pq_p->waitq);
++	}
++
++	list_for_each_safe(lptr, tptr, &request_list) {
++		pq_p = list_entry(lptr, struct work_element, liste);
++		pq_p->retcode = -ENODEV;
++		pq_p->status[0] |= STAT_FAILED;
++		list_del_init(lptr);
++		requestq_count--;
++		pq_p->audit[1] |= FP_REMREQUEST;
++		pq_p->audit[1] |= FP_AWAKENING;
++		atomic_set(&pq_p->alarmrung, 1);
++		wake_up(&pq_p->waitq);
++	}
++}
++
++static inline void
++helper_timeout_requests(void)
++{
++	struct work_element *pq_p;
++	struct list_head *lptr, *tptr;
++	long timelimit;
++
++	timelimit = jiffies - (CLEANUPTIME * HZ);
++	/* The list is in strict chronological order */
++	list_for_each_safe(lptr, tptr, &pending_list) {
++		pq_p = list_entry(lptr, struct work_element, liste);
++		if (pq_p->requestsent >= timelimit)
++			break;
++		PRINTKW("Purging(PQ) PSMID %02X%02X%02X%02X%02X%02X%02X%02X\n",
++		       ((struct caller *)pq_p->requestptr)->caller_id[0],
++		       ((struct caller *)pq_p->requestptr)->caller_id[1],
++		       ((struct caller *)pq_p->requestptr)->caller_id[2],
++		       ((struct caller *)pq_p->requestptr)->caller_id[3],
++		       ((struct caller *)pq_p->requestptr)->caller_id[4],
++		       ((struct caller *)pq_p->requestptr)->caller_id[5],
++		       ((struct caller *)pq_p->requestptr)->caller_id[6],
++		       ((struct caller *)pq_p->requestptr)->caller_id[7]);
++		pq_p->retcode = -ETIMEOUT;
++		pq_p->status[0] |= STAT_FAILED;
++		/* get this off any caller queue it may be on */
++		unbuild_caller(LONG2DEVPTR(pq_p->devindex),
++			       (struct caller *) pq_p->requestptr);
++		list_del_init(lptr);
++		pendingq_count--;
++		pq_p->audit[1] |= FP_TIMEDOUT;
++		pq_p->audit[1] |= FP_NOTPENDING;
++		pq_p->audit[1] |= FP_AWAKENING;
++		atomic_set(&pq_p->alarmrung, 1);
++		wake_up(&pq_p->waitq);
++	}
++
++	/**
++	 * If pending count is zero, items left on the request queue may
++	 * never be processed.
++	 */
++	if (pendingq_count <= 0) {
++		list_for_each_safe(lptr, tptr, &request_list) {
++			pq_p = list_entry(lptr, struct work_element, liste);
++			if (pq_p->requestsent >= timelimit)
++				break;
++		PRINTKW("Purging(RQ) PSMID %02X%02X%02X%02X%02X%02X%02X%02X\n",
++		       ((struct caller *)pq_p->requestptr)->caller_id[0],
++		       ((struct caller *)pq_p->requestptr)->caller_id[1],
++		       ((struct caller *)pq_p->requestptr)->caller_id[2],
++		       ((struct caller *)pq_p->requestptr)->caller_id[3],
++		       ((struct caller *)pq_p->requestptr)->caller_id[4],
++		       ((struct caller *)pq_p->requestptr)->caller_id[5],
++		       ((struct caller *)pq_p->requestptr)->caller_id[6],
++		       ((struct caller *)pq_p->requestptr)->caller_id[7]);
++			pq_p->retcode = -ETIMEOUT;
++			pq_p->status[0] |= STAT_FAILED;
++			list_del_init(lptr);
++			requestq_count--;
++			pq_p->audit[1] |= FP_TIMEDOUT;
++			pq_p->audit[1] |= FP_REMREQUEST;
++			pq_p->audit[1] |= FP_AWAKENING;
++			atomic_set(&pq_p->alarmrung, 1);
++			wake_up(&pq_p->waitq);
++		}
++	}
++}
++
++static void
++z90crypt_cleanup_task(unsigned long ptr)
++{
++	PDEBUG("jiffies %ld\n", jiffies);
++	spin_lock_irq(&queuespinlock);
++	if (z90crypt.mask.st_count <= 0) // no devices!
++		helper_drain_queues();
++	else
++		helper_timeout_requests();
++	spin_unlock_irq(&queuespinlock);
++	z90crypt_schedule_cleanup_task();
++}
++
++static void
++z90crypt_schedule_reader_task(unsigned long ptr)
++{
++	tasklet_schedule(&reader_tasklet);
++}
++
++/**
++ * Lowlevel Functions:
++ *
++ *   create_z90crypt:  creates and initializes basic data structures
++ *   refresh_z90crypt:	re-initializes basic data structures
++ *   find_crypto_devices: returns a count and mask of hardware status
++ *   create_crypto_device:  builds the descriptor for a device
++ *   destroy_crypto_device:  unallocates the descriptor for a device
++ *   destroy_z90crypt:	drains all work, unallocates structs
++ */
++
++/**
++ * build the z90crypt root structure using the given domain index
++ */
++static int
++create_z90crypt(int *cdx_p)
++{
++	struct hdware_block *hdware_blk_p;
++
++	memset(&z90crypt, 0x00, sizeof(struct z90crypt));
++	z90crypt.domain_established = 0;
++	z90crypt.len = sizeof(struct z90crypt);
++	z90crypt.max_count = Z90CRYPT_NUM_DEVS;
++	z90crypt.cdx = *cdx_p;
++
++	hdware_blk_p = (struct hdware_block *)
++		kmalloc(sizeof(struct hdware_block), GFP_ATOMIC);
++	if (!hdware_blk_p) {
++		PDEBUG("kmalloc for hardware block failed\n");
++		return ENOMEM;
++	}
++	memset(hdware_blk_p, 0x00, sizeof(struct hdware_block));
++	z90crypt.hdware_info = hdware_blk_p;
++
++	return 0;
++}
++
++static inline int
++helper_scan_devices(int cdx_array[16], int *cdx_p, int *correct_cdx_found)
++{
++	enum hdstat hd_stat;
++	int q_depth, dev_type;
++	int indx, chkdom, numdomains;
++
++	q_depth = dev_type = numdomains = 0;
++	for (chkdom = 0; chkdom <= 15; cdx_array[chkdom++] = -1);
++	for (indx = 0; indx < z90crypt.max_count; indx++) {
++		hd_stat = HD_NOT_THERE;
++		numdomains = 0;
++		for (chkdom = 0; chkdom <= 15; chkdom++) {
++			hd_stat = query_online(indx, chkdom, MAX_RESET,
++					       &q_depth, &dev_type);
++			if (hd_stat == HD_TSQ_EXCEPTION) {
++				z90crypt.terminating = 1;
++				PRINTKC("exception taken!\n");
++				break;
++			}
++			if (hd_stat == HD_ONLINE) {
++				cdx_array[numdomains++] = chkdom;
++				if (*cdx_p == chkdom) {
++					*correct_cdx_found  = 1;
++					break;
++				}
++			}
++		}
++		if ((*correct_cdx_found == 1) || (numdomains != 0))
++			break;
++		if (z90crypt.terminating)
++			break;
++	}
++	return numdomains;
++}
++
++static inline int
++probe_crypto_domain(int *cdx_p)
++{
++	int cdx_array[16];
++	char cdx_array_text[53], temp[5];
++	int correct_cdx_found, numdomains;
++
++	correct_cdx_found = 0;
++	numdomains = helper_scan_devices(cdx_array, cdx_p, &correct_cdx_found);
++
++	if (z90crypt.terminating)
++		return TSQ_FATAL_ERROR;
++
++	if (correct_cdx_found)
++		return 0;
++
++	if (numdomains == 0) {
++		PRINTKW("Unable to find crypto domain: No devices found\n");
++		return Z90C_NO_DEVICES;
++	}
++
++	if (numdomains == 1) {
++		if (*cdx_p == -1) {
++			*cdx_p = cdx_array[0];
++			return 0;
++		}
++		PRINTKW("incorrect domain: specified = %d, found = %d\n",
++		       *cdx_p, cdx_array[0]);
++		return Z90C_INCORRECT_DOMAIN;
++	}
++
++	numdomains--;
++	sprintf(cdx_array_text, "%d", cdx_array[numdomains]);
++	while (numdomains) {
++		numdomains--;
++		sprintf(temp, ", %d", cdx_array[numdomains]);
++		strcat(cdx_array_text, temp);
++	}
++
++	PRINTKW("ambiguous domain detected: specified = %d, found array = %s\n",
++		*cdx_p, cdx_array_text);
++	return Z90C_AMBIGUOUS_DOMAIN;
++}
++
++static int
++refresh_z90crypt(int *cdx_p)
++{
++	int i, j, indx, rv;
++	struct status local_mask;
++	struct device *devPtr;
++	unsigned char oldStat, newStat;
++	int return_unchanged;
++
++	if (z90crypt.len != sizeof(z90crypt))
++		return ENOTINIT;
++	if (z90crypt.terminating)
++		return TSQ_FATAL_ERROR;
++	rv = 0;
++	if (!z90crypt.hdware_info->hdware_mask.st_count &&
++	    !z90crypt.domain_established) {
++		rv = probe_crypto_domain(cdx_p);
++		if (z90crypt.terminating)
++			return TSQ_FATAL_ERROR;
++		if (rv == Z90C_NO_DEVICES)
++			return 0; // try later
++		if (rv)
++			return rv;
++		z90crypt.cdx = *cdx_p;
++		z90crypt.domain_established = 1;
++	}
++	rv = find_crypto_devices(&local_mask);
++	if (rv) {
++		PRINTK("find crypto devices returned %d\n", rv);
++		return rv;
++	}
++	if (!memcmp(&local_mask, &z90crypt.hdware_info->hdware_mask,
++		    sizeof(struct status))) {
++		return_unchanged = 1;
++		for (i = 0; i < Z90CRYPT_NUM_TYPES; i++) {
++			/**
++			 * Check for disabled cards.  If any device is marked
++			 * disabled, destroy it.
++			 */
++			for (j = 0;
++			     j < z90crypt.hdware_info->type_mask[i].st_count;
++			     j++) {
++				indx = z90crypt.hdware_info->type_x_addr[i].
++								device_index[j];
++				devPtr = z90crypt.device_p[indx];
++				if (devPtr && devPtr->disabled) {
++					local_mask.st_mask[indx] = HD_NOT_THERE;
++					return_unchanged = 0;
++				}
++			}
++		}
++		if (return_unchanged == 1)
++			return 0;
++	}
++
++	spin_lock_irq(&queuespinlock);
++	for (i = 0; i < z90crypt.max_count; i++) {
++		oldStat = z90crypt.hdware_info->hdware_mask.st_mask[i];
++		newStat = local_mask.st_mask[i];
++		if ((oldStat == HD_ONLINE) && (newStat != HD_ONLINE))
++			destroy_crypto_device(i);
++		else if ((oldStat != HD_ONLINE) && (newStat == HD_ONLINE)) {
++			rv = create_crypto_device(i);
++			if (rv >= REC_FATAL_ERROR)
++				return rv;
++			if (rv != 0) {
++				local_mask.st_mask[i] = HD_NOT_THERE;
++				local_mask.st_count--;
++			}
++		}
++	}
++	memcpy(z90crypt.hdware_info->hdware_mask.st_mask, local_mask.st_mask,
++	       sizeof(local_mask.st_mask));
++	z90crypt.hdware_info->hdware_mask.st_count = local_mask.st_count;
++	z90crypt.hdware_info->hdware_mask.disabled_count =
++						      local_mask.disabled_count;
++	refresh_index_array(&z90crypt.mask, &z90crypt.overall_device_x);
++	for (i = 0; i < Z90CRYPT_NUM_TYPES; i++)
++		refresh_index_array(&(z90crypt.hdware_info->type_mask[i]),
++				    &(z90crypt.hdware_info->type_x_addr[i]));
++	spin_unlock_irq(&queuespinlock);
++
++	return rv;
++}
++
++static int
++find_crypto_devices(struct status *deviceMask)
++{
++	int i, q_depth, dev_type;
++	enum hdstat hd_stat;
++
++	deviceMask->st_count = 0;
++	deviceMask->disabled_count = 0;
++	deviceMask->user_disabled_count = 0;
++
++	for (i = 0; i < z90crypt.max_count; i++) {
++		hd_stat = query_online(i, z90crypt.cdx, MAX_RESET, &q_depth,
++				       &dev_type);
++		if (hd_stat == HD_TSQ_EXCEPTION) {
++			z90crypt.terminating = 1;
++			PRINTKC("Exception during probe for crypto devices\n");
++			return TSQ_FATAL_ERROR;
++		}
++		deviceMask->st_mask[i] = hd_stat;
++		if (hd_stat == HD_ONLINE) {
++			PDEBUG("Got an online crypto!: %d\n", i);
++			PDEBUG("Got a queue depth of %d\n", q_depth);
++			PDEBUG("Got a device type of %d\n", dev_type);
++			if (q_depth <= 0)
++				return TSQ_FATAL_ERROR;
++			deviceMask->st_count++;
++			z90crypt.q_depth_array[i] = q_depth;
++			z90crypt.dev_type_array[i] = dev_type;
++		}
++	}
++
++	return 0;
++}
++
++static int
++refresh_index_array(struct status *status_str, struct device_x *index_array)
++{
++	int i, count;
++	enum devstat stat;
++
++	i = -1;
++	count = 0;
++	do {
++		stat = status_str->st_mask[++i];
++		if (stat == DEV_ONLINE)
++			index_array->device_index[count++] = i;
++	} while ((i < Z90CRYPT_NUM_DEVS) && (count < status_str->st_count));
++
++	return count;
++}
++
++static int
++create_crypto_device(int index)
++{
++	int rv, devstat, total_size;
++	struct device *dev_ptr;
++	struct status *type_str_p;
++	int deviceType;
++
++	dev_ptr = z90crypt.device_p[index];
++	if (!dev_ptr) {
++		total_size = sizeof(struct device) +
++			     z90crypt.q_depth_array[index] * sizeof(int);
++
++		dev_ptr = (struct device *) kmalloc(total_size, GFP_ATOMIC);
++		if (!dev_ptr) {
++			PRINTK("kmalloc device %d failed\n", index);
++			return ENOMEM;
++		}
++		memset(dev_ptr, 0, total_size);
++		dev_ptr->dev_resp_p = kmalloc(MAX_RESPONSE_SIZE, GFP_ATOMIC);
++		if (!dev_ptr->dev_resp_p) {
++			kfree(dev_ptr);
++			PRINTK("kmalloc device %d rec buffer failed\n", index);
++			return ENOMEM;
++		}
++		dev_ptr->dev_resp_l = MAX_RESPONSE_SIZE;
++		INIT_LIST_HEAD(&(dev_ptr->dev_caller_list));
++	}
++
++	devstat = reset_device(index, z90crypt.cdx, MAX_RESET);
++	if (devstat == DEV_RSQ_EXCEPTION) {
++		PRINTK("exception during reset device %d\n", index);
++		kfree(dev_ptr->dev_resp_p);
++		kfree(dev_ptr);
++		return RSQ_FATAL_ERROR;
++	}
++	if (devstat == DEV_ONLINE) {
++		dev_ptr->dev_self_x = index;
++		dev_ptr->dev_type = z90crypt.dev_type_array[index];
++		if (dev_ptr->dev_type == NILDEV) {
++			rv = probe_device_type(dev_ptr);
++			if (rv) {
++				PRINTK("rv = %d from probe_device_type %d\n",
++				       rv, index);
++				kfree(dev_ptr->dev_resp_p);
++				kfree(dev_ptr);
++				return rv;
++			}
++		}
++		if (dev_ptr->dev_type == PCIXCC_UNK) {
++			rv = probe_PCIXCC_type(dev_ptr);
++			if (rv) {
++				PRINTK("rv = %d from probe_PCIXCC_type %d\n",
++				       rv, index);
++				kfree(dev_ptr->dev_resp_p);
++				kfree(dev_ptr);
++				return rv;
++			}
++		}
++		deviceType = dev_ptr->dev_type;
++		z90crypt.dev_type_array[index] = deviceType;
++		if (deviceType == PCICA)
++			z90crypt.hdware_info->device_type_array[index] = 1;
++		else if (deviceType == PCICC)
++			z90crypt.hdware_info->device_type_array[index] = 2;
++		else if (deviceType == PCIXCC_MCL2)
++			z90crypt.hdware_info->device_type_array[index] = 3;
++		else if (deviceType == PCIXCC_MCL3)
++			z90crypt.hdware_info->device_type_array[index] = 4;
++		else if (deviceType == CEX2C)
++			z90crypt.hdware_info->device_type_array[index] = 5;
++		else
++			z90crypt.hdware_info->device_type_array[index] = -1;
++	}
++
++	/**
++	 * 'q_depth' returned by the hardware is one less than
++	 * the actual depth
++	 */
++	dev_ptr->dev_q_depth = z90crypt.q_depth_array[index];
++	dev_ptr->dev_type = z90crypt.dev_type_array[index];
++	dev_ptr->dev_stat = devstat;
++	dev_ptr->disabled = 0;
++	z90crypt.device_p[index] = dev_ptr;
++
++	if (devstat == DEV_ONLINE) {
++		if (z90crypt.mask.st_mask[index] != DEV_ONLINE) {
++			z90crypt.mask.st_mask[index] = DEV_ONLINE;
++			z90crypt.mask.st_count++;
++		}
++		deviceType = dev_ptr->dev_type;
++		type_str_p = &z90crypt.hdware_info->type_mask[deviceType];
++		if (type_str_p->st_mask[index] != DEV_ONLINE) {
++			type_str_p->st_mask[index] = DEV_ONLINE;
++			type_str_p->st_count++;
++		}
++	}
++
++	return 0;
++}
++
++static int
++destroy_crypto_device(int index)
++{
++	struct device *dev_ptr;
++	int t, disabledFlag;
++
++	dev_ptr = z90crypt.device_p[index];
++
++	/* remember device type; get rid of device struct */
++	if (dev_ptr) {
++		disabledFlag = dev_ptr->disabled;
++		t = dev_ptr->dev_type;
++		if (dev_ptr->dev_resp_p)
++			kfree(dev_ptr->dev_resp_p);
++		kfree(dev_ptr);
++	} else {
++		disabledFlag = 0;
++		t = -1;
++	}
++	z90crypt.device_p[index] = 0;
++
++	/* if the type is valid, remove the device from the type_mask */
++	if ((t != -1) && z90crypt.hdware_info->type_mask[t].st_mask[index]) {
++		  z90crypt.hdware_info->type_mask[t].st_mask[index] = 0x00;
++		  z90crypt.hdware_info->type_mask[t].st_count--;
++		  if (disabledFlag == 1)
++			z90crypt.hdware_info->type_mask[t].disabled_count--;
++	}
++	if (z90crypt.mask.st_mask[index] != DEV_GONE) {
++		z90crypt.mask.st_mask[index] = DEV_GONE;
++		z90crypt.mask.st_count--;
++	}
++	z90crypt.hdware_info->device_type_array[index] = 0;
++
++	return 0;
++}
++
++static void
++destroy_z90crypt(void)
++{
++	int i;
++	for (i = 0; i < z90crypt.max_count; i++)
++		if (z90crypt.device_p[i])
++			destroy_crypto_device(i);
++	if (z90crypt.hdware_info)
++		kfree((void *)z90crypt.hdware_info);
++	memset((void *)&z90crypt, 0, sizeof(z90crypt));
++}
++
++static unsigned char static_testmsg[384] = {
++0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x06,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x58,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x43,
++0x41,0x2d,0x41,0x50,0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01,0x00,0x00,0x00,0x00,
++0x50,0x4b,0x00,0x00,0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x32,
++0x01,0x00,0xa0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0xb8,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x49,0x43,0x53,0x46,
++0x20,0x20,0x20,0x20,0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53,0x2d,0x31,0x2e,0x32,
++0x37,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,
++0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
++0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,
++0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77,0x88,0x1e,0x00,0x00,
++0x57,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00,0x03,0x02,0x00,0x00,
++0x40,0x01,0x00,0x01,0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c,0xf6,0xd2,0x7b,0x58,
++0x4b,0xf9,0x28,0x68,0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66,0x63,0x42,0xef,0xf8,
++0xfd,0xa4,0xf8,0xb0,0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8,0x53,0x8c,0x6f,0x4e,
++0x72,0x8f,0x6c,0x04,0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57,0xf7,0xdd,0xfd,0x4f,
++0x11,0x36,0x95,0x5d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
++};
++
++static int
++probe_device_type(struct device *devPtr)
++{
++	int rv, dv, i, index, length;
++	unsigned char psmid[8];
++	static unsigned char loc_testmsg[sizeof(static_testmsg)];
++
++	index = devPtr->dev_self_x;
++	rv = 0;
++	do {
++		memcpy(loc_testmsg, static_testmsg, sizeof(static_testmsg));
++		length = sizeof(static_testmsg) - 24;
++		/* the -24 allows for the header */
++		dv = send_to_AP(index, z90crypt.cdx, length, loc_testmsg);
++		if (dv) {
++			PDEBUG("dv returned by send during probe: %d\n", dv);
++			if (dv == DEV_SEN_EXCEPTION) {
++				rv = SEN_FATAL_ERROR;
++				PRINTKC("exception in send to AP %d\n", index);
++				break;
++			}
++			PDEBUG("return value from send_to_AP: %d\n", rv);
++			switch (dv) {
++			case DEV_GONE:
++				PDEBUG("dev %d not available\n", index);
++				rv = SEN_NOT_AVAIL;
++				break;
++			case DEV_ONLINE:
++				rv = 0;
++				break;
++			case DEV_EMPTY:
++				rv = SEN_NOT_AVAIL;
++				break;
++			case DEV_NO_WORK:
++				rv = SEN_FATAL_ERROR;
++				break;
++			case DEV_BAD_MESSAGE:
++				rv = SEN_USER_ERROR;
++				break;
++			case DEV_QUEUE_FULL:
++				rv = SEN_QUEUE_FULL;
++				break;
++			default:
++				PRINTK("unknown dv=%d for dev %d\n", dv, index);
++				rv = SEN_NOT_AVAIL;
++				break;
++			}
++		}
++
++		if (rv)
++			break;
++
++		for (i = 0; i < 6; i++) {
++			mdelay(300);
++			dv = receive_from_AP(index, z90crypt.cdx,
++					     devPtr->dev_resp_l,
++					     devPtr->dev_resp_p, psmid);
++			PDEBUG("dv returned by DQ = %d\n", dv);
++			if (dv == DEV_REC_EXCEPTION) {
++				rv = REC_FATAL_ERROR;
++				PRINTKC("exception in dequeue %d\n",
++					index);
++				break;
++			}
++			switch (dv) {
++			case DEV_ONLINE:
++				rv = 0;
++				break;
++			case DEV_EMPTY:
++				rv = REC_EMPTY;
++				break;
++			case DEV_NO_WORK:
++				rv = REC_NO_WORK;
++				break;
++			case DEV_BAD_MESSAGE:
++			case DEV_GONE:
++			default:
++				rv = REC_NO_RESPONSE;
++				break;
++			}
++			if ((rv != 0) && (rv != REC_NO_WORK))
++				break;
++			if (rv == 0)
++				break;
++		}
++		if (rv)
++			break;
++		rv = (devPtr->dev_resp_p[0] == 0x00) &&
++		     (devPtr->dev_resp_p[1] == 0x86);
++		if (rv)
++			devPtr->dev_type = PCICC;
++		else
++			devPtr->dev_type = PCICA;
++		rv = 0;
++	} while (0);
++	/* In a general error case, the card is not marked online */
++	return rv;
++}
++
++static unsigned char MCL3_testmsg[] = {
++0x00,0x00,0x00,0x00,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,
++0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x43,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x00,0x00,0x00,0x01,0xC4,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xDC,0x02,0x00,0x00,0x00,0x54,0x32,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE8,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x24,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++0x00,0x00,0x00,0x00,0x50,0x4B,0x00,0x0A,0x4D,0x52,0x50,0x20,0x20,0x20,0x20,0x20,
++0x00,0x42,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,
++0x0E,0x0F,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,
++0xEE,0xFF,0xFF,0xEE,0xDD,0xCC,0xBB,0xAA,0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22,
++0x11,0x00,0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,0xFE,0xDC,0xBA,0x98,0x76,0x54,
++0x32,0x10,0x00,0x9A,0x00,0x98,0x00,0x00,0x1E,0x00,0x00,0x94,0x00,0x00,0x00,0x00,
++0x04,0x00,0x00,0x8C,0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x40,0xBA,0xE8,0x23,0x3C,
++0x75,0xF3,0x91,0x61,0xD6,0x73,0x39,0xCF,0x7B,0x6D,0x8E,0x61,0x97,0x63,0x9E,0xD9,
++0x60,0x55,0xD6,0xC7,0xEF,0xF8,0x1E,0x63,0x95,0x17,0xCC,0x28,0x45,0x60,0x11,0xC5,
++0xC4,0x4E,0x66,0xC6,0xE6,0xC3,0xDE,0x8A,0x19,0x30,0xCF,0x0E,0xD7,0xAA,0xDB,0x01,
++0xD8,0x00,0xBB,0x8F,0x39,0x9F,0x64,0x28,0xF5,0x7A,0x77,0x49,0xCC,0x6B,0xA3,0x91,
++0x97,0x70,0xE7,0x60,0x1E,0x39,0xE1,0xE5,0x33,0xE1,0x15,0x63,0x69,0x08,0x80,0x4C,
++0x67,0xC4,0x41,0x8F,0x48,0xDF,0x26,0x98,0xF1,0xD5,0x8D,0x88,0xD9,0x6A,0xA4,0x96,
++0xC5,0x84,0xD9,0x30,0x49,0x67,0x7D,0x19,0xB1,0xB3,0x45,0x4D,0xB2,0x53,0x9A,0x47,
++0x3C,0x7C,0x55,0xBF,0xCC,0x85,0x00,0x36,0xF1,0x3D,0x93,0x53
++};
++
++static int
++probe_PCIXCC_type(struct device *devPtr)
++{
++	int rv, dv, i, index, length;
++	unsigned char psmid[8];
++	static unsigned char loc_testmsg[548];
++	struct CPRBX *cprbx_p;
++
++	index = devPtr->dev_self_x;
++	rv = 0;
++	do {
++		memcpy(loc_testmsg, MCL3_testmsg, sizeof(MCL3_testmsg));
++		length = sizeof(MCL3_testmsg) - 0x0C;
++		dv = send_to_AP(index, z90crypt.cdx, length, loc_testmsg);
++		if (dv) {
++			PDEBUG("dv returned = %d\n", dv);
++			if (dv == DEV_SEN_EXCEPTION) {
++				rv = SEN_FATAL_ERROR;
++				PRINTKC("exception in send to AP %d\n", index);
++				break;
++			}
++			PDEBUG("return value from send_to_AP: %d\n", rv);
++			switch (dv) {
++			case DEV_GONE:
++				PDEBUG("dev %d not available\n", index);
++				rv = SEN_NOT_AVAIL;
++				break;
++			case DEV_ONLINE:
++				rv = 0;
++				break;
++			case DEV_EMPTY:
++				rv = SEN_NOT_AVAIL;
++				break;
++			case DEV_NO_WORK:
++				rv = SEN_FATAL_ERROR;
++				break;
++			case DEV_BAD_MESSAGE:
++				rv = SEN_USER_ERROR;
++				break;
++			case DEV_QUEUE_FULL:
++				rv = SEN_QUEUE_FULL;
++				break;
++			default:
++				PRINTK("unknown dv=%d for dev %d\n", dv, index);
++				rv = SEN_NOT_AVAIL;
++				break;
++			}
++		}
++
++		if (rv)
++			break;
++
++		for (i = 0; i < 6; i++) {
++			mdelay(300);
++			dv = receive_from_AP(index, z90crypt.cdx,
++					     devPtr->dev_resp_l,
++					     devPtr->dev_resp_p, psmid);
++			PDEBUG("dv returned by DQ = %d\n", dv);
++			if (dv == DEV_REC_EXCEPTION) {
++				rv = REC_FATAL_ERROR;
++				PRINTKC("exception in dequeue %d\n",
++					index);
++				break;
++			}
++			switch (dv) {
++			case DEV_ONLINE:
++				rv = 0;
++				break;
++			case DEV_EMPTY:
++				rv = REC_EMPTY;
++				break;
++			case DEV_NO_WORK:
++				rv = REC_NO_WORK;
++				break;
++			case DEV_BAD_MESSAGE:
++			case DEV_GONE:
++			default:
++				rv = REC_NO_RESPONSE;
++				break;
++			}
++			if ((rv != 0) && (rv != REC_NO_WORK))
++				break;
++			if (rv == 0)
++				break;
++		}
++		if (rv)
++			break;
++		cprbx_p = (struct CPRBX *) (devPtr->dev_resp_p + 48);
++		if ((cprbx_p->ccp_rtcode == 8) && (cprbx_p->ccp_rscode == 33)) {
++			devPtr->dev_type = PCIXCC_MCL2;
++			PDEBUG("device %d is MCL2\n", index);
++		} else {
++			devPtr->dev_type = PCIXCC_MCL3;
++			PDEBUG("device %d is MCL3\n", index);
++		}
++	} while (0);
++	/* In a general error case, the card is not marked online */
++	return rv;
++}
++
++#ifdef Z90CRYPT_USE_HOTPLUG
++static void
++z90crypt_hotplug_event(int dev_major, int dev_minor, int action)
++{
++#ifdef CONFIG_HOTPLUG
++	char *argv[3];
++	char *envp[6];
++	char  major[20];
++	char  minor[20];
++
++	sprintf(major, "MAJOR=%d", dev_major);
++	sprintf(minor, "MINOR=%d", dev_minor);
++
++	argv[0] = hotplug_path;
++	argv[1] = "z90crypt";
++	argv[2] = 0;
++
++	envp[0] = "HOME=/";
++	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
++
++	switch (action) {
++	case Z90CRYPT_HOTPLUG_ADD:
++		envp[2] = "ACTION=add";
++		break;
++	case Z90CRYPT_HOTPLUG_REMOVE:
++		envp[2] = "ACTION=remove";
++		break;
++	default:
++		BUG();
++		break;
++	}
++	envp[3] = major;
++	envp[4] = minor;
++	envp[5] = 0;
++
++	call_usermodehelper(argv[0], argv, envp);
++#endif
++}
++#endif
++
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/ctcmain.c	2006-01-30 22:25:25.000000000 -0700
+@@ -1,5 +1,5 @@
+ /*
+- * $Id: ctcmain.c,v 1.63 2003/10/22 19:32:57 felfert Exp $
++ * $Id: ctcmain.c,v 1.59.4.3 2003/10/22 20:14:47 felfert Exp $
+  *
+  * CTC / ESCON network driver
+  *
+@@ -35,7 +35,7 @@
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  *
+- * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.63 $
++ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.59.4.3 $
+  *
+  */
+ 
+@@ -419,7 +419,7 @@
+  */
+ static void print_banner(void) {
+ 	static int printed = 0;
+-	char vbuf[] = "$Revision: 1.63 $";
++	char vbuf[] = "$Revision: 1.59.4.3 $";
+ 	char *version = vbuf;
+ 
+ 	if (printed)
+@@ -2934,7 +2934,6 @@
+ 	file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL);
+ 	if (file->private_data == NULL)
+ 		return -ENOMEM;
+-	*(char *)file->private_data = '\0';
+ 	MOD_INC_USE_COUNT;
+ 	return 0;
+ }
+@@ -3000,7 +2999,6 @@
+ 	ctc_priv *privptr;
+ 	ssize_t ret = 0;
+ 	char *p = sbuf;
+-	loff_t pos = *off;
+ 	int l;
+ 
+ 	if (!(dev = find_netdev_by_ino(ino)))
+@@ -3010,19 +3008,19 @@
+ 
+ 	privptr = (ctc_priv *)dev->priv;
+ 
+-	if (!*sbuf || pos == 0)
++	if (file->f_pos == 0)
+ 		sprintf(sbuf, "%d\n", privptr->channel[READ]->max_bufsize);
+ 
+ 	l = strlen(sbuf);
+ 	p = sbuf;
+-	if (pos == (unsigned)pos && pos < l) {
+-		p += pos;
++	if (file->f_pos < l) {
++		p += file->f_pos;
+ 		l = strlen(p);
+ 		ret = (count > l) ? l : count;
+ 		if (copy_to_user(buf, p, ret))
+ 			return -EFAULT;
+-		*off = pos + ret;
+ 	}
++	file->f_pos += ret;
+ 	return ret;
+ }
+ 
+@@ -3086,7 +3084,6 @@
+ 	ctc_priv *privptr;
+ 	ssize_t ret = 0;
+ 	char *p = sbuf;
+-	loff_t pos = *off;
+ 	int l;
+ 
+ 	if (!(dev = find_netdev_by_ino(ino)))
+@@ -3096,19 +3093,19 @@
+ 
+ 	privptr = (ctc_priv *)dev->priv;
+ 
+-	if (!*sbus || pos == 0)
++	if (file->f_pos == 0)
+ 		sprintf(sbuf, "0x%02x\n", loglevel);
+ 
+ 	l = strlen(sbuf);
+ 	p = sbuf;
+-	if (pos == (unsigned)pos && pos < l) {
+-		p += pos;
++	if (file->f_pos < l) {
++		p += file->f_pos;
+ 		l = strlen(p);
+ 		ret = (count > l) ? l : count;
+ 		if (copy_to_user(buf, p, ret))
+ 			return -EFAULT;
+-		*off = pos + ret;
+ 	}
++	file->f_pos += ret;
+ 	return ret;
+ }
+ 
+@@ -3119,7 +3116,6 @@
+ 	file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL);
+ 	if (file->private_data == NULL)
+ 		return -ENOMEM;
+-	*(char *)file->private_data = '\0';
+ 	MOD_INC_USE_COUNT;
+ 	return 0;
+ }
+@@ -3159,7 +3155,6 @@
+ 	ctc_priv *privptr;
+ 	ssize_t ret = 0;
+ 	char *p = sbuf;
+-	loff_t pos = *off;
+ 	int l;
+ 
+ 	if (!(dev = find_netdev_by_ino(ino)))
+@@ -3169,7 +3164,7 @@
+ 
+ 	privptr = (ctc_priv *)dev->priv;
+ 
+-	if (!*sbus || pos == 0) {
++	if (file->f_pos == 0) {
+ 		p += sprintf(p, "Device FSM state: %s\n",
+ 			     fsm_getstate_str(privptr->fsm));
+ 		p += sprintf(p, "RX channel FSM state: %s\n",
+@@ -3191,14 +3186,14 @@
+ 	}
+ 	l = strlen(sbuf);
+ 	p = sbuf;
+-	if (pos == (unsigned)pos && pos < l) {
+-		p += pos;
++	if (file->f_pos < l) {
++		p += file->f_pos;
+ 		l = strlen(p);
+ 		ret = (count > l) ? l : count;
+ 		if (copy_to_user(buf, p, ret))
+ 			return -EFAULT;
+-		*off = pos + ret;
+ 	}
++	file->f_pos += ret;
+ 	return ret;
+ }
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/ctcmpc.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/ctcmpc.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,8876 @@
++/*  INTERNAL VERSION:  051804b
++ *
++ * CTC / SNA/MPC network driver
++ *
++ * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ * Author(s): Fritz Elfert (elfert at de.ibm.com, felfert at millenux.com)
++ * Fixes by : Jochen Röhrig (roehrig at de.ibm.com)
++ *            Arnaldo Carvalho de Melo <acme at conectiva.com.br>
++ * MPC additions: Belinda Thompson  (belindat at us.ibm.com)
++ *		  Andy Richter  (richtera at us.ibm.com)
++ *
++ * Documentation used:
++ *  - Principles of Operation (IBM doc#: SA22-7201-06)	   
++ *  - Common IO/-Device Commands and Self Description (IBM doc#: SA22-7204-02)
++ *  - Common IO/-Device Commands and Self Description (IBM doc#: SN22-5535)
++ *  - ESCON Channel-to-Channel Adapter (IBM doc#: SA22-7203-00)
++ *  - ESCON I/O Interface (IBM doc#: SA22-7202-029
++ *
++ * and the source of the original CTC driver by:
++ *  Dieter Wellerdiek (wel at de.ibm.com)
++ *  Martin Schwidefsky (schwidefsky at de.ibm.com)
++ *  Denis Joseph Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
++ *  Jochen Röhrig (roehrig 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.
++ *
++ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.2.2.2 $
++ *
++ */
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/timer.h>
++#include <linux/sched.h>
++
++#include <linux/signal.h>
++#include <linux/string.h>
++#include <linux/proc_fs.h>
++
++#include <linux/ip.h>
++#include <linux/if_arp.h>
++#include <linux/tcp.h>
++#include <linux/skbuff.h>
++#include <linux/ctype.h>
++#include <linux/netdevice.h>
++#include <net/dst.h>
++
++#include <asm/io.h>
++#include <asm/bitops.h>
++#include <asm/uaccess.h>
++#include <linux/wait.h>
++
++DECLARE_WAIT_QUEUE_HEAD(my_queue);
++
++#ifdef CONFIG_CHANDEV
++        #define CTC_CHANDEV
++#endif
++
++#ifdef CTC_CHANDEV
++        #include <asm/chandev.h>
++        #define REQUEST_IRQ chandev_request_irq
++        #define FREE_IRQ chandev_free_irq
++#else
++        #define REQUEST_IRQ request_irq
++        #define FREE_IRQ free_irq
++#endif
++
++#include <asm/idals.h>
++#include <asm/irq.h>
++
++#include "ctcmpc.h"
++#include "fsm.h"
++
++#ifdef MODULE
++MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert at millenux.com),"
++              "Belinda Thompson (belindat at us.ibm.com)");
++MODULE_DESCRIPTION("Linux for S/390 CTC/SNA MPC Driver");
++        #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
++MODULE_LICENSE("GPL");
++        #endif
++        #ifndef CTC_CHANDEV
++MODULE_PARM(mpc, "s");
++MODULE_PARM_DESC(mpc,
++                 "One or more definitions in the same format like the "
++                 "kernel param for mpc.\n"
++                 "E.g.: mpc0:0x700:0x701:4:mpc1:0x702:0x703:4\n");
++
++char *mpc = NULL;
++        #endif
++#else
++/**
++ * Number of devices in monolithic (not module) driver version.
++ */
++        #define MAX_STATIC_DEVICES 16
++#endif /* MODULE */
++
++//#define DEBUG2 1
++//#define DEBUGCCW 1
++//#define DEBUGXID 1
++//#define DEBUGDATA 1
++//#define DEBUGSEQ 1
++//#define DEBUG 1
++//#undef DEBUG
++
++#define ETH_P_SNA_DIX	       0x80D5
++
++/**
++ * CCW commands, used in this driver.
++ */
++#define CCW_CMD_WRITE		0x01
++#define CCW_CMD_READ		0x02
++#define CCW_CMD_NOOP		0x03
++#define CCW_CMD_TIC            0x08
++#define CCW_CMD_SENSE_CMD	0x14
++#define CCW_CMD_WRITE_CTL	0x17
++#define CCW_CMD_SET_EXTENDED	0xc3  /* Disable Compatibility Mode */
++#define CCW_CMD_PREPARE		0xe3
++
++#define CTC_PROTO_S390          0
++#define CTC_PROTO_LINUX         1
++#define CTC_PROTO_LINUX_TTY     2
++#define CTC_PROTO_OS390         3
++#define CTC_PROTO_MPC           4
++#define CTC_PROTO_MAX           4
++
++#define CTC_BUFSIZE_LIMIT      65535
++#define CTC_BUFSIZE_DEFAULT    32768
++#define MPC_BUFSIZE_DEFAULT	65535
++
++#define CTC_TIMEOUT_5SEC        5000
++#define CTC_TIMEOUT_1SEC        1000
++#define CTC_BUSYWAIT_10SEC      10000
++
++#define CTC_INITIAL_BLOCKLEN    2
++
++#define READ 0
++#define WRITE 1
++
++/**
++ * Enum for classifying detected devices.
++ */
++enum channel_types
++{
++        /**
++         * Device is not a channel.
++         */
++        channel_type_none,
++
++        /**
++         * Device is a channel, but we don't know
++         * anything about it.
++         */
++        channel_type_unknown,
++        /**
++         * Device is an mpc capable channel.
++         */
++        channel_type_mpc,
++
++        /**
++         * Device is a CTC/A.
++         */
++        channel_type_ctca,
++
++        /**
++         * Device is a ESCON channel.
++         */
++        channel_type_escon,
++        /**
++         * Device is an unsupported model.
++         */
++        channel_type_unsupported
++};
++
++typedef enum channel_types channel_type_t;
++
++#ifndef CTC_CHANDEV
++static int ctc_no_auto = 0;
++#endif
++
++/**
++ * If running on 64 bit, this must be changed. XXX Why? (bird)
++ */
++typedef unsigned long intparm_t;
++
++#ifndef CTC_CHANDEV
++/**
++ * Definition of a per device parameter block
++ */
++        #define MAX_PARAM_NAME_LEN 11
++typedef struct param_t
++{
++        struct param_t *next;
++        int            read_dev;
++        int            write_dev;
++        __u16          proto;
++        char           name[MAX_PARAM_NAME_LEN];
++} param;
++
++static param *params = NULL;
++#endif
++
++typedef struct
++{
++        unsigned long maxmulti;
++        unsigned long maxcqueue;
++        unsigned long doios_single;
++        unsigned long doios_multi;
++        unsigned long txlen;
++        unsigned long tx_time;
++        struct timeval send_stamp;
++} ctc_profile;
++
++/**
++ * Definition of an XID2 
++ *
++ */
++#define ALLZEROS 0x0000000000000000
++
++#define XID_FM2      0x20
++#define XID2_0	      0x00	
++#define XID2_7	      0x07
++#define XID2_MAX_READ   (2**16-1)
++#define XID2_WRITE_SIDE 0x04
++#define XID2_READ_SIDE	0x05
++
++struct xid2_t
++{
++        __u8    xid2_type_id;
++        __u8    xid2_len;
++        __u32   xid2_adj_id;
++        __u8    xid2_rlen;
++        __u8    xid2_resv1;
++        __u8    xid2_flag1;
++        __u8    xid2_fmtt;
++        __u8    xid2_flag4;
++        __u16   xid2_resv2;
++        __u8    xid2_tgnum;
++        __u32   xid2_sender_id;
++        __u8    xid2_flag2;
++        __u8    xid2_option;
++        char    xid2_resv3[8];
++        __u16   xid2_resv4;
++        __u8    xid2_dlc_type;
++        __u16   xid2_resv5;
++        __u8    xid2_mpc_flag;
++        __u8    xid2_resv6;
++        __u16   xid2_buf_len;
++        char xid2_buffer[255-(sizeof(__u8)*13)-(sizeof(__u32)*2)-
++                (sizeof(__u16)*4)-(sizeof(char)*8)];
++}__attribute__ ((packed)); 
++typedef struct xid2_t xid2;
++
++#define XID2_LENGTH  (sizeof(xid2))
++
++static const xid2 init_xid = {
++        xid2_type_id:   XID_FM2,
++        xid2_len:       0x45,
++        xid2_adj_id:    0,
++        xid2_rlen:      0x31,
++        xid2_resv1:     0,
++        xid2_flag1:     0,
++        xid2_fmtt:      0,
++        xid2_flag4:     0x80,
++        xid2_resv2:     0,
++        xid2_tgnum:     0,
++        xid2_sender_id: 0,
++        xid2_flag2:     0,
++        xid2_option:    XID2_0,
++        xid2_resv3:     "\x00",
++        xid2_resv4:     0,
++        xid2_dlc_type:  XID2_READ_SIDE,
++        xid2_resv5:     0,
++        xid2_mpc_flag:  0,
++        xid2_resv6:     0,
++        xid2_buf_len:   (MPC_BUFSIZE_DEFAULT - 35),
++};
++
++struct th_header_t
++{
++        __u8    th_seg;
++        __u8    th_ch_flag;
++#define TH_HAS_PDU	0xf0
++#define TH_IS_XID	0x01
++#define TH_SWEEP_REQ	0xfe
++#define TH_SWEEP_RESP	0xff
++        __u8    th_blk_flag;
++#define TH_DATA_IS_XID	0x80
++#define TH_RETRY	0x40
++#define TH_DISCONTACT	0xc0
++#define TH_SEG_BLK	0x20
++#define TH_LAST_SEG	0x10
++#define TH_PDU_PART	0x08
++        __u8    th_is_xid;      /* is 0x01 if this is XID  */
++        __u32   th_seq_num;
++}__attribute__ ((packed)); 
++typedef struct th_header_t th_header;
++
++static const th_header thnorm = {
++        th_seg:     0x00,
++        th_ch_flag: TH_IS_XID,
++        th_blk_flag:TH_DATA_IS_XID,
++        th_is_xid:  0x01,
++        th_seq_num: 0x00000000,
++};
++
++static const th_header thdummy = {
++        th_seg:     0x00,
++        th_ch_flag: 0x00,
++        th_blk_flag:TH_DATA_IS_XID,
++        th_is_xid:  0x01,
++        th_seq_num: 0x00000000,
++};
++
++
++struct th_addon_t
++{
++        __u32   th_last_seq;    
++        __u32   th_resvd;       
++}__attribute__ ((packed));
++typedef struct th_addon_t th_addon;
++
++struct th_sweep_t
++{
++        th_header th;
++        th_addon  sw;
++}__attribute__ ((packed));
++typedef struct th_sweep_t th_sweep;
++
++#define TH_HEADER_LENGTH (sizeof(th_header)) 
++#define TH_SWEEP_LENGTH (sizeof(th_sweep)) 
++
++#define PDU_LAST	0x80
++#define PDU_CNTL	0x40
++#define PDU_FIRST	0x20
++
++struct pdu_t
++{
++        __u32   pdu_offset;
++        __u8    pdu_flag;
++        __u8    pdu_proto;   /*  0x01 is APPN SNA  */ 
++        __u16   pdu_seq;
++}__attribute__ ((packed));
++typedef struct pdu_t pdu;
++#define PDU_HEADER_LENGTH  (sizeof(pdu))
++
++struct qllc_t
++{
++        __u8    qllc_address;
++#define QLLC_REQ        0xFF
++#define QLLC_RESP       0x00
++        __u8    qllc_commands;
++#define QLLC_DISCONNECT 0x53
++#define QLLC_UNSEQACK   0x73
++#define QLLC_SETMODE	 0x93
++#define QLLC_EXCHID	 0xBF
++}__attribute__ ((packed));
++typedef struct qllc_t qllc;
++
++
++static void ctcmpc_bh(unsigned long);
++
++/**
++ * Definition of one channel
++ */
++struct channel_t
++{
++
++        /**
++         * Pointer to next channel in list.
++         */
++        struct channel_t *next;
++        __u16 devno;
++        int irq;
++        /**
++         * Type of this channel.
++         * CTC/A or Escon for valid channels.
++         */
++        channel_type_t type;
++        /**
++         * Misc. flags. See CHANNEL_FLAGS_... below
++         */
++        __u32 flags;
++        /**
++         * The protocol of this channel
++         */
++        __u16 protocol;
++        /**
++         * I/O and irq related stuff
++         */
++        ccw1_t *ccw;
++        devstat_t *devstat;
++        /**
++         * RX/TX buffer size
++         */
++        __u32 max_bufsize;
++        /**
++         * Transmit/Receive buffer.
++         */
++        struct sk_buff *trans_skb;
++        /**
++         * Universal I/O queue.
++         */
++        struct sk_buff_head io_queue;
++        struct tasklet_struct ch_tasklet; 
++        /**
++         * TX queue for collecting skb's during busy.
++         */
++        struct sk_buff_head collect_queue;
++        /**
++         * Amount of data in collect_queue.
++         */
++        int collect_len;
++        /**
++         * spinlock for collect_queue and collect_len
++         */
++        spinlock_t collect_lock;
++        /**
++         * Timer for detecting unresposive
++         * I/O operations.
++         */
++        fsm_timer timer;
++        /**
++         * Retry counter for misc. operations.
++         */
++        int retry;
++        /**
++         * spinlock for serializing inbound SNA Segments
++         */
++        spinlock_t segment_lock;
++        /**
++          * SNA TH Seq Number
++          */
++        __u32           th_seq_num;
++        __u8            th_seg;
++        __u32           pdu_seq;
++        sk_buff         *xid_skb;
++        char            *xid_skb_data;
++        th_header       *xid_th;
++        xid2            *xid;
++        char            *xid_id;
++        th_header       *rcvd_xid_th;
++        xid2            *rcvd_xid;
++        char            *rcvd_xid_id;
++        __u8            in_mpcgroup;
++        fsm_timer       sweep_timer;
++        struct sk_buff_head     sweep_queue;
++        th_header       *discontact_th;
++        struct tasklet_struct ch_disc_tasklet; 
++        /**
++         * The finite state machine of this channel
++         */
++        fsm_instance     *fsm;
++        /**
++         * The corresponding net_device this channel
++         * belongs to.
++         */
++        struct net_device   *netdev;
++        ctc_profile         prof;
++        unsigned char       *trans_skb_data;
++};
++typedef struct channel_t channel;
++
++#define CHANNEL_FLAGS_READ                0
++#define CHANNEL_FLAGS_WRITE               1
++#define CHANNEL_FLAGS_INUSE               2
++#define CHANNEL_FLAGS_BUFSIZE_CHANGED     4
++#define CHANNEL_FLAGS_RWMASK 	           1
++#define CHANNEL_PRIMARY		   0x20   /* we are the x side */
++#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK)
++
++/**
++ * Linked list of all detected channels.
++ */
++static channel *channels = NULL;
++
++#ifdef CTC_CHANDEV
++static int activated;
++#endif
++
++
++/***
++  * Definition of one MPC group
++  */
++
++#define MAX_MPCGCHAN                10
++#define MPC_XID_TIMEOUT_VALUE       10000
++#define MPC_CHANNEL_TIMEOUT_1SEC    1000
++#define MPC_CHANNEL_ADD 0
++#define MPC_CHANNEL_REMOVE 1
++#define MPC_CHANNEL_ATTN 2
++#define XSIDE 1
++#define YSIDE 0
++
++
++
++struct mpcg_info_t
++{
++        struct sk_buff        *skb;
++        struct channel_t      *ch;
++        struct xid2_t         *xid;
++        struct th_sweep_t     *sweep;
++        struct th_header_t    *th;
++};
++typedef struct mpcg_info_t mpcg_info;
++
++struct mpc_group_t
++{
++        struct tasklet_struct mpc_tasklet; 
++        struct tasklet_struct mpc_tasklet2; 
++        int     changed_side;
++        int     saved_state;
++        int     channels_terminating;
++        int     out_of_sequence;
++        int     flow_off_called;
++        int     port_num;
++        int     port_persist;
++        int     alloc_called;
++        __u32   xid2_adj_id;
++        __u8    xid2_tgnum;
++        __u32   xid2_sender_id;
++        int     num_channel_paths;
++        int     active_channels[2];
++        __u16   group_max_buflen;
++        int     outstanding_xid2;
++        int     outstanding_xid7;
++        int     outstanding_xid7_p2;
++        int     sweep_req_pend_num;
++        int     sweep_rsp_pend_num;
++        sk_buff *xid_skb;
++        char    *xid_skb_data;
++        th_header *xid_th;
++        xid2    *xid;
++        char    *xid_id;
++        th_header *rcvd_xid_th;
++        sk_buff *rcvd_xid_skb;
++        char    *rcvd_xid_data;
++        __u8    in_sweep;
++        __u8    roll;
++        xid2    *saved_xid2;   
++        callbacktypei2  allochanfunc;  
++        int     allocchan_callback_retries;
++        callbacktypei3  estconnfunc;
++        int     estconn_callback_retries;
++        int     estconn_called;
++        int     xidnogood;
++        int     send_qllc_disc;
++        fsm_timer timer; 
++        fsm_instance    *fsm; /* group xid fsm */
++};
++typedef struct mpc_group_t mpc_group;
++
++typedef struct ctc_priv_t
++{
++        struct net_device_stats stats;
++        unsigned long           tbusy;
++
++        /**The MPC group struct of this interface
++        */
++        mpc_group               *mpcg;
++        /**
++         * The finite state machine of this interface.
++         */
++        fsm_instance            *fsm;
++        /**
++         * The protocol of this device
++         */
++        __u16                   protocol;
++        channel                 *channel[2];
++        xid2                    *xid;
++        struct proc_dir_entry   *proc_dentry;
++        struct proc_dir_entry   *proc_stat_entry;
++        struct proc_dir_entry   *proc_ctrl_entry;
++        int                     proc_registered;
++        /**
++         * Timer for restarting after I/O Errors
++         */
++        fsm_timer               restart_timer;
++
++} ctc_priv;
++
++
++static void ctcmpc_action_send_discontact(unsigned long);
++
++
++/**
++ * Compatibility macros for busy handling
++ * of network devices.
++ */
++static __inline__ void 
++ctcmpc_clear_busy(struct net_device *dev)
++{
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s enter:  %s()\n", dev->name,__FUNCTION__);
++#endif
++        if(((ctc_priv *)dev->priv)->mpcg->in_sweep == 0)
++        {
++                clear_bit(0, &(((ctc_priv *)dev->priv)->tbusy));
++                netif_wake_queue(dev);
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
++#endif
++
++
++}
++
++static __inline__ int 
++ctcmpc_test_and_set_busy(struct net_device *dev)
++{
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s enter:  %s()\n", dev->name,__FUNCTION__);
++#endif
++        netif_stop_queue(dev);
++        return test_and_set_bit(0, &((ctc_priv *)dev->priv)->tbusy);
++}
++
++#define SET_DEVICE_START(device, value)
++
++static __inline__ int ctcmpc_checkalloc_buffer(channel *,int);
++static void ctcmpc_purge_skb_queue(struct sk_buff_head *);
++
++
++/**
++ * Print Banner.
++ */
++static void 
++print_banner(void)
++{
++        static int printed = 0;
++        char vbuf[] = "$Revision: 1.2.2.2 $";
++        char *version = vbuf;
++
++        if(printed)
++                return;
++        if((version = strchr(version, ':')))
++        {
++                char *p = strchr(version + 1, '$');
++                if(p)
++                        *p = '\0';
++        } else
++                version = " ??? ";
++        printk(KERN_INFO
++               "CTC MPC driver Version%swith"
++#ifndef CTC_CHANDEV
++               "out"
++#endif
++               " CHANDEV support"
++#ifdef DEBUG
++               " (DEBUG-VERSION, " __DATE__ __TIME__ ")"
++#endif
++               " initialized\n", version);
++        printed = 1;
++}
++
++static inline int 
++gfp_type(void)
++{
++        return in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
++}
++
++
++/**
++  * MPC Group Station FSM States
++
++State Name		When In This State
++======================	=======================================	
++MPCG_STATE_RESET	Initial State When Driver Loaded
++                        We receive and send NOTHING
++
++MPCG_STATE_INOP         INOP Received. 
++                        Group level non-recoverable error
++
++MPCG_STATE_READY	XID exchanges for at least 1 write and
++                        1 read channel have completed.
++                        Group is ready for data transfer.
++
++States from ctc_mpc_alloc_channel
++==============================================================
++MPCG_STATE_XID2INITW	Awaiting XID2(0) Initiation
++                              ATTN from other side will start
++                              XID negotiations.
++                              Y-side protocol only.
++
++MPCG_STATE_XID2INITX	XID2(0) negotiations are in progress.
++                              At least 1, but not all, XID2(0)'s
++                              have been received from partner.
++
++MPCG_STATE_XID7INITW	XID2(0) complete 
++                              No XID2(7)'s have yet been received.
++                              XID2(7) negotiations pending.
++
++MPCG_STATE_XID7INITX	XID2(7) negotiations in progress.
++                              At least 1, but not all, XID2(7)'s 
++                              have been received from partner.
++
++MPCG_STATE_XID7INITF	XID2(7) negotiations complete.
++                              Transitioning to READY.
++
++MPCG_STATE_READY	      Ready for Data Transfer.
++                                                                        
++
++States from ctc_mpc_establish_connectivity call
++==============================================================
++MPCG_STATE_XID0IOWAIT	Initiating XID2(0) negotiations.
++                              X-side protocol only.
++                              ATTN-BUSY from other side will convert
++                              this to Y-side protocol and the
++                              ctc_mpc_alloc_channel flow will begin.
++
++MPCG_STATE_XID0IOWAIX	XID2(0) negotiations are in progress.
++                              At least 1, but not all, XID2(0)'s
++                              have been received from partner.
++
++MPCG_STATE_XID7INITI	XID2(0) complete 
++                              No XID2(7)'s have yet been received.
++                              XID2(7) negotiations pending.
++
++MPCG_STATE_XID7INITZ	XID2(7) negotiations in progress.
++                              At least 1, but not all, XID2(7)'s 
++                              have been received from partner.
++
++MPCG_STATE_XID7INITF	XID2(7) negotiations complete.
++                              Transitioning to READY.
++
++MPCG_STATE_READY	      Ready for Data Transfer.
++                                                                        
++*/
++
++enum mpcg_events
++{
++        MPCG_EVENT_INOP,
++        MPCG_EVENT_DISCONC,
++        MPCG_EVENT_XID0DO,
++        MPCG_EVENT_XID2,
++        MPCG_EVENT_XID2DONE,
++        MPCG_EVENT_XID7DONE,
++        MPCG_EVENT_TIMER,
++        MPCG_EVENT_DOIO,
++        NR_MPCG_EVENTS,  
++};
++
++static const char *mpcg_event_names[]  = {
++        "INOP Condition",
++        "Discontact Received",
++        "Channel Active - Start XID",
++        "XID2 Received",
++        "XID0 Complete",
++        "XID7 Complete",
++        "XID Setup Timer",
++        "XID DoIO",
++};
++
++
++enum mpcg_states
++{
++        MPCG_STATE_RESET,
++        MPCG_STATE_INOP,
++        MPCG_STATE_XID2INITW,
++        MPCG_STATE_XID2INITX,
++        MPCG_STATE_XID7INITW,
++        MPCG_STATE_XID7INITX,
++        MPCG_STATE_XID0IOWAIT,
++        MPCG_STATE_XID0IOWAIX,
++        MPCG_STATE_XID7INITI,
++        MPCG_STATE_XID7INITZ,
++        MPCG_STATE_XID7INITF,
++        MPCG_STATE_FLOWC,
++        MPCG_STATE_READY,
++        NR_MPCG_STATES,
++};
++
++
++static const char *mpcg_state_names[] =  {
++        "Reset",
++        "INOP",
++        "Passive XID- XID0 Pending Start",
++        "Passive XID- XID0 Pending Complete",
++        "Passive XID- XID7 Pending P1 Start",
++        "Passive XID- XID7 Pending P2 Complete",
++        "Active  XID- XID0 Pending Start",
++        "Active  XID- XID0 Pending Complete",
++        "Active  XID- XID7 Pending Start",
++        "Active  XID- XID7 Pending Complete ",
++        "XID        - XID7 Complete ",
++        "FLOW CONTROL ON",
++        "READY",
++};
++
++#ifndef CTC_CHANDEV
++/**
++ * Return type of a detected device.
++ */
++static channel_type_t channel_type (senseid_t *id)
++{
++        channel_type_t type = channel_type_none;
++
++        switch(id->cu_type)
++        {
++                case 0x3088:
++                        switch(id->cu_model)
++                        {
++                                case 0x1E:
++                                        /**
++                                         * 3088-1E = FICON channel
++                                         */
++                                case 0x08:
++                                        /**
++                                         * 3088-08 = CTCA
++                                         */
++                                case 0x1F:
++                                        /**
++                                         * 3088-1F = ESCON channel
++                                         */
++                                        type = channel_type_mpc;
++                                        break;
++
++                                        /**
++                                         * 3088-01 = P390 OSA emulation
++                                         */
++                                case 0x01:
++                                        /* fall thru */
++
++                                        /**
++                                         * 3088-60 = OSA/2 adapter
++                                         */
++                                case 0x60:
++                                        /* fall thru */
++
++                                        /**
++                                         * 3088-61 = CISCO 7206 CLAW proto
++                                         * on ESCON
++                                         */
++                                case 0x61:
++                                        /* fall thru */
++
++                                        /**
++                                         * 3088-62 = OSA/D device
++                                         */
++                                case 0x62:
++                                        type = channel_type_unsupported;
++                                        break;
++
++                                default:
++                                        type = channel_type_unknown;
++                                        printk(KERN_INFO
++                                               "channel: Unknown model found "
++                                               "3088-%02x\n", id->cu_model);
++                        }
++                        break;
++
++                default:
++                        type = channel_type_none;
++        }
++        return type;
++}
++#endif
++
++
++/**
++ * States of the interface statemachine.
++ */
++enum dev_states
++{
++        DEV_STATE_STOPPED,
++        DEV_STATE_STARTWAIT_RXTX,
++        DEV_STATE_STARTWAIT_RX,
++        DEV_STATE_STARTWAIT_TX,
++        DEV_STATE_STOPWAIT_RXTX,
++        DEV_STATE_STOPWAIT_RX,
++        DEV_STATE_STOPWAIT_TX,
++        DEV_STATE_RUNNING,
++        /**
++         * MUST be always the last element!!
++         */
++        NR_DEV_STATES
++};
++
++static const char *dev_state_names[] = {
++        "Stopped",
++        "StartWait RXTX",
++        "StartWait RX",
++        "StartWait TX",
++        "StopWait RXTX",
++        "StopWait RX",
++        "StopWait TX",
++        "Running",
++};
++
++/**
++ * Events of the interface statemachine.
++ */
++enum dev_events
++{
++        DEV_EVENT_START,
++        DEV_EVENT_STOP,
++        DEV_EVENT_RXUP,
++        DEV_EVENT_TXUP,
++        DEV_EVENT_RXDOWN,
++        DEV_EVENT_TXDOWN,
++        DEV_EVENT_RESTART,
++        /**
++         * MUST be always the last element!!
++         */
++        NR_DEV_EVENTS
++};
++
++static const char *dev_event_names[] = {
++        "Start",
++        "Stop",
++        "RX up",
++        "TX up",
++        "RX down",
++        "TX down",
++        "Restart",
++};
++
++/**
++ * Events of the channel statemachine
++ */
++enum ch_events
++{
++        /**
++         * Events, representing return code of
++         * I/O operations (do_IO, halt_IO et al.)
++         */
++        CH_EVENT_IO_SUCCESS,
++        CH_EVENT_IO_EBUSY,
++        CH_EVENT_IO_ENODEV,
++        CH_EVENT_IO_EIO,
++        CH_EVENT_IO_UNKNOWN,
++
++        CH_EVENT_ATTNBUSY,
++        CH_EVENT_ATTN,
++        CH_EVENT_BUSY,
++
++        /**
++         * Events, representing unit-check
++         */
++        CH_EVENT_UC_RCRESET,
++        CH_EVENT_UC_RSRESET,
++        CH_EVENT_UC_TXTIMEOUT,
++        CH_EVENT_UC_TXPARITY,
++        CH_EVENT_UC_HWFAIL,
++        CH_EVENT_UC_RXPARITY,
++        CH_EVENT_UC_ZERO,
++        CH_EVENT_UC_UNKNOWN,
++
++        /**
++         * Events, representing subchannel-check
++         */
++        CH_EVENT_SC_UNKNOWN,
++
++        /**
++         * Events, representing machine checks
++         */
++        CH_EVENT_MC_FAIL,
++        CH_EVENT_MC_GOOD,
++
++        /**
++         * Event, representing normal IRQ
++         */
++        CH_EVENT_IRQ,
++        CH_EVENT_FINSTAT,
++
++        /**
++         * Event, representing timer expiry.
++         */
++        CH_EVENT_TIMER,
++
++        /**
++         * Events, representing commands from upper levels.
++         */
++        CH_EVENT_START,
++        CH_EVENT_STOP,
++        CH_EVENT_SEND_XID,
++
++        /** 
++         * Events, representing TX MPC buffer states
++         */
++        CH_EVENT_TX_LOMEM,
++        CH_EVENT_TX_MEM_OK,
++        CH_EVENT_RSWEEP1_TIMER,
++
++        /**
++         * MUST be always the last element!!
++         */
++        NR_CH_EVENTS,
++};
++
++static const char *ch_event_names[] = {
++        "do_IO success",
++        "do_IO busy",
++        "do_IO enodev",
++        "do_IO ioerr",
++        "do_IO unknown",
++
++        "Status ATTN & BUSY",
++        "Status ATTN",
++        "Status BUSY",
++
++        "Unit check remote reset",
++        "Unit check remote system reset",
++        "Unit check TX timeout",
++        "Unit check TX parity",
++        "Unit check Hardware failure",
++        "Unit check RX parity",
++        "Unit check ZERO",
++        "Unit check Unknown",
++
++        "SubChannel check Unknown",
++
++        "Machine check failure",
++        "Machine check operational",
++
++        "IRQ normal",
++        "IRQ final",
++
++        "Timer",
++
++        "Start",
++        "Stop",
++        "XID Exchange",
++
++        "TX buffer shortage",
++        "TX buffer shortage relieved",
++        "MPC Group Sweep Timer",
++};
++
++/**
++ * States of the channel statemachine.
++ */
++enum ch_states
++{
++        /**
++         * Channel not assigned to any device,
++         * initial state, direction invalid
++         */
++        CH_STATE_IDLE,
++
++        /**
++         * Channel assigned but not operating
++         */
++        CH_STATE_STOPPED,
++        CH_STATE_STARTWAIT,
++        CH_STATE_STARTRETRY,
++        CH_STATE_SETUPWAIT,
++        CH_STATE_RXINIT,
++        CH_STATE_TXINIT,
++        CH_STATE_RX,
++        CH_STATE_TX,
++        CH_STATE_RXIDLE,
++        CH_STATE_TXIDLE,
++        CH_STATE_RXERR,
++        CH_STATE_TXERR,
++        CH_STATE_TERM,
++        CH_STATE_DTERM,
++        CH_STATE_NOTOP,
++        CH_STATE_TXLOMEM,
++        CH_XID0_PENDING,
++        CH_XID0_INPROGRESS,
++        CH_XID7_PENDING,
++        CH_XID7_PENDING1,
++        CH_XID7_PENDING2,
++        CH_XID7_PENDING3,
++        CH_XID7_PENDING4,
++
++        /**
++         * MUST be always the last element!!
++         */
++        NR_CH_STATES,
++};
++
++static const char *ch_state_names[] = {
++        "Idle",
++        "Stopped",
++        "StartWait",
++        "StartRetry",
++        "SetupWait",
++        "RX init",
++        "TX init",
++        "RX",
++        "TX",
++        "RX idle",
++        "TX idle",
++        "RX error",
++        "TX error",
++        "Terminating",
++        "Restarting",
++        "Not operational",
++        "TX Buffers low",
++        "Pending XID0 Start",
++        "In XID0 Negotiations ",
++        "Pending XID7 P1 Start",
++        "Active XID7 P1 Exchange ",
++        "Pending XID7 P2 Start ",
++        "Active XID7 P2 Exchange ",
++        "XID7 Complete - Pending READY ",
++};
++
++static int transmit_skb(channel *, struct sk_buff *);
++
++#if defined(DEBUGDATA) || defined(DEBUGXID)\
++  || defined(DEBUGCCW) || defined(DEBUGSEQ)
++/*-------------------------------------------------------------------*
++* Dump buffer format                                                 *
++*                                                                    *
++*--------------------------------------------------------------------*/
++static void 
++dumpit(char* buf, int len)
++{
++
++        __u32      ct, sw, rm, dup;
++        char       *ptr, *rptr;
++        char       tbuf[82], tdup[82];
++#if (UTS_MACHINE == s390x)
++        char       addr[22];
++#else
++        char       addr[12];
++#endif
++        char       boff[12];
++        char       bhex[82], duphex[82];
++        char       basc[40];
++
++        sw  = 0;
++        rptr =ptr=buf;
++        rm  = 16;
++        duphex[0]  = 0x00;
++        dup = 0;
++
++        for(ct=0; ct < len; ct++, ptr++, rptr++)
++        {
++                if(sw == 0)
++                {
++#if (UTS_MACHINE == s390x)
++                        sprintf(addr, "%16.16lx",(unsigned long)rptr);
++#else
++                        sprintf(addr, "%8.8X",(__u32)rptr);
++#endif
++                        sprintf(boff, "%4.4X", (__u32)ct);
++                        bhex[0] = '\0';
++                        basc[0] = '\0';
++                }
++                if((sw == 4) || (sw == 12))
++                {
++                        strcat(bhex, " ");
++                }
++                if(sw == 8)
++                {
++                        strcat(bhex, "  ");
++                }
++#if (UTS_MACHINE == s390x)
++                sprintf(tbuf,"%2.2lX", (unsigned long)*ptr);
++#else
++                sprintf(tbuf,"%2.2X", (__u32)*ptr);
++#endif
++                tbuf[2] = '\0'; 
++                strcat(bhex, tbuf);
++                if((0!=isprint(*ptr)) && (*ptr >= 0x20))
++                {
++                        basc[sw] = *ptr; 
++                } else
++                {
++                        basc[sw] = '.';
++                }
++                basc[sw+1] = '\0';
++                sw++;
++                rm--;
++                if(sw==16)
++                {
++                        if((strcmp(duphex, bhex)) !=0)
++                        {
++                                if(dup !=0)
++                                {
++                                        sprintf(tdup,"Duplicate as above "
++                                                "to %s", addr);
++                                        printk( KERN_INFO "               "
++                                                "     --- %s ---\n",tdup);
++                                }
++                                printk( KERN_INFO "   %s (+%s) : %s  [%s]\n",
++                                        addr, boff, bhex, basc);
++                                dup = 0;
++                                strcpy(duphex, bhex);
++                        } else
++                        {
++                                dup++;
++                        }
++                        sw = 0;
++                        rm = 16;
++                }
++        }  /* endfor */
++
++        if(sw != 0)
++        {
++                for(; rm > 0; rm--, sw++)
++                {
++                        if((sw==4) || (sw==12)) strcat(bhex, " ");
++                        if(sw==8)               strcat(bhex, "  ");
++                        strcat(bhex, "  ");
++                        strcat(basc, " ");
++                }
++                if(dup !=0)
++                {
++                        sprintf(tdup,"Duplicate as above to %s", addr);
++                        printk( KERN_INFO "               "
++                                "     --- %s ---\n",tdup);
++                }
++                printk( KERN_INFO "   %s (+%s) : %s  [%s]\n",
++                        addr, boff, bhex, basc);
++        } else
++        {
++                if(dup >=1)
++                {
++                        sprintf(tdup,"Duplicate as above to %s", addr);
++                        printk( KERN_INFO "               "
++                                "     --- %s ---\n",tdup);
++                }
++                if(dup !=0)
++                {
++                        printk( KERN_INFO "   %s (+%s) : %s  [%s]\n",
++                                addr, boff, bhex, basc);
++                }
++        }
++
++        return;
++
++}   /*   end of dumpit  */
++
++#endif
++
++#ifdef DEBUGDATA
++/**
++ * Dump header and first 16 bytes of an sk_buff for debugging purposes.
++ *
++ * @param skb    The sk_buff to dump.
++ * @param offset Offset relative to skb-data, where to start the dump.
++ */
++static void 
++ctcmpc_dump_skb(struct sk_buff *skb, int offset)
++{
++        unsigned char *p = skb->data;
++        th_header *header;
++        pdu *pheader;
++        int bl = skb->len;
++        int i;
++
++        if(p == NULL) return;
++        p += offset;
++        header = (th_header *)p;
++
++        printk(KERN_INFO "dump:\n");
++        printk(KERN_INFO "skb len=%d \n", skb->len);
++        if(skb->len > 2)
++        {
++                switch(header->th_ch_flag)
++                {
++                        case TH_HAS_PDU:
++                                break;
++                        case 0x00:
++                        case TH_IS_XID:
++                                if((header->th_blk_flag == TH_DATA_IS_XID) &&
++                                   (header->th_is_xid == 0x01))
++                                        goto dumpth;
++                        case TH_SWEEP_REQ:
++                                goto dumpth;
++                        case TH_SWEEP_RESP:
++                                goto dumpth;
++                        default:
++                                break;
++
++                }
++
++                pheader = (pdu *)p;
++                printk(KERN_INFO "pdu->offset: %d hex: %04x\n",
++                       pheader->pdu_offset,pheader->pdu_offset);
++                printk(KERN_INFO "pdu->flag  : %02x\n",pheader->pdu_flag);
++                printk(KERN_INFO "pdu->proto : %02x\n",pheader->pdu_proto);
++                printk(KERN_INFO "pdu->seq   : %02x\n",pheader->pdu_seq);
++                goto dumpdata;
++
++                dumpth:
++                printk(KERN_INFO "th->seg     : %02x\n", header->th_seg);
++                printk(KERN_INFO "th->ch      : %02x\n", header->th_ch_flag);
++                printk(KERN_INFO "th->blk_flag: %02x\n", header->th_blk_flag);
++                printk(KERN_INFO "th->type    : %s\n",
++                       (header->th_is_xid) ? "DATA" : "XID"); 
++                printk(KERN_INFO "th->seqnum  : %04x\n", header->th_seq_num);
++
++        }  /* only dump the data if the length is not greater than 2 */
++        dumpdata:
++
++        if(bl > 32)
++                bl = 32;
++        printk(KERN_INFO "data: ");
++        for(i = 0; i < bl; i++)
++                printk("%02x%s", *p++, (i % 16) ? " " : "\n<7>");
++        printk("\n");
++}
++
++#endif
++
++/**
++ * Dummy NOP action for statemachines
++ */
++static void 
++fsm_action_nop(fsm_instance *fi, int event, void *arg)
++{
++}
++
++static int ctcmpc_open(net_device *);
++static void ctcmpc_ch_action_rxidle(fsm_instance *fi, int event, void *arg);
++static void ctcmpc_ch_action_txidle(fsm_instance *fi, int event, void *arg);
++static void inline ccw_check_return_code (channel *,int);
++
++
++/*
++      ctc_mpc_alloc_channel
++      Device Initialization :  
++            ACTPATH  driven IO operations
++*/
++int 
++ctc_mpc_alloc_channel(int port_num,callbacktypei2  callback)
++{
++        char device[20];
++        char *devnam = "mpc";
++        net_device *dev = NULL;
++        mpc_group *grpptr;
++        ctc_priv *privptr;
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        sprintf(device, "%s%i",devnam,port_num);
++        dev = __dev_get_by_name(device);
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "ctc_mpc_alloc_channel %s dev=NULL\n",device);
++                return(1);
++        }
++
++
++        privptr = (ctc_priv *)dev->priv;
++        grpptr = privptr->mpcg;
++        if(!grpptr)
++                return(1);
++
++        grpptr->allochanfunc = callback;
++        grpptr->port_num = port_num;
++        grpptr->port_persist = 1;
++
++        printk(KERN_INFO "%s: %s called for device %s refcount=%d state=%s\n", 
++               dev->name,
++               __FUNCTION__,
++               dev->name,
++               atomic_read(&dev->refcnt),
++               fsm_getstate_str(grpptr->fsm));
++
++        switch(fsm_getstate(grpptr->fsm))
++        {
++                case MPCG_STATE_INOP:
++                        /* Group is in the process of terminating */
++                        grpptr->alloc_called = 1;
++                        break;
++                case MPCG_STATE_RESET:
++                        /* MPC Group will transition to state             */
++                        /* MPCG_STATE_XID2INITW iff the minimum number    */
++                        /* of 1 read and 1 write channel have successfully*/
++                        /* activated                                      */
++                        /*fsm_newstate(grpptr->fsm, MPCG_STATE_XID2INITW);*/
++                        if(callback)
++                                grpptr->send_qllc_disc = 1;
++                case MPCG_STATE_XID0IOWAIT:
++                        fsm_deltimer(&grpptr->timer);
++                        grpptr->outstanding_xid2 = 0;
++                        grpptr->outstanding_xid7 = 0;
++                        grpptr->outstanding_xid7_p2 = 0;
++                        grpptr->saved_xid2 = NULL;
++                        if(callback)
++                                ctcmpc_open(dev);
++                        fsm_event(((ctc_priv *)dev->priv)->fsm, 
++                                  DEV_EVENT_START, dev);
++                        break;;
++                case MPCG_STATE_READY:
++                        /* XID exchanges completed after PORT was activated */
++                        /* Link station already active                      */
++                        /* Maybe timing issue...retry callback              */
++                        grpptr->allocchan_callback_retries++;
++                        if(grpptr->allocchan_callback_retries < 4)
++                        {
++                                if(grpptr->allochanfunc)
++                                        grpptr->allochanfunc(grpptr->port_num, 
++                                                      grpptr->group_max_buflen);
++                        } else
++                        {
++                                /* there are problems...bail out            */
++                                /* there may be a state mismatch so restart */
++                                grpptr->port_persist = 1;
++                                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++                                grpptr->allocchan_callback_retries = 0;
++                        }
++                        break;
++                default:
++                        return(0);
++
++        }
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++        return(0);
++}
++
++
++void 
++ctc_mpc_establish_connectivity(int port_num, callbacktypei3 callback)
++{
++
++        char device[20];
++        char *devnam = "mpc";
++        net_device *dev = NULL;
++        mpc_group *grpptr;
++        ctc_priv *privptr;
++        channel *rch,*wch;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++
++#endif
++
++        sprintf(device,"%s%i",devnam,port_num);
++        dev = __dev_get_by_name(device);
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "ctc_mpc_establish_connectivity %s dev=NULL\n"
++                       ,device);
++                return;
++        }
++        privptr = (ctc_priv *)dev->priv;
++        rch = privptr->channel[READ];
++        wch = privptr->channel[WRITE];
++
++        grpptr = privptr->mpcg;
++
++        printk(KERN_INFO "%s: %s called for device %s refcount=%d state=%s\n", 
++               dev->name,
++               __FUNCTION__,
++               dev->name,
++               atomic_read(&dev->refcnt),
++               fsm_getstate_str(grpptr->fsm));
++
++        grpptr->estconnfunc = callback;
++        grpptr->port_num = port_num;
++
++        switch(fsm_getstate(grpptr->fsm))
++        {
++                case MPCG_STATE_READY:
++                        /* XID exchanges completed after PORT was activated */
++                        /* Link station already active                      */
++                        /* Maybe timing issue...retry callback              */
++                        fsm_deltimer(&grpptr->timer);
++                        grpptr->estconn_callback_retries++;
++                        if(grpptr->estconn_callback_retries < 4)
++                        {
++                                if(grpptr->estconnfunc)
++                                {
++                                        grpptr->estconnfunc(grpptr->port_num,0,
++                                                      grpptr->group_max_buflen);
++                                        grpptr->estconnfunc = NULL;
++                                }
++                        } else
++                        {
++                                /* there are problems...bail out         */
++                                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++                                grpptr->estconn_callback_retries = 0;
++                        }
++                        break;
++                case MPCG_STATE_INOP:
++                case MPCG_STATE_RESET:
++                        /* MPC Group is not ready to start XID - min number of*/
++                        /* 1 read and 1 write channel have not been acquired  */
++                        printk(KERN_WARNING "ctcmpc: %s() REJECTED ACTIVE XID"
++                               " Request - Channel Pair is not Active\n",
++                               __FUNCTION__);
++                        if(grpptr->estconnfunc)
++                        {
++                                grpptr->estconnfunc(grpptr->port_num,-1,0);
++                                grpptr->estconnfunc = NULL;
++                        }
++                        break;
++                case MPCG_STATE_XID2INITW:
++                        /* alloc channel was called but no XID exchange    */
++                        /* has occurred. initiate xside XID exchange       */
++                        /* make sure yside XID0 processing has not started */
++                        if((fsm_getstate(rch->fsm) > CH_XID0_PENDING) ||
++                           (fsm_getstate(wch->fsm) > CH_XID0_PENDING))
++                        {
++                                printk(KERN_WARNING "ctcmpc: %s() ABORT ACTIVE"
++                                       " XID Request - PASSIVE XID already in "
++                                       "process\n",
++                                       __FUNCTION__);
++                                break;
++                        }
++                        grpptr->send_qllc_disc = 1;
++                        fsm_newstate(grpptr->fsm, MPCG_STATE_XID0IOWAIT);
++                        fsm_deltimer(&grpptr->timer);
++                        fsm_addtimer(&grpptr->timer, 
++                                     MPC_XID_TIMEOUT_VALUE, 
++                                     MPCG_EVENT_TIMER, 
++                                     dev);
++                        grpptr->outstanding_xid7 = 0;
++                        grpptr->outstanding_xid7_p2 = 0;
++                        grpptr->saved_xid2 = NULL;
++                        if((rch->in_mpcgroup) && 
++                           (fsm_getstate(rch->fsm) == CH_XID0_PENDING))
++                                fsm_event(grpptr->fsm, MPCG_EVENT_XID0DO, rch);
++                        else
++                        {
++                                printk(KERN_WARNING "ctcmpc: %s() Unable to"
++                                       " start ACTIVE XID0 on read channel\n",
++                                       __FUNCTION__);
++                                if(grpptr->estconnfunc)
++                                {
++                                        grpptr->estconnfunc(grpptr->port_num,
++                                                            -1,
++                                                            0);
++                                        grpptr->estconnfunc = NULL;
++                                }
++                                fsm_deltimer(&grpptr->timer);
++                                goto done;
++                        }
++                        if((wch->in_mpcgroup) && 
++                           (fsm_getstate(wch->fsm) == CH_XID0_PENDING))
++                                fsm_event(grpptr->fsm, MPCG_EVENT_XID0DO, wch);
++                        else
++                        {
++                                printk(KERN_WARNING "ctcmpc: %s() Unable to "
++                                       "start ACTIVE XID0 on write channel\n",
++                                       __FUNCTION__);
++                                if(grpptr->estconnfunc)
++                                {
++                                        grpptr->estconnfunc(grpptr->port_num,
++                                                            -1,0);
++                                        grpptr->estconnfunc = NULL;
++                                }
++                                fsm_deltimer(&grpptr->timer);
++                                goto done;
++
++                        }
++                        break;
++                case MPCG_STATE_XID0IOWAIT:
++                        /* already in active XID negotiations */
++                default:
++                        break;
++        }
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return;
++}
++
++static int ctcmpc_close(net_device *);
++
++void 
++ctc_mpc_dealloc_ch(int port_num)
++{
++        net_device *dev;
++        char device[20];
++        char *devnam = "mpc";
++        ctc_priv *privptr;
++        mpc_group *grpptr;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        sprintf(device,"%s%i",devnam,port_num);
++        dev = __dev_get_by_name(device);
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "%s() %s dev=NULL\n",__FUNCTION__,device);
++                goto done;
++        }
++
++        printk(KERN_INFO "%s: %s called for device %s refcount=%d\n", 
++               dev->name,__FUNCTION__,dev->name,atomic_read(&dev->refcnt));
++
++        privptr  = (ctc_priv *)dev->priv;
++        if(privptr == NULL)
++        {
++                printk(KERN_INFO "%s() %s privptr=NULL\n",__FUNCTION__,device);
++                goto done;
++        }
++        fsm_deltimer(&privptr->restart_timer);
++
++        grpptr = privptr->mpcg;
++        if(grpptr == NULL)
++        {
++                printk(KERN_INFO "%s() %s dev=NULL\n",__FUNCTION__,device);
++                goto done;
++        }
++        grpptr->channels_terminating = 0;
++
++        fsm_deltimer(&grpptr->timer);
++
++        grpptr->allochanfunc = NULL;
++        grpptr->estconnfunc = NULL; 
++
++        grpptr->port_persist = 0;
++
++        grpptr->send_qllc_disc = 0;
++
++        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++
++        ctcmpc_close(dev);
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++        return;
++}
++
++void 
++ctc_mpc_flow_control(int port_num,int flowc)
++{
++        char device[20];
++        char *devnam = "mpc";
++        ctc_priv *privptr;
++        mpc_group *grpptr;
++        net_device *dev;
++        channel *rch = NULL;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %i\n", __FUNCTION__,flowc);
++#endif
++
++        sprintf(device,"%s%i",devnam,port_num);
++        dev = __dev_get_by_name(device);
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "ctc_mpc_flow_control %s dev=NULL\n",device);
++                return;
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s: %s called \n", 
++               dev->name,__FUNCTION__);
++#endif
++
++        privptr  = (ctc_priv *)dev->priv;
++        if(privptr == NULL)
++        {
++                printk(KERN_INFO "ctc_mpc_flow_control %s privptr=NULL\n",
++                       device);
++                return;
++        }
++        grpptr = privptr->mpcg;
++        rch = privptr->channel[READ];
++
++        switch(flowc)
++        {
++                case 1: 
++                        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_FLOWC)
++                                break;
++                        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_READY)
++                        {
++                                if(grpptr->flow_off_called == 1)
++                                        grpptr->flow_off_called = 0;
++                                else
++                                        fsm_newstate(grpptr->fsm, 
++                                                     MPCG_STATE_FLOWC); 
++                                break;
++                        }
++                        break;
++                case 0:
++                        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_FLOWC)
++                        {
++                                fsm_newstate(grpptr->fsm, MPCG_STATE_READY); 
++                                /* ensure any data that has accumulated */
++                                /* on the io_queue will now be sent     */
++                                tasklet_schedule(&rch->ch_tasklet);
++                        }
++                        /* possible race condition                      */
++                        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_READY)
++                        {
++                                grpptr->flow_off_called = 1;
++                                break;
++                        }
++                        break;
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %i\n", __FUNCTION__,flowc);
++#endif
++
++
++}
++
++static int ctcmpc_send_qllc_discontact(net_device *);
++/*********************************************************************/
++/*
++        invoked when the device transitions to dev_stopped
++        MPC will stop each individual channel if a single XID failure
++        occurs, or will intitiate all channels be stopped if a GROUP
++        level failure occurs.
++*/
++/*********************************************************************/
++
++static void 
++ctcmpc_action_go_inop(fsm_instance *fi, int event, void *arg)
++{
++        net_device  *dev = (net_device *)arg;
++        ctc_priv    *privptr;
++        mpc_group   *grpptr;
++        int rc = 0;
++        channel *wch,*rch;
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "%s() dev=NULL\n",__FUNCTION__);
++                return;
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s enter:  %s()\n", dev->name,__FUNCTION__);
++#endif
++
++        privptr  = (ctc_priv *)dev->priv;
++        grpptr =  privptr->mpcg;
++        grpptr->flow_off_called = 0;
++
++        fsm_deltimer(&grpptr->timer);
++
++        if(grpptr->channels_terminating)
++                goto done;
++
++        grpptr->channels_terminating = 1;
++
++        grpptr->saved_state = fsm_getstate(grpptr->fsm);
++        fsm_newstate(grpptr->fsm,MPCG_STATE_INOP);
++        if(grpptr->saved_state > MPCG_STATE_XID7INITF)
++                printk(KERN_NOTICE "%s:MPC GROUP INOPERATIVE\n", dev->name);
++        if((grpptr->saved_state != MPCG_STATE_RESET) ||
++           /* dealloc_channel has been called */
++           ((grpptr->saved_state == MPCG_STATE_RESET) &&
++            (grpptr->port_persist == 0)))
++                fsm_deltimer(&privptr->restart_timer);
++
++        wch = privptr->channel[WRITE];   
++        rch = privptr->channel[READ];    
++
++        switch(grpptr->saved_state)
++        {
++                case MPCG_STATE_RESET:
++                case MPCG_STATE_INOP:
++                case MPCG_STATE_XID2INITW:
++                case MPCG_STATE_XID0IOWAIT:
++                case MPCG_STATE_XID2INITX:
++                case MPCG_STATE_XID7INITW:
++                case MPCG_STATE_XID7INITX:
++                case MPCG_STATE_XID0IOWAIX:
++                case MPCG_STATE_XID7INITI:
++                case MPCG_STATE_XID7INITZ:
++                case MPCG_STATE_XID7INITF:
++                        break;
++                case MPCG_STATE_FLOWC:
++                case MPCG_STATE_READY:
++                default:
++                        tasklet_hi_schedule(&wch->ch_disc_tasklet); 
++        }
++
++        grpptr->xid2_tgnum = 0;
++        grpptr->group_max_buflen = 0;  /*min of all received */
++        grpptr->outstanding_xid2 = 0;
++        grpptr->outstanding_xid7 = 0;
++        grpptr->outstanding_xid7_p2 = 0;
++        grpptr->saved_xid2 = NULL;
++        grpptr->xidnogood = 0;
++        grpptr->changed_side = 0;
++
++        grpptr->rcvd_xid_skb->data = 
++                grpptr->rcvd_xid_skb->tail = grpptr->rcvd_xid_data;
++        grpptr->rcvd_xid_skb->len = 0;
++        grpptr->rcvd_xid_th = (th_header *)grpptr->rcvd_xid_skb->data;
++        memcpy(skb_put(grpptr->rcvd_xid_skb,TH_HEADER_LENGTH),
++               &thnorm,
++               TH_HEADER_LENGTH);
++
++        if(grpptr->send_qllc_disc == 1)
++        {
++                grpptr->send_qllc_disc = 0;
++                rc = ctcmpc_send_qllc_discontact(dev);
++        }
++
++        /* DO NOT issue DEV_EVENT_STOP directly out of this code */
++        /* This can result in INOP of VTAM PU due to halting of  */
++        /* outstanding IO which causes a sense to be returned    */
++        /* Only about 3 senses are allowed and then IOS/VTAM will*/
++        /* ebcome unreachable without manual intervention        */
++        if((grpptr->port_persist == 1)  || (grpptr->alloc_called))
++        {
++                grpptr->alloc_called = 0;
++                fsm_deltimer(&privptr->restart_timer);
++                fsm_addtimer(&privptr->restart_timer, 
++                             500, 
++                             DEV_EVENT_RESTART, 
++                             dev);
++                fsm_newstate(grpptr->fsm, MPCG_STATE_RESET);
++                if(grpptr->saved_state > MPCG_STATE_XID7INITF)
++                        printk(KERN_NOTICE "%s:MPC GROUP RECOVERY SCHEDULED\n", 
++                               dev->name);
++        } else
++        {
++                fsm_deltimer(&privptr->restart_timer);
++                fsm_addtimer(&privptr->restart_timer, 500, DEV_EVENT_STOP, dev);
++                fsm_newstate(grpptr->fsm, MPCG_STATE_RESET);
++                printk(KERN_NOTICE "%s:MPC GROUP RECOVERY NOT ATTEMPTED\n", 
++                       dev->name);
++        }
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
++#endif
++
++        return;
++}
++
++
++
++static void 
++ctcmpc_action_timeout(fsm_instance *fi, int event, void *arg)
++{
++        net_device *dev = (net_device *)arg;
++        ctc_priv *privptr;
++        mpc_group *grpptr;
++        channel *wch;
++        channel *rch;
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "%s() dev=NULL\n",__FUNCTION__);
++                return;
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s enter:  %s()\n", dev->name,__FUNCTION__);
++#endif
++
++        privptr = (ctc_priv *)dev->priv;
++        grpptr = privptr->mpcg;
++        wch = privptr->channel[WRITE];   
++        rch = privptr->channel[READ];    
++
++
++        switch(fsm_getstate(grpptr->fsm))
++        {
++                case MPCG_STATE_XID2INITW:
++                        /* Unless there is outstanding IO on the  */
++                        /* channel just return and wait for ATTN  */
++                        /* interrupt to begin XID negotiations    */
++                        if((fsm_getstate(rch->fsm) == CH_XID0_PENDING) &&
++                           (fsm_getstate(wch->fsm) == CH_XID0_PENDING))
++                                break;
++                default:
++                        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
++#endif
++
++
++        return;
++}
++
++
++static void 
++ctcmpc_action_discontact(fsm_instance *fi, int event, void *arg)
++{
++        mpcg_info   *mpcginfo   = (mpcg_info *)arg;
++        channel     *ch         = mpcginfo->ch;
++        net_device  *dev        = ch->netdev;
++        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
++        mpc_group   *grpptr     = privptr->mpcg;
++
++
++        if(ch == NULL)
++        {
++                printk(KERN_INFO "%s() ch=NULL\n",__FUNCTION__);
++                return;
++        }
++        if(ch->netdev == NULL)
++        {
++                printk(KERN_INFO "%s() dev=NULL, irq=%d\n",__FUNCTION__,
++                       ch->irq);
++                return;
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s enter:  %s()\n", dev->name,__FUNCTION__);
++#endif
++
++        grpptr->send_qllc_disc = 1;
++        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
++#endif
++
++        return;
++}
++
++static void 
++ctcmpc_action_send_discontact(unsigned long thischan)
++{
++        channel *ch = (channel *)thischan;  
++        int rc = 0;
++        unsigned long saveflags;
++#ifdef DEBUG
++	net_device *dev = ch->netdev;
++	ctc_priv *privptr = (ctc_priv *)dev->priv;
++	mpc_group = *grpptr = privptr->mpcg;
++#endif
++
++
++#ifdef DEBUG
++
++        printk(KERN_INFO "%s cp:%i enter: %s() irq=%d GrpState:%s ChState:%s\n", 
++               dev->name,
++               smp_processor_id(),
++               __FUNCTION__,
++               ch->irq,
++               fsm_getstate_str(grpptr->fsm),
++               fsm_getstate_str(ch->fsm));
++#endif
++
++        s390irq_spin_lock_irqsave(ch->irq,saveflags);
++        rc = do_IO(ch->irq, &ch->ccw[15], (intparm_t)ch, 0xff, 0);
++        s390irq_spin_unlock_irqrestore(ch->irq,saveflags);
++
++        if(rc != 0)
++        {
++#ifdef DEBUG
++                printk(KERN_INFO "%s() %04x do_IO failed \n", 
++                       __FUNCTION__,ch->devno);
++                ccw_check_return_code(ch, rc);
++#endif
++                /* Not checking return code value here */
++                /* Making best effort to notify partner*/
++                /* that MPC Group is going down        */
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
++#endif
++
++        return;
++}
++
++
++
++static int 
++ctcmpc_validate_xid(mpcg_info *mpcginfo)
++{
++        channel     *ch         = mpcginfo->ch;
++        net_device  *dev        = ch->netdev;
++        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
++        mpc_group   *grpptr     = privptr->mpcg;
++        xid2        *xid        = mpcginfo->xid;
++        int         failed      = 0;
++        int         rc          = 0;
++        __u64       our_id,their_id = 0;
++        int         len;
++
++        len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++
++        if(mpcginfo->xid == NULL)
++        {
++                printk(KERN_INFO "%s() xid=NULL, irq=%d\n",__FUNCTION__,
++                       ch->irq);
++                rc = 1;
++                goto done;
++        }
++
++#ifdef DEBUGXID
++        printk(KERN_INFO "ctcmpc :  %s  xid received()\n", __FUNCTION__);
++        dumpit((char *)mpcginfo->xid,XID2_LENGTH);
++#endif
++        /*the received direction should be the opposite of ours            */
++        if(((CHANNEL_DIRECTION(ch->flags) == READ) ? 
++            XID2_WRITE_SIDE : XID2_READ_SIDE )
++           != xid->xid2_dlc_type)
++        {
++                failed = 1;
++                printk(KERN_INFO "%s XID REJECTED - READ-WRITE CH "
++                       "Pairing Invalid \n",
++                       __FUNCTION__);
++        }
++
++        if(xid->xid2_dlc_type == XID2_READ_SIDE)
++        {
++#ifdef DEBUGDATA
++                printk(KERN_INFO "%s(): grpmaxbuf:%d xid2buflen:%d\n", 
++                       __FUNCTION__,
++                       grpptr->group_max_buflen,
++                       xid->xid2_buf_len);
++#endif
++
++                if(grpptr->group_max_buflen == 0)
++                        grpptr->group_max_buflen = xid->xid2_buf_len - len;
++                else
++                {
++                        if((xid->xid2_buf_len - len) < grpptr->group_max_buflen)
++                        {
++                                grpptr->group_max_buflen = 
++                                        xid->xid2_buf_len - len;
++                        }
++                }
++
++        }
++
++        if(grpptr->saved_xid2 == NULL)
++        {
++                grpptr->saved_xid2 = (xid2 *)grpptr->rcvd_xid_skb->tail;
++                memcpy(skb_put(grpptr->rcvd_xid_skb,XID2_LENGTH), 
++                       xid, 
++                       XID2_LENGTH);
++                grpptr->rcvd_xid_skb->data = 
++                        grpptr->rcvd_xid_skb->tail = 
++                        grpptr->rcvd_xid_data;
++                grpptr->rcvd_xid_skb->len = 0;
++
++                /* convert two 32 bit numbers into 1 64 bit for id compare */
++                our_id = (__u64)privptr->xid->xid2_adj_id;
++                our_id = our_id << 32;
++                our_id = our_id + privptr->xid->xid2_sender_id;
++                their_id = (__u64)xid->xid2_adj_id;
++                their_id = their_id << 32;
++                their_id = their_id + xid->xid2_sender_id;
++                /* lower id assume the xside role */
++                if(our_id < their_id)
++                {
++                        grpptr->roll = XSIDE;
++#ifdef DEBUGXID
++                        printk(KERN_INFO "ctcmpc :%s() WE HAVE LOW "
++                               "ID-TAKE XSIDE\n", __FUNCTION__);
++#endif
++                } else
++                {
++                        grpptr->roll = YSIDE;
++#ifdef DEBUGXID
++                        printk(KERN_INFO "ctcmpc :%s() WE HAVE HIGH "
++                               "ID-TAKE YSIDE\n", __FUNCTION__);
++#endif
++                } 
++
++        } else
++        {
++                if(xid->xid2_flag4 != grpptr->saved_xid2->xid2_flag4)
++                {
++                        failed = 1;
++                        printk(KERN_INFO "%s XID REJECTED - XID Flag Byte4\n",
++                               __FUNCTION__);
++
++                }
++                if(xid->xid2_flag2 == 0x40)
++                {
++                        failed = 1;
++                        printk(KERN_INFO "%s XID REJECTED - XID NOGOOD\n",
++                               __FUNCTION__);
++
++                }
++                if(xid->xid2_adj_id != grpptr->saved_xid2->xid2_adj_id)
++                {
++                        failed = 1;
++                        printk(KERN_INFO "%s XID REJECTED - "
++                               "Adjacent Station ID Mismatch\n",
++                               __FUNCTION__);
++
++                }
++                if(xid->xid2_sender_id != grpptr->saved_xid2->xid2_sender_id)
++                {
++                        failed = 1;
++                        printk(KERN_INFO "%s XID REJECTED - "
++                               "Sender Address Mismatch\n",
++                               __FUNCTION__);
++
++                }
++        }
++
++        if(failed)
++        {
++#ifdef DEBUG
++                printk(KERN_INFO "ctcmpc     :  %s() failed\n", __FUNCTION__);
++#endif
++
++                privptr->xid->xid2_flag2 = 0x40;
++                grpptr->saved_xid2->xid2_flag2 = 0x40;
++                rc = 1;
++                goto done;
++        }
++
++        done:
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return(rc);
++}
++
++
++static void 
++ctcmpc_action_yside_xid(fsm_instance *fsm,int event, void *arg) 
++{
++        channel           *ch         = (channel *)arg;
++        ctc_priv          *privptr;
++        mpc_group         *grpptr     = NULL;
++        int               rc          = 0;
++        unsigned long     saveflags;
++        int               gotlock     = 0;
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc cp:%i enter:  %s()  %04x\n", 
++               smp_processor_id(),__FUNCTION__,ch->devno);
++#endif
++
++        if(ch == NULL)
++        {
++                printk(KERN_INFO "%s ch=NULL\n",__FUNCTION__);
++                goto done;
++        }
++
++        net_device *dev = ch->netdev;
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "%s dev=NULL, irq=%d\n",
++                       __FUNCTION__,
++                       ch->irq);
++                goto done;
++        }
++
++        privptr = (ctc_priv *)dev->priv;
++        if(privptr == NULL)
++        {
++                printk(KERN_INFO "%s privptr=NULL, irq=%d\n",
++                       __FUNCTION__,
++                       ch->irq);
++                goto done;
++        }
++
++        grpptr = privptr->mpcg;
++        if(grpptr == NULL)
++        {
++                printk(KERN_INFO "%s grpptr=NULL, irq=%d\n",
++                       __FUNCTION__,
++                       ch->irq);
++                goto done;
++        }
++
++        if(ctcmpc_checkalloc_buffer(ch, 0))
++        {
++                rc = -ENOMEM;
++                goto done;
++        }
++
++
++        ch->trans_skb->data =  ch->trans_skb->tail = ch->trans_skb_data;
++        ch->trans_skb->len = 0;
++        memset(ch->trans_skb->data, 0, 16);
++        ch->rcvd_xid_th =  (th_header *)ch->trans_skb->data;
++        skb_put(ch->trans_skb,TH_HEADER_LENGTH);
++        ch->rcvd_xid = (xid2 *)ch->trans_skb->tail;
++        skb_put(ch->trans_skb,XID2_LENGTH);
++        ch->rcvd_xid_id = ch->trans_skb->tail;
++        ch->trans_skb->data =  ch->trans_skb->tail = ch->trans_skb_data;
++        ch->trans_skb->len = 0;
++
++        ch->ccw[8].flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[8].count        = 0;
++        ch->ccw[8].cda          = 0x00;  
++
++        if(ch->rcvd_xid_th == NULL)
++        {
++                printk(KERN_INFO "%s ch->rcvd_xid_th=NULL, irq=%d\n",
++                       __FUNCTION__,
++                       ch->irq);
++                goto done;
++        }
++        ch->ccw[9].cmd_code     = CCW_CMD_READ;
++        ch->ccw[9].flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[9].count        = TH_HEADER_LENGTH;
++        ch->ccw[9].cda          = virt_to_phys(ch->rcvd_xid_th);
++
++        if(ch->rcvd_xid == NULL)
++        {
++                printk(KERN_INFO "%s ch->rcvd_xid=NULL, irq=%d\n",
++                       __FUNCTION__,
++                       ch->irq);
++                goto done;
++        }
++        ch->ccw[10].cmd_code    = CCW_CMD_READ;
++        ch->ccw[10].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[10].count       = XID2_LENGTH;
++        ch->ccw[10].cda         = virt_to_phys(ch->rcvd_xid);
++
++        if(ch->xid_th == NULL)
++        {
++                printk(KERN_INFO "%s ch->xid_th=NULL, irq=%d\n",
++                       __FUNCTION__,
++                       ch->irq);
++                goto done;
++        }
++        ch->ccw[11].cmd_code    = CCW_CMD_WRITE;
++        ch->ccw[11].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[11].count       = TH_HEADER_LENGTH;
++        ch->ccw[11].cda         = virt_to_phys(ch->xid_th);
++
++        if(ch->xid == NULL)
++        {
++                printk(KERN_INFO "%s ch->xid=NULL, irq=%d\n",
++                       __FUNCTION__,
++                       ch->irq);
++                goto done;
++        }
++
++        ch->ccw[12].cmd_code    = CCW_CMD_WRITE;
++        ch->ccw[12].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[12].count       = XID2_LENGTH;
++        ch->ccw[12].cda         = virt_to_phys(ch->xid);
++
++        if(ch->xid_id == NULL)
++        {
++                printk(KERN_INFO "%s ch->xid_id=NULL, irq=%d\n",
++                       __FUNCTION__,
++                       ch->irq);
++                goto done;
++        }
++        ch->ccw[13].cmd_code    = CCW_CMD_WRITE;
++        ch->ccw[13].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[13].count       = 4;
++        ch->ccw[13].cda         = virt_to_phys(ch->xid_id);
++
++        ch->ccw[14].cmd_code    = CCW_CMD_NOOP;
++        ch->ccw[14].flags       = CCW_FLAG_SLI;
++        ch->ccw[14].count       = 0;
++        ch->ccw[14].cda         = 0;
++
++
++#ifdef DEBUGCCW
++        dumpit((char *)&ch->ccw[8],sizeof(ccw1_t) * 7);
++#endif
++#ifdef DEBUGXID
++        dumpit((char *)ch->xid_th,TH_HEADER_LENGTH);
++        dumpit((char *)ch->xid,XID2_LENGTH);
++        dumpit((char *)ch->xid_id,4);
++#endif
++
++
++        if(!in_irq())
++        {
++                s390irq_spin_lock_irqsave(ch->irq,saveflags);
++                gotlock = 1;
++        }
++
++        fsm_addtimer(&ch->timer, 5000 , CH_EVENT_TIMER, ch);
++        rc = do_IO(ch->irq, &ch->ccw[8], (intparm_t)ch, 0xff, 0);
++
++        if(gotlock)
++                s390irq_spin_unlock_irqrestore(ch->irq,saveflags);
++
++        if(rc != 0)
++        {
++#ifdef DEBUG
++                printk(KERN_INFO "ctcmpc: %s() %04x do_IO failed \n", 
++                       __FUNCTION__,ch->devno);
++#endif
++                ccw_check_return_code(ch, rc);
++                goto done;
++        }
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()  %04x\n", 
++               __FUNCTION__, ch->devno);
++#endif
++
++        return;
++
++}       
++
++static void 
++ctcmpc_action_doxid0(fsm_instance *fsm,int event, void *arg)    
++{
++        channel     *ch         = (channel *)arg;
++        ctc_priv    *privptr;
++        mpc_group   *grpptr     = NULL;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()  %04x\n", 
++               __FUNCTION__, ch->devno);
++#endif
++
++        if(ch == NULL)
++        {
++                printk(KERN_WARNING "%s ch=NULL\n",__FUNCTION__);
++                goto done;
++        }
++
++        net_device *dev = ch->netdev;
++        if(dev == NULL)
++        {
++                printk(KERN_WARNING "%s dev=NULL, irq=%d\n",
++                       __FUNCTION__, ch->irq);
++                goto done;
++        }
++
++        privptr = (ctc_priv *)dev->priv;
++        if(privptr == NULL)
++        {
++                printk(KERN_WARNING "%s privptr=NULL, irq=%d\n",
++                       __FUNCTION__, ch->irq);
++                goto done;
++        }
++
++        grpptr = privptr->mpcg;
++        if(grpptr == NULL)
++        {
++                printk(KERN_WARNING "%s grpptr=NULL, irq=%d\n",
++                       __FUNCTION__, ch->irq);
++                goto done;
++        }
++
++
++        if(ch->xid == NULL)
++        {
++                printk(KERN_WARNING "%s ch-xid=NULL, irq=%d\n",
++                       __FUNCTION__,ch->irq);
++                goto done;
++        }
++
++        fsm_newstate(ch->fsm, CH_XID0_INPROGRESS);
++
++        ch->xid->xid2_option =  XID2_0;
++
++        switch(fsm_getstate(grpptr->fsm))
++        {
++                case MPCG_STATE_XID2INITW:
++                case MPCG_STATE_XID2INITX:
++                        ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
++                        break;
++                case MPCG_STATE_XID0IOWAIT:
++                case MPCG_STATE_XID0IOWAIX:
++                        ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
++                        break;
++        }
++
++        fsm_event(grpptr->fsm,MPCG_EVENT_DOIO,ch);
++
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()  %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++        return;
++
++}       
++
++
++
++static void 
++ctcmpc_action_doxid7(fsm_instance *fsm,int event, void *arg)
++{
++        net_device  *dev        = (net_device *)arg;
++        int         direction;
++        int         rc          = 0;
++        int         send        = 0;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() \n", __FUNCTION__);
++#endif
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "%s dev=NULL \n",__FUNCTION__);
++                rc = 1;
++                goto done;
++        }
++
++        ctc_priv   *privptr = (ctc_priv *)dev->priv;
++        if(privptr == NULL)
++        {
++                printk(KERN_INFO "%s privptr=NULL \n",__FUNCTION__);
++                rc = 1;
++                goto done;
++        }
++
++        mpc_group  *grpptr = privptr->mpcg;
++        if(grpptr == NULL)
++        {
++                printk(KERN_INFO "%s grpptr=NULL \n",__FUNCTION__);
++                rc = 1;
++                goto done;
++        }
++
++        for(direction = READ; direction <= WRITE; direction++)
++        {
++                channel *ch = privptr->channel[direction];
++                xid2 *thisxid = ch->xid;
++                ch->xid_skb->data = ch->xid_skb->tail = ch->xid_skb_data;
++                ch->xid_skb->len = 0;
++                thisxid->xid2_option = XID2_7;
++                send = 0;
++
++                /* xid7 phase 1 */
++                if(grpptr->outstanding_xid7_p2 > 0)
++                {
++                        if(grpptr->roll == YSIDE)
++                        {
++                                if(fsm_getstate(ch->fsm) == CH_XID7_PENDING1)
++                                {
++                                        fsm_newstate(ch->fsm,CH_XID7_PENDING2);
++                                        ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
++                                        memcpy(skb_put(ch->xid_skb,
++                                                       TH_HEADER_LENGTH),
++                                               &thdummy,TH_HEADER_LENGTH);
++                                        send = 1;
++                                }
++                        } else
++                        {
++                                if(fsm_getstate(ch->fsm) < CH_XID7_PENDING2)
++                                {
++                                        fsm_newstate(ch->fsm,CH_XID7_PENDING2);
++                                        ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
++                                        memcpy(skb_put(ch->xid_skb,
++                                                       TH_HEADER_LENGTH),
++                                               &thnorm,TH_HEADER_LENGTH);
++                                        send = 1;
++                                }
++                        }
++                }
++                /* xid7 phase 2 */
++                else
++                {
++                        if(grpptr->roll == YSIDE)
++                        {
++                                if(fsm_getstate(ch->fsm) < CH_XID7_PENDING4)
++                                {
++                                        fsm_newstate(ch->fsm,CH_XID7_PENDING4);
++                                        memcpy(skb_put(ch->xid_skb,
++                                                       TH_HEADER_LENGTH),
++                                               &thnorm,TH_HEADER_LENGTH);
++                                        ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
++                                        send = 1;
++                                }
++                        } else
++                        {
++                                if(fsm_getstate(ch->fsm) == CH_XID7_PENDING3)
++                                {
++                                        fsm_newstate(ch->fsm,CH_XID7_PENDING4);
++                                        ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
++                                        memcpy(skb_put(ch->xid_skb,
++                                                       TH_HEADER_LENGTH),
++                                               &thdummy,TH_HEADER_LENGTH);
++                                        send = 1;
++                                }
++                        } 
++                }
++
++                if(send)
++                        fsm_event(grpptr->fsm,MPCG_EVENT_DOIO,ch);
++        }
++
++        done:
++
++        if(rc != 0)
++                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return;
++}
++
++
++
++static void 
++ctcmpc_action_xside_xid(fsm_instance *fsm,int event, void *arg)
++{
++        channel           *ch         = (channel *)arg;
++        ctc_priv          *privptr;
++        mpc_group         *grpptr     = NULL;
++        int               rc          = 0;
++        unsigned long     saveflags;
++        int               gotlock     = 0;
++
++        if(ch == NULL)
++        {
++                printk(KERN_INFO "%s ch=NULL\n",__FUNCTION__);
++                goto done;
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc cp:%i enter:  %s()  %04x\n", 
++               smp_processor_id(),__FUNCTION__,ch->devno);
++#endif
++
++
++        net_device *dev = ch->netdev;
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "%s dev=NULL, irq=%d\n",__FUNCTION__,ch->irq);
++                goto done;
++        }
++
++        privptr = (ctc_priv *)dev->priv;
++        if(privptr == NULL)
++        {
++                printk(KERN_INFO "%s privptr=NULL, irq=%d\n",
++                       __FUNCTION__,ch->irq);
++                goto done;
++        }
++
++        grpptr = privptr->mpcg;
++        if(grpptr == NULL)
++        {
++                printk(KERN_INFO "%s grpptr=NULL, irq=%d\n",
++                       __FUNCTION__,ch->irq);
++                goto done;
++        }
++
++        if(ctcmpc_checkalloc_buffer(ch, 0))
++        {
++                rc = -ENOMEM;
++                goto done;
++        }
++
++        ch->trans_skb->data =  ch->trans_skb->tail = ch->trans_skb_data;
++        ch->trans_skb->len = 0;
++        memset(ch->trans_skb->data, 0, 16);
++        ch->rcvd_xid_th =  (th_header *)ch->trans_skb->data;
++        skb_put(ch->trans_skb,TH_HEADER_LENGTH);
++        ch->rcvd_xid = (xid2 *)ch->trans_skb->tail;
++        skb_put(ch->trans_skb,XID2_LENGTH);
++        ch->rcvd_xid_id = ch->trans_skb->tail;
++        ch->trans_skb->data =  ch->trans_skb->tail = ch->trans_skb_data;
++        ch->trans_skb->len = 0;
++
++        ch->ccw[8].flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[8].count        = 0;
++        ch->ccw[8].cda        = 0x00;  /* null   */
++
++        if(ch->xid_th == NULL)
++        {
++                printk(KERN_INFO "%s ch->xid_th=NULL, irq=%d\n",
++                       __FUNCTION__,ch->irq);
++                goto done;
++        }
++        ch->ccw[9].cmd_code     = CCW_CMD_WRITE;
++        ch->ccw[9].flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[9].count        = TH_HEADER_LENGTH;
++        ch->ccw[9].cda          = virt_to_phys(ch->xid_th);
++
++        if(ch->xid == NULL)
++        {
++                printk(KERN_INFO "%s ch->xid=NULL, irq=%d\n",
++                       __FUNCTION__,ch->irq);
++                goto done;
++        }
++
++        ch->ccw[10].cmd_code    = CCW_CMD_WRITE;
++        ch->ccw[10].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[10].count       = XID2_LENGTH;
++        ch->ccw[10].cda         = virt_to_phys(ch->xid);
++
++        if(ch->rcvd_xid_th == NULL)
++        {
++                printk(KERN_INFO "%s ch->rcvd_xid_th=NULL, irq=%d\n",
++                       __FUNCTION__,ch->irq);
++                goto done;
++        }
++        ch->ccw[11].cmd_code    = CCW_CMD_READ;
++        ch->ccw[11].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[11].count       = TH_HEADER_LENGTH;
++        ch->ccw[11].cda         = virt_to_phys(ch->rcvd_xid_th);
++
++        if(ch->rcvd_xid == NULL)
++        {
++                printk(KERN_INFO "%s ch->rcvd_xid=NULL, irq=%d\n",
++                       __FUNCTION__,ch->irq);
++                goto done;
++        }
++        ch->ccw[12].cmd_code    = CCW_CMD_READ;
++        ch->ccw[12].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[12].count       = XID2_LENGTH;
++        ch->ccw[12].cda         = virt_to_phys(ch->rcvd_xid);
++
++        if(ch->xid_id == NULL)
++        {
++                printk(KERN_INFO "%s ch->xid_id=NULL, irq=%d\n",
++                       __FUNCTION__,ch->irq);
++                goto done;
++        }
++        ch->ccw[13].cmd_code    = CCW_CMD_READ;
++        ch->ccw[13].flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[13].count       = 4;
++        ch->ccw[13].cda         = virt_to_phys(ch->rcvd_xid_id);
++
++        ch->ccw[14].cmd_code    = CCW_CMD_NOOP;
++        ch->ccw[14].flags       = CCW_FLAG_SLI;
++        ch->ccw[14].count       = 0;
++        ch->ccw[14].cda         = 0;
++
++#ifdef DEBUGCCW
++        dumpit((char *)&ch->ccw[8],sizeof(ccw1_t) * 7);
++#endif
++#ifdef DEBUGXID
++        dumpit((char *)ch->xid_th,TH_HEADER_LENGTH);
++        dumpit((char *)ch->xid,XID2_LENGTH);
++#endif
++
++        if(!in_irq())
++        {
++                s390irq_spin_lock_irqsave(ch->irq,saveflags);
++                gotlock = 1;
++        }
++
++        fsm_addtimer(&ch->timer, 5000 , CH_EVENT_TIMER, ch);
++        rc = do_IO(ch->irq, &ch->ccw[8], (intparm_t)ch, 0xff, 0);
++
++        if(gotlock)
++                s390irq_spin_unlock_irqrestore(ch->irq,saveflags);
++
++        if(rc != 0)
++        {
++
++#ifdef DEBUG
++                printk(KERN_INFO "ctcmpc: %s() %04x do_IO failed \n", 
++                       __FUNCTION__,ch->devno);
++#endif
++                ccw_check_return_code(ch, rc);
++                goto done;
++        }
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++        return;
++}
++
++
++
++static void 
++ctcmpc_action_rcvd_xid0(fsm_instance *fsm,int event, void *arg)
++{
++
++        mpcg_info   *mpcginfo   = (mpcg_info *)arg;
++        channel     *ch         = mpcginfo->ch;
++        net_device  *dev        = ch->netdev;
++        ctc_priv    *privptr;
++        mpc_group   *grpptr;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++        privptr = (ctc_priv *)dev->priv;
++        grpptr = privptr->mpcg;
++
++#ifdef DEBUGXID
++        printk(KERN_INFO "ctcmpc in:%s() %04x xid2:%i xid7:%i xidt_p2:%i \n", 
++               __FUNCTION__,ch->devno,
++               grpptr->outstanding_xid2,
++               grpptr->outstanding_xid7,
++               grpptr->outstanding_xid7_p2); 
++#endif
++
++        if(fsm_getstate(ch->fsm) < CH_XID7_PENDING)
++                fsm_newstate(ch->fsm,CH_XID7_PENDING);
++
++        grpptr->outstanding_xid2--;
++        grpptr->outstanding_xid7++;
++        grpptr->outstanding_xid7_p2++;
++
++        /* must change state before validating xid to */
++        /* properly handle interim interrupts received*/
++        switch(fsm_getstate(grpptr->fsm))
++        {
++                case MPCG_STATE_XID2INITW:
++                        fsm_newstate(grpptr->fsm,MPCG_STATE_XID2INITX);
++                        ctcmpc_validate_xid(mpcginfo);
++                        break;
++                case MPCG_STATE_XID0IOWAIT:
++                        fsm_newstate(grpptr->fsm,MPCG_STATE_XID0IOWAIX);
++                        ctcmpc_validate_xid(mpcginfo);
++                        break;
++                case MPCG_STATE_XID2INITX:       
++                        if(grpptr->outstanding_xid2 == 0)
++                        {
++                                fsm_newstate(grpptr->fsm,MPCG_STATE_XID7INITW);
++                                ctcmpc_validate_xid(mpcginfo);
++                                fsm_event(grpptr->fsm,MPCG_EVENT_XID2DONE,dev);
++                        }
++                        break;
++                case MPCG_STATE_XID0IOWAIX:
++                        if(grpptr->outstanding_xid2 == 0)
++                        {
++                                fsm_newstate(grpptr->fsm,MPCG_STATE_XID7INITI);
++                                ctcmpc_validate_xid(mpcginfo);
++                                fsm_event(grpptr->fsm,MPCG_EVENT_XID2DONE,dev);
++                        }
++                        break;
++        }
++        kfree(mpcginfo);
++
++#ifdef DEBUGXID
++        printk(KERN_INFO "ctcmpc out:%s() %04x xid2:%i xid7:%i xidt_p2:%i \n", 
++               __FUNCTION__,ch->devno,
++               grpptr->outstanding_xid2,
++               grpptr->outstanding_xid7,
++               grpptr->outstanding_xid7_p2); 
++        printk(KERN_INFO "ctcmpc out:%s() %04x groupstate: %s chanstate: %s \n", 
++               __FUNCTION__,ch->devno,
++               fsm_getstate_str(grpptr->fsm), 
++               fsm_getstate_str(ch->fsm)); 
++#endif
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", __FUNCTION__,ch->devno);
++#endif
++
++        return;
++
++}
++
++
++static void 
++ctcmpc_action_rcvd_xid7(fsm_instance *fsm,int event, void *arg)
++{
++
++        mpcg_info   *mpcginfo   = (mpcg_info *)arg;
++        channel     *ch         = mpcginfo->ch;
++        net_device  *dev        = ch->netdev;
++        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
++        mpc_group   *grpptr     = privptr->mpcg;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++#ifdef DEBUGXID
++        printk(KERN_INFO "ctcmpc:  outstanding_xid7: %i, "
++               "outstanding_xid7_p2: %i\n",
++               grpptr->outstanding_xid7,
++               grpptr->outstanding_xid7_p2);
++#endif
++
++
++        grpptr->outstanding_xid7--;
++
++        ch->xid_skb->data = ch->xid_skb->tail = ch->xid_skb_data;
++        ch->xid_skb->len = 0;
++
++        switch(fsm_getstate(grpptr->fsm))
++        {
++                case MPCG_STATE_XID7INITI:
++                        fsm_newstate(grpptr->fsm,MPCG_STATE_XID7INITZ);
++                        ctcmpc_validate_xid(mpcginfo);
++                        break;
++                case MPCG_STATE_XID7INITW:
++                        fsm_newstate(grpptr->fsm,MPCG_STATE_XID7INITX);
++                        ctcmpc_validate_xid(mpcginfo);
++                        break;
++                case MPCG_STATE_XID7INITZ: 
++                case MPCG_STATE_XID7INITX:
++                        if(grpptr->outstanding_xid7 == 0)
++                        {
++                                if(grpptr->outstanding_xid7_p2 > 0)
++                                {
++                                        grpptr->outstanding_xid7 = 
++                                                grpptr->outstanding_xid7_p2;
++                                        grpptr->outstanding_xid7_p2 = 0;
++                                } else
++                                        fsm_newstate(grpptr->fsm,
++                                                     MPCG_STATE_XID7INITF);
++                                ctcmpc_validate_xid(mpcginfo);
++                                fsm_event(grpptr->fsm,MPCG_EVENT_XID7DONE,dev);
++                                break;
++                        }
++                        ctcmpc_validate_xid(mpcginfo);
++                        break;
++        }
++
++        kfree(mpcginfo);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++        return;
++
++}
++
++static void 
++ctcmpc_action_attn(fsm_instance *fsm,int event, void *arg)
++{
++        channel     *ch         = (channel *)arg;
++        net_device  *dev        = ch->netdev;
++        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
++        mpc_group   *grpptr     = privptr->mpcg;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %04x  \nGrpState:%s ChState:%s\n", 
++               __FUNCTION__,ch->devno,
++               fsm_getstate_str(grpptr->fsm),
++               fsm_getstate_str(ch->fsm));
++#endif
++
++        switch(fsm_getstate(grpptr->fsm))
++        {
++                case MPCG_STATE_XID2INITW:
++                        /* ok..start yside xid exchanges */
++                        if(ch->in_mpcgroup)
++                        {
++                                if(fsm_getstate(ch->fsm) ==  CH_XID0_PENDING)
++                                {
++                                        fsm_deltimer(&grpptr->timer);
++                                        fsm_addtimer(&grpptr->timer, 
++                                                     MPC_XID_TIMEOUT_VALUE, 
++                                                     MPCG_EVENT_TIMER, 
++                                                     dev);
++                                        fsm_event(grpptr->fsm, 
++                                                  MPCG_EVENT_XID0DO, 
++                                                  ch);
++                                } else
++                                {/* attn rcvd before xid0 processed via bh */
++                                        if(fsm_getstate(ch->fsm) < 
++                                           CH_XID7_PENDING1)
++                                                fsm_newstate(ch->fsm,
++                                                             CH_XID7_PENDING1);
++                                }
++                        }
++                        break;
++                case MPCG_STATE_XID2INITX:
++                case MPCG_STATE_XID0IOWAIT:
++                case MPCG_STATE_XID0IOWAIX:
++                        /* attn rcvd before xid0 processed on ch  
++                        but mid-xid0 processing for group    */
++                        if(fsm_getstate(ch->fsm) < CH_XID7_PENDING1)
++                                fsm_newstate(ch->fsm,CH_XID7_PENDING1);
++                        break;
++                case MPCG_STATE_XID7INITW:
++                case MPCG_STATE_XID7INITX:
++                case MPCG_STATE_XID7INITI:
++                case MPCG_STATE_XID7INITZ:
++                        switch(fsm_getstate(ch->fsm))
++                        {
++                                case CH_XID7_PENDING:
++                                        fsm_newstate(ch->fsm,CH_XID7_PENDING1);
++                                        break;
++                                case CH_XID7_PENDING2:
++                                        fsm_newstate(ch->fsm,CH_XID7_PENDING3);
++                                        break;
++                        }
++                        fsm_event(grpptr->fsm,MPCG_EVENT_XID7DONE,dev);
++                        break;
++        }
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", __FUNCTION__,ch->devno);
++#endif
++        return;
++
++}
++
++static void 
++ctcmpc_action_attnbusy(fsm_instance *fsm,int event, void *arg)
++{
++        channel     *ch         = (channel *)arg;
++        net_device  *dev        = ch->netdev;
++        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
++        mpc_group   *grpptr     = privptr->mpcg;
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s enter:  %s() %04x  \nGrpState:%s ChState:%s\n", 
++               dev->name,
++               __FUNCTION__,ch->devno,
++               fsm_getstate_str(grpptr->fsm),
++               fsm_getstate_str(ch->fsm));
++#endif
++
++
++        fsm_deltimer(&ch->timer);
++
++        switch(fsm_getstate(grpptr->fsm))
++        {
++                case MPCG_STATE_XID0IOWAIT:
++                        /* vtam wants to be primary..start yside xid exchanges*/
++                        /* will only rcv  one attn-busy at a time so must not */
++                        /* change state each time                             */
++                        grpptr->changed_side = 1;
++                        fsm_newstate(grpptr->fsm,MPCG_STATE_XID2INITW);
++                        break;
++                case MPCG_STATE_XID2INITW:
++                        if(grpptr->changed_side == 1)
++                        {
++                                grpptr->changed_side = 2;
++                                break;
++                        }
++                        /* process began via call to establish_conn      */
++                        /* so must report failure instead of reverting   */
++                        /* back to ready-for-xid passive state           */
++                        if(grpptr->estconnfunc)
++                                goto done;
++                        /* this attnbusy is NOT the result of xside xid  */
++                        /* collisions so yside must have been triggered  */
++                        /* by an ATTN that was not intended to start XID */
++                        /* processing. Revert back to ready-for-xid and  */
++                        /* wait for ATTN interrupt to signal xid start   */
++                        if(fsm_getstate(ch->fsm) == CH_XID0_INPROGRESS)
++                        {
++                                fsm_newstate(ch->fsm,CH_XID0_PENDING) ;
++                                fsm_deltimer(&grpptr->timer);
++                                goto done;
++                        }
++                        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++                        goto done;
++                case MPCG_STATE_XID2INITX:
++                        /* XID2 was received before ATTN Busy for second
++                           channel.Send yside xid for second channel.
++                        */
++                        if(grpptr->changed_side == 1)
++                        {
++                                grpptr->changed_side = 2;
++                                break;
++                        }
++                case MPCG_STATE_XID0IOWAIX:
++                case MPCG_STATE_XID7INITW:
++                case MPCG_STATE_XID7INITX:
++                case MPCG_STATE_XID7INITI:
++                case MPCG_STATE_XID7INITZ:
++                default:
++                        /* multiple attn-busy indicates too out-of-sync      */
++                        /* and they are certainly not being received as part */
++                        /* of valid mpc group negotiations..                 */
++                        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++                        goto done;
++        }
++
++        if(grpptr->changed_side == 1)
++        {
++                fsm_deltimer(&grpptr->timer);
++                fsm_addtimer(&grpptr->timer, MPC_XID_TIMEOUT_VALUE, 
++                             MPCG_EVENT_TIMER, dev);
++        }
++        if(ch->in_mpcgroup)
++                fsm_event(grpptr->fsm, MPCG_EVENT_XID0DO, ch);
++        else
++                printk( KERN_WARNING "ctcmpc: %s() Not all channels have "
++                        "been added to group\n",
++                        __FUNCTION__);
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s() %04x\n", 
++               dev->name,__FUNCTION__,ch->devno);
++#endif
++        return;
++
++}
++
++static void 
++ctcmpc_action_resend(fsm_instance *fsm,int event, void *arg)
++{
++        channel     *ch         = (channel *)arg;
++        net_device  *dev        = ch->netdev;
++        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
++        mpc_group   *grpptr     = privptr->mpcg;
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s enter:  %s() %04x  \nGrpState:%s ChState:%s\n", 
++               dev->name,__FUNCTION__,ch->devno,
++               fsm_getstate_str(grpptr->fsm),
++               fsm_getstate_str(ch->fsm));
++#endif
++        fsm_event(grpptr->fsm, MPCG_EVENT_XID0DO, ch);
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s() %04x\n", 
++               dev->name,__FUNCTION__,ch->devno);
++#endif
++        return;
++
++}
++
++
++static int 
++ctcmpc_send_qllc_discontact(net_device *dev)
++{
++        int               rc = 0, space = 0;
++        __u32         new_len = 0;
++        struct sk_buff    *skb;
++        qllc              *qllcptr;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n",
++               __FUNCTION__);
++#endif
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "%s() dev=NULL\n",
++                       __FUNCTION__);
++                rc = 1;
++                goto done;
++        }
++
++        ctc_priv *privptr = (ctc_priv *)dev->priv;
++        if(privptr == NULL)
++        {
++                printk(KERN_INFO "%s() privptr=NULL\n",
++                       __FUNCTION__);
++                rc = 1;
++                goto done;
++        }
++
++        mpc_group       *grpptr = privptr->mpcg;
++        if(grpptr == NULL)
++        {
++                printk(KERN_INFO "%s() grpptr=NULL\n",
++                       __FUNCTION__);
++                rc = 1;
++                goto done;
++        }
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc: %s() GROUP STATE: %s\n", 
++               __FUNCTION__,mpcg_state_names[grpptr->saved_state]);
++#endif
++
++
++        switch(grpptr->saved_state)
++        {       /* establish conn callback function is */
++                /* preferred method to report failure  */
++                case MPCG_STATE_XID0IOWAIT:
++                case MPCG_STATE_XID0IOWAIX:
++                case MPCG_STATE_XID7INITI:
++                case MPCG_STATE_XID7INITZ:
++                case MPCG_STATE_XID2INITW:
++                case MPCG_STATE_XID2INITX:
++                case MPCG_STATE_XID7INITW:
++                case MPCG_STATE_XID7INITX:
++                        if(grpptr->estconnfunc)
++                        {
++                                grpptr->estconnfunc(grpptr->port_num,-1,0);
++                                grpptr->estconnfunc = NULL;
++                                break;
++                        }
++                case MPCG_STATE_FLOWC:
++                case MPCG_STATE_READY:
++                        grpptr->send_qllc_disc = 2;
++                        new_len = sizeof(qllc);
++                        if((qllcptr = (qllc *)kmalloc(sizeof(qllc), 
++                                                      gfp_type() | GFP_DMA)) 
++                           == NULL)
++                        {
++                                printk(KERN_INFO "qllc: Out of memory"
++                                       " in send_qllc\n");
++                                rc = 1;
++                                goto done;
++                        }
++
++                        memset(qllcptr, 0, new_len);
++                        qllcptr->qllc_address = 0xcc;
++                        qllcptr->qllc_commands = 0x03;
++
++                        skb = __dev_alloc_skb(new_len,GFP_ATOMIC);
++
++                        if(skb == NULL)
++                        {
++                                printk(KERN_INFO
++                                       "%s Out of memory in ctcmpc_send_qllc\n",
++                                       dev->name);
++                                privptr->stats.rx_dropped++;
++                                rc = 1;
++                                kfree(qllcptr);
++                                goto done;
++                        }
++
++                        memcpy(skb_put(skb, new_len), qllcptr, new_len);
++                        kfree(qllcptr);
++
++                        space = skb_headroom(skb);
++                        if(space < 4)
++                        {
++                                printk(KERN_INFO "%s Unable to build "
++                                       "discontact for %s\n",
++                                       __FUNCTION__,dev->name);
++                                rc = 1;
++                                dev_kfree_skb_any(skb);
++                                goto done;
++                        }
++
++                        *((__u32 *) skb_push(skb, 4)) = 
++                                privptr->channel[READ]->pdu_seq;
++                        privptr->channel[READ]->pdu_seq++;
++#ifdef DEBUGSEQ
++                        printk(KERN_INFO "%s: ToDCM_pdu_seq= %08x\n" ,
++                               __FUNCTION__,privptr->channel[READ]->pdu_seq);
++#endif
++                        /* receipt of CC03 resets anticipated sequence "
++                        "number on receiving side */
++                        privptr->channel[READ]->pdu_seq = 0x00;
++
++                        skb->mac.raw = skb->data;
++                        skb->dev = dev;
++                        skb->protocol = htons(ETH_P_SNAP);
++                        skb->ip_summed = CHECKSUM_UNNECESSARY;
++
++#ifdef DEBUGDATA
++                        dumpit((char *)skb->data,(sizeof(qllc)+4));
++#endif
++                        netif_rx(skb);
++                        break;
++                default: break;
++
++        }
++
++        done:
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return(rc);
++
++}
++
++static void 
++ctcmpc_send_sweep(fsm_instance *fsm,int event, void *arg)
++{
++        channel           *ach        = (channel *)arg;
++        net_device        *dev        = ach->netdev;
++        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
++        mpc_group         *grpptr     = privptr->mpcg;
++        int               rc          = 0;
++        sk_buff           *skb;
++        unsigned long     saveflags;
++        channel           *wch        = privptr->channel[WRITE];        
++        channel           *rch        = privptr->channel[READ];  
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc cp:%i enter:  %s() %04x\n", 
++               smp_processor_id(),__FUNCTION__,ach->devno);
++#endif
++
++        if(grpptr->in_sweep == 0)
++                goto done;
++
++#ifdef DEBUGSEQ
++        printk(KERN_INFO "%s 1: ToVTAM_th_seq= %08x\n" ,
++               __FUNCTION__,wch->th_seq_num);
++        printk(KERN_INFO "%s 1: FromVTAM_th_seq= %08x\n" ,
++               __FUNCTION__,rch->th_seq_num);
++#endif
++
++
++        if(fsm_getstate(wch->fsm) != CH_STATE_TXIDLE)
++        {
++                /* give the previous do_IO time to complete */
++                fsm_addtimer(&wch->sweep_timer,200,CH_EVENT_RSWEEP1_TIMER,wch);
++                goto done;
++        }
++
++        skb = skb_dequeue(&wch->sweep_queue);
++        if(!skb)
++                goto done;
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21))
++        if(set_normalized_cda(&wch->ccw[4], virt_to_phys(skb->data)))
++        {
++#else
++        if(set_normalized_cda(&wch->ccw[4], skb->data))
++        {
++#endif
++                grpptr->in_sweep = 0;
++                ctcmpc_clear_busy(dev);
++                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);     
++                goto done;
++        } else
++                skb_queue_tail(&wch->io_queue, skb);
++
++        /* send out the sweep */
++        wch->ccw[4].count = skb->len;
++
++        th_sweep *header = (th_sweep *)skb->data;
++        switch(header->th.th_ch_flag)
++        {
++                case TH_SWEEP_REQ:      
++                        grpptr->sweep_req_pend_num--;
++                        break;
++                case TH_SWEEP_RESP:     
++                        grpptr->sweep_rsp_pend_num--;
++                        break;
++        }
++
++        header->sw.th_last_seq  = wch->th_seq_num;
++
++#ifdef DEBUGCCW
++        dumpit((char *)&wch->ccw[3],sizeof(ccw1_t) * 3);
++#endif
++
++#ifdef DEBUGSEQ
++        printk(KERN_INFO "%s(): sweep packet\n", __FUNCTION__);
++        dumpit((char *)header,TH_SWEEP_LENGTH);
++#endif
++
++
++        fsm_addtimer(&wch->timer,CTC_TIMEOUT_5SEC,CH_EVENT_TIMER, wch);
++        fsm_newstate(wch->fsm, CH_STATE_TX);
++
++        s390irq_spin_lock_irqsave(wch->irq, saveflags);
++        wch->prof.send_stamp = xtime;
++        rc = do_IO(wch->irq, &wch->ccw[3], (intparm_t)wch, 0xff, 0);
++        s390irq_spin_unlock_irqrestore(wch->irq, saveflags);
++
++        if((grpptr->sweep_req_pend_num == 0) &&
++           (grpptr->sweep_rsp_pend_num == 0))
++        {
++                grpptr->in_sweep = 0;
++                rch->th_seq_num = 0x00;
++                wch->th_seq_num = 0x00;
++                ctcmpc_clear_busy(dev);
++        }
++
++#ifdef DEBUGSEQ
++        printk(KERN_INFO "%s 2: ToVTAM_th_seq= %08x\n" ,
++               __FUNCTION__,wch->th_seq_num);
++        printk(KERN_INFO "%s 2: FromVTAM_th_seq= %08x\n" ,
++               __FUNCTION__,rch->th_seq_num);
++#endif
++
++        if(rc != 0)
++                ccw_check_return_code(wch, rc);
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", __FUNCTION__,ach->devno);
++#endif
++        return;
++
++}
++
++
++static void 
++ctcmpc_rcvd_sweep_resp(mpcg_info *mpcginfo)
++{
++        channel           *rch        = mpcginfo->ch;
++        net_device        *dev        = rch->netdev;
++        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
++        mpc_group         *grpptr     = privptr->mpcg;
++        channel           *ch         = privptr->channel[WRITE];        
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++#ifdef DEBUGSEQ
++        dumpit((char *)mpcginfo->sweep,TH_SWEEP_LENGTH);
++#endif
++
++
++        grpptr->sweep_rsp_pend_num--;
++
++        if((grpptr->sweep_req_pend_num == 0) &&
++           (grpptr->sweep_rsp_pend_num == 0))
++        {
++                fsm_deltimer(&ch->sweep_timer);
++                grpptr->in_sweep = 0;
++                rch->th_seq_num = 0x00;
++                ch->th_seq_num = 0x00;
++                ctcmpc_clear_busy(dev);
++        }
++
++        kfree(mpcginfo);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++        return;
++
++}
++
++
++static void 
++ctcmpc_send_sweep_req(channel *rch)
++{
++        net_device        *dev        = rch->netdev;
++        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
++        mpc_group         *grpptr     = privptr->mpcg;
++        th_sweep          *header;
++        struct sk_buff    *sweep_skb;
++        int               rc          = 0;
++        channel           *ch         = privptr->channel[WRITE];        
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++        /* sweep processing is not complete until response and request */
++        /* has completed for all read channels in group                */
++        if(grpptr->in_sweep == 0)
++        {
++                grpptr->in_sweep = 1;
++                grpptr->sweep_rsp_pend_num = grpptr->active_channels[READ];
++                grpptr->sweep_req_pend_num = grpptr->active_channels[READ];
++        }
++
++
++        sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
++                                    GFP_ATOMIC|GFP_DMA);
++
++        if(sweep_skb == NULL)
++        {
++                printk(KERN_INFO "Couldn't alloc sweep_skb\n");
++                rc = -ENOMEM;
++                goto done;
++        }
++
++        header = (th_sweep *)kmalloc(TH_SWEEP_LENGTH, gfp_type());
++
++        if(!header)
++        {
++                dev_kfree_skb_any(sweep_skb);
++                rc = -ENOMEM;
++                goto done;
++        }
++
++        header->th.th_seg       = 0x00 ;
++        header->th.th_ch_flag   = TH_SWEEP_REQ;  /* 0x0f */
++        header->th.th_blk_flag  = 0x00;
++        header->th.th_is_xid    = 0x00; 
++        header->th.th_seq_num   = 0x00;
++        header->sw.th_last_seq  = ch->th_seq_num;
++
++        memcpy(skb_put(sweep_skb,TH_SWEEP_LENGTH),header,TH_SWEEP_LENGTH);
++
++        kfree(header);
++
++        dev->trans_start = jiffies;
++        skb_queue_tail(&ch->sweep_queue,sweep_skb);
++
++        fsm_addtimer(&ch->sweep_timer,100,CH_EVENT_RSWEEP1_TIMER,ch);
++
++        return;
++        done:
++        if(rc != 0)
++        {
++                grpptr->in_sweep = 0;
++                ctcmpc_clear_busy(dev);
++                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++        return;
++}
++
++static void 
++ctcmpc_send_sweep_resp(channel *rch)
++{
++        net_device        *dev        = rch->netdev;
++        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
++        mpc_group         *grpptr     = privptr->mpcg;
++        int               rc          = 0;
++        th_sweep          *header;
++        struct sk_buff    *sweep_skb;
++        channel           *ch         = privptr->channel[WRITE];
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
++               __FUNCTION__,rch->devno);
++#endif
++
++        sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
++                                    GFP_ATOMIC|GFP_DMA);
++        if(sweep_skb == NULL)
++        {
++                printk(KERN_INFO
++                       "Couldn't alloc sweep_skb\n");
++                rc = -ENOMEM;
++                goto done;
++        }
++
++        header = (th_sweep *)kmalloc(sizeof(struct th_sweep_t), gfp_type());
++
++        if(!header)
++        {
++                dev_kfree_skb_any(sweep_skb);
++                rc = -ENOMEM;
++                goto done;
++        }
++
++        header->th.th_seg       = 0x00 ;
++        header->th.th_ch_flag   = TH_SWEEP_RESP;
++        header->th.th_blk_flag  = 0x00;
++        header->th.th_is_xid    = 0x00; 
++        header->th.th_seq_num   = 0x00;
++        header->sw.th_last_seq  = ch->th_seq_num;
++
++        memcpy(skb_put(sweep_skb,TH_SWEEP_LENGTH),header,TH_SWEEP_LENGTH);
++
++        kfree(header);
++
++        dev->trans_start = jiffies;
++        skb_queue_tail(&ch->sweep_queue,sweep_skb);
++
++        fsm_addtimer(&ch->sweep_timer,100,CH_EVENT_RSWEEP1_TIMER,ch);
++
++        return;
++
++        done:
++        if(rc != 0)
++        {
++                grpptr->in_sweep = 0;
++                ctcmpc_clear_busy(dev);
++                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++        return;
++
++}
++
++static void 
++ctcmpc_rcvd_sweep_req(mpcg_info *mpcginfo)
++{
++        channel     *rch        = mpcginfo->ch;
++        net_device  *dev        = rch->netdev;
++        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
++        mpc_group   *grpptr     = privptr->mpcg;
++        channel     *ch         = privptr->channel[WRITE];      
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++        if(grpptr->in_sweep == 0)
++        {
++                grpptr->in_sweep = 1;
++                ctcmpc_test_and_set_busy(dev);
++                grpptr->sweep_req_pend_num = grpptr->active_channels[READ];
++                grpptr->sweep_rsp_pend_num = grpptr->active_channels[READ];
++        }
++
++#ifdef DEBUGSEQ
++        dumpit((char *)mpcginfo->sweep,TH_SWEEP_LENGTH);
++#endif
++
++        grpptr->sweep_req_pend_num --;
++
++        ctcmpc_send_sweep_resp(ch);
++
++        kfree(mpcginfo);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++        return;
++}
++
++static void 
++ctcmpc_action_go_ready(fsm_instance *fsm,int event, void *arg)
++{
++        net_device     *dev = (net_device *)arg;
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "%s() dev=NULL\n",
++                       __FUNCTION__);
++                return;
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s enter:  %s()\n", 
++               dev->name,__FUNCTION__);
++#endif
++
++        ctc_priv *privptr = (ctc_priv *)dev->priv;
++        if(privptr == NULL)
++        {
++                printk(KERN_INFO "%s() privptr=NULL\n",
++                       __FUNCTION__);
++                return;
++        }
++
++        mpc_group       *grpptr = privptr->mpcg;
++        if(grpptr == NULL)
++        {
++                printk(KERN_INFO "%s() grpptr=NULL\n",
++                       __FUNCTION__);
++                return;
++        }
++
++        fsm_deltimer(&grpptr->timer);
++
++        if(grpptr->saved_xid2->xid2_flag2 == 0x40)
++        {
++                privptr->xid->xid2_flag2 = 0x00;
++                if(grpptr->estconnfunc)
++                {
++                        grpptr->estconnfunc(grpptr->port_num,1,
++                                            grpptr->group_max_buflen);
++                        grpptr->estconnfunc = NULL;
++                } else
++                        if(grpptr->allochanfunc)
++                        grpptr->send_qllc_disc = 1;
++                goto done;
++        }
++
++        grpptr->port_persist = 1;
++        grpptr->out_of_sequence = 0;
++        grpptr->estconn_called = 0;
++
++        tasklet_hi_schedule(&grpptr->mpc_tasklet2);
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
++#endif
++        return;
++
++        done:
++        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit: failure occurred: %s()\n", 
++               __FUNCTION__);
++#endif
++
++}
++
++
++static void 
++ctcmpc_group_ready(unsigned long adev)
++{
++        net_device *dev = (net_device *)adev;
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "%s() dev=NULL\n",__FUNCTION__);
++                return;
++        }
++
++        ctc_priv *privptr = (ctc_priv *)dev->priv;
++        if(privptr == NULL)
++        {
++                printk(KERN_INFO "%s() privptr=NULL\n",__FUNCTION__);
++                return;
++        }
++
++        mpc_group       *grpptr = privptr->mpcg;
++        if(grpptr == NULL)
++        {
++                printk(KERN_INFO "%s() grpptr=NULL\n",__FUNCTION__);
++                return;
++        }
++
++        printk(KERN_NOTICE "%s:GROUP TRANSITIONED TO READY maxbuf:%d\n", 
++               dev->name,grpptr->group_max_buflen);
++
++        fsm_newstate(grpptr->fsm, MPCG_STATE_READY);
++
++        /* Put up a read on the channel */
++        channel *ch = privptr->channel[READ];
++        ch->pdu_seq = 0;
++#ifdef DEBUGSEQ
++        printk(KERN_INFO "%s: ToDCM_pdu_seq= %08x\n" ,
++               __FUNCTION__,ch->pdu_seq);
++#endif
++
++        ctcmpc_ch_action_rxidle(ch->fsm, CH_EVENT_START, ch);
++        /* Put the write channel in idle state */
++        ch = privptr->channel[WRITE];
++        if(ch->collect_len > 0)
++        {
++                spin_lock(&ch->collect_lock);
++                ctcmpc_purge_skb_queue(&ch->collect_queue);
++                ch->collect_len = 0;
++                spin_unlock(&ch->collect_lock);
++        }
++        ctcmpc_ch_action_txidle(ch->fsm, CH_EVENT_START, ch);
++
++        ctcmpc_clear_busy(dev);
++
++        if(grpptr->estconnfunc)
++        {
++                grpptr->estconnfunc(grpptr->port_num,0,
++                                    grpptr->group_max_buflen);
++                grpptr->estconnfunc = NULL;
++        } else
++                if(grpptr->allochanfunc)
++                grpptr->allochanfunc(grpptr->port_num,
++                                     grpptr->group_max_buflen);
++
++        grpptr->send_qllc_disc = 1;
++        grpptr->changed_side = 0;
++
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return;
++
++}
++
++/****************************************************************/
++/* Increment the MPC Group Active Channel Counts                */
++/****************************************************************/
++static int 
++ctcmpc_channel_action(channel *ch, int direction, int action)
++{
++        net_device  *dev     = ch->netdev;
++        ctc_priv    *privptr;
++        mpc_group   *grpptr  = NULL;
++        int         rc = 0;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++
++        if(dev == NULL)
++        {
++                printk(KERN_INFO "mpc_channel_action %i dev=NULL, irq=%d\n",
++                       action,ch->irq);
++                rc = 1;
++                goto done;
++        }
++
++        privptr = (ctc_priv *)dev->priv;
++        if(privptr == NULL)
++        {
++                printk(KERN_INFO "mpc_channel_action%i privptr=NULL, dev=%s\n",
++                       action,dev->name);
++                rc = 2;
++                goto done;
++        }
++
++        grpptr = privptr->mpcg;
++
++        if(grpptr == NULL)
++        {
++                printk(KERN_INFO "mpc_channel_action%i mpcgroup=NULL, dev=%s\n",
++                       action,dev->name);
++                rc = 3;
++                goto done;
++        }
++#ifdef DEBUG
++        printk(KERN_INFO
++               "ctcmpc enter : %s %i(): Grp:%s total_channel_paths=%i "
++               "active_channels read=%i,write=%i\n", 
++               __FUNCTION__,
++               action,
++               fsm_getstate_str(grpptr->fsm),
++               grpptr->num_channel_paths, 
++               grpptr->active_channels[READ],
++               grpptr->active_channels[WRITE]);
++#endif
++
++        switch(action)
++        {
++                case MPC_CHANNEL_ADD:
++                        if(ch->in_mpcgroup == 0)
++                        {
++                                grpptr->num_channel_paths++;
++                                grpptr->active_channels[direction]++;
++                                grpptr->outstanding_xid2++;
++                                ch->in_mpcgroup = 1;
++
++                                if(ch->xid_skb != NULL)
++                                        dev_kfree_skb_any(ch->xid_skb);
++                                ch->xid_skb = 
++                                        __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
++                                                        GFP_ATOMIC|GFP_DMA);
++                                if(ch->xid_skb == NULL)
++                                {
++                                        printk(KERN_INFO 
++                                               "Couldn't alloc ch xid_skb\n");
++                                        fsm_event(grpptr->fsm,
++                                                  MPCG_EVENT_INOP,dev);
++                                        return 1;
++                                }
++
++                                ch->xid_skb_data = ch->xid_skb->data;
++                                ch->xid_th = (th_header *)ch->xid_skb->data;
++                                skb_put(ch->xid_skb,TH_HEADER_LENGTH);
++                                ch->xid = (xid2 *)ch->xid_skb->tail;
++                                skb_put(ch->xid_skb,XID2_LENGTH);
++                                ch->xid_id = ch->xid_skb->tail;
++                                ch->xid_skb->data =  
++                                        ch->xid_skb->tail = 
++                                        ch->xid_skb_data;
++                                ch->xid_skb->len = 0;
++
++
++                                memcpy(skb_put(ch->xid_skb,
++                                               grpptr->xid_skb->len),
++                                       grpptr->xid_skb->data,
++                                       grpptr->xid_skb->len);
++
++                                ch->xid->xid2_dlc_type = 
++                                ((CHANNEL_DIRECTION(ch->flags) == READ)
++                                 ? XID2_READ_SIDE : XID2_WRITE_SIDE );
++
++                                if(CHANNEL_DIRECTION(ch->flags) == WRITE)
++                                        ch->xid->xid2_buf_len = 0x00;
++
++
++                                ch->xid_skb->data = 
++                                        ch->xid_skb->tail = 
++                                        ch->xid_skb_data;
++                                ch->xid_skb->len = 0;
++
++                                fsm_newstate(ch->fsm,CH_XID0_PENDING);
++                                if((grpptr->active_channels[READ]  > 0) && 
++                                   (grpptr->active_channels[WRITE] > 0) &&
++                                   (fsm_getstate(grpptr->fsm) < 
++                                    MPCG_STATE_XID2INITW))
++                                {
++                                        fsm_newstate(grpptr->fsm, 
++                                                     MPCG_STATE_XID2INITW);
++                                        printk(KERN_NOTICE 
++                                               "%s MPC GROUP CHANNELS ACTIVE\n",
++                                               dev->name);
++                                }
++
++
++                        }
++                        break; 
++                case MPC_CHANNEL_REMOVE:
++                        if(ch->in_mpcgroup == 1)
++                        {
++                                ch->in_mpcgroup = 0;
++                                grpptr->num_channel_paths--;
++                                grpptr->active_channels[direction]--;
++
++                                if(ch->xid_skb != NULL)
++                                        dev_kfree_skb_any(ch->xid_skb);
++                                ch->xid_skb = NULL;
++
++                                if(grpptr->channels_terminating)
++                                        break;
++
++                                if(((grpptr->active_channels[READ]  == 0) && 
++                                    (grpptr->active_channels[WRITE] > 0)) || 
++                                   ((grpptr->active_channels[WRITE] == 0) && 
++                                    (grpptr->active_channels[READ]  > 0)))
++                                        fsm_event(grpptr->fsm,
++                                                  MPCG_EVENT_INOP,
++                                                  dev);
++                        }
++                        break;
++        }
++
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO
++               "ctcmpc leave: %s %i(): Grp:%s total_channel_paths=%i "
++               "active_channels read=%i,write=%i\n", 
++               __FUNCTION__,
++               action,
++               fsm_getstate_str(grpptr->fsm),
++               grpptr->num_channel_paths, 
++               grpptr->active_channels[READ],
++               grpptr->active_channels[WRITE]);
++#endif
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", 
++               __FUNCTION__,ch->devno);
++#endif
++
++        return(rc);
++
++}
++
++/**
++ * The minimum required active read or write channels 
++ * are no longer available.  Game over.
++ */
++
++/**
++ * The MPC Group Station FSM
++ *   22 events
++ */
++static const fsm_node mpcg_fsm[] = {
++        {  MPCG_STATE_RESET,     MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
++        {  MPCG_STATE_INOP,      MPCG_EVENT_INOP,     fsm_action_nop},
++
++        {  MPCG_STATE_FLOWC,     MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
++
++        {  MPCG_STATE_READY,     MPCG_EVENT_DISCONC,  ctcmpc_action_discontact},
++        {  MPCG_STATE_READY,     MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
++
++        {  MPCG_STATE_XID2INITW, MPCG_EVENT_XID0DO,   ctcmpc_action_doxid0},
++        {  MPCG_STATE_XID2INITW, MPCG_EVENT_XID2,     ctcmpc_action_rcvd_xid0},
++        {  MPCG_STATE_XID2INITW, MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
++        {  MPCG_STATE_XID2INITW, MPCG_EVENT_TIMER,    ctcmpc_action_timeout},
++        {  MPCG_STATE_XID2INITW, MPCG_EVENT_DOIO,     ctcmpc_action_yside_xid},
++
++        {  MPCG_STATE_XID2INITX, MPCG_EVENT_XID0DO,   ctcmpc_action_doxid0},
++        {  MPCG_STATE_XID2INITX, MPCG_EVENT_XID2,     ctcmpc_action_rcvd_xid0},
++        {  MPCG_STATE_XID2INITX, MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
++        {  MPCG_STATE_XID2INITX, MPCG_EVENT_TIMER,    ctcmpc_action_timeout},
++        {  MPCG_STATE_XID2INITX, MPCG_EVENT_DOIO,     ctcmpc_action_yside_xid},
++
++        {  MPCG_STATE_XID7INITW, MPCG_EVENT_XID2DONE, ctcmpc_action_doxid7},
++        {  MPCG_STATE_XID7INITW, MPCG_EVENT_DISCONC,  ctcmpc_action_discontact},
++        {  MPCG_STATE_XID7INITW, MPCG_EVENT_XID2,     ctcmpc_action_rcvd_xid7},
++        {  MPCG_STATE_XID7INITW, MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
++        {  MPCG_STATE_XID7INITW, MPCG_EVENT_TIMER,    ctcmpc_action_timeout},
++        {  MPCG_STATE_XID7INITW, MPCG_EVENT_XID7DONE, ctcmpc_action_doxid7},
++        {  MPCG_STATE_XID7INITW, MPCG_EVENT_DOIO,     ctcmpc_action_yside_xid},
++
++        {  MPCG_STATE_XID7INITX, MPCG_EVENT_DISCONC,  ctcmpc_action_discontact},
++        {  MPCG_STATE_XID7INITX, MPCG_EVENT_XID2,     ctcmpc_action_rcvd_xid7},
++        {  MPCG_STATE_XID7INITX, MPCG_EVENT_INOP,     ctcmpc_action_go_inop},
++        {  MPCG_STATE_XID7INITX, MPCG_EVENT_XID7DONE, ctcmpc_action_doxid7},
++        {  MPCG_STATE_XID7INITX, MPCG_EVENT_TIMER,    ctcmpc_action_timeout},
++        {  MPCG_STATE_XID7INITX, MPCG_EVENT_DOIO,     ctcmpc_action_yside_xid},
++
++        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID0DO,  ctcmpc_action_doxid0},
++        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DISCONC, ctcmpc_action_discontact},
++        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID2,    ctcmpc_action_rcvd_xid0},
++        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_INOP,    ctcmpc_action_go_inop},
++        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_TIMER,   ctcmpc_action_timeout},
++        {  MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DOIO,    ctcmpc_action_xside_xid},
++
++        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID0DO,  ctcmpc_action_doxid0},
++        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DISCONC, ctcmpc_action_discontact},
++        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID2,    ctcmpc_action_rcvd_xid0},
++        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_INOP,    ctcmpc_action_go_inop},
++        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_TIMER,   ctcmpc_action_timeout},
++        {  MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DOIO,    ctcmpc_action_xside_xid},
++
++        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_XID2DONE,ctcmpc_action_doxid7},
++        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_XID2,    ctcmpc_action_rcvd_xid7},
++        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_DISCONC, ctcmpc_action_discontact},
++        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_INOP,    ctcmpc_action_go_inop},
++        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_TIMER,   ctcmpc_action_timeout},
++        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_XID7DONE,ctcmpc_action_doxid7},
++        {  MPCG_STATE_XID7INITI,  MPCG_EVENT_DOIO,    ctcmpc_action_xside_xid},
++
++        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_XID2,    ctcmpc_action_rcvd_xid7},
++        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_XID7DONE,ctcmpc_action_doxid7},
++        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_DISCONC, ctcmpc_action_discontact},
++        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_INOP,    ctcmpc_action_go_inop},
++        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_TIMER,   ctcmpc_action_timeout},
++        {  MPCG_STATE_XID7INITZ,  MPCG_EVENT_DOIO,    ctcmpc_action_xside_xid},
++
++        {  MPCG_STATE_XID7INITF,  MPCG_EVENT_INOP,    ctcmpc_action_go_inop},
++        {  MPCG_STATE_XID7INITF,  MPCG_EVENT_XID7DONE,ctcmpc_action_go_ready},
++
++};
++
++static const int MPCG_FSM_LEN = sizeof(mpcg_fsm) / sizeof(fsm_node);
++
++
++/**
++ * Unpack a just received skb and hand it over to
++ * upper layers.
++ *
++ * @param ch The channel where this skb has been received.
++ * @param pskb The received packed skb.
++ */
++static __inline__ void 
++ctcmpc_unpack_skb(channel *ch, struct sk_buff *pskb)
++{
++        net_device *dev  = ch->netdev;
++        ctc_priv *privptr = (ctc_priv *)dev->priv;
++        mpc_group *grpptr = privptr->mpcg;
++        pdu *curr_pdu;
++        mpcg_info *mpcginfo;
++        int pdu_last_seen = 0;
++        __u32 new_len;
++        struct sk_buff *skb;
++        int sendrc = 0;
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s cp:%i enter:%s() %04x\n", 
++               dev->name,smp_processor_id(),__FUNCTION__,ch->devno);
++#endif
++
++        th_header *header = (th_header *)pskb->data;
++        if((header->th_seg == 0) && 
++           (header->th_ch_flag == 0) &&
++           (header->th_blk_flag == 0) &&
++           (header->th_seq_num == 0))
++                goto done;  /* nothing for us */
++
++#ifdef DEBUGDATA
++        printk(KERN_INFO "%s(): th_header\n", __FUNCTION__);
++        dumpit((char *)header,TH_HEADER_LENGTH);
++        printk(KERN_INFO "%s(): pskb len: %04x \n", __FUNCTION__,pskb->len);
++#endif
++
++        pskb->dev = dev;        
++        pskb->ip_summed = CHECKSUM_UNNECESSARY;
++        spin_lock(&ch->segment_lock);  /* make sure we are alone here */
++
++        skb_pull(pskb,TH_HEADER_LENGTH);
++
++        if(likely(header->th_ch_flag == TH_HAS_PDU))
++        {
++//               #ifdef DEBUGDATA
++//                        printk(KERN_INFO "%s(): came into th_has_pdu\n", 
++//                        __FUNCTION__);
++//               #endif
++
++
++                if((fsm_getstate(grpptr->fsm) == MPCG_STATE_FLOWC) ||
++                   ((fsm_getstate(grpptr->fsm) == MPCG_STATE_READY) &&
++                    (header->th_seq_num != ch->th_seq_num + 1) &&
++                    (ch->th_seq_num != 0)))
++                {
++                        /* This isn't the next segment          *
++                        * we are not the correct race winner   *  	
++                        * go away and let someone else win     *
++                        * BUT..this only applies if xid negot  *
++                        * is done                              *
++                       */
++                        grpptr->out_of_sequence +=1;
++                        __skb_push(pskb,TH_HEADER_LENGTH);
++                        spin_unlock(&ch->segment_lock);
++                        skb_queue_tail(&ch->io_queue, pskb);
++#ifdef DEBUGSEQ
++                        printk(KERN_INFO "%s() th_seq_num expect:%08x "
++                               "got:%08x\n",
++                               __FUNCTION__,
++                               ch->th_seq_num + 1,
++                               header->th_seq_num);
++#endif
++                        return;
++                }
++                grpptr->out_of_sequence = 0;
++                ch->th_seq_num = header->th_seq_num;
++
++#ifdef DEBUGSEQ
++                printk(KERN_INFO "%s: FromVTAM_th_seq= %08x\n" ,
++                       __FUNCTION__,ch->th_seq_num);
++#endif
++                pdu_last_seen = 0;
++                if(fsm_getstate(grpptr->fsm) == MPCG_STATE_READY)
++                        while((pskb->len > 0) && !pdu_last_seen)
++                        {
++                                curr_pdu = (pdu *)pskb->data;
++#ifdef DEBUGDATA
++                                printk(KERN_INFO "%s(): pdu_header\n", 
++                                       __FUNCTION__);
++                                dumpit((char *)pskb->data,PDU_HEADER_LENGTH);
++                                printk(KERN_INFO "%s(): pskb len: %04x \n", 
++                                       __FUNCTION__,pskb->len);
++#endif
++                                skb_pull(pskb,PDU_HEADER_LENGTH);
++                                if(curr_pdu->pdu_flag & PDU_LAST)
++                                        pdu_last_seen = 1;
++                                if(curr_pdu->pdu_flag & PDU_CNTL)
++                                        pskb->protocol = htons(ETH_P_SNAP);
++                                else
++                                        pskb->protocol = htons(ETH_P_SNA_DIX);
++                                if((pskb->len <= 0) ||
++                                   (pskb->len > ch->max_bufsize))
++                                {
++                                        printk(KERN_INFO
++                                               "%s Illegal packet size %d "
++                                               "received "
++                                               "dropping\n", dev->name, 
++                                               pskb->len);
++                                        privptr->stats.rx_dropped++;
++                                        privptr->stats.rx_length_errors++;
++                                        spin_unlock(&ch->segment_lock);
++                                        goto done;
++                                }
++                                pskb->mac.raw = pskb->data;
++                                new_len = curr_pdu->pdu_offset;
++//			  #ifdef DEBUGDATA
++//				printk(KERN_INFO "%s(): new_len: %04x \n", 
++//                                __FUNCTION__,new_len);
++//			  #endif
++                                if((new_len == 0) ||
++                                   (new_len > pskb->len))
++                                {
++                                        /* should never happen              */
++                                        /* pskb len must be hosed...bail out */
++                                        printk(KERN_INFO 
++                                               "%s(): invalid pdu offset "
++                                               "of %04x - data may be lost\n", 
++                                               __FUNCTION__,new_len);
++                                        spin_unlock(&ch->segment_lock);
++                                        goto done;
++                                }
++                                skb = __dev_alloc_skb(new_len+4,GFP_ATOMIC);
++
++                                if(!skb)
++                                {
++                                        printk(KERN_INFO
++                                               "%s Out of memory in %s- "
++                                               "request-len:%04x \n",
++                                               dev->name,
++                                               __FUNCTION__,
++                                               new_len+4);                                                                              
++                                        privptr->stats.rx_dropped++;
++                                        spin_unlock(&ch->segment_lock);
++                                        fsm_event(grpptr->fsm,
++                                                  MPCG_EVENT_INOP,
++                                                  dev);
++                                        goto done;
++                                }
++
++                                memcpy(skb_put(skb, new_len), 
++                                       pskb->data, 
++                                       new_len);
++
++                                skb->mac.raw = skb->data;
++                                skb->dev = pskb->dev;
++                                skb->protocol = pskb->protocol;
++                                skb->ip_summed = CHECKSUM_UNNECESSARY;
++                                *((__u32 *) skb_push(skb, 4)) = ch->pdu_seq;  
++                                ch->pdu_seq++;
++
++#ifdef DEBUGSEQ
++                                printk(KERN_INFO "%s: ToDCM_pdu_seq= %08x\n" ,
++                                       __FUNCTION__,ch->pdu_seq);
++#endif
++
++#ifdef DEBUGDATA
++                                __u32 out_len;
++                                if(skb->len > 32) out_len = 32;
++                                else out_len = skb->len;
++                                printk(KERN_INFO "%s(): skb:%0lx skb len:%d \n",
++                                        __FUNCTION__,
++                                       (unsigned long)skb,
++                                       skb->len);
++                                printk(KERN_INFO "%s(): up to 32 bytes "
++                                       "of pdu_data sent\n", 
++                                       __FUNCTION__);
++                                dumpit((char *)skb->data,out_len);
++#endif
++
++                                sendrc = netif_rx(skb);
++                                privptr->stats.rx_packets++;
++                                privptr->stats.rx_bytes += skb->len;
++                                skb_pull(pskb, new_len); /* point to next PDU */
++                        }
++        } else
++        {
++                if((mpcginfo = (mpcg_info *)kmalloc(sizeof(mpcg_info), 
++                                                    gfp_type())) == NULL)
++                {
++                        spin_unlock(&ch->segment_lock);
++                        goto done;
++                }
++
++                mpcginfo->ch = ch;
++                mpcginfo->th = header;
++                mpcginfo->skb = pskb;
++
++#ifdef DEBUG
++                printk(KERN_INFO "%s(): It's not PDU it may be control pkt\n", 
++                       __FUNCTION__);
++#endif
++                /*  it's a sweep?   */
++                th_sweep *sweep = (th_sweep *) pskb->data;
++                mpcginfo->sweep = sweep;
++                if(header->th_ch_flag == TH_SWEEP_REQ)
++                        ctcmpc_rcvd_sweep_req(mpcginfo);
++                else
++                        if(header->th_ch_flag == TH_SWEEP_RESP)
++                        ctcmpc_rcvd_sweep_resp(mpcginfo);
++                else
++                {
++                        if(header->th_blk_flag == TH_DATA_IS_XID)
++                        {
++                                xid2 *thisxid = (xid2 *)pskb->data;
++                                skb_pull(pskb,XID2_LENGTH);                                               
++                                mpcginfo->xid = thisxid;
++                                fsm_event(grpptr->fsm,MPCG_EVENT_XID2,mpcginfo);
++                        } else
++                        {
++                                if(header->th_blk_flag == TH_DISCONTACT)
++                                {
++                                        fsm_event(grpptr->fsm,
++                                                  MPCG_EVENT_DISCONC,mpcginfo);
++                                } else
++                                        if(header->th_seq_num != 0)
++                                {
++                                        printk(KERN_INFO
++                                               "%s unexpected packet expected"
++                                               " control pkt\n",
++                                               dev->name);
++                                        privptr->stats.rx_dropped++;
++#ifdef DEBUGDATA
++                                        ctcmpc_dump_skb(pskb, -8);
++#endif
++                                        kfree(mpcginfo);
++                                }
++                        }
++                }
++        }
++        spin_unlock(&ch->segment_lock);
++        done:
++
++        dev_kfree_skb_any(pskb);  
++        switch(sendrc)
++        {
++                case NET_RX_DROP:
++                        printk(KERN_WARNING "%s %s() NETWORK BACKLOG "
++                               "EXCEEDED - PACKET DROPPED\n",
++                               dev->name,
++                               __FUNCTION__);
++                        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++                        break;
++                case NET_RX_SUCCESS:
++                case NET_RX_CN_LOW:
++                case NET_RX_CN_MOD:
++                case NET_RX_CN_HIGH:
++                default:
++                        break;
++        }
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s() %04x\n", 
++               dev->name,__FUNCTION__,ch->devno);
++#endif
++
++}
++
++
++
++/**
++ * Bottom half routine.
++ *
++ * @param ch The channel to work on.
++ * Allow flow control back pressure to occur here.
++ * Throttling back channel can result in excessive
++ * channel inactivity and system deact of channel
++ */
++static void 
++ctcmpc_bh(unsigned long thischan)
++{
++        channel           *ch         = (channel *)thischan;
++        struct sk_buff    *peek_skb   = NULL;
++        struct sk_buff    *skb;
++        struct sk_buff    *same_skb   = NULL;
++        net_device        *dev        = ch->netdev;
++        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
++        mpc_group         *grpptr     = privptr->mpcg;
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s cp:%i enter:  %s()  %04x\n", 
++               dev->name,smp_processor_id(),__FUNCTION__,ch->devno);
++#endif
++        /* caller has requested driver to throttle back */
++        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_FLOWC)
++        {
++                goto done;
++        } else
++        {
++                while((skb = skb_dequeue(&ch->io_queue)))
++                {
++                        same_skb = skb;
++                        ctcmpc_unpack_skb(ch, skb);
++                        if(grpptr->out_of_sequence > 20)
++                        {
++                                /* assume data loss has occurred if */
++                                /* missing seq_num for extended     */
++                                /* period of time                   */
++                                grpptr->out_of_sequence = 0;
++                                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++                                goto done;
++                        }
++                        peek_skb = skb_peek(&ch->io_queue);
++                        if(peek_skb == same_skb)
++                                goto done;
++                        if(fsm_getstate(grpptr->fsm) == MPCG_STATE_FLOWC)
++                                goto done;
++                }
++        }
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()  %04x\n", 
++               dev->name,__FUNCTION__,ch->devno);
++#endif
++
++        return;
++}
++
++/**
++ * Check return code of a preceeding do_IO, halt_IO etc...
++ *
++ * @param ch          The channel, the error belongs to.
++ * @param return_code The error code to inspect.
++ */
++static void inline 
++ccw_check_return_code (channel *ch, int return_code)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter: cp:%i %s()\n", 
++               smp_processor_id(),__FUNCTION__);
++#endif
++
++
++        switch(return_code)
++        {
++                case 0:
++                        fsm_event(ch->fsm, CH_EVENT_IO_SUCCESS, ch);
++                        break;
++                case -EBUSY:
++                        printk(KERN_INFO "ch-%04x: Busy !\n", ch->devno);
++                        fsm_event(ch->fsm, CH_EVENT_IO_EBUSY, ch);
++                        break;
++                case -ENODEV:
++                        printk(KERN_EMERG
++                               "ch-%04x: Invalid device called for IO\n",
++                               ch->devno);
++                        fsm_event(ch->fsm, CH_EVENT_IO_ENODEV, ch);
++                        break;
++                case -EIO:
++                        printk(KERN_EMERG
++                               "ch-%04x: Status pending... \n", ch->devno);
++                        fsm_event(ch->fsm, CH_EVENT_IO_EIO, ch);
++                        break;
++                default:
++                        printk(KERN_EMERG
++                               "ch-%04x: Unknown error in do_IO %04x\n",
++                               ch->devno, return_code);
++                        fsm_event(ch->fsm, CH_EVENT_IO_UNKNOWN, ch);
++        }
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++
++}
++
++/**
++ * Check sense of a unit check.
++ *
++ * @param ch    The channel, the sense code belongs to.
++ * @param sense The sense code to inspect.
++ */
++static void inline 
++ccw_unit_check (channel *ch, unsigned char sense)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        if(sense & SNS0_INTERVENTION_REQ)
++        {
++                if(sense & 0x01)
++                {
++                        printk(KERN_INFO
++                               "ch-%04x: Interface disc. or Sel. reset "
++                               "(remote)\n", ch->devno);
++                        fsm_event(ch->fsm, CH_EVENT_UC_RCRESET, ch);
++                } else
++                {
++                        printk(KERN_INFO "ch-%04x: System reset (remote)\n",
++                               ch->devno);
++                        fsm_event(ch->fsm, CH_EVENT_UC_RSRESET, ch);
++                }
++        } else if(sense & SNS0_EQUIPMENT_CHECK)
++        {
++                if(sense & SNS0_BUS_OUT_CHECK)
++                {
++                        printk(KERN_INFO
++                               "ch-%04x: Hardware malfunction (remote)\n",
++                               ch->devno);
++                        fsm_event(ch->fsm, CH_EVENT_UC_HWFAIL, ch);
++                } else
++                {
++                        printk(KERN_INFO
++                               "ch-%04x: Read-data parity error (remote)\n",
++                               ch->devno);
++                        fsm_event(ch->fsm, CH_EVENT_UC_RXPARITY, ch);
++                }
++        } else if(sense & SNS0_BUS_OUT_CHECK)
++        {
++                if(sense & 0x04)
++                {
++                        printk(KERN_INFO
++                               "ch-%04x: Data-streaming timeout)\n",
++                               ch->devno);
++                        fsm_event(ch->fsm, CH_EVENT_UC_TXTIMEOUT, ch);
++                } else
++                {
++                        printk(KERN_INFO
++                               "ch-%04x: Data-transfer parity error\n",
++                               ch->devno);
++                        fsm_event(ch->fsm, CH_EVENT_UC_TXPARITY, ch);
++                }
++        } else if(sense & SNS0_CMD_REJECT)
++        {
++                printk(KERN_INFO "ch-%04x: Command reject\n",
++                       ch->devno);
++        } else if(sense == 0)
++        {
++                printk(KERN_INFO "ch-%04x: Unit check ZERO\n", ch->devno);
++                fsm_event(ch->fsm, CH_EVENT_UC_ZERO, ch);
++        } else
++        {
++                printk(KERN_INFO
++                       "ch-%04x: Unit Check with sense code: %02x\n",
++                       ch->devno, sense);
++                fsm_event(ch->fsm, CH_EVENT_UC_UNKNOWN, ch);
++        }
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++}
++
++static void 
++ctcmpc_purge_skb_queue(struct sk_buff_head *q)
++{
++        struct sk_buff *skb;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        while((skb = skb_dequeue(q)))
++        {
++                atomic_dec(&skb->users);
++
++                dev_kfree_skb_any(skb);
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++static __inline__ int 
++ctcmpc_checkalloc_buffer(channel *ch, int warn)
++{
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        if((ch->trans_skb == NULL) ||
++           (ch->flags & CHANNEL_FLAGS_BUFSIZE_CHANGED))
++        {
++                if(ch->trans_skb != NULL)
++                        dev_kfree_skb_any(ch->trans_skb);
++                clear_normalized_cda(&ch->ccw[1]);
++                ch->trans_skb = __dev_alloc_skb(ch->max_bufsize,
++                                                GFP_ATOMIC|GFP_DMA);
++                if(ch->trans_skb == NULL)
++                {
++                        if(warn)
++                                printk(KERN_INFO
++                                       "ch-%04x: Couldn't alloc %s trans_skb\n",
++                                       ch->devno,
++                                       (CHANNEL_DIRECTION(ch->flags) == READ) ?
++                                       "RX" : "TX");
++                        return -ENOMEM;
++                }
++                ch->ccw[1].count = ch->max_bufsize;
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21))
++                if(set_normalized_cda(&ch->ccw[1],
++                                      virt_to_phys(ch->trans_skb->data)))
++                {
++#else 
++                if(set_normalized_cda(&ch->ccw[1],ch->trans_skb->data))
++                {
++#endif
++                        dev_kfree_skb_any(ch->trans_skb);
++                        ch->trans_skb = NULL;
++                        if(warn)
++                                printk(KERN_INFO
++                                       "ch-%04x: set_normalized_cda for %s "
++                                       "trans_skb failed, dropping packets\n",
++                                       ch->devno,
++                                       (CHANNEL_DIRECTION(ch->flags) == READ) ?
++                                       "RX" : "TX");
++                        return -ENOMEM;
++                }
++                ch->ccw[1].count = 0;
++                ch->trans_skb_data = ch->trans_skb->data;
++                ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED;
++        }
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return 0;
++}
++
++/**
++ * Actions for channel - statemachines.
++ *****************************************************************************/
++
++/**
++ * Normal data has been sent. Free the corresponding
++ * skb (it's in io_queue), reset dev->tbusy and
++ * revert to idle state.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_txdone(fsm_instance *fi, int event, void *arg)
++{
++        channel        *ch = (channel *)arg;
++        net_device     *dev = ch->netdev;
++        ctc_priv       *privptr = (ctc_priv *)dev->priv;
++        mpc_group      *grpptr  = privptr->mpcg;
++        struct sk_buff *skb;
++        int            first = 1;
++        int            i;
++#ifdef DEBUGDATA
++        __u32                out_len = 0;
++#endif
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s cp:%i enter:  %s()\n", 
++               dev->name,smp_processor_id(),__FUNCTION__);
++#endif
++
++
++        struct timeval done_stamp = xtime;
++        unsigned long duration = 
++        (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
++        done_stamp.tv_usec - ch->prof.send_stamp.tv_usec;
++        if(duration > ch->prof.tx_time)
++                ch->prof.tx_time = duration;
++
++        if(ch->devstat->rescnt != 0)
++                printk(KERN_INFO "%s: TX not complete, remaining %d bytes\n",
++                       dev->name, ch->devstat->rescnt);
++
++        fsm_deltimer(&ch->timer);
++        while((skb = skb_dequeue(&ch->io_queue)))
++        {
++                privptr->stats.tx_packets++;
++                privptr->stats.tx_bytes += skb->len - TH_HEADER_LENGTH;
++                if(first)
++                {
++                        privptr->stats.tx_bytes += 2;
++                        first = 0;
++                }
++                atomic_dec(&skb->users);
++                dev_kfree_skb_irq(skb);
++        }
++        spin_lock(&ch->collect_lock);
++        clear_normalized_cda(&ch->ccw[4]);
++        if((ch->collect_len > 0) && (grpptr->in_sweep == 0))
++        {
++                int rc;
++                th_header       *header;
++                pdu     *p_header = NULL;
++
++                if(ctcmpc_checkalloc_buffer(ch, 1))
++                {
++                        spin_unlock(&ch->collect_lock);
++                        goto done;
++                }
++                ch->trans_skb->tail = ch->trans_skb->data = ch->trans_skb_data;
++                ch->trans_skb->len = 0;
++                if(ch->prof.maxmulti < (ch->collect_len + TH_HEADER_LENGTH))
++                        ch->prof.maxmulti = ch->collect_len + TH_HEADER_LENGTH;
++                if(ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
++                        ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
++                i = 0;
++#ifdef DEBUGDATA
++                printk(KERN_INFO "%s(): building trans_skb from collect_q \n", 
++                       __FUNCTION__);
++#endif
++
++                __u32 data_space = grpptr->group_max_buflen - TH_HEADER_LENGTH;
++
++#ifdef DEBUGDATA
++                printk(KERN_INFO "%s(): building trans_skb from "
++                       "collect_q data_space:%04x\n", 
++                       __FUNCTION__,data_space);
++#endif
++
++
++                while((skb = skb_dequeue(&ch->collect_queue)))
++                {
++                        memcpy(skb_put(ch->trans_skb, skb->len), skb->data,
++                               skb->len);
++                        p_header = (pdu *)(ch->trans_skb->tail - skb->len);  
++                        p_header->pdu_flag = 0x00;
++                        if(skb->protocol == ntohs(ETH_P_SNAP))
++                        {
++                                p_header->pdu_flag |= 0x60;
++                        } else
++                        {
++                                p_header->pdu_flag |= 0x20;
++                        }
++#ifdef DEBUGDATA
++                        __u32            out_len = 0;
++                        printk(KERN_INFO "%s():trans_skb len:%04x \n", 
++                               __FUNCTION__,ch->trans_skb->len);
++                        if(skb->len > 32) out_len = 32;
++                        else out_len = skb->len;
++                        printk(KERN_INFO "%s(): pdu header and data for "
++                               "up to 32 bytes sent to vtam\n", 
++                               __FUNCTION__);
++                        dumpit((char *)p_header,out_len);
++#endif
++                        ch->collect_len -= skb->len;
++                        data_space -= skb->len;
++                        privptr->stats.tx_packets++;
++                        privptr->stats.tx_bytes += skb->len;
++                        atomic_dec(&skb->users);
++                        dev_kfree_skb_any(skb);
++                        sk_buff *peekskb = skb_peek(&ch->collect_queue);
++                        if(peekskb->len > data_space)
++                                break;
++                        i++;
++                }
++                /* p_header points to the last one we handled */
++                if(p_header)
++                        p_header->pdu_flag |= PDU_LAST; 
++                header = (th_header *)kmalloc(TH_HEADER_LENGTH, gfp_type());
++
++                if(!header)
++                {
++                        printk(KERN_WARNING ": OUT OF MEMORY IN %s(): "
++                               "Data Lost \n",
++                               __FUNCTION__);
++                        spin_unlock(&ch->collect_lock);
++                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
++                        goto done;
++                }
++                header->th_seg = 0x00;
++                header->th_ch_flag = TH_HAS_PDU;  /* Normal data */
++                header->th_blk_flag = 0x00;
++                header->th_is_xid = 0x00; 
++                ch->th_seq_num++;
++                header->th_seq_num = ch->th_seq_num;
++
++#ifdef DEBUGSEQ
++                printk(KERN_INFO "%s: ToVTAM_th_seq= %08x\n" ,
++                       __FUNCTION__,ch->th_seq_num);
++#endif
++                memcpy(skb_push(ch->trans_skb, TH_HEADER_LENGTH), header,
++                       TH_HEADER_LENGTH);       /*  put the TH on the packet */
++
++                kfree(header);
++
++#ifdef DEBUGDATA
++                printk(KERN_INFO "%s():trans_skb len:%04x \n", 
++                       __FUNCTION__,ch->trans_skb->len);
++                if(ch->trans_skb->len > 50) out_len = 50;
++                else out_len = ch->trans_skb->len;
++                printk(KERN_INFO "%s(): up-to-50 bytes of trans_skb "
++                       "data to vtam from collect_q\n", 
++                       __FUNCTION__);
++                dumpit((char *)ch->trans_skb->data,out_len);
++#endif
++
++                spin_unlock(&ch->collect_lock);
++                clear_normalized_cda(&ch->ccw[1]);
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21))
++                if(set_normalized_cda(&ch->ccw[1],
++                                      virt_to_phys(ch->trans_skb->data)))
++                {
++#else
++                if(set_normalized_cda(&ch->ccw[1],ch->trans_skb->data))
++                {
++#endif
++                        dev_kfree_skb_any(ch->trans_skb);
++                        ch->trans_skb = NULL;
++                        printk(KERN_WARNING "%s():CCW failure - data lost\n", 
++                               __FUNCTION__);
++                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
++                        return;
++                }
++                ch->ccw[1].count = ch->trans_skb->len;
++                fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
++                ch->prof.send_stamp = xtime;
++
++#ifdef DEBUGCCW
++                dumpit((char *)&ch->ccw[0],sizeof(ccw1_t) * 3);
++#endif
++
++                rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
++                ch->prof.doios_multi++;
++                if(rc != 0)
++                {
++                        ccw_check_return_code(ch, rc);
++                }
++        } else
++        {
++                spin_unlock(&ch->collect_lock);
++                fsm_newstate(fi, CH_STATE_TXIDLE);
++        }
++
++        done:
++        ctcmpc_clear_busy(dev);
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
++#endif
++        return;
++
++}
++
++/**
++ * Initial data is sent.
++ * Notify device statemachine that we are up and
++ * running.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_txidle(fsm_instance *fi, int event, void *arg)
++{
++        channel *ch = (channel *)arg;
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++        fsm_deltimer(&ch->timer);
++        fsm_newstate(fi, CH_STATE_TXIDLE);
++        fsm_event(((ctc_priv *)ch->netdev->priv)->fsm, DEV_EVENT_TXUP,
++                  ch->netdev);
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Got normal data, check for sanity, queue it up, allocate new buffer
++ * trigger bottom half, and initiate next read.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_rx(fsm_instance *fi, int event, void *arg)
++{
++        channel           *ch         = (channel *)arg;
++        net_device        *dev        = ch->netdev;
++        ctc_priv          *privptr    = (ctc_priv *)dev->priv;
++        mpc_group         *grpptr     = privptr->mpcg;
++        __u32             len         = ch->max_bufsize - ch->devstat->rescnt;
++        struct sk_buff    *skb        = ch->trans_skb;
++        struct sk_buff    *new_skb;
++        int               rc          = 0;
++        __u32               block_len;
++        unsigned long     saveflags;
++        int               gotlock     = 0;
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s cp:%i enter:%s() %04x\n", 
++               dev->name,smp_processor_id(),__FUNCTION__,ch->devno);
++#endif
++#ifdef DEBUGDATA
++        printk(KERN_INFO "ctcmpc:%s() max_bufsize:%04x rescnt:%04x len:%04x\n", 
++               __FUNCTION__,ch->max_bufsize,ch->devstat->rescnt,len);
++#endif
++
++        fsm_deltimer(&ch->timer);
++
++        if(skb == NULL)
++        {
++#ifdef DEBUG
++                printk(KERN_INFO "ctcmpc exit:  %s() TRANS_SKB = NULL \n", 
++                       __FUNCTION__);
++#endif	
++                goto again;
++        }
++
++        if(len < TH_HEADER_LENGTH)
++        {
++                printk(KERN_INFO "%s: got packet with invalid length %d\n",
++                       dev->name, len);
++                privptr->stats.rx_dropped++;
++                privptr->stats.rx_length_errors++;
++                goto again;
++        } else
++        {
++                /* must have valid th header or game over */
++                block_len = len;
++                len = TH_HEADER_LENGTH + XID2_LENGTH + 4;
++                new_skb = __dev_alloc_skb(ch->max_bufsize,GFP_ATOMIC);
++
++                if(new_skb == NULL)
++                {
++                        printk(KERN_INFO "ctcmpc exit:%s() NEW_SKB = NULL \n", 
++                               __FUNCTION__);
++                        printk(KERN_WARNING "%s() MEMORY ALLOC FAILED - "
++                               "DATA LOST - MPC FAILED\n", 
++                               __FUNCTION__);
++                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
++                        goto again;
++                }
++                switch(fsm_getstate(grpptr->fsm))
++                {
++                        case MPCG_STATE_RESET:
++                        case MPCG_STATE_INOP:
++                                goto again;
++                        case MPCG_STATE_FLOWC:
++                        case MPCG_STATE_READY:
++                                memcpy(skb_put(new_skb, block_len), 
++                                       skb->data,
++                                       block_len);
++                                skb_queue_tail(&ch->io_queue, new_skb);
++                                tasklet_schedule(&ch->ch_tasklet); 
++                                goto again;
++                        default:
++                                memcpy(skb_put(new_skb, len), skb->data,len);
++                                skb_queue_tail(&ch->io_queue, new_skb);
++                                tasklet_hi_schedule(&ch->ch_tasklet); 
++                                goto again;
++                }
++
++        }
++
++        again:
++        switch(fsm_getstate(grpptr->fsm))
++        {
++                case MPCG_STATE_FLOWC:
++                case MPCG_STATE_READY:
++                        if(ctcmpc_checkalloc_buffer(ch, 1))
++                                break;
++                        ch->trans_skb->data = 
++                                ch->trans_skb->tail = 
++                                ch->trans_skb_data;
++                        ch->trans_skb->len = 0;
++                        ch->ccw[1].count = ch->max_bufsize;
++#ifdef DEBUGCCW
++                        dumpit((char *)&ch->ccw[0],sizeof(ccw1_t) * 3);
++#endif
++                        if(!in_irq())
++                        {
++                                s390irq_spin_lock_irqsave(ch->irq,saveflags);
++                                gotlock = 1;
++                        }
++                        rc = do_IO(ch->irq, &ch->ccw[0], 
++                                   (intparm_t)ch, 0xff, 0);
++                        if(gotlock)
++                                s390irq_spin_unlock_irqrestore(ch->irq,
++                                                               saveflags);
++                        if(rc != 0)
++                                ccw_check_return_code(ch, rc);
++                        break;
++                default: 
++                        break;
++        }
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()  %04x\n", 
++               dev->name,__FUNCTION__,ch->devno);
++#endif
++
++}
++
++/**
++ * Initialize connection by sending a __u16 of value 0.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_firstio(fsm_instance *fi, int event, void *arg)
++{
++        channel *ch = (channel *)arg;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", __FUNCTION__,ch->devno);
++#endif
++
++#ifdef DEBUG
++        net_device     *dev = ch->netdev;
++        mpc_group          *grpptr = ((ctc_priv *)dev->priv)->mpcg;
++        printk(KERN_INFO "%s() %04x chstate:%i grpstate:%i chprotocol:%i\n", 
++               __FUNCTION__, ch->devno,
++               fsm_getstate(fi),
++               fsm_getstate(grpptr->fsm),
++               ch->protocol);
++#endif
++
++        if(fsm_getstate(fi) == CH_STATE_TXIDLE)
++                printk(KERN_INFO "ch-%04x: remote side issued READ?, "
++                       "init ...\n", ch->devno);
++        fsm_deltimer(&ch->timer);
++        if(ctcmpc_checkalloc_buffer(ch, 1))
++        {
++                goto done;
++        }
++
++        if(ch->protocol == CTC_PROTO_MPC)
++                switch(fsm_getstate(fi))
++                {
++                        case CH_STATE_STARTRETRY:
++                        case CH_STATE_SETUPWAIT:
++                                if(CHANNEL_DIRECTION(ch->flags) == READ)
++                                {
++                                        ctcmpc_ch_action_rxidle(fi, event, arg);
++                                } else
++                                {
++                                        net_device *dev = ch->netdev;
++                                        fsm_newstate(fi, CH_STATE_TXIDLE);
++                                        fsm_event(((ctc_priv *)dev->priv)->fsm,
++                                                  DEV_EVENT_TXUP, dev);
++                                }
++                                goto done;
++                        default: 
++                                break;
++
++                };
++
++        fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ)
++                     ? CH_STATE_RXINIT : CH_STATE_TXINIT);
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", __FUNCTION__,ch->devno);
++#endif
++        return;
++}
++
++/**
++ * Got initial data, check it. If OK,
++ * notify device statemachine that we are up and
++ * running.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_rxidle(fsm_instance *fi, int event, void *arg)
++{
++        channel    *ch = (channel *)arg;
++        net_device *dev = ch->netdev;
++        ctc_priv   *privptr = (ctc_priv *)dev->priv;
++        mpc_group  *grpptr = privptr->mpcg;
++        int        rc;
++        unsigned long saveflags;
++
++
++        fsm_deltimer(&ch->timer);
++#ifdef DEBUG
++        printk(KERN_INFO "%s cp:%i enter:  %s()\n", 
++               dev->name,smp_processor_id(),__FUNCTION__);
++#endif
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s() %04x chstate:%i grpstate:%i\n", 
++               __FUNCTION__, ch->devno,
++               fsm_getstate(fi),
++               fsm_getstate(grpptr->fsm));
++#endif
++
++
++        fsm_newstate(fi, CH_STATE_RXIDLE);
++        /* XID processing complete */
++        switch(fsm_getstate(grpptr->fsm))
++        {
++                case MPCG_STATE_FLOWC:
++                case MPCG_STATE_READY:
++                        if(ctcmpc_checkalloc_buffer(ch, 1)) goto done;
++                        ch->trans_skb->data =  
++                                ch->trans_skb->tail = 
++                                ch->trans_skb_data;
++                        ch->trans_skb->len = 0;
++                        ch->ccw[1].count = ch->max_bufsize;
++#ifdef DEBUGCCW
++                        dumpit((char *)&ch->ccw[0],sizeof(ccw1_t) * 3);
++#endif
++                        if(event == CH_EVENT_START)
++                                s390irq_spin_lock_irqsave(ch->irq, saveflags);
++                        rc = do_IO(ch->irq, &ch->ccw[0], 
++                                   (intparm_t)ch, 0xff, 0);
++                        if(event == CH_EVENT_START)
++                                s390irq_spin_unlock_irqrestore(ch->irq, 
++                                                               saveflags);
++                        if(rc != 0)
++                        {
++                                fsm_newstate(fi, CH_STATE_RXINIT);
++                                ccw_check_return_code(ch, rc);
++                                goto done;
++                        }
++                        break;
++                default:
++                        break;
++        }
++
++        fsm_event(((ctc_priv *)dev->priv)->fsm,
++                  DEV_EVENT_RXUP, dev);
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
++#endif
++        return;
++}
++
++/**
++ * Set channel into extended mode.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_setmode(fsm_instance *fi, int event, void *arg)
++{
++        channel *ch = (channel *)arg;
++        int     rc;
++        unsigned long saveflags;
++
++        fsm_deltimer(&ch->timer);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc cp:%i enter:  %s()\n", 
++               smp_processor_id(),__FUNCTION__);
++#endif
++
++        fsm_addtimer(&ch->timer, 1500, CH_EVENT_TIMER, ch);
++        fsm_newstate(fi, CH_STATE_SETUPWAIT);
++
++#ifdef DEBUGCCW
++        dumpit((char *)&ch->ccw[6],sizeof(ccw1_t) * 2);
++#endif
++
++        if(event == CH_EVENT_TIMER)
++                s390irq_spin_lock_irqsave(ch->irq, saveflags);
++        rc = do_IO(ch->irq, &ch->ccw[6], (intparm_t)ch, 0xff, 0);
++        if(event == CH_EVENT_TIMER)
++                s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
++        if(rc != 0)
++        {
++                fsm_deltimer(&ch->timer);
++                fsm_newstate(fi, CH_STATE_STARTWAIT);
++                ccw_check_return_code(ch, rc);
++        } else
++                ch->retry = 0;
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Setup channel.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_start(fsm_instance *fi, int event, void *arg)
++{
++        channel *ch = (channel *)arg;
++        unsigned long saveflags;
++        int     rc;
++        net_device *dev;
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        if(ch == NULL)
++        {
++                printk(KERN_INFO "ctcmpc_ch_action_start ch=NULL\n");
++                goto done;
++        }
++        if(ch->netdev == NULL)
++        {
++                printk(KERN_INFO "ctcmpc_ch_action_start dev=NULL, irq=%d\n",
++                       ch->irq);
++                goto done;
++        }
++        dev = ch->netdev;
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s: %s channel start\n", dev->name,
++               (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
++#endif
++
++        if(ch->trans_skb != NULL)
++        {
++                clear_normalized_cda(&ch->ccw[1]);
++                dev_kfree_skb(ch->trans_skb);
++                ch->trans_skb = NULL;
++        }
++        if(CHANNEL_DIRECTION(ch->flags) == READ)
++        {
++                ch->ccw[1].cmd_code = CCW_CMD_READ;
++                ch->ccw[1].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
++                ch->ccw[1].count    = 0;
++        } else
++        {
++                ch->ccw[1].cmd_code = CCW_CMD_WRITE;
++                ch->ccw[1].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
++                ch->ccw[1].count    = 0;
++        }
++        if(ctcmpc_checkalloc_buffer(ch, 0))
++                printk(KERN_NOTICE
++                       "%s: Could not allocate %s trans_skb, delaying "
++                       "allocation until first transfer\n",
++                       dev->name, 
++                       (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
++
++
++        ch->ccw[0].cmd_code = CCW_CMD_PREPARE;
++        ch->ccw[0].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[0].count    = 0;
++        ch->ccw[0].cda      = 0;
++        ch->ccw[2].cmd_code = CCW_CMD_NOOP;      
++        ch->ccw[2].flags    = CCW_FLAG_SLI;
++        ch->ccw[2].count    = 0;
++        ch->ccw[2].cda      = 0;
++        /*************************************************
++        ch->ccw[2].cmd_code = CCW_CMD_TIC;	 
++        ch->ccw[2].flags    = 0;
++        ch->ccw[2].count    = 0;
++        ch->ccw[2].cda	    = virt_to_phys(&ch->ccw[15]);
++        **************************************************/
++        memcpy(&ch->ccw[3], &ch->ccw[0], sizeof(ccw1_t) * 3);
++        ch->ccw[4].cda      = 0;
++        ch->ccw[4].flags    &= ~CCW_FLAG_IDA;
++
++
++        fsm_newstate(fi, CH_STATE_STARTWAIT);
++        fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
++        s390irq_spin_lock_irqsave(ch->irq, saveflags);
++        rc = halt_IO(ch->irq, (intparm_t)ch, 0);
++        s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
++        if(rc != 0)
++        {
++                fsm_deltimer(&ch->timer);
++                ccw_check_return_code(ch, rc);
++        }
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++        return;
++}
++
++
++
++/**
++ * Shutdown a channel.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_haltio(fsm_instance *fi, int event, void *arg)
++{
++        channel *ch = (channel *)arg;
++        unsigned long saveflags;
++        int     rc;
++        int     oldstate;
++        int gotlock = 0;
++
++        fsm_deltimer(&ch->timer);
++        fsm_deltimer(&ch->sweep_timer);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
++        if(event == CH_EVENT_STOP)
++        {
++                s390irq_spin_lock_irqsave(ch->irq, saveflags);
++                gotlock = 1;
++        }
++        oldstate = fsm_getstate(fi);
++        fsm_newstate(fi, CH_STATE_TERM);
++        rc = halt_IO (ch->irq, (intparm_t)ch, 0);
++        if(gotlock)
++                s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
++        if(rc != 0)
++        {
++                fsm_deltimer(&ch->timer);
++                /* When I say stop..that means STOP */
++                if(event != CH_EVENT_STOP)
++                {
++                        fsm_newstate(fi, oldstate);
++                        ccw_check_return_code(ch, rc);
++                }
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * A channel has successfully been halted.
++ * Cleanup it's queue and notify interface statemachine.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_stopped(fsm_instance *fi, int event, void *arg)
++{
++        channel *ch = (channel *)arg;
++        net_device *dev = ch->netdev;
++
++        fsm_deltimer(&ch->timer);
++        fsm_deltimer(&ch->sweep_timer);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        fsm_newstate(fi, CH_STATE_STOPPED);
++        if(ch->trans_skb != NULL)
++        {
++                clear_normalized_cda(&ch->ccw[1]);
++                dev_kfree_skb_any(ch->trans_skb);
++                ch->trans_skb = NULL;
++        }
++
++        ch->th_seg = 0x00;
++        ch->th_seq_num = 0x00;
++
++#ifdef DEBUGSEQ
++        printk(KERN_INFO "%s: CH_th_seq= %08x\n" ,__FUNCTION__,ch->th_seq_num);
++#endif
++
++        if(CHANNEL_DIRECTION(ch->flags) == READ)
++        {
++                skb_queue_purge(&ch->io_queue);
++                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
++        } else
++        {
++                ctcmpc_purge_skb_queue(&ch->io_queue);
++                ctcmpc_purge_skb_queue(&ch->sweep_queue);
++                spin_lock(&ch->collect_lock);
++                ctcmpc_purge_skb_queue(&ch->collect_queue);
++                ch->collect_len = 0;
++                spin_unlock(&ch->collect_lock);
++                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * A stop command from device statemachine arrived and we are in
++ * not operational mode. Set state to stopped.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_stop(fsm_instance *fi, int event, void *arg)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        fsm_newstate(fi, CH_STATE_STOPPED);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * A machine check for no path, not operational status or gone device has
++ * happened.
++ * Cleanup queue and notify interface statemachine.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_fail(fsm_instance *fi, int event, void *arg)
++{
++        channel *ch = (channel *)arg;
++        net_device *dev = ch->netdev;
++
++        fsm_deltimer(&ch->timer);
++        fsm_deltimer(&ch->sweep_timer);
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        fsm_newstate(fi, CH_STATE_NOTOP);
++
++        ch->th_seg = 0x00;
++        ch->th_seq_num = 0x00;
++
++#ifdef DEBUGSEQ
++        printk(KERN_INFO "%s: CH_th_seq= %08x\n" ,__FUNCTION__,ch->th_seq_num);
++#endif
++        if(CHANNEL_DIRECTION(ch->flags) == READ)
++        {
++                skb_queue_purge(&ch->io_queue);
++                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
++        } else
++        {
++                ctcmpc_purge_skb_queue(&ch->io_queue);
++                ctcmpc_purge_skb_queue(&ch->sweep_queue);
++                spin_lock(&ch->collect_lock);
++                ctcmpc_purge_skb_queue(&ch->collect_queue);
++                ch->collect_len = 0;
++                spin_unlock(&ch->collect_lock);
++                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
++        }
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Handle error during setup of channel.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_setuperr(fsm_instance *fi, int event, void *arg)
++{
++        channel *ch = (channel *)arg;
++        net_device *dev = ch->netdev;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        /**
++         * Special case: Got UC_RCRESET on setmode.
++         * This means that remote side isn't setup. In this case
++         * simply retry after some secs...
++         */
++        if((fsm_getstate(fi) == CH_STATE_SETUPWAIT) &&
++           ((event == CH_EVENT_UC_RCRESET) ||
++            (event == CH_EVENT_UC_RSRESET)   ))
++        {
++                fsm_newstate(fi, CH_STATE_STARTRETRY);
++                fsm_deltimer(&ch->timer);
++                fsm_addtimer(&ch->timer, CTC_TIMEOUT_1SEC, CH_EVENT_TIMER, ch);
++//		if (CHANNEL_DIRECTION(ch->flags) == READ) {
++//			int rc = halt_IO (ch->irq, (intparm_t)ch, 0);
++//			if (rc != 0)
++//				ccw_check_return_code(ch, rc);
++//		}
++                goto done;
++        }
++
++        printk(KERN_INFO "%s: Error %s during %s channel setup state=%s\n",
++               dev->name, ch_event_names[event],
++               (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX",
++               fsm_getstate_str(fi));
++        if(CHANNEL_DIRECTION(ch->flags) == READ)
++        {
++                fsm_newstate(fi, CH_STATE_RXERR);
++                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
++        } else
++        {
++                fsm_newstate(fi, CH_STATE_TXERR);
++                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
++        }
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++        return;
++}
++
++/**
++ * Restart a channel after an error.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_restart(fsm_instance *fi, int event, void *arg)
++{
++        unsigned long saveflags;
++        int   oldstate;
++        int   rc;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        channel *ch = (channel *)arg;
++        net_device *dev = ch->netdev;
++
++        fsm_deltimer(&ch->timer);
++        printk(KERN_INFO "%s: %s channel restart\n", dev->name,
++               (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
++        fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
++        oldstate = fsm_getstate(fi);
++        fsm_newstate(fi, CH_STATE_STARTWAIT);
++        if(event == CH_EVENT_TIMER)
++                s390irq_spin_lock_irqsave(ch->irq, saveflags);
++        rc = halt_IO (ch->irq, (intparm_t)ch, 0);
++        if(event == CH_EVENT_TIMER)
++                s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
++        if(rc != 0)
++        {
++                fsm_deltimer(&ch->timer);
++                fsm_newstate(fi, oldstate);
++                ccw_check_return_code(ch, rc);
++        }
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Handle error during RX initial handshake (exchange of
++ * 0-length block header)
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_rxiniterr(fsm_instance *fi, int event, void *arg)
++{
++        channel *ch = (channel *)arg;
++        net_device *dev = ch->netdev;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        if(event == CH_EVENT_TIMER)
++        {
++                printk(KERN_INFO "%s: Timeout during RX init handshake\n",
++                       dev->name);
++                if(ch->retry++ < 3)
++                        ctcmpc_ch_action_restart(fi, event, arg);
++                else
++                {
++                        fsm_newstate(fi, CH_STATE_RXERR);
++                        fsm_event(((ctc_priv *)dev->priv)->fsm,
++                                  DEV_EVENT_RXDOWN, dev);
++                }
++        } else
++                printk(KERN_INFO "%s: Error during RX init handshake\n",
++                       dev->name);
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Notify device statemachine if we gave up initialization
++ * of RX channel.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_rxinitfail(fsm_instance *fi, int event, void *arg)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        channel *ch = (channel *)arg;
++        net_device *dev = ch->netdev;
++
++        fsm_newstate(fi, CH_STATE_RXERR);
++        printk(KERN_INFO "%s: RX initialization failed\n", dev->name);
++        printk(KERN_INFO "%s: RX <-> RX connection detected\n", dev->name);
++        fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Handle error during TX channel initialization.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_txiniterr(fsm_instance *fi, int event, void *arg)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        channel *ch = (channel *)arg;
++        net_device *dev = ch->netdev;
++
++        if(event == CH_EVENT_TIMER)
++        {
++                fsm_deltimer(&ch->timer);
++                printk(KERN_INFO "%s: Timeout during TX init handshake\n",
++                       dev->name);
++                if(ch->retry++ < 3)
++                        ctcmpc_ch_action_restart(fi, event, arg);
++                else
++                {
++                        fsm_newstate(fi, CH_STATE_TXERR);
++                        fsm_event(((ctc_priv *)dev->priv)->fsm,
++                                  DEV_EVENT_TXDOWN, dev);
++                }
++        } else
++                printk(KERN_INFO "%s: Error during TX init handshake\n",
++                       dev->name);
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Handle TX timeout by retrying operation.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_txretry(fsm_instance *fi, int event, void *arg)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc cp:%i enter:  %s()\n", 
++               smp_processor_id(),__FUNCTION__);
++#endif
++
++        channel    *ch = (channel *)arg;
++        net_device *dev = ch->netdev;
++        unsigned long saveflags;
++
++        ctc_priv *privptr = (ctc_priv *)dev->priv;
++        mpc_group *grpptr = privptr->mpcg;
++
++        fsm_deltimer(&ch->timer);
++
++
++        if(ch->retry++ > 3)
++        {
++                printk(KERN_INFO "%s: TX retry limit reached\n",
++                       dev->name);
++                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
++                if((grpptr) && (fsm_getstate(grpptr->fsm) == MPCG_STATE_READY))
++                        ctcmpc_ch_action_restart(fi, event, arg);
++        } else
++        {
++                struct sk_buff *skb;
++
++                printk(KERN_INFO "%s: TX retry %d\n", dev->name, ch->retry);
++                if((skb = skb_peek(&ch->io_queue)))
++                {
++                        int rc = 0;
++
++                        clear_normalized_cda(&ch->ccw[4]);
++                        ch->ccw[4].count = skb->len;
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21))
++                        if(set_normalized_cda(&ch->ccw[4],
++                                              virt_to_phys(skb->data)))
++                        {
++#else
++                        if(set_normalized_cda(&ch->ccw[4],skb->data))
++                        {
++#endif
++                                printk(KERN_INFO "%s: IDAL alloc failed, "
++                                       "restarting channel\n", dev->name);
++                                fsm_event(((ctc_priv *)dev->priv)->fsm,
++                                          DEV_EVENT_TXDOWN, dev);
++                                ctcmpc_ch_action_restart(fi, event, arg);
++                                goto done;
++                        }
++                        fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
++                        if(event == CH_EVENT_TIMER)
++                                s390irq_spin_lock_irqsave(ch->irq, saveflags);
++
++#ifdef DEBUGCCW
++                        dumpit((char *)&ch->ccw[3],sizeof(ccw1_t) * 3);
++#endif
++
++                        rc = do_IO(ch->irq, &ch->ccw[3], 
++                                   (intparm_t)ch, 0xff, 0);
++                        if(event == CH_EVENT_TIMER)
++                                s390irq_spin_unlock_irqrestore(ch->irq,
++                                                               saveflags);
++                        if(rc != 0)
++                        {
++                                /*not all return codes are bad */
++                                /* allow retries to occur      */
++                                /*ccw_check will result in ch  */
++                                /* down if rc was serious error*/
++                                ccw_check_return_code(ch, rc);
++                        }
++                }
++        }
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++        return;
++}
++/**
++ * Handle TX busy by setting timer to wait for completion
++ * then normal retry logic will occur
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_txbusy(fsm_instance *fi, int event, void *arg)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        channel    *ch = (channel *)arg;
++        if(ch->retry == 0)
++        {
++                fsm_deltimer(&ch->timer);
++                fsm_addtimer(&ch->timer, 
++                             CTC_BUSYWAIT_10SEC, 
++                             CH_EVENT_TIMER, 
++                             ch);
++        }
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++        return;
++}
++
++/**
++ * Handle fatal errors during an I/O command.
++ *
++ * @param fi    An instance of a channel statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from channel * upon call.
++ */
++static void 
++ctcmpc_ch_action_iofatal(fsm_instance *fi, int event, void *arg)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        channel *ch = (channel *)arg;
++        net_device *dev = ch->netdev;
++        ctc_priv   *privptr = (ctc_priv *)dev->priv;
++
++        fsm_deltimer(&ch->timer);
++
++        printk(KERN_WARNING "%s(): UNRECOVERABLE CHANNEL ERR - "
++               "CHANNEL REMOVED FROM MPC GROUP\n",
++               __FUNCTION__);
++        privptr->stats.tx_dropped++;
++        privptr->stats.tx_errors++;
++
++        if(CHANNEL_DIRECTION(ch->flags) == READ)
++        {
++                printk(KERN_INFO "%s: RX I/O error\n", dev->name);
++                fsm_newstate(fi, CH_STATE_RXERR);
++                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
++        } else
++        {
++                printk(KERN_INFO "%s: TX I/O error\n", dev->name);
++                fsm_newstate(fi, CH_STATE_TXERR);
++                fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
++        }
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * The statemachine for a channel.
++ */
++static const fsm_node ch_fsm[] = {
++        { CH_STATE_STOPPED,    CH_EVENT_STOP,        fsm_action_nop},
++        { CH_STATE_STOPPED,    CH_EVENT_START,       ctcmpc_ch_action_start},
++        { CH_STATE_STOPPED,    CH_EVENT_IO_ENODEV,   ctcmpc_ch_action_iofatal},
++
++        { CH_STATE_STOPPED,    CH_EVENT_FINSTAT,     fsm_action_nop},
++        { CH_STATE_STOPPED,    CH_EVENT_MC_FAIL,     fsm_action_nop},
++
++        { CH_STATE_NOTOP,      CH_EVENT_STOP,        ctcmpc_ch_action_stop},
++        { CH_STATE_NOTOP,      CH_EVENT_START,       fsm_action_nop},
++        { CH_STATE_NOTOP,      CH_EVENT_FINSTAT,     fsm_action_nop},
++        { CH_STATE_NOTOP,      CH_EVENT_MC_FAIL,     fsm_action_nop},
++        { CH_STATE_NOTOP,      CH_EVENT_MC_GOOD,     ctcmpc_ch_action_start},
++        { CH_STATE_NOTOP,      CH_EVENT_UC_RCRESET,  ctcmpc_ch_action_stop},
++        { CH_STATE_NOTOP,      CH_EVENT_UC_RSRESET,  ctcmpc_ch_action_stop},
++        { CH_STATE_NOTOP,      CH_EVENT_IO_ENODEV,   ctcmpc_ch_action_iofatal},
++
++        { CH_STATE_STARTWAIT,  CH_EVENT_STOP,        ctcmpc_ch_action_haltio},
++        { CH_STATE_STARTWAIT,  CH_EVENT_START,       fsm_action_nop},
++        { CH_STATE_STARTWAIT,  CH_EVENT_FINSTAT,     ctcmpc_ch_action_setmode},
++        { CH_STATE_STARTWAIT,  CH_EVENT_TIMER,       ctcmpc_ch_action_setuperr},
++        { CH_STATE_STARTWAIT,  CH_EVENT_IO_ENODEV,   ctcmpc_ch_action_iofatal},
++        { CH_STATE_STARTWAIT,  CH_EVENT_IO_EIO,      ctcmpc_ch_action_iofatal},
++        { CH_STATE_STARTWAIT,  CH_EVENT_MC_FAIL,     ctcmpc_ch_action_fail},
++
++        { CH_STATE_STARTRETRY, CH_EVENT_STOP,        ctcmpc_ch_action_haltio},
++        { CH_STATE_STARTRETRY, CH_EVENT_TIMER,       ctcmpc_ch_action_setmode},
++        { CH_STATE_STARTRETRY, CH_EVENT_FINSTAT,     ctcmpc_ch_action_setmode},
++//	{ CH_STATE_STARTRETRY, CH_EVENT_FINSTAT,     fsm_action_nop       },
++        { CH_STATE_STARTRETRY, CH_EVENT_MC_FAIL,     ctcmpc_ch_action_fail},
++        { CH_STATE_STARTRETRY, CH_EVENT_IO_ENODEV,   ctcmpc_ch_action_iofatal},
++
++
++        { CH_STATE_SETUPWAIT,  CH_EVENT_STOP,        ctcmpc_ch_action_haltio},
++        { CH_STATE_SETUPWAIT,  CH_EVENT_START,       fsm_action_nop},
++        { CH_STATE_SETUPWAIT,  CH_EVENT_FINSTAT,     ctcmpc_ch_action_firstio},
++        { CH_STATE_SETUPWAIT,  CH_EVENT_UC_RCRESET,  ctcmpc_ch_action_setuperr},
++        { CH_STATE_SETUPWAIT,  CH_EVENT_UC_RSRESET,  ctcmpc_ch_action_setuperr},
++        { CH_STATE_SETUPWAIT,  CH_EVENT_TIMER,       ctcmpc_ch_action_setmode},
++        { CH_STATE_SETUPWAIT,  CH_EVENT_IO_ENODEV,   ctcmpc_ch_action_iofatal},
++        { CH_STATE_SETUPWAIT,  CH_EVENT_IO_EIO,      ctcmpc_ch_action_iofatal},
++        { CH_STATE_SETUPWAIT,  CH_EVENT_MC_FAIL,     ctcmpc_ch_action_fail},
++
++        { CH_STATE_RXINIT,     CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
++        { CH_STATE_RXINIT,     CH_EVENT_START,      fsm_action_nop},
++        { CH_STATE_RXINIT,     CH_EVENT_FINSTAT,    ctcmpc_ch_action_rxidle},
++        { CH_STATE_RXINIT,     CH_EVENT_UC_RCRESET, ctcmpc_ch_action_rxiniterr},
++        { CH_STATE_RXINIT,     CH_EVENT_UC_RSRESET, ctcmpc_ch_action_rxiniterr},
++        { CH_STATE_RXINIT,     CH_EVENT_TIMER,      ctcmpc_ch_action_rxiniterr},
++        { CH_STATE_RXINIT,     CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_rxinitfail},
++        { CH_STATE_RXINIT,     CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
++        { CH_STATE_RXINIT,     CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
++        { CH_STATE_RXINIT,     CH_EVENT_UC_ZERO,    ctcmpc_ch_action_firstio},
++        { CH_STATE_RXINIT,     CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
++
++        { CH_XID0_PENDING,      CH_EVENT_FINSTAT,   fsm_action_nop},
++        { CH_XID0_PENDING,      CH_EVENT_ATTN,      ctcmpc_action_attn},
++        { CH_XID0_PENDING,      CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
++        { CH_XID0_PENDING,      CH_EVENT_START,     fsm_action_nop},
++        { CH_XID0_PENDING,      CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
++        { CH_XID0_PENDING,      CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
++        { CH_XID0_PENDING,      CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
++        { CH_XID0_PENDING,      CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID0_PENDING,      CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID0_PENDING,      CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID0_PENDING,      CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
++
++        { CH_XID0_INPROGRESS,  CH_EVENT_FINSTAT,    ctcmpc_ch_action_rx},
++        { CH_XID0_INPROGRESS,  CH_EVENT_ATTN,       ctcmpc_action_attn},
++        { CH_XID0_INPROGRESS,  CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
++        { CH_XID0_INPROGRESS,  CH_EVENT_START,      fsm_action_nop},
++        { CH_XID0_INPROGRESS,  CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
++        { CH_XID0_INPROGRESS,  CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
++        { CH_XID0_INPROGRESS,  CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
++        { CH_XID0_INPROGRESS,  CH_EVENT_UC_ZERO,    ctcmpc_ch_action_rx},
++        { CH_XID0_INPROGRESS,  CH_EVENT_UC_RCRESET, ctcmpc_ch_action_setuperr},
++        { CH_XID0_INPROGRESS,  CH_EVENT_ATTNBUSY,   ctcmpc_action_attnbusy},
++        { CH_XID0_INPROGRESS,  CH_EVENT_TIMER,      ctcmpc_action_resend},
++        { CH_XID0_INPROGRESS,  CH_EVENT_IO_EBUSY,   ctcmpc_ch_action_fail},
++
++        { CH_XID7_PENDING,      CH_EVENT_FINSTAT,   ctcmpc_ch_action_rx},
++        { CH_XID7_PENDING,      CH_EVENT_ATTN,      ctcmpc_action_attn},
++        { CH_XID7_PENDING,      CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
++        { CH_XID7_PENDING,      CH_EVENT_START,     fsm_action_nop},
++        { CH_XID7_PENDING,      CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING,      CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING,      CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
++        { CH_XID7_PENDING,      CH_EVENT_UC_ZERO,   ctcmpc_ch_action_rx},
++        { CH_XID7_PENDING,      CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING,      CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING,      CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING,      CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING,      CH_EVENT_TIMER,     ctcmpc_action_resend},
++        { CH_XID7_PENDING,      CH_EVENT_IO_EBUSY,  ctcmpc_ch_action_fail},
++
++
++        { CH_XID7_PENDING1,     CH_EVENT_FINSTAT,   ctcmpc_ch_action_rx},
++        { CH_XID7_PENDING1,     CH_EVENT_ATTN,      ctcmpc_action_attn},
++        { CH_XID7_PENDING1,     CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
++        { CH_XID7_PENDING1,     CH_EVENT_START,     fsm_action_nop},
++        { CH_XID7_PENDING1,     CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING1,     CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING1,     CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
++        { CH_XID7_PENDING1,     CH_EVENT_UC_ZERO,   ctcmpc_ch_action_rx},
++        { CH_XID7_PENDING1,     CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING1,     CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING1,     CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING1,     CH_EVENT_TIMER,     ctcmpc_action_resend},
++        { CH_XID7_PENDING1,     CH_EVENT_IO_EBUSY,  ctcmpc_ch_action_fail},
++
++        { CH_XID7_PENDING2,     CH_EVENT_FINSTAT,   ctcmpc_ch_action_rx},
++        { CH_XID7_PENDING2,     CH_EVENT_ATTN,      ctcmpc_action_attn},
++        { CH_XID7_PENDING2,     CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
++        { CH_XID7_PENDING2,     CH_EVENT_START,     fsm_action_nop},
++        { CH_XID7_PENDING2,     CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING2,     CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING2,     CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
++        { CH_XID7_PENDING2,     CH_EVENT_UC_ZERO,   ctcmpc_ch_action_rx},
++        { CH_XID7_PENDING2,     CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING2,     CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING2,     CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING2,     CH_EVENT_TIMER,     ctcmpc_action_resend},
++        { CH_XID7_PENDING2,     CH_EVENT_IO_EBUSY,  ctcmpc_ch_action_fail},
++
++
++        { CH_XID7_PENDING3,     CH_EVENT_FINSTAT,   ctcmpc_ch_action_rx},
++        { CH_XID7_PENDING3,     CH_EVENT_ATTN,      ctcmpc_action_attn},
++        { CH_XID7_PENDING3,     CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
++        { CH_XID7_PENDING3,     CH_EVENT_START,     fsm_action_nop},
++        { CH_XID7_PENDING3,     CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING3,     CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING3,     CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
++        { CH_XID7_PENDING3,     CH_EVENT_UC_ZERO,   ctcmpc_ch_action_rx},
++        { CH_XID7_PENDING3,     CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING3,     CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING3,     CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING3,     CH_EVENT_TIMER,     ctcmpc_action_resend},
++        { CH_XID7_PENDING3,     CH_EVENT_IO_EBUSY,  ctcmpc_ch_action_fail},
++
++
++        { CH_XID7_PENDING4,     CH_EVENT_FINSTAT,   ctcmpc_ch_action_rx},
++        { CH_XID7_PENDING4,     CH_EVENT_ATTN,      ctcmpc_action_attn},
++        { CH_XID7_PENDING4,     CH_EVENT_STOP,      ctcmpc_ch_action_haltio},
++        { CH_XID7_PENDING4,     CH_EVENT_START,     fsm_action_nop},
++        { CH_XID7_PENDING4,     CH_EVENT_IO_ENODEV, ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING4,     CH_EVENT_IO_EIO,    ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING4,     CH_EVENT_MC_FAIL,   ctcmpc_ch_action_fail},
++        { CH_XID7_PENDING4,     CH_EVENT_UC_ZERO,   ctcmpc_ch_action_rx},
++        { CH_XID7_PENDING4,     CH_EVENT_UC_RCRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING4,     CH_EVENT_UC_RSRESET,ctcmpc_ch_action_setuperr},
++        { CH_XID7_PENDING4,     CH_EVENT_ATTNBUSY,  ctcmpc_ch_action_iofatal},
++        { CH_XID7_PENDING4,     CH_EVENT_TIMER,     ctcmpc_action_resend},
++        { CH_XID7_PENDING4,     CH_EVENT_IO_EBUSY,  ctcmpc_ch_action_fail},
++
++        { CH_STATE_RXIDLE,     CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
++        { CH_STATE_RXIDLE,     CH_EVENT_START,      fsm_action_nop},
++        { CH_STATE_RXIDLE,     CH_EVENT_FINSTAT,    ctcmpc_ch_action_rx},
++        { CH_STATE_RXIDLE,     CH_EVENT_UC_RCRESET, ctcmpc_ch_action_fail},
++        { CH_STATE_RXIDLE,     CH_EVENT_UC_RSRESET, ctcmpc_ch_action_fail},
++        { CH_STATE_RXIDLE,     CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
++        { CH_STATE_RXIDLE,     CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
++        { CH_STATE_RXIDLE,     CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
++        { CH_STATE_RXIDLE,     CH_EVENT_UC_ZERO,    ctcmpc_ch_action_rx},
++
++        { CH_STATE_TXINIT,     CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
++        { CH_STATE_TXINIT,     CH_EVENT_START,      fsm_action_nop},
++        { CH_STATE_TXINIT,     CH_EVENT_FINSTAT,    ctcmpc_ch_action_txidle},
++        { CH_STATE_TXINIT,     CH_EVENT_UC_RCRESET, ctcmpc_ch_action_txiniterr},
++        { CH_STATE_TXINIT,     CH_EVENT_UC_RSRESET, ctcmpc_ch_action_txiniterr},
++        { CH_STATE_TXINIT,     CH_EVENT_TIMER,      ctcmpc_ch_action_txiniterr},
++        { CH_STATE_TXINIT,     CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
++        { CH_STATE_TXINIT,     CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
++        { CH_STATE_TXINIT,     CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
++        { CH_STATE_TXINIT,     CH_EVENT_RSWEEP1_TIMER,  ctcmpc_send_sweep},
++
++        { CH_STATE_TXIDLE,     CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
++        { CH_STATE_TXIDLE,     CH_EVENT_START,      fsm_action_nop},
++        { CH_STATE_TXIDLE,     CH_EVENT_FINSTAT,    ctcmpc_ch_action_firstio},
++        { CH_STATE_TXIDLE,     CH_EVENT_UC_RCRESET, ctcmpc_ch_action_fail},
++        { CH_STATE_TXIDLE,     CH_EVENT_UC_RSRESET, ctcmpc_ch_action_fail},
++        { CH_STATE_TXIDLE,     CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
++        { CH_STATE_TXIDLE,     CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
++        { CH_STATE_TXIDLE,     CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
++        { CH_STATE_TXIDLE,     CH_EVENT_RSWEEP1_TIMER,  ctcmpc_send_sweep},
++
++        { CH_STATE_TERM,       CH_EVENT_STOP,       fsm_action_nop},
++        { CH_STATE_TERM,       CH_EVENT_START,      ctcmpc_ch_action_restart},
++        { CH_STATE_TERM,       CH_EVENT_FINSTAT,    ctcmpc_ch_action_stopped},
++        { CH_STATE_TERM,       CH_EVENT_UC_RCRESET, fsm_action_nop},
++        { CH_STATE_TERM,       CH_EVENT_UC_RSRESET, fsm_action_nop},
++        { CH_STATE_TERM,       CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
++        { CH_STATE_TERM,       CH_EVENT_IO_EBUSY,   ctcmpc_ch_action_fail},
++        { CH_STATE_TERM,       CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
++                                                    
++        { CH_STATE_DTERM,      CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
++        { CH_STATE_DTERM,      CH_EVENT_START,      ctcmpc_ch_action_restart},
++        { CH_STATE_DTERM,      CH_EVENT_FINSTAT,    ctcmpc_ch_action_setmode},
++        { CH_STATE_DTERM,      CH_EVENT_UC_RCRESET, fsm_action_nop},
++        { CH_STATE_DTERM,      CH_EVENT_UC_RSRESET, fsm_action_nop},
++        { CH_STATE_DTERM,      CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
++        { CH_STATE_DTERM,      CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
++                                                    
++        { CH_STATE_TX,         CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
++        { CH_STATE_TX,         CH_EVENT_START,      fsm_action_nop},
++        { CH_STATE_TX,         CH_EVENT_FINSTAT,    ctcmpc_ch_action_txdone},
++        { CH_STATE_TX,         CH_EVENT_UC_RCRESET, ctcmpc_ch_action_fail},
++        { CH_STATE_TX,         CH_EVENT_UC_RSRESET, ctcmpc_ch_action_fail},
++        { CH_STATE_TX,         CH_EVENT_TIMER,      ctcmpc_ch_action_txretry},  
++        { CH_STATE_TX,         CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
++        { CH_STATE_TX,         CH_EVENT_IO_EIO,     ctcmpc_ch_action_iofatal},
++        { CH_STATE_TX,         CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
++        { CH_STATE_TX,         CH_EVENT_RSWEEP1_TIMER,  ctcmpc_send_sweep},
++        { CH_STATE_TX,         CH_EVENT_IO_EBUSY,   ctcmpc_ch_action_txbusy},
++
++        { CH_STATE_RXERR,      CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
++        { CH_STATE_TXERR,      CH_EVENT_STOP,       ctcmpc_ch_action_haltio},
++        { CH_STATE_TXERR,      CH_EVENT_IO_ENODEV,  ctcmpc_ch_action_iofatal},
++
++        { CH_STATE_TXERR,      CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
++        { CH_STATE_RXERR,      CH_EVENT_MC_FAIL,    ctcmpc_ch_action_fail},
++};
++
++static const int CH_FSM_LEN = sizeof(ch_fsm) / sizeof(fsm_node);
++
++/**
++ * Functions related to setup and device detection.
++ *****************************************************************************/
++
++/**
++ * Add a new channel to the list of channels.
++ * Keeps the channel list sorted.
++ *
++ * @param irq   The IRQ to be used by the new channel.
++ * @param devno The device number of the new channel.
++ * @param type  The type class of the new channel.
++ *
++ * @return 0 on success, !0 on error.
++ */
++static int 
++ctcmpc_add_channel(int irq, __u16 devno, channel_type_t type)
++{
++        channel **c = &channels;
++        channel *ch;
++        char name[10];
++        int rc = 0;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++
++        if((ch = (channel *)kmalloc(sizeof(channel), gfp_type())) == NULL)
++        {
++                printk(KERN_INFO "ctcmpc: Out of memory in %s\n",__FUNCTION__);
++                rc = -1;
++                goto done;
++        }
++        memset(ch, 0, sizeof(channel));
++
++        ch->discontact_th = (th_header *)kmalloc(TH_HEADER_LENGTH,gfp_type());
++        if(ch->discontact_th == NULL)
++        {
++                kfree(ch);
++                printk(KERN_INFO "ctcmpc: Out of memory in %s\n",__FUNCTION__);
++                rc = -1;
++                goto done;
++        }
++        memset(ch->discontact_th, 0, TH_HEADER_LENGTH);
++        ch->discontact_th->th_blk_flag = TH_DISCONTACT;
++        tasklet_init(&ch->ch_disc_tasklet,
++                     ctcmpc_action_send_discontact,
++                     (unsigned long)ch);
++
++
++        tasklet_init(&ch->ch_tasklet,ctcmpc_bh,(unsigned long)ch);
++        if((ch->ccw = (ccw1_t *)kmalloc(sizeof(ccw1_t) * 17,
++                                        GFP_KERNEL|GFP_DMA)) == NULL)
++        {
++                kfree(ch);
++                printk(KERN_INFO "ctcmpc: Out of memory in %s\n",__FUNCTION__);
++                rc = -1;
++                goto done;
++        }
++
++        ch->max_bufsize = (MPC_BUFSIZE_DEFAULT - 35);
++        /**
++         * "static" ccws are used in the following way:
++         *
++         * ccw[0..2] (Channel program for generic I/O):
++         *           0: prepare
++         *           1: read or write (depending on direction) with fixed
++         *              buffer (idal allocated once when buffer is allocated)
++         *           2: tic  (to double noops)
++         * ccw[3..5] (Channel program for direct write of packets)
++         *           3: prepare
++         *           4: write (idal allocated on every write).
++         *           5: tic  (to double noops)
++         * ccw[6..7] (Channel program for initial channel setup):
++         *           6: set extended mode
++         *           7: nop
++         *
++         * ch->ccw[0..5] are initialized in ctcmpc_ch_action_start because
++         * the channel's direction is yet unknown here.
++         *
++         * ccws used for xid2 negotiations
++         *  ch-ccw[8-14] need to be used for the XID exchange either 
++         *    X side XID2 Processing
++         *       8:  write control
++         *       9:  write th 
++         *	     10: write XID
++         *	     11: read th from secondary
++         *	     12: read XID   from secondary
++         *	     13: read 4 byte ID
++         *	     14: nop
++         *    Y side XID Processing
++         *	     8:  sense
++         *       9:  read th 
++         *	     10: read XID
++         *	     11: write th 
++         *	     12: write XID   
++         *	     13: write 4 byte ID
++         *	     14: nop
++         *
++         *  ccws used for double noop due to VM timing issues
++         *  which result in unrecoverable Busy on channel
++         *       15: nop
++         *       16: nop
++         */
++
++        ch->ccw[6].cmd_code = CCW_CMD_SET_EXTENDED;
++        ch->ccw[6].flags    = CCW_FLAG_SLI;
++        ch->ccw[6].count    = 0;
++        ch->ccw[6].cda      = 0;
++
++        ch->ccw[7].cmd_code = CCW_CMD_NOOP;
++        ch->ccw[7].flags    = CCW_FLAG_SLI;
++        ch->ccw[7].count    = 0;
++        ch->ccw[7].cda      = 0;
++
++        ch->ccw[15].cmd_code = CCW_CMD_WRITE;
++        ch->ccw[15].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
++        ch->ccw[15].count    = TH_HEADER_LENGTH;
++        ch->ccw[15].cda      = virt_to_phys(ch->discontact_th);
++
++        ch->ccw[16].cmd_code = CCW_CMD_NOOP;
++        ch->ccw[16].flags    = CCW_FLAG_SLI;
++        ch->ccw[16].count    = 0;
++        ch->ccw[16].cda      = 0;
++
++
++        ch->irq = irq;
++        ch->devno = devno;
++        ch->type = type;
++        ch->in_mpcgroup = 0;
++        sprintf(name, "ch-%04x", devno);
++        ch->fsm = init_fsm(name, ch_state_names,
++                           ch_event_names, NR_CH_STATES, NR_CH_EVENTS,
++                           ch_fsm, CH_FSM_LEN, GFP_KERNEL);
++        if(ch->fsm == NULL)
++        {
++                printk(KERN_INFO
++                       "ctcmpc: Could not create FSM in ctcmpc_add_channel\n");
++                kfree(ch);
++                rc = -1;
++                goto done;
++        }
++        fsm_newstate(ch->fsm, CH_STATE_IDLE);
++        if((ch->devstat = (devstat_t*)kmalloc(sizeof(devstat_t), gfp_type()))
++           == NULL)
++        {
++                printk(KERN_INFO "ctcmpc: Out of memory in %s\n",__FUNCTION__);
++                kfree_fsm(ch->fsm);
++                kfree(ch);
++                rc = -1;
++                goto done;
++        }
++        memset(ch->devstat, 0, sizeof(devstat_t));
++        while(*c && ((*c)->devno < devno))
++                c = &(*c)->next;
++        if((*c)->devno == devno)
++        {
++                printk(KERN_INFO
++                       "ctcmpc: %s: device %04x already in list, "
++                       "using old entry\n", __FUNCTION__,(*c)->devno);
++                kfree(ch->devstat);
++                kfree_fsm(ch->fsm);
++                kfree(ch);
++                rc = 0;
++                goto done;
++        }
++        fsm_settimer(ch->fsm, &ch->timer);
++        fsm_settimer(ch->fsm, &ch->sweep_timer);
++        skb_queue_head_init(&ch->io_queue);
++        skb_queue_head_init(&ch->collect_queue);
++        skb_queue_head_init(&ch->sweep_queue);
++        ch->next = *c;
++        *c = ch;
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return(rc);
++}
++
++#ifndef CTC_CHANDEV
++/**
++ * scan for all channels and create an entry in the channels list
++ * for every supported channel.
++ */
++static void 
++channel_scan(void)
++{
++        static int      print_result = 1;
++        int             irq;
++        int             nr_mpc = 0;
++        s390_dev_info_t di;
++
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++        #endif
++
++
++        for(irq = 0; irq < NR_IRQS; irq++)
++        {
++                if(get_dev_info_by_irq(irq, &di) == 0)
++                {
++                        if((di.status == DEVSTAT_NOT_OPER) ||
++                           (di.status == DEVSTAT_DEVICE_OWNED))
++                                continue;
++                        switch(channel_type(&di.sid_data))
++                        {
++                                case channel_type_mpc:
++                                        /* CTC/A  or ESCON*/
++                                        if(!ctcmpc_add_channel(irq, di.devno,
++                                                             channel_type_mpc))
++                                                nr_mpc++;
++                                        break;
++                                default: break;
++                        }
++                }
++        }
++        if(print_result)
++        {
++                if(nr_mpc)
++                        printk(KERN_INFO
++                               "ctcmpc: %d channel%s found.\n",
++                               nr_mpc, (nr_mpc == 1) ? "s" : "");
++                else
++                        printk(KERN_INFO "ctcmpc: No channel devices found.\n");
++        }
++        print_result = 0;
++
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++        #endif
++
++}
++#endif
++
++/**
++ * Release a specific channel in the channel list.
++ *
++ * @param ch Pointer to channel struct to be released.
++ */
++static void 
++channel_free(channel *ch)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        ch->flags &= ~CHANNEL_FLAGS_INUSE;
++        fsm_newstate(ch->fsm, CH_STATE_IDLE);
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Remove a specific channel in the channel list.
++ *
++ * @param ch Pointer to channel struct to be released.
++ */
++static void 
++channel_remove(channel *ch)
++{
++        channel **c = &channels;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        if(ch == NULL)
++                goto done;
++
++#ifndef CTC_CHANDEV
++        if(ch->flags & CHANNEL_FLAGS_INUSE)
++                FREE_IRQ(ch->irq, ch->devstat);
++#endif
++        channel_free(ch);
++        while(*c)
++        {
++                if(*c == ch)
++                {
++                        *c = ch->next;
++                        fsm_deltimer(&ch->timer);
++                        fsm_deltimer(&ch->sweep_timer);
++                        kfree_fsm(ch->fsm);
++                        clear_normalized_cda(&ch->ccw[4]);
++                        if(ch->trans_skb != NULL)
++                        {
++                                clear_normalized_cda(&ch->ccw[1]);
++                                dev_kfree_skb_any(ch->trans_skb);
++                        }
++                        tasklet_kill(&ch->ch_tasklet);
++                        tasklet_kill(&ch->ch_disc_tasklet);
++                        kfree(ch->discontact_th);
++                        kfree(ch->ccw);
++                        goto done;
++                }
++                c = &((*c)->next);
++        }
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++        return;
++}
++
++
++/**
++ * Get a specific channel from the channel list.
++ *
++ * @param type Type of channel we are interested in.
++ * @param devno Device number of channel we are interested in.
++ * @param direction Direction we want to use this channel for.
++ *
++ * @return Pointer to a channel or NULL if no matching channel available.
++ */
++static channel 
++*channel_get(channel_type_t type, int devno, int direction)
++{
++        channel *ch = channels;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++
++#ifdef DEBUG
++        printk(KERN_INFO
++               "ctcmpc: %s(): searching for ch with devno %d and type %d\n",
++               __FUNCTION__, devno, type);
++#endif
++
++        while(ch && ((ch->devno != devno) || (ch->type != type)))
++        {
++#ifdef DEBUG
++                printk(KERN_INFO
++                       "ctcmpc: %s(): ch=0x%p (devno=%d, type=%d\n",
++                       __FUNCTION__, ch, ch->devno, ch->type);
++#endif
++                ch = ch->next;
++        }
++#ifdef DEBUG
++        printk(KERN_INFO
++               "ctcmpc: %s(): ch=0x%pq (devno=%d, type=%d\n",
++               __FUNCTION__, ch, ch->devno, ch->type);
++#endif
++        if(!ch)
++        {
++                printk(KERN_INFO "ctcmpc: %s(): channel with devno %d "
++                       "and type %d not found in channel list\n",
++                       __FUNCTION__, devno, type);
++        } else
++        {
++                if(ch->flags & CHANNEL_FLAGS_INUSE)
++                        ch = NULL;
++                else
++                {
++                        ch->flags |= CHANNEL_FLAGS_INUSE;
++                        ch->flags &= ~CHANNEL_FLAGS_RWMASK;
++                        ch->flags |= (direction == WRITE)
++                                     ? CHANNEL_FLAGS_WRITE:CHANNEL_FLAGS_READ;
++                        fsm_newstate(ch->fsm, CH_STATE_STOPPED);
++                }
++        }
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return ch;
++}
++
++#ifndef CTC_CHANDEV
++/**
++ * Get the next free channel from the channel list
++ *
++ * @param type Type of channel we are interested in.
++ * @param direction Direction we want to use this channel for.
++ *
++ * @return Pointer to a channel or NULL if no matching channel available.
++ */
++static channel 
++*channel_get_next(channel_type_t type, int direction)
++{
++        channel *ch = channels;
++
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++        #endif
++
++        while(ch && (ch->type != type || (ch->flags & CHANNEL_FLAGS_INUSE)))
++                ch = ch->next;
++        if(ch)
++        {
++                ch->flags |= CHANNEL_FLAGS_INUSE;
++                ch->flags &= ~CHANNEL_FLAGS_RWMASK;
++                ch->flags |= (direction == WRITE)
++                             ? CHANNEL_FLAGS_WRITE:CHANNEL_FLAGS_READ;
++                fsm_newstate(ch->fsm, CH_STATE_STOPPED);
++        }
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++        #endif
++
++        return ch;
++}
++#endif
++
++/**
++ * Return the channel type by name.
++ *
++ * @param name Name of network interface.
++ *
++ * @return Type class of channel to be used for that interface.
++ */
++static channel_type_t inline 
++extract_channel_media(char *name)
++{
++        channel_type_t ret = channel_type_unknown;
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++
++        if(name != NULL)
++        {
++                if(strncmp(name, "mpc", 3) == 0)
++                        ret = channel_type_mpc;
++        }
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return ret;
++}
++
++/**
++ * Find a channel in the list by its IRQ.
++ *
++ * @param irq IRQ to search for.
++ *
++ * @return Pointer to channel or NULL if no matching channel found.
++ */
++static channel 
++*find_channel_by_irq(int irq)
++{
++        channel *ch = channels;
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        while(ch && (ch->irq != irq))
++                ch = ch->next;
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return ch;
++}
++
++/**
++ * Main IRQ handler.
++ *
++ * @param irq     The IRQ to handle.
++ * @param intparm IRQ params.
++ * @param regs    CPU registers.
++ */
++static void 
++ctcmpc_irq_handler (int irq, void *intparm, struct pt_regs *regs)
++{
++
++        devstat_t  *devstat = (devstat_t *)intparm;
++        channel    *ach = (channel *)devstat->intparm;
++        channel    *ch  = NULL;
++        net_device *dev;
++
++        /**
++         * Check for unsolicited interrupts.
++         * If intparm is NULL, then loop over all our known
++         * channels and try matching the irq number.
++         */
++        if(ach == NULL)
++        {
++                if((ch = find_channel_by_irq(irq)) == NULL)
++                {
++                        printk(KERN_INFO
++                               "ctcmpc: Got unsolicited irq: %04x c-%02x d-%02x"
++                               "f-%02x\n", devstat->devno, devstat->cstat,
++                               devstat->dstat, devstat->flag);
++                        goto done;
++                }
++        } else
++                ch = ach;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s() %04x\n", __FUNCTION__, ch->devno);
++#endif
++
++
++        dev = (net_device *)(ch->netdev);
++        if(dev == NULL)
++        {
++                printk(KERN_CRIT
++                       "ctcmpc: %s dev = NULL irq=%d, ch=0x%p\n",
++                       __FUNCTION__,irq, ch);
++                goto done;
++        }
++
++
++        if(intparm == NULL)
++                printk(KERN_INFO "%s: Channel %04x found by IRQ %d\n",
++                       dev->name, ch->devno, irq);
++
++
++#ifdef DEBUG
++        printk(KERN_INFO
++               "%s: cp:%i interrupt for device: %04x received "
++               "in state:%s c-%02x d-%02x "
++               "f-%02x\n", 
++               dev->name, 
++               smp_processor_id(),
++               devstat->devno, 
++               fsm_getstate_str(ch->fsm),
++               devstat->cstat,
++               devstat->dstat, 
++               devstat->flag);
++#endif
++
++        /* Check for good subchannel return code, otherwise error message */
++        if(devstat->cstat)
++        {
++                fsm_event(ch->fsm, CH_EVENT_SC_UNKNOWN, ch);
++                printk(KERN_INFO
++                       "%s: subchannel check for device: %04x - %02x %02x "
++                       "%02x\n", dev->name, ch->devno, devstat->cstat,
++                       devstat->dstat, devstat->flag);
++                goto done;
++        }
++
++        /* Check the reason-code of a unit check */
++        if(devstat->dstat & DEV_STAT_UNIT_CHECK)
++        {
++                ccw_unit_check(ch, devstat->ii.sense.data[0]);
++                goto done;
++        }
++        if(devstat->dstat & DEV_STAT_BUSY)
++        {
++                if(devstat->dstat & DEV_STAT_ATTENTION)
++                        fsm_event(ch->fsm, CH_EVENT_ATTNBUSY, ch);
++                else
++                        fsm_event(ch->fsm, CH_EVENT_BUSY, ch);
++                goto done;
++        }
++
++        if(devstat->dstat & DEV_STAT_ATTENTION)
++        {
++                fsm_event(ch->fsm, CH_EVENT_ATTN, ch);
++                goto done;
++        }
++
++        /* NOT unsolicited irq */
++        if(ach)
++        {
++                if(devstat->dstat & DEV_STAT_DEV_END)
++                {
++                        if((devstat->dstat & DEV_STAT_CHN_END) ||
++                           (devstat->flag & DEVSTAT_HALT_FUNCTION))
++                        {
++                                fsm_event(ch->fsm, CH_EVENT_FINSTAT, ch);
++                                goto done;
++                        } else
++                        {
++                                /* do_IO has not really completed */
++                                fsm_event(ch->fsm, CH_EVENT_IRQ, ch);
++                                goto done;
++                        }
++                }
++        }
++
++        if(devstat->flag & DEVSTAT_FINAL_STATUS)
++                fsm_event(ch->fsm, CH_EVENT_FINSTAT, ch);
++        else
++                fsm_event(ch->fsm, CH_EVENT_IRQ, ch);
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s() %04x\n", __FUNCTION__, ch->devno);
++#endif
++
++        return;
++
++}
++
++/**
++ * Actions for interface - statemachine.
++ *****************************************************************************/
++
++/**
++ * Startup channels by sending CH_EVENT_START to each channel.
++ *
++ * @param fi    An instance of an interface statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from net_device * upon call.
++ */
++static void 
++dev_action_start(fsm_instance *fi, int event, void *arg)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        net_device *dev = (net_device *)arg;
++        ctc_priv   *privptr = (ctc_priv *)dev->priv;
++        mpc_group  *grpptr = privptr->mpcg;
++        int        direction;
++
++        fsm_deltimer(&privptr->restart_timer);
++        fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
++        grpptr->channels_terminating = 0;
++        for(direction = READ; direction <= WRITE; direction++)
++        {
++                channel *ch = privptr->channel[direction];
++                fsm_event(ch->fsm, CH_EVENT_START, ch);
++        }
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Shutdown channels by sending CH_EVENT_STOP to each channel.
++ *
++ * @param fi    An instance of an interface statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from net_device * upon call.
++ */
++static void 
++dev_action_stop(fsm_instance *fi, int event, void *arg)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        net_device *dev = (net_device *)arg;
++        ctc_priv   *privptr = (ctc_priv *)dev->priv;
++        mpc_group  * grpptr = privptr->mpcg;
++        int        direction;
++
++        fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
++        for(direction = READ; direction <= WRITE; direction++)
++        {
++                channel *ch = privptr->channel[direction];
++                fsm_event(ch->fsm, CH_EVENT_STOP, ch);
++                ch->th_seq_num = 0x00;
++
++#ifdef DEBUGSEQ
++                printk(KERN_INFO "%s: CH_th_seq= %08x\n" ,
++                       __FUNCTION__,ch->th_seq_num);
++#endif
++        }
++        fsm_newstate(grpptr->fsm, MPCG_STATE_RESET);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++static void 
++dev_action_restart(fsm_instance *fi, int event, void *arg)
++{
++        net_device *dev = (net_device *)arg;
++        ctc_priv   *privptr = dev->priv;
++        mpc_group  * grpptr = privptr->mpcg;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        printk(KERN_DEBUG "ctcmpc: Restarting %s Device and MPC Group "
++               "in 5 seconds\n", 
++               dev->name);
++        dev_action_stop(fi, event, arg);
++        fsm_event(privptr->fsm, DEV_EVENT_STOP, dev);
++        fsm_newstate(grpptr->fsm, MPCG_STATE_RESET);
++        /* going back into start sequence too quickly can         */
++        /* result in the other side becoming unreachable   due    */
++        /* to sense reported when IO is aborted                   */
++        fsm_addtimer(&privptr->restart_timer, 1000, DEV_EVENT_START, dev);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Called from channel statemachine
++ * when a channel is up and running.
++ *
++ * @param fi    An instance of an interface statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from net_device * upon call.
++ */
++static void 
++dev_action_chup(fsm_instance *fi, int event, void *arg)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        net_device *dev = (net_device *)arg;
++        ctc_priv  *privptr = (ctc_priv *)dev->priv;
++
++        switch(fsm_getstate(fi))
++        {
++                case DEV_STATE_STARTWAIT_RXTX:
++                        if(event == DEV_EVENT_RXUP)
++                                fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
++                        else
++                                fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
++                        break;
++                case DEV_STATE_STARTWAIT_RX:
++                        if(event == DEV_EVENT_RXUP)
++                        {
++                                fsm_newstate(fi, DEV_STATE_RUNNING);
++                                printk(KERN_INFO
++                                       "%s: connected with remote side\n",
++                                       dev->name);
++                                ctcmpc_clear_busy(dev);
++                        }
++                        break;
++                case DEV_STATE_STARTWAIT_TX:
++                        if(event == DEV_EVENT_TXUP)
++                        {
++                                fsm_newstate(fi, DEV_STATE_RUNNING);
++                                printk(KERN_INFO
++                                       "%s: connected with remote side\n",
++                                       dev->name);
++                                ctcmpc_clear_busy(dev);
++                        }
++                        break;
++                case DEV_STATE_STOPWAIT_TX:
++                        if(event == DEV_EVENT_RXUP)
++                                fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
++                        break;
++                case DEV_STATE_STOPWAIT_RX:
++                        if(event == DEV_EVENT_TXUP)
++                                fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
++                        break;
++        }
++
++        if(event == DEV_EVENT_RXUP)
++                ctcmpc_channel_action(privptr->channel[READ],
++                                      READ,MPC_CHANNEL_ADD);
++        else
++                ctcmpc_channel_action(privptr->channel[WRITE],
++                                      WRITE,MPC_CHANNEL_ADD);
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++/**
++ * Called from channel statemachine
++ * when a channel has been shutdown.
++ *
++ * @param fi    An instance of an interface statemachine.
++ * @param event The event, just happened.
++ * @param arg   Generic pointer, casted from net_device * upon call.
++ */
++static void 
++dev_action_chdown(fsm_instance *fi, int event, void *arg)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        net_device *dev = (net_device *)arg;
++        ctc_priv   *privptr = (ctc_priv *)dev->priv;
++
++        switch(fsm_getstate(fi))
++        {
++                case DEV_STATE_RUNNING:
++                        if(event == DEV_EVENT_TXDOWN)
++                                fsm_newstate(fi, DEV_STATE_STARTWAIT_TX);
++                        else
++                                fsm_newstate(fi, DEV_STATE_STARTWAIT_RX);
++                        break;
++                case DEV_STATE_STARTWAIT_RX:
++                        if(event == DEV_EVENT_TXDOWN)
++                                fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
++                        break;
++                case DEV_STATE_STARTWAIT_TX:
++                        if(event == DEV_EVENT_RXDOWN)
++                                fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
++                        break;
++                case DEV_STATE_STOPWAIT_RXTX:
++                        if(event == DEV_EVENT_TXDOWN)
++                                fsm_newstate(fi, DEV_STATE_STOPWAIT_RX);
++                        else
++                                fsm_newstate(fi, DEV_STATE_STOPWAIT_TX);
++                        break;
++                case DEV_STATE_STOPWAIT_RX:
++                        if(event == DEV_EVENT_RXDOWN)
++                                fsm_newstate(fi, DEV_STATE_STOPPED);
++                        break;
++                case DEV_STATE_STOPWAIT_TX:
++                        if(event == DEV_EVENT_TXDOWN)
++                                fsm_newstate(fi, DEV_STATE_STOPPED);
++                        break;
++        }
++
++
++        if(event == DEV_EVENT_RXDOWN)
++                ctcmpc_channel_action(privptr->channel[READ],
++                                      READ,MPC_CHANNEL_REMOVE);
++        else
++                ctcmpc_channel_action(privptr->channel[WRITE],
++                                      WRITE,MPC_CHANNEL_REMOVE);
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++}
++
++static const fsm_node dev_fsm[] = {
++        { DEV_STATE_STOPPED,        DEV_EVENT_START,   dev_action_start},
++
++        { DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_START,   dev_action_start},
++        { DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_RXDOWN,  dev_action_chdown},
++        { DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_TXDOWN,  dev_action_chdown},
++        { DEV_STATE_STOPWAIT_RXTX,  DEV_EVENT_RESTART, dev_action_restart},
++
++        { DEV_STATE_STOPWAIT_RX,    DEV_EVENT_START,   dev_action_start},
++        { DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RXUP,    dev_action_chup},
++        { DEV_STATE_STOPWAIT_RX,    DEV_EVENT_TXUP,    dev_action_chup},
++        { DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RXDOWN,  dev_action_chdown},
++        { DEV_STATE_STOPWAIT_RX,    DEV_EVENT_RESTART, dev_action_restart},
++
++        { DEV_STATE_STOPWAIT_TX,    DEV_EVENT_START,   dev_action_start},
++        { DEV_STATE_STOPWAIT_TX,    DEV_EVENT_RXUP,    dev_action_chup},
++        { DEV_STATE_STOPWAIT_TX,    DEV_EVENT_TXUP,    dev_action_chup},
++        { DEV_STATE_STOPWAIT_TX,    DEV_EVENT_TXDOWN,  dev_action_chdown},
++        { DEV_STATE_STOPWAIT_TX,    DEV_EVENT_RESTART, dev_action_restart},
++
++        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP,    dev_action_stop},
++        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP,    dev_action_chup},
++        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP,    dev_action_chup},
++        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN,  dev_action_chdown},
++        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN,  dev_action_chdown},
++        { DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart},
++
++        { DEV_STATE_STARTWAIT_TX,   DEV_EVENT_STOP,    dev_action_stop},
++        { DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RXUP,    dev_action_chup},
++        { DEV_STATE_STARTWAIT_TX,   DEV_EVENT_TXUP,    dev_action_chup},
++        { DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RXDOWN,  dev_action_chdown},
++        { DEV_STATE_STARTWAIT_TX,   DEV_EVENT_RESTART, dev_action_restart},
++
++        { DEV_STATE_STARTWAIT_RX,   DEV_EVENT_STOP,    dev_action_stop},
++        { DEV_STATE_STARTWAIT_RX,   DEV_EVENT_RXUP,    dev_action_chup},
++        { DEV_STATE_STARTWAIT_RX,   DEV_EVENT_TXUP,    dev_action_chup},
++        { DEV_STATE_STARTWAIT_RX,   DEV_EVENT_TXDOWN,  dev_action_chdown},
++        { DEV_STATE_STARTWAIT_RX,   DEV_EVENT_RESTART, dev_action_restart},
++
++        { DEV_STATE_RUNNING,        DEV_EVENT_STOP,    dev_action_stop},
++        { DEV_STATE_RUNNING,        DEV_EVENT_RXDOWN,  dev_action_chdown},
++        { DEV_STATE_RUNNING,        DEV_EVENT_TXDOWN,  dev_action_chdown},
++        { DEV_STATE_RUNNING,        DEV_EVENT_TXUP,    fsm_action_nop},
++        { DEV_STATE_RUNNING,        DEV_EVENT_RXUP,    fsm_action_nop},
++        { DEV_STATE_RUNNING,        DEV_EVENT_RESTART, dev_action_restart},
++};
++
++static const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node);
++
++/**
++ * Transmit a packet.
++ * This is a helper function for ctcmpc_tx().
++ *
++ * @param ch Channel to be used for sending.
++ * @param skb Pointer to struct sk_buff of packet to send.
++ *            The linklevel header has already been set up
++ *            by ctcmpc_tx().
++ *
++ * @return 0 on success, -ERRNO on failure. (Never fails.)
++ */
++static int 
++transmit_skb(channel *ch, struct sk_buff *skb)
++{
++
++        unsigned long saveflags;
++        pdu      *p_header;
++        int       rc = 0;
++        net_device  *dev        = ch->netdev;
++        ctc_priv    *privptr    = (ctc_priv *)dev->priv;
++        mpc_group   *grpptr     = privptr->mpcg;
++#ifdef DEBUGDATA
++        __u32                out_len = 0;
++#endif
++
++
++
++#ifdef DEBUG
++        printk(KERN_INFO "%s cp:%i enter:  %s() state: %s \n", 
++               dev->name,
++               smp_processor_id(),
++               __FUNCTION__,
++               fsm_getstate_str(ch->fsm));
++#endif
++
++        if((fsm_getstate(ch->fsm) != CH_STATE_TXIDLE) || 
++           (grpptr->in_sweep))
++        {
++                spin_lock_irqsave(&ch->collect_lock, saveflags);
++                atomic_inc(&skb->users);
++                p_header = (pdu *)kmalloc(PDU_HEADER_LENGTH, gfp_type());
++
++                if(!p_header)
++                {
++                        printk(KERN_WARNING ": OUT OF MEMORY IN %s(): "
++                               "Data Lost \n",
++                               __FUNCTION__);
++                        atomic_dec(&skb->users);
++                        dev_kfree_skb_any(skb);
++                        spin_unlock_irqrestore(&ch->collect_lock, saveflags);
++                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
++                        goto done;
++                }
++
++                p_header->pdu_offset = skb->len;
++                p_header->pdu_proto = 0x01;
++                p_header->pdu_flag = 0x00;
++                if(skb->protocol == ntohs(ETH_P_SNAP))
++                {
++                        p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;   
++                } else
++                {
++                        p_header->pdu_flag |= PDU_FIRST; 
++                }
++                p_header->pdu_seq = 0;
++                memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header,
++                       PDU_HEADER_LENGTH);
++#ifdef DEBUGDATA
++                __u32 out_len;
++                if(skb->len > 32) out_len = 32;
++                else out_len = skb->len;
++                printk(KERN_INFO 
++                       "%s(): Putting on collect_q - skb len:%04x \n", 
++                       __FUNCTION__,skb->len);
++                printk(KERN_INFO 
++                       "%s(): pdu header and data for up to 32 bytes\n", 
++                       __FUNCTION__);
++                dumpit((char *)skb->data,out_len);
++#endif
++                skb_queue_tail(&ch->collect_queue, skb);
++                ch->collect_len += skb->len;
++                kfree(p_header);
++
++                spin_unlock_irqrestore(&ch->collect_lock, saveflags);
++        } else
++        {
++                __u16 block_len;
++                int ccw_idx;
++                struct sk_buff *nskb;
++                unsigned long hi;
++
++                /**
++                 * Protect skb against beeing free'd by upper
++                 * layers.
++                 */
++                atomic_inc(&skb->users);
++
++                block_len = skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
++
++                /**
++                 * IDAL support in CTC is broken, so we have to
++                 * care about skb's above 2G ourselves.
++                 */
++                hi = ((unsigned long)skb->tail + TH_HEADER_LENGTH) >> 31;
++                if(hi)
++                {
++                        nskb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
++
++                        if(!nskb)
++                        {
++                                printk(KERN_WARNING 
++                                       ": OUT OF MEMORY IN %s(): Data Lost \n",
++                                       __FUNCTION__);
++                                atomic_dec(&skb->users);
++                                dev_kfree_skb_any(skb);
++                                fsm_event(privptr->mpcg->fsm,
++                                          MPCG_EVENT_INOP,dev);
++                                goto done;
++                        } else
++                        {
++                                memcpy(skb_put(nskb, skb->len),
++                                       skb->data, skb->len);
++                                atomic_inc(&nskb->users);
++                                atomic_dec(&skb->users);
++                                dev_kfree_skb_any(skb);
++                                skb = nskb;
++                        }
++                }
++
++                p_header = (pdu *)kmalloc(PDU_HEADER_LENGTH, gfp_type());
++
++                if(!p_header)
++                {
++                        printk(KERN_WARNING 
++                               "ctcmpc: OUT OF MEMORY IN %s(): Data Lost \n",
++                               __FUNCTION__);
++                        atomic_dec(&skb->users);
++                        dev_kfree_skb_any(skb);
++                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
++                        goto done;
++                }
++
++                p_header->pdu_offset = skb->len;
++                p_header->pdu_proto = 0x01;
++                p_header->pdu_flag = 0x00;
++                p_header->pdu_seq = 0;
++                if(skb->protocol == ntohs(ETH_P_SNAP))
++                {
++                        p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;   
++                } else
++                {
++                        p_header->pdu_flag |= PDU_FIRST; 
++                }
++                memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header,
++                       PDU_HEADER_LENGTH);
++
++                kfree(p_header);
++
++                if(ch->collect_len > 0)
++                {
++                        spin_lock_irqsave(&ch->collect_lock, saveflags);
++                        skb_queue_tail(&ch->collect_queue, skb);
++                        ch->collect_len += skb->len;
++                        skb = skb_dequeue(&ch->collect_queue);
++                        ch->collect_len -= skb->len;
++                        spin_unlock_irqrestore(&ch->collect_lock, saveflags);
++                }
++
++                p_header = (pdu *)skb->data;  
++                p_header->pdu_flag |= PDU_LAST; 
++
++                ch->prof.txlen += skb->len - PDU_HEADER_LENGTH;
++
++                th_header *header;
++                header = (th_header *)kmalloc(TH_HEADER_LENGTH, gfp_type());
++
++                if(!header)
++                {
++                        printk(KERN_WARNING 
++                               "ctcmpc: OUT OF MEMORY IN %s(): Data Lost \n",
++                               __FUNCTION__);
++                        atomic_dec(&skb->users);
++                        dev_kfree_skb_any(skb);
++                        fsm_event(privptr->mpcg->fsm,MPCG_EVENT_INOP,dev);
++                        goto done;
++                }
++
++                header->th_seg = 0x00;
++                header->th_ch_flag = TH_HAS_PDU;  /* Normal data */
++                header->th_blk_flag = 0x00;
++                header->th_is_xid = 0x00;          /* Just data here */
++                ch->th_seq_num++;
++                header->th_seq_num = ch->th_seq_num;
++
++#ifdef DEBUGSEQ
++                printk(KERN_INFO "%s: ToVTAM_th_seq= %08x\n" ,
++                       __FUNCTION__,ch->th_seq_num);
++#endif
++
++                memcpy(skb_push(skb, TH_HEADER_LENGTH), header,
++                       TH_HEADER_LENGTH);       /*  put the TH on the packet */
++
++                kfree(header);
++
++#ifdef DEBUGDATA
++                if(skb->len > 32) out_len = 32;
++                else out_len = skb->len;
++                printk(KERN_INFO "%s(): skb len: %04x \n", 
++                       __FUNCTION__,skb->len);
++                printk(KERN_INFO 
++                       "%s(): pdu header and data for up to "
++                       "32 bytes sent to vtam\n", 
++                       __FUNCTION__);
++                dumpit((char *)skb->data,out_len);
++#endif
++
++                ch->ccw[4].count = skb->len;
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21))
++                if(set_normalized_cda(&ch->ccw[4], virt_to_phys(skb->data)))
++                {
++#else
++                if(set_normalized_cda(&ch->ccw[4], skb->data))
++                {
++#endif
++                        /**
++                         * idal allocation failed, try via copying to
++                         * trans_skb. trans_skb usually has a pre-allocated
++                         * idal.
++                         */
++                        if(ctcmpc_checkalloc_buffer(ch, 1))
++                        {
++                                /**
++                                 * We are toast.  Data lost.
++                                 */
++                                atomic_dec(&skb->users);
++                                dev_kfree_skb_any(skb);
++                                printk(KERN_WARNING 
++                                       "ctcmpc: OUT OF MEMORY IN %s():"
++                                       " Data Lost \n",
++                                       __FUNCTION__);
++                                fsm_event(privptr->mpcg->fsm,
++                                          MPCG_EVENT_INOP,dev);
++                                goto done;
++                        }
++
++                        ch->trans_skb->tail = ch->trans_skb->data;
++                        ch->trans_skb->len = 0;
++                        ch->ccw[1].count = skb->len;
++                        memcpy(skb_put(ch->trans_skb, skb->len), skb->data,
++                               skb->len);
++                        atomic_dec(&skb->users);
++                        dev_kfree_skb_any(skb);
++                        ccw_idx = 0;
++#ifdef DEBUGDATA
++                        if(ch->trans_skb->len > 32) out_len = 32;
++                        else out_len = ch->trans_skb->len;
++                        printk(KERN_INFO 
++                               "%s(): TRANS skb len: %d \n", 
++                               __FUNCTION__,ch->trans_skb->len);
++                        printk(KERN_INFO 
++                               "%s(): up to 32 bytes of data sent to vtam\n", 
++                               __FUNCTION__);
++                        dumpit((char *)ch->trans_skb->data,out_len);
++#endif
++
++
++                } else
++                {
++                        skb_queue_tail(&ch->io_queue, skb);
++                        ccw_idx = 3;
++                }
++
++                ch->retry = 0;
++                fsm_newstate(ch->fsm, CH_STATE_TX);
++                fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC,
++                             CH_EVENT_TIMER, ch);
++
++#ifdef DEBUGCCW
++                dumpit((char *)&ch->ccw[ccw_idx],sizeof(ccw1_t) * 3);
++#endif
++
++                s390irq_spin_lock_irqsave(ch->irq, saveflags);
++                ch->prof.send_stamp = xtime;
++
++
++                rc = do_IO(ch->irq, &ch->ccw[ccw_idx], (intparm_t)ch, 0xff, 0);
++
++                s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
++
++                if(ccw_idx == 3)
++                        ch->prof.doios_single++;
++                if(rc != 0)
++                {
++                        /* Not all rc from do_IO are bad.  ccw_check will     */
++                        /* handle cases in which this data will not be retried*/
++                        ccw_check_return_code(ch, rc);
++                } else
++                {
++                        if(ccw_idx == 0)
++                        {
++                                privptr->stats.tx_packets++;
++                                privptr->stats.tx_bytes +=
++                                skb->len - TH_HEADER_LENGTH;
++                        }
++                }
++                if(ch->th_seq_num > 0xf0000000)
++                {   /* Chose 4Billion at random.  */ 
++                        ctcmpc_send_sweep_req(ch);
++                }
++        }
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "%s exit:  %s()\n", dev->name,__FUNCTION__);
++#endif
++        return(0);
++}
++
++/**
++ * Interface API for upper network layers
++ *****************************************************************************/
++
++/**
++ * Open an interface.
++ * Called from generic network layer when ifconfig up is run.
++ *
++ * @param dev Pointer to interface struct.
++ *
++ * @return 0 on success, -ERRNO on failure. (Never fails.)
++ */
++static int 
++ctcmpc_open(net_device *dev)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        MOD_INC_USE_COUNT;
++        /* for MPC this is called from ctc_mpc_alloc_channel */
++        /*fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_START, dev);*/
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return 0;
++}
++
++/**
++ * Close an interface.
++ * Called from generic network layer when ifconfig down is run.
++ *
++ * @param dev Pointer to interface struct.
++ *
++ * @return 0 on success, -ERRNO on failure. (Never fails.)
++ */
++static int 
++ctcmpc_close(net_device *dev)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        SET_DEVICE_START(dev, 0);
++        /*Now called from mpc close only         */
++        /*fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_STOP, dev);*/
++        MOD_DEC_USE_COUNT;
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return 0;
++}
++
++/**
++ * Start transmission of a packet.
++ * Called from generic network device layer.
++ *
++ * @param skb Pointer to buffer containing the packet.
++ * @param dev Pointer to interface struct.
++ *
++ * @return 0 if packet consumed, !0 if packet rejected.
++ *         Note: If we return !0, then the packet is free'd by
++ *               the generic network layer.
++ */
++static int 
++ctcmpc_tx(struct sk_buff *skb, net_device *dev)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:%s() skb:%0lx\n", 
++               __FUNCTION__,(unsigned long)skb);
++#endif
++
++        int len;
++        ctc_priv  *privptr = (ctc_priv *)dev->priv;
++        mpc_group *grpptr  = privptr->mpcg;
++
++        /**
++         * Some sanity checks ...
++         */
++        if(skb == NULL)
++        {
++                printk(KERN_INFO 
++                       "%s: NULL sk_buff passed - EMPTY PACKET DROPPED\n", 
++                       dev->name);
++                privptr->stats.tx_dropped++;
++                goto done;
++        }
++        if(skb_headroom(skb) < (TH_HEADER_LENGTH + PDU_HEADER_LENGTH))
++        {
++                printk(KERN_INFO
++                       "%s: Got sk_buff with head room < %ld bytes\n",
++                       dev->name, TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
++                if(skb->len > 32) len = 32;
++                else len = skb->len;
++#ifdef DEBUGDATA
++                dumpit((char *)skb->data,len);
++#endif
++                len =  skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
++                sk_buff *newskb = __dev_alloc_skb(len,gfp_type() | GFP_DMA);
++
++                if(!newskb)
++                {
++                        printk(KERN_WARNING 
++                               "OUT OF MEMORY - in %s Data Lost\n",
++                               __FUNCTION__);
++                        printk(KERN_WARNING 
++                               "%s: DEVICE ERROR - UNRECOVERABLE DATA LOSS\n",
++                               __FUNCTION__);
++                        dev_kfree_skb_any(skb);
++                        privptr->stats.tx_dropped++;
++                        privptr->stats.tx_errors++;
++                        privptr->stats.tx_carrier_errors++;
++                        fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++                        goto done;
++                }
++                newskb->protocol = skb->protocol;
++                skb_reserve(newskb,TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
++                memcpy(skb_put(newskb,skb->len),skb->data,skb->len);
++                dev_kfree_skb_any(skb);
++                skb = newskb;
++        }
++
++        /**
++         * If channels are not running, 
++         * notify anybody about a link failure and throw
++         * away packet. 
++         */
++        if((fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) || 
++           (fsm_getstate(grpptr->fsm) <  MPCG_STATE_XID2INITW))
++        {
++                dev_kfree_skb_any(skb);
++                printk(KERN_INFO 
++                       "%s(): DATA RCVD - MPC GROUP NOT ACTIVE - DROPPED\n",
++                       __FUNCTION__);
++                privptr->stats.tx_dropped++;
++                privptr->stats.tx_errors++;
++                privptr->stats.tx_carrier_errors++;
++                goto done;
++        }
++
++        if(ctcmpc_test_and_set_busy(dev))
++        {
++                printk(KERN_WARNING 
++                       "%s: DEVICE ERROR - UNRECOVERABLE DATA LOSS\n",
++                       __FUNCTION__);
++                dev_kfree_skb_any(skb);
++                privptr->stats.tx_dropped++;
++                privptr->stats.tx_errors++;
++                privptr->stats.tx_carrier_errors++;
++                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++                goto done;
++        }
++
++        dev->trans_start = jiffies;
++        if(transmit_skb(privptr->channel[WRITE], skb) != 0)
++        {
++                printk(KERN_WARNING 
++                       "ctcmpc: DEVICE ERROR in %s(): Data Lost \n",
++                       __FUNCTION__);
++                printk(KERN_WARNING 
++                       "%s: DEVICE ERROR - UNRECOVERABLE DATA LOSS\n",
++                       __FUNCTION__);
++                dev_kfree_skb_any(skb);
++                privptr->stats.tx_dropped++;
++                privptr->stats.tx_errors++;
++                privptr->stats.tx_carrier_errors++;
++                ctcmpc_clear_busy(dev);
++                fsm_event(grpptr->fsm,MPCG_EVENT_INOP,dev);
++                goto done;
++        }
++        ctcmpc_clear_busy(dev);
++
++        done:
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return(0);      /*handle freeing of skb here */
++}
++
++
++/**
++ * Sets MTU of an interface.
++ *
++ * @param dev     Pointer to interface struct.
++ * @param new_mtu The new MTU to use for this interface.
++ *
++ * @return 0 on success, -EINVAL if MTU is out of valid range.
++ *         (valid range is 576 .. 65527). If VM is on the
++ *         remote side, maximum MTU is 32760, however this is
++ *         <em>not</em> checked here.
++ */
++static int 
++ctcmpc_change_mtu(net_device *dev, int new_mtu)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        ctc_priv  *privptr = (ctc_priv *)dev->priv;
++
++        if((new_mtu < 576) || (new_mtu > 65527) ||
++           (new_mtu > (privptr->channel[READ]->max_bufsize -
++                       TH_HEADER_LENGTH )))
++                return -EINVAL;
++        dev->mtu = new_mtu;
++        /* TH plus 4 byte sequence number on outbound path */
++        dev->hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:  %s()\n", __FUNCTION__);
++#endif
++
++        return 0;
++}
++
++
++/**
++ * Returns interface statistics of a device.
++ *
++ * @param dev Pointer to interface struct.
++ *
++ * @return Pointer to stats struct of this interface.
++ */
++static struct net_device_stats 
++*ctcmpc_stats(net_device *dev)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        return &((ctc_priv *)dev->priv)->stats;
++}
++
++/**
++ * procfs related structures and routines
++ *****************************************************************************/
++
++static net_device 
++*find_netdev_by_ino(unsigned long ino)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        channel *ch = channels;
++        net_device *dev = NULL;
++        ctc_priv *privptr;
++
++        while(ch)
++        {
++                if(ch->netdev != dev)
++                {
++                        dev = ch->netdev;
++                        privptr = (ctc_priv *)dev->priv;
++
++                        if((privptr->proc_ctrl_entry->low_ino == ino) ||
++                           (privptr->proc_stat_entry->low_ino == ino))
++                                return dev;
++                }
++                ch = ch->next;
++        }
++        return NULL;
++}
++
++#define CTRL_BUFSIZE 40
++
++static int 
++ctcmpc_ctrl_open(struct inode *inode, struct file *file)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL);
++        if(file->private_data == NULL)
++                return -ENOMEM;
++        MOD_INC_USE_COUNT;
++        return 0;
++}
++
++static int 
++ctcmpc_ctrl_close(struct inode *inode, struct file *file)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        kfree(file->private_data);
++        MOD_DEC_USE_COUNT;
++        return 0;
++}
++
++static ssize_t 
++ctcmpc_ctrl_write(struct file *file, const char *buf, size_t count,
++                  loff_t *off)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
++        net_device   *dev;
++        ctc_priv     *privptr;
++        char         *e;
++        int          bs1;
++        char         tmp[40];
++
++        return 0;
++        /* This function is currently disabled in this MPC environment */
++        if(!(dev = find_netdev_by_ino(ino)))
++                return -ENODEV;
++        if(off != &file->f_pos)
++                return -ESPIPE;
++
++        privptr = (ctc_priv *)dev->priv;
++
++        if(count >= 39)
++                return -EINVAL;
++
++        if(copy_from_user(tmp, buf, count))
++                return -EFAULT;
++        tmp[count+1] = '\0';
++        bs1 = simple_strtoul(tmp, &e, 0);
++
++        if((bs1 > CTC_BUFSIZE_LIMIT) ||
++           (e && (!isspace(*e))))
++                return -EINVAL;
++        if((dev->flags & IFF_RUNNING) &&
++           (bs1 < (dev->mtu + TH_HEADER_LENGTH )))
++                return -EINVAL;
++        if(bs1 < (576 + TH_HEADER_LENGTH ))
++                return -EINVAL;
++
++
++        privptr->channel[READ]->max_bufsize =
++        privptr->channel[WRITE]->max_bufsize = bs1;
++        if(!(dev->flags & IFF_RUNNING))
++                dev->mtu = bs1 - TH_HEADER_LENGTH ;
++        privptr->channel[READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
++        privptr->channel[WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
++
++        return count;
++}
++
++static ssize_t 
++ctcmpc_ctrl_read(struct file *file, char *buf, size_t count,
++                 loff_t *off)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
++        char *sbuf = (char *)file->private_data;
++        net_device *dev;
++        ctc_priv *privptr;
++        ssize_t ret = 0;
++        char *p = sbuf;
++        int l;
++
++        if(!(dev = find_netdev_by_ino(ino)))
++                return -ENODEV;
++        if(off != &file->f_pos)
++                return -ESPIPE;
++
++        privptr = (ctc_priv *)dev->priv;
++
++        if(file->f_pos == 0)
++                sprintf(sbuf, "%d\n", privptr->channel[READ]->max_bufsize);
++
++        l = strlen(sbuf);
++        p = sbuf;
++        if(file->f_pos < l)
++        {
++                p += file->f_pos;
++                l = strlen(p);
++                ret = (count > l) ? l : count;
++                if(copy_to_user(buf, p, ret))
++                        return -EFAULT;
++        }
++        file->f_pos += ret;
++        return ret;
++}
++
++#define STATS_BUFSIZE 2048
++
++static int 
++ctcmpc_stat_open(struct inode *inode, struct file *file)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL);
++        if(file->private_data == NULL)
++                return -ENOMEM;
++        MOD_INC_USE_COUNT;
++        return 0;
++}
++
++static int 
++ctcmpc_stat_close(struct inode *inode, struct file *file)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        kfree(file->private_data);
++        MOD_DEC_USE_COUNT;
++        return 0;
++}
++
++static ssize_t 
++ctcmpc_stat_write(struct file *file, const char *buf, size_t count,
++                  loff_t *off)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
++        net_device *dev;
++        ctc_priv *privptr;
++
++        if(!(dev = find_netdev_by_ino(ino)))
++                return -ENODEV;
++        privptr = (ctc_priv *)dev->priv;
++        privptr->channel[WRITE]->prof.maxmulti = 0;
++        privptr->channel[WRITE]->prof.maxcqueue = 0;
++        privptr->channel[WRITE]->prof.doios_single = 0;
++        privptr->channel[WRITE]->prof.doios_multi = 0;
++        privptr->channel[WRITE]->prof.txlen = 0;
++        privptr->channel[WRITE]->prof.tx_time = 0;
++        return count;
++}
++
++static ssize_t 
++ctcmpc_stat_read(struct file *file, char *buf, size_t count,
++                 loff_t *off)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
++        char *sbuf = (char *)file->private_data;
++        net_device *dev;
++        ctc_priv *privptr;
++        ssize_t ret = 0;
++        char *p = sbuf;
++        int l;
++
++        if(!(dev = find_netdev_by_ino(ino)))
++                return -ENODEV;
++        if(off != &file->f_pos)
++                return -ESPIPE;
++
++        privptr = (ctc_priv *)dev->priv;
++
++        if(file->f_pos == 0)
++        {
++                p += sprintf(p, "Device FSM state: %s\n",
++                             fsm_getstate_str(privptr->fsm));
++                p += sprintf(p, "RX channel FSM state: %s\n",
++                             fsm_getstate_str(privptr->channel[READ]->fsm));
++                p += sprintf(p, "TX channel FSM state: %s\n",
++                             fsm_getstate_str(privptr->channel[WRITE]->fsm));
++                p += sprintf(p, "Max. TX buffer used: %ld\n",
++                             privptr->channel[WRITE]->prof.maxmulti);
++                p += sprintf(p, "Max. chained SKBs: %ld\n",
++                             privptr->channel[WRITE]->prof.maxcqueue);
++                p += sprintf(p, "TX single write ops: %ld\n",
++                             privptr->channel[WRITE]->prof.doios_single);
++                p += sprintf(p, "TX multi write ops: %ld\n",
++                             privptr->channel[WRITE]->prof.doios_multi);
++                p += sprintf(p, "Netto bytes written: %ld\n",
++                             privptr->channel[WRITE]->prof.txlen);
++                p += sprintf(p, "Max. TX IO-time: %ld\n",
++                             privptr->channel[WRITE]->prof.tx_time);
++        }
++        l = strlen(sbuf);
++        p = sbuf;
++        if(file->f_pos < l)
++        {
++                p += file->f_pos;
++                l = strlen(p);
++                ret = (count > l) ? l : count;
++                if(copy_to_user(buf, p, ret))
++                        return -EFAULT;
++        }
++        file->f_pos += ret;
++        return ret;
++}
++
++static struct file_operations ctcmpc_stat_fops = {
++        read:    ctcmpc_stat_read,
++        write:   ctcmpc_stat_write,
++        open:    ctcmpc_stat_open,
++        release: ctcmpc_stat_close,
++};
++
++static struct file_operations ctcmpc_ctrl_fops = {
++        read:    ctcmpc_ctrl_read,
++        write:   ctcmpc_ctrl_write,
++        open:    ctcmpc_ctrl_open,
++        release: ctcmpc_ctrl_close,
++};
++
++static struct inode_operations ctcmpc_stat_iops = {
++};
++static struct inode_operations ctcmpc_ctrl_iops = {
++};
++
++static struct proc_dir_entry stat_entry = {
++        0,                           /* low_ino */
++        10,                          /* namelen */
++        "statistics",                /* name    */
++        S_IFREG | S_IRUGO | S_IWUSR, /* mode    */
++        1,                           /* nlink   */
++        0,                           /* uid     */
++        0,                           /* gid     */
++        0,                           /* size    */
++        &ctcmpc_stat_iops               /* ops     */
++};
++
++static struct proc_dir_entry ctrl_entry = {
++        0,                           /* low_ino */
++        10,                          /* namelen */
++        "buffersize",                /* name    */
++        S_IFREG | S_IRUSR | S_IWUSR, /* mode    */
++        1,                           /* nlink   */
++        0,                           /* uid     */
++        0,                           /* gid     */
++        0,                           /* size    */
++        &ctcmpc_ctrl_iops               /* ops     */
++};
++
++static struct proc_dir_entry *ctcmpc_dir = NULL;
++static struct proc_dir_entry *ctcmpc_template = NULL;
++
++/**
++ * Create the driver's main directory /proc/net/ctc
++ */
++static void 
++ctcmpc_proc_create_main(void)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        /**
++         * If not registered, register main proc dir-entry now
++         */
++        if(!ctcmpc_dir)
++                ctcmpc_dir = proc_mkdir("mpc", proc_net);
++
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc exit:%s() ctcmpc_dir:%lx\n", 
++               __FUNCTION__,(unsigned long)ctcmpc_dir);
++#endif
++
++}
++
++#ifdef MODULE
++/**
++ * Destroy /proc/net/ctc
++ */
++static void 
++ctcmpc_proc_destroy_main(void)
++{
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++        #endif
++
++        if(ctcmpc_dir)
++                remove_proc_entry("mpc", proc_net);
++}
++#endif /*MODULE*/
++
++/**
++ * Create a device specific subdirectory in /proc/net/ctc/ with the
++ * same name like the device. In that directory, create 2 entries
++ * "statistics" and "buffersize".
++ *
++ * @param dev The device for which the subdirectory should be created.
++ *
++ */
++static void 
++ctcmpc_proc_create_sub(net_device *dev)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        ctc_priv *privptr = (ctc_priv *)dev->priv;
++
++        privptr->proc_dentry = proc_mkdir(dev->name, ctcmpc_dir);
++        privptr->proc_stat_entry =
++        create_proc_entry("statistics",
++                          S_IFREG | S_IRUSR | S_IWUSR,
++                          privptr->proc_dentry);
++        privptr->proc_stat_entry->proc_fops = &ctcmpc_stat_fops;
++        privptr->proc_stat_entry->proc_iops = &ctcmpc_stat_iops;
++        privptr->proc_ctrl_entry =
++        create_proc_entry("buffersize",
++                          S_IFREG | S_IRUSR | S_IWUSR,
++                          privptr->proc_dentry);
++        privptr->proc_ctrl_entry->proc_fops = &ctcmpc_ctrl_fops;
++        privptr->proc_ctrl_entry->proc_iops = &ctcmpc_ctrl_iops;
++        privptr->proc_registered = 1;
++}
++
++
++/**
++ * Destroy a device specific subdirectory.
++ *
++ * @param privptr Pointer to device private data.
++ */
++static void 
++ctcmpc_proc_destroy_sub(ctc_priv *privptr)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        if(!privptr->proc_registered)
++                return;
++        remove_proc_entry("statistics", privptr->proc_dentry);
++        remove_proc_entry("buffersize", privptr->proc_dentry);
++        remove_proc_entry(privptr->proc_dentry->name, ctcmpc_dir);
++        privptr->proc_registered = 0;
++}
++
++
++
++#ifndef CTC_CHANDEV
++/**
++ * Setup related routines
++ *****************************************************************************/
++
++/**
++ * Parse a portion of the setup string describing a single device or option
++ * providing the following syntax:
++ *
++ * [Device/OptionName[:int1][:int2][:int3]]
++ *
++ *
++ * @param setup    Pointer to a pointer to the remainder of the parameter
++ *                 string to be parsed. On return, the content of this
++ *                 pointer is updated to point to the first character after
++ *                 the parsed portion (e.g. possible start of next portion)
++ *                 NOTE: The string pointed to must be writeable, since a
++ *                 \0 is written for termination of the device/option name.
++ *
++ * @param dev_name Pointer to a pointer to the name of the device whose
++ *                 parameters are parsed. On return, this is set to the
++ *                 name of the device/option.
++ *
++ * @param ints     Pointer to an array of integer parameters. On return,
++ *                 element 0 is set to the number of parameters found.
++ *
++ * @param maxip    Maximum number of ints to parse.
++ *                 (ints[] must have size maxip+1)
++ *
++ * @return     0 if string "setup" was empty, !=0 otherwise
++ */
++static int 
++parse_opts(char **setup, char **dev_name, int *ints, int maxip)
++{
++        char *cur = *setup;
++        int i = 1;
++        int rc = 0;
++        int in_name = 1;
++        int noauto = 0;
++
++        #ifdef DEBUG
++        printk(KERN_INFO
++               "ctcmpc: parse_opts(): *setup='%s', maxip=%d\n", *setup, maxip);
++        #endif
++        if(*setup)
++        {
++                *dev_name = *setup;
++
++                if(strncmp(cur, "noauto", 6) && strncmp(cur, "mpc", 3))
++                {
++                        if((*setup = strchr(cur, ':')))
++                                *(*setup)++ = '\0';
++                        printk(KERN_INFO
++                               "ctcmpc: Invalid device name or option '%s'\n",
++                               cur);
++                        return 1;
++                }
++                switch(*cur)
++                {
++                        case 'c':
++                                cur += 3;
++                                break;
++                        case 'm':
++                                cur += 3;
++                                break;
++                        case 'e':
++                                cur += 5;
++                                break;
++                        case 'n':
++                                cur += 6;
++                                *cur++ = '\0';
++                                noauto = 1;
++                }
++                if(!noauto)
++                {
++                        while(cur &&
++                              (*cur == '-' || isdigit(*cur)) &&
++                              i <= maxip)
++                        {
++                                if(in_name)
++                                {
++                                        cur++;
++                                        if(*cur == ':')
++                                        {
++                                                *cur++ = '\0';
++                                                in_name = 0;
++                                        }
++                                } else
++                                {
++                                        ints[i++] =
++                                        simple_strtoul(cur, NULL, 0);
++        #ifdef DEBUG
++                                        printk(KERN_INFO
++                                               "ctcmpc: %s: ints[%d]=%d\n",
++                                               __FUNCTION__,
++                                               i-1, ints[i-1]);
++        #endif
++                                        if((cur = strchr(cur, ':')) != NULL)
++                                                cur++;
++                                }
++                        }
++                }
++                ints[0] = i - 1;
++                *setup = cur;
++                if(cur && (*cur == ':'))
++                        (*setup)++;
++                rc = 1;
++        }
++        return rc;
++}
++
++/**
++ *
++ * Allocate one param struct
++ *
++ * If the driver is loaded as a module this functions is called during
++ *   module set up and we can allocate the struct by using kmalloc()
++ *
++ * If the driver is statically linked into the kernel this function is called
++ * when kmalloc() is not yet available so we must allocate from a static array
++ *
++ */
++        #ifdef MODULE
++                #define alloc_param() ((param *)kmalloc(sizeof(param), 
++                GFP_KERNEL));
++        #else
++static param parms_array[MAX_STATIC_DEVICES];
++static param *next_param = parms_array;
++                #define alloc_param() \
++        ((next_param<parms_array+MAX_STATIC_DEVICES)?next_param++:NULL)
++        #endif /*MODULE*/
++
++/**
++ * Returns commandline parameter using device name as key.
++ *
++ * @param name Name of interface to get parameters from.
++ *
++ * @return Pointer to corresponting param struct, NULL if not found.
++ */
++static param 
++*find_param(char *name)
++{
++        param *p = params;
++
++        while(p && strcmp(p->name, name))
++                p = p->next;
++        return p;
++}
++
++/**
++ * maximum number of integer parametes that may be specified
++ * for one device in the setup string
++ */
++        #define CTC_MAX_INTPARMS 3
++
++/**
++ * Parse configuration options for all interfaces.
++ *
++ * This function is called from two possible locations:
++ *  - If built as module, this function is called from init_module().
++ *  - If built in monolithic kernel, this function is called from within
++ *    init/main.c.
++ * Parsing is always done here.
++ *
++ * Valid parameters are:
++ *
++ *
++ *   [NAME[:0xRRRR[:0xWWWW[:P]]]]
++ *
++ *     where P       is the channel protocol (always 0)
++ *	      0xRRRR is the cu number for the read channel
++ *	      0xWWWW is the cu number for the write channel
++ *	      NAME   is either mpc0 ... mpcN  for sna MPC
++ *                 or     noauto
++ *                 which switches off auto-detection of channels.
++ *
++ * @param setup    The parameter string to parse. MUST be writeable!
++ * @param ints     Pointer to an array of ints. 
++ */
++        #ifdef MODULE
++static void ctcmpc_setup(char *setup)
++                #define ctcmpc_setup_return return
++        #else /*MODULE*/
++static int __init ctcmpc_setup(char *setup)
++                #define ctcmpc_setup_return return(1)
++        #endif /*MODULE*/
++{
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++        #endif
++
++        int write_dev;
++        int read_dev;
++        int proto;
++        param *par;
++        char *dev_name;
++        int ints[CTC_MAX_INTPARMS+1];
++
++        while(parse_opts(&setup, &dev_name, ints, CTC_MAX_INTPARMS))
++        {
++                write_dev = -1;
++                read_dev = -1;
++                proto = CTC_PROTO_MPC;
++        #ifdef DEBUG
++                printk(KERN_INFO
++                       "ctcmpc: ctcmpc_setup(): setup='%s' dev_name='%s',"
++                       " ints[0]=%d)\n",
++                       setup, dev_name, ints[0]);
++        #endif /*DEBUG*/
++                if(dev_name == NULL)
++                {
++                        /**
++                         * happens if device name is not specified in
++                         * parameter line (cf. init/main.c:get_options()
++                         */
++                        printk(KERN_INFO
++                               "ctcmpc: %s(): Device name not specified\n",
++                               __FUNCTION__);
++                        ctcmpc_setup_return;
++                }
++
++        #ifdef DEBUG
++                printk(KERN_INFO "name=´%s´ argc=%d\n", dev_name, ints[0]);
++        #endif
++
++                if(strcmp(dev_name, "noauto") == 0)
++                {
++                        printk(KERN_INFO "ctcmpc: autoprobing disabled\n");
++                        ctc_no_auto = 1;
++                        continue;
++                }
++
++                if(find_param(dev_name) != NULL)
++                {
++                        printk(KERN_INFO
++                               "ctcmpc: Definition for device %s already set. "
++                               "Ignoring second definition\n", dev_name);
++                        continue;
++                }
++
++                switch(ints[0])
++                {
++                        case 3: /* protocol type passed */
++                                proto = ints[3];
++                                if(proto != CTC_PROTO_MPC)
++                                {
++                                        printk(KERN_INFO
++                                               "%s: wrong protocol type "
++                                               "passed\n", dev_name);
++                                        ctcmpc_setup_return;
++                                }
++                        case 2: /* write channel passed */
++                                write_dev = ints[2];
++                        case 1: /* read channel passed */
++                                read_dev = ints[1];
++                                if(write_dev == -1)
++                                        write_dev = read_dev + 1;
++                                break;
++                        default:
++                                printk(KERN_INFO
++                                       "ctcmpc: wrong number of parameter "
++                                       "passed (is: %d, expected: [1..3]\n",
++                                       ints[0]);
++                                ctcmpc_setup_return;
++                }
++                par = alloc_param();
++                if(!par)
++                {
++        #ifdef MODULE
++                        printk(KERN_INFO
++                               "ctcmpc: Couldn't allocate setup param block\n");
++        #else
++                        printk(KERN_INFO
++                               "ctcmpc: Number of device definitions in "
++                               " kernel commandline exceeds builtin limit "
++                               " of %d devices.\n", MAX_STATIC_DEVICES);
++        #endif
++                        ctcmpc_setup_return;
++                }
++                par->read_dev = read_dev;
++                par->write_dev = write_dev;
++                par->proto = proto;
++                strncpy(par->name, dev_name, MAX_PARAM_NAME_LEN);
++                par->next = params;
++                params = par;
++        #ifdef DEBUG
++                printk(KERN_INFO "%s: protocol=%x read=%04x write=%04x\n",
++                       dev_name, proto, read_dev, write_dev);
++        #endif
++        }
++        ctcmpc_setup_return;
++}
++
++__setup("mpc=", ctcmpc_setup);
++#endif /* !CTC_CHANDEV */
++
++
++static void
++ctcmpc_netdev_unregister(net_device *dev)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        ctc_priv *privptr;
++
++        if(!dev)
++                return;
++        privptr = (ctc_priv *)dev->priv;
++        unregister_netdev(dev);
++}
++
++static int
++ctcmpc_netdev_register(net_device *dev)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        return register_netdev(dev);
++}
++
++static void
++ctcmpc_free_netdevice(net_device *dev, int free_dev)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        ctc_priv *privptr;
++        mpc_group *grpptr;
++
++        if(!dev)
++                return;
++        privptr = (ctc_priv *)dev->priv;
++        if(privptr)
++        {
++                grpptr = privptr->mpcg;
++                if(privptr->fsm)
++                        kfree_fsm(privptr->fsm);
++                ctcmpc_proc_destroy_sub(privptr);
++                if(grpptr)
++                {
++                        if(grpptr->fsm)
++                                kfree_fsm(grpptr->fsm);
++                        if(grpptr->xid_skb)
++                                dev_kfree_skb(grpptr->xid_skb);
++                        if(grpptr->rcvd_xid_skb)
++                                dev_kfree_skb(grpptr->rcvd_xid_skb);
++                        tasklet_kill(&grpptr->mpc_tasklet2);
++                        kfree(grpptr);
++                }
++                kfree(privptr);
++        }
++#ifdef MODULE
++        if(free_dev)
++                kfree(dev);
++#endif
++}
++
++#ifdef CTC_CHANDEV
++static int
++ctcmpc_shutdown(net_device *dev)
++{
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++        #endif
++
++        ctc_priv *privptr;
++
++        if(!dev)
++                return 0;
++        privptr = (ctc_priv *)dev->priv;
++        channel_remove(privptr->channel[READ]);
++        channel_remove(privptr->channel[WRITE]);
++        ctcmpc_free_netdevice(dev, 0);
++        return 0;
++}
++#endif
++
++/**
++ * Initialize everything of the net device except the name and the
++ * channel structs.
++ */
++static net_device *
++ctcmpc_init_netdevice(net_device *dev, int alloc_device)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++        ctc_priv *privptr;
++        mpc_group *grpptr;
++        int      priv_size;
++        if(alloc_device)
++        {
++                dev = kmalloc(sizeof(net_device)
++                              , GFP_KERNEL);
++                if(!dev)
++                        return NULL;
++#ifdef DEBUG
++                printk(KERN_INFO "kmalloc dev:  %s()\n", __FUNCTION__);
++#endif
++
++                memset(dev, 0, sizeof(net_device));
++        }
++        priv_size = sizeof(ctc_priv) + sizeof(ctcmpc_template) +
++                    sizeof(stat_entry) + sizeof(ctrl_entry);
++        dev->priv = kmalloc(priv_size, GFP_KERNEL);
++        if(dev->priv == NULL)
++        {
++                if(alloc_device)
++                        kfree(dev);
++                return NULL;
++        }
++
++        memset(dev->priv, 0, priv_size);
++        privptr = (ctc_priv *)dev->priv;
++
++        privptr->proc_dentry = (struct proc_dir_entry *)
++                               (((char *)privptr) + sizeof(ctc_priv));
++        privptr->proc_stat_entry = (struct proc_dir_entry *)
++                                   (((char *)privptr) + sizeof(ctc_priv) +
++                                    sizeof(ctcmpc_template));
++        privptr->proc_ctrl_entry = (struct proc_dir_entry *)
++                                   (((char *)privptr) + sizeof(ctc_priv) +
++                                    sizeof(ctcmpc_template) + 
++                                    sizeof(stat_entry));
++        memcpy(privptr->proc_dentry, &ctcmpc_template, sizeof(ctcmpc_template));
++        memcpy(privptr->proc_stat_entry, &stat_entry, sizeof(stat_entry));
++        memcpy(privptr->proc_ctrl_entry, &ctrl_entry, sizeof(ctrl_entry));
++
++        privptr->fsm = init_fsm("ctcdev", dev_state_names,
++                                dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
++                                dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
++        if(privptr->fsm == NULL)
++        {
++                kfree(privptr);
++                privptr = NULL;
++                if(alloc_device)
++                        kfree(dev);
++                return NULL;
++        }
++
++        fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
++        fsm_settimer(privptr->fsm, &privptr->restart_timer);
++        /********************************************************/
++        /*  MPC Group Initializations                           */
++        /********************************************************/
++        privptr->mpcg = kmalloc(sizeof(mpc_group),GFP_KERNEL);
++        if(privptr->mpcg == NULL)
++        {
++                kfree(privptr);
++                privptr = NULL;
++                if(alloc_device)
++                        kfree(dev);
++                return NULL;
++        }
++        grpptr = privptr->mpcg;
++        memset(grpptr, 0, sizeof(mpc_group));
++
++        grpptr->fsm = init_fsm("mpcg", mpcg_state_names,
++                               mpcg_event_names, 
++                               NR_MPCG_STATES, 
++                               NR_MPCG_EVENTS,
++                               mpcg_fsm, 
++                               MPCG_FSM_LEN, 
++                               GFP_KERNEL);
++        if(grpptr->fsm == NULL)
++        {
++                kfree(grpptr);
++                grpptr = NULL;
++                kfree(privptr);
++                privptr = NULL;
++                if(alloc_device)
++                        kfree(dev);
++                return NULL;
++        }
++
++        fsm_newstate(grpptr->fsm, MPCG_STATE_RESET);
++        fsm_settimer(grpptr->fsm,&grpptr->timer);
++
++
++        grpptr->xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
++                                          GFP_ATOMIC|GFP_DMA);
++        if(grpptr->xid_skb == NULL)
++        {
++                printk(KERN_INFO
++                       "Couldn't alloc MPCgroup xid_skb\n");
++                kfree_fsm(grpptr->fsm);
++                grpptr->fsm = NULL;
++                kfree(grpptr);
++                grpptr = NULL;
++                kfree(privptr);
++                privptr = NULL;
++                if(alloc_device)
++                        kfree(dev);
++                return NULL;
++        }
++        /*  base xid for all channels in group  */
++        grpptr->xid_skb_data = grpptr->xid_skb->data;
++        grpptr->xid_th = (th_header *)grpptr->xid_skb->data;
++        memcpy(skb_put(grpptr->xid_skb,
++                       TH_HEADER_LENGTH),
++               &thnorm,
++               TH_HEADER_LENGTH);
++
++        privptr->xid = grpptr->xid = (xid2 *)grpptr->xid_skb->tail;
++        memcpy(skb_put(grpptr->xid_skb,XID2_LENGTH),&init_xid,XID2_LENGTH);
++        privptr->xid->xid2_adj_id = jiffies | 0xfff00000;
++        privptr->xid->xid2_sender_id = jiffies;
++
++        grpptr->xid_id = (char *)grpptr->xid_skb->tail;
++        memcpy(skb_put(grpptr->xid_skb,4),"VTAM",4);
++
++
++        grpptr->rcvd_xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
++                                               GFP_ATOMIC|GFP_DMA);
++        if(grpptr->rcvd_xid_skb == NULL)
++        {
++                printk(KERN_INFO
++                       "Couldn't alloc MPCgroup rcvd_xid_skb\n");
++                kfree_fsm(grpptr->fsm);
++                grpptr->fsm = NULL;
++                dev_kfree_skb(grpptr->xid_skb);
++                grpptr->xid_skb = NULL;
++                grpptr->xid_id = NULL;
++                grpptr->xid_skb_data = NULL;
++                grpptr->xid_th = NULL;
++                kfree(grpptr);
++                grpptr = NULL;
++                privptr->xid = NULL;
++                kfree(privptr);
++                privptr = NULL;
++                if(alloc_device)
++                        kfree(dev);
++                return NULL;
++        }
++
++        grpptr->rcvd_xid_data =  grpptr->rcvd_xid_skb->data;
++        grpptr->rcvd_xid_th =   (th_header *)grpptr->rcvd_xid_skb->data;
++        memcpy(skb_put(grpptr->rcvd_xid_skb,TH_HEADER_LENGTH),
++               &thnorm,
++               TH_HEADER_LENGTH);
++        grpptr->saved_xid2 = NULL;
++
++        tasklet_init(&grpptr->mpc_tasklet2,
++                     ctcmpc_group_ready,
++                     (unsigned long)dev);
++        /********************************************************/
++        /*  MPC Group Initializations							 */
++        /********************************************************/
++
++
++        dev->mtu                 = 
++                MPC_BUFSIZE_DEFAULT - 
++                TH_HEADER_LENGTH - 
++                PDU_HEADER_LENGTH;
++        dev->hard_start_xmit     = ctcmpc_tx;
++        dev->open                = ctcmpc_open;
++        dev->stop                = ctcmpc_close;
++        dev->get_stats           = ctcmpc_stats;
++        dev->change_mtu          = ctcmpc_change_mtu;
++        dev->hard_header_len     = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
++        dev->addr_len            = 0;
++        dev->type                = ARPHRD_SLIP;
++        dev->tx_queue_len        = 100;
++        SET_DEVICE_START(dev, 1);
++        dev_init_buffers(dev);
++        dev->flags               = IFF_POINTOPOINT | IFF_NOARP;
++        return dev;
++}
++
++#ifdef CTC_CHANDEV
++static void
++ctcmpc_chandev_msck_notify(void *dev, int msck_irq,
++                           chandev_msck_status prevstatus,
++                           chandev_msck_status newstatus)
++{
++
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++        #endif
++
++        net_device *device = (net_device *)dev;
++        ctc_priv *privptr;
++        int direction;
++
++        if(!dev)
++                return;
++
++        privptr = device->priv;
++        if(prevstatus == chandev_status_revalidate)
++                for(direction = READ; direction <= WRITE; direction++)
++                {
++                        channel *ch = privptr->channel[direction];
++                        if(ch->irq == msck_irq)
++                        {
++                                s390_dev_info_t devinfo;
++
++                                if(get_dev_info_by_irq(ch->irq, &devinfo))
++                                        ch->devno = devinfo.devno;
++                                else
++                                        printk(KERN_INFO
++                                               "ctcmpc_chandev_msck_notify: "
++                                               "get_dev_info_by_irq failed for "
++                                               "irq %d\n", ch->irq);
++                        }
++                }
++        switch(newstatus)
++        {
++                case chandev_status_not_oper:
++                case chandev_status_no_path:
++                case chandev_status_gone:
++                        for(direction = READ; direction <= WRITE; direction++)
++                        {
++                                channel *ch = privptr->channel[direction];
++                                fsm_event(ch->fsm, CH_EVENT_MC_FAIL, ch);
++                        }
++                        printk(KERN_INFO
++                               "ctcmpc: %s channel deactivated\n", 
++                               device->name);
++                        break;
++                case chandev_status_all_chans_good:
++                        for(direction = READ; direction <= WRITE; direction++)
++                        {
++                                channel *ch = privptr->channel[direction];
++                                fsm_event(ch->fsm, CH_EVENT_MC_GOOD, ch);
++                        }
++                        printk(KERN_INFO
++                               "ctcmpc: %s channel activated\n", device->name);
++                        break;
++                default:
++                        break;
++        }
++}
++
++/**
++ *
++ * Setup an interface.
++ *
++ * Like ctcmpc_setup(),ctcmpc_probe()can be called from two different locations:
++ *  - If built as module, it is called from within init_module().
++ *  - If built in monolithic kernel, it is called from within generic network
++ *    layer during initialization for every corresponding device, declared in
++ *    drivers/net/Space.c
++ *
++ * @param dev Pointer to net_device to be initialized.
++ *
++ * @returns 0 on success, !0 on failure.
++ */
++static int 
++ctcmpc_chandev_probe(chandev_probeinfo *info)
++{
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++        #endif
++
++        int               devno[2];
++        __u16             proto;
++        int               rc;
++        int               direction;
++        channel_type_t    type;
++        ctc_priv          *privptr;
++        net_device        *dev;
++
++        ctcmpc_proc_create_main();
++
++
++        switch(info->chan_type)
++        {
++                case chandev_type_ctcmpc:
++                        type = channel_type_mpc;
++                        break;
++                case chandev_type_escon:
++                        type = channel_type_escon;
++                        break;
++                default:
++                        printk(KERN_INFO "ctcmpc_chandev_probe called with "
++                               "unsupported channel type %d\n", 
++                               info->chan_type);
++                        return -ENODEV;
++        }
++        devno[READ]  = info->read.devno;
++        devno[WRITE] = info->write.devno;
++        proto        = info->port_protocol_no;
++
++        if(ctcmpc_add_channel(info->read.irq, info->read.devno, type))
++                return -ENOMEM;
++        if(ctcmpc_add_channel(info->write.irq, info->write.devno, type))
++                return -ENOMEM;
++
++        dev = ctcmpc_init_netdevice(NULL, 1);
++
++
++        if(!dev)
++        {
++                printk(KERN_INFO "ctcmpc_init_netdevice failed\n");
++                return -ENODEV;
++        }
++
++        chandev_build_device_name(info, dev->name, "mpc", 1);
++
++        privptr = (ctc_priv *)dev->priv;
++        privptr->protocol = proto;
++        privptr->xid = (xid2 *)kmalloc(sizeof(xid2), GFP_KERNEL);
++        if(privptr->xid == NULL)
++        {
++                printk(KERN_INFO "ctcmpc: Out of memory in chandev_probe\n");
++                return -1;
++        }
++        for(direction = READ; direction <= WRITE; direction++)
++        {
++                privptr->channel[direction] =
++                channel_get(type, devno[direction], direction);
++                if(privptr->channel[direction] == NULL)
++                {
++                        if(direction == WRITE)
++                        {
++                                FREE_IRQ(privptr->channel[READ]->irq,
++                                         privptr->channel[READ]->devstat);
++                                channel_free(privptr->channel[READ]);
++                        }
++                        ctcmpc_free_netdevice(dev, 1);
++                        return -ENODEV;
++                }
++                privptr->channel[direction]->netdev = dev;
++                privptr->channel[direction]->protocol = proto;
++                /*todo...who put this 35 here....make it a define*/
++                privptr->channel[direction]->max_bufsize = 
++                        (MPC_BUFSIZE_DEFAULT - 35);
++                rc = REQUEST_IRQ(privptr->channel[direction]->irq,
++                                 (void *)ctcmpc_irq_handler, SA_INTERRUPT,
++                                 dev->name,
++                                 privptr->channel[direction]->devstat);
++                if(rc)
++                {
++                        printk(KERN_INFO
++                               "%s: requested irq %d is busy rc=%02x\n",
++                               dev->name, privptr->channel[direction]->irq,
++                               rc);
++                        if(direction == WRITE)
++                        {
++                                FREE_IRQ(privptr->channel[READ]->irq,
++                                         privptr->channel[READ]->devstat);
++                                channel_free(privptr->channel[READ]);
++                        }
++                        channel_free(privptr->channel[direction]);
++                        ctcmpc_free_netdevice(dev, 1);
++                        return -EBUSY;
++                }
++        }
++        if(ctcmpc_netdev_register(dev) != 0)
++        {
++                ctcmpc_free_netdevice(dev, 1);
++                return -ENODEV;
++        }
++
++        /**
++         * register subdir in /proc/net/mpc
++         */
++        ctcmpc_proc_create_sub(dev);
++        strncpy(privptr->fsm->name, dev->name, sizeof(privptr->fsm->name));
++        activated++;
++
++        print_banner();
++
++        printk(KERN_INFO
++               "%s: read: ch %04x (irq %04x), "
++               "write: ch %04x (irq %04x) proto: %d\n",
++               dev->name, privptr->channel[READ]->devno,
++               privptr->channel[READ]->irq, privptr->channel[WRITE]->devno,
++               privptr->channel[WRITE]->irq, proto);
++
++        chandev_initdevice(info, dev, 0, dev->name,
++                           (proto == CTC_PROTO_MPC)
++                           ? chandev_category_serial_device :
++                           chandev_category_network_device,
++                           (chandev_unregfunc)ctcmpc_netdev_unregister);
++        return 0;
++}
++#else /* ! CHANDEV */
++/**
++ *
++ * Setup an interface.
++ *
++ * Like ctcmpc_setup(),ctcmpc_probe()can be called from two different locations:
++ *  - If built as module, it is called from within init_module().
++ *  - If built in monolithic kernel, it is called from within generic network
++ *    layer during initialization for every corresponding device, declared in
++ *    drivers/net/Space.c
++ *
++ * @param dev Pointer to net_device to be initialized.
++ *
++ * @returns 0 on success, !0 on failure.
++ */
++int 
++ctcmpc_probe(net_device *dev)
++{
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++        #endif
++
++        int            devno[2];
++        __u16          proto;
++        int            rc;
++        int            direction;
++        channel_type_t type;
++        ctc_priv       *privptr;
++        param          *par;
++
++        ctcmpc_proc_create_main();
++
++        /**
++         * Scan for available channels only the first time,
++         * ctcmpc_probe gets control.
++         */
++        if(channels == NULL)
++                channel_scan();
++
++        type = extract_channel_media(dev->name);
++        if(type == channel_type_unknown)
++                return -ENODEV;
++
++        par = find_param(dev->name);
++        if(par)
++        {
++                devno[READ] = par->read_dev;
++                devno[WRITE] = par->write_dev;
++                proto = par->proto;
++        } else
++        {
++                if(ctc_no_auto)
++                        return -ENODEV;
++                else
++                {
++                        devno[READ] = -1;
++                        devno[WRITE] = -1;
++                        proto = CTC_PROTO_MPC;    
++                }
++        }
++
++        #ifndef MODULE
++        if(ctcmpc_init_netdevice(dev, 0) == NULL)
++                return -ENODEV;
++        #endif
++        privptr = (ctc_priv *)dev->priv;
++        privptr->protocol = proto;
++
++        for(direction = READ; direction <= WRITE; direction++)
++        {
++                if((ctc_no_auto == 0) || (devno[direction] == -1))
++                        privptr->channel[direction] =
++                        channel_get_next(type, direction);
++                else
++                        privptr->channel[direction] =
++                        channel_get(type, devno[direction], direction);
++                if(privptr->channel[direction] == NULL)
++                {
++                        if(direction == WRITE)
++                        {
++                                FREE_IRQ(privptr->channel[READ]->irq,
++                                         privptr->channel[READ]->devstat);
++                                channel_free(privptr->channel[READ]);
++                        }
++                        ctcmpc_free_netdevice(dev, 1);
++                        return -ENODEV;
++                }
++                privptr->channel[direction]->netdev = dev;
++                privptr->channel[direction]->protocol = proto;
++                privptr->channel[direction]->max_bufsize = 
++                        (MPC_BUFSIZE_DEFAULT - 35);
++                rc = REQUEST_IRQ(privptr->channel[direction]->irq,
++                                 (void *)ctcmpc_irq_handler, SA_INTERRUPT,
++                                 dev->name,
++                                 privptr->channel[direction]->devstat);
++                if(rc)
++                {
++                        printk(KERN_INFO
++                               "%s: requested irq %d is busy rc=%02x\n",
++                               dev->name, privptr->channel[direction]->irq,
++                               rc);
++                        if(direction == WRITE)
++                        {
++                                FREE_IRQ(privptr->channel[READ]->irq,
++                                         privptr->channel[READ]->devstat);
++                                channel_free(privptr->channel[READ]);
++                        }
++                        channel_free(privptr->channel[direction]);
++                        ctcmpc_free_netdevice(dev, 1);
++                        return -EBUSY;
++                }
++        }
++
++        /**
++         * register subdir in /proc/net/ctc
++         */
++        ctcmpc_proc_create_sub(dev);
++
++        print_banner();
++
++        printk(KERN_INFO
++               "%s: read: ch %04x (irq %04x), "
++               "write: ch %04x (irq %04x) proto: %d\n",
++               dev->name, privptr->channel[READ]->devno,
++               privptr->channel[READ]->irq, privptr->channel[WRITE]->devno,
++               privptr->channel[WRITE]->irq, proto);
++
++        return 0;
++}
++#endif
++
++/**
++ * Module related routines
++ *****************************************************************************/
++
++#ifdef MODULE
++/**
++ * Prepare to be unloaded. Free IRQ's and release all resources.
++ * This is called just before this module is unloaded. It is
++ * <em>not</em> called, if the usage count is !0, so we don't need to check
++ * for that.
++ */
++void 
++cleanup_module(void)
++{
++        #ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++        #endif
++
++
++        /* we are called if all interfaces are down only, so no need
++         * to bother around with locking stuff
++         */
++        #ifndef CTC_CHANDEV
++        while(channels)
++        {
++                if((channels->flags & CHANNEL_FLAGS_INUSE) &&
++                   (channels->netdev != NULL))
++                {
++                        net_device *dev = channels->netdev;
++                        ctc_priv *privptr = (ctc_priv *)dev->priv;
++
++                        if(privptr)
++                        {
++                                privptr->channel[READ]->netdev = NULL;
++                                privptr->channel[WRITE]->netdev = NULL;
++                        }
++                        channels->netdev = NULL;
++                        ctcmpc_netdev_unregister(dev);
++                        ctcmpc_free_netdevice(dev, 1);
++                }
++                channel_remove(channels);
++        }
++        channels = NULL;
++        #endif
++
++        ctcmpc_proc_destroy_main();
++        #ifdef CTC_CHANDEV
++        chandev_unregister(ctcmpc_chandev_probe, 1);
++        #endif
++        printk(KERN_INFO "MPC driver unloaded\n");
++}
++
++        #define ctcmpc_init init_module
++#endif /*MODULE*/
++
++/**
++ * Initialize module.
++ * This is called just after the module is loaded.
++ *
++ * @return 0 on success, !0 on error.
++ */
++int 
++ctcmpc_init(void)
++{
++#ifdef DEBUG
++        printk(KERN_INFO "ctcmpc enter:  %s()\n", __FUNCTION__);
++#endif
++
++#ifndef CTC_CHANDEV
++        int   cnt;
++        int   activated;
++        param *par;
++#endif
++        int   ret = 0;
++        int   probed = 0;
++
++        print_banner();
++
++#if defined(DEBUG) && !defined(CTC_CHANDEV)
++        printk(KERN_INFO
++               "ctcmpc: init_module(): got string '%s'\n", mpc);
++#endif
++
++#ifndef CTC_CHANDEV
++        #ifdef MODULE
++        ctcmpc_setup(mpc);
++        #endif
++        par = params;
++#endif
++
++        activated = 0;
++
++#ifdef CTC_CHANDEV
++        chandev_register_and_probe(ctcmpc_chandev_probe,
++                                   (chandev_shutdownfunc)ctcmpc_shutdown,
++                                   ctcmpc_chandev_msck_notify,
++                                   chandev_type_ctcmpc|chandev_type_escon);
++#else /* CTC_CHANDEV */
++        net_device *dev = NULL;
++        char       *bname = "mpc";
++
++        cnt = 0;
++        do
++        {
++                dev = ctcmpc_init_netdevice(NULL, 1);
++                if(!dev)
++                {
++                        ret = -ENOMEM;
++                        break;
++                }
++                if(par && par->name)
++                {
++                        char *p;
++                        int  n;
++
++                        sprintf(dev->name, "%s", par->name);
++                        par = par->next;
++                        for(p = dev->name; p && *p; p++)
++                                if(isdigit(*p))
++                                        break;
++                        if(p && *p)
++                        {
++                                n = simple_strtoul(p, NULL, 0);
++                                if(n >= cnt)
++                                        cnt = n + 1;
++                        }
++                } else
++                {
++                        if(ctc_no_auto)
++                        {
++                                ctcmpc_free_netdevice(dev, 1);
++                                dev = NULL;
++                                break;
++                        }
++                        sprintf(dev->name, "%s%d", bname,
++                                cnt++);
++                }
++        #ifdef DEBUG
++                printk(KERN_INFO "ctcmpc: %s(): probing for device %s\n",
++                       __FUNCTION__, dev->name);
++        #endif			
++                probed = 1;
++                if(ctcmpc_probe(dev) == 0)
++                {
++                        ctc_priv *privptr = (ctc_priv *)dev->priv;
++        #ifdef DEBUG
++                        printk(KERN_INFO
++                               "ctcmpc: %s(): probing succeeded\n",
++                               __FUNCTION__);
++                        printk(KERN_INFO
++                               "ctcmpc: %s(): registering device %s\n",
++                               __FUNCTION__, dev->name);
++        #endif
++                        if(ctcmpc_netdev_register(dev) != 0)
++                        {
++                                printk(KERN_INFO
++                                       "ctcmpc: Couldn't register %s\n",
++                                       dev->name);
++                                FREE_IRQ(
++                                        privptr->channel[READ]->irq,
++                                        privptr->channel[READ]->devstat);
++                                FREE_IRQ(
++                                        privptr->channel[WRITE]->irq,
++                                        privptr->channel[WRITE]->devstat);
++                                channel_free(privptr->channel[READ]);
++                                channel_free(privptr->channel[WRITE]);
++                                ctcmpc_free_netdevice(dev, 1);
++                                dev = NULL;
++                        } else
++                        {
++        #ifdef DEBUG
++                                printk(KERN_INFO
++                                       "ctcmpc: %s(): register succeed\n",
++                                       __FUNCTION__);
++        #endif			
++                                activated++;
++                        }
++                } else
++                {
++        #ifdef DEBUG
++                        printk(KERN_INFO
++                               "ctcmpc: %s(): probing failed\n",
++                               __FUNCTION__);
++        #endif			
++                        dev = NULL;
++                }
++        } while(dev && (ret == 0));
++#endif /* CHANDEV */
++#if !defined(CTC_CHANDEV) && defined(MODULE)
++        if(!activated)
++        {
++                printk(KERN_INFO "ctcmpc: No devices registered\n");
++                ret = -ENODEV;
++        }
++#endif
++        if(ret)
++        {
++#if defined(CTC_CHANDEV) && defined(MODULE)
++                chandev_unregister(ctcmpc_chandev_probe, 0);
++#endif
++#ifdef MODULE
++                if(probed)
++                        ctcmpc_proc_destroy_main();
++#endif
++        }
++        return ret;
++}
++
++#ifndef MODULE
++__initcall(ctcmpc_init);
++#endif /* MODULE */
++
++/* --- This is the END my friend --- */
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/ctcmpc.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/ctcmpc.h	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,52 @@
++/*
++ * $Id: ctcmpc.h,v 1.1.2.1 2004/10/04 13:28:55 ptiedem Exp $ 
++ *
++ * CTC / ESCON network driver, mpc interface.
++ *
++ * Copyright (C) 2003 IBM United States, IBM Corporation
++ * Author(s): Belinda Thompson (belindat at us.ibm.com)
++ *            Andy Richter (richtera at us.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.
++ *
++ * RELEASE-TAG: CTCMPC/ESCON network driver $Revision: 1.1.2.1 $
++ */
++
++#ifndef _CTCMPC_H_
++#define _CTCMPC_H_
++
++#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,3,0)
++typedef struct net_device  net_device;
++#else
++typedef struct device  net_device;
++#endif
++
++typedef struct sk_buff sk_buff;
++typedef void (*callbacktypei2)(int,int);	      /* void (*void)(int,int) */
++typedef void (*callbacktypei3)(int,int,int);	  /* void (*void)(int,int,int) */
++
++/*  port_number is the mpc device 0,1,2 etc mpc2 is port_number 2 */
++/*  passive open  Just wait for XID2 exchange */
++/*  ctc_mpc_alloc channel(port_number,
++                                 void(*callback)(port_number,max_write_size)) */
++extern  int ctc_mpc_alloc_channel(int,callbacktypei2);
++/* active open  Alloc then send XID2 */
++/*  ctc_mpc_establish_connectivity(port_number ,
++                              void(callback*)(port_number,rc,max_write_size)) */
++extern void ctc_mpc_establish_connectivity(int,callbacktypei3);
++extern void ctc_mpc_dealloc_ch(int);
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/ctctty.c	2006-01-30 22:25:25.000000000 -0700
+@@ -42,7 +42,7 @@
+ #define init_waitqueue_head(x) *(x)=NULL
+ #define __set_current_state(state_value) \
+ 	do { current->state = state_value; } while (0)
+-#ifdef CONFIG_SMP
++#ifdef __SMP__
+ #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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/iucv.c	2006-01-30 22:25:25.000000000 -0700
+@@ -1,5 +1,5 @@
+ /* 
+- * $Id: iucv.c,v 1.41 2003/06/24 16:05:32 felfert Exp $
++ * $Id: iucv.c,v 1.40.2.5 2004/06/29 07:37:33 braunu Exp $
+  *
+  * IUCV network driver
+  *
+@@ -29,7 +29,7 @@
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  *
+- * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.41 $
++ * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.40.2.5 $
+  *
+  */
+ 
+@@ -320,7 +320,7 @@
+ #define iucv_debug(lvl, fmt, args...) \
+ do { \
+ 	if (debuglevel >= lvl) \
+-		printk(KERN_DEBUG __FUNCTION__ ": " fmt "\n", ## args); \
++		printk(KERN_DEBUG "%s: " fmt "\n", __FUNCTION__, ## args); \
+ } while (0)
+ 
+ #else
+@@ -334,13 +334,15 @@
+  * Internal functions
+  *******************************************************************************/
+ 
++static int iucv_retrieve_buffer(void);
++
+ /**
+  * print start banner
+  */
+ static void
+ iucv_banner(void)
+ {
+-	char vbuf[] = "$Revision: 1.41 $";
++	char vbuf[] = "$Revision: 1.40.2.5 $";
+ 	char *version = vbuf;
+ 
+ 	if ((version = strchr(version, ':'))) {
+@@ -418,6 +420,7 @@
+ static void
+ iucv_exit(void)
+ {
++	iucv_retrieve_buffer();
+ 	if (iucv_external_int_buffer)
+ 		kfree(iucv_external_int_buffer);
+ 	if (iucv_param_pool)
+@@ -438,17 +441,19 @@
+ static __inline__ iucv_param *
+ grab_param(void)
+ {
+-	iucv_param *ret;
+-	int i = 0;
++	iucv_param *ptr;
++        static int hint = 0;
++
++	ptr = iucv_param_pool + hint;
++	do {
++		ptr++;
++		if (ptr >= iucv_param_pool + PARAM_POOL_SIZE)
++			ptr = iucv_param_pool;
++	} while (atomic_compare_and_swap(0, 1, &ptr->in_use));
++	hint = ptr - iucv_param_pool;
+ 
+-	while (atomic_compare_and_swap(0, 1, &iucv_param_pool[i].in_use)) {
+-		i++;
+-		if (i >= PARAM_POOL_SIZE)
+-			i = 0;
 -	}
+-	ret = &iucv_param_pool[i];
+-	memset(&ret->param, 0, sizeof(ret->param));
+-	return ret;
++	memset(&ptr->param, 0, sizeof(ptr->param));
++	return ptr;
+ }
+ 
+ /**
+@@ -549,10 +554,8 @@
+  *	   - ENOMEM - storage allocation for a new pathid table failed
+ */
+ static int
+-iucv_add_pathid(__u16 pathid, handler *handler)
++__iucv_add_pathid(__u16 pathid, handler *handler)
+ {
+-	ulong flags;
 -
--	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) {
+ 	iucv_debug(1, "entering");
+ 
+ 	iucv_debug(1, "handler is pointing to %p", handler);
+@@ -560,21 +563,30 @@
+ 	if (pathid > (max_connections - 1))
+ 		return -EINVAL;
+ 
+-	spin_lock_irqsave (&iucv_lock, flags);
+ 	if (iucv_pathid_table[pathid]) {
+-		spin_unlock_irqrestore (&iucv_lock, flags);
+ 		iucv_debug(1, "pathid entry is %p", iucv_pathid_table[pathid]);
+ 		printk(KERN_WARNING
+ 		       "%s: Pathid being used, error.\n", __FUNCTION__);
+ 		return -EINVAL;
+ 	}
+ 	iucv_pathid_table[pathid] = handler;
+-	spin_unlock_irqrestore (&iucv_lock, flags);
+ 
+ 	iucv_debug(1, "exiting");
+ 	return 0;
+ }				/* end of add_pathid function */
+ 
++static int
++iucv_add_pathid(__u16 pathid, handler *handler)
++{
++	ulong flags;
++	int rc;
++
++	spin_lock_irqsave (&iucv_lock, flags);
++	rc = __iucv_add_pathid(pathid, handler);
++	spin_unlock_irqrestore (&iucv_lock, flags);
++	return rc;
++}
++
+ static void
+ iucv_remove_pathid(__u16 pathid)
+ {
+@@ -688,7 +700,6 @@
+ 	spin_lock_irqsave (&iucv_lock, flags);
+ 	list_del(&handler->list);
+ 	if (list_empty(&iucv_handler_table)) {
+-		iucv_retrieve_buffer();
+ 		if (register_flag) {
+ 			unregister_external_interrupt(0x4000, iucv_irq_handler);
+ 			register_flag = 0;
+@@ -764,6 +775,7 @@
+ 		if (iucv_pathid_table == NULL) {
+ 			printk(KERN_WARNING "%s: iucv_pathid_table storage "
+ 			       "allocation failed\n", __FUNCTION__);
++			kfree(new_handler);
+ 			return NULL;
+ 		}
+ 		memset (iucv_pathid_table, 0, max_connections * sizeof(handler *));
+@@ -1002,6 +1014,8 @@
+ 	b2f0_result = b2f0(ACCEPT, parm);
+ 
+ 	if (b2f0_result == 0) {
++		if (msglim)
++			*msglim = parm->ipmsglim;
+ 		if (pgm_data)
+ 			h->pgm_data = pgm_data;
+ 		if (flags1_out)
+@@ -1133,11 +1147,15 @@
+ 	iucv_setmask(~(AllInterrupts));
+ 	messagesDisabled = 1;
+ 
++	spin_lock_irqsave (&iucv_lock, flags);
+ 	parm->ipflags1 = (__u8)flags1;
+ 	b2f0_result = b2f0(CONNECT, parm);
+ 	memcpy(&local_parm, parm, sizeof(local_parm));
+ 	release_param(parm);
+ 	parm = &local_parm;
++	if (b2f0_result == 0)
++		add_pathid_result = __iucv_add_pathid(parm->ippathid, h);
++	spin_unlock_irqrestore (&iucv_lock, flags);
+ 
+ 	if (b2f0_result) {
+ 		iucv_setmask(~0);
+@@ -1145,7 +1163,6 @@
+ 		return b2f0_result;
+ 	}
+ 
+-	add_pathid_result = iucv_add_pathid(parm->ippathid, h);
+ 	*pathid = parm->ippathid;
+ 
+ 	/* Enable everything again */
+@@ -2333,7 +2350,8 @@
+ 					iucv_debug(2,
+ 						   "found a matching handler");
+ 					break;
+-				}
++				} else
++					h = NULL;
+ 			}
+ 			spin_unlock_irqrestore (&iucv_lock, flags);
+ 			if (h) {
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/iucv.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/iucv.h	2006-01-30 22:25:25.000000000 -0700
+@@ -449,7 +449,7 @@
+  *        buflen - Length of reply buffer.                              
+  * Output: residual_buffer - Address of buffer updated by the number 
+  *                    of bytes you have moved.              
+- *         residual_length - Contains one of the following values:
++ *         residual_length - Contains on the the following values
+  *		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
+@@ -483,7 +483,7 @@
+  *        buffer - Address of array of reply buffers.                     
+  *        buflen - Total length of reply buffers.                         
+  * Output: residual_buffer - Address of buffer which IUCV is currently working on.
+- *         residual_length - Contains one of the following values:
++ *         residual_length - Contains on the the following values
+  *              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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/lcs.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,2192 @@
++/*
++ *  linux/drivers/s390/net/lcs.c
++ *
++ *  Linux for S/390 Lan Channel Station Network Driver
++ *
++ *  Copyright (C)  1999-2001 IBM Deutschland Entwicklung GmbH,
++ *			     IBM Corporation
++ *    Author(s): Original Code written by
++ *			  DJ Barrow (djbarrow at de.ibm.com,barrow_dj at yahoo.com)
++ *		 Rewritten by
++ *			  Frank Pavlic (pavlic at de.ibm.com) and
++ *		 	  Martin Schwidefsky <schwidefsky at de.ibm.com>
++ *
++ *    $Revision: 1.132.20.6 $	 $Date: 2004/11/24 10:17:56 $
++ *
++ * 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/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/if.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/trdevice.h>
++#include <linux/fddidevice.h>
++#include <linux/inetdevice.h>
++#include <linux/in.h>
++#include <linux/igmp.h>
++#include <net/arp.h>
++#include <net/ip.h>
++
++#include <asm/debug.h>
++#include <asm/idals.h>
++#include <asm/timex.h>
++
++#include "lcs.h"
++
++#if !defined(CONFIG_CHANDEV)
++#error Cannot compile lcs.c without chandev support.
++#endif
++
++#if !defined(CONFIG_NET_ETHERNET) && \
++    !defined(CONFIG_TR) && !defined(CONFIG_FDDI)
++#error Cannot compile lcs.c without some net devices switched on.
++#endif
++
++/**
++ * initialization string for output
++ */
++#define VERSION_LCS_C  "$Revision: 1.132.20.6 $"
++
++static const char *version="LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")";
++static const char *cardname = "S390 Lan Channel Station Interface";
++static char debug_buffer[255];
++
++/**
++ * Some prototypes.
++ */
++static void lcs_irq(int, void *, struct pt_regs *);
++static void lcs_tasklet(unsigned long);
++static void lcs_start_kernel_thread(struct lcs_card *card);
++static void lcs_get_frames_cb(struct lcs_channel *, struct lcs_buffer *);
++static int lcs_send_delipm(struct lcs_card *, struct lcs_ipm_list *);
++
++/**
++ * Debug Facility Stuff
++ */
++static debug_info_t *lcs_dbf_setup;
++static debug_info_t *lcs_dbf_trace;
++
++/**
++ *  LCS Debug Facility functions
++ */
++static void
++lcs_unregister_debug_facility(void)
++{
++	if (lcs_dbf_setup)
++		debug_unregister(lcs_dbf_setup);
++	if (lcs_dbf_trace)
++		debug_unregister(lcs_dbf_trace);
++}
++
++static int
++lcs_register_debug_facility(void)
++{
++	lcs_dbf_setup = debug_register("lcs_setup", 1, 1, 8);
++	lcs_dbf_trace = debug_register("lcs_trace", 1, 2, 8);
++	if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) {
++		PRINT_ERR("Not enough memory for debug facility.\n");
++		lcs_unregister_debug_facility();
++		return -ENOMEM;
++	}
++	debug_register_view(lcs_dbf_setup, &debug_hex_ascii_view);
++	debug_set_level(lcs_dbf_setup, 5);
++	debug_register_view(lcs_dbf_trace, &debug_hex_ascii_view);
++	debug_set_level(lcs_dbf_trace, 3);
++	return 0;
++}
++
++/**
++ * Allocate io buffers.
++ */
++static int
++lcs_alloc_channel(struct lcs_channel *channel)
++{
++	int cnt;
++
++	LCS_DBF_TEXT(2, setup, "ichalloc");
++	memset(channel, 0, sizeof(struct lcs_channel));
++	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
++		/* alloc memory fo iobuffer */
++		channel->iob[cnt].data = (void *)
++			kmalloc(LCS_IOBUFFERSIZE, GFP_DMA | GFP_KERNEL);
++		if (channel->iob[cnt].data == NULL)
++			break;
++		memset(channel->iob[cnt].data, 0, LCS_IOBUFFERSIZE);
++		channel->iob[cnt].state = BUF_STATE_EMPTY;
++	}
++	if (cnt < LCS_NUM_BUFFS) {
++		/* Not all io buffers could be allocated. */
++		LCS_DBF_TEXT(3, setup, "echalloc");
++		while (cnt-- > 0)
++			kfree(channel->iob[cnt].data);
++		return -ENOMEM;
++	}
++	return 0;
++}
++
++/**
++ * Free io buffers.
++ */
++static void
++lcs_free_channel(struct lcs_channel *channel)
++{
++	int cnt;
++
++	LCS_DBF_TEXT(2, setup, "ichfree");
++	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++)
++		kfree(channel->iob[cnt].data);
++}
++
++/**
++ * LCS alloc memory for card and channels
++ */
++static struct lcs_card *
++lcs_alloc_card(void)
++{
++	struct lcs_card *card;
++	int rc;
++
++	LCS_DBF_TEXT(2, setup, "alloclcs");
++	card = kmalloc(sizeof(struct lcs_card), GFP_KERNEL | GFP_DMA);
++	if (card == NULL)
++		return NULL;
++	memset(card, 0, sizeof(struct lcs_card));
++	card->lan_type = LCS_FRAME_TYPE_AUTO;
++	card->pkt_seq = 0;
++	/* Allocate io buffers for the read channel. */
++	rc = lcs_alloc_channel(&card->read);
++	if (rc){
++		LCS_DBF_TEXT(2, setup, "iccwerr");
++		kfree(card);
++		return NULL;
++	}
++	/* Allocate io buffers for the write channel. */
++	rc = lcs_alloc_channel(&card->write);
++	if (rc) {
++		LCS_DBF_TEXT(2, setup, "iccwerr");
++		lcs_free_channel(&card->read);
++		kfree(card);
++		return NULL;
++	}
++	LCS_DBF_HEX(2, setup, &card, sizeof(void*));
++	return card;
++}
++
++/**
++ * LCS free memory for card and channels.
++ */
++static void
++lcs_free_card(struct lcs_card *card)
++{
++	LCS_DBF_TEXT(2, setup, "remcard");
++	/* Free write channel buffers. */
++	lcs_free_channel(&card->write);
++	/* Free read channel buffers. */
++	lcs_free_channel(&card->read);
++	kfree(card);
++}
++
++/*
++ * Setup read channel.
++ */
++static void
++lcs_setup_read_ccws(struct lcs_card *card)
++{
++	int cnt;
++
++	LCS_DBF_TEXT(2, setup, "ireadccw");
++	/* Setup read ccws. */
++	memset(card->read.ccws, 0, sizeof (ccw1_t) * (LCS_NUM_BUFFS + 1));
++	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
++		card->read.ccws[cnt].cmd_code = LCS_CCW_READ;
++		card->read.ccws[cnt].count = LCS_IOBUFFERSIZE;
++		card->read.ccws[cnt].flags =
++			CCW_FLAG_CC | CCW_FLAG_SLI | CCW_FLAG_PCI;
++		/*
++		 * Note: we have allocated the buffer with GFP_DMA, so
++		 * we do not need to do set_normalized_cda.
++		 */
++		card->read.ccws[cnt].cda =
++			(__u32) __pa(card->read.iob[cnt].data);
++		((struct lcs_header *)
++		 card->read.iob[cnt].data)->offset = LCS_ILLEGAL_OFFSET;
++		card->read.iob[cnt].callback = lcs_get_frames_cb;
++		card->read.iob[cnt].state = BUF_STATE_READY;
++		card->read.iob[cnt].count = LCS_IOBUFFERSIZE;
++	}
++	card->read.ccws[0].flags &= ~CCW_FLAG_PCI;
++	card->read.ccws[LCS_NUM_BUFFS - 1].flags &= ~CCW_FLAG_PCI;
++	card->read.ccws[LCS_NUM_BUFFS - 1].flags |= CCW_FLAG_SUSPEND;
++	/* Last ccw is a tic (transfer in channel). */
++	card->read.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER;
++	card->read.ccws[LCS_NUM_BUFFS].cda =
++		(__u32) __pa(card->read.ccws);
++	/* Set initial state of the read channel. */
++	card->read.state = CH_STATE_INIT;
++
++	card->read.io_idx = 0;
++	card->read.buf_idx = 0;
++}
++
++static int
++lcs_setup_read(struct lcs_card *card)
++{
++	char dbf_text[15];
++	int rc;
++
++	LCS_DBF_TEXT(2, setup, "setpread");
++	/* Request irq for read channel. */
++	rc = chandev_request_irq(card->read.irq, (void *) lcs_irq,
++				 0, cardname, &card->read.devstat);
++	if (rc) {
++		sprintf(dbf_text, "chre%4x", card->read.irq);
++		LCS_DBF_TEXT(3, setup, dbf_text);
++		return rc;
++	}
++	lcs_setup_read_ccws(card);
++	/* Initialize read channel tasklet. */
++	card->read.irq_tasklet.data = (unsigned long) &card->read;
++	card->read.irq_tasklet.func = lcs_tasklet;
++	/* Initialize waitqueue. */
++	init_waitqueue_head(&card->read.wait_q);
++	return 0;
++}
++
++/*
++ * Setup write channel.
++ */
++static void
++lcs_setup_write_ccws(struct lcs_card *card)
++{
++	int cnt;
++
++	LCS_DBF_TEXT(2, setup, "iwritccw");
++	/* Setup write ccws. */
++	memset(card->write.ccws, 0, sizeof(ccw1_t) * LCS_NUM_BUFFS + 1);
++	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
++		card->write.ccws[cnt].cmd_code = LCS_CCW_WRITE;
++		card->write.ccws[cnt].count = 0;
++		card->write.ccws[cnt].flags =
++			CCW_FLAG_SUSPEND | CCW_FLAG_CC | CCW_FLAG_SLI;
++		/*
++		 * Note: we have allocated the buffer with GFP_DMA, so
++		 * we do not need to do set_normalized_cda.
++		 */
++		card->write.ccws[cnt].cda =
++			(__u32) __pa(card->write.iob[cnt].data);
++	}
++	/* Last ccw is a tic (transfer in channel). */
++	card->write.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER;
++	card->write.ccws[LCS_NUM_BUFFS].cda =
++		(__u32) __pa(card->write.ccws);
++	/* Set initial state of the write channel. */
++	card->read.state = CH_STATE_INIT;
++
++	card->write.io_idx = 0;
++	card->write.buf_idx = 0;
++}
++
++static int
++lcs_setup_write(struct lcs_card *card)
++{
++	char dbf_text[15];
++	int rc;
++
++	LCS_DBF_TEXT(2, setup, "setpwrit");
++	/* Request irq for write channel. */
++	rc = chandev_request_irq(card->write.irq, (void *) lcs_irq,
++				 0, cardname, &card->write.devstat);
++	if (rc) {
++		sprintf(dbf_text,"chwr%4x", card->write.irq);
++		LCS_DBF_TEXT(3, setup, dbf_text);
++		return rc;
++	}
++	lcs_setup_write_ccws(card);
++	/* Initialize write channel tasklet. */
++	card->write.irq_tasklet.data = (unsigned long) &card->write;
++	card->write.irq_tasklet.func = lcs_tasklet;
++	/* Initialize waitqueue. */
++	init_waitqueue_head(&card->write.wait_q);
++	return 0;
++}
++
++static void
++lcs_set_allowed_threads(struct lcs_card *card, unsigned long threads)
++{
++        unsigned long flags;
++
++        spin_lock_irqsave(&card->mask_lock, flags);
++        card->thread_allowed_mask = threads;
++        spin_unlock_irqrestore(&card->mask_lock, flags);
++        wake_up(&card->wait_q);
++}
++
++static inline int
++lcs_threads_running(struct lcs_card *card, unsigned long threads)
++{
++        unsigned long flags;
++        int rc = 0;
++
++        spin_lock_irqsave(&card->mask_lock, flags);
++        rc = (card->thread_running_mask & threads);
++        spin_unlock_irqrestore(&card->mask_lock, flags);
++        return rc;
++}
++
++static int
++lcs_wait_for_threads(struct lcs_card *card, unsigned long threads)
++{
++        return wait_event_interruptible(card->wait_q,
++                        lcs_threads_running(card, threads) == 0);
++}
++
++static inline int
++lcs_set_thread_start_bit(struct lcs_card *card, unsigned long thread)
++{
++        unsigned long flags;
++
++        spin_lock_irqsave(&card->mask_lock, flags);
++        if ( !(card->thread_allowed_mask & thread) ||
++              (card->thread_start_mask & thread) ) {
++                spin_unlock_irqrestore(&card->mask_lock, flags);
++                return -EPERM;
++        }
++        card->thread_start_mask |= thread;
++        spin_unlock_irqrestore(&card->mask_lock, flags);
++        return 0;
++}
++
++static void
++lcs_clear_thread_running_bit(struct lcs_card *card, unsigned long thread)
++{
++        unsigned long flags;
++
++        spin_lock_irqsave(&card->mask_lock, flags);
++        card->thread_running_mask &= ~thread;
++        spin_unlock_irqrestore(&card->mask_lock, flags);
++        wake_up(&card->wait_q);
++}
++
++static inline int
++__lcs_do_run_thread(struct lcs_card *card, unsigned long thread)
++{
++        unsigned long flags;
++        int rc = 0;
++
++        spin_lock_irqsave(&card->mask_lock, flags);
++        if (card->thread_start_mask & thread){
++                if ((card->thread_allowed_mask & thread) &&
++                    !(card->thread_running_mask & thread)){
++                        rc = 1;
++                        card->thread_start_mask &= ~thread;
++                        card->thread_running_mask |= thread;
++                } else
++                        rc = -EPERM;
++        }
++        spin_unlock_irqrestore(&card->mask_lock, flags);
++        return rc;
++}
++
++static int
++lcs_do_run_thread(struct lcs_card *card, unsigned long thread)
++{
++        int rc = 0;
++        wait_event(card->wait_q,
++                   (rc = __lcs_do_run_thread(card, thread)) >= 0);
++        return rc;
++}
++
++static int
++lcs_do_start_thread(struct lcs_card *card, unsigned long thread)
++{
++        unsigned long flags;
++        int rc = 0;
++
++        spin_lock_irqsave(&card->mask_lock, flags);
++        LCS_DBF_TEXT_(4, trace, "  %02x%02x%02x",
++                        (u8) card->thread_start_mask,
++                        (u8) card->thread_allowed_mask,
++                        (u8) card->thread_running_mask);
++        rc = (card->thread_start_mask & thread);
++        spin_unlock_irqrestore(&card->mask_lock, flags);
++        return rc;
++}
++
++/*
++ * Cleanup channel.
++ */
++static void
++lcs_cleanup_channel(struct lcs_channel *channel)
++{
++	LCS_DBF_TEXT(2, setup, "cleanch");
++	/* Kill write channel tasklets. */
++	tasklet_kill(&channel->irq_tasklet);
++	/* Free irq. */
++	chandev_free_irq(channel->irq, &channel->devstat);
++}
++
++/**
++ * Initialize channels,card and state machines.
++ */
++static int
++lcs_setup_card(struct lcs_card *card)
++{
++	int rc;
++
++	LCS_DBF_TEXT(2, setup, "initcard");
++
++	rc = lcs_setup_read(card);
++	if (rc) {
++		PRINT_ERR("Could not initialize read channel\n");
++		return rc;
++	}
++	rc = lcs_setup_write(card);
++	if (rc) {
++		PRINT_ERR("Could not initialize write channel\n");
++		lcs_cleanup_channel(&card->read);
++		return rc;
++	}
++	/* Set cards initial state. */
++	card->state = DEV_STATE_DOWN;
++	if (card->port_protocol_no != LCS_INVALID_PORT_NO )
++		card->portno = card->port_protocol_no;
++	else
++		card->portno = 0;
++	card->tx_buffer = NULL;
++	card->tx_emitted = 0;
++
++	/* Initialize kernel thread task used for LGW commands. */
++	card->kernel_thread_starter.routine = (void *) lcs_start_kernel_thread;
++	card->kernel_thread_starter.data = (void *) card;
++	card->thread_start_mask = 0;
++        card->thread_allowed_mask = 0;
++        card->thread_running_mask = 0;
++	init_waitqueue_head(&card->wait_q);
++	spin_lock_init(&card->lock);
++	spin_lock_init(&card->ipm_lock);
++	INIT_LIST_HEAD(&card->ipm_list);
++	INIT_LIST_HEAD(&card->kernel_thread_starter.list);
++	INIT_LIST_HEAD(&card->lancmd_waiters);
++	return 0;
++}
++
++static inline void
++lcs_clear_multicast_list(struct lcs_card *card)
++{
++#ifdef  CONFIG_IP_MULTICAST
++        struct lcs_ipm_list *ipm;
++        unsigned long flags;
++
++        /* Free multicast list. */
++        LCS_DBF_TEXT(3, setup, "clmclist");
++        spin_lock_irqsave(&card->ipm_lock, flags);
++        while (!list_empty(&card->ipm_list)){
++                ipm = list_entry(card->ipm_list.next,
++                                 struct lcs_ipm_list, list);
++                list_del(&ipm->list);
++                if (ipm->ipm_state != LCS_IPM_STATE_SET_REQUIRED){
++                        spin_unlock_irqrestore(&card->ipm_lock, flags);
++                        lcs_send_delipm(card, ipm);
++                        spin_lock_irqsave(&card->ipm_lock, flags);
++                }
++                kfree(ipm);
++        }
++        spin_unlock_irqrestore(&card->ipm_lock, flags);
++#endif
++}
++
++/**
++ * Cleanup channels,card and state machines.
++ */
++static void
++lcs_cleanup_card(struct lcs_card *card)
++{
++	LCS_DBF_TEXT(2, setup, "cleancrd");
++	kfree(card->dev);
++	/* Cleanup channels. */
++	lcs_cleanup_channel(&card->write);
++	lcs_cleanup_channel(&card->read);
++}
++
++/**
++ * Start channel.
++ */
++static int
++lcs_start_channel(struct lcs_channel *channel)
++{
++	char dbf_text[15];
++	unsigned long flags;
++	int rc;
++
++	sprintf(dbf_text,"ssch%4x", channel->irq);
++	LCS_DBF_TEXT(3, trace, dbf_text);
++	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
++	rc = do_IO(channel->irq, channel->ccws + channel->io_idx, 0,
++		   0, DOIO_DENY_PREFETCH | DOIO_ALLOW_SUSPEND);
++	if (rc == 0)
++		channel->state = CH_STATE_RUNNING;
++	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
++	if (rc) {
++		sprintf(dbf_text,"essc%4x", channel->irq);
++		LCS_DBF_TEXT(3, trace, dbf_text);
++		PRINT_ERR("Error in starting channel!\n");
++	}
++	return rc;
++}
++
++/**
++ * Stop channel.
++ */
++static int
++lcs_stop_channel(struct lcs_channel *channel)
++{
++	char dbf_text[15];
++	unsigned long flags;
++	int rc;
++
++	if (channel->state == CH_STATE_STOPPED)
++		return 0;
++	sprintf(dbf_text,"hsch%4x", channel->irq);
++	LCS_DBF_TEXT(3, trace, dbf_text);
++	channel->state = CH_STATE_INIT;
++	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
++	rc = halt_IO(channel->irq, (addr_t) channel, 0);
++	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
++	if (rc) {
++		sprintf(dbf_text,"ehsc%4x", channel->irq);
++		LCS_DBF_TEXT(3, trace, dbf_text);
++		return rc;
++	}
++	/* Asynchronous halt initialted. Wait for its completion. */
++	wait_event(channel->wait_q, (channel->state == CH_STATE_HALTED));
++	return 0;
++}
++
++/**
++ * start read and write channel
++ */
++static int
++lcs_start_channels(struct lcs_card *card)
++{
++	int rc;
++
++	LCS_DBF_TEXT(3, trace, "chstart");
++	/* start read channel */
++	rc = lcs_start_channel(&card->read);
++	if (rc)
++		return rc;
++	/* start write channel */
++	rc = lcs_start_channel(&card->write);
++	if (rc)
++		lcs_stop_channel(&card->read);
++	return rc;
++}
++
++/**
++ * stop read and write channel
++ */
++static int
++lcs_stop_channels(struct lcs_card *card)
++{
++	LCS_DBF_TEXT(3, trace, "chhalt");
++	lcs_stop_channel(&card->read);
++	lcs_stop_channel(&card->write);
++	return 0;
++}
++
++/**
++ * Get empty buffer.
++ */
++static struct lcs_buffer *
++__lcs_get_buffer(struct lcs_channel *channel)
++{
++	int index;
++
++	LCS_DBF_TEXT(5, trace, "_getbuff");
++	index = channel->io_idx;
++	do {
++		if (channel->iob[index].state == BUF_STATE_EMPTY) {
++			channel->iob[index].state = BUF_STATE_LOCKED;
++			return channel->iob + index;
++		}
++		index = (index + 1) & (LCS_NUM_BUFFS - 1);
++	} while (index != channel->io_idx);
++	return NULL;
++}
++
++static struct lcs_buffer *
++lcs_get_buffer(struct lcs_channel *channel)
++{
++	struct lcs_buffer *buffer;
++	unsigned long flags;
++
++	LCS_DBF_TEXT(5, trace, "getbuff");
++	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
++	buffer = __lcs_get_buffer(channel);
++	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
++	return buffer;
++}
++
++/**
++ * Resume channel program if the channel is suspended.
++ */
++static int
++__lcs_resume_channel(struct lcs_channel *channel)
++{
++	int rc;
++
++	if (channel->state != CH_STATE_SUSPENDED)
++		return 0;
++	if (channel->ccws[channel->io_idx].flags & CCW_FLAG_SUSPEND)
++		return 0;
++	LCS_DBF_TEXT_(5, trace, "rsch%4x",channel->irq);
++	rc = resume_IO(channel->irq);
++	if (rc) {
++		LCS_DBF_TEXT_(4, trace, "ersc%4x",channel->irq);
++		PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc);
++	} else
++		channel->state = CH_STATE_RUNNING;
++	return rc;
++
++}
++
++/**
++ * Make a buffer ready for processing.
++ */
++static inline void
++__lcs_ready_buffer_bits(struct lcs_channel *channel, int index)
++{
++	int prev, next;
++
++	LCS_DBF_TEXT(5, trace, "rdybits");
++	prev = (index - 1) & (LCS_NUM_BUFFS - 1);
++	next = (index + 1) & (LCS_NUM_BUFFS - 1);
++	/* Check if we may clear the suspend bit of this buffer. */
++	if (channel->ccws[next].flags & CCW_FLAG_SUSPEND) {
++		/* Check if we have to set the PCI bit. */
++		if (!(channel->ccws[prev].flags & CCW_FLAG_SUSPEND))
++			/* Suspend bit of the previous buffer is not set. */
++			channel->ccws[index].flags |= CCW_FLAG_PCI;
++		/* Suspend bit of the next buffer is set. */
++		channel->ccws[index].flags &= ~CCW_FLAG_SUSPEND;
++	}
++}
++
++static int
++lcs_ready_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
++{
++	unsigned long flags;
++	int index, rc;
++
++	LCS_DBF_TEXT(5, trace, "rdybuff");
++	if (buffer->state != BUF_STATE_LOCKED &&
++	    buffer->state != BUF_STATE_PROCESSED)
++		BUG();
++	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
++	buffer->state = BUF_STATE_READY;
++	index = buffer - channel->iob;
++	/* Set length. */
++	channel->ccws[index].count = buffer->count;
++	/* Check relevant PCI/suspend bits. */
++	__lcs_ready_buffer_bits(channel, index);
++	rc = __lcs_resume_channel(channel);
++	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
++	return rc;
++}
++
++/**
++ * Mark the buffer as processed. Take care of the suspend bit
++ * of the previous buffer. This function is called from
++ * interrupt context, so the lock must not be taken.
++ */
++static int
++__lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
++{
++	int index, prev, next;
++
++	LCS_DBF_TEXT(5, trace, "prcsbuff");
++	if (buffer->state != BUF_STATE_READY)
++		BUG();
++	buffer->state = BUF_STATE_PROCESSED;
++	index = buffer - channel->iob;
++	prev = (index - 1) & (LCS_NUM_BUFFS - 1);
++	next = (index + 1) & (LCS_NUM_BUFFS - 1);
++	/* Set the suspend bit and clear the PCI bit of this buffer. */
++	channel->ccws[index].flags |= CCW_FLAG_SUSPEND;
++	channel->ccws[index].flags &= ~CCW_FLAG_PCI;
++	/* Check the suspend bit of the previous buffer. */
++	if (channel->iob[prev].state == BUF_STATE_READY) {
++		/*
++		 * Previous buffer is in state ready. It might have
++		 * happened in lcs_ready_buffer that the suspend bit
++		 * has not been cleared to avoid an endless loop.
++		 * Do it now.
++		 */
++		__lcs_ready_buffer_bits(channel, prev);
++	}
++	/* Clear PCI bit of next buffer. */
++	channel->ccws[next].flags &= ~CCW_FLAG_PCI;
++	return __lcs_resume_channel(channel);
++}
++
++/**
++ * Put a processed buffer back to state empty.
++ */
++static void
++lcs_release_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
++{
++	unsigned long flags;
++
++	LCS_DBF_TEXT(5, trace, "relbuff");
++	if (buffer->state != BUF_STATE_LOCKED &&
++	    buffer->state != BUF_STATE_PROCESSED)
++		BUG();
++	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
++	buffer->state = BUF_STATE_EMPTY;
++	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
++}
++
++/**
++ * Get buffer for a lan command.
++ */
++static struct lcs_buffer *
++lcs_get_lancmd(struct lcs_card *card, int count)
++{
++	struct lcs_buffer *buffer;
++	struct lcs_cmd *cmd;
++	
++	LCS_DBF_TEXT(4, trace, "getlncmd");
++	
++	/* Get buffer and wait if none is available. */
++	wait_event(card->write.wait_q,
++		   ((buffer = lcs_get_buffer(&card->write)) != NULL));
++	count += sizeof(struct lcs_header);
++	*(__u16 *)(buffer->data + count) = 0;
++	buffer->count = count + sizeof(__u16);
++	buffer->callback = lcs_release_buffer;
++	cmd = (struct lcs_cmd *) buffer->data;
++	cmd->offset = count;
++	cmd->type = LCS_FRAME_TYPE_CONTROL;
++	cmd->slot = 0;
++	return buffer;
++}
++
++static void
++lcs_get_reply(struct lcs_reply *reply)
++{
++        atomic_inc(&reply->refcnt);
++}
++
++static void
++lcs_put_reply(struct lcs_reply *reply)
++{
++        if (atomic_dec_and_test(&reply->refcnt)) {
++                kfree(reply);
++        }
++
++}
++
++static struct lcs_reply *
++lcs_alloc_reply(struct lcs_cmd *cmd)
++{
++        struct lcs_reply *reply;
++
++        LCS_DBF_TEXT(4, trace, "getreply");
++
++        reply = kmalloc(sizeof(struct lcs_reply), GFP_ATOMIC);
++        if (!reply)
++                return NULL;
++        memset(reply,0,sizeof(struct lcs_reply));
++        atomic_set(&reply->refcnt,1);
++        reply->sequence_no = cmd->sequence_no;
++        reply->received = 0;
++        reply->rc = 0;
++        init_waitqueue_head(&reply->wait_q);
++
++        return reply;
++}
++
++
++/**
++ * Notifier function for lancmd replies. Called from read irq.
++ */
++static void
++lcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd)
++{
++	struct list_head *l, *n;
++	struct lcs_reply *reply;
++
++	LCS_DBF_TEXT(4, trace, "notiwait");
++	spin_lock(&card->lock);
++	list_for_each_safe(l, n, &card->lancmd_waiters) {
++		reply = list_entry(l, struct lcs_reply, list);
++		if (reply->sequence_no == cmd->sequence_no) {
++			lcs_get_reply(reply);
++			list_del_init(&reply->list);
++			if (reply->callback != NULL)
++				reply->callback(card, cmd);
++			reply->received = 1;
++			reply->rc = cmd->return_code;
++			wake_up(&reply->wait_q);
++			lcs_put_reply(reply);
++			break;
++		}
++	}
++	spin_unlock(&card->lock);
++}
++
++/**
++ * Emit buffer of a lan comand.
++ */
++void
++lcs_lancmd_timeout(unsigned long data)
++{
++	struct lcs_reply *reply, *list_reply;
++	struct list_head *l, *n;
++	unsigned long flags;
++
++	LCS_DBF_TEXT(4, trace, "timeout");
++	list_reply = (struct lcs_reply *) data;
++	spin_lock_irqsave(&list_reply->card->lock, flags);
++	list_for_each_safe(l, n, &list_reply->card->lancmd_waiters) {
++		reply = list_entry(l, struct lcs_reply, list);
++                if (reply == list_reply) {
++                        lcs_get_reply(reply);
++                        list_del_init(&reply->list);
++                        spin_unlock_irqrestore(&list_reply->card->lock, flags);
++			reply->received = 1;
++			reply->rc = -ETIME;
++			wake_up(&reply->wait_q);
++			lcs_put_reply(reply);
++			return;
++		}
++	}
++        spin_unlock_irqrestore(&list_reply->card->lock, flags);
++}
++
++static int
++lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer,
++		void (*reply_callback)(struct lcs_card *, struct lcs_cmd *))
++{
++	struct lcs_reply *reply;
++	struct lcs_cmd *cmd;
++	struct timer_list timer;
++	unsigned long flags;
++	int rc;
++
++	LCS_DBF_TEXT(4, trace, "sendcmd");
++	cmd = (struct lcs_cmd *) buffer->data;
++	cmd->sequence_no = ++card->sequence_no;
++	cmd->return_code = 0;
++	reply = lcs_alloc_reply(cmd);
++	if (!reply)
++		return -ENOMEM;
++	reply->callback = reply_callback;
++	reply->card = card;
++        spin_lock_irqsave(&card->lock, flags);
++        list_add_tail(&reply->list, &card->lancmd_waiters);
++        spin_unlock_irqrestore(&card->lock, flags);
++
++	buffer->callback = lcs_release_buffer;
++	rc = lcs_ready_buffer(&card->write, buffer);
++	if (rc)
++		return rc;
++	init_timer(&timer);
++	timer.function = lcs_lancmd_timeout;
++	timer.data = (unsigned long) reply;
++	timer.expires = jiffies + HZ*5;
++	add_timer(&timer);
++	wait_event(reply->wait_q, reply->received);
++	lcs_put_reply(reply);
++	del_timer_sync(&timer);
++	return reply->rc ? -EIO : 0;
++}
++
++/**
++ * LCS startup command
++ */
++static int
++lcs_send_startup(struct lcs_card *card, __u8 initiator)
++{
++	struct lcs_buffer *buffer;
++	struct lcs_cmd *cmd;
++
++	LCS_DBF_TEXT(2, trace, "startup");
++	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
++	cmd = (struct lcs_cmd *) buffer->data;
++	cmd->cmd_code = LCS_CMD_STARTUP;
++	cmd->initiator = initiator;
++	cmd->cmd.lcs_startup.buff_size = LCS_IOBUFFERSIZE;
++	return lcs_send_lancmd(card, buffer, NULL);
++}
++
++/**
++ * LCS shutdown command
++ */
++static int
++lcs_send_shutdown(struct lcs_card *card)
++{
++	struct lcs_buffer *buffer;
++	struct lcs_cmd *cmd;
++
++	LCS_DBF_TEXT(2, trace, "shutdown");
++	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
++	cmd = (struct lcs_cmd *) buffer->data;
++	cmd->cmd_code = LCS_CMD_SHUTDOWN;
++	cmd->initiator = LCS_INITIATOR_TCPIP;
++	return lcs_send_lancmd(card, buffer, NULL);
++}
++
++/**
++ * LCS lanstat command
++ */
++static void
++__lcs_lanstat_cb(struct lcs_card *card, struct lcs_cmd *cmd)
++{
++	LCS_DBF_TEXT(2, trace, "statcb");
++	memcpy(card->mac, cmd->cmd.lcs_lanstat_cmd.mac_addr,
++	       LCS_MAC_LENGTH);
++}
++
++static int
++lcs_send_lanstat(struct lcs_card *card)
++{
++	struct lcs_buffer *buffer;
++	struct lcs_cmd *cmd;
++
++	LCS_DBF_TEXT(2, trace, "cmdstat");
++	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
++	cmd = (struct lcs_cmd *) buffer->data;
++	/* Setup lanstat command. */
++	cmd->cmd_code = LCS_CMD_LANSTAT;
++	cmd->initiator = LCS_INITIATOR_TCPIP;
++	cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
++	cmd->cmd.lcs_std_cmd.portno = card->portno;
++	return lcs_send_lancmd(card, buffer, __lcs_lanstat_cb);
++}
++
++/**
++ * send stoplan command
++ */
++static int
++lcs_send_stoplan(struct lcs_card *card, __u8 initiator)
++{
++	struct lcs_buffer *buffer;
++	struct lcs_cmd *cmd;
++
++	LCS_DBF_TEXT(2, trace, "cmdstpln");
++	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
++	cmd = (struct lcs_cmd *) buffer->data;
++	cmd->cmd_code = LCS_CMD_STOPLAN;
++	cmd->initiator = initiator;
++	cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
++	cmd->cmd.lcs_std_cmd.portno = card->portno;
++	return lcs_send_lancmd(card, buffer, NULL);
++}
++
++/**
++ * send startlan command
++ */
++static void
++__lcs_send_startlan_cb(struct lcs_card *card, struct lcs_cmd *cmd)
++{
++	LCS_DBF_TEXT(2, trace, "srtlancb");
++	card->lan_type = cmd->cmd.lcs_std_cmd.lan_type;
++	card->portno = cmd->cmd.lcs_std_cmd.portno;
++}
++
++static int
++lcs_send_startlan(struct lcs_card *card, __u8 initiator)
++{
++	struct lcs_buffer *buffer;
++	struct lcs_cmd *cmd;
++
++	LCS_DBF_TEXT(2, trace, "cmdstaln");
++	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
++	cmd = (struct lcs_cmd *) buffer->data;
++	cmd->cmd_code = LCS_CMD_STARTLAN;
++	cmd->initiator = initiator;
++	cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
++	cmd->cmd.lcs_std_cmd.portno = card->portno;
++	return lcs_send_lancmd(card, buffer, __lcs_send_startlan_cb);
++}
++
++#ifdef CONFIG_IP_MULTICAST
++/**
++ * send setipm command (Multicast)
++ */
++static int
++lcs_send_setipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list)
++{
++	struct lcs_buffer *buffer;
++	struct lcs_cmd *cmd;
++
++	LCS_DBF_TEXT(2, trace, "cmdsetim");
++	buffer = lcs_get_lancmd(card, LCS_MULTICAST_CMD_SIZE);
++	cmd = (struct lcs_cmd *) buffer->data;
++	cmd->cmd_code = LCS_CMD_SETIPM;
++	cmd->initiator = LCS_INITIATOR_TCPIP;
++	cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
++	cmd->cmd.lcs_qipassist.portno = card->portno;
++	cmd->cmd.lcs_qipassist.version = 4;
++	cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
++	memcpy(cmd->cmd.lcs_qipassist.lcs_ipass_ctlmsg.ip_mac_pair,
++	       &ipm_list->ipm, sizeof (struct lcs_ip_mac_pair));
++	return lcs_send_lancmd(card, buffer, NULL);
++}
++
++/**
++ * send delipm command (Multicast)
++ */
++static int
++lcs_send_delipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list)
++{
++	struct lcs_buffer *buffer;
++	struct lcs_cmd *cmd;
++
++	LCS_DBF_TEXT(2, trace, "cmddelim");
++	buffer = lcs_get_lancmd(card, LCS_MULTICAST_CMD_SIZE);
++	cmd = (struct lcs_cmd *) buffer->data;
++	cmd->cmd_code = LCS_CMD_DELIPM;
++	cmd->initiator = LCS_INITIATOR_TCPIP;
++	cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
++	cmd->cmd.lcs_qipassist.portno = card->portno;
++	cmd->cmd.lcs_qipassist.version = 4;
++	cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
++	memcpy(cmd->cmd.lcs_qipassist.lcs_ipass_ctlmsg.ip_mac_pair,
++	       &ipm_list->ipm, sizeof (struct lcs_ip_mac_pair));
++	return lcs_send_lancmd(card, buffer, NULL);
++}
++
++/**
++ * check if multicast is supported by LCS
++ */
++static void
++__lcs_check_multicast_cb(struct lcs_card *card, struct lcs_cmd *cmd)
++{
++	LCS_DBF_TEXT(2, trace, "chkmccb");
++	card->ip_assists_supported =
++		cmd->cmd.lcs_qipassist.ip_assists_supported;
++	card->ip_assists_enabled =
++		cmd->cmd.lcs_qipassist.ip_assists_enabled;
++}
++
++static int
++lcs_check_multicast_support(struct lcs_card *card)
++{
++	struct lcs_buffer *buffer;
++	struct lcs_cmd *cmd;
++	int rc;
++
++	LCS_DBF_TEXT(2, trace, "cmdqipa");
++	/* Send query ipassist. */
++	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
++	cmd = (struct lcs_cmd *) buffer->data;
++	cmd->cmd_code = LCS_CMD_QIPASSIST;
++	cmd->initiator = LCS_INITIATOR_TCPIP;
++	cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
++	cmd->cmd.lcs_qipassist.portno = card->portno;
++	cmd->cmd.lcs_qipassist.version = 4;
++	cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
++	rc = lcs_send_lancmd(card, buffer, __lcs_check_multicast_cb);
++	if (rc != 0) {
++		PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n");
++		return -EOPNOTSUPP;
++	}
++	/* Print out supported assists: IPv6 */
++	PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name,
++		   (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
++		   "with" : "without");
++	/* Print out supported assist: Multicast */
++	PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name,
++		   (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
++		   "with" : "without");
++	if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT)
++		return 0;
++	return -EOPNOTSUPP;
++}
++
++/**
++ * set or del multicast address on LCS card
++ */
++static void
++lcs_fix_multicast_list(struct lcs_card *card)
++{
++	struct list_head failed_list;
++	struct list_head *l, *n;
++	struct lcs_ipm_list *ipm;
++	unsigned long flags;
++	int rc;
++	
++	LCS_DBF_TEXT(4, trace, "fixipm");
++	INIT_LIST_HEAD(&failed_list);
++	spin_lock_irqsave(&card->ipm_lock, flags);
++list_modified:
++	list_for_each_safe(l, n, &card->ipm_list) {
++		ipm = list_entry(l, struct lcs_ipm_list, list);
++		switch (ipm->ipm_state) {
++		case LCS_IPM_STATE_SET_REQUIRED:
++			list_del_init(&ipm->list);
++                        spin_unlock_irqrestore(&card->ipm_lock, flags);
++			rc = lcs_send_setipm(card, ipm);
++			spin_lock_irqsave(&card->ipm_lock, flags);
++			if (rc) {
++				PRINT_INFO("Adding multicast address failed."
++					   "Table possibly full!\n");
++                                list_add_tail(&ipm->list, &failed_list);
++			} else {
++				ipm->ipm_state = LCS_IPM_STATE_ON_CARD;
++				list_add_tail(&ipm->list, &card->ipm_list);
++			}
++			goto list_modified;
++		case LCS_IPM_STATE_DEL_REQUIRED:
++			list_del(&ipm->list);
++			spin_unlock_irqrestore(&card->ipm_lock, flags);
++			lcs_send_delipm(card, ipm);
++			spin_lock_irqsave(&card->ipm_lock, flags);
++			kfree(ipm);
++			goto list_modified;
++		case LCS_IPM_STATE_ON_CARD:
++			break;
++		}
++	}
++	list_for_each_entry(ipm, &failed_list, list) {
++		list_del_init(&ipm->list);
++		list_add_tail(&ipm->list, &card->ipm_list);
++	}
++        spin_unlock_irqrestore(&card->ipm_lock, flags);
++	if (card->state == DEV_STATE_UP)
++		netif_wake_queue(card->dev);
++}
++
++/**
++ * get mac address for the relevant Multicast address
++ */
++static void
++lcs_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev)
++{
++	LCS_DBF_TEXT(4, trace, "getmac");
++	if (dev->type == ARPHRD_IEEE802_TR)
++		ip_tr_mc_map(ipm, mac);
++	else
++		ip_eth_mc_map(ipm, mac);
++}
++
++static inline void
++lcs_remove_mc_addresses(struct lcs_card *card, struct in_device *in4_dev)
++{
++        struct ip_mc_list *im4;
++        struct list_head *l;
++        struct lcs_ipm_list *ipm;
++        unsigned long flags;
++        char buf[MAX_ADDR_LEN];
++
++        LCS_DBF_TEXT(4, trace, "remmclst");
++        spin_lock_irqsave(&card->ipm_lock, flags);
++        list_for_each(l, &card->ipm_list) {
++                ipm = list_entry(l, struct lcs_ipm_list, list);
++                for (im4 = in4_dev->mc_list; im4 != NULL; im4 = im4->next) {
++                        lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev);
++                        if ( (ipm->ipm.ip_addr == im4->multiaddr) &&
++                             (memcmp(buf, &ipm->ipm.mac_addr,
++                                     LCS_MAC_LENGTH) == 0) )
++                                break;
++                }
++                if (im4 == NULL)
++                        ipm->ipm_state = LCS_IPM_STATE_DEL_REQUIRED;
++        }
++        spin_unlock_irqrestore(&card->ipm_lock, flags);
++}
++
++static inline struct lcs_ipm_list *
++lcs_check_addr_entry(struct lcs_card *card, struct ip_mc_list *im4, char *buf)
++{
++        struct lcs_ipm_list *tmp, *ipm = NULL;
++        struct list_head *l;
++        unsigned long flags;
++
++        LCS_DBF_TEXT(4, trace, "chkmcent");
++        spin_lock_irqsave(&card->ipm_lock, flags);
++        list_for_each(l, &card->ipm_list) {
++                tmp = list_entry(l, struct lcs_ipm_list, list);
++                if ( (tmp->ipm.ip_addr == im4->multiaddr) &&
++                     (memcmp(buf, &tmp->ipm.mac_addr,
++                             LCS_MAC_LENGTH) == 0) ) {
++                        ipm = tmp;
++                        break;
++                }
++        }
++        spin_unlock_irqrestore(&card->ipm_lock, flags);
++        return ipm;
++}
++
++static inline void
++lcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev)
++{
++
++        struct ip_mc_list *im4;
++        struct lcs_ipm_list *ipm;
++        char buf[MAX_ADDR_LEN];
++        unsigned long flags;
++
++        LCS_DBF_TEXT(4, trace, "setmclst");
++        for (im4 = in4_dev->mc_list; im4; im4 = im4->next) {
++                lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev);
++                ipm = lcs_check_addr_entry(card, im4, buf);
++                if (ipm != NULL)
++                        continue;       /* Address already in list. */
++                ipm = (struct lcs_ipm_list *)
++                        kmalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC);
++                if (ipm == NULL) {
++                        PRINT_INFO("Not enough memory to add "
++                                   "new multicast entry!\n");
++                        break;
++                }
++                memset(ipm, 0, sizeof(struct lcs_ipm_list));
++                memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH);
++                ipm->ipm.ip_addr = im4->multiaddr;
++                ipm->ipm_state = LCS_IPM_STATE_SET_REQUIRED;
++                spin_lock_irqsave(&card->ipm_lock, flags);
++                list_add(&ipm->list, &card->ipm_list);
++                spin_unlock_irqrestore(&card->ipm_lock, flags);
++        }
++}
++
++/**
++ * register multicast addresses
++ */ 
++static int
++lcs_register_mc_addresses(void *data)
++{
++	struct lcs_card *card;
++	struct in_device *in4_dev;
++
++	card = (struct lcs_card *) data;
++	daemonize();
++
++	if (!lcs_do_run_thread(card, LCS_SET_MC_THREAD))
++                return 0;
++	LCS_DBF_TEXT(4, trace, "regmulti");
++	
++	in4_dev = in_dev_get(card->dev);
++	if (in4_dev == NULL)
++		goto out;
++	read_lock(&in4_dev->lock);
++	lcs_remove_mc_addresses(card, in4_dev);
++        lcs_set_mc_addresses(card, in4_dev);
++	read_unlock(&in4_dev->lock);
++	in_dev_put(in4_dev);
++
++	lcs_fix_multicast_list(card);
++out:
++	lcs_clear_thread_running_bit(card, LCS_SET_MC_THREAD);
++	return 0;
++}
++
++/**
++ * function called by net device to handle multicast address relevant things
++ */
++static void
++lcs_set_multicast_list(struct net_device *dev)
++{
++	struct lcs_card *card;
++
++	LCS_DBF_TEXT(4, trace, "setmulti");
++	card = (struct lcs_card *) dev->priv;
++
++	if (!lcs_set_thread_start_bit(card, LCS_SET_MC_THREAD))
++		schedule_task(&card->kernel_thread_starter);
++}
++
++#endif /* CONFIG_IP_MULTICAST */
++
++/**
++ * IRQ Handler for LCS channels
++ */
++static void
++lcs_irq(int irq, void *devstat, struct pt_regs *p)
++{
++	struct lcs_channel *channel;
++	devstat_t *stat;
++	int index;
++
++	stat  = (devstat_t *) devstat;
++	channel = (struct lcs_channel *)
++		((char *) devstat - offsetof(struct lcs_channel, devstat));
++	LCS_DBF_TEXT_(5, trace, "Rint%4x",irq);
++	LCS_DBF_TEXT_(5, trace, "%4x%4x",stat->cstat, stat->dstat);
++
++	/* How far in the ccw chain have we processed? */
++	if (channel->state != CH_STATE_INIT) {
++		index = (ccw1_t *) __va((addr_t) stat->cpa) - channel->ccws;
++		if ((stat->ii.irb.scsw.actl & SCSW_ACTL_SUSPENDED) ||
++		    (stat->cstat | SCHN_STAT_PCI))
++			/* Bloody io subsystem tells us lies about cpa... */
++			index = (index - 1) & (LCS_NUM_BUFFS - 1);
++		while (channel->io_idx != index) {
++			__lcs_processed_buffer(channel,
++					       channel->iob + channel->io_idx);
++			channel->io_idx =
++				(channel->io_idx + 1) & (LCS_NUM_BUFFS - 1);
++		}
++	}
++
++	if ((stat->dstat & DEV_STAT_DEV_END) ||
++	    (stat->dstat & DEV_STAT_CHN_END) ||
++	    (stat->dstat & DEV_STAT_UNIT_CHECK))
++		/* Mark channel as stopped. */
++		channel->state = CH_STATE_STOPPED;
++	else if (stat->ii.irb.scsw.actl & SCSW_ACTL_SUSPENDED)
++		/* CCW execution stopped on a suspend bit. */
++		channel->state = CH_STATE_SUSPENDED;
++
++	if (stat->ii.irb.scsw.fctl & SCSW_FCTL_HALT_FUNC)
++		/* The channel has been stopped by halt_IO. */
++		channel->state = CH_STATE_HALTED;
++
++	/* Do the rest in the tasklet. */
++	tasklet_schedule(&channel->irq_tasklet);
++}
++
++/**
++ * Tasklet for IRQ handler
++ */
++static void
++lcs_tasklet(unsigned long data)
++{
++	unsigned long flags;
++	struct lcs_channel *channel;
++	struct lcs_buffer *iob;
++	int buf_idx;
++	int rc;
++
++	channel = (struct lcs_channel *) data;
++
++	LCS_DBF_TEXT_(5, trace, "tlet%4x",channel->irq);
++	/* Check for processed buffers. */
++	iob = channel->iob;
++	buf_idx = channel->buf_idx;
++	while (iob[buf_idx].state == BUF_STATE_PROCESSED) {
++		/* Do the callback thing. */
++		if (iob[buf_idx].callback != NULL)
++			iob[buf_idx].callback(channel, iob + buf_idx);
++		buf_idx = (buf_idx + 1) & (LCS_NUM_BUFFS - 1);
++	}
++	channel->buf_idx = buf_idx;
++
++	if (channel->state == CH_STATE_STOPPED)
++		// FIXME: what if rc != 0 ??
++		rc = lcs_start_channel(channel);
++	spin_lock_irqsave(get_irq_lock(channel->irq), flags);
++	if (channel->state == CH_STATE_SUSPENDED &&
++	    channel->iob[channel->io_idx].state == BUF_STATE_READY) {
++		// FIXME: what if rc != 0 ??
++		rc = __lcs_resume_channel(channel);
++	}
++	spin_unlock_irqrestore(get_irq_lock(channel->irq), flags);
++
++	/* Something happened on the channel. Wake up waiters. */
++	wake_up(&channel->wait_q);
++}
++
++/**
++ * Finish current tx buffer and make it ready for transmit.
++ */
++static void
++__lcs_emit_txbuffer(struct lcs_card *card)
++{
++	LCS_DBF_TEXT(5, trace,"emittx");
++	*(__u16 *)(card->tx_buffer->data + card->tx_buffer->count) = 0;
++	card->tx_buffer->count += 2;
++	lcs_ready_buffer(&card->write, card->tx_buffer);
++	card->tx_buffer = NULL;
++	card->tx_emitted++;
++}
++
++/**
++ * Callback for finished tx buffers.
++ */
++static void
++lcs_txbuffer_cb(struct lcs_channel *channel, struct lcs_buffer *buffer)
++{
++	struct lcs_card *card;
++
++	LCS_DBF_TEXT(5, trace,"txbuffcb");
++	/* Put buffer back to pool. */
++	lcs_release_buffer(channel, buffer);
++	card = (struct lcs_card *)
++		((char *) channel - offsetof(struct lcs_card, write));
++	spin_lock(&card->lock);
++	card->tx_emitted--;
++	if (card->tx_emitted <= 0 && card->tx_buffer != NULL)
++		/*
++		 * Last running tx buffer has finished. Submit partially
++		 * filled current buffer.
++		 */
++		__lcs_emit_txbuffer(card);
++	spin_unlock(&card->lock);
++}
++
++/**
++ * Packet transmit function called by network stack
++ */
++static int
++__lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb,
++		 struct net_device *dev)
++{
++	struct lcs_header *header;
++
++	LCS_DBF_TEXT(5, trace,"hardxmit");
++	if (skb == NULL) {
++		card->stats.tx_dropped++;
++		card->stats.tx_errors++;
++		return -EIO;
++	}
++	if (card->state != DEV_STATE_UP) {
++		dst_link_failure(skb);
++		dev_kfree_skb(skb);
++		card->stats.tx_dropped++;
++		card->stats.tx_errors++;
++		card->stats.tx_carrier_errors++;
++		return 0;
++	}
++	if (netif_queue_stopped(dev) ) {
++		card->stats.tx_dropped++;
++		return -EBUSY;
++	}
++	if (card->tx_buffer != NULL &&
++	    card->tx_buffer->count + sizeof(struct lcs_header) +
++	    skb->len + sizeof(u16) > LCS_IOBUFFERSIZE)
++		/* skb too big for current tx buffer. */
++		__lcs_emit_txbuffer(card);
++	if (card->tx_buffer == NULL) {
++		/* Get new tx buffer */
++		card->tx_buffer = lcs_get_buffer(&card->write);
++		if (card->tx_buffer == NULL) {
++			card->stats.tx_dropped++;
++			return -EBUSY;
++		}
++		card->tx_buffer->callback = lcs_txbuffer_cb;
++		card->tx_buffer->count = 0;
++	}
++	header = (struct lcs_header *)
++		(card->tx_buffer->data + card->tx_buffer->count);
++	card->tx_buffer->count += skb->len + sizeof(struct lcs_header);
++	header->offset = card->tx_buffer->count;
++	header->type = card->lan_type;
++	header->slot = card->portno;
++	memcpy(header + 1, skb->data, skb->len);
++	card->stats.tx_bytes += skb->len;
++	card->stats.tx_packets++;
++	dev_kfree_skb(skb);
++	if (card->tx_emitted <= 0)
++		/* If this is the first tx buffer emit it immediatly. */
++		__lcs_emit_txbuffer(card);
++	return 0;
++}
++
++static int
++lcs_start_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++	struct lcs_card *card;
++	int rc;
++
++	LCS_DBF_TEXT(5, trace, "pktxmit");
++	card = (struct lcs_card *) dev->priv;
++	spin_lock(&card->lock);
++	rc = __lcs_start_xmit(card, skb, dev);
++	spin_unlock(&card->lock);
++	return rc;
++}
++
++/**
++ * send startlan and lanstat command to make LCS device ready
++ */
++static int
++lcs_startlan_auto(struct lcs_card *card)
++{
++	int rc;
++
++	LCS_DBF_TEXT(2, trace,"strtauto");
++#ifdef CONFIG_NET_ETHERNET
++	card->lan_type = LCS_FRAME_TYPE_ENET;
++	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
++	if (rc == 0)
++		return 0;
++
++#endif
++#ifdef CONFIG_TR
++	card->lan_type = LCS_FRAME_TYPE_TR;
++	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
++	if (rc == 0)
++		return 0;
++#endif
++#ifdef CONFIG_FDDI
++	card->lan_type = LCS_FRAME_TYPE_FDDI;
++	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
++	if (rc == 0)
++		return 0;
++#endif
++	return -EIO;
++}
++
++static int
++lcs_startlan(struct lcs_card *card)
++{
++	int rc, i;
++
++	LCS_DBF_TEXT(2, trace, "startlan");
++	rc = 0;
++	if (card->device_forced) {
++		card->portno = card->port_protocol_no;
++		if (card->lan_type == LCS_FRAME_TYPE_AUTO)
++			rc = lcs_startlan_auto(card);
++		else
++			rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
++	} else {
++		for (i = 0; i <= card->max_port_no; i++) {
++			card->portno = i;
++			if (i == 0)
++				card->portno = card->hint_port_no;
++			else if (i == card->hint_port_no)
++				card->portno = 0;
++
++			if (card->lan_type != LCS_FRAME_TYPE_AUTO)
++				rc = lcs_send_startlan(card,
++						       LCS_INITIATOR_TCPIP);
++			else
++				/* autodetecting lan type */
++				rc = lcs_startlan_auto(card);
++			if (rc == 0)
++				break;
++		}
++	}
++	if (rc == 0)
++		return lcs_send_lanstat(card);
++	return rc;
++}
++
++/**
++ * LCS detect function
++ * setup channels and make them I/O ready
++ */
++static int
++lcs_detect(struct lcs_card *card)
++{
++	int rc;
++
++	LCS_DBF_TEXT(2, setup,"lcsdetct");
++
++	/* start/reset card */
++	if (card->dev)
++		netif_stop_queue(card->dev);
++	rc = lcs_stop_channels(card);
++	if (rc == 0) {
++		rc = lcs_start_channels(card);
++		if (rc == 0) {
++			rc = lcs_send_startup(card, LCS_INITIATOR_TCPIP);
++			if (rc == 0)
++				rc = lcs_startlan(card);
++		}
++	}
++	if (rc == 0) {
++		card->state = DEV_STATE_UP;
++	} else {
++		card->state = DEV_STATE_DOWN;
++		card->write.state = CH_STATE_INIT;
++		card->read.state =  CH_STATE_INIT;
++	}
++	return rc;
++}
++
++/**
++ * reset card
++ */
++static int
++lcs_resetcard(struct lcs_card *card)
++{
++	int retries;
++
++	LCS_DBF_TEXT(2, trace, "rescard");
++	for (retries = 0; retries < 10; retries++) {
++		if (lcs_detect(card) == 0) {
++			netif_wake_queue(card->dev);
++			card->state = DEV_STATE_UP;
++			PRINT_INFO("LCS device %s successfully restarted!\n",
++				   card->dev->name);
++			return 0;
++		}
++		schedule_timeout(3 * HZ);
++	}
++	PRINT_ERR("Error in Reseting LCS card!\n");
++	return -EIO;
++}
++
++/**
++ * LCS Stop card
++ */
++static int
++lcs_stopcard(struct lcs_card *card)
++{
++	int rc;
++
++	LCS_DBF_TEXT(2, setup, "stopcard");
++	if (card->read.state != CH_STATE_STOPPED &&
++	    card->write.state != CH_STATE_STOPPED &&
++	    card->state == DEV_STATE_UP) {
++		lcs_clear_multicast_list(card);
++		rc = lcs_send_stoplan(card,LCS_INITIATOR_TCPIP);
++		rc = lcs_send_shutdown(card);
++	}
++	rc = lcs_stop_channels(card);
++	card->state = DEV_STATE_DOWN;
++	return rc;
++}
++
++/**
++ * LGW initiated commands
++ */
++static int
++lcs_lgw_startlan_thread(void *data)
++{
++	struct lcs_card *card;
++
++	card = (struct lcs_card *) data;
++	daemonize();
++
++	if (!lcs_do_run_thread(card, LCS_STARTLAN_THREAD))
++		return 0;
++	LCS_DBF_TEXT(2, trace, "lgwstpln");
++	if (card->dev)
++		netif_stop_queue(card->dev);
++	if (lcs_startlan(card) == 0) {
++		netif_wake_queue(card->dev);
++		card->state = DEV_STATE_UP;
++		PRINT_INFO("LCS Startlan for device %s succeeded!\n",
++			   card->dev->name);
++
++	} else
++		PRINT_ERR("LCS Startlan for device %s failed!\n",
++			  card->dev->name);
++	lcs_clear_thread_running_bit(card, LCS_STARTLAN_THREAD);
++	return 0;
++}
++
++/**
++ * Send startup command initiated by Lan Gateway
++ */
++static int
++lcs_lgw_startup_thread(void *data)
++{
++	int rc;
++
++	struct lcs_card *card;
++
++	card = (struct lcs_card *) data;
++	daemonize();
++
++	if (!lcs_do_run_thread(card, LCS_STARTUP_THREAD))
++                return 0;
++	LCS_DBF_TEXT(2, trace, "lgwstaln");
++	if (card->dev)
++		netif_stop_queue(card->dev);
++	rc = lcs_send_startup(card, LCS_INITIATOR_LGW);
++	if (rc != 0) {
++		PRINT_ERR("Startup for LCS device %s initiated " \
++			  "by LGW failed!\nReseting card ...\n",
++			  card->dev->name);
++		/* do a card reset */
++		rc = lcs_resetcard(card);
++		if (rc == 0)
++			goto Done;
++	}
++	rc = lcs_startlan(card);
++	if (rc == 0) {
++		netif_wake_queue(card->dev);
++		card->state = DEV_STATE_UP;
++	}
++Done:
++	if (rc == 0)
++		PRINT_INFO("LCS Startup for device %s succeeded!\n",
++			   card->dev->name);
++	else
++		PRINT_ERR("LCS Startup for device %s failed!\n",
++			  card->dev->name);
++	lcs_clear_thread_running_bit(card, LCS_STARTUP_THREAD);
++	return 0;
++}
++
++
++/**
++ * send stoplan command initiated by Lan Gateway
++ */
++static int
++lcs_lgw_stoplan_thread(void *data)
++{
++	struct lcs_card *card;
++	int rc;
++
++	card = (struct lcs_card *) data;
++	daemonize();
++	
++	if (!lcs_do_run_thread(card, LCS_STOPLAN_THREAD))
++                return 0;
++	LCS_DBF_TEXT(2, trace, "lgwstop");
++	if (card->dev)
++		netif_stop_queue(card->dev);
++	if (lcs_send_stoplan(card, LCS_INITIATOR_LGW) == 0)
++		PRINT_INFO("Stoplan for %s initiated by LGW succeeded!\n",
++			   card->dev->name);
++	else
++		PRINT_ERR("Stoplan %s initiated by LGW failed!\n",
++			  card->dev->name);
++	/*Try to reset the card, stop it on failure */
++	rc = lcs_resetcard(card);
++	if (rc != 0)
++		rc = lcs_stopcard(card);
++	lcs_clear_thread_running_bit(card, LCS_STOPLAN_THREAD);
++	return rc;
++}
++
++/**
++ * Kernel Thread helper functions for LGW initiated commands
++ */
++static void
++lcs_start_kernel_thread(struct lcs_card *card)
++{
++	LCS_DBF_TEXT(5, trace, "krnthrd");
++	if (lcs_do_start_thread(card, LCS_STARTUP_THREAD))
++                kernel_thread(lcs_lgw_startup_thread, (void *) card, SIGCHLD);
++        if (lcs_do_start_thread(card, LCS_STARTLAN_THREAD))
++                kernel_thread(lcs_lgw_startlan_thread, (void *) card, SIGCHLD);
++        if (lcs_do_start_thread(card, LCS_STOPLAN_THREAD))
++                kernel_thread(lcs_lgw_stoplan_thread, (void *) card, SIGCHLD);
++#ifdef CONFIG_IP_MULTICAST
++        if (lcs_do_start_thread(card, LCS_SET_MC_THREAD))
++                kernel_thread(lcs_register_mc_addresses, (void *) card, SIGCHLD);
++#endif
++}
++
++/**
++ * Process control frames.
++ */
++static void
++lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd)
++{
++	LCS_DBF_TEXT(5, trace, "getctrl");
++	if (cmd->initiator == LCS_INITIATOR_LGW) {
++		switch(cmd->cmd_code) {
++		case LCS_CMD_STARTUP:
++			if (!lcs_set_thread_start_bit(card,
++                                                      LCS_STARTUP_THREAD))
++				schedule_task(&card->kernel_thread_starter);
++			break;
++		case LCS_CMD_STARTLAN:
++			if (!lcs_set_thread_start_bit(card,
++                                                      LCS_STARTLAN_THREAD))
++			schedule_task(&card->kernel_thread_starter);
++			break;
++		case LCS_CMD_STOPLAN:
++			if (!lcs_set_thread_start_bit(card,
++                                                      LCS_STOPLAN_THREAD))
++			schedule_task(&card->kernel_thread_starter);
++			break;
++		default:
++			PRINT_INFO("UNRECOGNIZED LGW COMMAND\n");
++			break;
++		}
++	} else
++		lcs_notify_lancmd_waiters(card, cmd);
++}
++
++/**
++ * Unpack network packet.
++ */
++static void
++lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
++{
++	struct sk_buff *skb;
++
++	LCS_DBF_TEXT(5, trace, "getskb");
++	if (card->dev == NULL ||
++	    card->state != DEV_STATE_UP)
++		/* The card isn't up. Ignore the packet. */
++		return;
++
++	skb = dev_alloc_skb(skb_len);
++	if (skb == NULL) {
++		PRINT_ERR("LCS: alloc_skb failed for device=%s\n",
++			  card->dev->name);
++		card->stats.rx_dropped++;
++		return;
++	}
++	skb->dev = card->dev;
++	memcpy(skb_put(skb, skb_len), skb_data, skb_len);
++	skb->protocol =	card->lan_type_trans(skb, card->dev);
++	card->stats.rx_bytes += skb_len;
++	card->stats.rx_packets++;
++	*((__u32 *)skb->cb) = ++card->pkt_seq;
++	netif_rx(skb);
++}
++
++/**
++ * LCS main routine to get packets and lancmd replies from the buffers
++ */
++static void
++lcs_get_frames_cb(struct lcs_channel *channel, struct lcs_buffer *buffer)
++{
++	struct lcs_card *card;
++	struct lcs_header *lcs_hdr;
++	__u16 offset;
++
++	LCS_DBF_TEXT(5, trace, "lcsgtpkt");
++	lcs_hdr = (struct lcs_header *) buffer->data;
++	if (lcs_hdr->offset == LCS_ILLEGAL_OFFSET) {
++		LCS_DBF_TEXT(4, trace, "-eiogpkt");
++		return;
++	}
++	card = (struct lcs_card *)
++		((char *) channel - offsetof(struct lcs_card, read));
++	offset = 0;
++	while (lcs_hdr->offset != 0) {
++		if (lcs_hdr->offset <= 0 ||
++		    lcs_hdr->offset > LCS_IOBUFFERSIZE ||
++		    lcs_hdr->offset < offset) {
++			/* Offset invalid. */
++			card->stats.rx_length_errors++;
++			card->stats.rx_errors++;
++			return;
++		}
++		/* What kind of frame is it? */
++		if (lcs_hdr->type == LCS_FRAME_TYPE_CONTROL)
++			/* Control frame. */
++			lcs_get_control(card, (struct lcs_cmd *) lcs_hdr);
++		else if (lcs_hdr->type == LCS_FRAME_TYPE_ENET ||
++			 lcs_hdr->type == LCS_FRAME_TYPE_TR ||
++			 lcs_hdr->type == LCS_FRAME_TYPE_FDDI)
++			/* Normal network packet. */
++			lcs_get_skb(card, (char *)(lcs_hdr + 1),
++				    lcs_hdr->offset - offset -
++				    sizeof(struct lcs_header));
++		else
++			/* Unknown frame type. */
++			; // FIXME: error message ?
++		/* Proceed to next frame. */
++		offset = lcs_hdr->offset;
++		lcs_hdr->offset = LCS_ILLEGAL_OFFSET;
++		lcs_hdr = (struct lcs_header *) (buffer->data + offset);
++	}
++	/* The buffer is now empty. Make it ready again. */
++	lcs_ready_buffer(&card->read, buffer);
++}
++
++/**
++ * get network statistics for ifconfig and other user programs
++ */
++struct net_device_stats *
++lcs_getstats(struct net_device *dev)
++{
++	struct lcs_card *card;
++
++	LCS_DBF_TEXT(4, trace, "netstats");
++	card = (struct lcs_card *) dev->priv;
++	return &card->stats;
++}
++
++/**
++ * stop lcs device
++ * This function will be called by user doing ifconfig xxx down
++ */
++int
++lcs_stop_device(struct net_device *dev)
++{
++	struct lcs_card *card;
++
++	LCS_DBF_TEXT(2, trace, "stopdev");
++	card = (struct lcs_card *) dev->priv;
++	netif_stop_queue(dev);
++	MOD_DEC_USE_COUNT;
++	return 0;
++}
++
++/**
++ * start lcs device and make it runnable
++ * This function will be called by user doing ifconfig xxx up
++ */
++int
++lcs_open_device(struct net_device *dev)
++{
++	struct lcs_card *card;
++
++	LCS_DBF_TEXT(2, trace, "opendev");
++	LCS_DBF_TEXT_(3,trace,"%s",dev->name);
++	card = (struct lcs_card *) dev->priv;
++	LCS_DBF_HEX(2, trace, &card, sizeof(void*));
++	/* initialize statistics */
++	MOD_INC_USE_COUNT;
++	netif_wake_queue(dev);
++	card->state = DEV_STATE_UP;
++	return 0; 
++}
++
++/**
++ * LCS probe function
++ * Main device detection routine called whenever lcs will be started
++ * either as module or on bootup
++ */
++static int
++lcs_probe(chandev_probeinfo *info)
++{
++	struct lcs_card *card;
++	struct net_device *dev;
++	int rc;
++
++	LCS_DBF_TEXT(2, setup, "lcsprobe");
++	card = lcs_alloc_card();
++	if (card == NULL) {
++		PRINT_ERR("Allocation of lcs card failed\n");
++		return -ENOMEM;
++	}
++	card->read.irq = info->read.irq;
++	card->write.irq = info->write.irq;
++	card->device_forced = info->device_forced;
++	card->max_port_no = info->max_port_no;
++	card->hint_port_no = info->hint_port_no;
++	card->port_protocol_no = info->port_protocol_no;
++	rc = lcs_setup_card(card);
++	if (rc) {
++		LCS_DBF_TEXT(3, setup, "errinit");
++		PRINT_ERR("LCS card Initialization failed\n");
++		lcs_free_card(card);
++		return rc;
++	}
++
++	/* Now let's detect and start LCS. */
++	rc = lcs_detect(card);
++	if (rc) {
++		LCS_DBF_TEXT(2, setup, "dtctfail");
++		lcs_stopcard(card);
++		lcs_cleanup_card(card);
++		lcs_free_card(card);
++		return -ENODEV;
++	}
++	info->memory_usage_in_k =
++		-((LCS_IOBUFFERSIZE * LCS_NUM_BUFFS +
++		   LCS_NUM_BUFFS * LCS_IOBUFFERSIZE) / 1024);
++	switch (card->lan_type) {
++#ifdef CONFIG_NET_ETHERNET
++	case LCS_FRAME_TYPE_ENET:
++		card->lan_type_trans = eth_type_trans;
++		dev = chandev_initnetdevice(info, card->portno,
++					    NULL, 0, "eth",
++					    init_etherdev,
++					    unregister_netdev);
++		break;
++#endif
++#ifdef CONFIG_TR
++	case LCS_FRAME_TYPE_TR:
++		card->lan_type_trans = tr_type_trans;
++		dev = chandev_initnetdevice(info, card->portno,
++					    NULL, 0, "tr",
++					    init_trdev,
++					    unregister_netdev);
++		break;
++#endif
++#ifdef CONFIG_FDDI
++	case LCS_FRAME_TYPE_FDDI:
++		card->lan_type_trans = fddi_type_trans;
++		dev = chandev_initnetdevice(info, card->portno,
++					    NULL, 0, "fddi",
++					    init_fddidev,
++					    unregister_netdev);
++		break;
++#endif
++	default:
++		LCS_DBF_TEXT(2, setup, "errinit");
++		PRINT_ERR("LCS: Initialization failed\n");
++		PRINT_ERR("LCS: No device found!\n");
++		lcs_cleanup_channel(&card->read);
++		lcs_cleanup_channel(&card->write);
++		lcs_free_card(card);
++		return -ENODEV;
++	}
++	memcpy(dev->dev_addr, card->mac, LCS_MAC_LENGTH);
++	card->dev = dev;
++	dev->priv = card;
++	dev->open = lcs_open_device;
++	dev->stop = lcs_stop_device;
++	dev->hard_start_xmit = lcs_start_xmit;
++#ifdef CONFIG_IP_MULTICAST
++	if (lcs_check_multicast_support(card))
++		dev->set_multicast_list = lcs_set_multicast_list;
++#endif
++	dev->get_stats = lcs_getstats;
++	netif_stop_queue(dev);
++	lcs_set_allowed_threads(card, 0xffffffff);
++	return 0;
++}
++
++/**
++ * shutdown function called by chandev
++ */
++static int
++lcs_shutdown(struct net_device *dev)
++{
++	struct lcs_card *card;
++
++	LCS_DBF_TEXT(2, setup, "shtdndev");
++	card = dev->priv;
++	lcs_set_allowed_threads(card, 0);
++	if (lcs_wait_for_threads(card, LCS_SET_MC_THREAD))
++                return -ERESTARTSYS;
++	if (card != NULL) {
++		lcs_stopcard(card);
++		lcs_cleanup_card(card);
++		lcs_free_card(card);
++	}
++	return 0;
++}
++
++/**
++ * chandev notification function,only used by 2.4 kernel
++ */
++static void
++lcs_msck_notify(struct net_device *device, int msck_irq,
++		chandev_msck_status prevstatus,
++		chandev_msck_status newstatus)
++{
++	struct lcs_card *card;
++
++	if (device->priv == NULL)
++		return;
++	LCS_DBF_TEXT(2, trace, "mscknot");
++	card = (struct lcs_card *) device->priv;
++
++	if ((prevstatus != chandev_status_good) ||
++	    (prevstatus != chandev_status_all_chans_good))
++		if ((newstatus == chandev_status_good) ||
++		    (newstatus == chandev_status_all_chans_good))
++			lcs_resetcard(card);
++	if ((newstatus == chandev_status_gone) ||
++	    (newstatus == chandev_status_no_path) ||
++	    (newstatus == chandev_status_not_oper))
++		lcs_stopcard(card);
++}
++
++static int 
++lcs_verify_dev(struct net_device *dev)
++{
++	return (dev->hard_start_xmit==lcs_start_xmit);
++}
++
++/**
++ * multicast notifier structures
++ */
++#ifdef CONFIG_IP_MULTICAST
++static int lcs_mc_event(struct notifier_block *this,
++                       unsigned long event,void *ptr)
++{
++	struct ip_mc_list *mc  = (struct ip_mc_list *) ptr;
++	struct net_device *dev = mc->interface->dev;
++	struct lcs_card *card; 
++
++	LCS_DBF_TEXT(3,trace,"mcevent");
++
++	if (!lcs_verify_dev(dev)) 
++		return NOTIFY_DONE;
++	card  = (struct lcs_card *) dev->priv;
++	if (!card)
++		return NOTIFY_DONE;
++	lcs_set_multicast_list(dev);
++	return NOTIFY_DONE;
++}
++
++static struct notifier_block lcs_mc_notifier = {
++       lcs_mc_event,
++       0
++};
++#endif
++
++/**
++ *  LCS Module/Kernel initialization function
++ */
++static int
++__init lcs_init_module(void)
++{
++	int cardsfound;
++	int rc;
++
++	LCS_DBF_TEXT(0, setup, "lcsinit");
++	PRINT_INFO("Loading %s\n",version);
++	rc = lcs_register_debug_facility();
++	if (rc) {
++		PRINT_ERR("Initialization failed\n");
++		return rc;
++	}
++
++	cardsfound =
++		chandev_register_and_probe(lcs_probe,
++					   (chandev_shutdownfunc)
++					   lcs_shutdown,
++					   (chandev_msck_notification_func)
++					   lcs_msck_notify, chandev_type_lcs);
++#ifdef MODULE
++	if (cardsfound <= 0 &&
++	    !chandev_persist(chandev_type_lcs)) {
++		chandev_unregister(lcs_probe,0);
++		return -ENODEV;
++	}
++#else
++	if (cardsfound <= 0)
++		return -ENODEV;
++#endif
++
++#ifdef CONFIG_IP_MULTICAST
++	if (register_multicast_notifier(&lcs_mc_notifier)) {
++		PRINT_ERR("register_multicast_notifier failed, maybe not " \
++			  "all multicast addresses will be registered\n");
++	}
++#endif
++
++	return 0;
++}
++
++
++/**
++ *  LCS module cleanup function
++ */
++static void
++__exit lcs_cleanup_module(void)
++{
++	PRINT_INFO("Terminating lcs module.\n");
++	LCS_DBF_TEXT(0, trace, "cleanup");
++	chandev_unregister(lcs_probe, 1);
++
++#ifdef CONFIG_IP_MULTICAST
++	unregister_multicast_notifier(&lcs_mc_notifier);
++#endif
++
++	lcs_unregister_debug_facility();
++}
++
++module_init(lcs_init_module);
++module_exit(lcs_cleanup_module);
++
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/lcs.h	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,312 @@
++/*lcs.h*/
++
++#include <linux/interrupt.h>
++#include <linux/netdevice.h>
++#include <linux/skbuff.h>
++#include <asm/chandev.h>
++#include <asm/irq.h>
++
++#define VERSION_LCS_H "$Revision: 1.1.4.1 $"
++
++#define LCS_DBF_TEXT(level, name, text) \
++	do { \
++		debug_text_event(lcs_dbf_##name, level, text); \
++	} while (0)
++
++#define LCS_DBF_HEX(level,name,addr,len) \
++do { \
++	        debug_event(lcs_dbf_##name,level,(void*)(addr),len); \
++} while (0)
++
++
++#define LCS_DBF_TEXT_(level,name,text...) \
++do {                                       \
++	sprintf(debug_buffer, text);  \
++		debug_text_event(lcs_dbf_##name,level, debug_buffer);\
++} while (0)
++
++/**
++ * some more definitions for debug or output stuff
++ */
++#define PRINTK_HEADER		" lcs: "
++
++/**
++ * CCW commands used in this driver
++ */
++#define LCS_CCW_WRITE		0x01
++#define LCS_CCW_READ		0x02
++#define LCS_CCW_TRANSFER	0x08
++
++/**
++ * LCS device status primitives
++ */
++#define LCS_CMD_STARTLAN	0x01
++#define LCS_CMD_STOPLAN		0x02
++#define LCS_CMD_LANSTAT		0x04
++#define LCS_CMD_STARTUP		0x07
++#define LCS_CMD_SHUTDOWN	0x08
++#define LCS_CMD_QIPASSIST	0xb2
++#define LCS_CMD_SETIPM		0xb4
++#define LCS_CMD_DELIPM		0xb5
++
++#define LCS_INITIATOR_TCPIP	0x00
++#define LCS_INITIATOR_LGW	0x01
++#define LCS_STD_CMD_SIZE	16
++#define LCS_MULTICAST_CMD_SIZE	404
++
++/**
++ * LCS IPASSIST MASKS,only used when multicast is switched on
++ */
++/* Not supported by LCS */
++#define LCS_IPASS_ARP_PROCESSING	0x0001
++#define LCS_IPASS_IN_CHECKSUM_SUPPORT	0x0002
++#define LCS_IPASS_OUT_CHECKSUM_SUPPORT	0x0004
++#define LCS_IPASS_IP_FRAG_REASSEMBLY	0x0008
++#define LCS_IPASS_IP_FILTERING		0x0010
++/* Supported by lcs 3172 */
++#define LCS_IPASS_IPV6_SUPPORT		0x0020
++#define LCS_IPASS_MULTICAST_SUPPORT	0x0040
++
++/**
++ * LCS sense byte definitions
++ */
++#define LCS_SENSE_INTERFACE_DISCONNECT	0x01
++#define LCS_SENSE_EQUIPMENT_CHECK	0x10
++#define LCS_SENSE_BUS_OUT_CHECK		0x20
++#define LCS_SENSE_INTERVENTION_REQUIRED 0x40
++#define LCS_SENSE_CMD_REJECT		0x80
++#define LCS_SENSE_RESETTING_EVENT	0x0080
++#define LCS_SENSE_DEVICE_ONLINE		0x0020
++
++/**
++ * LCS packet type definitions
++ */
++#define LCS_FRAME_TYPE_CONTROL		0
++#define LCS_FRAME_TYPE_ENET		1
++#define LCS_FRAME_TYPE_TR		2
++#define LCS_FRAME_TYPE_FDDI		7
++#define LCS_FRAME_TYPE_AUTO		-1
++
++/**
++ * some more definitions,we will sort them later
++ */
++#define LCS_ILLEGAL_OFFSET		0xffff
++#define LCS_IOBUFFERSIZE		0x5000
++#define LCS_NUM_BUFFS			8	/* needs to be power of 2 */
++#define LCS_MAC_LENGTH			6
++#define LCS_INVALID_PORT_NO		-1
++
++/**
++ * Multicast state
++ */
++#define	 LCS_IPM_STATE_SET_REQUIRED	0
++#define	 LCS_IPM_STATE_DEL_REQUIRED	1
++#define	 LCS_IPM_STATE_ON_CARD		2
++
++/**
++ * LCS IP Assist declarations
++ * seems to be only used for multicast
++ */
++#define	 LCS_IPASS_ARP_PROCESSING	0x0001
++#define	 LCS_IPASS_INBOUND_CSUM_SUPP	0x0002
++#define	 LCS_IPASS_OUTBOUND_CSUM_SUPP	0x0004
++#define	 LCS_IPASS_IP_FRAG_REASSEMBLY	0x0008
++#define	 LCS_IPASS_IP_FILTERING		0x0010
++#define	 LCS_IPASS_IPV6_SUPPORT		0x0020
++#define	 LCS_IPASS_MULTICAST_SUPPORT	0x0040
++
++/**
++ * LCS Buffer states
++ */
++enum lcs_buffer_states {
++	BUF_STATE_EMPTY,	/* buffer is empty */
++	BUF_STATE_LOCKED,	/* buffer is locked, don't touch */
++	BUF_STATE_READY,	/* buffer is ready for read/write */
++	BUF_STATE_PROCESSED,
++};
++
++/**
++ * LCS Channel State Machine declarations
++ */
++enum lcs_channel_states {
++	CH_STATE_INIT,
++	CH_STATE_HALTED,
++	CH_STATE_STOPPED,
++	CH_STATE_RUNNING,
++	CH_STATE_SUSPENDED,
++};
++
++/**
++ * LCS device state machine
++ */
++enum lcs_dev_states {
++	DEV_STATE_DOWN,
++	DEV_STATE_UP,
++};
++
++enum lcs_threads {
++	LCS_SET_MC_THREAD       = 1,
++	LCS_STARTLAN_THREAD     = 2,
++	LCS_STOPLAN_THREAD      = 4,
++	LCS_STARTUP_THREAD      = 8,
++};
++
++/**
++ * LCS struct declarations
++ */
++struct lcs_header {
++	__u16  offset;
++	__u8   type;
++	__u8   slot;
++}  __attribute__ ((packed));
++
++struct lcs_ip_mac_pair {
++	__u32  ip_addr;
++	__u8   mac_addr[LCS_MAC_LENGTH];
++	__u8   reserved[2];
++}  __attribute__ ((packed));
++
++struct lcs_ipm_list {
++	struct list_head list;
++	struct lcs_ip_mac_pair ipm;
++	__u8 ipm_state;
++};
++
++struct lcs_cmd {
++	__u16  offset;
++	__u8   type;
++	__u8   slot;
++	__u8   cmd_code;
++	__u8   initiator;
++	__u16  sequence_no;
++	__u16  return_code;
++	union {
++		struct {
++			__u8   lan_type;
++			__u8   portno;
++			__u16  parameter_count;
++			__u8   operator_flags[3];
++			__u8   reserved[3];
++		} lcs_std_cmd;
++		struct {
++			__u16  unused1;
++			__u16  buff_size;
++			__u8   unused2[6];
++		} lcs_startup;
++		struct {
++			__u8   lan_type;
++			__u8   portno;
++			__u8   unused[10];
++			__u8   mac_addr[LCS_MAC_LENGTH];
++			__u32  num_packets_deblocked;
++			__u32  num_packets_blocked;
++			__u32  num_packets_tx_on_lan;
++			__u32  num_tx_errors_detected;
++			__u32  num_tx_packets_disgarded;
++			__u32  num_packets_rx_from_lan;
++			__u32  num_rx_errors_detected;
++			__u32  num_rx_discarded_nobuffs_avail;
++			__u32  num_rx_packets_too_large;
++		} lcs_lanstat_cmd;
++#ifdef CONFIG_IP_MULTICAST
++		struct {
++			__u8   lan_type;
++			__u8   portno;
++			__u16  num_ip_pairs;
++			__u16  ip_assists_supported;
++			__u16  ip_assists_enabled;
++			__u16  version;
++			struct {
++				struct lcs_ip_mac_pair
++				ip_mac_pair[32];
++				__u32	  response_data;
++			} lcs_ipass_ctlmsg __attribute__ ((packed));
++		} lcs_qipassist __attribute__ ((packed));
++#endif /*CONFIG_IP_MULTICAST */
++	} cmd __attribute__ ((packed));
++}  __attribute__ ((packed));
++
++/**
++ * Forward declarations.
++ */
++struct lcs_card;
++struct lcs_channel;
++
++/**
++ * Definition of an lcs buffer.
++ */
++struct lcs_buffer {
++	enum lcs_buffer_states state;
++	void *data;
++	int count;
++	/* Callback for completion notification. */
++	void (*callback)(struct lcs_channel *, struct lcs_buffer *);
++};
++
++struct lcs_reply {
++	struct list_head list;
++	__u16 sequence_no;
++	 atomic_t refcnt;
++	/* Callback for completion notification. */
++	void (*callback)(struct lcs_card *, struct lcs_cmd *);
++	wait_queue_head_t wait_q;
++	struct lcs_card *card;
++	int received;
++	int rc;
++};
++
++/**
++ * Definition of an lcs channel
++ */
++struct lcs_channel {
++	enum lcs_channel_states state;
++	__u16 irq;
++	ccw1_t ccws[LCS_NUM_BUFFS + 1];
++	devstat_t devstat;
++	wait_queue_head_t wait_q;
++	struct tasklet_struct irq_tasklet;
++	struct lcs_buffer iob[LCS_NUM_BUFFS];
++	int io_idx;
++	int buf_idx;
++};
++
++/**
++ * definition of the lcs card
++ */
++struct lcs_card {
++	spinlock_t lock;
++	spinlock_t ipm_lock;
++	enum lcs_dev_states state;
++	struct net_device *dev;
++	struct net_device_stats stats;
++	unsigned short (*lan_type_trans)(struct sk_buff *skb,
++					 struct net_device *dev);
++	struct lcs_channel read;
++	struct lcs_channel write;
++	struct lcs_buffer *tx_buffer;
++	int tx_emitted;
++	struct list_head lancmd_waiters;
++
++	struct tq_struct kernel_thread_starter;
++	spinlock_t mask_lock;
++	unsigned long thread_start_mask;
++	unsigned long thread_running_mask;
++	unsigned long thread_allowed_mask;
++	wait_queue_head_t wait_q;
++	
++#ifdef CONFIG_IP_MULTICAST
++	struct list_head ipm_list;
++#endif
++	__u8 mac[LCS_MAC_LENGTH];
++	__u16 ip_assists_supported;
++	__u16 ip_assists_enabled;
++	__s8 lan_type;
++	__u32 pkt_seq;
++	__u16 sequence_no;
++	__u16 portno;
++	/* Some info copied from probeinfo */
++	u8 device_forced;
++	u8 max_port_no;
++	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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/netiucv.c	2006-01-30 22:25:25.000000000 -0700
+@@ -1,5 +1,5 @@
+ /*
+- * $Id: netiucv.c,v 1.23 2003/06/24 16:05:32 felfert Exp $
++ * $Id: netiucv.c,v 1.21.8.6 2004/06/29 07:37:33 braunu Exp $
+  *
+  * IUCV network driver
+  *
+@@ -28,7 +28,7 @@
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+  *
+- * RELEASE-TAG: IUCV network driver $Revision: 1.23 $
++ * RELEASE-TAG: IUCV network driver $Revision: 1.21.8.6 $
+  *
+  */
+ 
+@@ -114,17 +114,13 @@
+ 	spinlock_t               collect_lock;
+ 	int                      collect_len;
+ 	int                      max_buffsize;
+-	int                      flags;
+ 	fsm_timer                timer;
+-	int                      retry;
+ 	fsm_instance             *fsm;
+ 	net_device               *netdev;
+ 	connection_profile       prof;
+ 	char                     userid[9];
+ } iucv_connection;
+ 
+-#define CONN_FLAGS_BUFSIZE_CHANGED 1
 -
--		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);
+ /**
+  * Linked list of all connection structs.
+  */
+@@ -590,7 +586,7 @@
+ 	iucv_MessagePending *eib = (iucv_MessagePending *)ev->data;
+ 	netiucv_priv *privptr = (netiucv_priv *)conn->netdev->priv;
+ 
+-	__u16 msglen = eib->ln1msg2.ipbfln1f;
++	__u32 msglen = eib->ln1msg2.ipbfln1f;
+ 	int rc;
+ 
+ #ifdef DEBUG
+@@ -613,6 +609,7 @@
+ 			  conn->rx_buff->data, msglen, NULL, NULL, NULL);
+ 	if (rc != 0 || msglen < 5) {
+ 		privptr->stats.rx_errors++;
++		printk(KERN_INFO "iucv_receive returned %08x\n", rc);
+ 		return;
+ 	}
+ 	netiucv_unpack_skb(conn, conn->rx_buff);
+@@ -637,7 +634,6 @@
+ #ifdef DEBUG
+ 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
+ #endif
+-	fsm_deltimer(&conn->timer);
+ 	if (conn && conn->netdev && conn->netdev->priv)
+ 		privptr = (netiucv_priv *)conn->netdev->priv;
+ 	conn->prof.tx_pending--;
+@@ -645,11 +641,12 @@
+ 		if ((skb = skb_dequeue(&conn->commit_queue))) {
+ 			atomic_dec(&skb->users);
+ 			dev_kfree_skb_any(skb);
 -		}
--	} 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 (privptr) {
+-			privptr->stats.tx_packets++;
+-			privptr->stats.tx_bytes +=
+-				(skb->len - NETIUCV_HDRLEN - NETIUCV_HDRLEN);
++			if (privptr) {
++				privptr->stats.tx_packets++;
++				privptr->stats.tx_bytes +=
++					(skb->len - NETIUCV_HDRLEN 
++						  - NETIUCV_HDRLEN);
++			}
+ 		}
+ 	}
+ 	conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head;
+@@ -677,8 +674,6 @@
+ 		memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header,
+ 		       NETIUCV_HDRLEN);
+ 
+-		fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
+-			     CONN_EVENT_TIMER, conn);
+ 		conn->prof.send_stamp = xtime;
+ 		rc = iucv_send(conn->pathid, NULL, 0, 0, 0, 0,
+ 			       conn->tx_buff->data, conn->tx_buff->len);
+@@ -688,12 +683,11 @@
+ 		if (conn->prof.tx_pending > conn->prof.tx_max_pending)
+ 			conn->prof.tx_max_pending = conn->prof.tx_pending;
+ 		if (rc != 0) {
+-			fsm_deltimer(&conn->timer);
+ 			conn->prof.tx_pending--;
+ 			fsm_newstate(fi, CONN_STATE_IDLE);
+ 			if (privptr)
+ 				privptr->stats.tx_errors += txpackets;
+-			printk(KERN_DEBUG "iucv_send returned %08x\n",
++			printk(KERN_INFO "iucv_send returned %08x\n",
+ 			       rc);
+ 		} else {
+ 			if (privptr) {
+@@ -762,6 +756,7 @@
+ #ifdef DEBUG
+ 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
+ #endif
++	fsm_deltimer(&conn->timer);
+ 	fsm_newstate(fi, CONN_STATE_IDLE);
+ 	conn->pathid = eib->ippathid;
+ 	netdev->tx_queue_len = eib->ipmsglim;
+@@ -769,6 +764,19 @@
+ }
+ 
+ static void
++conn_action_conntimsev(fsm_instance *fi, int event, void *arg)
++{
++	iucv_connection *conn = (iucv_connection *)arg;
++	__u8 udata[16];
++
++	pr_debug("%s() called\n", __FUNCTION__);
++	
++	fsm_deltimer(&conn->timer);
++	iucv_sever(conn->pathid, udata);
++	fsm_newstate(fi, CONN_STATE_STARTWAIT);
++}
++
++static void
+ conn_action_connsever(fsm_instance *fi, int event, void *arg)
+ {
+ 	iucv_event *ev = (iucv_event *)arg;
+@@ -776,30 +784,17 @@
+ 	// iucv_ConnectionSevered *eib = (iucv_ConnectionSevered *)ev->data;
+ 	net_device *netdev = conn->netdev;
+ 	netiucv_priv *privptr = (netiucv_priv *)netdev->priv;
+-	int state = fsm_getstate(fi);
++	__u8 udata[16];
+ 
+ #ifdef DEBUG
+ 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
+ #endif
+-	switch (state) {
+-		case CONN_STATE_SETUPWAIT:
+-			printk(KERN_INFO "%s: Remote dropped connection\n",
+-			       netdev->name);
+-			conn->handle = 0;
+-			fsm_newstate(fi, CONN_STATE_STOPPED);
+-			fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
+-			break;
+-		case CONN_STATE_IDLE:
+-		case CONN_STATE_TX:
+-			printk(KERN_INFO "%s: Remote dropped connection\n",
+-			       netdev->name);
+-			if (conn->handle)
+-				iucv_unregister_program(conn->handle);
+-			conn->handle = 0;
+-			fsm_newstate(fi, CONN_STATE_STOPPED);
+-			fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
+-			break;
 -	}
++	fsm_deltimer(&conn->timer);
++	iucv_sever(conn->pathid, udata);
++	printk(KERN_INFO "%s: Remote dropped connection\n",
++	       netdev->name);
++	fsm_newstate(fi, CONN_STATE_STARTWAIT);
++	fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
+ }
+ 
+ static void
+@@ -807,7 +802,7 @@
+ {
+ 	iucv_event *ev = (iucv_event *)arg;
+ 	iucv_connection *conn = ev->conn;
 -
--	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;
--}
-=== drivers/s390/char/tape.c
-==================================================================
---- drivers/s390/char/tape.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/char/tape.c   (/trunk/2.4.27)   (revision 52)
-@@ -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>
++	__u16 msglimit;
+ 	int rc;
+ 
+ #ifdef DEBUG
+@@ -839,10 +834,13 @@
+ 
+ 	fsm_newstate(fi, CONN_STATE_SETUPWAIT);
+ 	rc = iucv_connect(&(conn->pathid), NETIUCV_QUEUELEN_DEFAULT, iucvMagic,
+-			  conn->userid, iucv_host, 0, NULL, NULL, conn->handle,
++			  conn->userid, iucv_host, 0, NULL, &msglimit, conn->handle,
+ 			  conn);
+ 	switch (rc) {
+ 		case 0:
++			conn->netdev->tx_queue_len = msglimit;
++			fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
++				CONN_EVENT_TIMER, conn);
+ 			return;
+ 		case 11:
+ 			printk(KERN_NOTICE
+@@ -910,6 +908,7 @@
+ #ifdef DEBUG
+ 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
+ #endif
++	fsm_deltimer(&conn->timer);
+ 	fsm_newstate(fi, CONN_STATE_STOPPED);
+ 	netiucv_purge_skb_queue(&conn->collect_queue);
+ 	if (conn->handle)
+@@ -934,8 +933,8 @@
+ static const fsm_node conn_fsm[] = {
+ 	{ CONN_STATE_INVALID,   CONN_EVENT_START,    conn_action_inval      },
+ 	{ CONN_STATE_STOPPED,   CONN_EVENT_START,    conn_action_start      },
+-	{ CONN_STATE_STARTWAIT, CONN_EVENT_START,    conn_action_start      },
+ 
++	{ CONN_STATE_STOPPED,   CONN_EVENT_STOP,     conn_action_stop       },
+ 	{ CONN_STATE_STARTWAIT, CONN_EVENT_STOP,     conn_action_stop       },
+ 	{ CONN_STATE_SETUPWAIT, CONN_EVENT_STOP,     conn_action_stop       },
+ 	{ CONN_STATE_IDLE,      CONN_EVENT_STOP,     conn_action_stop       },
+@@ -950,6 +949,7 @@
+ 	{ CONN_STATE_TX,        CONN_EVENT_CONN_REQ, conn_action_connreject },
+ 
+ 	{ CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack    },
++	{ CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER,    conn_action_conntimsev },
+ 
+ 	{ CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever  },
+ 	{ CONN_STATE_IDLE,      CONN_EVENT_CONN_REJ, conn_action_connsever  },
+@@ -1026,6 +1026,7 @@
+ dev_action_connup(fsm_instance *fi, int event, void *arg)
+ {
+ 	net_device   *dev = (net_device *)arg;
++	netiucv_priv *privptr = (netiucv_priv *)dev->priv;
+ 
+ #ifdef DEBUG
+ 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
+@@ -1034,8 +1035,8 @@
+ 		case DEV_STATE_STARTWAIT:
+ 			fsm_newstate(fi, DEV_STATE_RUNNING);
+ 			printk(KERN_INFO
+-			       "%s: connected with remote side\n",
+-			       dev->name);
++			       "%s: connected with remote side %s\n",
++			       dev->name, privptr->conn->userid);
+ 			break;
+ 		case DEV_STATE_STOPWAIT:
+ 			printk(KERN_INFO
+@@ -1056,9 +1057,6 @@
+ static void
+ dev_action_conndown(fsm_instance *fi, int event, void *arg)
+ {
+-	net_device   *dev = (net_device *)arg;
+-	netiucv_priv *privptr = dev->priv;
+-	iucv_event   ev;
+ 
+ #ifdef DEBUG
+ 	printk(KERN_DEBUG "%s() called\n", __FUNCTION__);
+@@ -1066,10 +1064,6 @@
+ 	switch (fsm_getstate(fi)) {
+ 		case DEV_STATE_RUNNING:
+ 			fsm_newstate(fi, DEV_STATE_STARTWAIT);
+-			ev.conn = privptr->conn;
+-			fsm_event(privptr->conn->fsm, CONN_EVENT_START, &ev);
+-			break;
+-		case DEV_STATE_STARTWAIT:
+ 			break;
+ 		case DEV_STATE_STOPWAIT:
+ 			fsm_newstate(fi, DEV_STATE_STOPPED);
+@@ -1085,7 +1079,6 @@
+ 
+ 	{ DEV_STATE_STARTWAIT, DEV_EVENT_STOP,    dev_action_stop     },
+ 	{ DEV_STATE_STARTWAIT, DEV_EVENT_CONUP,   dev_action_connup   },
+-	{ DEV_STATE_STARTWAIT, DEV_EVENT_CONDOWN, dev_action_conndown },
+ 
+ 	{ DEV_STATE_RUNNING,   DEV_EVENT_STOP,    dev_action_stop     },
+ 	{ DEV_STATE_RUNNING,   DEV_EVENT_CONDOWN, dev_action_conndown },
+@@ -1141,6 +1134,7 @@
+ 				       "%s: Could not allocate tx_skb\n",
+ 				       conn->netdev->name);
+ 				rc = -ENOMEM;
++				return rc;
+ 			} else {
+ 				skb_reserve(nskb, NETIUCV_HDRLEN);
+ 				memcpy(skb_put(nskb, skb->len),
+@@ -1156,10 +1150,7 @@
+ 		header.next = 0;
+ 		memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header,  NETIUCV_HDRLEN);
+ 
+-		conn->retry = 0;
+ 		fsm_newstate(conn->fsm, CONN_STATE_TX);
+-		fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
+-			     CONN_EVENT_TIMER, conn);
+ 		conn->prof.send_stamp = xtime;
+ 		
+ 		rc = iucv_send(conn->pathid, NULL, 0, 0, 1 /* single_flag */,
+@@ -1171,7 +1162,6 @@
+ 			conn->prof.tx_max_pending = conn->prof.tx_pending;
+ 		if (rc != 0) {
+ 			netiucv_priv *privptr;
+-			fsm_deltimer(&conn->timer);
+ 			fsm_newstate(conn->fsm, CONN_STATE_IDLE);
+ 			conn->prof.tx_pending--;
+ 			privptr = (netiucv_priv *)conn->netdev->priv;
+@@ -1276,7 +1266,6 @@
+ 	 */
+ 	if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
+ 		fsm_event(privptr->fsm, DEV_EVENT_START, dev);
+-		dst_link_failure(skb);
+ 		dev_kfree_skb(skb);
+ 		privptr->stats.tx_dropped++;
+ 		privptr->stats.tx_errors++;
+@@ -1375,7 +1364,6 @@
+ 	file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL);
+ 	if (file->private_data == NULL)
+ 		return -ENOMEM;
+-	*(char *)file->private_data = '\0';
+ 	MOD_INC_USE_COUNT;
+ 	return 0;
+ }
+@@ -1427,7 +1415,6 @@
+ 	privptr->conn->max_buffsize = bs1;
+ 	if (!(dev->flags & IFF_RUNNING))
+ 		dev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN;
+-	privptr->conn->flags |= CONN_FLAGS_BUFSIZE_CHANGED;
+ 
+ 	return count;
+ }
+@@ -1441,7 +1428,6 @@
+ 	netiucv_priv *privptr;
+ 	ssize_t ret = 0;
+ 	char *p = sbuf;
+-	loff_t pos = *ppos;
+ 	int l;
+ 
+ 	if (!(dev = find_netdev_by_ino(ino)))
+@@ -1451,20 +1437,19 @@
+ 
+ 	privptr = (netiucv_priv *)dev->priv;
+ 
+-	if (!*sbuf || pos == 0)
++	if (file->f_pos == 0)
+ 		sprintf(sbuf, "%d\n", privptr->conn->max_buffsize);
+ 
+ 	l = strlen(sbuf);
+ 	p = sbuf;
+-	if (pos == (unsigned)pos && pos < l) {
+-		p += pos;
++	if (file->f_pos < l) {
++		p += file->f_pos;
+ 		l = strlen(p);
+ 		ret = (count > l) ? l : count;
+ 		if (copy_to_user(buf, p, ret))
+ 			return -EFAULT;
+ 	}
+-	pos += ret;
+-	*ppos = pos;
++	file->f_pos += ret;
+ 	return ret;
+ }
+ 
+@@ -1474,7 +1459,6 @@
+ 	file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL);
+ 	if (file->private_data == NULL)
+ 		return -ENOMEM;
+-	*(char *)file->private_data = '\0';
+ 	MOD_INC_USE_COUNT;
+ 	return 0;
+ }
+@@ -1539,7 +1523,6 @@
+ 	netiucv_priv *privptr;
+ 	ssize_t ret = 0;
+ 	char *p = sbuf;
+-	loff_t pos = *ppos;
+ 	int l;
+ 
+ 	if (!(dev = find_netdev_by_ino(ino)))
+@@ -1550,20 +1533,20 @@
+ 	privptr = (netiucv_priv *)dev->priv;
+ 
+ 
+-	if (!*sbuf || pos == 0)
++	if (file->f_pos == 0)
+ 		sprintf(sbuf, "%s\n",
+ 			netiucv_printname(privptr->conn->userid));
+ 
+ 	l = strlen(sbuf);
+ 	p = sbuf;
+-	if (pos == (unsigned)pos && pos < l) {
+-		p += pos;
++	if (file->f_pos < l) {
++		p += file->f_pos;
+ 		l = strlen(p);
+ 		ret = (count > l) ? l : count;
+ 		if (copy_to_user(buf, p, ret))
+ 			return -EFAULT;
+-		*ppos = pos + ret;
+ 	}
++	file->f_pos += ret;
+ 	return ret;
+ }
+ 
+@@ -1575,7 +1558,6 @@
+ 	file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL);
+ 	if (file->private_data == NULL)
+ 		return -ENOMEM;
+-	*(char *)file->private_data = '\0';
+ 	MOD_INC_USE_COUNT;
+ 	return 0;
+ }
+@@ -1606,7 +1588,6 @@
+ netiucv_stat_read(struct file *file, char *buf, size_t count, loff_t *off)
+ {
+ 	unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
+-	loff_t pos = *ppos;
+ 	char *sbuf = (char *)file->private_data;
+ 	net_device *dev;
+ 	netiucv_priv *privptr;
+@@ -1621,7 +1602,7 @@
+ 
+ 	privptr = (netiucv_priv *)dev->priv;
+ 
+-	if (!*sbuf || pos == 0) {
++	if (file->f_pos == 0) {
+ 		p += sprintf(p, "Device FSM state: %s\n",
+ 			     fsm_getstate_str(privptr->fsm));
+ 		p += sprintf(p, "Connection FSM state: %s\n",
+@@ -1645,14 +1626,14 @@
+ 	}
+ 	l = strlen(sbuf);
+ 	p = sbuf;
+-	if (pos == (unsigned)pos && pos < l) {
+-		p += pos;
++	if (file->f_pos < l) {
++		p += file->f_pos;
+ 		l = strlen(p);
+ 		ret = (count > l) ? l : count;
+ 		if (copy_to_user(buf, p, ret))
+ 			return -EFAULT;
+-		*ppos = pos + ret;
+ 	}
++	file->f_pos += ret;
+ 	return ret;
+ }
+ 
+@@ -2059,7 +2040,7 @@
+ static void
+ netiucv_banner(void)
+ {
+-	char vbuf[] = "$Revision: 1.23 $";
++	char vbuf[] = "$Revision: 1.21.8.6 $";
+ 	char *version = vbuf;
+ 
+ 	if ((version = strchr(version, ':'))) {
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/qeth.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/qeth.c	2006-01-30 22:25:25.000000000 -0700
+@@ -1,6 +1,6 @@
+ /*
+  *
+- * linux/drivers/s390/net/qeth.c ($Revision: 1.337 $)
++ * linux/drivers/s390/net/qeth.c ($Revision: 1.337.4.32 $)
+  *
+  * Linux on zSeries OSA Express and HiperSockets support
+  *
+@@ -28,9 +28,6 @@
+  */
+ 
+ /*
+- * The driver supports in general all QDIO driven network devices on the
+- * Hydra card.
 - *
-- ***********************************************************************
-- */
--
--#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);
+  * For all devices, three channels must be available to the driver. One
+  * channel is the read channel, one is the write channel and the third
+  * one is the channel used to control QDIO.
+@@ -149,6 +146,7 @@
+ #include <linux/trdevice.h>
+ #include <linux/etherdevice.h>
+ #include <linux/reboot.h>
++#include <linux/mii.h>
+ 
+ #include <linux/if_vlan.h>
+ #include <asm/chandev.h>
+@@ -156,8 +154,10 @@
+ #include <asm/irq.h>
+ #include <asm/s390dyn.h>
+ #include <asm/debug.h>
++#include <asm/processor.h>
+ 
+ #include <asm/qdio.h>
++#include <asm/qeth.h>
+ 
+ #include "qeth_mpc.h"
+ #include "qeth.h"
+@@ -171,7 +171,7 @@
+ static int global_stay_in_mem=0;
+ 
+ /****************** MODULE STUFF **********************************/
+-#define VERSION_QETH_C "$Revision: 1.337 $"
++#define VERSION_QETH_C "$Revision: 1.337.4.32 $"
+ static const char *version="qeth S/390 OSA-Express driver (" \
+ 	VERSION_QETH_C "/" VERSION_QETH_H "/" VERSION_QETH_MPC_H
+ 	QETH_VERSION_IPV6 QETH_VERSION_VLAN ")";
+@@ -183,10 +183,8 @@
+ 
+ /******************** HERE WE GO ***********************************/
+ 
+-#define PROCFILE_SLEEP_SEM_MAX_VALUE 0
+-#define PROCFILE_IOCTL_SEM_MAX_VALUE 3
+-static struct semaphore qeth_procfile_ioctl_lock;
+-static struct semaphore qeth_procfile_ioctl_sem;
++
++
+ static qeth_card_t *firstcard=NULL;
+ 
+ static sparebufs_t sparebufs[MAX_SPARE_BUFFERS];
+@@ -224,9 +222,12 @@
+ /* thought I could get along without forward declarations...
+  * just lazyness here */
+ static int qeth_reinit_thread(void*);
+-static void qeth_schedule_recovery(qeth_card_t *card);
++static inline void qeth_schedule_recovery(qeth_card_t *card);
++static int qeth_fake_header(struct sk_buff *skb, struct net_device *dev,
++       			    unsigned short type, void *daddr, void *saddr,
++       			    unsigned len);
+ 
+-inline static int QETH_IP_VERSION(struct sk_buff *skb)
++static inline int QETH_IP_VERSION(struct sk_buff *skb)
+ {
+         switch (skb->protocol) {
+         case ETH_P_IPV6: return 6;
+@@ -243,6 +244,21 @@
+ 		return b;
+ }
+ 
++/*                                                                             
++ * This is our local skb_unshare, only with pskb_copy instead of skb_copy.
++ * We place our headers whare Ethernet MAC was, so we do not need
++ * full skb_copy.
++ */
++static inline struct sk_buff *qeth_pskb_unshare(struct sk_buff *skb, int pri)
++{
++	struct sk_buff *nskb;
++	if (!skb_cloned(skb))
++		return skb;
++	nskb = skb_copy(skb, pri);
++	kfree_skb(skb); /* free our shared copy */
++	return nskb;
++}
++
+ static inline unsigned int qeth_get_millis(void)
+ {
+ 	__u64 time;
+@@ -291,7 +307,8 @@
+ 	set_task_state(current,TASK_RUNNING);
+ }
+ 
+-static void qeth_get_mac_for_ipm(__u32 ipm,char *mac,struct net_device *dev) {
++static inline void qeth_get_mac_for_ipm(__u32 ipm,char *mac,
++					struct net_device *dev) {
+ 	if (dev->type==ARPHRD_IEEE802_TR)
+ 		ip_tr_mc_map(ipm,mac);
+ 	else
+@@ -403,7 +420,7 @@
+ #define QETH_GET_ADDR(x) ((__u32)x)
+ #endif /* CONFIG_ARCH_S390X */
+ 
+-static int qeth_does_card_exist(qeth_card_t *card)
++static inline int qeth_does_card_exist(qeth_card_t *card)
+ {
+ 	qeth_card_t *c=firstcard;
+ 	int rc=0;
+@@ -696,6 +713,12 @@
+ 	QETH_DBF_TEXT2(0,trace,dbf_text);
+ 	QETH_DBF_TEXT2(0,setup,dbf_text);
+ 
++	if ((card->options.layer2 == DO_LAYER2) && 
++	    (!atomic_read(&card->mac_registered))) {
++		QETH_DBF_TEXT2(0,trace,"nomacaddr");
++		return -EPERM;
++	}
++		
+ 	qeth_save_dev_flag_state(card);
+ 
+ 	netif_start_queue(dev);
+@@ -718,7 +741,9 @@
+ 
+ static int qeth_is_multicast_skb_at_all(struct sk_buff *skb,int version)
+ {
+-	int i;
++	int i=RTN_UNSPEC;
++	qeth_card_t *card = (qeth_card_t *)skb->dev->priv;
++	
+ 	if (skb->dst && skb->dst->neighbour) {
+ 		i=skb->dst->neighbour->type;
+ 		return ((i==RTN_BROADCAST)||
+@@ -731,13 +756,38 @@
+ 	} else if (version==6) {
+ 		return (skb->nh.raw[24]==0xff)?RTN_MULTICAST:0;
+ 	}
+-	return 0;
++
++	if (!memcmp(skb->data,skb->dev->broadcast,6)) {
++		i=RTN_BROADCAST;
++	} else {
++	        __u16 hdr_mac;
++	        hdr_mac=*((__u16*)skb->data);
++	        /* tr multicast? */
++	        switch (card->link_type) {
++	        case QETH_MPC_LINK_TYPE_HSTR:
++	        case QETH_MPC_LINK_TYPE_LANE_TR:
++	        	if ( (hdr_mac==QETH_TR_MC_MAC_NC) ||
++			     (hdr_mac==QETH_TR_MC_MAC_C) )
++				i = RTN_MULTICAST;
++			break;
++	        /* eth or so multicast? */
++                default:
++                      	if ( (hdr_mac==QETH_ETH_MC_MAC_V4) ||
++			     (hdr_mac==QETH_ETH_MC_MAC_V6) )
++			        i = RTN_MULTICAST;
++	        }
++        }
++	return ((i==RTN_BROADCAST)||
++	        (i==RTN_MULTICAST)||
++	        (i==RTN_ANYCAST))?i:0;
+ }
+ 
+ static int qeth_get_prioqueue(qeth_card_t *card,struct sk_buff *skb,
+ 			      int multicast,int version)
+ {
+-	if (!version) return QETH_DEFAULT_QUEUE;
++	if (!version &&
++	    (card->type==QETH_CARD_TYPE_OSAE)) 
++		return QETH_DEFAULT_QUEUE;
+         switch (card->no_queues) {
+         case 1:
+                 return 0;
+@@ -901,6 +951,7 @@
+ 			sprintf(dbf_text,"CBOT%04x",card->irq0);
+ 			QETH_DBF_TEXT1(0,trace,dbf_text);
+ 			qeth_set_dev_flag_norunning(card);
++			netif_carrier_off(card->dev);
+                         problem=0;
+ 			goto out;
+                 }
+@@ -910,6 +961,7 @@
+ 				atomic_set(&card->is_startlaned,1);
+ 				problem=PROBLEM_CARD_HAS_STARTLANED;
+ 			}
++			netif_carrier_on(card->dev);
+ 			goto out;
+                 }
+ 		if ( *(PDU_ENCAPSULATION(buffer))==
+@@ -1046,7 +1098,7 @@
+ 	return retval;
+ }
+ 
+-static int qeth_get_spare_buf(void)
++static inline int qeth_get_spare_buf(void)
+ {
+ 	int i=0;
+ 	char dbf_text[15];
+@@ -1131,8 +1183,8 @@
+ 	}
+ }
+ 
+-static void qeth_queue_input_buffer(qeth_card_t *card,int bufno,
+-				    unsigned int under_int)
++static inline void qeth_queue_input_buffer(qeth_card_t *card,int bufno,
++	       				   unsigned int under_int)
+ {
+ 	int count=0,start=0,stop=0,pos;
+ 	int result;
+@@ -1283,10 +1335,11 @@
+ 	return skb;
+ }
+ 
+-static struct sk_buff *qeth_get_next_skb(qeth_card_t *card,
+-	       				 int *element_ptr,int *pos_in_el_ptr,
+-			       		 void **hdr_ptr,
+-					 qdio_buffer_t *buffer)
++static inline struct sk_buff *qeth_get_next_skb(qeth_card_t *card,
++		     				int *element_ptr,
++						int *pos_in_el_ptr,
++		       				void **hdr_ptr,
++		       				qdio_buffer_t *buffer)
+ {
+ 	int length;
+ 	char *data_ptr;
+@@ -1357,7 +1410,9 @@
+ 	}
+ 
+ 	*hdr_ptr=SBALE_ADDR(element)+pos_in_el;
 -
--/* 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;
++        if (card->options.layer2 == DO_LAYER2)
++		length=*(__u16*)((char*)(*hdr_ptr)+QETH_HEADER2_LEN_POS);
++        else
+ 	length=*(__u16*)((char*)(*hdr_ptr)+QETH_HEADER_LEN_POS);
+ 
+ #ifdef QETH_DBF_LIKE_HELL
+@@ -1394,10 +1449,6 @@
+ 		skb=qeth_get_skb(length+QETH_FAKE_LL_LEN);
+ 		if (!skb) goto nomem;
+ 		skb_pull(skb,QETH_FAKE_LL_LEN);
+-		if (!skb) {
+-			dev_kfree_skb_irq(skb);
+-			goto nomem;
+-		}
+ 	} else {
+ 		skb=qeth_get_skb(length);
+ 		if (!skb) goto nomem;
+@@ -1472,8 +1523,8 @@
+ 	return NULL;
+ }
+ 
+-static void qeth_transform_outbound_addrs(qeth_card_t *card,
+-					  qdio_buffer_t *buffer)
++static inline void qeth_transform_outbound_addrs(qeth_card_t *card,
++	       					 qdio_buffer_t *buffer)
+ {
+ 	int i;
+ 	void *ptr;
+@@ -1485,7 +1536,8 @@
+ 		}
+ 	}
+ }
+-static void qeth_get_linux_addrs_for_buffer(qeth_card_t *card,int buffer_no)
++static inline void qeth_get_linux_addrs_for_buffer(qeth_card_t *card,
++						   int buffer_no)
+ {
+ 	int i;
+ 	void *ptr;
+@@ -1501,7 +1553,7 @@
+ 	}
+ }
+ 
+-static void qeth_read_in_buffer(qeth_card_t *card,int buffer_no)
++static inline void qeth_read_in_buffer(qeth_card_t *card,int buffer_no)
+ {
+         struct sk_buff *skb;
+         void *hdr_ptr;
+@@ -1511,8 +1563,10 @@
+ 	unsigned short cast_type;
+ #ifdef QETH_VLAN
+ 	__u16 *vlan_tag;
++	__u16 l2_vlan_tag=0;
+ #endif
+ 	int i;
++	__u32 l2_cast_type=0;
+ 	int max_elements;
+ 	char dbf_text[15];
+ 	struct net_device *dev;
+@@ -1559,12 +1613,41 @@
+ 		if (skb) {
+ 			skb->dev=dev;
+ 
+-#ifdef QETH_IPV6
++			/* QDIO header type 2 -> layer 2 layout */
++			if ( (*(__u8 *)(hdr_ptr))==2) {
++		        	l2_cast_type = (*(__u8*)(hdr_ptr+3));
++				/* unicast is probably most of the traffic,
++				 * so we don't use the default branch down at
++				 * the bottom for this */
++		                if (l2_cast_type &
++		                    QETH_QDIO_HEADER2_FLAG_UNICAST_FRAME)
++		                	skb->pkt_type = PACKET_HOST;
++		                else if (l2_cast_type & 
++					 QETH_QDIO_HEADER2_FLAG_MULTICAST_FRAME)
++		                	skb->pkt_type = PACKET_MULTICAST;
++		                else if (l2_cast_type &
++		                         QETH_QDIO_HEADER2_FLAG_BROADCAST_FRAME)
++		                        skb->pkt_type = PACKET_BROADCAST;
++				else /* default: unicast */
++					skb->pkt_type = PACKET_HOST;
++				version=0;
++
++#ifdef QETH_VLAN
++ 				if (l2_cast_type==
++  				     QETH_QDIO_HEADER2_FLAG_VLAN_FRAME)
++  					l2_vlan_tag=
++						*(__u16*)(((__u8*)hdr_ptr)+10);
++#endif
++	                       skb->protocol=card->type_trans(skb,dev);
++			       if(card->options.checksum_type==NO_CHECKSUMMING)
++					skb->ip_summed = CHECKSUM_UNNECESSARY;
++			       else
++					skb->ip_summed = CHECKSUM_NONE;
++                               goto jump_layer2;
++			}
+ 			if ( (*(__u16 *)(hdr_ptr))&(QETH_HEADER_PASSTHRU) ) {
+ 				skb->protocol=card->type_trans(skb,dev);
+-			} else
+-#endif /* QETH_IPV6 */
+-			{
++			} else {
+ 				version=((*(__u16 *)(hdr_ptr))&
+ 					 (QETH_HEADER_IPV6))?6:4;
+ 				skb->protocol=htons((version==4)?ETH_P_IP:
+@@ -1597,7 +1680,27 @@
+ 					sprintf(dbf_text,"castun%2x",cast_type);
+ 					QETH_DBF_TEXT2(1,trace,dbf_text);
+ 				}
 -
--#ifdef TAPE_DEBUG
--debug_info_t *tape_debug_area = NULL;
++#ifdef QETH_VLAN
++				if (*(__u8*)(hdr_ptr+11)&
++				    QETH_EXT_HEADER_VLAN_FRAME) {
++					vlan_tag=(__u16 *)skb_push(skb,
++								   VLAN_HLEN);
++					/*
++					 if (*(__u8*)(hdr_ptr+11) & 
++					     QETH_EXT_HEADER_INCLUDE_VLAN_TAG) {
++				   	   *vlan_tag = *(__u16*)(hdr_ptr+28);
++			  		   *(vlan_tag+1)= *(__u16*)(hdr_ptr+30);
++			 		 } else {
++					 */
++				    	*vlan_tag = *(__u16*)(hdr_ptr+12);
++			 		*(vlan_tag+1) = skb->protocol;
++					/*
++					   }
++					 */
++					skb->protocol=
++						__constant_htons(ETH_P_8021Q);
++				}
++#endif				
+ 		if (card->options.fake_ll==FAKE_LL) {
+ 			skb->mac.raw=skb->data-QETH_FAKE_LL_LEN;
+ 			if (skb->pkt_type==PACKET_MULTICAST) {
+@@ -1643,7 +1746,7 @@
+ 			} else {
+ 				/* clear source MAC for security reasons */
+ 				memset(skb->mac.raw+
+-				       QETH_FAKE_LL_DEST_MAC_POS,0,
++				       QETH_FAKE_LL_SRC_MAC_POS,0,
+ 				       QETH_FAKE_LL_ADDR_LEN);
+ 			}
+ 			memcpy(skb->mac.raw+
+@@ -1654,58 +1757,51 @@
+ 			skb->mac.raw=skb->data;
+ 		}
+ 
+-		skb->ip_summed=card->options.checksum_type;
+ 		if (card->options.checksum_type==HW_CHECKSUMMING) {
+ 			/* do we have a checksummed packet? */
+-			if (*(__u8*)(hdr_ptr+11)&
+-			    QETH_EXT_HEADER_CSUM_TRANSP_REQ) {
+-				/* skb->ip_summed is set already */
+ 
+-				/* vlan is not an issue here, it's still in
++			/* we only check for TCP/UDP checksums when the
++			 * pseudo header was also checked sucessfully -- for
++			 * the rest of the packets, it's not clear, whether
++			 * the upper layer csum is alright. And they
++			 * shouldn't occur too often anyway in real life */
++			if ( (*(__u8*)(hdr_ptr+11)&
++			      (QETH_EXT_HEADER_CSUM_HDR_REQ|
++			       QETH_EXT_HEADER_CSUM_TRANSP_REQ)) ==
++			      (QETH_EXT_HEADER_CSUM_HDR_REQ|
++			       QETH_EXT_HEADER_CSUM_TRANSP_REQ) ) {
++				/* csum does not need to be set
++				 * inbound anyway
++				 *
++				 * vlan is not an issue here, it's still in
+ 				 * the QDIO header, not pushed in the
+-				 * skb yet */
++				 * skb yet *
+ 				int ip_len=(skb->data[0]&0x0f)<<2;
+ 
+ 				if (*(__u8*)(hdr_ptr+11)&
+ 				    QETH_EXT_HEADER_CSUM_TRANSP_FRAME_TYPE) {
+-					/* get the UDP checksum */
++					* get the UDP checksum *
+ 					skb->csum=*(__u16*)
+ 						(&skb->data[ip_len+
+ 						 QETH_UDP_CSUM_OFFSET]);
+ 				} else {
+-					/* get the TCP checksum */
++					* get the TCP checksum *
+ 					skb->csum=*(__u16*)
+ 						(&skb->data[ip_len+
+ 						 QETH_TCP_CSUM_OFFSET]);
+ 				}
++				 */
++				skb->ip_summed=CHECKSUM_UNNECESSARY;
+ 			} else {
+ 				/* make the stack check it */
+-				skb->ip_summed=SW_CHECKSUMMING;
++				skb->ip_summed=CHECKSUM_NONE;
+ 			}
++		} else {
++			skb->ip_summed=card->options.checksum_type;
+ 		}
+ 
+-#ifdef QETH_VLAN
+-				if (*(__u8*)(hdr_ptr+11)&
+-				    QETH_EXT_HEADER_VLAN_FRAME) {
+-					vlan_tag=(__u16 *)skb_push(skb,
+-								   VLAN_HLEN);
+-					/*
+-					 if (*(__u8*)(hdr_ptr+11) & 
+-					     QETH_EXT_HEADER_INCLUDE_VLAN_TAG) {
+-				   	   *vlan_tag = *(__u16*)(hdr_ptr+28);
+-			  		   *(vlan_tag+1)= *(__u16*)(hdr_ptr+30);
+-			 		 } else {
+-					 */
+-				    	*vlan_tag = *(__u16*)(hdr_ptr+12);
+-			 		*(vlan_tag+1) = skb->protocol;
+-					/*
+-					   }
+-					 */
+-					skb->protocol=
+-						__constant_htons(ETH_P_8021Q);
+-				}
+-#endif				
+ 			}
++jump_layer2:
+ 
+ #ifdef QETH_PERFORMANCE_STATS
+ 			card->perf_stats.inbound_time+=
+@@ -1718,7 +1814,19 @@
+ 			QETH_DBF_TEXT6(0,trace,dbf_text);
+ #endif /* QETH_DBF_LIKE_HELL */
+ 
++#ifdef QETH_VLAN
++			if (l2_vlan_tag) {
++				/* the tag was in the VLAN information has
++				 * been in the QDIO header, therefore remove
++				 * the tag for the hw acceleration function */
++				skb_pull(skb,VLAN_HLEN);
++				vlan_hwaccel_rx(skb,card->vlangrp,l2_vlan_tag);
++			} else {
++				netif_rx(skb);
++			}
++#else
+ 			netif_rx(skb);
++#endif /* QETH_VLAN */
+ 
+ 			card->stats->rx_packets++;
+ 			card->stats->rx_bytes+=skb->len;
+@@ -1735,39 +1843,95 @@
+ 				   buffer_no]);
+ }
+ 
+-static void qeth_fill_header(qeth_hdr_t *hdr,struct sk_buff *skb,
+-	       		     int version,int multicast)
++static inline void qeth_fill_header(qeth_hdr_t *hdr,struct sk_buff *skb,
++		     		    int version,int multicast)
+ {
+ #ifdef QETH_DBF_LIKE_HELL
+ 	char dbf_text[15];
+ #endif /* QETH_DBF_LIKE_HELL */
+-#ifdef QETH_VLAN
+ 	qeth_card_t *card;
 -#endif
++	int is_l2=0;
++
++	card = (qeth_card_t *)skb->dev->priv;
+ 
++	if (card->options.layer2 == DO_LAYER2) {
++		is_l2=1;
++	} else {
+ 	hdr->id=1;
+ 	hdr->ext_flags=0;
+ 
++		/* as skb->len includes the header now */
++		hdr->length=skb->len-QETH_HEADER_SIZE; 
+ #ifdef QETH_VLAN
+         /* before we're going to overwrite 
+-	   this location with next hop ip 
+-	*/
+-	card = (qeth_card_t *)skb->dev->priv;
++	 * this location with next hop ip.
++	 * v6 uses passthrough, v4 sets the tag in the QDIO header */
+ 	if ((card->vlangrp != NULL) && 
+-	    (version == 4)          &&
+-	    vlan_tx_tag_present(skb)) 
+-	{
+-		hdr->ext_flags = QETH_EXT_HEADER_VLAN_FRAME;
++		    vlan_tx_tag_present(skb)) {
++		if (version == 4) {
++			hdr->ext_flags = QETH_EXT_HEADER_VLAN_FRAME;
++		} else {
++				hdr->ext_flags =
++					QETH_EXT_HEADER_INCLUDE_VLAN_TAG;
++		}
+ 		hdr->vlan_id  = vlan_tx_tag_get(skb);
+ 	}
+ #endif
++	}
+ 
+-	hdr->length=skb->len-QETH_HEADER_SIZE; /* as skb->len includes
+-						  the header now */
 -
--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;
+-	/* yes, I know this is doubled code, but a small little bit
+-	   faster maybe */
+-	if (version==4) { /* IPv4 */
++	if (is_l2) {
++		/* set byte 0 to "0x02" and byte 3 to casting flags */
++		if (multicast==RTN_MULTICAST) {
++			*(__u32*)hdr=(2<<24)+
++				QETH_QDIO_HEADER2_FLAG_MULTICAST_FRAME;
++		} else if (multicast==RTN_BROADCAST) {
++			*(__u32*)hdr=(2<<24)+
++				QETH_QDIO_HEADER2_FLAG_BROADCAST_FRAME;
++ 		} else {
++ 			/* do it on our own :-( */
++ 			if (!memcmp(skb->data+QETH_HEADER_SIZE,
++ 				    skb->dev->broadcast,6)) { /* broadcast? */
++ 				*(__u32*)hdr=(2<<24)+
++ 					QETH_QDIO_HEADER2_FLAG_BROADCAST_FRAME;
++ 			} else {
++ 				__u16 hdr_mac;
++ 				hdr_mac=*((__u16*)skb->data);
++ 				/* tr multicast? */
++ 				switch (card->link_type) {
++ 				case QETH_MPC_LINK_TYPE_HSTR:
++ 				case QETH_MPC_LINK_TYPE_LANE_TR:
++ 					if ( (hdr_mac==QETH_TR_MC_MAC_NC) ||
++ 					     (hdr_mac==QETH_TR_MC_MAC_C) )
++						*(__u32*)hdr=(2<<24)+
++ 					QETH_QDIO_HEADER2_FLAG_MULTICAST_FRAME;
++					else
++						*(__u32*)hdr=(2<<24)+
++ 					QETH_QDIO_HEADER2_FLAG_UNICAST_FRAME;
++ 					break;
++ 				/* eth or so multicast? */
++ 				default:
++ 					if ( (hdr_mac==QETH_ETH_MC_MAC_V4) ||
++ 					     (hdr_mac==QETH_ETH_MC_MAC_V6) )
++						*(__u32*)hdr=(2<<24)+
++ 					QETH_QDIO_HEADER2_FLAG_MULTICAST_FRAME;
++					else
++						*(__u32*)hdr=(2<<24)+
++ 					QETH_QDIO_HEADER2_FLAG_UNICAST_FRAME;
++ 				}
++ 			}
++ 		}
++
++ 		*(__u16*)(((__u8*)hdr)+6)=skb->len-QETH_HEADER_SIZE;
++ #ifdef QETH_VLAN
++ 		/* VSWITCH relies on the VLAN information to be present
++ 		 * in the QDIO header */
++ 		if ((card->vlangrp != NULL) && 
++ 		    vlan_tx_tag_present(skb)) {
++ 			*(__u32*)hdr|=QETH_QDIO_HEADER2_FLAG_VLAN_FRAME;
++ 			*(__u16*)(((__u8*)hdr)+10)=vlan_tx_tag_get(skb);
++ 		}
++ #endif
++ 	} else if (version==4) { /* IPv4 */
+ 		if (multicast==RTN_MULTICAST) {
+ 			hdr->flags=QETH_CAST_MULTICAST;
+ 		} else if (multicast==RTN_BROADCAST) {
+@@ -1820,7 +1984,13 @@
+ 			    skb->dev->broadcast,6)) { /* broadcast? */
+ 			hdr->flags=QETH_CAST_BROADCAST|QETH_HEADER_PASSTHRU;
+ 		} else {
+-			hdr->flags=QETH_CAST_UNICAST|QETH_HEADER_PASSTHRU;
++			if (multicast==RTN_MULTICAST) {
++				hdr->flags=QETH_CAST_MULTICAST|
++					QETH_HEADER_PASSTHRU;
++			} else {
++				hdr->flags=QETH_CAST_UNICAST|
++					QETH_HEADER_PASSTHRU;
++			}
+ 		}
+ 	}
+ #ifdef QETH_DBF_LIKE_HELL
+@@ -1836,7 +2006,7 @@
+ #endif /* QETH_DBF_LIKE_HELL */
+ }
+ 
+-static int inline qeth_fill_buffer(qdio_buffer_t *buffer,char *dataptr,
++static inline int qeth_fill_buffer(qdio_buffer_t *buffer,char *dataptr,
+ 		       		   int length,int element)
+ {
+ 	int length_here;
+@@ -1891,8 +2061,8 @@
+ 	return element;
+ }
+ 
+-static void qeth_flush_packed_packets(qeth_card_t *card,int queue,
+-				      int under_int)
++static inline void qeth_flush_packed_packets(qeth_card_t *card,int queue,
++	       				     int under_int)
+ {
+ 	qdio_buffer_t *buffer;
+ 	int result;
+@@ -1951,8 +2121,23 @@
+ 	 * adapter honors it or not */
+ 	switch (card->send_state[queue]) {
+  	case SEND_STATE_DONT_PACK:
++		/* only request a PCI, if the fill level of the queue
++		 * is close to the high watermark, so that we don't
++		 * loose initiative during packing */
+ 		if (atomic_read(&card->outbound_used_buffers[queue])
+ 		    <HIGH_WATERMARK_PACK-WATERMARK_FUZZ) break;
++
++		last_pci=atomic_read(&card->last_pci_pos[queue]);
++		/* compensate queues that wrapped around */
++		if (position_for_do_qdio<last_pci)
++			last_pci-=QDIO_MAX_BUFFERS_PER_Q;
++
++		/* reduce the number of PCIs in cases where we are always
++		 * a little below the high watermark for packing -- request
++		 * PCIs less frequently */
++		if (position_for_do_qdio-last_pci<
++		    HIGH_WATERMARK_PACK-WATERMARK_FUZZ) break;
++
+ 		/* set the PCI bit */
+ 		card->outbound_ringbuffer[queue]->
+ 			buffer[position_for_do_qdio].element[0].flags|=0x40;
+@@ -1966,13 +2151,13 @@
+ 		 * last_pci is the position of the last pci we've set
+ 		 * position_for_do_qdio is the position we will send out now
+ 		 * outbound_used_buffers is the number of buffers used (means
+-		 *   all buffers hydra has, inclusive position_for_do_qdio)
++		 *   all buffers OSA has, inclusive position_for_do_qdio)
+ 		 *
+ 		 * we have to request a pci, if we have got the buffer of the
+ 		 * last_pci position back.
+ 		 *
+ 		 * position_for_do_qdio-outbound_used_buffers is the newest
+-		 *   buffer that we got back from hydra
++		 *   buffer that we got back from OSA
+ 		 *
+ 		 * if this is greater or equal than the last_pci position,
+ 		 * we should request a pci, as no pci request is
+@@ -2055,8 +2240,8 @@
+ 	return ERROR_LINK_FAILURE; /* should never happen */
+ }
+ 
+-static void qeth_free_buffer(qeth_card_t *card,int queue,int bufno,
+-	       		     int qdio_error,int siga_error)
++static inline void qeth_free_buffer(qeth_card_t *card,int queue,int bufno,
++		     		    int qdio_error,int siga_error)
+ {
+ 	struct sk_buff *skb;
+ 	int error;
+@@ -2139,7 +2324,8 @@
+ 			case ERROR_LINK_FAILURE:
+ 			case ERROR_KICK_THAT_PUPPY:
+ 				QETH_DBF_TEXT4(0,trace,"endeglnd");
+-				dst_link_failure(skb);
++				card->stats->tx_dropped++;
++				card->stats->tx_errors++;
+ 				atomic_dec(&skb->users);
+ 				dev_kfree_skb_irq(skb);
+ 				break;
+@@ -2164,7 +2350,7 @@
+ 	card->send_retries[queue][bufno]=0;
+ }
+ 
+-static void qeth_free_all_skbs(qeth_card_t *card)
++static inline void qeth_free_all_skbs(qeth_card_t *card)
+ {
+ 	int q,b;
+ 
+@@ -2199,7 +2385,7 @@
+ }
+ #ifdef QETH_VLAN
+ 
+-void qeth_insert_ipv6_vlan_tag(struct sk_buff *__skb)
++static inline void qeth_insert_ipv6_vlan_tag(struct sk_buff *__skb)
+ {
+ 
+ 	/* Move the mac addresses to the beginning of the new header.
+@@ -2230,9 +2416,9 @@
+ 
+ 
+ 
+-static void qeth_send_packet_fast(qeth_card_t *card,struct sk_buff *skb,
+-				  struct net_device *dev,
+-				  int queue,int version,int multicast)
++static inline void qeth_send_packet_fast(qeth_card_t *card,struct sk_buff *skb,
++	       				 struct net_device *dev,
++	       				 int queue,int version,int multicast)
+ {
+ 	qeth_ringbuffer_element_t *mybuffer;
+ 	int position;
+@@ -2250,8 +2436,8 @@
+ 		if ((version)&&(!card->realloc_message)) {
+ 			card->realloc_message=1;
+ 			PRINT_WARN("%s: not enough headroom in skb. " \
+-				   "Try increasing the " \
+-				   "add_hhlen parameter by %i.\n",
++				   "Increasing the " \
++				   "add_hhlen parameter by %i may help.\n",
+ 				   card->dev_name,
+ 				   QETH_HEADER_SIZE-skb_headroom(skb));
+ 		}
+@@ -2277,9 +2463,11 @@
+ 		skb=nskb;
+ 	}
+ #ifdef QETH_VLAN
++	/* ATT: this assumes, L2 does want the VLAN tag in the payload,
++	 * otherwise remove (version==0) */
+ 	if ( (card->vlangrp != NULL) &&
+              vlan_tx_tag_present(skb) &&
+-             (version==6)) {
++             ((version==6)||(version==0))) {
+                 qeth_insert_ipv6_vlan_tag(skb);
+         }
+ #endif
+@@ -2327,9 +2515,11 @@
+ 
+ /* no checks, if all elements are used, as then we would not be here (at most
+    127 buffers are enqueued) */
+-static void qeth_send_packet_packed(qeth_card_t *card,struct sk_buff *skb,
+-       				    struct net_device *dev,
+-       				    int queue,int version,int multicast)
++static inline void qeth_send_packet_packed(qeth_card_t *card,
++					   struct sk_buff *skb,
++					   struct net_device *dev,
++					   int queue,int version,
++					   int multicast)
+ {
+ 	qeth_ringbuffer_element_t *mybuffer;
+ 	int elements_needed;
+@@ -2373,9 +2563,11 @@
+ 		skb=nskb;
+ 	}
+ #ifdef QETH_VLAN
++	/* ATT: this assumes, L2 does want the VLAN tag in the payload,
++	 * otherwise remove (version==0) */
+         if ( (card->vlangrp != NULL) &&
+              vlan_tx_tag_present(skb) &&
+-             (version==6)) {
++             ((version==6)||(version==0))) {
+                 qeth_insert_ipv6_vlan_tag(skb);
+         }
+ 
+@@ -2490,16 +2682,25 @@
+ 	return old_val;
+ }
+ 
+-static int qeth_do_send_packet(qeth_card_t *card,struct sk_buff *skb,
+-       			       struct net_device *dev)
++static inline int qeth_do_send_packet(qeth_card_t *card,struct sk_buff *skb,
++	       			      struct net_device *dev)
+ {
+         int queue,result=0;
+ 	int multicast,version;
+ 	char dbf_text[15];
+ 	char dbf_text2[15]="stchupXX";
+ 
++	if (card->options.layer2 == DO_LAYER2)
++		version=0;
++	else
+ 	version=QETH_IP_VERSION(skb);
+ 	multicast=qeth_is_multicast_skb_at_all(skb,version);
++	if ((multicast == RTN_BROADCAST) && (card->broadcast_capable == 0)) {
++		card->stats->tx_dropped++;
++		card->stats->tx_errors++;
++		dev_kfree_skb_irq(skb);
++		return 0;
++	}
+         queue=qeth_get_prioqueue(card,skb,multicast,version);
+ 
+ #ifdef QETH_DBF_LIKE_HELL
+@@ -2610,21 +2811,37 @@
+         qeth_card_t *card;
+ 	char dbf_text[15];
+ 	int result;
++	unsigned long stackptr;
+ 
+         card=(qeth_card_t*)(dev->priv);
+ 
+-        if (skb==NULL)
+-		return 0;
++#ifdef CONFIG_ARCH_S390X
++	asm volatile ("lgr %0,15" : "=d" (stackptr));
++#else /* CONFIG_ARCH_S390X */
++	asm volatile ("lr %0,15" : "=d" (stackptr));
++#endif /* CONFIG_ARCH_S390X */
++	/* prevent stack overflows */
++	/* normal and async stack is both 8k on s390 and 16k on s390x,
++	 * so it doesn't matter whether we're in an interrupt */
++	if ( (stackptr & STACK_PTR_MASK)<
++	     (sizeof(struct task_struct) + WORST_CASE_STACK_USAGE) ) {
++		PRINT_ERR("delaying packet transmission " \
++		       	  "due to potential stack overflow\n");
++		sprintf(dbf_text,"STOF%4x",card->irq0);
++		QETH_DBF_TEXT1(1,trace,dbf_text);
++		PRINT_ERR("Backtrace follows:\n");
++		show_trace((unsigned long *)stackptr);
++		return -EBUSY;
++	}
+ 
+ #ifdef QETH_DBF_LIKE_HELL
+ 	QETH_DBF_HEX4(0,data,skb->data,__max(QETH_DBF_DATA_LEN,skb->len));
+ #endif /* QETH_DBF_LIKE_HELL */
+ 
+-	netif_stop_queue(dev);
 -
--inline void
--tape_mkdevfsroots (tape_info_t* ti) 
+ 	if (!card) {
+ 		QETH_DBF_TEXT2(0,trace,"XMNSNOCD");
+-		dst_link_failure(skb);
++		card->stats->tx_dropped++;
++		card->stats->tx_errors++;
+ 		dev_kfree_skb_irq(skb);
+ 		return 0;
+ 	}
+@@ -2637,11 +2854,31 @@
+ 		card->stats->tx_carrier_errors++;
+ 		sprintf(dbf_text,"XMNS%4x",card->irq0);
+ 		QETH_DBF_TEXT2(0,trace,dbf_text);
+-		dst_link_failure(skb);
++		card->stats->tx_dropped++;
++		card->stats->tx_errors++;
+ 		dev_kfree_skb_irq(skb);
+ 		return 0;
+ 	}
+ 
++	if (dev->hard_header == qeth_fake_header) {
++		/*
++		 * in theory, if we run in undef-ed QETH_IPV6, we should
++		 * always unshare, because we do skb_push, then overwrite
++		 * that place with OSA header in qeth_send_packet_fast().
++		 * But it is only visible to one application - tcpdump.
++		 * Nobody else cares if (fake) MAC header gets smashed.
++		 * So, we only do it if fake_ll is in effect.
++		 */
++		if ((skb = qeth_pskb_unshare(skb, GFP_ATOMIC)) == NULL) {
++			card->stats->tx_dropped++;
++			dev_kfree_skb_irq(skb);
++			return 0;
++		}
++		skb_pull(skb, QETH_FAKE_LL_LEN);
++	}
++
++	netif_stop_queue(dev);
++
+ 	result=qeth_do_send_packet(card,skb,dev);
+ 
+ 	if (!result)
+@@ -2650,6 +2887,36 @@
+ 	return result;
+ }
+ 
++/*
++ * This function is needed to tell af_packet.c to process headers.
++ * It is not called from there, but only from the transmit path,
++ * when we do not need any actual header.
++ *
++ * N.B. Why do we insist on kludging here instead of fixing tcpdump?
++ * Because tcpdump is shared among gazillions of platforms, and
++ * there is a) no reliable way to identify qeth or its packets
++ * in pcap-linux.c (sll->sll_halen is the only hope); b) no easy
++ * way to pass this information from libpcap to tcpdump proper.
++ *
++ * XXX This fails with TR: traffic flows ok, but tcpdump remains confused.
++ */
++int qeth_fake_header(struct sk_buff *skb, struct net_device *dev,
++		     unsigned short type, void *daddr, void *saddr,
++		     unsigned len)
++{
++	unsigned char *hdr;
++
++	hdr = skb_push(skb, QETH_FAKE_LL_LEN);
++	memcpy(hdr, "FAKELLFAKELL", ETH_ALEN*2);
++	if (type != ETH_P_802_3)
++		*(u16 *)(hdr + ETH_ALEN*2) = htons(type);
++	else
++		*(u16 *)(hdr + ETH_ALEN*2) = htons(len);
++
++	/* XXX Maybe dev->hard_header_len here? Then skb_pull by same size. */
++	return QETH_FAKE_LL_LEN;
++}
++
+ static struct net_device_stats* qeth_get_stats(struct net_device *dev)
+ {
+         qeth_card_t *card;
+@@ -2803,23 +3070,19 @@
+ 	return retval;
+ }
+ 
+-static void qeth_wakeup_procfile(void) 
 -{
--    char devno [5];
--    sprintf (devno,"%04x",ti->devinfo.devno);
--    ti->devfs_dir=devfs_mk_dir (tape_devfs_root_entry, devno, ti);
+-	QETH_DBF_TEXT5(0,trace,"procwkup");
+-	if (atomic_read(&qeth_procfile_ioctl_sem.count)<
+-	    PROCFILE_SLEEP_SEM_MAX_VALUE)
+-  		up(&qeth_procfile_ioctl_sem);
 -}
 -
--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;
+-static int qeth_sleepon_procfile(void)
++static void qeth_snmp_notify(void) 
+ {
+-	QETH_DBF_TEXT5(0,trace,"procslp");
+-	if (down_interruptible(&qeth_procfile_ioctl_sem)) {
+-      		up(&qeth_procfile_ioctl_sem);
+-      		return -ERESTARTSYS;
++	/*notify all  registered processes */
++	struct list_head *l;
++	struct qeth_notify_list *n_entry;
++	
++	QETH_DBF_TEXT5(0,trace,"snmpnoti");
++	spin_lock(&notify_lock);
++	list_for_each(l, &notify_list) {
++		n_entry = list_entry(l, struct qeth_notify_list, list);
++		send_sig(n_entry->signum, n_entry->task, 1);
+ 	}
+-	return 0;
++	spin_unlock(&notify_lock);
+ }
+ 
+ static char* qeth_send_control_data(qeth_card_t *card,unsigned char *buffer,
+@@ -2889,17 +3152,19 @@
+ 		QETH_DBF_TEXT2(0,trace,"scd:doio");
+ 		sprintf(dbf_text,"%4x",(__s16)result);
+ 		QETH_DBF_TEXT2(0,trace,dbf_text);
++		/* re-enable qeth_send_control_data again */
++		atomic_set(&card->write_busy,0);
+ 		return NULL;
+ 	}
+ 
+ 	if (intparam==IPA_IOCTL_STATE) {
+ 		if (qeth_sleepon_ioctl(card,QETH_IPA_TIMEOUT)) {
+-			QETH_DBF_TEXT2(0,trace,"scd:ioctime");
++			QETH_DBF_TEXT2(0,trace,"scd:ioct");
+ 			/* re-enable qeth_send_control_data again */
+ 			atomic_set(&card->write_busy,0);
+ 			return NULL;
+ 		}
+-		rec_buf=card->ipa_buf;
++		rec_buf=card->dma_stuff->recbuf;
+ 		sprintf(dbf_text,"scro%4x",card->irq0);
+ 	} else {
+ 		if (qeth_sleepon(card,(setip)?QETH_IPA_TIMEOUT:
+@@ -2929,6 +3194,7 @@
+         ipa_cmd_t *reply;
+         int ipa_cmd;
+         int result;
++	unsigned char prot;
+ 
+ 	/* don't muck around with ipv6 if there's no use to do so */
+ 	if ( (cmd->prot_version==6) &&
+@@ -2938,7 +3204,12 @@
+ 
+         memcpy(card->send_buf,IPA_PDU_HEADER,
+ 	       IPA_PDU_HEADER_SIZE);
 -
--typedef struct {
++	if (card->options.layer2 == DO_LAYER2) {
++		prot=QETH_IPA_CMD_PROT_LAYER2;
++	} else {
++		prot=QETH_IPA_CMD_PROT_TCPIP;
++	}
++	memcpy(QETH_IPA_CMD_PROT_TYPE(card->send_buf),&prot,1);
+         memcpy(QETH_IPA_CMD_DEST_ADDR(card->send_buf),
+                &card->token.ulp_connection_r,QETH_MPC_TOKEN_LENGTH);
+ 
+@@ -2963,6 +3234,15 @@
+ 		if ((ipa_cmd==IPA_CMD_SETADAPTERPARMS)&&(result==0)) {
+ 			result=reply->data.setadapterparms.return_code;
+ 		}
++		if ( (ipa_cmd==IPA_CMD_SETASSPARMS) &&
++		     (result==0) &&
++		     (reply->data.setassparms.assist_no==
++		      IPA_INBOUND_CHECKSUM) &&
++		     (reply->data.setassparms.command_code==
++		      IPA_CMD_ASS_START) ) {
++			card->csum_enable_mask=
++				reply->data.setassparms.data.flags_32bit;
++		}
+ 	}
+         return result;
+ }
+@@ -2976,7 +3256,11 @@
+         cmd->seq_no=card->seqno.ipa++;
+         cmd->adapter_type=qeth_get_adapter_type_for_ipa(card->link_type);
+         cmd->rel_adapter_no=(__u8)card->options.portno;
++	if (card->options.layer2 == DO_LAYER2) {
++		cmd->prim_version_no=2;
++	} else {
+         cmd->prim_version_no=1;
++	}
+         cmd->param_count=1;
+         cmd->prot_version=ip_vers;
+         cmd->ipa_supported=0;
+@@ -3072,6 +3356,7 @@
+         int ipa_cmd;
+         int result;
+ 	__u16 s1,s2;
++	unsigned char prot;
+ 
+ 	/* don't muck around with ipv6 if there's no use to do so */
+ 	if ( (cmd->prot_version==6) &&
+@@ -3081,6 +3366,12 @@
+ 
+         memcpy(card->send_buf,IPA_PDU_HEADER,
+ 	       IPA_PDU_HEADER_SIZE);
++	if (card->options.layer2 == DO_LAYER2) {
++		prot=QETH_IPA_CMD_PROT_LAYER2;
++	} else {
++		prot=QETH_IPA_CMD_PROT_TCPIP;
++	}
++	memcpy(QETH_IPA_CMD_PROT_TYPE(card->send_buf),&prot,1);
+         memcpy(QETH_IPA_CMD_DEST_ADDR(card->send_buf),
+                &card->token.ulp_connection_r,QETH_MPC_TOKEN_LENGTH);
+         memcpy(card->send_buf+IPA_PDU_HEADER_SIZE,
+@@ -3183,17 +3474,29 @@
+ 
+ static int qeth_ioctl_handle_arp_data(qeth_card_t *card, arp_cmd_t *reply) 
+ {
++	if ( (reply->data.setassparms.command_code==
++	      IPA_CMD_ASS_ARP_SET_NO_ENTRIES) ||
++	     (reply->data.setassparms.command_code==
++	      IPA_CMD_ASS_ARP_ADD_ENTRY) ||
++	     (reply->data.setassparms.command_code==
++	      IPA_CMD_ASS_ARP_REMOVE_ENTRY) ) {
++		if (reply->data.setassparms.return_code) {
++			return ARP_RETURNCODE_ERROR;
++		} else {
++			return ARP_RETURNCODE_LASTREPLY;
++		}
++	}
++	/* check for corrupt data */
++	if (reply->data.setassparms.seq_no <= 0)
++		return ARP_RETURNCODE_ERROR;
++	
+ 	if (reply->data.setassparms.seq_no == 1) {
+ 		if (card->ioctl_buffersize <= 
+ 		    (sizeof(__u16) + sizeof(int) + reply->data.
+-		     setassparms.number_of_replies * ARP_DATA_SIZE)) {
++		     setassparms.number_of_replies * ARP_DATA_SIZE))
+ 			card->ioctl_returncode = ARP_RETURNCODE_ERROR;
+-		} else {
++		else
+ 			card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+-			card->number_of_entries = 0;
+-			card->ioctl_buffer_pointer = card->ioctl_data_buffer+
+-				sizeof(__u16) + sizeof(int);
+-		}
+ 	}
+ 
+ 	if (card->ioctl_returncode != ARP_RETURNCODE_ERROR &&
+@@ -3229,6 +3532,21 @@
+ 	}
+ 	return card->ioctl_returncode;
+ }
++static int qeth_is_arp_command(int cmd)
++{
++	switch (cmd) {
++		case IPA_CMD_ASS_ARP_SET_NO_ENTRIES:
++		case IPA_CMD_ASS_ARP_QUERY_CACHE:
++		case IPA_CMD_ASS_ARP_ADD_ENTRY:
++		case IPA_CMD_ASS_ARP_REMOVE_ENTRY:
++		case IPA_CMD_ASS_ARP_FLUSH_CACHE:
++		case IPA_CMD_ASS_ARP_QUERY_INFO:
++		case IPA_CMD_ASS_ARP_QUERY_STATS:
++			return 1;
++		default:
++			return 0;
++	}
++}
+ 
+ static int qeth_look_for_arp_data(qeth_card_t *card) 
+ {
+@@ -3245,9 +3563,7 @@
+ 		result=ARP_FLUSH;
+ 	} else if ( (reply->command == IPA_CMD_SETASSPARMS) &&
+ 	     (reply->data.setassparms.assist_no == IPA_ARP_PROCESSING) &&
+-	     (reply->data.setassparms.command_code == 
+-	      IPA_CMD_ASS_ARP_QUERY_INFO) &&
+-	     (card->ioctl_returncode == ARP_RETURNCODE_SUCCESS)) {
++	     (qeth_is_arp_command(reply->data.setassparms.command_code)) ) {
+ 		result = qeth_ioctl_handle_arp_data(card,reply);
+ 	} else if ( (reply->command == IPA_CMD_SETADAPTERPARMS)	&&
+ 	            (reply->data.setadapterparms.command_code == 
+@@ -3286,8 +3602,13 @@
+         cmd->data.setassparms.return_code=0;
+ 	cmd->data.setassparms.seq_no=0;
+ 
++	/* initialize ioctl pointers and buffer sizes */
+ 	card->ioctl_buffersize = data_size;
+ 	card->ioctl_data_buffer = (char *) vmalloc(data_size);
++	memset(card->ioctl_data_buffer, 0, data_size);
++	card->ioctl_buffer_pointer = card->ioctl_data_buffer +
++		sizeof(__u16) + sizeof(int); /* flags and # of entries */
++	card->number_of_entries = 0;
+ 	if (!card->ioctl_data_buffer) {
+ 		kfree(cmd);
+ 		return IPA_REPLY_FAILED;
+@@ -3305,8 +3626,9 @@
+ 		result = IPA_REPLY_SUCCESS;
+ 		memcpy(((char *)(card->ioctl_data_buffer)) + sizeof(__u16),
+ 		       &(card->number_of_entries),sizeof(int));
+-		copy_to_user(req->ifr_ifru.ifru_data,
+-			     card->ioctl_data_buffer,data_size);		
++		if (copy_to_user(req->ifr_ifru.ifru_data,
++			         card->ioctl_data_buffer,data_size))
++			result =-EFAULT;		
+ 	}
+ 	card->ioctl_buffer_pointer = NULL;
+ 	vfree(card->ioctl_data_buffer);
+@@ -3373,16 +3695,14 @@
+ 		result = IPA_REPLY_FAILED;
+ 		goto snmp_out;
+ 	}
+-	if (result == ARP_RETURNCODE_ERROR ) {
+-		copy_to_user(req->ifr_ifru.ifru_data+SNMP_REQUEST_DATA_OFFSET,
+-			     card->ioctl_data_buffer,card->ioctl_buffersize);
++	if (result == ARP_RETURNCODE_ERROR ) 
+ 		result = IPA_REPLY_FAILED;
+-	}
+-	else {
+-		copy_to_user(req->ifr_ifru.ifru_data+SNMP_REQUEST_DATA_OFFSET,
+-			     card->ioctl_data_buffer,card->ioctl_buffersize);
++	else 
+ 		result = IPA_REPLY_SUCCESS;
+-	}
++
++	if (copy_to_user(req->ifr_ifru.ifru_data + SNMP_REQUEST_DATA_OFFSET, 
++			 card->ioctl_data_buffer, card->ioctl_buffersize))
++		result = -EFAULT;
+ snmp_out:
+ 	card->number_of_entries = 0;
+ 	card->ioctl_buffersize = 0;
+@@ -3556,6 +3876,43 @@
+ 				 ((ipacmd==IPA_CMD_SETIPM)?IPA_SETIP_FLAG:0));
+ }
+ 
++static int qeth_send_setdelmac(qeth_card_t *card,__u8 *mac, int ipacmd)
++{
++	ipa_cmd_t cmd;
++
++	qeth_fill_ipa_cmd(card,&cmd,ipacmd,4);
++	cmd.data.setdelmac.mac_length = 6;
++	memcpy(&cmd.data.setdelmac.mac,mac,6);
++	return qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
++}
++#ifdef QETH_VLAN
++static void qeth_send_setdel_vlan(qeth_card_t *card,int i,int ipacmd)
++{
++	int result;
++	ipa_cmd_t cmd;
++	char dbf_text[15];
++
++	qeth_fill_ipa_cmd(card,&cmd,ipacmd,4);
++	cmd.data.setdelvlan.vlan_id = i;
++	result=qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
++
++	if (result) {
++		PRINT_ERR("Could not %s VLAN %i on %s: 0x%x. Continuing\n",
++			  (ipacmd==IPA_CMD_SETVLAN)?"set":"delete",i,
++			  card->dev_name,result);
++		if (ipacmd==IPA_CMD_SETVLAN) {
++			sprintf(dbf_text,"STVLF%3x",i);
++			QETH_DBF_TEXT2(0,trace,dbf_text);
++		} else {
++			sprintf(dbf_text,"DLVLF%3x",i);
++			QETH_DBF_TEXT2(0,trace,dbf_text);
++		}
++		sprintf(dbf_text,"%4x%4x",card->irq0,result);
++		QETH_DBF_TEXT2(0,trace,dbf_text);
++	}
++}
++#endif
++
+ #define PRINT_SETIP_ERROR(x) \
+ 	if (result) \
+ 		PRINT_ERR("setip%c: return code 0x%x (%s)\n",x,result, \
+@@ -3571,6 +3928,11 @@
+ 			  (result==0xe00e)?"unsupported arp assist cmd": \
+ 			  (result==0xe00f)?"arp assist not enabled": \
+ 			  (result==0xe080)?"startlan disabled": \
++			  (result==0xf012)?"unicast IP address invalid": \
++			  (result==0xf013)?"multicast router limit reached": \
++			  (result==0xf014)?"stop assist not supported": \
++			  (result==0xf015)?"multicast assist not set": \
++			  (result==0xf080)?"VM: startlan disabled": \
+ 			  (result==-1)?"IPA communication timeout": \
+ 			  "unknown return code")
+ 
+@@ -3612,7 +3974,8 @@
+ 		QETH_DBF_TEXT2(0,trace,dbf_text);
+ 	}
+ 
+-	if (((result==-1)||(result==0xe080))&&(retries--)) {
++	if ( ((result==-1)||(result==0xe080)||(result==0xf080))&&
++	     (retries--) ) {
+ 		sprintf(dbf_text,"sipr%4x",card->irq0);
+ 		QETH_DBF_TEXT2(0,trace,dbf_text);
+ 		if (ip_vers==4) {
+@@ -3648,6 +4011,29 @@
+ 	int retries;
+ 	char dbf_text[15];
+ 	
++	if (card->options.layer2 == DO_LAYER2) {
++		result=qeth_send_setdelmac(card,mac,IPA_CMD_SETGMAC);
++			return 0;
++		if (result) {
++			QETH_DBF_TEXT2(0,trace,"SETMCFLD");
++			QETH_DBF_HEX2(0,trace,mac,QETH_DBF_TRACE_LEN);
++			sprintf(dbf_text,"%4x%4x",card->irq0,result);
++			QETH_DBF_TEXT2(0,trace,dbf_text);
++			if (result==0x2005) {
++				PRINT_WARN("Group MAC " \
++					  "%02x:%02x:%02x:%02x:%02x:%02x already " \
++					  "existing on %s !\n",
++					  mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],
++					  card->dev_name);
++				result = 0;
++			} else
++				PRINT_ERR("Could not set group MAC " \
++					  "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
++					  mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],
++					  card->dev_name,result);
++		}
++		return result;
++	}
+ 	retries=(use_retries)?QETH_SETIP_RETRIES:1;
+ 	if (qeth_is_ipa_covered_by_ipato_entries(ip_vers,ip,card)) {
+ 		sprintf(dbf_text,"imto%4x",card->irq0);
+@@ -3694,6 +4080,23 @@
+ static inline int qeth_send_delipm(qeth_card_t *card,__u8 *ip,
+ 				   __u8 *mac,short ip_vers)
+ {
++	int result;
++	char dbf_text[15];
++ 
++	if (card->options.layer2 == DO_LAYER2) {
++		result=qeth_send_setdelmac(card,mac,IPA_CMD_DELGMAC);
++		if (result) {
++			QETH_DBF_TEXT2(0,trace,"DELMCFLD");
++			QETH_DBF_HEX2(0,trace,mac,QETH_DBF_TRACE_LEN);
++			sprintf(dbf_text,"%4x%4x",card->irq0,result);
++			QETH_DBF_TEXT2(0,trace,dbf_text);
++			PRINT_ERR("Could not delete group MAC " \
++				  "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
++				  mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],
++				  card->dev_name,result);
++		}
++		return result;
++	} else
+         return qeth_send_setdelipm(card,ip,mac,IPA_CMD_DELIPM,ip_vers);
+ }
+ 
+@@ -3771,8 +4174,8 @@
+      					      le is last entry */
+ 	char dbf_text[15];
+ 	int result;
+-	__u8 netmask[16]={0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+-		0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
++	__u8 netmask[16]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
++		0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ 	qeth_vipa_entry_t *priv_add_list=NULL;
+ 	qeth_vipa_entry_t *priv_del_list=NULL;
+ 
+@@ -3790,6 +4193,7 @@
+ 					GFP_KERNEL);
+ 			if (ne) {
+ 				ne->version=e->version;
++				ne->flag=e->flag;
+ 				memcpy(ne->ip,e->ip,16);
+ 				ne->next=priv_add_list;
+ 				priv_add_list=ne;
+@@ -3821,6 +4225,7 @@
+ 					GFP_KERNEL);
+ 			if (ne) {
+ 				ne->version=e->version;
++				ne->flag=e->flag;
+ 				memcpy(ne->ip,e->ip,16);
+ 				ne->next=priv_del_list;
+ 				priv_del_list=ne;
+@@ -3852,7 +4257,7 @@
+ 			sprintf(dbf_text,"%4x%4x",card->irq0,result);
+ 			QETH_DBF_TEXT2(0,trace,dbf_text);
+ 			if (priv_add_list->version==4) {
+-				PRINT_ERR("going to leave vipa/rxip %08x" \
++				PRINT_ERR("going to leave vipa/rxip x%08x " \
+ 					  "unset...\n",
+ 					  *((__u32*)&priv_add_list->ip[0]));
+ 				sprintf(dbf_text,"%08x",
+@@ -4214,7 +4619,8 @@
+ 	sprintf(dbf_text,"stim%4x",card->irq0);
+ 	QETH_DBF_TEXT3(0,trace,dbf_text);
+ 
+-        if (qeth_is_supported(IPA_MULTICASTING)) {
++        if (qeth_is_supported(IPA_MULTICASTING) ||
++	   (card->options.layer2 == DO_LAYER2)) {
+ 		addr=card->ip_mc_current_state.ipm_ifa;
+ 		while (addr) {
+ 			if (!qeth_is_ipma_in_list(addr,card->
+@@ -4404,20 +4810,127 @@
+ }
+ #endif /* QETH_IPV6 */
+ 
++/* FIXME: new values for 10gig */
++static int mdio_read(struct net_device *dev, int phy_id, int regnum)
++{
++	int ret_val=0;
++	qeth_card_t *card = (qeth_card_t*)dev->priv;
++
++	switch(regnum){
++		case MII_BMCR: /* Basic mode control register */
++			/* XXX Get real values from card */
++			ret_val = BMCR_FULLDPLX;
++
++			if ( ( card->link_type == 
++			       QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET ) ||
++			     ( card->link_type ==
++			       QETH_MPC_LINK_TYPE_10GIG_ETHERNET ) )
++				ret_val |= BMCR_SPEED1000;
++			else
++				ret_val |= BMCR_SPEED100;
++			break;
++
++		case MII_BMSR: /* Basic mode status register */
++			/* XXX Get real values from card */
++			ret_val = BMSR_ERCAP | BMSR_ANEGCOMPLETE | 
++				  BMSR_LSTATUS | BMSR_10HALF | BMSR_10FULL | 
++				  BMSR_100HALF | BMSR_100FULL | BMSR_100BASE4;
++			if ( ( card->link_type == 
++			       QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET ) ||
++			     ( card->link_type ==
++			       QETH_MPC_LINK_TYPE_10GIG_ETHERNET ) )
++				ret_val |= BMSR_EXTSTATUS;
++			
++			break;
++
++		case MII_EXTSTATUS: /* Extended status register */
++			if ( ( card->link_type == 
++			       QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET ) ||
++			     ( card->link_type ==
++			       QETH_MPC_LINK_TYPE_10GIG_ETHERNET ) )
++				ret_val = EXTSTATUS_1000TFULL | 
++					  EXTSTATUS_1000THALF | 
++					  EXTSTATUS_1000XFULL | 
++					  EXTSTATUS_1000XHALF;
++			else
++				ret_val = 0;
++			break;
++
++		case MII_PHYSID1: /* PHYS ID 1 */
++			ret_val = ( dev->dev_addr[0] << 16 ) | 
++				  ( dev->dev_addr[1] << 8) | dev->dev_addr[2];
++			ret_val = (ret_val >> 5) & 0xFFFF;
++			break;
++
++		case MII_PHYSID2: /* PHYS ID 2 */
++			ret_val = (dev->dev_addr[2] << 10) & 0xFFFF;
++			break;
++
++		case MII_ADVERTISE: /* Advertisement control reg */
++			ret_val = ADVERTISE_ALL;
++			break;
++
++		case MII_LPA: /* Link partner ability reg */
++			ret_val = LPA_10HALF | LPA_10FULL | LPA_100HALF | 
++				  LPA_100FULL | LPA_100BASE4 | LPA_LPACK;
++			break;
++
++		case MII_EXPANSION: /* Expansion register */
++			ret_val = 0;
++			break;
++		
++		default:
++			ret_val = 0;
++			break;
++	}
++
++	return ret_val;
++}
++                                                                                                                                                                             
++static void mdio_write(struct net_device *dev,int phy_id,int regnum,int value)
++{
++	/* writing MII registers is not yet implmented */
++
++	switch(regnum){
++		case MII_BMCR:          /* Basic mode control register */
++		case MII_BMSR:          /* Basic mode status register  */
++		case MII_ADVERTISE:     /* Advertisement control reg   */
++		case MII_LPA:           /* Link partner ability reg    */
++		case MII_EXPANSION:     /* Expansion register          */
++		case MII_DCOUNTER:      /* Disconnect counter          */
++		case MII_FCSCOUNTER:    /* False carrier counter       */
++		case MII_NWAYTEST:      /* N-way auto-neg test reg     */
++		case MII_RERRCOUNTER:   /* Receive error counter       */
++		case MII_SREVISION:     /* Silicon revision            */
++		case MII_LBRERROR:      /* Lpback, rx, bypass error    */
++		case MII_PHYADDR:       /* PHY address                 */
++		case MII_TPISTATUS:     /* TPI status for 10mbps       */
++		case MII_NCONFIG:       /* Network interface config    */
++		case MII_PHYSID1:       /* PHYS ID 1                   */
++		case MII_PHYSID2:       /* PHYS ID 2                   */
++		case MII_RESV1:         /* Reserved...                 */
++		case MII_RESV2:         /* Reserved...                 */
++		default:
++			break;
++	}
++}
++
+ #define QETH_STANDARD_RETVALS \
+ 		ret_val=-EIO; \
+ 		if (result==IPA_REPLY_SUCCESS) ret_val=0; \
++		if (result==-EFAULT) ret_val=-EFAULT; \
+ 		if (result==IPA_REPLY_FAILED) ret_val=-EIO; \
+ 		if (result==IPA_REPLY_OPNOTSUPP) ret_val=-EOPNOTSUPP
+ 
+ static int qeth_do_ioctl(struct net_device *dev,struct ifreq *rq,int cmd)
+ {
 -	char *data;
--	int len;
--} tempinfo_t;
+ 	int result,i,ret_val;
+ 	int version=4;
+ 	qeth_card_t *card;
+ 	char dbf_text[15];
+-	char buff[100];
++	char data[100];
++        struct mii_ioctl_data* mii_data = 
++			(struct mii_ioctl_data*)&(rq->ifr_ifru.ifru_data);
+ 
+ 	card=(qeth_card_t*)dev->priv;
+ 
+@@ -4427,20 +4940,20 @@
+ 	QETH_DBF_TEXT2(0,trace,dbf_text);
+ 	QETH_DBF_HEX2(0,trace,&rq,sizeof(void*));
+ 
+-	if ((cmd<SIOCDEVPRIVATE) || (cmd>SIOCDEVPRIVATE+5))
+-		return -EOPNOTSUPP;
+-	copy_from_user(buff,rq->ifr_ifru.ifru_data,sizeof(buff));
+-	data=buff;
 -
+ 	if ( (!atomic_read(&card->is_registered))||
+ 	     (!atomic_read(&card->is_hardsetup))||
+ 	     (atomic_read(&card->is_gone)) ) return -ENODEV;
+ 
+ 	if (atomic_read(&card->shutdown_phase)) return -ENODEV;
+ 
+-	my_spin_lock(&card->ioctl_lock);
++	if (down_interruptible ( &card->ioctl_sem ) )
++		return -ERESTARTSYS;
++
++	if (atomic_read(&card->shutdown_phase)) {
++		ret_val=-ENODEV;
++		goto out;
++	}
+ 
+-	if (atomic_read(&card->shutdown_phase)) return -ENODEV;
+ 	if ( (!atomic_read(&card->is_registered))||
+ 	     (!atomic_read(&card->is_hardsetup))||
+ 	     (atomic_read(&card->is_gone)) ) {
+@@ -4449,71 +4962,154 @@
+ 	}
+ 
+ 	switch (cmd) {
+-	case SIOCDEVPRIVATE+0:
+-		if (!capable(CAP_NET_ADMIN)) {
++	case SIOCDEVPRIVATE+0:		
++	case SIOC_QETH_ARP_SET_NO_ENTRIES:
+ 			ret_val=-EPERM;
++		if (!capable(CAP_NET_ADMIN) ||
++		     (card->options.layer2 == DO_LAYER2)) {
+ 			break;
+ 		}
+-		result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
++		result=qeth_send_setassparms(card,version,
++					     IPA_ARP_PROCESSING,
+ 					     IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
+ 					     rq->ifr_ifru.ifru_ivalue,4);
+ 		QETH_STANDARD_RETVALS;
+ 		if (result==3) ret_val=-EINVAL;
+ 		break;
+ 	case SIOCDEVPRIVATE+1:
+-		if (!capable(CAP_NET_ADMIN)) {
++	case SIOC_QETH_ARP_QUERY_INFO:
++		if (!capable(CAP_NET_ADMIN) ||
++		    (card->options.layer2 == DO_LAYER2)) {
+ 			ret_val=-EPERM;
+ 			break;
+ 		}
+-		result = qeth_queryarp(card,rq,version,IPA_ARP_PROCESSING,
++
++		if (copy_from_user(data,rq->ifr_ifru.ifru_data,
++				  sizeof(data))) {
++			ret_val = -EFAULT;
++			break;
++		}
++
++		result = qeth_queryarp(card,rq,version,
++				       IPA_ARP_PROCESSING,
+ 				       IPA_CMD_ASS_ARP_QUERY_INFO,data,4);
+ 
+ 		QETH_STANDARD_RETVALS;
+ 		break;
+ 	case SIOCDEVPRIVATE+2:
+-		if (!capable(CAP_NET_ADMIN)) {
++	case SIOC_QETH_ARP_ADD_ENTRY:
++		if (!capable(CAP_NET_ADMIN) ||
++		    (card->options.layer2 == DO_LAYER2)) {
+ 			ret_val=-EPERM;
+ 			break;
+ 		}
+-		for (i=12;i<24;i++) if (data[i]) version=6;
+-		result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
++
++		if (copy_from_user(data,rq->ifr_ifru.ifru_data,
++		 		  sizeof(data))) {
++			ret_val = -EFAULT;
++			break;
++		}
++
++		for (i=12;i<24;i++) if (data[i]) version=6;
++		result=qeth_send_setassparms(card,version,
++					     IPA_ARP_PROCESSING,
+ 					     IPA_CMD_ASS_ARP_ADD_ENTRY,
+ 					     (long)data,56);
+ 		QETH_STANDARD_RETVALS;
+ 		break;
+ 	case SIOCDEVPRIVATE+3:
+-		if (!capable(CAP_NET_ADMIN)) {
++	case SIOC_QETH_ARP_REMOVE_ENTRY:
++		if (!capable(CAP_NET_ADMIN) ||
++		    (card->options.layer2 == DO_LAYER2)) {
+ 			ret_val=-EPERM;
+ 			break;
+ 		}
+-		for (i=4;i<12;i++) if (data[i]) version=6;
+-		result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
++
++		if (copy_from_user(data,rq->ifr_ifru.ifru_data,
++				   sizeof(data))) {
++			ret_val = -EFAULT;
++			break;
++		}
++
++		for (i=12;i<24;i++) if (data[i]) version=6;
++		result=qeth_send_setassparms(card,version,
++					     IPA_ARP_PROCESSING,
+ 					     IPA_CMD_ASS_ARP_REMOVE_ENTRY,
+ 					     (long)data,16);
+ 		QETH_STANDARD_RETVALS;
+ 		break;
+ 	case SIOCDEVPRIVATE+4:
+-		if (!capable(CAP_NET_ADMIN)) {
++	case SIOC_QETH_ARP_FLUSH_CACHE:
++		if (!capable(CAP_NET_ADMIN) ||
++			(card->options.layer2 == DO_LAYER2)) {
+ 			ret_val=-EPERM;
+ 			break;
+ 		}
+-		result=qeth_send_setassparms(card,version,IPA_ARP_PROCESSING,
++
++		result=qeth_send_setassparms(card,version,
++					     IPA_ARP_PROCESSING,
+ 					     IPA_CMD_ASS_ARP_FLUSH_CACHE,
+ 					     0,0);
+ 		QETH_STANDARD_RETVALS;
+ 		break;
+ 	case SIOCDEVPRIVATE+5:
+-		result=qeth_send_snmp_control(card,rq,IPA_CMD_SETADAPTERPARMS,
++	case SIOC_QETH_ADP_SET_SNMP_CONTROL:
++		if (copy_from_user(data,rq->ifr_ifru.ifru_data,
++				   sizeof(data))) {
++			ret_val = -EFAULT;
++			break;
++		}
++
++		result=qeth_send_snmp_control(card,rq,
++					      IPA_CMD_SETADAPTERPARMS,
+ 					      IPA_SETADP_SET_SNMP_CONTROL,
+ 					      data,4);
+ 		QETH_STANDARD_RETVALS;
+ 		break;
++	case SIOCDEVPRIVATE+6:
++	case SIOC_QETH_GET_CARD_TYPE:
++		if (!card->is_guest_lan &&
++		    (card->type == QETH_CARD_TYPE_OSAE))
++			ret_val = 1;
++		else
++			ret_val = 0;
++		break;
++	case SIOCGMIIPHY:
++		mii_data->phy_id = 0; /* for now we set this 
++					 fixed to one phy with ID 0 */
++		ret_val = 0;
++		break;
++
++	case SIOCGMIIREG:
++		if(mii_data->phy_id != 0) {
++			ret_val = -EINVAL;
++			break;
++		}
++		mii_data->val_out = mdio_read(dev, mii_data->phy_id, 
++					      mii_data->reg_num);
++		ret_val = 0;
++		break;
++
++	case SIOCSMIIREG:
++		if (!capable(CAP_NET_ADMIN)) {
++			ret_val=-EPERM;
++			break;
++		}
++		if(mii_data->phy_id != 0) {
++			ret_val = -EINVAL;
++			break;
++		}
++		mdio_write(dev, mii_data->phy_id, 
++			   mii_data->reg_num, mii_data->val_in );
++		ret_val = 0;
++		break;
+ 
+ 	default:
+-		return -EOPNOTSUPP;
++		ret_val=-EOPNOTSUPP;
++		goto out;
+ 	}
+ out:
+-	my_spin_unlock(&card->ioctl_lock);
++	up (&card->ioctl_sem);
+ 
+ 	sprintf(dbf_text,"ret=%4x",ret_val);
+ 	QETH_DBF_TEXT2(0,trace,dbf_text);
+@@ -4710,7 +5306,8 @@
+ 	}
+ #ifdef QETH_VLAN
+ 	QETH_DBF_TEXT4(0,trace,"tovipm6s");
+-	if ( (qeth_is_supported(IPA_FULL_VLAN)) &&
++	if ( ((card->options.layer2 == DO_LAYER2) ||
++              (qeth_is_supported(IPA_FULL_VLAN))) &&
+ 	     (atomic_read(&card->is_open)) ) {
+ 		card_group = (struct vlan_group *) card->vlangrp;
+ 		if (card_group) for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
+@@ -4785,6 +5382,53 @@
+ }
+ #endif /* QETH_IPV6 */
+ 
++#ifdef QETH_VLAN
++/* ATT: not a very readable order: bytes count from lower numbers, bits
++   count from lsb */
++static void qeth_set_bit(__u8 *ptr,int i)
++{
++	ptr[i/8]|=0x80>>(i%8);
++}
++
++static int qeth_get_bit(__u8 *ptr,int i)
++{
++	return (ptr[i/8]&(0x80>>(i%8)))?1:0;
++}
++
++static void qeth_takeover_vlans(qeth_card_t *card)
++{
++	int i;
++
++	/* copy new to current */
++	memcpy(&card->vlans_current[0],
++	       &card->vlans_new[0],
++	       VLAN_GROUP_ARRAY_LEN/(8*sizeof(__u8)));
++
++	/* clear new vector */
++	memset(&card->vlans_new[0],0,VLAN_GROUP_ARRAY_LEN/(8*sizeof(__u8)));
++
++	for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
++		if ( (card->vlangrp) &&
++		     (card->vlangrp->vlan_devices[i]) )
++			qeth_set_bit(&card->vlans_new[0],i);
++	}
++}
++
++static void qeth_set_vlans(qeth_card_t *card)
++{
++	int i;
++
++	for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
++		if ( (qeth_get_bit(&card->vlans_current[0],i)) &&
++		     (!qeth_get_bit(&card->vlans_new[0],i)) )
++			qeth_send_setdel_vlan(card,i,IPA_CMD_DELVLAN);
++		if ( (!qeth_get_bit(&card->vlans_current[0],i)) &&
++		     (qeth_get_bit(&card->vlans_new[0],i)) )
++			qeth_send_setdel_vlan(card,i,IPA_CMD_SETVLAN);
++	}
++}
++#endif
++
+ static void qeth_clear_ifa4_list(struct in_ifaddr **ifa_list)
+ {
+ 	struct in_ifaddr *ifa;
+@@ -4962,7 +5606,8 @@
+ 
+ #ifdef QETH_VLAN
+ 	QETH_DBF_TEXT4(0,trace,"to-vipms");
+-	if ( (qeth_is_supported(IPA_FULL_VLAN)) &&
++	if ( ((card->options.layer2 == DO_LAYER2) ||
++              (qeth_is_supported(IPA_FULL_VLAN))) &&
+ 	     (atomic_read(&card->is_open)) ) {
+ 		card_group = (struct vlan_group *) card->vlangrp;
+                 if (card_group) for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
+@@ -5192,6 +5837,50 @@
+ 		}
+ 	}
+ }
++static int qeth_do_layer2_mac_stuff(qeth_card_t *card) 
++{
++	int result;
++	char dbf_text[15];
++
++	sprintf(dbf_text,"dols%4x",card->irq0);
++	QETH_DBF_TEXT4(0,trace,dbf_text);
++
++	if (atomic_read(&card->mac_registered))  
++	    return 0;
++	/* get mac addr */
++	/* we read the mac regardless of the return code */
++	result=qeth_send_setadapterparms_change_addr(card,
++			     IPA_SETADP_ALTER_MAC_ADDRESS,
++			     CHANGE_ADDR_READ_MAC,card->dev->dev_addr,
++			     OSA_ADDR_LEN);
++	if (result) {
++		PRINT_WARN("couldn't get Layer 2 MAC address on " \
++			   "irq 0x%x: x%x\n",card->irq0,result);
++		QETH_DBF_TEXT1(0,trace,"L2NMCADD");
++		sprintf(dbf_text,"%4x%4x",card->irq0,result);
++		QETH_DBF_TEXT1(1,trace,dbf_text);
++		return result;
++	} else {
++		QETH_DBF_HEX2(0,setup,card->dev->dev_addr,
++			      __max(OSA_ADDR_LEN,QETH_DBF_SETUP_LEN));
++		QETH_DBF_HEX3(0,trace,card->dev->dev_addr,
++			      __max(OSA_ADDR_LEN,QETH_DBF_TRACE_LEN));
++	}
++	result=qeth_send_setdelmac(card,&card->dev->dev_addr[0],
++				   IPA_CMD_SETVMAC);
++	if (result) {
++		PRINT_WARN("couldn't register MAC address on " \
++			   "irq 0x%x: x%x\n",card->irq0,result);
++		QETH_DBF_TEXT1(0,trace,"NOREGMAC");
++		sprintf(dbf_text,"%4x%4x",card->irq0,result);
++		QETH_DBF_TEXT1(1,trace,dbf_text);
++		atomic_set(&card->mac_registered,0);
++	} else {
++		atomic_set(&card->mac_registered,1);
++	}
++	return 0; /* it's ok if SETVMAC fails -- we'll track that in
++		     mac_registered */
++}
+ 
+ static int qeth_softsetup_card(qeth_card_t *card,int wait_for_lock)
+ {
+@@ -5240,13 +5929,17 @@
+ 					   "failure -- please check the " \
+ 					   "network, plug in the cable or " \
+ 					   "enable the OSA port":
++					   (result==0xf080)?
++					   "startlan disabled (VM: LAN " \
++					   "is offline for functions " \
++					   "requiring LAN access.":
+ 					   "unknown return code");
+ 				sprintf(dbf_text,"stln%4x",result);
+ 				QETH_DBF_TEXT2(0,trace,dbf_text);
+ 				atomic_set(&card->is_softsetup,0);
+ 				atomic_set(&card->is_startlaned,0);
+ 				/* do not return an error */
+-				if (result==0xe080) {
++				if ((result==0xe080)||(result==0xf080)) {
+ 					result=0;
+ 				}
+ 				goto out;
+@@ -5255,6 +5948,20 @@
+ 		}
+ 		netif_wake_queue(card->dev);
+ 
++		if (card->options.layer2 == DO_LAYER2) {
++			card->dev->features |=
++				NETIF_F_HW_VLAN_TX |
++				NETIF_F_HW_VLAN_RX;
++			card->dev->flags|=IFF_MULTICAST|IFF_BROADCAST;
++			card->broadcast_capable=1;
++			result=qeth_do_layer2_mac_stuff(card);
++			if (result) {
++				atomic_set(&card->is_softsetup,0);
++				return result;
++			}
++			atomic_set(&card->is_softsetup,1);
++			goto layer2_1;
++		} else
+ 		qeth_do_setadapterparms_stuff(card);
+ 
+                 if (!qeth_is_supported(IPA_ARP_PROCESSING)) {
+@@ -5372,7 +6079,8 @@
+ 					QETH_DBF_TEXT2(0,trace,dbf_text);
+ 					atomic_set(&card->is_softsetup,0);
+ 					/* do not return an error */
+-					if (result==0xe080) {
++					if ((result==0xe080)||
++					    (result==0xf080)) {
+ 						result=0;
+ 					}
+ 					goto out;
+@@ -5390,8 +6098,9 @@
+ 				goto out;
+ 			}
+ 
+-			sprintf(dbf_text,"%4x%4x",card->ipa6_supported,
+-				card->ipa6_enabled);
++			sprintf(dbf_text,"%8x",card->ipa6_supported);
++			QETH_DBF_TEXT2(0,trace,dbf_text);
++			sprintf(dbf_text,"%8x",card->ipa6_enabled);
+ 			QETH_DBF_TEXT2(0,trace,dbf_text);
+ 			QETH_DBF_TEXT2(0,trace,"enaipv46");
+                         result=qeth_send_setassparms_simple_with_data(
+@@ -5461,8 +6170,19 @@
+ 				goto go_on_filt;
+ 			}
+ 			card->dev->flags|=IFF_BROADCAST;
+-			card->broadcast_capable=1;
++			card->broadcast_capable=BROADCAST_WITH_ECHO;
+ 
++			result=qeth_send_setassparms_simple_with_data(
++                                card,IPA_FILTERING,IPA_CMD_ASS_ENABLE,1);
++			sprintf(dbf_text,"Flt3%4x",result);
++			QETH_DBF_TEXT2(0,trace,dbf_text);
++			QETH_DBF_TEXT2(0,setup,dbf_text);
++			if (!result) {
++				PRINT_INFO("Broadcast packets will not be " \
++					   "echoed back on %s.\n",
++					   card->dev_name);
++				card->broadcast_capable=BROADCAST_WITHOUT_ECHO;
++			}
+ 		}
+ go_on_filt:
+ 		if (card->options.checksum_type==HW_CHECKSUMMING) {
+@@ -5494,7 +6214,7 @@
+ 				result=qeth_send_setassparms_simple_with_data(
+ 					card,IPA_INBOUND_CHECKSUM,
+ 					IPA_CMD_ASS_ENABLE,
+-					IPA_CHECKSUM_ENABLE_MASK);
++					card->csum_enable_mask);
+ 				if (result) {
+ 					PRINT_WARN("Could not enable inbound " \
+ 						   "checksumming on %s: " \
+@@ -5571,7 +6291,14 @@
+ 
+ #ifdef QETH_IPV6
+ 	if (atomic_read(&card->enable_routing_attempts6)) {
+-		if (card->options.routing_type6) {
++		/* for OSAs that can't do v6 multicast routing, we don't try */
++		if ( (card->type==QETH_CARD_TYPE_OSAE) &&
++		     ( (card->options.routing_type6&ROUTER_MASK) ==
++		       MULTICAST_ROUTER) &&
++		     (!qeth_is_supported6(IPA_OSA_MC_ROUTER_AVAIL)) ) {
++			atomic_set(&card->enable_routing_attempts6,0);
++			atomic_set(&card->rt6fld,0);
++		} else if (card->options.routing_type6) {
+ 			sprintf(dbf_text,"strtg6%2x",
+ 				card->options.routing_type6);
+ 			QETH_DBF_TEXT2(0,trace,dbf_text);
+@@ -5625,13 +6352,22 @@
+ 	}
+ #endif /* QETH_IPV6 */
+ 
+-	QETH_DBF_TEXT2(0,trace,"delvipa");
+-	qeth_set_vipas(card,0);
++	if (card->options.layer2 == DONT_LAYER2) {
++		QETH_DBF_TEXT2(0,trace,"delvipa");
++		qeth_set_vipas(card,0);
++	}
++layer2_1:
+ 	QETH_DBF_TEXT2(0,trace,"toip/ms");
+ 	qeth_takeover_ip_ipms(card);
+ #ifdef QETH_IPV6
+ 	qeth_takeover_ip_ipms6(card);
+ #endif /* QETH_IPV6 */
++	if (card->options.layer2 == DO_LAYER2) {
++#ifdef QETH_VLAN
++		qeth_takeover_vlans(card);
++#endif
++		goto layer2_2;
++	}
+ 	QETH_DBF_TEXT2(0,trace,"setvipa");
+ 	qeth_set_vipas(card,1);
+ 
+@@ -5644,7 +6380,12 @@
+ 		atomic_set(&card->is_softsetup,0);
+                 goto out;
+         }
 -
--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;
--}
++layer2_2:
++#ifdef QETH_VLAN
++ 	if (card->options.layer2 == DO_LAYER2) {
++		qeth_set_vlans(card);
++	}
++#endif
+         result=qeth_setipms(card,use_setip_retries);
+         if (result) { /* by now, qeth_setipms does not return errors */
+                 PRINT_WARN("couldn't set up multicast IPs on %s: 0x%x\n",
+@@ -5753,9 +6494,12 @@
+ 	sprintf(dbf_text,"PROB%4x",i);
+ 	QETH_DBF_TEXT2(0,trace,dbf_text);
+ 
+-        PRINT_WARN("recovery was scheduled on irq 0x%x (%s) with " \
+-		   "problem 0x%x\n",
+-                   card->irq0,card->dev_name,i);
++	if (i!=PROBLEM_TX_TIMEOUT) {
++		PRINT_WARN("recovery was scheduled on irq 0x%x (%s) with " \
++			   "problem 0x%x\n",
++			   card->irq0,card->dev_name,i);
++	}
++
+         switch (i) {
+         case PROBLEM_RECEIVED_IDX_TERMINATE:
+ 		if (atomic_read(&card->in_recovery))
+@@ -5802,7 +6546,7 @@
+         }
+ }
+ 
+-static void qeth_schedule_recovery(qeth_card_t *card)
++static inline void qeth_schedule_recovery(qeth_card_t *card)
+ {
+ 	if (card) {
+    		INIT_LIST_HEAD(&card->tqueue.list);
+@@ -5858,7 +6602,7 @@
+ 			return;
+ 		}
+ 		sbalf15=(card->inbound_qdio_buffers[(first_element+count-1)&
+-			 QDIO_MAX_BUFFERS_PER_Q].
++			 (QDIO_MAX_BUFFERS_PER_Q-1)].
+ 			 element[15].flags)&&0xff;
+ 		PRINT_STUPID("inbound qdio transfer error on irq 0x%04x. " \
+ 			     "qdio_error=0x%x (more than one: %c), " \
+@@ -5920,6 +6664,9 @@
+ 	card=(qeth_card_t *)card_ptr;
+ 
+ 	if (status&QDIO_STATUS_LOOK_FOR_ERROR) {
++		sbalf15=(card->outbound_ringbuffer[queue]->buffer[
++			 (first_element+count-1)&
++			 (QDIO_MAX_BUFFERS_PER_Q-1)].element[15].flags)&0xff;
+ 		if (status&QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
+ 			problem=PROBLEM_ACTIVATE_CHECK_CONDITION;
+ 			PRINT_WARN("activate queues on irq 0x%x: " \
+@@ -5937,9 +6684,29 @@
+ 			qeth_schedule_recovery(card);
+ 			goto out;
+ 		}
+-		sbalf15=(card->outbound_ringbuffer[queue]->buffer[
+-			 (first_element+count-1)&
+-			 QDIO_MAX_BUFFERS_PER_Q].element[15].flags)&0xff;
++		if ( (siga_error==0x01) ||
++		     (siga_error==(0x02|QDIO_SIGA_ERROR_B_BIT_SET)) ||
++		     (siga_error==0x03) ) {
++			sprintf(dbf_text,"BS%4x%2x",card->irq0,queue);
++			QETH_DBF_TEXT2(0,trace,dbf_text);
++			QETH_DBF_TEXT2(0,qerr,dbf_text);
++			QETH_DBF_TEXT2(1,setup,dbf_text);
++			sprintf(dbf_text,"%2x%2x%2x%2x",
++				first_element+count-1,
++				siga_error,qdio_error,sbalf15);
++			QETH_DBF_TEXT2(1,trace,dbf_text);
++			QETH_DBF_TEXT2(1,qerr,dbf_text);
++			PRINT_ERR("Outbound queue x%x on irq x%x (%s); " \
++				  "errs: siga: x%x, qdio: x%x, flags15: " \
++				  "x%x. The device will be taken down.\n",
++				  queue,card->irq0,card->dev_name,
++				  siga_error,qdio_error,sbalf15);
++			netif_stop_queue(card->dev);
++			qeth_set_dev_flag_norunning(card);
++			atomic_set(&card->problem,PROBLEM_BAD_SIGA_RESULT);
++			qeth_schedule_recovery(card);
++			goto out;
++		}
+ 		PRINT_STUPID("outbound qdio transfer error on irq %04x, " \
+ 			     "queue=%i. qdio_error=0x%x (more than one: %c)," \
+ 			     " siga_error=0x%x (more than one: %c), " \
+@@ -5988,7 +6755,7 @@
+ 			      (&card->outbound_used_buffers[queue])<=
+ 			      LOW_WATERMARK_PACK);
+ 		/* first_element is the last buffer that we got back
+-		 * from hydra */
++		 * from OSA */
+ 		if (switch_state||last_pci_hit) {
+ 			*((__u16*)(&dbf_text2[6]))=card->irq0;
+ 			QETH_DBF_HEX3(0,trace,dbf_text2,QETH_DBF_TRACE_LEN);
+@@ -6010,6 +6777,14 @@
+ 				if (switch_state)
+ 					card->send_state[queue]=
+ 						SEND_STATE_DONT_PACK;
++
++				/* reset the last_pci position to avoid
++				 * races, when we get close to packing again
++				 * immediately (in order to never loose
++				 * a PCI) */
++				atomic_set(&card->last_pci_pos[queue],
++					   (-2*QDIO_MAX_BUFFERS_PER_Q));
++
+ 				netif_wake_queue(card->dev);
+ 				atomic_set(&card->outbound_ringbuffer_lock[
+ 					   queue],QETH_LOCK_UNLOCKED);
+@@ -6137,8 +6912,9 @@
+                 goto wakeup_out;
+         }
+ 
+-        if (!IS_IPA(card->dma_stuff->recbuf)||
+-            IS_IPA_REPLY(card->dma_stuff->recbuf)) {
++        if ( (!IS_IPA(card->dma_stuff->recbuf))||
++	     (IS_IPA(card->dma_stuff->recbuf)&&
++	      IS_IPA_REPLY(card->dma_stuff->recbuf)) ) {
+                 /* setup or unknown data */
+ 		result = qeth_look_for_arp_data(card);
+ 		switch (result) {
+@@ -6387,10 +7163,21 @@
+ static void qeth_softshutdown(qeth_card_t *card)
+ {
+ 	char dbf_text[15];
++	int result;
+ 
+ 	sprintf(dbf_text,"ssht%4x",card->irq0);
+ 	QETH_DBF_TEXT3(0,trace,dbf_text);
 -
--static ssize_t
--tape_devices_read (struct file *file, char *user_buf, size_t user_len, loff_t * offset)
++	if (card->options.layer2 == DO_LAYER2) {
++		result=qeth_send_setdelmac(card,&card->dev->dev_addr[0],
++					   IPA_CMD_DELVMAC);
++		if (result) {
++			PRINT_WARN("couldn't de-register MAC address on " \
++				   "irq 0x%x: x%x\n",card->irq0,result);
++			QETH_DBF_TEXT1(0,trace,"NODRGMAC");
++			sprintf(dbf_text,"%4x%4x",card->irq0,result);
++			QETH_DBF_TEXT1(1,trace,dbf_text);
++		}
++	}
+         qeth_send_stoplan(card);
+ }
+ 
+@@ -6669,6 +7456,7 @@
+ 		atomic_set(&card->is_registered,0);
+ 	}
+ 
++	if (card->options.layer2 == DONT_LAYER2)
+ 	qeth_put_unique_id(card);
+ 
+ 	QETH_DBF_TEXT2(0,trace,"clrcard");
+@@ -6716,15 +7504,6 @@
+ 	qeth_start_softsetup_thread(card);
+ }
+ 
+-static int qeth_set_mac_address(struct net_device *dev,void *addr)
 -{
--	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" */
--	}
--}
+-	char dbf_text[15];
 -
--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;
+-	sprintf(dbf_text,"stmc%4x",((qeth_card_t *)dev->priv)->irq0);
+-	QETH_DBF_TEXT2(0,trace,dbf_text);
+-	return -EOPNOTSUPP;
 -}
 -
--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;
+ static int qeth_neigh_setup(struct net_device *dev,struct neigh_parms *np)
+ {
+ 	char dbf_text[15];
+@@ -6870,6 +7649,10 @@
+ 	card->portname_required=
+ 		((!QETH_IDX_NO_PORTNAME_REQUIRED(card->dma_stuff->recbuf))&&
+ 		 (card->type==QETH_CARD_TYPE_OSAE));
++	/* however, as the portname indication of OSA is wrong, we have to
++         * do this: */
++	card->portname_required=(card->type==QETH_CARD_TYPE_OSAE);
++		
+ 
+         memcpy(&temp,QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->recbuf),2);
+         if (temp!=qeth_peer_func_level(card->func_level)) {
+@@ -7111,11 +7894,18 @@
+ 	__u16 len;
+ 	__u8 link_type;
+ 	int result;
++	char prot_type;
+ 	char dbf_text[15];
+ 
+         memcpy(card->send_buf,ULP_ENABLE,ULP_ENABLE_SIZE);
+ 
+ 	*(QETH_ULP_ENABLE_LINKNUM(card->send_buf))=(__u8)card->options.portno;
++	/*is it layer2 or tcpip*/
++	if (card->options.layer2 == DO_LAYER2)
++		prot_type = QETH_ULP_ENABLE_PROT_LAYER2;
++	else
++		prot_type = QETH_ULP_ENABLE_PROT_TCPIP;
++ 	memcpy(QETH_ULP_ENABLE_PROT_TYPE(card->send_buf),&prot_type,1);
+ 
+         memcpy(QETH_ULP_ENABLE_DEST_ADDR(card->send_buf),
+                &card->token.cm_connection_r,QETH_MPC_TOKEN_LENGTH);
+@@ -7220,12 +8010,17 @@
+ static int qeth_qdio_establish(qeth_card_t *card)
+ {
+ 	int result;
+-	char adapter_area[15];
++	char *adapter_area;
+ 	char dbf_text[15];
+ 	void **input_array,**output_array,**ptr;
+ 	int i,j;
+ 	qdio_initialize_t init_data;
+ 
++	adapter_area=vmalloc(QDIO_MAX_BUFFERS_PER_Q*sizeof(char));
++	if (!adapter_area) return -ENOMEM;
++
++	memset(adapter_area,0,QDIO_MAX_BUFFERS_PER_Q*sizeof(char));
++
+ 	adapter_area[0]=_ascebc['P'];
+ 	adapter_area[1]=_ascebc['C'];
+ 	adapter_area[2]=_ascebc['I'];
+@@ -7235,7 +8030,10 @@
+ 	*((unsigned int*)(&adapter_area[12]))=PCI_TIMER_VALUE;
+ 
+ 	input_array=vmalloc(QDIO_MAX_BUFFERS_PER_Q*sizeof(void*));
+-	if (!input_array) return -ENOMEM;
++	if (!input_array) {
++		vfree(adapter_area);
++		return -ENOMEM;
++	}
+ 	ptr=input_array;
+ 	for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+ 		*ptr=(void*)virt_to_phys
+@@ -7247,6 +8045,7 @@
+ 			     card->no_queues);
+ 	if (!output_array) {
+ 		vfree(input_array);
++		vfree(adapter_area);
+ 		return -ENOMEM;
+ 	}
+ 	ptr=output_array;
+@@ -7285,6 +8084,7 @@
+ 	
+ 	vfree(input_array);
+ 	vfree(output_array);
++	vfree(adapter_area);
+ 
+ 	sprintf(dbf_text,"qde=%4i",result);
+ 	QETH_DBF_TEXT3(0,trace,dbf_text);
+@@ -7349,19 +8149,17 @@
+ 	for (;tmp&&(!result);tmp=tmp->next) {
+ 		if (atomic_read(&tmp->shutdown_phase))
+ 			continue;
+-		if (dev==tmp->dev) {
++		if (dev==tmp->dev)
+ 			result=QETH_VERIFY_IS_REAL_DEV;
 -		}
--		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 */
+ #ifdef QETH_VLAN
+ 		/* check all vlan devices */
+ 		vlan_grp = (struct vlan_group *) tmp->vlangrp;
+ 		if (vlan_grp) {
+ 			for (i=0;i<VLAN_GROUP_ARRAY_LEN;i++) {
+-				if (vlan_grp->vlan_devices[i]==dev) {
++				if (vlan_grp->vlan_devices[i]==dev)
+ 					result=QETH_VERIFY_IS_VLAN_DEV;
+ 				}
+ 			}
+-		}
+ 
+ #endif
+ 	}
+@@ -7388,6 +8186,110 @@
+ 	return result;
+ }
+ 
++static int qeth_set_mac_address(struct net_device *dev,void *p)
++{
++	char dbf_text[15];
++	struct sockaddr *addr=p;
++	qeth_card_t *card;
++	int result;
++#ifdef QETH_VLAN
++	if (qeth_verify_dev(dev)!=QETH_VERIFY_IS_REAL_DEV) {
++		QETH_DBF_TEXT2(0,trace,"setmcINV");
++		return -EOPNOTSUPP;
++	}
++#endif
++	card=(qeth_card_t *)dev->priv;
++	if (card->options.layer2 != DO_LAYER2) {
++		QETH_DBF_TEXT2(0,trace,"setmc_l3");
++		PRINT_WARN("Setting MAC address on %s is not supported "
++			   "in Layer 3 mode\n",dev->name);
++		return -EOPNOTSUPP;
++	}
++
++	sprintf(dbf_text,"stmc%4x",card->irq0);
++	QETH_DBF_TEXT2(0,trace,dbf_text);
++	QETH_DBF_TEXT2(0,setup,dbf_text);
++	QETH_DBF_HEX2(0,trace,addr->sa_data,6);
++	QETH_DBF_HEX2(0,setup,addr->sa_data,6);
++
++	if (atomic_read(&card->mac_registered)) {
++		result=qeth_send_setdelmac(card,&card->dev->dev_addr[0],
++					   IPA_CMD_DELVMAC);
++		if (result) {
++			PRINT_WARN("couldn't de-register MAC address on " \
++				   "irq 0x%x: x%x\n",card->irq0,result);
++			QETH_DBF_TEXT1(0,trace,"STMCFLDd");
++			sprintf(dbf_text,"%4x%4x",card->irq0,result);
++			QETH_DBF_TEXT1(1,trace,dbf_text);
++			return -EIO;
++		}
++		atomic_set(&card->mac_registered,0);
++	}
++
++	result=qeth_send_setdelmac(card,addr->sa_data,IPA_CMD_SETVMAC);
++	if (result) {
++		PRINT_WARN("couldn't register MAC address on " \
++			   "irq 0x%x: x%x\n",card->irq0,result);
++		QETH_DBF_TEXT1(0,trace,"STMCFLDr");
++		sprintf(dbf_text,"%4x%4x",card->irq0,result);
++		QETH_DBF_TEXT1(1,trace,dbf_text);
++		return -EIO;
++	}
++	atomic_set(&card->mac_registered, 1);
++	memcpy(dev->dev_addr,addr->sa_data,ETH_ALEN);
++	PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
++		   "successfully registered on device %s\n",
++		   dev->dev_addr[0],dev->dev_addr[1],dev->dev_addr[2],
++		   dev->dev_addr[3],dev->dev_addr[4],dev->dev_addr[5],
++		   dev->name);
++	return 0;
++}
++
++static void qeth_correct_routing_status(qeth_card_t *card)
++{
++	if (card->type==QETH_CARD_TYPE_IQD) {
++		/* if it's not a mc router, it's no router */
++		if ( (card->options.routing_type4 == PRIMARY_ROUTER) ||
++		     (card->options.routing_type4 == SECONDARY_ROUTER) 
++#ifdef QETH_IPV6
++		     ||
++		     (card->options.routing_type6 == PRIMARY_ROUTER) ||
++		     (card->options.routing_type6 == SECONDARY_ROUTER) 
++#endif /* QETH_IPV6 */
++		     ) {
++			PRINT_WARN("routing not applicable, reset " \
++				   "routing status.\n");
++			card->options.routing_type4=NO_ROUTER;
++#ifdef QETH_IPV6
++			card->options.routing_type6=NO_ROUTER;
++#endif /* QETH_IPV6 */
++		}
++		card->options.do_prio_queueing=NO_PRIO_QUEUEING;
++	} else {
++		/* if it's a mc router, it's no router */
++		if ( (((!qeth_is_supported(IPA_OSA_MC_ROUTER_AVAIL))&&
++		       card->options.routing_type4 == MULTICAST_ROUTER)) ||
++		     (card->options.routing_type4 == PRIMARY_CONNECTOR) ||
++		     (card->options.routing_type4 == SECONDARY_CONNECTOR) 
++#ifdef QETH_IPV6
++		     ||
++		     (((!qeth_is_supported(IPA_OSA_MC_ROUTER_AVAIL))&&
++		       card->options.routing_type6 == MULTICAST_ROUTER)) ||
++		     (card->options.routing_type6 == PRIMARY_CONNECTOR) ||
++		     (card->options.routing_type6 == SECONDARY_CONNECTOR) 
++#endif /* QETH_IPV6 */
++		     ) {
++			PRINT_WARN("routing not applicable, reset " \
++				   "routing status. (Did you mean " \
++				   "primary_router or secondary_router?)\n");
++			card->options.routing_type4=NO_ROUTER;
++#ifdef QETH_IPV6
++			card->options.routing_type6=NO_ROUTER;
++#endif /* QETH_IPV6 */
++		}
++	}
++}
++
+ #ifdef QETH_IPV6
+ extern struct neigh_table arp_tbl;
+ int (*qeth_old_arp_constructor)(struct neighbour *);
+@@ -7411,13 +8313,31 @@
+         char dbf_text[15];
+         struct net_device *dev = neigh->dev;
+         struct in_device *in_dev = in_dev_get(dev);
++	int is_qeth_device;
++	qeth_card_t *card;
++	int tweak_neighbour=1;
+ 
+         if (in_dev == NULL)
+                 return -EINVAL;
+ 
+         QETH_DBF_TEXT4(0,trace,"arpconst");
+-        if (!qeth_verify_dev(dev)) {
+ 	    	
++        is_qeth_device=qeth_verify_dev(dev);
++	/* so if we're dealing wiht a qeth device, we've got to
++	 * check, whether we're in layer 2 mode. if yes, don't
++	 * mind fiddling with the neighbours */
++	if (is_qeth_device) {
++		if (is_qeth_device==QETH_VERIFY_IS_VLAN_DEV)
++			card=(qeth_card_t *)VLAN_DEV_INFO(dev)->
++				real_dev->priv;
++		else
++			card=(qeth_card_t*)dev->priv;
++	       	if (card->options.layer2 == DO_LAYER2)
++			tweak_neighbour=0;
++	} else
++		tweak_neighbour=0;
++
++        if (!tweak_neighbour) {
+                 in_dev_put(in_dev);
+                 return qeth_old_arp_constructor(neigh);
+         }
+@@ -7490,10 +8410,14 @@
+ 	card->hard_header_cache=qeth_get_hard_header_cache(card->link_type);
+ 	card->header_cache_update=
+ 		qeth_get_header_cache_update(card->link_type);
+-	card->type_trans=qeth_get_type_trans(card->link_type);
+ }
+ #endif /* QETH_IPV6 */
+ 
++static void qeth_tt_init_card(qeth_card_t *card)
++{
++	card->type_trans=qeth_get_type_trans(card->link_type);
++}
++
+ #ifdef QETH_VLAN
+ static void qeth_vlan_rx_register(struct net_device *dev, 
+ 				  struct vlan_group *grp)
+@@ -7503,6 +8427,7 @@
+         spin_lock_irq(&card->vlan_lock);
+         card->vlangrp = grp;
+         spin_unlock_irq(&card->vlan_lock);
++	qeth_start_softsetup_thread(card);
+ }
+ static void qeth_vlan_rx_kill_vid(struct net_device *dev, 
+ 				  unsigned short vid)
+@@ -7513,6 +8438,7 @@
+ 	if (card->vlangrp)
+                 card->vlangrp->vlan_devices[vid] = NULL;
+         spin_unlock_irq(&card->vlan_lock);
++	qeth_start_softsetup_thread(card);
+ }
+ 
+ #endif /*QETH_VLAN*/
+@@ -7557,39 +8483,43 @@
+ 
+ 	dev->rebuild_header=
+ #ifdef QETH_IPV6
+-		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
++		(!(qeth_get_additional_dev_flags(card)&IFF_NOARP))?
+ 		(qeth_get_rebuild_header(card->link_type)?
+ 		 qeth_rebuild_header:NULL):
+ #endif /* QETH_IPV6 */
+-		NULL;
++		(card->options.layer2 == DO_LAYER2) ? 
++		qeth_get_rebuild_header(card->link_type) : NULL;
+ 	dev->hard_header=
+ #ifdef QETH_IPV6
+-		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
++		(!(qeth_get_additional_dev_flags(card)&IFF_NOARP))?
+ 		(qeth_get_hard_header(card->link_type)?
+ 		 qeth_hard_header:NULL):
++#else /* QETH_IPV6 */
++		(card->options.fake_ll==FAKE_LL)?qeth_fake_header:
+ #endif /* QETH_IPV6 */
+-		NULL;
++		(card->options.layer2 == DO_LAYER2) ? 
++		qeth_get_hard_header(card->link_type) : NULL;
+ 	dev->header_cache_update=
+ #ifdef QETH_IPV6
+-		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
++		(!(qeth_get_additional_dev_flags(card)&IFF_NOARP))?
+ 		(qeth_get_header_cache_update(card->link_type)?
+ 		 qeth_header_cache_update:NULL):
+ #endif /* QETH_IPV6 */
+-		NULL;
++		(card->options.layer2 == DO_LAYER2) ? 
++		qeth_get_header_cache_update(card->link_type) : NULL;
+ 	dev->hard_header_cache=
+ #ifdef QETH_IPV6
+-		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
++		(!(qeth_get_additional_dev_flags(card)&IFF_NOARP))?
+ 		qeth_get_hard_header_cache(card->link_type):
+ #endif /* QETH_IPV6 */
+-		NULL;
++		(card->options.layer2 == DO_LAYER2) ? 
++		qeth_get_hard_header_cache(card->link_type) : NULL;
+         dev->hard_header_parse=NULL;
+         dev->destructor=qeth_destructor;
+         dev->set_multicast_list=qeth_set_multicast_list;
+         dev->set_mac_address=qeth_set_mac_address;
+         dev->neigh_setup=qeth_neigh_setup;
 -
--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;
--}
+-	dev->flags|=qeth_get_additional_dev_flags(card->type);
 -
--static inline devreg_t *
--tape_create_devreg (int devno)
++	dev->flags|=qeth_get_additional_dev_flags(card);
+ 	dev->flags|=(
+ 		     (card->options.fake_broadcast==FAKE_BROADCAST)||
+ 		     (card->broadcast_capable)
+@@ -7600,11 +8530,12 @@
+ 	qeth_send_qipassist(card,4);*/
+ 
+ 	/* that was the old place. one id. we need to make sure, that
+-	 * hydra knows about us going to use the same id again, so we
++	 * OSA knows about us going to use the same id again, so we
+ 	 * do that in hardsetup_card every time
+ 	qeth_get_unique_id(card);*/
+ 
+ #ifdef CONFIG_SHARED_IPV6_CARDS
++/*in Layer2 mode unique id is zero*/
+ 	dev->features=(card->unique_id&UNIQUE_ID_NOT_BY_CARD)?
+ 		0:NETIF_F_SHARED_IPV6;
+ 	dev->dev_id=card->unique_id&0xffff;
+@@ -7621,6 +8552,7 @@
+ #ifdef QETH_IPV6
+ 	qeth_ipv6_init_card(card);
+ #endif /* QETH_IPV6 */
++	qeth_tt_init_card(card);
+ 
+ 	dev_init_buffers(dev);
+  
+@@ -7650,8 +8582,9 @@
+ 	card->unit_addr2 = prcd[31];
+ 	card->cula = prcd[63];
+ 	/* Don't build queues with diag98 for VM guest lan. */
+-	card->do_pfix = (MACHINE_HAS_PFIX) ? ((prcd[0x10]!=_ascebc['V']) ||
+-					      (prcd[0x11]!=_ascebc['M'])):0;
++	card->is_guest_lan= ((prcd[0x10]==_ascebc['V']) &&
++			     (prcd[0x11]==_ascebc['M']));
++	card->do_pfix = (MACHINE_HAS_PFIX) ? (!(card->is_guest_lan)):0;
+ 
+ 	sprintf(dbf_text,"chpid:%02x",card->chpid);
+ 	QETH_DBF_TEXT2(0,trace,dbf_text);
+@@ -8032,6 +8965,7 @@
+ 
+ 	/* here we need to know, whether we should include a value
+ 	 * into eui-64 address generation */
++	if (card->options.layer2 == DONT_LAYER2) {
+ 	QETH_DBF_TEXT2(0,trace,"qipassi4");
+ 	r=qeth_send_qipassist(card,4);
+ 	if (r) {
+@@ -8040,10 +8974,16 @@
+ 		sprintf(dbf_text,"QIP4%4x",r);
+ 		QETH_DBF_TEXT2(0,trace,dbf_text);
+ 	}
++	}
+ 
+-	sprintf(dbf_text,"%4x%4x",card->ipa_supported,card->ipa_enabled);
++	sprintf(dbf_text,"%8x",card->ipa_supported);
+ 	QETH_DBF_TEXT2(0,trace,dbf_text);
++	sprintf(dbf_text,"%8x",card->ipa_enabled);
++	QETH_DBF_TEXT2(0,trace,dbf_text);
++
++	qeth_correct_routing_status(card);
+ 
++	if (card->options.layer2 == DONT_LAYER2) 
+ 	qeth_get_unique_id(card);
+ 
+ 	/* print out status */
+@@ -8094,7 +9034,8 @@
+ 			       "card%s%s%s\n" \
+ 			       "with link type %s (portname: %s)\n",
+ 			       card->devno0,card->devno1,card->devno2,
+-			       qeth_get_cardname(card->type),
++			       qeth_get_cardname(card->type,
++						 card->is_guest_lan),
+ 			       (card->level[0])?" (level: ":"",
+ 			       (card->level[0])?card->level:"",
+ 			       (card->level[0])?")":"",
+@@ -8102,16 +9043,32 @@
+ 						       card->link_type),
+ 			       dbf_text);
+ 		} else {
+-			printk("qeth: Device 0x%X/0x%X/0x%X is a%s " \
+-			       "card%s%s%s\nwith link type %s " \
+-			       "(no portname needed by interface)\n",
+-			       card->devno0,card->devno1,card->devno2,
+-			       qeth_get_cardname(card->type),
+-			       (card->level[0])?" (level: ":"",
+-			       (card->level[0])?card->level:"",
+-			       (card->level[0])?")":"",
+-			       qeth_get_link_type_name(card->type,
+-						       card->link_type));
++			if (card->options.portname[0]) {
++				printk("qeth: Device 0x%X/0x%X/0x%X is a%s " \
++				       "card%s%s%s\nwith link type %s " \
++				       "(no portname needed by interface)\n",
++				       card->devno0,card->devno1,card->devno2,
++				       qeth_get_cardname(card->type,
++							 card->is_guest_lan),
++				       (card->level[0])?" (level: ":"",
++				       (card->level[0])?card->level:"",
++				       (card->level[0])?")":"",
++				       qeth_get_link_type_name(card->type,
++							       card->
++							       link_type));
++			} else {
++				printk("qeth: Device 0x%X/0x%X/0x%X is a%s " \
++				       "card%s%s%s\nwith link type %s\n",
++				       card->devno0,card->devno1,card->devno2,
++				       qeth_get_cardname(card->type,
++							 card->is_guest_lan),
++				       (card->level[0])?" (level: ":"",
++				       (card->level[0])?card->level:"",
++				       (card->level[0])?")":"",
++				       qeth_get_link_type_name(card->type,
++							       card->
++							       link_type));
++			}
+ 		}
+ 	}
+         
+@@ -8210,7 +9167,7 @@
+ 			atomic_set(&card->is_startlaned,0);
+ 			/* show status in /proc/qeth */
+ 			atomic_set(&card->is_gone,1);
+-			qeth_wakeup_procfile();
++			qeth_snmp_notify();
+ 		} else {
+ 			QETH_DBF_TEXT1(0,trace,"ri-sftst");
+ 			qeth_softsetup_card(card,QETH_LOCK_ALREADY_HELD);
+@@ -8223,7 +9180,7 @@
+ 			qeth_restore_dev_flag_state(card);
+ 			atomic_set(&card->is_gone,0);
+ 			netif_wake_queue(card->dev);
+-			qeth_wakeup_procfile();
++			qeth_snmp_notify();
+ 		}
+ 		my_spin_unlock(&setup_lock);
+ 	}
+@@ -8264,6 +9221,7 @@
+ 	card->options.add_hhlen=DEFAULT_ADD_HHLEN;
+ 	card->options.fake_ll=DONT_FAKE_LL;
+ 	card->options.async_iqd=SYNC_IQD;
++	card->options.layer2=QETH_DEFAULT_LAYER2;
+ }
+ 
+ static qeth_card_t *qeth_alloc_card(void)
+@@ -8324,7 +9282,8 @@
+ 	spin_lock_init(&card->wait_q_lock);
+ 	spin_lock_init(&card->softsetup_lock);
+ 	spin_lock_init(&card->hardsetup_lock);
+-	spin_lock_init(&card->ioctl_lock);
++	sema_init(&card->ioctl_sem, 1);
++
+ #ifdef QETH_VLAN
+ 	spin_lock_init(&card->vlan_lock);
+         card->vlangrp = NULL;
+@@ -8346,6 +9305,8 @@
+ 	card->ip_mc_new_state.ipm6_ifa=NULL;
+ #endif /* QETH_IPV6 */
+ 
++	card->csum_enable_mask=IPA_CHECKSUM_DEFAULT_ENABLE_MASK;
++
+         /* setup net_device stuff */
+ 	card->dev->priv=card;
+ 
+@@ -8697,6 +9658,11 @@
+ 	doit1("dont_fake_broadcast",PARSE_FAKE_BROADCAST,
+ 	      card->options.fake_broadcast,DONT_FAKE_BROADCAST);
+ 	
++	doit1("layer2",PARSE_LAYER2,
++	      card->options.layer2,DO_LAYER2);
++	doit1("no_layer2",PARSE_LAYER2,
++	      card->options.layer2,DONT_LAYER2);
++	
+ 	doit1("fake_ll",PARSE_FAKE_LL,
+ 	      card->options.fake_ll,FAKE_LL);
+ 	doit1("dont_fake_ll",PARSE_FAKE_LL,
+@@ -8795,6 +9761,59 @@
+         return 0;
+ }
+ 
++#define IGNORE_PARAM_EQ(option,value,reset_value,msg) \
++        if (card->options.option == value) { \
++                PRINT_ERR("%s not supported with layer 2 " \
++                          "functionality, ignoring option on irq " \
++                          "0x%x.\n",msg,card->irq0); \
++                card->options.option = reset_value; \
++        }
++#define IGNORE_PARAM_NEQ(option,value,reset_value,msg) \
++        if (card->options.option != value) { \
++                PRINT_ERR("%s not supported with layer 2 " \
++                          "functionality, ignoring option on irq " \
++                          "0x%x.\n",msg,card->irq0); \
++                card->options.option = reset_value; \
++        }
++
++
++static void qeth_make_parameters_consistent(qeth_card_t *card) 
++{
++
++	if (card->options.layer2 == DO_LAYER2) {
++		if (card->type == QETH_CARD_TYPE_IQD) {
++			PRINT_ERR("Device on irq 0x%x does not support " \
++				  "layer 2 functionality. "  \
++				  "Ignoring layer2 option.\n",card->irq0 );
++		}
++		IGNORE_PARAM_NEQ(routing_type4,NO_ROUTER,NO_ROUTER,
++				 "Routing options are");
++#ifdef QETH_IPV6
++		IGNORE_PARAM_NEQ(routing_type6,NO_ROUTER,NO_ROUTER,
++				 "Routing options are");
++#endif /* QETH_IPV6 */
++		IGNORE_PARAM_EQ(checksum_type,HW_CHECKSUMMING,
++				QETH_CHECKSUM_DEFAULT,
++				"Checksumming options are");
++		IGNORE_PARAM_NEQ(broadcast_mode,BROADCAST_ALLRINGS,
++				 BROADCAST_ALLRINGS,
++				 "Broadcast mode options are");
++		IGNORE_PARAM_NEQ(macaddr_mode,MACADDR_NONCANONICAL,
++				 MACADDR_NONCANONICAL,
++				 "Canonical MAC addr options are");
++		IGNORE_PARAM_EQ(ena_ipat,ENABLE_TAKEOVER,
++				 DISABLE_TAKEOVER,
++				 "Takeover options are");
++		IGNORE_PARAM_NEQ(fake_broadcast,DONT_FAKE_BROADCAST,
++				 DONT_FAKE_BROADCAST,
++				 "Broadcast faking options are");
++		IGNORE_PARAM_NEQ(add_hhlen,DEFAULT_ADD_HHLEN,
++				 DEFAULT_ADD_HHLEN,"Option add_hhlen is");
++		IGNORE_PARAM_NEQ(fake_ll,DONT_FAKE_LL,
++				 DONT_FAKE_LL,"Option fake_ll is");
++	}
++}
++
+ static void qeth_detach_handler(int irq,int status)
+ {
+         qeth_card_t *card;
+@@ -8819,7 +9838,7 @@
+ 	if ((card=qeth_get_card_by_irq(irq))) {
+ 		qeth_remove_card(card,remove_method);
+         }
+-	qeth_wakeup_procfile();
++	qeth_snmp_notify();
+         my_spin_unlock(&setup_lock);
+ }
+ 
+@@ -8905,49 +9924,6 @@
+ 	return cnt;
+ }
+ 
+-static void qeth_correct_routing_status(qeth_card_t *card)
 -{
--	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;
+-	if (card->type==QETH_CARD_TYPE_IQD) {
+-		/* if it's not a mc router, it's no router */
+-		if ( (card->options.routing_type4 == PRIMARY_ROUTER) ||
+-		     (card->options.routing_type4 == SECONDARY_ROUTER) 
+-#ifdef QETH_IPV6
+-		     ||
+-		     (card->options.routing_type6 == PRIMARY_ROUTER) ||
+-		     (card->options.routing_type6 == SECONDARY_ROUTER) 
+-#endif /* QETH_IPV6 */
+-		     ) {
+-			PRINT_WARN("routing not applicable, reset " \
+-				   "routing status.\n");
+-			card->options.routing_type4=NO_ROUTER;
+-#ifdef QETH_IPV6
+-			card->options.routing_type6=NO_ROUTER;
+-#endif /* QETH_IPV6 */
+-		}
+-		card->options.do_prio_queueing=NO_PRIO_QUEUEING;
+-	} else {
+-		/* if it's a mc router, it's no router */
+-		if ( (card->options.routing_type4 == MULTICAST_ROUTER) ||
+-		     (card->options.routing_type4 == PRIMARY_CONNECTOR) ||
+-		     (card->options.routing_type4 == SECONDARY_CONNECTOR) 
+-#ifdef QETH_IPV6
+-		     ||
+-		     (card->options.routing_type6 == MULTICAST_ROUTER) ||
+-		     (card->options.routing_type6 == PRIMARY_CONNECTOR) ||
+-		     (card->options.routing_type6 == SECONDARY_CONNECTOR) 
+-#endif /* QETH_IPV6 */
+-		     ) {
+-			PRINT_WARN("routing not applicable, reset " \
+-				   "routing status. (Did you mean " \
+-				   "primary_router or secondary_router?)\n");
+-			card->options.routing_type4=NO_ROUTER;
+-#ifdef QETH_IPV6
+-			card->options.routing_type6=NO_ROUTER;
+-#endif /* QETH_IPV6 */
+-		}
 -	}
--	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++;
--        }
--}
+ static int qeth_attach_handler(int irq_to_scan,chandev_probeinfo *probeinfo)
+ {
+ 	int result = 0;
+@@ -9070,6 +10046,7 @@
+ 		card->options.portno = 0;
+ 
+ 	qeth_chandev_parse_options(card,probeinfo->parmstr);
++	qeth_make_parameters_consistent(card);
+ 
+ 	card->has_irq=0;
+ 	card->irq0=irq;
+@@ -9102,7 +10079,6 @@
+ 		goto endloop;
+ 	}
+ 	
+-	qeth_correct_routing_status(card);
+ 	qeth_insert_card_into_list(card);
+ 	
+ 	QETH_DBF_TEXT3(0,trace,"request0");
+@@ -9247,7 +10223,7 @@
+ 			/* means, we prevent looping in
+ 			 * qeth_send_control_data */
+ 			atomic_set(&card->write_busy,0);
+-			qeth_wakeup_procfile();
++			qeth_snmp_notify();
+ 		}
+ 		my_read_unlock(&list_lock);
+ 	}
+@@ -9297,7 +10273,7 @@
+ 	card->tqueue_sst.sync=0;
+ 	schedule_task(&card->tqueue_sst);
+  out:
+-	qeth_wakeup_procfile();
++	qeth_snmp_notify();
+ 	return dev;
+ 
+ }
+@@ -9385,11 +10361,10 @@
+ 	sprintf(dbf_text,"ipevent");
+ 	QETH_DBF_TEXT3(0,trace,dbf_text);
+ 	QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
+-	QETH_DBF_HEX3(0,trace,&dev,sizeof(void*));
+-	sprintf(dbf_text,"%08x",ifa->ifa_address);
+-	QETH_DBF_TEXT3(0,trace,dbf_text);
+-	sprintf(dbf_text,"%08x",ifa->ifa_mask);
+-	QETH_DBF_TEXT3(0,trace,dbf_text);
++		sprintf(dbf_text,"%08x",ifa->ifa_address);
++		QETH_DBF_TEXT3(0,trace,dbf_text);
++		sprintf(dbf_text,"%08x",ifa->ifa_mask);
++		QETH_DBF_TEXT3(0,trace,dbf_text);
+ 
+ #ifdef QETH_VLAN
+ 	if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
+@@ -9418,10 +10393,9 @@
+ 	sprintf(dbf_text,"ip6event");
+ 	QETH_DBF_TEXT3(0,trace,dbf_text);
+ 	QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
+-	QETH_DBF_HEX3(0,trace,&dev,sizeof(void*));
+-	QETH_DBF_HEX3(0,trace,ifa->addr.s6_addr,QETH_DBF_TRACE_LEN);
+-	QETH_DBF_HEX3(0,trace,ifa->addr.s6_addr+QETH_DBF_TRACE_LEN,
+-		      QETH_DBF_TRACE_LEN);
++		QETH_DBF_HEX3(0,trace,ifa->addr.s6_addr,QETH_DBF_TRACE_LEN);
++		QETH_DBF_HEX3(0,trace,ifa->addr.s6_addr+QETH_DBF_TRACE_LEN,
++			      QETH_DBF_TRACE_LEN);
+ 
+ #ifdef QETH_VLAN
+ 	if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
+@@ -9438,8 +10412,63 @@
+ 
+ 	return NOTIFY_DONE;
+ }
++
++static int 
++qeth_multicast6_event(struct notifier_block *this,
++		     unsigned long event, void *ptr)
++{
++	qeth_card_t *card;
++	struct ifmcaddr6 *mc  = (struct ifmcaddr6 *) ptr; 
++	struct net_device *dev = mc->idev->dev;
++	char dbf_text[15];
++
++	sprintf(dbf_text,"mc6event");
++	QETH_DBF_TEXT3(0,trace,dbf_text);
++	QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
++#ifdef QETH_VLAN
++	if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
++		card = (qeth_card_t *)VLAN_DEV_INFO(dev)->real_dev->priv;
++        else
++#endif
++	card=(qeth_card_t *)dev->priv;
++        if (qeth_does_card_exist(card)) {
++		QETH_DBF_HEX3(0,trace,&card,sizeof(void*));
++		qeth_save_dev_flag_state(card);
++		qeth_start_softsetup_thread(card);
++	}
++
++	return NOTIFY_DONE;
++}
++
+ #endif /* QETH_IPV6 */
+ 
++static int 
++qeth_multicast_event(struct notifier_block *this,
++		     unsigned long event, void *ptr)
++{
++	qeth_card_t *card;
++	struct ip_mc_list *mc  = (struct ip_mc_list *) ptr; 
++	struct net_device *dev = mc->interface->dev;
++	char dbf_text[15];
++
++	sprintf(dbf_text,"mc4event");
++	QETH_DBF_TEXT3(0,trace,dbf_text);
++	QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
++#ifdef QETH_VLAN
++	if (qeth_verify_dev(dev)==QETH_VERIFY_IS_VLAN_DEV)
++		card = (qeth_card_t *)VLAN_DEV_INFO(dev)->real_dev->priv;
++        else
++#endif
++	card=(qeth_card_t *)dev->priv;
++        if (qeth_does_card_exist(card)) {
++		QETH_DBF_HEX3(0,trace,&card,sizeof(void*));
++		qeth_save_dev_flag_state(card);
++		qeth_start_softsetup_thread(card);
++	}
++
++	return NOTIFY_DONE;
++}
++
+ static int qeth_reboot_event(struct notifier_block *this,
+    			     unsigned long event,void *ptr)
+ {
+@@ -9481,11 +10510,22 @@
+         0
+ };
+ 
++static struct notifier_block qeth_mc_notifier = {
++        qeth_multicast_event,
++        0
++};
++
+ #ifdef QETH_IPV6
+ static struct notifier_block qeth_ip6_notifier = {
+         qeth_ip6_event,
+         0
+ };
++
++static struct notifier_block qeth_mc6_notifier = {
++        qeth_multicast6_event,
++        0
++};
++
+ #endif /* QETH_IPV6 */
+ 
+ static struct notifier_block qeth_reboot_notifier = {
+@@ -9500,10 +10540,11 @@
+ 	QETH_DBF_TEXT5(0,trace,"regnotif");
+         /* register to be notified on events */
+ 	r=register_netdevice_notifier(&qeth_dev_notifier);
+-        
+ 	r=register_inetaddr_notifier(&qeth_ip_notifier);
++	r=register_multicast_notifier(&qeth_mc_notifier);
+ #ifdef QETH_IPV6
+ 	r=register_inet6addr_notifier(&qeth_ip6_notifier);
++	r=register_multicast6_notifier(&qeth_mc6_notifier);
+ #endif /* QETH_IPV6 */
+ 	r=register_reboot_notifier(&qeth_reboot_notifier);
+ }
+@@ -9516,8 +10557,10 @@
+ 	QETH_DBF_TEXT5(0,trace,"unregnot");
+ 	r=unregister_netdevice_notifier(&qeth_dev_notifier);
+ 	r=unregister_inetaddr_notifier(&qeth_ip_notifier);
++	r=unregister_multicast_notifier(&qeth_mc_notifier);
+ #ifdef QETH_IPV6
+ 	r=unregister_inet6addr_notifier(&qeth_ip6_notifier);
++	r=unregister_multicast6_notifier(&qeth_mc6_notifier);
+ #endif /* QETH_IPV6 */
+ 	r=unregister_reboot_notifier(&qeth_reboot_notifier);
+ }
+@@ -9533,9 +10576,11 @@
+ 	int size;
+ 	tempinfo_t *info;
+ 
++        MOD_INC_USE_COUNT;
+ 	info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
+ 	if (info == NULL) {
+ 		PRINT_WARN("No memory available for data\n");
++		MOD_DEC_USE_COUNT;
+ 		return -ENOMEM;
+ 	} else {
+ 		file->private_data = (void *) info;
+@@ -9556,6 +10601,7 @@
+ 		PRINT_WARN("No memory available for data\n");
+ 		vfree (info);
+ 		rc=-ENOMEM;
++		MOD_DEC_USE_COUNT;
+ 		goto out;
+ 	}
+ 	
+@@ -9583,23 +10629,23 @@
+ 			       "by_ToS");
+ 		}
+ 
++		/* a '+' in the routing indicator means, that broadcast
++		 * packets are not echoed back to the sender */
+ #ifdef QETH_IPV6
+-		if (atomic_read(&card->rt4fld) &&
+-		    atomic_read(&card->rt6fld))
+-			strcpy(router_str, "no");
+-		else if (atomic_read(&card->rt4fld) ||
+-			 atomic_read(&card->rt6fld))
+-			strcpy(router_str, "mix");
++		if (atomic_read(&card->rt4fld) ||
++	   	    atomic_read(&card->rt6fld))
++			strcpy(router_str, "FLD");
+ #else /* QETH_IPV6 */
+ 		if (atomic_read(&card->rt4fld))
+-			strcpy(router_str, "no");
++			strcpy(router_str, "FLD");
+ #endif /* QETH_IPV6 */		
+ 		else if ( ((card->options.routing_type4&ROUTER_MASK)==
+ 		      PRIMARY_ROUTER) 
+ #ifdef QETH_IPV6
+ 		     &&
+-		     ((card->options.routing_type6&ROUTER_MASK)==
+-		      PRIMARY_ROUTER) 
++		     ( ((card->options.routing_type6&ROUTER_MASK)==
++			PRIMARY_ROUTER)||
++		       (!qeth_is_supported(IPA_IPv6)) )
+ #endif /* QETH_IPV6 */
+ 		     ) {
+ 			strcpy(router_str,"pri");
+@@ -9608,8 +10654,9 @@
+ 		      SECONDARY_ROUTER) 
+ #ifdef QETH_IPV6
+ 		     &&
+-		     ((card->options.routing_type6&ROUTER_MASK)==
+-		      SECONDARY_ROUTER) 
++		     ( ((card->options.routing_type6&ROUTER_MASK)==
++			SECONDARY_ROUTER)||
++		       (!qeth_is_supported(IPA_IPv6)) )
+ #endif /* QETH_IPV6 */
+ 		     ) {
+ 			strcpy(router_str,"sec");
+@@ -9618,38 +10665,51 @@
+ 		      MULTICAST_ROUTER) 
+ #ifdef QETH_IPV6
+ 		     &&
+-		     ((card->options.routing_type6&ROUTER_MASK)==
+-		      MULTICAST_ROUTER) 
++		     ( ((card->options.routing_type6&ROUTER_MASK)==
++			MULTICAST_ROUTER)||
++		       (!qeth_is_supported(IPA_IPv6)) )
+ #endif /* QETH_IPV6 */
+ 		     ) {
+-			strcpy(router_str,"mc");
++			if (card->broadcast_capable==BROADCAST_WITHOUT_ECHO)
++				strcpy(router_str,"mc+");
++			else
++				strcpy(router_str,"mc");
+ 		} else
+ 		if ( ((card->options.routing_type4&ROUTER_MASK)==
+ 		      PRIMARY_CONNECTOR) 
+ #ifdef QETH_IPV6
+ 		     &&
+-		     ((card->options.routing_type6&ROUTER_MASK)==
+-		      PRIMARY_CONNECTOR) 
++		     ( ((card->options.routing_type6&ROUTER_MASK)==
++			PRIMARY_CONNECTOR)||
++		       (!qeth_is_supported(IPA_IPv6)) )
+ #endif /* QETH_IPV6 */
+ 		     ) {
+-			strcpy(router_str,"p.c");
++			if (card->broadcast_capable==BROADCAST_WITHOUT_ECHO)
++				strcpy(router_str,"p+c");
++			else
++				strcpy(router_str,"p.c");
+ 		} else
+ 		if ( ((card->options.routing_type4&ROUTER_MASK)==
+ 		      SECONDARY_CONNECTOR)
+ #ifdef QETH_IPV6
+ 		     &&
+-		     ((card->options.routing_type6&ROUTER_MASK)==
+-		      SECONDARY_CONNECTOR) 
++		     ( ((card->options.routing_type6&ROUTER_MASK)==
++			SECONDARY_CONNECTOR)||
++		       (!qeth_is_supported(IPA_IPv6)) )
+ #endif /* QETH_IPV6 */
+ 		     ) {
+-			strcpy(router_str,"s.c");
++			if (card->broadcast_capable==BROADCAST_WITHOUT_ECHO)
++				strcpy(router_str,"s+c");
++			else
++				strcpy(router_str,"s.c");
+ 		} else
+ 		if ( ((card->options.routing_type4&ROUTER_MASK)==
+ 		      NO_ROUTER) 
+ #ifdef QETH_IPV6
+ 		     &&
+-		     ((card->options.routing_type6&ROUTER_MASK)==
+-		      NO_ROUTER) 
++		     ( ((card->options.routing_type6&ROUTER_MASK)==
++			NO_ROUTER)||
++		       (!qeth_is_supported(IPA_IPv6)) )
+ #endif /* QETH_IPV6 */
+ 		     ) {
+ 			strcpy(router_str,"no");
+@@ -9670,7 +10730,8 @@
+ 					card->chpid,
+ 					card->dev_name,
+ 					qeth_get_cardname_short
+-					(card->type,card->link_type),
++					(card->type,card->link_type,
++					 card->is_guest_lan),
+ 					card->options.portno);
+ 		} else if (!atomic_read(&card->is_startlaned)) {
+ 			length+=sprintf(buffer+length,
+@@ -9680,7 +10741,19 @@
+ 					card->chpid,
+ 					card->dev_name,
+ 					qeth_get_cardname_short
+-					(card->type,card->link_type),
++					(card->type,card->link_type,
++					 card->is_guest_lan),
++					card->options.portno);
++		} else if (card->options.layer2 == DO_LAYER2) {
++			length+=sprintf(buffer+length,
++					"%04X/%04X/%04X x%02X %10s %14s %2i" 
++					"  +++ LAYER 2 +++\n",
++					card->devno0,card->devno1,card->devno2,
++					card->chpid,
++					card->dev_name,
++					qeth_get_cardname_short
++					(card->type,card->link_type,
++					 card->is_guest_lan),
+ 					card->options.portno);
+ 		} else {
+ 			length+=sprintf(buffer+length,
+@@ -9689,7 +10762,8 @@
+ 					card->devno0,card->devno1,card->devno2,
+ 					card->chpid,card->dev_name,
+ 					qeth_get_cardname_short
+-					(card->type,card->link_type),
++					(card->type,card->link_type,
++					 card->is_guest_lan),
+ 					card->options.portno,
+ 					checksum_str,
+ 					queueing_str,router_str,bufsize_str,
+@@ -9792,7 +10866,7 @@
+ 	int pos=0,end_pos;
+ 	char dbf_text[15];
+ 
+-	if (*offset) return user_len;
++	if (*offset>0) return user_len;
+ 	buffer=vmalloc(__max(user_len+1,QETH_DBF_MISC_LEN));
+ 	if (buffer == NULL)
+ 		return -ENOMEM;
+@@ -10174,6 +11248,7 @@
+ 	int size;
+ 	char entry_type[5];
+ 
++        MOD_INC_USE_COUNT;
+ 	info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
+ 	if (info == NULL) {
+ 		PRINT_WARN("No memory available for data\n");
+@@ -10308,16 +11383,14 @@
+ {
+ 	loff_t len;
+ 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+-	loff_t n = *offset;
+-	unsigned long pos = n;
+ 	
+-	if (pos != n || pos >= p_info->len) {
++	if (*offset >= p_info->len) {
+ 		return 0;
+ 	} 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;
+ 	}
+ }
+@@ -10327,13 +11400,25 @@
+ static int qeth_procfile_release(struct inode *inode,struct file *file)
+ {
+    	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
++	struct list_head *l,*n;
++  	struct qeth_notify_list *n_entry;
+ 	
+ 	if (p_info) {
+ 		if (p_info->data)
+ 			vfree (p_info->data);
+ 		vfree (p_info);
+ 	}
+-	
++/*remove task-entry to notify from list */
++	spin_lock(&notify_lock);
++	list_for_each_safe(l, n, &notify_list) {
++		n_entry = list_entry(l, struct qeth_notify_list, list);
++		if (n_entry->task == current) {
++			list_del(&n_entry->list);
++			kfree(n_entry);
++		}
++	}
++	spin_unlock(&notify_lock);
++	MOD_DEC_USE_COUNT;
+ 	return 0;
+ }
+ 
+@@ -10352,7 +11437,7 @@
+ 	qeth_card_t *card;
+ #define BUFFER_LEN (10+32+1+5+1+DEV_NAME_LEN+1)
+ 
+-	if (*offset) return user_len;
++	if (*offset>0) return user_len;
+ 	buffer=vmalloc(__max(__max(user_len+1,BUFFER_LEN),QETH_DBF_MISC_LEN));
+ 
+ 	if (buffer == NULL)
+@@ -10476,123 +11561,40 @@
+ 	PRINT_ERR("unknown ipato information command\n");
+ out:
+ 	vfree(buffer);
+-	*offset = user_len;
++	*offset = *offset + user_len;
+ #undef BUFFER_LEN
+ 	return user_len;
+ }
+ 
+-static int qeth_procfile_getinterfaces(unsigned long arg) 
+-{
+-	qeth_card_t *card;
 -
+-	char parms[16];
+-	char *buffer;
+-	char *buffer_pointer;
+-	__u32 version,valid_fields,qeth_version,number_of_devices,if_index;
+-	__u32 data_size,data_len;
+-	unsigned long ioctl_flags;
+-	int result=0;
 -
--/* SECTION: Managing wrappers for ccwcache */
+-	/* the struct of version 0 is:
+-typedef struct dev_list
++static int qeth_snmp_register(struct task_struct *p, unsigned long arg)
+ {
+-  char device_name[IFNAME_MAXLEN]; // OSA-Exp device name (e.g. eth0)
+-  __u32 if_index;                  // interface index from kernel
+-  __u32 flags;             	   // device charateristics
+-} __attribute__((packed)) DEV_LIST;
 -
--#define TAPE_EMERGENCY_REQUESTS 16
+-typedef struct osaexp_dev_ver0
+-{
+-  __u32 version;                // structure version
+-  __u32 valid_fields;           // bitmask of fields that are really filled
+-  __u32 qeth_version;           // qeth driver version
+-  __u32 number_of_devices;      // number of OSA Express devices
+-  struct dev_list devices[0]; // list of OSA Express devices
+-} __attribute__((packed)) OSAEXP_DEV_VER0;
+-	*/
 -
--static ccw_req_t *tape_emergency_req[TAPE_EMERGENCY_REQUESTS] =
--{NULL,};
--static spinlock_t tape_emergency_req_lock = SPIN_LOCK_UNLOCKED;
+-	version = 0;
+-	valid_fields = 0;
+-	qeth_version = 0;
+-	number_of_devices= 0;
 -
--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);
--	}
--}
+-	copy_from_user((void*)parms,(void*)arg,sizeof(parms));
+-	memcpy(&data_size,parms,sizeof(__u32));
 -
--#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
+-	if ( !(data_size > 0) ) 
+-	     return -EFAULT;
+-	if ( data_size > IOCTL_MAX_TRANSFER_SIZE ) 
+-	     return -EFAULT;
+-	if ( !access_ok(VERIFY_WRITE, (void *)arg, data_size) )
+-	     return -EFAULT;
 -
--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));
+-	my_read_lock(&list_lock);
+-	card = firstcard;
+-#define IOCTL_USER_STRUCT_SIZE (DEV_NAME_LEN*sizeof(char)) + \
+-	sizeof(__u32) + sizeof(__u32)
+-	while (card) {
+-	     if (card->type == QETH_CARD_TYPE_OSAE)
+-		  number_of_devices=number_of_devices + IOCTL_USER_STRUCT_SIZE;
+-	     card = card->next;
 -	}
--	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);
+-#undef IOCTL_USER_STRUCT_SIZE
+-	if ((number_of_devices + 4*sizeof(__u32)) >= data_size) {
+-		result=-ENOMEM;
+-		goto out;
 -	}
--}
--
--/*
-- * 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);
+-	number_of_devices=0;
+-	card = firstcard;
+-	buffer = (char *)vmalloc(data_size);
+-	if (!buffer) {
+-		result=-EFAULT;
+-		goto out;
++	struct qeth_notify_list *n_entry;
++	struct list_head *l;	
++	QETH_DBF_TEXT5(0,trace,"snmpreg");
++
++	/*check first if entry already exists*/
++	
++	spin_lock(&notify_lock);
++	list_for_each(l, &notify_list) {
++		n_entry = list_entry(l, struct qeth_notify_list, list);
++		if (n_entry->task == p) {
++			n_entry->signum = (int) arg;
++			goto reg_out;
++		}
++			
+ 	}
+-	buffer_pointer = ((char *)(buffer)) + (4*sizeof(__u32)) ; 
+-	while (card) {
+-	     if ((card->type == QETH_CARD_TYPE_OSAE)&&
+-		 (!atomic_read(&card->is_gone))&&
+-		 (atomic_read(&card->is_hardsetup))&&
+-		 (atomic_read(&card->is_registered))) {
 -
--	if (!cqr) {
--#ifdef TAPE_DEBUG
--		PRINT_WARN ("empty CQR generated\n");
--#endif
+-		  memcpy(buffer_pointer,card->dev_name,DEV_NAME_LEN);
+-		  buffer_pointer = buffer_pointer + DEV_NAME_LEN;
+-		  if_index=card->dev->ifindex;
+-		  memcpy(buffer_pointer,&if_index,sizeof(__u32));
+-		  buffer_pointer = buffer_pointer + sizeof(__u32);
+-		  memcpy(buffer_pointer,&ioctl_flags,sizeof(__u32));
+-		  buffer_pointer = buffer_pointer + sizeof(__u32);
+-		  number_of_devices=number_of_devices+1;
+-	     }
+-	     card = card->next;
 -	}
--	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;
--}
+-	/* we copy the real size */
+-	data_len=buffer_pointer-buffer;
 -
+-	buffer_pointer = buffer;
+-	/* copy the header information at the beginning of the buffer */
+-	memcpy(buffer_pointer,&version,sizeof(__u32));
+-	memcpy(((char *)buffer_pointer)+sizeof(__u32),&valid_fields,
+-	       sizeof(__u32));
+-	memcpy(((char *)buffer_pointer)+(2*sizeof(__u32)),&qeth_version,
+-	       sizeof(__u32));
+-	memcpy(((char *)buffer_pointer)+(3*sizeof(__u32)),&number_of_devices,
+-	       sizeof(__u32));
+-	copy_to_user((char *)arg,buffer,data_len);
+-	vfree(buffer);
+-out:
+-	my_read_unlock(&list_lock);
+-	return result;
 -
--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;
--}
+-#undef PARMS_BUFFERLENGTH
 -
+-};
 -
--void
--tape_dump_sense (devstat_t * stat)
+-static int qeth_procfile_interfacechanges(unsigned long arg) 
 -{
--#ifdef TAPE_DEBUG
--        int sl;
--#endif
--#if 0
+-	return qeth_sleepon_procfile();
 -
--	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
--}
++	spin_unlock(&notify_lock);
++	n_entry = (struct qeth_notify_list *)
++			kmalloc(sizeof(struct qeth_notify_list),GFP_KERNEL);
++	if (!n_entry)
++		return -ENOMEM;
++	n_entry->task = p;
++	n_entry->signum = (int) arg;
++	spin_lock(&notify_lock);
++	list_add(&n_entry->list,&notify_list);
++reg_out:
++	spin_unlock(&notify_lock);
++	return 0;
+ } 
+ 
+ static int qeth_procfile_ioctl(struct inode *inode, struct file *file,
+@@ -10600,19 +11602,17 @@
+ {
+ 
+      int result;
+-     down_interruptible(&qeth_procfile_ioctl_lock);
++     
+      switch (cmd) {
 -
--/*
-- * Setup tape_info_t structure of a tape device
-- */
--int
--tape_setup (tape_info_t * ti, int irq, int minor)
+-     case QETH_IOCPROC_OSAEINTERFACES:
+-	     result = qeth_procfile_getinterfaces(arg);
+-	     break;
+-     case QETH_IOCPROC_INTERFACECHANGES:
+-	     result = qeth_procfile_interfacechanges(arg);
++     case QETH_IOCPROC_REGISTER:
++	     if ( (arg > 0) && (arg < 32) ) 
++		     result = qeth_snmp_register(current,arg);
++	     else 
++		     result = -EINVAL;
+ 	     break;
+      default:
+ 	     result = -EOPNOTSUPP;	     
+      }
+-     up(&qeth_procfile_ioctl_lock);
+      return result;
+ };
+ 
+@@ -10644,10 +11644,6 @@
+ 					 S_IFREG|0644,&proc_root);
+ 	if (qeth_proc_file) {
+ 		qeth_proc_file->proc_fops = &qeth_procfile_fops;
+-		sema_init(&qeth_procfile_ioctl_sem,
+-			  PROCFILE_SLEEP_SEM_MAX_VALUE);
+-		sema_init(&qeth_procfile_ioctl_lock,
+-			  PROCFILE_IOCTL_SEM_MAX_VALUE);
+ 	} else proc_file_registration=-1;
+ 
+ 	if (proc_file_registration)
+@@ -10796,7 +11792,11 @@
+ 	global_stay_in_mem = chandev_persist(chandev_type_qeth);
+ #endif /* MODULE */
+ 
+-        spin_lock_init(&setup_lock);
++/*SNMP init stuff*/
++	spin_lock_init(&notify_lock);
++	INIT_LIST_HEAD(&notify_list);
++
++	spin_lock_init(&setup_lock);
+ 
+ 	spin_lock_init(&ipato_list_lock);
+ 
+@@ -10918,6 +11918,7 @@
+ #endif /* QETH_IPV6 */
+ 	qeth_unregister_dbf_views();
+ 	qeth_free_all_spare_bufs();
++
+ 	return result;
+ }
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/qeth.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/qeth.h	2006-01-30 22:25:25.000000000 -0700
+@@ -15,7 +15,7 @@
+ 
+ #define QETH_NAME " qeth"
+ 
+-#define VERSION_QETH_H "$Revision: 1.113 $"
++#define VERSION_QETH_H "$Revision: 1.113.4.9 $"
+ 
+ /******************** CONFIG STUFF ***********************/
+ //#define QETH_DBF_LIKE_HELL
+@@ -47,7 +47,7 @@
+ /********************* TUNING STUFF **************************/
+ #define HIGH_WATERMARK_PACK		5
+ #define LOW_WATERMARK_PACK		2
+-#define WATERMARK_FUZZ			2
++#define WATERMARK_FUZZ			1
+ 
+ #define QETH_MAX_INPUT_THRESHOLD 500
+ #define QETH_MAX_OUTPUT_THRESHOLD 300 /* ? */
+@@ -85,6 +85,22 @@
+ #define QETH_HARDSETUP_CLEAR_LAPS 3
+ #define QETH_RECOVERY_HARDSETUP_RETRY 2
+ 
++/* the worst case stack usage is:
++ * qeth_hard_start_xmit
++ * do_QDIO
++ * qeth_qdio_output_handler
++ * do_QDIO
++ * qeth_qdio_output_handler
++ * (no more recursion as we have called netif_stop_queue)
++ */
++#ifdef CONFIG_ARCH_S390X
++#define STACK_PTR_MASK 0x3fff
++#define WORST_CASE_STACK_USAGE 1100
++#else /* CONFIG_ARCH_S390X */
++#define STACK_PTR_MASK 0x1fff
++#define WORST_CASE_STACK_USAGE 800
++#endif /* CONFIG_ARCH_S390X */
++
+ /************************* DEBUG FACILITY STUFF *********************/
+ 
+ #define QETH_DBF_HEX(ex,name,level,addr,len) \
+@@ -238,6 +254,7 @@
+ #define QETH_MPC_LINK_TYPE_FAST_ETHERNET 0x01
+ #define QETH_MPC_LINK_TYPE_HSTR 0x02
+ #define QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET 0x03
++#define QETH_MPC_LINK_TYPE_10GIG_ETHERNET 0x10
+ #define QETH_MPC_LINK_TYPE_LANE_ETH100 0x81
+ #define QETH_MPC_LINK_TYPE_LANE_TR 0x82
+ #define QETH_MPC_LINK_TYPE_LANE_ETH1000 0x83
+@@ -249,10 +266,18 @@
+ #define QETH_HEADER_SIZE	32
+ #define QETH_IP_HEADER_SIZE	40
+ #define QETH_HEADER_LEN_POS	8
++/*ATT: packet length in LAYER 2 mode is at offset 0x06*/	
++#define QETH_HEADER2_LEN_POS 	6	 
+ /* flags for the header: */
+ #define QETH_HEADER_PASSTHRU	0x10
+ #define QETH_HEADER_IPV6	0x80
+ 
++#define QETH_ETH_MC_MAC_V4      0x0100 /* like v4 */
++#define QETH_ETH_MC_MAC_V6      0x3333 /* like v6 */
++/* tr mc mac is longer, but that will be enough to detect mc frames */
++#define QETH_TR_MC_MAC_NC       0xc000 /* non-canonical */
++#define QETH_TR_MC_MAC_C        0x0300 /* canonical */
++
+ #define QETH_CAST_FLAGS		0x07
+ #define QETH_CAST_UNICAST	6
+ #define QETH_CAST_MULTICAST	4
+@@ -260,6 +285,11 @@
+ #define QETH_CAST_ANYCAST	7
+ #define QETH_CAST_NOCAST	0
+ 
++#define QETH_QDIO_HEADER2_FLAG_MULTICAST_FRAME     0x01
++#define QETH_QDIO_HEADER2_FLAG_BROADCAST_FRAME     0x02
++#define QETH_QDIO_HEADER2_FLAG_UNICAST_FRAME       0x04
++#define QETH_QDIO_HEADER2_FLAG_VLAN_FRAME          0x10
++
+ /* VLAN defines */
+ #define QETH_EXT_HEADER_VLAN_FRAME	  0x01
+ #define QETH_EXT_HEADER_TOKEN_ID	  0x02
+@@ -284,18 +314,6 @@
+ 	}
+ }
+ 
+-inline static unsigned short qeth_get_additional_dev_flags(int cardtype)
 -{
--	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;
+-	switch (cardtype) {
+-	case QETH_CARD_TYPE_IQD: return IFF_NOARP;
+-#ifdef QETH_IPV6
+-	default: return 0;
+-#else /* QETH_IPV6 */
+-	default: return IFF_NOARP;
+-#endif /* QETH_IPV6 */
 -	}
--	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 */
+ inline static int qeth_get_hlen(__u8 link_type)
+ {
+ #ifdef QETH_IPV6
+@@ -330,7 +348,6 @@
+ void (*qeth_my_eth_header_cache_update)(struct hh_cache *,struct net_device *,
+ 	 unsigned char *);
+ 
+-#ifdef QETH_IPV6
+ typedef int (*__qeth_temp1)(struct sk_buff *,struct net_device *,
+ 	unsigned short,void *,void *,unsigned);
+ inline static __qeth_temp1 qeth_get_hard_header(__u8 link_type)
+@@ -391,7 +408,7 @@
+ 	struct ethhdr *eth;
+ 	
+ 	skb->mac.raw=skb->data;
+-	skb_pull(skb,ETH_ALEN*2+sizeof(short));
++	skb_pull(skb,ETH_ALEN*2+2); /* dest, src, type */
+ 	eth=skb->mac.ethernet;
+ 	
+ 	if(*eth->h_dest&1) {
+@@ -400,7 +417,10 @@
+ 		else
+ 			skb->pkt_type=PACKET_MULTICAST;
+    	} else {
++		if (memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
+ 		skb->pkt_type=PACKET_OTHERHOST;
++		else
++			skb->pkt_type=PACKET_HOST;
+ 	}
+ 	if (ntohs(eth->h_proto)>=1536) return eth->h_proto;
+ 	if (*(unsigned short *)(skb->data) == 0xFFFF)
+@@ -417,9 +437,9 @@
+ 		return tr_type_trans;
+ 	default:
+ 		return qeth_eth_type_trans;
++		
+ 	}
+ }
+-#endif /* QETH_IPV6 */
+ 
+ inline static const char *qeth_get_link_type_name(int cardtype,__u8 linktype)
+ {
+@@ -430,6 +450,7 @@
+ 		case QETH_MPC_LINK_TYPE_FAST_ETHERNET: return "Fast Eth";
+ 		case QETH_MPC_LINK_TYPE_HSTR: return "HSTR";
+ 		case QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET: return "Gigabit Eth";
++		case QETH_MPC_LINK_TYPE_10GIG_ETHERNET: return "10Gig Eth";
+ 		case QETH_MPC_LINK_TYPE_LANE_ETH100: return "LANE Eth100";
+ 		case QETH_MPC_LINK_TYPE_LANE_TR: return "LANE TR";
+ 		case QETH_MPC_LINK_TYPE_LANE_ETH1000: return "LANE Eth1000";
+@@ -529,7 +550,7 @@
+ #define QETH_FAKE_LL_ADDR_LEN ETH_ALEN /* 6 */
+ #define QETH_FAKE_LL_DEST_MAC_POS 0
+ #define QETH_FAKE_LL_SRC_MAC_POS 6
+-#define QETH_FAKE_LL_SRC_MAC_POS_IN_QDIO_HDR 6
++#define QETH_FAKE_LL_SRC_MAC_POS_IN_QDIO_HDR 18
+ #define QETH_FAKE_LL_PROT_POS 12 
+ #define QETH_FAKE_LL_V4_ADDR_POS 16
+ #define QETH_FAKE_LL_V6_ADDR_POS 24
+@@ -591,8 +612,10 @@
+ #define PARSE_ROUTING_TYPE6 17
+ #define PARSE_FAKE_LL 18
+ #define PARSE_ASYNC_IQD 19
++#define PARSE_LAYER2 20
++#define PARSE_COUNT 21
++#define QETH_DEFAULT_LAYER2 0
+ 
+-#define PARSE_COUNT 20
+ 
+ #define NO_PRIO_QUEUEING 0
+ #define PRIO_QUEUEING_PREC 1
+@@ -603,7 +626,7 @@
+ #define MULTICAST_ROUTER 3
+ #define PRIMARY_CONNECTOR 4
+ #define SECONDARY_CONNECTOR 5
+-#define ROUTER_MASK 0xf /* used to remove SET_ROUTING_FLAG
++#define ROUTER_MASK 0xf /* used to remove RESET_ROUTING_FLAG
+ 			   from routing_type */
+ #define RESET_ROUTING_FLAG 0x10 /* used to indicate, that setting
+ 				   the routing type is desired */
+@@ -621,7 +644,8 @@
+ #define DONT_FAKE_LL 1
+ #define SYNC_IQD 0
+ #define ASYNC_IQD 1
 -
--        /* 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
++#define DONT_LAYER2 0
++#define DO_LAYER2 1
+ #define QETH_BREAKOUT_LEAVE 1
+ #define QETH_BREAKOUT_AGAIN 2
+ 
+@@ -629,6 +653,9 @@
+ #define QETH_DONT_WAIT_FOR_LOCK 1
+ #define QETH_LOCK_ALREADY_HELD 2
+ 
++#define BROADCAST_WITH_ECHO 1
++#define BROADCAST_WITHOUT_ECHO 2
++
+ #define PROBLEM_CARD_HAS_STARTLANED 1
+ #define PROBLEM_RECEIVED_IDX_TERMINATE 2
+ #define PROBLEM_ACTIVATE_CHECK_CONDITION 3
+@@ -712,6 +739,7 @@
+ 	int add_hhlen;
+ 	int fake_ll;
+ 	int async_iqd;
++	int layer2;
+ };
+ 
+ typedef struct qeth_hdr_t {
+@@ -782,6 +810,7 @@
+ 
+ 	__u8 link_type;
+ 
++	int is_guest_lan;
+ 	int do_pfix; /* to avoid doing diag98 for vm guest lan devices */
+ 
+ 	/* inbound buffer management */
+@@ -820,15 +849,16 @@
+ 	int (*hard_header_cache)(struct neighbour *,struct hh_cache *);
+ 	void (*header_cache_update)(struct hh_cache *,struct net_device *,
+ 				    unsigned char *);
+-	unsigned short (*type_trans)(struct sk_buff *,struct net_device *);
+-	int type_trans_correction;
+ #endif /* QETH_IPV6 */
++	unsigned short (*type_trans)(struct sk_buff *,struct net_device *);
+ 
+ #ifdef QETH_VLAN
+         struct vlan_group *vlangrp;
+         spinlock_t vlan_lock;
 -
--#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
+ #endif
++	__u8 vlans_current[VLAN_GROUP_ARRAY_LEN/(8*sizeof(__u8))];
++	__u8 vlans_new[VLAN_GROUP_ARRAY_LEN/(8*sizeof(__u8))];
++
+ 	char dev_name[DEV_NAME_LEN];		/* pointed to by dev->name */
+ 	char dev_basename[DEV_NAME_LEN];
+ 	struct net_device *dev;
+@@ -846,13 +876,14 @@
+ 	atomic_t is_softsetup;		/* card is setup by softsetup */
+ 	atomic_t is_open;		/* card is in use */
+ 	atomic_t is_gone;               /* after a msck */
++	atomic_t mac_registered;
+ 
+ 	int has_irq;			/* once a request_irq was successful */
+ 
+ 	/* prevents deadlocks :-O */
+ 	spinlock_t softsetup_lock;
+ 	spinlock_t hardsetup_lock;
+-	spinlock_t ioctl_lock;
++	struct semaphore ioctl_sem;
+ 	atomic_t softsetup_thread_is_running;
+ 	struct semaphore softsetup_thread_sem;
+ 	struct tq_struct tqueue_sst;
+@@ -907,6 +938,8 @@
+ 	__u32 ipa6_enabled;
+ 	__u32 adp_supported;
+ 
++	__u32 csum_enable_mask;
++
+ 	atomic_t startlan_attempts;
+ 	atomic_t enable_routing_attempts4;
+ 	atomic_t rt4fld;
+@@ -1004,6 +1037,30 @@
+ 	struct mydevreg_t *prev;
+ } mydevreg_t;
+ 
++/*user process notification stuff */
++spinlock_t notify_lock;
++struct list_head notify_list;
++struct qeth_notify_list {
++	struct list_head list;
++	struct task_struct *task;
++	int signum;
++};
++
++inline static unsigned short 
++qeth_get_additional_dev_flags(qeth_card_t *card)
++{
++	if (card->options.layer2 == DO_LAYER2)
++		return 0;
++	switch (card->type) {
++	case QETH_CARD_TYPE_IQD: return IFF_NOARP;
++#ifdef QETH_IPV6
++	default: return 0;
++#else /* QETH_IPV6 */
++	default: return IFF_NOARP;
++#endif /* QETH_IPV6 */
++	}
++}
++
+ inline static int qeth_get_arphrd_type(int cardtype,int linktype)
+ {
+ 	switch (cardtype) {
+@@ -1011,7 +1068,7 @@
+ 				  case QETH_MPC_LINK_TYPE_LANE_TR:
+ 					  /* fallthrough */
+ 				  case QETH_MPC_LINK_TYPE_HSTR:
+-					  return ARPHRD_IEEE802;
++					  return ARPHRD_IEEE802_TR;
+ 				  default: return ARPHRD_ETHER;
+ 				  }
+ 	case QETH_CARD_TYPE_IQD: return ARPHRD_ETHER;
+@@ -1036,28 +1093,42 @@
+ 	}
+ }
+ 
+-inline static const char *qeth_get_cardname(int cardtype)
++inline static const char *qeth_get_cardname(int cardtype,int is_guest_lan)
+ {
+-	switch (cardtype) {
+-	case QETH_CARD_TYPE_UNKNOWN: return "n unknown";
+-	case QETH_CARD_TYPE_OSAE: return "n OSD Express";
+-	case QETH_CARD_TYPE_IQD: return " HiperSockets";
+-	default: return " strange";
++	if (is_guest_lan) {
++		switch (cardtype) {
++		case QETH_CARD_TYPE_UNKNOWN: return "n unknown";
++		case QETH_CARD_TYPE_OSAE: return " Guest LAN QDIO";
++		case QETH_CARD_TYPE_IQD: return " Guest LAN Hiper";
++		default: return " strange";
++		}
++	} else {
++		switch (cardtype) {
++		case QETH_CARD_TYPE_UNKNOWN: return "n unknown";
++		case QETH_CARD_TYPE_OSAE: return "n OSD Express";
++		case QETH_CARD_TYPE_IQD: return " HiperSockets";
++		default: return " strange";
++		}
+ 	}
+ }
+ 
+ /* max length to be returned: 14 */
+-inline static const char *qeth_get_cardname_short(int cardtype,__u8 link_type)
++inline static const char *qeth_get_cardname_short(int cardtype,__u8 link_type,
++						  int is_guest_lan)
+ {
+ 	switch (cardtype) {
+ 	case QETH_CARD_TYPE_UNKNOWN: return "unknown";
+-	case QETH_CARD_TYPE_OSAE: switch (link_type) {
++	case QETH_CARD_TYPE_OSAE: if (is_guest_lan)
++					  return "GuestLAN QDIO";
++				  switch (link_type) {
+ 			      	  case QETH_MPC_LINK_TYPE_FAST_ETHERNET:
+ 		     			  return "OSD_100";
+ 			      	  case QETH_MPC_LINK_TYPE_HSTR:
+ 		     			  return "HSTR";
+ 	     			  case QETH_MPC_LINK_TYPE_GIGABIT_ETHERNET:
+      					  return "OSD_1000";
++				  case QETH_MPC_LINK_TYPE_10GIG_ETHERNET:
++					  return "OSD_10GIG";
+ 				  case QETH_MPC_LINK_TYPE_LANE_ETH100:
+      					  return "OSD_FE_LANE";
+ 				  case QETH_MPC_LINK_TYPE_LANE_TR:
+@@ -1068,7 +1139,7 @@
+      					  return "OSD_ATM_LANE";
+ 				  default: return "OSD_Express";
+ 				  }
+-	case QETH_CARD_TYPE_IQD: return "HiperSockets";
++	case QETH_CARD_TYPE_IQD: return (is_guest_lan)?"GuestLAN Hiper":"HiperSockets";
+ 	default: return " strange";
+ 	}
+ }
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/net/qeth_mpc.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/qeth_mpc.h	2006-01-30 22:25:25.000000000 -0700
+@@ -11,7 +11,7 @@
+ #ifndef __QETH_MPC_H__
+ #define __QETH_MPC_H__
+ 
+-#define VERSION_QETH_MPC_H "$Revision: 1.42 $"
++#define VERSION_QETH_MPC_H "$Revision: 1.42.4.5 $"
+ 
+ #define QETH_IPA_TIMEOUT (card->ipa_timeout)
+ #define QETH_MPC_TIMEOUT 2000
+@@ -164,6 +164,9 @@
+ 
+ #define QETH_ULP_ENABLE_LINKNUM(buffer) (buffer+0x61)
+ #define QETH_ULP_ENABLE_DEST_ADDR(buffer) (buffer+0x2c)
++#define QETH_ULP_ENABLE_PROT_TYPE(buffer) (buffer+0x50)
++#define QETH_ULP_ENABLE_PROT_TCPIP  0x03
++#define QETH_ULP_ENABLE_PROT_LAYER2 0x08
+ #define QETH_ULP_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53)
+ #define QETH_ULP_ENABLE_PORTNAME_AND_LL(buffer) (buffer+0x62)
+ 
+@@ -245,6 +248,12 @@
+ 
+ #define IPA_CMD_STARTLAN 0x01
+ #define IPA_CMD_STOPLAN 0x02
++#define IPA_CMD_SETVMAC 0x21
++#define IPA_CMD_DELVMAC 0x22
++#define IPA_CMD_SETGMAC 0x23
++#define IPA_CMD_DELGMAC 0x24
++#define IPA_CMD_SETVLAN 0x25
++#define IPA_CMD_DELVLAN 0x26
+ #define IPA_CMD_SETIP 0xb1
+ #define IPA_CMD_DELIP 0xb7
+ #define IPA_CMD_QIPASSIST 0xb2
+@@ -288,6 +297,7 @@
+ #define IPA_PASSTHRU 0x00001000L
+ #define IPA_FULL_VLAN 0x00004000L
+ #define IPA_SOURCE_MAC_AVAIL 0x00010000L
++#define IPA_OSA_MC_ROUTER_AVAIL 0x00020000L
+ 
+ #define IPA_SETADP_QUERY_COMMANDS_SUPPORTED 0x01
+ #define IPA_SETADP_ALTER_MAC_ADDRESS 0x02
+@@ -331,7 +341,7 @@
+ #define IPA_CMD_ASS_ARP_QUERY_INFO 0x0104
+ #define IPA_CMD_ASS_ARP_QUERY_STATS 0x0204
+ 
+-#define IPA_CHECKSUM_ENABLE_MASK 0x001f
++#define IPA_CHECKSUM_DEFAULT_ENABLE_MASK 0x001a
+ 
+ #define IPA_CMD_ASS_FILTER_SET_TYPES 0x0003
+ 
+@@ -434,6 +444,14 @@
+ 			__u8 type;
+ 		} setrtg;
+ 		struct ipa_setadp_cmd setadapterparms;
++/*set/del Vmacs and Gmacs*/
++		struct {
++			__u32 mac_length;
++			__u8 mac[6];
++		} setdelmac;
++		struct {
++			__u16  vlan_id;
++		} setdelvlan;
+ 		struct {
+ 			__u32 command;
+ #define ADDR_FRAME_TYPE_DIX 1
+@@ -461,12 +479,8 @@
+ 	} data;
+ } ipa_cmd_t __attribute__ ((packed));
+ 
+-#define QETH_IOC_MAGIC 0x22
+-#define QETH_IOCPROC_OSAEINTERFACES _IOWR(QETH_IOC_MAGIC, 1, arg)
+-#define QETH_IOCPROC_INTERFACECHANGES _IOWR(QETH_IOC_MAGIC, 2, arg)
 -
--#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);
--		}
--            }
+ #define SNMP_QUERY_CARD_INFO 0x00000002L
+-#define SNMP_REGISETER_MIB   0x00000004L
++#define SNMP_REGISTER_MIB    0x00000004L
+ #define SNMP_GET_OID         0x00000010L
+ #define SNMP_SET_OID         0x00000011L
+ #define SNMP_GET_NEXT_OID    0x00000012L
+@@ -565,6 +579,9 @@
+ 				0x00,0x00,0x00,0x40,
+ };
+ 
++#define QETH_IPA_CMD_PROT_TYPE(buffer) (buffer+0x19)
++#define QETH_IPA_CMD_PROT_TCPIP  0x03
++#define QETH_IPA_CMD_PROT_LAYER2 0x08
+ #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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/smsgiucv.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,167 @@
++/*
++ * IUCV special message driver
++ *
++ * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ * Author(s): Martin Schwidefsky (schwidefsky 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 <linux/errno.h>
++#include <linux/slab.h>
++#include <asm/cpcmd.h>
++#include <asm/ebcdic.h>
++
++#include "iucv.h"
++
++struct smsg_callback {
++	struct list_head list;
++	char *prefix;
++	int len;
++	void (*callback)(char *str);
++};
++
++MODULE_AUTHOR
++   ("(C) 2003 IBM Corporation by Martin Schwidefsky (schwidefsky at de.ibm.com)");
++MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
++
++static iucv_handle_t smsg_handle;
++static unsigned short smsg_pathid;
++static spinlock_t smsg_list_lock = SPIN_LOCK_UNLOCKED;
++static struct list_head smsg_list = LIST_HEAD_INIT(smsg_list);
++
++static void
++smsg_connection_complete(iucv_ConnectionComplete *eib, void *pgm_data)
++{
++}
++
++
++static void
++smsg_message_pending(iucv_MessagePending *eib, void *pgm_data)
++{
++	struct smsg_callback *cb;
++        unsigned char *msg;
++	unsigned short len;
++	int rc;
++
++	len = eib->ln1msg2.ipbfln1f;
++	msg = kmalloc(len + 1, GFP_ATOMIC|GFP_DMA);
++	if (!msg) {
++		iucv_reject(eib->ippathid, eib->ipmsgid, eib->iptrgcls);
++		return;
++	}
++	rc = iucv_receive(eib->ippathid, eib->ipmsgid, eib->iptrgcls,
++			  msg, len, 0, 0, 0);
++	if (rc == 0) {
++		msg[len] = 0;
++		EBCASC(msg, len);
++		spin_lock(&smsg_list_lock);
++		list_for_each_entry(cb, &smsg_list, list)
++			if (strncmp(msg + 8, cb->prefix, cb->len) == 0) {
++				cb->callback(msg + 8);
++				break;
++			}
++		spin_unlock(&smsg_list_lock);
++	}
++	kfree(msg);
++}
++
++static iucv_interrupt_ops_t smsg_ops = {
++	.ConnectionComplete = smsg_connection_complete,
++	.MessagePending     = smsg_message_pending,
++};
++
++int
++smsg_register_callback(char *prefix, void (*callback)(char *str))
++{
++	struct smsg_callback *cb;
++
++	cb = kmalloc(sizeof(struct smsg_callback), GFP_KERNEL);
++	if (!cb)
++		return -ENOMEM;
++	cb->prefix = prefix;
++	cb->len = strlen(prefix);
++	cb->callback = callback;
++	spin_lock(&smsg_list_lock);
++	list_add_tail(&cb->list, &smsg_list);
++	spin_unlock(&smsg_list_lock);
++	return 0;
++}
++
++void
++smsg_unregister_callback(char *prefix, void (*callback)(char *str))
++{
++	struct smsg_callback *cb, *tmp;
++
++	spin_lock(&smsg_list_lock);
++	cb = 0;
++	list_for_each_entry(tmp, &smsg_list, list)
++		if (tmp->callback == callback &&
++		    strcmp(tmp->prefix, prefix) == 0) {
++			cb = tmp;
++			list_del(&cb->list);
++			break;
++		}
++	spin_unlock(&smsg_list_lock);
++	kfree(cb);
++}
++
++static void __exit
++smsg_exit(void)
++{
++	if (smsg_handle > 0) {
++		cpcmd("SET SMSG OFF", 0, 0);
++		iucv_sever(smsg_pathid, 0);
++		iucv_unregister_program(smsg_handle);
++	}
++	return;
++}
++
++static int __init
++smsg_init(void)
++{
++	static unsigned char pgmmask[24] = {
++		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
++		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
++		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
++	};
++	int rc;
++	
++	smsg_handle = iucv_register_program("SMSGIUCV        ", "*MSG    ",
++					    pgmmask, &smsg_ops, 0);
++	if (!smsg_handle) {
++		printk(KERN_ERR "SMSGIUCV: failed to register to iucv");
++		return -EIO;	/* better errno ? */
++	}
++	rc = iucv_connect (&smsg_pathid, 1, 0, "*MSG    ", 0, 0, 0, 0,
++			   smsg_handle, 0);
++	if (rc) {
++		printk(KERN_ERR "SMSGIUCV: failed to connect to *MSG");
++		iucv_unregister_program(smsg_handle);
++		smsg_handle = 0;
++		return -EIO;
++	}
++	cpcmd("SET SMSG IUCV", 0, 0);
++	return 0;
++}
++	
++module_init(smsg_init);
++module_exit(smsg_exit);
++MODULE_LICENSE("GPL");
++
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/net/smsgiucv.h	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,10 @@
++/*
++ * IUCV special message driver
++ *
++ * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ * Author(s): Martin Schwidefsky (schwidefsky at de.ibm.com)
++ */
++
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/qdio.c	2006-01-30 22:25:26.000000000 -0700
+@@ -57,7 +57,7 @@
+ 
+ #include <asm/qdio.h>
+ 
+-#define VERSION_QDIO_C "$Revision: 1.145 $"
++#define VERSION_QDIO_C "$Revision: 1.145.4.11 $"
+ 
+ /****************** MODULE PARAMETER VARIABLES ********************/
+ MODULE_AUTHOR("Utz Bacher <utz.bacher at de.ibm.com>");
+@@ -97,10 +97,12 @@
+ #endif /* QDIO_PERFORMANCE_STATS */
+ 
+ static int hydra_thinints=0;
++static int omit_svs=0;
+ 
+ static int indicator_used[INDICATORS_PER_CACHELINE];
+ static __u32 * volatile indicators;
+ static __u32 volatile spare_indicator;
++static atomic_t spare_indicator_usecount;
+ 
+ static debug_info_t *qdio_dbf_setup=NULL;
+ static debug_info_t *qdio_dbf_sbal=NULL;
+@@ -121,6 +123,7 @@
+ static struct semaphore init_sema;
+ 
+ static qdio_chsc_area_t *chsc_area;
++static spinlock_t chsc_area_lock=SPIN_LOCK_UNLOCKED;
+ /* iQDIO stuff: */
+ static volatile qdio_q_t *tiq_list=NULL; /* volatile as it could change
+ 					   during a while loop */
+@@ -198,7 +201,7 @@
+ }
+ static inline unsigned long qdio_get_millis(void)
+ {
+-	return (unsigned long)(qdio_get_micros()>>12);
++	return (unsigned long)(qdio_get_micros()>>10);
+ }
+ 
+ static __inline__ int atomic_return_add (int i, atomic_t *v)
+@@ -518,8 +521,10 @@
+ 
+ 	if (found)
+ 		return indicators+i;
+-	else
++	else {
++		atomic_inc(&spare_indicator_usecount);
+ 		return (__u32 * volatile) &spare_indicator;
++	}
+ }
+ 
+ /* locked by the locks in qdio_activate and qdio_cleanup */
+@@ -531,6 +536,9 @@
+ 		i=addr-indicators;
+ 		indicator_used[i]=0;
+ 	}
++	if (addr==&spare_indicator) {
++		atomic_dec(&spare_indicator_usecount);
++	}
+ }
+ 
+ static inline volatile void tiqdio_clear_summary_bit(__u32 *location)
+@@ -621,8 +629,8 @@
+ 		set_slsb(&q->slsb.acc.val[(gsf+QDIO_MAX_BUFFERS_PER_Q-1)&
+ 			 (QDIO_MAX_BUFFERS_PER_Q-1)],SLSB_P_INPUT_NOT_INIT);
+ 		/* we don't issue this SYNC_MEMORY, as we trust Rick T and
+-		 * moreover will not use the PROCESSING state, so q->polling
+-		 * was 0
++		 * moreover will not use the PROCESSING state under VM,
++		 * so q->polling was 0 anyway.
+ 		SYNC_MEMORY;*/
+ 		if (q->slsb.acc.val[gsf]==SLSB_P_INPUT_PRIMED) {
+ 			/* set our summary bit again, as otherwise there is a
+@@ -655,6 +663,11 @@
+ 	if ((q->is_thinint_q)&&(q->is_input_q)) {
+ 		/* iQDIO */
+ 		spin_lock_irqsave(&ttiq_list_lock,flags);
++		/* in case cleanup has done this already and simultanously
++		 * qdio_unmark_q is called from the interrupt handler, we've
++		 * got to check this in this specific case again */
++		if ((!q->list_prev)||(!q->list_next))
++			goto out;
+ 		if (q->list_next==q) {
+ 			/* q was the only interesting q */
+ 			tiq_list=NULL;
+@@ -667,6 +680,7 @@
+ 			q->list_next=NULL;
+ 			q->list_prev=NULL;
+ 		}
++out:
+ 		spin_unlock_irqrestore(&ttiq_list_lock,flags);
+ 	}
+ }
+@@ -710,7 +724,7 @@
+ 		       (void*)q->sbal[bufno],SBAL_SIZE);
+ }
+ 
+-inline static int qdio_get_outbound_buffer_frontier(qdio_q_t *q)
++static inline int qdio_get_outbound_buffer_frontier(qdio_q_t *q)
+ {
+ 	int f,f_mod_no;
+ 	volatile char *slsb;
+@@ -739,7 +753,7 @@
+ 	if (f==first_not_to_check) goto out;
+ 	slsbyte=slsb[f_mod_no];
+ 
+-	/* the hydra has not fetched the output yet */
++	/* the card has not fetched the output yet */
+ 	if (slsbyte==SLSB_CU_OUTPUT_PRIMED) {
+ #ifdef QDIO_DBF_LIKE_HELL
+ 		QDIO_DBF_TEXT5(0,trace,"outpprim");
+@@ -747,7 +761,7 @@
+ 		goto out;
+ 	}
+ 
+-	/* the hydra got it */
++	/* the card got it */
+ 	if (slsbyte==SLSB_P_OUTPUT_EMPTY) {
+ 		atomic_dec(&q->number_of_buffers_used);
+ 		f++;
+@@ -797,7 +811,7 @@
+ }
+ 
+ /* all buffers are processed */
+-inline static int qdio_is_outbound_q_done(qdio_q_t *q)
++static inline int qdio_is_outbound_q_done(qdio_q_t *q)
+ {
+ 	int no_used;
+ #ifdef QDIO_DBF_LIKE_HELL
+@@ -818,7 +832,7 @@
+ 	return (no_used==0);
+ }
+ 
+-inline static int qdio_has_outbound_q_moved(qdio_q_t *q)
++static inline int qdio_has_outbound_q_moved(qdio_q_t *q)
+ {
+ 	int i;
+ 
+@@ -827,7 +841,6 @@
+ 	if ( (i!=GET_SAVED_FRONTIER(q)) ||
+ 	     (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) {
+ 		SAVE_FRONTIER(q,i);
+-		SAVE_TIMESTAMP(q);
+ #ifdef QDIO_DBF_LIKE_HELL
+ 		QDIO_DBF_TEXT4(0,trace,"oqhasmvd");
+ 		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+@@ -842,9 +855,10 @@
+ 	}
+ }
+ 
+-inline static void qdio_kick_outbound_q(qdio_q_t *q)
++static inline void qdio_kick_outbound_q(qdio_q_t *q)
+ {
+ 	int result;
++	char dbf_text[15];
+ #ifdef QDIO_DBF_LIKE_HELL
+ 	QDIO_DBF_TEXT4(0,trace,"kickoutq");
+ 	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+@@ -852,9 +866,68 @@
+ 
+ 	if (!q->siga_out) return;
+ 
++	/* here's the story with cc=2 and busy bit set (thanks, Rick):
++	 * VM's CP could present us cc=2 and busy bit set on SIGA-write
++	 * during reconfiguration of their Guest LAN (only in HIPERS mode,
++	 * QDIO mode is asynchronous -- cc=2 and busy bit there will take
++	 * the queues down immediately; and not being under VM we have a
++	 * problem on cc=2 and busy bit set right away).
++	 *
++	 * Therefore qdio_siga_output will try for a short time constantly,
++	 * if such a condition occurs. If it doesn't change, it will
++	 * increase the busy_siga_counter and save the timestamp, and
++	 * schedule the queue for later processing (via mark_q, using the
++	 * queue tasklet). __qdio_outbound_processing will check out the
++	 * counter. If non-zero, it will call qdio_kick_outbound_q as often
++	 * as the value of the counter. This will attempt further SIGA
++	 * instructions.
++	 * Every successful SIGA instruction will decrease the counter.
++	 * After some time of no movement, qdio_kick_outbound_q will
++	 * finally fail and reflect corresponding error codes to call
++	 * the upper layer module and have it take the queues down.
++	 *
++	 * Note that this is a change from the original HiperSockets design
++	 * (saying cc=2 and busy bit means take the queues down), but in
++	 * these days Guest LAN didn't exist... excessive cc=2 with busy bit
++	 * conditions will still take the queues down, but the threshold is
++	 * higher due to the Guest LAN environment.
++	 */
++
+ 	result=qdio_siga_output(q);
+ 
+-	if (result) {
++		switch (result) {
++		case 0:
++		/* went smooth this time, reset timestamp */
++#ifdef QDIO_DBF_LIKE_HELL
++			QDIO_DBF_TEXT3(0,trace,"sigawsuc");
++			sprintf(dbf_text,"%4x%2x%2x",q->irq,q->q_no,
++				atomic_read(&q->busy_siga_counter));
++			QDIO_DBF_TEXT3(0,trace,dbf_text);
++#endif /* QDIO_DBF_LIKE_HELL */
++			q->timing.busy_start=0;
++			break;
++		case (2|QDIO_SIGA_ERROR_B_BIT_SET):
++			/* cc=2 and busy bit: */
++		atomic_inc(&q->busy_siga_counter);
++
++			/* if the last siga was successful, save
++			 * timestamp here */
++			if (!q->timing.busy_start)
++				q->timing.busy_start=NOW;
++
++			/* if we're in time, don't touch error_status_flags
++			 * and siga_error */
++			if (NOW-q->timing.busy_start<QDIO_BUSY_BIT_GIVE_UP) {
++				qdio_mark_q(q);
++				break;
++			}
++			QDIO_DBF_TEXT2(0,trace,"cc2REPRT");
++			sprintf(dbf_text,"%4x%2x%2x",q->irq,q->q_no,
++				atomic_read(&q->busy_siga_counter));
++			QDIO_DBF_TEXT3(0,trace,dbf_text);
++			/* else fallthrough and report error */
++		default:
++			/* for plain cc=1, 2 or 3: */
+ 		if (q->siga_error)
+ 			q->error_status_flags|=
+ 				QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;
+@@ -864,7 +937,7 @@
+ 	}
+ }
+ 
+-inline static void qdio_kick_outbound_handler(qdio_q_t *q)
++static inline void qdio_kick_outbound_handler(qdio_q_t *q)
+ {
+ #ifdef QDIO_DBF_LIKE_HELL
+ 	char dbf_text[15];
+@@ -901,8 +974,9 @@
+ 	q->error_status_flags=0;
+ }
+ 
+-static void qdio_outbound_processing(qdio_q_t *q)
++static inline void __qdio_outbound_processing(qdio_q_t *q)
+ {
++	int siga_attempts;
+ #ifdef QDIO_DBF_LIKE_HELL
+ 	QDIO_DBF_TEXT4(0,trace,"qoutproc");
+ 	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+@@ -926,6 +1000,14 @@
+ 	o_p_nc++;
+ #endif /* QDIO_PERFORMANCE_STATS */
+ 
++	/* see comment in qdio_kick_outbound_q */
++	siga_attempts=atomic_read(&q->busy_siga_counter);
++	while (siga_attempts) {
++		atomic_dec(&q->busy_siga_counter);
++		qdio_kick_outbound_q(q);
++		siga_attempts--;
++	}
++
+ #ifdef QDIO_PERFORMANCE_STATS
+ 	perf_stats.tl_runs++;
+ #endif /* QDIO_PERFORMANCE_STATS */
+@@ -936,7 +1018,8 @@
+ 
+ 	if (q->is_iqdio_q) {
+ 		/* for asynchronous queues, we better check, if the fill
+-		 * level is too high */
++		 * level is too high. for synchronous queues, the fill
++		 * level will never be that high. */
+ 		if (atomic_read(&q->number_of_buffers_used)>
+ 		    IQDIO_FILL_LEVEL_TO_POLL) {
+ 			qdio_mark_q(q);
+@@ -950,16 +1033,24 @@
+ 	qdio_release_q(q);
+ }
+ 
++static void qdio_outbound_processing(qdio_q_t *q)
++{
++	__qdio_outbound_processing(q);
++}
++
+ /************************* INBOUND ROUTINES *******************************/
+ 
+ 
+-inline static int qdio_get_inbound_buffer_frontier(qdio_q_t *q)
++static inline int qdio_get_inbound_buffer_frontier(qdio_q_t *q)
+ {
+ 	int f,f_mod_no;
+ 	volatile char *slsb;
+ 	char slsbyte;
+ 	int first_not_to_check;
+ 	char dbf_text[15];
++#ifdef QDIO_USE_PROCESSING_STATE
++	int last_position=-1;
++#endif /* QDIO_USE_PROCESSING_STATE */
+ 
+ #ifdef QDIO_DBF_LIKE_HELL
+ 	QDIO_DBF_TEXT4(0,trace,"getibfro");
+@@ -1002,8 +1093,14 @@
+ 		if (q->siga_sync) {
+ 			set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT);
+ 		} else {
+-			set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_PROCESSING);
++			/* set the previous buffer to NOT_INIT. The current
++			 * buffer will be set to PROCESSING at the end of
++			 * this function to avoid further interrupts. */
++			if (last_position>=0)
++				set_slsb(&slsb[last_position],
++					SLSB_P_INPUT_NOT_INIT);
+ 			atomic_set(&q->polling,1);
++			last_position=f_mod_no;
+ 		}
+ #else /* QDIO_USE_PROCESSING_STATE */
+ 		set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT);
+@@ -1044,6 +1141,10 @@
+ 		f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+ 		atomic_dec(&q->number_of_buffers_used);
+ 
++#ifdef QDIO_USE_PROCESSING_STATE
++		last_position=-1;
++#endif /* QDIO_USE_PROCESSING_STATE */
++
+ 		goto out;
+ 	}
+ 
+@@ -1051,6 +1152,11 @@
+ out:
+ 	q->first_to_check=f_mod_no;
+ 
++#ifdef QDIO_USE_PROCESSING_STATE
++	if (last_position>=0)
++		set_slsb(&slsb[last_position],SLSB_P_INPUT_PROCESSING);
++#endif /* QDIO_USE_PROCESSING_STATE */
++
+ #ifdef QDIO_DBF_LIKE_HELL
+ 	QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));
+ #endif /* QDIO_DBF_LIKE_HELL */
+@@ -1058,7 +1164,7 @@
+ 	return q->first_to_check;
+ }
+ 
+-inline static int qdio_has_inbound_q_moved(qdio_q_t *q)
++static inline int qdio_has_inbound_q_moved(qdio_q_t *q)
+ {
+ 	int i;
+ 
+@@ -1095,7 +1201,7 @@
+ }
+ 
+ /* means, no more buffers to be filled */
+-inline static int iqdio_is_inbound_q_done(qdio_q_t *q)
++static inline int iqdio_is_inbound_q_done(qdio_q_t *q)
+ {
+ 	int no_used;
+ #ifdef QDIO_DBF_LIKE_HELL
+@@ -1148,7 +1254,7 @@
+ 	return 0;
+ }
+ 
+-inline static int qdio_is_inbound_q_done(qdio_q_t *q)
++static inline int qdio_is_inbound_q_done(qdio_q_t *q)
+ {
+ 	int no_used;
+ #ifdef QDIO_DBF_LIKE_HELL
+@@ -1157,7 +1263,7 @@
+ 
+ 	no_used=atomic_read(&q->number_of_buffers_used);
+ 
+-	/* we need that one for synchronization with Hydra, as Hydra
++	/* we need that one for synchronization with the OSA/FCP card, as it
+ 	 * does a kind of PCI avoidance */
+ 	SYNC_MEMORY;
+ 
+@@ -1204,7 +1310,7 @@
+ 	}
+ }
+ 
+-inline static void qdio_kick_inbound_handler(qdio_q_t *q)
++static inline void qdio_kick_inbound_handler(qdio_q_t *q)
+ {
+ 	int count=0;
+ 	int start,end,real_end,i;
+@@ -1250,7 +1356,8 @@
+ #endif /* QDIO_PERFORMANCE_STATS */
+ }
+ 
+-static inline void tiqdio_inbound_processing(qdio_q_t *q)
++static inline void __tiqdio_inbound_processing(qdio_q_t *q,
++					       int spare_ind_was_set)
+ {
+ 	qdio_irq_t *irq_ptr;
+ 	qdio_q_t *oq;
+@@ -1282,8 +1389,18 @@
+ 		goto out;
+ 	}
+ 
+-	if (*(q->dev_st_chg_ind)) {
+-		tiqdio_clear_summary_bit((__u32*)q->dev_st_chg_ind);
++	/* we reset spare_ind_was_set, when the queue does not use the
++	 * spare indicator */
++	if (spare_ind_was_set) {
++		spare_ind_was_set = (q->dev_st_chg_ind==&spare_indicator);
++	}
++
++	if ( (*(q->dev_st_chg_ind)) || (spare_ind_was_set) ) {
++		/* q->dev_st_chg_ind is the indicator, be it shared or not.
++		 * only clear it, if indicator is non-shared */
++		if (!spare_ind_was_set) {
++			tiqdio_clear_summary_bit((__u32*)q->dev_st_chg_ind);
++		}
+ 
+ 		if (q->hydra_gives_outbound_pcis) {
+ 			if (!q->siga_sync_done_on_thinints) {
+@@ -1297,7 +1414,7 @@
+ 		}
+ 
+ 		/* maybe we have to do work on our outbound queues... at least
+-		 * we have to check Hydra outbound-int-capable thinint-capable
++		 * we have to check for outbound-int-capable thinint-capable
+ 		 * queues */
+ 		if (q->hydra_gives_outbound_pcis) {
+ 			irq_ptr=(qdio_irq_t*)q->irq_ptr;
+@@ -1307,7 +1424,7 @@
+ 				perf_stats.tl_runs--;
+ #endif /* QDIO_PERFORMANCE_STATS */
+ 				if (!qdio_is_outbound_q_done(oq)) {
+-					qdio_outbound_processing(oq);
++					__qdio_outbound_processing(oq);
+ 				}
+ 			}
+ 		}
+@@ -1330,7 +1447,12 @@
+ 	qdio_release_q(q);
+ }
+ 
+-static void qdio_inbound_processing(qdio_q_t *q)
++static void tiqdio_inbound_processing(qdio_q_t *q)
++{
++	__tiqdio_inbound_processing(q,atomic_read(&spare_indicator_usecount));
++}
++
++static inline void __qdio_inbound_processing(qdio_q_t *q)
+ {
+ 	int q_laps=0;
+ 
+@@ -1375,11 +1497,17 @@
+ 	qdio_release_q(q);
+ }
+ 
++static void qdio_inbound_processing(qdio_q_t *q)
++{
++	__qdio_inbound_processing(q);
++}
++
+ /************************* MAIN ROUTINES *******************************/
+ 
+ static inline void tiqdio_inbound_checks(void)
+ {
+ 	qdio_q_t *q;
++	int spare_ind_was_set=0;
+ #ifdef QDIO_USE_PROCESSING_STATE
+ 	int q_laps=0;
+ #endif /* QDIO_USE_PROCESSING_STATE */
+@@ -1398,15 +1526,22 @@
+ again:
+ #endif /* QDIO_USE_PROCESSING_STATE */
+ 
++	/* when the spare indicator is used and set, save that and clear it */
++	if ( (atomic_read(&spare_indicator_usecount)) && (spare_indicator) ) {
++		spare_ind_was_set=1;
++		tiqdio_clear_summary_bit((__u32*)&spare_indicator);
++	}
++
+ 	q=(qdio_q_t*)tiq_list;
+ 	/* switch all active queues to processing state */
+ 	do {
+ 		if (!q) break;
+-		tiqdio_inbound_processing(q);
++		__tiqdio_inbound_processing(q,spare_ind_was_set);
+ 		q=(qdio_q_t*)q->list_next;
+ 	} while (q!=(qdio_q_t*)tiq_list);
+ 
+-	/* switch off all queues' processing state */
++	/* switch off all queues' processing state, see comments in
++	 * qdio_get_inbound_buffer_frontier */
+ #ifdef QDIO_USE_PROCESSING_STATE
+ 	q=(qdio_q_t*)tiq_list;
+ 	do {
+@@ -1589,7 +1724,7 @@
+ 		kfree(irq_ptr->output_qs[i]);
+ 
+ 	}
+-	if (irq_ptr->qdr) kfree(irq_ptr->qdr);
++	kfree(irq_ptr->qdr);
+ 	kfree(irq_ptr);
+ }
+ 
+@@ -1758,6 +1893,10 @@
+ 			((irq_ptr->is_thinint_irq)?&tiqdio_inbound_processing:
+ 			 &qdio_inbound_processing);
+ 
++		/* actually this is not used for inbound queues. yet. */
++		atomic_set(&q->busy_siga_counter,0);
++		q->timing.busy_start=0;
++
+ /*		for (j=0;j<QDIO_STATS_NUMBER;j++)
+ 			q->timing.last_transfer_times[j]=(qdio_get_micros()/
+ 							  QDIO_STATS_NUMBER)*j;
+@@ -1849,6 +1988,9 @@
+ 		q->tasklet.func=(void(*)(unsigned long))
+ 			&qdio_outbound_processing;
+ 
++		atomic_set(&q->busy_siga_counter,0);
++		q->timing.busy_start=0;
++
+ 		/* fill in slib */
+ 		if (i>0) irq_ptr->output_qs[i-1]->slib->nsliba=
+ 				 QDIO_PFIX_GET_ADDR(q->slib);
+@@ -1928,9 +2070,10 @@
+ 	perf_stats.start_time_inbound=NOW;
+ #endif /* QDIO_PERFORMANCE_STATS */
+ 
+-	/* VM will do the SVS for us
+-	 * issue SVS to benefit from iqdio interrupt avoidance (SVS clears AISOI)*/
+-	if (!MACHINE_IS_VM) {
++	/* SVS only when needed:
++	 * issue SVS to benefit from iqdio interrupt avoidance
++	 * (SVS clears AISOI)*/
++	if (!omit_svs) {
+ 		tiqdio_clear_global_summary();
+ 	}
+ 
+@@ -2014,7 +2157,7 @@
+ #ifdef QDIO_PERFORMANCE_STATS
+ 				perf_stats.tl_runs--;
+ #endif /* QDIO_PERFORMANCE_STATS */
+-				qdio_inbound_processing(q);
++				__qdio_inbound_processing(q);
+ 			}
+ 		}
+ 		if (irq_ptr->hydra_gives_outbound_pcis) {
+@@ -2027,7 +2170,7 @@
+ 					if (!irq_ptr->sync_done_on_outb_pcis) {
+ 						SYNC_MEMORY;
+ 					}
+-					qdio_outbound_processing(q);
++					__qdio_outbound_processing(q);
+ 				}
+ 			}
+ 		}
+@@ -2206,6 +2349,9 @@
+ static unsigned char qdio_check_siga_needs(int sch)
+ {
+ 	int resp_code,result;
++	unsigned long flags;
++
++	spin_lock_irqsave(&chsc_area_lock,flags);
+ 
+ 	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
+ 	chsc_area->request_block.command_code1=0x0010; /* length */
+@@ -2219,7 +2365,8 @@
+ 		QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \
+ 				"SIGAs for sch x%x.\n",
+ 				result,sch);
+-		return -1; /* all flags set */
++		result=-1; /* all flags set */
++		goto out;
+ 	}
+ 
+ 	resp_code=chsc_area->request_block.operation_data_area.
+@@ -2228,7 +2375,8 @@
+ 		QDIO_PRINT_WARN("response upon checking SIGA needs " \
+ 				"is 0x%x. Using all SIGAs for sch x%x.\n",
+ 				resp_code,sch);
+-		return -1; /* all flags set */
++		result=-1; /* all flags set */
++		goto out;
+ 	}
+ 	if (
+ 	    (!(chsc_area->request_block.operation_data_area.
+@@ -2240,18 +2388,25 @@
+ 	    ) {
+ 		QDIO_PRINT_WARN("huh? problems checking out sch x%x... " \
+ 				"using all SIGAs.\n",sch);
+-		return CHSC_FLAG_SIGA_INPUT_NECESSARY |
++		result=CHSC_FLAG_SIGA_INPUT_NECESSARY |
+ 			CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
+ 			CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */
++		goto out;
+ 	}
+ 
+-	return chsc_area->request_block.operation_data_area.
++	result=chsc_area->request_block.operation_data_area.
+ 		store_qdio_data_response.qdioac;
++out:
++	spin_unlock_irqrestore(&chsc_area_lock,flags);
++	return result;
+ }
+ 
+-static int qdio_check_for_hydra_thinints(void)
++static void qdio_check_for_machine_features(void)
+ {
+ 	int i,result;
++	unsigned long flags;
++
++	spin_lock_irqsave(&chsc_area_lock,flags);
+ 
+ 	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
+ 	chsc_area->request_block.command_code1=0x0010;
+@@ -2260,16 +2415,19 @@
+ 
+ 	if (result) {
+ 		QDIO_PRINT_WARN("CHSC returned cc %i. Won't use adapter " \
+-				"interrupts for any Hydra.\n",result);
+-		return 0;
++				"interrupts for any QDIO device.\n",result);
++		result=0;
++		goto out;
+ 	}
+ 
+ 	i=chsc_area->request_block.operation_data_area.
+ 		store_qdio_data_response.response_code;
+ 	if (i!=1) {
+ 		QDIO_PRINT_WARN("Was not able to determine general " \
+-				"characteristics of all Hydras aboard.\n");
+-		return 0;
++				"characteristics of all QDIO devices " \
++				"aboard.\n");
++		result=0;
++		goto out;
+ 	}
+ 
+ 	/* 4: request block
+@@ -2277,16 +2435,29 @@
+ 	 * 512: chsc char */
+ 	/* check for bit 67 */
+ 	if ( (*(((unsigned int*)(chsc_area))+4+2+2)&0x10000000)!=0x10000000) {
+-		return 0;
++		hydra_thinints=0;
+ 	} else {
+-		return 1;
++		hydra_thinints=1;
++	}
++
++	/* check for bit 56: if aif time delay disablement fac installed,
++	 * omit svs even under lpar (good point by rick again) */
++	if ( (*(((unsigned int*)(chsc_area))+4+2+1)&0x00000080)!=0x00000080) {
++		omit_svs=1;
++	} else {
++		omit_svs=0;
+ 	}
++out:
++	spin_unlock_irqrestore(&chsc_area_lock,flags);
+ }
+ 
+ /* the chsc_area is locked by the lock in qdio_activate */
+ static unsigned int tiqdio_check_chsc_availability(void) {
+ 	int result;
+ 	int i;
++	unsigned long flags;
++
++	spin_lock_irqsave(&chsc_area_lock,flags);
+ 
+ 	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
+ 	chsc_area->request_block.command_code1=0x0010;
+@@ -2327,6 +2498,7 @@
+ 		goto exit;
+ 	}
+ exit:
++	spin_unlock_irqrestore(&chsc_area_lock,flags);
+ 	return result;
+ }
+ 
+@@ -2337,6 +2509,7 @@
+ 	unsigned long real_addr_local_summary_bit;
+ 	unsigned long real_addr_dev_st_chg_ind;
+ 	void *ptr;
++	unsigned long flags;
+ 	char dbf_text[15];
+ 
+ 	unsigned int resp_code;
+@@ -2354,6 +2527,8 @@
+ 			virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind);
+ 	}
+ 
++	spin_lock_irqsave(&chsc_area_lock,flags);
++
+ 	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
+ 	chsc_area->request_block.command_code1=0x0fe0;
+ 	chsc_area->request_block.command_code2=0x0021;
+@@ -2372,12 +2547,19 @@
+ 		isc=TIQDIO_THININT_ISC;
+ 	chsc_area->request_block.operation_data_area.set_chsc.
+ 		subsystem_id=(1<<16)+irq_ptr->irq;
++	/* enables the time delay disablement facility. Don't care
++	 * whether it is really there (i.e. we haven't checked for
++	 * it) */
++	chsc_area->request_block.operation_data_area.set_chsc.
++               word_with_d_bit=0x10000000;
++
+ 
+ 	result=qdio_chsc(chsc_area);
+ 	if (result) {
+ 		QDIO_PRINT_WARN("could not set indicators on irq x%x, " \
+ 				"cc=%i.\n",irq_ptr->irq,result);
+-		return -EIO;
++		result=-EIO;
++		goto out;
+ 	}
+ 
+ 	resp_code=chsc_area->response_block.response_code;
+@@ -2389,14 +2571,18 @@
+ 		QDIO_DBF_TEXT1(0,setup,dbf_text);
+ 		ptr=&chsc_area->response_block;
+ 		QDIO_DBF_HEX2(1,setup,&ptr,QDIO_DBF_SETUP_LEN);
+-		return -EIO;
++		result=-EIO;
++		goto out;
+ 	}
+ 
+ 	QDIO_DBF_TEXT2(0,setup,"setscind");
+ 	QDIO_DBF_HEX2(0,setup,&real_addr_local_summary_bit,
+ 		      sizeof(unsigned long));
+ 	QDIO_DBF_HEX2(0,setup,&real_addr_dev_st_chg_ind,sizeof(unsigned long));
+-	return 0;
++	result=0;
++out:
++	spin_unlock_irqrestore(&chsc_area_lock,flags);
++	return result;
+ }
+ 
+ /* chsc_area would have to be locked if called from outside qdio_activate */
+@@ -2406,10 +2592,13 @@
+ 	unsigned int resp_code;
+ 	int result;
+ 	void *ptr;
++	unsigned long flags;
+ 	char dbf_text[15];
+ 
+ 	if (!irq_ptr->is_thinint_irq) return -ENODEV;
+ 
++	spin_lock_irqsave(&chsc_area_lock,flags);
++
+ 	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
+ 	chsc_area->request_block.command_code1=0x0fe0;
+ 	chsc_area->request_block.command_code2=0x1027;
+@@ -2420,7 +2609,8 @@
+ 	if (result) {
+ 		QDIO_PRINT_WARN("could not set delay target on irq x%x, " \
+ 				"cc=%i. Continuing.\n",irq_ptr->irq,result);
+-		return -EIO;
++		result=-EIO;
++		goto out;
+ 	}
+ 
+ 	resp_code=chsc_area->response_block.response_code;
+@@ -2435,7 +2625,10 @@
+ 	}
+ 	QDIO_DBF_TEXT2(0,trace,"delytrgt");
+ 	QDIO_DBF_HEX2(0,trace,&delay_target,sizeof(unsigned long));
+-	return 0;
++	result=0;
++out:
++	spin_unlock_irqrestore(&chsc_area_lock,flags);
++	return result;
+ }
+ 
+ int qdio_cleanup(int irq,int how)
+@@ -2445,7 +2638,7 @@
+ 	int do_an_irqrestore=0;
+ 	unsigned long flags;
+ 	int timeout;
+-	char dbf_text[15]="12345678";
++	char dbf_text[15];
+ 
+ 	result=0;
+ 	sprintf(dbf_text,"qcln%4x",irq);
+@@ -2455,7 +2648,7 @@
+ 	irq_ptr=qdio_get_irq_ptr(irq);
+ 	if (!irq_ptr) return -ENODEV;
+ 
+-	spin_lock(&irq_ptr->setting_up_lock);
++	down(&irq_ptr->setting_up_lock);
+ 
+ 	/* mark all qs as uninteresting */
+ 	for (i=0;i<irq_ptr->no_input_qs;i++) {
+@@ -2527,7 +2720,7 @@
+ 	if (do_an_irqrestore)
+ 		s390irq_spin_unlock_irqrestore(irq,flags);
+ 
+-	spin_unlock(&irq_ptr->setting_up_lock);
++	up(&irq_ptr->setting_up_lock);
+ 
+ 	qdio_remove_irq_ptr(irq_ptr);
+ 	qdio_release_irq_memory(irq_ptr);
+@@ -2574,16 +2767,15 @@
+ 	int result,result2;
+ 	int found;
+ 	unsigned long flags;
+-	char dbf_text[20]; /* if a printf would print out more than 8 chars */
++	char dbf_text[20]; /* if a printf printed out more than 8 chars */
+ 
+-	down_interruptible(&init_sema);
++	down(&init_sema);
+ 
+ 	sprintf(dbf_text,"qini%4x",init_data->irq);
+ 	QDIO_DBF_TEXT0(0,setup,dbf_text);
+ 	QDIO_DBF_TEXT0(0,trace,dbf_text);
+ 	sprintf(dbf_text,"qfmt:%x",init_data->q_format);
+ 	QDIO_DBF_TEXT0(0,setup,dbf_text);
+-	QDIO_DBF_TEXT0(0,setup,init_data->adapter_name);
+ 	QDIO_DBF_HEX0(0,setup,init_data->adapter_name,8);
+ 	sprintf(dbf_text,"qpff%4x",init_data->qib_param_field_format);
+ 	QDIO_DBF_TEXT0(0,setup,dbf_text);
+@@ -2672,7 +2864,6 @@
+ 
+ 	irq_ptr->qdr=kmalloc(sizeof(qdr_t),GFP_DMA);
+   	if (!(irq_ptr->qdr)) {
+-   		kfree(irq_ptr->qdr);
+    		kfree(irq_ptr);
+     		QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n");
+      		result=-ENOMEM;
+@@ -2736,12 +2927,12 @@
+ 
+ 	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE);
+ 
+-	irq_ptr->setting_up_lock=SPIN_LOCK_UNLOCKED;
++	sema_init(&irq_ptr->setting_up_lock,1);
+ 
+ 	MOD_INC_USE_COUNT;
+ 	QDIO_DBF_TEXT3(0,setup,"MOD_INC_");
+ 
+-	spin_lock(&irq_ptr->setting_up_lock);
++	down(&irq_ptr->setting_up_lock);
+ 
+ 	qdio_insert_irq_ptr(irq_ptr);
+ 
+@@ -2867,10 +3058,10 @@
+ 		}
+ 		result=tiqdio_set_subchannel_ind(irq_ptr,0);
+ 		if (result) {
+-			spin_unlock(&irq_ptr->setting_up_lock);
++			up(&irq_ptr->setting_up_lock);
+ 			qdio_cleanup(irq_ptr->irq,
+ 				     QDIO_FLAG_CLEANUP_USING_CLEAR);
+-			goto out2;
++			goto out;
+ 		}
+ 		tiqdio_set_delay_target(irq_ptr,TIQDIO_DELAY_TARGET);
+ 	}
+@@ -2906,9 +3097,9 @@
+ 	s390irq_spin_unlock_irqrestore(irq_ptr->irq,saveflags);
+ 
+ 	if (result) {
+-		spin_unlock(&irq_ptr->setting_up_lock);
++		up(&irq_ptr->setting_up_lock);
+ 		qdio_cleanup(irq_ptr->irq,QDIO_FLAG_CLEANUP_USING_CLEAR);
+-		goto out2;
++		goto out;
+ 	}
+ 
+ 	result=qdio_sleepon(&irq_ptr->interrupt_has_arrived,
+@@ -2918,9 +3109,9 @@
+ 		QDIO_PRINT_ERR("establish queues on irq %04x: timed out\n",
+ 			   irq_ptr->irq);
+ 		QDIO_DBF_TEXT2(1,setup,"eq:timeo");
+-		spin_unlock(&irq_ptr->setting_up_lock);
++		up(&irq_ptr->setting_up_lock);
+ 		qdio_cleanup(irq_ptr->irq,QDIO_FLAG_CLEANUP_USING_CLEAR);
+-		goto out2;
++		goto out;
+ 	}
+ 
+ 	if (!(irq_ptr->io_result_dstat & DEV_STAT_DEV_END)) {
+@@ -2937,10 +3128,10 @@
+ 			       irq_ptr->irq,irq_ptr->io_result_dstat,
+ 			       irq_ptr->io_result_cstat,
+ 			       irq_ptr->io_result_flags);
+-		spin_unlock(&irq_ptr->setting_up_lock);
++		up(&irq_ptr->setting_up_lock);
+ 		qdio_cleanup(irq_ptr->irq,QDIO_FLAG_CLEANUP_USING_CLEAR);
+ 		result=-EIO;
+-		goto out2;
++		goto out;
+ 	}
+ 
+ 	if (irq_ptr->io_result_dstat & ~(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) {
+@@ -2958,18 +3149,19 @@
+ 			       irq_ptr->io_result_cstat,
+ 			       irq_ptr->io_result_flags);
+ 		result=-EIO;
++		up(&irq_ptr->setting_up_lock);
+ 		goto out;
+ 	}
+ 
+-	if (MACHINE_IS_VM)
+ 		irq_ptr->qdioac=qdio_check_siga_needs(irq_ptr->irq);
+-	else { 
+-                irq_ptr->qdioac=CHSC_FLAG_SIGA_INPUT_NECESSARY
+-                        | CHSC_FLAG_SIGA_OUTPUT_NECESSARY;
 -        }
--            
--	/* 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);
+ 	sprintf(dbf_text,"qdioac%2x",irq_ptr->qdioac);
+ 	QDIO_DBF_TEXT2(0,setup,dbf_text);
+ 
++	/* if this gets set once, we're running under VM and can omit SVSes */
++	if (irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY) {
++		omit_svs=1;
++	}
++
+ 	sprintf(dbf_text,"qib ac%2x",irq_ptr->qib.ac);
+ 	QDIO_DBF_TEXT2(0,setup,dbf_text);
+ 
+@@ -3026,11 +3218,10 @@
+ 
+ 	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED);
+ 
+- out:
+ 	if (irq_ptr) {
+-		spin_unlock(&irq_ptr->setting_up_lock);
++		up(&irq_ptr->setting_up_lock);
+ 	}
+- out2:
++ out:
+ 	up(&init_sema);
+ 
+ 	return result;
+@@ -3046,7 +3237,7 @@
+ 	irq_ptr=qdio_get_irq_ptr(irq);
+ 	if (!irq_ptr) return -ENODEV;
+ 
+-	spin_lock(&irq_ptr->setting_up_lock);
++	down(&irq_ptr->setting_up_lock);
+ 	if (irq_ptr->state==QDIO_IRQ_STATE_INACTIVE) {
+ 		result=-EBUSY;
+ 		goto out;
+@@ -3141,14 +3332,15 @@
+ 	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ACTIVE);
+ 
+  out:
+-	spin_unlock(&irq_ptr->setting_up_lock);
++	up(&irq_ptr->setting_up_lock);
+ 
+ 	return result;
+ }
+ 
+ /* buffers filled forwards again to make Rick happy */
+-static void qdio_do_qdio_fill_input(qdio_q_t *q,unsigned int qidx,
+-				    unsigned int count,qdio_buffer_t *buffers)
++static inline void qdio_do_qdio_fill_input(qdio_q_t *q,unsigned int qidx,
++	       				   unsigned int count,
++					   qdio_buffer_t *buffers)
+ {
+ 	for (;;) {
+ 		if (!q->is_0copy_sbals_q) {
+@@ -3281,7 +3473,7 @@
+ 					qdio_kick_outbound_q(q);
+ 				}
+ 
+-				qdio_outbound_processing(q);
++				__qdio_outbound_processing(q);
+ 			} else {
+ 				/* under VM, we do a SIGA sync
+ 				 * unconditionally */
+@@ -3309,7 +3501,7 @@
+ 				 * too long, the upper layer
+ 				 * module could do a lot of
+ 				 * traffic in that time */
+-				qdio_outbound_processing(q);
++				__qdio_outbound_processing(q);
+ 			}
+ 		}
+ 
+@@ -3349,7 +3541,7 @@
+ 		 perf_stats.siga_ins);
+ 	_OUTP_IT("Number of SIGA out's issued                     : %u\n",
+ 		 perf_stats.siga_outs);
+-	_OUTP_IT("Number of PCI's caught                          : %u\n",
++	_OUTP_IT("Number of PCIs caught                           : %u\n",
+ 		 perf_stats.pcis);
+ 	_OUTP_IT("Number of adapter interrupts caught             : %u\n",
+ 		 perf_stats.thinints);
+@@ -3576,10 +3768,12 @@
+ 
+ 	qdio_add_procfs_entry();
+ 
+-	hydra_thinints=qdio_check_for_hydra_thinints();
++	qdio_check_for_machine_features();
+ 
+ 	sprintf(dbf_text,"hydrati%1x",hydra_thinints);
+ 	QDIO_DBF_TEXT0(0,setup,dbf_text);
++	sprintf(dbf_text,"omitsvs%1x",omit_svs);
++	QDIO_DBF_TEXT0(0,setup,dbf_text);
+ 
+ 	tiqdio_register_thinints();
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/s390io.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/s390io.c	2006-01-30 22:25:25.000000000 -0700
+@@ -1,7 +1,7 @@
+ /*
+  *  drivers/s390/s390io.c
+  *   S/390 common I/O routines
+- *   $Revision: 1.258 $
++ *   $Revision: 1.247.4.4 $
+  *
+  *  S390 version
+  *    Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH,
+@@ -3043,7 +3043,16 @@
+ 		if (!ioinfo[irq]->ui.flags.ready)
+ 			return (ending_status);
+ 
+-		memcpy (udp, &(ioinfo[irq]->devstat), sdevstat);
++		/*
++		 * Special case: We got a deferred cc 3 on a basic sense.
++		 * We have to notify the device driver of the former unit
++		 * check, but must not confuse it by calling it with the status
++		 * for the failed basic sense.
++		 */
++		if (ioinfo[irq]->ui.flags.w4sense)
++			ioinfo[irq]->ui.flags.w4sense = 0;
++		else
++			memcpy (udp, &(ioinfo[irq]->devstat), sdevstat);
+ 
+ 		ioinfo[irq]->devstat.intparm = 0;
+ 
+@@ -8328,15 +8337,14 @@
+ {
+ 	loff_t len;
+ 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+-	loff_t pos = *offset;
+ 
+-	if (pos < 0 || pos >= p_info->len) {
++	if (*offset >= p_info->len) {
+ 		return 0;
+ 	} 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;
+ 	}
+ }
+@@ -8411,15 +8419,14 @@
+ {
+ 	loff_t len;
+ 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+-	loff_t pos = *offset;
+ 
+-	if (pos < 0 || pos >= p_info->len) {
++	if (*offset >= p_info->len) {
+ 		return 0;
+ 	} 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;
+ 	}
+ }
+@@ -8876,15 +8883,14 @@
+ {
+ 	loff_t len;
+ 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+-	loff_t pos = *offset;
+ 
+-	if (pos < 0 || pos >= p_info->len) {
++	if (*offset >= p_info->len) {
+ 		return 0;
+ 	} else {
+ 		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;
+ 	}
+ }
+@@ -8997,15 +9003,14 @@
+ {
+ 	loff_t len;
+ 	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+-	loff_t pos = *offset;
+ 
+-	if (pos < 0 || pos >= p_info->len) {
++	if (*offset >= p_info->len) {
+ 		return 0;
+ 	} else {
+ 		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;
+ 	}
+ }
+@@ -9127,15 +9132,14 @@
+ {
+      loff_t len;
+      tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+-     loff_t pos = *offset;
+      
+-     if (pos < 0 || pos >= p_info->len) {
++     if ( *offset>=p_info->len) {
+ 	  return 0;
+      } 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;
+      }
+ }
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/Makefile kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/scsi/Makefile	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,25 @@
++#
++# Makefile for the S/390 specific device drivers
++#
++
++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-2.4.27.orig/drivers/s390/scsi/zfcp.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/scsi/zfcp.h	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,1298 @@
++/* 
++ * $Id$
++ *
++ * FCP adapter driver for IBM eServer zSeries
++ *
++ * (C) Copyright IBM Corp. 2002, 2003
++ *
++ * 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 of the License, or
++ * (at your option) any later version. See the file COPYING for more
++ * information.
++ *
++ * Authors:
++ *      Martin Peschke <mpeschke at de.ibm.com>
++ *      Raimund Schroeder <raimund.schroeder at de.ibm.com>
++ *      Aron Zeh
++ *      Wolfgang Taphorn
++ *      Stefan Bader <stefan.bader at de.ibm.com>
++ *      Andreas Herrmann <aherrman at de.ibm.com>
++ *      Stefan Voelkel <Stefan.Voelkel at millenux.com>
++ */
++
++#ifndef _ZFCP_H_
++#define _ZFCP_H_
++
++#define ZFCP_LOW_MEM_CREDITS
++#define ZFCP_STAT_REQSIZES
++#define ZFCP_STAT_QUEUES
++
++#define ZFCP_PARSE_ERR_BUF_SIZE 100
++
++#include <linux/config.h>
++#include <linux/notifier.h>
++#include <linux/types.h>
++#include <linux/list.h>
++#include <linux/completion.h>
++
++#include <asm/types.h>
++#include <asm/irq.h>
++#include <asm/s390io.h>
++#include <asm/s390dyn.h>	/* devreg_t */
++#include <asm/debug.h>		/* debug_info_t */
++#include <asm/qdio.h>		/* qdio_buffer_t */
++#ifdef CONFIG_S390_SUPPORT
++#include <asm/ioctl32.h>
++#endif
++
++#include <linux/blk.h>
++#include <../drivers/scsi/scsi.h>
++#include <../drivers/scsi/hosts.h>
++
++#include "zfcp_fsf.h"
++
++/* 32 bit for SCSI ID and LUN as long as the SCSI stack uses this type */
++typedef u32	scsi_id_t;
++typedef u32	scsi_lun_t;
++
++typedef u16	devno_t;
++typedef u16	irq_t;
++
++typedef u64	wwn_t;
++typedef u32	fc_id_t;
++typedef u64	fcp_lun_t;
++
++
++struct _zfcp_adapter;
++struct _zfcp_fsf_req;
++
++/*
++ * very simple implementation of an emergency pool:
++ * a pool consists of a fixed number of equal elements,
++ * for each purpose a different pool should be created
++ */
++typedef struct _zfcp_mem_pool_element {
++	atomic_t use;
++	void *buffer;
++} zfcp_mem_pool_element_t;
++
++typedef struct _zfcp_mem_pool {
++	int entries;
++	int size;
++	zfcp_mem_pool_element_t *element;
++        struct timer_list timer;
++} zfcp_mem_pool_t;
++
++typedef struct _zfcp_adapter_mem_pool {
++        zfcp_mem_pool_t fsf_req_status_read;
++	zfcp_mem_pool_t data_status_read;
++        zfcp_mem_pool_t data_gid_pn;
++        zfcp_mem_pool_t fsf_req_erp;
++        zfcp_mem_pool_t fsf_req_scsi;
++} zfcp_adapter_mem_pool_t;
++
++typedef void zfcp_fsf_req_handler_t(struct _zfcp_fsf_req*);
++
++typedef struct {
++} zfcp_exchange_config_data_t;
++
++typedef struct {
++	struct _zfcp_port *port;
++} zfcp_open_port_t;
++
++typedef struct {
++	struct _zfcp_port *port;
++} zfcp_close_port_t;
++
++typedef struct {
++	struct _zfcp_unit *unit;
++} zfcp_open_unit_t;
++
++typedef struct {
++	struct _zfcp_unit *unit;
++} zfcp_close_unit_t;
++
++typedef struct {
++	struct _zfcp_port *port;
++} zfcp_close_physical_port_t;
++
++typedef struct {
++	struct _zfcp_unit *unit;
++	Scsi_Cmnd *scsi_cmnd;
++	unsigned long start_jiffies;
++} zfcp_send_fcp_command_task_t;
++
++
++typedef struct {
++	struct _zfcp_unit *unit;
++} zfcp_send_fcp_command_task_management_t;
++
++typedef struct {
++	struct _zfcp_fsf_req_t *fsf_req;
++	struct _zfcp_unit *unit;
++} zfcp_abort_fcp_command_t;
++
++/*
++ * FC-GS-2 stuff
++ */
++#define ZFCP_CT_REVISION		0x01
++#define ZFCP_CT_DIRECTORY_SERVICE	0xFC
++#define ZFCP_CT_NAME_SERVER		0x02
++#define ZFCP_CT_SYNCHRONOUS		0x00
++#define ZFCP_CT_GID_PN			0x0121
++#define ZFCP_CT_GA_NXT			0x0100
++#define ZFCP_CT_MAX_SIZE		0x1020
++#define ZFCP_CT_ACCEPT			0x8002
++#define ZFCP_CT_REJECT			0x8001
++
++/*
++ * FC-FS stuff
++ */
++#define R_A_TOV				10 /* seconds */
++#define ZFCP_ELS_TIMEOUT		(2 * R_A_TOV)
++
++#define ZFCP_LS_RJT			0x01
++#define ZFCP_LS_ACC			0x02
++#define ZFCP_LS_RTV			0x0E
++#define ZFCP_LS_RLS			0x0F
++#define ZFCP_LS_PDISC			0x50
++#define ZFCP_LS_ADISC			0x52
++#define ZFCP_LS_RSCN			0x61
++#define ZFCP_LS_RNID			0x78
++#define ZFCP_LS_RLIR			0x7A
++#define ZFCP_LS_RTV_E_D_TOV_FLAG	0x04000000
++
++/* LS_ACC Reason Codes */
++#define ZFCP_LS_RJT_INVALID_COMMAND_CODE	0x01
++#define ZFCP_LS_RJT_LOGICAL_ERROR		0x03
++#define ZFCP_LS_RJT_LOGICAL_BUSY		0x05
++#define ZFCP_LS_RJT_PROTOCOL_ERROR		0x07
++#define ZFCP_LS_RJT_UNABLE_TO_PERFORM		0x09
++#define ZFCP_LS_RJT_COMMAND_NOT_SUPPORTED	0x0B
++#define ZFCP_LS_RJT_VENDOR_UNIQUE_ERROR		0xFF
++
++struct zfcp_ls_rjt_par {
++	u8 action;
++ 	u8 reason_code;
++ 	u8 reason_expl;
++ 	u8 vendor_unique;
++} __attribute__ ((packed));
++
++struct zfcp_ls_rtv {
++	u8		code;
++	u8		field[3];
++} __attribute__ ((packed));
++
++struct zfcp_ls_rtv_acc {
++	u8		code;
++	u8		field[3];
++	u32		r_a_tov;
++	u32		e_d_tov;
++	u32		qualifier;
++} __attribute__ ((packed));
++
++struct zfcp_ls_rls {
++	u8		code;
++	u8		field[3];
++	fc_id_t		port_id;
++} __attribute__ ((packed));
++
++struct zfcp_ls_rls_acc {
++	u8		code;
++	u8		field[3];
++	u32		link_failure_count;
++	u32		loss_of_sync_count;
++	u32		loss_of_signal_count;
++	u32		prim_seq_prot_error;
++	u32		invalid_transmition_word;
++	u32		invalid_crc_count;
++} __attribute__ ((packed));
++
++struct zfcp_ls_pdisc {
++	u8		code;
++	u8		field[3];
++	u8		common_svc_parm[16];
++	wwn_t		wwpn;
++	wwn_t		wwnn;
++	struct {
++		u8	class1[16];
++		u8	class2[16];
++		u8	class3[16];
++	} svc_parm;
++	u8		reserved[16];
++	u8		vendor_version[16];
++} __attribute__ ((packed));
++
++struct zfcp_ls_pdisc_acc {
++	u8		code;
++	u8		field[3];
++	u8		common_svc_parm[16];
++	wwn_t		wwpn;
++	wwn_t		wwnn;
++	struct {
++		u8	class1[16];
++		u8	class2[16];
++		u8	class3[16];
++	} svc_parm;
++	u8		reserved[16];
++	u8		vendor_version[16];
++} __attribute__ ((packed));
++
++struct zfcp_ls_adisc {
++	u8		code;
++	u8		field[3];
++	fc_id_t		hard_nport_id;
++	wwn_t		wwpn;
++	wwn_t		wwnn;
++	fc_id_t		nport_id;
++} __attribute__ ((packed));
++
++struct zfcp_ls_adisc_acc {
++	u8		code;
++	u8		field[3];
++	fc_id_t		hard_nport_id;
++	wwn_t		wwpn;
++	wwn_t		wwnn;
++	fc_id_t		nport_id;
++} __attribute__ ((packed));
++
++struct zfcp_ls_rnid {
++	u8		code;
++	u8		field[3];
++	u8		node_id_format;
++	u8		reserved[3];
++} __attribute__((packed));
++
++/* common identification data */
++struct zfcp_ls_rnid_common_id {
++	u64		n_port_name;
++	u64		node_name;
++} __attribute__((packed));
++
++/* general topology specific identification data */
++struct zfcp_ls_rnid_general_topology_id {
++	u8		vendor_unique[16];
++	u32		associated_type;
++	u32		physical_port_number;
++	u32		nr_attached_nodes;
++	u8		node_management;
++	u8		ip_version;
++	u16		port_number;
++	u8		ip_address[16];
++	u8		reserved[2];
++	u16		vendor_specific;
++} __attribute__((packed));
++
++struct zfcp_ls_rnid_acc {
++	u8		code;
++	u8		field[3];
++	u8		node_id_format;
++	u8		common_id_length;
++	u8		reserved;
++	u8		specific_id_length;
++	struct zfcp_ls_rnid_common_id
++			common_id;
++	struct zfcp_ls_rnid_general_topology_id
++			specific_id;
++} __attribute__((packed));
++
++struct zfcp_rc_entry {
++	u8 code;
++	const char *description;
++};
++
++
++/*
++ * FC-GS-4 stuff
++ */
++#define ZFCP_CT_TIMEOUT			(3 * R_A_TOV)
++
++
++/*
++ * header for CT_IU
++ */
++struct ct_hdr {
++	u8 revision;		// 0x01
++	u8 in_id[3];		// 0x00
++	u8 gs_type;		// 0xFC	Directory Service
++	u8 gs_subtype;		// 0x02	Name Server
++	u8 options;		// 0x00 single bidirectional exchange
++	u8 reserved0;
++	u16 cmd_rsp_code;	// 0x0121 GID_PN, or 0x0100 GA_NXT
++	u16 max_res_size;	// <= (4096 - 16) / 4
++	u8 reserved1;
++	u8 reason_code;
++	u8 reason_code_expl;
++	u8 vendor_unique;
++} __attribute__ ((packed));
++
++/*
++ * nameserver request CT_IU -- for requests where
++ * a port identifier or a port name is required
++ */
++struct ct_iu_ns_req {
++	struct ct_hdr header;
++	union {
++		wwn_t wwpn;	/* e.g .for GID_PN */
++		fc_id_t d_id;	/* e.g. for GA_NXT */
++	} data;
++} __attribute__ ((packed));
++
++/* FS_ACC IU and data unit for GID_PN nameserver request */
++struct ct_iu_gid_pn {
++	struct ct_hdr header;
++	fc_id_t d_id;
++} __attribute__ ((packed));
++
++/* data unit for GA_NXT nameserver request */
++struct ns_ga_nxt {
++        u8 port_type;
++        u8 port_id[3];
++        u64 port_wwn;
++        u8 port_symbolic_name_length;
++        u8 port_symbolic_name[255];
++        u64 node_wwn;
++        u8 node_symbolic_name_length;
++        u8 node_symbolic_name[255];
++        u64 initial_process_associator;
++        u8 node_ip[16];
++        u32 cos;
++        u8 fc4_types[32];
++        u8 port_ip[16];
++        u64 fabric_wwn;
++        u8 reserved;
++        u8 hard_address[3];
++} __attribute__ ((packed));
++
++/* FS_ACC IU and data unit for GA_NXT nameserver request */
++struct ct_iu_ga_nxt {
++	struct ct_hdr header;
++	struct ns_ga_nxt du;
++} __attribute__ ((packed));
++
++
++typedef void (*zfcp_send_ct_handler_t)(unsigned long);
++
++/* used to pass parameters to zfcp_send_ct() */
++struct zfcp_send_ct {
++	struct _zfcp_port *port;
++	struct scatterlist *req;
++	struct scatterlist *resp;
++	unsigned int req_count;
++	unsigned int resp_count;
++	zfcp_send_ct_handler_t handler;
++	unsigned long handler_data;
++	struct _zfcp_mem_pool *pool;
++	int timeout;
++	struct timer_list *timer;
++	struct completion *completion;
++	int status;
++};
++
++/* used for name server requests in error recovery */
++struct zfcp_gid_pn_data {
++	struct zfcp_send_ct ct;
++	struct scatterlist req;
++	struct scatterlist resp;
++	struct ct_iu_ns_req ct_iu_req;
++	struct ct_iu_gid_pn ct_iu_resp;
++};
++
++typedef void (*zfcp_send_els_handler_t)(unsigned long);
++
++/* used to pass parameters to zfcp_send_els() */
++struct zfcp_send_els {
++	struct _zfcp_port *port;
++	struct scatterlist *req;
++	struct scatterlist *resp;
++	unsigned int req_count;
++	unsigned int resp_count;
++	zfcp_send_els_handler_t handler;
++	unsigned long handler_data;
++	struct completion *completion;
++	int ls_code;
++	int status;
++};
++
++typedef struct {
++	fsf_status_read_buffer_t *buffer;
++} zfcp_status_read_t;
++
++/* request specific data */
++typedef union _zfcp_req_data {
++	zfcp_exchange_config_data_t	exchange_config_data;
++	zfcp_open_port_t		open_port;
++	zfcp_close_port_t		close_port;
++	zfcp_open_unit_t		open_unit;
++	zfcp_close_unit_t		close_unit;
++	zfcp_close_physical_port_t	close_physical_port;
++	zfcp_send_fcp_command_task_t	send_fcp_command_task;
++	zfcp_send_fcp_command_task_management_t
++		send_fcp_command_task_management;
++	zfcp_abort_fcp_command_t	abort_fcp_command;
++	struct zfcp_send_ct		*send_ct;
++	struct zfcp_send_els		*send_els;
++	zfcp_status_read_t		status_read;
++	fsf_qtcb_bottom_port_t		*port_data;
++} zfcp_req_data_t;
++
++/* FSF request */
++typedef struct _zfcp_fsf_req {
++	/* driver wide common magic */
++	u32				common_magic;
++	/* data structure specific magic */
++	u32				specific_magic;
++	/* list of FSF requests */
++	struct list_head		list;
++	/* adapter this request belongs to */
++	struct _zfcp_adapter		*adapter;
++	/* number of SBALs that can be used */
++	u8				sbal_number;
++	/* first SBAL for this request */
++	u8				sbal_first;
++	/* last possible SBAL for this request */
++	u8				sbal_last;
++	/* current SBAL during creation of request */
++	u8				sbal_curr;
++	/* current SBALE during creation of request */
++	u8				sbale_curr;
++
++	/* can be used by routine to wait for request completion */
++	wait_queue_head_t completion_wq;
++	/* status of this request */
++	volatile u32			status;
++	/* copy of FSF Command (avoid to touch SBAL when it is QDIO owned) */
++	u32				fsf_command;
++	/* address of QTCB*/
++	fsf_qtcb_t			*qtcb;
++	/* Sequence number used with this request */
++	u32				seq_no;
++	/* Information fields corresponding to the various types of request */ 
++	zfcp_req_data_t			data;
++	/* used if this request is issued on behalf of erp */
++	struct _zfcp_erp_action		*erp_action;
++	/* used if this request is alloacted from emergency pool */
++	struct _zfcp_mem_pool		*pool;
++} zfcp_fsf_req_t;
++
++typedef struct _zfcp_erp_action {
++	struct list_head list;
++	/* requested action */
++	int action;
++	/* thing which should be recovered */
++	struct _zfcp_adapter *adapter;
++	struct _zfcp_port *port;
++	struct _zfcp_unit *unit;
++	/* status of recovery */
++	volatile u32 status;
++	/* step which is currently taken */
++	u32 step;
++	/* fsf_req which is currently pending for this action */
++	struct _zfcp_fsf_req *fsf_req;
++	struct timer_list timer;
++	/* retry counter, ... ? */
++	union {
++		/* used for nameserver requests (GID_PN) in error recovery */
++		struct zfcp_gid_pn_data *gid_pn;
++	} data;
++} zfcp_erp_action_t;
++
++/* logical unit */
++typedef struct _zfcp_unit {
++	/* driver wide common magic */
++	u32				common_magic;
++	/* data structure specific magic */
++	u32				specific_magic;
++	/* list of logical units */
++	struct list_head		list;
++	/* remote port this logical unit belongs to */
++	struct _zfcp_port		*port;
++	/* status of this logical unit */
++	atomic_t			status;
++	/* own SCSI LUN */
++	scsi_lun_t			scsi_lun;
++	/* own FCP_LUN */
++	fcp_lun_t			fcp_lun;
++	/* handle assigned by FSF */
++	u32				handle;
++	/* save scsi device struct pointer locally */
++	Scsi_Device                     *device;
++	/* used for proc_fs support */
++	char                            *proc_buffer;
++	struct proc_dir_entry		*proc_file;
++	struct proc_dir_entry		*proc_dir;
++	/* error recovery action pending for this unit (if any) */
++	struct _zfcp_erp_action		erp_action;
++	atomic_t                        erp_counter;
++	/* list of units in order of configuration via mapping */
++	struct list_head		map_list;
++} zfcp_unit_t;
++
++/* remote port */
++typedef struct _zfcp_port {
++	/* driver wide common magic */
++	u32				common_magic;
++	/* data structure specific magic */
++	u32				specific_magic;
++	/* list of remote ports */
++	struct list_head		list;
++	/* adapter this remote port accessed */
++	struct _zfcp_adapter		*adapter;
++	/* head of logical unit list */
++	struct list_head		unit_list_head;
++	/* lock for critical operations on list of logical units */
++	rwlock_t			unit_list_lock;
++	/* number of logical units in list */
++	u32				units;
++	/* status of this remote port */
++	atomic_t			status;
++	/* own SCSI ID */
++	scsi_id_t			scsi_id;
++	/* WWNN of node this remote port belongs to (if known) */
++	wwn_t				wwnn;
++	/* own WWPN */
++	wwn_t				wwpn;
++	/* D_ID */
++	fc_id_t				d_id;
++	/* largest SCSI LUN of units attached to this port */
++	scsi_lun_t			max_scsi_lun;
++	/* handle assigned by FSF */
++	u32				handle;
++	/* used for proc_fs support */
++        char                            *proc_buffer;
++	struct proc_dir_entry		*proc_file;
++	struct proc_dir_entry		*proc_dir;
++	/* error recovery action pending for this port (if any) */
++	struct _zfcp_erp_action		erp_action;
++        atomic_t                        erp_counter;
++} zfcp_port_t;
++
++
++/* QDIO request/response queue */
++typedef struct _zfcp_qdio_queue {
++	/* SBALs */
++	qdio_buffer_t			*buffer[QDIO_MAX_BUFFERS_PER_Q];
++	/* index of next free buffer in queue (only valid if free_count>0) */
++	u8				free_index;
++	/* number of free buffers in queue */
++	atomic_t			free_count;
++	/* lock for critical operations on queue */
++	rwlock_t			queue_lock;
++        /* outbound queue only, SBALs since PCI indication */
++        int                             distance_from_int;
++} zfcp_qdio_queue_t;
++
++
++/* Control file data channel sense data record */
++typedef struct _zfcp_cfdc_sense_data {
++	/* Request signature */
++	u32				signature;
++	/* FCP adapter device number */
++	u32				devno;
++	/* Command code */
++	u32				command;
++	/* FSF request status */
++	u32				fsf_status;
++	/* FSF request status qualifier */
++	u32				fsf_status_qual[4];
++	/* Access conflicts list */
++	u8				payloads[256];
++	/* Access control table */
++	u8				control_file[0];
++} zfcp_cfdc_sense_data_t;
++
++#define ZFCP_CFDC_SIGNATURE			0xCFDCACDF
++
++#define ZFCP_CFDC_CMND_DOWNLOAD_NORMAL		0x00010001
++#define ZFCP_CFDC_CMND_DOWNLOAD_FORCE		0x00010101
++#define ZFCP_CFDC_CMND_FULL_ACCESS		0x00000201
++#define ZFCP_CFDC_CMND_RESTRICTED_ACCESS	0x00000401
++#define ZFCP_CFDC_CMND_UPLOAD			0x00010002
++
++#define ZFCP_CFDC_DOWNLOAD			0x00000001
++#define ZFCP_CFDC_UPLOAD			0x00000002
++#define ZFCP_CFDC_WITH_CONTROL_FILE		0x00010000
++
++#define ZFCP_CFDC_MAX_CONTROL_FILE_SIZE		127 * 1024
++
++
++#define ZFCP_PARSE_ERR_BUF_SIZE 100
++
++/* adapter */
++typedef struct _zfcp_adapter {
++/* elements protected by zfcp_data.adapter_list_lock */
++	/* driver wide common magic */
++	u32				common_magic;
++	/* data structure specific magic */
++	u32				specific_magic;
++	struct list_head		list;
++	/* WWNN */
++	wwn_t				wwnn;
++	/* WWPN */
++	wwn_t				wwpn;
++	/* N_Port ID */
++	fc_id_t				s_id;
++	/* irq (subchannel) */
++	irq_t				irq;
++	/* device number */
++	devno_t				devno;
++	/* default FC service class */
++	u8				fc_service_class;
++	/* topology which this adapter is attached to */
++	u32				fc_topology;
++	/* FC interface speed */
++	u32				fc_link_speed;
++	/* Hydra version */
++	u32				hydra_version;
++	/* Licensed Internal Code version of FSF in adapter */
++	u32				fsf_lic_version;
++	/* supported features of FCP channel */
++	u32				supported_features;
++	/* hardware version of FCP channel */
++	u32				hardware_version;
++	/* serial number of hardware */
++	u8				serial_number[32];
++	/* SCSI host structure of the mid layer of the SCSI stack */
++	struct Scsi_Host		*scsi_host;
++	/* Start of packets in flight list */
++	Scsi_Cmnd                       *first_fake_cmnd;
++	/* lock for the above */
++	rwlock_t			fake_list_lock;
++	/* starts processing of faked commands */
++	struct timer_list               fake_scsi_timer;
++	atomic_t			fake_scsi_reqs_active;
++	/* name */
++	unsigned char			name[9];
++	/* elements protected by port_list_lock */
++	/* head of remote port list */
++	struct list_head		port_list_head;
++	/* lock for critical operations on list of remote ports */
++	rwlock_t			port_list_lock;
++	/* number of remote currently configured */
++	u32				ports;
++	/* largest SCSI ID of ports attached to this adapter */
++	scsi_id_t			max_scsi_id;
++	/* largest SCSI LUN of units of ports attached to this adapter */
++	scsi_lun_t			max_scsi_lun;
++	/* elements protected by fsf_req_list_lock */
++	/* head of FSF request list */
++	struct list_head		fsf_req_list_head;
++	/* lock for critical operations on list of FSF requests */
++	rwlock_t			fsf_req_list_lock;
++	/* number of existing FSF requests pending */
++	atomic_t       			fsf_reqs_active;
++	/* elements partially protected by request_queue.lock */
++	/* request queue */
++	struct _zfcp_qdio_queue		request_queue;
++	/* FSF command sequence number */
++	u32				fsf_req_seq_no;
++	/* can be used to wait for avaliable SBALs in request queue */
++	wait_queue_head_t		request_wq;
++	/* elements partially protected by response_queue.lock */
++	/* response queue */
++	struct _zfcp_qdio_queue		response_queue;
++	devstat_t			devstat;
++	s390_dev_info_t                 devinfo;
++	rwlock_t			abort_lock;
++	/* number of status reads failed */
++	u16				status_read_failed;
++	/* elements which various bits are protected by several locks */
++	/* status of this adapter */
++	atomic_t			status;
++	/* for proc_info */
++	char                            *proc_buffer;
++	/* and here for the extra proc_dir */
++	struct proc_dir_entry 		*proc_dir;
++	struct proc_dir_entry		*proc_file;
++	/* nameserver avaliable via this adapter */
++	struct _zfcp_port		*nameserver_port;
++	/* error recovery for this adapter and associated devices */
++	struct list_head		erp_ready_head;
++	struct list_head		erp_running_head;
++	rwlock_t			erp_lock;
++	struct semaphore		erp_ready_sem;
++	wait_queue_head_t		erp_thread_wqh;
++	wait_queue_head_t		erp_done_wqh;
++	/* error recovery action pending for this adapter (if any) */
++	struct _zfcp_erp_action		erp_action;
++	atomic_t                        erp_counter;
++	debug_info_t                    *erp_dbf;
++	debug_info_t			*abort_dbf;
++	debug_info_t                    *req_dbf;
++	debug_info_t			*in_els_dbf;
++	debug_info_t			*cmd_dbf;
++	rwlock_t			cmd_dbf_lock;
++	zfcp_adapter_mem_pool_t		pool;
++	/* SCSI error recovery watch */
++	struct timer_list               scsi_er_timer;
++	/* Used to handle buffer positioning when reopening queues*/
++	atomic_t                        reqs_in_progress;
++#ifdef ZFCP_ERP_DEBUG_SINGLE_STEP
++	struct				semaphore erp_continue_sem;
++#endif /* ZFCP_ERP_DEBUG_SINGLE_STEP */
++#ifdef ZFCP_STAT_REQSIZES
++	struct list_head		read_req_head;
++	struct list_head		write_req_head;
++	rwlock_t			stat_lock;
++	atomic_t			stat_errors;
++	atomic_t			stat_on;
++#endif
++#ifdef ZFCP_STAT_QUEUES
++	atomic_t                        outbound_queue_full;
++	atomic_t			outbound_total;
++#endif
++} zfcp_adapter_t;
++
++/* driver data */
++typedef struct _zfcp_data {
++	/* SCSI stack data structure storing information about this driver */
++	Scsi_Host_Template		scsi_host_template;
++	/* head of adapter list */
++	struct list_head		adapter_list_head;
++	/* lock for critical operations on list of adapters */
++	rwlock_t			adapter_list_lock;
++	/* number of adapters in list */
++	u16				adapters;
++	/* data used for dynamic I/O */
++	devreg_t			devreg;
++	devreg_t			devreg_priv;
++	/* driver version number derived from cvs revision */
++	u32				driver_version;
++	/* serialises proc-fs/configuration changes */
++	struct semaphore                proc_sema;
++	/* for proc_info */
++	char                            *proc_buffer_parm;
++	char                            *proc_buffer_map;
++	char                            *proc_line;
++	unsigned long                   proc_line_length;
++	/* and here for the extra proc_dir */
++	struct proc_dir_entry		*proc_dir;
++	struct proc_dir_entry 		*parm_proc_file;
++	struct proc_dir_entry 		*map_proc_file;
++	struct proc_dir_entry 		*add_map_proc_file;
++	/* buffer for parse error messages (don't want to put it on stack) */
++	unsigned char 			perrbuf[ZFCP_PARSE_ERR_BUF_SIZE];
++	atomic_t                        mem_count;
++	struct notifier_block		reboot_notifier;
++	atomic_t			loglevel;
++#ifdef ZFCP_LOW_MEM_CREDITS
++	atomic_t			lowmem_credit;
++#endif
++	/* no extra lock here, we have the proc_sema */
++	struct list_head		map_list_head;
++} zfcp_data_t;
++
++/* struct used by memory pools for fsf_requests */
++struct zfcp_fsf_req_pool_buffer {
++	struct _zfcp_fsf_req fsf_req;
++	struct fsf_qtcb qtcb;
++};
++
++/* record generated from parsed conf. lines */
++typedef struct _zfcp_config_record {
++	int			valid;
++	unsigned long		devno;
++	unsigned long		scsi_id;
++	unsigned long long	wwpn;
++	unsigned long		scsi_lun;
++	unsigned long long	fcp_lun;
++} zfcp_config_record_t;
++
++/* for use by zfcp_sg_list_...() */
++typedef struct _zfcp_sg_list {
++	struct scatterlist *sg;
++	unsigned int count;
++} zfcp_sg_list_t;
++
++extern zfcp_data_t zfcp_data;
++
++#ifdef ZFCP_LOW_MEM_CREDITS
++/* only substract i from v if v is not equal to no_sub; returns 0 then, 1 otherwise */
++static __inline__ int atomic_test_and_sub(int no_sub, int i, atomic_t *v)
++{
++        int old_val, new_val;
++	do {
++		old_val = atomic_read(v);
++		if (old_val == no_sub)
++			return 1;
++		new_val = old_val - i;
++	} while (atomic_compare_and_swap(old_val, new_val, v));
++        return 0;
++}
++
++/* only decrement v if v is not equal to no_dec; returns 0 then, 1 otherwise */
++static __inline__ int atomic_test_and_dec(int no_dec, atomic_t *v)
++{
++	return atomic_test_and_sub(no_dec, 1, v);
++}
++#endif
++
++#ifndef atomic_test_mask
++#define atomic_test_mask(mask, target) \
++           ((atomic_read(target) & mask) == mask)
++#endif
++
++/*
++ * Macros used for logging etc.
++ */
++
++#define ZFCP_NAME			"zfcp"
++
++/*
++ * Logging may be applied on certain kinds of driver operations
++ * independently. Besides different log levels are supported for
++ * each of these areas.
++ */
++
++/* independent areas being subject of logging */
++#define ZFCP_LOG_AREA_OTHER	0
++#define ZFCP_LOG_AREA_SCSI	1
++#define ZFCP_LOG_AREA_FSF	2
++#define ZFCP_LOG_AREA_CONFIG	3
++#define ZFCP_LOG_AREA_DIO	4
++#define ZFCP_LOG_AREA_QDIO	5
++#define ZFCP_LOG_AREA_ERP	6
++
++/* values for log level - keep it simple for now */
++#define ZFCP_LOG_LEVEL_NORMAL	0
++#define ZFCP_LOG_LEVEL_INFO	1
++#define ZFCP_LOG_LEVEL_DEBUG	2
++#define ZFCP_LOG_LEVEL_TRACE	3
++
++/* default log levels for different log areas */
++#define ZFCP_LOG_LEVEL_DEFAULT_OTHER	ZFCP_LOG_LEVEL_NORMAL
++#define ZFCP_LOG_LEVEL_DEFAULT_SCSI	ZFCP_LOG_LEVEL_NORMAL
++#define ZFCP_LOG_LEVEL_DEFAULT_FSF	ZFCP_LOG_LEVEL_NORMAL
++#define ZFCP_LOG_LEVEL_DEFAULT_CONFIG	ZFCP_LOG_LEVEL_NORMAL
++#define ZFCP_LOG_LEVEL_DEFAULT_DIO	ZFCP_LOG_LEVEL_NORMAL
++#define ZFCP_LOG_LEVEL_DEFAULT_QDIO	ZFCP_LOG_LEVEL_NORMAL
++#define ZFCP_LOG_LEVEL_DEFAULT_ERP	ZFCP_LOG_LEVEL_NORMAL
++
++/*
++ * this allows to remove excluded logs from the code by the preprocessor
++ * (this is the last log level compiled in, higher log levels are removed)
++ */
++#define ZFCP_LOG_LEVEL_LIMIT	ZFCP_LOG_LEVEL_DEBUG
++
++/* nibbles of "loglevel" are used for particular purposes */
++#define ZFCP_LOG_VALUE(zfcp_lognibble) \
++		((atomic_read(&zfcp_data.loglevel) >> (zfcp_lognibble<<2)) & 0xF)
++
++#define ZFCP_LOG_VALUE_OTHER	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_OTHER)
++#define ZFCP_LOG_VALUE_SCSI	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_SCSI)
++#define ZFCP_LOG_VALUE_FSF	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_FSF)
++#define ZFCP_LOG_VALUE_CONFIG	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_CONFIG)
++#define ZFCP_LOG_VALUE_DIO	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_DIO)
++#define ZFCP_LOG_VALUE_QDIO	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_QDIO)
++#define ZFCP_LOG_VALUE_ERP	ZFCP_LOG_VALUE(ZFCP_LOG_AREA_ERP)
++
++/* all log level defaults put together into log level word */
++#define ZFCP_LOG_LEVEL_DEFAULTS \
++	((ZFCP_LOG_LEVEL_DEFAULT_OTHER	<< (ZFCP_LOG_AREA_OTHER<<2))	| \
++	 (ZFCP_LOG_LEVEL_DEFAULT_SCSI	<< (ZFCP_LOG_AREA_SCSI<<2))	| \
++	 (ZFCP_LOG_LEVEL_DEFAULT_FSF	<< (ZFCP_LOG_AREA_FSF<<2))	| \
++	 (ZFCP_LOG_LEVEL_DEFAULT_CONFIG	<< (ZFCP_LOG_AREA_CONFIG<<2))	| \
++	 (ZFCP_LOG_LEVEL_DEFAULT_DIO	<< (ZFCP_LOG_AREA_DIO<<2))	| \
++	 (ZFCP_LOG_LEVEL_DEFAULT_QDIO	<< (ZFCP_LOG_AREA_QDIO<<2))	| \
++	 (ZFCP_LOG_LEVEL_DEFAULT_ERP	<< (ZFCP_LOG_AREA_ERP<<2)))
++
++/* that's the prefix placed at the beginning of each driver message */
++#define ZFCP_LOG_PREFIX ZFCP_NAME": "
++
++/* log area specific log prefixes */
++#define ZFCP_LOG_AREA_PREFIX_OTHER	""
++#define ZFCP_LOG_AREA_PREFIX_SCSI	"SCSI: "
++#define ZFCP_LOG_AREA_PREFIX_FSF	"FSF: "
++#define ZFCP_LOG_AREA_PREFIX_CONFIG	"config: "
++#define ZFCP_LOG_AREA_PREFIX_DIO	"dynamic I/O: "
++#define ZFCP_LOG_AREA_PREFIX_QDIO	"QDIO: "
++#define ZFCP_LOG_AREA_PREFIX_ERP	"ERP: "
++
++/* check whether we have the right level for logging */
++#define ZFCP_LOG_CHECK(ll)	(ZFCP_LOG_VALUE(ZFCP_LOG_AREA)) >= ll
++
++/* As we have two printks it is possible for them to be seperated by another
++ * message. This holds true even for printks from within this module.
++ * In any case there should only be a small readability hit, however.
++ */
++#define _ZFCP_LOG(m...) \
++		{ \
++			printk( "%s%s: ", \
++				ZFCP_LOG_PREFIX ZFCP_LOG_AREA_PREFIX, \
++				__FUNCTION__); \
++			printk(m); \
++		}
++
++#define ZFCP_LOG(ll, m...) \
++		if (ZFCP_LOG_CHECK(ll)) \
++			_ZFCP_LOG(m)
++	
++#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL
++#define ZFCP_LOG_NORMAL(m...)
++#else	/* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_NORMAL */
++#define ZFCP_LOG_NORMAL(m...)		ZFCP_LOG(ZFCP_LOG_LEVEL_NORMAL, m)
++#endif
++
++#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO
++#define ZFCP_LOG_INFO(m...)
++#else	/* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_INFO */
++#define ZFCP_LOG_INFO(m...)		ZFCP_LOG(ZFCP_LOG_LEVEL_INFO, m)
++#endif
++
++#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG
++#define ZFCP_LOG_DEBUG(m...)
++#else	/* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_DEBUG */
++#define ZFCP_LOG_DEBUG(m...)		ZFCP_LOG(ZFCP_LOG_LEVEL_DEBUG, m)
++#endif
++
++#if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_TRACE
++#define ZFCP_LOG_TRACE(m...)
++#else	/* ZFCP_LOG_LEVEL_LIMIT >= ZFCP_LOG_LEVEL_TRACE */
++#define ZFCP_LOG_TRACE(m...)		ZFCP_LOG(ZFCP_LOG_LEVEL_TRACE, m)
++#endif
++
++/**************** memory management wrappers ************************/
++
++#define ZFCP_KMALLOC(params...)		zfcp_kmalloc(params, __FUNCTION__)
++#define ZFCP_KFREE(params...)		zfcp_kfree(params, __FUNCTION__)
++#define ZFCP_GET_ZEROED_PAGE(params...)	zfcp_get_zeroed_page(params, __FUNCTION__)
++#define ZFCP_FREE_PAGE(params...)	zfcp_free_page(params, __FUNCTION__)
++
++static inline void *zfcp_kmalloc(size_t size, int type, char *origin)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++        void *ret = NULL;
++#if 0
++        if (error_counter>=10000) {
++                if(error_counter==10000) {
++                        printk("********LOW MEMORY********\n");
++                }
++                error_counter=10001;
++                goto out;
++        }
++#endif
++
++#ifdef ZFCP_LOW_MEM_CREDITS
++	if (!atomic_test_and_dec(0, &zfcp_data.lowmem_credit))
++		return NULL;
++#endif
++
++        ret = kmalloc(size, type);
++        if (ret) {
++                atomic_add(size, &zfcp_data.mem_count);
++		memset(ret, 0, size);
++        }
++#ifdef ZFCP_MEMORY_DEBUG
++	/* FIXME(design): shouldn't this rather be a dbf entry? */
++        ZFCP_LOG_NORMAL(
++		"origin: %s, addr=0x%lx, size=%li, type=%d\n",
++		origin,
++		(unsigned long)ret,
++		size,
++		type);
++#endif
++        /* out: */
++        return ret;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++static inline unsigned long zfcp_get_zeroed_page(int flags, char *origin) 
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++        unsigned long ret = 0;
++#if 0
++        if (error_counter>=10000) {
++                if(error_counter==10000) {
++                        printk("********LOW MEMORY********\n");
++                }
++                error_counter=10001;
++                goto out;
++        }
++#endif
++        ret = get_zeroed_page(flags);
++        if (ret) {
++                atomic_add(PAGE_SIZE, &zfcp_data.mem_count);
++        }
++
++#ifdef ZFCP_MEMORY_DEBUG
++	/* FIXME(design): shouldn't this rather be a dbf entry? */
++        ZFCP_LOG_NORMAL(
++		"origin=%s, addr=0x%lx, type=%d\n",
++		origin,
++		ret,
++		flags);
++#endif
++        /* out :*/
++        return ret;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * Note:
++ * 'kfree' may free a different amount of storage than specified here by
++ * 'size' since 'kfree' has its own means to figure this number out.
++ * Thus, an arbitrary value assigned to 'size' (usage error) will
++ * mess up our storage accounting even in cases of no memory leaks.
++ */
++static inline void zfcp_kfree(void *addr, size_t size, char *origin) 
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++        atomic_sub(size, &zfcp_data.mem_count);
++#ifdef ZFCP_MEMORY_DEBUG
++	/* FIXME(design): shouldn't this rather be a dbf entry? */
++        ZFCP_LOG_NORMAL(
++		"origin: %s, addr=0x%lx, count=%ld \n",
++		origin,
++		(unsigned long)addr,
++                size);
++#endif
++        kfree(addr);
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++static inline void zfcp_free_page(unsigned long addr, char *origin) 
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++        atomic_sub(PAGE_SIZE, &zfcp_data.mem_count);
++#ifdef ZFCP_MEMORY_DEBUG
++        ZFCP_LOG_NORMAL("origin: %s, addr=0x%lx\n",
++                        origin,
++                        addr);
++#endif
++        free_page(addr);
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++int	zfcp_config_parse_record_add (zfcp_config_record_t*);
++
++#define ZFCP_FIRST_ENTITY(head,type) \
++	( \
++		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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/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
++
++/* 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;
++
++typedef u64 fsf_wwn_t;
++
++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;
++
++typedef struct fsf_plogi {
++	u32			code;
++	fsf_nport_serv_param_t	serv_param;
++} __attribute__ ((packed)) fsf_plogi_t;
++
++#define FSF_FCP_CMND_SIZE	288
++#define FSF_FCP_RSP_SIZE	128
++
++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;
++
++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_main.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/scsi/zfcp_main.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,21393 @@
++/*
++ * 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
++ *      Stefan Bader <stefan.bader at de.ibm.com>
++ *      Andreas Herrmann <aherrman at de.ibm.com>
++ *      Stefan Voelkel <Stefan.Voelkel at millenux.com>
++ */
++
++/* this drivers version (do not edit !!! generated and updated by cvs) */
++#define ZFCP_REVISION		"$Revision: 1.31.2.25 $"
++
++#define ZFCP_QTCB_VERSION	FSF_QTCB_CURRENT_VERSION
++
++#define ZFCP_PRINT_FLAGS
++
++#undef ZFCP_CAUSE_ERRORS
++
++#undef ZFCP_MEMORY_DEBUG
++
++#undef ZFCP_MEM_POOL_ONLY
++
++#define ZFCP_DEBUG_REQUESTS	/* fsf_req tracing */
++
++#define ZFCP_DEBUG_COMMANDS	/* host_byte tracing */
++
++#define ZFCP_DEBUG_ABORTS	/* scsi_cmnd abort tracing */
++
++#define ZFCP_DEBUG_INCOMING_ELS	/* incoming ELS tracing */
++
++#undef ZFCP_RESID
++
++#define ZFCP_STAT_REQSIZES
++#define ZFCP_STAT_QUEUES
++
++// current implementation does not work due to proc_sema
++#undef ZFCP_ERP_DEBUG_SINGLE_STEP
++
++#ifdef ZFCP_CAUSE_ERRORS
++struct timer_list zfcp_force_error_timer;
++#endif
++
++/* ATTENTION: value must not be used by hardware */
++#define FSF_QTCB_UNSOLICITED_STATUS		0x6305
++
++#define ZFCP_FAKE_SCSI_COMPLETION_TIME	        (HZ / 3)
++
++#define ZFCP_SCSI_LOW_MEM_TIMEOUT               (100*HZ)
++
++#define ZFCP_SCSI_ER_TIMEOUT                    (100*HZ)
++
++#define ZFCP_SCSI_RETRY_TIMEOUT			(120*HZ)
++
++/********************* QDIO SPECIFIC DEFINES *********************************/
++
++/* allow as much chained SBALs as supported by hardware */
++#define ZFCP_MAX_SBALS_PER_REQ		FSF_MAX_SBALS_PER_REQ
++#define ZFCP_MAX_SBALS_PER_CT_REQ	FSF_MAX_SBALS_PER_REQ
++#define ZFCP_MAX_SBALS_PER_ELS_REQ	FSF_MAX_SBALS_PER_ELS_REQ
++/* DMQ bug workaround: don't use last SBALE */
++#define ZFCP_MAX_SBALES_PER_SBAL	(QDIO_MAX_ELEMENTS_PER_BUFFER - 1)
++/* index of last SBALE (with respect to DMQ bug workaround) */
++#define ZFCP_LAST_SBALE_PER_SBAL	(ZFCP_MAX_SBALES_PER_SBAL - 1)
++/* max. number of (data buffer) SBALEs in largest SBAL chain */
++#define ZFCP_MAX_SBALES_PER_REQ		\
++	(ZFCP_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL \
++	 - 2)	/* request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */
++
++/* FIXME(tune): free space should be one max. SBAL chain plus what? */
++#define ZFCP_QDIO_PCI_INTERVAL		(QDIO_MAX_BUFFERS_PER_Q - (ZFCP_MAX_SBALS_PER_REQ + 4))
++
++#define ZFCP_SBAL_TIMEOUT (5 * HZ)
++
++#define ZFCP_STATUS_READ_FAILED_THRESHOLD	3
++
++/* parsing stuff */
++#define ZFCP_PARSE_SPACE_CHARS		" \t"
++#define ZFCP_PARSE_RECORD_DELIM_CHARS	";\n"
++#define ZFCP_PARSE_DELIM_CHARS		":"
++#define ZFCP_PARSE_COMMENT_CHARS	"#"
++#define ZFCP_PARSE_ADD		1
++#define ZFCP_PARSE_DEL		0
++
++#include <linux/config.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/delay.h>
++#include <linux/slab.h>
++#include <linux/version.h>
++#include <linux/interrupt.h>
++#include <linux/init.h>
++#include <linux/proc_fs.h>
++#include <linux/notifier.h>
++#include <linux/reboot.h>
++#include <linux/time.h>
++
++#include <linux/ioctl.h>
++#include <linux/major.h>
++#include <linux/miscdevice.h>
++
++#include "../../fc4/fc.h"
++
++#include <linux/module.h>
++
++#include <asm/semaphore.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++
++#include <asm/ebcdic.h>
++#include <asm/cpcmd.h>               /* Debugging only */
++#include <asm/processor.h>           /* Debugging only */
++#include <asm/div64.h>
++#include <asm/ebcdic.h>
++
++#include "zfcp.h"
++#include "zfcp_zh.h"
++
++#define ZFCP_MAX_SBALES_PER_CONTROL_FILE \
++	(ZFCP_CFDC_MAX_CONTROL_FILE_SIZE + PAGE_SIZE - 1) >> PAGE_SHIFT
++
++/* Cosmetics */
++#define ZFCP_FSFREQ_CLEANUP_TIMEOUT	HZ/10
++
++#define ZFCP_TYPE2_RECOVERY_TIME        8*HZ
++
++#ifdef ZFCP_STAT_REQSIZES
++#define ZFCP_MAX_PROC_SIZE              3 * PAGE_SIZE
++#else
++#define ZFCP_MAX_PROC_SIZE              PAGE_SIZE
++#endif
++
++#define ZFCP_64BIT			(BITS_PER_LONG == 64)
++#define ZFCP_31BIT			(!ZFCP_64BIT)
++
++#define QDIO_SCSI_QFMT			1	/* 1 for FSF */
++
++/* queue polling (values in microseconds) */
++#define ZFCP_MAX_INPUT_THRESHOLD 	5000	/* FIXME: tune */
++#define ZFCP_MAX_OUTPUT_THRESHOLD 	1000	/* FIXME: tune */
++#define ZFCP_MIN_INPUT_THRESHOLD 	1	/* ignored by QDIO layer */
++#define ZFCP_MIN_OUTPUT_THRESHOLD 	1	/* ignored by QDIO layer */
++
++#define ZFCP_PARM_FILE                  "mod_parm"
++#define ZFCP_MAP_FILE                   "map"
++#define ZFCP_ADD_MAP_FILE               "add_map"
++#define ZFCP_STATUS_FILE                "status"
++#define ZFCP_MAX_PROC_LINE              1024
++
++#define ZFCP_CFDC_DEV_NAME		"zfcp_cfdc"
++#define ZFCP_CFDC_DEV_MAJOR		MISC_MAJOR
++#define ZFCP_CFDC_DEV_MINOR		MISC_DYNAMIC_MINOR
++
++#define ZFCP_CFDC_IOC_MAGIC		0xDD
++#define ZFCP_CFDC_IOC \
++	_IOWR(ZFCP_CFDC_IOC_MAGIC, 0, zfcp_cfdc_sense_data_t)
++
++#ifdef CONFIG_S390_SUPPORT
++
++#define ZFCP_IOC_DEFAULT(cmd)		{cmd, (void*)sys_ioctl}
++
++typedef struct _zfcp_ioctl_entry {
++	unsigned int cmd;
++	ioctl_trans_handler_t handler;
++} zfcp_ioctl_entry_t;
++
++static zfcp_ioctl_entry_t zfcp_cfdc_ioc __initdata =
++	ZFCP_IOC_DEFAULT(ZFCP_CFDC_IOC);
++
++#endif
++
++#define ZFCP_RESET_ERP			"reset erp"
++#define ZFCP_SET_OFFLINE		"set offline"
++#define ZFCP_SET_ONLINE			"set online"
++#define ZFCP_RTV			"rtv"
++#define ZFCP_RLS			"rls"
++#define ZFCP_PDISC			"pdisc"
++#define ZFCP_ADISC			"adisc"
++#define ZFCP_STAT_ON			"stat on"
++#define ZFCP_STAT_OFF			"stat off"
++#define ZFCP_STAT_RESET			"stat reset"
++
++#define ZFCP_DID_MASK                   0x00ffffff
++
++/* Adapter Identification Parameters */
++#define ZFCP_CONTROL_UNIT_TYPE	0x1731
++#define ZFCP_CONTROL_UNIT_MODEL	0x03
++#define ZFCP_DEVICE_TYPE	0x1732
++#define ZFCP_DEVICE_MODEL	0x03
++#define ZFCP_DEVICE_MODEL_PRIV	0x04
++ 
++#define ZFCP_FC_SERVICE_CLASS_DEFAULT	FSF_CLASS_3
++
++/* timeout for name-server lookup (in seconds) */
++/* FIXME(tune) */
++#define ZFCP_NS_GID_PN_TIMEOUT		10
++#define ZFCP_NS_GA_NXT_TIMEOUT		120
++
++#define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES	6
++#define ZFCP_EXCHANGE_CONFIG_DATA_SLEEP		50
++
++#define ZFCP_STATUS_READS_RECOM		FSF_STATUS_READS_RECOM
++
++/* largest SCSI command we can process */
++/* FCP-2 (FCP_CMND IU) allows up to (255-3+16) */
++#define ZFCP_MAX_SCSI_CMND_LENGTH	255
++/* maximum number of commands in LUN queue */
++#define ZFCP_CMND_PER_LUN               32
++
++/* debug feature entries per adapter */
++#define ZFCP_ERP_DBF_INDEX	1 
++#define ZFCP_ERP_DBF_AREAS	2
++#define ZFCP_ERP_DBF_LENGTH	16
++#define ZFCP_ERP_DBF_LEVEL	3
++#define ZFCP_ERP_DBF_NAME	"zfcp_erp"
++
++#define ZFCP_REQ_DBF_INDEX	1
++#define ZFCP_REQ_DBF_AREAS	1
++#define ZFCP_REQ_DBF_LENGTH	8
++#define ZFCP_REQ_DBF_LEVEL	DEBUG_OFF_LEVEL
++#define ZFCP_REQ_DBF_NAME	"zfcp_req"
++
++#define ZFCP_CMD_DBF_INDEX	2
++#define ZFCP_CMD_DBF_AREAS	1
++#define ZFCP_CMD_DBF_LENGTH	8
++#define ZFCP_CMD_DBF_LEVEL	3
++#define ZFCP_CMD_DBF_NAME	"zfcp_cmd"
++
++#define ZFCP_ABORT_DBF_INDEX	2
++#define ZFCP_ABORT_DBF_AREAS	1
++#define ZFCP_ABORT_DBF_LENGTH	8
++#define ZFCP_ABORT_DBF_LEVEL	6
++#define ZFCP_ABORT_DBF_NAME	"zfcp_abt"
++
++#define ZFCP_IN_ELS_DBF_INDEX	2
++#define ZFCP_IN_ELS_DBF_AREAS	1
++#define ZFCP_IN_ELS_DBF_LENGTH	8
++#define ZFCP_IN_ELS_DBF_LEVEL	6
++#define ZFCP_IN_ELS_DBF_NAME	"zfcp_els"
++
++/*
++ * paranoia: some extra checks ensuring driver consistency and probably
++ * reducing performance,
++ * should be compiled in and defined per default during development
++ * should be compiled in and disabled per default in beta program
++ * should not be compiled in field
++ */
++
++/* do (not) compile in paranoia code (by means of "dead code") */
++#undef ZFCP_PARANOIA_DEAD_CODE 
++
++/* enable/disable paranoia checks if compiled in */
++#define ZFCP_PARANOIA_PER_DEFAULT
++
++/* paranoia status (override by means of module parameter allowed) */
++#ifdef ZFCP_PARANOIA_PER_DEFAULT
++unsigned char zfcp_paranoia = 1;
++#else
++unsigned char zfcp_paranoia = 0;
++#endif
++
++/*
++ * decide whether paranoia checks are (1) dead code,
++ * (2) active code + disabled, or (3) active code + enabled
++ */
++#ifdef ZFCP_PARANOIA_DEAD_CODE
++#define ZFCP_PARANOIA		if (0)
++#else
++#define ZFCP_PARANOIA		if (zfcp_paranoia)
++#endif
++
++/* association between FSF command and FSF QTCB type */
++static u32 fsf_qtcb_type[] = {
++  [ FSF_QTCB_FCP_CMND ]			= FSF_IO_COMMAND,
++  [ FSF_QTCB_ABORT_FCP_CMND ]		= FSF_SUPPORT_COMMAND,
++  [ FSF_QTCB_OPEN_PORT_WITH_DID ]	= FSF_SUPPORT_COMMAND,
++  [ FSF_QTCB_OPEN_LUN ]			= FSF_SUPPORT_COMMAND,
++  [ FSF_QTCB_CLOSE_LUN ]		= FSF_SUPPORT_COMMAND,
++  [ FSF_QTCB_CLOSE_PORT ]		= FSF_SUPPORT_COMMAND,
++  [ FSF_QTCB_CLOSE_PHYSICAL_PORT ]	= FSF_SUPPORT_COMMAND,
++  [ FSF_QTCB_SEND_ELS ]			= FSF_SUPPORT_COMMAND,
++  [ FSF_QTCB_SEND_GENERIC ]		= FSF_SUPPORT_COMMAND,
++  [ FSF_QTCB_EXCHANGE_CONFIG_DATA ]	= FSF_CONFIG_COMMAND,
++  [ FSF_QTCB_EXCHANGE_PORT_DATA ]       = FSF_PORT_COMMAND,
++  [ FSF_QTCB_DOWNLOAD_CONTROL_FILE ]	= FSF_SUPPORT_COMMAND,
++  [ FSF_QTCB_UPLOAD_CONTROL_FILE ]	= FSF_SUPPORT_COMMAND
++};
++
++/* accumulated log level (module parameter) */
++static u32 loglevel = ZFCP_LOG_LEVEL_DEFAULTS;
++
++unsigned long debug_addr;
++unsigned long debug_len;
++
++const char zfcp_topologies[5][25] = {
++	{"<error>"},
++	{"point-to-point"},
++	{"fabric"}, 
++	{"arbitrated loop"},
++	{"fabric (virt. adapter)"}
++};
++
++const char zfcp_act_subtable_type[5][8] = {
++	{"unknown"}, {"OS"}, {"WWPN"}, {"DID"}, {"LUN"}
++};
++
++inline void _zfcp_hex_dump(char *addr, int count)
++{
++	int i;
++       	for (i = 0; i < count; i++) {
++       	        printk("%02x", addr[i]);
++        	if ((i % 4) == 3)
++       	        	printk(" ");
++               	if ((i % 32) == 31)
++                	printk("\n");
++	}
++	if ((i % 32) != 31)
++		printk("\n");
++}
++ 
++#define ZFCP_HEX_DUMP(level, addr, count) \
++		if (ZFCP_LOG_CHECK(level)) { \
++			_zfcp_hex_dump(addr, count); \
++		}
++
++static int proc_debug=1;
++
++/*
++ * buffer struct used for private_data entry (proc interface)
++ */
++
++typedef struct { 
++     int len;
++     char *buf;
++} procbuf_t;
++
++
++/*
++ * not yet optimal but useful:
++ * waits until condition is met or timeout is expired,
++ * condition might be a function call which allows to
++ * execute some additional instructions aside from check
++ * (e.g. get a lock without race if condition is met),
++ * timeout is modified and holds the remaining time,
++ * thus timeout is zero if timeout is expired,
++ * result value zero indicates that condition has not been met
++ */
++#define __ZFCP_WAIT_EVENT_TIMEOUT(timeout, condition) \
++do { \
++	set_current_state(TASK_UNINTERRUPTIBLE); \
++	while (!(condition) && timeout) \
++		timeout = schedule_timeout(timeout); \
++	current->state = TASK_RUNNING; \
++} while (0);
++
++#define ZFCP_WAIT_EVENT_TIMEOUT(waitqueue, timeout, condition) \
++do { \
++	wait_queue_t entry; \
++	init_waitqueue_entry(&entry, current); \
++	add_wait_queue(&waitqueue, &entry); \
++	__ZFCP_WAIT_EVENT_TIMEOUT(timeout, condition) \
++	remove_wait_queue(&waitqueue, &entry); \
++} while (0);
++
++
++/* General type defines */
++
++typedef long long unsigned int  llui_t;
++
++/* QDIO request identifier */
++typedef u64	qdio_reqid_t;
++
++
++/* FCP(-2) FCP_CMND IU */
++typedef struct _fcp_cmnd_iu {
++	/* FCP logical unit number */
++	fcp_lun_t	fcp_lun;
++	/* command reference number */
++	u8		crn;
++	/* reserved */
++	u8		reserved0:5;
++	/* task attribute */
++	u8		task_attribute:3;
++	/* task management flags */
++	u8		task_management_flags;
++	/* additional FCP_CDB length */
++	u8		add_fcp_cdb_length:6;
++	/* read data */
++	u8		rddata:1;
++	/* write data */
++	u8		wddata:1;
++	/* */
++	u8		fcp_cdb[FCP_CDB_LENGTH];
++	/* variable length fields (additional FCP_CDB, FCP_DL) */
++} __attribute__((packed)) fcp_cmnd_iu_t;
++
++/* data length field may be at variable position in FCP-2 FCP_CMND IU */
++typedef u32	fcp_dl_t;
++
++
++#define RSP_CODE_GOOD			0
++#define RSP_CODE_LENGTH_MISMATCH	1
++#define RSP_CODE_FIELD_INVALID		2
++#define RSP_CODE_RO_MISMATCH		3
++#define RSP_CODE_TASKMAN_UNSUPP		4
++#define RSP_CODE_TASKMAN_FAILED		5
++
++/* see fc-fs */
++#define LS_FAN 0x60000000
++#define LS_RSCN 0x61040000
++
++typedef struct _fcp_rscn_head {
++        u8  command;
++        u8  page_length; /* always 0x04 */
++        u16 payload_len;
++} __attribute__((packed)) fcp_rscn_head_t;
++
++typedef struct _fcp_rscn_element {
++        u8  reserved:2;
++        u8  event_qual:4;
++        u8  addr_format:2;
++        u32 nport_did:24;
++} __attribute__((packed)) fcp_rscn_element_t;
++
++#define ZFCP_PORT_ADDRESS   0x0
++#define ZFCP_AREA_ADDRESS   0x1
++#define ZFCP_DOMAIN_ADDRESS 0x2
++#define ZFCP_FABRIC_ADDRESS 0x3
++
++#define ZFCP_PORTS_RANGE_PORT   0xFFFFFF
++#define ZFCP_PORTS_RANGE_AREA   0xFFFF00
++#define ZFCP_PORTS_RANGE_DOMAIN 0xFF0000
++#define ZFCP_PORTS_RANGE_FABRIC 0x000000
++
++#define ZFCP_NO_PORTS_PER_AREA    0x100
++#define ZFCP_NO_PORTS_PER_DOMAIN  0x10000
++#define ZFCP_NO_PORTS_PER_FABRIC  0x1000000
++
++typedef struct _fcp_fan {
++        u32 command;
++        u32 fport_did;
++        wwn_t fport_wwpn;
++        wwn_t fport_wwname;
++} __attribute__((packed)) fcp_fan_t;
++
++/* see fc-ph */
++typedef struct _fcp_logo {
++        u32 command;
++        u32 nport_did;
++        wwn_t nport_wwpn;
++} __attribute__((packed)) fcp_logo_t;
++
++
++/* FCP(-2) FCP_RSP IU */
++typedef struct _fcp_rsp_iu {
++	/* reserved */
++	u8		reserved0[10];
++	union {
++		struct {
++			/* reserved */
++			u8		reserved1:3;
++			/* */
++			u8		fcp_conf_req:1;
++			/* */
++			u8		fcp_resid_under:1;
++			/* */
++			u8		fcp_resid_over:1;
++			/* */
++			u8		fcp_sns_len_valid:1;
++			/* */
++			u8		fcp_rsp_len_valid:1;
++		} bits;
++		u8		value;
++	} validity;
++	/* */
++	u8		scsi_status;
++	/* */
++	u32		fcp_resid;
++	/* */
++	u32		fcp_sns_len;
++	/* */
++	u32		fcp_rsp_len;
++	/* variable length fields: FCP_RSP_INFO, FCP_SNS_INFO */
++} __attribute__((packed)) fcp_rsp_iu_t;
++
++
++inline char *zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu_t *fcp_rsp_iu)
++{
++        char *fcp_rsp_info_ptr = NULL;
++        fcp_rsp_info_ptr=
++                (unsigned char*)fcp_rsp_iu + (sizeof(fcp_rsp_iu_t));
++
++        return fcp_rsp_info_ptr;
++}
++
++
++inline char *zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu_t *fcp_rsp_iu)
++{
++	char *fcp_sns_info_ptr = NULL;
++        fcp_sns_info_ptr =
++                (unsigned char*)fcp_rsp_iu + (sizeof(fcp_rsp_iu_t));
++          // NOTE:fcp_rsp_info is really only a part of the whole as 
++          // defined in FCP-2 documentation
++        if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid) 
++                fcp_sns_info_ptr = (char *)fcp_sns_info_ptr +
++                        fcp_rsp_iu->fcp_rsp_len;
++
++	return fcp_sns_info_ptr;
++}
++
++#ifdef ZFCP_STAT_REQSIZES
++typedef struct _zfcp_statistics {
++        struct list_head list;
++        u32 num;
++        u32 hits;
++} zfcp_statistics_t;
++#endif
++
++
++/*********************** LIST DEFINES ****************************************/
++
++#define ZFCP_FIRST_ADAPTER \
++	ZFCP_FIRST_ENTITY(&zfcp_data.adapter_list_head,zfcp_adapter_t)
++
++#define ZFCP_FIRST_PORT(a) \
++	ZFCP_FIRST_ENTITY(&(a)->port_list_head,zfcp_port_t)
++
++#define ZFCP_FIRST_UNIT(p) \
++	ZFCP_FIRST_ENTITY(&(p)->unit_list_head,zfcp_unit_t)
++
++#define ZFCP_FIRST_SCSIREQ(a) \
++	ZFCP_FIRST_ENTITY(&(a)->scsi_req_list_head,zfcp_scsi_req_t)
++
++#define ZFCP_FIRST_FSFREQ(a) \
++	ZFCP_FIRST_ENTITY(&(a)->fsf_req_list_head,zfcp_fsf_req_t)
++
++#define ZFCP_LAST_ADAPTER \
++	ZFCP_LAST_ENTITY(&zfcp_data.adapter_list_head,zfcp_adapter_t)
++
++#define ZFCP_LAST_PORT(a) \
++	ZFCP_LAST_ENTITY(&(a)->port_list_head,zfcp_port_t)
++
++#define ZFCP_LAST_UNIT(p) \
++	ZFCP_LAST_ENTITY(&(p)->unit_list_head,zfcp_unit_t)
++
++#define ZFCP_LAST_SCSIREQ(a) \
++	ZFCP_LAST_ENTITY(&(a)->scsi_req_list_head,zfcp_scsi_req_t)
++
++#define ZFCP_LAST_FSFREQ(a) \
++	ZFCP_LAST_ENTITY(&(a)->fsf_req_list_head,zfcp_fsf_req_t)
++
++#define ZFCP_PREV_ADAPTER(a) \
++	ZFCP_PREV_ENTITY(&zfcp_data.adapter_list_head,(a),zfcp_adapter_t)
++
++#define ZFCP_PREV_PORT(p) \
++	ZFCP_PREV_ENTITY(&(p)->adapter->port_list_head,(p),zfcp_port_t)
++
++#define ZFCP_PREV_UNIT(u) \
++	ZFCP_PREV_ENTITY(&(u)->port->unit_list_head,(u),zfcp_unit_t)
++
++#define ZFCP_PREV_SCSIREQ(s) \
++	ZFCP_PREV_ENTITY(&(s)->adapter->scsi_req_list_head,(s),zfcp_scsi_req_t)
++
++#define ZFCP_PREV_FSFREQ(o) \
++	ZFCP_PREV_ENTITY(&(o)->adapter->fsf_req_list_head,(o), \
++				zfcp_fsf_req_t)
++
++#define ZFCP_NEXT_ADAPTER(a) \
++	ZFCP_NEXT_ENTITY(&zfcp_data.adapter_list_head,(a),zfcp_adapter_t)
++
++#define ZFCP_NEXT_PORT(p) \
++	ZFCP_NEXT_ENTITY(&(p)->adapter->port_list_head,(p),zfcp_port_t)
++
++#define ZFCP_NEXT_UNIT(u) \
++	ZFCP_NEXT_ENTITY(&(u)->port->unit_list_head,(u),zfcp_unit_t)
++
++#define ZFCP_NEXT_SCSIREQ(s) \
++	ZFCP_NEXT_ENTITY(&(s)->adapter->scsi_req_list_head,(s),zfcp_scsi_req_t)
++
++#define ZFCP_NEXT_FSFREQ(o) \
++	ZFCP_NEXT_ENTITY(&(o)->adapter->fsf_req_list_head,(o), \
++				zfcp_fsf_req_t)
++
++#define ZFCP_FOR_EACH_FSFREQ(a,o) \
++	ZFCP_FOR_EACH_ENTITY(&(a)->fsf_req_list_head,(o),zfcp_fsf_req_t)
++
++/*
++ * use these macros if you traverse a list and do not stop after
++ * altering the list,
++ * attention: do not modify "tmp" (last) arg during iterations,
++ * usually: removing several elements 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 write variant of lock
++ */
++
++#define ZFCP_FOR_NEXT_ENTITY(head,curr,type,tmp) \
++	for (curr = ZFCP_FIRST_ENTITY(head,type), \
++	     tmp = ZFCP_NEXT_ENTITY(head,curr,type); \
++	     curr; \
++	     curr = tmp, \
++	     tmp = ZFCP_NEXT_ENTITY(head,curr,type))
++
++#define ZFCP_FOR_NEXT_ADAPTER(a,n) \
++	ZFCP_FOR_NEXT_ENTITY(&zfcp_data.adapter_list_head,(a), \
++				zfcp_adapter_t,(n))
++
++#define ZFCP_FOR_NEXT_PORT(a,p,n) \
++	ZFCP_FOR_NEXT_ENTITY(&(a)->port_list_head,(p),zfcp_port_t,(n))
++
++#define ZFCP_FOR_NEXT_UNIT(p,u,n) \
++	ZFCP_FOR_NEXT_ENTITY(&(p)->unit_list_head,(u),zfcp_unit_t,(n))
++
++#define ZFCP_FOR_NEXT_SCSIREQ(a,s,n) \
++	ZFCP_FOR_NEXT_ENTITY(&(a)->scsi_req_list_head,(s),zfcp_scsi_req_t,(n))
++
++#define ZFCP_FOR_NEXT_FSFREQ(a,o,n) \
++	ZFCP_FOR_NEXT_ENTITY(&(a)->fsf_req_list_head,(o), \
++				zfcp_fsf_req_t,(n))
++
++/*
++ * use these macros for loops do not walking through lists but*
++ * changing the list at their heads,
++ * attention: without changing the head of the list or any other
++ * break condition this will become an endless loop,
++ * next and previous pointers may become invalid !!!
++ * usually: removing all elements in lists
++ * 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 write variant of lock
++ */
++#define ZFCP_WHILE_ENTITY(head,curr,type) \
++	while (((curr) = ZFCP_FIRST_ENTITY(head,type)))
++
++#define ZFCP_WHILE_ADAPTER(a) \
++	ZFCP_WHILE_ENTITY(&zfcp_data.adapter_list_head,(a),zfcp_adapter_t)
++
++#define ZFCP_WHILE_PORT(a,p) \
++	ZFCP_WHILE_ENTITY(&(a)->port_list_head,(p),zfcp_port_t)
++
++#define ZFCP_WHILE_UNIT(p,u) \
++	ZFCP_WHILE_ENTITY(&(p)->unit_list_head,(u),zfcp_unit_t)
++
++#define ZFCP_WHILE_SCSIREQ(a,s) \
++	ZFCP_WHILE_ENTITY(&(a)->scsi_req_list_head,(s),zfcp_scsi_req_t)
++
++#define ZFCP_WHILE_FSFREQ(a,o) \
++	ZFCP_WHILE_ENTITY(&(a)->fsf_req_list_head,(o),zfcp_fsf_req_t)
++
++/* prototypes for functions which could kernel lib functions (but aren't) */
++static size_t	strnspn(const char*, const char*, size_t);
++char*		strnpbrk(const char*, const char*, size_t);
++char*		strnchr(const char*, int, size_t);
++char*		strnrchr(const char*, int, size_t);
++
++/* prototypes for functions written against the modul interface */
++static int __init	zfcp_module_init(void);
++static void __exit	zfcp_module_exit(void);
++
++int zfcp_reboot_handler (struct notifier_block*, unsigned long, void*);
++
++/* prototypes for functions written against the SCSI stack HBA driver interface */
++int		zfcp_scsi_detect (Scsi_Host_Template*);
++int		zfcp_scsi_revoke (Scsi_Device*);
++int		zfcp_scsi_release (struct Scsi_Host*);
++int		zfcp_scsi_queuecommand (Scsi_Cmnd*, void (*done)(Scsi_Cmnd*));
++int		zfcp_scsi_eh_abort_handler (Scsi_Cmnd*);
++int		zfcp_scsi_eh_device_reset_handler (Scsi_Cmnd*);
++int		zfcp_scsi_eh_bus_reset_handler (Scsi_Cmnd*);
++int		zfcp_scsi_eh_host_reset_handler (Scsi_Cmnd*);
++void            zfcp_scsi_select_queue_depth(struct Scsi_Host*, Scsi_Device*);
++
++static inline int zfcp_sg_list_alloc(zfcp_sg_list_t*, size_t);
++static inline int zfcp_sg_list_free(zfcp_sg_list_t*);
++static inline int zfcp_sg_list_copy_from_user(zfcp_sg_list_t*, void*, size_t);
++static inline int zfcp_sg_list_copy_to_user(void*, zfcp_sg_list_t*, size_t);
++
++/* prototypes for functions written against the FSF interface */
++static int zfcp_fsf_req_send(zfcp_fsf_req_t*, struct timer_list*);
++static int zfcp_fsf_req_create(zfcp_adapter_t *, u32, int, zfcp_mem_pool_t *,
++                               unsigned long *, zfcp_fsf_req_t **);
++static int zfcp_fsf_req_free(zfcp_fsf_req_t*);
++static int	zfcp_fsf_exchange_config_data (zfcp_erp_action_t*);
++static int	zfcp_fsf_open_port (zfcp_erp_action_t*);
++static int	zfcp_fsf_close_port (zfcp_erp_action_t*);
++static int	zfcp_fsf_open_unit (zfcp_erp_action_t*);
++static int	zfcp_fsf_close_unit (zfcp_erp_action_t*);
++static int	zfcp_fsf_close_physical_port (zfcp_erp_action_t*);
++static int	zfcp_fsf_send_fcp_command_task (zfcp_unit_t*, Scsi_Cmnd *);
++static zfcp_fsf_req_t*	zfcp_fsf_send_fcp_command_task_management
++			(zfcp_adapter_t*, zfcp_unit_t*, u8, int);
++static zfcp_fsf_req_t*	zfcp_fsf_abort_fcp_command
++			(unsigned long, zfcp_adapter_t*, zfcp_unit_t*, int);
++static void zfcp_fsf_start_scsi_er_timer(zfcp_adapter_t*);
++static void zfcp_fsf_scsi_er_timeout_handler(unsigned long);
++static int	zfcp_fsf_status_read (zfcp_adapter_t*, int);
++static int	zfcp_fsf_control_file(
++	zfcp_adapter_t*, zfcp_fsf_req_t**, u32, u32, zfcp_sg_list_t*);
++
++static int	zfcp_fsf_exchange_config_data_handler
++			(zfcp_fsf_req_t*);
++static int	zfcp_fsf_exchange_port_data_handler (zfcp_fsf_req_t*);
++static int	zfcp_fsf_open_port_handler (zfcp_fsf_req_t*);
++static int      zfcp_fsf_close_port_handler (zfcp_fsf_req_t*);
++static int      zfcp_fsf_close_physical_port_handler (zfcp_fsf_req_t*);
++static int	zfcp_fsf_open_unit_handler (zfcp_fsf_req_t*);
++static int      zfcp_fsf_close_unit_handler (zfcp_fsf_req_t*);
++static int	zfcp_fsf_send_fcp_command_handler (zfcp_fsf_req_t*);
++static int	zfcp_fsf_send_fcp_command_task_handler
++			(zfcp_fsf_req_t*);
++static int	zfcp_fsf_send_fcp_command_task_management_handler
++			(zfcp_fsf_req_t*);
++static int	zfcp_fsf_abort_fcp_command_handler (zfcp_fsf_req_t*);
++static int	zfcp_fsf_send_ct_handler (zfcp_fsf_req_t*);
++static int      zfcp_fsf_status_read_handler (zfcp_fsf_req_t*);
++void		zfcp_fsf_incoming_els (zfcp_fsf_req_t *); 
++static int	zfcp_fsf_control_file_handler(zfcp_fsf_req_t*);
++
++static inline int
++		zfcp_fsf_req_create_sbal_check
++			(unsigned long*, zfcp_qdio_queue_t*, int);
++
++static int	zfcp_fsf_send_els_handler(zfcp_fsf_req_t *);
++
++static inline int
++		zfcp_mem_pool_element_alloc (zfcp_mem_pool_t*, int);
++static inline int
++		zfcp_mem_pool_element_free (zfcp_mem_pool_t*, int);
++static inline void*
++		zfcp_mem_pool_element_get (zfcp_mem_pool_t*, int);
++static inline int
++		zfcp_mem_pool_element_put (zfcp_mem_pool_t*, int);
++
++static inline int
++		zfcp_mem_pool_create (zfcp_mem_pool_t*, int, int,
++                                      void (*function)(unsigned long),
++                                      unsigned long);
++static inline int
++		zfcp_mem_pool_destroy (zfcp_mem_pool_t*);
++static inline void*
++		zfcp_mem_pool_find (zfcp_mem_pool_t*);
++static inline int
++		zfcp_mem_pool_return (void*, zfcp_mem_pool_t*);
++
++
++
++static void	zfcp_scsi_low_mem_buffer_timeout_handler
++			(unsigned long);
++
++static zfcp_fsf_req_t* zfcp_fsf_req_alloc(zfcp_mem_pool_t *, int, int);
++static int	zfcp_fsf_req_cleanup (zfcp_fsf_req_t*);
++static int	zfcp_fsf_req_wait_and_cleanup
++			(zfcp_fsf_req_t*, int, u32*);
++static int	zfcp_fsf_req_complete (zfcp_fsf_req_t*);
++static int	zfcp_fsf_protstatus_eval (zfcp_fsf_req_t*);
++static int	zfcp_fsf_fsfstatus_eval (zfcp_fsf_req_t*);
++static int      zfcp_fsf_fsfstatus_qual_eval(zfcp_fsf_req_t *);
++static int 	zfcp_fsf_req_dispatch (zfcp_fsf_req_t*);
++static int	zfcp_fsf_req_dismiss (zfcp_fsf_req_t*);
++static int	zfcp_fsf_req_dismiss_all (zfcp_adapter_t*);
++
++/* prototypes for FCP related functions */
++static int	zfcp_nameserver_enqueue (zfcp_adapter_t*);
++static int zfcp_ns_gid_pn_request(zfcp_erp_action_t*);
++static void zfcp_ns_gid_pn_handler(unsigned long);
++static void zfcp_ns_ga_nxt_handler(unsigned long);
++
++/* prototypes for functions written against the QDIO layer interface */
++qdio_handler_t	zfcp_qdio_request_handler;
++qdio_handler_t	zfcp_qdio_response_handler;
++
++int             zfcp_qdio_handler_error_check
++			(zfcp_adapter_t*, unsigned int, unsigned int,
++			 unsigned int);
++
++/* prototypes for functions written against the Dynamic I/O layer interface */
++static int	zfcp_dio_oper_handler (int, devreg_t *);
++static void	zfcp_dio_not_oper_handler (int, int);
++
++/* prototypes for functions written against the Common I/O layer interface */
++static void	zfcp_cio_handler (int, void *, struct pt_regs *);
++
++/* prototypes for other functions */
++static int	zfcp_task_management_function (zfcp_unit_t*, u8);
++
++static void	zfcp_config_parse_error(
++			unsigned char *, unsigned char *, const char *, ...)
++			__attribute__ ((format (printf, 3, 4)));
++static int	zfcp_config_parse_record(
++			unsigned char*, int, zfcp_config_record_t*);
++static int	zfcp_config_parse_record_list (unsigned char*, int, int);
++//static int	zfcp_config_parse_record_del (zfcp_config_record_t*);
++static int	zfcp_config_cleanup (void);
++
++static int      zfcp_adapter_enqueue (devno_t, zfcp_adapter_t**);
++static int      zfcp_port_enqueue (zfcp_adapter_t*, scsi_id_t, wwn_t, u32, 
++                                   zfcp_port_t**);
++static int      zfcp_unit_enqueue (zfcp_port_t*, scsi_lun_t, fcp_lun_t,
++                                   zfcp_unit_t**);
++
++static int	zfcp_adapter_dequeue (zfcp_adapter_t*);
++static int	zfcp_port_dequeue (zfcp_port_t*);
++static int	zfcp_unit_dequeue (zfcp_unit_t*);
++
++static int	zfcp_adapter_detect(zfcp_adapter_t*);
++static int	zfcp_adapter_irq_register(zfcp_adapter_t*);
++static int	zfcp_adapter_irq_unregister(zfcp_adapter_t*);
++static int	zfcp_adapter_scsi_register(zfcp_adapter_t*);
++static int	zfcp_adapter_scsi_register_all (void);
++static int	zfcp_adapter_shutdown_all (void);
++
++static u32	zfcp_derive_driver_version (void);
++
++static inline int
++		zfcp_qdio_reqid_check(zfcp_adapter_t*, void*);
++
++static inline void zfcp_qdio_sbal_limit(zfcp_fsf_req_t*, int);
++static inline volatile qdio_buffer_element_t*
++	zfcp_qdio_sbale_get
++		(zfcp_qdio_queue_t*, int, int);
++static inline volatile qdio_buffer_element_t*
++	zfcp_qdio_sbale_req
++		(zfcp_fsf_req_t*, int, int);
++static inline volatile qdio_buffer_element_t*
++	zfcp_qdio_sbale_resp
++		(zfcp_fsf_req_t*, int, int);
++static inline volatile qdio_buffer_element_t*
++	zfcp_qdio_sbale_curr
++		(zfcp_fsf_req_t*);
++static inline volatile qdio_buffer_element_t*
++	zfcp_qdio_sbal_chain
++		(zfcp_fsf_req_t*, unsigned long);
++static inline volatile qdio_buffer_element_t*
++	zfcp_qdio_sbale_next
++		(zfcp_fsf_req_t*, unsigned long);
++static inline int
++	zfcp_qdio_sbals_zero
++		(zfcp_qdio_queue_t*, int, int);
++static inline int
++	zfcp_qdio_sbals_wipe
++		(zfcp_fsf_req_t*);
++static inline void
++	zfcp_qdio_sbale_fill
++		(zfcp_fsf_req_t*, unsigned long, void*, int);
++static inline int
++	zfcp_qdio_sbals_from_segment
++		(zfcp_fsf_req_t*, unsigned long, void*, unsigned long);
++static inline int zfcp_qdio_sbals_from_buffer(zfcp_fsf_req_t *, unsigned long,
++                                              void *, unsigned long, int);
++static inline int zfcp_qdio_sbals_from_sg(zfcp_fsf_req_t*, unsigned long,
++                                          struct scatterlist*, int, int);
++static inline int
++	zfcp_qdio_sbals_from_scsicmnd
++		(zfcp_fsf_req_t*, unsigned long, struct scsi_cmnd*);
++static inline void
++		zfcp_zero_sbals(qdio_buffer_t**, int, int);
++
++static zfcp_unit_t*
++		zfcp_unit_lookup (zfcp_adapter_t*, int, int, int);
++
++/* prototypes for functions faking callbacks of lower layers */
++inline void     zfcp_scsi_process_and_clear_fake_queue(unsigned long);
++inline void     zfcp_scsi_insert_into_fake_queue(zfcp_adapter_t *,
++                                                 Scsi_Cmnd *);
++void		zfcp_fake_outbound_callback (unsigned long);
++void		zfcp_fake_inbound_callback (unsigned long);
++
++/* prototypes for proc-file interfacing stuff */
++unsigned long zfcp_find_forward(char**, 
++                                unsigned long*,
++                                char**,
++                                unsigned long*);
++unsigned long zfcp_find_backward(char**, 
++                                 unsigned long*,
++                                 char**,
++                                 unsigned long*);
++int zfcp_create_root_proc(void);
++int zfcp_delete_root_proc(void);
++int zfcp_create_data_procs(void);
++int zfcp_delete_data_procs(void);
++int zfcp_proc_map_open(struct inode*, struct file*);
++int zfcp_proc_map_close(struct inode*, struct file*);
++int zfcp_open_parm_proc(struct inode*, struct file*);
++int zfcp_close_parm_proc(struct inode*, struct file*);
++int zfcp_open_add_map_proc(struct inode*, struct file*);
++int zfcp_close_add_map_proc(struct inode*, struct file*);
++int zfcp_adapter_proc_open(struct inode*, struct file*);
++int zfcp_adapter_proc_close(struct inode*, struct file*);
++int zfcp_port_proc_open(struct inode*, struct file*);
++int zfcp_port_proc_close(struct inode*, struct file*);
++int zfcp_unit_proc_open(struct inode*, struct file*);
++int zfcp_unit_proc_close(struct inode*, struct file*);
++int zfcp_cfdc_dev_ioctl(struct inode*, struct file*, unsigned int, unsigned long);
++ssize_t zfcp_parm_proc_read(struct file*, 
++                             char*, 
++                             size_t, 
++                             loff_t*);
++ssize_t zfcp_parm_proc_write(struct file*, 
++                             const char*, 
++                             size_t, 
++                             loff_t*);
++ssize_t zfcp_add_map_proc_write(struct file*, 
++                             const char*, 
++                             size_t, 
++                             loff_t*);
++ssize_t zfcp_proc_map_read(struct file*, 
++                             char*, 
++                             size_t, 
++                             loff_t*);
++ssize_t zfcp_adapter_proc_read(struct file*,
++                             char*,
++                             size_t,
++                             loff_t*);
++ssize_t zfcp_adapter_proc_write(struct file*,
++                             const char*,
++                             size_t,
++                             loff_t*);
++ssize_t zfcp_port_proc_read(struct file*,
++                             char*,
++                             size_t,
++                             loff_t*);
++ssize_t zfcp_port_proc_write(struct file*,
++                             const char*,
++                             size_t,
++                             loff_t*);
++ssize_t zfcp_unit_proc_read(struct file*,
++                             char*,
++                             size_t,
++                             loff_t*);
++ssize_t zfcp_unit_proc_write(struct file*,
++                             const char*,
++                             size_t,
++                             loff_t*);
++int zfcp_create_adapter_proc(zfcp_adapter_t*);
++int zfcp_delete_adapter_proc(zfcp_adapter_t*);
++int zfcp_create_port_proc(zfcp_port_t*);
++int zfcp_delete_port_proc(zfcp_port_t*);
++int zfcp_create_unit_proc(zfcp_unit_t*);
++int zfcp_delete_unit_proc(zfcp_unit_t*);
++
++/* prototypes for initialisation functions */
++static int zfcp_dio_register (void); 
++
++/* prototypes for extended link services functions */
++static int zfcp_els(zfcp_port_t*, u8);
++static void zfcp_els_handler(unsigned long);
++static int zfcp_test_link(zfcp_port_t*);
++
++/* prototypes for error recovery functions */
++static int zfcp_erp_adapter_reopen (zfcp_adapter_t*, int);
++static int zfcp_erp_port_forced_reopen (zfcp_port_t*, int);
++static int zfcp_erp_port_reopen (zfcp_port_t*, int);
++static int zfcp_erp_unit_reopen (zfcp_unit_t*, int);
++
++static int zfcp_erp_adapter_reopen_internal (zfcp_adapter_t*, int);
++static int zfcp_erp_port_forced_reopen_internal (zfcp_port_t*, int);
++static int zfcp_erp_port_reopen_internal (zfcp_port_t*, int);
++static int zfcp_erp_unit_reopen_internal (zfcp_unit_t*, int);
++
++static int zfcp_erp_port_reopen_all (zfcp_adapter_t*, int);
++static int zfcp_erp_port_reopen_all_internal (zfcp_adapter_t*, int);
++static int zfcp_erp_unit_reopen_all_internal (zfcp_port_t*, int);
++
++static inline int zfcp_erp_adapter_shutdown (zfcp_adapter_t*, int);
++static inline int zfcp_erp_port_shutdown (zfcp_port_t*, int);
++static inline int zfcp_erp_port_shutdown_all (zfcp_adapter_t*, int);
++static inline int zfcp_erp_unit_shutdown (zfcp_unit_t*, int);
++
++static int zfcp_erp_adapter_block (zfcp_adapter_t*, int);
++static int zfcp_erp_adapter_unblock (zfcp_adapter_t*);
++static int zfcp_erp_port_block (zfcp_port_t*, int);
++static int zfcp_erp_port_unblock (zfcp_port_t*);
++static int zfcp_erp_unit_block (zfcp_unit_t*, int);
++static int zfcp_erp_unit_unblock (zfcp_unit_t*);
++
++static int  zfcp_erp_thread (void*);
++static int  zfcp_erp_thread_setup (zfcp_adapter_t*);
++static void zfcp_erp_thread_setup_task (void*);
++static int  zfcp_erp_thread_kill (zfcp_adapter_t*);
++
++static int zfcp_erp_strategy (zfcp_erp_action_t*);
++
++static int zfcp_erp_strategy_do_action (zfcp_erp_action_t*);
++static int zfcp_erp_strategy_memwait (zfcp_erp_action_t*);
++static int zfcp_erp_strategy_check_target (zfcp_erp_action_t*, int);
++static int zfcp_erp_strategy_check_unit (zfcp_unit_t*, int);
++static int zfcp_erp_strategy_check_port (zfcp_port_t*, int);
++static int zfcp_erp_strategy_check_adapter (zfcp_adapter_t*, int);
++static int zfcp_erp_strategy_statechange
++		(int, u32, zfcp_adapter_t*, zfcp_port_t*, zfcp_unit_t*, int);
++static inline int zfcp_erp_strategy_statechange_detected (atomic_t*, u32);
++static int zfcp_erp_strategy_followup_actions
++		(int, zfcp_adapter_t*, zfcp_port_t*, zfcp_unit_t*, int);
++static int zfcp_erp_strategy_check_queues (zfcp_adapter_t*);
++static int zfcp_erp_strategy_check_action(zfcp_erp_action_t *, int);
++
++static int zfcp_erp_adapter_strategy (zfcp_erp_action_t*);
++static int zfcp_erp_adapter_strategy_generic (zfcp_erp_action_t*, int);
++static int zfcp_erp_adapter_strategy_close (zfcp_erp_action_t*);
++static int zfcp_erp_adapter_strategy_close_irq (zfcp_erp_action_t*);
++static int zfcp_erp_adapter_strategy_close_qdio (zfcp_erp_action_t*);
++static int zfcp_erp_adapter_strategy_close_fsf (zfcp_erp_action_t*);
++static int zfcp_erp_adapter_strategy_open (zfcp_erp_action_t*);
++static int zfcp_erp_adapter_strategy_open_irq (zfcp_erp_action_t*);
++static int zfcp_erp_adapter_strategy_open_qdio (zfcp_erp_action_t*);
++static int zfcp_erp_adapter_strategy_open_fsf (zfcp_erp_action_t*);
++static int zfcp_erp_adapter_strategy_open_fsf_xconfig (zfcp_erp_action_t*);
++static int zfcp_erp_adapter_strategy_open_fsf_statusread (zfcp_erp_action_t*);
++
++static int zfcp_erp_port_forced_strategy (zfcp_erp_action_t*);
++static int zfcp_erp_port_forced_strategy_close (zfcp_erp_action_t*);
++
++static int zfcp_erp_port_strategy (zfcp_erp_action_t*);
++static int zfcp_erp_port_strategy_clearstati (zfcp_port_t*);
++static int zfcp_erp_port_strategy_close (zfcp_erp_action_t*);
++static int zfcp_erp_port_strategy_open (zfcp_erp_action_t*);
++static int zfcp_erp_port_strategy_open_nameserver (zfcp_erp_action_t*);
++static int zfcp_erp_port_strategy_open_nameserver_wakeup (zfcp_erp_action_t*);
++static int zfcp_erp_port_strategy_open_common (zfcp_erp_action_t*);
++static int zfcp_erp_port_strategy_open_common_lookup (zfcp_erp_action_t*);
++static int zfcp_erp_port_strategy_open_port (zfcp_erp_action_t*);
++
++static int zfcp_erp_unit_strategy (zfcp_erp_action_t*);
++static int zfcp_erp_unit_strategy_clearstati (zfcp_unit_t*);
++static int zfcp_erp_unit_strategy_close (zfcp_erp_action_t*);
++static int zfcp_erp_unit_strategy_open (zfcp_erp_action_t*);
++
++static void zfcp_erp_modify_adapter_status(zfcp_adapter_t*, u32, int);
++static void zfcp_erp_modify_port_status(zfcp_port_t*, u32, int);
++static void zfcp_erp_modify_unit_status(zfcp_unit_t*, u32, int);
++static void zfcp_erp_adapter_failed(zfcp_adapter_t*);
++static void zfcp_erp_port_failed(zfcp_port_t*);
++static void zfcp_erp_unit_failed(zfcp_unit_t*);
++
++static int zfcp_erp_action_dismiss_adapter (zfcp_adapter_t*);
++static int zfcp_erp_action_dismiss_port(zfcp_port_t*);
++/* zfcp_erp_action_dismiss_unit not needed */
++static int zfcp_erp_action_dismiss (zfcp_erp_action_t*);
++
++static int zfcp_erp_action_enqueue
++	(int, zfcp_adapter_t*, zfcp_port_t*, zfcp_unit_t*);
++static int zfcp_erp_action_dequeue (zfcp_erp_action_t*);
++
++static int zfcp_erp_action_ready (zfcp_erp_action_t*);
++static int zfcp_erp_action_exists (zfcp_erp_action_t*);
++
++static inline void zfcp_erp_action_to_ready (zfcp_erp_action_t*);
++static inline void zfcp_erp_action_to_running (zfcp_erp_action_t*);
++static inline void zfcp_erp_from_one_to_other
++	(struct list_head*, struct list_head*);
++
++static int  zfcp_erp_async_handler (zfcp_erp_action_t*, unsigned long);
++static void zfcp_erp_memwait_handler (unsigned long);
++static void zfcp_erp_timeout_handler (unsigned long);
++static int zfcp_erp_timeout_init (zfcp_erp_action_t*);
++
++int zfcp_erp_wait (zfcp_adapter_t*);
++
++
++#ifdef ZFCP_STAT_REQSIZES
++static int zfcp_statistics_clear
++	(zfcp_adapter_t*, struct list_head*);
++static int zfcp_statistics_print
++	(zfcp_adapter_t*, struct list_head*, char*, char*, int, int);
++static void zfcp_statistics_inc
++	(zfcp_adapter_t*, struct list_head*, u32);
++static inline void zfcp_statistics_new
++	(zfcp_adapter_t*, struct list_head*, u32);
++static inline void zfcp_statistics_sort
++	(struct list_head*, struct list_head*, zfcp_statistics_t*);
++#endif
++
++
++/* driver data */
++static struct file_operations zfcp_parm_fops =
++{
++     open: zfcp_open_parm_proc, 
++     read: zfcp_parm_proc_read,
++     write: zfcp_parm_proc_write, 
++     release: zfcp_close_parm_proc, 
++};
++
++static struct file_operations zfcp_map_fops =
++{
++     open:	zfcp_proc_map_open,
++     read:	zfcp_proc_map_read,
++     release:	zfcp_proc_map_close,
++};
++
++static struct file_operations zfcp_add_map_fops =
++{
++     open: zfcp_open_add_map_proc,
++     write: zfcp_add_map_proc_write,
++     release: zfcp_close_add_map_proc,
++};
++
++static struct file_operations zfcp_adapter_fops =
++{
++     open: zfcp_adapter_proc_open,
++     read: zfcp_adapter_proc_read,
++     write: zfcp_adapter_proc_write,
++     release: zfcp_adapter_proc_close,
++};
++
++static struct file_operations zfcp_port_fops =
++{
++     open: zfcp_port_proc_open,
++     read: zfcp_port_proc_read,
++     write: zfcp_port_proc_write,
++     release: zfcp_port_proc_close,
++};
++
++static struct file_operations zfcp_unit_fops =
++{
++     open: zfcp_unit_proc_open,
++     read: zfcp_unit_proc_read,
++     write: zfcp_unit_proc_write,
++     release: zfcp_unit_proc_close,
++};
++
++static struct file_operations zfcp_cfdc_fops =
++{
++	ioctl: zfcp_cfdc_dev_ioctl
++};
++
++static struct miscdevice zfcp_cfdc_misc = {
++	minor: ZFCP_CFDC_DEV_MINOR,
++	name: ZFCP_CFDC_DEV_NAME,
++	fops: &zfcp_cfdc_fops
++};
++
++zfcp_data_t zfcp_data = {
++	{	/* Scsi Host Template */
++                name:                   ZFCP_NAME,
++                proc_name:              "dummy",
++                proc_info:              NULL, /* we don't need scsi proc info */
++		detect:			zfcp_scsi_detect,
++		revoke:			zfcp_scsi_revoke,
++		release:		zfcp_scsi_release,
++		queuecommand:		zfcp_scsi_queuecommand,
++		eh_abort_handler:	zfcp_scsi_eh_abort_handler,
++		eh_device_reset_handler:zfcp_scsi_eh_device_reset_handler,
++		eh_bus_reset_handler:	zfcp_scsi_eh_bus_reset_handler,
++		eh_host_reset_handler:	zfcp_scsi_eh_host_reset_handler,
++		/* FIXME(openfcp): Tune */
++		can_queue:	   	4096,
++		this_id:		0,
++		/*
++		 * FIXME:
++		 * one less? can zfcp_create_sbale cope with it?
++		 */
++                sg_tablesize:	        ZFCP_MAX_SBALES_PER_REQ,
++		/* some moderate value for the moment */
++		cmd_per_lun:		ZFCP_CMND_PER_LUN,
++		/* no requirement on the addresses of data buffers */
++		unchecked_isa_dma:	0,
++		/* maybe try it later */
++		use_clustering:		1,
++		/* we are straight forward */
++		use_new_eh_code:	1
++	}
++	/* rest initialized with zeros */
++};
++
++
++inline fcp_dl_t *zfcp_get_fcp_dl_ptr(fcp_cmnd_iu_t *fcp_cmd)
++{
++	int additional_length = fcp_cmd->add_fcp_cdb_length << 2;
++	fcp_dl_t *fcp_dl_addr=
++			(fcp_dl_t *)(
++				(unsigned char*)fcp_cmd +				
++                                sizeof(fcp_cmnd_iu_t) +
++				additional_length);    
++	/*
++	 * fcp_dl_addr = start address of fcp_cmnd structure + 
++	 * size of fixed part + size of dynamically sized add_dcp_cdb field
++	 * SEE FCP-2 documentation
++	 */
++	return fcp_dl_addr;
++}
++
++
++inline fcp_dl_t zfcp_get_fcp_dl(fcp_cmnd_iu_t *fcp_cmd)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++     
++	ZFCP_LOG_TRACE("enter (fcp_cmd=0x%lx)\n",
++                       (unsigned long) fcp_cmd);
++	ZFCP_LOG_TRACE("exit 0x%lx\n",
++                       (unsigned long)*zfcp_get_fcp_dl_ptr(fcp_cmd));
++	return *zfcp_get_fcp_dl_ptr(fcp_cmd);
++#undef ZFCP_LOG_AREA       
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++inline void zfcp_set_fcp_dl(fcp_cmnd_iu_t *fcp_cmd, fcp_dl_t fcp_dl)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++     ZFCP_LOG_TRACE("enter (fcp_cmd=0x%lx), (fcp_dl=0x%x)\n",
++                    (unsigned long)fcp_cmd,
++                    fcp_dl);
++     *zfcp_get_fcp_dl_ptr(fcp_cmd)=fcp_dl;
++     ZFCP_LOG_TRACE("exit\n");
++#undef ZFCP_LOG_AREA       
++#undef ZFCP_LOG_AREA_PREFIX
++}  
++
++
++#ifdef MODULE
++/* declare driver module init/cleanup functions */
++module_init(zfcp_module_init);
++module_exit(zfcp_module_exit);
++
++MODULE_AUTHOR(
++	"Martin Peschke <mpeschke at de.ibm.com>, "
++        "Raimund Schroeder <raimund.schroeder at de.ibm.com>, "
++	"Aron Zeh <arzeh at de.ibm.com>, "
++	"IBM Deutschland Entwicklung GmbH");
++/* what this driver module is about */
++MODULE_DESCRIPTION(
++	"FCP (SCSI over Fibre Channel) HBA driver for IBM eServer zSeries, " ZFCP_REVISION);
++MODULE_LICENSE("GPL");
++/* log level may be provided as a module parameter */
++MODULE_PARM(loglevel, "i");
++/* short explaination of the previous module parameter */
++MODULE_PARM_DESC(loglevel,
++	"log levels, 8 nibbles: "
++	"(unassigned) ERP QDIO DIO Config FSF SCSI Other, "
++	"levels: 0=none 1=normal 2=devel 3=trace");
++#endif /* MODULE */
++
++#ifdef ZFCP_PRINT_FLAGS
++static u32 flags_dump=0;
++MODULE_PARM(flags_dump, "i");
++#define ZFCP_LOG_FLAGS(ll, m...) \
++		if (ll<=flags_dump) \
++			_ZFCP_LOG(m)
++#else
++#define ZFCP_LOG_FLAGS(ll, m...)
++#endif
++
++static char *map = NULL;
++#ifdef MODULE
++MODULE_PARM(map, "s");
++MODULE_PARM_DESC(map,
++	"Initial FC to SCSI mapping table");
++
++/* enable/disable paranoia (extra checks to ensure driver consistency) */
++MODULE_PARM(zfcp_paranoia, "b");
++/* short explaination of the previous module parameter */
++MODULE_PARM_DESC(zfcp_paranoia,
++	"extra checks to ensure driver consistency, "
++	"0=disabled other !0=enabled");
++#else
++
++/* zfcp_map boot parameter */
++static int __init zfcp_map_setup(char *str)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	/* don't parse trailing " */
++	map = str + 1;		
++	/* don't parse final " */
++	map[strlen(map) - 1] = ZFCP_PARSE_SPACE_CHARS[0];
++	ZFCP_LOG_INFO("map is %s\n", map);
++	return 1;	/* why just 1? */
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++__setup("zfcp_map=", zfcp_map_setup);
++
++/* zfcp_loglevel boot_parameter */
++static int __init zfcp_loglevel_setup(char *str)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	loglevel = simple_strtoul(str, NULL, 0);
++	//ZFCP_LOG_NORMAL("loglevel is 0x%x\n", loglevel);
++	return 1;	/* why just 1? */
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++__setup("zfcp_loglevel=", zfcp_loglevel_setup);
++
++#endif /* MODULE */
++
++#ifdef ZFCP_CAUSE_ERRORS
++void zfcp_force_error(unsigned long data)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
++        
++        zfcp_adapter_t *adapter;
++
++        ZFCP_LOG_NORMAL("Cause error....\n");
++        adapter = ZFCP_FIRST_ADAPTER;
++        printk("adater reopen\n");
++        zfcp_erp_adapter_reopen(adapter, 0);
++        printk("adater close\n");
++	zfcp_erp_adapter_shutdown(adapter, 0);
++        /*
++	zfcp_force_error_timer.function = zfcp_force_error;
++        zfcp_force_error_timer.data = 0;
++        zfcp_force_error_timer.expires = jiffies + 60*HZ;
++	add_timer(&zfcp_force_error_timer);
++        */
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++#endif //ZFCP_CAUSE_ERRORS
++
++
++static inline int zfcp_fsf_req_is_scsi_cmnd(zfcp_fsf_req_t *fsf_req)
++{
++	return ((fsf_req->fsf_command == FSF_QTCB_FCP_CMND) &&
++		!(fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT));
++}
++
++
++static inline void zfcp_cmd_dbf_event_fsf(
++		const char *text,
++		zfcp_fsf_req_t *fsf_req,
++		void *add_data,
++		int add_length)
++{
++#ifdef ZFCP_DEBUG_COMMANDS
++	zfcp_adapter_t *adapter = fsf_req->adapter;
++	Scsi_Cmnd *scsi_cmnd;
++	int level = 3;
++	int i;
++	unsigned long flags;
++
++	write_lock_irqsave(&adapter->cmd_dbf_lock, flags);
++	if (zfcp_fsf_req_is_scsi_cmnd(fsf_req)) {
++		scsi_cmnd = fsf_req->data.send_fcp_command_task.scsi_cmnd;
++		debug_text_event(adapter->cmd_dbf, level, "fsferror");
++		debug_text_event(adapter->cmd_dbf, level, text);
++		debug_event(adapter->cmd_dbf, level, &fsf_req, sizeof(unsigned long));
++		debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no, sizeof(u32));
++		debug_event(adapter->cmd_dbf, level, &scsi_cmnd, sizeof(unsigned long));
++		debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd,
++			    min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len));
++		for (i = 0; i < add_length; i += ZFCP_CMD_DBF_LENGTH)
++			debug_event(
++				adapter->cmd_dbf,
++				level,
++				(char*)add_data + i,
++				min(ZFCP_CMD_DBF_LENGTH, add_length - i));
++	}
++	write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags);
++#endif
++}
++
++
++static inline void zfcp_cmd_dbf_event_scsi(
++		const char *text,
++		zfcp_adapter_t *adapter,
++		Scsi_Cmnd *scsi_cmnd)
++{
++#ifdef ZFCP_DEBUG_COMMANDS
++	zfcp_fsf_req_t *fsf_req = (zfcp_fsf_req_t*) scsi_cmnd->host_scribble;
++	int level = ((host_byte(scsi_cmnd->result) != 0) ? 1 : 5);
++	unsigned long flags;
++
++	write_lock_irqsave(&adapter->cmd_dbf_lock, flags);	
++	debug_text_event(adapter->cmd_dbf, level, "hostbyte");
++	debug_text_event(adapter->cmd_dbf, level, text);
++	debug_event(adapter->cmd_dbf, level, &scsi_cmnd->result, sizeof(u32));
++	debug_event(adapter->cmd_dbf, level, &scsi_cmnd, sizeof(unsigned long));
++	debug_event(adapter->cmd_dbf, level, &scsi_cmnd->cmnd,
++		    min(ZFCP_CMD_DBF_LENGTH, (int)scsi_cmnd->cmd_len));
++	if (fsf_req) {
++		debug_event(adapter->cmd_dbf, level, &fsf_req, sizeof(unsigned long));
++		debug_event(adapter->cmd_dbf, level, &fsf_req->seq_no, sizeof(u32));
++	} else	{
++		debug_text_event(adapter->cmd_dbf, level, "");
++		debug_text_event(adapter->cmd_dbf, level, "");
++	}
++	write_unlock_irqrestore(&adapter->cmd_dbf_lock, flags);
++#endif
++}
++
++
++static inline void zfcp_in_els_dbf_event(
++		zfcp_adapter_t *adapter,
++		const char *text,
++		fsf_status_read_buffer_t *status_buffer,
++		int length)
++{
++#ifdef ZFCP_DEBUG_INCOMING_ELS
++	int level = 1;
++	int i;
++
++	debug_text_event(adapter->in_els_dbf, level, text);
++	debug_event(adapter->in_els_dbf, level, &status_buffer->d_id, 8);
++	for (i = 0; i < length; i += ZFCP_IN_ELS_DBF_LENGTH)
++		debug_event(
++			adapter->in_els_dbf,
++			level,
++			(char*)status_buffer->payload + i,
++			min(ZFCP_IN_ELS_DBF_LENGTH, length - i));
++#endif
++}
++
++
++/****************************************************************/
++
++
++/*
++ * function:	zfcp_module_init
++ *
++ * purpose:	driver module initialization routine
++ *
++ * locks:       initialises zfcp_data.proc_sema, zfcp_data.adapter_list_lock
++ *              zfcp_data.proc_sema is taken and released within this 
++ *              function
++ *
++ * returns:	0	success
++ *		!0	failure
++ */
++static int __init zfcp_module_init(void)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++	int retval = 0;
++
++	atomic_set(&zfcp_data.loglevel, loglevel);
++
++#ifdef ZFCP_LOW_MEM_CREDITS
++	atomic_set(&zfcp_data.lowmem_credit, 0);
++#endif
++	
++	ZFCP_LOG_DEBUG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); 
++	ZFCP_LOG_TRACE("enter\n");
++
++        ZFCP_LOG_TRACE(
++		"Start Address of module: 0x%lx\n",
++		(unsigned long) &zfcp_module_init);
++
++	/* derive driver version number from cvs revision string */
++	zfcp_data.driver_version = zfcp_derive_driver_version();
++	ZFCP_LOG_NORMAL(
++		"driver version 0x%x\n",
++		zfcp_data.driver_version);
++
++	/* initialize adapter list */
++	rwlock_init(&zfcp_data.adapter_list_lock);
++	INIT_LIST_HEAD(&zfcp_data.adapter_list_head);
++
++        /* initialize lock for callback handling */
++        rwlock_init(&zfcp_callback.lock);
++
++	/* initialize map list */
++	INIT_LIST_HEAD(&zfcp_data.map_list_head);
++
++#ifdef CONFIG_S390_SUPPORT
++	retval = register_ioctl32_conversion(
++		zfcp_cfdc_ioc.cmd, zfcp_cfdc_ioc.handler);
++	if (retval != 0) {
++		ZFCP_LOG_INFO(
++			"Cannot register a 32-bit support of the IOC handler\n");
++		goto failed_ioctl32;
++	}
++#endif
++	retval = misc_register(&zfcp_cfdc_misc);
++	if (retval != 0) {
++		ZFCP_LOG_INFO(
++			"Special file of the control file data channel "
++			"cannot be registered\n");
++		goto failed_misc_register;
++	} else {
++		ZFCP_LOG_INFO(
++			"Special file of the control file data channel "
++			"has become MAJOR/MINOR numbers %d/%d\n",
++			ZFCP_CFDC_DEV_MAJOR,
++			zfcp_cfdc_misc.minor);
++	}
++
++        /* Initialise proc semaphores */
++        sema_init(&zfcp_data.proc_sema,1);
++	down(&zfcp_data.proc_sema); /* config changes protected by proc_sema */
++
++#ifdef CONFIG_PROC_FS
++	retval = zfcp_create_root_proc();
++	if (retval) {
++		ZFCP_LOG_NORMAL(
++			"Error: Proc fs startup failed\n");
++		goto failed_root_proc;
++	}
++
++	retval = zfcp_create_data_procs();
++	if (retval) {
++                ZFCP_LOG_NORMAL(
++                        "Error: Proc fs startup failed\n");
++		goto failed_data_procs;
++        }
++#endif
++
++	/* always succeeds for now */
++	/* FIXME: set priority? */
++	zfcp_data.reboot_notifier.notifier_call = zfcp_reboot_handler;
++	register_reboot_notifier(&zfcp_data.reboot_notifier);
++
++	/*
++	 * parse module parameter string for valid configurations and create
++	 * entries for configured adapters, remote ports and logical units
++	 */
++	if (map) {
++		retval = zfcp_config_parse_record_list(
++				map,
++				strlen(map),
++				ZFCP_PARSE_ADD);
++
++		if (retval < 0)
++			goto failed_parse;	/* some entries may have been created */
++	}
++
++	/* save address of data structure managing the driver module */
++	zfcp_data.scsi_host_template.module = THIS_MODULE;
++
++	/*
++	 * register driver module with SCSI stack
++	 * we do this last to avoid the need to revert this step
++	 * if other init stuff goes wrong
++	 * (scsi_unregister_module() does not work here!)
++	 */
++        retval = scsi_register_module(
++			MODULE_SCSI_HA,
++			&zfcp_data.scsi_host_template);
++	if (retval) {
++		ZFCP_LOG_NORMAL(
++			"error: Registration of the driver module "
++			"with the Linux SCSI stack failed.\n");
++		goto failed_scsi;
++	}
++
++        /* setup dynamic I/O */
++        retval = zfcp_dio_register();
++        if (retval) {
++                ZFCP_LOG_NORMAL(
++                        "warning: Dynamic attach/detach facilities for "
++                        "the adapter(s) could not be started. \n");
++		retval = 0;
++        }
++
++	up(&zfcp_data.proc_sema); /* release procfs */
++
++#ifdef ZFCP_CAUSE_ERRORS
++	init_timer(&zfcp_force_error_timer);
++	zfcp_force_error_timer.function = zfcp_force_error;
++        zfcp_force_error_timer.data = 0;
++        zfcp_force_error_timer.expires = jiffies + 60*HZ;
++	add_timer(&zfcp_force_error_timer);
++#endif
++
++	/* we did it, skip all cleanups related to failures */
++        goto out;
++
++ failed_scsi:
++ failed_parse:
++	/* FIXME: might there be a race between module unload and shutdown? */
++	unregister_reboot_notifier(&zfcp_data.reboot_notifier);
++	zfcp_adapter_shutdown_all();
++	zfcp_config_cleanup();
++	/*
++	 * FIXME(design):
++	 * We need a way to cancel all proc usage at this point.
++	 * Just having a semaphore is not sufficient since this
++	 * semaphore makes exploiters sleep in our proc code.
++	 * If we wake them then we do not know when they actually
++	 * left the proc path. They must left the proc path before
++	 * we are allowed to delete proc entries. We need a kind of
++	 * handshaking to ensure that all proc-users are really
++	 * gone. Even if we have this then we can't ensure
++	 * that another proc-user enters the proc-path before
++	 * we delete proc-entries.
++	 */
++	zfcp_delete_data_procs();
++
++ failed_data_procs:
++	zfcp_delete_root_proc();
++        
++ failed_root_proc:
++	misc_deregister(&zfcp_cfdc_misc);
++ failed_misc_register:
++#ifdef CONFIG_S390_SUPPORT
++	unregister_ioctl32_conversion(zfcp_cfdc_ioc.cmd);
++ failed_ioctl32:
++#endif
++
++	 ZFCP_LOG_NORMAL("error: Module could not be loaded.\n");
++
++ out:
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++	return retval;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++__initcall(zfcp_module_init);
++
++/*
++ * function:	zfcp_module_exit
++ *
++ * purpose:	driver module cleanup routine
++ *
++ * locks:       zfcp_data.proc_sema is acquired prior to calling 
++ *                zfcp_config_cleanup and released afterwards
++ *
++ * returns:	void
++ */
++static void __exit zfcp_module_exit(void)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++        int temp_ret=0;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	/* FIXME: might there be a race between module unload and shutdown? */
++	unregister_reboot_notifier(&zfcp_data.reboot_notifier);
++
++	/* unregister driver module from SCSI stack */
++	scsi_unregister_module(MODULE_SCSI_HA, &zfcp_data.scsi_host_template);
++
++	/* shutdown all adapters (incl. those not registered in SCSI stack) */
++	zfcp_adapter_shutdown_all();
++
++	zfcp_delete_data_procs();
++	/* unregister from Dynamic I/O */
++	temp_ret = s390_device_unregister(&zfcp_data.devreg);
++        ZFCP_LOG_TRACE(
++		"s390_device_unregister returned %i\n", 
++		temp_ret);
++	temp_ret = s390_device_unregister(&zfcp_data.devreg_priv);
++        ZFCP_LOG_TRACE(
++		"s390_device_unregister returned %i (privileged subchannel)\n",
++		temp_ret);
++        ZFCP_LOG_TRACE("Before zfcp_config_cleanup\n");
++
++	/* free all resources dynamically allocated */
++
++	/* block proc access to config */
++        down(&zfcp_data.proc_sema);
++	temp_ret=zfcp_config_cleanup();
++        up(&zfcp_data.proc_sema);
++
++        if (temp_ret) {
++                ZFCP_LOG_NORMAL("bug: Could not free all memory "
++                               "(debug info %d)\n",
++                               temp_ret);
++	}
++
++        zfcp_delete_root_proc();
++
++	misc_deregister(&zfcp_cfdc_misc);
++#ifdef CONFIG_S390_SUPPORT
++	unregister_ioctl32_conversion(zfcp_cfdc_ioc.cmd);
++#endif
++
++#ifdef ZFCP_CAUSE_ERRORS
++        del_timer(&zfcp_force_error_timer);
++#endif	
++
++        ZFCP_LOG_TRACE("exit\n");
++	ZFCP_LOG_DEBUG("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_reboot_handler
++ *
++ * purpose:	This function is called automatically by the kernel whenever
++ *              a reboot or a shut-down is initiated and zfcp is still
++ *              loaded
++ *
++ * locks:       zfcp_data.proc_sema is taken prior to shutting down the module
++ *              and removing all structures
++ *
++ * returns:     NOTIFY_DONE in all cases
++ */
++int zfcp_reboot_handler(struct notifier_block *notifier, unsigned long code, void *ptr)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++	int retval = NOTIFY_DONE;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	/* block proc access to config (for rest of lifetime of this Linux) */
++        down(&zfcp_data.proc_sema);
++	zfcp_adapter_shutdown_all();
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_adapter_shutdown_all
++ *
++ * purpose:	recursively calls zfcp_erp_adapter_shutdown to stop all
++ *              IO on each adapter, return all outstanding packets and 
++ *              relinquish all IRQs
++ *              Note: This function waits for completion of all shutdowns
++ *
++ * returns:     0 in all cases
++ */
++static int zfcp_adapter_shutdown_all(void)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++	int retval = 0;
++	zfcp_adapter_t *adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	/*
++	 * no adapter list lock since list won't change (proc is blocked),
++	 * this allows sleeping while iterating the list
++	 */
++	ZFCP_FOR_EACH_ADAPTER(adapter)
++		zfcp_erp_adapter_shutdown(adapter, 0);
++	/* start all shutdowns first before any waiting to allow for concurreny */
++	ZFCP_FOR_EACH_ADAPTER(adapter)
++		zfcp_erp_wait(adapter);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_erp_port_shutdown_all
++ *
++ * purpose:	wrapper around zfcp_erp_port_reopen_all setting all the 
++ *              required parameters to close a port
++ *
++ * returns:     0 in all cases
++ */
++static int zfcp_erp_port_shutdown_all(zfcp_adapter_t *adapter, int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n",
++                       (unsigned long)adapter);
++
++        zfcp_erp_port_reopen_all(adapter, 
++                                 ZFCP_STATUS_COMMON_RUNNING |
++                                 ZFCP_STATUS_COMMON_ERP_FAILED |
++                                 clear_mask);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++
++/*
++ * function:  zfcp_dio_register
++ *
++ * purpose:   Registers the FCP-adapter specific device number with the common
++ *            io layer. All oper/not_oper calls will only be presented for 
++ *            devices that match the below criteria.	
++ *
++ * returns:   0 on success
++ *            -error code on failure
++ */
++static int zfcp_dio_register()
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_DIO
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_DIO
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	/* register handler for Dynamic I/O */
++        zfcp_data.devreg.ci.hc.ctype = ZFCP_CONTROL_UNIT_TYPE;
++        zfcp_data.devreg.ci.hc.cmode = ZFCP_CONTROL_UNIT_MODEL;
++        zfcp_data.devreg.ci.hc.dtype = ZFCP_DEVICE_TYPE;
++        zfcp_data.devreg.ci.hc.dmode = ZFCP_DEVICE_MODEL;
++        zfcp_data.devreg.flag = DEVREG_TYPE_DEVCHARS | DEVREG_EXACT_MATCH;
++        zfcp_data.devreg.oper_func = &zfcp_dio_oper_handler;
++
++	retval = s390_device_register(&zfcp_data.devreg);
++        if (retval < 0) {
++                ZFCP_LOG_NORMAL(
++			"bug: The FSF device type could not "
++			"be registered with the S/390 i/o layer "
++			"(debug info %d)",
++			retval);
++        }
++
++        zfcp_data.devreg_priv.ci.hc.ctype = ZFCP_CONTROL_UNIT_TYPE;
++        zfcp_data.devreg_priv.ci.hc.cmode = ZFCP_CONTROL_UNIT_MODEL;
++        zfcp_data.devreg_priv.ci.hc.dtype = ZFCP_DEVICE_TYPE;
++        zfcp_data.devreg_priv.ci.hc.dmode = ZFCP_DEVICE_MODEL_PRIV;
++        zfcp_data.devreg_priv.flag = DEVREG_TYPE_DEVCHARS | DEVREG_EXACT_MATCH;
++        zfcp_data.devreg_priv.oper_func = &zfcp_dio_oper_handler;
++
++	retval = s390_device_register(&zfcp_data.devreg_priv);
++        if (retval < 0) {
++                ZFCP_LOG_NORMAL(
++			"bug: The FSF privileged device type could not "
++			"be registered with the S/390 i/o layer "
++			"(debug info %d)",
++			retval);
++         }
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_config_cleanup
++ *
++ * purpose:	must only be called after all adapters are properly shut down
++ *              Frees all device structs (unit, port, adapter) and removes them 
++ *              from the lists
++ *
++ * returns:	0 - 	no error occured during cleanup
++ *		!0 - 	one or more errors occured during cleanup
++ *			(retval is not guaranteed to be a valid -E*)
++ *
++ * context:	called on failure of module_init and from module_exit
++ *
++ * locks:       zfcp_data.proc_sema needs to be held on function entry
++ *              adapter->port_list_lock,
++ *                port->unit_list_lock are held when walking the 
++ *                respective lists
++ */
++static int zfcp_config_cleanup(void)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_OTHER
++ 
++	int retval = 0;
++ 	zfcp_adapter_t *adapter, *tmp_adapter;
++	zfcp_port_t *port, *tmp_port;
++	zfcp_unit_t *unit, *tmp_unit;
++	unsigned long flags = 0;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        /* Note: no adapter_list_lock is needed as we have the proc_sema */ 
++	ZFCP_FOR_NEXT_ADAPTER (adapter, tmp_adapter) {
++		write_lock_irqsave(&adapter->port_list_lock, flags);
++		ZFCP_FOR_NEXT_PORT (adapter, port, tmp_port) {
++			write_lock(&port->unit_list_lock);
++			ZFCP_FOR_NEXT_UNIT (port, unit, tmp_unit){ 
++				retval |= zfcp_unit_dequeue(unit);
++                        }
++			write_unlock(&port->unit_list_lock);
++			retval |= zfcp_port_dequeue(port);
++ 		}
++		write_unlock_irqrestore(&adapter->port_list_lock, flags);
++		retval |= zfcp_adapter_dequeue(adapter);
++	}
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_derive_driver_version
++ *
++ * purpose:	generates a 32 bit value from the cvs mantained revision,
++ *
++ * returns:	!0 - driver version
++ *			format:	0 .. 7		8 .. 15		16 .. 31
++ *				(reserved)	major	.	minor
++ *		0 - if no version string could be assembled
++ */
++static u32 zfcp_derive_driver_version(void)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++	char *revision = ZFCP_REVISION;
++	u32 version = 0;
++	char *d;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	/* major */
++	for (d = revision; !isdigit(d[0]); d++) {}
++	version |= (simple_strtoul(d, &d, 10) & 0xFF) << 16; 
++
++	/* dot */
++	if (d[0] != '.') {
++		ZFCP_LOG_NORMAL(
++			"bug: Revision number generation from string "
++                        "unsuccesfull. Setting revision number to 0 and "
++                        "continuing (debug info %s).\n",
++			revision);
++		version = 0;
++		goto out;
++	}
++	d++;
++
++	/* minor */
++	version |= simple_strtoul(d, NULL, 10) & 0xFFFF;
++
++out:
++	ZFCP_LOG_TRACE("exit (0x%x)\n", version);
++
++	return version;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_buffers_enqueue
++ *
++ * purpose:	allocates BUFFER memory to each of the pointers of
++ *              the qdio_buffer_t array in the adapter struct
++ *
++ * returns:	number of buffers allocated
++ *
++ * comments:    cur_buf is the pointer array and count can be any
++ *              number of required buffers, the page-fitting arithmetic is
++ *              done entirely within this funciton
++ *
++ * locks:       must only be called with zfcp_data.proc_sema taken
++ */
++int zfcp_buffers_enqueue(qdio_buffer_t **cur_buf, int count)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++        int buf_pos;
++        int qdio_buffers_per_page;
++        int page_pos = 0;
++        qdio_buffer_t *first_in_page = NULL;
++        
++	ZFCP_LOG_TRACE(
++		"enter cur_buf 0x%lx\n",
++                (unsigned long)cur_buf);
++
++        qdio_buffers_per_page = PAGE_SIZE / sizeof(qdio_buffer_t);
++        ZFCP_LOG_TRACE(
++		"Buffers per page %d.\n",
++                qdio_buffers_per_page);
++
++        for (buf_pos = 0; buf_pos < count; buf_pos++) {
++                if (page_pos == 0) {
++                        cur_buf[buf_pos] = (qdio_buffer_t*) ZFCP_GET_ZEROED_PAGE(GFP_KERNEL);
++                        if (cur_buf[buf_pos] == NULL) {
++                                ZFCP_LOG_INFO(
++					"error: Could not allocate "
++                                        "memory for qdio transfer structures.\n");
++                                goto out;
++                        }
++                        first_in_page = cur_buf[buf_pos];
++                } else {
++                        cur_buf[buf_pos] = first_in_page + page_pos;
++
++                }
++                /* was initialised to zero */
++                page_pos++;
++                page_pos %= qdio_buffers_per_page;
++        } // for (buf_pos = 0; buf_pos < count; buf_pos++)
++ out:
++	ZFCP_LOG_TRACE("exit (%d)\n", buf_pos);
++        return buf_pos; 
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_buffers_dequeue
++ *
++ * purpose:	frees BUFFER memory for each of the pointers of
++ *              the qdio_buffer_t array in the adapter struct
++ *
++ * returns:	sod all
++ *
++ * comments:    cur_buf is the pointer array and count can be any
++ *              number of buffers in the array that should be freed
++ *              starting from buffer 0
++ *
++ * locks:       must only be called with zfcp_data.proc_sema taken
++ */
++void zfcp_buffers_dequeue(qdio_buffer_t **cur_buf, int count)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++        int buf_pos;
++        int qdio_buffers_per_page;
++
++	ZFCP_LOG_TRACE("enter cur_buf 0x%lx count %d\n",
++                       (unsigned long)cur_buf,
++                       count);
++        
++        qdio_buffers_per_page = PAGE_SIZE / sizeof(qdio_buffer_t);
++        ZFCP_LOG_TRACE(
++		"Buffers per page %d.\n",
++                qdio_buffers_per_page);
++
++        for (buf_pos = 0; buf_pos < count; buf_pos += qdio_buffers_per_page) {
++                ZFCP_FREE_PAGE((unsigned long)cur_buf[buf_pos]);
++        }
++
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_allocate_qdio_queues
++ *
++ * purpose:	wrapper around zfcp_buffers_enqueue with possible calls
++ *              to zfcp_buffers_dequeue in the error case. Deals with
++ *              request and response queues
++ *
++ * returns:	0 on success
++ *              -EIO if allocation of buffers failed
++ *               (all buffers are guarranteed to be un-allocated in this case)
++ *
++ * comments:    called only from adapter_enqueue
++ *
++ * locks:       must only be called with zfcp_data.proc_sema taken
++ */
++int zfcp_allocate_qdio_queues(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++        int buffer_count;
++        int retval=0;
++
++        ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n",
++                       (unsigned long)adapter);
++
++        buffer_count = zfcp_buffers_enqueue(
++                            &(adapter->request_queue.buffer[0]),
++                            QDIO_MAX_BUFFERS_PER_Q);
++        if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) {
++                ZFCP_LOG_DEBUG("error: Out of memory allocating "
++                               "request queue, only %d buffers got. "
++                               "Binning them.\n",
++                                buffer_count);
++                zfcp_buffers_dequeue(
++                     &(adapter->request_queue.buffer[0]),
++                     buffer_count);
++                retval = -ENOMEM;
++                goto out;
++        }
++
++        buffer_count = zfcp_buffers_enqueue(
++                            &(adapter->response_queue.buffer[0]),
++                            QDIO_MAX_BUFFERS_PER_Q);
++        if (buffer_count < QDIO_MAX_BUFFERS_PER_Q) {
++                ZFCP_LOG_DEBUG("error: Out of memory allocating "
++                               "response queue, only %d buffers got. "
++                               "Binning them.\n",
++                               buffer_count);
++                zfcp_buffers_dequeue(
++                     &(adapter->response_queue.buffer[0]),
++                     buffer_count);
++                ZFCP_LOG_TRACE("Deallocating request_queue Buffers.\n");
++                zfcp_buffers_dequeue(
++                     &(adapter->request_queue.buffer[0]),
++                     QDIO_MAX_BUFFERS_PER_Q);
++                retval = -ENOMEM;
++                goto out;
++        }
++ out:
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_free_qdio_queues
++ *
++ * purpose:	wrapper around zfcp_buffers_dequeue for request and response
++ *              queues
++ *
++ * returns:	sod all
++ *
++ * comments:    called only from adapter_dequeue
++ *
++ * locks:       must only be called with zfcp_data.proc_sema taken
++ */
++void zfcp_free_qdio_queues(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n", 
++                       (unsigned long)adapter);
++        
++        ZFCP_LOG_TRACE("Deallocating request_queue Buffers.\n");
++        zfcp_buffers_dequeue(
++             &(adapter->request_queue.buffer[0]),
++             QDIO_MAX_BUFFERS_PER_Q);
++
++        ZFCP_LOG_TRACE("Deallocating response_queue Buffers.\n");
++        zfcp_buffers_dequeue(
++             &(adapter->response_queue.buffer[0]),
++             QDIO_MAX_BUFFERS_PER_Q);
++
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_free_low_mem_buffers
++ *
++ * purpose:	frees all static memory in the pools previously allocated by
++ *              zfcp_allocate_low_mem buffers
++ *
++ * returns:     sod all
++ *
++ * locks:       must only be called with zfcp_data.proc_sema taken
++ */
++static void zfcp_free_low_mem_buffers(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	ZFCP_LOG_TRACE("enter (adapter 0x%lx)\n",
++                       (unsigned long)adapter);
++
++	zfcp_mem_pool_destroy(&adapter->pool.fsf_req_status_read);
++	zfcp_mem_pool_destroy(&adapter->pool.data_status_read);
++	zfcp_mem_pool_destroy(&adapter->pool.data_gid_pn);
++	zfcp_mem_pool_destroy(&adapter->pool.fsf_req_erp);
++	zfcp_mem_pool_destroy(&adapter->pool.fsf_req_scsi);
++        
++        ZFCP_LOG_TRACE("exit\n");
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_allocate_low_mem_buffers
++ *
++ * purpose:	The pivot for the static memory buffer pool generation.
++ *              Called only from zfcp_adapter_enqueue in order to allocate
++ *              a combined QTCB/fsf_req buffer for erp actions and fcp/SCSI
++ *              commands.
++ *              It also genrates fcp-nameserver request/response buffer pairs
++ *              and unsolicited status read fsf_req buffers by means of 
++ *              function calls to the apropriate handlers.
++ *
++ * returns:     0 on success
++ *              -ENOMEM on failure (some buffers might be allocated)
++ *
++ * locks:       must only be called with zfcp_data.proc_sema taken
++ */
++static int zfcp_allocate_low_mem_buffers(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter (adapter 0x%lx)\n",
++                       (unsigned long)adapter);
++
++	retval = zfcp_mem_pool_create(&adapter->pool.fsf_req_erp, 1,
++                                      sizeof(struct zfcp_fsf_req_pool_buffer),
++                                      0, 0);
++	if (retval) {
++		ZFCP_LOG_INFO(
++			"error: FCP command buffer pool allocation failed\n");
++		goto out;
++	}
++
++	retval = zfcp_mem_pool_create(&adapter->pool.data_gid_pn, 1,
++                                      sizeof(struct zfcp_gid_pn_data), 0, 0);
++
++	if (retval) {
++		ZFCP_LOG_INFO(
++			"error: Nameserver buffer pool allocation failed\n");
++		goto out;
++	}
++
++	retval = zfcp_mem_pool_create(&adapter->pool.fsf_req_status_read,
++                                      ZFCP_STATUS_READS_RECOM,
++                                      sizeof(zfcp_fsf_req_t), 0, 0);
++	if (retval) {
++		ZFCP_LOG_INFO(
++			"error: Status read request pool allocation failed\n");
++		goto out;
++	}
++
++	retval = zfcp_mem_pool_create(&adapter->pool.data_status_read,
++                                      ZFCP_STATUS_READS_RECOM,
++                                      sizeof(fsf_status_read_buffer_t), 0, 0);
++	if (retval) {
++		ZFCP_LOG_INFO(
++			"error: Status read buffer pool allocation failed\n");
++		goto out;
++	}
++
++	retval = zfcp_mem_pool_create(&adapter->pool.fsf_req_scsi,
++                                      1, sizeof(struct zfcp_fsf_req_pool_buffer),
++                                      zfcp_scsi_low_mem_buffer_timeout_handler,
++                                      (unsigned long) adapter);
++	if (retval) {
++		ZFCP_LOG_INFO(
++			"error: FCP command buffer pool allocation failed\n");
++		goto out;
++	}
++
++out:
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_adapter_enqueue
++ *
++ * purpose:	enqueues an adapter at the end of the adapter list
++ *		in the driver data
++ *              all adapter internal structures are set up
++ *              proc-fs entries are also created
++ *
++ * returns:	0 if a new adapter was successfully enqueued
++ *              ZFCP_KNOWN if an adapter with this devno was already present
++ *		-ENOMEM if alloc failed
++ *	   
++ * locks:	proc_sema must be held to serialise chnages to the adapter list
++ *              zfcp_data.adapter_list_lock is taken and released several times
++ *              within the function (must not be held on entry)
++ */
++static int zfcp_adapter_enqueue(devno_t devno, zfcp_adapter_t **adapter_p)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	int retval = 0;
++        zfcp_adapter_t *adapter;
++	unsigned long flags;
++        char dbf_name[20];
++        
++        ZFCP_LOG_TRACE(
++		"enter (devno=0x%04x ,adapter_p=0x%lx)\n", 
++		devno,
++		(unsigned long)adapter_p);
++
++	read_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
++	ZFCP_FOR_EACH_ADAPTER(adapter) {
++		if (adapter->devno == devno) {
++			ZFCP_LOG_TRACE(
++				"Adapter with devno 0x%04x "
++				"already exists.\n",
++				 devno);
++                        retval = ZFCP_KNOWN;
++                        break;
++		}
++        }
++        read_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
++        if (retval == ZFCP_KNOWN)
++                goto known_adapter;
++
++        /*
++	 * Note: It is safe to release the list_lock, as any list changes 
++         * are protected by the proc_sema, which must be held to get here
++         */
++
++	/* try to allocate new adapter data structure (zeroed) */
++	adapter = ZFCP_KMALLOC(sizeof(zfcp_adapter_t), GFP_KERNEL);
++	if (!adapter) {
++		ZFCP_LOG_INFO(
++			"error: Allocation of base adapter "
++			"structure failed\n");
++                retval = -ENOMEM;
++                goto adapter_alloc_failed;
++	}
++
++	retval = zfcp_allocate_qdio_queues(adapter);        
++        if (retval)
++                goto queues_alloc_failed;
++ 
++        retval = zfcp_allocate_low_mem_buffers(adapter);
++        if (retval)
++                goto failed_low_mem_buffers;
++
++        /* initialise list of ports */
++        rwlock_init(&adapter->port_list_lock);
++        INIT_LIST_HEAD(&adapter->port_list_head);
++        
++        /* initialize list of fsf requests */
++        rwlock_init(&adapter->fsf_req_list_lock);
++	INIT_LIST_HEAD(&adapter->fsf_req_list_head);
++        
++        /* initialize abort lock */
++        rwlock_init(&adapter->abort_lock);
++
++        /* initialise scsi faking structures */
++        rwlock_init(&adapter->fake_list_lock);
++        init_timer(&adapter->fake_scsi_timer);
++
++	/* initialise some erp stuff */
++        init_waitqueue_head(&adapter->erp_thread_wqh);
++        init_waitqueue_head(&adapter->erp_done_wqh);
++        
++        /* initialize lock of associated request queue */
++        rwlock_init(&adapter->request_queue.queue_lock);
++
++        /* intitialise SCSI ER timer */
++	init_timer(&adapter->scsi_er_timer);
++
++        /* save devno */
++        adapter->devno = devno;
++        
++        /* set FC service class used per default */
++        adapter->fc_service_class = ZFCP_FC_SERVICE_CLASS_DEFAULT; 
++
++#ifdef ZFCP_DEBUG_REQUESTS
++	/* debug feature area which records fsf request sequence numbers */
++	sprintf(dbf_name, ZFCP_REQ_DBF_NAME"0x%04x",adapter->devno);
++	adapter->req_dbf = debug_register(
++				dbf_name,
++				ZFCP_REQ_DBF_INDEX,
++				ZFCP_REQ_DBF_AREAS,
++				ZFCP_REQ_DBF_LENGTH);
++	if (!adapter->req_dbf) {
++		ZFCP_LOG_INFO(
++			"error: Out of resources. Request debug feature for "
++			"adapter with devno 0x%04x could not be generated.\n",
++			adapter->devno);
++		retval = -ENOMEM;
++		goto failed_req_dbf;
++	}
++	debug_register_view(adapter->req_dbf, &debug_hex_ascii_view);
++	debug_set_level(adapter->req_dbf, ZFCP_REQ_DBF_LEVEL);
++	debug_text_event(adapter->req_dbf, 1, "zzz");
++#endif /* ZFCP_DEBUG_REQUESTS */
++
++#ifdef ZFCP_DEBUG_COMMANDS
++	/* debug feature area which records SCSI command failures (hostbyte) */
++	rwlock_init(&adapter->cmd_dbf_lock);
++	sprintf(dbf_name, ZFCP_CMD_DBF_NAME"0x%04x", adapter->devno);
++	adapter->cmd_dbf = debug_register(
++				dbf_name, 
++				ZFCP_CMD_DBF_INDEX,
++				ZFCP_CMD_DBF_AREAS,
++				ZFCP_CMD_DBF_LENGTH);
++	if (!adapter->cmd_dbf) {
++		ZFCP_LOG_INFO(
++			"error: Out of resources. Command debug feature for "
++			"adapter with devno 0x%04x could not be generated.\n",
++			adapter->devno);
++		retval = -ENOMEM;
++		goto failed_cmd_dbf;
++	}
++	debug_register_view(adapter->cmd_dbf, &debug_hex_ascii_view);
++	debug_set_level(adapter->cmd_dbf, ZFCP_CMD_DBF_LEVEL);
++#endif /* ZFCP_DEBUG_COMMANDS */
++
++#ifdef ZFCP_DEBUG_ABORTS
++	/* debug feature area which records SCSI command aborts */
++	sprintf(dbf_name, ZFCP_ABORT_DBF_NAME"0x%04x", adapter->devno);
++	adapter->abort_dbf = debug_register(
++				dbf_name, 
++				ZFCP_ABORT_DBF_INDEX,
++				ZFCP_ABORT_DBF_AREAS,
++				ZFCP_ABORT_DBF_LENGTH);
++	if (!adapter->abort_dbf) {
++		ZFCP_LOG_INFO(
++			"error: Out of resources. Abort debug feature for "
++			"adapter with devno 0x%04x could not be generated.\n",
++			adapter->devno);
++		retval = -ENOMEM;
++		goto failed_abort_dbf;
++	}
++	debug_register_view(adapter->abort_dbf, &debug_hex_ascii_view);
++	debug_set_level(adapter->abort_dbf, ZFCP_ABORT_DBF_LEVEL);
++#endif /* ZFCP_DEBUG_ABORTS */
++
++#ifdef ZFCP_DEBUG_INCOMING_ELS
++	/* debug feature area which records SCSI command aborts */
++	sprintf(dbf_name, ZFCP_IN_ELS_DBF_NAME"0x%04x", adapter->devno);
++	adapter->in_els_dbf = debug_register(
++				dbf_name,
++				ZFCP_IN_ELS_DBF_INDEX,
++				ZFCP_IN_ELS_DBF_AREAS,
++				ZFCP_IN_ELS_DBF_LENGTH);
++	if (!adapter->in_els_dbf) {
++		ZFCP_LOG_INFO(
++			"error: Out of resources. ELS debug feature for "
++			"adapter with devno 0x%04x could not be generated.\n",
++			adapter->devno);
++		retval = -ENOMEM;
++		goto failed_in_els_dbf;
++	}
++	debug_register_view(adapter->in_els_dbf, &debug_hex_ascii_view);
++	debug_set_level(adapter->in_els_dbf, ZFCP_IN_ELS_DBF_LEVEL);
++#endif /* ZFCP_DEBUG_INCOMING_ELS */
++
++	sprintf(dbf_name, ZFCP_ERP_DBF_NAME"0x%04x", adapter->devno);
++	adapter->erp_dbf = debug_register(
++				dbf_name, 
++				ZFCP_ERP_DBF_INDEX, 
++				ZFCP_ERP_DBF_AREAS,
++				ZFCP_ERP_DBF_LENGTH);
++	if (!adapter->erp_dbf) { 
++		ZFCP_LOG_INFO(
++			"error: Out of resources. ERP debug feature for "
++			"adapter with devno 0x%04x could not be generated.\n",
++			adapter->devno);
++		retval = -ENOMEM;
++		goto failed_erp_dbf;
++	}
++	debug_register_view(adapter->erp_dbf, &debug_hex_ascii_view);
++	debug_set_level(adapter->erp_dbf, ZFCP_ERP_DBF_LEVEL);
++
++        /* Init proc structures */
++#ifdef CONFIG_PROC_FS
++        ZFCP_LOG_TRACE("Generating proc entry....\n");
++	retval = zfcp_create_adapter_proc(adapter);
++        if (retval) {
++                ZFCP_LOG_INFO(
++			"error: Out of resources. "
++			"proc-file entries for adapter with "
++			"devno 0x%04x could not be generated\n",
++			adapter->devno);
++                goto proc_failed;
++        }
++        ZFCP_LOG_TRACE("Proc entry created.\n");
++#endif
++
++	retval = zfcp_erp_thread_setup(adapter);
++        if (retval) {
++                ZFCP_LOG_INFO(
++			"error: out of resources. "
++			"error recovery thread for the adapter with "
++			"devno 0x%04x could not be started\n",
++			adapter->devno);
++                goto thread_failed;
++        }
++
++#ifndef ZFCP_PARANOIA_DEAD_CODE
++        /* set magics */
++        adapter->common_magic = ZFCP_MAGIC;
++        adapter->specific_magic = ZFCP_MAGIC_ADAPTER;
++#endif
++
++#ifdef ZFCP_STAT_REQSIZES
++	rwlock_init(&adapter->stat_lock);
++	atomic_set(&adapter->stat_on, 0);
++	atomic_set(&adapter->stat_errors, 0);
++	INIT_LIST_HEAD(&adapter->read_req_head);
++	INIT_LIST_HEAD(&adapter->write_req_head);
++#endif
++
++        /* put allocated adapter at list tail */
++	write_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
++        list_add_tail(&adapter->list, &zfcp_data.adapter_list_head);
++	zfcp_data.adapters++;
++	write_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
++
++	sprintf(adapter->name, "0x%04x", adapter->devno);
++	ASCEBC(adapter->name, strlen(adapter->name));
++
++	atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &adapter->status);
++
++	ZFCP_LOG_TRACE(
++		"adapter allocated at 0x%lx, %i adapters in list\n",
++		(unsigned long)adapter,
++		zfcp_data.adapters);
++        goto out;
++
++thread_failed:
++	zfcp_delete_adapter_proc(adapter);
++
++proc_failed:
++	debug_unregister(adapter->erp_dbf);
++
++failed_erp_dbf:
++#ifdef ZFCP_DEBUG_INCOMING_ELS
++	debug_unregister(adapter->in_els_dbf);
++failed_in_els_dbf:
++#endif
++
++#ifdef ZFCP_DEBUG_ABORTS
++	debug_unregister(adapter->abort_dbf);
++failed_abort_dbf:
++#endif
++
++#ifdef ZFCP_DEBUG_COMMANDS
++	debug_unregister(adapter->cmd_dbf);
++failed_cmd_dbf:
++#endif
++
++#ifdef ZFCP_DEBUG_REQUESTS
++        debug_unregister(adapter->req_dbf);
++failed_req_dbf:
++#endif
++
++failed_low_mem_buffers:
++        zfcp_free_low_mem_buffers(adapter);
++        zfcp_free_qdio_queues(adapter);
++
++queues_alloc_failed:
++        ZFCP_LOG_TRACE(
++		"freeing adapter struct 0x%lx\n",
++		(unsigned long) adapter);
++	/* 'typeof' works as well */
++        ZFCP_KFREE(adapter, sizeof(typeof(adapter)));
++
++adapter_alloc_failed:
++	adapter = NULL;
++
++known_adapter:
++out:
++	*adapter_p = adapter;
++        ZFCP_LOG_TRACE("exit (%d)\n", retval);
++        
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_adapter_dequeue
++ *
++ * purpose:	dequeues the specified adapter from the list in the driver data
++ *
++ * returns:	0 - zfcp_adapter_t data structure successfully removed
++ *		!0 - zfcp_adapter_t data structure could not be removed
++ *			(e.g. still used)
++ *
++ * locks:	adapter list write lock is assumed to be held by caller
++ *              adapter->fsf_req_list_lock is taken and released within this 
++ *              function and must not be held on entry
++ */
++static int zfcp_adapter_dequeue(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	int retval = 0;
++	unsigned long flags;
++
++	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n", (unsigned long)adapter);
++
++	/*
++	 * sanity check:
++	 * I/O interrupt should be disabled, leave if not
++	 */
++
++        /* Note: no adapter_list_lock is needed as we have the proc_sema */ 
++
++	/* sanity check: valid adapter data structure address */
++	if (!adapter) {
++		ZFCP_LOG_NORMAL(
++			"bug: Pointer to an adapter struct is a null "
++                        "pointer\n");
++		retval = -EINVAL;
++		goto out;
++	}
++
++	/* sanity check: no remote ports pending */
++	if (adapter->ports) {
++		ZFCP_LOG_NORMAL(
++			"bug: Adapter with devno 0x%04x is still in use, "
++			"%i remote ports are still existing "
++                        "(debug info 0x%lx)\n",
++			adapter->devno, 
++                        adapter->ports,
++                        (unsigned long)adapter);
++		retval = -EBUSY;
++		goto out;
++	}
++
++	/* sanity check: no pending FSF requests */
++	read_lock_irqsave(&adapter->fsf_req_list_lock, flags);
++
++	retval = !list_empty(&adapter->fsf_req_list_head);
++
++	read_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
++	if (retval) {
++		ZFCP_LOG_NORMAL(
++			"bug: Adapter with devno 0x%04x is still in use, "
++			"%i requests are still outstanding "
++                        "(debug info 0x%lx)\n",
++			adapter->devno, 
++                        atomic_read(&adapter->fsf_reqs_active),
++                        (unsigned long)adapter);
++		retval = -EBUSY;
++		goto out;
++	}
++
++	/* remove specified adapter data structure from list */
++	list_del(&adapter->list);
++
++	/* decrease number of adapters in list */
++	zfcp_data.adapters--;
++
++	ZFCP_LOG_TRACE(
++		"adapter 0x%lx removed from list, "
++		"%i adapters still in list\n",
++		(unsigned long)adapter,
++		zfcp_data.adapters);
++
++        retval = zfcp_erp_thread_kill(adapter);
++
++#ifdef ZFCP_STAT_REQSIZES
++	zfcp_statistics_clear(adapter, &adapter->read_req_head);
++	zfcp_statistics_clear(adapter, &adapter->write_req_head);
++#endif
++
++        zfcp_delete_adapter_proc(adapter);
++        ZFCP_LOG_TRACE("Proc entry removed.\n");
++
++        debug_unregister(adapter->erp_dbf);
++
++#ifdef ZFCP_DEBUG_REQUESTS
++        debug_unregister(adapter->req_dbf);
++#endif
++
++#ifdef ZFCP_DEBUG_COMMANDS
++	debug_unregister(adapter->cmd_dbf);
++#endif
++
++#ifdef ZFCP_DEBUG_ABORTS
++	debug_unregister(adapter->abort_dbf);
++#endif
++
++#ifdef ZFCP_DEBUG_INCOMING_ELS
++	debug_unregister(adapter->in_els_dbf);
++#endif
++
++
++        zfcp_free_low_mem_buffers(adapter);
++	/* free memory of adapter data structure and queues */
++        zfcp_free_qdio_queues(adapter);
++        ZFCP_LOG_TRACE("Freeing adapter structure.\n");
++        ZFCP_KFREE(adapter, sizeof(zfcp_adapter_t));
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;	/* succeed */
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_port_enqueue
++ *
++ * purpose:	enqueues an remote port at the end of the port list
++ *		associated with the specified adapter
++ *              all port internal structures are set-up and the proc-fs
++ *              entry is also allocated
++ *              some SCSI-stack structures are modified for the port
++ *
++ * returns:	0 if a new port was successfully enqueued
++ *              ZFCP_KNOWN if a port with the requested wwpn already exists
++ *              -ENOMEM if allocation failed
++ *              -EINVAL if at least one of the specified parameters was wrong
++ *
++ * locks:       proc_sema must be held to serialise changes to the port list
++ *              adapter->port_list_lock is taken and released several times
++ *              within this function (must not be held on entry)
++ */
++static int 
++	zfcp_port_enqueue(
++		zfcp_adapter_t *adapter,
++		scsi_id_t scsi_id,
++		wwn_t wwpn,
++		u32 status,
++                zfcp_port_t **port_p)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	zfcp_port_t *port = NULL;
++	int check_scsi_id, check_wwpn;
++        unsigned long flags;
++        int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx scsi_id=%i wwpn=0x%Lx status=0x%x)\n",
++		(unsigned long)adapter,
++		scsi_id,
++		(llui_t)wwpn,
++		status);
++
++        /* to check that there is not a port with either this 
++	 * SCSI ID or WWPN already in list
++	 */
++	check_scsi_id = !(status & ZFCP_STATUS_PORT_NO_SCSI_ID);
++	check_wwpn = !(status & ZFCP_STATUS_PORT_NO_WWPN);
++
++	if (check_scsi_id && check_wwpn) {
++		read_lock_irqsave(&adapter->port_list_lock, flags);
++		ZFCP_FOR_EACH_PORT(adapter, port) {
++			if ((port->scsi_id != scsi_id) && (port->wwpn != wwpn))
++				continue;
++			if ((port->scsi_id == scsi_id) && (port->wwpn == wwpn)) {
++				ZFCP_LOG_TRACE(
++					"Port with SCSI ID 0x%x and WWPN 0x%016Lx already in list\n",
++					scsi_id, (llui_t)wwpn);
++				retval = ZFCP_KNOWN;
++				read_unlock_irqrestore(&adapter->port_list_lock, flags);
++				goto known_port;
++			}
++			ZFCP_LOG_NORMAL(
++				"user error: new mapping 0x%x:0x%016Lx "
++				"does not match existing mapping 0x%x:0x%016Lx "
++				"(adapter devno 0x%04x)\n",
++				scsi_id,
++				(llui_t)wwpn,
++				port->scsi_id,
++				(llui_t)port->wwpn,
++				port->adapter->devno);
++			retval = -EINVAL;
++			read_unlock_irqrestore(&adapter->port_list_lock, flags);
++			goto match_failed;
++		}
++		read_unlock_irqrestore(&adapter->port_list_lock, flags);
++	}
++
++        /*
++	 * Note: It is safe to release the list_lock, as any list changes 
++         * are protected by the proc_sema, which must be held to get here
++         */
++
++	/* try to allocate new port data structure (zeroed) */
++	port = ZFCP_KMALLOC(sizeof(zfcp_port_t), GFP_KERNEL);
++	if (!port) {
++		ZFCP_LOG_INFO(
++			"error: Allocation of port struct failed. "
++			"Out of memory.\n");
++                retval = -ENOMEM;
++                goto port_alloc_failed;
++        }
++
++        /* initialize unit list */
++        rwlock_init(&port->unit_list_lock);
++        INIT_LIST_HEAD(&port->unit_list_head);
++
++        /* save pointer to "parent" adapter */
++        port->adapter = adapter;
++
++        /* save SCSI ID */
++	if (check_scsi_id)
++		port->scsi_id = scsi_id;
++
++        /* save WWPN */
++	if (check_wwpn)
++		port->wwpn = wwpn;
++
++        /* save initial status */
++        atomic_set_mask(status, &port->status);
++ 
++#ifndef ZFCP_PARANOIA_DEAD_CODE
++        /* set magics */
++        port->common_magic = ZFCP_MAGIC;
++        port->specific_magic = ZFCP_MAGIC_PORT;
++#endif
++
++	/* Init proc structures */
++#ifdef CONFIG_PROC_FS
++        ZFCP_LOG_TRACE("Generating proc entry....\n");
++	retval = zfcp_create_port_proc(port);
++        if (retval)
++                goto proc_failed;
++        ZFCP_LOG_TRACE("Proc entry created.\n");
++#endif
++      
++        if (check_scsi_id) {
++	        /*
++        	 * update max. SCSI ID of remote ports attached to
++	         * "parent" adapter if necessary
++        	 * (do not care about the adapters own SCSI ID)
++	         */
++		if (adapter->max_scsi_id < scsi_id) {
++			adapter->max_scsi_id = scsi_id;
++			ZFCP_LOG_TRACE(
++				"max. SCSI ID of adapter 0x%lx now %i\n",
++				(unsigned long)adapter,
++				scsi_id);
++		}
++		/*
++		 * update max. SCSI ID of remote ports attached to
++		 * "parent" host (SCSI stack) if necessary
++		 */
++		if (adapter->scsi_host &&
++		    (adapter->scsi_host->max_id < (scsi_id + 1))) {
++			adapter->scsi_host->max_id = scsi_id + 1;
++			ZFCP_LOG_TRACE(
++				"max. SCSI ID of ports attached "
++				"via host # %d now %i\n",
++				adapter->scsi_host->host_no,
++				adapter->scsi_host->max_id);
++        	}
++        }
++
++        /* Port is allocated, enqueue it*/
++        write_lock_irqsave(&adapter->port_list_lock,flags);
++        list_add_tail(&port->list, &adapter->port_list_head);
++	adapter->ports++;
++        write_unlock_irqrestore(&adapter->port_list_lock,flags);
++        
++	atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status);
++
++	/* ignore nameserver port */
++	if (port->wwpn != 0)
++		zfcp_callback_do_port_add(NULL, adapter, port);
++
++        ZFCP_LOG_TRACE(
++		"port allocated at 0x%lx, %i ports in list "
++		"of adapter 0x%lx\n",
++		(unsigned long)port,
++		adapter->ports,
++		(unsigned long)adapter);
++        goto out;
++
++proc_failed:
++        ZFCP_KFREE(port, sizeof(zfcp_port_t));
++        ZFCP_LOG_TRACE(
++		"freeing port struct 0x%lx\n",
++		(unsigned long) port);
++
++port_alloc_failed:
++match_failed:
++	port = NULL;
++
++known_port:
++out:
++	*port_p = port;
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_port_dequeue
++ *
++ * purpose:	dequeues the specified port from the list of the
++ *		 "parent" adapter
++ *
++ * returns:	0 - zfcp_port_t data structure successfully removed
++ *		!0 - zfcp_port_t data structure could not be removed
++ *			(e.g. still used)
++ *
++ * locks :	port list write lock is assumed to be held by caller
++ */
++static int zfcp_port_dequeue(zfcp_port_t *port)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter (port=0x%lx)\n", (unsigned long)port);
++
++	/* sanity check: valid port data structure address (simple check) */
++	if (!port) {
++		ZFCP_LOG_NORMAL(
++			"bug: Pointer to a port struct is a null "
++                        "pointer\n");
++		retval = -EINVAL;
++		goto out;
++	}
++
++	/*
++	 * sanity check:
++	 * leave if required list lock is not held,
++	 * do not know whether it is held by the calling routine (required!)
++	 * protecting this critical area or someone else (must not occur!),
++	 * but a lock not held by anyone is definetely wrong
++	 */
++	if (!spin_is_locked(&port->adapter->port_list_lock)) {
++		ZFCP_LOG_NORMAL("bug: Port list lock not held "
++                               "(debug info 0x%lx)\n",
++                               (unsigned long) port);
++		retval = -EPERM;
++		goto out;
++	}
++
++	/* sanity check: no logical units pending */
++	if (port->units) {
++		ZFCP_LOG_NORMAL(
++			"bug: Port with SCSI-id 0x%x is still in use, "
++			"%i units (LUNs) are still existing "
++                        "(debug info 0x%lx)\n",
++			port->scsi_id, 
++                        port->units,
++                        (unsigned long)port);
++		retval = -EBUSY;
++		goto out;
++	}
++
++	/* remove specified port data structure from list */
++	list_del(&port->list);
++
++	/* decrease number of ports in list */
++	port->adapter->ports--;
++
++	ZFCP_LOG_TRACE(
++		"port 0x%lx removed from list of adapter 0x%lx, "
++		"%i ports still in list\n",
++		(unsigned long)port,
++		(unsigned long)port->adapter,
++		port->adapter->ports);
++
++	/* free memory of port data structure */
++        ZFCP_LOG_TRACE("Deleting proc entry......\n");
++        zfcp_delete_port_proc(port);
++        ZFCP_LOG_TRACE("Proc entry removed.\n");
++	ZFCP_KFREE(port, sizeof(zfcp_port_t));
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;	/* succeed */
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:     zfcp_scsi_low_mem_buffer_timeout_handler
++ *
++ * purpose:      This function needs to be called whenever the SCSI command
++ *               in the low memory buffer does not return.
++ *               Re-opening the adapter means that the command can be returned
++ *               by zfcp (it is guarranteed that it does not return via the
++ *               adapter anymore). The buffer can then be used again.
++ *    
++ * returns:      sod all
++ */
++static void zfcp_scsi_low_mem_buffer_timeout_handler(unsigned long data)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	zfcp_adapter_t *adapter = (zfcp_adapter_t *)data   ;
++
++	ZFCP_LOG_TRACE("enter (data=0x%lx)\n", 
++                       (unsigned long) data);
++        /*DEBUG*/
++        ZFCP_LOG_INFO("*****************************mem_timeout******************************\n");
++        zfcp_erp_adapter_reopen(adapter, 0);
++	ZFCP_LOG_TRACE("exit\n");
++
++	return;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_fsf_scsi_er_timeout_handler
++ *
++ * purpose:     This function needs to be called whenever a SCSI error recovery
++ *              action (abort/reset) does not return.
++ *              Re-opening the adapter means that the command can be returned
++ *              by zfcp (it is guarranteed that it does not return via the
++ *              adapter anymore). The buffer can then be used again.
++ *    
++ * returns:     sod all
++ */
++static void zfcp_fsf_scsi_er_timeout_handler(unsigned long data)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	zfcp_adapter_t *adapter = (zfcp_adapter_t *)data;
++
++	ZFCP_LOG_TRACE("enter (data=0x%lx)\n", 
++                       (unsigned long) data);
++        /*DEBUG*/
++        ZFCP_LOG_INFO("*****************************er_timeout******************************\n");
++        zfcp_erp_adapter_reopen(adapter, 0);
++	ZFCP_LOG_TRACE("exit\n");
++
++	return;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * memory pool implementation
++ * the first four functions (element_alloc, element_release, element_get, element_put)
++ * are for internal use,
++ * the other four functions (create, destroy, find, free) are the external interface
++ * which should be used by exploiter of the memory pool
++ */
++
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++/* associate a buffer with the specified memory pool element */
++static inline int zfcp_mem_pool_element_alloc(
++		zfcp_mem_pool_t *pool,
++		int index)
++{
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (pool=0x%lx, index=%i)\n",
++		(unsigned long)pool,
++		index);
++
++	pool->element[index].buffer = ZFCP_KMALLOC(pool->size, GFP_KERNEL);
++	if (!pool->element[index].buffer) {
++		retval = -ENOMEM;
++        };
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++}
++
++
++/* release the buffer associated with the specified memory pool element */
++static inline int zfcp_mem_pool_element_free(
++		zfcp_mem_pool_t *pool,
++		int index)
++{
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (pool=0x%lx, index=%i)\n",
++		(unsigned long)pool,
++		index);
++
++	if (atomic_read(&pool->element[index].use) != 0) {
++		ZFCP_LOG_NORMAL("bug: memory pool is in use\n");
++		retval = -EINVAL;
++	} else if (pool->element[index].buffer)
++		ZFCP_KFREE(pool->element[index].buffer, pool->size);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++}
++
++
++/* try to get hold of buffer associated with the specified memory pool element */
++static inline void *zfcp_mem_pool_element_get(
++		zfcp_mem_pool_t *pool,
++		int index)
++{
++	void *buffer;
++
++	ZFCP_LOG_TRACE(
++		"enter (pool=0x%lx, index=%i)\n",
++		(unsigned long)pool,
++		index);
++
++	ZFCP_LOG_DEBUG("buffer=0x%lx\n",
++                       (unsigned long)pool->element[index].buffer);
++	ZFCP_HEX_DUMP(
++		ZFCP_LOG_LEVEL_DEBUG,
++		(char*)pool->element,
++		pool->entries * sizeof(zfcp_mem_pool_element_t));
++
++	if (atomic_compare_and_swap(0, 1, &pool->element[index].use))
++		buffer = NULL;
++	else {
++                memset(pool->element[index].buffer, 0, pool->size);
++                buffer = pool->element[index].buffer;
++        }
++
++
++	ZFCP_LOG_TRACE("exit (0x%lx)\n", (unsigned long)buffer);
++
++	return buffer;
++}
++
++
++/* mark buffer associated with the specified memory pool element as available */
++static inline int zfcp_mem_pool_element_put(
++		zfcp_mem_pool_t *pool,
++		int index)
++{
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (pool=0x%lx, index=%i)\n",
++		(unsigned long)pool,
++		index);
++
++	if (atomic_compare_and_swap(1, 0, &pool->element[index].use)) {
++		ZFCP_LOG_NORMAL("bug: memory pool is broken (element not in use)\n");
++		retval = -EINVAL;
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++}
++
++
++/*
++ * creation of a new memory pool including setup of management data structures
++ * as well as allocation of memory pool elements
++ * (this routine does not cleanup partially set up pools, instead the corresponding
++ * destroy routine should be called)
++ */
++static inline int zfcp_mem_pool_create(zfcp_mem_pool_t *pool,
++                                       int entries, int size,
++                                       void (*function) (unsigned long),
++                                       unsigned long data)
++{
++	int retval = 0;
++	int i;
++
++	ZFCP_LOG_TRACE(
++		"enter (pool=0x%lx, entries=%i, size=%i)\n",
++		(unsigned long)pool,
++		entries,
++		size);
++
++	if (pool->element || pool->entries) {
++		ZFCP_LOG_NORMAL("bug: memory pool is broken (pool is in use)\n");
++		retval = -EINVAL;
++		goto out;
++	}
++
++	pool->element = ZFCP_KMALLOC(entries * sizeof(zfcp_mem_pool_element_t),
++                                     GFP_KERNEL);
++	if (!pool->element) {
++		ZFCP_LOG_NORMAL("warning: memory pool not avalaible\n");
++		retval = -ENOMEM;
++		goto out;
++	}
++        /* Ensure that the use flag is 0. */
++
++        memset(pool->element, 0, entries * sizeof(zfcp_mem_pool_element_t)); 
++	pool->entries = entries;
++	pool->size = size;
++
++	for (i = 0; i < entries; i++) {
++		retval = zfcp_mem_pool_element_alloc(pool, i);
++		if (retval) {
++			ZFCP_LOG_NORMAL("warning: memory pool not avalaible\n");
++                        retval = -ENOMEM;
++			goto out;
++		}
++	}
++	ZFCP_HEX_DUMP(
++		ZFCP_LOG_LEVEL_DEBUG,
++		(char*)pool->element,
++		entries * sizeof(zfcp_mem_pool_element_t));
++
++	init_timer(&pool->timer);
++	pool->timer.function = function;
++	pool->timer.data = data;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++}
++
++
++/*
++ * give up memory pool with all its memory pool elements as well as
++ * data structures used for management purposes
++ * (this routine is able to handle partially alloacted memory pools)
++ */
++static inline int zfcp_mem_pool_destroy(
++		zfcp_mem_pool_t *pool)
++{
++	int retval = 0;
++	int i;
++
++	ZFCP_LOG_TRACE(
++		"enter (pool=0x%lx)\n",
++		(unsigned long)pool);
++
++	for (i = 0; i < pool->entries; i++)
++		retval |= zfcp_mem_pool_element_free(pool, i);
++
++	if (pool->element)
++		ZFCP_KFREE(pool->element, pool->entries);
++
++	pool->element = NULL;
++	pool->entries = 0;
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++}
++
++
++/*
++ * try to find next available element in the specified memory pool,
++ * on success get hold of buffer associated with the selected element
++ */
++static inline void* zfcp_mem_pool_find(
++		zfcp_mem_pool_t *pool)
++{
++	void *buffer = NULL;
++	int i;
++
++	ZFCP_LOG_TRACE(
++		"enter (pool=0x%lx)\n",
++		(unsigned long)pool);
++
++	for (i = 0; i < pool->entries; i++) {
++		buffer = zfcp_mem_pool_element_get(pool, i);
++		if (buffer)
++			break;
++	}
++
++        if ((buffer != 0) && (pool->timer.function != 0)) {
++                /*
++                 * watch low mem buffer
++                 * Note: Take care if more than 1 timer is active.
++                 * The first expired timer has to delete all other
++                 * timers. (See ZFCP_SCSI_LOW_MEM_TIMEOUT and
++                 * ZFCP_SCSI_ER_TIMEOUT)
++                 */
++                pool->timer.expires = jiffies + ZFCP_SCSI_LOW_MEM_TIMEOUT;
++                add_timer(&pool->timer);
++        }
++
++	ZFCP_LOG_TRACE("exit (0x%lx)\n", (unsigned long)buffer);
++
++	return buffer;
++}
++
++
++/*
++ * make buffer available to memory pool again,
++ * (since buffers are specified by their own address instead of the
++ * memory pool element they are associated with a search for the
++ * right element of the given memory pool)
++ */ 
++static inline int zfcp_mem_pool_return(void *buffer, zfcp_mem_pool_t *pool)
++{
++	int retval = 0;
++	int i;
++
++	ZFCP_LOG_TRACE(
++		"enter (buffer=0x%lx, pool=0x%lx)\n",
++		(unsigned long)buffer,
++		(unsigned long)pool);
++
++        if (pool->timer.function) {
++                del_timer(&pool->timer);
++        }
++
++	for (i = 0; i < pool->entries; i++) {
++		if (buffer == pool->element[i].buffer) {
++			retval = zfcp_mem_pool_element_put(pool, i);
++			goto out;
++		}
++	}
++
++	if (i == pool->entries) {
++		ZFCP_LOG_NORMAL("bug: memory pool is broken (buffer not found)\n");
++		retval = -EINVAL;
++	}
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++}
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++
++/* end of memory pool implementation */
++
++/*
++ * function:	zfcp_fsf_req_alloc
++ *
++ * purpose:     Obtains an fsf_req and potentially a qtcb (for all but 
++ *              unsolicited requests) via helper functions
++ *              Does some initial fsf request set-up.
++ *              
++ * returns:	pointer to allocated fsf_req if successfull
++ *              NULL otherwise
++ *
++ * locks:       none
++ *
++ */
++static zfcp_fsf_req_t *zfcp_fsf_req_alloc(zfcp_mem_pool_t *pool, int flags,
++                                          int kmalloc_flags)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++        
++        zfcp_fsf_req_t *fsf_req = NULL;
++
++        if (!(flags & ZFCP_REQ_USE_MEMPOOL)) {
++                fsf_req = ZFCP_KMALLOC(sizeof(struct zfcp_fsf_req_pool_buffer),
++                                       kmalloc_flags);
++        }
++
++	if ((fsf_req == 0) && (pool != 0)) {
++		fsf_req = zfcp_mem_pool_find(pool);
++		if (fsf_req){
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_POOL;
++                        fsf_req->pool = pool;
++                }
++	}
++
++	if (fsf_req == 0) {
++		ZFCP_LOG_DEBUG("error: Out of memory. Allocation of FSF "
++                              "request structure failed\n");
++	}
++
++	return fsf_req;	
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_fsf_req_free
++ *
++ * purpose:     Frees the memory of an fsf_req (and potentially a qtcb) or
++ *              returns it into the pool via helper functions.
++ *
++ * returns:     sod all
++ *
++ * locks:       none
++ */
++static inline int zfcp_fsf_req_free(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++        if (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL) {
++                retval = zfcp_mem_pool_return(fsf_req, fsf_req->pool);
++        } else {
++                ZFCP_KFREE(fsf_req, sizeof(struct zfcp_fsf_req_pool_buffer));
++        }
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_unit_enqueue
++ *
++ * purpose:	enqueues a logical unit at the end of the unit list
++ *		associated with the specified port
++ *              also sets up unit internal structures
++ *
++ * returns:	0 if a new unit was successfully enqueued
++ *              -ENOMEM if the allocation failed
++ *              -EINVAL if at least one specified parameter was faulty    
++ *
++ * locks:	proc_sema must be held to serialise changes to the unit list
++ *              port->unit_list_lock is taken and released several times
++ */
++static int
++	zfcp_unit_enqueue(
++		zfcp_port_t *port,
++		scsi_lun_t scsi_lun,
++		fcp_lun_t fcp_lun,
++                zfcp_unit_t **unit_p)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	zfcp_unit_t *unit;
++        int retval = 0;
++        unsigned long flags;
++
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx scsi_lun=%i fcp_lun=0x%Lx)\n",
++		(unsigned long)port, scsi_lun, (llui_t)fcp_lun);
++
++	/*
++	 * check that there is no unit with either this
++	 * SCSI LUN or FCP_LUN already in list
++         * Note: Unlike for the adapter and the port, this is an error
++	 */
++	read_lock_irqsave(&port->unit_list_lock, flags);
++	ZFCP_FOR_EACH_UNIT(port, unit) {
++		if (unit->scsi_lun == scsi_lun) {
++			ZFCP_LOG_NORMAL(
++				"Warning: A Unit with SCSI LUN 0x%x already "
++				"exists. Skipping record.\n",
++				scsi_lun);
++                        retval = -EINVAL;
++                        break;
++                } else if (unit->fcp_lun == fcp_lun) {
++                        ZFCP_LOG_NORMAL(
++                              "Warning: A Unit with FCP_LUN 0x%016Lx is already present. "
++                              "Record was ignored\n",
++                              (llui_t)fcp_lun);
++                        retval = -EINVAL;
++                        break;
++		}
++	}
++        read_unlock_irqrestore(&port->unit_list_lock, flags);
++        if (retval == -EINVAL)
++                goto known_unit;
++
++	/* try to allocate new unit data structure (zeroed) */
++	unit = ZFCP_KMALLOC(sizeof(zfcp_unit_t), GFP_KERNEL);
++	if (!unit) {
++		ZFCP_LOG_INFO("error: Allocation of unit struct failed. "
++                                "Out of memory.\n");
++                retval = -ENOMEM;
++                goto unit_alloc_failed;
++        }
++        
++        /* save pointer to "parent" port */
++        unit->port = port;
++        
++        /* save SCSI LUN */
++        unit->scsi_lun = scsi_lun;
++        
++        /* save FCP_LUN */
++        unit->fcp_lun = fcp_lun;
++ 
++#ifndef ZFCP_PARANOIA_DEAD_CODE
++        /* set magics */
++        unit->common_magic = ZFCP_MAGIC;
++        unit->specific_magic = ZFCP_MAGIC_UNIT;
++#endif
++
++        /* Init proc structures */
++#ifdef CONFIG_PROC_FS
++        ZFCP_LOG_TRACE("Generating proc entry....\n");
++	retval = zfcp_create_unit_proc(unit);
++        if (retval) {
++                ZFCP_LOG_TRACE(
++			"freeing unit struct 0x%lx\n",
++			(unsigned long) unit);
++                goto proc_failed;
++        }
++        ZFCP_LOG_TRACE("Proc entry created.\n");
++#endif
++       
++        /*
++         * update max. SCSI LUN of logical units attached to 
++         * "parent" remote port if necessary
++         */
++        if (port->max_scsi_lun < scsi_lun) {
++                port->max_scsi_lun = scsi_lun;
++                ZFCP_LOG_TRACE(
++			"max. SCSI LUN of units of remote "
++			"port 0x%lx now %i\n",
++			(unsigned long)port,
++			scsi_lun);
++        }
++        
++        /*
++         * update max. SCSI LUN of logical units attached to
++         * "parent" adapter if necessary
++         */
++        if (port->adapter->max_scsi_lun < scsi_lun) {
++                port->adapter->max_scsi_lun = scsi_lun;
++                ZFCP_LOG_TRACE(
++			"max. SCSI LUN of units attached "
++			"via adapter with devno 0x%04x now %i\n",
++			port->adapter->devno,
++			scsi_lun);
++        }
++
++        /*
++         * update max. SCSI LUN of logical units attached to
++         * "parent" host (SCSI stack) if necessary
++         */
++        if (port->adapter->scsi_host &&
++            (port->adapter->scsi_host->max_lun < (scsi_lun + 1))) {
++                port->adapter->scsi_host->max_lun = scsi_lun + 1;
++                ZFCP_LOG_TRACE(
++			"max. SCSI LUN of units attached "
++			"via host # %d now %i\n",
++			port->adapter->scsi_host->host_no,
++			port->adapter->scsi_host->max_lun);
++        }
++
++        /* Unit is new and needs to be added to list */
++        write_lock_irqsave(&port->unit_list_lock, flags);
++        list_add_tail(&unit->list, &port->unit_list_head);
++	port->units++;
++        write_unlock_irqrestore(&port->unit_list_lock, flags);
++        
++	/* also add unit to map list to get them in order of addition */
++	list_add_tail(&unit->map_list, &zfcp_data.map_list_head);
++         
++	atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
++
++	zfcp_callback_do_unit_add(NULL, port->adapter, port, unit);
++
++        ZFCP_LOG_TRACE(
++		"unit allocated at 0x%lx, %i units in "
++		"list of port 0x%lx\n",
++		(unsigned long)unit,
++		port->units,
++		(unsigned long)port);
++        goto out;                
++
++proc_failed:
++        ZFCP_KFREE(unit, sizeof(zfcp_unit_t));
++
++unit_alloc_failed:
++	unit = NULL;
++
++known_unit:
++out:
++	*unit_p = unit;
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_unit_dequeue
++ *
++ * purpose:	dequeues the specified logical unit from the list of
++ *		the "parent" port
++ *
++ * returns:	0 - zfcp_unit_t data structure successfully removed
++ *		!0 - zfcp_unit_t data structure could not be removed
++ *			(e.g. still used)
++ *
++ * locks :	unit list write lock is assumed to be held by caller
++ */
++static int zfcp_unit_dequeue(zfcp_unit_t *unit)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter (unit=0x%lx)\n", (unsigned long)unit);
++
++	/* sanity check: valid unit data structure address (simple check) */
++	if (!unit) {
++		ZFCP_LOG_NORMAL(
++			"bug: Pointer to a unit struct is a null "
++                        "pointer\n");
++                retval = -EINVAL;
++		goto out;
++	}
++
++	/*
++	 * sanity check:
++	 * leave if required list lock is not held,
++	 * do not know whether it is held by the calling routine (required!)
++	 * protecting this critical area or someone else (must not occur!),
++	 * but a lock not held by anyone is definetely wrong
++	 */
++	if (!spin_is_locked(&unit->port->unit_list_lock)) {
++		ZFCP_LOG_NORMAL("bug: Unit list lock not held "
++                               "(debug info 0x%lx)\n",
++                               (unsigned long) unit);
++		retval = -EPERM;
++		goto out;
++	}
++
++	/* remove specified unit data structure from list */
++	list_del(&unit->list);
++
++	/* decrease number of units in list */
++	unit->port->units--;
++
++	ZFCP_LOG_TRACE(
++		"unit 0x%lx removed, %i units still in list of port 0x%lx\n",
++		(unsigned long)unit,
++		unit->port->units,
++		(unsigned long)unit->port);
++
++        ZFCP_LOG_TRACE("Deleting proc entry......\n");
++        zfcp_delete_unit_proc(unit);
++        ZFCP_LOG_TRACE("Proc entry removed.\n");
++
++	/* free memory of unit data structure */
++	ZFCP_KFREE(unit, sizeof(zfcp_unit_t));
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;	/* succeed */
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:   zfcp_create_unit_proc
++ *
++ * purpose:    creates proc-dir and status file for the unit passed in
++ *
++ * returns:    0      if all entries could be created properly
++ *             -EPERM if at least one entry could not be created
++ *                    (all entries are guarranteed to be freed in this 
++ *                     case)
++ *
++ * locks:      proc_sema must be held on call and throughout the function  
++ */
++int zfcp_create_unit_proc(zfcp_unit_t *unit)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++     char unit_scsi_lun[20];
++     int length = 0;
++     int retval = 0;
++     
++     ZFCP_LOG_TRACE("enter (unit=0x%lx)\n",
++                    (unsigned long)unit);
++
++     length += sprintf(&unit_scsi_lun[length],"lun0x%x", unit->scsi_lun);
++     unit_scsi_lun[length]='\0';
++     unit->proc_dir = proc_mkdir (unit_scsi_lun, 
++                                  unit->port->proc_dir);
++     if (!unit->proc_dir) {
++          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the unit "
++                          "with SCSI LUN 0x%x failed. Out of resources.\n",
++                          unit_scsi_lun,
++                          unit->scsi_lun);
++          retval=-EPERM;
++          goto out;
++     }
++     unit->proc_file=create_proc_entry(ZFCP_STATUS_FILE, 
++                                       S_IFREG|S_IRUGO|S_IWUSR,
++                                       unit->proc_dir);
++     if (!unit->proc_file) {
++          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the unit "
++                          "with SCSI LUN 0x%x failed. Out of resources.\n",
++                          ZFCP_STATUS_FILE,
++                          unit->scsi_lun);
++          remove_proc_entry (unit_scsi_lun, unit->port->proc_dir);          
++          retval=-EPERM;
++          goto out;
++     }
++
++     unit->proc_file->proc_fops = &zfcp_unit_fops;
++     unit->proc_file->data=(void *)unit;
++     
++     ZFCP_LOG_TRACE("exit (%i)\n", retval);
++out:
++     return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:   zfcp_delete_unit_proc
++ *
++ * purpose:    deletes proc-dir and status file for the unit passed in
++ *
++ * returns:    0 in all cases
++ *
++ * locks:      proc_sema must be held on call and throughout the function  
++ */
++int zfcp_delete_unit_proc(zfcp_unit_t *unit)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++     char unit_scsi_lun[20];
++     int length = 0;     
++     int retval = 0;
++
++     ZFCP_LOG_TRACE("enter (unit=0x%lx)\n",
++                    (unsigned long)unit);
++     
++     remove_proc_entry (ZFCP_STATUS_FILE, 
++                        unit->proc_dir);
++     length += sprintf(&unit_scsi_lun[length],"lun0x%x", unit->scsi_lun);
++     unit_scsi_lun[length]='\0';
++     remove_proc_entry (unit_scsi_lun, unit->port->proc_dir);
++
++     ZFCP_LOG_TRACE("exit (%i)\n", retval);
++     return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:   zfcp_create_port_proc
++ *
++ * purpose:    creates proc-dir and status file for the port passed in
++ *
++ * returns:    0      if all entries could be created properly
++ *             -EPERM if at least one entry could not be created
++ *                    (all entries are guarranteed to be freed in this 
++ *                     case)
++ *
++ * locks:      proc_sema must be held on call and throughout the function  
++ */
++int zfcp_create_port_proc(zfcp_port_t *port)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++     char port_scsi_id[20];
++     int length = 0;
++     int retval = 0;
++     
++     length +=sprintf(&port_scsi_id[length],"id0x%x", port->scsi_id);
++     port_scsi_id[length]='\0';
++     port->proc_dir = proc_mkdir (port_scsi_id, 
++                                  port->adapter->proc_dir);
++     if (!port->proc_dir) {
++          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the port "
++                          "with SCSI-id 0x%x failed. Out of resources.\n",
++                          port_scsi_id,
++                          port->scsi_id);
++          retval=-EPERM;
++          goto out;
++     }
++     ZFCP_LOG_TRACE("enter (port=0x%lx)\n",
++                    (unsigned long)port);
++     port->proc_file=create_proc_entry(ZFCP_STATUS_FILE, 
++                                       S_IFREG|S_IRUGO|S_IWUSR,
++                                       port->proc_dir);
++     if (!port->proc_file) {
++          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the port "
++                          "with SCSI-id 0x%x failed. Out of resources.\n",
++                          ZFCP_STATUS_FILE,
++                          port->scsi_id);
++          remove_proc_entry (port_scsi_id, port->adapter->proc_dir);
++          retval=-EPERM;
++          goto out;
++     }
++
++     port->proc_file->proc_fops = &zfcp_port_fops;
++     port->proc_file->data=(void *)port;
++     
++out:
++     ZFCP_LOG_TRACE("exit (%i)\n", retval);
++     return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:   zfcp_delete_port_proc
++ *
++ * purpose:    deletes proc-dir and status file for the port passed in
++ *
++ * returns:    0 in all cases
++ *
++ * locks:      proc_sema must be held on call and throughout the function  
++ */
++int zfcp_delete_port_proc(zfcp_port_t *port)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++     char port_scsi_id[20];
++     int length = 0;     
++     int retval = 0;
++
++     ZFCP_LOG_TRACE("enter (port=0x%lx)\n",
++                    (unsigned long)port);
++     
++     remove_proc_entry (ZFCP_STATUS_FILE, port->proc_dir);
++     length = 0;
++     length +=sprintf(&port_scsi_id[length],"id0x%x", port->scsi_id);
++     port_scsi_id[length]='\0';
++     remove_proc_entry (port_scsi_id, port->adapter->proc_dir);
++     
++     ZFCP_LOG_TRACE("exit (%i)\n", retval);
++     return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:   zfcp_create_adapter_proc
++ *
++ * purpose:    creates proc-dir and status file for the adapter passed in
++ *
++ * returns:    0      if all entries could be created properly
++ *             -EPERM if at least one entry could not be created
++ *                    (all entries are guarranteed to be freed in this 
++ *                     case)
++ *
++ * locks:      proc_sema must be held on call and throughout the function  
++ */
++int zfcp_create_adapter_proc(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++     char devno[20];
++     int length = 0;
++     int retval = 0;
++
++     ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n",
++                    (unsigned long)adapter);
++
++     length +=sprintf(&devno[length],"devno0x%04x", adapter->devno);
++     devno[length]='\0';
++     adapter->proc_dir = proc_mkdir (devno, zfcp_data.proc_dir);
++     if (!adapter->proc_dir) {
++          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the adapter "
++                          "with devno 0x%04x failed. Out of resources.\n",
++                          devno,
++                          adapter->devno);
++          retval=-EPERM;
++          goto out;
++     }
++     adapter->proc_file=create_proc_entry(ZFCP_STATUS_FILE, 
++                                          S_IFREG|S_IRUGO|S_IWUSR,
++                                          adapter->proc_dir);
++     if (!adapter->proc_file) {
++          ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for the adapter "
++                          "with devno 0x%04x failed. Out of resources.\n",
++                          ZFCP_STATUS_FILE,
++                          adapter->devno);
++          remove_proc_entry (devno, zfcp_data.proc_dir);
++          retval=-EPERM;
++          goto out;
++     }
++
++     adapter->proc_file->proc_fops = &zfcp_adapter_fops;
++
++     adapter->proc_file->data=(void *)adapter;
++
++     out:
++
++     ZFCP_LOG_TRACE("exit (%i)\n", retval);
++     return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:   zfcp_delete_adapter_proc
++ *
++ * purpose:    deletes proc-dir and status file for the adapter passed in
++ *
++ * returns:    0 in all cases
++ *
++ * locks:      proc_sema must be held on call and throughout the function  
++ */
++int zfcp_delete_adapter_proc(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++        char devno[20];
++        int length = 0;
++        int retval = 0;
++        
++        ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n",
++                       (unsigned long)adapter);
++        
++        remove_proc_entry (ZFCP_STATUS_FILE, adapter->proc_dir);
++        length += sprintf(&devno[length],"devno0x%04x", adapter->devno);
++        devno[length]='\0';
++        remove_proc_entry (devno, zfcp_data.proc_dir);
++        
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        return retval;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/* 
++ * function:   zfcp_open_parm_proc
++ *
++ * purpose:    sets-up and fills the contents of the parm proc_entry
++ *             during a read access
++ *
++ * retval:     0 if successfull
++ *             -ENOMEM if at least one buffer could not be allocated
++ *              (all buffers will be freed on exit)
++ */
++int zfcp_open_parm_proc(struct inode *inode, struct file *file)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++	
++/*
++ * Note: modified proc fs utilization (instead of using ..._generic):
++ *
++ *  - to avoid (SMP) races, allocate buffers for output using
++ *    the private_data member in the respective file struct
++ *    such that read() just has to copy out of this buffer
++ *
++ */
++	int len = 0;
++	procbuf_t *pbuf;
++        int retval=0;
++
++	ZFCP_LOG_TRACE("enter (inode=0x%lx, file=0x%lx)\n",
++		(unsigned long)inode,
++		(unsigned long) file);
++
++#if 0
++	/* DEBUG: force an abort which is being hung than, usage of mod_parm dismisses pending fsf_req */
++	ZFCP_LOG_NORMAL("try to recover forced and hung abort\n");
++	zfcp_erp_adapter_reopen(ZFCP_FIRST_ADAPTER, 0);
++#endif
++
++	pbuf = ZFCP_KMALLOC(sizeof(procbuf_t), GFP_KERNEL);
++	if (pbuf == NULL) {
++		ZFCP_LOG_NORMAL("error: Not enough memory available for "
++                               "proc-fs action. Action will be ignored.\n");
++		retval = -ENOMEM;
++                goto out;
++	} else {
++		file->private_data = ( void * ) pbuf;
++	}
++
++	pbuf->buf = ZFCP_KMALLOC(ZFCP_MAX_PROC_SIZE, GFP_KERNEL);
++	if (pbuf->buf == NULL) {
++		ZFCP_LOG_NORMAL("error: Not enough memory available for "
++                               "proc-fs action. Action will be ignored.\n");
++		ZFCP_KFREE(pbuf, sizeof(*pbuf));
++		retval = -ENOMEM;
++                goto out;
++	}
++
++	ZFCP_LOG_TRACE("Memory for proc parm output allocated.\n");
++
++	MOD_INC_USE_COUNT;
++
++	len += sprintf(pbuf->buf+len,"\n");
++
++	len += sprintf(pbuf->buf+len,"Module Information: \n");
++
++	len += sprintf(pbuf->buf+len,"\n");
++
++	len += sprintf(pbuf->buf+len,"Module Version %s running in mode: ",
++                                ZFCP_REVISION);
++
++	len += sprintf(pbuf->buf+len,"FULL FEATURED\n");
++
++	len += sprintf(pbuf->buf+len,"Debug proc output enabled: %s\n",
++                                     proc_debug ? "  YES" : "   NO");
++
++	len += sprintf(pbuf->buf+len,"\n");
++
++	len += sprintf(pbuf->buf+len,
++                                "Full log-level is:    0x%08x which means:\n",
++                                atomic_read(&zfcp_data.loglevel));
++
++        len += sprintf(pbuf->buf+len,
++                       "ERP log-level:                 %01x\n",
++                       (atomic_read(&zfcp_data.loglevel) >> 6*4) & 0xf);
++        len += sprintf(pbuf->buf+len,
++                                "QDIO log-level:                %01x     "
++                                "Dynamic IO log-level:               %01x\n",
++                                (atomic_read(&zfcp_data.loglevel) >> 5*4) & 0xf,
++                                (atomic_read(&zfcp_data.loglevel) >> 4*4) & 0xf);
++        len += sprintf(pbuf->buf+len,
++                                "Configuration log-level:       %01x     "
++                                "FSF log-level:                      %01x\n",
++                                (atomic_read(&zfcp_data.loglevel) >> 3*4) & 0xf,
++                                (atomic_read(&zfcp_data.loglevel) >> 2*4) & 0xf);
++        len += sprintf(pbuf->buf+len,
++                                "SCSI log-level:                %01x     "
++                                "Other log-level:                    %01x\n",
++                                (atomic_read(&zfcp_data.loglevel) >> 1*4) & 0xf,
++                                atomic_read(&zfcp_data.loglevel) & 0xf);
++        len += sprintf(pbuf->buf+len,"\n");
++
++        len += sprintf(pbuf->buf+len,
++                       "Registered Adapters:       %5d\n",
++                       zfcp_data.adapters);
++        len += sprintf(pbuf->buf+len,"\n");
++
++	if (proc_debug != 0) {
++	        len += sprintf(pbuf->buf+len,
++                                        "Data Structure information:\n");
++	        len += sprintf(pbuf->buf+len,
++                               "Data struct at:       0x%08lx\n",
++                               (unsigned long) &zfcp_data);
++	       	len += sprintf(pbuf->buf+len,"\n");
++
++		len += sprintf(pbuf->buf+len,
++                                        "Adapter list head at: 0x%08lx\n",
++                                        (unsigned long) &(zfcp_data.adapter_list_head));
++		len += sprintf(pbuf->buf+len,
++                                        "Next list head:       0x%08lx     "
++                                        "Previous list head:        0x%08lx\n",
++                                        (unsigned long) zfcp_data.adapter_list_head.next,
++                                        (unsigned long) zfcp_data.adapter_list_head.prev);
++                len += sprintf(pbuf->buf+len,
++                                        "List lock:            0x%08lx     "
++                                        "List lock owner PC:        0x%08lx\n",
++                                        zfcp_data.adapter_list_lock.lock,
++                                        zfcp_data.adapter_list_lock.owner_pc);
++                len += sprintf(pbuf->buf+len,"\n");
++
++		len += sprintf(pbuf->buf+len,
++                                        "Total memory used(bytes): 0x%08x\n",
++                                        atomic_read(&zfcp_data.mem_count));
++                len += sprintf(pbuf->buf+len,"\n");
++
++                len += sprintf(pbuf->buf+len,
++                                        "DEVICE REGISTRATION INFO (devreg):\n");
++                len += sprintf(pbuf->buf+len,
++                                        "Control Unit Type:        0x%04x     "
++                                        "Control Unit Mode:               0x%02x\n",
++                                        zfcp_data.devreg.ci.hc.ctype,
++                                        zfcp_data.devreg.ci.hc.cmode);
++                len += sprintf(pbuf->buf+len,
++                                        "Channel Status:           0x%04x     "
++                                        "Device Status:                   0x%02x\n",
++                                        zfcp_data.devreg.ci.hc.dtype,
++                                        zfcp_data.devreg.ci.hc.dmode);
++                len += sprintf(pbuf->buf+len,
++                                        "Flags:                0x%08x\n",
++                                        zfcp_data.devreg.flag);
++                len += sprintf(pbuf->buf+len,"\n");
++                len += sprintf(pbuf->buf+len,
++                                        "PRIVILEGED DEVICE REGISTRATION INFO (devreg):\n");
++                len += sprintf(pbuf->buf+len,
++                                        "Control Unit Type:        0x%04x     "
++                                        "Control Unit Model:              0x%02x\n",
++                                        zfcp_data.devreg_priv.ci.hc.ctype,
++                                        zfcp_data.devreg_priv.ci.hc.cmode);
++                len += sprintf(pbuf->buf+len,
++                                        "Device Type:              0x%04x     "
++                                        "Device Model:                    0x%02x\n",
++                                        zfcp_data.devreg_priv.ci.hc.dtype,
++                                        zfcp_data.devreg_priv.ci.hc.dmode);
++                len += sprintf(pbuf->buf+len,
++                                        "Flags:                0x%08x\n",
++                                        zfcp_data.devreg_priv.flag);
++                len += sprintf(pbuf->buf+len,"\n");
++	}// if (proc_debug != 0)
++
++        pbuf->len = len;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/* 
++ * function:   zfcp_close_parm_proc
++ *
++ * purpose:    releases the memory allocated by zfcp_open_parm_proc
++ *
++ * retval:     0 in all cases
++ *
++ */
++int zfcp_close_parm_proc(struct inode *inode, struct file *file)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int rc=0;
++	procbuf_t *pbuf = (procbuf_t *) file->private_data;
++
++        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
++                       (unsigned long)inode,
++                       (unsigned long) file);
++
++	if (pbuf) {
++		if (pbuf->buf) {
++                        ZFCP_LOG_TRACE("Freeing pbuf->buf\n");
++			ZFCP_KFREE(pbuf->buf, ZFCP_MAX_PROC_SIZE);
++                } else {
++                        ZFCP_LOG_DEBUG("No procfile buffer found to be freed\n");
++                }
++                ZFCP_LOG_TRACE("Freeing pbuf\n");
++		ZFCP_KFREE(pbuf, sizeof(*pbuf));
++	} else {
++                ZFCP_LOG_DEBUG("No procfile buffer found to be freed.\n");
++        }
++
++        ZFCP_LOG_TRACE("exit (%i)\n", rc);
++
++        MOD_DEC_USE_COUNT;
++
++        return rc;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/* 
++ * function:   zfcp_open_add_map_proc
++ *
++ * purpose:    allocates memory for proc_line, intitalises count
++ *
++ * retval:     0 if successfull
++ *             -ENOMEM if memory coud not be obtained
++ *
++ * locks:     grabs the zfcp_data.sema_map semaphore
++ *            it is released upon exit of zfcp_close_add_map_proc
++ */
++int zfcp_open_add_map_proc(struct inode *inode, struct file *buffer)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int retval=0;
++        
++        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
++                       (unsigned long)inode,
++                       (unsigned long) buffer);
++
++	down(&zfcp_data.proc_sema);
++
++	zfcp_data.proc_line = ZFCP_KMALLOC(ZFCP_MAX_PROC_LINE, GFP_KERNEL);
++        if (zfcp_data.proc_line == NULL) {
++		/* release semaphore on memory shortage */
++		up(&zfcp_data.proc_sema);
++
++                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
++                               " input. Input will be ignored.\n");
++
++                retval = -ENOMEM;
++                goto out;
++        }
++
++        /* This holds the length of the part acutally containing data, not the
++           size of the buffer */
++        zfcp_data.proc_line_length=0;
++        
++        MOD_INC_USE_COUNT;
++
++        ZFCP_LOG_TRACE("proc_line buffer allocated...\n");
++out:
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++
++/* 
++ * function:   zfcp_close_add_map_proc
++ *
++ * purpose:    parses any remaining string in proc_line, then
++ *             releases memory for proc_line, then calls
++ *             zfcp_adapter_scsi_register_all to tell the SCSI stack about
++ *             possible new devices
++ *
++ * retval:     0 in all cases
++ *
++ * locks:      upon exit of zfcp_close_add_map_proc, releases the proc_sema
++ */
++int zfcp_close_add_map_proc(struct inode *inode, struct file *buffer)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int retval=0;
++
++        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
++                       (unsigned long)inode,
++                       (unsigned long) buffer);
++
++	if (zfcp_data.proc_line == NULL)
++		goto out;
++
++        if (zfcp_data.proc_line_length > 0) {
++                ZFCP_LOG_TRACE("Passing leftover line to parser\n");
++                retval=zfcp_config_parse_record_list(
++                             zfcp_data.proc_line,
++                             zfcp_data.proc_line_length,
++                             ZFCP_PARSE_ADD);
++                if(retval<0) {
++                        ZFCP_LOG_NORMAL("Warning: One or several mapping "
++                                        "entries were not added to the "
++                                        "module configuration.\n");
++                }
++        }
++        ZFCP_KFREE(zfcp_data.proc_line, ZFCP_MAX_PROC_LINE);
++        ZFCP_LOG_TRACE("proc_line buffer released...\n");
++        zfcp_data.proc_line=NULL;
++        zfcp_data.proc_line_length=0;
++
++	zfcp_adapter_scsi_register_all();
++
++	/* release semaphore */
++	up(&zfcp_data.proc_sema);
++
++        MOD_DEC_USE_COUNT;
++out:
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++
++/* 
++ * function:   zfcp_create_root_proc
++ *
++ * purpose:    creates the main proc-directory for the zfcp driver
++ *
++ * retval:     0 if successfull
++ *             -EPERM if the proc-directory could not be created
++ *
++ * locks:      the proc_sema is held on entry and throughout this function
++ */
++int zfcp_create_root_proc()
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++     int retval = 0;
++
++     ZFCP_LOG_TRACE("enter\n");
++
++     zfcp_data.proc_dir = proc_mkdir (ZFCP_NAME, proc_scsi);
++     if (!zfcp_data.proc_dir) {
++          ZFCP_LOG_INFO("error: Allocation of proc-fs directory %s for the  "
++                        "zfcp-driver failed.\n",
++                        ZFCP_NAME);
++          retval = -EPERM;
++     }
++     ZFCP_LOG_TRACE("exit (%i)\n", retval);
++     return retval;
++     
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++
++/* 
++ * function:   zfcp_create_data_procs
++ *
++ * purpose:    creates the module-centric proc-entries
++ *
++ * retval:     0 if successfull
++ *             -EPERM if the proc-entries could not be created
++ *              (all entries are removed on exit)
++ *
++ * locks:      the proc_sema is held on entry and throughout this function
++ */
++int zfcp_create_data_procs()
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++     int retval = 0;
++
++     ZFCP_LOG_TRACE("enter\n");
++     /* parm_file */
++     zfcp_data.parm_proc_file=create_proc_entry(ZFCP_PARM_FILE, 
++                                            S_IFREG|S_IRUGO|S_IWUSR,
++                                            zfcp_data.proc_dir);
++     if (!zfcp_data.parm_proc_file) {
++             ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for module "
++                             "configuration failed. Out of resources.\n",
++                             ZFCP_PARM_FILE);
++             retval = -EPERM;
++             goto out;
++     }
++     zfcp_data.parm_proc_file->proc_fops=&zfcp_parm_fops;
++
++     /* map file */
++     zfcp_data.map_proc_file=create_proc_entry(ZFCP_MAP_FILE, 
++                                            S_IFREG|S_IRUGO,
++                                            zfcp_data.proc_dir);
++     if (!zfcp_data.map_proc_file) {
++             ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for module "
++                             "configuration failed. Out of resources.\n",
++                             ZFCP_MAP_FILE);
++          retval = -EPERM;
++          goto fail_map_proc_file;
++     }
++     zfcp_data.map_proc_file->proc_fops=&zfcp_map_fops;
++
++     /* add_map file */
++     zfcp_data.add_map_proc_file=create_proc_entry(ZFCP_ADD_MAP_FILE, 
++                                            S_IFREG|S_IWUSR,
++                                            zfcp_data.proc_dir);
++     if (!zfcp_data.map_proc_file) {
++             ZFCP_LOG_INFO("error: Allocation of proc-fs entry %s for module "
++                             "configuration failed. Out of resources.\n",
++                             ZFCP_ADD_MAP_FILE);
++             retval = -EPERM;
++             goto fail_add_map_proc_file;
++     }
++     zfcp_data.add_map_proc_file->proc_fops=&zfcp_add_map_fops;
++     goto out;
++
++ fail_add_map_proc_file:
++     remove_proc_entry (ZFCP_MAP_FILE, zfcp_data.proc_dir);
++ fail_map_proc_file:
++     remove_proc_entry (ZFCP_PARM_FILE, zfcp_data.proc_dir);
++     
++ out:
++
++     ZFCP_LOG_TRACE("exit (%i)\n", retval);
++     return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++
++/* 
++ * function:   zfcp_delete_root_proc
++ *
++ * purpose:    deletes the main proc-directory for the zfcp driver
++ *
++ * retval:     0 in all cases
++ */
++int zfcp_delete_root_proc()
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++        int retval = 0;
++        
++        ZFCP_LOG_TRACE("enter\n");
++        
++        remove_proc_entry (ZFCP_NAME, proc_scsi);
++        
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++};
++
++
++
++/* 
++ * function:   zfcp_delete_data_proc
++ *
++ * purpose:    deletes the module-specific proc-entries for the zfcp driver
++ *
++ * retval:     0 in all cases
++ */
++int zfcp_delete_data_procs()
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_CONFIG
++        int retval = 0;
++        
++        ZFCP_LOG_TRACE("enter\n");
++        
++        remove_proc_entry (ZFCP_MAP_FILE, zfcp_data.proc_dir);
++        remove_proc_entry (ZFCP_ADD_MAP_FILE, zfcp_data.proc_dir);
++        remove_proc_entry (ZFCP_PARM_FILE, zfcp_data.proc_dir);
++        
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++};
++
++
++
++/*
++ * function:	zfcp_parm_proc_read
++ *
++ * purpose:	Provides information about module settings as proc-output
++ *
++ * returns:     number of characters copied to user-space
++ *              - <error-type> otherwise
++ */
++ssize_t zfcp_parm_proc_read(struct file *file, 
++                            char *user_buf, 
++                            size_t user_len, 
++                            loff_t *offset)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++        
++	loff_t len;
++	procbuf_t *pbuf = (procbuf_t *) file->private_data;
++
++	ZFCP_LOG_TRACE(
++          "enter (file=0x%lx  user_buf=0x%lx "
++          "user_length=%li *offset=0x%lx)\n",
++          (unsigned long)file,
++          (unsigned long)user_buf,
++          user_len,
++          (unsigned long)*offset);
++
++	if ( *offset>=pbuf->len) {
++		return 0;
++	} else {
++		len = min(user_len, (unsigned long)(pbuf->len - *offset));
++		if (copy_to_user( user_buf, &(pbuf->buf[*offset]), len))
++			return -EFAULT;
++		(* offset) += len;
++		return len;
++	}
++
++	/* FIXME: the following code is never reached */
++
++        ZFCP_LOG_TRACE("Size-offset is %ld, user_len is %ld\n",
++                       ((unsigned long)(pbuf->len  - *offset)),
++                       user_len);
++
++        ZFCP_LOG_TRACE("exit (%Li)\n", len);
++        
++        return len;
++     
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:   zfcp_parm_proc_write
++ *
++ * purpose:    parses write requests to parm procfile
++ *
++ * returns:    number of characters passed into function
++ *             -<error code> on failure
++ *
++ * known bugs: does not work when small buffers are used
++ */
++
++ssize_t zfcp_parm_proc_write(struct file *file,
++			const char *user_buf,
++			size_t user_len,
++			loff_t *offset)
++
++{
++
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	char *buffer, *tmp = NULL;
++	char *buffer_start = NULL;
++	char *pos;
++	size_t my_count = user_len;
++	u32 value;
++	int retval = user_len;
++
++	ZFCP_LOG_TRACE(
++		"enter (file=0x%lx  user_buf=0x%lx "
++		"user_length=%li *offset=0x%lx)\n",
++		(unsigned long)file,
++		(unsigned long)user_buf,
++		user_len,
++		(unsigned long)*offset);
++
++	buffer = ZFCP_KMALLOC(my_count + 1, GFP_KERNEL);
++	if (!buffer) {
++                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
++                                " input. Input will be ignored.\n");
++		retval = -ENOMEM;
++		goto out;
++	}
++	buffer_start=buffer;
++	ZFCP_LOG_TRACE("buffer allocated...\n");
++
++	copy_from_user(buffer, user_buf, my_count);
++
++	buffer[my_count] = '\0';
++
++	ZFCP_LOG_TRACE("user_len= %ld, strlen= %ld, buffer=%s<\n", 
++		user_len, strlen("loglevel=0x00000000"), buffer);
++
++	/* look for new loglevel */
++	pos = strstr(buffer, "loglevel=");
++	if (pos) {
++		tmp = pos + strlen("loglevel=");
++		value = simple_strtoul(tmp, &pos, 0);
++		if (pos == tmp) {
++			ZFCP_LOG_INFO(
++				"warning: Log-level could not be changed, syntax faulty."
++				"\nSyntax is loglevel=0xueqdcfso, see device driver "
++				"documentation for details.\n");
++			retval = -EFAULT;
++		} else	{
++			ZFCP_LOG_TRACE(
++				"setting new loglevel (old is 0x%x, new is 0x%x)\n",
++				atomic_read(&zfcp_data.loglevel), value);
++			atomic_set(&zfcp_data.loglevel, value);
++		}
++	}
++
++#ifdef ZFCP_LOW_MEM_CREDITS
++	/* look for low mem trigger/credit */
++	pos = strstr(buffer, "lowmem=");
++	if (pos) {
++		tmp = pos + strlen("lowmem=");
++		value = simple_strtoul(tmp, &pos, 0);
++		if (pos == tmp) {
++			ZFCP_LOG_INFO("warning: lowmem credit faulty.");
++			retval = -EFAULT;
++		} else	{
++			ZFCP_LOG_INFO("setting lowmem credit to %d\n", value);
++			atomic_set(&zfcp_data.lowmem_credit, value);
++		}
++	}
++#endif
++
++	ZFCP_LOG_TRACE("freeing buffer..\n");
++	ZFCP_KFREE(buffer_start, my_count + 1);
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++/* 
++ * function:   zfcp_open_proc_map
++ *
++ * purpose:    allocates memory for proc_buffer_map
++ *
++ * retval:     0 if successfull
++ *             -ENOMEM if memory coud not be obtained
++ *
++ * locks:     grabs the zfcp_data.sema_map semaphore 
++ *            it is released upon exit of zfcp_close_proc_map
++ */
++int zfcp_proc_map_open(struct inode *inode, struct file *buffer)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int retval = 0;
++
++        ZFCP_LOG_TRACE(
++                "enter (inode=0x%lx, buffer=0x%lx)\n",
++                (unsigned long)inode,
++                (unsigned long) buffer);
++
++        /* block access */
++        down(&zfcp_data.proc_sema);
++
++        zfcp_data.proc_buffer_map = ZFCP_KMALLOC(
++                                        ZFCP_MAX_PROC_SIZE,
++                                        GFP_KERNEL);
++        if (!zfcp_data.proc_buffer_map) {
++                /* release semaphore on memory shortage */
++                up(&zfcp_data.proc_sema);
++                ZFCP_LOG_NORMAL(
++                        "error: Not enough free memory for procfile"
++                        " output. No output will be given.\n");
++                retval = -ENOMEM;
++        } else  MOD_INC_USE_COUNT;
++
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/* 
++ * function:   zfcp_close_proc_map
++ *
++ * purpose:    releases memory for proc_buffer_map
++ *
++ * retval:     0 in all cases
++ *
++ * locks:      upon exit releases zfcp_close_proc_map
++ */
++int zfcp_proc_map_close(struct inode *inode, struct file *buffer)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int retval=0;
++
++        ZFCP_LOG_TRACE(
++                "enter (inode=0x%lx, buffer=0x%lx)\n",
++                (unsigned long)inode,
++                (unsigned long) buffer);
++
++        if (zfcp_data.proc_buffer_map) {
++                ZFCP_LOG_TRACE("Freeing zfcp_data.proc_buffer_map.\n");
++                ZFCP_KFREE(zfcp_data.proc_buffer_map, ZFCP_MAX_PROC_SIZE);
++                up(&zfcp_data.proc_sema);
++                MOD_DEC_USE_COUNT;
++        }
++
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_proc_map_read
++ *
++ * purpose:	Provides a list of all configured devices in identical format
++ *		to expected configuration input as proc-output
++ *
++ * returns:     number of characters copied to user-space
++ *              - <error-type> otherwise
++ *
++ * locks:       proc_sema must be held on entry and throughout function
++ */
++ssize_t zfcp_proc_map_read(
++		struct file *file, 
++		char *user_buf, 
++		size_t user_len, 
++		loff_t *offset)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	size_t real_len = 0;
++	size_t print_len = 0;
++	loff_t line_offset = 0;
++	u64 current_unit = 0;
++	zfcp_unit_t *unit;
++	int i = 0;
++	static size_t item_size = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (file=0x%lx  user_buf=0x%lx "
++		"user_length=%li, *offset=%Ld)\n",
++		(unsigned long)file,
++		(unsigned long)user_buf,
++		user_len,
++		*offset);
++
++	if (*offset) {
++		/*
++		 * current_unit: unit that needs to be printed (might be remainder)
++		 * line_offset: bytes of current_unit that have already been printed
++		 */
++		current_unit = (*offset);
++		line_offset = do_div(current_unit, item_size);
++		ZFCP_LOG_TRACE(
++			"item_size %ld, current_unit %Ld, line_offset %Ld\n",
++			item_size,
++			(llui_t)current_unit,
++			line_offset);
++	}
++
++	list_for_each_entry(unit, &zfcp_data.map_list_head, map_list) {
++		/* skip all units that have already been completely printed */
++		if (i < current_unit) {
++			i++;
++			continue;
++		}
++		/* a unit to be printed (at least partially) */
++		ZFCP_LOG_TRACE("unit=0x%lx\n", (unsigned long)unit);
++		/* assumption: item_size <= ZFCP_MAX_PROC_SIZE */
++		item_size = sprintf(
++				&zfcp_data.proc_buffer_map[real_len],
++				"0x%04x 0x%08x:0x%016Lx 0x%08x:0x%016Lx\n",
++				unit->port->adapter->devno,
++				unit->port->scsi_id,
++				(llui_t)(unit->port->wwpn),
++				unit->scsi_lun,
++				(llui_t)(unit->fcp_lun));
++		/* re-calculate used bytes in kernel buffer */
++		real_len += item_size;
++		/* re-calculate bytes to be printed */
++		print_len = real_len - line_offset;
++		/* stop if there is not enough user buffer space left */
++		if (print_len > user_len) {
++			/* adjust number of bytes to be printed */
++			print_len = user_len;
++			break;
++		}
++		/* stop if there is not enough kernel buffer space left */
++		if (real_len + item_size > ZFCP_MAX_PROC_SIZE)
++			break;
++	}
++
++	/* print if there is something in buffer */
++	if (print_len) {
++		ZFCP_LOG_TRACE(
++			"Trying to do output (line_offset=%Ld, print_len=%ld, "
++			"real_len=%ld, user_len=%ld).\n",
++			line_offset, print_len, real_len, user_len);
++		if (copy_to_user(
++				user_buf,
++				&zfcp_data.proc_buffer_map[line_offset],
++				print_len)) {
++			ZFCP_LOG_NORMAL(
++				"bug: Copying proc-file output to user space "
++				"failed (debug info %ld)",
++				print_len);
++			print_len = -EFAULT;
++		} else	/* re-calculate offset in proc-output for next call */
++			(*offset) += print_len;
++	}
++
++	ZFCP_LOG_TRACE("exit (%li)\n", print_len);
++
++	return print_len;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/* why is such a function not provided by the kernel? */
++static size_t strnspn(const char *string, const char *chars, size_t limit)
++{
++	size_t pos = 0;
++	const char *s = string, *c;
++
++	while ((*s != '\0') && (pos < limit)) {
++		c = chars;
++		do {
++			if (*c == '\0')
++				goto out;
++		} while (*c++ != *s);
++		s++;
++		pos++;
++	}
++
++out:
++	return pos;
++}
++
++
++/* why is such a function not provided by the kernel? */
++char* strnchr(const char *string, int character, size_t count)
++{
++	char *s = (char*) string;
++
++	for (;; s++, count--) {
++		if (!count)
++			return NULL;
++		if (*s == character)
++			return s;
++		if (*s == '\0')
++			return NULL;
++	}
++}
++
++
++/* why is such a function not provided by the kernel? */
++char* strnpbrk(const char *string, const char *chars, size_t count)
++{
++	char *s = (char*) string;
++
++	for (;; s++, count--) {
++		if (!count)
++			return NULL;
++		if (strnspn(s, chars, 1))
++			return s;
++		if (*s == '\0')
++			return NULL;
++	}
++}
++
++
++/*
++ * function:	zfcp_find_forward
++ *
++ * purpose:	Scans buffer for '\n' to a max length of *buffer_length
++ *		buffer is incremented to after the first occurance of
++ *              '\n' and *buffer_length decremented to reflect the new
++ *              buffer length.
++ *              fragment is a pointer to the original buffer start address
++ *              and contains the initial fragment string of length 
++ *              *fragment_length
++ *
++ * returns:     0 if found
++ *              -1 otherwise
++ */
++unsigned long zfcp_find_forward(char **buffer, 
++                      unsigned long *buffer_length,
++                      char **fragment,
++                      unsigned long *fragment_length)
++{
++                        
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++        
++        unsigned long retval=0;
++        
++	ZFCP_LOG_TRACE(
++                       "enter (*buffer=0x%lx, *buffer_length=%ld, "
++                       "*fragment=0x%lx, *fragment_length=%ld)\n",
++                       (unsigned long)*buffer,
++                       *buffer_length,
++                       (unsigned long)*fragment,
++                       *fragment_length);
++
++        *fragment = *buffer;
++        for(;*buffer < (*fragment + *buffer_length);){
++                if (**buffer=='\n') break;
++                (*buffer)++;
++        }
++        if(*buffer >= (*fragment + *buffer_length)){
++                *fragment_length = *buffer_length;
++                *buffer_length = 0;
++                retval = -1;
++                goto out;
++        }
++        (*buffer)++;
++        *fragment_length = *buffer - *fragment;
++        *buffer_length -= *fragment_length;
++
++ out:
++        ZFCP_LOG_TRACE("exit (%li)\n", retval);
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:	zfcp_find_backward
++ *
++ * purpose:	Scans buffer for '\n' backwards to a max length of 
++ *              *buffer_length. Buffer is left unchanged, but 
++ *              *buffer_length is decremented to reflect the new
++ *              buffer length.
++ *              rest points to the part of the string past the last
++ *              occurrence of '\n' in the original buffer contents
++ *              rest_length is the length of this part
++ *
++ * returns:     0 if found
++ *              -1 otherwise
++ */
++unsigned long zfcp_find_backward(char **buffer, 
++                      unsigned long *buffer_length,
++                      char **rest,
++                      unsigned long *rest_length)
++{
++                        
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++        
++        unsigned long retval=0;
++        
++	ZFCP_LOG_TRACE(
++                       "enter (*buffer=0x%lx, *buffer_length=%ld, "
++                       "*rest=0x%lx, *rest_length=%ld)\n",
++                       (unsigned long)*buffer,
++                       *buffer_length,
++                       (unsigned long)*rest,
++                       *rest_length);
++
++        *rest = *buffer + *buffer_length - 1;
++        /*
++          n    n+1    n+2    n+3    n+4    n+5     n+6   n+7    n+8
++          ^                                               ^     ^(*buffer+*buffer_length)
++          *buffer                                 *rest (buffer end)        
++        */
++        for(;*rest!=*buffer;){
++                if (**rest=='\n') break;
++                (*rest)--;
++        }
++        if (*rest <= *buffer) {
++                *rest_length = *buffer_length;
++                *buffer_length = 0;
++                retval = -1;
++                goto out;
++        }
++        (*rest)++;
++        /*
++          n    n+1    n+2    n+3     n+4    n+5    n+6   n+7   n+8
++          ^                           ^     ^             ^    ^(*buffer+*buffer_length)
++          *buffer                     '\n'  *rest      (buffer end)        
++        */
++        *rest_length = (*buffer + *buffer_length) - *rest;
++        *buffer_length -= *rest_length;
++
++ out:
++        ZFCP_LOG_TRACE("*rest= 0x%lx\n",
++                       (unsigned long)*rest);
++
++        ZFCP_LOG_TRACE("exit (%li)\n", retval);
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_add_map_proc_write
++ *
++ * purpose:	Breaks down the input map entries in user_buf into lines
++ *              to be parsed by zfcp_config_parse_record_list.
++ *              Also takes care of recombinations, multiple calls, etc.
++ *
++ * returns:     user_len as passed in
++ *
++ * locks:       proc_sema must be held on entry and throughout function
++ */
++ssize_t zfcp_add_map_proc_write(struct file *file, 
++                             const char *user_buf, 
++                             size_t user_len, 
++                             loff_t *offset)
++{
++
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++        char *buffer = NULL;
++        char *buffer_start = NULL; /* buffer is modified, this isn't (see kfree) */
++        char *frag = NULL;
++        size_t frag_length = 0;
++        size_t my_count = user_len;
++        int temp_ret = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (file=0x%lx  user_buf=0x%lx "
++		"user_length=%li *offset=0x%lx)\n",
++		(unsigned long)file,
++		(unsigned long)user_buf,
++		user_len,
++		(unsigned long)*offset);
++
++
++        buffer = ZFCP_KMALLOC(my_count, GFP_KERNEL);
++	if (!buffer) {
++                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
++                                " input. Input will be ignored.\n");
++		user_len = -ENOMEM;
++		goto out;
++	}
++        buffer_start=buffer;
++	ZFCP_LOG_TRACE("buffer allocated...\n");
++     
++	copy_from_user(buffer, user_buf, my_count);
++
++	if (zfcp_data.proc_line_length > 0) {
++		ZFCP_LOG_TRACE(
++			"Remnants were present...(%ld)\n",
++			zfcp_data.proc_line_length);
++		temp_ret = zfcp_find_forward(
++				&buffer, &my_count, &frag, &frag_length);
++		ZFCP_LOG_TRACE(
++			"fragment = 0x%lx, length= %ld\n",
++			(unsigned long) frag,
++			frag_length);
++
++		if ((zfcp_data.proc_line_length + frag_length) >
++		    (ZFCP_MAX_PROC_LINE - 1)) {
++			ZFCP_LOG_INFO(
++				"Maximum line length exceeded while parsing (%ld)\n",
++				zfcp_data.proc_line_length + frag_length);
++			zfcp_data.proc_line_length = 0;
++                        user_len= -EINVAL;
++			goto free_buffer;
++		} 
++
++		if (frag_length > 0) {
++			memcpy(	zfcp_data.proc_line + zfcp_data.proc_line_length,
++				frag, 
++				frag_length);
++			zfcp_data.proc_line_length += frag_length;
++		}
++
++		if(temp_ret) {
++			ZFCP_LOG_TRACE("\"\\n\" was not found \n");
++			goto free_buffer;
++		}
++
++		ZFCP_LOG_TRACE(
++			"my_count= %ld, buffer=0x%lx text: \"%s\"\n",
++			my_count,
++			(unsigned long) buffer,
++			buffer);
++
++		/* process line combined from several buffers */
++                if (zfcp_config_parse_record_list(
++			zfcp_data.proc_line,
++			zfcp_data.proc_line_length,
++                        ZFCP_PARSE_ADD) < 0) {
++                        user_len=-EINVAL;
++                        /* Do not try another parse in close_proc */
++                        zfcp_data.proc_line_length = 0;
++                        ZFCP_LOG_NORMAL("Warning: One or several mapping "
++                                        "entries were not added to the "
++                                        "module configuration.\n");
++                }
++		zfcp_data.proc_line_length = 0;
++	}// if(zfcp_data.proc_line_length > 0)
++
++	temp_ret = zfcp_find_backward(&buffer, &my_count, &frag, &frag_length);
++	ZFCP_LOG_TRACE(
++		"fragment length = %ld\n",
++		frag_length);
++	if (frag_length > (ZFCP_MAX_PROC_LINE - 1)) {
++                ZFCP_LOG_NORMAL(
++                       "warning: Maximum line length exceeded while parsing "
++                       "input. Length is already %ld. Some part of the input "
++                       "will be ignored.\n",
++			frag_length);
++		zfcp_data.proc_line_length = 0;
++                user_len = -EINVAL;
++		goto free_buffer;
++	}
++
++	if (frag_length > 0) {
++		memcpy(zfcp_data.proc_line, frag, frag_length);
++		zfcp_data.proc_line_length += frag_length;
++	}
++
++	if (temp_ret) {
++		ZFCP_LOG_TRACE("\"\\n\" was not found \n");
++		goto free_buffer;
++	}
++
++	ZFCP_LOG_TRACE(
++		"my_count= %ld, buffer=0x%lx text: \"%s\"\n",
++		my_count,
++		(unsigned long) buffer,
++		buffer);
++	if (zfcp_config_parse_record_list(
++                   buffer,
++                   my_count,
++                   ZFCP_PARSE_ADD) < 0) {
++                user_len=-EINVAL;
++                /* Do not try another parse in close_proc */
++                zfcp_data.proc_line_length = 0;
++                ZFCP_LOG_NORMAL("Warning: One or several mapping "
++                                "entries were not added to the "
++                                "module configuration.\n");
++        }
++free_buffer:
++	ZFCP_LOG_TRACE("freeing buffer..\n");
++	ZFCP_KFREE(buffer_start, my_count + 1);
++out:
++	ZFCP_LOG_TRACE("exit (%li)\n", user_len);
++	return (user_len);
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * zfcp_adapter_proc_open
++ *
++ * modified proc fs utilization (instead of using ..._generic):
++ *
++ *  - to avoid (SMP) races, allocate buffers for output using
++ *    the private_data member in the respective file struct
++ *    such that read() just has to copy out of this buffer
++ *
++ */
++
++int zfcp_adapter_proc_open(struct inode *inode, struct file *file)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int len = 0;
++        procbuf_t *pbuf;
++        int retval=0;
++        const struct inode *ino = file->f_dentry->d_inode;
++        const struct proc_dir_entry *dp = ino->u.generic_ip;
++	zfcp_adapter_t *adapter = dp->data;
++	int i;
++
++        ZFCP_LOG_TRACE("enter (inode=0x%lx, file=0x%lx)\n",
++                (unsigned long)inode,
++                (unsigned long) file);
++
++#if 0
++        /* DEBUG: force an abort which is being hung than, usage of mod_parm dismisses pending fsf_req */
++        ZFCP_LOG_NORMAL("try to recover forced and hung abort\n");
++        zfcp_erp_adapter_reopen(ZFCP_FIRST_ADAPTER, 0);
++#endif
++
++        pbuf = ZFCP_KMALLOC(sizeof(procbuf_t), GFP_KERNEL);
++        if (pbuf == NULL) {
++                ZFCP_LOG_NORMAL("error: Not enough memory available for "
++                               "proc-fs action. Action will be ignored.\n");
++                retval = -ENOMEM;
++                goto out;
++        } else {
++                file->private_data = ( void * ) pbuf;
++        }
++
++        pbuf->buf = ZFCP_KMALLOC(ZFCP_MAX_PROC_SIZE, GFP_KERNEL);
++        if (pbuf->buf == NULL) {
++                ZFCP_LOG_NORMAL("error: Not enough memory available for "
++                               "proc-fs action. Action will be ignored.\n");
++                ZFCP_KFREE(pbuf, sizeof(*pbuf));
++                retval = -ENOMEM;
++                goto out;
++        }
++
++        ZFCP_LOG_TRACE("Memory for adapter proc output allocated.\n");
++
++        MOD_INC_USE_COUNT;
++
++        len += sprintf(pbuf->buf+len,
++                                "\nFCP adapter\n\n");
++
++        len += sprintf(pbuf->buf+len,
++                                "FCP driver %s "
++                                "(or for cryptography's sake 0x%08x)\n\n",
++                                ZFCP_REVISION,
++                                zfcp_data.driver_version);
++
++        len += sprintf(pbuf->buf+len,
++				"device number:            0x%04x     "
++                                "registered on irq:             0x%04x\n",
++                                adapter->devno,
++                                adapter->irq);
++        len += sprintf(pbuf->buf+len,
++                                "WWNN:         0x%016Lx\n",
++                                (llui_t)adapter->wwnn);
++        len += sprintf(pbuf->buf+len,
++				"WWPN:         0x%016Lx     "
++				"S_ID:                        0x%06x\n",
++                                (llui_t)adapter->wwpn,
++				adapter->s_id);
++        len += sprintf(pbuf->buf+len,
++                                "HW version:               0x%04x     "
++                                "LIC version:               0x%08x\n",
++                                adapter->hydra_version,
++                                adapter->fsf_lic_version);
++        len += sprintf(pbuf->buf+len,
++                                "FC link speed:            %d Gb/s     "
++                                "FC service class:                   %d\n",
++				adapter->fc_link_speed,
++                                adapter->fc_service_class);
++        len += sprintf(pbuf->buf+len,
++                       "Hardware Version:     0x%08x\n"
++                       "Serial Number: %17s\n",
++                       adapter->hardware_version,
++                       adapter->serial_number);
++        len += sprintf(pbuf->buf+len,
++                                "FC topology:   %s\n",
++                                zfcp_topologies[adapter->fc_topology]);
++#if 0
++	if (adapter->fc_topology == FSF_TOPO_P2P)
++        len += sprintf(pbuf->buf+len,
++				"D_ID of peer:         0x%06x\n",
++				adapter->peer_d_id);
++#endif
++        len += sprintf(pbuf->buf+len,
++                                "SCSI host number:     0x%08x\n",
++                                adapter->scsi_host->host_no);
++        len += sprintf(pbuf->buf+len,"\n");
++
++        len += sprintf(pbuf->buf+len,
++                                "Attached ports:       %10d     "
++                                "QTCB size (bytes):         %10ld\n",
++                                adapter->ports,
++                                sizeof(fsf_qtcb_t));
++        len += sprintf(pbuf->buf+len,
++                                "Max SCSI ID of ports: 0x%08x     "
++                                "Max SCSI LUN of ports:     0x%08x\n",
++                                adapter->max_scsi_id,
++                                adapter->max_scsi_lun);
++        len += sprintf(pbuf->buf+len,
++                                "FSF req seq. no:      0x%08x     "
++                                "FSF reqs active:           %10d\n",
++                                adapter->fsf_req_seq_no,
++                                atomic_read(&adapter->fsf_reqs_active));
++        len += sprintf(pbuf->buf+len,
++                                "Scatter-gather table-size: %5d     "
++                                "Max no of queued commands: %10d\n",
++                                zfcp_data.scsi_host_template.sg_tablesize,
++                                zfcp_data.scsi_host_template.can_queue);
++        len += sprintf(pbuf->buf+len,
++                                "Uses clustering:               %1d     "
++                                "Uses New Error-Handling Code:       %1d\n",
++                                zfcp_data.scsi_host_template.use_clustering,
++                                zfcp_data.scsi_host_template.use_new_eh_code);
++        len += sprintf(pbuf->buf+len,
++                                "ERP counter:          0x%08x     ",
++                                atomic_read(&adapter->erp_counter));
++        len += sprintf(pbuf->buf+len,
++                                "Adapter Status:            0x%08x\n",
++                                atomic_read(&adapter->status));
++	len += sprintf(pbuf->buf+len,
++				"SCSI commands delayed: %10d\n",
++				atomic_read(&adapter->fake_scsi_reqs_active));
++        len += sprintf(pbuf->buf+len,"\n");
++
++        if (proc_debug != 0) {
++		len += sprintf(pbuf->buf+len,
++                                        "Adapter Structure information:\n");
++                len += sprintf(pbuf->buf+len,
++                                        "Common Magic:         0x%08x     "
++                                        "Specific Magic:            0x%08x\n",
++                                        adapter->common_magic,
++                                        adapter->specific_magic);
++                len += sprintf(pbuf->buf+len,
++                                        "Adapter struct at:    0x%08lx     "
++                                        "List head at:              0x%08lx\n",
++                                        (unsigned long) adapter,
++                                        (unsigned long) &(adapter->list));
++                len += sprintf(pbuf->buf+len,
++                                        "Next list head:       0x%08lx     "
++                                        "Previous list head:        0x%08lx\n",
++                                        (unsigned long) adapter->list.next,
++                                        (unsigned long) adapter->list.prev);
++                len += sprintf(pbuf->buf+len,"\n");
++
++                len += sprintf(pbuf->buf+len,
++                                        "Scsi_Host struct at:  0x%08lx\n",
++                                        (unsigned long) adapter->scsi_host);
++                len += sprintf(pbuf->buf+len,
++                                        "Port list head at:    0x%08lx\n",
++                                        (unsigned long) &(adapter->port_list_head));
++                len += sprintf(pbuf->buf+len,
++                                        "Next list head:       0x%08lx     "
++                                        "Previous list head:        0x%08lx\n",
++                                        (unsigned long) adapter->port_list_head.next,
++                                        (unsigned long) adapter->port_list_head.prev);
++                len += sprintf(pbuf->buf+len,
++                                        "List lock:            0x%08lx     "
++                                        "List lock owner PC:        0x%08lx\n",
++                                        adapter->port_list_lock.lock,
++                                        adapter->port_list_lock.owner_pc);
++                len += sprintf(pbuf->buf+len,"\n");
++
++                len += sprintf(pbuf->buf+len,
++                                        "O-FCP req list head:  0x%08lx\n",
++                                        (unsigned long) &(adapter->fsf_req_list_head));
++                len += sprintf(pbuf->buf+len,
++                                        "Next list head:       0x%08lx     "
++                                        "Previous list head:        0x%08lx\n",
++                                        (unsigned long) adapter->fsf_req_list_head.next,
++                                        (unsigned long) adapter->fsf_req_list_head.prev);
++                len += sprintf(pbuf->buf+len,
++                                        "List lock:            0x%08lx     "
++                                        "List lock owner PC:        0x%08lx\n",
++                                        adapter->fsf_req_list_lock.lock,
++                                        adapter->fsf_req_list_lock.owner_pc);
++                len += sprintf(pbuf->buf+len,"\n");
++
++                len += sprintf(pbuf->buf+len,
++                                        "Request queue at:     0x%08lx\n",
++                                        (unsigned long)&(adapter->request_queue));
++                len += sprintf(pbuf->buf+len,
++                                        "Free index:                  %03d     "
++                                        "Free count:                       %03d\n",
++                                        adapter->request_queue.free_index,
++                                        atomic_read(&adapter->request_queue.free_count));
++                len += sprintf(pbuf->buf+len,
++                                        "List lock:            0x%08lx     "
++                                        "List lock owner PC:        0x%08lx\n",
++                                        adapter->request_queue.queue_lock.lock,
++                                        adapter->request_queue.queue_lock.owner_pc);
++                len += sprintf(pbuf->buf+len,"\n");
++
++                len += sprintf(pbuf->buf+len,
++                                        "Response queue at:    0x%08lx\n",
++                                        (unsigned long)&(adapter->response_queue));
++                len += sprintf(pbuf->buf+len,
++                                        "Free index:                  %03d     "
++                                        "Free count:                       %03d\n",
++                                        adapter->response_queue.free_index,
++                                        atomic_read(&adapter->response_queue.free_count));
++                len += sprintf(pbuf->buf+len,
++                                        "List lock:            0x%08lx     "
++                                        "List lock owner PC:        0x%08lx\n",
++                                        adapter->response_queue.queue_lock.lock,
++                                        adapter->response_queue.queue_lock.owner_pc);
++                len += sprintf(pbuf->buf+len,"\n");
++
++                len += sprintf(pbuf->buf+len,"DEVICE INFORMATION (devinfo):\n");
++                len += sprintf(pbuf->buf+len,"Status: ");
++                switch(adapter->devinfo.status) {
++                        case 0:
++                                len += sprintf(pbuf->buf+len,
++                                                "\"OK\"\n");
++                        break;
++                        case DEVSTAT_NOT_OPER:
++                                len += sprintf(pbuf->buf+len,
++                                                "\"DEVSTAT_NOT_OPER\"\n");
++                                break;
++                        case DEVSTAT_DEVICE_OWNED:
++                                len += sprintf(pbuf->buf+len,
++                                                "\"DEVSTAT_DEVICE_OWNED\"\n");
++                                break;
++                        case DEVSTAT_UNKNOWN_DEV:
++                                len += sprintf(pbuf->buf+len,
++                                                "\"DEVSTAT_UNKNOWN_DEV\"\n");
++                                break;
++                        default:
++                                len += sprintf(pbuf->buf+len,
++                                                "UNSPECIFIED STATE (value is 0x%x)\n",
++                                        adapter->devinfo.status);
++                                break;
++                }
++                len += sprintf(pbuf->buf+len,
++                                        "Control Unit Type:        0x%04x     "
++                                        "Control Unit Model:              0x%02x\n",
++                                        adapter->devinfo.sid_data.cu_type,
++                                        adapter->devinfo.sid_data.cu_model);
++                len += sprintf(pbuf->buf+len,
++                                        "Device Type:              0x%04x     "
++                                        "Device Model:                    0x%02x\n",
++                                        adapter->devinfo.sid_data.dev_type,
++                                        adapter->devinfo.sid_data.dev_model);
++                len += sprintf(pbuf->buf+len,
++                                        "CIWs:       ");
++                for(i=0;i<4;i++){
++                                len += sprintf(pbuf->buf+len,
++                                                "0x%08x ",
++                                                *(unsigned int *)(&adapter->devinfo.sid_data.ciw[i]));
++                }
++                len += sprintf(pbuf->buf+len,"\n            ");
++                for(i=4;i<8;i++){
++                                len += sprintf(pbuf->buf+len,
++                                                "0x%08x ",
++                                                *(unsigned int *)(&adapter->devinfo.sid_data.ciw[i]));
++                }
++                len += sprintf(pbuf->buf+len,"\n");
++                len += sprintf(pbuf->buf+len,"\n");
++
++                len += sprintf(pbuf->buf+len,"DEVICE INFORMATION (devstat):\n");
++                len += sprintf(pbuf->buf+len,
++                                        "Interrupt Parameter:  0x%08lx     "
++                                        "Last path used mask:             0x%02x\n",
++                                        adapter->devstat.intparm,
++                                        adapter->devstat.lpum);
++                len += sprintf(pbuf->buf+len,
++                                        "Channel Status:             0x%02x     "
++                                        "Device Status:                   0x%02x\n",
++                                        adapter->devstat.cstat,
++                                        adapter->devstat.dstat);
++		len += sprintf(pbuf->buf+len,
++                                        "Flag:                 0x%08x     "
++                                        "CCW address (from irb):    0x%08lx\n",
++                                        adapter->devstat.flag,
++                                        (unsigned long)adapter->devstat.cpa);
++                len += sprintf(pbuf->buf+len,
++                                        "Response count:       0x%08x     "
++                                        "Sense Count:               0x%08x\n",
++                                        adapter->devstat.rescnt,
++                                        adapter->devstat.scnt);
++                len += sprintf(pbuf->buf+len,
++                                "IRB:        ");
++                for(i=0;i<4;i++){
++                                len += sprintf(pbuf->buf+len,
++                                                "0x%08x ",
++                                                *((unsigned int *)(&adapter->devstat.ii.irb)+i));
++                }
++                len += sprintf(pbuf->buf+len,"\n");
++                len += sprintf(pbuf->buf+len,
++                                        "Sense Data: ");
++                for(i=0;i<4;i++){
++                                len += sprintf(pbuf->buf+len,
++                                                "0x%08x ",
++                                                *((unsigned int *)(&adapter->devstat.ii.sense.data)+i));
++                }
++                        len += sprintf(pbuf->buf+len,"\n");
++        }
++
++#ifdef ZFCP_STAT_QUEUES
++        len += sprintf(pbuf->buf + len, "\nOutbound queue full:  0x%08x     ",
++                       atomic_read(&adapter->outbound_queue_full));
++	len += sprintf(pbuf->buf + len, "Outbound requests:    0x%08x\n\n",
++			atomic_read(&adapter->outbound_total));
++#endif
++#ifdef ZFCP_STAT_REQSIZES
++	len += sprintf(pbuf->buf + len, "missed stats 0x%x\n",
++			atomic_read(&adapter->stat_errors));
++	len = zfcp_statistics_print(
++			adapter, &adapter->read_req_head,
++			"rr", pbuf->buf, len, ZFCP_MAX_PROC_SIZE);
++	len = zfcp_statistics_print(
++			adapter, &adapter->write_req_head,
++			"wr", pbuf->buf, len, ZFCP_MAX_PROC_SIZE);
++#endif
++
++	ZFCP_LOG_TRACE("stored %d bytes in proc buffer\n", len);
++
++        pbuf->len = len;
++
++out:
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++int zfcp_adapter_proc_close(struct inode *inode, struct file *file)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int rc=0;
++        procbuf_t *pbuf = (procbuf_t *) file->private_data;
++
++        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
++                       (unsigned long)inode,
++                       (unsigned long) file);
++
++        if (pbuf) {
++                if (pbuf->buf) {
++                        ZFCP_LOG_TRACE("Freeing pbuf->buf\n");
++                        ZFCP_KFREE(pbuf->buf, ZFCP_MAX_PROC_SIZE);
++                } else {
++                        ZFCP_LOG_DEBUG("No procfile buffer found to be freed\n");
++                }
++                ZFCP_LOG_TRACE("Freeing pbuf\n");
++                ZFCP_KFREE(pbuf, sizeof(*pbuf));
++        } else {
++                ZFCP_LOG_DEBUG("No procfile buffer found to be freed.\n");
++        }
++
++        ZFCP_LOG_TRACE("exit (%i)\n", rc);
++
++        MOD_DEC_USE_COUNT;
++
++        return rc;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:    zfcp_adapter_proc_read
++ *
++ * returns:     number of characters copied to user-space
++ *              - <error-type> otherwise
++ */
++ssize_t zfcp_adapter_proc_read(struct file *file,
++                            char *user_buf,
++                            size_t user_len,
++                            loff_t *offset)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        loff_t len;
++        procbuf_t *pbuf = (procbuf_t *) file->private_data;
++
++        ZFCP_LOG_TRACE(
++          "enter (file=0x%lx  user_buf=0x%lx "
++          "user_length=%li *offset=0x%lx)\n",
++          (unsigned long)file,
++          (unsigned long)user_buf,
++          user_len,
++          (unsigned long)*offset);
++
++        if ( *offset>=pbuf->len) {
++                return 0;
++        } else {
++                len = min(user_len, (unsigned long)(pbuf->len - *offset));
++                if (copy_to_user( user_buf, &(pbuf->buf[*offset]), len))
++                        return -EFAULT;
++                (* offset) += len;
++                return len;
++        }
++
++        ZFCP_LOG_TRACE("Size-offset is %ld, user_len is %ld\n",
++                       ((unsigned long)(pbuf->len  - *offset)),
++                       user_len);
++
++        ZFCP_LOG_TRACE("exit (%Li)\n", len);
++
++        return len;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function: zfcp_adapter_proc_write
++ *
++ * known bugs: does not work when small buffers are used
++ *
++ */
++
++ssize_t zfcp_adapter_proc_write(struct file *file,
++                        const char *user_buf,
++                        size_t user_len,
++                        loff_t *offset)
++
++{
++
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        char *buffer = NULL;
++        size_t my_count = user_len;
++        const struct inode *ino = file->f_dentry->d_inode;
++        const struct proc_dir_entry *dp = ino->u.generic_ip;
++	zfcp_adapter_t *adapter = dp->data;
++
++        ZFCP_LOG_TRACE(
++                "enter (file=0x%lx  user_buf=0x%lx "
++                "user_length=%li *offset=0x%lx)\n",
++                (unsigned long)file,
++                (unsigned long)user_buf,
++                user_len,
++                (unsigned long)*offset);
++
++        buffer = ZFCP_KMALLOC(my_count + 1, GFP_KERNEL);
++        if (!buffer) {
++                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
++                                " input. Input will be ignored.\n");
++                user_len = -ENOMEM;
++                goto out;
++        }
++        ZFCP_LOG_TRACE("buffer allocated...\n");
++
++        copy_from_user(buffer, user_buf, my_count);
++
++        buffer[my_count] = '\0'; /* for debugging */
++
++        ZFCP_LOG_TRACE("user_len= %ld, buffer=>%s<\n",
++                user_len, buffer);
++
++	if ((strncmp(ZFCP_RESET_ERP, buffer, strlen(ZFCP_RESET_ERP)) == 0) ||
++	    (strncmp(ZFCP_SET_ONLINE, buffer, strlen(ZFCP_SET_ONLINE)) == 0)) {
++		ZFCP_LOG_NORMAL(
++			"user triggered (re)start of all operations on the "
++			"adapter with devno 0x%04x\n",
++			adapter->devno);
++		zfcp_erp_modify_adapter_status(
++			adapter,
++			ZFCP_STATUS_COMMON_RUNNING,
++			ZFCP_SET);
++		zfcp_erp_adapter_reopen(
++			adapter,
++			ZFCP_STATUS_COMMON_ERP_FAILED);
++		zfcp_erp_wait(adapter);
++		user_len = strlen(buffer);
++	} else  if (strncmp(ZFCP_SET_OFFLINE, buffer, strlen(ZFCP_SET_OFFLINE)) == 0) {
++                ZFCP_LOG_NORMAL(
++			"user triggered shutdown of all operations on the "
++			"adapter with devno 0x%04x\n",
++			adapter->devno);
++		zfcp_erp_adapter_shutdown(adapter, 0);
++		zfcp_erp_wait(adapter);
++		user_len = strlen(buffer);
++	} else	if (strncmp(ZFCP_STAT_RESET, buffer, strlen(ZFCP_STAT_RESET)) == 0) {
++#ifdef ZFCP_STAT_REQSIZES
++		ZFCP_LOG_NORMAL(
++			"user triggered reset of all statisticss for the "
++			"adapter with devno 0x%04x\n",
++			adapter->devno);
++		atomic_compare_and_swap(1, 0, &adapter->stat_on);
++		zfcp_statistics_clear(adapter, &adapter->read_req_head);
++		zfcp_statistics_clear(adapter, &adapter->write_req_head);
++		atomic_set(&adapter->stat_errors, 0);
++		atomic_compare_and_swap(0, 1, &adapter->stat_on);
++#endif
++		user_len = strlen(buffer);
++	} else	if (strncmp(ZFCP_STAT_OFF, buffer, strlen(ZFCP_STAT_OFF)) == 0) {
++#ifdef ZFCP_STAT_REQSIZES
++		if (atomic_compare_and_swap(1, 0, &adapter->stat_on)) {
++			ZFCP_LOG_NORMAL(
++				"warning: all statistics for the adapter "
++				"with devno 0x%04x already off\n ",
++			adapter->devno);
++		} else	{
++			ZFCP_LOG_NORMAL(
++				"user triggered shutdown of all statistics for the "
++				"adapter with devno 0x%04x\n",
++			adapter->devno);
++			zfcp_statistics_clear(adapter, &adapter->read_req_head);
++			zfcp_statistics_clear(adapter, &adapter->write_req_head);
++		}
++#endif
++		user_len = strlen(buffer);
++	} else	if (strncmp(ZFCP_STAT_ON, buffer, strlen(ZFCP_STAT_ON)) == 0) {
++#ifdef ZFCP_STAT_REQSIZES
++		if (atomic_compare_and_swap(0, 1, &adapter->stat_on)) {
++			ZFCP_LOG_NORMAL(
++				"warning: all statistics for the adapter "
++				"with devno 0x%04x already on\n ",
++			adapter->devno);
++		} else	{
++			ZFCP_LOG_NORMAL(
++				"user triggered (re)start of all statistics for the "
++				"adapter with devno 0x%04x\n",
++			adapter->devno);
++		}
++#endif
++		user_len = strlen(buffer);
++	} else	{
++		ZFCP_LOG_INFO("error: unknown procfs command\n");
++		user_len = -EINVAL;
++	}
++
++        ZFCP_LOG_TRACE("freeing buffer..\n");
++        ZFCP_KFREE(buffer, my_count + 1);
++out:
++        ZFCP_LOG_TRACE("exit (%li)\n", user_len);
++        return (user_len);
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++int zfcp_port_proc_close(struct inode *inode, struct file *file)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int rc=0;
++        procbuf_t *pbuf = (procbuf_t *) file->private_data;
++
++        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
++                       (unsigned long)inode,
++                       (unsigned long) file);
++
++        if (pbuf) {
++                if (pbuf->buf) {
++                        ZFCP_LOG_TRACE("Freeing pbuf->buf\n");
++                        ZFCP_KFREE(pbuf->buf, ZFCP_MAX_PROC_SIZE);
++                } else {
++                        ZFCP_LOG_DEBUG("No procfile buffer found to be freed\n");
++                }
++                ZFCP_LOG_TRACE("Freeing pbuf\n");
++                ZFCP_KFREE(pbuf, sizeof(*pbuf));
++        } else {
++                ZFCP_LOG_DEBUG("No procfile buffer found to be freed.\n");
++        }
++
++        ZFCP_LOG_TRACE("exit (%i)\n", rc);
++
++        MOD_DEC_USE_COUNT;
++
++        return rc;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:    zfcp_port_proc_read
++ *
++ * returns:     number of characters copied to user-space
++ *              - <error-type> otherwise
++ */
++ssize_t zfcp_port_proc_read(struct file *file,
++                            char *user_buf,
++                            size_t user_len,
++                            loff_t *offset)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        loff_t len;
++        procbuf_t *pbuf = (procbuf_t *) file->private_data;
++
++        ZFCP_LOG_TRACE(
++          "enter (file=0x%lx  user_buf=0x%lx "
++          "user_length=%li *offset=0x%lx)\n",
++          (unsigned long)file,
++          (unsigned long)user_buf,
++          user_len,
++          (unsigned long)*offset);
++
++        if ( *offset>=pbuf->len) {
++                return 0;
++        } else {
++                len = min(user_len, (unsigned long)(pbuf->len - *offset));
++                if (copy_to_user( user_buf, &(pbuf->buf[*offset]), len))
++                        return -EFAULT;
++                (* offset) += len;
++                return len;
++        }
++
++        ZFCP_LOG_TRACE("Size-offset is %ld, user_len is %ld\n",
++                       ((unsigned long)(pbuf->len  - *offset)),
++                       user_len);
++
++        ZFCP_LOG_TRACE("exit (%Li)\n", len);
++
++        return len;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function: zfcp_port_proc_write
++ *
++ * known bugs: does not work when small buffers are used
++ *
++ */
++
++ssize_t zfcp_port_proc_write(struct file *file,
++                        const char *user_buf,
++                        size_t user_len,
++                        loff_t *offset)
++
++{
++
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        char *buffer = NULL;
++        size_t my_count = user_len;
++        const struct inode *ino = file->f_dentry->d_inode;
++        const struct proc_dir_entry *dp = ino->u.generic_ip;
++        zfcp_port_t *port = dp->data;
++	zfcp_adapter_t *adapter = port->adapter;
++
++        ZFCP_LOG_TRACE(
++                "enter (file=0x%lx  user_buf=0x%lx "
++                "user_length=%li *offset=0x%lx)\n",
++                (unsigned long)file,
++                (unsigned long)user_buf,
++                user_len,
++                (unsigned long)*offset);
++
++        buffer = ZFCP_KMALLOC(my_count + 1, GFP_KERNEL);
++        if (!buffer) {
++                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
++                                " input. Input will be ignored.\n");
++                user_len = -ENOMEM;
++                goto out;
++        }
++        ZFCP_LOG_TRACE("buffer allocated...\n");
++
++        copy_from_user(buffer, user_buf, my_count);
++
++        buffer[my_count] = '\0'; /* for debugging */
++
++        ZFCP_LOG_TRACE("user_len= %ld, buffer=>%s<\n",
++                user_len, buffer);
++
++	if ((strncmp(ZFCP_RESET_ERP, buffer, strlen(ZFCP_RESET_ERP)) == 0) ||
++	    (strncmp(ZFCP_SET_ONLINE, buffer, strlen(ZFCP_SET_ONLINE)) == 0)) {
++		ZFCP_LOG_NORMAL(
++			"user triggered (re)start of all operations on the "
++			"port with WWPN 0x%016Lx on the adapter with devno "
++			"0x%04x\n",
++			(llui_t)port->wwpn,
++			adapter->devno);
++		zfcp_erp_modify_port_status(
++			port,
++			ZFCP_STATUS_COMMON_RUNNING,
++			ZFCP_SET);
++		zfcp_erp_port_reopen(
++			port,
++			ZFCP_STATUS_COMMON_ERP_FAILED);
++		zfcp_erp_wait(adapter);
++		user_len = strlen(buffer);
++	} else  if (strncmp(ZFCP_SET_OFFLINE, buffer, strlen(ZFCP_SET_OFFLINE)) == 0) {
++		ZFCP_LOG_NORMAL(
++			"user triggered shutdown of all operations on the "
++			"port with WWPN 0x%016Lx on the adapter with devno "
++			"0x%04x\n",
++			(llui_t)port->wwpn,
++			adapter->devno);
++		zfcp_erp_port_shutdown(port, 0);
++		zfcp_erp_wait(adapter);
++		user_len = strlen(buffer);
++	} else  if (strncmp(ZFCP_RTV, buffer, strlen(ZFCP_RTV)) == 0) {
++		ZFCP_LOG_NORMAL(
++			"Read timeout value (RTV) ELS "
++			"(wwpn=0x%016Lx devno=0x%04x)\n",
++			(llui_t)port->wwpn,
++			adapter->devno);
++		zfcp_els(port, ZFCP_LS_RTV);
++		user_len = strlen(buffer);
++	} else  if (strncmp(ZFCP_RLS, buffer, strlen(ZFCP_RLS)) == 0) {
++		ZFCP_LOG_NORMAL(
++			"Read link status (RLS) ELS "
++			"(wwpn=0x%016Lx devno=0x%04x)\n",
++			(llui_t)port->wwpn,
++			adapter->devno);
++		zfcp_els(port, ZFCP_LS_RLS);
++		user_len = strlen(buffer);
++	} else  if (strncmp(ZFCP_PDISC, buffer, strlen(ZFCP_PDISC)) == 0) {
++		ZFCP_LOG_NORMAL(
++			"Port discovery (PDISC) ELS "
++			"(wwpn=0x%016Lx devno=0x%04x)\n",
++			(llui_t)port->wwpn,
++			adapter->devno);
++		zfcp_els(port, ZFCP_LS_PDISC);
++		user_len = strlen(buffer);
++	} else  if (strncmp(ZFCP_ADISC, buffer, strlen(ZFCP_ADISC)) == 0) {
++		ZFCP_LOG_NORMAL(
++			"Address discovery (ADISC) ELS "
++			"(wwpn=0x%016Lx devno=0x%04x)\n",
++			(llui_t)port->wwpn,
++			adapter->devno);
++		zfcp_els(port, ZFCP_LS_ADISC);
++	} else	{
++		ZFCP_LOG_INFO("error: unknown procfs command\n");
++		user_len = -EINVAL;
++	}
++
++        ZFCP_LOG_TRACE("freeing buffer..\n");
++        ZFCP_KFREE(buffer, my_count + 1);
++out:
++        ZFCP_LOG_TRACE("exit (%li)\n", user_len);
++        return (user_len);
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * zfcp_port_proc_open
++ *
++ * modified proc fs utilization (instead of using ..._generic):
++ *
++ *  - to avoid (SMP) races, allocate buffers for output using
++ *    the private_data member in the respective file struct
++ *    such that read() just has to copy out of this buffer
++ *
++ */
++
++int zfcp_port_proc_open(struct inode *inode, struct file *file)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int len = 0;
++        procbuf_t *pbuf;
++        int retval=0;
++        const struct inode *ino = file->f_dentry->d_inode;
++        const struct proc_dir_entry *dp = ino->u.generic_ip;
++        zfcp_port_t *port = dp->data;
++
++        ZFCP_LOG_TRACE("enter (inode=0x%lx, file=0x%lx)\n",
++                (unsigned long)inode,
++                (unsigned long) file);
++
++        pbuf = ZFCP_KMALLOC(sizeof(procbuf_t), GFP_KERNEL);
++        if (pbuf == NULL) {
++                ZFCP_LOG_NORMAL("error: Not enough memory available for "
++                               "proc-fs action. Action will be ignored.\n");
++                retval = -ENOMEM;
++                goto out;
++        } else {
++                file->private_data = ( void * ) pbuf;
++        }
++
++        pbuf->buf = ZFCP_KMALLOC(ZFCP_MAX_PROC_SIZE, GFP_KERNEL);
++        if (pbuf->buf == NULL) {
++                ZFCP_LOG_NORMAL("error: Not enough memory available for "
++                               "proc-fs action. Action will be ignored.\n");
++                ZFCP_KFREE(pbuf, sizeof(*pbuf));
++                retval = -ENOMEM;
++                goto out;
++        }
++
++        ZFCP_LOG_TRACE("Memory for port proc output allocated.\n");
++
++        MOD_INC_USE_COUNT;
++
++        len += sprintf(pbuf->buf+len,"\n");
++
++        len += sprintf(pbuf->buf+len,
++                               "Port Information: \n");
++        len += sprintf(pbuf->buf+len,"\n");
++
++        len += sprintf(pbuf->buf+len,
++                                "WWNN:         0x%016Lx     "
++                                "WWPN:              0x%016Lx\n",
++                                (llui_t)port->wwnn,
++                                (llui_t)port->wwpn);
++        len += sprintf(pbuf->buf+len,
++                                "SCSI ID:              0x%08x     "
++                                "Max SCSI LUN:              0x%08x\n",
++                                port->scsi_id,
++                                port->max_scsi_lun);
++        len += sprintf(pbuf->buf+len,
++                                "D_ID:                   0x%06x\n",
++                                port->d_id);
++        len += sprintf(pbuf->buf+len,
++                                "Handle:               0x%08x\n",
++                                port->handle);
++        len += sprintf(pbuf->buf+len,"\n");
++
++        len += sprintf(pbuf->buf+len,
++                                "Attached units:       %10d\n",
++                                port->units);
++        len += sprintf(pbuf->buf+len,
++                                "ERP counter:          0x%08x\n",
++                                atomic_read(&port->erp_counter));
++        len += sprintf(pbuf->buf+len,
++                                "Port Status:          0x%08x\n",
++                                atomic_read(&port->status));
++        len += sprintf(pbuf->buf+len,"\n");
++
++        if (proc_debug != 0) {
++		len += sprintf(pbuf->buf+len,
++                                        "Port Structure information:\n");
++                len += sprintf(pbuf->buf+len,
++                                        "Common Magic:         0x%08x     "
++                                        "Specific Magic:            0x%08x\n",
++                                        port->common_magic,
++                                        port->specific_magic);
++                len += sprintf(pbuf->buf+len,
++                                        "Port struct at:       0x%08lx     "
++                                        "List head at:              0x%08lx\n",
++                                        (unsigned long) port,
++                                        (unsigned long) &(port->list));
++                len += sprintf(pbuf->buf+len,
++                                        "Next list head:       0x%08lx     "
++                                        "Previous list head:        0x%08lx\n",
++                                        (unsigned long) port->list.next,
++                                        (unsigned long) port->list.prev);
++                len += sprintf(pbuf->buf+len,"\n");
++
++                len += sprintf(pbuf->buf+len,
++                                        "Unit list head at:    0x%08lx\n",
++                                        (unsigned long) &(port->unit_list_head));
++                len += sprintf(pbuf->buf+len,
++                                        "Next list head:       0x%08lx     "
++                                        "Previous list head:        0x%08lx\n",
++                                        (unsigned long) port->unit_list_head.next,
++                                        (unsigned long) port->unit_list_head.prev);
++                len += sprintf(pbuf->buf+len,
++                                        "List lock:            0x%08lx     "
++                                        "List lock owner PC:        0x%08lx\n",
++                                        port->unit_list_lock.lock,
++                                        port->unit_list_lock.owner_pc);
++                len += sprintf(pbuf->buf+len,"\n");
++
++                len += sprintf(pbuf->buf+len,
++                                        "Parent adapter at:    0x%08lx\n",
++                                        (unsigned long) port->adapter);
++        }
++
++        ZFCP_LOG_TRACE("stored %d bytes in proc buffer\n", len);
++
++        pbuf->len = len;
++
++out:
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * zfcp_unit_proc_open
++ *
++ *  - to avoid (SMP) races, allocate buffers for output using
++ *    the private_data member in the respective file struct
++ *    such that read() just has to copy out of this buffer
++ *
++ */
++
++int zfcp_unit_proc_open(struct inode *inode, struct file *file)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int len = 0;
++        procbuf_t *pbuf;
++        int retval=0;
++        const struct inode *ino = file->f_dentry->d_inode;
++        const struct proc_dir_entry *dp = ino->u.generic_ip;
++        zfcp_unit_t *unit = dp->data;
++
++        ZFCP_LOG_TRACE("enter (inode=0x%lx, file=0x%lx)\n",
++                (unsigned long)inode,
++                (unsigned long) file);
++
++        pbuf = ZFCP_KMALLOC(sizeof(procbuf_t), GFP_KERNEL);
++        if (pbuf == NULL) {
++                ZFCP_LOG_NORMAL("error: Not enough memory available for "
++                               "proc-fs action. Action will be ignored.\n");
++                retval = -ENOMEM;
++                goto out;
++        } else {
++                file->private_data = ( void * ) pbuf;
++        }
++
++        pbuf->buf = ZFCP_KMALLOC(ZFCP_MAX_PROC_SIZE, GFP_KERNEL);
++        if (pbuf->buf == NULL) {
++                ZFCP_LOG_NORMAL("error: Not enough memory available for "
++                               "proc-fs action. Action will be ignored.\n");
++                ZFCP_KFREE(pbuf, sizeof(*pbuf));
++                retval = -ENOMEM;
++                goto out;
++        }
++
++        ZFCP_LOG_TRACE("Memory for unit proc output allocated.\n");
++
++        MOD_INC_USE_COUNT;
++
++        len += sprintf(pbuf->buf+len,"\n");
++
++        len += sprintf(pbuf->buf+len,
++                                "Unit Information: \n");
++        len += sprintf(pbuf->buf+len,"\n");
++
++        len += sprintf(pbuf->buf+len,
++                                "SCSI LUN:             0x%08x     "
++                                "FCP_LUN:           0x%016Lx\n",
++                                unit->scsi_lun,
++                                (llui_t)unit->fcp_lun);
++        len += sprintf(pbuf->buf+len,
++                                "Handle:               0x%08x\n",
++                                unit->handle);
++        len += sprintf(pbuf->buf+len,"\n");
++
++        len += sprintf(pbuf->buf+len,
++                                "ERP counter:          0x%08x\n",
++                                atomic_read(&unit->erp_counter));
++        len += sprintf(pbuf->buf+len,
++                                "Unit Status:          0x%08x\n",
++                                atomic_read(&unit->status));
++        len += sprintf(pbuf->buf+len,"\n");
++
++        if (proc_debug != 0) {
++        	len += sprintf(pbuf->buf+len,
++                                        "Unit Structure information:\n");
++                len += sprintf(pbuf->buf+len,
++                                        "Common Magic:         0x%08x     "
++                                        "Specific Magic:            0x%08x\n",
++                                        unit->common_magic,
++                                        unit->specific_magic);
++                len += sprintf(pbuf->buf+len,
++                                        "Unit struct at:       0x%08lx     "
++                                        "List head at:              0x%08lx\n",
++                                        (unsigned long) unit,
++                                        (unsigned long) &(unit->list));
++                len += sprintf(pbuf->buf+len,
++                                        "Next list head:       0x%08lx     "
++                                        "Previous list head:        0x%08lx\n",
++                                        (unsigned long) unit->list.next,
++                                        (unsigned long) unit->list.prev);
++                len += sprintf(pbuf->buf+len,"\n");
++
++                len += sprintf(pbuf->buf+len,
++                                        "Parent port at:       0x%08lx     "
++                                        "SCSI dev struct at:        0x%08lx\n",
++                                        (unsigned long) unit->port,
++                                        (unsigned long) unit->device);
++	}
++
++        ZFCP_LOG_TRACE("stored %d bytes in proc buffer\n", len);
++
++        pbuf->len = len;
++
++out:
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++int zfcp_unit_proc_close(struct inode *inode, struct file *file)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int rc=0;
++        procbuf_t *pbuf = (procbuf_t *) file->private_data;
++
++        ZFCP_LOG_TRACE("enter (inode=0x%lx, buffer=0x%lx)\n",
++                       (unsigned long)inode,
++                       (unsigned long) file);
++
++        if (pbuf) {
++                if (pbuf->buf) {
++                        ZFCP_LOG_TRACE("Freeing pbuf->buf\n");
++                        ZFCP_KFREE(pbuf->buf, ZFCP_MAX_PROC_SIZE);
++                } else {
++                        ZFCP_LOG_DEBUG("No procfile buffer found to be freed\n");
++                }
++                ZFCP_LOG_TRACE("Freeing pbuf\n");
++                ZFCP_KFREE(pbuf, sizeof(*pbuf));
++        } else {
++                ZFCP_LOG_DEBUG("No procfile buffer found to be freed.\n");
++        }
++
++        ZFCP_LOG_TRACE("exit (%i)\n", rc);
++
++        MOD_DEC_USE_COUNT;
++
++        return rc;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:    zfcp_unit_proc_read
++ *
++ * returns:     number of characters copied to user-space
++ *              - <error-type> otherwise
++ */
++ssize_t zfcp_unit_proc_read(struct file *file,
++                            char *user_buf,
++                            size_t user_len,
++                            loff_t *offset)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        loff_t len;
++        procbuf_t *pbuf = (procbuf_t *) file->private_data;
++
++        ZFCP_LOG_TRACE(
++          "enter (file=0x%lx  user_buf=0x%lx "
++          "user_length=%li *offset=0x%lx)\n",
++          (unsigned long)file,
++          (unsigned long)user_buf,
++          user_len,
++          (unsigned long)*offset);
++
++        if ( *offset>=pbuf->len) {
++                return 0;
++        } else {
++                len = min(user_len, (unsigned long)(pbuf->len - *offset));
++                if (copy_to_user( user_buf, &(pbuf->buf[*offset]), len))
++                        return -EFAULT;
++                (* offset) += len;
++                return len;
++        }
++
++        ZFCP_LOG_TRACE("Size-offset is %ld, user_len is %ld\n",
++                       ((unsigned long)(pbuf->len  - *offset)),
++                       user_len);
++
++        ZFCP_LOG_TRACE("exit (%Li)\n", len);
++
++        return len;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function: zfcp_unit_proc_write
++ *
++ */
++
++ssize_t zfcp_unit_proc_write(struct file *file,
++                        const char *user_buf,
++                        size_t user_len,
++                        loff_t *offset)
++
++{
++
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        char *buffer = NULL;
++        size_t my_count = user_len;
++        const struct inode *ino = file->f_dentry->d_inode;
++        const struct proc_dir_entry *dp = ino->u.generic_ip;
++        zfcp_unit_t *unit = dp->data;
++
++        ZFCP_LOG_TRACE(
++                "enter (file=0x%lx  user_buf=0x%lx "
++                "user_length=%li *offset=0x%lx)\n",
++                (unsigned long)file,
++                (unsigned long)user_buf,
++                user_len,
++                (unsigned long)*offset);
++
++        buffer = ZFCP_KMALLOC(my_count + 1, GFP_KERNEL);
++        if (!buffer) {
++                ZFCP_LOG_NORMAL("error: Not enough free memory for procfile"
++                                " input. Input will be ignored.\n");
++                user_len = -ENOMEM;
++                goto out;
++        }
++        ZFCP_LOG_TRACE("buffer allocated...\n");
++
++        copy_from_user(buffer, user_buf, my_count);
++
++        buffer[my_count] = '\0'; /* for debugging */
++
++        ZFCP_LOG_TRACE("user_len= %ld, buffer=>%s<\n",
++                user_len, buffer);
++
++	if ((strncmp(ZFCP_RESET_ERP, buffer, strlen(ZFCP_RESET_ERP)) == 0) ||
++	    (strncmp(ZFCP_SET_ONLINE, buffer, strlen(ZFCP_SET_ONLINE)) == 0)) {
++		ZFCP_LOG_NORMAL(
++			"user triggered (re)start of all operations on the "
++			"unit with FCP_LUN 0x%016Lx on the port with WWPN 0x%016Lx "
++			"on the adapter with devno 0x%04x\n",
++			(llui_t)unit->fcp_lun,
++			(llui_t)unit->port->wwpn,
++			unit->port->adapter->devno);
++		zfcp_erp_modify_unit_status(
++			unit,
++			ZFCP_STATUS_COMMON_RUNNING,
++			ZFCP_SET);
++		zfcp_erp_unit_reopen(unit, ZFCP_STATUS_COMMON_ERP_FAILED);
++		zfcp_erp_wait(unit->port->adapter);
++		user_len = strlen(buffer);
++	} else	if (strncmp(ZFCP_SET_OFFLINE, buffer, strlen(ZFCP_SET_OFFLINE)) == 0) {
++		ZFCP_LOG_NORMAL(
++			"user triggered shutdown of all operations on the "
++			"unit with FCP_LUN 0x%016Lx on the port with WWPN 0x%016Lx "
++			"on the adapter with devno 0x%04x\n",
++			(llui_t)unit->fcp_lun,
++			(llui_t)unit->port->wwpn,
++			unit->port->adapter->devno);
++		zfcp_erp_unit_shutdown(unit, 0);
++		zfcp_erp_wait(unit->port->adapter);
++		user_len = strlen(buffer);
++	} else  {
++		ZFCP_LOG_INFO("error: unknown procfs command\n");
++		user_len = -EINVAL;
++	}
++
++        ZFCP_LOG_TRACE("freeing buffer..\n");
++        ZFCP_KFREE(buffer, my_count + 1);
++out:
++        ZFCP_LOG_TRACE("exit (%li)\n", user_len);
++        return (user_len);
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:	zfcp_scsi_detect
++ *
++ * purpose:	This routine is called by the SCSI stack mid layer
++ *		to query detected host bus adapters.
++ *
++ * returns:	number of detcted HBAs (0, if no HBAs detected)
++ */
++int zfcp_scsi_detect(Scsi_Host_Template *shtpnt)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++     
++	int adapters = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (shtpnt =0x%lx)\n",
++		(unsigned long) shtpnt);
++
++	spin_unlock_irq(&io_request_lock);
++	adapters = zfcp_adapter_scsi_register_all();
++	spin_lock_irq(&io_request_lock);
++
++	ZFCP_LOG_TRACE(
++		"exit (adapters =%d)\n",
++		adapters);
++
++	return adapters;
++     
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	for all adapters which are not yet registered with SCSI stack:
++ *		wait for finish of erp and register adapter with SCSI stack then 
++ *
++ * returns:	number of adapters registered with SCSI stack
++ *
++ * FIXME(design):	/proc/scsi/zfcp/add-del_map must be locked as long as we
++ *			are in such a loop as implemented here.
++ *			We need a guarantee that no adapter will (dis)sappear.
++ *			Otherwise list corruption may be caused.
++ *			(We can't hold the lock all the time due to possible
++ *			 calls to schedule())
++ */
++static int zfcp_adapter_scsi_register_all()
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	int retval = 0;
++	unsigned long flags;
++	zfcp_adapter_t *adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	read_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
++	adapter = ZFCP_FIRST_ADAPTER;
++	while (adapter) {
++		read_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
++		if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status)) {
++			ZFCP_LOG_DEBUG(
++				"adapter with devno 0x%04x needs "
++				"to be registered with SCSI stack, "
++				"waiting for erp to settle\n",
++				adapter->devno);
++			zfcp_erp_wait(adapter);
++			if (zfcp_adapter_scsi_register(adapter) == 0);
++				retval++;
++		}
++		read_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
++		adapter = ZFCP_NEXT_ADAPTER(adapter);
++	}
++	read_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++void zfcp_scsi_select_queue_depth(struct Scsi_Host *host, Scsi_Device *dev_list)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++        zfcp_adapter_t *adapter = (zfcp_adapter_t *)host->hostdata[0];
++        zfcp_port_t *port = NULL;
++        zfcp_unit_t *unit = NULL;
++        unsigned long flags=0;
++        
++        ZFCP_LOG_TRACE("enter (host =0x%lx, dev_list=0x%lx)\n",
++                       (unsigned long) host,
++                       (unsigned long) dev_list);
++        
++        read_lock_irqsave(&adapter->port_list_lock, flags);
++        ZFCP_FOR_EACH_PORT(adapter, port) {
++                read_lock(&port->unit_list_lock);
++                ZFCP_FOR_EACH_UNIT(port, unit) {
++                        ZFCP_LOG_DEBUG("Determinig if unit 0x%lx"
++                                       " supports tagging\n",
++                                       (unsigned long) unit);
++                        if (!unit->device) 
++                                continue; 
++
++                        if (unit->device->tagged_supported) {
++                                ZFCP_LOG_DEBUG("Enabling tagging for "
++                                               "unit 0x%lx \n",
++                                               (unsigned long) unit);
++                                unit->device->tagged_queue = 1;
++                                unit->device->current_tag = 0;
++                                unit->device->queue_depth = ZFCP_CMND_PER_LUN;
++				atomic_set_mask(ZFCP_STATUS_UNIT_ASSUMETCQ, &unit->status);
++                        } else {
++                                ZFCP_LOG_DEBUG("Disabling tagging for "
++                                               "unit 0x%lx \n",
++                                               (unsigned long) unit);
++                                unit->device->tagged_queue = 0;
++                                unit->device->current_tag = 0;
++                                unit->device->queue_depth = 1;
++				atomic_clear_mask(ZFCP_STATUS_UNIT_ASSUMETCQ, &unit->status);
++                        }
++		}
++		read_unlock(&port->unit_list_lock);
++	}
++	read_unlock_irqrestore(&adapter->port_list_lock, flags);
++
++        ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_scsi_revoke
++ *
++ * purpose:
++ *
++ * returns:
++ */
++int zfcp_scsi_revoke(Scsi_Device *sdpnt)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	int retval = 0;
++	zfcp_unit_t *unit = (zfcp_unit_t*) sdpnt->hostdata;
++#if 0
++	zfcp_port_t *port = unit->port;
++#endif
++
++	ZFCP_LOG_TRACE("enter (sdpnt=0x%lx)\n", (unsigned long)sdpnt);
++
++	if (!unit) {
++		ZFCP_LOG_INFO(
++			"no unit associated with SCSI device at "
++			"address 0x%lx\n",
++			(unsigned long)sdpnt);
++		goto out;
++	}
++
++#if 0
++	/* Shutdown entire port if we are going to shutdown the last unit. */
++	if (port->units == 1) {
++                zfcp_erp_port_shutdown(port, 0);
++                zfcp_erp_wait(port->adapter);
++        } else {
++                zfcp_erp_unit_shutdown(unit, 0);
++                zfcp_erp_wait(port->adapter);
++        }
++#endif
++	sdpnt->hostdata = NULL;
++	unit->device = NULL;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_scsi_release
++ *
++ * purpose:	called from SCSI stack mid layer to make this driver
++ *		cleanup I/O and resources for this adapter
++ *
++ * returns:
++ */
++int zfcp_scsi_release(struct Scsi_Host *shpnt)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter (shpnt=0x%lx)\n", (unsigned long)shpnt);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_scsi_insert_into_fake_queue
++ *
++ * purpose:
++ *		
++ *
++ * returns:
++ */
++inline void zfcp_scsi_insert_into_fake_queue(zfcp_adapter_t *adapter, Scsi_Cmnd *new_cmnd)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        unsigned long flags;
++        Scsi_Cmnd *current_cmnd;
++
++	ZFCP_LOG_TRACE("enter (adapter=0x%lx, cmnd=0x%lx)\n", 
++                       (unsigned long)adapter,
++                       (unsigned long)new_cmnd);
++
++        ZFCP_LOG_DEBUG("Faking SCSI command:\n");
++        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
++                      (char*)new_cmnd->cmnd,
++                      new_cmnd->cmd_len);
++        
++        new_cmnd->host_scribble = NULL;
++
++        write_lock_irqsave(&adapter->fake_list_lock,flags);
++        if(adapter->first_fake_cmnd==NULL) {
++                adapter->first_fake_cmnd = new_cmnd;
++                adapter->fake_scsi_timer.function = 
++                        zfcp_scsi_process_and_clear_fake_queue;
++                adapter->fake_scsi_timer.data = 
++                        (unsigned long)adapter;
++                adapter->fake_scsi_timer.expires = 
++                        jiffies + ZFCP_FAKE_SCSI_COMPLETION_TIME;
++                add_timer(&adapter->fake_scsi_timer);
++        } else {        
++                for(current_cmnd=adapter->first_fake_cmnd;
++                    current_cmnd->host_scribble != NULL;
++                    current_cmnd = (Scsi_Cmnd *)(current_cmnd->host_scribble));
++                current_cmnd->host_scribble = (char *)new_cmnd;
++        }
++	atomic_inc(&adapter->fake_scsi_reqs_active);
++        write_unlock_irqrestore(&adapter->fake_list_lock,flags);
++
++	ZFCP_LOG_TRACE("exit\n");
++ 
++	return;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_scsi_process_and_clear_fake_queue
++ *
++ * purpose:
++ *		
++ *
++ * returns:
++ */
++inline void zfcp_scsi_process_and_clear_fake_queue(unsigned long data)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        unsigned long flags;
++        Scsi_Cmnd *current_cmnd;
++        Scsi_Cmnd *next_cmnd;
++        zfcp_adapter_t *adapter=(zfcp_adapter_t *)data;
++
++	ZFCP_LOG_TRACE("enter (data=0x%lx)\n", data);
++
++        /*
++         * We need a common lock for scsi_req on command completion
++         * as well as on command abort to avoid race conditions
++         * during completions and aborts taking place at the same time.
++         * It needs to be the outer lock as in the eh_abort_handler.
++         */
++        read_lock_irqsave(&adapter->abort_lock, flags);
++        write_lock(&adapter->fake_list_lock);
++        if(adapter->first_fake_cmnd == NULL) {
++                ZFCP_LOG_DEBUG("Processing of fake-queue called "
++                               "for an empty queue.\n");
++        } else {        
++                current_cmnd=adapter->first_fake_cmnd;
++                do {
++                        next_cmnd=(Scsi_Cmnd *)(current_cmnd->host_scribble);
++                        current_cmnd->host_scribble = NULL;
++#if 0
++			zfcp_cmd_dbf_event_scsi("clrfake", adapter, current_cmnd);
++#endif
++                        current_cmnd->scsi_done(current_cmnd);
++#ifdef ZFCP_DEBUG_REQUESTS
++                        debug_text_event(adapter->req_dbf, 2, "fk_done:");
++                        debug_event(adapter->req_dbf, 2, &current_cmnd, sizeof(unsigned long));
++#endif /* ZFCP_DEBUG_REQUESTS */
++			atomic_dec(&adapter->fake_scsi_reqs_active);
++                        current_cmnd=next_cmnd;
++                } while (next_cmnd != NULL);
++                /* Set list to empty */
++                adapter->first_fake_cmnd = NULL;
++        }
++        write_unlock(&adapter->fake_list_lock);
++        read_unlock_irqrestore(&adapter->abort_lock, flags);
++
++	ZFCP_LOG_TRACE("exit\n");
++ 
++	return;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++static void zfcp_scsi_command_fail(
++		zfcp_unit_t *unit,
++		Scsi_Cmnd *scsi_cmnd,
++		int result)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++	zfcp_adapter_t *adapter = unit->port->adapter;
++
++#ifdef ZFCP_DEBUG_REQUESTS
++	debug_text_event(adapter->req_dbf, 2, "de_done:");
++	debug_event(adapter->req_dbf, 2, &scsi_cmnd, sizeof(unsigned long));
++#endif /* ZFCP_DEBUG_REQUESTS */
++
++	scsi_cmnd->SCp.ptr = (char*)0;
++	scsi_cmnd->result = result;
++
++	zfcp_cmd_dbf_event_scsi("failing", adapter, scsi_cmnd);
++
++	scsi_cmnd->scsi_done(scsi_cmnd);
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++ 
++static void zfcp_scsi_command_fake(
++		zfcp_unit_t *unit,
++		Scsi_Cmnd *scsi_cmnd)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++	if (scsi_cmnd->SCp.ptr) {
++		if (((unsigned long)scsi_cmnd->SCp.ptr + ZFCP_SCSI_RETRY_TIMEOUT)
++		    < jiffies) {
++			/* leave it to the SCSI stack eh */
++			zfcp_scsi_command_fail(unit, scsi_cmnd, DID_TIME_OUT << 16);
++			return;
++		}
++	} else	scsi_cmnd->SCp.ptr = (char*)jiffies;
++	scsi_cmnd->retries--;	/* -1 is ok */
++	scsi_cmnd->result |= DID_SOFT_ERROR << 16
++			     | SUGGEST_RETRY << 24;
++	zfcp_scsi_insert_into_fake_queue(unit->port->adapter, scsi_cmnd);
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/**
++ * zfcp_scsi_command_async - worker for zfcp_scsi_queuecommand and
++ * zfcp_scsi_command_sync
++ */
++int zfcp_scsi_command_async(
++		zfcp_unit_t *unit,
++		Scsi_Cmnd *scsi_cmnd,
++		void (* done)(Scsi_Cmnd *))
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++	scsi_cmnd->scsi_done = done;
++	scsi_cmnd->result = 0;
++
++	if (!unit) {
++		zfcp_scsi_command_fail(unit, scsi_cmnd, DID_NO_CONNECT << 16);
++		goto out;
++	}
++
++	if (atomic_test_mask(
++			ZFCP_STATUS_COMMON_ERP_FAILED,
++			&unit->status)) {
++		zfcp_scsi_command_fail(unit, scsi_cmnd, DID_ERROR << 16);
++		goto out;
++	}
++
++	if (!atomic_test_mask(
++			ZFCP_STATUS_COMMON_RUNNING,
++			&unit->status)) {
++		zfcp_scsi_command_fail(unit, scsi_cmnd, DID_ERROR << 16);
++		goto out;
++	}
++
++	if (!atomic_test_mask(
++			ZFCP_STATUS_COMMON_UNBLOCKED,
++			&unit->status))  {
++		zfcp_scsi_command_fake(unit, scsi_cmnd);
++		goto out;
++	}
++
++	if (zfcp_fsf_send_fcp_command_task(unit, scsi_cmnd) < 0)
++		zfcp_scsi_command_fake(unit, scsi_cmnd);
++
++out:
++	return 0;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++void zfcp_scsi_command_sync_handler(Scsi_Cmnd *scsi_cmnd)
++{
++	struct completion *wait = (struct completion*) scsi_cmnd->bh_next;
++	complete(wait);
++}
++
++
++/**
++ * zfcp_scsi_command_sync - send a SCSI command and wait for completion
++ * returns 0, errors are indicated by scsi_cmnd->result
++ */
++int zfcp_scsi_command_sync(
++		zfcp_unit_t *unit,
++		Scsi_Cmnd *scsi_cmnd)
++{
++	DECLARE_COMPLETION(wait);
++
++	scsi_cmnd->bh_next = (void*) &wait;  /* silent re-use */
++        zfcp_scsi_command_async(
++			unit,
++			scsi_cmnd,
++			zfcp_scsi_command_sync_handler);
++	wait_for_completion(&wait);
++
++	return 0;
++}
++
++
++ 
++/*
++ * function:	zfcp_scsi_queuecommand
++ *
++ * purpose:	enqueues a SCSI command to the specified target device
++ *
++ * note:        The scsi_done midlayer function may be called directly from
++ *              within queuecommand provided queuecommand returns with success (0)
++ *              If it fails, it is expected that the command could not be sent
++ *              and is still available for processing.
++ *              As we ensure that queuecommand never fails, we have the choice 
++ *              to call done directly wherever we please.
++ *              Thus, any kind of send errors other than those indicating
++ *              'infinite' retries will be reported directly.
++ *              Retry requests are put into a list to be processed under timer 
++ *              control once in a while to allow for other operations to
++ *              complete in the meantime.
++ *
++ * returns:	0 - success, SCSI command enqueued
++ *		!0 - failure, note that we never allow this to happen as the 
++ *              SCSI stack would block indefinitely should a non-zero return
++ *              value be reported if there are no outstanding commands
++ *              (as in when the queues are down)
++ */
++int zfcp_scsi_queuecommand(
++		Scsi_Cmnd *scsi_cmnd,
++		void (* done)(Scsi_Cmnd *))
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	zfcp_unit_t *unit;
++	zfcp_adapter_t *adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (scsi_cmnd=0x%lx done=0x%lx)\n",
++		(unsigned long)scsi_cmnd,
++		(unsigned long)done);
++
++	spin_unlock_irq(&io_request_lock);
++
++	/*
++	 * figure out adapter
++	 * (previously stored there by the driver when
++	 * the adapter was registered)
++	 */
++	adapter = (zfcp_adapter_t*) scsi_cmnd->host->hostdata[0];
++
++	/*
++	 * figure out target device
++	 * (stored there by the driver when the first command
++	 * is sent to this target device)
++	 * ATTENTION: assumes hostdata initialized to NULL by
++	 * mid layer (see scsi_scan.c)
++	 */
++	if (!scsi_cmnd->device->hostdata) {
++		unit = zfcp_unit_lookup(
++				adapter,
++				scsi_cmnd->device->channel,
++				scsi_cmnd->device->id,
++				scsi_cmnd->device->lun);
++		/* Is specified unit configured? */
++		if (unit) {
++			scsi_cmnd->device->hostdata = unit;
++			unit->device = scsi_cmnd->device;
++			ZFCP_LOG_DEBUG(
++				"logical unit address (0x%lx) saved "
++				"for direct lookup and scsi_stack "
++				"pointer 0x%lx saved in unit structure\n",
++				(unsigned long)unit,
++				(unsigned long)unit->device);
++		}
++	} else	unit = (zfcp_unit_t*) scsi_cmnd->device->hostdata;
++
++	zfcp_scsi_command_async(unit, scsi_cmnd, done);
++
++	spin_lock_irq(&io_request_lock);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", 0);
++
++	return 0;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_unit_lookup
++ *
++ * purpose:
++ *
++ * returns:
++ *
++ * context:	
++ */
++static zfcp_unit_t* zfcp_unit_lookup(
++	zfcp_adapter_t *adapter,
++	int channel,
++	int id,
++	int lun)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_OTHER
++
++	zfcp_port_t *port; 
++	zfcp_unit_t *unit = NULL;
++	unsigned long flags;
++ 
++	ZFCP_LOG_TRACE(
++		"enter (adapter devno=0x%04x, channel=%i, id=%i, lun=%i)\n",
++		adapter->devno,
++		channel,
++		id,
++		lun);
++
++	read_lock_irqsave(&adapter->port_list_lock, flags);
++	ZFCP_FOR_EACH_PORT(adapter, port) {
++		if ((scsi_id_t)id != port->scsi_id)
++			continue;
++		read_lock(&port->unit_list_lock);
++		ZFCP_FOR_EACH_UNIT(port, unit) {
++			if ((scsi_lun_t)lun == unit->scsi_lun) {
++				ZFCP_LOG_TRACE("found unit\n");
++				break;
++			}
++		}
++		read_unlock(&port->unit_list_lock);
++		if (unit)
++			break;
++	}
++	read_unlock_irqrestore(&adapter->port_list_lock, flags);
++ 
++	ZFCP_LOG_TRACE("exit (0x%lx)\n", (unsigned long)unit);
++ 
++	return unit;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_scsi_potential_abort_on_fake
++ *
++ * purpose:
++ *
++ * returns:     0 - no fake request aborted
++ *              1 - fake request was aborted
++ *
++ * context:	both the adapter->abort_lock and the 
++ *              adapter->fake_list_lock are assumed to be held write lock
++ *              irqsave
++ */
++inline int zfcp_scsi_potential_abort_on_fake(zfcp_adapter_t *adapter, Scsi_Cmnd *cmnd)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        Scsi_Cmnd *current_cmnd, *prev_cmnd;
++	unsigned long flags;
++        int retval = 0;
++
++	ZFCP_LOG_TRACE("enter (adapter=0x%lx, cmnd=0x%lx)\n",
++                       (unsigned long)adapter,
++                       (unsigned long)cmnd);
++
++	write_lock_irqsave(&adapter->fake_list_lock, flags);
++
++        current_cmnd=adapter->first_fake_cmnd;
++
++	if (!current_cmnd)
++		goto out;
++
++        if(current_cmnd==cmnd) {
++                adapter->first_fake_cmnd=(Scsi_Cmnd *)cmnd->host_scribble;
++                cmnd->host_scribble=NULL;
++                if(adapter->first_fake_cmnd==NULL) {
++                        /* No need to wake anymore  */
++                        /* Note: It does not matter if the timer has already
++                         * expired, the fake_list_lock takes care of 
++                         * potential races 
++                         */
++                        del_timer(&adapter->fake_scsi_timer);
++                }
++		atomic_dec(&adapter->fake_scsi_reqs_active);
++                retval=1;
++                goto out;
++        } 
++        do {
++                prev_cmnd = current_cmnd;
++                current_cmnd = (Scsi_Cmnd *)(current_cmnd->host_scribble);
++                if (current_cmnd==cmnd) {
++                        prev_cmnd->host_scribble=current_cmnd->host_scribble;
++                        current_cmnd->host_scribble=NULL;
++			atomic_dec(&adapter->fake_scsi_reqs_active);
++                        retval=1;
++                        goto out;
++                }
++        } while (current_cmnd->host_scribble != NULL);         
++
++out:
++	write_unlock_irqrestore(&adapter->fake_list_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++                }        
++
++
++/*
++ * function:	zfcp_scsi_eh_abort_handler
++ *
++ * purpose:	tries to abort the specified (timed out) SCSI command
++ *
++ * note: 	We do not need to care for a SCSI command which completes
++ *		normally but late during this abort routine runs.
++ *		We are allowed to return late commands to the SCSI stack.
++ *		It tracks the state of commands and will handle late commands.
++ *		(Usually, the normal completion of late commands is ignored with
++ *		respect to the running abort operation. Grep for 'done_late'
++ *		in the SCSI stacks sources.)
++ *
++ * returns:	SUCCESS	- command has been aborted and cleaned up in internal
++ *			  bookkeeping,
++ *			  SCSI stack won't be called for aborted command
++ *		FAILED	- otherwise
++ */
++int zfcp_scsi_eh_abort_handler(Scsi_Cmnd *scpnt)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	int retval = SUCCESS;
++	zfcp_fsf_req_t *new_fsf_req, *old_fsf_req;
++	zfcp_adapter_t *adapter = (zfcp_adapter_t*) scpnt->host->hostdata[0];
++	zfcp_unit_t *unit = (zfcp_unit_t*) scpnt->device->hostdata;
++	zfcp_port_t *port = unit->port;
++	unsigned long flags;
++	u32 status = 0;
++#ifdef ZFCP_DEBUG_ABORTS
++	/* the components of a abort_dbf record (fixed size record) */
++	u64		dbf_scsi_cmnd	= (unsigned long)scpnt;
++	char		dbf_opcode[ZFCP_ABORT_DBF_LENGTH];
++	wwn_t		dbf_wwn		= port->wwpn;
++	fcp_lun_t	dbf_fcp_lun	= unit->fcp_lun;
++	u64		dbf_retries	= scpnt->retries;
++	u64		dbf_allowed	= scpnt->allowed;
++	u64		dbf_timeout	= 0;
++	u64		dbf_fsf_req	= 0;
++	u64		dbf_fsf_status	= 0;
++	u64		dbf_fsf_qual[2]	= { 0, 0 };
++	char		dbf_result[ZFCP_ABORT_DBF_LENGTH]
++					= { "##undef" };
++
++	memset(dbf_opcode, 0, ZFCP_ABORT_DBF_LENGTH);
++	memcpy(	dbf_opcode,
++		scpnt->cmnd,
++		min(scpnt->cmd_len, (unsigned char)ZFCP_ABORT_DBF_LENGTH));
++#endif
++
++        /*TRACE*/
++	ZFCP_LOG_TRACE("enter (scpnt=0x%lx)\n", (unsigned long)scpnt);
++
++	ZFCP_LOG_INFO(
++		"Aborting for adapter=0x%lx, devno=0x%04x, scsi_cmnd=0x%lx\n",
++		(unsigned long)adapter,
++		adapter->devno,
++		(unsigned long)scpnt);
++
++	spin_unlock_irq(&io_request_lock);
++#if 0
++               /* DEBUG */
++        retval=FAILED;
++        goto out;
++#endif
++
++	/*
++	 * Race condition between normal (late) completion and abort has
++	 * to be avoided.
++	 * The entirity of all accesses to scsi_req have to be atomic.
++	 * scsi_req is usually part of the fsf_req (for requests which
++	 * are not faked) and thus we block the release of fsf_req
++	 * as long as we need to access scsi_req.
++	 * For faked commands we use the same lock even if they are not
++	 * put into the fsf_req queue. This makes implementation
++	 * easier. 
++	 */
++	write_lock_irqsave(&adapter->abort_lock, flags);
++
++	/*
++	 * Check if we deal with a faked command, which we may just forget
++	 * about from now on
++	 */
++	if (zfcp_scsi_potential_abort_on_fake(adapter, scpnt)) {
++		write_unlock_irqrestore(&adapter->abort_lock, flags);
++#ifdef ZFCP_DEBUG_ABORTS
++		strncpy(dbf_result, "##faked", ZFCP_ABORT_DBF_LENGTH);
++#endif
++		retval = SUCCESS;
++		goto out;
++	}
++
++	/*
++	 * Check whether command has just completed and can not be aborted.
++	 * Even if the command has just been completed late, we can access
++	 * scpnt since the SCSI stack does not release it at least until
++	 * this routine returns. (scpnt is parameter passed to this routine
++	 * and must not disappear during abort even on late completion.)
++	 */
++	old_fsf_req = (zfcp_fsf_req_t*) scpnt->host_scribble;
++	if (!old_fsf_req) {
++		ZFCP_LOG_DEBUG("late command completion overtook abort\n");
++		/*
++		 * That's it.
++		 * Do not initiate abort but return SUCCESS.
++		 */
++		write_unlock_irqrestore(&adapter->abort_lock, flags);
++		retval = SUCCESS;
++#ifdef ZFCP_DEBUG_ABORTS
++		strncpy(dbf_result, "##late1", ZFCP_ABORT_DBF_LENGTH);
++#endif
++		goto out;
++	}
++#ifdef ZFCP_DEBUG_ABORTS
++	dbf_fsf_req = (unsigned long)old_fsf_req;
++	dbf_timeout = (jiffies - old_fsf_req->data.send_fcp_command_task.start_jiffies) / HZ;
++#endif
++
++        old_fsf_req->data.send_fcp_command_task.scsi_cmnd = NULL;
++	/* mark old request as being aborted */
++	old_fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTING;
++	/*
++	 * We have to collect all information (e.g. unit) needed by 
++	 * zfcp_fsf_abort_fcp_command before calling that routine
++	 * since that routine is not allowed to access
++	 * fsf_req which it is going to abort.
++	 * This is because of we need to release fsf_req_list_lock
++	 * before calling zfcp_fsf_abort_fcp_command.
++	 * Since this lock will not be held, fsf_req may complete
++	 * late and may be released meanwhile.
++	 */
++	ZFCP_LOG_DEBUG(
++		"unit=0x%lx, unit_fcp_lun=0x%Lx\n",
++		(unsigned long)unit,
++		(llui_t)unit->fcp_lun);
++
++	/*
++	 * We block (call schedule)
++	 * That's why we must release the lock and enable the
++	 * interrupts before.
++	 * On the other hand we do not need the lock anymore since
++	 * all critical accesses to scsi_req are done.
++	 */
++	write_unlock_irqrestore(&adapter->abort_lock, flags);
++	/* call FSF routine which does the abort */
++	new_fsf_req = zfcp_fsf_abort_fcp_command(
++				(unsigned long)old_fsf_req, adapter, unit, 0);
++	ZFCP_LOG_DEBUG(
++		"new_fsf_req=0x%lx\n",
++		(unsigned long) new_fsf_req);
++	if (!new_fsf_req) {
++		retval = FAILED;
++		ZFCP_LOG_DEBUG(
++			"warning: Could not abort SCSI command "
++			"at 0x%lx\n",
++			(unsigned long)scpnt);
++#ifdef ZFCP_DEBUG_ABORTS
++		strncpy(dbf_result, "##nores", ZFCP_ABORT_DBF_LENGTH);
++#endif
++		goto out;
++	}
++
++	/* wait for completion of abort */
++	ZFCP_LOG_DEBUG("Waiting for cleanup....\n");
++#ifdef ZFCP_DEBUG_ABORTS
++	/* FIXME: copying zfcp_fsf_req_wait_and_cleanup code is not really nice */
++	__wait_event(
++		new_fsf_req->completion_wq,
++		new_fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
++	status = new_fsf_req->status;
++	dbf_fsf_status = new_fsf_req->qtcb->header.fsf_status;
++	/*
++	 * Ralphs special debug load provides timestamps in the FSF
++	 * status qualifier. This might be specified later if being
++	 * useful for debugging aborts.
++	 */
++	dbf_fsf_qual[0] = *(u64*)&new_fsf_req->qtcb->header.fsf_status_qual.word[0];
++	dbf_fsf_qual[1] = *(u64*)&new_fsf_req->qtcb->header.fsf_status_qual.word[2];
++	retval = zfcp_fsf_req_cleanup(new_fsf_req);
++#else
++	retval = zfcp_fsf_req_wait_and_cleanup(
++			new_fsf_req,
++			ZFCP_UNINTERRUPTIBLE,
++			&status);
++#endif
++	ZFCP_LOG_DEBUG(
++		"Waiting for cleanup complete, status=0x%x\n",
++		status);
++	/* status should be valid since signals were not permitted */
++	if (status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED) {
++		retval = SUCCESS;
++#ifdef ZFCP_DEBUG_ABORTS
++		strncpy(dbf_result, "##succ", ZFCP_ABORT_DBF_LENGTH);
++#endif
++	} else	if (status & ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED) {
++		retval = SUCCESS;
++#ifdef ZFCP_DEBUG_ABORTS
++		strncpy(dbf_result, "##late2", ZFCP_ABORT_DBF_LENGTH);
++#endif
++	} else	{
++		retval = FAILED;
++#ifdef ZFCP_DEBUG_ABORTS
++		strncpy(dbf_result, "##fail", ZFCP_ABORT_DBF_LENGTH);
++#endif
++	}
++
++out:
++#ifdef ZFCP_DEBUG_ABORTS
++	debug_event(adapter->abort_dbf, 1, &dbf_scsi_cmnd, sizeof(u64));
++	debug_event(adapter->abort_dbf, 1, &dbf_opcode, ZFCP_ABORT_DBF_LENGTH);
++	debug_event(adapter->abort_dbf, 1, &dbf_wwn, sizeof(wwn_t));
++	debug_event(adapter->abort_dbf, 1, &dbf_fcp_lun, sizeof(fcp_lun_t));
++	debug_event(adapter->abort_dbf, 1, &dbf_retries, sizeof(u64));
++	debug_event(adapter->abort_dbf, 1, &dbf_allowed, sizeof(u64));
++	debug_event(adapter->abort_dbf, 1, &dbf_timeout, sizeof(u64));
++	debug_event(adapter->abort_dbf, 1, &dbf_fsf_req, sizeof(u64));
++	debug_event(adapter->abort_dbf, 1, &dbf_fsf_status, sizeof(u64));
++	debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[0], sizeof(u64));
++	debug_event(adapter->abort_dbf, 1, &dbf_fsf_qual[1], sizeof(u64));
++	debug_text_event(adapter->abort_dbf, 1, dbf_result);
++#endif
++
++	spin_lock_irq(&io_request_lock);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_scsi_eh_device_reset_handler
++ *
++ * purpose:
++ *
++ * returns:
++ */
++int zfcp_scsi_eh_device_reset_handler(Scsi_Cmnd *scpnt)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	int retval;
++	zfcp_unit_t *unit = (zfcp_unit_t*) scpnt->device->hostdata;
++        /*TRACE*/
++	ZFCP_LOG_TRACE("enter (scpnt=0x%lx)\n", (unsigned long)scpnt);
++
++        spin_unlock_irq(&io_request_lock);
++	/*
++	 * We should not be called to reset a target which we 'sent' faked SCSI
++	 * commands since the abort of faked SCSI commands should always
++	 * succeed (simply delete timer). 
++	 */
++	if (!unit) {
++		ZFCP_LOG_NORMAL(
++			"bug: Tried to reset a non existant unit.\n");
++		retval = SUCCESS;
++		goto out;
++	}
++	ZFCP_LOG_NORMAL(
++		"Resetting SCSI device "
++		"(unit with FCP_LUN 0x%016Lx on the port with WWPN 0x%016Lx "
++		"on the adapter with devno 0x%04x)\n",
++		(llui_t)unit->fcp_lun,
++		(llui_t)unit->port->wwpn,
++		unit->port->adapter->devno);
++
++	/*
++	 * If we do not know whether the unit supports 'logical unit reset'
++	 * then try 'logical unit reset' and proceed with 'target reset'
++	 * if 'logical unit reset' fails.
++	 * If the unit is known not to support 'logical unit reset' then
++	 * skip 'logical unit reset' and try 'target reset' immediately.
++	 */
++	if (!atomic_test_mask(ZFCP_STATUS_UNIT_NOTSUPPUNITRESET, &unit->status)) { 
++		retval = zfcp_task_management_function(unit, LOGICAL_UNIT_RESET);
++		if (retval) {
++			ZFCP_LOG_DEBUG(
++				"logical unit reset failed (unit=0x%lx)\n",
++				(unsigned long)unit);
++			if (retval == -ENOTSUPP)
++				atomic_set_mask(ZFCP_STATUS_UNIT_NOTSUPPUNITRESET, 
++                                                &unit->status); 
++			/* fall through and try 'target reset' next */
++		} else	{
++			ZFCP_LOG_DEBUG(
++				"logical unit reset succeeded (unit=0x%lx)\n",
++				(unsigned long)unit);
++			/* avoid 'target reset' */
++			retval = SUCCESS;
++			goto out;
++		}
++	}
++	retval = zfcp_task_management_function(unit, TARGET_RESET);
++	if (retval) {
++		ZFCP_LOG_DEBUG(
++			"target reset failed (unit=0x%lx)\n",
++			(unsigned long)unit);
++		retval = FAILED;
++	} else	{
++		ZFCP_LOG_DEBUG(
++			"target reset succeeded (unit=0x%lx)\n",
++			(unsigned long)unit);
++		retval = SUCCESS;
++	}
++
++out:
++	spin_lock_irq(&io_request_lock);
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++static int zfcp_task_management_function(zfcp_unit_t *unit, u8 tm_flags)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	zfcp_adapter_t *adapter = unit->port->adapter;
++	int retval;
++	int status;
++	zfcp_fsf_req_t *fsf_req;
++
++	ZFCP_LOG_TRACE(
++		"enter (unit=0x%lx tm_flags=0x%x)\n",
++		(unsigned long)unit,
++		tm_flags);
++
++	/* issue task management function */	
++	fsf_req = zfcp_fsf_send_fcp_command_task_management
++	 		(adapter, unit, tm_flags, 0);
++	if (!fsf_req) {
++                ZFCP_LOG_INFO(
++			"error: Out of resources. Could not create a "
++			"task management (abort, reset, etc) request "
++			"for the unit with FCP_LUN 0x%016Lx connected to "
++			"the port with WWPN 0x%016Lx connected to "
++			"the adapter with devno 0x%04x.\n",
++			(llui_t)unit->fcp_lun,
++			(llui_t)unit->port->wwpn,
++			adapter->devno);
++		retval = -ENOMEM;
++		goto out;
++	}
++
++	retval = zfcp_fsf_req_wait_and_cleanup(
++			fsf_req,
++                        ZFCP_UNINTERRUPTIBLE,
++			&status);
++	/*
++	 * check completion status of task management function
++	 * (status should always be valid since no signals permitted)
++	 */
++        if (status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED)
++		retval = -EIO;
++	else	if (status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP)
++			retval = -ENOTSUPP;
++		else	retval = 0;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_scsi_eh_bus_reset_handler
++ *
++ * purpose:
++ *
++ * returns:
++ */
++int zfcp_scsi_eh_bus_reset_handler(Scsi_Cmnd *scpnt)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	int retval = 0;
++        zfcp_unit_t *unit;
++
++	ZFCP_LOG_TRACE("enter (scpnt=0x%lx)\n", (unsigned long)scpnt);
++        spin_unlock_irq(&io_request_lock);
++
++        unit = (zfcp_unit_t *)scpnt->device->hostdata; 
++        /*DEBUG*/
++	ZFCP_LOG_NORMAL(
++		"Resetting SCSI bus "
++		"(unit with FCP_LUN 0x%016Lx on the port with WWPN 0x%016Lx "
++		"on the adapter with devno 0x%04x)\n",
++		(llui_t)unit->fcp_lun,
++		(llui_t)unit->port->wwpn,
++		unit->port->adapter->devno);
++        zfcp_erp_adapter_reopen(unit->port->adapter, 0);
++	zfcp_erp_wait(unit->port->adapter);
++        retval = SUCCESS;
++
++	spin_lock_irq(&io_request_lock);
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_scsi_eh_host_reset_handler
++ *
++ * purpose:
++ *
++ * returns:
++ */
++int zfcp_scsi_eh_host_reset_handler(Scsi_Cmnd *scpnt)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	int retval = 0;
++        zfcp_unit_t *unit;
++
++	ZFCP_LOG_TRACE("enter (scpnt=0x%lx)\n", (unsigned long)scpnt);
++        spin_unlock_irq(&io_request_lock);
++
++        unit = (zfcp_unit_t *)scpnt->device->hostdata; 
++        /*DEBUG*/
++	ZFCP_LOG_NORMAL(
++		"Resetting SCSI host "
++		"(unit with FCP_LUN 0x%016Lx on the port with WWPN 0x%016Lx "
++		"on the adapter with devno 0x%04x)\n",
++		(llui_t)unit->fcp_lun,
++		(llui_t)unit->port->wwpn,
++		unit->port->adapter->devno);
++        zfcp_erp_adapter_reopen(unit->port->adapter, 0);
++	zfcp_erp_wait(unit->port->adapter);
++        retval=SUCCESS;
++
++	spin_lock_irq(&io_request_lock);
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_adapter_detect
++ *
++ * purpose:	checks whether the specified zSeries device is
++ *		a supported adapter
++ *
++  * returns:	0 - for supported adapter
++ *		!0 - for unsupported devices
++ */
++int zfcp_adapter_detect(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_OTHER
++     
++        int retval = 0;
++
++	ZFCP_LOG_TRACE("enter: (adapter=0x%lx)\n", (unsigned long)adapter);
++        retval = get_dev_info_by_devno(adapter->devno, &adapter->devinfo);
++        if (retval) {
++		ZFCP_LOG_INFO(
++			"warning: Device information for the adapter "
++			"with devno 0x%04x could not be determined. "
++			"The attempt returned %d. It is probable that "
++			"no device with this devno exists.\n",
++			adapter->devno,
++			retval);
++		goto out;
++        }
++        
++        if (adapter->devinfo.status == 0){
++		ZFCP_LOG_TRACE(
++			"Adapter returned \"OK\", " 
++			"devno is 0x%04x.\n", 
++			(unsigned int) adapter->devno);
++		goto ok;
++        }
++        if (adapter->devinfo.status & DEVSTAT_NOT_OPER) {
++                ZFCP_LOG_INFO(
++			"error: Adapter with devno 0x%04x is not "
++			"operational.\n",
++			(unsigned int) adapter->devno);
++		retval = -EBUSY;
++        }
++        if (adapter->devinfo.status & DEVSTAT_DEVICE_OWNED) {
++		ZFCP_LOG_INFO(
++			"error: Adapter with devno 0x%04x is already "
++			"owned by another driver.\n",
++			(unsigned int) adapter->devno);
++		retval = -EACCES;
++        }
++        if (adapter->devinfo.status & DEVSTAT_UNKNOWN_DEV) {
++		ZFCP_LOG_INFO(
++			"error: Adapter with devno 0x%04x is not "
++			"an FCP card.\n",
++			(unsigned int) adapter->devno);
++		retval = -EACCES;
++        }
++        if (adapter->devinfo.status & (~(DEVSTAT_NOT_OPER |
++                                        DEVSTAT_DEVICE_OWNED |
++                                        DEVSTAT_UNKNOWN_DEV))){
++		ZFCP_LOG_NORMAL(
++			"bug: Adapter with devno 0x%04x returned an "
++			"unexpected condition during the identification "
++			"phase. (debug info %d)\n",
++			(unsigned int) adapter->devno,
++			adapter->devinfo.status);
++		retval = -ENODEV;
++        }
++        if (retval < 0)
++		goto out;
++ok:
++        if ((adapter->devinfo.sid_data.cu_type != ZFCP_CONTROL_UNIT_TYPE) ||
++            (adapter->devinfo.sid_data.cu_model != ZFCP_CONTROL_UNIT_MODEL) ||
++            (adapter->devinfo.sid_data.dev_type != ZFCP_DEVICE_TYPE) ||
++           ((adapter->devinfo.sid_data.dev_model != ZFCP_DEVICE_MODEL) &&
++            (adapter->devinfo.sid_data.dev_model != ZFCP_DEVICE_MODEL_PRIV))) {
++		ZFCP_LOG_NORMAL(
++			"error: Adapter with devno 0x%04x is not "
++			"an FCP card.\n",
++			(unsigned int) adapter->devno);
++		retval = -ENODEV;
++        }
++        
++out:
++        ZFCP_LOG_TRACE(
++		"CU type, model, dev type, model" 
++		" 0x%x, 0x%x, 0x%x, 0x%x.\n", 
++		adapter->devinfo.sid_data.cu_type,
++		adapter->devinfo.sid_data.cu_model,
++		adapter->devinfo.sid_data.dev_type,
++		adapter->devinfo.sid_data.dev_model);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_adapter_irq_register(zfcp_adapter_t* adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++	int retval = 0;
++	signed int tmp_irq;     /* adapter->irq is unsigned 16 bit! */
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	/* find out IRQ */
++	tmp_irq = get_irq_by_devno(adapter->devno);
++
++        if (tmp_irq < 0 || tmp_irq > 0x0FFFF) {
++		ZFCP_LOG_NORMAL(
++			"bug: The attempt to identify the irq for the "
++			"adapter with devno 0x%04x failed. All map entries "
++			"containing this devno are ignored. "
++			"(debug info 0x%x)\n",
++			adapter->devno,
++			tmp_irq);
++		retval = -ENXIO;
++		goto out;
++        }
++	ZFCP_LOG_TRACE(
++		"get_irq_by_devno returned irq=0x%x.\n",
++		tmp_irq);
++	adapter->irq = tmp_irq;
++
++	/* request IRQ */
++	retval = s390_request_irq_special(
++			adapter->irq,
++			(void *)zfcp_cio_handler,
++			zfcp_dio_not_oper_handler,
++			0,
++			zfcp_data.scsi_host_template.name,
++			(void *)&adapter->devstat);
++	if (retval) {
++		ZFCP_LOG_INFO(
++			"error: Could not allocate irq %i to the adapter "
++                        "with devno 0x%04x (debug info %i).\n",
++			adapter->irq,
++                        adapter->devno,
++			retval);
++		goto out;
++	}
++	atomic_set_mask(ZFCP_STATUS_ADAPTER_IRQOWNER, &adapter->status);
++        ZFCP_LOG_DEBUG("request irq %i successfull\n", adapter->irq);
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_adapter_irq_unregister(zfcp_adapter_t* adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n", (unsigned long)adapter);
++
++        if(!atomic_test_mask(ZFCP_STATUS_ADAPTER_IRQOWNER, &adapter->status)) {
++                ZFCP_LOG_DEBUG("Adapter with devno 0x%04x does not own "
++                               "an irq, skipping over freeing attempt.\n",
++                               adapter->devno);
++                goto out;    
++        }
++        /* Note: There exists no race condition when the irq is given up by some
++           other agency while at this point. The CIO layer will still handle the
++           subsequent free_irq correctly.
++        */
++	free_irq(adapter->irq, (void *) &adapter->devstat);
++	atomic_clear_mask(ZFCP_STATUS_ADAPTER_IRQOWNER, &adapter->status);
++	ZFCP_LOG_DEBUG("gave up irq=%i\n", adapter->irq);
++ out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_adapter_scsi_register(zfcp_adapter_t* adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx)\n",
++		(unsigned long)adapter);
++
++	/* register adapter as SCSI host with mid layer of SCSI stack */
++	adapter->scsi_host = scsi_register(
++				&zfcp_data.scsi_host_template,
++				sizeof(zfcp_adapter_t*));
++	if (!adapter->scsi_host) {
++		ZFCP_LOG_NORMAL(
++			"error: Not enough free memory. "
++			"Could not register host-adapter with "
++			"devno 0x%04x with the SCSI-stack.\n",
++			adapter->devno);
++		retval = -EIO;
++		goto out;
++	} 
++	atomic_set_mask(ZFCP_STATUS_ADAPTER_REGISTERED, &adapter->status);
++	ZFCP_LOG_DEBUG(
++		"host registered, scsi_host at 0x%lx\n",
++		(unsigned long)adapter->scsi_host);
++
++	/* tell the SCSI stack some characteristics of this adapter */
++	adapter->scsi_host->max_id = adapter->max_scsi_id + 1;
++	adapter->scsi_host->max_lun = adapter->max_scsi_lun + 1;
++	adapter->scsi_host->max_channel = 0;
++	adapter->scsi_host->irq = adapter->irq;
++	adapter->scsi_host->unique_id = adapter->devno;
++	adapter->scsi_host->max_cmd_len = ZFCP_MAX_SCSI_CMND_LENGTH;
++	adapter->scsi_host->loaded_as_module
++		= (zfcp_data.scsi_host_template.module ? 1 : 0);
++	adapter->scsi_host->select_queue_depths
++		= zfcp_scsi_select_queue_depth;
++
++	/*
++	 * save a pointer to our own adapter data structure within
++	 * hostdata field of SCSI host data structure
++	 */
++	adapter->scsi_host->hostdata[0] = (unsigned long)adapter;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++int zfcp_initialize_with_0copy(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
++
++        int retval = 0;
++        qdio_initialize_t init_data;
++
++	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n", (unsigned long)adapter);
++                
++        init_data.irq = adapter->irq;
++        init_data.q_format = QDIO_SCSI_QFMT;
++        memcpy(init_data.adapter_name,&adapter->name,8);
++        init_data.qib_param_field_format = 0;
++        init_data.qib_param_field = NULL;
++        init_data.input_slib_elements = NULL;
++        init_data.output_slib_elements = NULL;
++        init_data.min_input_threshold = ZFCP_MIN_INPUT_THRESHOLD;
++        init_data.max_input_threshold = ZFCP_MAX_INPUT_THRESHOLD;
++        init_data.min_output_threshold = ZFCP_MIN_OUTPUT_THRESHOLD;
++        init_data.max_output_threshold = ZFCP_MAX_OUTPUT_THRESHOLD;
++        init_data.no_input_qs = 1;
++        init_data.no_output_qs = 1;
++        init_data.input_handler = zfcp_qdio_response_handler;
++        init_data.output_handler = zfcp_qdio_request_handler;
++        init_data.int_parm = (unsigned long)adapter;
++        init_data.flags = QDIO_INBOUND_0COPY_SBALS|
++                QDIO_OUTBOUND_0COPY_SBALS|
++                QDIO_USE_OUTBOUND_PCIS;
++        init_data.input_sbal_addr_array = 
++                (void **)(adapter->response_queue.buffer);
++        init_data.output_sbal_addr_array = 
++                (void **)(adapter->request_queue.buffer);
++        ZFCP_LOG_TRACE("Before qdio_initialise\n");
++	retval = qdio_initialize(&init_data);
++        ZFCP_LOG_TRACE("After qdio_initialise\n");
++        
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ *
++ * note:	qdio queues shall be down (no ongoing inbound processing)
++ */
++static int zfcp_fsf_req_dismiss_all(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++
++	int retval = 0;
++	zfcp_fsf_req_t *fsf_req, *next_fsf_req;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx)\n",
++		(unsigned long)adapter);
++
++	ZFCP_FOR_NEXT_FSFREQ(adapter, fsf_req, next_fsf_req)
++		zfcp_fsf_req_dismiss(fsf_req);
++	while (!list_empty(&adapter->fsf_req_list_head)) {
++		ZFCP_LOG_DEBUG(
++			"fsf req list of adapter with "
++			"devno 0x%04x not yet empty\n",
++			adapter->devno);
++		/* wait for woken intiators to clean up their requests */
++		set_current_state(TASK_UNINTERRUPTIBLE);
++		schedule_timeout(ZFCP_FSFREQ_CLEANUP_TIMEOUT);
++	}
++
++	/* consistency check */
++        if (atomic_read(&adapter->fsf_reqs_active)) {
++                ZFCP_LOG_NORMAL(
++			"bug: There are still %d FSF requests pending "
++			"on the adapter with devno 0x%04x after "
++			"cleanup.\n",
++			atomic_read(&adapter->fsf_reqs_active),
++			adapter->devno);
++		atomic_set(&adapter->fsf_reqs_active, 0);
++        }
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_fsf_req_dismiss(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++	fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
++	zfcp_fsf_req_complete(fsf_req);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:   	zfcp_qdio_handler_error_check
++ *
++ * purpose:     called by the response handler to determine error condition
++ *
++ * returns:	error flag
++ *
++ */
++inline int zfcp_qdio_handler_error_check(
++        zfcp_adapter_t *adapter,
++	unsigned int status,
++	unsigned int qdio_error,
++	unsigned int siga_error)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
++
++     int retval=0;
++
++     ZFCP_LOG_TRACE(
++          "enter (adapter=0x%lx, status=%i qdio_error=%i siga_error=%i\n",
++          (unsigned long) adapter,
++          status,
++          qdio_error,
++          siga_error);
++
++     if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_TRACE)){     
++             if (status &  QDIO_STATUS_INBOUND_INT){
++                     ZFCP_LOG_TRACE("status is"
++                                    " QDIO_STATUS_INBOUND_INT \n");
++             }
++             if (status & QDIO_STATUS_OUTBOUND_INT){
++                     ZFCP_LOG_TRACE("status is"
++                                    " QDIO_STATUS_OUTBOUND_INT \n");
++             }
++     }// if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_TRACE))
++     if (status & QDIO_STATUS_LOOK_FOR_ERROR){
++             retval=-EIO;
++             
++             ZFCP_LOG_FLAGS(1,"QDIO_STATUS_LOOK_FOR_ERROR \n");
++
++             ZFCP_LOG_INFO("A qdio problem occured. The status, qdio_error and "
++                           "siga_error are 0x%x, 0x%x and 0x%x\n", 
++                           status,
++                           qdio_error,
++                           siga_error); 
++
++             if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION){
++                     ZFCP_LOG_FLAGS(2, "QDIO_STATUS_ACTIVATE_CHECK_CONDITION\n");
++             }
++             if (status & QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR){
++                     ZFCP_LOG_FLAGS(2, "QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR\n");
++             }
++             if (status & QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR){
++                     ZFCP_LOG_FLAGS(2, "QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR\n");
++             }
++             
++             if (siga_error & QDIO_SIGA_ERROR_ACCESS_EXCEPTION) {
++                     ZFCP_LOG_FLAGS(2, "QDIO_SIGA_ERROR_ACCESS_EXCEPTION\n"); 
++             }
++             
++             if (siga_error & QDIO_SIGA_ERROR_B_BIT_SET) {
++                     ZFCP_LOG_FLAGS(2, "QDIO_SIGA_ERROR_B_BIT_SET\n"); 
++             }
++
++             switch (qdio_error) {
++             case 0:
++                     ZFCP_LOG_FLAGS(3, "QDIO_OK");
++                     break;
++             case SLSB_P_INPUT_ERROR :
++                     ZFCP_LOG_FLAGS(1, "SLSB_P_INPUT_ERROR\n");
++                     break;
++             case SLSB_P_OUTPUT_ERROR :
++                     ZFCP_LOG_FLAGS(1, "SLSB_P_OUTPUT_ERROR\n"); 
++                     break;
++             default :
++                     ZFCP_LOG_NORMAL("bug: Unknown qdio error reported "
++                                     "(debug info 0x%x)\n",
++                                     qdio_error);
++                     break;
++             }
++             /* Restarting IO on the failed adapter from scratch */
++             debug_text_event(adapter->erp_dbf,1,"qdio_err");
++		/*
++		 * Since we have been using this adapter, it is save to assume
++		 * that it is not failed but recoverable. The card seems to
++		 * report link-up events by self-initiated queue shutdown.
++		 * That is why we need to clear the the link-down flag
++		 * which is set again in case we have missed by a mile.
++		 */
++		zfcp_erp_adapter_reopen(
++			adapter, 
++			ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
++			ZFCP_STATUS_COMMON_ERP_FAILED);
++     } // if(status & QDIO_STATUS_LOOK_FOR_ERROR)
++
++     ZFCP_LOG_TRACE("exit (%i)\n", retval);             
++     return retval;
++     
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_qdio_request_handler
++ *
++ * purpose:	is called by QDIO layer for completed SBALs in request queue
++ *
++ * returns:	(void)
++ */
++void zfcp_qdio_request_handler(
++	int irq,
++	unsigned int status,
++	unsigned int qdio_error,
++	unsigned int siga_error,
++	unsigned int queue_number,
++	int first_element,
++	int elements_processed,
++	unsigned long int_parm)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
++
++	zfcp_adapter_t *adapter;
++	zfcp_qdio_queue_t *queue;
++
++	ZFCP_LOG_TRACE(
++		"enter (irq=%i status=%i qdio_error=%i siga_error=%i "
++		"queue_number=%i first_element=%i elements_processed=%i "
++		"int_parm=0x%lx)\n",
++		irq,
++		status,
++		qdio_error,
++		siga_error,
++		queue_number,
++		first_element,
++		elements_processed,
++		int_parm);
++
++	adapter = (zfcp_adapter_t*)int_parm;
++      	queue = &adapter->request_queue;
++
++        ZFCP_LOG_DEBUG("devno=0x%04x, first=%d, count=%d\n",
++                        adapter->devno,
++                        first_element,
++                        elements_processed);
++
++	if (zfcp_qdio_handler_error_check(adapter, status, qdio_error, siga_error))
++		goto out;        
++
++	/* cleanup all SBALs being program-owned now */
++	zfcp_zero_sbals(
++	        queue->buffer, 
++		first_element, 
++		elements_processed);
++
++	/* increase free space in outbound queue */
++	atomic_add(elements_processed, &queue->free_count);
++        ZFCP_LOG_DEBUG("free_count=%d\n",
++                       atomic_read(&queue->free_count));
++        wake_up(&adapter->request_wq);
++	ZFCP_LOG_DEBUG(
++		"Elements_processed = %d, free count=%d \n",
++		elements_processed,
++		atomic_read(&queue->free_count));
++
++out: 
++	ZFCP_LOG_TRACE("exit\n");
++
++	return;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:   	zfcp_qdio_response_handler
++ *
++ * purpose:	is called by QDIO layer for completed SBALs in response queue
++ *
++ * returns:	(void)
++ */
++void zfcp_qdio_response_handler(
++	int irq,
++	unsigned int status,
++	unsigned int qdio_error,
++	unsigned int siga_error,
++	unsigned int queue_number,
++	int first_element,
++	int elements_processed,
++	unsigned long int_parm)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
++
++	zfcp_adapter_t *adapter;
++	zfcp_qdio_queue_t *queue;
++	int buffer_index;
++	int i;
++	qdio_buffer_t *buffer;
++	int retval = 0;
++	u8 count;
++	u8 start;
++        volatile qdio_buffer_element_t *buffere=NULL;
++	int buffere_index;
++
++	ZFCP_LOG_TRACE(
++		"enter (irq=0x%x status=0x%x qdio_error=0x%x siga_error=0x%x "
++		"queue_number=%i first_element=%i elements_processed=%i "
++		"int_parm=0x%lx)\n",
++		irq,
++		status,
++		qdio_error,
++		siga_error,
++		queue_number,
++		first_element,
++		elements_processed,
++		int_parm);
++        
++	adapter = (zfcp_adapter_t*)int_parm;
++      	queue = &adapter->response_queue;
++
++        if (zfcp_qdio_handler_error_check(adapter, status, qdio_error, siga_error))
++                goto out;        
++
++        buffere = &(queue->buffer[first_element]->element[0]);
++        ZFCP_LOG_DEBUG("first BUFFERE flags=0x%x \n",
++                       buffere->flags);
++	/*
++	 * go through all SBALs from input queue currently
++	 * returned by QDIO layer
++         */ 
++
++	for (i = 0; i < elements_processed; i++) {
++
++		buffer_index = first_element + i;
++		buffer_index %= QDIO_MAX_BUFFERS_PER_Q;
++		buffer = queue->buffer[buffer_index];
++
++		/* go through all SBALEs of SBAL */
++                for(buffere_index = 0;
++                    buffere_index < QDIO_MAX_ELEMENTS_PER_BUFFER;
++                    buffere_index++) {
++
++			/* look for QDIO request identifiers in SB */
++			buffere = &buffer->element[buffere_index];
++			retval = zfcp_qdio_reqid_check(adapter, 
++                                                       (void *)buffere->addr);
++
++			if (retval) {
++				ZFCP_LOG_NORMAL(
++					"bug: Inbound packet seems not to have "
++					"been sent at all. It will be ignored."
++					"(debug info 0x%lx, 0x%lx, %d, %d, 0x%x)\n",
++					(unsigned long)buffere->addr,
++					(unsigned long)&(buffere->addr),
++                                        first_element,
++                                        elements_processed,
++					adapter->devno);
++
++				ZFCP_LOG_NORMAL(
++					"Dump of inbound BUFFER %d BUFFERE %d "
++					"at address 0x%lx\n",
++					buffer_index,
++                                        buffere_index, 
++					(unsigned long)buffer);
++				ZFCP_HEX_DUMP(
++					ZFCP_LOG_LEVEL_NORMAL,
++					(char*)buffer,
++					SBAL_SIZE);
++			}
++                        if (buffere->flags & SBAL_FLAGS_LAST_ENTRY)
++				break;                        
++		};
++
++                if (!(buffere->flags & SBAL_FLAGS_LAST_ENTRY)) {
++			ZFCP_LOG_NORMAL("bug: End of inbound data not marked!\n");
++                } 
++	}
++
++	/*
++	 * put range of SBALs back to response queue
++	 * (including SBALs which have already been free before)
++	 */
++	count = atomic_read(&queue->free_count) + elements_processed;
++	start = queue->free_index;
++
++        ZFCP_LOG_TRACE(
++		"Calling do QDIO irq=0x%x,flags=0x%x, queue_no=%i, "
++		"index_in_queue=%i, count=%i, buffers=0x%lx\n",
++		irq,
++		QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
++		0,
++		start,
++		count,
++		(unsigned long)&queue->buffer[start]);	
++
++        retval = do_QDIO(
++                        irq,
++                        QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
++                        0,
++                        start,
++                        count,
++                        NULL);
++        if (retval) {
++		atomic_set(&queue->free_count, count);
++                ZFCP_LOG_DEBUG(
++			"Inbound data regions could not be cleared "
++                        "Transfer queues may be down. "
++                        "(info %d, %d, %d)\n",
++			count,
++			start,
++			retval);
++        } else  {
++		queue->free_index += count;
++		queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;
++		atomic_set(&queue->free_count, 0);
++                ZFCP_LOG_TRACE(
++			"%i buffers successfully enqueued to response queue "
++			"starting at position %i\n",
++			count,
++			start);
++        }
++
++out:
++        /*
++          ZFCP_LOG_DEBUG("response_queue->free_count=%i,response_queue->free_index=%i\n",
++          atomic_read(&queue->free_count),
++          queue->free_index) ;
++        */
++	ZFCP_LOG_TRACE("exit\n");
++ 
++	return;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_qdio_reqid_check
++ *
++ * purpose:	checks for valid reqids or unsolicited status
++ *
++ * returns:	0 - valid request id or unsolicited status
++ *		!0 - otherwise
++ */
++static inline int zfcp_qdio_reqid_check(zfcp_adapter_t *adapter, void *sbale_addr)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_QDIO
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_QDIO
++
++	zfcp_fsf_req_t *fsf_req;
++        int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (sbale_addr=0x%lx)\n",
++		(unsigned long)sbale_addr);
++
++#ifdef ZFCP_DEBUG_REQUESTS
++        /* Note: seq is entered later */
++        debug_text_event(adapter->req_dbf, 1, "i:a/seq");
++        debug_event(adapter->req_dbf, 1, &sbale_addr, sizeof(unsigned long));
++#endif /* ZFCP_DEBUG_REQUESTS */
++
++	/* invalid (per convention used in this driver) */
++	if (!sbale_addr) {
++		ZFCP_LOG_NORMAL(
++			"bug: Inbound data faulty, contains null-pointer!\n");
++		retval = -EINVAL;
++		goto out;
++        }
++
++	/* valid request id and thus (hopefully :) valid fsf_req address */
++	fsf_req = (zfcp_fsf_req_t*)sbale_addr;
++
++	ZFCP_PARANOIA {
++		if ((fsf_req->common_magic != ZFCP_MAGIC)
++                    ||(fsf_req->specific_magic != ZFCP_MAGIC_FSFREQ)) {
++			ZFCP_LOG_NORMAL(
++				"bug: An inbound FSF acknowledgement was "
++				"faulty (debug info 0x%x, 0x%x, 0x%lx)\n",
++				fsf_req->common_magic,
++				fsf_req->specific_magic,
++				(unsigned long)fsf_req);
++			retval = -EINVAL;
++                        //                        panic("void of grace");
++			goto out;
++		}
++
++		if (adapter != fsf_req->adapter) {
++			ZFCP_LOG_NORMAL(
++				"bug: An inbound FSF acknowledgement was not "
++				"correct (debug info 0x%lx, 0x%lx, 0%lx) \n",
++				(unsigned long)fsf_req,
++				(unsigned long)fsf_req->adapter,
++                                (unsigned long)adapter);
++			retval = -EINVAL;
++			goto out;
++		}
++	}
++
++#ifdef ZFCP_DEBUG_REQUESTS
++	/* debug feature stuff (test for QTCB: remember new unsol. status!) */
++	if (fsf_req->qtcb) {
++                debug_event(adapter->req_dbf, 1, &fsf_req->qtcb->prefix.req_seq_no,
++                            sizeof(u32));
++	}
++#endif /* ZFCP_DEBUG_REQUESTS */
++
++	ZFCP_LOG_TRACE(
++		"fsf_req at 0x%lx, QTCB at 0x%lx\n",
++		(unsigned long)fsf_req,
++		(unsigned long)fsf_req->qtcb);
++	if (fsf_req->qtcb) {
++		ZFCP_LOG_TRACE("HEX DUMP OF 1ST BUFFERE PAYLOAD (QTCB):\n");
++		ZFCP_HEX_DUMP(
++			ZFCP_LOG_LEVEL_TRACE,
++			(char*)fsf_req->qtcb,
++			sizeof(fsf_qtcb_t));
++	}
++
++	/* finish the FSF request */
++	zfcp_fsf_req_complete(fsf_req);
++          
++out:
++	ZFCP_LOG_TRACE("exit \n");
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_activate_adapter
++ *
++ * purpose:
++ *
++ * returns:
++ */
++inline static void zfcp_activate_adapter(int irq)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_DIO
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_DIO
++
++	int devno;
++	zfcp_adapter_t *adapter;
++
++	ZFCP_LOG_TRACE("enter (irq=%i)\n", irq);
++
++	devno = get_devno_by_irq(irq);
++	ZFCP_LOG_TRACE("devno is 0x%04x\n",devno);
++
++	/* Find the new adapter and open it */
++	ZFCP_FOR_EACH_ADAPTER(adapter) {
++		if (adapter->devno == devno) {
++			ZFCP_LOG_INFO(
++				"The adapter with devno 0x%04x "
++				"will now be activated.\n",
++				devno);
++			debug_text_event(adapter->erp_dbf,1,"activate");
++			zfcp_erp_modify_adapter_status(
++				adapter,
++				ZFCP_STATUS_COMMON_RUNNING,
++				ZFCP_SET);
++			zfcp_erp_adapter_reopen(
++				adapter,
++				ZFCP_STATUS_COMMON_ERP_FAILED);
++		}
++	}
++	if (!adapter)
++		ZFCP_LOG_DEBUG(
++			"An unconfigured adapter has become "
++			"active, it's devno 0x%04x.\n",
++			devno);
++
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_dio_oper_handler
++ *
++ * purpose:
++ *
++ * returns:
++ */
++static int zfcp_dio_oper_handler(int irq, devreg_t *dreg)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_DIO
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_DIO
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (irq=%i, dreg=0x%lx)\n",
++		irq, (unsigned long)dreg);
++
++        zfcp_activate_adapter(irq);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_dio_not_oper_handler
++ *
++ * purpose:
++ *
++ * returns:
++ */
++static void zfcp_dio_not_oper_handler(int irq,  int status)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_DIO
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_DIO
++        
++        zfcp_adapter_t *adapter;
++        int known=0;
++
++	ZFCP_LOG_TRACE(
++		"enter (irq=%i, status=%i)\n",
++		irq, status);
++
++        ZFCP_FOR_EACH_ADAPTER(adapter) {
++                if(atomic_test_mask(ZFCP_STATUS_ADAPTER_IRQOWNER, &adapter->status) &&
++                   (adapter->irq==irq)) {
++                        known=1;
++                        break;
++                }
++        }
++
++        switch (status) {
++        case DEVSTAT_DEVICE_GONE:
++                ZFCP_LOG_FLAGS(1,"DEVSTAT_DEVICE_GONE\n");
++        case DEVSTAT_NOT_OPER:
++                ZFCP_LOG_FLAGS(1,"DEVSTAT_NOT_OPER\n");
++                if (!known) {
++                        ZFCP_LOG_DEBUG("An unconfigured or an already "
++                                       "disabled adapter became "
++                                       "unoperational on irq 0x%x.\n",
++                                       irq);
++                        goto out;
++                }
++                ZFCP_LOG_INFO("The adapter with devno 0x%04x became "
++                              "unoperational.\n",
++                              adapter->devno);
++                /* shut-down the adapter and wait for completion */
++                debug_text_event(adapter->erp_dbf,1,"not_oper");
++                zfcp_erp_adapter_shutdown(adapter, ZFCP_STATUS_ADAPTER_IRQOWNER);
++                zfcp_erp_wait(adapter);
++                break;
++        case DEVSTAT_REVALIDATE:
++                ZFCP_LOG_FLAGS(1,"DEVSTAT_REVALIDATE\n");
++                /* The irq should still be that of the old adapter */
++                if(known) {
++                        ZFCP_LOG_INFO("The adapter with devno 0x%04x became "
++                                      "unoperational.\n",
++                                      adapter->devno);
++                        /* shut-down the adapter and wait for completion */
++                        /* Note: This adapter is not the real IRQ-owner anymore 
++                         * The ERP strategy requires the IRQ to be freed somehow
++                         * though 
++                         */
++                        debug_text_event(adapter->erp_dbf,1,"reval");
++                        zfcp_erp_adapter_shutdown(adapter, 0);
++                        zfcp_erp_wait(adapter);
++                } else {
++                        ZFCP_LOG_DEBUG("An unconfigured adapter was the "
++                                       "origin of a VM define, it's irq 0x%x.\n",
++                                       irq);
++                }
++                /* The new adapter already owns the irq and needs to be activated  */
++                zfcp_activate_adapter(irq);
++                break;
++        default:
++                ZFCP_LOG_NORMAL("bug: Common I/O layer presented information  "
++                               "unknown to the zfcp module (debug info "
++                               "0x%x, 0x%x)\n",
++                               irq,
++                               status);
++        }      
++ out:
++        ZFCP_LOG_TRACE("exit\n");
++	
++        return;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_cio_handler
++ *
++ * purpose:
++ *
++ * returns:
++ */
++static void zfcp_cio_handler(int irq, void *devstat, struct pt_regs *rgs)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_DIO
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_DIO
++
++	ZFCP_LOG_TRACE(
++		"enter (irq=%i, devstat=0%lx, pt_regs=0%lx)\n",
++		irq, (unsigned long)devstat,
++		(unsigned long)rgs);
++        ZFCP_LOG_DEBUG("Normally, this function would never be called. "
++                       "(info 0x%x, 0x%lx, 0x%lx)\n",
++                       irq,
++                       (unsigned long)devstat,
++                       (unsigned long)rgs);
++	ZFCP_LOG_TRACE("exit\n");
++
++	return;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_req_complete
++ *
++ * purpose:	Updates active counts and timers for openfcp-reqs
++ *              May cleanup request after req_eval returns
++ *
++ * returns:	0 - success
++ *		!0 - failure
++ *
++ * context:	
++ */
++static int zfcp_fsf_req_complete(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = 0;
++        int cleanup;
++        zfcp_adapter_t *adapter = fsf_req->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++	/* do some statistics */
++	atomic_dec(&adapter->fsf_reqs_active);
++
++	if (fsf_req->fsf_command == FSF_QTCB_UNSOLICITED_STATUS) {
++                ZFCP_LOG_DEBUG("Status read response received\n");
++                /* Note: all cleanup handling is done in the callchain of 
++                   the function call-chain below.
++                */
++		zfcp_fsf_status_read_handler(fsf_req);
++                goto out;
++	} else	zfcp_fsf_protstatus_eval(fsf_req);
++
++        /*
++	 * fsf_req may be deleted due to waking up functions, so 
++         * cleanup is saved here and used later 
++         */
++        if (fsf_req->status & ZFCP_STATUS_FSFREQ_CLEANUP) 
++                cleanup = 1;
++        else    cleanup = 0;        
++
++        fsf_req->status |= ZFCP_STATUS_FSFREQ_COMPLETED;
++        
++        /* cleanup request if requested by initiator */
++        if (cleanup) {
++                ZFCP_LOG_TRACE(
++			"removing FSF request 0x%lx\n",
++			(unsigned long)fsf_req);
++		/*
++		 * lock must not be held here since it will be
++		 * grabed by the called routine, too
++		 */
++		if (zfcp_fsf_req_cleanup(fsf_req)) {
++			ZFCP_LOG_NORMAL(
++				"bug: Could not remove one FSF "
++				"request. Memory leakage possible. "
++				"(debug info 0x%lx).\n",
++				(unsigned long)fsf_req);
++		}
++        } else {
++                /* notify initiator waiting for the requests completion */
++                ZFCP_LOG_TRACE(
++                    "waking initiator of FSF request 0x%lx\n",
++                    (unsigned long)fsf_req);
++                wake_up(&fsf_req->completion_wq);
++        }
++        
++ out:
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++        return retval;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_protstatus_eval
++ *
++ * purpose:	evaluates the QTCB of the finished FSF request
++ *		and initiates appropriate actions
++ *		(usually calling FSF command specific handlers)
++ *
++ * returns:	
++ *
++ * context:	
++ *
++ * locks:
++ */
++static int zfcp_fsf_protstatus_eval(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++     
++	int retval = 0;
++	zfcp_adapter_t *adapter = fsf_req->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++	ZFCP_LOG_DEBUG(
++		"QTCB is at 0x%lx\n",
++		(unsigned long)fsf_req->qtcb);
++
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
++		ZFCP_LOG_DEBUG(
++			"fsf_req 0x%lx has been dismissed\n",
++			(unsigned long)fsf_req);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR |
++				   ZFCP_STATUS_FSFREQ_RETRY;	/* only for SCSI cmnds. */
++		zfcp_cmd_dbf_event_fsf("dismiss", fsf_req, NULL, 0);
++		goto skip_protstatus;
++	}
++
++	/* log additional information provided by FSF (if any) */
++	if (fsf_req->qtcb->header.log_length) {
++		/* do not trust them ;-) */
++		if (fsf_req->qtcb->header.log_start > sizeof(fsf_qtcb_t)) {
++			ZFCP_LOG_NORMAL(
++				"bug: ULP (FSF logging) log data starts "
++                                "beyond end of packet header. Ignored. "
++				"(start=%i, size=%li)\n",
++				fsf_req->qtcb->header.log_start,
++				sizeof(fsf_qtcb_t));
++			goto forget_log;
++		}
++		if ((size_t)(fsf_req->qtcb->header.log_start +
++		     fsf_req->qtcb->header.log_length)
++		    > sizeof(fsf_qtcb_t)) {
++			ZFCP_LOG_NORMAL(
++				"bug: ULP (FSF logging) log data ends "
++                                "beyond end of packet header. Ignored. "
++				"(start=%i, length=%i, size=%li)\n",
++				fsf_req->qtcb->header.log_start,
++				fsf_req->qtcb->header.log_length,
++				sizeof(fsf_qtcb_t));
++			goto forget_log;
++		}
++		ZFCP_LOG_TRACE("ULP log data: \n");
++		ZFCP_HEX_DUMP(
++			ZFCP_LOG_LEVEL_TRACE,
++			(char*)fsf_req->qtcb + fsf_req->qtcb->header.log_start,
++			fsf_req->qtcb->header.log_length);
++	}
++forget_log:
++
++	/* evaluate FSF Protocol Status */
++	switch (fsf_req->qtcb->prefix.prot_status) {
++
++		case FSF_PROT_GOOD :
++			ZFCP_LOG_TRACE("FSF_PROT_GOOD\n");
++			break;
++
++		case FSF_PROT_FSF_STATUS_PRESENTED :
++			ZFCP_LOG_TRACE("FSF_PROT_FSF_STATUS_PRESENTED\n");
++			break;
++
++		case FSF_PROT_QTCB_VERSION_ERROR :
++			ZFCP_LOG_FLAGS(0, "FSF_PROT_QTCB_VERSION_ERROR\n");
++			/* DEBUG */
++			ZFCP_LOG_NORMAL(
++				"fsf_req=0x%lx, qtcb=0x%lx\n",
++				(unsigned long)fsf_req,
++				(unsigned long)fsf_req->qtcb);
++			ZFCP_HEX_DUMP(
++				ZFCP_LOG_LEVEL_NORMAL,
++				(char*)fsf_req,
++				sizeof(zfcp_fsf_req_t));
++			ZFCP_LOG_NORMAL(
++				"error: The adapter with devno 0x%04x contains "
++                                "microcode of version 0x%x, the device driver "
++                                "only supports 0x%x. Aborting.\n",
++				adapter->devno,
++				fsf_req->qtcb->prefix.prot_status_qual.version_error.fsf_version,
++				ZFCP_QTCB_VERSION);
++			/* stop operation for this adapter */
++                        debug_text_exception(adapter->erp_dbf,0,"prot_ver_err");
++                        zfcp_erp_adapter_shutdown(adapter, 0);
++			zfcp_cmd_dbf_event_fsf(
++				"qverserr", fsf_req,
++				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			break;
++
++		case FSF_PROT_SEQ_NUMB_ERROR :
++			ZFCP_LOG_FLAGS(0, "FSF_PROT_SEQ_NUMB_ERROR\n");
++			ZFCP_LOG_NORMAL(
++				"bug: Sequence number mismatch between "
++				"driver (0x%x) and adapter of devno 0x%04x "
++				"(0x%x). Restarting all operations on this "
++                                "adapter.\n",
++				fsf_req->qtcb->prefix.req_seq_no,
++                                adapter->devno,
++				fsf_req->qtcb->prefix.prot_status_qual.sequence_error.exp_req_seq_no);
++#ifdef ZFCP_DEBUG_REQUESTS
++                        debug_text_event(adapter->req_dbf, 1, "exp_seq!");
++                        debug_event(adapter->req_dbf, 1, &fsf_req->qtcb->prefix.prot_status_qual.sequence_error.exp_req_seq_no, 4);
++                        debug_text_event(adapter->req_dbf, 1, "qtcb_seq!");
++                        debug_exception(adapter->req_dbf, 1, &fsf_req->qtcb->prefix.req_seq_no, 4);
++#endif /* ZFCP_DEBUG_REQUESTS */
++                        debug_text_exception(adapter->erp_dbf,0,"prot_seq_err");
++			/* restart operation on this adapter */
++                        zfcp_erp_adapter_reopen(adapter,0);
++			zfcp_cmd_dbf_event_fsf(
++				"seqnoerr", fsf_req,
++				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY;
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			break;
++
++		case FSF_PROT_UNSUPP_QTCB_TYPE :
++			ZFCP_LOG_FLAGS(0, "FSF_PROT_UNSUP_QTCB_TYPE\n");
++			ZFCP_LOG_NORMAL("error: Packet header type used by the "
++                                        "device driver is incompatible with "
++                                        "that used on the adapter with devno "
++                                        "0x%04x. "
++                                        "Stopping all operations on this adapter.\n", 
++                                        adapter->devno);
++			ZFCP_LOG_NORMAL(
++				"fsf_req=0x%lx, qtcb=0x%lx\n",
++				(unsigned long)fsf_req,
++				(unsigned long)fsf_req->qtcb);
++			ZFCP_HEX_DUMP(
++				ZFCP_LOG_LEVEL_NORMAL,
++				(char*)fsf_req,
++				sizeof(zfcp_fsf_req_t));
++                        debug_text_exception(adapter->erp_dbf,0,"prot_unsup_qtcb");
++                        zfcp_erp_adapter_shutdown(adapter, 0);
++			zfcp_cmd_dbf_event_fsf(
++				"unsqtcbt", fsf_req,
++				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			break;
++
++		case FSF_PROT_HOST_CONNECTION_INITIALIZING :
++			ZFCP_LOG_FLAGS(1, "FSF_PROT_HOST_CONNECTION_INITIALIZING\n");
++			zfcp_cmd_dbf_event_fsf(
++				"hconinit", fsf_req,
++				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			atomic_set_mask(ZFCP_STATUS_ADAPTER_HOST_CON_INIT, 
++                                        &(adapter->status));
++                        debug_text_event(adapter->erp_dbf,4,"prot_con_init");
++			break;
++
++		case FSF_PROT_DUPLICATE_REQUEST_ID :
++			ZFCP_LOG_FLAGS(0, "FSF_PROT_DUPLICATE_REQUEST_IDS\n");
++			if (fsf_req->qtcb) {
++				ZFCP_LOG_NORMAL(
++					"bug: The request identifier 0x%Lx "
++                                	"to the adapter with devno 0x%04x is " 
++					"ambiguous. "
++                                        "Stopping all operations on this adapter.\n",
++        	                        *(llui_t*)(&fsf_req->qtcb->bottom.support.req_handle),
++					adapter->devno);
++			} else	{
++				ZFCP_LOG_NORMAL(
++					"bug: The request identifier 0x%lx "
++                                	"to the adapter with devno 0x%04x is " 
++					"ambiguous. "
++                                        "Stopping all operations on this adapter. "
++                                        "(bug: got this for an unsolicited "
++					"status read request)\n",
++					(unsigned long)fsf_req,
++					adapter->devno);
++			}
++                        debug_text_exception(adapter->erp_dbf,0,"prot_dup_id");
++                        zfcp_erp_adapter_shutdown(adapter, 0);
++			zfcp_cmd_dbf_event_fsf(
++				"dupreqid", fsf_req,
++				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			break;
++                        
++		case FSF_PROT_LINK_DOWN :
++			ZFCP_LOG_FLAGS(1, "FSF_PROT_LINK_DOWN\n");
++			/*
++			 * 'test and set' is not atomic here -
++			 * it's ok as long as calls to our response queue handler
++			 * (and thus execution of this code here) are serialized
++			 * by the qdio module
++			 */
++			if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, 
++                                               &adapter->status)) {
++				switch (fsf_req->qtcb->prefix.prot_status_qual.locallink_error.code) {
++				case FSF_PSQ_LINK_NOLIGHT :
++					ZFCP_LOG_INFO(
++						"The local link to the adapter with "
++						"devno 0x%04x is down"
++						"(no light detected).\n",
++						adapter->devno);
++					break;
++				case FSF_PSQ_LINK_WRAPPLUG :
++					ZFCP_LOG_INFO(
++						"The local link to the adapter with "
++						"devno 0x%04x is down"
++						"(wrap plug detected).\n",
++						adapter->devno);
++					break;
++				case FSF_PSQ_LINK_NOFCP :
++					ZFCP_LOG_INFO(
++						"The local link to the adapter with "
++						"devno 0x%04x is down"
++						"(the adjacent node on the link "
++						"does not support FCP).\n",
++						adapter->devno);
++					break;
++				default :
++					ZFCP_LOG_INFO(
++						"The local link to the adapter with "
++						"devno 0x%04x is down"
++						"(warning: unknown reason code).\n",
++						adapter->devno);
++					break;
++
++				}
++				/*
++				 * Due to the 'erp failed' flag the adapter won't
++				 * be recovered but will be just set to 'blocked'
++				 * state. All subordinary devices will have state
++				 * 'blocked' and 'erp failed', too.
++				 * Thus the adapter is still able to provide
++				 * 'link up' status without being flooded with
++				 * requests.
++				 * (note: even 'close port' is not permitted)
++				 */
++				ZFCP_LOG_INFO(
++					"Stopping all operations for the adapter "
++					"with devno 0x%04x.\n",
++					adapter->devno);
++				atomic_set_mask(
++					ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED |
++					ZFCP_STATUS_COMMON_ERP_FAILED,
++					&adapter->status);
++				zfcp_erp_adapter_reopen(adapter, 0);
++                                debug_text_event(adapter->erp_dbf,1,"prot_link_down");
++                        }
++			zfcp_cmd_dbf_event_fsf(
++				"linkdown", fsf_req,
++				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                        zfcp_callback_do_link_down(adapter);
++			break;
++
++		case FSF_PROT_REEST_QUEUE :
++			ZFCP_LOG_FLAGS(1, "FSF_PROT_REEST_QUEUE\n"); 
++                        debug_text_event(adapter->erp_dbf,1,"prot_reest_queue");
++                        ZFCP_LOG_INFO("The local link to the adapter with "
++                                      "devno 0x%04x was re-plugged. "
++                                      "Re-starting operations on this adapter.\n", 
++                                      adapter->devno);
++                        /* All ports should be marked as ready to run again */
++                        zfcp_erp_modify_adapter_status(
++                                       adapter,
++                                       ZFCP_STATUS_COMMON_RUNNING,
++                                       ZFCP_SET);
++                        zfcp_erp_adapter_reopen(
++                                       adapter, 
++                                       ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED
++                                       | ZFCP_STATUS_COMMON_ERP_FAILED);
++			zfcp_cmd_dbf_event_fsf(
++				"reestque", fsf_req,
++				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                        break;
++			
++		case FSF_PROT_ERROR_STATE :
++			ZFCP_LOG_FLAGS(0, "FSF_PROT_ERROR_STATE\n");
++			ZFCP_LOG_NORMAL(
++				"error: The adapter with devno 0x%04x "
++				"has entered the error state. "
++                                "Restarting all operations on this "
++                                "adapter.\n",
++                                adapter->devno);
++                        debug_text_event(adapter->erp_dbf,0,"prot_err_sta");
++			/* restart operation on this adapter */
++                        zfcp_erp_adapter_reopen(adapter,0);
++			zfcp_cmd_dbf_event_fsf(
++				"proterrs", fsf_req,
++				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_RETRY;
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			break;
++
++		default :
++			ZFCP_LOG_NORMAL(
++				"bug: Transfer protocol status information "
++				"provided by the adapter with devno 0x%04x "
++                                "is not compatible with the device driver. "
++                                "Stopping all operations on this adapter. "
++                                "(debug info 0x%x).\n",
++                                adapter->devno,
++				fsf_req->qtcb->prefix.prot_status);
++			ZFCP_LOG_NORMAL(
++				"fsf_req=0x%lx, qtcb=0x%lx\n",
++				(unsigned long)fsf_req,
++				(unsigned long)fsf_req->qtcb);
++			ZFCP_HEX_DUMP(
++				ZFCP_LOG_LEVEL_NORMAL,
++				(char*)fsf_req,
++				sizeof(zfcp_fsf_req_t));
++                        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL, (char *)fsf_req->qtcb, sizeof(fsf_qtcb_t));
++                        debug_text_event(adapter->erp_dbf,0,"prot_inval:");
++                        debug_exception(adapter->erp_dbf,0,
++                                        &fsf_req->qtcb->prefix.prot_status,
++                                        sizeof(u32));
++                        //                        panic("it was pity");
++                        zfcp_erp_adapter_shutdown(adapter, 0);
++			zfcp_cmd_dbf_event_fsf(
++				"undefps", fsf_req,
++				&fsf_req->qtcb->prefix.prot_status_qual, sizeof(fsf_prot_status_qual_t));
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++	}
++        
++ skip_protstatus:
++	/*
++	 * always call specific handlers to give them a chance to do
++	 * something meaningful even in error cases
++	 */
++	zfcp_fsf_fsfstatus_eval(fsf_req);
++        
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++	return retval;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_fsf_fsfstatus_eval
++ *
++ * purpose:	evaluates FSF status of completed FSF request
++ *		and acts accordingly
++ *
++ * returns:
++ */
++static int zfcp_fsf_fsfstatus_eval(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		goto skip_fsfstatus;
++	}
++
++	/* evaluate FSF Status */
++	switch (fsf_req->qtcb->header.fsf_status) {
++		case FSF_UNKNOWN_COMMAND :
++			ZFCP_LOG_FLAGS(0, "FSF_UNKNOWN_COMMAND\n");
++			ZFCP_LOG_NORMAL("bug: Command issued by the device driver is "
++                                        "not known by the adapter with devno 0x%04x "
++                                        "Stopping all operations on this adapter. "
++                                        "(debug info 0x%x).\n",
++                                        fsf_req->adapter->devno,
++                                        fsf_req->qtcb->header.fsf_command);
++                        debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_s_unknown");
++                        zfcp_erp_adapter_shutdown(fsf_req->adapter, 0);
++			zfcp_cmd_dbf_event_fsf(
++				"unknownc", fsf_req,
++				&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			break;
++                        
++                case FSF_FCP_RSP_AVAILABLE :
++                        ZFCP_LOG_FLAGS(2, "FSF_FCP_RSP_AVAILABLE\n");
++                        ZFCP_LOG_DEBUG("FCP Sense data will be presented to the "
++                                      "SCSI stack.\n");
++                        debug_text_event(fsf_req->adapter->erp_dbf,4,"fsf_s_rsp");
++                        break;
++
++		case FSF_ADAPTER_STATUS_AVAILABLE :
++			ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
++                        debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_s_astatus");
++                        zfcp_fsf_fsfstatus_qual_eval(fsf_req);
++                        break;
++
++		default :
++			break;
++	}
++
++skip_fsfstatus:
++	/*
++         * always call specific handlers to give them a chance to do
++         * something meaningful even in error cases
++         */ 
++	zfcp_fsf_req_dispatch(fsf_req);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++
++/*
++ * function:	zfcp_fsf_fsfstatus_qual_eval
++ *
++ * purpose:	evaluates FSF status-qualifier of completed FSF request
++ *		and acts accordingly
++ *
++ * returns:
++ */
++static int zfcp_fsf_fsfstatus_qual_eval(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++        switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
++        case FSF_SQ_FCP_RSP_AVAILABLE :
++                ZFCP_LOG_FLAGS(2, "FSF_SQ_FCP_RSP_AVAILABLE\n");
++                debug_text_event(fsf_req->adapter->erp_dbf,4,"fsf_sq_rsp");
++                break;
++        case FSF_SQ_RETRY_IF_POSSIBLE :
++                ZFCP_LOG_FLAGS(2, "FSF_SQ_RETRY_IF_POSSIBLE\n");
++                /* The SCSI-stack may now issue retries or escalate */
++                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_sq_retry");
++		zfcp_cmd_dbf_event_fsf(
++			"sqretry", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++        case FSF_SQ_COMMAND_ABORTED :
++                ZFCP_LOG_FLAGS(2, "FSF_SQ_COMMAND_ABORTED\n");
++		/* Carry the aborted state on to upper layer */
++                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_sq_abort");
++		zfcp_cmd_dbf_event_fsf(
++			"sqabort", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ABORTED;
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++        case FSF_SQ_NO_RECOM :
++                ZFCP_LOG_FLAGS(0, "FSF_SQ_NO_RECOM\n");
++                debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_sq_no_rec");
++                ZFCP_LOG_NORMAL("bug: No recommendation could be given for a"
++                                "problem on the adapter with devno 0x%04x "
++                                "Stopping all operations on this adapter. ",
++                                fsf_req->adapter->devno);
++                zfcp_erp_adapter_shutdown(fsf_req->adapter, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"sqnrecom", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++        case FSF_SQ_ULP_PROGRAMMING_ERROR :
++                ZFCP_LOG_FLAGS(0, "FSF_SQ_ULP_PROGRAMMING_ERROR\n");
++		ZFCP_LOG_NORMAL("error: not enough SBALs for data transfer "
++				"(adapter devno=0x%04x)\n",
++                                fsf_req->adapter->devno);
++                debug_text_exception(fsf_req->adapter->erp_dbf, 0,
++                                     "fsf_sq_ulp_err");
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++        case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
++        case FSF_SQ_NO_RETRY_POSSIBLE :
++        case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
++                /* dealt with in the respective functions */
++                break;
++        default:
++                ZFCP_LOG_NORMAL("bug: Additional status info could "
++                                "not be interpreted properly.\n");
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
++                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
++                              16);
++                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
++                debug_exception(fsf_req->adapter->erp_dbf,0,
++                                &fsf_req->qtcb->header.fsf_status_qual.word[0],
++                                sizeof(u32));
++		zfcp_cmd_dbf_event_fsf(
++			"squndef", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++        }
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_fsf_req_dispatch
++ *
++ * purpose:	calls the appropriate command specific handler
++ *
++ * returns:	
++ */
++static int zfcp_fsf_req_dispatch(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++
++	zfcp_erp_action_t *erp_action = fsf_req->erp_action;
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		ZFCP_LOG_TRACE(
++			"fsf_req=0x%lx, QTCB=0x%lx\n",
++			(unsigned long)fsf_req,
++			(unsigned long)(fsf_req->qtcb));
++		ZFCP_HEX_DUMP(
++			ZFCP_LOG_LEVEL_TRACE,
++			(char *)fsf_req->qtcb,
++			sizeof(fsf_qtcb_t));
++	}
++
++	switch (fsf_req->fsf_command) {
++
++		case FSF_QTCB_FCP_CMND :
++			ZFCP_LOG_FLAGS(3, "FSF_QTCB_FCP_CMND\n");
++			zfcp_fsf_send_fcp_command_handler(fsf_req);
++			break;
++
++		case FSF_QTCB_ABORT_FCP_CMND :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_ABORT_FCP_CMND\n");
++			zfcp_fsf_abort_fcp_command_handler(fsf_req);
++			break;
++
++		case FSF_QTCB_SEND_GENERIC :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_SEND_GENERIC\n");
++			zfcp_fsf_send_ct_handler(fsf_req);
++			break;
++
++		case FSF_QTCB_OPEN_PORT_WITH_DID :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_OPEN_PORT_WITH_DID\n");
++			zfcp_fsf_open_port_handler(fsf_req);
++			break;
++
++		case FSF_QTCB_OPEN_LUN :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_OPEN_LUN\n");
++			zfcp_fsf_open_unit_handler(fsf_req);
++			break;
++
++		case FSF_QTCB_CLOSE_LUN :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_LUN\n");
++			zfcp_fsf_close_unit_handler(fsf_req);
++			break;
++
++		case FSF_QTCB_CLOSE_PORT :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_PORT\n");
++			zfcp_fsf_close_port_handler(fsf_req);
++			break;
++
++                case FSF_QTCB_CLOSE_PHYSICAL_PORT :
++                        ZFCP_LOG_FLAGS(2, "FSF_QTCB_CLOSE_PHYSICAL_PORT\n");
++                        zfcp_fsf_close_physical_port_handler(fsf_req);
++                        break;
++
++		case FSF_QTCB_EXCHANGE_CONFIG_DATA :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_EXCHANGE_CONFIG_DATA\n");
++			zfcp_fsf_exchange_config_data_handler(fsf_req);
++                        break;
++
++		case FSF_QTCB_EXCHANGE_PORT_DATA :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_EXCHANGE_PORT_DATA\n");
++			zfcp_fsf_exchange_port_data_handler(fsf_req);
++			break;
++
++		case FSF_QTCB_SEND_ELS :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_SEND_ELS\n");
++			zfcp_fsf_send_els_handler(fsf_req);
++			break;
++
++		case FSF_QTCB_DOWNLOAD_CONTROL_FILE :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_DOWNLOAD_CONTROL_FILE\n");
++			zfcp_fsf_control_file_handler(fsf_req);
++			break;
++
++		case FSF_QTCB_UPLOAD_CONTROL_FILE :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_UPLOAD_CONTROL_FILE\n");
++			zfcp_fsf_control_file_handler(fsf_req);
++			break;
++
++		default :
++			ZFCP_LOG_FLAGS(2, "FSF_QTCB_UNKNOWN\n");
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			ZFCP_LOG_NORMAL(
++				"bug: Command issued by the device driver is "
++                                "not supported by the adapter with devno 0x%04x "
++                                "(debug info 0x%lx 0x%x).\n",
++                                fsf_req->adapter->devno,
++				(unsigned long)fsf_req,
++				fsf_req->fsf_command);
++			if (fsf_req->fsf_command !=
++			    fsf_req->qtcb->header.fsf_command)
++                                ZFCP_LOG_NORMAL(
++                                     "bug: Command issued by the device driver differs "
++                                     "from the command returned by the adapter with devno "
++                                     "0x%04x (debug info 0x%x, 0x%x).\n",
++                                     fsf_req->adapter->devno,
++                                     fsf_req->fsf_command,
++                                     fsf_req->qtcb->header.fsf_command);
++	}
++
++	if (erp_action)
++	        zfcp_erp_async_handler(erp_action, 0);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_status_read
++ *
++ * purpose:	initiates a Status Read command at the specified adapter
++ *
++ * returns:
++ */
++static int zfcp_fsf_status_read(
++	zfcp_adapter_t *adapter,
++	int req_flags)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	zfcp_fsf_req_t *fsf_req;
++	fsf_status_read_buffer_t *status_buffer;
++	unsigned long lock_flags;
++	volatile qdio_buffer_element_t *sbale;
++        int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx, req_flags=0x%x)\n", 
++		(unsigned long)adapter,
++		req_flags);
++
++        /* setup new FSF request */
++        retval = zfcp_fsf_req_create(
++			adapter,
++			FSF_QTCB_UNSOLICITED_STATUS,
++			req_flags | ZFCP_REQ_USE_MEMPOOL,
++                        &adapter->pool.fsf_req_status_read,
++			&lock_flags,
++			&fsf_req);
++	if (retval < 0) {
++		ZFCP_LOG_INFO(
++			"error: Out of resources. Could not create an "
++			"unsolicited status buffer for "
++			"the adapter with devno 0x%04x.\n",
++			adapter->devno);
++                goto failed_req_create;
++        }
++
++	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
++        sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
++        sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
++        fsf_req->sbale_curr = 2;
++
++	status_buffer = zfcp_mem_pool_find(&adapter->pool.data_status_read);
++	if (!status_buffer) {
++		ZFCP_LOG_NORMAL("bug: could not get some buffer\n");
++		goto failed_buf;
++	}
++	fsf_req->data.status_read.buffer = status_buffer;
++
++	/* insert pointer to respective buffer */
++	sbale = zfcp_qdio_sbale_curr(fsf_req);
++	sbale->addr = (void *)status_buffer;
++	sbale->length = sizeof(fsf_status_read_buffer_t);
++
++	/* start QDIO request for this FSF request */
++        retval = zfcp_fsf_req_send(fsf_req, NULL);
++        if (retval) {
++                ZFCP_LOG_DEBUG(
++                        "error: Could not set-up unsolicited status "
++                        "environment.\n");
++                goto failed_req_send;
++        }
++
++        ZFCP_LOG_TRACE(
++                "Status Read request initiated "
++                "(adapter devno=0x%04x)\n",
++                adapter->devno);
++#ifdef ZFCP_DEBUG_REQUESTS
++        debug_text_event(adapter->req_dbf, 1, "unso");
++#endif
++
++	goto out;
++
++failed_req_send:
++	zfcp_mem_pool_return(status_buffer, &adapter->pool.data_status_read);
++
++failed_buf:
++	if (zfcp_fsf_req_free(fsf_req)) {
++		ZFCP_LOG_NORMAL(
++			"bug: Could not remove one FSF "
++			"request. Memory leakage possible. "
++			"(debug info 0x%lx).\n",
++			(unsigned long)fsf_req);
++	};
++
++failed_req_create:
++out:
++        write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
++        
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++static int zfcp_fsf_status_read_port_closed(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	fsf_status_read_buffer_t *status_buffer = fsf_req->data.status_read.buffer;
++	zfcp_adapter_t *adapter = fsf_req->adapter;
++	unsigned long flags;
++	zfcp_port_t *port;
++
++	write_lock_irqsave(&adapter->port_list_lock, flags);
++	ZFCP_FOR_EACH_PORT (adapter, port)
++		if (port->d_id == (status_buffer->d_id & ZFCP_DID_MASK))
++			break;
++	write_unlock_irqrestore(&adapter->port_list_lock, flags);
++
++	if (!port) {
++		ZFCP_LOG_NORMAL(
++			"bug: Re-open port indication received for the "
++			"non-existing port with DID 0x%06x, on the adapter "
++			"with devno 0x%04x. Ignored.\n",
++			status_buffer->d_id & ZFCP_DID_MASK,
++			adapter->devno);
++		goto out;
++	}
++
++	switch (status_buffer->status_subtype) {
++
++		case FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT:
++			ZFCP_LOG_FLAGS(2, "FSF_STATUS_READ_SUB_CLOSE_PHYS_PORT\n");
++			debug_text_event(adapter->erp_dbf, 3, "unsol_pc_phys:");
++			zfcp_erp_port_reopen(port, 0);
++			break;
++
++		case FSF_STATUS_READ_SUB_ERROR_PORT:
++			ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_SUB_ERROR_PORT\n");
++			debug_text_event(adapter->erp_dbf, 1, "unsol_pc_err:");
++			zfcp_erp_port_shutdown(port, 0);
++			break;
++
++		default:
++			debug_text_event(adapter->erp_dbf, 0, "unsol_unk_sub:");
++			debug_exception(
++				adapter->erp_dbf, 0,
++				&status_buffer->status_subtype, sizeof(u32));
++			ZFCP_LOG_NORMAL(
++				"bug: Undefined status subtype received "
++				"for a re-open indication on the port with "
++				"DID 0x%06x, on the adapter with devno "
++				"0x%04x. Ignored. (debug info 0x%x)\n",
++				status_buffer->d_id,
++				adapter->devno,
++				status_buffer->status_subtype);
++	}
++
++out:
++	return 0;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_status_read_handler
++ *
++ * purpose:	is called for finished Open Port command
++ *
++ * returns:	
++ */
++static int zfcp_fsf_status_read_handler(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = 0;
++        zfcp_adapter_t *adapter = fsf_req->adapter;
++        fsf_status_read_buffer_t *status_buffer = fsf_req->data.status_read.buffer; 
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
++		zfcp_mem_pool_return(status_buffer, &adapter->pool.data_status_read);
++                if (zfcp_fsf_req_cleanup(fsf_req)) {
++                        ZFCP_LOG_NORMAL("bug: Could not remove one FSF "
++                                        "request. Memory leakage possible. "
++                                        "(debug info 0x%lx).\n",
++                                        (unsigned long)fsf_req);
++                }
++		goto out;
++        }
++
++	switch (status_buffer->status_type) {
++
++	case FSF_STATUS_READ_PORT_CLOSED:
++                ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_PORT_CLOSED\n");
++                debug_text_event(adapter->erp_dbf,3,"unsol_pclosed:");
++                debug_event(adapter->erp_dbf,3,
++                            &status_buffer->d_id,
++                            sizeof(u32));
++		zfcp_fsf_status_read_port_closed(fsf_req);
++		break;
++
++	case FSF_STATUS_READ_INCOMING_ELS:
++                ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_INCOMING_ELS\n");
++                debug_text_event(adapter->erp_dbf,3,"unsol_els:");
++                zfcp_fsf_incoming_els(fsf_req);
++		break;
++
++        case FSF_STATUS_READ_BIT_ERROR_THRESHOLD:
++                ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_BIT_ERROR_THRESHOLD\n");
++                debug_text_event(adapter->erp_dbf,3,"unsol_bit_err:");
++                ZFCP_LOG_NORMAL("Bit error threshold data received:\n");
++                ZFCP_HEX_DUMP(
++                              ZFCP_LOG_LEVEL_NORMAL, 
++                              (char*)status_buffer,
++                              sizeof(fsf_status_read_buffer_t));
++		break;
++                
++        case FSF_STATUS_READ_LINK_DOWN:
++                ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_LINK_DOWN\n");
++		debug_text_event(adapter->erp_dbf, 0, "unsol_link_down:");
++		ZFCP_LOG_INFO(
++			"Local link to adapter with devno 0x%04x is down\n",
++			adapter->devno);
++		atomic_set_mask(
++			ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
++			&adapter->status);
++		zfcp_erp_adapter_failed(adapter);
++                zfcp_callback_do_link_down(adapter);
++                break;
++                
++
++	case FSF_STATUS_READ_CFDC_UPDATED:
++		ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_CFDC_UPDATED\n");
++		debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_upd:");
++		ZFCP_LOG_NORMAL(
++			"CFDC has been updated on the FCP adapter "
++			"(devno=0x%04x)\n",
++			adapter->devno);
++		break;
++                 
++	case FSF_STATUS_READ_CFDC_HARDENED:
++		ZFCP_LOG_FLAGS(1, "FSF_STATUS_READ_CFDC_HARDENED\n");
++		debug_text_event(adapter->erp_dbf, 2, "unsol_cfdc_harden:");
++		switch (status_buffer->status_subtype) {
++		case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE:
++			ZFCP_LOG_NORMAL(
++				"CFDC has been saved on the SE "
++				"(devno=0x%04x)\n",
++				adapter->devno);
++			break;
++		case FSF_STATUS_READ_SUB_CFDC_HARDENED_ON_SE2:
++			ZFCP_LOG_NORMAL(
++				"CFDC has been copied to the secondary SE "
++				"(devno=0x%04x)\n",
++				adapter->devno);
++			break;
++		default:
++			ZFCP_LOG_NORMAL(
++				"CFDC has been hardened on the FCP adapter "
++				"(devno=0x%04x)\n",
++				adapter->devno);
++		}
++		break;
++
++        case FSF_STATUS_READ_LINK_UP:
++                ZFCP_LOG_FLAGS(1,"FSF_STATUS_READ_LINK_UP\n");
++                debug_text_event(adapter->erp_dbf,2,"unsol_link_up:");
++                ZFCP_LOG_INFO("The local link to the adapter with "
++                              "devno 0x%04x was re-plugged. "
++                              "Re-starting operations on this adapter..\n", 
++                              adapter->devno);
++                /* All ports should be marked as ready to run again */
++                zfcp_erp_modify_adapter_status(
++                                  adapter,
++                                  ZFCP_STATUS_COMMON_RUNNING,
++                                  ZFCP_SET);
++                zfcp_erp_adapter_reopen(
++                                  adapter, 
++                                  ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED
++                                  | ZFCP_STATUS_COMMON_ERP_FAILED);
++
++                zfcp_callback_do_link_up(adapter);
++                break;
++                
++	default:
++                debug_text_event(adapter->erp_dbf,0,"unsol_unknown:");
++                debug_exception(adapter->erp_dbf,0,
++                                &status_buffer->status_type,
++                                sizeof(u32));
++		ZFCP_LOG_NORMAL("bug: An unsolicited status packet of unknown "
++                               "type was received by the zfcp-driver "
++                               "(debug info 0x%x)\n",
++                                status_buffer->status_type);
++                ZFCP_LOG_DEBUG("Dump of status_read_buffer 0x%lx:\n", 
++                              (unsigned long)status_buffer);
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, 
++                              (char*)status_buffer,
++                              sizeof(fsf_status_read_buffer_t));
++                break;
++	}
++
++	zfcp_mem_pool_return(status_buffer, &adapter->pool.data_status_read);
++        if (zfcp_fsf_req_cleanup(fsf_req)) {
++                ZFCP_LOG_NORMAL("bug: Could not remove one FSF "
++                                "request. Memory leakage possible. "
++                                "(debug info 0x%lx).\n",
++                                (unsigned long)fsf_req);
++        }
++        /* recycle buffer and start new request 
++         * repeat until outbound queue is empty or adapter shutdown is requested*/
++
++        /* FIXME(qdio) - we may wait in the req_create for 5s during shutdown, so 
++           qdio_cleanup will have to wait at least that long before returning with
++           failure to allow us a proper cleanup under all circumstances
++        */
++	/* FIXME: allocation failure possible? (Is this code needed?) */
++	retval = zfcp_fsf_status_read(adapter, 0);
++	if (retval < 0) {
++		ZFCP_LOG_INFO(
++			"Outbound queue busy. "
++			"Could not create use an "
++			"unsolicited status read request for "
++			"the adapter with devno 0x%04x.\n",
++			adapter->devno);
++		/* temporary fix to avoid status read buffer shortage */
++		adapter->status_read_failed++;
++		if ((ZFCP_STATUS_READS_RECOM - adapter->status_read_failed)
++		    < ZFCP_STATUS_READ_FAILED_THRESHOLD) {
++			ZFCP_LOG_INFO(
++				"restart adapter due to status read "
++				"buffer shortage (devno 0x%04x)\n",
++				adapter->devno);
++				zfcp_erp_adapter_reopen(adapter, 0);
++		}
++	}
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++void zfcp_fsf_incoming_els_rscn(
++		zfcp_adapter_t *adapter,
++		fsf_status_read_buffer_t *status_buffer) 
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++	fcp_rscn_head_t *fcp_rscn_head
++		= (fcp_rscn_head_t *) status_buffer->payload;
++        fcp_rscn_element_t *fcp_rscn_element
++		= (fcp_rscn_element_t *) status_buffer->payload;
++
++        unsigned long flags;
++        zfcp_port_t *port;
++        int i;
++        int known=0;
++        int no_notifications=0;
++        int range_mask=0;
++        int reopen_unknown=0;
++        /* see FC-FS */
++        int no_entries=(fcp_rscn_head->payload_len / 4);
++
++	zfcp_in_els_dbf_event(adapter, "##rscn", status_buffer, fcp_rscn_head->payload_len);
++
++        for (i=1; i < no_entries; i++) {
++                /* skip head and start with 1st element */
++                fcp_rscn_element++;
++                switch (fcp_rscn_element->addr_format) {
++                case ZFCP_PORT_ADDRESS:
++                        ZFCP_LOG_FLAGS(1,"ZFCP_PORT_ADDRESS\n");
++                        range_mask=ZFCP_PORTS_RANGE_PORT;
++                        no_notifications=1;
++                        break;
++                case ZFCP_AREA_ADDRESS:
++                        ZFCP_LOG_FLAGS(1,"ZFCP_AREA_ADDRESS\n");
++			/* skip head and start with 1st element */
++                        range_mask=ZFCP_PORTS_RANGE_AREA;
++                        no_notifications = ZFCP_NO_PORTS_PER_AREA;
++                        break;
++                case ZFCP_DOMAIN_ADDRESS:
++                        ZFCP_LOG_FLAGS(1,"ZFCP_DOMAIN_ADDRESS\n");
++                        range_mask=ZFCP_PORTS_RANGE_DOMAIN;
++                        no_notifications = ZFCP_NO_PORTS_PER_DOMAIN;
++                        break;
++                case ZFCP_FABRIC_ADDRESS:
++                        ZFCP_LOG_FLAGS(1,"ZFCP_FABRIC_ADDRESS\n");
++                        range_mask=ZFCP_PORTS_RANGE_FABRIC;
++                        no_notifications = ZFCP_NO_PORTS_PER_FABRIC;
++                        break;
++                }
++                known=0;
++                write_lock_irqsave(&adapter->port_list_lock, flags);
++                ZFCP_FOR_EACH_PORT (adapter, port) {
++                        if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status))
++                                continue;
++                        if(((u32)port->d_id & range_mask) 
++                           == (u32)(fcp_rscn_element->nport_did & range_mask)) {
++                                known++;
++#if 0
++                                printk("known=%d, reopen did 0x%x\n",
++                                       known,
++                                       fcp_rscn_element->nport_did);
++#endif
++				debug_text_event(adapter->erp_dbf,1,"unsol_els_rscnk:");
++				zfcp_test_link(port);
++                        }
++                }
++                write_unlock_irqrestore(&adapter->port_list_lock, flags);
++#if 0
++                printk("known %d, no_notifications %d\n",
++                       known, no_notifications);
++#endif
++                if(known<no_notifications) {
++                        ZFCP_LOG_DEBUG("At least one unknown port changed state. "
++                                      "Unknown ports need to be reopened.\n");
++                        reopen_unknown=1;
++                }
++        } // for (i=1; i < no_entries; i++)
++        
++        if(reopen_unknown) {
++                ZFCP_LOG_DEBUG("At least one unknown did "
++                              "underwent a state change.\n");
++                write_lock_irqsave(&adapter->port_list_lock, flags);
++                ZFCP_FOR_EACH_PORT (adapter, port) {
++			if (atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER, &port->status))
++				continue;
++			if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status)) {
++                                ZFCP_LOG_INFO("Received state change notification."
++                                                "Trying to open the port with WWPN "
++                                                "0x%016Lx. Hope it's there now.\n",
++                                                (llui_t)port->wwpn);
++				debug_text_event(adapter->erp_dbf,1,"unsol_els_rscnu:");
++                                zfcp_erp_port_reopen(port, 
++                                                     ZFCP_STATUS_COMMON_ERP_FAILED);
++                        }
++                }
++                write_unlock_irqrestore(&adapter->port_list_lock, flags);
++        }
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++void zfcp_fsf_incoming_els_plogi(
++		zfcp_adapter_t *adapter,
++		fsf_status_read_buffer_t *status_buffer)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	logi *els_logi = (logi*) status_buffer->payload;
++	zfcp_port_t *port;
++	unsigned long flags;
++
++	zfcp_in_els_dbf_event(adapter, "##plogi", status_buffer, 28);
++
++	write_lock_irqsave(&adapter->port_list_lock, flags);
++	ZFCP_FOR_EACH_PORT(adapter, port) {
++		if (port->wwpn == (*(wwn_t *)&els_logi->nport_wwn))
++			break;
++	}
++	write_unlock_irqrestore(&adapter->port_list_lock, flags);
++
++	if (!port) {
++		ZFCP_LOG_DEBUG(
++			"Re-open port indication received "
++			"for the non-existing port with D_ID "
++			"0x%06x, on the adapter with devno "
++			"0x%04x. Ignored.\n",
++			status_buffer->d_id,
++			adapter->devno);
++	} else	{
++		debug_text_event(adapter->erp_dbf, 1, "unsol_els_plogi:");
++		debug_event(adapter->erp_dbf, 1, &els_logi->nport_wwn, 8);
++		zfcp_erp_port_forced_reopen(port, 0);
++	}
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++void zfcp_fsf_incoming_els_logo(
++		zfcp_adapter_t *adapter,
++		fsf_status_read_buffer_t *status_buffer)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	fcp_logo_t *els_logo = (fcp_logo_t*) status_buffer->payload;
++	zfcp_port_t *port;
++	unsigned long flags;
++
++	zfcp_in_els_dbf_event(adapter, "##logo", status_buffer, 16);
++
++	write_lock_irqsave(&adapter->port_list_lock, flags);
++	ZFCP_FOR_EACH_PORT(adapter, port) {
++		if (port->wwpn == els_logo->nport_wwpn)
++			break;
++	}
++	write_unlock_irqrestore(&adapter->port_list_lock, flags);
++
++	if (!port) {
++		ZFCP_LOG_DEBUG(
++			"Re-open port indication received "
++			"for the non-existing port with D_ID "
++			"0x%06x, on the adapter with devno "
++			"0x%04x. Ignored.\n",
++			status_buffer->d_id,
++			adapter->devno);
++	} else	{
++		debug_text_event(adapter->erp_dbf, 1, "unsol_els_logo:");
++		debug_event(adapter->erp_dbf, 1, &els_logo->nport_wwpn, 8);
++		zfcp_erp_port_forced_reopen(port, 0);
++	}
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++void zfcp_fsf_incoming_els_unknown(
++		zfcp_adapter_t *adapter,
++		fsf_status_read_buffer_t *status_buffer)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	zfcp_in_els_dbf_event(adapter, "##undef", status_buffer, 24);
++	ZFCP_LOG_NORMAL(
++		"warning: Unknown incoming ELS (0x%x) received "
++		"for the adapter with devno 0x%04x\n",
++		*(u32*)(status_buffer->payload),
++		adapter->devno);
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++void zfcp_fsf_incoming_els(zfcp_fsf_req_t *fsf_req) 
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	fsf_status_read_buffer_t *status_buffer = fsf_req->data.status_read.buffer; 
++	u32 els_type = *(u32*)(status_buffer->payload);
++	zfcp_adapter_t *adapter = fsf_req->adapter;
++
++	if (els_type == LS_PLOGI)
++        	zfcp_fsf_incoming_els_plogi(adapter, status_buffer);
++	else if (els_type == LS_LOGO)
++		zfcp_fsf_incoming_els_logo(adapter, status_buffer);
++	else if ((els_type & 0xffff0000) == LS_RSCN)
++		/* we are only concerned with the command, not the length */
++		zfcp_fsf_incoming_els_rscn(adapter, status_buffer);
++	else	zfcp_fsf_incoming_els_unknown(adapter, status_buffer);
++
++	zfcp_callback_do_incomming_els(adapter, status_buffer->payload);
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_start_scsi_er_timer
++ *
++ * purpose:     sets up the timer to watch over SCSI error recovery
++ *              actions and starts it
++ *
++ */
++static void zfcp_fsf_start_scsi_er_timer(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++	ZFCP_LOG_TRACE("enter (adapter=0x%lx\n",
++                       (unsigned long)adapter);
++        adapter->scsi_er_timer.function = 
++                zfcp_fsf_scsi_er_timeout_handler;
++        adapter->scsi_er_timer.data = 
++                (unsigned long)adapter;
++        adapter->scsi_er_timer.expires = 
++                jiffies + ZFCP_SCSI_ER_TIMEOUT;
++        add_timer(&adapter->scsi_er_timer);
++
++        ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++
++/*
++ * function:    zfcp_fsf_abort_fcp_command
++ *
++ * purpose:	tells FSF to abort a running SCSI command
++ *
++ * returns:	address of initiated FSF request
++ *		NULL - request could not be initiated
++ *
++ * FIXME(design) shouldn't this be modified to return an int
++ *               also...don't know how though
++ */
++static zfcp_fsf_req_t * zfcp_fsf_abort_fcp_command(
++		unsigned long old_req_id,
++		zfcp_adapter_t *adapter,
++		zfcp_unit_t *unit,
++		int req_flags)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	volatile qdio_buffer_element_t *sbale;
++	zfcp_fsf_req_t *fsf_req = NULL;
++	int retval = 0;
++	unsigned long lock_flags;
++ 
++	ZFCP_LOG_TRACE(
++		"enter (old_req_id=0x%lx, adapter=0x%lx, "
++		"unit=0x%lx, req_flags=0x%x)\n",
++		old_req_id,
++		(unsigned long)adapter,
++		(unsigned long)unit,
++		req_flags);
++
++	/* setup new FSF request */
++	retval = zfcp_fsf_req_create(
++			adapter,
++			FSF_QTCB_ABORT_FCP_CMND,
++			req_flags,
++                        &adapter->pool.fsf_req_scsi,
++			&lock_flags,
++			&fsf_req);
++	if (retval < 0) {
++                ZFCP_LOG_INFO(
++			"error: Out of resources. Could not create an "
++                        "abort command request on the device with "
++                        "the FCP_LUN 0x%016Lx connected to "
++                        "the port with WWPN 0x%016Lx connected to "
++                        "the adapter with devno 0x%04x.\n",
++                        (llui_t)unit->fcp_lun,
++                        (llui_t)unit->port->wwpn,
++                        adapter->devno);
++		goto out;
++	}
++        
++	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
++        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
++        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
++
++	fsf_req->data.abort_fcp_command.unit = unit;
++
++	/* set handles of unit and its parent port in QTCB */
++	fsf_req->qtcb->header.lun_handle = unit->handle;
++	fsf_req->qtcb->header.port_handle = unit->port->handle;
++
++	/* set handle of request which should be aborted */
++        fsf_req->qtcb->bottom.support.req_handle = (u64)old_req_id;
++
++#if 0
++	/* DEBUG */
++	goto out;
++#endif
++
++	/* start QDIO request for this FSF request */
++        
++        zfcp_fsf_start_scsi_er_timer(adapter);
++        retval = zfcp_fsf_req_send(fsf_req, NULL);
++	if (retval) {
++                del_timer(&adapter->scsi_er_timer);
++		ZFCP_LOG_INFO(
++			"error: Could not send an abort command request "
++			"for a command on the adapter with devno 0x%04x, "
++                        "port WWPN 0x%016Lx and unit FCP_LUN 0x%016Lx\n",
++			adapter->devno,
++			(llui_t)unit->port->wwpn,
++			(llui_t)unit->fcp_lun);
++		if (zfcp_fsf_req_free(fsf_req)) {
++			ZFCP_LOG_NORMAL(
++				"bug: Could not remove one FSF "
++				"request. Memory leakage possible. "
++                                "(debug info 0x%lx).\n",
++                                (unsigned long)fsf_req);
++                };
++		fsf_req = NULL;
++		goto out;
++	}
++
++	ZFCP_LOG_DEBUG(
++		"Abort FCP Command request initiated "
++		"(adapter devno=0x%04x, port D_ID=0x%06x, "
++		"unit FCP_LUN=0x%016Lx, old_req_id=0x%lx)\n",
++		adapter->devno,
++		unit->port->d_id,
++		(llui_t)unit->fcp_lun,
++		old_req_id);
++	
++out:
++        write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
++
++	ZFCP_LOG_DEBUG("exit (0x%lx)\n", (unsigned long)fsf_req);
++ 
++	return fsf_req;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_abort_fcp_command_handler
++ *
++ * purpose:	is called for finished Abort FCP Command request
++ *
++ * returns:	
++ */
++static int zfcp_fsf_abort_fcp_command_handler(
++		zfcp_fsf_req_t *new_fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = -EINVAL;
++	zfcp_unit_t *unit = new_fsf_req->data.abort_fcp_command.unit;
++        unsigned char status_qual = new_fsf_req->qtcb->header.fsf_status_qual.word[0];
++
++	ZFCP_LOG_TRACE(
++		"enter (new_fsf_req=0x%lx)\n",
++		(unsigned long)new_fsf_req);
++
++        del_timer(&new_fsf_req->adapter->scsi_er_timer);
++
++	if (new_fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		/* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */
++		goto skip_fsfstatus;
++	}
++     
++	/* evaluate FSF status in QTCB */
++	switch (new_fsf_req->qtcb->header.fsf_status) {
++                
++        case FSF_PORT_HANDLE_NOT_VALID :
++                if(status_qual>>4 != status_qual%0xf) {
++                        ZFCP_LOG_FLAGS(2, "FSF_PORT_HANDLE_NOT_VALID\n");
++                        debug_text_event(new_fsf_req->adapter->erp_dbf,3,"fsf_s_phand_nv0");
++                        /* In this case a command that was sent prior to a port
++                         * reopen was aborted (handles are different). This is fine.
++                         */
++                } else {
++                        ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
++                        ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
++                                        "for the port with WWPN 0x%016Lx connected to "
++                                        "the adapter of devno 0x%04x is "
++                                        "not valid. This may happen occasionally.\n",
++                                        unit->port->handle,
++                                        (llui_t)unit->port->wwpn,
++                                        unit->port->adapter->devno);
++                        ZFCP_LOG_INFO("status qualifier:\n");
++                        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
++                                      (char*)&new_fsf_req->qtcb->header.fsf_status_qual,
++                                      16);
++                        /* Let's hope this sorts out the mess */
++                        debug_text_event(new_fsf_req->adapter->erp_dbf,1,"fsf_s_phand_nv1");
++                        zfcp_erp_adapter_reopen(unit->port->adapter, 0);
++                        new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                }
++                break;
++                
++        case FSF_LUN_HANDLE_NOT_VALID :
++                if(status_qual>>4 != status_qual%0xf) {
++                        /* 2 */
++                        ZFCP_LOG_FLAGS(0, "FSF_LUN_HANDLE_NOT_VALID\n");
++                        debug_text_event(new_fsf_req->adapter->erp_dbf,3,"fsf_s_lhand_nv0");
++                        /* In this case a command that was sent prior to a unit
++                         * reopen was aborted (handles are different). This is fine.
++                         */
++                } else {
++                        ZFCP_LOG_FLAGS(1, "FSF_LUN_HANDLE_NOT_VALID\n");
++                        ZFCP_LOG_INFO("Warning: Temporary LUN identifier (handle) 0x%x "
++                                      "of the logical unit with FCP_LUN 0x%016Lx at "
++                                      "the remote port with WWPN 0x%016Lx connected "
++                                      "to the adapter with devno 0x%04x is " 
++                                      "not valid. This may happen in rare cases."
++                                      "Trying to re-establish link.\n",
++                                      unit->handle,
++                                      (llui_t)unit->fcp_lun,
++                                      (llui_t)unit->port->wwpn,
++                                      unit->port->adapter->devno);
++                        ZFCP_LOG_DEBUG("Status qualifier data:\n");
++                        ZFCP_HEX_DUMP(
++                                      ZFCP_LOG_LEVEL_DEBUG,
++                                      (char*)&new_fsf_req->qtcb->header.fsf_status_qual,
++                                      16);
++                        /* Let's hope this sorts out the mess */
++                        debug_text_event(new_fsf_req->adapter->erp_dbf,1,"fsf_s_lhand_nv1");
++                        zfcp_erp_port_reopen(unit->port, 0);
++                        new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                }
++                break;
++
++        case FSF_FCP_COMMAND_DOES_NOT_EXIST :
++                ZFCP_LOG_FLAGS(2, "FSF_FCP_COMMAND_DOES_NOT_EXIST\n");
++                retval = 0;
++#ifdef ZFCP_DEBUG_REQUESTS
++                  /* debug feature area which records fsf request sequence numbers */
++	        debug_text_event(new_fsf_req->adapter->req_dbf, 3, "no_exist");
++	        debug_event(new_fsf_req->adapter->req_dbf, 3, 
++        	            &new_fsf_req->qtcb->bottom.support.req_handle,
++	                    sizeof(unsigned long));
++#endif /* ZFCP_DEBUG_REQUESTS */
++	        debug_text_event(new_fsf_req->adapter->erp_dbf,3,"fsf_s_no_exist");
++                new_fsf_req->status
++                        |= ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED;
++                break;
++                
++        case FSF_PORT_BOXED :
++                /* 2 */
++                ZFCP_LOG_FLAGS(0, "FSF_PORT_BOXED\n");
++                ZFCP_LOG_DEBUG("The remote port "
++                               "with WWPN 0x%016Lx on the adapter with "
++                               "devno 0x%04x needs to be reopened\n",
++                               (llui_t)unit->port->wwpn,
++                               unit->port->adapter->devno);
++                debug_text_event(new_fsf_req->adapter->erp_dbf,2,"fsf_s_pboxed");
++                zfcp_erp_port_reopen(unit->port, 0);
++                new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
++                        | ZFCP_STATUS_FSFREQ_RETRY;
++                break;
++                        
++        case FSF_ADAPTER_STATUS_AVAILABLE :
++                /* 2 */
++                ZFCP_LOG_FLAGS(0, "FSF_ADAPTER_STATUS_AVAILABLE\n");
++                switch (new_fsf_req->qtcb->header.fsf_status_qual.word[0]){
++                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
++                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
++                        debug_text_event(new_fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
++                        /* reopening link to port */
++                        zfcp_erp_port_reopen(unit->port, 0);
++                        new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;
++                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
++                        ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
++                        /* SCSI stack will escalate */
++                        debug_text_event(new_fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
++                        new_fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;
++                default:
++                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
++                                        new_fsf_req->qtcb->header.fsf_status_qual.word[0]);
++                        debug_text_event(new_fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
++                        debug_exception(new_fsf_req->adapter->erp_dbf,0,
++                                        &new_fsf_req->qtcb->header.fsf_status_qual.word[0],
++                                        sizeof(u32));
++                        break;
++                }
++                break;
++                
++        case FSF_GOOD :
++                /* 3 */
++                ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
++                retval = 0;
++                new_fsf_req->status
++                        |= ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED;
++                break;
++                
++        default :
++                ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
++                                "(debug info 0x%x)\n",
++                                new_fsf_req->qtcb->header.fsf_status);
++                debug_text_event(new_fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
++                debug_exception(new_fsf_req->adapter->erp_dbf,0,
++                                &new_fsf_req->qtcb->header.fsf_status,
++                                sizeof(u32));
++                break;
++	}
++        
++ skip_fsfstatus:
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++     
++	return retval;
++     
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_nameserver_enqueue
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_nameserver_enqueue(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++	int retval = 0;
++	zfcp_port_t *port;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	/* generate port structure */
++	retval = zfcp_port_enqueue(
++			adapter,
++			0,
++			0,
++			ZFCP_STATUS_PORT_NAMESERVER,
++                        &port);
++	if (retval) {
++		ZFCP_LOG_INFO(
++			"error: Could not establish a connection to the "
++                        "fabric name server connected to the "
++			"adapter with devno 0x%04x\n",
++			adapter->devno);
++		goto out;
++	}
++	/* set special D_ID */
++	port->d_id = ZFCP_DID_NAMESERVER;
++        /* enter nameserver port into adapter struct */
++        adapter->nameserver_port=port;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ *
++ */
++static void zfcp_gid_pn_buffers_free(struct zfcp_gid_pn_data *gid_pn)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++	ZFCP_LOG_TRACE("enter\n");
++        if ((gid_pn->ct.pool != 0)) {
++		zfcp_mem_pool_return(gid_pn, gid_pn->ct.pool);
++        } else {
++                ZFCP_KFREE(gid_pn, sizeof(struct zfcp_gid_pn_data));
++        }
++
++	ZFCP_LOG_TRACE("exit\n");
++	return;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ *
++ */
++static int zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn,
++                                     zfcp_mem_pool_t *pool)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++#ifdef ZFCP_MEM_POOL_ONLY
++	*gid_pn = NULL;
++#else
++	*gid_pn = ZFCP_KMALLOC(sizeof(struct zfcp_gid_pn_data), GFP_KERNEL);
++#endif
++	if ((*gid_pn == 0) && (pool != 0))
++		*gid_pn = zfcp_mem_pool_find(pool);
++
++        if (*gid_pn == 0)
++                return -ENOMEM;
++
++        (*gid_pn)->ct.req = &(*gid_pn)->req;
++        (*gid_pn)->ct.resp = &(*gid_pn)->resp;
++	(*gid_pn)->ct.req_count = (*gid_pn)->ct.resp_count = 1;
++        (*gid_pn)->req.address = (char *) &(*gid_pn)->ct_iu_req;
++        (*gid_pn)->resp.address = (char *) &(*gid_pn)->ct_iu_resp;
++        (*gid_pn)->req.length = sizeof(struct ct_iu_ns_req);
++        (*gid_pn)->resp.length = sizeof(struct ct_iu_gid_pn);
++
++	return 0;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ *
++ */
++static int zfcp_ns_gid_pn_request(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++        zfcp_adapter_t *adapter = erp_action->adapter;
++        struct zfcp_gid_pn_data *gid_pn = 0;
++        struct ct_iu_ns_req *ct_iu_req;
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
++	if (!adapter->nameserver_port) {
++		ZFCP_LOG_NORMAL("bug: no nameserver available\n");
++		retval = -EINVAL;
++		goto out;
++	}
++
++        retval = zfcp_gid_pn_buffers_alloc(&gid_pn, &adapter->pool.data_gid_pn);
++	if (retval < 0) {
++		ZFCP_LOG_INFO("error: Out of memory. Could not allocate "
++                              "buffers for nameserver request GID_PN. "
++                              "(adapter: 0x%04x)\n", adapter->devno);
++		goto out;
++	}
++
++	/* setup nameserver request */
++        ct_iu_req = (struct ct_iu_ns_req *) gid_pn->ct.req->address;
++        ct_iu_req->header.revision = ZFCP_CT_REVISION;
++        ct_iu_req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
++        ct_iu_req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
++        ct_iu_req->header.options = ZFCP_CT_SYNCHRONOUS;
++        ct_iu_req->header.cmd_rsp_code = ZFCP_CT_GID_PN;
++        ct_iu_req->header.max_res_size = ZFCP_CT_MAX_SIZE;
++	ct_iu_req->data.wwpn = erp_action->port->wwpn;
++
++        /* setup parameters for send generic command */
++        gid_pn->ct.port = adapter->nameserver_port;
++	gid_pn->ct.handler = zfcp_ns_gid_pn_handler;
++	gid_pn->ct.handler_data = (unsigned long) erp_action;
++        gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
++        gid_pn->ct.timer = &erp_action->timer;
++        erp_action->data.gid_pn = gid_pn;
++
++	retval = zfcp_fsf_send_ct(&gid_pn->ct,
++                                  &erp_action->adapter->pool.fsf_req_erp,
++                                  erp_action);
++	if (retval) {
++		ZFCP_LOG_INFO("error: Could not send nameserver request GID_PN "
++                              "via adapter with devno 0x%04x\n",
++                              adapter->devno);
++                zfcp_gid_pn_buffers_free(gid_pn);
++                erp_action->data.gid_pn = 0;
++	}
++
++ out:
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/**
++ * 
++ */
++static void zfcp_ns_gid_pn_handler(unsigned long data)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++        zfcp_erp_action_t *erp_action = (zfcp_erp_action_t *) data;
++	zfcp_port_t *port = erp_action->port;
++        struct zfcp_send_ct *ct = &erp_action->data.gid_pn->ct;
++	struct ct_iu_ns_req *ct_iu_req =
++                (struct ct_iu_ns_req *) ct->req->address;
++	struct ct_iu_gid_pn *ct_iu_resp =
++                (struct ct_iu_gid_pn *) ct->resp->address;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        if (ct->status)
++                goto failed;
++
++	if (zfcp_check_ct_response(&ct_iu_resp->header)) {
++		/* FIXME: do we need some specific erp entry points */
++		atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status);
++		goto failed;
++	}
++	/* paranoia */
++	if (ct_iu_req->data.wwpn != port->wwpn) {
++		ZFCP_LOG_NORMAL(
++			"bug: Port WWPN returned by nameserver lookup "
++                        "does not correspond to "
++                        "the expected value on the adapter with devno 0x%04x. "
++			"(debug info 0x%016Lx, 0x%016Lx)\n",
++                        port->adapter->devno,
++			(llui_t)port->wwpn,
++                        (llui_t)ct_iu_req->data.wwpn);
++		goto failed;
++	}
++
++	/* looks like a valid d_id */
++        port->d_id = ZFCP_DID_MASK & ct_iu_resp->d_id;
++	atomic_set_mask(ZFCP_STATUS_PORT_DID_DID, &port->status);
++	ZFCP_LOG_DEBUG(
++		"devno 0x%04x:  WWPN=0x%016Lx ---> D_ID=0x%06x\n",
++		port->adapter->devno,
++		(llui_t)port->wwpn,
++		port->d_id);
++	goto out;
++
++ failed:
++	ZFCP_LOG_NORMAL(
++		"warning: WWPN 0x%016Lx not found by nameserver lookup "
++		"using the adapter with devno 0x%04x\n", 
++		(llui_t)port->wwpn,
++		port->adapter->devno);
++	ZFCP_LOG_DEBUG("CT IUs do not match:\n");
++	ZFCP_HEX_DUMP(
++		ZFCP_LOG_LEVEL_DEBUG,
++		(char*)ct_iu_req,
++		sizeof(struct ct_iu_ns_req));
++	ZFCP_HEX_DUMP(
++		ZFCP_LOG_LEVEL_DEBUG,
++		(char*)ct_iu_resp,
++		sizeof(struct ct_iu_gid_pn));
++
++ out:
++        zfcp_gid_pn_buffers_free(erp_action->data.gid_pn);
++        erp_action->data.gid_pn = 0;
++	ZFCP_LOG_TRACE("exit\n");
++	return;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/**
++ * FIXME: document
++ * FIXME: check for FS_RJT IU and set appropriate return code
++ */
++int zfcp_ns_ga_nxt_request(zfcp_port_t *port, struct ct_iu_ga_nxt *ct_iu_resp)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++        struct ct_iu_ns_req *ct_iu_req;
++        struct zfcp_send_ct *ct;
++        zfcp_adapter_t *adapter = port->adapter;
++	int ret = 0;
++
++	DECLARE_COMPLETION(wait);
++
++        memset(ct_iu_resp, 0, sizeof(*ct_iu_resp));
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	if (!adapter->nameserver_port) {
++		ZFCP_LOG_NORMAL("bug: no nameserver available\n");
++		ret = -EINVAL;
++		goto out;
++	}
++
++        if ((ct_iu_req =
++             ZFCP_KMALLOC(sizeof(struct ct_iu_ns_req), GFP_KERNEL)) == 0) {
++                ZFCP_LOG_INFO("error: Out of memory. Unable to create "
++                              "CT request (FC-GS), adapter devno 0x%04x.\n",
++                              adapter->devno);
++                ret = -ENOMEM;
++                goto out;
++        }
++
++        if ((ct =
++             ZFCP_KMALLOC(sizeof(struct zfcp_send_ct), GFP_KERNEL)) == 0) {
++                ZFCP_LOG_INFO("error: Out of memory. Unable to create "
++                              "CT request (FC-GS), adapter devno 0x%04x.\n",
++                              adapter->devno);
++                ret = -ENOMEM;
++                goto free_ct_iu_req;
++        }
++
++        if ((ct->req =
++             ZFCP_KMALLOC(sizeof(struct scatterlist), GFP_KERNEL)) == 0) {
++                ZFCP_LOG_INFO("error: Out of memory. Unable to create "
++                              "CT request (FC-GS), adapter devno 0x%04x.\n",
++                              adapter->devno);
++                ret = -ENOMEM;
++                goto free_ct;
++        }
++
++        if ((ct->resp =
++             ZFCP_KMALLOC(sizeof(struct scatterlist), GFP_KERNEL)) == 0) {
++                ZFCP_LOG_INFO("error: Out of memory. Unable to create "
++                              "CT request (FC-GS), adapter devno 0x%04x.\n",
++                              adapter->devno);
++                ret = -ENOMEM;
++                goto free_req;
++        }
++
++	/* setup nameserver request */
++        ct_iu_req->header.revision = ZFCP_CT_REVISION;
++        ct_iu_req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
++        ct_iu_req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
++        ct_iu_req->header.options = ZFCP_CT_SYNCHRONOUS;
++        ct_iu_req->header.cmd_rsp_code = ZFCP_CT_GA_NXT;
++        ct_iu_req->header.max_res_size = ZFCP_CT_MAX_SIZE;
++	ct_iu_req->data.d_id = ZFCP_DID_MASK & (port->d_id - 1);
++
++	ct->completion = &wait;
++        ct->req->address = (char *) ct_iu_req;
++        ct->resp->address = (char *) ct_iu_resp;
++        ct->req->length = sizeof(*ct_iu_req);
++        ct->resp->length = sizeof(*ct_iu_resp);
++        ct->req_count = ct->resp_count = 1;
++
++        /* setup parameters for send generic command */
++        ct->port = adapter->nameserver_port;
++	ct->handler = zfcp_ns_ga_nxt_handler;
++	ct->handler_data = (unsigned long) ct;
++
++        ct->timeout = ZFCP_NS_GA_NXT_TIMEOUT;
++
++	ret = zfcp_fsf_send_ct(ct, NULL, NULL);
++	if (ret) {
++		ZFCP_LOG_INFO("error: Could not send nameserver request GA_NXT "
++                              "via adapter with devno 0x%04x\n",
++                              adapter->devno);
++                goto free_resp;
++	}
++        wait_for_completion(&wait);
++        ret = ct->status;
++
++ free_resp:
++        ZFCP_KFREE(ct->resp, sizeof(struct scatterlist));
++ free_req:
++        ZFCP_KFREE(ct->req, sizeof(struct scatterlist));
++ free_ct:
++        ZFCP_KFREE(ct, sizeof(struct zfcp_send_ct));
++ free_ct_iu_req:
++        ZFCP_KFREE(ct_iu_req, sizeof(struct ct_iu_ns_req));
++ out:
++	ZFCP_LOG_TRACE("exit (%d)\n", ret);
++	return ret;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * FIXME: document
++ * FIXME: check for FS_RJT IU and return appropriate status
++ */
++static void zfcp_ns_ga_nxt_handler(unsigned long data)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++        struct zfcp_send_ct *ct = (struct zfcp_send_ct *) data;
++	struct ct_iu_ns_req *ct_iu_req =
++                (struct ct_iu_ns_req *) ct->req[0].address;
++	struct ct_iu_ga_nxt *ct_iu_resp =
++                (struct ct_iu_ga_nxt *) ct->resp[0].address;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	if (zfcp_check_ct_response(&ct_iu_resp->header))
++		goto failed;
++
++	goto out;
++
++ failed:
++        ct->status = -EIO;
++	ZFCP_LOG_DEBUG("CT IU headers do not match:\n");
++	ZFCP_HEX_DUMP(
++		ZFCP_LOG_LEVEL_DEBUG,
++		(char*)ct_iu_req,
++		sizeof(struct ct_iu_ns_req));
++	ZFCP_HEX_DUMP(
++		ZFCP_LOG_LEVEL_DEBUG,
++		(char*)ct_iu_resp,
++		sizeof(struct ct_iu_gid_pn));
++out:
++	if (ct->completion != NULL) {
++                complete(ct->completion);
++        }
++	ZFCP_LOG_TRACE("exit\n");
++	return;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/* reject CT_IU reason codes acc. to FC-GS-4 */
++static const struct zfcp_rc_entry zfcp_ct_rc[] = {
++	{0x01, "invalid command code"},
++	{0x02, "invalid version level"},
++	{0x03, "logical error"},
++	{0x04, "invalid CT_IU size"},
++	{0x05, "logical busy"},
++	{0x07, "protocol error"},
++	{0x09, "unable to perform command request"},
++	{0x0b, "command not supported"},
++	{0x0d, "server not available"},
++	{0x0e, "session could not be established"},
++	{0xff, "vendor specific error"},
++	{0, NULL},
++};
++
++/* LS_RJT reason codes acc. to FC-FS */
++static const struct zfcp_rc_entry zfcp_ls_rjt_rc[] = {
++	{0x01, "invalid LS_Command code"},
++	{0x03, "logical error"},
++	{0x05, "logical busy"},
++	{0x07, "protocol error"},
++	{0x09, "unable to perform command request"},
++	{0x0b, "command not supported"},
++	{0x0e, "command already in progress"},
++	{0xff, "vendor specific error"},
++	{0, NULL},
++};
++
++/* reject reason codes according to FC-PH/FC-FS */
++static const struct zfcp_rc_entry zfcp_p_rjt_rc[] = {
++	{0x01, "invalid D_ID"},
++	{0x02, "invalid S_ID"},
++	{0x03, "Nx_Port not available, temporary"},
++	{0x04, "Nx_Port not available, permament"},
++	{0x05, "class not supported"},
++	{0x06, "delimiter usage error"},
++	{0x07, "TYPE not supported"},
++	{0x08, "invalid Link_Control"},
++	{0x09, "invalid R_CTL field"},
++	{0x0a, "invalid F_CTL field"},
++	{0x0b, "invalid OX_ID"},
++	{0x0c, "invalid RX_ID"},
++	{0x0d, "invalid SEQ_ID"},
++	{0x0e, "invalid DF_CTL"},
++	{0x0f, "invalid SEQ_CNT"},
++	{0x10, "invalid parameter field"},
++	{0x11, "exchange error"},
++	{0x12, "protocol error"},
++	{0x13, "incorrect length"},
++	{0x14, "unsupported ACK"},
++	{0x15, "class of service not supported by entity at FFFFFE"},
++	{0x16, "login required"},
++	{0x17, "excessive sequences attempted"},
++	{0x18, "unable to establish exchange"},
++	{0x1a, "fabric path not available"},
++	{0x1b, "invalid VC_ID (class 4)"},
++	{0x1c, "invalid CS_CTL field"},
++	{0x1d, "insufficient resources for VC (class 4)"},
++	{0x1f, "invalid class of service"},
++	{0x20, "preemption request rejected"},
++	{0x21, "preemption not enabled"},
++	{0x22, "multicast error"},
++	{0x23, "multicast error terminate"},
++	{0x24, "process login required"},
++	{0xff, "vendor specific reject"},
++	{0, NULL},
++};
++
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++/**
++ * zfcp_rc_description - return description for given reaon code
++ * @code: reason code
++ * @rc_table: table of reason codes and descriptions
++ */
++static inline const char *
++zfcp_rc_description(u8 code, const struct zfcp_rc_entry *rc_table)
++{
++	const char *descr = "unknown reason code";
++
++	do {
++		if (code == rc_table->code) {
++			descr = rc_table->description;
++			break;
++		}
++		rc_table++;
++	} while (rc_table->code && rc_table->description);
++
++	return descr;
++}
++		     
++/**
++ * zfcp_check_ct_response - evaluate reason code for CT_IU
++ * @rjt: response payload to an CT_IU request
++ * Return: 0 for accept CT_IU, 1 for reject CT_IU or invlid response code
++ */
++int
++zfcp_check_ct_response(struct ct_hdr *rjt)
++{
++	if (rjt->cmd_rsp_code == ZFCP_CT_ACCEPT)
++		return 0;
++
++	if (rjt->cmd_rsp_code != ZFCP_CT_REJECT) {
++		ZFCP_LOG_NORMAL("error: invalid Generic Service command/"
++				"response code (0x%04hx)\n",
++				rjt->cmd_rsp_code);
++		return 1;
++	}
++
++	ZFCP_LOG_INFO("Generic Service command rejected\n");
++	ZFCP_LOG_INFO("%s (0x%02x, 0x%02x, 0x%02x)\n",
++		      zfcp_rc_description(rjt->reason_code, zfcp_ct_rc),
++		      (u32) rjt->reason_code, (u32) rjt->reason_code_expl,
++		      (u32) rjt->vendor_unique);
++
++	return 1;
++}
++
++/**
++ * zfcp_print_els_rjt - print reject parameter and description for ELS reject
++ * @rjt_par: reject parameter acc. to FC-PH/FC-FS
++ * @rc_table: table of reason codes and descriptions
++ */
++static inline void
++zfcp_print_els_rjt(struct zfcp_ls_rjt_par *rjt_par,
++		   const struct zfcp_rc_entry *rc_table)
++{
++	ZFCP_LOG_INFO("%s (%02x %02x %02x %02x)\n",
++		      zfcp_rc_description(rjt_par->reason_code, rc_table),
++		      (u32) rjt_par->action, (u32) rjt_par->reason_code,
++		      (u32) rjt_par->reason_expl, (u32) rjt_par->vendor_unique);
++}
++
++/**
++ * zfcp_fsf_handle_els_rjt - evaluate status qualifier/reason code on ELS reject
++ * @sq: status qualifier word
++ * @rjt_par: reject parameter as described in FC-PH and FC-FS
++ * Return: -EROMTEIO for LS_RJT, -EREMCHG for invalid D_ID, -EIO else
++ */
++int
++zfcp_handle_els_rjt(u32 sq, struct zfcp_ls_rjt_par *rjt_par)
++{
++	int ret = -EIO;
++
++	if (sq == FSF_IOSTAT_NPORT_RJT) {
++		ZFCP_LOG_INFO("ELS rejected (P_RJT)\n");
++		zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc);
++		/* invalid d_id */
++		if (rjt_par->reason_code == 0x01)
++			ret = -EREMCHG;
++	} else if (sq == FSF_IOSTAT_FABRIC_RJT) {
++		ZFCP_LOG_INFO("ELS rejected (F_RJT)\n");
++		zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc);
++		/* invalid d_id */
++		if (rjt_par->reason_code == 0x01)
++			ret = -EREMCHG; 
++	} else if (sq == FSF_IOSTAT_LS_RJT) {
++		ZFCP_LOG_INFO("ELS rejected (LS_RJT)\n");
++		zfcp_print_els_rjt(rjt_par, zfcp_ls_rjt_rc);
++		ret = -EREMOTEIO;
++	} else
++		ZFCP_LOG_INFO("unexpected SQ: 0x%02x\n", sq);
++
++	return ret;
++}
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++
++
++/*
++ * checks whether req buffer and resp bother fit into one SBALE each
++ */
++static inline int
++zfcp_use_one_sbal(struct scatterlist *req, int req_count,
++                  struct scatterlist *resp, int resp_count)
++{
++        return ((req_count == 1) && (resp_count == 1) &&
++                (((unsigned long) req[0].address & PAGE_MASK) ==
++                 ((unsigned long) (req[0].address +
++                                   req[0].length - 1) & PAGE_MASK)) &&
++                (((unsigned long) resp[0].address & PAGE_MASK) ==
++                 ((unsigned long) (resp[0].address +
++                                  resp[0].length - 1) & PAGE_MASK)));
++}
++
++/**
++ * FIXME: doc
++ */
++int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, zfcp_mem_pool_t *pool,
++                     zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = 0;
++	volatile qdio_buffer_element_t *sbale;
++	zfcp_port_t *port = ct->port;
++	zfcp_adapter_t *adapter = port->adapter;
++        zfcp_fsf_req_t *fsf_req;
++        unsigned long lock_flags;
++        int bytes;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	/* setup new FSF request */
++	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC,
++                                     ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
++                                     pool, &lock_flags, &fsf_req);
++	if (retval < 0) {
++                ZFCP_LOG_INFO("error: Out of resources. "
++                              "Could not create a CT request (FC-GS), "
++                              "destination port D_ID is 0x%06x "
++                              "at the adapter with devno 0x%04x.\n",
++                              ct->port->d_id, adapter->devno);
++		goto failed_req;
++	}
++
++        if (erp_action != 0) {
++                erp_action->fsf_req = fsf_req;
++                fsf_req->erp_action = erp_action;
++        }
++                
++	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
++        if (zfcp_use_one_sbal(ct->req, ct->req_count,
++                              ct->resp, ct->resp_count)){
++                /* both request buffer and response buffer
++                   fit into one sbale each */
++                sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
++                sbale[2].addr = ct->req[0].address;
++                sbale[2].length = ct->req[0].length;
++                sbale[3].addr = ct->resp[0].address;
++                sbale[3].length = ct->resp[0].length;
++                sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
++        } else if (adapter->supported_features &
++                   FSF_FEATURE_ELS_CT_CHAINED_SBALS) {
++                /* try to use chained SBALs */
++                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
++                                                SBAL_FLAGS0_TYPE_WRITE_READ,
++                                                ct->req, ct->req_count,
++                                                ZFCP_MAX_SBALS_PER_CT_REQ);
++                if (bytes <= 0) {
++                        ZFCP_LOG_INFO("error: Out of resources (outbuf). "
++                                      "Could not create a CT request (FC-GS), "
++                                      "destination port D_ID is 0x%06x "
++                                      "at the adapter with devno 0x%04x.\n",
++                                      ct->port->d_id, adapter->devno);
++                        if (bytes == 0) {
++                                retval = -ENOMEM;
++                        } else {
++                                retval = bytes;
++                        }
++                        goto failed_send;
++                }
++                fsf_req->qtcb->bottom.support.req_buf_length = bytes;
++                fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
++                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
++                                                SBAL_FLAGS0_TYPE_WRITE_READ,
++                                                ct->resp, ct->resp_count,
++                                                ZFCP_MAX_SBALS_PER_CT_REQ);
++                if (bytes <= 0) {
++                        ZFCP_LOG_INFO("error: Out of resources (inbuf). "
++                                      "Could not create a CT request (FC-GS), "
++                                      "destination port D_ID is 0x%06x "
++                                      "at the adapter with devno 0x%04x.\n",
++                                      ct->port->d_id, adapter->devno);
++                        if (bytes == 0) {
++                                retval = -ENOMEM;
++                        } else {
++                                retval = bytes;
++                        }
++                        goto failed_send;
++                }
++                fsf_req->qtcb->bottom.support.resp_buf_length = bytes;
++        } else {
++                /* reject send generic request */
++		ZFCP_LOG_INFO(
++			"error: microcode does not support chained SBALs."
++                        "CT request (FC-GS) too big."
++                        "Destination port D_ID is 0x%06x "
++			"at the adapter with devno 0x%04x.\n",
++			port->d_id, adapter->devno);
++                retval = -EOPNOTSUPP;
++                goto failed_send;
++        }
++
++	/* settings in QTCB */
++	fsf_req->qtcb->header.port_handle = port->handle;
++	fsf_req->qtcb->bottom.support.service_class = adapter->fc_service_class;
++	fsf_req->qtcb->bottom.support.timeout = ct->timeout;
++        fsf_req->data.send_ct = ct;
++
++	/* start QDIO request for this FSF request */
++	retval = zfcp_fsf_req_send(fsf_req, ct->timer);
++	if (retval) {
++		ZFCP_LOG_DEBUG("error: Out of resources. Could not send a "
++                               "generic services command via adapter with "
++                               "devno 0x%04x, port WWPN 0x%016Lx\n",
++                               adapter->devno,	(llui_t) port->wwpn);
++		goto failed_send;
++	} else {
++                ZFCP_LOG_DEBUG("Send Generic request initiated "
++                               "(adapter devno=0x%04x, port D_ID=0x%06x)\n",
++                               adapter->devno, port->d_id);
++                goto out;
++        }
++
++ failed_send:
++	if (zfcp_fsf_req_free(fsf_req)) {
++                ZFCP_LOG_NORMAL("bug: Could not remove one FSF request. Memory "
++                                "leakage possible. (debug info 0x%lx).\n",
++                                (unsigned long)fsf_req);
++                retval = -EINVAL;
++	};
++        if (erp_action != 0) {
++                erp_action->fsf_req = NULL;
++        }
++ failed_req:
++ out:
++        write_unlock_irqrestore(&adapter->request_queue.queue_lock,
++                                     lock_flags);
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_send_ct_handler
++ *
++ * purpose:	is called for finished Send Generic request
++ *
++ * returns:	
++ */
++static int zfcp_fsf_send_ct_handler(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = -EINVAL;
++	zfcp_port_t *port = fsf_req->data.send_ct->port;
++	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
++	u16 subtable, rule, counter;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		/* do not set ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED */
++		goto skip_fsfstatus;
++	}
++
++	/* evaluate FSF status in QTCB */
++	switch (fsf_req->qtcb->header.fsf_status) {
++
++        case FSF_PORT_HANDLE_NOT_VALID :
++                ZFCP_LOG_FLAGS(1,"FSF_PORT_HANDLE_NOT_VALID\n");
++                ZFCP_LOG_DEBUG("Temporary port identifier (handle) 0x%x "
++				"for the port with WWPN 0x%016Lx connected to "
++                                "the adapter of devno 0x%04x is "
++				"not valid. This may happen occasionally.\n",
++				port->handle,
++				(llui_t)port->wwpn,
++                                port->adapter->devno);
++                ZFCP_LOG_INFO("status qualifier:\n");
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
++                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
++                              16);
++                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_phandle_nv");
++                zfcp_erp_adapter_reopen(port->adapter, 0);
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++                
++        case FSF_SERVICE_CLASS_NOT_SUPPORTED :
++                ZFCP_LOG_FLAGS(0, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n");
++                if(fsf_req->adapter->fc_service_class <= 3) {
++                        ZFCP_LOG_NORMAL("error: The adapter with devno=0x%04x does "
++                                        "not support fibre-channel class %d.\n",
++                                        port->adapter->devno,
++                                        fsf_req->adapter->fc_service_class);
++                } else {
++                        ZFCP_LOG_NORMAL( "bug: The fibre channel class at the adapter "
++                                        "with devno 0x%04x is invalid. "
++                                        "(debug info %d)\n",
++                                        port->adapter->devno,
++                                        fsf_req->adapter->fc_service_class);
++                }
++                /* stop operation for this adapter */
++                debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_s_class_nsup");
++                zfcp_erp_adapter_shutdown(port->adapter, 0);
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++
++	case FSF_ACCESS_DENIED :
++		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
++		ZFCP_LOG_NORMAL("Access denied, cannot send generic command "
++				"(devno=0x%04x wwpn=0x%016Lx)\n",
++				port->adapter->devno,
++				(llui_t)port->wwpn);
++		for (counter = 0; counter < 2; counter++) {
++			subtable = header->fsf_status_qual.halfword[counter * 2];
++			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
++			switch (subtable) {
++			case FSF_SQ_CFDC_SUBTABLE_OS:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
++			case FSF_SQ_CFDC_SUBTABLE_LUN:
++				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
++					zfcp_act_subtable_type[subtable], rule);
++				break;
++			}
++		}
++		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++        case FSF_GENERIC_COMMAND_REJECTED :
++                ZFCP_LOG_FLAGS(1,"FSF_GENERIC_COMMAND_REJECTED\n");
++                ZFCP_LOG_INFO("warning: The port with WWPN 0x%016Lx connected to "
++                              "the adapter of devno 0x%04x has "
++                              "rejected a generic services command.\n",
++                              (llui_t)port->wwpn,
++                              port->adapter->devno);
++                ZFCP_LOG_INFO("status qualifier:\n");
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_INFO,
++                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
++                              16);
++                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_gcom_rej");
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++
++	case FSF_REQUEST_BUF_NOT_VALID :
++		ZFCP_LOG_FLAGS(1, "FSF_REQUEST_BUF_NOT_VALID\n");
++		ZFCP_LOG_NORMAL(
++			"error: The port with WWPN 0x%016Lx connected to "
++			"the adapter of devno 0x%04x has "
++			"rejected a generic services command "
++			"due to invalid request buffer.\n",
++			(llui_t)port->wwpn,
++			port->adapter->devno);
++		debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_reqiv");
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++	case FSF_RESPONSE_BUF_NOT_VALID :
++		ZFCP_LOG_FLAGS(1, "FSF_RESPONSE_BUF_NOT_VALID\n");
++		ZFCP_LOG_NORMAL(
++			"error: The port with WWPN 0x%016Lx connected to "
++			"the adapter of devno 0x%04x has "
++			"rejected a generic services command "
++			"due to invalid response buffer.\n",
++			(llui_t)port->wwpn,
++			port->adapter->devno);
++		debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_resiv");
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++        case FSF_PORT_BOXED :
++                ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
++                ZFCP_LOG_DEBUG("The remote port "
++                               "with WWPN 0x%016Lx on the adapter with "
++                               "devno 0x%04x needs to be reopened\n",
++                               (llui_t)port->wwpn,
++                               port->adapter->devno);
++                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_s_pboxed");
++                zfcp_erp_port_reopen(port, 0);
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
++                        | ZFCP_STATUS_FSFREQ_RETRY;
++                break;
++                        
++        case FSF_ADAPTER_STATUS_AVAILABLE :
++                ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
++                switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
++                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
++                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
++                        /* reopening link to port */
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
++			zfcp_test_link(port);
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;
++                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
++                        /* ERP strategy will escalate */
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;
++                
++                default:
++                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
++                                        fsf_req->qtcb->header.fsf_status_qual.word[0]);
++                        break;
++                }
++                break;
++                
++        case FSF_GOOD :
++                ZFCP_LOG_FLAGS(2,"FSF_GOOD\n");
++                retval = 0;
++                break;
++
++        default :
++                ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
++                                "(debug info 0x%x)\n",
++                                fsf_req->qtcb->header.fsf_status);          
++                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
++                debug_exception(fsf_req->adapter->erp_dbf,0,
++                                &fsf_req->qtcb->header.fsf_status_qual.word[0],
++                                sizeof(u32));
++                break;
++	}
++
++skip_fsfstatus:
++	fsf_req->data.send_ct->status = retval;
++
++        /* callback */
++	if (fsf_req->data.send_ct->handler != 0) {
++                (fsf_req->data.send_ct->handler)
++                        (fsf_req->data.send_ct->handler_data);
++        }
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++     
++	return retval;
++     
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_send_els_handler
++ *
++ * purpose:     Handler for the Send ELS FSF requests
++ *
++ * returns:     0       - FSF request processed successfuly
++ *              -EINVAL - FSF status is not 0
++ */
++static int zfcp_fsf_send_els_handler(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++
++	zfcp_adapter_t *adapter = fsf_req->adapter;
++	zfcp_port_t *port = fsf_req->data.send_els->port;
++	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
++	fsf_qtcb_bottom_support_t *bottom = &fsf_req->qtcb->bottom.support;
++	struct zfcp_send_els *send_els = fsf_req->data.send_els;
++	u16 subtable, rule, counter;
++	int retval = -EINVAL;
++
++	ZFCP_LOG_TRACE("enter (fsf_req=0x%lx)\n", (unsigned long)fsf_req);
++
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
++		goto skip_fsfstatus;
++
++	switch (header->fsf_status) {
++
++	case FSF_GOOD:
++		ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
++		ZFCP_LOG_INFO(
++			"The FSF request has been successfully completed "
++			"(devno=0x%04x fsf_req.seq_no=%d)\n",
++			adapter->devno,
++			fsf_req->seq_no);
++		retval = 0;
++		break;
++
++	case FSF_SERVICE_CLASS_NOT_SUPPORTED:
++		ZFCP_LOG_FLAGS(2, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n");
++		if (adapter->fc_service_class <= 3) {
++			ZFCP_LOG_INFO(
++				"error: The adapter with devno=0x%04x does "
++				"not support fibre-channel class %d\n",
++				adapter->devno,
++				adapter->fc_service_class);
++		} else {
++			ZFCP_LOG_INFO(
++				"bug: The fibre channel class at the adapter "
++				"with devno 0x%04x is invalid "
++				"(debug info %d)\n",
++				adapter->devno,
++				adapter->fc_service_class);
++		}
++		debug_text_exception(adapter->erp_dbf, 0, "fsf_s_class_nsup");
++		zfcp_erp_adapter_shutdown(port->adapter, 0);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++	case FSF_ACCESS_DENIED:
++		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
++		ZFCP_LOG_NORMAL("Access denied, cannot send ELS "
++				"(devno=0x%04x wwpn=0x%016Lx)\n",
++				adapter->devno,
++				(llui_t)port->wwpn);
++		for (counter = 0; counter < 2; counter++) {
++			subtable = header->fsf_status_qual.halfword[counter * 2];
++			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
++			switch (subtable) {
++			case FSF_SQ_CFDC_SUBTABLE_OS:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
++			case FSF_SQ_CFDC_SUBTABLE_LUN:
++				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
++					zfcp_act_subtable_type[subtable], rule);
++				break;
++			}
++		}
++		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++	case FSF_ELS_COMMAND_REJECTED:
++		ZFCP_LOG_FLAGS(2, "FSF_ELS_COMMAND_REJECTED\n");
++		ZFCP_LOG_INFO(
++			"The ELS command has been rejected because "
++			"a command filter in the FCP channel prohibited "
++			"sending of the ELS to the SAN "
++			"(devno=0x%04x wwpn=0x%016Lx)\n",
++			adapter->devno,
++			(llui_t)port->wwpn);
++		break;
++
++	case FSF_PAYLOAD_SIZE_MISMATCH:
++		ZFCP_LOG_FLAGS(2, "FSF_PAYLOAD_SIZE_MISMATCH\n");
++		ZFCP_LOG_INFO(
++			"ELS request size and ELS response size must be either "
++			"both 0, or both greater than 0 "
++			"(devno=0x%04x req_buf_length=%d resp_buf_length=%d)\n",
++			adapter->devno,
++			bottom->req_buf_length,
++			bottom->resp_buf_length);
++		break;
++
++	case FSF_REQUEST_SIZE_TOO_LARGE:
++		ZFCP_LOG_FLAGS(2, "FSF_REQUEST_SIZE_TOO_LARGE\n");
++		ZFCP_LOG_INFO(
++			"Length of the ELS request buffer, "
++			"specified in QTCB bottom, "
++			"exceeds the size of the buffers "
++			"that have been allocated for ELS request data "
++			"(devno=0x%04x req_buf_length=%d)\n",
++			adapter->devno,
++			bottom->req_buf_length);
++		break;
++
++	case FSF_RESPONSE_SIZE_TOO_LARGE:
++		ZFCP_LOG_FLAGS(2, "FSF_RESPONSE_SIZE_TOO_LARGE\n");
++		ZFCP_LOG_INFO(
++			"Length of the ELS response buffer, "
++			"specified in QTCB bottom, "
++			"exceeds the size of the buffers "
++			"that have been allocated for ELS response data "
++			"(devno=0x%04x resp_buf_length=%d)\n",
++			adapter->devno,
++			bottom->resp_buf_length);
++		break;
++
++	case FSF_ADAPTER_STATUS_AVAILABLE:
++		ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
++		switch (header->fsf_status_qual.word[0]){
++
++		case FSF_SQ_RETRY_IF_POSSIBLE:
++			ZFCP_LOG_FLAGS(2, "FSF_SQ_RETRY_IF_POSSIBLE\n");
++			debug_text_event(adapter->erp_dbf, 1, "fsf_sq_retry");
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			break;
++
++		case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
++			ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
++			debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ulp");
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			retval =
++			  zfcp_handle_els_rjt(header->fsf_status_qual.word[1],
++					      (struct zfcp_ls_rjt_par *)
++					      &header->fsf_status_qual.word[2]);
++			break;
++
++		case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
++			ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
++			debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ltest");
++			if (send_els->ls_code != ZFCP_LS_ADISC)
++				zfcp_test_link(port);
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			break;
++
++		default:
++			ZFCP_LOG_INFO(
++				"bug: Wrong status qualifier 0x%x arrived.\n",
++				header->fsf_status_qual.word[0]);
++			ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
++				(char*)header->fsf_status_qual.word, 16);
++		}
++		break;
++
++	case FSF_UNKNOWN_COMMAND:
++		ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_COMMAND\n");
++		ZFCP_LOG_INFO(
++			"FSF command 0x%x is not supported by FCP adapter "
++			"(devno=0x%04x)\n",
++			fsf_req->fsf_command,
++			adapter->devno);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++	default:
++		ZFCP_LOG_NORMAL(
++			"bug: An unknown FSF Status was presented "
++			"(devno=0x%04x fsf_status=0x%08x)\n",
++			adapter->devno,
++			header->fsf_status);
++		debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval");
++		debug_exception(fsf_req->adapter->erp_dbf, 0,
++			&header->fsf_status_qual.word[0], sizeof(u32));
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++	}
++
++skip_fsfstatus:
++	send_els->status = retval;
++
++	if (send_els->handler != 0)
++		send_els->handler(send_els->handler_data);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/**
++ * zfcp_fsf_send_els - Send an ELS
++ * @*els: to send
++ * Returns: 0 on success, -E* code else
++ *
++ * Create a FSF request from an ELS and queue it for sending. Chaining is used
++ * if needed and supported (in that order).
++ */
++int zfcp_fsf_send_els(struct zfcp_send_els *els)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	unsigned long lock_flags;
++	int retval;
++	zfcp_fsf_req_t *fsf_req;
++	zfcp_port_t *port = els->port;
++	zfcp_adapter_t *adapter = port->adapter;
++	volatile struct qdio_buffer_element_t *sbale;
++        int bytes;
++
++        retval = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS,
++                                     ZFCP_REQ_AUTO_CLEANUP,
++                                     NULL, &lock_flags, &fsf_req);
++	if (retval < 0) {
++                ZFCP_LOG_INFO("error: Out of resources. "
++                              "Could not create an ELS request, "
++                              "destination port D_ID is 0x%06x "
++                              "at the adapter with devno 0x%04x.\n",
++                              port->d_id, adapter->devno);
++                goto failed_req;
++	}
++
++	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
++        if (zfcp_use_one_sbal(els->req, els->req_count,
++                              els->resp, els->resp_count)){
++                /* both request buffer and response buffer
++                   fit into one sbale each */
++                sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
++                sbale[2].addr = els->req[0].address;
++                sbale[2].length = els->req[0].length;
++                sbale[3].addr = els->resp[0].address;
++                sbale[3].length = els->resp[0].length;
++                sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
++        } else if (adapter->supported_features &
++                   FSF_FEATURE_ELS_CT_CHAINED_SBALS) {
++                /* try to use chained SBALs */
++                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
++                                                SBAL_FLAGS0_TYPE_WRITE_READ,
++                                                els->req, els->req_count,
++                                                ZFCP_MAX_SBALS_PER_ELS_REQ);
++                if (bytes <= 0) {
++                        ZFCP_LOG_INFO("error: Out of resources (outbuf). "
++                                      "Could not create an ELS request, "
++                                      "destination port D_ID is 0x%06x "
++                                      "at the adapter with devno 0x%04x.\n",
++                                      port->d_id, adapter->devno);
++                        if (bytes == 0) {
++                                retval = -ENOMEM;
++                        } else {
++                                retval = bytes;
++                        }
++                        goto failed_send;
++                }
++                fsf_req->qtcb->bottom.support.req_buf_length = bytes;
++                fsf_req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
++                bytes = zfcp_qdio_sbals_from_sg(fsf_req,
++                                                SBAL_FLAGS0_TYPE_WRITE_READ,
++                                                els->resp, els->resp_count,
++                                                ZFCP_MAX_SBALS_PER_ELS_REQ);
++                if (bytes <= 0) {
++                        ZFCP_LOG_INFO("error: Out of resources (inbuf). "
++                                      "Could not create an ELS request, "
++                                      "destination port D_ID is 0x%06x "
++                                      "at the adapter with devno 0x%04x.\n",
++                                      port->d_id, adapter->devno);
++                        if (bytes == 0) {
++                                retval = -ENOMEM;
++                        } else {
++                                retval = bytes;
++                        }
++                        goto failed_send;
++                }
++                fsf_req->qtcb->bottom.support.resp_buf_length = bytes;
++        } else {
++                /* reject request */
++		ZFCP_LOG_INFO("error: microcode does not support chained SBALs."
++                              "ELS request too big."
++                              "Destination port D_ID is 0x%06x "
++                              "at the adapter with devno 0x%04x.\n",
++                              port->d_id, adapter->devno);
++                retval = -EOPNOTSUPP;
++                goto failed_send;
++        }
++
++	/* settings in QTCB */
++	fsf_req->qtcb->bottom.support.d_id = port->d_id;
++	fsf_req->qtcb->bottom.support.service_class = adapter->fc_service_class;
++	fsf_req->qtcb->bottom.support.timeout = ZFCP_ELS_TIMEOUT;
++	fsf_req->data.send_els = els;
++
++	/* start QDIO request for this FSF request */
++	retval = zfcp_fsf_req_send(fsf_req, NULL);
++	if (retval) {
++		ZFCP_LOG_DEBUG("error: Out of resources. Could not send an "
++                               "ELS command via adapter with "
++                               "devno 0x%04x, port WWPN 0x%016Lx\n",
++                               adapter->devno,	(llui_t) port->wwpn);
++		goto failed_send;
++	} else {
++                ZFCP_LOG_DEBUG("ELS request initiated "
++                               "(adapter devno=0x%04x, port D_ID=0x%06x)\n",
++                               adapter->devno, port->d_id);
++                goto out;
++        }
++
++ failed_send:
++	if (zfcp_fsf_req_free(fsf_req)) {
++                ZFCP_LOG_NORMAL("bug: Could not remove one FSF request. Memory "
++                                "leakage possible. (debug info 0x%lx).\n",
++                                (unsigned long)fsf_req);
++                retval = -EINVAL;
++	};
++ failed_req:
++ out:
++	write_unlock_irqrestore(&adapter->request_queue.queue_lock,
++                                     lock_flags);
++
++        return retval;
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++static inline volatile qdio_buffer_element_t * zfcp_qdio_sbale_get(
++		zfcp_qdio_queue_t *queue,
++		int sbal,
++		int sbale)
++{
++	return &queue->buffer[sbal]->element[sbale];
++}
++
++
++static inline volatile qdio_buffer_element_t * zfcp_qdio_sbale_req(
++		zfcp_fsf_req_t *fsf_req,
++		int sbal,
++		int sbale)
++{
++	return zfcp_qdio_sbale_get(
++			&fsf_req->adapter->request_queue,
++			sbal,
++			sbale);
++}
++
++
++static inline volatile qdio_buffer_element_t * zfcp_qdio_sbale_resp(
++		zfcp_fsf_req_t *fsf_req,
++		int sbal,
++		int sbale)
++{
++	return zfcp_qdio_sbale_get(
++			&fsf_req->adapter->response_queue,
++			sbal,
++			sbale);
++}
++
++
++/* the following routines work on outbound queues */
++static inline volatile qdio_buffer_element_t * zfcp_qdio_sbale_curr(
++		zfcp_fsf_req_t *fsf_req)
++{
++	return zfcp_qdio_sbale_req(
++			fsf_req,
++			fsf_req->sbal_curr,
++			fsf_req->sbale_curr);
++}
++
++
++/* can assume at least one free SBAL in outbound queue when called */
++static inline void zfcp_qdio_sbal_limit(zfcp_fsf_req_t *fsf_req, int max_sbals)
++{
++	int count = atomic_read(&fsf_req->adapter->request_queue.free_count);
++	count = min(count, max_sbals);
++	fsf_req->sbal_last  = fsf_req->sbal_first;
++	fsf_req->sbal_last += (count - 1);
++	fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
++}
++
++
++static inline volatile qdio_buffer_element_t * zfcp_qdio_sbal_chain(
++		zfcp_fsf_req_t *fsf_req,
++		unsigned long sbtype)
++{
++	volatile qdio_buffer_element_t *sbale;
++
++	/* set last entry flag in current SBALE of current SBAL */
++	sbale = zfcp_qdio_sbale_curr(fsf_req);
++	sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
++
++	/* don't exceed last allowed SBAL */
++	if (fsf_req->sbal_curr == fsf_req->sbal_last)
++		return NULL;
++
++	/* set chaining flag in first SBALE of current SBAL */
++	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
++	sbale->flags |= SBAL_FLAGS0_MORE_SBALS;
++
++	/* calculate index of next SBAL */
++	fsf_req->sbal_curr++;
++	fsf_req->sbal_curr %= QDIO_MAX_BUFFERS_PER_Q;
++
++	/* keep this requests number of SBALs up-to-date */
++	fsf_req->sbal_number++;
++
++	/* start at first SBALE of new SBAL */
++	fsf_req->sbale_curr = 0;
++
++	/* set storage-block type for new SBAL */
++	sbale = zfcp_qdio_sbale_curr(fsf_req);
++	sbale->flags |= sbtype;
++
++	return sbale;
++}
++
++
++static inline volatile qdio_buffer_element_t * zfcp_qdio_sbale_next(
++		zfcp_fsf_req_t *fsf_req, unsigned long sbtype)
++{
++	if (fsf_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL)
++		return zfcp_qdio_sbal_chain(fsf_req, sbtype);
++
++	fsf_req->sbale_curr++;
++
++	return zfcp_qdio_sbale_curr(fsf_req);
++}
++
++
++static inline int zfcp_qdio_sbals_zero(
++		zfcp_qdio_queue_t *queue,
++		int first,
++		int last)
++{
++	qdio_buffer_t **buf = queue->buffer;
++	int curr = first;
++	int count = 0;
++
++	for(;;) {
++		curr %= QDIO_MAX_BUFFERS_PER_Q;
++		count++;
++		memset(buf[curr], 0, sizeof(qdio_buffer_t));
++		if (curr == last)
++			break;
++		curr++;
++	}
++	return count;
++}
++
++
++static inline int zfcp_qdio_sbals_wipe(
++		zfcp_fsf_req_t *fsf_req)
++{
++	return zfcp_qdio_sbals_zero(
++			&fsf_req->adapter->request_queue,
++			fsf_req->sbal_first,
++			fsf_req->sbal_curr);
++}
++
++
++static inline void zfcp_qdio_sbale_fill(
++		zfcp_fsf_req_t *fsf_req,
++		unsigned long sbtype,
++		void *addr,
++		int length)
++{
++	volatile qdio_buffer_element_t *sbale = zfcp_qdio_sbale_curr(fsf_req);
++
++	sbale->addr = addr;
++	sbale->length = length;
++}
++
++
++static inline int zfcp_qdio_sbals_from_segment(
++		zfcp_fsf_req_t *fsf_req,
++		unsigned long sbtype,
++		void* start_addr,
++		unsigned long total_length)
++{
++	unsigned long remaining, length;
++	void *addr;
++
++	/* split segment up heeding page boundaries */
++	for (addr = start_addr,
++	     remaining = total_length;
++	     remaining;
++	     addr += length,
++	     remaining -= length) {
++		/* get next free SBALE for new piece */
++		if (!zfcp_qdio_sbale_next(fsf_req, sbtype)) {
++			/* no SBALE left, clean up and leave */
++			zfcp_qdio_sbals_wipe(fsf_req);
++			return -EINVAL;
++		}
++		/* calculate length of new piece */
++		length = min(remaining,
++			     (PAGE_SIZE - ((unsigned long)addr & (PAGE_SIZE - 1))));
++		/* fill current SBALE with calculated piece */
++		zfcp_qdio_sbale_fill(fsf_req, sbtype, addr, length);
++	}
++	return total_length;
++}
++
++
++/* for exploiters with a scatter-gather list ready at hand */
++static inline int
++zfcp_qdio_sbals_from_sg(zfcp_fsf_req_t *fsf_req, unsigned long sbtype,
++                        struct scatterlist *sg,	int sg_count, int max_sbals)
++{
++	int sg_index;
++	struct scatterlist *sg_segment;
++	int bytes, retval;
++	volatile qdio_buffer_element_t *sbale;
++	zfcp_adapter_t *adapter;
++
++	/* figure out last allowed SBAL */
++	zfcp_qdio_sbal_limit(fsf_req, max_sbals);
++
++	/* set storage-block type for current SBAL */
++	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
++	sbale->flags |= sbtype;
++
++	/* process all segements of scatter-gather list */
++	for (sg_index = 0, sg_segment = sg, bytes = 0;
++	     sg_index < sg_count;
++	     sg_index++, sg_segment++) {
++		retval = zfcp_qdio_sbals_from_segment(
++				fsf_req,
++				sbtype,
++				sg_segment->address,
++				sg_segment->length);
++		if (retval < 0)
++			return retval;
++		bytes += retval;
++	}
++	/* assume that no other SBALEs are to follow in the same SBAL */
++	sbale = zfcp_qdio_sbale_curr(fsf_req);
++	sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
++
++#ifdef ZFCP_STAT_REQSIZES
++	adapter = fsf_req->adapter;
++	if (sbtype == SBAL_FLAGS0_TYPE_READ)
++		zfcp_statistics_inc(adapter, &adapter->read_req_head, bytes);
++	else	zfcp_statistics_inc(adapter, &adapter->write_req_head, bytes);
++#endif
++	return bytes;
++}
++
++
++/* for exploiters with just a buffer ready at hand */
++static inline int zfcp_qdio_sbals_from_buffer(
++		zfcp_fsf_req_t *fsf_req,
++		unsigned long sbtype,
++		void *buffer,
++		unsigned long length,
++		int max_sbals)
++{
++	struct scatterlist sg_segment;
++
++	sg_segment.address = buffer;
++	sg_segment.length = length;
++
++	return zfcp_qdio_sbals_from_sg(fsf_req, sbtype, &sg_segment, 1,
++                                       max_sbals);
++}
++
++
++/* for exploiters with a SCSI command ready at hand */
++static inline int zfcp_qdio_sbals_from_scsicmnd(
++		zfcp_fsf_req_t *fsf_req,
++		unsigned long sbtype,
++		struct scsi_cmnd *scsi_cmnd)
++{
++	if (scsi_cmnd->use_sg)
++		return zfcp_qdio_sbals_from_sg(fsf_req,	sbtype,
++                                               (struct scatterlist *)
++                                               scsi_cmnd->request_buffer,
++                                               scsi_cmnd->use_sg,
++                                               ZFCP_MAX_SBALS_PER_REQ);
++	else
++                return zfcp_qdio_sbals_from_buffer(fsf_req, sbtype,
++                                                   scsi_cmnd->request_buffer,
++                                                   scsi_cmnd->request_bufflen,
++                                                   ZFCP_MAX_SBALS_PER_REQ);
++}
++
++/*
++ * function:
++ *
++ * purpose:
++ *
++ * returns:	address of initiated FSF request
++ *		NULL - request could not be initiated
++ */
++static int zfcp_fsf_exchange_config_data(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	volatile qdio_buffer_element_t *sbale;
++	int retval = 0;
++	unsigned long lock_flags;
++
++	ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
++
++	/* setup new FSF request */
++	retval = zfcp_fsf_req_create(
++			erp_action->adapter,
++			FSF_QTCB_EXCHANGE_CONFIG_DATA,
++			ZFCP_REQ_AUTO_CLEANUP,
++                        &erp_action->adapter->pool.fsf_req_erp,
++			&lock_flags,
++			&erp_action->fsf_req);
++	if (retval < 0) {
++                ZFCP_LOG_INFO(
++			 "error: Out of resources. Could not create an "
++                         "exchange configuration data request for"
++                         "the adapter with devno 0x%04x.\n",
++                         erp_action->adapter->devno);
++		goto out;
++	}
++
++
++	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
++                                    erp_action->fsf_req->sbal_curr, 0);
++        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
++        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
++
++	erp_action->fsf_req->erp_action = erp_action;
++	erp_action->fsf_req->qtcb->bottom.config.feature_selection =
++		FSF_FEATURE_CFDC;
++
++	/* start QDIO request for this FSF request */
++        retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
++        if (retval) {
++		ZFCP_LOG_INFO(
++			"error: Could not send an exchange configuration data "
++			"command on the adapter with devno 0x%04x\n",
++			erp_action->adapter->devno);
++                if (zfcp_fsf_req_free(erp_action->fsf_req)) {
++			ZFCP_LOG_NORMAL(
++				"bug: Could not remove one FSF "
++				"request. Memory leakage possible. "
++                                "(debug info 0x%lx).\n",
++                                (unsigned long)erp_action->fsf_req);
++                }
++		erp_action->fsf_req = NULL;
++                goto out;
++        }
++ 
++        ZFCP_LOG_DEBUG(
++                "Exchange Configuration Data request initiated "
++                "(adapter devno=0x%04x)\n",
++                erp_action->adapter->devno);
++
++out:
++        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
++
++        ZFCP_LOG_TRACE("exit (%d)\n", retval);
++ 
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/**
++ * zfcp_fsf_exchange_config_evaluate
++ * @fsf_req: fsf_req which belongs to xchg config data request
++ * @xchg_ok: specifies if xchg config data was incomplete or complete (0/1)
++ *
++ * returns: -EIO on error, 0 otherwise
++ */
++static int
++zfcp_fsf_exchange_config_evaluate(zfcp_fsf_req_t *fsf_req, int xchg_ok)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	fsf_qtcb_bottom_config_t *bottom;
++	zfcp_adapter_t *adapter = fsf_req->adapter;
++
++	bottom = &fsf_req->qtcb->bottom.config;
++	ZFCP_LOG_DEBUG(
++		"low/high QTCB version 0x%x/0x%x of FSF\n",
++		bottom->low_qtcb_version, bottom->high_qtcb_version);
++	adapter->fsf_lic_version = bottom->lic_version;
++	adapter->supported_features = bottom->supported_features;
++
++	if (xchg_ok) {
++		adapter->wwnn = bottom->nport_serv_param.wwnn;
++		adapter->wwpn = bottom->nport_serv_param.wwpn;
++		adapter->s_id = bottom->s_id & ZFCP_DID_MASK;
++		adapter->fc_topology = bottom->fc_topology;
++		adapter->fc_link_speed = bottom->fc_link_speed;
++		adapter->hydra_version = bottom->adapter_type;
++	} else {
++		adapter->wwnn = 0;
++		adapter->wwpn = 0;
++		adapter->s_id = 0;
++		adapter->fc_topology = 0;
++		adapter->fc_link_speed = 0;
++		adapter->hydra_version = 0;
++	}
++
++	if (adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT) {
++		adapter->hardware_version = bottom->hardware_version;
++		memcpy(adapter->serial_number, bottom->serial_number, 17);
++		EBCASC(adapter->serial_number, sizeof(adapter->serial_number));
++	}
++
++	ZFCP_LOG_INFO(
++		"The adapter with devno=0x%04x reported "
++		"the following characteristics:\n"
++		"WWNN 0x%016Lx, WWPN 0x%016Lx, S_ID 0x%08x,\n"
++		"adapter version 0x%x, LIC version 0x%x, "
++		"FC link speed %d Gb/s\n",
++		adapter->devno,
++		(llui_t) adapter->wwnn, (llui_t) adapter->wwpn,
++		(unsigned int) adapter->s_id,
++		adapter->hydra_version,
++		adapter->fsf_lic_version,
++		adapter->fc_link_speed);
++	if (ZFCP_QTCB_VERSION < bottom->low_qtcb_version) {
++		ZFCP_LOG_NORMAL(
++			"error: the adapter with devno 0x%04x "
++			"only supports newer control block "
++			"versions in comparison to this device "
++			"driver (try updated device driver)\n",
++			adapter->devno);
++		debug_text_event(adapter->erp_dbf, 0, "low_qtcb_ver");
++		zfcp_erp_adapter_shutdown(adapter, 0);
++		return -EIO;
++	}
++	if (ZFCP_QTCB_VERSION > bottom->high_qtcb_version) {
++		ZFCP_LOG_NORMAL(
++			"error: the adapter with devno 0x%04x "
++			"only supports older control block "
++			"versions than this device driver uses"
++			"(consider a microcode upgrade)\n",
++			adapter->devno);
++		debug_text_event(adapter->erp_dbf, 0, "high_qtcb_ver");
++		zfcp_erp_adapter_shutdown(adapter, 0);
++		return -EIO;
++	}
++	return 0;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_exchange_config_data_handler
++ *
++ * purpose:     is called for finished Exchange Configuration Data command
++ *
++ * returns:
++ */
++int zfcp_fsf_exchange_config_data_handler
++	(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	int retval = -EIO;
++	fsf_qtcb_bottom_config_t *bottom;
++	zfcp_adapter_t *adapter = fsf_req->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++                (unsigned long)fsf_req);
++
++        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		/* don't set any value, stay with the old (unitialized) ones */ 
++                goto skip_fsfstatus;
++        }
++
++	switch (fsf_req->qtcb->header.fsf_status) {
++
++        case FSF_GOOD :
++                ZFCP_LOG_FLAGS(2,"FSF_GOOD\n");
++		if (zfcp_fsf_exchange_config_evaluate(fsf_req, 1))
++			goto skip_fsfstatus;
++                switch (adapter->fc_topology) {
++                case FSF_TOPO_P2P:
++                        ZFCP_LOG_FLAGS(1,"FSF_TOPO_P2P\n");
++                        ZFCP_LOG_NORMAL("error: Point-to-point fibre-channel "
++                                      "configuration detected "
++                                      "at the adapter with devno "
++                                      "0x%04x, not supported, shutting down adapter\n",
++                                      adapter->devno);
++                        debug_text_event(fsf_req->adapter->erp_dbf,0,"top-p-to-p");
++			zfcp_erp_adapter_shutdown(adapter, 0);
++			goto skip_fsfstatus;
++                case FSF_TOPO_AL:
++                        ZFCP_LOG_FLAGS(1,"FSF_TOPO_AL\n");
++                        ZFCP_LOG_NORMAL("error: Arbitrated loop fibre-channel "
++                                      "topology detected "
++                                      "at the adapter with devno "
++                                      "0x%04x, not supported, shutting down adapter\n",
++                                      adapter->devno);
++                        debug_text_event(fsf_req->adapter->erp_dbf,0,"top-al");
++			zfcp_erp_adapter_shutdown(adapter, 0);
++                        goto skip_fsfstatus;
++                case FSF_TOPO_FABRIC:
++                        ZFCP_LOG_FLAGS(1,"FSF_TOPO_FABRIC\n");
++                        ZFCP_LOG_INFO("Switched fabric fibre-channel "
++                                      "network detected "
++                                      "at the adapter with devno "
++                                      "0x%04x\n",
++                                      adapter->devno);
++                        break;
++                default:
++                        ZFCP_LOG_NORMAL("bug: The fibre-channel topology "
++                                        "reported by the exchange "
++                                        "configuration command for "
++                                        "the adapter with devno "
++                                        "0x%04x is not "
++                                        "of a type known to the zfcp "
++                                        "driver, shutting down adapter\n",
++                                        adapter->devno);
++                        debug_text_exception(fsf_req->adapter->erp_dbf,0,
++                                             "unknown-topo");
++			zfcp_erp_adapter_shutdown(adapter, 0);
++                        goto skip_fsfstatus;
++                }
++		bottom = &fsf_req->qtcb->bottom.config;
++                if (bottom->max_qtcb_size < sizeof(fsf_qtcb_t)) {
++                        ZFCP_LOG_NORMAL("bug: Maximum QTCB size (%d bytes) "
++                                        "allowed by the adapter with devno "
++                                        "0x%04x is lower than the minimum "
++                                        "required by the driver (%ld bytes).\n",
++                                        bottom->max_qtcb_size,
++                                        adapter->devno,
++                                        sizeof(fsf_qtcb_t));
++                        debug_text_event(fsf_req->adapter->erp_dbf,0,"qtcb-size");
++                        debug_event(fsf_req->adapter->erp_dbf,0,&bottom->max_qtcb_size,
++                                    sizeof(u32)); 
++			zfcp_erp_adapter_shutdown(adapter, 0);
++			goto skip_fsfstatus;
++                }
++                atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
++                retval = 0;
++
++                zfcp_callback_do_adapter_add(NULL, adapter);
++
++                break;
++
++	case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE:
++		debug_text_event(adapter->erp_dbf, 0, "xchg-inco");
++
++		if (zfcp_fsf_exchange_config_evaluate(fsf_req, 0))
++			goto skip_fsfstatus;
++
++		ZFCP_LOG_INFO(
++			"Local link to adapter with devno 0x%04x is down\n",
++			adapter->devno);
++		atomic_set_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK |
++				ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED,
++				&adapter->status);
++		zfcp_erp_adapter_failed(adapter);
++		break;
++
++        default:
++                /* retval is -EIO by default */
++                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf-stat-ng");
++                debug_event(fsf_req->adapter->erp_dbf,0,
++                            &fsf_req->qtcb->header.fsf_status,
++                            sizeof(u32)); 
++                zfcp_erp_adapter_shutdown(adapter, 0);
++	}
++        
++ skip_fsfstatus:
++        
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++int zfcp_fsf_exchange_port_data(zfcp_adapter_t *adapter,
++                                fsf_qtcb_bottom_port_t *data)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	volatile qdio_buffer_element_t *sbale;
++	int retval = 0;
++        int status;
++	unsigned long lock_flags;
++        zfcp_fsf_req_t *fsf_req;
++
++        if(!(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT)){
++		ZFCP_LOG_INFO("error: exchange port data "
++                              "command not supported by adapter  0x%4.4x\n",
++                              adapter->devno);
++                retval = ENOTSUPP;
++                goto out;
++        }
++
++	/* setup new FSF request */
++	retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
++                                     0, 0, &lock_flags, &fsf_req);
++	if (retval < 0) {
++		ZFCP_LOG_INFO("error: Out of resources. Could not create an "
++                              "exchange port data request for"
++                              "the adapter with devno 0x%4.4x.\n",
++                              adapter->devno);
++		write_unlock_irqrestore(&adapter->request_queue.queue_lock,
++                                             lock_flags);
++		goto out;
++	}
++
++	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
++        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
++        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
++
++        fsf_req->data.port_data = data;
++
++	/* start QDIO request for this FSF request */
++	retval = zfcp_fsf_req_send(fsf_req, NULL);
++	if (retval) {
++		ZFCP_LOG_INFO("error: Could not send an exchange port data "
++                              "command on the adapter with devno 0x%4.4x\n",
++                              adapter->devno);
++		if (zfcp_fsf_req_free(fsf_req)) {
++			ZFCP_LOG_NORMAL("bug: Could not remove one FSF "
++					"request. Memory leakage possible. "
++					"(debug info 0x%lx).\n",
++					(unsigned long)fsf_req);
++		}
++		fsf_req = NULL;
++		write_unlock_irqrestore(&adapter->request_queue.queue_lock,
++                                             lock_flags);
++		goto out;
++	}
++
++	ZFCP_LOG_DEBUG("Exchange Port Data request initiated "
++                       "(adapter devno=0x%x)\n", adapter->devno);
++
++
++	write_unlock_irqrestore(&adapter->request_queue.queue_lock,
++                                     lock_flags);
++
++        /* FIXME: could we wait interruptible here ? */
++	retval = zfcp_fsf_req_wait_and_cleanup(fsf_req, ZFCP_UNINTERRUPTIBLE,
++                                               &status);
++
++ out:
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++static int zfcp_fsf_exchange_port_data_handler(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	int retval = -EIO;
++	fsf_qtcb_bottom_port_t *bottom;
++	fsf_qtcb_bottom_port_t *data = fsf_req->data.port_data;
++
++	ZFCP_LOG_TRACE("enter (fsf_req=0x%lx)\n",
++                       (unsigned long)fsf_req);
++
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		/* don't set any value, stay with the old (unitialized) ones */ 
++		goto skip_fsfstatus;
++	}
++
++	/* evaluate FSF status in QTCB */
++	switch (fsf_req->qtcb->header.fsf_status) {
++        case FSF_GOOD :
++                ZFCP_LOG_FLAGS(2,"FSF_GOOD\n");
++                bottom = &fsf_req->qtcb->bottom.port;
++                memcpy(data, bottom, sizeof(fsf_qtcb_bottom_port_t));
++                retval = 0;
++                break;
++        default:
++                /* retval is -EIO by default */
++                debug_text_event(fsf_req->adapter->erp_dbf, 0,
++                                 "fsf-stat-ng");
++                debug_event(fsf_req->adapter->erp_dbf,0,
++                            &fsf_req->qtcb->header.fsf_status,
++                            sizeof(u32)); 
++	}
++
++ skip_fsfstatus:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:    zfcp_fsf_open_port
++ *
++ * purpose:	
++ *
++ * returns:	address of initiated FSF request
++ *		NULL - request could not be initiated 
++ */
++static int zfcp_fsf_open_port(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	volatile qdio_buffer_element_t *sbale;
++	int retval = 0;
++	unsigned long lock_flags;
++ 
++	ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
++
++	/* setup new FSF request */
++	retval = zfcp_fsf_req_create(
++			erp_action->adapter,
++			FSF_QTCB_OPEN_PORT_WITH_DID,
++			ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
++                        &erp_action->adapter->pool.fsf_req_erp,
++			&lock_flags,
++			&erp_action->fsf_req);
++	if (retval < 0) {
++		ZFCP_LOG_INFO(
++			"error: Out of resources. Could not create an "
++			"open port request for "
++			"the port with WWPN 0x%016Lx connected to "
++			"the adapter with devno 0x%04x.\n",
++			(llui_t)erp_action->port->wwpn,
++			erp_action->adapter->devno);
++		goto out;
++	}
++
++	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
++                                    erp_action->fsf_req->sbal_curr, 0);
++        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
++        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
++
++	erp_action->fsf_req->qtcb->bottom.support.d_id = erp_action->port->d_id;
++	atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->port->status);
++	erp_action->fsf_req->data.open_port.port = erp_action->port;
++	erp_action->fsf_req->erp_action = erp_action;
++
++	/* start QDIO request for this FSF request */
++	retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
++	if (retval) {
++                ZFCP_LOG_INFO(
++			"error: Could not send an "
++                        "open port request for "
++                        "the port with WWPN 0x%016Lx connected to "
++                        "the adapter with devno 0x%04x.\n",
++                        (llui_t)erp_action->port->wwpn,
++                        erp_action->adapter->devno);
++		if (zfcp_fsf_req_free(erp_action->fsf_req)) {
++			ZFCP_LOG_NORMAL(
++				"bug: Could not remove one FSF "
++				"request. Memory leakage possible. "
++                                "(debug info 0x%lx).\n",
++                                (unsigned long)erp_action->fsf_req);
++                        retval=-EINVAL;
++                };
++		erp_action->fsf_req = NULL;
++		goto out;
++	}
++
++	ZFCP_LOG_DEBUG(
++		"Open Port request initiated "
++		"(adapter devno=0x%04x, port WWPN=0x%016Lx)\n",
++		erp_action->adapter->devno,
++		(llui_t)erp_action->port->wwpn);
++	
++out:
++        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
++
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_open_port_handler
++ *
++ * purpose:	is called for finished Open Port command
++ *
++ * returns:	
++ */
++static int zfcp_fsf_open_port_handler(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = -EINVAL;
++	zfcp_port_t *port;
++	fsf_plogi_t *plogi;
++	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
++	u16 subtable, rule, counter;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++     
++	port = fsf_req->data.open_port.port;
++
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		/* don't change port status in our bookkeeping */
++		goto skip_fsfstatus;
++	}
++     
++	/* evaluate FSF status in QTCB */
++	switch (fsf_req->qtcb->header.fsf_status) {
++          
++        case FSF_PORT_ALREADY_OPEN :
++                ZFCP_LOG_FLAGS(0, "FSF_PORT_ALREADY_OPEN\n");
++                ZFCP_LOG_NORMAL("bug: The remote port with WWPN=0x%016Lx "
++                                "connected to the adapter with "
++                                "devno=0x%04x is already open.\n",
++                                (llui_t)port->wwpn,
++                                port->adapter->devno);
++                debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_s_popen");
++                /* This is a bug, however operation should continue normally
++                 * if it is simply ignored */
++                break;
++                
++	case FSF_ACCESS_DENIED :
++		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
++		ZFCP_LOG_NORMAL("Access denied, cannot open port "
++				"(devno=0x%04x wwpn=0x%016Lx)\n",
++				port->adapter->devno,
++				(llui_t)port->wwpn);
++		for (counter = 0; counter < 2; counter++) {
++			subtable = header->fsf_status_qual.halfword[counter * 2];
++			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
++			switch (subtable) {
++			case FSF_SQ_CFDC_SUBTABLE_OS:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
++			case FSF_SQ_CFDC_SUBTABLE_LUN:
++				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
++					zfcp_act_subtable_type[subtable], rule);
++				break;
++			}
++		}
++		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
++		zfcp_erp_port_failed(port);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++        case FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED :
++                ZFCP_LOG_FLAGS(1, "FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED\n");
++                ZFCP_LOG_INFO("error: The FSF adapter is out of resources. "
++                              "The remote port with WWPN=0x%016Lx "
++                              "connected to the adapter with "
++                              "devno=0x%04x could not be opened. "
++                              "Disabling it.\n",
++                              (llui_t)port->wwpn,
++                              port->adapter->devno);
++                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_max_ports");
++                zfcp_erp_port_failed(port);
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++
++        case FSF_ADAPTER_STATUS_AVAILABLE :
++                ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
++                switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
++                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
++                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
++                        /* ERP strategy will escalate */
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;
++                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
++                        /* ERP strategy will escalate */
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;  
++                case FSF_SQ_NO_RETRY_POSSIBLE :
++                        ZFCP_LOG_FLAGS(0, "FSF_SQ_NO_RETRY_POSSIBLE\n");
++                        ZFCP_LOG_NORMAL("The remote port with WWPN=0x%016Lx "
++                                        "connected to the adapter with "
++                                        "devno=0x%04x could not be opened. "
++                                        "Disabling it.\n",
++                                        (llui_t)port->wwpn,
++                                        port->adapter->devno);
++                        debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_sq_no_retry");
++                        zfcp_erp_port_failed(port);
++                        zfcp_cmd_dbf_event_fsf("sqnretry", fsf_req,
++                                               &fsf_req->qtcb->header.fsf_status_qual,
++                                               sizeof(fsf_status_qual_t));
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                        break;
++                default:
++                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
++                                        fsf_req->qtcb->header.fsf_status_qual.word[0]);
++                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
++                        debug_exception(fsf_req->adapter->erp_dbf,0,
++                                        &fsf_req->qtcb->header.fsf_status_qual.word[0],
++                                        sizeof(u32));
++                        break;
++                }
++                break;
++
++        case FSF_GOOD :
++                ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
++                /* save port handle assigned by FSF */
++                port->handle = fsf_req->qtcb->header.port_handle;
++                ZFCP_LOG_INFO("The remote port (WWPN=0x%016Lx) via adapter "
++                              "(devno=0x%04x) was opened, it's "
++                              "port handle is 0x%x\n",
++                              (llui_t)port->wwpn,
++                              port->adapter->devno,
++                              port->handle);
++                /* mark port as open */
++                atomic_set_mask(
++			ZFCP_STATUS_COMMON_OPEN |
++			ZFCP_STATUS_PORT_PHYS_OPEN,
++			&port->status);
++                retval = 0;
++		/* check whether D_ID has changed during open */
++		/*
++		 * FIXME: This check is not airtight, as the FCP channel does
++		 * not monitor closures of target port connections caused on
++		 * the remote side. Thus, they might miss out on invalidating
++		 * locally cached WWPNs (and other N_Port parameters) of gone
++		 * target ports. So, our heroic attempt to make things safe
++		 * could be undermined by 'open port' response data tagged with
++		 * obsolete WWPNs. Another reason to monitor potential
++		 * connection closures ourself at least (by interpreting
++		 * incoming ELS' and unsolicited status). It just crosses my
++		 * mind that one should be able to cross-check by means of
++		 * another GID_PN straight after a port has been opened.
++		 * Alternately, an ADISC/PDISC ELS should suffice, as well.
++		 */
++		plogi = (fsf_plogi_t*) fsf_req->qtcb->bottom.support.els;
++		if (!atomic_test_mask(ZFCP_STATUS_PORT_NO_WWPN, &port->status)) {
++			if (fsf_req->qtcb->bottom.support.els1_length <
++			    ((((unsigned long)&plogi->serv_param.wwpn) -
++			      ((unsigned long)plogi)) +
++			     sizeof(fsf_wwn_t))) {
++				ZFCP_LOG_INFO(
++					"warning: insufficient length of PLOGI payload (%i)\n",
++					fsf_req->qtcb->bottom.support.els1_length);
++				debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_short_plogi:");
++				/* skip sanity check and assume wwpn is ok */
++			} else	{
++				if (plogi->serv_param.wwpn != port->wwpn) {
++					ZFCP_LOG_INFO(
++						"warning: D_ID of port with WWPN 0x%016Lx changed "
++						"during open\n",
++						(llui_t)port->wwpn);
++					debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_s_did_change:");
++					atomic_clear_mask(
++						ZFCP_STATUS_PORT_DID_DID,
++						&port->status);
++				}
++			}
++		}
++                break;
++                
++	default :
++		ZFCP_LOG_NORMAL(
++			"bug: An unknown FSF Status was presented "
++			"(debug info 0x%x)\n",
++			fsf_req->qtcb->header.fsf_status);
++		debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
++		debug_exception(fsf_req->adapter->erp_dbf,0,
++				&fsf_req->qtcb->header.fsf_status,
++				sizeof(u32));
++		break;
++	}
++
++skip_fsfstatus:
++
++	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &port->status);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_close_port
++ *
++ * purpose:     submit FSF command "close port"
++ *
++ * returns:     address of initiated FSF request
++ *              NULL - request could not be initiated
++ */
++static int zfcp_fsf_close_port(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	volatile qdio_buffer_element_t *sbale;
++        int retval = 0;
++	unsigned long lock_flags;
++
++        ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
++
++        /* setup new FSF request */
++        retval = zfcp_fsf_req_create(
++			erp_action->adapter,
++			FSF_QTCB_CLOSE_PORT,
++			ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
++                        &erp_action->adapter->pool.fsf_req_erp,
++			&lock_flags,
++			&erp_action->fsf_req);
++        if (retval < 0) {
++		ZFCP_LOG_INFO(
++			"error: Out of resources. Could not create a "
++			"close port request for WWPN 0x%016Lx connected to "
++			"the adapter with devno 0x%04x.\n",
++			(llui_t)erp_action->port->wwpn,
++			erp_action->adapter->devno);
++		goto out;
++        }
++
++	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
++                                    erp_action->fsf_req->sbal_curr, 0);
++        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
++        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
++
++        atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->port->status);
++        erp_action->fsf_req->data.close_port.port = erp_action->port;
++	erp_action->fsf_req->erp_action = erp_action;
++        erp_action->fsf_req->qtcb->header.port_handle = erp_action->port->handle;
++
++        /* start QDIO request for this FSF request */
++        retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
++        if (retval) {
++		ZFCP_LOG_INFO(
++			"error: Could not send a "
++			"close port request for WWPN 0x%016Lx connected to "
++			"the adapter with devno 0x%04x.\n",
++			(llui_t)erp_action->port->wwpn,
++			erp_action->adapter->devno);
++		if (zfcp_fsf_req_free(erp_action->fsf_req)) {
++			ZFCP_LOG_NORMAL(
++				"bug: Could not remove one FSF "
++				"request. Memory leakage possible. "
++				"(debug info 0x%lx).\n",
++				(unsigned long)erp_action->fsf_req);
++                        retval=-EINVAL;
++		};
++		erp_action->fsf_req = NULL;
++		goto out;
++        }
++
++        ZFCP_LOG_TRACE(
++                "Close Port request initiated "
++                "(adapter devno=0x%04x, port WWPN=0x%016Lx)\n",
++                erp_action->adapter->devno,
++                (llui_t)erp_action->port->wwpn);
++
++out:
++        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
++
++        ZFCP_LOG_TRACE("exit (%d)\n", retval);
++
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_close_port_handler
++ *
++ * purpose:     is called for finished Close Port FSF command
++ *
++ * returns:
++ */
++static int zfcp_fsf_close_port_handler(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++        int retval = -EINVAL;
++        zfcp_port_t *port;
++
++        ZFCP_LOG_TRACE(
++                "enter (fsf_req=0x%lx)\n",
++                (unsigned long)fsf_req);
++
++        port = fsf_req->data.close_port.port; 
++
++        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++                /* don't change port status in our bookkeeping */
++                goto skip_fsfstatus;
++        }
++
++        /* evaluate FSF status in QTCB */
++        switch (fsf_req->qtcb->header.fsf_status) {
++
++		case FSF_PORT_HANDLE_NOT_VALID :
++			ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
++			ZFCP_LOG_INFO(
++				"Temporary port identifier (handle) 0x%x "
++				"for the port with WWPN 0x%016Lx connected to "
++				"the adapter of devno 0x%04x is "
++				"not valid. This may happen occasionally.\n",
++				port->handle,
++				(llui_t)port->wwpn,
++				port->adapter->devno);
++			ZFCP_LOG_DEBUG("status qualifier:\n");
++			ZFCP_HEX_DUMP(
++				ZFCP_LOG_LEVEL_DEBUG,
++				(char*)&fsf_req->qtcb->header.fsf_status_qual,
++				16);
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_phand_nv");
++			zfcp_erp_adapter_reopen(port->adapter, 0);
++			fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++			break;
++
++		case FSF_ADAPTER_STATUS_AVAILABLE :
++			ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
++                        /* Note: FSF has actually closed the port in this case.
++                         * The status code is just daft. Fingers crossed for a change
++                         */
++                        retval=0;
++                        break;
++#if 0
++			switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
++                        case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
++                                ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
++                                /* This will now be escalated by ERP */
++                                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
++                                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                                break;
++                        case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
++                                ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
++                                /* ERP strategy will escalate */
++                                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
++                                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                                break;
++                        default:
++                                ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
++						fsf_req->qtcb->header.fsf_status_qual.word[0]);
++                                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
++                                debug_exception(fsf_req->adapter->erp_dbf,0,
++                                                &fsf_req->qtcb->header.fsf_status_qual.word[0],
++                                                sizeof(u32));
++                                break;
++			}
++			break;
++#endif
++
++		case FSF_GOOD :
++			ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
++			ZFCP_LOG_TRACE(
++				"remote port (WWPN=0x%016Lx) via adapter "
++				"(devno=0x%04x) closed, "
++				"port handle 0x%x\n",
++				(llui_t)port->wwpn,
++				port->adapter->devno,
++				port->handle);
++			zfcp_erp_modify_port_status(
++				port,
++				ZFCP_STATUS_COMMON_OPEN,
++				ZFCP_CLEAR);
++			retval = 0;
++			break;
++
++		default :
++			ZFCP_LOG_NORMAL(
++				"bug: An unknown FSF Status was presented "
++				"(debug info 0x%x)\n",
++				fsf_req->qtcb->header.fsf_status);
++                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
++                        debug_exception(fsf_req->adapter->erp_dbf,0,
++                                        &fsf_req->qtcb->header.fsf_status,
++                                        sizeof(u32));
++                        break;
++	}
++
++skip_fsfstatus:
++        atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &port->status);
++
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_close_physical_port
++ *
++ * purpose:     submit FSF command "close physical port"
++ *
++ * returns:     address of initiated FSF request
++ *              NULL - request could not be initiated
++ */
++static int zfcp_fsf_close_physical_port(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	volatile qdio_buffer_element_t *sbale;
++        int retval = 0;
++	unsigned long lock_flags;
++
++        ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
++
++        /* setup new FSF request */
++        retval = zfcp_fsf_req_create(
++			erp_action->adapter,
++			FSF_QTCB_CLOSE_PHYSICAL_PORT,
++			ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
++                        &erp_action->adapter->pool.fsf_req_erp,
++			&lock_flags,
++			&erp_action->fsf_req);
++        if (retval < 0) {
++                ZFCP_LOG_INFO(
++			"error: Out of resources. Could not create a "
++                        "close physical port request for "
++                        "the port with WWPN 0x%016Lx connected to "
++                        "the adapter with devno 0x%04x.\n",
++                        (llui_t)erp_action->port->wwpn,
++                        erp_action->adapter->devno);
++                goto out;
++        }
++
++	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
++                                    erp_action->fsf_req->sbal_curr, 0);
++        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
++        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
++
++        /* mark port as being closed */
++        atomic_set_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, &erp_action->port->status);
++        /* save a pointer to this port */
++        erp_action->fsf_req->data.close_physical_port.port = erp_action->port;
++        /* port to be closeed */
++        erp_action->fsf_req->qtcb->header.port_handle = erp_action->port->handle;
++	erp_action->fsf_req->erp_action = erp_action;
++
++        /* start QDIO request for this FSF request */
++        retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
++        if (retval) {
++		ZFCP_LOG_INFO(
++			"error: Could not send an "
++			"close physical port request for "
++			"the port with WWPN 0x%016Lx connected to "
++			"the adapter with devno 0x%04x.\n",
++			(llui_t)erp_action->port->wwpn,
++			erp_action->adapter->devno);
++		if (zfcp_fsf_req_free(erp_action->fsf_req)){
++			ZFCP_LOG_NORMAL(
++				"bug: Could not remove one FSF "
++				"request. Memory leakage possible. "
++				"(debug info 0x%lx).\n",
++				(unsigned long)erp_action->fsf_req);
++                        retval=-EINVAL;
++		};
++		erp_action->fsf_req = NULL;
++		goto out;
++        }
++
++        ZFCP_LOG_TRACE(
++                "Close Physical Port request initiated "
++                "(adapter devno=0x%04x, port WWPN=0x%016Lx)\n",
++                erp_action->adapter->devno,
++                (llui_t)erp_action->port->wwpn);
++
++out:
++        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
++
++        ZFCP_LOG_TRACE("exit (%d)\n", retval);
++
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_close_physical_port_handler
++ *
++ * purpose:     is called for finished Close Physical Port FSF command
++ *
++ * returns:
++ */
++static int zfcp_fsf_close_physical_port_handler(zfcp_fsf_req_t *fsf_req){
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
++
++        int retval = -EINVAL;
++        zfcp_port_t *port;
++        zfcp_unit_t *unit;
++        unsigned long flags;
++	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
++	u16 subtable, rule, counter;
++
++        ZFCP_LOG_TRACE(
++                "enter (fsf_req=0x%lx)\n",
++                (unsigned long)fsf_req);
++
++        port = fsf_req->data.close_physical_port.port;
++
++        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++                /* don't change port status in our bookkeeping */
++                goto skip_fsfstatus;
++        }
++
++        /* evaluate FSF status in QTCB */
++        switch (fsf_req->qtcb->header.fsf_status) {
++
++		case FSF_PORT_HANDLE_NOT_VALID :
++			ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
++			ZFCP_LOG_INFO(
++				"Temporary port identifier (handle) 0x%x "
++				"for the port with WWPN 0x%016Lx connected to "
++				"the adapter of devno 0x%04x is "
++				"not valid. This may happen occasionally.\n",
++				port->handle,
++				(llui_t)port->wwpn,
++				port->adapter->devno);
++			ZFCP_LOG_DEBUG("status qualifier:\n");
++			ZFCP_HEX_DUMP(
++				ZFCP_LOG_LEVEL_DEBUG,
++				(char*)&fsf_req->qtcb->header.fsf_status_qual,
++				16);
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_phand_nv");
++                        zfcp_erp_adapter_reopen(port->adapter, 0);
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                        //                        panic("for ralph");
++			break;
++
++	case FSF_ACCESS_DENIED :
++		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
++		ZFCP_LOG_NORMAL("Access denied, cannot close physical port "
++				"(devno=0x%04x wwpn=0x%016Lx)\n",
++				port->adapter->devno,
++				(llui_t)port->wwpn);
++		for (counter = 0; counter < 2; counter++) {
++			subtable = header->fsf_status_qual.halfword[counter * 2];
++			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
++			switch (subtable) {
++			case FSF_SQ_CFDC_SUBTABLE_OS:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
++			case FSF_SQ_CFDC_SUBTABLE_LUN:
++				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
++					zfcp_act_subtable_type[subtable], rule);
++				break;
++			}
++		}
++		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++        case FSF_PORT_BOXED :
++                ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
++                ZFCP_LOG_DEBUG("The remote port "
++                               "with WWPN 0x%016Lx on the adapter with "
++                               "devno 0x%04x needs to be reopened but "
++                               "it was attempted to close it physically.\n",
++                               (llui_t)port->wwpn,
++                               port->adapter->devno);
++                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_pboxed");
++                zfcp_erp_port_reopen(port, 0);
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
++                        | ZFCP_STATUS_FSFREQ_RETRY;
++                break;
++
++
++		case FSF_ADAPTER_STATUS_AVAILABLE :
++			ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
++			switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
++                        case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
++                                ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
++                                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
++                                /* This will now be escalated by ERP */
++                                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                                break;
++                        case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
++                                ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
++                                /* ERP strategy will escalate */
++                                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
++                                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                                break;
++                        default:
++                                ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
++						fsf_req->qtcb->header.fsf_status_qual.word[0]);
++                                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
++                                debug_exception(fsf_req->adapter->erp_dbf,0,
++                                                &fsf_req->qtcb->header.fsf_status_qual.word[0],
++                                                sizeof(u32));
++                                break;
++			}
++			break;
++	
++		case FSF_GOOD :
++			ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
++			ZFCP_LOG_DEBUG(
++				"Remote port (WWPN=0x%016Lx) via adapter "
++				"(devno=0x%04x) physically closed, "
++				"port handle 0x%x\n",
++				(llui_t)port->wwpn,
++				port->adapter->devno,
++				port->handle);
++                        /* can't use generic zfcp_erp_modify_port_status because
++                         * ZFCP_STATUS_COMMON_OPEN must not be reset for the port
++                         */
++			atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, 
++                                          &port->status);
++                        read_lock_irqsave(&port->unit_list_lock, flags);
++                        ZFCP_FOR_EACH_UNIT(port, unit) {
++                                atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, 
++                                                  &unit->status);
++                        }
++                        read_unlock_irqrestore(&port->unit_list_lock, flags);
++			retval = 0;
++			break;
++
++		default :
++			ZFCP_LOG_NORMAL(
++				"bug: An unknown FSF Status was presented "
++				"(debug info 0x%x)\n",
++				fsf_req->qtcb->header.fsf_status);
++                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
++                        debug_exception(fsf_req->adapter->erp_dbf,0,
++                                        &fsf_req->qtcb->header.fsf_status,
++                                        sizeof(u32));
++                        break;
++        }
++
++skip_fsfstatus:
++        atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_CLOSING, &port->status);
++        
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_open_unit
++ *
++ * purpose:
++ *
++ * returns:
++ *
++ * assumptions:	This routine does not check whether the associated
++ *		remote port has already been opened. This should be
++ *		done by calling routines. Otherwise some status
++ *		may be presented by FSF
++ */
++static int zfcp_fsf_open_unit(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	volatile qdio_buffer_element_t *sbale;
++	int retval = 0;
++	unsigned long lock_flags;
++
++	ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
++
++	/* setup new FSF request */
++	retval = zfcp_fsf_req_create(
++			erp_action->adapter,
++			FSF_QTCB_OPEN_LUN,
++			ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
++                        &erp_action->adapter->pool.fsf_req_erp,
++			&lock_flags,
++			&erp_action->fsf_req);
++	if (retval < 0) {
++		ZFCP_LOG_INFO(
++			"error: Out of resources. Could not create an "
++			"open unit request for FCP_LUN 0x%016Lx connected to "
++			"the port with WWPN 0x%016Lx connected to "
++			"the adapter with devno 0x%04x.\n",
++			(llui_t)erp_action->unit->fcp_lun,
++			(llui_t)erp_action->unit->port->wwpn,
++			erp_action->adapter->devno);
++		goto out;
++	}
++
++	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
++                                    erp_action->fsf_req->sbal_curr, 0);
++        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
++        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
++
++	erp_action->fsf_req->qtcb->header.port_handle =
++                erp_action->port->handle;
++	erp_action->fsf_req->qtcb->bottom.support.fcp_lun =
++                erp_action->unit->fcp_lun;
++	atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->unit->status);
++	erp_action->fsf_req->data.open_unit.unit = erp_action->unit;
++	erp_action->fsf_req->erp_action = erp_action;
++
++	/* start QDIO request for this FSF request */
++	retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
++	if (retval) {
++		ZFCP_LOG_INFO(
++			"error: Could not send an open unit request "
++			"on the adapter with devno 0x%04x, "
++			"port WWPN 0x%016Lx for unit FCP_LUN 0x%016Lx\n",
++			erp_action->adapter->devno,
++			(llui_t)erp_action->port->wwpn,
++			(llui_t)erp_action->unit->fcp_lun);
++		if (zfcp_fsf_req_free(erp_action->fsf_req)) {
++			ZFCP_LOG_NORMAL(
++				"bug: Could not remove one FSF "
++				"request. Memory leakage possible. "
++				"(debug info 0x%lx).\n",
++				(unsigned long)erp_action->fsf_req);
++                        retval=-EINVAL;
++		};
++		erp_action->fsf_req = NULL;
++		goto out;
++	}
++
++	ZFCP_LOG_TRACE(
++		"Open LUN request initiated "
++		"(adapter devno=0x%04x, port WWPN=0x%016Lx, unit FCP_LUN=0x%016Lx)\n",
++		erp_action->adapter->devno,
++		(llui_t)erp_action->port->wwpn,
++		(llui_t)erp_action->unit->fcp_lun);
++
++out:
++        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
++
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++
++/*
++ * function:    zfcp_fsf_open_unit_handler
++ *
++ * purpose:	is called for finished Open LUN command
++ *
++ * returns:	
++ */
++static int zfcp_fsf_open_unit_handler(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = -EINVAL;
++	zfcp_adapter_t *adapter;
++	zfcp_unit_t *unit;
++	fsf_qtcb_header_t *header;
++	fsf_queue_designator_t *queue_designator;
++	u16 subtable, rule, counter;
++ 
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++	adapter = fsf_req->adapter;
++	unit = fsf_req->data.open_unit.unit;
++	header = &fsf_req->qtcb->header;
++	queue_designator = &header->fsf_status_qual.fsf_queue_designator;
++
++        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		/* don't change unit status in our bookkeeping */ 
++                goto skip_fsfstatus;
++        }
++
++	/* evaluate FSF status in QTCB */
++	switch (fsf_req->qtcb->header.fsf_status) {
++
++        case FSF_PORT_HANDLE_NOT_VALID :
++                ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
++                ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
++                              "for the port with WWPN 0x%016Lx connected to "
++                              "the adapter of devno 0x%04x is "
++                              "not valid. This may happen occasionally.\n",
++                              unit->port->handle,
++                              (llui_t)unit->port->wwpn,
++                              unit->port->adapter->devno);
++                ZFCP_LOG_DEBUG("status qualifier:\n");
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
++                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
++                              16);
++                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_ph_nv");
++                zfcp_erp_adapter_reopen(unit->port->adapter, 0);
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++                
++        case FSF_LUN_ALREADY_OPEN :
++                ZFCP_LOG_FLAGS(0, "FSF_LUN_ALREADY_OPEN\n");
++                ZFCP_LOG_NORMAL("bug: Attempted to open the logical unit "
++				"with FCP_LUN 0x%016Lx at "
++				"the remote port with WWPN 0x%016Lx connected "
++				"to the adapter with devno 0x%04x twice.\n", 
++				(llui_t)unit->fcp_lun,
++				(llui_t)unit->port->wwpn,
++				unit->port->adapter->devno);
++                debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_s_uopen");
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++
++	case FSF_ACCESS_DENIED :
++		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
++		ZFCP_LOG_NORMAL("Access denied, cannot open unit 0x%016Lx "
++				"on the remote port 0x%016Lx "
++				"on adapter with devno 0x%04x\n",
++				(llui_t)unit->fcp_lun,
++				(llui_t)unit->port->wwpn,
++				adapter->devno);
++		for (counter = 0; counter < 2; counter++) {
++			subtable = header->fsf_status_qual.halfword[counter * 2];
++			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
++			switch (subtable) {
++			case FSF_SQ_CFDC_SUBTABLE_OS:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
++			case FSF_SQ_CFDC_SUBTABLE_LUN:
++				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
++					zfcp_act_subtable_type[subtable], rule);
++				break;
++			}
++		}
++		debug_text_event(adapter->erp_dbf, 1, "fsf_s_access");
++		zfcp_erp_unit_failed(unit);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++        case FSF_PORT_BOXED :
++                ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
++                ZFCP_LOG_DEBUG("The remote port "
++                               "with WWPN 0x%016Lx on the adapter with "
++                               "devno 0x%04x needs to be reopened\n",
++                               (llui_t)unit->port->wwpn,
++                               unit->port->adapter->devno);
++                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_s_pboxed");
++                zfcp_erp_port_reopen(unit->port, 0);
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
++                        | ZFCP_STATUS_FSFREQ_RETRY;
++                break;
++                        
++	case FSF_LUN_SHARING_VIOLATION :
++		ZFCP_LOG_FLAGS(2, "FSF_LUN_SHARING_VIOLATION\n");
++		if (header->fsf_status_qual.word[0] != 0) {
++			ZFCP_LOG_NORMAL("FCP-LUN 0x%Lx at the remote port with "
++					"WWPN 0x%Lx connected to the adapter "
++					"with devno 0x%04x is already in use "
++					"in LPAR%d, CSS%d\n",
++					(llui_t)unit->fcp_lun,
++					(llui_t)unit->port->wwpn,
++					adapter->devno,
++					queue_designator->hla,
++					queue_designator->cssid);
++		} else {
++			subtable = header->fsf_status_qual.halfword[4];
++			rule = header->fsf_status_qual.halfword[5];
++			switch (subtable) {
++			case FSF_SQ_CFDC_SUBTABLE_OS:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
++			case FSF_SQ_CFDC_SUBTABLE_LUN:
++				ZFCP_LOG_NORMAL("Access to FCP-LUN 0x%Lx at the "
++						"remote port with WWPN 0x%Lx "
++						"connected to the adapter "
++						"with devno 0x%04x "
++						"is denied (%s rule %d)\n",
++						(llui_t)unit->fcp_lun,
++						(llui_t)unit->port->wwpn,
++						adapter->devno,
++						zfcp_act_subtable_type[subtable],
++						rule);
++				break;
++			}
++		}
++		ZFCP_LOG_DEBUG("Additional sense data is presented:\n");
++		ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
++			(char*)&header->fsf_status_qual,
++			sizeof(fsf_status_qual_t));
++		debug_text_event(adapter->erp_dbf,2,"fsf_s_l_sh_vio");
++		zfcp_erp_unit_failed(unit);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++        case FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED :
++                ZFCP_LOG_FLAGS(1, "FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED\n");
++                ZFCP_LOG_INFO("error: The adapter ran out of resources. "
++                              "There is no handle (temporary port identifier) "
++                              "available for the unit with "
++                              "FCP_LUN 0x%016Lx at the remote port with WWPN 0x%016Lx "
++                              "connected to the adapter with devno 0x%04x\n",
++                              (llui_t)unit->fcp_lun,
++                              (llui_t)unit->port->wwpn,
++                              unit->port->adapter->devno);
++                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_max_units");
++                zfcp_erp_unit_failed(unit);
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++                
++        case FSF_ADAPTER_STATUS_AVAILABLE :
++                ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
++                switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
++                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
++                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
++                        /* Re-establish link to port */
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
++                        zfcp_erp_port_reopen(unit->port, 0);
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;
++                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
++                        ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
++                        /* ERP strategy will escalate */
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;
++                default:
++                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
++					fsf_req->qtcb->header.fsf_status_qual.word[0]);
++                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
++                        debug_exception(fsf_req->adapter->erp_dbf,0,
++                                        &fsf_req->qtcb->header.fsf_status_qual.word[0],
++                                        sizeof(u32));
++                }
++                break;
++
++        case FSF_GOOD :
++                ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
++                /* save LUN handle assigned by FSF */
++                unit->handle = fsf_req->qtcb->header.lun_handle;
++			ZFCP_LOG_TRACE("unit (FCP_LUN=0x%016Lx) of remote port "
++                                       "(WWPN=0x%016Lx) via adapter (devno=0x%04x) opened, "
++                                       "port handle 0x%x \n",
++                                       (llui_t)unit->fcp_lun,
++                                       (llui_t)unit->port->wwpn,
++                                       unit->port->adapter->devno,
++                                       unit->handle);
++			/* mark unit as open */
++			atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
++			retval = 0;
++			break;
++                        
++        default :
++                ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
++				"(debug info 0x%x)\n",
++				fsf_req->qtcb->header.fsf_status);
++                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
++                debug_exception(fsf_req->adapter->erp_dbf,0,
++                                &fsf_req->qtcb->header.fsf_status,
++                                sizeof(u32));
++                break;
++	}
++        
++skip_fsfstatus:
++	atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &unit->status); 
++        
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++	return retval;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_close_unit
++ *
++ * purpose:
++ *
++ * returns:	address of fsf_req - request successfully initiated
++ *		NULL - 
++ *
++ * assumptions: This routine does not check whether the associated
++ *              remote port/lun has already been opened. This should be
++ *              done by calling routines. Otherwise some status
++ *              may be presented by FSF
++ */
++static int zfcp_fsf_close_unit(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	volatile qdio_buffer_element_t *sbale;
++        int retval = 0;
++	unsigned long lock_flags;
++
++        ZFCP_LOG_TRACE("enter (erp_action=0x%lx)\n", (unsigned long)erp_action);
++
++        /* setup new FSF request */
++        retval = zfcp_fsf_req_create(
++			erp_action->adapter,
++			FSF_QTCB_CLOSE_LUN,
++			ZFCP_WAIT_FOR_SBAL | ZFCP_REQ_AUTO_CLEANUP,
++                        &erp_action->adapter->pool.fsf_req_erp,
++			&lock_flags,
++			&erp_action->fsf_req);
++	if (retval < 0) {
++		ZFCP_LOG_INFO(
++			"error: Out of resources. Could not create a "
++			"close unit request for FCP_LUN 0x%016Lx connected to "
++			"the port with WWPN 0x%016Lx connected to "
++			"the adapter with devno 0x%04x.\n",
++			(llui_t)erp_action->unit->fcp_lun,
++			(llui_t)erp_action->port->wwpn,
++			erp_action->adapter->devno);
++		goto out;
++        }
++
++	sbale = zfcp_qdio_sbale_req(erp_action->fsf_req,
++                                    erp_action->fsf_req->sbal_curr, 0);
++        sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
++        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
++
++        erp_action->fsf_req->qtcb->header.port_handle = erp_action->port->handle;
++	erp_action->fsf_req->qtcb->header.lun_handle = erp_action->unit->handle;
++        atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->unit->status);
++        erp_action->fsf_req->data.close_unit.unit = erp_action->unit;
++	erp_action->fsf_req->erp_action = erp_action;
++
++        /* start QDIO request for this FSF request */
++        retval = zfcp_fsf_req_send(erp_action->fsf_req, &erp_action->timer);
++	if (retval) {
++		ZFCP_LOG_INFO(
++			"error: Could not send a "
++			"close unit request for FCP_LUN 0x%016Lx connected to "
++			"the port with WWPN 0x%016Lx connected to "
++			"the adapter with devno 0x%04x.\n",
++			(llui_t)erp_action->unit->fcp_lun,
++			(llui_t)erp_action->port->wwpn,
++			erp_action->adapter->devno);
++		if (zfcp_fsf_req_free(erp_action->fsf_req)){
++			ZFCP_LOG_NORMAL(
++				"bug: Could not remove one FSF "
++				"request. Memory leakage possible. "
++				"(debug info 0x%lx).\n",
++				(unsigned long)erp_action->fsf_req);
++                        retval = -EINVAL;
++		};
++		erp_action->fsf_req = NULL;
++		goto out;
++	}
++
++        ZFCP_LOG_TRACE(
++                "Close LUN request initiated "
++                "(adapter devno=0x%04x, port WWPN=0x%016Lx, unit FCP_LUN=0x%016Lx)\n",
++                erp_action->adapter->devno,
++                (llui_t)erp_action->port->wwpn,
++                (llui_t)erp_action->unit->fcp_lun);
++
++out:
++        write_unlock_irqrestore(&erp_action->adapter->request_queue.queue_lock, lock_flags);
++
++        ZFCP_LOG_TRACE("exit (%d)\n", retval);
++
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_close_unit_handler
++ *
++ * purpose:     is called for finished Close LUN FSF command
++ *
++ * returns:
++ */
++static int zfcp_fsf_close_unit_handler(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++        int retval = -EINVAL;
++        zfcp_unit_t *unit;
++
++        ZFCP_LOG_TRACE(
++                "enter (fsf_req=0x%lx)\n",
++                (unsigned long)fsf_req);
++
++        unit = fsf_req->data.close_unit.unit; /* restore unit */
++
++        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++                /* don't change unit status in our bookkeeping */
++                goto skip_fsfstatus;
++        }
++
++        /* evaluate FSF status in QTCB */
++        switch (fsf_req->qtcb->header.fsf_status) {
++
++                case FSF_PORT_HANDLE_NOT_VALID :
++			ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
++			ZFCP_LOG_INFO(
++				"Temporary port identifier (handle) 0x%x "
++				"for the port with WWPN 0x%016Lx connected to "
++                                "the adapter of devno 0x%04x is "
++				"not valid. This may happen in rare "
++                                "circumstances\n",
++				unit->port->handle,
++				(llui_t)unit->port->wwpn,
++                                unit->port->adapter->devno);
++			ZFCP_LOG_DEBUG("status qualifier:\n");
++                        ZFCP_HEX_DUMP(
++				ZFCP_LOG_LEVEL_DEBUG,
++				(char*)&fsf_req->qtcb->header.fsf_status_qual,
++				16);
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_phand_nv");
++                        zfcp_erp_adapter_reopen(unit->port->adapter, 0);
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                        break;
++
++                case FSF_LUN_HANDLE_NOT_VALID :
++			ZFCP_LOG_FLAGS(1, "FSF_LUN_HANDLE_NOT_VALID\n");
++			ZFCP_LOG_INFO(
++                                "Temporary LUN identifier (handle) 0x%x "
++				"of the logical unit with FCP_LUN 0x%016Lx at "
++				"the remote port with WWPN 0x%016Lx connected "
++                                "to the adapter with devno 0x%04x is " 
++				"not valid. This may happen occasionally.\n",
++				unit->handle,
++				(llui_t)unit->fcp_lun,
++				(llui_t)unit->port->wwpn,
++				unit->port->adapter->devno);
++			ZFCP_LOG_DEBUG("Status qualifier data:\n");
++                        ZFCP_HEX_DUMP(
++				ZFCP_LOG_LEVEL_DEBUG,
++				(char*)&fsf_req->qtcb->header.fsf_status_qual,
++				16);
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_lhand_nv");
++                        zfcp_erp_port_reopen(unit->port, 0);
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                        break;
++
++        case FSF_PORT_BOXED :
++                ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
++                ZFCP_LOG_DEBUG("The remote port "
++                               "with WWPN 0x%016Lx on the adapter with "
++                               "devno 0x%04x needs to be reopened\n",
++                               (llui_t)unit->port->wwpn,
++                               unit->port->adapter->devno);
++                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_s_pboxed");
++                zfcp_erp_port_reopen(unit->port, 0);
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
++                        | ZFCP_STATUS_FSFREQ_RETRY;
++                break;
++
++        case FSF_ADAPTER_STATUS_AVAILABLE :
++                ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
++                switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
++                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
++                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
++                        /* re-establish link to port */
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
++                        zfcp_erp_port_reopen(unit->port, 0);
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;
++                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
++                        ZFCP_LOG_FLAGS(2, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
++                        /* ERP strategy will escalate */
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;
++                default:
++                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
++                                        fsf_req->qtcb->header.fsf_status_qual.word[0]);
++                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
++                        debug_exception(fsf_req->adapter->erp_dbf,0,
++                                        &fsf_req->qtcb->header.fsf_status_qual.word[0],
++                                        sizeof(u32));
++                        break;
++                }
++                break;
++                
++        case FSF_GOOD :
++                ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
++                ZFCP_LOG_TRACE("unit (FCP_LUN=0x%016Lx) of remote port "
++                               "(WWPN=0x%016Lx) via adapter (devno=0x%04x) closed, "
++                               "port handle 0x%x \n",
++                                (llui_t)unit->fcp_lun,
++                               (llui_t)unit->port->wwpn,
++                                unit->port->adapter->devno,
++                               unit->handle);
++                /* mark unit as closed */
++                atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status);
++                retval = 0;
++                break;
++                
++        default :
++                ZFCP_LOG_NORMAL("bug: An unknown FSF Status was presented "
++                                "(debug info 0x%x)\n",
++                                fsf_req->qtcb->header.fsf_status);
++                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
++                debug_exception(fsf_req->adapter->erp_dbf,0,
++                                &fsf_req->qtcb->header.fsf_status,
++                                sizeof(u32));
++                break;
++        }
++
++skip_fsfstatus:
++
++        atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &unit->status);
++
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_control_file
++ *
++ * purpose:     Initiator of the control file upload/download FSF requests
++ *
++ * returns:     0           - FSF request is successfuly created and queued
++ *              -EOPNOTSUPP - The FCP adapter does not have Control File support
++ *              -EINVAL     - Invalid direction specified
++ *              -ENOMEM     - Insufficient memory
++ *              -EPERM      - Cannot create FSF request or or place it in QDIO queue
++ */
++static int zfcp_fsf_control_file(
++	zfcp_adapter_t *adapter,
++	zfcp_fsf_req_t **fsf_req_ptr,
++	u32 fsf_command,
++	u32 option,
++	zfcp_sg_list_t *sg_list)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	zfcp_fsf_req_t *fsf_req;
++	fsf_qtcb_bottom_support_t *bottom;
++	unsigned long lock_flags;
++	int req_flags = 0;
++	int direction;
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx fsf_req_ptr=0x%lx "
++		"fsf_command=0x%x option=0x%x sg_list=0x%lx)\n",
++		(unsigned long)adapter,
++		(unsigned long)fsf_req_ptr,
++		fsf_command,
++		option,
++		(unsigned long)sg_list);
++
++	if (!(adapter->supported_features & FSF_FEATURE_CFDC)) {
++		ZFCP_LOG_INFO(
++			"Adapter does not support control file "
++			"(devno=0x%04x)\n",
++			adapter->devno);
++		retval = -EOPNOTSUPP;
++		goto no_act_support;
++	}
++
++	switch (fsf_command) {
++
++	case FSF_QTCB_DOWNLOAD_CONTROL_FILE:
++		direction = SBAL_FLAGS0_TYPE_WRITE;
++		if ((option != FSF_CFDC_OPTION_FULL_ACCESS) &&
++		    (option != FSF_CFDC_OPTION_RESTRICTED_ACCESS))
++			req_flags |= ZFCP_WAIT_FOR_SBAL;
++		break;
++
++	case FSF_QTCB_UPLOAD_CONTROL_FILE:
++		direction = SBAL_FLAGS0_TYPE_READ;
++		break;
++
++	default:
++		ZFCP_LOG_INFO("Invalid FSF command code 0x%08x\n", fsf_command);
++		goto invalid_command;
++	}
++
++	retval = zfcp_fsf_req_create(
++		adapter, fsf_command, req_flags, NULL, &lock_flags, &fsf_req);
++	if (retval < 0) {
++		ZFCP_LOG_INFO(
++			"error: Could not create FSF request (devno=0x%04x)\n",
++			adapter->devno);
++		retval = -EPERM;
++		goto out;
++	}
++
++	bottom = &fsf_req->qtcb->bottom.support;
++	bottom->op_subtype = FSF_CFDC_OPERATION_SUBTYPE;
++	bottom->option = option;
++
++	if (sg_list->count > 0) {
++		int bytes = zfcp_qdio_sbals_from_sg(
++			fsf_req, direction, sg_list->sg,
++			ZFCP_MAX_SBALES_PER_CONTROL_FILE,
++			ZFCP_MAX_SBALS_PER_REQ);
++		
++		if (bytes != ZFCP_CFDC_MAX_CONTROL_FILE_SIZE) {
++			ZFCP_LOG_INFO(
++				"error: Could not create sufficient number "
++				"of SBALS (devno=0x%04x)\n",
++				adapter->devno);
++			retval = -ENOMEM;
++			goto sbals_failed;
++		}
++	} else {
++		volatile qdio_buffer_element_t *sbale =
++			zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
++		sbale[0].flags |= direction;
++		sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
++	}
++
++	retval = zfcp_fsf_req_send(fsf_req, NULL);
++	if (retval < 0) {
++		ZFCP_LOG_INFO(
++			"error: Could not send FSF request (devno=0x%04x)\n",
++			adapter->devno);
++		retval = -EPERM;
++		goto queue_failed;
++	}
++
++	ZFCP_LOG_NORMAL(
++		"Control file %s FSF request initiated (devno=0x%04x)\n",
++		fsf_command == FSF_QTCB_DOWNLOAD_CONTROL_FILE ?
++			"download" : "upload",
++		adapter->devno);
++
++	*fsf_req_ptr = fsf_req;
++
++	goto out;
++
++sbals_failed:
++queue_failed:
++	if (zfcp_fsf_req_free(fsf_req)) {
++		ZFCP_LOG_INFO(
++			"bug: Could not remove the FSF request "
++			"(devno=0x%04x fsf_req=0x%lx)\n",
++			adapter->devno,
++			(unsigned long)fsf_req);
++	}
++
++out:
++	write_unlock_irqrestore(
++		&adapter->request_queue.queue_lock, lock_flags);
++
++no_act_support:
++invalid_command:
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_control_file_handler
++ *
++ * purpose:     Handler of the control file upload/download FSF requests
++ *
++ * returns:     0       - FSF request successfuly processed
++ *              -EAGAIN - Operation has to be repeated because of a temporary problem
++ *              -EACCES - There is no permission to execute an operation
++ *              -EPERM  - The control file is not in a right format
++ *              -EIO    - There is a problem with the FCP adapter
++ *              -EINVAL - Invalid operation
++ *              -EFAULT - User space memory I/O operation fault
++ */
++static int zfcp_fsf_control_file_handler(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	zfcp_adapter_t *adapter = fsf_req->adapter;
++	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
++	fsf_qtcb_bottom_support_t *bottom = &fsf_req->qtcb->bottom.support;
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter (fsf_req=0x%lx)\n", (unsigned long)fsf_req);
++
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		retval = -EINVAL;
++		goto skip_fsfstatus;
++	}
++
++	switch (header->fsf_status) {
++
++	case FSF_GOOD:
++		ZFCP_LOG_FLAGS(2, "FSF_GOOD\n");
++		ZFCP_LOG_NORMAL(
++			"The FSF request has been successfully completed "
++			"(devno=0x%04x fsf_req.seq_no=%d)\n",
++			adapter->devno,
++			fsf_req->seq_no);
++		break;
++
++	case FSF_OPERATION_PARTIALLY_SUCCESSFUL:
++		ZFCP_LOG_FLAGS(2, "FSF_OPERATION_PARTIALLY_SUCCESSFUL\n");
++		if (bottom->op_subtype == FSF_CFDC_OPERATION_SUBTYPE) {
++			switch (header->fsf_status_qual.word[0]) {
++
++			case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE:
++				ZFCP_LOG_NORMAL(
++					"CDFC could not be saved "
++					"on the SE (devno=0x%04x)\n",
++					adapter->devno);
++				break;
++
++			case FSF_SQ_CFDC_COULD_NOT_HARDEN_ON_SE2:
++				ZFCP_LOG_NORMAL(
++					"CDFC could not be copied "
++					"to the secondary SE (devno=0x%04x)\n",
++					adapter->devno);
++				break;
++
++			default:
++				ZFCP_LOG_NORMAL(
++					"CDFC could not be hardened "
++					"on the FCP adapter (devno=0x%04x)\n",
++					adapter->devno);
++			}
++		}
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EAGAIN;
++		break;
++
++	case FSF_AUTHORIZATION_FAILURE:
++		ZFCP_LOG_FLAGS(2, "FSF_AUTHORIZATION_FAILURE\n");
++		ZFCP_LOG_NORMAL(
++			"Subchannel does not accept privileged commands "
++			"(devno=0x%04x)\n",
++			adapter->devno);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EACCES;
++		break;
++
++	case FSF_CFDC_ERROR_DETECTED:
++		ZFCP_LOG_FLAGS(2, "FSF_CFDC_ERROR_DETECTED\n");
++		ZFCP_LOG_NORMAL(
++			"Error at position %d in the CDFC, "
++			"CDFC is discarded by the FCP adapter (devno=0x%04x)\n",
++			header->fsf_status_qual.word[0],
++			adapter->devno);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EPERM;
++		break;
++
++	case FSF_CONTROL_FILE_UPDATE_ERROR:
++		ZFCP_LOG_FLAGS(2, "FSF_CONTROL_FILE_UPDATE_ERROR\n");
++		ZFCP_LOG_NORMAL(
++			"FCP adapter cannot harden the control file, "
++			"file is discarded (devno=0x%04x)\n",
++			adapter->devno);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EIO;
++		break;
++
++	case FSF_CONTROL_FILE_TOO_LARGE:
++		ZFCP_LOG_FLAGS(2, "FSF_CONTROL_FILE_TOO_LARGE\n");
++		ZFCP_LOG_NORMAL(
++			"Control file is too large, file is discarded "
++			"by the FCP adapter (devno=0x%04x)\n",
++			adapter->devno);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EIO;
++		break;
++
++	case FSF_ACCESS_CONFLICT_DETECTED:
++		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_CONFLICT_DETECTED\n");
++		if (bottom->op_subtype == FSF_CFDC_OPERATION_SUBTYPE)
++			ZFCP_LOG_NORMAL(
++				"CDFC has been discarded, because activation "
++				"would impact %d active connection(s) "
++				"(devno=0x%04x)\n",
++				header->fsf_status_qual.word[0],
++				adapter->devno);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EIO;
++		break;
++
++	case FSF_CONFLICTS_OVERRULED:
++		ZFCP_LOG_FLAGS(2, "FSF_CONFLICTS_OVERRULED\n");
++		if (bottom->op_subtype == FSF_CFDC_OPERATION_SUBTYPE)
++			ZFCP_LOG_NORMAL(
++				"CDFC has been activated, but activation "
++				"has impacted %d active connection(s) "
++				"(devno=0x%04x)\n",
++				header->fsf_status_qual.word[0],
++				adapter->devno);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EIO;
++		break;
++
++	case FSF_UNKNOWN_COMMAND:
++		ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_COMMAND\n");
++		ZFCP_LOG_NORMAL(
++			"FSF command 0x%x is not supported by FCP adapter "
++			"(devno=0x%04x)\n",
++			fsf_req->fsf_command,
++			adapter->devno);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EINVAL;
++		break;
++
++	case FSF_UNKNOWN_OP_SUBTYPE:
++		ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_OP_SUBTYPE\n");
++		ZFCP_LOG_NORMAL(
++			"Invalid operation subtype 0x%x has been specified "
++			"in QTCB bottom (devno=0x%04x)\n",
++			bottom->op_subtype,
++			adapter->devno);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EINVAL;
++		break;
++
++	case FSF_INVALID_COMMAND_OPTION:
++		ZFCP_LOG_FLAGS(2, "FSF_INVALID_COMMAND_OPTION\n");
++		ZFCP_LOG_NORMAL(
++			"Invalid option 0x%x has been specified in QTCB bottom "
++			"(devno=0x%04x)\n",
++			bottom->option,
++			adapter->devno);
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EINVAL;
++		break;
++
++	default:
++		ZFCP_LOG_NORMAL(
++			"bug: An unknown FSF Status was presented "
++			"(devno=0x%04x fsf_status=0x%08x)\n",
++			adapter->devno,
++			header->fsf_status);
++		debug_text_event(fsf_req->adapter->erp_dbf, 0, "fsf_sq_inval");
++		debug_exception(fsf_req->adapter->erp_dbf, 0,
++			&header->fsf_status_qual.word[0], sizeof(u32));
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		retval = -EINVAL;
++		break;
++	}
++
++skip_fsfstatus:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++#ifdef ZFCP_RESID
++/*
++ * function:    zfcp_scsi_truncte_command
++ *
++ * purpose:
++ *
++ * returns:
++ */
++inline int zfcp_scsi_truncate_command(unsigned char *command_struct,
++                                      unsigned long original_byte_length,
++                                      unsigned long new_byte_length)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_SCSI
++
++        int retval=0;
++        unsigned long factor, new_block_size;
++        u8 *len_6;
++        u16 *len_10;
++        u32 *len_12;
++        /* trace */
++        ZFCP_LOG_NORMAL(
++                 "enter command_struct = 0x%lx, "
++                 "original_byte_length = %ld "
++                 "new_byte_length = %ld\n",
++                 (unsigned long)command_struct,
++                 original_byte_length,
++                 new_byte_length);
++        
++        /*trace*/
++        ZFCP_LOG_NORMAL("original SCSI command:\n");
++        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
++                      command_struct,
++                      12);
++
++        switch(command_struct[0]) {
++        case WRITE_6:
++        case READ_6:
++                len_6 = &command_struct[4];
++                factor = (unsigned long)(original_byte_length
++                                       / *len_6);
++                new_block_size = new_byte_length / factor;
++                if(new_byte_length % factor) {
++                        ZFCP_LOG_NORMAL("bug: Recalculation of command size "
++                                        "failed. "
++                                        "(debug info %d, %ld, %d, %ld, %ld)\n",
++                                        6,
++                                        original_byte_length,
++                                        *len_6,
++                                        new_byte_length,
++                                        factor);
++                        goto error;
++                }
++                /* trace */
++                ZFCP_LOG_NORMAL("*len_6=%d, factor= %ld, new_byte_length= %ld\n",
++                               *len_6, factor, new_byte_length);
++                *len_6=(u8)new_block_size;
++                /* trace */
++                ZFCP_LOG_NORMAL("new *len_6=%d\n",
++                                *len_6);
++                break;
++        case WRITE_10:
++        case READ_10:
++        case WRITE_VERIFY:
++                len_10= (u16 *)&command_struct[7];
++                factor = (unsigned long)(original_byte_length
++                                       / *len_10);
++                new_block_size = new_byte_length / factor;
++                if(new_byte_length % factor) {
++                        ZFCP_LOG_NORMAL("bug: Recalculation of command size "
++                                        "failed. "
++                                        "(debug info %d, %ld, %d, %ld, %ld)\n",
++                                        10,
++                                        original_byte_length,
++                                        *len_10,
++                                        new_byte_length,
++                                        factor);
++                        goto error;
++                }
++                /* TRACE */
++                ZFCP_LOG_NORMAL("*len_10 = %d, factor = %ld, new_byte_length = %ld\n",
++                               *len_10, factor, new_byte_length);
++                *len_10=(u16)new_block_size;
++                /* trace */
++                ZFCP_LOG_NORMAL("new *len_10=%d\n",
++                                *len_10);
++                break;
++        case WRITE_12:
++        case READ_12:
++        case WRITE_VERIFY_12:
++                len_12= (u32 *)&command_struct[7];
++                factor = (unsigned long)(original_byte_length
++                                       / *len_12);
++                new_block_size = new_byte_length / factor;
++                if(new_byte_length % factor) {
++                        ZFCP_LOG_NORMAL("bug: Recalculation of command size "
++                                        "failed. "
++                                        "(debug info %d, %ld, %d, %ld, %ld)\n",
++                                        12,
++                                        original_byte_length,
++                                        *len_12,
++                                        new_byte_length,
++                                        factor);
++                        goto error;
++                }
++                /* TRACE */
++                ZFCP_LOG_NORMAL("*len_12 = %d, factor = %ld, new_byte_length = %ld\n",
++                               *len_12, factor, new_byte_length);
++                *len_12=(u32)new_block_size;
++                /* trace */
++                ZFCP_LOG_NORMAL("new *len_12=%d\n",
++                                *len_12);
++                break;
++        default:
++                /* INFO */
++                ZFCP_LOG_NORMAL("Command to be truncated is not in the list of "
++                              "known objects.\n");
++                goto error;
++                break;
++        }
++                goto out;
++                
++        error:
++                retval=1;
++        out:                
++        /*trace*/
++        ZFCP_LOG_NORMAL("truncated SCSI command:\n");
++        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
++                      command_struct,
++                      12);
++
++        /* TRACE */
++        ZFCP_LOG_NORMAL("exit (%i)\n", retval);
++        
++        return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++#endif // ZFCP_RESID
++
++/*
++ * function:    zfcp_fsf_send_fcp_command_task
++ *
++ * purpose:
++ *
++ * returns:
++ *
++ * note: we do not employ linked commands (not supported by HBA anyway)
++ */
++static int
++	zfcp_fsf_send_fcp_command_task(
++		zfcp_unit_t *unit,
++                Scsi_Cmnd *scsi_cmnd)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++  
++	zfcp_fsf_req_t *fsf_req = NULL;
++	fcp_cmnd_iu_t *fcp_cmnd_iu;
++	zfcp_adapter_t *adapter = unit->port->adapter;
++	unsigned int sbtype;
++	unsigned long lock_flags;
++	int real_bytes = 0;
++        int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter devno=0x%04x, unit=0x%lx)\n",
++		adapter->devno,
++		(unsigned long)unit);
++
++	/* setup new FSF request */
++	retval = zfcp_fsf_req_create(
++			adapter,
++			FSF_QTCB_FCP_CMND,
++			ZFCP_REQ_AUTO_CLEANUP,
++                        &adapter->pool.fsf_req_scsi,
++			&lock_flags,
++			&fsf_req);
++	if (retval < 0) {
++		ZFCP_LOG_DEBUG(
++			"error: Out of resources. Could not create an "
++			"FCP command request for FCP_LUN 0x%016Lx connected to "
++			"the port with WWPN 0x%016Lx connected to "
++			"the adapter with devno 0x%04x.\n",
++			(llui_t)unit->fcp_lun,
++			(llui_t)unit->port->wwpn,
++			adapter->devno);
++		goto failed_req_create;
++	}
++
++	/*
++	 * associate FSF request with SCSI request
++	 * (need this for look up on abort)
++	 */
++	scsi_cmnd->host_scribble = (char*) fsf_req;
++
++	/*
++	 * associate SCSI command with FSF request
++	 * (need this for look up on normal command completion)
++	 */
++	fsf_req->data.send_fcp_command_task.scsi_cmnd = scsi_cmnd;
++#ifdef ZFCP_DEBUG_REQUESTS
++	debug_text_event(adapter->req_dbf, 3, "fsf/sc");
++	debug_event(adapter->req_dbf, 3, &fsf_req, sizeof(unsigned long));
++	debug_event(adapter->req_dbf, 3, &scsi_cmnd, sizeof(unsigned long));
++#endif /* ZFCP_DEBUG_REQUESTS */
++#ifdef ZFCP_DEBUG_ABORTS
++	fsf_req->data.send_fcp_command_task.start_jiffies = jiffies;
++#endif
++
++	fsf_req->data.send_fcp_command_task.unit = unit;
++        ZFCP_LOG_DEBUG("unit=0x%lx, unit_fcp_lun=0x%Lx\n",
++                       (unsigned long)unit,
++                       (llui_t)unit->fcp_lun);
++
++	/* set handles of unit and its parent port in QTCB */
++	fsf_req->qtcb->header.lun_handle = unit->handle;
++	fsf_req->qtcb->header.port_handle = unit->port->handle;
++
++	/* FSF does not define the structure of the FCP_CMND IU */
++	fcp_cmnd_iu = (fcp_cmnd_iu_t*)
++			&(fsf_req->qtcb->bottom.io.fcp_cmnd);
++
++	/*
++	 * set depending on data direction:
++	 *	data direction bits in SBALE (SB Type)
++	 * 	data direction bits in QTCB
++	 *	data direction bits in FCP_CMND IU
++         */
++	switch (scsi_cmnd->sc_data_direction) {
++		case SCSI_DATA_NONE:
++                        ZFCP_LOG_FLAGS(3, "SCSI_DATA_NONE\n");
++			fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
++			/*
++			 * FIXME(qdio):
++			 * what is the correct type for commands
++			 * without 'real' data buffers?
++			 */
++			sbtype = SBAL_FLAGS0_TYPE_READ;
++			break;
++		case SCSI_DATA_READ:
++                        ZFCP_LOG_FLAGS(3, "SCSI_DATA_READ\n");
++			fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
++			sbtype = SBAL_FLAGS0_TYPE_READ;
++			fcp_cmnd_iu->rddata = 1;
++			break;
++		case SCSI_DATA_WRITE:
++                        ZFCP_LOG_FLAGS(3, "SCSI_DATA_WRITE\n");
++			fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
++			sbtype = SBAL_FLAGS0_TYPE_WRITE;
++			fcp_cmnd_iu->wddata = 1;
++			break;
++		case SCSI_DATA_UNKNOWN:
++			ZFCP_LOG_FLAGS(0, "SCSI_DATA_UNKNOWN not supported\n");
++		default:
++			/*
++			 * dummy, catch this condition earlier
++			 * in zfcp_scsi_queuecommand
++			 */
++			goto failed_scsi_cmnd;
++	}
++
++	/* set FC service class in QTCB (3 per default) */
++	fsf_req->qtcb->bottom.io.service_class = adapter->fc_service_class;
++
++	/* set FCP_LUN in FCP_CMND IU in QTCB */
++	fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
++
++	/* set task attributes in FCP_CMND IU in QTCB */
++	if ((scsi_cmnd->device && scsi_cmnd->device->tagged_queue) ||
++	    atomic_test_mask(ZFCP_STATUS_UNIT_ASSUMETCQ, &unit->status)) {
++		fcp_cmnd_iu->task_attribute = SIMPLE_Q;
++		ZFCP_LOG_TRACE("setting SIMPLE_Q task attribute\n");
++	} else	{
++		fcp_cmnd_iu->task_attribute = UNTAGGED;
++		ZFCP_LOG_TRACE("setting UNTAGGED task attribute\n");
++	}
++
++	/* set additional length of FCP_CDB in FCP_CMND IU in QTCB, if needed */
++	if (scsi_cmnd->cmd_len > FCP_CDB_LENGTH) {
++		fcp_cmnd_iu->add_fcp_cdb_length
++			= (scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2;
++		ZFCP_LOG_TRACE("SCSI CDB length is 0x%x, "
++				"additional FCP_CDB length is 0x%x "
++				"(shifted right 2 bits)\n",
++				scsi_cmnd->cmd_len,
++				fcp_cmnd_iu->add_fcp_cdb_length);
++	}
++	/*
++	 * copy SCSI CDB (including additional length, if any) to
++	 * FCP_CDB in FCP_CMND IU in QTCB
++	 */ 
++	memcpy( fcp_cmnd_iu->fcp_cdb,
++		scsi_cmnd->cmnd,
++		scsi_cmnd->cmd_len);
++
++	/* FCP CMND IU length in QTCB */
++	fsf_req->qtcb->bottom.io.fcp_cmnd_length 
++		= sizeof(fcp_cmnd_iu_t) +
++		  fcp_cmnd_iu->add_fcp_cdb_length +
++		  sizeof(fcp_dl_t);
++
++	/* generate SBALEs from data buffer */
++	real_bytes = zfcp_qdio_sbals_from_scsicmnd(
++			fsf_req,
++			sbtype,
++			scsi_cmnd);
++	if (real_bytes < 0) {
++		if (fsf_req->sbal_number < ZFCP_MAX_SBALS_PER_REQ) {
++			ZFCP_LOG_DEBUG(
++				"Data did not fit into available buffer(s), "
++				"waiting for more...\n");
++			retval = -EIO;
++        	} else	{
++                	ZFCP_LOG_NORMAL(
++				"error: Too large SCSI data buffer. "
++				"Shutting down unit "
++				"(devno=0x%04x, WWPN=0x%016Lx, FCP_LUN=0x%016Lx)\n",
++				unit->port->adapter->devno,
++				(llui_t)unit->port->wwpn,
++				(llui_t)unit->fcp_lun);
++			zfcp_erp_unit_shutdown(unit, 0);
++			retval = -EINVAL;
++		}
++		goto no_fit;
++        }
++
++        /* set length of FCP data length in FCP_CMND IU in QTCB */
++	zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes);
++
++        ZFCP_LOG_DEBUG("Sending SCSI command:\n");
++        ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
++                      (char *)scsi_cmnd->cmnd,
++                      scsi_cmnd->cmd_len);
++
++	/*
++	 * start QDIO request for this FSF request
++	 *  covered by an SBALE)
++	 */
++	{
++		int i, pos;
++		ZFCP_LOG_DEBUG(
++			"opcode=0x%x, sbal_first=%d, "
++			"sbal_curr=%d, sbal_last=%d, "
++			"sbal_number=%d, sbale_curr=%d\n",
++			scsi_cmnd->cmnd[0],
++			fsf_req->sbal_first,
++			fsf_req->sbal_curr,
++			fsf_req->sbal_last,
++			fsf_req->sbal_number,
++			fsf_req->sbale_curr);
++		for (i = 0; i < fsf_req->sbal_number; i++) {
++			pos = (fsf_req->sbal_first + i) % QDIO_MAX_BUFFERS_PER_Q;
++			ZFCP_HEX_DUMP(
++				ZFCP_LOG_LEVEL_DEBUG,
++				(char*)adapter->request_queue.buffer[pos],
++				sizeof(qdio_buffer_t));
++		}
++	}
++        retval = zfcp_fsf_req_send(fsf_req, NULL);
++	if (retval < 0) {
++		ZFCP_LOG_INFO(
++			"error: Could not send an FCP command request "
++			"for a command on the adapter with devno 0x%04x, "
++                        "port WWPN 0x%016Lx and unit FCP_LUN 0x%016Lx\n",
++			adapter->devno,
++			(llui_t)unit->port->wwpn,
++			(llui_t)unit->fcp_lun);
++                goto send_failed;
++	}
++
++        ZFCP_LOG_TRACE(
++		"Send FCP Command initiated "
++		"(adapter devno=0x%04x, port WWPN=0x%016Lx, unit FCP_LUN=0x%016Lx)\n",
++		adapter->devno,
++		(llui_t)unit->port->wwpn,
++		(llui_t)unit->fcp_lun);
++        goto success;
++
++send_failed:
++no_fit:
++failed_scsi_cmnd:
++	/* dequeue new FSF request previously enqueued */
++#ifdef ZFCP_DEBUG_REQUESTS
++	debug_text_event(adapter->req_dbf, 3, "fail_sc");
++	debug_event(adapter->req_dbf, 3, &scsi_cmnd, sizeof(unsigned long));
++#endif /* ZFCP_DEBUG_REQUESTS */
++         
++        if (zfcp_fsf_req_free(fsf_req)) {
++                ZFCP_LOG_INFO(
++			"error: Could not remove an FSF request from "
++			"the otubound (send) list (debug info 0x%lx)\n",
++			(unsigned long)fsf_req);
++        }
++        fsf_req = NULL;
++
++success:
++failed_req_create:
++        write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
++
++	ZFCP_LOG_TRACE("exit (%d)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_send_fcp_command_task_management
++ *
++ * purpose:
++ *
++ * returns:
++ *
++ * FIXME(design) shouldn't this be modified to return an int
++ *               also...don't know how though
++
++ */
++static zfcp_fsf_req_t*
++	zfcp_fsf_send_fcp_command_task_management(
++		zfcp_adapter_t *adapter,
++		zfcp_unit_t *unit,
++                u8 tm_flags,
++		int req_flags)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++  
++	zfcp_fsf_req_t *fsf_req = NULL;
++	int retval = 0;
++	fcp_cmnd_iu_t *fcp_cmnd_iu;
++	unsigned long lock_flags;
++
++	volatile qdio_buffer_element_t *sbale;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter devno=0x%04x, unit=0x%lx, tm_flags=0x%x, "
++		"req_flags=0x%x)\n",
++		adapter->devno,
++		(unsigned long)unit,
++		tm_flags,
++		req_flags);
++
++
++	/* setup new FSF request */
++	retval = zfcp_fsf_req_create(
++			adapter,
++			FSF_QTCB_FCP_CMND,
++			req_flags,
++                        &adapter->pool.fsf_req_scsi,
++			&lock_flags,
++			&fsf_req);
++	if (retval < 0) {
++                ZFCP_LOG_INFO("error: Out of resources. Could not create an "
++                              "FCP command (task management) request for "
++                              "the adapter with devno 0x%04x, port with "
++                              "WWPN 0x%016Lx and FCP_LUN 0x%016Lx.\n",
++                              adapter->devno,
++                              (llui_t)unit->port->wwpn,
++                              (llui_t)unit->fcp_lun);
++                goto out;
++	}
++
++        /* Used to decide on proper handler in the return path,
++         * could be either zfcp_fsf_send_fcp_command_task_handler or
++         * zfcp_fsf_send_fcp_command_task_management_handler */
++        fsf_req->status|=ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT;
++	/*
++	 * hold a pointer to the unit being target of this
++	 * task management request
++	 */
++	fsf_req->data.send_fcp_command_task_management.unit = unit;
++
++	/* set FSF related fields in QTCB */
++	fsf_req->qtcb->header.lun_handle = unit->handle;
++	fsf_req->qtcb->header.port_handle = unit->port->handle;
++	fsf_req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
++	fsf_req->qtcb->bottom.io.service_class
++		= adapter->fc_service_class;
++	fsf_req->qtcb->bottom.io.fcp_cmnd_length
++		= sizeof(fcp_cmnd_iu_t) + sizeof(fcp_dl_t);
++
++	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
++	sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
++        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; 
++
++	/* set FCP related fields in FCP_CMND IU in QTCB */
++	fcp_cmnd_iu = (fcp_cmnd_iu_t*)
++			&(fsf_req->qtcb->bottom.io.fcp_cmnd);
++	fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
++	fcp_cmnd_iu->task_management_flags = tm_flags;
++
++	/* start QDIO request for this FSF request */
++        zfcp_fsf_start_scsi_er_timer(adapter);
++	retval = zfcp_fsf_req_send(fsf_req, NULL);
++	if (retval) {
++                del_timer(&adapter->scsi_er_timer);
++		ZFCP_LOG_INFO(
++                        "error: Could not send an FCP-command (task management) "
++                        "on the adapter with devno 0x%04x, "
++                        "port WWPN 0x%016Lx for unit FCP_LUN 0x%016Lx\n",
++			adapter->devno,
++			(llui_t)unit->port->wwpn,
++			(llui_t)unit->fcp_lun);
++                if (zfcp_fsf_req_free(fsf_req)){
++			ZFCP_LOG_NORMAL(
++				"bug: Could not remove one FSF "
++				"request. Memory leakage possible. "
++                                "(debug info 0x%lx).\n",
++                                (unsigned long)fsf_req);
++                        retval=-EINVAL;
++                };
++		fsf_req = NULL;
++		goto out;
++	}
++
++	ZFCP_LOG_TRACE(
++		"Send FCP Command (task management function) initiated "
++		"(adapter devno=0x%04x, port WWPN=0x%016Lx, unit FCP_LUN=0x%016Lx, "
++		"tm_flags=0x%x)\n",
++		adapter->devno,
++		(llui_t)unit->port->wwpn,
++		(llui_t)unit->fcp_lun,
++		tm_flags);
++
++out:
++        write_unlock_irqrestore(&adapter->request_queue.queue_lock, lock_flags);
++
++	ZFCP_LOG_TRACE("exit (0x%lx)\n", (unsigned long)fsf_req);
++ 
++	return fsf_req;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_send_fcp_command_handler
++ *
++ * purpose:	is called for finished Send FCP Command
++ *
++ * returns:	
++ */
++static int
++	zfcp_fsf_send_fcp_command_handler(
++		zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = -EINVAL;
++        zfcp_unit_t *unit;
++	fsf_qtcb_header_t *header = &fsf_req->qtcb->header;
++	u16 subtable, rule, counter;
++
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) 
++		unit = fsf_req->data.send_fcp_command_task_management.unit;
++	else	unit = fsf_req->data.send_fcp_command_task.unit;
++        
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		/* go directly to calls of special handlers */ 
++                goto skip_fsfstatus;
++        }
++
++	/* evaluate FSF status in QTCB */
++	switch (fsf_req->qtcb->header.fsf_status) {
++
++        case FSF_PORT_HANDLE_NOT_VALID:
++                ZFCP_LOG_FLAGS(1, "FSF_PORT_HANDLE_NOT_VALID\n");
++                ZFCP_LOG_INFO("Temporary port identifier (handle) 0x%x "
++				"for the port with WWPN 0x%016Lx connected to "
++                                "the adapter of devno 0x%04x is not valid.\n",
++				unit->port->handle,
++				(llui_t)unit->port->wwpn,
++                                unit->port->adapter->devno);
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
++                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
++                              16);
++                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_phand_nv");
++                zfcp_erp_adapter_reopen(unit->port->adapter, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"porthinv", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++                
++        case FSF_LUN_HANDLE_NOT_VALID:
++                ZFCP_LOG_FLAGS(1, "FSF_LUN_HANDLE_NOT_VALID\n");
++                ZFCP_LOG_INFO("Temporary LUN identifier (handle) 0x%x "
++                              "of the logical unit with FCP_LUN 0x%016Lx at "
++                              "the remote port with WWPN 0x%016Lx connected "
++                              "to the adapter with devno 0x%04x is " 
++                              "not valid. This may happen occasionally.\n",
++                              unit->handle,
++                              (llui_t)unit->fcp_lun,
++                              (llui_t)unit->port->wwpn,
++                              unit->port->adapter->devno);
++                ZFCP_LOG_NORMAL("Status qualifier data:\n");
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
++                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
++                              16);
++                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_uhand_nv");
++                zfcp_erp_port_reopen(unit->port, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"lunhinv", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++
++        case FSF_HANDLE_MISMATCH:
++                ZFCP_LOG_FLAGS(0, "FSF_HANDLE_MISMATCH\n");
++                ZFCP_LOG_NORMAL("bug: The port handle (temporary port "
++                                "identifier) 0x%x has changed unexpectedly. " 
++				"This was detected upon receiveing the response "
++                                "of a command send to the unit with FCP_LUN "
++                                "0x%016Lx at the remote port with WWPN 0x%016Lx "
++                                "connected to the adapter with devno 0x%04x.\n",
++				unit->port->handle,
++				(llui_t)unit->fcp_lun,
++				(llui_t)unit->port->wwpn,
++				unit->port->adapter->devno);
++                ZFCP_LOG_NORMAL("status qualifier:\n");
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_NORMAL,
++                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
++                              16);
++                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_hand_mis");
++                zfcp_erp_adapter_reopen(unit->port->adapter, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"handmism", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++
++        case FSF_SERVICE_CLASS_NOT_SUPPORTED :
++                ZFCP_LOG_FLAGS(0, "FSF_SERVICE_CLASS_NOT_SUPPORTED\n");
++                if(fsf_req->adapter->fc_service_class <= 3) {
++                        ZFCP_LOG_NORMAL( "error: The adapter with devno=0x%04x does "
++                                         "not support fibre-channel class %d.\n",
++                                         unit->port->adapter->devno,
++                                         fsf_req->adapter->fc_service_class);
++                } else {
++                        ZFCP_LOG_NORMAL(
++                                        "bug: The fibre channel class at the adapter "
++                                        "with devno 0x%04x is invalid. "
++                                        "(debug info %d)\n",
++                                        unit->port->adapter->devno,
++                                        fsf_req->adapter->fc_service_class);
++                }
++                /* stop operation for this adapter */
++                debug_text_exception(fsf_req->adapter->erp_dbf,0,"fsf_s_class_nsup");
++                zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"unsclass", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++
++        case FSF_FCPLUN_NOT_VALID:
++                ZFCP_LOG_FLAGS(0, "FSF_FCPLUN_NOT_VALID\n");
++                ZFCP_LOG_NORMAL("bug: The FCP_LUN 0x%016Lx behind the remote port "
++				"of WWPN 0x%016Lx via the adapter with "
++                                "devno 0x%04x does not have the correct unit "
++                                "handle (temporary unit identifier) 0x%x\n",
++				(llui_t)unit->fcp_lun,
++				(llui_t)unit->port->wwpn,
++				unit->port->adapter->devno,
++				unit->handle);
++                ZFCP_LOG_DEBUG("status qualifier:\n");
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
++                              (char*)&fsf_req->qtcb->header.fsf_status_qual,
++                              16);
++                debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_s_fcp_lun_nv");
++                zfcp_erp_port_reopen(unit->port, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"fluninv", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++                
++
++	case FSF_ACCESS_DENIED :
++		ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n");
++		ZFCP_LOG_NORMAL("Access denied, cannot send FCP command "
++				"(devno=0x%04x wwpn=0x%016Lx lun=0x%016Lx)\n",
++				unit->port->adapter->devno,
++				(llui_t)unit->port->wwpn,
++				(llui_t)unit->fcp_lun);
++		for (counter = 0; counter < 2; counter++) {
++			subtable = header->fsf_status_qual.halfword[counter * 2];
++			rule = header->fsf_status_qual.halfword[counter * 2 + 1];
++			switch (subtable) {
++			case FSF_SQ_CFDC_SUBTABLE_OS:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_WWPN:
++			case FSF_SQ_CFDC_SUBTABLE_PORT_DID:
++			case FSF_SQ_CFDC_SUBTABLE_LUN:
++				ZFCP_LOG_INFO("Access denied (%s rule %d)\n",
++					zfcp_act_subtable_type[subtable], rule);
++				break;
++			}
++		}
++		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_access");
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++		break;
++
++        case FSF_DIRECTION_INDICATOR_NOT_VALID:
++                ZFCP_LOG_FLAGS(0, "FSF_DIRECTION_INDICATOR_NOT_VALID\n");
++                ZFCP_LOG_INFO("bug: Invalid data direction given for the unit "
++                              "with FCP_LUN 0x%016Lx at the remote port with "
++                              "WWPN 0x%016Lx via the adapter with devno 0x%04x "
++                              "(debug info %d)\n",
++                              (llui_t)unit->fcp_lun,
++                              (llui_t)unit->port->wwpn,
++                              unit->port->adapter->devno,
++                              fsf_req->qtcb->bottom.io.data_direction);
++                /* stop operation for this adapter */
++                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_dir_ind_nv");
++                zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"dirinv", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++
++	/* FIXME: this should be obsolete, isn' it? */
++        case FSF_INBOUND_DATA_LENGTH_NOT_VALID:
++                ZFCP_LOG_FLAGS(0, "FSF_INBOUND_DATA_LENGTH_NOT_VALID\n");
++                ZFCP_LOG_NORMAL("bug: An invalid inbound data length field "
++				"was found in a command for the unit with "
++                                "FCP_LUN 0x%016Lx of the remote port "
++				"with WWPN 0x%016Lx via the adapter with "
++                                "devno 0x%04x\n",
++				(llui_t)unit->fcp_lun,
++				(llui_t)unit->port->wwpn,
++				unit->port->adapter->devno);
++                /* stop operation for this adapter */
++                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_in_dl_nv");
++                zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"odleninv", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++
++	/* FIXME: this should be obsolete, isn' it? */
++        case FSF_OUTBOUND_DATA_LENGTH_NOT_VALID:
++                ZFCP_LOG_FLAGS(0, "FSF_OUTBOUND_DATA_LENGTH_NOT_VALID\n");
++                ZFCP_LOG_NORMAL("bug: An invalid outbound data length field "
++				"was found in a command for the unit with "
++                                "FCP_LUN 0x%016Lx of the remote port "
++				"with WWPN 0x%016Lx via the adapter with "
++                                "devno 0x%04x\n",
++				(llui_t)unit->fcp_lun,
++				(llui_t)unit->port->wwpn,
++				unit->port->adapter->devno);
++                /* stop operation for this adapter */
++                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_out_dl_nv");
++                zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"idleninv", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++
++        case FSF_CMND_LENGTH_NOT_VALID:
++                ZFCP_LOG_FLAGS(0, "FSF_CMND_LENGTH_NOT_VALID\n");
++                ZFCP_LOG_NORMAL("bug: An invalid control-data-block length field "
++				"was found in a command for the unit with "
++                                "FCP_LUN 0x%016Lx of the remote port "
++				"with WWPN 0x%016Lx via the adapter with "
++                                "devno 0x%04x (debug info %d)\n",
++				(llui_t)unit->fcp_lun,
++				(llui_t)unit->port->wwpn,
++				unit->port->adapter->devno,
++				fsf_req->qtcb->bottom.io.fcp_cmnd_length);
++                /* stop operation for this adapter */
++                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_cmd_len_nv");
++                zfcp_erp_adapter_shutdown(unit->port->adapter, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"cleninv", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                break;
++                
++        case FSF_PORT_BOXED :
++                ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n");
++                ZFCP_LOG_DEBUG("The remote port "
++                               "with WWPN 0x%016Lx on the adapter with "
++                               "devno 0x%04x needs to be reopened\n",
++                               (llui_t)unit->port->wwpn,
++                               unit->port->adapter->devno);
++                debug_text_event(fsf_req->adapter->erp_dbf,2,"fsf_s_pboxed");
++                zfcp_erp_port_reopen(unit->port, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"portbox", fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
++                        | ZFCP_STATUS_FSFREQ_RETRY;
++                break;
++                        
++
++	case FSF_LUN_BOXED :
++		ZFCP_LOG_FLAGS(0, "FSF_LUN_BOXED\n");
++		ZFCP_LOG_NORMAL(
++			"The remote unit needs to be reopened "
++			"(devno=0x%04x wwpn=0x%016Lx lun=0x%016Lx)\n",
++			unit->port->adapter->devno,
++			(llui_t)unit->port->wwpn,
++			(llui_t)unit->fcp_lun);
++		debug_text_event(fsf_req->adapter->erp_dbf, 1, "fsf_s_lboxed");
++		zfcp_erp_unit_reopen(unit, 0);
++		zfcp_cmd_dbf_event_fsf(
++			"unitbox",
++			fsf_req,
++			&fsf_req->qtcb->header.fsf_status_qual,
++			sizeof(fsf_status_qual_t));
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR
++			| ZFCP_STATUS_FSFREQ_RETRY;
++		break;
++
++        case FSF_ADAPTER_STATUS_AVAILABLE :
++                ZFCP_LOG_FLAGS(2, "FSF_ADAPTER_STATUS_AVAILABLE\n");
++                switch (fsf_req->qtcb->header.fsf_status_qual.word[0]){
++                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE :
++                        ZFCP_LOG_FLAGS(2, "FSF_SQ_INVOKE_LINK_TEST_PROCEDURE\n");
++                        /* re-establish link to port */
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ltest");
++                        zfcp_erp_port_reopen(unit->port, 0);
++			zfcp_cmd_dbf_event_fsf(
++				"sqltest", fsf_req,
++				&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;                        
++                        break;
++                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED :
++                        ZFCP_LOG_FLAGS(3, "FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED\n");
++                        /* FIXME(hw) need proper specs for proper action */
++                        /* let scsi stack deal with retries and escalation */
++                        debug_text_event(fsf_req->adapter->erp_dbf,1,"fsf_sq_ulp");
++			zfcp_cmd_dbf_event_fsf(
++				"sqdeperp", fsf_req,
++				&fsf_req->qtcb->header.fsf_status_qual, sizeof(fsf_status_qual_t));
++                        fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
++                        break;
++                default:
++			/* FIXME: shall we consider this a successful transfer? */
++                        ZFCP_LOG_NORMAL("bug: Wrong status qualifier 0x%x arrived.\n",
++                                        fsf_req->qtcb->header.fsf_status_qual.word[0]);
++                        debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_sq_inval:");
++                        debug_exception(fsf_req->adapter->erp_dbf,0,
++                                        &fsf_req->qtcb->header.fsf_status_qual.word[0],
++                                        sizeof(u32));
++                        break;
++                }
++                break;
++                
++	case FSF_GOOD:
++                ZFCP_LOG_FLAGS(3, "FSF_GOOD\n");
++		break;
++
++        case FSF_FCP_RSP_AVAILABLE:
++                ZFCP_LOG_FLAGS(2, "FSF_FCP_RSP_AVAILABLE\n");
++                break;
++
++	default :
++                debug_text_event(fsf_req->adapter->erp_dbf,0,"fsf_s_inval:");
++                debug_exception(fsf_req->adapter->erp_dbf,0,
++                                &fsf_req->qtcb->header.fsf_status,
++                                sizeof(u32));
++                break;
++	}
++
++skip_fsfstatus:
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT) {
++		retval = zfcp_fsf_send_fcp_command_task_management_handler(
++				fsf_req);
++	} else	{
++		retval = zfcp_fsf_send_fcp_command_task_handler(
++				fsf_req);
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_send_fcp_command_task_handler
++ *
++ * purpose:	evaluates FCP_RSP IU
++ *
++ * returns:	
++ */
++static int zfcp_fsf_send_fcp_command_task_handler(
++		zfcp_fsf_req_t *fsf_req)
++
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++	int retval = 0;
++
++	Scsi_Cmnd *scpnt;
++	fcp_rsp_iu_t *fcp_rsp_iu =
++		(fcp_rsp_iu_t*)
++		&(fsf_req->qtcb->bottom.io.fcp_rsp);
++	fcp_cmnd_iu_t *fcp_cmnd_iu =
++		(fcp_cmnd_iu_t*)
++		&(fsf_req->qtcb->bottom.io.fcp_cmnd);
++	u32 sns_len;
++        char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
++	unsigned long flags;
++        zfcp_unit_t *unit = fsf_req->data.send_fcp_command_task.unit;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++	read_lock_irqsave(&fsf_req->adapter->abort_lock, flags);
++        scpnt = fsf_req->data.send_fcp_command_task.scsi_cmnd;
++        if (!scpnt) {
++                ZFCP_LOG_DEBUG("Command with fsf_req 0x%lx is not associated to "
++                               "a scsi command anymore. Aborted?\n",
++                               (unsigned long)fsf_req);
++                goto out;
++        }
++
++        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ABORTED) {
++		/* FIXME: (design) mid-layer should handle DID_ABORT like
++		 *        DID_SOFT_ERROR by retrying the request for devices
++		 *        that allow retries.
++		 */
++                ZFCP_LOG_DEBUG("Setting DID_SOFT_ERROR and SUGGEST_RETRY\n");
++		scpnt->result |= DID_SOFT_ERROR << 16 |
++				 SUGGEST_RETRY << 24;
++		goto skip_fsfstatus;
++        }
++
++        if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++                ZFCP_LOG_DEBUG("Setting DID_ERROR\n");
++		scpnt->result |= DID_ERROR << 16;
++                goto skip_fsfstatus;
++        }
++
++	/* set message byte of result in SCSI command */
++	scpnt->result |= COMMAND_COMPLETE << 8;
++
++	/*
++	 * copy SCSI status code of FCP_STATUS of FCP_RSP IU to status byte
++	 * of result in SCSI command
++	 */
++	scpnt->result |= fcp_rsp_iu->scsi_status;
++        if(fcp_rsp_iu->scsi_status) {
++                /* DEBUG */
++                ZFCP_LOG_DEBUG("status for SCSI Command:\n");
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
++                              scpnt->cmnd,
++                              scpnt->cmd_len);
++                ZFCP_LOG_DEBUG("SCSI status code 0x%x\n",
++                               fcp_rsp_iu->scsi_status);
++                ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG,
++                              (void *)fcp_rsp_iu,
++                              sizeof(fcp_rsp_iu_t));
++                ZFCP_HEX_DUMP(
++                              ZFCP_LOG_LEVEL_DEBUG,
++                              zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu),
++                              fcp_rsp_iu->fcp_sns_len);
++        }
++
++	/* check FCP_RSP_INFO */
++	if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid) {
++		ZFCP_LOG_DEBUG("rsp_len is valid\n");
++		switch (fcp_rsp_info[3]) {
++			case RSP_CODE_GOOD:
++                                ZFCP_LOG_FLAGS(3, "RSP_CODE_GOOD\n");
++				/* ok, continue */
++				ZFCP_LOG_TRACE(
++					"no failure or Task Management "
++					"Function complete\n");
++				scpnt->result |= DID_OK << 16;
++				break;
++			case RSP_CODE_LENGTH_MISMATCH:
++                                ZFCP_LOG_FLAGS(0, "RSP_CODE_LENGTH_MISMATCH\n");
++				/* hardware bug */
++				ZFCP_LOG_NORMAL(
++					"bug: FCP response code indictates "
++                                        " that the fibre-channel protocol data "
++                                        "length differs from the burst "
++                                        "length. The problem occured on the unit "
++                                        "with FCP_LUN 0x%016Lx connected to the "
++                                        "port with WWPN 0x%016Lx at the adapter with "
++                                        "devno 0x%04x\n",
++                                        (llui_t)unit->fcp_lun,
++                                        (llui_t)unit->port->wwpn,
++                                        unit->port->adapter->devno);
++				/* dump SCSI CDB as prepared by zfcp */
++                                ZFCP_HEX_DUMP(
++					ZFCP_LOG_LEVEL_DEBUG,
++					(char*)&fsf_req->qtcb->
++					bottom.io.fcp_cmnd,
++					FSF_FCP_CMND_SIZE);
++				zfcp_cmd_dbf_event_fsf("clenmism", fsf_req, NULL, 0);
++				scpnt->result |= DID_ERROR << 16;
++                                goto skip_fsfstatus;
++			case RSP_CODE_FIELD_INVALID:
++                                ZFCP_LOG_FLAGS(0, "RSP_CODE_FIELD_INVALID\n");
++				/* driver or hardware bug */
++				ZFCP_LOG_NORMAL(
++					"bug: FCP response code indictates "
++                                        "that the fibre-channel protocol data "
++                                        "fields were incorrectly set-up. "
++                                        "The problem occured on the unit "
++                                        "with FCP_LUN 0x%016Lx connected to the "
++                                        "port with WWPN 0x%016Lx at the adapter with "
++                                        "devno 0x%04x\n",
++                                        (llui_t)unit->fcp_lun,
++                                        (llui_t)unit->port->wwpn,
++                                        unit->port->adapter->devno);
++				/* dump SCSI CDB as prepared by zfcp */
++                                ZFCP_HEX_DUMP(
++					ZFCP_LOG_LEVEL_DEBUG,
++					(char*)&fsf_req->qtcb->
++					bottom.io.fcp_cmnd,
++					FSF_FCP_CMND_SIZE);
++				zfcp_cmd_dbf_event_fsf("codeinv", fsf_req, NULL, 0);
++				scpnt->result |= DID_ERROR << 16;
++                                goto skip_fsfstatus;
++			case RSP_CODE_RO_MISMATCH:
++                                ZFCP_LOG_FLAGS(0, "RSP_CODE_RO_MISMATCH\n");
++				/* hardware bug */
++				ZFCP_LOG_NORMAL(
++					"bug: The FCP response code indicates "
++                                        "that conflicting  values for the "
++                                        "fibre-channel payload offset from the "
++                                        "header were found. "
++                                        "The problem occured on the unit "
++                                        "with FCP_LUN 0x%016Lx connected to the "
++                                        "port with WWPN 0x%016Lx at the adapter with "
++                                        "devno 0x%04x\n",
++                                        (llui_t)unit->fcp_lun,
++                                        (llui_t)unit->port->wwpn,
++                                        unit->port->adapter->devno);
++				/* dump SCSI CDB as prepared by zfcp */
++                                ZFCP_HEX_DUMP(
++					ZFCP_LOG_LEVEL_DEBUG,
++					(char*)&fsf_req->qtcb->
++					bottom.io.fcp_cmnd,
++					FSF_FCP_CMND_SIZE);
++				zfcp_cmd_dbf_event_fsf("codemism", fsf_req, NULL, 0);
++				scpnt->result |= DID_ERROR << 16;
++                                goto skip_fsfstatus;
++			default :
++				ZFCP_LOG_NORMAL(
++                                      "bug: An invalid FCP response "
++                                      "code was detected for a command. "
++                                      "The problem occured on the unit "
++                                      "with FCP_LUN 0x%016Lx connected to the "
++                                      "port with WWPN 0x%016Lx at the adapter with "
++                                      "devno 0x%04x (debug info 0x%x)\n",
++                                      (llui_t)unit->fcp_lun,
++                                      (llui_t)unit->port->wwpn,
++                                      unit->port->adapter->devno,
++                                      fcp_rsp_info[3]);
++				/* dump SCSI CDB as prepared by zfcp */
++                                ZFCP_HEX_DUMP(
++					ZFCP_LOG_LEVEL_DEBUG,
++					(char*)&fsf_req->qtcb->
++					bottom.io.fcp_cmnd,
++					FSF_FCP_CMND_SIZE);
++				zfcp_cmd_dbf_event_fsf("undeffcp", fsf_req, NULL, 0);
++				scpnt->result |= DID_ERROR << 16;
++		}
++	}
++
++	/* check for sense data */
++	if (fcp_rsp_iu->validity.bits.fcp_sns_len_valid) {
++		sns_len = FSF_FCP_RSP_SIZE -
++			  sizeof(fcp_rsp_iu_t) +
++			  fcp_rsp_iu->fcp_rsp_len;
++		ZFCP_LOG_TRACE(
++			"room for %i bytes sense data in QTCB\n",
++			sns_len);
++		sns_len = min(sns_len, (u32)SCSI_SENSE_BUFFERSIZE);
++		ZFCP_LOG_TRACE(
++			"room for %i bytes sense data in SCSI command\n",
++			SCSI_SENSE_BUFFERSIZE);
++		sns_len = min(sns_len, fcp_rsp_iu->fcp_sns_len);
++                ZFCP_LOG_TRACE("scpnt->result =0x%x, command was:\n",
++                               scpnt->result);
++                ZFCP_HEX_DUMP(
++			ZFCP_LOG_LEVEL_TRACE,
++			(void *)&scpnt->cmnd,
++                        scpnt->cmd_len);
++
++		ZFCP_LOG_TRACE(
++			"%i bytes sense data provided by FCP\n",
++			fcp_rsp_iu->fcp_sns_len);
++		memcpy(	&scpnt->sense_buffer,
++			zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu),
++			sns_len);
++                ZFCP_HEX_DUMP(
++			ZFCP_LOG_LEVEL_TRACE,
++			(void *)&scpnt->sense_buffer,
++                        sns_len);
++	}
++
++	/* check for overrun */
++	if (fcp_rsp_iu->validity.bits.fcp_resid_over) {
++		ZFCP_LOG_INFO(
++                        "A data overrun was detected for a command. "
++                        "This happened for a command to the unit "
++                        "with FCP_LUN 0x%016Lx connected to the "
++                        "port with WWPN 0x%016Lx at the adapter with "
++                        "devno 0x%04x. The response data length is "
++                        "%d, the original length was %d.\n",
++                        (llui_t)unit->fcp_lun,
++                        (llui_t)unit->port->wwpn,
++                        unit->port->adapter->devno,
++			fcp_rsp_iu->fcp_resid,
++                        zfcp_get_fcp_dl(fcp_cmnd_iu));
++	}
++
++	/* check for underrun */ 
++	if (fcp_rsp_iu->validity.bits.fcp_resid_under) {
++		ZFCP_LOG_DEBUG(
++                        "A data underrun was detected for a command. "
++                        "This happened for a command to the unit "
++                        "with FCP_LUN 0x%016Lx connected to the "
++                        "port with WWPN 0x%016Lx at the adapter with "
++                        "devno 0x%04x. The response data length is "
++                        "%d, the original length was %d.\n",
++                        (llui_t)unit->fcp_lun,
++                        (llui_t)unit->port->wwpn,
++                        unit->port->adapter->devno,
++			fcp_rsp_iu->fcp_resid,
++                        zfcp_get_fcp_dl(fcp_cmnd_iu));
++                /*
++                 * It may not have been possible to send all data and the
++                 * underrun on send may already be in scpnt->resid, so it's add
++                 * not equals in the below statement.
++                 */
++		scpnt->resid += fcp_rsp_iu->fcp_resid;
++                ZFCP_LOG_TRACE("scpnt->resid=0x%x\n",
++                                scpnt->resid);
++	}
++
++skip_fsfstatus:
++#if 0
++	/*
++	 * This nasty chop at the problem is not working anymore
++	 * as we do not adjust the retry count anylonger in order
++	 * to have a number of retries that avoids I/O errors.
++	 * The manipulation of the retry count has been removed
++	 * in favour of a safe tape device handling. We must not
++	 * sent SCSI commands more than once to a device if no
++	 * retries are permitted by the high level driver. Generally
++	 * speaking, it was a mess to change retry counts. So it is
++	 * fine that this sort of workaround is gone.
++	 * Then, we had to face a certain number of immediate retries in case of
++	 * busy and queue full conditions (see below).
++	 * This is not acceptable
++	 * for the latter. Queue full conditions are used
++	 * by devices to indicate to a host that the host can rely
++	 * on the completion (or timeout) of at least one outstanding
++	 * command as a suggested trigger for command retries.
++	 * Busy conditions require a different trigger since
++	 * no commands are outstanding for that initiator from the
++	 * devices perspective.
++	 * The drawback of mapping a queue full condition to a
++	 * busy condition is the chance of wasting all retries prior
++	 * to the time when the device indicates that a command
++	 * rejected due to a queue full condition should be re-driven.
++	 * This case would lead to unnecessary I/O errors that
++	 * have to be considered fatal if for example ext3's
++	 * journaling would be torpedoed by such an avoidable
++	 * I/O error.
++	 * So, what issues are there with not mapping a queue-full
++	 * condition to a busy condition?
++	 * Due to the 'exclusive LUN'
++	 * policy enforced by the zSeries FCP channel, this 
++	 * Linux instance is the only initiator with regard to
++	 * this adapter. It is safe to rely on the information
++	 * 'don't disturb me now ... and btw. no other commands
++	 * pending for you' (= queue full) sent by the LU,
++	 * since no other Linux can use this LUN via this adapter
++	 * at the same time. If there is a potential race
++	 * introduced by the FCP channel by not inhibiting Linux A
++	 * to give up a LU with commands pending while Linux B
++	 * grabs this LU and sends commands  - thus providing
++	 * an exploit at the 'exclusive LUN' policy - then this
++	 * issue has to be considered a hardware problem. It should
++	 * be tracked as such if it really occurs. Even if the
++	 * FCP Channel spec. begs exploiters to wait for the
++	 * completion of all request sent to a LU prior to
++	 * closing this LU connection.
++	 * This spec. statement in conjunction with
++	 * the 'exclusive LUN' policy is not consistent design.
++	 * Another issue is how resource constraints for SCSI commands
++	 * might be handled by the FCP channel (just guessing for now).
++	 * If the FCP channel would always map resource constraints,
++	 * e.g. no free FC exchange ID due to I/O stress caused by
++	 * other sharing Linux instances, to faked queue-full
++	 * conditions then this would be a misinterpretation and
++	 * violation of SCSI standards.
++	 * If there are SCSI stack races as indicated below
++	 * then they need to be fixed just there.
++	 * Providing all issue above are not applicable or will
++	 * be fixed appropriately, removing the following hack
++	 * is the right thing to do.
++	 */
++
++	/*
++	 * Note: This is a rather nasty chop at the problem. We cannot 
++	 * risk adding to the mlqueue however as this will block the 
++	 * device. If it is the last outstanding command for this host
++	 * it will remain blocked indefinitely. This would be quite possible
++	 * on the zSeries FCP adapter.
++	 * Also, there exists a race with scsi_insert_special relying on 
++	 * scsi_request_fn to recalculate some command data which may not 
++	 * happen when q->plugged is true in scsi_request_fn
++	 */
++	if (status_byte(scpnt->result) == QUEUE_FULL) {
++		ZFCP_LOG_DEBUG("Changing QUEUE_FULL to BUSY....\n");
++		scpnt->result &= ~(QUEUE_FULL << 1);
++		scpnt->result |= (BUSY << 1);
++	}
++#endif
++
++        ZFCP_LOG_DEBUG("scpnt->result =0x%x\n",
++                       scpnt->result);
++
++	zfcp_cmd_dbf_event_scsi("response", fsf_req->adapter, scpnt);
++
++	/* cleanup pointer (need this especially for abort) */
++	scpnt->host_scribble = NULL;
++	scpnt->SCp.ptr = (char*)0;
++
++	/*
++	 * NOTE:
++	 * according to the outcome of a discussion on linux-scsi we
++	 * don't need to grab the io_request_lock here since we use
++	 * the new eh
++	 */
++	/* always call back */
++#ifdef ZFCP_DEBUG_REQUESTS
++        debug_text_event(fsf_req->adapter->req_dbf, 2, "ok_done:");
++        debug_event(fsf_req->adapter->req_dbf, 2, &scpnt, sizeof(unsigned long));
++        debug_event(fsf_req->adapter->req_dbf, 2, &scpnt->scsi_done, 
++                      sizeof(unsigned long));
++        debug_event(fsf_req->adapter->req_dbf, 2, &fsf_req, sizeof(unsigned long));
++#endif /* ZFCP_DEBUG_REQUESTS */
++        (scpnt->scsi_done)(scpnt);
++ 	/*
++	 * We must hold this lock until scsi_done has been called.
++	 * Otherwise we may call scsi_done after abort regarding this
++	 * command has completed.
++	 * Note: scsi_done must not block!
++	 */
++out:
++	read_unlock_irqrestore(&fsf_req->adapter->abort_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_send_fcp_command_task_management_handler
++ *
++ * purpose:	evaluates FCP_RSP IU
++ *
++ * returns:	
++ */
++static int zfcp_fsf_send_fcp_command_task_management_handler(
++     zfcp_fsf_req_t *fsf_req)
++
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = 0;
++	fcp_rsp_iu_t *fcp_rsp_iu =
++		(fcp_rsp_iu_t*)
++		&(fsf_req->qtcb->bottom.io.fcp_rsp);
++        char *fcp_rsp_info = zfcp_get_fcp_rsp_info_ptr(fcp_rsp_iu);
++        zfcp_unit_t *unit = fsf_req->data.send_fcp_command_task_management.unit;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx)\n",
++		(unsigned long)fsf_req);
++
++        del_timer(&fsf_req->adapter->scsi_er_timer);
++	if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) {
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED; 
++                goto skip_fsfstatus;
++        }
++
++	/* check FCP_RSP_INFO */
++	switch (fcp_rsp_info[3]) {
++		case RSP_CODE_GOOD:
++                        ZFCP_LOG_FLAGS(3, "RSP_CODE_GOOD\n");
++			/* ok, continue */
++			ZFCP_LOG_DEBUG(
++				"no failure or Task Management "
++				"Function complete\n");
++			break;
++		case RSP_CODE_TASKMAN_UNSUPP:
++                        ZFCP_LOG_FLAGS(0, "RSP_CODE_TASKMAN_UNSUPP\n");
++			ZFCP_LOG_NORMAL(
++				"bug: A reuested task management function "
++                                "is not supported on the target device "
++                                "The corresponding device is the unit with "
++                                "FCP_LUN 0x%016Lx at the port "
++                                "with WWPN 0x%016Lx at the adapter with devno "
++                                "0x%04x\n",
++                                (llui_t)unit->fcp_lun,
++                                (llui_t)unit->port->wwpn,
++                                unit->port->adapter->devno);
++                        fsf_req->status
++				|= ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP;
++			break;
++		case RSP_CODE_TASKMAN_FAILED:
++                        ZFCP_LOG_FLAGS(0, "RSP_CODE_TASKMAN_FAILED\n");
++			ZFCP_LOG_NORMAL(
++				"bug: A reuested task management function "
++                                "failed to complete successfully. "
++                                "The corresponding device is the unit with "
++                                "FCP_LUN 0x%016Lx at the port "
++                                "with WWPN 0x%016Lx at the adapter with devno "
++                                "0x%04x\n",
++                                (llui_t)unit->fcp_lun,
++                                (llui_t)unit->port->wwpn,
++                                unit->port->adapter->devno);
++			fsf_req->status
++				|= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
++			break;
++		default :
++                        ZFCP_LOG_NORMAL(
++                               "bug: An invalid FCP response "
++                               "code was detected for a command. "
++                               "The problem occured on the unit "
++                               "with FCP_LUN 0x%016Lx connected to the "
++                               "port with WWPN 0x%016Lx at the adapter with "
++                               "devno 0x%04x (debug info 0x%x)\n",
++                               (llui_t)unit->fcp_lun,
++                               (llui_t)unit->port->wwpn,
++                               unit->port->adapter->devno,
++                               fcp_rsp_info[3]);
++			fsf_req->status
++                                |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
++	}
++
++skip_fsfstatus:
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_req_wait_and_cleanup
++ *
++ * purpose:
++ *
++ * FIXME(design): signal seems to be <0 !!!
++ * returns:	0	- request completed (*status is valid), cleanup succeeded
++ *		<0	- request completed (*status is valid), cleanup failed
++ *		>0	- signal which interrupted waiting (*status is not valid),
++ *			  request not completed, no cleanup
++ *
++ *		*status is a copy of status of completed fsf_req
++ */
++static int zfcp_fsf_req_wait_and_cleanup(
++		zfcp_fsf_req_t *fsf_req,
++		int interruptible,
++		u32 *status)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = 0;
++	int signal = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx,"
++		"interruptible=%d, *status=0x%x\n",
++		(unsigned long)fsf_req,
++		interruptible,
++		*status);
++
++	if (interruptible) {
++		__wait_event_interruptible(
++			fsf_req->completion_wq,
++			fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED,
++			signal);
++		if (signal) {
++			ZFCP_LOG_DEBUG(
++				"Caught signal %i while waiting for the "
++                                "completion of the request at 0x%lx\n",
++				signal,
++				(unsigned long)fsf_req);
++			retval = signal;
++			goto out;
++		}
++	} else	{
++		__wait_event(
++			fsf_req->completion_wq,
++			fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
++	}
++
++	*status = fsf_req->status;
++
++	/* cleanup request */
++	retval = zfcp_fsf_req_cleanup(fsf_req);
++out: 
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++static inline int zfcp_fsf_req_create_sbal_check(
++		unsigned long *flags,
++		zfcp_qdio_queue_t *queue,
++		int needed)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++
++        write_lock_irqsave(&queue->queue_lock, *flags);
++	if (atomic_read(&queue->free_count) >= needed) 
++		return 1;
++        write_unlock_irqrestore(&queue->queue_lock, *flags);
++	return 0;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * set qtcb pointer in fsf_req and initialize QTCB
++ */
++static inline void zfcp_fsf_req_qtcb_init(zfcp_fsf_req_t *fsf_req, u32 fsf_cmd)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++	if (fsf_cmd != FSF_QTCB_UNSOLICITED_STATUS) {
++                struct zfcp_fsf_req_pool_buffer *data =
++                        (struct zfcp_fsf_req_pool_buffer *) fsf_req;
++                fsf_req->qtcb = &data->qtcb;
++        }
++
++	if (fsf_req->qtcb) {
++	        ZFCP_LOG_TRACE("fsf_req->qtcb=0x%lx\n",
++                               (unsigned long ) fsf_req->qtcb);
++		fsf_req->qtcb->prefix.req_id = (unsigned long)fsf_req;
++		fsf_req->qtcb->prefix.ulp_info = zfcp_data.driver_version;
++		fsf_req->qtcb->prefix.qtcb_type = fsf_qtcb_type[fsf_cmd];
++		fsf_req->qtcb->prefix.qtcb_version = ZFCP_QTCB_VERSION;
++		fsf_req->qtcb->header.req_handle = (unsigned long)fsf_req;
++		fsf_req->qtcb->header.fsf_command = fsf_cmd;
++		/* Request Sequence Number is set later when the request is
++                   actually sent. */
++	}
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * try to get needed SBALs in request queue
++ * (get queue lock on success)
++ */
++static int zfcp_fsf_req_sbal_get(zfcp_adapter_t *adapter, int req_flags,
++                                 unsigned long *lock_flags)
++{
++        int condition;
++        unsigned long timeout = ZFCP_SBAL_TIMEOUT;
++        zfcp_qdio_queue_t *req_queue = &adapter->request_queue;
++
++        if (req_flags & ZFCP_WAIT_FOR_SBAL) {
++                ZFCP_WAIT_EVENT_TIMEOUT(adapter->request_wq, timeout,
++                                        (condition =
++                                         (zfcp_fsf_req_create_sbal_check)
++                                         (lock_flags, req_queue, 1)));
++                if (!condition)
++                        return -EIO;
++        } else if (!zfcp_fsf_req_create_sbal_check(lock_flags, req_queue, 1))
++                return -EIO;
++
++        return 0;
++}
++
++
++/*
++ * function:    zfcp_fsf_req_create
++ *
++ * purpose:	create an FSF request at the specified adapter and
++ *		setup common fields
++ *
++ * returns:	-ENOMEM if there was insufficient memory for a request
++ *              -EIO if no qdio buffers could be allocate to the request
++ *              -EINVAL/-EPERM on bug conditions in req_dequeue
++ *              0 in success
++ *
++ * note:        The created request is returned by reference.
++ *
++ * locks:	lock of concerned request queue must not be held,
++ *		but is held on completion (write, irqsave)
++ */
++static int zfcp_fsf_req_create(zfcp_adapter_t *adapter, u32 fsf_cmd,
++                               int req_flags, zfcp_mem_pool_t *mem_pool,
++                               unsigned long *lock_flags,
++                               zfcp_fsf_req_t **fsf_req_p)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++	zfcp_fsf_req_t *fsf_req = NULL;
++       	int retval=0;
++        zfcp_qdio_queue_t *req_queue = &adapter->request_queue;
++        volatile qdio_buffer_element_t *sbale;
++
++        ZFCP_LOG_TRACE("enter (adapter=0x%lx fsf_cmd=0x%x *lock_flags=0x%lx "
++                       "req_flags=0x%x)\n", (unsigned long)adapter,
++                       fsf_cmd, *lock_flags, req_flags);
++
++        atomic_inc(&adapter->reqs_in_progress);
++
++	/* allocate new FSF request */
++	fsf_req = zfcp_fsf_req_alloc(mem_pool, req_flags, GFP_ATOMIC);
++	if (!fsf_req) {
++                ZFCP_LOG_DEBUG(
++			"error: Could not put an FSF request into"
++                        "the outbound (send) queue.\n");
++                retval=-ENOMEM;
++		goto failed_fsf_req;
++	}
++
++        zfcp_fsf_req_qtcb_init(fsf_req, fsf_cmd);
++
++	/* initialize waitqueue which may be used to wait on 
++	   this request completion */
++	init_waitqueue_head(&fsf_req->completion_wq);
++
++        retval = zfcp_fsf_req_sbal_get(adapter, req_flags, lock_flags);
++        if(retval < 0)
++                goto failed_sbals;
++
++        /*
++         * We hold queue_lock here. Check if QDIOUP is set and let request fail
++         * if it is not set (see also *_open_qdio and *_close_qdio).
++         */
++
++        if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
++                write_unlock_irqrestore(&req_queue->queue_lock, *lock_flags);
++                retval = -EIO;
++                goto failed_sbals;
++        }
++
++#ifndef ZFCP_PARANOIA_DEAD_CODE
++	/* set magics */
++	fsf_req->common_magic = ZFCP_MAGIC;
++	fsf_req->specific_magic = ZFCP_MAGIC_FSFREQ;
++#endif
++	fsf_req->adapter = adapter;
++	fsf_req->fsf_command = fsf_cmd;
++	fsf_req->sbal_number = 1;
++	fsf_req->sbal_first = req_queue->free_index;
++	fsf_req->sbal_curr = req_queue->free_index;
++        fsf_req->sbale_curr = 1;
++
++	if (req_flags & ZFCP_REQ_AUTO_CLEANUP)
++		fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
++
++	sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
++
++	/* setup common SBALE fields */
++	sbale[0].addr = fsf_req;
++	sbale[0].flags |= SBAL_FLAGS0_COMMAND;
++	if (fsf_req->qtcb != 0) {
++		sbale[1].addr = (void *)fsf_req->qtcb;
++		sbale[1].length = sizeof(fsf_qtcb_t);
++	}
++
++	ZFCP_LOG_TRACE("got %i free BUFFERs starting at index %i\n",
++                       fsf_req->sbal_number, fsf_req->sbal_first);
++
++	goto success;
++
++ failed_sbals:
++#ifdef ZFCP_STAT_QUEUES
++        atomic_inc(&adapter->outbound_queue_full);
++#endif
++        /* dequeue new FSF request previously enqueued */
++        zfcp_fsf_req_free(fsf_req);
++        fsf_req = NULL;
++        
++ failed_fsf_req:
++        //failed_running:
++        write_lock_irqsave(&req_queue->queue_lock, *lock_flags);
++
++ success: 
++        *fsf_req_p = fsf_req;
++        ZFCP_LOG_TRACE("exit (%d)\n", retval);
++	return retval;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++static inline int zfcp_qdio_determine_pci(zfcp_qdio_queue_t *req_queue, 
++                                          zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_QDIO
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_QDIO
++        int new_distance_from_int;
++        int pci_pos;
++	volatile qdio_buffer_element_t *sbale;
++
++        ZFCP_LOG_TRACE("enter (0x%lx, 0x%lx)\n", 
++                       (unsigned long)req_queue,
++                       (unsigned long)fsf_req);
++
++        new_distance_from_int = req_queue->distance_from_int +
++                fsf_req->sbal_number;
++
++        if (new_distance_from_int >= ZFCP_QDIO_PCI_INTERVAL) {
++                new_distance_from_int %= ZFCP_QDIO_PCI_INTERVAL;
++                pci_pos  = fsf_req->sbal_first;
++		pci_pos += fsf_req->sbal_number;
++		pci_pos -= new_distance_from_int;
++		pci_pos -= 1;
++		pci_pos %= QDIO_MAX_BUFFERS_PER_Q;
++		sbale = zfcp_qdio_sbale_req(fsf_req, pci_pos, 0);
++		sbale->flags |= SBAL_FLAGS0_PCI;
++                ZFCP_LOG_DEBUG(
++			"Setting PCI flag at pos %d (0x%lx)\n",
++			pci_pos,
++			(unsigned long)sbale);
++		ZFCP_HEX_DUMP(
++			ZFCP_LOG_LEVEL_TRACE,
++			(char*)sbale,
++			sizeof(qdio_buffer_t));
++        }
++
++        ZFCP_LOG_TRACE("exit (%d)\n", new_distance_from_int);
++	return new_distance_from_int;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++
++/*
++ * function:    zfcp_fsf_req_send
++ *
++ * purpose:	start transfer of FSF request via QDIO
++ *
++ * returns:	0 - request transfer succesfully started
++ *		!0 - start of request transfer failed
++ */
++static int zfcp_fsf_req_send(zfcp_fsf_req_t *fsf_req, struct timer_list *timer)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval = 0;
++        zfcp_adapter_t *adapter = fsf_req->adapter;
++        zfcp_qdio_queue_t *req_queue = &adapter->request_queue;
++        volatile qdio_buffer_element_t* sbale;
++	int inc_seq_no = 1;
++        int new_distance_from_int;
++	unsigned long flags;
++	int test_count;
++
++	u8 sbal_index = fsf_req->sbal_first;
++
++	ZFCP_LOG_TRACE(
++		"enter (fsf_req=0x%lx timer=0x%lx)\n",
++		(unsigned long)fsf_req,
++		(unsigned long)timer);
++        
++	/* FIXME(debug): remove it later */
++	sbale = zfcp_qdio_sbale_req(fsf_req, sbal_index, 0);
++	ZFCP_LOG_DEBUG(
++		"SBALE0 flags=0x%x\n",
++		sbale[0].flags);
++	ZFCP_LOG_TRACE("HEX DUMP OF SBALE1 PAYLOAD:\n");
++	ZFCP_HEX_DUMP(
++		ZFCP_LOG_LEVEL_TRACE,
++		(char*)sbale[1].addr,
++		sbale[1].length);
++
++	test_count = (fsf_req->sbal_curr - fsf_req->sbal_first) + 1;
++	test_count += QDIO_MAX_BUFFERS_PER_Q; /* no module of <0 */
++	test_count %= QDIO_MAX_BUFFERS_PER_Q;
++	if (fsf_req->sbal_number != test_count)
++		ZFCP_LOG_NORMAL(
++			"error: inconsistent SBAL count in request "
++			"(%d, %d, %d, %d, %d)\n",
++			fsf_req->sbal_first,
++			fsf_req->sbal_curr,
++			fsf_req->sbal_last,
++			fsf_req->sbal_number,
++			test_count);
++        
++	/* set sequence counter in QTCB */
++	if (fsf_req->qtcb) {
++		fsf_req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no;
++		fsf_req->seq_no = adapter->fsf_req_seq_no;
++		ZFCP_LOG_TRACE(
++			"FSF request 0x%lx of adapter 0x%lx gets "
++			"FSF sequence counter value of %i\n",
++			(unsigned long)fsf_req,
++			(unsigned long)adapter,
++			fsf_req->qtcb->prefix.req_seq_no);
++	} else
++                inc_seq_no = 0;
++
++        /* put allocated FSF request at list tail */
++	write_lock_irqsave(&adapter->fsf_req_list_lock, flags);
++        list_add_tail(&fsf_req->list,
++                      &adapter->fsf_req_list_head);
++	write_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
++
++	/* figure out expiration time of timeout and start timeout */
++	if (timer) {
++		timer->expires += jiffies;
++		add_timer(timer);
++	}
++
++	ZFCP_LOG_TRACE(
++		"request queue of adapter with devno=0x%04x: "
++		"next free SBAL is %i, %i free SBALs\n",
++		adapter->devno,
++		req_queue->free_index,
++		atomic_read(&req_queue->free_count));
++
++        ZFCP_LOG_DEBUG(
++		"Calling do QDIO irq=0x%x, flags=0x%x, queue_no=%i, "
++		"index_in_queue=%i, count=%i, buffers=0x%lx\n",
++		adapter->irq,
++		QDIO_FLAG_SYNC_OUTPUT,
++		0,
++		fsf_req->sbal_first,
++		fsf_req->sbal_number,
++		(unsigned long)&req_queue->buffer[sbal_index]);	
++
++        /*
++         * adjust the number of free SBALs in request queue as well as
++         * position of first one
++         */
++        atomic_sub(fsf_req->sbal_number, &req_queue->free_count);
++        ZFCP_LOG_TRACE("free_count=%d\n",
++                       atomic_read(&req_queue->free_count));
++        req_queue->free_index += fsf_req->sbal_number;	/* increase */
++        req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;    /* wrap if needed */
++        new_distance_from_int = zfcp_qdio_determine_pci(req_queue, fsf_req);
++	retval = do_QDIO(
++			adapter->irq,
++			QDIO_FLAG_SYNC_OUTPUT,
++                        0,
++			fsf_req->sbal_first,
++                        fsf_req->sbal_number,
++                        NULL);
++
++	if (retval) {
++                /* Queues are down..... */
++                retval=-EIO;
++		/* FIXME(potential race): timer might be expired (absolutely unlikely) */
++		if (timer)
++			del_timer_sync(timer);
++		write_lock_irqsave(&adapter->fsf_req_list_lock, flags);
++        	list_del(&fsf_req->list);
++		write_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
++                /*
++                 * adjust the number of free SBALs in request queue as well as
++                 * position of first one
++                 */
++                zfcp_zero_sbals(
++			req_queue->buffer,
++			fsf_req->sbal_first,
++			fsf_req->sbal_number);
++                atomic_add(fsf_req->sbal_number, &req_queue->free_count);
++                req_queue->free_index += QDIO_MAX_BUFFERS_PER_Q;
++                req_queue->free_index -= fsf_req->sbal_number;
++                req_queue->free_index %= QDIO_MAX_BUFFERS_PER_Q;
++
++		ZFCP_LOG_DEBUG(
++			"error: do_QDIO failed. Buffers could not be enqueued "
++                        "to request queue.\n");
++        } else {
++                req_queue->distance_from_int = new_distance_from_int;
++#ifdef ZFCP_DEBUG_REQUESTS
++                debug_text_event(adapter->req_dbf, 1, "o:a/seq");
++                debug_event(adapter->req_dbf, 1, &fsf_req,
++                            sizeof(unsigned long));
++                if (inc_seq_no)
++                        debug_event(adapter->req_dbf, 1,
++                                    &adapter->fsf_req_seq_no, sizeof(u32));
++                else
++                        debug_text_event(adapter->req_dbf, 1, "nocb");
++                debug_event(adapter->req_dbf, 4, &fsf_req->fsf_command,
++                            sizeof(fsf_req->fsf_command));
++                if (fsf_req->qtcb)
++                        debug_event(adapter->req_dbf, 5, &fsf_req->qtcb,
++                                    sizeof(unsigned long));
++                if (fsf_req && (fsf_req->status & ZFCP_STATUS_FSFREQ_POOL))
++                        debug_text_event(adapter->req_dbf, 5, "fsfa_pl");
++#endif /* ZFCP_DEBUG_REQUESTS */
++		/*
++		 * increase FSF sequence counter -
++		 * this must only be done for request successfully enqueued to QDIO
++		 * this rejected requests may be cleaned up by calling routines
++		 * resulting in missing sequence counter values otherwise,
++		 */
++                /* Don't increase for unsolicited status */ 
++                if (inc_seq_no) {
++                        adapter->fsf_req_seq_no++;
++			ZFCP_LOG_TRACE(
++				"FSF sequence counter value of adapter 0x%lx "
++				"increased to %i\n",
++				(unsigned long)adapter,
++				adapter->fsf_req_seq_no);
++		}
++		/* count FSF requests pending */
++		atomic_inc(&adapter->fsf_reqs_active);
++#ifdef ZFCP_STAT_QUEUES
++		atomic_inc(&adapter->outbound_total);
++#endif
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_req_cleanup
++ *
++ * purpose:	cleans up an FSF request and removes it from the specified list
++ *
++ * returns:
++ *
++ * assumption:	no pending SB in SBALEs other than QTCB
++ */
++static int zfcp_fsf_req_cleanup(zfcp_fsf_req_t *fsf_req)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_FSF
++ 
++	int retval;
++        zfcp_adapter_t *adapter = fsf_req->adapter;
++	unsigned long flags;
++
++	ZFCP_LOG_TRACE("enter (fsf_req=0x%lx)\n", (unsigned long)fsf_req);
++
++	write_lock_irqsave(&adapter->fsf_req_list_lock, flags);
++        list_del(&fsf_req->list);
++	write_unlock_irqrestore(&adapter->fsf_req_list_lock, flags);
++        retval = zfcp_fsf_req_free(fsf_req);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:	zfcp_zero_sbals
++ *
++ * purpose:	zeros specified range of SBALs
++ *
++ * returns:
++ */
++static inline void zfcp_zero_sbals(qdio_buffer_t *buf[], int first, int clean_count)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_QDIO
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_QDIO
++
++        int cur_pos;
++        int index;
++        
++        
++	ZFCP_LOG_TRACE(
++		"enter (buf=0x%lx, first=%i, clean_count=%i\n",
++		(unsigned long)buf, first, clean_count);
++        
++        for (cur_pos = first; cur_pos < (first + clean_count); cur_pos++){
++                index = cur_pos % QDIO_MAX_BUFFERS_PER_Q;
++                memset(buf[index], 0, sizeof(qdio_buffer_t));
++		ZFCP_LOG_TRACE(
++			"zeroing BUFFER %d at address 0x%lx\n",
++                        index,
++			(unsigned long) buf[index]);
++        }
++
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++static void zfcp_config_parse_error(
++	unsigned char	*s,		/* complete mapping string */
++	unsigned char	*err_pos,	/* position of error in mapping string */
++	const char	*err_msg,	/* error message */
++	...)				/* additional arguments to be integrated into error message */
++{
++#define ZFCP_LOG_AREA		ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX	ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	int buf_l;
++	va_list args;
++	unsigned char *pos;
++	unsigned char c;
++
++	ZFCP_LOG_TRACE(
++		"enter (s=0x%lx, err_pos=0x%lx, err_msg=0x%lx\n",
++		(unsigned long)s, 
++                (unsigned long)err_pos, 
++                (unsigned long)err_msg);
++
++	/* integrate additional arguments into error message */
++	va_start(args, err_msg);
++	buf_l = vsprintf(zfcp_data.perrbuf, err_msg, args);
++	va_end(args);
++	if (buf_l > ZFCP_PARSE_ERR_BUF_SIZE) {
++		ZFCP_LOG_NORMAL("Buffer overflow while parsing error message\n");
++		/* truncate error message */
++		zfcp_data.perrbuf[ZFCP_PARSE_ERR_BUF_SIZE - 1] = '\0';
++		buf_l = ZFCP_PARSE_ERR_BUF_SIZE;
++	}
++
++	/* calculate and print substring of mapping followed by error info */
++	pos = min((s + strlen(s) - 1), (err_pos + 1));
++	c = *pos;
++	*pos = '\0';
++	ZFCP_LOG_NORMAL("\"%s\" <- %s\n", s, zfcp_data.perrbuf);
++	*pos = c;
++
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/* these macros implement the logic of the following 3 functions */
++#define ZFCP_PARSE_CHECK(condition, err_msg...) \
++		if (condition) { \
++			zfcp_config_parse_error(s, s + s_l - ts_l, err_msg); \
++			retval = -EINVAL; \
++			goto out; \
++		}
++
++#define ZFCP_PARSE_CHECKEND \
++		ZFCP_PARSE_CHECK(!ts_l, "syntax error: unexpected end of record")
++
++#define ZFCP_PARSE_TRUNCATE \
++		ts += count; ts_l -= count;
++
++#define ZFCP_PARSE_SKIP_CHARS(characters, min, max) \
++		count = strnspn(ts, characters, ts_l); \
++		ZFCP_PARSE_CHECK((size_t)count < (size_t)min, "syntax error: missing \"%c\" or equivalent character", *characters) \
++		ZFCP_PARSE_CHECK((size_t)count > (size_t)max, "syntax error: extranous \"%c\" or equivalent character", *characters) \
++		ZFCP_PARSE_TRUNCATE
++
++#define ZFCP_PARSE_SKIP_COMMENT \
++		count = strnspn(ts, ZFCP_PARSE_COMMENT_CHARS, ts_l); \
++		if (count) { \
++			char *tmp; \
++			ZFCP_PARSE_TRUNCATE \
++			tmp = strnpbrk(ts, ZFCP_PARSE_RECORD_DELIM_CHARS, ts_l); \
++			if (tmp) \
++				count = (unsigned long)tmp - (unsigned long)ts; \
++			else	count = ts_l; \
++			ZFCP_PARSE_TRUNCATE \
++		}
++
++#define ZFCP_PARSE_NUMBER(func, value, add_cond, msg...) \
++		value = func(ts, &endp, 0); \
++		count = (unsigned long)endp - (unsigned long)ts; \
++		ZFCP_PARSE_CHECK(!count || (add_cond), msg) \
++		ZFCP_PARSE_TRUNCATE
++		
++#define ZFCP_PARSE_UL(value, cond, msg...) \
++		ZFCP_PARSE_NUMBER(simple_strtoul, value, cond, msg)
++
++#define ZFCP_PARSE_ULL(value, cond, msg...) \
++		ZFCP_PARSE_NUMBER(simple_strtoull, value, cond, msg)
++
++
++static int zfcp_config_parse_record_list(unsigned char *s, int s_l, int flags)
++{
++#define ZFCP_LOG_AREA           ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX    ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	int retval;
++	int count;
++	zfcp_config_record_t rec;
++	int ts_l = s_l;
++	unsigned char *ts = s;
++
++	ZFCP_LOG_TRACE(
++		"enter (s=0x%lx, s_l=%i, flags=%i)\n",
++		(unsigned long)s, s_l, flags);
++
++	while (ts_l) {
++		/* parse single line */
++		count = zfcp_config_parse_record(ts, ts_l, &rec);
++		if (count < 0) {
++			retval = count;
++			goto out;
++		}
++		ZFCP_PARSE_TRUNCATE;
++
++		/* create configuration according to parsed line */
++		if (rec.valid) {
++			if (flags & ZFCP_PARSE_ADD) {
++				retval = zfcp_config_parse_record_add(&rec);
++                        } else {	
++                                /* FIXME (implement) switch in when record_del works again */
++#if 0
++                                retval = zfcp_config_parse_record_del(&rec);
++#endif
++                                ZFCP_LOG_TRACE("DEL\n");
++                                retval = -1;
++                        }
++			if (retval < 0)
++				goto out;
++		} /* else we parsed an empty line or a comment */
++                if (ts_l > 0) {
++                        /* skip expected 'new line' */
++			ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_RECORD_DELIM_CHARS, 1, ts_l);
++                }
++	}
++	retval = s_l;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++static int zfcp_config_parse_record(
++	unsigned char	*s,
++	int		s_l,
++	zfcp_config_record_t *rec)
++{
++#define ZFCP_LOG_AREA           ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX    ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	int retval;
++	int count = 0;
++	char *endp;
++	unsigned char *ts = s;
++	int ts_l = s_l;
++
++	ZFCP_LOG_TRACE(
++		"enter (s=0x%lx, s_l=%i, rec=0x%lx)\n",
++		(unsigned long)s, 
++                s_l,
++                (unsigned long)rec);
++
++	rec->valid = 0;
++
++	/* skip any leading spaces + tabs */
++	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_SPACE_CHARS, 0, -1UL);
++
++	/* allow for comments */
++	ZFCP_PARSE_SKIP_COMMENT;
++
++	/* allow 'empty' line */
++	if (strnspn(ts, ZFCP_PARSE_RECORD_DELIM_CHARS, 1))
++		goto calculate;
++
++	/* parse device number of host */
++	ZFCP_PARSE_UL(rec->devno, rec->devno > 0xFFFF, "no valid device number");
++	ZFCP_LOG_TRACE("devno \"0x%lx\"\n", rec->devno);
++	ZFCP_PARSE_CHECKEND;
++
++	/* skip delimiting spaces + tabs (at least 1 character is mandatory */
++	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_SPACE_CHARS, 1, -1UL);
++	ZFCP_PARSE_CHECKEND;
++
++	/* parse scsi id of remote port */
++	ZFCP_PARSE_UL(rec->scsi_id, 0, "no valid SCSI ID");
++	ZFCP_LOG_TRACE("SCSI ID \"0x%lx\"\n", rec->scsi_id);
++	ZFCP_PARSE_CHECKEND;
++
++	/* skip delimiting character */
++	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_DELIM_CHARS, 1, 1);
++	ZFCP_PARSE_CHECKEND;
++
++	/* parse wwpn of remote port */
++	ZFCP_PARSE_ULL(rec->wwpn, 0, "no valid WWPN");
++	ZFCP_LOG_TRACE("WWPN \"0x%016Lx\"\n", rec->wwpn);
++	ZFCP_PARSE_CHECKEND;
++
++	/* skip delimiting spaces + tabs (at least 1 character is mandatory */
++	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_SPACE_CHARS, 1, -1UL);
++	ZFCP_PARSE_CHECKEND;
++
++	/* parse scsi lun of logical unit */
++	ZFCP_PARSE_UL(rec->scsi_lun, 0, "no valid SCSI LUN");
++	ZFCP_LOG_TRACE("SCSI LUN \"0x%lx\"\n", rec->scsi_lun);
++	ZFCP_PARSE_CHECKEND;
++
++	/* skip delimiting character */
++	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_DELIM_CHARS, 1, 1);
++	ZFCP_PARSE_CHECKEND;
++
++	/* parse fcp_lun of logical unit */
++	ZFCP_PARSE_ULL(rec->fcp_lun, 0, "no valid FCP_LUN");
++	ZFCP_LOG_TRACE("FCP_LUN \"0x%016Lx\"\n", rec->fcp_lun);
++
++        /* skip any ending spaces + tabs */
++	ZFCP_PARSE_SKIP_CHARS(ZFCP_PARSE_SPACE_CHARS, 0, -1UL);
++
++	/* allow for comments */
++	ZFCP_PARSE_SKIP_COMMENT;
++
++	/* this is something valid */
++	rec->valid = 1;
++
++calculate:
++	/* length of string which has been parsed */
++	retval = s_l - ts_l;
++
++out:
++        ZFCP_LOG_TRACE("exit %d\n",
++                       retval);
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++#define ZFCP_PRINT_FAILED_RECORD(rec, log_func) \
++	log_func( \
++		"warning: unable to add record: " \
++		"0x%04lx %li:0x%016Lx %li:0x%016Lx\n", \
++		rec->devno, \
++		rec->scsi_id, rec->wwpn, \
++		rec->scsi_lun, rec->fcp_lun);
++
++
++/*
++ * function:	zfcp_config_parse_record_add
++ *
++ * purpose:	Alloctes the required adapter, port and unit structs
++ *              and puts them into their respective lists
++ *
++ * returns:	0 on success
++ *              -E* on failure (depends on called routines)
++ */
++int zfcp_config_parse_record_add(zfcp_config_record_t *rec)
++{
++#define ZFCP_LOG_AREA		ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX	ZFCP_LOG_AREA_PREFIX_SCSI
++
++	int retval;
++	zfcp_adapter_t *adapter;
++	zfcp_port_t *port;
++	zfcp_unit_t *unit;
++
++	ZFCP_LOG_TRACE("enter (rec=0x%lx)\n", (unsigned long)rec);
++
++        /* don't allow SCSI ID 0 for any port since it is reserved for adapters */
++        if (rec->scsi_id == 0) {
++                ZFCP_LOG_NORMAL(
++                        "warning: SCSI ID 0 is not allowed for ports as it is "
++                        "reserved for adapters\n");
++		retval = -EINVAL;
++                goto failed_record;
++        }
++	/* check for adapter and configure it if needed */
++        retval = zfcp_adapter_enqueue(rec->devno, &adapter);
++        if (retval < 0)
++                goto failed_record;
++
++	/*
++	 * no explicit adapter reopen necessary,
++	 * will be escalated by unit reopen if required
++	 */
++
++        retval = zfcp_port_enqueue(
++			adapter,
++			rec->scsi_id,
++			rec->wwpn,
++			0,
++			&port);
++        if (retval < 0)
++                goto failed_record;
++
++	/*
++	 * no explicit port reopen necessary,
++	 * will be escalated by unit reopen if required
++	 */
++
++        retval = zfcp_unit_enqueue(
++			port,
++			rec->scsi_lun,
++			rec->fcp_lun,
++			&unit);
++        if (retval < 0)
++                goto failed_record;
++
++	zfcp_erp_unit_reopen(unit, 0);
++
++	/* processed record successfully */
++	goto out;
++
++failed_record:
++	ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_NORMAL);
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++#if 0
++/* FIXME(design): rewrite necessary */
++static int zfcp_config_parse_record_del(zfcp_config_record_t *rec)
++{
++#define ZFCP_LOG_AREA		ZFCP_LOG_AREA_CONFIG
++#define ZFCP_LOG_AREA_PREFIX	ZFCP_LOG_AREA_PREFIX_CONFIG
++
++	int retval = 0;
++	unsigned long flags;
++	zfcp_adapter_t *adapter;
++	zfcp_port_t *port;
++	zfcp_unit_t *unit;
++
++	ZFCP_LOG_TRACE(
++		"enter (rec=0x%lx)\n",
++		(unsigned long)rec);
++
++	/* check for adapter */
++	write_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
++	ZFCP_FOR_EACH_ADAPTER(adapter) {
++                if (adapter->devno == rec->devno)
++			break;
++	}
++	if (!adapter) {
++		ZFCP_LOG_NORMAL(
++			"warning: Could not delete a record. "
++                        "The adapter with devno 0x%04x does not exist.\n",
++			adapter->devno);
++		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_DEBUG);
++		goto unlock_adapter;
++	}
++
++	/* check for remote port */
++	write_lock(&adapter->port_list_lock);
++	ZFCP_FOR_EACH_PORT(adapter, port) {
++		if (port->scsi_id == rec->scsi_id)
++			break;
++	}
++	if (!port) {
++		ZFCP_LOG_NORMAL(
++                               "warning: Could not delete a record. "
++                               "The port with SCSI ID %i does not exist.\n",
++                               port->scsi_id);
++		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_DEBUG);
++		goto unlock_port;
++	}
++	if (port->wwpn != rec->wwpn) {
++		ZFCP_LOG_NORMAL(
++			"error: The port WWPN 0x%016Lx "
++			"does not match the already configured WWPN 0x%016Lx\n",
++			rec->wwpn,
++			(llui_t)port->wwpn);
++		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_INFO);
++		goto unlock_port;
++	}
++
++	/* check for logical unit */
++	write_lock(&port->unit_list_lock);
++	ZFCP_FOR_EACH_UNIT(port, unit) {
++		if (unit->scsi_lun == rec->scsi_lun)
++			break;
++	}
++	if (!unit) {
++		ZFCP_LOG_NORMAL(
++			"warning: Could not delete a record. "
++                        "The unit with SCSI LUN %i does not exist\n",
++			unit->scsi_lun);
++		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_DEBUG);
++		goto unlock_unit;
++	}
++	if (unit->fcp_lun != rec->fcp_lun) {
++		ZFCP_LOG_NORMAL(
++			"error: The record for the FCP_LUN 0x%016Lx "
++			"does not match that of the already "
++                        "configured FCP_LUN 0x%016Lx\n",
++			rec->fcp_lun,
++			(llui_t)unit->fcp_lun);
++		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_INFO);
++		goto unlock_unit;
++	}
++
++	/* FIXME: do more work here: CLOSE UNIT */
++	retval = zfcp_unit_dequeue(unit);
++	if (retval == -EBUSY) {
++		ZFCP_LOG_NORMAL("warning: Attempt to remove active unit with "
++                               "FCP_LUN 0x%016Lx, at the port with WWPN 0x%016Lx of the "
++                               "adapter with devno 0x%04x was ignored. Unit "
++                               "is still in use.\n",
++                               (llui_t)unit->fcp_lun,
++                               (llui_t)unit->port->wwpn,
++                               unit->port->adapter->devno);
++		ZFCP_PRINT_FAILED_RECORD(rec, ZFCP_LOG_INFO);
++		goto unlock_unit;
++	}
++
++	/* FIXME: do more work here: CLOSE PORT */
++	retval = zfcp_port_dequeue(port);
++	if (retval == -EBUSY) {
++		retval = 0;
++		goto unlock_unit;
++	}
++
++	/* FIXME: do more work here: shutdown adapter */
++	retval = zfcp_adapter_dequeue(adapter);
++	if (retval == -EBUSY)
++		retval = 0;
++
++unlock_unit:
++	write_unlock(&port->unit_list_lock);
++unlock_port:
++	write_unlock(&adapter->port_list_lock);
++unlock_adapter:
++	write_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++#endif
++
++
++
++/*
++ * function:	
++ *
++ * purpose:	called if an adapter failed,
++ *		initiates adapter recovery which is done
++ *		asynchronously
++ *
++ * returns:	0	- initiated action succesfully
++ *		<0	- failed to initiate action
++ */
++static int zfcp_erp_adapter_reopen_internal(
++		zfcp_adapter_t *adapter,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)adapter,
++		clear_mask);
++
++        debug_text_event(adapter->erp_dbf,5,"a_ro");
++        ZFCP_LOG_DEBUG(
++		"Reopen on the adapter with devno 0x%04x\n",
++		adapter->devno);
++
++	zfcp_erp_adapter_block(adapter, clear_mask);
++
++	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
++		ZFCP_LOG_DEBUG(
++			"skipped reopen on the failed adapter with devno 0x%04x\n",
++			adapter->devno);
++                debug_text_event(adapter->erp_dbf,5,"a_ro_f");
++		/* ensure propagation of failed status to new devices */
++		zfcp_erp_adapter_failed(adapter);
++		retval = -EIO;
++		goto out;
++	}
++	retval = zfcp_erp_action_enqueue(
++			ZFCP_ERP_ACTION_REOPEN_ADAPTER,
++			adapter,
++			NULL,
++			NULL);
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	Wrappper for zfcp_erp_adapter_reopen_internal
++ *              used to ensure the correct locking
++ *
++ * returns:	0	- initiated action succesfully
++ *		<0	- failed to initiate action
++ */
++static int zfcp_erp_adapter_reopen(
++		zfcp_adapter_t *adapter,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        unsigned long flags;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)adapter,
++		clear_mask);
++
++        write_lock_irqsave(&adapter->erp_lock, flags);
++        retval = zfcp_erp_adapter_reopen_internal(adapter, clear_mask);
++        write_unlock_irqrestore(&adapter->erp_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static inline int zfcp_erp_adapter_shutdown(zfcp_adapter_t* adapter, int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)adapter,
++		clear_mask);
++
++        retval=zfcp_erp_adapter_reopen(
++			adapter,
++			ZFCP_STATUS_COMMON_RUNNING |
++			ZFCP_STATUS_COMMON_ERP_FAILED |
++			clear_mask);	
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static inline int zfcp_erp_port_shutdown(zfcp_port_t* port, int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)port,
++		clear_mask);
++
++	retval = zfcp_erp_port_reopen(
++			port,
++			ZFCP_STATUS_COMMON_RUNNING |
++			ZFCP_STATUS_COMMON_ERP_FAILED |
++			clear_mask);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static inline int zfcp_erp_unit_shutdown(zfcp_unit_t* unit, int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++
++	ZFCP_LOG_TRACE(
++		"enter (unit=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)unit,
++		clear_mask);
++
++	retval = zfcp_erp_unit_reopen(
++			unit,
++			ZFCP_STATUS_COMMON_RUNNING |
++			ZFCP_STATUS_COMMON_ERP_FAILED |
++			clear_mask);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_els
++ *
++ * purpose:     Originator of the ELS commands
++ *
++ * returns:     0       - Operation completed successfuly
++ *              -EINVAL - Unknown IOCTL command or invalid sense data record
++ *              -ENOMEM - Insufficient memory
++ *              -EPERM  - Cannot create or queue FSF request
++ */
++static int zfcp_els(zfcp_port_t *port, u8 ls_code)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++
++	struct zfcp_send_els *send_els;
++	struct zfcp_ls_rls *rls;
++	struct zfcp_ls_pdisc *pdisc;
++	struct zfcp_ls_adisc *adisc;
++	void *page = NULL;
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx ls_code=0x%02x)\n",
++		(unsigned long)port, ls_code);
++
++	send_els = (struct zfcp_send_els*)ZFCP_KMALLOC(
++		sizeof(struct zfcp_send_els), GFP_ATOMIC);
++	if (send_els == NULL)
++		goto nomem;
++
++	send_els->req = (struct scatterlist*)ZFCP_KMALLOC(
++		sizeof(struct scatterlist), GFP_ATOMIC);
++	if (send_els->req == NULL)
++		goto nomem;
++	send_els->req_count = 1;
++
++	send_els->resp = (struct scatterlist*)ZFCP_KMALLOC(
++		sizeof(struct scatterlist), GFP_ATOMIC);
++	if (send_els->resp == NULL)
++		goto nomem;
++	send_els->resp_count = 1;
++
++	page = (void*)ZFCP_GET_ZEROED_PAGE(GFP_ATOMIC);
++	if (page == NULL)
++		goto nomem;
++	send_els->req->address = (char*)page;
++	send_els->resp->address = (char*)(page + (PAGE_SIZE >> 1));
++
++	send_els->port = port;
++	send_els->ls_code = ls_code;
++	send_els->handler = zfcp_els_handler;
++	send_els->handler_data = (unsigned long)send_els;
++
++	*(u32*)page = 0;
++	*(u8*)page = ls_code;
++
++	switch (ls_code) {
++
++	case ZFCP_LS_RTV:
++		send_els->req->length = sizeof(struct zfcp_ls_rtv);
++		send_els->resp->length = sizeof(struct zfcp_ls_rtv_acc);
++		ZFCP_LOG_NORMAL(
++			"RTV request from sid 0x%06x to did 0x%06x\n",
++			port->adapter->s_id, port->d_id);
++		break;
++
++	case ZFCP_LS_RLS:
++		send_els->req->length = sizeof(struct zfcp_ls_rls);
++		send_els->resp->length = sizeof(struct zfcp_ls_rls_acc);
++		rls = (struct zfcp_ls_rls*)send_els->req->address;
++		rls->port_id = port->adapter->s_id;
++		ZFCP_LOG_NORMAL(
++			"RLS request from sid 0x%06x to did 0x%06x "
++			"payload(port_id=0x%06x)\n",
++			port->adapter->s_id, port->d_id, rls->port_id);
++		break;
++
++	case ZFCP_LS_PDISC:
++		send_els->req->length = sizeof(struct zfcp_ls_pdisc);
++		send_els->resp->length = sizeof(struct zfcp_ls_pdisc_acc);
++		pdisc = (struct zfcp_ls_pdisc*)send_els->req->address;
++		pdisc->wwpn = port->adapter->wwpn;
++		pdisc->wwnn = port->adapter->wwnn;
++		ZFCP_LOG_NORMAL(
++			"PDISC request from sid 0x%06x to did 0x%06x "
++			"payload(wwpn=0x%016Lx wwnn=0x%016Lx)\n",
++			port->adapter->s_id, port->d_id,
++			(unsigned long long)pdisc->wwpn,
++			(unsigned long long)pdisc->wwnn);
++		break;
++
++	case ZFCP_LS_ADISC:
++		send_els->req->length = sizeof(struct zfcp_ls_adisc);
++		send_els->resp->length = sizeof(struct zfcp_ls_adisc_acc);
++		adisc = (struct zfcp_ls_adisc*)send_els->req->address;
++		adisc->hard_nport_id = port->adapter->s_id;
++		adisc->wwpn = port->adapter->wwpn;
++		adisc->wwnn = port->adapter->wwnn;
++		adisc->nport_id = port->adapter->s_id;
++		ZFCP_LOG_NORMAL(
++			"ADISC request from sid 0x%06x to did 0x%06x "
++			"payload(wwpn=0x%016Lx wwnn=0x%016Lx "
++			"hard_nport_id=0x%06x nport_id=0x%06x)\n",
++			port->adapter->s_id, port->d_id,
++			(unsigned long long)adisc->wwpn,
++			(unsigned long long)adisc->wwnn,
++			adisc->hard_nport_id, adisc->nport_id);
++		break;
++
++	default:
++		ZFCP_LOG_NORMAL(
++			"ELS command code 0x%02x is not supported\n", ls_code);
++		retval = -EINVAL;
++		goto invalid_ls_code;
++	}
++
++	retval = zfcp_fsf_send_els(send_els);
++	if (retval != 0) {
++		ZFCP_LOG_NORMAL(
++			"ELS request could not be processed "
++			"(sid=0x%06x did=0x%06x)\n",
++			port->adapter->s_id, port->d_id);
++		retval = -EPERM;
++	}
++
++	goto out;
++
++nomem:
++	ZFCP_LOG_INFO("Out of memory!\n");
++	retval = -ENOMEM;
++
++invalid_ls_code:
++	if (page != NULL)
++		ZFCP_FREE_PAGE((unsigned long)page);
++	if (send_els != NULL) {
++		if (send_els->req != NULL)
++			ZFCP_KFREE(send_els->req, sizeof(struct scatterlist));
++		if (send_els->resp != NULL)
++			ZFCP_KFREE(send_els->resp, sizeof(struct scatterlist));
++		ZFCP_KFREE(send_els, sizeof(struct zfcp_send_els));
++	}
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/**
++ * zfcp_els_handler - handler for ELS commands
++ * @data: pointer to struct zfcp_send_els
++ * If ELS failed (LS_RJT or timed out) forced reopen of the port is triggered.
++ */
++static void zfcp_els_handler(unsigned long data)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++
++	struct zfcp_send_els *send_els = (struct zfcp_send_els*)data;
++	zfcp_port_t *port = send_els->port;
++	zfcp_adapter_t *adapter = port->adapter;
++	u8 req_code = *(u8*)send_els->req->address;
++	struct zfcp_ls_rtv_acc *rtv;
++	struct zfcp_ls_rls_acc *rls;
++	struct zfcp_ls_pdisc_acc *pdisc;
++	struct zfcp_ls_adisc_acc *adisc;
++
++	ZFCP_LOG_TRACE("enter (data=0x%lx)\n", data);
++
++	/* request rejected or timed out */
++	if (send_els->status != 0) {
++		ZFCP_LOG_NORMAL("ELS request failed, force physical port "
++			"reopen (wwpn=0x%016Lx devno=0x%04x)\n",
++			(unsigned long long)port->wwpn, adapter->devno);
++		debug_text_event(adapter->erp_dbf, 3, "forcreop");
++		if (zfcp_erp_port_forced_reopen(port, 0))
++			ZFCP_LOG_NORMAL(
++				"Cannot reopen a remote port "
++				"(wwpn=0x%016Lx devno=0x%04x)\n",
++				(unsigned long long)port->wwpn,
++				adapter->devno);
++		goto out;
++	}
++
++        switch (req_code) {
++
++        case ZFCP_LS_RTV:
++                rtv = (struct zfcp_ls_rtv_acc*)send_els->resp->address;
++                ZFCP_LOG_NORMAL(
++				"RTV response from did 0x%06x to sid 0x%06x "
++				"with payload(R_A_TOV=%ds E_D_TOV=%d%cs)\n",
++				port->d_id, port->adapter->s_id,
++				rtv->r_a_tov, rtv->e_d_tov,
++				rtv->qualifier & ZFCP_LS_RTV_E_D_TOV_FLAG ?
++                                'n' : 'm');
++                break;
++
++        case ZFCP_LS_RLS:
++                rls = (struct zfcp_ls_rls_acc*)send_els->resp->address;
++                ZFCP_LOG_NORMAL(
++				"RLS response from did 0x%06x to sid 0x%06x "
++				"with payload(link_failure_count=%u "
++				"loss_of_sync_count=%u "
++				"loss_of_signal_count=%u "
++				"primitive_sequence_protocol_error=%u "
++				"invalid_transmition_word=%u "
++				"invalid_crc_count=%u)\n",
++				port->d_id, port->adapter->s_id,
++				rls->link_failure_count,
++				rls->loss_of_sync_count,
++				rls->loss_of_signal_count,
++				rls->prim_seq_prot_error,
++				rls->invalid_transmition_word,
++				rls->invalid_crc_count);
++                break;
++
++        case ZFCP_LS_PDISC:
++                pdisc = (struct zfcp_ls_pdisc_acc*)send_els->resp->address;
++                ZFCP_LOG_NORMAL(
++				"PDISC response from did 0x%06x to sid 0x%06x "
++				"with payload(wwpn=0x%016Lx wwnn=0x%016Lx "
++				"vendor='%-16s')\n",
++				port->d_id, port->adapter->s_id,
++				(unsigned long long)pdisc->wwpn,
++				(unsigned long long)pdisc->wwnn,
++				pdisc->vendor_version);
++                break;
++
++        case ZFCP_LS_ADISC:
++                adisc = (struct zfcp_ls_adisc_acc*)send_els->resp->address;
++                ZFCP_LOG_NORMAL(
++				"ADISC response from did 0x%06x to sid 0x%06x "
++				"with payload(wwpn=0x%016Lx wwnn=0x%016Lx "
++				"hard_nport_id=0x%06x nport_id=0x%06x)\n",
++				port->d_id, port->adapter->s_id,
++				(unsigned long long)adisc->wwpn,
++				(unsigned long long)adisc->wwnn,
++				adisc->hard_nport_id, adisc->nport_id);
++		if (port->wwpn != adisc->wwpn) {
++			ZFCP_LOG_NORMAL("d_id assignment changed, reopening "
++					"port (devno=0x%04x, wwpn=0x%016Lx, "
++					"adisc_resp_wwpn=0x%016Lx)\n",
++					adapter->devno,
++                                        (unsigned long long) port->wwpn,
++                                        (unsigned long long) adisc->wwpn);
++			if (zfcp_erp_port_reopen(port, 0))
++				ZFCP_LOG_NORMAL("failed reopen of port "
++						"(devno=0x%04x, "
++                                                "wwpn=0x%016Lx)\n",
++						adapter->devno,
++                                                (long long) port->wwpn);
++                } else
++			if (port->wwnn == 0)
++				port->wwnn = adisc->wwnn;
++
++                break;
++        }
++
++ out:
++	ZFCP_FREE_PAGE((unsigned long)send_els->req->address);
++	ZFCP_KFREE(send_els->req, sizeof(struct scatterlist));
++	ZFCP_KFREE(send_els->resp, sizeof(struct scatterlist));
++	ZFCP_KFREE(send_els, sizeof(struct zfcp_send_els));
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_test_link
++ *
++ * purpose:     Test a status of a link to a remote port using the ELS command ADISC
++ *
++ * returns:     0       - Link is OK
++ *              -EPERM  - Port forced reopen failed
++ */
++static int zfcp_test_link(zfcp_port_t *port)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_FSF
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_FSF
++
++	int retval;
++
++	ZFCP_LOG_TRACE("enter (port=0x%lx)\n", (unsigned long)port);
++
++	retval = zfcp_els(port, ZFCP_LS_ADISC);
++	if (retval != 0) {
++		ZFCP_LOG_NORMAL(
++			"Port needs to be reopened "
++			"(wwpn=0x%016Lx devno=0x%04x)\n",
++			(unsigned long long)port->wwpn,
++			port->adapter->devno);
++		retval = zfcp_erp_port_forced_reopen(port, 0);
++		if (retval != 0) {
++			ZFCP_LOG_NORMAL(
++				"Cannot reopen a remote port "
++				"(wwpn=0x%016Lx devno=0x%04x)\n",
++				(unsigned long long)port->wwpn,
++				port->adapter->devno);
++			retval = -EPERM;
++		}
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	called if a port failed to be opened normally
++ *		initiates Forced Reopen recovery which is done
++ *		asynchronously
++ *
++ * returns:	0	- initiated action succesfully
++ *		<0	- failed to initiate action
++ */
++static int zfcp_erp_port_forced_reopen_internal(
++		zfcp_port_t *port,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++	zfcp_adapter_t *adapter = port->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)port,
++		clear_mask);
++
++        debug_text_event(adapter->erp_dbf,5,"pf_ro");
++        debug_event(adapter->erp_dbf,5,&port->wwpn,
++                    sizeof(wwn_t));
++
++        ZFCP_LOG_DEBUG(
++		"Forced reopen of the port with WWPN 0x%016Lx "
++		"on the adapter with devno 0x%04x\n",
++		(llui_t)port->wwpn,
++		adapter->devno);
++
++	zfcp_erp_port_block(port, clear_mask);
++
++	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
++		ZFCP_LOG_DEBUG(
++			"skipped forced reopen on the failed port "
++			"with WWPN 0x%016Lx on the adapter with devno 0x%04x\n",
++			(llui_t)port->wwpn,
++			adapter->devno);
++                debug_text_event(adapter->erp_dbf,5,"pf_ro_f");
++                debug_event(adapter->erp_dbf,5,&port->wwpn,
++                            sizeof(wwn_t));
++		retval = -EIO;
++		goto out;
++	}
++
++	retval = zfcp_erp_action_enqueue(
++			ZFCP_ERP_ACTION_REOPEN_PORT_FORCED,
++			adapter,
++			port,
++			NULL);
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	Wrappper for zfcp_erp_port_forced_reopen_internal
++ *              used to ensure the correct locking
++ *
++ * returns:	0	- initiated action succesfully
++ *		<0	- failed to initiate action
++ */
++static int zfcp_erp_port_forced_reopen(
++		zfcp_port_t *port,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        unsigned long flags;
++        zfcp_adapter_t *adapter = port->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx clear_mask=x0%x)\n",
++		(unsigned long)port,
++		clear_mask);
++
++        write_lock_irqsave(&adapter->erp_lock, flags);
++        retval = zfcp_erp_port_forced_reopen_internal(port, clear_mask);
++        write_unlock_irqrestore(&adapter->erp_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	called if a port is to be opened
++ *		initiates Reopen recovery which is done
++ *		asynchronously
++ *
++ * returns:	0	- initiated action succesfully
++ *		<0	- failed to initiate action
++ */
++static int zfcp_erp_port_reopen_internal(
++		zfcp_port_t *port,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++	zfcp_adapter_t *adapter = port->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)port,
++		clear_mask);
++
++        debug_text_event(adapter->erp_dbf, 5, "p_ro");
++        debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++
++        ZFCP_LOG_DEBUG(
++		"Reopen of the port with WWPN 0x%016Lx "
++		"on the adapter with devno 0x%04x\n",
++		(llui_t)port->wwpn,
++		adapter->devno);
++
++	zfcp_erp_port_block(port, clear_mask);
++
++	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
++		ZFCP_LOG_DEBUG(
++			"skipped reopen on the failed port with WWPN 0x%016Lx "
++			"on the adapter with devno 0x%04x\n",
++			(llui_t)port->wwpn,
++			adapter->devno);
++                debug_text_event(adapter->erp_dbf, 5, "p_ro_f");
++                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++		/* ensure propagation of failed status to new devices */
++		zfcp_erp_port_failed(port);
++		retval = -EIO;
++		goto out;
++	}
++
++	retval = zfcp_erp_action_enqueue(
++			ZFCP_ERP_ACTION_REOPEN_PORT,
++			adapter,
++			port,
++			NULL);
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	Wrappper for zfcp_erp_port_reopen_internal
++ *              used to ensure the correct locking
++ *
++ * returns:	0	- initiated action succesfully
++ *		<0	- failed to initiate action
++ */
++static int zfcp_erp_port_reopen(
++		zfcp_port_t *port,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        unsigned long flags;
++        zfcp_adapter_t *adapter = port->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)port,
++		clear_mask);
++
++        write_lock_irqsave(&adapter->erp_lock, flags);
++        retval = zfcp_erp_port_reopen_internal(port, clear_mask);
++        write_unlock_irqrestore(&adapter->erp_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	called if a unit is to be opened
++ *		initiates Reopen recovery which is done
++ *		asynchronously
++ *
++ * returns:	0	- initiated action succesfully
++ *		<0	- failed to initiate action
++ */
++static int zfcp_erp_unit_reopen_internal(
++		zfcp_unit_t *unit,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++	zfcp_adapter_t *adapter = unit->port->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (unit=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)unit,
++		clear_mask);
++
++        debug_text_event(adapter->erp_dbf,5,"u_ro");
++        debug_event(adapter->erp_dbf,5,&unit->fcp_lun,
++                    sizeof(fcp_lun_t));
++        ZFCP_LOG_DEBUG(
++		"Reopen of the unit with FCP_LUN 0x%016Lx on the "
++		"port with WWPN 0x%016Lx "
++		"on the adapter with devno 0x%04x\n",
++		(llui_t)unit->fcp_lun,
++		(llui_t)unit->port->wwpn,
++		adapter->devno);
++
++	zfcp_erp_unit_block(unit, clear_mask);
++
++	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
++		ZFCP_LOG_DEBUG(
++			"skipped reopen on the failed unit with FCP_LUN 0x%016Lx on the "
++			"port with WWPN 0x%016Lx "
++			"on the adapter with devno 0x%04x\n",
++			(llui_t)unit->fcp_lun,
++			(llui_t)unit->port->wwpn,
++			adapter->devno);
++                debug_text_event(adapter->erp_dbf,5,"u_ro_f");
++                debug_event(adapter->erp_dbf,5,&unit->fcp_lun,
++                            sizeof(fcp_lun_t));
++		retval = -EIO;
++		goto out;
++	}
++
++	retval = zfcp_erp_action_enqueue(
++			ZFCP_ERP_ACTION_REOPEN_UNIT,
++			unit->port->adapter,
++			unit->port,
++			unit);
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	Wrappper for zfcp_erp_unit_reopen_internal
++ *              used to ensure the correct locking
++ *
++ * returns:	0	- initiated action succesfully
++ *		<0	- failed to initiate action
++ */
++static int zfcp_erp_unit_reopen(
++		zfcp_unit_t *unit,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        unsigned long flags;
++        zfcp_adapter_t *adapter = unit->port->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (unit=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)unit,
++		clear_mask);
++
++        write_lock_irqsave(&adapter->erp_lock, flags);
++        retval = zfcp_erp_unit_reopen_internal(unit, clear_mask);
++        write_unlock_irqrestore(&adapter->erp_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:	
++ *
++ * purpose:	disable I/O,
++ *		return any open requests and clean them up,
++ *		aim: no pending and incoming I/O
++ *
++ * returns:
++ */
++static int zfcp_erp_adapter_block(zfcp_adapter_t *adapter, int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)adapter,
++		clear_mask);
++
++        debug_text_event(adapter->erp_dbf,6,"a_bl");
++
++	zfcp_erp_modify_adapter_status(
++                   adapter,
++                   ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
++                   ZFCP_CLEAR);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	enable I/O
++ *
++ * returns:
++ */
++static int zfcp_erp_adapter_unblock(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        debug_text_event(adapter->erp_dbf,6,"a_ubl");
++	atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	disable I/O,
++ *		return any open requests and clean them up,
++ *		aim: no pending and incoming I/O
++ *
++ * returns:
++ */
++static int zfcp_erp_port_block(zfcp_port_t *port, int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++        zfcp_adapter_t *adapter=port->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)port,
++		clear_mask);
++
++        debug_text_event(adapter->erp_dbf,6,"p_bl");
++        debug_event(adapter->erp_dbf,6,&port->wwpn,
++                    sizeof(wwn_t));
++
++	zfcp_erp_modify_port_status(
++                   port,
++                   ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
++                   ZFCP_CLEAR);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	enable I/O
++ *
++ * returns:
++ */
++static int zfcp_erp_port_unblock(zfcp_port_t *port)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++        zfcp_adapter_t *adapter=port->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        debug_text_event(adapter->erp_dbf,6,"p_ubl");
++        debug_event(adapter->erp_dbf,6,&port->wwpn,
++                    sizeof(wwn_t));
++	atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	disable I/O,
++ *		return any open requests and clean them up,
++ *		aim: no pending and incoming I/O
++ *
++ * returns:
++ */
++static int zfcp_erp_unit_block(zfcp_unit_t *unit, int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++        zfcp_adapter_t *adapter=unit->port->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (unit=0x%lx clear_mask=0x%x\n",
++		(unsigned long)unit,
++		clear_mask);
++
++        debug_text_event(adapter->erp_dbf,6,"u_bl");
++        debug_event(adapter->erp_dbf,6,&unit->fcp_lun,
++                    sizeof(fcp_lun_t));
++
++	zfcp_erp_modify_unit_status(
++                   unit,
++                   ZFCP_STATUS_COMMON_UNBLOCKED | clear_mask,
++                   ZFCP_CLEAR);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	enable I/O
++ *
++ * returns:
++ */
++static int zfcp_erp_unit_unblock(zfcp_unit_t *unit)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++        zfcp_adapter_t *adapter=unit->port->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        debug_text_event(adapter->erp_dbf,6,"u_ubl");
++        debug_event(adapter->erp_dbf,6,&unit->fcp_lun,
++                    sizeof(fcp_lun_t));
++	atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_action_ready(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (erp_action=0x%lx)\n",
++		(unsigned long)erp_action);
++
++        debug_text_event(adapter->erp_dbf, 4, "a_ar");
++        debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof(int));
++
++	zfcp_erp_action_to_ready(erp_action);
++	ZFCP_LOG_DEBUG(
++		"Waking erp_thread of adapter with devno 0x%04x\n",
++		adapter->devno);
++	up(&adapter->erp_ready_sem);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:
++ *
++ * returns:	<0			erp_action not found in any list
++ *		ZFCP_ERP_ACTION_READY	erp_action is in ready list
++ *		ZFCP_ERP_ACTION_RUNNING	erp_action is in running list
++ *
++ * locks:	erp_lock must be held
++ */
++static int zfcp_erp_action_exists(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = -EINVAL;
++	struct list_head *entry;
++	zfcp_erp_action_t *entry_erp_action;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (erp_action=0x%lx)\n",
++		(unsigned long)erp_action);
++
++	/* search in running list */
++	list_for_each(entry, &adapter->erp_running_head) {
++		entry_erp_action = list_entry(entry, zfcp_erp_action_t, list);
++		if (entry_erp_action == erp_action) {
++			retval = ZFCP_ERP_ACTION_RUNNING;
++			goto out;
++		}
++	}
++	/* search in ready list */
++	list_for_each(entry, &adapter->erp_ready_head) {
++		entry_erp_action = list_entry(entry, zfcp_erp_action_t, list);
++		if (entry_erp_action == erp_action) {
++			retval = ZFCP_ERP_ACTION_READY;
++			goto out;
++		}
++	}
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * purpose:	checks current status of action (timed out, dismissed, ...)
++ *		and does appropriate preparations (dismiss fsf request, ...)
++ *
++ * locks:	called under erp_lock (disabled interrupts)
++ *
++ * returns:	0
++ */
++static int
++zfcp_erp_strategy_check_fsfreq(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	zfcp_fsf_req_t *fsf_req;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	if (erp_action->fsf_req) {
++		/* take lock to ensure that request is not being deleted meanwhile */
++		write_lock(&adapter->fsf_req_list_lock);
++		/* check whether fsf req does still exist */
++		list_for_each_entry(fsf_req, &adapter->fsf_req_list_head, list)
++		    if (fsf_req == erp_action->fsf_req)
++			break;
++		if (fsf_req == erp_action->fsf_req) {
++			/* fsf_req still exists */
++			debug_text_event(adapter->erp_dbf, 3, "a_ca_req");
++			debug_event(adapter->erp_dbf, 3, &fsf_req,
++				    sizeof (unsigned long));
++			/* dismiss fsf_req of timed out or dismissed erp_action */
++			if (erp_action->status & (ZFCP_STATUS_ERP_DISMISSED |
++						  ZFCP_STATUS_ERP_TIMEDOUT)) {
++				debug_text_event(adapter->erp_dbf, 3,
++						 "a_ca_disreq");
++				fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
++			}
++			if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
++				ZFCP_LOG_NORMAL("error: erp step timed out "
++						"(action=%d, fsf_req=%p)\n ",
++						erp_action->action,
++						erp_action->fsf_req);
++			}
++			/*
++			 * If fsf_req is neither dismissed nor completed
++			 * then keep it running asynchronously and don't mess
++			 * with the association of erp_action and fsf_req.
++			 */
++			if (fsf_req->status & (ZFCP_STATUS_FSFREQ_COMPLETED |
++					       ZFCP_STATUS_FSFREQ_DISMISSED)) {
++				/* forget about association between fsf_req
++				   and erp_action */
++				fsf_req->erp_action = NULL;
++				erp_action->fsf_req = NULL;
++			}
++		} else {
++			debug_text_event(adapter->erp_dbf, 3, "a_ca_gonereq");
++			/*
++			 * even if this fsf_req has gone, forget about
++			 * association between erp_action and fsf_req
++			 */
++			erp_action->fsf_req = NULL;
++		}
++		write_unlock(&adapter->fsf_req_list_lock);
++	} else
++		debug_text_event(adapter->erp_dbf, 3, "a_ca_noreq");
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * purpose:	generic handler for asynchronous events related to erp_action events
++ *		(normal completion, time-out, dismissing, retry after
++ *		low memory condition)
++ *
++ * note:	deletion of timer is not required (e.g. in case of a time-out),
++ *		but a second try does no harm,
++ *		we leave it in here to allow for greater simplification
++ *
++ * returns:	0 - there was an action to handle
++ *		!0 - otherwise
++ */
++static int
++zfcp_erp_async_handler_nolock(zfcp_erp_action_t *erp_action,
++			      unsigned long set_mask)
++{
++	int retval;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) {
++		debug_text_event(adapter->erp_dbf, 2, "a_asyh_ex");
++		debug_event(adapter->erp_dbf, 2, &erp_action->action,
++			    sizeof (int));
++		if (!(set_mask & ZFCP_STATUS_ERP_TIMEDOUT))
++			del_timer_sync(&erp_action->timer);
++		erp_action->status |= set_mask;
++		zfcp_erp_action_ready(erp_action);
++		retval = 0;
++	} else {
++		/* action is ready or gone - nothing to do */
++		debug_text_event(adapter->erp_dbf, 3, "a_asyh_gone");
++		debug_event(adapter->erp_dbf, 3, &erp_action->action,
++			    sizeof (int));
++		retval = 1;
++	}
++
++	return retval;
++}
++
++/*
++ * purpose:	generic handler for asynchronous events related to erp_action
++ *               events	(normal completion, time-out, dismissing, retry after
++ *		low memory condition)
++ *
++ * note:	deletion of timer is not required (e.g. in case of a time-out),
++ *		but a second try does no harm,
++ *		we leave it in here to allow for greater simplification
++ *
++ * returns:	0 - there was an action to handle
++ *		!0 - otherwise
++ */
++static int
++zfcp_erp_async_handler(zfcp_erp_action_t *erp_action,
++		       unsigned long set_mask)
++{
++	zfcp_adapter_t *adapter = erp_action->adapter;
++	unsigned long flags;
++	int retval;
++
++	write_lock_irqsave(&adapter->erp_lock, flags);
++	retval = zfcp_erp_async_handler_nolock(erp_action, set_mask);
++	write_unlock_irqrestore(&adapter->erp_lock, flags);
++
++	return retval;
++}
++
++/*
++ * purpose:	is called for erp_action which was slept waiting for
++ *		memory becoming avaliable,
++ *		will trigger that this action will be continued
++ */
++static void
++zfcp_erp_memwait_handler(unsigned long data)
++{
++	zfcp_erp_action_t *erp_action = (zfcp_erp_action_t *) data;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	debug_text_event(adapter->erp_dbf, 2, "a_mwh");
++	debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
++
++	zfcp_erp_async_handler(erp_action, 0);
++}
++
++/*
++ * purpose:	is called if an asynchronous erp step timed out,
++ *		action gets an appropriate flag and will be processed
++ *		accordingly
++ */
++static void
++zfcp_erp_timeout_handler(unsigned long data)
++{
++	zfcp_erp_action_t *erp_action = (zfcp_erp_action_t *) data;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	debug_text_event(adapter->erp_dbf, 2, "a_th");
++	debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
++
++	zfcp_erp_async_handler(erp_action, ZFCP_STATUS_ERP_TIMEDOUT);
++}
++
++/*
++ * purpose:	is called for an erp_action which needs to be ended
++ *		though not being done,
++ *		this is usually required if an higher is generated,
++ *		action gets an appropriate flag and will be processed
++ *		accordingly
++ *
++ * locks:	erp_lock held (thus we need to call another handler variant)
++ */
++static int
++zfcp_erp_action_dismiss(zfcp_erp_action_t *erp_action)
++{
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	debug_text_event(adapter->erp_dbf, 2, "a_adis");
++	debug_event(adapter->erp_dbf, 2, &erp_action->action, sizeof (int));
++
++	zfcp_erp_async_handler_nolock(erp_action, ZFCP_STATUS_ERP_DISMISSED);
++
++	return 0;
++}
++
++
++static int zfcp_erp_thread_setup(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++        int retval = 0;
++        struct tq_struct *erp_task = NULL;
++
++	ZFCP_LOG_TRACE(
++               "enter (adapter=0x%lx)\n",
++               (unsigned long)adapter);
++
++        erp_task = ZFCP_KMALLOC(sizeof(struct tq_struct), GFP_KERNEL); 
++        if (!erp_task) {
++                ZFCP_LOG_INFO(
++			"error: Not enough memory for the error handler "
++			"of the adapter with devno 0x%04x, leaving.\n",
++			adapter->devno);
++                retval = -ENOMEM;
++                goto out;
++        }
++
++	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_DONE, &adapter->status);
++
++	rwlock_init(&adapter->erp_lock);
++	INIT_LIST_HEAD(&adapter->erp_ready_head);
++	INIT_LIST_HEAD(&adapter->erp_running_head);
++	sema_init(&adapter->erp_ready_sem, 0);
++#ifdef ZFCP_ERP_DEBUG_SINGLE_STEP
++	sema_init(&adapter->erp_continue_sem, 0);
++#endif // ZFCP_ERP_DEBUG_SINGLE_STEP
++
++        INIT_TQUEUE(erp_task, 
++                    zfcp_erp_thread_setup_task, 
++                    adapter); 
++        schedule_task(erp_task);
++
++	__wait_event_interruptible(
++		adapter->erp_thread_wqh,
++		atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_DONE, &adapter->status),
++		retval);
++
++	if (retval) {
++		ZFCP_LOG_INFO(
++			"error: The error recovery procedure thread creation "
++			"for the adapter with devno 0x%04x was aborted. An "
++                        "OS signal was receiveved.\n",
++			adapter->devno);
++		if (atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, 
++                                     &adapter->status)) {
++			zfcp_erp_thread_kill(adapter);
++                }
++	}
++
++	ZFCP_KFREE(erp_task, sizeof(*erp_task));
++ 
++        debug_text_event(adapter->erp_dbf, 5, "a_thset_ok");
++out:
++	ZFCP_LOG_TRACE("exit %d\n", retval);
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++static void zfcp_erp_thread_setup_task(void *data)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	zfcp_adapter_t *adapter = (zfcp_adapter_t *)data;
++	int retval;
++
++	ZFCP_LOG_TRACE(
++               "enter (data=0x%lx)\n",
++               (unsigned long)data);
++
++	retval = kernel_thread(zfcp_erp_thread, adapter, SIGCHLD);
++	if (retval < 0) {
++		ZFCP_LOG_INFO(
++			"error: Out of resources. Could not create an "
++                        "error recovery procedure  thread "
++			"for the adapter with devno 0x%04x\n",
++			adapter->devno);
++		atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_DONE, &adapter->status);
++		wake_up_interruptible(&adapter->erp_thread_wqh);
++	} else	{
++		ZFCP_LOG_DEBUG(
++			"created erp thread "
++			"for the adapter with devno 0x%04x\n",
++			adapter->devno);
++                debug_text_event(adapter->erp_dbf,5,"a_thset_t_ok");
++	}
++		
++	ZFCP_LOG_TRACE("exit\n");
++
++	return;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ *
++ * context:	process (i.e. proc-fs or rmmod/insmod)
++ *
++ * note:	The caller of this routine ensures that the specified
++ *		adapter has been shut down and that this operation
++ *		has been completed. Thus, there are no pending erp_actions
++ *		which would need to be handled here.
++ */
++static int zfcp_erp_thread_kill(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++               "enter (adapter=0x%lx)\n",
++               (unsigned long)adapter);
++
++	ZFCP_LOG_DEBUG(
++		"Killing erp_thread for the adapter with devno 0x%04x\n",
++		adapter->devno);
++	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
++	up(&adapter->erp_ready_sem);
++	wait_event(
++		adapter->erp_thread_wqh,
++		!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status));
++	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
++
++        debug_text_event(adapter->erp_dbf,5,"a_thki_ok");
++
++	ZFCP_LOG_DEBUG(
++		"Killed erp_thread for the adapter with devno 0x%04x\n",
++		adapter->devno);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	should be run as a thread,
++ *		goes through list of error recovery actions of associated adapter
++ *		and delegates single action to execution
++ *
++ * returns:
++ */
++/* FIXME(design): static or not? */
++static int zfcp_erp_thread(void *data)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	zfcp_adapter_t *adapter = (zfcp_adapter_t*)data;
++	struct list_head *next;
++	zfcp_erp_action_t *erp_action;
++	unsigned long flags;
++
++	ZFCP_LOG_TRACE(
++		"enter (data=0x%lx)\n",
++		(unsigned long)data);
++
++	__sti();
++	daemonize();
++        /* disable all signals */
++	siginitsetinv(&current->blocked, 0);
++
++	sprintf(current->comm, "zfcp_erp_0x%04x", adapter->devno);
++
++	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
++	ZFCP_LOG_DEBUG(
++		"erp thread for adapter with devno 0x%04x is up.\n",
++		adapter->devno);
++        debug_text_event(adapter->erp_dbf, 5, "a_th_run");
++	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_DONE, &adapter->status);
++	wake_up_interruptible(&adapter->erp_thread_wqh);
++
++	/* (nearly) infinite loop */
++	for (;;) {
++		/* sleep as long as there is no action in 'ready' queue */
++		down_interruptible(&adapter->erp_ready_sem);
++#ifdef ZFCP_ERP_DEBUG_SINGLE_STEP
++		down(&adapter->erp_continue_sem);
++#endif // ZFCP_ERP_DEBUG_SINGLE_STEP
++		ZFCP_LOG_TRACE(
++			"erp thread woken on adapter with devno 0x%04x\n",
++			adapter->devno);
++
++		/* killing this thread */
++		if (atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status)) {
++                        debug_text_event(adapter->erp_dbf,5,"a_th_kill");
++			ZFCP_LOG_DEBUG(
++				"Recognized kill flag for the erp_thread of "
++				"the adapter with devno 0x%04x\n",
++				adapter->devno);
++			read_lock_irqsave(&adapter->erp_lock, flags);
++			retval = !list_empty(&adapter->erp_ready_head) ||
++				 !list_empty(&adapter->erp_running_head);
++			read_unlock_irqrestore(&adapter->erp_lock, flags);
++			if (retval) {
++				debug_text_exception(adapter->erp_dbf, 1, "a_th_bkill");
++				ZFCP_LOG_NORMAL(
++					"bug: error recovery thread is "
++					"shutting down although there are "
++					"error recovery actions pending at "
++					"adapter with devno 0x%04x\n",
++					adapter->devno);
++				/* don't exit erp to avoid potential system crash */
++			} else	break;
++		}
++
++		ZFCP_PARANOIA {
++			/* there should be something in 'ready' queue */
++			/*
++			 * need lock since list_empty checks for entry at
++			 * lists head while lists head is subject to
++			 * modification when another action is put to this
++			 * queue (only list tail won't be modified then)
++			 */
++			read_lock_irqsave(&adapter->erp_lock, flags);
++			retval = list_empty(&adapter->erp_ready_head);
++			read_unlock_irqrestore(&adapter->erp_lock, flags);
++			if (retval) {
++                                debug_text_exception(adapter->erp_dbf, 1, "a_th_empt");
++				ZFCP_LOG_NORMAL(
++					"bug: Error recovery procedure thread "
++                                        "woken for empty action list on the "
++					"adapter with devno 0x%04x.\n",
++					adapter->devno);
++				/* sleep until next try */
++				continue;
++			}
++		}
++
++		/*
++		 * get next action to be executed; FIFO -> head.prev
++		 * (don't need lock since there is an action at lists tail and
++		 * lists tail won't be modified concurrently; only lists head
++		 * would be modified if another action is put to this queue)
++		 */
++		next = adapter->erp_ready_head.prev;
++		erp_action = list_entry(next, zfcp_erp_action_t, list);
++		/* process action (incl. [re]moving it from 'ready' queue) */
++		retval = zfcp_erp_strategy(erp_action);
++	}
++
++	atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
++	ZFCP_LOG_DEBUG(
++		"erp thread for adapter with devno 0x%04x is down.\n",
++		adapter->devno);
++
++	wake_up(&adapter->erp_thread_wqh);
++
++        debug_text_event(adapter->erp_dbf, 5, "a_th_stop");
++
++	ZFCP_LOG_TRACE("exit %d\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	drives single error recovery action and schedules higher and
++ *		subordinate actions, if necessary
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
++ *		ZFCP_ERP_SUCCEEDED	- action finished successfully (action dequeued)
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully (action dequeued)
++ *		ZFCP_ERP_EXIT		- action finished (action dequeued), target offline
++ *		ZFCP_ERP_DISMISSED	- action canceled (action dequeued)
++ */
++static int zfcp_erp_strategy(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++	zfcp_port_t *port = erp_action->port;
++	zfcp_unit_t *unit = erp_action->unit;
++	int action = erp_action->action;
++	u32 status = erp_action->status;
++	unsigned long flags;
++
++	ZFCP_LOG_TRACE(
++		"enter (erp_action=0x%lx)\n",
++		(unsigned long)erp_action);
++
++	write_lock_irqsave(&adapter->erp_lock, flags);
++	/* dequeue dismissed action and leave, if required */
++	retval = zfcp_erp_strategy_check_action(erp_action, retval);
++	if (retval == ZFCP_ERP_DISMISSED) {
++                debug_text_event(adapter->erp_dbf, 4, "a_st_dis1");
++		goto unlock;
++        }
++
++	/*
++	 * move action to 'running' queue before processing it
++	 * (to avoid a race condition regarding moving the
++	 * action to the 'running' queue and back)
++	 */
++	zfcp_erp_action_to_running(erp_action);
++
++	write_unlock_irqrestore(&adapter->erp_lock, flags);
++	retval = zfcp_erp_strategy_do_action(erp_action);
++	write_lock_irqsave(&adapter->erp_lock, flags);
++
++	/*
++	 * check for dismissed status again to avoid follow-up actions,
++	 * failing of targets and so on for dismissed actions
++	 */
++	retval = zfcp_erp_strategy_check_action(erp_action, retval);
++
++	switch (retval) {
++	case ZFCP_ERP_DISMISSED:
++		/* leave since this action has ridden to its ancestors */
++		debug_text_event(adapter->erp_dbf, 6, "a_st_dis2");
++		goto unlock;
++	case ZFCP_ERP_NOMEM :
++		/* no memory to continue immediately, let it sleep */
++		debug_text_event(adapter->erp_dbf, 2, "a_st_memw");
++		retval = zfcp_erp_strategy_memwait(erp_action);
++		goto unlock;
++	case ZFCP_ERP_CONTINUES :
++		/* leave since this action runs asynchronously */
++		debug_text_event(adapter->erp_dbf, 6, "a_st_cont");
++		goto unlock;
++        }
++
++	/* ok, finished action (whatever its result is) */
++
++	/* check for unrecoverable targets */
++	retval = zfcp_erp_strategy_check_target(erp_action, retval);
++
++	/* action must be dequeued (here to allow for further ones) */
++	zfcp_erp_action_dequeue(erp_action);
++
++	/*
++	 * put this target through the erp mill again if someone has
++	 * requested to change the status of a target being online 
++	 * to offline or the other way around
++	 * (old retval is preserved if nothing has to be done here)
++	 */
++	retval = zfcp_erp_strategy_statechange(
++			action, status, adapter, port, unit, retval);
++
++	/*
++	 * leave if target is in permanent error state or if
++	 * action is repeated in order to process state change
++	 */
++	if (retval == ZFCP_ERP_EXIT) {
++                debug_text_event(adapter->erp_dbf, 2, "a_st_exit");
++                goto unlock;
++        }
++
++	/* trigger follow up actions */
++	zfcp_erp_strategy_followup_actions(
++		action, adapter, port, unit, retval);
++
++unlock:
++	write_unlock_irqrestore(&adapter->erp_lock, flags);
++
++	/*
++	 * 2 things we want to check finally if the erp queues will be empty
++	 * (don't do that if the last action evaluated was dismissed
++	 * since this clearly indicates that there is more to come) :
++	 * - close the name server port if it is open yet (enqueues another final action)
++	 * - otherwise, wake up whoever wants to be woken when we are done with erp
++	 */
++	if (retval != ZFCP_ERP_DISMISSED)
++		zfcp_erp_strategy_check_queues(adapter);
++
++        debug_text_event(adapter->erp_dbf, 6, "a_st_done");
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:	ZFCP_ERP_DISMISSED	- if action has been dismissed
++ *		retval			- otherwise
++ */
++static int
++zfcp_erp_strategy_check_action(zfcp_erp_action_t *erp_action, int retval)
++{
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	zfcp_erp_strategy_check_fsfreq(erp_action);
++
++	debug_event(adapter->erp_dbf, 5, &erp_action->action, sizeof (int));
++	if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
++		debug_text_event(adapter->erp_dbf, 3, "a_stcd_dis");
++		zfcp_erp_action_dequeue(erp_action);
++		retval = ZFCP_ERP_DISMISSED;
++	} else
++		debug_text_event(adapter->erp_dbf, 5, "a_stcd_nodis");
++
++	return retval;
++}
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_strategy_do_action(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = ZFCP_ERP_FAILED;
++        zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (erp_action=0x%lx)\n",
++		(unsigned long)erp_action);
++	/*
++	 * no lock in subsequent stratetgy routines
++	 * (this allows these routine to call schedule, e.g.
++	 * kmalloc with such flags or qdio_initialize & friends)
++	 */
++
++	if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
++		/* DEBUG */
++		//unsigned long timeout = 1000 * HZ;
++       
++		debug_text_event(adapter->erp_dbf, 3, "a_stda_tim");
++		debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof(int));
++	
++		/* DEBUG */
++		//__ZFCP_WAIT_EVENT_TIMEOUT(timeout, 0);
++        }
++        /* Note: in case of timeout, the seperate strategies will fail
++           anyhow. No need for a special action. Even worse, a nameserver
++           failure would not wake up waiting ports without the call.
++        */ 
++        /* try to execute/continue action as far as possible */
++        switch (erp_action->action) {
++        case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
++                retval = zfcp_erp_adapter_strategy(erp_action);
++                break;
++        case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
++                retval = zfcp_erp_port_forced_strategy(erp_action);
++                break;
++        case ZFCP_ERP_ACTION_REOPEN_PORT :
++                retval = zfcp_erp_port_strategy(erp_action);
++                break;
++        case ZFCP_ERP_ACTION_REOPEN_UNIT :
++                retval = zfcp_erp_unit_strategy(erp_action);
++                break;
++        default :
++                debug_text_exception(adapter->erp_dbf, 1, "a_stda_bug");
++                debug_event(adapter->erp_dbf, 1, &erp_action->action, sizeof(int));
++                ZFCP_LOG_NORMAL("bug: Unknown error recovery procedure "
++                                "action requested on the adapter with "
++                                "devno 0x%04x (debug info %d)\n",
++                                erp_action->adapter->devno,
++                                erp_action->action);
++        }
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	triggers retry of this action after a certain amount of time
++ *		by means of timer provided by erp_action
++ *
++ * returns:	ZFCP_ERP_CONTINUES - erp_action sleeps in erp running queue
++ */
++static int zfcp_erp_strategy_memwait(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = ZFCP_ERP_CONTINUES;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (erp_action=0x%lx)\n",
++		(unsigned long)erp_action);
++
++        debug_text_event(adapter->erp_dbf, 6, "a_mwinit");
++        debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof(int));
++	init_timer(&erp_action->timer);
++	erp_action->timer.function = zfcp_erp_memwait_handler;
++	erp_action->timer.data = (unsigned long)erp_action;
++	erp_action->timer.expires = jiffies + ZFCP_ERP_MEMWAIT_TIMEOUT;
++	add_timer(&erp_action->timer);
++	
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/* 
++ * function:    zfcp_erp_adapter_failed
++ *
++ * purpose:     sets the adapter and all underlying devices to ERP_FAILED
++ *
++ */
++static void zfcp_erp_adapter_failed(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
++
++
++        ZFCP_LOG_TRACE(
++                "enter (adapter=0x%lx)\n",
++                (unsigned long)adapter);
++
++        zfcp_erp_modify_adapter_status(adapter,
++                                       ZFCP_STATUS_COMMON_ERP_FAILED,
++                                       ZFCP_SET);
++        ZFCP_LOG_NORMAL(
++                "Adapter recovery failed on the "
++                "adapter with devno 0x%04x.\n",
++                adapter->devno);
++        debug_text_event(adapter->erp_dbf, 2, "a_afail");
++
++
++        ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/* 
++ * function:    zfcp_erp_port_failed
++ *
++ * purpose:     sets the port and all underlying devices to ERP_FAILED
++ *
++ */
++static void zfcp_erp_port_failed(zfcp_port_t *port)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
++
++
++        ZFCP_LOG_TRACE("enter (port=0x%lx)\n",
++                       (unsigned long)port);
++        
++        zfcp_erp_modify_port_status(port,
++                                    ZFCP_STATUS_COMMON_ERP_FAILED,
++                                    ZFCP_SET);
++        ZFCP_LOG_NORMAL("Port recovery failed on the "
++                        "port with WWPN 0x%016Lx at the "
++                        "adapter with devno 0x%04x.\n",
++                        (llui_t)port->wwpn,
++                        port->adapter->devno);
++        debug_text_event(port->adapter->erp_dbf, 2, "p_pfail");
++        debug_event(port->adapter->erp_dbf, 2, &port->wwpn, sizeof(wwn_t));
++        
++        ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/* 
++ * function:    zfcp_erp_unit_failed
++ *
++ * purpose:     sets the unit to ERP_FAILED
++ *
++ */
++static void zfcp_erp_unit_failed(zfcp_unit_t *unit)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
++
++
++        ZFCP_LOG_TRACE(
++                "enter (unit=0x%lx)\n",
++                (unsigned long)unit);
++
++        zfcp_erp_modify_unit_status(unit,
++                                    ZFCP_STATUS_COMMON_ERP_FAILED,
++                                    ZFCP_SET);
++        ZFCP_LOG_NORMAL(
++                "Unit recovery failed on the unit with FCP_LUN 0x%016Lx "
++                "connected to the port with WWPN 0x%016Lx at the "
++                "adapter with devno 0x%04x.\n",
++                (llui_t)unit->fcp_lun,
++                (llui_t)unit->port->wwpn,
++                unit->port->adapter->devno);
++        debug_text_event(unit->port->adapter->erp_dbf, 2, "u_ufail");
++        debug_event(unit->port->adapter->erp_dbf, 2, 
++                    &unit->fcp_lun, sizeof(fcp_lun_t));
++
++        ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_erp_strategy_check_target
++ *
++ * purpose:	increments the erp action count on the device currently in recovery if
++ *              the action failed or resets the count in case of success. If a maximum
++ *              count is exceeded the device is marked as ERP_FAILED.
++ *		The 'blocked' state of a target which has been recovered successfully is reset.
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (not considered)
++ *		ZFCP_ERP_SUCCEEDED	- action finished successfully 
++ *		ZFCP_ERP_EXIT		- action failed and will not continue
++ */
++static int zfcp_erp_strategy_check_target(
++		zfcp_erp_action_t *erp_action, 
++		int result)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	zfcp_adapter_t *adapter = erp_action->adapter;
++	zfcp_port_t *port = erp_action->port;
++	zfcp_unit_t *unit = erp_action->unit;
++
++        ZFCP_LOG_TRACE(
++		"enter (erp_action=0x%lx, result=%d)\n",
++		(unsigned long)erp_action,
++		result);
++        
++        debug_text_event(adapter->erp_dbf, 5, "a_stct_norm");
++        debug_event(adapter->erp_dbf, 5, &erp_action->action, sizeof(int));
++        debug_event(adapter->erp_dbf, 5, &result, sizeof(int));
++
++	switch (erp_action->action) {
++
++		case ZFCP_ERP_ACTION_REOPEN_UNIT :
++			result = zfcp_erp_strategy_check_unit(unit, result);
++			break;
++
++		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
++		case ZFCP_ERP_ACTION_REOPEN_PORT :
++			result = zfcp_erp_strategy_check_port(port, result);
++			break;
++
++		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
++			result = zfcp_erp_strategy_check_adapter(adapter, result);
++			break;
++	}
++        
++	ZFCP_LOG_TRACE("exit (%d)\n", result);
++        
++	return result;
++        
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_strategy_statechange(
++		int action,
++		u32 status,
++                zfcp_adapter_t *adapter,
++                zfcp_port_t *port,
++                zfcp_unit_t *unit,
++                int retval)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	ZFCP_LOG_TRACE(
++		"enter (action=%d status=0x%x adapter=0x%lx port=0x%lx "
++		"unit=0x%lx retval=0x%x)\n",
++		action,
++		status,
++		(unsigned long)adapter,
++		(unsigned long)port,
++		(unsigned long)unit,
++		retval);
++        debug_text_event(adapter->erp_dbf, 5, "a_stsc");
++        debug_event(adapter->erp_dbf, 5, &action, sizeof(int));
++
++	switch (action) {
++
++		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
++			if (zfcp_erp_strategy_statechange_detected(&adapter->status, status)) {
++				zfcp_erp_adapter_reopen_internal(
++					adapter,
++					ZFCP_STATUS_COMMON_ERP_FAILED);
++				retval = ZFCP_ERP_EXIT;
++			}
++			break;
++
++		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
++		case ZFCP_ERP_ACTION_REOPEN_PORT :
++			if (zfcp_erp_strategy_statechange_detected(&port->status, status)) {
++				zfcp_erp_port_reopen_internal(
++					port,
++					ZFCP_STATUS_COMMON_ERP_FAILED);
++				retval = ZFCP_ERP_EXIT;
++			}
++			break;
++
++		case ZFCP_ERP_ACTION_REOPEN_UNIT :
++			if (zfcp_erp_strategy_statechange_detected(&unit->status, status)) {
++				zfcp_erp_unit_reopen_internal(
++					unit,
++					ZFCP_STATUS_COMMON_ERP_FAILED);
++				retval = ZFCP_ERP_EXIT;
++			}
++			break;
++
++		default :
++			debug_text_exception(adapter->erp_dbf, 1, "a_stsc_bug");
++			debug_event(adapter->erp_dbf, 1, &action, sizeof(int));
++			ZFCP_LOG_NORMAL(
++				"bug: Unknown error recovery procedure "
++				"action requested on the adapter with "
++				"devno 0x%04x (debug info %d)\n",
++				adapter->devno,
++				action);
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static inline int zfcp_erp_strategy_statechange_detected(atomic_t *target_status, u32 erp_status)
++{
++	return
++		/* take it online */
++		(atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
++		 (ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status)) ||
++		/* take it offline */
++		(!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, target_status) &&
++		 !(ZFCP_STATUS_ERP_CLOSE_ONLY & erp_status));
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_strategy_check_unit(zfcp_unit_t *unit, int result)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	ZFCP_LOG_TRACE(
++		"enter (unit=0x%lx result=%d)\n",
++		(unsigned long)unit,
++		result);
++
++	debug_text_event(unit->port->adapter->erp_dbf, 5, "u_stct");
++	debug_event(unit->port->adapter->erp_dbf, 5, &unit->fcp_lun, sizeof(fcp_lun_t));
++
++	switch (result) {
++	case ZFCP_ERP_SUCCEEDED :
++		atomic_set(&unit->erp_counter, 0);
++		zfcp_erp_unit_unblock(unit);
++		break;
++	case ZFCP_ERP_FAILED :
++		atomic_inc(&unit->erp_counter);
++		if (atomic_read(&unit->erp_counter) > ZFCP_MAX_ERPS)
++			zfcp_erp_unit_failed(unit);
++		break;
++	case ZFCP_ERP_EXIT :
++		/* nothing */
++		break;
++	}
++
++	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &unit->status)) {
++		zfcp_erp_unit_block(unit, 0); /* for ZFCP_ERP_SUCCEEDED */
++		result = ZFCP_ERP_EXIT;
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", result);
++
++	return result;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_strategy_check_port(zfcp_port_t *port, int result)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx result=%d\n",
++		(unsigned long)port,
++		result);
++
++	debug_text_event(port->adapter->erp_dbf, 5, "p_stct");
++	debug_event(port->adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++
++	switch (result) {
++	case ZFCP_ERP_SUCCEEDED :
++		atomic_set(&port->erp_counter, 0);
++		zfcp_erp_port_unblock(port);
++		break;
++	case ZFCP_ERP_FAILED :
++		atomic_inc(&port->erp_counter);
++		if (atomic_read(&port->erp_counter) > ZFCP_MAX_ERPS)
++			zfcp_erp_port_failed(port);
++		break;
++	case ZFCP_ERP_EXIT :
++		/* nothing */
++		break;
++	}
++
++	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &port->status)) {
++		zfcp_erp_port_block(port, 0); /* for ZFCP_ERP_SUCCEEDED */
++		result = ZFCP_ERP_EXIT;
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", result);
++
++	return result;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_strategy_check_adapter(zfcp_adapter_t *adapter, int result)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx result=%d)\n",
++		(unsigned long)adapter,
++		result);
++
++	debug_text_event(adapter->erp_dbf, 5, "a_stct");
++
++	switch (result) {
++	case ZFCP_ERP_SUCCEEDED :
++		atomic_set(&adapter->erp_counter, 0);
++		zfcp_erp_adapter_unblock(adapter);
++		break;
++	case ZFCP_ERP_FAILED :
++		atomic_inc(&adapter->erp_counter);
++		if (atomic_read(&adapter->erp_counter) > ZFCP_MAX_ERPS)
++			zfcp_erp_adapter_failed(adapter);
++		break;
++	case ZFCP_ERP_EXIT :
++		/* nothing */
++		break;
++	}
++
++	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
++		zfcp_erp_adapter_block(adapter, 0); /* for ZFCP_ERP_SUCCEEDED */
++		result = ZFCP_ERP_EXIT;
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", result);
++
++	return result;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	remaining things in good cases,
++ *		escalation in bad cases
++ *
++ * returns:
++ */
++static int zfcp_erp_strategy_followup_actions(
++		int action,
++		zfcp_adapter_t *adapter,
++		zfcp_port_t *port,
++		zfcp_unit_t *unit,
++		int status)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	ZFCP_LOG_TRACE(
++		"enter (action=%d adapter=0x%lx port=0x%lx "
++		"unit=0x%lx status=0x%x)\n",
++		action,
++		(unsigned long)adapter,
++		(unsigned long)port,
++		(unsigned long)unit,
++		status);
++        debug_text_event(adapter->erp_dbf, 5, "a_stfol");
++        debug_event(adapter->erp_dbf, 5, &action, sizeof(int));
++
++	/* initiate follow-up actions depending on success of finished action */
++	switch (action) {
++
++		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
++			if (status == ZFCP_ERP_SUCCEEDED)
++				zfcp_erp_port_reopen_all_internal(adapter, 0);
++			else	zfcp_erp_adapter_reopen_internal(adapter, 0);
++			break;
++
++		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
++			if (status == ZFCP_ERP_SUCCEEDED)
++				zfcp_erp_port_reopen_internal(port, 0);
++			else	zfcp_erp_adapter_reopen_internal(adapter, 0);
++			break;
++
++		case ZFCP_ERP_ACTION_REOPEN_PORT :
++			if (status == ZFCP_ERP_SUCCEEDED)
++				zfcp_erp_unit_reopen_all_internal(port, 0);
++			else	zfcp_erp_port_forced_reopen_internal(port, 0);
++			break;
++		
++		case ZFCP_ERP_ACTION_REOPEN_UNIT :
++			if (status == ZFCP_ERP_SUCCEEDED)
++				;/* no further action */
++			else	zfcp_erp_port_reopen_internal(unit->port, 0);
++			break;
++
++		default :
++			debug_text_exception(adapter->erp_dbf, 1, "a_stda_bug");
++			debug_event(adapter->erp_dbf, 1, &action, sizeof(int));
++			ZFCP_LOG_NORMAL(
++				"bug: Unknown error recovery procedure "
++				"action requested on the adapter with "
++				"devno 0x%04x (debug info %d)\n",
++				adapter->devno,
++				action);
++	}
++
++	ZFCP_LOG_TRACE("exit\n");
++
++	return 0;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/**
++ * FIXME: document
++ */
++static int zfcp_erp_strategy_check_queues(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	unsigned long flags;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	read_lock_irqsave(&adapter->erp_lock, flags);
++	if (list_empty(&adapter->erp_ready_head) &&
++	    list_empty(&adapter->erp_running_head)) {
++                debug_text_event(adapter->erp_dbf, 4, "a_cq_wake");
++                atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
++                                  &adapter->status);
++                wake_up(&adapter->erp_done_wqh);
++	} else	debug_text_event(adapter->erp_dbf, 5, "a_cq_notempty");
++	read_unlock_irqrestore(&adapter->erp_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++int zfcp_erp_wait(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	wait_event(
++		adapter->erp_done_wqh,
++		!atomic_test_mask(
++			ZFCP_STATUS_ADAPTER_ERP_PENDING,
++			&adapter->status));
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_erp_modify_adapter_status
++ *
++ * purpose:	
++ *
++ */
++static void zfcp_erp_modify_adapter_status(zfcp_adapter_t *adapter, 
++                                           u32 mask,
++                                           int set_or_clear)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++        zfcp_port_t *port;
++        unsigned long flags;
++        u32 common_mask=mask & ZFCP_COMMON_FLAGS;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx)\n",
++		(unsigned long)adapter);
++
++        if(set_or_clear==ZFCP_SET) {
++                atomic_set_mask(mask, &adapter->status);
++                debug_text_event(adapter->erp_dbf, 3, "a_mod_as_s");
++        } else {
++                atomic_clear_mask(mask, &adapter->status);
++                if(mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
++                        atomic_set(&adapter->erp_counter, 0);
++                }
++                debug_text_event(adapter->erp_dbf, 3, "a_mod_as_c");
++        }
++	debug_event(adapter->erp_dbf, 3, &mask, sizeof(u32));
++
++        if(!common_mask) goto out;
++        /* Deal with all underlying devices, only pass common_mask */
++	read_lock_irqsave(&adapter->port_list_lock, flags);
++	ZFCP_FOR_EACH_PORT(adapter, port) {
++                zfcp_erp_modify_port_status(port,
++                                            common_mask,
++                                            set_or_clear);
++        }
++	read_unlock_irqrestore(&adapter->port_list_lock, flags);
++ out:
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_erp_modify_port_status
++ *
++ * purpose:	sets the port and all underlying devices to ERP_FAILED
++ *
++ */
++static void zfcp_erp_modify_port_status(zfcp_port_t *port,
++                                        u32 mask,
++                                        int set_or_clear)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++        zfcp_unit_t *unit;
++        unsigned long flags;
++        u32 common_mask=mask & ZFCP_COMMON_FLAGS;
++ 
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx)\n",
++		(unsigned long)port);
++
++        if(set_or_clear==ZFCP_SET) {
++                atomic_set_mask(mask, &port->status);
++                debug_text_event(port->adapter->erp_dbf, 3, 
++                                 "p_mod_ps_s");
++        } else {
++                atomic_clear_mask(mask, &port->status);
++                if(mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
++                        atomic_set(&port->erp_counter, 0);
++                }
++                debug_text_event(port->adapter->erp_dbf, 3, 
++                                 "p_mod_ps_c");
++        }
++        debug_event(port->adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t));
++	debug_event(port->adapter->erp_dbf, 3, &mask, sizeof(u32));
++       
++        if(!common_mask) goto out;
++        /* Modify status of all underlying devices, only pass common mask */
++	read_lock_irqsave(&port->unit_list_lock, flags);
++	ZFCP_FOR_EACH_UNIT(port, unit) {
++                zfcp_erp_modify_unit_status(unit,
++                                            common_mask,
++                                            set_or_clear);
++        }
++	read_unlock_irqrestore(&port->unit_list_lock, flags);
++ out:
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	zfcp_erp_modify_unit_status
++ *
++ * purpose:	sets the unit to ERP_FAILED
++ *
++ */
++static void zfcp_erp_modify_unit_status(zfcp_unit_t *unit,
++                                        u32 mask,
++                                        int set_or_clear)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	ZFCP_LOG_TRACE(
++		"enter (unit=0x%lx)\n",
++		(unsigned long)unit);
++
++        if(set_or_clear==ZFCP_SET) {
++                atomic_set_mask(mask, &unit->status);
++                debug_text_event(unit->port->adapter->erp_dbf, 3, "u_mod_us_s");
++        } else {
++                atomic_clear_mask(mask, &unit->status);
++                if(mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
++                        atomic_set(&unit->erp_counter, 0);
++                }
++                debug_text_event(unit->port->adapter->erp_dbf, 3, "u_mod_us_c");
++        }
++        debug_event(unit->port->adapter->erp_dbf, 3, &unit->fcp_lun, sizeof(fcp_lun_t));
++	debug_event(unit->port->adapter->erp_dbf, 3, &mask, sizeof(u32));
++
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	Wrappper for zfcp_erp_port_reopen_all_internal
++ *              used to ensure the correct locking
++ *
++ * returns:	0	- initiated action succesfully
++ *		<0	- failed to initiate action
++ */
++static int zfcp_erp_port_reopen_all(
++		zfcp_adapter_t *adapter,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        unsigned long flags;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)adapter,
++		clear_mask);
++
++        write_lock_irqsave(&adapter->erp_lock, flags);
++        retval = zfcp_erp_port_reopen_all_internal(adapter, clear_mask);
++        write_unlock_irqrestore(&adapter->erp_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_port_reopen_all_internal(
++		zfcp_adapter_t *adapter,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	unsigned long flags;
++	zfcp_port_t *port;
++
++	ZFCP_LOG_TRACE(
++		"enter (adapter=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)adapter,
++		clear_mask);
++
++	read_lock_irqsave(&adapter->port_list_lock, flags);
++	ZFCP_FOR_EACH_PORT(adapter, port) {
++		if (!atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER, &port->status)) 
++			zfcp_erp_port_reopen_internal(port, clear_mask);
++	}
++	read_unlock_irqrestore(&adapter->port_list_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_unit_reopen_all_internal(
++		zfcp_port_t *port,
++		int clear_mask)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	unsigned long flags;
++	zfcp_unit_t *unit;
++
++	ZFCP_LOG_TRACE(
++		"enter (port=0x%lx clear_mask=0x%x)\n",
++		(unsigned long)port,
++		clear_mask);
++
++	read_lock_irqsave(&port->unit_list_lock, flags);
++	ZFCP_FOR_EACH_UNIT(port, unit)
++		zfcp_erp_unit_reopen_internal(unit, clear_mask);
++	read_unlock_irqrestore(&port->unit_list_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	this routine executes the 'Reopen Adapter' action
++ *		(the entire action is processed synchronously, since
++ *		there are no actions which might be run concurrently
++ *		per definition)
++ *
++ * returns:	ZFCP_ERP_SUCCEEDED	- action finished successfully
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
++ */
++static int zfcp_erp_adapter_strategy(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        unsigned long timeout;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++        
++	retval = zfcp_erp_adapter_strategy_close(erp_action);
++	if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
++		retval = ZFCP_ERP_EXIT;
++        else	retval = zfcp_erp_adapter_strategy_open(erp_action);
++
++        debug_text_event(adapter->erp_dbf, 3, "a_ast/ret");
++        debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof(int));
++        debug_event(adapter->erp_dbf, 3, &retval, sizeof(int));
++
++        if(retval==ZFCP_ERP_FAILED) {
++                /*INFO*/
++                ZFCP_LOG_NORMAL("Waiting to allow the adapter with devno "
++                              "0x%04x to recover itself\n",
++                              adapter->devno);
++		/*
++		 * SUGGESTION: substitute by
++		 * timeout = ZFCP_TYPE2_RECOVERY_TIME;
++		 * __ZFCP_WAIT_EVENT_TIMEOUT(timeout, 0);
++		 */
++                timeout=ZFCP_TYPE2_RECOVERY_TIME;
++                set_current_state(TASK_UNINTERRUPTIBLE);
++                do { 
++                        timeout=schedule_timeout(timeout);
++                } while (timeout);
++		/* FIXME: why no  current->state = TASK_RUNNING ? */
++        }
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:	ZFCP_ERP_SUCCEEDED      - action finished successfully
++ *              ZFCP_ERP_FAILED         - action finished unsuccessfully
++ */
++static int zfcp_erp_adapter_strategy_close(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        atomic_set_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->adapter->status);
++	retval = zfcp_erp_adapter_strategy_generic(erp_action, 1);
++        atomic_clear_mask(ZFCP_STATUS_COMMON_CLOSING, &erp_action->adapter->status);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:	ZFCP_ERP_SUCCEEDED      - action finished successfully
++ *              ZFCP_ERP_FAILED         - action finished unsuccessfully
++ */
++static int zfcp_erp_adapter_strategy_open(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        atomic_set_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->adapter->status);
++	retval = zfcp_erp_adapter_strategy_generic(erp_action, 0);
++        atomic_clear_mask(ZFCP_STATUS_COMMON_OPENING, &erp_action->adapter->status);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_register_adapter
++ *
++ * purpose:	allocate the irq associated with this devno and register
++ *		the FSF adapter with the SCSI stack
++ *
++ * returns:	
++ */
++static int zfcp_erp_adapter_strategy_generic(zfcp_erp_action_t *erp_action, int close)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
++ 
++	int retval = ZFCP_ERP_SUCCEEDED;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	if (close)
++		goto close_only;
++
++	retval = zfcp_erp_adapter_strategy_open_irq(erp_action);
++	if (retval != ZFCP_ERP_SUCCEEDED) {
++		ZFCP_LOG_INFO(
++			"error: Could not setup irq, "
++			"adapter with devno 0x%04x. ",
++			adapter->devno);
++		goto failed_irq;
++	}
++	ZFCP_LOG_DEBUG(
++		"got irq %d (adapter devno=0x%04x)\n",
++		adapter->irq,
++		adapter->devno);
++
++	/* setup QDIO for this adapter */
++	retval = zfcp_erp_adapter_strategy_open_qdio(erp_action);
++	if (retval != ZFCP_ERP_SUCCEEDED) {
++		ZFCP_LOG_INFO(
++			"error: Could not start QDIO (data transfer mechanism) "
++                        "adapter with devno 0x%04x.\n",
++			adapter->devno);
++		goto failed_qdio;
++	}
++	ZFCP_LOG_DEBUG("QDIO started (adapter devno=0x%04x)\n", adapter->devno);
++
++	/* setup FSF for this adapter */
++	retval = zfcp_erp_adapter_strategy_open_fsf(erp_action);
++	if (retval != ZFCP_ERP_SUCCEEDED) {
++		ZFCP_LOG_INFO(
++			"error: Could not communicate with the adapter with "
++                        "devno 0x%04x. Card may be busy.\n",
++			adapter->devno);
++		goto failed_openfcp;
++	}
++	ZFCP_LOG_DEBUG("FSF started (adapter devno=0x%04x)\n", adapter->devno);
++
++        /* Success */
++        atomic_set_mask(ZFCP_STATUS_COMMON_OPEN, &erp_action->adapter->status);
++        goto out;
++
++close_only:
++        atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN, 
++                          &erp_action->adapter->status); 
++failed_openfcp:
++	zfcp_erp_adapter_strategy_close_qdio(erp_action);
++	zfcp_erp_adapter_strategy_close_fsf(erp_action);
++        
++failed_qdio:
++	zfcp_erp_adapter_strategy_close_irq(erp_action);
++        
++failed_irq:
++                
++        /* NOP */
++out:
++                
++        ZFCP_LOG_TRACE("exit (%i)\n", retval);
++        
++ return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	gets irq associated with devno and requests irq
++ *
++ * returns:
++ */
++static int zfcp_erp_adapter_strategy_open_irq(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = ZFCP_ERP_FAILED;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++#if 0
++        retval = zfcp_adapter_detect(adapter)
++        if (retval == -ENOMEM) {
++                retval = ZFCP_ERP_NOMEM;
++                goto out;
++        }
++        if (retval != 0) {
++                retval = ZFCP_ERP_FAILED;
++                goto out;
++        }
++
++        retval = zfcp_adapter_detect(adapter)
++        if (retval == -ENOMEM) {
++                retval = ZFCP_ERP_NOMEM;
++                goto out;
++        }
++        if (retval != 0) {
++                retval = ZFCP_ERP_FAILED;
++                goto out;
++        }
++        retval = ZFCP_ERP_SUCCEEDED;
++#endif
++
++	if (zfcp_adapter_detect(adapter) == 0)
++		if (zfcp_adapter_irq_register(adapter) == 0)
++			retval = ZFCP_ERP_SUCCEEDED;
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	releases owned irq
++ *
++ * returns:
++ */
++static int zfcp_erp_adapter_strategy_close_irq(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = ZFCP_ERP_SUCCEEDED;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	zfcp_adapter_irq_unregister(erp_action->adapter);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_qdio_init
++ *
++ * purpose:	setup QDIO operation for specified adapter
++ *
++ * returns:	0 - successful setup
++ *		!0 - failed setup
++ */
++int zfcp_erp_adapter_strategy_open_qdio(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++	int i;
++	volatile qdio_buffer_element_t *buffere;
++	
++        ZFCP_LOG_TRACE("enter\n");
++
++	if (atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
++		ZFCP_LOG_NORMAL(
++			"bug: QDIO (data transfer mechanism) start-up on "
++                        "adapter with devno 0x%04x attempted twice. "
++                        "Second attempt ignored.",
++			adapter->devno);
++			goto failed_sanity;
++	}
++
++	if (zfcp_initialize_with_0copy(adapter) != 0) {
++		ZFCP_LOG_INFO(
++			"error: Could not establish queues for QDIO (data "
++                        "transfer mechanism) operation on adapter with devno "
++                        "0x%04x.\n",
++                        adapter->devno);
++		goto failed_qdio_initialize;
++	}
++	ZFCP_LOG_DEBUG("queues established\n");
++
++        /* activate QDIO request and response queue */
++	if (qdio_activate(adapter->irq, 0) != 0) {
++		ZFCP_LOG_INFO(
++			"error: Could not activate queues for QDIO (data "
++                        "transfer mechanism) operation on adapter with devno "
++                        "0x%04x.\n",
++                        adapter->devno);
++		goto failed_qdio_activate;
++	}
++	ZFCP_LOG_DEBUG("queues activated\n");
++
++	/*
++	 * put buffers into response queue,
++	 */
++	for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
++		buffere =  &(adapter->response_queue.buffer[i]->element[0]);
++		buffere->length = 0;
++        	buffere->flags = SBAL_FLAGS_LAST_ENTRY;
++		buffere->addr = 0;
++	}
++
++        ZFCP_LOG_TRACE(
++		"Calling do QDIO irq=0x%x,flags=0x%x, queue_no=%i, "
++		"index_in_queue=%i, count=%i\n",
++		adapter->irq,
++		QDIO_FLAG_SYNC_INPUT,
++		0,
++		0,
++		QDIO_MAX_BUFFERS_PER_Q);	
++
++	retval = do_QDIO(
++			adapter->irq,
++			QDIO_FLAG_SYNC_INPUT,
++			0,
++			0,
++			QDIO_MAX_BUFFERS_PER_Q,
++			NULL);
++
++	if (retval) {
++		ZFCP_LOG_NORMAL(
++			"bug: QDIO (data transfer mechanism) inobund transfer "
++                        "structures could not be set-up (debug info %d)\n",
++			retval);
++		goto failed_do_qdio;
++	} else	{
++		adapter->response_queue.free_index = 0;
++		atomic_set(&adapter->response_queue.free_count, 0);
++		ZFCP_LOG_DEBUG(
++			"%i buffers successfully enqueued to response queue\n",
++			QDIO_MAX_BUFFERS_PER_Q);
++	}
++
++	/* set index of first avalable SBALS / number of available SBALS */
++	adapter->request_queue.free_index = 0;
++	atomic_set(&adapter->request_queue.free_count, QDIO_MAX_BUFFERS_PER_Q);
++        adapter->request_queue.distance_from_int = 0;
++
++	/* initialize waitqueue used to wait for free SBALs in requests queue */
++	init_waitqueue_head(&adapter->request_wq);
++
++	/* ok, we did it - skip all cleanups for different failures */
++	atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
++	retval = ZFCP_ERP_SUCCEEDED;
++
++	goto out;
++
++failed_do_qdio:
++	/* NOP */
++
++failed_qdio_activate:
++        debug_text_event(adapter->erp_dbf, 3, "qdio_down1a");
++        while (qdio_cleanup(adapter->irq,
++                            QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS) {
++                set_current_state(TASK_UNINTERRUPTIBLE);
++                schedule_timeout(HZ);
++        }
++        debug_text_event(adapter->erp_dbf, 3, "qdio_down1b");
++
++	/*
++	 * First we had to stop QDIO operation.
++	 * Now it is safe to take the following actions.
++	 */
++
++failed_qdio_initialize:
++failed_sanity:
++	retval = ZFCP_ERP_FAILED;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_qdio_cleanup
++ *
++ * purpose:	cleans up QDIO operation for the specified adapter
++ *
++ * returns:	0 - successful cleanup
++ *		!0 - failed cleanup
++ */
++int zfcp_erp_adapter_strategy_close_qdio(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
++ 
++	int retval = ZFCP_ERP_SUCCEEDED;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status)) {
++		ZFCP_LOG_DEBUG(
++			"Termination of QDIO (data transfer operation) "
++                        "attempted for an inactive qdio on the "
++                        "adapter with devno 0x%04x....ignored.\n",
++			adapter->devno);
++		retval = ZFCP_ERP_FAILED;
++		goto out;
++	}
++
++        /*
++         * Get queue_lock and clear QDIOUP flag. Thus it's guaranteed that
++         * do_QDIO won't be called while qdio_shutdown is in progress.
++         */
++
++        write_lock_irq(&adapter->request_queue.queue_lock);
++        atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
++        write_unlock_irq(&adapter->request_queue.queue_lock);
++
++        debug_text_event(adapter->erp_dbf, 3, "qdio_down2a");
++        while (qdio_cleanup(adapter->irq,
++                            QDIO_FLAG_CLEANUP_USING_CLEAR) == -EINPROGRESS) {
++                set_current_state(TASK_UNINTERRUPTIBLE);
++                schedule_timeout(HZ);
++        }
++        debug_text_event(adapter->erp_dbf, 3, "qdio_down2b");
++
++	/*
++	 * First we had to stop QDIO operation.
++	 * Now it is safe to take the following actions.
++	 */
++
++	zfcp_zero_sbals(
++		adapter->request_queue.buffer,
++		0,
++		QDIO_MAX_BUFFERS_PER_Q);
++        adapter->response_queue.free_index = 0;
++        atomic_set(&adapter->response_queue.free_count, 0);
++        adapter->request_queue.free_index = 0;
++        atomic_set(&adapter->request_queue.free_count, 0);
++        adapter->request_queue.distance_from_int = 0;
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_init
++ *
++ * purpose:	initializes FSF operation for the specified adapter
++ *
++ * returns:	0 - succesful initialization of FSF operation
++ *		!0 - failed to initialize FSF operation
++ */
++static int zfcp_erp_adapter_strategy_open_fsf(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
++ 
++	int retval;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	/* do 'exchange configuration data' */
++	retval = zfcp_erp_adapter_strategy_open_fsf_xconfig(erp_action);
++	if (retval == ZFCP_ERP_FAILED)
++		goto out;
++
++	/* start the desired number of Status Reads */
++	retval = zfcp_erp_adapter_strategy_open_fsf_statusread(erp_action);
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++ 
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_adapter_strategy_open_fsf_xconfig(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = ZFCP_ERP_SUCCEEDED;
++	int retries;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        atomic_clear_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status);
++	retries = ZFCP_EXCHANGE_CONFIG_DATA_RETRIES;
++
++	do {
++		atomic_clear_mask(
++			ZFCP_STATUS_ADAPTER_HOST_CON_INIT, 
++			&adapter->status);
++	        ZFCP_LOG_DEBUG("Doing exchange config data\n");
++		zfcp_erp_action_to_running(erp_action);
++		zfcp_erp_timeout_init(erp_action);
++		if (zfcp_fsf_exchange_config_data(erp_action)) {
++			retval = ZFCP_ERP_FAILED;
++        	        debug_text_event(adapter->erp_dbf, 5, "a_fstx_xf");
++			ZFCP_LOG_INFO(
++				"error: Out of resources. Could not "
++				"start exchange of configuration data "
++				"between the adapter with devno "
++				"0x%04x and the device driver.\n",
++				adapter->devno);
++			break;
++		}
++		debug_text_event(adapter->erp_dbf, 6, "a_fstx_xok");
++		ZFCP_LOG_DEBUG("Xchange underway\n");
++
++		/*
++		 * Why this works:
++		 * Both the normal completion handler as well as the timeout
++		 * handler will do an 'up' when the 'exchange config data'
++		 * request completes or times out. Thus, the signal to go on
++		 * won't be lost utilizing this semaphore.
++		 * Furthermore, this 'adapter_reopen' action is
++		 * guaranteed to be the only action being there (highest action
++		 * which prevents other actions from being created).
++		 * Resulting from that, the wake signal recognized here
++		 * _must_ be the one belonging to the 'exchange config
++		 * data' request.
++		 */
++		down(&adapter->erp_ready_sem);
++		if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
++			ZFCP_LOG_INFO(
++				"error: Exchange of configuration data between "
++				"the adapter with devno 0x%04x and the device "
++				"driver timed out\n",
++				adapter->devno);
++			break;
++		}
++		if (atomic_test_mask(
++				ZFCP_STATUS_ADAPTER_HOST_CON_INIT,
++				&adapter->status)) {
++                        ZFCP_LOG_DEBUG(
++				"Host connection still initialising... "
++				"waiting and retrying....\n");
++			/* sleep a little bit before retry */
++			set_current_state(TASK_INTERRUPTIBLE);
++			schedule_timeout(ZFCP_EXCHANGE_CONFIG_DATA_SLEEP);
++		}
++	} while ((retries--) &&
++		 atomic_test_mask(
++			ZFCP_STATUS_ADAPTER_HOST_CON_INIT, 
++			&adapter->status));
++
++	if (!atomic_test_mask(ZFCP_STATUS_ADAPTER_XCONFIG_OK, &adapter->status)) {
++                ZFCP_LOG_INFO(
++			"error: Exchange of configuration data between "
++			"the adapter with devno 0x%04x and the device "
++			"driver failed.\n",
++			adapter->devno);
++		retval = ZFCP_ERP_FAILED;;
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_adapter_strategy_open_fsf_statusread(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = ZFCP_ERP_SUCCEEDED;
++        int temp_ret;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++	int i;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	adapter->status_read_failed = 0;
++	for (i = 0; i < ZFCP_STATUS_READS_RECOM; i++) {
++		ZFCP_LOG_TRACE("issuing status read request #%d...\n", i);
++		temp_ret = zfcp_fsf_status_read(
++				adapter,
++				ZFCP_WAIT_FOR_SBAL);
++		if (temp_ret < 0) {
++        	        ZFCP_LOG_INFO(
++				"error: Out of resources. Could not "
++				"set-up the infrastructure for "
++				"unsolicited status presentation "
++				"for the adapter with devno "
++				"0x%04x.\n",
++				adapter->devno);
++			retval = ZFCP_ERP_FAILED;
++			i--;
++			break;
++		}
++	}
++	ZFCP_LOG_DEBUG("started %i status reads\n", i);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_fsf_cleanup
++ *
++ * purpose:	cleanup FSF operation for specified adapter
++ *
++ * returns:	0 - FSF operation successfully cleaned up
++ *		!0 - failed to cleanup FSF operation for this adapter
++ */
++static int zfcp_erp_adapter_strategy_close_fsf(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA                   ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX            ZFCP_LOG_AREA_PREFIX_ERP
++ 
++	int retval = ZFCP_ERP_SUCCEEDED;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++	ZFCP_LOG_TRACE("enter (adapter=0x%lx)\n", (unsigned long)adapter);
++
++	/*
++	 * wake waiting initiators of requests,
++	 * return SCSI commands (with error status),
++	 * clean up all requests (synchronously)
++	 */
++        zfcp_fsf_req_dismiss_all(adapter);
++       	/* reset FSF request sequence number */
++	adapter->fsf_req_seq_no = 0;
++	/* all ports and units are closed */
++	zfcp_erp_modify_adapter_status(
++		adapter,
++		ZFCP_STATUS_COMMON_OPEN,
++		ZFCP_CLEAR);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++ 
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	this routine executes the 'Reopen Physical Port' action
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
++ *		ZFCP_ERP_SUCCEEDED	- action finished successfully
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
++ */
++static int zfcp_erp_port_forced_strategy(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = ZFCP_ERP_FAILED;
++	zfcp_port_t *port = erp_action->port;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	switch (erp_action->step) {
++
++		/* FIXME: the ULP spec. begs for waiting for oustanding commands */
++		case ZFCP_ERP_STEP_UNINITIALIZED :
++			zfcp_erp_port_strategy_clearstati(port);
++			/*
++			 * it would be sufficient to test only the normal open flag
++			 * since the phys. open flag cannot be set if the normal
++			 * open flag is unset - however, this is for readabilty ...
++			 */
++			if (atomic_test_mask(
++					(ZFCP_STATUS_PORT_PHYS_OPEN |
++					 ZFCP_STATUS_COMMON_OPEN),
++				 	&port->status)) {
++				ZFCP_LOG_DEBUG(
++					"Port WWPN=0x%016Lx is open -> trying close physical\n",
++					(llui_t)port->wwpn);
++				retval = zfcp_erp_port_forced_strategy_close(erp_action);
++			} else	retval = ZFCP_ERP_FAILED;
++			break;
++
++		case ZFCP_ERP_STEP_PHYS_PORT_CLOSING :
++			if (atomic_test_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status)) { 
++				ZFCP_LOG_DEBUG(
++					"failed to close physical port WWPN=0x%016Lx\n",
++					(llui_t)port->wwpn);
++				retval = ZFCP_ERP_FAILED;
++			} else	retval = ZFCP_ERP_SUCCEEDED;
++			break;
++	}
++
++        debug_text_event(adapter->erp_dbf, 3, "p_pfst/ret");
++        debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t));
++        debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof(int));
++        debug_event(adapter->erp_dbf, 3, &retval, sizeof(int));
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	this routine executes the 'Reopen Port' action
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
++ *		ZFCP_ERP_SUCCEEDED	- action finished successfully
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
++ */
++static int zfcp_erp_port_strategy(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = ZFCP_ERP_FAILED;
++	zfcp_port_t *port = erp_action->port;
++        zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	switch (erp_action->step) {
++
++		/* FIXME: the ULP spec. begs for waiting for oustanding commands */
++		case ZFCP_ERP_STEP_UNINITIALIZED :
++			zfcp_erp_port_strategy_clearstati(port);
++			if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
++				ZFCP_LOG_DEBUG(
++					"port WWPN=0x%016Lx is open -> trying close\n",
++					(llui_t)port->wwpn);
++				retval = zfcp_erp_port_strategy_close(erp_action);
++				goto out;
++			} /* else it's already closed, open it */
++			break;
++
++		case ZFCP_ERP_STEP_PORT_CLOSING :
++                        if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) { 
++				ZFCP_LOG_DEBUG(
++					"failed to close port WWPN=0x%016Lx\n",
++					(llui_t)port->wwpn);
++				retval = ZFCP_ERP_FAILED;
++				goto out;
++			} /* else it's closed now, open it */
++			break;
++	}
++        if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
++		retval = ZFCP_ERP_EXIT;
++	else	retval = zfcp_erp_port_strategy_open(erp_action);
++
++out:
++        debug_text_event(adapter->erp_dbf, 3, "p_pst/ret");
++        debug_event(adapter->erp_dbf, 3, &port->wwpn, sizeof(wwn_t));
++        debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof(int));
++        debug_event(adapter->erp_dbf, 3, &retval, sizeof(int));
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_port_strategy_open(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	if (atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER, &erp_action->port->status))
++		retval = zfcp_erp_port_strategy_open_nameserver(erp_action);
++	else	retval = zfcp_erp_port_strategy_open_common(erp_action);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ *
++ * FIXME(design):	currently only prepared for fabric (nameserver!)
++ */
++static int zfcp_erp_port_strategy_open_common(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++	zfcp_port_t *port = erp_action->port;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	switch (erp_action->step) {
++
++		case ZFCP_ERP_STEP_UNINITIALIZED :
++		case ZFCP_ERP_STEP_PHYS_PORT_CLOSING :
++		case ZFCP_ERP_STEP_PORT_CLOSING :
++			if (!(adapter->nameserver_port)) {
++				retval = zfcp_nameserver_enqueue(adapter);
++				if (retval == -ENOMEM) {
++					retval = ZFCP_ERP_NOMEM;
++					break;
++				}
++				if (retval != 0) {
++					ZFCP_LOG_NORMAL(
++						"error: nameserver port not available "
++						"(adapter with devno 0x%04x)\n",
++						adapter->devno);
++					retval = ZFCP_ERP_FAILED;
++					break;
++				}
++			}
++			if (!atomic_test_mask(
++					ZFCP_STATUS_COMMON_UNBLOCKED, 
++					&adapter->nameserver_port->status)) {
++				ZFCP_LOG_DEBUG(
++					"nameserver port is not open -> open nameserver port\n");
++                                /* nameserver port may live again */
++				atomic_set_mask(
++					ZFCP_STATUS_COMMON_RUNNING,
++					&adapter->nameserver_port->status); 
++				if (zfcp_erp_port_reopen(adapter->nameserver_port, 0) >= 0) {
++					erp_action->step = ZFCP_ERP_STEP_NAMESERVER_OPEN;
++					retval = ZFCP_ERP_CONTINUES;
++				} else	retval = ZFCP_ERP_FAILED;
++				break;
++			} /* else nameserver port is already open, fall through */
++
++		case ZFCP_ERP_STEP_NAMESERVER_OPEN :
++			if (!atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, 
++                                              &adapter->nameserver_port->status)) {
++				ZFCP_LOG_DEBUG("failed to open nameserver port\n");
++				retval = ZFCP_ERP_FAILED;
++			} else	{
++				ZFCP_LOG_DEBUG(
++					"nameserver port is open -> "
++					"ask nameserver for current D_ID of port with WWPN 0x%016Lx\n",
++					(llui_t)port->wwpn);
++				retval = zfcp_erp_port_strategy_open_common_lookup(erp_action);
++			}
++			break;
++
++		case ZFCP_ERP_STEP_NAMESERVER_LOOKUP :
++			if (!atomic_test_mask(ZFCP_STATUS_PORT_DID_DID, &port->status)) { 
++				if (atomic_test_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status)) {
++					ZFCP_LOG_DEBUG(
++						"failed to look up the D_ID of the port WWPN=0x%016Lx "
++						"(misconfigured WWPN?)\n",
++						(llui_t)port->wwpn);
++                                        zfcp_erp_port_failed(port);
++					retval = ZFCP_ERP_EXIT;
++				} else	{
++					ZFCP_LOG_DEBUG(
++						"failed to look up the D_ID of the port WWPN=0x%016Lx\n",
++						(llui_t)port->wwpn);
++					retval = ZFCP_ERP_FAILED;
++				}
++			} else	{
++				ZFCP_LOG_DEBUG(
++					"port WWPN=0x%016Lx has D_ID=0x%06x -> trying open\n",
++					(llui_t)port->wwpn,
++					port->d_id);
++				retval = zfcp_erp_port_strategy_open_port(erp_action);
++			}
++			break;
++
++		case ZFCP_ERP_STEP_PORT_OPENING :
++			if (atomic_test_mask(
++					(ZFCP_STATUS_COMMON_OPEN |
++					 ZFCP_STATUS_PORT_DID_DID),/* D_ID might have changed during open */
++					&port->status)) {
++				ZFCP_LOG_DEBUG(
++					"port WWPN=0x%016Lx is open ",
++					(llui_t)port->wwpn);
++				retval = ZFCP_ERP_SUCCEEDED;
++			} else	{
++				ZFCP_LOG_DEBUG(
++					"failed to open port WWPN=0x%016Lx\n",
++					(llui_t)port->wwpn);
++				retval = ZFCP_ERP_FAILED;
++			}
++			break;
++
++		default :
++			ZFCP_LOG_NORMAL(
++				"bug: unkown erp step 0x%x\n",
++				erp_action->step);
++			retval = ZFCP_ERP_FAILED;
++	}
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_port_strategy_open_nameserver(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++	zfcp_port_t *port = erp_action->port;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	switch (erp_action->step) {
++
++		case ZFCP_ERP_STEP_UNINITIALIZED :
++		case ZFCP_ERP_STEP_PHYS_PORT_CLOSING :
++		case ZFCP_ERP_STEP_PORT_CLOSING :
++			ZFCP_LOG_DEBUG(
++				"port WWPN=0x%016Lx has D_ID=0x%06x -> trying open\n",
++				(llui_t)port->wwpn,
++				port->d_id);
++			retval = zfcp_erp_port_strategy_open_port(erp_action);
++			break;
++
++		case ZFCP_ERP_STEP_PORT_OPENING :
++			if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) {
++				ZFCP_LOG_DEBUG("nameserver port is open\n");
++				retval = ZFCP_ERP_SUCCEEDED;
++			} else	{
++				ZFCP_LOG_DEBUG("failed to open nameserver port\n");
++				retval = ZFCP_ERP_FAILED;
++			}
++			/* this is needed anyway (dont care for retval of wakeup) */
++			ZFCP_LOG_DEBUG("continue other open port operations\n");
++			zfcp_erp_port_strategy_open_nameserver_wakeup(erp_action);
++			break;
++
++		default :
++			ZFCP_LOG_NORMAL(
++				"bug: unkown erp step 0x%x\n",
++				erp_action->step);
++			retval = ZFCP_ERP_FAILED;
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	makes the erp thread continue with reopen (physical) port
++ *		actions which have been paused until the name server port
++ *		is opened (or failed)
++ *
++ * returns:	0	(a kind of void retval, its not used)
++ */
++static int zfcp_erp_port_strategy_open_nameserver_wakeup(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	unsigned long flags;
++	zfcp_adapter_t *adapter = erp_action->adapter;
++	struct list_head *entry, *temp_entry;
++	zfcp_erp_action_t *tmp_erp_action;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	write_lock_irqsave(&adapter->erp_lock, flags);
++	list_for_each_safe(entry, temp_entry, &adapter->erp_running_head) {
++		tmp_erp_action = list_entry(entry, zfcp_erp_action_t, list);
++		debug_text_event(adapter->erp_dbf, 3, "p_pstnsw_n");
++		debug_event(adapter->erp_dbf, 3, &tmp_erp_action->port->wwpn, sizeof(wwn_t));
++		if (tmp_erp_action->step == ZFCP_ERP_STEP_NAMESERVER_OPEN) {
++			debug_text_event(adapter->erp_dbf, 3, "p_pstnsw_w");
++			debug_event(adapter->erp_dbf, 3, &tmp_erp_action->port->wwpn, sizeof(wwn_t));
++			if (atomic_test_mask(
++					ZFCP_STATUS_COMMON_ERP_FAILED,
++					&adapter->nameserver_port->status))
++				zfcp_erp_port_failed(tmp_erp_action->port);
++			zfcp_erp_action_ready(tmp_erp_action);
++		}
++	}
++	write_unlock_irqrestore(&adapter->erp_lock, flags);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
++ */
++static int zfcp_erp_port_forced_strategy_close(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        zfcp_adapter_t *adapter = erp_action->adapter;
++        zfcp_port_t *port = erp_action->port;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	zfcp_erp_timeout_init(erp_action);
++	retval = zfcp_fsf_close_physical_port(erp_action);
++	if (retval == -ENOMEM) {
++                debug_text_event(adapter->erp_dbf, 5, "o_pfstc_nomem");
++                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++		retval = ZFCP_ERP_NOMEM;
++		goto out;
++	}
++	erp_action->step = ZFCP_ERP_STEP_PHYS_PORT_CLOSING;
++	if (retval != 0) {
++                debug_text_event(adapter->erp_dbf, 5, "o_pfstc_cpf");
++                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++		/* could not send 'open', fail */
++		retval = ZFCP_ERP_FAILED;
++		goto out;
++	}
++	debug_text_event(adapter->erp_dbf, 6, "o_pfstc_cpok");
++	debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof(wwn_t));
++	retval = ZFCP_ERP_CONTINUES;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_port_strategy_clearstati(zfcp_port_t *port)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++        zfcp_adapter_t *adapter = port->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        debug_text_event(adapter->erp_dbf, 5, "p_pstclst");
++        debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++
++	atomic_clear_mask(
++		ZFCP_STATUS_COMMON_OPENING |
++		ZFCP_STATUS_COMMON_CLOSING |
++		ZFCP_STATUS_PORT_DID_DID |
++		ZFCP_STATUS_PORT_PHYS_CLOSING |
++		ZFCP_STATUS_PORT_INVALID_WWPN,
++		&port->status);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
++ */
++static int zfcp_erp_port_strategy_close(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        zfcp_adapter_t *adapter = erp_action->adapter;
++        zfcp_port_t *port = erp_action->port;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	zfcp_erp_timeout_init(erp_action);
++	retval = zfcp_fsf_close_port(erp_action);
++	if (retval == -ENOMEM) {
++                debug_text_event(adapter->erp_dbf, 5, "p_pstc_nomem");
++                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++		retval = ZFCP_ERP_NOMEM;
++		goto out;
++	}
++	erp_action->step = ZFCP_ERP_STEP_PORT_CLOSING;
++	if (retval != 0) {
++		debug_text_event(adapter->erp_dbf, 5, "p_pstc_cpf");
++		debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++		/* could not send 'close', fail */
++		retval = ZFCP_ERP_FAILED;
++		goto out;
++	}
++	debug_text_event(adapter->erp_dbf, 6, "p_pstc_cpok");
++	debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof(wwn_t));
++	retval = ZFCP_ERP_CONTINUES;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
++ */
++static int zfcp_erp_port_strategy_open_port(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        zfcp_adapter_t *adapter = erp_action->adapter;
++        zfcp_port_t *port = erp_action->port;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	zfcp_erp_timeout_init(erp_action);
++	retval = zfcp_fsf_open_port(erp_action);
++	if (retval == -ENOMEM) {
++                debug_text_event(adapter->erp_dbf, 5, "p_psto_nomem");
++                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++		retval = ZFCP_ERP_NOMEM;
++		goto out;
++	}
++	erp_action->step = ZFCP_ERP_STEP_PORT_OPENING;
++	if (retval != 0) {
++                debug_text_event(adapter->erp_dbf, 5, "p_psto_opf");
++                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++		/* could not send 'open', fail */
++		retval = ZFCP_ERP_FAILED;
++		goto out;
++	}
++	debug_text_event(adapter->erp_dbf, 6, "p_psto_opok");
++	debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof(wwn_t));
++	retval = ZFCP_ERP_CONTINUES;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
++ */
++static int zfcp_erp_port_strategy_open_common_lookup(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        zfcp_adapter_t *adapter = erp_action->adapter;
++        zfcp_port_t *port = erp_action->port;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	zfcp_erp_timeout_init(erp_action);
++	retval = zfcp_ns_gid_pn_request(erp_action);
++	if (retval == -ENOMEM) {
++                debug_text_event(adapter->erp_dbf, 5, "p_pstn_nomem");
++                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++		retval = ZFCP_ERP_NOMEM;
++		goto out;
++	}
++	erp_action->step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
++	if (retval != 0) {
++                debug_text_event(adapter->erp_dbf, 5, "p_pstn_ref");
++                debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++		/* could not send nameserver request, fail */
++		retval = ZFCP_ERP_FAILED;
++		goto out;
++	}
++	debug_text_event(adapter->erp_dbf, 6, "p_pstn_reok");
++	debug_event(adapter->erp_dbf, 6, &port->wwpn, sizeof(wwn_t));
++	retval = ZFCP_ERP_CONTINUES;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	this routine executes the 'Reopen Unit' action
++ *		currently no retries
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
++ *		ZFCP_ERP_SUCCEEDED	- action finished successfully
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
++ */
++static int zfcp_erp_unit_strategy(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = ZFCP_ERP_FAILED;
++	zfcp_unit_t *unit = erp_action->unit;
++        zfcp_adapter_t *adapter=erp_action->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	switch (erp_action->step) {
++
++		/* FIXME: the ULP spec. begs for waiting for oustanding commands */
++		case ZFCP_ERP_STEP_UNINITIALIZED :
++			zfcp_erp_unit_strategy_clearstati(unit);
++			if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
++				ZFCP_LOG_DEBUG(
++					"unit FCP_LUN=0x%016Lx is open -> trying close\n",
++					(llui_t)unit->fcp_lun);
++				retval = zfcp_erp_unit_strategy_close(erp_action);
++				break;
++			} /* else it's already closed, fall through */
++
++		case ZFCP_ERP_STEP_UNIT_CLOSING :
++			if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) {
++				ZFCP_LOG_DEBUG(
++					"failed to close unit FCP_LUN=0x%016Lx\n",
++					(llui_t)unit->fcp_lun);
++				retval = ZFCP_ERP_FAILED;
++			} else	{
++				if (erp_action->status & ZFCP_STATUS_ERP_CLOSE_ONLY)
++			                retval = ZFCP_ERP_EXIT;
++				else	{
++					ZFCP_LOG_DEBUG(
++						"unit FCP_LUN=0x%016Lx is not open -> trying open\n",
++						(llui_t)unit->fcp_lun);
++					retval = zfcp_erp_unit_strategy_open(erp_action);
++				}
++			}
++			break;
++
++		case ZFCP_ERP_STEP_UNIT_OPENING :
++			if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &unit->status)) { 
++				ZFCP_LOG_DEBUG(
++					"unit FCP_LUN=0x%016Lx is open\n",
++					(llui_t)unit->fcp_lun);
++				retval = ZFCP_ERP_SUCCEEDED;
++			} else	{
++				ZFCP_LOG_DEBUG(
++					"failed to open unit FCP_LUN=0x%016Lx\n",
++					(llui_t)unit->fcp_lun);
++				retval = ZFCP_ERP_FAILED;
++			}
++			break;
++	}
++
++        debug_text_event(adapter->erp_dbf, 3, "u_ust/ret");
++        debug_event(adapter->erp_dbf, 3, &unit->fcp_lun, sizeof(fcp_lun_t));
++        debug_event(adapter->erp_dbf, 3, &erp_action->action, sizeof(int));
++        debug_event(adapter->erp_dbf, 3, &retval, sizeof(int));
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_unit_strategy_clearstati(zfcp_unit_t *unit)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++        zfcp_adapter_t *adapter=unit->port->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++       
++        debug_text_event(adapter->erp_dbf,5,"u_ustclst");
++        debug_event(adapter->erp_dbf,5,&unit->fcp_lun,
++                    sizeof(fcp_lun_t));
++
++	atomic_clear_mask(
++		ZFCP_STATUS_COMMON_OPENING |
++		ZFCP_STATUS_COMMON_CLOSING,
++		&unit->status);
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
++ */
++static int zfcp_erp_unit_strategy_close(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        zfcp_adapter_t *adapter = erp_action->adapter;
++        zfcp_unit_t *unit = erp_action->unit;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	zfcp_erp_timeout_init(erp_action);
++	retval = zfcp_fsf_close_unit(erp_action);
++	if (retval == -ENOMEM) {
++                debug_text_event(adapter->erp_dbf, 5, "u_ustc_nomem");
++                debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof(fcp_lun_t));
++		retval = ZFCP_ERP_NOMEM;
++		goto out;
++	}
++	erp_action->step = ZFCP_ERP_STEP_UNIT_CLOSING;
++	if (retval != 0) {
++                debug_text_event(adapter->erp_dbf, 5, "u_ustc_cuf");
++                debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof(fcp_lun_t));
++		/* could not send 'close', fail */
++		retval = ZFCP_ERP_FAILED;
++		goto out;
++	}
++	debug_text_event(adapter->erp_dbf, 6, "u_ustc_cuok");
++	debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof(fcp_lun_t));
++	retval = ZFCP_ERP_CONTINUES;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:	ZFCP_ERP_CONTINUES	- action continues (asynchronously)
++ *		ZFCP_ERP_FAILED		- action finished unsuccessfully
++ */
++static int zfcp_erp_unit_strategy_open(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval;
++        zfcp_adapter_t *adapter = erp_action->adapter;
++        zfcp_unit_t *unit = erp_action->unit;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++	zfcp_erp_timeout_init(erp_action);
++	retval = zfcp_fsf_open_unit(erp_action);
++	if (retval == -ENOMEM) {
++                debug_text_event(adapter->erp_dbf, 5, "u_usto_nomem");
++                debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof(fcp_lun_t));
++		retval = ZFCP_ERP_NOMEM;
++		goto out;
++	}
++	erp_action->step = ZFCP_ERP_STEP_UNIT_OPENING;
++	if (retval != 0) {
++                debug_text_event(adapter->erp_dbf, 5, "u_usto_ouf");
++                debug_event(adapter->erp_dbf, 5, &unit->fcp_lun, sizeof(fcp_lun_t));
++		/* could not send 'open', fail */
++		retval = ZFCP_ERP_FAILED;
++		goto out;
++	}
++	debug_text_event(adapter->erp_dbf, 6, "u_usto_ouok");
++	debug_event(adapter->erp_dbf, 6, &unit->fcp_lun, sizeof(fcp_lun_t));
++	retval = ZFCP_ERP_CONTINUES;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static /*inline*/ int zfcp_erp_timeout_init(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++        zfcp_adapter_t *adapter=erp_action->adapter;
++
++	ZFCP_LOG_TRACE("enter\n");
++        
++        debug_text_event(adapter->erp_dbf, 6, "a_timinit");
++        debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof(int));
++	init_timer(&erp_action->timer);
++	erp_action->timer.function = zfcp_erp_timeout_handler;
++	erp_action->timer.data = (unsigned long)erp_action;
++        /* jiffies will be added in zfcp_fsf_req_send */
++	erp_action->timer.expires = ZFCP_ERP_FSFREQ_TIMEOUT;
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	enqueue the specified error recovery action, if needed
++ *
++ * returns:
++ */
++static int zfcp_erp_action_enqueue(
++	int action,
++	zfcp_adapter_t *adapter,
++	zfcp_port_t *port,
++	zfcp_unit_t *unit)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 1;
++	zfcp_erp_action_t *erp_action = NULL;
++	int stronger_action = 0;
++	u32 status = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (action=%d adapter=0x%lx "
++		"port=0x%lx unit=0x%lx)\n",
++		action,
++		(unsigned long)adapter,
++		(unsigned long)port,
++		(unsigned long)unit);
++
++	/*
++	 * We need some rules here which check whether we really need
++	 * this action or whether we should just drop it.
++	 * E.g. if there is a unfinished 'Reopen Port' request then we drop a
++	 * 'Reopen Unit' request for an associated unit since we can't
++	 * satisfy this request now. A 'Reopen Port' action will trigger
++	 * 'Reopen Unit' actions when it completes.
++	 * Thus, there are only actions in the queue which can immediately be
++	 * executed. This makes the processing of the action queue more
++	 * efficient.
++	 */
++
++        debug_text_event(adapter->erp_dbf, 4, "a_actenq");
++        debug_event(adapter->erp_dbf, 4, &action, sizeof(int));
++	/* check whether we really need this */
++	switch (action) {
++		case ZFCP_ERP_ACTION_REOPEN_UNIT :
++			if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status)) {
++                                debug_text_event(adapter->erp_dbf, 4, "u_actenq_drp");
++                                debug_event(adapter->erp_dbf, 4, &unit->fcp_lun, sizeof(fcp_lun_t));
++				ZFCP_LOG_DEBUG(
++					"drop: erp action %i on unit "
++					"FCP_LUN=0x%016Lx "
++					"(erp action %i already exists)\n",
++					action,
++					(llui_t)unit->fcp_lun,
++					unit->erp_action.action);
++				goto out;
++			}
++			if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status)) { 
++				stronger_action = ZFCP_ERP_ACTION_REOPEN_PORT;
++				unit = NULL;
++			}
++			/* fall through !!! */
++
++		case ZFCP_ERP_ACTION_REOPEN_PORT :
++			if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)) {
++                                debug_text_event(adapter->erp_dbf, 4, "p_actenq_drp");
++                                debug_event(adapter->erp_dbf, 4, &port->wwpn, sizeof(wwn_t));
++				ZFCP_LOG_DEBUG(
++					"drop: erp action %i on port "
++					"WWPN=0x%016Lx "
++					"(erp action %i already exists)\n",
++					action,
++					(llui_t)port->wwpn,
++					port->erp_action.action);
++				goto out;
++			}
++			/* fall through !!! */
++
++		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
++                        /*
++                         * FIXME(erpmod):
++                         * Can't override normal port_reopen in the usual
++                         * manner. Trying adapter_reopen as a workaround
++                         * won't do it. The old code indifferently overwrote
++                         * the normal port_reopen action. That was not a valid
++                         * approach. The old implementation left the erp-action
++                         * list corrupted which had held the former normal
++                         * port_reopen action, since the latter had not been
++                         * removed from the list, just added in another place.
++                         * The hapless endeavour continued with the immediate
++                         * dismissal of the shiny new port_forced_reopen,
++                         * a doom that was meant to be met by the just wiped out
++                         * normal port_reopen action. Not to mention the stray
++                         * fsf_req that might still have felt attached to the
++                         * converted action.   Lots of accidents .... who knows
++                         * what else was about to go awry in the wake of them.
++                         * As a (permanent) hack we are going to use a flag to
++			 * get this action scheduled as a retry after completion
++			 * of the normal port_reopen.
++			 * But til then, just gracefully exit here (and warn).
++                         */
++			if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)) {
++				if (port->erp_action.action == ZFCP_ERP_ACTION_REOPEN_PORT_FORCED) {
++	                                debug_text_event(adapter->erp_dbf, 4, "pf_actenq_drp");
++        	                        debug_event(adapter->erp_dbf, 4, &port->wwpn, sizeof(wwn_t));
++					ZFCP_LOG_DEBUG(
++						"drop: erp action %i on port "
++						"WWPN=0x%016Lx "
++						"(erp action %i already exists)\n",
++						action,
++						(llui_t)port->wwpn,
++						port->erp_action.action);
++				} else	{
++	                                debug_text_event(adapter->erp_dbf, 0, "pf_actenq_drpcp");
++        	                        debug_event(adapter->erp_dbf, 0, &port->wwpn, sizeof(wwn_t));
++					ZFCP_LOG_NORMAL(
++						"drop: erp action %i on port "
++						"WWPN=0x%016Lx "
++						"(erp action %i already exists)\n",
++						action,
++						(llui_t)port->wwpn,
++						port->erp_action.action);
++
++				}
++				goto out;
++			}
++			if (!atomic_test_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status)) {
++				stronger_action = ZFCP_ERP_ACTION_REOPEN_ADAPTER;
++				port = NULL;
++			}
++			/* fall through !!! */
++
++		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
++			if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status)) {
++                                debug_text_event(adapter->erp_dbf, 4, "a_actenq_drp");
++				ZFCP_LOG_DEBUG(
++					"drop: erp action %i on adapter "
++					"devno=0x%04x "
++					"(erp action %i already exists)\n",
++					action,
++					adapter->devno,
++					adapter->erp_action.action);
++				goto out;
++			}
++			break;
++
++		default :
++                        debug_text_exception(adapter->erp_dbf, 1, "a_actenq_bug");
++                        debug_event(adapter->erp_dbf, 1, &action, sizeof(int));
++                        ZFCP_LOG_NORMAL(
++				"bug: Unknown error recovery procedure "
++				"action requested on the adapter with "
++				"devno 0x%04x "
++				"(debug info %d)\n",
++				adapter->devno,
++				action);
++			goto out;
++	}
++
++	/* check whether we need something stronger first */
++	if (stronger_action) {
++                debug_text_event(adapter->erp_dbf, 4, "a_actenq_str");
++                debug_event(adapter->erp_dbf, 4, &stronger_action, sizeof(int));
++		ZFCP_LOG_DEBUG(
++			"shortcut: need erp action %i before "
++			"erp action %i (adapter devno=0x%04x)\n",
++			stronger_action,
++			action,
++			adapter->devno);
++		action = stronger_action;
++	}
++
++	/* mark adapter to have some error recovery pending */
++	atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status);
++
++	/* setup error recovery action */
++	switch (action) {
++
++		case ZFCP_ERP_ACTION_REOPEN_UNIT :
++			atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
++			erp_action = &unit->erp_action;
++			if (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status))
++				status = ZFCP_STATUS_ERP_CLOSE_ONLY;
++			break;
++
++		case ZFCP_ERP_ACTION_REOPEN_PORT :
++		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
++			zfcp_erp_action_dismiss_port(port);
++			atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
++			erp_action = &port->erp_action;
++			if (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status))
++				status = ZFCP_STATUS_ERP_CLOSE_ONLY;
++			break;
++
++		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
++			zfcp_erp_action_dismiss_adapter(adapter);
++			atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
++			erp_action = &adapter->erp_action;
++			if (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &adapter->status))
++				status = ZFCP_STATUS_ERP_CLOSE_ONLY;
++			break;
++	}
++
++	memset(erp_action, 0, sizeof(zfcp_erp_action_t));
++	erp_action->adapter = adapter;
++	erp_action->port = port;
++	erp_action->unit = unit;
++	erp_action->action = action;
++	erp_action->status = status;
++
++	/* finally put it into 'ready' queue and kick erp thread */
++	list_add(&erp_action->list, &adapter->erp_ready_head);
++	ZFCP_LOG_DEBUG(
++		"waking erp_thread of the adapter with devno=0x%04x\n",
++		adapter->devno);
++	up(&adapter->erp_ready_sem);
++	retval = 0;
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_action_dequeue(
++                zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++        zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (erp_action=0x%lx)\n",
++		(unsigned long)erp_action);
++
++        debug_text_event(adapter->erp_dbf, 4, "a_actdeq");
++        debug_event(adapter->erp_dbf, 4, &erp_action->action, sizeof(int));
++        list_del(&erp_action->list);
++	switch (erp_action->action) {
++		case ZFCP_ERP_ACTION_REOPEN_UNIT :
++			atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &erp_action->unit->status);
++			break;
++		case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED :
++		case ZFCP_ERP_ACTION_REOPEN_PORT :
++			atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &erp_action->port->status);
++			break;
++		case ZFCP_ERP_ACTION_REOPEN_ADAPTER :
++			atomic_clear_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &erp_action->adapter->status);
++			break;
++		default :
++			/* bug */
++			break;
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_action_dismiss_adapter(zfcp_adapter_t *adapter)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	zfcp_port_t *port;
++	unsigned long flags;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        debug_text_event(adapter->erp_dbf, 5, "a_actab");
++	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status))
++		/* that's really all in this case */
++		zfcp_erp_action_dismiss(&adapter->erp_action);
++	else	{
++		/* have a deeper look */
++		read_lock_irqsave(&adapter->port_list_lock, flags);
++		ZFCP_FOR_EACH_PORT(adapter, port) {
++			zfcp_erp_action_dismiss_port(port);
++		}
++		read_unlock_irqrestore(&adapter->port_list_lock, flags);	
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	
++ *
++ * returns:
++ */
++static int zfcp_erp_action_dismiss_port(zfcp_port_t *port)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	int retval = 0;
++	zfcp_unit_t *unit;
++        zfcp_adapter_t *adapter = port->adapter;
++	unsigned long flags;
++
++	ZFCP_LOG_TRACE("enter\n");
++
++        debug_text_event(adapter->erp_dbf, 5, "p_actab");
++        debug_event(adapter->erp_dbf, 5, &port->wwpn, sizeof(wwn_t));
++	if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status))
++		/* that's really all in this case */
++		zfcp_erp_action_dismiss(&port->erp_action);
++	else	{
++		/* have a deeper look */
++		read_lock_irqsave(&port->unit_list_lock, flags);
++		ZFCP_FOR_EACH_UNIT(port, unit) {
++                        if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status)) {
++                                zfcp_erp_action_dismiss(&unit->erp_action);
++                        }
++		}
++		read_unlock_irqrestore(&port->unit_list_lock, flags);	
++	}
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	moves erp_action to 'erp running list'
++ *
++ * returns:
++ */
++static inline void zfcp_erp_action_to_running(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++        zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (erp_action=0x%lx\n",
++		(unsigned long)erp_action);
++
++        debug_text_event(adapter->erp_dbf, 6, "a_toru");
++        debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof(int));
++        zfcp_erp_from_one_to_other(
++		&erp_action->list,
++		&erp_action->adapter->erp_running_head);
++
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	moves erp_action to 'erp ready list'
++ *
++ * returns:
++ */
++static inline void zfcp_erp_action_to_ready(zfcp_erp_action_t *erp_action)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++        zfcp_adapter_t *adapter = erp_action->adapter;
++
++	ZFCP_LOG_TRACE(
++		"enter (erp_action=0x%lx\n",
++		(unsigned long)erp_action);
++
++        debug_text_event(adapter->erp_dbf, 6, "a_tore");
++        debug_event(adapter->erp_dbf, 6, &erp_action->action, sizeof(int));
++	zfcp_erp_from_one_to_other(
++		&erp_action->list,
++		&erp_action->adapter->erp_ready_head);
++
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:	
++ *
++ * purpose:	moves a request from one erp_action list to the other
++ *
++ * returns:
++ */
++static inline void zfcp_erp_from_one_to_other(
++	struct list_head *entry,
++	struct list_head *head)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_ERP
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_ERP
++
++	ZFCP_LOG_TRACE(
++		"enter entry=0x%lx, head=0x%lx\n",
++		(unsigned long)entry,
++                (unsigned long)head);
++
++	list_del(entry);
++	list_add(entry, head);
++
++	ZFCP_LOG_TRACE("exit\n");
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++#ifdef ZFCP_STAT_REQSIZES
++
++static int zfcp_statistics_clear(
++		zfcp_adapter_t *adapter,
++		struct list_head *head)
++{
++	int retval = 0;
++	unsigned long flags;
++	struct list_head *entry, *next_entry;
++	zfcp_statistics_t *stat;
++
++	write_lock_irqsave(&adapter->stat_lock, flags);
++	list_for_each_safe(entry, next_entry, head) {
++		stat = list_entry(entry, zfcp_statistics_t, list);
++		list_del(entry);
++		kfree(stat);
++	}
++	write_unlock_irqrestore(&adapter->stat_lock, flags);
++
++	return retval;
++}
++
++
++static inline void zfcp_statistics_new(
++		zfcp_adapter_t *adapter,
++		struct list_head *head,
++		u32 num)
++{
++	zfcp_statistics_t *stat;
++
++	stat = ZFCP_KMALLOC(sizeof(zfcp_statistics_t), GFP_ATOMIC);
++	if (stat) {
++		stat->num = num;
++		stat->hits = 1;
++		list_add_tail(&stat->list, head);
++	} else	atomic_inc(&adapter->stat_errors);
++}
++
++/**
++ * list_for_some_prev   -       iterate over a list backwards
++ * 				starting somewhere in the middle
++ *				of the list
++ * @pos:        the &list_t to use as a loop counter.
++ * @middle:	the &list_t pointing to the antecessor to start at
++ * @head:       the head for your list.
++ */
++#define list_for_some_prev(pos, middle, head) \
++	for (pos = (middle)->prev, prefetch(pos->prev); pos != (head); \
++		pos = pos->prev, prefetch(pos->prev))
++
++/*
++ * Sort list if necessary to find frequently used entries quicker.
++ * Since counters are only increased by one, sorting can be implemented
++ * in a quite efficient way. It usually comprimises swapping positions
++ * of the given entry with its antecessor, if at all. In rare cases
++ * (= if there is a series of antecessors with identical counter values
++ * which are in turn less than the value hold by the current entry)
++ * searching for the position where we want to move the current entry to
++ * takes more than one hop back through the list. As to the overall
++ * performance of our statistics this is not a big deal.
++ * As a side-effect, we provide statistics sorted by hits to the user.
++ */
++static inline void zfcp_statistics_sort(
++		struct list_head *head,
++		struct list_head *entry,
++		zfcp_statistics_t *stat)
++{
++	zfcp_statistics_t *stat_sort = NULL;
++	struct list_head *entry_sort = NULL;
++
++	list_for_some_prev(entry_sort, entry, head) {
++		stat_sort = list_entry(entry_sort, zfcp_statistics_t, list);
++		if (stat_sort->hits >= stat->hits)
++			break;
++	}
++	if (stat_sort &&
++	    entry->prev != entry_sort)
++		list_move(entry, entry_sort);
++}
++
++
++static void zfcp_statistics_inc(
++		zfcp_adapter_t *adapter,
++                struct list_head *head,
++		u32 num)
++{
++        unsigned long flags;
++        zfcp_statistics_t *stat;
++        struct list_head *entry;
++
++	if (atomic_read(&adapter->stat_on) == 0)
++		return;
++
++        write_lock_irqsave(&adapter->stat_lock, flags);
++        list_for_each(entry, head) {
++                stat = list_entry(entry, zfcp_statistics_t, list);
++                if (stat->num == num) {
++                        stat->hits++;
++			zfcp_statistics_sort(head, entry, stat);
++                        goto unlock;
++                }
++        }
++        /* hits is initialized to 1 */
++        zfcp_statistics_new(adapter, head, num);
++unlock:
++        write_unlock_irqrestore(&adapter->stat_lock, flags);
++}
++
++
++static int zfcp_statistics_print(
++		zfcp_adapter_t *adapter,
++                struct list_head *head,
++		char *prefix,
++		char *buf,
++		int len,
++		int max)
++{
++        unsigned long flags;
++        zfcp_statistics_t *stat;
++        struct list_head *entry;
++
++        write_lock_irqsave(&adapter->stat_lock, flags);
++	list_for_each(entry, head) {
++		if (len > max - 26)
++			break;
++		stat = list_entry(entry, zfcp_statistics_t, list);
++		len += sprintf(buf + len, "%s 0x%08x: 0x%08x\n",
++			       prefix, stat->num, stat->hits);
++        }
++        write_unlock_irqrestore(&adapter->stat_lock, flags);
++
++        return len;
++}
++
++#endif // ZFCP_STAT_REQSIZES
++
++/*
++ * function:    zfcp_cfdc_dev_ioctl
++ *
++ * purpose:     Handle control file upload/download transaction via IOCTL interface
++ *
++ * returns:     0           - Operation completed successfuly
++ *              -ENOTTY     - Unknown IOCTL command
++ *              -EINVAL     - Invalid sense data record
++ *              -ENXIO      - The FCP adapter is not available
++ *              -EOPNOTSUPP - The FCP adapter does not have Control File support
++ *              -ENOMEM     - Insufficient memory
++ *              -EFAULT     - User space memory I/O operation fault
++ *              -EPERM      - Cannot create or queue FSF request or create SBALs
++ */
++int zfcp_cfdc_dev_ioctl(
++	struct inode *inode,
++	struct file *file,
++	unsigned int command,
++	unsigned long buffer)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	zfcp_cfdc_sense_data_t *sense_data, *sense_data_user;
++	zfcp_adapter_t *adapter;
++	zfcp_fsf_req_t *fsf_req = NULL;
++	zfcp_sg_list_t *sg_list = NULL;
++	u32 fsf_command, option;
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (inode=0x%lx file=0x%lx command=0x%x buffer=0x%lx)\n",
++		(unsigned long)inode, (unsigned long)file, command, buffer);
++
++	ZFCP_LOG_NORMAL(
++		"Control file data channel transaction opened\n");
++
++	sense_data = (zfcp_cfdc_sense_data_t*)ZFCP_KMALLOC(
++		sizeof(zfcp_cfdc_sense_data_t), GFP_KERNEL);
++	if (sense_data == NULL) {
++		ZFCP_LOG_NORMAL(
++			"Not enough memory for the sense data record\n");
++		retval = -ENOMEM;
++		goto out;
++	}
++
++	sg_list = (zfcp_sg_list_t*)ZFCP_KMALLOC(
++		sizeof(zfcp_sg_list_t), GFP_KERNEL);
++	if (sg_list == NULL) {
++		ZFCP_LOG_NORMAL(
++			"Not enough memory for the scatter-gather list\n");
++		retval = -ENOMEM;
++		goto out;
++	}
++
++	if (command != ZFCP_CFDC_IOC) {
++		ZFCP_LOG_NORMAL(
++			"IOC request code 0x%x is not valid\n",
++			command);
++		retval = -ENOTTY;
++		goto out;
++	}
++
++	if ((sense_data_user = (zfcp_cfdc_sense_data_t*)buffer) == NULL) {
++		ZFCP_LOG_NORMAL(
++			"Sense data record is required\n");
++		retval = -EINVAL;
++		goto out;
++	}
++
++	retval = copy_from_user(
++		sense_data, sense_data_user, sizeof(zfcp_cfdc_sense_data_t));
++	if (retval) {
++		ZFCP_LOG_NORMAL(
++			"Cannot copy sense data record from user space memory\n");
++		retval = -EFAULT;
++		goto out;
++	}
++
++	if (sense_data->signature != ZFCP_CFDC_SIGNATURE) {
++		ZFCP_LOG_NORMAL(
++			"No valid sense data request signature 0x%08x found\n",
++			ZFCP_CFDC_SIGNATURE);
++		retval = -EINVAL;
++		goto out;
++	}
++
++	switch (sense_data->command) {
++
++	case ZFCP_CFDC_CMND_DOWNLOAD_NORMAL:
++		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
++		option = FSF_CFDC_OPTION_NORMAL_MODE;
++		break;
++
++	case ZFCP_CFDC_CMND_DOWNLOAD_FORCE:
++		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
++		option = FSF_CFDC_OPTION_FORCE;
++		break;
++
++	case ZFCP_CFDC_CMND_FULL_ACCESS:
++		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
++		option = FSF_CFDC_OPTION_FULL_ACCESS;
++		break;
++
++	case ZFCP_CFDC_CMND_RESTRICTED_ACCESS:
++		fsf_command = FSF_QTCB_DOWNLOAD_CONTROL_FILE;
++		option = FSF_CFDC_OPTION_RESTRICTED_ACCESS;
++		break;
++
++	case ZFCP_CFDC_CMND_UPLOAD:
++		fsf_command = FSF_QTCB_UPLOAD_CONTROL_FILE;
++		option = 0;
++		break;
++
++	default:
++		ZFCP_LOG_NORMAL(
++			"Command code 0x%08x is not valid\n",
++			sense_data->command);
++		retval = -EINVAL;
++		goto out;
++	}
++
++	retval = zfcp_adapter_enqueue(sense_data->devno, &adapter);
++	if (retval != 0) {
++		if (retval != ZFCP_KNOWN) {
++			ZFCP_LOG_NORMAL(
++				"Cannot enqueue the FCP adapter (devno=0x%04x)\n",
++				sense_data->devno);
++			retval = -ENXIO;
++			goto out;
++		}
++	} else {
++		retval = zfcp_erp_adapter_reopen(adapter, 0);
++		if (retval < 0) {
++			ZFCP_LOG_NORMAL(
++				"Cannot reopen the FCP adapter (devno=0x%04x)\n",
++				adapter->devno);
++			retval = -ENXIO;
++			goto out;
++		}
++		zfcp_erp_wait(adapter);
++	}
++
++	if (!atomic_test_mask(ZFCP_STATUS_COMMON_RUNNING, &adapter->status) ||
++	    atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED, &adapter->status)) {
++		ZFCP_LOG_NORMAL(
++			"The FCP adapter is not available (devno=0x%04x)\n",
++			adapter->devno);
++		retval = -ENXIO;
++		goto out;
++	}
++
++	if (adapter->devinfo.sid_data.dev_model != ZFCP_DEVICE_MODEL_PRIV) {
++		ZFCP_LOG_NORMAL(
++			"Control file upload/download is accepted "
++			"only on privileged subchannels (devno=0x%04x)\n",
++			adapter->devno);
++		retval = -ENXIO;
++		goto out;
++	}
++
++	if (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE) {
++		retval = zfcp_sg_list_alloc(sg_list, ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
++		if (retval) {
++			ZFCP_LOG_NORMAL(
++				"Not enough memory for the scatter-gather list\n");
++			retval = -ENOMEM;
++			goto out;
++		}
++	}
++
++	if ((sense_data->command & ZFCP_CFDC_DOWNLOAD) &&
++	    (sense_data->command & ZFCP_CFDC_WITH_CONTROL_FILE)) {
++		retval = zfcp_sg_list_copy_from_user(
++			sg_list, &sense_data_user->control_file,
++			ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
++		if (retval) {
++			ZFCP_LOG_NORMAL(
++				"Cannot copy control file from user space memory\n");
++			retval = -EFAULT;
++			goto out;
++		}
++	}
++
++	retval = zfcp_fsf_control_file(
++		adapter, &fsf_req, fsf_command, option, sg_list);
++	if (retval == -EOPNOTSUPP) {
++		ZFCP_LOG_NORMAL(
++			"Specified adapter does not support control file\n");
++		goto out;
++	} else if (retval != 0) {
++		ZFCP_LOG_NORMAL(
++			"Cannot create or queue FSF request or create SBALs\n");
++		retval = -EPERM;
++		goto out;
++	}
++
++	wait_event(fsf_req->completion_wq,
++		   fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
++
++	if ((fsf_req->qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
++	    (fsf_req->qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) {
++		retval = -ENXIO;
++		goto out;
++	}
++
++	sense_data->fsf_status = fsf_req->qtcb->header.fsf_status;
++	memcpy(&sense_data->fsf_status_qual,
++	       &fsf_req->qtcb->header.fsf_status_qual,
++	       sizeof(fsf_status_qual_t));
++	memcpy(&sense_data->payloads, &fsf_req->qtcb->bottom.support.els, 256);
++
++	retval = copy_to_user(
++		sense_data_user, sense_data, sizeof(zfcp_cfdc_sense_data_t));
++	if (retval) {
++		ZFCP_LOG_NORMAL(
++			"Cannot copy sense data record to user space memory\n");
++		retval = -EFAULT;
++		goto out;
++	}
++
++	if (sense_data->command & ZFCP_CFDC_UPLOAD) {
++		retval = zfcp_sg_list_copy_to_user(
++			&sense_data_user->control_file, sg_list,
++			ZFCP_CFDC_MAX_CONTROL_FILE_SIZE);
++		if (retval) {
++			ZFCP_LOG_NORMAL(
++				"Cannot copy control file to user space memory\n");
++			retval = -EFAULT;
++			goto out;
++		}
++	}
++
++out:
++	if (fsf_req) {
++		retval = zfcp_fsf_req_cleanup(fsf_req);
++		if (retval) {
++			ZFCP_LOG_NORMAL(
++				"bug: Could not remove the FSF request "
++				"(devno=0x%04x fsf_req=0x%lx)\n",
++				adapter->devno,
++				(unsigned long)fsf_req);
++			retval = -EPERM;
++		}
++	}
++
++	if (sg_list != NULL) {
++		zfcp_sg_list_free(sg_list);
++		ZFCP_KFREE(sg_list, sizeof(zfcp_sg_list_t));
++	}
++
++	if (sense_data != NULL)
++		ZFCP_KFREE(sense_data, sizeof(zfcp_cfdc_sense_data_t));
++
++	ZFCP_LOG_NORMAL(
++		"Control file data channel transaction closed\n");
++
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_sg_list_alloc
++ *
++ * purpose:     Create a scatter-gather list of the specified size
++ *
++ * returns:     0       - Scatter gather list is created
++ *              -ENOMEM - Insufficient memory (*list_ptr is then set to NULL)
++ */
++static inline int zfcp_sg_list_alloc(zfcp_sg_list_t *sg_list, size_t size)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	struct scatterlist *sg;
++	int i;
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (sg_list=0x%lx size=%ld)\n",
++		(unsigned long)sg_list, size);
++
++	sg_list->count = size >> PAGE_SHIFT;
++	if (size & ~PAGE_MASK)
++		sg_list->count++;
++	sg_list->sg = (struct scatterlist*)ZFCP_KMALLOC(
++		sg_list->count * sizeof(struct scatterlist), GFP_KERNEL);
++	if (sg_list->sg == NULL) {
++		ZFCP_LOG_INFO("Out of memory!\n");
++		retval = -ENOMEM;
++		goto out;
++	}
++
++	for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) {
++		if (i < sg_list->count - 1) {
++			sg->length = PAGE_SIZE;
++			sg->address = (char*)ZFCP_GET_ZEROED_PAGE(GFP_KERNEL);
++		} else {
++			sg->length = size & ~PAGE_MASK;
++			sg->address = (char*)ZFCP_KMALLOC(sg->length, GFP_KERNEL);
++		}
++		if (sg->address == NULL) {
++			while (sg-- != sg_list->sg)
++				ZFCP_FREE_PAGE((unsigned long)sg->address);
++			ZFCP_KFREE(sg_list->sg,
++				sg_list->count * sizeof(struct scatterlist));
++			ZFCP_LOG_INFO("Out of memory!\n");
++			retval = -ENOMEM;
++			goto out;
++		}
++	}
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_sg_list_free
++ *
++ * purpose:     Destroy a scatter-gather list and release memory
++ *
++ * returns:     Always 0
++ */
++static inline int zfcp_sg_list_free(zfcp_sg_list_t *sg_list)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	struct scatterlist *sg;
++	int i;
++	int retval = 0;
++
++	ZFCP_LOG_TRACE("enter (sg_list=0x%lx)\n", (unsigned long)sg_list);
++
++	if ((sg_list->sg == NULL) || (sg_list->count == 0))
++		goto out;
++
++	for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++)
++		ZFCP_FREE_PAGE((unsigned long)sg->address);
++	ZFCP_KFREE(sg_list->sg, sg_list->count * sizeof(struct scatterlist));
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_sg_list_copy_from_user
++ *
++ * purpose:     Copy data from user space memory to the scatter-gather list
++ *
++ * returns:     0       - The data has been copied from user
++ *              -EFAULT - Memory I/O operation fault
++ */
++static inline int zfcp_sg_list_copy_from_user(
++	zfcp_sg_list_t *sg_list, void *buffer, size_t size)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	struct scatterlist *sg;
++	unsigned int length;
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (sg_list=0x%lx buffer=0x%lx size=%ld)\n",
++		(unsigned long)sg_list, (unsigned long)buffer, size);
++
++	for (sg = sg_list->sg; size > 0; sg++) {
++		length = min((unsigned int)size, sg->length);
++		if (copy_from_user(sg->address, buffer, length)) {
++			ZFCP_LOG_INFO("Memory error (copy_from_user)\n");
++			retval = -EFAULT;
++			goto out;
++		}
++		buffer += length;
++		size -= length;
++	}
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++/*
++ * function:    zfcp_sg_list_copy_to_user
++ *
++ * purpose:     Copy data from the scatter-gather list to user space memory
++ *
++ * returns:     0       - The data has been copied to user
++ *              -EFAULT - Memory I/O operation fault
++ */
++static inline int zfcp_sg_list_copy_to_user(
++	void *buffer, zfcp_sg_list_t *sg_list, size_t size)
++{
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_SCSI
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_SCSI
++
++	struct scatterlist *sg;
++	unsigned int length;
++	int retval = 0;
++
++	ZFCP_LOG_TRACE(
++		"enter (buffer=0x%lx sg_list=0x%lx size=%ld)\n",
++		(unsigned long)buffer, (unsigned long)sg_list, size);
++
++	for (sg = sg_list->sg; size > 0; sg++) {
++		length = min((unsigned int)size, sg->length);
++		if (copy_to_user(buffer, sg->address, length)) {
++			ZFCP_LOG_INFO("Memory error (copy_to_user)\n");
++			retval = -EFAULT;
++			goto out;
++		}
++		buffer += length;
++		size -= length;
++	}
++
++out:
++	ZFCP_LOG_TRACE("exit (%i)\n", retval);
++
++	return retval;
++
++#undef ZFCP_LOG_AREA
++#undef ZFCP_LOG_AREA_PREFIX
++}
++
++
++EXPORT_SYMBOL(zfcp_data);
++
++/*
++ * Overrides for Emacs so that we get a uniform 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/scsi/zfcp_zh.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/scsi/zfcp_zh.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,951 @@
++/* 
++ * $Id: zfcp_zh.c,v 1.3.2.4 2004/09/20 16:20:30 aherrman Exp $
++ *
++ * Module providing an interface for HBA API (FC-HBA) implementation
++ * to the zfcp driver.
++ *
++ * (C) Copyright IBM Corp. 2002, 2003
++ *
++ * 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 of the License, or
++ * (at your option) any later version. See the file COPYING for more
++ * information.
++ *
++ * Authors:
++ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
++ *       Andreas Herrmann <aherrman at de.ibm.com>
++ */
++#include "zfcp.h"
++#include "zfcp_zh.h"
++
++#include <linux/module.h>
++
++#define ZFCP_LOG_AREA			ZFCP_LOG_AREA_OTHER
++#define ZFCP_LOG_AREA_PREFIX		ZFCP_LOG_AREA_PREFIX_OTHER
++
++extern zfcp_data_t zfcp_data;
++
++struct zfcp_callbacks zfcp_callback = { };
++
++/**
++ * zfcp_callback_do_adapter_add - callback wrapper
++ * @*filp: passed through to the callback
++ * @*a: adapter that was added
++ *
++ * We do not need irq safe spinlocks here, since we onyl do a read_lock. All
++ * writers must use the _irq version though.
++ */
++void zfcp_callback_do_adapter_add(struct file *filp, const zfcp_adapter_t *a)
++{
++	unsigned long flags;
++	
++	read_lock_irqsave(&zfcp_callback.lock, flags);
++
++	if ((zfcp_callback.callbacks != NULL) &&
++	    (zfcp_callback.callbacks->adapter_add != NULL)) {
++		zfcp_callback.callbacks->adapter_add(filp, a->devno, a->wwnn,
++						     a->wwpn);
++	}
++
++	read_unlock_irqrestore(&zfcp_callback.lock, flags);
++}
++
++/**
++ * zfcp_callback_do_port_add - callback wrapper
++ * @*filp: passed through to the callback
++ * @*a: adapter the port was added to
++ * @*p: port that was added
++ */
++void zfcp_callback_do_port_add(struct file *filp, const zfcp_adapter_t *a,
++			       const zfcp_port_t *p)
++{
++	unsigned long flags;
++
++	read_lock_irqsave(&zfcp_callback.lock, flags);
++
++	if ((zfcp_callback.callbacks != NULL) &&
++	    (zfcp_callback.callbacks->port_add != NULL)) {
++		zfcp_callback.callbacks->port_add(filp, a->devno, p->wwpn,
++						  p->wwnn, p->d_id);
++	}
++
++	read_unlock_irqrestore(&zfcp_callback.lock, flags);
++}
++
++/**
++ * zfcp_callback_do_unit_add - callback wrapper
++ * @*filp: passed through to the callback
++ * @*a: Adapter the port belongs to
++ * @*p: Port the unit belongs to
++ * @*u: unit that was added
++ */
++void zfcp_callback_do_unit_add(struct file *filp, const zfcp_adapter_t *a,
++			       const zfcp_port_t *p, const zfcp_unit_t *u)
++{
++	unsigned long flags;
++
++	read_lock_irqsave(&zfcp_callback.lock, flags);
++
++	if ((zfcp_callback.callbacks != NULL) &&
++	    (zfcp_callback.callbacks->unit_add != NULL)) {
++		zfcp_callback.callbacks->unit_add(filp, a->devno, p->wwpn,
++				u->fcp_lun, a->scsi_host->host_no,
++				u->device->channel, u->device->id,
++				u->device->lun);
++	}
++
++	read_unlock_irqrestore(&zfcp_callback.lock, flags);
++}
++
++/**
++ * zfcp_callback_do_incomming_els - callback wrapper
++ * @*a: adapter the ELS was recieved
++ * @*v: pointer to the ELS payload
++ */
++void zfcp_callback_do_incomming_els(const zfcp_adapter_t *a, const void *v)
++{
++	unsigned long flags;
++
++	read_lock_irqsave(&zfcp_callback.lock, flags);
++
++	if ((zfcp_callback.callbacks != NULL) &&
++	    (zfcp_callback.callbacks->incomming_els != NULL)) {
++		zfcp_callback.callbacks->incomming_els(a->devno, a->s_id, v);
++	}
++
++	read_unlock_irqrestore(&zfcp_callback.lock, flags);
++}
++
++/**
++ * zfcp_callback_do_link_down - callback wrapper
++ * @*a: adapter that lost the link
++ */
++void zfcp_callback_do_link_down(const zfcp_adapter_t *a)
++{
++	unsigned long flags;
++
++	read_lock_irqsave(&zfcp_callback.lock, flags);
++
++	if ((zfcp_callback.callbacks != NULL) &&
++	    (zfcp_callback.callbacks->link_down != NULL)) {
++		zfcp_callback.callbacks->link_down(a->s_id);
++	}
++
++	read_unlock_irqrestore(&zfcp_callback.lock, flags);
++}
++
++/**
++ * zfcp_callback_do_link_up - callback wrapper
++ * @*a: adapter with link up event
++ */
++void zfcp_callback_do_link_up(const zfcp_adapter_t *a)
++{
++	unsigned long flags;
++
++	read_lock_irqsave(&zfcp_callback.lock, flags);
++
++	if ((zfcp_callback.callbacks != NULL) &&
++	    (zfcp_callback.callbacks->link_up != NULL)) {
++		zfcp_callback.callbacks->link_up(a->s_id);
++	}
++
++	read_unlock_irqrestore(&zfcp_callback.lock, flags);
++}
++
++/**
++ * zfcp_search_unit - search for a unit
++ * @*port: pointer to port structure of port where unit is attached to
++ * @lun: FC LUN of the unit to search for
++ * @**unit: address to write pointer to found unit structure to
++ * Return: 0 on success, -E* code else
++ * Locks: lock/unlock of port->unit_list_lock
++ *
++ * Search for an unit and return its address.
++ * See also zfcp_search_port_and_unit().
++ */
++static int zfcp_search_unit(zfcp_port_t *port, fcp_lun_t lun,
++			    zfcp_unit_t **unit)
++{
++	zfcp_unit_t *u;
++	unsigned long flags;
++	int ret = 0;
++
++	*unit = NULL;
++
++	read_lock_irqsave(&port->unit_list_lock, flags);
++	ZFCP_FOR_EACH_UNIT(port, u)
++	{
++		if (u->fcp_lun == lun) {
++			*unit = u;
++		}
++	}
++	read_unlock_irqrestore(&port->unit_list_lock, flags);
++	if (NULL == *unit) {
++		ret = -ENOUNI;
++	}
++	
++	return ret;
++}
++
++/**
++ * zfcp_search_port_unit - search for a port and unit
++ * @*adapter: pointer to adapter structure of adapter where port is attached to
++ * @wwpn: of the port to search for
++ * @lun: of the unit to search for, ignored if @**unit == NULL
++ * @**port: address to write pointer to the found port structure to
++ * @**unit: address to write pointer to the found unit structure to
++ *	(iff @**unit != NULL)
++ * Return: 0 on success, -E* code else
++ * Locks: lock/unlock of adapter->port_list_lock
++ *
++ * Search for port and unit and return their addresses.
++ * If @**unit == NULL search only for port.
++ * If @**unit != NULL search also for unit.
++ * See also zfcp_search_adapter_port_unit() and zfcp_search_unit().
++ */
++static int zfcp_search_port_unit(zfcp_adapter_t *adapter, wwn_t wwpn,
++				 fcp_lun_t lun,  zfcp_port_t **port,
++				 zfcp_unit_t **unit)
++{
++	zfcp_port_t *p;
++	unsigned long flags;
++	int ret = 0;
++
++	*port = NULL;
++
++	read_lock_irqsave(&adapter->port_list_lock, flags);
++	ZFCP_FOR_EACH_PORT(adapter, p)
++	{
++		if (p->wwpn == wwpn) {
++			*port = p;
++		}
++	}
++	read_unlock_irqrestore(&adapter->port_list_lock, flags);
++
++	if (NULL == *port) {
++		ret = -ENOPOR;
++	} else {
++		if (NULL != unit) {
++			ret = zfcp_search_unit(*port, lun, unit);
++		}
++	}
++
++	return ret;
++}
++
++/**
++ * zfcp_search_adapter_port_unit - search for an adapter, port and unit
++ * @devno: of the adapter to search for
++ * @wwpn: of the port to search for, ignored if **port == NULL
++ * @lun: of the unit to search for, ignored if **port == NULL || **unit == NULL
++ * @**adapter: address to write pointer to found adapter structure to
++ * @**port: address to write pointer to found port structure to,
++ *	(iff **port != NULL)
++ * @**unit: address to write pointer to found unit structure to,
++ *	(iff **port != NULL && **unit != NULL)
++ * Return: 0 on success, -E* code else
++ * Locks: lock/unlock of zfcp_data.adapter_list_lock
++ *
++ * Search for an adapter, port and unit and return their addresses.
++ * If @**port == NULL, search only for an adapter.
++ * If @**port != NULL, search also for port.
++ * If @**port != NULL and @**unit != NULL search also for port and unit.
++ * See also zfcp_search_port_unit().
++ */
++static int zfcp_search_adapter_port_unit(devno_t devno, wwn_t wwpn,
++					 fcp_lun_t lun,
++					 zfcp_adapter_t **adapter,
++					 zfcp_port_t **port, zfcp_unit_t **unit)
++{
++	zfcp_adapter_t *a;
++	unsigned long flags;
++	int ret = 0;
++
++	if (NULL == adapter) {
++		return -EINVAL;
++	}
++
++	*adapter = NULL;
++	
++	read_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
++	ZFCP_FOR_EACH_ADAPTER(a)
++	{
++		if (a->devno == devno) {
++			if (!atomic_test_mask(ZFCP_STATUS_COMMON_ERP_FAILED,
++					      &a->status) &&
++			    atomic_test_mask((ZFCP_STATUS_COMMON_RUNNING |
++					      ZFCP_STATUS_COMMON_UNBLOCKED),
++					     &a->status)) {
++				*adapter = a;
++			}
++			break;
++		}
++	}
++	read_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
++	if (NULL == *adapter) {
++		ret = -ENOADA;
++	} else {
++		if (NULL != port) {
++			ret = zfcp_search_port_unit(*adapter, wwpn, lun,
++						    port, unit);
++		}
++	}
++
++	return ret;
++}
++
++/**
++ * get_config_units - create unit config events
++ * @*filp: passed through to callback
++ * @*adapter: the unit belongs to
++ * @*port: the unit belongs to
++ *
++ * generate one unit add event for each unit below the passed port
++ */
++static inline int get_config_units(struct file *filp, zfcp_adapter_t *adapter,
++				   zfcp_port_t *port)
++{
++	zfcp_unit_t *unit;
++	unsigned long flags;
++	int ret = 0;
++	
++	read_lock_irqsave(&port->unit_list_lock, flags);
++
++	ZFCP_FOR_EACH_UNIT(port, unit)
++	{
++		if (unit->device != NULL) {
++			zfcp_callback_do_unit_add(filp, adapter, port, unit);
++			++ret;
++		}
++	}
++
++	read_unlock_irqrestore(&port->unit_list_lock, flags);
++
++	return ret;
++}
++
++/**
++ * get_config_ports - create port config events, or search for a port.
++ * @*filp: passed through to callback
++ * @*adapter: the port belongs to
++ * @wwpn: generate port add events if 0, or search port and get_config_units()
++ */
++static inline int get_config_ports(struct file *filp, zfcp_adapter_t *adapter,
++				   wwn_t wwpn, unsigned int config_flags)
++{
++	zfcp_port_t *port;
++	unsigned long flags;
++	int ret = 0;
++
++	read_lock_irqsave(&adapter->port_list_lock, flags);
++
++	ZFCP_FOR_EACH_PORT(adapter, port)
++	{
++		if (ZH_GET_CONFIG_PORTS == config_flags) {
++			/* ignore name server port */
++			if (0 != port->wwpn) {
++				zfcp_callback_do_port_add(filp, adapter, port);
++				++ret;
++			}
++		} else {
++			if (port->wwpn != wwpn) {
++				ret = -ENOPOR;
++			} else {
++				ret = get_config_units(filp, adapter, port);
++				break;
++			}
++		}
++	}
++
++	read_unlock_irqrestore(&adapter->port_list_lock, flags);
++
++	return ret;
++}
++
++/**
++ * zfcp_zh_get_config - Prepare config for userspace
++ * @*filp: passed through to callback
++ * @devno: generate adapter add events if 0, or search and get_config_ports()
++ * @wwpn: passed to get_config_ports()
++ *
++ * TNG get_config method, uses the callback methods and so generates
++ * events for each configured item. The events are private to the
++ * passed file descriptor.
++ */
++int zfcp_zh_get_config(struct file *filp, devno_t devno, wwn_t wwpn,
++		unsigned int config_flags)
++{
++	zfcp_adapter_t *adapter;
++	unsigned long flags;
++	int ret = 0;
++
++	read_lock_irqsave(&zfcp_data.adapter_list_lock, flags);
++
++	ZFCP_FOR_EACH_ADAPTER(adapter)
++	{
++		if (ZH_GET_CONFIG_ADAPTERS == config_flags) {
++			zfcp_callback_do_adapter_add(filp, adapter);
++			++ret;
++		} else {
++			if (adapter->devno != devno) {
++				ret = -ENOADA;
++			} else {
++				ret = get_config_ports(filp, adapter, wwpn,
++						       config_flags);
++				break;
++			}
++				
++		}
++	}
++		
++	read_unlock_irqrestore(&zfcp_data.adapter_list_lock, flags);
++
++	return ret;
++}
++
++/**
++ * zfcp_zh_get_adapter_attributes - provide data for the api call
++ * @devno: of the adapter 
++ * @attr: pointer to struct zfcp_adapter_attributes to return attributes
++ * Return: 0 on success, -E* else
++ * Context: user
++ * Locks: lock/unlock zfcp_data.adapter_list_lock
++ */
++int zfcp_zh_get_adapter_attributes(devno_t devno,
++				   struct zfcp_adapter_attributes *attr)
++{
++	zfcp_adapter_t *adapter;
++	int ret = 0;
++	
++	memset(attr, 0, sizeof(*attr));
++
++	ret = zfcp_search_adapter_port_unit(devno, 0, 0, &adapter, NULL, NULL);
++	if (ret != 0) {
++		return ret;
++	}
++
++	strcpy(attr->manufacturer, "IBM");
++	strncpy(attr->serial_number, adapter->serial_number, 32);
++	switch (adapter->hydra_version) {
++	case FSF_ADAPTER_TYPE_FICON:
++		strcpy(attr->model, "FICON FCP");
++		break;
++	case FSF_ADAPTER_TYPE_FICON_EXPRESS:
++		strcpy(attr->model, "FICON Express FCP");
++		break;
++	}
++	strcpy(attr->model_description, "zSeries Fibre Channel Adapter");
++	attr->node_wwn = adapter->wwnn;
++	sprintf(attr->hardware_version, "0x%x",
++		adapter->hardware_version);
++	sprintf(attr->driver_version, "0x%x", zfcp_data.driver_version);
++	sprintf(attr->firmware_version, "0x%x",
++		adapter->fsf_lic_version);
++	attr->vendor_specific_id = 42;
++	attr->number_of_ports = 1;
++	strcpy(attr->driver_name, "zfcp.o");
++	/* option_rom_version not used, node_symbolic_name not set */
++
++	return ret;
++}
++
++/**
++ * zfcp_zh_get_port_statistics - Retrieve statistics of adapter port
++ * @devno: adapter of which port data should be reported
++ * @stat: pointer to struct zfcp_port_statistics to return statistics
++ * Return: 0 on success,  -E* else
++ * Context: user
++ * Locks: lock/unlock zfcp_data.adapter_list_lock
++ */
++int zfcp_zh_get_port_statistics(devno_t devno,
++				struct zfcp_port_statistics *stat)
++{
++	zfcp_adapter_t *adapter;
++	fsf_qtcb_bottom_port_t data;
++	int ret = 0;
++
++	memset(stat, 0, sizeof(*stat));
++	
++	ret = zfcp_search_adapter_port_unit(devno, 0, 0, &adapter, NULL, NULL);
++	if (ret != 0) {
++		return ret;
++	}
++
++	if(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT){
++		ret = zfcp_fsf_exchange_port_data(adapter, &data);
++		if (0 == ret) {
++			/* convert fsf_qtcb_bottom_port into
++			   zfcp_port_statistics */
++			stat->last_reset = data.seconds_since_last_reset;
++			stat->tx_frames = data.tx_frames;
++			stat->tx_words = data.tx_words;
++			stat->rx_frames = data.rx_words;
++			stat->lip = data.lip;
++			stat->nos = data.nos;
++			stat->error_frames = data.error_frames;
++			stat->dumped_frames = data.dumped_frames;
++			stat->link_failure = data.link_failure;
++			stat->loss_of_sync = data.loss_of_sync;
++			stat->loss_of_signal = data.loss_of_signal;
++			stat->prim_seq_prot_error = data.psp_error_counts;
++			stat->invalid_tx_words = data.invalid_tx_words;
++			stat->invalid_crc = data.invalid_crcs;
++			stat->input_requests = data.input_requests;
++			stat->output_requests = data.output_requests;
++			stat->control_requests = data.control_requests;
++			stat->input_megabytes = data.input_mb;
++			stat->output_megabytes = data.output_mb;
++		}
++	} else {
++		ret = -EOPNOTSUPP;
++	}
++
++	return ret;
++}
++
++
++/**
++ * zfcp_zh_get_port_attributes - Retrieve attributes of adapter port
++ * @devno: adapter of which port data should be reported
++ * @attr: pointer to struct zfcp_port_attributes to return attributes
++ * Return: 0 on success,  -E* else
++ * Context: user
++ * Locks: lock/unlock zfcp_data.adapter_list_lock, adapter->port_list_lock
++ */
++int zfcp_zh_get_port_attributes(devno_t devno,
++				struct zfcp_port_attributes *attr)
++{
++	zfcp_adapter_t *adapter;
++	zfcp_port_t *port;
++	fsf_qtcb_bottom_port_t data;
++	unsigned long flags;
++	int ret = 0;
++
++	memset(attr, 0, sizeof(*attr));
++	
++	ret = zfcp_search_adapter_port_unit(devno, 0, 0, &adapter, NULL, NULL);
++	if (ret != 0) {
++		return ret;
++	}
++
++	attr->wwnn = adapter->wwnn;
++	attr->wwpn = adapter->wwpn;
++	attr->speed = adapter->fc_link_speed;
++	attr->discovered_ports = adapter->ports;
++
++	/* ignore nameserver port */
++	read_lock_irqsave(&adapter->port_list_lock, flags);
++	ZFCP_FOR_EACH_PORT(adapter, port)
++	{
++		if(port->wwpn == 0){
++			if(attr->discovered_ports)
++				--attr->discovered_ports;
++			break;
++		}
++	}
++	read_unlock_irqrestore(&adapter->port_list_lock, flags);
++
++	if(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT){
++		ret = zfcp_fsf_exchange_port_data(adapter, &data);
++		if (0 == ret) {
++			/* convert fsf_qtcb_bottom_port into
++			   zfcp_port_attributes */
++			attr->fcid = data.fc_port_id;
++			attr->type = data.port_type;
++			attr->state = data.port_state;
++			attr->supported_class_of_service =
++				data.class_of_service;
++			attr->supported_speed = data.supported_speed;
++			attr->max_frame_size = data.maximum_frame_size;
++			memcpy(&attr->supported_fc4_types,
++			       &data.supported_fc4_types, 32);
++			memcpy(&attr->active_fc4_types,
++			       &data.active_fc4_types, 32);
++		}
++	} else {
++		ret = -EOPNOTSUPP;
++	}
++	/* fabric_name and symbolic_name not set */
++
++	return ret;
++}
++
++/**
++ * zfcp_zh_get_discovered_port_attributes - Retrieve attributes of target port
++ * @devno: adapter for which port data should be reported
++ * @wwpn: wwn of discovered port
++ * @attr: pointer to struct zfcp_port_attributes to return attributes
++ * Return: 0 on success,  -E* else
++ * Context: user
++ * Locks: lock/unlock zfcp_data.adapter_list_lock, adapter->port_list_lock
++ */
++int zfcp_zh_get_dport_attributes(devno_t devno, wwn_t wwpn,
++				 struct zfcp_port_attributes *attr)
++{
++	zfcp_adapter_t *adapter;
++	zfcp_port_t *port;
++	struct ct_iu_ga_nxt *ct_iu_resp;
++	int ret;
++
++	memset(attr, 0, sizeof(*attr));
++
++	ret = zfcp_search_adapter_port_unit(devno, wwpn, 0,
++					    &adapter, &port, NULL);
++	if (ret != 0) {
++		return ret;
++	}
++
++	ct_iu_resp = kmalloc(sizeof(*ct_iu_resp), GFP_KERNEL);
++	if (0 == ct_iu_resp) {
++		return -ENOMEM;
++	}
++
++	ret = zfcp_ns_ga_nxt_request(port, ct_iu_resp);
++	if (0 == ret) {
++		attr->wwnn = port->wwnn;
++		attr->wwpn = port->wwpn;
++		attr->fabric_name = (wwn_t) ct_iu_resp->du.fabric_wwn;
++		attr->fcid = port->d_id;
++
++		/* map FC-GS-2 port types to HBA API
++		   port types */
++		switch(ct_iu_resp->du.port_type) {
++		case 0x01: /* N_Port */
++			attr->type = FSF_HBA_PORTTYPE_NPORT;
++		case 0x02: /* NL_Port */
++			attr->type = FSF_HBA_PORTTYPE_NLPORT;
++		case 0x81: /* F_Port */
++			attr->type = FSF_HBA_PORTTYPE_FPORT;
++		case 0x82: /* FL_Port */
++			attr->type = FSF_HBA_PORTTYPE_FLPORT;
++		case 0x03: /* F/NL_Port */
++		case 0x7f: /* Nx_Port */
++		case 0x84: /* E_Port */
++			attr->type = FSF_HBA_PORTTYPE_OTHER;
++		case 0x00: /* Unidentified */
++		default: /* reserved */
++			attr->type = FSF_HBA_PORTTYPE_UNKNOWN;
++		}
++
++		attr->state = FSF_HBA_PORTSTATE_UNKNOWN;
++		attr->supported_class_of_service = ct_iu_resp->du.cos;
++		memcpy(&attr->active_fc4_types, &ct_iu_resp->du.fc4_types, 32);
++		memcpy(&attr->symbolic_name,
++		       &ct_iu_resp->du.node_symbolic_name,
++		       ct_iu_resp->du.port_symbolic_name_length);
++	}
++	kfree(ct_iu_resp);
++
++	/* supported_speed, speed, max_frame_size, supported_fc4_types,
++	   discovered_ports not set */
++
++	return ret;
++}
++
++
++/**
++ * zfcp_zh_send_ct_handler() - handler for zfcp_zh_send_ct()
++ * @data: a pointer to struct zfcp_send_ct, It was set as handler_data
++ *	in zfcp_zh_send_ct().
++ * Context: interrupt
++ *
++ * This handler is called on completion of a send_ct request. We just wake up
++ * our own zfcp_zh_send_ct() function.
++ */
++static void zfcp_zh_send_ct_handler(unsigned long data)
++{
++
++        struct zfcp_send_ct *ct = (struct zfcp_send_ct *) data;
++	struct completion *wait = (struct completion*) ct->completion;
++
++	complete(wait);
++	return;
++}
++
++
++/**
++ * zfcp_zh_send_ct() - send a CT_IU containing FC-GS-4 command
++ * @devno: adapter for which port data should be reported
++ * @req: scatter-gather list with request data
++ * @req_count: number of elements in @req
++ * @resp: scatter-gather list for response data
++ * @resp_count: number of elements in @resp
++ * Return: 0 on success,  -E* else
++ * Context: user
++ * Locks: lock/unlock zfcp_data.adapter_list_lock
++ *
++ * Note: Currently only requests to the nameserver port are supported.
++ *	Other well-known ports currently have no representation in zfcp.
++ */
++int zfcp_zh_send_ct(devno_t devno,
++		    struct scatterlist *req, unsigned int req_count,
++		    struct scatterlist *resp, unsigned int resp_count)
++{
++	struct zfcp_send_ct ct;
++	zfcp_adapter_t *adapter;
++	struct ct_hdr *ct_header;
++	int ret;
++
++	DECLARE_COMPLETION(wait);
++
++	memset(&ct, 0, sizeof(ct));
++
++	ret = zfcp_search_adapter_port_unit(devno, 0, 0, &adapter, NULL, NULL);
++	if (ret != 0) {
++		return ret;
++	}
++
++	ct_header = (struct ct_hdr *) req[0].address;
++	if ((ct_header->gs_type != ZFCP_CT_DIRECTORY_SERVICE) ||
++	    (ct_header->gs_subtype != ZFCP_CT_NAME_SERVER)) {
++		/* currently only nameserver requests are supported */
++		ZFCP_LOG_NORMAL("Tried to send CT IU to other service than "
++				"Name Server Directory Service. This is "
++				"currently not supported.\n");
++		return -ENOPOR;
++	}
++
++	if (!adapter->nameserver_port) {
++		ZFCP_LOG_NORMAL("Name Server port not available\n.");
++		return -ENOPOR;
++	}
++	ct.port = adapter->nameserver_port;
++
++	ct.req = req;
++	ct.resp = resp;
++	ct.req_count = req_count;
++	ct.resp_count = resp_count;
++	ct.handler = zfcp_zh_send_ct_handler;
++	ct.handler_data = (unsigned long) &ct;
++	ct.completion = &wait;
++	ct.timeout = ZFCP_CT_TIMEOUT;
++
++	ret = zfcp_fsf_send_ct(&ct, 0, 0);
++	if (0 == ret) {
++		wait_for_completion(&wait);
++		if (ct.status)
++			ret = -EIO;
++		else
++			zfcp_check_ct_response((void *)resp->address);
++	}
++
++	return ret;
++}
++
++/**
++ * zfcp_zh_callbacks_register - register callbacks of zfcp_hbaapi
++ * @*cb: pointer to the callback structure
++ * Return: 0 on success, else -E* code
++ * Locks: lock/unlock of zfcp_callback.lock
++ * Context: user
++ */
++int zfcp_zh_callbacks_register(struct zfcp_zh_callbacks *cb)
++{
++	unsigned long flags;
++	int ret = 0;
++	
++	write_lock_irqsave(&zfcp_callback.lock, flags);
++
++	if (zfcp_callback.callbacks != NULL) {
++		ret = -EBUSY;
++	} else {
++		zfcp_callback.callbacks = cb;
++	}
++
++	write_unlock_irqrestore(&zfcp_callback.lock, flags);
++
++	return ret;
++}
++
++/**
++ * zfcp_zh_callbacks_unregister - unregister callbacks for zfcp_hbaapi
++ * @*cb: pointer to the callback structure
++ * Return: 0 on success, -E* code else
++ * Locks: lock/unlock of zfcp_callback.lock
++ * Context: user
++ */
++int zfcp_zh_callbacks_unregister(struct zfcp_zh_callbacks *cb)
++{
++	unsigned long flags;
++	int ret = 0;
++
++	write_lock_irqsave(&zfcp_callback.lock, flags);
++
++	if (zfcp_callback.callbacks == NULL) {
++		ret = -EBUSY;
++	} else {
++		if (zfcp_callback.callbacks != cb) {
++			ZFCP_LOG_DEBUG("Tried to unregister callbacks from "
++				       "different address.\n");
++			ret = -EINVAL;
++		} else {
++			zfcp_callback.callbacks = NULL;
++		}
++	}
++
++	write_unlock_irqrestore(&zfcp_callback.lock, flags);
++
++	return ret;
++}
++
++/**
++ * zfcp_zh_assert_fclun_zero - Assert that there is a FC LUN 0
++ * @devno: devno of the adapter
++ * @wwpn: wwpn of the discovered port
++ *
++ * Look for an unit at the passed adapter:port with FC LUN 0.
++ * Add it if it does not exist. This unit is needed for
++ * REPORT_LUNS.
++ *
++ * Note: No unit with a FC LUN 0 can be added for the same adpater and port
++ *	after this call. (We could add an "overwriteable" flag to the
++ *	zfcp_unit_t structure as a work-around for this.)
++ */
++int zfcp_zh_assert_fclun_zero(devno_t devno, wwn_t wwpn)
++{
++	int ret;
++	zfcp_config_record_t cfg;
++	zfcp_adapter_t *adapter;
++	zfcp_port_t *port;
++	zfcp_unit_t *unit;
++
++	ret = zfcp_search_adapter_port_unit(devno, wwpn, 0,
++					    &adapter, &port, &unit);
++	if (ret != -ENOUNI) {
++		return ret;
++	}
++
++	memset (&cfg, 0, sizeof(cfg));
++	cfg.devno = devno;
++	cfg.scsi_id = port->scsi_id;
++	cfg.wwpn = port->wwpn;
++	cfg.scsi_lun = port->max_scsi_lun +1;
++
++	ret = zfcp_config_parse_record_add(&cfg);
++	if (0 != ret) {
++		return ret;
++	}
++
++	/* wait until the unit is opened */
++	return zfcp_erp_wait(port->adapter);
++}
++
++/** 
++ * zfcp_zh_send_scsi - Send a SCSI command to an unit
++ * @devno: devno of the adapter
++ * @wwpn: WWPN of the discovered port the unit is attached to
++ * @lun: FC LUN of the unit to send the command to
++ * @cmnd: address of the prepared Scsi_Cmnd
++ * Return: 0 on success, > 0 on SCSI error, -E* code else
++ *
++ * FC LUN 0 is handled with extra care, to be able to send REPORT_LUNS.
++ */
++int zfcp_zh_send_scsi(devno_t devno, wwn_t wwpn, fcp_lun_t lun,
++		      Scsi_Cmnd *cmnd)
++{
++	int ret;
++	zfcp_adapter_t *adapter;
++	zfcp_port_t *port;
++	zfcp_unit_t *unit;
++	
++	ret = zfcp_search_adapter_port_unit(devno, wwpn, lun,
++					    &adapter, &port, &unit);
++	if (ret != 0) {
++		return ret;
++	}
++
++	ret = zfcp_scsi_command_sync(unit, cmnd);
++	if (ret < 0) {
++		return ret;
++	} else {
++		return cmnd->result;
++	}
++}
++
++/**
++ * zfcp_zh_send_els_handler() - handler for zfcp_zh_send_els()
++ * @data: a pointer to struct zfcp_send_els. It is set as handler_data
++ *	in zfcp_zh_send_els().
++ * Context: interrupt
++ *
++ * This handler is called on completion of a send_els request. We just wake up
++ * our own zfcp_zh_send_els() function.
++ */
++static void
++zfcp_zh_send_els_handler(unsigned long data)
++{
++  struct zfcp_send_els *els = (struct zfcp_send_els *) data;
++  complete(els->completion);
++}
++
++/**
++ * zfcp_zh_send_els - send an els to a port
++ * @devno: of the adapter to send via
++ * @wwpn: of the port to send to
++ * @send: scatterlist describing the els payload to be sent
++ * @send_count: number of elements in the send scatterlist
++ * @receive: scatterlist describing buffers for the reply payload
++ * @receive_count: number of elements in the receive scatterlist
++ * Return: 0 on success, -E* code else
++ * Locks: lock/unlock of zfcp_data.adapter_list_lock, adapter->port_list_lock
++ */
++int zfcp_zh_send_els(devno_t devno, wwn_t wwpn, struct scatterlist *send,
++		     unsigned int send_count, struct scatterlist *receive,
++		     unsigned int receive_count)
++{
++	int ret;
++	struct zfcp_send_els *req;
++	zfcp_adapter_t *adapter;
++	zfcp_port_t *port;
++
++	DECLARE_COMPLETION(wait);
++
++	ret = zfcp_search_adapter_port_unit(devno, wwpn, 0,
++					    &adapter, &port, NULL);
++	if (ret != 0) {
++		return ret;
++	}
++
++	req = kmalloc(sizeof(*req), GFP_KERNEL);
++	if (NULL == req) {
++		ret = -ENOMEM;
++		goto out;
++	}
++	memset(req, 0, sizeof(*req));
++
++	req->port = port;
++	req->req = send;
++	req->req_count = send_count;
++	req->resp = receive;
++	req->resp_count = receive_count;
++	req->handler = zfcp_zh_send_els_handler;
++	req->handler_data = (unsigned long) req;
++	req->completion = &wait;
++
++	ret = zfcp_fsf_send_els(req);
++	if (0 == ret) {
++		wait_for_completion(&wait);
++		if (req->status)
++			ret = -EIO;
++	}
++
++	kfree(req);
++
++	goto out;
++
++out:
++	return ret;
++}
++
++EXPORT_SYMBOL(zfcp_zh_callbacks_register);
++EXPORT_SYMBOL(zfcp_zh_callbacks_unregister);
++EXPORT_SYMBOL(zfcp_zh_get_config);
++EXPORT_SYMBOL(zfcp_zh_get_adapter_attributes);
++EXPORT_SYMBOL(zfcp_zh_get_port_attributes);
++EXPORT_SYMBOL(zfcp_zh_get_port_statistics);
++EXPORT_SYMBOL(zfcp_zh_get_dport_attributes);
++EXPORT_SYMBOL(zfcp_zh_send_ct);
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/scsi/zfcp_zh.h	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,171 @@
++/* 
++ * $Id: zfcp_zh.h,v 1.3.2.1 2004/01/26 17:26:34 mschwide Exp $
++ *
++ * Module providing an interface for HBA API (FC-HBA) implementation
++ * to the zfcp driver.
++ *
++ * (C) Copyright IBM Corp. 2002, 2003
++ *
++ * 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 of the License, or
++ * (at your option) any later version. See the file COPYING for more
++ * information.
++ *
++ * Authors:
++ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
++ *       Andreas Herrmann <aherrman at de.ibm.com>
++ */
++
++
++#ifndef _ZFCP_ZH_H_
++#define _ZFCP_ZH_H_
++
++#include "zfcp.h"
++#include <scsi/scsi_ioctl.h>
++#include <asm/scatterlist.h>
++
++/*
++ * Besides a punch of standard error codes we use some newly defined error
++ * codes.
++ */
++#define ENOADA 200 /* no such adapter */
++#define ENOPOR 201 /* no such port */
++#define ENOUNI 202 /* no such unit */
++
++/*
++ * flags for get_config
++ * */
++#define ZH_GET_CONFIG_ADAPTERS 0
++#define ZH_GET_CONFIG_PORTS 1
++#define ZH_GET_CONFIG_UNITS 2
++
++/**
++ * struct zfcp_zh_callbacks
++ * @adapter_add: callback for adapter add events
++ *
++ * structure containing all the callbacks
++ */
++struct zfcp_zh_callbacks
++{
++	void (*adapter_add) (struct file *, devno_t, wwn_t, wwn_t);
++	void (*port_add) (struct file *, devno_t, wwn_t, wwn_t, fc_id_t);
++	void (*unit_add) (struct file *, devno_t, wwn_t, fcp_lun_t,
++			  unsigned int, unsigned int,
++			  unsigned int, unsigned int);
++	void (*incomming_els) (const devno_t, const fc_id_t, const void *);
++	void (*link_down) (const fc_id_t);
++	void (*link_up) (const fc_id_t);
++};
++
++/**
++ * struct zfcp_callbacks
++ * @lock: rw-lock
++ * @callbacks: relevant callbacks into zh module
++ *
++ * callbacks and according lock
++ */
++struct zfcp_callbacks
++{
++	rwlock_t lock;
++	struct zfcp_zh_callbacks *callbacks;
++};
++
++extern struct zfcp_callbacks zfcp_callback;
++
++/**
++ * struct zfcp_adapter_attributes
++ */
++struct zfcp_adapter_attributes
++{
++	char		manufacturer[64];
++	char		serial_number[64];
++	char		model[256];
++	char		model_description[256];
++	wwn_t		node_wwn;
++	char		node_symbolic_name[256];
++	char		hardware_version[256];
++	char		driver_version[256];
++	char		option_rom_version[256];
++	char		firmware_version[256];
++	u32		vendor_specific_id;
++	u32		number_of_ports;
++	char		driver_name[256];
++};
++
++/**
++ * struct zfcp_port_attributes
++ */
++struct zfcp_port_attributes {
++	wwn_t wwnn;
++	wwn_t wwpn;
++	wwn_t fabric_name;
++	u32 fcid;
++	u32 type;
++	u32 state;
++	u32 supported_class_of_service;
++	u32 supported_speed;
++	u32 speed;
++	u32 max_frame_size;
++	u32 discovered_ports;
++	u8 supported_fc4_types[32];
++	u8 active_fc4_types[32];
++	char symbolic_name[256];
++};
++
++/**
++ * struct zfcp_port_statistics
++ */
++struct zfcp_port_statistics {
++	u64 last_reset;
++	u64 tx_frames;
++	u64 tx_words;
++	u64 rx_frames;
++	u64 rx_words;
++	u64 lip;
++	u64 nos;
++	u64 error_frames;
++	u64 dumped_frames;
++	u64 link_failure;
++	u64 loss_of_sync;
++	u64 loss_of_signal;
++	u64 prim_seq_prot_error;
++	u64 invalid_tx_words;
++	u64 invalid_crc;
++	u64 input_requests;
++	u64 output_requests;
++	u64 control_requests;
++	u64 input_megabytes;
++	u64 output_megabytes;
++};
++
++/*
++ * functions needed by zfcp_hbaapi in zfcp
++ */
++int zfcp_zh_callbacks_register(struct zfcp_zh_callbacks *);
++int zfcp_zh_callbacks_unregister(struct zfcp_zh_callbacks *);
++int zfcp_zh_get_config(struct file *, devno_t, wwn_t, unsigned int);
++int zfcp_zh_get_adapter_attributes(devno_t, struct zfcp_adapter_attributes *);
++int zfcp_zh_get_port_attributes(devno_t, struct zfcp_port_attributes *);
++int zfcp_zh_get_port_statistics(devno_t, struct zfcp_port_statistics *);
++int zfcp_zh_get_dport_attributes(devno_t, wwn_t, struct zfcp_port_attributes *);
++int zfcp_zh_send_ct(devno_t, struct scatterlist *, unsigned int,
++		    struct scatterlist *, unsigned int);
++int zfcp_zh_send_els(devno_t, wwn_t, struct scatterlist*, unsigned int,
++		     struct scatterlist*, unsigned int);
++int zfcp_zh_send_scsi(devno_t, wwn_t, fcp_lun_t, Scsi_Cmnd *);
++int zfcp_zh_assert_fclun_zero(devno_t, wwn_t); /* needed for ReportLUNs */
++
++/*
++ * functions needed for callbacks into zfcp_hbaapi
++ */
++void zfcp_callback_do_adapter_add(struct file *, const zfcp_adapter_t *);
++void zfcp_callback_do_port_add(struct file *, const zfcp_adapter_t *,
++			       const zfcp_port_t *);
++void zfcp_callback_do_unit_add(struct file *, const zfcp_adapter_t *,
++			       const zfcp_port_t *, const zfcp_unit_t *);
++void zfcp_callback_do_incomming_els(const zfcp_adapter_t *, const void *);
++void zfcp_callback_do_link_down(const zfcp_adapter_t *);
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/scsi/zh.h	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,512 @@
++/* 
++ * $Id: zh.h,v 1.7.2.2 2004/03/24 11:18:00 aherrman Exp $
++ *
++ * Module providing an interface for HBA API (FC-HBA) implementation
++ * to the zfcp driver.
++ *
++ * (C) Copyright IBM Corp. 2003
++ *
++ * 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 of the License, or
++ * (at your option) any later version. See the file COPYING for more
++ * information.
++ *
++ * Authors:
++ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
++ *       Andreas Herrmann <aherrman at de.ibm.com>
++ */
++
++/*
++ * We expect wwn to be a 64 bit type and not to be an array of 8 characters.
++ * Conversion of wwn thus has to be done in vendor/os specific library.
++ */
++
++#ifndef _ZH_H_
++#define _ZH_H_
++
++#include <linux/ioctl.h>
++#include <asm/string.h>
++
++#include "zfcp_zh.h"
++
++typedef u64 devid_t;
++
++#define DEVID_TO_DEVNO(devid) (devno_t)(devid & 0xffff)
++#define ZH_DEVID(scsid, cssid, devno) \
++ ((devid_t) (scsid << 24) | (cssid << 16) | devno)
++
++/* Maximum number of events in the queues (shared and polled) */
++#define ZH_EVENTS_MAX 20
++#define ZH_GET_EVENT_BUFFER_COUNT 10
++
++/* SPC-2 defines the addional_length field in the sense data format as a byte,
++ * thus only 255 bytes of additional data may be returned. Size of header in
++ * sense data format is 8 bytes.
++ */
++#define ZH_SCSI_SENSE_BUFFERSIZE   263
++
++typedef unsigned short zh_count_t;
++
++/**
++ * struct sg_list - ng scatterlist
++ * @*sg: pointer to the real scatterlist
++ * @count: number of allocated pages
++ *
++ * Even if this struct is small, it groups logical depended information. And it
++ * reduces parameter list length.
++ */
++struct sg_list
++{
++	struct scatterlist *sg;
++	unsigned int count;
++};
++
++/**
++ * struct scsi_inquiry_cmd
++ * FIXME: to be documented
++ * The following are defined by the SCSI-2 specification.
++ */
++struct scsi_inquiry_cmd
++{
++	u8 op;
++	u8 reserved1:6;
++	u8 cmdt:1;
++	u8 evpd:1;
++	u8 page_code;
++	u8 reserved2;
++	u8 alloc_length;
++	u8 control;
++} __attribute__((packed));
++
++/**
++ * struct scsi_read_capacity_cmd
++ * FIXME: to be documented
++ * the 10 byte version of the command, see SBC-2
++ */
++struct scsi_read_capacity_cmd
++{
++	u8 op;
++	u8 reserved1:7;
++	u8 reladdr:1;
++	u32 lba;
++	u16 reserved2;
++	u8 reserved3:7;
++	u8 pmi:1;
++	u8 control;
++} __attribute__((packed));
++
++/**
++ * struct scsi_report_luns_cmd
++ * FIXME: to be documented
++ */
++struct scsi_report_luns_cmd
++{
++	u8 op;
++	u8 reserved1[5];
++	u32 alloc_length;
++	u8 reserved2;
++	u8 control;
++} __attribute__((packed));
++
++/* minimum size of the response data for REPORT LUNS */ 
++#define SCSI_REPORT_LUNS_SIZE_MIN 8
++
++#ifndef REPORT_LUNS
++#define REPORT_LUNS 0xA0
++#endif
++
++/**
++ * struct zh_get_config
++ * @devid: of the adapter to generate config events for
++ * @wwpn: of the port to generate config events for
++ * @size: must be set to sizeof(struct zh_event)
++ *
++ * The size member is used to assert that the events structure in user- and
++ * kernelspace are of the same size.
++ * If devid is 0, adapter_add events are generated for all configured adapters.
++ * If devid is != 0 and wwpn is zero, port_add events are generated for all
++ * ports of the specified adapter.
++ * if devid != 0 and wwpn != 0, unit_add events are generated for all units
++ * configured on the specified port;
++ */
++struct zh_get_config
++{
++	devid_t devid;
++	wwn_t wwpn;
++	unsigned int flags:2;
++};
++
++/**
++ * struct zh_get_adapterattributes
++ * @devid: unique id for adapter device
++ * @attributes: adapter attributes
++ *
++ * structure passed by ioctl ZH_IOC_GET_ADAPTERATTRIBUTES
++ */
++struct zh_get_adapterattributes
++{
++	devid_t devid;
++	struct zfcp_adapter_attributes attributes;
++};
++
++/**
++ * struct zh_get_portattributes
++ * @devid: unique id for adapter device
++ * @wwpn: wwn of discovered or local port (optional)
++ * @attributes: port attributes
++ *
++ * structure passed by ioctls ZH_IOC_GET_PORTATTRIBUTES and
++ * ZH_IOC_GET_DPORTATTRIBUTES
++ */
++struct zh_get_portattributes
++{
++	devid_t devid;
++	wwn_t wwpn;
++	struct zfcp_port_attributes attributes;
++};
++
++/**
++ * struct zh_get_portstatistics
++ * @devid: unique id for adapter device
++ * @portwwn: wwn local port (optional)
++ * @stat: port statistics
++ *
++ * structure passed by ioctl ZH_IOC_GET_PORTSTATISTICS
++ */
++struct zh_get_portstatistics
++{
++	devid_t devid;
++	wwn_t portwwn;
++	struct zfcp_port_statistics statistics;
++};
++
++/**
++ * struct zh_event_polled_link
++ * @event: subtype of link event, see @zh_event_polled_link_e
++ * @port_fc_id: port FC id, where this event occured
++ * 
++ * The standard defines an array of 3 u32 as reserved
++ * in its structure. We do not need this here since no data
++ * is passed with this member from kernel, to userspace
++ */
++struct zh_event_polled_link
++{
++  u32 port_fc_id;
++};
++
++/**
++ * struct zh_event_polled_rscn
++ * @port_fc_id: port FC id, where this event occured
++ * @port_page: affected port_id pages
++ * 
++ * The standard defines an array of 2 u32 as reserved
++ * in its structure. We do not need this here since no data
++ * is passed with this member from kernel, to userspace
++ */
++struct zh_event_polled_rscn
++{
++  u32 port_fc_id;
++  u32 port_page;
++};
++
++/**
++ * struct zh_event_polled_pty
++ * @pty_data: proprietary data
++ */
++struct zh_event_polled_pty
++{
++  u32 pty_data[4];
++};
++
++/**
++ * struct zh_event_polled
++ * @event: type of occured event
++ * @data: union of diffrent events
++ * @link: link event, @see zh_event_polled_link
++ * @rscn: rscn event, @see zh_event_polled_rscn
++ * @pty: pty event, @see zh_event_polled_pty
++ */
++struct zh_event_polled
++{
++  u32 event;
++  devid_t devid;
++  union
++  {
++    struct zh_event_polled_link link;
++    struct zh_event_polled_rscn rscn;
++    struct zh_event_polled_pty pty;
++  } data;
++};
++
++/**
++ * struct zh_get_event_buffer
++ * @devid: of the adapter to poll events
++ * @polled: array of events
++ */
++struct zh_get_event_buffer
++{
++	devid_t devid;
++	unsigned int count;
++	struct zh_event_polled polled[ZH_GET_EVENT_BUFFER_COUNT];
++};
++
++/**
++ * struct zh_event_adapter_add
++ * @devid: unique id of adapter device
++ * @wwnn: wwn of adapter node
++ * @wwpn: wwn of adapter port
++ *
++ * structure used for adapter add events
++ */
++struct zh_event_adapter_add
++{
++	devid_t	devid;
++	wwn_t	wwnn;
++	wwn_t	wwpn;
++};
++
++/**
++ * struct zh_event_port_edd
++ * @devid: of the adapter the port was added
++ * @wwpn: of the added port
++ * @wwnn: of the added port
++ * @did: of the added port
++ */
++struct zh_event_port_add
++{
++	devid_t	devid;
++	wwn_t wwpn;
++	wwn_t wwnn;
++	fc_id_t did;
++};
++
++/**
++ * struct zh_event_unit_add
++ * @devid: of the adapter the unit was added
++ * @wwpn: of the adapter the unit was added
++ * @fclun: of the added unit
++ * @host: SCSI host id
++ * @channel: SCSI channel
++ * @id: SCSI id
++ * @lun: SCSI lun
++ */
++struct zh_event_unit_add
++{
++	devid_t	devid;
++	wwn_t wwpn;
++	fcp_lun_t fclun;
++	unsigned int host;
++	unsigned int channel;
++	unsigned int id;
++	unsigned int lun;
++};
++
++/**
++ * struct zh_event
++ * @event: type of event
++ * @data: union of event specific structures
++ *
++ * structure passed by ioctl ZH_IOC_EVENT (and others?)
++ */
++struct zh_event
++{
++	u8 event;
++	union
++	{
++		struct zh_event_polled polled;
++		struct zh_event_adapter_add adapter_add;
++		struct zh_event_port_add port_add;
++		struct zh_event_unit_add unit_add;
++	} data;
++};
++
++/* SPC-2 defines the addional_length field of the inquiry reply as a byte, thus
++ * only 255 bytes of additional data may be returned. Size of header for
++ * standard INQUIRY data is 5 bytes.
++ */
++#define ZH_SCSI_INQUIRY_SIZE 260
++
++/**
++ * struct zh_scsi_inquiry - data needed for an INQUIRY
++ * @devid: of the adapter
++ * @wwpn: of the port
++ * @fclun: of the unit to send the command to
++ * @evpd: request EVPD?
++ * @page_code: of the EVPD to request
++ * @inquiry: payload of the response
++ * @sense: buffer for sense data
++ */
++struct zh_scsi_inquiry
++{
++	devid_t devid;
++	wwn_t wwpn;
++	u64 fclun;
++	u8 evpd;
++	u32 page_code;
++	u8 inquiry[ZH_SCSI_INQUIRY_SIZE];
++	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
++};
++
++/* SBC-2 defines the READ CAPACITY data */
++#define ZH_SCSI_READ_CAPACITY_SIZE 8
++
++/**
++ * struct zh_scsi_read_capacity - data needed for a READ_CAPACITY
++ * @devid: of the adapter
++ * @wwpn: of the port
++ * @fclun: of the unit to send the command to
++ * @read_capacity: payload of the response
++ * @sense: buffer for sense data
++ */
++struct zh_scsi_read_capacity
++{
++	devid_t devid;
++	wwn_t wwpn;
++	u64 fclun;
++	u8 read_capacity[ZH_SCSI_READ_CAPACITY_SIZE];
++	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
++};
++
++/**
++ * struct zh_scsi_report_luns - data needed for an REPORT_LUNS
++ * @devid: of the adapter
++ * @wwpn: of the port
++ * @*rsp_buffer: pointer to response buffer
++ * @rsp_buffer_size: of the response buffer
++ * @sense: buffer for sense data
++ */
++struct zh_scsi_report_luns
++{
++	devid_t devid;
++	wwn_t wwpn;
++	void *rsp_buffer;
++	u32 rsp_buffer_size;
++	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
++} __attribute__((packed));
++
++/**
++ * struct zh_els_rscn_payload - RSCN ELS payload as of FC-FS
++ * @qualifier: event qualifier
++ * @address_format: format of the address
++ * @domain:
++ * @area:
++ * @sequence:
++ */
++struct zh_els_rscn_payload
++{
++	u8 reserved1:2;
++	u8 qualifier:4;
++	u8 address_format:2;
++	u8 domain;
++	u8 area;
++	u8 sequence;
++} __attribute__((packed));
++
++/**
++ * struct zh_els_rscn - RSCN ELS as of FC-FS
++ * FIXME: to be documented
++ */
++struct zh_els_rscn
++{
++	u8 op;
++	u8 page_length;
++	u16 payload_length;
++	struct zh_els_rscn_payload payload[0];
++} __attribute__((packed));
++
++/**
++ * struct zh_get_rnid - retrieve RNID from adapter
++ * @devid: to send the rnid via
++ * @payload: payload for RNID ELS
++ */
++struct zh_get_rnid
++{
++	devid_t devid;
++	struct zfcp_ls_rnid_acc payload;
++};
++
++/**
++ * struct zh_send_rnid - send out an RNID ELS
++ * @devid: to send the rnid via
++ * @wwpn: to send it to
++ * @size: of the buffer
++ * @payload: payload buffer
++ */
++struct zh_send_rnid
++{
++	devid_t devid;
++	wwn_t wwpn;
++	u32 size;
++	struct zfcp_ls_rnid_acc payload;
++};
++
++/**
++ * struct zh_send_ct - data needed to send out a Generic Service command
++ * struct zh_send_ct - send out a Generic Service command 
++ * @devid: id of HBA via which to send CT
++ * @req_length: size the request buffer
++ * @req: request buffer
++ * @resp_length: size of response buffer
++ * @resp: response buffer
++ */
++struct zh_send_ct
++{
++	devid_t devid;
++	u32 req_length;
++	void *req;
++	u32 resp_length;
++	void *resp;
++} __attribute__((packed));
++
++/* IOCTL's */
++#define ZH_IOC_MAGIC 0xDD
++
++#define ZH_IOC_GET_ADAPTERATTRIBUTES \
++ _IOWR(ZH_IOC_MAGIC, 1, struct zh_get_adapterattributes)
++#define ZH_IOC_GET_PORTATTRIBUTES \
++ _IOWR(ZH_IOC_MAGIC, 2, struct zh_get_portattributes)
++#define ZH_IOC_GET_PORTSTATISTICS \
++ _IOWR(ZH_IOC_MAGIC, 3, struct zh_get_portstatistics)
++#define ZH_IOC_GET_DPORTATTRIBUTES \
++ _IOWR(ZH_IOC_MAGIC, 4, struct zh_get_portattributes)
++#define ZH_IOC_GET_RNID _IOWR(ZH_IOC_MAGIC, 5, struct zh_get_rnid)
++#define ZH_IOC_SEND_RNID _IOWR(ZH_IOC_MAGIC, 6, struct zh_send_rnid)
++#define ZH_IOC_SEND_CT _IOWR(ZH_IOC_MAGIC, 7, struct zh_send_ct)
++#define ZH_IOC_SCSI_INQUIRY _IOW(ZH_IOC_MAGIC, 8, struct zh_scsi_inquiry)
++#define ZH_IOC_SCSI_READ_CAPACITY \
++ _IOW(ZH_IOC_MAGIC, 9, struct zh_scsi_read_capacity)
++#define ZH_IOC_SCSI_REPORT_LUNS \
++ _IOW(ZH_IOC_MAGIC, 10, struct zh_scsi_report_luns)
++#define ZH_IOC_GET_EVENT_BUFFER \
++ _IOWR(ZH_IOC_MAGIC, 11, struct zh_get_event_buffer)
++#define ZH_IOC_GET_CONFIG _IOW(ZH_IOC_MAGIC, 12, struct zh_get_config)
++#define ZH_IOC_CLEAR_CONFIG _IO(ZH_IOC_MAGIC, 13)
++#define ZH_IOC_EVENT_START _IO(ZH_IOC_MAGIC, 14)
++#define ZH_IOC_EVENT_STOP _IO(ZH_IOC_MAGIC, 15)
++#define ZH_IOC_EVENT _IOR(ZH_IOC_MAGIC, 16, struct zh_event)
++#define ZH_IOC_EVENT_INSERT _IO(ZH_IOC_MAGIC, 17)
++
++enum zh_event_e {
++	ZH_EVENT_DUMMY,
++	ZH_EVENT_ADAPTER_ADD,
++	ZH_EVENT_ADAPTER_DEL,
++	ZH_EVENT_PORT_ADD,
++	ZH_EVENT_UNIT_ADD,
++	ZH_EVENT_POLLED
++};
++
++enum zh_event_polled_e {
++	ZH_EVENT_POLLED_LINK_UP,
++	ZH_EVENT_POLLED_LINK_DOWN,
++	ZH_EVENT_POLLED_RSCN,
++	ZH_EVENT_POLLED_PTY
++};
++
++#define ZH_LOG(level, fmt, args...) \
++printk(level"%s:%d, "fmt, __FUNCTION__, __LINE__, ##args)
++
++int zh_send_ct_helper(struct zh_send_ct *);
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/scsi/zh_ioctl32.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,248 @@
++/* 
++ * $Id: zh_ioctl32.c,v 1.4.2.2 2004/03/24 11:18:00 aherrman Exp $
++ *
++ * (C) Copyright IBM Corp. 2003
++ *
++ * 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 of the License, or
++ * (at your option) any later version. See the file COPYING for more
++ * information.
++ *
++ * Authors:
++ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
++ *       Andreas Herrmann <aherrman at de.ibm.com>
++ *
++ * No need for special handler functions here since our ioctl() method is 32 BIT
++ * aware.
++ *
++ * For this trick to work we define a 32bit structure (see "zh.h") and a normal
++ * one (eg struct zh_scsi_report_luns32 and struct zh_scsi_report_luns). We also
++ * define an extra 32 BIT ioctl number, ZH_IOC_SCSI_REPORT_LUNS32 using the
++ * zh_scsi_report_luns32 structure.
++ *
++ * This creates an ioctl command that is the same on 32 and 64 BIT. On a 64 BIT
++ * kernel only 32 BIT applications will call the ioctl() with the 32 BIT ioctl
++ * command, thus we can react on that and apply our pointer voodoo.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/fs.h>
++#include <linux/ioctl.h>
++#include <asm/uaccess.h>
++#include <asm/ioctl32.h>
++
++#include "zh.h"
++
++#include "../../../arch/s390x/kernel/linux32.h"
++
++/* void* casts needed because of fourth parameter */
++#define ZH_IOC_DEFAULT(cmd) {cmd, (void*) sys_ioctl}
++#define ZH_IOC_HANDLER(cmd, handler) {cmd, (void*) handler}
++
++/**
++ * zh_ioctl_entry - one entry in the conversion array
++ * @cmd: ioctl cmd
++ * @handler: the corresponding handler
++ */
++struct zh_ioctl_entry
++{
++	unsigned int cmd;
++	ioctl_trans_handler_t handler;
++};
++
++/**
++ * struct zh_send_ct32 - data needed to send out a Generic Service command,
++ *	32BIT version
++ * @devid: id of HBA via which to send CT
++ * @req_length: size the request buffer
++ * @req: request buffer
++ * @resp_length: size of response buffer
++ * @resp: response buffer
++ */
++struct zh_send_ct32
++{
++	devid_t devid;
++	u32 req_length;
++	u32 req;
++	u32 resp_length;
++	u32 resp;
++} __attribute__((packed));
++
++#define ZH_IOC_SEND_CT32 _IOWR(ZH_IOC_MAGIC, 7, struct zh_send_ct32)
++
++/**
++ * zh_send_ct32 - ioctl32 conversion function for ZH_IOC_SEND_CT
++ * @fd: fd of device file
++ * @cmd: command to execute
++ * @arg: parameter(s) for the command
++ * Return:  0 on success, else -E* code
++ * Context: User
++ */
++static inline int zh_send_ct32(unsigned int fd, unsigned int cmd,
++			       unsigned long arg)
++{
++	int ret;
++	struct zh_send_ct ioc_data;
++	struct zh_send_ct32 ioc_data32;
++	struct zh_send_ct32 *u_ptr = (struct zh_send_ct32 *) A(arg);
++
++	if (copy_from_user(&ioc_data32, u_ptr, sizeof(ioc_data32)))
++		return -EFAULT;
++
++	ioc_data.devid = ioc_data32.devid;
++	ioc_data.req_length = ioc_data32.req_length;
++	ioc_data.resp_length = ioc_data32.resp_length;
++	ioc_data.req = (void *) A(ioc_data32.req);
++	ioc_data.resp = (void *) A(ioc_data32.resp);
++
++	ret = zh_send_ct_helper(&ioc_data);
++
++	return ret;
++}
++
++/**
++ * struct zh_scsi_report_luns32 - data needed for an REPORT_LUNS, 32BIT version
++ * @devid: of the adapter
++ * @wwpn: of the port
++ * @*rsp_buffer: pointer to response buffer
++ * @rsp_buffer_size: of the response buffer
++ * @sense: buffer for sense data
++ */
++struct zh_scsi_report_luns32
++{
++	devid_t devid;
++	wwn_t wwpn;
++	u32 rsp_buffer;
++	u32 rsp_buffer_size;
++	u8 sense[ZH_SCSI_SENSE_BUFFERSIZE];
++} __attribute__((packed));
++
++#define ZH_IOC_SCSI_REPORT_LUNS32 \
++_IOW(ZH_IOC_MAGIC, 10, struct zh_scsi_report_luns32)
++
++/**
++ * zh_scsi_report_luns32 - ioctl32 conversion function for
++ *	ZH_SCSI_REPORT_LUNS32
++ * @fd: fd of device file
++ * @cmd: command to execute
++ * @arg: parameter(s) for the command
++ * Return:  0 on success, else -E* code
++ * Context: User
++ */
++static inline int zh_scsi_report_luns32(unsigned int fd, unsigned int cmd,
++					unsigned long arg)
++{
++	int ret;
++	struct zh_scsi_report_luns ioc_data = { 0 };
++	struct zh_scsi_report_luns32 ioc_data32;
++	struct zh_scsi_report_luns32 *u_ptr;
++
++	u_ptr = (struct zh_scsi_report_luns32 *) A(arg);
++
++	if (copy_from_user(&ioc_data32, u_ptr, sizeof(ioc_data32)))
++		return -EFAULT;
++
++	ioc_data.devid = ioc_data32.devid;
++	ioc_data.wwpn = ioc_data32.wwpn;
++	ioc_data.rsp_buffer = (void *) A(ioc_data32.rsp_buffer);
++	ioc_data.rsp_buffer_size = ioc_data32.rsp_buffer_size;
++
++	ret = zh_report_luns_helper(&ioc_data);
++
++	if (ret >= 0) {
++		memcpy(&ioc_data32.sense, &(ioc_data.sense),
++		       ZH_SCSI_SENSE_BUFFERSIZE);
++		if (copy_to_user(u_ptr, &ioc_data32, sizeof(ioc_data32))) {
++			ret = -EFAULT;
++		}
++		if (ret > 0) {
++			ret = 0;
++		}
++	}
++
++	return ret;
++}
++
++/*
++ * All the commands we have to register, may be freed if not compiled as module
++ */
++static struct zh_ioctl_entry zh_conversion[] __initdata = {
++	ZH_IOC_DEFAULT(ZH_IOC_GET_ADAPTERATTRIBUTES),
++	ZH_IOC_DEFAULT(ZH_IOC_GET_PORTATTRIBUTES),
++	ZH_IOC_DEFAULT(ZH_IOC_GET_PORTSTATISTICS),
++	ZH_IOC_DEFAULT(ZH_IOC_GET_DPORTATTRIBUTES),
++	ZH_IOC_DEFAULT(ZH_IOC_GET_RNID),
++	ZH_IOC_DEFAULT(ZH_IOC_SEND_RNID),
++	ZH_IOC_HANDLER(ZH_IOC_SEND_CT32, zh_send_ct32),
++	ZH_IOC_DEFAULT(ZH_IOC_SCSI_INQUIRY),
++	ZH_IOC_DEFAULT(ZH_IOC_SCSI_READ_CAPACITY),
++	ZH_IOC_HANDLER(ZH_IOC_SCSI_REPORT_LUNS32, zh_scsi_report_luns32),
++	ZH_IOC_DEFAULT(ZH_IOC_GET_EVENT_BUFFER),
++	ZH_IOC_DEFAULT(ZH_IOC_GET_CONFIG),
++	ZH_IOC_DEFAULT(ZH_IOC_CLEAR_CONFIG),
++	ZH_IOC_DEFAULT(ZH_IOC_EVENT_START),
++	ZH_IOC_DEFAULT(ZH_IOC_EVENT_STOP),
++	ZH_IOC_DEFAULT(ZH_IOC_EVENT),
++	ZH_IOC_DEFAULT(ZH_IOC_EVENT_INSERT),
++};
++
++/**
++ * do_unregister - unregister previously registered conversion ioctl()s
++ * @from: index in the zh_conversion table to start deregistering from
++ *
++ * All ioctl()s _before_ from will be deregistered. If from is 0 all will be
++ * unregistered.
++ */
++static int do_unregister(int from)
++{
++	int i;
++
++	if (0 == from) {
++		from = sizeof(zh_conversion)/sizeof(zh_conversion[0]);
++	}
++	
++	for (i = from - 1; i >= 0; --i) {
++		unregister_ioctl32_conversion(zh_conversion[i].cmd);
++	}
++
++	return 0;
++}
++
++/**
++ * do_register - register ioctl() conversion routines
++ */
++static int do_register(void)
++{
++	unsigned int i;
++	int ret;
++
++	for (i = 0; i < sizeof(zh_conversion)/sizeof(zh_conversion[0]); ++i) {
++		ret = register_ioctl32_conversion(zh_conversion[i].cmd,
++				zh_conversion[i].handler);
++		if (ret != 0) {
++			do_unregister(i - 1);
++			break;
++		}
++	}
++	
++	return ret;
++}
++
++/**
++ * zh_unregister_ioctl_conversion - clean up method
++ */
++int zh_unregister_ioctl_conversion(void)
++{
++	return do_unregister(0);
++}
++
++/**
++ * zh_register_ioctl_conversion - initialization method
++ */
++int zh_register_ioctl_conversion(void)
++{
++	return do_register();
++}
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/s390/scsi/zh_ioctl32.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/scsi/zh_ioctl32.h	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,26 @@
++/* 
++ * $Id: zh_ioctl32.h,v 1.2.2.1 2004/01/26 17:26:34 mschwide Exp $
++ *
++ * Module providing an interface for HBA API (FC-HBA) implementation
++ * to the zfcp driver.
++ *
++ * (C) Copyright IBM Corp. 2003
++ *
++ * 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 of the License, or
++ * (at your option) any later version. See the file COPYING for more
++ * information.
++ *
++ * Authors:
++ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
++ *       Andreas Herrmann <aherrman at de.ibm.com>
++ */
++
++#ifndef _ZH_IOCTL32_H_
++#define _ZH_IOCTL32_H_
++
++int zh_register_ioctl_conversion(void);
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/s390/scsi/zh_main.c	2006-01-30 22:25:25.000000000 -0700
+@@ -0,0 +1,2046 @@
++/* 
++ * $Id: zh_main.c,v 1.10.2.3 2004/09/17 08:29:41 aherrman Exp $
++ *
++ * Module providing an interface for HBA API (FC-HBA) implementation
++ * to the zfcp driver.
++ *
++ * (C) Copyright IBM Corp. 2003
++ *
++ * 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 of the License, or
++ * (at your option) any later version. See the file COPYING for more
++ * information.
++ *
++ * Authors:
++ *       Stefan Voelkel <Stefan.Voelkel at millenux.com>
++ *       Andreas Herrmann <aherrman at de.ibm.com>
++ */
++
++/*
++ * To automatically create the device node (after module loading) use:
++ *
++ *	minor=`cat /proc/misc | awk "\\$2==\\"zfcp_hbaapi\\" {print \\$1}"`
++ *	mknod /dev/zfcp_hbaapi c 10 $minor
++ */
++
++#define HBAAPI_REVISION "$Revision: 1.10.2.3 $"
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/init.h>
++
++#include <linux/kmod.h>
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++#include <linux/miscdevice.h>
++#include <linux/stringify.h>
++
++#include <asm/uaccess.h>
++#include <asm/current.h>
++#include <asm/atomic.h>
++#include <asm/div64.h>
++#include <asm/semaphore.h>
++
++#include <linux/spinlock.h>
++
++#include "zh.h"
++#include "zfcp.h"
++#include "zfcp_zh.h"
++
++#ifdef CONFIG_S390_SUPPORT
++# include "zh_ioctl32.h"
++#endif
++
++MODULE_AUTHOR("Stefan Voelkel <Stefan.Voelkel at millenux.com>, "
++	"Andreas Herrmann  <aherrman at de.ibm.com>, "
++	"IBM Deutschland Entwicklung GmbH");
++MODULE_DESCRIPTION("Interface for HBA API to FCP HBA driver for IBM zSeries, "
++	HBAAPI_REVISION);
++MODULE_LICENSE("GPL");
++
++EXPORT_NO_SYMBOLS;
++
++/*
++ * module and kernel parameters
++ */
++int maxshared = ZH_EVENTS_MAX;
++int maxpolled = ZH_EVENTS_MAX;
++int minor = MISC_DYNAMIC_MINOR;
++
++#ifdef MODULE
++MODULE_PARM(maxshared,"i");
++MODULE_PARM_DESC(maxshared, "Maximum number of events in the shared event"
++		" queue, defaults to "__stringify(ZH_EVENTS_MAX));
++
++MODULE_PARM(maxpolled,"i");
++MODULE_PARM_DESC(maxpolled, "Maximum number of events in the polled event"
++		" queue, defaults to "__stringify(ZH_EVENTS_MAX));
++
++MODULE_PARM(minor, "i");
++MODULE_PARM_DESC(minor, "Minor of the misc device to register, defaults to"
++		"dynamic registration");
++#else
++static int __init zh_maxshared_setup(char *str)
++{
++	maxshared = simple_strtol(str, NULL, 0);
++	return 1;
++}
++__setup("zfcp_hbaapi_maxshared=", zh_maxshared_setup);
++
++static int __init zh_maxpolled_setup(char *str)
++{
++	maxpolled = simple_strtol(str, NULL, 0);
++	return 1;
++}
++__setup("zfcp_hbaapi_maxpolled=", zh_maxpolled_setup);
++
++static int __init zh_minor_setup(char *str)
++{
++	minor = simple_strtol(str, NULL, 0);
++	return 1;
++}
++__setup("zfcp_hbaapi_minor=", zh_minor_setup);
++#endif
++
++/**
++ * struct zh_event_item - An event
++ * @event: the event itself, as it is passed to userspace
++ * @count: reference counter
++ * @list: list handling
++ *
++ * An item in the kernel event queue.
++ */
++struct zh_event_item
++{
++	struct zh_event event;
++	atomic_t count;
++	struct list_head list;
++};
++
++/**
++ * struct zh_config - An config entry
++ * @event: the event itself
++ * @list: list handling
++ *
++ * A non-counted event. Used for configuration and polled events.
++ */
++struct zh_config
++{
++	struct zh_event event;
++	struct list_head list;
++};
++
++/**
++ * struct zh_client - per client data
++ * @registered: 1 if the fd is registered for events, else 0
++ * @lost: 1 if the fd has lost an event, else 0
++ * @*last: pointer to the last delivered event or NULL
++ * @config: list of private config events
++ * @clients: list handling for list of clients
++ *
++ * This structure is attached to filp->private_data and used throughout the
++ * module to save per client data. 
++ */
++struct zh_client
++{
++	struct semaphore sem;
++	unsigned int registered:1;
++	unsigned int lost:1;
++	struct list_head *last;
++	struct list_head config;
++	struct list_head clients;
++};
++
++/* ioctl workers
++ * We could inline them since only about two are called from more than one
++ * place. OTOH this is no time critical code */
++static int zh_ioc_get_adapterattributes(struct zh_get_adapterattributes *);
++static int zh_ioc_get_portattributes(struct zh_get_portattributes *);
++static int zh_ioc_get_portstatistics(struct zh_get_portstatistics *);
++static int zh_ioc_get_dportattributes(struct zh_get_portattributes *);
++static int zh_ioc_get_config(struct zh_get_config *, struct file *);
++static int zh_ioc_clear_config(struct file *);
++static int zh_ioc_get_event_buffer(struct zh_get_event_buffer *);
++static int zh_ioc_event_start(struct file *);
++static int zh_ioc_event_stop(struct file *);
++static int zh_ioc_event(struct zh_event *, struct file *);
++static int zh_ioc_event_insert(void);
++static int zh_ioc_scsi_inquiry(struct zh_scsi_inquiry *);
++static int zh_ioc_scsi_read_capacity(struct zh_scsi_read_capacity *);
++static int zh_ioc_scsi_report_luns(struct zh_scsi_report_luns *);
++static int zh_ioc_get_rnid(struct zh_get_rnid *);
++static int zh_ioc_send_rnid(struct zh_send_rnid *);
++static int zh_ioc_send_ct(struct zh_send_ct *);
++
++/* callbacks for asynchronous event generation. called form zfcp.o */
++static void zh_cb_adapter_add(struct file *, devno_t, wwn_t, wwn_t);
++static void zh_cb_port_add(struct file *, devno_t, wwn_t, wwn_t, fc_id_t);
++static void zh_cb_unit_add(struct file *, devno_t, wwn_t, fcp_lun_t,
++			   unsigned int, unsigned int, unsigned int,
++			   unsigned int);
++static void zh_cb_incomming_els(const devno_t, const fc_id_t, const void *);
++static void zh_cb_link_down(const fc_id_t);
++static void zh_cb_link_up(const fc_id_t);
++
++/* implemented file operations for our device file */
++static int zh_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
++static int zh_open(struct inode *, struct file *);
++static ssize_t zh_read(struct file *, char *, size_t, loff_t *);
++static int zh_release(struct inode *, struct file *);
++
++/* auxiliary functions */
++static void zh_map_port_speed(u32 *, int);
++#define ZH_PORT_OPERATING_SPEED 1
++#define ZH_PORT_SUPPORTED_SPEED 0
++
++/*
++ * zh_fops - Device file operations
++ *
++ * Structure describing the possible operations on our device file
++ * */
++static struct file_operations zh_fops = {
++	.ioctl = zh_ioctl,
++	.open = zh_open,
++	.release = zh_release,
++	.read = zh_read,
++};
++
++/*
++ * struct zh_misc - misc device description
++ * @minor: our minor
++ * @name: that is what we are called
++ * @fops: file operations
++ *
++ * Passed to register_misc
++ */
++static struct miscdevice zh_misc = {
++	.name = "zfcp_hbaapi",
++	.fops = &zh_fops
++};
++
++/*
++ * struct zh_callbacks - Callbacks for zfcp events
++ * @adapter_add: called on adapter add events
++ *
++ * This is passed to the zfcp_register_callbacks method, to enable
++ * zfcp to call our hooks.
++ */
++static struct zfcp_zh_callbacks zh_callbacks = {
++	.adapter_add = zh_cb_adapter_add,
++	.port_add = zh_cb_port_add,
++	.unit_add = zh_cb_unit_add,
++	.incomming_els = zh_cb_incomming_els,
++	.link_down = zh_cb_link_down,
++	.link_up = zh_cb_link_up
++};
++
++/**
++ * struct zh_events
++ * @lock: spinlock protecting the structure
++ * @wq: wait queue to wait for events
++ * @registered: number of processes to notify on events
++ * @pending: number of events in the queue
++ * @queue: list of events
++ * @clients: anchor for list of clients
++ *
++ * This structure contains all data needed for asynchronous event handling
++ */
++struct zh_shared_events {
++	spinlock_t lock;
++	wait_queue_head_t wq;
++	unsigned short registered;
++	unsigned int pending;
++	struct list_head queue;
++	struct list_head clients;
++};
++
++static struct zh_shared_events zh_shared;
++
++/**
++ * struct zh_polled_events
++ * @lock: spinlock protecting this structure
++ * @pending: number of events pending
++ * @queue: list of events
++ *
++ * Polled events must be in an extra queue according to FC-HBA.
++ */
++struct zh_polled_events
++{
++	spinlock_t lock;
++	zh_count_t pending;
++	struct list_head queue;
++};
++
++static struct zh_polled_events zh_polled;
++
++/**
++ * add_event_to_polled - Add an event to the polled queue
++ * @*c: pointer to an event
++ *
++ * We use the zh_config structure here. It distinguishes itself from the
++ * zh_event only through the missing "left" counter. Since a polled event is
++ * only delivered once, we do not need a counter here.
++ */
++static void add_event_to_polled(struct zh_config *c)
++{
++	struct zh_config *last;
++	unsigned long flags;
++
++	spin_lock_irqsave(&zh_polled.lock, flags);
++
++	if (zh_polled.pending == maxpolled) {
++		last = list_entry(zh_polled.queue.next, struct zh_config, list);
++		list_del(zh_polled.queue.next);
++		kfree(last);
++	} else {
++		++zh_polled.pending;
++	}
++
++	list_add_tail(&c->list, &zh_polled.queue);
++
++	spin_unlock_irqrestore(&zh_polled.lock, flags);
++}
++
++/**
++ * add_event_to_shared - Add an event to the list of pending events
++ * @e: The event that should be added
++ * Context: irq/user
++ *
++ * Events will be thrown away if nobody is registered for delivery. If there
++ * are already &maxevents events in the list, the oldest is discarded.
++ */
++static void add_event_to_shared(struct zh_event_item *e)
++{
++	struct zh_event_item *item;
++	struct list_head *go, *first;
++	struct zh_client *c;
++	unsigned long flags;
++
++	/* atm we can be called from user, and from irq context
++	 * so we need the irqsafe thingie here. */
++	spin_lock_irqsave(&zh_shared.lock, flags);
++
++	/* do not keep events if we have nobody to deliver it to */
++	if (zh_shared.registered == 0) {
++		spin_unlock_irqrestore(&zh_shared.lock, flags);
++		kfree(e);
++		return;
++	}
++
++	/* is the queue full? */
++	if (zh_shared.pending == maxshared) {
++		first = zh_shared.queue.next;
++
++		/* check if we have to flag some clients */
++		list_for_each(go, &zh_shared.clients) {
++			c = list_entry(go, struct zh_client, clients);
++
++			if (NULL == c->last) {
++				ZH_LOG(KERN_INFO, "lost event for client "
++				       "with pid %u\n", current->pid);
++				c->lost = 1;
++			} else {
++				if (first == c->last) {
++					c->last = NULL;
++				}
++			}
++		}
++
++		item = list_entry(first, struct zh_event_item, list);
++		list_del(first);
++		kfree(item);
++		
++		ZH_LOG(KERN_INFO, "event queue full, deleted item %p\n",
++		       item);
++
++	} else {
++		++zh_shared.pending;
++	}
++
++	/* initialize event, add it to the list */
++	atomic_set(&e->count, zh_shared.registered);
++	
++	list_add_tail(&e->list, &zh_shared.queue);
++
++	spin_unlock_irqrestore(&zh_shared.lock, flags);
++
++	/* wake up all processes waiting for events */
++	wake_up_interruptible_all(&zh_shared.wq);
++}
++
++/**
++ * add_event - Enqueue an event
++ * @filp: struct file to add the event to, or NULL
++ * @e: the event to enqueue
++ * @c: the event to enqueue
++ *
++ * Either add the event privately to the fd directly, or to the
++ * global queue of events.
++ * FIXME (stage 2) check whether some polled events have to be added to shared
++ * queueu, too.
++ */
++static inline void add_event(struct file *filp, struct zh_event_item *e, struct
++		zh_config *c)
++{
++	if (NULL == filp) {
++		add_event_to_shared(e);
++	} else {
++		struct zh_client *head = (void*) filp->private_data;
++		
++		list_add_tail(&c->list, &head->config);
++	}
++}
++
++/**
++ * zh_open - Implements the device open call
++ * @inode: struct inode
++ * @filp: struct file
++ * Return: 0 on success, else -ENOMEM 
++ * Context: user
++ * 
++ * Called when the zfcp_hbaapi device file is opened. Iniializes
++ * filp->private_data.
++ */
++static int zh_open(struct inode *inode, struct file *filp)
++{
++	struct zh_client *data;
++
++	MOD_INC_USE_COUNT;
++
++	data = kmalloc(sizeof(*data), GFP_KERNEL);
++
++	if (data == NULL) {
++		MOD_DEC_USE_COUNT;
++		return -ENOMEM;
++	}
++
++	sema_init(&data->sem, 1);
++	data->last = NULL;
++	data->registered = 0;
++	data->lost = 0;
++	INIT_LIST_HEAD(&data->config);
++	INIT_LIST_HEAD(&data->clients);
++	filp->private_data = data;
++
++	return 0;
++}
++
++/**
++ * zh_release - Called on file release
++ * @inode: struct inode
++ * @filp: struct file
++ * Return: always 0
++ * Context: user
++ *
++ * Called when all copies of a filedescriptor are closed, thus we
++ * can mess around with private_data, and free it.
++ */
++static int zh_release(struct inode *inode, struct file *filp)
++{
++	zh_ioc_event_stop(filp);
++	zh_ioc_clear_config(filp);
++
++	kfree(filp->private_data);
++	filp->private_data = NULL;
++
++	MOD_DEC_USE_COUNT;
++	return 0;
++}
++
++/**
++ * zh_read - The device's read method
++ * Context: user
++ *
++ * Used to read the whole configuration data, eg adapters, ports or units
++ * from zfcp.
++ *
++ * Access is serialized with a semaphore thus cloned file descriptors may block,
++ * eg fd = open(); fork(); parent:ioctl(ZH_IOC_EVENT); child:read(); The child
++ * will be block until the parent returns from the ioctl(), IF they use *
++ * _exactly the same_ file descriptor. Diffrent file descriptors do _not_ block
++ * each other.
++ */
++static ssize_t zh_read(struct file *filp, char* buf, size_t s, loff_t *off)
++{
++	size_t count, i, ret;
++	struct zh_config *c;
++	struct list_head *go, *safe;
++	struct zh_client *client = (struct zh_client*) filp->private_data;
++
++	if (down_interruptible(&client->sem))
++		return -ERESTARTSYS;
++
++	if (s < sizeof(c->event)) {
++		ret = -ENOSPC;
++		goto up;
++	}
++
++	if (list_empty(&client->config)) {
++		ret = 0;
++		goto up;
++	}
++
++	count = s / sizeof(c->event);
++	i = 0;
++
++	list_for_each_safe(go, safe, &client->config)
++	{
++		c = list_entry(go, struct zh_config, list);
++		
++		if (copy_to_user(buf, &c->event, sizeof(c->event))) {
++			ret = -EFAULT;
++			goto up;
++		}
++		
++		list_del(go);
++		kfree(c);
++
++		buf += sizeof(c->event);
++
++		if (++i >= count) {
++			break;
++		}
++	}
++
++	ret = i * sizeof(c->event);
++
++up:
++	up(&client->sem);
++
++	return ret;
++};
++
++/**
++ * zh_ioctl - The device's ioctl() method
++ * @inode: struct inode
++ * @filp: struct file
++ * @cmd: command to execute
++ * @arg: parameter(s) for the command
++ * Return:  0 on success, else -E* code
++ * Context: User
++ * 
++ * This is the main interaction method between the vendor lib and the
++ * kernel. Here we only determine what we should do, and then call the
++ * corresponding worker method.
++ *
++ * Also read zh_read()'s comment.
++ */
++static int zh_ioctl(struct inode *inode, struct file *filp,
++		    unsigned int cmd, unsigned long arg)
++{
++
++	int ret;
++	struct zh_client *client = (void*) filp->private_data;
++
++	if (down_interruptible(&client->sem))
++		return -ERESTARTSYS;
++
++	switch (cmd)
++	{
++	case ZH_IOC_GET_ADAPTERATTRIBUTES:
++		ret = zh_ioc_get_adapterattributes(
++			(struct zh_get_adapterattributes *) arg);
++		break;
++	case ZH_IOC_GET_PORTATTRIBUTES:
++		ret = zh_ioc_get_portattributes(
++			(struct zh_get_portattributes *) arg);
++		break;
++	case ZH_IOC_GET_DPORTATTRIBUTES:
++		ret = zh_ioc_get_dportattributes(
++			(struct zh_get_portattributes *) arg);
++		break;
++	case ZH_IOC_GET_PORTSTATISTICS:
++		ret = zh_ioc_get_portstatistics(
++			(struct zh_get_portstatistics *) arg);
++		break;
++	case ZH_IOC_GET_EVENT_BUFFER:
++		ret = zh_ioc_get_event_buffer(
++			(struct zh_get_event_buffer *) arg);
++		break;
++	case ZH_IOC_EVENT_START:
++		ret = zh_ioc_event_start(filp);
++		break;
++	case ZH_IOC_EVENT_STOP:
++		ret = zh_ioc_event_stop(filp);
++		break;
++	case ZH_IOC_EVENT:
++		ret = zh_ioc_event((struct zh_event*) arg, filp);
++		break;
++	case ZH_IOC_EVENT_INSERT: /* DEBUG ONLY */
++		ret = zh_ioc_event_insert();
++		break;
++	case ZH_IOC_SCSI_INQUIRY:
++		ret = zh_ioc_scsi_inquiry((struct zh_scsi_inquiry*) arg);
++		break;
++	case ZH_IOC_SCSI_READ_CAPACITY:
++		ret = zh_ioc_scsi_read_capacity(
++			(struct zh_scsi_read_capacity *) arg);
++		break;
++	case ZH_IOC_SCSI_REPORT_LUNS:
++		ret = zh_ioc_scsi_report_luns(
++			(struct zh_scsi_report_luns *) arg);
++		break;
++	case ZH_IOC_GET_CONFIG:
++		ret = zh_ioc_get_config((struct zh_get_config *) arg, filp);
++		break;
++	case ZH_IOC_CLEAR_CONFIG:
++		ret = zh_ioc_clear_config(filp);
++		break;
++	case ZH_IOC_GET_RNID:
++		ret = zh_ioc_get_rnid((struct zh_get_rnid *) arg);
++		break;
++	case ZH_IOC_SEND_RNID:
++		ret = zh_ioc_send_rnid((struct zh_send_rnid *) arg);
++		break;
++	case ZH_IOC_SEND_CT:
++		ret = zh_ioc_send_ct((struct zh_send_ct *) arg);
++		break;
++	default:
++		ret = -ENOTTY;
++		break;
++	}
++
++	up(&client->sem);
++
++	return ret;
++}
++
++/**
++ * zh_init - Module initialisation
++ * Return: 0 on success, else < 0
++ * Context: user
++ * 
++ * Sets owner, registers with zfcp, registers misc device, initializes
++ * global events structure.
++ *
++ * FIXME Register a misc minor number for zfcp_hbaapi at www.lanana.org.
++ */
++static int __init zh_init(void)
++{
++	int ret;
++	
++	if (0 >= maxshared) {
++		ZH_LOG(KERN_ERR, "illegal value for maxshared: %d, "
++		       "minimum is 1\n", maxshared);
++		return -EINVAL;
++	}
++
++#ifdef CONFIG_ARCH_S390X
++	ret = zh_register_ioctl_conversion();
++	if (ret < 0) {
++		return ret;
++	}
++#endif
++
++	/* register callbacks with zfcp */
++	ret = zfcp_zh_callbacks_register(&zh_callbacks);
++	if (ret < 0) {
++		return ret;
++	}
++
++	SET_MODULE_OWNER(&zh_fops);
++
++	/* register a misc char device */
++	zh_misc.minor = minor;
++	ret = misc_register(&zh_misc);
++	if (ret < 0) {
++		goto failed_misc;
++	}
++
++	/* initialize shared events */
++	spin_lock_init(&zh_shared.lock);
++	init_waitqueue_head(&zh_shared.wq);
++	zh_shared.registered = 0;
++	zh_shared.pending = 0;
++	INIT_LIST_HEAD(&zh_shared.queue);
++	INIT_LIST_HEAD(&zh_shared.clients);
++
++	/* initalize polled events */
++	spin_lock_init(&zh_polled.lock);
++	zh_polled.pending = 0;
++	INIT_LIST_HEAD(&zh_polled.queue);
++
++	ZH_LOG(KERN_NOTICE, "loaded hbaapi.o, version %s, maxshared=%d, "
++			"maxpolled=%d\n", HBAAPI_REVISION, maxshared,
++			maxpolled);
++
++	if (MISC_DYNAMIC_MINOR == minor) {
++		ZH_LOG(KERN_INFO, "registered dynamic minor with misc "
++		       "device\n");
++	} else {
++		ZH_LOG(KERN_INFO, "registered minor %d with misc device\n",
++		       minor);
++	}
++
++	return 0;
++
++failed_misc:
++	zfcp_zh_callbacks_unregister(&zh_callbacks);
++	
++	return ret;
++}
++
++/**
++ * zh_exit - Module finalisation
++ * Context: user
++ *
++ * Undoes all work done in zh_init()
++ */
++static void __exit zh_exit(void)
++{
++	struct list_head *go, *save;
++	struct zh_event_item *e;
++	struct zh_config *c;
++
++	zfcp_zh_callbacks_unregister(&zh_callbacks);
++	misc_deregister(&zh_misc);
++
++#ifdef CONFIG_ARCH_S390X
++	/* FIXME return value? log message? */
++	zh_unregister_ioctl_conversion();
++#endif
++
++	if (!list_empty(&zh_shared.queue)) {
++		ZH_LOG(KERN_ERR, "event queue not empty while unloading "
++		       "module\n");
++
++		/* the module can only be unloaded when all file descriptors
++		 * have been closed. If there are events left in the queue, we
++		 * do have an error in our code. Since nobody else can access
++		 * these events, we can free them.
++		 */
++		list_for_each_safe(go, save, &zh_shared.queue)
++		{
++			e = list_entry(go, struct zh_event_item, list);
++			list_del(go);
++			kfree(e);
++			--zh_shared.pending;
++		}
++	}
++
++	if (zh_shared.pending) {
++		ZH_LOG(KERN_ERR, "number of pending events not zero but %u\n",
++		       zh_shared.pending);
++	}
++
++	/* throw away polled events */
++	list_for_each_safe(go, save, &zh_polled.queue) {
++		c = list_entry(go, struct zh_config, list);
++		list_del(go);
++		kfree(c);
++	}
++}
++
++/**
++ * zh_ioc_get_adapterattributes - Retrieve attributes of an adapter
++ * @u_ptr: userspace pointer to copy data from and to
++ * Return: 0 on success, else -E* code
++ * Context: user
++ */
++static int zh_ioc_get_adapterattributes(struct zh_get_adapterattributes *u_ptr)
++{
++	struct zh_get_adapterattributes ioc_data;
++	int ret;
++
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++
++	ret = zfcp_zh_get_adapter_attributes(DEVID_TO_DEVNO(ioc_data.devid),
++					     &ioc_data.attributes);
++	if (0 == ret) {
++		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
++			ret = -EFAULT;
++		}
++	}
++
++	return ret;
++}
++
++/**
++ * zh_ioc_get_portattributes - Retrieve attributes of an adapter port
++ * @*u_ptr: userspace pointer to copy the data to
++ * Return: 0 on success, else -E* code
++ * Context: user
++ */
++static int zh_ioc_get_portattributes(struct zh_get_portattributes *u_ptr)
++{
++	struct zh_get_portattributes ioc_data;
++	int ret;
++	
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++
++	ret = zfcp_zh_get_port_attributes(DEVID_TO_DEVNO(ioc_data.devid),
++					  &ioc_data.attributes);
++
++	if ((0 == ret) || (EOPNOTSUPP == ret)) {
++		zh_map_port_speed(&ioc_data.attributes.supported_speed,
++				  ZH_PORT_SUPPORTED_SPEED);
++		zh_map_port_speed(&ioc_data.attributes.speed,
++				  ZH_PORT_OPERATING_SPEED);
++		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
++			ret = -EFAULT;
++		}
++	}
++
++	return ret;
++}
++
++/**
++ * zh_ioc_get_portstatistics - Retrieve statistics of an adapter port
++ * @*u_ptr: userspace pointer to copy data from and to
++ * Return: 0 on success, else -E* code
++ * Context: user
++ */
++static int zh_ioc_get_portstatistics(struct zh_get_portstatistics *u_ptr)
++{
++	struct zh_get_portstatistics ioc_data;
++	int ret;
++	
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++
++	ret = zfcp_zh_get_port_statistics(DEVID_TO_DEVNO(ioc_data.devid),
++					  &ioc_data.statistics);
++
++	if ((0 == ret) || (EOPNOTSUPP == ret)) {
++		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
++			ret = -EFAULT;
++		}
++	}
++
++	return ret;
++}
++
++/**
++ * zh_ioc_get_dportattributes - Retrieve attributes of an target port
++ * @*u_ptr: userspace pointer to copy data from and to
++ * Return: 0 on success, else -E* code
++ * Context: user
++ */
++static int zh_ioc_get_dportattributes(struct zh_get_portattributes *u_ptr)
++{
++	struct zh_get_portattributes ioc_data;
++	int ret;
++	
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++
++	ret = zfcp_zh_get_dport_attributes(DEVID_TO_DEVNO(ioc_data.devid),
++					   ioc_data.wwpn,
++					   &ioc_data.attributes);
++
++	if (0 == ret) {
++		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
++			ret = -EFAULT;
++		}
++	}
++
++	return ret;
++}
++
++/**
++ * zh_ioc_get_event_buffer - Retrieve events from the polled queue
++ * @*u_ptr: userspace pointer to copy data from and to
++ * Return: number of return events on success, else -E* code
++ * 
++ * Copy events belonging to an adapter to userspace and delete them.
++ */
++static int zh_ioc_get_event_buffer(struct zh_get_event_buffer *u_ptr)
++{
++	int ret;
++	struct zh_get_event_buffer ioc_data;
++	struct zh_config *c;
++	struct list_head *go, *safe;
++	unsigned short i = 0;
++
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++
++	if (ioc_data.count > ZH_GET_EVENT_BUFFER_COUNT) {
++		ioc_data.count = ZH_GET_EVENT_BUFFER_COUNT;
++	}
++
++	spin_lock_irq(&zh_polled.lock);
++
++	list_for_each_safe(go, safe, &zh_polled.queue)
++	{
++		c = list_entry(go, struct zh_config, list);
++
++		if (i >= ioc_data.count) {
++			break;
++		}
++
++		if (ioc_data.devid == c->event.data.polled.devid){
++			ioc_data.polled[i] = c->event.data.polled;
++
++			list_del(go);
++			kfree(c);
++			--zh_polled.pending;
++
++			++i;
++		}
++	}
++
++	spin_unlock_irq(&zh_polled.lock);
++
++	if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
++		ret = -EFAULT;
++	} else {
++		ret = i;
++	}
++
++	return ret;
++}
++
++/**
++ * zh_ioc_event_start - Called to enable event delivery
++ * @*filp: file for which event delivery should be enabled
++ * Return: 0 on success, else -E* code
++ * Context: user
++ *
++ * Mark the fd as target for events, increase each events
++ * "to-be-delivered-to" counter by 1.
++ */
++static int zh_ioc_event_start(struct file *filp)
++{
++	struct zh_client *client = (struct zh_client*) filp->private_data;
++
++	/* registration is only allowed once */
++	if (client->registered) {
++		return -EINVAL;
++	}
++
++	spin_lock_irq(&zh_shared.lock);
++
++	/* remember that there is one more fd events have to be delivered to */
++	++zh_shared.registered;
++
++	client->registered = 1;
++
++	/* set number of the next event, 0 means no events available ATM */
++	if (list_empty(&zh_shared.queue)) {
++		client->last = NULL;
++	} else {
++		client->last = zh_shared.queue.prev;
++	}
++
++	list_add_tail(&client->clients, &zh_shared.clients);
++
++	spin_unlock_irq(&zh_shared.lock);
++
++	return 0;
++}
++
++/**
++ * count_down_event - Refcount event
++ * @list: containded list structure of the event
++ * Context: user
++ * Return: 1 of the event was deleted, 0 else
++ * Locks: zh_shared.lock must be held
++ * 
++ * Removes an event from the list, if it was delivered to all fd's. Otherwise
++ * just refcount it.
++ */
++static inline int count_down_event(struct list_head *list)
++{
++	struct zh_event_item *e = list_entry(list, struct zh_event_item, list);
++
++	if (atomic_dec_and_test(&e->count)) {
++		--zh_shared.pending;
++
++		list_del(list);
++		kfree(e);
++			
++		return 1;
++	} else {
++		return 0;
++	}
++}
++
++/**
++ * zh_ioc_event_stop - Stop event delivery
++ * @*filp: file for which event delivery should be disabled
++ * Return: 0 on success, else -E* code
++ * Context: user
++ *
++ * Decrease total number of fd's which get events, count down all events
++ * _after_ the event delivered to this fd.
++ */
++static int zh_ioc_event_stop(struct file *filp)
++{
++	struct list_head *go, *safe;
++	struct zh_client *client = (struct zh_client*) filp->private_data;
++
++	/* deregistration is only allowed once */
++	if (!client->registered) {
++		return -EINVAL;
++	}
++
++	spin_lock_irq(&zh_shared.lock);
++
++	--zh_shared.registered;
++
++	list_del(&client->clients);
++
++	/* count down all not yet delivered events for this fd */
++	list_for_each_safe(go, safe, &zh_shared.queue)
++	{
++		if (NULL == client->last) {
++			count_down_event(go);
++		} else {
++			if (go == client->last) {
++				client->last = NULL;
++			}
++		}
++	}
++	
++	spin_unlock_irq(&zh_shared.lock);
++
++	client->registered = 0;
++	client->lost = 0;
++	client->last = NULL;
++
++	return 0;
++}
++
++/**
++ * has_next_event - Condition for wait_event_interruptible
++ * @head: private data of the fd
++ * Return: 1 if there is an event waiting, 0 else
++ * Locks: lock/unlock of zh_shared.lock
++ *
++ * This is used as the condition for the wait_event_interruptible()
++ * call. It avoids a wakeup, statechange race.
++ */
++static inline int has_next_event(struct zh_client *client)
++{
++	int ret = 0;
++	unsigned long flags;
++
++	spin_lock_irqsave(&zh_shared.lock, flags);
++
++	if (NULL == client->last) {
++		ret = !list_empty(&zh_shared.queue);
++	} else {
++		ret = (zh_shared.queue.prev != client->last);
++	}
++	
++	spin_unlock_irqrestore(&zh_shared.lock, flags);
++
++	return ret;
++}
++
++/**
++ * zh_ioc_event - Wait for an event
++ * @*u_ptr: userspace pointer to copy data to
++ * @filp: descriptor receiving events
++ * Return: 0 on success, -E* code else
++ * Context: user
++ *
++ * The heart of the event delivery. Waits for events and delivers the next one
++ */
++static int zh_ioc_event(struct zh_event *u_ptr, struct file *filp)
++{
++	struct zh_event_item *event;
++	struct list_head *entry;
++	int ret;
++	struct zh_client *client = (struct zh_client*) filp->private_data;
++
++	if (!client->registered) {
++		return -EINVAL;
++	}
++
++	/* wait for events */
++	ret = wait_event_interruptible(zh_shared.wq, has_next_event(client));
++	if (-ERESTARTSYS == ret) {
++		/* ERESTARTSYS should never be seen by user programs */
++		return -ERESTART;
++	}
++
++	spin_lock_irq(&zh_shared.lock);
++
++	/* need to check it lock protected */
++	if (client->lost) {
++		client->last = NULL;
++		client->lost = 0;
++		ret = -ENXIO;
++		goto release;
++	}
++
++	if (NULL == client->last) {
++		entry = zh_shared.queue.next;
++	} else {
++		entry = client->last->next;
++	}
++	
++	event = list_entry(entry, struct zh_event_item, list);
++
++	if (copy_to_user(u_ptr, &event->event, sizeof(*u_ptr))) {
++		ret = -EFAULT;
++		goto release; /* keep the event in the queue */
++	}
++
++	if (count_down_event(entry)) {
++		client->last = NULL;
++	} else {
++		client->last = entry;
++	}
++
++	ret = 0;
++
++release:
++	spin_unlock_irq(&zh_shared.lock);
++	return ret;
++}
++
++/**
++ * zh_ioc_event_insert - Insert an event into the list
++ * @*u_ptr: userspace pointer to copy data from
++ * Return: 0 on success, else -E* code
++ * Debug: DEBUG ONLY
++ *
++ * Insert a dummy event into the list of events, used to determine if
++ * the event handling code is working. Insert a dummy event into the 
++ * polled event buffer, used to test the polled event buffer code.
++ */
++static int zh_ioc_event_insert(void)
++{
++	struct zh_event_item *e;
++	
++	e = (struct zh_event_item*) kmalloc(sizeof(*e), GFP_KERNEL);
++	if (e == NULL) {
++		return -ENOMEM;
++	}
++
++	memset(e, 0, sizeof(*e));
++	e->event.event = ZH_EVENT_DUMMY;
++
++	add_event_to_shared(e);
++
++	return 0;
++}
++
++/**
++ * zh_sg_free - Free an allocated scatterlist
++ * @sg: the scatterlist
++ */
++static void zh_sg_free(struct sg_list *sg)
++{
++	int i;
++	
++	for (i = sg->count - 1; i >= 0; --i) {
++		free_page((unsigned long) sg->sg[i].address);
++	}
++
++	kfree(sg->sg);
++	memset(sg, 0, sizeof(*sg));
++}
++
++/**
++ * zh_sg_alloc - Allocate a scatterlist
++ * @*sg: scatterlist
++ * Return: 0 on success, else -E* code
++ * Context: User
++ *
++ * Either all pages can be allocated, or none. On error *sg is invalid and
++ * has to be reinitialized.
++ */
++static int zh_sg_alloc(struct sg_list *sg, size_t size)
++{
++	unsigned int i;
++	size_t lastsize, array;
++
++	if (0 == size) {
++		return -EINVAL;
++	}
++
++	sg->count = size / PAGE_SIZE;
++	lastsize = size - (sg->count * PAGE_SIZE);
++	if (0 != lastsize) {
++		++sg->count;
++	} else {
++		lastsize = PAGE_SIZE;
++	}
++
++	/* allocate the array */
++	array = sizeof(struct scatterlist) * sg->count;
++	sg->sg = kmalloc(array, GFP_KERNEL);
++	if (NULL == sg->sg) {
++		return -ENOMEM;
++	}
++
++	memset(sg->sg, 0, array);
++
++	/* allocate needed pages */
++	for (i = 0; i < sg->count; ++i) {
++		sg->sg[i].address = (void*) get_zeroed_page(GFP_KERNEL);
++		if (NULL == sg->sg[i].address) {
++			sg->count = i;
++			zh_sg_free(sg);
++			return -ENOMEM;
++		}
++
++		sg->sg[i].length = PAGE_SIZE;
++	}
++
++	sg->sg[i-1].length = lastsize;
++
++	return 0;
++}
++
++/**
++ * zh_sg_get_size - calculate size in bytes of a sg
++ * @*sg: pointer to a sg structure
++ */
++static inline size_t zh_sg_get_size(struct sg_list *sg)
++{
++	return (((sg->count - 1) * PAGE_SIZE) + sg->sg[sg->count - 1].length);
++}
++
++/**
++ * zh_sg_copy_from_user - copy data from user
++ * @*u_ptr: address in userspace to copy from
++ * @size: bytes to copy
++ * @*sg: describing space to copy to
++ * Return: 0 on success, -E* code else
++ * 
++ * This method refuses to copy data if size is too big (-ENOSPC)
++ */
++static int zh_sg_copy_from_user(char *u_ptr, size_t size, struct sg_list *sg)
++{
++	unsigned int i;
++
++	if (size > zh_sg_get_size(sg)) {
++		return -ENOSPC;
++	}
++
++	for (i = 0; i < sg->count - 1; ++i) {
++		if (copy_from_user(sg->sg[i].address, u_ptr, PAGE_SIZE)) {
++			return -EFAULT;
++		}
++
++		u_ptr += PAGE_SIZE;
++	}
++
++	if (copy_from_user(sg->sg[i].address, u_ptr,
++			   sg->sg[sg->count - 1].length)) {
++		return -EFAULT;
++	}
++
++	return 0;
++}
++
++/**
++ * zh_sg_copy_from_user_alloc - Allocate a scatterlist and copy data from user
++ */
++static inline int zh_sg_copy_from_user_alloc(char *u_ptr, size_t size,
++					     struct sg_list *sg)
++{
++	int ret;
++
++	ret = zh_sg_alloc(sg, size);
++	if (ret < 0) {
++		return ret;
++	}
++
++	return zh_sg_copy_from_user(u_ptr, size, sg);
++}
++
++/**
++ * zh_sg_copy_to_user - Copy sg to userspace
++ * @*u_ptr: address in userspace to copy to
++ * @*size: bytes to copy, contains number of copied bytes on return
++ * @*sg: describing data to copy
++ * Return: copied bytes on success, -E* code else
++ */
++static int zh_sg_copy_to_user(char *u_ptr, size_t *size, struct sg_list *sg)
++{
++	unsigned int i;
++	unsigned int copy;
++	size_t left;
++	char *begin;
++
++	begin = u_ptr;
++	left = *size;
++	*size = 0;
++
++	for (i = 0; (i < sg->count) && (left > 0); ++i)	{
++		/* FIXME: What to check here? I think one assumption is
++		   (sg[i].address == 0) => (sg[i].offset == 0) which becomes
++		   (sg[i].address != 0) ||
++		   ((sg[i].address == 0) && sg[i].offset == 0) */
++		if ((NULL == sg->sg[i].address) && (0 != sg->sg[i].offset)) {
++			return -EINVAL;
++		}
++
++		copy = min(PAGE_SIZE, left);
++		copy = min(copy, sg->sg[i].length);
++
++		if (copy_to_user(u_ptr, sg->sg[i].address, copy)) {
++			return -EFAULT;
++		}
++
++		u_ptr += copy;
++		left -= copy;
++		*size += copy;
++	}
++
++	if ((u_ptr - begin) != zh_sg_get_size(sg)) {
++		return -ENOSPC;
++	} else {
++		return 0;
++	}
++}
++
++/**
++ * zh_sg_copy_to_user_truncate - Copy as much from sg to user as fits
++ * @*u_ptr: address in userspace to copy to
++ * @*size: bytes to copy, contains number of copied bytes on return
++ * @*sg: describing data to copy
++ * Return: 0 on success, -E* code else
++ *
++ * This wrapper to zh_sg_copy_to_user() simply ignores -ENOSPC errors, which is
++ * what we want when we use this function.
++ */
++static inline int zh_sg_copy_to_user_truncate(char *u_ptr, size_t *size,
++					      struct sg_list *sg)
++{
++	long long ret;
++	
++	ret = zh_sg_copy_to_user(u_ptr, size, sg);
++
++	if (-ENOSPC == ret) {
++		ret = 0;
++	}
++	
++	return ret;
++}
++
++/**
++ * zh_alloc_scsi_cmnd - Allocate and fill a Scsi_Cmnd
++ * @cmd: The scsi command as specified in the SCSI standards
++ * @cmd_size: size of cmd in bytes
++ * @*sg: pointer to scatterlist to retrieve response
++ * Return: The created Scsi_Cmnd on succes, else NULL
++ */
++static Scsi_Cmnd* zh_alloc_scsi_cmnd(void *cmd, size_t cmd_size,
++				     struct sg_list *sg)
++{
++	Scsi_Cmnd *sc;
++
++	sc = kmalloc(sizeof(*sc), GFP_KERNEL);
++	if (NULL == sc) {
++		return NULL;
++	}
++	
++	memset(sc, 0, sizeof(*sc));
++	/* zfcp uses host_scribble, bh_next and scsi_done, don't touch em */
++
++	sc->sc_data_direction = SCSI_DATA_READ;
++
++	sc->use_sg = sg->count;
++	sc->sglist_len = sg->count;
++	sc->buffer = sg->sg;
++	sc->bufflen = zh_sg_get_size(sg);
++	sc->request_buffer = sc->buffer;
++	sc->request_bufflen = sc->bufflen;
++
++	sc->cmd_len = cmd_size;
++
++	memcpy(sc->cmnd, cmd, cmd_size);
++	memcpy(sc->data_cmnd, cmd, cmd_size);
++
++	return sc;
++}
++
++/**
++ * zh_do_scsi_command - worker for sending scsi commands
++ * @devid: of the adpapter to send via
++ * @wwpn: of the port to send the command to
++ * @lun: to send the command to
++ * @cmd: SCSI command to send
++ * @cmd_size: size of the command in bytes
++ * @rsp: userspace pointer to copy the response to
++ * @rsp_size: size of the userspace buffer
++ * @sense: userspace pointer to copy sense data to
++ */
++static int zh_do_scsi_command(devid_t devid, wwn_t wwpn, fcp_lun_t lun,
++			      void *cmd, size_t cmd_size, struct sg_list *rsp,
++			      void *sense)
++{
++	Scsi_Cmnd *sc;
++	int ret;
++
++	sc = zh_alloc_scsi_cmnd(cmd, cmd_size, rsp);
++	if (NULL == sc) {
++		return -ENOMEM;
++	}
++
++	ret = zfcp_zh_send_scsi(DEVID_TO_DEVNO(devid), wwpn, lun, sc);
++
++	/* the scsi stack sets this, if there was a scsi error */
++	if (ret > 0) {
++		memcpy(sense, sc->sense_buffer, SCSI_SENSE_BUFFERSIZE);
++	}
++
++	kfree(sc);
++
++	return ret;
++}
++
++/**
++ * zh_ioc_scsi_report_luns - Send SCSI REPORT LUNS
++ * @*u_ptr: userspace pointer to copy data from and to
++ * Return: 0 on success, < 0 -E* code, > 0 SCSI ERROR
++ * Context: user
++ */
++static int zh_ioc_scsi_report_luns(struct zh_scsi_report_luns *u_ptr)
++{
++	int ret;
++	struct zh_scsi_report_luns ioc_data;
++
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data)))
++		return -EFAULT;
++
++	ret = zh_report_luns_helper(&ioc_data);
++
++	if (ret >= 0) {
++		if (copy_to_user(u_ptr, &ioc_data, sizeof(ioc_data))) {
++			return -EFAULT;
++		}
++		if (ret > 0) {
++			ret = 0;
++		}
++	}
++
++	return ret;
++}
++
++/**
++ * zh_report_luns_helper - Send SCSI REPORT LUNS
++ * @*u_ptr: userspace pointer to copy data from and to
++ * Return: 0 on success, < 0 -E* code, > 0 SCSI ERROR
++ * Context: user
++ */
++int zh_report_luns_helper(struct zh_scsi_report_luns *ioc_data)
++{
++	int ret;
++	size_t copy;
++	struct sg_list sg;
++	struct scsi_report_luns_cmd cmd = { 0 };
++
++	if (ioc_data->rsp_buffer_size < SCSI_REPORT_LUNS_SIZE_MIN) {
++		return -EINVAL;
++	}
++
++	ret = zfcp_zh_assert_fclun_zero(DEVID_TO_DEVNO(ioc_data->devid),
++					ioc_data->wwpn);
++	if (0 != ret) {
++		return ret;
++	}
++
++	ret = zh_sg_alloc(&sg, ioc_data->rsp_buffer_size);
++	if (ret < 0) {
++		return ret;
++	}
++	
++	cmd.op = REPORT_LUNS;
++	cmd.alloc_length = ioc_data->rsp_buffer_size;
++
++	ret = zh_do_scsi_command(ioc_data->devid, ioc_data->wwpn, 0, &cmd,
++				 sizeof(cmd), &sg, ioc_data->sense);
++
++	if (ret >= 0) {
++		copy = ioc_data->rsp_buffer_size;
++		ret =  zh_sg_copy_to_user_truncate(ioc_data->rsp_buffer, &copy,
++						   &sg);
++	}
++
++	zh_sg_free(&sg);
++
++	return ret;
++}
++
++/**
++ * zh_ioc_scsi_read_capacity - send SCSI READ CAPACITY
++ * @*u_ptr: userspace pointer to copy data from and to
++ * Return: 0 on success, < 0 -E* code, > 0 SCSI ERROR
++ * Context: user
++ */
++static int zh_ioc_scsi_read_capacity(struct zh_scsi_read_capacity *u_ptr)
++{
++	int ret;
++	struct sg_list sg;
++	struct zh_scsi_read_capacity ioc_data;
++	struct scsi_read_capacity_cmd cmd = { 0 };
++
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++
++	ret = zh_sg_alloc(&sg, ZH_SCSI_READ_CAPACITY_SIZE);
++	if (ret < 0) {
++		return ret;
++	}
++
++	cmd.op = READ_CAPACITY;
++
++	ret = zh_do_scsi_command(ioc_data.devid, ioc_data.wwpn,
++				 ioc_data.fclun, &cmd, sizeof(cmd), &sg,
++				 ioc_data.sense);
++
++	if (ret >= 0) {
++		memcpy(ioc_data.read_capacity, sg.sg[0].address,
++		       sg.sg[0].length);
++
++		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
++			ret = -EFAULT;
++		}
++	}
++
++	zh_sg_free(&sg);
++
++	return ret;
++}
++
++/**
++ * zh_ioc_scsi_inquiry - send SCSI INQUIRY
++ * @*u_ptr: userspace pointer to copy data from and to
++ * Return: 0 on success, < 0 -E* code, > 0 SCSI ERROR
++ * Context: user
++ */
++static int zh_ioc_scsi_inquiry(struct zh_scsi_inquiry *u_ptr)
++{
++	int ret;
++	struct sg_list sg;
++	struct zh_scsi_inquiry ioc_data;
++	struct scsi_inquiry_cmd cmd = { 0 };
++
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++
++	ret = zh_sg_alloc(&sg, ZH_SCSI_INQUIRY_SIZE);
++	if (ret < 0) {
++		return ret;
++	}
++
++	/* alloc_lentgh is of type u8, thus can only range to 255 */
++	cmd.op  = INQUIRY;
++	cmd.alloc_length = 255;
++
++	if (ioc_data.evpd)	{
++		cmd.evpd = 1;
++		cmd.page_code = ioc_data.page_code;
++	}
++
++	ret = zh_do_scsi_command(ioc_data.devid, ioc_data.wwpn,
++				 ioc_data.fclun, &cmd, sizeof(cmd), &sg,
++				 ioc_data.sense);
++
++	if (ret >= 0) {
++		memcpy(ioc_data.inquiry, sg.sg[0].address, sg.sg[0].length);
++
++		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
++			ret = -EFAULT;
++		}
++	}
++
++	zh_sg_free(&sg);
++
++	return ret;
++}
++
++/**
++ * zh_ioc_get_config - create private events for a specfific struct
++ * @*u_ptr: userspace pointer to copy data from
++ * @*filp: requesting fd
++ * Return: no of created events, else -E* code
++ * Context: user
++ *
++ * With this ioctl events are generated and attached to the fd only.  Used to
++ * enumerate currently configured adapters/ports/units. Subsequent calls
++ * discard prior created events.
++ */
++static int zh_ioc_get_config(struct zh_get_config *u_ptr, struct file *filp)
++{
++	struct zh_get_config ioc_data;
++	struct zh_client *head = (void*) filp->private_data;
++
++	if (!list_empty(&head->config)) {
++		zh_ioc_clear_config(filp);
++	}
++	
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++
++	return zfcp_zh_get_config(filp, DEVID_TO_DEVNO(ioc_data.devid),
++				  ioc_data.wwpn, ioc_data.flags);
++}
++
++/**
++ * zh_ioc_clear_config - remove config events from fd
++ * @filp: fd requesting to clear its config
++ * Return: always 0
++ * Context: user
++ */
++static int zh_ioc_clear_config(struct file *filp)
++{
++	struct zh_config *c;
++	struct list_head *go, *safe;
++	struct zh_client *client = (struct zh_client*) filp->private_data;
++
++	list_for_each_safe(go, safe, &client->config)
++	{
++		c = list_entry(go, struct zh_config, list);
++		list_del(go);
++		kfree(c);
++	}
++
++	INIT_LIST_HEAD(&client->config);
++
++	return 0;
++}
++
++/**
++ * zh_ioc_get_rnid() - get RNID from adapter
++ * @u_ptr: userspace pointer to copy data from and to
++ *
++ * Note: We set data in zfcp_hbaapi since we can not access the data
++ *	the adapter sends out.
++ * Note: wwpn and wwnn not set because not used in ZFCP HBA API Library
++ */
++static int zh_ioc_get_rnid(struct zh_get_rnid *u_ptr)
++{
++	struct zh_get_rnid ioc_data;
++
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++	
++	memset(&ioc_data.payload, 0, sizeof(ioc_data.payload));
++
++	ioc_data.payload.code = ZFCP_LS_RNID;
++	ioc_data.payload.node_id_format = 0xDF;
++	ioc_data.payload.common_id_length =
++		sizeof(struct zfcp_ls_rnid_common_id);
++	ioc_data.payload.specific_id_length =
++		sizeof(struct zfcp_ls_rnid_general_topology_id);
++
++	/* all other fields not set */
++	ioc_data.payload.specific_id.associated_type = 0x000000a;
++	ioc_data.payload.specific_id.physical_port_number = 1;
++	ioc_data.payload.specific_id.nr_attached_nodes = 1;
++
++	if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
++		return -EFAULT;
++	}
++
++	return 0;
++}
++
++/**
++ * zh_ioc_send_rnid - send an rnid to a port
++ * @*u_ptr: userspace pointer to copy data from and to
++ *
++ * Send a FC-FS ELS RNID to a discovered port.
++ */
++static int zh_ioc_send_rnid(struct zh_send_rnid *u_ptr)
++{
++	int ret;
++	struct sg_list sg_send, sg_receive;
++	struct zh_send_rnid ioc_data;
++	struct zfcp_ls_rnid *rnid;
++
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++
++	ret = zh_sg_alloc(&sg_send, sizeof(struct zfcp_ls_rnid));
++	if (ret < 0) {
++		return ret;
++	}
++
++	ret = zh_sg_alloc(&sg_receive, sizeof(struct zfcp_ls_rnid_acc));
++	if (ret < 0) {
++		goto free_send;
++	}
++
++	rnid = (void*) sg_send.sg[0].address;
++	rnid->code = ZFCP_LS_RNID;
++	rnid->node_id_format = 0xDF;
++
++	ret = zfcp_zh_send_els(DEVID_TO_DEVNO(ioc_data.devid), ioc_data.wwpn,
++			       sg_send.sg, sg_send.count, sg_receive.sg,
++			       sg_receive.count);
++	if (0 == ret) {
++		ioc_data.size = sg_receive.sg[0].length;
++		memcpy(&ioc_data.payload, sg_receive.sg[0].address,
++		       ioc_data.size);
++		
++		if (copy_to_user(u_ptr, &ioc_data, sizeof(*u_ptr))) {
++			ret = -EFAULT;
++		}
++	}
++
++	zh_sg_free(&sg_receive);
++free_send:
++	zh_sg_free(&sg_send);
++	
++	return ret;
++}
++
++/**
++ * zh_ioc_send_ct - send a Generic Service command
++ * @u_ptr: userspace pointer to parameter structure
++ * Return: 0 on success, -E* code else
++ *
++ * Send a FC-GS CT IU
++ */
++static int zh_ioc_send_ct(struct zh_send_ct *u_ptr)
++{
++	int ret;
++	struct zh_send_ct ioc_data;
++
++	if (copy_from_user(&ioc_data, u_ptr, sizeof(ioc_data))) {
++		return -EFAULT;
++	}
++
++	ret = zh_send_ct_helper(&ioc_data);
++
++	return ret;
++}
++
++/**
++ * zh_send_ct_helper - send a Generic Service command
++ * @send_ct: userspace pointer to parameter structure
++ * Return: 0 on success, -E* code else
++ *
++ * Send a FC-GS CT IU
++ */
++int zh_send_ct_helper(struct zh_send_ct *send_ct)
++{
++	int ret;
++	struct sg_list req, resp;
++
++	ret = zh_sg_alloc(&req, send_ct->req_length);
++	if (ret < 0) {
++		return ret;
++	}
++
++	if (zh_sg_copy_from_user(send_ct->req, send_ct->req_length, &req)) {
++		ret = -EFAULT;
++		goto free_req;
++	}
++
++	ret = zh_sg_alloc(&resp, send_ct->resp_length);
++	if (ret < 0) {
++		goto free_req;
++	}
++
++	ret = zfcp_zh_send_ct(DEVID_TO_DEVNO(send_ct->devid),
++			      req.sg, req.count, resp.sg, resp.count);
++
++	if (0 == ret) {
++		size_t size = send_ct->resp_length;
++		ret = zh_sg_copy_to_user_truncate(send_ct->resp, &size, &resp);
++	}
++
++	zh_sg_free(&resp);
++free_req:
++	zh_sg_free(&req);
++	
++	return ret;
++}
++
++/**
++ * prepare_event - prepare event structure
++ * @filp: fd event to attach
++ * @e: countable event
++ * @c: non-countable event
++ * Return: created event on success, else NULL
++ * Context: irq/user
++ *
++ * To be able to generate private and shared events with the same callbacks,
++ * this function creates a private or shared event depending on filp beeing NULL
++ * or not.
++ */
++static inline struct zh_event*
++prepare_event(struct file *filp, struct zh_event_item **e, struct zh_config **c)
++{
++	if (NULL != filp) {
++		*c = (struct zh_config*) kmalloc(sizeof(**c), GFP_ATOMIC);
++		if (NULL == *c) {
++			return NULL;
++		}
++
++		*e = NULL;
++		return &(*c)->event;
++	} else {
++		*e = (struct zh_event_item*) kmalloc(sizeof(**e), GFP_ATOMIC);
++		if (NULL == *e) {
++			return NULL;
++		}
++
++		*c = NULL;
++		return &(*e)->event;
++	}
++}
++
++/**
++ * zh_cb_adapter_add - Callback for adapter add events
++ * @filp: Address of struct file (see below)
++ * @devno: of the adapter
++ * @wwnn: of the adapter
++ * @wwpn: of the adapter
++ * Context: irq/user
++ * FIXME: check context
++ *
++ * Called in two situations:
++ *   - initial config creation (filp != NULL) (USER-ctx)
++ *   - adapter added to running system (filp == NULL) (IRQ-ctx)
++ *
++ * When called with (filp == NULL) the created event is added to the
++ * global list of events, and thus delivered to all registered fd's.
++ * When called with (filp != NULL) the created event is private to the
++ * specific filp, and is appended only there.
++ * */
++static void zh_cb_adapter_add(struct file *filp, devno_t devno, wwn_t wwnn,
++			      wwn_t wwpn)
++{
++	struct zh_event *event;
++	struct zh_event_item *e = NULL;
++	struct zh_config *c = NULL;
++	
++	ZH_LOG(KERN_DEBUG, "wwpn: 0x%llx\n", (long long) wwpn);
++
++	event = prepare_event(filp, &e, &c);
++	if (NULL == event) {
++		return;
++	}
++
++	event->event = ZH_EVENT_ADAPTER_ADD;
++	event->data.adapter_add.devid = ZH_DEVID(0, 0, devno);
++	event->data.adapter_add.wwnn = wwnn;
++	event->data.adapter_add.wwpn = wwpn;
++
++	add_event(filp, e, c);
++}
++
++/**
++ * zh_cb_port_add() - Called on port add events
++ * @filp: Address of struct file (see zh_cb_adapter_add)
++ * @devno: of the adapter
++ * @wwnn: of the port
++ * @wwpn: of the port
++ * @did: of the port
++ * Context: irq/user
++ * FIXME: check context
++ */
++static void zh_cb_port_add(struct file *filp, devno_t devno, wwn_t wwpn,
++			   wwn_t wwnn, fc_id_t did)
++{
++	struct zh_event *event;
++	struct zh_event_item *e;
++	struct zh_config *c;
++
++	ZH_LOG(KERN_DEBUG, "wwpn: 0x%llx\n", (long long) wwpn);
++
++	event = prepare_event(filp, &e, &c);
++	if (NULL == event) {
++		return;
++	}
++
++	event->event = ZH_EVENT_PORT_ADD;
++	event->data.port_add.devid = ZH_DEVID(0, 0, devno);
++	event->data.port_add.wwpn = wwpn;
++	event->data.port_add.wwnn = wwnn;
++	event->data.port_add.did = did;
++
++	add_event(filp, e, c);
++}
++
++/**
++ * zh_cb_unit_add() - Called on unit add events
++ * @filp: Address of struct file (see zh_cb_adapter_add)
++ * @devno: of the adapter
++ * @wwnn: of the port
++ * @wwpn: of the port
++ * @fclun: of the unit
++ * @host: SCSI host id
++ * @channel: SCSI channel
++ * @target: SCSI target
++ * @lun: SCSI lun
++ * Context: irq/user
++ * FIXME: check context
++ */
++static void zh_cb_unit_add(struct file *filp, devno_t devno, wwn_t wwpn,
++			   fcp_lun_t fclun, unsigned int host,
++			   unsigned int channel, unsigned int target,
++			   unsigned int lun)
++{
++	struct zh_event *event;
++	struct zh_event_item *e;
++	struct zh_config *c;
++
++	ZH_LOG(KERN_DEBUG, "wwpn: 0x%llx lun: 0x%llx\n", (long long) wwpn,
++	       (long long) fclun);
++
++	event = prepare_event(filp, &e, &c);
++	if (NULL == event) {
++		return;
++	}
++
++	event->event = ZH_EVENT_UNIT_ADD;
++	event->data.unit_add.devid = ZH_DEVID(0, 0, devno);
++	event->data.unit_add.wwpn = wwpn;
++	event->data.unit_add.fclun = fclun;
++	event->data.unit_add.host = host;
++	event->data.unit_add.channel = channel;
++	event->data.unit_add.id = target;
++	event->data.unit_add.lun = lun;
++
++	add_event(filp, e, c);
++}
++
++/**
++ * zh_cb_incomming_els_rscn - RSCN ELS worker
++ * @devno: of the adapter
++ * @s_id: sid of the sending port
++ * @v: pointer to rscn payload
++ * Context: irq
++ */
++static inline void zh_cb_incomming_els_rscn(const devno_t devno,
++					    const fc_id_t s_id, const void *v)
++{
++	int count, i;
++	struct zh_config *c;
++	const struct zh_els_rscn *rscn = v;
++	struct zh_els_rscn_payload *payload = rscn->payload;
++
++	ZH_LOG(KERN_INFO, "incoming RSCN\n");
++
++	count = rscn->payload_length / sizeof(struct zh_els_rscn_payload);
++	
++	for (i = 0; i < count; ++i, ++payload) {
++		c = kmalloc(sizeof(*c), GFP_ATOMIC);
++		if (NULL == c) {
++			return;
++		}
++
++		c->event.event = ZH_EVENT_POLLED;
++		c->event.data.polled.event = ZH_EVENT_POLLED_RSCN;
++		c->event.data.polled.devid = ZH_DEVID(0, 0, devno);
++		c->event.data.polled.data.rscn.port_fc_id = s_id;
++		memcpy(&c->event.data.polled.data.rscn.port_page, payload,
++		       sizeof(*payload));
++
++		add_event_to_polled(c);
++	}
++}
++
++/**
++ * zh_cb_incomming_els - hook for incomming els'
++ * @devno: of the adapter the els came in
++ * @s_id: sid of the port the els came in
++ * @*v: pointer to the els payload
++ * Context: irq
++ */
++static void zh_cb_incomming_els(const devno_t devno, const fc_id_t s_id,
++				const void *v)
++{
++	const u8 *op = v;
++
++	switch (*op)
++	{
++	case ZFCP_LS_RSCN:
++		zh_cb_incomming_els_rscn(devno, s_id, v);
++		break;
++	}
++}
++
++/**
++ * zh_cb_link_down - hook for link down events
++ * @id: s_id this event occured on
++ * Context: irq
++ *
++ * Atm this hook is only called for local link down events.
++ */
++static void zh_cb_link_down(const fc_id_t id)
++{
++	struct zh_config *c;
++
++	c = kmalloc(sizeof(*c), GFP_ATOMIC);
++	if (NULL == c) {
++		return;
++	}
++
++	c->event.event = ZH_EVENT_POLLED_LINK_DOWN;
++	c->event.data.polled.data.link.port_fc_id = id;
++
++	add_event_to_polled(c);
++}
++
++/**
++ * zh_cb_link_up - hook for link up events
++ * @id: s_id this event occured on
++ * Context: irq
++ *
++ * Atm this hook is only called for local link up events.
++ */
++static void zh_cb_link_up(const fc_id_t id)
++{
++	struct zh_config *c;
++
++	c = kmalloc(sizeof(*c), GFP_ATOMIC);
++	if (NULL == c) {
++		return;
++	}
++
++	c->event.event = ZH_EVENT_POLLED_LINK_UP;
++	c->event.data.polled.data.link.port_fc_id = id;
++
++	add_event_to_polled(c);
++}
++
++/**
++ * zh_map_port_speed - maps port speed between FC-FS/FC-GS4 and FC-HBA
++ * @speed: address of u32 to be mapped
++ */
++static void zh_map_port_speed(u32 *speed, int flag_operating_speed)
++{
++	if (flag_operating_speed == ZH_PORT_OPERATING_SPEED) {
++		switch(*speed) {
++		case 4:
++			*speed = 8;
++		case 8:	
++			*speed = 4;
++		case (1<<14):
++			*speed = 0;
++		}
++	} else {		/* ZH_PORT_SUPPORTED_SPEED */
++		switch(*speed) {
++		case 4:
++			*speed = 8;
++		case 8:
++			*speed = 4;
++		case (1<<15):
++			*speed = 0;
++		}
++	}
++
++	return;
++}
++
++module_init(zh_init);
++module_exit(zh_exit);
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/Config.in kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/scsi/Config.in	2006-01-30 22:25:26.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
+ fi
++
++if [ "$CONFIG_ARCH_S390" != "y" ]; then
+ dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
+ dep_tristate 'ACARD SCSI support' CONFIG_SCSI_ACARD $CONFIG_SCSI
+ dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
+@@ -263,6 +265,13 @@
+    fi
+ fi
+ 
++fi
++
++if [ "$CONFIG_ARCH_S390" = "y" ]; then
++   dep_tristate 'FCP host bus adapter driver for IBM z800, z900, z990 (GA2)' CONFIG_ZFCP $CONFIG_QDIO
++   dep_tristate 'HBA API support for FCP host bus adapter driver for IBM z990 (GA2)' CONFIG_ZFCP_HBAAPI $CONFIG_ZFCP
++fi
++
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/scsi/hosts.c	2006-01-30 22:25:26.000000000 -0700
+@@ -88,6 +88,24 @@
+ scsi_unregister(struct Scsi_Host * sh){
+     struct Scsi_Host * shpnt;
+     Scsi_Host_Name *shn;
++    char name[10];
++
++    /* kill error handling thread */
++    if (sh->hostt->use_new_eh_code
++        && sh->ehandler != NULL) {
++            DECLARE_MUTEX_LOCKED(sem);
++            
++            sh->eh_notify = &sem;
++            send_sig(SIGHUP, sh->ehandler, 1);
++            down(&sem);
++            sh->eh_notify = NULL;
++    }
++   
++    /* remove proc entry */
++#ifdef CONFIG_PROC_FS
++    sprintf(name, "%d", sh->host_no);
++    remove_proc_entry(name, sh->hostt->proc_dir);
++#endif
+         
+     if(scsi_hostlist == sh)
+ 	scsi_hostlist = sh->next;
+@@ -107,7 +125,35 @@
+     if (shn) shn->host_registered = 0;
+     /* else {} : This should not happen, we should panic here... */
+     
++#if 1
++    /* We shoult not decrement max_scsi_hosts (and make this value
++     * candidate for re-allocation by a different driver).
++     * Reason: the device is _still_ on the
++     * scsi_host_no_list and it's identified by its name. When the same
++     * device is re-registered it will get the same host_no again while
++     * new devices may use the allocation scheme and get this very same
++     * host_no.
++     * It's OK to have "holes" in the allocation but it does not mean
++     * "leaks".
++     */
++#else // if 0
++    /* If we are removing the last host registered, it is safe to reuse
++     * its host number (this avoids "holes" at boot time) (DB) 
++     * It is also safe to reuse those of numbers directly below which have
++     * been released earlier (to avoid some holes in numbering).
++     */
++    if(sh->host_no == max_scsi_hosts - 1) {
++	while(--max_scsi_hosts >= next_scsi_host) {
++	    shpnt = scsi_hostlist;
++	    while(shpnt && shpnt->host_no != max_scsi_hosts - 1)
++		shpnt = shpnt->next;
++	    if(shpnt)
++		break;
++	}
++    }
++#endif
+     next_scsi_host--;
++    sh->hostt->present--;
+ 
+     kfree((char *) sh);
+ }
+@@ -122,6 +168,7 @@
+     Scsi_Host_Name *shn, *shn2;
+     int flag_new = 1;
+     const char * hname;
++    char *name;
+     size_t hname_len;
+     retval = (struct Scsi_Host *)kmalloc(sizeof(struct Scsi_Host) + j,
+ 					 (tpnt->unchecked_isa_dma && j ? 
+@@ -252,6 +299,37 @@
+         }
+     }
+     
++#ifdef CONFIG_PROC_FS
++    build_proc_dir_entry(retval);
++#endif
++    
++    /* Start error handling thread */
++    if (retval->hostt->use_new_eh_code) {
++            DECLARE_MUTEX_LOCKED(sem);
++            
++            retval->eh_notify = &sem;
++            kernel_thread((int (*)(void *)) scsi_error_handler,
++                          (void *) retval, 0);
++            
++            /*
++             * Now wait for the kernel error thread to initialize itself
++             * as it might be needed when we scan the bus.
++             */
++            down(&sem);
++            retval->eh_notify = NULL;
++    }
++
++    tpnt->present++;
++
++    if (tpnt->info) {
++            name = (char *)tpnt->info(retval);
++    } else {
++            name = (char *)tpnt->name;
++    }
++    printk(KERN_INFO "scsi%d : %s\n",		/* And print a little message */
++                   retval->host_no, name);
++    
++    
+     return retval;
+ }
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/hosts.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/scsi/hosts.h	2006-01-30 22:25:26.000000000 -0700
+@@ -471,7 +471,10 @@
+ 
+ extern Scsi_Host_Template * scsi_hosts;
+ 
+-extern void build_proc_dir_entries(Scsi_Host_Template  *);
++#ifdef CONFIG_PROC_FS
++extern void build_proc_dir(Scsi_Host_Template *);
++extern void build_proc_dir_entry(struct Scsi_Host *);
++#endif
+ 
+ /*
+  *  scsi_init initializes the scsi hosts.
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/scsi/scsi.c	2006-01-30 22:25:26.000000000 -0700
+@@ -537,22 +537,10 @@
+ 				   SCpnt->target,
+ 				   atomic_read(&SCpnt->host->host_active),
+ 				   SCpnt->host->host_failed));
+-	if (SCpnt->host->host_failed != 0) {
+-		SCSI_LOG_ERROR_RECOVERY(5, printk("Error handler thread %d %d\n",
+-						SCpnt->host->in_recovery,
+-						SCpnt->host->eh_active));
 -	}
--#endif
--#endif /* CONFIG_PROC_FS */
+-	/*
+-	 * If the host is having troubles, then look to see if this was the last
+-	 * command that might have failed.  If so, wake up the error handler.
+-	 */
+-	if (SCpnt->host->in_recovery
+-	    && !SCpnt->host->eh_active
+-	    && SCpnt->host->host_busy == SCpnt->host->host_failed) {
+-		SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n",
+-			     atomic_read(&SCpnt->host->eh_wait->count)));
+-		up(SCpnt->host->eh_wait);
+-	}
++
++        /* Note: The eh_thread is now started in scsi_bottom_half_handler for 
++         * all cases except command timeout
++         */
+ 
+ 	spin_unlock_irqrestore(&device_request_lock, flags);
+ 
+@@ -1300,26 +1288,38 @@
+ 					SCpnt->owner = SCSI_OWNER_ERROR_HANDLER;
+ 					SCpnt->state = SCSI_STATE_FAILED;
+ 					SCpnt->host->in_recovery = 1;
+-					/*
+-					 * If the host is having troubles, then look to see if this was the last
+-					 * command that might have failed.  If so, wake up the error handler.
+-					 */
+-					if (SCpnt->host->host_busy == SCpnt->host->host_failed) {
+-						SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n",
+-										  atomic_read(&SCpnt->host->eh_wait->count)));
+-						up(SCpnt->host->eh_wait);
+-					}
+-				} else {
+-					/*
+-					 * We only get here if the error recovery thread has died.
+-					 */
++                                } else {
++                                        /* eh not present....trying to continue anyway */
+ 					scsi_finish_command(SCpnt);
+-				}
++                                }
++                                break;
++                        } // switch
++                        if (SCpnt->host->eh_wait != NULL) {
++                                /*
++                                 * If the host is having troubles, then look to see if this was the last
++                                 * command that might have failed.  If so, wake up the error handler.
++                                 */
++                                if (SCpnt->host->in_recovery && 
++                                    !SCpnt->host->eh_active &&
++                                    (SCpnt->host->host_busy == SCpnt->host->host_failed)) {
++                                        SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n",
++                                                                          atomic_read(&SCpnt->host->eh_wait->count)));
++                                        printk("(in_recovery=%d, host_busy=%d, host_failed=%d) "
++                                               "Waking error handler thread bh(%d)\n",
++                                               SCpnt->host->in_recovery,
++                                               SCpnt->host->host_busy,
++                                               SCpnt->host->host_failed,
++                                               atomic_read(&SCpnt->host->eh_wait->count));
++                                        up(SCpnt->host->eh_wait);
++                                }
++                        } else {
++                                SCSI_LOG_ERROR_RECOVERY(5, printk("Warning: eh_thread not present\n"));
+ 			}
++                        
+ 		}		/* for(; SCpnt...) */
 -
--	return 0;
--}
++                
+ 	}			/* while(1==1) */
 -
--#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");
++        
+ }
+ 
+ /*
+@@ -1868,7 +1868,6 @@
+ 	struct Scsi_Host *shpnt;
+ 	Scsi_Device *SDpnt;
+ 	struct Scsi_Device_Template *sdtpnt;
+-	const char *name;
+ 	unsigned long flags;
+ 	int out_of_space = 0;
+ 
+@@ -1895,10 +1894,17 @@
+ 
+ 	if (tpnt->use_new_eh_code) {
+ 		spin_lock_irqsave(&io_request_lock, flags);
+-		tpnt->present = tpnt->detect(tpnt);
++		tpnt->detect(tpnt);
+ 		spin_unlock_irqrestore(&io_request_lock, flags);
+-	} else
+-		tpnt->present = tpnt->detect(tpnt);
++	} else	tpnt->detect(tpnt);
++
++	/* Add the new driver to /proc/scsi (directory only) */
++#ifdef CONFIG_PROC_FS
++	build_proc_dir(tpnt);
++#endif
++
++	tpnt->next = scsi_hosts;	/* Add to the linked list */
++	scsi_hosts = tpnt;
+ 
+ 	if (tpnt->present) {
+ 		if (pcount == next_scsi_host) {
+@@ -1918,47 +1924,6 @@
+ 				return 1;
+ 			}
+ 		}
+-		tpnt->next = scsi_hosts;	/* Add to the linked list */
+-		scsi_hosts = tpnt;
 -
--int
--init_module (void)
--{
--#ifdef CONFIG_S390_TAPE_CHAR
--	tapechar_init ();
--#endif
--#ifdef CONFIG_S390_TAPE_BLOCK
--	tapeblock_init ();
+-		/* Add the new driver to /proc/scsi */
+-#ifdef CONFIG_PROC_FS
+-		build_proc_dir_entries(tpnt);
 -#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 */
+-		/*
+-		 * Add the kernel threads for each host adapter that will
+-		 * handle error correction.
+-		 */
+-		for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+-			if (shpnt->hostt == tpnt && shpnt->hostt->use_new_eh_code) {
+-				DECLARE_MUTEX_LOCKED(sem);
 -
--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;
--    }
--}
+-				shpnt->eh_notify = &sem;
+-				kernel_thread((int (*)(void *)) scsi_error_handler,
+-					      (void *) shpnt, 0);
 -
--inline int
--tapestate_get (tape_info_t * ti)
--{
--	return (ti->tape_state);
--}
+-				/*
+-				 * Now wait for the kernel error thread to initialize itself
+-				 * as it might be needed when we scan the bus.
+-				 */
+-				down(&sem);
+-				shpnt->eh_notify = NULL;
+-			}
+-		}
 -
--void
--tapestate_event (tape_info_t * ti, int event)
+-		for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+-			if (shpnt->hostt == tpnt) {
+-				if (tpnt->info) {
+-					name = tpnt->info(shpnt);
+-				} else {
+-					name = tpnt->name;
+-				}
+-				printk(KERN_INFO "scsi%d : %s\n",		/* And print a little message */
+-				       shpnt->host_no, name);
+-			}
+-		}
+ 
+ 		/* The next step is to call scan_scsis here.  This generates the
+ 		 * Scsi_Devices entries
+@@ -2036,7 +2001,6 @@
+ 	struct Scsi_Device_Template *sdtpnt;
+ 	struct Scsi_Host *sh1;
+ 	struct Scsi_Host *shpnt;
+-	char name[10];	/* host_no>=10^9? I don't think so. */
+ 
+ 	/* get the big kernel lock, so we don't race with open() */
+ 	lock_kernel();
+@@ -2138,18 +2102,6 @@
+ 	/*
+ 	 * Next, kill the kernel error recovery thread for this host.
+ 	 */
+-	for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
+-		if (shpnt->hostt == tpnt
+-		    && shpnt->hostt->use_new_eh_code
+-		    && shpnt->ehandler != NULL) {
+-			DECLARE_MUTEX_LOCKED(sem);
+-
+-			shpnt->eh_notify = &sem;
+-			send_sig(SIGHUP, shpnt->ehandler, 1);
+-			down(&sem);
+-			shpnt->eh_notify = NULL;
+-		}
+-	}
+ 
+ 	/* Next we free up the Scsi_Cmnd structures for this host */
+ 
+@@ -2178,9 +2130,6 @@
+ 		if (shpnt->hostt != tpnt)
+ 			continue;
+ 		pcount = next_scsi_host;
+-		/* Remove the /proc/scsi directory entry */
+-		sprintf(name,"%d",shpnt->host_no);
+-		remove_proc_entry(name, tpnt->proc_dir);
+ 		if (tpnt->release)
+ 			(*tpnt->release) (shpnt);
+ 		else {
+@@ -2197,7 +2146,6 @@
+ 		}
+ 		if (pcount == next_scsi_host)
+ 			scsi_unregister(shpnt);
+-		tpnt->present--;
+ 	}
+ 
+ 	/*
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/scsi/scsi.h	2006-01-30 22:25:26.000000000 -0700
+@@ -18,6 +18,7 @@
+ #include <linux/config.h>	/* for CONFIG_SCSI_LOGGING */
+ #include <linux/devfs_fs_kernel.h>
+ #include <linux/proc_fs.h>
++#include <linux/blkdev.h>
+ 
+ /*
+  * 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/scsi/scsi_lib.c	2006-01-30 22:25:26.000000000 -0700
+@@ -256,12 +256,32 @@
+ 	if (SCpnt != NULL) {
+ 
+ 		/*
++		 * This is a work around for a case where this Scsi_Cmnd
++		 * may have been through the busy retry paths already. We
++		 * clear the special flag and try to restore the
++		 * read/write request cmd value.
++		 */
++		if (SCpnt->request.cmd == SPECIAL)
++			SCpnt->request.cmd = 
++				(SCpnt->sc_data_direction ==
++				 SCSI_DATA_WRITE) ? WRITE : READ;
++
++		/*
+ 		 * For some reason, we are not done with this request.
+ 		 * This happens for I/O errors in the middle of the request,
+ 		 * in which case we need to request the blocks that come after
+ 		 * the bad sector.
+ 		 */
+ 		SCpnt->request.special = (void *) SCpnt;
++                /*
++                 * We need to recount the number of
++                 * scatter-gather segments here - the
++                 * normal case code assumes this to be
++                 * correct, as it would be a performance
++                 * loss to always recount.  Handling
++                 * errors is always unusual, of course.
++                 */
++                recount_segments(SCpnt);
+ 		list_add(&SCpnt->request.queue, &q->queue_head);
+ 	}
+ 
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi_proc.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/scsi/scsi_proc.c	2006-01-30 22:25:26.000000000 -0700
+@@ -120,35 +120,34 @@
+ 	return(ret);
+ }
+ 
+-void build_proc_dir_entries(Scsi_Host_Template * tpnt)
 -{
--#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);
--            }
+-	struct Scsi_Host *hpnt;
+-	char name[10];	/* see scsi_unregister_host() */
++void build_proc_dir_entry(struct Scsi_Host *shpnt) {
++	char name[10]; /* host_no>=10^9? I don't think so. */
++	struct proc_dir_entry *p;
++
++	if(shpnt->hostt->proc_dir) {
++		sprintf(name, "%d", shpnt->host_no);
++		p = create_proc_read_entry(
++			name,
++			S_IFREG | S_IRUGO | S_IWUSR,
++			shpnt->hostt->proc_dir,
++			proc_scsi_read,
++			(void *) shpnt
++		);
++		if (!p)
++			panic("Not enough memory to register SCSI HBA in /proc/scsi !\n");
++		p->write_proc=proc_scsi_write;
++		p->owner = shpnt->hostt->module;
++	}
++}
+ 
++void build_proc_dir(Scsi_Host_Template * tpnt)
++{
+ 	tpnt->proc_dir = proc_mkdir(tpnt->proc_name, proc_scsi);
+-        if (!tpnt->proc_dir) {
+-                printk(KERN_ERR "Unable to proc_mkdir in scsi.c/build_proc_dir_entries");
+-                return;
 -        }
--}
+-	tpnt->proc_dir->owner = tpnt->module;
 -
--/*
-- * 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:
-- */
-=== drivers/s390/qdio.c
-==================================================================
---- drivers/s390/qdio.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/s390/qdio.c   (/trunk/2.4.27)   (revision 52)
-@@ -57,7 +57,7 @@
+-	hpnt = scsi_hostlist;
+-	while (hpnt) {
+-		if (tpnt == hpnt->hostt) {
+-			struct proc_dir_entry *p;
+-			sprintf(name,"%d",hpnt->host_no);
+-			p = create_proc_read_entry(name,
+-					S_IFREG | S_IRUGO | S_IWUSR,
+-					tpnt->proc_dir,
+-					proc_scsi_read,
+-					(void *)hpnt);
+-			if (!p)
+-				panic("Not enough memory to register SCSI HBA in /proc/scsi !\n");
+-			p->write_proc=proc_scsi_write;
+-			p->owner = tpnt->module;
+-		}
+-		hpnt = hpnt->next;
++	if (!tpnt->proc_dir) {
++		printk(KERN_ERR "Unable to proc_mkdir in scsi.c/build_proc_dir_entries");
++		return;
+ 	}
++	tpnt->proc_dir->owner = tpnt->module;
+ }
+ 
+ /*
+diff -urN kernel-source-2.4.27-2.4.27.orig/drivers/scsi/scsi_queue.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/drivers/scsi/scsi_queue.c	2006-01-30 22:25:26.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.
+ 		 */
+-		if (host->host_busy == 0) {
++		if (host->host_busy == 1) {
+ 			if (scsi_retry_command(cmd) == 0) {
+ 				return 0;
+ 			}
+@@ -118,7 +118,7 @@
+ 		 * If a host is inactive and cannot queue any commands, I don't see
+ 		 * how things could possibly work anyways.
+ 		 */
+-		if (cmd->device->device_busy == 0) {
++		if (cmd->device->device_busy == 1) {
+ 			if (scsi_retry_command(cmd) == 0) {
+ 				return 0;
+ 			}
+diff -urN kernel-source-2.4.27-2.4.27.orig/fs/Config.in kernel-source-2.4.27-2.4.27/fs/Config.in
+--- kernel-source-2.4.27-2.4.27.orig/fs/Config.in	2006-01-30 22:23:45.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/fs/Config.in	2006-01-30 22:25:23.000000000 -0700
+@@ -100,6 +100,8 @@
+ 
+ tristate 'ROM file system support' CONFIG_ROMFS_FS
+ 
++tristate 'XIP2 execute-in-place filesystem support' CONFIG_XIP2FS 
++
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/partitions/check.c	2006-01-30 22:25:23.000000000 -0700
+@@ -89,7 +89,7 @@
+ #ifdef CONFIG_ARCH_S390
+ int (*genhd_dasd_name)(char*,int,int,struct gendisk*) = NULL;
+ int (*genhd_dasd_ioctl)(struct inode *inp, struct file *filp,
+-			    unsigned int no, unsigned long data);
++			unsigned int no, unsigned long data);
+ EXPORT_SYMBOL(genhd_dasd_name);
+ EXPORT_SYMBOL(genhd_dasd_ioctl);
+ #endif
+@@ -281,6 +281,7 @@
+ 	int devnum = minor >> dev->minor_shift;
+ 	devfs_handle_t dir;
+ 	unsigned int devfs_flags = DEVFS_FL_DEFAULT;
++	umode_t devfs_perm  = S_IFBLK | S_IRUSR | S_IWUSR;
+ 	char devname[16];
+ 
+ 	if (dev->part[minor + part].de) return;
+@@ -288,11 +289,14 @@
+ 	if (!dir) return;
+ 	if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
+ 		devfs_flags |= DEVFS_FL_REMOVABLE;
++	if (is_read_only(MKDEV(dev->major, minor+part))) {
++	        devfs_perm &= ~(S_IWUSR);
++	}
+ 	sprintf (devname, "part%d", part);
+ 	dev->part[minor + part].de =
+ 	    devfs_register (dir, devname, devfs_flags,
+ 			    dev->major, minor + part,
+-			    S_IFBLK | S_IRUSR | S_IWUSR,
++			    devfs_perm,
+ 			    dev->fops, NULL);
+ }
+ 
+@@ -304,12 +308,16 @@
+ 	int devnum = minor >> dev->minor_shift;
+ 	devfs_handle_t dir, slave;
+ 	unsigned int devfs_flags = DEVFS_FL_DEFAULT;
++	umode_t devfs_perm  = S_IFBLK | S_IRUSR | S_IWUSR;
+ 	char dirname[64], symlink[16];
+ 	static devfs_handle_t devfs_handle;
+ 
+ 	if (dev->part[minor].de) return;
+ 	if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
+ 		devfs_flags |= DEVFS_FL_REMOVABLE;
++	if (is_read_only(MKDEV(dev->major, minor))) {
++	        devfs_perm &= ~(S_IWUSR);
++	}
+ 	if (dev->de_arr) {
+ 		dir = dev->de_arr[devnum];
+ 		if (!dir)  /*  Aware driver wants to block disc management  */
+@@ -331,7 +339,7 @@
+ 			  dirname + pos, &slave, NULL);
+ 	dev->part[minor].de =
+ 	    devfs_register (dir, "disc", devfs_flags, dev->major, minor,
+-			    S_IFBLK | S_IRUSR | S_IWUSR, dev->fops, NULL);
++			    devfs_perm, dev->fops, NULL);
+ 	devfs_auto_unregister (dev->part[minor].de, slave);
+ 	if (!dev->de_arr)
+ 		devfs_auto_unregister (slave, dir);
+@@ -359,6 +367,10 @@
+ 		dev->part[minor].de = NULL;
+ 		devfs_dealloc_unique_number (&disc_numspace,
+ 					     dev->part[minor].number);
++		if(dev->label_arr && dev->label_arr[minor >> dev->minor_shift]) {
++			devfs_unregister(dev->label_arr[minor >> dev->minor_shift]);
++			dev->label_arr[minor >> dev->minor_shift] = NULL;
++		}
+ 	}
+ #endif  /*  CONFIG_DEVFS_FS  */
+ }
+@@ -415,6 +427,93 @@
+ 	}
+ }
+ 
++/*
++ * This function creates a link from /dev/labels/<labelname> to the devfs
++ * device directory. The device driver must allocate memory to the label_arr
++ * for this to work.
++ * This enables devices/partition tables that support labels to be accessed
++ * by that name instead of the device name (which can change if devices are
++ * moved around).
++ *
++ * Current restictions:
++ *   - Only the first device that uses a certain label creates the link
++ *     (which can also be good in case there is a backup device)
++ *   - When removing devices that created labels previously suppressed
++ *     devices won't show up.
++ */
++void register_disk_label(struct gendisk *hd, int minor, char *label) {
++#ifdef CONFIG_DEVFS_FS
++	int                   disknum         = minor >> hd->minor_shift;
++	static devfs_handle_t devfs_label_dir = NULL;
++	int                   i;
++
++	/*
++	 * Check the given label. Trailing whitespaces are removed. Otherwise
++	 * only alphanumeric characters are allowed. (fstab)
++	 * Added [$#%@] since these are allowed by fdasd and seem
++	 * to work in fstab.
++	 */
++	for(i=0; label[i] != '\0'; i++);
++	for(i--; i >= 0; i--) {
++		if(label[i] == ' ' && label[i+1] == '\0') {
++			label[i] = '\0';
++			continue;
++		}
++		if(
++			label[i] == '$' || label[i] == '#' ||
++			label[i] == '@' || label[i] == '%'
++		)
++			continue;
++		if(label[i] >= 'a' && label[i] <= 'z')
++			continue;
++		if(label[i] >= 'A' && label[i] <= 'Z')
++			continue;
++		if(label[i] >= '0' && label[i] <= '9')
++			continue;
++
++		printk(KERN_WARNING "\nregister_disk_label: invalid character(s)"
++			" in label <%s>\n", label);
++		printk(KERN_WARNING "register_label: refusing to create devfs entry.\n");
++		return;
++	}
++
++	if(!hd->label_arr)
++		return;
++
++	if(!devfs_label_dir)
++		if(!(devfs_label_dir = devfs_mk_dir(NULL, "labels", NULL)))
++			return;
++
++	if(hd->label_arr[disknum]) {
++		if(strcmp(devfs_get_name(hd->label_arr[disknum], NULL), label) == 0)
++			return;
++
++		devfs_unregister(hd->label_arr[disknum]);
++		hd->label_arr[disknum] = NULL;
++	}
++	if(!devfs_find_handle(devfs_label_dir, label, 0, 0, 0, 0)) {
++		int  pos      = 0;
++		char path[64];
++
++		if(hd->de_arr) {
++			if(!hd->de_arr[disknum])
++				return;
++
++			pos = devfs_generate_path(hd->de_arr[disknum], path+3, sizeof(path)-3);
++			if(pos < 0)
++				return;
++
++			strncpy(path+pos, "../", 3);
++		} else {
++			sprintf(path, "../%s/disc/%d", hd->major_name, disknum);
++		}
++		devfs_mk_symlink(
++			devfs_label_dir, label, DEVFS_FL_DEFAULT, path+pos,
++			&hd->label_arr[disknum], NULL);
++	}
++#endif
++}
++
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/partitions/check.h	2006-01-30 22:25:23.000000000 -0700
+@@ -13,4 +13,6 @@
+ 	page_cache_release(p.v);
+ }
+ 
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/partitions/ibm.c	2006-01-30 22:25:23.000000000 -0700
+@@ -9,6 +9,7 @@
+  * 07/10/00 Fixed detection of CMS formatted disks     
+  * 02/13/00 VTOC partition support added
+  * 12/27/01 fixed PL030593 (CMS reserved minidisk not detected on 64 bit)
++ * 07/24/03 no longer using contents of freed page for CMS label recognition (BZ3611)
+  */
+ 
+ #include <linux/config.h>
+@@ -134,6 +135,9 @@
+ 	EBCASC(type, 4);
+ 	EBCASC(name, 6);
+ 
++	if(name[0] != '\0')
++		register_disk_label(hd, MINOR(to_kdev_t(bdev->bd_dev)), name);
++
+ 	/*
+ 	 * Three different types: CMS1, VOL1 and LNX1/unlabeled
+ 	 */
+@@ -141,7 +145,7 @@
+ 		/*
+ 		 * VM style CMS1 labeled disk
+ 		 */
+-		int *label = (int *) data;
++		int *label = (int *) vlabel;
+ 
+ 		if (label[13] != 0) {
+ 			printk("CMS1/%8s(MDSK):", name);
+@@ -158,7 +162,8 @@
+ 		add_gd_partition(hd, first_part_minor,
+ 				 offset*(blocksize >> 9),
+ 				 size-offset*(blocksize >> 9));
+-	} else if (strncmp(type, "VOL1", 4) == 0) {
++	} else if ((strncmp(type, "VOL1", 4) == 0) &&
++		(!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) {
+ 		/*
+ 		 * New style VOL1 labeled disk
+ 		 */
+diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/Makefile kernel-source-2.4.27-2.4.27/fs/xip2fs/Makefile
+--- kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/Makefile	1969-12-31 17:00:00.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/xip2fs/balloc.c	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,83 @@
++/*
++ *  linux/fs/xip2/balloc.c, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
++ */
++
++#include <linux/config.h>
++#include <linux/fs.h>
++#include <linux/xip2_fs.h>
++#include <linux/locks.h>
++#include <linux/quotaops.h>
++
++/*
++ * balloc.c contains the blocks allocation and deallocation routines
++ */
++
++unsigned long xip2_count_free_blocks (struct super_block * sb)
++{
++	return le32_to_cpu(sb->u.xip2_sb.s_es->s_free_blocks_count);
++}
++
++static inline int test_root(int a, int b)
++{
++	if (a == 0)
++		return 1;
++	while (1) {
++		if (a == 1)
++			return 1;
++		if (a % b)
++			return 0;
++		a = a / b;
++	}
++}
++
++int xip2_group_sparse(int group)
++{
++	return (test_root(group, 3) || test_root(group, 5) ||
++		test_root(group, 7));
++}
++
++/**
++ *	xip_bg_has_super - number of blocks used by the superblock in group
++ *	@sb: superblock for filesystem
++ *	@group: group number to check
++ *
++ *	Return the number of blocks used by the superblock (primary or backup)
++ *	in this group.  Currently this will be only 0 or 1.
++ */
++int xip2_bg_has_super(struct super_block *sb, int group)
++{
++	if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
++	    && !xip2_group_sparse(group))
++		return 0;
++	return 1;
++}
++
++/**
++ *	xip2_bg_num_gdb - number of blocks used by the group table in group
++ *	@sb: superblock for filesystem
++ *	@group: group number to check
++ *
++ *	Return the number of blocks used by the group descriptor table
++ *	(primary or backup) in this group.  In the future there may be a
++ *	different number of descriptor blocks in each group.
++ */
++unsigned long xip2_bg_num_gdb(struct super_block *sb, int group)
++{
++	if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
++	    && !xip2_group_sparse(group))
++		return 0;
++	return XIP2_SB(sb)->s_gdb_count;
++}
++
++
++void* xip2_maread (xip2_mem_area_t* mem_area, int block, int size) {
++	if ((block+1)*size-1 > (unsigned long)mem_area->end -
++		(unsigned long)mem_area->start) {
++		return NULL;
++	}
++	return (void*)(mem_area->start + block*size);
++}
+diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/dir.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/xip2fs/dir.c	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,302 @@
++/*
++ *  linux/fs/xip2/dir.c, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
++ */
++
++#include <linux/fs.h>
++#include <linux/xip2_fs.h>
++#include <linux/pagemap.h>
++
++typedef struct ext2_dir_entry_2 ext2_dirent;
++
++static inline unsigned xip2_chunk_size(struct inode *inode)
++{
++	return inode->i_sb->s_blocksize;
++}
++
++static inline unsigned long dir_pages(struct inode *inode)
++{
++	return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
++}
++
++static int xip2_check_page(struct inode* dir, unsigned long index, void *kaddr)
++{
++	struct super_block *sb = dir->i_sb;
++	unsigned chunk_size = xip2_chunk_size(dir);
++	u32 max_inumber = le32_to_cpu(sb->u.xip2_sb.s_es->s_inodes_count);
++	unsigned offs, rec_len;
++	unsigned limit = PAGE_CACHE_SIZE;
++	ext2_dirent *p;
++	char *error;
++
++	if ((dir->i_size >> PAGE_CACHE_SHIFT) == index) {
++		limit = dir->i_size & ~PAGE_CACHE_MASK;
++		if (limit & (chunk_size - 1))
++			goto Ebadsize;
++		for (offs = limit; offs<PAGE_CACHE_SIZE; offs += chunk_size) {
++			ext2_dirent *p = (ext2_dirent*)(kaddr + offs);
++			p->rec_len = cpu_to_le16(chunk_size);
++		}
++		if (!limit)
++			goto out;
++	}
++	for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) {
++		p = (ext2_dirent *)(kaddr + offs);
++		rec_len = le16_to_cpu(p->rec_len);
++
++		if (rec_len < EXT2_DIR_REC_LEN(1))
++			goto Eshort;
++		if (rec_len & 3)
++			goto Ealign;
++		if (rec_len < EXT2_DIR_REC_LEN(p->name_len))
++			goto Enamelen;
++		if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1))
++			goto Espan;
++		if (le32_to_cpu(p->inode) > max_inumber)
++			goto Einumber;
++	}
++	if (offs != limit)
++		goto Eend;
++out:
++	return 0;
++
++	/* Too bad, we had an error */
++
++Ebadsize:
++	xip2_error(sb, "xip2_check_page",
++		"size of directory #%lu is not a multiple of chunk size",
++		dir->i_ino
++	);
++	goto fail;
++Eshort:
++	error = "rec_len is smaller than minimal";
++	goto bad_entry;
++Ealign:
++	error = "unaligned directory entry";
++	goto bad_entry;
++Enamelen:
++	error = "rec_len is too small for name_len";
++	goto bad_entry;
++Espan:
++	error = "directory entry across blocks";
++	goto bad_entry;
++Einumber:
++	error = "inode out of bounds";
++bad_entry:
++	xip2_error (sb, "xip2_check_page", "bad entry in directory #%lu: %s - "
++		"offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
++		dir->i_ino, error, (index<<PAGE_CACHE_SHIFT)+offs,
++		(unsigned long) le32_to_cpu(p->inode),
++		rec_len, p->name_len);
++	goto fail;
++Eend:
++	p = (ext2_dirent *)(kaddr + offs);
++	xip2_error (sb, "xip2_check_page",
++		"entry in directory #%lu spans the page boundary"
++		"offset=%lu, inode=%lu",
++		dir->i_ino, (index<<PAGE_CACHE_SHIFT)+offs,
++		(unsigned long) le32_to_cpu(p->inode));
++fail:
++	return -EBADFD;
++}
++
++
++/*
++ * NOTE! unlike strncmp, xip2_match returns 1 for success, 0 for failure.
++ *
++ * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller.
++ */
++static inline int xip2_match (int len, const char * const name,
++					struct ext2_dir_entry_2 * de)
++{
++	if (len != de->name_len)
++		return 0;
++	if (!de->inode)
++		return 0;
++	return !memcmp(name, de->name, len);
++}
++
++/*
++ * p is at least 6 bytes before the end of page
++ */
++static inline ext2_dirent *xip2_next_entry(ext2_dirent *p)
++{
++	return (ext2_dirent *)((char*)p + le16_to_cpu(p->rec_len));
++}
++
++static inline unsigned 
++xip2_validate_entry(char *base, unsigned offset, unsigned mask)
++{
++	ext2_dirent *de = (ext2_dirent*)(base + offset);
++	ext2_dirent *p = (ext2_dirent*)(base + (offset&mask));
++	while ((char*)p < (char*)de)
++		p = xip2_next_entry(p);
++	return (char *)p - base;
++}
++
++static unsigned char xip2_filetype_table[EXT2_FT_MAX] = {
++	[EXT2_FT_UNKNOWN]	DT_UNKNOWN,
++	[EXT2_FT_REG_FILE]	DT_REG,
++	[EXT2_FT_DIR]		DT_DIR,
++	[EXT2_FT_CHRDEV]	DT_CHR,
++	[EXT2_FT_BLKDEV]	DT_BLK,
++	[EXT2_FT_FIFO]		DT_FIFO,
++	[EXT2_FT_SOCK]		DT_SOCK,
++	[EXT2_FT_SYMLINK]	DT_LNK,
++};
++
++#define S_SHIFT 12
++static unsigned char xip2_type_by_mode[S_IFMT >> S_SHIFT] = {
++	[S_IFREG >> S_SHIFT]	EXT2_FT_REG_FILE,
++	[S_IFDIR >> S_SHIFT]	EXT2_FT_DIR,
++	[S_IFCHR >> S_SHIFT]	EXT2_FT_CHRDEV,
++	[S_IFBLK >> S_SHIFT]	EXT2_FT_BLKDEV,
++	[S_IFIFO >> S_SHIFT]	EXT2_FT_FIFO,
++	[S_IFSOCK >> S_SHIFT]	EXT2_FT_SOCK,
++	[S_IFLNK >> S_SHIFT]	EXT2_FT_SYMLINK,
++};
++
++static inline void xip2_set_de_type(ext2_dirent *de, struct inode *inode)
++{
++	mode_t mode = inode->i_mode;
++	if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, 
++				      EXT2_FEATURE_INCOMPAT_FILETYPE))
++		de->file_type = xip2_type_by_mode[(mode & S_IFMT)>>S_SHIFT];
++	else
++		de->file_type = 0;
++}
++
++static int
++xip2_readdir (struct file * filp, void * dirent, filldir_t filldir)
++{
++	int err;
++	loff_t pos = filp->f_pos;
++	unsigned long blockno;
++	struct inode *inode = filp->f_dentry->d_inode;
++	struct super_block *sb = inode->i_sb;
++	unsigned offset = pos & ~PAGE_CACHE_MASK;
++	unsigned long n = pos >> PAGE_CACHE_SHIFT;
++	unsigned long npages = dir_pages(inode);
++	unsigned chunk_mask = ~(xip2_chunk_size(inode)-1);
++	unsigned char *types = NULL;
++	int need_revalidate = (filp->f_version != inode->i_version);
++
++	if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
++		goto done;
++
++	if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
++		types = xip2_filetype_table;
++
++	for ( ; n < npages; n++, offset = 0) {
++		char *kaddr, *limit;
++		ext2_dirent *de;
++		err=xip2_get_block(inode, n, &blockno, 0);
++		if (err) goto done;
++		kaddr = xip2_maread (inode->i_sb->u.xip2_sb.mem_area, 
++				     blockno, PAGE_CACHE_SIZE);
++		if (kaddr==NULL) 
++			BUG();
++		xip2_check_page(inode, n, kaddr);
++		if (need_revalidate) {
++			offset = xip2_validate_entry(kaddr, offset, 
++						     chunk_mask);
++			need_revalidate = 0;
++		}
++		de = (ext2_dirent *)(kaddr+offset);
++		limit = kaddr + PAGE_CACHE_SIZE - EXT2_DIR_REC_LEN(1);
++		for ( ;(char*)de <= limit; de = xip2_next_entry(de))
++			if (de->inode) {
++				int over;
++				unsigned char d_type = DT_UNKNOWN;
++
++				if (types && de->file_type < EXT2_FT_MAX)
++					d_type = types[de->file_type];
++
++				offset = (char *)de - kaddr;
++				over = filldir(dirent, de->name, de->name_len,
++						(n<<PAGE_CACHE_SHIFT) | offset,
++						le32_to_cpu(de->inode), d_type);
++				if (over) {
++					goto done;
++				}
++			}
++	}
++
++done:
++	filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset;
++	filp->f_version = inode->i_version;
++	UPDATE_ATIME(inode);
++	return 0;
++}
++
++/*
++ *	xip2_find_entry()
++ *
++ * finds an entry in the specified directory with the wanted name. It
++ * returns the page in which the entry was found, and the entry itself
++ * (as a parameter - res_dir). Page is returned mapped and unlocked.
++ * Entry is guaranteed to be valid.
++ */
++struct ext2_dir_entry_2 * xip2_find_entry (struct inode * dir,
++			struct dentry *dentry)
++{
++	unsigned long blockno;
++	const char *name = dentry->d_name.name;
++	int err;
++	int namelen = dentry->d_name.len;
++	unsigned reclen = EXT2_DIR_REC_LEN(namelen);
++	unsigned long start, n;
++	unsigned long npages = dir_pages(dir);
++	ext2_dirent * de;
++
++	start = dir->u.ext2_i.i_dir_start_lookup;
++	if (start >= npages)
++		start = 0;
++	n = start;
++	do {
++		char *kaddr;
++		err=xip2_get_block(dir, n, &blockno, 0);
++		if (err) return NULL;
++		kaddr = xip2_maread (dir->i_sb->u.xip2_sb.mem_area, blockno, 
++				     PAGE_CACHE_SIZE);
++		if (kaddr==NULL) 
++			BUG();
++		if (!xip2_check_page(dir, n, kaddr)) {
++			de = (ext2_dirent *) kaddr;
++			kaddr += PAGE_CACHE_SIZE - reclen;
++			while ((char *) de <= kaddr) {
++				if (xip2_match (namelen, name, de))
++					goto found;
++				de = xip2_next_entry(de);
++			}
++		}
++		if (++n >= npages)
++			n = 0;
++	} while (n != start);
++	return NULL;
++
++found:
++	dir->u.ext2_i.i_dir_start_lookup = n;
++	return de;
++}
++
++ino_t xip2_inode_by_name(struct inode * dir, struct dentry *dentry)
++{
++	ino_t res = 0;
++	struct ext2_dir_entry_2 * de;
++	
++	de = xip2_find_entry (dir, dentry);
++	if (de) {
++		res = le32_to_cpu(de->inode);
++	}
++	return res;
++}
++
++struct file_operations xip2_dir_operations = {
++	read:		generic_read_dir,
++	readdir:	xip2_readdir,
++	ioctl:		xip2_ioctl,
++};
+diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/file.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/xip2fs/file.c	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,169 @@
++/*
++ *  linux/fs/xip2/file.c, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
++ */
++
++#include <linux/fs.h>
++#include <linux/xip2_fs.h>
++#include <linux/sched.h>
++#include <linux/mm.h>
++#include <linux/pagemap.h>
++#include <asm/uaccess.h>
++#include <asm/page.h>
++#include <asm/pgtable.h>
++
++/*
++ * We have mostly NULL's here: the current defaults are ok for
++ * the xip2 filesystem. for mmap, we use a special implementation
++ * that provides execute in place functionality
++ */
++struct file_operations xip2_file_operations = {
++	llseek:		generic_file_llseek,
++	read:		xip2_file_read,
++	write:		generic_file_write,
++	ioctl:		xip2_ioctl,
++	mmap:		xip2_file_mmap,
++	open:		generic_file_open,
++};
++
++struct inode_operations xip2_file_inode_operations = {};
++
++static struct vm_operations_struct xip2_file_vm_ops = {
++	nopage:		xip2_nopage_in_place,
++};
++
++/* This is used for a general mmap of a disk file */
++
++int xip2_file_mmap(struct file * file, struct vm_area_struct * vma)
++{
++	struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
++	struct inode *inode = mapping->host;
++
++	if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
++		if (!mapping->a_ops->writepage)
++			return -EINVAL;
++}
++	if (!mapping->a_ops->readpage)
++		return -ENOEXEC;
++	UPDATE_ATIME(inode);
++	vma->vm_ops = &xip2_file_vm_ops;
++	return 0;
++}
++
++struct page * xip2_nopage_in_place(struct vm_area_struct * area, 
++				   unsigned long address, int unused)
++{
++	int error;
++	unsigned long blockno=ULONG_MAX;
++	void* block_ptr;
++	struct file *file = area->vm_file;
++	struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
++	struct inode *inode = mapping->host;
++	unsigned long pgoff;
++
++	pgoff = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + 
++		area->vm_pgoff;
++	error=xip2_get_block(inode, pgoff, &blockno, 0);
++	if (error) {
++		printk ("XIP2-FS: xip2_nopage_in_place could not fullfill "
++			"page request\n");
++		return NULL;
++	}
++	block_ptr = xip2_maread(inode->i_sb->u.xip2_sb.mem_area, blockno, 
++				PAGE_SIZE);
++	if (!block_ptr) 
++		return virt_to_page(empty_zero_page);
++	return virt_to_page(block_ptr);
++}
++
++void xip2_do_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc)
++{
++	struct address_space *mapping = filp->f_dentry->d_inode->i_mapping;
++	struct inode *inode = mapping->host;
++	unsigned long block,offset,rdlen,count, iblock, lblock, blockno;
++	void* block_ptr,* cpystart;
++	int error,cpycount;
++
++	iblock = (*ppos)/PAGE_SIZE;
++	offset = (*ppos)%PAGE_SIZE;
++	rdlen = desc->count;
++	if ((*ppos)+desc->count > inode->i_size)
++		rdlen = inode->i_size - (*ppos);
++	lblock = (*ppos + rdlen) / PAGE_SIZE;
++	count = 0;
++	for (block = iblock; block <= lblock; block++) {
++		error=xip2_get_block(inode, block, &blockno, 0);
++		if (error) {
++			desc->error = error;
++			desc->written = count;
++			return;
++}
++		block_ptr = xip2_maread (inode->i_sb->u.xip2_sb.mem_area, 
++					 blockno, PAGE_SIZE);
++		if (block_ptr) {
++			if (block == iblock) {
++				cpystart = block_ptr + offset;
++				cpycount = PAGE_SIZE - offset;
++	} else {
++				cpystart = block_ptr;
++				cpycount = PAGE_SIZE;
++			}
++		} else {
++			// there is no block assigned, copy zeros over
++			if (block == iblock) {
++				cpystart = empty_zero_page;
++				cpycount = PAGE_SIZE - offset;
++			} else {
++				cpystart = empty_zero_page;
++				cpycount = PAGE_SIZE;
++		}
++	}
++		if (cpycount > rdlen-count) {
++			cpycount = rdlen-count;
++			if (block!=lblock) BUG();
++}
++		if (copy_to_user(desc->buf+count, cpystart, cpycount)) {
++			desc->error = -EFAULT;
++			desc->written = count;
++			return;
++}
++		count += cpycount;
++	}
++	if (rdlen-count>0) BUG();
++	desc->error = 0;
++	desc->written = count;
++	*ppos+=count;
++	return;
++}
++
++
++ssize_t xip2_file_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
++{
++	ssize_t retval;
++
++	if ((ssize_t) count < 0)
++		return -EINVAL;
++
++	retval = -EFAULT;
++	if (access_ok(VERIFY_WRITE, buf, count)) {
++		retval = 0;
++
++		if (count) {
++			read_descriptor_t desc;
++
++			desc.written = 0;
++			desc.count = count;
++			desc.buf = buf;
++			desc.error = 0;
++			xip2_do_file_read(filp, ppos, &desc);
++
++			retval = desc.written;
++			if (!retval)
++				retval = desc.error;
++	}
++}
++	return retval;
++}
+diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/ialloc.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/xip2fs/ialloc.c	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,34 @@
++/*
++ *  linux/fs/xip2/ialloc.c, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
++ */
++
++#include <linux/config.h>
++#include <linux/fs.h>
++#include <linux/xip2_fs.h>
++#include <linux/locks.h>
++#include <linux/quotaops.h>
++
++
++/*
++ * ialloc.c contains the inodes allocation and deallocation routines
++ */
++
++/*
++ * The free inodes are managed by bitmaps.  A file system contains several
++ * blocks groups.  Each group contains 1 bitmap block for blocks, 1 bitmap
++ * block for inodes, N blocks for the inode table and data blocks.
++ *
++ * The file system contains group descriptors which are located after the
++ * super block.  Each descriptor contains the number of the bitmap block and
++ * the free blocks count in the block.  The descriptors are loaded in memory
++ * when a file system is mounted (see xip2_read_super).
++ */
++
++unsigned long xip2_count_free_inodes (struct super_block * sb)
++{
++	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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/xip2fs/inode.c	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,382 @@
++/*
++ *  linux/fs/xip2/inode.c, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
++ */
++
++#include <linux/fs.h>
++#include <linux/xip2_fs.h>
++#include <linux/iobuf.h>
++#include <linux/locks.h>
++#include <linux/smp_lock.h>
++#include <linux/sched.h>
++#include <linux/highuid.h>
++#include <linux/quotaops.h>
++#include <linux/module.h>
++
++#define MAX_BUF_PER_PAGE (PAGE_CACHE_SIZE / 512)
++
++MODULE_AUTHOR("Carsten Otte, Remy Card and others");
++MODULE_DESCRIPTION("XIP2 filesystem derived from ext2");
++MODULE_LICENSE("GPL");
++
++typedef struct {
++	u32	*p;
++	u32	key;
++	void *block_ptr;
++} Indirect;
++
++static inline void add_chain(Indirect *p, void *block_ptr, u32 *v)
++{
++	p->key = *(p->p = v);
++	p->block_ptr = block_ptr;
++}
++
++static inline int verify_chain(Indirect *from, Indirect *to)
++{
++	while (from <= to && from->key == *from->p)
++		from++;
++	return (from > to);
++}
++
++/**
++ *	xip2_block_to_path - parse the block number into array of offsets
++ *	@inode: inode in question (we are only interested in its superblock)
++ *	@i_block: block number to be parsed
++ *	@offsets: array to store the offsets in
++ *
++ *	To store the locations of file's data ext2 uses a data structure common
++ *	for UNIX filesystems - tree of pointers anchored in the inode, with
++ *	data blocks at leaves and indirect blocks in intermediate nodes.
++ *	This function translates the block number into path in that tree -
++ *	return value is the path length and @offsets[n] is the offset of
++ *	pointer to (n+1)th node in the nth one. If @block is out of range
++ *	(negative or too large) warning is printed and zero returned.
++ *
++ *	Note: function doesn't find node addresses, so no IO is needed. All
++ *	we need to know is the capacity of indirect blocks (taken from the
++ *	inode->i_sb).
++ */
++
++/*
++ * Portability note: the last comparison (check that we fit into triple
++ * indirect block) is spelled differently, because otherwise on an
++ * architecture with 32-bit longs and 8Kb pages we might get into trouble
++ * if our filesystem had 8Kb blocks. We might use long long, but that would
++ * kill us on x86. Oh, well, at least the sign propagation does not matter -
++ * i_block would have to be negative in the very beginning, so we would not
++ * get there at all.
++ */
++
++static int xip2_block_to_path(struct inode *inode, long i_block, int offsets[4])
++{
++	int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb);
++	int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
++	const long direct_blocks = EXT2_NDIR_BLOCKS,
++		indirect_blocks = ptrs,
++		double_blocks = (1 << (ptrs_bits * 2));
++	int n = 0;
++
++	if (i_block < 0) {
++		xip2_warning (inode->i_sb, "xip2_block_to_path", "block < 0");
++	} else if (i_block < direct_blocks) {
++		offsets[n++] = i_block;
++	} else if ( (i_block -= direct_blocks) < indirect_blocks) {
++		offsets[n++] = EXT2_IND_BLOCK;
++		offsets[n++] = i_block;
++	} else if ((i_block -= indirect_blocks) < double_blocks) {
++		offsets[n++] = EXT2_DIND_BLOCK;
++		offsets[n++] = i_block >> ptrs_bits;
++		offsets[n++] = i_block & (ptrs - 1);
++	} else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
++		offsets[n++] = EXT2_TIND_BLOCK;
++		offsets[n++] = i_block >> (ptrs_bits * 2);
++		offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
++		offsets[n++] = i_block & (ptrs - 1);
++	} else {
++		xip2_warning (inode->i_sb, "xip2_block_to_path", 
++			      "block > big");
++	}
++	return n;
++}
++
++/**
++ *	xip2_get_branch - read the chain of indirect blocks leading to data
++ *	@inode: inode in question
++ *	@depth: depth of the chain (1 - direct pointer, etc.)
++ *	@offsets: offsets of pointers in inode/indirect blocks
++ *	@chain: place to store the result
++ *	@err: here we store the error value
++ *
++ */
++static Indirect *xip2_get_branch(struct inode *inode,
++				 int depth,
++				 int *offsets,
++				 Indirect chain[4],
++				 int *err)
++{
++	int size = inode->i_sb->s_blocksize;
++	Indirect *p = chain;
++	void *block_ptr;
++
++	*err = 0;
++	/* i_data is not going away, no lock needed */
++	add_chain (chain, NULL, inode->u.ext2_i.i_data + *offsets);
++	if (!p->key)
++		goto no_block;
++	while (--depth) {
++		block_ptr = xip2_maread(inode->i_sb->u.xip2_sb.mem_area, 
++					le32_to_cpu(p->key), size);
++		if (!block_ptr)
++			goto failure;
++		/* Reader: pointers */
++		if (!verify_chain(chain, p))
++			goto changed;
++		add_chain(++p, block_ptr, (u32*)block_ptr + *++offsets);
++		/* Reader: end */
++		if (!p->key)
++			goto no_block;
++	}
++	return NULL;
++
++changed:
++	*err = -EAGAIN;
++	goto no_block;
++failure:
++	*err = -EIO;
++no_block:
++	return p;
++}
++
++int xip2_get_block(struct inode *inode, long iblock, unsigned long* blockno_result, 
++		   int create)
++{
++	int err = -EIO;
++	int offsets[4];
++	Indirect chain[4];
++	Indirect *partial;
++	int depth = xip2_block_to_path(inode, iblock, offsets);
++
++	if (depth == 0)
++		goto out;
++
++	partial = xip2_get_branch(inode, depth, offsets, chain, &err);
++
++	/* Simplest case - block found, no allocation needed */
++	if (!partial) {
++		*blockno_result = le32_to_cpu(chain[depth-1].key);
++		/* Clean up and exit */
++		partial = chain+depth-1; /* the whole chain */
++		goto cleanup;
++	}
++
++	/* Next simple case - plain lookup or failed read of indirect block */
++	if (!create || err) {
++cleanup:
++		while (partial > chain) {
++			partial--;
++		}
++out:
++		return err;
++	}
++	xip2_warning (inode->i_sb, "xip2_get_block", "allocation of a block "
++		      "would be needed");
++	return -EROFS;
++}
++
++static int xip2_readpage(struct file *file, struct page *page)
++{
++	struct inode *inode = page->mapping->host;
++	unsigned long iblock;
++	unsigned long blocksize, blocks, blockno;
++	void* block_ptr;
++	int err;
++
++	printk ("Whoot whooot! Readpage was called! Trace-me at %p\n",
++		xip2_readpage);
++
++	if (!PageLocked(page))
++		PAGE_BUG(page);
++	blocksize = 1 << inode->i_blkbits;
++	blocks = PAGE_CACHE_SIZE >> inode->i_blkbits;
++	iblock = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
++
++	if ((blocksize != PAGE_SIZE) || (blocks != 1))
++		BUG();
++	err=xip2_get_block(inode, iblock, &blockno, 0);
++	if (err)
++		return err;
++	block_ptr = xip2_maread(inode->i_sb->u.xip2_sb.mem_area, blockno, 
++				blocksize);
++	if (!block_ptr)
++		return -EIO;
++	memcpy (page_address(page),block_ptr,blocksize);
++	SetPageUptodate(page);
++	UnlockPage(page);
++	return 0;
++}
++
++struct address_space_operations xip2_aops = {
++	readpage: xip2_readpage,
++};
++
++/*
++ * Probably it should be a library function... search for first non-zero word
++ * or memcmp with zero_page, whatever is better for particular architecture.
++ * Linus?
++ */
++static inline int all_zeroes(u32 *p, u32 *q)
++{
++	while (p < q)
++		if (*p++)
++			return 0;
++	return 1;
++}
++
++void xip2_read_inode (struct inode * inode)
++{
++	void * block_ptr;
++	struct ext2_inode * raw_inode;
++	unsigned long block_group;
++	unsigned long group_desc;
++	unsigned long desc;
++	unsigned long block;
++	unsigned long offset;
++	struct ext2_group_desc * gdp;
++
++	if ((inode->i_ino != EXT2_ROOT_INO && 
++	     inode->i_ino != EXT2_ACL_IDX_INO &&
++	     inode->i_ino != EXT2_ACL_DATA_INO &&
++	     inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
++	    inode->i_ino > 
++	    le32_to_cpu(inode->i_sb->u.xip2_sb.s_es->s_inodes_count)) {
++		xip2_error (inode->i_sb, "xip2_read_inode",
++			    "bad inode number: %lu", inode->i_ino);
++		goto bad_inode;
++	}
++	block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
++	if (block_group >= inode->i_sb->u.xip2_sb.s_groups_count) {
++		xip2_error (inode->i_sb, "xip2_read_inode",
++			    "group >= groups count");
++		goto bad_inode;
++	}
++	group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);
++	desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);
++	block_ptr = inode->i_sb->u.xip2_sb.s_group_desc[group_desc];
++	if (!block_ptr) {
++		xip2_error (inode->i_sb, "xip2_read_inode",
++			    "Descriptor not loaded");
++		goto bad_inode;
++	}
++
++	gdp = (struct ext2_group_desc *) block_ptr;
++	/*
++	 * Figure out the offset within the block group inode table
++	 */
++	offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
++		EXT2_INODE_SIZE(inode->i_sb);
++	block = le32_to_cpu(gdp[desc].bg_inode_table) +
++		(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
++	if (!(block_ptr = xip2_maread (inode->i_sb->u.xip2_sb.mem_area, block, 
++				       inode->i_sb->s_blocksize))) {
++		xip2_error (inode->i_sb, "xip2_read_inode",
++			    "unable to read inode block - "
++			    "inode=%lu, block=%lu", inode->i_ino, block);
++		goto bad_inode;
++	}
++	offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1);
++	raw_inode = (struct ext2_inode *) (block_ptr + offset);
++
++	inode->i_mode = le16_to_cpu(raw_inode->i_mode);
++	inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
++	inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
++	if(!(test_opt (inode->i_sb, NO_UID32))) {
++		inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
++		inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
++	}
++	inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
++	inode->i_size = le32_to_cpu(raw_inode->i_size);
++	inode->i_atime = le32_to_cpu(raw_inode->i_atime);
++	inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
++	inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
++	inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
++	/* We now have enough fields to check if the inode was active or not.
++	 * This is needed because nfsd might try to access dead inodes
++	 * the test is that same one that e2fsck uses
++	 * NeilBrown 1999oct15
++	 */
++	if (inode->i_nlink == 0 && (inode->i_mode == 0 || 
++				    inode->u.ext2_i.i_dtime)) {
++		/* this inode is deleted */
++		goto bad_inode;
++	}
++	inode->i_blksize = PAGE_SIZE;
++	inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
++	inode->i_version = ++event;
++	inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags);
++	inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
++	inode->u.ext2_i.i_frag_no = raw_inode->i_frag;
++	inode->u.ext2_i.i_frag_size = raw_inode->i_fsize;
++	inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
++	if (S_ISREG(inode->i_mode))
++		inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) 
++			<< 32;
++	else
++		inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
++	inode->i_generation = le32_to_cpu(raw_inode->i_generation);
++	inode->u.ext2_i.i_prealloc_count = 0;
++	inode->u.ext2_i.i_block_group = block_group;
++
++	/*
++	 * NOTE! The in-memory inode i_data array is in little-endian order
++	 * even on big-endian machines: we do NOT byteswap the block numbers!
++	 */
++	for (block = 0; block < EXT2_N_BLOCKS; block++)
++		inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
++
++	if (inode->i_ino == EXT2_ACL_IDX_INO ||
++	    inode->i_ino == EXT2_ACL_DATA_INO)
++		/* Nothing to do */ ;
++	else if (S_ISREG(inode->i_mode)) {
++		inode->i_op = &xip2_file_inode_operations;
++		inode->i_fop = &xip2_file_operations;
++		inode->i_mapping->a_ops = &xip2_aops;
++	} else if (S_ISDIR(inode->i_mode)) {
++		inode->i_op = &xip2_dir_inode_operations;
++		inode->i_fop = &xip2_dir_operations;
++		inode->i_mapping->a_ops = &xip2_aops;
++	} else if (S_ISLNK(inode->i_mode)) {
++		if (!inode->i_blocks)
++			inode->i_op = &xip2_fast_symlink_inode_operations;
++		else {
++			inode->i_op = &xip2_symlink_inode_operations;
++			inode->i_mapping->a_ops = &xip2_aops;
++		}
++	} else 
++		init_special_inode(inode, inode->i_mode,
++				   le32_to_cpu(raw_inode->i_block[0]));
++	inode->i_attr_flags = 0;
++	if (inode->u.ext2_i.i_flags & EXT2_SYNC_FL) {
++		inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS;
++		inode->i_flags |= S_SYNC;
++	}
++	if (inode->u.ext2_i.i_flags & EXT2_APPEND_FL) {
++		inode->i_attr_flags |= ATTR_FLAG_APPEND;
++		inode->i_flags |= S_APPEND;
++	}
++	if (inode->u.ext2_i.i_flags & EXT2_IMMUTABLE_FL) {
++		inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE;
++		inode->i_flags |= S_IMMUTABLE;
++	}
++	if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL) {
++		inode->i_attr_flags |= ATTR_FLAG_NOATIME;
++		inode->i_flags |= S_NOATIME;
++	}
++	return;
++	
++bad_inode:
++	make_bad_inode(inode);
++	return;
++}
+diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/ioctl.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/xip2fs/ioctl.c	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,31 @@
++/*
++ *  linux/fs/xip2/ioctl.c, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
++ */
++
++#include <linux/fs.h>
++#include <linux/xip2_fs.h>
++#include <linux/sched.h>
++#include <asm/uaccess.h>
++
++
++int xip2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
++		unsigned long arg)
++{
++	unsigned int flags;
++
++	xip2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
++
++	switch (cmd) {
++	case EXT2_IOC_GETFLAGS:
++		flags = inode->u.ext2_i.i_flags & EXT2_FL_USER_VISIBLE;
++		return put_user(flags, (int *) arg);
++	case EXT2_IOC_GETVERSION:
++		return put_user(inode->i_generation, (int *) arg);
++	default:
++		return -ENOTTY;
++	}
++}
+diff -urN kernel-source-2.4.27-2.4.27.orig/fs/xip2fs/namei.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/xip2fs/namei.c	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,38 @@
++/*
++ *  linux/fs/xip2/namei.c, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
++ */
++
++#include <linux/fs.h>
++#include <linux/xip2_fs.h>
++#include <linux/pagemap.h>
++
++/*
++ * Methods themselves.
++ */
++
++static struct dentry *xip2_lookup(struct inode * dir, struct dentry *dentry)
++{
++	struct inode * inode;
++	ino_t ino;
++	
++	if (dentry->d_name.len > EXT2_NAME_LEN)
++		return ERR_PTR(-ENAMETOOLONG);
++
++	ino = xip2_inode_by_name(dir, dentry);
++	inode = NULL;
++	if (ino) {
++		inode = iget(dir->i_sb, ino);
++		if (!inode) 
++			return ERR_PTR(-EACCES);
++	}
++	d_add(dentry, inode);
++	return NULL;
++}
++
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/xip2fs/super.c	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,661 @@
++/*
++ *  linux/fs/xip2/super.c, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/fs.h>
++#include <linux/xip2_fs.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/locks.h>
++#include <linux/blkdev.h>
++#include <asm/dcss.h>
++#include <asm/uaccess.h>
++
++static char error_buf[1024];
++
++void xip2_error (struct super_block * sb, const char * function,
++		 const char * fmt, ...)
++{
++	va_list args;
++
++	va_start (args, fmt);
++	vsprintf (error_buf, fmt, args);
++	va_end (args);
++	if (test_opt (sb, ERRORS_PANIC) ||
++	    (le16_to_cpu(sb->u.xip2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC &&
++	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO)))
++		panic ("XIP2-fs panic (device %s): %s: %s\n",
++		       bdevname(sb->s_dev), function, error_buf);
++	printk (KERN_CRIT "XIP2-fs error (device %s): %s: %s\n",
++		bdevname(sb->s_dev), function, error_buf);
++	if (test_opt (sb, ERRORS_RO) ||
++	    (le16_to_cpu(sb->u.xip2_sb.s_es->s_errors) == EXT2_ERRORS_RO &&
++	     !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) {
++		printk ("Remounting filesystem read-only\n");
++		sb->s_flags |= MS_RDONLY;
++	}
++}
++
++NORET_TYPE void xip2_panic (struct super_block * sb, const char * function,
++			    const char * fmt, ...)
++{
++	va_list args;
++
++	if (!(sb->s_flags & MS_RDONLY)) {
++		sb->u.xip2_sb.s_mount_state |= EXT2_ERROR_FS;
++		sb->u.xip2_sb.s_es->s_state =
++			cpu_to_le16(le16_to_cpu(sb->u.xip2_sb.s_es->s_state) 
++				    | EXT2_ERROR_FS);
++		sb->s_dirt = 1;
++	}
++	va_start (args, fmt);
++	vsprintf (error_buf, fmt, args);
++	va_end (args);
++	sb->s_flags |= MS_RDONLY;
++	panic ("XIP2-fs panic (device %s): %s: %s\n",
++	       bdevname(sb->s_dev), function, error_buf);
++}
++
++void xip2_warning (struct super_block * sb, const char * function,
++		   const char * fmt, ...)
++{
++	va_list args;
++
++	va_start (args, fmt);
++	vsprintf (error_buf, fmt, args);
++	va_end (args);
++	printk (KERN_WARNING "XIP2-fs warning (device %s): %s: %s\n",
++		bdevname(sb->s_dev), function, error_buf);
++}
++
++void xip2_update_dynamic_rev(struct super_block *sb)
++{
++	struct ext2_super_block *es = XIP2_SB(sb)->s_es;
++
++	if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)
++		return;
++
++	xip2_warning(sb, __FUNCTION__,
++		     "updating to rev %d because of new feature flag, "
++		     "running e2fsck is recommended",
++		     EXT2_DYNAMIC_REV);
++
++	es->s_first_ino = cpu_to_le32(EXT2_GOOD_OLD_FIRST_INO);
++	es->s_inode_size = cpu_to_le16(EXT2_GOOD_OLD_INODE_SIZE);
++	es->s_rev_level = cpu_to_le32(EXT2_DYNAMIC_REV);
++	/* leave es->s_feature_*compat flags alone */
++	/* es->s_uuid will be set by e2fsck if empty */
++
++	/*
++	 * The rest of the superblock fields should be zero, and if not it
++	 * means they are likely already in use, so leave them alone.  We
++	 * can leave it up to e2fsck to clean up any inconsistencies there.
++	 */
++}
++
++void xip2_put_super (struct super_block * sb)
++{
++	if (sb->u.xip2_sb.mem_area) {
++		segment_unload (((xip2_mem_area_t*)(sb->u.xip2_sb.mem_area))
++				->name);
++		kfree (sb->u.xip2_sb.mem_area);
++	}
++	kfree(sb->u.xip2_sb.s_group_desc);
++	return;
++}
++
++static struct super_operations xip2_sops = {
++	read_inode:	xip2_read_inode,
++	put_super:	xip2_put_super,
++	statfs:		xip2_statfs,
++	remount_fs:	xip2_remount,
++};
++
++/*
++ * This function has been shamelessly adapted from the msdos fs
++ */
++static int parse_options (char * options, unsigned long * sb_block,
++			  unsigned short *resuid, unsigned short * resgid,
++			  unsigned long * mount_options,
++			  xip2_mem_area_t ** mem_area)
++{
++	char * this_char;
++	char * value;
++
++	if (!options)
++		return 1;
++	for (this_char = strtok (options, ",");
++	     this_char != NULL;
++	     this_char = strtok (NULL, ",")) {
++		if ((value = strchr (this_char, '=')) != NULL)
++			*value++ = 0;
++		if (!strcmp (this_char, "bsddf"))
++			clear_opt (*mount_options, MINIX_DF);
++		else if (!strcmp (this_char, "nouid32")) {
++			set_opt (*mount_options, NO_UID32);
++		}
++		else if (!strcmp (this_char, "check")) {
++			if (!value || !*value || !strcmp (value, "none"))
++				clear_opt (*mount_options, CHECK);
++			else
++#ifdef CONFIG_EXT2_CHECK
++				set_opt (*mount_options, CHECK);
++#else
++				printk("XIP2 Check option not supported\n");
++#endif
++		}
++		else if (!strcmp (this_char, "debug"))
++			set_opt (*mount_options, DEBUG);
++		else if (!strcmp (this_char, "errors")) {
++			if (!value || !*value) {
++				printk ("XIP2-fs: the errors option requires "
++					"an argument\n");
++				return 0;
++			}
++			if (!strcmp (value, "continue")) {
++				clear_opt (*mount_options, ERRORS_RO);
++				clear_opt (*mount_options, ERRORS_PANIC);
++				set_opt (*mount_options, ERRORS_CONT);
++			}
++			else if (!strcmp (value, "remount-ro")) {
++				clear_opt (*mount_options, ERRORS_CONT);
++				clear_opt (*mount_options, ERRORS_PANIC);
++				set_opt (*mount_options, ERRORS_RO);
++			}
++			else if (!strcmp (value, "panic")) {
++				clear_opt (*mount_options, ERRORS_CONT);
++				clear_opt (*mount_options, ERRORS_RO);
++				set_opt (*mount_options, ERRORS_PANIC);
++			}
++			else {
++				printk ("XIP2-fs: Invalid errors option: %s\n",
++					value);
++				return 0;
++			}
++		}
++		else if (!strcmp (this_char, "grpid") ||
++			 !strcmp (this_char, "bsdgroups"))
++			set_opt (*mount_options, GRPID);
++		else if (!strcmp (this_char, "minixdf"))
++			set_opt (*mount_options, MINIX_DF);
++		else if (!strcmp (this_char, "nocheck"))
++			clear_opt (*mount_options, CHECK);
++		else if (!strcmp (this_char, "nogrpid") ||
++			 !strcmp (this_char, "sysvgroups"))
++			clear_opt (*mount_options, GRPID);
++		else if (!strcmp (this_char, "resgid")) {
++			if (!value || !*value) {
++				printk ("XIP2-fs: the resgid option requires "
++					"an argument\n");
++				return 0;
++			}
++			*resgid = simple_strtoul (value, &value, 0);
++			if (*value) {
++				printk ("XIP2-fs: Invalid resgid option: %s\n",
++					value);
++				return 0;
++			}
++		}
++		else if (!strcmp (this_char, "resuid")) {
++			if (!value || !*value) {
++				printk ("XIP2-fs: the resuid option requires "
++					"an argument");
++				return 0;
++			}
++			*resuid = simple_strtoul (value, &value, 0);
++			if (*value) {
++				printk ("XIP2-fs: Invalid resuid option: %s\n",
++					value);
++				return 0;
++			}
++		}
++		else if (!strcmp (this_char, "sb")) {
++			if (!value || !*value) {
++				printk ("XIP2-fs: the sb option requires "
++					"an argument");
++				return 0;
++			}
++			*sb_block = simple_strtoul (value, &value, 0);
++			if (*value) {
++				printk ("XIP2-fs: Invalid sb option: %s\n",
++					value);
++				return 0;
++			}
++		}
++		else if (!strcmp (this_char, "memarea")) {
++			if (!value || !*value) {
++				printk ("XIP2-fs: the memarea option requires "
++					"an argument\n");
++				return 0;
++			}
++			*mem_area=kmalloc (sizeof(xip2_mem_area_t)+strlen(value)+1,GFP_ATOMIC);
++			if (mem_area==NULL) {
++				printk ("XIP2-fs: out of memory\n");
++				return 0;
++			}
++			(*mem_area)->name=((char*)(*mem_area)) +
++					sizeof(xip2_mem_area_t);
++			strcpy ((*mem_area)->name, value);
++			if (segment_load(value, SEGMENT_SHARED_RO, &(*mem_area)->start, 
++					 &(*mem_area)->end)<0) {
++				printk ("XIP2-fs: the memarea specified could "
++					"not be found\n");
++				kfree (*mem_area);
++				*mem_area = NULL;
++				return 0;
++			}
++		}
++		/* Silently ignore the quota options */
++		else if (!strcmp (this_char, "grpquota")
++		         || !strcmp (this_char, "noquota")
++		         || !strcmp (this_char, "quota")
++		         || !strcmp (this_char, "usrquota"))
++			/* Don't do anything ;-) */ ;
++		else {
++			printk ("XIP2-fs: Unrecognized mount option %s\n", 
++				this_char);
++			return 0;
++		}
++	}
++	return 1;
++}
++
++static int xip2_check_descriptors (struct super_block * sb)
++{
++	int i;
++	int desc_block = 0;
++	unsigned long block = le32_to_cpu(sb->u.xip2_sb.s_es->
++					  s_first_data_block);
++	struct ext2_group_desc * gdp = NULL;
++
++	xip2_debug ("Checking group descriptors");
++
++	for (i = 0; i < sb->u.xip2_sb.s_groups_count; i++)
++	{
++		if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
++			gdp = (struct ext2_group_desc *) 
++				sb->u.xip2_sb.s_group_desc[desc_block++];
++		if (le32_to_cpu(gdp->bg_block_bitmap) < block ||
++		    le32_to_cpu(gdp->bg_block_bitmap) >= 
++		    block + EXT2_BLOCKS_PER_GROUP(sb))
++		{
++			xip2_error (sb, "xip2_check_descriptors",
++				    "Block bitmap for group %d"
++				    " not in group (block %lu)!",
++				    i, (unsigned long) 
++				    le32_to_cpu(gdp->bg_block_bitmap));
++			return 0;
++		}
++		if (le32_to_cpu(gdp->bg_inode_bitmap) < block ||
++		    le32_to_cpu(gdp->bg_inode_bitmap) >= block + 
++		    EXT2_BLOCKS_PER_GROUP(sb))
++		{
++			xip2_error (sb, "xip2_check_descriptors",
++				    "Inode bitmap for group %d"
++				    " not in group (block %lu)!",
++				    i, (unsigned long) 
++				    le32_to_cpu(gdp->bg_inode_bitmap));
++			return 0;
++		}
++		if (le32_to_cpu(gdp->bg_inode_table) < block ||
++		    le32_to_cpu(gdp->bg_inode_table) + 
++		    sb->u.xip2_sb.s_itb_per_group >=
++		    block + EXT2_BLOCKS_PER_GROUP(sb))
++		{
++			xip2_error (sb, "xip2_check_descriptors",
++				    "Inode table for group %d"
++				    " not in group (block %lu)!",
++				    i, (unsigned long) 
++				    le32_to_cpu(gdp->bg_inode_table));
++			return 0;
++		}
++		block += EXT2_BLOCKS_PER_GROUP(sb);
++		gdp++;
++	}
++	return 1;
++}
++
++#define log2(n) ffz(~(n))
++ 
++/*
++ * Maximal file size.  There is a direct, and {,double-,triple-}indirect
++ * block limit, and also a limit of (2^32 - 1) 512-byte sectors in i_blocks.
++ * We need to be 1 filesystem block less than the 2^32 sector limit.
++ */
++static loff_t xip2_max_size(int bits)
++{
++	loff_t res = EXT2_NDIR_BLOCKS;
++	res += 1LL << (bits-2);
++	res += 1LL << (2*(bits-2));
++	res += 1LL << (3*(bits-2));
++	res <<= bits;
++	if (res > (512LL << 32) - (1 << bits))
++		res = (512LL << 32) - (1 << bits);
++	return res;
++}
++
++struct super_block * xip2_read_super (struct super_block * sb, void * data,
++				      int silent)
++{
++	xip2_mem_area_t* mem_area = NULL;
++	void * block_ptr;
++	struct ext2_super_block * es;
++	unsigned long sb_block = 0;
++	unsigned short resuid = EXT2_DEF_RESUID;
++	unsigned short resgid = EXT2_DEF_RESGID;
++	kdev_t dev = sb->s_dev;
++	int blocksize = BLOCK_SIZE;
++	int db_count;
++	int i;
++
++
++	blocksize = PAGE_SIZE;
++	sb->u.xip2_sb.s_mount_opt = 0;
++	if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
++	    &sb->u.xip2_sb.s_mount_opt,&mem_area)) {
++		return NULL;
++	}
++
++	if (!mem_area) {
++		printk ("XIP2-fs: no memarea specified\n");
++		return NULL;
++	}
++
++	if (!(block_ptr = xip2_maread (mem_area, sb_block, blocksize))) {
++		printk ("XIP2-fs: unable to read superblock\n");
++		kfree (mem_area);
++		return NULL;
++	}
++	/*
++	 * Note: s_es must be initialized as soon as possible because
++	 *       some ext2 macro-instructions depend on its value
++	 */
++	es = (struct ext2_super_block *) (block_ptr + BLOCK_SIZE);
++	sb->u.xip2_sb.s_es = es;
++	sb->u.xip2_sb.mem_area = mem_area;
++	sb->s_magic = le16_to_cpu(es->s_magic);
++	if (sb->s_magic != EXT2_SUPER_MAGIC) {
++		if (!silent)
++			printk ("VFS: Can't find xip2 filesystem on "
++				"memarea %s.\n", mem_area->name);
++		goto failed_mount;
++	}
++	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &&
++	    (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) ||
++	     EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
++	     EXT2_HAS_INCOMPAT_FEATURE(sb, ~0U)))
++		printk("XIP2-fs warning: feature flags set on rev 0 fs, "
++		       "running e2fsck is recommended\n");
++	/*
++	 * Check if fs should be mounted rdonly. If not let the mount fail.
++	 */
++	if (!(sb->s_flags & MS_RDONLY)) {
++		printk("XIP2-fs: %s: mounting RDWR was requested, "
++		       "but is not supported. Please mount using"
++		       "the RO option\n", mem_area->name);
++		goto failed_mount;
++	}
++	
++	/*
++	 * Check feature flags regardless of the revision level, since we
++	 * previously didn't change the revision level when setting the flags,
++	 * so there is a chance incompat flags are set on a rev 0 filesystem.
++	 */
++	if ((i = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP))) {
++		printk("XIP2-fs: %s: couldn't mount because of "
++		       "unsupported optional features (%x).\n",
++		       mem_area->name, i);
++		goto failed_mount;
++	}
++	sb->s_blocksize_bits =
++		le32_to_cpu(XIP2_SB(sb)->s_es->s_log_block_size) + 10;
++	sb->s_blocksize = 1 << sb->s_blocksize_bits;
++
++	sb->s_maxbytes = xip2_max_size(sb->s_blocksize_bits);
++
++	/* If the blocksize doesn't match, tell the user.. */
++	if (sb->s_blocksize != blocksize) {
++		printk("XIP2-fs: Blocksize of the filesystem "
++		       "on %s does not match PAGE_SIZE.\n",
++		       mem_area->name);
++			goto failed_mount;
++		}
++
++	if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
++		sb->u.xip2_sb.s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
++		sb->u.xip2_sb.s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
++	} else {
++		sb->u.xip2_sb.s_inode_size = le16_to_cpu(es->s_inode_size);
++		sb->u.xip2_sb.s_first_ino = le32_to_cpu(es->s_first_ino);
++		if (sb->u.xip2_sb.s_inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
++			printk ("XIP2-fs: unsupported inode size: %d\n",
++				sb->u.xip2_sb.s_inode_size);
++			goto failed_mount;
++		}
++	}
++	sb->u.xip2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
++				   le32_to_cpu(es->s_log_frag_size);
++	if (sb->u.xip2_sb.s_frag_size)
++		sb->u.xip2_sb.s_frags_per_block = sb->s_blocksize /
++						  sb->u.xip2_sb.s_frag_size;
++	else
++		sb->s_magic = 0;
++	sb->u.xip2_sb.s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
++	sb->u.xip2_sb.s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
++	sb->u.xip2_sb.s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
++	sb->u.xip2_sb.s_inodes_per_block = sb->s_blocksize /
++					   EXT2_INODE_SIZE(sb);
++	sb->u.xip2_sb.s_itb_per_group = sb->u.xip2_sb.s_inodes_per_group /
++				        sb->u.xip2_sb.s_inodes_per_block;
++	sb->u.xip2_sb.s_desc_per_block = sb->s_blocksize /
++					 sizeof (struct ext2_group_desc);
++	sb->u.xip2_sb.s_sbp = block_ptr;
++	if (resuid != EXT2_DEF_RESUID)
++		sb->u.xip2_sb.s_resuid = resuid;
++	else
++		sb->u.xip2_sb.s_resuid = le16_to_cpu(es->s_def_resuid);
++	if (resgid != EXT2_DEF_RESGID)
++		sb->u.xip2_sb.s_resgid = resgid;
++	else
++		sb->u.xip2_sb.s_resgid = le16_to_cpu(es->s_def_resgid);
++	sb->u.xip2_sb.s_mount_state = le16_to_cpu(es->s_state);
++	sb->u.xip2_sb.s_addr_per_block_bits =
++		log2 (EXT2_ADDR_PER_BLOCK(sb));
++	sb->u.xip2_sb.s_desc_per_block_bits =
++		log2 (EXT2_DESC_PER_BLOCK(sb));
++	if (sb->s_magic != EXT2_SUPER_MAGIC) {
++		if (!silent)
++			printk ("VFS: Can't find an xip2 filesystem on dev "
++				"%s.\n",
++				bdevname(dev));
++		goto failed_mount;
++	}
++
++	if (sb->s_blocksize != sb->u.xip2_sb.s_frag_size) {
++		printk ("XIP2-fs: fragsize %lu != blocksize %lu "
++			"(not supported yet)\n",
++			sb->u.xip2_sb.s_frag_size, sb->s_blocksize);
++		goto failed_mount;
++	}
++
++	if (sb->u.xip2_sb.s_blocks_per_group > sb->s_blocksize * 8) {
++		printk ("XIP2-fs: #blocks per group too big: %lu\n",
++			sb->u.xip2_sb.s_blocks_per_group);
++		goto failed_mount;
++	}
++	if (sb->u.xip2_sb.s_frags_per_group > sb->s_blocksize * 8) {
++		printk ("XIP2-fs: #fragments per group too big: %lu\n",
++			sb->u.xip2_sb.s_frags_per_group);
++		goto failed_mount;
++	}
++	if (sb->u.xip2_sb.s_inodes_per_group > sb->s_blocksize * 8) {
++		printk ("XIP2-fs: #inodes per group too big: %lu\n",
++			sb->u.xip2_sb.s_inodes_per_group);
++		goto failed_mount;
++	}
++
++	sb->u.xip2_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) -
++				        le32_to_cpu(es->s_first_data_block) +
++				       EXT2_BLOCKS_PER_GROUP(sb) - 1) /
++				       EXT2_BLOCKS_PER_GROUP(sb);
++	db_count = (sb->u.xip2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1)
++		/ EXT2_DESC_PER_BLOCK(sb);
++	sb->u.xip2_sb.s_group_desc = kmalloc (db_count * sizeof (void *), 
++					      GFP_KERNEL);
++	if (sb->u.xip2_sb.s_group_desc == NULL) {
++		printk ("XIP2-fs: not enough memory\n");
++		goto failed_mount;
++	}
++	for (i = 0; i < db_count; i++) {
++		sb->u.xip2_sb.s_group_desc[i] = xip2_maread 
++			(mem_area, sb_block + i + 1, sb->s_blocksize);
++		if (!sb->u.xip2_sb.s_group_desc[i]) {
++			kfree(sb->u.xip2_sb.s_group_desc);
++			printk ("XIP2-fs: unable to read group descriptors\n");
++			goto failed_mount;
++		}
++	}
++	if (!xip2_check_descriptors (sb)) {
++		printk ("XIP2-fs: group descriptors corrupted!\n");
++		db_count = i;
++		goto failed_mount2;
++	}
++	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
++		sb->u.xip2_sb.s_inode_bitmap_number[i] = 0;
++		sb->u.xip2_sb.s_inode_bitmap[i] = NULL;
++		sb->u.xip2_sb.s_block_bitmap_number[i] = 0;
++		sb->u.xip2_sb.s_block_bitmap[i] = NULL;
++	}
++	sb->u.xip2_sb.s_loaded_inode_bitmaps = 0;
++	sb->u.xip2_sb.s_loaded_block_bitmaps = 0;
++	sb->u.xip2_sb.s_gdb_count = db_count;
++	/*
++	 * set up enough so that it can read an inode
++	 */
++	sb->s_op = &xip2_sops;
++	sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO));
++	if (!sb->s_root || !S_ISDIR(sb->s_root->d_inode->i_mode) ||
++	    !sb->s_root->d_inode->i_blocks || !sb->s_root->d_inode->i_size) {
++		if (sb->s_root) {
++			dput(sb->s_root);
++			sb->s_root = NULL;
++			printk(KERN_ERR "XIP2-fs: corrupt root inode, run "
++			       "e2fsck\n");
++		} else
++			printk(KERN_ERR "XIP2-fs: get root inode failed\n");
++		goto failed_mount2;
++	}
++	return sb;
++failed_mount2:
++	kfree(sb->u.xip2_sb.s_group_desc);
++failed_mount:
++	kfree(mem_area);
++	return NULL;
++}
++
++int xip2_remount (struct super_block * sb, int * flags, char * data)
++{
++	struct ext2_super_block * es;
++	xip2_mem_area_t* mem_area;
++	unsigned short resuid = sb->u.xip2_sb.s_resuid;
++	unsigned short resgid = sb->u.xip2_sb.s_resgid;
++	unsigned long new_mount_opt;
++	unsigned long tmp;
++
++	/* filesystem is read-only _only_, therefore mounting rdwr is not permitted */
++	if (!(*flags & MS_RDONLY)) 
++		return -EINVAL;
++
++	/*
++	 * Allow the "check" option to be passed as a remount option.
++	 */
++	new_mount_opt = sb->u.xip2_sb.s_mount_opt;
++	mem_area = sb->u.xip2_sb.mem_area;
++	if (!parse_options (data, &tmp, &resuid, &resgid,
++			    &new_mount_opt, &mem_area))
++		return -EINVAL;
++
++	if ((!mem_area) || (mem_area != sb->u.xip2_sb.mem_area))
++		return -EINVAL;
++
++	sb->u.xip2_sb.s_mount_opt = new_mount_opt;
++	sb->u.xip2_sb.s_resuid = resuid;
++	sb->u.xip2_sb.s_resgid = resgid;
++	es = sb->u.xip2_sb.s_es;
++	return 0;
++}
++
++int xip2_statfs (struct super_block * sb, struct statfs * buf)
++{
++	unsigned long overhead;
++	int i;
++
++	if (test_opt (sb, MINIX_DF))
++		overhead = 0;
++	else {
++		/*
++		 * Compute the overhead (FS structures)
++		 */
++
++		/*
++		 * All of the blocks before first_data_block are
++		 * overhead
++		 */
++		overhead = le32_to_cpu(sb->u.xip2_sb.s_es->s_first_data_block);
++
++		/*
++		 * Add the overhead attributed to the superblock and
++		 * block group descriptors.  If the sparse superblocks
++		 * feature is turned on, then not all groups have this.
++		 */
++		for (i = 0; i < XIP2_SB(sb)->s_groups_count; i++)
++			overhead += xip2_bg_has_super(sb, i) +
++				xip2_bg_num_gdb(sb, i);
++
++		/*
++		 * Every block group has an inode bitmap, a block
++		 * bitmap, and an inode table.
++		 */
++		overhead += (sb->u.xip2_sb.s_groups_count *
++			     (2 + sb->u.xip2_sb.s_itb_per_group));
++	}
++
++	buf->f_type = EXT2_SUPER_MAGIC;
++	buf->f_bsize = sb->s_blocksize;
++	buf->f_blocks = le32_to_cpu(sb->u.xip2_sb.s_es->s_blocks_count) 
++		- overhead;
++	buf->f_bfree = xip2_count_free_blocks (sb);
++	buf->f_bavail = buf->f_bfree - 
++		le32_to_cpu(sb->u.xip2_sb.s_es->s_r_blocks_count);
++	if (buf->f_bfree < le32_to_cpu(sb->u.xip2_sb.s_es->s_r_blocks_count))
++		buf->f_bavail = 0;
++	buf->f_files = le32_to_cpu(sb->u.xip2_sb.s_es->s_inodes_count);
++	buf->f_ffree = xip2_count_free_inodes (sb);
++	buf->f_namelen = EXT2_NAME_LEN;
++	return 0;
++}
++
++static DECLARE_FSTYPE (xip2_fs_type, "xip2", xip2_read_super, 
++		       FS_NO_DCACHE|FS_NO_PRELIM);
++
++static int __init init_xip2_fs(void)
++{
++        return register_filesystem(&xip2_fs_type);
++}
++
++static void __exit exit_xip2_fs(void)
++{
++	unregister_filesystem(&xip2_fs_type);
++}
++
++EXPORT_NO_SYMBOLS;
++
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/fs/xip2fs/symlink.c	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,66 @@
++/*
++ *  linux/fs/xip2/symlink.c, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
++ */
++
++#include <linux/fs.h>
++#include <linux/xip2_fs.h>
++#include <linux/mm.h>
++
++static char *xip2_getlink(struct dentry * dentry)
++{
++	char *res;
++	unsigned long blockno;
++
++	if (xip2_get_block(dentry->d_inode, 0, &blockno, 0)) {
++		printk ("XIP2-FS: could not resolve symbolic link\n");
++		return NULL;
++	}
++	res = (char*) xip2_maread (dentry->d_inode->i_sb->u.xip2_sb.mem_area,
++			blockno, PAGE_SIZE);
++        if (!res)
++               return (char*)empty_zero_page;
++        return (char*)res;
++}
++
++
++static int xip2_readlink(struct dentry *dentry, char *buffer, int buflen)
++{
++        char *s = xip2_getlink(dentry);
++        int res = vfs_readlink(dentry,buffer,buflen,s);
++
++        return res;
++}
++
++static int xip2_follow_link(struct dentry *dentry, struct nameidata *nd)
++{
++        char *s = xip2_getlink(dentry);
++        int res = vfs_follow_link(nd, s);
++
++        return res;
++}
++
++static int xip2_fast_readlink(struct dentry *dentry, char *buffer, int buflen)
++{
++	char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
++	return vfs_readlink(dentry, buffer, buflen, s);
++}
++
++static int xip2_fast_follow_link(struct dentry *dentry, struct nameidata *nd)
++{
++	char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
++	return vfs_follow_link(nd, s);
++}
++
++struct inode_operations xip2_fast_symlink_inode_operations = {
++	readlink:	xip2_fast_readlink,
++	follow_link:	xip2_fast_follow_link,
++};
++
++struct inode_operations xip2_symlink_inode_operations = {
++	readlink:	xip2_readlink,
++	follow_link:	xip2_follow_link,
++};
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-alpha/pgalloc.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-alpha/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+@@ -347,4 +347,6 @@
+ 
+ extern int do_check_pgt_cache(int, int);
+ 
++#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-arm/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+@@ -138,4 +138,6 @@
+ 
+ extern int do_check_pgt_cache(int, int);
+ 
++#include <asm-generic/pgalloc.h>
++
+ #endif
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-generic/pgalloc.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-generic/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+@@ -0,0 +1,37 @@
++#ifndef _ASM_GENERIC_PGALLOC_H
++#define _ASM_GENERIC_PGALLOC_H
++
++static inline int ptep_test_and_clear_and_flush_young(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
++{
++	if (!ptep_test_and_clear_young(ptep))
++		return 0;
++	flush_tlb_page(vma, address);
++	return 1;
++}
++
++static inline int ptep_test_and_clear_and_flush_dirty(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
++{
++	if (!ptep_test_and_clear_dirty(ptep))
++		return 0;
++	flush_tlb_page(vma, address);
++	return 1;
++}
++
++static inline void ptep_establish(struct vm_area_struct *vma, unsigned long address, pte_t *ptep, pte_t entry)
++{
++	set_pte(ptep, entry);
++	flush_tlb_page(vma, address);
++	update_mmu_cache(vma, address, entry);
++}
++
++static inline pte_t ptep_invalidate(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
++{
++	pte_t pte;
++
++	flush_cache_page(vma, address);
++        pte = ptep_get_and_clear(ptep);
++	flush_tlb_page(vma, address);
++	return pte;
++}
++
++#endif /* _ASM_GENERIC_PGALLOC_H */
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-i386/pgalloc.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-i386/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+@@ -235,4 +235,6 @@
+ 	flush_tlb_mm(mm);
+ }
  
- #include <asm/qdio.h>
++#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-ia64/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+@@ -297,4 +297,6 @@
+ 	set_bit(PG_arch_1, &page->flags);	/* mark page as clean */
+ }
  
--#define VERSION_QDIO_C "$Revision: 1.145 $"
-+#define VERSION_QDIO_C "$Revision: 1.145.4.11 $"
++#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-m68k/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+@@ -163,4 +163,6 @@
+ #include <asm/motorola_pgalloc.h>
+ #endif
  
- /****************** MODULE PARAMETER VARIABLES ********************/
- MODULE_AUTHOR("Utz Bacher <utz.bacher at de.ibm.com>");
-@@ -97,10 +97,12 @@
- #endif /* QDIO_PERFORMANCE_STATS */
++#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-mips/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+@@ -196,4 +196,6 @@
  
- static int hydra_thinints=0;
-+static int omit_svs=0;
+ extern int do_check_pgt_cache(int, int);
  
- static int indicator_used[INDICATORS_PER_CACHELINE];
- static __u32 * volatile indicators;
- static __u32 volatile spare_indicator;
-+static atomic_t spare_indicator_usecount;
++#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-mips64/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+@@ -200,4 +200,6 @@
  
- static debug_info_t *qdio_dbf_setup=NULL;
- static debug_info_t *qdio_dbf_sbal=NULL;
-@@ -121,6 +123,7 @@
- static struct semaphore init_sema;
+ extern int do_check_pgt_cache(int, int);
  
- static qdio_chsc_area_t *chsc_area;
-+static spinlock_t chsc_area_lock=SPIN_LOCK_UNLOCKED;
- /* iQDIO stuff: */
- static volatile qdio_q_t *tiq_list=NULL; /* volatile as it could change
- 					   during a while loop */
-@@ -198,7 +201,7 @@
- }
- static inline unsigned long qdio_get_millis(void)
- {
--	return (unsigned long)(qdio_get_micros()>>12);
-+	return (unsigned long)(qdio_get_micros()>>10);
- }
++#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-parisc/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+@@ -306,4 +306,6 @@
  
- static __inline__ int atomic_return_add (int i, atomic_t *v)
-@@ -518,8 +521,10 @@
+ extern int do_check_pgt_cache(int, int);
+ 
++#include <asm-generic/pgalloc.h>
++
+ #endif
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-ppc/pgalloc.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-ppc/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+@@ -150,5 +150,7 @@
+ 
+ extern int do_check_pgt_cache(int, int);
+ 
++#include <asm-generic/pgalloc.h>
++
+ #endif /* _PPC_PGALLOC_H */
+ #endif /* __KERNEL__ */
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/ccwcache.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/ccwcache.h	2006-01-30 22:25:23.000000000 -0700
+@@ -4,7 +4,7 @@
+  * Bugreports.to..: <Linux390 at de.ibm.com>
+  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
+  *
+- * $Revision: 1.9 $
++ * $Revision: 1.9.4.2 $
+  *
+  */
+ #ifndef CCWCACHE_H
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/chandev.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/chandev.h	2006-01-30 22:25:23.000000000 -0700
+@@ -28,6 +28,7 @@
+ 	chandev_type_osad=0x8,
+ 	chandev_type_qeth=0x10,
+ 	chandev_type_claw=0x20,
++       chandev_type_ctcmpc=0x40,      
+ } chandev_type;
+ 
+ typedef enum
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/cmb.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/cmb.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,194 @@
++/*
++ * include/asm-s390/cmb.h
++ * include/asm-s390x/cmb.h
++ *
++ *         Copyright (C) 2003 IBM Corporation
++ *         Author: Arnd Bergmann <arndb at de.ibm.com>
++ */
++
++#ifndef S390_CMB_H
++#define S390_CMB_H
++/**
++ * struct cmbdata -- channel measurement block data for user space
++ *
++ * @size:			size of the stored data
++ * @elapsed_time:		time since last reset
++ * @ssch_rsch_count:		number I/O attempts, may overflow at
++ * 				2^16 or 2^32, depending on machine
++ * @sample_count:		number of sampled I/O attempts, should
++ * 				be identical to @ssch_rsch_count
++ * @device_connect_time:	time that device communicated with
++ * 				channel
++ * @function_pending_time:	time between command initiate and
++ * 				start of execution
++ * @device_disconnect_time:	time of device-inactive while
++ * 				subchannel-active
++ * @control_unit_queuing_time:	time that a device is blocked by
++ * 				I/O from another system
++ * @device_active_only_time:	time of device-active while
++ * 				subchannel-inactive
++ * @device_busy_time:		time where the device is busy
++ * 				when attemting to start I/O
++ * @initial_command_response_time: time between sending and 
++ * 				the device accepting a command
++ *
++ * all values are stored as 64 bit for simplicity, especially
++ * in 32 bit emulation mode. All time values are normalized to
++ * nanoseconds.
++ * Currently, two formats are known, which differ by the size of
++ * this structure, i.e. the last two members are only set when
++ * the extended channel measurement facility (first shipped in
++ * z990 machines) is activated.
++ * Potentially, more fields could be added, which results in a
++ * new ioctl number.
++ **/
++struct cmbdata {
++	__u64 size;
++	__u64 elapsed_time;
++ /* basic and exended format: */
++	__u64 ssch_rsch_count;
++	__u64 sample_count;
++	__u64 device_connect_time;
++	__u64 function_pending_time;
++	__u64 device_disconnect_time;
++	__u64 control_unit_queuing_time;
++	__u64 device_active_only_time;
++ /* extended format only: */
++	__u64 device_busy_time;
++	__u64 initial_command_response_time;
++};
++
++/* enable channel measurement */
++#define BIODASDCMFENABLE	_IO(DASD_IOCTL_LETTER,32)
++/* enable channel measurement */
++#define BIODASDCMFDISABLE	_IO(DASD_IOCTL_LETTER,33)
++/* read channel measurement data */
++#define BIODASDREADALLCMB	_IOWR(DASD_IOCTL_LETTER,33,struct cmbdata)
++
++#ifdef __KERNEL__
++#include <linux/config.h>
++#include <asm/irq.h>
++
++#if defined(CONFIG_S390_CMF) || defined(CONFIG_S390_CMF_MODULE)
++/**
++ * struct cmf_device - basic building block of the CMF code
++ *
++ * @cmb_list:	entry in the global list of cmf_devices
++ * @ccwlock:	a pointer to the subchannel's spinlock
++ * @cmb_start_time: clock value of previous cmf_reset operation
++ * @cmb:	pointer to the hardware channel measurement block
++ * @callback:	function pointer called by cmf_device_callback()
++ * @irq:	subchannel number of the associated device
++ */
++struct cmf_device {
++	struct list_head cmb_list;
++	spinlock_t *ccwlock;
++	u64 cmb_start_time;
++	void *cmb;
++	void (*callback)(struct cmf_device *);
++	u16  irq;
++};
++
++/**
++ * cmf_device_callback - call the callback function for set_cmf
++ *
++ * cmf_device_callback() can be integrated in an appropriate
++ * point in the device driver where no I/O is ongoing on the
++ * subchannel and it is safe to call set_cmf.
++ * This is a nop when CONFIG_S390_CMF is disabled
++ */
++static inline void cmf_device_callback(struct cmf_device *cdev)
++{
++	if (cdev->callback) {
++		cdev->callback(cdev);
++	}
++}
++
++/**
++ * cmf_device_init - initialize the cmf_device structure
++ *
++ * cmf_device_init() needs to be called before doing any other operations
++ * on a cmf_device.
++ */
++static inline void cmf_device_init(struct cmf_device *cdev, int devno)
++{
++	INIT_LIST_HEAD (&cdev->cmb_list);
++	cdev->irq = get_irq_by_devno(devno);
++	cdev->ccwlock = get_irq_lock(cdev->irq);
++}
++
++#else /* !CONFIG_S390_CMF */
++struct cmf_device { };
++static inline void cmf_device_callback(struct cmf_device *cdev)        { }
++static inline void cmf_device_init(struct cmf_device *cdev, int devno) { }
++#endif
++
++/**
++ * enable_cmf() - switch on the channel measurement for a specific device
++ *  @cdev:	The ccw device to be enabled
++ *  returns 0 for success or a negative error value.
++ *
++ *  This function only allocates memory for the measurement block, the
++ *  actual activation is done with set_cmf()
++ *
++ *  Context:
++ *    may sleep, device is disabled
++ **/
++extern int enable_cmf(struct cmf_device *cdev);
++
++/**
++ * disable_cmf() - switch off the channel measurement for a specific device
++ *  @cdev:	The ccw device to be disabled
++ *  returns 0 for success or a negative error value.
++ *
++ *  This function only frees the memory allocated with enable_cmf. If
++ *  measurement has been activated with set_cmf(), it also needs to be 
++ *  deactivated with that function before calling disable_cmf(). 
++ *
++ *  Context:
++ *    may sleep, device is enabled and inactive
++ **/
++extern void disable_cmf(struct cmf_device *cdev);
++
++/**
++ * cmf_readall() - read one value from the current channel measurement block
++ * @cmf:	the device to be read
++ * @data:	a pointer to a data block that will be filled
++ *
++ *  Context:
++ *    device is active
++ **/
++extern int cmf_readall(struct cmf_device *cdev, struct cmbdata*data);
++
++/**
++ * set_cmf() - Set the measurement mode for a specific device
++ * @cmf:	the device to be modified
++ * @mme:	measurement mode enable value, can be either
++ *		0 for deactivation or 2 for activation
++ *
++ * It is important to call this function only when there is no I/O
++ * activity on the subchannel. Therefore it may be necessary to call
++ * it from an interrupt handler at the point where the previous
++ * request is finished.
++ * In 2.6.x, set_cmf() is integrated within enable_cmf() and disable_cmf(),
++ * which makes a lot of sense and life much easier for users.
++ *
++ *  Context:
++ *    device lock held, device is enabled, subchannel is idle
++ **/
++extern int set_cmf(struct cmf_device *cdev, u32 mme);
++
++/**
++ * reset_cmf() - clear a channel measurement block
++ * @cmf:	the device to be cleared
++ *
++ * This function is used to set all values in a channel measurement block
++ * to sane values. It should be called between enable_cmf() and set_cmf(cdev,2)
++ * but can be called as well when the device is already active.
++ *
++ *  Context:
++ *    device is enabled
++ **/
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/dasd.h	2006-01-30 22:25:23.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
+  *
+- * $Revision: 1.53 $
++ * $Revision: 1.52.6.3 $
+  *
+  * 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/dcss.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,19 @@
++/*
++ *  include/asm-s390/dcss.h
++ *
++ *  definitions for discontiguous saved segment support
++ *  Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ */
++
++#ifndef _ASM_S390_DCSS_H
++#define _ASM_S390_DCSS_H
++#ifndef __ASSEMBLY__
++#define SEGMENT_SHARED_RW       0
++#define SEGMENT_SHARED_RO       1
++#define SEGMENT_EXCLUSIVE_RW    2
++#define SEGMENT_EXCLUSIVE_RO    3
++extern int segment_load (char *name,int segtype,unsigned long *addr,unsigned long *length);
++extern void segment_unload(char *name);
++extern void segment_replace(char *name);
++#endif
++#endif
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/ioctl32.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/ioctl32.h	2006-01-30 22:25:23.000000000 -0700
+@@ -8,7 +8,7 @@
+ #ifndef ASM_IOCTL32_H
+ #define ASM_IOCTL32_H
+ 
+-extern int sys_ioctl(unsigned int, unsigned int, unsigned long, struct file*);
++extern int sys_ioctl(unsigned int, unsigned int, unsigned long);
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/irq.h	2006-01-30 22:25:23.000000000 -0700
+@@ -42,7 +42,9 @@
+       __u8  chpid[8];     /* CHPID 0-7 (if available) */
+       __u32 unused1 : 8;  /* reserved zeros */
+       __u32 st      : 3;  /* subchannel type */
+-      __u32 unused2 : 20; /* reserved zeros */
++      __u32 unused2 : 18; /* reserved zeros */
++      __u32 mbfc    : 1;  /* measurement block format control */
++      __u32 xmwme   : 1;  /* extended measurement word mode enable */
+       __u32 csense  : 1;  /* concurrent sense; can be enabled ...*/
+                           /*  ... per MSCH, however, if facility */
+                           /*  ... is not installed, this results */
+@@ -156,7 +158,8 @@
+ typedef struct {
+       pmcw_t pmcw;             /* path management control word */
+       scsw_t scsw;             /* subchannel status word */
+-      __u8 mda[12];            /* model dependent area */
++      __u64 mba;               /* measurement block address */
++      __u8 mda[4];             /* model dependent area */
+    } __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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/kmap_types.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,16 @@
++#ifndef _ASM_KMAP_TYPES_H
++#define _ASM_KMAP_TYPES_H
++
++enum km_type {
++	KM_BOUNCE_READ,
++	KM_SKB_SUNRPC_DATA,
++	KM_SKB_DATA_SOFTIRQ,
++	KM_USER0,
++	KM_USER1,
++	KM_BH_IRQ,
++	KM_SOFTIRQ0,
++	KM_SOFTIRQ1,
++	KM_TYPE_NR
++};
++
++#endif
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/lowcore.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/lowcore.h	2006-01-30 22:25:23.000000000 -0700
+@@ -47,6 +47,7 @@
+ #define __LC_IPLDEV                     0xC7C
+ 
+ #define __LC_JIFFY_TIMER		0xC80
++#define __LC_INT_CLOCK			0xC88
+ 
+ #define __LC_PANIC_MAGIC                0xE00
+ 
+@@ -165,8 +166,9 @@
+ 
+         /* SMP info area: defined by DJB */
+         __u64        jiffy_timer;              /* 0xc80 */
+-	atomic_t     ext_call_fast;            /* 0xc88 */
+-        __u8         pad11[0xe00-0xc8c];       /* 0xc8c */
++        __u64        int_clock;                /* 0xc88 */
++	atomic_t     ext_call_fast;            /* 0xc90 */
++        __u8         pad11[0xe00-0xc94];       /* 0xc94 */
+ 
+         /* 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/pgtable.h	2006-01-30 22:25:23.000000000 -0700
+@@ -426,9 +426,12 @@
+ 	__pte;                                                            \
+ })
+ 
+-#define arch_set_page_uptodate(__page)					  \
++#define SetPageUptodate(_page) \
+ 	do {								  \
+-		asm volatile ("sske %0,%1" : : "d" (get_storage_key()),	  \
++		struct page *__page = (_page);				  \
++		if (!test_and_set_bit(PG_uptodate, &__page->flags))	  \
++			asm volatile ("sske %0,%1" 			  \
++			      : : "d" (get_storage_key()),		  \
+ 			      "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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/processor.h	2006-01-30 22:25:23.000000000 -0700
+@@ -50,6 +50,8 @@
+ 
+ extern void print_cpu_info(struct cpuinfo_S390 *);
+ 
++extern void show_trace(unsigned long* esp);
++
+ /* 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/qdio.h	2006-01-30 22:25:23.000000000 -0700
+@@ -11,7 +11,7 @@
+ #ifndef __QDIO_H__
+ #define __QDIO_H__
+ 
+-#define VERSION_QDIO_H "$Revision: 1.66 $"
++#define VERSION_QDIO_H "$Revision: 1.66.4.5 $"
+ 
+ /* note, that most of the typedef's are from ingo. */
+ 
+@@ -42,11 +42,18 @@
+ #define QDIO_MAX_ELEMENTS_PER_BUFFER 16
+ #define SBAL_SIZE 256
+ 
+-#define IQDIO_FILL_LEVEL_TO_POLL (QDIO_MAX_BUFFERS_PER_Q*4/3)
++/* unfortunately this can't be (QDIO_MAX_BUFFERS_PER_Q*4/3) or so -- as
++ * we never know, whether we'll get initiative again, e.g. to give the
++ * transmit skb's back to the stack, however the stack may be waiting for
++ * them... therefore we define 4 as threshold to start polling (which
++ * will stop as soon as the asynchronous queue catches up)
++ * btw, this only applies to the asynchronous HiperSockets queue */
++#define IQDIO_FILL_LEVEL_TO_POLL 4
+ 
+ #define TIQDIO_THININT_ISC 3
+ #define TIQDIO_DELAY_TARGET 0
+-#define QDIO_BUSY_BIT_PATIENCE 2000 /* in microsecs */
++#define QDIO_BUSY_BIT_PATIENCE 100 /* in microsecs */
++#define QDIO_BUSY_BIT_GIVE_UP 10000000 /* 10 seconds */
+ #define IQDIO_GLOBAL_LAPS 2 /* GLOBAL_LAPS are not used as we */
+ #define IQDIO_GLOBAL_LAPS_INT 1 /* dont global summary */
+ #define IQDIO_LOCAL_LAPS 4
+@@ -612,6 +619,8 @@
+ typedef struct qdio_q_t {
+ 	volatile slsb_t slsb;
+ 
++	char unused[QDIO_MAX_BUFFERS_PER_Q];
++
+ 	__u32 * volatile dev_st_chg_ind;
+ 
+ 	int is_input_q;
+@@ -697,7 +706,9 @@
+ 		int last_transfer_index; */
+ 
+ 		__u64 last_transfer_time;
++		__u64 busy_start;
+ 	} timing;
++	atomic_t busy_siga_counter;
+         unsigned int queue_type;
+ } __attribute__ ((aligned(256))) qdio_q_t;
+ 
+@@ -713,7 +724,7 @@
+ 	unsigned int sync_done_on_outb_pcis;
+ 
+ 	unsigned int state;
+-	spinlock_t setting_up_lock;
++	struct semaphore setting_up_lock;
+ 
+ 	unsigned int no_input_qs;
+ 	unsigned int no_output_qs;
+@@ -783,7 +794,10 @@
+ 				int reserved1:21;
+ 				int isc:3;
+ 				/* word 9&10 */
+-				__u32 reserved2[2];
++                                __u32 word_with_d_bit;
++                                /* set to 0x10000000 to enable
++                                 * time delay disablement facility */
++                                __u32 reserved2;
+ 				/* word 11 */
+ 				__u32 subsystem_id;
+ 				/* word 12-1015 */
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/qeth.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/qeth.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,26 @@
++/*
++ * include/asm-s390/qeth.h
++ *
++ * S390 and zSeries version
++ *         Copyright (C) 2003 IBM Corporation
++ *         Author(s): Erwin Rol <erwinrol at de.ibm.com>
++ *
++ */
++
++#include <linux/s390net.h>
++
++#ifndef ASM_QETH_H
++#define ASM_QETH_H
++
++#define QETH_IOCPROC_REGISTER		_IOW(S390NET_IOC_MAGIC, 1, int)
++
++#define SIOC_QETH_ARP_SET_NO_ENTRIES	_IOWR(S390NET_IOC_MAGIC, 2, int)
++#define SIOC_QETH_ARP_QUERY_INFO	_IOWR(S390NET_IOC_MAGIC, 3, int)
++#define SIOC_QETH_ARP_ADD_ENTRY		_IOWR(S390NET_IOC_MAGIC, 4, int)
++#define SIOC_QETH_ARP_REMOVE_ENTRY	_IOWR(S390NET_IOC_MAGIC, 5, int)
++#define SIOC_QETH_ARP_FLUSH_CACHE	_IOWR(S390NET_IOC_MAGIC, 6, int)
++#define SIOC_QETH_ADP_SET_SNMP_CONTROL	_IOWR(S390NET_IOC_MAGIC, 7, int)
++#define SIOC_QETH_GET_CARD_TYPE		_IOWR(S390NET_IOC_MAGIC, 8, int)
++
++#endif /* !ASM_QETH_H */
++
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/setup.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/setup.h	2006-01-30 22:25:23.000000000 -0700
+@@ -25,15 +25,16 @@
+  */
+ extern unsigned long machine_flags;
  
- 	if (found)
- 		return indicators+i;
--	else
-+	else {
-+		atomic_inc(&spare_indicator_usecount);
- 		return (__u32 * volatile) &spare_indicator;
-+	}
- }
+-#define MACHINE_IS_VM    (machine_flags & 1)
+-#define MACHINE_HAS_IEEE (machine_flags & 2)
+-#define MACHINE_IS_P390  (machine_flags & 4)
+-#define MACHINE_HAS_CSP  (machine_flags & 8)
+-#define MACHINE_HAS_MVPG (machine_flags & 16)
++#define MACHINE_IS_VM		(machine_flags & 1)
++#define MACHINE_HAS_IEEE	(machine_flags & 2)
++#define MACHINE_IS_P390		(machine_flags & 4)
++#define MACHINE_HAS_CSP		(machine_flags & 8)
++#define MACHINE_HAS_MVPG	(machine_flags & 16)
++/* 32 is MACHINE_HAS_DIAG44 on s390x */
+ #define MACHINE_NEW_STIDP	(machine_flags & 64)
+-#define MACHINE_HAS_PFIX (machine_flags & 128)
++#define MACHINE_HAS_PFIX  	(machine_flags & 128)
  
- /* locked by the locks in qdio_activate and qdio_cleanup */
-@@ -531,6 +536,9 @@
- 		i=addr-indicators;
- 		indicator_used[i]=0;
- 	}
-+	if (addr==&spare_indicator) {
-+		atomic_dec(&spare_indicator_usecount);
-+	}
- }
+-#define MACHINE_HAS_HWC  (!MACHINE_IS_P390)
++#define MACHINE_HAS_SCLP	(!MACHINE_IS_P390)
  
- static inline volatile void tiqdio_clear_summary_bit(__u32 *location)
-@@ -621,8 +629,8 @@
- 		set_slsb(&q->slsb.acc.val[(gsf+QDIO_MAX_BUFFERS_PER_Q-1)&
- 			 (QDIO_MAX_BUFFERS_PER_Q-1)],SLSB_P_INPUT_NOT_INIT);
- 		/* we don't issue this SYNC_MEMORY, as we trust Rick T and
--		 * moreover will not use the PROCESSING state, so q->polling
--		 * was 0
-+		 * moreover will not use the PROCESSING state under VM,
-+		 * so q->polling was 0 anyway.
- 		SYNC_MEMORY;*/
- 		if (q->slsb.acc.val[gsf]==SLSB_P_INPUT_PRIMED) {
- 			/* set our summary bit again, as otherwise there is a
-@@ -655,6 +663,11 @@
- 	if ((q->is_thinint_q)&&(q->is_input_q)) {
- 		/* iQDIO */
- 		spin_lock_irqsave(&ttiq_list_lock,flags);
-+		/* in case cleanup has done this already and simultanously
-+		 * qdio_unmark_q is called from the interrupt handler, we've
-+		 * got to check this in this specific case again */
-+		if ((!q->list_prev)||(!q->list_next))
-+			goto out;
- 		if (q->list_next==q) {
- 			/* q was the only interesting q */
- 			tiq_list=NULL;
-@@ -667,6 +680,7 @@
- 			q->list_next=NULL;
- 			q->list_prev=NULL;
- 		}
-+out:
- 		spin_unlock_irqrestore(&ttiq_list_lock,flags);
- 	}
- }
-@@ -710,7 +724,7 @@
- 		       (void*)q->sbal[bufno],SBAL_SIZE);
- }
+ /*
+  * Console mode. Override with conmode=
+@@ -42,10 +43,10 @@
+ extern unsigned int console_device;
  
--inline static int qdio_get_outbound_buffer_frontier(qdio_q_t *q)
-+static inline int qdio_get_outbound_buffer_frontier(qdio_q_t *q)
+ #define CONSOLE_IS_UNDEFINED	(console_mode == 0)
+-#define CONSOLE_IS_HWC		(console_mode == 1)
++#define CONSOLE_IS_SCLP		(console_mode == 1)
+ #define CONSOLE_IS_3215		(console_mode == 2)
+ #define CONSOLE_IS_3270		(console_mode == 3)
+-#define SET_CONSOLE_HWC		do { console_mode = 1; } while (0)
++#define SET_CONSOLE_SCLP	do { console_mode = 1; } while (0)
+ #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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/sigp.h	2006-01-30 22:25:23.000000000 -0700
+@@ -106,7 +106,7 @@
+  * Signal processor with parameter and return status
+  */
+ extern __inline__ sigp_ccode
+-signal_processor_ps(__u32 *statusptr, __u32 parameter,
++signal_processor_ps(unsigned long *statusptr, __u32 parameter,
+ 		    __u16 cpu_addr, sigp_order_code order_code)
  {
- 	int f,f_mod_no;
- 	volatile char *slsb;
-@@ -739,7 +753,7 @@
- 	if (f==first_not_to_check) goto out;
- 	slsbyte=slsb[f_mod_no];
+ 	sigp_ccode ccode;
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/smp.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/smp.h	2006-01-30 22:25:23.000000000 -0700
+@@ -26,6 +26,9 @@
+ 	__u16      cpu;
+ } sigp_info;
  
--	/* the hydra has not fetched the output yet */
-+	/* the card has not fetched the output yet */
- 	if (slsbyte==SLSB_CU_OUTPUT_PRIMED) {
- #ifdef QDIO_DBF_LIKE_HELL
- 		QDIO_DBF_TEXT5(0,trace,"outpprim");
-@@ -747,7 +761,7 @@
- 		goto out;
- 	}
++extern int smp_call_function_on(void (*func) (void *info), void *info,
++				int nonatomic, int wait, int cpu);
++
+ extern unsigned long cpu_online_map;
  
--	/* the hydra got it */
-+	/* the card got it */
- 	if (slsbyte==SLSB_P_OUTPUT_EMPTY) {
- 		atomic_dec(&q->number_of_buffers_used);
- 		f++;
-@@ -797,7 +811,7 @@
- }
+ #define NO_PROC_ID		0xFF		/* No processor magic marker */
+@@ -46,23 +49,28 @@
  
- /* all buffers are processed */
--inline static int qdio_is_outbound_q_done(qdio_q_t *q)
-+static inline int qdio_is_outbound_q_done(qdio_q_t *q)
+ extern __inline__ int cpu_logical_map(int cpu)
  {
- 	int no_used;
- #ifdef QDIO_DBF_LIKE_HELL
-@@ -818,7 +832,7 @@
- 	return (no_used==0);
+-        return cpu;
++	return cpu;
  }
  
--inline static int qdio_has_outbound_q_moved(qdio_q_t *q)
-+static inline int qdio_has_outbound_q_moved(qdio_q_t *q)
+ extern __inline__ int cpu_number_map(int cpu)
  {
- 	int i;
- 
-@@ -827,7 +841,6 @@
- 	if ( (i!=GET_SAVED_FRONTIER(q)) ||
- 	     (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) {
- 		SAVE_FRONTIER(q,i);
--		SAVE_TIMESTAMP(q);
- #ifdef QDIO_DBF_LIKE_HELL
- 		QDIO_DBF_TEXT4(0,trace,"oqhasmvd");
- 		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-@@ -842,9 +855,10 @@
- 	}
+-        return cpu;
++	return cpu;
  }
  
--inline static void qdio_kick_outbound_q(qdio_q_t *q)
-+static inline void qdio_kick_outbound_q(qdio_q_t *q)
+ extern __inline__ __u16 hard_smp_processor_id(void)
  {
- 	int result;
-+	char dbf_text[15];
- #ifdef QDIO_DBF_LIKE_HELL
- 	QDIO_DBF_TEXT4(0,trace,"kickoutq");
- 	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-@@ -852,9 +866,68 @@
+-        __u16 cpu_address;
++	__u16 cpu_address;
+  
+-        __asm__ ("stap %0\n" : "=m" (cpu_address));
+-        return cpu_address;
++	__asm__ ("stap %0\n" : "=m" (cpu_address));
++	return cpu_address;
+ }
  
- 	if (!q->siga_out) return;
+ #define cpu_logical_map(cpu) (cpu)
  
-+	/* here's the story with cc=2 and busy bit set (thanks, Rick):
-+	 * VM's CP could present us cc=2 and busy bit set on SIGA-write
-+	 * during reconfiguration of their Guest LAN (only in HIPERS mode,
-+	 * QDIO mode is asynchronous -- cc=2 and busy bit there will take
-+	 * the queues down immediately; and not being under VM we have a
-+	 * problem on cc=2 and busy bit set right away).
-+	 *
-+	 * Therefore qdio_siga_output will try for a short time constantly,
-+	 * if such a condition occurs. If it doesn't change, it will
-+	 * increase the busy_siga_counter and save the timestamp, and
-+	 * schedule the queue for later processing (via mark_q, using the
-+	 * queue tasklet). __qdio_outbound_processing will check out the
-+	 * counter. If non-zero, it will call qdio_kick_outbound_q as often
-+	 * as the value of the counter. This will attempt further SIGA
-+	 * instructions.
-+	 * Every successful SIGA instruction will decrease the counter.
-+	 * After some time of no movement, qdio_kick_outbound_q will
-+	 * finally fail and reflect corresponding error codes to call
-+	 * the upper layer module and have it take the queues down.
-+	 *
-+	 * Note that this is a change from the original HiperSockets design
-+	 * (saying cc=2 and busy bit means take the queues down), but in
-+	 * these days Guest LAN didn't exist... excessive cc=2 with busy bit
-+	 * conditions will still take the queues down, but the threshold is
-+	 * higher due to the Guest LAN environment.
-+	 */
+ #endif
 +
- 	result=qdio_siga_output(q);
- 
--	if (result) {
-+		switch (result) {
-+		case 0:
-+		/* went smooth this time, reset timestamp */
-+#ifdef QDIO_DBF_LIKE_HELL
-+			QDIO_DBF_TEXT3(0,trace,"sigawsuc");
-+			sprintf(dbf_text,"%4x%2x%2x",q->irq,q->q_no,
-+				atomic_read(&q->busy_siga_counter));
-+			QDIO_DBF_TEXT3(0,trace,dbf_text);
-+#endif /* QDIO_DBF_LIKE_HELL */
-+			q->timing.busy_start=0;
-+			break;
-+		case (2|QDIO_SIGA_ERROR_B_BIT_SET):
-+			/* cc=2 and busy bit: */
-+		atomic_inc(&q->busy_siga_counter);
++#ifndef CONFIG_SMP
++#define smp_call_function_on(func,info,nonatomic,wait,cpu)      ({ 0; })
++#endif
 +
-+			/* if the last siga was successful, save
-+			 * timestamp here */
-+			if (!q->timing.busy_start)
-+				q->timing.busy_start=NOW;
+ #endif
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/tape390.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/tape390.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,39 @@
++/*************************************************************************
++ *
++ * tape390.h
++ *         enables user programs to display messages on the tape device
++ *
++ *  S390 and zSeries version
++ *         Copyright (C) 2001 IBM Corporation
++ *         Author(s): Despina Papadopoulou <despina_p at de.ibm.com>
++ *
++ *************************************************************************/
 +
-+			/* if we're in time, don't touch error_status_flags
-+			 * and siga_error */
-+			if (NOW-q->timing.busy_start<QDIO_BUSY_BIT_GIVE_UP) {
-+				qdio_mark_q(q);
-+				break;
-+			}
-+			QDIO_DBF_TEXT2(0,trace,"cc2REPRT");
-+			sprintf(dbf_text,"%4x%2x%2x",q->irq,q->q_no,
-+				atomic_read(&q->busy_siga_counter));
-+			QDIO_DBF_TEXT3(0,trace,dbf_text);
-+			/* else fallthrough and report error */
-+		default:
-+			/* for plain cc=1, 2 or 3: */
- 		if (q->siga_error)
- 			q->error_status_flags|=
- 				QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;
-@@ -864,7 +937,7 @@
- 	}
- }
- 
--inline static void qdio_kick_outbound_handler(qdio_q_t *q)
-+static inline void qdio_kick_outbound_handler(qdio_q_t *q)
- {
- #ifdef QDIO_DBF_LIKE_HELL
- 	char dbf_text[15];
-@@ -901,8 +974,9 @@
- 	q->error_status_flags=0;
- }
- 
--static void qdio_outbound_processing(qdio_q_t *q)
-+static inline void __qdio_outbound_processing(qdio_q_t *q)
- {
-+	int siga_attempts;
- #ifdef QDIO_DBF_LIKE_HELL
- 	QDIO_DBF_TEXT4(0,trace,"qoutproc");
- 	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
-@@ -926,6 +1000,14 @@
- 	o_p_nc++;
- #endif /* QDIO_PERFORMANCE_STATS */
- 
-+	/* see comment in qdio_kick_outbound_q */
-+	siga_attempts=atomic_read(&q->busy_siga_counter);
-+	while (siga_attempts) {
-+		atomic_dec(&q->busy_siga_counter);
-+		qdio_kick_outbound_q(q);
-+		siga_attempts--;
-+	}
++#ifndef _TAPE390_H
++#define _TAPE390_H
 +
- #ifdef QDIO_PERFORMANCE_STATS
- 	perf_stats.tl_runs++;
- #endif /* QDIO_PERFORMANCE_STATS */
-@@ -936,7 +1018,8 @@
- 
- 	if (q->is_iqdio_q) {
- 		/* for asynchronous queues, we better check, if the fill
--		 * level is too high */
-+		 * level is too high. for synchronous queues, the fill
-+		 * level will never be that high. */
- 		if (atomic_read(&q->number_of_buffers_used)>
- 		    IQDIO_FILL_LEVEL_TO_POLL) {
- 			qdio_mark_q(q);
-@@ -950,16 +1033,24 @@
- 	qdio_release_q(q);
- }
- 
-+static void qdio_outbound_processing(qdio_q_t *q)
-+{
-+	__qdio_outbound_processing(q);
-+}
++#define TAPE390_DISPLAY _IOW('d', 1, struct display_struct)
 +
- /************************* INBOUND ROUTINES *******************************/
- 
- 
--inline static int qdio_get_inbound_buffer_frontier(qdio_q_t *q)
-+static inline int qdio_get_inbound_buffer_frontier(qdio_q_t *q)
- {
- 	int f,f_mod_no;
- 	volatile char *slsb;
- 	char slsbyte;
- 	int first_not_to_check;
- 	char dbf_text[15];
-+#ifdef QDIO_USE_PROCESSING_STATE
-+	int last_position=-1;
-+#endif /* QDIO_USE_PROCESSING_STATE */
- 
- #ifdef QDIO_DBF_LIKE_HELL
- 	QDIO_DBF_TEXT4(0,trace,"getibfro");
-@@ -1002,8 +1093,14 @@
- 		if (q->siga_sync) {
- 			set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT);
- 		} else {
--			set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_PROCESSING);
-+			/* set the previous buffer to NOT_INIT. The current
-+			 * buffer will be set to PROCESSING at the end of
-+			 * this function to avoid further interrupts. */
-+			if (last_position>=0)
-+				set_slsb(&slsb[last_position],
-+					SLSB_P_INPUT_NOT_INIT);
- 			atomic_set(&q->polling,1);
-+			last_position=f_mod_no;
- 		}
- #else /* QDIO_USE_PROCESSING_STATE */
- 		set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT);
-@@ -1044,6 +1141,10 @@
- 		f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
- 		atomic_dec(&q->number_of_buffers_used);
- 
-+#ifdef QDIO_USE_PROCESSING_STATE
-+		last_position=-1;
-+#endif /* QDIO_USE_PROCESSING_STATE */
++/*
++ * The TAPE390_DISPLAY ioctl calls the Load Display command
++ * which transfers 17 bytes of data from the channel to the subsystem:
++ *     - 1 format control byte, and
++ *     - two 8-byte messages
++ *
++ * Format control byte:
++ *   0-2: New Message Overlay
++ *     3: Alternate Messages
++ *     4: Blink Message
++ *     5: Display Low/High Message
++ *     6: Reserved
++ *     7: Automatic Load Request
++ *
++ */
 +
- 		goto out;
- 	}
- 
-@@ -1051,6 +1152,11 @@
- out:
- 	q->first_to_check=f_mod_no;
++typedef struct display_struct {
++        char cntrl;
++        char message1[8];
++        char message2[8];
++} display_struct;
++
++#endif 
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390/timer.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390/timer.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,49 @@
++/*
++ *  include/asm-s390/timer.h
++ *
++ *  (C) Copyright IBM Corp. 2003
++ *  Virtual CPU timer
++ *
++ *  Author: Jan Glauber (jang at de.ibm.com)
++ */
++
++#ifndef _ASM_S390_TIMER_H
++#define _ASM_S390_TIMER_H
++
++#include <linux/timer.h>
++
++#define VTIMER_MAX_SLICE (0x7ffffffffffff000LL)
++
++struct vtimer_list {
++	struct list_head entry;
++
++	int cpu;
++	__u64 expires;
++	__u64 interval;
++
++	spinlock_t lock;
++	unsigned long magic;
++
++	void (*function)(unsigned long, struct pt_regs*);
++	unsigned long data;
++};
++
++/* the offset value will wrap after ca. 71 years */
++struct vtimer_queue {
++	struct list_head list;
++	spinlock_t lock;
++	__u64 to_expire;	  /* current event expire time */
++	__u64 offset;		  /* list offset to zero */
++	__u64 idle;		  /* temp var for idle */
++};
++
++void set_vtimer(__u64 expires);
++
++extern void init_virt_timer(struct vtimer_list *timer);
++extern void add_virt_timer(void *new);
++extern void add_virt_timer_periodic(void *new);
++extern int mod_virt_timer(struct vtimer_list *timer, __u64 expires);
++extern int del_virt_timer(struct vtimer_list *timer);
++
++extern atomic_t active_cpu_timer;
++#endif
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/ccwcache.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/ccwcache.h	2006-01-30 22:25:23.000000000 -0700
+@@ -4,7 +4,7 @@
+  * Bugreports.to..: <Linux390 at de.ibm.com>
+  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
+  *
+- * $Revision: 1.11 $
++ * $Revision: 1.11.4.2 $
+  *
+  */
+ #ifndef CCWCACHE_H
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/chandev.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/chandev.h	2006-01-30 22:25:23.000000000 -0700
+@@ -28,6 +28,7 @@
+ 	chandev_type_osad=0x8,
+ 	chandev_type_qeth=0x10,
+ 	chandev_type_claw=0x20,
++       chandev_type_ctcmpc=0x40,      
+ } chandev_type;
  
-+#ifdef QDIO_USE_PROCESSING_STATE
-+	if (last_position>=0)
-+		set_slsb(&slsb[last_position],SLSB_P_INPUT_PROCESSING);
-+#endif /* QDIO_USE_PROCESSING_STATE */
+ typedef enum
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/cmb.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/cmb.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,194 @@
++/*
++ * include/asm-s390/cmb.h
++ * include/asm-s390x/cmb.h
++ *
++ *         Copyright (C) 2003 IBM Corporation
++ *         Author: Arnd Bergmann <arndb at de.ibm.com>
++ */
++
++#ifndef S390_CMB_H
++#define S390_CMB_H
++/**
++ * struct cmbdata -- channel measurement block data for user space
++ *
++ * @size:			size of the stored data
++ * @elapsed_time:		time since last reset
++ * @ssch_rsch_count:		number I/O attempts, may overflow at
++ * 				2^16 or 2^32, depending on machine
++ * @sample_count:		number of sampled I/O attempts, should
++ * 				be identical to @ssch_rsch_count
++ * @device_connect_time:	time that device communicated with
++ * 				channel
++ * @function_pending_time:	time between command initiate and
++ * 				start of execution
++ * @device_disconnect_time:	time of device-inactive while
++ * 				subchannel-active
++ * @control_unit_queuing_time:	time that a device is blocked by
++ * 				I/O from another system
++ * @device_active_only_time:	time of device-active while
++ * 				subchannel-inactive
++ * @device_busy_time:		time where the device is busy
++ * 				when attemting to start I/O
++ * @initial_command_response_time: time between sending and 
++ * 				the device accepting a command
++ *
++ * all values are stored as 64 bit for simplicity, especially
++ * in 32 bit emulation mode. All time values are normalized to
++ * nanoseconds.
++ * Currently, two formats are known, which differ by the size of
++ * this structure, i.e. the last two members are only set when
++ * the extended channel measurement facility (first shipped in
++ * z990 machines) is activated.
++ * Potentially, more fields could be added, which results in a
++ * new ioctl number.
++ **/
++struct cmbdata {
++	__u64 size;
++	__u64 elapsed_time;
++ /* basic and exended format: */
++	__u64 ssch_rsch_count;
++	__u64 sample_count;
++	__u64 device_connect_time;
++	__u64 function_pending_time;
++	__u64 device_disconnect_time;
++	__u64 control_unit_queuing_time;
++	__u64 device_active_only_time;
++ /* extended format only: */
++	__u64 device_busy_time;
++	__u64 initial_command_response_time;
++};
++
++/* enable channel measurement */
++#define BIODASDCMFENABLE	_IO(DASD_IOCTL_LETTER,32)
++/* enable channel measurement */
++#define BIODASDCMFDISABLE	_IO(DASD_IOCTL_LETTER,33)
++/* read channel measurement data */
++#define BIODASDREADALLCMB	_IOWR(DASD_IOCTL_LETTER,33,struct cmbdata)
++
++#ifdef __KERNEL__
++#include <linux/config.h>
++#include <asm/irq.h>
++
++#if defined(CONFIG_S390_CMF) || defined(CONFIG_S390_CMF_MODULE)
++/**
++ * struct cmf_device - basic building block of the CMF code
++ *
++ * @cmb_list:	entry in the global list of cmf_devices
++ * @ccwlock:	a pointer to the subchannel's spinlock
++ * @cmb_start_time: clock value of previous cmf_reset operation
++ * @cmb:	pointer to the hardware channel measurement block
++ * @callback:	function pointer called by cmf_device_callback()
++ * @irq:	subchannel number of the associated device
++ */
++struct cmf_device {
++	struct list_head cmb_list;
++	spinlock_t *ccwlock;
++	u64 cmb_start_time;
++	void *cmb;
++	void (*callback)(struct cmf_device *);
++	u16  irq;
++};
 +
- #ifdef QDIO_DBF_LIKE_HELL
- 	QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));
- #endif /* QDIO_DBF_LIKE_HELL */
-@@ -1058,7 +1164,7 @@
- 	return q->first_to_check;
- }
- 
--inline static int qdio_has_inbound_q_moved(qdio_q_t *q)
-+static inline int qdio_has_inbound_q_moved(qdio_q_t *q)
- {
- 	int i;
- 
-@@ -1095,7 +1201,7 @@
- }
- 
- /* means, no more buffers to be filled */
--inline static int iqdio_is_inbound_q_done(qdio_q_t *q)
-+static inline int iqdio_is_inbound_q_done(qdio_q_t *q)
- {
- 	int no_used;
- #ifdef QDIO_DBF_LIKE_HELL
-@@ -1148,7 +1254,7 @@
- 	return 0;
- }
- 
--inline static int qdio_is_inbound_q_done(qdio_q_t *q)
-+static inline int qdio_is_inbound_q_done(qdio_q_t *q)
- {
- 	int no_used;
- #ifdef QDIO_DBF_LIKE_HELL
-@@ -1157,7 +1263,7 @@
- 
- 	no_used=atomic_read(&q->number_of_buffers_used);
- 
--	/* we need that one for synchronization with Hydra, as Hydra
-+	/* we need that one for synchronization with the OSA/FCP card, as it
- 	 * does a kind of PCI avoidance */
- 	SYNC_MEMORY;
- 
-@@ -1204,7 +1310,7 @@
- 	}
- }
- 
--inline static void qdio_kick_inbound_handler(qdio_q_t *q)
-+static inline void qdio_kick_inbound_handler(qdio_q_t *q)
- {
- 	int count=0;
- 	int start,end,real_end,i;
-@@ -1250,7 +1356,8 @@
- #endif /* QDIO_PERFORMANCE_STATS */
- }
- 
--static inline void tiqdio_inbound_processing(qdio_q_t *q)
-+static inline void __tiqdio_inbound_processing(qdio_q_t *q,
-+					       int spare_ind_was_set)
- {
- 	qdio_irq_t *irq_ptr;
- 	qdio_q_t *oq;
-@@ -1282,9 +1389,19 @@
- 		goto out;
- 	}
- 
--	if (*(q->dev_st_chg_ind)) {
--		tiqdio_clear_summary_bit((__u32*)q->dev_st_chg_ind);
-+	/* we reset spare_ind_was_set, when the queue does not use the
-+	 * spare indicator */
-+	if (spare_ind_was_set) {
-+		spare_ind_was_set = (q->dev_st_chg_ind==&spare_indicator);
++/**
++ * cmf_device_callback - call the callback function for set_cmf
++ *
++ * cmf_device_callback() can be integrated in an appropriate
++ * point in the device driver where no I/O is ongoing on the
++ * subchannel and it is safe to call set_cmf.
++ * This is a nop when CONFIG_S390_CMF is disabled
++ */
++static inline void cmf_device_callback(struct cmf_device *cdev)
++{
++	if (cdev->callback) {
++		cdev->callback(cdev);
 +	}
- 
-+	if ( (*(q->dev_st_chg_ind)) || (spare_ind_was_set) ) {
-+		/* q->dev_st_chg_ind is the indicator, be it shared or not.
-+		 * only clear it, if indicator is non-shared */
-+		if (!spare_ind_was_set) {
-+			tiqdio_clear_summary_bit((__u32*)q->dev_st_chg_ind);
-+		}
-+
- 		if (q->hydra_gives_outbound_pcis) {
- 			if (!q->siga_sync_done_on_thinints) {
- 				SYNC_MEMORY_ALL;
-@@ -1297,7 +1414,7 @@
- 		}
- 
- 		/* maybe we have to do work on our outbound queues... at least
--		 * we have to check Hydra outbound-int-capable thinint-capable
-+		 * we have to check for outbound-int-capable thinint-capable
- 		 * queues */
- 		if (q->hydra_gives_outbound_pcis) {
- 			irq_ptr=(qdio_irq_t*)q->irq_ptr;
-@@ -1307,7 +1424,7 @@
- 				perf_stats.tl_runs--;
- #endif /* QDIO_PERFORMANCE_STATS */
- 				if (!qdio_is_outbound_q_done(oq)) {
--					qdio_outbound_processing(oq);
-+					__qdio_outbound_processing(oq);
- 				}
- 			}
- 		}
-@@ -1330,8 +1447,13 @@
- 	qdio_release_q(q);
- }
- 
--static void qdio_inbound_processing(qdio_q_t *q)
-+static void tiqdio_inbound_processing(qdio_q_t *q)
- {
-+	__tiqdio_inbound_processing(q,atomic_read(&spare_indicator_usecount));
 +}
 +
-+static inline void __qdio_inbound_processing(qdio_q_t *q)
-+{
- 	int q_laps=0;
- 
- #ifdef QDIO_DBF_LIKE_HELL
-@@ -1375,11 +1497,17 @@
- 	qdio_release_q(q);
- }
- 
-+static void qdio_inbound_processing(qdio_q_t *q)
++/**
++ * cmf_device_init - initialize the cmf_device structure
++ *
++ * cmf_device_init() needs to be called before doing any other operations
++ * on a cmf_device.
++ */
++static inline void cmf_device_init(struct cmf_device *cdev, int devno)
 +{
-+	__qdio_inbound_processing(q);
++	INIT_LIST_HEAD (&cdev->cmb_list);
++	cdev->irq = get_irq_by_devno(devno);
++	cdev->ccwlock = get_irq_lock(cdev->irq);
 +}
 +
- /************************* MAIN ROUTINES *******************************/
- 
- static inline void tiqdio_inbound_checks(void)
- {
- 	qdio_q_t *q;
-+	int spare_ind_was_set=0;
- #ifdef QDIO_USE_PROCESSING_STATE
- 	int q_laps=0;
- #endif /* QDIO_USE_PROCESSING_STATE */
-@@ -1398,15 +1526,22 @@
- again:
- #endif /* QDIO_USE_PROCESSING_STATE */
- 
-+	/* when the spare indicator is used and set, save that and clear it */
-+	if ( (atomic_read(&spare_indicator_usecount)) && (spare_indicator) ) {
-+		spare_ind_was_set=1;
-+		tiqdio_clear_summary_bit((__u32*)&spare_indicator);
-+	}
++#else /* !CONFIG_S390_CMF */
++struct cmf_device { };
++static inline void cmf_device_callback(struct cmf_device *cdev)        { }
++static inline void cmf_device_init(struct cmf_device *cdev, int devno) { }
++#endif
 +
- 	q=(qdio_q_t*)tiq_list;
- 	/* switch all active queues to processing state */
- 	do {
- 		if (!q) break;
--		tiqdio_inbound_processing(q);
-+		__tiqdio_inbound_processing(q,spare_ind_was_set);
- 		q=(qdio_q_t*)q->list_next;
- 	} while (q!=(qdio_q_t*)tiq_list);
- 
--	/* switch off all queues' processing state */
-+	/* switch off all queues' processing state, see comments in
-+	 * qdio_get_inbound_buffer_frontier */
- #ifdef QDIO_USE_PROCESSING_STATE
- 	q=(qdio_q_t*)tiq_list;
- 	do {
-@@ -1589,7 +1724,7 @@
- 		kfree(irq_ptr->output_qs[i]);
- 
- 	}
--	if (irq_ptr->qdr) kfree(irq_ptr->qdr);
-+	kfree(irq_ptr->qdr);
- 	kfree(irq_ptr);
- }
- 
-@@ -1758,6 +1893,10 @@
- 			((irq_ptr->is_thinint_irq)?&tiqdio_inbound_processing:
- 			 &qdio_inbound_processing);
- 
-+		/* actually this is not used for inbound queues. yet. */
-+		atomic_set(&q->busy_siga_counter,0);
-+		q->timing.busy_start=0;
++/**
++ * enable_cmf() - switch on the channel measurement for a specific device
++ *  @cdev:	The ccw device to be enabled
++ *  returns 0 for success or a negative error value.
++ *
++ *  This function only allocates memory for the measurement block, the
++ *  actual activation is done with set_cmf()
++ *
++ *  Context:
++ *    may sleep, device is disabled
++ **/
++extern int enable_cmf(struct cmf_device *cdev);
 +
- /*		for (j=0;j<QDIO_STATS_NUMBER;j++)
- 			q->timing.last_transfer_times[j]=(qdio_get_micros()/
- 							  QDIO_STATS_NUMBER)*j;
-@@ -1849,6 +1988,9 @@
- 		q->tasklet.func=(void(*)(unsigned long))
- 			&qdio_outbound_processing;
- 
-+		atomic_set(&q->busy_siga_counter,0);
-+		q->timing.busy_start=0;
++/**
++ * disable_cmf() - switch off the channel measurement for a specific device
++ *  @cdev:	The ccw device to be disabled
++ *  returns 0 for success or a negative error value.
++ *
++ *  This function only frees the memory allocated with enable_cmf. If
++ *  measurement has been activated with set_cmf(), it also needs to be 
++ *  deactivated with that function before calling disable_cmf(). 
++ *
++ *  Context:
++ *    may sleep, device is enabled and inactive
++ **/
++extern void disable_cmf(struct cmf_device *cdev);
 +
- 		/* fill in slib */
- 		if (i>0) irq_ptr->output_qs[i-1]->slib->nsliba=
- 				 QDIO_PFIX_GET_ADDR(q->slib);
-@@ -1928,9 +2070,10 @@
- 	perf_stats.start_time_inbound=NOW;
- #endif /* QDIO_PERFORMANCE_STATS */
- 
--	/* VM will do the SVS for us
--	 * issue SVS to benefit from iqdio interrupt avoidance (SVS clears AISOI)*/
--	if (!MACHINE_IS_VM) {
-+	/* SVS only when needed:
-+	 * issue SVS to benefit from iqdio interrupt avoidance
-+	 * (SVS clears AISOI)*/
-+	if (!omit_svs) {
- 		tiqdio_clear_global_summary();
- 	}
- 
-@@ -2014,7 +2157,7 @@
- #ifdef QDIO_PERFORMANCE_STATS
- 				perf_stats.tl_runs--;
- #endif /* QDIO_PERFORMANCE_STATS */
--				qdio_inbound_processing(q);
-+				__qdio_inbound_processing(q);
- 			}
- 		}
- 		if (irq_ptr->hydra_gives_outbound_pcis) {
-@@ -2027,7 +2170,7 @@
- 					if (!irq_ptr->sync_done_on_outb_pcis) {
- 						SYNC_MEMORY;
- 					}
--					qdio_outbound_processing(q);
-+					__qdio_outbound_processing(q);
- 				}
- 			}
- 		}
-@@ -2206,7 +2349,10 @@
- static unsigned char qdio_check_siga_needs(int sch)
- {
- 	int resp_code,result;
-+	unsigned long flags;
- 
-+	spin_lock_irqsave(&chsc_area_lock,flags);
++/**
++ * cmf_readall() - read one value from the current channel measurement block
++ * @cmf:	the device to be read
++ * @data:	a pointer to a data block that will be filled
++ *
++ *  Context:
++ *    device is active
++ **/
++extern int cmf_readall(struct cmf_device *cdev, struct cmbdata*data);
 +
- 	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
- 	chsc_area->request_block.command_code1=0x0010; /* length */
- 	chsc_area->request_block.command_code2=0x0024; /* op code */
-@@ -2219,7 +2365,8 @@
- 		QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \
- 				"SIGAs for sch x%x.\n",
- 				result,sch);
--		return -1; /* all flags set */
-+		result=-1; /* all flags set */
-+		goto out;
- 	}
- 
- 	resp_code=chsc_area->request_block.operation_data_area.
-@@ -2228,7 +2375,8 @@
- 		QDIO_PRINT_WARN("response upon checking SIGA needs " \
- 				"is 0x%x. Using all SIGAs for sch x%x.\n",
- 				resp_code,sch);
--		return -1; /* all flags set */
-+		result=-1; /* all flags set */
-+		goto out;
- 	}
- 	if (
- 	    (!(chsc_area->request_block.operation_data_area.
-@@ -2240,19 +2388,26 @@
- 	    ) {
- 		QDIO_PRINT_WARN("huh? problems checking out sch x%x... " \
- 				"using all SIGAs.\n",sch);
--		return CHSC_FLAG_SIGA_INPUT_NECESSARY |
-+		result=CHSC_FLAG_SIGA_INPUT_NECESSARY |
- 			CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
- 			CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */
-+		goto out;
- 	}
- 
--	return chsc_area->request_block.operation_data_area.
-+	result=chsc_area->request_block.operation_data_area.
- 		store_qdio_data_response.qdioac;
-+out:
-+	spin_unlock_irqrestore(&chsc_area_lock,flags);
-+	return result;
- }
- 
--static int qdio_check_for_hydra_thinints(void)
-+static void qdio_check_for_machine_features(void)
- {
- 	int i,result;
-+	unsigned long flags;
- 
-+	spin_lock_irqsave(&chsc_area_lock,flags);
++/**
++ * set_cmf() - Set the measurement mode for a specific device
++ * @cmf:	the device to be modified
++ * @mme:	measurement mode enable value, can be either
++ *		0 for deactivation or 2 for activation
++ *
++ * It is important to call this function only when there is no I/O
++ * activity on the subchannel. Therefore it may be necessary to call
++ * it from an interrupt handler at the point where the previous
++ * request is finished.
++ * In 2.6.x, set_cmf() is integrated within enable_cmf() and disable_cmf(),
++ * which makes a lot of sense and life much easier for users.
++ *
++ *  Context:
++ *    device lock held, device is enabled, subchannel is idle
++ **/
++extern int set_cmf(struct cmf_device *cdev, u32 mme);
 +
- 	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
- 	chsc_area->request_block.command_code1=0x0010;
- 	chsc_area->request_block.command_code2=0x0010;
-@@ -2260,16 +2415,19 @@
++/**
++ * reset_cmf() - clear a channel measurement block
++ * @cmf:	the device to be cleared
++ *
++ * This function is used to set all values in a channel measurement block
++ * to sane values. It should be called between enable_cmf() and set_cmf(cdev,2)
++ * but can be called as well when the device is already active.
++ *
++ *  Context:
++ *    device is enabled
++ **/
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/dasd.h	2006-01-30 22:25:23.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
+  *
+- * $Revision: 1.53 $
++ * $Revision: 1.52.6.3 $
+  *
+  * 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/dcss.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,19 @@
++/*
++ *  include/asm-s390x/dcss.h
++ *
++ *  definitions for discontiguous saved segment support
++ *  Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
++ */
++
++#ifndef _ASM_S390X_DCSS_H
++#define _ASM_S390X_DCSS_H
++#ifndef __ASSEMBLY__
++#define SEGMENT_SHARED_RW       0
++#define SEGMENT_SHARED_RO       1
++#define SEGMENT_EXCLUSIVE_RW    2
++#define SEGMENT_EXCLUSIVE_RO    3
++extern int segment_load (char *name,int segtype,unsigned long *addr,unsigned long *length);
++extern void segment_unload(char *name);
++extern void segment_replace(char *name);
++#endif
++#endif
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/ioctl32.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/ioctl32.h	2006-01-30 22:25:23.000000000 -0700
+@@ -8,7 +8,7 @@
+ #ifndef ASM_IOCTL32_H
+ #define ASM_IOCTL32_H
  
- 	if (result) {
- 		QDIO_PRINT_WARN("CHSC returned cc %i. Won't use adapter " \
--				"interrupts for any Hydra.\n",result);
--		return 0;
-+				"interrupts for any QDIO device.\n",result);
-+		result=0;
-+		goto out;
- 	}
+-extern int sys_ioctl(unsigned int, unsigned int, unsigned long, struct file*);
++extern int sys_ioctl(unsigned int, unsigned int, unsigned long);
+ typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *);
  
- 	i=chsc_area->request_block.operation_data_area.
- 		store_qdio_data_response.response_code;
- 	if (i!=1) {
- 		QDIO_PRINT_WARN("Was not able to determine general " \
--				"characteristics of all Hydras aboard.\n");
--		return 0;
-+				"characteristics of all QDIO devices " \
-+				"aboard.\n");
-+		result=0;
-+		goto out;
- 	}
+ #ifdef CONFIG_S390_SUPPORT
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/irq.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/irq.h	2006-01-30 22:25:23.000000000 -0700
+@@ -42,7 +42,9 @@
+       __u8  chpid[8];     /* CHPID 0-7 (if available) */
+       __u32 unused1 : 8;  /* reserved zeros */
+       __u32 st      : 3;  /* subchannel type */
+-      __u32 unused2 : 20; /* reserved zeros */
++      __u32 unused2 : 18; /* reserved zeros */
++      __u32 mbfc    : 1;  /* measurement block format control */
++      __u32 xmwme   : 1;  /* extended measurement word mode enable */
+       __u32 csense  : 1;  /* concurrent sense; can be enabled ...*/
+                           /*  ... per MSCH, however, if facility */
+                           /*  ... is not installed, this results */
+@@ -156,7 +158,8 @@
+ typedef struct {
+       pmcw_t pmcw;             /* path management control word */
+       scsw_t scsw;             /* subchannel status word */
+-      __u8 mda[12];            /* model dependent area */
++      __u64 mba;               /* measurement block address */
++      __u8 mda[4];             /* model dependent area */
+    } __attribute__ ((packed,aligned(4))) schib_t;
+ #endif /* __KERNEL__ */
  
- 	/* 4: request block
-@@ -2277,17 +2435,30 @@
- 	 * 512: chsc char */
- 	/* check for bit 67 */
- 	if ( (*(((unsigned int*)(chsc_area))+4+2+2)&0x10000000)!=0x10000000) {
--		return 0;
-+		hydra_thinints=0;
- 	} else {
--		return 1;
-+		hydra_thinints=1;
- 	}
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/kmap_types.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/kmap_types.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,16 @@
++#ifndef _ASM_KMAP_TYPES_H
++#define _ASM_KMAP_TYPES_H
 +
-+	/* check for bit 56: if aif time delay disablement fac installed,
-+	 * omit svs even under lpar (good point by rick again) */
-+	if ( (*(((unsigned int*)(chsc_area))+4+2+1)&0x00000080)!=0x00000080) {
-+		omit_svs=1;
-+	} else {
-+		omit_svs=0;
-+	}
-+out:
-+	spin_unlock_irqrestore(&chsc_area_lock,flags);
- }
- 
- /* the chsc_area is locked by the lock in qdio_activate */
- static unsigned int tiqdio_check_chsc_availability(void) {
- 	int result;
- 	int i;
-+	unsigned long flags;
- 
-+	spin_lock_irqsave(&chsc_area_lock,flags);
++enum km_type {
++	KM_BOUNCE_READ,
++	KM_SKB_SUNRPC_DATA,
++	KM_SKB_DATA_SOFTIRQ,
++	KM_USER0,
++	KM_USER1,
++	KM_BH_IRQ,
++	KM_SOFTIRQ0,
++	KM_SOFTIRQ1,
++	KM_TYPE_NR
++};
 +
- 	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
- 	chsc_area->request_block.command_code1=0x0010;
- 	chsc_area->request_block.command_code2=0x0010;
-@@ -2327,6 +2498,7 @@
- 		goto exit;
- 	}
- exit:
-+	spin_unlock_irqrestore(&chsc_area_lock,flags);
- 	return result;
- }
- 
-@@ -2337,6 +2509,7 @@
- 	unsigned long real_addr_local_summary_bit;
- 	unsigned long real_addr_dev_st_chg_ind;
- 	void *ptr;
-+	unsigned long flags;
- 	char dbf_text[15];
++#endif
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/lowcore.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/lowcore.h	2006-01-30 22:25:23.000000000 -0700
+@@ -48,6 +48,7 @@
+ #define __LC_IPLDEV                     0xDB8
  
- 	unsigned int resp_code;
-@@ -2354,6 +2527,8 @@
- 			virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind);
- 	}
+ #define __LC_JIFFY_TIMER		0xDC0
++#define __LC_INT_CLOCK			0xDC8
  
-+	spin_lock_irqsave(&chsc_area_lock,flags);
-+
- 	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
- 	chsc_area->request_block.command_code1=0x0fe0;
- 	chsc_area->request_block.command_code2=0x0021;
-@@ -2372,12 +2547,19 @@
- 		isc=TIQDIO_THININT_ISC;
- 	chsc_area->request_block.operation_data_area.set_chsc.
- 		subsystem_id=(1<<16)+irq_ptr->irq;
-+	/* enables the time delay disablement facility. Don't care
-+	 * whether it is really there (i.e. we haven't checked for
-+	 * it) */
-+	chsc_area->request_block.operation_data_area.set_chsc.
-+               word_with_d_bit=0x10000000;
+ #define __LC_PANIC_MAGIC                0xE00
  
-+
- 	result=qdio_chsc(chsc_area);
- 	if (result) {
- 		QDIO_PRINT_WARN("could not set indicators on irq x%x, " \
- 				"cc=%i.\n",irq_ptr->irq,result);
--		return -EIO;
-+		result=-EIO;
-+		goto out;
- 	}
+@@ -164,8 +165,9 @@
  
- 	resp_code=chsc_area->response_block.response_code;
-@@ -2389,14 +2571,18 @@
- 		QDIO_DBF_TEXT1(0,setup,dbf_text);
- 		ptr=&chsc_area->response_block;
- 		QDIO_DBF_HEX2(1,setup,&ptr,QDIO_DBF_SETUP_LEN);
--		return -EIO;
-+		result=-EIO;
-+		goto out;
- 	}
+         /* SMP info area: defined by DJB */
+         __u64        jiffy_timer;              /* 0xdc0 */
+-	__u64        ext_call_fast;            /* 0xdc8 */
+-        __u8         pad12[0xe00-0xdd0];       /* 0xdd0 */
++        __u64        int_clock;                /* 0xdc8 */
++	__u64        ext_call_fast;            /* 0xdd0 */
++        __u8         pad12[0xe00-0xdd8];       /* 0xdd8 */
  
- 	QDIO_DBF_TEXT2(0,setup,"setscind");
- 	QDIO_DBF_HEX2(0,setup,&real_addr_local_summary_bit,
- 		      sizeof(unsigned long));
- 	QDIO_DBF_HEX2(0,setup,&real_addr_dev_st_chg_ind,sizeof(unsigned long));
--	return 0;
-+	result=0;
-+out:
-+	spin_unlock_irqrestore(&chsc_area_lock,flags);
-+	return result;
- }
+         /* 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/pgtable.h	2006-01-30 22:25:23.000000000 -0700
+@@ -484,9 +484,11 @@
+ 	__pte;                                                            \
+ })
  
- /* chsc_area would have to be locked if called from outside qdio_activate */
-@@ -2406,10 +2592,13 @@
- 	unsigned int resp_code;
- 	int result;
- 	void *ptr;
-+	unsigned long flags;
- 	char dbf_text[15];
+-#define arch_set_page_uptodate(__page)					  \
++#define SetPageUptodate(_page) \
+ 	do {								  \
+-		asm volatile ("sske %0,%1" : : "d" (0),			  \
++		struct page *__page = (_page);				  \
++		if (!test_and_set_bit(PG_uptodate, &__page->flags))	  \
++			asm volatile ("sske %0,%1" : : "d" (0),		  \
+ 			      "a" (__pa((__page-mem_map) << PAGE_SHIFT)));\
+ 	} while (0)
  
- 	if (!irq_ptr->is_thinint_irq) return -ENODEV;
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/processor.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/processor.h	2006-01-30 22:25:23.000000000 -0700
+@@ -52,6 +52,8 @@
  
-+	spin_lock_irqsave(&chsc_area_lock,flags);
+ extern void print_cpu_info(struct cpuinfo_S390 *);
+ 
++extern void show_trace(unsigned long* esp);
 +
- 	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
- 	chsc_area->request_block.command_code1=0x0fe0;
- 	chsc_area->request_block.command_code2=0x1027;
-@@ -2420,7 +2609,8 @@
- 	if (result) {
- 		QDIO_PRINT_WARN("could not set delay target on irq x%x, " \
- 				"cc=%i. Continuing.\n",irq_ptr->irq,result);
--		return -EIO;
-+		result=-EIO;
-+		goto out;
- 	}
+ /* Lazy FPU handling on uni-processor */
+ extern struct task_struct *last_task_used_math;
  
- 	resp_code=chsc_area->response_block.response_code;
-@@ -2435,7 +2625,10 @@
- 	}
- 	QDIO_DBF_TEXT2(0,trace,"delytrgt");
- 	QDIO_DBF_HEX2(0,trace,&delay_target,sizeof(unsigned long));
--	return 0;
-+	result=0;
-+out:
-+	spin_unlock_irqrestore(&chsc_area_lock,flags);
-+	return result;
- }
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/qdio.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/qdio.h	2006-01-30 22:25:23.000000000 -0700
+@@ -11,7 +11,7 @@
+ #ifndef __QDIO_H__
+ #define __QDIO_H__
  
- int qdio_cleanup(int irq,int how)
-@@ -2445,7 +2638,7 @@
- 	int do_an_irqrestore=0;
- 	unsigned long flags;
- 	int timeout;
--	char dbf_text[15]="12345678";
-+	char dbf_text[15];
+-#define VERSION_QDIO_H "$Revision: 1.57 $"
++#define VERSION_QDIO_H "$Revision: 1.57.4.5 $"
  
- 	result=0;
- 	sprintf(dbf_text,"qcln%4x",irq);
-@@ -2455,7 +2648,7 @@
- 	irq_ptr=qdio_get_irq_ptr(irq);
- 	if (!irq_ptr) return -ENODEV;
+ /* note, that most of the typedef's are from ingo. */
  
--	spin_lock(&irq_ptr->setting_up_lock);
-+	down(&irq_ptr->setting_up_lock);
+@@ -42,11 +42,18 @@
+ #define QDIO_MAX_ELEMENTS_PER_BUFFER 16
+ #define SBAL_SIZE 256
  
- 	/* mark all qs as uninteresting */
- 	for (i=0;i<irq_ptr->no_input_qs;i++) {
-@@ -2527,7 +2720,7 @@
- 	if (do_an_irqrestore)
- 		s390irq_spin_unlock_irqrestore(irq,flags);
+-#define IQDIO_FILL_LEVEL_TO_POLL (QDIO_MAX_BUFFERS_PER_Q*4/3)
++/* unfortunately this can't be (QDIO_MAX_BUFFERS_PER_Q*4/3) or so -- as
++ * we never know, whether we'll get initiative again, e.g. to give the
++ * transmit skb's back to the stack, however the stack may be waiting for
++ * them... therefore we define 4 as threshold to start polling (which
++ * will stop as soon as the asynchronous queue catches up)
++ * btw, this only applies to the asynchronous HiperSockets queue */
++#define IQDIO_FILL_LEVEL_TO_POLL 4
  
--	spin_unlock(&irq_ptr->setting_up_lock);
-+	up(&irq_ptr->setting_up_lock);
+ #define TIQDIO_THININT_ISC 3
+ #define TIQDIO_DELAY_TARGET 0
+-#define QDIO_BUSY_BIT_PATIENCE 2000 /* in microsecs */
++#define QDIO_BUSY_BIT_PATIENCE 100 /* in microsecs */
++#define QDIO_BUSY_BIT_GIVE_UP 10000000 /* 10 seconds */
+ #define IQDIO_GLOBAL_LAPS 2 /* GLOBAL_LAPS are not used as we */
+ #define IQDIO_GLOBAL_LAPS_INT 1 /* dont global summary */
+ #define IQDIO_LOCAL_LAPS 4
+@@ -612,6 +619,8 @@
+ typedef struct qdio_q_t {
+ 	volatile slsb_t slsb;
  
- 	qdio_remove_irq_ptr(irq_ptr);
- 	qdio_release_irq_memory(irq_ptr);
-@@ -2574,16 +2767,15 @@
- 	int result,result2;
- 	int found;
- 	unsigned long flags;
--	char dbf_text[20]; /* if a printf would print out more than 8 chars */
-+	char dbf_text[20]; /* if a printf printed out more than 8 chars */
++	char unused[QDIO_MAX_BUFFERS_PER_Q];
++
+ 	__u32 * volatile dev_st_chg_ind;
  
--	down_interruptible(&init_sema);
-+	down(&init_sema);
+ 	int is_input_q;
+@@ -697,7 +706,9 @@
+ 		int last_transfer_index; */
  
- 	sprintf(dbf_text,"qini%4x",init_data->irq);
- 	QDIO_DBF_TEXT0(0,setup,dbf_text);
- 	QDIO_DBF_TEXT0(0,trace,dbf_text);
- 	sprintf(dbf_text,"qfmt:%x",init_data->q_format);
- 	QDIO_DBF_TEXT0(0,setup,dbf_text);
--	QDIO_DBF_TEXT0(0,setup,init_data->adapter_name);
- 	QDIO_DBF_HEX0(0,setup,init_data->adapter_name,8);
- 	sprintf(dbf_text,"qpff%4x",init_data->qib_param_field_format);
- 	QDIO_DBF_TEXT0(0,setup,dbf_text);
-@@ -2672,7 +2864,6 @@
+ 		__u64 last_transfer_time;
++		__u64 busy_start;
+ 	} timing;
++	atomic_t busy_siga_counter;
+         unsigned int queue_type;
+ } __attribute__ ((aligned(256))) qdio_q_t;
  
- 	irq_ptr->qdr=kmalloc(sizeof(qdr_t),GFP_DMA);
-   	if (!(irq_ptr->qdr)) {
--   		kfree(irq_ptr->qdr);
-    		kfree(irq_ptr);
-     		QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n");
-      		result=-ENOMEM;
-@@ -2736,12 +2927,12 @@
+@@ -713,7 +724,7 @@
+ 	unsigned int sync_done_on_outb_pcis;
  
- 	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE);
+ 	unsigned int state;
+-	spinlock_t setting_up_lock;
++	struct semaphore setting_up_lock;
  
--	irq_ptr->setting_up_lock=SPIN_LOCK_UNLOCKED;
-+	sema_init(&irq_ptr->setting_up_lock,1);
+ 	unsigned int no_input_qs;
+ 	unsigned int no_output_qs;
+@@ -783,7 +794,10 @@
+ 				int reserved1:21;
+ 				int isc:3;
+ 				/* word 9&10 */
+-				__u32 reserved2[2];
++                                __u32 word_with_d_bit;
++                                /* set to 0x10000000 to enable
++                                 * time delay disablement facility */
++                                __u32 reserved2;
+ 				/* word 11 */
+ 				__u32 subsystem_id;
+ 				/* word 12-1015 */
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/qeth.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/qeth.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,26 @@
++/*
++ * include/asm-s390x/qeth.h
++ *
++ * S390 and zSeries version
++ *         Copyright (C) 2003 IBM Corporation
++ *         Author(s): Erwin Rol <erwinrol at de.ibm.com>
++ *
++ */
++
++#include <linux/s390net.h>
++
++#ifndef ASM_QETH_H
++#define ASM_QETH_H
++
++#define QETH_IOCPROC_REGISTER		_IOW(S390NET_IOC_MAGIC, 1, int)
++
++#define SIOC_QETH_ARP_SET_NO_ENTRIES	_IOWR(S390NET_IOC_MAGIC, 2, int)
++#define SIOC_QETH_ARP_QUERY_INFO	_IOWR(S390NET_IOC_MAGIC, 3, int)
++#define SIOC_QETH_ARP_ADD_ENTRY		_IOWR(S390NET_IOC_MAGIC, 4, int)
++#define SIOC_QETH_ARP_REMOVE_ENTRY	_IOWR(S390NET_IOC_MAGIC, 5, int)
++#define SIOC_QETH_ARP_FLUSH_CACHE	_IOWR(S390NET_IOC_MAGIC, 6, int)
++#define SIOC_QETH_ADP_SET_SNMP_CONTROL	_IOWR(S390NET_IOC_MAGIC, 7, int)
++#define SIOC_QETH_GET_CARD_TYPE		_IOWR(S390NET_IOC_MAGIC, 8, int)
++
++#endif /* !ASM_QETH_H */
++
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/setup.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/setup.h	2006-01-30 22:25:23.000000000 -0700
+@@ -32,7 +32,7 @@
+ #define MACHINE_NEW_STIDP	(machine_flags & 64)
+ #define MACHINE_HAS_PFIX  	(0)
  
- 	MOD_INC_USE_COUNT;
- 	QDIO_DBF_TEXT3(0,setup,"MOD_INC_");
+-#define MACHINE_HAS_HWC		(!MACHINE_IS_P390)
++#define MACHINE_HAS_SCLP	(!MACHINE_IS_P390)
  
--	spin_lock(&irq_ptr->setting_up_lock);
-+	down(&irq_ptr->setting_up_lock);
+ /*
+  * Console mode. Override with conmode=
+@@ -41,10 +41,10 @@
+ extern unsigned int console_device;
  
- 	qdio_insert_irq_ptr(irq_ptr);
+ #define CONSOLE_IS_UNDEFINED	(console_mode == 0)
+-#define CONSOLE_IS_HWC		(console_mode == 1)
++#define CONSOLE_IS_SCLP		(console_mode == 1)
+ #define CONSOLE_IS_3215		(console_mode == 2)
+ #define CONSOLE_IS_3270		(console_mode == 3)
+-#define SET_CONSOLE_HWC		do { console_mode = 1; } while (0)
++#define SET_CONSOLE_SCLP	do { console_mode = 1; } while (0)
+ #define SET_CONSOLE_3215	do { console_mode = 2; } while (0)
+ #define SET_CONSOLE_3270	do { console_mode = 3; } while (0)
  
-@@ -2867,10 +3058,10 @@
- 		}
- 		result=tiqdio_set_subchannel_ind(irq_ptr,0);
- 		if (result) {
--			spin_unlock(&irq_ptr->setting_up_lock);
-+			up(&irq_ptr->setting_up_lock);
- 			qdio_cleanup(irq_ptr->irq,
- 				     QDIO_FLAG_CLEANUP_USING_CLEAR);
--			goto out2;
-+			goto out;
- 		}
- 		tiqdio_set_delay_target(irq_ptr,TIQDIO_DELAY_TARGET);
- 	}
-@@ -2906,9 +3097,9 @@
- 	s390irq_spin_unlock_irqrestore(irq_ptr->irq,saveflags);
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/siginfo.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/siginfo.h	2006-01-30 22:25:23.000000000 -0700
+@@ -19,7 +19,7 @@
+ } sigval_t;
  
- 	if (result) {
--		spin_unlock(&irq_ptr->setting_up_lock);
-+		up(&irq_ptr->setting_up_lock);
- 		qdio_cleanup(irq_ptr->irq,QDIO_FLAG_CLEANUP_USING_CLEAR);
--		goto out2;
-+		goto out;
- 	}
+ #define SI_MAX_SIZE	128
+-#define SI_PAD_SIZE	((SI_MAX_SIZE/sizeof(int)) - 3)
++#define SI_PAD_SIZE	((SI_MAX_SIZE/sizeof(int)) - 4)
  
- 	result=qdio_sleepon(&irq_ptr->interrupt_has_arrived,
-@@ -2918,9 +3109,9 @@
- 		QDIO_PRINT_ERR("establish queues on irq %04x: timed out\n",
- 			   irq_ptr->irq);
- 		QDIO_DBF_TEXT2(1,setup,"eq:timeo");
--		spin_unlock(&irq_ptr->setting_up_lock);
-+		up(&irq_ptr->setting_up_lock);
- 		qdio_cleanup(irq_ptr->irq,QDIO_FLAG_CLEANUP_USING_CLEAR);
--		goto out2;
-+		goto out;
- 	}
+ typedef struct siginfo {
+ 	int si_signo;
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/sigp.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/sigp.h	2006-01-30 22:25:23.000000000 -0700
+@@ -107,7 +107,7 @@
+  * Signal processor with parameter and return status
+  */
+ extern __inline__ sigp_ccode
+-signal_processor_ps(__u32 *statusptr, __u64 parameter,
++signal_processor_ps(unsigned long *statusptr, __u64 parameter,
+ 		    __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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/smp.h	2006-01-30 22:25:23.000000000 -0700
+@@ -26,6 +26,9 @@
+ 	__u16      cpu;
+ } sigp_info;
  
- 	if (!(irq_ptr->io_result_dstat & DEV_STAT_DEV_END)) {
-@@ -2937,10 +3128,10 @@
- 			       irq_ptr->irq,irq_ptr->io_result_dstat,
- 			       irq_ptr->io_result_cstat,
- 			       irq_ptr->io_result_flags);
--		spin_unlock(&irq_ptr->setting_up_lock);
-+		up(&irq_ptr->setting_up_lock);
- 		qdio_cleanup(irq_ptr->irq,QDIO_FLAG_CLEANUP_USING_CLEAR);
- 		result=-EIO;
--		goto out2;
-+		goto out;
- 	}
++extern int smp_call_function_on(void (*func) (void *info), void *info,
++				int nonatomic, int wait, int cpu);
++
+ extern unsigned long cpu_online_map;
  
- 	if (irq_ptr->io_result_dstat & ~(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) {
-@@ -2958,18 +3149,19 @@
- 			       irq_ptr->io_result_cstat,
- 			       irq_ptr->io_result_flags);
- 		result=-EIO;
-+		up(&irq_ptr->setting_up_lock);
- 		goto out;
- 	}
+ #define NO_PROC_ID		0xFF		/* No processor magic marker */
+@@ -65,4 +68,9 @@
+ #define cpu_logical_map(cpu) (cpu)
  
--	if (MACHINE_IS_VM)
- 		irq_ptr->qdioac=qdio_check_siga_needs(irq_ptr->irq);
--	else { 
--                irq_ptr->qdioac=CHSC_FLAG_SIGA_INPUT_NECESSARY
--                        | CHSC_FLAG_SIGA_OUTPUT_NECESSARY;
--        }
- 	sprintf(dbf_text,"qdioac%2x",irq_ptr->qdioac);
- 	QDIO_DBF_TEXT2(0,setup,dbf_text);
+ #endif
++
++#ifndef CONFIG_SMP
++#define smp_call_function_on(func,info,nonatomic,wait,cpu)      ({ 0; })
++#endif
++
+ #endif
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/tape390.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/tape390.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,39 @@
++/*************************************************************************
++ *
++ * tape390.h
++ *         enables user programs to display messages on the tape device
++ *
++ *  S390 and zSeries version
++ *         Copyright (C) 2001 IBM Corporation
++ *         Author(s): Despina Papadopoulou <despina_p at de.ibm.com>
++ *
++ *************************************************************************/
++
++#ifndef _TAPE390_H
++#define _TAPE390_H
++
++#define TAPE390_DISPLAY _IOW('d', 1, struct display_struct)
++
++/*
++ * The TAPE390_DISPLAY ioctl calls the Load Display command
++ * which transfers 17 bytes of data from the channel to the subsystem:
++ *     - 1 format control byte, and
++ *     - two 8-byte messages
++ *
++ * Format control byte:
++ *   0-2: New Message Overlay
++ *     3: Alternate Messages
++ *     4: Blink Message
++ *     5: Display Low/High Message
++ *     6: Reserved
++ *     7: Automatic Load Request
++ *
++ */
++
++typedef struct display_struct {
++        char cntrl;
++        char message1[8];
++        char message2[8];
++} display_struct;
++
++#endif 
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-s390x/timer.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-s390x/timer.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,48 @@
++/*
++ *  include/asm-s390/timer.h
++ *
++ *  (C) Copyright IBM Corp. 2003
++ *  Virtual CPU timer
++ *
++ *  Author: Jan Glauber (jang at de.ibm.com)
++ */
++
++#ifndef _ASM_S390_TIMER_H
++#define _ASM_S390_TIMER_H
++
++#include <linux/timer.h>
++
++#define VTIMER_MAX_SLICE (0x7ffffffffffff000LL)
++
++struct vtimer_list {
++	struct list_head entry;
++
++	int cpu;
++	__u64 expires;
++	__u64 interval;
++
++	spinlock_t lock;
++	unsigned long magic;
++
++	void (*function)(unsigned long, struct pt_regs*);
++	unsigned long data;
++};
++
++/* the offset value will wrap after ca. 71 years */
++struct vtimer_queue {
++	struct list_head list;
++	spinlock_t lock;
++	__u64 to_expire;	  /* current event expire time */
++	__u64 offset;		  /* list offset to zero */
++	__u64 idle;		  /* temp var for idle */
++};
++
++void set_vtimer(__u64 expires);
++
++extern void init_virt_timer(struct vtimer_list *timer);
++extern void add_virt_timer(void *new);
++extern void add_virt_timer_periodic(void *new);
++extern int mod_virt_timer(struct vtimer_list *timer, __u64 expires);
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-sh/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+@@ -153,4 +153,7 @@
+ 	pte_t old_pte = *ptep;
+ 	set_pte(ptep, pte_mkdirty(old_pte));
+ }
++
++#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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-sparc/pgalloc.h	2006-01-30 22:25:22.000000000 -0700
+@@ -141,4 +141,6 @@
  
-+	/* if this gets set once, we're running under VM and can omit SVSes */
-+	if (irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY) {
-+		omit_svs=1;
-+	}
+ #define pte_free(pte)		free_pte_fast(pte)
+ 
++#include <asm-generic/pgalloc.h>
 +
- 	sprintf(dbf_text,"qib ac%2x",irq_ptr->qib.ac);
- 	QDIO_DBF_TEXT2(0,setup,dbf_text);
+ #endif /* _SPARC_PGALLOC_H */
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/asm-sparc64/pgalloc.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/asm-sparc64/pgalloc.h	2006-01-30 22:25:23.000000000 -0700
+@@ -304,4 +304,6 @@
  
-@@ -3026,11 +3218,10 @@
+ extern int do_check_pgt_cache(int, int);
  
- 	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED);
++#include <asm-generic/pgalloc.h>
++
+ #endif /* _SPARC64_PGALLOC_H */
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/fs.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/fs.h	2006-01-30 22:25:22.000000000 -0700
+@@ -735,6 +735,7 @@
+ #include <linux/usbdev_fs_sb.h>
+ #include <linux/cramfs_fs_sb.h>
+ #include <linux/jffs2_fs_sb.h>
++#include <linux/xip2_fs_sb.h>
  
-- out:
- 	if (irq_ptr) {
--		spin_unlock(&irq_ptr->setting_up_lock);
-+		up(&irq_ptr->setting_up_lock);
- 	}
-- out2:
-+ out:
- 	up(&init_sema);
+ extern struct list_head super_blocks;
+ extern spinlock_t sb_lock;
+@@ -794,6 +795,7 @@
+ 		struct usbdev_sb_info   usbdevfs_sb;
+ 		struct jffs2_sb_info	jffs2_sb;
+ 		struct cramfs_sb_info	cramfs_sb;
++		struct xip2_sb_info	xip2_sb;
+ 		void			*generic_sbp;
+ 	} u;
+ 	/*
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/genhd.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/genhd.h	2006-01-30 22:25:23.000000000 -0700
+@@ -103,6 +103,7 @@
  
- 	return result;
-@@ -3046,7 +3237,7 @@
- 	irq_ptr=qdio_get_irq_ptr(irq);
- 	if (!irq_ptr) return -ENODEV;
+ 	devfs_handle_t *de_arr;         /* one per physical disc */
+ 	char *flags;                    /* one per physical disc */
++	devfs_handle_t *label_arr;      /* one per physical disc */
+ };
  
--	spin_lock(&irq_ptr->setting_up_lock);
-+	down(&irq_ptr->setting_up_lock);
- 	if (irq_ptr->state==QDIO_IRQ_STATE_INACTIVE) {
- 		result=-EBUSY;
- 		goto out;
-@@ -3141,14 +3332,15 @@
- 	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ACTIVE);
+ /* drivers/block/genhd.c */
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/igmp.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/igmp.h	2006-01-30 22:25:22.000000000 -0700
+@@ -183,6 +183,9 @@
+ 	unsigned char		crcount;
+ };
  
-  out:
--	spin_unlock(&irq_ptr->setting_up_lock);
-+	up(&irq_ptr->setting_up_lock);
++extern int register_multicast_notifier(struct notifier_block *nb);
++extern int unregister_multicast_notifier(struct notifier_block *nb);
++
+ /* 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/inetdevice.h	2006-01-30 22:25:22.000000000 -0700
+@@ -28,6 +28,7 @@
+ };
  
- 	return result;
- }
+ extern struct ipv4_devconf ipv4_devconf;
++extern struct notifier_block *inetaddr_chain;
  
- /* buffers filled forwards again to make Rick happy */
--static void qdio_do_qdio_fill_input(qdio_q_t *q,unsigned int qidx,
--				    unsigned int count,qdio_buffer_t *buffers)
-+static inline void qdio_do_qdio_fill_input(qdio_q_t *q,unsigned int qidx,
-+	       				   unsigned int count,
-+					   qdio_buffer_t *buffers)
+ struct in_device
  {
- 	for (;;) {
- 		if (!q->is_0copy_sbals_q) {
-@@ -3281,7 +3473,7 @@
- 					qdio_kick_outbound_q(q);
- 				}
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/mii.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/mii.h	2006-01-30 22:25:23.000000000 -0700
+@@ -19,6 +19,7 @@
+ #define MII_ADVERTISE       0x04        /* Advertisement control reg   */
+ #define MII_LPA             0x05        /* Link partner ability reg    */
+ #define MII_EXPANSION       0x06        /* Expansion register          */
++#define MII_EXTSTATUS       0x0f        /* Extended status register    */
+ #define MII_DCOUNTER        0x12        /* Disconnect counter          */
+ #define MII_FCSCOUNTER      0x13        /* False carrier counter       */
+ #define MII_NWAYTEST        0x14        /* N-way auto-neg test reg     */
+@@ -32,7 +33,8 @@
+ #define MII_NCONFIG         0x1c        /* Network interface config    */
  
--				qdio_outbound_processing(q);
-+				__qdio_outbound_processing(q);
- 			} else {
- 				/* under VM, we do a SIGA sync
- 				 * unconditionally */
-@@ -3309,7 +3501,7 @@
- 				 * too long, the upper layer
- 				 * module could do a lot of
- 				 * traffic in that time */
--				qdio_outbound_processing(q);
-+				__qdio_outbound_processing(q);
- 			}
- 		}
+ /* Basic mode control register. */
+-#define BMCR_RESV               0x007f  /* Unused...                   */
++#define BMCR_RESV               0x003f  /* Unused...                   */
++#define BMCR_SPEED1000          0x0040  /* Select 1Gbps                */
+ #define BMCR_CTST               0x0080  /* Collision test              */
+ #define BMCR_FULLDPLX           0x0100  /* Full duplex                 */
+ #define BMCR_ANRESTART          0x0200  /* Auto negotiation restart    */
+@@ -50,13 +52,21 @@
+ #define BMSR_ANEGCAPABLE        0x0008  /* Able to do auto-negotiation */
+ #define BMSR_RFAULT             0x0010  /* Remote fault detected       */
+ #define BMSR_ANEGCOMPLETE       0x0020  /* Auto-negotiation complete   */
+-#define BMSR_RESV               0x07c0  /* Unused...                   */
++#define BMSR_EXTSTATUS          0x0100  /* extended status             */
++#define BMSR_RESV               0x06c0  /* Unused...                   */
+ #define BMSR_10HALF             0x0800  /* Can do 10mbps, half-duplex  */
+ #define BMSR_10FULL             0x1000  /* Can do 10mbps, full-duplex  */
+ #define BMSR_100HALF            0x2000  /* Can do 100mbps, half-duplex */
+ #define BMSR_100FULL            0x4000  /* Can do 100mbps, full-duplex */
+ #define BMSR_100BASE4           0x8000  /* Can do 100mbps, 4k packets  */
  
-@@ -3349,7 +3541,7 @@
- 		 perf_stats.siga_ins);
- 	_OUTP_IT("Number of SIGA out's issued                     : %u\n",
- 		 perf_stats.siga_outs);
--	_OUTP_IT("Number of PCI's caught                          : %u\n",
-+	_OUTP_IT("Number of PCIs caught                           : %u\n",
- 		 perf_stats.pcis);
- 	_OUTP_IT("Number of adapter interrupts caught             : %u\n",
- 		 perf_stats.thinints);
-@@ -3576,10 +3768,12 @@
++/* Extended status register */
++#define EXTSTATUS_RESV          0x0fff  /* Unused..                        */
++#define EXTSTATUS_1000THALF     0x1000  /* Can do 1000 BASE-T, half-duplex */
++#define EXTSTATUS_1000TFULL     0x2000  /* Can do 1000 BASE-T, full-duplex */
++#define EXTSTATUS_1000XHALF     0x4000  /* Can do 1000 BASE-X, half-duplex */
++#define EXTSTATUS_1000XFULL     0x8000  /* Can do 1000 BASE-X, full-duplex */
++
+ /* 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/mm.h	2006-01-30 22:25:23.000000000 -0700
+@@ -308,11 +308,9 @@
+ /* Make it prettier to test the above... */
+ #define UnlockPage(page)	unlock_page(page)
+ #define Page_Uptodate(page)	test_bit(PG_uptodate, &(page)->flags)
+-#define SetPageUptodate(page) \
+-	do {								\
+-		arch_set_page_uptodate(page);				\
+-		set_bit(PG_uptodate, &(page)->flags);			\
+-	} while (0)
++#ifndef SetPageUptodate
++#define SetPageUptodate(page)	set_bit(PG_uptodate, &(page)->flags);
++#endif
+ #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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/netdevice.h	2006-01-30 22:25:22.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 */
+ #define NETIF_F_VLAN_CHALLENGED	1024	/* Device cannot handle VLAN packets */
++#define NETIF_F_SHARED_IPV6	2048	/* make IPv6 address autogeneration
++					   network card instance aware */
  
- 	qdio_add_procfs_entry();
+ 	/* Called after device is detached from network. */
+ 	void			(*uninit)(struct net_device *dev);
+@@ -458,6 +460,9 @@
+ 	/* this will get initialized at each interface type init routine */
+ 	struct divert_blk	*divert;
+ #endif /* CONFIG_NET_DIVERT */
++#ifdef CONFIG_SHARED_IPV6_CARDS
++	unsigned short		dev_id;
++#endif /* CONFIG_SHARED_IPV6_CARDS */
+ };
  
--	hydra_thinints=qdio_check_for_hydra_thinints();
-+	qdio_check_for_machine_features();
+ /* 2.6 compatibility */
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/s390net.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/s390net.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,17 @@
++/*
++ * include/linux/s390net.h
++ *
++ * S390 and zSeries version
++ *         Copyright (C) 2003 IBM Corporation
++ *         Author(s): Erwin Rol <erwinrol at de.ibm.com>
++ *
++ */
++
++#ifndef LINUX_S390NET_H
++#define LINUX_S390NET_H
++
++#define S390NET_IOC_MAGIC 'Z'
++
++#endif /* !LINUX_S390NET_H */
++
++
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/linux/sched.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/sched.h	2006-01-30 22:25:23.000000000 -0700
+@@ -141,6 +141,9 @@
+ extern void cpu_init (void);
+ extern void trap_init(void);
+ extern void update_process_times(int user);
++#ifdef CONFIG_NO_IDLE_HZ
++extern void update_process_times_us(int user, int system);
++#endif
+ extern void update_one_process(struct task_struct *p, unsigned long user,
+ 			       unsigned long system, int cpu);
  
- 	sprintf(dbf_text,"hydrati%1x",hydra_thinints);
- 	QDIO_DBF_TEXT0(0,setup,dbf_text);
-+	sprintf(dbf_text,"omitsvs%1x",omit_svs);
-+	QDIO_DBF_TEXT0(0,setup,dbf_text);
+@@ -585,6 +588,9 @@
+ extern unsigned long itimer_next;
+ extern struct timeval xtime;
+ extern void do_timer(struct pt_regs *);
++#ifdef CONFIG_NO_IDLE_HZ
++extern void do_timer_ticks(int ticks);
++#endif
  
- 	tiqdio_register_thinints();
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/sysctl.h	2006-01-30 22:25:22.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 */
+ 	KERN_CADPID=54,		/* int: PID of the process to notify on CAD */
++	KERN_S390_HZ_TIMER=55,  /* int: hz timer on or off */
+  	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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/timer.h	2006-01-30 22:25:22.000000000 -0700
+@@ -23,6 +23,9 @@
  
-=== drivers/char/tty_io.c
-==================================================================
---- drivers/char/tty_io.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/char/tty_io.c   (/trunk/2.4.27)   (revision 52)
-@@ -145,8 +145,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);
-@@ -2110,8 +2112,63 @@
- #endif /* CONFIG_DEVFS_FS */
- }
+ extern void add_timer(struct timer_list * timer);
+ extern int del_timer(struct timer_list * timer);
++#ifdef CONFIG_NO_IDLE_HZ
++extern struct timer_list *next_timer_event(void);
++#endif
  
+ #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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/tty.h	2006-01-30 22:25:23.000000000 -0700
+@@ -383,6 +383,13 @@
+ extern void tty_register_devfs (struct tty_driver *driver, unsigned int flags,
+ 				unsigned minor);
+ extern void tty_unregister_devfs (struct tty_driver *driver, unsigned minor);
++struct devfs_entry;
++extern void tty_register_devfs_name (struct tty_driver *driver,
++				     unsigned int flags, unsigned minor,
++				     struct devfs_entry *dir, const char *name);
++extern void tty_unregister_devfs_name (struct tty_driver *driver,
++				       unsigned minor, struct devfs_entry *dir,
++				       const char *name);
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/xip2_fs.h	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,117 @@
 +/*
-+ * Register a tty device described by <driver>, with minor number <minor>,
-+ * device name <name> and in the /dev directory given by <dir>.
++ *  linux/include/linux/xip2_fs.h, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
 + */
-+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 */
-+}
++#ifndef _LINUX_XIP2_FS_H
++#define _LINUX_XIP2_FS_H
++#include <linux/ext2_fs.h>
 +
-+void tty_unregister_devfs_name (struct tty_driver *driver, unsigned minor,
-+				devfs_handle_t dir, const char *name)
-+{
-+#ifdef CONFIG_DEVFS_FS
-+	void * handle;
++/* bit operations */
++#define xip2_clear_bit ext2_clear_bit
++#define xip2_test_bit  ext2_test_bit
++#define xip2_set_bit   ext2_set_bit
++#define xip2_find_next_zero_bit ext2_find_next_zero_bit
++#define xip2_find_first_zero_bit ext2_find_first_zero_bit
 +
-+	handle = devfs_find_handle (dir, name, driver->major, minor,
-+				    DEVFS_SPECIAL_CHR, 0);
-+	devfs_unregister (handle);
-+#endif /* CONFIG_DEVFS_FS */
++/* the memory area structure */
++typedef struct _xip2_mem_area_t {
++	char* name; 
++	unsigned long start; 
++	unsigned long end;
++} xip2_mem_area_t;
++
++/*
++ * Debug code
++ */
++#undef XIP2FS_DEBUG
++
++#ifdef XIP2FS_DEBUG
++#define xip2_debug(f, a...)	{ \
++				printk ("XIP2-fs DEBUG (%s, %d): %s:", \
++						__FILE__, __LINE__, __FUNCTION__); \
++				  	printk (f, ## a); \
++					}
++#else
++#	define xip2_debug(f, a...)	/**/
++#endif
++
++/*
++ * Useful macros
++ */
++#ifdef __KERNEL__
++#define XIP2_SB(sb)	(&((sb)->u.xip2_sb))
++#else
++/* Assume that user mode programs are passing in an ext2fs superblock, not
++ * a kernel struct super_block.  This will allow us to call the feature-test
++ * macros from user land. */
++#define XIP2_SB(sb)	(sb)
++#endif
++
++
++/* functions defined in fs/xip2/balloc.c */
++extern int xip2_bg_has_super(struct super_block *sb, int group);
++extern unsigned long xip2_bg_num_gdb(struct super_block *sb, int group);
++extern unsigned long xip2_count_free_blocks (struct super_block *);
++extern void xip2_check_blocks_bitmap (struct super_block *);
++extern void* xip2_maread (xip2_mem_area_t* mem_area, int block, int size);
++
++/* dir.c */
++extern ino_t xip2_inode_by_name(struct inode *, struct dentry *);
++extern struct ext2_dir_entry_2 * xip2_find_entry (struct inode *,struct dentry *);
++
++/* functions defined in fs/xip2/ialloc.c */
++extern unsigned long xip2_count_free_inodes (struct super_block *);
++
++/* functions defined in fs/xip2/inode.c */
++extern void xip2_read_inode (struct inode *);
++extern int xip2_get_block(struct inode *inode, long iblock, 
++			  unsigned long* blockno_result, int create);
++
++/* functions defined in fs/xip2/ioctl.c */
++extern int xip2_ioctl (struct inode *, struct file *, unsigned int,
++		       unsigned long);
++
++/* functions defined in fs/xip2/super.c */
++extern void xip2_error (struct super_block *, const char *, const char *, ...)
++	__attribute__ ((format (printf, 3, 4)));
++extern NORET_TYPE void xip2_panic (struct super_block *, const char *,
++				   const char *, ...)
++	__attribute__ ((NORET_AND format (printf, 3, 4)));
++extern void xip2_warning (struct super_block *, const char *, const char *, ...)
++	__attribute__ ((format (printf, 3, 4)));
++extern void xip2_update_dynamic_rev (struct super_block *sb);
++extern void xip2_put_super (struct super_block *);
++extern int xip2_remount (struct super_block *, int *, char *);
++extern struct super_block * xip2_read_super (struct super_block *,void *,int);
++extern int xip2_statfs (struct super_block *, struct statfs *);
++
++/*
++ * Inodes and files operations
++ */
++
++/* dir.c */
++extern struct file_operations xip2_dir_operations;
++
++/* file.c */
++extern struct inode_operations xip2_file_inode_operations;
++extern struct file_operations xip2_file_operations;
++extern int xip2_file_mmap(struct file * file, struct vm_area_struct * vma);
++extern struct page * xip2_nopage_in_place(struct vm_area_struct * area, 
++					  unsigned long address, int unused);
++extern ssize_t xip2_file_read(struct file * filp, char * buf, size_t count, 
++			      loff_t *ppos);
++
++/* inode.c */
++extern struct address_space_operations xip2_aops;
++
++/* namei.c */
++extern struct inode_operations xip2_dir_inode_operations;
++
++/* symlink.c */
++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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/linux/xip2_fs_sb.h	2006-01-30 22:25:22.000000000 -0700
+@@ -0,0 +1,47 @@
++/*
++ *  linux/include/linux/xip2_fs_sb.h, Version 1
++ *
++ * (C) Copyright IBM Corp. 2002,2004
++ * Author(s): Carsten Otte <cotte at de.ibm.com>
++ * derived from second extended filesystem (ext2)
++ */
++
++#ifndef _LINUX_XIP2_FS_SB
++#define _LINUX_XIP2_FS_SB
++
++/*
++ * second extended-fs super-block data in memory
++ */
++struct xip2_sb_info {
++	unsigned long s_frag_size;	/* Size of a fragment in bytes */
++	unsigned long s_frags_per_block;/* Number of fragments per block */
++	unsigned long s_inodes_per_block;/* Number of inodes per block */
++	unsigned long s_frags_per_group;/* Number of fragments in a group */
++	unsigned long s_blocks_per_group;/* Number of blocks in a group */
++	unsigned long s_inodes_per_group;/* Number of inodes in a group */
++	unsigned long s_itb_per_group;	/* Number of inode table blocks per group */
++	unsigned long s_gdb_count;	/* Number of group descriptor blocks */
++	unsigned long s_desc_per_block;	/* Number of group descriptors per block */
++	unsigned long s_groups_count;	/* Number of groups in the fs */
++	void * s_sbp;	                /* Pointer to the super block */
++	struct ext2_super_block * s_es;	/* Pointer to the super block in buffer */
++	void ** s_group_desc;
++	unsigned short s_loaded_inode_bitmaps;
++	unsigned short s_loaded_block_bitmaps;
++	unsigned long s_inode_bitmap_number[EXT2_MAX_GROUP_LOADED];
++	void * s_inode_bitmap[EXT2_MAX_GROUP_LOADED];
++	unsigned long s_block_bitmap_number[EXT2_MAX_GROUP_LOADED];
++	void * s_block_bitmap[EXT2_MAX_GROUP_LOADED];
++	unsigned long  s_mount_opt;
++	uid_t s_resuid;
++	gid_t s_resgid;
++	unsigned short s_mount_state;
++	unsigned short s_pad;
++	int s_addr_per_block_bits;
++	int s_desc_per_block_bits;
++	int s_inode_size;
++	int s_first_ino;
++	void* mem_area;
++};
++
++#endif	/* _LINUX_XIP2_FS_SB */
+diff -urN kernel-source-2.4.27-2.4.27.orig/include/net/addrconf.h kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/net/addrconf.h	2006-01-30 22:25:22.000000000 -0700
+@@ -78,6 +78,9 @@
+ /*
+  *	multicast prototypes (mcast.c)
+  */
++extern int register_multicast6_notifier(struct notifier_block *nb);
++extern int unregister_multicast6_notifier(struct notifier_block *nb);
++
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/include/net/if_inet6.h	2006-01-30 22:25:22.000000000 -0700
+@@ -134,6 +134,8 @@
+ #define	IFA_SITE	IPV6_ADDR_SITELOCAL
+ #define	IFA_GLOBAL	0x0000U
+ 
++extern struct notifier_block *inet6addr_chain;
++
+ struct inet6_dev 
+ {
+ 	struct net_device		*dev;
+diff -urN kernel-source-2.4.27-2.4.27.orig/init/do_mounts.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/init/do_mounts.c	2006-01-30 22:25:23.000000000 -0700
+@@ -164,6 +164,27 @@
+ 	{ "dasdf", (DASD_MAJOR << MINORBITS) + (5 << 2) },
+ 	{ "dasdg", (DASD_MAJOR << MINORBITS) + (6 << 2) },
+ 	{ "dasdh", (DASD_MAJOR << MINORBITS) + (7 << 2) },
++	{ "dasdi", (DASD_MAJOR << MINORBITS) + (8 << 2) },
++	{ "dasdj", (DASD_MAJOR << MINORBITS) + (9 << 2) },
++	{ "dasdk", (DASD_MAJOR << MINORBITS) + (10 << 2) },
++	{ "dasdl", (DASD_MAJOR << MINORBITS) + (11 << 2) },
++	{ "dasdm", (DASD_MAJOR << MINORBITS) + (12 << 2) },
++	{ "dasdn", (DASD_MAJOR << MINORBITS) + (13 << 2) },
++	{ "dasdo", (DASD_MAJOR << MINORBITS) + (14 << 2) },
++	{ "dasdp", (DASD_MAJOR << MINORBITS) + (15 << 2) },
++	{ "dasdq", (DASD_MAJOR << MINORBITS) + (16 << 2) },
++	{ "dasdr", (DASD_MAJOR << MINORBITS) + (17 << 2) },
++	{ "dasds", (DASD_MAJOR << MINORBITS) + (18 << 2) },
++	{ "dasdt", (DASD_MAJOR << MINORBITS) + (19 << 2) },
++	{ "dasdu", (DASD_MAJOR << MINORBITS) + (20 << 2) },
++	{ "dasdv", (DASD_MAJOR << MINORBITS) + (21 << 2) },
++	{ "dasdw", (DASD_MAJOR << MINORBITS) + (22 << 2) },
++	{ "dasdx", (DASD_MAJOR << MINORBITS) + (23 << 2) },
++	{ "dasdy", (DASD_MAJOR << MINORBITS) + (24 << 2) },
++	{ "dasdz", (DASD_MAJOR << MINORBITS) + (25 << 2) },
++#endif
++#ifdef CONFIG_BLK_DEV_XPRAM
++	{ "xpram", (XPRAM_MAJOR << MINORBITS) },
+ #endif
+ 	{ "ida/c0d0p",0x4800 },
+ 	{ "ida/c0d1p",0x4810 },
+diff -urN kernel-source-2.4.27-2.4.27.orig/init/kerntypes.c kernel-source-2.4.27-2.4.27/init/kerntypes.c
+--- kernel-source-2.4.27-2.4.27.orig/init/kerntypes.c	1969-12-31 17:00:00.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/init/kerntypes.c	2006-01-30 22:25:23.000000000 -0700
+@@ -0,0 +1,49 @@
++/*
++ * kerntypes.c
++ *
++ * Dummy module that includes headers for all kernel types of interest.
++ * The kernel type information is used by the lcrash utility when
++ * analyzing system crash dumps or the live system. Using the type
++ * information for the running system, rather than kernel header files,
++ * makes for a more flexible and robust analysis tool.
++ *
++ * This source code is released under the GNU GPL.
++ */
++
++#ifndef __KERNEL__
++#define __KERNEL__
++#endif
++
++/* General linux types */
++
++#include <linux/autoconf.h>
++#include <linux/mm.h>
++#include <linux/config.h>
++#include <linux/utsname.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/compile.h>
++
++#ifdef CONFIG_ARCH_S390
++
++       /* s390 specific types */
++
++       #include <asm/lowcore.h>
++       #include <asm/debug.h>
++       #include <asm/irq.h>
++       #include <asm/io.h>
++
++       #if defined (CONFIG_DASD) || defined (CONFIG_DASD_MODULE)
++	       #include "../drivers/s390/block/dasd_int.h"
++       #endif /* CONFIG_DASD */
++
++       #if defined (CONFIG_QDIO) || defined (CONFIG_QDIO_MODULE)
++	       #include "asm/qdio.h"
++       #endif /* CONFIG_QDIO */
++
++#endif /* CONFIG_ARCH_S390 */
++
++void
++kerntypes_dummy(void)
++{
 +}
+diff -urN kernel-source-2.4.27-2.4.27.orig/init/version.c kernel-source-2.4.27-2.4.27/init/version.c
+--- kernel-source-2.4.27-2.4.27.orig/init/version.c	2001-11-09 15:11:15.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/init/version.c	2006-01-30 22:25:23.000000000 -0700
+@@ -10,6 +10,7 @@
+ #include <linux/utsname.h>
+ #include <linux/version.h>
+ #include <linux/compile.h>
++#include <linux/stringify.h>
+ 
+ #define version(a) Version_ ## a
+ #define version_string(a) version(a)
+@@ -24,3 +25,5 @@
+ const char *linux_banner = 
+ 	"Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
+ 	LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";
 +
-+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);
++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-source-2.4.27-2.4.27/kernel/ksyms.c
+--- kernel-source-2.4.27-2.4.27.orig/kernel/ksyms.c	2006-01-30 22:23:46.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/kernel/ksyms.c	2006-01-30 22:25:22.000000000 -0700
+@@ -619,6 +619,7 @@
+ EXPORT_SYMBOL(do_softirq);
+ EXPORT_SYMBOL(raise_softirq);
+ EXPORT_SYMBOL(cpu_raise_softirq);
++EXPORT_SYMBOL(open_softirq);
+ EXPORT_SYMBOL(__tasklet_schedule);
+ EXPORT_SYMBOL(__tasklet_hi_schedule);
  
- /*
-  * Called by a tty driver to register itself.
-@@ -2282,9 +2339,12 @@
- #ifdef CONFIG_TN3215
- 	con3215_init();
- #endif
--#ifdef CONFIG_HWC
--        hwc_console_init();
-+#ifdef CONFIG_SCLP_CONSOLE
-+        sclp_console_init();
+diff -urN kernel-source-2.4.27-2.4.27.orig/kernel/sysctl.c kernel-source-2.4.27-2.4.27/kernel/sysctl.c
+--- kernel-source-2.4.27-2.4.27.orig/kernel/sysctl.c	2006-01-30 22:23:49.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/kernel/sysctl.c	2006-01-30 22:25:22.000000000 -0700
+@@ -92,6 +92,9 @@
+ extern int sysctl_ieee_emulation_warnings;
  #endif
-+#ifdef CONFIG_SCLP_VT220_CONSOLE
-+        sclp_vt220_con_init();
+ extern int sysctl_userprocess_debug;
++#ifdef CONFIG_NO_IDLE_HZ
++extern int sysctl_hz_timer;
 +#endif
- #ifdef CONFIG_STDIO_CONSOLE
- 	stdio_console_init();
- #endif
-@@ -2454,9 +2514,12 @@
- #ifdef CONFIG_TN3215
- 	tty3215_init();
  #endif
--#ifdef CONFIG_HWC
--	hwc_tty_init();
-+#ifdef CONFIG_SCLP_TTY
-+	sclp_tty_init();
+ 
+ #ifdef CONFIG_PPC32
+@@ -274,6 +277,10 @@
+ 	{KERN_S390_USER_DEBUG_LOGGING,"userprocess_debug",
+ 	 &sysctl_userprocess_debug,sizeof(int),0644,NULL,&proc_dointvec},
  #endif
-+#ifdef CONFIG_SCLP_VT220_TTY
-+	sclp_vt220_tty_init();
++#ifdef CONFIG_NO_IDLE_HZ
++	{KERN_S390_HZ_TIMER,"hz_timer",
++	 &sysctl_hz_timer,sizeof(int),0644,NULL,&proc_dointvec},
 +#endif
- #ifdef CONFIG_A2232
- 	a2232board_init();
- #endif
-=== drivers/block/Makefile
-==================================================================
---- drivers/block/Makefile   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/block/Makefile   (/trunk/2.4.27)   (revision 52)
-@@ -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
- 
-=== drivers/block/elevator.c
-==================================================================
---- drivers/block/elevator.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/block/elevator.c   (/trunk/2.4.27)   (revision 52)
-@@ -219,3 +219,9 @@
- 	*elevator = type;
- 	elevator->queue_ID = queue_ID++;
+ #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-source-2.4.27-2.4.27/kernel/timer.c
+--- kernel-source-2.4.27-2.4.27.orig/kernel/timer.c	2002-11-28 16:53:15.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/kernel/timer.c	2006-01-30 22:25:22.000000000 -0700
+@@ -339,6 +339,64 @@
+ 	spin_unlock_irq(&timerlist_lock);
  }
-+
-+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);
-=== drivers/scsi/scsi_lib.c
-==================================================================
---- drivers/scsi/scsi_lib.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/scsi/scsi_lib.c   (/trunk/2.4.27)   (revision 52)
-@@ -256,12 +256,32 @@
- 	if (SCpnt != NULL) {
  
- 		/*
-+		 * This is a work around for a case where this Scsi_Cmnd
-+		 * may have been through the busy retry paths already. We
-+		 * clear the special flag and try to restore the
-+		 * read/write request cmd value.
-+		 */
-+		if (SCpnt->request.cmd == SPECIAL)
-+			SCpnt->request.cmd = 
-+				(SCpnt->sc_data_direction ==
-+				 SCSI_DATA_WRITE) ? WRITE : READ;
++#ifdef CONFIG_NO_IDLE_HZ
++/*
++ * Find out when the next timer event is due to happen. This
++ * is used on S/390 to stop all activity when all cpus are idle.
++ * The timerlist_lock must be acquired before calling this function.
++ */
++struct timer_list *next_timer_event(void)
++{
++	struct timer_list *nte, *tmp;
++	struct list_head *lst;
++	int i, j;
 +
-+		/*
- 		 * For some reason, we are not done with this request.
- 		 * This happens for I/O errors in the middle of the request,
- 		 * in which case we need to request the blocks that come after
- 		 * the bad sector.
- 		 */
- 		SCpnt->request.special = (void *) SCpnt;
-+                /*
-+                 * We need to recount the number of
-+                 * scatter-gather segments here - the
-+                 * normal case code assumes this to be
-+                 * correct, as it would be a performance
-+                 * loss to always recount.  Handling
-+                 * errors is always unusual, of course.
-+                 */
-+                recount_segments(SCpnt);
- 		list_add(&SCpnt->request.queue, &q->queue_head);
- 	}
- 
-=== drivers/scsi/scsi_proc.c
-==================================================================
---- drivers/scsi/scsi_proc.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/scsi/scsi_proc.c   (/trunk/2.4.27)   (revision 52)
-@@ -120,35 +120,34 @@
- 	return(ret);
- }
- 
--void build_proc_dir_entries(Scsi_Host_Template * tpnt)
--{
--	struct Scsi_Host *hpnt;
--	char name[10];	/* see scsi_unregister_host() */
-+void build_proc_dir_entry(struct Scsi_Host *shpnt) {
-+	char name[10]; /* host_no>=10^9? I don't think so. */
-+	struct proc_dir_entry *p;
- 
--	tpnt->proc_dir = proc_mkdir(tpnt->proc_name, proc_scsi);
--        if (!tpnt->proc_dir) {
--                printk(KERN_ERR "Unable to proc_mkdir in scsi.c/build_proc_dir_entries");
--                return;
--        }
--	tpnt->proc_dir->owner = tpnt->module;
-+	if(shpnt->hostt->proc_dir) {
-+		sprintf(name, "%d", shpnt->host_no);
-+		p = create_proc_read_entry(
-+			name,
-+			S_IFREG | S_IRUGO | S_IWUSR,
-+			shpnt->hostt->proc_dir,
-+			proc_scsi_read,
-+			(void *) shpnt
-+		);
-+		if (!p)
-+			panic("Not enough memory to register SCSI HBA in /proc/scsi !\n");
-+		p->write_proc=proc_scsi_write;
-+		p->owner = shpnt->hostt->module;
++	/* Look for the next timer event in tv1. */
++	i = 0;
++	j = tvecs[0]->index;
++	do {
++		struct list_head *head = tvecs[0]->vec + j;
++		if (!list_empty(head)) {
++			nte = list_entry(head->next, struct timer_list, list);
++			goto found;
++		}
++		j = (j + 1) & TVR_MASK;
++	} while (j != tv1.index);
++
++	/* No event found in tv1. Check tv2-tv5. */
++	for (i = 1; i < NOOF_TVECS; i++) {
++		j = tvecs[i]->index;
++		do {
++			nte = NULL;
++			list_for_each(lst, tvecs[i]->vec + j) {
++				tmp = list_entry(lst, struct timer_list, list);
++				if (nte == NULL ||
++				    time_before(tmp->expires, nte->expires))
++					nte = tmp;
++			}
++			if (nte)
++				goto found;
++			j = (j + 1) & TVN_MASK;
++		} while (j != tvecs[i]->index);
 +	}
++	return NULL;
++found:
++	/* Found timer event in tvecs[i]->vec[j] */
++	if (j < tvecs[i]->index && i < NOOF_TVECS-1) {
++		/* 
++		 * The search wrapped. We need to look at the next list
++		 * from tvecs[i+1] that would cascade into tvecs[i].
++		 */
++		list_for_each(lst, tvecs[i+1]->vec+tvecs[i+1]->index) {
++			tmp = list_entry(lst, struct timer_list, list);
++			if (time_before(tmp->expires, nte->expires))
++				nte = tmp;
++		}
++	}
++	return nte;
 +}
++#endif
++
+ spinlock_t tqueue_lock = SPIN_LOCK_UNLOCKED;
  
--	hpnt = scsi_hostlist;
--	while (hpnt) {
--		if (tpnt == hpnt->hostt) {
--			struct proc_dir_entry *p;
--			sprintf(name,"%d",hpnt->host_no);
--			p = create_proc_read_entry(name,
--					S_IFREG | S_IRUGO | S_IWUSR,
--					tpnt->proc_dir,
--					proc_scsi_read,
--					(void *)hpnt);
--			if (!p)
--				panic("Not enough memory to register SCSI HBA in /proc/scsi !\n");
--			p->write_proc=proc_scsi_write;
--			p->owner = tpnt->module;
--		}
--		hpnt = hpnt->next;
-+void build_proc_dir(Scsi_Host_Template * tpnt)
-+{
-+	tpnt->proc_dir = proc_mkdir(tpnt->proc_name, proc_scsi);
-+	if (!tpnt->proc_dir) {
-+		printk(KERN_ERR "Unable to proc_mkdir in scsi.c/build_proc_dir_entries");
-+		return;
- 	}
-+	tpnt->proc_dir->owner = tpnt->module;
+ void tqueue_bh(void)
+@@ -527,7 +585,7 @@
+ 		update_wall_time_one_tick();
+ 	} while (ticks);
+ 
+-	if (xtime.tv_usec >= 1000000) {
++	while (xtime.tv_usec >= 1000000) {
+ 	    xtime.tv_usec -= 1000000;
+ 	    xtime.tv_sec++;
+ 	    second_overflow();
+@@ -620,6 +678,31 @@
  }
  
  /*
-=== drivers/scsi/scsi.c
-==================================================================
---- drivers/scsi/scsi.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/scsi/scsi.c   (/trunk/2.4.27)   (revision 52)
-@@ -537,23 +537,11 @@
- 				   SCpnt->target,
- 				   atomic_read(&SCpnt->host->host_active),
- 				   SCpnt->host->host_failed));
--	if (SCpnt->host->host_failed != 0) {
--		SCSI_LOG_ERROR_RECOVERY(5, printk("Error handler thread %d %d\n",
--						SCpnt->host->in_recovery,
--						SCpnt->host->eh_active));
--	}
--	/*
--	 * If the host is having troubles, then look to see if this was the last
--	 * command that might have failed.  If so, wake up the error handler.
--	 */
--	if (SCpnt->host->in_recovery
--	    && !SCpnt->host->eh_active
--	    && SCpnt->host->host_busy == SCpnt->host->host_failed) {
--		SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n",
--			     atomic_read(&SCpnt->host->eh_wait->count)));
--		up(SCpnt->host->eh_wait);
--	}
- 
-+        /* Note: The eh_thread is now started in scsi_bottom_half_handler for 
-+         * all cases except command timeout
-+         */
++ * Called from the timer interrupt handler to charge a couple of ticks
++ * to the current process.
++ */
++void update_process_times_us(int user_ticks, int system_ticks)
++{
++	struct task_struct *p = current;
++	int cpu = smp_processor_id();
 +
- 	spin_unlock_irqrestore(&device_request_lock, flags);
++	update_one_process(p, user_ticks, system_ticks, cpu);
++	if (p->pid) {
++		p->counter -= user_ticks + system_ticks;
++		if (p->counter <= 0) {
++			p->counter = 0;
++			p->need_resched = 1;
++		}
++		if (p->nice > 0)
++			kstat.per_cpu_nice[cpu] += user_ticks;
++		else
++			kstat.per_cpu_user[cpu] += user_ticks;
++		kstat.per_cpu_system[cpu] += system_ticks;
++	} else if (local_bh_count(cpu) || local_irq_count(cpu) > 1)
++		kstat.per_cpu_system[cpu] += system_ticks;
++}
++
++/*
+  * Nr of active tasks - counted in fixed-point numbers
+  */
+ static unsigned long count_active_tasks(void)
+@@ -651,7 +734,7 @@
+ 	static int count = LOAD_FREQ;
  
-         /*
-@@ -1300,26 +1288,38 @@
- 					SCpnt->owner = SCSI_OWNER_ERROR_HANDLER;
- 					SCpnt->state = SCSI_STATE_FAILED;
- 					SCpnt->host->in_recovery = 1;
--					/*
--					 * If the host is having troubles, then look to see if this was the last
--					 * command that might have failed.  If so, wake up the error handler.
--					 */
--					if (SCpnt->host->host_busy == SCpnt->host->host_failed) {
--						SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n",
--										  atomic_read(&SCpnt->host->eh_wait->count)));
--						up(SCpnt->host->eh_wait);
--					}
--				} else {
--					/*
--					 * We only get here if the error recovery thread has died.
--					 */
-+                                } else {
-+                                        /* eh not present....trying to continue anyway */
- 					scsi_finish_command(SCpnt);
--				}
-+                                }
-+                                break;
-+                        } // switch
-+                        if (SCpnt->host->eh_wait != NULL) {
-+                                /*
-+                                 * If the host is having troubles, then look to see if this was the last
-+                                 * command that might have failed.  If so, wake up the error handler.
-+                                 */
-+                                if (SCpnt->host->in_recovery && 
-+                                    !SCpnt->host->eh_active &&
-+                                    (SCpnt->host->host_busy == SCpnt->host->host_failed)) {
-+                                        SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread (%d)\n",
-+                                                                          atomic_read(&SCpnt->host->eh_wait->count)));
-+                                        printk("(in_recovery=%d, host_busy=%d, host_failed=%d) "
-+                                               "Waking error handler thread bh(%d)\n",
-+                                               SCpnt->host->in_recovery,
-+                                               SCpnt->host->host_busy,
-+                                               SCpnt->host->host_failed,
-+                                               atomic_read(&SCpnt->host->eh_wait->count));
-+                                        up(SCpnt->host->eh_wait);
-+                                }
-+                        } else {
-+                                SCSI_LOG_ERROR_RECOVERY(5, printk("Warning: eh_thread not present\n"));
- 			}
-+                        
- 		}		/* for(; SCpnt...) */
--
-+                
- 	}			/* while(1==1) */
--
-+        
+ 	count -= ticks;
+-	if (count < 0) {
++	while (count < 0) {
+ 		count += LOAD_FREQ;
+ 		active_tasks = count_active_tasks();
+ 		CALC_LOAD(avenrun[0], EXP_1, active_tasks);
+@@ -709,6 +792,14 @@
+ 		mark_bh(TQUEUE_BH);
  }
  
- /*
-@@ -1868,7 +1868,6 @@
- 	struct Scsi_Host *shpnt;
- 	Scsi_Device *SDpnt;
- 	struct Scsi_Device_Template *sdtpnt;
--	const char *name;
- 	unsigned long flags;
- 	int out_of_space = 0;
++void do_timer_ticks(int ticks)
++{
++	(*(unsigned long *)&jiffies) += ticks;
++	mark_bh(TIMER_BH);
++	if (TQ_ACTIVE(tq_timer))
++		mark_bh(TQUEUE_BH);
++}
++
+ #if !defined(__alpha__) && !defined(__ia64__)
  
-@@ -1895,11 +1894,18 @@
+ /*
+diff -urN kernel-source-2.4.27-2.4.27.orig/mm/filemap.c kernel-source-2.4.27-2.4.27/mm/filemap.c
+--- kernel-source-2.4.27-2.4.27.orig/mm/filemap.c	2006-01-30 22:23:48.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/mm/filemap.c	2006-01-30 22:25: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..
+  */
+-void mark_page_accessed(struct page *page)
++inline void mark_page_accessed(struct page *page)
+ {
+ 	if (!PageActive(page) && PageReferenced(page)) {
+ 		activate_page(page);
+@@ -2212,8 +2212,8 @@
  
- 	if (tpnt->use_new_eh_code) {
- 		spin_lock_irqsave(&io_request_lock, flags);
--		tpnt->present = tpnt->detect(tpnt);
-+		tpnt->detect(tpnt);
- 		spin_unlock_irqrestore(&io_request_lock, flags);
--	} else
--		tpnt->present = tpnt->detect(tpnt);
-+	} else	tpnt->detect(tpnt);
+ 	if (pte_present(pte)) {
+ 		struct page *page = pte_page(pte);
+-		if (VALID_PAGE(page) && !PageReserved(page) && ptep_test_and_clear_dirty(ptep)) {
+-			flush_tlb_page(vma, address);
++		if (VALID_PAGE(page) && !PageReserved(page) &&
++                    ptep_test_and_clear_and_flush_dirty(vma, address, ptep)) {
+ 			set_page_dirty(page);
+ 		}
+ 	}
+diff -urN kernel-source-2.4.27-2.4.27.orig/mm/memory.c kernel-source-2.4.27-2.4.27/mm/memory.c
+--- kernel-source-2.4.27-2.4.27.orig/mm/memory.c	2006-01-30 22:23:48.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/mm/memory.c	2006-01-30 22:25:24.000000000 -0700
+@@ -163,6 +163,86 @@
+ #define PMD_TABLE_MASK	((PTRS_PER_PMD-1) * sizeof(pmd_t))
  
-+	/* Add the new driver to /proc/scsi (directory only) */
-+#ifdef CONFIG_PROC_FS
-+	build_proc_dir(tpnt);
+ /*
++ * Allocate page middle directory.
++ *
++ * We've already handled the fast-path in-line, and we own the
++ * page table lock.
++ *
++ * On a two-level page table, this ends up actually being entirely
++ * optimized away.
++ */
++pmd_t *__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
++{
++	pmd_t *new;
++
++	/* "fast" allocation can happen without dropping the lock.. */
++	new = pmd_alloc_one_fast(mm, address);
++	if (!new) {
++		spin_unlock(&mm->page_table_lock);
++		new = pmd_alloc_one(mm, address);
++		spin_lock(&mm->page_table_lock);
++		if (!new)
++			return NULL;
++
++		/*
++		 * Because we dropped the lock, we should re-check the
++		 * entry, as somebody else could have populated it..
++		 */
++		if (!pgd_none(*pgd)) {
++			pmd_free(new);
++			check_pgt_cache();
++			goto out;
++		}
++	}
++#if defined(CONFIG_ARCH_S390X)
++	new = pgd_populate(mm, pgd, new);
++	if (!new)
++		return NULL;
++#else
++	pgd_populate(mm, pgd, new);
 +#endif
++out:
++	return pmd_offset(pgd, address);
++}
 +
-+	tpnt->next = scsi_hosts;	/* Add to the linked list */
-+	scsi_hosts = tpnt;
++/*
++ * Allocate the page table directory.
++ *
++ * We've already handled the fast-path in-line, and we own the
++ * page table lock.
++ */
++inline pte_t *
++pte_alloc(struct mm_struct *mm, pmd_t *pmd, unsigned long address)
++{
++	if (pmd_none(*pmd)) {
++		pte_t *new;
 +
- 	if (tpnt->present) {
- 		if (pcount == next_scsi_host) {
- 			if (tpnt->present > 1) {
-@@ -1918,48 +1924,7 @@
- 				return 1;
- 			}
++		/* "fast" allocation can happen without dropping the lock.. */
++		new = pte_alloc_one_fast(mm, address);
++		if (!new) {
++			spin_unlock(&mm->page_table_lock);
++			new = pte_alloc_one(mm, address);
++			spin_lock(&mm->page_table_lock);
++			if (!new)
++				return NULL;
++
++			/*
++			 * Because we dropped the lock, we should re-check the
++			 * entry, as somebody else could have populated it..
++			 */
++			if (!pmd_none(*pmd)) {
++				pte_free(new);
++				check_pgt_cache();
++				goto out;
++			}
++		}
++		pmd_populate(mm, pmd, new);
++	}
++out:
++	return pte_offset(pmd, address);
++}
++
++/*
+  * copy one vm_area from one task to the other. Assumes the page tables
+  * already present in the new task to be cleared in the whole range
+  * covered by this vma.
+@@ -422,9 +502,14 @@
+ 
+ 	pte = *ptep;
+ 	if (pte_present(pte)) {
+-		if (!write ||
+-		    (pte_write(pte) && pte_dirty(pte)))
+-			return pte_page(pte);
++		struct page * page = pte_page(pte);
++		if (!write)
++			return page;
++		if (pte_write(pte)) {
++			if (!pte_dirty(pte) && !PageDirty(page))
++				set_page_dirty(page);
++			return page;
++		}
+ 	}
+ 
+ out:
+@@ -901,20 +986,6 @@
+ 	return error;
+ }
+ 
+-/*
+- * Establish a new mapping:
+- *  - flush the old one
+- *  - update the page tables
+- *  - inform the TLB about the new one
+- *
+- * We hold the mm semaphore for reading and vma->vm_mm->page_table_lock
+- */
+-static inline void establish_pte(struct vm_area_struct * vma, unsigned long address, pte_t *page_table, pte_t entry)
+-{
+-	set_pte(page_table, entry);
+-	flush_tlb_page(vma, address);
+-	update_mmu_cache(vma, address, entry);
+-}
+ 
+ /*
+  * We hold the mm semaphore for reading and vma->vm_mm->page_table_lock
+@@ -924,7 +995,8 @@
+ {
+ 	flush_page_to_ram(new_page);
+ 	flush_cache_page(vma, address);
+-	establish_pte(vma, address, page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
++	ptep_establish(vma, address, page_table,
++		pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
+ }
+ 
+ /*
+@@ -961,7 +1033,8 @@
+ 		unlock_page(old_page);
+ 		if (reuse) {
+ 			flush_cache_page(vma, address);
+-			establish_pte(vma, address, page_table, pte_mkyoung(pte_mkdirty(pte_mkwrite(pte))));
++			ptep_establish(vma, address, page_table,
++				pte_mkyoung(pte_mkdirty(pte_mkwrite(pte))));
+ 			spin_unlock(&mm->page_table_lock);
+ 			return 1;	/* Minor fault */
  		}
--		tpnt->next = scsi_hosts;	/* Add to the linked list */
--		scsi_hosts = tpnt;
+@@ -1356,7 +1429,7 @@
+ 		entry = pte_mkdirty(entry);
+ 	}
+ 	entry = pte_mkyoung(entry);
+-	establish_pte(vma, address, pte, entry);
++	ptep_establish(vma, address, pte, entry);
+ 	spin_unlock(&mm->page_table_lock);
+ 	return 1;
+ }
+@@ -1389,79 +1462,6 @@
+ 	return -1;
+ }
  
--		/* Add the new driver to /proc/scsi */
--#ifdef CONFIG_PROC_FS
--		build_proc_dir_entries(tpnt);
--#endif
+-/*
+- * Allocate page middle directory.
+- *
+- * We've already handled the fast-path in-line, and we own the
+- * page table lock.
+- *
+- * On a two-level page table, this ends up actually being entirely
+- * optimized away.
+- */
+-pmd_t *__pmd_alloc(struct mm_struct *mm, pgd_t *pgd, unsigned long address)
+-{
+-	pmd_t *new;
 -
+-	/* "fast" allocation can happen without dropping the lock.. */
+-	new = pmd_alloc_one_fast(mm, address);
+-	if (!new) {
+-		spin_unlock(&mm->page_table_lock);
+-		new = pmd_alloc_one(mm, address);
+-		spin_lock(&mm->page_table_lock);
+-		if (!new)
+-			return NULL;
 -
 -		/*
--		 * Add the kernel threads for each host adapter that will
--		 * handle error correction.
+-		 * Because we dropped the lock, we should re-check the
+-		 * entry, as somebody else could have populated it..
 -		 */
--		for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
--			if (shpnt->hostt == tpnt && shpnt->hostt->use_new_eh_code) {
--				DECLARE_MUTEX_LOCKED(sem);
+-		if (!pgd_none(*pgd)) {
+-			pmd_free(new);
+-			check_pgt_cache();
+-			goto out;
+-		}
+-	}
+-	pgd_populate(mm, pgd, new);
+-out:
+-	return pmd_offset(pgd, address);
+-}
 -
--				shpnt->eh_notify = &sem;
--				kernel_thread((int (*)(void *)) scsi_error_handler,
--					      (void *) shpnt, 0);
+-/*
+- * Allocate the page table directory.
+- *
+- * We've already handled the fast-path in-line, and we own the
+- * page table lock.
+- */
+-pte_t *pte_alloc(struct mm_struct *mm, pmd_t *pmd, unsigned long address)
+-{
+-	if (pmd_none(*pmd)) {
+-		pte_t *new;
 -
--				/*
--				 * Now wait for the kernel error thread to initialize itself
--				 * as it might be needed when we scan the bus.
--				 */
--				down(&sem);
--				shpnt->eh_notify = NULL;
--			}
--		}
+-		/* "fast" allocation can happen without dropping the lock.. */
+-		new = pte_alloc_one_fast(mm, address);
+-		if (!new) {
+-			spin_unlock(&mm->page_table_lock);
+-			new = pte_alloc_one(mm, address);
+-			spin_lock(&mm->page_table_lock);
+-			if (!new)
+-				return NULL;
 -
--		for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
--			if (shpnt->hostt == tpnt) {
--				if (tpnt->info) {
--					name = tpnt->info(shpnt);
--				} else {
--					name = tpnt->name;
--				}
--				printk(KERN_INFO "scsi%d : %s\n",		/* And print a little message */
--				       shpnt->host_no, name);
+-			/*
+-			 * Because we dropped the lock, we should re-check the
+-			 * entry, as somebody else could have populated it..
+-			 */
+-			if (!pmd_none(*pmd)) {
+-				pte_free(new);
+-				check_pgt_cache();
+-				goto out;
 -			}
 -		}
+-		pmd_populate(mm, pmd, new);
+-	}
+-out:
+-	return pte_offset(pmd, address);
+-}
 -
- 		/* The next step is to call scan_scsis here.  This generates the
- 		 * Scsi_Devices entries
- 		 */
-@@ -2036,7 +2001,6 @@
- 	struct Scsi_Device_Template *sdtpnt;
- 	struct Scsi_Host *sh1;
- 	struct Scsi_Host *shpnt;
--	char name[10];	/* host_no>=10^9? I don't think so. */
- 
- 	/* get the big kernel lock, so we don't race with open() */
- 	lock_kernel();
-@@ -2138,19 +2102,7 @@
- 	/*
- 	 * Next, kill the kernel error recovery thread for this host.
+ 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 kernel-source-2.4.27-2.4.27/mm/vmscan.c
+--- kernel-source-2.4.27-2.4.27.orig/mm/vmscan.c	2006-01-30 22:23:45.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/mm/vmscan.c	2006-01-30 22:25:24.000000000 -0700
+@@ -145,9 +145,7 @@
+ 	 * is needed on CPUs which update the accessed and dirty
+ 	 * bits in hardware.
  	 */
--	for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
--		if (shpnt->hostt == tpnt
--		    && shpnt->hostt->use_new_eh_code
--		    && shpnt->ehandler != NULL) {
--			DECLARE_MUTEX_LOCKED(sem);
+-	flush_cache_page(vma, address);
+-	pte = ptep_get_and_clear(page_table);
+-	flush_tlb_page(vma, address);
++	pte = ptep_invalidate(vma, address, page_table);
  
--			shpnt->eh_notify = &sem;
--			send_sig(SIGHUP, shpnt->ehandler, 1);
--			down(&sem);
--			shpnt->eh_notify = NULL;
--		}
--	}
--
- 	/* Next we free up the Scsi_Cmnd structures for this host */
+ 	if (pte_dirty(pte))
+ 		set_page_dirty(page);
+diff -urN kernel-source-2.4.27-2.4.27.orig/net/802/tr.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/net/802/tr.c	2006-01-30 22:25:23.000000000 -0700
+@@ -327,9 +327,9 @@
+ 	int i;
+ 	unsigned int hash, rii_p = 0;
+ 	rif_cache entry;
+-	unsigned long flags;
  
- 	for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
-@@ -2178,9 +2130,6 @@
- 		if (shpnt->hostt != tpnt)
- 			continue;
- 		pcount = next_scsi_host;
--		/* Remove the /proc/scsi directory entry */
--		sprintf(name,"%d",shpnt->host_no);
--		remove_proc_entry(name, tpnt->proc_dir);
- 		if (tpnt->release)
- 			(*tpnt->release) (shpnt);
- 		else {
-@@ -2197,7 +2146,6 @@
+-	spin_lock_irqsave(&rif_lock, flags);
++
++	spin_lock_bh(&rif_lock);
+ 	
+ 	/*
+ 	 *	Firstly see if the entry exists
+@@ -368,7 +368,7 @@
+ 		if(!entry) 
+ 		{
+ 			printk(KERN_DEBUG "tr.c: Couldn't malloc rif cache entry !\n");
+-			spin_unlock_irqrestore(&rif_lock,flags);
++			spin_unlock_bh(&rif_lock);
+ 			return;
  		}
- 		if (pcount == next_scsi_host)
- 			scsi_unregister(shpnt);
--		tpnt->present--;
- 	}
  
- 	/*
-=== drivers/scsi/scsi.h
-==================================================================
---- drivers/scsi/scsi.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/scsi/scsi.h   (/trunk/2.4.27)   (revision 52)
-@@ -18,6 +18,7 @@
- #include <linux/config.h>	/* for CONFIG_SCSI_LOGGING */
- #include <linux/devfs_fs_kernel.h>
- #include <linux/proc_fs.h>
-+#include <linux/blkdev.h>
+@@ -410,7 +410,7 @@
+ 		    }                                         
+            	entry->last_used=jiffies;               
+ 	}
+-	spin_unlock_irqrestore(&rif_lock,flags);
++	spin_unlock_bh(&rif_lock);
+ }
  
  /*
-  * Some of the public constants are being moved to this file.
-=== drivers/scsi/scsi_queue.c
-==================================================================
---- drivers/scsi/scsi_queue.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/scsi/scsi_queue.c   (/trunk/2.4.27)   (revision 52)
-@@ -103,7 +103,7 @@
- 		 * If a host is inactive and cannot queue any commands, I don't see
- 		 * how things could possibly work anyways.
- 		 */
--		if (host->host_busy == 0) {
-+		if (host->host_busy == 1) {
- 			if (scsi_retry_command(cmd) == 0) {
- 				return 0;
- 			}
-@@ -118,7 +118,7 @@
- 		 * If a host is inactive and cannot queue any commands, I don't see
- 		 * how things could possibly work anyways.
- 		 */
--		if (cmd->device->device_busy == 0) {
-+		if (cmd->device->device_busy == 1) {
- 			if (scsi_retry_command(cmd) == 0) {
- 				return 0;
- 			}
-=== drivers/scsi/Config.in
-==================================================================
---- drivers/scsi/Config.in   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/scsi/Config.in   (/trunk/2.4.27)   (revision 52)
-@@ -43,6 +43,8 @@
- if [ "$CONFIG_PCI" = "y" ]; then
-    dep_tristate '3ware Hardware ATA-RAID support' CONFIG_BLK_DEV_3W_XXXX_RAID $CONFIG_SCSI
- fi
-+
-+if [ "$CONFIG_ARCH_S390" != "y" ]; then
- dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
- dep_tristate 'ACARD SCSI support' CONFIG_SCSI_ACARD $CONFIG_SCSI
- dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
-@@ -262,6 +264,13 @@
+diff -urN kernel-source-2.4.27-2.4.27.orig/net/8021q/vlan.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/net/8021q/vlan.c	2006-01-30 22:25:23.000000000 -0700
+@@ -444,6 +444,10 @@
+ 	/* IFF_BROADCAST|IFF_MULTICAST; ??? */
+ 	new_dev->flags = real_dev->flags;
+ 	new_dev->flags &= ~IFF_UP;
++#ifdef CONFIG_SHARED_IPV6_CARDS
++	new_dev->features |= (real_dev->features & NETIF_F_SHARED_IPV6);
++	new_dev->dev_id = real_dev->dev_id;
++#endif
+ 
+ 	/* Make this thing known as a VLAN device */
+ 	new_dev->priv_flags |= IFF_802_1Q_VLAN;
+@@ -482,16 +486,16 @@
+ 	new_dev->stop = vlan_dev_stop;
+ 
+ 	if (real_dev->features & NETIF_F_HW_VLAN_TX) {
+-		new_dev->hard_header = real_dev->hard_header;
++		new_dev->hard_header     = real_dev->hard_header;
+ 		new_dev->hard_start_xmit = vlan_dev_hwaccel_hard_start_xmit;
+-		new_dev->rebuild_header = real_dev->rebuild_header;
++		new_dev->rebuild_header  = real_dev->rebuild_header;
+ 	} else {
+-		new_dev->hard_header = vlan_dev_hard_header;
++		new_dev->hard_header     = vlan_dev_hard_header;
+ 		new_dev->hard_start_xmit = vlan_dev_hard_start_xmit;
+-		new_dev->rebuild_header = vlan_dev_rebuild_header;
++		new_dev->rebuild_header  = vlan_dev_rebuild_header;
+ 	}
+-	new_dev->hard_header_parse = real_dev->hard_header_parse;
+-	new_dev->set_mac_address = vlan_dev_set_mac_address;
++	new_dev->hard_header_parse  = real_dev->hard_header_parse;
++	new_dev->set_mac_address    = vlan_dev_set_mac_address;
+ 	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 kernel-source-2.4.27-2.4.27/net/Config.in
+--- kernel-source-2.4.27-2.4.27.orig/net/Config.in	2006-01-30 22:23:44.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/net/Config.in	2006-01-30 22:25:23.000000000 -0700
+@@ -26,6 +26,7 @@
+       if [ "$CONFIG_IPV6" != "n" ]; then
+ 	 source net/ipv6/Config.in
+       fi
++      bool '  Prepare net_device struct for shared IPv6 cards' CONFIG_SHARED_IPV6_CARDS
     fi
- 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/net/core/dev.c	2006-01-30 22:25:23.000000000 -0700
+@@ -104,6 +104,7 @@
+ #include <linux/wireless.h>		/* Note : will define WIRELESS_EXT */
+ #include <net/iw_handler.h>
+ #endif	/* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
++#include <linux/s390net.h>
+ #ifdef CONFIG_PLIP
+ extern int plip_init(void);
+ #endif
+@@ -2201,6 +2202,7 @@
+ 		default:
+ 			if ((cmd >= SIOCDEVPRIVATE &&
+ 			    cmd <= SIOCDEVPRIVATE + 15) ||
++			    _IOC_TYPE(cmd) == S390NET_IOC_MAGIC ||
+ 			    cmd == SIOCBONDENSLAVE ||
+ 			    cmd == SIOCBONDRELEASE ||
+ 			    cmd == SIOCBONDSETHWADDR ||
+@@ -2390,6 +2392,7 @@
+ 		 
+ 		default:
+ 			if (cmd == SIOCWANDEV ||
++			    _IOC_TYPE(cmd) == S390NET_IOC_MAGIC ||
+ 			    (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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/net/ipv4/igmp.c	2006-01-30 22:25:23.000000000 -0700
+@@ -152,6 +152,18 @@
  
-+fi
+ #ifdef CONFIG_IP_MULTICAST
+ 
++static struct notifier_block *multicast_chain;
 +
-+if [ "$CONFIG_ARCH_S390" = "y" ]; then
-+   dep_tristate 'FCP host bus adapter driver for IBM z800, z900, z990 (GA2)' CONFIG_ZFCP $CONFIG_QDIO
-+   dep_tristate 'HBA API support for FCP host bus adapter driver for IBM z990 (GA2)' CONFIG_ZFCP_HBAAPI $CONFIG_ZFCP
-+fi
++int register_multicast_notifier(struct notifier_block *nb)
++{
++	return notifier_chain_register(&multicast_chain, nb);
++}
 +
- endmenu
- 
- if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then
-=== drivers/scsi/hosts.c
-==================================================================
---- drivers/scsi/hosts.c   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/scsi/hosts.c   (/trunk/2.4.27)   (revision 52)
-@@ -88,6 +88,24 @@
- scsi_unregister(struct Scsi_Host * sh){
-     struct Scsi_Host * shpnt;
-     Scsi_Host_Name *shn;
-+    char name[10];
++int unregister_multicast_notifier(struct notifier_block *nb)
++{
++	return notifier_chain_unregister(&multicast_chain,nb);
++}
 +
-+    /* kill error handling thread */
-+    if (sh->hostt->use_new_eh_code
-+        && sh->ehandler != NULL) {
-+            DECLARE_MUTEX_LOCKED(sem);
-+            
-+            sh->eh_notify = &sem;
-+            send_sig(SIGHUP, sh->ehandler, 1);
-+            down(&sem);
-+            sh->eh_notify = NULL;
-+    }
-+   
-+    /* remove proc entry */
-+#ifdef CONFIG_PROC_FS
-+    sprintf(name, "%d", sh->host_no);
-+    remove_proc_entry(name, sh->hostt->proc_dir);
-+#endif
-         
-     if(scsi_hostlist == sh)
- 	scsi_hostlist = sh->next;
-@@ -107,7 +125,35 @@
-     if (shn) shn->host_registered = 0;
-     /* else {} : This should not happen, we should panic here... */
-     
-+#if 1
-+    /* We shoult not decrement max_scsi_hosts (and make this value
-+     * candidate for re-allocation by a different driver).
-+     * Reason: the device is _still_ on the
-+     * scsi_host_no_list and it's identified by its name. When the same
-+     * device is re-registered it will get the same host_no again while
-+     * new devices may use the allocation scheme and get this very same
-+     * host_no.
-+     * It's OK to have "holes" in the allocation but it does not mean
-+     * "leaks".
-+     */
-+#else // if 0
-+    /* If we are removing the last host registered, it is safe to reuse
-+     * its host number (this avoids "holes" at boot time) (DB) 
-+     * It is also safe to reuse those of numbers directly below which have
-+     * been released earlier (to avoid some holes in numbering).
-+     */
-+    if(sh->host_no == max_scsi_hosts - 1) {
-+	while(--max_scsi_hosts >= next_scsi_host) {
-+	    shpnt = scsi_hostlist;
-+	    while(shpnt && shpnt->host_no != max_scsi_hosts - 1)
-+		shpnt = shpnt->next;
-+	    if(shpnt)
-+		break;
-+	}
-+    }
-+#endif
-     next_scsi_host--;
-+    sh->hostt->present--;
- 
-     kfree((char *) sh);
+ /*
+  *	Timer management
+  */
+@@ -1165,6 +1177,7 @@
+ 	igmp_group_added(im);
+ 	if (!in_dev->dead)
+ 		ip_rt_multicast_event(in_dev);
++	notifier_call_chain(&multicast_chain, NETDEV_REGISTER, im);
+ out:
+ 	return;
  }
-@@ -122,6 +168,7 @@
-     Scsi_Host_Name *shn, *shn2;
-     int flag_new = 1;
-     const char * hname;
-+    char *name;
-     size_t hname_len;
-     retval = (struct Scsi_Host *)kmalloc(sizeof(struct Scsi_Host) + j,
- 					 (tpnt->unchecked_isa_dma && j ? 
-@@ -252,6 +299,37 @@
-         }
-     }
-     
-+#ifdef CONFIG_PROC_FS
-+    build_proc_dir_entry(retval);
-+#endif
-+    
-+    /* Start error handling thread */
-+    if (retval->hostt->use_new_eh_code) {
-+            DECLARE_MUTEX_LOCKED(sem);
-+            
-+            retval->eh_notify = &sem;
-+            kernel_thread((int (*)(void *)) scsi_error_handler,
-+                          (void *) retval, 0);
-+            
-+            /*
-+             * Now wait for the kernel error thread to initialize itself
-+             * as it might be needed when we scan the bus.
-+             */
-+            down(&sem);
-+            retval->eh_notify = NULL;
-+    }
+@@ -1190,6 +1203,9 @@
+ 				if (!in_dev->dead)
+ 					ip_rt_multicast_event(in_dev);
+ 
++				notifier_call_chain(&multicast_chain,
++						    NETDEV_UNREGISTER,
++						    i);
+ 				ip_ma_put(i);
+ 				return;
+ 			}
+diff -urN kernel-source-2.4.27-2.4.27.orig/net/ipv6/addrconf.c kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/net/ipv6/addrconf.c	2006-01-30 22:25:23.000000000 -0700
+@@ -1015,9 +1015,20 @@
+ 			return -1;
+ 		memcpy(eui, dev->dev_addr, 3);
+ 		memcpy(eui + 5, dev->dev_addr+3, 3);
++#ifdef CONFIG_SHARED_IPV6_CARDS
++		if (dev->features&NETIF_F_SHARED_IPV6) {
++			eui[3] = (dev->dev_id>>8)&0xff;
++			eui[4] = dev->dev_id&0xff;
++		} else {
++			eui[3] = 0xFF;
++			eui[4] = 0xFE;
++			eui[0] ^= 2;
++		}
++#else /* CONFIG_SHARED_IPV6_CARDS */
+ 		eui[3] = 0xFF;
+ 		eui[4] = 0xFE;
+ 		eui[0] ^= 2;
++#endif /* CONFIG_SHARED_IPV6_CARDS */
+ 		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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/net/ipv6/ipv6_syms.c	2006-01-30 22:25:23.000000000 -0700
+@@ -15,6 +15,8 @@
+ EXPORT_SYMBOL(ndisc_mc_map);
+ EXPORT_SYMBOL(register_inet6addr_notifier);
+ EXPORT_SYMBOL(unregister_inet6addr_notifier);
++EXPORT_SYMBOL(register_multicast6_notifier);
++EXPORT_SYMBOL(unregister_multicast6_notifier);
+ 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 kernel-source-2.4.27-2.4.27/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
++++ kernel-source-2.4.27-2.4.27/net/ipv6/mcast.c	2006-01-30 22:25:23.000000000 -0700
+@@ -127,6 +127,18 @@
+ 
+ static struct socket *igmp6_socket;
+ 
++static struct notifier_block *multicast_chain;
 +
-+    tpnt->present++;
++int register_multicast6_notifier(struct notifier_block *nb)
++{
++	return notifier_chain_register(&multicast_chain, nb);
++}
 +
-+    if (tpnt->info) {
-+            name = (char *)tpnt->info(retval);
-+    } else {
-+            name = (char *)tpnt->name;
-+    }
-+    printk(KERN_INFO "scsi%d : %s\n",		/* And print a little message */
-+                   retval->host_no, name);
-+    
-+    
-     return retval;
- }
++int unregister_multicast6_notifier(struct notifier_block *nb)
++{
++	return notifier_chain_unregister(&multicast_chain,nb);
++}
++
+ static void igmp6_join_group(struct ifmcaddr6 *ma);
+ static void igmp6_leave_group(struct ifmcaddr6 *ma);
+ static void igmp6_timer_handler(unsigned long data);
+@@ -857,6 +869,7 @@
  
-=== drivers/scsi/hosts.h
-==================================================================
---- drivers/scsi/hosts.h   (/upstream/vanilla/2.4.27)   (revision 52)
-+++ drivers/scsi/hosts.h   (/trunk/2.4.27)   (revision 52)
-@@ -471,7 +471,10 @@
+ 	mld_del_delrec(idev, &mc->mca_addr);
+ 	igmp6_group_added(mc);
++	notifier_call_chain(&multicast_chain, NETDEV_REGISTER, mc);
+ 	ma_put(mc);
+ 	return 0;
+ }
+@@ -877,6 +890,8 @@
  
- extern Scsi_Host_Template * scsi_hosts;
+ 				igmp6_group_dropped(ma);
  
--extern void build_proc_dir_entries(Scsi_Host_Template  *);
-+#ifdef CONFIG_PROC_FS
-+extern void build_proc_dir(Scsi_Host_Template *);
-+extern void build_proc_dir_entry(struct Scsi_Host *);
++				notifier_call_chain(&multicast_chain,
++						    NETDEV_UNREGISTER, ma);
+ 				ma_put(ma);
+ 				return 0;
+ 			}
+diff -urN kernel-source-2.4.27-2.4.27.orig/net/netsyms.c kernel-source-2.4.27-2.4.27/net/netsyms.c
+--- kernel-source-2.4.27-2.4.27.orig/net/netsyms.c	2006-01-30 22:23:49.000000000 -0700
++++ kernel-source-2.4.27-2.4.27/net/netsyms.c	2006-01-30 22:25:23.000000000 -0700
+@@ -297,7 +297,10 @@
+ EXPORT_SYMBOL(devinet_ioctl);
+ EXPORT_SYMBOL(register_inetaddr_notifier);
+ EXPORT_SYMBOL(unregister_inetaddr_notifier);
+-
++#ifdef CONFIG_IP_MULTICAST
++EXPORT_SYMBOL(register_multicast_notifier);
++EXPORT_SYMBOL(unregister_multicast_notifier);
 +#endif
+ /* needed for ip_gre -cw */
+ EXPORT_SYMBOL(ip_statistics);
  
- /*
-  *  scsi_init initializes the scsi hosts.



More information about the Kernel-svn-changes mailing list