## Automatically generated incremental diff
## From:   linux-2.4.21-bk9
## To:     linux-2.4.21-bk10
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.4.21-bk9/Documentation/Configure.help linux-2.4.21-bk10/Documentation/Configure.help
--- linux-2.4.21-bk9/Documentation/Configure.help	2003-07-15 02:54:57.000000000 -0700
+++ linux-2.4.21-bk10/Documentation/Configure.help	2003-07-15 02:55:04.000000000 -0700
@@ -3611,6 +3611,14 @@
   Say Y here to support the Serverworks AGP card.  See 
   <http://www.serverworks.com/> for product descriptions and images.
 
+NVIDIA chipset support
+CONFIG_AGP_NVIDIA
+  This option gives you AGP support for the GLX component of the
+  XFree86 4.x on NVIDIA nForce/nForce2 chipsets.
+
+  You should say Y here if you use XFree86 3.3.6 or 4.x and want to
+  use GLX or DRI.  If unsure, say N.
+
 ALI chipset support
 CONFIG_AGP_ALI
   This option gives you AGP support for the GLX component of the
@@ -4797,27 +4805,46 @@
   packed pixel and 32 bpp packed pixel. You can also use font widths
   different from 8.
 
-Matrox G100/G200/G400/G450/G550 support
-CONFIG_FB_MATROX_G100
-  Say Y here if you have a Matrox G100, G200, G400, G450, or G550
-  based video card. If you select "Advanced lowlevel driver options",
-  you should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp
-  packed pixel and 32 bpp packed pixel. You can also use font widths
+CONFIG_FB_MATROX_G450
+  Say Y here if you have a Matrox G100, G200, G400, G450 or G550 based
+  video card. If you select "Advanced lowlevel driver options", you
+  should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp packed
+  pixel and 32 bpp packed pixel. You can also use font widths
   different from 8.
 
   If you need support for G400 secondary head, you must first say Y to
   "I2C support" and "I2C bit-banging support" in the character devices
   section, and then to "Matrox I2C support" and "G400 second head
-  support" here in the framebuffer section.
+  support" here in the framebuffer section. G450/G550 secondary head
+  and digital output are supported without additional modules.
+  
+  The driver starts in monitor mode. You must use the matroxset tool 
+  (available at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to 
+  swap primary and secondary head outputs, or to change output mode.  
+  Secondary head driver always start in 640x480 resolution and you 
+  must use fbset to change it.
+
+  Do not forget that second head supports only 16 and 32 bpp
+  packed pixels, so it is a good idea to compile them into the kernel
+  too. You can use only some font widths, as the driver uses generic
+  painting procedures (the secondary head does not use acceleration
+  engine).
   
-  If you have G550, you must also compile support for G450/G550 secondary
-  head into kernel, otherwise picture will be shown only on the output you
-  are probably not using...
+  G450/G550 hardware can display TV picture only from secondary CRTC,
+  and it performs no scaling, so picture must have 525 or 625 lines.
+
+CONFIG_FB_MATROX_G100A
+  Say Y here if you have a Matrox G100, G200 or G400 based
+  video card. If you select "Advanced lowlevel driver options", you
+  should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp packed
+  pixel and 32 bpp packed pixel. You can also use font widths
+  different from 8.
 
-  If you need support for G450 or G550 secondary head, say Y to
-  "Matrox G450/G550 second head support" below.
+  If you need support for G400 secondary head, you must first say Y to
+  "I2C support" and "I2C bit-banging support" in the character devices
+  section, and then to "Matrox I2C support" and "G400 second head
+  support" here in the framebuffer section.
 
-Matrox I2C support
 CONFIG_FB_MATROX_I2C
   This drivers creates I2C buses which are needed for accessing the
   DDC (I2C) bus present on all Matroxes, an I2C bus which
@@ -4860,32 +4887,14 @@
   painting procedures (the secondary head does not use acceleration
   engine).
 
-Matrox G450 second head support
-CONFIG_FB_MATROX_G450
-  Say Y or M here if you want to use a secondary head (meaning two
-  monitors in parallel) on G450, or if you are using analog output
-  of G550.
-
-  If you compile it as module, two modules are created,
-  matroxfb_crtc2.o and matroxfb_g450.o. Both modules are needed if you
-  want two independent display devices.
-
-  The driver starts in monitor mode and currently does not support
-  output in TV modes.  You must use the matroxset tool (available
-  at <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to swap
-  primary and secondary head outputs.  Secondary head driver always
-  start in 640x480 resolution and you must use fbset to change it.
-
-  Note on most G550 cards the analog output is the secondary head,
-  so you will need to say Y here to use it.
-
-  Also do not forget that second head supports only 16 and 32 bpp
-  packed pixels, so it is a good idea to compile them into the kernel
-  too. You can use only some font widths, as the driver uses generic
-  painting procedures (the secondary head does not use acceleration
-  engine).
-
-Matrox unified driver multihead support
+CONFIG_FB_MATROX_PROC
+  Say Y or M here if you want to access some informations about driver
+  state through /proc interface.
+  
+  You should download matrox_pins tool (available at
+  <ftp://platan.vc.cvut.cz/pub/linux/matrox-latest/>) to get human
+  readable output.
+  
 CONFIG_FB_MATROX_MULTIHEAD
   Say Y here if you have more than one (supported) Matrox device in
   your computer and you want to use all of them for different monitors
@@ -18806,6 +18815,14 @@
   of verbosity. Saying Y enables these statements. This will increase
   your kernel size by around 50K.
 
+ACPI Relaxed AML Checking
+CONFIG_ACPI_RELAXED_AML
+  If you say `Y' here, the ACPI interpreter will relax its checking
+  for valid AML and will ignore some AML mistakes, such as off-by-one
+  errors in region sizes.  Some laptops may require this option.  In
+  particular, many Toshiba laptops require this for correct operation
+  of the AC module.
+
 ACPI Bus Manager
 CONFIG_ACPI_BUSMGR
   The ACPI Bus Manager enumerates devices in the ACPI namespace, and
@@ -26875,6 +26892,140 @@
 
   If compiled as a module, it will be called scx200_docflash.o.
 
+BIOS flash chip on AMD76x southbridge
+CONFIG_MTD_AMD76XROM
+  Support for treating the BIOS flash chip on AMD76x motherboards
+  as an MTD device - with this you can reprogram your BIOS.
+
+  BE VERY CAREFUL.
+
+  If compiled as a module, it will be called amd76xrom.o.
+
+BIOS flash chip on Intel Hub Controller 2
+CONFIG_MTD_ICH2ROM
+  Support for treating the BIOS flash chip on ICH2 motherboards
+  as an MTD device - with this you can reprogram your BIOS.
+
+  BE VERY CAREFUL.
+
+  If compiled as a module, it will be called ich2rom.o.
+
+BIOS flash chip on Intel SCB2 boards
+CONFIG_MTD_SCB2_FLASH
+  Support for treating the BIOS flash chip on Intel SCB2 boards
+  as an MTD device - with this you can reprogram your BIOS.
+
+  BE VERY CAREFUL.
+
+  If compiled as a module, it will be called scb2_flash.o.
+
+Flash chips on Tsunami TIG bus
+CONFIG_MTD_TSUNAMI
+  Support for the flash chip on Tsunami TIG bus.
+
+  If compiled as a module, it will be called tsunami_flash.o.
+
+Flash chips on LASAT board
+CONFIG_MTD_LASAT
+  Support for the flash chips on the Lasat 100 and 200 boards.
+
+  If compiled as a module, it will be called lasat.o.
+
+CFI flash device on SnapGear/SecureEdge
+CONFIG_MTD_NETtel
+  Support for flash chips on NETtel/SecureEdge/SnapGear boards.
+
+  If compiled as a module, it will be called nettel.o.
+
+CFI Flash device mapped on DIL/Net PC
+CONFIG_MTD_DILNETPC
+  MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP".
+  For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm>
+   and <http://www.ssv-embedded.de/ssv/pc104/p170.htm>
+
+  If compiled as a module, it will be called dilnetpc.o.
+
+Size of DIL/Net PC flash boot partition
+CONFIG_MTD_DILNETPC_BOOTSIZE
+  The amount of space taken up by the kernel or Etherboot
+  on the DIL/Net PC flash chips.
+
+CFI Flash device mapped on Epxa10db
+CONFIG_MTD_EPXA10DB
+  This enables support for the flash devices on the Altera
+  Excalibur XA10 Development Board. If you are building a kernel
+  for on of these boards then you should say 'Y' otherwise say 'N'.
+
+  If compiled as a module, it will be called epxa10db-flash.o.
+
+CFI Flash device mapped on the FortuNet board
+CONFIG_MTD_FORTUNET
+  This enables access to the Flash on the FortuNet board.  If you
+  have such a board, say 'Y'.
+
+  If compiled as a module, it will be called fortunet.o.
+
+NV-RAM mapping AUTCPU12 board
+CONFIG_MTD_AUTCPU12
+  This enables access to the NV-RAM on autronix autcpu12 board.
+  If you have such a board, say 'Y'.
+
+  If compiled as a module, it will be called autcpu12-nvram.o.
+
+CFI Flash device mapped on EDB7312
+CONFIG_MTD_EDB7312
+  This enables access to the CFI Flash on the Cogent EDB7312 board.
+  If you have such a board, say 'Y' here.
+
+  If compiled as a module, it will be called edb7312.o.
+
+JEDEC Flash device mapped on impA7
+CONFIG_MTD_IMPA7
+  This enables access to the NOR Flash on the impA7 board of
+  implementa GmbH. If you have such a board, say 'Y' here.
+
+  If compiled as a module, it will be called impa7.o.
+
+JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame
+CONFIG_MTD_CEIVA
+  This enables access to the flash chips on the Ceiva/Polaroid
+  PhotoMax Digital Picture Frame.
+  If you have such a device, say 'Y'.
+
+  If compiled as a module, it will be called ceiva.o.
+
+System flash on MBX860 board
+CONFIG_MTD_MBX860
+  This enables access routines for the flash chips on the Motorola
+  MBX860 board. If you have one of these boards and would like
+  to use the flash chips on it, say 'Y'.
+
+  If compiled as a module, it will be called mbx860.o.
+
+PCI MTD driver
+CONFIG_MTD_PCI
+  Mapping for accessing flash devices on add-in cards like the Intel XScale
+  IQ80310 card, and the Intel EBSA285 card in blank ROM programming mode
+  (please see the manual for the link settings).
+
+  If compiled as a module, it will be called pci.o.
+
+  If you are not sure, say N.
+
+PCMCIA MTD driver
+CONFIG_MTD_PCMCIA
+  Map driver for accessing PCMCIA linear flash memory cards. These
+  cards are usually around 4-16MiB in size. This does not include
+  Compact Flash cards which are treated as IDE devices.
+
+  If compiled as a module, it will be called pcmciamtd.o.
+
+Generic uClinux RAM/ROM filesystem support
+CONFIG_MTD_UCLINUX
+  Map driver to support image based filesystems for uClinux.
+
+  If compiled as a module, it will be called uclinux.o.
+
 NatSemi SCx200 I2C using GPIO pins
 CONFIG_SCx200_GPIO
   Enable the use of two GPIO pins of a SCx200 processor as an I2C bus.
diff -urN linux-2.4.21-bk9/MAINTAINERS linux-2.4.21-bk10/MAINTAINERS
--- linux-2.4.21-bk9/MAINTAINERS	2003-07-15 02:54:57.000000000 -0700
+++ linux-2.4.21-bk10/MAINTAINERS	2003-07-15 02:55:04.000000000 -0700
@@ -1831,7 +1831,7 @@
 L:	linux-usb-devel@lists.sourceforge.net
 S:	Maintained
 
-USB BLUETOOTH DRIVER
+USB BLUETOOTH TTY CONVERTER DRIVER
 P:	Greg Kroah-Hartman
 M:	greg@kroah.com
 L:	linux-usb-users@lists.sourceforge.net
diff -urN linux-2.4.21-bk9/Makefile linux-2.4.21-bk10/Makefile
--- linux-2.4.21-bk9/Makefile	2003-07-15 02:54:57.000000000 -0700
+++ linux-2.4.21-bk10/Makefile	2003-07-15 02:55:04.000000000 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 21
-EXTRAVERSION = -bk9
+EXTRAVERSION = -bk10
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.21-bk9/arch/i386/boot/bootsect.S linux-2.4.21-bk10/arch/i386/boot/bootsect.S
--- linux-2.4.21-bk9/arch/i386/boot/bootsect.S	2001-11-09 13:58:02.000000000 -0800
+++ linux-2.4.21-bk10/arch/i386/boot/bootsect.S	2003-07-15 02:55:04.000000000 -0700
@@ -234,7 +234,8 @@
 die:	jne	die			# %es must be at 64kB boundary
 	xorw	%bx, %bx		# %bx is starting address within segment
 rp_read:
-#ifdef __BIG_KERNEL__			# look in setup.S for bootsect_kludge
+#ifdef __BIG_KERNEL__
+					# look in setup.S for bootsect_kludge
 	bootsect_kludge = 0x220		# 0x200 + 0x20 which is the size of the
 	lcall	bootsect_kludge		# bootsector + bootsect_kludge offset
 #else
diff -urN linux-2.4.21-bk9/arch/i386/kernel/mpparse.c linux-2.4.21-bk10/arch/i386/kernel/mpparse.c
--- linux-2.4.21-bk9/arch/i386/kernel/mpparse.c	2003-07-15 02:54:57.000000000 -0700
+++ linux-2.4.21-bk10/arch/i386/kernel/mpparse.c	2003-07-15 02:55:04.000000000 -0700
@@ -516,7 +516,7 @@
 	mp_bus_id_to_local = (int *)&bus_data[(max_mp_busses * sizeof(int)) * 2];
 	mp_bus_id_to_pci_bus = (int *)&bus_data[(max_mp_busses * sizeof(int)) * 3];
 	mp_irqs = (struct mpc_config_intsrc *)&bus_data[(max_mp_busses * sizeof(int)) * 4];
-	memset(mp_bus_id_to_pci_bus, -1, max_mp_busses);
+	memset(mp_bus_id_to_pci_bus, -1, max_mp_busses * sizeof(int));
 
 	/*
 	 *	Now process the configuration blocks.
diff -urN linux-2.4.21-bk9/arch/s390/config.in linux-2.4.21-bk10/arch/s390/config.in
--- linux-2.4.21-bk9/arch/s390/config.in	2003-07-15 02:54:58.000000000 -0700
+++ linux-2.4.21-bk10/arch/s390/config.in	2003-07-15 02:55:05.000000000 -0700
@@ -39,6 +39,10 @@
 bool 'Fast IRQ handling' CONFIG_FAST_IRQ
 bool 'Process warning machine checks' CONFIG_MACHCHK_WARNING
 bool 'Use chscs for Common I/O' CONFIG_CHSC
+tristate 'QDIO support' CONFIG_QDIO
+if [ "$CONFIG_QDIO" != "n" ]; then
+  bool '   Performance statistics in /proc' CONFIG_QDIO_PERF_STATS
+fi
 bool 'Builtin IPL record support' CONFIG_IPL
 if [ "$CONFIG_IPL" = "y" ]; then
   choice 'IPL method generated into head.S' \
diff -urN linux-2.4.21-bk9/arch/s390x/config.in linux-2.4.21-bk10/arch/s390x/config.in
--- linux-2.4.21-bk9/arch/s390x/config.in	2003-07-15 02:54:58.000000000 -0700
+++ linux-2.4.21-bk10/arch/s390x/config.in	2003-07-15 02:55:05.000000000 -0700
@@ -42,6 +42,10 @@
 bool 'Fast IRQ handling' CONFIG_FAST_IRQ
 bool 'Process warning machine checks' CONFIG_MACHCHK_WARNING
 bool 'Use chscs for Common I/O' CONFIG_CHSC
+tristate 'QDIO support' CONFIG_QDIO
+if [ "$CONFIG_QDIO" != "n" ]; then
+  bool '   Performance statistics in /proc' CONFIG_QDIO_PERF_STATS
+fi
 bool 'Builtin IPL record support' CONFIG_IPL
 if [ "$CONFIG_IPL" = "y" ]; then
   choice 'IPL method generated into head.S' \
diff -urN linux-2.4.21-bk9/arch/sh64/config.in linux-2.4.21-bk10/arch/sh64/config.in
--- linux-2.4.21-bk9/arch/sh64/config.in	2003-07-15 02:54:58.000000000 -0700
+++ linux-2.4.21-bk10/arch/sh64/config.in	2003-07-15 02:55:05.000000000 -0700
@@ -49,6 +49,11 @@
        "Little-Endian		CONFIG_LITTLE_ENDIAN		\
 	Big-Endian		CONFIG_BIG_ENDIAN" Little-Endian
 
+bool 'Flush floating point denorms to zero' CONFIG_SH64_FPU_DENORM_FLUSH
+choice 'Page table levels' \
+	"2			CONFIG_SH64_PGTABLE_2_LEVEL \
+	 3			CONFIG_SH64_PGTABLE_3_LEVEL" 2
+
 # Use 32-bit addressing for now.
 # EMI based.
 # P2 (UNCACHED) required to use identity mapping
@@ -79,6 +84,7 @@
 
 bool 'ICache Disabling' CONFIG_ICACHE_DISABLED
 
+
 #
 # CPU Subtype specific options (errata and such)
 #
diff -urN linux-2.4.21-bk9/arch/sh64/mm/fault.c linux-2.4.21-bk10/arch/sh64/mm/fault.c
--- linux-2.4.21-bk9/arch/sh64/mm/fault.c	2003-07-15 02:54:58.000000000 -0700
+++ linux-2.4.21-bk10/arch/sh64/mm/fault.c	2003-07-15 02:55:05.000000000 -0700
@@ -147,19 +147,6 @@
 	 * have just faulted on is not going to work.
 	 */
 
-	//		printk("fault, address is 0x%08x textaccess %d writeaccess %d\n",
-	//			       address,textaccess,writeaccess);
-
-	/* Temporary plumbing - this will go into entry.S soon.  If the fast
-	   path can service the fault, get out quick. */
-#if 0
-	/* Now on fast path */
-	if (do_fast_page_fault(regs, writeaccess, textaccess, address) == 1) return;
-#endif
-
-	/* Otherwise, there is work to be done to put something into the PTE for the
-	   faulting address */
-
 	tsk = current;
 	mm = tsk->mm;
 
@@ -308,26 +295,6 @@
 		printk(KERN_ALERT "Unable to handle kernel paging request");
 	printk(" at virtual address %08lx\n", address);
 	printk(KERN_ALERT "pc = %08Lx%08Lx\n", regs->pc >> 32, regs->pc & 0xffffffff);
-	if (0 == mmu_pdtp_cache) {
-		printk("mmu_pdtp_cache==0!\n");
-	} else {
-		page = pgd_val(*mmu_pdtp_cache);
-		if (page && (page & _PAGE_PRESENT)) {
-			page = (((unsigned long *) page)[address >> PGDIR_SHIFT] & PAGE_MASK);
-			printk(KERN_ALERT "*pmd = %08lx\n", page);
-			if (page && (page & _PAGE_PRESENT)) {
-				page = (((unsigned long *) page)[(address >> PMD_SHIFT) & PMD_MASK] & \
-					 PAGE_MASK);
-				printk(KERN_ALERT "*pte = %08lx\n", page);
-				if (page & (page & _PAGE_PRESENT)) {
-					lpage = ((unsigned long long *) (page)) \
-						 [(address >> PAGE_SHIFT) & PAGE_MASK];
-					printk(KERN_ALERT "pte = %08Lx%08Lx\n",
-							   lpage >> 32, lpage & 0xffffffff);
-				}
-			}
-		}
-	}
 	die("Oops", regs, writeaccess);
 	do_exit(SIGKILL);
 
@@ -584,7 +551,6 @@
 	unsigned long y;
 
 	y = x & mask;
-	/* Copy logic from pgtable-3level.h */
 	if (y) return 1;
 	else   return 0;
 }
diff -urN linux-2.4.21-bk9/arch/sh64/mm/tlbmiss.c linux-2.4.21-bk10/arch/sh64/mm/tlbmiss.c
--- linux-2.4.21-bk9/arch/sh64/mm/tlbmiss.c	2003-07-15 02:54:58.000000000 -0700
+++ linux-2.4.21-bk10/arch/sh64/mm/tlbmiss.c	2003-07-15 02:55:05.000000000 -0700
@@ -141,13 +141,13 @@
 	   page table tree stored for the top half of the address space since
 	   virtual pages in that region should never be mapped in user mode.
 	   (In kernel mode, the only things in that region are the 512Mb super
-	   page (locked in) and I/O device pages (handled by
-	   handle_vmalloc_fault), so no PGD for the upper half is required by
-	   kernel mode either).
-	   
+	   page (locked in), and vmalloc (modules) +  I/O device pages (handled
+	   by handle_vmalloc_fault), so no PGD for the upper half is required
+	   by kernel mode either).
+
 	   See how mm->pgd is allocated and initialised in pgd_alloc to see why
 	   the next test is necessary.  - RPC */
-	if (address & 0x80000000UL) {
+	if (address >= (unsigned long) TASK_SIZE) {
 		/* upper half - never has page table entries. */
 		return 0;
 	}
diff -urN linux-2.4.21-bk9/arch/sparc64/defconfig linux-2.4.21-bk10/arch/sparc64/defconfig
--- linux-2.4.21-bk9/arch/sparc64/defconfig	2003-07-15 02:54:58.000000000 -0700
+++ linux-2.4.21-bk10/arch/sparc64/defconfig	2003-07-15 02:55:05.000000000 -0700
@@ -180,19 +180,104 @@
 CONFIG_PACKET=y
 CONFIG_PACKET_MMAP=y
 CONFIG_NETLINK_DEV=y
-# CONFIG_NETFILTER is not set
-# CONFIG_FILTER is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_FILTER=y
 CONFIG_UNIX=y
 CONFIG_INET=y
-# CONFIG_IP_MULTICAST is not set
+CONFIG_IP_MULTICAST=y
 # CONFIG_IP_ADVANCED_ROUTER is not set
 # CONFIG_IP_PNP is not set
-# CONFIG_NET_IPIP is not set
-# CONFIG_NET_IPGRE is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
 CONFIG_ARPD=y
 CONFIG_INET_ECN=y
 # CONFIG_SYN_COOKIES is not set
+
+#
+#   IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_AMANDA=m
+CONFIG_IP_NF_TFTP=m
+CONFIG_IP_NF_IRC=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_PKTTYPE=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_DSCP=m
+CONFIG_IP_NF_MATCH_AH_ESP=m
+CONFIG_IP_NF_MATCH_LENGTH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_TCPMSS=m
+CONFIG_IP_NF_MATCH_HELPER=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_CONNTRACK=m
+CONFIG_IP_NF_MATCH_UNCLEAN=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_MIRROR=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_NAT_AMANDA=m
+# CONFIG_IP_NF_NAT_LOCAL is not set
+CONFIG_IP_NF_NAT_SNMP_BASIC=m
+CONFIG_IP_NF_NAT_IRC=m
+CONFIG_IP_NF_NAT_FTP=m
+CONFIG_IP_NF_NAT_TFTP=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_DSCP=m
+CONFIG_IP_NF_TARGET_MARK=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_TCPMSS=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_NAT_NEEDED=y
+CONFIG_IP_NF_COMPAT_IPFWADM=m
+CONFIG_IP_NF_NAT_NEEDED=y
 CONFIG_IPV6=m
+
+#
+#   IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MATCH_LIMIT=m
+CONFIG_IP6_NF_MATCH_MAC=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_MULTIPORT=m
+CONFIG_IP6_NF_MATCH_OWNER=m
+CONFIG_IP6_NF_MATCH_MARK=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_AHESP=m
+CONFIG_IP6_NF_MATCH_LENGTH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_TARGET_MARK=m
 # CONFIG_KHTTPD is not set
 # CONFIG_ATM is not set
 CONFIG_VLAN_8021Q=m
@@ -235,6 +320,7 @@
 CONFIG_NET_SCH_TBF=m
 CONFIG_NET_SCH_GRED=m
 CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_INGRESS=m
 CONFIG_NET_QOS=y
 CONFIG_NET_ESTIMATOR=y
 CONFIG_NET_CLS=y
@@ -600,6 +686,7 @@
 # File systems
 #
 # CONFIG_QUOTA is not set
+# CONFIG_QFMT_V2 is not set
 CONFIG_AUTOFS_FS=m
 CONFIG_AUTOFS4_FS=m
 # CONFIG_REISERFS_FS is not set
@@ -609,9 +696,9 @@
 # CONFIG_ADFS_FS_RW is not set
 CONFIG_AFFS_FS=m
 # CONFIG_HFS_FS is not set
+CONFIG_HFSPLUS_FS=m
 CONFIG_BEFS_FS=m
 # CONFIG_BEFS_DEBUG is not set
-CONFIG_HFSPLUS_FS=m
 CONFIG_BFS_FS=m
 CONFIG_EXT3_FS=m
 CONFIG_JBD=m
diff -urN linux-2.4.21-bk9/arch/sparc64/kernel/sys_sparc32.c linux-2.4.21-bk10/arch/sparc64/kernel/sys_sparc32.c
--- linux-2.4.21-bk9/arch/sparc64/kernel/sys_sparc32.c	2003-07-15 02:54:58.000000000 -0700
+++ linux-2.4.21-bk10/arch/sparc64/kernel/sys_sparc32.c	2003-07-15 02:55:05.000000000 -0700
@@ -507,7 +507,7 @@
 
 static int do_sys32_msgsnd (int first, int second, int third, void *uptr)
 {
-	struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER);
+	struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf), GFP_USER);
 	struct msgbuf32 *up = (struct msgbuf32 *)uptr;
 	mm_segment_t old_fs;
 	int err;
@@ -549,12 +549,12 @@
 		msgtyp = ipck.msgtyp;
 	}
 	err = -ENOMEM;
-	p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER);
+	p = kmalloc (second + sizeof (struct msgbuf), GFP_USER);
 	if (!p)
 		goto out;
 	old_fs = get_fs ();
 	set_fs (KERNEL_DS);
-	err = sys_msgrcv (first, p, second + 4, msgtyp, third);
+	err = sys_msgrcv (first, p, second, msgtyp, third);
 	set_fs (old_fs);
 	if (err < 0)
 		goto free_then_out;
@@ -2156,13 +2156,6 @@
 	spin_lock_irq(&current->sigmask_lock);
 	sig = dequeue_signal(&these, &info);
 	if (!sig) {
-		/* None ready -- temporarily unblock those we're interested
-		   in so that we'll be awakened when they arrive.  */
-		sigset_t oldblocked = current->blocked;
-		sigandsets(&current->blocked, &current->blocked, &these);
-		recalc_sigpending(current);
-		spin_unlock_irq(&current->sigmask_lock);
-
 		timeout = MAX_SCHEDULE_TIMEOUT;
 		if (uts)
 			timeout = (timespec_to_jiffies(&ts)
@@ -2171,10 +2164,23 @@
 		current->state = TASK_INTERRUPTIBLE;
 		timeout = schedule_timeout(timeout);
 
-		spin_lock_irq(&current->sigmask_lock);
-		sig = dequeue_signal(&these, &info);
-		current->blocked = oldblocked;
-		recalc_sigpending(current);
+		if (timeout) {
+			/* None ready -- temporarily unblock those we're
+			 * interested while we are sleeping in so that we'll
+			 * be awakened when they arrive.  */
+			sigset_t oldblocked = current->blocked;
+			sigandsets(&current->blocked, &current->blocked, &these);
+			recalc_sigpending(current);
+			spin_unlock_irq(&current->sigmask_lock);
+
+			current->state = TASK_INTERRUPTIBLE;
+			timeout = schedule_timeout(timeout);
+
+			spin_lock_irq(&current->sigmask_lock);
+			sig = dequeue_signal(&these, &info);
+			current->blocked = oldblocked;
+			recalc_sigpending(current);
+		}
 	}
 	spin_unlock_irq(&current->sigmask_lock);
 
@@ -2965,33 +2971,6 @@
 	return ret;
 }
 
-static int do_set_icmpv6_filter(int fd, int level, int optname,
-				char *optval, int optlen)
-{
-	struct icmp6_filter kfilter;
-	mm_segment_t old_fs;
-	int ret, i;
-
-	if (copy_from_user(&kfilter, optval, sizeof(kfilter)))
-		return -EFAULT;
-
-
-	for (i = 0; i < 8; i += 2) {
-		u32 tmp = kfilter.data[i];
-
-		kfilter.data[i] = kfilter.data[i + 1];
-		kfilter.data[i + 1] = tmp;
-	}
-
-	old_fs = get_fs();
-	set_fs(KERNEL_DS);
-	ret = sys_setsockopt(fd, level, optname,
-			     (char *) &kfilter, sizeof(kfilter));
-	set_fs(old_fs);
-
-	return ret;
-}
-
 static int do_set_sock_timeout(int fd, int level, int optname, char *optval, int optlen)
 {
 	struct timeval32 *up = (struct timeval32 *) optval;
@@ -3023,9 +3002,6 @@
 					    optval, optlen);
 	if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)
 		return do_set_sock_timeout(fd, level, optname, optval, optlen);
-	if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER)
-		return do_set_icmpv6_filter(fd, level, optname,
-					    optval, optlen);
 
 	return sys_setsockopt(fd, level, optname, optval, optlen);
 }
diff -urN linux-2.4.21-bk9/drivers/acpi/hardware/hwacpi.c linux-2.4.21-bk10/drivers/acpi/hardware/hwacpi.c
--- linux-2.4.21-bk9/drivers/acpi/hardware/hwacpi.c	2003-07-15 02:54:58.000000000 -0700
+++ linux-2.4.21-bk10/drivers/acpi/hardware/hwacpi.c	2003-07-15 02:55:05.000000000 -0700
@@ -208,6 +208,10 @@
 
 	ACPI_FUNCTION_TRACE ("hw_get_mode");
 
+	/* If there's no smi_cmd port, then it's ACPI only hw */
+	if (!acpi_gbl_FADT->smi_cmd)
+		return_VALUE (ACPI_SYS_MODE_ACPI);
+
 	status = acpi_get_register (ACPI_BITREG_SCI_ENABLE, &value, ACPI_MTX_LOCK);
 	if (ACPI_FAILURE (status)) {
 		return_VALUE (ACPI_SYS_MODE_LEGACY);
diff -urN linux-2.4.21-bk9/drivers/char/drm/drm_agpsupport.h linux-2.4.21-bk10/drivers/char/drm/drm_agpsupport.h
--- linux-2.4.21-bk9/drivers/char/drm/drm_agpsupport.h	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/char/drm/drm_agpsupport.h	2003-06-13 07:51:32.000000000 -0700
@@ -283,15 +283,8 @@
 			break;
 		case VIA_APOLLO_PRO: 	head->chipset = "VIA Apollo Pro";
 			break;
-/*
- * Alan merged agp_backend.h which removes VIA_APOLLO_P4X400, but 
- * didnt update this file. I just commented it out so we dont 
- * get compile errors on -pre4 and it can be fixed in -pre5.
- * 
 		case VIA_APOLLO_P4X400:	head->chipset = "VIA Apollo P4X400";
 			break;
-	
-*/
 
 		case SIS_GENERIC:	head->chipset = "SiS";           break;
 		case AMD_GENERIC:	head->chipset = "AMD";           break;
diff -urN linux-2.4.21-bk9/drivers/char/drm-4.0/agpsupport.c linux-2.4.21-bk10/drivers/char/drm-4.0/agpsupport.c
--- linux-2.4.21-bk9/drivers/char/drm-4.0/agpsupport.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.21-bk10/drivers/char/drm-4.0/agpsupport.c	2003-07-15 02:55:05.000000000 -0700
@@ -278,6 +278,7 @@
 		case VIA_APOLLO_KT400:  head->chipset = "VIA Apollo KT400";
 			break;
 		case VIA_APOLLO_P4X400:	head->chipset = "VIA Apollo P4X400";
+			break;
 #endif
 
 		case VIA_APOLLO_PRO: 	head->chipset = "VIA Apollo Pro";
diff -urN linux-2.4.21-bk9/drivers/ide/ide-floppy.c linux-2.4.21-bk10/drivers/ide/ide-floppy.c
--- linux-2.4.21-bk9/drivers/ide/ide-floppy.c	2003-06-13 07:51:33.000000000 -0700
+++ linux-2.4.21-bk10/drivers/ide/ide-floppy.c	2003-07-15 02:55:06.000000000 -0700
@@ -2195,7 +2195,6 @@
 	idefloppy_floppy_t *floppy;
 	int failed = 0;
 
-	printk(KERN_INFO "ide-floppy driver " IDEFLOPPY_VERSION "\n");
 #endif /* CLASSIC_BUILTINS_METHOD */
 	MOD_INC_USE_COUNT;
 #ifdef CLASSIC_BUILTINS_METHOD
diff -urN linux-2.4.21-bk9/drivers/ide/ide.c linux-2.4.21-bk10/drivers/ide/ide.c
--- linux-2.4.21-bk9/drivers/ide/ide.c	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/ide/ide.c	2003-07-15 02:55:06.000000000 -0700
@@ -2221,6 +2221,7 @@
  */
 
 static __initdata ide_driver_call ide_scan[NUM_DRIVER];
+static int drivers_run = 0;
 
 void __init ide_register_driver(ide_driver_call scan)
 {
@@ -2228,6 +2229,11 @@
 	if(ide_scans == NUM_DRIVER)
 		panic("Too many IDE drivers");
 	ide_scan[ide_scans++]=scan;
+	if(drivers_run)
+	{
+		printk(KERN_ERR "ide: late registration of driver.\n");
+		scan();
+	}
 }
 
 EXPORT_SYMBOL(ide_register_driver);
@@ -2240,6 +2246,7 @@
 		(ide_scan[i])();
 		i++;
 	}
+	drivers_run = 1;
 }
 
 /*
diff -urN linux-2.4.21-bk9/drivers/ide/setup-pci.c linux-2.4.21-bk10/drivers/ide/setup-pci.c
--- linux-2.4.21-bk9/drivers/ide/setup-pci.c	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/ide/setup-pci.c	2003-07-15 02:55:06.000000000 -0700
@@ -172,7 +172,7 @@
  *	is already in DMA mode we check and enforce IDE simplex rules.
  */
 
-static unsigned long __init ide_get_or_set_dma_base (ide_hwif_t *hwif)
+static unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif)
 {
 	unsigned long	dma_base = 0;
 	struct pci_dev	*dev = hwif->pci_dev;
diff -urN linux-2.4.21-bk9/drivers/isdn/isdn_ppp.c linux-2.4.21-bk10/drivers/isdn/isdn_ppp.c
--- linux-2.4.21-bk9/drivers/isdn/isdn_ppp.c	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/isdn/isdn_ppp.c	2003-07-15 02:55:06.000000000 -0700
@@ -111,8 +111,8 @@
 	struct ippp_struct *is;
 
 	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR __FUNCTION__": ppp_slot(%d) out of range\n",
-			lp->ppp_slot);
+		printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
+			__FUNCTION__, lp->ppp_slot);
 		return 0;
 	}
 
@@ -131,8 +131,8 @@
 	spin_unlock(&lp->netdev->pb->lock);
 #endif /* CONFIG_ISDN_MPP */
 	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR __FUNCTION__": ppp_slot(%d) now invalid\n",
-			lp->ppp_slot);
+		printk(KERN_ERR "%s: ppp_slot(%d) now invalid\n",
+			__FUNCTION__, lp->ppp_slot);
 		restore_flags(flags);
 		return 0;
 	}
@@ -232,8 +232,8 @@
 isdn_ppp_wakeup_daemon(isdn_net_local * lp)
 {
 	if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR __FUNCTION__": ppp_slot(%d) out of range\n",
-			lp->ppp_slot);
+		printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
+			__FUNCTION__, lp->ppp_slot);
 		return;
 	}
 	ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
@@ -251,8 +251,8 @@
 	struct ippp_struct *is;
 
 	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR __FUNCTION__": slot(%d) out of range\n",
-			slot);
+		printk(KERN_ERR "%s: slot(%d) out of range\n",
+			__FUNCTION__, slot);
 		return 0;
 	}
 	is = ippp_table[slot];
@@ -350,7 +350,7 @@
 	is = file->private_data;
 
 	if (!is) {
-		printk(KERN_ERR __FUNCTION__": no file->private_data\n");
+		printk(KERN_ERR "%s: no file->private_data\n", __FUNCTION__);
 		return;
 	}
 	if (is->debug & 0x1)
@@ -360,7 +360,7 @@
 		isdn_net_dev *p = is->lp->netdev;
 
 		if (!p) {
-			printk(KERN_ERR __FUNCTION__": no lp->netdev\n");
+			printk(KERN_ERR "%s: no lp->netdev\n", __FUNCTION__);
 			return;
 		}
 		is->state &= ~IPPP_CONNECT;	/* -> effect: no call of wakeup */
@@ -1077,8 +1077,8 @@
 			if (is->debug & 0x20)
 				printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
 			if (net_dev->local->ppp_slot < 0) {
-				printk(KERN_ERR __FUNCTION__": net_dev->local->ppp_slot(%d) out of range\n",
-					net_dev->local->ppp_slot);
+				printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n",
+					__FUNCTION__, net_dev->local->ppp_slot);
 				goto drop_packet;
 			}
 			if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
@@ -1103,8 +1103,8 @@
 				skb_put(skb, skb_old->len + 128);
 				memcpy(skb->data, skb_old->data, skb_old->len);
 				if (net_dev->local->ppp_slot < 0) {
-					printk(KERN_ERR __FUNCTION__": net_dev->local->ppp_slot(%d) out of range\n",
-						net_dev->local->ppp_slot);
+					printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n",
+						__FUNCTION__, net_dev->local->ppp_slot);
 					goto drop_packet;
 				}
 				pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp,
@@ -1540,8 +1540,8 @@
 	struct ippp_struct * is;
 
 	if (lp->ppp_slot < 0) {
-		printk(KERN_ERR __FUNCTION__": lp->ppp_slot(%d) out of range\n",
-			lp->ppp_slot);
+		printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+			__FUNCTION__, lp->ppp_slot);
 		return(-EINVAL);
 	}
 
@@ -1591,8 +1591,8 @@
         stats = &mp->stats;
 	slot = lp->ppp_slot;
 	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR __FUNCTION__": lp->ppp_slot(%d)\n",
-			lp->ppp_slot);
+		printk(KERN_ERR "%s: lp->ppp_slot(%d)\n",
+			__FUNCTION__, lp->ppp_slot);
 		stats->frame_drops++;
 		dev_kfree_skb(skb);
 		spin_unlock_irqrestore(&mp->lock, flags);
@@ -1628,8 +1628,8 @@
 	for (lpq = net_dev->queue;;) {
 		slot = lpq->ppp_slot;
 		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-			printk(KERN_ERR __FUNCTION__": lpq->ppp_slot(%d)\n",
-				lpq->ppp_slot);
+			printk(KERN_ERR "%s: lpq->ppp_slot(%d)\n",
+				__FUNCTION__, lpq->ppp_slot);
 		} else {
 			u32 lls = ippp_table[slot]->last_link_seqno;
 			if (MP_LT(lls, minseq))
@@ -1862,8 +1862,8 @@
 	unsigned int tot_len;
 
 	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR __FUNCTION__": lp->ppp_slot(%d) out of range\n",
-			lp->ppp_slot);
+		printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+			__FUNCTION__, lp->ppp_slot);
 		return;
 	}
 	if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) {
@@ -2639,8 +2639,8 @@
 	printk(KERN_DEBUG "Received CCP frame from peer slot(%d)\n",
 		lp->ppp_slot);
 	if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR __FUNCTION__": lp->ppp_slot(%d) out of range\n",
-			lp->ppp_slot);
+		printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+			__FUNCTION__, lp->ppp_slot);
 		return;
 	}
 	is = ippp_table[lp->ppp_slot];
@@ -2649,8 +2649,8 @@
 	if(lp->master) {
 		int slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
 		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-			printk(KERN_ERR __FUNCTION__": slot(%d) out of range\n",
-				slot);
+			printk(KERN_ERR "%s: slot(%d) out of range\n",
+				__FUNCTION__, slot);
 			return;
 		}	
 		mis = ippp_table[slot];
@@ -2813,8 +2813,8 @@
 	if(!skb || skb->len < 3)
 		return;
 	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-		printk(KERN_ERR __FUNCTION__": lp->ppp_slot(%d) out of range\n",
-			slot);
+		printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+			__FUNCTION__, slot);
 		return;
 	}	
 	is = ippp_table[slot];
@@ -2836,8 +2836,8 @@
 	if (lp->master) {
 		slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
 		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-			printk(KERN_ERR __FUNCTION__": slot(%d) out of range\n",
-				slot);
+			printk(KERN_ERR "%s: slot(%d) out of range\n",
+				__FUNCTION__, slot);
 			return;
 		}	
 		mis = ippp_table[slot];
diff -urN linux-2.4.21-bk9/drivers/net/wan/sbni.c linux-2.4.21-bk10/drivers/net/wan/sbni.c
--- linux-2.4.21-bk9/drivers/net/wan/sbni.c	2002-11-28 15:53:14.000000000 -0800
+++ linux-2.4.21-bk10/drivers/net/wan/sbni.c	2003-07-15 02:55:06.000000000 -0700
@@ -1552,13 +1552,13 @@
 static u32
 calc_crc32( u32  crc,  u8  *p,  u32  len )
 {
-	register u32  _crc __asm ( "ax" );
+	register u32  _crc;
 	_crc = crc;
 	
 	__asm __volatile (
 		"xorl	%%ebx, %%ebx\n"
-		"movl	%1, %%esi\n" 
-		"movl	%2, %%ecx\n" 
+		"movl	%2, %%esi\n" 
+		"movl	%3, %%ecx\n" 
 		"movl	$crc32tab, %%edi\n"
 		"shrl	$2, %%ecx\n"
 		"jz	1f\n"
@@ -1594,7 +1594,7 @@
 		"jnz	0b\n"
 
 	"1:\n"
-		"movl	%2, %%ecx\n"
+		"movl	%3, %%ecx\n"
 		"andl	$3, %%ecx\n"
 		"jz	2f\n"
 
@@ -1619,9 +1619,9 @@
 		"xorb	2(%%esi), %%bl\n"
 		"xorl	(%%edi,%%ebx,4), %%eax\n"
 	"2:\n"
-		:
-		: "a" (_crc), "g" (p), "g" (len)
-		: "ax", "bx", "cx", "dx", "si", "di"
+		: "=a" (_crc)
+		: "0" (_crc), "g" (p), "g" (len)
+		: "bx", "cx", "dx", "si", "di"
 	);
 
 	return  _crc;
diff -urN linux-2.4.21-bk9/drivers/pcmcia/ti113x.h linux-2.4.21-bk10/drivers/pcmcia/ti113x.h
--- linux-2.4.21-bk9/drivers/pcmcia/ti113x.h	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/pcmcia/ti113x.h	2003-07-15 02:55:06.000000000 -0700
@@ -171,21 +171,26 @@
 	/*
 	 * If ISA interrupts don't work, then fall back to routing card
 	 * interrupts to the PCI interrupt of the socket.
+	 *
+	 * Tweaking this when we are using serial PCI IRQs causes hangs
+	 *   --rmk
 	 */
 	if (!socket->cap.irq_mask) {
-		int irqmux, devctl;
-
-		printk (KERN_INFO "ti113x: Routing card interrupts to PCI\n");
+		u8 irqmux, devctl;
 
 		devctl = config_readb(socket, TI113X_DEVICE_CONTROL);
-		devctl &= ~TI113X_DCR_IMODE_MASK;
+		if (devctl & TI113X_DCR_IMODE_MASK != TI12XX_DCR_IMODE_ALL_SERIAL) {
+			printk (KERN_INFO "ti113x: Routing card interrupts to PCI\n");
+
+			devctl &= ~TI113X_DCR_IMODE_MASK;
 
-		irqmux = config_readl(socket, TI122X_IRQMUX);
-		irqmux = (irqmux & ~0x0f) | 0x02; /* route INTA */
-		irqmux = (irqmux & ~0xf0) | 0x20; /* route INTB */
+			irqmux = config_readl(socket, TI122X_IRQMUX);
+			irqmux = (irqmux & ~0x0f) | 0x02; /* route INTA */
+			irqmux = (irqmux & ~0xf0) | 0x20; /* route INTB */
 
-		config_writel(socket, TI122X_IRQMUX, irqmux);
-		config_writeb(socket, TI113X_DEVICE_CONTROL, devctl);
+			config_writel(socket, TI122X_IRQMUX, irqmux);
+			config_writeb(socket, TI113X_DEVICE_CONTROL, devctl);
+		}
 	}
 
 	return 0;
diff -urN linux-2.4.21-bk9/drivers/s390/net/Makefile linux-2.4.21-bk10/drivers/s390/net/Makefile
--- linux-2.4.21-bk9/drivers/s390/net/Makefile	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/s390/net/Makefile	2003-07-15 02:55:06.000000000 -0700
@@ -13,6 +13,8 @@
 obj-$(CONFIG_CTC) += ctc.o fsm.o
 obj-$(CONFIG_IUCV) += netiucv.o
 obj-$(CONFIG_C7000) += c7000.o
+obj-$(CONFIG_QETH) += qeth.o
+export-objs += qeth.o
 
 include $(TOPDIR)/Rules.make
 
diff -urN linux-2.4.21-bk9/drivers/s390/net/qeth.c linux-2.4.21-bk10/drivers/s390/net/qeth.c
--- linux-2.4.21-bk9/drivers/s390/net/qeth.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/drivers/s390/net/qeth.c	2003-07-15 02:55:06.000000000 -0700
@@ -0,0 +1,10946 @@
+/*
+ *
+ * linux/drivers/s390/net/qeth.c ($Revision: 1.337 $)
+ *
+ * Linux on zSeries OSA Express and HiperSockets support
+ *
+ * Copyright 2000,2003 IBM Corporation
+ *
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ *            Cornelia Huck <cohuck@de.ibm.com> (chandev stuff,
+ *                                               numerous bugfixes)
+ *            Frank Pavlic <pavlic@de.ibm.com>  (query/purge ARP, SNMP, fixes)
+ *            Andreas Herrmann <aherrman@de.ibm.com> (bugfixes)
+ *
+ * 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.
+ */
+
+/*
+ * 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.
+ *
+ * There are several stages from the channel recognition to the running
+ * network device:
+ * - The channels are scanned and ordered due to the parameters (see
+ *   MODULE_PARM_DESC)
+ * - The card is hardsetup: this means, that the communication channels
+ *   are prepared
+ * - The card is softsetup: this means, that commands are issued
+ *   to activate the network parameters
+ * - After that, data can flow through the card (transported by QDIO)
+ *
+ *IPA Takeover:
+ * /proc/qeth_ipa_takeover provides the possibility to add and remove
+ * certain ranges of IP addresses to the driver. As soon as these
+ * addresses have to be set by the driver, the driver uses the OSA
+ * Address Takeover mechanism.
+ * reading out of the proc-file displays the registered addresses;
+ * writing into it changes the information. Only one command at one
+ * time must be written into the file. Subsequent commands are ignored.
+ * The following commands are available:
+ * inv4
+ * inv6
+ * add4 <ADDR>/<mask bits>[:<interface>]
+ * add6 <ADDR>/<mask bits>[:<interface>]
+ * del4 <ADDR>/<mask bits>[:<interface>]
+ * del6 <ADDR>/<mask bits>[:<interface>]
+ * inv4 and inv6 toggle the IPA takeover behaviour for all interfaces:
+ * when inv4 was input once, all addresses specified with add4 are not
+ * set using the takeover mechanism, but all other IPv4 addresses are set so.
+ *
+ * add# adds an address range, del# deletes an address range. # corresponds
+ * to the IP version (4 or 6).
+ * <ADDR> is a 8 or 32byte hexadecimal view of the IP address.
+ * <mask bits> specifies the number of bits which are set in the network mask.
+ * <interface> is optional and specifies the interface name to which the
+ * address range is bound.
+ * E. g.
+ *   add4 C0a80100/24
+ * activates all addresses in the 192.168.10 subnet for address takeover.
+ * Note, that the address is not taken over before an according ifconfig
+ * is executed.
+ *
+ *VIPA:
+ * add_vipa4 <ADDR>:<interface>
+ * add_vipa6 <ADDR>:<interface>
+ * del_vipa4 <ADDR>:<interface>
+ * del_vipa6 <ADDR>:<interface>
+ *
+ * the specified address is set/unset as VIPA on the specified interface.
+ * use the src_vipa package to exploit this out of arbitrary applications.
+ *
+ *Proxy ARP:
+ *
+ * add_rxip4 <ADDR>:<interface>
+ * add_rxip6 <ADDR>:<interface>
+ * del_rxip4 <ADDR>:<interface>
+ * del_rxip6 <ADDR>:<interface>
+ *
+ * the specified address is set/unset as "do not fail a gratuitous ARP"
+ * on the specified interface. this can be used to act as a proxy ARP.
+ */
+
+void volatile qeth_eyecatcher(void)
+{
+	return;
+}
+
+#include <linux/config.h>
+
+#ifndef CONFIG_CHANDEV
+#error "qeth can only be compiled with chandev support"
+#endif /* CONFIG_CHANDEV */
+
+#include <linux/module.h>
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+
+#include <linux/version.h>
+
+#include <asm/io.h>
+#include <asm/ebcdic.h>
+#include <linux/ctype.h>
+#include <asm/semaphore.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <linux/inetdevice.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/tcp.h>
+#include <linux/icmp.h>
+#include <linux/skbuff.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif /* CONFIG_PROC_FS */
+#include <net/route.h>
+#include <net/arp.h>
+#include <linux/in.h>
+#include <linux/igmp.h>
+#include <net/ip.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+#include <net/ipv6.h>
+#include <linux/in6.h>
+#include <net/if_inet6.h>
+#include <net/addrconf.h>
+#include <linux/if_tr.h>
+#include <linux/trdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/reboot.h>
+
+#include <linux/if_vlan.h>
+#include <asm/chandev.h>
+
+#include <asm/irq.h>
+#include <asm/s390dyn.h>
+#include <asm/debug.h>
+
+#include <asm/qdio.h>
+
+#include "qeth_mpc.h"
+#include "qeth.h"
+
+/****************** MODULE PARAMETER VARIABLES ********************/
+static int qeth_sparebufs=0;
+MODULE_PARM(qeth_sparebufs,"i");
+MODULE_PARM_DESC(qeth_sparebufs,"the number of pre-allocated spare buffers " \
+		 "reserved for low memory situations");
+
+static int global_stay_in_mem=0;
+
+/****************** MODULE STUFF **********************************/
+#define VERSION_QETH_C "$Revision: 1.337 $"
+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 ")";
+
+MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
+MODULE_DESCRIPTION("Linux on zSeries OSA Express and HiperSockets support\n" \
+		   "Copyright 2000,2003 IBM Corporation\n");
+MODULE_LICENSE("GPL");
+
+/******************** 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];
+static int sparebuffer_count;
+
+static unsigned int known_devices[][10]=QETH_MODELLIST_ARRAY;
+
+static spinlock_t setup_lock;
+static rwlock_t list_lock=RW_LOCK_UNLOCKED;
+
+static debug_info_t *qeth_dbf_setup=NULL;
+static debug_info_t *qeth_dbf_data=NULL;
+static debug_info_t *qeth_dbf_misc=NULL;
+static debug_info_t *qeth_dbf_control=NULL;
+static debug_info_t *qeth_dbf_trace=NULL;
+static debug_info_t *qeth_dbf_sense=NULL;
+static debug_info_t *qeth_dbf_qerr=NULL;
+
+static int proc_file_registration;
+#ifdef QETH_PERFORMANCE_STATS
+static int proc_perf_file_registration;
+#define NOW qeth_get_micros()
+#endif /* QETH_PERFORMANCE_STATS */
+static int proc_ipato_file_registration;
+
+static int ipato_inv4=0,ipato_inv6=0;
+static ipato_entry_t *ipato_entries=NULL;
+static spinlock_t ipato_list_lock;
+
+typedef struct {
+	char *data;
+	int len;
+} tempinfo_t;
+
+/* 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);
+
+inline static int QETH_IP_VERSION(struct sk_buff *skb)
+{
+        switch (skb->protocol) {
+        case ETH_P_IPV6: return 6;
+        case ETH_P_IP: return 4;
+        default: return 0;
+        }
+}
+/* not a macro, as one of the arguments is atomic_read */
+static inline int qeth_min(int a,int b)
+{
+	if (a<b)
+		return a;
+	else
+		return b;
+}
+
+static inline unsigned int qeth_get_millis(void)
+{
+	__u64 time;
+
+	asm volatile ("STCK %0" : "=m" (time));
+	return (int) (time>>22); /* time>>12 is microseconds, we divide it
+				    by 1024 */
+}
+
+#ifdef QETH_PERFORMANCE_STATS
+static inline unsigned int qeth_get_micros(void)
+{
+	__u64 time;
+
+	asm volatile ("STCK %0" : "=m" (time));
+	return (int) (time>>12);
+}
+#endif /* QETH_PERFORMANCE_STATS */
+
+static void qeth_delay_millis(unsigned long msecs)
+{
+	unsigned int start;
+
+	start=qeth_get_millis();
+	while (qeth_get_millis()-start<msecs)
+		;
+}
+
+static void qeth_wait_nonbusy(unsigned int timeout)
+{
+	unsigned int start;
+	char dbf_text[15];
+
+	sprintf(dbf_text,"wtnb%4x",timeout);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	start=qeth_get_millis();
+        for (;;) {
+                set_task_state(current,TASK_INTERRUPTIBLE);
+                if (qeth_get_millis()-start>timeout) {
+                        goto out;
+                }
+                schedule_timeout(((start+timeout-qeth_get_millis())>>10)*HZ);
+        }
+ out:
+	set_task_state(current,TASK_RUNNING);
+}
+
+static 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
+		ip_eth_mc_map(ipm,mac);
+}
+
+#define HEXDUMP16(importance,header,ptr) \
+PRINT_##importance(header "%02x %02x %02x %02x  %02x %02x %02x %02x  " \
+		   "%02x %02x %02x %02x  %02x %02x %02x %02x\n", \
+		   *(((char*)ptr)),*(((char*)ptr)+1),*(((char*)ptr)+2), \
+		   *(((char*)ptr)+3),*(((char*)ptr)+4),*(((char*)ptr)+5), \
+		   *(((char*)ptr)+6),*(((char*)ptr)+7),*(((char*)ptr)+8), \
+		   *(((char*)ptr)+9),*(((char*)ptr)+10),*(((char*)ptr)+11), \
+		   *(((char*)ptr)+12),*(((char*)ptr)+13), \
+		   *(((char*)ptr)+14),*(((char*)ptr)+15)); \
+PRINT_##importance(header "%02x %02x %02x %02x  %02x %02x %02x %02x  " \
+		   "%02x %02x %02x %02x  %02x %02x %02x %02x\n", \
+		   *(((char*)ptr)+16),*(((char*)ptr)+17), \
+		   *(((char*)ptr)+18),*(((char*)ptr)+19), \
+		   *(((char*)ptr)+20),*(((char*)ptr)+21), \
+		   *(((char*)ptr)+22),*(((char*)ptr)+23), \
+		   *(((char*)ptr)+24),*(((char*)ptr)+25), \
+		   *(((char*)ptr)+26),*(((char*)ptr)+27), \
+		   *(((char*)ptr)+28),*(((char*)ptr)+29), \
+		   *(((char*)ptr)+30),*(((char*)ptr)+31));
+
+#define atomic_swap(a,b) xchg((int*)a.counter,b)
+
+#ifdef QETH_DBF_LIKE_HELL
+
+#define my_read_lock(x) do { \
+	void *ptr=x; \
+	QETH_DBF_TEXT6(0,trace,"rd_lck"); \
+	QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+	read_lock(x); \
+} while (0)
+#define my_read_unlock(x) do { \
+	void *ptr=x; \
+	QETH_DBF_TEXT6(0,trace,"rd_unlck"); \
+	QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+	read_unlock(x); \
+} while (0)
+#define my_write_lock(x) do { \
+	void *ptr=x; \
+	QETH_DBF_TEXT6(0,trace,"wr_lck"); \
+	QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+	write_lock(x); \
+} while (0)
+#define my_write_unlock(x) do { \
+	void *ptr=x; \
+	QETH_DBF_TEXT6(0,trace,"wr_unlck"); \
+	QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+	write_unlock(x); \
+} while (0)
+
+#define my_spin_lock(x) do { \
+	void *ptr=x; \
+	QETH_DBF_TEXT6(0,trace,"sp_lck"); \
+	QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+	spin_lock(x); \
+} while (0)
+#define my_spin_unlock(x) do { \
+	void *ptr=x; \
+	QETH_DBF_TEXT6(0,trace,"sp_unlck"); \
+	QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+	spin_unlock(x); \
+} while (0)
+#define my_spin_lock_irqsave(x,y) do { \
+	void *ptr=x; \
+	QETH_DBF_TEXT6(0,trace,"sp_lck_i"); \
+	QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+	spin_lock_irqsave(x,y); \
+} while (0)
+#define my_spin_unlock_irqrestore(x,y) do { \
+	void *ptr=x; \
+	QETH_DBF_TEXT6(0,trace,"sp_nlk_i"); \
+	QETH_DBF_HEX6(0,trace,&ptr,sizeof(void*)); \
+	spin_unlock_irqrestore(x,y); \
+} while (0)
+
+#else /* QETH_DBF_LIKE_HELL */
+
+#define my_read_lock(x) read_lock(x)
+#define my_write_lock(x) write_lock(x)
+#define my_read_unlock(x) read_unlock(x)
+#define my_write_unlock(x) write_unlock(x)
+
+#define my_spin_lock(x) spin_lock(x)
+#define my_spin_unlock(x) spin_unlock(x)
+#define my_spin_lock_irqsave(x,y) spin_lock_irqsave(x,y)
+#define my_spin_unlock_irqrestore(x,y) spin_unlock_irqrestore(x,y)
+
+#endif /* QETH_DBF_LIKE_HELL */
+
+static int inline my_spin_lock_nonbusy(qeth_card_t *card,spinlock_t *lock)
+{
+	for (;;) {
+		if (card) {
+			if (atomic_read(&card->shutdown_phase)) return -1;
+		}
+		if (spin_trylock(lock)) return 0;
+		qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
+	}
+}
+
+#ifdef CONFIG_ARCH_S390X
+#define QETH_GET_ADDR(x) ((__u32)(unsigned long)x)
+#else /* CONFIG_ARCH_S390X */
+#define QETH_GET_ADDR(x) ((__u32)x)
+#endif /* CONFIG_ARCH_S390X */
+
+static int qeth_does_card_exist(qeth_card_t *card)
+{
+	qeth_card_t *c=firstcard;
+	int rc=0;
+
+	my_read_lock(&list_lock);
+	while (c) {
+		if (c==card) {
+			rc=1;
+			break;
+		}
+		c=c->next;
+	}
+	my_read_unlock(&list_lock);
+	return rc;
+}
+
+static inline qeth_card_t *qeth_get_card_by_irq(int irq)
+{
+        qeth_card_t *card;
+
+	my_read_lock(&list_lock);
+        card=firstcard;
+        while (card) {
+                if ((card->irq0==irq)&&
+		    (atomic_read(&card->shutdown_phase)!=
+		     QETH_REMOVE_CARD_QUICK)) break;
+                if ((card->irq1==irq)&&
+		    (atomic_read(&card->shutdown_phase)!=
+		     QETH_REMOVE_CARD_QUICK)) break;
+                if ((card->irq2==irq)&&
+		    (atomic_read(&card->shutdown_phase)!=
+		     QETH_REMOVE_CARD_QUICK)) break;
+                card=card->next;
+        }
+	my_read_unlock(&list_lock);
+
+        return card;
+}
+
+static int qeth_getxdigit(char c)
+{
+        if ((c>='0') && (c<='9')) return c-'0';
+        if ((c>='a') && (c<='f')) return c+10-'a';
+        if ((c>='A') && (c<='F')) return c+10-'A';
+        return -1;
+}
+
+static qeth_card_t *qeth_get_card_by_name(char *name)
+{
+        qeth_card_t *card;
+
+	my_read_lock(&list_lock);
+        card=firstcard;
+        while (card) {
+		if (!strncmp(name,card->dev_name,DEV_NAME_LEN)) break;
+                card=card->next;
+        }
+	my_read_unlock(&list_lock);
+
+        return card;
+}
+
+static void qeth_convert_addr_to_text(int version,__u8 *addr,char *text)
+{
+	if (version==4) {
+		sprintf(text,"%02x%02x%02x%02x",
+			addr[0],addr[1],addr[2],addr[3]);
+	} else {
+		sprintf(text,"%02x%02x%02x%02x%02x%02x%02x%02x" \
+			"%02x%02x%02x%02x%02x%02x%02x%02x",
+			addr[0],addr[1],addr[2],addr[3],
+			addr[4],addr[5],addr[6],addr[7],
+			addr[8],addr[9],addr[10],addr[11],
+			addr[12],addr[13],addr[14],addr[15]);
+	}
+}
+
+static int qeth_convert_text_to_addr(int version,char *text,__u8 *addr)
+{
+	int olen=(version==4)?4:16;
+
+	while (olen--) {
+		if ( (!isxdigit(*text)) || (!isxdigit(*(text+1))) )
+			return -EINVAL;
+		*addr=(qeth_getxdigit(*text)<<4)+qeth_getxdigit(*(text+1));
+		addr++;
+		text+=2;
+	}
+	return 0;
+}
+
+static void qeth_add_ipato_entry(int version,__u8 *addr,int mask_bits,
+				 char *dev_name)
+{
+	ipato_entry_t *entry,*e;
+	int len=(version==4)?4:16;
+
+	entry=(ipato_entry_t*)kmalloc(sizeof(ipato_entry_t),GFP_KERNEL);
+	if (!entry) {
+		PRINT_ERR("not enough memory for ipato allocation\n");
+		return;
+	}
+	entry->version=version;
+	memcpy(entry->addr,addr,len);
+	if (dev_name) {
+		strncpy(entry->dev_name,dev_name,DEV_NAME_LEN);
+		if (qeth_get_card_by_name(dev_name)->options.ena_ipat!=
+		    ENABLE_TAKEOVER)
+			PRINT_WARN("IP takeover is not enabled on %s! " \
+				   "Ignoring line\n",dev_name);
+	} else
+		memset(entry->dev_name,0,DEV_NAME_LEN);
+	entry->mask_bits=mask_bits;
+	entry->next=NULL;
+
+	my_spin_lock(&ipato_list_lock);
+	if (ipato_entries) {
+		e=ipato_entries;
+		while (e) {
+			if ( (e->version==version) &&
+			     (e->mask_bits==mask_bits) &&
+			     ( ((dev_name)&&!strncmp(e->dev_name,dev_name,
+						     DEV_NAME_LEN)) ||
+			       (!dev_name) ) &&
+			     (!memcmp(e->addr,addr,len)) ) {
+				PRINT_INFO("ipato to be added does already " \
+					   "exist\n");
+				kfree(entry);
+				goto out;
+			     }
+			if (e->next) e=e->next; else break;
+		}
+		e->next=entry;
+	} else
+		ipato_entries=entry;
+out:
+	my_spin_unlock(&ipato_list_lock);
+}
+
+static void qeth_del_ipato_entry(int version,__u8 *addr,int mask_bits,
+				 char *dev_name)
+{
+	ipato_entry_t *e,*e_before;
+	int len=(version==4)?4:16;
+	int found=0;
+	
+	my_spin_lock(&ipato_list_lock);
+	e=ipato_entries;
+	if ( (e->version==version) &&
+	     (e->mask_bits==mask_bits) &&
+	     (!memcmp(e->addr,addr,len)) ) {
+		ipato_entries=e->next;
+		kfree(e);
+	} else while (e) {
+		e_before=e;
+		e=e->next;
+		if (!e) break;
+		if ( (e->version==version) &&
+		     (e->mask_bits==mask_bits) &&
+		     ( ((dev_name)&&!strncmp(e->dev_name,dev_name,
+					     DEV_NAME_LEN)) ||
+		       (!dev_name) ) &&
+		     (!memcmp(e->addr,addr,len)) ) {
+			e_before->next=e->next;
+			kfree(e);
+			found=1;
+			break;
+		}
+	}
+	if (!found)
+		PRINT_INFO("ipato to be deleted does not exist\n");
+	my_spin_unlock(&ipato_list_lock);
+}
+
+static void qeth_convert_addr_to_bits(__u8 *addr,char *bits,int len)
+{
+	int i,j;
+	__u8 octet;
+	
+	for (i=0;i<len;i++) {
+		octet=addr[i];
+		for (j=7;j>=0;j--) {
+			bits[i*8+j]=(octet&1)?1:0;
+			octet>>=1;
+		}
+	}
+}
+
+static int qeth_is_ipa_covered_by_ipato_entries(int version,__u8 *addr,
+						qeth_card_t *card)
+{
+	char *memarea,*addr_bits,*entry_bits;
+	int len=(version==4)?4:16;
+	int invert=(version==4)?ipato_inv4:ipato_inv6;
+	int result=0;
+	ipato_entry_t *e;
+
+	if (card->options.ena_ipat!=ENABLE_TAKEOVER) {
+		return 0;
+	}
+
+	memarea=kmalloc(256,GFP_KERNEL);
+	if (!memarea) {
+		PRINT_ERR("not enough memory to check out whether to " \
+			  "use ipato\n");
+		return 0;
+	}
+	addr_bits=memarea;
+	entry_bits=memarea+128;
+	qeth_convert_addr_to_bits(addr,addr_bits,len);
+	e=ipato_entries;
+	while (e) {
+		qeth_convert_addr_to_bits(e->addr,entry_bits,len);
+		if ( (!memcmp(addr_bits,entry_bits,
+			      __min(len*8,e->mask_bits))) &&
+		     ( (e->dev_name[0]&&
+			(!strncmp(e->dev_name,card->dev_name,DEV_NAME_LEN))) ||
+		       (!e->dev_name[0]) ) ) {
+			result=1;
+			break;
+		}
+		e=e->next;
+	}
+
+	kfree(memarea);
+	if (invert)
+		return !result;
+	else
+		return result;
+}
+
+static void qeth_set_dev_flag_running(qeth_card_t *card)
+{
+	if (card) {
+		card->dev->flags|=IFF_RUNNING;
+/*
+		clear_bit(__LINK_STATE_DOWN,&dev->flags);
+*/
+	}
+}
+
+static void qeth_set_dev_flag_norunning(qeth_card_t *card)
+{
+	if (card) {
+		card->dev->flags&=~IFF_RUNNING;
+/*
+		set_bit(__LINK_STATE_DOWN,&dev->flags);
+*/
+	}
+}
+
+static void qeth_restore_dev_flag_state(qeth_card_t *card)
+{
+	if (card) {
+		if (card->saved_dev_flags&IFF_RUNNING)
+			card->dev->flags|=IFF_RUNNING;
+		else
+			card->dev->flags&=~IFF_RUNNING;
+/*
+		if (card->saved_dev_flags&__LINK_STATE_DOWN)
+			set_bit(__LINK_STATE_DOWN,&card->dev->flags);
+		else
+			clear_bit(__LINK_STATE_DOWN,&card->dev->flags);
+*/
+	}
+}
+
+static void qeth_save_dev_flag_state(qeth_card_t *card)
+{
+	if (card) {
+		card->saved_dev_flags=card->dev->flags&IFF_RUNNING;
+/*
+		card->saved_dev_flags=card->dev->flags&__LINK_STATE_DOWN;
+*/
+	}
+}
+
+static inline int netif_is_busy(struct net_device *dev)
+{
+        return(test_bit(__LINK_STATE_XOFF,&dev->flags));
+}
+
+static int qeth_open(struct net_device *dev)
+{
+	char dbf_text[15];
+	qeth_card_t *card;
+
+	card=(qeth_card_t *)dev->priv;
+	sprintf(dbf_text,"open%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	QETH_DBF_TEXT2(0,setup,dbf_text);
+
+	qeth_save_dev_flag_state(card);
+
+	netif_start_queue(dev);
+        if (!atomic_swap(&((qeth_card_t*)dev->priv)->is_open,1)) {
+                MOD_INC_USE_COUNT;
+        }
+	return 0;
+}
+
+static int qeth_set_config(struct net_device *dev,struct ifmap *map)
+{
+	qeth_card_t *card=(qeth_card_t*)dev->priv;
+	char dbf_text[15];
+
+	sprintf(dbf_text,"nscf%04x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	return -EOPNOTSUPP;
+}
+
+static int qeth_is_multicast_skb_at_all(struct sk_buff *skb,int version)
+{
+	int i;
+	if (skb->dst && skb->dst->neighbour) {
+		i=skb->dst->neighbour->type;
+		return ((i==RTN_BROADCAST)||
+			(i==RTN_MULTICAST)||
+			(i==RTN_ANYCAST))?i:0;
+	}
+	/* ok, we've to try it somehow else */
+	if (version==4) {
+		return ((skb->nh.raw[16]&0xf0)==0xe0)?RTN_MULTICAST:0;
+	} else if (version==6) {
+		return (skb->nh.raw[24]==0xff)?RTN_MULTICAST:0;
+	}
+	return 0;
+}
+
+static int qeth_get_prioqueue(qeth_card_t *card,struct sk_buff *skb,
+			      int multicast,int version)
+{
+	if (!version) return QETH_DEFAULT_QUEUE;
+        switch (card->no_queues) {
+        case 1:
+                return 0;
+        case 4:
+		if ( (card->can_do_async_iqd) &&
+		     (card->options.async_iqd==ASYNC_IQD) ) {
+			return card->no_queues-1;
+		}
+		if (card->is_multicast_different) {
+			if (multicast) {
+			    return card->is_multicast_different&
+				    (card->no_queues-1);
+			} else {
+				return 0;
+			}
+		}
+		if (card->options.do_prio_queueing) {
+			if (version==4) {
+				if (card->options.do_prio_queueing==
+				    PRIO_QUEUEING_TOS) {
+					if (skb->nh.iph->tos&
+					    IP_TOS_NOTIMPORTANT) {
+						return 3;
+					}
+					if (skb->nh.iph->tos&
+					    IP_TOS_LOWDELAY) {
+						return 0;
+					}
+					if (skb->nh.iph->tos&
+					    IP_TOS_HIGHTHROUGHPUT) {
+						return 1;
+					}
+					if (skb->nh.iph->tos&
+					    IP_TOS_HIGHRELIABILITY) {
+						return 2;
+					}
+					return QETH_DEFAULT_QUEUE;
+				}
+				if (card->options.do_prio_queueing==
+				    PRIO_QUEUEING_PREC) {
+					return 3-(skb->nh.iph->tos>>6);
+				}
+			} else if (version==6) {
+/********************
+ ********************
+TODO: IPv6!!!
+********************/
+			}
+                        return card->options.default_queue;
+                } else return card->options.default_queue;
+        default:
+                return 0;
+        }
+}
+
+static void qeth_wakeup(qeth_card_t *card) {
+	char dbf_text[15];
+
+	sprintf(dbf_text,"wkup%4x",card->irq0);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+
+        atomic_set(&card->data_has_arrived,1);
+	spin_lock(&card->wait_q_lock);
+	if (atomic_read(&card->wait_q_active)) {
+		wake_up(&card->wait_q);
+	}
+	spin_unlock(&card->wait_q_lock);
+}
+
+static int qeth_check_idx_response(unsigned char *buffer)
+{
+	if (!buffer)
+		return 0;
+        if ((buffer[2]&0xc0)==0xc0) {
+                return -EIO;
+        }
+        return 0;
+}
+
+static int qeth_get_cards_problem(qeth_card_t *card,unsigned char *buffer,
+				  int irq,int dstat,int cstat,int rqparam,
+				  char *irb,char *sense)
+{
+	char dbf_text[15];
+	int problem=0;
+
+	if (atomic_read(&card->shutdown_phase)) return 0;
+	if (dstat&DEV_STAT_UNIT_CHECK) {
+		if (irq==card->irq2) {
+			sprintf(dbf_text,"ACHK%04x",card->irq0);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+			problem=PROBLEM_ACTIVATE_CHECK_CONDITION;
+			goto out;
+		}
+		if (sense[SENSE_RESETTING_EVENT_BYTE]&
+		    SENSE_RESETTING_EVENT_FLAG) {
+			sprintf(dbf_text,"REVN%04x",card->irq0);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+			problem=PROBLEM_RESETTING_EVENT_INDICATOR;
+			goto out;
+		}
+		if (sense[SENSE_COMMAND_REJECT_BYTE]&
+		    SENSE_COMMAND_REJECT_FLAG) {
+			sprintf(dbf_text,"CREJ%04x",card->irq0);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+			problem=PROBLEM_COMMAND_REJECT;
+			goto out;
+		}
+		if ( (sense[2]==0xaf)&&(sense[3]==0xfe) ) {
+			sprintf(dbf_text,"AFFE%04x",card->irq0);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+			problem=PROBLEM_AFFE;
+			goto out;
+		}
+		if ( (!sense[0]) && (!sense[1]) &&
+		     (!sense[2]) && (!sense[3]) ) {
+			sprintf(dbf_text,"ZSNS%04x",card->irq0);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+			problem=PROBLEM_ZERO_SENSE_DATA;
+			goto out;
+		}
+		sprintf(dbf_text,"GCHK%04x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		problem=PROBLEM_GENERAL_CHECK;
+		goto out;
+	}
+	if (cstat& (SCHN_STAT_CHN_CTRL_CHK|SCHN_STAT_INTF_CTRL_CHK|
+		    SCHN_STAT_CHN_DATA_CHK|SCHN_STAT_CHAIN_CHECK|
+		    SCHN_STAT_PROT_CHECK|SCHN_STAT_PROG_CHECK) ) {
+		sprintf(dbf_text,"GCHK%04x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_HEX1(0,misc,irb,__max(QETH_DBF_MISC_LEN,64));
+		PRINT_WARN("check on irq x%x, dstat=x%x, cstat=x%x, " \
+			   "rqparam=x%x\n",irq,dstat,cstat,rqparam);
+		HEXDUMP16(WARN,"irb: ",irb);
+		HEXDUMP16(WARN,"irb: ",((char*)irb)+32);
+		problem=PROBLEM_GENERAL_CHECK;
+		goto out;
+	}
+        if (qeth_check_idx_response(buffer)) {
+                PRINT_WARN("received an IDX TERMINATE on irq 0x%X/0x%X " \
+			   "with cause code 0x%02x%s\n",
+			   card->irq0,card->irq1,buffer[4],
+			   (buffer[4]==0x22)?" -- try another portname":"");
+		sprintf(dbf_text,"RTRM%04x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+                problem=PROBLEM_RECEIVED_IDX_TERMINATE;
+		goto out;
+        }
+        if (IS_IPA(buffer) && !IS_IPA_REPLY(buffer)) {
+                if ( *(PDU_ENCAPSULATION(buffer))==IPA_CMD_STOPLAN ) {
+			atomic_set(&card->is_startlaned,0);
+			/* we don't do a  netif_stop_queue(card->dev);
+			   we better discard all packets --
+			   the outage could take longer */
+			PRINT_WARN("Link failure on %s (CHPID 0x%X) -- " \
+				   "there is a network problem or someone " \
+				   "pulled the cable or disabled the port."
+				   "Discarding outgoing packets.\n",
+				   card->dev_name,card->chpid);
+			sprintf(dbf_text,"CBOT%04x",card->irq0);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+			qeth_set_dev_flag_norunning(card);
+                        problem=0;
+			goto out;
+                }
+		/* we checked for buffer!=0 in IS_IPA */
+                if ( *(PDU_ENCAPSULATION(buffer))==IPA_CMD_STARTLAN ) {
+			if (!atomic_read(&card->is_startlaned)) {
+				atomic_set(&card->is_startlaned,1);
+				problem=PROBLEM_CARD_HAS_STARTLANED;
+			}
+			goto out;
+                }
+		if ( *(PDU_ENCAPSULATION(buffer))==
+		     IPA_CMD_REGISTER_LOCAL_ADDR ) {
+			sprintf(dbf_text,"irla%04x",card->irq0);
+			QETH_DBF_TEXT3(0,trace,dbf_text);
+		}
+		if ( *(PDU_ENCAPSULATION(buffer))==
+		     IPA_CMD_UNREGISTER_LOCAL_ADDR ) {
+			sprintf(dbf_text,"irla%04x",card->irq0);
+			QETH_DBF_TEXT3(0,trace,dbf_text);
+		}
+                PRINT_WARN("probably a problem on %s: received data is " \
+			   "IPA, but not a reply: command=0x%x\n",
+			   card->dev_name,*(PDU_ENCAPSULATION(buffer)+1));
+		sprintf(dbf_text,"INRP%04x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		goto out;
+        }
+        /* no probs */
+out:
+	if (problem) {
+		sprintf(dbf_text,"gcpr%4x",card->irq0);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		sprintf(dbf_text,"%2x%2x%4x",dstat,cstat,problem);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		sprintf(dbf_text,"%8x",rqparam);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		if (buffer)
+			QETH_DBF_HEX3(0,trace,&buffer,sizeof(void*));
+		QETH_DBF_HEX3(0,trace,&irb,sizeof(void*));
+		QETH_DBF_HEX3(0,trace,&sense,sizeof(void*));
+	}
+	atomic_set(&card->problem,problem);
+	return problem;
+}
+
+
+static void qeth_issue_next_read(qeth_card_t *card)
+{
+        int result,result2;
+	char dbf_text[15];
+
+	sprintf(dbf_text,"isnr%04x",card->irq0);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+
+        /* set up next read ccw */
+        memcpy(&card->dma_stuff->read_ccw,READ_CCW,sizeof(ccw1_t));
+        card->dma_stuff->read_ccw.count=QETH_BUFSIZE;
+        /* recbuf is not yet used by read channel program */
+        card->dma_stuff->read_ccw.cda=QETH_GET_ADDR(card->dma_stuff->recbuf);
+
+	/* we don't s390irq_spin_lock_irqsave(card->irq0,flags), as
+	   we are only called in the interrupt handler */
+        result=do_IO(card->irq0,&card->dma_stuff->read_ccw,
+		     MPC_SETUP_STATE,0,0);
+        if (result) {
+		qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+                result2=do_IO(card->irq0,&card->dma_stuff->read_ccw,
+			      MPC_SETUP_STATE,0,0);
+                PRINT_WARN("read handler on irq x%x, read: do_IO " \
+                           "returned %i, next try returns %i\n",
+                           card->irq0,result,result2);
+		sprintf(dbf_text,"IsNR%04x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		sprintf(dbf_text,"%04x%04x",(__s16)result,(__s16)result2);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+        }
+}
+
+static int qeth_is_to_recover(qeth_card_t *card,int problem)
+{
+        switch (problem) {
+        case PROBLEM_CARD_HAS_STARTLANED:
+                return 1;
+        case PROBLEM_RECEIVED_IDX_TERMINATE:
+                if (atomic_read(&card->in_recovery)) {
+			return 1;
+		} else {
+			qeth_set_dev_flag_norunning(card);
+			return 0;
+		}
+	case PROBLEM_ACTIVATE_CHECK_CONDITION:
+		return 1;
+	case PROBLEM_RESETTING_EVENT_INDICATOR:
+		return 1;
+	case PROBLEM_COMMAND_REJECT:
+		return 0;
+	case PROBLEM_ZERO_SENSE_DATA:
+		return 0;
+	case PROBLEM_GENERAL_CHECK:
+		return 1;
+	case PROBLEM_BAD_SIGA_RESULT:
+		return 1;
+	case PROBLEM_USER_TRIGGERED_RECOVERY:
+		return 1;
+	case PROBLEM_AFFE:
+		return 1;
+	case PROBLEM_MACHINE_CHECK:
+		return 1;
+	case PROBLEM_TX_TIMEOUT:
+		return 1;
+        }
+        return 0;
+}
+
+static int qeth_wait_for_event(atomic_t *var,unsigned int timeout)
+{
+        unsigned int start;
+	int retval;
+	char dbf_text[15];
+
+	QETH_DBF_TEXT5(0,trace,"wait4evn");
+	sprintf(dbf_text,"%08x",timeout);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+	QETH_DBF_HEX5(0,trace,&var,sizeof(void*));
+
+        start=qeth_get_millis();
+        for (;;) {
+                set_task_state(current,TASK_INTERRUPTIBLE);
+                if (atomic_read(var)) {
+			retval=0;
+			goto out;
+		}
+                if (qeth_get_millis()-start>timeout) {
+			retval=-ETIME;
+			goto out;
+		}
+                schedule_timeout(((start+timeout-qeth_get_millis())>>10)*HZ);
+        }
+ out:
+	set_task_state(current,TASK_RUNNING);
+
+	return retval;
+}
+
+static int qeth_get_spare_buf(void)
+{
+	int i=0;
+	char dbf_text[15];
+
+	while (i<sparebuffer_count) {
+		if (!atomic_compare_and_swap(SPAREBUF_FREE,SPAREBUF_USED,
+					     &sparebufs[i].status)) {
+			sprintf(dbf_text,"gtspb%3x",i);
+			QETH_DBF_TEXT4(0,trace,dbf_text);
+			return i;
+		}
+		i++;
+	}
+	QETH_DBF_TEXT3(0,trace,"nospbuf");
+
+	return -1;
+}
+
+static void qeth_put_spare_buf(int no)
+{
+	char dbf_text[15];
+	
+	sprintf(dbf_text,"ptspb%3x",no);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+	atomic_set(&sparebufs[no].status, SPAREBUF_FREE);
+}
+
+static inline void qeth_put_buffer_pool_entry(qeth_card_t *card,int entry_no)
+{
+	if (entry_no&SPAREBUF_MASK)
+		qeth_put_spare_buf(entry_no&(~SPAREBUF_MASK));
+	else
+		card->inbound_buffer_pool_entry_used[entry_no]=BUFFER_UNUSED;
+}
+
+static inline int qeth_get_empty_buffer_pool_entry(qeth_card_t *card)
+{
+	int i;
+	int max_buffers=card->options.inbound_buffer_count;
+	
+	for (i=0;i<max_buffers;i++) {
+		if (xchg((int*)&card->inbound_buffer_pool_entry_used[i],
+			 BUFFER_USED)==BUFFER_UNUSED) return i;
+	}
+	return -1;
+}
+
+static inline void qeth_clear_input_buffer(qeth_card_t *card,int bufno)
+{
+	qdio_buffer_t *buffer;
+	int i;
+	int elements,el_m_1;
+	void *ptr;
+#ifdef QETH_DBF_LIKE_HELL
+	char dbf_text[15];
+
+	sprintf(dbf_text,"clib%4x",card->irq0);
+	QETH_DBF_TEXT6(0,trace,dbf_text);
+	sprintf(dbf_text,"bufno%3x",bufno);
+	QETH_DBF_TEXT6(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+
+	buffer=&card->inbound_qdio_buffers[bufno];
+	elements=BUFFER_MAX_ELEMENTS;
+	el_m_1=elements-1;
+
+	for (i=0;i<elements;i++) {
+		if (i==el_m_1)
+			buffer->element[i].flags=SBAL_FLAGS_LAST_ENTRY;
+		else
+			buffer->element[i].flags=0;
+
+		buffer->element[i].length=PAGE_SIZE;
+		ptr=INBOUND_BUFFER_POS(card,bufno,i);
+		if (card->do_pfix) {
+			/* we assume here, that ptr&(PAGE_SIZE-1)==0 */
+			buffer->element[i].addr=(void *)pfix_get_page_addr(ptr);
+			card->real_inb_buffer_addr[bufno][i]=ptr;
+		} else {
+			buffer->element[i].addr=ptr;
+		}
+	}
+}
+
+static 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;
+	int cnt1,cnt2=0;
+	int wrapped=0;
+	int i;
+	int requeue_counter;
+	char dbf_text[15];
+	int no;
+
+#ifdef QETH_DBF_LIKE_HELL
+	sprintf(dbf_text,"qibf%4x",card->irq0);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+	sprintf(dbf_text,"%4x%4x",under_int,bufno);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+	atomic_inc(&card->requeue_counter);
+	if (atomic_read(&card->requeue_counter) > QETH_REQUEUE_THRESHOLD) {
+		if (!spin_trylock(&card->requeue_input_lock)) {
+#ifndef QETH_DBF_LIKE_HELL
+			sprintf(dbf_text,"qibl%4x",card->irq0);
+			QETH_DBF_TEXT5(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+			return;
+		}
+		requeue_counter=atomic_read(&card->requeue_counter);
+		pos=atomic_read(&card->requeue_position);
+
+		start=pos;
+		/* omit the situation with 128 simultaneously
+		   enqueued buffers, as then we can't benefit from PCI
+		   avoidance anymore -- therefore we let count not grow as
+		   big as requeue_counter */
+		while ( (!atomic_read(&card->inbound_buffer_refcnt[pos])) &&
+			(count<requeue_counter-1) ) {
+			no=qeth_get_empty_buffer_pool_entry(card);
+			if (no==-1) {
+				if (count) break;
+				no=qeth_get_spare_buf();
+				if (no==-1) {
+					PRINT_ERR("%s: no more input "\
+						  "buffers available! " \
+						  "Inbound traffic could " \
+						  "be lost! Try to spend " \
+						  "more memory for qeth\n",
+						  card->dev_name);
+					sprintf(dbf_text,"QINB%4x",card->irq0);
+					QETH_DBF_TEXT2(1,trace,dbf_text);
+					goto out;
+				}
+				card->inbound_buffer_entry_no[pos]=
+					no|SPAREBUF_MASK;
+			}
+			card->inbound_buffer_entry_no[pos]=no;
+			atomic_set(&card->inbound_buffer_refcnt[pos],1);
+			count++;
+			if (pos>=QDIO_MAX_BUFFERS_PER_Q-1) {
+				pos=0;
+				wrapped=1;
+			} else pos++;
+		}
+		/* stop points to the position after the last element */
+		stop=pos;
+
+#ifdef QETH_DBF_LIKE_HELL
+		sprintf(dbf_text,"qibi%4x",card->irq0);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		sprintf(dbf_text,"%4x",requeue_counter);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		sprintf(dbf_text,"%4x%4x",start,stop);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+		if (wrapped) {
+			cnt1=QDIO_MAX_BUFFERS_PER_Q-start;
+			cnt2=stop;
+		} else {
+			cnt1=count;
+			/* cnt2 is already set to 0 */
+		}
+
+		atomic_sub(count,&card->requeue_counter);
+		/* this is the only place where card->requeue_position is
+		   written to, so that's ok (as it is in a lock) */
+		atomic_set(&card->requeue_position,
+			   (atomic_read(&card->requeue_position)+count)
+			   &(QDIO_MAX_BUFFERS_PER_Q-1));
+
+		if (cnt1) {
+			for (i=start;i<start+cnt1;i++) {
+				qeth_clear_input_buffer(card,i);
+			}
+			result=do_QDIO(card->irq2,
+				       QDIO_FLAG_SYNC_INPUT|under_int,
+				       0,start,cnt1,
+				       NULL);
+			if (result) {
+				PRINT_WARN("qeth_queue_input_buffer's " \
+			    		   "do_QDIO returnd %i " \
+		    			   "(irq 0x%x)\n",
+	    				   result,card->irq2);
+				sprintf(dbf_text,"QIDQ%4x",card->irq0);
+				QETH_DBF_TEXT1(0,trace,dbf_text);
+				sprintf(dbf_text,"%4x%4x",result,
+					requeue_counter);
+				QETH_DBF_TEXT1(0,trace,dbf_text);
+				sprintf(dbf_text,"%4x%4x",start,cnt1);
+				QETH_DBF_TEXT1(1,trace,dbf_text);
+			}
+		}
+		if (cnt2) {
+			for (i=0;i<cnt2;i++) {
+				qeth_clear_input_buffer(card,i);
+			}
+			result=do_QDIO(card->irq2,
+				       QDIO_FLAG_SYNC_INPUT|under_int,0,
+				       0,cnt2,
+				       NULL);
+			if (result) {
+				PRINT_WARN("qeth_queue_input_buffer's " \
+			    		   "do_QDIO returnd %i " \
+		    			   "(irq 0x%x)\n",
+	    				   result,card->irq2);
+				sprintf(dbf_text,"QIDQ%4x",card->irq0);
+				QETH_DBF_TEXT1(0,trace,dbf_text);
+				sprintf(dbf_text,"%4x%4x",result,
+					requeue_counter);
+				QETH_DBF_TEXT1(0,trace,dbf_text);
+				sprintf(dbf_text,"%4x%4x",0,cnt2);
+				QETH_DBF_TEXT1(1,trace,dbf_text);
+			}
+		}
+out:
+		my_spin_unlock(&card->requeue_input_lock);
+	}
+
+}
+
+static inline struct sk_buff *qeth_get_skb(unsigned int len)
+{
+	struct sk_buff *skb;
+
+#ifdef QETH_VLAN
+	skb=dev_alloc_skb(len+VLAN_HLEN);
+	if (skb) skb_reserve(skb,VLAN_HLEN);
+#else /* QETH_VLAN */
+	skb=dev_alloc_skb(len);
+#endif /* QETH_VLAN */
+	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)
+{
+	int length;
+	char *data_ptr;
+	int step,len_togo,element,pos_in_el;
+	int curr_len;
+	int max_elements;
+	struct sk_buff *skb;
+	char dbf_text[15];
+
+	max_elements=BUFFER_MAX_ELEMENTS;
+
+#define SBALE_LEN(x) ((x>=max_elements)?0:(buffer->element[x].length))
+#define SBALE_ADDR(x) (buffer->element[x].addr)
+
+	element=*element_ptr;
+
+	if (element>=max_elements) {
+		PRINT_WARN("irq 0x%x: error in interpreting buffer (data " \
+			   "too long), %i elements.\n",card->irq0,element);
+		sprintf(dbf_text,"IEDL%4x",card->irq0);
+		QETH_DBF_TEXT0(0,trace,dbf_text);
+		sprintf(dbf_text,"%4x%4x",*element_ptr,*pos_in_el_ptr);
+		QETH_DBF_TEXT0(1,trace,dbf_text);
+		QETH_DBF_HEX0(0,misc,buffer,QETH_DBF_MISC_LEN);
+		QETH_DBF_HEX0(0,misc,buffer+QETH_DBF_MISC_LEN,
+			      QETH_DBF_MISC_LEN);
+		return NULL;
+	}
+
+	pos_in_el=*pos_in_el_ptr;
+
+	curr_len=SBALE_LEN(element);
+	if (curr_len>PAGE_SIZE) {
+		PRINT_WARN("irq 0x%x: bad element length in element %i: " \
+			   "0x%x\n",card->irq0,element,curr_len);
+		sprintf(dbf_text,"BELN%4x",card->irq0);
+		QETH_DBF_TEXT0(0,trace,dbf_text);
+		sprintf(dbf_text,"%4x",curr_len);
+		QETH_DBF_TEXT0(0,trace,dbf_text);
+		sprintf(dbf_text,"%4x%4x",*element_ptr,*pos_in_el_ptr);
+		QETH_DBF_TEXT0(1,trace,dbf_text);
+		QETH_DBF_HEX0(0,misc,buffer,QETH_DBF_MISC_LEN);
+		QETH_DBF_HEX0(0,misc,buffer+QETH_DBF_MISC_LEN,
+			      QETH_DBF_MISC_LEN);
+		return NULL;
+	}
+	/* header fits in current element? */
+	if (curr_len<pos_in_el+QETH_HEADER_SIZE) {
+		if (!pos_in_el) {
+#ifdef QETH_DBF_LIKE_HELL
+			sprintf(dbf_text,"gnmh%4x",card->irq0);
+			QETH_DBF_TEXT6(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+			return NULL; /* no more data in buffer */
+		}
+		/* set hdr to next element */
+		element++;
+		pos_in_el=0;
+		curr_len=SBALE_LEN(element);
+		/* does it fit in there? */
+		if (curr_len<QETH_HEADER_SIZE) {
+#ifdef QETH_DBF_LIKE_HELL
+			sprintf(dbf_text,"gdnf%4x",card->irq0);
+			QETH_DBF_TEXT6(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+			return NULL;
+		}
+	}
+
+	*hdr_ptr=SBALE_ADDR(element)+pos_in_el;
+
+	length=*(__u16*)((char*)(*hdr_ptr)+QETH_HEADER_LEN_POS);
+
+#ifdef QETH_DBF_LIKE_HELL
+	sprintf(dbf_text,"gnHd%4x",card->irq0);
+	QETH_DBF_TEXT6(0,trace,dbf_text);
+	QETH_DBF_HEX6(0,trace,hdr_ptr,sizeof(void*));
+#endif /* QETH_DBF_LIKE_HELL */
+
+	pos_in_el+=QETH_HEADER_SIZE;
+	if (curr_len<=pos_in_el) {
+		/* switch to next element for data */
+		pos_in_el=0;
+		element++;
+		curr_len=SBALE_LEN(element);
+		if (!curr_len) {
+			PRINT_WARN("irq 0x%x: inb. buffer with more headers " \
+				   "than data areas (%i elements).\n",
+				   card->irq0,element);
+			sprintf(dbf_text,"IEMH%4x",card->irq0);
+			QETH_DBF_TEXT0(0,trace,dbf_text);
+			sprintf(dbf_text,"%2x%2x%4x",element,*element_ptr,
+				*pos_in_el_ptr);
+			QETH_DBF_TEXT0(1,trace,dbf_text);
+			QETH_DBF_HEX0(0,misc,buffer,QETH_DBF_MISC_LEN);
+			QETH_DBF_HEX0(0,misc,buffer+QETH_DBF_MISC_LEN,
+				      QETH_DBF_MISC_LEN);
+			return NULL;
+		}
+	}
+
+	data_ptr=SBALE_ADDR(element)+pos_in_el;
+
+	if (card->options.fake_ll==FAKE_LL) {
+		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;
+	}
+
+	if (card->easy_copy_cap)
+		memcpy(skb_put(skb,length),data_ptr,length);
+
+#ifdef QETH_DBF_LIKE_HELL
+	QETH_DBF_HEX6(0,trace,&data_ptr,sizeof(void*));
+	QETH_DBF_HEX6(0,trace,&skb,sizeof(void*));
+#endif /* QETH_DBF_LIKE_HELL */
+
+	len_togo=length;
+	while (1) {
+		step=qeth_min(len_togo,curr_len-pos_in_el);
+		if (!step) {
+			PRINT_WARN("irq 0x%x: unexpected end of buffer, " \
+				   "length of element %i is 0. Discarding " \
+				   "packet.\n",card->irq0,element);
+			sprintf(dbf_text,"IEUE%4x",card->irq0);
+			QETH_DBF_TEXT0(0,trace,dbf_text);
+			sprintf(dbf_text,"%2x%2x%4x",element,*element_ptr,
+				*pos_in_el_ptr);
+			QETH_DBF_TEXT0(0,trace,dbf_text);
+			sprintf(dbf_text,"%4x%4x",len_togo,step);
+			QETH_DBF_TEXT0(0,trace,dbf_text);
+			sprintf(dbf_text,"%4x%4x",curr_len,pos_in_el);
+			QETH_DBF_TEXT0(1,trace,dbf_text);
+			QETH_DBF_HEX0(0,misc,buffer,QETH_DBF_MISC_LEN);
+			QETH_DBF_HEX0(0,misc,buffer+QETH_DBF_MISC_LEN,
+				      QETH_DBF_MISC_LEN);
+			dev_kfree_skb_irq(skb);
+			return NULL;
+		}
+		if (!card->easy_copy_cap)
+			memcpy(skb_put(skb,step),data_ptr,step);
+		len_togo-=step;
+		if (len_togo) {
+			pos_in_el=0;
+			element++;
+			curr_len=SBALE_LEN(element);
+			data_ptr=SBALE_ADDR(element);
+		} else {
+#ifdef QETH_INBOUND_PACKING_1_PACKET_PER_SBALE
+			element++;
+			/* we don't need to calculate curr_len */
+			pos_in_el=0;
+#else /* QETH_INBOUND_PACKING_1_PACKET_PER_SBALE */
+			pos_in_el+=step;
+#endif /* QETH_INBOUND_PACKING_1_PACKET_PER_SBALE */
+			break;
+		}
+	}
+
+#ifdef QETH_DBF_LIKE_HELL
+	sprintf(dbf_text,"%4x%4x",element,pos_in_el);
+	QETH_DBF_TEXT6(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+
+	*element_ptr=element;
+	*pos_in_el_ptr=pos_in_el;
+
+	return skb;
+
+nomem:
+	if (net_ratelimit()) {
+		PRINT_WARN("no memory for packet from %s\n",card->dev_name);
+	}
+	sprintf(dbf_text,"NOMM%4x",card->irq0);
+	QETH_DBF_TEXT0(0,trace,dbf_text);
+	return NULL;
+}
+
+static void qeth_transform_outbound_addrs(qeth_card_t *card,
+					  qdio_buffer_t *buffer)
+{
+	int i;
+	void *ptr;
+
+	if (card->do_pfix) {
+		for (i=0;i<QDIO_MAX_ELEMENTS_PER_BUFFER;i++) {
+			ptr=buffer->element[i].addr;
+			buffer->element[i].addr=(void *)pfix_get_addr(ptr);
+		}
+	}
+}
+static void qeth_get_linux_addrs_for_buffer(qeth_card_t *card,int buffer_no)
+{
+	int i;
+	void *ptr;
+
+	if (card->do_pfix) {
+		for (i=0;i<QDIO_MAX_ELEMENTS_PER_BUFFER;i++) {
+			ptr=card->inbound_qdio_buffers[buffer_no].
+				element[i].addr;
+			card->inbound_qdio_buffers[buffer_no].element[i].addr=
+				card->real_inb_buffer_addr[buffer_no][i]+
+				((unsigned long)ptr&(PAGE_SIZE-1));
+		}
+	}
+}
+
+static void qeth_read_in_buffer(qeth_card_t *card,int buffer_no)
+{
+        struct sk_buff *skb;
+        void *hdr_ptr;
+	int element=0,pos_in_el=0;
+	int version;
+	qdio_buffer_t *buffer;
+	unsigned short cast_type;
+#ifdef QETH_VLAN
+	__u16 *vlan_tag;
+#endif
+	int i;
+	int max_elements;
+	char dbf_text[15];
+	struct net_device *dev;
+
+	dev=card->dev;
+	max_elements=BUFFER_MAX_ELEMENTS;
+
+	buffer=&card->inbound_qdio_buffers[buffer_no];
+
+	/* inform about errors */
+	if (buffer->element[15].flags&0xff) {
+		PRINT_WARN("on irq 0x%x: incoming SBALF 15 on buffer " \
+			   "0x%x are 0x%x\n",card->irq0,buffer_no,
+			   buffer->element[15].flags&0xff);
+		sprintf(dbf_text,"SF##%2x%2x",buffer_no,
+			buffer->element[15].flags&0xff);
+		*((__u16*)(&dbf_text[2]))=(__u16)card->irq0;
+		QETH_DBF_HEX1(1,trace,dbf_text,QETH_DBF_TRACE_LEN);
+	}
+
+	for (i=0;i<max_elements-1;i++) {
+		if (buffer->element[i].flags&SBAL_FLAGS_LAST_ENTRY) {
+			buffer->element[i+1].length=0;
+			break;
+		}
+	}
+#ifdef QETH_PERFORMANCE_STATS
+	card->perf_stats.bufs_rec++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+#ifdef QETH_DBF_LIKE_HELL
+	sprintf(dbf_text,"ribX%4x",card->irq0);
+	dbf_text[3]=buffer_no;
+	QETH_DBF_HEX6(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+#endif /* QETH_DBF_LIKE_HELL */
+
+        while ((skb=qeth_get_next_skb(card,&element,&pos_in_el,
+				      &hdr_ptr,buffer))) {
+
+#ifdef QETH_PERFORMANCE_STATS
+		card->perf_stats.skbs_rec++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+		if (skb) {
+			skb->dev=dev;
+
+#ifdef QETH_IPV6
+			if ( (*(__u16 *)(hdr_ptr))&(QETH_HEADER_PASSTHRU) ) {
+				skb->protocol=card->type_trans(skb,dev);
+			} else
+#endif /* QETH_IPV6 */
+			{
+				version=((*(__u16 *)(hdr_ptr))&
+					 (QETH_HEADER_IPV6))?6:4;
+				skb->protocol=htons((version==4)?ETH_P_IP:
+						    (version==6)?ETH_P_IPV6:
+						    ETH_P_ALL);
+				cast_type=(*(__u16 *)(hdr_ptr))&
+					(QETH_CAST_FLAGS);
+				if (cast_type==QETH_CAST_UNICAST) {
+					skb->pkt_type=PACKET_HOST;
+				} else if (cast_type==QETH_CAST_MULTICAST) {
+					skb->pkt_type=PACKET_MULTICAST;
+				} else if (cast_type==QETH_CAST_BROADCAST) {
+					skb->pkt_type=PACKET_BROADCAST;
+				} else if ( (cast_type==QETH_CAST_ANYCAST) ||
+					    (cast_type==QETH_CAST_NOCAST) ) {
+					sprintf(dbf_text,"ribf%4x",card->irq0);
+					QETH_DBF_TEXT2(0,trace,dbf_text);
+					sprintf(dbf_text,"castan%2x",cast_type);
+					QETH_DBF_TEXT2(1,trace,dbf_text);
+					skb->pkt_type=PACKET_HOST;
+				} else {
+					PRINT_WARN("adapter is using an " \
+						   "unknown casting value " \
+						   "of 0x%x. Using " \
+						   "unicasting instead.\n",
+						   cast_type);
+					skb->pkt_type=PACKET_HOST;
+					sprintf(dbf_text,"ribf%4x",card->irq0);
+					QETH_DBF_TEXT2(0,trace,dbf_text);
+					sprintf(dbf_text,"castun%2x",cast_type);
+					QETH_DBF_TEXT2(1,trace,dbf_text);
+				}
+
+		if (card->options.fake_ll==FAKE_LL) {
+			skb->mac.raw=skb->data-QETH_FAKE_LL_LEN;
+			if (skb->pkt_type==PACKET_MULTICAST) {
+				switch (skb->protocol) {
+#ifdef QETH_IPV6
+				case __constant_htons(ETH_P_IPV6):
+					ndisc_mc_map((struct in6_addr *)
+					skb->data+QETH_FAKE_LL_V6_ADDR_POS,
+						skb->mac.raw+
+				   		QETH_FAKE_LL_DEST_MAC_POS,
+			   			card->dev,0);
+					break;
+#endif /* QETH_IPV6 */
+				case __constant_htons(ETH_P_IP):
+					qeth_get_mac_for_ipm(*(__u32*)
+					skb->data+QETH_FAKE_LL_V4_ADDR_POS,
+						skb->mac.raw+
+				   		QETH_FAKE_LL_DEST_MAC_POS,
+			   			card->dev);
+					break;
+				default:
+					memcpy(skb->mac.raw+
+					       QETH_FAKE_LL_DEST_MAC_POS,
+					       card->dev->dev_addr,
+					       QETH_FAKE_LL_ADDR_LEN);
+				}
+			} else if (skb->pkt_type==PACKET_BROADCAST) {
+				memset(skb->mac.raw+
+				       QETH_FAKE_LL_DEST_MAC_POS,0xff,
+				       QETH_FAKE_LL_ADDR_LEN);
+			} else {
+				memcpy(skb->mac.raw+
+				       QETH_FAKE_LL_DEST_MAC_POS,
+				       card->dev->dev_addr,
+				       QETH_FAKE_LL_ADDR_LEN);
+			}
+			if (*(__u8*)(hdr_ptr+11)&
+			    QETH_EXT_HEADER_SRC_MAC_ADDRESS) {
+				memcpy(skb->mac.raw+
+				       QETH_FAKE_LL_SRC_MAC_POS,
+			       hdr_ptr+QETH_FAKE_LL_SRC_MAC_POS_IN_QDIO_HDR,
+				       QETH_FAKE_LL_ADDR_LEN);
+			} else {
+				/* clear source MAC for security reasons */
+				memset(skb->mac.raw+
+				       QETH_FAKE_LL_DEST_MAC_POS,0,
+				       QETH_FAKE_LL_ADDR_LEN);
+			}
+			memcpy(skb->mac.raw+
+			       QETH_FAKE_LL_PROT_POS,
+			       &skb->protocol,
+			       QETH_FAKE_LL_PROT_LEN);
+		} else {
+			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
+				 * the QDIO header, not pushed in the
+				 * 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 */
+					skb->csum=*(__u16*)
+						(&skb->data[ip_len+
+						 QETH_UDP_CSUM_OFFSET]);
+				} else {
+					/* get the TCP checksum */
+					skb->csum=*(__u16*)
+						(&skb->data[ip_len+
+						 QETH_TCP_CSUM_OFFSET]);
+				}
+			} else {
+				/* make the stack check it */
+				skb->ip_summed=SW_CHECKSUMMING;
+			}
+		}
+
+#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				
+			}
+
+#ifdef QETH_PERFORMANCE_STATS
+			card->perf_stats.inbound_time+=
+				NOW-card->perf_stats.inbound_start_time;
+			card->perf_stats.inbound_cnt++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+#ifdef QETH_DBF_LIKE_HELL
+			sprintf(dbf_text,"rxpk%4x",card->irq0);
+			QETH_DBF_TEXT6(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+
+			netif_rx(skb);
+
+			card->stats->rx_packets++;
+			card->stats->rx_bytes+=skb->len;
+		} else {
+			PRINT_WARN("%s: dropped packet, no buffers " \
+				   "available.\n",card->dev_name);
+			sprintf(dbf_text,"DROP%4x",card->irq0);
+			QETH_DBF_TEXT2(1,trace,dbf_text);
+			card->stats->rx_dropped++;
+		}
+        }
+	atomic_set(&card->inbound_buffer_refcnt[buffer_no],0);
+	qeth_put_buffer_pool_entry(card,card->inbound_buffer_entry_no[
+				   buffer_no]);
+}
+
+static 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
+
+	hdr->id=1;
+	hdr->ext_flags=0;
+
+#ifdef QETH_VLAN
+        /* before we're going to overwrite 
+	   this location with next hop ip 
+	*/
+	card = (qeth_card_t *)skb->dev->priv;
+	if ((card->vlangrp != NULL) && 
+	    (version == 4)          &&
+	    vlan_tx_tag_present(skb)) 
+	{
+		hdr->ext_flags = QETH_EXT_HEADER_VLAN_FRAME;
+		hdr->vlan_id  = vlan_tx_tag_get(skb);
+	}
+#endif
+
+	hdr->length=skb->len-QETH_HEADER_SIZE; /* as skb->len includes
+						  the header now */
+
+	/* yes, I know this is doubled code, but a small little bit
+	   faster maybe */
+	if (version==4) { /* IPv4 */
+		if (multicast==RTN_MULTICAST) {
+			hdr->flags=QETH_CAST_MULTICAST;
+		} else if (multicast==RTN_BROADCAST) {
+			hdr->flags=QETH_CAST_BROADCAST;
+		} else {
+			hdr->flags=QETH_CAST_UNICAST;
+		}
+		*((__u32*)(&hdr->dest_addr[0]))=0;
+		*((__u32*)(&hdr->dest_addr[4]))=0;
+		*((__u32*)(&hdr->dest_addr[8]))=0;
+		if ((skb->dst) && (skb->dst->neighbour)) {
+		*((__u32*)(&hdr->dest_addr[12]))=
+			       *((__u32*)skb->dst->neighbour->primary_key);
+		} else {
+			/* fill in destination address used
+			 * in ip header */
+			*((__u32*)(&hdr->dest_addr[12]))=
+				skb->nh.iph->daddr;
+		}
+	} else if (version==6) { /* IPv6 or passthru */
+		if (multicast==RTN_MULTICAST) {
+			hdr->flags=QETH_CAST_MULTICAST|
+				QETH_HEADER_PASSTHRU|
+				QETH_HEADER_IPV6;
+		} else if (multicast==RTN_ANYCAST) {
+			hdr->flags=QETH_CAST_ANYCAST|
+				QETH_HEADER_PASSTHRU|
+				QETH_HEADER_IPV6;
+		} else if (multicast==RTN_BROADCAST) {
+			hdr->flags=QETH_CAST_BROADCAST|
+				QETH_HEADER_PASSTHRU|
+				QETH_HEADER_IPV6;
+		} else { /* default: RTN_UNICAST */
+			hdr->flags=QETH_CAST_UNICAST|
+				QETH_HEADER_PASSTHRU|
+				QETH_HEADER_IPV6;
+		}
+
+		if ((skb->dst) && (skb->dst->neighbour)) {
+			memcpy(hdr->dest_addr,
+			       skb->dst->neighbour->primary_key,16);
+		} else {
+			/* fill in destination address used
+			 * in ip header */
+			memcpy(hdr->dest_addr,
+			       &skb->nh.ipv6h->daddr,16);
+		}
+	} else { /* passthrough */
+		if (!memcmp(skb->data+QETH_HEADER_SIZE,
+			    skb->dev->broadcast,6)) { /* broadcast? */
+			hdr->flags=QETH_CAST_BROADCAST|QETH_HEADER_PASSTHRU;
+		} else {
+			hdr->flags=QETH_CAST_UNICAST|QETH_HEADER_PASSTHRU;
+		}
+	}
+#ifdef QETH_DBF_LIKE_HELL
+	sprintf(dbf_text,"filhdr%2x",version);
+	QETH_DBF_TEXT6(0,trace,dbf_text);
+	sprintf(dbf_text,"%2x",multicast);
+	QETH_DBF_TEXT6(0,trace,dbf_text);
+	QETH_DBF_HEX6(0,trace,&skb,sizeof(void*));
+	QETH_DBF_HEX6(0,trace,&skb->data,sizeof(void*));
+	QETH_DBF_HEX6(0,misc,hdr,__max(QETH_HEADER_SIZE,QETH_DBF_MISC_LEN));
+	QETH_DBF_HEX6(0,data,skb->data,
+		      __max(QETH_DBF_DATA_LEN,QETH_DBF_DATA_LEN));
+#endif /* QETH_DBF_LIKE_HELL */
+}
+
+static int inline qeth_fill_buffer(qdio_buffer_t *buffer,char *dataptr,
+		       		   int length,int element)
+{
+	int length_here;
+	int first_lap=1;
+#ifdef QETH_DBF_LIKE_HELL
+	char dbf_text[15];
+#endif /* QETH_DBF_LIKE_HELL */
+	int first_element=element;
+
+	while (length>0) {
+		/* length_here is the remaining amount of data in this page */
+		length_here=PAGE_SIZE-((unsigned long)dataptr&(PAGE_SIZE-1));
+		if (length<length_here) length_here=length;
+
+		buffer->element[element].addr=dataptr;
+		buffer->element[element].length=length_here;
+		length-=length_here;
+		if (!length) {
+			if (first_lap) {
+				buffer->element[element].flags=0;
+			} else {
+				buffer->element[element].flags=
+					SBAL_FLAGS_LAST_FRAG;
+			}
+		} else {
+			if (first_lap) {
+				buffer->element[element].flags=
+					SBAL_FLAGS_FIRST_FRAG;
+			} else {
+				buffer->element[element].flags=
+					SBAL_FLAGS_MIDDLE_FRAG;
+			}
+		}
+		dataptr=dataptr+length_here;
+		element++;
+		if (element>QDIO_MAX_ELEMENTS_PER_BUFFER) {
+			PRINT_ERR("qeth_fill_buffer: IP packet too big!\n");
+			QETH_DBF_TEXT1(0,trace,"IPpktobg");
+			QETH_DBF_HEX1(1,trace,&dataptr,sizeof(void*));
+			buffer->element[first_element].length=0;
+			break;
+		}
+		first_lap=0;
+	}
+#ifdef QETH_DBF_LIKE_HELL
+	sprintf(dbf_text,"filbuf%2x",element);
+	QETH_DBF_TEXT6(0,trace,dbf_text);
+	QETH_DBF_HEX3(0,misc,buffer,QETH_DBF_MISC_LEN);
+	QETH_DBF_HEX3(0,misc,buffer+QETH_DBF_MISC_LEN,QETH_DBF_MISC_LEN);
+#endif /* QETH_DBF_LIKE_HELL */
+
+	return element;
+}
+
+static void qeth_flush_packed_packets(qeth_card_t *card,int queue,
+				      int under_int)
+{
+	qdio_buffer_t *buffer;
+	int result;
+	int position;
+	int position_for_do_qdio;
+	char dbf_text[15];
+	int last_pci;
+
+	position=card->outbound_first_free_buffer[queue];
+	/* can happen, when in the time between deciding to pack and sending
+	   the next packet the lower mark was reached: */
+	if (!card->outbound_ringbuffer[queue]->ringbuf_element[position].
+	    next_element_to_fill)
+		return;
+
+	buffer=&card->outbound_ringbuffer[queue]->buffer[position];
+	buffer->element[card->outbound_ringbuffer[queue]->
+		       ringbuf_element[position].
+		       next_element_to_fill-1].flags|=SBAL_FLAGS_LAST_ENTRY;
+
+	card->dev->trans_start=jiffies;
+	
+#ifdef QETH_PERFORMANCE_STATS
+	if (card->outbound_buffer_send_state[queue][position]==
+	    SEND_STATE_DONT_PACK) {
+		card->perf_stats.bufs_sent_dont_pack++;
+	} else if (card->outbound_buffer_send_state[queue][position]==
+		   SEND_STATE_PACK) {
+		card->perf_stats.bufs_sent_pack++;
+	}
+	card->perf_stats.bufs_sent++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+	position_for_do_qdio=position;
+
+	position=(position+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+	card->outbound_first_free_buffer[queue]=position;
+
+	card->outbound_bytes_in_buffer[queue]=0;
+	/* we can override that, as we have at most 127 buffers enqueued */
+	card->outbound_ringbuffer[queue]->ringbuf_element[position].
+		next_element_to_fill=0;
+
+	atomic_inc(&card->outbound_used_buffers[queue]);
+
+#ifdef QETH_DBF_LIKE_HELL
+	sprintf(dbf_text,"flsp%4x",card->irq0);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+	sprintf(dbf_text,"%4x%2x%2x",position_for_do_qdio,under_int,queue);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+	QETH_DBF_HEX5(0,misc,buffer,QETH_DBF_MISC_LEN);
+	QETH_DBF_HEX5(0,misc,buffer+QETH_DBF_MISC_LEN,QETH_DBF_MISC_LEN);
+#endif /* QETH_DBF_LIKE_HELL */
+
+	/* we always set the outbound pci flag, don't care, whether the
+	 * adapter honors it or not */
+	switch (card->send_state[queue]) {
+ 	case SEND_STATE_DONT_PACK:
+		if (atomic_read(&card->outbound_used_buffers[queue])
+		    <HIGH_WATERMARK_PACK-WATERMARK_FUZZ) break;
+		/* set the PCI bit */
+		card->outbound_ringbuffer[queue]->
+			buffer[position_for_do_qdio].element[0].flags|=0x40;
+		atomic_set(&card->last_pci_pos[queue],position_for_do_qdio);
+		break;
+	case SEND_STATE_PACK:
+		last_pci=atomic_read(&card->last_pci_pos[queue]);
+		if (position_for_do_qdio<last_pci)
+			last_pci-=QDIO_MAX_BUFFERS_PER_Q;
+		/* so:
+		 * 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)
+		 *
+		 * 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
+		 *
+		 * if this is greater or equal than the last_pci position,
+		 * we should request a pci, as no pci request is
+		 * outstanding anymore
+		 */
+		if (position_for_do_qdio-
+		    atomic_read(&card->outbound_used_buffers[queue])>=
+		    last_pci) {
+			/* set the PCI bit */
+			card->outbound_ringbuffer[queue]->
+				buffer[position_for_do_qdio].
+				element[0].flags|=0x40;
+			atomic_set(&card->last_pci_pos[queue],
+				   position_for_do_qdio);
+		}
+	}
+
+	qeth_transform_outbound_addrs(card,
+				      &card->outbound_ringbuffer[queue]->
+				      buffer[position_for_do_qdio]);
+	/* this has to be at the end, otherwise a buffer could be flushed
+	   twice (see coment in qeth_do_send_packet) */
+	result=do_QDIO(card->irq2,QDIO_FLAG_SYNC_OUTPUT|under_int,queue,
+		       position_for_do_qdio,1,
+				       NULL);
+
+	if (result) {
+		PRINT_WARN("Outbound do_QDIO returned %i " \
+	    		   "(irq 0x%x)\n",result,card->irq2);
+		sprintf(dbf_text,"FLSP%4x",card->irq0);
+		QETH_DBF_TEXT5(0,trace,dbf_text);
+		sprintf(dbf_text,"odoQ%4x",result);
+		QETH_DBF_TEXT5(0,trace,dbf_text);
+		sprintf(dbf_text,"%4x%2x%2x",position_for_do_qdio,
+			under_int,queue);
+		QETH_DBF_TEXT5(0,trace,dbf_text);
+		QETH_DBF_HEX5(0,misc,buffer,QETH_DBF_MISC_LEN);
+		QETH_DBF_HEX5(0,misc,buffer+QETH_DBF_MISC_LEN,
+			      QETH_DBF_MISC_LEN);
+	}
+}
+
+#define ERROR_NONE 0
+#define ERROR_RETRY 1
+#define ERROR_LINK_FAILURE 2
+#define ERROR_KICK_THAT_PUPPY 3
+static inline int qeth_determine_send_error(int cc,int qdio_error,int sbalf15)
+{
+	char dbf_text[15];
+
+	switch (cc&3) {
+	case 0:
+		if (qdio_error)
+			return ERROR_LINK_FAILURE;
+		return ERROR_NONE;
+	case 2:
+		if (cc&QDIO_SIGA_ERROR_B_BIT_SET) {
+			QETH_DBF_TEXT3(0,trace,"sigacc2b");
+			return ERROR_KICK_THAT_PUPPY;
+		}
+		if (qeth_sbalf15_in_retrieable_range(sbalf15))
+			return ERROR_RETRY;
+		return ERROR_LINK_FAILURE;
+		/* look at qdio_error and sbalf 15 */
+	case 1:
+		PRINT_WARN("siga returned cc 1! cc=0x%x, " \
+			   "qdio_error=0x%x, sbalf15=0x%x\n",
+			   cc,qdio_error,sbalf15);
+
+		QETH_DBF_TEXT3(0,trace,"siga-cc1");
+		QETH_DBF_TEXT2(0,qerr,"siga-cc1");
+		sprintf(dbf_text,"%1x%2x%2x",cc,qdio_error,sbalf15);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		QETH_DBF_TEXT2(0,qerr,dbf_text);
+		return ERROR_LINK_FAILURE;
+	case 3:
+		QETH_DBF_TEXT3(0,trace,"siga-cc3");
+		return ERROR_KICK_THAT_PUPPY;
+	}
+	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)
+{
+	struct sk_buff *skb;
+	int error;
+	int retries;
+	int sbalf15;
+	char dbf_text[15];
+	qdio_buffer_t *buffer;
+
+	switch (card->outbound_buffer_send_state[queue][bufno]) {
+	case SEND_STATE_DONT_PACK: /* fallthrough */
+	case SEND_STATE_PACK:
+#ifdef QETH_DBF_LIKE_HELL
+		sprintf(dbf_text,"frbf%4x",card->irq0);
+		QETH_DBF_TEXT5(0,trace,dbf_text);
+		sprintf(dbf_text,"%2x%2x%4x",queue,bufno,
+			card->outbound_buffer_send_state[queue][bufno]);
+		QETH_DBF_TEXT5(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+
+		buffer=&card->outbound_ringbuffer[queue]->buffer[bufno];
+		sbalf15=buffer->element[15].flags&0xff;
+		error=qeth_determine_send_error(siga_error,qdio_error,sbalf15);
+		if (error==ERROR_KICK_THAT_PUPPY) {
+			sprintf(dbf_text,"KP%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",bufno,
+				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);
+		} else if (error==ERROR_RETRY) {
+			/* analyze, how many retries we did so far */
+			retries=card->send_retries[queue][bufno];
+
+			sprintf(dbf_text,"Rt%4x%2x",card->irq0,queue);
+			QETH_DBF_TEXT4(0,trace,dbf_text);
+			sprintf(dbf_text,"b%2x:%2x%2x",bufno,
+				sbalf15,retries);
+			QETH_DBF_TEXT4(0,trace,dbf_text);
+
+			if (++retries>SEND_RETRIES_ALLOWED) {
+				error=ERROR_LINK_FAILURE;
+				QETH_DBF_TEXT4(1,trace,"ndegelnd");
+			}
+			/* else error stays RETRY for the switch statemnet */
+		} else if (error==ERROR_LINK_FAILURE) {
+			/* we don't want to log failures resulting from
+			 * too many retries */
+			sprintf(dbf_text,"Fail%4x",card->irq0);
+			QETH_DBF_TEXT3(1,trace,dbf_text);
+			QETH_DBF_HEX3(0,misc,buffer,QETH_DBF_MISC_LEN);
+			QETH_DBF_HEX3(0,misc,buffer+QETH_DBF_MISC_LEN,
+				      QETH_DBF_MISC_LEN);
+		}
+
+		while ((skb=skb_dequeue(&card->outbound_ringbuffer[queue]->
+					ringbuf_element[bufno].skb_list))) {
+			switch (error) {
+			case ERROR_NONE:
+				atomic_dec(&skb->users);
+				dev_kfree_skb_irq(skb);
+				break;
+			case ERROR_RETRY:
+				QETH_DBF_TEXT3(0,qerr,"RETRY!!!");
+				QETH_DBF_TEXT4(0,trace,"RETRY!!!");
+				/* retry packet async (quickly) ... */
+				atomic_dec(&skb->users);
+				dev_kfree_skb_irq(skb);
+				break;
+			case ERROR_LINK_FAILURE:
+			case ERROR_KICK_THAT_PUPPY:
+				QETH_DBF_TEXT4(0,trace,"endeglnd");
+				dst_link_failure(skb);
+				atomic_dec(&skb->users);
+				dev_kfree_skb_irq(skb);
+				break;
+			}
+		}
+		break;
+	default:
+		PRINT_WARN("oops... wrong send_state on %s. " \
+			   "shouldn't happen " \
+			   "(line %i). q=%i, bufno=x%x, state=%i\n",
+			   card->dev_name,__LINE__,queue,bufno,
+			   card->outbound_buffer_send_state[queue][bufno]);
+		sprintf(dbf_text,"UPSf%4x",card->irq0);
+		QETH_DBF_TEXT0(1,trace,dbf_text);
+		QETH_DBF_TEXT0(1,qerr,dbf_text);
+		sprintf(dbf_text,"%2x%2x%4x",queue,bufno,
+			card->outbound_buffer_send_state[queue][bufno]);
+		QETH_DBF_TEXT0(1,trace,dbf_text);
+		QETH_DBF_TEXT0(1,qerr,dbf_text);
+	}
+	card->outbound_buffer_send_state[queue][bufno]=SEND_STATE_INACTIVE;
+	card->send_retries[queue][bufno]=0;
+}
+
+static void qeth_free_all_skbs(qeth_card_t *card)
+{
+	int q,b;
+
+	for (q=0;q<card->no_queues;q++)
+		for (b=0;b<QDIO_MAX_BUFFERS_PER_Q;b++)
+			if (card->outbound_buffer_send_state[q][b]!=
+			    SEND_STATE_INACTIVE)
+				qeth_free_buffer(card,q,b,0,0);
+}
+
+static inline void qeth_flush_buffer(qeth_card_t *card,int queue,
+				     int under_int)
+{
+#ifdef QETH_DBF_LIKE_HELL
+	char dbf_text[15];
+	sprintf(dbf_text,"flsb%4x",card->irq0);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+	sprintf(dbf_text,"%2x%2x%2x",queue,under_int,
+		card->outbound_buffer_send_state[queue][
+		card->outbound_first_free_buffer[queue] ]);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+#endif /* QETH_DBF_LIKE_HELL */
+
+	switch (card->outbound_buffer_send_state[queue][
+		card->outbound_first_free_buffer[queue] ]) {
+	case SEND_STATE_DONT_PACK: break;
+	case SEND_STATE_PACK:
+		qeth_flush_packed_packets(card,queue,under_int);
+		break;
+	default:break;
+	}
+}
+#ifdef QETH_VLAN
+
+void qeth_insert_ipv6_vlan_tag(struct sk_buff *__skb)
+{
+
+	/* Move the mac addresses to the beginning of the new header.
+         * We are using three memcpys instead of one memmove to save
+         * cycles.
+         */
+#define TMP_CPYSIZE 4
+        __u16 *tag;
+        tag = (__u16*)skb_push(__skb, VLAN_HLEN);
+        memcpy(__skb->data,
+	       __skb->data+TMP_CPYSIZE,TMP_CPYSIZE);
+        memcpy(__skb->data+TMP_CPYSIZE,
+               __skb->data+(2*TMP_CPYSIZE),TMP_CPYSIZE);
+        memcpy(__skb->data+(2*TMP_CPYSIZE),
+	       __skb->data+(3*TMP_CPYSIZE),TMP_CPYSIZE);
+        tag = (__u16*)(__skb->data+(3*TMP_CPYSIZE));
+
+        /*first two bytes  = ETH_P_8021Q (0x8100)
+         *second two bytes = VLANID
+         */
+
+	*tag =  __constant_htons(ETH_P_8021Q);
+	*(tag+1) = vlan_tx_tag_get(__skb);
+        *(tag+1)=htons(*(tag+1));
+#undef TMP_CPYSIZE
+}
+#endif
+
+
+
+static 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;
+	qeth_hdr_t *hdr;
+	char *dataptr;
+	char dbf_text[15];
+	struct sk_buff *nskb;
+
+	position=card->outbound_first_free_buffer[queue];
+
+	card->outbound_buffer_send_state[queue][position]=SEND_STATE_DONT_PACK;
+
+	mybuffer=&card->outbound_ringbuffer[queue]->ringbuf_element[position];
+	if (skb_headroom(skb)<QETH_HEADER_SIZE) {
+		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",
+				   card->dev_name,
+				   QETH_HEADER_SIZE-skb_headroom(skb));
+		}
+		PRINT_STUPID("%s: not enough headroom in skb (missing: %i)\n",
+			     card->dev_name,QETH_HEADER_SIZE-skb_headroom(skb));
+		sprintf(dbf_text,"NHRf%4x",card->irq0);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		sprintf(dbf_text,"%2x%2x%2x%2x",skb_headroom(skb),
+			version,multicast,queue);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		QETH_DBF_HEX3(0,trace,&skb->head,sizeof(void*));
+		QETH_DBF_HEX3(0,trace,&skb->data,sizeof(void*));
+		nskb=skb_realloc_headroom(skb,QETH_HEADER_SIZE);
+		if (!nskb) {
+			PRINT_WARN("%s: could not realloc headroom\n",
+				   card->dev_name);
+			sprintf(dbf_text,"CNRf%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+			dev_kfree_skb_irq(skb);
+			return;
+		}
+		dev_kfree_skb_irq(skb);
+		skb=nskb;
+	}
+#ifdef QETH_VLAN
+	if ( (card->vlangrp != NULL) &&
+             vlan_tx_tag_present(skb) &&
+             (version==6)) {
+                qeth_insert_ipv6_vlan_tag(skb);
+        }
+#endif
+		hdr=(qeth_hdr_t*)(skb_push(skb,QETH_HEADER_SIZE));
+	/* sanity check, the Linux memory allocation scheme should
+	   never present us cases like this one (the 32bytes header plus
+	   the first 40 bytes of the paket cross a 4k boundary) */
+	dataptr=(char*)hdr;
+	if ( (((unsigned long)dataptr)&(~(PAGE_SIZE-1))) !=
+	     ( ((unsigned long)dataptr+QETH_HEADER_SIZE+QETH_IP_HEADER_SIZE)&
+	       (~(PAGE_SIZE-1)) ) ) {
+		PRINT_ERR("%s: packet misaligned -- the first %i bytes " \
+			  "are not in the same page. Discarding packet!\n",
+			  card->dev_name,
+			  QETH_HEADER_SIZE+QETH_IP_HEADER_SIZE);
+		PRINT_ERR("head=%p, data=%p\n",skb->head,skb->data);
+		sprintf(dbf_text,"PMAf%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		sprintf(dbf_text,"%2x%2x%2x%2x",skb_headroom(skb),
+			version,multicast,queue);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_HEX1(0,trace,&skb->head,sizeof(void*));
+		QETH_DBF_HEX1(1,trace,&skb->data,sizeof(void*));
+		dev_kfree_skb_irq(skb);
+		return;
+	}
+
+	atomic_inc(&skb->users);
+	skb_queue_tail(&mybuffer->skb_list,skb);
+	qeth_fill_header(hdr,skb,version,multicast);
+	/* we need to write to next_element_to_fill as
+	   qeth_flush_packed_packets checks it */
+	card->outbound_ringbuffer[queue]->ringbuf_element[position].
+		next_element_to_fill=
+		qeth_fill_buffer(&card->outbound_ringbuffer[queue]->
+				 buffer[position],(char *)hdr,
+				 skb->len,0);
+
+#ifdef QETH_PERFORMANCE_STATS
+	card->perf_stats.skbs_sent_dont_pack++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+	qeth_flush_packed_packets(card,queue,0);
+}
+
+/* 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)
+{
+	qeth_ringbuffer_element_t *mybuffer;
+	int elements_needed;
+	int element_to_fill;
+	int buffer_no;
+	int length;
+	char *dataptr;
+	qeth_hdr_t *hdr;
+	char dbf_text[15];
+	struct sk_buff *nskb;
+
+	/* sanity check, dev->hard_header_len should prevent this */
+	if (skb_headroom(skb)<QETH_HEADER_SIZE) {
+		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",
+				   card->dev_name,
+				   QETH_HEADER_SIZE-skb_headroom(skb));
+		}
+		PRINT_STUPID("%s: not enough headroom in skb (missing: %i)\n",
+			     card->dev_name,QETH_HEADER_SIZE-skb_headroom(skb));
+		sprintf(dbf_text,"NHRp%4x",card->irq0);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		sprintf(dbf_text,"%2x%2x%2x%2x",skb_headroom(skb),
+			version,multicast,queue);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		QETH_DBF_HEX3(0,trace,&skb->head,sizeof(void*));
+		QETH_DBF_HEX3(0,trace,&skb->data,sizeof(void*));
+		nskb=skb_realloc_headroom(skb,QETH_HEADER_SIZE);
+		if (!nskb) {
+			PRINT_WARN("%s: could not realloc headroom\n",
+				   card->dev_name);
+			sprintf(dbf_text,"CNRp%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+			dev_kfree_skb_irq(skb);
+			return;
+		}
+		dev_kfree_skb_irq(skb);
+		skb=nskb;
+	}
+#ifdef QETH_VLAN
+        if ( (card->vlangrp != NULL) &&
+             vlan_tx_tag_present(skb) &&
+             (version==6)) {
+                qeth_insert_ipv6_vlan_tag(skb);
+        }
+
+#endif
+	hdr=(qeth_hdr_t*)(skb_push(skb,QETH_HEADER_SIZE));
+
+	length=skb->len;
+
+	/* sanity check, the Linux memory allocation scheme should
+	   never present us cases like this one (the 32bytes header plus
+	   the first 40 bytes of the paket cross a 4k boundary) */
+	dataptr=(char*)hdr;
+	if ( (((unsigned long)dataptr)&(~(PAGE_SIZE-1))) !=
+	     ( ((unsigned long)dataptr+QETH_HEADER_SIZE+QETH_IP_HEADER_SIZE)&
+	       (~(PAGE_SIZE-1)) ) ) {
+		PRINT_ERR("%s: packet misaligned -- the first %i bytes " \
+			  "are not in the same page. Discarding packet!\n",
+			  card->dev_name,
+			  QETH_HEADER_SIZE+QETH_IP_HEADER_SIZE);
+		sprintf(dbf_text,"PMAp%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		sprintf(dbf_text,"%2x%2x%2x%2x",skb_headroom(skb),
+			version,multicast,queue);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_HEX1(0,trace,&skb->head,sizeof(void*));
+		QETH_DBF_HEX1(1,trace,&skb->data,sizeof(void*));
+		dev_kfree_skb_irq(skb);
+		return;
+	}
+
+	buffer_no=card->outbound_first_free_buffer[queue];
+
+	element_to_fill=card->outbound_ringbuffer[queue]->
+		ringbuf_element[buffer_no].
+		next_element_to_fill;
+
+	elements_needed=1+( ( (((unsigned long)dataptr)&(PAGE_SIZE-1))+
+			      length ) >>PAGE_SHIFT);
+	if ( (elements_needed>(QDIO_MAX_ELEMENTS_PER_BUFFER-element_to_fill)) ||
+	     ( (elements_needed==
+		(QDIO_MAX_ELEMENTS_PER_BUFFER-element_to_fill)) &&
+		((element_to_fill>>PAGE_SHIFT)==
+		 card->outbound_bytes_in_buffer[queue]) ) ) {
+		qeth_flush_packed_packets(card,queue,0);
+		element_to_fill=0;
+		card->outbound_bytes_in_buffer[queue]=0;
+		buffer_no=(buffer_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+	}
+
+	if (!element_to_fill)
+ 		card->outbound_buffer_send_state[queue][buffer_no]
+			=SEND_STATE_PACK;
+
+#ifdef QETH_PERFORMANCE_STATS
+	card->perf_stats.skbs_sent_pack++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+	mybuffer=&card->outbound_ringbuffer[queue]->ringbuf_element[buffer_no];
+	atomic_inc(&skb->users);
+	skb_queue_tail(&mybuffer->skb_list,skb);
+	qeth_fill_header(hdr,skb,version,multicast);
+	card->outbound_bytes_in_buffer[queue]+=length+QETH_HEADER_SIZE;
+	card->outbound_ringbuffer[queue]->ringbuf_element[buffer_no].
+		next_element_to_fill=
+		qeth_fill_buffer(&card->outbound_ringbuffer[queue]->
+				 buffer[buffer_no],
+				 dataptr,length,element_to_fill);
+}
+
+static void qeth_alloc_spare_bufs(void)
+{
+	int i;
+	int dont_alloc_more=0;
+	char dbf_text[15];
+
+	sparebuffer_count=0;
+	for (i=0;i<qeth_sparebufs;i++) {
+		if (!dont_alloc_more) {
+			sparebufs[i].buf=(char*)
+				kmalloc(DEFAULT_BUFFER_SIZE,GFP_KERNEL);
+			if (sparebufs[i].buf)
+				sparebuffer_count++;
+			else
+				dont_alloc_more=1;
+		}
+		atomic_set(&sparebufs[i].status,(dont_alloc_more)?
+			   SPAREBUF_UNAVAIL:SPAREBUF_FREE);
+	}
+	sprintf(dbf_text,"alspb%3x",sparebuffer_count);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+	PRINT_INFO("allocated %i spare buffers\n",sparebuffer_count);
+}
+
+static void qeth_free_all_spare_bufs(void)
+{
+	int i;
+
+	QETH_DBF_TEXT2(0,trace,"frealspb");
+
+	for (i=0;i<qeth_sparebufs;i++)
+		if (atomic_read(&sparebufs[i].status)!=SPAREBUF_UNAVAIL) {
+			kfree(sparebufs[i].buf);
+			atomic_set(&sparebufs[i].status,SPAREBUF_UNAVAIL);
+		}
+}
+
+static __inline__ int atomic_return_sub(int i, atomic_t *v)
+{
+	int old_val, new_val;
+	__CS_LOOP(old_val, new_val, v, i, "sr");
+	return old_val;
+}
+
+static 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";
+
+	version=QETH_IP_VERSION(skb);
+	multicast=qeth_is_multicast_skb_at_all(skb,version);
+        queue=qeth_get_prioqueue(card,skb,multicast,version);
+
+#ifdef QETH_DBF_LIKE_HELL
+	sprintf(dbf_text,"dsp:%4x",card->irq0);
+	QETH_DBF_TEXT6(0,trace,dbf_text);
+	sprintf(dbf_text,"%c %c%4x",(version==4)?'4':((version==6)?'6':'0'),
+		(multicast)?'m':'_',queue);
+	QETH_DBF_TEXT6(0,trace,dbf_text);
+	sprintf(dbf_text,"%4x%4x",
+		card->outbound_first_free_buffer[queue],
+		atomic_read(&card->outbound_used_buffers[queue]));
+	QETH_DBF_TEXT6(0,trace,dbf_text);
+	if (qeth_sbal_packing_on_card(card->type)) {
+		switch (card->send_state[queue]) {
+		case SEND_STATE_DONT_PACK:
+			QETH_DBF_TEXT6(0,trace,"usngfast");
+			break;
+		case SEND_STATE_PACK:
+			QETH_DBF_TEXT6(0,trace,"usngpack");
+			break;
+		}
+	} else {
+		QETH_DBF_TEXT6(0,trace,"usngfast");
+	}
+#endif /* QETH_DBF_LIKE_HELL */
+
+	if (atomic_read(&card->outbound_used_buffers[queue])
+	    >=QDIO_MAX_BUFFERS_PER_Q-1) {
+		sprintf(dbf_text,"cdbs%4x",card->irq0);
+		QETH_DBF_TEXT2(1,trace,dbf_text);
+		netif_stop_queue(dev);
+		return -EBUSY;
+	}
+	
+	/* we are not called under int, so we just spin */
+	/* happens around once a second under heavy traffic. takes a little
+	 * bit less than 10usec in avg. on a z900 */
+	if (atomic_compare_and_swap(QETH_LOCK_UNLOCKED,QETH_LOCK_NORMAL,
+			    	    &card->outbound_ringbuffer_lock[queue])) {
+		sprintf(dbf_text,"SPIN%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		while (atomic_compare_and_swap
+		       (QETH_LOCK_UNLOCKED,QETH_LOCK_NORMAL,
+    			&card->outbound_ringbuffer_lock[queue]))
+			;
+		sprintf(dbf_text,"spin%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+	}
+
+#ifdef QETH_PERFORMANCE_STATS
+	card->perf_stats.skbs_sent++;
+#endif /* QETH_PERFORMANCE_STATS */
+	
+	if (qeth_sbal_packing_on_card(card->type)) {
+		switch (card->send_state[queue]) {
+		case SEND_STATE_DONT_PACK:
+			qeth_send_packet_fast(card,skb,dev,queue,
+					      version,multicast);
+			if (atomic_read(&card->outbound_used_buffers[queue])
+			    >=HIGH_WATERMARK_PACK) {
+				card->send_state[queue]=SEND_STATE_PACK;
+				*((__u16*)(&dbf_text2[6]))=card->irq0;
+				QETH_DBF_HEX3(0,trace,dbf_text2,
+					      QETH_DBF_TRACE_LEN);
+#ifdef QETH_PERFORMANCE_STATS
+				card->perf_stats.sc_dp_p++;
+#endif /* QETH_PERFORMANCE_STATS */
+			}
+			break;
+		case SEND_STATE_PACK:
+			qeth_send_packet_packed(card,skb,dev,queue,
+						version,multicast);
+			break;
+		default:
+			result=-EBUSY;
+			sprintf(dbf_text,"UPSs%4x",card->irq0);
+			QETH_DBF_TEXT0(1,trace,dbf_text);
+			PRINT_ALL("oops... shouldn't happen (line %i:%i).\n",
+				  __LINE__,card->send_state[queue]);
+		}
+	} else {
+		qeth_send_packet_fast(card,skb,dev,queue,
+				      version,multicast);
+	}
+
+again:
+	/* ATOMIC: (NORMAL->UNLOCKED, FLUSH->NORMAL) */
+	if (atomic_dec_return(&card->outbound_ringbuffer_lock[queue])) {
+		qeth_flush_buffer(card,queue,0);
+		card->send_state[queue]=SEND_STATE_DONT_PACK;
+		goto again;
+	}
+
+#ifdef QETH_PERFORMANCE_STATS
+	card->perf_stats.outbound_time+=
+		NOW-card->perf_stats.outbound_start_time;
+	card->perf_stats.outbound_cnt++;
+#endif /* QETH_PERFORMANCE_STATS */
+
+	card->stats->tx_packets++;
+	card->stats->tx_bytes+=skb->len;
+
+	return result;
+}
+
+static int qeth_hard_start_xmit(struct sk_buff *skb,struct net_device *dev)
+{
+        qeth_card_t *card;
+	char dbf_text[15];
+	int result;
+
+        card=(qeth_card_t*)(dev->priv);
+
+        if (skb==NULL)
+		return 0;
+
+#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);
+		dev_kfree_skb_irq(skb);
+		return 0;
+	}
+
+#ifdef QETH_PERFORMANCE_STATS
+	card->perf_stats.outbound_start_time=NOW;
+#endif /* QETH_PERFORMANCE_STATS */
+
+	if (!atomic_read(&card->is_startlaned)) {
+		card->stats->tx_carrier_errors++;
+		sprintf(dbf_text,"XMNS%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		dst_link_failure(skb);
+		dev_kfree_skb_irq(skb);
+		return 0;
+	}
+
+	result=qeth_do_send_packet(card,skb,dev);
+
+	if (!result)
+		netif_wake_queue(card->dev);
+
+	return result;
+}
+
+static struct net_device_stats* qeth_get_stats(struct net_device *dev)
+{
+        qeth_card_t *card;
+	char dbf_text[15];
+
+        card=(qeth_card_t*)(dev->priv);
+
+	sprintf(dbf_text,"gtst%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	return card->stats;
+}
+
+static int qeth_change_mtu(struct net_device *dev,int new_mtu)
+{
+        qeth_card_t *card;
+	char dbf_text[15];
+
+        card=(qeth_card_t*)(dev->priv);
+
+	sprintf(dbf_text,"mtu %4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	sprintf(dbf_text,"%8x",new_mtu);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+        if (new_mtu<64) return -EINVAL;
+        if (new_mtu>65535) return -EINVAL;
+        if ((!qeth_is_supported(IPA_IP_FRAGMENTATION)) &&
+            (!qeth_mtu_is_valid(card,new_mtu)))
+	    return -EINVAL;
+	dev->mtu=new_mtu;
+	return 0;
+}
+
+static void qeth_start_softsetup_thread(qeth_card_t *card)
+{
+	char dbf_text[15];
+	if (!atomic_read(&card->shutdown_phase)) {
+		sprintf(dbf_text,"stss%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		up(&card->softsetup_thread_sem);
+	}
+}
+
+static int qeth_sleepon(qeth_card_t *card,int timeout)
+{
+	unsigned long flags;
+        unsigned long start;
+	int retval;
+	char dbf_text[15];
+
+        DECLARE_WAITQUEUE (current_wait_q,current);
+
+	sprintf(dbf_text,"slpn%4x",card->irq0);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+	sprintf(dbf_text,"%08x",timeout);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+
+	add_wait_queue(&card->wait_q,&current_wait_q);
+	atomic_set(&card->wait_q_active,1);
+        start=qeth_get_millis();
+        for (;;) {
+                set_task_state(current,TASK_INTERRUPTIBLE);
+                if (atomic_read(&card->data_has_arrived)) {
+			atomic_set(&card->data_has_arrived,0);
+			retval=0;
+			goto out;
+                }
+                if (qeth_get_millis()-start>timeout) {
+			retval=-ETIME;
+			goto out;
+		}
+                schedule_timeout(((start+timeout-qeth_get_millis())>>10)*HZ);
+        }
+ out:
+	spin_lock_irqsave(&card->wait_q_lock,flags);
+	atomic_set(&card->wait_q_active,0);
+	spin_unlock_irqrestore(&card->wait_q_lock,flags);
+
+	/* we've got to check once again to close the window */
+	if (atomic_read(&card->data_has_arrived)) {
+		atomic_set(&card->data_has_arrived,0);
+		retval=0;
+	}
+
+	set_task_state(current,TASK_RUNNING);
+	remove_wait_queue(&card->wait_q,&current_wait_q);
+	
+	return retval;
+}
+
+static void qeth_wakeup_ioctl(qeth_card_t *card) {
+	char dbf_text[15];
+
+	sprintf(dbf_text,"wkup%4x",card->irq0);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+
+        atomic_set(&card->ioctl_data_has_arrived,1);
+	spin_lock(&card->ioctl_wait_q_lock);
+	if (atomic_read(&card->ioctl_wait_q_active)) {
+		wake_up(&card->ioctl_wait_q);
+	}
+	spin_unlock(&card->ioctl_wait_q_lock);
+}
+
+static int qeth_sleepon_ioctl(qeth_card_t *card,int timeout)
+{
+	unsigned long flags;
+        unsigned long start;
+	int retval;
+	char dbf_text[15];
+
+        DECLARE_WAITQUEUE (current_wait_q,current);
+
+	sprintf(dbf_text,"ioctlslpn%4x",card->irq0);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+	sprintf(dbf_text,"%08x",timeout);
+	QETH_DBF_TEXT5(0,trace,dbf_text);
+
+	save_flags(flags); 
+	add_wait_queue(&card->ioctl_wait_q,&current_wait_q);
+	atomic_set(&card->ioctl_wait_q_active,1);
+        start=qeth_get_millis();
+        for (;;) {
+                set_task_state(current,TASK_INTERRUPTIBLE);
+                if (atomic_read(&card->ioctl_data_has_arrived)) {
+			atomic_set(&card->ioctl_data_has_arrived,0);
+			retval=0;
+			goto out;
+                }
+                if (qeth_get_millis()-start>timeout) {
+			retval=-ETIME;
+			goto out;
+		}
+                schedule_timeout(((start+timeout-qeth_get_millis())>>10)*HZ);
+        }
+ out:
+	spin_lock_irqsave(&card->ioctl_wait_q_lock,flags);
+	atomic_set(&card->ioctl_wait_q_active,0);
+	spin_unlock_irqrestore(&card->ioctl_wait_q_lock,flags);
+
+	/* we've got to check once again to close the window */
+	if (atomic_read(&card->ioctl_data_has_arrived)) {
+		atomic_set(&card->ioctl_data_has_arrived,0);
+		retval=0;
+	}
+
+	set_task_state(current,TASK_RUNNING);
+	remove_wait_queue(&card->ioctl_wait_q,&current_wait_q);
+	
+	return retval;
+}
+
+static void qeth_wakeup_procfile(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;
+	}
+	return 0;
+}
+
+static char* qeth_send_control_data(qeth_card_t *card,unsigned char *buffer,
+				    int len,unsigned long intparam)
+{
+        unsigned long flags;
+        int result,result2;
+	char dbf_text[15];
+	unsigned char *rec_buf;
+	int setip=(intparam&IPA_SETIP_FLAG)?1:0;
+
+again:
+	if (atomic_read(&card->shutdown_phase)==
+	    QETH_REMOVE_CARD_QUICK) return NULL;
+	if (atomic_read(&card->escape_softsetup))
+		return NULL;
+
+	/* we lock very early to synchronize access to seqnos */
+	if (atomic_swap(&card->write_busy,1)) {
+		qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
+		sprintf(dbf_text,"LSCD%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		goto again;
+	}
+	memcpy(card->dma_stuff->sendbuf,card->send_buf,QETH_BUFSIZE);
+
+        memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(buffer),
+               &card->seqno.trans_hdr,QETH_SEQ_NO_LENGTH);
+        card->seqno.trans_hdr++;
+
+        memcpy(QETH_PDU_HEADER_SEQ_NO(buffer),
+               &card->seqno.pdu_hdr,QETH_SEQ_NO_LENGTH);
+        card->seqno.pdu_hdr++;
+        memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(buffer),
+               &card->seqno.pdu_hdr_ack,QETH_SEQ_NO_LENGTH);
+
+        /* there is noone doing this except sleep and this function */
+	atomic_set(&card->data_has_arrived,0);
+
+        memcpy(&card->dma_stuff->write_ccw,WRITE_CCW,sizeof(ccw1_t));
+        card->dma_stuff->write_ccw.count=len;
+        card->dma_stuff->write_ccw.cda=
+		QETH_GET_ADDR(card->dma_stuff->sendbuf);
+
+	sprintf(dbf_text,"scdw%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	sprintf(dbf_text,"%8x",len);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+	QETH_DBF_HEX4(0,trace,&intparam,QETH_DBF_TRACE_LEN);
+	QETH_DBF_HEX2(0,control,buffer,QETH_DBF_CONTROL_LEN);
+
+        s390irq_spin_lock_irqsave(card->irq1,flags);
+        result=do_IO(card->irq1,&card->dma_stuff->write_ccw,intparam,0,0);
+        if (result) {
+		qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+                result2=do_IO(card->irq1,&card->dma_stuff->write_ccw,
+			      intparam,0,0);
+		if (result2!=-ENODEV)
+			PRINT_WARN("qeth_send_control_data: do_IO " \
+				   "returned %i, next try returns %i\n",
+				   result,result2);
+		result=result2;
+        }
+        s390irq_spin_unlock_irqrestore(card->irq1,flags);
+
+	if (result) {
+		QETH_DBF_TEXT2(0,trace,"scd:doio");
+		sprintf(dbf_text,"%4x",(__s16)result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		return NULL;
+	}
+
+	if (intparam==IPA_IOCTL_STATE) {
+		if (qeth_sleepon_ioctl(card,QETH_IPA_TIMEOUT)) {
+			QETH_DBF_TEXT2(0,trace,"scd:ioctime");
+			/* re-enable qeth_send_control_data again */
+			atomic_set(&card->write_busy,0);
+			return NULL;
+		}
+		rec_buf=card->ipa_buf;
+		sprintf(dbf_text,"scro%4x",card->irq0);
+	} else {
+		if (qeth_sleepon(card,(setip)?QETH_IPA_TIMEOUT:
+				 QETH_MPC_TIMEOUT)) {
+			QETH_DBF_TEXT2(0,trace,"scd:time");
+			/* re-enable qeth_send_control_data again */
+			atomic_set(&card->write_busy,0);
+			return NULL;
+		}
+		rec_buf=card->ipa_buf;
+		sprintf(dbf_text,"scri%4x",card->irq0);
+	}
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	QETH_DBF_HEX2(0,control,rec_buf,QETH_DBF_CONTROL_LEN);
+
+        memcpy(&card->seqno.pdu_hdr_ack,
+               QETH_PDU_HEADER_SEQ_NO(rec_buf),
+               QETH_SEQ_NO_LENGTH);
+
+        return rec_buf;
+}
+
+static int qeth_send_ipa_cmd(qeth_card_t *card,ipa_cmd_t *cmd,int update_cmd,
+			     int ipatype)
+{
+        unsigned char *buffer;
+        ipa_cmd_t *reply;
+        int ipa_cmd;
+        int result;
+
+	/* don't muck around with ipv6 if there's no use to do so */
+	if ( (cmd->prot_version==6) &&
+	     (!qeth_is_supported(IPA_IPv6)) ) return 0;
+
+        ipa_cmd=cmd->command;
+
+        memcpy(card->send_buf,IPA_PDU_HEADER,
+	       IPA_PDU_HEADER_SIZE);
+
+        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,
+	       cmd,sizeof(ipa_cmd_t));
+
+        buffer=qeth_send_control_data(card,card->send_buf,
+                                      IPA_PDU_HEADER_SIZE+sizeof(ipa_cmd_t),
+                                      ipatype);
+
+	if (!buffer) {
+		if (atomic_read(&card->escape_softsetup)) result=0;
+		else result=-1;
+	} else {
+		reply=(ipa_cmd_t*)PDU_ENCAPSULATION(buffer);
+		if ((update_cmd)&&(reply)) memcpy(cmd,reply,sizeof(ipa_cmd_t));
+		result=reply->return_code;
+
+		if ((ipa_cmd==IPA_CMD_SETASSPARMS)&&(result==0)) {
+			result=reply->data.setassparms.return_code;
+		}
+		if ((ipa_cmd==IPA_CMD_SETADAPTERPARMS)&&(result==0)) {
+			result=reply->data.setadapterparms.return_code;
+		}
+	}
+        return result;
+}
+
+static void qeth_fill_ipa_cmd(qeth_card_t *card,ipa_cmd_t *cmd,
+			      __u8 command,int ip_vers)
+{
+        memset(cmd,0,sizeof(ipa_cmd_t));
+        cmd->command=command;
+        cmd->initiator=INITIATOR_HOST;
+        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;
+        cmd->prim_version_no=1;
+        cmd->param_count=1;
+        cmd->prot_version=ip_vers;
+        cmd->ipa_supported=0;
+        cmd->ipa_enabled=0;
+}
+
+static int qeth_send_startstoplan(qeth_card_t *card,__u8 ipacmd,__u16 ip_vers)
+{
+        ipa_cmd_t cmd;
+	int result;
+
+        qeth_fill_ipa_cmd(card,&cmd,ipacmd,0);
+        cmd.param_count=0;
+        cmd.prot_version=ip_vers;
+        cmd.ipa_supported=0;
+        cmd.ipa_enabled=0;
+
+        result=qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
+	return result;
+}
+
+static int qeth_send_startlan(qeth_card_t *card,__u16 ip_vers)
+{
+	int result;
+	char dbf_text[15];
+
+	sprintf(dbf_text,"stln%4x",card->irq0);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+
+        result=qeth_send_startstoplan(card,IPA_CMD_STARTLAN,ip_vers);
+	if (!result) atomic_set(&card->is_startlaned,1);
+
+	if (result) {
+		QETH_DBF_TEXT2(0,trace,"STRTLNFL");
+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+	}
+
+	return result;
+}
+
+static int qeth_send_stoplan(qeth_card_t *card)
+{
+#ifdef QETH_SEND_STOPLAN_ON_SHUTDOWN
+	int result;
+	char dbf_text[15];
+
+	atomic_set(&card->is_startlaned,0);
+
+	sprintf(dbf_text,"spln%4x",card->irq0);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+
+        result=qeth_send_startstoplan(card,IPA_CMD_STOPLAN,4);
+
+	if (result) {
+		QETH_DBF_TEXT2(0,trace,"STPLNFLD");
+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+	}
+
+	return result;
+#else /* QETH_SEND_STOPLAN_ON_SHUTDOWN */
+	return 0;
+#endif /* QETH_SEND_STOPLAN_ON_SHUTDOWN */
+}
+
+static int qeth_send_qipassist(qeth_card_t *card,short ip_vers)
+{
+        ipa_cmd_t cmd;
+	int result;
+
+        qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_QIPASSIST,ip_vers);
+
+	result=qeth_send_ipa_cmd(card,&cmd,1,IPA_CMD_STATE);
+
+	if (!result) {
+		if (ip_vers==4) {
+			card->ipa_supported=cmd.ipa_supported;
+			card->ipa_enabled=cmd.ipa_enabled;
+		} else {
+			card->ipa6_supported=cmd.ipa_supported;
+			card->ipa6_enabled=cmd.ipa_enabled;
+		}
+	}
+
+        return result;
+}
+
+static int qeth_send_ipa_arpcmd(qeth_card_t *card,arp_cmd_t *cmd,
+				int update_cmd,int ipatype,__u32 req_size)
+{
+        unsigned char *buffer;
+        int ipa_cmd;
+        int result;
+	__u16 s1,s2;
+
+	/* don't muck around with ipv6 if there's no use to do so */
+	if ( (cmd->prot_version==6) &&
+	     (!qeth_is_supported(IPA_IPv6)) ) return 0;
+	result = 0;
+        ipa_cmd=cmd->command;
+
+        memcpy(card->send_buf,IPA_PDU_HEADER,
+	       IPA_PDU_HEADER_SIZE);
+        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,
+	       cmd,sizeof(arp_cmd_t));
+
+	if (req_size) {
+		/* adjust sizes for big requests */
+		s1=(__u32)IPA_PDU_HEADER_SIZE+SNMP_BASE_CMDLENGTH+req_size;
+		s2=(__u32)SNMP_BASE_CMDLENGTH+req_size;
+		memcpy(QETH_IPA_PDU_LEN_TOTAL(card->send_buf),&s1,2);
+		memcpy(QETH_IPA_PDU_LEN_PDU1(card->send_buf),&s2,2);
+		memcpy(QETH_IPA_PDU_LEN_PDU2(card->send_buf),&s2,2);
+		memcpy(QETH_IPA_PDU_LEN_PDU3(card->send_buf),&s2,2);
+	}
+
+        buffer=qeth_send_control_data(card,card->send_buf,
+                                      IPA_PDU_HEADER_SIZE+sizeof(arp_cmd_t),
+                                      ipatype);
+	if (!buffer) 
+		result = -ENODATA;
+	else 
+		result = card->ioctl_returncode;
+        return result;
+}
+
+static int qeth_ioctl_handle_snmp_data(qeth_card_t *card,arp_cmd_t *reply)
+{
+	__u16 data_len;
+
+#define SNMP_HEADER_SIZE_WITH_TOKEN 36
+	
+	data_len = *((__u16*)QETH_IPA_PDU_LEN_PDU1(card->dma_stuff->recbuf));
+
+	if (reply->data.setadapterparms.frame_seq_no == 1) {
+		data_len = data_len - 
+		    (__u16)((char*)reply->data.setadapterparms.
+					  data.snmp_subcommand.
+					  snmp_data - (char*)reply); 
+	} else {
+		data_len = data_len - 
+		 	(__u16)((char*)&reply->data.setadapterparms.data.
+                                       	snmp_subcommand.
+					snmp_request - (char*)reply);
+	}
+	if (reply->data.setadapterparms.frame_seq_no == 1) {
+		if (card->ioctl_buffersize <= (SNMP_HEADER_SIZE_WITH_TOKEN + 
+		    reply->data.setadapterparms.frames_used_total *
+		    ARP_DATA_SIZE)) {
+			card->ioctl_returncode = ARP_RETURNCODE_ERROR;
+			reply->data.setadapterparms.data.
+				snmp_subcommand.snmp_returncode = -ENOMEM;
+		} else {
+			card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+			card->number_of_entries = 0;
+			memcpy(((char *)card->ioctl_data_buffer),
+	                         reply->data.setadapterparms.snmp_token,
+		                 SNMP_HEADER_SIZE_WITH_TOKEN);
+			card->ioctl_buffer_pointer = card->ioctl_data_buffer+
+		  				     SNMP_HEADER_SIZE_WITH_TOKEN;
+		}
+	}
+
+	if (card->ioctl_returncode != ARP_RETURNCODE_ERROR &&
+	    reply->data.setadapterparms.frame_seq_no <=
+	    reply->data.setadapterparms.frames_used_total) {
+		if (reply->data.setadapterparms.return_code==
+		    IPA_REPLY_SUCCESS) {
+			if (reply->data.setadapterparms.frame_seq_no == 1) {
+				memcpy(card->ioctl_buffer_pointer,
+			       		reply->data.setadapterparms.data.
+			       		snmp_subcommand.snmp_data,data_len);
+			} else {
+				memcpy(card->ioctl_buffer_pointer,
+			       		(char*)&reply->data.setadapterparms.
+						data.snmp_subcommand.
+						snmp_request,data_len);
+			}
+			card->ioctl_buffer_pointer =
+				card->ioctl_buffer_pointer + data_len;
+			card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+
+			if (reply->data.setadapterparms.frame_seq_no == 
+			    reply->data.setadapterparms.frames_used_total) {
+				card->ioctl_returncode =
+					ARP_RETURNCODE_LASTREPLY;
+			}
+		} else {
+			card->ioctl_returncode = ARP_RETURNCODE_ERROR;
+			memset(card->ioctl_data_buffer,0,
+			       card->ioctl_buffersize);
+			reply->data.setadapterparms.data.
+				snmp_subcommand.snmp_returncode = 
+				reply->data.setadapterparms.return_code;
+		}
+	}
+#undef  SNMP_HEADER_SIZE_WITH_TOKEN
+
+	return card->ioctl_returncode;
+}
+
+static int qeth_ioctl_handle_arp_data(qeth_card_t *card, arp_cmd_t *reply) 
+{
+	if (reply->data.setassparms.seq_no == 1) {
+		if (card->ioctl_buffersize <= 
+		    (sizeof(__u16) + sizeof(int) + reply->data.
+		     setassparms.number_of_replies * ARP_DATA_SIZE)) {
+			card->ioctl_returncode = ARP_RETURNCODE_ERROR;
+		} 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 &&
+	    reply->data.setassparms.seq_no <=
+	    reply->data.setassparms.number_of_replies) {
+		
+		if (reply->data.setassparms.return_code==IPA_REPLY_SUCCESS) {
+		
+			card->number_of_entries = card->number_of_entries + 
+				                  reply->data.setassparms.
+						  data.queryarp_data.
+						  number_of_entries;
+			memcpy(card->ioctl_buffer_pointer,
+			       reply->data.setassparms.data.queryarp_data.
+			       arp_data,ARP_DATA_SIZE);
+			card->ioctl_buffer_pointer = card->
+				ioctl_buffer_pointer + ARP_DATA_SIZE;
+			card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+			if (reply->data.setassparms.seq_no ==
+			    reply->data.setassparms.number_of_replies) {
+				memcpy(card->ioctl_data_buffer,
+				       &reply->data.setassparms.data.
+				       queryarp_data.osa_setbitmask,
+				       sizeof(__u16));
+				card->ioctl_returncode=
+					ARP_RETURNCODE_LASTREPLY;
+			}
+		} else {
+			card->ioctl_returncode = ARP_RETURNCODE_ERROR;
+			memset(card->ioctl_data_buffer,0,
+			       card->ioctl_buffersize);
+		}
+	}
+	return card->ioctl_returncode;
+}
+
+static int qeth_look_for_arp_data(qeth_card_t *card) 
+{
+	arp_cmd_t *reply;
+	int result;
+
+	
+	reply=(arp_cmd_t*)PDU_ENCAPSULATION(card->dma_stuff->recbuf);
+
+	if ( (reply->command==IPA_CMD_SETASSPARMS) &&
+	     (reply->data.setassparms.assist_no==IPA_ARP_PROCESSING) &&
+	     (reply->data.setassparms.command_code==
+	      IPA_CMD_ASS_ARP_FLUSH_CACHE) ) {
+		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)) {
+		result = qeth_ioctl_handle_arp_data(card,reply);
+	} else if ( (reply->command == IPA_CMD_SETADAPTERPARMS)	&&
+	            (reply->data.setadapterparms.command_code == 
+		     IPA_SETADP_SET_SNMP_CONTROL) &&
+		    (card->ioctl_returncode == ARP_RETURNCODE_SUCCESS) ){
+		result = qeth_ioctl_handle_snmp_data(card,reply);
+	} else
+		result = ARP_RETURNCODE_NOARPDATA;		
+	
+	return result;
+}
+
+
+
+static int qeth_queryarp(qeth_card_t *card,struct ifreq *req,int version,
+			 __u32 assist_no, __u16 command_code,char *c_data,
+			 __u16 len)
+{
+	int data_size;
+	arp_cmd_t *cmd;
+	int result;
+        
+
+	cmd = (arp_cmd_t *) kmalloc(sizeof(arp_cmd_t),GFP_KERNEL);
+	if (!cmd) {
+		return IPA_REPLY_FAILED;
+	}
+
+	memcpy(&data_size,c_data,sizeof(int));
+
+	qeth_fill_ipa_cmd(card,(ipa_cmd_t*)cmd,IPA_CMD_SETASSPARMS,version);
+
+	cmd->data.setassparms.assist_no=assist_no;
+	cmd->data.setassparms.length=8+len;
+        cmd->data.setassparms.command_code=command_code;
+        cmd->data.setassparms.return_code=0;
+	cmd->data.setassparms.seq_no=0;
+
+	card->ioctl_buffersize = data_size;
+	card->ioctl_data_buffer = (char *) vmalloc(data_size);
+	if (!card->ioctl_data_buffer) {
+		kfree(cmd);
+		return IPA_REPLY_FAILED;
+	}
+
+	card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+
+	result=qeth_send_ipa_arpcmd(card,cmd,1,IPA_IOCTL_STATE,0);
+	
+	if ((result == ARP_RETURNCODE_ERROR) || 
+	    (result == -ENODATA)) {
+		result = IPA_REPLY_FAILED;
+	}
+	else {
+		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);		
+	}
+	card->ioctl_buffer_pointer = NULL;
+	vfree(card->ioctl_data_buffer);
+	kfree(cmd);
+	card->number_of_entries = 0;
+	card->ioctl_buffersize = 0;
+
+	return result;
+}
+
+static int snmp_set_setadapterparms_command(qeth_card_t *card,
+					    arp_cmd_t *cmd,struct ifreq *req, 
+					    char *data,__u16 len,
+					    __u16 command_code,int req_size)
+{
+	__u32 data_size;
+
+	memcpy(&data_size,data,sizeof(__u32));
+
+	card->ioctl_buffersize = data_size;
+	card->ioctl_data_buffer = (char *) vmalloc(data_size);
+	if (!card->ioctl_data_buffer) {
+		return -ENOMEM;
+	}
+	card->ioctl_returncode = ARP_RETURNCODE_SUCCESS;
+
+	memcpy(cmd->data.setadapterparms.snmp_token,
+	       data+SNMP_REQUEST_DATA_OFFSET,req_size); 
+
+	cmd->data.setadapterparms.cmdlength=SNMP_SETADP_CMDLENGTH+req_size;
+	cmd->data.setadapterparms.command_code = command_code;
+	cmd->data.setadapterparms.frames_used_total=1;
+	cmd->data.setadapterparms.frame_seq_no=1;
+
+	return 0;
+}
+
+static int qeth_send_snmp_control(qeth_card_t *card,struct ifreq *req,
+				  __u32 command,__u16 command_code,
+				  char *c_data,__u16 len)
+{
+	arp_cmd_t *cmd;
+	__u32 result,req_size;
+
+        cmd = (arp_cmd_t *) kmalloc(sizeof(arp_cmd_t),GFP_KERNEL);
+	if (!cmd) {
+		return IPA_REPLY_FAILED;
+	}
+
+	qeth_fill_ipa_cmd(card,(ipa_cmd_t*)cmd,command,4);
+
+	memcpy(&req_size,((char*)c_data)+sizeof(__u32),sizeof(__u32));
+
+	if (snmp_set_setadapterparms_command(card,cmd,req,c_data,
+					     len,command_code,req_size))
+	{
+		kfree(cmd);
+		return IPA_REPLY_FAILED;
+	}
+
+	result=qeth_send_ipa_arpcmd(card,cmd,1,IPA_IOCTL_STATE,req_size);
+
+	if (result == -ENODATA) {
+		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);
+		result = IPA_REPLY_FAILED;
+	}
+	else {
+		copy_to_user(req->ifr_ifru.ifru_data+SNMP_REQUEST_DATA_OFFSET,
+			     card->ioctl_data_buffer,card->ioctl_buffersize);
+		result = IPA_REPLY_SUCCESS;
+	}
+snmp_out:
+	card->number_of_entries = 0;
+	card->ioctl_buffersize = 0;
+	card->ioctl_buffer_pointer = NULL;
+	vfree(card->ioctl_data_buffer);
+	kfree(cmd);
+
+	return result;
+}
+
+static int qeth_send_setassparms(qeth_card_t *card,int version,
+				 __u32 assist_no,__u16 command_code,
+				 long data,__u16 len)
+{
+        ipa_cmd_t cmd;
+        int result;
+
+        qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_SETASSPARMS,version);
+
+        cmd.data.setassparms.assist_no=assist_no;
+        cmd.data.setassparms.length=8+len;
+        cmd.data.setassparms.command_code=command_code;
+        cmd.data.setassparms.return_code=0;
+        cmd.data.setassparms.seq_no=0;
+
+	if (len<=sizeof(__u32))
+		cmd.data.setassparms.data.flags_32bit=(__u32)data;
+	else if (len>sizeof(__u32))
+		memcpy(&cmd.data.setassparms.data,(void*)data,
+		       /* limit here to a page or so */
+		       qeth_min(len,PAGE_SIZE));
+	if (command_code != IPA_CMD_ASS_START) {
+		result=qeth_send_ipa_cmd(card,&cmd,0,
+			 ((assist_no==IPA_ARP_PROCESSING)&&
+			  (command_code!=IPA_CMD_ASS_ARP_FLUSH_CACHE))?
+					 IPA_IOCTL_STATE:IPA_CMD_STATE);
+ 
+	} else
+		result=qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
+		
+	return result;
+}
+
+static int qeth_send_setadapterparms_query(qeth_card_t *card)
+{
+        ipa_cmd_t cmd;
+	int result;
+
+	qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_SETADAPTERPARMS,
+			  IPA_SETADAPTERPARMS_IP_VERSION);
+	cmd.data.setadapterparms.cmdlength=sizeof(struct ipa_setadp_cmd);
+	cmd.data.setadapterparms.command_code=
+		IPA_SETADP_QUERY_COMMANDS_SUPPORTED;
+	cmd.data.setadapterparms.frames_used_total=1;
+	cmd.data.setadapterparms.frame_seq_no=1;
+	result=qeth_send_ipa_cmd(card,&cmd,1,IPA_CMD_STATE);
+
+	if (cmd.data.setadapterparms.data.query_cmds_supp.lan_type&0x7f)
+		card->link_type=cmd.data.setadapterparms.data.
+			query_cmds_supp.lan_type;
+
+	card->adp_supported=
+		cmd.data.setadapterparms.data.query_cmds_supp.supported_cmds;
+
+	return result;
+}
+
+static int qeth_send_setadapterparms_mode(qeth_card_t *card,__u32 command,
+					  __u32 mode)
+{
+        ipa_cmd_t cmd;
+	int result;
+
+	qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_SETADAPTERPARMS,
+			  IPA_SETADAPTERPARMS_IP_VERSION);
+	cmd.data.setadapterparms.cmdlength=sizeof(struct ipa_setadp_cmd);
+	cmd.data.setadapterparms.command_code=command;
+	cmd.data.setadapterparms.frames_used_total=1;
+	cmd.data.setadapterparms.frame_seq_no=1;
+	cmd.data.setadapterparms.data.mode=mode;
+	result=qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
+
+	return result;
+}
+
+static int qeth_send_setadapterparms_change_addr(qeth_card_t *card,
+						 __u32 command,
+						 __u32 subcmd,__u8 *mac_addr,
+						 int addr_len)
+{
+        ipa_cmd_t cmd;
+	int result;
+
+	qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_SETADAPTERPARMS,
+			  IPA_SETADAPTERPARMS_IP_VERSION);
+	cmd.data.setadapterparms.cmdlength=sizeof(struct ipa_setadp_cmd);
+	cmd.data.setadapterparms.command_code=command;
+	cmd.data.setadapterparms.frames_used_total=1;
+	cmd.data.setadapterparms.frame_seq_no=1;
+	cmd.data.setadapterparms.data.change_addr.cmd=subcmd;
+	cmd.data.setadapterparms.data.change_addr.addr_size=addr_len;
+	memcpy(&cmd.data.setadapterparms.data.change_addr.addr,
+	       mac_addr,addr_len);
+
+	result=qeth_send_ipa_cmd(card,&cmd,1,IPA_CMD_STATE);
+
+	memcpy(mac_addr,&cmd.data.setadapterparms.data.change_addr.addr,
+	       addr_len);
+
+	return result;
+}
+
+static int qeth_send_setassparms_simple_with_data(qeth_card_t *card,
+						  __u32 assist_no,
+						  __u16 command_code,
+						  long data)
+{
+        return qeth_send_setassparms(card,4,assist_no,command_code,data,4);
+}
+
+static int qeth_send_setassparms_simple_without_data(qeth_card_t *card,
+						     __u32 assist_no,
+						     __u16 command_code)
+{
+        return qeth_send_setassparms(card,4,assist_no,command_code,0,0);
+}
+
+static int qeth_send_setassparms_simple_without_data6(qeth_card_t *card,
+						      __u32 assist_no,
+						      __u16 command_code)
+{
+ 	return qeth_send_setassparms(card,6,assist_no,command_code,0,0);
+}
+
+static int qeth_send_setdelip(qeth_card_t *card,__u8 *ip,__u8 *netmask,
+			      int ipacmd,short ip_vers,unsigned int flags)
+{
+        ipa_cmd_t cmd;
+        int ip_len=(ip_vers==6)?16:4;
+
+        qeth_fill_ipa_cmd(card,&cmd,ipacmd,ip_vers);
+        if (ip_vers==6) {
+                memcpy(&cmd.data.setdelip6.ip,ip,ip_len);
+                memcpy(&cmd.data.setdelip6.netmask,netmask,ip_len);
+                cmd.data.setdelip6.flags=flags;
+        } else {
+                memcpy(&cmd.data.setdelip4.ip,ip,ip_len);
+                memcpy(&cmd.data.setdelip4.netmask,netmask,ip_len);
+                cmd.data.setdelip4.flags=flags;
+        }
+
+        return qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE|
+				 ((ipacmd==IPA_CMD_SETIP)?IPA_SETIP_FLAG:0));
+}
+
+static int qeth_send_setdelipm(qeth_card_t *card,__u8 *ip,__u8 *mac,
+			       int ipacmd,short ip_vers)
+{
+        ipa_cmd_t cmd;
+        int ip_len=(ip_vers==6)?16:4;
+
+        qeth_fill_ipa_cmd(card,&cmd,ipacmd,ip_vers);
+        memcpy(&cmd.data.setdelipm.mac,mac,6);
+        if (ip_vers==6) {
+                memcpy(&cmd.data.setdelipm.ip6,ip,ip_len);
+        } else {
+                memcpy(&cmd.data.setdelipm.ip4_6,ip,ip_len);
+        }
+
+        return qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE|
+				 ((ipacmd==IPA_CMD_SETIPM)?IPA_SETIP_FLAG:0));
+}
+
+#define PRINT_SETIP_ERROR(x) \
+	if (result) \
+		PRINT_ERR("setip%c: return code 0x%x (%s)\n",x,result, \
+			  (result==0xe002)?"invalid mtu size": \
+	       		  (result==0xe005)?"duplicate ip address": \
+	       		  (result==0xe0a5)?"duplicate ip address": \
+       			  (result==0xe006)?"ip table full": \
+			  (result==0xe008)?"startlan not received": \
+			  (result==0xe009)?"setip already received": \
+			  (result==0xe00a)?"dup network ip address": \
+			  (result==0xe00b)?"mblk no free main task entry": \
+			  (result==0xe00d)?"invalid ip version": \
+			  (result==0xe00e)?"unsupported arp assist cmd": \
+			  (result==0xe00f)?"arp assist not enabled": \
+			  (result==0xe080)?"startlan disabled": \
+			  (result==-1)?"IPA communication timeout": \
+			  "unknown return code")
+
+static inline int qeth_send_setip(qeth_card_t *card,__u8 *ip,
+				  __u8 *netmask,short ip_vers,int use_retries)
+{
+	int result;
+	int retries;
+	char dbf_text[15];
+	int takeover=0;
+
+	retries=(use_retries)?QETH_SETIP_RETRIES:1;
+	if (qeth_is_ipa_covered_by_ipato_entries(ip_vers,ip,card)) {
+		sprintf(dbf_text,"ipto%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		if (ip_vers==4) {
+			*((__u32*)(&dbf_text[0]))=*((__u32*)ip);
+			*((__u32*)(&dbf_text[4]))=*((__u32*)netmask);
+			QETH_DBF_HEX2(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+		} else {
+			QETH_DBF_HEX2(0,trace,ip,QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX2(0,trace,ip+QETH_DBF_TRACE_LEN,
+				      QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX2(0,trace,netmask,QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX2(0,trace,netmask+QETH_DBF_TRACE_LEN,
+				      QETH_DBF_TRACE_LEN);
+		}
+		takeover=1;
+	}
+retry:
+        result=qeth_send_setdelip(card,ip,netmask,IPA_CMD_SETIP,ip_vers,
+				  (takeover)?IPA_SETIP_TAKEOVER_FLAGS:
+				  IPA_SETIP_FLAGS);
+	PRINT_SETIP_ERROR(' ');
+
+	if (result) {
+		QETH_DBF_TEXT2(0,trace,"SETIPFLD");
+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+	}
+
+	if (((result==-1)||(result==0xe080))&&(retries--)) {
+		sprintf(dbf_text,"sipr%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		if (ip_vers==4) {
+			*((__u32*)(&dbf_text[0]))=*((__u32*)ip);
+			*((__u32*)(&dbf_text[4]))=*((__u32*)netmask);
+			QETH_DBF_HEX2(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+		} else {
+			QETH_DBF_HEX2(0,trace,ip,QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX2(0,trace,ip+QETH_DBF_TRACE_LEN,
+				      QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX2(0,trace,netmask,QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX2(0,trace,netmask+QETH_DBF_TRACE_LEN,
+				      QETH_DBF_TRACE_LEN);
+		}
+		PRINT_WARN("trying again...\n");
+		goto retry;
+	}
+
+	return result;
+}
+
+static inline int qeth_send_delip(qeth_card_t *card,__u8 *ip,
+				  __u8 *netmask,short ip_vers)
+{
+        return qeth_send_setdelip(card,ip,netmask,IPA_CMD_DELIP,ip_vers,
+				  IPA_DELIP_FLAGS);
+}
+
+static inline int qeth_send_setipm(qeth_card_t *card,__u8 *ip,
+				   __u8 *mac,short ip_vers,int use_retries)
+{
+	int result;
+	int retries;
+	char dbf_text[15];
+	
+	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);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		if (ip_vers==4) {
+			*((__u32*)(&dbf_text[0]))=*((__u32*)ip);
+			QETH_DBF_HEX2(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+		} else {
+			QETH_DBF_HEX2(0,trace,ip,QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX2(0,trace,ip+QETH_DBF_TRACE_LEN,
+				      QETH_DBF_TRACE_LEN);
+		}
+	}
+
+retry:
+        result=qeth_send_setdelipm(card,ip,mac,IPA_CMD_SETIPM,ip_vers);
+	PRINT_SETIP_ERROR('m');
+
+	if (result) {
+		QETH_DBF_TEXT2(0,trace,"SETIMFLD");
+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+	}
+
+	if ((result==-1)&&(retries--)) {
+		sprintf(dbf_text,"simr%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		if (ip_vers==4) {
+			sprintf(dbf_text,"%08x",*((__u32*)ip));
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+		} else {
+			QETH_DBF_HEX2(0,trace,ip,QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX2(0,trace,ip+QETH_DBF_TRACE_LEN,
+				      QETH_DBF_TRACE_LEN);
+		}
+		QETH_DBF_HEX2(0,trace,mac,OSA_ADDR_LEN);
+		PRINT_WARN("trying again...\n");
+		goto retry;
+	}
+
+	return result;
+}
+
+static inline int qeth_send_delipm(qeth_card_t *card,__u8 *ip,
+				   __u8 *mac,short ip_vers)
+{
+        return qeth_send_setdelipm(card,ip,mac,IPA_CMD_DELIPM,ip_vers);
+}
+
+static int qeth_add_vipa_entry(qeth_card_t *card,int version,__u8 *addr,
+			       int flag)
+{
+	qeth_vipa_entry_t *entry,*e;
+	int result=0;
+
+	entry=(qeth_vipa_entry_t*)kmalloc(sizeof(qeth_vipa_entry_t),
+					  GFP_KERNEL);
+	if (!entry) {
+		PRINT_ERR("not enough memory for vipa handling\n");
+		return -ENOMEM;
+	}
+	entry->version=version;
+	entry->flag=flag;
+	memcpy(entry->ip,addr,16);
+	entry->state=VIPA_2_B_ADDED;
+
+	my_write_lock(&card->vipa_list_lock);
+	e=card->vipa_list;
+	while (e) {
+		if (e->version!=version) goto next;
+		if (memcmp(e->ip,addr,(version==4)?4:16)) goto next;
+		if (flag==IPA_SETIP_VIPA_FLAGS) {
+			PRINT_ERR("vipa already set\n");
+		} else {
+			PRINT_ERR("rxip already set\n");
+		}
+		kfree(entry);
+		result=-EALREADY;
+		goto out;
+next:
+		e=e->next;
+	}
+	entry->next=card->vipa_list;
+	card->vipa_list=entry;
+out:
+	my_write_unlock(&card->vipa_list_lock);
+	return result;
+}
+
+static int qeth_del_vipa_entry(qeth_card_t *card,int version,__u8 *addr,
+			       int flag)
+{
+	qeth_vipa_entry_t *e;
+	int result=0;
+
+	my_write_lock(&card->vipa_list_lock);
+	e=card->vipa_list;
+	while (e) {
+		if (e->version!=version) goto next;
+		if (e->flag!=flag) goto next;
+		if (memcmp(e->ip,addr,(version==4)?4:16)) goto next;
+		e->state=VIPA_2_B_REMOVED;
+		goto out;
+next:
+		e=e->next;
+	}
+	if (flag==IPA_SETIP_VIPA_FLAGS) {
+		PRINT_ERR("vipa not found\n");
+	} else {
+		PRINT_ERR("rxip not found\n");
+	}
+	result=-ENOENT;
+out:
+	my_write_unlock(&card->vipa_list_lock);
+	return result;
+}
+
+static void qeth_set_vipas(qeth_card_t *card,int set_only)
+{
+	qeth_vipa_entry_t *e,*le=NULL,*ne; /* ne stands for new entry,
+     					      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};
+	qeth_vipa_entry_t *priv_add_list=NULL;
+	qeth_vipa_entry_t *priv_del_list=NULL;
+
+	my_write_lock(&card->vipa_list_lock);
+	e=card->vipa_list;
+	while (e) {
+		switch (e->state) {
+		case VIPA_2_B_ADDED:
+			if (!set_only) break;
+			if (!atomic_read(&card->is_open)) break;
+			/* we don't want to hold the lock for a long time...
+			 * so we clone the entry */
+			ne=(qeth_vipa_entry_t*)
+				kmalloc(sizeof(qeth_vipa_entry_t),
+					GFP_KERNEL);
+			if (ne) {
+				ne->version=e->version;
+				memcpy(ne->ip,e->ip,16);
+				ne->next=priv_add_list;
+				priv_add_list=ne;
+
+				e->state=VIPA_ESTABLISHED;
+			} else {
+				PRINT_ERR("not enough for internal vipa " \
+					  "handling... trying to set " \
+					  "vipa next time.\n");
+				qeth_start_softsetup_thread(card);
+			}
+			break;
+		case VIPA_2_B_REMOVED:
+			if (set_only) break;
+			if (le)
+				le->next=e->next;
+			else card->vipa_list=e->next;
+			ne=e->next;
+			e->next=priv_del_list;
+			priv_del_list=e;
+			e=ne;
+			continue;
+		case VIPA_ESTABLISHED:
+			if (atomic_read(&card->is_open)) break;
+			/* we don't want to hold the lock for a long time...
+			 * so we clone the entry */
+			ne=(qeth_vipa_entry_t*)
+				kmalloc(sizeof(qeth_vipa_entry_t),
+					GFP_KERNEL);
+			if (ne) {
+				ne->version=e->version;
+				memcpy(ne->ip,e->ip,16);
+				ne->next=priv_del_list;
+				priv_del_list=ne;
+
+				e->state=VIPA_2_B_ADDED;
+			} else {
+				PRINT_ERR("not enough for internal vipa " \
+					  "handling... VIPA/RXIP remains set " \
+					  "although device is stopped.\n");
+				qeth_start_softsetup_thread(card);
+			}
+			break;
+		default:
+			break;
+		}
+		le=e;
+		e=e->next;
+	}
+	my_write_unlock(&card->vipa_list_lock);
+
+	while (priv_add_list) {
+		result=qeth_send_setdelip(card,priv_add_list->ip,netmask,
+					  IPA_CMD_SETIP,priv_add_list->version,
+					  priv_add_list->flag);
+		PRINT_SETIP_ERROR('s');
+
+		if (result) {
+			QETH_DBF_TEXT2(0,trace,"SETSVFLD");
+			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" \
+					  "unset...\n",
+					  *((__u32*)&priv_add_list->ip[0]));
+				sprintf(dbf_text,"%08x",
+					  *((__u32*)&priv_add_list->ip[0]));
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+			} else {
+				PRINT_ERR("going to leave vipa/rxip " \
+					  "%08x%08x%08x%08x unset...\n",
+					  *((__u32*)&priv_add_list->ip[0]),
+					  *((__u32*)&priv_add_list->ip[4]),
+					  *((__u32*)&priv_add_list->ip[8]),
+					  *((__u32*)&priv_add_list->ip[12]));
+				QETH_DBF_HEX2(0,trace,&priv_add_list->ip[0],
+					      QETH_DBF_TRACE_LEN);
+				QETH_DBF_HEX2(0,trace,&priv_add_list->ip[8],
+					      QETH_DBF_TRACE_LEN);
+			}
+		}
+		e=priv_add_list;
+		priv_add_list=priv_add_list->next;
+		kfree(e);
+	}
+
+	while (priv_del_list) {
+		result=qeth_send_setdelip(card,priv_del_list->ip,netmask,
+					  IPA_CMD_DELIP,priv_del_list->version,
+					  priv_del_list->flag);
+		if (result) {
+			QETH_DBF_TEXT2(0,trace,"DELSVFLD");
+			sprintf(dbf_text,"%4x%4x",card->irq0,result);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+			if (priv_del_list->version==4) {
+				PRINT_ERR("could not delete vipa/rxip " \
+					  "%08x...\n",
+					  *((__u32*)&priv_del_list->ip[0]));
+				sprintf(dbf_text,"%08x",
+					  *((__u32*)&priv_del_list->ip[0]));
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+			} else {
+				PRINT_ERR("could not delete vipa/rxip " \
+					  "%08x%08x%08x%08x...\n",
+					  *((__u32*)&priv_del_list->ip[0]),
+					  *((__u32*)&priv_del_list->ip[4]),
+					  *((__u32*)&priv_del_list->ip[8]),
+					  *((__u32*)&priv_del_list->ip[12]));
+				QETH_DBF_HEX2(0,trace,&priv_del_list->ip[0],
+					      QETH_DBF_TRACE_LEN);
+				QETH_DBF_HEX2(0,trace,&priv_del_list->ip[8],
+					      QETH_DBF_TRACE_LEN);
+			}
+		}
+		e=priv_del_list;
+		priv_del_list=priv_del_list->next;
+		kfree(e);
+	}
+}
+
+static void qeth_refresh_vipa_states(qeth_card_t *card)
+{
+	qeth_vipa_entry_t *e;
+
+	my_write_lock(&card->vipa_list_lock);
+	e=card->vipa_list;
+	while (e) {
+		if (e->state==VIPA_ESTABLISHED) e->state=VIPA_2_B_ADDED;
+		e=e->next;
+	}
+	my_write_unlock(&card->vipa_list_lock);
+}
+
+static inline int qeth_send_setrtg(qeth_card_t *card,int routing_type,
+				   short ip_vers)
+{
+        ipa_cmd_t cmd;
+
+        qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_SETRTG,ip_vers);
+	/* strip off RESET_ROUTING_FLAG */
+        cmd.data.setrtg.type=(routing_type)&(ROUTER_MASK);
+
+        return qeth_send_ipa_cmd(card,&cmd,0,IPA_CMD_STATE);
+}
+
+static int qeth_is_ipa_in_list(struct in_ifaddr *ip,struct in_ifaddr *list)
+{
+	while (list) {
+		if (ip->ifa_address==list->ifa_address) return 1;
+		list=list->ifa_next;
+	}
+	return 0;
+}
+
+#ifdef QETH_IPV6
+static int qeth_is_ipa_in_list6(struct inet6_ifaddr *ip,
+				struct inet6_ifaddr *list)
+{
+	while (list) {
+		if (!memcmp(&ip->addr.s6_addr,&list->addr.s6_addr,16))
+			return 1;
+		list=list->if_next;
+	}
+	return 0;
+}
+
+static int qeth_add_ifa6_to_list(struct inet6_ifaddr **list,
+       				 struct inet6_ifaddr *ifa)
+{
+	struct inet6_ifaddr *i;
+
+	if (*list==NULL) {
+		*list=ifa;
+	} else {
+		if (qeth_is_ipa_in_list6(ifa,*list))
+			return -EALREADY;
+		i=*list;
+		while (i->if_next) {
+			i=i->if_next;
+		}
+		i->if_next=ifa;
+	}
+	ifa->if_next=NULL;
+	return 0;
+}
+#endif /* QETH_IPV6 */
+
+static int qeth_add_ifa_to_list(struct in_ifaddr **list,struct in_ifaddr *ifa)
+{
+	struct in_ifaddr *i;
+
+	if (*list==NULL) {
+		*list=ifa;
+	} else {
+		if (qeth_is_ipa_in_list(ifa,*list))
+			return -EALREADY;
+		i=*list;
+		while (i->ifa_next) {
+			i=i->ifa_next;
+		}
+		i->ifa_next=ifa;
+	}
+	ifa->ifa_next=NULL;
+	return 0;
+}
+
+static int qeth_setips(qeth_card_t *card,int use_setip_retries)
+{
+	struct in_ifaddr *addr;
+	int result;
+	char dbf_text[15];
+#ifdef QETH_IPV6
+	struct inet6_ifaddr *addr6;
+	__u8 netmask[16];
+#endif /* QETH_IPV6 */
+
+	sprintf(dbf_text,"stip%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	addr=card->ip_current_state.ip_ifa;
+	while (addr) {
+		if (!qeth_is_ipa_in_list(addr,card->ip_new_state.ip_ifa)) {
+			QETH_DBF_TEXT3(0,trace,"setipdel");
+			*((__u32*)(&dbf_text[0]))=*((__u32*)&addr->ifa_address);
+			*((__u32*)(&dbf_text[4]))=*((__u32*)&addr->ifa_mask);
+			QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+			result=qeth_send_delip(card,(__u8*)&addr->ifa_address,
+					       (__u8*)&addr->ifa_mask,4);
+			if (result) {
+				PRINT_ERR("was not able to delete ip " \
+			    		  "%08x/%08x on irq x%x " \
+					  "(result: 0x%x), " \
+		    			  "trying to continue\n",
+	    				  addr->ifa_address,
+    					  addr->ifa_mask,card->irq0,result);
+				sprintf(dbf_text,"stdl%4x",result);
+				QETH_DBF_TEXT3(0,trace,dbf_text);
+			}
+		}
+		addr=addr->ifa_next;
+	}
+
+	addr=card->ip_new_state.ip_ifa;
+	while (addr) {
+		if (!qeth_is_ipa_in_list(addr,
+					 card->ip_current_state.ip_ifa)) {
+			QETH_DBF_TEXT3(0,trace,"setipset");
+			*((__u32*)(&dbf_text[0]))=
+				*((__u32*)&addr->ifa_address);
+			*((__u32*)(&dbf_text[4]))=
+				*((__u32*)&addr->ifa_mask);
+			QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+			result=qeth_send_setip(card,(__u8*)&addr->ifa_address,
+					       (__u8*)&addr->ifa_mask,4,
+					       use_setip_retries);
+			if (result) {
+				PRINT_ERR("was not able to set ip " \
+					  "%08x/%08x on irq x%x, trying to " \
+					  "continue\n",
+					  addr->ifa_address,
+					  addr->ifa_mask,card->irq0);
+				sprintf(dbf_text,"stst%4x",result);
+				QETH_DBF_TEXT3(0,trace,dbf_text);
+			}
+		}
+		addr=addr->ifa_next;
+	}
+
+#ifdef QETH_IPV6
+#define FILL_NETMASK(len) { \
+	int i,j; \
+	for (i=0;i<16;i++) { \
+		j=(len)-(i*8); \
+		if (j>=8) netmask[i]=0xff; else \
+			if (j<=0) netmask[i]=0x0; else \
+				netmask[i]=(__u8)(0xFF00>>j); \
+	} \
+}
+	/* here we go with IPv6 */
+	addr6=card->ip_current_state.ip6_ifa;
+	while (addr6) {
+		if (!qeth_is_ipa_in_list6(addr6,card->ip_new_state.ip6_ifa)) {
+			QETH_DBF_TEXT3(0,trace,"setipdl6");
+			QETH_DBF_HEX3(0,trace,&addr6->addr.s6_addr,
+				      QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX3(0,trace,
+				      ((char *)(&addr6->addr.s6_addr))+
+				      QETH_DBF_TRACE_LEN,QETH_DBF_TRACE_LEN);
+			sprintf(dbf_text,"nmsk%4u",addr6->prefix_len);
+			QETH_DBF_TEXT3(0,trace,dbf_text);
+			FILL_NETMASK(addr6->prefix_len);
+			result=qeth_send_delip(card,
+					       (__u8*)&addr6->addr.s6_addr,
+					       (__u8*)&netmask,6);
+			if (result) {
+				PRINT_ERR("was not able to delete ip " \
+			    		  "%04x:%04x:%04x:%04x:%04x:%04x:" \
+					  "%04x:%04x/%u on irq x%x " \
+					  "(result: 0x%x), " \
+		    			  "trying to continue\n",
+					  addr6->addr.s6_addr16[0],
+					  addr6->addr.s6_addr16[1],
+					  addr6->addr.s6_addr16[2],
+					  addr6->addr.s6_addr16[3],
+					  addr6->addr.s6_addr16[4],
+					  addr6->addr.s6_addr16[5],
+					  addr6->addr.s6_addr16[6],
+					  addr6->addr.s6_addr16[7],
+					  addr6->prefix_len,
+					  card->irq0,result);
+				sprintf(dbf_text,"std6%4x",result);
+				QETH_DBF_TEXT3(0,trace,dbf_text);
+			}
+		}
+		addr6=addr6->if_next;
+	}
+
+	addr6=card->ip_new_state.ip6_ifa;
+	while (addr6) {
+		if (!qeth_is_ipa_in_list6(addr6,
+					  card->ip_current_state.ip6_ifa)) {
+			QETH_DBF_TEXT3(0,trace,"setipst6");
+			QETH_DBF_HEX3(0,trace,&addr6->addr.s6_addr,
+				      QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX3(0,trace,
+				      ((char *)(&addr6->addr.s6_addr))+
+				      QETH_DBF_TRACE_LEN,QETH_DBF_TRACE_LEN);
+			sprintf(dbf_text,"nmsk%4u",addr6->prefix_len);
+			QETH_DBF_TEXT3(0,trace,dbf_text);
+			FILL_NETMASK(addr6->prefix_len);
+			result=qeth_send_setip(card,
+					       (__u8*)&addr6->addr.s6_addr,
+					       (__u8*)&netmask,6,
+					       use_setip_retries);
+			if (result) {
+				PRINT_ERR("was not able to set ip " \
+			    		  "%04x:%04x:%04x:%04x:%04x:%04x:" \
+					  "%04x:%04x/%u on irq x%x " \
+					  "(result: 0x%x), " \
+		    			  "trying to continue\n",
+					  addr6->addr.s6_addr16[0],
+					  addr6->addr.s6_addr16[1],
+					  addr6->addr.s6_addr16[2],
+					  addr6->addr.s6_addr16[3],
+					  addr6->addr.s6_addr16[4],
+					  addr6->addr.s6_addr16[5],
+					  addr6->addr.s6_addr16[6],
+					  addr6->addr.s6_addr16[7],
+					  addr6->prefix_len,
+					  card->irq0,result);
+				sprintf(dbf_text,"sts6%4x",result);
+				QETH_DBF_TEXT3(0,trace,dbf_text);
+			}
+		}
+		addr6=addr6->if_next;
+	}
+#endif /* QETH_IPV6 */
+
+        return 0;
+}
+
+static int qeth_is_ipma_in_list(struct qeth_ipm_mac *ipma,
+				struct qeth_ipm_mac *list)
+{
+	while (list) {
+		if ( (!memcmp(ipma->ip,list->ip,16)) &&
+		     (!memcmp(ipma->mac,list->mac,6)) ) return 1;
+		list=list->next;
+	}
+	return 0;
+}
+
+static void qeth_remove_mc_ifa_from_list(struct qeth_ipm_mac **list,
+       					 struct qeth_ipm_mac *ipma)
+{
+	struct qeth_ipm_mac *i,*li=NULL;
+
+	if ((!(*list)) || (!ipma)) return;
+
+	if (*list==ipma) {
+		*list=ipma->next;
+	} else {
+		i=*list;
+		while (i) {
+			if (i==ipma) {
+				li->next=i->next;
+			} else {
+				li=i;
+			}
+			i=i->next;
+		}
+	}
+}
+
+static int qeth_add_mc_ifa_to_list(struct qeth_ipm_mac **list,
+       				   struct qeth_ipm_mac *ipma)
+{
+	struct qeth_ipm_mac *i;
+
+	if (qeth_is_ipma_in_list(ipma,*list))
+		return -EALREADY;
+
+	if (*list==NULL) {
+		*list=ipma;
+	} else {
+		i=*list;
+		while (i->next) {
+			i=i->next;
+		}
+		i->next=ipma;
+	}
+	ipma->next=NULL;
+	return 0;
+}
+
+static int qeth_setipms(qeth_card_t *card,int use_setipm_retries)
+{
+	struct qeth_ipm_mac *addr;
+	int result;
+	char dbf_text[15];
+
+	sprintf(dbf_text,"stim%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+        if (qeth_is_supported(IPA_MULTICASTING)) {
+		addr=card->ip_mc_current_state.ipm_ifa;
+		while (addr) {
+			if (!qeth_is_ipma_in_list(addr,card->
+						  ip_mc_new_state.ipm_ifa)) {
+				QETH_DBF_TEXT3(0,trace,"setimdel");
+				sprintf(dbf_text,"%08x",
+					*((__u32*)&addr->ip[0]));
+				QETH_DBF_TEXT3(0,trace,dbf_text);
+				*((__u32*)(&dbf_text[0]))=
+					*((__u32*)&addr->mac);
+				*((__u32*)(&dbf_text[4]))=
+					*(((__u32*)&addr->mac)+1);
+				QETH_DBF_HEX3(0,trace,dbf_text,
+					      QETH_DBF_TRACE_LEN);
+				result=qeth_send_delipm(
+					card,(__u8*)&addr->ip[0],
+					(__u8*)addr->mac,4);
+				if (result) {
+					PRINT_ERR("was not able to delete " \
+						  "multicast ip %08x/" \
+						  "%02x%02x%02x%02x%02x%02x " \
+						  "on irq x%x " \
+						  "(result: 0x%x), " \
+						  "trying to continue\n",
+						  *((__u32*)&addr->ip[0]),
+						  addr->mac[0],addr->mac[1],
+						  addr->mac[2],addr->mac[3],
+						  addr->mac[4],addr->mac[5],
+						  card->irq0,result);
+					sprintf(dbf_text,"smdl%4x",result);
+					QETH_DBF_TEXT3(0,trace,dbf_text);
+				}
+			}
+			addr=addr->next;
+		}
+		
+		addr=card->ip_mc_new_state.ipm_ifa;
+		while (addr) {
+			if (!qeth_is_ipma_in_list(addr,card->
+						  ip_mc_current_state.
+						  ipm_ifa)) {
+				QETH_DBF_TEXT3(0,trace,"setimset");
+				sprintf(dbf_text,"%08x",
+					*((__u32*)&addr->ip[0]));
+				QETH_DBF_TEXT3(0,trace,dbf_text);
+				*((__u32*)(&dbf_text[0]))=
+					*((__u32*)&addr->mac);
+				*((__u32*)(&dbf_text[4]))=
+					*(((__u32*)&addr->mac)+1);
+				QETH_DBF_HEX3(0,trace,dbf_text,
+					      QETH_DBF_TRACE_LEN);
+				result=qeth_send_setipm(
+					card,(__u8*)&addr->ip[0],
+					(__u8*)addr->mac,4,
+					use_setipm_retries);
+				if (result) {
+					PRINT_ERR("was not able to set " \
+						  "multicast ip %08x/" \
+						  "%02x%02x%02x%02x%02x%02x " \
+						  "on irq x%x " \
+						  "(result: 0x%x), " \
+						  "trying to continue\n",
+						  *((__u32*)&addr->ip[0]),
+						  addr->mac[0],addr->mac[1],
+						  addr->mac[2],addr->mac[3],
+						  addr->mac[4],addr->mac[5],
+						  card->irq0,result);
+					sprintf(dbf_text,"smst%4x",result);
+					QETH_DBF_TEXT3(0,trace,dbf_text);
+					qeth_remove_mc_ifa_from_list(
+						&card->ip_mc_current_state.
+						ipm_ifa,addr);
+				}
+			}
+			addr=addr->next;
+		}
+
+#ifdef QETH_IPV6
+		/* here we go with IPv6 */
+		addr=card->ip_mc_current_state.ipm6_ifa;
+		while (addr) {
+			if (!qeth_is_ipma_in_list(addr,card->
+						  ip_mc_new_state.ipm6_ifa)) {
+				QETH_DBF_TEXT3(0,trace,"setimdl6");
+				QETH_DBF_HEX3(0,trace,&addr->ip[0],
+					      QETH_DBF_TRACE_LEN);
+				QETH_DBF_HEX3(0,trace,(&addr->ip[0])+
+					      QETH_DBF_TRACE_LEN,
+					      QETH_DBF_TRACE_LEN);
+				QETH_DBF_HEX3(0,trace,&addr->mac,
+					      QETH_DBF_TRACE_LEN);
+				result=qeth_send_delipm(
+					card,(__u8*)&addr->ip[0],
+					(__u8*)addr->mac,6);
+				if (result) {
+					PRINT_ERR("was not able to delete " \
+						  "multicast ip %04x:%04x:" \
+						  "%04x:%04x:%04x:%04x:" \
+						  "%04x:%04x/" \
+						  "%02x%02x%02x%02x%02x%02x " \
+						  "on irq x%x " \
+						  "(result: 0x%x), " \
+						  "trying to continue\n",
+						  *((__u16*)&addr->ip[0]),
+						  *((__u16*)&addr->ip[2]),
+						  *((__u16*)&addr->ip[4]),
+						  *((__u16*)&addr->ip[6]),
+						  *((__u16*)&addr->ip[8]),
+						  *((__u16*)&addr->ip[10]),
+						  *((__u16*)&addr->ip[12]),
+						  *((__u16*)&addr->ip[14]),
+						  addr->mac[0],addr->mac[1],
+						  addr->mac[2],addr->mac[3],
+						  addr->mac[4],addr->mac[5],
+						  card->irq0,result);
+					sprintf(dbf_text,"smd6%4x",result);
+					QETH_DBF_TEXT3(0,trace,dbf_text);
+				}
+			}
+			addr=addr->next;
+		}
+		
+		addr=card->ip_mc_new_state.ipm6_ifa;
+		while (addr) {
+			if (!qeth_is_ipma_in_list(addr,card->
+						  ip_mc_current_state.
+						  ipm6_ifa)) {
+				QETH_DBF_TEXT3(0,trace,"setimst6");
+				QETH_DBF_HEX3(0,trace,&addr->ip[0],
+					      QETH_DBF_TRACE_LEN);
+				QETH_DBF_HEX3(0,trace,(&addr->ip[0])+
+					      QETH_DBF_TRACE_LEN,
+					      QETH_DBF_TRACE_LEN);
+				QETH_DBF_HEX3(0,trace,&addr->mac,
+					      QETH_DBF_TRACE_LEN);
+				result=qeth_send_setipm(
+					card,(__u8*)&addr->ip[0],
+					(__u8*)addr->mac,6,
+					use_setipm_retries);
+				if (result) {
+					PRINT_ERR("was not able to set " \
+						  "multicast ip %04x:%04x:" \
+						  "%04x:%04x:%04x:%04x:" \
+						  "%04x:%04x/" \
+						  "%02x%02x%02x%02x%02x%02x " \
+						  "on irq x%x " \
+						  "(result: 0x%x), " \
+						  "trying to continue\n",
+						  *((__u16*)&addr->ip[0]),
+						  *((__u16*)&addr->ip[2]),
+						  *((__u16*)&addr->ip[4]),
+						  *((__u16*)&addr->ip[6]),
+						  *((__u16*)&addr->ip[8]),
+						  *((__u16*)&addr->ip[10]),
+						  *((__u16*)&addr->ip[12]),
+						  *((__u16*)&addr->ip[14]),
+						  addr->mac[0],addr->mac[1],
+						  addr->mac[2],addr->mac[3],
+						  addr->mac[4],addr->mac[5],
+						  card->irq0,result);
+					sprintf(dbf_text,"sms6%4x",result);
+					QETH_DBF_TEXT3(0,trace,dbf_text);
+					qeth_remove_mc_ifa_from_list(
+						&card->ip_mc_current_state.
+						ipm6_ifa,addr);
+				}
+			}
+			addr=addr->next;
+		}
+#endif /* QETH_IPV6 */
+                return 0;
+        } else return 0;
+}
+
+static void qeth_clone_ifa(struct in_ifaddr *src,struct in_ifaddr *dest)
+{
+	memcpy(dest,src,sizeof(struct in_ifaddr));
+	dest->ifa_next=NULL;
+}
+
+#ifdef QETH_IPV6
+static void qeth_clone_ifa6(struct inet6_ifaddr *src,
+			    struct inet6_ifaddr *dest)
+{
+	memcpy(dest,src,sizeof(struct inet6_ifaddr));
+	dest->if_next=NULL;
+}
+#endif /* QETH_IPV6 */
+
+#define QETH_STANDARD_RETVALS \
+		ret_val=-EIO; \
+		if (result==IPA_REPLY_SUCCESS) ret_val=0; \
+		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];
+
+	card=(qeth_card_t*)dev->priv;
+
+	sprintf(dbf_text,"ioct%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	sprintf(dbf_text,"cmd=%4x",cmd);
+	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 (atomic_read(&card->shutdown_phase)) return -ENODEV;
+	if ( (!atomic_read(&card->is_registered))||
+	     (!atomic_read(&card->is_hardsetup))||
+	     (atomic_read(&card->is_gone)) ) {
+		ret_val=-ENODEV;
+		goto out;
+	}
+
+	switch (cmd) {
+	case SIOCDEVPRIVATE+0:
+		if (!capable(CAP_NET_ADMIN)) {
+			ret_val=-EPERM;
+			break;
+		}
+		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)) {
+			ret_val=-EPERM;
+			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)) {
+			ret_val=-EPERM;
+			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)) {
+			ret_val=-EPERM;
+			break;
+		}
+		for (i=4;i<12;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)) {
+			ret_val=-EPERM;
+			break;
+		}
+		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,
+					      IPA_SETADP_SET_SNMP_CONTROL,
+					      data,4);
+		QETH_STANDARD_RETVALS;
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+out:
+	my_spin_unlock(&card->ioctl_lock);
+
+	sprintf(dbf_text,"ret=%4x",ret_val);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+	return ret_val;
+}
+
+static void qeth_clear_ifamc_list(struct qeth_ipm_mac **ifa_list)
+{
+	struct qeth_ipm_mac *ifa;
+	while (*ifa_list) {
+		ifa=*ifa_list;
+		*ifa_list=ifa->next;
+		kfree(ifa);
+	}
+}
+
+#ifdef QETH_IPV6
+static void qeth_clear_ifa6_list(struct inet6_ifaddr **ifa_list)
+{
+	struct inet6_ifaddr *ifa;
+	while (*ifa_list) {
+		ifa=*ifa_list;
+		*ifa_list=ifa->if_next;
+		kfree(ifa);
+	}
+}
+
+static void qeth_takeover_ip_ipms6(qeth_card_t *card)
+{
+	struct inet6_ifaddr *ifa,*ifanew;
+	char dbf_text[15];
+	int remove;
+#ifdef QETH_VLAN
+        struct vlan_group *card_group;
+	int i;
+#endif
+
+	struct qeth_ipm_mac *ipmanew;
+        struct ifmcaddr6 *im6;
+        struct inet6_dev *in6_dev;
+#ifdef QETH_VLAN
+        struct inet6_dev *in6_vdev;
+#endif
+	char buf[MAX_ADDR_LEN];
+
+	sprintf(dbf_text,"tip6%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+	/* unicast */
+	/* clear ip_current_state */
+	qeth_clear_ifa6_list(&card->ip_current_state.ip6_ifa);
+	/* take it over */
+	card->ip_current_state.ip6_ifa=card->ip_new_state.ip6_ifa;
+	card->ip_new_state.ip6_ifa=NULL;
+
+	/* multicast */
+	/* clear ip_mc_current_state */
+	qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm6_ifa);
+	/* take it over */
+	card->ip_mc_current_state.ipm6_ifa=card->ip_mc_new_state.ipm6_ifa;
+	/* get new one, we try to have the same order as ifa_list in device
+	   structure, for what reason ever*/
+	card->ip_mc_new_state.ipm6_ifa=NULL;
+
+        if((in6_dev=in6_dev_get(card->dev))==NULL) {
+		sprintf(dbf_text,"id16%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+                goto out;
+	}
+        read_lock(&in6_dev->lock);
+
+	/* get new one, we try to have the same order as ifa_list in device
+	   structure, for what reason ever*/
+	QETH_DBF_TEXT4(0,trace,"to-ip6s");
+	if ( (atomic_read(&card->is_open)) && (card->dev->ip6_ptr) &&
+	     (((struct inet6_dev*)card->dev->ip6_ptr)->addr_list) ) {
+		ifa=((struct inet6_dev*)card->dev->ip6_ptr)->addr_list;
+
+		while (ifa) {
+			ifanew=kmalloc(sizeof(struct inet6_ifaddr),GFP_KERNEL);
+			if (!ifanew) {
+				PRINT_WARN("No memory for IP address " \
+					   "handling. Some of the IPs " \
+					   "will not be set on %s.\n",
+					   card->dev_name);
+				QETH_DBF_TEXT2(0,trace,"TOIPNMEM");
+			} else {
+				qeth_clone_ifa6(ifa,ifanew);
+				remove=qeth_add_ifa6_to_list(
+					&card->ip_new_state.ip6_ifa,ifanew);
+				QETH_DBF_HEX4(0,trace,&ifanew->addr.s6_addr,
+					      QETH_DBF_TRACE_LEN);
+				QETH_DBF_HEX4(0,trace,&ifanew->addr.s6_addr+
+					      QETH_DBF_TRACE_LEN,
+					      QETH_DBF_TRACE_LEN);
+				sprintf(dbf_text,"pref%4u",ifanew->prefix_len);
+				QETH_DBF_TEXT4(0,trace,dbf_text);
+				if (remove) {
+					kfree(ifanew);
+					QETH_DBF_TEXT4(0,trace,"alrdy6rm");
+				}
+			}
+			ifa=ifa->if_next;
+		}
+	}
+#ifdef QETH_VLAN
+/*append all known VLAN IP Addresses corresponding 
+  to the real device card->dev->ifindex 
+*/
+	QETH_DBF_TEXT4(0,trace,"to-vip6s");
+	if ( (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++) {
+			if ( (card_group->vlan_devices[i]) &&
+			     (card_group->vlan_devices[i]->flags&IFF_UP)&&
+			     ((struct inet6_dev *) card_group->
+			      vlan_devices[i]->ip6_ptr) ) {
+				ifa=((struct inet6_dev *) 
+				     card_group->vlan_devices[i]->ip6_ptr)->
+					addr_list;
+				
+		while (ifa) {
+			ifanew=kmalloc(sizeof(struct inet6_ifaddr),GFP_KERNEL);
+			if (!ifanew) {
+				PRINT_WARN("No memory for IP address " \
+					   "handling. Some of the IPs " \
+					   "will not be set on %s.\n",
+					   card->dev_name);
+				QETH_DBF_TEXT2(0,trace,"TOIPNMEM");
+			} else {
+				qeth_clone_ifa6(ifa,ifanew);
+				remove=qeth_add_ifa6_to_list
+					(&card->ip_new_state.ip6_ifa,ifanew);
+				QETH_DBF_HEX4(0,trace,&ifanew->addr.s6_addr,
+					      QETH_DBF_TRACE_LEN);
+				QETH_DBF_HEX4(0,trace,&ifanew->addr.s6_addr+
+					      QETH_DBF_TRACE_LEN,
+					      QETH_DBF_TRACE_LEN);
+				sprintf(dbf_text,"pref%4u",ifanew->prefix_len);
+				QETH_DBF_TEXT4(0,trace,dbf_text);
+				if (remove) {
+					kfree(ifanew);
+					QETH_DBF_TEXT4(0,trace,"alrdv6rm");
+				}
+			}
+			ifa=ifa->if_next;
+		}
+			}
+		}
+	}
+#endif
+	
+	QETH_DBF_TEXT4(0,trace,"to-ipm6s");
+	if (atomic_read(&card->is_open))
+	for (im6=in6_dev->mc_list;im6;im6=im6->next) {
+		ndisc_mc_map(&im6->mca_addr,buf,card->dev,0);
+		ipmanew=(struct qeth_ipm_mac*)kmalloc(
+			sizeof(struct qeth_ipm_mac),GFP_KERNEL);
+		if (!ipmanew) {
+			PRINT_WARN("No memory for IPM address " \
+				   "handling. Multicast IP " \
+				   "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" \
+				   "will not be set on %s.\n",
+				   im6->mca_addr.s6_addr16[0],
+				   im6->mca_addr.s6_addr16[1],
+				   im6->mca_addr.s6_addr16[2],
+				   im6->mca_addr.s6_addr16[3],
+				   im6->mca_addr.s6_addr16[4],
+				   im6->mca_addr.s6_addr16[5],
+				   im6->mca_addr.s6_addr16[6],
+				   im6->mca_addr.s6_addr16[7],
+				   card->dev_name);
+			QETH_DBF_TEXT2(0,trace,"TOIPMNMM");
+		} else {
+			memset(ipmanew,0,sizeof(struct qeth_ipm_mac));
+			memcpy(ipmanew->mac,buf,OSA_ADDR_LEN);
+			memcpy(ipmanew->ip,im6->mca_addr.s6_addr,16);
+			ipmanew->next=NULL;
+			remove=qeth_add_mc_ifa_to_list(
+				&card->ip_mc_new_state.ipm6_ifa,ipmanew);
+			QETH_DBF_HEX4(0,trace,&ipmanew->ip,
+				      QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX4(0,trace,&ipmanew->ip+
+				      QETH_DBF_TRACE_LEN,
+				      QETH_DBF_TRACE_LEN);
+			QETH_DBF_HEX4(0,trace,&ipmanew->mac,
+				      QETH_DBF_TRACE_LEN);
+			if (remove) {
+				QETH_DBF_TEXT4(0,trace,"mlrdy6rm");
+				kfree(ipmanew);
+			}
+		}
+	}
+#ifdef QETH_VLAN
+	QETH_DBF_TEXT4(0,trace,"tovipm6s");
+	if ( (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++) {
+			if ((card_group->vlan_devices[i])&&
+			    (card_group->vlan_devices[i]->flags&IFF_UP)) {
+				in6_vdev=in6_dev_get(card_group->
+						     vlan_devices[i]);
+				if(!(in6_vdev==NULL)) {
+					read_lock(&in6_vdev->lock);
+		for (im6=in6_vdev->mc_list;
+		     im6;im6=im6->next) {
+			ndisc_mc_map(&im6->mca_addr,
+				     buf,card_group->vlan_devices[i],
+				     0);
+			ipmanew=(struct qeth_ipm_mac*)
+				kmalloc(sizeof(struct qeth_ipm_mac),
+					GFP_KERNEL);
+			if (!ipmanew) {
+				PRINT_WARN("No memory for IPM address " \
+					   "handling. Multicast IP " \
+					   "%04x:%04x:%04x:%04x:" \
+					   "%04x:%04x:%04x:%04x" \
+					   "will not be set on %s.\n",
+					   im6->mca_addr.s6_addr16[0],
+					   im6->mca_addr.s6_addr16[1],
+					   im6->mca_addr.s6_addr16[2],
+					   im6->mca_addr.s6_addr16[3],
+					   im6->mca_addr.s6_addr16[4],
+					   im6->mca_addr.s6_addr16[5],
+					   im6->mca_addr.s6_addr16[6],
+					   im6->mca_addr.s6_addr16[7],
+					   card->dev_name);
+				QETH_DBF_TEXT2(0,trace,"TOIPMNMM");
+			} else {
+				memset(ipmanew,0,
+				       sizeof(struct qeth_ipm_mac));
+				memcpy(ipmanew->mac,buf,OSA_ADDR_LEN);
+				memcpy(ipmanew->ip,
+				       im6->mca_addr.s6_addr,16);
+				ipmanew->next=NULL;
+				remove=qeth_add_mc_ifa_to_list
+					(&card->ip_mc_new_state.ipm6_ifa,
+					 ipmanew);
+				QETH_DBF_HEX4(0,trace,&ipmanew->ip,
+					      QETH_DBF_TRACE_LEN);
+				QETH_DBF_HEX4(0,trace,&ipmanew->ip+
+					      QETH_DBF_TRACE_LEN,
+					      QETH_DBF_TRACE_LEN);
+				QETH_DBF_HEX4(0,trace,&ipmanew->mac,
+					      QETH_DBF_TRACE_LEN);
+				
+				if (remove) {
+					QETH_DBF_TEXT4(0,trace,"mlrdv6rm");
+					kfree(ipmanew);
+				}
+			}
+		}
+					read_unlock(&in6_vdev->lock);
+					in6_dev_put(in6_vdev);
+				} else {
+					sprintf(dbf_text,"id26%4x",card->irq0);
+					QETH_DBF_TEXT2(0,trace,dbf_text);
+				}
+			}
+		}
+	}
+#endif
+        read_unlock(&in6_dev->lock);
+        in6_dev_put(in6_dev);
+ out:
+	;
+}
+#endif /* QETH_IPV6 */
+
+static void qeth_clear_ifa4_list(struct in_ifaddr **ifa_list)
+{
+	struct in_ifaddr *ifa;
+	while (*ifa_list) {
+		ifa=*ifa_list;
+		*ifa_list=ifa->ifa_next;
+		kfree(ifa);
+	}
+}
+
+static void qeth_takeover_ip_ipms(qeth_card_t *card)
+{
+	struct in_ifaddr *ifa,*ifanew;
+	char dbf_text[15];
+	int remove;
+#ifdef QETH_VLAN
+	struct vlan_group *card_group;
+	int i;
+        struct in_device        *vin4_dev;
+#endif
+
+	struct qeth_ipm_mac *ipmanew;
+        struct ip_mc_list       *im4;
+        struct in_device        *in4_dev;
+	char buf[MAX_ADDR_LEN];
+	__u32 maddr;
+
+	sprintf(dbf_text,"tips%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+	/* unicast */
+	/* clear ip_current_state */
+	qeth_clear_ifa4_list(&card->ip_current_state.ip_ifa);
+	/* take it over */
+	card->ip_current_state.ip_ifa=card->ip_new_state.ip_ifa;
+	card->ip_new_state.ip_ifa=NULL;
+
+	/* multicast */
+	/* clear ip_mc_current_state */
+	qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm_ifa);
+	/* take it over */
+	card->ip_mc_current_state.ipm_ifa=card->ip_mc_new_state.ipm_ifa;
+	/* get new one, we try to have the same order as ifa_list in device
+	   structure, for what reason ever*/
+	card->ip_mc_new_state.ipm_ifa=NULL;
+
+        if((in4_dev=in_dev_get(card->dev))==NULL) {
+		QETH_DBF_TEXT2(0,trace,"nodvhol1");
+		QETH_DBF_TEXT2(0,trace,card->dev_name);
+                return;
+	}
+        read_lock(&in4_dev->lock);
+
+	/* get new one, we try to have the same order as ifa_list in device
+	   structure, for what reason ever*/
+	QETH_DBF_TEXT4(0,trace,"to-ips");
+	if ( (atomic_read(&card->is_open)) && (card->dev->ip_ptr) &&
+	     (((struct in_device*)card->dev->ip_ptr)->ifa_list) ) {
+		ifa=((struct in_device*)card->dev->ip_ptr)->ifa_list;
+
+		while (ifa) {
+			ifanew=kmalloc(sizeof(struct in_ifaddr),GFP_KERNEL);
+			if (!ifanew) {
+				PRINT_WARN("No memory for IP address " \
+					   "handling. Some of the IPs " \
+					   "will not be set on %s.\n",
+					   card->dev_name);
+				QETH_DBF_TEXT2(0,trace,"TOIPNMEM");
+			} else {
+				qeth_clone_ifa(ifa,ifanew);
+				remove=qeth_add_ifa_to_list(
+					&card->ip_new_state.ip_ifa,ifanew);
+				*((__u32*)(&dbf_text[0]))=
+					*((__u32*)&ifanew->ifa_address);
+				*((__u32*)(&dbf_text[4]))=
+					*((__u32*)&ifanew->ifa_mask);
+				QETH_DBF_TEXT4(0,trace,dbf_text);
+				if (remove) {
+					kfree(ifanew);
+					QETH_DBF_TEXT4(0,trace,"alrdy4rm");
+				}
+			}
+			
+			ifa=ifa->ifa_next;
+		}
+	}
+
+#ifdef QETH_VLAN
+	/* append all known VLAN IP Addresses corresponding to the
+	 * real device card->dev->ifindex */
+	QETH_DBF_TEXT4(0,trace,"to-vips");
+	if ( (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++) {
+			if ((vin4_dev=in_dev_get(card->dev))) {
+				read_lock(&vin4_dev->lock);
+			if ((card_group->vlan_devices[i])&&
+			    (card_group->vlan_devices[i]->flags&IFF_UP)) {
+			    ifa=((struct in_device*)
+				 card_group->vlan_devices[i]->ip_ptr)->
+				    ifa_list;
+			    while (ifa) {
+				ifanew=kmalloc(sizeof(struct in_ifaddr),
+					       GFP_KERNEL);
+				if (!ifanew) {
+				    PRINT_WARN("No memory for IP address " \
+					       "handling. Some of the IPs " \
+					       "will not be set on %s.\n",
+					       card->dev_name);
+				    QETH_DBF_TEXT2(0,trace,"TOIPNMEM");
+				} else {
+					qeth_clone_ifa(ifa,ifanew);
+					remove=qeth_add_ifa_to_list(
+						&card->ip_new_state.ip_ifa,
+						ifanew);
+					*((__u32*)(&dbf_text[0]))=
+						*((__u32*)&ifanew->
+						  ifa_address);
+					*((__u32*)(&dbf_text[4]))=
+						*((__u32*)&ifanew->ifa_mask);
+					QETH_DBF_TEXT4(0,trace,dbf_text);
+					if (remove) {
+						kfree(ifanew);
+						QETH_DBF_TEXT4(0,trace,
+							       "alrdv4rm");
+					}
+				}
+				ifa=ifa->ifa_next;
+			    }
+			}
+			read_unlock(&vin4_dev->lock);
+			in_dev_put(vin4_dev);
+			} else {
+				QETH_DBF_TEXT2(0,trace,"nodvhol2");
+				QETH_DBF_TEXT2(0,trace,card->dev_name);
+			}
+		    }
+		}
+	}
+#endif /* QETH_VLAN */
+
+	QETH_DBF_TEXT4(0,trace,"to-ipms");
+	if (atomic_read(&card->is_open))
+	for (im4=in4_dev->mc_list;im4;im4=im4->next) {
+		qeth_get_mac_for_ipm(im4->multiaddr,buf,in4_dev->dev);
+		ipmanew=(struct qeth_ipm_mac*)kmalloc(
+			sizeof(struct qeth_ipm_mac),GFP_KERNEL);
+		if (!ipmanew) {
+			PRINT_WARN("No memory for IPM address " \
+				   "handling. Multicast IP %08x" \
+				   "will not be set on %s.\n",
+				   (__u32)im4->multiaddr,
+				   card->dev_name);
+			QETH_DBF_TEXT2(0,trace,"TOIPMNMM");
+		} else {
+			memset(ipmanew,0,sizeof(struct qeth_ipm_mac));
+			memcpy(ipmanew->mac,buf,OSA_ADDR_LEN);
+			maddr=im4->multiaddr;
+			memcpy(&(ipmanew->ip[0]),&maddr,4);
+			memset(&(ipmanew->ip[4]),0xff,12);
+			ipmanew->next=NULL;
+			remove=qeth_add_mc_ifa_to_list(
+				&card->ip_mc_new_state.ipm_ifa,ipmanew);
+			sprintf(dbf_text,"%08x",*((__u32*)&ipmanew->ip));
+			QETH_DBF_TEXT4(0,trace,dbf_text);
+			QETH_DBF_HEX4(0,trace,&ipmanew->mac,
+				      QETH_DBF_TRACE_LEN);
+			if (remove) {
+				QETH_DBF_TEXT4(0,trace,"mlrdy4rm");
+				kfree(ipmanew);
+			}
+		}
+	}
+
+#ifdef QETH_VLAN
+	QETH_DBF_TEXT4(0,trace,"to-vipms");
+	if ( (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++) {
+			if ((card_group->vlan_devices[i])&&
+			    (card_group->vlan_devices[i]->flags&IFF_UP)) {
+				if ((vin4_dev=in_dev_get(card_group->
+				 			 vlan_devices[i]))) {
+					read_lock(&vin4_dev->lock);
+		for (im4=vin4_dev->mc_list;im4;im4=im4->next) {
+			qeth_get_mac_for_ipm(im4->multiaddr,buf,vin4_dev->dev);
+			ipmanew=(struct qeth_ipm_mac*)kmalloc(
+				sizeof(struct qeth_ipm_mac),GFP_KERNEL);
+			if (!ipmanew) {
+				PRINT_WARN("No memory for IPM address " \
+					   "handling. Multicast VLAN IP %08x" \
+					   "will not be set on %s.\n",
+					   (__u32)im4->multiaddr,
+					   card->dev_name);
+				QETH_DBF_TEXT2(0,trace,"TOIPMNMM");
+			} else {
+				memset(ipmanew,0,sizeof(struct qeth_ipm_mac));
+				memcpy(ipmanew->mac,buf,OSA_ADDR_LEN);
+				maddr=im4->multiaddr;
+				memcpy(&(ipmanew->ip[0]),&maddr,4);
+				memset(&(ipmanew->ip[4]),0xff,12);
+				ipmanew->next=NULL;
+				remove=qeth_add_mc_ifa_to_list(
+				       &card->ip_mc_new_state.ipm_ifa,
+				       ipmanew);
+				sprintf(dbf_text,"%08x",
+					*((__u32*)&ipmanew->ip));
+				QETH_DBF_TEXT4(0,trace,dbf_text);
+				QETH_DBF_HEX4(0,trace,&ipmanew->mac,
+					      QETH_DBF_TRACE_LEN);
+				if (remove) {
+					QETH_DBF_TEXT4(0,trace,"mlrdv4rm");
+					kfree(ipmanew);
+				}
+			}
+		}
+					read_unlock(&vin4_dev->lock);
+					in_dev_put(vin4_dev);
+				} else {
+					QETH_DBF_TEXT2(0,trace,"novdhol3");
+					QETH_DBF_TEXT2(0,trace,card->dev_name);
+					QETH_DBF_TEXT2(0,trace,card_group->
+						       vlan_devices[i]->name);
+				}
+			    }
+			}
+	}
+#endif /* QETH_VLAN */
+
+        read_unlock(&in4_dev->lock);
+        in_dev_put(in4_dev);
+}
+
+static void qeth_get_unique_id(qeth_card_t *card)
+{
+#ifdef QETH_IPV6
+#ifdef CONFIG_SHARED_IPV6_CARDS
+        ipa_cmd_t cmd;
+	int result;
+	char dbf_text[15];
+
+	if (!qeth_is_supported(IPA_IPv6)) {
+		card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|
+			UNIQUE_ID_NOT_BY_CARD;
+		return;
+	}
+        qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_CREATE_ADDR,6);
+
+	*((__u16*)&cmd.data.create_destroy_addr.unique_id[6])=card->unique_id;
+
+	result=qeth_send_ipa_cmd(card,&cmd,1,IPA_CMD_STATE);
+
+	if (result) {
+		card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|
+			UNIQUE_ID_NOT_BY_CARD;
+		PRINT_WARN("couldn't get a unique id from the card on irq " \
+			   "x%x (result=x%x), using default id. ipv6 " \
+			   "autoconfig on other lpars may lead to duplicate " \
+			   "ip addresses. please use manually " \
+			   "configured ones.\n",card->irq0,result);
+		QETH_DBF_TEXT2(0,trace,"unid fld");
+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+	} else { 
+		card->unique_id=
+			*((__u16*)&cmd.data.create_destroy_addr.unique_id[6]);
+		QETH_DBF_TEXT2(0,setup,"uniqueid");
+		sprintf(dbf_text,"%4x%4x",card->irq0,card->unique_id);
+		QETH_DBF_TEXT2(0,setup,dbf_text);
+	}
+#else /* CONFIG_SHARED_IPV6_CARDS */
+	card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|UNIQUE_ID_NOT_BY_CARD;
+#endif /* CONFIG_SHARED_IPV6_CARDS */
+#else /* QETH_IPV6 */
+	card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|UNIQUE_ID_NOT_BY_CARD;
+#endif /* QETH_IPV6 */
+}
+
+static void qeth_put_unique_id(qeth_card_t *card)
+{
+#ifdef QETH_IPV6
+#ifdef CONFIG_SHARED_IPV6_CARDS
+        ipa_cmd_t cmd;
+	int result;
+	char dbf_text[15];
+
+	/* is also true, if ipv6 is not supported on the card */
+	if ((card->unique_id&UNIQUE_ID_NOT_BY_CARD)==UNIQUE_ID_NOT_BY_CARD)
+		return;
+
+        qeth_fill_ipa_cmd(card,&cmd,IPA_CMD_DESTROY_ADDR,6);
+	*((__u16*)&cmd.data.create_destroy_addr.unique_id[6])=card->unique_id;
+	memcpy(&cmd.data.create_destroy_addr.unique_id[0],
+	       card->dev->dev_addr,OSA_ADDR_LEN);
+
+	result=qeth_send_ipa_cmd(card,&cmd,1,IPA_CMD_STATE);
+
+	if (result) {
+		QETH_DBF_TEXT2(0,trace,"unibkfld");
+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+	}
+#else /* CONFIG_SHARED_IPV6_CARDS */
+	card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|UNIQUE_ID_NOT_BY_CARD;
+#endif /* CONFIG_SHARED_IPV6_CARDS */
+#else /* QETH_IPV6 */
+	card->unique_id=UNIQUE_ID_IF_CREATE_ADDR_FAILED|UNIQUE_ID_NOT_BY_CARD;
+#endif /* QETH_IPV6 */
+}
+
+static void qeth_do_setadapterparms_stuff(qeth_card_t *card)
+{
+	int result;
+	char dbf_text[15];
+
+	if (!qeth_is_supported(IPA_SETADAPTERPARMS)) {
+		return;
+	}
+
+	sprintf(dbf_text,"stap%4x",card->irq0);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+
+	result=qeth_send_setadapterparms_query(card);
+
+	if (result) {
+		PRINT_WARN("couldn't set adapter parameters on irq 0x%x: " \
+			   "x%x\n",card->irq0,result);
+		QETH_DBF_TEXT1(0,trace,"SETADPFL");
+		sprintf(dbf_text,"%4x%4x",card->irq0,result);
+		QETH_DBF_TEXT1(1,trace,dbf_text);
+		return;
+	}
+
+	sprintf(dbf_text,"spap%4x",card->adp_supported);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+	if (qeth_is_adp_supported(IPA_SETADP_ALTER_MAC_ADDRESS)) {
+		sprintf(dbf_text,"rdmc%4x",card->irq0);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+		QETH_DBF_TEXT2(0,setup,dbf_text);
+
+		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 MAC address on " \
+				   "irq 0x%x: x%x\n",card->irq0,result);
+			QETH_DBF_TEXT1(0,trace,"NOMACADD");
+			sprintf(dbf_text,"%4x%4x",card->irq0,result);
+			QETH_DBF_TEXT1(1,trace,dbf_text);
+		} 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));
+		}
+	}
+
+	if ( (card->link_type==QETH_MPC_LINK_TYPE_HSTR) ||
+	     (card->link_type==QETH_MPC_LINK_TYPE_LANE_TR) ) {
+		sprintf(dbf_text,"hstr%4x",card->irq0);
+		QETH_DBF_TEXT3(0,trace,dbf_text);
+
+		if (qeth_is_adp_supported(IPA_SETADP_SET_BROADCAST_MODE)) {
+			result=qeth_send_setadapterparms_mode(card,
+				IPA_SETADP_SET_BROADCAST_MODE,
+				card->options.broadcast_mode);
+			if (result) {
+				PRINT_WARN("couldn't set broadcast mode on " \
+					   "irq 0x%x: x%x\n",
+					   card->irq0,result);
+				QETH_DBF_TEXT1(0,trace,"STBRDCST");
+				sprintf(dbf_text,"%4x%4x",card->irq0,result);
+				QETH_DBF_TEXT1(1,trace,dbf_text);
+			}
+		} else if (card->options.broadcast_mode) {
+			PRINT_WARN("set adapter parameters not available " \
+				   "to set broadcast mode, using ALLRINGS " \
+				   "on irq 0x%x:\n",card->irq0);
+			sprintf(dbf_text,"NOBC%4x",card->irq0);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+		}
+
+		if (qeth_is_adp_supported(IPA_SETADP_SET_BROADCAST_MODE)) {
+			result=qeth_send_setadapterparms_mode(card,
+				IPA_SETADP_ALTER_MAC_ADDRESS,
+				card->options.macaddr_mode);
+			if (result) {
+				PRINT_WARN("couldn't set macaddr mode on " \
+					   "irq 0x%x: x%x\n",
+					   card->irq0,result);
+				QETH_DBF_TEXT1(0,trace,"STMACMOD");
+				sprintf(dbf_text,"%4x%4x",card->irq0,result);
+				QETH_DBF_TEXT1(1,trace,dbf_text);
+			}
+		} else if (card->options.macaddr_mode) {
+			PRINT_WARN("set adapter parameters not available " \
+				   "to set macaddr mode, using NONCANONICAL " \
+				   "on irq 0x%x:\n",card->irq0);
+			sprintf(dbf_text,"NOMA%4x",card->irq0);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+		}
+	}
+}
+
+static int qeth_softsetup_card(qeth_card_t *card,int wait_for_lock)
+{
+        int result;
+	int use_setip_retries=1;
+	char dbf_text[15];
+	int do_a_startlan6=0;
+
+	if (wait_for_lock==QETH_WAIT_FOR_LOCK) {
+		my_spin_lock(&card->softsetup_lock);
+	} else if (wait_for_lock==QETH_DONT_WAIT_FOR_LOCK) {
+		if (!spin_trylock(&card->softsetup_lock)) {
+			return -EAGAIN;
+		}
+	} else if (wait_for_lock==QETH_LOCK_ALREADY_HELD) {
+		use_setip_retries=0; /* we are in recovery and don't want
+					to repeat setting ips on and on */
+	} else {
+		return -EINVAL;
+	}
+
+	qeth_save_dev_flag_state(card);
+
+	sprintf(dbf_text,"ssc%c%4x",wait_for_lock?'w':'n',card->irq0);
+	QETH_DBF_TEXT1(0,trace,dbf_text);
+
+        if (!atomic_read(&card->is_softsetup)) {
+		atomic_set(&card->enable_routing_attempts4,
+			   QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+		atomic_set(&card->enable_routing_attempts6,
+			   QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+		if ( (!atomic_read(&card->is_startlaned)) &&
+		     (atomic_read(&card->startlan_attempts)) ) {
+			atomic_dec(&card->startlan_attempts);
+			QETH_DBF_TEXT2(0,trace,"startlan");
+			netif_stop_queue(card->dev);
+			result=qeth_send_startlan(card,4);
+			if (result) {
+				PRINT_WARN("couldn't send STARTLAN on %s " \
+					   "(CHPID 0x%X): 0x%x (%s)\n",
+					   card->dev_name,card->chpid,result,
+					   (result==0xe080)?
+					   "startlan disabled (link " \
+					   "failure -- please check the " \
+					   "network, plug in the cable or " \
+					   "enable the OSA port":
+					   "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) {
+					result=0;
+				}
+				goto out;
+			}
+			do_a_startlan6=1;
+		}
+		netif_wake_queue(card->dev);
+
+		qeth_do_setadapterparms_stuff(card);
+
+                if (!qeth_is_supported(IPA_ARP_PROCESSING)) {
+                        PRINT_WARN("oops... ARP processing not supported " \
+				   "on %s!\n",card->dev_name);
+			QETH_DBF_TEXT1(0,trace,"NOarpPRC");
+                } else {
+			QETH_DBF_TEXT2(0,trace,"enaARPpr");
+                        result=qeth_send_setassparms_simple_without_data(
+                                card,IPA_ARP_PROCESSING,IPA_CMD_ASS_START);
+                        if (result) {
+                                PRINT_WARN("Could not start ARP processing " \
+                                           "assist on %s: 0x%x\n",
+					   card->dev_name,result);
+				sprintf(dbf_text,"ARPp%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				atomic_set(&card->is_softsetup,0);
+                                goto out;
+                        }
+                }
+
+                if (qeth_is_supported(IPA_IP_FRAGMENTATION)) {
+                        PRINT_INFO("IP fragmentation supported on " \
+				   "%s... :-)\n",card->dev_name);
+			QETH_DBF_TEXT2(0,trace,"enaipfrg");
+                        result=qeth_send_setassparms_simple_without_data(
+                                card,IPA_IP_FRAGMENTATION,IPA_CMD_ASS_START);
+                        if (result) {
+                                PRINT_WARN("Could not start IP fragmenting " \
+                                           "assist on %s: 0x%x, continuing\n",
+					   card->dev_name,result);
+				sprintf(dbf_text,"IFRG%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				/* go on */
+                        }
+                }
+
+		if (card->options.fake_ll==FAKE_LL) {
+			if (qeth_is_supported(IPA_SOURCE_MAC_AVAIL)) {
+				QETH_DBF_TEXT2(0,trace,"enainsrc");
+			result=qeth_send_setassparms_simple_without_data(
+		     		 card,IPA_SOURCE_MAC_AVAIL,IPA_CMD_ASS_START);
+				if (result) {
+					PRINT_WARN("Could not start " \
+						   "inbound source " \
+						   "assist on %s: 0x%x, " \
+						   "continuing\n",
+						   card->dev_name,result);
+					sprintf(dbf_text,"INSR%4x",result);
+					QETH_DBF_TEXT2(0,trace,dbf_text);
+					/* go on */
+				}
+			} else {
+				PRINT_INFO("Inbound source addresses not " \
+					   "supported on %s\n",card->dev_name);
+			}
+		}
+#ifdef QETH_VLAN
+                if (!qeth_is_supported(IPA_FULL_VLAN)) {
+                        PRINT_WARN("VLAN not supported on %s\n",
+				   card->dev_name);
+			QETH_DBF_TEXT2(0,trace,"vlnotsup");
+                } else {
+                        result=qeth_send_setassparms_simple_without_data(
+                                card,IPA_VLAN_PRIO,IPA_CMD_ASS_START);
+			QETH_DBF_TEXT2(0,trace,"enavlan");
+                        if (result) {
+                                PRINT_WARN("Could not start vlan "
+					   "assist on %s: 0x%x, continuing\n",
+					   card->dev_name,result);
+				sprintf(dbf_text,"VLAN%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				/* go on*/
+			}
+			else {
+		               card->dev->features |=
+				       NETIF_F_HW_VLAN_TX |
+				       NETIF_F_HW_VLAN_RX;
+
+			}
+                }
+#endif /* QETH_VLAN */
+
+                if (!qeth_is_supported(IPA_MULTICASTING)) {
+                        PRINT_WARN("multicasting not supported on %s\n",
+				   card->dev_name);
+			QETH_DBF_TEXT2(0,trace,"mcnotsup");
+                } else {
+                        result=qeth_send_setassparms_simple_without_data(
+                                card,IPA_MULTICASTING,IPA_CMD_ASS_START);
+			QETH_DBF_TEXT2(0,trace,"enamcass");
+                        if (result) {
+                                PRINT_WARN("Could not start multicast "
+					   "assist on %s: 0x%x, continuing\n",
+					   card->dev_name,result);
+				sprintf(dbf_text,"MCAS%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				/* go on*/
+                        } else {
+				card->dev->flags|=IFF_MULTICAST;
+			}
+                }
+
+                if (!qeth_is_supported(IPA_IPv6)) {
+			QETH_DBF_TEXT2(0,trace,"ipv6ntsp");
+                        PRINT_WARN("IPv6 not supported on %s\n",
+				   card->dev_name);
+                } else {
+			if (do_a_startlan6) {
+				QETH_DBF_TEXT2(0,trace,"startln6");
+				netif_stop_queue(card->dev);
+				result=qeth_send_startlan(card,6);
+				if (result) {
+					sprintf(dbf_text,"stl6%4x",result);
+					QETH_DBF_TEXT2(0,trace,dbf_text);
+					atomic_set(&card->is_softsetup,0);
+					/* do not return an error */
+					if (result==0xe080) {
+						result=0;
+					}
+					goto out;
+				}
+			}
+			netif_wake_queue(card->dev);
+			QETH_DBF_TEXT2(0,trace,"qipassi6");
+			result=qeth_send_qipassist(card,6);
+			if (result) {
+				PRINT_WARN("couldn't send QIPASSIST6 on %s: " \
+					   "0x%x\n",card->dev_name,result);
+				sprintf(dbf_text,"QIP6%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				atomic_set(&card->is_softsetup,0);
+				goto out;
+			}
+
+			sprintf(dbf_text,"%4x%4x",card->ipa6_supported,
+				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);
+                        if (result) {
+                                PRINT_WARN("Could not enable IPv4&6 assist " \
+					   "on %s: " \
+                                           "0x%x, continuing\n",
+					   card->dev_name,result);
+					sprintf(dbf_text,"I46A%4x",result);
+					QETH_DBF_TEXT2(0,trace,dbf_text);
+				/* go on */
+			}
+			QETH_DBF_TEXT2(0,trace,"enaipv6");
+                        result=qeth_send_setassparms_simple_without_data6(
+                                card,IPA_IPv6,IPA_CMD_ASS_START);
+                        if (result) {
+                                PRINT_WARN("Could not start IPv6 assist " \
+					   "on %s: " \
+                                           "0x%x, continuing\n",
+					   card->dev_name,result);
+				sprintf(dbf_text,"I6AS%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				/* go on */
+                        }
+			QETH_DBF_TEXT2(0,trace,"enapstr6");
+                        result=qeth_send_setassparms_simple_without_data6(
+                                card,IPA_PASSTHRU,IPA_CMD_ASS_START);
+                        if (result) {
+                                PRINT_WARN("Could not enable passthrough " \
+					   "on %s: " \
+                                           "0x%x, continuing\n",
+					   card->dev_name,result);
+				sprintf(dbf_text,"PSTR%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				/* go on */
+                        }
+                }
+
+		card->broadcast_capable=0;
+                if (!qeth_is_supported(IPA_FILTERING)) {
+			QETH_DBF_TEXT2(0,trace,"filtntsp");
+                        PRINT_WARN("Broadcasting not supported on %s\n",
+				   card->dev_name);
+                } else {
+			QETH_DBF_TEXT2(0,trace,"enafiltr");
+                        result=qeth_send_setassparms_simple_without_data(
+                                card,IPA_FILTERING,IPA_CMD_ASS_START);
+                        if (result) {
+                                PRINT_WARN("Could not enable broadcast " \
+					   "filtering on %s: " \
+                                           "0x%x, continuing\n",
+					   card->dev_name,result);
+				sprintf(dbf_text,"FLT1%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				goto go_on_filt;
+                        }
+			result=qeth_send_setassparms_simple_with_data(
+                                card,IPA_FILTERING,IPA_CMD_ASS_CONFIGURE,1);
+			if (result) {
+                                PRINT_WARN("Could not set up broadcast " \
+					   "filtering on %s: " \
+                                           "0x%x, continuing\n",
+					   card->dev_name,result);
+				sprintf(dbf_text,"FLT2%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				goto go_on_filt;
+			}
+			card->dev->flags|=IFF_BROADCAST;
+			card->broadcast_capable=1;
+
+		}
+go_on_filt:
+		if (card->options.checksum_type==HW_CHECKSUMMING) {
+			if (!qeth_is_supported(IPA_INBOUND_CHECKSUM)) {
+				PRINT_WARN("Inbound HW checksumming not " \
+					   "supported on %s, continuing " \
+					   "using inbound sw checksumming\n",
+					   card->dev_name);
+				QETH_DBF_TEXT2(0,trace,"ibckntsp");
+				card->options.checksum_type=SW_CHECKSUMMING;
+			} else {
+				QETH_DBF_TEXT2(0,trace,"ibcksupp");
+			result=qeth_send_setassparms_simple_without_data(
+					card,IPA_INBOUND_CHECKSUM,
+					IPA_CMD_ASS_START);
+				if (result) {
+					PRINT_WARN("Could not start inbound " \
+						   "checksumming on %s: " \
+						   "0x%x, " \
+						   "continuing using " \
+						   "inbound sw checksumming\n",
+						   card->dev_name,result);
+					sprintf(dbf_text,"SIBC%4x",result);
+					QETH_DBF_TEXT2(0,trace,dbf_text);
+					card->options.checksum_type=
+						SW_CHECKSUMMING;
+					goto go_on_checksum;
+				}
+				result=qeth_send_setassparms_simple_with_data(
+					card,IPA_INBOUND_CHECKSUM,
+					IPA_CMD_ASS_ENABLE,
+					IPA_CHECKSUM_ENABLE_MASK);
+				if (result) {
+					PRINT_WARN("Could not enable inbound " \
+						   "checksumming on %s: " \
+						   "0x%x, " \
+						   "continuing using " \
+						   "inbound sw checksumming\n",
+						   card->dev_name,result);
+					sprintf(dbf_text,"EIBC%4x",result);
+					QETH_DBF_TEXT2(0,trace,dbf_text);
+					card->options.checksum_type=
+						SW_CHECKSUMMING;
+					goto go_on_checksum;
+				}
+			}
+		}
+go_on_checksum:
+
+                atomic_set(&card->is_softsetup,1);
+        }
+
+	if (atomic_read(&card->enable_routing_attempts4)) {
+		if (card->options.routing_type4) {
+			sprintf(dbf_text,"strtg4%2x",
+				card->options.routing_type4);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+			result=qeth_send_setrtg(card,card->options.
+						routing_type4,4);
+			if (result) {
+				if (atomic_dec_return(&card->
+					enable_routing_attempts4)) {
+				PRINT_WARN("couldn't set up v4 routing type " \
+					   "on %s: 0x%x (%s).\nWill try " \
+					   "next time again.\n",
+					   card->dev_name,result,
+					   ((result==0xe010)||
+					    (result==0xe008))?
+					   "primary already defined":
+					   ((result==0xe011)||
+					    (result==0xe009))?
+					   "secondary already defined":
+					   (result==0xe012)?
+					   "invalid indicator":
+					   "unknown return code");
+				sprintf(dbf_text,"sRT4%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				atomic_set(&card->rt4fld,1);
+				} else {
+				PRINT_WARN("couldn't set up v4 routing type " \
+					   "on %s: 0x%x (%s).\nTrying to " \
+					   "continue without routing.\n",
+					   card->dev_name,result,
+					   ((result==0xe010)||
+					    (result==0xe008))?
+					   "primary already defined":
+					   ((result==0xe011)||
+					    (result==0xe009))?
+					   "secondary already defined":
+					   (result==0xe012)?
+					   "invalid indicator":
+					   "unknown return code");
+				sprintf(dbf_text,"SRT4%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				atomic_set(&card->rt4fld,1);
+				}
+			} else { /* routing set correctly */
+				atomic_set(&card->enable_routing_attempts4,0);
+				atomic_set(&card->rt4fld,0);
+			}
+		} else {
+			atomic_set(&card->enable_routing_attempts4,0);
+			atomic_set(&card->rt4fld,0);
+		}
+	}
+
+#ifdef QETH_IPV6
+	if (atomic_read(&card->enable_routing_attempts6)) {
+		if (card->options.routing_type6) {
+			sprintf(dbf_text,"strtg6%2x",
+				card->options.routing_type6);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+			result=qeth_send_setrtg(card,card->options.
+						routing_type6,6);
+			if (result) {
+				if (atomic_dec_return(&card->
+					enable_routing_attempts6)) {
+				PRINT_WARN("couldn't set up v6 routing type " \
+					   "on %s: 0x%x (%s).\nWill try " \
+					   "next time again.\n",
+					   card->dev_name,result,
+					   ((result==0xe010)||
+					    (result==0xe008))?
+					   "primary already defined":
+					   ((result==0xe011)||
+					    (result==0xe009))?
+					   "secondary already defined":
+					   (result==0xe012)?
+					   "invalid indicator":
+					   "unknown return code");
+				sprintf(dbf_text,"sRT6%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				atomic_set(&card->rt6fld,1);
+				} else {
+				PRINT_WARN("couldn't set up v6 routing type " \
+					   "on %s: 0x%x (%s).\nTrying to " \
+					   "continue without routing.\n",
+					   card->dev_name,result,
+					   ((result==0xe010)||
+					    (result==0xe008))?
+					   "primary already defined":
+					   ((result==0xe011)||
+					    (result==0xe009))?
+					   "secondary already defined":
+					   (result==0xe012)?
+					   "invalid indicator":
+					   "unknown return code");
+				sprintf(dbf_text,"SRT6%4x",result);
+				QETH_DBF_TEXT2(0,trace,dbf_text);
+				atomic_set(&card->rt6fld,1);
+				}
+			} else { /* routing set correctly */
+				atomic_set(&card->enable_routing_attempts6,0);
+				atomic_set(&card->rt6fld,0);
+			}
+		} else {
+			atomic_set(&card->enable_routing_attempts6,0);
+			atomic_set(&card->rt6fld,0);
+		}
+	}
+#endif /* QETH_IPV6 */
+
+	QETH_DBF_TEXT2(0,trace,"delvipa");
+	qeth_set_vipas(card,0);
+	QETH_DBF_TEXT2(0,trace,"toip/ms");
+	qeth_takeover_ip_ipms(card);
+#ifdef QETH_IPV6
+	qeth_takeover_ip_ipms6(card);
+#endif /* QETH_IPV6 */
+	QETH_DBF_TEXT2(0,trace,"setvipa");
+	qeth_set_vipas(card,1);
+
+        result=qeth_setips(card,use_setip_retries);
+        if (result) { /* by now, qeth_setips does not return errors */
+                PRINT_WARN("couldn't set up IPs on %s: 0x%x\n",
+			   card->dev_name,result);
+		sprintf(dbf_text,"SSIP%4x",result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		atomic_set(&card->is_softsetup,0);
+                goto out;
+        }
+
+        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",
+			   card->dev_name,result);
+		sprintf(dbf_text,"ssim%4x",result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		atomic_set(&card->is_softsetup,0);
+                goto out;
+        }
+ out:
+	if (!result) {
+		netif_wake_queue(card->dev);
+	}
+	if (wait_for_lock!=QETH_LOCK_ALREADY_HELD)
+		my_spin_unlock(&card->softsetup_lock);
+        return result;
+}
+
+static int qeth_softsetup_thread(void *param)
+{
+	char dbf_text[15];
+	char name[15];
+	qeth_card_t *card=(qeth_card_t*)param;
+
+	daemonize();
+
+	/* set a nice name ... */
+	sprintf(name, "qethsoftd%04x", card->irq0);
+	strcpy(current->comm, name);
+
+	sprintf(dbf_text,"ssth%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+	atomic_set(&card->softsetup_thread_is_running,1);
+	for (;;) {
+		if (atomic_read(&card->shutdown_phase)) goto out;
+		down_interruptible(&card->softsetup_thread_sem);
+		sprintf(dbf_text,"ssst%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		if (atomic_read(&card->shutdown_phase)) goto out;
+		while (qeth_softsetup_card(card,QETH_DONT_WAIT_FOR_LOCK)
+		       ==-EAGAIN) {
+			if (atomic_read(&card->shutdown_phase)) goto out;
+			qeth_wait_nonbusy(QETH_IDLE_WAIT_TIME);
+		}
+		sprintf(dbf_text,"sstd%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		netif_wake_queue(card->dev);
+	}
+ out:
+	atomic_set(&card->softsetup_thread_is_running,0);
+
+	sprintf(dbf_text,"lsst%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+	return 0;
+}
+
+static void qeth_softsetup_thread_starter(void *data)
+{
+	char dbf_text[15];
+	qeth_card_t *card=(qeth_card_t *)data;
+
+	sprintf(dbf_text,"ssts%4x",card->irq0);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+	sema_init(&card->softsetup_thread_sem,0);
+	kernel_thread(qeth_softsetup_thread,card,SIGCHLD);
+}
+
+static void qeth_start_reinit_thread(qeth_card_t *card)
+{
+	char dbf_text[15];
+
+	/* we allow max 2 reinit threads, one could be just about to
+	 * finish and the next would be waiting. another waiting
+	 * reinit_thread is not necessary. */
+	if (atomic_read(&card->reinit_counter)<2) {
+		atomic_inc(&card->reinit_counter);
+		if (atomic_read(&card->shutdown_phase)) {
+			atomic_dec(&card->reinit_counter);
+			return;
+		}
+		sprintf(dbf_text,"stri%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		PRINT_STUPID("starting reinit-thread\n");
+		kernel_thread(qeth_reinit_thread,card,SIGCHLD);
+	}
+}
+
+static void qeth_recover(void *data)
+{
+        qeth_card_t *card;
+        int i;
+	char dbf_text[15];
+
+        card=(qeth_card_t*)data;
+
+	sprintf(dbf_text,"recv%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+	if (atomic_compare_and_swap(0,1,&card->in_recovery))
+		return;
+
+        i=atomic_read(&card->problem);
+
+	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);
+        switch (i) {
+        case PROBLEM_RECEIVED_IDX_TERMINATE:
+		if (atomic_read(&card->in_recovery))
+			atomic_set(&card->break_out,QETH_BREAKOUT_AGAIN);
+		break;
+        case PROBLEM_CARD_HAS_STARTLANED:
+		PRINT_WARN("You are lucky! Somebody either fixed the " \
+			   "network problem, plugged the cable back in " \
+			   "or enabled the OSA port on %s (CHPID 0x%X). " \
+			   "The link has come up.\n",
+			   card->dev_name,card->chpid);
+		sprintf(dbf_text,"CBIN%4x",i);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		atomic_set(&card->is_softsetup,0);
+		qeth_set_dev_flag_running(card);
+		atomic_set(&card->enable_routing_attempts4,
+			   QETH_ROUTING_ATTEMPTS);
+		qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
+#ifdef QETH_IPV6
+		atomic_set(&card->enable_routing_attempts6,
+			   QETH_ROUTING_ATTEMPTS);
+		qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
+#endif /* QETH_IPV6 */
+		qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
+#ifdef QETH_IPV6
+		qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
+#endif /* QETH_IPV6 */
+		qeth_refresh_vipa_states(card);
+		qeth_start_softsetup_thread(card);
+		atomic_set(&card->in_recovery,0);
+		break;
+	case PROBLEM_RESETTING_EVENT_INDICATOR:
+		/* we do nothing here */
+		break;
+	case PROBLEM_ACTIVATE_CHECK_CONDITION:
+	case PROBLEM_GENERAL_CHECK:
+	case PROBLEM_USER_TRIGGERED_RECOVERY:
+	case PROBLEM_AFFE:
+	case PROBLEM_MACHINE_CHECK:
+	case PROBLEM_BAD_SIGA_RESULT:
+	case PROBLEM_TX_TIMEOUT:
+		qeth_start_reinit_thread(card);
+		break;
+        }
+}
+
+static void qeth_schedule_recovery(qeth_card_t *card)
+{
+	if (card) {
+   		INIT_LIST_HEAD(&card->tqueue.list);
+		card->tqueue.routine=qeth_recover;
+		card->tqueue.data=card;
+		card->tqueue.sync=0;
+		schedule_task(&card->tqueue);
+	} else {
+		QETH_DBF_TEXT2(1,trace,"scdnocrd");
+		PRINT_WARN("recovery requested to be scheduled " \
+			   "with no card!\n");
+	}
+}
+
+static void qeth_qdio_input_handler(int irq,unsigned int status,
+			     unsigned int qdio_error,unsigned int siga_error,
+			     unsigned int queue,
+			     int first_element,int count,
+			     unsigned long card_ptr)
+{
+        struct net_device *dev;
+        qeth_card_t *card;
+	int problem;
+	int sbalf15;
+	char dbf_text[15]="qinbhnXX";
+
+	*((__u16*)(&dbf_text[6]))=(__u16)irq;
+	QETH_DBF_HEX6(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+
+	card=(qeth_card_t *)card_ptr;
+
+#ifdef QETH_PERFORMANCE_STATS
+	card->perf_stats.inbound_start_time=NOW;
+#endif /* QETH_PERFORMANCE_STATS */
+	dev=card->dev;
+
+	if (status&QDIO_STATUS_LOOK_FOR_ERROR) {
+		if (status&QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
+			problem=PROBLEM_ACTIVATE_CHECK_CONDITION;
+			PRINT_WARN("activate queues on irq 0x%x: " \
+				   "dstat=0x%x, cstat=0x%x\n",irq,
+				   card->devstat2->dstat,
+				   card->devstat2->cstat);
+			atomic_set(&card->problem,problem);
+			QETH_DBF_TEXT1(0,trace,"IHACTQCK");
+			sprintf(dbf_text,"%4x%4x",first_element,count);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+			sprintf(dbf_text,"%4x%4x",queue,status);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+			sprintf(dbf_text,"qscd%4x",card->irq0);
+			QETH_DBF_TEXT1(1,trace,dbf_text);
+			qeth_schedule_recovery(card);
+			return;
+		}
+		sbalf15=(card->inbound_qdio_buffers[(first_element+count-1)&
+			 QDIO_MAX_BUFFERS_PER_Q].
+			 element[15].flags)&&0xff;
+		PRINT_STUPID("inbound qdio transfer error on irq 0x%04x. " \
+			     "qdio_error=0x%x (more than one: %c), " \
+			     "siga_error=0x%x (more than one: %c), " \
+			     "sbalf15=x%x, bufno=x%x\n",
+			     irq,qdio_error,
+			     (status&QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR)?
+			     'y':'n',siga_error,
+			     (status&QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR)?
+			     'y':'n',
+			     sbalf15,first_element);
+		sprintf(dbf_text,"IQTI%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_TEXT1(0,qerr,dbf_text);
+		sprintf(dbf_text,"%4x%4x",first_element,count);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_TEXT1(0,qerr,dbf_text);
+		sprintf(dbf_text,"%2x%4x%2x",queue,status,sbalf15);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_TEXT1(0,qerr,dbf_text);
+		sprintf(dbf_text,"%4x%4x",qdio_error,siga_error);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_TEXT1(0,qerr,dbf_text);
+		/* we inform about error more detailed in
+		 * qeth_read_in_buffer() */
+	}
+
+	for (;;) {
+		qeth_get_linux_addrs_for_buffer(card,first_element);
+		qeth_read_in_buffer(card,first_element);
+		qeth_queue_input_buffer(card,first_element,
+					QDIO_FLAG_UNDER_INTERRUPT);
+		count--;
+		if (count)
+			first_element=(first_element+1)&
+				(QDIO_MAX_BUFFERS_PER_Q-1);
+		else break;
+	}
+}
+
+static void qeth_qdio_output_handler(int irq,unsigned int status,
+			      unsigned int qdio_error,unsigned int siga_error,
+			      unsigned int queue,
+			      int first_element,int count,
+			      unsigned long card_ptr)
+{
+	qeth_card_t *card;
+	int mycnt,problem,buffers_used;
+	int sbalf15;
+	char dbf_text[15]="qouthnXX";
+	char dbf_text2[15]="stchdwXX";
+	int last_pci_hit=0,switch_state;
+	int last_pci;
+
+	*((__u16*)(&dbf_text[6]))=(__u16)irq;
+	QETH_DBF_HEX6(0,trace,dbf_text,QETH_DBF_TRACE_LEN);
+
+	mycnt=count;
+	card=(qeth_card_t *)card_ptr;
+
+	if (status&QDIO_STATUS_LOOK_FOR_ERROR) {
+		if (status&QDIO_STATUS_ACTIVATE_CHECK_CONDITION) {
+			problem=PROBLEM_ACTIVATE_CHECK_CONDITION;
+			PRINT_WARN("activate queues on irq 0x%x: " \
+				   "dstat=0x%x, cstat=0x%x\n",irq,
+				   card->devstat2->dstat,
+				   card->devstat2->cstat);
+			atomic_set(&card->problem,problem);
+			QETH_DBF_TEXT1(0,trace,"OHACTQCK");
+			sprintf(dbf_text,"%4x%4x",first_element,count);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+			sprintf(dbf_text,"%4x%4x",queue,status);
+			QETH_DBF_TEXT1(0,trace,dbf_text);
+			sprintf(dbf_text,"qscd%4x",card->irq0);
+			QETH_DBF_TEXT1(1,trace,dbf_text);
+			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;
+		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), " \
+			     "sbalf15=x%x, bufno=x%x\n",
+			     irq,queue,qdio_error,status&
+			     QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR?'y':'n',
+			     siga_error,status&
+			     QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR?'y':'n',
+			     sbalf15,first_element);
+		sprintf(dbf_text,"IQTO%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_TEXT1(0,qerr,dbf_text);
+		sprintf(dbf_text,"%4x%4x",first_element,count);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_TEXT1(0,qerr,dbf_text);
+		sprintf(dbf_text,"%2x%4x%2x",queue,status,sbalf15);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_TEXT1(0,qerr,dbf_text);
+		sprintf(dbf_text,"%4x%4x",qdio_error,siga_error);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_TEXT1(0,qerr,dbf_text);
+		/* we maybe do recovery or dst_link_failures
+		 * in qeth_free_buffer */
+	}
+
+	if (mycnt) {
+		last_pci=atomic_read(&card->last_pci_pos[queue]);
+		for (;;) {
+			qeth_free_buffer(card,queue,first_element,
+					 qdio_error,siga_error);
+			if (first_element==last_pci) last_pci_hit=1;
+			mycnt--;
+			if (mycnt>0)
+				first_element=(first_element+1)&
+					(QDIO_MAX_BUFFERS_PER_Q-1);
+			else break;
+		}
+	}
+
+	buffers_used=atomic_return_sub(count,
+				       &card->outbound_used_buffers[queue]);
+
+	switch (card->send_state[queue]) {
+	case SEND_STATE_PACK:
+		switch_state=(atomic_read
+			      (&card->outbound_used_buffers[queue])<=
+			      LOW_WATERMARK_PACK);
+		/* first_element is the last buffer that we got back
+		 * from hydra */
+		if (switch_state||last_pci_hit) {
+			*((__u16*)(&dbf_text2[6]))=card->irq0;
+			QETH_DBF_HEX3(0,trace,dbf_text2,QETH_DBF_TRACE_LEN);
+			if (atomic_swap(&card->outbound_ringbuffer_lock
+		       			[queue],QETH_LOCK_FLUSH)
+			    ==QETH_LOCK_UNLOCKED) {
+				/* we stop the queue as we try to not run
+				 * onto the outbound_ringbuffer_lock --
+				 * this will not prevent it totally, but
+				 * reduce it. in high traffic situations,
+				 * it saves around 20us per second, hopefully
+				 * this is amortized by calling netif_... */
+				netif_stop_queue(card->dev);
+				qeth_flush_packed_packets
+					(card,queue,
+					QDIO_FLAG_UNDER_INTERRUPT);
+				/* only switch state to non-packing, if
+				 * the amount of used buffers decreased */
+				if (switch_state)
+					card->send_state[queue]=
+						SEND_STATE_DONT_PACK;
+				netif_wake_queue(card->dev);
+				atomic_set(&card->outbound_ringbuffer_lock[
+					   queue],QETH_LOCK_UNLOCKED);
+			} /* if the lock was UNLOCKED, we flush ourselves,
+			     otherwise this is done in do_send_packet when
+			     the lock is released */
+#ifdef QETH_PERFORMANCE_STATS
+			card->perf_stats.sc_p_dp++;
+#endif /* QETH_PERFORMANCE_STATS */
+		}
+		break;
+	default: break;
+	}
+
+	/* we don't have to start the queue, if it was started already */
+	if (buffers_used<QDIO_MAX_BUFFERS_PER_Q-1)
+		return;
+
+ out:
+	netif_wake_queue(card->dev);
+}
+
+static void qeth_interrupt_handler_read(int irq,void *devstat,
+					struct pt_regs *p)
+{
+        devstat_t *stat;
+        int cstat,dstat;
+        int problem;
+        qeth_card_t *card;
+        int rqparam;
+	char dbf_text[15];
+	int result;
+
+        stat=(devstat_t *)devstat;
+        cstat=stat->cstat;
+        dstat=stat->dstat;
+        rqparam=stat->intparm;
+
+	sprintf(dbf_text,"rint%4x",irq);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+	sprintf(dbf_text,"%4x%4x",cstat,dstat);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+	sprintf(dbf_text,"%4x",rqparam);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+
+	if (!rqparam) {
+		PRINT_STUPID("got unsolicited interrupt in read handler, " \
+			     "irq 0x%x\n",irq);
+		return;
+	}
+
+        card=qeth_get_card_by_irq(irq);
+	if (!card) return;
+
+	if ((rqparam==CLEAR_STATE) || (stat->flag&DEVSTAT_CLEAR_FUNCTION)) {
+		atomic_set(&card->clear_succeeded0,1);
+		return;
+	}
+
+	if ((dstat==0)&&(cstat==0)) return;
+
+	if (card->devstat0->flag&DEVSTAT_FLAG_SENSE_AVAIL) {
+		PRINT_WARN("sense data available on read channel.\n");
+		HEXDUMP16(WARN,"irb: ",&card->devstat0->ii.irb);
+		HEXDUMP16(WARN,"sense data: ",&card->devstat0->ii.
+			  sense.data[0]);
+		sprintf(dbf_text,"RSNS%4x",irq);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_HEX0(0,sense,&card->devstat0->ii.irb,
+			      QETH_DBF_SENSE_LEN);
+	}
+
+        if (cstat!=0) {
+                PRINT_WARN("got nonzero-nonpci channel status in read_" \
+                           "handler (irq 0x%x, devstat 0x%02x, schstat " \
+                           "0x%02x, rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+        }
+
+        problem=qeth_get_cards_problem(card,card->dma_stuff->recbuf,
+				       irq,dstat,cstat,rqparam,
+				       (char*)&card->devstat0->ii.irb,
+				       (char*)&card->devstat0->
+				       ii.sense.data[0]);
+
+        /* detect errors in dstat here */
+        if ( (dstat&DEV_STAT_UNIT_EXCEP) || (dstat&DEV_STAT_UNIT_CHECK) ) {
+                PRINT_WARN("unit check/exception in read_handler " \
+                           "(irq 0x%x, devstat 0x%02x, schstat 0x%02x, " \
+			   "rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+		if (!atomic_read(&card->is_hardsetup)) {
+			if ((problem)&&(qeth_is_to_recover(card,problem)))
+				atomic_set(&card->break_out,
+					   QETH_BREAKOUT_AGAIN);
+			else
+				atomic_set(&card->break_out,
+					   QETH_BREAKOUT_LEAVE);
+			goto wakeup_out;
+		} else goto recover;
+        }
+	
+	if (!(dstat&DEV_STAT_CHN_END)) {
+                PRINT_WARN("didn't get device end in read_handler " \
+                           "(irq 0x%x, devstat 0x%02x, schstat 0x%02x, " \
+			   "rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+                goto wakeup_out;
+        }
+
+        if ( (rqparam==IDX_ACTIVATE_WRITE_STATE) ||
+	     (rqparam==NOP_STATE) ) {
+		goto wakeup_out;
+	}
+
+        /* at this point, (maybe channel end and) device end has appeared */
+
+	/* we don't start the next read until we have examined the buffer. */
+	if ( (rqparam!=IDX_ACTIVATE_READ_STATE) &&
+     	     (rqparam!=IDX_ACTIVATE_WRITE_STATE) )
+		qeth_issue_next_read(card);
+
+recover:
+        if (qeth_is_to_recover(card,problem)) {
+		sprintf(dbf_text,"rscd%4x",card->irq0);
+		QETH_DBF_TEXT2(1,trace,dbf_text);
+		qeth_schedule_recovery(card);
+                goto wakeup_out;
+        }
+
+        if (!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) {
+			case ARP_RETURNCODE_ERROR:
+			case ARP_RETURNCODE_LASTREPLY:
+				qeth_wakeup_ioctl(card);
+				return;
+			default:
+				break;
+		}
+        }
+
+ wakeup_out:
+	memcpy(card->ipa_buf,card->dma_stuff->recbuf,QETH_BUFSIZE);
+	qeth_wakeup(card);
+}
+
+static void qeth_interrupt_handler_write(int irq,void *devstat,
+					 struct pt_regs *p)
+{
+        devstat_t *stat;
+        int cstat,dstat,rqparam;
+        qeth_card_t *card;
+	int problem;
+	char dbf_text[15];
+
+        stat = (devstat_t *)devstat;
+        cstat = stat->cstat;
+        dstat = stat->dstat;
+        rqparam=stat->intparm;
+
+	sprintf(dbf_text,"wint%4x",irq);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+	sprintf(dbf_text,"%4x%4x",cstat,dstat);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+	sprintf(dbf_text,"%4x",rqparam);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+
+	if (!rqparam) {
+		PRINT_STUPID("got unsolicited interrupt in write handler, " \
+			     "irq 0x%x\n",irq);
+		return;
+	}
+
+        card=qeth_get_card_by_irq(irq);
+	if (!card) return;
+
+	if ((rqparam==CLEAR_STATE) || (stat->flag&DEVSTAT_CLEAR_FUNCTION)) {
+		atomic_set(&card->clear_succeeded1,1);
+		goto out;
+	}
+
+	if ((dstat==0)&&(cstat==0)) goto out;
+
+	if (card->devstat1->flag&DEVSTAT_FLAG_SENSE_AVAIL) {
+		PRINT_WARN("sense data available on write channel.\n");
+		HEXDUMP16(WARN,"irb: ",&card->devstat1->ii.irb);
+		HEXDUMP16(WARN,"sense data: ",&card->devstat1->ii.
+			  sense.data[0]);
+		sprintf(dbf_text,"WSNS%4x",irq);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_HEX0(0,sense,&card->devstat1->ii.irb,
+			      QETH_DBF_SENSE_LEN);
+	}
+
+        if (cstat != 0) {
+                PRINT_WARN("got nonzero channel status in write_handler " \
+                           "(irq 0x%x, devstat 0x%02x, schstat 0x%02x, " \
+			   "rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+        }
+
+        problem=qeth_get_cards_problem(card,NULL,
+				       irq,dstat,cstat,rqparam,
+				       (char*)&card->devstat1->ii.irb,
+				       (char*)&card->devstat1->
+				       ii.sense.data[0]);
+
+        /* detect errors in dstat here */
+        if ( (dstat&DEV_STAT_UNIT_EXCEP) || (dstat&DEV_STAT_UNIT_CHECK) ) {
+                PRINT_WARN("unit check/exception in write_handler " \
+                           "(irq 0x%x, devstat 0x%02x, schstat 0x%02x, " \
+			   "rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+		if (!atomic_read(&card->is_hardsetup)) {
+			if (problem==PROBLEM_RESETTING_EVENT_INDICATOR) {
+				atomic_set(&card->break_out,
+					   QETH_BREAKOUT_AGAIN);
+				qeth_wakeup(card);
+				goto out;
+			}
+			atomic_set(&card->break_out,QETH_BREAKOUT_LEAVE);
+			goto out;
+		} else goto recover;
+        }
+	
+	if (dstat==DEV_STAT_DEV_END) goto out;
+
+	if (!(dstat&DEV_STAT_CHN_END)) {
+                PRINT_WARN("didn't get device end in write_handler " \
+                           "(irq 0x%x, devstat 0x%02x, schstat 0x%02x, " \
+			   "rqparam 0x%x)\n",irq,dstat,cstat,rqparam);
+                goto out;
+        }
+
+recover:
+        if (qeth_is_to_recover(card,problem)) {
+		sprintf(dbf_text,"wscd%4x",card->irq1);
+		QETH_DBF_TEXT2(1,trace,dbf_text);
+		qeth_schedule_recovery(card);
+                goto out;
+        }
+
+        /* at this point, (maybe channel end and) device end has appeared */
+        if ( (rqparam==IDX_ACTIVATE_READ_STATE) ||
+	     (rqparam==IDX_ACTIVATE_WRITE_STATE) ||
+	     (rqparam==NOP_STATE) ) {
+                qeth_wakeup(card);
+                goto out;
+        }
+
+        /* well, a write has been done successfully. */
+
+ out:
+	/* all statuses are final statuses on the write channel */
+	atomic_set(&card->write_busy,0);
+}
+
+static void qeth_interrupt_handler_qdio(int irq,void *devstat,
+					struct pt_regs *p)
+{
+        devstat_t *stat;
+        int cstat,dstat,rqparam;
+	char dbf_text[15];
+
+        qeth_card_t *card;
+
+        stat = (devstat_t *)devstat;
+        cstat = stat->cstat;
+        dstat = stat->dstat;
+        rqparam=stat->intparm;
+
+	sprintf(dbf_text,"qint%4x",irq);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+	sprintf(dbf_text,"%4x%4x",cstat,dstat);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+	sprintf(dbf_text,"%4x",rqparam);
+	QETH_DBF_TEXT4(0,trace,dbf_text);
+
+
+	if (!rqparam) {
+		PRINT_STUPID("got unsolicited interrupt in qdio handler, " \
+			     "irq 0x%x\n",irq);
+		return;
+	}
+
+        card=qeth_get_card_by_irq(irq);
+	if (!card) return;
+
+	if ((rqparam==CLEAR_STATE) || (stat->flag&DEVSTAT_CLEAR_FUNCTION)) {
+		atomic_set(&card->clear_succeeded2,1);
+		return;
+	}
+
+	if ((dstat==0)&&(cstat==0)) return;
+
+	if (card->devstat2->flag&DEVSTAT_FLAG_SENSE_AVAIL) {
+		PRINT_WARN("sense data available on qdio channel.\n");
+		HEXDUMP16(WARN,"irb: ",&card->devstat2->ii.irb);
+		HEXDUMP16(WARN,"sense data: ",&card->devstat2->ii.
+			  sense.data[0]);
+		sprintf(dbf_text,"QSNS%4x",irq);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		QETH_DBF_HEX0(0,sense,&card->devstat2->ii.irb,
+			      QETH_DBF_SENSE_LEN);
+	}
+
+        if ( (rqparam==READ_CONF_DATA_STATE) ||
+	     (rqparam==NOP_STATE) ) {
+                qeth_wakeup(card);
+		return;
+        }
+
+        if (cstat != 0) {
+		sprintf(dbf_text,"qchk%4x",irq);
+		QETH_DBF_TEXT2(0,trace,dbf_text); 
+		sprintf(dbf_text,"%4x%4x",cstat,dstat);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		sprintf(dbf_text,"%4x",rqparam);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+                PRINT_WARN("got nonzero channel status in qdio_handler " \
+                           "(irq 0x%x, devstat 0x%02x, schstat 0x%02x)\n",
+                           irq,dstat,cstat);
+        }
+
+        if (dstat&~(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) {
+                PRINT_WARN("got the following dstat on the qdio channel: " \
+                           "irq 0x%x, dstat 0x%02x, cstat 0x%02x, " \
+			   "rqparam=%i\n",
+                           irq,dstat,cstat,rqparam);
+        }
+
+}
+
+static int qeth_register_netdev(qeth_card_t *card)
+{
+	int result;
+	char dbf_text[15];
+
+	sprintf(dbf_text,"rgnd%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	result=register_netdev(card->dev);
+
+	return result;
+}
+
+static void qeth_unregister_netdev(qeth_card_t *card)
+{
+	char dbf_text[15];
+
+	sprintf(dbf_text,"nrgn%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	unregister_netdev(card->dev);
+}
+
+static int qeth_stop(struct net_device *dev)
+{
+	char dbf_text[15];
+	qeth_card_t *card;
+
+	card=(qeth_card_t *)dev->priv;
+	sprintf(dbf_text,"stop%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	QETH_DBF_TEXT2(0,setup,dbf_text);
+
+	qeth_save_dev_flag_state(card);
+
+	netif_stop_queue(dev);
+        if (atomic_swap(&((qeth_card_t*)dev->priv)->is_open,0)) {
+                MOD_DEC_USE_COUNT;
+        }
+
+	return 0;
+}
+
+static void qeth_softshutdown(qeth_card_t *card)
+{
+	char dbf_text[15];
+
+	sprintf(dbf_text,"ssht%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+        qeth_send_stoplan(card);
+}
+
+static void qeth_clear_card(qeth_card_t *card,int qdio_clean,int use_halt)
+{
+	unsigned long flags0,flags1,flags2;
+	char dbf_text[15];
+
+	sprintf(dbf_text,"clr%c%4x",(qdio_clean)?'q':' ',card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+	QETH_DBF_TEXT1(0,setup,dbf_text);
+
+	atomic_set(&card->write_busy,0);
+	if (qdio_clean)
+		qdio_cleanup(card->irq2,
+			     (card->type==QETH_CARD_TYPE_IQD)?
+			     QDIO_FLAG_CLEANUP_USING_HALT:
+			     QDIO_FLAG_CLEANUP_USING_CLEAR);
+
+	if (use_halt) {
+		atomic_set(&card->clear_succeeded0,0);
+		atomic_set(&card->clear_succeeded1,0);
+		atomic_set(&card->clear_succeeded2,0);
+		
+		s390irq_spin_lock_irqsave(card->irq0,flags0);
+		halt_IO(card->irq0,CLEAR_STATE,0);
+		s390irq_spin_unlock_irqrestore(card->irq0,flags0);
+		
+		s390irq_spin_lock_irqsave(card->irq1,flags1);
+		halt_IO(card->irq1,CLEAR_STATE,0);
+		s390irq_spin_unlock_irqrestore(card->irq1,flags1);
+
+		s390irq_spin_lock_irqsave(card->irq2,flags2);
+		halt_IO(card->irq2,CLEAR_STATE,0);
+		s390irq_spin_unlock_irqrestore(card->irq2,flags2);
+
+		if (qeth_wait_for_event(&card->clear_succeeded0,
+					QETH_CLEAR_TIMEOUT)==-ETIME) {
+			if (atomic_read(&card->shutdown_phase)!=
+			    QETH_REMOVE_CARD_QUICK) {
+				PRINT_ERR("Did not get interrupt on halt_IO " \
+					  "on irq 0x%x.\n",card->irq0);
+			}
+		}
+		if (qeth_wait_for_event(&card->clear_succeeded1,
+					QETH_CLEAR_TIMEOUT)==-ETIME) {
+			if (atomic_read(&card->shutdown_phase)!=
+			    QETH_REMOVE_CARD_QUICK) {
+				PRINT_ERR("Did not get interrupt on halt_IO " \
+					  "on irq 0x%x.\n",card->irq1);
+			}
+		}
+		if (qeth_wait_for_event(&card->clear_succeeded2,
+					QETH_CLEAR_TIMEOUT)==-ETIME) {
+			if (atomic_read(&card->shutdown_phase)!=
+			    QETH_REMOVE_CARD_QUICK) {
+				PRINT_ERR("Did not get interrupt on halt_IO " \
+					  "on irq 0x%x.\n",card->irq2);
+			}
+		}
+	}
+		
+	atomic_set(&card->clear_succeeded0,0);
+	atomic_set(&card->clear_succeeded1,0);
+	atomic_set(&card->clear_succeeded2,0);
+
+	s390irq_spin_lock_irqsave(card->irq0,flags0);
+        clear_IO(card->irq0,CLEAR_STATE,0);
+	s390irq_spin_unlock_irqrestore(card->irq0,flags0);
+
+	s390irq_spin_lock_irqsave(card->irq1,flags1);
+        clear_IO(card->irq1,CLEAR_STATE,0);
+	s390irq_spin_unlock_irqrestore(card->irq1,flags1);
+
+	s390irq_spin_lock_irqsave(card->irq2,flags2);
+        clear_IO(card->irq2,CLEAR_STATE,0);
+	s390irq_spin_unlock_irqrestore(card->irq2,flags2);
+
+	if (qeth_wait_for_event(&card->clear_succeeded0,
+				QETH_CLEAR_TIMEOUT)==-ETIME) {
+		if (atomic_read(&card->shutdown_phase)!=
+		    QETH_REMOVE_CARD_QUICK) {
+			PRINT_ERR("Did not get interrupt on clear_IO on " \
+				  "irq 0x%x.\n",card->irq0);
+		}
+	}
+	if (qeth_wait_for_event(&card->clear_succeeded1,
+				QETH_CLEAR_TIMEOUT)==-ETIME) {
+		if (atomic_read(&card->shutdown_phase)!=
+		    QETH_REMOVE_CARD_QUICK) {
+			PRINT_ERR("Did not get interrupt on clear_IO on " \
+				  "irq 0x%x.\n",card->irq1);
+		}
+	}
+	if (qeth_wait_for_event(&card->clear_succeeded2,
+				QETH_CLEAR_TIMEOUT)==-ETIME) {
+		if (atomic_read(&card->shutdown_phase)!=
+		    QETH_REMOVE_CARD_QUICK) {
+			PRINT_ERR("Did not get interrupt on clear_IO on " \
+				  "irq 0x%x.\n",card->irq2);
+		}
+	}
+}
+
+static void qeth_free_card(qeth_card_t *card)
+{
+	int i,j;
+	char dbf_text[15];
+	int element_count;
+	qeth_vipa_entry_t *e,*e2;
+
+        if (!card) return;
+
+	sprintf(dbf_text,"free%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+	QETH_DBF_TEXT1(0,setup,dbf_text);
+
+	my_write_lock(&card->vipa_list_lock);
+	e=card->vipa_list;
+	while (e) {
+		e2=e->next;
+		kfree(e);
+		e=e2;
+	}
+	my_write_unlock(&card->vipa_list_lock);
+
+	element_count=(card->options.memusage==MEMUSAGE_DISCONTIG)?
+		BUFFER_MAX_ELEMENTS:1;
+	for (i=0;i<card->options.inbound_buffer_count;i++) {
+		for (j=0;j<element_count;j++) {
+			if (card->inbound_buffer_pool_entry[i][j]) {
+				kfree(card->inbound_buffer_pool_entry[i][j]);
+				card->inbound_buffer_pool_entry[i][j]=NULL;
+			}
+		}
+	}
+	for (i=0;i<card->no_queues;i++)
+		if (card->outbound_ringbuffer[i])
+			vfree(card->outbound_ringbuffer[i]);
+
+        if (card->stats) kfree(card->stats);
+        if (card->dma_stuff) kfree(card->dma_stuff);
+        if (card->dev) kfree(card->dev);
+        if (card->devstat2) kfree(card->devstat2);
+        if (card->devstat1) kfree(card->devstat1);
+        if (card->devstat0) kfree(card->devstat0);
+        vfree(card); /* we checked against NULL already */
+}
+
+/* also locked from outside (setup_lock) */
+static void qeth_remove_card_from_list(qeth_card_t *card)
+{
+        qeth_card_t *cn;
+	unsigned long flags0,flags1,flags2;
+	char dbf_text[15];
+
+	my_write_lock(&list_lock);
+	if (!card) {
+		QETH_DBF_TEXT2(0,trace,"RMCWNOCD");
+		PRINT_WARN("qeth_remove_card_from_list call with no card!\n");
+		return;
+	}
+
+	sprintf(dbf_text,"rmcl%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+        /* check first, if card is in list */
+        if (!firstcard) {
+		QETH_DBF_TEXT2(0,trace,"NOCRDINL");
+                PRINT_WARN("qeth_remove_card_from_list called on " \
+			   "empty card list!!\n");
+                return;
+        }
+
+	s390irq_spin_lock_irqsave(card->irq0,flags0);
+	s390irq_spin_lock_irqsave(card->irq1,flags1);
+	s390irq_spin_lock_irqsave(card->irq2,flags2);
+
+	if (firstcard==card)
+		firstcard=card->next;
+	else {
+		cn=firstcard;
+		while (cn->next) {
+			if (cn->next==card) {
+				cn->next=card->next;
+				card->next=NULL;
+				break;
+			}
+			cn = cn->next;
+		}
+	}
+
+	s390irq_spin_unlock_irqrestore(card->irq2,flags2);
+	s390irq_spin_unlock_irqrestore(card->irq1,flags1);
+	s390irq_spin_unlock_irqrestore(card->irq0,flags0);
+	
+
+	my_write_unlock(&list_lock);
+
+}
+
+static void qeth_delete_all_ips(qeth_card_t *card)
+{
+	qeth_vipa_entry_t *e;
+
+	if (atomic_read(&card->is_softsetup)) {
+		qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
+		qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
+
+#ifdef QETH_IPV6
+		qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
+		qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
+#endif /* QETH_IPV6 */
+
+		my_write_lock(&card->vipa_list_lock);
+		e=card->vipa_list;
+		while (e) {
+			e->state=VIPA_2_B_REMOVED;
+			e=e->next;
+		}
+		my_write_unlock(&card->vipa_list_lock);
+		qeth_start_softsetup_thread(card);
+	}
+}
+
+static void qeth_remove_card(qeth_card_t *card,int method)
+{
+	char dbf_text[15];
+
+	if (!card) {
+		return;
+	}
+	sprintf(dbf_text,"rmcd%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	QETH_DBF_TEXT1(0,setup,dbf_text);
+
+	if (method==QETH_REMOVE_CARD_PROPER) {
+		atomic_set(&card->shutdown_phase,QETH_REMOVE_CARD_PROPER);
+		if (atomic_read(&card->is_open)) {
+			qeth_stop(card->dev);
+			qeth_wait_nonbusy(QETH_REMOVE_WAIT_TIME);
+		}
+		qeth_delete_all_ips(card);
+	} else {
+		atomic_set(&card->shutdown_phase,QETH_REMOVE_CARD_QUICK);
+	}
+	atomic_set(&card->write_busy,0);
+
+	QETH_DBF_TEXT4(0,trace,"freeskbs");
+	qeth_free_all_skbs(card);
+
+	QETH_DBF_TEXT2(0,trace,"upthrsem");
+
+	up(&card->softsetup_thread_sem);
+	up(&card->reinit_thread_sem);
+	while ( (atomic_read(&card->softsetup_thread_is_running)) ||
+   		(atomic_read(&card->reinit_counter)) ) {
+		qeth_wait_nonbusy(QETH_WAIT_FOR_THREAD_TIME);
+	}
+
+	if (method==QETH_REMOVE_CARD_PROPER) {
+		QETH_DBF_TEXT4(0,trace,"softshut");
+		qeth_softshutdown(card);
+		qeth_wait_nonbusy(QETH_REMOVE_WAIT_TIME);
+	}
+
+	atomic_set(&card->is_startlaned,0); /* paranoia, qeth_stop
+					       should prevent
+					       further calls of
+					       hard_start_xmit */
+
+	if (atomic_read(&card->is_registered)) {
+		QETH_DBF_TEXT2(0,trace,"unregdev");
+                qeth_unregister_netdev(card);
+		qeth_wait_nonbusy(QETH_REMOVE_WAIT_TIME);
+		atomic_set(&card->is_registered,0);
+	}
+
+	qeth_put_unique_id(card);
+
+	QETH_DBF_TEXT2(0,trace,"clrcard");
+	if (atomic_read(&card->is_hardsetup)) {
+		PRINT_STUPID("clearing card %s\n",card->dev_name);
+		qeth_clear_card(card,1,0);
+	}
+
+	atomic_set(&card->is_hardsetup,0);
+	atomic_set(&card->is_softsetup,0);
+
+
+	if (card->has_irq>=3) { 
+		QETH_DBF_TEXT2(0,trace,"freeirq2");
+		chandev_free_irq(card->irq2,card->devstat2);
+	}
+	if (card->has_irq>=2) {
+		QETH_DBF_TEXT2(0,trace,"freeirq1");
+		chandev_free_irq(card->irq1,card->devstat1);
+	}
+	if (card->has_irq>=1) {
+		QETH_DBF_TEXT2(0,trace,"freeirq0");
+		chandev_free_irq(card->irq0,card->devstat0);
+	}
+}
+
+static void qeth_destructor(struct net_device *dev)
+{
+	char dbf_text[15];
+	qeth_card_t *card;
+
+	card=(qeth_card_t *)(dev->priv);
+	sprintf(dbf_text,"dstr%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+}
+
+static void qeth_set_multicast_list(struct net_device *dev)
+{
+	char dbf_text[15];
+	qeth_card_t *card=dev->priv;
+
+	sprintf(dbf_text,"smcl%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+	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];
+
+	sprintf(dbf_text,"ngst%4x",((qeth_card_t *)dev->priv)->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+        return 0;
+}
+
+static void qeth_generate_tokens(qeth_card_t *card)
+{
+        card->token.issuer_rm_w=0x00010103UL;
+        card->token.cm_filter_w=0x00010108UL;
+        card->token.cm_connection_w=0x0001010aUL;
+        card->token.ulp_filter_w=0x0001010bUL;
+        card->token.ulp_connection_w=0x0001010dUL;
+}
+
+static int qeth_peer_func_level(int level)
+{
+	if ((level&0xff)==8) return (level&0xff)+0x400;
+        if ( ((level>>8)&3)==1) return (level&0xff)+0x200;
+        return level; /* hmmm... don't know what to do with that level. */
+}
+
+static int qeth_idx_activate_read(qeth_card_t *card)
+{
+        int result,result2;
+        __u16 temp;
+        unsigned long flags;
+	char dbf_text[15];
+
+        result=result2=0;
+
+        memcpy(&card->dma_stuff->write_ccw,WRITE_CCW,sizeof(ccw1_t));
+        card->dma_stuff->write_ccw.count=IDX_ACTIVATE_SIZE;
+        card->dma_stuff->write_ccw.cda=
+		QETH_GET_ADDR(card->dma_stuff->sendbuf);
+
+        memcpy(card->dma_stuff->sendbuf,IDX_ACTIVATE_READ,
+	       IDX_ACTIVATE_SIZE);
+        memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(card->dma_stuff->sendbuf),
+               &card->seqno.trans_hdr,QETH_SEQ_NO_LENGTH);
+
+        memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(card->dma_stuff->sendbuf),
+               &card->token.issuer_rm_w,QETH_MPC_TOKEN_LENGTH);
+        memcpy(QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->sendbuf),
+               &card->func_level,2);
+
+        temp=card->devno2;
+        memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(card->dma_stuff->sendbuf),
+	       &temp,2);
+        temp=(card->cula<<8)+card->unit_addr2;
+        memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(card->dma_stuff->sendbuf),
+               &temp,2);
+
+	sprintf(dbf_text,"iarw%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	QETH_DBF_HEX2(0,control,card->dma_stuff->sendbuf,
+		      QETH_DBF_CONTROL_LEN);
+
+        s390irq_spin_lock_irqsave(card->irq0,flags);
+        result=do_IO(card->irq0,&card->dma_stuff->write_ccw,
+		     IDX_ACTIVATE_WRITE_STATE,0,0);
+        if (result) {
+		qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+                result2=do_IO(card->irq0,&card->dma_stuff->write_ccw,
+			      IDX_ACTIVATE_WRITE_STATE,0,0);
+		sprintf(dbf_text,"IRW1%4x",result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		sprintf(dbf_text,"IRW2%4x",result2);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+                PRINT_WARN("qeth_idx_activate_read (write): do_IO returned " \
+                           "%i, next try returns %i\n",result,result2);
+        }
+        s390irq_spin_unlock_irqrestore(card->irq0,flags);
+
+	if (atomic_read(&card->break_out)) {
+		QETH_DBF_TEXT3(0,trace,"IARWBRKO");
+		result=-EIO;
+		goto exit;
+	}
+
+        if (qeth_sleepon(card,QETH_MPC_TIMEOUT)) {
+		sprintf(dbf_text,"IRWT%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+                PRINT_ERR("IDX_ACTIVATE(wr) on read channel irq 0x%x: " \
+			  "timeout\n",card->irq0);
+                result=-EIO;
+                goto exit;
+        }
+
+	/* start reading on read channel, card->read_ccw is not yet used */
+        memcpy(&card->dma_stuff->read_ccw,READ_CCW,sizeof(ccw1_t));
+        card->dma_stuff->read_ccw.count=QETH_BUFSIZE;
+        card->dma_stuff->read_ccw.cda=QETH_GET_ADDR(card->dma_stuff->recbuf);
+
+        s390irq_spin_lock_irqsave(card->irq0,flags);
+	result2=0;
+        result=do_IO(card->irq0,&card->dma_stuff->read_ccw,
+		     IDX_ACTIVATE_READ_STATE,0,0);
+        if (result) {
+		qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+                result2=do_IO(card->irq0,&card->dma_stuff->read_ccw,
+			      IDX_ACTIVATE_READ_STATE,0,0);
+		sprintf(dbf_text,"IRR1%4x",result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		sprintf(dbf_text,"IRR2%4x",result2);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+                PRINT_WARN("qeth_idx_activate_read (read): do_IO " \
+                           "returned %i, next try returns %i\n",
+                           result,result2);
+        }
+        s390irq_spin_unlock_irqrestore(card->irq0,flags);
+
+        if (result2) {
+		result=result2;
+		if (result) goto exit;
+	}
+
+        if (qeth_sleepon(card,QETH_MPC_TIMEOUT)) {
+		sprintf(dbf_text,"IRRT%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+                PRINT_ERR("IDX_ACTIVATE(rd) on read channel irq 0x%x: " \
+			  "timeout\n",card->irq0);
+                result=-EIO;
+                goto exit;
+        }
+	sprintf(dbf_text,"iarr%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	QETH_DBF_HEX2(0,control,card->dma_stuff->recbuf,
+		      QETH_DBF_CONTROL_LEN);
+
+        if (!(QETH_IS_IDX_ACT_POS_REPLY(card->dma_stuff->recbuf))) {
+		sprintf(dbf_text,"IRNR%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+                PRINT_ERR("IDX_ACTIVATE on read channel irq 0x%x: negative " \
+                          "reply\n",card->irq0);
+                result=-EIO;
+                goto exit;
+        }
+
+	card->portname_required=
+		((!QETH_IDX_NO_PORTNAME_REQUIRED(card->dma_stuff->recbuf))&&
+		 (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)) {
+		sprintf(dbf_text,"IRFL%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		sprintf(dbf_text,"%4x%4x",card->func_level,temp);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+                PRINT_WARN("IDX_ACTIVATE on read channel irq 0x%x: " \
+			   "function level mismatch (sent: 0x%x, " \
+			   "received: 0x%x)\n",
+                           card->irq0,card->func_level,temp);
+		result=-EIO;
+	}
+
+        memcpy(&card->token.issuer_rm_r,
+               QETH_IDX_ACT_ISSUER_RM_TOKEN(card->dma_stuff->recbuf),
+               QETH_MPC_TOKEN_LENGTH);
+
+	memcpy(&card->level[0],
+	       QETH_IDX_REPLY_LEVEL(card->dma_stuff->recbuf),
+	       QETH_MCL_LENGTH);
+ exit:
+        return result;
+}
+
+static int qeth_idx_activate_write(qeth_card_t *card)
+{
+        int result,result2;
+        __u16 temp;
+        unsigned long flags;
+	char dbf_text[15];
+
+        result=result2=0;
+
+        memcpy(&card->dma_stuff->write_ccw,WRITE_CCW,sizeof(ccw1_t));
+        card->dma_stuff->write_ccw.count=IDX_ACTIVATE_SIZE;
+        card->dma_stuff->write_ccw.cda=
+		QETH_GET_ADDR(card->dma_stuff->sendbuf);
+
+        memcpy(card->dma_stuff->sendbuf,IDX_ACTIVATE_WRITE,
+	       IDX_ACTIVATE_SIZE);
+        memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(card->dma_stuff->sendbuf),
+               &card->seqno.trans_hdr,QETH_SEQ_NO_LENGTH);
+        card->seqno.trans_hdr++;
+
+        memcpy(QETH_IDX_ACT_ISSUER_RM_TOKEN(card->dma_stuff->sendbuf),
+               &card->token.issuer_rm_w,QETH_MPC_TOKEN_LENGTH);
+        memcpy(QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->sendbuf),
+               &card->func_level,2);
+        
+        temp=card->devno2;
+        memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(card->dma_stuff->sendbuf),
+	       &temp,2);
+        temp=(card->cula<<8)+card->unit_addr2;
+        memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(card->dma_stuff->sendbuf),
+               &temp,2);
+       
+	sprintf(dbf_text,"iaww%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	QETH_DBF_HEX2(0,control,card->dma_stuff->sendbuf,
+		      QETH_DBF_CONTROL_LEN);
+
+        s390irq_spin_lock_irqsave(card->irq1,flags);
+        result=do_IO(card->irq1,&card->dma_stuff->write_ccw,
+		     IDX_ACTIVATE_WRITE_STATE,0,0);
+        if (result) {
+		qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+                result2=do_IO(card->irq1,&card->dma_stuff->write_ccw,
+			      IDX_ACTIVATE_WRITE_STATE,0,0);
+		sprintf(dbf_text,"IWW1%4x",result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		sprintf(dbf_text,"IWW2%4x",result2);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+                PRINT_WARN("qeth_idx_activate_write (write): do_IO " \
+                           "returned %i, next try returns %i\n",
+                           result,result2);
+        }
+        s390irq_spin_unlock_irqrestore(card->irq1,flags);
+
+	if (atomic_read(&card->break_out)) {
+		QETH_DBF_TEXT3(0,trace,"IAWWBRKO");
+		result=-EIO;
+		goto exit;
+	}
+
+        if (qeth_sleepon(card,QETH_MPC_TIMEOUT)) {
+		sprintf(dbf_text,"IWWT%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+                PRINT_ERR("IDX_ACTIVATE(wr) on write channel irq 0x%x: " \
+			  "timeout\n",card->irq1);
+                result=-EIO;
+                goto exit;
+        }
+
+	QETH_DBF_TEXT3(0,trace,"idxawrrd");
+
+	/* start one read on write channel */
+        memcpy(&card->dma_stuff->read_ccw,READ_CCW,sizeof(ccw1_t));
+        card->dma_stuff->read_ccw.count=QETH_BUFSIZE;
+
+        /* recbuf and card->read_ccw is not yet used by any other
+	   read channel program */
+        card->dma_stuff->read_ccw.cda=QETH_GET_ADDR(card->dma_stuff->recbuf);
+
+        s390irq_spin_lock_irqsave(card->irq1,flags);
+	result2=0;
+        result=do_IO(card->irq1,&card->dma_stuff->read_ccw,
+		     IDX_ACTIVATE_READ_STATE,0,0);
+        if (result) {
+		qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO);
+                result2=do_IO(card->irq1,&card->dma_stuff->read_ccw,
+			      IDX_ACTIVATE_READ_STATE,0,0);
+		sprintf(dbf_text,"IWR1%4x",result);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		sprintf(dbf_text,"IWR2%4x",result2);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+                PRINT_WARN("qeth_idx_activate_write (read): do_IO returned " \
+                           "%i, next try returns %i\n",result,result2);
+        }
+
+        s390irq_spin_unlock_irqrestore(card->irq1,flags);
+
+        if (result2) {
+		result=result2;
+		if (result) goto exit;
+	}
+
+        if (qeth_sleepon(card,QETH_MPC_TIMEOUT)) {
+		sprintf(dbf_text,"IWRT%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+                PRINT_ERR("IDX_ACTIVATE(rd) on write channel irq 0x%x: " \
+			  "timeout\n",card->irq1);
+                result=-EIO;
+                goto exit;
+        }
+	sprintf(dbf_text,"iawr%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	QETH_DBF_HEX2(0,control,card->dma_stuff->recbuf,
+		      QETH_DBF_CONTROL_LEN);
+
+        if (!(QETH_IS_IDX_ACT_POS_REPLY(card->dma_stuff->recbuf))) {
+		sprintf(dbf_text,"IWNR%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+                PRINT_ERR("IDX_ACTIVATE on write channel irq 0x%x: " \
+			  "negative reply\n",card->irq1);
+                result=-EIO;
+                goto exit;
+        }
+
+        memcpy(&temp,QETH_IDX_ACT_FUNC_LEVEL(card->dma_stuff->recbuf),2);
+        if ((temp&~0x0100)!=qeth_peer_func_level(card->func_level)) {
+		sprintf(dbf_text,"IWFM%4x",card->irq0);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+		sprintf(dbf_text,"%4x%4x",card->func_level,temp);
+		QETH_DBF_TEXT1(0,trace,dbf_text);
+                PRINT_WARN("IDX_ACTIVATE on write channel irq 0x%x: " \
+			   "function level mismatch (sent: 0x%x, " \
+			   "received: 0x%x)\n",
+                           card->irq1,card->func_level,temp);
+		result=-EIO;
+	}
+
+ exit:
+        return result;
+}
+
+static int qeth_cm_enable(qeth_card_t *card)
+{
+        unsigned char *buffer;
+	int result;
+	char dbf_text[15];
+
+        memcpy(card->send_buf,CM_ENABLE,CM_ENABLE_SIZE);
+
+        memcpy(QETH_CM_ENABLE_ISSUER_RM_TOKEN(card->send_buf),
+               &card->token.issuer_rm_r,QETH_MPC_TOKEN_LENGTH);
+        memcpy(QETH_CM_ENABLE_FILTER_TOKEN(card->send_buf),
+               &card->token.cm_filter_w,QETH_MPC_TOKEN_LENGTH);
+
+        buffer=qeth_send_control_data(card,card->send_buf,
+                                      CM_ENABLE_SIZE,MPC_SETUP_STATE);
+
+	if (!buffer) {
+		QETH_DBF_TEXT2(0,trace,"CME:NOBF");
+		return -EIO;
+	}
+
+        memcpy(&card->token.cm_filter_r,
+               QETH_CM_ENABLE_RESP_FILTER_TOKEN(buffer),
+               QETH_MPC_TOKEN_LENGTH);
+
+	result=qeth_check_idx_response(buffer);
+
+	sprintf(dbf_text,"cme=%4x",result);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+        return result;
+}
+
+static int qeth_cm_setup(qeth_card_t *card)
+{
+        unsigned char *buffer;
+	int result;
+	char dbf_text[15];
+
+        memcpy(card->send_buf,CM_SETUP,CM_SETUP_SIZE);
+
+        memcpy(QETH_CM_SETUP_DEST_ADDR(card->send_buf),
+               &card->token.issuer_rm_r,QETH_MPC_TOKEN_LENGTH);
+        memcpy(QETH_CM_SETUP_CONNECTION_TOKEN(card->send_buf),
+               &card->token.cm_connection_w,QETH_MPC_TOKEN_LENGTH);
+        memcpy(QETH_CM_SETUP_FILTER_TOKEN(card->send_buf),
+               &card->token.cm_filter_r,QETH_MPC_TOKEN_LENGTH);
+
+        buffer=qeth_send_control_data(card,card->send_buf,
+                                      CM_SETUP_SIZE,MPC_SETUP_STATE);
+
+	if (!buffer) {
+		QETH_DBF_TEXT2(0,trace,"CMS:NOBF");
+		return -EIO;
+	}
+
+        memcpy(&card->token.cm_connection_r,
+               QETH_CM_SETUP_RESP_DEST_ADDR(buffer),
+               QETH_MPC_TOKEN_LENGTH);
+
+	result=qeth_check_idx_response(buffer);
+
+	sprintf(dbf_text,"cms=%4x",result);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+        return result;
+}
+
+static int qeth_ulp_enable(qeth_card_t *card)
+{
+        unsigned char *buffer;
+	__u16 mtu,framesize;
+	__u16 len;
+	__u8 link_type;
+	int result;
+	char dbf_text[15];
+
+        memcpy(card->send_buf,ULP_ENABLE,ULP_ENABLE_SIZE);
+
+	*(QETH_ULP_ENABLE_LINKNUM(card->send_buf))=(__u8)card->options.portno;
+
+        memcpy(QETH_ULP_ENABLE_DEST_ADDR(card->send_buf),
+               &card->token.cm_connection_r,QETH_MPC_TOKEN_LENGTH);
+        memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(card->send_buf),
+               &card->token.ulp_filter_w,QETH_MPC_TOKEN_LENGTH);
+
+        memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(card->send_buf),
+               card->options.portname,9);
+
+        buffer=qeth_send_control_data(card,card->send_buf,
+                                      ULP_ENABLE_SIZE,MPC_SETUP_STATE);
+
+	if (!buffer) {
+		QETH_DBF_TEXT2(0,trace,"ULE:NOBF");
+		return -EIO;
+	}
+
+        memcpy(&card->token.ulp_filter_r,
+               QETH_ULP_ENABLE_RESP_FILTER_TOKEN(buffer),
+               QETH_MPC_TOKEN_LENGTH);
+
+	/* to be done before qeth_init_ringbuffers and qeth_init_dev */
+	if (qeth_get_mtu_out_of_mpc(card->type)) {
+		memcpy(&framesize,QETH_ULP_ENABLE_RESP_MAX_MTU(buffer),2);
+		mtu=qeth_get_mtu_outof_framesize(framesize);
+
+		sprintf(dbf_text,"ule:%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		sprintf(dbf_text,"mtu=%4x",mtu);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+
+		if (!mtu) return -EINVAL;
+
+		card->max_mtu=mtu;
+		card->initial_mtu=mtu;
+		card->inbound_buffer_size=mtu+2*PAGE_SIZE;
+	} else {
+		card->initial_mtu=qeth_get_initial_mtu_for_card(card);
+		card->max_mtu=qeth_get_max_mtu_for_card(card->type);
+		card->inbound_buffer_size=DEFAULT_BUFFER_SIZE;
+	}
+
+	memcpy(&len,QETH_ULP_ENABLE_RESP_DIFINFO_LEN(buffer),2);
+	if (len>=QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) {
+		memcpy(&link_type,QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer),1);
+		card->link_type=link_type;
+		sprintf(dbf_text,"link=%2x",link_type);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+	} else card->link_type=0;
+
+	result=qeth_check_idx_response(buffer);
+
+	sprintf(dbf_text,"ule=%4x",result);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+        return result;
+}
+
+static int qeth_ulp_setup(qeth_card_t *card)
+{
+        unsigned char *buffer;
+        __u16 temp;
+	int result;
+	char dbf_text[15];
+
+        memcpy(card->send_buf,ULP_SETUP,ULP_SETUP_SIZE);
+
+        memcpy(QETH_ULP_SETUP_DEST_ADDR(card->send_buf),
+               &card->token.cm_connection_r,QETH_MPC_TOKEN_LENGTH);
+        memcpy(QETH_ULP_SETUP_CONNECTION_TOKEN(card->send_buf),
+               &card->token.ulp_connection_w,QETH_MPC_TOKEN_LENGTH);
+        memcpy(QETH_ULP_SETUP_FILTER_TOKEN(card->send_buf),
+               &card->token.ulp_filter_r,QETH_MPC_TOKEN_LENGTH);
+
+        temp=card->devno2;
+        memcpy(QETH_ULP_SETUP_CUA(card->send_buf),
+               &temp,2);
+        temp=(card->cula<<8)+card->unit_addr2;
+        memcpy(QETH_ULP_SETUP_REAL_DEVADDR(card->send_buf),
+               &temp,2);
+
+        buffer=qeth_send_control_data(card,card->send_buf,
+                                      ULP_SETUP_SIZE,MPC_SETUP_STATE);
+
+	if (!buffer) {
+		QETH_DBF_TEXT2(0,trace,"ULS:NOBF");
+		return -EIO;
+	}
+
+        memcpy(&card->token.ulp_connection_r,
+               QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(buffer),
+               QETH_MPC_TOKEN_LENGTH);
+
+        result=qeth_check_idx_response(buffer);
+
+	sprintf(dbf_text,"uls=%4x",result);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	return result;
+}
+
+static int qeth_qdio_establish(qeth_card_t *card)
+{
+	int result;
+	char adapter_area[15];
+	char dbf_text[15];
+	void **input_array,**output_array,**ptr;
+	int i,j;
+	qdio_initialize_t init_data;
+
+	adapter_area[0]=_ascebc['P'];
+	adapter_area[1]=_ascebc['C'];
+	adapter_area[2]=_ascebc['I'];
+	adapter_area[3]=_ascebc['T'];
+	*((unsigned int*)(&adapter_area[4]))=PCI_THRESHOLD_A;
+	*((unsigned int*)(&adapter_area[8]))=PCI_THRESHOLD_B;
+	*((unsigned int*)(&adapter_area[12]))=PCI_TIMER_VALUE;
+
+	input_array=vmalloc(QDIO_MAX_BUFFERS_PER_Q*sizeof(void*));
+	if (!input_array) return -ENOMEM;
+	ptr=input_array;
+	for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+		*ptr=(void*)virt_to_phys
+			(&card->inbound_qdio_buffers[j]);
+		ptr++;
+	}
+	
+	output_array=vmalloc(QDIO_MAX_BUFFERS_PER_Q*sizeof(void*)*
+			     card->no_queues);
+	if (!output_array) {
+		vfree(input_array);
+		return -ENOMEM;
+	}
+	ptr=output_array;
+	for (i=0;i<card->no_queues;i++)
+		for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+			*ptr=(void*)virt_to_phys
+				(&card->outbound_ringbuffer[i]->
+				 buffer[j]);
+			ptr++;
+		}
+	
+	init_data.irq=card->irq2;
+	init_data.q_format=qeth_get_q_format(card->type);
+	init_data.qib_param_field_format=0;
+	init_data.qib_param_field=adapter_area;
+	init_data.input_slib_elements=NULL;
+	init_data.output_slib_elements=NULL;
+	init_data.min_input_threshold=card->options.polltime;
+	init_data.max_input_threshold=card->options.polltime;
+	init_data.min_output_threshold=QETH_MIN_OUTPUT_THRESHOLD;
+	init_data.max_output_threshold=QETH_MAX_OUTPUT_THRESHOLD;
+	init_data.no_input_qs=1;
+	init_data.no_output_qs=card->no_queues;
+	init_data.input_handler=qeth_qdio_input_handler;
+	init_data.output_handler=qeth_qdio_output_handler;
+	init_data.int_parm=(unsigned long)card;
+	init_data.flags=QDIO_INBOUND_0COPY_SBALS|
+			QDIO_OUTBOUND_0COPY_SBALS|
+			QDIO_USE_OUTBOUND_PCIS;
+	if (card->do_pfix)
+		init_data.flags |= QDIO_PFIX;
+	init_data.input_sbal_addr_array=input_array;
+	init_data.output_sbal_addr_array=output_array;
+	
+	result=qdio_initialize(&init_data);
+	
+	vfree(input_array);
+	vfree(output_array);
+
+	sprintf(dbf_text,"qde=%4i",result);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	return result;
+}
+
+static int qeth_qdio_activate(qeth_card_t *card)
+{
+	int result;
+	char dbf_text[15];
+
+	result=qdio_activate(card->irq2,0);
+
+	sprintf(dbf_text,"qda=%4x",result);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	return result;
+}
+
+static int qeth_dm_act(qeth_card_t *card)
+{
+        unsigned char *buffer;
+	int result;
+	char dbf_text[15];
+
+        memcpy(card->send_buf,DM_ACT,DM_ACT_SIZE);
+
+        memcpy(QETH_DM_ACT_DEST_ADDR(card->send_buf),
+               &card->token.cm_connection_r,QETH_MPC_TOKEN_LENGTH);
+        memcpy(QETH_DM_ACT_CONNECTION_TOKEN(card->send_buf),
+               &card->token.ulp_connection_r,QETH_MPC_TOKEN_LENGTH);
+
+        buffer=qeth_send_control_data(card,card->send_buf,
+                                      DM_ACT_SIZE,MPC_SETUP_STATE);
+
+	if (!buffer) {
+		QETH_DBF_TEXT2(0,trace,"DMA:NOBF");
+		return -EIO;
+	}
+
+        result=qeth_check_idx_response(buffer);
+
+	sprintf(dbf_text,"dma=%4x",result);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	return result;
+}
+
+#if defined(QETH_VLAN)||defined(QETH_IPV6)
+static int qeth_verify_dev(struct net_device *dev)
+{
+	qeth_card_t *tmp;
+        int result=0;
+#ifdef QETH_VLAN
+        struct vlan_group *vlan_grp;
+        int i;
+#endif
+
+	my_read_lock(&list_lock);
+	tmp=firstcard;
+	for (;tmp&&(!result);tmp=tmp->next) {
+		if (atomic_read(&tmp->shutdown_phase))
+			continue;
+		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) {
+					result=QETH_VERIFY_IS_VLAN_DEV;
+				}
+			}
+		}
+
+#endif
+	}
+        my_read_unlock(&list_lock);
+	return result;
+}
+#endif /* defined(QETH_VLAN)||defined(QETH_IPV6) */
+
+static int qeth_verify_card(qeth_card_t *card)
+{
+  	qeth_card_t *tmp;
+	int result=0;
+
+	my_read_lock(&list_lock);
+  	tmp=firstcard;
+    	while (tmp) {
+      		if ((card==tmp) && (!atomic_read(&card->shutdown_phase))) {
+			result=1;
+			break;
+		}
+		tmp=tmp->next;
+	}
+	my_read_unlock(&list_lock);
+	return result;
+}
+
+#ifdef QETH_IPV6
+extern struct neigh_table arp_tbl;
+int (*qeth_old_arp_constructor)(struct neighbour *);
+static struct neigh_ops arp_direct_ops_template =
+{
+	family:			AF_INET,
+	destructor:		NULL,
+	solicit:		NULL,
+	error_report:		NULL,
+	output:        		dev_queue_xmit,
+	connected_output:	dev_queue_xmit,
+	hh_output:		dev_queue_xmit,
+	queue_xmit:		dev_queue_xmit
+};
+/* as we have neighbour structures point to this structure, even
+ * after our life time, this will stay in memory as a leak */
+static struct neigh_ops *arp_direct_ops;
+
+static int qeth_arp_constructor(struct neighbour *neigh)
+{
+        char dbf_text[15];
+        struct net_device *dev = neigh->dev;
+        struct in_device *in_dev = in_dev_get(dev);
+
+        if (in_dev == NULL)
+                return -EINVAL;
+
+        QETH_DBF_TEXT4(0,trace,"arpconst");
+        if (!qeth_verify_dev(dev)) {
+	    	
+                in_dev_put(in_dev);
+                return qeth_old_arp_constructor(neigh);
+        }
+
+        neigh->type = inet_addr_type(*(u32*)neigh->primary_key);
+        if (in_dev->arp_parms)
+                neigh->parms = in_dev->arp_parms;
+
+        in_dev_put(in_dev);
+
+        sprintf(dbf_text,"%08x",ntohl(*((__u32*)(neigh->primary_key))));
+        QETH_DBF_TEXT4(0,trace,dbf_text);
+        QETH_DBF_HEX4(0,trace,&neigh,sizeof(void*));
+
+        neigh->nud_state = NUD_NOARP;
+        neigh->ops = arp_direct_ops;
+        neigh->output = neigh->ops->queue_xmit;
+        return 0;
+}
+
+static int qeth_hard_header(struct sk_buff *skb,struct net_device *dev,
+			    unsigned short type,void *daddr,void *saddr,
+			    unsigned len)
+{
+	qeth_card_t *card;
+
+	QETH_DBF_TEXT5(0,trace,"hardhdr");
+
+#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;
+
+	return card->hard_header(skb,dev,type,daddr,saddr,len);
+}
+
+static void qeth_header_cache_update(struct hh_cache *hh,
+                                     struct net_device *dev,
+                                     unsigned char *haddr)
+{
+	qeth_card_t *card;
+
+	card=(qeth_card_t*)dev->priv;
+	QETH_DBF_TEXT5(0,trace,"hdrcheup");
+        return card->header_cache_update(hh,dev,haddr);
+}
+
+static int qeth_rebuild_header(struct sk_buff *skb)
+{
+	qeth_card_t *card;
+	QETH_DBF_TEXT5(0,trace,"rebldhdr");
+	if (skb->protocol==__constant_htons(ETH_P_IP)) return 0;
+
+#ifdef QETH_VLAN
+	if (qeth_verify_dev(skb->dev)==QETH_VERIFY_IS_VLAN_DEV)
+		card = (qeth_card_t *)VLAN_DEV_INFO(skb->dev)->real_dev->priv;
+        else
+#endif
+	card=(qeth_card_t*)skb->dev->priv;
+
+	return card->rebuild_header(skb);
+}
+
+static void qeth_ipv6_init_card(qeth_card_t *card)
+{
+	card->hard_header=qeth_get_hard_header(card->link_type);
+	card->rebuild_header=qeth_get_rebuild_header(card->link_type);
+	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 */
+
+#ifdef QETH_VLAN
+static void qeth_vlan_rx_register(struct net_device *dev, 
+				  struct vlan_group *grp)
+{
+        qeth_card_t *card;
+        card = (qeth_card_t *)dev->priv;
+        spin_lock_irq(&card->vlan_lock);
+        card->vlangrp = grp;
+        spin_unlock_irq(&card->vlan_lock);
+}
+static void qeth_vlan_rx_kill_vid(struct net_device *dev, 
+				  unsigned short vid)
+{
+        qeth_card_t *card;
+        card = (qeth_card_t *)dev->priv;
+        spin_lock_irq(&card->vlan_lock);
+	if (card->vlangrp)
+                card->vlangrp->vlan_devices[vid] = NULL;
+        spin_unlock_irq(&card->vlan_lock);
+}
+
+#endif /*QETH_VLAN*/
+
+
+static void qeth_tx_timeout(struct net_device *dev)
+{
+	char dbf_text[15];
+	qeth_card_t *card;
+
+	card=(qeth_card_t *)dev->priv;
+	sprintf(dbf_text,"XMTO%4x",card->irq0);
+	QETH_DBF_TEXT2(1,trace,dbf_text);
+	card->stats->tx_errors++;
+	atomic_set(&card->problem,PROBLEM_TX_TIMEOUT);
+	qeth_schedule_recovery(card);
+}
+
+static int qeth_init_dev(struct net_device *dev)
+{
+	qeth_card_t *card;
+	char dbf_text[15];
+
+	card=(qeth_card_t *)dev->priv;
+
+	sprintf(dbf_text,"inid%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	dev->tx_timeout=&qeth_tx_timeout;
+	dev->watchdog_timeo=QETH_TX_TIMEOUT;
+	dev->open=qeth_open;
+	dev->stop=qeth_stop;
+	dev->set_config=qeth_set_config;
+	dev->hard_start_xmit=qeth_hard_start_xmit;
+	dev->do_ioctl=qeth_do_ioctl;
+	dev->get_stats=qeth_get_stats;
+	dev->change_mtu=qeth_change_mtu;
+#ifdef QETH_VLAN
+	dev->vlan_rx_register = qeth_vlan_rx_register;
+        dev->vlan_rx_kill_vid = qeth_vlan_rx_kill_vid;
+#endif
+
+	dev->rebuild_header=
+#ifdef QETH_IPV6
+		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
+		(qeth_get_rebuild_header(card->link_type)?
+		 qeth_rebuild_header:NULL):
+#endif /* QETH_IPV6 */
+		NULL;
+	dev->hard_header=
+#ifdef QETH_IPV6
+		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
+		(qeth_get_hard_header(card->link_type)?
+		 qeth_hard_header:NULL):
+#endif /* QETH_IPV6 */
+		NULL;
+	dev->header_cache_update=
+#ifdef QETH_IPV6
+		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
+		(qeth_get_header_cache_update(card->link_type)?
+		 qeth_header_cache_update:NULL):
+#endif /* QETH_IPV6 */
+		NULL;
+	dev->hard_header_cache=
+#ifdef QETH_IPV6
+		(!(qeth_get_additional_dev_flags(card->type)&IFF_NOARP))?
+		qeth_get_hard_header_cache(card->link_type):
+#endif /* QETH_IPV6 */
+		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|=(
+		     (card->options.fake_broadcast==FAKE_BROADCAST)||
+		     (card->broadcast_capable)
+		     )?
+		IFF_BROADCAST:0;
+
+	/* is done in hardsetup_card... see comment below
+	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
+	 * do that in hardsetup_card every time
+	qeth_get_unique_id(card);*/
+
+#ifdef CONFIG_SHARED_IPV6_CARDS
+	dev->features=(card->unique_id&UNIQUE_ID_NOT_BY_CARD)?
+		0:NETIF_F_SHARED_IPV6;
+	dev->dev_id=card->unique_id&0xffff;
+#endif /* CONFIG_SHARED_IPV6_CARDS */
+
+	dev->tx_queue_len=qeth_get_device_tx_q_len(card->type);
+	dev->hard_header_len=qeth_get_hlen(card->link_type)+
+		card->options.add_hhlen;
+	dev->addr_len=OSA_ADDR_LEN; /* is ok for eth, tr, atm lane */
+	netif_start_queue(dev);
+
+        dev->mtu=card->initial_mtu;
+
+#ifdef QETH_IPV6
+	qeth_ipv6_init_card(card);
+#endif /* QETH_IPV6 */
+
+	dev_init_buffers(dev);
+ 
+	return 0;
+}
+
+static int qeth_get_unitaddr(qeth_card_t *card)
+{
+	char *prcd;
+        int result=0;
+	char dbf_text[15];
+	int length;
+
+	sprintf(dbf_text,"gtua%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+	result = read_conf_data(card->irq2, (void **)&prcd, &length, 0);
+	if (result) {
+		sprintf(dbf_text, "rcd%4x", result);
+		QETH_DBF_TEXT3(0, trace, dbf_text);
+		PRINT_ERR("read_conf_data for sch %x returned %i\n",
+			  card->irq2, result);
+		goto exit;
+	}
+
+	card->chpid = prcd[30];
+	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;
+
+	sprintf(dbf_text,"chpid:%02x",card->chpid);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	sprintf(dbf_text,"unad2:%02x",card->unit_addr2);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	sprintf(dbf_text,"cula:%02x",card->cula);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+        result=0;
+
+ exit:
+        return result;
+}
+
+static int qeth_send_nops(qeth_card_t *card)
+{
+	int result,result2;
+	char dbf_text[15];
+	int irq;
+	unsigned long saveflags;
+
+	card->dma_stuff->write_ccw.cmd_code=CCW_NOP_CMD;
+	card->dma_stuff->write_ccw.flags=CCW_FLAG_SLI;
+	card->dma_stuff->write_ccw.count=CCW_NOP_COUNT;
+	card->dma_stuff->write_ccw.cda=(unsigned long)NULL;
+
+#define DO_SEND_NOP(subchannel) \
+do { \
+	irq=subchannel; \
+	sprintf(dbf_text,"snnp%4x",irq); \
+	QETH_DBF_TEXT3(0,trace,dbf_text); \
+\
+	s390irq_spin_lock_irqsave(irq,saveflags); \
+        result=do_IO(irq,&card->dma_stuff->write_ccw,NOP_STATE,0,0); \
+        if (result) { \
+		qeth_delay_millis(QETH_WAIT_BEFORE_2ND_DOIO); \
+                result2=do_IO(irq,&card->dma_stuff->write_ccw, \
+			      NOP_STATE,0,0); \
+                PRINT_WARN("qeth_send_nops on irq 0x%x: do_IO returned %i, " \
+                           "next try returns %i\n", \
+                           irq,result,result2); \
+		result=result2; \
+        } \
+        s390irq_spin_unlock_irqrestore(irq,saveflags); \
+\
+	if (result) goto exit; \
+\
+        if (qeth_sleepon(card,QETH_NOP_TIMEOUT)) { \
+		QETH_DBF_TEXT2(0,trace,"snnp:tme"); \
+		result=-EIO; \
+		goto exit; \
+        } \
+} while (0)
+
+	DO_SEND_NOP(card->irq0);
+	DO_SEND_NOP(card->irq1);
+	DO_SEND_NOP(card->irq2);
+
+exit:
+	return result;
+}
+
+static void qeth_clear_card_structures(qeth_card_t *card)
+{
+	int i,j;
+	char dbf_text[15];
+
+	if (!card) {
+		QETH_DBF_TEXT2(0,trace,"clrCRDnc");
+		return;
+	}
+
+	sprintf(dbf_text,"clcs%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+	
+	atomic_set(&card->is_startlaned,0);
+					   
+	for (i=0;i<QETH_MAX_QUEUES;i++) {
+		card->send_state[i]=SEND_STATE_DONT_PACK;
+		card->outbound_first_free_buffer[i]=0;
+		atomic_set(&card->outbound_used_buffers[i],0);
+		atomic_set(&card->outbound_ringbuffer_lock[i],0);
+		
+		for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+			card->outbound_buffer_send_state[i][j]=
+				SEND_STATE_DONT_PACK;
+			card->send_retries[i][j]=0;
+			
+			if (i<card->no_queues) {
+				card->outbound_ringbuffer[i]->
+					ringbuf_element[j].
+					next_element_to_fill=0;
+				card->outbound_bytes_in_buffer[i]=0;
+				skb_queue_head_init(&card->
+						    outbound_ringbuffer[i]->
+						    ringbuf_element[j].
+						    skb_list);
+			}
+		}
+	}
+
+	for (i=0;i<card->options.inbound_buffer_count;i++) {
+		xchg((int*)&card->inbound_buffer_pool_entry_used[i],
+			 BUFFER_UNUSED);
+	}
+
+	spin_lock_init(&card->requeue_input_lock);
+	atomic_set(&card->requeue_position,0);
+	atomic_set(&card->requeue_counter,0);
+	
+	card->seqno.trans_hdr=0;
+	card->seqno.pdu_hdr=0;
+	card->seqno.pdu_hdr_ack=0;
+	card->seqno.ipa=0;
+	
+	qeth_clear_ifa4_list(&card->ip_current_state.ip_ifa);
+	qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
+	qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm_ifa);
+	qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
+
+#ifdef QETH_IPV6
+	qeth_clear_ifa6_list(&card->ip_current_state.ip6_ifa);
+	qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
+	qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm6_ifa);
+	qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
+#endif /* QETH_IPV6 */
+}
+
+static void qeth_init_input_buffers(qeth_card_t *card)
+{
+	int i;
+
+	/* slowly, slowly (we don't want to enqueue all buffers
+	 * at one time) */
+	for (i=0;i<QDIO_MAX_BUFFERS_PER_Q;i++) {
+		atomic_set(&card->inbound_buffer_refcnt[i],1);
+	}
+	for (i=0;i<QDIO_MAX_BUFFERS_PER_Q;i++) {
+	   	atomic_set(&card->inbound_buffer_refcnt[i],0);
+		/* only try to queue as many buffers as we have at all */
+		if (i<card->options.inbound_buffer_count) {
+			qeth_queue_input_buffer(card,i,0);
+		}
+	}
+	qdio_synchronize(card->irq2,QDIO_FLAG_SYNC_INPUT,0);
+}
+
+/* initializes all the structures for a card */
+static int qeth_hardsetup_card(qeth_card_t *card,int in_recovery)
+{
+        int result,q,breakout;
+	unsigned long flags;
+	int laps=QETH_HARDSETUP_LAPS;
+	int clear_laps;
+	int cleanup_qdio;
+	char dbf_text[15];
+	int i,r;
+
+	/* setup name and so on */
+	atomic_set(&card->shutdown_phase,0);
+
+        if (atomic_read(&card->is_hardsetup)) {
+		sprintf(dbf_text,"hscd%4x",card->irq0);
+		QETH_DBF_TEXT2(1,trace,dbf_text);
+                PRINT_ALL("card is already hardsetup.\n");
+                return 0;
+        }
+
+	cleanup_qdio=in_recovery; /* if we are in recovery, we clean
+				     the qdio stuff up */
+
+	my_spin_lock(&card->hardsetup_lock);
+	atomic_set(&card->write_busy,0);
+
+	do {
+		if (in_recovery) {
+			PRINT_STUPID("qeth: recovery: quiescing %s...\n",
+				     card->dev_name);
+			sprintf(dbf_text,"Rqsc%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+			qeth_wait_nonbusy(QETH_QUIESCE_WAIT_BEFORE_CLEAR);
+		}
+		clear_laps=QETH_HARDSETUP_CLEAR_LAPS;
+		do {
+			if (in_recovery)
+				PRINT_STUPID("clearing card %s\n",
+					     card->dev_name);
+			qeth_clear_card(card,cleanup_qdio,
+					(card->type==QETH_CARD_TYPE_OSAE));
+			result=qeth_send_nops(card);
+			breakout=atomic_read(&card->break_out);
+		} while ( (--clear_laps) && (result) );
+		if (result) {
+			goto exit;
+		}
+
+		if (in_recovery) {
+			PRINT_STUPID("qeth: recovery: still quiescing %s...\n",
+				     card->dev_name);
+			sprintf(dbf_text,"RQsc%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+			qeth_wait_nonbusy(QETH_QUIESCE_WAIT_AFTER_CLEAR);
+		} else {
+			atomic_set(&card->shutdown_phase,0);
+		}
+
+		cleanup_qdio=0; /* qdio was cleaned now, if necessary */
+
+		result=qeth_get_unitaddr(card);
+		if (result) goto exit;
+
+		qeth_generate_tokens(card);
+
+#define PRINT_TOKENS do { \
+		sprintf(dbf_text,"stra    "); \
+		memcpy(&dbf_text[4],&card->seqno.trans_hdr,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"spdu    "); \
+		memcpy(&dbf_text[4],&card->seqno.pdu_hdr,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"spda    "); \
+		memcpy(&dbf_text[4],&card->seqno.pdu_hdr_ack,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"sipa    "); \
+		memcpy(&dbf_text[4],&card->seqno.ipa,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"tisw    "); \
+		memcpy(&dbf_text[4],&card->token.issuer_rm_w,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"tisr    "); \
+		memcpy(&dbf_text[4],&card->token.issuer_rm_r,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"tcfw    "); \
+		memcpy(&dbf_text[4],&card->token.cm_filter_w,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"tcfr    "); \
+		memcpy(&dbf_text[4],&card->token.cm_filter_r,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"tccw    "); \
+		memcpy(&dbf_text[4],&card->token.cm_connection_w,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"tccr    "); \
+		memcpy(&dbf_text[4],&card->token.cm_connection_r,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"tufw    "); \
+		memcpy(&dbf_text[4],&card->token.ulp_filter_w,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"tufr    "); \
+		memcpy(&dbf_text[4],&card->token.ulp_filter_r,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"tucw    "); \
+		memcpy(&dbf_text[4],&card->token.ulp_connection_w,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+		sprintf(dbf_text,"tucr    "); \
+		memcpy(&dbf_text[4],&card->token.ulp_connection_r,4); \
+		QETH_DBF_HEX3(0,trace,dbf_text,QETH_DBF_TRACE_LEN); \
+	} while (0)
+		PRINT_TOKENS;
+
+		/* card->break_out and problem will be set here to 0
+		 * (in each lap) (there can't be a problem at this
+		 * early time) */
+		atomic_set(&card->problem,0);
+		atomic_set(&card->break_out,0);
+
+#define CHECK_ERRORS \
+		breakout=atomic_read(&card->break_out); \
+		if (breakout==QETH_BREAKOUT_AGAIN) \
+			continue; \
+		else if (breakout==QETH_BREAKOUT_LEAVE) { \
+			result=-EIO; \
+			goto exit; \
+		} \
+		if (result) goto exit
+
+		QETH_DBF_TEXT2(0,trace,"hsidxard");
+		result=qeth_idx_activate_read(card);
+		CHECK_ERRORS;
+
+		PRINT_TOKENS;
+		QETH_DBF_TEXT2(0,trace,"hsidxawr");
+		result=qeth_idx_activate_write(card);
+		CHECK_ERRORS;
+
+		QETH_DBF_TEXT2(0,trace,"hsissurd");
+		/* from here, there will always be an outstanding read */
+		s390irq_spin_lock_irqsave(card->irq0,flags);
+		qeth_issue_next_read(card);
+		s390irq_spin_unlock_irqrestore(card->irq0,flags);
+
+		PRINT_TOKENS;
+		QETH_DBF_TEXT2(0,trace,"hscmenab");
+		result=qeth_cm_enable(card);
+		CHECK_ERRORS;
+
+		PRINT_TOKENS;
+		QETH_DBF_TEXT2(0,trace,"hscmsetu");
+		result=qeth_cm_setup(card);
+		CHECK_ERRORS;
+
+		PRINT_TOKENS;
+		QETH_DBF_TEXT2(0,trace,"hsulpena");
+		result=qeth_ulp_enable(card);
+		CHECK_ERRORS;
+
+		PRINT_TOKENS;
+		QETH_DBF_TEXT2(0,trace,"hsulpset");
+		result=qeth_ulp_setup(card);
+		CHECK_ERRORS;
+
+		cleanup_qdio=1;
+
+		QETH_DBF_TEXT2(0,trace,"hsqdioes");
+		result=qeth_qdio_establish(card);
+		CHECK_ERRORS;
+
+		PRINT_TOKENS;
+		QETH_DBF_TEXT2(0,trace,"hsqdioac");
+		result=qeth_qdio_activate(card);
+		CHECK_ERRORS;
+
+		PRINT_TOKENS;
+		QETH_DBF_TEXT2(0,trace,"hsdmact");
+		result=qeth_dm_act(card);
+		CHECK_ERRORS;
+	} while ( (laps--) && (breakout==QETH_BREAKOUT_AGAIN) );
+	if (breakout==QETH_BREAKOUT_AGAIN) {
+		sprintf(dbf_text,"hsnr%4x",card->irq0);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+		printk("qeth: recovery not successful on device " \
+		       "0x%X/0x%X/0x%X; giving up.\n",
+		       card->devno0,card->devno1,card->devno2);
+		result=-EIO;
+		goto exit;
+	}
+
+	qeth_clear_ifa4_list(&card->ip_current_state.ip_ifa);
+	qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
+	qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm_ifa);
+	qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
+
+#ifdef QETH_IPV6
+	qeth_clear_ifa6_list(&card->ip_current_state.ip6_ifa);
+	qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
+	qeth_clear_ifamc_list(&card->ip_mc_current_state.ipm6_ifa);
+	qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
+#endif /* QETH_IPV6 */
+
+	if (!atomic_read(&card->is_registered)) {
+		card->dev->dev_addr[0]=0; /* we don't know the mac addr yet */
+		card->dev->dev_addr[1]=0;
+		card->dev->dev_addr[2]=0;
+		card->dev->dev_addr[3]=0;
+		card->dev->dev_addr[4]=0;
+		card->dev->dev_addr[5]=0;
+		card->dev->broadcast[0]=card->dev->broadcast[1]=0xff;
+		card->dev->broadcast[2]=card->dev->broadcast[3]=0xff;
+		card->dev->broadcast[4]=card->dev->broadcast[5]=0xff;
+		
+		card->dev->type=qeth_get_arphrd_type(card->type,
+						     card->link_type);
+
+		card->dev->init=qeth_init_dev;
+
+		if (card->options.memusage==MEMUSAGE_CONTIG) {
+			card->easy_copy_cap=
+				qeth_determine_easy_copy_cap(card->type);
+		} else card->easy_copy_cap=0;
+		card->ipa_timeout=qeth_get_ipa_timeout(card->type);
+	}
+
+	atomic_set(&card->is_hardsetup,1);
+	atomic_set(&card->is_softsetup,0);
+	atomic_set(&card->startlan_attempts,1);
+
+	for (q=0;q<card->no_queues;q++)
+		card->send_state[q]=SEND_STATE_DONT_PACK;
+
+	/* here we need to know, whether we should include a value
+	 * into eui-64 address generation */
+	QETH_DBF_TEXT2(0,trace,"qipassi4");
+	r=qeth_send_qipassist(card,4);
+	if (r) {
+		PRINT_WARN("couldn't send QIPASSIST4 on %s: " \
+			   "0x%x\n",card->dev_name,r);
+		sprintf(dbf_text,"QIP4%4x",r);
+		QETH_DBF_TEXT2(0,trace,dbf_text);
+	}
+
+	sprintf(dbf_text,"%4x%4x",card->ipa_supported,card->ipa_enabled);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+	qeth_get_unique_id(card);
+
+	/* print out status */
+	if (in_recovery) {
+		qeth_clear_card_structures(card);
+		qeth_init_input_buffers(card);
+		QETH_DBF_TEXT1(0,trace,"RECOVSUC");
+		printk("qeth: recovered device 0x%X/0x%X/0x%X (%s) " \
+		       "successfully.\n",
+		       card->devno0,card->devno1,card->devno2,
+		       card->dev_name);
+	} else {
+		QETH_DBF_TEXT2(0,trace,"hrdsetok");
+
+		switch (card->type) {
+		case QETH_CARD_TYPE_OSAE:
+			/* VM will use a non-zero first character to indicate
+			 * a HiperSockets like reporting of the level
+			 * OSA sets the first character to zero */
+			if (!card->level[0]) {
+				sprintf(card->level,"%02x%02x",card->level[2],
+					card->level[3]);
+				card->level[QETH_MCL_LENGTH]=0;
+				break;
+			} else {
+				/* fallthrough */
+			}
+		case QETH_CARD_TYPE_IQD:
+			card->level[0]=(char)_ebcasc[(__u8)card->level[0]];
+			card->level[1]=(char)_ebcasc[(__u8)card->level[1]];
+			card->level[2]=(char)_ebcasc[(__u8)card->level[2]];
+			card->level[3]=(char)_ebcasc[(__u8)card->level[3]];
+			card->level[QETH_MCL_LENGTH]=0;
+			break;
+		default:
+			memset(&card->level[0],0,QETH_MCL_LENGTH+1);
+		}
+
+		sprintf(dbf_text,"lvl:%s",card->level);
+		QETH_DBF_TEXT2(0,setup,dbf_text);
+
+		if (card->portname_required) {
+			sprintf(dbf_text,"%s",card->options.portname+1);
+			for (i=0;i<8;i++)
+				dbf_text[i]=(char)_ebcasc[(__u8)dbf_text[i]];
+			dbf_text[8]=0;
+			printk("qeth: Device 0x%X/0x%X/0x%X is a%s " \
+			       "card%s%s%s\n" \
+			       "with link type %s (portname: %s)\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),
+			       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));
+		}
+	}
+        
+ exit:
+	my_spin_unlock(&card->hardsetup_lock);
+        return result;
+}
+
+static int qeth_reinit_thread(void *param)
+{
+	qeth_card_t *card=(qeth_card_t*)param;
+	int already_registered;
+	int already_hardsetup;
+	int retry=QETH_RECOVERY_HARDSETUP_RETRY;
+	int result;
+	char dbf_text[15];
+	char name[15];
+
+	sprintf(dbf_text,"RINI%4x",card->irq0);
+	QETH_DBF_TEXT1(0,trace,dbf_text);
+
+	daemonize();
+
+	/* set a nice name ... */
+	sprintf(name, "qethrinid%04x", card->irq0);
+	strcpy(current->comm, name);
+	
+	if (atomic_read(&card->shutdown_phase)) goto out_wakeup;
+	down_interruptible(&card->reinit_thread_sem);
+	if (atomic_read(&card->shutdown_phase)) goto out_wakeup;
+
+	QETH_DBF_TEXT1(0,trace,"ri-gotin");
+	PRINT_STUPID("entering recovery (reinit) thread for device %s\n",
+		     card->dev_name);
+
+	atomic_set(&card->is_startlaned,0);
+	atomic_set(&card->is_softsetup,0);
+
+	my_read_lock(&list_lock);
+	if (!qeth_verify_card(card)) goto out;
+	QETH_DBF_TEXT1(0,trace,"ri-vrfd");
+
+	atomic_set(&card->write_busy,0);
+	qeth_set_dev_flag_norunning(card);
+	already_hardsetup=atomic_read(&card->is_hardsetup);
+	already_registered=atomic_read(&card->is_registered);
+	if (already_hardsetup) {
+		atomic_set(&card->is_hardsetup,0);
+
+		if (-1==my_spin_lock_nonbusy(card,&setup_lock)) goto out;
+		if (atomic_read(&card->shutdown_phase)) goto out_wakeup;
+
+		atomic_set(&card->escape_softsetup,1);
+		if (-1==my_spin_lock_nonbusy(card,&card->softsetup_lock)) {
+			atomic_set(&card->escape_softsetup,0);
+			goto out;
+		}
+		atomic_set(&card->escape_softsetup,0);
+		if (atomic_read(&card->shutdown_phase)) {
+			my_spin_unlock(&card->softsetup_lock);
+			goto out_wakeup;
+		}
+		if (!qeth_verify_card(card)) goto out;
+
+		if (already_registered)
+			netif_stop_queue(card->dev);
+
+		qeth_wait_nonbusy(QETH_QUIESCE_NETDEV_TIME);
+
+		atomic_set(&card->is_startlaned,0);
+
+		QETH_DBF_TEXT1(0,trace,"ri-frskb");
+		qeth_free_all_skbs(card);
+		do {
+			QETH_DBF_TEXT1(0,trace,"ri-hrdst");
+			result=qeth_hardsetup_card(card,1);
+		} while (result&&(retry--));
+
+		/* tries to remove old ips, that's paranoid, but ok */
+		qeth_clear_ifa4_list(&card->ip_new_state.ip_ifa);
+		qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm_ifa);
+
+#ifdef QETH_IPV6
+		qeth_clear_ifa6_list(&card->ip_new_state.ip6_ifa);
+		qeth_clear_ifamc_list(&card->ip_mc_new_state.ipm6_ifa);
+#endif /* QETH_IPV6 */
+
+		if (result) {
+			QETH_DBF_TEXT1(0,trace,"ri-nosuc");
+			printk("qeth: RECOVERY WAS NOT SUCCESSFUL ON %s " \
+		     	       "(devnos 0x%X/0x%X/0x%X), GIVING UP, " \
+	     		       "OUTGOING PACKETS WILL BE DISCARDED!\n",
+     			       card->dev_name,card->devno0,
+			       card->devno1,card->devno2);
+			/* early leave hard_start_xmit! */
+			atomic_set(&card->is_startlaned,0);
+			/* show status in /proc/qeth */
+			atomic_set(&card->is_gone,1);
+			qeth_wakeup_procfile();
+		} else {
+			QETH_DBF_TEXT1(0,trace,"ri-sftst");
+			qeth_softsetup_card(card,QETH_LOCK_ALREADY_HELD);
+			my_spin_unlock(&card->softsetup_lock);
+
+			if (!already_registered) {
+				QETH_DBF_TEXT1(0,trace,"ri-regcd");
+				qeth_register_netdev(card);
+			}
+			qeth_restore_dev_flag_state(card);
+			atomic_set(&card->is_gone,0);
+			netif_wake_queue(card->dev);
+			qeth_wakeup_procfile();
+		}
+		my_spin_unlock(&setup_lock);
+	}
+out:
+	atomic_set(&card->in_recovery,0);
+	my_read_unlock(&list_lock);
+	QETH_DBF_TEXT1(0,trace,"ri-leave");
+out_wakeup:
+	up(&card->reinit_thread_sem);
+	atomic_dec(&card->reinit_counter);
+
+	return 0;
+}
+
+static void qeth_fill_qeth_card_options(qeth_card_t *card)
+{
+        int i;
+
+	card->options.portname[0]=0;
+	for (i=1;i<9;i++)
+		card->options.portname[i]=_ascebc[' '];
+	strcpy(card->options.devname," ");
+	card->options.routing_type4=NO_ROUTER;
+#ifdef QETH_IPV6
+	card->options.routing_type6=NO_ROUTER;
+#endif /* QETH_IPV6 */
+	card->options.portno=0;
+	card->options.checksum_type=QETH_CHECKSUM_DEFAULT;
+	card->options.do_prio_queueing=0;
+	card->options.default_queue=QETH_DEFAULT_QUEUE;
+	card->options.inbound_buffer_count=DEFAULT_BUFFER_COUNT;
+	card->options.polltime=QETH_MAX_INPUT_THRESHOLD;
+	card->options.memusage=MEMUSAGE_DISCONTIG;
+	card->options.macaddr_mode=MACADDR_NONCANONICAL;
+	card->options.broadcast_mode=BROADCAST_ALLRINGS;
+	card->options.fake_broadcast=DONT_FAKE_BROADCAST;
+	card->options.ena_ipat=ENABLE_TAKEOVER;
+	card->options.add_hhlen=DEFAULT_ADD_HHLEN;
+	card->options.fake_ll=DONT_FAKE_LL;
+	card->options.async_iqd=SYNC_IQD;
+}
+
+static qeth_card_t *qeth_alloc_card(void)
+{
+        qeth_card_t *card;
+
+	QETH_DBF_TEXT3(0,trace,"alloccrd");
+	card=(qeth_card_t *)vmalloc(sizeof(qeth_card_t));
+	if (!card) goto exit_card;
+	memset(card,0,sizeof(qeth_card_t));
+	init_waitqueue_head(&card->wait_q);
+	init_waitqueue_head(&card->ioctl_wait_q);
+
+	qeth_fill_qeth_card_options(card);
+
+	card->dma_stuff=(qeth_dma_stuff_t*)kmalloc(sizeof(qeth_dma_stuff_t),
+						   GFP_DMA);
+	if (!card->dma_stuff) goto exit_dma;
+	memset(card->dma_stuff,0,sizeof(qeth_dma_stuff_t));
+
+	card->dma_stuff->recbuf=(char*)kmalloc(QETH_BUFSIZE,GFP_DMA);
+	if (!card->dma_stuff->recbuf) goto exit_dma1;
+	memset(card->dma_stuff->recbuf,0,QETH_BUFSIZE);
+
+	card->dma_stuff->sendbuf=(char*)kmalloc(QETH_BUFSIZE,GFP_DMA);
+	if (!card->dma_stuff->sendbuf) goto exit_dma2;
+	memset(card->dma_stuff->sendbuf,0,QETH_BUFSIZE);
+
+	card->dev=(struct net_device *)kmalloc(sizeof(struct net_device),
+                                              GFP_KERNEL);
+	if (!card->dev) goto exit_dev;
+	memset(card->dev,0,sizeof(struct net_device));
+
+	card->stats=(struct net_device_stats *)kmalloc(
+                sizeof(struct net_device_stats),GFP_KERNEL);
+	if (!card->stats) goto exit_stats;
+	memset(card->stats,0,sizeof(struct net_device_stats));
+
+	card->devstat0=(devstat_t *)kmalloc(sizeof(devstat_t),GFP_KERNEL);
+	if (!card->devstat0) goto exit_stats;
+	memset(card->devstat0,0,sizeof(devstat_t));
+
+	card->devstat1=(devstat_t *)kmalloc(sizeof(devstat_t),GFP_KERNEL);
+	if (!card->devstat1) {
+		kfree(card->devstat0);
+		goto exit_stats;
+	}
+	memset(card->devstat1,0,sizeof(devstat_t));
+
+	card->devstat2=(devstat_t *)kmalloc(sizeof(devstat_t),GFP_KERNEL);
+	if (!card->devstat2) {
+		kfree(card->devstat1);
+		kfree(card->devstat0);
+		goto exit_stats;
+	}
+	memset(card->devstat2,0,sizeof(devstat_t));
+
+	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);
+#ifdef QETH_VLAN
+	spin_lock_init(&card->vlan_lock);
+        card->vlangrp = NULL;
+#endif
+	card->unique_id=0;
+	sema_init(&card->reinit_thread_sem,0);
+	up(&card->reinit_thread_sem);
+
+        /* setup card stuff */
+        card->ip_current_state.ip_ifa=NULL;
+        card->ip_new_state.ip_ifa=NULL;
+        card->ip_mc_current_state.ipm_ifa=NULL;
+	card->ip_mc_new_state.ipm_ifa=NULL;
+
+#ifdef QETH_IPV6
+	card->ip_current_state.ip6_ifa=NULL;
+	card->ip_new_state.ip6_ifa=NULL;
+	card->ip_mc_current_state.ipm6_ifa=NULL;
+	card->ip_mc_new_state.ipm6_ifa=NULL;
+#endif /* QETH_IPV6 */
+
+        /* setup net_device stuff */
+	card->dev->priv=card;
+
+	strncpy(card->dev->name,card->dev_name,IFNAMSIZ);
+
+        /* setup net_device_stats stuff */
+        /* =nothing yet */
+
+        /* and return to the sender */
+        return card;
+
+        /* these are quick exits in case of failures of the kmallocs */
+exit_stats:
+        kfree(card->dev);
+exit_dev:
+	kfree(card->dma_stuff->sendbuf);
+exit_dma2:
+	kfree(card->dma_stuff->recbuf);
+exit_dma1:
+	kfree(card->dma_stuff);
+exit_dma:
+        kfree(card);
+exit_card:
+        return NULL;
+}
+
+static int qeth_init_ringbuffers1(qeth_card_t *card)
+{
+	int i,j;
+	char dbf_text[15];
+
+	sprintf(dbf_text,"irb1%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	for (i=0;i<card->no_queues;i++) {
+		card->outbound_ringbuffer[i]=
+			vmalloc(sizeof(qeth_ringbuffer_t));
+		if (!card->outbound_ringbuffer[i]) {
+			for (j=i-1;j>=0;j--) {
+				vfree(card->outbound_ringbuffer[j]);
+				card->outbound_ringbuffer[j]=NULL;
+			}
+			return -ENOMEM;
+		}
+		memset(card->outbound_ringbuffer[i],0,
+		       sizeof(qeth_ringbuffer_t));
+		for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
+			skb_queue_head_init(&card->outbound_ringbuffer[i]->
+					    ringbuf_element[j].skb_list);
+	}
+
+	return 0;
+}
+
+static int qeth_init_ringbuffers2(qeth_card_t *card)
+{
+	int i,j;
+	int failed=0;
+	char dbf_text[15];
+	int discont_mem,element_count;
+	long alloc_size;
+
+	sprintf(dbf_text,"irb2%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	discont_mem=(card->options.memusage==MEMUSAGE_DISCONTIG);
+	element_count=(discont_mem)?BUFFER_MAX_ELEMENTS:1;
+	alloc_size=(discont_mem)?PAGE_SIZE:BUFFER_SIZE;
+	if (discont_mem) {
+		for (i=0;i<card->options.inbound_buffer_count;i++) {
+			for (j=0;j<element_count;j++) {
+				card->inbound_buffer_pool_entry[i][j]=
+					kmalloc(alloc_size,GFP_KERNEL);
+				if (!card->inbound_buffer_pool_entry[i][j]) {
+					failed=1;
+					goto out;
+				}
+			}
+			card->inbound_buffer_pool_entry_used[i]=BUFFER_UNUSED;
+		}
+	} else {
+		for (i=0;i<card->options.inbound_buffer_count;i++) {
+			card->inbound_buffer_pool_entry[i][0]=
+				kmalloc(alloc_size,GFP_KERNEL);
+			if (!card->inbound_buffer_pool_entry[i][0]) failed=1;
+			for (j=1;j<element_count;j++)
+				card->inbound_buffer_pool_entry[i][j]=
+					card->inbound_buffer_pool_entry[i][0]+
+					PAGE_SIZE*j;
+			card->inbound_buffer_pool_entry_used[i]=BUFFER_UNUSED;
+		}
+	}
+		
+out:
+	if (failed) {
+		for (i=0;i<card->options.inbound_buffer_count;i++) {
+			for (j=0;j<QDIO_MAX_ELEMENTS_PER_BUFFER;j++) {
+				if (card->inbound_buffer_pool_entry[i][j]) {
+					if (j<element_count) kfree(card->
+						inbound_buffer_pool_entry
+						[i][j]);
+					card->inbound_buffer_pool_entry
+						[i][j]=NULL;
+				}
+			}
+		}
+		for (i=0;i<card->no_queues;i++) {
+			vfree(card->outbound_ringbuffer[i]);
+			card->outbound_ringbuffer[i]=NULL;
+		}
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&card->requeue_input_lock);
+
+	return 0;
+}
+
+/* also locked from outside (setup_lock) */
+static void qeth_insert_card_into_list(qeth_card_t *card)
+{
+	char dbf_text[15];
+
+	sprintf(dbf_text,"icil%4x",card->irq0);
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+
+	my_write_lock(&list_lock);
+        card->next=firstcard;
+        firstcard=card;
+	my_write_unlock(&list_lock);
+}
+
+static int qeth_determine_card_type(qeth_card_t *card)
+{
+        int i=0;
+	char dbf_text[15];
+
+        while (known_devices[i][4]) {
+                if ( (card->dev_type==known_devices[i][2]) &&
+                     (card->dev_model==known_devices[i][3]) ) {
+                        card->type=known_devices[i][4];
+			if (card->options.ena_ipat==ENABLE_TAKEOVER)
+				card->func_level=known_devices[i][6];
+			else
+				card->func_level=known_devices[i][7];
+                        card->no_queues=known_devices[i][8];
+			card->is_multicast_different=known_devices[i][9];
+			sprintf(dbf_text,"irq %4x",card->irq0);
+			QETH_DBF_TEXT2(0,setup,dbf_text);
+			sprintf(dbf_text,"ctyp%4x",card->type);
+			QETH_DBF_TEXT2(0,setup,dbf_text);
+                        return 0;
+                }
+                i++;
+        }
+        card->type=QETH_CARD_TYPE_UNKNOWN;
+	sprintf(dbf_text,"irq %4x",card->irq0);
+	QETH_DBF_TEXT2(0,setup,dbf_text);
+	sprintf(dbf_text,"ctypUNKN");
+	QETH_DBF_TEXT2(0,setup,dbf_text);
+	PRINT_ERR("unknown card type on irq x%x\n",card->irq0);
+	return -ENOENT;
+}
+
+static int qeth_getint(char *s,int longint)
+{
+        int cnt;
+        int hex;
+        int result;
+        char c;
+        
+        if (!s) return -1;
+        hex=((s[0]=='0')&&( (s[1]=='x') || (s[1]=='X') ))?1:0;
+        cnt=(hex)?2:0; /* start from the first real digit */
+        if (!(s[cnt])) return -1;
+        result=0;
+        while ((c=s[cnt++])) {
+                if (hex) {
+                        if (isxdigit(c)) result=result*16+qeth_getxdigit(c);
+                        else return -1;
+                } else {
+                        if (isdigit(c)) result=result*10+c-'0';
+			else return -1;
+                }
+                /* prevent overflow, 0xffff is enough for us */
+		if (longint) {
+			if (result>0xfffffff) return -1;
+		} else {
+			if (result>0xffff) return -1;
+		}
+        }
+        return result;
+}
+
+static int qeth_setvalue_if_possible(qeth_card_t *card,char *option,
+				     int parse_category,
+				     int *variable,int value)
+{
+	int *a_p=0; /* to shut the compiler up */
+	int routing_already_set_conflict=0;
+
+	a_p= &card->options.already_parsed[parse_category];
+
+	/* reject a general router statement, if router4 or router6
+	 * have already been set */
+	if (parse_category==PARSE_ROUTING_TYPE) {
+		if ( (card->options.already_parsed[PARSE_ROUTING_TYPE4]) ||
+		     (card->options.already_parsed[PARSE_ROUTING_TYPE6]) )
+			routing_already_set_conflict=1;
+	}
+
+	/* reject a router4 statement, if a general router statement
+	 * has already been set */
+	if (parse_category==PARSE_ROUTING_TYPE4) {
+		if ( (card->options.already_parsed[PARSE_ROUTING_TYPE4]) &&
+		     (card->options.already_parsed[PARSE_ROUTING_TYPE6]) )
+			routing_already_set_conflict=1;
+	}
+
+	/* reject a router6 statement, if a general router statement
+	 * has already been set */
+	if (parse_category==PARSE_ROUTING_TYPE6) {
+		if ( (card->options.already_parsed[PARSE_ROUTING_TYPE4]) &&
+		     (card->options.already_parsed[PARSE_ROUTING_TYPE6]) )
+			routing_already_set_conflict=1;
+	}
+
+	if ( (routing_already_set_conflict) ||
+	     ((parse_category!=-1) && (*a_p)) ) {
+                PRINT_ERR("parameter %s does not fit to previous " \
+			  "parameters\n",option);
+                return 0;
+        }
+        *a_p=1;
+        *variable=value;
+        return 1;
+}
+
+/* um... */
+#define doit1(a,b,c,d) \
+   if (!strcmp(option,a)) \
+     return qeth_setvalue_if_possible(card,option,b,((int*)&c),d)
+
+#define doit2(a,b,c,d,e,f) \
+   doit1(a,b,c,d) | qeth_setvalue_if_possible(card,option,-1,((int*)&e),f)
+
+/* returns 0, if option could notr be parsed alright */
+static int qeth_parse_option(qeth_card_t *card,char *option)
+{
+	int i;
+	int *a_p;
+
+#ifdef QETH_IPV6
+	doit2("no_router",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,NO_ROUTER,
+	      card->options.routing_type6,NO_ROUTER);
+	doit2("primary_router",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,PRIMARY_ROUTER,
+	      card->options.routing_type6,PRIMARY_ROUTER);
+	doit2("secondary_router",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,SECONDARY_ROUTER,
+	      card->options.routing_type6,SECONDARY_ROUTER);
+	doit2("multicast_router",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,MULTICAST_ROUTER,
+	      card->options.routing_type6,MULTICAST_ROUTER);
+	doit2("primary_connector",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,PRIMARY_CONNECTOR,
+	      card->options.routing_type6,PRIMARY_CONNECTOR);
+	doit2("secondary_connector",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,SECONDARY_CONNECTOR,
+	      card->options.routing_type6,SECONDARY_CONNECTOR);
+#else /* QETH_IPV6 */
+	doit1("no_router",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,NO_ROUTER);
+	doit1("primary_router",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,PRIMARY_ROUTER);
+	doit1("secondary_router",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,SECONDARY_ROUTER);
+	doit1("multicast_router",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,MULTICAST_ROUTER);
+	doit1("primary_connector",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,PRIMARY_CONNECTOR);
+	doit1("secondary_connector",PARSE_ROUTING_TYPE,
+	      card->options.routing_type4,SECONDARY_CONNECTOR);
+#endif /* QETH_IPV6 */
+	
+	doit1("no_router4",PARSE_ROUTING_TYPE4,
+	      card->options.routing_type4,NO_ROUTER);
+	doit1("primary_router4",PARSE_ROUTING_TYPE4,
+	      card->options.routing_type4,PRIMARY_ROUTER);
+	doit1("secondary_router4",PARSE_ROUTING_TYPE4,
+	      card->options.routing_type4,SECONDARY_ROUTER);
+	doit1("multicast_router4",PARSE_ROUTING_TYPE4,
+	      card->options.routing_type4,MULTICAST_ROUTER);
+	doit1("primary_connector4",PARSE_ROUTING_TYPE4,
+	      card->options.routing_type4,PRIMARY_CONNECTOR);
+	doit1("secondary_connector4",PARSE_ROUTING_TYPE4,
+	      card->options.routing_type4,SECONDARY_CONNECTOR);
+	
+#ifdef QETH_IPV6
+	doit1("no_router6",PARSE_ROUTING_TYPE6,
+	      card->options.routing_type6,NO_ROUTER);
+	doit1("primary_router6",PARSE_ROUTING_TYPE6,
+	      card->options.routing_type6,PRIMARY_ROUTER);
+	doit1("secondary_router6",PARSE_ROUTING_TYPE6,
+	      card->options.routing_type6,SECONDARY_ROUTER);
+	doit1("multicast_router6",PARSE_ROUTING_TYPE6,
+	      card->options.routing_type6,MULTICAST_ROUTER);
+	doit1("primary_connector6",PARSE_ROUTING_TYPE6,
+	      card->options.routing_type6,PRIMARY_CONNECTOR);
+	doit1("secondary_connector6",PARSE_ROUTING_TYPE6,
+	      card->options.routing_type6,SECONDARY_CONNECTOR);
+#endif /* QETH_IPV6 */
+	
+	doit1("sw_checksumming",PARSE_CHECKSUMMING,
+	      card->options.checksum_type,SW_CHECKSUMMING);
+	doit1("hw_checksumming",PARSE_CHECKSUMMING,
+	      card->options.checksum_type,HW_CHECKSUMMING);
+	doit1("no_checksumming",PARSE_CHECKSUMMING,
+	      card->options.checksum_type,NO_CHECKSUMMING);
+	
+	doit1("prio_queueing_prec",PARSE_PRIO_QUEUEING,
+	      card->options.do_prio_queueing,PRIO_QUEUEING_PREC);
+	doit1("prio_queueing_tos",PARSE_PRIO_QUEUEING,
+	      card->options.do_prio_queueing,PRIO_QUEUEING_TOS);
+	
+	doit1("mem_discontig",PARSE_MEMUSAGE,
+	      card->options.memusage,MEMUSAGE_DISCONTIG);
+	doit1("mem_contig",PARSE_MEMUSAGE,
+	      card->options.memusage,MEMUSAGE_CONTIG);
+	
+	doit1("broadcast_allrings",PARSE_BROADCAST_MODE,
+	      card->options.broadcast_mode,BROADCAST_ALLRINGS);
+	doit1("broadcast_local",PARSE_BROADCAST_MODE,
+	      card->options.broadcast_mode,BROADCAST_LOCAL);
+	
+	doit1("macaddr_noncanon",PARSE_MACADDR_MODE,
+	      card->options.macaddr_mode,MACADDR_NONCANONICAL);
+	doit1("macaddr_canon",PARSE_MACADDR_MODE,
+	      card->options.macaddr_mode,MACADDR_CANONICAL);
+	
+	doit1("enable_takeover",PARSE_ENA_IPAT,
+	      card->options.ena_ipat,ENABLE_TAKEOVER);
+	doit1("disable_takeover",PARSE_ENA_IPAT,
+	      card->options.ena_ipat,DISABLE_TAKEOVER);
+	
+	doit1("fake_broadcast",PARSE_FAKE_BROADCAST,
+	      card->options.fake_broadcast,FAKE_BROADCAST);
+	doit1("dont_fake_broadcast",PARSE_FAKE_BROADCAST,
+	      card->options.fake_broadcast,DONT_FAKE_BROADCAST);
+	
+	doit1("fake_ll",PARSE_FAKE_LL,
+	      card->options.fake_ll,FAKE_LL);
+	doit1("dont_fake_ll",PARSE_FAKE_LL,
+	      card->options.fake_ll,DONT_FAKE_LL);
+	
+	doit1("sync_hsi",PARSE_ASYNC_IQD,
+	      card->options.async_iqd,SYNC_IQD);
+	doit1("async_hsi",PARSE_ASYNC_IQD,
+	      card->options.async_iqd,ASYNC_IQD);
+	
+	doit2("no_prio_queueing:0",PARSE_PRIO_QUEUEING,
+	      card->options.do_prio_queueing,NO_PRIO_QUEUEING,
+	      card->options.default_queue,0);
+	doit2("no_prio_queueing:1",PARSE_PRIO_QUEUEING,
+	      card->options.do_prio_queueing,NO_PRIO_QUEUEING,
+	      card->options.default_queue,1);
+	doit2("no_prio_queueing:2",PARSE_PRIO_QUEUEING,
+	      card->options.do_prio_queueing,NO_PRIO_QUEUEING,
+	      card->options.default_queue,2);
+	doit2("no_prio_queueing:3",PARSE_PRIO_QUEUEING,
+	      card->options.do_prio_queueing,NO_PRIO_QUEUEING,
+	      card->options.default_queue,3);
+	doit2("no_prio_queueing",PARSE_PRIO_QUEUEING,
+	      card->options.do_prio_queueing,NO_PRIO_QUEUEING,
+	      card->options.default_queue,QETH_DEFAULT_QUEUE);
+	
+	if (!strncmp(option,"polltime:",9)) {
+		i=qeth_getint(option+9,1);
+		if (i==-1) {
+			PRINT_ERR("parameter %s -- does not contain " \
+				  "a valid number.\n",option);
+			return 0;
+		}
+		return qeth_setvalue_if_possible(card,option,PARSE_POLLTIME,
+						 &card->options.polltime,i);
+	}
+	
+	if (!strncmp(option,"port:",5)) {
+		i=qeth_getint(option+5,0);
+		if (i==-1) {
+			PRINT_ERR("parameter %s -- does not contain " \
+				  "a valid number.\n",option);
+			return 0;
+		}
+		if ( (i<0) || (i>MAX_PORTNO) ) {
+			PRINT_ERR("parameter %s -- out of range\n",
+				  option);
+			return 0;
+		}
+		return qeth_setvalue_if_possible(card,option,PARSE_PORTNO,
+						 &card->options.portno,i);
+	}
+	
+	if (!strncmp(option,"add_hhlen:",10)) {
+		i=qeth_getint(option+10,0);
+		if (i==-1) {
+			PRINT_ERR("parameter %s -- does not contain " \
+				  "a valid number.\n",option);
+			return 0;
+		}
+		if ( (i<0) || (i>MAX_ADD_HHLEN) ) {
+			PRINT_ERR("parameter %s -- out of range\n",
+				  option);
+			return 0;
+		}
+		return qeth_setvalue_if_possible(card,option,PARSE_ADD_HHLEN,
+						 &card->options.add_hhlen,i);
+	}
+	
+	if (!strncmp(option,"portname:",9)) {
+		a_p=&card->options.already_parsed[PARSE_PORTNAME];
+		if (*a_p) {
+			PRINT_ERR("parameter %s does not fit to " \
+				  "previous parameters\n",option);
+			return 0;
+		}
+		if ((strlen(option)-9)>8) {
+			PRINT_ERR("parameter %s -- too long\n",option);
+			return 0;
+		}
+		*a_p=1;
+		card->options.portname[0]=strlen(option)-9;
+		/* for beauty reasons: */
+		for (i=1;i<9;i++)
+			card->options.portname[i]=' ';
+		strcpy(card->options.portname+1,option+9);
+		for (i=1;i<9;i++)
+			card->options.portname[i]=
+				_ascebc[(unsigned char)
+					card->options.portname[i]];
+		return 1;
+	}
+
+	PRINT_ERR("unknown parameter: %s\n",option);
+
+        return 0;
+}
+
+static void qeth_detach_handler(int irq,int status)
+{
+        qeth_card_t *card;
+	int remove_method;
+	char dbf_text[15];
+
+	if (irq>=NR_IRQS) {
+		irq-=NR_IRQS;
+		remove_method=QETH_REMOVE_CARD_PROPER;
+	} else {
+		remove_method=QETH_REMOVE_CARD_QUICK;
+	}
+
+	QETH_DBF_TEXT1(0,trace,"detchhnd");
+	sprintf(dbf_text,"%4x%4x",irq,status);
+	QETH_DBF_TEXT1(0,trace,dbf_text);
+
+	/* try to get the lock, if we didn't get it (hold by recovery),
+	 * give up initiative to enable others to release the lock */
+        my_spin_lock_nonbusy(NULL,&setup_lock);
+
+	if ((card=qeth_get_card_by_irq(irq))) {
+		qeth_remove_card(card,remove_method);
+        }
+	qeth_wakeup_procfile();
+        my_spin_unlock(&setup_lock);
+}
+
+static void qeth_chandev_do_unregister_device(struct net_device *dev,
+					      int quick) 
+{
+	qeth_card_t *card;
+	
+	card=(qeth_card_t *)dev->priv;
+	
+	if (quick) {
+		qeth_detach_handler(card->irq0,0x1234);
+	} else {
+		qeth_detach_handler(NR_IRQS+card->irq0,0x1234);
+	}
+}
+
+static void qeth_chandev_unregister_device(struct net_device *dev)
+{
+	qeth_chandev_do_unregister_device(dev, 0);
+}
+
+static void qeth_chandev_parse_options(qeth_card_t *card,char *parmstring)
+{
+	/* parse options coming from the chandev layer */
+	char *optionstr;
+	char *tmp;
+
+	QETH_DBF_HEX2(0,misc,parmstring,
+		      qeth_min(QETH_DBF_MISC_LEN,strlen(parmstring)));
+	tmp = kmalloc ((strlen(parmstring) + 1) * sizeof (char), GFP_KERNEL);
+	if (tmp) {
+		memset (tmp, 0, strlen(parmstring) + 1);
+		memcpy (tmp, parmstring, strlen(parmstring) + 1);
+		do {
+			char *end;
+			int len;
+			end = strchr (tmp, ',');
+			if (end == NULL) {
+				len = strlen (tmp) + 1;
+			} else {
+				len = (long) end - (long) tmp + 1;
+			*end = '\0';
+			end++;
+			}
+
+			if (len>1) {
+				optionstr = kmalloc (len * sizeof (char),
+						     GFP_KERNEL);
+				if (optionstr) {
+					memset(optionstr,0,len*sizeof(char));
+					memcpy(optionstr,tmp,len*sizeof(char));
+					
+					qeth_parse_option(card,optionstr);
+					kfree(optionstr);
+					tmp = end;
+				} else {
+					PRINT_ERR("Cannot allocate memory " \
+						  "for option parsing!\n");
+					break;
+				}
+			}
+		} while (tmp != NULL && *tmp != '\0');
+	} else {
+		PRINT_ERR("Cannot allocate memory " \
+			  "for option parsing!\n");
+	}
+}
+
+static int qeth_get_bufcnt_from_memamnt(qeth_card_t *card,__s32 *memamnt) 
+{
+	int cnt = 0;
+	int bufsize = BUFFER_SIZE;
+
+	if (bufsize == 24576)
+		bufsize = 32768;
+	if (bufsize == 40960) 
+		bufsize = 65536;
+	cnt = (*memamnt)*1024 / bufsize;
+	cnt = (cnt<BUFCNT_MIN)?BUFCNT_MIN:((cnt>BUFCNT_MAX)?BUFCNT_MAX:cnt);
+	(*memamnt) = cnt * bufsize/1024;
+
+	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;
+        int irq1,irq2;
+        unsigned int irq;
+	int nr;
+	__u8 mask;
+	int read_chpid=-1,write_chpid=-2,data_chpid=-3; /* so that it will
+							   fail, if getting
+							   the chpids fails */
+        qeth_card_t *card;
+        int success = 0;
+	char dbf_text[15];
+	chandev_subchannel_info temp;
+
+	QETH_DBF_TEXT2(0,trace,"athandlr");
+	sprintf(dbf_text,"irq:%4x",irq_to_scan);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+
+        my_spin_lock_nonbusy(NULL,&setup_lock);
+
+	for (nr=0; nr<8; nr++) {
+		mask = 0x80 >> nr;
+		if (probeinfo->read.pim & mask) {
+			read_chpid=probeinfo->read.chpid[nr];
+			/* we take the first chpid -- well, there's
+			 * usually only one... */
+			break;
+		}
+	}
+	for (nr=0; nr<8; nr++) {
+		mask = 0x80 >> nr;
+		if (probeinfo->write.pim & mask) {
+			write_chpid=probeinfo->write.chpid[nr];
+			/* we take the first chpid -- well, there's
+			 * usually only one... */
+			break;
+		}
+	}
+	for (nr=0; nr<8; nr++) {
+		mask = 0x80 >> nr;
+		if (probeinfo->data.pim & mask) {
+			data_chpid=probeinfo->data.chpid[nr];
+			/* we take the first chpid -- well, there's
+			 * usually only one... */
+			break;
+		}
+	}
+	if ((read_chpid!=write_chpid)||(read_chpid!=data_chpid)) {
+		PRINT_ERR("devices are not on the same CHPID!\n");
+		goto endloop;
+	}
+
+	/* Try to reorder the devices, if neccessary */
+	if (probeinfo->read.dev_model == 0x05)
+		/* No odd/even restr. for IQD */
+		goto correct_order;
+	if ((probeinfo->read.devno %2 == 0) &&
+	    (probeinfo->write.devno == probeinfo->read.devno + 1))
+		goto correct_order;
+	if ((probeinfo->write.devno %2 == 0) &&
+	    (probeinfo->data.devno == probeinfo->write.devno + 1)) {
+		temp = probeinfo->read;
+		probeinfo->read = probeinfo->write;
+		probeinfo->write = probeinfo->data;
+		probeinfo->data = temp;
+		goto correct_order;
+	}
+	if ((probeinfo->write.devno %2 == 0) &&
+	    (probeinfo->read.devno == probeinfo->write.devno + 1)) {
+		temp = probeinfo->read;
+		probeinfo->read = probeinfo->write;
+		probeinfo->write = temp;
+		goto correct_order;
+	}
+	if ((probeinfo->read.devno %2 == 0) &&
+	    (probeinfo->data.devno == probeinfo->read.devno + 1)) {
+		temp = probeinfo->write;
+		probeinfo->write = probeinfo->data;
+		probeinfo->data = temp;
+		goto correct_order;
+	}
+	if ((probeinfo->data.devno %2 == 0) &&
+	    (probeinfo->write.devno == probeinfo->data.devno + 1)) {
+		temp = probeinfo->read;
+		probeinfo->read = probeinfo->data;
+		probeinfo->data = temp;
+		goto correct_order;
+	}
+	if ((probeinfo->data.devno %2 == 0) &&
+	    (probeinfo->read.devno == probeinfo->data.devno + 1)) {
+		temp = probeinfo->read;
+		probeinfo->read = probeinfo->data;
+		probeinfo->data = probeinfo->write;
+		probeinfo->write = temp;
+		goto correct_order;
+	}
+	PRINT_ERR("Failed to reorder devices %04x,%04x,%04x; "
+		  "please check your configuration\n",
+		  probeinfo->read.devno, probeinfo->write.devno,
+		  probeinfo->data.devno);
+	goto endloop;
+	
+correct_order:
+	irq = probeinfo->read.irq;
+	irq1 = probeinfo->write.irq;
+	irq2 = probeinfo->data.irq;	
+
+	card=qeth_alloc_card();
+	if (card==NULL) {
+		QETH_DBF_TEXT2(0,trace,"nocrdmem");
+		PRINT_ERR("memory structures could not be allocated\n");
+		goto endloop;
+	}
+	card->chpid=read_chpid;
+
+	if (probeinfo->port_protocol_no != -1 )
+		card->options.portno = probeinfo->port_protocol_no;
+	else
+		card->options.portno = 0;
+
+	qeth_chandev_parse_options(card,probeinfo->parmstr);
+
+	card->has_irq=0;
+	card->irq0=irq;
+	card->irq1=irq1;
+	card->irq2=irq2;
+	card->devno0=probeinfo->read.devno;
+	card->devno1=probeinfo->write.devno;
+	card->devno2=probeinfo->data.devno;
+	card->dev_type=probeinfo->read.dev_type;
+	card->dev_model=probeinfo->read.dev_model;
+	atomic_set(&card->is_gone,0);
+	atomic_set(&card->rt4fld,0);
+#ifdef QETH_IPV6
+	atomic_set(&card->rt6fld,0);
+#endif /* QETH_IPV6 */
+	
+	sprintf(dbf_text,"atch%4x",card->irq0);
+	QETH_DBF_TEXT1(0,setup,dbf_text);
+	QETH_DBF_HEX1(0,setup,&card,sizeof(void*));
+	QETH_DBF_HEX1(0,setup,&card->dev,sizeof(void*));
+	QETH_DBF_HEX1(0,setup,&card->stats,sizeof(void*));
+	QETH_DBF_HEX1(0,setup,&card->devstat0,sizeof(void*));
+	QETH_DBF_HEX1(0,setup,&card->devstat1,sizeof(void*));
+	QETH_DBF_HEX1(0,setup,&card->devstat2,sizeof(void*));
+	
+	QETH_DBF_HEX2(0,misc,&card->options,QETH_DBF_MISC_LEN);
+
+	if (qeth_determine_card_type(card)) {
+		qeth_free_card(card);
+		goto endloop;
+	}
+	
+	qeth_correct_routing_status(card);
+	qeth_insert_card_into_list(card);
+	
+	QETH_DBF_TEXT3(0,trace,"request0");
+	/* 0 is irqflags. what is SA_SAMPLE_RANDOM? */
+	result=chandev_request_irq(irq,(void*)
+				   qeth_interrupt_handler_read,
+				   0,QETH_NAME,card->devstat0);
+	if (result) goto attach_error;
+	card->has_irq++;
+
+	QETH_DBF_TEXT3(0,trace,"request1");
+	result=chandev_request_irq(irq1,(void*)
+				   qeth_interrupt_handler_write,
+				   0,QETH_NAME,card->devstat1);
+	if (result) goto attach_error;
+	card->has_irq++;
+
+	QETH_DBF_TEXT3(0,trace,"request2");
+	result=chandev_request_irq(irq2,(void*)
+				   qeth_interrupt_handler_qdio,
+				   0,QETH_NAME,card->devstat2);
+	if (result) goto attach_error;
+	card->has_irq++;
+	
+	printk("qeth: Trying to use card with devnos 0x%X/0x%X/0x%X\n",
+	       card->devno0,card->devno1,card->devno2);
+	
+	result=qeth_init_ringbuffers1(card);
+	if (result) goto attach_error;
+	
+	result=qeth_hardsetup_card(card,0);
+	if (result) goto attach_error;
+	if (probeinfo->memory_usage_in_k != 0) {
+		card->options.inbound_buffer_count=
+			qeth_get_bufcnt_from_memamnt
+			(card,&probeinfo->memory_usage_in_k);
+		card->options.memory_usage_in_k=
+			probeinfo->memory_usage_in_k;
+	} else {
+		/* sick... */
+		probeinfo->memory_usage_in_k=
+			-card->options.inbound_buffer_count*
+			BUFFER_SIZE/1024;
+	}
+	result=qeth_init_ringbuffers2(card);
+	if (result) goto attach_error;
+	
+	success = 1;
+	goto endloop;
+
+ attach_error:
+	sprintf(dbf_text,"ATER%4x",card->irq0);
+	QETH_DBF_TEXT2(0,trace,dbf_text);
+	switch (result) {
+	case 0:
+		break;
+	case -EINVAL:
+		PRINT_ERR("oops... invalid parameter.\n");
+                        break;
+	case -EBUSY:
+		PRINT_WARN("Device is busy!\n");
+		break;
+	case -ENODEV:
+		PRINT_WARN("Device became not operational.\n");
+		break;
+	case -ENOMEM:
+		PRINT_ERR("Not enough kernel memory for operation.\n");
+		break;
+	case -EIO:
+		PRINT_ERR("There were problems in hard-setting up " \
+			  "the card.\n");
+		break;
+	case -ETIME:
+		PRINT_WARN("Timeout on initializing the card.\n");
+		break;
+	default:
+		PRINT_ERR("Unknown error %d in attach_handler.\n",
+			  result);
+	}
+	if (result) {
+		qeth_remove_card(card,QETH_REMOVE_CARD_PROPER);
+		
+		qeth_remove_card_from_list(card);
+		QETH_DBF_TEXT4(0,trace,"freecard");
+		qeth_free_card(card);
+	}
+ endloop:
+
+	QETH_DBF_TEXT3(0,trace,"leftloop");
+	
+        if (!success) {
+		QETH_DBF_TEXT2(0,trace,"noaddcrd");
+
+		/* we want to return which problem we had */
+		result=result?result:-ENODEV;
+                goto exit;
+        }
+
+    
+ exit:
+        my_spin_unlock(&setup_lock);
+	
+        return result;
+}
+
+static void qeth_chandev_msck_notfunc(struct net_device *device, 
+       				      int msck_irq, 
+       				      chandev_msck_status prevstatus,
+				      chandev_msck_status newstatus )
+{
+ 
+	if (!(device->priv))
+		return;
+	
+	if ((prevstatus != chandev_status_good) ||
+	    (prevstatus != chandev_status_all_chans_good)) {
+		if ((newstatus == chandev_status_good) ||
+		    (newstatus == chandev_status_all_chans_good)) {
+			qeth_card_t *card = (qeth_card_t *)device->priv;
+
+			atomic_set(&card->problem,PROBLEM_MACHINE_CHECK);
+			atomic_set(&card->write_busy,0);
+			qeth_schedule_recovery(card);
+		}
+	}
+	if ((newstatus == chandev_status_gone) ||
+	    (newstatus == chandev_status_no_path) ||
+	    (newstatus == chandev_status_not_oper)) {
+		qeth_card_t *card = (qeth_card_t *)device->priv;
+
+		my_read_lock(&list_lock);
+		if (qeth_verify_card(card)) {
+			atomic_set(&card->is_startlaned,0);
+			qeth_set_dev_flag_norunning(card);
+			/*
+			 * Unfortunately, the chandev layer does not provide 
+			 * a possibility to unregister a single device. So
+			 * we mark the card as "gone" to avoid internal
+			 * mishandling.
+			 */
+			atomic_set(&card->is_gone,1);
+			/* means, we prevent looping in
+			 * qeth_send_control_data */
+			atomic_set(&card->write_busy,0);
+			qeth_wakeup_procfile();
+		}
+		my_read_unlock(&list_lock);
+	}
+}
+
+struct net_device *qeth_chandev_init_netdev(struct net_device *dev,
+					   int sizeof_priv)
+{
+	
+	qeth_card_t *card = NULL;
+	int result; 
+	char dbf_text[15];	
+	
+	if (!dev) {
+		PRINT_ERR("qeth_chandev_init_netdev called with no device!\n");
+		goto out;
+	}
+
+	card = (qeth_card_t *)dev->priv;
+	strcpy(card->dev_name,dev->name);
+	result=qeth_register_netdev(card);
+	if (result) {
+		PRINT_ALL("         register_netdev %s -- rc=%i\n",
+			  ((qeth_card_t*)firstcard->dev->priv)->
+			  dev_name,result);
+		sprintf(dbf_text,"rgnd%4x",(__u16)result);
+		QETH_DBF_TEXT2(1,trace,dbf_text);
+		atomic_set(&card->is_registered,0);
+		goto out;
+	}
+	strcpy(card->dev_name,dev->name);
+	atomic_set(&card->write_busy,0);
+        atomic_set(&card->is_registered,1);
+
+        result=qeth_softsetup_card(card,QETH_WAIT_FOR_LOCK);
+
+	if (!result) {
+		qeth_init_input_buffers(card);
+	} else {
+		QETH_DBF_TEXT2(0,trace,"SSFAILED");
+		PRINT_WARN("soft-setup of card failed!\n");
+	}
+
+	INIT_LIST_HEAD(&card->tqueue_sst.list);
+	card->tqueue_sst.routine=qeth_softsetup_thread_starter;
+	card->tqueue_sst.data=card;
+	card->tqueue_sst.sync=0;
+	schedule_task(&card->tqueue_sst);
+ out:
+	qeth_wakeup_procfile();
+	return dev;
+
+}
+
+static int qeth_probe(chandev_probeinfo *probeinfo)
+{
+	int result;
+	struct net_device *pdevice = NULL;
+	int sizeof_priv;
+	qeth_card_t *card;
+	char *basename = NULL;
+
+	result = qeth_attach_handler(probeinfo->read.irq, probeinfo);
+	if (result)
+		return result;
+
+	sizeof_priv = sizeof(qeth_card_t);
+	card = qeth_get_card_by_irq(probeinfo->read.irq); 
+
+	pdevice = card->dev; 
+	basename = (char *)qeth_get_dev_basename(card->type, card->link_type);
+
+	pdevice->irq=card->irq0;
+
+	if (probeinfo->memory_usage_in_k>=0)
+		probeinfo->memory_usage_in_k=
+			-card->options.memory_usage_in_k;
+	pdevice = chandev_initnetdevice(probeinfo,
+					card->options.portno,
+					pdevice,
+					sizeof_priv,
+					basename,
+					qeth_chandev_init_netdev,
+					qeth_chandev_unregister_device);
+	if (pdevice) {
+		return 0;
+	} else {
+		qeth_remove_card(card,QETH_REMOVE_CARD_PROPER);
+		qeth_remove_card_from_list(card);
+		QETH_DBF_TEXT4(0,trace,"freecard");
+		qeth_free_card(card);
+		return -ENODEV;
+	}
+			
+}
+
+static int qeth_dev_event(struct notifier_block *this,
+			  unsigned long event,void *ptr)
+{
+	qeth_card_t *card;
+	struct net_device *dev = (struct net_device *)ptr;
+	char dbf_text[15];
+
+	sprintf(dbf_text,"devevent");
+	QETH_DBF_TEXT3(0,trace,dbf_text);
+	QETH_DBF_HEX3(0,trace,&event,sizeof(unsigned long));
+	QETH_DBF_HEX3(0,trace,&dev,sizeof(void*));
+
+#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_save_dev_flag_state(card);
+		switch (event) {
+		default:
+			qeth_start_softsetup_thread(card);
+			break;
+		}
+	}
+
+	return NOTIFY_DONE;
+}
+
+static int qeth_ip_event(struct notifier_block *this,
+			 unsigned long event,void *ptr)
+{
+	qeth_card_t *card;
+	struct in_ifaddr *ifa=(struct in_ifaddr *)ptr;
+	struct net_device *dev = ifa->ifa_dev->dev;
+	char dbf_text[15];
+
+	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);
+
+#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;
+}
+
+#ifdef QETH_IPV6
+static int qeth_ip6_event(struct notifier_block *this,
+		 	  unsigned long event,void *ptr)
+{
+	qeth_card_t *card;
+	struct inet6_ifaddr *ifa=(struct inet6_ifaddr *)ptr;
+	struct net_device *dev = ifa->idev->dev;
+	char dbf_text[15];
+
+	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);
+
+#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_reboot_event(struct notifier_block *this,
+   			     unsigned long event,void *ptr)
+{
+	qeth_card_t *card;
+
+	my_read_lock(&list_lock);
+	if (firstcard) {
+		card = firstcard;
+clear_another_one:
+		if (card->has_irq) {
+			if (card->type==QETH_CARD_TYPE_IQD) {
+				halt_IO(card->irq2,0,0);
+				clear_IO(card->irq0,0,0);
+				clear_IO(card->irq1,0,0);
+				clear_IO(card->irq2,0,0);
+			} else {
+				clear_IO(card->irq2,0,0);
+				clear_IO(card->irq0,0,0);
+				clear_IO(card->irq1,0,0);
+			}
+		}
+		if (card->next) {
+			card = card->next;
+			goto clear_another_one;
+		}
+	} 
+	my_read_unlock(&list_lock);
+
+	return 0;
+}
+
+static struct notifier_block qeth_dev_notifier = {
+        qeth_dev_event,
+        0
+};
+
+static struct notifier_block qeth_ip_notifier = {
+        qeth_ip_event,
+        0
+};
+
+#ifdef QETH_IPV6
+static struct notifier_block qeth_ip6_notifier = {
+        qeth_ip6_event,
+        0
+};
+#endif /* QETH_IPV6 */
+
+static struct notifier_block qeth_reboot_notifier = {
+        qeth_reboot_event,
+        0
+};
+
+static void qeth_register_notifiers(void)
+{
+	int r;
+
+	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);
+#ifdef QETH_IPV6
+	r=register_inet6addr_notifier(&qeth_ip6_notifier);
+#endif /* QETH_IPV6 */
+	r=register_reboot_notifier(&qeth_reboot_notifier);
+}
+
+#ifdef MODULE
+static void qeth_unregister_notifiers(void)
+{
+	int r;
+
+	QETH_DBF_TEXT5(0,trace,"unregnot");
+	r=unregister_netdevice_notifier(&qeth_dev_notifier);
+	r=unregister_inetaddr_notifier(&qeth_ip_notifier);
+#ifdef QETH_IPV6
+	r=unregister_inet6addr_notifier(&qeth_ip6_notifier);
+#endif /* QETH_IPV6 */
+	r=unregister_reboot_notifier(&qeth_reboot_notifier);
+}
+#endif /* MODULE */
+
+static int qeth_procfile_open(struct inode *inode, struct file *file)
+{
+	int length=0;
+	qeth_card_t *card;
+	char checksum_str[5],queueing_str[14],router_str[8],bufsize_str[4];
+	char *buffer;
+	int rc=0;
+	int size;
+	tempinfo_t *info;
+
+	info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
+	if (info == NULL) {
+		PRINT_WARN("No memory available for data\n");
+		return -ENOMEM;
+	} else {
+		file->private_data = (void *) info;
+	}
+
+	/* lock all the stuff */
+	my_read_lock(&list_lock);
+	card=firstcard;
+	size=200; /* 2 lines plus some sanity space */
+	while (card) {
+		size+=90; /* if device name is > 10 chars, (should never
+			     happen...), we'll need that */
+		card=card->next;
+	}
+
+	buffer=info->data = (char *) vmalloc (size);
+	if (info->data == NULL) {
+		PRINT_WARN("No memory available for data\n");
+		vfree (info);
+		rc=-ENOMEM;
+		goto out;
+	}
+	
+	QETH_DBF_TEXT2(0,trace,"procread");
+	length+=sprintf(buffer+length,
+			"devnos (hex) CHPID     " \
+			"device     cardtype port chksum prio-q'ing " \
+			"rtr fsz C cnt\n");
+	length+=sprintf(buffer+length,
+			"-------------- --- ----" \
+			"------ -------------- --     -- ---------- " \
+			"--- --- - ---\n");
+	card=firstcard;
+	while (card) {
+		strcpy(checksum_str,
+		       (card->options.checksum_type==SW_CHECKSUMMING)?"SW":
+		       (card->options.checksum_type==HW_CHECKSUMMING)?"HW":
+		       "no");
+		if (card->options.do_prio_queueing==NO_PRIO_QUEUEING) {
+			sprintf(queueing_str,"always_q_%i",
+				card->options.default_queue);
+		} else {
+			strcpy(queueing_str,(card->options.do_prio_queueing
+					     ==PRIO_QUEUEING_PREC)?"by_prec.":
+			       "by_ToS");
+		}
+
+#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");
+#else /* QETH_IPV6 */
+		if (atomic_read(&card->rt4fld))
+			strcpy(router_str, "no");
+#endif /* QETH_IPV6 */		
+		else if ( ((card->options.routing_type4&ROUTER_MASK)==
+		      PRIMARY_ROUTER) 
+#ifdef QETH_IPV6
+		     &&
+		     ((card->options.routing_type6&ROUTER_MASK)==
+		      PRIMARY_ROUTER) 
+#endif /* QETH_IPV6 */
+		     ) {
+			strcpy(router_str,"pri");
+		} else
+		if ( ((card->options.routing_type4&ROUTER_MASK)==
+		      SECONDARY_ROUTER) 
+#ifdef QETH_IPV6
+		     &&
+		     ((card->options.routing_type6&ROUTER_MASK)==
+		      SECONDARY_ROUTER) 
+#endif /* QETH_IPV6 */
+		     ) {
+			strcpy(router_str,"sec");
+		} else
+		if ( ((card->options.routing_type4&ROUTER_MASK)==
+		      MULTICAST_ROUTER) 
+#ifdef QETH_IPV6
+		     &&
+		     ((card->options.routing_type6&ROUTER_MASK)==
+		      MULTICAST_ROUTER) 
+#endif /* QETH_IPV6 */
+		     ) {
+			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) 
+#endif /* QETH_IPV6 */
+		     ) {
+			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) 
+#endif /* QETH_IPV6 */
+		     ) {
+			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) 
+#endif /* QETH_IPV6 */
+		     ) {
+			strcpy(router_str,"no");
+		} else {
+			strcpy(router_str,"mix");
+		}
+		strcpy(bufsize_str,
+		       (BUFFER_SIZE==16384)?"16k":
+		       (BUFFER_SIZE==24576)?"24k":
+		       (BUFFER_SIZE==32768)?"32k":
+		       (BUFFER_SIZE==40960)?"40k":
+		       "64k");
+		if (atomic_read(&card->is_gone)) {
+			length+=sprintf(buffer+length,
+					"%04X/%04X/%04X x%02X %10s %14s %2i" 
+					"  +++ CARD IS GONE +++\n",
+					card->devno0,card->devno1,card->devno2,
+					card->chpid,
+					card->dev_name,
+					qeth_get_cardname_short
+					(card->type,card->link_type),
+					card->options.portno);
+		} else if (!atomic_read(&card->is_startlaned)) {
+			length+=sprintf(buffer+length,
+					"%04X/%04X/%04X x%02X %10s %14s %2i" 
+					"  +++ CABLE PULLED +++\n",
+					card->devno0,card->devno1,card->devno2,
+					card->chpid,
+					card->dev_name,
+					qeth_get_cardname_short
+					(card->type,card->link_type),
+					card->options.portno);
+		} else {
+			length+=sprintf(buffer+length,
+					"%04X/%04X/%04X x%02X %10s %14s %2i" \
+					"     %2s %10s %3s %3s %c %3i\n",
+					card->devno0,card->devno1,card->devno2,
+					card->chpid,card->dev_name,
+					qeth_get_cardname_short
+					(card->type,card->link_type),
+					card->options.portno,
+					checksum_str,
+					queueing_str,router_str,bufsize_str,
+					(card->options.memusage==
+					 MEMUSAGE_CONTIG)?'c':' ',
+					card->options.inbound_buffer_count);
+		}
+		card=card->next;
+	}
+
+out:
+	info->len=length;
+	/* unlock all the stuff */
+	my_read_unlock(&list_lock);
+	return rc;
+}
+
+static qeth_card_t *qeth_find_card(char *buffer,int len)
+{
+        qeth_card_t *card;
+	int devnamelen;
+
+	my_read_lock(&list_lock);
+        card=firstcard;
+        while (card) {
+		devnamelen=0;
+		while ( (devnamelen<DEV_NAME_LEN) &&
+			( (card->dev_name[devnamelen]!=' ') &&
+			  (card->dev_name[devnamelen]!=0) &&
+			  (card->dev_name[devnamelen]!='\n') ) ) {
+			devnamelen++;
+		}
+                if ((!strncmp(card->dev_name,buffer,
+			      qeth_min(len,DEV_NAME_LEN)))&&
+		    (devnamelen==len) &&
+		    (!atomic_read(&card->shutdown_phase))) break;
+                card=card->next;
+        }
+	my_read_unlock(&list_lock);
+
+	return card;
+}
+
+static int qeth_get_next_token(char *buffer,int *token_len,
+			       int *pos,int *end_pos,int count)
+{
+	*token_len=0;
+	*end_pos=*pos;
+	if (*end_pos>=count) return 0;
+	if (!buffer[*end_pos]) return 0;
+	for (;;) {
+		if ( (buffer[*end_pos]!=' ') &&
+		     (buffer[*end_pos]!='\t') &&
+		     (buffer[*end_pos]!='\n') &&
+		     (buffer[*end_pos]!='\r') &&
+		     (buffer[*end_pos]!=0) &&
+		     (*end_pos<count) ) {
+			*end_pos=(*end_pos)+1;
+			*token_len=(*token_len)+1;
+		}
+		else break;
+	}
+	return 1;
+}
+
+static void qeth_skip_whitespace(char *buffer,int *pos,int count)
+{
+	for (;;) {
+		if (*pos>=count) return;
+		if ((buffer[*pos]==' ')||
+		    (buffer[*pos]=='\t')||
+		    (buffer[*pos]=='\n')||
+		    (buffer[*pos]=='\r')) *pos=(*pos)+1;
+		else return;
+	}
+}
+
+#define CHECK_MISSING_PARAMETER do { \
+	pos=end_pos; \
+	if (!qeth_get_next_token(buffer,&token_len,&pos,&end_pos,user_len)) { \
+		PRINT_WARN("paramter missing on procfile input, " \
+			   "ignoring input!\n"); \
+		goto out; \
+	} \
+	card=qeth_find_card(buffer+pos,token_len); \
+	if (!card) { \
+		PRINT_WARN("paramter invalid on procfile input, " \
+			   "ignoring input!\n"); \
+		goto out; \
+	} \
+} while (0)
+
+static ssize_t qeth_procfile_write(struct file *file,
+	  			   const char *user_buffer,
+  				   size_t user_len,loff_t *offset)
+{
+	qeth_card_t *card;
+	char *buffer;
+	int token_len;
+	int pos=0,end_pos;
+	char dbf_text[15];
+
+	if (*offset>0) return user_len;
+	buffer=vmalloc(__max(user_len+1,QETH_DBF_MISC_LEN));
+	if (buffer == NULL)
+		return -ENOMEM;
+	memset(buffer,0,user_len+1);
+
+	if (copy_from_user(buffer, user_buffer, user_len)) {
+		vfree (buffer);
+		return -EFAULT;
+	}
+		
+	QETH_DBF_TEXT2(0,trace,"procwrit");
+	QETH_DBF_TEXT2(0,misc,buffer);
+
+	if (!qeth_get_next_token(buffer,&token_len,&pos,&end_pos,user_len))
+		goto out;
+	qeth_skip_whitespace(buffer,&end_pos,user_len);
+
+	if (!strncmp(buffer+pos,"recover",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"UTRC%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+			atomic_set(&card->problem,
+				   PROBLEM_USER_TRIGGERED_RECOVERY);
+			qeth_schedule_recovery(card);
+
+	} else if (!strncmp(buffer+pos,"remember",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"remb%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+			card->save_state_flag=1;
+
+	} else if (!strncmp(buffer+pos,"forget",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"forg%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+			card->save_state_flag=0;
+	} else if (!strncmp(buffer+pos,"primary_router",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"prir%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=PRIMARY_ROUTER|
+				RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+			card->options.routing_type6=PRIMARY_ROUTER|
+				RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+			qeth_start_softsetup_thread(card);
+	} else if (!strncmp(buffer+pos,"primary_router4",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"pri4%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=PRIMARY_ROUTER|
+				RESET_ROUTING_FLAG;
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+	} else if (!strncmp(buffer+pos,"primary_router6",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"pri6%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type6=PRIMARY_ROUTER|
+				RESET_ROUTING_FLAG;
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+	} else if (!strncmp(buffer+pos,"secondary_router",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"secr%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=SECONDARY_ROUTER|
+				RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+			card->options.routing_type6=SECONDARY_ROUTER|
+				RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+			qeth_start_softsetup_thread(card);
+	} else if (!strncmp(buffer+pos,"secondary_router4",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"sec4%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=SECONDARY_ROUTER|
+				RESET_ROUTING_FLAG;
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+	} else if (!strncmp(buffer+pos,"secondary_router6",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"sec6%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type6=SECONDARY_ROUTER|
+				RESET_ROUTING_FLAG;
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+	} else if (!strncmp(buffer+pos,"multicast_router",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"mcr %4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=MULTICAST_ROUTER|
+				RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+			card->options.routing_type6=MULTICAST_ROUTER|
+				RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+			qeth_start_softsetup_thread(card);
+	} else if (!strncmp(buffer+pos,"multicast_router4",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"mcr4%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=MULTICAST_ROUTER|
+				RESET_ROUTING_FLAG;
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+	} else if (!strncmp(buffer+pos,"multicast_router6",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"mcr6%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type6=MULTICAST_ROUTER|
+				RESET_ROUTING_FLAG;
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+	} else if (!strncmp(buffer+pos,"primary_connector",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"prc %4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=PRIMARY_CONNECTOR|
+				RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+			card->options.routing_type6=PRIMARY_CONNECTOR|
+				RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+			qeth_start_softsetup_thread(card);
+	} else if (!strncmp(buffer+pos,"primary_connector4",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"prc4%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=PRIMARY_CONNECTOR|
+				RESET_ROUTING_FLAG;
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+	} else if (!strncmp(buffer+pos,"primary_connector6",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"prc6%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type6=PRIMARY_CONNECTOR|
+				RESET_ROUTING_FLAG;
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+	} else if (!strncmp(buffer+pos,"secondary_connector",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"scc %4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=SECONDARY_CONNECTOR|
+				RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+			card->options.routing_type6=SECONDARY_CONNECTOR|
+				RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+			qeth_start_softsetup_thread(card);
+	} else if (!strncmp(buffer+pos,"secondary_connector4",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"scc4%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=SECONDARY_CONNECTOR|
+				RESET_ROUTING_FLAG;
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+	} else if (!strncmp(buffer+pos,"secondary_connector6",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"scc6%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type6=SECONDARY_CONNECTOR|
+				RESET_ROUTING_FLAG;
+			qeth_correct_routing_status(card);
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+	} else if (!strncmp(buffer+pos,"no_router",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"nor %4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=NO_ROUTER|
+				RESET_ROUTING_FLAG;
+#ifdef QETH_IPV6
+			card->options.routing_type6=NO_ROUTER|
+				RESET_ROUTING_FLAG;
+#endif /* QETH_IPV6 */
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+#ifdef QETH_IPV6
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+#endif /* QETH_IPV6 */
+			qeth_start_softsetup_thread(card);
+	} else if (!strncmp(buffer+pos,"no_router4",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"nor4%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type4=NO_ROUTER|
+				RESET_ROUTING_FLAG;
+			atomic_set(&card->enable_routing_attempts4,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#ifdef QETH_IPV6
+	} else if (!strncmp(buffer+pos,"no_router6",token_len)) {
+			CHECK_MISSING_PARAMETER;
+			sprintf(dbf_text,"nor6%4x",card->irq0);
+			QETH_DBF_TEXT2(0,trace,dbf_text);
+
+			card->options.routing_type6=NO_ROUTER|
+				RESET_ROUTING_FLAG;
+			atomic_set(&card->enable_routing_attempts6,
+				   QETH_ROUTING_ATTEMPTS);
+			qeth_start_softsetup_thread(card);
+#endif /* QETH_IPV6 */
+	} else {
+		PRINT_WARN("unknown command input in procfile\n");
+	}
+#undef CHECK_MISSING_PARAMETER
+out:
+
+	*offset = *offset + user_len;
+	vfree(buffer);
+
+	return user_len;
+}
+
+#define _OUTP_IT(x...) c+=sprintf(buffer+c,x)
+
+#ifdef QETH_PERFORMANCE_STATS
+static int qeth_perf_procfile_read(char *buffer,char **buffer_location,
+		    		   off_t offset,int buffer_length,int *eof,
+				   void *data)
+{
+	int c=0;
+	qeth_card_t *card;
+	/* we are always called with buffer_length=4k, so we all
+	   deliver on the first read */
+	if (offset>0) return 0;
+
+	QETH_DBF_TEXT2(0,trace,"perfpfrd");
+
+	card=firstcard;
+
+	while (card) {
+		_OUTP_IT("For card with devnos 0x%X/0x%X/0x%X (%s):\n",
+			 card->devno0,card->devno1,card->devno2,
+			 card->dev_name);
+		_OUTP_IT("  Skb's/buffers received                 : %i/%i\n",
+			 card->perf_stats.skbs_rec,
+			 card->perf_stats.bufs_rec);
+		_OUTP_IT("  Skb's/buffers sent                     : %i/%i\n",
+			 card->perf_stats.skbs_sent,
+			 card->perf_stats.bufs_sent);
+		_OUTP_IT("\n");
+		_OUTP_IT("  Skb's/buffers sent without packing     : %i/%i\n",
+			 card->perf_stats.skbs_sent_dont_pack,
+			 card->perf_stats.bufs_sent_dont_pack);
+		_OUTP_IT("  Skb's/buffers sent with packing        : %i/%i\n",
+			 card->perf_stats.skbs_sent_pack,
+			 card->perf_stats.bufs_sent_pack);
+		_OUTP_IT("\n");
+		_OUTP_IT("  Packing state changes no pkg.->packing : %i/%i\n",
+			 card->perf_stats.sc_dp_p,
+			 card->perf_stats.sc_p_dp);
+		_OUTP_IT("  Current buffer usage (outbound q's)    : " \
+			 "%i/%i/%i/%i\n",
+			 atomic_read(&card->outbound_used_buffers[0]),
+			 atomic_read(&card->outbound_used_buffers[1]),
+			 atomic_read(&card->outbound_used_buffers[2]),
+			 atomic_read(&card->outbound_used_buffers[3]));
+		_OUTP_IT("\n");
+		_OUTP_IT("  Inbound time (in us)                   : %i\n",
+			 card->perf_stats.inbound_time);
+		_OUTP_IT("  Inbound cnt                            : %i\n",
+			 card->perf_stats.inbound_cnt);
+		_OUTP_IT("  Outbound time (in us, incl QDIO)       : %i\n",
+			 card->perf_stats.outbound_time);
+		_OUTP_IT("  Outbound cnt                           : %i\n",
+			 card->perf_stats.outbound_cnt);
+		_OUTP_IT("  Watermarks: L/H=%i/%i\n",
+			 LOW_WATERMARK_PACK,HIGH_WATERMARK_PACK);
+		_OUTP_IT("\n");
+
+		card=card->next;
+	}
+
+	return c;
+}
+
+static struct proc_dir_entry *qeth_perf_proc_file;
+
+#endif /* QETH_PERFORMANCE_STATS */
+
+static int qeth_ipato_procfile_open(struct inode *inode, struct file *file)
+{
+	char text[33];
+	ipato_entry_t *ipato_entry;
+	qeth_card_t *card;
+	qeth_vipa_entry_t *vipa_entry;
+	int rc=0;
+	tempinfo_t *info;
+	int size;
+	char entry_type[5];
+
+	info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
+	if (info == NULL) {
+		PRINT_WARN("No memory available for data\n");
+		return -ENOMEM;
+	} else {
+		file->private_data = (void *) info;
+	}
+	info->len=0;
+
+	QETH_DBF_TEXT2(0,trace,"ipatorea");
+	/* lock all the stuff */
+	my_spin_lock(&ipato_list_lock);
+	my_read_lock(&list_lock);
+
+	size=64; /* for inv4/6 etc. */
+
+	ipato_entry=ipato_entries;
+	while (ipato_entry) {
+		ipato_entry=ipato_entry->next;
+		size+=64;
+	}
+	card=firstcard;
+	while (card) {
+		my_read_lock(&card->vipa_list_lock);
+		vipa_entry=card->vipa_list;
+		while (vipa_entry) {
+			vipa_entry=vipa_entry->next;
+			size+=64;
+		}
+		/*my_read_unlock(&card->vipa_list_lock); don't unlock it here*/
+		card=card->next;
+	}
+	info->data = (char *) vmalloc (size);
+	if (info->data == NULL) {
+		PRINT_WARN("No memory available for data\n");
+		vfree (info);
+		rc=-ENOMEM;
+		goto out;
+	}
+
+#define _IOUTP_IT(x...) info->len+=sprintf(info->data+info->len,x)
+	if (ipato_inv4) 
+		_IOUTP_IT("inv4\n");
+	ipato_entry=ipato_entries;
+	text[8]=0;
+	while (ipato_entry) {
+		if (ipato_entry->version==4) {
+			qeth_convert_addr_to_text(4,ipato_entry->addr,text);
+			_IOUTP_IT("add4 %s/%i%s%s\n",text,
+				 ipato_entry->mask_bits,
+				 ipato_entry->dev_name[0]?":":"",
+				 ipato_entry->dev_name[0]?
+				 ipato_entry->dev_name:"");
+		}
+		ipato_entry=ipato_entry->next;
+	}
+	
+	if (ipato_inv6) 
+		_IOUTP_IT("inv6\n");
+	ipato_entry=ipato_entries;
+	text[32]=0;
+	while (ipato_entry) {
+		if (ipato_entry->version==6) {
+			qeth_convert_addr_to_text(6,ipato_entry->addr,text);
+			_IOUTP_IT("add6 %s/%i%s%s\n",text,
+				 ipato_entry->mask_bits,
+				 ipato_entry->dev_name[0]?":":"",
+				 ipato_entry->dev_name[0]?
+				 ipato_entry->dev_name:"");
+		}
+		ipato_entry=ipato_entry->next;
+	}
+	card=firstcard;
+	while (card) {
+		vipa_entry=card->vipa_list;
+		while (vipa_entry) {
+			strcpy(entry_type,(vipa_entry->flag==
+					   IPA_SETIP_VIPA_FLAGS)?
+			       "vipa":"rxip");
+			if (vipa_entry->version==4) {
+				_IOUTP_IT("add_%s4 %02x%02x%02x%02x:%s\n",
+					  entry_type,
+					  vipa_entry->ip[0],
+					  vipa_entry->ip[1],
+					  vipa_entry->ip[2],
+					  vipa_entry->ip[3],
+					  card->dev_name);
+			} else {
+				_IOUTP_IT("add_%s6 %02x%02x%02x%02x" \
+					 "%02x%02x%02x%02x" \
+					 "%02x%02x%02x%02x" \
+					 "%02x%02x%02x%02x:%s\n",
+					  entry_type,
+					  vipa_entry->ip[0],
+					  vipa_entry->ip[1],
+					  vipa_entry->ip[2],
+					  vipa_entry->ip[3],
+					  vipa_entry->ip[4],
+					  vipa_entry->ip[5],
+					  vipa_entry->ip[6],
+					  vipa_entry->ip[7],
+					  vipa_entry->ip[8],
+					  vipa_entry->ip[9],
+					  vipa_entry->ip[10],
+					  vipa_entry->ip[11],
+					  vipa_entry->ip[12],
+					  vipa_entry->ip[13],
+					  vipa_entry->ip[14],
+					  vipa_entry->ip[15],
+					  card->dev_name);
+			}
+			vipa_entry=vipa_entry->next;
+		}
+		card=card->next;
+	}
+out:
+	/* unlock all the stuff */
+	card=firstcard;
+	while (card) {
+		/*my_read_lock(&card->vipa_list_lock); don't lock it here */
+		my_read_unlock(&card->vipa_list_lock);
+		card=card->next;
+	}
+	my_read_unlock(&list_lock);
+	my_spin_unlock(&ipato_list_lock);
+	
+	return rc;
+}
+
+static ssize_t qeth_procfile_read(struct file *file,char *user_buf,
+				  size_t user_len,loff_t * offset)
+{
+	loff_t len;
+	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+	
+	if (*offset >= p_info->len) {
+		return 0;
+	} else {
+		len = __min(user_len, (p_info->len - *offset));
+		if (copy_to_user (user_buf, &(p_info->data[*offset]), len))
+			return -EFAULT;
+		(*offset) += len;
+		return len;
+	}
+}
+
+/* ATT: this is also the procfile release function for the ipato
+ * procfs entry */
+static int qeth_procfile_release(struct inode *inode,struct file *file)
+{
+   	tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+	
+	if (p_info) {
+		if (p_info->data)
+			vfree (p_info->data);
+		vfree (p_info);
+	}
+	
+	return 0;
+}
+
+static ssize_t qeth_ipato_procfile_write(struct file *file,
+					 const char *user_buffer,
+					 size_t user_len,loff_t *offset)
+{
+	int add,version;
+	char text[33];
+	__u8 addr[16];
+	int len,i,flag;
+	int mask_bits;
+	char *buffer;
+	int dev_name_there;
+	char *dev_name_ptr;
+	qeth_card_t *card;
+#define BUFFER_LEN (10+32+1+5+1+DEV_NAME_LEN+1)
+
+	if (*offset>0) return user_len;
+	buffer=vmalloc(__max(__max(user_len+1,BUFFER_LEN),QETH_DBF_MISC_LEN));
+
+	if (buffer == NULL)
+		return -ENOMEM;
+	/* BUFFER_LEN=command incl. blank+addr+slash+mask_bits+
+	 * colon+DEV_NAME_LEN+zero */
+	memset(buffer,0,BUFFER_LEN);
+
+	if (copy_from_user(buffer, user_buffer, user_len)) {
+		vfree (buffer);
+		return -EFAULT;
+	}
+		
+	QETH_DBF_TEXT2(0,trace,"ipatowri");
+	QETH_DBF_TEXT2(0,misc,buffer);
+	if (!strncmp(buffer,"inv4",4)) {
+		ipato_inv4=1-ipato_inv4;
+		goto out;
+	}
+	if (!strncmp(buffer,"inv6",4)) {
+		ipato_inv6=1-ipato_inv6;
+		goto out;
+	}
+	if ( (!strncmp(buffer,"add4 ",5)) ||
+	     (!strncmp(buffer,"add6 ",5)) ||
+	     (!strncmp(buffer,"del4 ",5)) ||
+	     (!strncmp(buffer,"del6 ",5)) ) {
+		text[8]=0;
+		text[32]=0;
+		add=!strncmp(buffer,"add",3);
+		version=(buffer[3]=='4')?4:6;
+		len=(version==4)?8:32;
+		strncpy(text,buffer+5,len);
+		if (qeth_convert_text_to_addr(version,text,addr)) {
+			PRINT_ERR("error in parsing ipato information " \
+				  "(addr)\n");
+			goto out;
+		}
+		strncpy(text,buffer+5+len+1,10);
+		/* we prepare mask_bits for qeth_getints */
+		dev_name_there=0;
+		for (i=5+len+1;i<BUFFER_LEN;i++) {
+			if (*(buffer+i)=='\n') {
+				*(buffer+i)=0;
+				break;
+			}
+			if (*(buffer+i)==':') {
+				*(buffer+i)=0; /* so that qeth_getint works */
+				dev_name_there=i;
+				break;
+			}
+			if (*(buffer+i)==0)
+				break;
+		}
+		mask_bits=qeth_getint(buffer+5+len+1,0);
+		if ((mask_bits<0)||(mask_bits>((version==4)?32:128))) {
+			PRINT_ERR("error in parsing ipato information " \
+				  "(mask bits)\n");
+			goto out;
+		}
+		if (dev_name_there) {
+			dev_name_ptr=buffer+dev_name_there+1;
+			/* wipe out the linefeed */
+			for (i=dev_name_there+1;
+			     i<dev_name_there+1+DEV_NAME_LEN+1;i++)
+				if (*(buffer+i)=='\n') *(buffer+i)=0;
+		} else
+			dev_name_ptr=NULL;
+
+		if (add)
+			qeth_add_ipato_entry(version,addr,mask_bits,
+					     dev_name_ptr);
+		else
+			qeth_del_ipato_entry(version,addr,mask_bits,
+					     dev_name_ptr);
+		goto out;
+	}
+	if ( (!strncmp(buffer,"add_vipa4 ",10)) ||
+	     (!strncmp(buffer,"add_rxip4 ",10)) ||
+	     (!strncmp(buffer,"add_vipa6 ",10)) ||
+	     (!strncmp(buffer,"add_rxip6 ",10)) ||
+	     (!strncmp(buffer,"del_vipa4 ",10)) ||
+	     (!strncmp(buffer,"del_rxip4 ",10)) ||
+	     (!strncmp(buffer,"del_vipa6 ",10)) ||
+	     (!strncmp(buffer,"del_rxip6 ",10)) ) {
+		text[8]=0;
+		text[32]=0;
+		add=!strncmp(buffer,"add",3);
+		flag=(!strncmp(buffer+4,"vipa",4))?IPA_SETIP_VIPA_FLAGS:
+			IPA_SETIP_TAKEOVER_FLAGS;
+		version=(buffer[8]=='4')?4:6;
+		len=(version==4)?8:32;
+		strncpy(text,buffer+10,len);
+		if (qeth_convert_text_to_addr(version,text,addr)) {
+			PRINT_ERR("error in parsing vipa/rxip information " \
+				  "(addr)\n");
+			goto out;
+		}
+		if (*(buffer+10+len)!=':') {
+			PRINT_ERR("error in parsing vipa/rxip information " \
+				  "(no interface)\n");
+			goto out;
+		}
+		/* interface name is at buffer+10+len+1 */
+		/* wipe out the \n */
+		for (i=10+len+1;i<10+len+1+DEV_NAME_LEN+1;i++)
+			if (*(buffer+i)=='\n') *(buffer+i)=0;
+		card=qeth_get_card_by_name(buffer+10+len+1);
+		if (!card) {
+			PRINT_ERR("error in parsing vipa/rxip information " \
+				  "(unknown interface)\n");
+			goto out;
+		}
+		if (add)
+			i=qeth_add_vipa_entry(card,version,addr,flag);
+		else
+			i=qeth_del_vipa_entry(card,version,addr,flag);
+		if (!i) qeth_start_softsetup_thread(card);
+		goto out;
+	}
+	PRINT_ERR("unknown ipato information command\n");
+out:
+	vfree(buffer);
+	*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;
+
+	/* 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;
+	}
+#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();
+
+} 
+
+static int qeth_procfile_ioctl(struct inode *inode, struct file *file,
+			       unsigned int cmd, unsigned long arg) 
+{
+
+     int result;
+     down_interruptible(&qeth_procfile_ioctl_lock);
+     switch (cmd) {
+
+     case QETH_IOCPROC_OSAEINTERFACES:
+	     result = qeth_procfile_getinterfaces(arg);
+	     break;
+     case QETH_IOCPROC_INTERFACECHANGES:
+	     result = qeth_procfile_interfacechanges(arg);
+	     break;
+     default:
+	     result = -EOPNOTSUPP;	     
+     }
+     up(&qeth_procfile_ioctl_lock);
+     return result;
+};
+
+static struct file_operations qeth_procfile_fops =
+{
+	ioctl:qeth_procfile_ioctl,
+	read:qeth_procfile_read,
+	write:qeth_procfile_write,
+	open:qeth_procfile_open,
+	release:qeth_procfile_release,
+};
+
+static struct proc_dir_entry *qeth_proc_file;
+
+static struct file_operations qeth_ipato_procfile_fops =
+{
+	read:qeth_procfile_read, /* same as above! */
+	write:qeth_ipato_procfile_write,
+	open:qeth_ipato_procfile_open,
+	release:qeth_procfile_release /* same as above! */
+};
+
+static struct proc_dir_entry *qeth_ipato_proc_file;
+
+static void qeth_add_procfs_entries(void)
+{
+	proc_file_registration=0;
+	qeth_proc_file=create_proc_entry(QETH_PROCFILE_NAME,
+					 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)
+		PRINT_WARN("was not able to register proc-file (%i).\n",
+			   proc_file_registration);
+	proc_ipato_file_registration=0;
+	qeth_ipato_proc_file=create_proc_entry(QETH_IPA_PROCFILE_NAME,
+		  			       S_IFREG|0644,&proc_root);
+	if (qeth_ipato_proc_file) {
+		qeth_ipato_proc_file->proc_fops = &qeth_ipato_procfile_fops;
+	} else proc_ipato_file_registration=-1;
+
+	if (proc_ipato_file_registration)
+		PRINT_WARN("was not able to register ipato-proc-file (%i).\n",
+			   proc_ipato_file_registration);
+
+#ifdef QETH_PERFORMANCE_STATS
+	proc_perf_file_registration=0;
+	qeth_perf_proc_file=create_proc_entry(QETH_PERF_PROCFILE_NAME,
+					      S_IFREG|0444,&proc_root);
+	if (qeth_perf_proc_file) {
+		qeth_perf_proc_file->read_proc=&qeth_perf_procfile_read;
+	} else proc_perf_file_registration=-1;
+
+	if (proc_perf_file_registration)
+		PRINT_WARN("was not able to register perf. proc-file (%i).\n",
+			   proc_perf_file_registration);
+#endif /* QETH_PERFORMANCE_STATS */
+}
+
+#ifdef MODULE
+static void qeth_remove_procfs_entries(void)
+{
+	if (!proc_file_registration) /* means if it went ok earlier */
+		remove_proc_entry(QETH_PROCFILE_NAME,&proc_root);
+
+	if (!proc_ipato_file_registration) /* means if it went ok earlier */
+		remove_proc_entry(QETH_IPA_PROCFILE_NAME,&proc_root);
+
+#ifdef QETH_PERFORMANCE_STATS
+	if (!proc_perf_file_registration) /* means if it went ok earlier */
+		remove_proc_entry(QETH_PERF_PROCFILE_NAME,&proc_root);
+#endif /* QETH_PERFORMANCE_STATS */
+}
+#endif /* MODULE */
+
+static void qeth_unregister_dbf_views(void)
+{
+	if (qeth_dbf_setup)
+		debug_unregister(qeth_dbf_setup);
+	if (qeth_dbf_qerr)
+		debug_unregister(qeth_dbf_qerr);
+	if (qeth_dbf_sense)
+		debug_unregister(qeth_dbf_sense);
+	if (qeth_dbf_misc)
+		debug_unregister(qeth_dbf_misc);
+	if (qeth_dbf_data)
+		debug_unregister(qeth_dbf_data);
+	if (qeth_dbf_control)
+		debug_unregister(qeth_dbf_control);
+	if (qeth_dbf_trace)
+		debug_unregister(qeth_dbf_trace);
+}
+
+static int qeth_chandev_shutdown(struct net_device *dev)
+{
+
+	qeth_card_t* card;
+
+	card = (qeth_card_t *)dev->priv;
+
+	my_spin_lock(&setup_lock);
+
+        qeth_remove_card_from_list(card);
+	QETH_DBF_TEXT4(0,trace,"freecard");
+        qeth_free_card(card);
+
+	my_spin_unlock(&setup_lock);
+	
+	return 0;
+
+}
+
+#ifdef QETH_IPV6
+static int qeth_ipv6_init(void)
+{
+	qeth_old_arp_constructor=arp_tbl.constructor;
+	write_lock(&arp_tbl.lock);
+	arp_tbl.constructor=qeth_arp_constructor;
+	write_unlock(&arp_tbl.lock);
+
+	/* generate the memory leak here */
+	arp_direct_ops=(struct neigh_ops*)
+		kmalloc(sizeof(struct neigh_ops),GFP_KERNEL);
+	if (!arp_direct_ops)
+		return -ENOMEM;
+
+	memcpy(arp_direct_ops,&arp_direct_ops_template,
+	       sizeof(struct neigh_ops));
+	return 0;
+}
+
+static void qeth_ipv6_uninit(void)
+{
+	write_lock(&arp_tbl.lock);
+	arp_tbl.constructor=qeth_old_arp_constructor;
+	write_unlock(&arp_tbl.lock);
+}
+#endif /* QETH_IPV6 */
+
+static void qeth_get_internal_functions(void)
+{
+	struct net_device dev;
+	ether_setup(&dev);
+	qeth_my_eth_header=dev.hard_header;
+	qeth_my_eth_rebuild_header=dev.rebuild_header;
+	qeth_my_eth_header_cache=dev.hard_header_cache;
+	qeth_my_eth_header_cache_update=dev.header_cache_update;
+	qeth_my_eth_header=dev.hard_header;
+#ifdef CONFIG_TR
+	tr_setup(&dev);
+	qeth_my_tr_header=dev.hard_header;
+	qeth_my_tr_rebuild_header=dev.rebuild_header;
+#endif /* CONFIG_TR */
+}
+
+#ifdef MODULE
+int init_module(void)
+#else /* MODULE */
+static int __init qeth_init(void)
+#endif /* MODULE */
+{
+	int result;
+#ifdef MODULE
+	void *ptr;
+#endif /* MODULE */
+
+	int unregister_from_chandev=0;
+	int cards_found;
+
+	qeth_eyecatcher();
+
+  	printk("qeth: loading %s\n",version);
+
+#ifdef MODULE
+	global_stay_in_mem = chandev_persist(chandev_type_qeth);
+#endif /* MODULE */
+
+        spin_lock_init(&setup_lock);
+
+	spin_lock_init(&ipato_list_lock);
+
+	qeth_get_internal_functions();
+
+	qeth_alloc_spare_bufs();
+
+#ifdef QETH_IPV6
+	if (qeth_ipv6_init()) goto oom;
+#endif /* QETH_IPV6 */
+
+        qeth_dbf_setup=debug_register(QETH_DBF_SETUP_NAME,
+                                      QETH_DBF_SETUP_INDEX,
+                                      QETH_DBF_SETUP_NR_AREAS,
+                                      QETH_DBF_SETUP_LEN);
+        if (!qeth_dbf_setup) goto oom;
+
+        debug_register_view(qeth_dbf_setup,&debug_hex_ascii_view);
+	debug_set_level(qeth_dbf_setup,QETH_DBF_SETUP_LEVEL);
+
+        qeth_dbf_misc=debug_register(QETH_DBF_MISC_NAME,
+				     QETH_DBF_MISC_INDEX,
+				     QETH_DBF_MISC_NR_AREAS,
+				     QETH_DBF_MISC_LEN);
+        if (!qeth_dbf_misc) goto oom;
+
+        debug_register_view(qeth_dbf_misc,&debug_hex_ascii_view);
+	debug_set_level(qeth_dbf_misc,QETH_DBF_MISC_LEVEL);
+
+        qeth_dbf_data=debug_register(QETH_DBF_DATA_NAME,
+				     QETH_DBF_DATA_INDEX,
+				     QETH_DBF_DATA_NR_AREAS,
+				     QETH_DBF_DATA_LEN);
+        if (!qeth_dbf_data) goto oom;
+
+        debug_register_view(qeth_dbf_data,&debug_hex_ascii_view);
+	debug_set_level(qeth_dbf_data,QETH_DBF_DATA_LEVEL);
+
+        qeth_dbf_control=debug_register(QETH_DBF_CONTROL_NAME,
+					QETH_DBF_CONTROL_INDEX,
+					QETH_DBF_CONTROL_NR_AREAS,
+					QETH_DBF_CONTROL_LEN);
+        if (!qeth_dbf_control) goto oom;
+
+        debug_register_view(qeth_dbf_control,&debug_hex_ascii_view);
+	debug_set_level(qeth_dbf_control,QETH_DBF_CONTROL_LEVEL);
+
+        qeth_dbf_sense=debug_register(QETH_DBF_SENSE_NAME,
+                                      QETH_DBF_SENSE_INDEX,
+                                      QETH_DBF_SENSE_NR_AREAS,
+                                      QETH_DBF_SENSE_LEN);
+        if (!qeth_dbf_sense) goto oom;
+
+        debug_register_view(qeth_dbf_sense,&debug_hex_ascii_view);
+	debug_set_level(qeth_dbf_sense,QETH_DBF_SENSE_LEVEL);
+
+        qeth_dbf_qerr=debug_register(QETH_DBF_QERR_NAME,
+       				     QETH_DBF_QERR_INDEX,
+      				     QETH_DBF_QERR_NR_AREAS,
+     				     QETH_DBF_QERR_LEN);
+        if (!qeth_dbf_qerr) goto oom;
+
+        debug_register_view(qeth_dbf_qerr,&debug_hex_ascii_view);
+	debug_set_level(qeth_dbf_qerr,QETH_DBF_QERR_LEVEL);
+
+        qeth_dbf_trace=debug_register(QETH_DBF_TRACE_NAME,
+                                      QETH_DBF_TRACE_INDEX,
+                                      QETH_DBF_TRACE_NR_AREAS,
+                                      QETH_DBF_TRACE_LEN);
+        if (!qeth_dbf_trace) goto oom;
+
+        debug_register_view(qeth_dbf_trace,&debug_hex_ascii_view);
+	debug_set_level(qeth_dbf_trace,QETH_DBF_TRACE_LEVEL);
+	
+	cards_found = chandev_register_and_probe
+		(qeth_probe,(chandev_shutdownfunc)qeth_chandev_shutdown,
+		 (chandev_msck_notification_func)qeth_chandev_msck_notfunc,
+		 chandev_type_qeth);
+	if (cards_found>0)
+		result=0;
+	else if (cards_found<0) {
+		result=cards_found;
+		global_stay_in_mem=0;
+	} else result=-ENODEV;
+
+	unregister_from_chandev=(cards_found>=0);
+	if ((result)&&(global_stay_in_mem)) {
+		result=0;
+        }
+
+#ifdef MODULE
+	QETH_DBF_TEXT0(0,setup,"initmodl");
+	ptr=&init_module;
+	QETH_DBF_HEX0(0,setup,&ptr,sizeof(void*));
+#endif /* MODULE */
+
+	if (!result) {
+                qeth_register_notifiers();
+		qeth_add_procfs_entries();
+	} else {
+		/* don't call it with shutdown -- there was not device
+		 * initialized or an internal problem in chandev, better
+		 * have him not try to call us */
+		if (unregister_from_chandev)
+			chandev_unregister(qeth_probe,0);
+		qeth_unregister_dbf_views();
+#ifdef QETH_IPV6
+		qeth_ipv6_uninit();
+#endif /* QETH_IPV6 */
+		qeth_free_all_spare_bufs();
+	}
+
+	return result;
+oom:
+	PRINT_ERR("not enough memory for dbf. Will not load module.\n");
+	result=-ENOMEM;
+#ifdef QETH_IPV6
+	qeth_ipv6_uninit();
+#endif /* QETH_IPV6 */
+	qeth_unregister_dbf_views();
+	qeth_free_all_spare_bufs();
+	return result;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+#ifdef QETH_IPV6
+	qeth_ipv6_uninit();
+#endif /* QETH_IPV6 */
+        qeth_unregister_notifiers();
+
+	qeth_remove_procfs_entries();
+
+	QETH_DBF_TEXT1(0,trace,"cleanup.");
+
+	chandev_unregister(qeth_probe, 1 );
+
+	qeth_free_all_spare_bufs();
+
+	qeth_unregister_dbf_views();
+
+	printk("qeth: %s: module removed\n",version);
+}
+EXPORT_SYMBOL(qeth_eyecatcher); /* yeah, i know, could be outside of
+				   the ifdef */
+#else /* MODULE */
+__initcall(qeth_init);
+#endif /* MODULE */
diff -urN linux-2.4.21-bk9/drivers/s390/net/qeth.h linux-2.4.21-bk10/drivers/s390/net/qeth.h
--- linux-2.4.21-bk9/drivers/s390/net/qeth.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/drivers/s390/net/qeth.h	2003-07-15 02:55:06.000000000 -0700
@@ -0,0 +1,1235 @@
+/*
+ * linux/drivers/s390/net/qeth.h
+ *
+ * Linux on zSeries OSA Express and HiperSockets support
+ *
+ * Copyright 2000,2003 IBM Corporation
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ *
+ */
+
+#ifndef __QETH_H__
+#define __QETH_H__
+
+#include <asm/qdio.h>
+
+#define QETH_NAME " qeth"
+
+#define VERSION_QETH_H "$Revision: 1.113 $"
+
+/******************** CONFIG STUFF ***********************/
+//#define QETH_DBF_LIKE_HELL
+
+#ifdef CONFIG_QETH_IPV6
+#define QETH_IPV6
+#define QETH_VERSION_IPV6 ":IPv6"
+#else
+#define QETH_VERSION_IPV6 ""
+#endif /* CONFIG_QETH_IPV6 */
+
+#ifdef CONFIG_QETH_VLAN
+#define QETH_VLAN
+#define QETH_VERSION_VLAN ":VLAN"
+#else
+#define QETH_VERSION_VLAN ""
+#endif /* CONFIG_QETH_VLAN */
+
+/* these values match CHECKSUM_* in include/linux/skbuff.h */
+#define SW_CHECKSUMMING 0
+#define HW_CHECKSUMMING 1
+#define NO_CHECKSUMMING 2
+
+#define QETH_CHECKSUM_DEFAULT NO_CHECKSUMMING
+
+#define QETH_DEFAULT_QUEUE 2
+
+/******************** CONFIG STUFF END ***********************/
+/********************* TUNING STUFF **************************/
+#define HIGH_WATERMARK_PACK		5
+#define LOW_WATERMARK_PACK		2
+#define WATERMARK_FUZZ			2
+
+#define QETH_MAX_INPUT_THRESHOLD 500
+#define QETH_MAX_OUTPUT_THRESHOLD 300 /* ? */
+
+/* only the MAX values are used */
+#define QETH_MIN_INPUT_THRESHOLD 1
+#define QETH_MIN_OUTPUT_THRESHOLD 1
+
+#define QETH_REQUEUE_THRESHOLD (card->options.inbound_buffer_count/4)
+
+#ifdef CONFIG_QETH_PERF_STATS
+#define QETH_PERFORMANCE_STATS
+#endif /* CONFIG_QETH_PERF_STATS */
+
+#define QETH_VERBOSE_LEVEL 7
+
+#define PCI_THRESHOLD_A (card->options.inbound_buffer_count+1) /* buffers we have to be behind
+			     before we get a PCI */
+#define PCI_THRESHOLD_B 0 /* enqueued free buffers left before we get a PCI */
+#define PCI_TIMER_VALUE 3 /* not used, unless the microcode gets patched */
+
+#define DEFAULT_SPARE_BUFFERS 0
+#define MAX_SPARE_BUFFERS 1024
+#define SPAREBUF_MASK 65536
+#define MAX_PORTNO 15
+
+#define QETH_PROCFILE_NAME "qeth"
+#define QETH_PERF_PROCFILE_NAME "qeth_perf"
+#define QETH_IPA_PROCFILE_NAME "qeth_ipa_takeover"
+
+#define SEND_RETRIES_ALLOWED 5
+#define QETH_ROUTING_ATTEMPTS 2
+
+#define QETH_HARDSETUP_LAPS 5
+#define QETH_HARDSETUP_CLEAR_LAPS 3
+#define QETH_RECOVERY_HARDSETUP_RETRY 2
+
+/************************* DEBUG FACILITY STUFF *********************/
+
+#define QETH_DBF_HEX(ex,name,level,addr,len) \
+        do { \
+        if (ex) \
+                debug_exception(qeth_dbf_##name,level,(void*)addr,len); \
+        else \
+                debug_event(qeth_dbf_##name,level,(void*)addr,len); \
+        } while (0)
+#define QETH_DBF_TEXT(ex,name,level,text) \
+        do { \
+        if (ex) \
+                debug_text_exception(qeth_dbf_##name,level,text); \
+        else \
+                debug_text_event(qeth_dbf_##name,level,text); \
+        } while (0)
+
+#define QETH_DBF_HEX0(ex,name,addr,len) QETH_DBF_HEX(ex,name,0,addr,len)
+#define QETH_DBF_HEX1(ex,name,addr,len) QETH_DBF_HEX(ex,name,1,addr,len)
+#define QETH_DBF_HEX2(ex,name,addr,len) QETH_DBF_HEX(ex,name,2,addr,len)
+#define QETH_DBF_HEX3(ex,name,addr,len) QETH_DBF_HEX(ex,name,3,addr,len)
+#define QETH_DBF_HEX4(ex,name,addr,len) QETH_DBF_HEX(ex,name,4,addr,len)
+#define QETH_DBF_HEX5(ex,name,addr,len) QETH_DBF_HEX(ex,name,5,addr,len)
+#define QETH_DBF_HEX6(ex,name,addr,len) QETH_DBF_HEX(ex,name,6,addr,len)
+#ifdef QETH_DBF_LIKE_HELL
+#endif /* QETH_DBF_LIKE_HELL */
+
+#define QETH_DBF_TEXT0(ex,name,text) QETH_DBF_TEXT(ex,name,0,text)
+#define QETH_DBF_TEXT1(ex,name,text) QETH_DBF_TEXT(ex,name,1,text)
+#define QETH_DBF_TEXT2(ex,name,text) QETH_DBF_TEXT(ex,name,2,text)
+#define QETH_DBF_TEXT3(ex,name,text) QETH_DBF_TEXT(ex,name,3,text)
+#define QETH_DBF_TEXT4(ex,name,text) QETH_DBF_TEXT(ex,name,4,text)
+#define QETH_DBF_TEXT5(ex,name,text) QETH_DBF_TEXT(ex,name,5,text)
+#define QETH_DBF_TEXT6(ex,name,text) QETH_DBF_TEXT(ex,name,6,text)
+#ifdef QETH_DBF_LIKE_HELL
+#endif /* QETH_DBF_LIKE_HELL */
+
+#define QETH_DBF_SETUP_NAME "qeth_setup"
+#define QETH_DBF_SETUP_LEN 8
+#define QETH_DBF_SETUP_INDEX 3
+#define QETH_DBF_SETUP_NR_AREAS 1
+#ifdef QETH_DBF_LIKE_HELL
+#define QETH_DBF_SETUP_LEVEL 6
+#else /* QETH_DBF_LIKE_HELL */
+#define QETH_DBF_SETUP_LEVEL 3
+#endif /* QETH_DBF_LIKE_HELL */
+
+#define QETH_DBF_MISC_NAME "qeth_misc"
+#define QETH_DBF_MISC_LEN 128
+#define QETH_DBF_MISC_INDEX 1
+#define QETH_DBF_MISC_NR_AREAS 1
+#ifdef QETH_DBF_LIKE_HELL
+#define QETH_DBF_MISC_LEVEL 6
+#else /* QETH_DBF_LIKE_HELL */
+#define QETH_DBF_MISC_LEVEL 2
+#endif /* QETH_DBF_LIKE_HELL */
+
+#define QETH_DBF_DATA_NAME "qeth_data"
+#define QETH_DBF_DATA_LEN 96
+#define QETH_DBF_DATA_INDEX 3
+#define QETH_DBF_DATA_NR_AREAS 1
+#ifdef QETH_DBF_LIKE_HELL
+#define QETH_DBF_DATA_LEVEL 6
+#else /* QETH_DBF_LIKE_HELL */
+#define QETH_DBF_DATA_LEVEL 2
+#endif /* QETH_DBF_LIKE_HELL */
+
+#define QETH_DBF_CONTROL_NAME "qeth_control"
+/* buffers are 255 bytes long, but no prob */
+#define QETH_DBF_CONTROL_LEN 256
+#define QETH_DBF_CONTROL_INDEX 3
+#define QETH_DBF_CONTROL_NR_AREAS 2
+#ifdef QETH_DBF_LIKE_HELL
+#define QETH_DBF_CONTROL_LEVEL 6
+#else /* QETH_DBF_LIKE_HELL */
+#define QETH_DBF_CONTROL_LEVEL 2
+#endif /* QETH_DBF_LIKE_HELL */
+
+#define QETH_DBF_TRACE_NAME "qeth_trace"
+#define QETH_DBF_TRACE_LEN 8
+#ifdef QETH_DBF_LIKE_HELL
+#define QETH_DBF_TRACE_INDEX 3
+#else /* QETH_DBF_LIKE_HELL */
+#define QETH_DBF_TRACE_INDEX 2
+#endif /* QETH_DBF_LIKE_HELL */
+#define QETH_DBF_TRACE_NR_AREAS 2
+#ifdef QETH_DBF_LIKE_HELL
+#define QETH_DBF_TRACE_LEVEL 6
+#else /* QETH_DBF_LIKE_HELL */
+#define QETH_DBF_TRACE_LEVEL 2
+#endif /* QETH_DBF_LIKE_HELL */
+
+#define QETH_DBF_SENSE_NAME "qeth_sense"
+#define QETH_DBF_SENSE_LEN 64
+#define QETH_DBF_SENSE_INDEX 1
+#define QETH_DBF_SENSE_NR_AREAS 1
+#ifdef QETH_DBF_LIKE_HELL
+#define QETH_DBF_SENSE_LEVEL 6
+#else /* QETH_DBF_LIKE_HELL */
+#define QETH_DBF_SENSE_LEVEL 2
+#endif /* QETH_DBF_LIKE_HELL */
+
+
+#define QETH_DBF_QERR_NAME "qeth_qerr"
+#define QETH_DBF_QERR_LEN 8
+#define QETH_DBF_QERR_INDEX 1
+#define QETH_DBF_QERR_NR_AREAS 2
+#ifdef QETH_DBF_LIKE_HELL
+#define QETH_DBF_QERR_LEVEL 6
+#else /* QETH_DBF_LIKE_HELL */
+#define QETH_DBF_QERR_LEVEL 2
+#endif /* QETH_DBF_LIKE_HELL */
+/****************** END OF DEBUG FACILITY STUFF *********************/
+
+/********************* CARD DATA STUFF **************************/
+
+#define QETH_MAX_PARAMS 150
+
+#define QETH_CARD_TYPE_UNKNOWN	0
+#define QETH_CARD_TYPE_OSAE	10
+#define QETH_CARD_TYPE_IQD	1234
+
+#define QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT 0x0101
+#define QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT 0x0101
+/* as soon as steve is ready:
+#define QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT 0x4101
+#define QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT 0x5101
+*/
+#define QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT 0x4108
+#define QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT 0x5108
+
+#define QETH_MAX_QUEUES 4
+
+#define UNIQUE_ID_IF_CREATE_ADDR_FAILED 0xfffe
+#define UNIQUE_ID_NOT_BY_CARD 0x10000
+
+/* CU type & model, Dev type & model, card_type, odd_even_restriction, func level, no of queues, multicast is different (multicast-queue_no + 0x100) */
+#define QETH_MODELLIST_ARRAY \
+	{{0x1731,0x01,0x1732,0x01,QETH_CARD_TYPE_OSAE,1, \
+	  QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT, \
+	  QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT, \
+	  QETH_MAX_QUEUES,0}, \
+	 {0x1731,0x05,0x1732,0x05,QETH_CARD_TYPE_IQD,0, \
+	  QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT, \
+	  QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT, \
+	  QETH_MAX_QUEUES,0x103}, \
+	 {0,0,0,0,0,0,0,0,0}}
+
+#define QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE 0x18
+ /* only the first two bytes are looked at in qeth_get_cardname_short */
+#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_LANE_ETH100 0x81
+#define QETH_MPC_LINK_TYPE_LANE_TR 0x82
+#define QETH_MPC_LINK_TYPE_LANE_ETH1000 0x83
+#define QETH_MPC_LINK_TYPE_LANE 0x88
+
+#define DEFAULT_ADD_HHLEN 0
+#define MAX_ADD_HHLEN 1024
+
+#define QETH_HEADER_SIZE	32
+#define QETH_IP_HEADER_SIZE	40
+#define QETH_HEADER_LEN_POS	8
+/* flags for the header: */
+#define QETH_HEADER_PASSTHRU	0x10
+#define QETH_HEADER_IPV6	0x80
+
+#define QETH_CAST_FLAGS		0x07
+#define QETH_CAST_UNICAST	6
+#define QETH_CAST_MULTICAST	4
+#define QETH_CAST_BROADCAST	5
+#define QETH_CAST_ANYCAST	7
+#define QETH_CAST_NOCAST	0
+
+/* VLAN defines */
+#define QETH_EXT_HEADER_VLAN_FRAME	  0x01
+#define QETH_EXT_HEADER_TOKEN_ID	  0x02
+#define QETH_EXT_HEADER_INCLUDE_VLAN_TAG  0x04
+
+#define QETH_EXT_HEADER_SRC_MAC_ADDRESS   0x08
+#define QETH_EXT_HEADER_CSUM_HDR_REQ      0x10
+#define QETH_EXT_HEADER_CSUM_TRANSP_REQ   0x20
+#define QETH_EXT_HEADER_CSUM_TRANSP_FRAME_TYPE   0x40
+
+#define QETH_UDP_CSUM_OFFSET	6
+#define QETH_TCP_CSUM_OFFSET	16
+
+#define QETH_VERIFY_IS_REAL_DEV               1
+#define QETH_VERIFY_IS_VLAN_DEV               2
+
+inline static unsigned int qeth_get_ipa_timeout(int cardtype)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_IQD: return 2000;
+	default: return 20000;
+	}
+}
+
+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
+	switch (link_type) {
+	case QETH_MPC_LINK_TYPE_HSTR:
+	case QETH_MPC_LINK_TYPE_LANE_TR:
+		return QETH_HEADER_SIZE+TR_HLEN;
+	default:
+#ifdef QETH_VLAN
+		return QETH_HEADER_SIZE+VLAN_ETH_HLEN;
+#else
+		return QETH_HEADER_SIZE+ETH_HLEN;
+#endif
+	}
+#else /* QETH_IPV6 */
+#ifdef QETH_VLAN
+	return QETH_HEADER_SIZE+VLAN_HLEN;
+#else
+	return QETH_HEADER_SIZE;
+#endif
+
+#endif /* QETH_IPV6 */
+}
+
+int (*qeth_my_eth_header)(struct sk_buff *,struct net_device *,
+	unsigned short,void *,void *,unsigned);
+int (*qeth_my_tr_header)(struct sk_buff *,struct net_device *,
+	unsigned short,void *,void *,unsigned);
+int (*qeth_my_eth_rebuild_header)(struct sk_buff *);
+int (*qeth_my_tr_rebuild_header)(struct sk_buff *);
+int (*qeth_my_eth_header_cache)(struct neighbour *,struct hh_cache *);
+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)
+{
+	switch (link_type) {
+#ifdef CONFIG_TR
+	case QETH_MPC_LINK_TYPE_HSTR:
+	case QETH_MPC_LINK_TYPE_LANE_TR:
+		return qeth_my_tr_header;
+#endif /* CONFIG_TR */
+	default:
+		return qeth_my_eth_header;
+	}
+}
+
+typedef int (*__qeth_temp2)(struct sk_buff *);
+inline static __qeth_temp2 qeth_get_rebuild_header(__u8 link_type)
+{
+	switch (link_type) {
+#ifdef CONFIG_TR
+	case QETH_MPC_LINK_TYPE_HSTR:
+	case QETH_MPC_LINK_TYPE_LANE_TR:
+		return qeth_my_tr_rebuild_header;
+#endif /* CONFIG_TR */
+	default:
+		return qeth_my_eth_rebuild_header;
+	}
+}
+
+typedef int (*__qeth_temp3)(struct neighbour *,struct hh_cache *);
+inline static __qeth_temp3 qeth_get_hard_header_cache(__u8 link_type)
+{
+	switch (link_type) {
+	case QETH_MPC_LINK_TYPE_HSTR:
+	case QETH_MPC_LINK_TYPE_LANE_TR:
+		return NULL;
+	default:
+		return qeth_my_eth_header_cache;
+	}
+}
+
+typedef void (*__qeth_temp4)(struct hh_cache *,struct net_device *,
+	 unsigned char *);
+inline static __qeth_temp4 qeth_get_header_cache_update(__u8 link_type)
+{
+	switch (link_type) {
+	case QETH_MPC_LINK_TYPE_HSTR:
+	case QETH_MPC_LINK_TYPE_LANE_TR:
+		return NULL;
+	default:
+		return qeth_my_eth_header_cache_update;
+	}
+}
+
+static unsigned short qeth_eth_type_trans(struct sk_buff *skb,
+					  struct net_device *dev)
+{
+	struct ethhdr *eth;
+	
+	skb->mac.raw=skb->data;
+	skb_pull(skb,ETH_ALEN*2+sizeof(short));
+	eth=skb->mac.ethernet;
+	
+	if(*eth->h_dest&1) {
+		if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+			skb->pkt_type=PACKET_BROADCAST;
+		else
+			skb->pkt_type=PACKET_MULTICAST;
+   	} else {
+		skb->pkt_type=PACKET_OTHERHOST;
+	}
+	if (ntohs(eth->h_proto)>=1536) return eth->h_proto;
+	if (*(unsigned short *)(skb->data) == 0xFFFF)
+		return htons(ETH_P_802_3);
+	return htons(ETH_P_802_2);
+}
+
+typedef unsigned short (*__qeth_temp5)(struct sk_buff *,struct net_device *);
+inline static __qeth_temp5 qeth_get_type_trans(__u8 link_type)
+{
+	switch (link_type) {
+	case QETH_MPC_LINK_TYPE_HSTR:
+	case QETH_MPC_LINK_TYPE_LANE_TR:
+		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)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_UNKNOWN: return "unknown";
+	case QETH_CARD_TYPE_OSAE:
+		switch (linktype) {
+		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_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";
+		default: return "unknown";
+		}
+	case QETH_CARD_TYPE_IQD: return "magic";
+	default: return "unknown";
+	}
+}
+
+inline static const char* qeth_get_dev_basename(int cardtype,__u8 link_type)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_UNKNOWN: return "eth";
+	case QETH_CARD_TYPE_OSAE: switch (link_type) {
+				  case QETH_MPC_LINK_TYPE_LANE_TR:
+					  /* fallthrough */
+				  case QETH_MPC_LINK_TYPE_HSTR: return "tr";
+				  default: return "eth";
+				  }
+	case QETH_CARD_TYPE_IQD: return "hsi";
+	default: return "eth";
+	}
+}
+
+/* inbound: */
+#define DEFAULT_BUFFER_SIZE 65536
+#define DEFAULT_BUFFER_COUNT 128
+#define BUFCNT_MIN 8
+#define BUFCNT_MAX 128
+#define BUFFER_SIZE (card->inbound_buffer_size)
+#define BUFFER_MAX_ELEMENTS (BUFFER_SIZE>>12)
+	/* 8k for each pair header-buffer: */
+
+inline static int qeth_sbal_packing_on_card(int cardtype)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_IQD: return 0;
+	default: return 1;
+	}
+}
+
+/* do it this way round -> __MODULE_STRING needs with */
+/* QETH_PRIO_NICE_LEVELS a single number */
+#define QETH_MAX_PRIO_QUEUES QETH_PRIO_NICE_LEVELS+1
+
+static inline int qeth_sbalf15_in_retrieable_range(int sbalf15)
+{
+	return ( (sbalf15>=15) && (sbalf15<=31) );
+}
+
+#define INBOUND_BUFFER_POS(card,bufno,sbale) \
+	( (bufno&SPAREBUF_MASK)? \
+	  ( \
+	    (sparebufs[bufno&(~SPAREBUF_MASK)].buf+ \
+	     PAGE_SIZE*sbale) \
+	  ):( \
+	      (card->inbound_buffer_pool_entry[card-> \
+	       inbound_buffer_entry_no[bufno]][sbale]) \
+	    ) )
+
+#define SPAREBUF_UNAVAIL 0
+#define SPAREBUF_FREE 1
+#define SPAREBUF_USED 2
+
+typedef struct sparebufs_t {
+	char *buf;
+	atomic_t status;
+} sparebufs_t;
+
+#define SEND_STATE_INACTIVE		0
+#define SEND_STATE_DONT_PACK		1
+#define SEND_STATE_PACK			2
+
+#define QETH_LOCK_UNLOCKED 0
+#define QETH_LOCK_NORMAL 1
+#define QETH_LOCK_FLUSH 2
+
+#define QETH_MAX_DEVICES 16
+	/* DEPENDENCY ON QETH_MAX_DEVICES.
+	 *__MOUDLE_STRING expects simple literals */
+#define QETH_MAX_DEVICES_TIMES_4 64
+#define QETH_MAX_DEVNAMES 16
+#define QETH_DEVNAME "eth"
+
+#define QETH_TX_TIMEOUT 100*HZ /* 100 seconds */
+
+#define QETH_REMOVE_WAIT_TIME 200
+#define QETH_WAIT_FOR_THREAD_TIME 20
+#define QETH_IDLE_WAIT_TIME 10
+#define QETH_WAIT_BEFORE_2ND_DOIO 1000
+
+#define QETH_MAX_PARM_LEN 128
+
+#define QETH_FAKE_LL_LEN ETH_HLEN /* 14 */
+#define QETH_FAKE_LL_PROT_LEN 2
+#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_PROT_POS 12 
+#define QETH_FAKE_LL_V4_ADDR_POS 16
+#define QETH_FAKE_LL_V6_ADDR_POS 24
+
+#define DEV_NAME_LEN 16
+#define IOCTL_MAX_TRANSFER_SIZE 65535
+
+#define IP_TOS_LOWDELAY 0x10
+#define IP_TOS_HIGHTHROUGHPUT 0x08
+#define IP_TOS_HIGHRELIABILITY 0x04
+#define IP_TOS_NOTIMPORTANT 0x02
+
+#define QETH_RCD_LENGTH 128
+
+#define __max(a,b) ( ((a)>(b))?(a):(b) )
+#define __min(a,b) ( ((a)<(b))?(a):(b) )
+#define QETH_BUFSIZE __max(__max(IPA_PDU_HEADER_SIZE+ \
+				 sizeof(arp_cmd_t),IPA_PDU_HEADER_SIZE+ \
+				 sizeof(ipa_cmd_t)),QETH_RCD_LENGTH)
+
+#define QETH_FINAL_STATUS_TIMEOUT 1500
+#define QETH_CLEAR_TIMEOUT 1500
+#define QETH_RCD_TIMEOUT 1500
+#define QETH_NOP_TIMEOUT 1500
+#define QETH_QUIESCE_NETDEV_TIME 300
+#define QETH_QUIESCE_WAIT_BEFORE_CLEAR 4000
+#define QETH_QUIESCE_WAIT_AFTER_CLEAR 4000
+
+#define NOP_STATE 0x1001
+#define READ_CONF_DATA_STATE 0x1002
+#define IDX_ACTIVATE_READ_STATE 0x1003
+#define IDX_ACTIVATE_WRITE_STATE 0x1004
+#define MPC_SETUP_STATE 0x1005
+#define CLEAR_STATE 0x1006
+#define IPA_CMD_STATE 0x1007
+#define IPA_IOCTL_STATE 0x1009
+#define IPA_SETIP_FLAG 0x100000
+
+#define QETH_REMOVE_CARD_PROPER 1
+#define QETH_REMOVE_CARD_QUICK 2
+
+#define PARSE_AUTO 0
+#define PARSE_ROUTING_TYPE 1
+#define PARSE_CHECKSUMMING 2
+#define PARSE_PRIO_QUEUEING 3
+#define PARSE_STAYINMEM 4
+#define PARSE_BUFFERCOUNT 5
+#define PARSE_PORTNAME 6
+#define PARSE_POLLTIME 7
+#define PARSE_SPARE_BUFFERCOUNT 8
+#define PARSE_PORTNO 9
+#define PARSE_BROADCAST_MODE 10
+#define PARSE_MACADDR_MODE 11
+#define PARSE_MEMUSAGE 12
+#define PARSE_ENA_IPAT 13
+#define PARSE_FAKE_BROADCAST 14
+#define PARSE_ADD_HHLEN 15
+#define PARSE_ROUTING_TYPE4 16
+#define PARSE_ROUTING_TYPE6 17
+#define PARSE_FAKE_LL 18
+#define PARSE_ASYNC_IQD 19
+
+#define PARSE_COUNT 20
+
+#define NO_PRIO_QUEUEING 0
+#define PRIO_QUEUEING_PREC 1
+#define PRIO_QUEUEING_TOS 2
+#define NO_ROUTER 0
+#define PRIMARY_ROUTER 1
+#define SECONDARY_ROUTER 2
+#define MULTICAST_ROUTER 3
+#define PRIMARY_CONNECTOR 4
+#define SECONDARY_CONNECTOR 5
+#define ROUTER_MASK 0xf /* used to remove SET_ROUTING_FLAG
+			   from routing_type */
+#define RESET_ROUTING_FLAG 0x10 /* used to indicate, that setting
+				   the routing type is desired */
+#define BROADCAST_ALLRINGS 0
+#define BROADCAST_LOCAL 1
+#define MACADDR_NONCANONICAL 0
+#define MACADDR_CANONICAL 1
+#define MEMUSAGE_DISCONTIG 0
+#define MEMUSAGE_CONTIG 1
+#define ENABLE_TAKEOVER 0
+#define DISABLE_TAKEOVER 1
+#define FAKE_BROADCAST 0
+#define DONT_FAKE_BROADCAST 1
+#define FAKE_LL 0
+#define DONT_FAKE_LL 1
+#define SYNC_IQD 0
+#define ASYNC_IQD 1
+
+#define QETH_BREAKOUT_LEAVE 1
+#define QETH_BREAKOUT_AGAIN 2
+
+#define QETH_WAIT_FOR_LOCK 0
+#define QETH_DONT_WAIT_FOR_LOCK 1
+#define QETH_LOCK_ALREADY_HELD 2
+
+#define PROBLEM_CARD_HAS_STARTLANED 1
+#define PROBLEM_RECEIVED_IDX_TERMINATE 2
+#define PROBLEM_ACTIVATE_CHECK_CONDITION 3
+#define PROBLEM_RESETTING_EVENT_INDICATOR 4
+#define PROBLEM_COMMAND_REJECT 5
+#define PROBLEM_ZERO_SENSE_DATA 6
+#define PROBLEM_GENERAL_CHECK 7
+#define PROBLEM_BAD_SIGA_RESULT 8
+#define PROBLEM_USER_TRIGGERED_RECOVERY 9
+#define PROBLEM_AFFE 10
+#define PROBLEM_MACHINE_CHECK 11
+#define PROBLEM_TX_TIMEOUT 12
+
+#define SENSE_COMMAND_REJECT_BYTE 0
+#define SENSE_COMMAND_REJECT_FLAG 0x80
+#define SENSE_RESETTING_EVENT_BYTE 1
+#define SENSE_RESETTING_EVENT_FLAG 0x80
+
+#define DEFAULT_RCD_CMD 0x72
+#define DEFAULT_RCD_COUNT 0x80
+
+#define BUFFER_USED 1
+#define BUFFER_UNUSED -1
+
+/*typedef struct wait_queue* wait_queue_head_t; already typedefed in qdio.h */
+
+typedef int (*reg_notifier_t)(struct notifier_block*);
+
+typedef struct ipato_entry_t {
+	int version;
+	__u8 addr[16];
+	int mask_bits;
+	char dev_name[DEV_NAME_LEN];
+	struct ipato_entry_t *next;
+} ipato_entry_t;
+
+typedef struct qeth_vipa_entry_t {
+	int version;
+	__u8 ip[16];
+	int flag;
+	volatile int state;
+	struct qeth_vipa_entry_t *next;
+} qeth_vipa_entry_t;
+
+typedef struct ip_state_t {
+	struct in_ifaddr *ip_ifa;	/* pointer to IPv4 adresses */
+	struct inet6_ifaddr *ip6_ifa;
+} ip_state_t;
+
+struct qeth_ipm_mac {
+	__u8 mac[ETH_ALEN];
+	__u8 ip[16];
+	struct qeth_ipm_mac *next;
+};
+
+typedef struct ip_mc_state_t {
+	struct qeth_ipm_mac *ipm_ifa;
+	struct qeth_ipm_mac *ipm6_ifa;
+} ip_mc_state_t;
+
+struct qeth_card_options {
+	char devname[DEV_NAME_LEN];
+	volatile int routing_type4;
+#ifdef QETH_IPV6
+	volatile int routing_type6;
+#endif /* QETH_IPV6 */
+	int checksum_type;
+	int do_prio_queueing;
+	int default_queue;
+	int already_parsed[PARSE_COUNT];
+	int inbound_buffer_count;
+	__s32 memory_usage_in_k;
+	int polltime;
+	char portname[9];
+	int portno;
+	int memusage;
+	int broadcast_mode;
+	int macaddr_mode;
+	int ena_ipat;
+	int fake_broadcast;
+	int add_hhlen;
+	int fake_ll;
+	int async_iqd;
+};
+
+typedef struct qeth_hdr_t {
+	__u8 id;
+	__u8 flags;
+	__u16 inbound_checksum;
+	__u32 token;
+	__u16 length;
+	__u8 vlan_prio;
+	__u8 ext_flags;
+	__u16 vlan_id;
+	__u16 frame_offset;
+	__u8 dest_addr[16];
+} qeth_hdr_t;
+
+typedef struct qeth_ringbuffer_element_t {
+	struct sk_buff_head skb_list;
+	int next_element_to_fill;
+} __attribute__ ((packed)) qeth_ringbuffer_element_t;
+
+typedef struct qeth_ringbuffer_t {
+	qdio_buffer_t buffer[QDIO_MAX_BUFFERS_PER_Q];
+	qeth_ringbuffer_element_t ringbuf_element[QDIO_MAX_BUFFERS_PER_Q];
+} qeth_ringbuffer_t __attribute__ ((packed,aligned(PAGE_SIZE)));
+
+typedef struct qeth_dma_stuff_t {
+	unsigned char *sendbuf;
+	unsigned char *recbuf;
+	ccw1_t read_ccw;
+	ccw1_t write_ccw;
+} qeth_dma_stuff_t __attribute__ ((packed,aligned(PAGE_SIZE)));
+
+typedef struct qeth_perf_stats_t {
+	unsigned int skbs_rec;
+	unsigned int bufs_rec;
+
+	unsigned int skbs_sent;
+	unsigned int bufs_sent;
+
+	unsigned int skbs_sent_dont_pack;
+	unsigned int bufs_sent_dont_pack;
+	unsigned int skbs_sent_pack;
+	unsigned int bufs_sent_pack;
+	unsigned int skbs_sent_pack_better;
+	unsigned int bufs_sent_pack_better;
+
+	unsigned int sc_dp_p;
+	unsigned int sc_p_dp;
+
+	__u64 inbound_start_time;
+	unsigned int inbound_cnt;
+	unsigned int inbound_time;
+	__u64 outbound_start_time;
+	unsigned int outbound_cnt;
+	unsigned int outbound_time;
+} qeth_perf_stats_t;
+
+/* ugly. I know. */
+typedef struct qeth_card_t {			/* pointed to by dev->priv */
+	int easy_copy_cap;
+
+	/* pointer to options (defaults + parameters) */
+	struct qeth_card_options options;
+
+ 	atomic_t is_startlaned; /* card did not get a stoplan */
+				/* also 0 when card is gone after a
+				   machine check */
+
+	__u8 link_type;
+
+	int do_pfix; /* to avoid doing diag98 for vm guest lan devices */
+
+	/* inbound buffer management */
+	atomic_t inbound_buffer_refcnt[QDIO_MAX_BUFFERS_PER_Q];
+	qdio_buffer_t inbound_qdio_buffers[QDIO_MAX_BUFFERS_PER_Q];
+	void *real_inb_buffer_addr[QDIO_MAX_BUFFERS_PER_Q]
+		[QDIO_MAX_ELEMENTS_PER_BUFFER];
+
+	/* inbound data area */
+	void *inbound_buffer_pool_entry[QDIO_MAX_BUFFERS_PER_Q]
+		[QDIO_MAX_ELEMENTS_PER_BUFFER];
+	volatile int inbound_buffer_pool_entry_used[QDIO_MAX_BUFFERS_PER_Q];
+	int inbound_buffer_entry_no[QDIO_MAX_BUFFERS_PER_Q];
+
+	/* for requeueing of buffers */
+	spinlock_t requeue_input_lock;
+	atomic_t requeue_position;
+	atomic_t requeue_counter;
+
+	/* outbound QDIO stuff */
+	volatile int send_state[QETH_MAX_QUEUES];
+	volatile int outbound_first_free_buffer[QETH_MAX_QUEUES];
+	atomic_t outbound_used_buffers[QETH_MAX_QUEUES];
+	int outbound_buffer_send_state[QETH_MAX_QUEUES]
+		[QDIO_MAX_BUFFERS_PER_Q];
+	int send_retries[QETH_MAX_QUEUES][QDIO_MAX_BUFFERS_PER_Q];
+	volatile int outbound_bytes_in_buffer[QETH_MAX_QUEUES];
+	qeth_ringbuffer_t *outbound_ringbuffer[QETH_MAX_QUEUES];
+	atomic_t outbound_ringbuffer_lock[QETH_MAX_QUEUES];
+	atomic_t last_pci_pos[QETH_MAX_QUEUES];
+
+#ifdef QETH_IPV6
+	int (*hard_header)(struct sk_buff *,struct net_device *,
+			   unsigned short,void *,void *,unsigned);
+	int (*rebuild_header)(struct sk_buff *);
+	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 */
+
+#ifdef QETH_VLAN
+        struct vlan_group *vlangrp;
+        spinlock_t vlan_lock;
+
+#endif
+	char dev_name[DEV_NAME_LEN];		/* pointed to by dev->name */
+	char dev_basename[DEV_NAME_LEN];
+	struct net_device *dev;
+	struct net_device_stats *stats;
+
+	int no_queues;
+
+#ifdef QETH_PERFORMANCE_STATS
+	qeth_perf_stats_t perf_stats;
+#endif /* QETH_PERFORMANCE_STATS */
+
+	/* our state */
+	atomic_t is_registered;		/* card registered as netdev? */
+	atomic_t is_hardsetup;		/* card has gone through hardsetup */
+	atomic_t is_softsetup;		/* card is setup by softsetup */
+	atomic_t is_open;		/* card is in use */
+	atomic_t is_gone;               /* after a msck */
+
+	int has_irq;			/* once a request_irq was successful */
+
+	/* prevents deadlocks :-O */
+	spinlock_t softsetup_lock;
+	spinlock_t hardsetup_lock;
+	spinlock_t ioctl_lock;
+	atomic_t softsetup_thread_is_running;
+	struct semaphore softsetup_thread_sem;
+	struct tq_struct tqueue_sst;
+
+	atomic_t escape_softsetup;	/* active, when recovery has to
+					   wait for softsetup */
+	struct semaphore reinit_thread_sem;
+	atomic_t in_recovery;
+	atomic_t reinit_counter;
+
+	/* problem management */
+	atomic_t break_out;
+	atomic_t problem;
+	struct tq_struct tqueue;
+
+	struct {
+		__u32 trans_hdr;
+		__u32 pdu_hdr;
+		__u32 pdu_hdr_ack;
+		__u32 ipa;
+	} seqno;
+
+	struct {
+		__u32 issuer_rm_w;
+		__u32 issuer_rm_r;
+		__u32 cm_filter_w;
+		__u32 cm_filter_r;
+		__u32 cm_connection_w;
+		__u32 cm_connection_r;
+		__u32 ulp_filter_w;
+		__u32 ulp_filter_r;
+		__u32 ulp_connection_w;
+		__u32 ulp_connection_r;
+	} token;
+
+	/* this is card-related */
+	int type;
+	__u16 func_level;
+	int initial_mtu;
+	int max_mtu;
+	int inbound_buffer_size;
+
+	int is_multicast_different; /* if multicast traffic is to be sent
+				       on a different queue, this is the
+				       queue+no_queues */
+	int can_do_async_iqd; /* 1 only on IQD that provides async
+		      		 unicast sigas */
+
+	__u32 ipa_supported;
+	__u32 ipa_enabled;
+	__u32 ipa6_supported;
+	__u32 ipa6_enabled;
+	__u32 adp_supported;
+
+	atomic_t startlan_attempts;
+	atomic_t enable_routing_attempts4;
+	atomic_t rt4fld;
+#ifdef QETH_IPV6
+	atomic_t enable_routing_attempts6;
+	atomic_t rt6fld;
+#endif /* QETH_IPV6 */
+	int unique_id;
+
+	/* device and I/O data */
+	int devno0;
+	int devno1;
+	int devno2;
+	int irq0;
+	int irq1;
+	int irq2;
+	unsigned short unit_addr2;
+	unsigned short cula;
+	unsigned short dev_type;
+	unsigned char dev_model;
+	unsigned short chpid;
+	devstat_t *devstat0;
+	devstat_t *devstat1;
+	devstat_t *devstat2;
+
+	unsigned char ipa_buf[QETH_BUFSIZE];
+	unsigned char send_buf[QETH_BUFSIZE];
+
+/* IOCTL Stuff */
+	unsigned char *ioctl_data_buffer;
+	unsigned char *ioctl_buffer_pointer;
+	int ioctl_returncode;
+	int ioctl_buffersize;
+	int number_of_entries;
+
+
+	atomic_t ioctl_data_has_arrived;
+	wait_queue_head_t ioctl_wait_q;
+	atomic_t ioctl_wait_q_active;
+	spinlock_t ioctl_wait_q_lock;
+
+/* stuff under 2 gb */
+	qeth_dma_stuff_t *dma_stuff;
+
+	unsigned int ipa_timeout;
+
+	atomic_t write_busy;
+
+	int read_state; /* only modified and read in the int handler */
+
+	/* vipa stuff */
+	rwlock_t vipa_list_lock;
+	qeth_vipa_entry_t *vipa_list;
+
+	/* state information when doing I/O */
+	atomic_t shutdown_phase;
+	volatile int save_state_flag;
+	atomic_t data_has_arrived;
+	wait_queue_head_t wait_q;
+	atomic_t wait_q_active;
+	spinlock_t wait_q_lock; /* for wait_q_active and wait_q */
+
+	atomic_t final_status0;
+	atomic_t final_status1;
+	atomic_t final_status2;
+	atomic_t clear_succeeded0;
+	atomic_t clear_succeeded1;
+	atomic_t clear_succeeded2;
+
+	/* bookkeeping of IP and multicast addresses */
+	ip_state_t ip_current_state;
+	ip_state_t ip_new_state;
+	
+#ifdef CONFIG_IP_MULTICAST
+	ip_mc_state_t ip_mc_current_state;
+	ip_mc_state_t ip_mc_new_state;
+#endif /* CONFIG_IF_MULTICAST */
+
+	int broadcast_capable;
+	int portname_required;
+
+	int realloc_message;
+
+	char level[QETH_MCL_LENGTH+1];
+
+	volatile int saved_dev_flags;
+
+	/* for our linked list */
+	struct qeth_card_t *next;
+} qeth_card_t;
+
+typedef struct mydevreg_t {
+	devreg_t devreg;
+	struct mydevreg_t *next;
+	struct mydevreg_t *prev;
+} mydevreg_t;
+
+inline static int qeth_get_arphrd_type(int cardtype,int linktype)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_OSAE: switch (linktype) {
+				  case QETH_MPC_LINK_TYPE_LANE_TR:
+					  /* fallthrough */
+				  case QETH_MPC_LINK_TYPE_HSTR:
+					  return ARPHRD_IEEE802;
+				  default: return ARPHRD_ETHER;
+				  }
+	case QETH_CARD_TYPE_IQD: return ARPHRD_ETHER;
+	default: return ARPHRD_ETHER;
+	}
+}
+
+inline static int qeth_determine_easy_copy_cap(int cardtype)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_UNKNOWN: return 0; /* better be cautious */
+	case QETH_CARD_TYPE_OSAE: return 1;
+	case QETH_CARD_TYPE_IQD: return 0;
+	default: return 0; /* ?? */
+	}
+}
+inline static __u8 qeth_get_adapter_type_for_ipa(int link_type)
+{
+	switch (link_type) {
+	case QETH_MPC_LINK_TYPE_HSTR: return 2;
+	default: return 1;
+	}
+}
+
+inline static const char *qeth_get_cardname(int cardtype)
+{
+	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)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_UNKNOWN: return "unknown";
+	case QETH_CARD_TYPE_OSAE: 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_LANE_ETH100:
+     					  return "OSD_FE_LANE";
+				  case QETH_MPC_LINK_TYPE_LANE_TR:
+     					  return "OSD_TR_LANE";
+				  case QETH_MPC_LINK_TYPE_LANE_ETH1000:
+     					  return "OSD_GbE_LANE";
+				  case QETH_MPC_LINK_TYPE_LANE:
+     					  return "OSD_ATM_LANE";
+				  default: return "OSD_Express";
+				  }
+	case QETH_CARD_TYPE_IQD: return "HiperSockets";
+	default: return " strange";
+	}
+}
+
+inline static int qeth_mtu_is_valid(qeth_card_t *card,int mtu)
+{
+	switch (card->type) {
+	case QETH_CARD_TYPE_UNKNOWN: return 1;
+	case QETH_CARD_TYPE_OSAE: return ( (mtu>=576) && (mtu<=61440) );
+	case QETH_CARD_TYPE_IQD: return ( (mtu>=576) &&
+					    (mtu<=card->max_mtu+4096-32) );
+	default: return 1;
+	}
+}
+
+inline static int qeth_get_initial_mtu_for_card(qeth_card_t *card)
+{
+	switch (card->type) {
+	case QETH_CARD_TYPE_UNKNOWN: return 1500;
+	case QETH_CARD_TYPE_IQD: return card->max_mtu;
+	case QETH_CARD_TYPE_OSAE:
+		switch (card->link_type) {
+		case QETH_MPC_LINK_TYPE_HSTR:
+		case QETH_MPC_LINK_TYPE_LANE_TR:
+			return 2000;
+		default:
+			return 1492;
+		}
+	default: return 1500;
+	}
+}
+
+inline static int qeth_get_max_mtu_for_card(int cardtype)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_UNKNOWN: return 61440;
+	case QETH_CARD_TYPE_OSAE: return 61440;
+	case QETH_CARD_TYPE_IQD: return 57344;
+	default: return 1500;
+	}
+}
+
+inline static int qeth_get_mtu_out_of_mpc(int cardtype)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_IQD: return 1;
+	default: return 0;
+	}
+}
+
+inline static int qeth_get_mtu_outof_framesize(int framesize)
+{
+	switch (framesize) {
+	case 0x4000: return 8192;
+	case 0x6000: return 16384;
+	case 0xa000: return 32768;
+	case 0xffff: return 57344;
+	default: return 0;
+	}
+}
+
+inline static int qeth_get_buffersize_for_card(int cardtype)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_UNKNOWN: return 65536;
+	case QETH_CARD_TYPE_OSAE: return 65536;
+	case QETH_CARD_TYPE_IQD: return 16384;
+	default: return 65536;
+	}
+}
+
+inline static int qeth_get_min_number_of_buffers(int cardtype)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_UNKNOWN: return 32;
+	case QETH_CARD_TYPE_OSAE: return 32;
+	case QETH_CARD_TYPE_IQD: return 64;
+	default: return 64;
+	}
+}
+
+inline static int qeth_get_q_format(int cardtype)
+{
+	switch (cardtype) {
+	case QETH_CARD_TYPE_IQD: return 2;
+	default: return 0;
+	}
+}
+
+inline static int qeth_get_device_tx_q_len(int cardtype)
+{
+	return 100;
+}
+
+inline static int qeth_get_max_number_of_buffers(int cardtype)
+{
+	return 127;
+}
+
+/******************** OUTPUT FACILITIES **************************/
+
+#ifdef PRINT_INFO
+#undef PRINTK_HEADER
+#undef PRINT_STUPID
+#undef PRINT_ALL
+#undef PRINT_INFO
+#undef PRINT_WARN
+#undef PRINT_ERR
+#undef PRINT_CRIT
+#undef PRINT_ALERT
+#undef PRINT_EMERG
+#endif /* PRINT_INFO */
+
+#define PRINTK_HEADER QETH_NAME ": "
+
+#if QETH_VERBOSE_LEVEL>8
+#define PRINT_STUPID(x...) printk( KERN_DEBUG PRINTK_HEADER x)
+#else
+#define PRINT_STUPID(x...)
+#endif
+
+#if QETH_VERBOSE_LEVEL>7
+#define PRINT_ALL(x...) printk( KERN_DEBUG PRINTK_HEADER x)
+#else
+#define PRINT_ALL(x...)
+#endif
+
+#if QETH_VERBOSE_LEVEL>6
+#define PRINT_INFO(x...) printk( KERN_INFO PRINTK_HEADER x)
+#else
+#define PRINT_INFO(x...)
+#endif
+
+#if QETH_VERBOSE_LEVEL>5
+#define PRINT_WARN(x...) printk( KERN_WARNING PRINTK_HEADER x)
+#else
+#define PRINT_WARN(x...)
+#endif
+
+#if QETH_VERBOSE_LEVEL>4
+#define PRINT_ERR(x...) printk( KERN_ERR PRINTK_HEADER x)
+#else
+#define PRINT_ERR(x...)
+#endif
+
+#if QETH_VERBOSE_LEVEL>3
+#define PRINT_CRIT(x...) printk( KERN_CRIT PRINTK_HEADER x)
+#else
+#define PRINT_CRIT(x...)
+#endif
+
+#if QETH_VERBOSE_LEVEL>2
+#define PRINT_ALERT(x...) printk( KERN_ALERT PRINTK_HEADER x)
+#else
+#define PRINT_ALERT(x...)
+#endif
+
+#if QETH_VERBOSE_LEVEL>1
+#define PRINT_EMERG(x...) printk( KERN_EMERG PRINTK_HEADER x)
+#else
+#define PRINT_EMERG(x...)
+#endif
+
+#endif /* __QETH_H__ */
diff -urN linux-2.4.21-bk9/drivers/s390/net/qeth_mpc.h linux-2.4.21-bk10/drivers/s390/net/qeth_mpc.h
--- linux-2.4.21-bk9/drivers/s390/net/qeth_mpc.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/drivers/s390/net/qeth_mpc.h	2003-07-15 02:55:06.000000000 -0700
@@ -0,0 +1,611 @@
+/*
+ * linux/drivers/s390/net/qeth_mpc.h
+ *
+ * Linux on zSeries OSA Express and HiperSockets support
+ *
+ * Copyright 2000,2003 IBM Corporation
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ *
+ */
+
+#ifndef __QETH_MPC_H__
+#define __QETH_MPC_H__
+
+#define VERSION_QETH_MPC_H "$Revision: 1.42 $"
+
+#define QETH_IPA_TIMEOUT (card->ipa_timeout)
+#define QETH_MPC_TIMEOUT 2000
+#define QETH_ADDR_TIMEOUT 1000
+
+#define QETH_SETIP_RETRIES 2
+
+#define IDX_ACTIVATE_SIZE 0x22
+#define CM_ENABLE_SIZE 0x63
+#define CM_SETUP_SIZE 0x64
+#define ULP_ENABLE_SIZE 0x6b
+#define ULP_SETUP_SIZE 0x6c
+#define DM_ACT_SIZE 0x55
+
+#define QETH_MPC_TOKEN_LENGTH 4
+#define QETH_SEQ_NO_LENGTH 4
+#define QETH_IPA_SEQ_NO_LENGTH 2
+
+#define QETH_TRANSPORT_HEADER_SEQ_NO(buffer) (buffer+4)
+#define QETH_PDU_HEADER_SEQ_NO(buffer) (buffer+0x1c)
+#define QETH_PDU_HEADER_ACK_SEQ_NO(buffer) (buffer+0x20)
+
+static unsigned char IDX_ACTIVATE_READ[]={
+	0x00,0x00,0x80,0x00, 0x00,0x00,0x00,0x00,
+
+	0x19,0x01,0x01,0x80, 0x00,0x00,0x00,0x00,
+	0x00,0x00,0x00,0x00, 0x00,0x00,0xc8,0xc1,
+	0xd3,0xd3,0xd6,0xd3, 0xc5,0x40,0x00,0x00,
+	0x00,0x00
+};
+
+static unsigned char IDX_ACTIVATE_WRITE[]={
+	0x00,0x00,0x80,0x00, 0x00,0x00,0x00,0x00,
+
+	0x15,0x01,0x01,0x80, 0x00,0x00,0x00,0x00,
+	0xff,0xff,0x00,0x00, 0x00,0x00,0xc8,0xc1,
+	0xd3,0xd3,0xd6,0xd3, 0xc5,0x40,0x00,0x00,
+	0x00,0x00
+};
+
+#define QETH_IDX_ACT_ISSUER_RM_TOKEN(buffer) (buffer+0x0c)
+#define QETH_IDX_NO_PORTNAME_REQUIRED(buffer) ((buffer)[0x0b]&0x80)
+#define QETH_IDX_ACT_FUNC_LEVEL(buffer) (buffer+0x10)
+#define QETH_IDX_ACT_DATASET_NAME(buffer) (buffer+0x16)
+#define QETH_IDX_ACT_QDIO_DEV_CUA(buffer) (buffer+0x1e)
+#define QETH_IDX_ACT_QDIO_DEV_REALADDR(buffer) (buffer+0x20)
+
+#define QETH_IS_IDX_ACT_POS_REPLY(buffer) (((buffer)[0x08]&3)==2)
+
+#define QETH_IDX_REPLY_LEVEL(buffer) (buffer+0x12)
+#define QETH_MCL_LENGTH 4
+
+static unsigned char CM_ENABLE[]={
+	0x00,0xe0,0x00,0x00, 0x00,0x00,0x00,0x01,
+	0x00,0x00,0x00,0x14, 0x00,0x00,0x00,0x63,
+	0x10,0x00,0x00,0x01,
+
+		0x00,0x00,0x00,0x00,
+	0x81,0x7e,0x00,0x01, 0x00,0x00,0x00,0x00,
+	0x00,0x00,0x00,0x00, 0x00,0x24,0x00,0x23,
+	0x00,0x00,0x23,0x05, 0x00,0x00,0x00,0x00,
+	0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
+
+	0x01,0x00,0x00,0x23, 0x00,0x00,0x00,0x40,
+
+	0x00,0x0c,0x41,0x02, 0x00,0x17,0x00,0x00,
+
+	0x00,0x00,0x00,0x00,
+		0x00,0x0b,0x04,0x01,
+
+	0x7e,0x04,0x05,0x00, 0x01,0x01,0x0f,
+
+		0x00,
+
+	0x0c,0x04,0x02,0xff, 0xff,0xff,0xff,0xff,
+
+	0xff,0xff,0xff
+};
+
+#define QETH_CM_ENABLE_ISSUER_RM_TOKEN(buffer) (buffer+0x2c)
+#define QETH_CM_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53)
+#define QETH_CM_ENABLE_USER_DATA(buffer) (buffer+0x5b)
+
+#define QETH_CM_ENABLE_RESP_FILTER_TOKEN(buffer) (PDU_ENCAPSULATION(buffer)+ \
+						  0x13)
+
+static unsigned char CM_SETUP[]={
+	0x00,0xe0,0x00,0x00, 0x00,0x00,0x00,0x02,
+	0x00,0x00,0x00,0x14, 0x00,0x00,0x00,0x64,
+	0x10,0x00,0x00,0x01,
+
+		0x00,0x00,0x00,0x00,
+	0x81,0x7e,0x00,0x01, 0x00,0x00,0x00,0x00,
+	0x00,0x00,0x00,0x00, 0x00,0x24,0x00,0x24,
+	0x00,0x00,0x24,0x05, 0x00,0x00,0x00,0x00,
+	0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
+
+	0x01,0x00,0x00,0x24, 0x00,0x00,0x00,0x40,
+
+	0x00,0x0c,0x41,0x04, 0x00,0x18,0x00,0x00,
+
+	0x00,0x00,0x00,0x00,
+		0x00,0x09,0x04,0x04,
+
+	0x05,0x00,0x01,0x01, 0x11,
+
+		0x00,0x09,0x04,
+
+	0x05,0x05,0x00,0x00, 0x00,0x00,
+
+		0x00,0x06,
+
+	0x04,0x06,0xc8,0x00
+};
+
+#define QETH_CM_SETUP_DEST_ADDR(buffer) (buffer+0x2c)
+#define QETH_CM_SETUP_CONNECTION_TOKEN(buffer) (buffer+0x51)
+#define QETH_CM_SETUP_FILTER_TOKEN(buffer) (buffer+0x5a)
+
+#define QETH_CM_SETUP_RESP_DEST_ADDR(buffer) (PDU_ENCAPSULATION(buffer)+ \
+					      0x1a)
+
+static unsigned char ULP_ENABLE[]={
+	0x00,0xe0,0x00,0x00, 0x00,0x00,0x00,0x03,
+	0x00,0x00,0x00,0x14, 0x00,0x00,0x00,0x6b,
+	0x10,0x00,0x00,0x01,
+
+		0x00,0x00,0x00,0x00,
+	0x41,0x7e,0x00,0x01, 0x00,0x00,0x00,0x01,
+	0x00,0x00,0x00,0x00, 0x00,0x24,0x00,0x2b,
+	0x00,0x00,0x2b,0x05, 0x20,0x01,0x00,0x00,
+	0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
+
+	0x01,0x00,0x00,0x2b, 0x00,0x00,0x00,0x40,
+
+	0x00,0x0c,0x41,0x02, 0x00,0x1f,0x00,0x00,
+
+	0x00,0x00,0x00,0x00,
+		0x00,0x0b,0x04,0x01,
+
+	0x03,0x04,0x05,0x00, 0x01,0x01,0x12,
+
+		0x00,
+
+	0x14,0x04,0x0a,0x00, 0x20,0x00,0x00,0xff,
+	0xff,0x00,0x08,0xc8, 0xe8,0xc4,0xf1,0xc7,
+
+	0xf1,0x00,0x00
+};
+
+#define QETH_ULP_ENABLE_LINKNUM(buffer) (buffer+0x61)
+#define QETH_ULP_ENABLE_DEST_ADDR(buffer) (buffer+0x2c)
+#define QETH_ULP_ENABLE_FILTER_TOKEN(buffer) (buffer+0x53)
+#define QETH_ULP_ENABLE_PORTNAME_AND_LL(buffer) (buffer+0x62)
+
+#define QETH_ULP_ENABLE_RESP_FILTER_TOKEN(buffer) (PDU_ENCAPSULATION(buffer)+ \
+						   0x13)
+#define QETH_ULP_ENABLE_RESP_MAX_MTU(buffer) (PDU_ENCAPSULATION(buffer)+ \
+					      0x1f)
+#define QETH_ULP_ENABLE_RESP_DIFINFO_LEN(buffer) (PDU_ENCAPSULATION(buffer)+ \
+					  	  0x17)
+#define QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer) (PDU_ENCAPSULATION(buffer)+ \
+						0x2b)
+
+static unsigned char ULP_SETUP[]={
+	0x00,0xe0,0x00,0x00, 0x00,0x00,0x00,0x04,
+	0x00,0x00,0x00,0x14, 0x00,0x00,0x00,0x6c,
+	0x10,0x00,0x00,0x01,
+
+		0x00,0x00,0x00,0x00,
+	0x41,0x7e,0x00,0x01, 0x00,0x00,0x00,0x02,
+	0x00,0x00,0x00,0x01, 0x00,0x24,0x00,0x2c,
+	0x00,0x00,0x2c,0x05, 0x20,0x01,0x00,0x00,
+	0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
+
+	0x01,0x00,0x00,0x2c, 0x00,0x00,0x00,0x40,
+
+	0x00,0x0c,0x41,0x04, 0x00,0x20,0x00,0x00,
+
+	0x00,0x00,0x00,0x00,
+		0x00,0x09,0x04,0x04,
+
+	0x05,0x00,0x01,0x01, 0x14,
+		0x00,0x09,0x04,
+
+	0x05,0x05,0x30,0x01, 0x00,0x00,
+
+		0x00,0x06,
+
+	0x04,0x06,0x40,0x00,
+
+		0x00,0x08,0x04,0x0b,
+
+	0x00,0x00,0x00,0x00
+};
+
+#define QETH_ULP_SETUP_DEST_ADDR(buffer) (buffer+0x2c)
+#define QETH_ULP_SETUP_CONNECTION_TOKEN(buffer) (buffer+0x51)
+#define QETH_ULP_SETUP_FILTER_TOKEN(buffer) (buffer+0x5a)
+#define QETH_ULP_SETUP_CUA(buffer) (buffer+0x68)
+#define QETH_ULP_SETUP_REAL_DEVADDR(buffer) (buffer+0x6a)
+
+#define QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(buffer) (PDU_ENCAPSULATION \
+						      (buffer)+0x1a)
+						
+
+static unsigned char DM_ACT[]={
+	0x00,0xe0,0x00,0x00, 0x00,0x00,0x00,0x05,
+	0x00,0x00,0x00,0x14, 0x00,0x00,0x00,0x55,
+	0x10,0x00,0x00,0x01,
+
+		0x00,0x00,0x00,0x00,
+	0x41,0x7e,0x00,0x01, 0x00,0x00,0x00,0x03,
+	0x00,0x00,0x00,0x02, 0x00,0x24,0x00,0x15,
+	0x00,0x00,0x2c,0x05, 0x20,0x01,0x00,0x00,
+	0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
+
+	0x01,0x00,0x00,0x15, 0x00,0x00,0x00,0x40,
+
+	0x00,0x0c,0x43,0x60, 0x00,0x09,0x00,0x00,
+
+	0x00,0x00,0x00,0x00,
+
+		0x00,0x09,0x04,0x04,
+
+	0x05,0x40,0x01,0x01, 0x00
+};
+
+#define QETH_DM_ACT_DEST_ADDR(buffer) (buffer+0x2c)
+#define QETH_DM_ACT_CONNECTION_TOKEN(buffer) (buffer+0x51)
+
+#define IPA_CMD_STARTLAN 0x01
+#define IPA_CMD_STOPLAN 0x02
+#define IPA_CMD_SETIP 0xb1
+#define IPA_CMD_DELIP 0xb7
+#define IPA_CMD_QIPASSIST 0xb2
+#define IPA_CMD_SETASSPARMS 0xb3
+#define IPA_CMD_SETIPM 0xb4
+#define IPA_CMD_DELIPM 0xb5
+#define IPA_CMD_SETRTG 0xb6
+#define IPA_CMD_SETADAPTERPARMS 0xb8
+#define IPA_CMD_ADD_ADDR_ENTRY 0xc1
+#define IPA_CMD_DELETE_ADDR_ENTRY 0xc2
+#define IPA_CMD_CREATE_ADDR 0xc3
+#define IPA_CMD_DESTROY_ADDR 0xc4
+#define IPA_CMD_REGISTER_LOCAL_ADDR 0xd1
+#define IPA_CMD_UNREGISTER_LOCAL_ADDR 0xd2
+
+#define INITIATOR_HOST 0
+#define INITIATOR_HYDRA 1
+
+#define PRIM_VERSION_IPA 1
+
+#define PROT_VERSION_SNA 1
+#define PROT_VERSION_IPv4 4
+#define PROT_VERSION_IPv6 6
+
+#define OSA_ADDR_LEN 6
+#define IPA_SETADAPTERPARMS_IP_VERSION PROT_VERSION_IPv4
+#define SR_INFO_LEN 16
+
+#define IPA_ARP_PROCESSING 0x00000001L
+#define IPA_INBOUND_CHECKSUM 0x00000002L
+#define IPA_OUTBOUND_CHECKSUM 0x00000004L
+#define IPA_IP_FRAGMENTATION 0x00000008L
+#define IPA_FILTERING 0x00000010L
+#define IPA_IPv6 0x00000020L
+#define IPA_MULTICASTING 0x00000040L
+#define IPA_IP_REASSEMBLY 0x00000080L
+#define IPA_QUERY_ARP_COUNTERS 0x00000100L
+#define IPA_QUERY_ARP_ADDR_INFO 0x00000200L
+#define IPA_SETADAPTERPARMS 0x00000400L
+#define IPA_VLAN_PRIO 0x00000800L
+#define IPA_PASSTHRU 0x00001000L
+#define IPA_FULL_VLAN 0x00004000L
+#define IPA_SOURCE_MAC_AVAIL 0x00010000L
+
+#define IPA_SETADP_QUERY_COMMANDS_SUPPORTED 0x01
+#define IPA_SETADP_ALTER_MAC_ADDRESS 0x02
+#define IPA_SETADP_ADD_DELETE_GROUP_ADDRESS 0x04
+#define IPA_SETADP_ADD_DELETE_FUNCTIONAL_ADDR 0x08
+#define IPA_SETADP_SET_ADDRESSING_MODE 0x10
+#define IPA_SETADP_SET_CONFIG_PARMS 0x20
+#define IPA_SETADP_SET_CONFIG_PARMS_EXTENDED 0x40
+#define IPA_SETADP_SET_BROADCAST_MODE 0x80
+#define IPA_SETADP_SEND_OSA_MESSAGE 0x0100
+#define IPA_SETADP_SET_SNMP_CONTROL 0x0200
+#define IPA_SETADP_READ_SNMP_PARMS 0x0400
+#define IPA_SETADP_WRITE_SNMP_PARMS 0x0800
+#define IPA_SETADP_QUERY_CARD_INFO 0x1000
+
+#define CHANGE_ADDR_READ_MAC 0
+#define CHANGE_ADDR_REPLACE_MAC 1
+#define CHANGE_ADDR_ADD_MAC 2
+#define CHANGE_ADDR_DEL_MAC 4
+#define CHANGE_ADDR_RESET_MAC 8
+#define CHANGE_ADDR_READ_ADDR 0
+#define CHANGE_ADDR_ADD_ADDR 1
+#define CHANGE_ADDR_DEL_ADDR 2
+#define CHANGE_ADDR_FLUSH_ADDR_TABLE 4
+ 
+#define qeth_is_supported(str) (card->ipa_supported&str)
+#define qeth_is_supported6(str) (card->ipa6_supported&str)
+#define qeth_is_adp_supported(str) (card->adp_supported&str)
+
+#define IPA_CMD_ASS_START 0x0001
+#define IPA_CMD_ASS_STOP 0x0002
+
+#define IPA_CMD_ASS_CONFIGURE 0x0003
+#define IPA_CMD_ASS_ENABLE 0x0004
+
+#define IPA_CMD_ASS_ARP_SET_NO_ENTRIES 0x0003
+#define IPA_CMD_ASS_ARP_QUERY_CACHE 0x0004
+#define IPA_CMD_ASS_ARP_ADD_ENTRY 0x0005
+#define IPA_CMD_ASS_ARP_REMOVE_ENTRY 0x0006
+#define IPA_CMD_ASS_ARP_FLUSH_CACHE 0x0007
+#define IPA_CMD_ASS_ARP_QUERY_INFO 0x0104
+#define IPA_CMD_ASS_ARP_QUERY_STATS 0x0204
+
+#define IPA_CHECKSUM_ENABLE_MASK 0x001f
+
+#define IPA_CMD_ASS_FILTER_SET_TYPES 0x0003
+
+#define IPA_CMD_ASS_IPv6_SET_FUNCTIONS 0x0003
+
+#define IPA_REPLY_SUCCESS 0
+#define IPA_REPLY_FAILED 1
+#define IPA_REPLY_OPNOTSUPP 2
+#define IPA_REPLY_OPNOTSUPP2 4
+#define IPA_REPLY_NOINFO 8
+
+#define IPA_SETIP_FLAGS 0
+#define IPA_SETIP_VIPA_FLAGS 1
+#define IPA_SETIP_TAKEOVER_FLAGS 2
+
+#define VIPA_2_B_ADDED 0
+#define VIPA_ESTABLISHED 1
+#define VIPA_2_B_REMOVED 2
+
+#define IPA_DELIP_FLAGS 0
+
+#define IPA_SETADP_CMDSIZE 40
+
+struct ipa_setadp_cmd {
+	__u32 supp_hw_cmds;
+	__u32 reserved1;
+	__u16 cmdlength;
+	__u16 reserved2;
+	__u32 command_code;
+	__u16 return_code;
+	__u8 frames_used_total;
+	__u8 frame_seq_no;
+	__u32 reserved3;
+	union {
+		struct {
+			__u32 no_lantypes_supp;
+			__u8 lan_type;
+			__u8 reserved1[3];
+			__u32 supported_cmds;
+			__u8 reserved2[8];
+		} query_cmds_supp;
+		struct {
+			__u32 cmd;
+			__u32 addr_size;
+			__u32 no_macs;
+			__u8 addr[OSA_ADDR_LEN];
+		} change_addr;
+		__u32 mode;
+	} data;
+};
+
+typedef struct ipa_cmd_t {
+	__u8 command;
+	__u8 initiator;
+	__u16 seq_no;
+	__u16 return_code;
+	__u8 adapter_type;
+	__u8 rel_adapter_no;
+	__u8 prim_version_no;
+	__u8 param_count;
+	__u16 prot_version;
+	__u32 ipa_supported;
+	__u32 ipa_enabled;
+	union {
+		struct {
+			__u8 ip[4];
+			__u8 netmask[4];
+			__u32 flags;
+		} setdelip4;
+		struct {
+			__u8 ip[16];
+			__u8 netmask[16];
+			__u32 flags;
+		} setdelip6;
+		struct {
+			__u32 assist_no;
+			__u16 length;
+			__u16 command_code;
+			__u16 return_code;
+			__u8 number_of_replies;
+			__u8 seq_no;
+			union {
+				__u32 flags_32bit;
+				struct {
+					__u8 mac[6];
+					__u8 reserved[2];
+					__u8 ip[16];
+					__u8 reserved2[32];
+				} add_arp_entry;
+				__u8 ip[16];
+			} data;
+		} setassparms;
+		struct {
+			__u8 mac[6];
+			__u8 padding[2];
+			__u8 ip6[12];
+			__u8 ip4_6[4];
+		} setdelipm;
+		struct {
+			__u8 type;
+		} setrtg;
+		struct ipa_setadp_cmd setadapterparms;
+		struct {
+			__u32 command;
+#define ADDR_FRAME_TYPE_DIX 1
+#define ADDR_FRAME_TYPE_802_3 2
+#define ADDR_FRAME_TYPE_TR_WITHOUT_SR 0x10
+#define ADDR_FRAME_TYPE_TR_WITH_SR 0x20
+			__u32 frame_type;
+			__u32 cmd_flags;
+			__u8 ip_addr[16];
+			__u32 tag_field;
+			__u8 mac_addr[6];
+			__u8 reserved[10];
+			__u32 sr_len;
+			__u8 sr_info[SR_INFO_LEN];
+		} add_addr_entry;
+		struct {
+			__u32 command;
+			__u32 cmd_flags;
+			__u8 ip_addr[16];
+			__u32 tag_field;
+		} delete_addr_entry;
+		struct {
+			__u8 unique_id[8];
+		} create_destroy_addr;
+	} 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_GET_OID         0x00000010L
+#define SNMP_SET_OID         0x00000011L
+#define SNMP_GET_NEXT_OID    0x00000012L
+#define SNMP_QUERY_ALERTS    0x00000020L
+#define SNMP_SET_TRAP        0x00000021L
+
+
+#define ARP_DATA_SIZE 3968
+#define ARP_FLUSH -3
+#define ARP_RETURNCODE_NOARPDATA -2
+#define ARP_RETURNCODE_ERROR -1
+#define ARP_RETURNCODE_SUCCESS 0
+#define ARP_RETURNCODE_LASTREPLY 1
+
+#define SNMP_BASE_CMDLENGTH 44
+#define SNMP_SETADP_CMDLENGTH 16
+#define SNMP_REQUEST_DATA_OFFSET 16
+
+typedef struct snmp_ipa_setadp_cmd_t {
+	__u32 supp_hw_cmds;
+	__u32 reserved1;
+	__u16 cmdlength;
+	__u16 reserved2;
+	__u32 command_code;
+	__u16 return_code;
+	__u8 frames_used_total;
+	__u8 frame_seq_no;
+	__u32 reserved3;
+	__u8 snmp_token[16];
+	union {
+		struct {
+			__u32 snmp_request;
+			__u32 snmp_interface;
+			__u32 snmp_returncode;
+			__u32 snmp_firmwarelevel;
+			__u32 snmp_seqno;
+			__u8 snmp_data[ARP_DATA_SIZE];
+		} snmp_subcommand;
+	} data;
+} snmp_ipa_setadp_cmd_t __attribute__ ((packed));
+
+typedef struct arp_cmd_t {
+	__u8 command;
+	__u8 initiator;
+	__u16 seq_no;
+	__u16 return_code;
+	__u8 adapter_type;
+	__u8 rel_adapter_no;
+	__u8 prim_version_no;
+	__u8 param_count;
+	__u16 prot_version;
+	__u32 ipa_supported;
+	__u32 ipa_enabled;
+	union {
+		struct {
+			__u32 assist_no;
+			__u16 length;
+			__u16 command_code;
+			__u16 return_code;
+			__u8 number_of_replies;
+			__u8 seq_no;
+			union {
+				struct {
+					__u16 tcpip_requestbitmask;
+					__u16 osa_setbitmask;
+					__u32 number_of_entries;
+					__u8 arp_data[ARP_DATA_SIZE];
+				} queryarp_data;
+			} data;
+		} setassparms;
+                snmp_ipa_setadp_cmd_t setadapterparms; 
+	} data;
+} arp_cmd_t __attribute__ ((packed));
+
+
+
+#define IPA_PDU_HEADER_SIZE 0x40
+#define QETH_IPA_PDU_LEN_TOTAL(buffer) (buffer+0x0e)
+#define QETH_IPA_PDU_LEN_PDU1(buffer) (buffer+0x26)
+#define QETH_IPA_PDU_LEN_PDU2(buffer) (buffer+0x2a)
+#define QETH_IPA_PDU_LEN_PDU3(buffer) (buffer+0x3a)
+
+static unsigned char IPA_PDU_HEADER[]={
+	0x00,0xe0,0x00,0x00, 0x77,0x77,0x77,0x77,
+	0x00,0x00,0x00,0x14, 0x00,0x00,
+		(IPA_PDU_HEADER_SIZE+sizeof(ipa_cmd_t))/256,
+		(IPA_PDU_HEADER_SIZE+sizeof(ipa_cmd_t))%256,
+	0x10,0x00,0x00,0x01,
+
+		0x00,0x00,0x00,0x00,
+	0xc1,0x03,0x00,0x01, 0x00,0x00,0x00,0x00,
+	0x00,0x00,0x00,0x00, 0x00,0x24,0x00,sizeof(ipa_cmd_t),
+	0x00,0x00,sizeof(ipa_cmd_t),0x05, 0x77,0x77,0x77,0x77,
+	0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
+	0x01,0x00,sizeof(ipa_cmd_t)/256,sizeof(ipa_cmd_t)%256,
+				0x00,0x00,0x00,0x40,
+};
+
+#define QETH_IPA_CMD_DEST_ADDR(buffer) (buffer+0x2c)
+
+#define PDU_ENCAPSULATION(buffer) \
+	(buffer+ \
+	 *(buffer+ (*(buffer+0x0b))+ *(buffer+*(buffer+0x0b)+0x11) +0x07))
+
+#define IS_IPA(buffer) ((buffer) && ( *(buffer+ ((*(buffer+0x0b))+4) )==0xc1) )
+
+#define IS_IPA_REPLY(buffer) ( (buffer) && ( (*(PDU_ENCAPSULATION(buffer)+1)) \
+					     ==INITIATOR_HOST ) )
+
+#define IS_ADDR_IPA(buffer) ( (buffer) && ( \
+	( ((ipa_cmd_t*)PDU_ENCAPSULATION(buffer))->command== \
+	  IPA_CMD_ADD_ADDR_ENTRY ) || \
+	( ((ipa_cmd_t*)PDU_ENCAPSULATION(buffer))->command== \
+	  IPA_CMD_DELETE_ADDR_ENTRY ) ) )
+
+#define CCW_NOP_CMD 0x03
+#define CCW_NOP_COUNT 1
+
+static unsigned char WRITE_CCW[]={
+	0x01,CCW_FLAG_SLI,0,0,
+	0,0,0,0
+};
+
+static unsigned char READ_CCW[]={
+	0x02,CCW_FLAG_SLI,0,0,
+	0,0,0,0
+};
+
+#endif /* __QETH_MPC_H__ */
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -urN linux-2.4.21-bk9/drivers/s390/qdio.c linux-2.4.21-bk10/drivers/s390/qdio.c
--- linux-2.4.21-bk9/drivers/s390/qdio.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/drivers/s390/qdio.c	2003-07-15 02:55:06.000000000 -0700
@@ -0,0 +1,3625 @@
+/*
+ *
+ * linux/drivers/s390/qdio.c
+ *
+ * Linux for S/390 QDIO base support, Hipersocket base support
+ * version 2
+ *
+ * Copyright 2000,2002 IBM Corporation
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ *
+ * Restriction: only 63 iqdio subchannels would have its own indicator,
+ * after that, subsequent subchannels share one indicator
+ *
+ *
+ *
+ *
+ * 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.
+ */
+
+/* we want the eyecatcher to be at the top of the code */
+void volatile qdio_eyecatcher(void)
+{
+	return;
+}
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+#include <asm/page.h>
+
+#include <asm/debug.h>
+
+#include <asm/qdio.h>
+
+#define VERSION_QDIO_C "$Revision: 1.145 $"
+
+/****************** MODULE PARAMETER VARIABLES ********************/
+MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
+MODULE_DESCRIPTION("QDIO base support version 2, " \
+		   "Copyright 2000 IBM Corporation");
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
+MODULE_LICENSE("GPL");
+#endif
+
+/******************** HERE WE GO ***********************************/
+
+static const char *version="QDIO base support version 2 ("
+	VERSION_QDIO_C "/" VERSION_QDIO_H ")";
+
+#ifdef QDIO_PERFORMANCE_STATS
+static int proc_perf_file_registration;
+unsigned long i_p_c=0,i_p_nc=0,o_p_c=0,o_p_nc=0,ii_p_c=0,ii_p_nc=0;
+
+static struct {
+	unsigned int tl_runs;
+
+	unsigned int siga_outs;
+	unsigned int siga_ins;
+	unsigned int siga_syncs;
+	unsigned int pcis;
+	unsigned int thinints;
+	unsigned int fast_reqs;
+
+	__u64 start_time_outbound;
+	unsigned int outbound_cnt;
+	unsigned int outbound_time;
+	__u64 start_time_inbound;
+	unsigned int inbound_cnt;
+	unsigned int inbound_time;
+} perf_stats;
+
+#endif /* QDIO_PERFORMANCE_STATS */
+
+static int hydra_thinints=0;
+
+static int indicator_used[INDICATORS_PER_CACHELINE];
+static __u32 * volatile indicators;
+static __u32 volatile spare_indicator;
+
+static debug_info_t *qdio_dbf_setup=NULL;
+static debug_info_t *qdio_dbf_sbal=NULL;
+static debug_info_t *qdio_dbf_trace=NULL;
+static debug_info_t *qdio_dbf_sense=NULL;
+#ifdef QDIO_DBF_LIKE_HELL
+static debug_info_t *qdio_dbf_slsb_out=NULL;
+static debug_info_t *qdio_dbf_slsb_in=NULL;
+#endif /* QDIO_DBF_LIKE_HELL */
+
+static qdio_irq_t *first_irq[QDIO_IRQ_BUCKETS]={
+	[0 ... (QDIO_IRQ_BUCKETS-1)] = NULL
+};
+static rwlock_t irq_list_lock[QDIO_IRQ_BUCKETS]={
+	[0 ... (QDIO_IRQ_BUCKETS-1)] = RW_LOCK_UNLOCKED
+};
+
+static struct semaphore init_sema;
+
+static qdio_chsc_area_t *chsc_area;
+/* iQDIO stuff: */
+static volatile qdio_q_t *tiq_list=NULL; /* volatile as it could change
+					   during a while loop */
+static spinlock_t ttiq_list_lock=SPIN_LOCK_UNLOCKED;
+static int register_thinint_result;
+static void tiqdio_tl(unsigned long);
+static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0);
+
+#define HEXDUMP16(importance,header,ptr) \
+QDIO_PRINT_##importance(header "%02x %02x %02x %02x  " \
+			"%02x %02x %02x %02x  %02x %02x %02x %02x  " \
+			"%02x %02x %02x %02x\n",*(((char*)ptr)), \
+			*(((char*)ptr)+1),*(((char*)ptr)+2), \
+			*(((char*)ptr)+3),*(((char*)ptr)+4), \
+			*(((char*)ptr)+5),*(((char*)ptr)+6), \
+			*(((char*)ptr)+7),*(((char*)ptr)+8), \
+			*(((char*)ptr)+9),*(((char*)ptr)+10), \
+			*(((char*)ptr)+11),*(((char*)ptr)+12), \
+			*(((char*)ptr)+13),*(((char*)ptr)+14), \
+			*(((char*)ptr)+15)); \
+QDIO_PRINT_##importance(header "%02x %02x %02x %02x  %02x %02x %02x %02x  " \
+			"%02x %02x %02x %02x  %02x %02x %02x %02x\n", \
+			*(((char*)ptr)+16),*(((char*)ptr)+17), \
+			*(((char*)ptr)+18),*(((char*)ptr)+19), \
+			*(((char*)ptr)+20),*(((char*)ptr)+21), \
+			*(((char*)ptr)+22),*(((char*)ptr)+23), \
+			*(((char*)ptr)+24),*(((char*)ptr)+25), \
+			*(((char*)ptr)+26),*(((char*)ptr)+27), \
+			*(((char*)ptr)+28),*(((char*)ptr)+29), \
+			*(((char*)ptr)+30),*(((char*)ptr)+31));
+
+#define atomic_swap(a,b) xchg((int*)a.counter,b)
+
+/* not a macro, as one of the arguments is atomic_read */
+static inline int qdio_min(int a,int b)
+{
+	if (a<b)
+		return a;
+	else
+		return b;
+}
+
+/* unlikely as the later the better */
+#define SYNC_MEMORY if (unlikely(q->siga_sync)) qdio_siga_sync_q(q)
+#define SYNC_MEMORY_ALL if (unlikely(q->siga_sync)) \
+	qdio_siga_sync(q,~0U,~0U)
+#define SYNC_MEMORY_ALL_OUTB if (unlikely(q->siga_sync)) \
+	qdio_siga_sync(q,~0U,0)
+
+#define NOW qdio_get_micros()
+#define SAVE_TIMESTAMP(q) q->timing.last_transfer_time=NOW
+#define GET_SAVED_TIMESTAMP(q) (q->timing.last_transfer_time)
+#define SAVE_FRONTIER(q,val) q->last_move_ftc=val
+#define GET_SAVED_FRONTIER(q) (q->last_move_ftc)
+
+#define MY_MODULE_STRING(x) #x
+
+#ifdef QDIO_32_BIT
+#define QDIO_GET_32BIT_ADDR(x) ((__u32)(long)x)
+#else /* QDIO_32_BIT */
+#define QDIO_GET_32BIT_ADDR(x) ((__u32)(unsigned long)x)
+#endif /* QDIO_32_BIT */
+
+#define QDIO_PFIX_GET_ADDR(x) ((flags&QDIO_PFIX) ? \
+			       pfix_get_addr(x):((unsigned long)x))
+
+/***************** SCRUBBER HELPER ROUTINES **********************/
+
+static inline volatile __u64 qdio_get_micros(void)
+{
+        __u64 time;
+
+        asm volatile ("STCK %0" : "=m" (time));
+        return time>>12; /* time>>12 is microseconds*/
+}
+static inline unsigned long qdio_get_millis(void)
+{
+	return (unsigned long)(qdio_get_micros()>>12);
+}
+
+static __inline__ int atomic_return_add (int i, atomic_t *v)
+{
+	int old, new;
+	__CS_LOOP(old, new, v, i, "ar");
+	return old;
+}
+
+static void qdio_wait_nonbusy(unsigned int timeout)
+{
+        unsigned int start;
+        char dbf_text[15];
+
+	sprintf(dbf_text,"wtnb%4x",timeout);
+	QDIO_DBF_TEXT3(0,trace,dbf_text);
+
+	start=qdio_get_millis();
+	for (;;) {
+		set_task_state(current,TASK_INTERRUPTIBLE);
+		if (qdio_get_millis()-start>timeout) {
+			goto out;
+		}
+		schedule_timeout(((start+timeout-qdio_get_millis())>>10)*HZ);
+	}
+out:
+	set_task_state(current,TASK_RUNNING);
+}
+
+static int qdio_wait_for_no_use_count(atomic_t *use_count)
+{
+	unsigned long start;
+
+	QDIO_DBF_TEXT3(0,trace,"wtnousec");
+	start=qdio_get_millis();
+	for (;;) {
+		if (qdio_get_millis()-start>QDIO_NO_USE_COUNT_TIMEOUT) {
+			QDIO_DBF_TEXT1(1,trace,"WTNOUSTO");
+			return -ETIME;
+		}
+		if (!atomic_read(use_count)) {
+			QDIO_DBF_TEXT3(0,trace,"wtnoused");
+			return 0;
+		}
+		qdio_wait_nonbusy(QDIO_NO_USE_COUNT_TIME);
+	}
+}
+
+/* unfortunately, we can't just xchg the values; in do_QDIO we want to reserve
+ * the q in any case, so that we'll not be interrupted when we are in
+ * qdio_mark_tiq... shouldn't have a really bad impact, as reserving almost
+ * ever works (last famous words) */
+static inline int qdio_reserve_q(qdio_q_t *q)
+{
+	return atomic_return_add(1,&q->use_count);
+}
+
+static inline void qdio_release_q(qdio_q_t *q)
+{
+	atomic_dec(&q->use_count);
+}
+#ifdef QDIO_DBF_LIKE_HELL 
+#define set_slsb(x,y) \
+  if(q->queue_type==QDIO_TRACE_QTYPE) { \
+        if(q->is_input_q) { \
+            QDIO_DBF_HEX2(0,slsb_in,&q->slsb,QDIO_MAX_BUFFERS_PER_Q); \
+        } else { \
+            QDIO_DBF_HEX2(0,slsb_out,&q->slsb,QDIO_MAX_BUFFERS_PER_Q); \
+        } \
+  } \
+  qdio_set_slsb(x,y); \
+  if(q->queue_type==QDIO_TRACE_QTYPE) { \
+        if(q->is_input_q) { \
+            QDIO_DBF_HEX2(0,slsb_in,&q->slsb,QDIO_MAX_BUFFERS_PER_Q); \
+        } else { \
+            QDIO_DBF_HEX2(0,slsb_out,&q->slsb,QDIO_MAX_BUFFERS_PER_Q); \
+        } \
+  }
+#else /* QDIO_DBF_LIKE_HELL */
+#define set_slsb(x,y) qdio_set_slsb(x,y)
+#endif /* QDIO_DBF_LIKE_HELL */
+static volatile inline void qdio_set_slsb(volatile char *slsb,
+					  unsigned char value)
+{
+	xchg((char*)slsb,value);
+}
+
+static inline int qdio_siga_sync(qdio_q_t *q,
+				 unsigned int gpr2,
+				 unsigned int gpr3)
+{
+	int cc;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"sigasync");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(int));
+	QDIO_DBF_HEX4(0,trace,&gpr2,sizeof(int));
+	QDIO_DBF_HEX4(0,trace,&gpr3,sizeof(int));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_PERFORMANCE_STATS
+	perf_stats.siga_syncs++;
+#endif /* QDIO_PERFORMANCE_STATS */
+
+#ifdef QDIO_32_BIT
+	asm volatile (
+		"lhi	0,2	\n\t"
+		"lr	1,%1	\n\t"
+		"lr	2,%2	\n\t"
+		"lr	3,%3	\n\t"
+		"siga   0	\n\t"
+		"ipm	%0	\n\t"
+		"srl	%0,28	\n\t"
+		: "=d" (cc)
+		: "d" (0x10000|q->irq), "d" (gpr2), "d" (gpr3)
+		: "cc", "0", "1", "2", "3"
+		);
+#else /* QDIO_32_BIT */
+	asm volatile (
+		"lghi	0,2	\n\t"
+		"llgfr	1,%1	\n\t"
+		"llgfr	2,%2	\n\t"
+		"llgfr	3,%3	\n\t"
+		"siga   0	\n\t"
+		"ipm	%0	\n\t"
+		"srl	%0,28	\n\t"
+		: "=d" (cc)
+		: "d" (0x10000|q->irq), "d" (gpr2), "d" (gpr3)
+		: "cc", "0", "1", "2", "3"
+		);
+#endif /* QDIO_32_BIT */
+
+	if (cc) {
+#ifndef QDIO_DBF_LIKE_HELL
+		/* when QDIO_DBF_LIKE_HELL, we put that already out */
+		QDIO_DBF_TEXT3(0,trace,"sigasync");
+		QDIO_DBF_HEX3(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+		QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));
+	}
+
+	return cc;
+}
+
+static inline int qdio_siga_sync_q(qdio_q_t *q)
+{
+	if (q->is_input_q) {
+		return qdio_siga_sync(q,0,q->mask);
+	} else {
+		return qdio_siga_sync(q,q->mask,0);
+	}
+}
+
+/* returns QDIO_SIGA_ERROR_ACCESS_EXCEPTION as cc, when SIGA returns
+   an access exception */
+static inline int qdio_siga_output(qdio_q_t *q)
+{
+	int cc;
+	__u32 busy_bit;
+	__u64 start_time=0;
+
+#ifdef QDIO_PERFORMANCE_STATS
+	perf_stats.siga_outs++;
+#endif /* QDIO_PERFORMANCE_STATS */
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"sigaout");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	for (;;) {
+
+#ifdef QDIO_32_BIT
+		asm volatile (
+		      "lhi	0,0	\n\t"
+		      "lr	1,%2	\n\t"
+		      "lr	2,%3	\n\t"
+		      "siga	0	\n\t"
+		      "0:"
+		      "ipm	%0	\n\t"
+		      "srl	%0,28	\n\t"
+		      "srl	0,31	\n\t"
+		      "lr	%1,0	\n\t"
+		      "1:	\n\t"
+		      ".section .fixup,\"ax\"\n\t"
+		      "2:	\n\t"
+		      "lhi	%0,%4	\n\t"
+		      "bras	1,3f	\n\t"
+		      ".long 1b	\n\t"
+		      "3:	\n\t"
+		      "l	1,0(1)	\n\t"
+		      "br	1	\n\t"
+		      ".previous	\n\t"
+		      ".section __ex_table,\"a\"\n\t"
+		      ".align 4	\n\t"
+		      ".long	0b,2b	\n\t"
+		      ".previous	\n\t"
+			: "=d" (cc), "=d" (busy_bit)
+			: "d" (0x10000|q->irq), "d" (q->mask),
+			"i" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION)
+			: "cc", "0", "1", "2", "memory"
+		);
+#else /* QDIO_32_BIT */
+		asm volatile (
+		      "lghi	0,0	\n\t"
+		      "llgfr	1,%2	\n\t"
+		      "llgfr	2,%3	\n\t"
+	      	      "siga	0	\n\t"
+		      "0:"
+		      "ipm	%0	\n\t"
+		      "srl	%0,28	\n\t"
+		      "srl	0,31	\n\t"
+		      "llgfr	%1,0	\n\t"
+		      "1:	\n\t"
+		      ".section .fixup,\"ax\"\n\t"
+		      "lghi	%0,%4	\n\t"
+		      "jg	1b	\n\t"
+		      ".previous\n\t"
+      		      ".section __ex_table,\"a\"\n\t"
+      		      ".align 8	\n\t"
+      		      ".quad	0b,1b	\n\t"
+      		      ".previous	\n\t"
+		      : "=d" (cc), "=d" (busy_bit)
+		      : "d" (0x10000|q->irq), "d" (q->mask),
+	      	      "i" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION)
+	      	      : "cc", "0", "1", "2", "memory"
+	     );
+#endif /* QDIO_32_BIT */
+
+//QDIO_PRINT_ERR("cc=%x, busy=%x\n",cc,busy_bit);
+	     if ( (cc==2) && (busy_bit) &&
+		  (q->is_iqdio_q) ) {
+		     if (!start_time) start_time=NOW;
+		     if ((NOW-start_time)>QDIO_BUSY_BIT_PATIENCE)
+			     break;
+	     } else
+		     break;
+	}
+
+	if ((cc==2) && (busy_bit)) cc|=QDIO_SIGA_ERROR_B_BIT_SET;
+
+	if (cc) {
+#ifndef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT3(0,trace,"sigaout");
+		QDIO_DBF_HEX3(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+		QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));
+	}
+
+	return cc;
+}
+
+static inline int qdio_siga_input(qdio_q_t *q)
+{
+	int cc;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"sigain");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_PERFORMANCE_STATS
+	perf_stats.siga_ins++;
+#endif /* QDIO_PERFORMANCE_STATS */
+
+#ifdef QDIO_32_BIT
+	asm volatile (
+		"lhi	0,1	\n\t"
+		"lr	1,%1	\n\t"
+		"lr	2,%2	\n\t"
+		"siga   0	\n\t"
+		"ipm	%0	\n\t"
+		"srl	%0,28	\n\t"
+		: "=d" (cc)
+		: "d" (0x10000|q->irq), "d" (q->mask)
+		: "cc", "0", "1", "2", "memory"
+		);
+#else /* QDIO_32_BIT */
+	asm volatile (
+		"lghi	0,1	\n\t"
+		"llgfr	1,%1	\n\t"
+		"llgfr	2,%2	\n\t"
+		"siga   0	\n\t"
+		"ipm	%0	\n\t"
+		"srl	%0,28	\n\t"
+		: "=d" (cc)
+		: "d" (0x10000|q->irq), "d" (q->mask)
+		: "cc", "0", "1", "2", "memory"
+		);
+#endif /* QDIO_32_BIT */
+
+	if (cc) {
+#ifndef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT3(0,trace,"sigain");
+		QDIO_DBF_HEX3(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+		QDIO_DBF_HEX3(0,trace,&cc,sizeof(int*));
+	}
+
+	return cc;
+}
+
+/* locked by the locks in qdio_activate and qdio_cleanup */
+static __u32 * volatile qdio_get_indicator(void)
+{
+	int i=1;
+	int found=0;
+
+	while (i<INDICATORS_PER_CACHELINE) {
+		if (!indicator_used[i]) {
+			indicator_used[i]=1;
+			found=1;
+			break;
+		}
+		i++;
+	}
+
+	if (found)
+		return indicators+i;
+	else
+		return (__u32 * volatile) &spare_indicator;
+}
+
+/* locked by the locks in qdio_activate and qdio_cleanup */
+static void qdio_put_indicator(__u32 *addr)
+{
+	int i;
+
+	if ( (addr) && (addr!=&spare_indicator) ) {
+		i=addr-indicators;
+		indicator_used[i]=0;
+	}
+}
+
+static inline volatile void tiqdio_clear_summary_bit(__u32 *location)
+{
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT5(0,trace,"clrsummb");
+	QDIO_DBF_HEX5(0,trace,&location,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+	xchg(location,0);
+}
+
+static inline volatile void tiqdio_set_summary_bit(__u32 *location)
+{
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT5(0,trace,"setsummb");
+	QDIO_DBF_HEX5(0,trace,&location,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+	xchg(location,-1);
+}
+
+static inline void tiqdio_sched_tl(void)
+{
+	tasklet_hi_schedule(&tiqdio_tasklet);
+}
+
+static inline void qdio_mark_tiq(qdio_q_t *q)
+{
+	unsigned long flags;
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"mark iq");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	spin_lock_irqsave(&ttiq_list_lock,flags);
+	if (unlikely(atomic_read(&q->is_in_shutdown))) goto out_unlock;
+
+	if (q->is_input_q) {
+		if ((q->list_prev) || (q->list_next)) goto out_unlock;
+
+		if (!tiq_list) {
+			tiq_list=q;
+			q->list_prev=q;
+			q->list_next=q;
+		} else {
+			q->list_next=tiq_list;
+			q->list_prev=tiq_list->list_prev;
+			tiq_list->list_prev->list_next=q;
+			tiq_list->list_prev=q;
+		}
+		spin_unlock_irqrestore(&ttiq_list_lock,flags);
+
+		tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);
+		tiqdio_sched_tl();
+	}
+	return;
+out_unlock:
+	spin_unlock_irqrestore(&ttiq_list_lock,flags);
+	return;
+}
+
+static inline void qdio_mark_q(qdio_q_t *q)
+{
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"mark q");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	if (unlikely(atomic_read(&q->is_in_shutdown))) return;
+
+	tasklet_schedule(&q->tasklet);
+}
+
+static inline int qdio_stop_polling(qdio_q_t *q)
+{
+#ifdef QDIO_USE_PROCESSING_STATE
+	int gsf;
+
+	if (!atomic_swap(&q->polling,0)) return 1;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"stoppoll");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	/* show the card that we are not polling anymore */
+	if (q->is_input_q) {
+		gsf=GET_SAVED_FRONTIER(q);
+		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
+		SYNC_MEMORY;*/
+		if (q->slsb.acc.val[gsf]==SLSB_P_INPUT_PRIMED) {
+			/* set our summary bit again, as otherwise there is a
+			 * small window we can miss between resetting it and
+			 * checking for PRIMED state */
+			if (q->is_thinint_q)
+				tiqdio_set_summary_bit
+					((__u32*)q->dev_st_chg_ind);
+			return 0;
+		}
+	}
+#endif /* QDIO_USE_PROCESSING_STATE */
+	return 1;
+}
+
+/* see the comment in do_QDIO and before qdio_reserve_q about the
+ * sophisticated locking outside of unmark_q, so that we don't need to
+ * disable the interrupts :-) */
+static inline void qdio_unmark_q(qdio_q_t *q)
+{
+	unsigned long flags;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"unmark q");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	if ((!q->list_prev)||(!q->list_next)) return;
+
+	if ((q->is_thinint_q)&&(q->is_input_q)) {
+		/* iQDIO */
+		spin_lock_irqsave(&ttiq_list_lock,flags);
+		if (q->list_next==q) {
+			/* q was the only interesting q */
+			tiq_list=NULL;
+			q->list_next=NULL;
+			q->list_prev=NULL;
+		} else {
+			q->list_next->list_prev=q->list_prev;
+			q->list_prev->list_next=q->list_next;
+			tiq_list=q->list_next;
+			q->list_next=NULL;
+			q->list_prev=NULL;
+		}
+		spin_unlock_irqrestore(&ttiq_list_lock,flags);
+	}
+}
+
+static inline volatile unsigned long tiqdio_clear_global_summary(void)
+{
+	unsigned long time;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT5(0,trace,"clrglobl");
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_32_BIT
+	asm volatile (
+		"lhi	1,3	\n\t"
+		".insn	rre,0xb2650000,2,0	\n\t"
+		"lr	%0,3	\n\t"
+		: "=d" (time) : : "cc", "1", "2", "3"
+		);
+#else /* QDIO_32_BIT */
+	asm volatile (
+		"lghi	1,3	\n\t"
+		".insn	rre,0xb2650000,2,0	\n\t"
+		"lgr	%0,3	\n\t"
+		: "=d" (time) : : "cc", "1", "2", "3"
+		);
+#endif /* QDIO_32_BIT */
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_HEX5(0,trace,&time,sizeof(unsigned long));
+#endif /* QDIO_DBF_LIKE_HELL */
+	return time;
+}
+
+/************************* OUTBOUND ROUTINES *******************************/
+
+static inline void qdio_translate_buffer_back(qdio_q_t *q,int bufno)
+{
+	if (unlikely(!q->is_0copy_sbals_q))
+		memcpy(q->qdio_buffers[bufno],
+		       (void*)q->sbal[bufno],SBAL_SIZE);
+}
+
+inline static int qdio_get_outbound_buffer_frontier(qdio_q_t *q)
+{
+	int f,f_mod_no;
+	volatile char *slsb;
+	int first_not_to_check;
+	char dbf_text[15];
+	char slsbyte;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"getobfro");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	slsb=&q->slsb.acc.val[0];
+	f_mod_no=f=q->first_to_check;
+	/* f point to already processed elements, so f+no_used is correct...
+	 * ... but: we don't check 128 buffers, as otherwise
+	 * qdio_has_outbound_q_moved would return 0 */
+	first_not_to_check=f+qdio_min(atomic_read(&q->number_of_buffers_used),
+				      (QDIO_MAX_BUFFERS_PER_Q-1));
+
+	if ((!q->is_iqdio_q)&&(!q->hydra_gives_outbound_pcis)) {
+		SYNC_MEMORY;
+	}
+
+check_next:
+	if (f==first_not_to_check) goto out;
+	slsbyte=slsb[f_mod_no];
+
+	/* the hydra has not fetched the output yet */
+	if (slsbyte==SLSB_CU_OUTPUT_PRIMED) {
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT5(0,trace,"outpprim");
+#endif /* QDIO_DBF_LIKE_HELL */
+		goto out;
+	}
+
+	/* the hydra got it */
+	if (slsbyte==SLSB_P_OUTPUT_EMPTY) {
+		atomic_dec(&q->number_of_buffers_used);
+		f++;
+		f_mod_no=f&(QDIO_MAX_BUFFERS_PER_Q-1);
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT5(0,trace,"outpempt");
+#endif /* QDIO_DBF_LIKE_HELL */
+		goto check_next;
+	}
+
+	if (slsbyte==SLSB_P_OUTPUT_ERROR) {
+		QDIO_DBF_TEXT3(0,trace,"outperr");
+		sprintf(dbf_text,"%x-%x-%x",f_mod_no,
+			q->sbal[f_mod_no]->element[14].sbalf.value,
+			q->sbal[f_mod_no]->element[15].sbalf.value);
+		QDIO_DBF_TEXT3(1,trace,dbf_text);
+		QDIO_DBF_HEX2(1,sbal,q->sbal[f_mod_no],256);
+
+		/* kind of process the buffer */
+		set_slsb(&q->slsb.acc.val[f_mod_no],
+			 SLSB_P_OUTPUT_NOT_INIT);
+
+		qdio_translate_buffer_back(q,f_mod_no);
+
+		/* we increment the frontier, as this buffer
+		 * was processed obviously */
+		atomic_dec(&q->number_of_buffers_used);
+		f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+
+		if (q->qdio_error)
+			q->error_status_flags|=
+				QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR;
+		q->qdio_error=SLSB_P_OUTPUT_ERROR;
+		q->error_status_flags|=QDIO_STATUS_LOOK_FOR_ERROR;
+
+		goto out;
+	}
+
+	/* no new buffers */
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT5(0,trace,"outpni");
+#endif /* QDIO_DBF_LIKE_HELL */
+	goto out;
+
+out:
+	return (q->first_to_check=f_mod_no);
+}
+
+/* all buffers are processed */
+inline static int qdio_is_outbound_q_done(qdio_q_t *q)
+{
+	int no_used;
+#ifdef QDIO_DBF_LIKE_HELL
+	char dbf_text[15];
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	no_used=atomic_read(&q->number_of_buffers_used);
+
+#ifdef QDIO_DBF_LIKE_HELL
+	if (no_used) {
+		sprintf(dbf_text,"oqisnt%02x",no_used);
+		QDIO_DBF_TEXT4(0,trace,dbf_text);
+	} else {
+		QDIO_DBF_TEXT4(0,trace,"oqisdone");
+	}
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+	return (no_used==0);
+}
+
+inline static int qdio_has_outbound_q_moved(qdio_q_t *q)
+{
+	int i;
+
+	i=qdio_get_outbound_buffer_frontier(q);
+
+	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*));
+#endif /* QDIO_DBF_LIKE_HELL */
+		return 1;
+	} else {
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT4(0,trace,"oqhsntmv");
+		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+		return 0;
+	}
+}
+
+inline static void qdio_kick_outbound_q(qdio_q_t *q)
+{
+	int result;
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"kickoutq");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	if (!q->siga_out) return;
+
+	result=qdio_siga_output(q);
+
+	if (result) {
+		if (q->siga_error)
+			q->error_status_flags|=
+				QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;
+		q->error_status_flags|=
+			QDIO_STATUS_LOOK_FOR_ERROR;
+		q->siga_error=result;
+	}
+}
+
+inline static void qdio_kick_outbound_handler(qdio_q_t *q)
+{
+#ifdef QDIO_DBF_LIKE_HELL
+	char dbf_text[15];
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	int start=q->first_element_to_kick;
+	/* last_move_ftc was just updated */
+	int real_end=GET_SAVED_FRONTIER(q);
+	int end=(real_end+QDIO_MAX_BUFFERS_PER_Q-1)&
+		(QDIO_MAX_BUFFERS_PER_Q-1);
+	int count=(end+QDIO_MAX_BUFFERS_PER_Q+1-start)&
+		(QDIO_MAX_BUFFERS_PER_Q-1);
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"kickouth");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_DBF_LIKE_HELL
+	sprintf(dbf_text,"s=%2xc=%2x",start,count);
+	QDIO_DBF_TEXT4(0,trace,dbf_text);
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	if (q->state==QDIO_IRQ_STATE_ACTIVE)
+		q->handler(q->irq,QDIO_STATUS_OUTBOUND_INT|
+			   q->error_status_flags,
+			   q->qdio_error,q->siga_error,q->q_no,start,count,
+			   q->int_parm);
+
+	/* for the next time: */
+	q->first_element_to_kick=real_end;
+	q->qdio_error=0;
+	q->siga_error=0;
+	q->error_status_flags=0;
+}
+
+static void qdio_outbound_processing(qdio_q_t *q)
+{
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"qoutproc");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	if (unlikely(qdio_reserve_q(q))) {
+		qdio_release_q(q);
+#ifdef QDIO_PERFORMANCE_STATS
+		o_p_c++;
+#endif /* QDIO_PERFORMANCE_STATS */
+		/* as we're sissies, we'll check next time */
+		if (likely(!atomic_read(&q->is_in_shutdown))) {
+			qdio_mark_q(q);
+#ifdef QDIO_DBF_LIKE_HELL
+			QDIO_DBF_TEXT4(0,trace,"busy,agn");
+#endif /* QDIO_DBF_LIKE_HELL */
+		}
+		return;
+	}
+#ifdef QDIO_PERFORMANCE_STATS
+	o_p_nc++;
+#endif /* QDIO_PERFORMANCE_STATS */
+
+#ifdef QDIO_PERFORMANCE_STATS
+	perf_stats.tl_runs++;
+#endif /* QDIO_PERFORMANCE_STATS */
+
+	if (qdio_has_outbound_q_moved(q)) {
+		qdio_kick_outbound_handler(q);
+	}
+
+	if (q->is_iqdio_q) {
+		/* for asynchronous queues, we better check, if the fill
+		 * level is too high */
+		if (atomic_read(&q->number_of_buffers_used)>
+		    IQDIO_FILL_LEVEL_TO_POLL) {
+			qdio_mark_q(q);
+		}
+	} else if (!q->hydra_gives_outbound_pcis) {
+		if (!qdio_is_outbound_q_done(q)) {
+			qdio_mark_q(q);
+		}
+	}
+
+	qdio_release_q(q);
+}
+
+/************************* INBOUND ROUTINES *******************************/
+
+
+inline static 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_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"getibfro");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	slsb=&q->slsb.acc.val[0];
+	f_mod_no=f=q->first_to_check;
+	/* we don't check 128 buffers, as otherwise qdio_has_inbound_q_moved
+	 * would return 0 */
+	first_not_to_check=f+qdio_min(atomic_read(&q->number_of_buffers_used),
+				      (QDIO_MAX_BUFFERS_PER_Q-1));
+
+	/* we don't use this one, as a PCI or we after a thin interrupt
+	 * will sync the queues
+	SYNC_MEMORY;*/
+
+check_next:
+	f_mod_no=f&(QDIO_MAX_BUFFERS_PER_Q-1);
+	if (f==first_not_to_check) goto out;
+	slsbyte=slsb[f_mod_no];
+
+	/* CU_EMPTY means frontier is reached */
+	if (slsbyte==SLSB_CU_INPUT_EMPTY) {
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT5(0,trace,"inptempt");
+#endif /* QDIO_DBF_LIKE_HELL */
+		goto out;
+	}
+
+	/* P_PRIMED means set slsb to P_PROCESSING and move on */
+	if (slsbyte==SLSB_P_INPUT_PRIMED) {
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT5(0,trace,"inptprim");
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_USE_PROCESSING_STATE
+		/* as soon as running under VM, polling the input queues will
+		 * kill VM in terms of CP overhead */
+		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);
+			atomic_set(&q->polling,1);
+		}
+#else /* QDIO_USE_PROCESSING_STATE */
+		set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT);
+#endif /* QDIO_USE_PROCESSING_STATE */
+		/* not needed, as the inbound queue will be synced on the next
+		 * siga-r
+		SYNC_MEMORY;*/
+		f++;
+		atomic_dec(&q->number_of_buffers_used);
+		goto check_next;
+	}
+
+	if ( (slsbyte==SLSB_P_INPUT_NOT_INIT) ||
+	     (slsbyte==SLSB_P_INPUT_PROCESSING) ) {
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT5(0,trace,"inpnipro");
+#endif /* QDIO_DBF_LIKE_HELL */
+		goto out;
+	}
+
+	/* P_ERROR means frontier is reached, break and report error */
+	if (slsbyte==SLSB_P_INPUT_ERROR) {
+		sprintf(dbf_text,"inperr%2x",f_mod_no);
+		QDIO_DBF_TEXT3(1,trace,dbf_text);
+		QDIO_DBF_HEX2(1,sbal,q->sbal[f_mod_no],256);
+
+		/* kind of process the buffer */
+		set_slsb(&slsb[f_mod_no],SLSB_P_INPUT_NOT_INIT);
+
+		if (q->qdio_error)
+			q->error_status_flags|=
+				QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR;
+		q->qdio_error=SLSB_P_INPUT_ERROR;
+		q->error_status_flags|=QDIO_STATUS_LOOK_FOR_ERROR;
+
+		/* we increment the frontier, as this buffer
+		 * was processed obviously */
+		f_mod_no=(f_mod_no+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+		atomic_dec(&q->number_of_buffers_used);
+
+		goto out;
+	}
+
+	/* everything else means frontier not changed (HALTED or so) */
+out:
+	q->first_to_check=f_mod_no;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	return q->first_to_check;
+}
+
+inline static int qdio_has_inbound_q_moved(qdio_q_t *q)
+{
+	int i;
+
+#ifdef QDIO_PERFORMANCE_STATS
+	static int old_pcis=0;
+	static int old_thinints=0;
+
+	if ((old_pcis==perf_stats.pcis)&&(old_thinints==perf_stats.thinints))
+		perf_stats.start_time_inbound=NOW;
+	else
+		old_pcis=perf_stats.pcis;
+#endif /* QDIO_PERFORMANCE_STATS */
+
+	i=qdio_get_inbound_buffer_frontier(q);
+	if ( (i!=GET_SAVED_FRONTIER(q)) ||
+	     (q->error_status_flags&QDIO_STATUS_LOOK_FOR_ERROR) ) {
+		SAVE_FRONTIER(q,i);
+		if ((!q->siga_sync)&&(!q->hydra_gives_outbound_pcis)) {
+			SAVE_TIMESTAMP(q);
+		}
+
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT4(0,trace,"inhasmvd");
+		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+		return 1;
+	} else {
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT4(0,trace,"inhsntmv");
+		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+		return 0;
+	}
+}
+
+/* means, no more buffers to be filled */
+inline static int iqdio_is_inbound_q_done(qdio_q_t *q)
+{
+	int no_used;
+#ifdef QDIO_DBF_LIKE_HELL
+	char dbf_text[15];
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	no_used=atomic_read(&q->number_of_buffers_used);
+
+	/* propagate the change from 82 to 80 through VM */
+	SYNC_MEMORY;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	if (no_used) {
+		sprintf(dbf_text,"iqisnt%02x",no_used);
+		QDIO_DBF_TEXT4(0,trace,dbf_text);
+	} else {
+		QDIO_DBF_TEXT4(0,trace,"iniqisdo");
+	}
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	if (!no_used) {
+		return 1;
+	}
+
+	if (q->siga_sync) {
+		if (q->slsb.acc.val[q->first_to_check]==SLSB_P_INPUT_PRIMED) {
+			/* ok, the next input buffer is primed. that means,
+			 * that device state change indicator and adapter
+			 * local summary are set, so we will find it next
+			 * time.
+			 * we will return 0 below, as there is nothing to
+			 * do, except of scheduling ourselves for the next
+			 * time. */
+			tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);
+			tiqdio_sched_tl();
+		} else {
+			/* nothing more to do, if next buffer is not PRIMED.
+			 * note that we did a SYNC_MEMORY before, that there
+			 * has been a sychnronization.
+			 * we will return 0 below, as there is nothing to do
+			 * (stop_polling not necessary, as we have not been
+			 * using the PROCESSING state */
+		}
+	} else {
+		/* we'll check for more primed buffers
+		 * in qeth_stop_polling */
+	}
+
+	return 0;
+}
+
+inline static int qdio_is_inbound_q_done(qdio_q_t *q)
+{
+	int no_used;
+#ifdef QDIO_DBF_LIKE_HELL
+	char dbf_text[15];
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	no_used=atomic_read(&q->number_of_buffers_used);
+
+	/* we need that one for synchronization with Hydra, as Hydra
+	 * does a kind of PCI avoidance */
+	SYNC_MEMORY;
+
+	if (!no_used) {
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT4(0,trace,"inqisdnA");
+		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+		QDIO_DBF_TEXT4(0,trace,dbf_text);
+#endif /* QDIO_DBF_LIKE_HELL */
+		return 1;
+	}
+
+	if (q->slsb.acc.val[q->first_to_check]==SLSB_P_INPUT_PRIMED) {
+		/* we got something to do */
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT4(0,trace,"inqisntA");
+		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+		return 0;
+	}
+
+	/* on VM, we don't poll, so the q is always done here */
+	if (q->siga_sync) return 1;
+	if (q->hydra_gives_outbound_pcis) return 1;
+
+	/* at this point we know, that inbound first_to_check
+	   has (probably) not moved (see qdio_inbound_processing) */
+	if (NOW>GET_SAVED_TIMESTAMP(q)+q->timing.threshold) {
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT4(0,trace,"inqisdon");
+		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+		sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used);
+		QDIO_DBF_TEXT4(0,trace,dbf_text);
+#endif /* QDIO_DBF_LIKE_HELL */
+		return 1;
+	} else {
+#ifdef QDIO_DBF_LIKE_HELL
+		QDIO_DBF_TEXT4(0,trace,"inqisntd");
+		QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+		sprintf(dbf_text,"pf%02xcn%02x",q->first_to_check,no_used);
+		QDIO_DBF_TEXT4(0,trace,dbf_text);
+#endif /* QDIO_DBF_LIKE_HELL */
+		return 0;
+	}
+}
+
+inline static void qdio_kick_inbound_handler(qdio_q_t *q)
+{
+	int count=0;
+	int start,end,real_end,i;
+#ifdef QDIO_DBF_LIKE_HELL
+	char dbf_text[15];
+
+	QDIO_DBF_TEXT4(0,trace,"kickinh");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	start=q->first_element_to_kick;
+	real_end=q->first_to_check;
+	end=(real_end+QDIO_MAX_BUFFERS_PER_Q-1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+
+	i=start;
+	while (1) {
+		count++;
+		qdio_translate_buffer_back(q,i);
+		if (i==end) break;
+		i=(i+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+	}
+
+#ifdef QDIO_DBF_LIKE_HELL
+	sprintf(dbf_text,"s=%2xc=%2x",start,count);
+	QDIO_DBF_TEXT4(0,trace,dbf_text);
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	if (likely(q->state==QDIO_IRQ_STATE_ACTIVE))
+		q->handler(q->irq,
+			   QDIO_STATUS_INBOUND_INT|q->error_status_flags,
+			   q->qdio_error,q->siga_error,q->q_no,start,count,
+			   q->int_parm);
+
+	/* for the next time: */
+	q->first_element_to_kick=real_end;
+	q->qdio_error=0;
+	q->siga_error=0;
+	q->error_status_flags=0;
+
+#ifdef QDIO_PERFORMANCE_STATS
+	perf_stats.inbound_time+=NOW-perf_stats.start_time_inbound;
+	perf_stats.inbound_cnt++;
+#endif /* QDIO_PERFORMANCE_STATS */
+}
+
+static inline void tiqdio_inbound_processing(qdio_q_t *q)
+{
+	qdio_irq_t *irq_ptr;
+	qdio_q_t *oq;
+	int i;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"iqinproc");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	/* we first want to reserve the q, so that we know, that we don't
+	 * interrupt ourselves and call qdio_unmark_q, as is_in_shutdown might
+	 * be set */
+	if (unlikely(qdio_reserve_q(q))) {
+		qdio_release_q(q);
+#ifdef QDIO_PERFORMANCE_STATS
+		ii_p_c++;
+#endif /* QDIO_PERFORMANCE_STATS */
+		/* as we might just be about to stop polling, we make
+		 * sure that we check again at least once more */
+		tiqdio_sched_tl();
+		return;
+	}
+#ifdef QDIO_PERFORMANCE_STATS
+	ii_p_nc++;
+#endif /* QDIO_PERFORMANCE_STATS */
+	if (unlikely(atomic_read(&q->is_in_shutdown))) {
+		qdio_unmark_q(q);
+		goto out;
+	}
+
+	if (*(q->dev_st_chg_ind)) {
+		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;
+			} else if ((!q->siga_sync_done_on_outb_tis)&&
+				   (q->hydra_gives_outbound_pcis)) {
+				SYNC_MEMORY_ALL_OUTB;
+			}
+		} else {
+			SYNC_MEMORY;
+		}
+
+		/* maybe we have to do work on our outbound queues... at least
+		 * we have to check Hydra outbound-int-capable thinint-capable
+		 * queues */
+		if (q->hydra_gives_outbound_pcis) {
+			irq_ptr=(qdio_irq_t*)q->irq_ptr;
+ 			for (i=0;i<irq_ptr->no_output_qs;i++) {
+ 				oq=irq_ptr->output_qs[i];
+#ifdef QDIO_PERFORMANCE_STATS
+				perf_stats.tl_runs--;
+#endif /* QDIO_PERFORMANCE_STATS */
+				if (!qdio_is_outbound_q_done(oq)) {
+					qdio_outbound_processing(oq);
+				}
+			}
+		}
+
+		if (qdio_has_inbound_q_moved(q)) {
+			qdio_kick_inbound_handler(q);
+			if (iqdio_is_inbound_q_done(q)) {
+				if (!qdio_stop_polling(q)) {
+					/* we set the flags to get into
+					 * the stuff next time, see also
+					 * comment in qdio_stop_polling */
+					tiqdio_set_summary_bit
+						((__u32*)q->dev_st_chg_ind);
+					tiqdio_sched_tl();
+				}
+			}
+		}
+	}
+out:
+	qdio_release_q(q);
+}
+
+static void qdio_inbound_processing(qdio_q_t *q)
+{
+	int q_laps=0;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"qinproc");
+	QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	if (unlikely(qdio_reserve_q(q))) {
+		qdio_release_q(q);
+#ifdef QDIO_PERFORMANCE_STATS
+		i_p_c++;
+#endif /* QDIO_PERFORMANCE_STATS */
+		/* as we're sissies, we'll check next time */
+		if (likely(!atomic_read(&q->is_in_shutdown))) {
+			qdio_mark_q(q);
+#ifdef QDIO_DBF_LIKE_HELL
+			QDIO_DBF_TEXT4(0,trace,"busy,agn");
+#endif /* QDIO_DBF_LIKE_HELL */
+		}
+		return;
+	}
+#ifdef QDIO_PERFORMANCE_STATS
+	i_p_nc++;
+	perf_stats.tl_runs++;
+#endif /* QDIO_PERFORMANCE_STATS */
+
+again:
+	if (qdio_has_inbound_q_moved(q)) {
+		qdio_kick_inbound_handler(q);
+		if (!qdio_stop_polling(q)) {
+			q_laps++;
+			if (q_laps<QDIO_Q_LAPS) goto again;
+		}
+		qdio_mark_q(q);
+	} else {
+		if (!qdio_is_inbound_q_done(q)) /* means poll time is 
+						   not yet over */
+			qdio_mark_q(q);
+	}
+
+	qdio_release_q(q);
+}
+
+/************************* MAIN ROUTINES *******************************/
+
+static inline void tiqdio_inbound_checks(void)
+{
+	qdio_q_t *q;
+#ifdef QDIO_USE_PROCESSING_STATE
+	int q_laps=0;
+#endif /* QDIO_USE_PROCESSING_STATE */
+
+#ifdef QDIO_DBF_LIKE_HELL
+	char dbf_text[15];
+
+	QDIO_DBF_TEXT4(0,trace,"iqdinbck");
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT5(0,trace,"iqlocsum");
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_USE_PROCESSING_STATE
+again:
+#endif /* QDIO_USE_PROCESSING_STATE */
+
+	q=(qdio_q_t*)tiq_list;
+	/* switch all active queues to processing state */
+	do {
+		if (!q) break;
+		tiqdio_inbound_processing(q);
+		q=(qdio_q_t*)q->list_next;
+	} while (q!=(qdio_q_t*)tiq_list);
+
+	/* switch off all queues' processing state */
+#ifdef QDIO_USE_PROCESSING_STATE
+	q=(qdio_q_t*)tiq_list;
+	do {
+		if (!q) {
+			tiqdio_sched_tl();
+			break;
+		}
+		/* under VM, we have not used the PROCESSING state, so no
+		 * need to stop polling */
+		if (q->siga_sync) {
+			q=(qdio_q_t*)q->list_next;
+			continue;
+		}
+
+		if (unlikely(qdio_reserve_q(q))) {
+			qdio_release_q(q);
+#ifdef QDIO_PERFORMANCE_STATS
+			ii_p_c++;
+#endif /* QDIO_PERFORMANCE_STATS */
+			/* as we might just be about to stop polling, we make
+			 * sure that we check again at least once more */
+
+			/* sanity -- we'd get here without setting the
+			 * dev st chg ind */
+			tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind);
+			tiqdio_sched_tl();
+			break;
+		}
+		if (!qdio_stop_polling(q)) {
+			q_laps++;
+			if (q_laps<QDIO_Q_LAPS) {
+				qdio_release_q(q);
+				goto again;
+			} else {
+				/* we set the flags to get into the stuff
+				 * next time, see also comment in
+				 * qdio_stop_polling */
+				tiqdio_set_summary_bit((__u32*)
+						      q->dev_st_chg_ind);
+				tiqdio_sched_tl();
+			}
+		}
+		qdio_release_q(q);
+		q=(qdio_q_t*)q->list_next;
+	} while (q!=(qdio_q_t*)tiq_list);
+#endif /* QDIO_USE_PROCESSING_STATE */
+}
+
+static void tiqdio_tl(unsigned long data)
+{
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"tiqdio_tl");
+#endif /* QDIO_DBF_LIKE_HELL */
+#ifdef QDIO_PERFORMANCE_STATS
+	perf_stats.tl_runs++;
+#endif /* QDIO_PERFORMANCE_STATS */
+
+	tiqdio_inbound_checks();
+}
+
+/********************* GENERAL HELPER_ROUTINES ***********************/
+
+static qdio_irq_t *qdio_get_irq_ptr(int irq)
+{
+	qdio_irq_t *irq_ptr;
+	int bucket=irq&(QDIO_IRQ_BUCKETS-1);
+
+	read_lock(&irq_list_lock[bucket]);
+	irq_ptr=first_irq[bucket];
+	while (irq_ptr) {
+		if (irq_ptr->irq==irq) break;
+		irq_ptr=irq_ptr->next;
+	}
+	read_unlock(&irq_list_lock[bucket]);
+	return irq_ptr;
+}
+
+static qdio_irq_t *qdio_get_irq_ptr_wolock(int irq)
+{
+	qdio_irq_t *irq_ptr=first_irq[irq&(QDIO_IRQ_BUCKETS-1)];
+
+	while (irq_ptr) {
+		if (irq_ptr->irq==irq) break;
+		irq_ptr=irq_ptr->next;
+	}
+	return irq_ptr;
+}
+
+/* irq_ptr->irq should be set already! */
+static void qdio_insert_irq_ptr(qdio_irq_t *irq_ptr)
+{
+	qdio_irq_t *i_p;
+	int irq,bucket;
+
+	if (!irq_ptr) return;
+
+	irq=irq_ptr->irq;
+	bucket=irq&(QDIO_IRQ_BUCKETS-1);
+
+	write_lock(&irq_list_lock[bucket]);
+
+	if (irq_ptr==qdio_get_irq_ptr_wolock(irq)) goto out;
+
+	if (!first_irq[bucket]) {
+		first_irq[bucket]=irq_ptr;
+	} else {
+		i_p=first_irq[bucket];
+		while (i_p->next)
+			i_p=i_p->next;
+		i_p->next=irq_ptr;
+	}
+	irq_ptr->next=NULL;
+out:
+	write_unlock(&irq_list_lock[bucket]);
+}
+
+static void qdio_remove_irq_ptr(qdio_irq_t *irq_ptr)
+{
+	qdio_irq_t *i_p;
+	int irq,bucket;
+
+	if (!irq_ptr) return;
+
+	irq=irq_ptr->irq;
+	bucket=irq&(QDIO_IRQ_BUCKETS-1);
+
+	write_lock(&irq_list_lock[bucket]);
+
+	if (!qdio_get_irq_ptr_wolock(irq)) goto out;
+
+	if (first_irq[irq&(QDIO_IRQ_BUCKETS-1)]==irq_ptr) {
+		first_irq[irq&(QDIO_IRQ_BUCKETS-1)]=irq_ptr->next;
+	} else {
+		for (i_p=first_irq[irq&(QDIO_IRQ_BUCKETS-1)];
+		     i_p->next!=irq_ptr;i_p=i_p->next);
+		i_p->next=irq_ptr->next;
+	}
+	irq_ptr->next=NULL;
+out:
+	write_unlock(&irq_list_lock[bucket]);
+}
+
+static void qdio_release_irq_memory(qdio_irq_t *irq_ptr)
+{
+	int i,j;
+	int available;
+
+	for (i=0;i<QDIO_MAX_QUEUES_PER_IRQ;i++) {
+		if (!irq_ptr->input_qs[i]) goto next;
+		available=0;
+		if (!irq_ptr->input_qs[i]->is_0copy_sbals_q)
+			for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+				if (!available) {
+					if (irq_ptr->input_qs[i]->sbal[j])
+						kfree((void*)irq_ptr->
+						      input_qs[i]->sbal[j]);
+					available=PAGE_SIZE;
+				}
+				available-=sizeof(sbal_t);
+			}
+		if (irq_ptr->input_qs[i]->slib)
+			kfree(irq_ptr->input_qs[i]->slib);
+			kfree(irq_ptr->input_qs[i]);
+
+next:
+		if (!irq_ptr->output_qs[i]) continue;
+		available=0;
+		if (!irq_ptr->output_qs[i]->is_0copy_sbals_q)
+			for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+				if (!available) {
+					if (irq_ptr->output_qs[i]->sbal[j])
+						kfree((void*)irq_ptr->
+						      output_qs[i]->sbal[j]);
+					available=PAGE_SIZE;
+				}
+				available-=sizeof(sbal_t);
+			}
+		if (irq_ptr->output_qs[i]->slib)
+			kfree(irq_ptr->output_qs[i]->slib);
+		kfree(irq_ptr->output_qs[i]);
+
+	}
+	if (irq_ptr->qdr) kfree(irq_ptr->qdr);
+	kfree(irq_ptr);
+}
+
+static inline void qdio_wakeup(atomic_t *var,qdio_irq_t *irq_ptr)
+{
+	wait_queue_head_t *wait_q;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	char dbf_text[20];
+	sprintf(dbf_text,"qwkp%4x",irq_ptr->irq);
+	QDIO_DBF_TEXT3(0,trace,dbf_text);
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	atomic_set(var,1);
+	if ((wait_q=&irq_ptr->wait_q))
+		wake_up(wait_q);
+}
+
+static int qdio_sleepon(atomic_t *var,int timeout,qdio_irq_t *irq_ptr)
+{
+        __u64 stop;
+	int retval;
+        DECLARE_WAITQUEUE (current_wait_q,current);
+
+#ifdef QDIO_DBF_LIKE_HELL
+	char dbf_text[20];
+	sprintf(dbf_text,"qslp%4x",irq_ptr->irq);
+	QDIO_DBF_TEXT3(0,trace,dbf_text);
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	add_wait_queue(&irq_ptr->wait_q,&current_wait_q);
+        stop=(qdio_get_micros()>>10)+timeout;
+        for (;;) {
+		set_task_state(current,TASK_INTERRUPTIBLE);
+		if (atomic_read(var)) {
+			atomic_set(var,0);
+                        retval=0;
+			goto out;
+                }
+                if (qdio_get_micros()>>10>stop) {
+#ifdef QDIO_DBF_LIKE_HELL
+			sprintf(dbf_text,"%xtime",irq_ptr->irq);
+			QDIO_DBF_TEXT3(0,trace,dbf_text);
+#endif /* QDIO_DBF_LIKE_HELL */
+			retval=-ETIME;
+			goto out;
+		}
+                schedule_timeout(((stop-(qdio_get_micros()>>10))>>10)*HZ);
+        }
+ out:
+	set_task_state(current,TASK_RUNNING);
+	remove_wait_queue(&irq_ptr->wait_q,&current_wait_q);
+	return retval;
+}
+
+static void qdio_set_impl_params(qdio_irq_t *irq_ptr,
+				 unsigned int qib_param_field_format,
+			  /* pointer to 128 bytes or NULL, if no param field */
+				 unsigned char *qib_param_field,
+				 unsigned int no_input_qs,
+				 unsigned int no_output_qs,
+			  /* pointer to no_queues*128 words of data or NULL */
+				 unsigned long *input_slib_elements,
+				 unsigned long *output_slib_elements)
+{
+	int i,j;
+
+	if (!irq_ptr) return;
+
+	irq_ptr->qib.pfmt=qib_param_field_format;
+	if (qib_param_field)
+		memcpy(irq_ptr->qib.parm,qib_param_field,
+		       QDIO_MAX_BUFFERS_PER_Q);
+
+	if (input_slib_elements)
+		for (i=0;i<no_input_qs;i++) {
+			for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
+				irq_ptr->input_qs[i]->slib->slibe[j].parms=
+					input_slib_elements[
+						i*QDIO_MAX_BUFFERS_PER_Q+j];
+		}
+	if (output_slib_elements)
+		for (i=0;i<no_output_qs;i++) {
+			for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
+				irq_ptr->output_qs[i]->slib->slibe[j].parms=
+					output_slib_elements[
+						i*QDIO_MAX_BUFFERS_PER_Q+j];
+		}
+}
+
+
+
+static int qdio_alloc_qs(qdio_irq_t *irq_ptr,int no_input_qs,int no_output_qs,
+			 qdio_handler_t *input_handler,
+			 qdio_handler_t *output_handler,
+			 unsigned long int_parm,int q_format,
+			 unsigned long flags,
+			 void **inbound_sbals_array,
+			 void **outbound_sbals_array)
+{
+	qdio_q_t *q;
+	int i,j,result=0;
+	char dbf_text[20]; /* see qdio_initialize */
+	void *ptr;
+	int available;
+
+	for (i=0;i<no_input_qs;i++) {
+		q=kmalloc(sizeof(qdio_q_t),GFP_KERNEL);
+
+		if (!q) {
+			QDIO_PRINT_ERR("kmalloc of q failed!\n");
+			goto out;
+		}
+		memset(q,0,sizeof(qdio_q_t));
+
+		sprintf(dbf_text,"in-q%4x",i);
+		QDIO_DBF_TEXT0(0,setup,dbf_text);
+		QDIO_DBF_HEX0(0,setup,&q,sizeof(void*));
+
+		q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL);
+		if (!q->slib) {
+			QDIO_PRINT_ERR("kmalloc of slib failed!\n");
+			goto out;
+		}
+		memset(q->slib,0,PAGE_SIZE);
+		q->sl=(sl_t*)(((char*)q->slib)+PAGE_SIZE/2);
+
+		available=0;
+		if (flags&QDIO_INBOUND_0COPY_SBALS) {
+			for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+				q->sbal[j]=*(inbound_sbals_array++);
+			}
+		} else for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+			if (!available) {
+				q->sbal[j]=kmalloc(PAGE_SIZE,GFP_KERNEL);
+				if (!q->sbal[j]) {
+					goto out;
+				}
+				available=PAGE_SIZE;
+				memset((void*)q->sbal[j],0,PAGE_SIZE);
+			} else {
+				q->sbal[j]=(volatile sbal_t *)
+					(((char*)q->sbal[j-1])+sizeof(sbal_t));
+			}
+			available-=sizeof(sbal_t);
+		}
+
+                q->queue_type=q_format;
+		q->int_parm=int_parm;
+		irq_ptr->input_qs[i]=q;
+		q->irq=irq_ptr->irq;
+		q->irq_ptr=irq_ptr;
+		q->mask=1<<(31-i);
+		q->q_no=i;
+		q->is_input_q=1;
+		q->is_0copy_sbals_q=flags&QDIO_INBOUND_0COPY_SBALS;
+		q->first_to_check=0;
+		q->last_move_ftc=0;
+		q->handler=input_handler;
+		q->dev_st_chg_ind=irq_ptr->dev_st_chg_ind;
+
+		q->tasklet.data=(unsigned long)q;
+		/* q->is_thinint_q isn't valid at this time, but
+		 * irq_ptr->is_thinint_irq is */
+		q->tasklet.func=(void(*)(unsigned long))
+			((irq_ptr->is_thinint_irq)?&tiqdio_inbound_processing:
+			 &qdio_inbound_processing);
+
+/*		for (j=0;j<QDIO_STATS_NUMBER;j++)
+			q->timing.last_transfer_times[j]=(qdio_get_micros()/
+							  QDIO_STATS_NUMBER)*j;
+		q->timing.last_transfer_index=QDIO_STATS_NUMBER-1;
+*/
+
+		/* fill in slib */
+		if (i>0) irq_ptr->input_qs[i-1]->slib->nsliba=
+				 QDIO_PFIX_GET_ADDR(q->slib);
+		q->slib->sla=QDIO_PFIX_GET_ADDR(q->sl);
+		q->slib->slsba=QDIO_PFIX_GET_ADDR((void *)&q->slsb.acc.val[0]);
+
+		/* fill in sl */
+		for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
+			q->sl->element[j].sbal=
+				QDIO_PFIX_GET_ADDR((void *)q->sbal[j]);
+
+		QDIO_DBF_TEXT2(0,setup,"sl-sb-b0");
+		ptr=(void*)q->sl;
+		QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
+		ptr=(void*)&q->slsb;
+		QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
+		ptr=(void*)q->sbal[0];
+		QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
+
+		/* fill in slsb */
+		for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+			set_slsb(&q->slsb.acc.val[j],
+		   		 SLSB_P_INPUT_NOT_INIT);
+			q->sbal[j]->element[1].sbalf.i1.key=
+				QDIO_STORAGE_ACC_KEY;
+		}
+	}
+
+	for (i=0;i<no_output_qs;i++) {
+		q=kmalloc(sizeof(qdio_q_t),GFP_KERNEL);
+
+		if (!q) {
+			goto out;
+		}
+		memset(q,0,sizeof(qdio_q_t));
+
+		sprintf(dbf_text,"outq%4x",i);
+		QDIO_DBF_TEXT0(0,setup,dbf_text);
+		QDIO_DBF_HEX0(0,setup,&q,sizeof(void*));
+
+		q->slib=kmalloc(PAGE_SIZE,GFP_KERNEL);
+		if (!q->slib) {
+			QDIO_PRINT_ERR("kmalloc of slib failed!\n");
+			goto out;
+		}
+		memset(q->slib,0,PAGE_SIZE);
+		q->sl=(sl_t*)(((char*)q->slib)+PAGE_SIZE/2);
+
+		available=0;
+		if (flags&QDIO_OUTBOUND_0COPY_SBALS) {
+			for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+				q->sbal[j]=*(outbound_sbals_array++);
+			}
+		} else for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+			if (!available) {
+				q->sbal[j]=kmalloc(PAGE_SIZE,GFP_KERNEL);
+				if (!q->sbal[j]) {
+					goto out;
+				}
+				available=PAGE_SIZE;
+				memset((void*)q->sbal[j],0,PAGE_SIZE);
+			} else {
+				q->sbal[j]=(volatile sbal_t *)
+					(((char*)q->sbal[j-1])+sizeof(sbal_t));
+			}
+			available-=sizeof(sbal_t);
+		}
+
+                q->queue_type=q_format;
+		q->int_parm=int_parm;
+		irq_ptr->output_qs[i]=q;
+		q->is_input_q=0;
+		q->is_0copy_sbals_q=flags&QDIO_OUTBOUND_0COPY_SBALS;
+		q->irq=irq_ptr->irq;
+		q->irq_ptr=irq_ptr;
+		q->mask=1<<(31-i);
+		q->q_no=i;
+		q->first_to_check=0;
+		q->last_move_ftc=0;
+		q->handler=output_handler;
+
+		q->tasklet.data=(unsigned long)q;
+		q->tasklet.func=(void(*)(unsigned long))
+			&qdio_outbound_processing;
+
+		/* fill in slib */
+		if (i>0) irq_ptr->output_qs[i-1]->slib->nsliba=
+				 QDIO_PFIX_GET_ADDR(q->slib);
+		q->slib->sla=QDIO_PFIX_GET_ADDR(q->sl);
+		q->slib->slsba=QDIO_PFIX_GET_ADDR((void *)&q->slsb.acc.val[0]);
+
+		/* fill in sl */
+		for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++)
+			q->sl->element[j].sbal=
+				QDIO_PFIX_GET_ADDR((void *)q->sbal[j]);
+
+		QDIO_DBF_TEXT2(0,setup,"sl-sb-b0");
+		ptr=(void*)q->sl;
+		QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
+		ptr=(void*)&q->slsb;
+		QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
+		ptr=(void*)q->sbal[0];
+		QDIO_DBF_HEX2(0,setup,&ptr,sizeof(void*));
+
+		/* fill in slsb */
+		for (j=0;j<QDIO_MAX_BUFFERS_PER_Q;j++) {
+			set_slsb(&q->slsb.acc.val[j],
+		   		 SLSB_P_OUTPUT_NOT_INIT);
+			q->sbal[j]->element[1].sbalf.i1.key=
+				QDIO_STORAGE_ACC_KEY;
+		}
+	}
+
+	result=1;
+out:
+	return result;
+}
+
+static void qdio_fill_thresholds(qdio_irq_t *irq_ptr,
+				 unsigned int no_input_qs,
+				 unsigned int no_output_qs,
+				 unsigned int min_input_threshold,
+				 unsigned int max_input_threshold,
+				 unsigned int min_output_threshold,
+				 unsigned int max_output_threshold)
+{
+	int i;
+	qdio_q_t *q;
+
+	for (i=0;i<no_input_qs;i++) {
+		q=irq_ptr->input_qs[i];
+		q->timing.threshold=max_input_threshold;
+/*		for (j=0;j<QDIO_STATS_CLASSES;j++) {
+			q->threshold_classes[j].threshold=
+				min_input_threshold+
+				(max_input_threshold-min_input_threshold)/
+				QDIO_STATS_CLASSES;
+		}
+		qdio_use_thresholds(q,QDIO_STATS_CLASSES/2);*/
+	}
+	for (i=0;i<no_output_qs;i++) {
+		q=irq_ptr->output_qs[i];
+		q->timing.threshold=max_output_threshold;
+/*		for (j=0;j<QDIO_STATS_CLASSES;j++) {
+			q->threshold_classes[j].threshold=
+				min_output_threshold+
+				(max_output_threshold-min_output_threshold)/
+				QDIO_STATS_CLASSES;
+		}
+		qdio_use_thresholds(q,QDIO_STATS_CLASSES/2);*/
+	}
+}
+
+static int tiqdio_thinint_handler(__u32 intparm)
+{
+#ifdef QDIO_DBF_LIKE_HELL
+	QDIO_DBF_TEXT4(0,trace,"thin_int");
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_PERFORMANCE_STATS
+	perf_stats.thinints++;
+	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) {
+		tiqdio_clear_global_summary();
+	}
+
+	tiqdio_inbound_checks();
+	return 0;
+}
+
+static void qdio_set_state(qdio_irq_t *irq_ptr,int state)
+{
+	int i;
+	char dbf_text[15];
+
+	QDIO_DBF_TEXT5(0,trace,"newstate");
+	sprintf(dbf_text,"%4x%4x",irq_ptr->irq,state);
+	QDIO_DBF_TEXT5(0,trace,dbf_text);
+
+	irq_ptr->state=state;
+	for (i=0;i<irq_ptr->no_input_qs;i++)
+		irq_ptr->input_qs[i]->state=state;
+	for (i=0;i<irq_ptr->no_output_qs;i++)
+		irq_ptr->output_qs[i]->state=state;
+	mb();
+}
+
+static void qdio_handler(int irq,devstat_t *devstat,struct pt_regs *p)
+{
+	qdio_irq_t *irq_ptr;
+	qdio_q_t *q;
+	int i;
+	int cstat,dstat;
+	int rqparam;
+	char dbf_text[15]="qintXXXX";
+
+        cstat = devstat->cstat;
+        dstat = devstat->dstat;
+        rqparam=devstat->intparm;
+
+	*((int*)(&dbf_text[4]))=irq;
+	QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN);
+	
+	if (!rqparam) {
+		QDIO_PRINT_STUPID("got unsolicited interrupt in qdio " \
+				  "handler, irq 0x%x\n",irq);
+		return;
+	}
+
+	irq_ptr=qdio_get_irq_ptr(irq);
+	if (!irq_ptr) {
+		sprintf(dbf_text,"uint%4x",irq);
+		QDIO_DBF_TEXT2(1,trace,dbf_text);
+		QDIO_PRINT_ERR("received interrupt on unused irq 0x%04x!\n",
+			       irq);
+		return;
+	}
+
+	if (devstat->flag&DEVSTAT_FLAG_SENSE_AVAIL) {
+		sprintf(dbf_text,"sens%4x",irq);
+		QDIO_DBF_TEXT2(1,trace,dbf_text);
+		QDIO_DBF_HEX0(0,sense,&devstat->ii.irb,QDIO_DBF_SENSE_LEN);
+
+		QDIO_PRINT_WARN("sense data available on qdio channel.\n");
+		HEXDUMP16(WARN,"irb: ",&devstat->ii.irb);
+		HEXDUMP16(WARN,"sense data: ",&devstat->ii.sense.data[0]);
+	}
+
+	irq_ptr->io_result_cstat=ioinfo[irq]->devstat.cstat;
+	irq_ptr->io_result_dstat=ioinfo[irq]->devstat.dstat;
+	irq_ptr->io_result_flags=ioinfo[irq]->devstat.flag;
+
+	if ( (rqparam==QDIO_DOING_ACTIVATE) &&
+	     (cstat & SCHN_STAT_PCI) ) {
+#ifdef QDIO_PERFORMANCE_STATS
+		perf_stats.pcis++;
+		perf_stats.start_time_inbound=NOW;
+#endif /* QDIO_PERFORMANCE_STATS */
+		for (i=0;i<irq_ptr->no_input_qs;i++) {
+			q=irq_ptr->input_qs[i];
+			if (q->is_input_q&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT)
+				qdio_mark_q(q);
+			else {
+#ifdef QDIO_PERFORMANCE_STATS
+				perf_stats.tl_runs--;
+#endif /* QDIO_PERFORMANCE_STATS */
+				qdio_inbound_processing(q);
+			}
+		}
+		if (irq_ptr->hydra_gives_outbound_pcis) {
+ 			for (i=0;i<irq_ptr->no_output_qs;i++) {
+ 				q=irq_ptr->output_qs[i];
+#ifdef QDIO_PERFORMANCE_STATS
+				perf_stats.tl_runs--;
+#endif /* QDIO_PERFORMANCE_STATS */
+				if (!qdio_is_outbound_q_done(q)) {
+					if (!irq_ptr->sync_done_on_outb_pcis) {
+						SYNC_MEMORY;
+					}
+					qdio_outbound_processing(q);
+				}
+			}
+		}
+		return;
+	}
+
+	if ( (rqparam==QDIO_DOING_ACTIVATE) &&
+	     (!cstat) &&
+	     (!dstat) ) {
+		rqparam=QDIO_DOING_CLEANUP;
+	}
+
+	if ( (rqparam==QDIO_DOING_ESTABLISH) &&
+	     ( (cstat) ||
+	       (dstat & ~(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ) ) {
+		sprintf(dbf_text,"ick1%4x",irq);
+		QDIO_DBF_TEXT2(1,trace,dbf_text);
+		QDIO_DBF_HEX2(0,trace,&rqparam,sizeof(int));
+		QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int));
+		QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int));
+		QDIO_PRINT_ERR("received check condition on establish " \
+			       "queues on irq 0x%x (cs=x%x, ds=x%x).\n",
+			       irq,cstat,dstat);
+		qdio_set_state(irq_ptr,QDIO_IRQ_STATE_STOPPED);
+		return;
+	}
+
+	if ( ( (rqparam==QDIO_DOING_ACTIVATE) &&
+	       (dstat==(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ) ||
+	     ( (rqparam==QDIO_DOING_CLEANUP) || (!rqparam) ) ) {
+		qdio_wakeup(&irq_ptr->interrupt_has_been_cleaned,irq_ptr);
+		return;
+	}
+
+	if ( (rqparam==QDIO_DOING_ACTIVATE) &&
+	     ( (cstat & ~SCHN_STAT_PCI) ||
+	       (dstat) ) ) {
+		sprintf(dbf_text,"ick2%4x",irq);
+		QDIO_DBF_TEXT2(1,trace,dbf_text);
+		QDIO_DBF_HEX2(0,trace,&rqparam,sizeof(int));
+		QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int));
+		QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int));
+		QDIO_PRINT_ERR("received check condition on activate " \
+			       "queues on irq 0x%x (cs=x%x, ds=x%x).\n",
+			       irq,cstat,dstat);
+		if (irq_ptr->no_input_qs) {
+			q=irq_ptr->input_qs[0];
+		} else if (irq_ptr->no_output_qs) {
+			q=irq_ptr->output_qs[0];
+		} else {
+			QDIO_PRINT_ERR("oops... no queue registered on irq " \
+				  "0x%x!?\n",irq);
+			goto omit_handler_call;
+		}
+		q->handler(q->irq,QDIO_STATUS_ACTIVATE_CHECK_CONDITION|
+			   QDIO_STATUS_LOOK_FOR_ERROR,
+			   0,0,0,-1,-1,q->int_parm);
+	omit_handler_call:
+		qdio_set_state(irq_ptr,QDIO_IRQ_STATE_STOPPED);
+		return;
+	}
+
+	qdio_wakeup(&irq_ptr->interrupt_has_arrived,irq_ptr);
+}
+
+/* this is for mp3 */
+int qdio_synchronize(int irq,unsigned int flags,
+		     unsigned int queue_number)
+{
+	unsigned int gpr2,gpr3;
+	int cc;
+	qdio_q_t *q;
+	qdio_irq_t *irq_ptr;
+	char dbf_text[15]="SyncXXXX";
+	void *ptr;
+
+	*((int*)(&dbf_text[4]))=irq;
+	QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN);
+	*((int*)(&dbf_text[0]))=flags;
+	*((int*)(&dbf_text[4]))=queue_number;
+	QDIO_DBF_HEX4(0,trace,dbf_text,QDIO_DBF_TRACE_LEN);
+
+	irq_ptr=qdio_get_irq_ptr(irq);
+	if (!irq_ptr) return -ENODEV;
+
+	if (flags&QDIO_FLAG_SYNC_INPUT) {
+		q=irq_ptr->input_qs[queue_number];
+		if (!q) return -EINVAL;
+
+		gpr2=0;
+		gpr3=q->mask;
+	} else if (flags&QDIO_FLAG_SYNC_OUTPUT) {
+		q=irq_ptr->output_qs[queue_number];
+		if (!q) return -EINVAL;
+
+		gpr2=q->mask;
+		gpr3=0;
+	} else return -EINVAL;
+
+#ifdef QDIO_32_BIT
+	asm volatile (
+		"lhi	0,2	\n\t"
+		"lr	1,%1	\n\t"
+		"lr	2,%2	\n\t"
+		"lr	3,%3	\n\t"
+		"siga   0	\n\t"
+		"ipm	%0	\n\t"
+		"srl	%0,28	\n\t"
+		: "=d" (cc)
+		: "d" (0x10000|q->irq), "d" (gpr2), "d" (gpr3)
+		: "cc", "0", "1", "2", "3"
+		);
+#else /* QDIO_32_BIT */
+	asm volatile (
+		"lghi	0,2	\n\t"
+		"llgfr	1,%1	\n\t"
+		"llgfr	2,%2	\n\t"
+		"llgfr	3,%3	\n\t"
+		"siga   0	\n\t"
+		"ipm	%0	\n\t"
+		"srl	%0,28	\n\t"
+		: "=d" (cc)
+		: "d" (0x10000|q->irq), "d" (gpr2), "d" (gpr3)
+		: "cc", "0", "1", "2", "3"
+		);
+#endif /* QDIO_32_BIT */
+
+	ptr=&cc;
+	if (cc) QDIO_DBF_HEX3(0,trace,&ptr,sizeof(int));
+
+	return cc;
+}
+
+unsigned char qdio_get_slsb_state(int irq,unsigned int flag,
+				  unsigned int queue_number,
+				  unsigned int qidx)
+{
+	qdio_irq_t *irq_ptr;
+	qdio_q_t *q;
+
+	irq_ptr=qdio_get_irq_ptr(irq);
+	if (!irq_ptr) return SLSB_ERROR_DURING_LOOKUP;
+
+	if (flag&QDIO_FLAG_SYNC_INPUT) {
+		q=irq_ptr->input_qs[queue_number];
+	} else if (flag&QDIO_FLAG_SYNC_OUTPUT) {
+		q=irq_ptr->output_qs[queue_number];
+	} else return SLSB_ERROR_DURING_LOOKUP;
+
+	return q->slsb.acc.val[qidx&(QDIO_MAX_BUFFERS_PER_Q-1)];
+}
+
+static int qdio_chsc(qdio_chsc_area_t *chsc_area)
+{
+	int cc;
+
+#ifdef QDIO_32_BIT
+	asm volatile (
+		".insn	rre,0xb25f0000,%1,0	\n\t"
+		"ipm	%0	\n\t"
+		"srl	%0,28	\n\t"
+		: "=d" (cc) : "d" (chsc_area) : "cc"
+		);
+#else /* QDIO_32_BIT */
+	asm volatile (
+		".insn	rre,0xb25f0000,%1,0	\n\t"
+		"ipm	%0	\n\t"
+		"srl	%0,28	\n\t"
+		: "=d" (cc) : "d" (chsc_area) : "cc"
+		);
+#endif /* QDIO_32_BIT */
+
+	return cc;
+}
+
+static unsigned char qdio_check_siga_needs(int sch)
+{
+	int resp_code,result;
+
+	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 */
+	chsc_area->request_block.first_sch=sch;
+	chsc_area->request_block.last_sch=sch;
+
+	result=qdio_chsc(chsc_area);
+
+	if (result) {
+		QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \
+				"SIGAs for sch x%x.\n",
+				result,sch);
+		return -1; /* all flags set */
+	}
+
+	resp_code=chsc_area->request_block.operation_data_area.
+		store_qdio_data_response.response_code;
+	if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) {
+		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 */
+	}
+	if (
+	    (!(chsc_area->request_block.operation_data_area.
+	       store_qdio_data_response.flags&CHSC_FLAG_QDIO_CAPABILITY)) ||
+	    (!(chsc_area->request_block.operation_data_area.
+	       store_qdio_data_response.flags&CHSC_FLAG_VALIDITY)) ||
+	    (chsc_area->request_block.operation_data_area.
+	     store_qdio_data_response.sch!=sch)
+	    ) {
+		QDIO_PRINT_WARN("huh? problems checking out sch x%x... " \
+				"using all SIGAs.\n",sch);
+		return CHSC_FLAG_SIGA_INPUT_NECESSARY |
+			CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
+			CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */
+	}
+
+	return chsc_area->request_block.operation_data_area.
+		store_qdio_data_response.qdioac;
+}
+
+static int qdio_check_for_hydra_thinints(void)
+{
+	int i,result;
+
+	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
+	chsc_area->request_block.command_code1=0x0010;
+	chsc_area->request_block.command_code2=0x0010;
+	result=qdio_chsc(chsc_area);
+
+	if (result) {
+		QDIO_PRINT_WARN("CHSC returned cc %i. Won't use adapter " \
+				"interrupts for any Hydra.\n",result);
+		return 0;
+	}
+
+	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;
+	}
+
+	/* 4: request block
+	 * 2: general char
+	 * 512: chsc char */
+	/* check for bit 67 */
+	if ( (*(((unsigned int*)(chsc_area))+4+2+2)&0x10000000)!=0x10000000) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+/* the chsc_area is locked by the lock in qdio_activate */
+static unsigned int tiqdio_check_chsc_availability(void) {
+	int result;
+	int i;
+
+	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
+	chsc_area->request_block.command_code1=0x0010;
+	chsc_area->request_block.command_code2=0x0010;
+	result=qdio_chsc(chsc_area);
+	if (result) {
+		QDIO_PRINT_WARN("Was not able to determine " \
+				"available CHSCs, cc=%i.\n",
+				result);
+		result=-EIO;
+		goto exit;
+	}
+	result=0;
+	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 " \
+				"available CHSCs.\n");
+		result=-EIO;
+		goto exit;
+	}
+	/* 4: request block
+	 * 2: general char
+	 * 512: chsc char */
+	/* check for bit 41 */
+	if ( (*(((unsigned int*)(chsc_area))+4+2+1)&0x00400000)!=0x00400000) {
+		QDIO_PRINT_WARN("Adapter interruption facility not " \
+				"installed.\n");
+		result=-ENOENT;
+		goto exit;
+	}
+	/* check for bits 107 and 108 */
+	if ( (*(((unsigned int*)(chsc_area))+4+512+3)&0x00180000)!=
+	     0x00180000 ) {
+		QDIO_PRINT_WARN("Set Chan Subsys. Char. & Fast-CHSCs " \
+				"not available.\n");
+		result=-ENOENT;
+		goto exit;
+	}
+exit:
+	return result;
+}
+
+/* the chsc_area is locked by the lock in qdio_activate */
+static unsigned int tiqdio_set_subchannel_ind(qdio_irq_t *irq_ptr,
+					     int reset_to_zero)
+{
+	unsigned long real_addr_local_summary_bit;
+	unsigned long real_addr_dev_st_chg_ind;
+	void *ptr;
+	char dbf_text[15];
+
+	unsigned int resp_code;
+	int result;
+
+	if (!irq_ptr->is_thinint_irq) return -ENODEV;
+
+	if (reset_to_zero) {
+		real_addr_local_summary_bit=0;
+		real_addr_dev_st_chg_ind=0;
+	} else {
+		real_addr_local_summary_bit=
+			virt_to_phys((volatile void *)indicators);
+		real_addr_dev_st_chg_ind=
+			virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind);
+	}
+
+	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
+	chsc_area->request_block.command_code1=0x0fe0;
+	chsc_area->request_block.command_code2=0x0021;
+	chsc_area->request_block.operation_code=0;
+	chsc_area->request_block.image_id=0;
+
+	chsc_area->request_block.operation_data_area.set_chsc.
+		summary_indicator_addr=real_addr_local_summary_bit;
+	chsc_area->request_block.operation_data_area.set_chsc.
+		subchannel_indicator_addr=real_addr_dev_st_chg_ind;
+	chsc_area->request_block.operation_data_area.set_chsc.
+		ks=QDIO_STORAGE_ACC_KEY;
+	chsc_area->request_block.operation_data_area.set_chsc.
+		kc=QDIO_STORAGE_ACC_KEY;
+	chsc_area->request_block.operation_data_area.set_chsc.
+		isc=TIQDIO_THININT_ISC;
+	chsc_area->request_block.operation_data_area.set_chsc.
+		subsystem_id=(1<<16)+irq_ptr->irq;
+
+	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;
+	}
+
+	resp_code=chsc_area->response_block.response_code;
+	if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) {
+		QDIO_PRINT_WARN("response upon setting indicators " \
+				"is 0x%x.\n",resp_code);
+		sprintf(dbf_text,"sidR%4x",resp_code);
+		QDIO_DBF_TEXT1(0,trace,dbf_text);
+		QDIO_DBF_TEXT1(0,setup,dbf_text);
+		ptr=&chsc_area->response_block;
+		QDIO_DBF_HEX2(1,setup,&ptr,QDIO_DBF_SETUP_LEN);
+		return -EIO;
+	}
+
+	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;
+}
+
+/* chsc_area would have to be locked if called from outside qdio_activate */
+static unsigned int tiqdio_set_delay_target(qdio_irq_t *irq_ptr,
+       					   unsigned long delay_target)
+{
+	unsigned int resp_code;
+	int result;
+	void *ptr;
+	char dbf_text[15];
+
+	if (!irq_ptr->is_thinint_irq) return -ENODEV;
+
+	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
+	chsc_area->request_block.command_code1=0x0fe0;
+	chsc_area->request_block.command_code2=0x1027;
+	chsc_area->request_block.operation_data_area.set_chsc_fast.
+		delay_target=delay_target<<16;
+
+	result=qdio_chsc(chsc_area);
+	if (result) {
+		QDIO_PRINT_WARN("could not set delay target on irq x%x, " \
+				"cc=%i. Continuing.\n",irq_ptr->irq,result);
+		return -EIO;
+	}
+
+	resp_code=chsc_area->response_block.response_code;
+	if (resp_code!=QDIO_CHSC_RESPONSE_CODE_OK) {
+		QDIO_PRINT_WARN("response upon setting delay target " \
+				"is 0x%x. Continuing.\n",resp_code);
+		sprintf(dbf_text,"sdtR%4x",resp_code);
+		QDIO_DBF_TEXT1(0,trace,dbf_text);
+		QDIO_DBF_TEXT1(0,setup,dbf_text);
+		ptr=&chsc_area->response_block;
+		QDIO_DBF_HEX2(1,trace,&ptr,QDIO_DBF_TRACE_LEN);
+	}
+	QDIO_DBF_TEXT2(0,trace,"delytrgt");
+	QDIO_DBF_HEX2(0,trace,&delay_target,sizeof(unsigned long));
+	return 0;
+}
+
+int qdio_cleanup(int irq,int how)
+{
+	qdio_irq_t *irq_ptr;
+	int i,result;
+	int do_an_irqrestore=0;
+	unsigned long flags;
+	int timeout;
+	char dbf_text[15]="12345678";
+
+	result=0;
+	sprintf(dbf_text,"qcln%4x",irq);
+	QDIO_DBF_TEXT1(0,trace,dbf_text);
+	QDIO_DBF_TEXT0(0,setup,dbf_text);
+
+	irq_ptr=qdio_get_irq_ptr(irq);
+	if (!irq_ptr) return -ENODEV;
+
+	spin_lock(&irq_ptr->setting_up_lock);
+
+	/* mark all qs as uninteresting */
+	for (i=0;i<irq_ptr->no_input_qs;i++) {
+		atomic_set(&irq_ptr->input_qs[i]->is_in_shutdown,1);
+	}
+	for (i=0;i<irq_ptr->no_output_qs;i++) {
+		atomic_set(&irq_ptr->output_qs[i]->is_in_shutdown,1);
+	}
+
+	tasklet_kill(&tiqdio_tasklet);
+
+	for (i=0;i<irq_ptr->no_input_qs;i++) {
+		qdio_unmark_q(irq_ptr->input_qs[i]);
+		tasklet_kill(&irq_ptr->input_qs[i]->tasklet);
+		if (qdio_wait_for_no_use_count(&irq_ptr->input_qs[i]->
+					       use_count))
+			result=-EINPROGRESS;
+	}
+
+	for (i=0;i<irq_ptr->no_output_qs;i++) {
+		tasklet_kill(&irq_ptr->output_qs[i]->tasklet);
+		if (qdio_wait_for_no_use_count(&irq_ptr->output_qs[i]->
+					       use_count))
+			result=-EINPROGRESS;
+	}
+
+	atomic_set(&irq_ptr->interrupt_has_been_cleaned,0);
+
+	/* cleanup subchannel */
+	s390irq_spin_lock_irqsave(irq,flags);
+	if (how&QDIO_FLAG_CLEANUP_USING_CLEAR) {
+		clear_IO(irq_ptr->irq,QDIO_DOING_CLEANUP,0);
+		timeout=QDIO_CLEANUP_CLEAR_TIMEOUT;
+	} else if (how&QDIO_FLAG_CLEANUP_USING_HALT) {
+		halt_IO(irq_ptr->irq,QDIO_DOING_CLEANUP,0);
+		timeout=QDIO_CLEANUP_HALT_TIMEOUT;
+	} else { /* default behaviour */
+		halt_IO(irq_ptr->irq,QDIO_DOING_CLEANUP,0);
+		timeout=QDIO_CLEANUP_HALT_TIMEOUT;
+	}
+	s390irq_spin_unlock_irqrestore(irq,flags);
+
+	if (qdio_sleepon(&irq_ptr->interrupt_has_been_cleaned,
+			 timeout,irq_ptr)==-ETIME) {
+		s390irq_spin_lock_irqsave(irq,flags);
+		QDIO_PRINT_INFO("Did not get interrupt on %s_IO, " \
+				"irq=0x%x.\n",
+				(how==QDIO_FLAG_CLEANUP_USING_CLEAR)?
+				"clear":"halt",irq);
+		do_an_irqrestore=1;
+	}
+
+	if (irq_ptr->is_thinint_irq) {
+		qdio_put_indicator((__u32*)irq_ptr->dev_st_chg_ind);
+		tiqdio_set_subchannel_ind(irq_ptr,1); /* reset adapter
+							interrupt indicators */
+	}
+
+	/* exchange int handlers, if necessary */
+	if ((void*)ioinfo[irq]->irq_desc.handler
+	    ==(void*)qdio_handler) {
+		ioinfo[irq]->irq_desc.handler=
+			irq_ptr->original_int_handler;
+/*		irq_ptr->original_int_handler=NULL; */
+	}
+
+	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE);
+
+	if (do_an_irqrestore)
+		s390irq_spin_unlock_irqrestore(irq,flags);
+
+	spin_unlock(&irq_ptr->setting_up_lock);
+
+	qdio_remove_irq_ptr(irq_ptr);
+	qdio_release_irq_memory(irq_ptr);
+
+	QDIO_DBF_TEXT3(0,setup,"MOD_DEC_");
+	MOD_DEC_USE_COUNT;
+
+	return result;
+}
+
+unsigned long qdio_get_characteristics(int irq)
+{
+	qdio_irq_t *irq_ptr;
+	int result=0;
+
+	irq_ptr=qdio_get_irq_ptr(irq);
+	if (!irq_ptr) return -ENODEV;
+
+	if (irq_ptr->state!=QDIO_IRQ_STATE_ACTIVE) {
+		return -EBUSY;
+	}
+
+	if (irq_ptr->state==QDIO_IRQ_STATE_INACTIVE) {
+		result|=QDIO_STATE_INACTIVE;
+	} else if (irq_ptr->state==QDIO_IRQ_STATE_ESTABLISHED) {
+		result|=QDIO_STATE_ESTABLISHED;
+	} else if (irq_ptr->state==QDIO_IRQ_STATE_ACTIVE) {
+		result|=QDIO_STATE_ACTIVE;
+	} else if (irq_ptr->state==QDIO_IRQ_STATE_STOPPED) {
+		result|=QDIO_STATE_STOPPED;
+	}
+
+	if (irq_ptr->hydra_gives_outbound_pcis)
+		result|=QDIO_STATE_MUST_USE_OUTB_PCI;
+
+	return result;
+}
+
+int qdio_initialize(qdio_initialize_t *init_data)
+{
+	int i,ciw_cnt;
+	unsigned long saveflags;
+	qdio_irq_t *irq_ptr=NULL;
+	int result,result2;
+	int found;
+	unsigned long flags;
+	char dbf_text[20]; /* if a printf would print out more than 8 chars */
+
+	down_interruptible(&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);
+	QDIO_DBF_HEX0(0,setup,&init_data->qib_param_field,sizeof(char*));
+	QDIO_DBF_HEX0(0,setup,&init_data->input_slib_elements,sizeof(long*));
+	QDIO_DBF_HEX0(0,setup,&init_data->output_slib_elements,sizeof(long*));
+	sprintf(dbf_text,"miit%4x",init_data->min_input_threshold);
+	QDIO_DBF_TEXT0(0,setup,dbf_text);
+	sprintf(dbf_text,"mait%4x",init_data->min_input_threshold);
+	QDIO_DBF_TEXT0(0,setup,dbf_text);
+	sprintf(dbf_text,"miot%4x",init_data->max_output_threshold);
+	QDIO_DBF_TEXT0(0,setup,dbf_text);
+	sprintf(dbf_text,"maot%4x",init_data->max_output_threshold);
+	QDIO_DBF_TEXT0(0,setup,dbf_text);
+	sprintf(dbf_text,"niq:%4x",init_data->no_input_qs);
+	QDIO_DBF_TEXT0(0,setup,dbf_text);
+	sprintf(dbf_text,"noq:%4x",init_data->no_output_qs);
+	QDIO_DBF_TEXT0(0,setup,dbf_text);
+	QDIO_DBF_HEX0(0,setup,&init_data->input_handler,sizeof(void*));
+	QDIO_DBF_HEX0(0,setup,&init_data->output_handler,sizeof(void*));
+	QDIO_DBF_HEX0(0,setup,&init_data->int_parm,sizeof(long));
+	QDIO_DBF_HEX0(0,setup,&init_data->flags,sizeof(long));
+	QDIO_DBF_HEX0(0,setup,&init_data->input_sbal_addr_array,sizeof(void*));
+	QDIO_DBF_HEX0(0,setup,&init_data->output_sbal_addr_array,sizeof(void*));
+	flags=init_data->flags;
+
+	/* sanity checks */
+	if (qdio_get_irq_ptr(init_data->irq)) {
+		result=-EBUSY;
+		goto out;
+	}
+
+	if (ioinfo[init_data->irq]==INVALID_STORAGE_AREA) {
+		result=-ENODEV;
+		goto out;
+	}
+
+	if (!ioinfo[init_data->irq]) {
+		QDIO_PRINT_WARN("ioinfo[%i] is NULL!\n",init_data->irq);
+		result=-ENODEV;
+		goto out;
+	}
+
+#define DEVSTAT_FLAG_IRQ_QDIO ~0UL
+	if (!(ioinfo[init_data->irq]->devstat.flag&DEVSTAT_FLAG_IRQ_QDIO)) {
+		QDIO_PRINT_WARN("ioinfo[%i]->devstat.flag=0x%08x\n",
+				init_data->irq,
+				ioinfo[init_data->irq]->devstat.flag);
+		result=-ENODEV;
+		goto out;
+	}
+
+	if ( (init_data->no_input_qs>QDIO_MAX_QUEUES_PER_IRQ) ||
+	     (init_data->no_output_qs>QDIO_MAX_QUEUES_PER_IRQ) ||
+	     ((init_data->no_input_qs) && (!init_data->input_handler)) ||
+	     ((init_data->no_output_qs) && (!init_data->output_handler)) ) {
+		result=-EINVAL;
+		goto out;
+	}
+
+	if ( (init_data->flags&QDIO_INBOUND_0COPY_SBALS)&&
+	     (!init_data->input_sbal_addr_array) ) {
+		result=-EINVAL;
+		goto out;
+	}
+	if ( (init_data->flags&QDIO_OUTBOUND_0COPY_SBALS)&&
+	     (!init_data->output_sbal_addr_array) ) {
+		result=-EINVAL;
+		goto out;
+	}
+
+	/* create irq */
+	irq_ptr=kmalloc(sizeof(qdio_irq_t),GFP_DMA);
+
+	QDIO_DBF_TEXT0(0,setup,"irq_ptr:");
+	QDIO_DBF_HEX0(0,setup,&irq_ptr,sizeof(void*));
+
+	if (!irq_ptr) {
+		QDIO_PRINT_ERR("kmalloc of irq_ptr failed!\n");
+		result=-ENOMEM;
+		goto out;
+	}
+
+	memset(irq_ptr,0,sizeof(qdio_irq_t)); /* wipes qib.ac,
+						 required by ar7063 */
+
+	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;
+      		goto out;
+       	}
+	memset(irq_ptr->qdr,0,sizeof(qdr_t));
+	QDIO_DBF_TEXT0(0,setup,"qdr:");
+	QDIO_DBF_HEX0(0,setup,&irq_ptr->qdr,sizeof(void*));
+
+	init_waitqueue_head(&irq_ptr->wait_q);
+
+	irq_ptr->int_parm=init_data->int_parm;
+
+	irq_ptr->irq=init_data->irq;
+	irq_ptr->no_input_qs=init_data->no_input_qs;
+	irq_ptr->no_output_qs=init_data->no_output_qs;
+
+	if (init_data->q_format==QDIO_IQDIO_QFMT) {
+		irq_ptr->is_iqdio_irq=1;
+		irq_ptr->is_thinint_irq=1;
+	} else {
+		irq_ptr->is_iqdio_irq=0;
+		irq_ptr->is_thinint_irq=hydra_thinints;
+	}
+	sprintf(dbf_text,"is_i_t%1x%1x",
+		irq_ptr->is_iqdio_irq,irq_ptr->is_thinint_irq);
+	QDIO_DBF_TEXT2(0,setup,dbf_text);
+
+	if (irq_ptr->is_thinint_irq) {
+		irq_ptr->dev_st_chg_ind=qdio_get_indicator();
+		QDIO_DBF_HEX1(0,setup,&irq_ptr->dev_st_chg_ind,sizeof(void*));
+		if (!irq_ptr->dev_st_chg_ind) {
+			QDIO_PRINT_WARN("no indicator location available " \
+					"for irq 0x%x\n",irq_ptr->irq);
+			qdio_release_irq_memory(irq_ptr);
+			result=-ENOBUFS;
+			goto out;
+		}
+	}
+
+	if (flags&QDIO_PFIX)
+		irq_ptr->other_flags |= QDIO_PFIX;
+
+	/* defaults */
+	irq_ptr->commands.eq=DEFAULT_ESTABLISH_QS_CMD;
+	irq_ptr->commands.count_eq=DEFAULT_ESTABLISH_QS_COUNT;
+	irq_ptr->commands.aq=DEFAULT_ACTIVATE_QS_CMD;
+	irq_ptr->commands.count_aq=DEFAULT_ACTIVATE_QS_COUNT;
+
+	if (!qdio_alloc_qs(irq_ptr,init_data->no_input_qs,
+			   init_data->no_output_qs,
+			   init_data->input_handler,
+			   init_data->output_handler,init_data->int_parm,
+			   init_data->q_format,init_data->flags,
+			   init_data->input_sbal_addr_array,
+			   init_data->output_sbal_addr_array)) {
+		qdio_release_irq_memory(irq_ptr);
+		result=-ENOMEM;
+		goto out;
+	}
+
+	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_INACTIVE);
+
+	irq_ptr->setting_up_lock=SPIN_LOCK_UNLOCKED;
+
+	MOD_INC_USE_COUNT;
+	QDIO_DBF_TEXT3(0,setup,"MOD_INC_");
+
+	spin_lock(&irq_ptr->setting_up_lock);
+
+	qdio_insert_irq_ptr(irq_ptr);
+
+	qdio_fill_thresholds(irq_ptr,init_data->no_input_qs,
+			     init_data->no_output_qs,
+			     init_data->min_input_threshold,
+			     init_data->max_input_threshold,
+			     init_data->min_output_threshold,
+			     init_data->max_output_threshold);
+
+	/* fill in qdr */
+	irq_ptr->qdr->qfmt=init_data->q_format;
+	irq_ptr->qdr->iqdcnt=init_data->no_input_qs;
+	irq_ptr->qdr->oqdcnt=init_data->no_output_qs;
+	irq_ptr->qdr->iqdsz=sizeof(qdesfmt0_t)/4; /* size in words */
+	irq_ptr->qdr->oqdsz=sizeof(qdesfmt0_t)/4;
+
+	irq_ptr->qdr->qiba=QDIO_PFIX_GET_ADDR(&irq_ptr->qib);
+	irq_ptr->qdr->qkey=QDIO_STORAGE_ACC_KEY;
+
+	/* fill in qib */
+	irq_ptr->qib.qfmt=init_data->q_format;
+	if (init_data->no_input_qs) irq_ptr->qib.isliba=
+				 QDIO_PFIX_GET_ADDR(irq_ptr->input_qs[0]->slib);
+	if (init_data->no_output_qs) irq_ptr->qib.osliba=(unsigned long)
+				  QDIO_PFIX_GET_ADDR(irq_ptr->output_qs[0]->slib);
+	memcpy(irq_ptr->qib.ebcnam,init_data->adapter_name,8);
+
+	qdio_set_impl_params(irq_ptr,init_data->qib_param_field_format,
+			     init_data->qib_param_field,
+			     init_data->no_input_qs,
+			     init_data->no_output_qs,
+			     init_data->input_slib_elements,
+			     init_data->output_slib_elements);
+
+	/* first input descriptors, then output descriptors */
+	for (i=0;i<init_data->no_input_qs;i++) {
+		irq_ptr->input_qs[i]->is_iqdio_q=
+			(init_data->q_format==QDIO_IQDIO_QFMT)?1:0;
+		irq_ptr->input_qs[i]->is_thinint_q=irq_ptr->is_thinint_irq;
+
+		irq_ptr->qdr->qdf0[i].sliba=
+			QDIO_PFIX_GET_ADDR(irq_ptr->input_qs[i]->slib);
+
+		irq_ptr->qdr->qdf0[i].sla=
+			QDIO_PFIX_GET_ADDR(irq_ptr->input_qs[i]->sl);
+
+		irq_ptr->qdr->qdf0[i].slsba=
+			QDIO_PFIX_GET_ADDR
+			((void *)&irq_ptr->input_qs[i]->slsb.acc.val[0]);
+
+		irq_ptr->qdr->qdf0[i].akey=QDIO_STORAGE_ACC_KEY;
+		irq_ptr->qdr->qdf0[i].bkey=QDIO_STORAGE_ACC_KEY;
+		irq_ptr->qdr->qdf0[i].ckey=QDIO_STORAGE_ACC_KEY;
+		irq_ptr->qdr->qdf0[i].dkey=QDIO_STORAGE_ACC_KEY;
+	}
+
+	for (i=0;i<init_data->no_output_qs;i++) {
+		irq_ptr->output_qs[i]->is_iqdio_q=
+			(init_data->q_format==QDIO_IQDIO_QFMT)?1:0;
+		irq_ptr->output_qs[i]->is_thinint_q=irq_ptr->is_thinint_irq;
+
+		irq_ptr->qdr->qdf0[i+init_data->no_input_qs].sliba=
+			QDIO_PFIX_GET_ADDR(irq_ptr->output_qs[i]->slib);
+
+		irq_ptr->qdr->qdf0[i+init_data->no_input_qs].sla=
+			QDIO_PFIX_GET_ADDR(irq_ptr->output_qs[i]->sl);
+
+		irq_ptr->qdr->qdf0[i+init_data->no_input_qs].slsba=
+			QDIO_PFIX_GET_ADDR
+			((void *)&irq_ptr->output_qs[i]->slsb.acc.val[0]);
+
+		irq_ptr->qdr->qdf0[i+init_data->no_input_qs].akey=
+			QDIO_STORAGE_ACC_KEY;
+		irq_ptr->qdr->qdf0[i+init_data->no_input_qs].bkey=
+			QDIO_STORAGE_ACC_KEY;
+		irq_ptr->qdr->qdf0[i+init_data->no_input_qs].ckey=
+			QDIO_STORAGE_ACC_KEY;
+		irq_ptr->qdr->qdf0[i+init_data->no_input_qs].dkey=
+			QDIO_STORAGE_ACC_KEY;
+	}
+	/* qdr, qib, sls, slsbs, slibs, sbales filled. */
+
+	s390irq_spin_lock_irqsave(irq_ptr->irq,saveflags);
+	/* keep track of original int handler */
+	irq_ptr->original_int_handler=ioinfo[irq_ptr->irq]->irq_desc.handler;
+
+	/* insert qdio int handler */
+	ioinfo[irq_ptr->irq]->irq_desc.handler=(void*)qdio_handler;
+
+	s390irq_spin_unlock_irqrestore(irq_ptr->irq,saveflags);
+
+#define TAKEOVER_CIW(x) irq_ptr->commands.x=ioinfo[irq_ptr->irq]->senseid.ciw[ciw_cnt].cmd; irq_ptr->commands.count_##x=ioinfo[irq_ptr->irq]->senseid.ciw[ciw_cnt].count
+	/* get qdio commands */
+	found=0;
+	for (ciw_cnt=0;ciw_cnt<MAX_CIWS;ciw_cnt++) {
+		switch (ioinfo[irq_ptr->irq]->senseid.ciw[ciw_cnt].ct) {
+		case CIW_TYPE_RCD: TAKEOVER_CIW(rcd); break;
+		case CIW_TYPE_SII: TAKEOVER_CIW(sii); break;
+		case CIW_TYPE_RNI: TAKEOVER_CIW(rni); break;
+		case CIW_TYPE_EQUEUE: TAKEOVER_CIW(eq); found++; break;
+		case CIW_TYPE_AQUEUE: TAKEOVER_CIW(aq); found++; break;
+		}
+		if ( (ciw_cnt>0) &&
+		     (ioinfo[irq_ptr->irq]->senseid.ciw[ciw_cnt].ct==0) &&
+		     (ioinfo[irq_ptr->irq]->senseid.ciw[ciw_cnt].cmd==0) &&
+		     (ioinfo[irq_ptr->irq]->senseid.ciw[ciw_cnt].count) )
+			break;
+	}
+	if (found<2) {
+		QDIO_DBF_TEXT2(1,setup,"no ciws");
+		QDIO_PRINT_INFO("No CIWs found for QDIO commands. Trying to " \
+				"use defaults.\n");
+	}
+
+	/* the thinint CHSC stuff */
+	if (irq_ptr->is_thinint_irq) {
+/*		iqdio_enable_adapter_int_facility(irq_ptr);*/
+
+		if (tiqdio_check_chsc_availability()) {
+			QDIO_PRINT_ERR("Not all CHSCs supported. " \
+				       "Continuing.\n");
+		}
+		result=tiqdio_set_subchannel_ind(irq_ptr,0);
+		if (result) {
+			spin_unlock(&irq_ptr->setting_up_lock);
+			qdio_cleanup(irq_ptr->irq,
+				     QDIO_FLAG_CLEANUP_USING_CLEAR);
+			goto out2;
+		}
+		tiqdio_set_delay_target(irq_ptr,TIQDIO_DELAY_TARGET);
+	}
+
+	/* establish q */
+	irq_ptr->ccw.cmd_code=irq_ptr->commands.eq;
+	irq_ptr->ccw.flags=CCW_FLAG_SLI;
+	irq_ptr->ccw.count=irq_ptr->commands.count_eq;
+	irq_ptr->ccw.cda=QDIO_GET_32BIT_ADDR(QDIO_PFIX_GET_ADDR(irq_ptr->qdr));
+
+	s390irq_spin_lock_irqsave(irq_ptr->irq,saveflags);
+	atomic_set(&irq_ptr->interrupt_has_arrived,0);
+
+	result=do_IO(irq_ptr->irq,&irq_ptr->ccw,QDIO_DOING_ESTABLISH,0,
+		     ((irq_ptr->other_flags&QDIO_PFIX)?DOIO_USE_DIAG98:0));
+	if (result) {
+		result2=do_IO(irq_ptr->irq,&irq_ptr->ccw,
+			      QDIO_DOING_ESTABLISH,0,
+			      ((irq_ptr->other_flags&QDIO_PFIX)?
+			       DOIO_USE_DIAG98:0));
+		sprintf(dbf_text,"eq:io%4x",result);
+		QDIO_DBF_TEXT2(1,setup,dbf_text);
+		if (result2) {
+			sprintf(dbf_text,"eq:io%4x",result);
+			QDIO_DBF_TEXT2(1,setup,dbf_text);
+		}
+		QDIO_PRINT_WARN("establish queues on irq %04x: do_IO " \
+                           "returned %i, next try returned %i\n",
+                           irq_ptr->irq,result,result2);
+		result=result2;
+	}
+
+	s390irq_spin_unlock_irqrestore(irq_ptr->irq,saveflags);
+
+	if (result) {
+		spin_unlock(&irq_ptr->setting_up_lock);
+		qdio_cleanup(irq_ptr->irq,QDIO_FLAG_CLEANUP_USING_CLEAR);
+		goto out2;
+	}
+
+	result=qdio_sleepon(&irq_ptr->interrupt_has_arrived,
+			    QDIO_ESTABLISH_TIMEOUT,irq_ptr);
+
+	if (result) {
+		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);
+		qdio_cleanup(irq_ptr->irq,QDIO_FLAG_CLEANUP_USING_CLEAR);
+		goto out2;
+	}
+
+	if (!(irq_ptr->io_result_dstat & DEV_STAT_DEV_END)) {
+		QDIO_DBF_TEXT2(1,setup,"eq:no de");
+		QDIO_DBF_HEX2(0,setup,&irq_ptr->io_result_dstat,
+			      sizeof(irq_ptr->io_result_dstat));
+		QDIO_DBF_HEX2(0,setup,&irq_ptr->io_result_cstat,
+			      sizeof(irq_ptr->io_result_cstat));
+		QDIO_DBF_HEX2(0,setup,&irq_ptr->io_result_flags,
+			      sizeof(irq_ptr->io_result_flags));
+		QDIO_PRINT_ERR("establish queues on irq %04x: didn't get " \
+			       "device end: dstat=%02x, cstat=%02x, " \
+			       "flags=%02x\n",
+			       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);
+		qdio_cleanup(irq_ptr->irq,QDIO_FLAG_CLEANUP_USING_CLEAR);
+		result=-EIO;
+		goto out2;
+	}
+
+	if (irq_ptr->io_result_dstat & ~(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) {
+		QDIO_DBF_TEXT2(1,setup,"eq:badio");
+		QDIO_DBF_HEX2(0,setup,&irq_ptr->io_result_dstat,
+			      sizeof(irq_ptr->io_result_dstat));
+		QDIO_DBF_HEX2(0,setup,&irq_ptr->io_result_cstat,
+			      sizeof(irq_ptr->io_result_cstat));
+		QDIO_DBF_HEX2(0,setup,&irq_ptr->io_result_flags,
+			      sizeof(irq_ptr->io_result_flags));
+		QDIO_PRINT_ERR("establish queues on irq %04x: got " \
+			       "the following devstat: dstat=%02x, " \
+			       "cstat=%02x, flags=%02x\n",
+			       irq_ptr->irq,irq_ptr->io_result_dstat,
+			       irq_ptr->io_result_cstat,
+			       irq_ptr->io_result_flags);
+		result=-EIO;
+		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;
+        }
+	sprintf(dbf_text,"qdioac%2x",irq_ptr->qdioac);
+	QDIO_DBF_TEXT2(0,setup,dbf_text);
+
+	sprintf(dbf_text,"qib ac%2x",irq_ptr->qib.ac);
+	QDIO_DBF_TEXT2(0,setup,dbf_text);
+
+	if (init_data->flags&QDIO_USE_OUTBOUND_PCIS) {
+		irq_ptr->hydra_gives_outbound_pcis=
+			irq_ptr->qib.ac&QIB_AC_OUTBOUND_PCI_SUPPORTED;
+	} else {
+		irq_ptr->hydra_gives_outbound_pcis=0;
+	}
+	irq_ptr->sync_done_on_outb_pcis=
+		irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS;
+
+	for (i=0;i<init_data->no_input_qs;i++) {
+		irq_ptr->input_qs[i]->siga_sync=
+			irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY;
+		irq_ptr->input_qs[i]->siga_in=
+			irq_ptr->qdioac&CHSC_FLAG_SIGA_INPUT_NECESSARY;
+		irq_ptr->input_qs[i]->siga_out=
+			irq_ptr->qdioac&CHSC_FLAG_SIGA_OUTPUT_NECESSARY;
+		irq_ptr->input_qs[i]->siga_sync_done_on_thinints=
+			irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS;
+		irq_ptr->input_qs[i]->hydra_gives_outbound_pcis=
+			irq_ptr->hydra_gives_outbound_pcis;
+		irq_ptr->input_qs[i]->siga_sync_done_on_outb_tis=
+			( (irq_ptr->qdioac&
+			   (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS|
+			    CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS))==
+			  (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS|
+			   CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS) );
+	}
+
+	for (i=0;i<init_data->no_output_qs;i++) {
+		irq_ptr->output_qs[i]->siga_sync=
+			irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_NECESSARY;
+		irq_ptr->output_qs[i]->siga_in=
+			irq_ptr->qdioac&CHSC_FLAG_SIGA_INPUT_NECESSARY;
+		irq_ptr->output_qs[i]->siga_out=
+			irq_ptr->qdioac&CHSC_FLAG_SIGA_OUTPUT_NECESSARY;
+		irq_ptr->output_qs[i]->siga_sync_done_on_thinints=
+			irq_ptr->qdioac&CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS;
+		irq_ptr->output_qs[i]->hydra_gives_outbound_pcis=
+			irq_ptr->hydra_gives_outbound_pcis;
+		irq_ptr->output_qs[i]->siga_sync_done_on_outb_tis=
+			( (irq_ptr->qdioac&
+			   (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS|
+			    CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS))==
+			  (CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS|
+			   CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS) );
+	}
+
+	if (init_data->qib_param_field)
+		memcpy(init_data->qib_param_field,irq_ptr->qib.parm,
+		       QDIO_MAX_BUFFERS_PER_Q);
+
+	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED);
+
+ out:
+	if (irq_ptr) {
+		spin_unlock(&irq_ptr->setting_up_lock);
+	}
+ out2:
+	up(&init_sema);
+
+	return result;
+}
+
+int qdio_activate(int irq,int flags)
+{
+	qdio_irq_t *irq_ptr;
+	int i,result=0,result2;
+	unsigned long saveflags;
+	char dbf_text[20]; /* see qdio_initialize */
+
+	irq_ptr=qdio_get_irq_ptr(irq);
+	if (!irq_ptr) return -ENODEV;
+
+	spin_lock(&irq_ptr->setting_up_lock);
+	if (irq_ptr->state==QDIO_IRQ_STATE_INACTIVE) {
+		result=-EBUSY;
+		goto out;
+	}
+
+	sprintf(dbf_text,"qact%4x",irq);
+	QDIO_DBF_TEXT2(0,setup,dbf_text);
+	QDIO_DBF_TEXT2(0,trace,dbf_text);
+
+	/* activate q */
+	irq_ptr->ccw.cmd_code=irq_ptr->commands.aq;
+	irq_ptr->ccw.flags=CCW_FLAG_SLI;
+	irq_ptr->ccw.count=irq_ptr->commands.count_aq;
+	irq_ptr->ccw.cda=QDIO_GET_32BIT_ADDR(0); /* no QDIO_PFIX_GET_ADDR here,
+						    not needed */
+
+	s390irq_spin_lock_irqsave(irq_ptr->irq,saveflags);
+	atomic_set(&irq_ptr->interrupt_has_arrived,0);
+
+	result=do_IO(irq_ptr->irq,&irq_ptr->ccw,QDIO_DOING_ACTIVATE,
+		     0,DOIO_REPORT_ALL|DOIO_DENY_PREFETCH|
+		     ((irq_ptr->other_flags&QDIO_PFIX)?DOIO_USE_DIAG98:0));
+	if (result) {
+		result2=do_IO(irq_ptr->irq,&irq_ptr->ccw,
+			      QDIO_DOING_ACTIVATE,0,DOIO_REPORT_ALL|
+			      ((irq_ptr->other_flags&QDIO_PFIX) ? 
+			       DOIO_USE_DIAG98:0));
+		sprintf(dbf_text,"aq:io%4x",result);
+		QDIO_DBF_TEXT2(1,setup,dbf_text);
+		if (result2) {
+			sprintf(dbf_text,"aq:io%4x",result);
+			QDIO_DBF_TEXT2(1,setup,dbf_text);
+		}
+		QDIO_PRINT_WARN("activate queues on irq %04x: do_IO " \
+                           "returned %i, next try returned %i\n",
+                           irq_ptr->irq,result,result2);
+		result=result2;
+	}
+
+	s390irq_spin_unlock_irqrestore(irq_ptr->irq,saveflags);
+	if (result) {
+		goto out;
+	}
+
+	if (!(flags&QDIO_FLAG_UNDER_INTERRUPT)) {
+		result=qdio_sleepon(&irq_ptr->interrupt_has_arrived,
+				    QDIO_ACTIVATE_TIMEOUT,irq_ptr);
+
+		if (result!=-ETIME) {
+			QDIO_DBF_TEXT2(1,setup,"aq:badio");
+			QDIO_DBF_HEX2(0,setup,&irq_ptr->io_result_dstat,
+				      sizeof(irq_ptr->io_result_dstat));
+			QDIO_DBF_HEX2(0,setup,&irq_ptr->io_result_cstat,
+				      sizeof(irq_ptr->io_result_cstat));
+			QDIO_DBF_HEX2(0,setup,&irq_ptr->io_result_flags,
+				      sizeof(irq_ptr->io_result_flags));
+			QDIO_PRINT_ERR("activate queues on irq %04x: got " \
+				       "the following devstat: dstat=%02x, " \
+				       "cstat=%02x, flags=%02x\n",
+				       irq_ptr->irq,irq_ptr->io_result_dstat,
+				       irq_ptr->io_result_cstat,
+				       irq_ptr->io_result_flags);
+			result=-EIO;
+			goto out;
+		} else { /* result is -ETIME, but timeout is ok for us */
+			result=0;
+		}
+	} else result=0;
+
+	for (i=0;i<irq_ptr->no_input_qs;i++) {
+		if (irq_ptr->is_thinint_irq) {
+			/* that way we know, that, if we will
+			 * get interrupted
+			 * by tiqdio_inbound_processing,
+			 * qdio_unmark_q will
+			 * not be called */
+			qdio_reserve_q(irq_ptr->input_qs[i]);
+			qdio_mark_tiq(irq_ptr->input_qs[i]);
+			qdio_release_q(irq_ptr->input_qs[i]);
+		}
+	}
+
+	if (flags&QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT) {
+		for (i=0;i<irq_ptr->no_input_qs;i++) {
+			irq_ptr->input_qs[i]->is_input_q|=
+				QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT;
+		}
+	}
+
+	qdio_wait_nonbusy(QDIO_ACTIVATE_DELAY);
+
+	qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ACTIVE);
+
+ out:
+	spin_unlock(&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)
+{
+	for (;;) {
+		if (!q->is_0copy_sbals_q) {
+			memcpy((void*)q->sbal[qidx],buffers,SBAL_SIZE);
+			q->qdio_buffers[qidx]=buffers;
+			buffers++;
+		}
+		set_slsb(&q->slsb.acc.val[qidx],SLSB_CU_INPUT_EMPTY);
+		count--;
+		if (!count) break;
+		qidx=(qidx+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+	}
+
+	/* not necessary, as the queues are synced during the SIGA read
+	SYNC_MEMORY;*/
+}
+
+static inline void qdio_do_qdio_fill_output(qdio_q_t *q,unsigned int qidx,
+					    unsigned int count,
+					    qdio_buffer_t *buffers)
+{
+	for (;;) {
+		if (!q->is_0copy_sbals_q) {
+			memcpy((void*)q->sbal[qidx],buffers,SBAL_SIZE);
+			q->qdio_buffers[qidx]=buffers;
+			buffers++;
+		}
+		set_slsb(&q->slsb.acc.val[qidx],SLSB_CU_OUTPUT_PRIMED);
+		count--;
+		if (!count) break;
+		qidx=(qidx+1)&(QDIO_MAX_BUFFERS_PER_Q-1);
+	}
+
+	/* SIGA write will sync the queues
+	SYNC_MEMORY;*/
+}
+
+/* count must be 1 in iqdio */
+int do_QDIO(int irq,unsigned int callflags,unsigned int queue_number,
+	    unsigned int qidx,unsigned int count,qdio_buffer_t *buffers)
+{
+	qdio_q_t *q;
+	qdio_irq_t *irq_ptr;
+	int result;
+	int used_elements;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	char dbf_text[20];
+
+	sprintf(dbf_text,"doQD%04x",irq);
+	QDIO_DBF_TEXT3(0,trace,dbf_text);
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	if ( (qidx>QDIO_MAX_BUFFERS_PER_Q) ||
+	     (count>QDIO_MAX_BUFFERS_PER_Q) ||
+	     (queue_number>QDIO_MAX_QUEUES_PER_IRQ) )
+		return -EINVAL;
+
+	if (count==0) return 0;
+
+	irq_ptr=qdio_get_irq_ptr(irq);
+	if (!irq_ptr) return -ENODEV;
+
+#ifdef QDIO_DBF_LIKE_HELL
+	if (callflags&QDIO_FLAG_SYNC_INPUT)
+		QDIO_DBF_HEX3(0,trace,&irq_ptr->input_qs[queue_number],
+			      sizeof(void*));
+	else
+		QDIO_DBF_HEX3(0,trace,&irq_ptr->output_qs[queue_number],
+			      sizeof(void*));
+	sprintf(dbf_text,"flag%04x",callflags);
+	QDIO_DBF_TEXT3(0,trace,dbf_text);
+	sprintf(dbf_text,"qi%02xct%02x",qidx,count);
+	QDIO_DBF_TEXT3(0,trace,dbf_text);
+	if ( ((callflags&QDIO_FLAG_SYNC_INPUT)&&
+	      (!irq_ptr->input_qs[queue_number]->is_0copy_sbals_q)) ||
+	     ((callflags&QDIO_FLAG_SYNC_OUTPUT)&&
+	      (!irq_ptr->output_qs[queue_number]->is_0copy_sbals_q)) )
+		QDIO_DBF_HEX5(0,sbal,buffers,256);
+#endif /* QDIO_DBF_LIKE_HELL */
+
+	if (irq_ptr->state!=QDIO_IRQ_STATE_ACTIVE) {
+		return -EBUSY;
+	}
+
+	if (callflags&QDIO_FLAG_SYNC_INPUT) {
+		/* This is the inbound handling of queues */
+		q=irq_ptr->input_qs[queue_number];
+
+		used_elements=atomic_return_add(count,
+						&q->number_of_buffers_used);
+
+		qdio_do_qdio_fill_input(q,qidx,count,buffers);
+
+		if ((used_elements+count==QDIO_MAX_BUFFERS_PER_Q)&&
+		    (callflags&QDIO_FLAG_UNDER_INTERRUPT))
+			atomic_swap(&q->polling,0);
+
+		if (!used_elements) if (!(callflags&QDIO_FLAG_DONT_SIGA)) {
+			if (q->siga_in) {
+				result=qdio_siga_input(q);
+				if (result) {
+					if (q->siga_error)
+						q->error_status_flags|=
+							QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;
+					q->error_status_flags|=
+						QDIO_STATUS_LOOK_FOR_ERROR;
+					q->siga_error=result;
+				}
+			}
+
+			qdio_mark_q(q);
+		}
+	} else if (callflags&QDIO_FLAG_SYNC_OUTPUT) {
+		/* This is the outbound handling of queues */
+#ifdef QDIO_PERFORMANCE_STATS
+		perf_stats.start_time_outbound=NOW;
+#endif /* QDIO_PERFORMANCE_STATS */
+		q=irq_ptr->output_qs[queue_number];
+
+		qdio_do_qdio_fill_output(q,qidx,count,buffers);
+
+		used_elements=atomic_return_add(count,
+						&q->number_of_buffers_used);
+
+		if (!(callflags&QDIO_FLAG_DONT_SIGA)) {
+			if (q->is_iqdio_q) {
+				/* one siga for every sbal */
+				while (count--) {
+					qdio_kick_outbound_q(q);
+				}
+
+				qdio_outbound_processing(q);
+			} else {
+				/* under VM, we do a SIGA sync
+				 * unconditionally */
+				SYNC_MEMORY;
+				else {
+					/* w/o shadow queues (else branch of
+					 * SYNC_MEMORY :-/ ), we try to
+					 * fast-requeue buffers */
+					if (q->slsb.acc.val
+					    [(qidx+QDIO_MAX_BUFFERS_PER_Q-1)
+					    &(QDIO_MAX_BUFFERS_PER_Q-1)]!=
+					    SLSB_CU_OUTPUT_PRIMED) {
+						qdio_kick_outbound_q(q);
+					} else {
+#ifdef QDIO_DBF_LIKE_HELL
+						QDIO_DBF_TEXT3(0,trace,
+							       "fast-req");
+#endif /* QDIO_DBF_LIKE_HELL */
+#ifdef QDIO_PERFORMANCE_STATS
+						perf_stats.fast_reqs++;
+#endif /* QDIO_PERFORMANCE_STATS */
+					}
+				}
+				/* only marking the q could take
+				 * too long, the upper layer
+				 * module could do a lot of
+				 * traffic in that time */
+				qdio_outbound_processing(q);
+			}
+		}
+
+#ifdef QDIO_PERFORMANCE_STATS
+		perf_stats.outbound_time+=NOW-perf_stats.start_time_outbound;
+		perf_stats.outbound_cnt++;
+#endif /* QDIO_PERFORMANCE_STATS */
+	} else {
+		QDIO_DBF_TEXT3(1,trace,"doQD:inv");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#ifdef QDIO_PERFORMANCE_STATS
+static int qdio_perf_procfile_read(char *buffer,char **buffer_location,
+       				   off_t offset,int buffer_length,int *eof,
+				   void *data)
+{
+        int c=0,bucket;
+	qdio_irq_t *irq_ptr;
+
+        /* we are always called with buffer_length=4k, so we all
+           deliver on the first read */
+        if (offset>0) return 0;
+
+#define _OUTP_IT(x...) c+=sprintf(buffer+c,x)
+	_OUTP_IT("i_p_nc/c=%lu/%lu\n",i_p_nc,i_p_c);
+	_OUTP_IT("ii_p_nc/c=%lu/%lu\n",ii_p_nc,ii_p_c);
+	_OUTP_IT("o_p_nc/c=%lu/%lu\n",o_p_nc,o_p_c);
+	_OUTP_IT("Number of tasklet runs (total)                  : %u\n",
+		 perf_stats.tl_runs);
+	_OUTP_IT("\n");
+	_OUTP_IT("Number of SIGA sync's issued                    : %u\n",
+		 perf_stats.siga_syncs);
+	_OUTP_IT("Number of SIGA in's issued                      : %u\n",
+		 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",
+		 perf_stats.pcis);
+	_OUTP_IT("Number of adapter interrupts caught             : %u\n",
+		 perf_stats.thinints);
+	_OUTP_IT("Number of fast requeues (outg. SBALs w/o SIGA)  : %u\n",
+		 perf_stats.fast_reqs);
+	_OUTP_IT("\n");
+	_OUTP_IT("Total time of all inbound actions (us) incl. UL : %u\n",
+		 perf_stats.inbound_time);
+	_OUTP_IT("Number of inbound transfers                     : %u\n",
+		 perf_stats.inbound_cnt);
+	_OUTP_IT("Total time of all outbound do_QDIOs (us)        : %u\n",
+		 perf_stats.outbound_time);
+	_OUTP_IT("Number of do_QDIOs outbound                     : %u\n",
+		 perf_stats.outbound_cnt);
+	_OUTP_IT("\n");
+
+	for (bucket=0;bucket<QDIO_IRQ_BUCKETS;bucket++) {
+		irq_ptr=first_irq[bucket];
+
+		while (irq_ptr) {
+			_OUTP_IT("Polling time on irq %4x" \
+				 "                        : %u\n",
+				 irq_ptr->irq,irq_ptr->input_qs[0]->
+				 timing.threshold);
+			irq_ptr=irq_ptr->next;
+		}
+	}
+
+        return c;
+}
+
+static struct proc_dir_entry *qdio_perf_proc_file;
+#endif /* QDIO_PERFORMANCE_STATS */
+
+static void qdio_add_procfs_entry(void)
+{
+#ifdef QDIO_PERFORMANCE_STATS
+        proc_perf_file_registration=0;
+	qdio_perf_proc_file=create_proc_entry(QDIO_PERF,
+					      S_IFREG|0444,&proc_root);
+	if (qdio_perf_proc_file) {
+		qdio_perf_proc_file->read_proc=&qdio_perf_procfile_read;
+	} else proc_perf_file_registration=-1;
+
+        if (proc_perf_file_registration)
+                QDIO_PRINT_WARN("was not able to register perf. " \
+				"proc-file (%i).\n",
+				proc_perf_file_registration);
+#endif /* QDIO_PERFORMANCE_STATS */
+}
+#ifdef MODULE
+static void qdio_remove_procfs_entry(void)
+{
+#ifdef QDIO_PERFORMANCE_STATS
+	perf_stats.tl_runs=0;
+
+        if (!proc_perf_file_registration) /* means if it went ok earlier */
+		remove_proc_entry(QDIO_PERF,&proc_root);
+#endif /* QDIO_PERFORMANCE_STATS */
+}
+#endif /* MODULE */
+
+static void tiqdio_register_thinints(void)
+{
+	char dbf_text[20];
+	register_thinint_result=
+		s390_register_adapter_interrupt(&tiqdio_thinint_handler);
+	if (register_thinint_result) {
+		sprintf(dbf_text,"regthn%x",(register_thinint_result&0xff));
+		QDIO_DBF_TEXT0(0,setup,dbf_text);
+		QDIO_PRINT_ERR("failed to register adapter handler " \
+			       "(rc=%i).\nAdapter interrupts might " \
+			       "not work. Continuing.\n",
+			       register_thinint_result);
+	}
+}
+
+#ifdef MODULE
+static void tiqdio_unregister_thinints(void)
+{
+	if (!register_thinint_result)
+		s390_unregister_adapter_interrupt(&tiqdio_thinint_handler);
+}
+#endif /* MODULE */
+
+static int qdio_get_qdio_memory(void)
+{
+	int i;
+	indicator_used[0]=1;
+
+	for (i=1;i<INDICATORS_PER_CACHELINE;i++)
+		indicator_used[i]=0;
+	indicators=(__u32*)kmalloc(sizeof(__u32)*(INDICATORS_PER_CACHELINE),
+				   GFP_KERNEL);
+       	if (!indicators) return -ENOMEM;
+	memset(indicators,0,sizeof(__u32)*(INDICATORS_PER_CACHELINE));
+
+	chsc_area=(qdio_chsc_area_t *)
+		kmalloc(sizeof(qdio_chsc_area_t),GFP_KERNEL);
+	QDIO_DBF_TEXT3(0,trace,"chscarea"); \
+	QDIO_DBF_HEX3(0,trace,&chsc_area,sizeof(void*)); \
+	if (!chsc_area) {
+		/* ahem... let's call it data area */
+		QDIO_PRINT_ERR("not enough memory for data area. Cannot " \
+			       "initialize QDIO.\n");
+		kfree(indicators);
+		return -ENOMEM;
+	}
+	memset(chsc_area,0,sizeof(qdio_chsc_area_t));
+	return 0;
+}
+
+#ifdef MODULE
+static void qdio_release_qdio_memory(void)
+{
+	kfree(chsc_area);
+	if (indicators) kfree(indicators);
+}
+#endif /* MODULE */
+
+int init_module(void); /* we want to use it in init_QDIO */
+
+static void qdio_unregister_dbf_views(void)
+{
+	if (qdio_dbf_setup)
+		debug_unregister(qdio_dbf_setup);
+	if (qdio_dbf_sbal)
+		debug_unregister(qdio_dbf_sbal);
+	if (qdio_dbf_sense)
+		debug_unregister(qdio_dbf_sense);
+	if (qdio_dbf_trace)
+		debug_unregister(qdio_dbf_trace);
+#ifdef QDIO_DBF_LIKE_HELL
+        if (qdio_dbf_slsb_out)
+                debug_unregister(qdio_dbf_slsb_out);
+        if (qdio_dbf_slsb_in)
+                debug_unregister(qdio_dbf_slsb_in);
+#endif /* QDIO_DBF_LIKE_HELL */
+}
+
+/* this is not __initfunc, as it is called from init_module */
+int init_QDIO(void)
+{
+	char dbf_text[20];
+	int res;
+#if defined(MODULE)||defined(QDIO_PERFORMANCE_STATS)
+	void *ptr;
+#endif /* defined(MODULE)||defined(QDIO_PERFORMANCE_STATS) */
+
+	qdio_eyecatcher();
+
+	printk("qdio: loading %s\n",version);
+
+	res=qdio_get_qdio_memory();
+	if (res) return res;
+
+	sema_init(&init_sema,1);
+
+	qdio_dbf_setup=debug_register(QDIO_DBF_SETUP_NAME,
+				      QDIO_DBF_SETUP_INDEX,
+				      QDIO_DBF_SETUP_NR_AREAS,
+				      QDIO_DBF_SETUP_LEN);
+	if (!qdio_dbf_setup) goto oom;
+	debug_register_view(qdio_dbf_setup,&debug_hex_ascii_view);
+	debug_set_level(qdio_dbf_setup,QDIO_DBF_SETUP_LEVEL);
+
+	qdio_dbf_sbal=debug_register(QDIO_DBF_SBAL_NAME,
+				     QDIO_DBF_SBAL_INDEX,
+				     QDIO_DBF_SBAL_NR_AREAS,
+				     QDIO_DBF_SBAL_LEN);
+	if (!qdio_dbf_sbal) goto oom;
+
+	debug_register_view(qdio_dbf_sbal,&debug_hex_ascii_view);
+	debug_set_level(qdio_dbf_sbal,QDIO_DBF_SBAL_LEVEL);
+
+	qdio_dbf_sense=debug_register(QDIO_DBF_SENSE_NAME,
+				      QDIO_DBF_SENSE_INDEX,
+				      QDIO_DBF_SENSE_NR_AREAS,
+				      QDIO_DBF_SENSE_LEN);
+	if (!qdio_dbf_sense) goto oom;
+
+	debug_register_view(qdio_dbf_sense,&debug_hex_ascii_view);
+	debug_set_level(qdio_dbf_sense,QDIO_DBF_SENSE_LEVEL);
+
+	qdio_dbf_trace=debug_register(QDIO_DBF_TRACE_NAME,
+				      QDIO_DBF_TRACE_INDEX,
+				      QDIO_DBF_TRACE_NR_AREAS,
+				      QDIO_DBF_TRACE_LEN);
+	if (!qdio_dbf_trace) goto oom;
+
+	debug_register_view(qdio_dbf_trace,&debug_hex_ascii_view);
+	debug_set_level(qdio_dbf_trace,QDIO_DBF_TRACE_LEVEL);
+
+#ifdef QDIO_DBF_LIKE_HELL
+        qdio_dbf_slsb_out=debug_register(QDIO_DBF_SLSB_OUT_NAME,
+                                         QDIO_DBF_SLSB_OUT_INDEX,
+                                         QDIO_DBF_SLSB_OUT_NR_AREAS,
+                                         QDIO_DBF_SLSB_OUT_LEN);
+        if (!qdio_dbf_slsb_out) goto oom;
+        debug_register_view(qdio_dbf_slsb_out,&debug_hex_ascii_view);
+        debug_set_level(qdio_dbf_slsb_out,QDIO_DBF_SLSB_OUT_LEVEL);
+
+        qdio_dbf_slsb_in=debug_register(QDIO_DBF_SLSB_IN_NAME,
+                                        QDIO_DBF_SLSB_IN_INDEX,
+                                        QDIO_DBF_SLSB_IN_NR_AREAS,
+                                        QDIO_DBF_SLSB_IN_LEN);
+        if (!qdio_dbf_slsb_in) goto oom;
+        debug_register_view(qdio_dbf_slsb_in,&debug_hex_ascii_view);
+        debug_set_level(qdio_dbf_slsb_in,QDIO_DBF_SLSB_IN_LEVEL);
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_PERFORMANCE_STATS
+       	memset((void*)&perf_stats,0,sizeof(perf_stats));
+	QDIO_DBF_TEXT0(0,setup,"perfstat");
+	ptr=&perf_stats;
+	QDIO_DBF_HEX0(0,setup,&ptr,sizeof(void*));
+#endif /* QDIO_PERFORMANCE_STATS */
+
+#ifdef MODULE
+	QDIO_DBF_TEXT0(0,setup,"initmodl");
+	ptr=&init_module;
+	QDIO_DBF_HEX0(0,setup,&ptr,sizeof(void*));
+#endif /* MODULE */
+
+	qdio_add_procfs_entry();
+
+	hydra_thinints=qdio_check_for_hydra_thinints();
+
+	sprintf(dbf_text,"hydrati%1x",hydra_thinints);
+	QDIO_DBF_TEXT0(0,setup,dbf_text);
+
+	tiqdio_register_thinints();
+
+	return 0;
+ oom:
+	QDIO_PRINT_ERR("not enough memory for dbf.\n");
+	qdio_unregister_dbf_views();
+	return -ENOMEM;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return init_QDIO();
+}
+
+void cleanup_module(void)
+{
+	tiqdio_unregister_thinints();
+
+	qdio_remove_procfs_entry();
+
+	qdio_release_qdio_memory();
+
+	qdio_unregister_dbf_views();
+
+  	printk("qdio: %s: module removed\n",version);
+}
+#else /* MODULE */
+static int __init initcall_QDIO(void)
+{
+	        return init_QDIO();
+}
+__initcall(initcall_QDIO);
+#endif /* MODULE */
+
+EXPORT_SYMBOL(qdio_initialize);
+EXPORT_SYMBOL(qdio_activate);
+EXPORT_SYMBOL(do_QDIO);
+EXPORT_SYMBOL(qdio_cleanup);
+EXPORT_SYMBOL(qdio_eyecatcher);
+EXPORT_SYMBOL(qdio_synchronize);
+
diff -urN linux-2.4.21-bk9/drivers/sbus/sbus.c linux-2.4.21-bk10/drivers/sbus/sbus.c
--- linux-2.4.21-bk9/drivers/sbus/sbus.c	2002-08-02 17:39:44.000000000 -0700
+++ linux-2.4.21-bk10/drivers/sbus/sbus.c	2003-07-15 02:55:06.000000000 -0700
@@ -20,6 +20,9 @@
 struct sbus_bus *sbus_root = NULL;
 
 static struct linux_prom_irqs irqs[PROMINTR_MAX] __initdata = { { 0 } };
+#ifdef CONFIG_SPARC32
+static int interrupts[PROMINTR_MAX] __initdata = { 0 };
+#endif
 
 /* Perhaps when I figure out more about the iommu we'll put a
  * device registration routine here that probe_sbus() calls to
@@ -122,20 +125,37 @@
 #else
 	len = prom_getproperty(prom_node, "intr",
 			       (char *)irqs, sizeof(irqs));
-	if (len == -1)
-		len = 0;
-	sdev->num_irqs = len / 8;
-	if (sdev->num_irqs == 0) {
-		sdev->irqs[0] = 0;
-	} else if (sparc_cpu_model == sun4d) {
-		extern unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq);
+	if (len != -1) {
+		sdev->num_irqs = len / 8;
+		if (sdev->num_irqs == 0) {
+			sdev->irqs[0] = 0;
+		} else if (sparc_cpu_model == sun4d) {
+			extern unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq);
 
-		for (len = 0; len < sdev->num_irqs; len++)
-			sdev->irqs[len] = sun4d_build_irq(sdev, irqs[len].pri);
+			for (len = 0; len < sdev->num_irqs; len++)
+				sdev->irqs[len] = sun4d_build_irq(sdev, irqs[len].pri);
+		} else {
+			for (len = 0; len < sdev->num_irqs; len++)
+				sdev->irqs[len] = irqs[len].pri;
+		}
 	} else {
-		for (len = 0; len < sdev->num_irqs; len++)
-			sdev->irqs[len] = irqs[len].pri;
-	}
+		/* No "intr" node found-- check for "interrupts" node.
+		 * This node contains SBus interrupt levels, not IPLs
+		 * as in "intr", and no vector values.  We convert 
+		 * SBus interrupt levels to PILs (platform specific).
+		 */
+		len = prom_getproperty(prom_node, "interrupts", 
+					(char *)interrupts, sizeof(interrupts));
+		if (len == -1) {
+			sdev->irqs[0] = 0;
+			sdev->num_irqs = 0;
+		} else {
+			sdev->num_irqs = len / sizeof(int);
+			for (len = 0; len < sdev->num_irqs; len++) {
+				sdev->irqs[len] = sbint_to_irq(sdev, interrupts[len]);
+			}
+		}
+	} 
 #endif /* !__sparc_v9__ */
 }
 
diff -urN linux-2.4.21-bk9/drivers/scsi/nsp32.h linux-2.4.21-bk10/drivers/scsi/nsp32.h
--- linux-2.4.21-bk9/drivers/scsi/nsp32.h	2003-06-13 07:51:36.000000000 -0700
+++ linux-2.4.21-bk10/drivers/scsi/nsp32.h	2003-07-15 02:55:07.000000000 -0700
@@ -425,5 +425,5 @@
 #define BUSPHASE_STATUS      ( BUSMON_STATUS      & BUSMON_PHASE_MASK )
 #define BUSPHASE_SELECT      ( BUSMON_SEL | BUSMON_IO )
 
-#endif _NSP32_H
+#endif /* _NSP32_H */
 /* end */
diff -urN linux-2.4.21-bk9/drivers/scsi/nsp32_io.h linux-2.4.21-bk10/drivers/scsi/nsp32_io.h
--- linux-2.4.21-bk9/drivers/scsi/nsp32_io.h	2003-06-13 07:51:36.000000000 -0700
+++ linux-2.4.21-bk10/drivers/scsi/nsp32_io.h	2003-07-15 02:55:07.000000000 -0700
@@ -265,5 +265,5 @@
 	nsp32_multi_write4(base, FIFO_DATA_LOW, buf, count);
 }
 
-#endif _NSP32_IO_H
+#endif /* _NSP32_IO_H */
 /* end */
diff -urN linux-2.4.21-bk9/drivers/sound/ac97_codec.c linux-2.4.21-bk10/drivers/sound/ac97_codec.c
--- linux-2.4.21-bk9/drivers/sound/ac97_codec.c	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/sound/ac97_codec.c	2003-07-15 02:55:07.000000000 -0700
@@ -740,7 +740,6 @@
 	memset(codec, 0, sizeof(*codec));
 	spin_lock_init(&codec->lock);
 	INIT_LIST_HEAD(&codec->list);
-	list_add(&codec->list, &codecs);
 	return codec;
 }
 
@@ -869,6 +868,7 @@
 	 */
 	 
 	down(&codec_sem);
+	list_add(&codec->list, &codecs);
 
 	list_for_each(l, &codec_drivers) {
 		d = list_entry(l, struct ac97_driver, list);
diff -urN linux-2.4.21-bk9/drivers/sound/ac97_plugin_ad1980.c linux-2.4.21-bk10/drivers/sound/ac97_plugin_ad1980.c
--- linux-2.4.21-bk9/drivers/sound/ac97_plugin_ad1980.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/drivers/sound/ac97_plugin_ad1980.c	2003-07-15 02:55:07.000000000 -0700
@@ -0,0 +1,124 @@
+/*
+    ac97_plugin_ad1980.c  Copyright (C) 2003 Red Hat, Inc. All rights reserved.
+
+   The contents of this file are subject to the Open Software License version 1.1
+   that can be found at http://www.opensource.org/licenses/osl-1.1.txt and is 
+   included herein by reference. 
+   
+   Alternatively, the contents of this file may be used under the
+   terms of the GNU General Public License version 2 (the "GPL") as 
+   distributed in the kernel source COPYING file, in which
+   case the provisions of the GPL are applicable instead of the
+   above.  If you wish to allow the use of your version of this file
+   only under the terms of the GPL and not to allow others to use
+   your version of this file under the OSL, indicate your decision
+   by deleting the provisions above and replace them with the notice
+   and other provisions required by the GPL.  If you do not delete
+   the provisions above, a recipient may use your version of this
+   file under either the OSL or the GPL.
+   
+   Authors: 	Alan Cox <alan@redhat.com>
+
+   This is an example codec plugin. This one switches the connections
+   around to match the setups some vendors use with audio switched to
+   non standard front connectors not the normal rear ones
+
+   This code primarily exists to demonstrate how to use the codec
+   interface
+
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/ac97_codec.h>
+
+/**
+ *	ad1980_remove		-	codec remove callback
+ *	@codec: The codec that is being removed
+ *
+ *	This callback occurs when an AC97 codec is being removed. A
+ *	codec remove call will not occur for a codec during that codec
+ *	probe callback.
+ *
+ *	Most drivers will need to lock their remove versus their 
+ *	use of the codec after the probe function.
+ */
+ 
+static void ad1980_remove(struct ac97_codec *codec)
+{
+	/* Nothing to do in the simple example */
+}
+
+
+/**
+ *	ad1980_probe		-	codec found callback
+ *	@codec: ac97 codec matching the idents
+ *	@driver: ac97_driver it matched
+ *
+ *	This entry point is called when a codec is found which matches
+ *	the driver. At the point it is called the codec is basically
+ *	operational, mixer operations have been initialised and can
+ *	be overriden. Called in process context. The field driver_private
+ *	is available for the driver to use to store stuff.
+ *
+ *	The caller can claim the device by returning zero, or return
+ *	a negative error code. 
+ */
+ 
+static int ad1980_probe(struct ac97_codec *codec, struct ac97_driver *driver)
+{
+	u16 control;
+
+#define AC97_AD_MISC	0x76
+
+	/* Switch the inputs/outputs over (from Dell code) */
+	control = codec->codec_read(codec, AC97_AD_MISC);
+	codec->codec_write(codec, AC97_AD_MISC, control | 0x0420);
+	
+	/* We could refuse the device since we dont need to hang around,
+	   but we will claim it */
+	return 0;
+}
+	
+ 
+static struct ac97_driver ad1980_driver = {
+	codec_id: 0x41445370,
+	codec_mask: 0xFFFFFFFF,
+	name: "AD1980 example",
+	probe:	ad1980_probe,
+	remove: __devexit_p(ad1980_remove),
+};
+
+/**
+ *	ad1980_exit		-	module exit path
+ *
+ *	Our module is being unloaded. At this point unregister_driver
+ *	will call back our remove handler for any existing codecs. You
+ *	may not unregister_driver from interrupt context or from a 
+ *	probe/remove callback.
+ */
+
+static void ad1980_exit(void)
+{
+	ac97_unregister_driver(&ad1980_driver);
+}
+
+/**
+ *	ad1980_init		-	set up ad1980 handlers
+ *
+ *	After we call the register function it will call our probe
+ *	function for each existing matching device before returning to us.
+ *	Any devices appearing afterwards whose id's match the codec_id
+ *	will also cause the probe function to be called.
+ *	You may not register_driver from interrupt context or from a 
+ *	probe/remove callback.
+ */
+ 
+static int ad1980_init(void)
+{
+	return ac97_register_driver(&ad1980_driver);
+}
+
+module_init(ad1980_init);
+module_exit(ad1980_exit);
diff -urN linux-2.4.21-bk9/drivers/sound/cs46xx.c linux-2.4.21-bk10/drivers/sound/cs46xx.c
--- linux-2.4.21-bk9/drivers/sound/cs46xx.c	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/sound/cs46xx.c	2003-07-15 02:55:07.000000000 -0700
@@ -4257,7 +4257,6 @@
 	for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) {
 		if ((codec = ac97_alloc_codec()) == NULL)
 			return -ENOMEM;
-		memset(codec, 0, sizeof(struct ac97_codec));
 
 		/* initialize some basic codec information, other fields will be filled
 		   in ac97_probe_codec */
diff -urN linux-2.4.21-bk9/drivers/sound/i810_audio.c linux-2.4.21-bk10/drivers/sound/i810_audio.c
--- linux-2.4.21-bk9/drivers/sound/i810_audio.c	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/sound/i810_audio.c	2003-07-15 02:55:07.000000000 -0700
@@ -2875,7 +2875,6 @@
 		
 		if ((codec = ac97_alloc_codec()) == NULL)
 			return -ENOMEM;
-		memset(codec, 0, sizeof(struct ac97_codec));
 
 		/* initialize some basic codec information, other fields will be filled
 		   in ac97_probe_codec */
diff -urN linux-2.4.21-bk9/drivers/sound/via82cxxx_audio.c linux-2.4.21-bk10/drivers/sound/via82cxxx_audio.c
--- linux-2.4.21-bk9/drivers/sound/via82cxxx_audio.c	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/sound/via82cxxx_audio.c	2003-07-15 02:55:07.000000000 -0700
@@ -3226,7 +3226,7 @@
 	/* inform device of an upcoming pause in input (or output). */
 	case SNDCTL_DSP_POST:
 		DPRINTK ("DSP_POST\n");
-		if (0 && wr) {
+		if (wr) {
 			if (card->ch_out.slop_len > 0)
 				via_chan_flush_frag (&card->ch_out);
 			via_chan_maybe_start (&card->ch_out);
diff -urN linux-2.4.21-bk9/drivers/usb/ax8817x.c linux-2.4.21-bk10/drivers/usb/ax8817x.c
--- linux-2.4.21-bk9/drivers/usb/ax8817x.c	2003-07-15 02:54:59.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/ax8817x.c	2003-07-15 02:55:07.000000000 -0700
@@ -10,6 +10,8 @@
  *
  * History 
  *
+ *	2003-06-28 - Dave Hollis <dhollis@davehollis.com>  1.0.2
+ *		* Added support for Intellinet
  *	2003-06-12 - Dave Hollis <dhollis@davehollis.com>  1.0.1
  *		* use usb_make_path for ethtool info
  *		* Use crc32.h for crc functions
@@ -174,6 +176,8 @@
       {USB_DEVICE(0x0846, 0x1040), driver_info:0x00130103},
 	/* D-Link DUB-E100 */
       {USB_DEVICE(0x2001, 0x1a00), driver_info:0x009f9d9f},
+	/* Intellinet USB Ethernet*/
+      {USB_DEVICE(0x0b95, 0x1720), driver_info:0x00130103},
 
 	{}
 };
diff -urN linux-2.4.21-bk9/drivers/usb/host/Config.in linux-2.4.21-bk10/drivers/usb/host/Config.in
--- linux-2.4.21-bk9/drivers/usb/host/Config.in	2003-06-13 07:51:36.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/host/Config.in	2003-07-15 02:55:07.000000000 -0700
@@ -12,4 +12,6 @@
    define_bool CONFIG_USB_UHCI_ALT n
 fi
 dep_tristate '  OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
-
+if [ "$CONFIG_ARM" = "y" ]; then
+  dep_tristate '  SL811HS Alternate (support isochornous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB
+fi
diff -urN linux-2.4.21-bk9/drivers/usb/host/Makefile linux-2.4.21-bk10/drivers/usb/host/Makefile
--- linux-2.4.21-bk9/drivers/usb/host/Makefile	2003-06-13 07:51:36.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/host/Makefile	2003-07-15 02:55:07.000000000 -0700
@@ -9,6 +9,7 @@
 obj-$(CONFIG_USB_UHCI_ALT)			+= uhci.o
 obj-$(CONFIG_USB_UHCI)				+= usb-uhci.o
 obj-$(CONFIG_USB_OHCI)				+= usb-ohci.o
+obj-$(CONFIG_USB_SL811HS_ALT)  			+= sl811.o
 
 # Extract lists of the multi-part drivers.
 # The 'int-*' lists are the intermediate files used to build the multi's.
diff -urN linux-2.4.21-bk9/drivers/usb/host/sl811.c linux-2.4.21-bk10/drivers/usb/host/sl811.c
--- linux-2.4.21-bk9/drivers/usb/host/sl811.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/drivers/usb/host/sl811.c	2003-07-15 02:55:07.000000000 -0700
@@ -0,0 +1,2749 @@
+/*
+ * SL811 Host Controller Interface driver for USB.
+ *
+ * Copyright (c) 2003/06, Courage Co., Ltd.
+ *
+ * Based on: 
+ *	1.uhci.c by Linus Torvalds, Johannes Erdfelt, Randy Dunlap,
+ * 	  Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber,
+ * 	  Adam Richter, Gregory P. Smith;
+  	2.Original SL811 driver (hc_sl811.o) by Pei Liu <pbl@cypress.com>
+ *
+ * It's now support isochronous mode and more effective than hc_sl811.o
+ *
+ * To do:
+ *	1.Modify the timeout part, it's some messy
+ *	2.Use usb-a and usb-b set in Ping-Pong mode
+ 	
+ * 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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/list.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/irq.h>
+#include <linux/usb.h>
+
+#include "../hcd.h"
+#include "../hub.h"
+#include "sl811.h"
+
+#define DRIVER_VERSION "v0.28"
+#define DRIVER_AUTHOR "Yin Aihua <yinah@couragetech.com.cn>"
+#define DRIVER_DESC "Sl811 USB Host Controller Driver"
+
+static int sl811_addr_io = 0xf100000e;
+static int sl811_data_io = 0xf100000f;
+static int sl811_irq = 44;
+
+static LIST_HEAD(sl811_hcd_list);
+
+/*
+ * 0, normal prompt and information
+ * 1, error should not occur in normal
+ * 2, error maybe occur in normal
+ * 3, useful and detail debug information
+ * 4, function level enter and level inforamtion
+ * 5, endless information will output because of timer function or interrupt 
+ */
+static int debug = 0;
+
+MODULE_PARM(sl811_addr_io,"i");
+MODULE_PARM_DESC(sl811_addr_io,"sl811 address io port 0xf100000e");
+MODULE_PARM(sl811_data_io,"i");
+MODULE_PARM_DESC(sl811_data_io,"sl811 data io port 0xf100000f");
+MODULE_PARM(sl811_irq,"i");
+MODULE_PARM_DESC(sl811_irq,"sl811 irq 44(default)");
+MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug,"debug level");
+
+static void sl811_rh_int_timer_do(unsigned long ptr);
+static void sl811_transfer_done(struct sl811_hc *hc, int sof); 
+
+/*
+ * Read	a byte of data from the	SL811H/SL11H
+ */
+static __u8 sl811_read(struct sl811_hc *hc,	__u8 offset)
+{
+	__u8 data;
+
+	writeb(offset, hc->addr_io);
+	wmb();
+	data = readb(hc->data_io);
+	rmb();
+
+	return data;
+}
+
+/*
+ * Write a byte	of data	to the SL811H/SL11H
+ */
+static void sl811_write(struct sl811_hc *hc, __u8 offset, __u8 data)
+{
+	writeb(offset, hc->addr_io);
+	writeb(data, hc->data_io);
+	wmb();
+}
+
+/*
+ * Read	consecutive bytes of data from the SL811H/SL11H	buffer
+ */
+static void sl811_read_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size)
+{
+	writeb(offset, hc->addr_io);
+	wmb();
+	while (size--) {
+		*buf++ = readb(hc->data_io);
+		rmb();
+	}
+}
+
+/*
+ * Write consecutive bytes of data to the SL811H/SL11H buffer
+ */
+static void sl811_write_buf(struct sl811_hc *hc, __u8 offset, __u8 *buf, __u8 size)
+{
+	writeb(offset, hc->addr_io);
+	wmb();
+	while (size--) {
+		writeb(*buf, hc->data_io);
+		wmb();
+		buf++;
+	}
+}
+
+/*
+ * This	routine	test the Read/Write functionality of SL811HS registers
+ */
+static int sl811_reg_test(struct sl811_hc *hc)
+{
+	int i, data, result = 0;
+	__u8 buf[256];
+
+	for (i = 0x10; i < 256;	i++) {
+		/* save	the original buffer */
+		buf[i] = sl811_read(hc,	i);
+
+		/* Write the new data to the buffer */
+		sl811_write(hc,	i, i);
+	}
+
+	/* compare the written data */
+	for (i = 0x10; i < 256;	i++) {
+		data = sl811_read(hc, i);
+		if (data != i) {
+			PDEBUG(1, "Pattern test failed!! value = 0x%x, s/b 0x%x", data, i);
+			result = -1;
+		}
+	}
+
+	/* restore the data */
+	for (i = 0x10; i < 256;	i++)
+		sl811_write(hc,	i, buf[i]);
+
+	return result;
+}
+
+/*
+ * Display all SL811HS register	values
+ */
+static void sl811_reg_show(struct sl811_hc *hc)
+{
+	int i;
+
+	for (i = 0; i <	256; i++)
+		PDEBUG(4, "offset %d: 0x%x", i, sl811_read(hc, i));
+}
+
+/*
+ * This	function enables SL811HS interrupts
+ */
+static void sl811_enable_interrupt(struct sl811_hc *hc)
+{
+	PDEBUG(4, "enter");
+	sl811_write(hc, SL811_INTR, SL811_INTR_DONE_A | SL811_INTR_SOF | SL811_INTR_INSRMV);
+}
+
+/*
+ * This	function disables SL811HS interrupts
+ */
+static void sl811_disable_interrupt(struct sl811_hc *hc)
+{
+	PDEBUG(4, "enter");
+	// Disable all other interrupt except for insert/remove.
+	sl811_write(hc,	SL811_INTR, SL811_INTR_INSRMV);
+}
+
+/*
+ * SL811 Virtual Root Hub
+ */
+
+/* Device descriptor */
+static __u8 sl811_rh_dev_des[] =
+{
+	0x12,       /*  __u8  bLength; */
+	0x01,       /*  __u8  bDescriptorType; Device */
+	0x10,	    /*  __u16 bcdUSB; v1.1 */
+	0x01,
+	0x09,	    /*  __u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,	    /*  __u8  bDeviceSubClass; */
+	0x00,       /*  __u8  bDeviceProtocol; */
+	0x08,       /*  __u8  bMaxPacketSize0; 8 Bytes */
+	0x00,       /*  __u16 idVendor; */
+	0x00,
+	0x00,       /*  __u16 idProduct; */
+ 	0x00,
+	0x00,       /*  __u16 bcdDevice; */
+ 	0x00,
+	0x00,       /*  __u8  iManufacturer; */
+	0x02,       /*  __u8  iProduct; */
+	0x01,       /*  __u8  iSerialNumber; */
+	0x01        /*  __u8  bNumConfigurations; */
+};
+
+/* Configuration descriptor */
+static __u8 sl811_rh_config_des[] =
+{
+	0x09,       /*  __u8  bLength; */
+	0x02,       /*  __u8  bDescriptorType; Configuration */
+	0x19,       /*  __u16 wTotalLength; */
+	0x00,
+	0x01,       /*  __u8  bNumInterfaces; */
+	0x01,       /*  __u8  bConfigurationValue; */
+	0x00,       /*  __u8  iConfiguration; */
+	0x40,       /*  __u8  bmAttributes;
+                    Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,
+                    4..0: resvd */
+	0x00,       /*  __u8  MaxPower; */
+
+	/* interface */
+	0x09,       /*  __u8  if_bLength; */
+	0x04,       /*  __u8  if_bDescriptorType; Interface */
+	0x00,       /*  __u8  if_bInterfaceNumber; */
+	0x00,       /*  __u8  if_bAlternateSetting; */
+	0x01,       /*  __u8  if_bNumEndpoints; */
+	0x09,       /*  __u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,       /*  __u8  if_bInterfaceSubClass; */
+	0x00,       /*  __u8  if_bInterfaceProtocol; */
+	0x00,       /*  __u8  if_iInterface; */
+
+	/* endpoint */
+	0x07,       /*  __u8  ep_bLength; */
+	0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
+	0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
+ 	0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+ 	0x08,       /*  __u16 ep_wMaxPacketSize; */
+ 	0x00,
+	0xff        /*  __u8  ep_bInterval; 255 ms */
+};
+
+/* root hub class descriptor*/
+static __u8 sl811_rh_hub_des[] =
+{
+	0x09,			/*  __u8  bLength; */
+	0x29,			/*  __u8  bDescriptorType; Hub-descriptor */
+	0x01,			/*  __u8  bNbrPorts; */
+	0x00,			/* __u16  wHubCharacteristics; */
+	0x00,
+	0x50,			/*  __u8  bPwrOn2pwrGood; 2ms */
+	0x00,			/*  __u8  bHubContrCurrent; 0 mA */
+	0xfc,			/*  __u8  DeviceRemovable; *** 7 Ports max *** */
+	0xff			/*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
+};
+
+/*
+ * This function examine the port change in the virtual root hub. HUB INTERRUPT ENDPOINT.
+ */
+static int sl811_rh_send_irq(struct sl811_hc *hc, __u8 *rh_change, int rh_len)
+{
+	__u8 data = 0;
+		
+	PDEBUG(5, "enter");
+
+	/*
+	 * Right now, It is assume the power is good and no changes and only one port.
+	 */
+	if (hc->rh_status.wPortChange & (USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE)) {  
+		data = 1<<1;
+		*(__u8 *)rh_change = data;
+		return 1;
+	} else
+		return 0;
+}
+
+/*
+ * This function creates a timer that act as interrupt pipe in the virtual hub.
+ *
+ * Note:  The virtual root hub's interrupt pipe are polled by the timer
+ *        every "interval" ms
+ */
+static void sl811_rh_init_int_timer(struct urb * urb)
+{
+	 struct sl811_hc *hc = urb->dev->bus->hcpriv;
+	 hc->rh.interval = urb->interval;
+
+	 init_timer(&hc->rh.rh_int_timer);
+	 hc->rh.rh_int_timer.function = sl811_rh_int_timer_do;
+	 hc->rh.rh_int_timer.data = (unsigned long)urb;
+	 hc->rh.rh_int_timer.expires = jiffies +
+		(HZ * (urb->interval < 30? 30: urb->interval)) / 1000;
+	 add_timer (&hc->rh.rh_int_timer);
+}
+
+/*
+ * This function is called when the timer expires.  It gets the the port
+ * change data and pass along to the upper protocol.
+ */
+static void sl811_rh_int_timer_do(unsigned long ptr)
+{
+	int len;
+	struct urb *urb = (struct urb *)ptr;
+	struct sl811_hc *hc = urb->dev->bus->hcpriv;
+	PDEBUG (5, "enter");
+
+	if(hc->rh.send) {
+		len = sl811_rh_send_irq(hc, urb->transfer_buffer,
+			urb->transfer_buffer_length);
+		if (len > 0) {
+			urb->actual_length = len;
+			if (urb->complete)
+				urb->complete(urb);
+		}
+	}
+
+#ifdef SL811_TIMEOUT
+	
+{
+	struct list_head *head, *tmp;
+	struct sl811_urb_priv *urbp;
+	struct urb *u;
+	int i;
+	static int timeout_count = 0;
+
+// check time out every second
+	if (++timeout_count > 4) {
+		int max_scan = hc->active_urbs;
+		timeout_count = 0;
+		for (i = 0; i < 6; ++i) {
+			head = &hc->urb_list[i];
+			tmp = head->next;
+			while (tmp != head && max_scan--) {
+				u = list_entry(tmp, struct urb, urb_list);
+				urbp = (struct sl811_urb_priv *)u->hcpriv;
+				tmp = tmp->next;
+				// Check if the URB timed out
+				if (u->timeout && time_after_eq(jiffies, urbp->inserttime + u->timeout)) {
+					PDEBUG(3, "urb = %p time out, we kill it", urb);
+					u->transfer_flags |= USB_TIMEOUT_KILLED;
+				}
+			}
+		}
+	}
+}
+
+#endif
+	// re-activate the timer
+	sl811_rh_init_int_timer(urb);
+}
+
+/* helper macro */
+#define OK(x)	len = (x); break
+
+/*
+ * This function handles all USB request to the the virtual root hub
+ */
+static int sl811_rh_submit_urb(struct urb *urb)
+{
+	struct usb_device *usb_dev = urb->dev;
+	struct sl811_hc *hc = usb_dev->bus->hcpriv;
+	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *)urb->setup_packet;
+	void *data = urb->transfer_buffer;
+	int buf_len = urb->transfer_buffer_length;
+	unsigned int pipe = urb->pipe;
+	__u8 data_buf[16];
+	__u8 *bufp = data_buf;
+	int len = 0;
+	int status = 0;
+	
+	__u16 bmRType_bReq;
+	__u16 wValue;
+	__u16 wIndex;
+	__u16 wLength;
+
+	if (usb_pipeint(pipe)) {
+		hc->rh.urb =  urb;
+		hc->rh.send = 1;
+		hc->rh.interval = urb->interval;
+		sl811_rh_init_int_timer(urb);
+		urb->status = 0;
+
+		return 0;
+	}
+
+	bmRType_bReq  = cmd->bRequestType | (cmd->bRequest << 8);
+	wValue        = le16_to_cpu (cmd->wValue);
+	wIndex        = le16_to_cpu (cmd->wIndex);
+	wLength       = le16_to_cpu (cmd->wLength);
+
+	PDEBUG(5, "submit rh urb, req = %d(%x) len=%d", bmRType_bReq, bmRType_bReq, wLength);
+
+	/* Request Destination:
+		   without flags: Device,
+		   USB_RECIP_INTERFACE: interface,
+		   USB_RECIP_ENDPOINT: endpoint,
+		   USB_TYPE_CLASS means HUB here,
+		   USB_RECIP_OTHER | USB_TYPE_CLASS  almost ever means HUB_PORT here
+	*/
+	switch (bmRType_bReq) {
+	case RH_GET_STATUS:
+		*(__u16 *)bufp = cpu_to_le16(1);
+		OK(2);
+
+	case RH_GET_STATUS | USB_RECIP_INTERFACE:
+		*(__u16 *)bufp = cpu_to_le16(0);
+		OK(2);
+
+	case RH_GET_STATUS | USB_RECIP_ENDPOINT:
+		*(__u16 *)bufp = cpu_to_le16(0);
+		OK(2);
+
+	case RH_GET_STATUS | USB_TYPE_CLASS:
+		*(__u32 *)bufp = cpu_to_le32(0);
+		OK(4);
+
+	case RH_GET_STATUS | USB_RECIP_OTHER | USB_TYPE_CLASS:
+		*(__u32 *)bufp = cpu_to_le32(hc->rh_status.wPortChange<<16 | hc->rh_status.wPortStatus);
+		OK(4);
+
+	case RH_CLEAR_FEATURE | USB_RECIP_ENDPOINT:
+		switch (wValue)	{
+		case 1: 
+			OK(0);
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | USB_TYPE_CLASS:
+		switch (wValue) {
+		case C_HUB_LOCAL_POWER:
+			OK(0);
+
+		case C_HUB_OVER_CURRENT:
+			OK(0);
+		}
+		break;
+
+	case RH_CLEAR_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
+		switch (wValue) {
+		case USB_PORT_FEAT_ENABLE:
+			hc->rh_status.wPortStatus &= ~USB_PORT_STAT_ENABLE;
+			OK(0);
+
+		case USB_PORT_FEAT_SUSPEND:
+			hc->rh_status.wPortStatus &= ~USB_PORT_STAT_SUSPEND;
+			OK(0);
+
+		case USB_PORT_FEAT_POWER:
+			hc->rh_status.wPortStatus &= ~USB_PORT_STAT_POWER;
+			OK(0);
+
+		case USB_PORT_FEAT_C_CONNECTION:
+			hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_CONNECTION;
+			OK(0);
+
+		case USB_PORT_FEAT_C_ENABLE:
+			hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_ENABLE;
+			OK(0);
+
+		case USB_PORT_FEAT_C_SUSPEND:
+			hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_SUSPEND;
+			OK(0);
+
+		case USB_PORT_FEAT_C_OVER_CURRENT:
+			hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_OVERCURRENT;
+			OK(0);
+
+		case USB_PORT_FEAT_C_RESET:
+			hc->rh_status.wPortChange &= ~USB_PORT_STAT_C_RESET;
+			OK(0);
+		}
+		break;
+
+	case RH_SET_FEATURE | USB_RECIP_OTHER | USB_TYPE_CLASS:
+		switch (wValue) {
+		case USB_PORT_FEAT_SUSPEND:
+			hc->rh_status.wPortStatus |= USB_PORT_STAT_SUSPEND;
+			OK(0);
+
+		case USB_PORT_FEAT_RESET:
+			hc->rh_status.wPortStatus |= USB_PORT_STAT_RESET;
+			hc->rh_status.wPortChange = 0;
+			hc->rh_status.wPortChange |= USB_PORT_STAT_C_RESET;
+			hc->rh_status.wPortStatus &= ~USB_PORT_STAT_RESET;
+			hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
+			OK(0);
+
+		case USB_PORT_FEAT_POWER:
+			hc->rh_status.wPortStatus |= USB_PORT_STAT_POWER;
+			OK(0);
+
+		case USB_PORT_FEAT_ENABLE:
+			hc->rh_status.wPortStatus |= USB_PORT_STAT_ENABLE;
+			OK(0);
+		}
+		break;
+
+	case RH_SET_ADDRESS:
+		hc->rh.devnum = wValue;
+		OK(0);
+
+	case RH_GET_DESCRIPTOR:
+		switch ((wValue & 0xff00) >> 8) {
+		case USB_DT_DEVICE:
+			len = sizeof(sl811_rh_dev_des);
+			bufp = sl811_rh_dev_des;
+			OK(len);
+
+		case USB_DT_CONFIG: 
+			len = sizeof(sl811_rh_config_des);
+			bufp = sl811_rh_config_des;
+			OK(len);
+
+		case USB_DT_STRING:
+			len = usb_root_hub_string(wValue & 0xff, (int)(long)0,	"SL811HS", data, wLength);
+			if (len > 0) {
+				bufp = data;
+				OK(len);
+			}
+		
+		default:
+			status = -EPIPE;
+		}
+		break;
+
+	case RH_GET_DESCRIPTOR | USB_TYPE_CLASS:
+		len = sizeof(sl811_rh_hub_des);
+		bufp = sl811_rh_hub_des;
+		OK(len);
+
+	case RH_GET_CONFIGURATION:
+		bufp[0] = 0x01;
+		OK(1);
+
+	case RH_SET_CONFIGURATION:
+		OK(0);
+
+	default:
+		PDEBUG(1, "unsupported root hub command");
+		status = -EPIPE;
+	}
+
+	len = min(len, buf_len);
+	if (data != bufp)
+		memcpy(data, bufp, len);
+	urb->actual_length = len;
+	urb->status = status;
+
+	PDEBUG(5, "len = %d, status = %d", len, status);
+	
+	urb->hcpriv = NULL;
+	urb->dev = NULL;
+	if (urb->complete)
+		urb->complete(urb);
+
+	return 0;
+}
+
+/*
+ * This function unlinks the URB
+ */
+static int sl811_rh_unlink_urb(struct urb *urb)
+{
+	struct sl811_hc *hc = urb->dev->bus->hcpriv;
+
+	PDEBUG(5, "enter");
+	
+	if (hc->rh.urb == urb) {
+		hc->rh.send = 0;
+		del_timer(&hc->rh.rh_int_timer);
+		hc->rh.urb = NULL;
+		urb->hcpriv = NULL;
+		usb_dec_dev_use(urb->dev);
+		urb->dev = NULL;
+		if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+			urb->status = -ECONNRESET;
+			if (urb->complete)
+				urb->complete(urb);
+		} else
+			urb->status = -ENOENT;
+	}
+
+	return 0;
+}
+
+/*
+ * This function connect the virtual root hub to the USB stack
+ */
+static int sl811_connect_rh(struct sl811_hc * hc)
+{
+	struct usb_device *usb_dev;
+
+	hc->rh.devnum = 0;
+	usb_dev = usb_alloc_dev(NULL, hc->bus);
+	if (!usb_dev)
+		return -ENOMEM;
+
+	hc->bus->root_hub = usb_dev;
+	usb_connect(usb_dev);
+
+	if (usb_new_device(usb_dev)) {
+		usb_free_dev(usb_dev);
+		return -ENODEV;
+	}
+	
+	PDEBUG(5, "leave success");
+	
+	return 0;
+}
+
+/*
+ * This function allocates private data space for the usb device
+ */
+static int sl811_alloc_dev_priv(struct usb_device *usb_dev)
+{
+	return 0;
+}
+
+/*
+ * This function de-allocates private data space for the usb devic
+ */
+static int sl811_free_dev_priv (struct usb_device *usb_dev)
+{
+	return 0;
+}
+
+/*
+ * This function allocates private data space for the urb
+ */
+static struct sl811_urb_priv* sl811_alloc_urb_priv(struct urb *urb)
+{
+	struct sl811_urb_priv *urbp;
+	
+	urbp = kmalloc(sizeof(*urbp), GFP_KERNEL);
+	if (!urbp)
+		return NULL;
+	
+	memset(urbp, 0, sizeof(*urbp));
+	
+	INIT_LIST_HEAD(&urbp->td_list);
+	
+	urbp->urb = urb;
+	urb->hcpriv = urbp;
+	
+	return urbp;
+}
+
+/*
+ * This function free private data space for the urb
+ */
+static void sl811_free_urb_priv(struct urb *urb)
+{
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td;
+	struct list_head *head, *tmp;
+	
+	if (!urbp)
+		return ;
+	
+	head = &urbp->td_list;
+	tmp = head->next;
+	
+	while (tmp != head) {
+		td = list_entry(tmp, struct sl811_td, td_list);
+		tmp = tmp->next;
+		kfree(td);
+	}
+	
+	kfree(urbp);
+	urb->hcpriv = NULL;
+	
+	return ;
+}
+
+/*
+ * This	function calculate the bus time need by this td.
+ * Fix me! Can this use usb_calc_bus_time()?
+ */
+static void sl811_calc_td_time(struct sl811_td *td)
+{
+#if 1
+	int time;
+	int len = td->len;
+	struct sl811_hc *hc = td->urb->dev->bus->hcpriv; 
+
+	if (hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED)
+		time = 8*8*len + 1024;
+	else {
+		if (td->ctrl & SL811_USB_CTRL_PREAMBLE)
+			time = 8*8*len + 2048;
+		else
+			time = 8*len + 256;
+	}
+
+	time += 2*10 * len;
+
+	td->bustime = time;
+	
+#else
+
+	unsigned long tmp;
+	int time;
+	int low_speed = usb_pipeslow(td->urb->pipe);
+	int input_dir = usb_pipein(td->urb->pipe);
+	int bytecount = td->len;
+	int isoc = usb_pipeisoc(td->urb->pipe); 
+
+	if (low_speed) {	/* no isoc. here */
+		if (input_dir) {
+			tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
+			time =  (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+		} else {
+			tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
+			time =  (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+		}
+	} else if (!isoc){	/* for full-speed: */
+		tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+		time = (9107L + BW_HOST_DELAY + tmp);
+	} else {		/* for isoc: */
+		tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+		time =  (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+	}
+	
+	td->bustime = time / 84;
+
+#endif		 
+}
+
+/*
+ * This	function calculate the remainder bus time in current frame.
+ */
+static inline int sl811_calc_bus_remainder(struct sl811_hc *hc)
+{
+	return (sl811_read(hc, SL811_SOFCNTDIV) * 64);
+}
+
+/*
+ * This function allocates td for the urb
+ */
+static struct sl811_td* sl811_alloc_td(struct urb *urb)
+{
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td;
+	
+	td = kmalloc(sizeof (*td), GFP_KERNEL);
+	if (!td)
+		return NULL;
+	
+	memset(td, 0, sizeof(*td));
+	
+	INIT_LIST_HEAD(&td->td_list);
+	
+	td->urb = urb;
+	list_add_tail(&td->td_list, &urbp->td_list);
+	
+	return td;
+}
+
+/*
+ * Fill the td.
+ */
+static inline void sl811_fill_td(struct sl811_td *td, __u8 ctrl, __u8 addr, __u8 len, __u8 pidep, __u8 dev, __u8 *buf)
+{
+	td->ctrl = ctrl;
+	td->addr = addr;
+	td->len = len;
+	td->pidep = pidep;
+	td->dev = dev;
+	td->buf = buf;
+	td->left = len;
+	td->errcnt = 3;
+}
+
+/*
+ * Fill the td.
+ */
+static inline void sl811_reset_td(struct sl811_td *td)
+{
+	td->status = 0;
+	td->left = td->len;
+	td->done = 0;
+	td->errcnt = 3;
+	td->nakcnt = 0;
+	td->td_status = 0;
+}
+
+static void sl811_print_td(int level, struct sl811_td *td)
+{
+	 PDEBUG(level, "td = %p, ctrl = %x, addr = %x, len = %x, pidep = %x\n 
+		dev = %x, status = %x, left = %x, errcnt = %x, done = %x\n 
+		buf = %p, bustime = %d, td_status = %d\n", 
+		td, td->ctrl, td->addr, td->len, td->pidep,
+		td->dev, td->status, td->left, td->errcnt, td->done,
+		td->buf, td->bustime, td->td_status);
+}
+
+/*
+ * Isochronous transfers
+ */
+static int sl811_submit_isochronous(struct urb *urb)
+{
+	__u8 dev = usb_pipedevice(urb->pipe);
+	__u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
+	__u8 ctrl = 0;
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td = NULL;
+	int i;
+	
+	PDEBUG(4, "enter, urb = %p, urbp = %p", urb, urbp);
+	
+	/* Can't have low speed bulk transfers */
+	if (usb_pipeslow(urb->pipe)) {
+		PDEBUG(1, "error, urb = %p, low speed device", urb);
+		return -EINVAL;
+	}
+	
+	if (usb_pipeout(urb->pipe))
+		ctrl |= SL811_USB_CTRL_DIR_OUT;
+		
+	ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_ISO;
+	
+	for (i = 0; i < urb->number_of_packets; i++) {
+		urb->iso_frame_desc[i].actual_length = 0;
+		urb->iso_frame_desc[i].status = -EXDEV;
+			
+		td = sl811_alloc_td(urb);
+		if (!td)
+			return -ENOMEM;
+
+		sl811_fill_td(td, ctrl, SL811_DATA_START, 
+			urb->iso_frame_desc[i].length,
+			pidep, dev,
+			urb->transfer_buffer + urb->iso_frame_desc[i].offset);
+		sl811_calc_td_time(td);
+		if (urbp->cur_td == NULL)
+			urbp->cur_td = urbp->first_td = td;	
+	}
+	
+	urbp->last_td = td;	
+	
+	PDEBUG(4, "leave success");
+
+/*	
+// for debug
+	{
+		struct list_head *head, *tmp;
+		struct sl811_td *td;
+		int i = 0;
+		head = &urbp->td_list;
+		tmp = head->next;
+	
+		if (list_empty(&urbp->td_list)) {
+			PDEBUG(1, "bug!!! td list is empty!");
+			return -ENODEV;
+		}
+		
+		while (tmp != head) {
+			++i;
+			td = list_entry(tmp, struct sl811_td, td_list);
+			PDEBUG(2, "td = %p, i = %d", td, i);
+			tmp = tmp->next;
+		}
+	}
+*/	
+	return 0;
+}
+
+/*
+ * Reset isochronous transfers
+ */
+static void sl811_reset_isochronous(struct urb *urb)
+{
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td = NULL;
+	struct list_head *head, *tmp;
+	int i;
+
+	PDEBUG(4, "enter, urb = %p", urb);
+	
+	for (i = 0; i < urb->number_of_packets; i++) {
+		urb->iso_frame_desc[i].actual_length = 0;
+		urb->iso_frame_desc[i].status = -EXDEV;
+	}
+
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		td = list_entry(tmp, struct sl811_td, td_list);
+		tmp = tmp->next;
+		sl811_reset_td(td);
+	}
+	
+	urbp->cur_td = urbp->first_td;
+	
+	urb->status = -EINPROGRESS;
+	urb->actual_length = 0;
+	urb->error_count = 0;
+}
+
+/*
+ * Result the iso urb.
+ */
+static void sl811_result_isochronous(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	int status = 0;
+	struct sl811_td *td;
+	int i;
+
+	PDEBUG(4, "enter, urb = %p", urb);
+		
+	urb->actual_length = 0;
+
+	i = 0;
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		td = list_entry(tmp, struct sl811_td, td_list);
+		tmp = tmp->next;
+		
+		if (!td->done) {
+			if (urbp->unlink)
+				urb->status = -ENOENT;
+			else {
+				PDEBUG(1, "we should not get here!");
+				urb->status = -EXDEV;
+			}
+			return ;	
+		}
+		if (td->td_status) {
+			status = td->td_status;
+			urb->error_count++;
+			PDEBUG(1, "error: td = %p, td status = %d", td, td->td_status);
+		}
+
+		urb->iso_frame_desc[i].actual_length = td->len - td->left;
+		urb->actual_length += td->len - td->left;
+		urb->iso_frame_desc[i].status = td->td_status;
+		++i;
+		if (td->left)
+			PDEBUG(3, "short packet, td = %p, len = %d, left = %d", td, td->len, td->left);
+	}
+
+	urb->status = status;
+/*
+// for debug
+	PDEBUG(2, "iso urb complete, len = %d, status =%d ", urb->actual_length, urb->status);		
+*/
+	PDEBUG(4, "leave success");
+}
+
+/*
+ * Interrupt transfers
+ */
+static int sl811_submit_interrupt(struct urb *urb)
+{
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	__u8 *data = urb->transfer_buffer;
+	__u8 dev = usb_pipedevice(urb->pipe);
+	__u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
+	__u8 ctrl = 0;
+	struct sl811_hc *hc = urb->dev->bus->hcpriv;
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td = NULL;
+	
+	PDEBUG(4, "enter, urb = %p", urb);
+	
+	if (len > maxsze) {
+		PDEBUG(1, "length is big than max packet size, len = %d, max packet = %d", len, maxsze);
+		return -EINVAL;
+	}
+	if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED))
+		ctrl |= SL811_USB_CTRL_PREAMBLE;
+	
+	ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
+	if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
+		ctrl |= SL811_USB_CTRL_TOGGLE_1;
+	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));	
+	td = sl811_alloc_td(urb);
+	if (!td)
+		return -ENOMEM;
+		
+	sl811_fill_td(td, ctrl, SL811_DATA_START, len, pidep, dev, data);
+	sl811_calc_td_time(td);
+	urbp->cur_td = urbp->first_td = urbp->last_td = td;
+	urbp->interval = 0;
+	
+	PDEBUG(4, "leave success");
+	
+	return 0;
+}
+
+/*
+ * Reset interrupt transfers
+ */
+static void sl811_reset_interrupt(struct urb *urb)
+{
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td = urbp->cur_td;
+	
+	PDEBUG(4, "enter, interval = %d", urb->interval);
+	
+	td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
+	if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
+		td->ctrl |= SL811_USB_CTRL_TOGGLE_1;
+	usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));	
+	
+	sl811_reset_td(td);
+
+	urbp->interval = urb->interval;
+	
+	urb->status = -EINPROGRESS;
+	urb->actual_length = 0;
+}
+
+/*
+ * Result the interrupt urb.
+ */
+static void sl811_result_interrupt(struct urb *urb)
+{
+	struct list_head *tmp;
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td;
+	int toggle;
+	
+	PDEBUG(4, "enter, urb = %p", urb);
+	
+	urb->actual_length = 0;
+
+	tmp = &urbp->td_list;
+	tmp = tmp->next;
+	td = list_entry(tmp, struct sl811_td, td_list);
+
+	// success.
+	if (td->done && td->td_status == 0) {
+		urb->actual_length += td->len - td->left;
+		urb->status = 0;
+		return ;
+	}
+	// tranfer is done but fail, reset the toggle.
+	else if (td->done && td->td_status) {
+		urb->status = td->td_status;
+reset_toggle:
+		toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0;
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle);
+		PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status);
+		return ;
+	}
+	// unlink, and not do transfer yet
+	else if (td->done == 0 && urbp->unlink && td->td_status == 0) {
+		urb->status = -ENOENT;
+		PDEBUG(3, "unlink and not transfer!");
+		return ;
+	}
+	// unlink, and transfer not complete yet.
+	else if (td->done == 0 && urbp->unlink && td->td_status) {
+		urb->status = -ENOENT;
+		PDEBUG(3, "unlink and not complete!");
+		goto reset_toggle;
+	}
+	// must be bug!!!
+	else {// (td->done == 0 && urbp->unlink == 0)
+		PDEBUG(1, "we should not get here!");
+		urb->status = -EPIPE;
+		return ;
+	}
+}
+
+/*
+ * Control transfers
+ */
+static int sl811_submit_control(struct urb *urb)
+{
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	__u8 *data = urb->transfer_buffer;
+	__u8 dev = usb_pipedevice(urb->pipe);
+	__u8 pidep = 0;
+	__u8 ctrl = 0;
+	struct sl811_hc *hc = urb->dev->bus->hcpriv;
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td = NULL;
+	
+	PDEBUG(4, "enter, urb = %p", urb);
+	
+	if (usb_pipeslow(urb->pipe) && !(hc->rh_status.wPortStatus & USB_PORT_STAT_LOW_SPEED))
+		ctrl |= SL811_USB_CTRL_PREAMBLE;
+	
+	/* Build SETUP TD */
+	pidep = PIDEP(USB_PID_SETUP, usb_pipeendpoint(urb->pipe));
+	ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE | SL811_USB_CTRL_DIR_OUT;
+	td = sl811_alloc_td(urb);
+	if (!td)
+		return -ENOMEM;
+		
+	sl811_fill_td(td, ctrl, SL811_DATA_START, 8, pidep, dev, urb->setup_packet);
+	sl811_calc_td_time(td);
+	
+	urbp->cur_td = urbp->first_td = td;
+	
+	/*
+	 * If direction is "send", change the frame from SETUP (0x2D)
+	 * to OUT (0xE1). Else change it from SETUP to IN (0x69).
+	 */
+	pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
+	if (usb_pipeout(urb->pipe))
+		ctrl |= SL811_USB_CTRL_DIR_OUT;
+	else
+		ctrl &= ~SL811_USB_CTRL_DIR_OUT;
+
+	/* Build the DATA TD's */
+	while (len > 0) {
+		int pktsze = len;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		/* Alternate Data0/1 (start with Data1) */
+		ctrl ^= SL811_USB_CTRL_TOGGLE_1;
+	
+		td = sl811_alloc_td(urb);
+		if (!td)
+			return -ENOMEM;
+
+		sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data);	
+		sl811_calc_td_time(td);
+		
+		data += pktsze;
+		len -= pktsze;
+	}
+
+	/* Build the final TD for control status */
+	td = sl811_alloc_td(urb);
+	if (!td)
+		return -ENOMEM;
+
+	/* It's IN if the pipe is an output pipe or we're not expecting data back */
+	if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) {
+		pidep = PIDEP(USB_PID_IN, usb_pipeendpoint(urb->pipe));
+		ctrl &= ~SL811_USB_CTRL_DIR_OUT;	
+	} else {
+		pidep = PIDEP(USB_PID_OUT, usb_pipeendpoint(urb->pipe));
+		ctrl |= SL811_USB_CTRL_DIR_OUT;
+	}
+		
+	/* End in Data1 */
+	ctrl |= SL811_USB_CTRL_TOGGLE_1;
+
+	sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0);
+	sl811_calc_td_time(td);
+	urbp->last_td = td;
+/*	
+// for debug
+	{
+		struct list_head *head, *tmp;
+		struct sl811_td *td;
+		int i = 0;
+		head = &urbp->td_list;
+		tmp = head->next;
+	
+		if (list_empty(&urbp->td_list)) {
+			PDEBUG(1, "bug!!! td list is empty!");
+			return -ENODEV;
+		}
+		
+		while (tmp != head) {
+			++i;
+			td = list_entry(tmp, struct sl811_td, td_list);
+			PDEBUG(3, "td = %p, i = %d", td, i);
+			tmp = tmp->next;
+		}
+	}
+*/	
+	PDEBUG(4, "leave success");
+	
+	return 0;
+}
+
+/*
+ * Result the control urb.
+ */
+static void sl811_result_control(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td;
+
+	PDEBUG(4, "enter, urb = %p", urb);
+	
+	if (list_empty(&urbp->td_list)) {
+		PDEBUG(1, "td list is empty");
+		return ;
+	}
+
+	head = &urbp->td_list;
+
+	tmp = head->next;
+	td = list_entry(tmp, struct sl811_td, td_list);
+
+	/* The first TD is the SETUP phase, check the status, but skip the count */
+	if (!td->done) {
+		PDEBUG(3, "setup phase error, td = %p, done = %d", td, td->done);
+		goto err_done;
+	}
+	if (td->td_status)  {
+		PDEBUG(3, "setup phase error, td = %p, td status = %d", td, td->td_status);
+		goto err_status;
+	}
+
+	urb->actual_length = 0;
+
+	/* The rest of the TD's (but the last) are data */
+	tmp = tmp->next;
+	while (tmp != head && tmp->next != head) {
+		td = list_entry(tmp, struct sl811_td, td_list);
+		tmp = tmp->next;
+		if (!td->done) {
+			PDEBUG(3, "data phase error, td = %p, done = %d", td, td->done);
+			goto err_done;
+		}
+		if (td->td_status)  {
+			PDEBUG(3, "data phase error, td = %p, td status = %d", td, td->td_status);
+			goto err_status;
+		}
+
+		urb->actual_length += td->len - td->left;
+		// short packet.
+		if (td->left) {
+			PDEBUG(3, "data phase short packet, td = %p, count = %d", td, td->len - td->left);
+			break;
+		}
+	}
+
+	/* The last td is status phase */
+	td = urbp->last_td;
+	if (!td->done) {
+		PDEBUG(3, "status phase error, td = %p, done = %d", td, td->done);
+		goto err_done;
+	}
+	if (td->td_status)  {
+		PDEBUG(3, "status phase error, td = %p, td status = %d", td, td->td_status);
+		goto err_status;
+	}
+	
+	PDEBUG(4, "leave success");
+	
+	urb->status = 0;
+	return ;
+
+err_done:
+	if (urbp->unlink)
+		urb->status = -ENOENT;
+	else {
+		PDEBUG(1, "we should not get here! td = %p", td);
+		urb->status = -EPIPE;
+	}
+	return ;	
+
+err_status:
+	urb->status = td->td_status;		
+	return ;
+}
+
+/*
+ * Bulk transfers
+ */
+static int sl811_submit_bulk(struct urb *urb)
+{
+	int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	int len = urb->transfer_buffer_length;
+	__u8 *data = urb->transfer_buffer;
+	__u8 dev = usb_pipedevice(urb->pipe);
+	__u8 pidep = PIDEP(usb_packetid(urb->pipe), usb_pipeendpoint(urb->pipe));
+	__u8 ctrl = 0;
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td = NULL;
+
+	PDEBUG(4, "enter, urb = %p", urb);
+		
+	if (len < 0) {
+		PDEBUG(1, "error, urb = %p, len = %d", urb, len);
+		return -EINVAL;
+	}
+
+	/* Can't have low speed bulk transfers */
+	if (usb_pipeslow(urb->pipe)) {
+		PDEBUG(1, "error, urb = %p, low speed device", urb);
+		return -EINVAL;
+	}
+
+	if (usb_pipeout(urb->pipe))
+		ctrl |= SL811_USB_CTRL_DIR_OUT;
+		
+	ctrl |= SL811_USB_CTRL_ARM | SL811_USB_CTRL_ENABLE;
+			
+	/* Build the DATA TD's */
+	do {	/* Allow zero length packets */
+		int pktsze = len;
+
+		if (pktsze > maxsze)
+			pktsze = maxsze;
+
+		td = sl811_alloc_td(urb);
+		if (!td)
+			return -ENOMEM;
+
+		/* Alternate Data0/1 (start with Data1) */
+		ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
+		if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
+			ctrl |= SL811_USB_CTRL_TOGGLE_1;
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));		
+		
+		sl811_fill_td(td, ctrl, SL811_DATA_START, pktsze, pidep, dev, data);
+		sl811_calc_td_time(td);
+		
+		if (urbp->cur_td == NULL)
+			urbp->cur_td = urbp->first_td = td;
+			
+		data += pktsze;
+		len -= maxsze;
+	} while (len > 0);
+
+	/*
+	 * USB_ZERO_PACKET means adding a 0-length packet, if
+	 * direction is OUT and the transfer_length was an
+	 * exact multiple of maxsze, hence
+	 * (len = transfer_length - N * maxsze) == 0
+	 * however, if transfer_length == 0, the zero packet
+	 * was already prepared above.
+	 */
+	if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) &&
+	   !len && urb->transfer_buffer_length) {
+		
+		td = sl811_alloc_td(urb);
+		if (!td)
+			return -ENOMEM;
+
+		/* Alternate Data0/1 (start with Data1) */
+		ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
+		if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
+			ctrl |= SL811_USB_CTRL_TOGGLE_1;
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+			
+		sl811_fill_td(td, ctrl, SL811_DATA_START, 0, pidep, dev, 0);
+		sl811_calc_td_time(td);
+	}
+	
+	urbp->last_td = td;
+	
+	PDEBUG(4, "leave success");
+	
+	return 0;
+}
+
+/*
+ * Reset bulk transfers
+ */
+static int sl811_reset_bulk(struct urb *urb)
+{
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td;
+	struct list_head *head, *tmp;
+
+	PDEBUG(4, "enter, urb = %p", urb);
+	
+	
+	head = &urbp->td_list;	
+	tmp = head->next;
+	
+	while (tmp != head) {
+		td = list_entry(tmp, struct sl811_td, td_list);
+
+		/* Alternate Data0/1 (start with Data1) */
+		td->ctrl &= ~SL811_USB_CTRL_TOGGLE_1;
+		if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)))
+			td->ctrl |= SL811_USB_CTRL_TOGGLE_1;
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+		
+		sl811_reset_td(td);
+	} 
+
+	urb->status = -EINPROGRESS;
+	urb->actual_length = 0;
+	urbp->cur_td = urbp->first_td;
+
+	PDEBUG(4, "leave success");
+	
+	return 0;
+}
+
+/*
+ * Result the bulk urb.
+ */
+static void sl811_result_bulk(struct urb *urb)
+{
+	struct list_head *tmp, *head;
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td = NULL;
+	int toggle;
+
+	PDEBUG(4, "enter, urb = %p", urb);
+	
+	urb->actual_length = 0;
+
+	head = &urbp->td_list;
+	tmp = head->next;
+	while (tmp != head) {
+		td = list_entry(tmp, struct sl811_td, td_list);
+		tmp = tmp->next;
+
+		// success.
+		if (td->done && td->td_status == 0) {
+			urb->actual_length += td->len - td->left;
+			
+			// short packet
+			if (td->left) {
+				urb->status = 0;
+				PDEBUG(3, "short packet, td = %p, count = %d", td, td->len - td->left);
+				goto reset_toggle;
+			}
+		}
+		// tranfer is done but fail, reset the toggle.
+		else if (td->done && td->td_status) {
+			urb->status = td->td_status;
+			PDEBUG(3, "error: td = %p, td status = %d", td, td->td_status);
+			goto reset_toggle;
+		}
+		// unlink, and not do transfer yet
+		else if (td->done == 0 && urbp->unlink && td->td_status == 0) {
+			urb->status = -ENOENT;
+			PDEBUG(3, "unlink and not transfer!");
+			return ;
+		}
+		// unlink, and transfer not complete yet.
+		else if (td->done == 0 && urbp->unlink && td->td_status) {
+			PDEBUG(3, "unlink and not complete!");
+			urb->status = -ENOENT;
+			goto reset_toggle;
+		}
+		// must be bug!!!
+		else {// (td->done == 0 && urbp->unlink == 0)
+			urb->status = -EPIPE;
+			PDEBUG(1, "we should not get here!");
+			return ;
+		}
+	}
+	
+	PDEBUG(4, "leave success");		
+	urb->status = 0;
+	return ;
+
+reset_toggle:
+	toggle = (td->ctrl & SL811_USB_CTRL_TOGGLE_1) ? 1 : 0;
+	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle);
+}
+
+/*
+ * Find the first urb have the same dev and endpoint.
+ */
+static inline int sl811_find_same_urb(struct list_head *head, struct urb *urb)
+{
+	struct list_head *tmp;
+	struct urb *u;
+	
+	if (!head || !urb)
+		return 0;
+		
+	tmp = head->next;
+	
+	while (tmp != head) {
+		u = list_entry(tmp, struct urb, urb_list);
+		if (u == urb)
+			return 1;
+		tmp = tmp->next;	
+	}
+	
+	return 0;
+}
+
+/*
+ * Find the first urb have the same dev and endpoint.
+ */
+static inline struct urb* sl811_find_same_devep(struct list_head *head, struct urb *urb)
+{
+	struct list_head *tmp;
+	struct urb *u;
+	
+	if (!head || !urb)
+		return NULL;
+		
+	tmp = head->next;
+	
+	while (tmp != head) {
+		u = list_entry(tmp, struct urb, urb_list);
+		if ((usb_pipe_endpdev(u->pipe)) == (usb_pipe_endpdev(urb->pipe)))
+			return u;
+		tmp = tmp->next;	
+	}
+	
+	return NULL;
+}
+
+/*
+ * This function is called by the USB core API when an URB is available to
+ * process. 
+ */
+static int sl811_submit_urb(struct urb *urb)
+{
+	struct sl811_hc *hc = urb->dev->bus->hcpriv;
+	unsigned int pipe = urb->pipe;
+	struct list_head *head = NULL;
+	unsigned long flags;
+	int bustime;
+	int ret = 0;
+	
+	if (!urb) {
+		PDEBUG(1, "urb is null");
+		return -EINVAL;
+	}
+	
+	if (urb->hcpriv) {
+		PDEBUG(1, "urbp is not null, urb = %p, urbp = %p", urb, urb->hcpriv);
+		return -EINVAL;
+	}
+	
+	if (!urb->dev || !urb->dev->bus || !hc)  {
+		PDEBUG(1, "dev or bus or hc is null");
+		return -ENODEV;
+	}
+	
+	if (usb_endpoint_halted(urb->dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
+		PDEBUG(2, "sl811_submit_urb: endpoint_halted");
+		return -EPIPE;
+	}
+	
+	if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) > SL811_DATA_LIMIT) {
+		printk(KERN_ERR "Packet size is big for SL811, should < %d!\n", SL811_DATA_LIMIT);
+		return -EINVAL;
+	}
+	
+	/* a request to the virtual root hub */ 
+	if (usb_pipedevice(pipe) == hc->rh.devnum)
+		return sl811_rh_submit_urb(urb);
+	
+	spin_lock_irqsave(&hc->hc_lock, flags);
+	spin_lock(&urb->lock);
+	
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		head = &hc->iso_list;
+		break;
+	case PIPE_INTERRUPT:
+		head = &hc->intr_list;
+		break;
+	case PIPE_CONTROL:
+		head = &hc->ctrl_list;
+		break;
+	case PIPE_BULK:
+		head = &hc->bulk_list;
+		break;
+	}
+		
+	if (sl811_find_same_devep(head, urb)) {
+		list_add(&urb->urb_list, &hc->wait_list);
+		PDEBUG(4, "add to wait list");
+		goto out_unlock;
+	}
+	
+	if (!sl811_alloc_urb_priv(urb)) {
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+	
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		if (urb->number_of_packets <= 0) {
+			ret = -EINVAL;
+			break;
+		}
+		bustime = usb_check_bandwidth(urb->dev, urb);
+		if (bustime < 0) {
+			ret = bustime;
+			break;
+		}
+		if (!(ret = sl811_submit_isochronous(urb)))
+			usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+		break;
+	case PIPE_INTERRUPT:
+		bustime = usb_check_bandwidth(urb->dev, urb);
+		if (bustime < 0)
+			ret = bustime;
+		else if (!(ret = sl811_submit_interrupt(urb)))
+			usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+		break;
+	case PIPE_CONTROL:
+		ret = sl811_submit_control(urb);
+		break;
+	case PIPE_BULK:
+		ret = sl811_submit_bulk(urb);
+		break;
+	}
+	
+	if (!ret) {
+		((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies;
+		list_add(&urb->urb_list, head);
+		PDEBUG(4, "add to type list");
+		urb->status = -EINPROGRESS;
+		if (++hc->active_urbs == 1)
+			sl811_enable_interrupt(hc);
+		goto out_unlock;	
+	} else {
+		PDEBUG(2, "submit urb fail! error = %d", ret);
+		sl811_free_urb_priv(urb);
+	}
+	
+out_unlock:	
+	spin_unlock(&urb->lock);
+	spin_unlock_irqrestore(&hc->hc_lock, flags);
+
+	return ret;
+}
+
+/*
+ * Submit the urb the wait list.
+ */
+static int sl811_submit_urb_with_lock(struct urb *urb)
+{
+	struct sl811_hc *hc = urb->dev->bus->hcpriv;
+	struct list_head *head = NULL;
+	int bustime;
+	int ret = 0;
+	
+	spin_lock(&urb->lock);
+	
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		head = &hc->iso_list;
+		break;
+	case PIPE_INTERRUPT:
+		head = &hc->intr_list;
+		break;
+	case PIPE_CONTROL:
+		head = &hc->ctrl_list;
+		break;
+	case PIPE_BULK:
+		head = &hc->bulk_list;
+		break;
+	}
+		
+	if (!sl811_alloc_urb_priv(urb)) {
+		ret = -ENOMEM;
+		goto out_unlock;
+	}
+	
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		if (urb->number_of_packets <= 0) {
+			ret = -EINVAL;
+			break;
+		}
+		bustime = usb_check_bandwidth(urb->dev, urb);
+		if (bustime < 0) {
+			ret = bustime;
+			break;
+		}
+		if (!(ret = sl811_submit_isochronous(urb)))
+			usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+		break;
+	case PIPE_INTERRUPT:
+		bustime = usb_check_bandwidth(urb->dev, urb);
+		if (bustime < 0)
+			ret = bustime;
+		else if (!(ret = sl811_submit_interrupt(urb)))
+			usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+		break;
+	case PIPE_CONTROL:
+		ret = sl811_submit_control(urb);
+		break;
+	case PIPE_BULK:
+		ret = sl811_submit_bulk(urb);
+		break;
+	}
+	
+	if (ret == 0) {
+		((struct sl811_urb_priv *)urb->hcpriv)->inserttime = jiffies;
+		list_add(&urb->urb_list, head);
+		PDEBUG(4, "add to type list");
+		urb->status = -EINPROGRESS;
+		if (++hc->active_urbs == 1)
+			sl811_enable_interrupt(hc);
+		goto out_unlock;	
+	} else {
+		PDEBUG(2, "submit urb fail! error = %d", ret);
+		sl811_free_urb_priv(urb);
+	}
+	
+out_unlock:	
+	spin_unlock(&urb->lock);
+
+	return ret;
+}
+
+/*
+ * Reset the urb
+ */
+static void sl811_reset_urb(struct urb *urb)
+{
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		sl811_reset_isochronous(urb);
+		break;
+	case PIPE_INTERRUPT:
+		sl811_reset_interrupt(urb);
+		break;
+	case PIPE_CONTROL:
+		return;
+	case PIPE_BULK:
+		sl811_reset_bulk(urb);
+		break;
+	}
+	urbp->inserttime = jiffies;
+}
+
+/*
+ * Return the result of a transfer
+ */
+static void sl811_result_urb(struct urb *urb)
+{
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_hc *hc = urb->dev->bus->hcpriv;
+	struct list_head *head = NULL;
+	struct urb *u = NULL;
+	int reset = 0;
+	int ring = 0;
+
+	if (urb->status != -EINPROGRESS) {
+		PDEBUG(1, "urb status is not EINPROGRESS!");
+		return ;
+	}
+	
+	spin_lock(&urb->lock);
+	
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_ISOCHRONOUS:
+		head = &hc->iso_list;
+		sl811_result_isochronous(urb);
+		
+		// if the urb is not unlink and is in a urb "ring", we reset it
+		if (!urbp->unlink && urb->next) 
+			ring = 1;
+		break;
+	case PIPE_INTERRUPT:
+		head = &hc->intr_list;
+		sl811_result_interrupt(urb);
+		
+		// if the urb is not unlink and not "once" query, we reset.
+		if (!urbp->unlink && urb->interval)
+			reset = 1;
+		break;
+	case PIPE_CONTROL:
+		head = &hc->ctrl_list;
+		sl811_result_control(urb);
+		break;
+	case PIPE_BULK:
+		head = &hc->bulk_list;
+		sl811_result_bulk(urb);
+		
+		// if the urb is not unlink and is in a urb "ring", we reset it
+		if (!urbp->unlink && urb->next)
+			ring = 1;
+		break;
+	}
+	
+	PDEBUG(4, "result urb status = %d", urb->status);
+	
+	if (ring && urb->next == urb)
+		reset = 1;
+	
+	if (!reset) {
+		switch (usb_pipetype(urb->pipe)) {
+		case PIPE_ISOCHRONOUS:
+			usb_release_bandwidth(urb->dev, urb, 1);
+			break;
+		case PIPE_INTERRUPT:
+			usb_release_bandwidth(urb->dev, urb, 0);
+			break;
+		}
+		sl811_free_urb_priv(urb);
+	}
+	
+	spin_unlock(&urb->lock);
+	
+	if (urb->complete)
+		urb->complete(urb);
+	
+	if (reset) {
+		spin_lock(&urb->lock);
+		sl811_reset_urb(urb);
+		if (usb_pipeint(urb->pipe))
+			list_add(&urb->urb_list, &hc->idle_intr_list);
+		else
+			list_add(&urb->urb_list, head);
+		spin_unlock(&urb->lock);
+	} else {
+		if (--hc->active_urbs <= 0) {
+			hc->active_urbs = 0;
+			sl811_disable_interrupt(hc);
+		}
+		
+		if (ring) 
+			u = urb->next;
+		else
+			u = sl811_find_same_devep(&hc->wait_list, urb);
+			
+		if (u) {
+			if (!list_empty(&u->urb_list))
+				list_del(&u->urb_list);
+			if (sl811_submit_urb_with_lock(u))
+				list_add(&u->urb_list, &hc->wait_list);
+		}
+	}
+}
+
+
+#ifdef SL811_TIMEOUT
+
+/*
+ * Unlink the urb from the urb list
+ */
+static int sl811_unlink_urb(struct urb *urb)
+{
+	unsigned long flags;
+	struct sl811_hc *hc;
+	struct sl811_urb_priv *urbp;
+	int call = 0;
+	int schedule = 0;
+	int count = 0;
+	
+	if (!urb) {
+		PDEBUG(1, "urb is null");
+		return -EINVAL;
+	}
+	
+	if (!urb->dev || !urb->dev->bus) {
+		PDEBUG(1, "dev or bus is null");
+		return -ENODEV;
+	}
+	
+	hc = urb->dev->bus->hcpriv; 
+	urbp = urb->hcpriv;
+	
+	/* a request to the virtual root hub */
+	if (usb_pipedevice(urb->pipe) == hc->rh.devnum)
+		return sl811_rh_unlink_urb(urb); 
+	
+	spin_lock_irqsave(&hc->hc_lock, flags);
+	spin_lock(&urb->lock);
+
+	// in wait list
+	if (sl811_find_same_urb(&hc->wait_list, urb)) {	
+		PDEBUG(4, "unlink urb in wait list");
+		list_del_init(&urb->urb_list);
+		urb->status = -ENOENT;
+		call = 1;
+		goto out;
+	}
+	
+	// in intr idle list.
+	if (sl811_find_same_urb(&hc->idle_intr_list, urb)) {	
+		PDEBUG(4, "unlink urb in idle intr list");
+		list_del_init(&urb->urb_list);
+		urb->status = -ENOENT;
+		sl811_free_urb_priv(urb);
+		usb_release_bandwidth(urb->dev, urb, 0);
+		if (--hc->active_urbs <= 0) {
+			hc->active_urbs = 0;
+			sl811_disable_interrupt(hc);
+		}
+		call = 1;
+		goto out;
+	}
+
+	if (urb->status == -EINPROGRESS) {  
+		PDEBUG(3, "urb is still in progress");
+		urbp->unlink = 1;
+
+re_unlink:
+		// Is it in progress?
+		urbp = urb->hcpriv;
+		if (urbp && hc->cur_td == urbp->cur_td) {
+			++count;
+			if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) {
+				PDEBUG(3, "unlink: cur td is still in progress! count = %d", count);
+re_schedule:				
+				schedule = 1;
+				spin_unlock(&urb->lock);
+				spin_unlock_irqrestore(&hc->hc_lock, flags);
+				schedule_timeout(HZ/50);
+				spin_lock_irqsave(&hc->hc_lock, flags);
+				spin_lock(&urb->lock);
+			} else {
+				PDEBUG(3, "unlink: lost of interrupt? do parse! count = %d", count);
+				spin_unlock(&urb->lock);
+				sl811_transfer_done(hc, 0);
+				spin_lock(&urb->lock);
+			}
+			goto re_unlink;
+		}
+		
+		if (list_empty(&urb->urb_list)) {
+			PDEBUG(3, "unlink: list empty!");
+			goto out;
+		}
+			
+		if (urb->transfer_flags & USB_TIMEOUT_KILLED) { 
+			PDEBUG(3, "unlink: time out killed");
+			// it is timeout killed by us 
+			goto result;
+		} else if (urb->transfer_flags & USB_ASYNC_UNLINK) { 
+			// we do nothing, just let it be processing later
+			PDEBUG(3, "unlink async, do nothing");
+			goto out;
+		} else {
+			// synchron without callback
+			PDEBUG(3, "unlink synchron, we wait the urb complete or timeout");
+			if (schedule == 0) {
+				PDEBUG(3, "goto re_schedule");
+				goto re_schedule;
+			} else {
+				PDEBUG(3, "already scheduled");
+				goto result;
+			}
+		}
+	} else if (!list_empty(&urb->urb_list)) {
+		PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status);
+		//list_del_init(&urb->urb_list);
+		//call = 1;
+	}
+
+out:
+	spin_unlock(&urb->lock);
+	spin_unlock_irqrestore(&hc->hc_lock, flags);
+	
+	if (call && urb->complete)
+		urb->complete(urb);
+	
+	return 0;
+	
+result:
+	spin_unlock(&urb->lock);
+	
+	list_del_init(&urb->urb_list);
+	sl811_result_urb(urb);	
+	
+	spin_unlock_irqrestore(&hc->hc_lock, flags);
+	
+	return 0;
+}
+
+#else
+
+/*
+ * Unlink the urb from the urb list
+ */
+static int sl811_unlink_urb(struct urb *urb)
+{
+	unsigned long flags;
+	struct sl811_hc *hc;
+	struct sl811_urb_priv *urbp;
+	int call = 0;
+	
+	if (!urb) {
+		PDEBUG(1, "urb is null");
+		return -EINVAL;
+	}
+	
+	if (!urb->dev || !urb->dev->bus) {
+		PDEBUG(1, "dev or bus is null");
+		return -ENODEV;
+	}
+	
+	hc = urb->dev->bus->hcpriv; 
+	urbp = urb->hcpriv;
+	
+	/* a request to the virtual root hub */
+	if (usb_pipedevice(urb->pipe) == hc->rh.devnum)
+		return sl811_rh_unlink_urb(urb); 
+	
+	spin_lock_irqsave(&hc->hc_lock, flags);
+	spin_lock(&urb->lock);
+
+	// in wait list
+	if (sl811_find_same_urb(&hc->wait_list, urb)) {	
+		PDEBUG(2, "unlink urb in wait list");
+		list_del_init(&urb->urb_list);
+		urb->status = -ENOENT;
+		call = 1;
+		goto out;
+	}
+	
+	if (urb->status == -EINPROGRESS) {  
+		PDEBUG(2, "urb is still in progress");
+		urbp->unlink = 1;
+
+		// Is it in progress?
+		urbp = urb->hcpriv;
+		if (urbp && hc->cur_td == urbp->cur_td) {
+			// simple, let it out
+			PDEBUG(2, "unlink: cur td is still in progress!");
+			hc->cur_td = NULL;
+		}
+		
+		goto result;
+	} else if (!list_empty(&urb->urb_list)) {
+		PDEBUG(1, "urb = %p, status = %d is in a list, why?", urb, urb->status);
+		list_del_init(&urb->urb_list);
+		if (urbp)
+			goto result;
+		else
+			call = 1;
+	}
+
+out:
+	spin_unlock(&urb->lock);
+	spin_unlock_irqrestore(&hc->hc_lock, flags);
+	
+	if (call && urb->complete)
+		urb->complete(urb);
+			
+	return 0;
+	
+result:
+	spin_unlock(&urb->lock);
+	
+	list_del_init(&urb->urb_list);
+	sl811_result_urb(urb);	
+	
+	spin_unlock_irqrestore(&hc->hc_lock, flags);
+	
+	return 0;
+}
+
+#endif
+
+static int sl811_get_current_frame_number(struct usb_device *usb_dev)
+{
+	return ((struct sl811_hc *)(usb_dev->bus->hcpriv))->frame_number;
+}
+
+static struct usb_operations sl811_device_operations = 
+{
+	sl811_alloc_dev_priv,
+	sl811_free_dev_priv,
+	sl811_get_current_frame_number,
+	sl811_submit_urb,
+	sl811_unlink_urb
+};
+
+/*
+ * This	functions transmit a td.
+ */
+static inline void sl811_trans_cur_td(struct sl811_hc *hc, struct sl811_td *td)
+{
+	sl811_print_td(4, td);
+	sl811_write_buf(hc, SL811_ADDR_A,  &td->addr, 4);
+	if (td->len && (td->ctrl & SL811_USB_CTRL_DIR_OUT))
+		sl811_write_buf(hc, td->addr,  td->buf, td->len);
+
+	sl811_write(hc,	SL811_CTRL_A, td->ctrl);
+}
+
+		
+/*
+ * This	function checks	the status of the transmitted or received packet
+ * and copy the	data from the SL811HS register into a buffer.
+ */
+static void sl811_parse_cur_td(struct sl811_hc *hc, struct sl811_td *td)
+{
+	struct urb *urb = td->urb;
+#ifdef SL811_DEBUG
+	int dev = usb_pipedevice(td->urb->pipe);
+	int ep = usb_pipeendpoint(td->urb->pipe);
+#endif
+
+	sl811_read_buf(hc, SL811_STS_A, &td->status, 2);
+	
+	if (td->status & SL811_USB_STS_ACK) {
+		td->done = 1;
+		
+/*		if ((td->ctrl & SL811_USB_CTRL_TOGGLE_1) != (td->status & SL811_USB_STS_TOGGLE_1)) {
+			PDEBUG(2, "dev %d endpoint %d unexpect data toggle!", dev, ep);
+			td->td_status = -EILSEQ;
+		}
+*/		
+		if (!(td->ctrl & SL811_USB_CTRL_DIR_OUT) && td->len > 0)
+			sl811_read_buf(hc, td->addr, td->buf, td->len - td->left);
+			
+		if (td->left && (urb->transfer_flags & USB_DISABLE_SPD)) {
+			PDEBUG(2, "dev %d endpoint %d unexpect short packet! td = %p", dev, ep, td);
+			td->td_status = -EREMOTEIO;
+		} else
+			td->td_status = 0;
+	} else if (td->status & SL811_USB_STS_STALL) {
+		PDEBUG(2, "dev %d endpoint %d halt, td = %p", dev, ep, td);
+		td->td_status = -EPIPE;
+		if (urb->dev)
+			usb_endpoint_halt(td->urb->dev, usb_pipeendpoint(td->urb->pipe), usb_pipeout(td->urb->pipe));
+		td->done = 1;
+	} else if (td->status & SL811_USB_STS_OVERFLOW) {
+		PDEBUG(1, "dev %d endpoint %d overflow, sl811 only support packet less than %d", dev, ep, SL811_DATA_LIMIT);	
+		td->td_status = -EOVERFLOW;
+		td->done = 1;
+	} else if (td->status & SL811_USB_STS_TIMEOUT ) {
+		PDEBUG(2, "dev %d endpoint %d timeout, td = %p", dev, ep, td);	
+		td->td_status = -ETIMEDOUT;
+		if (--td->errcnt == 0)
+			td->done = 1;
+	} else if (td->status & SL811_USB_STS_ERROR) {
+		PDEBUG(2, "dev %d endpoint %d error, td = %p", dev, ep, td);
+		td->td_status = -EILSEQ;
+		if (--td->errcnt == 0)
+			td->done = 1;
+	} else if (td->status & SL811_USB_STS_NAK) {
+		++td->nakcnt;
+		PDEBUG(3, "dev %d endpoint %d nak, td = %p, count = %d", dev, ep, td, td->nakcnt);
+		td->td_status = -EINPROGRESS;
+		if (!usb_pipeslow(td->urb->pipe) && td->nakcnt > 1024) {
+			PDEBUG(2, "too many naks, td = %p, count = %d", td, td->nakcnt);
+			td->td_status = -ETIMEDOUT;
+			td->done = 1;
+		} 
+	} 
+	
+	sl811_print_td(4, td);
+}
+
+/*
+ * This	function checks	the status of current urb.
+ */
+static int sl811_parse_cur_urb(struct urb *urb)
+{
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	struct sl811_td *td = urbp->cur_td;
+	struct list_head *tmp;
+	
+	sl811_print_td(5, td);
+	
+	// this td not done yet.
+	if (!td->done)
+		return 0;
+	
+	// the last ld, so the urb is done.
+	if (td == urbp->last_td) {
+		PDEBUG(4, "urb = %p is done success", td->urb);
+		if (usb_pipeisoc(td->urb->pipe))
+			PDEBUG(4, "ISO URB DONE, td = %p", td);
+		return 1;
+	}
+	
+	// iso transfer, we always advance to next td 
+	if (usb_pipeisoc(td->urb->pipe)) {
+		tmp = &td->td_list;
+		tmp = tmp->next;
+		urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);
+		PDEBUG(4, "ISO NEXT, td = %p", urbp->cur_td);	
+		return 0;
+	}
+		
+	// some error occur, so the urb is done.
+	if (td->td_status) {
+		PDEBUG(3, "urb = %p is done error, td status is = %d", td->urb, td->td_status);
+		return 1;
+	}
+		
+	// short packet.
+	if (td->left) {
+		if (usb_pipecontrol(td->urb->pipe)) {
+			// control packet, we advance to the last td
+			PDEBUG(3, "ctrl short packet, advance to last td");
+			urbp->cur_td = urbp->last_td;
+			return 0;
+		} else {
+			// interrut and bulk packet, urb is over.
+			PDEBUG(3, "bulk or intr short packet, urb is over");
+			return 1;
+		}
+	}
+
+	// we advance to next td.	
+	tmp = &td->td_list;
+	tmp = tmp->next;
+	urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);
+#ifdef SL811_DEBUG
+	PDEBUG(4, "advance to the next td, urb = %p, td = %p", urb, urbp->cur_td);
+	sl811_print_td(5, urbp->cur_td);
+	if (td == urbp->cur_td)
+		PDEBUG(1, "bug!!!");
+#endif		
+	return 0;
+}
+
+/*
+ * Find the next td to transfer.
+ */
+static inline struct sl811_td* sl811_schedule_next_td(struct urb *urb, struct sl811_td *cur_td)
+{
+	struct sl811_urb_priv *urbp = urb->hcpriv;
+	
+	PDEBUG(4, "urb at %p, cur td at %p", urb, cur_td);
+	
+	// iso don't schedule the td in the same frame.
+	if (usb_pipeisoc(cur_td->urb->pipe))
+		return NULL;
+	
+	// cur td is not complete
+	if (!cur_td->done)
+		return NULL;	
+	
+	// here, urbp->cur_td is already the next td;
+	return urbp->cur_td;
+}
+
+/*
+ * Scan the list to find a active urb
+ */
+static inline struct urb* sl811_get_list_next_urb(struct sl811_hc *hc, struct list_head *next)
+{
+	struct urb *urb;
+	int i;
+	
+	if (list_empty(next))
+		return NULL;
+	
+	if (next == hc->cur_list)
+		return NULL;
+			
+	for (i = 0; i < 4; ++i) 
+		if (next == &hc->urb_list[i])
+			return NULL;
+			
+	urb = list_entry(next, struct urb, urb_list);
+	PDEBUG(4, "next urb in list is at %p", urb);
+		
+	return urb;
+}
+
+/*
+ * Find the next td to transfer.
+ */
+static struct sl811_td* sl811_schedule_next_urb(struct sl811_hc *hc, struct list_head *next)
+{
+	struct urb *urb = NULL;
+	int back_loop = 1;
+	struct list_head *old_list = hc->cur_list;
+		
+	// try to get next urb in the same list.
+	if (next) {
+		urb = sl811_get_list_next_urb(hc, next);
+		if (!urb)
+			++hc->cur_list;
+	}
+
+	// try other list.
+	if (!urb) {			
+re_loop:
+		// try all the list.
+		while (hc->cur_list < &hc->urb_list[4]) { 
+			if ((urb = sl811_get_list_next_urb(hc, hc->cur_list->next)))
+				return ((struct sl811_urb_priv *)urb->hcpriv)->cur_td;
+			++hc->cur_list;
+		}
+		// the last list is try 
+		if (back_loop && (old_list >= &hc->ctrl_list)) {
+			hc->cur_list = &hc->ctrl_list;
+			back_loop = 0;
+			goto re_loop;
+		}
+	}
+	
+	if (hc->cur_list > &hc->urb_list[3])
+		hc->cur_list = &hc->ctrl_list;
+			
+	return NULL;
+}
+
+/*
+ * This function process the transfer rusult.
+ */
+static void sl811_transfer_done(struct sl811_hc *hc, int sof) 
+{
+	struct sl811_td *cur_td = hc->cur_td, *next_td = NULL;
+	struct urb *cur_urb = NULL;
+       	struct list_head *next = NULL;
+       	int done;
+	
+	PDEBUG(5, "enter");
+	
+	if (cur_td == NULL) {
+		PDEBUG(1, "in done interrupt, but td is null, be already parsed?");
+		return ;
+	}
+
+	cur_urb = cur_td->urb;
+	hc->cur_td = NULL;
+	next = &cur_urb->urb_list;
+	next = next->next;
+	
+	spin_lock(&cur_urb->lock);	
+	sl811_parse_cur_td(hc, cur_td);
+	done = sl811_parse_cur_urb(cur_urb);
+	spin_unlock(&cur_urb->lock);
+	
+	if (done) {
+		list_del_init(&cur_urb->urb_list);
+		cur_td = NULL;
+		sl811_result_urb(cur_urb);	
+	}
+
+	if (sof)
+		return ;
+	
+	if (!done) {
+		next_td = sl811_schedule_next_td(cur_urb, cur_td);
+		if (next_td && next_td != cur_td && (sl811_calc_bus_remainder(hc) > next_td->bustime)) {
+			hc->cur_td = next_td;
+			PDEBUG(5, "ADD TD");
+			sl811_trans_cur_td(hc, next_td);
+			return ;
+		}
+	}
+	
+	while (1) {
+		next_td = sl811_schedule_next_urb(hc, next);
+		if (!next_td)
+			return;
+		if (next_td == cur_td)
+			return;
+		next = &next_td->urb->urb_list;
+		next = next->next;
+		if (sl811_calc_bus_remainder(hc) > next_td->bustime) {
+			hc->cur_td = next_td;
+			PDEBUG(5, "ADD TD");
+			sl811_trans_cur_td(hc, next_td);
+			return ;
+		}
+	}
+}
+
+/*
+ *
+ */
+static void inline sl811_dec_intr_interval(struct sl811_hc *hc)
+{
+	struct list_head *head, *tmp;
+	struct urb *urb;
+	struct sl811_urb_priv *urbp;
+	
+	if (list_empty(&hc->idle_intr_list))
+		return ;
+	
+	head = &hc->idle_intr_list;
+	tmp = head->next;
+	
+	while (tmp != head) {
+		urb = list_entry(tmp, struct urb, urb_list);
+		tmp = tmp->next;
+		spin_lock(&urb->lock);
+		urbp = urb->hcpriv;
+		if (--urbp->interval == 0) {
+			list_del(&urb->urb_list);
+			list_add(&urb->urb_list, &hc->intr_list);
+			PDEBUG(4, "intr urb active");
+		}
+		spin_unlock(&urb->lock);
+	}
+}
+
+/*
+ * The sof interrupt is happen.	
+ */
+static void sl811_start_sof(struct sl811_hc *hc)
+{
+	struct sl811_td *next_td;
+#ifdef SL811_DEBUG
+	static struct sl811_td *repeat_td = NULL;
+	static repeat_cnt = 1;
+#endif	
+	if (++hc->frame_number > 1024)
+		hc->frame_number = 0;
+	
+	if (hc->active_urbs == 0)
+		return ;
+	
+	sl811_dec_intr_interval(hc);
+	
+	if (hc->cur_td) {
+		if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) {
+#ifdef SL811_DEBUG
+			if (repeat_td == hc->cur_td) 
+				++repeat_cnt;
+			else {
+				if (repeat_cnt >= 2)
+					PDEBUG(2, "cur td = %p repeat %d", hc->cur_td, repeat_cnt);
+				repeat_cnt = 1;
+				repeat_td = hc->cur_td;
+			}
+#endif
+			return ;
+		} else {
+			PDEBUG(2, "lost of interrupt in sof? do parse!");
+			sl811_transfer_done(hc, 1);
+			
+			// let this frame idle	
+			return;
+		}
+	}
+	
+	hc->cur_list = &hc->iso_list;
+	
+	if (hc->active_urbs == 0)
+		return ;
+	
+	next_td = sl811_schedule_next_urb(hc, NULL);
+	if (!next_td) {
+#ifdef SL811_DEBUG
+		if (list_empty(&hc->idle_intr_list))
+			PDEBUG(2, "not schedule a td, why? urbs = %d", hc->active_urbs);
+#endif
+		return; 
+	}
+	if (sl811_calc_bus_remainder(hc) > next_td->bustime) {
+		hc->cur_td = next_td;
+		sl811_trans_cur_td(hc, next_td);
+	} else
+		PDEBUG(2, "bus time if not enough, why?");
+}
+
+/*
+ * This	function resets	SL811HS	controller and detects the speed of
+ * the connecting device
+ *
+ * Return: 0 = no device attached; 1 = USB device attached
+ */
+static int sl811_hc_reset(struct sl811_hc *hc)
+{
+	int status ;
+
+	sl811_write(hc,	SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
+	sl811_write(hc,	SL811_CTRL1, SL811_CTRL1_RESET);
+
+	mdelay(20);
+	
+	// Disable hardware SOF generation.
+	sl811_write(hc,	SL811_CTRL1, 0);
+	mdelay(2);
+	sl811_write(hc, SL811_INTRSTS, 0xff); 
+	status = sl811_read(hc, SL811_INTRSTS);
+
+	if (status & SL811_INTR_NOTPRESENT) {
+		// Device is not present
+		PDEBUG(0, "Device not present");
+		hc->rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE);
+		hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
+		sl811_write(hc,	SL811_INTR, SL811_INTR_INSRMV);
+		return 0;
+	}
+
+	// Send SOF to address 0, endpoint 0.
+	sl811_write(hc, SL811_LEN_B, 0);
+	sl811_write(hc, SL811_PIDEP_B, PIDEP(USB_PID_SOF, 0));
+	sl811_write(hc, SL811_DEV_B, 0x00);
+	sl811_write (hc, SL811_SOFLOW, SL811_12M_HI);
+
+	if (status & SL811_INTR_SPEED_FULL) {
+		/* full	speed device connect directly to root hub */
+		PDEBUG (0, "Full speed Device attached");
+		
+		sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
+		mdelay(20);
+		sl811_write(hc,	SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
+		sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SOF);
+
+		/* start the SOF or EOP	*/
+		sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM);
+		hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION;
+		hc->rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED;
+		mdelay(2);
+		sl811_write (hc, SL811_INTRSTS, 0xff);
+	} else {
+		/* slow	speed device connect directly to root-hub */
+		PDEBUG(0, "Low speed Device attached");
+		
+		sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
+		mdelay(20);
+		sl811_write(hc,	SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI);
+		sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF);
+
+		/* start the SOF or EOP	*/
+		sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM);
+		hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED;
+		mdelay(2);
+		sl811_write(hc, SL811_INTRSTS, 0xff);
+	}
+
+	hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
+	sl811_write(hc,	SL811_INTR, SL811_INTR_INSRMV);
+	
+	return 1;
+}
+
+/*
+ * Interrupt service routine.
+ */
+static void sl811_interrupt(int irq, void *__hc, struct pt_regs * r)
+{
+	__u8 status;
+	struct sl811_hc *hc = __hc;
+
+	status = sl811_read(hc, SL811_INTRSTS);
+	if (status == 0)
+		return ;
+
+	sl811_write(hc,	SL811_INTRSTS, 0xff);
+
+	if (status & SL811_INTR_INSRMV) {
+		sl811_write(hc,	SL811_INTR, 0);
+		sl811_write(hc,	SL811_CTRL1, 0);
+		// wait	for device stable
+		mdelay(100);			
+		sl811_hc_reset(hc);
+		return ;
+	}
+
+	spin_lock(&hc->hc_lock);
+	
+	if (status & SL811_INTR_DONE_A) {
+		if (status & SL811_INTR_SOF) {
+			sl811_transfer_done(hc, 1);
+			PDEBUG(4, "sof in done!");
+			sl811_start_sof(hc);
+		} else
+			sl811_transfer_done(hc, 0);
+	} else if (status & SL811_INTR_SOF)
+		sl811_start_sof(hc);	
+
+	spin_unlock(&hc->hc_lock);	
+
+	return ;
+}
+
+/*
+ * This	function allocates all data structure and store	in the
+ * private data	structure.
+ *
+ * Return value	 : data structure for the host controller
+ */
+static struct sl811_hc* __devinit sl811_alloc_hc(void)
+{
+	struct sl811_hc *hc;
+	struct usb_bus *bus;
+	int i;
+
+	PDEBUG(5, "enter");
+
+	hc = (struct sl811_hc *)kmalloc(sizeof(struct sl811_hc), GFP_KERNEL);
+	if (!hc)
+		return NULL;
+
+	memset(hc, 0, sizeof(struct sl811_hc));
+
+	hc->rh_status.wPortStatus = USB_PORT_STAT_POWER;
+	hc->rh_status.wPortChange = 0;
+
+	hc->active_urbs	= 0;
+	INIT_LIST_HEAD(&hc->hc_hcd_list);
+	list_add(&hc->hc_hcd_list, &sl811_hcd_list);
+	
+	init_waitqueue_head(&hc->waitq);
+	
+	for (i = 0; i < 6; ++i)
+		INIT_LIST_HEAD(&hc->urb_list[i]);
+	
+	hc->cur_list = &hc->iso_list;
+
+	bus = usb_alloc_bus(&sl811_device_operations);
+	if (!bus) {
+		kfree (hc);
+		return NULL;
+	}
+
+	hc->bus	= bus;
+	bus->hcpriv = hc;
+
+	return hc;
+}
+
+/*
+ * This	function De-allocate all resources
+ */
+static void sl811_release_hc(struct sl811_hc *hc)
+{
+	PDEBUG(5, "enter");
+
+	/* disconnect all devices */
+	if (hc->bus->root_hub)
+		usb_disconnect(&hc->bus->root_hub);
+
+	if (hc->addr_io)
+		release_region(hc->addr_io, 1);
+
+	if (hc->data_io)
+		release_region(hc->data_io, 1);
+
+	if (hc->irq)
+		free_irq(hc->irq, hc);
+
+	usb_deregister_bus(hc->bus);
+	usb_free_bus(hc->bus);
+
+	list_del(&hc->hc_hcd_list);
+	INIT_LIST_HEAD(&hc->hc_hcd_list);
+
+	kfree (hc);
+}
+
+/*
+ * This	function is board specific.  It	sets up	the interrupt to
+ * be an edge trigger and trigger on the rising	edge
+ */
+static void sl811_init_irq(void)
+{
+	GPDR &=	~(1<<23);
+	set_GPIO_IRQ_edge(1<<23, GPIO_RISING_EDGE);
+}
+
+/*
+ * This	function request IO memory regions, request IRQ, and
+ * allocate all	other resources.
+ *
+ * Input: addr_io = first IO address
+ *	  data_io = second IO address
+ *	  irq =	interrupt number
+ *
+ * Return: 0 = success or error	condition
+ */
+static int __devinit sl811_found_hc(int addr_io, int data_io, int irq)
+{
+	struct sl811_hc *hc;
+
+	PDEBUG(5, "enter");
+
+	hc = sl811_alloc_hc();
+	if (!hc)
+		return -ENOMEM;
+
+	sl811_init_irq();
+
+	if (!request_region(addr_io, 1, "SL811 USB HOST")) {
+		PDEBUG(1, "request address %d failed", addr_io);
+		sl811_release_hc(hc);
+		return -EBUSY;
+	}
+	hc->addr_io =	addr_io;
+
+	if (!request_region(data_io, 1, "SL811 USB HOST")) {
+		PDEBUG(1, "request address %d failed", data_io);
+		sl811_release_hc (hc);
+		return -EBUSY;
+	}
+	hc->data_io =	data_io;
+
+	usb_register_bus(hc->bus);
+
+	if (request_irq(irq, sl811_interrupt, 0, "SL811", hc)) {
+		PDEBUG(1, "request interrupt %d failed", irq);
+		sl811_release_hc(hc);
+		return -EBUSY;
+	}
+	hc->irq	= irq;
+
+	printk(KERN_INFO __FILE__ ": USB SL811 at %x, addr2 = %x, IRQ %d\n",
+		addr_io, data_io, irq);
+
+	if (sl811_reg_test(hc)) {
+		PDEBUG(1, "SL811 register test failed!");
+		sl811_release_hc(hc);
+		return -ENODEV;
+	}
+	
+	sl811_hc_reset(hc);
+	sl811_connect_rh(hc);
+	
+	return 0;
+}
+
+/*
+ * This	is an init function, and it is the first function being	called
+ *
+ * Return: 0 = success or error	condition
+ */
+static int __init sl811_hcd_init(void)
+{
+	int ret;
+
+	PDEBUG(5, "etner");
+
+	info(DRIVER_VERSION " : " DRIVER_DESC);
+	
+	ret = sl811_found_hc(sl811_addr_io, sl811_data_io, sl811_irq);
+
+	return ret;
+}
+
+/*
+ * This	is a cleanup function, and it is called	when module is unloaded.
+ */
+static void __exit sl811_hcd_cleanup(void)
+{
+	struct list_head *list = sl811_hcd_list.next;
+	struct sl811_hc *hc;
+
+	PDEBUG(5, "enter");
+
+	for (; list != &sl811_hcd_list; ) {
+		hc = list_entry(list, struct sl811_hc, hc_hcd_list);
+		list = list->next;
+		sl811_release_hc(hc);
+	}
+}
+
+module_init(sl811_hcd_init);
+module_exit(sl811_hcd_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff -urN linux-2.4.21-bk9/drivers/usb/host/sl811.h linux-2.4.21-bk10/drivers/usb/host/sl811.h
--- linux-2.4.21-bk9/drivers/usb/host/sl811.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/drivers/usb/host/sl811.h	2003-07-15 02:55:07.000000000 -0700
@@ -0,0 +1,177 @@
+#ifndef __LINUX_SL811_H
+#define __LINUX_SL811_H
+
+//#define SL811_DEBUG
+
+#ifdef SL811_DEBUG
+	#define PDEBUG(level, fmt, args...) \
+		if (debug >= (level)) info("[%s:%d] " fmt, \
+		__PRETTY_FUNCTION__, __LINE__ , ## args)
+#else
+	#define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+//#define SL811_TIMEOUT
+		
+/* Sl811 host control register */
+#define	SL811_CTRL_A		0x00
+#define	SL811_ADDR_A		0x01
+#define	SL811_LEN_A		0x02
+#define	SL811_STS_A		0x03	/* read	*/
+#define	SL811_PIDEP_A		0x03	/* write */
+#define	SL811_CNT_A		0x04	/* read	*/
+#define	SL811_DEV_A		0x04	/* write */
+#define	SL811_CTRL1		0x05
+#define	SL811_INTR		0x06
+#define	SL811_CTRL_B		0x08
+#define	SL811_ADDR_B		0x09
+#define	SL811_LEN_B		0x0A
+#define	SL811_STS_B		0x0B	/* read	*/
+#define	SL811_PIDEP_B		0x0B	/* write */
+#define	SL811_CNT_B		0x0C	/* read	*/
+#define	SL811_DEV_B		0x0C	/* write */
+#define	SL811_INTRSTS		0x0D	/* write clears	bitwise	*/
+#define	SL811_HWREV		0x0E	/* read	*/
+#define	SL811_SOFLOW		0x0E	/* write */
+#define	SL811_SOFCNTDIV		0x0F	/* read	*/
+#define	SL811_CTRL2		0x0F	/* write */
+
+/* USB control register bits (addr 0x00 and addr 0x08) */
+#define	SL811_USB_CTRL_ARM	0x01
+#define	SL811_USB_CTRL_ENABLE	0x02
+#define	SL811_USB_CTRL_DIR_OUT	0x04
+#define	SL811_USB_CTRL_ISO	0x10
+#define	SL811_USB_CTRL_SOF	0x20
+#define	SL811_USB_CTRL_TOGGLE_1	0x40
+#define	SL811_USB_CTRL_PREAMBLE	0x80
+
+/* USB status register bits (addr 0x03 and addr 0x0B) */
+#define	SL811_USB_STS_ACK	0x01
+#define	SL811_USB_STS_ERROR	0x02
+#define	SL811_USB_STS_TIMEOUT	0x04
+#define	SL811_USB_STS_TOGGLE_1	0x08
+#define	SL811_USB_STS_SETUP	0x10
+#define	SL811_USB_STS_OVERFLOW	0x20
+#define	SL811_USB_STS_NAK	0x40
+#define	SL811_USB_STS_STALL	0x80
+
+/* Control register 1 bits (addr 0x05) */
+#define	SL811_CTRL1_SOF		0x01
+#define	SL811_CTRL1_RESET	0x08
+#define	SL811_CTRL1_JKSTATE	0x10
+#define	SL811_CTRL1_SPEED_LOW	0x20
+#define	SL811_CTRL1_SUSPEND	0x40
+
+/* Interrut enable (addr 0x06) and interrupt status register bits (addr 0x0D) */
+#define	SL811_INTR_DONE_A	0x01
+#define	SL811_INTR_DONE_B	0x02
+#define	SL811_INTR_SOF		0x10
+#define	SL811_INTR_INSRMV	0x20
+#define	SL811_INTR_DETECT	0x40
+#define	SL811_INTR_NOTPRESENT	0x40
+#define	SL811_INTR_SPEED_FULL	0x80    /* only in status reg */
+
+/* HW rev and SOF lo register bits (addr 0x0E) */
+#define	SL811_HWR_HWREV		0xF0
+
+/* SOF counter and control reg 2 (addr 0x0F) */
+#define	SL811_CTL2_SOFHI	0x3F
+#define	SL811_CTL2_DSWAP	0x40
+#define	SL811_CTL2_HOST		0x80
+
+/* Set up for 1-ms SOF time. */
+#define SL811_12M_LOW		0xE0
+#define SL811_12M_HI		0x2E
+
+#define SL811_DATA_START	0x10
+#define SL811_DATA_LIMIT	240
+
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS           0x0080
+#define RH_CLEAR_FEATURE        0x0100
+#define RH_SET_FEATURE          0x0300
+#define RH_SET_ADDRESS		0x0500
+#define RH_GET_DESCRIPTOR	0x0680
+#define RH_SET_DESCRIPTOR       0x0700
+#define RH_GET_CONFIGURATION	0x0880
+#define RH_SET_CONFIGURATION	0x0900
+#define RH_GET_STATE            0x0280
+#define RH_GET_INTERFACE        0x0A80
+#define RH_SET_INTERFACE        0x0B00
+#define RH_SYNC_FRAME           0x0C80
+
+
+#define PIDEP(pid, ep) (((pid) & 0x0f) << 4 | (ep))
+
+/* Virtual Root HUB */
+struct virt_root_hub {
+	int devnum; 			/* Address of Root Hub endpoint */ 
+	void *urb;			/* interrupt URB of root hub */
+	int send;			/* active flag */
+	int interval;			/* intervall of roothub interrupt transfers */
+	struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */
+};
+
+struct sl811_td {
+	/* hardware */
+	__u8 ctrl;			/* control register */
+	
+	/* write */			
+	__u8 addr;			/* base adrress register */
+	__u8 len;			/* base length register */
+	__u8 pidep;			/* PId and endpoint register */
+	__u8 dev;			/* device address register */
+	
+	/* read */
+	__u8 status;			/* status register */
+	__u8 left;			/* transfer count register */
+	
+	/* software */
+	__u8 errcnt;			/* error count, begin with 3 */
+	__u8 done;			/* is this td tranfer done */
+	__u8 *buf;			/* point to data buffer for tranfer */
+	int bustime;			/* the bus time need by this td */
+	int td_status;			/* the status of this td */
+	int nakcnt;			/* number of naks */
+	struct urb *urb;			/* the urb this td belongs to */
+	struct list_head td_list;	/* link to a list of the urb */
+};
+
+struct sl811_urb_priv {
+	struct urb *urb;			/* the urb this priv beloings to */
+	struct list_head td_list;	/* list of all the td of this urb */
+	struct sl811_td *cur_td;		/* current td is in processing or it will be */
+	struct sl811_td *first_td;		/* the first td of this urb */
+	struct sl811_td *last_td;		/* the last td of this urb */
+	int interval;			/* the query time value for intr urb */
+	int unlink;			/* is the this urb unlinked */
+	unsigned long inserttime;	/* the time when insert to list */
+};
+
+struct sl811_hc {
+	spinlock_t hc_lock;		/* Lock for this structure */
+	
+	int irq;			/* IRQ number this hc use */
+	int addr_io;			/* I/O address line address */
+	int data_io;			/* I/O data line address */
+	struct virt_root_hub rh;		/* root hub */
+	struct usb_port_status rh_status;/* root hub port status */
+	struct list_head urb_list[6];	/* set of urbs, the order is iso,intr,ctrl,bulk,inactive intr, wait */
+	struct list_head *cur_list;	/* the current list is in process */
+	wait_queue_head_t waitq;	/* deletion of URBs and devices needs a waitqueue */
+	struct sl811_td *cur_td;		/* point to the td is in process */
+	struct list_head hc_hcd_list;	/* list of all hci_hcd */
+	struct usb_bus *bus;    	/* our bus */
+	int active_urbs;		/* total number of active usbs */
+	int frame_number;		/* the current frame number, we do't use it, any one need it? */
+};
+
+#define iso_list 	urb_list[0]	/* set of isoc urbs */
+#define intr_list	urb_list[1]	/* ordered (tree) set of int urbs */
+#define ctrl_list	urb_list[2]	/* set of ctrl urbs */
+#define bulk_list	urb_list[3]	/* set of bulk urbs */
+#define idle_intr_list	urb_list[4]	/* set of intr urbs in its idle time*/
+#define wait_list	urb_list[5]	/* set of wait urbs */
+
+#endif
diff -urN linux-2.4.21-bk9/drivers/usb/serial/ftdi_sio.c linux-2.4.21-bk10/drivers/usb/serial/ftdi_sio.c
--- linux-2.4.21-bk9/drivers/usb/serial/ftdi_sio.c	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/serial/ftdi_sio.c	2003-07-15 02:55:07.000000000 -0700
@@ -250,6 +250,7 @@
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_634_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_VNHCPCUSB_D_PID, 0, 0x3ff) },
+	{ USB_DEVICE_VER(FTDI_VID, FTDI_DSS20_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID, 0, 0x3ff) },
 	{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID, 0, 0x3ff) },
@@ -314,6 +315,7 @@
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_634_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(FTDI_VID, FTDI_VNHCPCUSB_D_PID, 0x400, 0xffff) },
+	{ USB_DEVICE_VER(FTDI_VID, FTDI_DSS20_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID, 0x400, 0xffff) },
 	{ USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID, 0x400, 0xffff) },
@@ -389,6 +391,7 @@
 	{ USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_XF_634_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) },
 	{ USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) },
 	{ USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID) },
diff -urN linux-2.4.21-bk9/drivers/usb/serial/ftdi_sio.h linux-2.4.21-bk10/drivers/usb/serial/ftdi_sio.h
--- linux-2.4.21-bk9/drivers/usb/serial/ftdi_sio.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/serial/ftdi_sio.h	2003-07-15 02:55:07.000000000 -0700
@@ -105,7 +105,13 @@
 #define SEALEVEL_2803_6_PID	0X2863 	/* SeaLINK+8 (2803) Port 6 */
 #define SEALEVEL_2803_7_PID	0X2873 	/* SeaLINK+8 (2803) Port 7 */
 #define SEALEVEL_2803_8_PID	0X2883 	/* SeaLINK+8 (2803) Port 8 */
+
+/*
+ * DSS-20 Sync Station for Sony Ericsson P800
+ */
  
+#define FTDI_DSS20_PID          0xFC82  
+
 /*
  * Home Electronics (www.home-electro.com) USB gadgets
  */
diff -urN linux-2.4.21-bk9/drivers/usb/serial/ipaq.c linux-2.4.21-bk10/drivers/usb/serial/ipaq.c
--- linux-2.4.21-bk9/drivers/usb/serial/ipaq.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/serial/ipaq.c	2003-07-15 02:55:07.000000000 -0700
@@ -125,10 +125,13 @@
 	{ USB_DEVICE(LINKUP_VENDOR_ID, LINKUP_PRODUCT_ID) },
 	{ USB_DEVICE(MICROSOFT_VENDOR_ID, MICROSOFT_00CE_ID) },
 	{ USB_DEVICE(PORTATEC_VENDOR_ID, PORTATEC_PRODUCT_ID) },
+	{ USB_DEVICE(ROVER_VENDOR_ID, ROVER_P5_ID) },
 	{ USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_WIRELESS_ID) },
 	{ USB_DEVICE(SOCKET_VENDOR_ID, SOCKET_PRODUCT_ID) },
 	{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_ID) },
+	{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_E310_ID) },
 	{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_E740_ID) },
+	{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_E335_ID) },
 	{ USB_DEVICE(HTC_VENDOR_ID, HTC_PRODUCT_ID) },
 	{ USB_DEVICE(NEC_VENDOR_ID, NEC_PRODUCT_ID) },
 	{ USB_DEVICE(ASUS_VENDOR_ID, ASUS_A600_PRODUCT_ID) },
diff -urN linux-2.4.21-bk9/drivers/usb/serial/ipaq.h linux-2.4.21-bk10/drivers/usb/serial/ipaq.h
--- linux-2.4.21-bk9/drivers/usb/serial/ipaq.h	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/serial/ipaq.h	2003-07-15 02:55:07.000000000 -0700
@@ -61,6 +61,9 @@
 #define PORTATEC_VENDOR_ID	0x0961
 #define PORTATEC_PRODUCT_ID	0x0010
 
+#define ROVER_VENDOR_ID		0x047b
+#define ROVER_P5_ID		0x3000
+
 #define SAGEM_VENDOR_ID		0x5e04
 #define SAGEM_WIRELESS_ID	0xce00
 
@@ -69,7 +72,9 @@
 
 #define TOSHIBA_VENDOR_ID	0x0930
 #define TOSHIBA_PRODUCT_ID	0x0700
+#define TOSHIBA_E310_ID		0x0705
 #define TOSHIBA_E740_ID		0x0706
+#define TOSHIBA_E335_ID		0x0707
 
 #define HTC_VENDOR_ID		0x0bb4
 #define HTC_PRODUCT_ID		0x00ce
diff -urN linux-2.4.21-bk9/drivers/usb/storage/initializers.h linux-2.4.21-bk10/drivers/usb/storage/initializers.h
--- linux-2.4.21-bk9/drivers/usb/storage/initializers.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/storage/initializers.h	2003-07-15 02:55:07.000000000 -0700
@@ -37,14 +37,19 @@
  * 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/config.h>
 #include "usb.h"
 
-#define UCR61S2B_INIT "\xec\x0a\x06\x00$PCCHIPS"
-
 /* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target
  * mode */
 int usb_stor_euscsi_init(struct us_data *us);
 
+#ifdef CONFIG_USB_STORAGE_SDDR09
+int sddr09_init(struct us_data *us);
+#endif
+
+#define UCR61S2B_INIT "\xec\x0a\x06\x00$PCCHIPS"
+
 /* This function is required to activate all four slots on the UCR-61S2B
  * flash reader */
 int usb_stor_ucr61s2b_init(struct us_data *us);
diff -urN linux-2.4.21-bk9/drivers/usb/storage/protocol.c linux-2.4.21-bk10/drivers/usb/storage/protocol.c
--- linux-2.4.21-bk9/drivers/usb/storage/protocol.c	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/storage/protocol.c	2003-07-15 02:55:07.000000000 -0700
@@ -53,6 +53,22 @@
 /***********************************************************************
  * Helper routines
  ***********************************************************************/
+ 
+static void * find_data_location(Scsi_Cmnd *srb)
+{
+	if (srb->use_sg) {
+		/*
+		 * This piece of code only works if the first page is
+		 * big enough to hold more than 3 bytes -- which is
+		 * _very_ likely.
+		 */
+		struct scatterlist *sg;
+
+		sg = (struct scatterlist *) srb->request_buffer;
+		return (void *) sg[0].address;
+	} else
+		return (void *) srb->request_buffer;
+}
 
 /* Fix-up the return data from an INQUIRY command to show 
  * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
@@ -65,21 +81,48 @@
 	if (srb->cmnd[0] != INQUIRY)
 		return;
 
-	US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2\n");
+	/* oddly short buffer -- bail out */
+	if (srb->request_bufflen < 3)
+		return;
 
-	/* find the location of the data */
-	if (srb->use_sg) {
-		struct scatterlist *sg;
+	data_ptr = find_data_location(srb);
 
-		sg = (struct scatterlist *) srb->request_buffer;
-		data_ptr = (unsigned char *) sg[0].address;
-	} else
-		data_ptr = (unsigned char *)srb->request_buffer;
+	/* if it's already 2, bail */
+	if ((data_ptr[2] & 7) == 2)
+		return;
+
+	US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2 - was %d\n",
+		data_ptr[2] & 7);
 
 	/* Change the SCSI revision number */
 	data_ptr[2] = (data_ptr[2] & ~7) | 2;
 }
 
+/*
+ * Fix-up the return data from a READ CAPACITY command. A Feiya reader
+ * returns a value that is 1 too large.
+ */
+static void fix_read_capacity(Scsi_Cmnd *srb)
+{
+	unsigned char *dp;
+	unsigned long capacity;
+
+	/* verify that it's a READ CAPACITY command */
+	if (srb->cmnd[0] != READ_CAPACITY)
+		return;
+
+	dp = find_data_location(srb);
+
+	capacity = (dp[0]<<24) + (dp[1]<<16) + (dp[2]<<8) + (dp[3]);
+	US_DEBUGP("US: Fixing capacity: from %ld to %ld\n",
+		capacity+1, capacity);
+	capacity--;
+	dp[0] = (capacity >> 24);
+	dp[1] = (capacity >> 16);
+	dp[2] = (capacity >> 8);
+	dp[3] = (capacity);
+}
+
 /***********************************************************************
  * Protocol routines
  ***********************************************************************/
@@ -345,6 +388,10 @@
 
 		/* fix the INQUIRY data if necessary */
 		fix_inquiry_data(srb);
+
+		/* Fix the READ CAPACITY result if necessary */
+		if (us->flags & US_FL_FIX_CAPACITY)
+			fix_read_capacity(srb);
 	}
 }
 
diff -urN linux-2.4.21-bk9/drivers/usb/storage/sddr09.c linux-2.4.21-bk10/drivers/usb/storage/sddr09.c
--- linux-2.4.21-bk9/drivers/usb/storage/sddr09.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/storage/sddr09.c	2003-07-15 02:55:07.000000000 -0700
@@ -1504,7 +1504,6 @@
 	}
 }
 
-#if 0
 /*
  * It is unclear whether this does anything.
  * However, the request sense succeeds only after a reboot,
@@ -1512,6 +1511,7 @@
  */
 int
 sddr09_init(struct us_data *us) {
+#if 0
 	int result;
 	unsigned char data[2];
 
@@ -1550,9 +1550,9 @@
 		// additional transfer length * = sizeof(data) - 7
 	}
 #endif
+#endif
 	return USB_STOR_TRANSPORT_GOOD;		/* not result */
 }
-#endif
 
 /*
  * Transport for the Sandisk SDDR-09
diff -urN linux-2.4.21-bk9/drivers/usb/storage/unusual_devs.h linux-2.4.21-bk10/drivers/usb/storage/unusual_devs.h
--- linux-2.4.21-bk9/drivers/usb/storage/unusual_devs.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/storage/unusual_devs.h	2003-07-15 02:55:07.000000000 -0700
@@ -75,29 +75,21 @@
 
 /* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
  * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message
- * always fails and confuses drive; without US_FL_START_STOP, drive accesses
- * (read or write) all fail.
+ * always fails and confuses drive.
  */
 UNUSUAL_DEV(  0x0411, 0x001c, 0x0113, 0x0113,
 		"Buffalo",
 		"DUB-P40G HDD",
-		US_SC_SCSI, US_PR_BULK, NULL,
-		US_FL_FIX_INQUIRY | US_FL_START_STOP),
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_FIX_INQUIRY ),
 
 #ifdef CONFIG_USB_STORAGE_DPCM
 UNUSUAL_DEV(  0x0436, 0x0005, 0x0100, 0x0100,
 		"Microtech",
 		"CameraMate (DPCM_USB)",
- 		US_SC_SCSI, US_PR_DPCM_USB, NULL,
-		US_FL_START_STOP ),
+ 		US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ),
 #endif
 
-/* Made with the help of Edd Dumbill <edd@usefulinc.com> */
-UNUSUAL_DEV(  0x0451, 0x5409, 0x0001, 0x0001,
-		"Frontier Labs",
-		"Nex II Digital",
-		US_SC_SCSI, US_PR_BULK, NULL, US_FL_START_STOP),
-
 /* Patch submitted by Philipp Friedrich <philipp@void.at> */
 UNUSUAL_DEV(  0x0482, 0x0100, 0x0100, 0x0100,
 		"Kyocera",
@@ -124,15 +116,6 @@
 		"785EPX Storage",
 		US_SC_SCSI, US_PR_BULK, NULL, US_FL_SINGLE_LUN),
 
-/* Reported by Jan Willamowius <jan@willamowius.de>
- * The device needs the flags only.
- */
-UNUSUAL_DEV(  0x04c8, 0x0723, 0x0000, 0x9999,
-		"Konica",
-		"KD-200Z",
-		US_SC_SCSI, US_PR_BULK, NULL,
-		US_FL_START_STOP),
-
 UNUSUAL_DEV(  0x04cb, 0x0100, 0x0000, 0x2210,
 		"Fujifilm",
 		"FinePix 1400Zoom",
@@ -144,7 +127,7 @@
 UNUSUAL_DEV(  0x04ce, 0x0002, 0x0074, 0x0074,
 		"ScanLogic",
 		"SL11R-IDE",
-		US_SC_SCSI, US_PR_BULK, NULL,
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_FIX_INQUIRY),
 
 /* Reported by Kriston Fincher <kriston@airmail.net>
@@ -153,9 +136,16 @@
  * This entry is needed because the device reports Sub=ff 
  */
 UNUSUAL_DEV(  0x04da, 0x0901, 0x0100, 0x0200,
-               "Panasonic",
-               "LS-120 Camera",
-               US_SC_UFI, US_PR_CBI, NULL, 0),
+		"Panasonic",
+		"LS-120 Camera",
+		US_SC_UFI, US_PR_CBI, NULL, 0),
+
+/* From Yukihiro Nakai, via zaitcev@yahoo.com.
+ * This is needed for CB instead of CBI */
+UNUSUAL_DEV(  0x04da, 0x0d05, 0x0000, 0x0000,
+		"Sharp CE-CW05",
+		"CD-R/RW Drive",
+		US_SC_8070, US_PR_CB, NULL, 0),
 
 /* Most of the following entries were developed with the help of
  * Shuttle/SCM directly.
@@ -176,15 +166,15 @@
 		"Sandisk",
 		"ImageMate SDDR09",
 		US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
-		US_FL_SINGLE_LUN | US_FL_START_STOP ),
-#endif
+		US_FL_SINGLE_LUN ),
 
 /* This entry is from Andries.Brouwer@cwi.nl */
 UNUSUAL_DEV(  0x04e6, 0x0005, 0x0100, 0x0208,
 		"SCM Microsystems",
 		"eUSB SmartMedia / CompactFlash Adapter",
-		US_SC_SCSI, US_PR_DPCM_USB, NULL, 
-		US_FL_START_STOP), 
+		US_SC_SCSI, US_PR_DPCM_USB, sddr09_init, 
+		0), 
+#endif
 
 UNUSUAL_DEV(  0x04e6, 0x0006, 0x0100, 0x0205, 
 		"Shuttle",
@@ -240,58 +230,58 @@
 		"Iomega",
 		"USB Clik! 40",
 		US_SC_8070, US_PR_BULK, NULL,
-		US_FL_FIX_INQUIRY | US_FL_START_STOP ),
-
-/* Submitted by Lars Gemeinhardt <linux-usb@gemeinhardt.info>
- * Needed for START_STOP flag */
-UNUSUAL_DEV(  0x0547, 0x2810, 0x0001, 0x0001,
-                "Mello",
-                "MP3 Player",
-		US_SC_SCSI, US_PR_BULK, NULL,
-		US_FL_START_STOP),
+		US_FL_FIX_INQUIRY ),
 
 /* This entry is needed because the device reports Sub=ff */
 UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0450, 
 		"Sony",
 		"DSC-S30/S70/S75/505V/F505/F707/F717/P8", 
 		US_SC_SCSI, US_PR_CB, NULL,
-		US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE ),
+		US_FL_SINGLE_LUN | US_FL_MODE_XLATE ),
 
 /* Reported by wim@geeks.nl */
 UNUSUAL_DEV(  0x054c, 0x0025, 0x0100, 0x0100, 
 		"Sony",
 		"Memorystick NW-MS7",
 		US_SC_UFI, US_PR_CB, NULL,
-		US_FL_SINGLE_LUN | US_FL_START_STOP ),
+		US_FL_SINGLE_LUN ),
+
+#ifdef CONFIG_USB_STORAGE_ISD200
+UNUSUAL_DEV(  0x054c, 0x002b, 0x0100, 0x0110,
+		"Sony",
+		"Portable USB Harddrive V2",
+		US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+		0 ),
+#endif
 
 UNUSUAL_DEV(  0x054c, 0x002d, 0x0100, 0x0100, 
 		"Sony",
 		"Memorystick MSAC-US1",
 		US_SC_UFI, US_PR_CB, NULL,
-		US_FL_SINGLE_LUN | US_FL_START_STOP ),
+		US_FL_SINGLE_LUN ),
 
 /* Submitted by Klaus Mueller <k.mueller@intershop.de> */
 UNUSUAL_DEV(  0x054c, 0x002e, 0x0106, 0x0310, 
 		"Sony",
 		"Handycam",
 		US_SC_SCSI, US_PR_CB, NULL,
-		US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE),
+		US_FL_SINGLE_LUN | US_FL_MODE_XLATE),
 
 UNUSUAL_DEV(  0x054c, 0x0032, 0x0000, 0x9999,
-                "Sony",
+		"Sony",
 		"Memorystick MSC-U01N",
-		US_SC_UFI, US_PR_CB, NULL,
-		US_FL_SINGLE_LUN | US_FL_START_STOP ),
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_SINGLE_LUN ),
 		
 UNUSUAL_DEV(  0x054c, 0x0069, 0x0000, 0x9999,
 		"Sony",
 		"Memorystick MSC-U03",
 		US_SC_UFI, US_PR_CB, NULL,
-		US_FL_SINGLE_LUN | US_FL_START_STOP ),
+		US_FL_SINGLE_LUN ),
 
 /* Submitted by Nathan Babb <nathan@lexi.com> */
 UNUSUAL_DEV(  0x054c, 0x006d, 0x0000, 0x9999,
-                "Sony",
+		"Sony",
 		"PEG Mass Storage",
 		US_SC_8070, US_PR_CBI, NULL,
 		US_FL_FIX_INQUIRY ),
@@ -305,7 +295,7 @@
 UNUSUAL_DEV(  0x057b, 0x0000, 0x0300, 0x9999, 
 		"Y-E Data",
 		"Flashbuster-U",
-		US_SC_UFI,  US_PR_CBI, NULL,
+		US_SC_DEVICE,  US_PR_DEVICE, NULL,
 		US_FL_SINGLE_LUN),
 
 UNUSUAL_DEV(  0x059f, 0xa601, 0x0200, 0x0200, 
@@ -313,65 +303,30 @@
 		"USB Hard Disk",
 		US_SC_RBC, US_PR_CB, NULL, 0 ), 
 
-/* Enable USB storage access to the MMC/SD and CompactFlash cards inside the
- * Pontis SP600 MP3 player (entry found on http://www.pontis.de/).
- */
-UNUSUAL_DEV(  0x09bc, 0x0003, 0x0000, 0x9999,
-		"PONTIS",
-		"SP600",
-		US_SC_SCSI, US_PR_BULK, NULL,
-		US_FL_START_STOP ),
-
-/* This Pentax still camera is not conformant
- * to the USB storage specification: -
- * - It does not like the INQUIRY command. So we must handle this command
- *   of the SCSI layer ourselves.
- * Tested on Rev. 10.00 (0x1000)
- * Submitted by James Courtier-Dutton <James@superbug.demon.co.uk>
- */
-UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1000,
-                "Pentax",
-                "Optio 2/3/400",
-                US_SC_8070, US_PR_CBI, NULL,
-                US_FL_FIX_INQUIRY ),
-
-/* Submitted by Per Winkvist <per.winkvist@uk.com> */
-UNUSUAL_DEV( 0x0a17, 0x006, 0x1000, 0x9009,
-                "Pentax",
-                "Optio S",
-                US_SC_8070, US_PR_CBI, NULL,
-                US_FL_FIX_INQUIRY ),
-
 #ifdef CONFIG_USB_STORAGE_ISD200
 UNUSUAL_DEV(  0x05ab, 0x0031, 0x0100, 0x0110,
-                "In-System",
-                "USB/IDE Bridge (ATA/ATAPI)",
-                US_SC_ISD200, US_PR_BULK, isd200_Initialization,
-                0 ),
+		"In-System",
+		"USB/IDE Bridge (ATA/ATAPI)",
+		US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+		0 ),
 
 UNUSUAL_DEV(  0x05ab, 0x0301, 0x0100, 0x0110,
-                "In-System",
-                "Portable USB Harddrive V2",
-                US_SC_ISD200, US_PR_BULK, isd200_Initialization,
-                0 ),
+		"In-System",
+		"Portable USB Harddrive V2",
+		US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+		0 ),
 
 UNUSUAL_DEV(  0x05ab, 0x0351, 0x0100, 0x0110,
-                "In-System",
-                "Portable USB Harddrive V2",
-                US_SC_ISD200, US_PR_BULK, isd200_Initialization,
-                0 ),
+		"In-System",
+		"Portable USB Harddrive V2",
+		US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+		0 ),
 
 UNUSUAL_DEV(  0x05ab, 0x5701, 0x0100, 0x0110,
-                "In-System",
-                "USB Storage Adapter V2",
-                US_SC_ISD200, US_PR_BULK, isd200_Initialization,
-                0 ),
-
-UNUSUAL_DEV(  0x054c, 0x002b, 0x0100, 0x0110,
-                "Sony",
-                "Portable USB Harddrive V2",
-                US_SC_ISD200, US_PR_BULK, isd200_Initialization,
-                0 ),
+		"In-System",
+		"USB Storage Adapter V2",
+		US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+		0 ),
 #endif
 
 #ifdef CONFIG_USB_STORAGE_JUMPSHOT
@@ -396,7 +351,7 @@
 UNUSUAL_DEV(  0x05e3, 0x0700, 0x0000, 0xffff,
 		"SIIG",
 		"CompactFlash Card Reader",
-		US_SC_SCSI, US_PR_BULK, NULL,
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_FIX_INQUIRY ),
 
 /* Reported by Peter Marks <peter.marks@turner.com>
@@ -409,7 +364,7 @@
 UNUSUAL_DEV(  0x05e3, 0x0702, 0x0000, 0x0001,
 		"EagleTec",
 		"External Hard Disk",
-		US_SC_SCSI, US_PR_BULK, NULL,
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_FIX_INQUIRY ),
 
 /* Reported by Hanno Boeck <hanno@gmx.de>
@@ -430,31 +385,9 @@
 		"Olympus",
 		"Camedia MAUSB-2",
 		US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
-		US_FL_SINGLE_LUN | US_FL_START_STOP ),
+		US_FL_SINGLE_LUN ),
 #endif
 
-/* Submitted by kedar@centillium
- * Needed for START_STOP flag, but that is unconfirmed */
-UNUSUAL_DEV( 0x0686, 0x4006, 0x0001, 0x0001,
-                "Minolta",
-                "Dimage S304",
-                US_SC_SCSI, US_PR_BULK, NULL,
-                US_FL_START_STOP ),
-
-/* Submitted by f.brugmans@hccnet.nl
- * Needed for START_STOP flag */
-UNUSUAL_DEV( 0x0686, 0x4007, 0x0001, 0x0001,
-                "Minolta",
-                "Dimage S304",
-                US_SC_SCSI, US_PR_BULK, NULL,
-                US_FL_START_STOP ),
-
-UNUSUAL_DEV( 0x0686, 0x400b, 0x0001, 0x0001, 
-		"Minolta", 
-		"Dimage 7i", 
-		US_SC_SCSI, US_PR_BULK, NULL, 
-		US_FL_START_STOP ),
-
 UNUSUAL_DEV(  0x0693, 0x0002, 0x0100, 0x0100, 
 		"Hagiwara",
 		"FlashGate SmartMedia",
@@ -469,33 +402,33 @@
 		"Sandisk",
 		"ImageMate SDDR-05a",
 		US_SC_SCSI, US_PR_CB, NULL,
-		US_FL_SINGLE_LUN | US_FL_START_STOP),
+		US_FL_SINGLE_LUN ),
 
 UNUSUAL_DEV(  0x0781, 0x0002, 0x0009, 0x0009, 
 		"Sandisk",
 		"ImageMate SDDR-31",
 		US_SC_SCSI, US_PR_BULK, NULL,
-		US_FL_IGNORE_SER),
+		US_FL_IGNORE_SER ),
 
 UNUSUAL_DEV(  0x0781, 0x0100, 0x0100, 0x0100,
-                "Sandisk",
-                "ImageMate SDDR-12",
-                US_SC_SCSI, US_PR_CB, NULL,
-                US_FL_SINGLE_LUN ),
+		"Sandisk",
+		"ImageMate SDDR-12",
+		US_SC_SCSI, US_PR_CB, NULL,
+		US_FL_SINGLE_LUN ),
 
 #ifdef CONFIG_USB_STORAGE_SDDR09
 UNUSUAL_DEV(  0x0781, 0x0200, 0x0000, 0x9999, 
 		"Sandisk",
 		"ImageMate SDDR-09",
 		US_SC_SCSI, US_PR_EUSB_SDDR09, NULL,
-		US_FL_SINGLE_LUN | US_FL_START_STOP ),
+		US_FL_SINGLE_LUN ),
 #endif
 
 #ifdef CONFIG_USB_STORAGE_FREECOM
 UNUSUAL_DEV(  0x07ab, 0xfc01, 0x0000, 0x9999,
-                "Freecom",
-                "USB-IDE",
-                US_SC_QIC, US_PR_FREECOM, freecom_init, 0),
+		"Freecom",
+		"USB-IDE",
+		US_SC_QIC, US_PR_FREECOM, freecom_init, 0),
 #endif
 
 UNUSUAL_DEV(  0x07af, 0x0004, 0x0100, 0x0133, 
@@ -514,8 +447,7 @@
 UNUSUAL_DEV(  0x07af, 0x0006, 0x0100, 0x0100,
 		"Microtech",
 		"CameraMate (DPCM_USB)",
- 		US_SC_SCSI, US_PR_DPCM_USB, NULL,
-		US_FL_START_STOP ),
+ 		US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ),
 #endif
 
 #ifdef CONFIG_USB_STORAGE_DATAFAB
@@ -565,7 +497,18 @@
 		"Simple Tech/Datafab CF+SM Reader",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
 		US_FL_MODE_XLATE ),
+#endif
+		
+#ifdef CONFIG_USB_STORAGE_SDDR55
+/* Contributed by Peter Waechtler */
+UNUSUAL_DEV( 0x07c4, 0xa103, 0x0000, 0x9999,
+		"Datafab",
+		"MDSM-B reader",
+		US_SC_SCSI, US_PR_SDDR55, NULL,
+		US_FL_FIX_INQUIRY ),
+#endif
 
+#ifdef CONFIG_USB_STORAGE_DATAFAB
 /* Submitted by Olaf Hering <olh@suse.de> */
 UNUSUAL_DEV(  0x07c4, 0xa109, 0x0000, 0xffff,
 		"Datafab Systems, Inc.",
@@ -583,7 +526,7 @@
 UNUSUAL_DEV(  0x07c4, 0xa400, 0x0000, 0xffff,
 		"Datafab",
 		"KECF-USB",
-		US_SC_SCSI, US_PR_BULK, NULL,
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
 		US_FL_FIX_INQUIRY ),
 
 /* Casio QV 2x00/3x00/4000/8000 digital still cameras are not conformant
@@ -594,18 +537,34 @@
  *   of the SCSI layer ourselves.
  */
 UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9009,
-                "Casio",
-                "QV DigitalCamera",
-                US_SC_8070, US_PR_CB, NULL,
-                US_FL_FIX_INQUIRY ),
+		"Casio",
+		"QV DigitalCamera",
+		US_SC_8070, US_PR_CB, NULL,
+		US_FL_FIX_INQUIRY ),
 
 /* Submitted by Hartmut Wahl <hwahl@hwahl.de>*/
 UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001,
-        "Samsung",
-        "Digimax 410",
-        US_SC_SCSI, US_PR_BULK, NULL,
-        US_FL_FIX_INQUIRY),
-                
+		"Samsung",
+		"Digimax 410",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_FIX_INQUIRY),
+
+/* Aiptek PocketCAM 3Mega
+ * Nicolas DUPEUX <nicolas@dupeux.net> 
+ */
+UNUSUAL_DEV(  0x08ca, 0x2011, 0x0000, 0x9999,
+		"AIPTEK",
+		"PocketCAM 3Mega",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_MODE_XLATE ),
+
+/* aeb */
+UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff,
+		"Feiya",
+		"5-in-1 Card Reader",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_FIX_CAPACITY ),
+
 UNUSUAL_DEV(  0x097a, 0x0001, 0x0000, 0x0001,
 		"Minds@Work",
 		"Digital Wallet",
@@ -617,28 +576,35 @@
 		"IBM USB Memory Key",
 		US_SC_SCSI, US_PR_BULK, NULL,
 		US_FL_FIX_INQUIRY ),
+
+/* This Pentax still camera is not conformant
+ * to the USB storage specification: -
+ * - It does not like the INQUIRY command. So we must handle this command
+ *   of the SCSI layer ourselves.
+ * Tested on Rev. 10.00 (0x1000)
+ * Submitted by James Courtier-Dutton <James@superbug.demon.co.uk>
+ */
+UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1000,
+                "Pentax",
+                "Optio 2/3/400",
+                US_SC_DEVICE, US_PR_DEVICE, NULL,
+                US_FL_FIX_INQUIRY ),
+
+/* Submitted by Per Winkvist <per.winkvist@uk.com> */
+UNUSUAL_DEV( 0x0a17, 0x006, 0x1000, 0x9009,
+                "Pentax",
+                "Optio S",
+                US_SC_8070, US_PR_CBI, NULL,
+                US_FL_FIX_INQUIRY ),
 		
 #ifdef CONFIG_USB_STORAGE_ISD200
 UNUSUAL_DEV(  0x0bf6, 0xa001, 0x0100, 0x0110,
-                "ATI",
-                "USB Cable 205",
-                US_SC_ISD200, US_PR_BULK, isd200_Initialization,
-                0 ),
+		"ATI",
+		"USB Cable 205",
+		US_SC_ISD200, US_PR_BULK, isd200_Initialization,
+		0 ),
 #endif
 
-/* Submitted by Brian Hall <brihall@pcisys.net>
- * Needed for START_STOP flag */
-UNUSUAL_DEV(  0x0c76, 0x0003, 0x0100, 0x0100,
-		"JMTek",
-		"USBDrive",
-		US_SC_SCSI, US_PR_BULK, NULL,
-		US_FL_START_STOP ),
-UNUSUAL_DEV(  0x0c76, 0x0005, 0x0100, 0x0100,
-		"JMTek",
-		"USBDrive",
-		US_SC_SCSI, US_PR_BULK, NULL,
-		US_FL_START_STOP ),
-
 /* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu>
  * Tested on hardware version 1.10.
  * Entry is needed only for the initializer function override.
@@ -657,8 +623,8 @@
 UNUSUAL_DEV(  0x1065, 0x2136, 0x0000, 0x9999,
 		"CCYU TECHNOLOGY",
 		"EasyDisk Portable Device",
-		US_SC_SCSI, US_PR_BULK, NULL,
-		US_FL_MODE_XLATE | US_FL_START_STOP),
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_MODE_XLATE ),
 
 #ifdef CONFIG_USB_STORAGE_SDDR55
 UNUSUAL_DEV(  0x55aa, 0xa103, 0x0000, 0x9999, 
@@ -667,13 +633,3 @@
 		US_SC_SCSI, US_PR_SDDR55, NULL,
 		US_FL_SINGLE_LUN),
 #endif
-
-/* Aiptek PocketCAM 3Mega
- * Nicolas DUPEUX <nicolas@dupeux.net> 
- */
-UNUSUAL_DEV(  0x08ca, 0x2011, 0x0000, 0x9999,
-	"AIPTEK",
-	"PocketCAM 3Mega",
-	US_SC_SCSI, US_PR_BULK, NULL,
-	US_FL_MODE_XLATE | US_FL_START_STOP),
-
diff -urN linux-2.4.21-bk9/drivers/usb/storage/usb.c linux-2.4.21-bk10/drivers/usb/storage/usb.c
--- linux-2.4.21-bk9/drivers/usb/storage/usb.c	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/storage/usb.c	2003-07-15 02:55:07.000000000 -0700
@@ -401,18 +401,6 @@
 				break;
 			}
 
-			/* handle those devices which can't do a START_STOP */
-			if ((us->srb->cmnd[0] == START_STOP) &&
-			    (us->flags & US_FL_START_STOP)) {
-				US_DEBUGP("Skipping START_STOP command\n");
-				us->srb->result = GOOD << 1;
-
-				set_current_state(TASK_INTERRUPTIBLE);
-				us->srb->scsi_done(us->srb);
-				us->srb = NULL;
-				break;
-			}
-
 			/* lock the device pointers */
 			down(&(us->dev_semaphore));
 
diff -urN linux-2.4.21-bk9/drivers/usb/storage/usb.h linux-2.4.21-bk10/drivers/usb/storage/usb.h
--- linux-2.4.21-bk9/drivers/usb/storage/usb.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/storage/usb.h	2003-07-15 02:55:07.000000000 -0700
@@ -99,10 +99,10 @@
 #define US_FL_SINGLE_LUN      0x00000001 /* allow access to only LUN 0	    */
 #define US_FL_MODE_XLATE      0x00000002 /* translate _6 to _10 commands for
 						    Win/MacOS compatibility */
-#define US_FL_START_STOP      0x00000004 /* ignore START_STOP commands	    */
 #define US_FL_IGNORE_SER      0x00000010 /* Ignore the serial number given  */
 #define US_FL_SCM_MULT_TARG   0x00000020 /* supports multiple targets */
 #define US_FL_FIX_INQUIRY     0x00000040 /* INQUIRY response needs fixing */
+#define US_FL_FIX_CAPACITY    0x00000080 /* READ_CAPACITY response too big */
 
 #define USB_STOR_STRING_LEN 32
 
diff -urN linux-2.4.21-bk9/drivers/usb/usb.c linux-2.4.21-bk10/drivers/usb/usb.c
--- linux-2.4.21-bk9/drivers/usb/usb.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/usb.c	2003-07-15 02:55:07.000000000 -0700
@@ -2154,7 +2154,7 @@
 		if (err < 0) {
 			err("error getting string descriptor 0 (error=%d)", err);
 			goto errout;
-		} else if (tbuf[0] < 4) {
+		} else if (err < 4 || tbuf[0] < 4) {
 			err("string descriptor 0 too short");
 			err = -EINVAL;
 			goto errout;
diff -urN linux-2.4.21-bk9/drivers/usb/usbnet.c linux-2.4.21-bk10/drivers/usb/usbnet.c
--- linux-2.4.21-bk9/drivers/usb/usbnet.c	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/usbnet.c	2003-07-15 02:55:07.000000000 -0700
@@ -162,7 +162,7 @@
 #define	CONFIG_USB_GENESYS
 #define	CONFIG_USB_NET1080
 #define	CONFIG_USB_PL2301
-#define	CONFIG_USB_SA1100
+#define	CONFIG_USB_ARMLINUX
 #define	CONFIG_USB_ZAURUS
 
 
@@ -218,6 +218,11 @@
 	struct list_head	dev_list;
 	wait_queue_head_t	*wait;
 
+	// i/o info: pipes etc
+	unsigned		in, out;
+	unsigned		maxpacket;
+	//struct timer_list	delay;
+
 	// protocol/interface state
 	struct net_device	net;
 	struct net_device_stats	stats;
@@ -266,14 +271,12 @@
 	// FIXME -- also an interrupt mechanism
 	// useful for at least PL2301/2302 and GL620USB-A
 
-	/* framework currently "knows" bulk EPs talk packets */
+	/* for new devices, use the descriptor-reading code instead */
 	int		in;		/* rx endpoint */
 	int		out;		/* tx endpoint */
 	int		epsize;
 };
 
-#define EP_SIZE(usbnet)	((usbnet)->driver_info->epsize)
-
 // we record the state for each of our queued skbs
 enum skb_state {
 	illegal = 0,
@@ -310,6 +313,61 @@
 
 /*-------------------------------------------------------------------------*/
 
+/* handles CDC Ethernet and many other network "bulk data" interfaces */
+static int
+get_endpoints (struct usbnet *dev, struct usb_interface *intf)
+{
+	int				tmp;
+	struct usb_interface_descriptor	*alt;
+	struct usb_endpoint_descriptor	*in, *out;
+
+	for (tmp = 0; tmp < intf->max_altsetting; tmp++) {
+		unsigned	ep;
+
+		in = out = 0;
+		alt = intf->altsetting + tmp;
+
+		/* take the first altsetting with in-bulk + out-bulk;
+		 * ignore other endpoints and altsetttings.
+		 */
+		for (ep = 0; ep < alt->bNumEndpoints; ep++) {
+			struct usb_endpoint_descriptor	*e;
+
+			e = alt->endpoint + ep;
+			if (e->bmAttributes != USB_ENDPOINT_XFER_BULK)
+				continue;
+			if (e->bEndpointAddress & USB_DIR_IN) {
+				if (!in)
+					in = e;
+			} else {
+				if (!out)
+					out = e;
+			}
+			if (in && out)
+				goto found;
+		}
+	}
+	return -EINVAL;
+
+found:
+	if (alt->bAlternateSetting != 0
+			|| !(dev->driver_info->flags & FLAG_NO_SETINT)) {
+		tmp = usb_set_interface (dev->udev, alt->bInterfaceNumber,
+				alt->bAlternateSetting);
+		if (tmp < 0)
+			return tmp;
+	}
+	
+	dev->in = usb_rcvbulkpipe (dev->udev,
+			in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	dev->out = usb_sndbulkpipe (dev->udev,
+			out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
 #ifdef DEBUG
 #define devdbg(usbnet, fmt, arg...) \
 	printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net.name, ## arg)
@@ -361,9 +419,6 @@
 
 static const struct driver_info	belkin_info = {
 	.description =	"Belkin, eTEK, or compatible",
-
-	.in = 1, .out = 1,		// direction distinguishes these
-	.epsize =64,
 };
 
 #endif	/* CONFIG_USB_BELKIN */
@@ -708,7 +763,7 @@
 	*packet_len = length;
 
 	// add padding byte
-	if ((skb->len % EP_SIZE (dev)) == 0)
+	if ((skb->len % dev->maxpacket) == 0)
 		skb_put (skb, 1);
 
 	return skb;
@@ -1174,9 +1229,6 @@
 	.check_connect =net1080_check_connect,
 	.rx_fixup =	net1080_rx_fixup,
 	.tx_fixup =	net1080_tx_fixup,
-
-	.in = 1, .out = 1,		// direction distinguishes these
-	.epsize =64,
 };
 
 #endif /* CONFIG_USB_NET1080 */
@@ -1241,67 +1293,45 @@
 	.flags =	FLAG_NO_SETINT,
 		/* some PL-2302 versions seem to fail usb_set_interface() */
 	.reset =	pl_reset,
-
-	.in = 3, .out = 2,
-	.epsize =64,
 };
 
 #endif /* CONFIG_USB_PL2301 */
 
 
 
-#ifdef	CONFIG_USB_PXA
+#ifdef	CONFIG_USB_ARMLINUX
 
 /*-------------------------------------------------------------------------
  *
- * PXA250 and PXA210 use XScale cores (ARM v5TE) with better USB support,
- * and different USB endpoint numbering than the SA1100 devices.
+ * Standard ARM kernels include a "usb-eth" driver, or a newer
+ * "ethernet gadget" driver for basic USB connectivity.  The vendor
+ * and product code may also be used for other non-CDC Linux devices,
+ * if they all maintain protocol compatibility.
+ *
+ * That means lots of hardware could match here, possibly using
+ * different endpoint numbers (and bcdVersion ids).  so we rely on
+ * endpoint descriptors to sort that out for us.
  *
- *-------------------------------------------------------------------------*/
-
-static const struct driver_info	pxa_info = {
-	.description =	"PXA-250 Linux Device",
-	.check_connect = always_connected,
-
-	.in = 1, .out = 2,
-	.epsize = 64,
-};
-
-#endif	/* CONFIG_USB_PXA */
-
-
-
-#ifdef	CONFIG_USB_SA1100
-
-/*-------------------------------------------------------------------------
- *
- * Intel's SA-1100 chip integrates basic USB support, and is used
- * in PDAs like some iPAQs, the Yopy, some Zaurus models, and more.
- * When they run Linux, arch/arm/mach-sa1100/usb-eth.c may be used to
- * network using minimal USB framing data.
- *
- * This describes the driver currently in standard ARM Linux kernels.
- * The Zaurus uses a different driver (see later).
+ * (Current Zaurus models need a different driver; see later.)
  *
  *-------------------------------------------------------------------------*/
 
 static const struct driver_info	linuxdev_info = {
-	.description =	"SA-1100 Linux Device",
+	.description =	"Linux Device",
 	.check_connect = always_connected,
-
-	.in = 2, .out = 1,
-	.epsize = 64,
 };
 
 static const struct driver_info	yopy_info = {
 	.description =	"Yopy",
 	.check_connect = always_connected,
+};
 
-	.in = 2, .out = 1,
-	.epsize = 64,
+static const struct driver_info	blob_info = {
+	.description =	"Boot Loader OBject",
+	.check_connect = always_connected,
 };
 
-#endif	/* CONFIG_USB_SA1100 */
+#endif	/* CONFIG_USB_ARMLINUX */
 
 
 #ifdef CONFIG_USB_ZAURUS
@@ -1310,8 +1340,8 @@
 
 /*-------------------------------------------------------------------------
  *
- * Zaurus is also a SA-1110 based PDA, but one using a different driver
- * (and framing) for its USB slave/gadget controller than the case above.
+ * Zaurus PDAs are also ARM based, but currently use different drivers
+ * (and framing) for USB slave/gadget controllers than the case above.
  *
  * For the current version of that driver, the main way that framing is
  * nonstandard (also from perspective of the CDC ethernet model!) is a
@@ -1349,6 +1379,7 @@
 	return skb;
 }
 
+/* SA-1100 based */
 static const struct driver_info	zaurus_sl5x00_info = {
 	.description =	"Sharp Zaurus SL-5x00",
 	.flags =	FLAG_FRAMING_Z,
@@ -1358,6 +1389,8 @@
 	.in = 2, .out = 1,
 	.epsize = 64,
 };
+
+/* PXA-2xx based */
 static const struct driver_info	zaurus_sla300_info = {
 	.description =	"Sharp Zaurus SL-A300",
 	.flags =	FLAG_FRAMING_Z,
@@ -1387,9 +1420,6 @@
 	.epsize = 64,
 };
 
-// SL-5600 and C-700 are PXA based; should resemble A300
-// but C-700 had a different idProduct so i had an entry :)
-
 #endif
 
 
@@ -1417,7 +1447,7 @@
 		return -EINVAL;
 #endif
 	// no second zero-length packet read wanted after mtu-sized packets
-	if (((new_mtu + sizeof (struct ethhdr)) % EP_SIZE (dev)) == 0)
+	if (((new_mtu + sizeof (struct ethhdr)) % dev->maxpacket) == 0)
 		return -EDOM;
 	net->mtu = new_mtu;
 	return 0;
@@ -1506,8 +1536,7 @@
 	entry->state = rx_start;
 	entry->length = 0;
 
-	usb_fill_bulk_urb (urb, dev->udev,
-		usb_rcvbulkpipe (dev->udev, dev->driver_info->in),
+	usb_fill_bulk_urb (urb, dev->udev, dev->in,
 		skb->data, size, rx_complete, skb);
 	urb->transfer_flags |= USB_ASYNC_UNLINK;
 
@@ -1856,8 +1885,7 @@
 	/* usb_clear_halt() needs a thread context */
 	if (test_bit (EVENT_TX_HALT, &dev->flags)) {
 		unlink_urbs (&dev->txq);
-		status = usb_clear_halt (dev->udev,
-			usb_sndbulkpipe (dev->udev, dev->driver_info->out));
+		status = usb_clear_halt (dev->udev, dev->out);
 		if (status < 0)
 			err ("%s: can't clear tx halt, status %d",
 				dev->net.name, status);
@@ -1868,8 +1896,7 @@
 	}
 	if (test_bit (EVENT_RX_HALT, &dev->flags)) {
 		unlink_urbs (&dev->rxq);
-		status = usb_clear_halt (dev->udev,
-			usb_rcvbulkpipe (dev->udev, dev->driver_info->in));
+		status = usb_clear_halt (dev->udev, dev->in);
 		if (status < 0)
 			err ("%s: can't clear rx halt, status %d",
 				dev->net.name, status);
@@ -1980,11 +2007,10 @@
 #endif	/* CONFIG_USB_NET1080 */
 
 	/* don't assume the hardware handles USB_ZERO_PACKET */
-	if ((length % EP_SIZE (dev)) == 0)
+	if ((length % dev->maxpacket) == 0)
 		skb->len++;
 
-	usb_fill_bulk_urb (urb, dev->udev,
-			usb_sndbulkpipe (dev->udev, info->out),
+	usb_fill_bulk_urb (urb, dev->udev, dev->out,
 			skb->data, skb->len, tx_complete, skb);
 	urb->transfer_flags |= USB_ASYNC_UNLINK;
 
@@ -2021,7 +2047,7 @@
 	if (retval) {
 		devdbg (dev, "drop, code %d", retval);
 drop:
-		retval = NET_XMIT_DROP;
+		retval = NET_XMIT_SUCCESS;
 		dev->stats.tx_dropped++;
 		if (skb)
 			dev_kfree_skb_any (skb);
@@ -2215,6 +2241,21 @@
 	net->tx_timeout = usbnet_tx_timeout;
 	net->do_ioctl = usbnet_ioctl;
 
+	// get rx/tx params from descriptors; avoid compiled-in details
+	if (!info->in || !info->out) {
+		int status = get_endpoints (dev,
+					udev->actconfig->interface + ifnum);
+		if (status < 0) {
+			err ("get_endpoints failed, %d", status);
+			kfree (dev);
+			return 0;
+		}
+	} else {
+		dev->in = usb_rcvbulkpipe (udev, info->in);
+		dev->out = usb_sndbulkpipe (udev, info->out);
+		dev->maxpacket = info->epsize;
+	}
+
 	register_netdev (&dev->net);
 	devinfo (dev, "register usbnet usb-%s-%s, %s",
 		udev->bus->bus_name, udev->devpath,
@@ -2302,32 +2343,30 @@
 },
 #endif
 
-#ifdef	CONFIG_USB_PXA
-/*
- * PXA250 or PXA210 ...  these use a "usb-eth" driver much like
- * the sa1100 one.
- */
-{
-	// Compaq "Itsy" vendor/product id, version "2.0"
-	USB_DEVICE_VER (0x049F, 0x505A, 0x0200, 0x0200),
-	.driver_info =	(unsigned long) &pxa_info,
-}, 
-#endif
-
-#ifdef	CONFIG_USB_SA1100
+#ifdef	CONFIG_USB_ARMLINUX
 /*
  * SA-1100 using standard ARM Linux kernels, or compatible.
  * Often used when talking to Linux PDAs (iPaq, Yopy, etc).
  * The sa-1100 "usb-eth" driver handles the basic framing.
+ * ARMv4.
+ *
+ * PXA2xx using usb "gadget" driver, or older "usb-eth" much like
+ * the sa1100 one. (But PXA hardware uses different endpoints.)
+ * ARMv5TE.
  */
 {
 	// 1183 = 0x049F, both used as hex values?
-	// Compaq "Itsy" vendor/product id, version "0.0"
-	USB_DEVICE_VER (0x049F, 0x505A, 0, 0),
+	// Compaq "Itsy" vendor/product id
+	// version numbers vary, along with endpoint usage
+	// but otherwise they're protocol-compatible
+	USB_DEVICE (0x049F, 0x505A),
 	.driver_info =	(unsigned long) &linuxdev_info,
 }, {
 	USB_DEVICE (0x0E7E, 0x1001),	// G.Mate "Yopy"
 	.driver_info =	(unsigned long) &yopy_info,
+}, {
+	USB_DEVICE (0x8086, 0x07d3),	// "blob" bootloader
+	.driver_info =	(unsigned long) &blob_info,
 }, 
 #endif
 
diff -urN linux-2.4.21-bk9/drivers/usb/vicam.c linux-2.4.21-bk10/drivers/usb/vicam.c
--- linux-2.4.21-bk9/drivers/usb/vicam.c	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/drivers/usb/vicam.c	2003-07-15 02:55:07.000000000 -0700
@@ -763,8 +763,8 @@
 static void
 vicam_close(struct video_device *dev)
 {
-	DBG("close\n");
 	struct vicam_camera *cam = (struct vicam_camera *) dev->priv;
+	DBG("close\n");
 
 
 	if (cam->is_removed) {
diff -urN linux-2.4.21-bk9/drivers/video/intel/Makefile linux-2.4.21-bk10/drivers/video/intel/Makefile
--- linux-2.4.21-bk9/drivers/video/intel/Makefile	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.21-bk10/drivers/video/intel/Makefile	2003-07-15 02:55:07.000000000 -0700
@@ -3,6 +3,7 @@
 
 O_TARGET := intelfb.o
 
-obj-$(CONFIG_FB_INTEL)	+= intelfbdrv.o intelfbhw.o
+obj-y	:= intelfbdrv.o intelfbhw.o
+obj-m	:= $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.21-bk9/drivers/video/intel/intelfb.h linux-2.4.21-bk10/drivers/video/intel/intelfb.h
--- linux-2.4.21-bk9/drivers/video/intel/intelfb.h	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.21-bk10/drivers/video/intel/intelfb.h	2003-07-15 02:55:07.000000000 -0700
@@ -6,7 +6,7 @@
 
 
 /*** Version/name ***/
-#define INTELFB_VERSION			"0.7.5"
+#define INTELFB_VERSION			"0.7.7"
 #define INTELFB_MODULE_NAME		"intelfb"
 #define SUPPORTED_CHIPSETS		"830M/845G/852GM/855GM/865G"
 
diff -urN linux-2.4.21-bk9/drivers/video/intel/intelfbhw.h linux-2.4.21-bk10/drivers/video/intel/intelfbhw.h
--- linux-2.4.21-bk9/drivers/video/intel/intelfbhw.h	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.21-bk10/drivers/video/intel/intelfbhw.h	2003-07-15 02:55:07.000000000 -0700
@@ -183,7 +183,7 @@
 #endif
 #define PALETTE_8_SIZE			(PALETTE_8_ENTRIES * 4)
 #define PALETTE_10_ENTRIES		128
-#define PALETTE_8_SIZE			(PALETTE_10_ENTRIES * 8)
+#define PALETTE_10_SIZE			(PALETTE_10_ENTRIES * 8)
 #define PALETTE_8_MASK			0xff
 #define PALETTE_8_RED_SHIFT		16
 #define PALETTE_8_GREEN_SHIFT		8
diff -urN linux-2.4.21-bk9/fs/dquot.c linux-2.4.21-bk10/fs/dquot.c
--- linux-2.4.21-bk9/fs/dquot.c	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/fs/dquot.c	2003-07-15 02:55:07.000000000 -0700
@@ -395,7 +395,7 @@
 		if (dquot->dq_flags & DQ_LOCKED)
 			wait_on_dquot(dquot);
 		if (dquot_dirty(dquot))
-			commit_dqblk(dquot);
+			sb->dq_op->sync_dquot(dquot);
 		dqput(dquot);
 		goto restart;
 	}
@@ -1201,10 +1201,10 @@
 	flush_warnings(transfer_to, warntype);
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		/* First we must put duplicate - otherwise we might deadlock */
-		if (transfer_to[cnt] != NODQUOT)
-			dqputduplicate(transfer_to[cnt]);
 		if (transfer_from[cnt] != NODQUOT)
-			dqput(transfer_from[cnt]);
+			dqputduplicate(transfer_from[cnt]);
+		if (transfer_to[cnt] != NODQUOT)
+			dqput(transfer_to[cnt]);
 	}
 	return ret;
 }
@@ -1219,9 +1219,16 @@
 	alloc_inode:	dquot_alloc_inode,
 	free_space:	dquot_free_space,
 	free_inode:	dquot_free_inode,
-	transfer:	dquot_transfer
+	transfer:	dquot_transfer,
+	sync_dquot:	commit_dqblk
 };
 
+/* Function used by filesystems for initializing the dquot_operations structure */
+void init_dquot_operations(struct dquot_operations *fsdqops)
+{
+	memcpy(fsdqops, &dquot_operations, sizeof(dquot_operations));
+}
+
 static inline void set_enable_flags(struct quota_info *dqopt, int type)
 {
 	switch (type) {
@@ -1517,3 +1524,4 @@
 EXPORT_SYMBOL(register_quota_format);
 EXPORT_SYMBOL(unregister_quota_format);
 EXPORT_SYMBOL(dqstats);
+EXPORT_SYMBOL(init_dquot_operations);
diff -urN linux-2.4.21-bk9/fs/ext3/super.c linux-2.4.21-bk10/fs/ext3/super.c
--- linux-2.4.21-bk9/fs/ext3/super.c	2003-06-13 07:51:37.000000000 -0700
+++ linux-2.4.21-bk10/fs/ext3/super.c	2003-07-15 02:55:07.000000000 -0700
@@ -448,6 +448,9 @@
 	return;
 }
 
+static struct dquot_operations ext3_qops;
+static int (*old_sync_dquot)(struct dquot *dquot);
+
 static struct super_operations ext3_sops = {
 	read_inode:	ext3_read_inode,	/* BKL held */
 	write_inode:	ext3_write_inode,	/* BKL not held.  Don't need */
@@ -1128,6 +1131,7 @@
 	 * set up enough so that it can read an inode
 	 */
 	sb->s_op = &ext3_sops;
+	sb->dq_op = &ext3_qops;
 	INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
 
 	sb->s_root = 0;
@@ -1758,10 +1762,65 @@
 	return 0;
 }
 
+/* Helper function for writing quotas on sync - we need to start transaction before quota file
+ * is locked for write. Otherwise the are possible deadlocks:
+ * Process 1                         Process 2
+ * ext3_create()                     quota_sync()
+ *   journal_start()                   write_dquot()
+ *   DQUOT_INIT()                        down(dqio_sem)
+ *     down(dqio_sem)                    journal_start()
+ *
+ */
+
+#ifdef CONFIG_QUOTA
+
+/* Blocks: (2 data blocks) * (3 indirect + 1 descriptor + 1 bitmap) + superblock */
+#define EXT3_OLD_QFMT_BLOCKS 11
+/* Blocks: quota info + (4 pointer blocks + 1 entry block) * (3 indirect + 1 descriptor + 1 bitmap) + superblock */
+#define EXT3_V0_QFMT_BLOCKS 27
+
+static int ext3_sync_dquot(struct dquot *dquot)
+{
+	int nblocks, ret;
+	handle_t *handle;
+	struct quota_info *dqops = sb_dqopt(dquot->dq_sb);
+	struct inode *qinode;
+
+	switch (dqops->info[dquot->dq_type].dqi_format->qf_fmt_id) {
+		case QFMT_VFS_OLD:
+			nblocks = EXT3_OLD_QFMT_BLOCKS;
+			break;
+		case QFMT_VFS_V0:
+			nblocks = EXT3_V0_QFMT_BLOCKS;
+			break;
+		default:
+			nblocks = EXT3_MAX_TRANS_DATA;
+	}
+	lock_kernel();
+	qinode = dqops->files[dquot->dq_type]->f_dentry->d_inode;
+	handle = ext3_journal_start(qinode, nblocks);
+	if (IS_ERR(handle)) {
+		unlock_kernel();
+		return PTR_ERR(handle);
+	}
+	unlock_kernel();
+	ret = old_sync_dquot(dquot);
+	lock_kernel();
+	ret = ext3_journal_stop(handle, qinode);
+	unlock_kernel();
+	return ret;
+}
+#endif
+
 static DECLARE_FSTYPE_DEV(ext3_fs_type, "ext3", ext3_read_super);
 
 static int __init init_ext3_fs(void)
 {
+#ifdef CONFIG_QUOTA
+	init_dquot_operations(&ext3_qops);
+	old_sync_dquot = ext3_qops.sync_dquot;
+	ext3_qops.sync_dquot = ext3_sync_dquot;
+#endif
         return register_filesystem(&ext3_fs_type);
 }
 
diff -urN linux-2.4.21-bk9/fs/quota_v1.c linux-2.4.21-bk10/fs/quota_v1.c
--- linux-2.4.21-bk9/fs/quota_v1.c	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/fs/quota_v1.c	2003-07-15 02:55:07.000000000 -0700
@@ -46,6 +46,7 @@
 	if (filp == (struct file *)NULL)
 		return -EINVAL;
 
+	memset(&dqblk, 0, sizeof(dqblk));	/* Initialize buffer in case file is too short */
 	/* Now we are sure filp is valid */
 	offset = v1_dqoff(dquot->dq_id);
 	fs = get_fs();
diff -urN linux-2.4.21-bk9/fs/reiserfs/prints.c linux-2.4.21-bk10/fs/reiserfs/prints.c
--- linux-2.4.21-bk9/fs/reiserfs/prints.c	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/fs/reiserfs/prints.c	2003-07-15 02:55:07.000000000 -0700
@@ -159,7 +159,7 @@
 
   *skip = 0;
   
-  while ((k = strstr (k, "%")) != NULL)
+  while ((k = strchr (k, '%')) != NULL)
   {
     if (k[1] == 'k' || k[1] == 'K' || k[1] == 'h' || k[1] == 't' ||
 	      k[1] == 'z' || k[1] == 'b' || k[1] == 'y' || k[1] == 'a' ) {
diff -urN linux-2.4.21-bk9/include/asm-i386/processor.h linux-2.4.21-bk10/include/asm-i386/processor.h
--- linux-2.4.21-bk9/include/asm-i386/processor.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/include/asm-i386/processor.h	2003-07-15 02:55:07.000000000 -0700
@@ -468,7 +468,7 @@
 /* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */
 static inline void rep_nop(void)
 {
-	__asm__ __volatile__("rep;nop");
+	__asm__ __volatile__("rep;nop" ::: "memory");
 }
 
 #define cpu_relax()	rep_nop()
diff -urN linux-2.4.21-bk9/include/asm-m68k/kmap_types.h linux-2.4.21-bk10/include/asm-m68k/kmap_types.h
--- linux-2.4.21-bk9/include/asm-m68k/kmap_types.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/include/asm-m68k/kmap_types.h	2003-07-15 02:55:07.000000000 -0700
@@ -0,0 +1,18 @@
+#ifdef __KERNEL__
+#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
+#endif /* __KERNEL__ */
diff -urN linux-2.4.21-bk9/include/asm-s390/qdio.h linux-2.4.21-bk10/include/asm-s390/qdio.h
--- linux-2.4.21-bk9/include/asm-s390/qdio.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/include/asm-s390/qdio.h	2003-07-15 02:55:07.000000000 -0700
@@ -0,0 +1,883 @@
+/*
+ * linux/include/asm/qdio.h
+ *
+ * Linux for S/390 QDIO base support, Hipersocket base support
+ * version 2
+ *
+ * Copyright 2000,2002 IBM Corporation
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ *
+ */
+#ifndef __QDIO_H__
+#define __QDIO_H__
+
+#define VERSION_QDIO_H "$Revision: 1.66 $"
+
+/* note, that most of the typedef's are from ingo. */
+
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+
+//#define QDIO_DBF_LIKE_HELL
+
+#define QDIO_NAME "qdio "
+
+#define QDIO_VERBOSE_LEVEL 9
+
+#ifndef CONFIG_ARCH_S390X
+#define QDIO_32_BIT
+#endif /* CONFIG_ARCH_S390X */
+
+#define QDIO_USE_PROCESSING_STATE
+
+#ifdef CONFIG_QDIO_PERF_STATS
+#define QDIO_PERFORMANCE_STATS
+#endif /* CONFIG_QDIO_PERF_STATS */
+
+/**** CONSTANTS, that are relied on without using these symbols *****/
+#define QDIO_MAX_QUEUES_PER_IRQ 32 /* used in width of unsigned int */
+/************************ END of CONSTANTS **************************/
+#define QDIO_MAX_BUFFERS_PER_Q 128 /* must be a power of 2 (%x=&(x-1)*/
+#define QDIO_BUF_ORDER 7 /* 2**this == number of pages used for sbals in 1 q */
+#define QDIO_MAX_ELEMENTS_PER_BUFFER 16
+#define SBAL_SIZE 256
+
+#define IQDIO_FILL_LEVEL_TO_POLL (QDIO_MAX_BUFFERS_PER_Q*4/3)
+
+#define TIQDIO_THININT_ISC 3
+#define TIQDIO_DELAY_TARGET 0
+#define QDIO_BUSY_BIT_PATIENCE 2000 /* in microsecs */
+#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
+#define IQDIO_LOCAL_LAPS_INT 1
+#define IQDIO_GLOBAL_SUMMARY_CC_MASK 2
+/*#define IQDIO_IQDC_INT_PARM 0x1234*/
+
+#define QDIO_Q_LAPS 5
+
+#define QDIO_STORAGE_ACC_KEY get_storage_key()
+
+#define L2_CACHELINE_SIZE 256
+#define INDICATORS_PER_CACHELINE (L2_CACHELINE_SIZE/sizeof(__u32))
+
+#define QDIO_PERF "qdio_perf"
+
+/* must be a power of 2 */
+/*#define QDIO_STATS_NUMBER 4
+
+#define QDIO_STATS_CLASSES 2
+#define QDIO_STATS_COUNT_NEEDED 2*/
+
+#define QDIO_ACTIVATE_DELAY 5 /* according to brenton belmar and paul
+				 gioquindo it can take up to 5ms before
+				 queues are really active */
+
+#define QDIO_NO_USE_COUNT_TIME 10
+#define QDIO_NO_USE_COUNT_TIMEOUT 1000 /* wait for 1 sec on each q before
+					  exiting without having use_count
+					  of the queue to 0 */
+
+#define QDIO_ESTABLISH_TIMEOUT 1000
+#define QDIO_ACTIVATE_TIMEOUT 100
+#define QDIO_CLEANUP_CLEAR_TIMEOUT 20000
+#define QDIO_CLEANUP_HALT_TIMEOUT 10000
+
+#define QDIO_BH AURORA_BH
+
+#define QDIO_IRQ_BUCKETS 256 /* heavy..., but does only use a few bytes, but
+			      be rather faster in cases of collisions
+			      (if there really is a collision, it is
+			      on every (traditional) interrupt and every
+			      do_QDIO, so we rather are generous */
+#define QDIO_QETH_QFMT 0
+#define QDIO_ZFCP_QFMT 1
+#define QDIO_IQDIO_QFMT 2
+
+#define QDIO_IRQ_STATE_FRESH 0 /* must be 0 -> memset has set it to 0 */
+#define QDIO_IRQ_STATE_INACTIVE 1
+#define QDIO_IRQ_STATE_ESTABLISHED 2
+#define QDIO_IRQ_STATE_ACTIVE 3
+#define QDIO_IRQ_STATE_STOPPED 4
+
+/* used as intparm in do_IO: */
+#define QDIO_DOING_SENSEID 0
+#define QDIO_DOING_ESTABLISH 1
+#define QDIO_DOING_ACTIVATE 2
+#define QDIO_DOING_CLEANUP 3
+
+/************************* DEBUG FACILITY STUFF *********************/
+/* #define QDIO_DBF_LIKE_HELL */
+
+#define QDIO_DBF_HEX(ex,name,level,addr,len) \
+	do { \
+	if (ex) \
+		debug_exception(qdio_dbf_##name,level,(void*)(addr),len); \
+	else \
+		debug_event(qdio_dbf_##name,level,(void*)(addr),len); \
+	} while (0)
+#define QDIO_DBF_TEXT(ex,name,level,text) \
+	do { \
+	if (ex) \
+		debug_text_exception(qdio_dbf_##name,level,text); \
+	else \
+		debug_text_event(qdio_dbf_##name,level,text); \
+	} while (0)
+
+#define QDIO_DBF_HEX0(ex,name,addr,len) QDIO_DBF_HEX(ex,name,0,addr,len)
+#define QDIO_DBF_HEX1(ex,name,addr,len) QDIO_DBF_HEX(ex,name,1,addr,len)
+#define QDIO_DBF_HEX2(ex,name,addr,len) QDIO_DBF_HEX(ex,name,2,addr,len)
+#define QDIO_DBF_HEX3(ex,name,addr,len) QDIO_DBF_HEX(ex,name,3,addr,len)
+#define QDIO_DBF_HEX4(ex,name,addr,len) QDIO_DBF_HEX(ex,name,4,addr,len)
+#define QDIO_DBF_HEX5(ex,name,addr,len) QDIO_DBF_HEX(ex,name,5,addr,len)
+#define QDIO_DBF_HEX6(ex,name,addr,len) QDIO_DBF_HEX(ex,name,6,addr,len)
+#ifdef QDIO_DBF_LIKE_HELL
+#endif /* QDIO_DBF_LIKE_HELL */
+#if 0
+#define QDIO_DBF_HEX0(ex,name,addr,len) do {} while (0)
+#define QDIO_DBF_HEX1(ex,name,addr,len) do {} while (0)
+#define QDIO_DBF_HEX2(ex,name,addr,len) do {} while (0)
+#ifndef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_HEX3(ex,name,addr,len) do {} while (0)
+#define QDIO_DBF_HEX4(ex,name,addr,len) do {} while (0)
+#define QDIO_DBF_HEX5(ex,name,addr,len) do {} while (0)
+#define QDIO_DBF_HEX6(ex,name,addr,len) do {} while (0)
+#endif /* QDIO_DBF_LIKE_HELL */
+#endif /* 0 */
+
+#define QDIO_DBF_TEXT0(ex,name,text) QDIO_DBF_TEXT(ex,name,0,text)
+#define QDIO_DBF_TEXT1(ex,name,text) QDIO_DBF_TEXT(ex,name,1,text)
+#define QDIO_DBF_TEXT2(ex,name,text) QDIO_DBF_TEXT(ex,name,2,text)
+#define QDIO_DBF_TEXT3(ex,name,text) QDIO_DBF_TEXT(ex,name,3,text)
+#define QDIO_DBF_TEXT4(ex,name,text) QDIO_DBF_TEXT(ex,name,4,text)
+#define QDIO_DBF_TEXT5(ex,name,text) QDIO_DBF_TEXT(ex,name,5,text)
+#define QDIO_DBF_TEXT6(ex,name,text) QDIO_DBF_TEXT(ex,name,6,text)
+#ifdef QDIO_DBF_LIKE_HELL
+#endif /* QDIO_DBF_LIKE_HELL */
+#if 0
+#define QDIO_DBF_TEXT0(ex,name,text) do {} while (0)
+#define QDIO_DBF_TEXT1(ex,name,text) do {} while (0)
+#define QDIO_DBF_TEXT2(ex,name,text) do {} while (0)
+#ifndef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_TEXT3(ex,name,text) do {} while (0)
+#define QDIO_DBF_TEXT4(ex,name,text) do {} while (0)
+#define QDIO_DBF_TEXT5(ex,name,text) do {} while (0)
+#define QDIO_DBF_TEXT6(ex,name,text) do {} while (0)
+#endif /* QDIO_DBF_LIKE_HELL */
+#endif /* 0 */
+
+#define QDIO_DBF_SETUP_NAME "qdio_setup"
+#define QDIO_DBF_SETUP_LEN 8
+#define QDIO_DBF_SETUP_INDEX 2
+#define QDIO_DBF_SETUP_NR_AREAS 1
+#ifdef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_SETUP_LEVEL 6
+#else /* QDIO_DBF_LIKE_HELL */
+#define QDIO_DBF_SETUP_LEVEL 2
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#define QDIO_DBF_SBAL_NAME "qdio_labs" /* sbal */
+#define QDIO_DBF_SBAL_LEN 256
+#define QDIO_DBF_SBAL_INDEX 2
+#define QDIO_DBF_SBAL_NR_AREAS 2
+#ifdef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_SBAL_LEVEL 6
+#else /* QDIO_DBF_LIKE_HELL */
+#define QDIO_DBF_SBAL_LEVEL 2
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#define QDIO_DBF_TRACE_NAME "qdio_trace"
+#define QDIO_DBF_TRACE_LEN 8
+#define QDIO_DBF_TRACE_NR_AREAS 2
+#ifdef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_TRACE_INDEX 4
+#define QDIO_DBF_TRACE_LEVEL 4 /* -------- could be even more verbose here */
+#else /* QDIO_DBF_LIKE_HELL */
+#define QDIO_DBF_TRACE_INDEX 2
+#define QDIO_DBF_TRACE_LEVEL 2
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#define QDIO_DBF_SENSE_NAME "qdio_sense"
+#define QDIO_DBF_SENSE_LEN 64
+#define QDIO_DBF_SENSE_INDEX 1
+#define QDIO_DBF_SENSE_NR_AREAS 1
+#ifdef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_SENSE_LEVEL 6
+#else /* QDIO_DBF_LIKE_HELL */
+#define QDIO_DBF_SENSE_LEVEL 2
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_DBF_LIKE_HELL
+#define QDIO_TRACE_QTYPE QDIO_ZFCP_QFMT
+
+#define QDIO_DBF_SLSB_OUT_NAME "qdio_slsb_out"
+#define QDIO_DBF_SLSB_OUT_LEN QDIO_MAX_BUFFERS_PER_Q
+#define QDIO_DBF_SLSB_OUT_INDEX 8
+#define QDIO_DBF_SLSB_OUT_NR_AREAS 1
+#define QDIO_DBF_SLSB_OUT_LEVEL 6
+
+#define QDIO_DBF_SLSB_IN_NAME "qdio_slsb_in"
+#define QDIO_DBF_SLSB_IN_LEN QDIO_MAX_BUFFERS_PER_Q
+#define QDIO_DBF_SLSB_IN_INDEX 8
+#define QDIO_DBF_SLSB_IN_NR_AREAS 1
+#define QDIO_DBF_SLSB_IN_LEVEL 6
+#endif /* QDIO_DBF_LIKE_HELL */
+
+/****************** END OF DEBUG FACILITY STUFF *********************/
+
+typedef struct qdio_buffer_element_t {
+	unsigned int flags;
+	unsigned int length;
+#ifdef QDIO_32_BIT
+	void *reserved;
+#endif /* QDIO_32_BIT */
+	void *addr;
+} __attribute__ ((packed,aligned(16))) qdio_buffer_element_t;
+
+typedef struct qdio_buffer_t {
+	volatile qdio_buffer_element_t element[16];
+} __attribute__ ((packed,aligned(256))) qdio_buffer_t;
+
+
+/* params are: irq, status, qdio_error, siga_error,
+   queue_number, first element processed, number of elements processed,
+   int_parm */
+typedef void qdio_handler_t(int,unsigned int,unsigned int,unsigned int,
+			    unsigned int,int,int,unsigned long);
+
+
+#define QDIO_STATUS_INBOUND_INT 0x01
+#define QDIO_STATUS_OUTBOUND_INT 0x02
+#define QDIO_STATUS_LOOK_FOR_ERROR 0x04
+#define QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR 0x08
+#define QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR 0x10
+#define QDIO_STATUS_ACTIVATE_CHECK_CONDITION 0x20
+
+#define QDIO_SIGA_ERROR_ACCESS_EXCEPTION 0x10
+#define QDIO_SIGA_ERROR_B_BIT_SET 0x20
+
+/* for qdio_initialize */
+#define QDIO_INBOUND_0COPY_SBALS 0x01
+#define QDIO_OUTBOUND_0COPY_SBALS 0x02
+#define QDIO_USE_OUTBOUND_PCIS 0x04
+#define QDIO_PFIX 0x08
+
+/* for qdio_cleanup */
+#define QDIO_FLAG_CLEANUP_USING_CLEAR 0x01
+#define QDIO_FLAG_CLEANUP_USING_HALT 0x02
+
+typedef struct qdio_initialize_t {
+	int irq;
+	unsigned char q_format;
+	unsigned char adapter_name[8];
+       	unsigned int qib_param_field_format; /*adapter dependent*/
+	/* pointer to 128 bytes or NULL, if no param field */
+	unsigned char *qib_param_field; /* adapter dependent */
+	/* pointer to no_queues*128 words of data or NULL */
+	unsigned long *input_slib_elements;
+	unsigned long *output_slib_elements;
+	unsigned int min_input_threshold;
+	unsigned int max_input_threshold;
+	unsigned int min_output_threshold;
+	unsigned int max_output_threshold;
+	unsigned int no_input_qs;
+	unsigned int no_output_qs;
+	qdio_handler_t *input_handler;
+	qdio_handler_t *output_handler;
+	unsigned long int_parm;
+	unsigned long flags;
+	void **input_sbal_addr_array; /* addr of n*128 void ptrs */
+	void **output_sbal_addr_array; /* addr of n*128 void ptrs */
+} qdio_initialize_t;
+extern int qdio_initialize(qdio_initialize_t *init_data);
+
+extern int qdio_activate(int irq,int flags);
+
+#define QDIO_STATE_MUST_USE_OUTB_PCI	0x00000001
+#define QDIO_STATE_INACTIVE 		0x00000002 /* after qdio_cleanup */
+#define QDIO_STATE_ESTABLISHED 		0x00000004 /* after qdio_initialize */
+#define QDIO_STATE_ACTIVE 		0x00000008 /* after qdio_activate */
+#define QDIO_STATE_STOPPED 		0x00000010 /* after queues went down */
+extern unsigned long qdio_get_status(int irq);
+
+
+#define QDIO_FLAG_SYNC_INPUT     0x01
+#define QDIO_FLAG_SYNC_OUTPUT    0x02
+#define QDIO_FLAG_UNDER_INTERRUPT 0x04
+#define QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT 0x08 /* no effect on
+						     adapter interrupts */
+#define QDIO_FLAG_DONT_SIGA 0x10
+
+extern int do_QDIO(int irq,unsigned int flags, unsigned int queue_number,
+		   unsigned int qidx,unsigned int count,
+		   qdio_buffer_t *buffers);
+
+extern int qdio_synchronize(int irq,unsigned int flags,
+			    unsigned int queue_number);
+
+extern int qdio_cleanup(int irq,int how);
+
+extern unsigned char qdio_get_slsb_state(int irq,unsigned int flag,
+				  unsigned int queue_number,
+				  unsigned int qidx);
+
+/*
+ * QDIO device commands returned by extended Sense-ID
+ */
+#define DEFAULT_ESTABLISH_QS_CMD 0x1b
+#define DEFAULT_ESTABLISH_QS_COUNT 0x1000
+#define DEFAULT_ACTIVATE_QS_CMD 0x1f
+#define DEFAULT_ACTIVATE_QS_COUNT 0
+typedef struct _qdio_cmds {
+	unsigned char rcd;            /* read configuration data */
+	unsigned short count_rcd;
+	unsigned char sii;            /* set interface identifier */
+	unsigned short count_sii;
+	unsigned char rni;            /* read node identifier */
+	unsigned short count_rni;
+	unsigned char eq;             /* establish QDIO queues */
+	unsigned short count_eq;
+	unsigned char aq;             /* activate QDIO queues */
+	unsigned short count_aq;
+} qdio_cmds_t;
+
+/*
+ * additional CIWs returned by extended Sense-ID
+ */
+#define CIW_TYPE_EQUEUE 0x3       /* establish QDIO queues */
+#define CIW_TYPE_AQUEUE 0x4       /* activate QDIO queues */
+
+typedef struct _qdesfmt0 {
+#ifdef QDIO_32_BIT
+	unsigned long res1;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long sliba;            /* storage-list-information-block
+					   address */
+#ifdef QDIO_32_BIT
+	unsigned long res2;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long sla;              /* storage-list address */
+#ifdef QDIO_32_BIT
+	unsigned long res3;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long slsba;            /* storage-list-state-block address */
+	unsigned int  res4;		/* reserved */
+	unsigned int  akey  :  4;       /* access key for DLIB */
+	unsigned int  bkey  :  4;       /* access key for SL */
+	unsigned int  ckey  :  4;       /* access key for SBALs */
+	unsigned int  dkey  :  4;       /* access key for SLSB */
+	unsigned int  res5  : 16;       /* reserved */
+} __attribute__ ((packed)) qdesfmt0_t;
+
+/*
+ * Queue-Description record (QDR)
+ */
+typedef struct _qdr {
+	unsigned int  qfmt    :  8;     /* queue format */
+	unsigned int  pfmt    :  8;     /* impl. dep. parameter format */
+	unsigned int  res1    :  8;     /* reserved */
+	unsigned int  ac      :  8;     /* adapter characteristics */
+	unsigned int  res2    :  8;     /* reserved */
+	unsigned int  iqdcnt  :  8;     /* input-queue-descriptor count */
+	unsigned int  res3    :  8;     /* reserved */
+	unsigned int  oqdcnt  :  8;     /* output-queue-descriptor count */
+	unsigned int  res4    :  8;     /* reserved */
+	unsigned int  iqdsz   :  8;     /* input-queue-descriptor size */
+	unsigned int  res5    :  8;     /* reserved */
+	unsigned int  oqdsz   :  8;     /* output-queue-descriptor size */
+	unsigned int  res6[9];          /* reserved */
+#ifdef QDIO_32_BIT
+	unsigned long res7;		/* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long qiba;             /* queue-information-block address */
+	unsigned int  res8;             /* reserved */
+	unsigned int  qkey    :  4;     /* queue-informatio-block key */
+	unsigned int  res9    : 28;     /* reserved */
+/*	union _qd {*/ /* why this? */
+		qdesfmt0_t qdf0[126];
+/*	} qd;*/
+} __attribute__ ((packed,aligned(4096))) qdr_t;
+
+
+/*
+ * queue information block (QIB)
+ */
+#define QIB_AC_INBOUND_PCI_SUPPORTED 0x80
+#define QIB_AC_OUTBOUND_PCI_SUPPORTED 0x40
+typedef struct _qib {
+	unsigned int  qfmt    :  8;     /* queue format */
+	unsigned int  pfmt    :  8;     /* impl. dep. parameter format */
+	unsigned int  res1    :  8;     /* reserved */
+	unsigned int  ac      :  8;     /* adapter characteristics */
+	unsigned int  res2;             /* reserved */
+#ifdef QDIO_32_BIT
+	unsigned long res3;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long isliba;           /* absolute address of 1st
+					   input SLIB */
+#ifdef QDIO_32_BIT
+	unsigned long res4;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long osliba;           /* absolute address of 1st
+					   output SLIB */
+	unsigned int  res5;             /* reserved */
+	unsigned int  res6;             /* reserved */
+	unsigned char ebcnam[8];        /* adapter identifier in EBCDIC */
+	unsigned char res7[88];         /* reserved */
+	unsigned char parm[QDIO_MAX_BUFFERS_PER_Q];
+					/* implementation dependent
+					   parameters */
+} __attribute__ ((packed,aligned(256))) qib_t;
+
+
+/*
+ * storage-list-information block element (SLIBE)
+ */
+typedef struct _slibe {
+#ifdef QDIO_32_BIT
+	unsigned long res;              /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long parms;            /* implementation dependent
+					   parameters */
+} slibe_t;
+
+/*
+ * storage-list-information block (SLIB)
+ */
+typedef struct _slib {
+#ifdef QDIO_32_BIT
+	unsigned long res1;             /* reserved */
+#endif /* QDIO_32_BIT */
+        unsigned long nsliba;           /* next SLIB address (if any) */
+#ifdef QDIO_32_BIT
+	unsigned long res2;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long sla;              /* SL address */
+#ifdef QDIO_32_BIT
+	unsigned long res3;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long slsba;            /* SLSB address */
+	unsigned char res4[1000];       /* reserved */
+	slibe_t       slibe[QDIO_MAX_BUFFERS_PER_Q];    /* SLIB elements */
+} __attribute__ ((packed,aligned(2048))) slib_t;
+
+typedef struct _sbal_flags {
+	unsigned char res1  : 1;   /* reserved */
+	unsigned char last  : 1;   /* last entry */
+	unsigned char cont  : 1;   /* contiguous storage */
+	unsigned char res2  : 1;   /* reserved */
+	unsigned char frag  : 2;   /* fragmentation (s.below) */
+	unsigned char res3  : 2;   /* reserved */
+} __attribute__ ((packed)) sbal_flags_t;
+
+#define SBAL_FLAGS_FIRST_FRAG	     0x04000000UL
+#define SBAL_FLAGS_MIDDLE_FRAG	     0x08000000UL
+#define SBAL_FLAGS_LAST_FRAG	     0x0c000000UL
+#define SBAL_FLAGS_LAST_ENTRY	     0x40000000UL
+#define SBAL_FLAGS_CONTIGUOUS	     0x20000000UL
+
+#define SBAL_FLAGS0_DATA_CONTINUATION 0x20UL
+
+/* Awesome FCP extensions */
+#define SBAL_FLAGS0_TYPE_STATUS       0x00UL
+#define SBAL_FLAGS0_TYPE_WRITE        0x08UL
+#define SBAL_FLAGS0_TYPE_READ         0x10UL
+#define SBAL_FLAGS0_TYPE_WRITE_READ   0x18UL
+#define SBAL_FLAGS0_MORE_SBALS	      0x04UL
+#define SBAL_FLAGS0_COMMAND           0x02UL
+#define SBAL_FLAGS0_LAST_SBAL         0x00UL
+#define SBAL_FLAGS0_ONLY_SBAL         SBAL_FLAGS0_COMMAND
+#define SBAL_FLAGS0_MIDDLE_SBAL       SBAL_FLAGS0_MORE_SBALS
+#define SBAL_FLAGS0_FIRST_SBAL        SBAL_FLAGS0_MORE_SBALS | SBAL_FLAGS0_COMMAND
+/* Naught of interest beyond this point */
+
+#define SBAL_FLAGS0_PCI		0x40
+typedef struct _sbal_sbalf_0 {
+	unsigned char res1  : 1;   /* reserved */
+	unsigned char pci   : 1;   /* PCI indicator */
+	unsigned char cont  : 1;   /* data continuation */
+	unsigned char sbtype: 2;   /* storage-block type (FCP) */
+	unsigned char res2  : 3;   /* reserved */
+} __attribute__ ((packed)) sbal_sbalf_0_t;
+
+typedef struct _sbal_sbalf_1 {
+	unsigned char res1  : 4;   /* reserved */
+	unsigned char key   : 4;   /* storage key */
+} __attribute__ ((packed)) sbal_sbalf_1_t;
+
+typedef struct _sbal_sbalf_14 {
+	unsigned char res1   : 4;  /* reserved */
+	unsigned char erridx : 4;  /* error index */
+} __attribute__ ((packed)) sbal_sbalf_14_t;
+
+typedef struct _sbal_sbalf_15 {
+	unsigned char reason;      /* reserved */
+} __attribute__ ((packed)) sbal_sbalf_15_t;
+
+typedef union _sbal_sbalf {
+	sbal_sbalf_0_t  i0;
+	sbal_sbalf_1_t  i1;
+	sbal_sbalf_14_t i14;
+	sbal_sbalf_15_t i15;
+	unsigned char value;
+} sbal_sbalf_t;
+
+typedef struct _sbale {
+	union {
+		sbal_flags_t  bits;       /* flags */
+		unsigned char value;
+	} flags;
+	unsigned int  res1  : 16;   /* reserved */
+	sbal_sbalf_t  sbalf;       /* SBAL flags */
+	unsigned int  res2  : 16;  /* reserved */
+	unsigned int  count : 16;  /* data count */
+#ifdef QDIO_32_BIT
+	unsigned long res3;        /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long addr;        /* absolute data address */
+} __attribute__ ((packed,aligned(16))) sbal_element_t;
+
+/*
+ * strorage-block access-list (SBAL)
+ */
+typedef struct _sbal {
+	sbal_element_t element[QDIO_MAX_ELEMENTS_PER_BUFFER];
+} __attribute__ ((packed,aligned(256))) sbal_t;
+
+/*
+ * storage-list (SL)
+ */
+typedef struct _sl_element {
+#ifdef QDIO_32_BIT
+        unsigned long res;     /* reserved */
+#endif /* QDIO_32_BIT */
+        unsigned long sbal;    /* absolute SBAL address */
+} __attribute__ ((packed)) sl_element_t;
+
+typedef struct _sl {
+	sl_element_t element[QDIO_MAX_BUFFERS_PER_Q];
+} __attribute__ ((packed,aligned(1024))) sl_t;
+
+/*
+ * storage-list-state block (SLSB)
+ */
+/*typedef struct _slsb_val {*/
+/*	unsigned char value;       */ /* SLSB entry as a single byte value */
+/*} __attribute__ ((packed)) slsb_val_t;*/
+
+typedef struct _slsb_flags {
+	unsigned char owner  : 2;   /* SBAL owner */
+	unsigned char type   : 1;   /* buffer type */
+	unsigned char state  : 5;   /* processing state */
+} __attribute__ ((packed)) slsb_flags_t;
+
+
+typedef struct _slsb {
+	union _acc {
+		unsigned char val[QDIO_MAX_BUFFERS_PER_Q];
+		slsb_flags_t flags[QDIO_MAX_BUFFERS_PER_Q];
+	} acc;
+} __attribute__ ((packed,aligned(256))) slsb_t;
+
+/*
+ * SLSB values
+ */
+#define SLSB_OWNER_PROG              1
+#define SLSB_OWNER_CU                2
+
+#define SLSB_TYPE_INPUT              0
+#define SLSB_TYPE_OUTPUT             1
+
+#define SLSB_STATE_NOT_INIT          0
+#define SLSB_STATE_EMPTY             1
+#define SLSB_STATE_PRIMED            2
+#define SLSB_STATE_HALTED          0xe
+#define SLSB_STATE_ERROR           0xf
+
+#define SLSB_P_INPUT_NOT_INIT     0x80
+#define SLSB_P_INPUT_PROCESSING	  0x81
+#define SLSB_CU_INPUT_EMPTY       0x41
+#define SLSB_P_INPUT_PRIMED       0x82
+#define SLSB_P_INPUT_HALTED       0x8E
+#define SLSB_P_INPUT_ERROR        0x8F
+
+#define SLSB_P_OUTPUT_NOT_INIT    0xA0
+#define SLSB_P_OUTPUT_EMPTY       0xA1
+#define SLSB_CU_OUTPUT_PRIMED     0x62
+#define SLSB_P_OUTPUT_HALTED      0xAE
+#define SLSB_P_OUTPUT_ERROR       0xAF
+
+#define SLSB_ERROR_DURING_LOOKUP  0xFF
+
+typedef struct qdio_q_t {
+	volatile slsb_t slsb;
+
+	__u32 * volatile dev_st_chg_ind;
+
+	int is_input_q;
+	int is_0copy_sbals_q;
+
+	unsigned int is_iqdio_q;
+	unsigned int is_thinint_q;
+
+	/* bit 0 means queue 0, bit 1 means queue 1, ... */
+	unsigned int mask;
+	unsigned int q_no;
+
+	qdio_handler_t (*handler);
+
+	/* points to the next buffer to be checked for having
+	 * been processed by the card (outbound)
+	 * or to the next buffer the program should check for (inbound) */
+	volatile int first_to_check;
+	/* and the last time it was: */
+	volatile int last_move_ftc;
+
+	atomic_t number_of_buffers_used;
+	atomic_t polling;
+
+	unsigned int siga_in;
+	unsigned int siga_out;
+	unsigned int siga_sync;
+	unsigned int siga_sync_done_on_thinints;
+	unsigned int siga_sync_done_on_outb_tis;
+	unsigned int hydra_gives_outbound_pcis;
+
+	/* used to save beginning position when calling dd_handlers */
+	int first_element_to_kick;
+
+	atomic_t use_count;
+	atomic_t is_in_shutdown;
+
+	int irq;
+	void *irq_ptr;
+
+#ifdef QDIO_USE_TIMERS_FOR_POLLING
+	struct timer_list timer;
+	atomic_t timer_already_set;
+	spinlock_t timer_lock;
+#else /* QDIO_USE_TIMERS_FOR_POLLING */
+	struct tasklet_struct tasklet;
+#endif /* QDIO_USE_TIMERS_FOR_POLLING */
+
+	unsigned int state;
+
+	/* used to store the error condition during a data transfer */
+	unsigned int qdio_error;
+	unsigned int siga_error;
+	unsigned int error_status_flags;
+
+	/* list of interesting queues */
+	volatile struct qdio_q_t *list_next;
+	volatile struct qdio_q_t *list_prev;
+
+	slib_t *slib; /* a page is allocated under this pointer,
+			 sl points into this page, offset PAGE_SIZE/2
+			 (after slib) */
+	sl_t *sl;
+	volatile sbal_t *sbal[QDIO_MAX_BUFFERS_PER_Q];
+
+	qdio_buffer_t *qdio_buffers[QDIO_MAX_BUFFERS_PER_Q];
+
+	unsigned long int_parm;
+
+	/*struct {
+		int in_bh_check_limit;
+		int threshold;
+	} threshold_classes[QDIO_STATS_CLASSES];*/
+
+	struct {
+		/* inbound: the time to stop polling
+		   outbound: the time to kick peer */
+		int threshold; /* the real value */
+
+		/* outbound: last time of do_QDIO
+		   inbound: last time of noticing incoming data */
+		/*__u64 last_transfer_times[QDIO_STATS_NUMBER];
+		int last_transfer_index; */
+
+		__u64 last_transfer_time;
+	} timing;
+        unsigned int queue_type;
+} __attribute__ ((aligned(256))) qdio_q_t;
+
+typedef struct qdio_irq_t {
+	__u32 * volatile dev_st_chg_ind;
+
+	unsigned long int_parm;
+	int irq;
+
+	unsigned int is_iqdio_irq;
+	unsigned int is_thinint_irq;
+	unsigned int hydra_gives_outbound_pcis;
+	unsigned int sync_done_on_outb_pcis;
+
+	unsigned int state;
+	spinlock_t setting_up_lock;
+
+	unsigned int no_input_qs;
+	unsigned int no_output_qs;
+
+	unsigned char qdioac;
+
+	qdio_q_t *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
+	qdio_q_t *output_qs[QDIO_MAX_QUEUES_PER_IRQ];
+
+	ccw1_t ccw;
+	int io_result_cstat;
+	int io_result_dstat;
+	int io_result_flags;
+	atomic_t interrupt_has_arrived;
+	atomic_t interrupt_has_been_cleaned;
+	wait_queue_head_t wait_q;
+
+	qdr_t *qdr;
+
+	qdio_cmds_t commands;
+
+	qib_t qib;
+
+	io_handler_func_t original_int_handler;
+
+	unsigned long other_flags; /* e.g. QDIO_PFIX */
+
+	struct qdio_irq_t *next;
+} qdio_irq_t;
+
+#define QDIO_CHSC_RESPONSE_CODE_OK 1
+/* flags for st qdio sch data */
+#define CHSC_FLAG_QDIO_CAPABILITY 0x80
+#define CHSC_FLAG_VALIDITY 0x40
+
+#define CHSC_FLAG_SIGA_INPUT_NECESSARY 0x40
+#define CHSC_FLAG_SIGA_OUTPUT_NECESSARY 0x20
+#define CHSC_FLAG_SIGA_SYNC_NECESSARY 0x10
+#define CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS 0x08
+#define CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS 0x04
+
+typedef struct qdio_chsc_area_t {
+	struct {
+		/* word 0 */
+		__u16 command_code1;
+		__u16 command_code2;
+		/* word 1 */
+		__u16 operation_code;
+		__u16 first_sch;
+		/* word 2 */
+		__u8 reserved1;
+		__u8 image_id;
+		__u16 last_sch;
+		/* word 3 */
+		__u32 reserved2;
+
+		/* word 4 */
+		union {
+			struct {
+				/* word 4&5 */
+				__u64 summary_indicator_addr;
+				/* word 6&7 */
+				__u64 subchannel_indicator_addr;
+				/* word 8 */
+				int ks:4;
+				int kc:4;
+				int reserved1:21;
+				int isc:3;
+				/* word 9&10 */
+				__u32 reserved2[2];
+				/* word 11 */
+				__u32 subsystem_id;
+				/* word 12-1015 */
+				__u32 reserved3[1004];
+			} __attribute__ ((packed,aligned(4))) set_chsc;
+			struct {
+				/* word 4&5 */
+				__u32 reserved1[2];	
+				/* word 6 */
+				__u32 delay_target;
+				/* word 7-1015 */
+				__u32 reserved4[1009];
+			} __attribute__ ((packed,aligned(4))) set_chsc_fast;
+			struct {
+				/* word 0 */
+				__u16 length;
+				__u16 response_code;
+				/* word 1 */
+				__u32 reserved1;
+				/* words 2 to 9 for st sch qdio data */
+				__u8 flags;
+				__u8 reserved2;
+				__u16 sch;
+				__u8 qfmt;
+				__u8 reserved3;
+				__u8 qdioac;
+				__u8 sch_class;
+				__u8 reserved4;
+				__u8 icnt;
+				__u8 reserved5;
+				__u8 ocnt;
+				/* plus 5 words of reserved fields */
+			} __attribute__ ((packed,aligned(8)))
+			store_qdio_data_response;
+		} operation_data_area;
+	} __attribute__ ((packed,aligned(8))) request_block;
+	struct {
+		/* word 0 */
+		__u16 length;
+		__u16 response_code;
+		/* word 1 */
+		__u32 reserved1;
+	} __attribute__ ((packed,aligned(8))) response_block;
+} __attribute__ ((packed,aligned(PAGE_SIZE))) qdio_chsc_area_t;
+
+
+#define QDIO_PRINTK_HEADER QDIO_NAME ": "
+
+#if QDIO_VERBOSE_LEVEL>8
+#define QDIO_PRINT_STUPID(x...) printk( KERN_DEBUG QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_STUPID(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>7
+#define QDIO_PRINT_ALL(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_ALL(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>6
+#define QDIO_PRINT_INFO(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_INFO(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>5
+#define QDIO_PRINT_WARN(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_WARN(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>4
+#define QDIO_PRINT_ERR(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_ERR(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>3
+#define QDIO_PRINT_CRIT(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_CRIT(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>2
+#define QDIO_PRINT_ALERT(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_ALERT(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>1
+#define QDIO_PRINT_EMERG(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_EMERG(x...)
+#endif
+
+#endif /* __QDIO_H__ */
diff -urN linux-2.4.21-bk9/include/asm-s390x/qdio.h linux-2.4.21-bk10/include/asm-s390x/qdio.h
--- linux-2.4.21-bk9/include/asm-s390x/qdio.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/include/asm-s390x/qdio.h	2003-07-15 02:55:07.000000000 -0700
@@ -0,0 +1,883 @@
+/*
+ * linux/include/asm/qdio.h
+ *
+ * Linux for S/390 QDIO base support, Hipersocket base support
+ * version 2
+ *
+ * Copyright 2000,2002 IBM Corporation
+ * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
+ *
+ */
+#ifndef __QDIO_H__
+#define __QDIO_H__
+
+#define VERSION_QDIO_H "$Revision: 1.57 $"
+
+/* note, that most of the typedef's are from ingo. */
+
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+
+//#define QDIO_DBF_LIKE_HELL
+
+#define QDIO_NAME "qdio "
+
+#define QDIO_VERBOSE_LEVEL 9
+
+#ifndef CONFIG_ARCH_S390X
+#define QDIO_32_BIT
+#endif /* CONFIG_ARCH_S390X */
+
+#define QDIO_USE_PROCESSING_STATE
+
+#ifdef CONFIG_QDIO_PERF_STATS
+#define QDIO_PERFORMANCE_STATS
+#endif /* CONFIG_QDIO_PERF_STATS */
+
+/**** CONSTANTS, that are relied on without using these symbols *****/
+#define QDIO_MAX_QUEUES_PER_IRQ 32 /* used in width of unsigned int */
+/************************ END of CONSTANTS **************************/
+#define QDIO_MAX_BUFFERS_PER_Q 128 /* must be a power of 2 (%x=&(x-1)*/
+#define QDIO_BUF_ORDER 7 /* 2**this == number of pages used for sbals in 1 q */
+#define QDIO_MAX_ELEMENTS_PER_BUFFER 16
+#define SBAL_SIZE 256
+
+#define IQDIO_FILL_LEVEL_TO_POLL (QDIO_MAX_BUFFERS_PER_Q*4/3)
+
+#define TIQDIO_THININT_ISC 3
+#define TIQDIO_DELAY_TARGET 0
+#define QDIO_BUSY_BIT_PATIENCE 2000 /* in microsecs */
+#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
+#define IQDIO_LOCAL_LAPS_INT 1
+#define IQDIO_GLOBAL_SUMMARY_CC_MASK 2
+/*#define IQDIO_IQDC_INT_PARM 0x1234*/
+
+#define QDIO_Q_LAPS 5
+
+#define QDIO_STORAGE_ACC_KEY 0
+
+#define L2_CACHELINE_SIZE 256
+#define INDICATORS_PER_CACHELINE (L2_CACHELINE_SIZE/sizeof(__u32))
+
+#define QDIO_PERF "qdio_perf"
+
+/* must be a power of 2 */
+/*#define QDIO_STATS_NUMBER 4
+
+#define QDIO_STATS_CLASSES 2
+#define QDIO_STATS_COUNT_NEEDED 2*/
+
+#define QDIO_ACTIVATE_DELAY 5 /* according to brenton belmar and paul
+				 gioquindo it can take up to 5ms before
+				 queues are really active */
+
+#define QDIO_NO_USE_COUNT_TIME 10
+#define QDIO_NO_USE_COUNT_TIMEOUT 1000 /* wait for 1 sec on each q before
+					  exiting without having use_count
+					  of the queue to 0 */
+
+#define QDIO_ESTABLISH_TIMEOUT 1000
+#define QDIO_ACTIVATE_TIMEOUT 100
+#define QDIO_CLEANUP_CLEAR_TIMEOUT 20000
+#define QDIO_CLEANUP_HALT_TIMEOUT 10000
+
+#define QDIO_BH AURORA_BH
+
+#define QDIO_IRQ_BUCKETS 256 /* heavy..., but does only use a few bytes, but
+			      be rather faster in cases of collisions
+			      (if there really is a collision, it is
+			      on every (traditional) interrupt and every
+			      do_QDIO, so we rather are generous */
+#define QDIO_QETH_QFMT 0
+#define QDIO_ZFCP_QFMT 1
+#define QDIO_IQDIO_QFMT 2
+
+#define QDIO_IRQ_STATE_FRESH 0 /* must be 0 -> memset has set it to 0 */
+#define QDIO_IRQ_STATE_INACTIVE 1
+#define QDIO_IRQ_STATE_ESTABLISHED 2
+#define QDIO_IRQ_STATE_ACTIVE 3
+#define QDIO_IRQ_STATE_STOPPED 4
+
+/* used as intparm in do_IO: */
+#define QDIO_DOING_SENSEID 0
+#define QDIO_DOING_ESTABLISH 1
+#define QDIO_DOING_ACTIVATE 2
+#define QDIO_DOING_CLEANUP 3
+
+/************************* DEBUG FACILITY STUFF *********************/
+/* #define QDIO_DBF_LIKE_HELL */
+
+#define QDIO_DBF_HEX(ex,name,level,addr,len) \
+	do { \
+	if (ex) \
+		debug_exception(qdio_dbf_##name,level,(void*)(addr),len); \
+	else \
+		debug_event(qdio_dbf_##name,level,(void*)(addr),len); \
+	} while (0)
+#define QDIO_DBF_TEXT(ex,name,level,text) \
+	do { \
+	if (ex) \
+		debug_text_exception(qdio_dbf_##name,level,text); \
+	else \
+		debug_text_event(qdio_dbf_##name,level,text); \
+	} while (0)
+
+#define QDIO_DBF_HEX0(ex,name,addr,len) QDIO_DBF_HEX(ex,name,0,addr,len)
+#define QDIO_DBF_HEX1(ex,name,addr,len) QDIO_DBF_HEX(ex,name,1,addr,len)
+#define QDIO_DBF_HEX2(ex,name,addr,len) QDIO_DBF_HEX(ex,name,2,addr,len)
+#define QDIO_DBF_HEX3(ex,name,addr,len) QDIO_DBF_HEX(ex,name,3,addr,len)
+#define QDIO_DBF_HEX4(ex,name,addr,len) QDIO_DBF_HEX(ex,name,4,addr,len)
+#define QDIO_DBF_HEX5(ex,name,addr,len) QDIO_DBF_HEX(ex,name,5,addr,len)
+#define QDIO_DBF_HEX6(ex,name,addr,len) QDIO_DBF_HEX(ex,name,6,addr,len)
+#ifdef QDIO_DBF_LIKE_HELL
+#endif /* QDIO_DBF_LIKE_HELL */
+#if 0
+#define QDIO_DBF_HEX0(ex,name,addr,len) do {} while (0)
+#define QDIO_DBF_HEX1(ex,name,addr,len) do {} while (0)
+#define QDIO_DBF_HEX2(ex,name,addr,len) do {} while (0)
+#ifndef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_HEX3(ex,name,addr,len) do {} while (0)
+#define QDIO_DBF_HEX4(ex,name,addr,len) do {} while (0)
+#define QDIO_DBF_HEX5(ex,name,addr,len) do {} while (0)
+#define QDIO_DBF_HEX6(ex,name,addr,len) do {} while (0)
+#endif /* QDIO_DBF_LIKE_HELL */
+#endif /* 0 */
+
+#define QDIO_DBF_TEXT0(ex,name,text) QDIO_DBF_TEXT(ex,name,0,text)
+#define QDIO_DBF_TEXT1(ex,name,text) QDIO_DBF_TEXT(ex,name,1,text)
+#define QDIO_DBF_TEXT2(ex,name,text) QDIO_DBF_TEXT(ex,name,2,text)
+#define QDIO_DBF_TEXT3(ex,name,text) QDIO_DBF_TEXT(ex,name,3,text)
+#define QDIO_DBF_TEXT4(ex,name,text) QDIO_DBF_TEXT(ex,name,4,text)
+#define QDIO_DBF_TEXT5(ex,name,text) QDIO_DBF_TEXT(ex,name,5,text)
+#define QDIO_DBF_TEXT6(ex,name,text) QDIO_DBF_TEXT(ex,name,6,text)
+#ifdef QDIO_DBF_LIKE_HELL
+#endif /* QDIO_DBF_LIKE_HELL */
+#if 0
+#define QDIO_DBF_TEXT0(ex,name,text) do {} while (0)
+#define QDIO_DBF_TEXT1(ex,name,text) do {} while (0)
+#define QDIO_DBF_TEXT2(ex,name,text) do {} while (0)
+#ifndef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_TEXT3(ex,name,text) do {} while (0)
+#define QDIO_DBF_TEXT4(ex,name,text) do {} while (0)
+#define QDIO_DBF_TEXT5(ex,name,text) do {} while (0)
+#define QDIO_DBF_TEXT6(ex,name,text) do {} while (0)
+#endif /* QDIO_DBF_LIKE_HELL */
+#endif /* 0 */
+
+#define QDIO_DBF_SETUP_NAME "qdio_setup"
+#define QDIO_DBF_SETUP_LEN 8
+#define QDIO_DBF_SETUP_INDEX 2
+#define QDIO_DBF_SETUP_NR_AREAS 1
+#ifdef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_SETUP_LEVEL 6
+#else /* QDIO_DBF_LIKE_HELL */
+#define QDIO_DBF_SETUP_LEVEL 2
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#define QDIO_DBF_SBAL_NAME "qdio_labs" /* sbal */
+#define QDIO_DBF_SBAL_LEN 256
+#define QDIO_DBF_SBAL_INDEX 2
+#define QDIO_DBF_SBAL_NR_AREAS 2
+#ifdef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_SBAL_LEVEL 6
+#else /* QDIO_DBF_LIKE_HELL */
+#define QDIO_DBF_SBAL_LEVEL 2
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#define QDIO_DBF_TRACE_NAME "qdio_trace"
+#define QDIO_DBF_TRACE_LEN 8
+#define QDIO_DBF_TRACE_NR_AREAS 2
+#ifdef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_TRACE_INDEX 4
+#define QDIO_DBF_TRACE_LEVEL 4 /* -------- could be even more verbose here */
+#else /* QDIO_DBF_LIKE_HELL */
+#define QDIO_DBF_TRACE_INDEX 2
+#define QDIO_DBF_TRACE_LEVEL 2
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#define QDIO_DBF_SENSE_NAME "qdio_sense"
+#define QDIO_DBF_SENSE_LEN 64
+#define QDIO_DBF_SENSE_INDEX 1
+#define QDIO_DBF_SENSE_NR_AREAS 1
+#ifdef QDIO_DBF_LIKE_HELL
+#define QDIO_DBF_SENSE_LEVEL 6
+#else /* QDIO_DBF_LIKE_HELL */
+#define QDIO_DBF_SENSE_LEVEL 2
+#endif /* QDIO_DBF_LIKE_HELL */
+
+#ifdef QDIO_DBF_LIKE_HELL
+#define QDIO_TRACE_QTYPE QDIO_ZFCP_QFMT
+
+#define QDIO_DBF_SLSB_OUT_NAME "qdio_slsb_out"
+#define QDIO_DBF_SLSB_OUT_LEN QDIO_MAX_BUFFERS_PER_Q
+#define QDIO_DBF_SLSB_OUT_INDEX 8
+#define QDIO_DBF_SLSB_OUT_NR_AREAS 1
+#define QDIO_DBF_SLSB_OUT_LEVEL 6
+
+#define QDIO_DBF_SLSB_IN_NAME "qdio_slsb_in"
+#define QDIO_DBF_SLSB_IN_LEN QDIO_MAX_BUFFERS_PER_Q
+#define QDIO_DBF_SLSB_IN_INDEX 8
+#define QDIO_DBF_SLSB_IN_NR_AREAS 1
+#define QDIO_DBF_SLSB_IN_LEVEL 6
+#endif /* QDIO_DBF_LIKE_HELL */
+
+/****************** END OF DEBUG FACILITY STUFF *********************/
+
+typedef struct qdio_buffer_element_t {
+	unsigned int flags;
+	unsigned int length;
+#ifdef QDIO_32_BIT
+	void *reserved;
+#endif /* QDIO_32_BIT */
+	void *addr;
+} __attribute__ ((packed,aligned(16))) qdio_buffer_element_t;
+
+typedef struct qdio_buffer_t {
+	volatile qdio_buffer_element_t element[16];
+} __attribute__ ((packed,aligned(256))) qdio_buffer_t;
+
+
+/* params are: irq, status, qdio_error, siga_error,
+   queue_number, first element processed, number of elements processed,
+   int_parm */
+typedef void qdio_handler_t(int,unsigned int,unsigned int,unsigned int,
+			    unsigned int,int,int,unsigned long);
+
+
+#define QDIO_STATUS_INBOUND_INT 0x01
+#define QDIO_STATUS_OUTBOUND_INT 0x02
+#define QDIO_STATUS_LOOK_FOR_ERROR 0x04
+#define QDIO_STATUS_MORE_THAN_ONE_QDIO_ERROR 0x08
+#define QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR 0x10
+#define QDIO_STATUS_ACTIVATE_CHECK_CONDITION 0x20
+
+#define QDIO_SIGA_ERROR_ACCESS_EXCEPTION 0x10
+#define QDIO_SIGA_ERROR_B_BIT_SET 0x20
+
+/* for qdio_initialize */
+#define QDIO_INBOUND_0COPY_SBALS 0x01
+#define QDIO_OUTBOUND_0COPY_SBALS 0x02
+#define QDIO_USE_OUTBOUND_PCIS 0x04
+#define QDIO_PFIX 0x08
+
+/* for qdio_cleanup */
+#define QDIO_FLAG_CLEANUP_USING_CLEAR 0x01
+#define QDIO_FLAG_CLEANUP_USING_HALT 0x02
+
+typedef struct qdio_initialize_t {
+	int irq;
+	unsigned char q_format;
+	unsigned char adapter_name[8];
+       	unsigned int qib_param_field_format; /*adapter dependent*/
+	/* pointer to 128 bytes or NULL, if no param field */
+	unsigned char *qib_param_field; /* adapter dependent */
+	/* pointer to no_queues*128 words of data or NULL */
+	unsigned long *input_slib_elements;
+	unsigned long *output_slib_elements;
+	unsigned int min_input_threshold;
+	unsigned int max_input_threshold;
+	unsigned int min_output_threshold;
+	unsigned int max_output_threshold;
+	unsigned int no_input_qs;
+	unsigned int no_output_qs;
+	qdio_handler_t *input_handler;
+	qdio_handler_t *output_handler;
+	unsigned long int_parm;
+	unsigned long flags;
+	void **input_sbal_addr_array; /* addr of n*128 void ptrs */
+	void **output_sbal_addr_array; /* addr of n*128 void ptrs */
+} qdio_initialize_t;
+extern int qdio_initialize(qdio_initialize_t *init_data);
+
+extern int qdio_activate(int irq,int flags);
+
+#define QDIO_STATE_MUST_USE_OUTB_PCI	0x00000001
+#define QDIO_STATE_INACTIVE 		0x00000002 /* after qdio_cleanup */
+#define QDIO_STATE_ESTABLISHED 		0x00000004 /* after qdio_initialize */
+#define QDIO_STATE_ACTIVE 		0x00000008 /* after qdio_activate */
+#define QDIO_STATE_STOPPED 		0x00000010 /* after queues went down */
+extern unsigned long qdio_get_status(int irq);
+
+
+#define QDIO_FLAG_SYNC_INPUT     0x01
+#define QDIO_FLAG_SYNC_OUTPUT    0x02
+#define QDIO_FLAG_UNDER_INTERRUPT 0x04
+#define QDIO_FLAG_NO_INPUT_INTERRUPT_CONTEXT 0x08 /* no effect on
+						     adapter interrupts */
+#define QDIO_FLAG_DONT_SIGA 0x10
+
+extern int do_QDIO(int irq,unsigned int flags, unsigned int queue_number,
+		   unsigned int qidx,unsigned int count,
+		   qdio_buffer_t *buffers);
+
+extern int qdio_synchronize(int irq,unsigned int flags,
+			    unsigned int queue_number);
+
+extern int qdio_cleanup(int irq,int how);
+
+extern unsigned char qdio_get_slsb_state(int irq,unsigned int flag,
+				  unsigned int queue_number,
+				  unsigned int qidx);
+
+/*
+ * QDIO device commands returned by extended Sense-ID
+ */
+#define DEFAULT_ESTABLISH_QS_CMD 0x1b
+#define DEFAULT_ESTABLISH_QS_COUNT 0x1000
+#define DEFAULT_ACTIVATE_QS_CMD 0x1f
+#define DEFAULT_ACTIVATE_QS_COUNT 0
+typedef struct _qdio_cmds {
+	unsigned char rcd;            /* read configuration data */
+	unsigned short count_rcd;
+	unsigned char sii;            /* set interface identifier */
+	unsigned short count_sii;
+	unsigned char rni;            /* read node identifier */
+	unsigned short count_rni;
+	unsigned char eq;             /* establish QDIO queues */
+	unsigned short count_eq;
+	unsigned char aq;             /* activate QDIO queues */
+	unsigned short count_aq;
+} qdio_cmds_t;
+
+/*
+ * additional CIWs returned by extended Sense-ID
+ */
+#define CIW_TYPE_EQUEUE 0x3       /* establish QDIO queues */
+#define CIW_TYPE_AQUEUE 0x4       /* activate QDIO queues */
+
+typedef struct _qdesfmt0 {
+#ifdef QDIO_32_BIT
+	unsigned long res1;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long sliba;            /* storage-list-information-block
+					   address */
+#ifdef QDIO_32_BIT
+	unsigned long res2;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long sla;              /* storage-list address */
+#ifdef QDIO_32_BIT
+	unsigned long res3;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long slsba;            /* storage-list-state-block address */
+	unsigned int  res4;		/* reserved */
+	unsigned int  akey  :  4;       /* access key for DLIB */
+	unsigned int  bkey  :  4;       /* access key for SL */
+	unsigned int  ckey  :  4;       /* access key for SBALs */
+	unsigned int  dkey  :  4;       /* access key for SLSB */
+	unsigned int  res5  : 16;       /* reserved */
+} __attribute__ ((packed)) qdesfmt0_t;
+
+/*
+ * Queue-Description record (QDR)
+ */
+typedef struct _qdr {
+	unsigned int  qfmt    :  8;     /* queue format */
+	unsigned int  pfmt    :  8;     /* impl. dep. parameter format */
+	unsigned int  res1    :  8;     /* reserved */
+	unsigned int  ac      :  8;     /* adapter characteristics */
+	unsigned int  res2    :  8;     /* reserved */
+	unsigned int  iqdcnt  :  8;     /* input-queue-descriptor count */
+	unsigned int  res3    :  8;     /* reserved */
+	unsigned int  oqdcnt  :  8;     /* output-queue-descriptor count */
+	unsigned int  res4    :  8;     /* reserved */
+	unsigned int  iqdsz   :  8;     /* input-queue-descriptor size */
+	unsigned int  res5    :  8;     /* reserved */
+	unsigned int  oqdsz   :  8;     /* output-queue-descriptor size */
+	unsigned int  res6[9];          /* reserved */
+#ifdef QDIO_32_BIT
+	unsigned long res7;		/* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long qiba;             /* queue-information-block address */
+	unsigned int  res8;             /* reserved */
+	unsigned int  qkey    :  4;     /* queue-informatio-block key */
+	unsigned int  res9    : 28;     /* reserved */
+/*	union _qd {*/ /* why this? */
+		qdesfmt0_t qdf0[126];
+/*	} qd;*/
+} __attribute__ ((packed,aligned(4096))) qdr_t;
+
+
+/*
+ * queue information block (QIB)
+ */
+#define QIB_AC_INBOUND_PCI_SUPPORTED 0x80
+#define QIB_AC_OUTBOUND_PCI_SUPPORTED 0x40
+typedef struct _qib {
+	unsigned int  qfmt    :  8;     /* queue format */
+	unsigned int  pfmt    :  8;     /* impl. dep. parameter format */
+	unsigned int  res1    :  8;     /* reserved */
+	unsigned int  ac      :  8;     /* adapter characteristics */
+	unsigned int  res2;             /* reserved */
+#ifdef QDIO_32_BIT
+	unsigned long res3;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long isliba;           /* absolute address of 1st
+					   input SLIB */
+#ifdef QDIO_32_BIT
+	unsigned long res4;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long osliba;           /* absolute address of 1st
+					   output SLIB */
+	unsigned int  res5;             /* reserved */
+	unsigned int  res6;             /* reserved */
+	unsigned char ebcnam[8];        /* adapter identifier in EBCDIC */
+	unsigned char res7[88];         /* reserved */
+	unsigned char parm[QDIO_MAX_BUFFERS_PER_Q];
+					/* implementation dependent
+					   parameters */
+} __attribute__ ((packed,aligned(256))) qib_t;
+
+
+/*
+ * storage-list-information block element (SLIBE)
+ */
+typedef struct _slibe {
+#ifdef QDIO_32_BIT
+	unsigned long res;              /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long parms;            /* implementation dependent
+					   parameters */
+} slibe_t;
+
+/*
+ * storage-list-information block (SLIB)
+ */
+typedef struct _slib {
+#ifdef QDIO_32_BIT
+	unsigned long res1;             /* reserved */
+#endif /* QDIO_32_BIT */
+        unsigned long nsliba;           /* next SLIB address (if any) */
+#ifdef QDIO_32_BIT
+	unsigned long res2;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long sla;              /* SL address */
+#ifdef QDIO_32_BIT
+	unsigned long res3;             /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long slsba;            /* SLSB address */
+	unsigned char res4[1000];       /* reserved */
+	slibe_t       slibe[QDIO_MAX_BUFFERS_PER_Q];    /* SLIB elements */
+} __attribute__ ((packed,aligned(2048))) slib_t;
+
+typedef struct _sbal_flags {
+	unsigned char res1  : 1;   /* reserved */
+	unsigned char last  : 1;   /* last entry */
+	unsigned char cont  : 1;   /* contiguous storage */
+	unsigned char res2  : 1;   /* reserved */
+	unsigned char frag  : 2;   /* fragmentation (s.below) */
+	unsigned char res3  : 2;   /* reserved */
+} __attribute__ ((packed)) sbal_flags_t;
+
+#define SBAL_FLAGS_FIRST_FRAG	     0x04000000UL
+#define SBAL_FLAGS_MIDDLE_FRAG	     0x08000000UL
+#define SBAL_FLAGS_LAST_FRAG	     0x0c000000UL
+#define SBAL_FLAGS_LAST_ENTRY	     0x40000000UL
+#define SBAL_FLAGS_CONTIGUOUS	     0x20000000UL
+
+#define SBAL_FLAGS0_DATA_CONTINUATION 0x20UL
+
+/* Awesome FCP extensions */
+#define SBAL_FLAGS0_TYPE_STATUS       0x00UL
+#define SBAL_FLAGS0_TYPE_WRITE        0x08UL
+#define SBAL_FLAGS0_TYPE_READ         0x10UL
+#define SBAL_FLAGS0_TYPE_WRITE_READ   0x18UL
+#define SBAL_FLAGS0_MORE_SBALS	      0x04UL
+#define SBAL_FLAGS0_COMMAND           0x02UL
+#define SBAL_FLAGS0_LAST_SBAL         0x00UL
+#define SBAL_FLAGS0_ONLY_SBAL         SBAL_FLAGS0_COMMAND
+#define SBAL_FLAGS0_MIDDLE_SBAL       SBAL_FLAGS0_MORE_SBALS
+#define SBAL_FLAGS0_FIRST_SBAL        SBAL_FLAGS0_MORE_SBALS | SBAL_FLAGS0_COMMAND
+/* Naught of interest beyond this point */
+
+#define SBAL_FLAGS0_PCI		0x40
+typedef struct _sbal_sbalf_0 {
+	unsigned char res1  : 1;   /* reserved */
+	unsigned char pci   : 1;   /* PCI indicator */
+	unsigned char cont  : 1;   /* data continuation */
+	unsigned char sbtype: 2;   /* storage-block type (FCP) */
+	unsigned char res2  : 3;   /* reserved */
+} __attribute__ ((packed)) sbal_sbalf_0_t;
+
+typedef struct _sbal_sbalf_1 {
+	unsigned char res1  : 4;   /* reserved */
+	unsigned char key   : 4;   /* storage key */
+} __attribute__ ((packed)) sbal_sbalf_1_t;
+
+typedef struct _sbal_sbalf_14 {
+	unsigned char res1   : 4;  /* reserved */
+	unsigned char erridx : 4;  /* error index */
+} __attribute__ ((packed)) sbal_sbalf_14_t;
+
+typedef struct _sbal_sbalf_15 {
+	unsigned char reason;      /* reserved */
+} __attribute__ ((packed)) sbal_sbalf_15_t;
+
+typedef union _sbal_sbalf {
+	sbal_sbalf_0_t  i0;
+	sbal_sbalf_1_t  i1;
+	sbal_sbalf_14_t i14;
+	sbal_sbalf_15_t i15;
+	unsigned char value;
+} sbal_sbalf_t;
+
+typedef struct _sbale {
+	union {
+		sbal_flags_t  bits;       /* flags */
+		unsigned char value;
+	} flags;
+	unsigned int  res1  : 16;   /* reserved */
+	sbal_sbalf_t  sbalf;       /* SBAL flags */
+	unsigned int  res2  : 16;  /* reserved */
+	unsigned int  count : 16;  /* data count */
+#ifdef QDIO_32_BIT
+	unsigned long res3;        /* reserved */
+#endif /* QDIO_32_BIT */
+	unsigned long addr;        /* absolute data address */
+} __attribute__ ((packed,aligned(16))) sbal_element_t;
+
+/*
+ * strorage-block access-list (SBAL)
+ */
+typedef struct _sbal {
+	sbal_element_t element[QDIO_MAX_ELEMENTS_PER_BUFFER];
+} __attribute__ ((packed,aligned(256))) sbal_t;
+
+/*
+ * storage-list (SL)
+ */
+typedef struct _sl_element {
+#ifdef QDIO_32_BIT
+        unsigned long res;     /* reserved */
+#endif /* QDIO_32_BIT */
+        unsigned long sbal;    /* absolute SBAL address */
+} __attribute__ ((packed)) sl_element_t;
+
+typedef struct _sl {
+	sl_element_t element[QDIO_MAX_BUFFERS_PER_Q];
+} __attribute__ ((packed,aligned(1024))) sl_t;
+
+/*
+ * storage-list-state block (SLSB)
+ */
+/*typedef struct _slsb_val {*/
+/*	unsigned char value;       */ /* SLSB entry as a single byte value */
+/*} __attribute__ ((packed)) slsb_val_t;*/
+
+typedef struct _slsb_flags {
+	unsigned char owner  : 2;   /* SBAL owner */
+	unsigned char type   : 1;   /* buffer type */
+	unsigned char state  : 5;   /* processing state */
+} __attribute__ ((packed)) slsb_flags_t;
+
+
+typedef struct _slsb {
+	union _acc {
+		unsigned char val[QDIO_MAX_BUFFERS_PER_Q];
+		slsb_flags_t flags[QDIO_MAX_BUFFERS_PER_Q];
+	} acc;
+} __attribute__ ((packed,aligned(256))) slsb_t;
+
+/*
+ * SLSB values
+ */
+#define SLSB_OWNER_PROG              1
+#define SLSB_OWNER_CU                2
+
+#define SLSB_TYPE_INPUT              0
+#define SLSB_TYPE_OUTPUT             1
+
+#define SLSB_STATE_NOT_INIT          0
+#define SLSB_STATE_EMPTY             1
+#define SLSB_STATE_PRIMED            2
+#define SLSB_STATE_HALTED          0xe
+#define SLSB_STATE_ERROR           0xf
+
+#define SLSB_P_INPUT_NOT_INIT     0x80
+#define SLSB_P_INPUT_PROCESSING	  0x81
+#define SLSB_CU_INPUT_EMPTY       0x41
+#define SLSB_P_INPUT_PRIMED       0x82
+#define SLSB_P_INPUT_HALTED       0x8E
+#define SLSB_P_INPUT_ERROR        0x8F
+
+#define SLSB_P_OUTPUT_NOT_INIT    0xA0
+#define SLSB_P_OUTPUT_EMPTY       0xA1
+#define SLSB_CU_OUTPUT_PRIMED     0x62
+#define SLSB_P_OUTPUT_HALTED      0xAE
+#define SLSB_P_OUTPUT_ERROR       0xAF
+
+#define SLSB_ERROR_DURING_LOOKUP  0xFF
+
+typedef struct qdio_q_t {
+	volatile slsb_t slsb;
+
+	__u32 * volatile dev_st_chg_ind;
+
+	int is_input_q;
+	int is_0copy_sbals_q;
+
+	unsigned int is_iqdio_q;
+	unsigned int is_thinint_q;
+
+	/* bit 0 means queue 0, bit 1 means queue 1, ... */
+	unsigned int mask;
+	unsigned int q_no;
+
+	qdio_handler_t (*handler);
+
+	/* points to the next buffer to be checked for having
+	 * been processed by the card (outbound)
+	 * or to the next buffer the program should check for (inbound) */
+	volatile int first_to_check;
+	/* and the last time it was: */
+	volatile int last_move_ftc;
+
+	atomic_t number_of_buffers_used;
+	atomic_t polling;
+
+	unsigned int siga_in;
+	unsigned int siga_out;
+	unsigned int siga_sync;
+	unsigned int siga_sync_done_on_thinints;
+	unsigned int siga_sync_done_on_outb_tis;
+	unsigned int hydra_gives_outbound_pcis;
+
+	/* used to save beginning position when calling dd_handlers */
+	int first_element_to_kick;
+
+	atomic_t use_count;
+	atomic_t is_in_shutdown;
+
+	int irq;
+	void *irq_ptr;
+
+#ifdef QDIO_USE_TIMERS_FOR_POLLING
+	struct timer_list timer;
+	atomic_t timer_already_set;
+	spinlock_t timer_lock;
+#else /* QDIO_USE_TIMERS_FOR_POLLING */
+	struct tasklet_struct tasklet;
+#endif /* QDIO_USE_TIMERS_FOR_POLLING */
+
+	unsigned int state;
+
+	/* used to store the error condition during a data transfer */
+	unsigned int qdio_error;
+	unsigned int siga_error;
+	unsigned int error_status_flags;
+
+	/* list of interesting queues */
+	volatile struct qdio_q_t *list_next;
+	volatile struct qdio_q_t *list_prev;
+
+	slib_t *slib; /* a page is allocated under this pointer,
+			 sl points into this page, offset PAGE_SIZE/2
+			 (after slib) */
+	sl_t *sl;
+	volatile sbal_t *sbal[QDIO_MAX_BUFFERS_PER_Q];
+
+	qdio_buffer_t *qdio_buffers[QDIO_MAX_BUFFERS_PER_Q];
+
+	unsigned long int_parm;
+
+	/*struct {
+		int in_bh_check_limit;
+		int threshold;
+	} threshold_classes[QDIO_STATS_CLASSES];*/
+
+	struct {
+		/* inbound: the time to stop polling
+		   outbound: the time to kick peer */
+		int threshold; /* the real value */
+
+		/* outbound: last time of do_QDIO
+		   inbound: last time of noticing incoming data */
+		/*__u64 last_transfer_times[QDIO_STATS_NUMBER];
+		int last_transfer_index; */
+
+		__u64 last_transfer_time;
+	} timing;
+        unsigned int queue_type;
+} __attribute__ ((aligned(256))) qdio_q_t;
+
+typedef struct qdio_irq_t {
+	__u32 * volatile dev_st_chg_ind;
+
+	unsigned long int_parm;
+	int irq;
+
+	unsigned int is_iqdio_irq;
+	unsigned int is_thinint_irq;
+	unsigned int hydra_gives_outbound_pcis;
+	unsigned int sync_done_on_outb_pcis;
+
+	unsigned int state;
+	spinlock_t setting_up_lock;
+
+	unsigned int no_input_qs;
+	unsigned int no_output_qs;
+
+	unsigned char qdioac;
+
+	qdio_q_t *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
+	qdio_q_t *output_qs[QDIO_MAX_QUEUES_PER_IRQ];
+
+	ccw1_t ccw;
+	int io_result_cstat;
+	int io_result_dstat;
+	int io_result_flags;
+	atomic_t interrupt_has_arrived;
+	atomic_t interrupt_has_been_cleaned;
+	wait_queue_head_t wait_q;
+
+	qdr_t *qdr;
+
+	qdio_cmds_t commands;
+
+	qib_t qib;
+
+	io_handler_func_t original_int_handler;
+
+	unsigned long other_flags; /* e.g. QDIO_PFIX */
+
+	struct qdio_irq_t *next;
+} qdio_irq_t;
+
+#define QDIO_CHSC_RESPONSE_CODE_OK 1
+/* flags for st qdio sch data */
+#define CHSC_FLAG_QDIO_CAPABILITY 0x80
+#define CHSC_FLAG_VALIDITY 0x40
+
+#define CHSC_FLAG_SIGA_INPUT_NECESSARY 0x40
+#define CHSC_FLAG_SIGA_OUTPUT_NECESSARY 0x20
+#define CHSC_FLAG_SIGA_SYNC_NECESSARY 0x10
+#define CHSC_FLAG_SIGA_SYNC_DONE_ON_THININTS 0x08
+#define CHSC_FLAG_SIGA_SYNC_DONE_ON_OUTB_PCIS 0x04
+
+typedef struct qdio_chsc_area_t {
+	struct {
+		/* word 0 */
+		__u16 command_code1;
+		__u16 command_code2;
+		/* word 1 */
+		__u16 operation_code;
+		__u16 first_sch;
+		/* word 2 */
+		__u8 reserved1;
+		__u8 image_id;
+		__u16 last_sch;
+		/* word 3 */
+		__u32 reserved2;
+
+		/* word 4 */
+		union {
+			struct {
+				/* word 4&5 */
+				__u64 summary_indicator_addr;
+				/* word 6&7 */
+				__u64 subchannel_indicator_addr;
+				/* word 8 */
+				int ks:4;
+				int kc:4;
+				int reserved1:21;
+				int isc:3;
+				/* word 9&10 */
+				__u32 reserved2[2];
+				/* word 11 */
+				__u32 subsystem_id;
+				/* word 12-1015 */
+				__u32 reserved3[1004];
+			} __attribute__ ((packed,aligned(4))) set_chsc;
+			struct {
+				/* word 4&5 */
+				__u32 reserved1[2];	
+				/* word 6 */
+				__u32 delay_target;
+				/* word 7-1015 */
+				__u32 reserved4[1009];
+			} __attribute__ ((packed,aligned(4))) set_chsc_fast;
+			struct {
+				/* word 0 */
+				__u16 length;
+				__u16 response_code;
+				/* word 1 */
+				__u32 reserved1;
+				/* words 2 to 9 for st sch qdio data */
+				__u8 flags;
+				__u8 reserved2;
+				__u16 sch;
+				__u8 qfmt;
+				__u8 reserved3;
+				__u8 qdioac;
+				__u8 sch_class;
+				__u8 reserved4;
+				__u8 icnt;
+				__u8 reserved5;
+				__u8 ocnt;
+				/* plus 5 words of reserved fields */
+			} __attribute__ ((packed,aligned(8)))
+			store_qdio_data_response;
+		} operation_data_area;
+	} __attribute__ ((packed,aligned(8))) request_block;
+	struct {
+		/* word 0 */
+		__u16 length;
+		__u16 response_code;
+		/* word 1 */
+		__u32 reserved1;
+	} __attribute__ ((packed,aligned(8))) response_block;
+} __attribute__ ((packed,aligned(PAGE_SIZE))) qdio_chsc_area_t;
+
+
+#define QDIO_PRINTK_HEADER QDIO_NAME ": "
+
+#if QDIO_VERBOSE_LEVEL>8
+#define QDIO_PRINT_STUPID(x...) printk( KERN_DEBUG QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_STUPID(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>7
+#define QDIO_PRINT_ALL(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_ALL(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>6
+#define QDIO_PRINT_INFO(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_INFO(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>5
+#define QDIO_PRINT_WARN(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_WARN(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>4
+#define QDIO_PRINT_ERR(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_ERR(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>3
+#define QDIO_PRINT_CRIT(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_CRIT(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>2
+#define QDIO_PRINT_ALERT(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_ALERT(x...)
+#endif
+
+#if QDIO_VERBOSE_LEVEL>1
+#define QDIO_PRINT_EMERG(x...) printk( QDIO_PRINTK_HEADER x)
+#else
+#define QDIO_PRINT_EMERG(x...)
+#endif
+
+#endif /* __QDIO_H__ */
diff -urN linux-2.4.21-bk9/include/asm-sh64/pgalloc-3level.h linux-2.4.21-bk10/include/asm-sh64/pgalloc-3level.h
--- linux-2.4.21-bk9/include/asm-sh64/pgalloc-3level.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/include/asm-sh64/pgalloc-3level.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,78 +0,0 @@
-#ifndef __ASM_SH64_PGALLOC_3LEVEL_H
-#define __ASM_SH64_PGALLOC_3LEVEL_H
-
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * include/asm-sh64/pgalloc-3level.h
- *
- * Copyright (C) 2000, 2001  Paolo Alberelli
- *
- */
-
-#if 0
-
-extern __inline__ pmd_t *get_pmd_slow(void)
-{
-	pmd_t *ret = (pmd_t *)__get_free_page(GFP_KERNEL);
-
-	if (ret)
-		memset(ret, 0, PAGE_SIZE);
-	return ret;
-}
-
-extern __inline__ pmd_t *get_pmd_fast(void)
-{
-	unsigned long *ret;
-
-	if ((ret = pmd_quicklist) != NULL) {
-		pmd_quicklist = (unsigned long *)(*ret);
-		ret[0] = 0;
-		pgtable_cache_size--;
-	} else
-		ret = (unsigned long *)get_pmd_slow();
-	return (pmd_t *)ret;
-}
-
-extern __inline__ void free_pmd_fast(pmd_t *pmd)
-{
-	*(unsigned long *)pmd = (unsigned long) pmd_quicklist;
-	pmd_quicklist = (unsigned long *) pmd;
-	pgtable_cache_size++;
-}
-
-extern __inline__ void free_pmd_slow(pmd_t *pmd)
-{
-	free_page((unsigned long)pmd);
-}
-
-extern inline pmd_t * pmd_alloc(pgd_t *pgd, unsigned long address)
-{
-	if (!pgd)
-		BUG();
-	address = (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1);
-	if (pgd_none(*pgd)) {
-		pmd_t *page = get_pmd_fast();
-
-		if (!page)
-			page = get_pmd_slow();
-		if (page) {
-			if (pgd_none(*pgd)) {
-				set_pgd(pgd, __pgd((unsigned long) page | _KERNPG_TABLE));
-
-				/* Flush TLB not required, SW tree walk */
-				return page + address;
-			} else
-				free_pmd_fast(page);
-		} else
-			return NULL;
-	}
-	return (pmd_t *)pgd_page(*pgd) + address;
-}
-
-#endif
-
-
-#endif /* __ASM_SH64_PGALLOC_3LEVEL_H */
diff -urN linux-2.4.21-bk9/include/asm-sh64/pgalloc.h linux-2.4.21-bk10/include/asm-sh64/pgalloc.h
--- linux-2.4.21-bk9/include/asm-sh64/pgalloc.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/include/asm-sh64/pgalloc.h	2003-07-15 02:55:07.000000000 -0700
@@ -1,5 +1,5 @@
-#ifndef __ASM_SH_PGALLOC_H
-#define __ASM_SH_PGALLOC_H
+#ifndef __ASM_SH64_PGALLOC_H
+#define __ASM_SH64_PGALLOC_H
 
 #include <asm/processor.h>
 #include <linux/threads.h>
@@ -20,10 +20,6 @@
  * Allocate and free page tables.
  */
 
-/* NB this is a pretty crap implementation, should use the quick functions
- * but speed is not a concern at the moment.
- */
-
 static inline pgd_t *pgd_alloc(struct mm_struct *mm)
 {
 	unsigned int pgd_size = (USER_PTRS_PER_PGD * sizeof(pgd_t));
@@ -68,27 +64,43 @@
 static inline pmd_t*
 pmd_alloc_one_fast (struct mm_struct *mm, unsigned long addr)
 {
+#if defined(CONFIG_SH64_PGTABLE_2_LEVEL)
+	BUG();
+#endif
 	return 0;
 }
  
 static inline pmd_t*
 pmd_alloc_one (struct mm_struct *mm, unsigned long addr)
 {
-        pmd_t *pmd = (pmd_t *) __get_free_page(GFP_KERNEL);
+        pmd_t *pmd = NULL;
+	
+#if defined(CONFIG_SH64_PGTABLE_2_LEVEL)
+	BUG();
+#elif defined(CONFIG_SH64_PGTABLE_3_LEVEL)
+	pmd = (pmd_t *) __get_free_page(GFP_KERNEL);
  
         if (pmd != NULL )
                 clear_page(pmd);
+#endif
         return pmd;
 }
  
+#if defined(CONFIG_SH64_PGTABLE_2_LEVEL)
+static inline void
+pmd_free (pmd_t *pmd)
+{
+	return;
+}
+#elif defined(CONFIG_SH64_PGTABLE_3_LEVEL)
 static inline void
 pmd_free (pmd_t *pmd)
 {
-
  free_page((unsigned long)pmd);      
 }
+#endif
+
 
- 
 static inline void
 pmd_populate (struct mm_struct *mm, pmd_t *pmd, pte_t *pte)
 {
@@ -169,5 +181,5 @@
 	pte_t old_pte = *ptep;
 	set_pte(ptep, pte_mkdirty(old_pte));
 }
-#endif /* __ASM_SH_PGALLOC_H */
+#endif /* __ASM_SH64_PGALLOC_H */
 
diff -urN linux-2.4.21-bk9/include/asm-sh64/pgtable-3level.h linux-2.4.21-bk10/include/asm-sh64/pgtable-3level.h
--- linux-2.4.21-bk9/include/asm-sh64/pgtable-3level.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/include/asm-sh64/pgtable-3level.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,152 +0,0 @@
-#ifndef __ASM_SH64_PGTABLE_3LEVEL_H
-#define __ASM_SH64_PGTABLE_3LEVEL_H
-
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * include/asm-sh64/pgtable-3level.h
- *
- * Copyright (C) 2000, 2001  Paolo Alberelli
- *
- * Supporting 32-bit addressing mode for now and, of course, 64-bit pointers.
- * Supporting variable NEFF/NPHYS defintions.
- *
- */
-
-/*
- * NEFF and NPHYS related defines.
- */
-#define NEFF		32
-#define	NEFF_SIGN	(1LL << (NEFF - 1))
-#define	NEFF_MASK	(-1LL << NEFF)
-
-#define NPHYS		32
-#define	NPHYS_SIGN	(1LL << (NPHYS - 1))
-#define	NPHYS_MASK	(-1LL << NPHYS)
-
-/*
- * three-level asymmetric paging structure: PGD is top level.
- * The asymmetry comes from 32-bit pointers and 64-bit PTEs.
- */
-/* bottom level: PTE. It's 9 bits = 512 pointers */
-#define PTRS_PER_PTE	((1<<PAGE_SHIFT)/sizeof(unsigned long long))
-#define PTE_MAGNITUDE	3	      /* sizeof(unsigned long long) magnit. */
-#define PTE_SHIFT	PAGE_SHIFT
-#define PTE_BITS	(PAGE_SHIFT - PTE_MAGNITUDE)
-
-/* middle level: PMD. It's 10 bits = 1024 pointers */
-#define PTRS_PER_PMD	((1<<PAGE_SHIFT)/sizeof(unsigned long long *))
-#define PMD_MAGNITUDE	2	      /* sizeof(unsigned long long *) magnit. */
-#define PMD_SHIFT	(PTE_SHIFT + PTE_BITS)
-#define PMD_BITS	(PAGE_SHIFT - PMD_MAGNITUDE)
-
-/* top level: PMD. It's 1 bit = 2 pointers */
-#define PGDIR_SHIFT	(PMD_SHIFT + PMD_BITS)
-#define PGD_BITS	(NEFF - PGDIR_SHIFT)
-#define PTRS_PER_PGD	(1<<PGD_BITS)
-
-/*
- * Error outputs.
- */
-#define pte_ERROR(e) \
-	printk("%s:%d: bad pte %016Lx.\n", __FILE__, __LINE__, pte_val(e))
-#define pmd_ERROR(e) \
-	printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
-#define pgd_ERROR(e) \
-	printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
-
-/*
- * Table setting routines. Used within arch/mm only.
- */
-#define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval)
-#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval)
-
-static __inline__ void set_pte(pte_t *pteptr, pte_t pteval)
-{
-	unsigned long long x = ((unsigned long long) pteval.pte);
-	unsigned long long *xp = (unsigned long long *) pteptr;
-	/*
-	 * Sign-extend based on NPHYS.
-	 */
-	*(xp) = (x & NPHYS_SIGN) ? (x | NPHYS_MASK) : x;
-}
-
-static __inline__ void pmd_set(pmd_t *pmdp,pte_t *ptep)
-{
-	pmd_val(*pmdp) = (unsigned long) ptep; 
-}
-
-  
-
-
-/*
- * PGD defines. Top level.
- */
-
-/* To find an entry in a generic PGD. */
-#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
-#define __pgd_offset(address) pgd_index(address)
-#define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address))
-
-/* To find an entry in a kernel PGD. */
-#define pgd_offset_k(address) pgd_offset(&init_mm, address)
-
-
-/*
- * PGD level access routines.
- *
- * Note1:
- * There's no need to use physical addresses since the tree walk is all
- * in performed in software, until the PTE translation.
- *
- * Note 2:
- * A PGD entry can be uninitialized (_PGD_UNUSED), generically bad,
- * clear (_PGD_EMPTY), present. When present, lower 3 nibbles contain
- * _KERNPG_TABLE. Being a kernel virtual pointer also bit 31 must
- * be 1. Assuming an arbitrary clear value of bit 31 set to 0 and
- * lower 3 nibbles set to 0xFFF (_PGD_EMPTY) any other value is a
- * bad pgd that must be notified via printk().
- *
- */
-#define _PGD_EMPTY		0x0
-#define pgd_present(pgd_entry)	(1)
-#define pgd_clear(pgd_entry_p)	(set_pgd((pgd_entry_p), __pgd(_PGD_EMPTY)))
-#define pgd_none(pgd_entry)	(pgd_val((pgd_entry)) == _PGD_EMPTY)
-/* TODO: Think later about what a useful definition of 'bad' would be now. */
-#define pgd_bad(pgd_entry)	(0)
-#define pgd_page(pgd_entry)	((unsigned long) (pgd_val(pgd_entry) & PAGE_MASK))
-
-
-/*
- * PMD defines. Middle level.
- */
-
-/* PGD to PMD dereferencing */
-#define __pmd_offset(address) \
-		(((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))
-#define pmd_offset(dir, addr) \
-		((pmd_t *) ((pgd_val(*(dir))) & PAGE_MASK) + __pmd_offset((addr)))
-
-
-/*
- * PMD level access routines. Same notes as above.
- */
-#define _PMD_EMPTY		0x0
-/* Either the PMD is empty or present, it's not paged out */
-#define pmd_present(pmd_entry)	(1)
-#define pmd_clear(pmd_entry_p)	(set_pmd((pmd_entry_p), __pmd(_PMD_EMPTY)))
-#define pmd_none(pmd_entry)	(pmd_val((pmd_entry)) == _PMD_EMPTY)
-/* TODO: Think later about what a useful definition of 'bad' would be now. */
-#define pmd_bad(pmd_entry)	(0)
-#define pmd_page(pmd_entry)	((unsigned long) (pmd_val(pmd_entry) & PAGE_MASK))
-
-/* PMD to PTE dereferencing */
-#define __pte_offset(address) \
-		((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
-
-#define pte_offset(dir, addr) \
-		((pte_t *) ((pmd_val(*(dir))) & PAGE_MASK) + __pte_offset((addr)))
-
-#endif /* __ASM_SH64_PGTABLE_3LEVEL_H */
diff -urN linux-2.4.21-bk9/include/asm-sh64/pgtable.h linux-2.4.21-bk10/include/asm-sh64/pgtable.h
--- linux-2.4.21-bk9/include/asm-sh64/pgtable.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/include/asm-sh64/pgtable.h	2003-07-15 02:55:07.000000000 -0700
@@ -10,6 +10,7 @@
  *
  * Copyright (C) 2000, 2001  Paolo Alberelli
  * Copyright (C) 2003  Paul Mundt
+ * Copyright (C) 2003  Richard Curnow
  *
  * This file contains the functions and defines necessary to modify and use
  * the SuperH page table tree.
@@ -18,11 +19,9 @@
 #ifndef __ASSEMBLY__
 #include <asm/processor.h>
 #include <asm/page.h>
-#include <asm/pgtable-3level.h>
 #include <linux/threads.h>
 #include <linux/config.h>
 
-extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
 extern void paging_init(void);
 
 extern void flush_cache_all(void);
@@ -85,13 +84,189 @@
 
 #endif /* !__ASSEMBLY__ */
 
-#include <asm/pgtable-3level.h>
+/*
+ * NEFF and NPHYS related defines.
+ * FIXME : These need to be model-dependent.  For now this is OK, SH5-101 and SH5-103
+ * implement 32 bits effective and 32 bits physical.  But future implementations may
+ * extend beyond this.
+ */
+#define NEFF		32
+#define	NEFF_SIGN	(1LL << (NEFF - 1))
+#define	NEFF_MASK	(-1LL << NEFF)
+
+#define NPHYS		32
+#define	NPHYS_SIGN	(1LL << (NPHYS - 1))
+#define	NPHYS_MASK	(-1LL << NPHYS)
+
+/* Typically 2-level is sufficient up to 32 bits of virtual address space, beyond
+   that 3-level would be appropriate. */
+#if defined(CONFIG_SH64_PGTABLE_2_LEVEL)
+/* For 4k pages, this contains 512 entries, i.e. 9 bits worth of address. */
+#define PTRS_PER_PTE	((1<<PAGE_SHIFT)/sizeof(unsigned long long))
+#define PTE_MAGNITUDE	3	      /* sizeof(unsigned long long) magnit. */
+#define PTE_SHIFT	PAGE_SHIFT
+#define PTE_BITS	(PAGE_SHIFT - PTE_MAGNITUDE)
+
+/* top level: PMD. */
+#define PGDIR_SHIFT	(PTE_SHIFT + PTE_BITS)
+#define PGD_BITS	(NEFF - PGDIR_SHIFT)
+#define PTRS_PER_PGD	(1<<PGD_BITS)
+
+/* middle level: PMD. This doesn't do anything for the 2-level case. */
+#define PTRS_PER_PMD	(1)
+
+#define PGDIR_SIZE	(1UL << PGDIR_SHIFT)
+#define PGDIR_MASK	(~(PGDIR_SIZE-1))
+#define PMD_SHIFT	PGDIR_SHIFT
+#define PMD_SIZE	PGDIR_SIZE
+#define PMD_MASK	PGDIR_MASK
+
+#elif defined(CONFIG_SH64_PGTABLE_3_LEVEL)
+/*
+ * three-level asymmetric paging structure: PGD is top level.
+ * The asymmetry comes from 32-bit pointers and 64-bit PTEs.
+ */
+/* bottom level: PTE. It's 9 bits = 512 pointers */
+#define PTRS_PER_PTE	((1<<PAGE_SHIFT)/sizeof(unsigned long long))
+#define PTE_MAGNITUDE	3	      /* sizeof(unsigned long long) magnit. */
+#define PTE_SHIFT	PAGE_SHIFT
+#define PTE_BITS	(PAGE_SHIFT - PTE_MAGNITUDE)
+
+/* middle level: PMD. It's 10 bits = 1024 pointers */
+#define PTRS_PER_PMD	((1<<PAGE_SHIFT)/sizeof(unsigned long long *))
+#define PMD_MAGNITUDE	2	      /* sizeof(unsigned long long *) magnit. */
+#define PMD_SHIFT	(PTE_SHIFT + PTE_BITS)
+#define PMD_BITS	(PAGE_SHIFT - PMD_MAGNITUDE)
+
+/* top level: PMD. It's 1 bit = 2 pointers */
+#define PGDIR_SHIFT	(PMD_SHIFT + PMD_BITS)
+#define PGD_BITS	(NEFF - PGDIR_SHIFT)
+#define PTRS_PER_PGD	(1<<PGD_BITS)
 
 #define PMD_SIZE	(1UL << PMD_SHIFT)
 #define PMD_MASK	(~(PMD_SIZE-1))
 #define PGDIR_SIZE	(1UL << PGDIR_SHIFT)
 #define PGDIR_MASK	(~(PGDIR_SIZE-1))
 
+#else
+#error "No defined number of page table levels"
+#endif
+
+/*
+ * Error outputs.
+ */
+#define pte_ERROR(e) \
+	printk("%s:%d: bad pte %016Lx.\n", __FILE__, __LINE__, pte_val(e))
+#define pmd_ERROR(e) \
+	printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
+#define pgd_ERROR(e) \
+	printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
+
+/*
+ * Table setting routines. Used within arch/mm only.
+ */
+#define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval)
+#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval)
+
+static __inline__ void set_pte(pte_t *pteptr, pte_t pteval)
+{
+	unsigned long long x = ((unsigned long long) pteval.pte);
+	unsigned long long *xp = (unsigned long long *) pteptr;
+	/*
+	 * Sign-extend based on NPHYS.
+	 */
+	*(xp) = (x & NPHYS_SIGN) ? (x | NPHYS_MASK) : x;
+}
+
+static __inline__ void pmd_set(pmd_t *pmdp,pte_t *ptep)
+{
+	pmd_val(*pmdp) = (unsigned long) ptep; 
+}
+
+/*
+ * PGD defines. Top level.
+ */
+
+/* To find an entry in a generic PGD. */
+#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))
+#define __pgd_offset(address) pgd_index(address)
+#define pgd_offset(mm, address) ((mm)->pgd+pgd_index(address))
+
+/* To find an entry in a kernel PGD. */
+#define pgd_offset_k(address) pgd_offset(&init_mm, address)
+
+/*
+ * PGD level access routines.
+ *
+ * Note1:
+ * There's no need to use physical addresses since the tree walk is all
+ * in performed in software, until the PTE translation.
+ *
+ * Note 2:
+ * A PGD entry can be uninitialized (_PGD_UNUSED), generically bad,
+ * clear (_PGD_EMPTY), present. When present, lower 3 nibbles contain
+ * _KERNPG_TABLE. Being a kernel virtual pointer also bit 31 must
+ * be 1. Assuming an arbitrary clear value of bit 31 set to 0 and
+ * lower 3 nibbles set to 0xFFF (_PGD_EMPTY) any other value is a
+ * bad pgd that must be notified via printk().
+ *
+ */
+#define _PGD_EMPTY		0x0
+
+#if defined(CONFIG_SH64_PGTABLE_2_LEVEL)
+static inline int pgd_none(pgd_t pgd)		{ return 0; }
+static inline int pgd_bad(pgd_t pgd)		{ return 0; }
+static inline int pgd_present(pgd_t pgd)	{ return 1; }
+#define pgd_clear(xx)				do { } while(0)
+
+#elif defined(CONFIG_SH64_PGTABLE_3_LEVEL)
+#define pgd_present(pgd_entry)	(1)
+#define pgd_none(pgd_entry)	(pgd_val((pgd_entry)) == _PGD_EMPTY)
+/* TODO: Think later about what a useful definition of 'bad' would be now. */
+#define pgd_bad(pgd_entry)	(0)
+#define pgd_clear(pgd_entry_p)	(set_pgd((pgd_entry_p), __pgd(_PGD_EMPTY)))
+
+#endif
+
+
+#define pgd_page(pgd_entry)	((unsigned long) (pgd_val(pgd_entry) & PAGE_MASK))
+
+/*
+ * PMD defines. Middle level.
+ */
+
+/* PGD to PMD dereferencing */
+#if defined(CONFIG_SH64_PGTABLE_2_LEVEL)
+static inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address)
+{
+	return (pmd_t *) dir;
+}
+#elif defined(CONFIG_SH64_PGTABLE_3_LEVEL)
+#define __pmd_offset(address) \
+		(((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))
+#define pmd_offset(dir, addr) \
+		((pmd_t *) ((pgd_val(*(dir))) & PAGE_MASK) + __pmd_offset((addr)))
+#endif
+
+/*
+ * PMD level access routines. Same notes as above.
+ */
+#define _PMD_EMPTY		0x0
+/* Either the PMD is empty or present, it's not paged out */
+#define pmd_present(pmd_entry)	(1)
+#define pmd_clear(pmd_entry_p)	(set_pmd((pmd_entry_p), __pmd(_PMD_EMPTY)))
+#define pmd_none(pmd_entry)	(pmd_val((pmd_entry)) == _PMD_EMPTY)
+/* TODO: Think later about what a useful definition of 'bad' would be now. */
+#define pmd_bad(pmd_entry)	(0)
+#define pmd_page(pmd_entry)	((unsigned long) (pmd_val(pmd_entry) & PAGE_MASK))
+
+/* PMD to PTE dereferencing */
+#define __pte_offset(address) \
+		((address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
+
+#define pte_offset(dir, addr) \
+		((pte_t *) ((pmd_val(*(dir))) & PAGE_MASK) + __pte_offset((addr)))
+
 /* Round it up ! */
 #define USER_PTRS_PER_PGD	((TASK_SIZE+PGDIR_SIZE-1)/PGDIR_SIZE)
 #define FIRST_USER_PGD_NR	0
@@ -120,7 +295,8 @@
    scheme doesn't map to SH-5 because bit [0] controls cacheability.  So bit
    [2] is used for _PAGE_PRESENT and the type field of swp_entry_t is split
    into 2 pieces.  That is handled by SWP_ENTRY and SWP_TYPE below. */
-#define _PAGE_WT	0x001  /* CB0: uncachable/device or write-back/write-thru */
+#define _PAGE_WT	0x001  /* CB0: if cacheable, 1->write-thru, 0->write-back */
+#define _PAGE_DEVICE	0x001  /* CB0: if uncacheable, 1->device (i.e. no write-combining or reordering at bus level) */
 #define _PAGE_CACHABLE	0x002  /* CB1: uncachable/cachable */
 #define _PAGE_PRESENT	0x004  /* software: if allocated */
 #define _PAGE_SIZE0	0x008  /* SZ0-bit : size of page */
@@ -195,6 +371,10 @@
 #define __S110	__pgprot(__MMAP_COMMON | _PAGE_SHARED | _PAGE_EXECUTE | _PAGE_WRITE)
 #define __S111	__pgprot(__MMAP_COMMON | _PAGE_SHARED | _PAGE_EXECUTE | _PAGE_READ | _PAGE_WRITE)
 
+/* Make it a device mapping for maximum safety (e.g. for mapping device
+   registers into user-space via /dev/map).  */
+#define pgprot_noncached(x) __pgprot(((x).pgprot & ~(_PAGE_CACHABLE)) | _PAGE_DEVICE)
+
 /*
  * Handling allocation failures during page table setup.
  */
@@ -303,6 +483,10 @@
 extern void update_mmu_cache(struct vm_area_struct * vma,
 			     unsigned long address, pte_t pte);
 
+
+/* Swap-related things */
+extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
+
 /* Encode and de-code a swap entry */
 #define SWP_TYPE(x)			(((x).val & 3) + (((x).val >> 1) & 0x3c))
 #define SWP_OFFSET(x)			((x).val >> 8)
diff -urN linux-2.4.21-bk9/include/asm-sh64/processor.h linux-2.4.21-bk10/include/asm-sh64/processor.h
--- linux-2.4.21-bk9/include/asm-sh64/processor.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/include/asm-sh64/processor.h	2003-07-15 02:55:07.000000000 -0700
@@ -76,9 +76,9 @@
 #endif
 
 /*
- * User space process size: 2GB - 64MB.
+ * User space process size: 2GB - 4k.
  */
-#define TASK_SIZE	0x7c000000UL
+#define TASK_SIZE	0x7ffff000UL
 
 /* This decides where the kernel will search for a free chunk of vm
  * space during mmap's.
@@ -230,8 +230,14 @@
 			     : "r" (~SR_FD));
 }
 
-/* Double presision, NANS as NANS, rounding to nearest, no exceptions */
+/* Round to nearest, no exceptions on inexact, overflow, underflow,
+   zero-divide, invalid.  Configure option for whether to flush denorms to
+   zero, or except if a denorm is encountered.  */
+#if defined(CONFIG_SH64_FPU_DENORM_FLUSH)
+#define FPSCR_INIT  0x00040000
+#else
 #define FPSCR_INIT  0x00000000
+#endif
 
 /* Save the current FP regs */
 void fpsave(struct sh_fpu_hard_struct *fpregs);
diff -urN linux-2.4.21-bk9/include/linux/agp_backend.h linux-2.4.21-bk10/include/linux/agp_backend.h
--- linux-2.4.21-bk9/include/linux/agp_backend.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/include/linux/agp_backend.h	2003-07-15 02:55:07.000000000 -0700
@@ -66,6 +66,7 @@
 	VIA_APOLLO_KM266,
 	VIA_APOLLO_KT400,
 	VIA_APOLLO_P4M266,
+	VIA_APOLLO_P4X400,
 	SIS_GENERIC,
 	AMD_GENERIC,
 	AMD_IRONGATE,
diff -urN linux-2.4.21-bk9/include/linux/quota.h linux-2.4.21-bk10/include/linux/quota.h
--- linux-2.4.21-bk9/include/linux/quota.h	2003-07-15 02:55:00.000000000 -0700
+++ linux-2.4.21-bk10/include/linux/quota.h	2003-07-15 02:55:08.000000000 -0700
@@ -251,6 +251,7 @@
 	void (*free_space) (struct inode *, qsize_t);
 	void (*free_inode) (const struct inode *, unsigned long);
 	int (*transfer) (struct inode *, struct iattr *);
+	int (*sync_dquot) (struct dquot *);
 };
 
 /* Operations handling requests from userspace */
@@ -312,6 +313,7 @@
 
 int register_quota_format(struct quota_format_type *fmt);
 void unregister_quota_format(struct quota_format_type *fmt);
+void init_dquot_operations(struct dquot_operations *fsdqops);
 
 #else
 
diff -urN linux-2.4.21-bk9/include/linux/radeonfb.h linux-2.4.21-bk10/include/linux/radeonfb.h
--- linux-2.4.21-bk9/include/linux/radeonfb.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk10/include/linux/radeonfb.h	2003-07-15 02:55:08.000000000 -0700
@@ -0,0 +1,15 @@
+#ifndef __LINUX_RADEONFB_H__
+#define __LINUX_RADEONFB_H__
+
+#include <asm/ioctl.h>
+#include <asm/types.h>
+
+#define ATY_RADEON_LCD_ON	0x00000001
+#define ATY_RADEON_CRT_ON	0x00000002
+
+
+#define FBIO_RADEON_GET_MIRROR	_IOR('@', 3, __u32)
+#define FBIO_RADEON_SET_MIRROR	_IOW('@', 4, __u32)
+
+#endif
+
